From: Cezary Rojewski <cezary.rojewski@intel.com>
To: linux-sound@vger.kernel.org
Cc: broonie@kernel.org, tiwai@suse.com, perex@perex.cz,
amade@asmblr.net, linux-kernel@vger.kernel.org,
yung-chuan.liao@linux.intel.com, pierre-louis.bossart@linux.dev,
vkoul@kernel.org, Cezary Rojewski <cezary.rojewski@intel.com>
Subject: [PATCH 7/7] ASoC: Intel: avs: SoundWire PCM BE operations
Date: Mon, 2 Nov 2026 19:10:42 +0100 [thread overview]
Message-ID: <20261102181042.1922005-8-cezary.rojewski@intel.com> (raw)
In-Reply-To: <20261102181042.1922005-1-cezary.rojewski@intel.com>
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 | 2 +
sound/soc/intel/avs/pcm_sdw.c | 276 ++++++++++++++++++++++++++++++++
sound/soc/intel/avs/soundwire.h | 7 +
6 files changed, 290 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 1d48953c7a31..07a82a6f5b23 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_SOUNDWIRE_INTEL),)
-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 ac127ade4ca1..66d6896a4af7 100644
--- a/sound/soc/intel/avs/pcm.h
+++ b/sound/soc/intel/avs/pcm.h
@@ -37,6 +37,8 @@ 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;
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..27cb285eca09
--- /dev/null
+++ b/sound/soc/intel/avs/pcm_sdw.c
@@ -0,0 +1,276 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright(c) 2024-2026 Intel Corporation
+//
+// Authors: Cezary Rojewski <cezary.rojewski@intel.com>
+// Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
+//
+
+#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_acpi_mach *mach;
+ struct avs_dma_data *data;
+ struct sdw_slave *codec;
+
+ data = snd_soc_dai_get_dma_data(dai, substream);
+ mach = dev_get_platdata(dai->component->card->dev);
+ codec = mach->pdata;
+
+ 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);
+
+ 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)
+ 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;
+ 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;
+
+ ret = avs_path_reset(data->path);
+ if (ret < 0)
+ dev_err(dai->dev, "reset BE path failed: %d\n", ret);
+ avs_path_free(data->path);
+ data->path = NULL;
+ sdw_deprepare_stream(data->sdw_stream);
+ 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);
+
+ ret = sdw_prepare_stream(data->sdw_stream);
+ if (ret) {
+ dev_err(dai->dev, "sdw stream prepare failed: %d\n", ret);
+ return ret;
+ }
+
+ return avs_dai_prepare(substream, dai);
+}
+
+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 < 0)
+ dev_err(dai->dev, "run BE path failed: %d\n", ret);
+ break;
+
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ if (rtd->dai_link->ignore_suspend)
+ break;
+ fallthrough;
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ case SNDRV_PCM_TRIGGER_STOP:
+ ret = avs_path_pause(data->path);
+ if (ret < 0)
+ dev_err(dai->dev, "pause BE path failed: %d\n", ret);
+
+ 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 96ce29ab21a3..1cf77d5ec2d8 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
prev parent reply other threads:[~2026-11-02 18:03 UTC|newest]
Thread overview: 10+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-02 18:10 [PATCH 0/7] ASoC: Intel: avs: Soundwire support Cezary Rojewski
2026-11-02 18:10 ` [PATCH 1/7] soundwire: intel: Define stubs when library is disabled Cezary Rojewski
2026-11-02 18:10 ` [PATCH 2/7] soundwire: intel: Allow for dynamic assignment of PDIs Cezary Rojewski
2026-11-02 18:10 ` [PATCH 3/7] soundwire: intel: Split PDIs and DAIs initialization Cezary Rojewski
2026-11-02 18:10 ` [PATCH 4/7] ASoC: Intel: avs: Pass irq to dsp_interrupt callback Cezary Rojewski
2026-11-02 18:10 ` [PATCH 5/7] ASoC: Intel: avs: SoundWire support Cezary Rojewski
2026-11-02 18:10 ` [PATCH 6/7] ASoC: Intel: avs: Expose common PCM functions Cezary Rojewski
2026-11-02 18:18 ` Amadeusz Sławiński
2026-11-02 18:20 ` Cezary Rojewski
2026-11-02 18:10 ` Cezary Rojewski [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261102181042.1922005-8-cezary.rojewski@intel.com \
--to=cezary.rojewski@intel.com \
--cc=amade@asmblr.net \
--cc=broonie@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-sound@vger.kernel.org \
--cc=perex@perex.cz \
--cc=pierre-louis.bossart@linux.dev \
--cc=tiwai@suse.com \
--cc=vkoul@kernel.org \
--cc=yung-chuan.liao@linux.intel.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®