* [PATCH v3 1/7] soundwire: intel: Define stubs when library is disabled
2026-11-06 12:15 [PATCH v3 0/7] ASoC: Intel: avs: Soundwire support Cezary Rojewski
@ 2026-11-06 12:15 ` Cezary Rojewski
2026-11-06 12:15 ` [PATCH v3 2/7] soundwire: intel: Allow for dynamic assignment of PDIs Cezary Rojewski
` (5 subsequent siblings)
6 siblings, 0 replies; 11+ messages in thread
From: Cezary Rojewski @ 2026-11-06 12:15 UTC (permalink / raw)
To: linux-sound
Cc: broonie, tiwai, perex, amade, linux-kernel, pierre-louis.bossart,
yung-chuan.liao, vkoul, Cezary Rojewski
The library defines no stubs when it is disabled so the actual users
have to do it locally to allow the code to compilte or, guard the code
that does interact with soundwire with #if(s). Define simple wrappers to
make the life of soundwire-code users easier.
The change allows for a driver to build against SOUNDWIRE_INTEL=n whilst
still calling the stubs in its common code without compilation failing.
The driver needs not to flank its soundwire-code with #if(s) either.
Not all sdw_intel_xxx()s are part of drivers/soundwire. Some definitions
are located within sound/hda/core instead. With the wrappers done,
update Kconfig for sound/hda/core as currently the ACPI-related function
are compiled regardless if SOUNDWIRE_INTEL is selected or not. By being
dependent on SOUNDWIRE_INTEL those functions are compiled only if the
SoundWire was actually requested.
Cc: Vinod Koul <vkoul@kernel.org>
Signed-off-by: Cezary Rojewski <cezary.rojewski@intel.com>
---
include/linux/soundwire/sdw_intel.h | 34 +++++++++++++++++++++++++++++
sound/hda/core/Kconfig | 2 +-
sound/hda/core/Makefile | 3 +++
3 files changed, 38 insertions(+), 1 deletion(-)
diff --git a/include/linux/soundwire/sdw_intel.h b/include/linux/soundwire/sdw_intel.h
index 9710f2dc04e2..4547dcc710e5 100644
--- a/include/linux/soundwire/sdw_intel.h
+++ b/include/linux/soundwire/sdw_intel.h
@@ -358,6 +358,8 @@ struct sdw_intel_res {
struct mutex *eml_lock;
};
+#if IS_ENABLED(CONFIG_SOUNDWIRE_INTEL)
+
/*
* On Intel platforms, the SoundWire IP has dependencies on power
* rails shared with the DSP, and the initialization steps are split
@@ -384,6 +386,38 @@ void sdw_intel_exit(struct sdw_intel_ctx *ctx);
irqreturn_t sdw_intel_thread(int irq, void *dev_id);
+#else
+
+static inline int sdw_intel_acpi_scan(acpi_handle parent_handle, struct sdw_intel_acpi_info *info)
+{
+ return -ENODEV;
+}
+
+static inline void sdw_intel_process_wakeen_event(struct sdw_intel_ctx *ctx)
+{
+}
+
+static inline struct sdw_intel_ctx *sdw_intel_probe(struct sdw_intel_res *res)
+{
+ return NULL;
+}
+
+static inline int sdw_intel_startup(struct sdw_intel_ctx *ctx)
+{
+ return 0;
+}
+
+static inline void sdw_intel_exit(struct sdw_intel_ctx *ctx)
+{
+}
+
+static inline irqreturn_t sdw_intel_thread(int irq, void *dev_id)
+{
+ return 0;
+}
+
+#endif
+
#define SDW_INTEL_QUIRK_MASK_BUS_DISABLE BIT(1)
struct sdw_intel;
diff --git a/sound/hda/core/Kconfig b/sound/hda/core/Kconfig
index bfdcf6384c52..6cbcd9e26420 100644
--- a/sound/hda/core/Kconfig
+++ b/sound/hda/core/Kconfig
@@ -29,7 +29,7 @@ config SND_INTEL_DSP_CONFIG
tristate
select ACPI_NHLT if ACPI
select SND_INTEL_NHLT if ACPI
- select SND_INTEL_SOUNDWIRE_ACPI if ACPI
+ select SND_INTEL_SOUNDWIRE_ACPI if SOUNDWIRE_INTEL != n
# this config should be selected only for Intel DSP platforms.
# A fallback is provided so that the code compiles in all cases.
diff --git a/sound/hda/core/Makefile b/sound/hda/core/Makefile
index 89cb46143050..28032ac7bc61 100644
--- a/sound/hda/core/Makefile
+++ b/sound/hda/core/Makefile
@@ -18,5 +18,8 @@ snd-intel-dspcfg-y := intel-dsp-config.o
snd-intel-dspcfg-$(CONFIG_SND_INTEL_NHLT) += intel-nhlt.o
obj-$(CONFIG_SND_INTEL_DSP_CONFIG) += snd-intel-dspcfg.o
+ifneq ($(CONFIG_SND_INTEL_SOUNDWIRE_ACPI),)
snd-intel-sdw-acpi-y := intel-sdw-acpi.o
+endif
+
obj-$(CONFIG_SND_INTEL_SOUNDWIRE_ACPI) += snd-intel-sdw-acpi.o
--
2.34.1
^ permalink raw reply [flat|nested] 11+ messages in thread* [PATCH v3 2/7] soundwire: intel: Allow for dynamic assignment of PDIs
2026-11-06 12:15 [PATCH v3 0/7] ASoC: Intel: avs: Soundwire support Cezary Rojewski
2026-11-06 12:15 ` [PATCH v3 1/7] soundwire: intel: Define stubs when library is disabled Cezary Rojewski
@ 2026-11-06 12:15 ` Cezary Rojewski
2026-11-06 12:32 ` Cezary Rojewski
2026-11-06 19:59 ` kernel test robot
2026-11-06 12:15 ` [PATCH v3 3/7] soundwire: intel: Split PDIs and DAIs initialization Cezary Rojewski
` (4 subsequent siblings)
6 siblings, 2 replies; 11+ messages in thread
From: Cezary Rojewski @ 2026-11-06 12:15 UTC (permalink / raw)
To: linux-sound
Cc: broonie, tiwai, perex, amade, linux-kernel, pierre-louis.bossart,
yung-chuan.liao, vkoul, Cezary Rojewski
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@kernel.org>
Signed-off-by: Cezary Rojewski <cezary.rojewski@intel.com>
---
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
^ permalink raw reply [flat|nested] 11+ messages in thread* Re: [PATCH v3 2/7] soundwire: intel: Allow for dynamic assignment of PDIs
2026-11-06 12:15 ` [PATCH v3 2/7] soundwire: intel: Allow for dynamic assignment of PDIs Cezary Rojewski
@ 2026-11-06 12:32 ` Cezary Rojewski
2026-11-06 19:59 ` kernel test robot
1 sibling, 0 replies; 11+ messages in thread
From: Cezary Rojewski @ 2026-11-06 12:32 UTC (permalink / raw)
To: linux-sound
Cc: broonie, tiwai, perex, amade, linux-kernel, pierre-louis.bossart,
yung-chuan.liao, vkoul
On 11/6/2026 1:15 PM, Cezary Rojewski wrote:
> 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@kernel.org>
> Signed-off-by: Cezary Rojewski <cezary.rojewski@intel.com>
Scratch that, will send v4 soon. While rebasing messed up this commit.
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v3 2/7] soundwire: intel: Allow for dynamic assignment of PDIs
2026-11-06 12:15 ` [PATCH v3 2/7] soundwire: intel: Allow for dynamic assignment of PDIs Cezary Rojewski
2026-11-06 12:32 ` Cezary Rojewski
@ 2026-11-06 19:59 ` kernel test robot
1 sibling, 0 replies; 11+ messages in thread
From: kernel test robot @ 2026-11-06 19:59 UTC (permalink / raw)
To: Cezary Rojewski, linux-sound
Cc: oe-kbuild-all, broonie, tiwai, perex, amade, linux-kernel,
pierre-louis.bossart, yung-chuan.liao, vkoul, Cezary Rojewski
Hi Cezary,
kernel test robot noticed the following build warnings:
[auto build test WARNING on broonie-sound/for-next]
[also build test WARNING on tiwai-sound/for-next tiwai-sound/for-linus linus/master v7.4-rc1 next-20261105]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Cezary-Rojewski/soundwire-intel-Define-stubs-when-library-is-disabled/20261106-131551
base: https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git for-next
patch link: https://lore.kernel.org/r/20261106121557.2338556-3-cezary.rojewski%40intel.com
patch subject: [PATCH v3 2/7] soundwire: intel: Allow for dynamic assignment of PDIs
config: x86_64-randconfig-1015-20261106 (https://download.01.org/0day-ci/archive/20261107/202611070307.UuD8O6zR-lkp@intel.com/config)
compiler: gcc-14 (Debian 14.2.0-19) 14.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20261107/202611070307.UuD8O6zR-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202611070307.UuD8O6zR-lkp@intel.com/
All warnings (new ones prefixed by >>):
drivers/soundwire/intel.c: In function 'intel_prepare':
>> drivers/soundwire/intel.c:807:17: warning: variable 'dir' set but not used [-Wunused-but-set-variable]
807 | int ch, dir;
| ^~~
>> drivers/soundwire/intel.c:807:13: warning: variable 'ch' set but not used [-Wunused-but-set-variable]
807 | int ch, dir;
| ^~
vim +/dir +807 drivers/soundwire/intel.c
c46302ec554c57 Vinod Koul 2018-04-26 800
27b198f409d876 Rander Wang 2020-02-14 801 static int intel_prepare(struct snd_pcm_substream *substream,
27b198f409d876 Rander Wang 2020-02-14 802 struct snd_soc_dai *dai)
27b198f409d876 Rander Wang 2020-02-14 803 {
a5a0239c27fe11 Bard Liao 2020-08-17 804 struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai);
a5a0239c27fe11 Bard Liao 2020-08-17 805 struct sdw_intel *sdw = cdns_to_intel(cdns);
e0767e39107968 Pierre-Louis Bossart 2022-11-01 806 struct sdw_cdns_dai_runtime *dai_runtime;
a5a0239c27fe11 Bard Liao 2020-08-17 @807 int ch, dir;
244eb888f9ab86 Pierre-Louis Bossart 2020-09-04 808 int ret = 0;
27b198f409d876 Rander Wang 2020-02-14 809
7dddead766c082 Pierre-Louis Bossart 2022-11-01 810 dai_runtime = cdns->dai_runtime_array[dai->id];
e0767e39107968 Pierre-Louis Bossart 2022-11-01 811 if (!dai_runtime) {
e0767e39107968 Pierre-Louis Bossart 2022-11-01 812 dev_err(dai->dev, "failed to get dai runtime in %s\n",
27b198f409d876 Rander Wang 2020-02-14 813 __func__);
27b198f409d876 Rander Wang 2020-02-14 814 return -EIO;
27b198f409d876 Rander Wang 2020-02-14 815 }
27b198f409d876 Rander Wang 2020-02-14 816
e0767e39107968 Pierre-Louis Bossart 2022-11-01 817 if (dai_runtime->suspended) {
50cd92e0c8d35d Kuninori Morimoto 2023-09-11 818 struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
0a0d1740bd8fd7 Pierre-Louis Bossart 2023-03-21 819 struct snd_pcm_hw_params *hw_params;
0a0d1740bd8fd7 Pierre-Louis Bossart 2023-03-21 820
0a0d1740bd8fd7 Pierre-Louis Bossart 2023-03-21 821 hw_params = &rtd->dpcm[substream->stream].hw_params;
0a0d1740bd8fd7 Pierre-Louis Bossart 2023-03-21 822
e0767e39107968 Pierre-Louis Bossart 2022-11-01 823 dai_runtime->suspended = false;
a5a0239c27fe11 Bard Liao 2020-08-17 824
a5a0239c27fe11 Bard Liao 2020-08-17 825 /*
a5a0239c27fe11 Bard Liao 2020-08-17 826 * .prepare() is called after system resume, where we
a5a0239c27fe11 Bard Liao 2020-08-17 827 * need to reinitialize the SHIM/ALH/Cadence IP.
a5a0239c27fe11 Bard Liao 2020-08-17 828 * .prepare() is also called to deal with underflows,
a5a0239c27fe11 Bard Liao 2020-08-17 829 * but in those cases we cannot touch ALH/SHIM
a5a0239c27fe11 Bard Liao 2020-08-17 830 * registers
a5a0239c27fe11 Bard Liao 2020-08-17 831 */
a5a0239c27fe11 Bard Liao 2020-08-17 832
a5a0239c27fe11 Bard Liao 2020-08-17 833 /* configure stream */
0a0d1740bd8fd7 Pierre-Louis Bossart 2023-03-21 834 ch = params_channels(hw_params);
a5a0239c27fe11 Bard Liao 2020-08-17 835 if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
a5a0239c27fe11 Bard Liao 2020-08-17 836 dir = SDW_DATA_DIR_RX;
a5a0239c27fe11 Bard Liao 2020-08-17 837 else
a5a0239c27fe11 Bard Liao 2020-08-17 838 dir = SDW_DATA_DIR_TX;
a5a0239c27fe11 Bard Liao 2020-08-17 839
5977f02f8733d2 Cezary Rojewski 2026-11-06 840 sdw_intel_pdi_config(&cdns->bus, dai_runtime->pdi);
a5a0239c27fe11 Bard Liao 2020-08-17 841
a5a0239c27fe11 Bard Liao 2020-08-17 842 /* Inform DSP about PDI stream number */
1d905d355ef329 Pierre-Louis Bossart 2023-05-15 843 ret = intel_params_stream(sdw, substream, dai,
0a0d1740bd8fd7 Pierre-Louis Bossart 2023-03-21 844 hw_params,
a5a0239c27fe11 Bard Liao 2020-08-17 845 sdw->instance,
e0767e39107968 Pierre-Louis Bossart 2022-11-01 846 dai_runtime->pdi->intel_alh_id);
a5a0239c27fe11 Bard Liao 2020-08-17 847 }
a5a0239c27fe11 Bard Liao 2020-08-17 848
a5a0239c27fe11 Bard Liao 2020-08-17 849 return ret;
27b198f409d876 Rander Wang 2020-02-14 850 }
27b198f409d876 Rander Wang 2020-02-14 851
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH v3 3/7] soundwire: intel: Split PDIs and DAIs initialization
2026-11-06 12:15 [PATCH v3 0/7] ASoC: Intel: avs: Soundwire support Cezary Rojewski
2026-11-06 12:15 ` [PATCH v3 1/7] soundwire: intel: Define stubs when library is disabled Cezary Rojewski
2026-11-06 12:15 ` [PATCH v3 2/7] soundwire: intel: Allow for dynamic assignment of PDIs Cezary Rojewski
@ 2026-11-06 12:15 ` Cezary Rojewski
2026-11-06 12:15 ` [PATCH v3 4/7] ASoC: Intel: avs: Pass irq to dsp_interrupt callback Cezary Rojewski
` (3 subsequent siblings)
6 siblings, 0 replies; 11+ messages in thread
From: Cezary Rojewski @ 2026-11-06 12:15 UTC (permalink / raw)
To: linux-sound
Cc: broonie, tiwai, perex, amade, linux-kernel, pierre-louis.bossart,
yung-chuan.liao, vkoul, Cezary Rojewski
Not every client of the soundwire-driver needs the secondary
soc-component and full range of DAIs immediately when initializing the
device. As function intel_register_dai() does all of that on top of
initializing PDIs, split it into two logical blocks to accommodate for
both approaches. In consequence, register_dai() callback becomes
optional.
To keep things in status quo for existing users, update
intel_link_startup() to call sdw_intel_init_pdis().
Cc: Vinod Koul <vkoul@kernel.org>
Signed-off-by: Cezary Rojewski <cezary.rojewski@intel.com>
---
drivers/soundwire/intel.c | 25 ++++++++++++++++---------
drivers/soundwire/intel.h | 9 ++++++++-
drivers/soundwire/intel_ace2x.c | 25 ++++++++++++++++---------
drivers/soundwire/intel_auxdevice.c | 4 ++++
include/linux/soundwire/sdw_intel.h | 4 +++-
5 files changed, 47 insertions(+), 20 deletions(-)
diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c
index 0baa032fb4e8..e18727695791 100644
--- a/drivers/soundwire/intel.c
+++ b/drivers/soundwire/intel.c
@@ -1023,23 +1023,29 @@ static int intel_create_dai(struct sdw_cdns *cdns,
return 0;
}
+static int intel_init_pdis(struct sdw_intel *sdw)
+{
+ struct sdw_cdns_stream_config config;
+ int ret;
+
+ /* Read the PDI config and initialize cadence PDI */
+ intel_pdi_init(sdw, &config);
+ ret = sdw_cdns_pdi_init(&sdw->cdns, config);
+ if (ret)
+ return ret;
+
+ intel_pdi_stream_ch_update(sdw, &sdw->cdns.pcm);
+ return 0;
+}
+
static int intel_register_dai(struct sdw_intel *sdw)
{
struct sdw_cdns_dai_runtime **dai_runtime_array;
- struct sdw_cdns_stream_config config;
struct sdw_cdns *cdns = &sdw->cdns;
struct sdw_cdns_streams *stream;
struct snd_soc_dai_driver *dais;
int num_dai, ret, off = 0;
- /* Read the PDI config and initialize cadence PDI */
- intel_pdi_init(sdw, &config);
- ret = sdw_cdns_pdi_init(cdns, config);
- if (ret)
- return ret;
-
- intel_pdi_stream_ch_update(sdw, &sdw->cdns.pcm);
-
/* DAIs are created based on total number of PDIs supported */
num_dai = cdns->pcm.num_pdi;
@@ -1083,6 +1089,7 @@ const struct sdw_intel_hw_ops sdw_intel_cnl_hw_ops = {
.debugfs_init = intel_debugfs_init,
.debugfs_exit = intel_debugfs_exit,
+ .init_pdis = intel_init_pdis,
.register_dai = intel_register_dai,
.check_clock_stop = intel_check_clock_stop,
diff --git a/drivers/soundwire/intel.h b/drivers/soundwire/intel.h
index 7a2e7e73ad63..65f7d53a3d2b 100644
--- a/drivers/soundwire/intel.h
+++ b/drivers/soundwire/intel.h
@@ -154,11 +154,18 @@ static inline void sdw_intel_debugfs_exit(struct sdw_intel *sdw)
SDW_INTEL_OPS(sdw, debugfs_exit)(sdw);
}
+static inline int sdw_intel_init_pdis(struct sdw_intel *sdw)
+{
+ if (SDW_INTEL_CHECK_OPS(sdw, init_pdis))
+ return SDW_INTEL_OPS(sdw, init_pdis)(sdw);
+ return -ENOTSUPP;
+}
+
static inline int sdw_intel_register_dai(struct sdw_intel *sdw)
{
if (SDW_INTEL_CHECK_OPS(sdw, register_dai))
return SDW_INTEL_OPS(sdw, register_dai)(sdw);
- return -ENOTSUPP;
+ return 0;
}
static inline void sdw_intel_check_clock_stop(struct sdw_intel *sdw)
diff --git a/drivers/soundwire/intel_ace2x.c b/drivers/soundwire/intel_ace2x.c
index 642b33ab5526..67b5d54bb2a7 100644
--- a/drivers/soundwire/intel_ace2x.c
+++ b/drivers/soundwire/intel_ace2x.c
@@ -1058,10 +1058,24 @@ static int intel_create_dai(struct sdw_cdns *cdns,
return 0;
}
+static int intel_init_pdis(struct sdw_intel *sdw)
+{
+ struct sdw_cdns_stream_config config;
+ int ret;
+
+ /* Read the PDI config and initialize cadence PDI */
+ intel_pdi_init(sdw, &config);
+ ret = sdw_cdns_pdi_init(&sdw->cdns, config);
+ if (ret)
+ return ret;
+
+ intel_pdi_stream_ch_update(sdw, &sdw->cdns.pcm);
+ return 0;
+}
+
static int intel_register_dai(struct sdw_intel *sdw)
{
struct sdw_cdns_dai_runtime **dai_runtime_array;
- struct sdw_cdns_stream_config config;
struct sdw_cdns *cdns = &sdw->cdns;
struct sdw_cdns_streams *stream;
struct snd_soc_dai_driver *dais;
@@ -1069,14 +1083,6 @@ static int intel_register_dai(struct sdw_intel *sdw)
int ret;
int off = 0;
- /* Read the PDI config and initialize cadence PDI */
- intel_pdi_init(sdw, &config);
- ret = sdw_cdns_pdi_init(cdns, config);
- if (ret)
- return ret;
-
- intel_pdi_stream_ch_update(sdw, &sdw->cdns.pcm);
-
/* DAIs are created based on total number of PDIs supported */
num_dai = cdns->pcm.num_pdi;
@@ -1149,6 +1155,7 @@ const struct sdw_intel_hw_ops sdw_intel_lnl_hw_ops = {
.get_link_count = intel_get_link_count,
+ .init_pdis = intel_init_pdis,
.register_dai = intel_register_dai,
.check_clock_stop = intel_check_clock_stop,
diff --git a/drivers/soundwire/intel_auxdevice.c b/drivers/soundwire/intel_auxdevice.c
index 5983fbc5725a..9031c96f7e9d 100644
--- a/drivers/soundwire/intel_auxdevice.c
+++ b/drivers/soundwire/intel_auxdevice.c
@@ -423,6 +423,10 @@ int intel_link_startup(struct auxiliary_device *auxdev)
if (ret)
goto err_init;
+ ret = sdw_intel_init_pdis(sdw);
+ if (ret)
+ goto err_power_up;
+
/* Register DAIs */
ret = sdw_intel_register_dai(sdw);
if (ret) {
diff --git a/include/linux/soundwire/sdw_intel.h b/include/linux/soundwire/sdw_intel.h
index fbfb216bd90c..0efb05a25d12 100644
--- a/include/linux/soundwire/sdw_intel.h
+++ b/include/linux/soundwire/sdw_intel.h
@@ -433,7 +433,8 @@ struct sdw_intel;
* @debugfs_init: initialize all debugfs capabilities
* @debugfs_exit: close and cleanup debugfs capabilities
* @get_link_count: fetch link count from hardware registers
- * @register_dai: read all PDI information and register DAIs
+ * @init_pdis: read all PDI information
+ * @register_dai: register DAIs
* @check_clock_stop: throw error message if clock is not stopped.
* @start_bus: normal start
* @start_bus_after_reset: start after reset
@@ -459,6 +460,7 @@ struct sdw_intel_hw_ops {
int (*get_link_count)(struct sdw_intel *sdw);
+ int (*init_pdis)(struct sdw_intel *sdw);
int (*register_dai)(struct sdw_intel *sdw);
void (*check_clock_stop)(struct sdw_intel *sdw);
--
2.34.1
^ permalink raw reply [flat|nested] 11+ messages in thread* [PATCH v3 4/7] ASoC: Intel: avs: Pass irq to dsp_interrupt callback
2026-11-06 12:15 [PATCH v3 0/7] ASoC: Intel: avs: Soundwire support Cezary Rojewski
` (2 preceding siblings ...)
2026-11-06 12:15 ` [PATCH v3 3/7] soundwire: intel: Split PDIs and DAIs initialization Cezary Rojewski
@ 2026-11-06 12:15 ` Cezary Rojewski
2026-11-06 12:15 ` [PATCH v3 5/7] ASoC: Intel: avs: SoundWire support Cezary Rojewski
` (2 subsequent siblings)
6 siblings, 0 replies; 11+ messages in thread
From: Cezary Rojewski @ 2026-11-06 12:15 UTC (permalink / raw)
To: linux-sound
Cc: broonie, tiwai, perex, amade, linux-kernel, pierre-louis.bossart,
yung-chuan.liao, vkoul, Cezary Rojewski
Soundwire IRQ thread function requires passing irq as one of the
arguments, as we will want to call it in dsp_interrupt callback, it
needs to be passed throguh.
Signed-off-by: Amadeusz Sławiński <amade@asmblr.net>
Signed-off-by: Cezary Rojewski <cezary.rojewski@intel.com>
---
sound/soc/intel/avs/apl.c | 2 +-
sound/soc/intel/avs/avs.h | 6 +++---
sound/soc/intel/avs/cnl.c | 2 +-
sound/soc/intel/avs/core.c | 2 +-
sound/soc/intel/avs/mtl.c | 2 +-
sound/soc/intel/avs/skl.c | 2 +-
6 files changed, 8 insertions(+), 8 deletions(-)
diff --git a/sound/soc/intel/avs/apl.c b/sound/soc/intel/avs/apl.c
index cf600e1c986e..3abcdf062035 100644
--- a/sound/soc/intel/avs/apl.c
+++ b/sound/soc/intel/avs/apl.c
@@ -17,7 +17,7 @@
#include "registers.h"
#include "topology.h"
-static irqreturn_t avs_apl_dsp_interrupt(struct avs_dev *adev)
+static irqreturn_t avs_apl_dsp_interrupt(struct avs_dev *adev, int irq)
{
u32 adspis = snd_hdac_adsp_readl(adev, AVS_ADSP_REG_ADSPIS);
irqreturn_t ret = IRQ_NONE;
diff --git a/sound/soc/intel/avs/avs.h b/sound/soc/intel/avs/avs.h
index b4f9d66d55ad..b3af2c4126be 100644
--- a/sound/soc/intel/avs/avs.h
+++ b/sound/soc/intel/avs/avs.h
@@ -45,7 +45,7 @@ struct avs_dsp_ops {
int (* const power)(struct avs_dev *, u32, bool);
int (* const reset)(struct avs_dev *, u32, bool);
int (* const stall)(struct avs_dev *, u32, bool);
- irqreturn_t (* const dsp_interrupt)(struct avs_dev *);
+ irqreturn_t (* const dsp_interrupt)(struct avs_dev *, int irq);
void (* const int_control)(struct avs_dev *, bool);
int (* const load_basefw)(struct avs_dev *, struct firmware *);
int (* const load_lib)(struct avs_dev *, struct firmware *, u32);
@@ -273,8 +273,8 @@ int avs_mtl_core_stall(struct avs_dev *adev, u32 core_mask, bool stall);
int avs_lnl_core_stall(struct avs_dev *adev, u32 core_mask, bool stall);
void avs_mtl_interrupt_control(struct avs_dev *adev, bool enable);
void avs_skl_ipc_interrupt(struct avs_dev *adev);
-irqreturn_t avs_cnl_dsp_interrupt(struct avs_dev *adev);
-irqreturn_t avs_mtl_dsp_interrupt(struct avs_dev *adev);
+irqreturn_t avs_cnl_dsp_interrupt(struct avs_dev *adev, int irq);
+irqreturn_t avs_mtl_dsp_interrupt(struct avs_dev *adev, int irq);
int avs_apl_enable_logs(struct avs_dev *adev, enum avs_log_enable enable, u32 aging_period,
u32 fifo_full_period, unsigned long resource_mask, u32 *priorities);
int avs_icl_enable_logs(struct avs_dev *adev, enum avs_log_enable enable, u32 aging_period,
diff --git a/sound/soc/intel/avs/cnl.c b/sound/soc/intel/avs/cnl.c
index 5b5359e9128b..31fd9f251d6c 100644
--- a/sound/soc/intel/avs/cnl.c
+++ b/sound/soc/intel/avs/cnl.c
@@ -58,7 +58,7 @@ static void avs_cnl_ipc_interrupt(struct avs_dev *adev)
AVS_ADSP_HIPCCTL_DONE | AVS_ADSP_HIPCCTL_BUSY);
}
-irqreturn_t avs_cnl_dsp_interrupt(struct avs_dev *adev)
+irqreturn_t avs_cnl_dsp_interrupt(struct avs_dev *adev, int irq)
{
u32 adspis = snd_hdac_adsp_readl(adev, AVS_ADSP_REG_ADSPIS);
irqreturn_t ret = IRQ_NONE;
diff --git a/sound/soc/intel/avs/core.c b/sound/soc/intel/avs/core.c
index 611ae9f034d4..9413027e1e32 100644
--- a/sound/soc/intel/avs/core.c
+++ b/sound/soc/intel/avs/core.c
@@ -344,7 +344,7 @@ static irqreturn_t avs_dsp_irq_thread(int irq, void *dev_id)
status = readl(bus->ppcap + AZX_REG_PP_PPSTS);
if (status & AZX_PPCTL_PIE)
- avs_dsp_op(adev, dsp_interrupt);
+ avs_dsp_op(adev, dsp_interrupt, irq);
/* Unmask GIE, masked in irq_handler(). */
snd_hdac_chip_updatel(bus, INTCTL, AZX_INT_GLOBAL_EN, AZX_INT_GLOBAL_EN);
diff --git a/sound/soc/intel/avs/mtl.c b/sound/soc/intel/avs/mtl.c
index d8bdd03275d7..29f79d6ff0d4 100644
--- a/sound/soc/intel/avs/mtl.c
+++ b/sound/soc/intel/avs/mtl.c
@@ -166,7 +166,7 @@ static void avs_mtl_ipc_interrupt(struct avs_dev *adev)
AVS_ADSP_HIPCCTL_DONE | AVS_ADSP_HIPCCTL_BUSY);
}
-irqreturn_t avs_mtl_dsp_interrupt(struct avs_dev *adev)
+irqreturn_t avs_mtl_dsp_interrupt(struct avs_dev *adev, int irq)
{
u32 adspis = snd_hdac_adsp_readl(adev, MTL_DWICTL_REG_FINALSTATUSL);
irqreturn_t ret = IRQ_NONE;
diff --git a/sound/soc/intel/avs/skl.c b/sound/soc/intel/avs/skl.c
index 8fb86f364ff3..54c883658cca 100644
--- a/sound/soc/intel/avs/skl.c
+++ b/sound/soc/intel/avs/skl.c
@@ -54,7 +54,7 @@ void avs_skl_ipc_interrupt(struct avs_dev *adev)
AVS_ADSP_HIPCCTL_DONE | AVS_ADSP_HIPCCTL_BUSY);
}
-static irqreturn_t avs_skl_dsp_interrupt(struct avs_dev *adev)
+static irqreturn_t avs_skl_dsp_interrupt(struct avs_dev *adev, int irq)
{
u32 adspis = snd_hdac_adsp_readl(adev, AVS_ADSP_REG_ADSPIS);
irqreturn_t ret = IRQ_NONE;
--
2.34.1
^ permalink raw reply [flat|nested] 11+ messages in thread* [PATCH v3 5/7] ASoC: Intel: avs: SoundWire support
2026-11-06 12:15 [PATCH v3 0/7] ASoC: Intel: avs: Soundwire support Cezary Rojewski
` (3 preceding siblings ...)
2026-11-06 12:15 ` [PATCH v3 4/7] ASoC: Intel: avs: Pass irq to dsp_interrupt callback Cezary Rojewski
@ 2026-11-06 12:15 ` Cezary Rojewski
2026-11-06 12:15 ` [PATCH v3 6/7] ASoC: Intel: avs: Expose common PCM functions Cezary Rojewski
2026-11-06 12:15 ` [PATCH v3 7/7] ASoC: Intel: avs: SoundWire PCM BE operations Cezary Rojewski
6 siblings, 0 replies; 11+ messages in thread
From: Cezary Rojewski @ 2026-11-06 12:15 UTC (permalink / raw)
To: linux-sound
Cc: broonie, tiwai, perex, amade, linux-kernel, pierre-louis.bossart,
yung-chuan.liao, vkoul, Cezary Rojewski
The SoundWire device is part of the Intel HDAudio PCI device. To support
transfer over SoundWire interface it has to be accounted for when
probing the parent device.
Most of the actual job is done by the SoundWire library which is already
part of the kernel. Utilize it in the core functions of the driver to
enumerate SoundWire component and service its interrupts.
Signed-off-by: Cezary Rojewski <cezary.rojewski@intel.com>
---
sound/soc/intel/Kconfig | 8 +++
sound/soc/intel/avs/Makefile | 4 ++
sound/soc/intel/avs/avs.h | 1 +
sound/soc/intel/avs/cnl.c | 23 ++++++-
sound/soc/intel/avs/core.c | 21 ++++++
sound/soc/intel/avs/loader.c | 3 +
sound/soc/intel/avs/registers.h | 4 ++
sound/soc/intel/avs/soundwire.c | 112 ++++++++++++++++++++++++++++++++
sound/soc/intel/avs/soundwire.h | 56 ++++++++++++++++
9 files changed, 230 insertions(+), 2 deletions(-)
create mode 100644 sound/soc/intel/avs/soundwire.c
create mode 100644 sound/soc/intel/avs/soundwire.h
diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig
index 63367364916a..c765eee25763 100644
--- a/sound/soc/intel/Kconfig
+++ b/sound/soc/intel/Kconfig
@@ -106,12 +106,20 @@ config SND_SOC_INTEL_AVS
select SND_HDA_EXT_CORE
select SND_HDA_DSP_LOADER
select SND_INTEL_DSP_CONFIG
+ select SND_INTEL_SOUNDWIRE_ACPI if SOUNDWIRE_INTEL != n
select WANT_DEV_COREDUMP
help
Enable support for Intel(R) cAVS 1.5 platforms with DSP
capabilities. This includes Skylake, Kabylake, Amberlake and
Apollolake.
+config SND_SOC_INTEL_AVS_SOUNDWIRE
+ tristate
+ default SND_SOC_INTEL_AVS
+ depends on SND_SOC_INTEL_AVS
+ depends on SOUNDWIRE_INTEL
+ depends on !(SOUNDWIRE_INTEL=m && SND_SOC_INTEL_AVS=y)
+
# Machine board drivers
source "sound/soc/intel/avs/boards/Kconfig"
diff --git a/sound/soc/intel/avs/Makefile b/sound/soc/intel/avs/Makefile
index 576dc0da381d..042e8dd232f8 100644
--- a/sound/soc/intel/avs/Makefile
+++ b/sound/soc/intel/avs/Makefile
@@ -14,6 +14,10 @@ ifneq ($(CONFIG_DEBUG_FS),)
snd-soc-avs-y += probes.o debugfs.o
endif
+ifneq ($(CONFIG_SND_SOC_INTEL_AVS_SOUNDWIRE),)
+snd-soc-avs-y += soundwire.o
+endif
+
obj-$(CONFIG_SND_SOC_INTEL_AVS) += snd-soc-avs.o
# Machine support
diff --git a/sound/soc/intel/avs/avs.h b/sound/soc/intel/avs/avs.h
index b3af2c4126be..40e65639adb2 100644
--- a/sound/soc/intel/avs/avs.h
+++ b/sound/soc/intel/avs/avs.h
@@ -136,6 +136,7 @@ struct avs_dev {
void __iomem *dsp_ba;
const struct avs_spec *spec;
struct avs_ipc *ipc;
+ struct sdw_intel_ctx *sdw;
struct avs_fw_cfg fw_cfg;
struct avs_hw_cfg hw_cfg;
diff --git a/sound/soc/intel/avs/cnl.c b/sound/soc/intel/avs/cnl.c
index 31fd9f251d6c..58e5b0f3fa47 100644
--- a/sound/soc/intel/avs/cnl.c
+++ b/sound/soc/intel/avs/cnl.c
@@ -6,11 +6,16 @@
// Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
//
+#include <linux/acpi.h>
+#include <linux/device.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_intel.h>
#include <sound/hdaudio_ext.h>
#include "avs.h"
#include "debug.h"
#include "messages.h"
#include "registers.h"
+#include "soundwire.h"
static void avs_cnl_ipc_interrupt(struct avs_dev *adev)
{
@@ -61,16 +66,30 @@ static void avs_cnl_ipc_interrupt(struct avs_dev *adev)
irqreturn_t avs_cnl_dsp_interrupt(struct avs_dev *adev, int irq)
{
u32 adspis = snd_hdac_adsp_readl(adev, AVS_ADSP_REG_ADSPIS);
+ u32 adspis2 = snd_hdac_adsp_readl(adev, AVS_ADSP_REG_ADSPIS2);
irqreturn_t ret = IRQ_NONE;
- if (adspis == UINT_MAX)
+ if (adspis == UINT_MAX && adspis2 == UINT_MAX)
return ret;
- if (adspis & AVS_ADSP_ADSPIS_IPC) {
+ if (adspis != UINT_MAX && (adspis & AVS_ADSP_ADSPIS_IPC)) {
avs_cnl_ipc_interrupt(adev);
ret = IRQ_HANDLED;
}
+ if (adev->sdw) {
+ u16 wakests;
+
+ if (adspis2 != UINT_MAX && (adspis2 & AVS_ADSP_ADSPIS2_SNDW))
+ ret = avs_sdw_thread(irq, adev);
+
+ wakests = snd_hdac_adsp_readw(adev, adev->sdw->shim_base + SDW_SHIM_WAKESTS);
+ if (wakests && wakests != U16_MAX) {
+ avs_sdw_process_wakeen_event(adev);
+ ret = IRQ_HANDLED;
+ }
+ }
+
return ret;
}
diff --git a/sound/soc/intel/avs/core.c b/sound/soc/intel/avs/core.c
index 9413027e1e32..83c19d3e91e6 100644
--- a/sound/soc/intel/avs/core.c
+++ b/sound/soc/intel/avs/core.c
@@ -31,6 +31,7 @@
#include "debug.h"
#include "messages.h"
#include "pcm.h"
+#include "soundwire.h"
static u32 pgctl_mask = AZX_PGCTL_LSRMD_MASK;
module_param(pgctl_mask, uint, 0444);
@@ -230,6 +231,11 @@ static void avs_hda_probe_work(struct work_struct *work)
list_for_each_entry(hlink, &bus->hlink_list, list)
snd_hdac_ext_bus_link_put(bus, hlink);
+ /* Soundwire component is optional */
+ ret = avs_sdw_probe(adev);
+ if (ret && ret != -ENODEV)
+ dev_err(adev->dev, "failed to probe sdw: %d\n", ret);
+
snd_hdac_ext_bus_ppcap_enable(bus, true);
snd_hdac_ext_bus_ppcap_int_enable(bus, true);
avs_debugfs_init(adev);
@@ -240,6 +246,14 @@ static void avs_hda_probe_work(struct work_struct *work)
acpi_nhlt_get_gbl_table();
+ ret = avs_sdw_startup(adev);
+ if (ret) {
+ dev_err(adev->dev, "failed to startup sdw: %d\n", ret);
+ return;
+ }
+
+ avs_sdw_enable_irq(adev, true);
+
avs_register_all_boards(adev);
/* configure PM */
@@ -561,6 +575,8 @@ static void avs_pci_shutdown(struct pci_dev *pci)
snd_hdac_bus_stop_chip(bus);
snd_hdac_display_power(bus, HDA_CODEC_IDX_CONTROLLER, false);
+ avs_sdw_enable_irq(adev, false);
+
pci_free_irq(pci, 0, adev);
pci_free_irq(pci, 0, bus);
pci_free_irq_vectors(pci);
@@ -579,11 +595,13 @@ static void avs_pci_remove(struct pci_dev *pci)
acpi_nhlt_put_gbl_table();
avs_debugfs_exit(adev);
+ avs_sdw_exit(adev);
if (avs_platattr_test(adev, CLDMA))
hda_cldma_free(&code_loader);
snd_hdac_stop_streams_and_chip(bus);
+ avs_sdw_enable_irq(adev, false);
avs_dsp_op(adev, int_control, false);
snd_hdac_ext_bus_ppcap_int_enable(bus, false);
@@ -661,6 +679,7 @@ static int avs_suspend_common(struct avs_dev *adev, bool low_power)
}
avs_ipc_block(adev->ipc);
+ avs_sdw_enable_irq(adev, false);
avs_dsp_op(adev, int_control, false);
snd_hdac_ext_bus_ppcap_int_enable(bus, false);
@@ -720,6 +739,8 @@ static int avs_resume_common(struct avs_dev *adev, bool low_power, bool purge)
return ret;
}
+ avs_sdw_enable_irq(adev, true);
+
return 0;
}
diff --git a/sound/soc/intel/avs/loader.c b/sound/soc/intel/avs/loader.c
index bebdc79ec88e..27d09a3bb5ef 100644
--- a/sound/soc/intel/avs/loader.c
+++ b/sound/soc/intel/avs/loader.c
@@ -18,6 +18,7 @@
#include "messages.h"
#include "registers.h"
#include "topology.h"
+#include "soundwire.h"
#define AVS_ROM_STS_MASK 0xFF
#define AVS_ROM_INIT_DONE 0x1
@@ -345,6 +346,8 @@ static int avs_imr_load_basefw(struct avs_dev *adev)
if (ret < 0)
return ret;
+ avs_sdw_process_wakeen_event(adev);
+
ret = wait_for_completion_timeout(&adev->fw_ready,
msecs_to_jiffies(AVS_FW_INIT_TIMEOUT_MS));
if (!ret) {
diff --git a/sound/soc/intel/avs/registers.h b/sound/soc/intel/avs/registers.h
index 97767882ffa1..9e403cfb04fa 100644
--- a/sound/soc/intel/avs/registers.h
+++ b/sound/soc/intel/avs/registers.h
@@ -25,11 +25,15 @@
#define AVS_ADSP_REG_ADSPCS (AVS_ADSP_GEN_BASE + 0x04)
#define AVS_ADSP_REG_ADSPIC (AVS_ADSP_GEN_BASE + 0x08)
#define AVS_ADSP_REG_ADSPIS (AVS_ADSP_GEN_BASE + 0x0C)
+#define AVS_ADSP_REG_ADSPIC2 (AVS_ADSP_GEN_BASE + 0x10)
+#define AVS_ADSP_REG_ADSPIS2 (AVS_ADSP_GEN_BASE + 0x14)
#define AVS_ADSP_ADSPIC_IPC BIT(0)
#define AVS_ADSP_ADSPIC_CLDMA BIT(1)
#define AVS_ADSP_ADSPIS_IPC BIT(0)
#define AVS_ADSP_ADSPIS_CLDMA BIT(1)
+#define AVS_ADSP_ADSPIC2_SNDW BIT(5)
+#define AVS_ADSP_ADSPIS2_SNDW BIT(5)
#define AVS_ADSPCS_CRST_MASK(cm) (cm)
#define AVS_ADSPCS_CSTALL_MASK(cm) ((cm) << 8)
diff --git a/sound/soc/intel/avs/soundwire.c b/sound/soc/intel/avs/soundwire.c
new file mode 100644
index 000000000000..35bb7ed56bef
--- /dev/null
+++ b/sound/soc/intel/avs/soundwire.c
@@ -0,0 +1,112 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright(c) 2021-2026 Intel Corporation
+//
+// Authors: Cezary Rojewski <cezary.rojewski@intel.com>
+// Amadeusz Slawinski <amade@asmblr.net>
+//
+
+#include <linux/acpi.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_intel.h>
+#include <sound/hdaudio_ext.h>
+#include "avs.h"
+#include "registers.h"
+#include "soundwire.h"
+
+static struct sdw_intel_hw_ops avs_sdw_cnl_ops;
+
+int avs_sdw_probe(struct avs_dev *adev)
+{
+ struct hdac_bus *hbus = &adev->base.core;
+ struct sdw_intel_acpi_info info;
+ struct sdw_intel_ctx *ctx;
+ struct sdw_intel_res res;
+ int ret;
+
+ if (avs_platattr_test(adev, ALTHDA))
+ return -ENODEV;
+
+ ret = sdw_intel_acpi_scan(ACPI_HANDLE(adev->dev), &info);
+ if (ret || !info.link_mask)
+ return ret;
+
+ memset(&res, 0, sizeof(res));
+ res.mmio_base = adev->dsp_ba;
+ res.shim_base = SDW_SHIM_BASE;
+ res.alh_base = SDW_ALH_BASE;
+ res.ext = false;
+ res.irq = adev->base.core.irq;
+ res.handle = info.handle;
+ res.parent = adev->dev;
+ res.dev = adev->dev;
+ res.clock_stop_quirks = SDW_INTEL_CLK_STOP_BUS_RESET;
+ res.hbus = hbus;
+
+ memcpy(&avs_sdw_cnl_ops, &sdw_intel_cnl_hw_ops, sizeof(avs_sdw_cnl_ops));
+ avs_sdw_cnl_ops.register_dai = NULL;
+ res.hw_ops = &avs_sdw_cnl_ops;
+
+ /* we could filter links here if needed, e.g.: for quirks */
+ res.count = info.count;
+ res.link_mask = info.link_mask;
+
+ ctx = sdw_intel_probe(&res);
+ if (!ctx) {
+ dev_err(adev->dev, "SoundWire probe failed\n");
+ return -EINVAL;
+ }
+
+ adev->sdw = ctx;
+ return 0;
+}
+
+int avs_sdw_startup(struct avs_dev *adev)
+{
+ if (adev->sdw)
+ return sdw_intel_startup(adev->sdw);
+ return 0;
+}
+
+void avs_sdw_exit(struct avs_dev *adev)
+{
+ if (adev->sdw)
+ sdw_intel_exit(adev->sdw);
+}
+
+static void sdw_intel_enable_irq(void __iomem *mmio_base, bool enable)
+{
+ u32 val;
+
+ val = readl(mmio_base + AVS_ADSP_REG_ADSPIC2);
+
+ if (enable)
+ val |= AVS_ADSP_ADSPIC2_SNDW;
+ else
+ val &= ~AVS_ADSP_ADSPIC2_SNDW;
+
+ writel(val, mmio_base + AVS_ADSP_REG_ADSPIC2);
+}
+
+void avs_sdw_enable_irq(struct avs_dev *adev, bool enable)
+{
+ if (adev->sdw)
+ sdw_intel_enable_irq(adev->dsp_ba, enable);
+}
+
+void avs_sdw_process_wakeen_event(struct avs_dev *adev)
+{
+ if (adev->sdw)
+ sdw_intel_process_wakeen_event(adev->sdw);
+}
+
+irqreturn_t avs_sdw_thread(int irq, struct avs_dev *adev)
+{
+ if (adev->sdw)
+ return sdw_intel_thread(irq, adev->sdw);
+ return IRQ_HANDLED;
+}
+
+MODULE_IMPORT_NS("SOUNDWIRE_INTEL");
+MODULE_IMPORT_NS("SOUNDWIRE_INTEL_INIT");
+MODULE_IMPORT_NS("SND_INTEL_SOUNDWIRE_ACPI");
diff --git a/sound/soc/intel/avs/soundwire.h b/sound/soc/intel/avs/soundwire.h
new file mode 100644
index 000000000000..e57e29ed16bf
--- /dev/null
+++ b/sound/soc/intel/avs/soundwire.h
@@ -0,0 +1,56 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright(c) 2021-2026 Intel Corporation
+ *
+ * Authors: Cezary Rojewski <cezary.rojewski@intel.com>
+ * Amadeusz Slawinski <amade@asmblr.net>
+ */
+
+#ifndef __SOUND_SOC_INTEL_AVS_SOUNDWIRE_H
+#define __SOUND_SOC_INTEL_AVS_SOUNDWIRE_H
+
+#include <linux/irqreturn.h>
+
+struct avs_dev;
+
+#if IS_ENABLED(CONFIG_SND_SOC_INTEL_AVS_SOUNDWIRE)
+
+int avs_sdw_probe(struct avs_dev *adev);
+int avs_sdw_startup(struct avs_dev *adev);
+void avs_sdw_exit(struct avs_dev *adev);
+void avs_sdw_enable_irq(struct avs_dev *adev, bool enable);
+void avs_sdw_process_wakeen_event(struct avs_dev *adev);
+irqreturn_t avs_sdw_thread(int irq, struct avs_dev *adev);
+
+#else
+
+static inline int avs_sdw_probe(struct avs_dev *adev)
+{
+ return 0;
+}
+
+static inline int avs_sdw_startup(struct avs_dev *adev)
+{
+ return 0;
+}
+
+static inline void avs_sdw_exit(struct avs_dev *adev)
+{
+}
+
+static inline void avs_sdw_enable_irq(struct avs_dev *adev, bool enable)
+{
+}
+
+static inline void avs_sdw_process_wakeen_event(struct avs_dev *adev)
+{
+}
+
+static inline irqreturn_t avs_sdw_thread(int irq, struct avs_dev *adev)
+{
+ return IRQ_HANDLED;
+}
+
+#endif
+
+#endif /* __SOUND_SOC_INTEL_AVS_SOUNDWIRE_H */
--
2.34.1
^ permalink raw reply [flat|nested] 11+ messages in thread* [PATCH v3 6/7] ASoC: Intel: avs: Expose common PCM functions
2026-11-06 12:15 [PATCH v3 0/7] ASoC: Intel: avs: Soundwire support Cezary Rojewski
` (4 preceding siblings ...)
2026-11-06 12:15 ` [PATCH v3 5/7] ASoC: Intel: avs: SoundWire support Cezary Rojewski
@ 2026-11-06 12:15 ` Cezary Rojewski
2026-11-06 12:15 ` [PATCH v3 7/7] ASoC: Intel: avs: SoundWire PCM BE operations Cezary Rojewski
6 siblings, 0 replies; 11+ messages in thread
From: Cezary Rojewski @ 2026-11-06 12:15 UTC (permalink / raw)
To: linux-sound
Cc: broonie, tiwai, perex, amade, linux-kernel, pierre-louis.bossart,
yung-chuan.liao, vkoul, Cezary Rojewski
All the PCM operations are currently bundled together within the pcm.c
file regardless of which transfer type they cover. To allow for
splitting them, provide a header that exposes the common operations
outside.
Signed-off-by: Cezary Rojewski <cezary.rojewski@intel.com>
---
sound/soc/intel/avs/avs.h | 8 -------
sound/soc/intel/avs/pcm.c | 42 +++++++++-------------------------
sound/soc/intel/avs/pcm.h | 47 +++++++++++++++++++++++++++++++++++++++
3 files changed, 58 insertions(+), 39 deletions(-)
diff --git a/sound/soc/intel/avs/avs.h b/sound/soc/intel/avs/avs.h
index 40e65639adb2..1b2f1e7d0556 100644
--- a/sound/soc/intel/avs/avs.h
+++ b/sound/soc/intel/avs/avs.h
@@ -347,14 +347,6 @@ struct avs_soc_component {
extern const struct snd_soc_dai_ops avs_dai_fe_ops;
-int avs_register_dmic_component(struct avs_dev *adev, const char *name);
-int avs_register_i2s_component(struct avs_dev *adev, const char *name, unsigned long port_mask,
- unsigned long *tdms);
-int avs_register_hda_component(struct avs_dev *adev, const char *name);
-int avs_register_component(struct device *dev, const char *name,
- struct snd_soc_component_driver *drv,
- struct snd_soc_dai_driver *cpu_dais, int num_cpu_dais);
-
int avs_register_all_boards(struct avs_dev *adev);
void avs_unregister_all_boards(struct avs_dev *adev);
diff --git a/sound/soc/intel/avs/pcm.c b/sound/soc/intel/avs/pcm.c
index f17bbc2ba63d..fb1af3bb493f 100644
--- a/sound/soc/intel/avs/pcm.c
+++ b/sound/soc/intel/avs/pcm.c
@@ -22,26 +22,6 @@
#include "utils.h"
#include "../../codecs/hda.h"
-struct avs_dma_data {
- struct avs_tplg_path_template *template;
- struct avs_path *path;
- struct avs_dev *adev;
-
- /* LINK-stream utilized in BE operations while HOST in FE ones. */
- union {
- struct hdac_ext_stream *link_stream;
- struct hdac_ext_stream *host_stream;
- };
-
- struct snd_pcm_hw_constraint_list rate_list;
- struct snd_pcm_hw_constraint_list channels_list;
- struct snd_pcm_hw_constraint_list sample_bits_list;
-
- struct work_struct period_elapsed_work;
- struct hdac_ext_link *link;
- struct snd_pcm_substream *substream;
-};
-
static struct avs_tplg_path_template *
avs_dai_find_path_template(struct snd_soc_dai *dai, bool is_fe, int direction)
{
@@ -117,7 +97,7 @@ static int avs_hw_constraints_init(struct snd_pcm_substream *substream, struct s
return 0;
}
-static int avs_dai_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+int avs_dai_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
{
struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
struct avs_dev *adev = to_avs_dev(dai->component->dev);
@@ -147,7 +127,7 @@ static int avs_dai_startup(struct snd_pcm_substream *substream, struct snd_soc_d
return avs_hw_constraints_init(substream, dai);
}
-static void avs_dai_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+void avs_dai_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
{
struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
struct avs_dma_data *data;
@@ -165,10 +145,10 @@ static void avs_dai_shutdown(struct snd_pcm_substream *substream, struct snd_soc
kfree(data);
}
-static int avs_dai_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *fe_hw_params,
- struct snd_pcm_hw_params *be_hw_params, struct snd_soc_dai *dai,
- int dma_id)
+int avs_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *fe_hw_params,
+ struct snd_pcm_hw_params *be_hw_params,
+ struct snd_soc_dai *dai, int dma_id)
{
struct avs_dma_data *data;
struct avs_path *path;
@@ -199,9 +179,9 @@ static int avs_dai_hw_params(struct snd_pcm_substream *substream,
return 0;
}
-static int avs_dai_be_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *be_hw_params, struct snd_soc_dai *dai,
- int dma_id)
+int avs_dai_be_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *be_hw_params,
+ struct snd_soc_dai *dai, int dma_id)
{
struct snd_pcm_hw_params *fe_hw_params = NULL;
struct snd_soc_pcm_runtime *fe, *be;
@@ -217,7 +197,7 @@ static int avs_dai_be_hw_params(struct snd_pcm_substream *substream,
return avs_dai_hw_params(substream, fe_hw_params, be_hw_params, dai, dma_id);
}
-static int avs_dai_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+int avs_dai_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
{
struct avs_dma_data *data;
int ret;
@@ -1376,7 +1356,7 @@ static int avs_component_new(struct snd_soc_component *component,
return 0;
}
-static struct snd_soc_component_driver avs_component_driver = {
+struct snd_soc_component_driver avs_component_driver = {
.name = "avs-pcm",
.probe = avs_component_probe,
.remove = avs_component_remove,
diff --git a/sound/soc/intel/avs/pcm.h b/sound/soc/intel/avs/pcm.h
index 0f3615c90398..a5093e62da26 100644
--- a/sound/soc/intel/avs/pcm.h
+++ b/sound/soc/intel/avs/pcm.h
@@ -9,8 +9,55 @@
#ifndef __SOUND_SOC_INTEL_AVS_PCM_H
#define __SOUND_SOC_INTEL_AVS_PCM_H
+#include <linux/device.h>
+#include <sound/hdaudio_ext.h>
#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc-acpi.h>
+#include <sound/soc-acpi-intel-match.h>
+#include <sound/soc-component.h>
+#include <sound/soc-dai.h>
+
+struct avs_dma_data {
+ struct avs_tplg_path_template *template;
+ struct avs_path *path;
+ struct avs_dev *adev;
+
+ /* LINK-stream utilized in BE operations while HOST in FE ones. */
+ union {
+ struct hdac_ext_stream *link_stream;
+ struct hdac_ext_stream *host_stream;
+ };
+
+ struct snd_pcm_hw_constraint_list rate_list;
+ struct snd_pcm_hw_constraint_list channels_list;
+ struct snd_pcm_hw_constraint_list sample_bits_list;
+
+ struct work_struct period_elapsed_work;
+ struct hdac_ext_link *link;
+ struct snd_pcm_substream *substream;
+};
void avs_period_elapsed(struct snd_pcm_substream *substream);
+int avs_dai_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai);
+void avs_dai_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai);
+int avs_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *fe_params,
+ struct snd_pcm_hw_params *be_params,
+ struct snd_soc_dai *dai, int dma_id);
+int avs_dai_be_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *be_params,
+ struct snd_soc_dai *dai, int dma_id);
+int avs_dai_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai);
+
+extern struct snd_soc_component_driver avs_component_driver;
+int avs_register_component(struct device *dev, const char *name,
+ struct snd_soc_component_driver *drv,
+ struct snd_soc_dai_driver *cpu_dais, int num_cpu_dais);
+
+int avs_register_dmic_component(struct avs_dev *adev, const char *name);
+int avs_register_i2s_component(struct avs_dev *adev, const char *name, unsigned long port_mask,
+ unsigned long *tdms);
+int avs_register_hda_component(struct avs_dev *adev, const char *name);
#endif
--
2.34.1
^ permalink raw reply [flat|nested] 11+ messages in thread* [PATCH v3 7/7] ASoC: Intel: avs: SoundWire PCM BE operations
2026-11-06 12:15 [PATCH v3 0/7] ASoC: Intel: avs: Soundwire support Cezary Rojewski
` (5 preceding siblings ...)
2026-11-06 12:15 ` [PATCH v3 6/7] ASoC: Intel: avs: Expose common PCM functions Cezary Rojewski
@ 2026-11-06 12:15 ` Cezary Rojewski
2026-11-06 12:55 ` Pierre-Louis Bossart
6 siblings, 1 reply; 11+ messages in thread
From: Cezary Rojewski @ 2026-11-06 12:15 UTC (permalink / raw)
To: linux-sound
Cc: broonie, tiwai, perex, amade, linux-kernel, pierre-louis.bossart,
yung-chuan.liao, vkoul, Cezary Rojewski
Implement the back-end PCM operations for the SoundWire Link to support
data transfer over SoundWire interface. Most of the actual job is done
by the SoundWire library, thus code added here just invokes them in
correct order.
Signed-off-by: Cezary Rojewski <cezary.rojewski@intel.com>
---
sound/soc/intel/avs/Makefile | 2 +-
sound/soc/intel/avs/messages.h | 2 +
sound/soc/intel/avs/path.c | 2 +
sound/soc/intel/avs/pcm.h | 3 +
sound/soc/intel/avs/pcm_sdw.c | 291 ++++++++++++++++++++++++++++++++
sound/soc/intel/avs/soundwire.h | 7 +
6 files changed, 306 insertions(+), 1 deletion(-)
create mode 100644 sound/soc/intel/avs/pcm_sdw.c
diff --git a/sound/soc/intel/avs/Makefile b/sound/soc/intel/avs/Makefile
index 042e8dd232f8..fab7e1ba622d 100644
--- a/sound/soc/intel/avs/Makefile
+++ b/sound/soc/intel/avs/Makefile
@@ -15,7 +15,7 @@ snd-soc-avs-y += probes.o debugfs.o
endif
ifneq ($(CONFIG_SND_SOC_INTEL_AVS_SOUNDWIRE),)
-snd-soc-avs-y += soundwire.o
+snd-soc-avs-y += soundwire.o pcm_sdw.o
endif
obj-$(CONFIG_SND_SOC_INTEL_AVS) += snd-soc-avs.o
diff --git a/sound/soc/intel/avs/messages.h b/sound/soc/intel/avs/messages.h
index 55c04b0142ae..d1ffc640dda2 100644
--- a/sound/soc/intel/avs/messages.h
+++ b/sound/soc/intel/avs/messages.h
@@ -750,6 +750,8 @@ enum avs_dma_type {
AVS_DMA_DMIC_LINK_INPUT = 11,
AVS_DMA_I2S_LINK_OUTPUT = 12,
AVS_DMA_I2S_LINK_INPUT = 13,
+ AVS_DMA_SDW_LINK_OUTPUT = 16,
+ AVS_DMA_SDW_LINK_INPUT = 17,
};
union avs_virtual_index {
diff --git a/sound/soc/intel/avs/path.c b/sound/soc/intel/avs/path.c
index a8a2b3484338..60d9245894e7 100644
--- a/sound/soc/intel/avs/path.c
+++ b/sound/soc/intel/avs/path.c
@@ -260,6 +260,8 @@ static void avs_init_node_id(union avs_connector_node_id *node_id,
case AVS_DMA_HDA_LINK_OUTPUT:
case AVS_DMA_HDA_LINK_INPUT:
+ case AVS_DMA_SDW_LINK_OUTPUT:
+ case AVS_DMA_SDW_LINK_INPUT:
node_id->vindex = te->copier.vindex.val | dma_id;
break;
diff --git a/sound/soc/intel/avs/pcm.h b/sound/soc/intel/avs/pcm.h
index a5093e62da26..f2620d6f3479 100644
--- a/sound/soc/intel/avs/pcm.h
+++ b/sound/soc/intel/avs/pcm.h
@@ -35,6 +35,9 @@ struct avs_dma_data {
struct work_struct period_elapsed_work;
struct hdac_ext_link *link;
+ struct sdw_stream_runtime *sdw_stream;
+ struct sdw_cdns_pdi *pdi;
+ bool sdw_suspended;
struct snd_pcm_substream *substream;
};
diff --git a/sound/soc/intel/avs/pcm_sdw.c b/sound/soc/intel/avs/pcm_sdw.c
new file mode 100644
index 000000000000..578162e9fc4d
--- /dev/null
+++ b/sound/soc/intel/avs/pcm_sdw.c
@@ -0,0 +1,291 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright(c) 2024-2026 Intel Corporation
+//
+// Authors: Cezary Rojewski <cezary.rojewski@intel.com>
+// Amadeusz Slawinski <amade@asmblr.net>
+//
+
+#include <linux/device.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_intel.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-acpi.h>
+#include <sound/soc-component.h>
+#include <sound/soc-dai.h>
+#include "avs.h"
+#include "path.h"
+#include "pcm.h"
+#include "soundwire.h"
+#include "topology.h"
+
+static int avs_sdw_dai_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct sdw_stream_runtime *sdw_stream;
+ struct snd_soc_acpi_mach *mach;
+ struct snd_soc_dai *codec_dai;
+ struct avs_dma_data *data;
+ struct sdw_cdns_pdi *pdi;
+ struct sdw_slave *codec;
+ int dir, ret;
+
+ mach = dev_get_platdata(dai->component->card->dev);
+ codec = mach->pdata;
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ dir = SDW_DATA_DIR_RX;
+ else
+ dir = SDW_DATA_DIR_TX;
+
+ ret = avs_dai_startup(substream, dai);
+ if (ret)
+ return ret;
+
+ sdw_stream = sdw_alloc_stream(substream->name, SDW_STREAM_PCM);
+ if (!sdw_stream) {
+ ret = -ENOMEM;
+ goto err_alloc_stream;
+ }
+
+ pdi = sdw_cdns_assign_pdi(codec->bus, dir);
+ if (!pdi) {
+ ret = -EBUSY;
+ goto err_assign_pdi;
+ }
+
+ /* Fulfill the codec needs by setting the sdw_stream. */
+ codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+ snd_soc_dai_set_stream(codec_dai, sdw_stream, substream->stream);
+
+ data = snd_soc_dai_get_dma_data(dai, substream);
+ data->sdw_stream = sdw_stream;
+ data->pdi = pdi;
+ return 0;
+
+err_assign_pdi:
+ sdw_release_stream(sdw_stream);
+err_alloc_stream:
+ avs_dai_shutdown(substream, dai);
+ return ret;
+}
+
+static void avs_sdw_dai_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_acpi_mach *mach;
+ struct snd_soc_dai *codec_dai;
+ struct avs_dma_data *data;
+ struct sdw_slave *codec;
+
+ codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+ data = snd_soc_dai_get_dma_data(dai, substream);
+ mach = dev_get_platdata(dai->component->card->dev);
+ codec = mach->pdata;
+
+ snd_soc_dai_set_stream(codec_dai, NULL, substream->stream);
+ sdw_cdns_release_pdi(codec->bus, data->pdi);
+ sdw_release_stream(data->sdw_stream);
+ avs_dai_shutdown(substream, dai);
+}
+
+static int avs_sdw_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params,
+ struct snd_soc_dai *dai)
+{
+ struct sdw_stream_config sconfig = {0};
+ struct sdw_port_config pconfig = {0};
+ struct snd_soc_acpi_mach *mach;
+ struct avs_dma_data *data;
+ struct sdw_cdns_pdi *pdi;
+ struct sdw_slave *codec;
+ int ret;
+
+ data = snd_soc_dai_get_dma_data(dai, substream);
+ if (data->path)
+ return 0;
+
+ mach = dev_get_platdata(dai->component->card->dev);
+ codec = mach->pdata;
+ pdi = data->pdi;
+ pdi->ch_count = params_channels(hw_params);
+ pdi->h_ch_num = pdi->ch_count - 1;
+ pdi->l_ch_num = 0;
+ sdw_intel_pdi_config(codec->bus, pdi);
+
+ if (data->sdw_suspended)
+ return avs_dai_be_hw_params(substream, hw_params, dai, pdi->intel_alh_id);
+
+ sconfig.direction = pdi->dir;
+ sconfig.ch_count = pdi->ch_count;
+ sconfig.frame_rate = params_rate(hw_params);
+ sconfig.type = SDW_STREAM_PCM;
+ sconfig.bps = snd_pcm_format_width(params_format(hw_params));
+ pconfig.num = pdi->num;
+ pconfig.ch_mask = (1 << pdi->ch_count) - 1;
+
+ ret = sdw_stream_add_master(codec->bus, &sconfig, &pconfig, 1, data->sdw_stream);
+ if (ret) {
+ sdw_stream_remove_master(codec->bus, data->sdw_stream);
+ return ret;
+ }
+
+ ret = avs_dai_be_hw_params(substream, hw_params, dai, pdi->intel_alh_id);
+ if (ret)
+ sdw_stream_remove_master(codec->bus, data->sdw_stream);
+ return ret;
+}
+
+static int avs_sdw_dai_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+ struct snd_soc_acpi_mach *mach;
+ struct avs_dma_data *data;
+ struct sdw_slave *codec;
+
+ data = snd_soc_dai_get_dma_data(dai, substream);
+ if (!data->path)
+ return 0;
+
+ mach = dev_get_platdata(dai->component->card->dev);
+ codec = mach->pdata;
+
+ avs_path_reset(data->path);
+ avs_path_free(data->path);
+ data->path = NULL;
+ sdw_deprepare_stream(data->sdw_stream);
+ if (!data->sdw_suspended)
+ sdw_stream_remove_master(codec->bus, data->sdw_stream);
+
+ return 0;
+}
+
+static int avs_sdw_dai_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+ struct avs_dma_data *data;
+ int ret;
+
+ data = snd_soc_dai_get_dma_data(dai, substream);
+
+ /*
+ * There is no guarantee trigger(RESUME) fires, so reset the flag here.
+ * Ensures there is no sdw_master leak should hw_free() occur without resuming.
+ */
+ data->sdw_suspended = false;
+
+ ret = sdw_prepare_stream(data->sdw_stream);
+ if (ret) {
+ dev_err(dai->dev, "sdw stream prepare failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = avs_dai_prepare(substream, dai);
+ if (ret)
+ sdw_deprepare_stream(data->sdw_stream);
+ return ret;
+}
+
+static int avs_sdw_dai_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct avs_dma_data *data;
+ int ret = 0;
+
+ data = snd_soc_dai_get_dma_data(dai, substream);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_RESUME:
+ if (rtd->dai_link->ignore_suspend)
+ break;
+ fallthrough;
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ ret = sdw_enable_stream(data->sdw_stream);
+ if (ret)
+ break;
+
+ ret = avs_path_run(data->path, AVS_TPLG_TRIGGER_AUTO);
+ if (ret)
+ sdw_disable_stream(data->sdw_stream);
+ break;
+
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ if (rtd->dai_link->ignore_suspend)
+ break;
+ data->sdw_suspended = true;
+ fallthrough;
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ case SNDRV_PCM_TRIGGER_STOP:
+ avs_path_pause(data->path);
+
+ ret = sdw_disable_stream(data->sdw_stream);
+ if (ret)
+ dev_err(dai->dev, "disable sdw stream failed: %d\n", ret);
+ break;
+
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static const struct snd_soc_dai_ops avs_sdw_dai_ops = {
+ .startup = avs_sdw_dai_startup,
+ .shutdown = avs_sdw_dai_shutdown,
+ .hw_params = avs_sdw_dai_hw_params,
+ .hw_free = avs_sdw_dai_hw_free,
+ .prepare = avs_sdw_dai_prepare,
+ .trigger = avs_sdw_dai_trigger,
+};
+
+static const struct snd_soc_dai_driver avs_sdw_dai_template = {
+ .playback = {
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_8000_192000 |
+ SNDRV_PCM_RATE_KNOT,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .subformats = SNDRV_PCM_SUBFMTBIT_MSBITS_20 |
+ SNDRV_PCM_SUBFMTBIT_MSBITS_24 |
+ SNDRV_PCM_SUBFMTBIT_MSBITS_MAX,
+ },
+ .capture = {
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_8000_192000 |
+ SNDRV_PCM_RATE_KNOT,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .subformats = SNDRV_PCM_SUBFMTBIT_MSBITS_20 |
+ SNDRV_PCM_SUBFMTBIT_MSBITS_24 |
+ SNDRV_PCM_SUBFMTBIT_MSBITS_MAX,
+ },
+ .ops = &avs_sdw_dai_ops,
+};
+
+int avs_register_sdw_component(struct avs_dev *adev, const char *name, u32 link_id)
+{
+ struct snd_soc_dai_driver *dai;
+ struct device *dev = adev->dev;
+
+ dai = devm_kzalloc(dev, sizeof(*dai), GFP_KERNEL);
+ if (!dai)
+ return -ENOMEM;
+
+ memcpy(dai, &avs_sdw_dai_template, sizeof(*dai));
+
+ dai->name = devm_kasprintf(dev, GFP_KERNEL, "avs-sdw%d-pin", link_id);
+ if (!dai->name)
+ return -ENOMEM;
+
+ dai->playback.stream_name = devm_kasprintf(dev, GFP_KERNEL, "%s Tx", dai->name);
+ dai->capture.stream_name = devm_kasprintf(dev, GFP_KERNEL, "%s Rx", dai->name);
+
+ if (!dai->playback.stream_name || !dai->capture.stream_name)
+ return -ENOMEM;
+
+ return avs_register_component(dev, name, &avs_component_driver, dai, 1);
+}
diff --git a/sound/soc/intel/avs/soundwire.h b/sound/soc/intel/avs/soundwire.h
index e57e29ed16bf..b2279613a326 100644
--- a/sound/soc/intel/avs/soundwire.h
+++ b/sound/soc/intel/avs/soundwire.h
@@ -22,6 +22,8 @@ void avs_sdw_enable_irq(struct avs_dev *adev, bool enable);
void avs_sdw_process_wakeen_event(struct avs_dev *adev);
irqreturn_t avs_sdw_thread(int irq, struct avs_dev *adev);
+int avs_register_sdw_component(struct avs_dev *adev, const char *name, u32 link_id);
+
#else
static inline int avs_sdw_probe(struct avs_dev *adev)
@@ -51,6 +53,11 @@ static inline irqreturn_t avs_sdw_thread(int irq, struct avs_dev *adev)
return IRQ_HANDLED;
}
+static inline int avs_register_sdw_component(struct avs_dev *adev, const char *name, u32 link_id)
+{
+ return 0;
+}
+
#endif
#endif /* __SOUND_SOC_INTEL_AVS_SOUNDWIRE_H */
--
2.34.1
^ permalink raw reply [flat|nested] 11+ messages in thread* Re: [PATCH v3 7/7] ASoC: Intel: avs: SoundWire PCM BE operations
2026-11-06 12:15 ` [PATCH v3 7/7] ASoC: Intel: avs: SoundWire PCM BE operations Cezary Rojewski
@ 2026-11-06 12:55 ` Pierre-Louis Bossart
0 siblings, 0 replies; 11+ messages in thread
From: Pierre-Louis Bossart @ 2026-11-06 12:55 UTC (permalink / raw)
To: Cezary Rojewski, linux-sound
Cc: broonie, tiwai, perex, amade, linux-kernel, yung-chuan.liao, vkoul
I am afraid this patch needs to be completely re-visited. You mixed dai
and dailink layers, and that explains most of the misunderstandings in
the review of the PDI stuff where I mentioned that what matters is how
the dailink is created.
> +static int avs_sdw_dai_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
> +{
> + struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
> + struct sdw_stream_runtime *sdw_stream;
> + struct snd_soc_acpi_mach *mach;
> + struct snd_soc_dai *codec_dai;
> + struct avs_dma_data *data;
> + struct sdw_cdns_pdi *pdi;
> + struct sdw_slave *codec;
> + int dir, ret;
> +
> + mach = dev_get_platdata(dai->component->card->dev);
> + codec = mach->pdata;
> + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
> + dir = SDW_DATA_DIR_RX;
> + else
> + dir = SDW_DATA_DIR_TX;
> +
> + ret = avs_dai_startup(substream, dai);
> + if (ret)
> + return ret;
> +
> + sdw_stream = sdw_alloc_stream(substream->name, SDW_STREAM_PCM);
> + if (!sdw_stream) {
> + ret = -ENOMEM;
> + goto err_alloc_stream;
> + }
This isn't quite the intented use of the SoundWire stream API, which
provides a sdw_startup_stream() that's supposed to be invoked during the
dailink creation in the machine driver that binds cpu and codec dais
"
sdw_alloc_stream should be called only once per stream. Typically
invoked from ALSA/ASoC machine/platform driver.
"
The dailink maps exactly 1:1 to the SoundWire stream concept, and that's
what allows us to deal with aggregation - when multiple cpu_dais on
multiple links are connected to multiple amps. That's the #1 reason why
SoundWire exists and the #1 difference to other audio interfaces.
BPT/BRA support is the only case where sdw_alloc_stream() is used in the
Intel platform driver, since there's clearly no concept of cpu_ or codec
DAI.
> +
> + pdi = sdw_cdns_assign_pdi(codec->bus, dir);
> + if (!pdi) {
> + ret = -EBUSY;
> + goto err_assign_pdi;
> + }
> +
> + /* Fulfill the codec needs by setting the sdw_stream. */
> + codec_dai = snd_soc_rtd_to_codec(rtd, 0);
> + snd_soc_dai_set_stream(codec_dai, sdw_stream, substream->stream);
That absolutely does not scale to the case where you have multiple
cpu_dais and multiple codec_dais.
Please take a look at the sof_sdw machine driver (same solution used by
AMD), and please use the sdw_utils library to deal with all the stream
management.
> + data = snd_soc_dai_get_dma_data(dai, substream);
> + data->sdw_stream = sdw_stream;
> + data->pdi = pdi;
> + return 0;
> +
> +err_assign_pdi:
> + sdw_release_stream(sdw_stream);
> +err_alloc_stream:
> + avs_dai_shutdown(substream, dai);
> + return ret;
> +}
^ permalink raw reply [flat|nested] 11+ messages in thread