ASoC: SOF: Intel: set the DMA TLV device as dai_index
[linux-2.6-block.git] / sound / soc / sof / intel / hda-dai.c
index c1682bcdb5a6636091d92319162a206cfe08723c..86efcbe8f0d8438f91cd5f8e4ad68756a617f943 100644 (file)
@@ -221,15 +221,15 @@ static int __maybe_unused hda_dai_hw_free(struct snd_pcm_substream *substream,
        return hda_link_dma_cleanup(substream, hext_stream, cpu_dai);
 }
 
-static int __maybe_unused hda_dai_hw_params(struct snd_pcm_substream *substream,
-                                           struct snd_pcm_hw_params *params,
-                                           struct snd_soc_dai *dai)
+static int __maybe_unused hda_dai_hw_params_data(struct snd_pcm_substream *substream,
+                                                struct snd_pcm_hw_params *params,
+                                                struct snd_soc_dai *dai,
+                                                struct snd_sof_dai_config_data *data,
+                                                unsigned int flags)
 {
        struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(dai, substream->stream);
        const struct hda_dai_widget_dma_ops *ops = hda_dai_get_ops(substream, dai);
        struct hdac_ext_stream *hext_stream;
-       struct snd_sof_dai_config_data data = { 0 };
-       unsigned int flags = SOF_DAI_CONFIG_FLAGS_HW_PARAMS;
        struct snd_sof_dev *sdev = widget_to_sdev(w);
        int ret;
 
@@ -249,9 +249,19 @@ static int __maybe_unused hda_dai_hw_params(struct snd_pcm_substream *substream,
        hext_stream = ops->get_hext_stream(sdev, dai, substream);
 
        flags |= SOF_DAI_CONFIG_FLAGS_2_STEP_STOP << SOF_DAI_CONFIG_FLAGS_QUIRK_SHIFT;
-       data.dai_data = hdac_stream(hext_stream)->stream_tag - 1;
+       data->dai_data = hdac_stream(hext_stream)->stream_tag - 1;
+
+       return hda_dai_config(w, flags, data);
+}
+
+static int __maybe_unused hda_dai_hw_params(struct snd_pcm_substream *substream,
+                                           struct snd_pcm_hw_params *params,
+                                           struct snd_soc_dai *dai)
+{
+       struct snd_sof_dai_config_data data = { 0 };
+       unsigned int flags = SOF_DAI_CONFIG_FLAGS_HW_PARAMS;
 
-       return hda_dai_config(w, flags, &data);
+       return hda_dai_hw_params_data(substream, params, dai, &data, flags);
 }
 
 /*
@@ -341,11 +351,14 @@ static struct sof_ipc4_copier *widget_to_copier(struct snd_soc_dapm_widget *w)
        return ipc4_copier;
 }
 
-static int non_hda_dai_hw_params(struct snd_pcm_substream *substream,
-                                struct snd_pcm_hw_params *params,
-                                struct snd_soc_dai *cpu_dai)
+static int non_hda_dai_hw_params_data(struct snd_pcm_substream *substream,
+                                     struct snd_pcm_hw_params *params,
+                                     struct snd_soc_dai *cpu_dai,
+                                     struct snd_sof_dai_config_data *data,
+                                     unsigned int flags)
 {
        struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(cpu_dai, substream->stream);
+       struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
        struct sof_ipc4_dma_config_tlv *dma_config_tlv;
        const struct hda_dai_widget_dma_ops *ops;
        struct sof_ipc4_dma_config *dma_config;
@@ -353,6 +366,8 @@ static int non_hda_dai_hw_params(struct snd_pcm_substream *substream,
        struct hdac_ext_stream *hext_stream;
        struct hdac_stream *hstream;
        struct snd_sof_dev *sdev;
+       struct snd_soc_dai *dai;
+       int cpu_dai_id;
        int stream_id;
        int ret;
 
@@ -363,9 +378,9 @@ static int non_hda_dai_hw_params(struct snd_pcm_substream *substream,
        }
 
        /* use HDaudio stream handling */
-       ret = hda_dai_hw_params(substream, params, cpu_dai);
+       ret = hda_dai_hw_params_data(substream, params, cpu_dai, data, flags);
        if (ret < 0) {
-               dev_err(cpu_dai->dev, "%s: hda_dai_hw_params failed: %d\n", __func__, ret);
+               dev_err(cpu_dai->dev, "%s: hda_dai_hw_params_data failed: %d\n", __func__, ret);
                return ret;
        }
 
@@ -392,7 +407,12 @@ static int non_hda_dai_hw_params(struct snd_pcm_substream *substream,
        /* configure TLV */
        ipc4_copier = widget_to_copier(w);
 
-       dma_config_tlv = &ipc4_copier->dma_config_tlv;
+       for_each_rtd_cpu_dais(rtd, cpu_dai_id, dai) {
+               if (dai == cpu_dai)
+                       break;
+       }
+
+       dma_config_tlv = &ipc4_copier->dma_config_tlv[cpu_dai_id];
        dma_config_tlv->type = SOF_IPC4_GTW_DMA_CONFIG_ID;
        /* dma_config_priv_size is zero */
        dma_config_tlv->length = sizeof(dma_config_tlv->dma_config);
@@ -403,13 +423,27 @@ static int non_hda_dai_hw_params(struct snd_pcm_substream *substream,
        dma_config->pre_allocated_by_host = 1;
        dma_config->dma_channel_id = stream_id - 1;
        dma_config->stream_id = stream_id;
-       dma_config->dma_stream_channel_map.device_count = 0; /* mapping not used */
+       /*
+        * Currently we use a DMA for each device in ALH blob. The device will
+        * be copied in sof_ipc4_prepare_copier_module.
+        */
+       dma_config->dma_stream_channel_map.device_count = 1;
        dma_config->dma_priv_config_size = 0;
 
 skip_tlv:
        return 0;
 }
 
+static int non_hda_dai_hw_params(struct snd_pcm_substream *substream,
+                                struct snd_pcm_hw_params *params,
+                                struct snd_soc_dai *cpu_dai)
+{
+       struct snd_sof_dai_config_data data = { 0 };
+       unsigned int flags = SOF_DAI_CONFIG_FLAGS_HW_PARAMS;
+
+       return non_hda_dai_hw_params_data(substream, params, cpu_dai, &data, flags);
+}
+
 static int non_hda_dai_prepare(struct snd_pcm_substream *substream,
                               struct snd_soc_dai *cpu_dai)
 {
@@ -439,12 +473,24 @@ int sdw_hda_dai_hw_params(struct snd_pcm_substream *substream,
                          int link_id)
 {
        struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(cpu_dai, substream->stream);
+       struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+       struct sof_ipc4_dma_config_tlv *dma_config_tlv;
+       struct snd_sof_dai_config_data data = { 0 };
+       unsigned int flags = SOF_DAI_CONFIG_FLAGS_HW_PARAMS;
        const struct hda_dai_widget_dma_ops *ops;
+       struct sof_ipc4_dma_config *dma_config;
+       struct sof_ipc4_copier *ipc4_copier;
        struct hdac_ext_stream *hext_stream;
+       struct snd_soc_dai *dai;
        struct snd_sof_dev *sdev;
+       bool cpu_dai_found = false;
+       int cpu_dai_id;
+       int ch_mask;
        int ret;
+       int i;
 
-       ret = non_hda_dai_hw_params(substream, params, cpu_dai);
+       data.dai_index = (link_id << 8) | cpu_dai->id;
+       ret = non_hda_dai_hw_params_data(substream, params, cpu_dai, &data, flags);
        if (ret < 0) {
                dev_err(cpu_dai->dev, "%s: non_hda_dai_hw_params failed %d\n", __func__, ret);
                return ret;
@@ -457,9 +503,25 @@ int sdw_hda_dai_hw_params(struct snd_pcm_substream *substream,
        if (!hext_stream)
                return -ENODEV;
 
-       /* in the case of SoundWire we need to program the PCMSyCM registers */
+       /*
+        * in the case of SoundWire we need to program the PCMSyCM registers. In case
+        * of aggregated devices, we need to define the channel mask for each sublink
+        * by reconstructing the split done in soc-pcm.c
+        */
+       for_each_rtd_cpu_dais(rtd, cpu_dai_id, dai) {
+               if (dai == cpu_dai) {
+                       cpu_dai_found = true;
+                       break;
+               }
+       }
+
+       if (!cpu_dai_found)
+               return -ENODEV;
+
+       ch_mask = GENMASK(params_channels(params) - 1, 0);
+
        ret = hdac_bus_eml_sdw_map_stream_ch(sof_to_bus(sdev), link_id, cpu_dai->id,
-                                            GENMASK(params_channels(params) - 1, 0),
+                                            ch_mask,
                                             hdac_stream(hext_stream)->stream_tag,
                                             substream->stream);
        if (ret < 0) {
@@ -468,6 +530,22 @@ int sdw_hda_dai_hw_params(struct snd_pcm_substream *substream,
                return ret;
        }
 
+       ipc4_copier = widget_to_copier(w);
+       dma_config_tlv = &ipc4_copier->dma_config_tlv[cpu_dai_id];
+       dma_config = &dma_config_tlv->dma_config;
+       dma_config->dma_stream_channel_map.mapping[0].device = data.dai_index;
+       dma_config->dma_stream_channel_map.mapping[0].channel_mask = ch_mask;
+
+       /*
+        * copy the dma_config_tlv to all ipc4_copier in the same link. Because only one copier
+        * will be handled in sof_ipc4_prepare_copier_module.
+        */
+       for_each_rtd_cpu_dais(rtd, i, dai) {
+               w = snd_soc_dai_get_widget(dai, substream->stream);
+               ipc4_copier = widget_to_copier(w);
+               memcpy(&ipc4_copier->dma_config_tlv[cpu_dai_id], dma_config_tlv,
+                      sizeof(*dma_config_tlv));
+       }
        return 0;
 }