ASoC: add N cpus to M codecs dai link support
authorBard Liao <yung-chuan.liao@linux.intel.com>
Wed, 7 Jun 2023 03:12:41 +0000 (11:12 +0800)
committerMark Brown <broonie@kernel.org>
Tue, 13 Jun 2023 11:11:15 +0000 (12:11 +0100)
Currently, ASoC supports dailinks with the following mappings:
1 cpu DAI to N codec DAIs
N cpu DAIs to N codec DAIs
But the mapping between N cpu DAIs and M codec DAIs is not supported.
The reason is that we didn't have a mechanism to map cpu and codec DAIs

This patch suggests a new snd_soc_dai_link_codec_ch_map struct in
struct snd_soc_dai_link{} which provides codec DAI to cpu DAI mapping
information used to implement N cpu DAIs to M codec DAIs
support.

When a dailink contains two or more cpu DAIs, we should set channel
number of cpus based on its channel mask. The new struct also provides
channel mask information for each codec and we can construct the cpu
channel mask by combining all codec channel masks which map to the cpu.

The N:M mapping is however restricted to the N <= M case due to physical
restrictions on a time-multiplexed bus such as I2S/TDM, AC97, SoundWire
and HDaudio.

Signed-off-by: Bard Liao <yung-chuan.liao@linux.intel.com>
Reviewed-by: Péter Ujfalusi <peter.ujfalusi@linux.intel.com>
Reviewed-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
Reviewed-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
Link: https://lore.kernel.org/r/20230607031242.1032060-2-yung-chuan.liao@linux.intel.com
Signed-off-by: Mark Brown <broonie@kernel.org>
include/sound/soc.h
sound/soc/soc-dapm.c
sound/soc/soc-pcm.c

index 10e4ea0664af4610622d011c29c6ea57fd5fcafa..1e48a113584438be00c088df8ad0e3a0f2d9b58b 100644 (file)
@@ -645,6 +645,11 @@ struct snd_soc_dai_link_component {
        const char *dai_name;
 };
 
