diff mbox series

[1/4] ASoC: makes CPU/Codec channel connection map more generic

Message ID 87a5sr8e7n.wl-kuninori.morimoto.gx@renesas.com (mailing list archive)
State Superseded
Headers show
Series ASoC: makes CPU/Codec channel connection map more generic | expand

Commit Message

Kuninori Morimoto Oct. 10, 2023, 1:21 a.m. UTC
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              | 48 ++++++++++++++++--
 sound/soc/intel/boards/sof_sdw.c | 14 +++---
 sound/soc/soc-core.c             | 83 ++++++++++++++++++++++++++++++++
 sound/soc/soc-dapm.c             | 47 +++++++-----------
 sound/soc/soc-pcm.c              | 73 +++++++++++++++-------------
 5 files changed, 191 insertions(+), 74 deletions(-)

Comments

Pierre-Louis Bossart Oct. 10, 2023, 1:40 p.m. UTC | #1
On 10/9/23 21:21, Kuninori Morimoto wrote:
> 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              | 48 ++++++++++++++++--
>  sound/soc/intel/boards/sof_sdw.c | 14 +++---
>  sound/soc/soc-core.c             | 83 ++++++++++++++++++++++++++++++++
>  sound/soc/soc-dapm.c             | 47 +++++++-----------
>  sound/soc/soc-pcm.c              | 73 +++++++++++++++-------------
>  5 files changed, 191 insertions(+), 74 deletions(-)
> 
> diff --git a/include/sound/soc.h b/include/sound/soc.h
> index 63b57f58cc569..13f1158df2b1e 100644
> --- a/include/sound/soc.h
> +++ b/include/sound/soc.h
> @@ -655,8 +655,50 @@ 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_maps is from CPU
> + *
> + *	.num_cpus   = 1;
> + *	.num_codecs = 1;
> + *	.connected_node = [0];
> + *
> + * CPU0 <---> Codec_x
> + * CPU1 <---> Codec_y
> + * CPU2 <---> Codec_z
> + *
> + *	.ch_maps is from CPU
> + *
> + *	.num_cpus   = 3;
> + *	.num_codecs = 3;
> + *	.connected_node = [x, y, z];
> + *
> + * CPU0 <---> Codec_x
> + * CPU1 <-+-> Codec_y
> + * CPU2 <-/
> + *
> + *	.ch_maps is from CPU
> + *
> + *	.num_cpus   = 3;
> + *	.num_codecs = 2;
> + *	.connected_node = [x, y, y];
> + *
> + *
> + * CPU_x <---> Codec0
> + * CPU_y <-+-> Codec1
> + *	   \-> Codec2
> + *
> + *	.ch_maps is from Codec

how would we know what the convention is? Is this based on the largest
number of dais, so here num_codecs > num_cpus so we use a codec-centric
convention? That would be worth explaining in clear text

