mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Matthew Schwartz <matthew.schwartz@linux.dev>
To: broonie@kernel.org
Cc: Matthew Schwartz <matthew.schwartz@linux.dev>,
	Liam Girdwood <lgirdwood@gmail.com>,
	Jaroslav Kysela <perex@perex.cz>, Takashi Iwai <tiwai@suse.com>,
	Vijendar Mukunda <Vijendar.Mukunda@amd.com>,
	Venkata Prasad Potturu <venkataprasad.potturu@amd.com>,
	Cezary Rojewski <cezary.rojewski@intel.com>,
	Peter Ujfalusi <peter.ujfalusi@linux.intel.com>,
	Bard Liao <yung-chuan.liao@linux.intel.com>,
	Kai Vehmanen <kai.vehmanen@linux.intel.com>,
	Pierre-Louis Bossart <pierre-louis.bossart@linux.dev>,
	linux-sound@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: [PATCH v2 4/5] ASoC: sdw_utils: honor empty DisCo port lists when creating DAI links
Date: Tue,  3 Nov 2026 12:05:38 -0800	[thread overview]
Message-ID: <20261103200540.3863272-5-matthew.schwartz@linux.dev> (raw)
In-Reply-To: <20261103200540.3863272-1-matthew.schwartz@linux.dev>

The codec_info tables give each DAI a fixed direction, so a TAS2783
always gets an amp feedback capture link. The ASUS ROG Xbox Ally X20
BIOS declares mipi-sdw-source-port-list as 0 for both of its TAS2783s,
so the capture port has no DisCo properties and every open of the
SmartAmp capture PCM fails in sdw_program_slave_port_params() with
-EINVAL. The PCM is probed at session start, which logs a burst of
transport errors on every boot, and the same failure is reported on the
ASUS ProArt PX13.

Store the effective direction in each endpoint and, for codecs whose
driver takes its ports from the DisCo port lists, drop a direction when
the peripheral declares an empty list for it, and skip the endpoint when
neither direction is left. Other codecs keep the table direction, as
many of their drivers use fixed port masks. Since endpoints in one group
can now differ, the machine drivers take the stream name and DAI link ID
from an endpoint that is part of the stream, and only count and
initialize those endpoints.

Signed-off-by: Matthew Schwartz <matthew.schwartz@linux.dev>
---
 include/sound/soc_sdw_utils.h           | 16 +++++++
 sound/soc/amd/acp/acp-sdw-legacy-mach.c |  9 ++--
 sound/soc/amd/acp/acp-sdw-sof-mach.c    |  7 ++-
 sound/soc/intel/boards/sof_sdw.c        |  7 ++-
 sound/soc/sdw_utils/soc_sdw_utils.c     | 60 ++++++++++++++++++++-----
 5 files changed, 76 insertions(+), 23 deletions(-)