+struct snd_soc_dai_link_codec_ch_map {
+       unsigned int connected_cpu_id;
+       unsigned int ch_mask;
+};
+
 struct snd_soc_dai_link {
        /* config - must be set by machine driver */
        const char *name;                       /* Codec name */
@@ -673,6 +678,7 @@ struct snd_soc_dai_link {
        struct snd_soc_dai_link_component *codecs;
        unsigned int num_codecs;
 
+       struct snd_soc_dai_link_codec_ch_map *codec_ch_maps;
        /*
         * You MAY specify the link's platform/PCM/DMA driver, either by
         * device name, or by DT/OF node, but not both. Some forms of link
index b7b31d4e8ae8017a42c3554a8d72145bd7c0677f..3091e8160bad77c22255e2655273051601994fee 100644 (file)
@@ -4444,9 +4444,31 @@ void snd_soc_dapm_connect_dai_link_widgets(struct snd_soc_card *card)
                        for_each_rtd_codec_dais(rtd, i, codec_dai)
                                dapm_connect_dai_pair(card, rtd, codec_dai,
                                                      asoc_rtd_to_cpu(rtd, i));
+               } else if (rtd->dai_link->num_codecs > rtd->dai_link->num_cpus) {
+                       int cpu_id;
+
+                       if (!rtd->dai_link->codec_ch_maps) {
+                               dev_err(card->dev, "%s: no codec channel mapping table provided\n",
+                                       __func__);
+                               continue;
+                       }
+
+                       for_each_rtd_codec_dais(rtd, i, codec_dai) {
+                               cpu_id = rtd->dai_link->codec_ch_maps[i].connected_cpu_id;
+                               if (cpu_id >= rtd->dai_link->num_cpus) {
+                                       dev_err(card->dev,
+                                               "%s: dai_link %s cpu_id %d too large, num_cpus is %d\n",
+                                               __func__, rtd->dai_link->name, cpu_id,
+                                               rtd->dai_link->num_cpus);
+                                       continue;
+                               }
+                               dapm_connect_dai_pair(card, rtd, codec_dai,
+                                                     asoc_rtd_to_cpu(rtd, cpu_id));
+                       }
                } else {
                        dev_err(card->dev,
-                               "N cpus to M codecs link is not supported yet\n");
+                               "%s: codec number %d < cpu number %d is not supported\n",
+                               __func__, rtd->dai_link->num_codecs, rtd->dai_link->num_cpus);
                }
        }
 }
index 799865a6eb561143a09b670da1de9c7d1372582d..60cfbe565759469d5536326d2e0fcf7f8dd7bc61 100644 (file)
@@ -1034,6 +1034,10 @@ static int __soc_pcm_hw_params(struct snd_soc_pcm_runtime *rtd,
        }
 
        for_each_rtd_cpu_dais(rtd, i, cpu_dai) {
+               struct snd_pcm_hw_params cpu_params;
+               unsigned int ch_mask = 0;
+               int j;
+
                /*
                 * Skip CPUs which don't support the current stream
                 * type. See soc_pcm_init_runtime_hw() for more details
@@ -1041,13 +1045,32 @@ static int __soc_pcm_hw_params(struct snd_soc_pcm_runtime *rtd,
                if (!snd_soc_dai_stream_valid(cpu_dai, substream->stream))
                        continue;
 
-               ret = snd_soc_dai_hw_params(cpu_dai, substream, params);
+               /* copy params for each cpu */
+               cpu_params = *params;
+
+               if (!rtd->dai_link->codec_ch_maps)
+                       goto hw_params;
+               /*
+                * construct cpu channel mask by combining ch_mask of each
+                * codec which maps to the cpu.
+                */
+               for_each_rtd_codec_dais(rtd, j, codec_dai) {
+                       if (rtd->dai_link->codec_ch_maps[j].connected_cpu_id == i)
+                               ch_mask |= rtd->dai_link->codec_ch_maps[j].ch_mask;
+               }
+
+               /* fixup cpu channel number */
+               if (ch_mask)
+                       soc_pcm_codec_params_fixup(&cpu_params, ch_mask);
+
+hw_params:
+               ret = snd_soc_dai_hw_params(cpu_dai, substream, &cpu_params);
                if (ret < 0)
                        goto out;
 
                /* store the parameters for each DAI */
-               soc_pcm_set_dai_params(cpu_dai, params);
-               snd_soc_dapm_update_dai(substream, params, cpu_dai);
+               soc_pcm_set_dai_params(cpu_dai, &cpu_params);
+               snd_soc_dapm_update_dai(substream, &cpu_params, cpu_dai);
        }
 
        ret = snd_soc_pcm_component_hw_params(substream, params);
@@ -2789,9 +2812,22 @@ static int soc_get_playback_capture(struct snd_soc_pcm_runtime *rtd,
                                cpu_dai = asoc_rtd_to_cpu(rtd, 0);
                        } else if (dai_link->num_cpus == dai_link->num_codecs) {
                                cpu_dai = asoc_rtd_to_cpu(rtd, i);
+                       } else if (rtd->dai_link->num_codecs > rtd->dai_link->num_cpus) {
+                               int cpu_id;
+
+                               if (!rtd->dai_link->codec_ch_maps) {
+                                       dev_err(rtd->card->dev, "%s: no codec channel mapping table provided\n",
+                                               __func__);
+                                       return -EINVAL;
+                               }
+
+                               cpu_id = rtd->dai_link->codec_ch_maps[i].connected_cpu_id;
+                               cpu_dai = asoc_rtd_to_cpu(rtd, cpu_id);
                        } else {
                                dev_err(rtd->card->dev,
-                                       "N cpus to M codecs link is not supported yet\n");
+                                       "%s codec number %d < cpu number %d is not supported\n",
+                                       __func__, rtd->dai_link->num_codecs,
+                                       rtd->dai_link->num_cpus);
                                return -EINVAL;
                        }