]> asedeno.scripts.mit.edu Git - linux.git/blobdiff - sound/soc/sof/topology.c
ASoC: SOF: topology: initial support for Intel ALH DAI type
[linux.git] / sound / soc / sof / topology.c
index 432ae343f960240c761bc1fdb1aad2e4273913a2..28a7a6e06a539531333a758c07cc557cd3adfa9c 100644 (file)
 /* size of tplg abi in byte */
 #define SOF_TPLG_ABI_SIZE 3
 
+struct sof_widget_data {
+       int ctrl_type;
+       int ipc_cmd;
+       struct sof_abi_hdr *pdata;
+       struct snd_sof_control *control;
+};
+
 /* send pcm params ipc */
 static int ipc_pcm_params(struct snd_sof_widget *swidget, int dir)
 {
@@ -339,6 +346,9 @@ static const struct sof_dai_types sof_dais[] = {
        {"SSP", SOF_DAI_INTEL_SSP},
        {"HDA", SOF_DAI_INTEL_HDA},
        {"DMIC", SOF_DAI_INTEL_DMIC},
+       {"ALH", SOF_DAI_INTEL_ALH},
+       {"SAI", SOF_DAI_IMX_SAI},
+       {"ESAI", SOF_DAI_IMX_ESAI},
 };
 
 static enum sof_ipc_dai_type find_dai(const char *name)
@@ -748,6 +758,9 @@ static const struct sof_topology_token ssp_tokens[] = {
                get_token_u16,
                offsetof(struct sof_ipc_dai_ssp_params,
                         tdm_per_slot_padding_flag), 0},
+       {SOF_TKN_INTEL_SSP_BCLK_DELAY, SND_SOC_TPLG_TUPLE_TYPE_WORD,
+               get_token_u32,
+               offsetof(struct sof_ipc_dai_ssp_params, bclk_delay), 0},
 
 };
 
@@ -1739,51 +1752,32 @@ static int sof_widget_load_siggen(struct snd_soc_component *scomp, int index,
        return ret;
 }
 
