From b28eb4cfc2eb119c095b9150e74cb8e212aa671d Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Tue, 1 Sep 2026 21:54:49 +0800 Subject: [PATCH 1/3] soundwire: allow drivers to check whether the peripheral is present 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 whether a peripheral is present. 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..0cb7e4fef00e19 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 whether any peripheral is present on the bus. */ 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 86bfa9b69ef4e244abcf00f225c9a9c1598c0f69 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 | 16 +++++++++++----- include/linux/soundwire/sdw.h | 2 +- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c index e234f4d834a6d8..fbfbd0c5353245 100644 --- a/drivers/soundwire/bus.c +++ b/drivers/soundwire/bus.c @@ -314,13 +314,16 @@ int sdw_transfer(struct sdw_bus *bus, struct sdw_msg *msg) * sdw_show_ping_status() - Direct report of PING status, to be used by Peripheral drivers * @bus: SDW bus * @sync_delay: Delay before reading status + * + * returns 0 if there is no peripherals attached, 1 if there are peripherals attached + * or a negative error code. */ -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 +338,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 0cb7e4fef00e19..1b03bfad2e6bbe 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 bc0e408294fe959bd5051254d763d1b13500a80d 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 | 47 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 45 insertions(+), 2 deletions(-) diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index 4dbba9186b29d4..e1815bfe358b5d 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,53 @@ 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 (link_mask & BIT(slave->bus->link_id)) { + /* + * At least one peripheral is present on the link which means that this + * link has already been enumerated + */ + goto skip_wait_link_enumeration; + } + + 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; + } + +skip_wait_link_enumeration: + /* 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";