[PATCH v3 2/7] soundwire: intel: Allow for dynamic assignment of PDIs
From: Cezary Rojewski
Date: Fri Nov 06 2026 - 07:20:37 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 | 25 ++---------
drivers/soundwire/intel.c | 24 +++++++---
include/linux/soundwire/sdw_cadence.h | 35 +++++++++++++++
include/linux/soundwire/sdw_intel.h | 7 +++
5 files changed, 128 insertions(+), 27 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..6c1ad11c6583 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;
+ }
+ }
+
+ for (i = 0; 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
+ * @bus: SoundWire bus instance
+ * @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..c3339c1e98ff 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
*
@@ -114,6 +95,7 @@ struct sdw_cdns_dai_runtime {
* @ports: Data ports
* @num_ports: Total number of data ports
* @pcm: PCM streams
+ * @pdi_lock: synchronizes assignment of PDIs found in PCM streams
* @registers: Cadence registers
* @link_up: Link status
* @msg_count: Messages sent on bus
@@ -140,6 +122,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..0baa032fb4e8 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,
@@ -745,9 +761,7 @@ static int intel_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
/* do run-time configurations for SHIM, ALH and PDI/PORT */
- intel_pdi_shim_configure(sdw, pdi);
- intel_pdi_alh_configure(sdw, pdi);
- sdw_cdns_config_stream(cdns, ch, dir, pdi);
+ sdw_intel_pdi_config(&cdns->bus, dai_runtime->pdi);
/* store pdi and hw_params, may be needed in prepare step */
dai_runtime->paused = false;
@@ -823,9 +837,7 @@ static int intel_prepare(struct snd_pcm_substream *substream,
else
dir = SDW_DATA_DIR_TX;
- intel_pdi_shim_configure(sdw, dai_runtime->pdi);
- intel_pdi_alh_configure(sdw, dai_runtime->pdi);
- sdw_cdns_config_stream(cdns, ch, dir, dai_runtime->pdi);
+ sdw_intel_pdi_config(&cdns->bus, dai_runtime->pdi);
/* Inform DSP about PDI stream number */
ret = intel_params_stream(sdw, substream, 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 4547dcc710e5..fbfb216bd90c 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