@@ -655,8 +655,68 @@ 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]
+ *
+ * if (num_cpus >= num_codecs)
+ * .ch_maps is [CPU] base
+ * else
+ * .ch_maps is [Codec] base
+ *
+ *-------------------------
+ * CPU0 <---> CodecX
+ *
+ * Because [num_cpus >= num_codecs]
+ * .ch_maps is [CPU] base
+ *
+ * .num_cpus = 1;
+ * .num_codecs = 1;
+ * .ch_maps[] = {{.connected_node = X; }}; CPU0 <-> CodecX
+ *
+ *-------------------------
+ * CPU0 <---> CodecX
+ * CPU1 <---> CodecY
+ * CPU2 <---> CodecZ
+ *
+ * Because [num_cpus >= num_codecs]
+ * .ch_maps is [CPU] base
+ *
+ * .num_cpus = 3;
+ * .num_codecs = 3;
+ * .ch_maps[] = {{.connected_node = X; }, CPU0 <-> CodecX
+ * {.connected_node = Y; }, CPU1 <-> CodecY
+ * {.connected_node = Z; }}; CPU2 <-> CodecZ
+ *
+ *-------------------------
+ * CPU0 <---> CodecX
+ * CPU1 <-+-> CodecY
+ * CPU2 <-/
+ *
+ * Because [num_cpus >= num_codecs]
+ * .ch_maps is [CPU] base
+ *
+ * .num_cpus = 3;
+ * .num_codecs = 2;
+ * .ch_maps[] = {{.connected_node = X; }, CPU0 <-> CodecX
+ * {.connected_node = Y; }, CPU1 <-> CodecY
+ * {.connected_node = Y; }}; CPU2 <-> CodecY
+ *
+ *-------------------------
+ * CPU_X <---> Codec0
+ * CPU_Y <-+-> Codec1
+ * \-> Codec2
+ *
+ * Because [num_cpus < num_codecs]
+ * .ch_maps is [Codec] base
+ *
+ * .num_cpus = 2;
+ * .num_codecs = 3;
+ * .ch_maps[] = {{.connected_node = X; }, Codec0 <-> CPU_X
+ * {.connected_node = Y; }, Codec1 <-> CPU_Y
+ * {.connected_node = Y; }}; Codec2 <-> CPU_Y
+ */
+struct snd_soc_dai_link_ch_map {
+ unsigned int connected_node;
unsigned int ch_mask;
};
@@ -688,7 +748,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;
+ 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
@@ -579,7 +579,7 @@ int sdw_hw_params(struct snd_pcm_substream *substream,
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 */
@@ -607,9 +607,9 @@ int sdw_hw_params(struct snd_pcm_substream *substream,
*/
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)
+ if (rtd->dai_link->ch_maps[j].connected_node != i)
continue;
- rtd->dai_link->codec_ch_maps[j].ch_mask = ch_mask << (j * step);
+ rtd->dai_link->ch_maps[j].ch_mask = ch_mask << (j * step);
}
}
return 0;
@@ -1350,7 +1350,7 @@ 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;
@@ -1358,7 +1358,7 @@ static void set_dailink_map(struct snd_soc_dai_link_codec_ch_map *sdw_codec_ch_m
step = codec_num / cpu_num;
for (i = 0; i < codec_num; i++)
- sdw_codec_ch_maps[i].connected_cpu_id = i / step;
+ sdw_codec_ch_maps[i].connected_node = i / step;
}
static const char * const type_strings[] = {"SimpleJack", "SmartAmp", "SmartMic"};
@@ -1453,7 +1453,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[] = {
@@ -1530,7 +1530,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) {
@@ -1015,6 +1015,83 @@ static int soc_dai_link_sanity_check(struct snd_soc_card *card,
return -EINVAL;
}
+#define MAX_DEFAULT_CONNECTION_MAP_SIZE 7
+static struct snd_soc_dai_link_ch_map default_connection_map1[MAX_DEFAULT_CONNECTION_MAP_SIZE] = {
+ { .connected_node = 0 },
+ { .connected_node = 1 },
+ { .connected_node = 2 },
+ { .connected_node = 3 },
+ { .connected_node = 4 },
+ { .connected_node = 5 },
+ { .connected_node = 6 },
+};
+static struct snd_soc_dai_link_ch_map default_connection_map2[MAX_DEFAULT_CONNECTION_MAP_SIZE] = {
+ { .connected_node = 0 },
+ { .connected_node = 0 },
+ { .connected_node = 0 },
+ { .connected_node = 0 },
+ { .connected_node = 0 },
+ { .connected_node = 0 },
+ { .connected_node = 0 },
+};
+static int snd_soc_compensate_channel_connection_map(struct snd_soc_card *card,
+ struct snd_soc_dai_link *dai_link)
+{
+ int n, 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_CONNECTION_MAP_SIZE ||
+ dai_link->num_codecs > MAX_DEFAULT_CONNECTION_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_connection_map1; /* for 1:1 or N:N */
+ else
+ dai_link->ch_maps = default_connection_map2; /* for 1:N or N:1 */
+
+sanity_check:
+ if (dai_link->num_cpus >= dai_link->num_codecs) {
+ n = dai_link->num_cpus;
+ max = dai_link->num_codecs;
+ } else {
+ n = dai_link->num_codecs;
+ max = dai_link->num_cpus;
+ }
+
+ for (int i = 0; i < n; i++)
+ if (dai_link->ch_maps[i].connected_node >= max) {
+ dev_err(card->dev,
+ "dai_link->ch_maps[%d].connected_node (= %d) is "
+ "larger than max (= %d)",
+ i, dai_link->ch_maps[i].connected_node, max);
+ return -EINVAL;
+ }
+
+ 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 +1198,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;
}
@@ -4426,6 +4426,7 @@ 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;
@@ -4438,39 +4439,25 @@ 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]
+ */
+ /* .ch_map is from CPU */
+ if (rtd->dai_link->num_cpus >= rtd->dai_link->num_codecs) {
+ for_each_rtd_cpu_dais(rtd, i, cpu_dai) {
+ codec_dai = snd_soc_rtd_to_codec(rtd, rtd->dai_link->ch_maps[i].connected_node);
+ dapm_connect_dai_pair(card, rtd, codec_dai, cpu_dai);
+ }
+ }
+ /* .ch_map is from Codec */
+ else {
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));
+ cpu_dai = snd_soc_rtd_to_cpu(rtd, rtd->dai_link->ch_maps[i].connected_node);
+
+ dapm_connect_dai_pair(card, rtd, codec_dai, cpu_dai);
}
- } 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);
}
}
}
@@ -1043,7 +1043,6 @@ static int __soc_pcm_hw_params(struct snd_soc_pcm_runtime *rtd,
for_each_rtd_cpu_dais(rtd, i, cpu_dai) {
unsigned int ch_mask = 0;
- int j;
/*
* Skip CPUs which don't support the current stream
@@ -1055,22 +1054,28 @@ 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;
+ if (rtd->dai_link->num_cpus >= rtd->dai_link->num_codecs) {
+ /* .ch_map is from CPU */
+ ch_mask = rtd->dai_link->ch_maps[i].ch_mask;
+ } else {
+ int j;
+
+ /* .ch_map is from Codec */
+ for_each_rtd_codec_dais(rtd, j, codec_dai)
+ if (rtd->dai_link->ch_maps[j].connected_node == i)
+ ch_mask |= rtd->dai_link->ch_maps[j].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;
@@ -2824,36 +2829,36 @@ static int soc_get_playback_capture(struct snd_soc_pcm_runtime *rtd,
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;
- }
+ /*
+ * see
+ * soc.h :: [dai_link->ch_maps Image sample]
+ */
+ /* .ch_map is from CPU */
+ if (dai_link->num_cpus >= dai_link->num_codecs) {
+ for_each_rtd_cpu_dais(rtd, i, cpu_dai) {
+ codec_dai = snd_soc_rtd_to_codec(rtd, dai_link->ch_maps[i].connected_node);
- 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;
+ if (snd_soc_dai_stream_valid(codec_dai, SNDRV_PCM_STREAM_PLAYBACK) &&
+ snd_soc_dai_stream_valid(cpu_dai, cpu_playback))
+ has_playback = 1;
+ if (snd_soc_dai_stream_valid(codec_dai, SNDRV_PCM_STREAM_CAPTURE) &&
+ snd_soc_dai_stream_valid(cpu_dai, cpu_capture))
+ has_capture = 1;
}
+ }
+ /* .ch_map is from Codec */
+ else {
+ for_each_rtd_codec_dais(rtd, i, codec_dai) {
+ cpu_dai = snd_soc_rtd_to_cpu(rtd, dai_link->ch_maps[i].connected_node);
+
+ if (snd_soc_dai_stream_valid(codec_dai, SNDRV_PCM_STREAM_PLAYBACK) &&
+ snd_soc_dai_stream_valid(cpu_dai, cpu_playback))
+ has_playback = 1;
+ if (snd_soc_dai_stream_valid(codec_dai, SNDRV_PCM_STREAM_CAPTURE) &&
+ snd_soc_dai_stream_valid(cpu_dai, cpu_capture))
+ has_capture = 1;
- if (snd_soc_dai_stream_valid(codec_dai, SNDRV_PCM_STREAM_PLAYBACK) &&
- snd_soc_dai_stream_valid(cpu_dai, cpu_playback))
- has_playback = 1;
- if (snd_soc_dai_stream_valid(codec_dai, SNDRV_PCM_STREAM_CAPTURE) &&
- snd_soc_dai_stream_valid(cpu_dai, cpu_capture))
- has_capture = 1;
+ }
}
}
Current ASoC CPU:Codec = N:M connection is using connection mapping idea, but it is used for CPU < Codec 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. Because it will use default mapping, no conversion patch is needed to exising CPU/Codec drivers. More over, CPU:Codec = M:N (M > N) also supported in the same time. Link: https://lore.kernel.org/r/87fs6wuszr.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> --- include/sound/soc.h | 66 +++++++++++++++++++++++-- sound/soc/intel/boards/sof_sdw.c | 14 +++--- sound/soc/soc-core.c | 84 +++++++++++++++++++++++++++++++- sound/soc/soc-dapm.c | 47 +++++++----------- sound/soc/soc-pcm.c | 73 ++++++++++++++------------- 5 files changed, 209 insertions(+), 75 deletions(-)