> + *
> + *	.num_cpus   = 2;
> + *	.num_codecs = 3;
> + *	.connected_node = [x, y, y];
> + */
> +struct snd_soc_dai_link_ch_map {
> +	unsigned int connected_node;

connected_node is a scalar here and an array above. maybe split this
patch between a rename and a functionality change?

>  	unsigned int ch_mask;
>  };
>  
> @@ -688,7 +730,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
> diff --git a/sound/soc/intel/boards/sof_sdw.c b/sound/soc/intel/boards/sof_sdw.c
> index 226a74a4c340f..7927b729866d9 100644
> --- a/sound/soc/intel/boards/sof_sdw.c
> +++ b/sound/soc/intel/boards/sof_sdw.c
> @@ -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) {
> diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
> index c305e94762c39..a4bb4c29331cf 100644
> --- a/sound/soc/soc-core.c
> +++ b/sound/soc/soc-core.c
> @@ -1824,6 +1824,84 @@ int snd_soc_set_dmi_name(struct snd_soc_card *card, const char *flavour)
>  EXPORT_SYMBOL_GPL(snd_soc_set_dmi_name);
>  #endif /* CONFIG_DMI */
>  
> +#define MAX_DEFAULT_CONNECTION_MAP_SIZE 7

why 7?

> +static struct snd_soc_dai_link_ch_map default_connction_map1[MAX_DEFAULT_CONNECTION_MAP_SIZE] = {

typo: connection

this is repeated multiple times in comments below

> +	{ .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_connction_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_connection_map(struct snd_soc_card *card)
> +{
> +	struct snd_soc_dai_link *dai_link;
> +	int i, j, 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]
> +	 */
> +	for_each_card_prelinks(card, i, dai_link) {
> +
> +		/* 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_connction_maps");
> +			return -EINVAL;
> +		}
> +
> +		/* Compensate missing map for ... */
> +		if (dai_link->num_cpus == dai_link->num_codecs)
> +			dai_link->ch_maps = default_connction_map1; /* for 1:1 or N:N */
> +		else
> +			dai_link->ch_maps = default_connction_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 (j = 0; j < n; j++)
> +			if (dai_link->ch_maps[j].connected_node >= max) {
> +				dev_err(card->dev, "strange connected_node (%d) was added to ch_maps",

maybe elaborate on what "strange" might mean so that average users can
figure this out?

> +					dai_link->ch_maps[j].connected_node);
> +				return -EINVAL;
> +			}
> +	}
> +
> +	return 0;
> +}
> +
Kuninori Morimoto Oct. 10, 2023, 11:26 p.m. UTC | #2
Hi Pierre-Louis

Thank you for your review !

> We can test the next version (comments in separate mail) but we don't
> have a configuration with more cpu dais than codec dais I am afraid, so
> the best we can contribute is a non-regression for the N < M case.

Yes, it is enough. Thanks a lot !

> > + * CPU_x <---> Codec0
> > + * CPU_y <-+-> Codec1
> > + *	   \-> Codec2
> > + *
> > + *	.ch_maps is from Codec
> 
> how would we know what the convention is? Is this based on the largest
> number of dais, so here num_codecs > num_cpus so we use a codec-centric
> convention? That would be worth explaining in clear text

These samles is good enough I thought,
but yes, adding clear text is better. Will do in v2.

> > + *	.num_cpus   = 2;
> > + *	.num_codecs = 3;
> > + *	.connected_node = [x, y, y];
> > + */
> > +struct snd_soc_dai_link_ch_map {
> > +	unsigned int connected_node;
> 
> connected_node is a scalar here and an array above. maybe split this
> patch between a rename and a functionality change?

The sample image is simplified to avoid complex image.
But let's use more actual image if there is no misunderstanding.

	CPU0 <---> Codec_x
	CPU1 <-+-> Codec_y
	CPU2 <-/

	.ch_maps is from CPU

	.num_cpus   = 3;
	.num_codecs = 2;
=>	.ch_map[] = {{connected_node = x,},
	             {connected_node = y,},
	             {connected_node = y,}};


> > +#define MAX_DEFAULT_CONNECTION_MAP_SIZE 7
> 
> why 7?

No big reason, but I thought 7 is big enough as default.
We need to increase it if 7 was not enough for default.

	/* 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_connction_maps");
		return -EINVAL;
	}

> > +static struct snd_soc_dai_link_ch_map default_connction_map1[MAX_DEFAULT_CONNECTION_MAP_SIZE] = {
> 
> typo: connection

Oops, thank you for pointing it.

> > +		for (j = 0; j < n; j++)
> > +			if (dai_link->ch_maps[j].connected_node >= max) {
> > +				dev_err(card->dev, "strange connected_node (%d) was added to ch_maps",
> 
> maybe elaborate on what "strange" might mean so that average users can
> figure this out?

Thanks. Will fix in v2

Thank you for your help !!

Best regards
---
Kuninori Morimoto
diff mbox series

Patch

diff --git a/include/sound/soc.h b/include/sound/soc.h
index 63b57f58cc569..13f1158df2b1e 100644
--- a/include/sound/soc.h
+++ b/include/sound/soc.h
@@ -655,8 +655,50 @@  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_maps is from CPU
+ *
+ *	.num_cpus   = 1;
+ *	.num_codecs = 1;
+ *	.connected_node = [0];
+ *
+ * CPU0 <---> Codec_x
+ * CPU1 <---> Codec_y
+ * CPU2 <---> Codec_z
+ *
+ *	.ch_maps is from CPU
+ *
+ *	.num_cpus   = 3;
+ *	.num_codecs = 3;
+ *	.connected_node = [x, y, z];
+ *
+ * CPU0 <---> Codec_x
+ * CPU1 <-+-> Codec_y
+ * CPU2 <-/
+ *
+ *	.ch_maps is from CPU
+ *
+ *	.num_cpus   = 3;
+ *	.num_codecs = 2;
+ *	.connected_node = [x, y, y];
+ *
+ *
+ * CPU_x <---> Codec0
+ * CPU_y <-+-> Codec1
+ *	   \-> Codec2
+ *
+ *	.ch_maps is from Codec
+ *
+ *	.num_cpus   = 2;
+ *	.num_codecs = 3;
+ *	.connected_node = [x, y, y];
+ */
+struct snd_soc_dai_link_ch_map {
+	unsigned int connected_node;
 	unsigned int ch_mask;
 };
 
@@ -688,7 +730,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
diff --git a/sound/soc/intel/boards/sof_sdw.c b/sound/soc/intel/boards/sof_sdw.c
index 226a74a4c340f..7927b729866d9 100644
--- a/sound/soc/intel/boards/sof_sdw.c
+++ b/sound/soc/intel/boards/sof_sdw.c
@@ -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) {
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
index c305e94762c39..a4bb4c29331cf 100644
--- a/sound/soc/soc-core.c
+++ b/sound/soc/soc-core.c
@@ -1824,6 +1824,84 @@  int snd_soc_set_dmi_name(struct snd_soc_card *card, const char *flavour)
 EXPORT_SYMBOL_GPL(snd_soc_set_dmi_name);
 #endif /* CONFIG_DMI */
 
+#define MAX_DEFAULT_CONNECTION_MAP_SIZE 7
+static struct snd_soc_dai_link_ch_map default_connction_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_connction_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_connection_map(struct snd_soc_card *card)
+{
+	struct snd_soc_dai_link *dai_link;
+	int i, j, 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]
+	 */
+	for_each_card_prelinks(card, i, dai_link) {
+
+		/* 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_connction_maps");
+			return -EINVAL;
+		}
+
+		/* Compensate missing map for ... */
+		if (dai_link->num_cpus == dai_link->num_codecs)
+			dai_link->ch_maps = default_connction_map1; /* for 1:1 or N:N */
+		else
+			dai_link->ch_maps = default_connction_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 (j = 0; j < n; j++)
+			if (dai_link->ch_maps[j].connected_node >= max) {
+				dev_err(card->dev, "strange connected_node (%d) was added to ch_maps",
+					dai_link->ch_maps[j].connected_node);
+				return -EINVAL;
+			}
+	}
+
+	return 0;
+}
+
 static void soc_check_tplg_fes(struct snd_soc_card *card)
 {
 	struct snd_soc_component *component;
@@ -2030,6 +2108,11 @@  static int snd_soc_bind_card(struct snd_soc_card *card)
 
 	snd_soc_dapm_init(&card->dapm, card, NULL);
 
+	/* for keeping compatibility */
+	ret = snd_soc_compensate_connection_map(card);
+	if (ret < 0)
+		goto probe_end;
+
 	/* check whether any platform is ignore machine FE and using topology */
 	soc_check_tplg_fes(card);
 
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c
index 2512aadf95f71..3c7c2b16bd64a 100644
--- a/sound/soc/soc-dapm.c
+++ b/sound/soc/soc-dapm.c
@@ -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);
 		}
 	}
 }
diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c
index 8c168dc553f65..0bfff2ea111d7 100644
--- a/sound/soc/soc-pcm.c
+++ b/sound/soc/soc-pcm.c
@@ -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;
+			}
 		}
 	}