diff --git a/include/sound/soc_sdw_utils.h b/include/sound/soc_sdw_utils.h
index 418466438ea1c..be7403c89901b 100644
--- a/include/sound/soc_sdw_utils.h
+++ b/include/sound/soc_sdw_utils.h
@@ -97,6 +97,8 @@ struct asoc_sdw_codec_info {
 	const int aux_num;
 	/* Force AMP-style name_prefix handling (append AMP index) even if MIC/Jack DAIs exist */
 	const bool is_amp;
+	/* The driver takes its ports from the DisCo port lists */
+	const bool disco_ports;
 
 	int (*codec_card_late_probe)(struct snd_soc_card *card);
 
@@ -114,6 +116,7 @@ struct asoc_sdw_endpoint {
 	const char *codec_name;
 	const char *name_prefix;
 	bool include_sidecar;
+	bool direction[2]; /* dai_info direction limited by the DisCo port lists */
 
 	struct asoc_sdw_codec_info *codec_info;
 	const struct asoc_sdw_dai_info *dai_info;
@@ -128,6 +131,19 @@ struct asoc_sdw_dailink {
 	struct list_head endpoints;
 };
 
+/* First endpoint of a DAI link that takes part in the stream */
+static inline struct asoc_sdw_endpoint *
+asoc_sdw_first_endpoint(struct asoc_sdw_dailink *soc_dai, int stream)
+{
+	struct asoc_sdw_endpoint *soc_end;
+
+	list_for_each_entry(soc_end, &soc_dai->endpoints, list)
+		if (soc_end->direction[stream])
+			return soc_end;
+
+	return NULL;
+}
+
 extern struct asoc_sdw_codec_info codec_info_list[];
 int asoc_sdw_get_codec_info_list_count(void);
 
diff --git a/sound/soc/amd/acp/acp-sdw-legacy-mach.c b/sound/soc/amd/acp/acp-sdw-legacy-mach.c
index a8b62817b2344..84b602e12f8ce 100644
--- a/sound/soc/amd/acp/acp-sdw-legacy-mach.c
+++ b/sound/soc/amd/acp/acp-sdw-legacy-mach.c
@@ -196,8 +196,7 @@ static int create_sdw_dailink(struct snd_soc_card *card,
 		if (!soc_dai->num_devs[stream])
 			continue;
 
-		soc_end = list_first_entry(&soc_dai->endpoints,
-					   struct asoc_sdw_endpoint, list);
+		soc_end = asoc_sdw_first_endpoint(soc_dai, stream);
 
 		*be_id = soc_end->dai_info->dailink[stream];
 		if (*be_id < 0) {
@@ -262,7 +261,7 @@ static int create_sdw_dailink(struct snd_soc_card *card,
 			return -ENOMEM;
 
 		list_for_each_entry(soc_end, &soc_dai->endpoints, list) {
-			if (!soc_end->dai_info->direction[stream])
+			if (!soc_end->direction[stream])
 				continue;
 
 			/*
@@ -307,7 +306,7 @@ static int create_sdw_dailink(struct snd_soc_card *card,
 		(*dai_links)->ch_maps = codec_maps;
 
 		list_for_each_entry(soc_end, &soc_dai->endpoints, list) {
-			if (soc_end->dai_info->init)
+			if (soc_end->direction[stream] && soc_end->dai_info->init)
 				soc_end->dai_info->init(card, *dai_links,
 							soc_end->codec_info,
 							playback);
@@ -472,7 +471,7 @@ static int soc_card_dai_links_create(struct snd_soc_card *card)
 			continue;
 
 		list_for_each_entry(e, &d->endpoints, list) {
-			if (e->dai_info->direction[SNDRV_PCM_STREAM_CAPTURE] &&
+			if (e->direction[SNDRV_PCM_STREAM_CAPTURE] &&
 			    e->dai_info->dai_type == SOC_SDW_DAI_TYPE_AMP)
 				d->num_devs[SNDRV_PCM_STREAM_CAPTURE]--;
 		}
diff --git a/sound/soc/amd/acp/acp-sdw-sof-mach.c b/sound/soc/amd/acp/acp-sdw-sof-mach.c
index ec3e1f5f1052c..c3cccb75f2349 100644
--- a/sound/soc/amd/acp/acp-sdw-sof-mach.c
+++ b/sound/soc/amd/acp/acp-sdw-sof-mach.c
@@ -112,8 +112,7 @@ static int create_sdw_dailink(struct snd_soc_card *card,
 		if (!sof_dai->num_devs[stream])
 			continue;
 
-		sof_end = list_first_entry(&sof_dai->endpoints,
-					   struct asoc_sdw_endpoint, list);
+		sof_end = asoc_sdw_first_endpoint(sof_dai, stream);
 
 		*be_id = sof_end->dai_info->dailink[stream];
 		if (*be_id < 0) {
@@ -177,7 +176,7 @@ static int create_sdw_dailink(struct snd_soc_card *card,
 			return -ENOMEM;
 
 		list_for_each_entry(sof_end, &sof_dai->endpoints, list) {
-			if (!sof_end->dai_info->direction[stream])
+			if (!sof_end->direction[stream])
 				continue;
 
 			int link_num = ffs(sof_end->link_mask) - 1;
@@ -215,7 +214,7 @@ static int create_sdw_dailink(struct snd_soc_card *card,
 		(*dai_links)->ch_maps = codec_maps;
 
 		list_for_each_entry(sof_end, &sof_dai->endpoints, list) {
-			if (sof_end->dai_info->init)
+			if (sof_end->direction[stream] && sof_end->dai_info->init)
 				sof_end->dai_info->init(card, *dai_links,
 							sof_end->codec_info,
 							playback);
diff --git a/sound/soc/intel/boards/sof_sdw.c b/sound/soc/intel/boards/sof_sdw.c
index 72fe013fffe86..bb96186146f85 100644
--- a/sound/soc/intel/boards/sof_sdw.c
+++ b/sound/soc/intel/boards/sof_sdw.c
@@ -939,8 +939,7 @@ static int create_sdw_dailink(struct snd_soc_card *card,
 		if (!sof_dai->num_devs[stream])
 			continue;
 
-		sof_end = list_first_entry(&sof_dai->endpoints,
-					   struct asoc_sdw_endpoint, list);
+		sof_end = asoc_sdw_first_endpoint(sof_dai, stream);
 
 		*be_id = sof_end->dai_info->dailink[stream];
 		if (*be_id < 0) {
@@ -973,7 +972,7 @@ static int create_sdw_dailink(struct snd_soc_card *card,
 			return -ENOMEM;
 
 		list_for_each_entry(sof_end, &sof_dai->endpoints, list) {
-			if (!sof_end->dai_info->direction[stream])
+			if (!sof_end->direction[stream])
 				continue;
 
 			if (cur_link != sof_end->link_mask) {
@@ -1020,7 +1019,7 @@ static int create_sdw_dailink(struct snd_soc_card *card,
 		(*dai_links)->ch_maps = codec_maps;
 
 		list_for_each_entry(sof_end, &sof_dai->endpoints, list) {
-			if (sof_end->dai_info->init)
+			if (sof_end->direction[stream] && sof_end->dai_info->init)
 				sof_end->dai_info->init(card, *dai_links,
 							sof_end->codec_info,
 							playback);
diff --git a/sound/soc/sdw_utils/soc_sdw_utils.c b/sound/soc/sdw_utils/soc_sdw_utils.c
index 7a19de75f32b5..ae14b4be66818 100644
--- a/sound/soc/sdw_utils/soc_sdw_utils.c
+++ b/sound/soc/sdw_utils/soc_sdw_utils.c
@@ -248,6 +248,7 @@ struct asoc_sdw_codec_info codec_info_list[] = {
 		.part_id = 0x0000, /* TAS2783A */
 		.name_prefix = "tas2783",
 		.is_amp = true,
+		.disco_ports = true,
 		.dais = {
 			{
 				.direction = {true, true},
@@ -2407,6 +2408,32 @@ static int is_sdca_endpoint_present(struct device *dev,
 	return 0;
 }
 
+/* A DisCo port list that is present but empty means the stream cannot run */
+static bool asoc_sdw_has_ports(struct device *dev,
+			       const struct snd_soc_acpi_link_adr *adr_link,
+			       int adr_index, int stream)
+{
+	const char *prop = stream == SNDRV_PCM_STREAM_PLAYBACK ?
+			   "mipi-sdw-sink-port-list" : "mipi-sdw-source-port-list";
+	const char *sdw_codec_name;
+	struct device *sdw_dev;
+	u32 ports;
+	int ret;
+
+	sdw_codec_name = _asoc_sdw_get_codec_name(dev, adr_link, adr_index);
+	if (!sdw_codec_name)
+		return true;
+
+	sdw_dev = bus_find_device_by_name(&sdw_bus_type, NULL, sdw_codec_name);
+	if (!sdw_dev)
+		return true;
+
+	ret = device_property_read_u32(sdw_dev, prop, &ports);
+	put_device(sdw_dev);
+
+	return ret || ports;
+}
+
 int asoc_sdw_parse_sdw_endpoints(struct device *dev,
 				 struct asoc_sdw_mc_private *ctx,
 				 struct snd_soc_aux_dev *soc_aux,
@@ -2493,6 +2520,26 @@ int asoc_sdw_parse_sdw_endpoints(struct device *dev,
 
 				adr_end = &adr_dev->endpoints[j];
 				dai_info = &codec_info->dais[adr_end->num];
+
+				if (adr_end->num >= codec_info->dai_num) {
+					dev_err(dev,
+						"%d is too many endpoints for codec: 0x%x\n",
+						adr_end->num, codec_info->part_id);
+					return -EINVAL;
+				}
+
+				for_each_pcm_streams(stream)
+					soc_end->direction[stream] = dai_info->direction[stream] &&
+						(!codec_info->disco_ports ||
+						 asoc_sdw_has_ports(dev, adr_link, i, stream));
+
+				/* no stream can use this endpoint */
+				if (!soc_end->direction[SNDRV_PCM_STREAM_PLAYBACK] &&
+				    !soc_end->direction[SNDRV_PCM_STREAM_CAPTURE]) {
+					(*num_devs)--;
+					continue;
+				}
+
 				soc_dai = asoc_sdw_find_dailink(soc_dais, adr_end);
 
 				/*
@@ -2533,18 +2580,11 @@ int asoc_sdw_parse_sdw_endpoints(struct device *dev,
 					"Add dev: %d, 0x%llx end: %d, dai: %d, %c/%c to %s: %d\n",
 					ffs(adr_link->mask) - 1, adr_dev->adr,
 					adr_end->num, dai_info->dai_type,
-					dai_info->direction[SNDRV_PCM_STREAM_PLAYBACK] ? 'P' : '-',
-					dai_info->direction[SNDRV_PCM_STREAM_CAPTURE] ? 'C' : '-',
+					soc_end->direction[SNDRV_PCM_STREAM_PLAYBACK] ? 'P' : '-',
+					soc_end->direction[SNDRV_PCM_STREAM_CAPTURE] ? 'C' : '-',
 					adr_end->aggregated ? "group" : "solo",
 					adr_end->group_id);
 
-				if (adr_end->num >= codec_info->dai_num) {
-					dev_err(dev,
-						"%d is too many endpoints for codec: 0x%x\n",
-						adr_end->num, codec_info->part_id);
-					return -EINVAL;
-				}
-
 				for_each_pcm_streams(stream) {
 					if (dai_info->direction[stream] &&
 					    dai_info->dailink[stream] < 0) {
@@ -2555,7 +2595,7 @@ int asoc_sdw_parse_sdw_endpoints(struct device *dev,
 						return -EINVAL;
 					}
 
-					if (dai_info->direction[stream]) {
+					if (soc_end->direction[stream]) {
 						num_dais += !soc_dai->num_devs[stream];
 						soc_dai->num_devs[stream]++;
 						soc_dai->link_mask[stream] |= adr_link->mask;
-- 
2.56.0


  parent reply	other threads:[~2026-11-03 20:07 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-11-03 20:05 [PATCH v2 0/5] ASoC: support the ASUS ROG Xbox Ally X20 audio Matthew Schwartz
2026-11-03 20:05 ` [PATCH v2 1/5] ASoC: amd: acp-config: force SoundWire probe on ASUS RC74XA Matthew Schwartz
2026-11-03 20:05 ` [PATCH v2 2/5] ASoC: amd: acp: add ACP70 SoundWire machine entry for " Matthew Schwartz
2026-11-03 20:05 ` [PATCH v2 3/5] ASoC: tas2783-sdw: use the BIOS speaker ID in the firmware name Matthew Schwartz
2026-11-03 20:05 ` Matthew Schwartz [this message]
2026-11-04  3:01   ` [PATCH v2 4/5] ASoC: sdw_utils: honor empty DisCo port lists when creating DAI links Liao, Bard
2026-11-03 20:05 ` [PATCH v2 5/5] ASoC: rt721-sdca: wait for PDEs to reach PS0 after powering them up Matthew Schwartz

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20261103200540.3863272-5-matthew.schwartz@linux.dev \
    --to=matthew.schwartz@linux.dev \
    --cc=Vijendar.Mukunda@amd.com \
    --cc=broonie@kernel.org \
    --cc=cezary.rojewski@intel.com \
    --cc=kai.vehmanen@linux.intel.com \
    --cc=lgirdwood@gmail.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-sound@vger.kernel.org \
    --cc=perex@perex.cz \
    --cc=peter.ujfalusi@linux.intel.com \
    --cc=pierre-louis.bossart@linux.dev \
    --cc=tiwai@suse.com \
    --cc=venkataprasad.potturu@amd.com \
    --cc=yung-chuan.liao@linux.intel.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®