[PATCH 2/7] soundwire: intel: Allow for dynamic assignment of PDIs
From: Cezary Rojewski
Date: Mon Nov 02 2026 - 13:12:01 EST
Currently, PDIs are assigned upfront based on the DAI IDs. Some drivers
do not assign anything upfront and request a free PDI when streaming is
about to start.
Provide assign/release() functions to allow them to be assigned
dynamically. This also streamlines the process of assigning/releasing
PDIs with what is done in HDAudio library (sound/hda) for HDAudio
streams, see snd_hdac_stream_assign/release().
As resource availability-check is typically done during dai->startup()
whilst hardware/firmware configuration occurs on dai->hw_params(), add
sdw_intel_pdi_config() to maintain this logical split - assign on
start(), configure on hw_params(). Existing code does all of that in
intel_hw_params() so the logical split is not possible.
Cc: Vinod Koul <vkoul@xxxxxxxxxx>
Signed-off-by: Cezary Rojewski <cezary.rojewski@xxxxxxxxx>
---
drivers/soundwire/cadence_master.c | 64 +++++++++++++++++++++++++++
drivers/soundwire/cadence_master.h | 24 ++--------
drivers/soundwire/intel.c | 16 +++++++
include/linux/soundwire/sdw_cadence.h | 35 +++++++++++++++
include/linux/soundwire/sdw_intel.h | 7 +++
5 files changed, 125 insertions(+), 21 deletions(-)
create mode 100644 include/linux/soundwire/sdw_cadence.h
diff --git a/drivers/soundwire/cadence_master.c b/drivers/soundwire/cadence_master.c
index a0a63b76793e..9375db9c08a2 100644
--- a/drivers/soundwire/cadence_master.c
+++ b/drivers/soundwire/cadence_master.c
@@ -17,6 +17,7 @@
#include <linux/pm_runtime.h>
#include <linux/soundwire/sdw_registers.h>
#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_cadence.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <linux/workqueue.h>
@@ -1835,6 +1836,7 @@ int sdw_cdns_probe(struct sdw_cdns *cdns)
mutex_init(&cdns->status_update_lock);
+ spin_lock_init(&cdns->pdi_lock);
INIT_WORK(&cdns->work, cdns_update_slave_status_work);
INIT_DELAYED_WORK(&cdns->attach_dwork, cdns_check_attached_status_dwork);
@@ -1991,6 +1993,68 @@ struct sdw_cdns_pdi *sdw_cdns_alloc_pdi(struct sdw_cdns *cdns,
}
EXPORT_SYMBOL(sdw_cdns_alloc_pdi);
+/**
+ * sdw_cdns_assign_pdi - Assign a PDI
+ * @bus: SoundWire bus instance
+ * @dir: Data direction
+ *
+ * Return: A valid PDI instance or %NULL if none available.
+ */
+struct sdw_cdns_pdi *sdw_cdns_assign_pdi(struct sdw_bus *bus, u32 dir)
+{
+ struct sdw_cdns *cdns = bus_to_cdns(bus);
+ struct sdw_cdns_streams *stream = &cdns->pcm;
+ struct sdw_cdns_pdi *pdis, *pdi;
+ size_t num_pdis;
+ int i;
+
+ if (dir == SDW_DATA_DIR_RX) {
+ num_pdis = stream->num_in;
+ pdis = stream->in;
+ } else {
+ num_pdis = stream->num_out;
+ pdis = stream->out;
+ }
+
+ spin_lock(&cdns->pdi_lock);
+ for (i = 0; i < num_pdis; i++) {
+ if (!pdis[i].assigned) {
+ pdi = &pdis[i];
+ goto found;
+ }
+ }
+
+ /* Indexes 0/1 are reserved for the Bulk transfer. */
+ for (i = 2; i < stream->num_bd; i++) {
+ if (!stream->bd[i].assigned) {
+ pdi = &stream->bd[i];
+ goto found;
+ }
+ }
+
+ spin_unlock(&cdns->pdi_lock);
+ return NULL;
+found:
+ pdi->dir = dir;
+ pdi->assigned = true;
+ spin_unlock(&cdns->pdi_lock);
+ return pdi;
+}
+EXPORT_SYMBOL(sdw_cdns_assign_pdi);
+
+/**
+ * sdw_cdns_release_pdi() - Release a PDI
+ * @pdi: PDI instance
+ */
+void sdw_cdns_release_pdi(struct sdw_bus *bus, struct sdw_cdns_pdi *pdi)
+{
+ struct sdw_cdns *cdns = bus_to_cdns(bus);
+
+ guard(spinlock)(&cdns->pdi_lock);
+ pdi->assigned = false;
+}
+EXPORT_SYMBOL(sdw_cdns_release_pdi);
+
/*
* the MIPI SoundWire CRC8 polynomial is X^8 + X^6 + X^3 + X^2 + 1, MSB first
* The value is (1)01001101 = 0x4D
diff --git a/drivers/soundwire/cadence_master.h b/drivers/soundwire/cadence_master.h
index f4e41a9ab165..f52c9cbec8a9 100644
--- a/drivers/soundwire/cadence_master.h
+++ b/drivers/soundwire/cadence_master.h
@@ -6,6 +6,8 @@
#ifndef __SDW_CADENCE_H
#define __SDW_CADENCE_H
+#include <linux/soundwire/sdw_cadence.h>
+
#define SDW_CADENCE_GSYNC_KHZ 4 /* 4 kHz */
#define SDW_CADENCE_GSYNC_HZ (SDW_CADENCE_GSYNC_KHZ * 1000)
@@ -17,27 +19,6 @@
#define SDW_CADENCE_MCP_IP_OFFSET 0x4000
-/**
- * struct sdw_cdns_pdi: PDI (Physical Data Interface) instance
- *
- * @num: pdi number
- * @intel_alh_id: link identifier
- * @l_ch_num: low channel for PDI
- * @h_ch_num: high channel for PDI
- * @ch_count: total channel count for PDI
- * @dir: data direction
- * @type: stream type, (only PCM supported)
- */
-struct sdw_cdns_pdi {
- int num;
- int intel_alh_id;
- int l_ch_num;
- int h_ch_num;
- int ch_count;
- enum sdw_data_direction dir;
- enum sdw_stream_type type;
-};
-
/**
* struct sdw_cdns_streams: Cadence stream data structure
*
@@ -140,6 +121,7 @@ struct sdw_cdns {
int num_ports;
struct sdw_cdns_streams pcm;
+ spinlock_t pdi_lock;
int pdi_loopback_source;
int pdi_loopback_target;
diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c
index af65214836b4..d53fa3d299bc 100644
--- a/drivers/soundwire/intel.c
+++ b/drivers/soundwire/intel.c
@@ -674,6 +674,22 @@ intel_pdi_alh_configure(struct sdw_intel *sdw, struct sdw_cdns_pdi *pdi)
intel_writel(alh, SDW_ALH_STRMZCFG(pdi->intel_alh_id), conf);
}
+/**
+ * sdw_intel_pdi_config - Configure specified PDI
+ * @bus: SoundWire bus instance
+ * @pdi: PDI instance to configure
+ */
+void sdw_intel_pdi_config(struct sdw_bus *bus, struct sdw_cdns_pdi *pdi)
+{
+ struct sdw_cdns *cdns = bus_to_cdns(bus);
+ struct sdw_intel *sdw = cdns_to_intel(cdns);
+
+ intel_pdi_shim_configure(sdw, pdi);
+ intel_pdi_alh_configure(sdw, pdi);
+ sdw_cdns_config_stream(cdns, pdi->ch_count, pdi->dir, pdi);
+}
+EXPORT_SYMBOL(sdw_intel_pdi_config);
+
static int intel_params_stream(struct sdw_intel *sdw,
struct snd_pcm_substream *substream,
struct snd_soc_dai *dai,
diff --git a/include/linux/soundwire/sdw_cadence.h b/include/linux/soundwire/sdw_cadence.h
new file mode 100644
index 000000000000..331b21adafc4
--- /dev/null
+++ b/include/linux/soundwire/sdw_cadence.h
@@ -0,0 +1,35 @@
+/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */
+/* Copyright(c) 2024-2026 Intel Corporation */
+
+#ifndef ___SDW_CADENCE_H
+#define ___SDW_CADENCE_H
+
+#include <linux/soundwire/sdw.h>
+
+/**
+ * struct sdw_cdns_pdi: PDI (Physical Data Interface) instance
+ *
+ * @num: pdi number
+ * @intel_alh_id: link identifier
+ * @l_ch_num: low channel for PDI
+ * @h_ch_num: high channel for PDI
+ * @ch_count: total channel count for PDI
+ * @dir: data direction
+ * @type: stream type, (only PCM supported)
+ * @assigned: whether PDI is in use
+ */
+struct sdw_cdns_pdi {
+ int num;
+ int intel_alh_id;
+ int l_ch_num;
+ int h_ch_num;
+ int ch_count;
+ enum sdw_data_direction dir;
+ enum sdw_stream_type type;
+ bool assigned;
+};
+
+struct sdw_cdns_pdi *sdw_cdns_assign_pdi(struct sdw_bus *bus, u32 dir);
+void sdw_cdns_release_pdi(struct sdw_bus *bus, struct sdw_cdns_pdi *pdi);
+
+#endif
diff --git a/include/linux/soundwire/sdw_intel.h b/include/linux/soundwire/sdw_intel.h
index 83021cc0f5b8..120f16b8d478 100644
--- a/include/linux/soundwire/sdw_intel.h
+++ b/include/linux/soundwire/sdw_intel.h
@@ -7,6 +7,7 @@
#include <linux/acpi.h>
#include <linux/irqreturn.h>
#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_cadence.h>
/*********************************************************************
* cAVS and ACE1.x definitions
@@ -386,6 +387,8 @@ void sdw_intel_exit(struct sdw_intel_ctx *ctx);
irqreturn_t sdw_intel_thread(int irq, void *dev_id);
+void sdw_intel_pdi_config(struct sdw_bus *bus, struct sdw_cdns_pdi *pdi);
+
#else
static inline int sdw_intel_acpi_scan(acpi_handle parent_handle, struct sdw_intel_acpi_info *info)
@@ -416,6 +419,10 @@ static inline irqreturn_t sdw_intel_thread(int irq, void *dev_id)
return 0;
}
+static inline void sdw_intel_pdi_config(struct sdw_bus *bus, struct sdw_cdns_pdi *pdi)
+{
+}
+
#endif
#define SDW_INTEL_QUIRK_MASK_BUS_DISABLE BIT(1)
--
2.34.1