-static int sof_process_load(struct snd_soc_component *scomp, int index,
-                           struct snd_sof_widget *swidget,
-                           struct snd_soc_tplg_dapm_widget *tw,
-                           struct sof_ipc_comp_reply *r,
-                           int type)
+static size_t sof_get_control_data(struct snd_sof_dev *sdev,
+                                  struct snd_soc_dapm_widget *widget,
+                                  struct sof_widget_data *wdata)
 {
-       struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
-       struct snd_soc_tplg_private *private = &tw->priv;
-       struct snd_soc_dapm_widget *widget = swidget->widget;
        const struct snd_kcontrol_new *kc;
-       struct soc_bytes_ext *sbe;
        struct soc_mixer_control *sm;
+       struct soc_bytes_ext *sbe;
        struct soc_enum *se;
-       struct snd_sof_control *scontrol = NULL;
-       struct sof_abi_hdr *pdata = NULL;
-       struct sof_ipc_comp_process *process;
-       size_t ipc_size, ipc_data_size = 0;
-       int ret, i, offset = 0;
-
-       if (type == SOF_COMP_NONE) {
-               dev_err(sdev->dev, "error: invalid process comp type %d\n",
-                       type);
-               return -EINVAL;
-       }
+       size_t size = 0;
+       int i;
 
-       /*
-        * get possible component controls - get size of all pdata,
-        * then memcpy with headers
-        */
        for (i = 0; i < widget->num_kcontrols; i++) {
-
                kc = &widget->kcontrol_news[i];
 
                switch (widget->dobj.widget.kcontrol_type) {
                case SND_SOC_TPLG_TYPE_MIXER:
                        sm = (struct soc_mixer_control *)kc->private_value;
-                       scontrol = sm->dobj.private;
+                       wdata[i].control = sm->dobj.private;
                        break;
                case SND_SOC_TPLG_TYPE_BYTES:
                        sbe = (struct soc_bytes_ext *)kc->private_value;
-                       scontrol = sbe->dobj.private;
+                       wdata[i].control = sbe->dobj.private;
                        break;
                case SND_SOC_TPLG_TYPE_ENUM:
                        se = (struct soc_enum *)kc->private_value;
-                       scontrol = se->dobj.private;
+                       wdata[i].control = se->dobj.private;
                        break;
                default:
                        dev_err(sdev->dev, "error: unknown kcontrol type %d in widget %s\n",
@@ -1792,31 +1786,98 @@ static int sof_process_load(struct snd_soc_component *scomp, int index,
                        return -EINVAL;
                }
 
-               if (!scontrol) {
+               if (!wdata[i].control) {
                        dev_err(sdev->dev, "error: no scontrol for widget %s\n",
                                widget->name);
                        return -EINVAL;
                }
 
-               /* don't include if no private data */
-               pdata = scontrol->control_data->data;
-               if (!pdata)
-                       continue;
+               wdata[i].pdata = wdata[i].control->control_data->data;
+               if (!wdata[i].pdata)
+                       return -EINVAL;
 
                /* make sure data is valid - data can be updated at runtime */
-               if (pdata->magic != SOF_ABI_MAGIC)
-                       continue;
+               if (wdata[i].pdata->magic != SOF_ABI_MAGIC)
+                       return -EINVAL;
+
+               size += wdata[i].pdata->size;
+
+               /* get data type */
+               switch (wdata[i].control->cmd) {
+               case SOF_CTRL_CMD_VOLUME:
+               case SOF_CTRL_CMD_ENUM:
+               case SOF_CTRL_CMD_SWITCH:
+                       wdata[i].ipc_cmd = SOF_IPC_COMP_SET_VALUE;
+                       wdata[i].ctrl_type = SOF_CTRL_TYPE_VALUE_CHAN_SET;
+                       break;
+               case SOF_CTRL_CMD_BINARY:
+                       wdata[i].ipc_cmd = SOF_IPC_COMP_SET_DATA;
+                       wdata[i].ctrl_type = SOF_CTRL_TYPE_DATA_SET;
+                       break;
+               default:
+                       break;
+               }
+       }
+
+       return size;
+}
+
+static int sof_process_load(struct snd_soc_component *scomp, int index,
+                           struct snd_sof_widget *swidget,
+                           struct snd_soc_tplg_dapm_widget *tw,
+                           struct sof_ipc_comp_reply *r,
+                           int type)
+{
+       struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
+       struct snd_soc_dapm_widget *widget = swidget->widget;
+       struct snd_soc_tplg_private *private = &tw->priv;
+       struct sof_ipc_comp_process *process = NULL;
+       struct sof_widget_data *wdata = NULL;
+       size_t ipc_data_size = 0;
+       size_t ipc_size;
+       int offset = 0;
+       int ret = 0;
+       int i;
 
-               ipc_data_size += pdata->size;
+       if (type == SOF_COMP_NONE) {
+               dev_err(sdev->dev, "error: invalid process comp type %d\n",
+                       type);
+               return -EINVAL;
+       }
+
+       /* allocate struct for widget control data sizes and types */
+       if (widget->num_kcontrols) {
+               wdata = kcalloc(widget->num_kcontrols,
+                               sizeof(*wdata),
+                               GFP_KERNEL);
+
+               if (!wdata)
+                       return -ENOMEM;
+
+               /* get possible component controls and get size of all pdata */
+               ipc_data_size = sof_get_control_data(sdev, widget, wdata);
+
+               if (ipc_data_size <= 0) {
+                       ret = ipc_data_size;
+                       goto out;
+               }
        }
 
        ipc_size = sizeof(struct sof_ipc_comp_process) +
                le32_to_cpu(private->size) +
                ipc_data_size;
 
+       /* we are exceeding max ipc size, config needs to be sent separately */
+       if (ipc_size > SOF_IPC_MSG_MAX_SIZE) {
+               ipc_size -= ipc_data_size;
+               ipc_data_size = 0;
+       }
+
        process = kzalloc(ipc_size, GFP_KERNEL);
-       if (!process)
-               return -ENOMEM;
+       if (!process) {
+               ret = -ENOMEM;
+               goto out;
+       }
 
        /* configure iir IPC message */
        process->comp.hdr.size = ipc_size;
@@ -1842,40 +1903,13 @@ static int sof_process_load(struct snd_soc_component *scomp, int index,
         * get possible component controls - get size of all pdata,
         * then memcpy with headers
         */
-       for (i = 0; i < widget->num_kcontrols; i++) {
-               kc = &widget->kcontrol_news[i];
-
-               switch (widget->dobj.widget.kcontrol_type) {
-               case SND_SOC_TPLG_TYPE_MIXER:
-                       sm = (struct soc_mixer_control *)kc->private_value;
-                       scontrol = sm->dobj.private;
-                       break;
-               case SND_SOC_TPLG_TYPE_BYTES:
-                       sbe = (struct soc_bytes_ext *)kc->private_value;
-                       scontrol = sbe->dobj.private;
-                       break;
-               case SND_SOC_TPLG_TYPE_ENUM:
-                       se = (struct soc_enum *)kc->private_value;
-                       scontrol = se->dobj.private;
-                       break;
-               default:
-                       dev_err(sdev->dev, "error: unknown kcontrol type %d in widget %s\n",
-                               widget->dobj.widget.kcontrol_type,
-                               widget->name);
-                       return -EINVAL;
+       if (ipc_data_size) {
+               for (i = 0; i < widget->num_kcontrols; i++) {
+                       memcpy(&process->data + offset,
+                              wdata[i].pdata->data,
+                              wdata[i].pdata->size);
+                       offset += wdata[i].pdata->size;
                }
-
-               /* don't include if no private data */
-               pdata = scontrol->control_data->data;
-               if (!pdata)
-                       continue;
-
-               /* make sure data is valid - data can be updated at runtime */
-               if (pdata->magic != SOF_ABI_MAGIC)
-                       continue;
-
-               memcpy(&process->data + offset, pdata->data, pdata->size);
-               offset += pdata->size;
        }
 
        process->size = ipc_data_size;
@@ -1883,10 +1917,35 @@ static int sof_process_load(struct snd_soc_component *scomp, int index,
 
        ret = sof_ipc_tx_message(sdev->ipc, process->comp.hdr.cmd, process,
                                 ipc_size, r, sizeof(*r));
-       if (ret >= 0)
-               return ret;
+
+       if (ret < 0) {
+               dev_err(sdev->dev, "error: create process failed\n");
+               goto err;
+       }
+
+       /* we sent the data in single message so return */
+       if (ipc_data_size)
+               goto out;
+
+       /* send control data with large message supported method */
+       for (i = 0; i < widget->num_kcontrols; i++) {
+               wdata[i].control->readback_offset = 0;
+               ret = snd_sof_ipc_set_get_comp_data(sdev->ipc, wdata[i].control,
+                                                   wdata[i].ipc_cmd,
+                                                   wdata[i].ctrl_type,
+                                                   wdata[i].control->cmd,
+                                                   true);
+               if (ret != 0) {
+                       dev_err(sdev->dev, "error: send control failed\n");
+                       break;
+               }
+       }
+
 err:
-       kfree(process);
+       if (ret < 0)
+               kfree(process);
+out:
+       kfree(wdata);
        return ret;
 }
 
@@ -2457,6 +2516,26 @@ static int sof_link_ssp_load(struct snd_soc_component *scomp, int index,
        return ret;
 }
 
+static int sof_link_sai_load(struct snd_soc_component *scomp, int index,
+                            struct snd_soc_dai_link *link,
+                            struct snd_soc_tplg_link_config *cfg,
+                            struct snd_soc_tplg_hw_config *hw_config,
+                            struct sof_ipc_dai_config *config)
+{
+       /*TODO: Add implementation */
+       return 0;
+}
+
+static int sof_link_esai_load(struct snd_soc_component *scomp, int index,
+                             struct snd_soc_dai_link *link,
+                             struct snd_soc_tplg_link_config *cfg,
+                             struct snd_soc_tplg_hw_config *hw_config,
+                             struct sof_ipc_dai_config *config)
+{
+       /*TODO: Add implementation */
+       return 0;
+}
+
 static int sof_link_dmic_load(struct snd_soc_component *scomp, int index,
                              struct snd_soc_dai_link *link,
                              struct snd_soc_tplg_link_config *cfg,
@@ -2685,6 +2764,40 @@ static int sof_link_hda_load(struct snd_soc_component *scomp, int index,
        return ret;
 }
 
+static int sof_link_alh_load(struct snd_soc_component *scomp, int index,
+                            struct snd_soc_dai_link *link,
+                            struct snd_soc_tplg_link_config *cfg,
+                            struct snd_soc_tplg_hw_config *hw_config,
+                            struct sof_ipc_dai_config *config)
+{
+       struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
+       struct sof_ipc_reply reply;
+       u32 size = sizeof(*config);
+       int ret;
+
+       /* init IPC */
+       config->hdr.size = size;
+
+       /* send message to DSP */
+       ret = sof_ipc_tx_message(sdev->ipc,
+                                config->hdr.cmd, config, size, &reply,
+                                sizeof(reply));
+
+       if (ret < 0) {
+               dev_err(sdev->dev, "error: failed to set DAI config for ALH %d\n",
+                       config->dai_index);
+               return ret;
+       }
+
+       /* set config for all DAI's with name matching the link name */
+       ret = sof_set_dai_config(sdev, size, link, config);
+       if (ret < 0)
+               dev_err(sdev->dev, "error: failed to save DAI config for ALH %d\n",
+                       config->dai_index);
+
+       return ret;
+}
+
 /* DAI link - used for any driver specific init */
 static int sof_link_load(struct snd_soc_component *scomp, int index,
                         struct snd_soc_dai_link *link,
@@ -2781,6 +2894,18 @@ static int sof_link_load(struct snd_soc_component *scomp, int index,
                ret = sof_link_hda_load(scomp, index, link, cfg, hw_config,
                                        &config);
                break;
+       case SOF_DAI_INTEL_ALH:
+               ret = sof_link_alh_load(scomp, index, link, cfg, hw_config,
+                                       &config);
+               break;
+       case SOF_DAI_IMX_SAI:
+               ret = sof_link_sai_load(scomp, index, link, cfg, hw_config,
+                                       &config);
+               break;
+       case SOF_DAI_IMX_ESAI:
+               ret = sof_link_esai_load(scomp, index, link, cfg, hw_config,
+                                        &config);
+               break;
        default:
                dev_err(sdev->dev, "error: invalid DAI type %d\n", config.type);
                ret = -EINVAL;
@@ -2838,7 +2963,8 @@ static int sof_link_unload(struct snd_soc_component *scomp,
        switch (sof_dai->dai_config->type) {
        case SOF_DAI_INTEL_SSP:
        case SOF_DAI_INTEL_DMIC:
-               /* no resource needs to be released for SSP and DMIC */
+       case SOF_DAI_INTEL_ALH:
+               /* no resource needs to be released for SSP, DMIC and ALH */
                break;
        case SOF_DAI_INTEL_HDA:
                ret = sof_link_hda_unload(sdev, link);