From 0ab4d2ea711c9c18de1f2e1bcb9985434db6ebf6 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 23 Oct 2023 05:35:41 +0000 Subject: [PATCH 01/10] ASoC: makes CPU/Codec channel connection map more generic Current ASoC CPU:Codec = N:M connection is using connection mapping idea, but it is used for N < M case only. We want to use it for any case. By this patch, not only N:M connection, but all existing connection (1:1, 1:N, N:N) will use same connection mapping. Then, because it will use default mapping, no conversion patch is needed to exising drivers. More over, CPU:Codec = N:M (N > M) also supported in the same time. ch_maps array will has CPU/Codec index by this patch. Image CPU0 <---> Codec0 CPU1 <-+-> Codec1 CPU2 <-/ ch_map ch_map[0].cpu = 0 ch_map[0].codec = 0 ch_map[1].cpu = 1 ch_map[1].codec = 1 ch_map[2].cpu = 2 ch_map[2].codec = 1 Link: https://lore.kernel.org/r/87fs6wuszr.wl-kuninori.morimoto.gx@renesas.com Link: https://lore.kernel.org/r/878r7yqeo4.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Kuninori Morimoto --- include/sound/soc.h | 55 +++++++++++++++++- sound/soc/intel/boards/sof_sdw.c | 28 ++++----- sound/soc/soc-core.c | 97 +++++++++++++++++++++++++++++++- sound/soc/soc-dapm.c | 45 ++++----------- sound/soc/soc-pcm.c | 44 +++++---------- 5 files changed, 186 insertions(+), 83 deletions(-) diff --git a/include/sound/soc.h b/include/sound/soc.h index 7792c393e2386d..73e2b44a1657e2 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -655,8 +655,45 @@ struct snd_soc_dai_link_component { struct of_phandle_args *dai_args; }; -struct snd_soc_dai_link_codec_ch_map { - unsigned int connected_cpu_id; +/* + * [dai_link->ch_maps Image sample] + * + *------------------------- + * CPU0 <---> Codec0 + * + * ch-map[0].cpu = 0 ch-map[0].codec = 0 + * + *------------------------- + * CPU0 <---> Codec0 + * CPU1 <---> Codec1 + * CPU2 <---> Codec2 + * + * ch-map[0].cpu = 0 ch-map[0].codec = 0 + * ch-map[1].cpu = 1 ch-map[1].codec = 1 + * ch-map[2].cpu = 2 ch-map[2].codec = 2 + * + *------------------------- + * CPU0 <---> Codec0 + * CPU1 <-+-> Codec1 + * CPU2 <-/ + * + * ch-map[0].cpu = 0 ch-map[0].codec = 0 + * ch-map[1].cpu = 1 ch-map[1].codec = 1 + * ch-map[2].cpu = 2 ch-map[2].codec = 1 + * + *------------------------- + * CPU0 <---> Codec0 + * CPU1 <-+-> Codec1 + * \-> Codec2 + * + * ch-map[0].cpu = 0 ch-map[0].codec = 0 + * ch-map[1].cpu = 1 ch-map[1].codec = 1 + * ch-map[2].cpu = 1 ch-map[2].codec = 2 + * + */ +struct snd_soc_dai_link_ch_map { + unsigned int cpu; + unsigned int codec; unsigned int ch_mask; }; @@ -688,7 +725,9 @@ 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; + /* num_ch_maps = max(num_cpu, num_codecs) */ + struct snd_soc_dai_link_ch_map *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 @@ -775,6 +814,10 @@ struct snd_soc_dai_link { #endif }; +static inline int snd_soc_link_num_ch_map(struct snd_soc_dai_link *link) { + return max(link->num_cpus, link->num_codecs); +} + static inline struct snd_soc_dai_link_component* snd_soc_link_to_cpu(struct snd_soc_dai_link *link, int n) { return &(link)->cpus[n]; @@ -808,6 +851,12 @@ snd_soc_link_to_platform(struct snd_soc_dai_link *link, int n) { ((cpu) = snd_soc_link_to_cpu(link, i)); \ (i)++) +#define for_each_link_ch_maps(link, i, ch_map) \ + for ((i) = 0; \ + ((i) < snd_soc_link_num_ch_map(link) && \ + ((ch_map) = link->ch_maps + i)); \ + (i)++) + /* * Sample 1 : Single CPU/Codec/Platform * diff --git a/sound/soc/intel/boards/sof_sdw.c b/sound/soc/intel/boards/sof_sdw.c index 61f763023e5d99..c5a39af54306b8 100644 --- a/sound/soc/intel/boards/sof_sdw.c +++ b/sound/soc/intel/boards/sof_sdw.c @@ -580,16 +580,14 @@ int sdw_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); + struct snd_soc_dai_link_ch_map *ch_maps; int ch = params_channels(params); - struct snd_soc_dai *codec_dai; - struct snd_soc_dai *cpu_dai; unsigned int ch_mask; int num_codecs; int step; int i; - int j; - if (!rtd->dai_link->codec_ch_maps) + if (!rtd->dai_link->ch_maps) return 0; /* Identical data will be sent to all codecs in playback */ @@ -615,13 +613,9 @@ int sdw_hw_params(struct snd_pcm_substream *substream, * link has more than one codec DAIs. Set codec channel mask and * ASoC will set the corresponding channel numbers for each cpu dai. */ - for_each_rtd_cpu_dais(rtd, i, cpu_dai) { - for_each_rtd_codec_dais(rtd, j, codec_dai) { - if (rtd->dai_link->codec_ch_maps[j].connected_cpu_id != i) - continue; - rtd->dai_link->codec_ch_maps[j].ch_mask = ch_mask << (j * step); - } - } + for_each_link_ch_maps(rtd->dai_link, i, ch_maps) + ch_maps->ch_mask = ch_mask << (i * step); + return 0; } @@ -1360,15 +1354,17 @@ static int get_slave_info(const struct snd_soc_acpi_link_adr *adr_link, return 0; } -static void set_dailink_map(struct snd_soc_dai_link_codec_ch_map *sdw_codec_ch_maps, +static void set_dailink_map(struct snd_soc_dai_link_ch_map *sdw_codec_ch_maps, int codec_num, int cpu_num) { int step; int i; step = codec_num / cpu_num; - for (i = 0; i < codec_num; i++) - sdw_codec_ch_maps[i].connected_cpu_id = i / step; + for (i = 0; i < codec_num; i++) { + sdw_codec_ch_maps[i].cpu = i / step; + sdw_codec_ch_maps[i].codec = i; + } } static const char * const type_strings[] = {"SimpleJack", "SmartAmp", "SmartMic"}; @@ -1463,7 +1459,7 @@ static int create_sdw_dailink(struct snd_soc_card *card, int *link_index, *ignore_pch_dmic = true; for_each_pcm_streams(stream) { - struct snd_soc_dai_link_codec_ch_map *sdw_codec_ch_maps; + struct snd_soc_dai_link_ch_map *sdw_codec_ch_maps; char *name, *cpu_name; int playback, capture; static const char * const sdw_stream_name[] = { @@ -1540,7 +1536,7 @@ static int create_sdw_dailink(struct snd_soc_card *card, int *link_index, dai_links[*link_index].nonatomic = true; set_dailink_map(sdw_codec_ch_maps, codec_num, cpu_dai_num); - dai_links[*link_index].codec_ch_maps = sdw_codec_ch_maps; + dai_links[*link_index].ch_maps = sdw_codec_ch_maps; ret = set_codec_init_func(card, adr_link, dai_links + (*link_index)++, playback, group_id, adr_index, dai_index); if (ret < 0) { diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index a60a7a2e17237c..fa0141c6472c12 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -1015,6 +1015,96 @@ static int soc_dai_link_sanity_check(struct snd_soc_card *card, return -EINVAL; } +#define MAX_DEFAULT_CH_MAP_SIZE 7 +static struct snd_soc_dai_link_ch_map default_ch_map_sync[MAX_DEFAULT_CH_MAP_SIZE] = { + { .cpu = 0, .codec = 0 }, + { .cpu = 1, .codec = 1 }, + { .cpu = 2, .codec = 2 }, + { .cpu = 3, .codec = 3 }, + { .cpu = 4, .codec = 4 }, + { .cpu = 5, .codec = 5 }, + { .cpu = 6, .codec = 6 }, +}; +static struct snd_soc_dai_link_ch_map default_ch_map_1cpu[MAX_DEFAULT_CH_MAP_SIZE] = { + { .cpu = 0, .codec = 0 }, + { .cpu = 0, .codec = 1 }, + { .cpu = 0, .codec = 2 }, + { .cpu = 0, .codec = 3 }, + { .cpu = 0, .codec = 4 }, + { .cpu = 0, .codec = 5 }, + { .cpu = 0, .codec = 6 }, +}; +static struct snd_soc_dai_link_ch_map default_ch_map_1codec[MAX_DEFAULT_CH_MAP_SIZE] = { + { .cpu = 0, .codec = 0 }, + { .cpu = 1, .codec = 0 }, + { .cpu = 2, .codec = 0 }, + { .cpu = 3, .codec = 0 }, + { .cpu = 4, .codec = 0 }, + { .cpu = 5, .codec = 0 }, + { .cpu = 6, .codec = 0 }, +}; +static int snd_soc_compensate_channel_connection_map(struct snd_soc_card *card, + struct snd_soc_dai_link *dai_link) +{ + struct snd_soc_dai_link_ch_map *ch_maps; + int i, max; + + /* + * dai_link->ch_maps indicates how CPU/Codec are connected. + * It will be a map seen from a larger number of DAI. + * see + * soc.h :: [dai_link->ch_maps Image sample] + */ + + /* it should have ch_maps if connection was N:M */ + if (dai_link->num_cpus > 1 && dai_link->num_codecs > 1 && + dai_link->num_cpus != dai_link->num_codecs && !dai_link->ch_maps) { + dev_err(card->dev, "need to have ch_maps when N:M connction (%s)", + dai_link->name); + return -EINVAL; + } + + /* do nothing if it has own maps */ + if (dai_link->ch_maps) + goto sanity_check; + + /* check default map size */ + if (dai_link->num_cpus > MAX_DEFAULT_CH_MAP_SIZE || + dai_link->num_codecs > MAX_DEFAULT_CH_MAP_SIZE) { + dev_err(card->dev, "soc-core.c needs update default_connection_maps"); + return -EINVAL; + } + + /* Compensate missing map for ... */ + if (dai_link->num_cpus == dai_link->num_codecs) + dai_link->ch_maps = default_ch_map_sync; /* for 1:1 or N:N */ + else if (dai_link->num_cpus < dai_link->num_codecs) + dai_link->ch_maps = default_ch_map_1cpu; /* for 1:N */ + else + dai_link->ch_maps = default_ch_map_1codec; /* for N:1 */ + +sanity_check: + max = min(dai_link->num_cpus, dai_link->num_codecs); + + dev_dbg(card->dev, "dai_link %s\n", dai_link->stream_name); + for_each_link_ch_maps(dai_link, i, ch_maps) { + if ((ch_maps->cpu >= dai_link->num_cpus) || + (ch_maps->codec >= dai_link->num_codecs)) { + dev_err(card->dev, + "unexpected dai_link->ch_maps[%d] index (cpu(%d/%d) codec(%d/%d))", + i, + ch_maps->cpu, dai_link->num_cpus, + ch_maps->codec, dai_link->num_codecs); + return -EINVAL; + } + + dev_dbg(card->dev, " [%d] cpu%d <-> codec%d\n", + i, ch_maps->cpu, ch_maps->codec); + } + + return 0; +} + /** * snd_soc_remove_pcm_runtime - Remove a pcm_runtime from card * @card: The ASoC card to which the pcm_runtime has @@ -1121,8 +1211,13 @@ int snd_soc_add_pcm_runtimes(struct snd_soc_card *card, int num_dai_link) { for (int i = 0; i < num_dai_link; i++) { - int ret = snd_soc_add_pcm_runtime(card, dai_link + i); + int ret; + + ret = snd_soc_compensate_channel_connection_map(card, dai_link + i); + if (ret < 0) + return ret; + ret = snd_soc_add_pcm_runtime(card, dai_link + i); if (ret < 0) return ret; } diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 4e2beda6f9bf2e..93ba9ec8dc48f3 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -4438,11 +4438,14 @@ static void soc_dapm_dai_stream_event(struct snd_soc_dai *dai, int stream, void snd_soc_dapm_connect_dai_link_widgets(struct snd_soc_card *card) { struct snd_soc_pcm_runtime *rtd; + struct snd_soc_dai *cpu_dai; struct snd_soc_dai *codec_dai; - int i; /* for each BE DAI link... */ for_each_card_rtds(card, rtd) { + struct snd_soc_dai_link_ch_map *ch_maps; + int i; + /* * dynamic FE links have no fixed DAI mapping. * CODEC<->CODEC links have no direct connection. @@ -4450,39 +4453,15 @@ void snd_soc_dapm_connect_dai_link_widgets(struct snd_soc_card *card) if (rtd->dai_link->dynamic) continue; - if (rtd->dai_link->num_cpus == 1) { - for_each_rtd_codec_dais(rtd, i, codec_dai) - dapm_connect_dai_pair(card, rtd, codec_dai, - snd_soc_rtd_to_cpu(rtd, 0)); - } else if (rtd->dai_link->num_codecs == rtd->dai_link->num_cpus) { - for_each_rtd_codec_dais(rtd, i, codec_dai) - dapm_connect_dai_pair(card, rtd, codec_dai, - snd_soc_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; - } + /* + * see + * soc.h :: [dai_link->ch_maps Image sample] + */ + for_each_link_ch_maps(rtd->dai_link, i, ch_maps) { + cpu_dai = snd_soc_rtd_to_cpu(rtd, ch_maps->cpu); + codec_dai = snd_soc_rtd_to_codec(rtd, ch_maps->codec); - 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, - snd_soc_rtd_to_cpu(rtd, cpu_id)); - } - } else { - dev_err(card->dev, - "%s: codec number %d < cpu number %d is not supported\n", - __func__, rtd->dai_link->num_codecs, rtd->dai_link->num_cpus); + dapm_connect_dai_pair(card, rtd, codec_dai, cpu_dai); } } } diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index 8c168dc553f659..c434fc19e253de 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -1042,6 +1042,7 @@ static int __soc_pcm_hw_params(struct snd_soc_pcm_runtime *rtd, } for_each_rtd_cpu_dais(rtd, i, cpu_dai) { + struct snd_soc_dai_link_ch_map *ch_maps; unsigned int ch_mask = 0; int j; @@ -1055,22 +1056,20 @@ static int __soc_pcm_hw_params(struct snd_soc_pcm_runtime *rtd, /* copy params for each cpu */ tmp_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. + * see + * soc.h :: [dai_link->ch_maps Image sample] */ - 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; - } + for_each_link_ch_maps(rtd->dai_link, j, ch_maps) + if (ch_maps->cpu == i) + ch_mask |= ch_maps->ch_mask; /* fixup cpu channel number */ if (ch_mask) soc_pcm_codec_params_fixup(&tmp_params, ch_mask); -hw_params: ret = snd_soc_dai_hw_params(cpu_dai, substream, &tmp_params); if (ret < 0) goto out; @@ -2818,35 +2817,20 @@ static int soc_get_playback_capture(struct snd_soc_pcm_runtime *rtd, } } } else { + struct snd_soc_dai_link_ch_map *ch_maps; struct snd_soc_dai *codec_dai; /* Adapt stream for codec2codec links */ int cpu_capture = snd_soc_get_stream_cpu(dai_link, SNDRV_PCM_STREAM_CAPTURE); int cpu_playback = snd_soc_get_stream_cpu(dai_link, SNDRV_PCM_STREAM_PLAYBACK); - for_each_rtd_codec_dais(rtd, i, codec_dai) { - if (dai_link->num_cpus == 1) { - cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); - } else if (dai_link->num_cpus == dai_link->num_codecs) { - cpu_dai = snd_soc_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 = snd_soc_rtd_to_cpu(rtd, cpu_id); - } else { - dev_err(rtd->card->dev, - "%s codec number %d < cpu number %d is not supported\n", - __func__, rtd->dai_link->num_codecs, - rtd->dai_link->num_cpus); - return -EINVAL; - } + /* + * see + * soc.h :: [dai_link->ch_maps Image sample] + */ + for_each_link_ch_maps(dai_link, i, ch_maps) { + cpu_dai = snd_soc_rtd_to_cpu(rtd, ch_maps->cpu); + codec_dai = snd_soc_rtd_to_codec(rtd, ch_maps->codec); if (snd_soc_dai_stream_valid(codec_dai, SNDRV_PCM_STREAM_PLAYBACK) && snd_soc_dai_stream_valid(cpu_dai, cpu_playback)) From d84cf8bef671f1a52d62607d8540056950066102 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 12 Oct 2023 01:32:03 +0000 Subject: [PATCH 02/10] ASoC: audio-graph-card2: add CPU:Codec = N:M support Now ASoC is supporting CPU:Codec = N:M support. This patch enables it on Audio Graph Card2. Signed-off-by: Kuninori Morimoto --- sound/soc/generic/audio-graph-card2.c | 29 +++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/sound/soc/generic/audio-graph-card2.c b/sound/soc/generic/audio-graph-card2.c index 1344e1adfc6784..3114b90d7b884a 100644 --- a/sound/soc/generic/audio-graph-card2.c +++ b/sound/soc/generic/audio-graph-card2.c @@ -513,7 +513,10 @@ static int graph_parse_node(struct simple_util_priv *priv, int ret = 0; if (graph_lnk_is_multi(port)) { + struct device_node *ports = of_get_parent(port); int idx; + int num; + char *props = "ch-maps"; of_node_get(port); @@ -528,6 +531,32 @@ static int graph_parse_node(struct simple_util_priv *priv, if (ret < 0) break; } + + /* read "ch-maps" property if exist */ + num = of_property_count_elems_of_size(ports, props, sizeof(u32)); + if (num > 0) { + struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, li->link); + struct device *dev = simple_priv_to_dev(priv); + struct snd_soc_dai_link_ch_map *ch_maps = devm_kcalloc(dev, num, sizeof(*ch_maps), GFP_KERNEL); + int *temp = devm_kcalloc(dev, num, sizeof(int), GFP_KERNEL); + int i; + + if (!ch_maps || !temp) { + ret = -ENOMEM; + goto multi_end; + } + + ret = of_property_read_u32_array(ports, props, temp, num); + if (ret < 0) + goto multi_end; + + dai_link->ch_maps = ch_maps; + for (i = 0; i < num; i++) + dai_link->ch_maps[i].connected_node = temp[i]; +multi_end: + devm_kfree(dev, temp); + } + of_node_put(ports); } else { /* Single CPU / Codec */ ep = port_to_endpoint(port); From 64ccc6a4c553d9756df00c10fffd7a2685adc114 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 12 Oct 2023 01:32:08 +0000 Subject: [PATCH 03/10] ASoC: audio-graph-card2-custom-sample: add CPU/Codec = N:M sample Now ASoC is supporting CPU/Codec = N:M connection. This patch adds its sample settings. But One note here is that it has many type of samples, it reached to maximum of sound minor number. Therefore, new sample is disabled so far. If you want to try it, you need to disable some other one instead. Signed-off-by: Kuninori Morimoto --- .../audio-graph-card2-custom-sample.dtsi | 138 +++++++++++++++--- 1 file changed, 121 insertions(+), 17 deletions(-) diff --git a/sound/soc/generic/audio-graph-card2-custom-sample.dtsi b/sound/soc/generic/audio-graph-card2-custom-sample.dtsi index 8acaa2ddb335f2..14d638de120a03 100644 --- a/sound/soc/generic/audio-graph-card2-custom-sample.dtsi +++ b/sound/soc/generic/audio-graph-card2-custom-sample.dtsi @@ -58,12 +58,32 @@ * | |-> codec13 * +-+ * - * [Multi-CPU/Codec] + * [Multi-CPU/Codec-0] * +-+ +-+ * cpu1 <--| |<-@--------->| |-> codec1 * cpu2 <--| | | |-> codec2 * +-+ +-+ * + * [Multi-CPU/Codec-1] + * About ch-map (*), see + * soc-core.c :: [dai_link->ch_maps Image sample] + * + * +-+ +-+ + * cpu8 <--| |<-@--------->| |-> codec14 [0]codec14 <---> [0]cpu8 + * cpu9 <--| | | |-> codec15 [1]codec15 <-+-> [1]cpu9 + * +-+ | |-> codec16 [2]codec16 <-/ + * +-+ (*) ch-map = [0, 1, 1] + * + * [Multi-CPU/Codec-2] + * About ch-map (*), see + * soc-core.c :: [dai_link->ch_maps Image sample] + * + * +-+ +-+ + * cpu10 <-| |<-@--------->| |-> codec17 [1]cpu11 <---> [0]codec17 + * cpu11 <-| | | |-> codec18 [0]cpu10 <-+-> [1]codec18 + * cpu12 <-| | +-+ [2]cpu12 <-/ + * +-+ (*) ch-map = [1, 0, 1] + * * [DPCM] * * CPU3/CPU4 are converting rate to 44100 @@ -144,15 +164,38 @@ */ &cpu0 - /* [Semi-Multi] */ + /* + * [Semi-Multi] + * cpu7/codec12/codec13 + */ &sm0 /* - * [Multi-CPU/Codec]: cpu side only + * [Multi-CPU/Codec-0]: cpu side only * cpu1/cpu2/codec1/codec2 */ &mcpu0 + /* + * [Multi-CPU/Codec-1]: cpu side only + * cpu8/cpu9/codec14/codec15/codec16 + * + * Because it will reach to the maximum of sound minor number, + * disable it so far. + * If you want to try it, please disable some other one instead. + */ + //&mcpu1 + + /* + * [Multi-CPU/Codec-2]: cpu side only + * cpu10/cpu11/cpu12/codec17/codec18 + * + * Because it will reach to the maximum of sound minor number, + * disable it so far. + * If you want to try it, please disable some other one instead. + */ + //&mcpu2 + /* * [DPCM]: both FE / BE * cpu3/cpu4/codec3 @@ -182,24 +225,24 @@ #address-cells = <1>; #size-cells = <0>; + /* [Multi-CPU-0] */ ports@0 { reg = <0>; #address-cells = <1>; #size-cells = <0>; - /* [Multi-CPU] */ - mcpu0: port@0 { reg = <0>; mcpu0_ep: endpoint { remote-endpoint = <&mcodec0_ep>; }; }; - port@1 { reg = <1>; mcpu1_ep: endpoint { remote-endpoint = <&cpu1_ep>; }; }; - port@2 { reg = <2>; mcpu2_ep: endpoint { remote-endpoint = <&cpu2_ep>; }; }; + mcpu0: port@0 { reg = <0>; mcpu00_ep: endpoint { remote-endpoint = <&mcodec00_ep>; }; }; + port@1 { reg = <1>; mcpu01_ep: endpoint { remote-endpoint = <&cpu1_ep>; }; }; + port@2 { reg = <2>; mcpu02_ep: endpoint { remote-endpoint = <&cpu2_ep>; }; }; }; - /* [Multi-Codec] */ + /* [Multi-Codec-0] */ ports@1 { reg = <1>; #address-cells = <1>; #size-cells = <0>; - port@0 { reg = <0>; mcodec0_ep: endpoint { remote-endpoint = <&mcpu0_ep>; }; }; - port@1 { reg = <1>; mcodec1_ep: endpoint { remote-endpoint = <&codec1_ep>; }; }; - port@2 { reg = <2>; mcodec2_ep: endpoint { remote-endpoint = <&codec2_ep>; }; }; + port@0 { reg = <0>; mcodec00_ep: endpoint { remote-endpoint = <&mcpu00_ep>; }; }; + port@1 { reg = <1>; mcodec01_ep: endpoint { remote-endpoint = <&codec1_ep>; }; }; + port@2 { reg = <2>; mcodec02_ep: endpoint { remote-endpoint = <&codec2_ep>; }; }; }; /* [DPCM-Multi]::BE */ @@ -241,6 +284,50 @@ port@1 { reg = <1>; smcodec1_ep: endpoint { remote-endpoint = <&codec12_ep>; }; }; port@2 { reg = <2>; smcodec2_ep: endpoint { remote-endpoint = <&codec13_ep>; }; }; }; + + /* [Multi-CPU-1] */ + ports@6 { + reg = <6>; + #address-cells = <1>; + #size-cells = <0>; + mcpu1: port@0 { reg = <0>; mcpu10_ep: endpoint { remote-endpoint = <&mcodec10_ep>; }; }; + port@1 { reg = <1>; mcpu11_ep: endpoint { remote-endpoint = <&cpu8_ep>; }; }; + port@2 { reg = <2>; mcpu12_ep: endpoint { remote-endpoint = <&cpu9_ep>; }; }; + }; + + /* [Multi-Codec-1] */ + ports@7 { + reg = <7>; + ch-maps = <0 1 1>; /* see [Multi-CPU/Codec-1] */ + #address-cells = <1>; + #size-cells = <0>; + port@0 { reg = <0>; mcodec10_ep: endpoint { remote-endpoint = <&mcpu10_ep>; }; }; + port@1 { reg = <1>; mcodec11_ep: endpoint { remote-endpoint = <&codec14_ep>; }; }; + port@2 { reg = <2>; mcodec12_ep: endpoint { remote-endpoint = <&codec15_ep>; }; }; + port@3 { reg = <3>; mcodec13_ep: endpoint { remote-endpoint = <&codec16_ep>; }; }; + }; + + /* [Multi-CPU-2] */ + ports@8 { + reg = <8>; + ch-maps = <1 0 1>; /* see [Multi-CPU/Codec-2] */ + #address-cells = <1>; + #size-cells = <0>; + mcpu2: port@0 { reg = <0>; mcpu20_ep: endpoint { remote-endpoint = <&mcodec20_ep>; }; }; + port@1 { reg = <1>; mcpu21_ep: endpoint { remote-endpoint = <&cpu10_ep>; }; }; + port@2 { reg = <2>; mcpu22_ep: endpoint { remote-endpoint = <&cpu11_ep>; }; }; + port@3 { reg = <3>; mcpu23_ep: endpoint { remote-endpoint = <&cpu12_ep>; }; }; + }; + + /* [Multi-Codec-2] */ + ports@9 { + reg = <9>; + #address-cells = <1>; + #size-cells = <0>; + port@0 { reg = <0>; mcodec20_ep: endpoint { remote-endpoint = <&mcpu20_ep>; }; }; + port@1 { reg = <1>; mcodec21_ep: endpoint { remote-endpoint = <&codec17_ep>; }; }; + port@2 { reg = <2>; mcodec22_ep: endpoint { remote-endpoint = <&codec18_ep>; }; }; + }; }; dpcm { @@ -323,9 +410,9 @@ /* [Normal] */ cpu0: port@0 { reg = <0>; cpu0_ep: endpoint { remote-endpoint = <&codec0_ep>; }; }; - /* [Multi-CPU] */ - port@1 { reg = <1>; cpu1_ep: endpoint { remote-endpoint = <&mcpu1_ep>; }; }; - port@2 { reg = <2>; cpu2_ep: endpoint { remote-endpoint = <&mcpu2_ep>; }; }; + /* [Multi-CPU-0] */ + port@1 { reg = <1>; cpu1_ep: endpoint { remote-endpoint = <&mcpu01_ep>; }; }; + port@2 { reg = <2>; cpu2_ep: endpoint { remote-endpoint = <&mcpu02_ep>; }; }; /* [DPCM]::FE */ port@3 { reg = <3>; cpu3_ep: endpoint { remote-endpoint = <&fe00_ep>; }; }; @@ -337,6 +424,15 @@ /* [Semi-Multi] */ sm0: port@7 { reg = <7>; cpu7_ep: endpoint { remote-endpoint = <&smcodec0_ep>; }; }; + + /* [Multi-CPU-1] */ + port@8 { reg = <8>; cpu8_ep: endpoint { remote-endpoint = <&mcpu11_ep>; }; }; + port@9 { reg = <9>; cpu9_ep: endpoint { remote-endpoint = <&mcpu12_ep>; }; }; + + /* [Multi-CPU-2] */ + port@a { reg = <10>; cpu10_ep: endpoint { remote-endpoint = <&mcpu21_ep>; }; }; + port@b { reg = <11>; cpu11_ep: endpoint { remote-endpoint = <&mcpu22_ep>; }; }; + port@c { reg = <12>; cpu12_ep: endpoint { remote-endpoint = <&mcpu23_ep>; }; }; }; }; @@ -363,9 +459,9 @@ /* [Normal] */ port@0 { reg = <0>; codec0_ep: endpoint { remote-endpoint = <&cpu0_ep>; }; }; - /* [Multi-Codec] */ - port@1 { reg = <1>; codec1_ep: endpoint { remote-endpoint = <&mcodec1_ep>; }; }; - port@2 { reg = <2>; codec2_ep: endpoint { remote-endpoint = <&mcodec2_ep>; }; }; + /* [Multi-Codec-0] */ + port@1 { reg = <1>; codec1_ep: endpoint { remote-endpoint = <&mcodec01_ep>; }; }; + port@2 { reg = <2>; codec2_ep: endpoint { remote-endpoint = <&mcodec02_ep>; }; }; /* [DPCM]::BE */ port@3 { @@ -395,6 +491,14 @@ port@c { reg = <12>; codec12_ep: endpoint { remote-endpoint = <&smcodec1_ep>; }; }; port@d { reg = <13>; codec13_ep: endpoint { remote-endpoint = <&smcodec2_ep>; }; }; + /* [Multi-Codec-1] */ + port@e { reg = <14>; codec14_ep: endpoint { remote-endpoint = <&mcodec11_ep>; }; }; + port@f { reg = <15>; codec15_ep: endpoint { remote-endpoint = <&mcodec12_ep>; }; }; + port@10 { reg = <16>; codec16_ep: endpoint { remote-endpoint = <&mcodec13_ep>; }; }; + + /* [Multi-Codec-2] */ + port@11 { reg = <17>; codec17_ep: endpoint { remote-endpoint = <&mcodec21_ep>; }; }; + port@12 { reg = <18>; codec18_ep: endpoint { remote-endpoint = <&mcodec22_ep>; }; }; }; }; }; From 0bc2fba698c596609b9055971abf0a3b1604eb69 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 12 Oct 2023 01:32:13 +0000 Subject: [PATCH 04/10] dt-bindings: audio-graph-port: add ch-maps property This patch adds ch-maps property to enable handling CPU:Codec = N:M connection. Signed-off-by: Kuninori Morimoto --- Documentation/devicetree/bindings/sound/audio-graph-port.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/sound/audio-graph-port.yaml b/Documentation/devicetree/bindings/sound/audio-graph-port.yaml index 60b5e3fd1115fe..3c4b331e849847 100644 --- a/Documentation/devicetree/bindings/sound/audio-graph-port.yaml +++ b/Documentation/devicetree/bindings/sound/audio-graph-port.yaml @@ -19,6 +19,8 @@ definitions: properties: mclk-fs: $ref: simple-card.yaml#/definitions/mclk-fs + ch-maps: + $ref: /schemas/types.yaml#/definitions/uint32-array endpoint-base: allOf: From 731b3542d0b9557219e45169c235775d4b3a8f10 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Thu, 12 Oct 2023 14:13:38 -0400 Subject: [PATCH 05/10] fixup! ASoC: SOF: Intel: hda-dai: fix channel map configuration for aggregated dailink Use new 'ch_maps' and 'connected_node' introduced by Morimoto-san Signed-off-by: Pierre-Louis Bossart --- sound/soc/sof/intel/hda-dai.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/sof/intel/hda-dai.c b/sound/soc/sof/intel/hda-dai.c index f1e94b362857a3..53345ee7e3328e 100644 --- a/sound/soc/sof/intel/hda-dai.c +++ b/sound/soc/sof/intel/hda-dai.c @@ -476,8 +476,8 @@ int sdw_hda_dai_hw_params(struct snd_pcm_substream *substream, ch_mask = 0; for_each_rtd_codec_dais(rtd, j, codec_dai) { - if (rtd->dai_link->codec_ch_maps[j].connected_cpu_id == cpu_dai_id) - ch_mask |= rtd->dai_link->codec_ch_maps[j].ch_mask; + if (rtd->dai_link->ch_maps[j].connected_node == cpu_dai_id) + ch_mask |= rtd->dai_link->ch_maps[j].ch_mask; } ret = hdac_bus_eml_sdw_map_stream_ch(sof_to_bus(sdev), link_id, cpu_dai->id, From 3d6f7c8ec1a3b4eab8728be79c76e5dfb63f5d76 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Thu, 12 Oct 2023 14:16:09 -0400 Subject: [PATCH 06/10] fixup! ASoC: SOF: Intel: hda-dai-ops: fix HDaudio link format use 'ch_maps' and 'connected_node' introduced by Morimoto-san Signed-off-by: Pierre-Louis Bossart --- sound/soc/sof/intel/hda-dai-ops.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/sof/intel/hda-dai-ops.c b/sound/soc/sof/intel/hda-dai-ops.c index 9399b47cf644e1..68b0e6dfeaef5b 100644 --- a/sound/soc/sof/intel/hda-dai-ops.c +++ b/sound/soc/sof/intel/hda-dai-ops.c @@ -249,7 +249,7 @@ static unsigned int generic_calc_stream_format(struct snd_sof_dev *sdev, * stream channel count - the params are modified in soc-pcm based on the codec_ch_maps info */ for_each_rtd_codec_dais(rtd, codec_dai_id, codec_dai) - ch_mask |= rtd->dai_link->codec_ch_maps[codec_dai_id].ch_mask; + ch_mask |= rtd->dai_link->ch_maps[codec_dai_id].ch_mask; num_channels = hweight_long(ch_mask); if (num_channels != params_channels(params)) From f4ab7fae0fecbb4372eb9f3e7b3f1d19f6675d6c Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 17 Oct 2023 09:42:36 -0500 Subject: [PATCH 07/10] ASoC: soc-pcm: don't use ch_maps for FEs Otherwise there's a kernel oops Suggested-by: Bard Liao Signed-off-by: Pierre-Louis Bossart --- sound/soc/soc-pcm.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index c434fc19e253de..478615c5b8d27c 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -1056,7 +1056,11 @@ static int __soc_pcm_hw_params(struct snd_soc_pcm_runtime *rtd, /* copy params for each cpu */ tmp_params = *params; - /* + /* ch_map is only set in BE dai link */ + if (rtd->dai_link->dynamic) + goto run; + + /* * construct cpu channel mask by combining ch_mask of each * codec which maps to the cpu. * see @@ -1070,6 +1074,7 @@ static int __soc_pcm_hw_params(struct snd_soc_pcm_runtime *rtd, if (ch_mask) soc_pcm_codec_params_fixup(&tmp_params, ch_mask); +run: ret = snd_soc_dai_hw_params(cpu_dai, substream, &tmp_params); if (ret < 0) goto out; From 4d742833278c76322d709807ed006ff0cb979ce3 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 23 Oct 2023 17:06:20 -0500 Subject: [PATCH 08/10] fixup! ASoC: SOF: Intel: hda-dai: fix channel map configuration for aggregated dailink Signed-off-by: Pierre-Louis Bossart --- sound/soc/sof/intel/hda-dai.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sound/soc/sof/intel/hda-dai.c b/sound/soc/sof/intel/hda-dai.c index 53345ee7e3328e..2f82837f013d2d 100644 --- a/sound/soc/sof/intel/hda-dai.c +++ b/sound/soc/sof/intel/hda-dai.c @@ -436,8 +436,8 @@ int sdw_hda_dai_hw_params(struct snd_pcm_substream *substream, 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); const struct hda_dai_widget_dma_ops *ops; + struct snd_soc_dai_link_ch_map *ch_maps; struct hdac_ext_stream *hext_stream; - struct snd_soc_dai *codec_dai; struct snd_soc_dai *dai; struct snd_sof_dev *sdev; bool cpu_dai_found = false; @@ -475,9 +475,9 @@ int sdw_hda_dai_hw_params(struct snd_pcm_substream *substream, return -ENODEV; ch_mask = 0; - for_each_rtd_codec_dais(rtd, j, codec_dai) { - if (rtd->dai_link->ch_maps[j].connected_node == cpu_dai_id) - ch_mask |= rtd->dai_link->ch_maps[j].ch_mask; + for_each_link_ch_maps(rtd->dai_link, j, ch_maps) { + if (ch_maps[j].cpu == cpu_dai_id) + ch_mask |= ch_maps[j].ch_mask; } ret = hdac_bus_eml_sdw_map_stream_ch(sof_to_bus(sdev), link_id, cpu_dai->id, From 4858952ff17e8858dd9ec69423e04a8119f441d9 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 23 Oct 2023 17:12:15 -0500 Subject: [PATCH 09/10] fixup! ASoC: SOF: Intel: hda-dai-ops: fix HDaudio link format Signed-off-by: Pierre-Louis Bossart --- sound/soc/sof/intel/hda-dai-ops.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/sound/soc/sof/intel/hda-dai-ops.c b/sound/soc/sof/intel/hda-dai-ops.c index 68b0e6dfeaef5b..4b93f283413cf1 100644 --- a/sound/soc/sof/intel/hda-dai-ops.c +++ b/sound/soc/sof/intel/hda-dai-ops.c @@ -238,18 +238,18 @@ static unsigned int generic_calc_stream_format(struct snd_sof_dev *sdev, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); - struct snd_soc_dai *codec_dai; + struct snd_soc_dai_link_ch_map *ch_maps; unsigned int format_val; u32 ch_mask = 0; int num_channels; - int codec_dai_id; + int i; /* * if the multiple dais are handled by the same dailink, we may need to update the - * stream channel count - the params are modified in soc-pcm based on the codec_ch_maps info + * stream channel count - the params are modified in soc-pcm based on the ch_maps info */ - for_each_rtd_codec_dais(rtd, codec_dai_id, codec_dai) - ch_mask |= rtd->dai_link->ch_maps[codec_dai_id].ch_mask; + for_each_link_ch_maps(rtd->dai_link, i, ch_maps) + ch_mask |= ch_maps[i].ch_mask; num_channels = hweight_long(ch_mask); if (num_channels != params_channels(params)) From 7019e7e052887590bd79afe4ec6ee1455ac73dc9 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 23 Oct 2023 17:12:50 -0500 Subject: [PATCH 10/10] Revert "ASoC: soc-pcm: don't use ch_maps for FEs" This reverts commit f4ab7fae0fecbb4372eb9f3e7b3f1d19f6675d6c. Signed-off-by: Pierre-Louis Bossart --- sound/soc/soc-pcm.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index 478615c5b8d27c..c434fc19e253de 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -1056,11 +1056,7 @@ static int __soc_pcm_hw_params(struct snd_soc_pcm_runtime *rtd, /* copy params for each cpu */ tmp_params = *params; - /* ch_map is only set in BE dai link */ - if (rtd->dai_link->dynamic) - goto run; - - /* + /* * construct cpu channel mask by combining ch_mask of each * codec which maps to the cpu. * see @@ -1074,7 +1070,6 @@ static int __soc_pcm_hw_params(struct snd_soc_pcm_runtime *rtd, if (ch_mask) soc_pcm_codec_params_fixup(&tmp_params, ch_mask); -run: ret = snd_soc_dai_hw_params(cpu_dai, substream, &tmp_params); if (ret < 0) goto out;