From 1cd69230908e931aa0539685789681ea0d3ca767 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Tue, 1 Sep 2026 21:54:49 +0800 Subject: [PATCH 1/3] soundwire: make sure a bus is enumerated before checking is a peripheral present or not A ghost peripheral may be listed in the ACPI table and we want to skip it. Add enumeration_complete and is_present in struct sdw_bus{} allow the driver to wait and check if a peripheral is present or not. Signed-off-by: Bard Liao --- drivers/soundwire/bus.c | 7 +++++++ include/linux/soundwire/sdw.h | 5 +++++ 2 files changed, 12 insertions(+) diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c index 92d76568e9e751..e234f4d834a6d8 100644 --- a/drivers/soundwire/bus.c +++ b/drivers/soundwire/bus.c @@ -161,7 +161,13 @@ int sdw_bus_master_add(struct sdw_bus *bus, struct device *parent, bus->params.curr_dr_freq = bus->params.max_dr_freq; bus->params.curr_bank = SDW_BANK0; bus->params.next_bank = SDW_BANK1; + /* + * Set is_present = true by default. It will be set to false when no peripherals + * are attached on the bus. + */ + bus->is_present = true; + init_completion(&bus->enumeration_complete); return 0; } EXPORT_SYMBOL(sdw_bus_master_add); @@ -847,6 +853,7 @@ static int sdw_program_device_num(struct sdw_bus *bus, bool *programmed) if (ret == -ENODATA) { /* end of device id reads */ dev_dbg(bus->dev, "No more devices to enumerate\n"); ret = 0; + complete_all(&bus->enumeration_complete); break; } if (ret < 0) { diff --git a/include/linux/soundwire/sdw.h b/include/linux/soundwire/sdw.h index f710e5932b4b23..26d39c95eb3b45 100644 --- a/include/linux/soundwire/sdw.h +++ b/include/linux/soundwire/sdw.h @@ -1001,6 +1001,8 @@ struct sdw_stream_runtime { * transport and port parameters * @defer_msg: Defer message * @params: Current bus parameters + * @enumeration_complete: completion utility to control potential races between + * enumeration completion and peripheral presence checks. * @stream_refcount: number of streams currently using this bus * @bpt_stream_refcount: number of BTP streams currently using this bus (should * be zero or one, multiple streams per link is not supported). @@ -1029,6 +1031,7 @@ struct sdw_stream_runtime { * are supported. This flag is populated by drivers after reading * appropriate firmware (ACPI/DT). * @lane_used_bandwidth: how much bandwidth in bits per second is used by each lane + * @is_present: indicates is there any peripheral present on the bus or not. */ struct sdw_bus { struct device *dev; @@ -1042,6 +1045,7 @@ struct sdw_bus { struct list_head m_rt_list; struct sdw_defer defer_msg; struct sdw_bus_params params; + struct completion enumeration_complete; int stream_refcount; int bpt_stream_refcount; struct sdw_stream_runtime *bpt_stream; @@ -1064,6 +1068,7 @@ struct sdw_bus { #endif bool multi_link; unsigned int lane_used_bandwidth[SDW_MAX_LANES]; + bool is_present; }; struct sdw_stream_runtime *sdw_alloc_stream(const char *stream_name, enum sdw_stream_type type); From b0643611dcd5819ee062e7a84389b32c868d4e42 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Wed, 2 Sep 2026 13:55:15 +0800 Subject: [PATCH 2/3] soundwire: change sdw_show_ping_status type to int So that the caller can get the ping results. Signed-off-by: Bard Liao --- drivers/soundwire/bus.c | 13 ++++++++----- include/linux/soundwire/sdw.h | 2 +- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c index e234f4d834a6d8..8c0724e1deabec 100644 --- a/drivers/soundwire/bus.c +++ b/drivers/soundwire/bus.c @@ -315,12 +315,12 @@ int sdw_transfer(struct sdw_bus *bus, struct sdw_msg *msg) * @bus: SDW bus * @sync_delay: Delay before reading status */ -void sdw_show_ping_status(struct sdw_bus *bus, bool sync_delay) +int sdw_show_ping_status(struct sdw_bus *bus, bool sync_delay) { u32 status; if (!bus->ops->read_ping_status) - return; + return -ENOTSUPP; /* * wait for peripheral to sync if desired. 10-15ms should be more than @@ -335,10 +335,13 @@ void sdw_show_ping_status(struct sdw_bus *bus, bool sync_delay) mutex_unlock(&bus->msg_lock); - if (!status) + if (!status) { dev_warn(bus->dev, "%s: no peripherals attached\n", __func__); - else - dev_dbg(bus->dev, "PING status: %#x\n", status); + return 0; + } + + dev_dbg(bus->dev, "PING status: %#x\n", status); + return 1; } EXPORT_SYMBOL(sdw_show_ping_status); diff --git a/include/linux/soundwire/sdw.h b/include/linux/soundwire/sdw.h index 26d39c95eb3b45..58a097c8cdcc58 100644 --- a/include/linux/soundwire/sdw.h +++ b/include/linux/soundwire/sdw.h @@ -901,7 +901,7 @@ int sdw_bus_master_add(struct sdw_bus *bus, struct device *parent, struct fwnode_handle *fwnode); void sdw_bus_master_delete(struct sdw_bus *bus); -void sdw_show_ping_status(struct sdw_bus *bus, bool sync_delay); +int sdw_show_ping_status(struct sdw_bus *bus, bool sync_delay); /** * struct sdw_port_config: Master or Slave Port configuration From 6ada487ab72aa10b5cfe820052a678c7eaa7a1de Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Mon, 20 Apr 2026 20:27:41 +0800 Subject: [PATCH 3/3] ASoC: SOF: Intel: wait and verifies the presence of SoundWire peripherals Wait and verifies the presence of SoundWire peripherals listed in the ACPI table. This prevents the system from probing non-existent (ghost) SoundWire devices. Signed-off-by: Bard Liao --- sound/soc/sof/intel/hda.c | 38 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index 4dbba9186b29d4..dc605a13d59a96 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -1304,6 +1304,8 @@ static struct snd_soc_acpi_adr_device *find_acpi_adr_device(struct device *dev, return adr_dev; } +#define SDW_ENUM_TIMEOUT_MS 3000 + static struct snd_soc_acpi_mach *hda_sdw_machine_select(struct snd_sof_dev *sdev) { struct snd_sof_pdata *pdata = sdev->pdata; @@ -1313,7 +1315,9 @@ static struct snd_soc_acpi_mach *hda_sdw_machine_select(struct snd_sof_dev *sdev struct sdw_peripherals *peripherals; struct snd_soc_acpi_mach *mach; struct sof_intel_hda_dev *hdev; + struct sdw_slave *slave; int link_index, link_num; + unsigned long time; int amp_index = 1; u32 link_mask = 0; int i; @@ -1415,14 +1419,44 @@ static struct snd_soc_acpi_mach *hda_sdw_machine_select(struct snd_sof_dev *sdev if (!links) return NULL; + /* + * Recalculate the link_mask as a link will be empty if all peripherals on the link are + * not enumerated + */ + link_mask = 0; /* Generate snd_soc_acpi_link_adr struct for each peripheral reported by the ACPI table */ for (i = 0; i < peripherals->num_peripherals; i++) { + slave = peripherals->array[i]; + + if (!slave->bus->is_present) + continue; + + if (sdw_show_ping_status(slave->bus, true) == 0) { + /* no peripherals attached on this link */ + slave->bus->is_present = false; + continue; + } + + time = wait_for_completion_timeout(&slave->bus->enumeration_complete, + msecs_to_jiffies(SDW_ENUM_TIMEOUT_MS)); + if (!time) { + dev_warn(slave->bus->dev, "No peripheral is present\n"); + slave->bus->is_present = false; + continue; + } + + /* Check if the SoundWire peripheral is present */ + if (!slave->dev_num_sticky) { + dev_warn(&slave->dev, "SoundWire peripheral is not present\n"); + continue; + } /* link_index = the number of used links below the current link */ - link_index = hweight32(link_mask & (BIT(peripherals->array[i]->bus->link_id) - 1)); - links[link_index].adr_d = find_acpi_adr_device(sdev->dev, peripherals->array[i], + link_index = hweight32(link_mask & (BIT(slave->bus->link_id) - 1)); + links[link_index].adr_d = find_acpi_adr_device(sdev->dev, slave, &links[link_index], &_index); if (!links[link_index].adr_d) return NULL; + link_mask |= BIT(slave->bus->link_id); } mach->drv_name = "sof_sdw";