Linux Sound subsystem development
 help / color / mirror / Atom feed
* [PATCH 00/23] ALSA compress / ASoC compress / SOF: Compressed audio support with IPC4
@ 2026-09-04  7:31 Peter Ujfalusi
  2026-09-04  7:31 ` [PATCH 01/23] ALSA: compress: pin card module while stream is open Peter Ujfalusi
                   ` (22 more replies)
  0 siblings, 23 replies; 49+ messages in thread
From: Peter Ujfalusi @ 2026-09-04  7:31 UTC (permalink / raw)
  To: vkoul, perex, tiwai, lgirdwood, broonie, srinivas.kandagatla
  Cc: linux-sound, kai.vehmanen, yung-chuan.liao, pierre-louis.bossart,
	daniel.baluta, Vijendar.Mukunda

Hi,

This series adds compressed audio support for SOF with IPC4 firmware.
Supported formats are PCM, MP3, AAC and VORBIS, the feature set is
PAUSE_PUSH/RELEASE, DRAIN and position reporting.

Patches 1-5 are prerequisites in the ALSA compress core and in
soc-compress.c.

These are core fixes rather than driver level workarounds on purpose.
The other users of compressed DPCM have DSP internal ports on their
BEs, driven by code they own. An Intel/SOF BE ends at a third party
CODEC driver which is shared with the non DPCM case and has no reason
to know that the FE is compressed. That set of CODECs is open ended,
so teaching each of them to cope with a compressed FE does not scale.
The core has to hand a BE the same substream state whether the FE is
a PCM or a compressed one.

Two of these patches change behaviour for the existing users of
compressed DPCM, so they would need a look from Qualcomm and NXP:

 - "ASoC: soc-compress: Implement trigger FE-BE sequencing as with
   normal PCMs" makes the FE/BE trigger order follow
   dai_link->trigger[], the way dpcm_fe_dai_do_trigger() does it.
   Neither sound/soc/qcom/ nor sound/soc/fsl/ sets it, so both default
   to SND_SOC_DPCM_TRIGGER_PRE: START keeps the current order, but
   STOP, SUSPEND and PAUSE_PUSH now trigger the BE before the FE.

 - "ASoC: soc-compress: Stop running dpcm on free" sends a STOP trigger
   from soc_compr_free_fe() if the DPCM is still in START state, which
   is the case when the last trigger the stream received was a DRAIN.

Patches 6-20 implement the IPC4 compressed support, patch 21 is a
Kconfig cleanup. The last two patches enable the feature, in Kconfig
for TGL and later and in the topology parser, so the compressed device
type only becomes usable once the code behind it is in place.

Testing:

 - IPC4: Arrow Lake, compressed playback on speaker and headphone
   (cs42l43), MP3, AAC and VORBIS.
 - IPC3: tested by NXP. This does not cover the STOP on free path
   above, as they do not implement DRAIN.

Regards,
Peter
---
Peter Ujfalusi (17):
  ALSA: compress: pin card module while stream is open
  ALSA: compress: stop active streams on disconnect
  ASoC: soc-compress: Provide a runtime for the compressed FE substream
  ASoC: soc-compress: Implement trigger FE-BE sequencing as with normal
    PCMs
  ASoC: soc-compress: Stop running dpcm on free
  ASoC: SOF: ipc4-pcm: harden pipeline teardown races
  ASoC: SOF: sof-audio: do not dereference swidget->spipe
    unconditionally on free
  ASoC: SOF: ipc4: Add definition of module data in init_ext object type
  ASoC: SOF: ipc4-topology: Support init_ext_module_data for process
    modules
  ASoC: SOF: ipc4-pcm: Make the timestamp info usable outside of
    ipc4-pcm.c
  ASoC: SOF: ipc4/ipc4-loader: Add SOF_INFO and CODEC_INFO to
    fw_config_params
  ASoC: SOF: ipc4-pcm: Handle COMPR DRAIN triggers as EOS pipeline state
  ASoC: SOF: ipc4-topology: Set FAST_MODE for host copier in compr mode
  ASoC: SOF: Add support for IPC4 compressed
  ASoC: SOF: ipc4: Handle compressed drain done notification from
    firmware
  ASoC: SOF: Intel: Kconfig: Remove redundant IPC version selects
  ASoC: SOF: Intel: Kconfig: Select compress support for TGL+ platforms

Ranjani Sridharan (6):
  ASoC: SOF: compress: Rename compress ops with ipc3 prefix
  ASoC: SOF: sof-audio: Expose a couple of functions
  ASoC: SOF: pcm: Modify the signature of a couple of PCM IPC ops
  ASoC: SOF: intel: hda-stream: Clear the current position when
    releasing stream
  ASoC: SOF: ops: Add new platform-specific ops for compress
  ASoC: SOF: topology: Add support for decoder and encoder widgets

 include/sound/compress_driver.h               |   2 +
 include/sound/sof/ipc4/header.h               |  24 +-
 sound/core/compress_offload.c                 |  77 +-
 sound/soc/soc-compress.c                      | 190 ++++-
 sound/soc/sof/Makefile                        |   4 +-
 sound/soc/sof/core.c                          |  10 +-
 sound/soc/sof/intel/Kconfig                   |   9 +-
 sound/soc/sof/intel/hda-common-ops.c          |   8 +
 sound/soc/sof/intel/hda-pcm.c                 | 142 ++++
 sound/soc/sof/intel/hda-stream.c              |  70 +-
 sound/soc/sof/intel/hda.h                     |  15 +
 sound/soc/sof/{compress.c => ipc3-compress.c} | 145 +---
 sound/soc/sof/ipc3-pcm.c                      |  29 +-
 sound/soc/sof/ipc3-priv.h                     |   3 +
 sound/soc/sof/ipc4-compress.c                 | 776 ++++++++++++++++++
 sound/soc/sof/ipc4-loader.c                   |  51 ++
 sound/soc/sof/ipc4-pcm.c                      | 125 +--
 sound/soc/sof/ipc4-priv.h                     |  45 +
 sound/soc/sof/ipc4-topology.c                 | 115 ++-
 sound/soc/sof/ipc4-topology.h                 |   9 +
 sound/soc/sof/ipc4.c                          |  13 +-
 sound/soc/sof/ops.h                           |  74 ++
 sound/soc/sof/pcm.c                           |  18 +-
 sound/soc/sof/sof-audio.c                     |  89 +-
 sound/soc/sof/sof-audio.h                     |  23 +-
 sound/soc/sof/sof-priv.h                      |  12 +
 sound/soc/sof/topology.c                      |   2 +
 27 files changed, 1773 insertions(+), 307 deletions(-)
 rename sound/soc/sof/{compress.c => ipc3-compress.c} (66%)
 create mode 100644 sound/soc/sof/ipc4-compress.c

-- 
2.55.0


^ permalink raw reply	[flat|nested] 49+ messages in thread

* [PATCH 01/23] ALSA: compress: pin card module while stream is open
  2026-09-04  7:31 [PATCH 00/23] ALSA compress / ASoC compress / SOF: Compressed audio support with IPC4 Peter Ujfalusi
@ 2026-09-04  7:31 ` Peter Ujfalusi
  2026-09-04  7:50   ` Takashi Iwai
  2026-09-04  7:31 ` [PATCH 02/23] ALSA: compress: stop active streams on disconnect Peter Ujfalusi
                   ` (21 subsequent siblings)
  22 siblings, 1 reply; 49+ messages in thread
From: Peter Ujfalusi @ 2026-09-04  7:31 UTC (permalink / raw)
  To: vkoul, perex, tiwai, lgirdwood, broonie, srinivas.kandagatla
  Cc: linux-sound, kai.vehmanen, yung-chuan.liao, pierre-louis.bossart,
	daniel.baluta, Vijendar.Mukunda

Take a module reference in snd_compr_open() and release it in
snd_compr_free(). This pins the card driver module for the
lifetime of an open compress stream and prevents card removal
while the stream file is still in use.

Adjust the open() cleanup paths to drop the added module
reference only when it was acquired, and keep release ordering
safe by dropping the module reference before freeing stream
data.

Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
Reviewed-by: Liam Girdwood <liam.r.girdwood@intel.com>
---
 sound/core/compress_offload.c | 28 ++++++++++++++++++----------
 1 file changed, 18 insertions(+), 10 deletions(-)

diff --git a/sound/core/compress_offload.c b/sound/core/compress_offload.c
index 23d62fede06e..c7d2f4fb3e02 100644
--- a/sound/core/compress_offload.c
+++ b/sound/core/compress_offload.c
@@ -75,11 +75,11 @@ static inline void snd_compr_task_free_all(struct snd_compr_stream *stream) { }
 static int snd_compr_open(struct inode *inode, struct file *f)
 {
 	struct snd_compr *compr;
-	struct snd_compr_file *data;
-	struct snd_compr_runtime *runtime;
+	struct snd_compr_file *data = NULL;
+	struct snd_compr_runtime *runtime = NULL;
 	enum snd_compr_direction dirn;
 	int maj = imajor(inode);
-	int ret;
+	int ret = 0;
 
 	if ((f->f_flags & O_ACCMODE) == O_WRONLY)
 		dirn = SND_COMPRESS_PLAYBACK;
@@ -101,16 +101,21 @@ static int snd_compr_open(struct inode *inode, struct file *f)
 		return -ENODEV;
 	}
 
+	if (!try_module_get(compr->card->module)) {
+		snd_card_unref(compr->card);
+		return -EFAULT;
+	}
+
 	if (dirn != compr->direction) {
 		pr_err("this device doesn't support this direction\n");
-		snd_card_unref(compr->card);
-		return -EINVAL;
+		ret = -EINVAL;
+		goto __error;
 	}
 
 	data = kzalloc(sizeof(*data), GFP_KERNEL);
 	if (!data) {
-		snd_card_unref(compr->card);
-		return -ENOMEM;
+		ret = -ENOMEM;
+		goto __error;
 	}
 
 	INIT_DELAYED_WORK(&data->stream.error_work, error_delayed_work);
@@ -121,9 +126,8 @@ static int snd_compr_open(struct inode *inode, struct file *f)
 	data->stream.device = compr;
 	runtime = kzalloc(sizeof(*runtime), GFP_KERNEL);
 	if (!runtime) {
-		kfree(data);
-		snd_card_unref(compr->card);
-		return -ENOMEM;
+		ret = -ENOMEM;
+		goto __error;
 	}
 	runtime->state = SNDRV_PCM_STATE_OPEN;
 	init_waitqueue_head(&runtime->sleep);
@@ -134,9 +138,12 @@ static int snd_compr_open(struct inode *inode, struct file *f)
 	f->private_data = (void *)data;
 	scoped_guard(mutex, &compr->lock)
 		ret = compr->ops->open(&data->stream);
+
+__error:
 	if (ret) {
 		kfree(runtime);
 		kfree(data);
+		module_put(compr->card->module);
 	}
 	snd_card_unref(compr->card);
 	return ret;
@@ -164,6 +171,7 @@ static int snd_compr_free(struct inode *inode, struct file *f)
 	data->stream.ops->free(&data->stream);
 	if (!data->stream.runtime->dma_buffer_p)
 		kfree(data->stream.runtime->buffer);
+	module_put(data->stream.device->card->module);
 	kfree(data->stream.runtime);
 	kfree(data);
 	return 0;
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 49+ messages in thread

* [PATCH 02/23] ALSA: compress: stop active streams on disconnect
  2026-09-04  7:31 [PATCH 00/23] ALSA compress / ASoC compress / SOF: Compressed audio support with IPC4 Peter Ujfalusi
  2026-09-04  7:31 ` [PATCH 01/23] ALSA: compress: pin card module while stream is open Peter Ujfalusi
@ 2026-09-04  7:31 ` Peter Ujfalusi
  2026-09-04  7:51   ` Takashi Iwai
  2026-09-04  7:31 ` [PATCH 03/23] ASoC: soc-compress: Provide a runtime for the compressed FE substream Peter Ujfalusi
                   ` (20 subsequent siblings)
  22 siblings, 1 reply; 49+ messages in thread
From: Peter Ujfalusi @ 2026-09-04  7:31 UTC (permalink / raw)
  To: vkoul, perex, tiwai, lgirdwood, broonie, srinivas.kandagatla
  Cc: linux-sound, kai.vehmanen, yung-chuan.liao, pierre-louis.bossart,
	daniel.baluta, Vijendar.Mukunda

Track open compressed streams per device so disconnect can stop
active streams and wake waiters before snd_unregister_device().

This aligns compressed stream teardown with PCM disconnect behavior
and prevents active userspace streams from running into unregister
races.

Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
Reviewed-by: Liam Girdwood <liam.r.girdwood@intel.com>
---
 include/sound/compress_driver.h |  2 ++
 sound/core/compress_offload.c   | 49 +++++++++++++++++++++++++++------
 2 files changed, 42 insertions(+), 9 deletions(-)

diff --git a/include/sound/compress_driver.h b/include/sound/compress_driver.h
index 9e3d801e45ec..84f51edc3f9d 100644
--- a/include/sound/compress_driver.h
+++ b/include/sound/compress_driver.h
@@ -188,6 +188,7 @@ struct snd_compr_ops {
  * @card: sound card pointer
  * @direction: Playback or capture direction
  * @lock: device lock
+ * @open_list: list of open compress files
  * @device: device id
  * @use_pause_in_draining: allow pause in draining, true when set
  */
@@ -199,6 +200,7 @@ struct snd_compr {
 	struct snd_card *card;
 	unsigned int direction;
 	struct mutex lock;
+	struct list_head open_list;
 	int device;
 	bool use_pause_in_draining;
 #ifdef CONFIG_SND_VERBOSE_PROCFS
diff --git a/sound/core/compress_offload.c b/sound/core/compress_offload.c
index c7d2f4fb3e02..f6e54ffc8575 100644
--- a/sound/core/compress_offload.c
+++ b/sound/core/compress_offload.c
@@ -42,6 +42,7 @@
 #endif
 
 struct snd_compr_file {
+	struct list_head list;
 	unsigned long caps;
 	struct snd_compr_stream stream;
 };
@@ -117,6 +118,7 @@ static int snd_compr_open(struct inode *inode, struct file *f)
 		ret = -ENOMEM;
 		goto __error;
 	}
+	INIT_LIST_HEAD(&data->list);
 
 	INIT_DELAYED_WORK(&data->stream.error_work, error_delayed_work);
 
@@ -136,8 +138,11 @@ static int snd_compr_open(struct inode *inode, struct file *f)
 #endif
 	data->stream.runtime = runtime;
 	f->private_data = (void *)data;
-	scoped_guard(mutex, &compr->lock)
+	scoped_guard(mutex, &compr->lock) {
 		ret = compr->ops->open(&data->stream);
+		if (!ret)
+			list_add_tail(&data->list, &compr->open_list);
+	}
 
 __error:
 	if (ret) {
@@ -153,17 +158,23 @@ static int snd_compr_free(struct inode *inode, struct file *f)
 {
 	struct snd_compr_file *data = f->private_data;
 	struct snd_compr_runtime *runtime = data->stream.runtime;
+	struct snd_compr *compr = data->stream.device;
 
 	cancel_delayed_work_sync(&data->stream.error_work);
 
-	switch (runtime->state) {
-	case SNDRV_PCM_STATE_RUNNING:
-	case SNDRV_PCM_STATE_DRAINING:
-	case SNDRV_PCM_STATE_PAUSED:
-		data->stream.ops->trigger(&data->stream, SNDRV_PCM_TRIGGER_STOP);
-		break;
-	default:
-		break;
+	scoped_guard(mutex, &compr->lock) {
+		if (!list_empty(&data->list))
+			list_del_init(&data->list);
+
+		switch (runtime->state) {
+		case SNDRV_PCM_STATE_RUNNING:
+		case SNDRV_PCM_STATE_DRAINING:
+		case SNDRV_PCM_STATE_PAUSED:
+			data->stream.ops->trigger(&data->stream, SNDRV_PCM_TRIGGER_STOP);
+			break;
+		default:
+			break;
+		}
 	}
 
 	snd_compr_task_free_all(&data->stream);
@@ -1440,8 +1451,27 @@ static int snd_compress_dev_register(struct snd_device *device)
 static int snd_compress_dev_disconnect(struct snd_device *device)
 {
 	struct snd_compr *compr;
+	struct snd_compr_file *data;
 
 	compr = device->device_data;
+	scoped_guard(mutex, &compr->lock) {
+		list_for_each_entry(data, &compr->open_list, list) {
+			switch (data->stream.runtime->state) {
+			case SNDRV_PCM_STATE_RUNNING:
+			case SNDRV_PCM_STATE_DRAINING:
+			case SNDRV_PCM_STATE_PAUSED:
+				data->stream.ops->trigger(&data->stream,
+							 SNDRV_PCM_TRIGGER_STOP);
+				break;
+			default:
+				break;
+			}
+
+			data->stream.runtime->state = SNDRV_PCM_STATE_DISCONNECTED;
+			wake_up(&data->stream.runtime->sleep);
+		}
+	}
+
 	snd_unregister_device(compr->dev);
 	return 0;
 }
@@ -1549,6 +1579,7 @@ int snd_compress_new(struct snd_card *card, int device,
 	compr->device = device;
 	compr->direction = dirn;
 	mutex_init(&compr->lock);
+	INIT_LIST_HEAD(&compr->open_list);
 
 	snd_compress_set_id(compr, id);
 
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 49+ messages in thread

* [PATCH 03/23] ASoC: soc-compress: Provide a runtime for the compressed FE substream
  2026-09-04  7:31 [PATCH 00/23] ALSA compress / ASoC compress / SOF: Compressed audio support with IPC4 Peter Ujfalusi
  2026-09-04  7:31 ` [PATCH 01/23] ALSA: compress: pin card module while stream is open Peter Ujfalusi
  2026-09-04  7:31 ` [PATCH 02/23] ALSA: compress: stop active streams on disconnect Peter Ujfalusi
@ 2026-09-04  7:31 ` Peter Ujfalusi
  2026-09-04  7:31 ` [PATCH 04/23] ASoC: soc-compress: Implement trigger FE-BE sequencing as with normal PCMs Peter Ujfalusi
                   ` (19 subsequent siblings)
  22 siblings, 0 replies; 49+ messages in thread
From: Peter Ujfalusi @ 2026-09-04  7:31 UTC (permalink / raw)
  To: vkoul, perex, tiwai, lgirdwood, broonie, srinivas.kandagatla
  Cc: linux-sound, kai.vehmanen, yung-chuan.liao, pierre-louis.bossart,
	daniel.baluta, Vijendar.Mukunda

When a compressed stream is used on the FE side of a DPCM link, the
BE is still running as 'normal' PCM.

DPCM lends the runtime of the FE to every BE it opens and
dpcm_be_reparent() re-points it at another FE when the lending one
disconnects. The internal PCM created for a compressed FE by
snd_soc_new_compress() is never opened via the PCM API, so its
substream has no runtime and the BEs of a compressed FE were left
with a NULL one.

BE DAI and codec drivers can look at substream->runtime, they add
their constraints to it in startup(), for example cs42l43, cs42l42,
hdac_hdmi.

The other users of compressed DPCM do not hit this as their BEs are
DSP internal ports, driven by code which takes everything from the
hw_params it is passed and never looks at substream->runtime. The
BEs here end at generic CODEC drivers which are shared with the non
DPCM case and cannot be expected to know about the FE type.

Allocate a runtime for the compressed FE substream while the stream
is open, initialize its constraints as snd_pcm_open() does and fill
it in from the BE parameters once the mandatory machine level
be_hw_params_fixup() has run, so that the compressed FE lends a valid
and populated runtime just like a PCM FE does.

This also allows a BE to be shared between a compressed and a PCM
FE, which is needed to play a notification over PCM to an endpoint
which a compressed stream is already using.

Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
Reviewed-by: Liam Girdwood <liam.r.girdwood@intel.com>
---
 sound/soc/soc-compress.c | 109 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 109 insertions(+)

diff --git a/sound/soc/soc-compress.c b/sound/soc/soc-compress.c
index b8402802ae78..e5a70c9b3d31 100644
--- a/sound/soc/soc-compress.c
+++ b/sound/soc/soc-compress.c
@@ -16,6 +16,7 @@
 #include <sound/core.h>
 #include <sound/compress_params.h>
 #include <sound/compress_driver.h>
+#include <sound/pcm_params.h>
 #include <sound/soc.h>
 #include <sound/initval.h>
 #include <sound/soc-dpcm.h>
@@ -130,6 +131,104 @@ static int soc_compr_open(struct snd_compr_stream *cstream)
 	return ret;
 }
 
+/*
+ * The internal PCM of a compressed FE is never opened via the PCM API, so its
+ * substream has no runtime attached to it.
+ *
+ * DPCM lends the runtime of the FE to every BE it opens and re-points it at
+ * another FE when the lending one goes away, so the compressed FE must provide
+ * one as well. Without it the BEs are left with a NULL runtime, which oopses
+ * in BE DAI and CODEC drivers looking at substream->runtime, and a BE can not
+ * be shared between a compressed and a PCM FE at all.
+ *
+ * The runtime is owned by the FE for as long as the compressed stream is open.
+ */
+static int soc_compr_alloc_fe_runtime(struct snd_soc_pcm_runtime *fe, int stream)
+{
+	struct snd_pcm_substream *fe_substream = snd_soc_dpcm_get_substream(fe, stream);
+	struct snd_pcm_hw_constraints *constrs;
+	int i;
+
+	if (!fe_substream || fe_substream->runtime)
+		return 0;
+
+	fe_substream->runtime = kzalloc_obj(*fe_substream->runtime);
+	if (!fe_substream->runtime)
+		return -ENOMEM;
+
+	/*
+	 * snd_pcm_open() initializes the constraints of a PCM runtime to
+	 * 'anything goes'. A zeroed one means an empty mask and a [0, 0]
+	 * interval instead, which the constraint helpers refine against and
+	 * reject, so initialize them the same way the PCM core does.
+	 *
+	 * The hw rules snd_pcm_hw_constraints_init() installs on top are only
+	 * evaluated by snd_pcm_hw_refine(), which never runs for this
+	 * substream, so they are not needed here.
+	 */
+	constrs = &fe_substream->runtime->hw_constraints;
+	for (i = SNDRV_PCM_HW_PARAM_FIRST_MASK; i <= SNDRV_PCM_HW_PARAM_LAST_MASK; i++)
+		snd_mask_any(constrs_mask(constrs, i));
+
+	for (i = SNDRV_PCM_HW_PARAM_FIRST_INTERVAL; i <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; i++)
+		snd_interval_any(constrs_interval(constrs, i));
+
+	return 0;
+}
+
+static void soc_compr_free_fe_runtime(struct snd_soc_pcm_runtime *fe, int stream)
+{
+	struct snd_pcm_substream *fe_substream = snd_soc_dpcm_get_substream(fe, stream);
+
+	if (!fe_substream || !fe_substream->runtime)
+		return;
+
+	/* BE startup callbacks may have added hw constraint rules */
+	kfree(fe_substream->runtime->hw_constraints.rules);
+	kfree(fe_substream->runtime);
+	fe_substream->runtime = NULL;
+}
+
+/*
+ * The BE parameters of a compressed FE are set up by the machine level
+ * be_hw_params_fixup(), which is mandatory for a compressed BE, see
+ * soc_compr_set_params_fe(). Once they are fixed up, use them to fill in the
+ * runtime the BEs have been lent, so that a BE DAI or CODEC driver sees the
+ * format it is being configured for.
+ */
+static void soc_compr_set_fe_runtime(struct snd_soc_pcm_runtime *fe, int stream)
+{
+	struct snd_pcm_substream *fe_substream = snd_soc_dpcm_get_substream(fe, stream);
+	struct snd_pcm_runtime *runtime;
+	struct snd_soc_dpcm *dpcm;
+
+	snd_soc_dpcm_mutex_assert_held(fe);
+
+	if (!fe_substream || !fe_substream->runtime)
+		return;
+
+	runtime = fe_substream->runtime;
+
+	for_each_dpcm_be(fe, stream, dpcm) {
+		struct snd_pcm_hw_params *params = &dpcm->be->dpcm[stream].hw_params;
+		int bits = snd_pcm_format_physical_width(params_format(params));
+
+		/* skip a BE which has not been fixed up */
+		if (bits <= 0)
+			continue;
+
+		runtime->access		= params_access(params);
+		runtime->format		= params_format(params);
+		runtime->subformat	= params_subformat(params);
+		runtime->channels	= params_channels(params);
+		runtime->rate		= params_rate(params);
+
+		runtime->sample_bits	= bits;
+		runtime->frame_bits	= bits * runtime->channels;
+		break;
+	}
+}
+
 static int soc_compr_open_fe(struct snd_compr_stream *cstream)
 {
 	struct snd_soc_pcm_runtime *fe = cstream->private_data;
@@ -141,6 +240,10 @@ static int soc_compr_open_fe(struct snd_compr_stream *cstream)
 
 	snd_soc_card_mutex_lock(fe->card);
 
+	ret = soc_compr_alloc_fe_runtime(fe, stream);
+	if (ret < 0)
+		goto be_err;
+
 	ret = dpcm_path_get(fe, stream, &list);
 	if (ret < 0)
 		goto be_err;
@@ -195,6 +298,7 @@ static int soc_compr_open_fe(struct snd_compr_stream *cstream)
 	dpcm_path_put(&list);
 	snd_soc_dpcm_mutex_unlock(fe);
 be_err:
+	soc_compr_free_fe_runtime(fe, stream);
 	fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO;
 	snd_soc_card_mutex_unlock(fe->card);
 	return ret;
@@ -237,6 +341,9 @@ static int soc_compr_free_fe(struct snd_compr_stream *cstream)
 
 	snd_soc_dai_compr_shutdown(cpu_dai, cstream, 0);
 
+	/* all BEs are shut down and disconnected, the runtime is unused now */
+	soc_compr_free_fe_runtime(fe, stream);
+
 	snd_soc_card_mutex_unlock(fe->card);
 	return 0;
 }
@@ -387,6 +494,8 @@ static int soc_compr_set_params_fe(struct snd_compr_stream *cstream,
 
 	snd_soc_dpcm_mutex_lock(fe);
 	ret = dpcm_be_dai_hw_params(fe, stream);
+	if (!ret)
+		soc_compr_set_fe_runtime(fe, stream);
 	snd_soc_dpcm_mutex_unlock(fe);
 	if (ret < 0)
 		goto out;
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 49+ messages in thread

* [PATCH 04/23] ASoC: soc-compress: Implement trigger FE-BE sequencing as with normal PCMs
  2026-09-04  7:31 [PATCH 00/23] ALSA compress / ASoC compress / SOF: Compressed audio support with IPC4 Peter Ujfalusi
                   ` (2 preceding siblings ...)
  2026-09-04  7:31 ` [PATCH 03/23] ASoC: soc-compress: Provide a runtime for the compressed FE substream Peter Ujfalusi
@ 2026-09-04  7:31 ` Peter Ujfalusi
  2026-09-04 19:45   ` Mark Brown
  2026-09-04  7:31 ` [PATCH 05/23] ASoC: soc-compress: Stop running dpcm on free Peter Ujfalusi
                   ` (18 subsequent siblings)
  22 siblings, 1 reply; 49+ messages in thread
From: Peter Ujfalusi @ 2026-09-04  7:31 UTC (permalink / raw)
  To: vkoul, perex, tiwai, lgirdwood, broonie, srinivas.kandagatla
  Cc: linux-sound, kai.vehmanen, yung-chuan.liao, pierre-louis.bossart,
	daniel.baluta, Vijendar.Mukunda

The FE-BE trigger sequence should be dynamic, similarly how soc-pcm.c
dpcm_fe_dai_do_trigger() does it.

Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
Reviewed-by: Liam Girdwood <liam.r.girdwood@intel.com>
---
 sound/soc/soc-compress.c | 71 +++++++++++++++++++++++++++++++++++-----
 1 file changed, 62 insertions(+), 9 deletions(-)

diff --git a/sound/soc/soc-compress.c b/sound/soc/soc-compress.c
index e5a70c9b3d31..f0529f6eeefe 100644
--- a/sound/soc/soc-compress.c
+++ b/sound/soc/soc-compress.c
@@ -380,31 +380,84 @@ static int soc_compr_trigger(struct snd_compr_stream *cstream, int cmd)
 	return ret;
 }
 
-static int soc_compr_trigger_fe(struct snd_compr_stream *cstream, int cmd)
+static int soc_compr_trigger_fe_be(struct snd_compr_stream *cstream, int cmd,
+				   bool fe_first)
 {
 	struct snd_soc_pcm_runtime *fe = cstream->private_data;
 	struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(fe, 0);
+	int ret;
+
+	if (fe_first) {
+		dev_dbg(fe->dev, "ASoC: pre trigger FE %s cmd %d\n",
+			fe->dai_link->name, cmd);
+
+		ret = snd_soc_dai_compr_trigger(cpu_dai, cstream, cmd);
+		if (ret < 0)
+			goto out;
+
+		ret = snd_soc_component_compr_trigger(cstream, cmd);
+		if (ret < 0)
+			goto out;
+
+		ret = dpcm_be_dai_trigger(fe, cstream->direction, cmd);
+	} else {
+		dev_dbg(fe->dev, "ASoC: post trigger FE %s cmd %d\n",
+			fe->dai_link->name, cmd);
+
+		ret = dpcm_be_dai_trigger(fe, cstream->direction, cmd);
+		if (ret < 0)
+			goto out;
+
+		ret = snd_soc_dai_compr_trigger(cpu_dai, cstream, cmd);
+		if (ret < 0)
+			goto out;
+
+		ret = snd_soc_component_compr_trigger(cstream, cmd);
+	}
+
+out:
+	return snd_soc_ret(fe->dev, ret, "trigger FE cmd: %d failed\n", cmd);
+}
+
+static int soc_compr_trigger_fe(struct snd_compr_stream *cstream, int cmd)
+{
+	struct snd_soc_pcm_runtime *fe = cstream->private_data;
 	int stream = cstream->direction; /* SND_COMPRESS_xxx is same as SNDRV_PCM_STREAM_xxx */
+	bool fe_first;
 	int ret;
 
 	if (cmd == SND_COMPR_TRIGGER_PARTIAL_DRAIN ||
 	    cmd == SND_COMPR_TRIGGER_DRAIN)
 		return snd_soc_component_compr_trigger(cstream, cmd);
 
+	if (fe->dai_link->trigger[stream] == SND_SOC_DPCM_TRIGGER_POST)
+		fe_first = false;
+	else
+		fe_first = true;
+
 	snd_soc_card_mutex_lock(fe->card);
 
-	ret = snd_soc_dai_compr_trigger(cpu_dai, cstream, cmd);
-	if (ret < 0)
-		goto out;
+	fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		ret = soc_compr_trigger_fe_be(cstream, cmd, fe_first);
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		ret = soc_compr_trigger_fe_be(cstream, cmd, !fe_first);
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	}
 
-	ret = snd_soc_component_compr_trigger(cstream, cmd);
 	if (ret < 0)
 		goto out;
 
-	fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE;
-
-	ret = dpcm_be_dai_trigger(fe, stream, cmd);
-
 	switch (cmd) {
 	case SNDRV_PCM_TRIGGER_START:
 	case SNDRV_PCM_TRIGGER_RESUME:
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 49+ messages in thread

* [PATCH 05/23] ASoC: soc-compress: Stop running dpcm on free
  2026-09-04  7:31 [PATCH 00/23] ALSA compress / ASoC compress / SOF: Compressed audio support with IPC4 Peter Ujfalusi
                   ` (3 preceding siblings ...)
  2026-09-04  7:31 ` [PATCH 04/23] ASoC: soc-compress: Implement trigger FE-BE sequencing as with normal PCMs Peter Ujfalusi
@ 2026-09-04  7:31 ` Peter Ujfalusi
  2026-09-04  7:31 ` [PATCH 06/23] ASoC: SOF: compress: Rename compress ops with ipc3 prefix Peter Ujfalusi
                   ` (17 subsequent siblings)
  22 siblings, 0 replies; 49+ messages in thread
From: Peter Ujfalusi @ 2026-09-04  7:31 UTC (permalink / raw)
  To: vkoul, perex, tiwai, lgirdwood, broonie, srinivas.kandagatla
  Cc: linux-sound, kai.vehmanen, yung-chuan.liao, pierre-louis.bossart,
	daniel.baluta, Vijendar.Mukunda

If the last trigger that the compr device received is a DRAIN then the
DPCM is left in running state (no stop trigger is sent).
Before we execute the free we need to send a STOP trigger to make sure that
both BE and FE is in expected state and prepared for closing.

Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
Reviewed-by: Liam Girdwood <liam.r.girdwood@intel.com>
---
 sound/soc/soc-compress.c | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/sound/soc/soc-compress.c b/sound/soc/soc-compress.c
index f0529f6eeefe..b562a634d107 100644
--- a/sound/soc/soc-compress.c
+++ b/sound/soc/soc-compress.c
@@ -311,6 +311,16 @@ static int soc_compr_free_fe(struct snd_compr_stream *cstream)
 	struct snd_soc_dpcm *dpcm;
 	int stream = cstream->direction; /* SND_COMPRESS_xxx is same as SNDRV_PCM_STREAM_xxx */
 
+	/*
+	 * The core will not send a STOP trigger on free if the device is in
+	 * DRAIN state, but we need to stop BE and FE before we can proceed to
+	 * free the stream.
+	 * Run the STOP trigger if the DPCM state is START (DRAIN is not
+	 * changing the DPCM state).
+	 */
+	if (fe->dpcm[stream].state == SND_SOC_DPCM_STATE_START)
+		cstream->ops->trigger(cstream, SNDRV_PCM_TRIGGER_STOP);
+
 	snd_soc_card_mutex_lock(fe->card);
 
 	snd_soc_dpcm_mutex_lock(fe);
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 49+ messages in thread

* [PATCH 06/23] ASoC: SOF: compress: Rename compress ops with ipc3 prefix
  2026-09-04  7:31 [PATCH 00/23] ALSA compress / ASoC compress / SOF: Compressed audio support with IPC4 Peter Ujfalusi
                   ` (4 preceding siblings ...)
  2026-09-04  7:31 ` [PATCH 05/23] ASoC: soc-compress: Stop running dpcm on free Peter Ujfalusi
@ 2026-09-04  7:31 ` Peter Ujfalusi
  2026-09-04  7:31 ` [PATCH 07/23] ASoC: SOF: ipc4-pcm: harden pipeline teardown races Peter Ujfalusi
                   ` (16 subsequent siblings)
  22 siblings, 0 replies; 49+ messages in thread
From: Peter Ujfalusi @ 2026-09-04  7:31 UTC (permalink / raw)
  To: vkoul, perex, tiwai, lgirdwood, broonie, srinivas.kandagatla
  Cc: linux-sound, kai.vehmanen, yung-chuan.liao, pierre-louis.bossart,
	daniel.baluta, Vijendar.Mukunda

From: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>

In preparation for adding support for compressed offload support for
IPC4, rename the current compress implementation with the IPC3 prefix.
Introduce a new field in struct sof_ipc_pcm_ops to save the
IPC-specific compressed ops pointer. This should be set when the
component driver ops are assigned during SOF device probe. Expose a couple
of common functions that will be used by both IPC-specific implementations
and rename the compress.c file to ipc3-compress.c

Signed-off-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
Reviewed-by: Liam Girdwood <liam.r.girdwood@intel.com>
---
 sound/soc/sof/Makefile                        |   2 +-
 sound/soc/sof/core.c                          |  10 +-
 sound/soc/sof/{compress.c => ipc3-compress.c} | 145 ++++--------------
 sound/soc/sof/ipc3-pcm.c                      |   3 +
 sound/soc/sof/ipc3-priv.h                     |   3 +
 sound/soc/sof/pcm.c                           |   5 +-
 sound/soc/sof/sof-audio.c                     |  81 ++++++++++
 sound/soc/sof/sof-audio.h                     |   5 +
 8 files changed, 137 insertions(+), 117 deletions(-)
 rename sound/soc/sof/{compress.c => ipc3-compress.c} (66%)

diff --git a/sound/soc/sof/Makefile b/sound/soc/sof/Makefile
index b0b22e6ebc03..3d52f8c4d173 100644
--- a/sound/soc/sof/Makefile
+++ b/sound/soc/sof/Makefile
@@ -19,7 +19,7 @@ ifneq ($(CONFIG_SND_SOC_SOF_CLIENT),)
 snd-sof-y += sof-client.o
 endif
 
-snd-sof-$(CONFIG_SND_SOC_SOF_COMPRESS) += compress.o
+snd-sof-$(CONFIG_SND_SOC_SOF_COMPRESS) += ipc3-compress.o
 
 snd-sof-pci-y := sof-pci-dev.o
 snd-sof-acpi-y := sof-acpi-dev.o
diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c
index 2d394389c945..41747ef0e432 100644
--- a/sound/soc/sof/core.c
+++ b/sound/soc/sof/core.c
@@ -463,11 +463,12 @@ static int sof_probe_continue(struct snd_sof_dev *sdev)
 
 	sof_set_fw_state(sdev, SOF_FW_BOOT_PREPARE);
 
-	/* set up platform component driver */
-	snd_sof_new_platform_drv(sdev);
-
 	if (sdev->dspless_mode_selected) {
 		sof_set_fw_state(sdev, SOF_DSPLESS_MODE);
+
+		/* set up platform component driver */
+		snd_sof_new_platform_drv(sdev);
+
 		goto skip_dsp_init;
 	}
 
@@ -492,6 +493,9 @@ static int sof_probe_continue(struct snd_sof_dev *sdev)
 		goto ipc_err;
 	}
 
+	/* set up platform component driver after initializing the IPC ops */
+	snd_sof_new_platform_drv(sdev);
+
 	/* load the firmware */
 	ret = snd_sof_load_firmware(sdev);
 	if (ret < 0) {
diff --git a/sound/soc/sof/compress.c b/sound/soc/sof/ipc3-compress.c
similarity index 66%
rename from sound/soc/sof/compress.c
rename to sound/soc/sof/ipc3-compress.c
index 93f2376585db..115d454bcaf5 100644
--- a/sound/soc/sof/compress.c
+++ b/sound/soc/sof/ipc3-compress.c
@@ -12,88 +12,8 @@
 #include "sof-utils.h"
 #include "ops.h"
 
-static void sof_set_transferred_bytes(struct sof_compr_stream *sstream,
-				      u64 host_pos, u64 buffer_size)
-{
-	u64 prev_pos;
-	unsigned int copied;
-
-	div64_u64_rem(sstream->copied_total, buffer_size, &prev_pos);
-
-	if (host_pos < prev_pos)
-		copied = (buffer_size - prev_pos) + host_pos;
-	else
-		copied = host_pos - prev_pos;
-
-	sstream->copied_total += copied;
-}
-
-static void snd_sof_compr_fragment_elapsed_work(struct work_struct *work)
-{
-	struct snd_sof_pcm_stream *sps =
-		container_of(work, struct snd_sof_pcm_stream,
-			     period_elapsed_work);
-
-	snd_compr_fragment_elapsed(sps->cstream);
-}
-
-void snd_sof_compr_init_elapsed_work(struct work_struct *work)
-{
-	INIT_WORK(work, snd_sof_compr_fragment_elapsed_work);
-}
-
-/*
- * sof compr fragment elapse, this could be called in irq thread context
- */
-void snd_sof_compr_fragment_elapsed(struct snd_compr_stream *cstream)
-{
-	struct snd_soc_pcm_runtime *rtd;
-	struct snd_compr_runtime *crtd;
-	struct snd_soc_component *component;
-	struct sof_compr_stream *sstream;
-	struct snd_sof_pcm *spcm;
-
-	if (!cstream)
-		return;
-
-	rtd = cstream->private_data;
-	crtd = cstream->runtime;
-	sstream = crtd->private_data;
-	component = snd_soc_rtdcom_lookup(rtd, SOF_AUDIO_PCM_DRV_NAME);
-
-	spcm = snd_sof_find_spcm_dai(component, rtd);
-	if (!spcm) {
-		dev_err(component->dev,
-			"fragment elapsed called for unknown stream!\n");
-		return;
-	}
-
-	sof_set_transferred_bytes(sstream, spcm->stream[cstream->direction].posn.host_posn,
-				  crtd->buffer_size);
-
-	/* use the same workqueue-based solution as for PCM, cf. snd_sof_pcm_elapsed */
-	schedule_work(&spcm->stream[cstream->direction].period_elapsed_work);
-}
-
-static int create_page_table(struct snd_soc_component *component,
-			     struct snd_compr_stream *cstream,
-			     unsigned char *dma_area, size_t size)
-{
-	struct snd_dma_buffer *dmab = cstream->runtime->dma_buffer_p;
-	struct snd_soc_pcm_runtime *rtd = cstream->private_data;
-	int dir = cstream->direction;
-	struct snd_sof_pcm *spcm;
-
-	spcm = snd_sof_find_spcm_dai(component, rtd);
-	if (!spcm)
-		return -EINVAL;
-
-	return snd_sof_create_page_table(component->dev, dmab,
-					 spcm->stream[dir].page_table.area, size);
-}
-
-static int sof_compr_open(struct snd_soc_component *component,
-			  struct snd_compr_stream *cstream)
+static int sof_ipc3_compr_open(struct snd_soc_component *component,
+			       struct snd_compr_stream *cstream)
 {
 	struct snd_soc_pcm_runtime *rtd = cstream->private_data;
 	struct snd_compr_runtime *crtd = cstream->runtime;
@@ -128,8 +48,8 @@ static int sof_compr_open(struct snd_soc_component *component,
 	return 0;
 }
 
-static int sof_compr_free(struct snd_soc_component *component,
-			  struct snd_compr_stream *cstream)
+static int sof_ipc3_compr_free(struct snd_soc_component *component,
+			       struct snd_compr_stream *cstream)
 {
 	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
 	struct sof_compr_stream *sstream = cstream->runtime->private_data;
@@ -159,8 +79,9 @@ static int sof_compr_free(struct snd_soc_component *component,
 	return ret;
 }
 
-static int sof_compr_set_params(struct snd_soc_component *component,
-				struct snd_compr_stream *cstream, struct snd_compr_params *params)
+static int sof_ipc3_compr_set_params(struct snd_soc_component *component,
+				     struct snd_compr_stream *cstream,
+				     struct snd_compr_params *params)
 {
 	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
 	struct snd_soc_pcm_runtime *rtd = cstream->private_data;
@@ -213,7 +134,7 @@ static int sof_compr_set_params(struct snd_soc_component *component,
 	if (ret < 0)
 		goto out;
 
-	ret = create_page_table(component, cstream, crtd->dma_area, crtd->dma_bytes);
+	ret = snd_sof_compr_create_page_table(component, cstream, crtd->dma_area, crtd->dma_bytes);
 	if (ret < 0)
 		goto out;
 
@@ -265,8 +186,9 @@ static int sof_compr_set_params(struct snd_soc_component *component,
 	return ret;
 }
 
-static int sof_compr_get_params(struct snd_soc_component *component,
-				struct snd_compr_stream *cstream, struct snd_codec *params)
+static int sof_ipc3_compr_get_params(struct snd_soc_component *component,
+				     struct snd_compr_stream *cstream,
+				     struct snd_codec *params)
 {
 	struct sof_compr_stream *sstream = cstream->runtime->private_data;
 
@@ -275,8 +197,8 @@ static int sof_compr_get_params(struct snd_soc_component *component,
 	return 0;
 }
 
-static int sof_compr_trigger(struct snd_soc_component *component,
-			     struct snd_compr_stream *cstream, int cmd)
+static int sof_ipc3_compr_trigger(struct snd_soc_component *component,
+				  struct snd_compr_stream *cstream, int cmd)
 {
 	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
 	struct snd_soc_pcm_runtime *rtd = cstream->private_data;
@@ -312,8 +234,8 @@ static int sof_compr_trigger(struct snd_soc_component *component,
 	return sof_ipc_tx_message_no_reply(sdev->ipc, &stream, sizeof(stream));
 }
 
-static int sof_compr_copy_playback(struct snd_compr_runtime *rtd,
-				   char __user *buf, size_t count)
+static int sof_ipc3_compr_copy_playback(struct snd_compr_runtime *rtd,
+					char __user *buf, size_t count)
 {
 	void *ptr;
 	unsigned int offset, n;
@@ -333,8 +255,8 @@ static int sof_compr_copy_playback(struct snd_compr_runtime *rtd,
 	return count - ret;
 }
 
-static int sof_compr_copy_capture(struct snd_compr_runtime *rtd,
-				  char __user *buf, size_t count)
+static int sof_ipc3_compr_copy_capture(struct snd_compr_runtime *rtd,
+				       char __user *buf, size_t count)
 {
 	void *ptr;
 	unsigned int offset, n;
@@ -354,9 +276,9 @@ static int sof_compr_copy_capture(struct snd_compr_runtime *rtd,
 	return count - ret;
 }
 
-static int sof_compr_copy(struct snd_soc_component *component,
-			  struct snd_compr_stream *cstream,
-			  char __user *buf, size_t count)
+static int sof_ipc3_compr_copy(struct snd_soc_component *component,
+			       struct snd_compr_stream *cstream,
+			       char __user *buf, size_t count)
 {
 	struct snd_compr_runtime *rtd = cstream->runtime;
 
@@ -364,14 +286,14 @@ static int sof_compr_copy(struct snd_soc_component *component,
 		count = rtd->buffer_size;
 
 	if (cstream->direction == SND_COMPRESS_PLAYBACK)
-		return sof_compr_copy_playback(rtd, buf, count);
+		return sof_ipc3_compr_copy_playback(rtd, buf, count);
 	else
-		return sof_compr_copy_capture(rtd, buf, count);
+		return sof_ipc3_compr_copy_capture(rtd, buf, count);
 }
 
-static int sof_compr_pointer(struct snd_soc_component *component,
-			     struct snd_compr_stream *cstream,
-			     struct snd_compr_tstamp64 *tstamp)
+static int sof_ipc3_compr_pointer(struct snd_soc_component *component,
+				  struct snd_compr_stream *cstream,
+				  struct snd_compr_tstamp64 *tstamp)
 {
 	struct snd_sof_pcm *spcm;
 	struct snd_soc_pcm_runtime *rtd = cstream->private_data;
@@ -392,13 +314,12 @@ static int sof_compr_pointer(struct snd_soc_component *component,
 	return 0;
 }
 
-struct snd_compress_ops sof_compressed_ops = {
-	.open		= sof_compr_open,
-	.free		= sof_compr_free,
-	.set_params	= sof_compr_set_params,
-	.get_params	= sof_compr_get_params,
-	.trigger	= sof_compr_trigger,
-	.pointer	= sof_compr_pointer,
-	.copy		= sof_compr_copy,
+const struct snd_compress_ops sof_ipc3_compressed_ops = {
+	.open		= sof_ipc3_compr_open,
+	.free		= sof_ipc3_compr_free,
+	.set_params	= sof_ipc3_compr_set_params,
+	.get_params	= sof_ipc3_compr_get_params,
+	.trigger	= sof_ipc3_compr_trigger,
+	.pointer	= sof_ipc3_compr_pointer,
+	.copy		= sof_ipc3_compr_copy,
 };
-EXPORT_SYMBOL(sof_compressed_ops);
diff --git a/sound/soc/sof/ipc3-pcm.c b/sound/soc/sof/ipc3-pcm.c
index 143bf0fe8dd9..1d0c80466f6a 100644
--- a/sound/soc/sof/ipc3-pcm.c
+++ b/sound/soc/sof/ipc3-pcm.c
@@ -442,4 +442,7 @@ const struct sof_ipc_pcm_ops ipc3_pcm_ops = {
 	.dai_link_fixup = sof_ipc3_pcm_dai_link_fixup,
 	.reset_hw_params_during_stop = true,
 	.d0i3_supported_in_s0ix = true,
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_COMPRESS)
+	.compress_ops =	&sof_ipc3_compressed_ops,
+#endif
 };
diff --git a/sound/soc/sof/ipc3-priv.h b/sound/soc/sof/ipc3-priv.h
index 866c5f67b91a..f95957453ab8 100644
--- a/sound/soc/sof/ipc3-priv.h
+++ b/sound/soc/sof/ipc3-priv.h
@@ -17,6 +17,9 @@ extern const struct sof_ipc_tplg_ops ipc3_tplg_ops;
 extern const struct sof_ipc_tplg_control_ops tplg_ipc3_control_ops;
 extern const struct sof_ipc_fw_loader_ops ipc3_loader_ops;
 extern const struct sof_ipc_fw_tracing_ops ipc3_dtrace_ops;
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_COMPRESS)
+extern const struct snd_compress_ops sof_ipc3_compressed_ops;
+#endif
 
 /* helpers for fw_ready and ext_manifest parsing */
 int sof_ipc3_get_ext_windows(struct snd_sof_dev *sdev,
diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c
index f748d072109a..941baa48b275 100644
--- a/sound/soc/sof/pcm.c
+++ b/sound/soc/sof/pcm.c
@@ -847,7 +847,10 @@ void snd_sof_new_platform_drv(struct snd_sof_dev *sdev)
 	pd->delay = sof_pcm_delay;
 
 #if IS_ENABLED(CONFIG_SND_SOC_SOF_COMPRESS)
-	pd->compress_ops = &sof_compressed_ops;
+	const struct sof_ipc_pcm_ops *pcm_ops = sof_ipc_get_ops(sdev, pcm);
+
+	if (pcm_ops)
+		pd->compress_ops = pcm_ops->compress_ops;
 #endif
 
 	pd->pcm_new = sof_pcm_new;
diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c
index 72ff03b0b80d..d244e90a734b 100644
--- a/sound/soc/sof/sof-audio.c
+++ b/sound/soc/sof/sof-audio.c
@@ -11,6 +11,7 @@
 #include <linux/bitfield.h>
 #include <trace/events/sof.h>
 #include "sof-audio.h"
+#include "sof-utils.h"
 #include "ops.h"
 
 /*
@@ -1050,3 +1051,83 @@ int sof_dai_get_tdm_slots(struct snd_soc_pcm_runtime *rtd)
 	return sof_dai_get_param(rtd, SOF_DAI_PARAM_INTEL_SSP_TDM_SLOTS);
 }
 EXPORT_SYMBOL(sof_dai_get_tdm_slots);
+
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_COMPRESS)
+static void sof_set_transferred_bytes(struct sof_compr_stream *sstream,
+				      u64 host_pos, u64 buffer_size)
+{
+	u64 prev_pos;
+	unsigned int copied;
+
+	div64_u64_rem(sstream->copied_total, buffer_size, &prev_pos);
+
+	if (host_pos < prev_pos)
+		copied = (buffer_size - prev_pos) + host_pos;
+	else
+		copied = host_pos - prev_pos;
+
+	sstream->copied_total += copied;
+}
+
+static void snd_sof_compr_fragment_elapsed_work(struct work_struct *work)
+{
+	struct snd_sof_pcm_stream *sps = container_of(work, struct snd_sof_pcm_stream,
+						      period_elapsed_work);
+
+	snd_compr_fragment_elapsed(sps->cstream);
+}
+
+void snd_sof_compr_init_elapsed_work(struct work_struct *work)
+{
+	INIT_WORK(work, snd_sof_compr_fragment_elapsed_work);
+}
+
+/*
+ * sof compr fragment elapse, this could be called in irq thread context
+ */
+void snd_sof_compr_fragment_elapsed(struct snd_compr_stream *cstream)
+{
+	struct snd_soc_pcm_runtime *rtd;
+	struct snd_compr_runtime *crtd;
+	struct snd_soc_component *component;
+	struct sof_compr_stream *sstream;
+	struct snd_sof_pcm *spcm;
+
+	if (!cstream)
+		return;
+
+	rtd = cstream->private_data;
+	crtd = cstream->runtime;
+	sstream = crtd->private_data;
+	component = snd_soc_rtdcom_lookup(rtd, SOF_AUDIO_PCM_DRV_NAME);
+
+	spcm = snd_sof_find_spcm_dai(component, rtd);
+	if (!spcm) {
+		dev_err(component->dev, "fragment elapsed called for unknown stream!\n");
+		return;
+	}
+
+	sof_set_transferred_bytes(sstream, spcm->stream[cstream->direction].posn.host_posn,
+				  crtd->buffer_size);
+
+	/* use the same workqueue-based solution as for PCM, cf. snd_sof_pcm_elapsed */
+	schedule_work(&spcm->stream[cstream->direction].period_elapsed_work);
+}
+
+int snd_sof_compr_create_page_table(struct snd_soc_component *component,
+				    struct snd_compr_stream *cstream,
+				    unsigned char *dma_area, size_t size)
+{
+	struct snd_dma_buffer *dmab = cstream->runtime->dma_buffer_p;
+	struct snd_soc_pcm_runtime *rtd = cstream->private_data;
+	int dir = cstream->direction;
+	struct snd_sof_pcm *spcm;
+
+	spcm = snd_sof_find_spcm_dai(component, rtd);
+	if (!spcm)
+		return -EINVAL;
+
+	return snd_sof_create_page_table(component->dev, dmab,
+					 spcm->stream[dir].page_table.area, size);
+}
+#endif
diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h
index ae95efc9be1c..bd5bbaf9ae44 100644
--- a/sound/soc/sof/sof-audio.h
+++ b/sound/soc/sof/sof-audio.h
@@ -119,6 +119,7 @@ struct snd_sof_dai_config_data {
  *				  therefore the host must do the same and should stop the DMA during
  *				  hw_free.
  * @d0i3_supported_in_s0ix: Allow DSP D0I3 during S0iX
+ * @compress_ops: Pointer to ops for compressed streams
  */
 struct sof_ipc_pcm_ops {
 	int (*hw_params)(struct snd_soc_component *component, struct snd_pcm_substream *substream,
@@ -139,6 +140,7 @@ struct sof_ipc_pcm_ops {
 	bool ipc_first_on_start;
 	bool platform_stop_during_hw_free;
 	bool d0i3_supported_in_s0ix;
+	const struct snd_compress_ops *compress_ops;
 };
 
 /**
@@ -660,6 +662,9 @@ void snd_sof_pcm_init_elapsed_work(struct work_struct *work);
 #if IS_ENABLED(CONFIG_SND_SOC_SOF_COMPRESS)
 void snd_sof_compr_fragment_elapsed(struct snd_compr_stream *cstream);
 void snd_sof_compr_init_elapsed_work(struct work_struct *work);
+int snd_sof_compr_create_page_table(struct snd_soc_component *component,
+				    struct snd_compr_stream *cstream,
+				    unsigned char *dma_area, size_t size);
 #else
 static inline void snd_sof_compr_fragment_elapsed(struct snd_compr_stream *cstream) { }
 static inline void snd_sof_compr_init_elapsed_work(struct work_struct *work) { }
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 49+ messages in thread

* [PATCH 07/23] ASoC: SOF: ipc4-pcm: harden pipeline teardown races
  2026-09-04  7:31 [PATCH 00/23] ALSA compress / ASoC compress / SOF: Compressed audio support with IPC4 Peter Ujfalusi
                   ` (5 preceding siblings ...)
  2026-09-04  7:31 ` [PATCH 06/23] ASoC: SOF: compress: Rename compress ops with ipc3 prefix Peter Ujfalusi
@ 2026-09-04  7:31 ` Peter Ujfalusi
  2026-09-04  7:31 ` [PATCH 08/23] ASoC: SOF: sof-audio: do not dereference swidget->spipe unconditionally on free Peter Ujfalusi
                   ` (15 subsequent siblings)
  22 siblings, 0 replies; 49+ messages in thread
From: Peter Ujfalusi @ 2026-09-04  7:31 UTC (permalink / raw)
  To: vkoul, perex, tiwai, lgirdwood, broonie, srinivas.kandagatla
  Cc: linux-sound, kai.vehmanen, yung-chuan.liao, pierre-louis.bossart,
	daniel.baluta, Vijendar.Mukunda

Serialize trigger/free with pipeline_state_mutex and validate
pipeline entries before use.

Also clear pipeline_list->count when freeing lists to avoid stale
entries during concurrent teardown.

Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
Reviewed-by: Liam Girdwood <liam.r.girdwood@intel.com>
---
 sound/soc/sof/ipc4-pcm.c | 18 ++++++++++++++++--
 1 file changed, 16 insertions(+), 2 deletions(-)

diff --git a/sound/soc/sof/ipc4-pcm.c b/sound/soc/sof/ipc4-pcm.c
index 5929ecf6642e..9d4be9ef4403 100644
--- a/sound/soc/sof/ipc4-pcm.c
+++ b/sound/soc/sof/ipc4-pcm.c
@@ -434,12 +434,16 @@ static int sof_ipc4_trigger_pipelines(struct snd_soc_component *component,
 	spcm_dbg(spcm, substream->stream, "cmd: %d, state: %d\n", cmd, state);
 
 	pipeline_list = &spcm->stream[substream->stream].pipeline_list;
+	guard(mutex)(&ipc4_data->pipeline_state_mutex);
 
 	/* nothing to trigger if the list is empty */
 	if (!pipeline_list->pipelines || !pipeline_list->count)
 		return 0;
 
 	spipe = pipeline_list->pipelines[0];
+	if (!spipe || !spipe->pipe_widget || !spipe->pipe_widget->private)
+		return 0;
+
 	pipe_widget = spipe->pipe_widget;
 	pipeline = pipe_widget->private;
 
@@ -487,8 +491,6 @@ static int sof_ipc4_trigger_pipelines(struct snd_soc_component *component,
 		return -ENOMEM;
 	}
 
-	guard(mutex)(&ipc4_data->pipeline_state_mutex);
-
 	/*
 	 * IPC4 requires pipelines to be triggered in order starting at the sink and
 	 * walking all the way to the source. So traverse the pipeline_list in the order
@@ -501,12 +503,16 @@ static int sof_ipc4_trigger_pipelines(struct snd_soc_component *component,
 	if (state == SOF_IPC4_PIPE_RUNNING || state == SOF_IPC4_PIPE_RESET)
 		for (i = pipeline_list->count - 1; i >= 0; i--) {
 			spipe = pipeline_list->pipelines[i];
+			if (!spipe || !spipe->pipe_widget || !spipe->pipe_widget->private)
+				continue;
 			sof_ipc4_add_pipeline_to_trigger_list(sdev, state, spipe, trigger_list,
 							      pipe_priority);
 		}
 	else
 		for (i = 0; i < pipeline_list->count; i++) {
 			spipe = pipeline_list->pipelines[i];
+			if (!spipe || !spipe->pipe_widget || !spipe->pipe_widget->private)
+				continue;
 			sof_ipc4_add_pipeline_to_trigger_list(sdev, state, spipe, trigger_list,
 							      pipe_priority);
 		}
@@ -546,6 +552,8 @@ static int sof_ipc4_trigger_pipelines(struct snd_soc_component *component,
 	/* update PAUSED state for all pipelines just triggered */
 	for (i = 0; i < pipeline_list->count ; i++) {
 		spipe = pipeline_list->pipelines[i];
+		if (!spipe || !spipe->pipe_widget || !spipe->pipe_widget->private)
+			continue;
 		sof_ipc4_update_pipeline_state(sdev, SOF_IPC4_PIPE_PAUSED, cmd, spipe,
 					       trigger_list);
 	}
@@ -589,6 +597,8 @@ static int sof_ipc4_trigger_pipelines(struct snd_soc_component *component,
 	/* update RUNNING/RESET state for all pipelines that were just triggered */
 	for (i = 0; i < pipeline_list->count; i++) {
 		spipe = pipeline_list->pipelines[i];
+		if (!spipe || !spipe->pipe_widget || !spipe->pipe_widget->private)
+			continue;
 		sof_ipc4_update_pipeline_state(sdev, state, cmd, spipe, trigger_list);
 	}
 
@@ -903,13 +913,17 @@ static int sof_ipc4_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd,
 static void sof_ipc4_pcm_free(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm)
 {
 	struct snd_sof_pcm_stream_pipeline_list *pipeline_list;
+	struct sof_ipc4_fw_data *ipc4_data = sdev->private;
 	struct sof_ipc4_pcm_stream_priv *stream_priv;
 	int stream;
 
+	guard(mutex)(&ipc4_data->pipeline_state_mutex);
+
 	for_each_pcm_streams(stream) {
 		pipeline_list = &spcm->stream[stream].pipeline_list;
 		kfree(pipeline_list->pipelines);
 		pipeline_list->pipelines = NULL;
+		pipeline_list->count = 0;
 
 		stream_priv = spcm->stream[stream].private;
 		kfree(stream_priv->time_info);
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 49+ messages in thread

* [PATCH 08/23] ASoC: SOF: sof-audio: do not dereference swidget->spipe unconditionally on free
  2026-09-04  7:31 [PATCH 00/23] ALSA compress / ASoC compress / SOF: Compressed audio support with IPC4 Peter Ujfalusi
                   ` (6 preceding siblings ...)
  2026-09-04  7:31 ` [PATCH 07/23] ASoC: SOF: ipc4-pcm: harden pipeline teardown races Peter Ujfalusi
@ 2026-09-04  7:31 ` Peter Ujfalusi
  2026-09-04  7:31 ` [PATCH 09/23] ASoC: SOF: sof-audio: Expose a couple of functions Peter Ujfalusi
                   ` (14 subsequent siblings)
  22 siblings, 0 replies; 49+ messages in thread
From: Peter Ujfalusi @ 2026-09-04  7:31 UTC (permalink / raw)
  To: vkoul, perex, tiwai, lgirdwood, broonie, srinivas.kandagatla
  Cc: linux-sound, kai.vehmanen, yung-chuan.liao, pierre-louis.bossart,
	daniel.baluta, Vijendar.Mukunda

sof_widget_free_unlocked() dereferences swidget->spipe without checking
it for two things: swidget->spipe->complete for a scheduler widget, and
swidget->spipe->pipe_widget for the recursive free of the pipeline's
scheduler widget. Both can be reached with spipe or pipe_widget not
set, which oopses in the free path - where there is nothing left to
bail out to.

Check both before use and cache swidget->spipe in the local spipe
variable that is already there. A widget with no pipeline has nothing
to put or complete, and no scheduler widget to free, so skipping is the
correct behaviour.

No functional change for a widget that was successfully set up:
sof_widget_setup_unlocked() already rejects a dynamic pipeline widget
with no spipe or no spipe->pipe_widget with -EINVAL.

Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
Reviewed-by: Liam Girdwood <liam.r.girdwood@intel.com>
---
 sound/soc/sof/sof-audio.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c
index d244e90a734b..0b2f41f4a59b 100644
--- a/sound/soc/sof/sof-audio.c
+++ b/sound/soc/sof/sof-audio.c
@@ -103,7 +103,7 @@ static int sof_widget_free_unlocked(struct snd_sof_dev *sdev,
 	 * decrement ref count for cores associated with all modules in the pipeline and clear
 	 * the complete flag
 	 */
-	if (swidget->id == snd_soc_dapm_scheduler) {
+	if (swidget->id == snd_soc_dapm_scheduler && spipe) {
 		int i;
 
 		for_each_set_bit(i, &spipe->core_mask, sdev->num_cores) {
@@ -115,16 +115,16 @@ static int sof_widget_free_unlocked(struct snd_sof_dev *sdev,
 					err = ret;
 			}
 		}
-		swidget->spipe->complete = 0;
+		spipe->complete = 0;
 	}
 
 	/*
 	 * free the scheduler widget (same as pipe_widget) associated with the current swidget.
 	 * skip for static pipelines
 	 */
-	if (swidget->spipe && swidget->dynamic_pipeline_widget &&
+	if (spipe && spipe->pipe_widget && swidget->dynamic_pipeline_widget &&
 	    swidget->id != snd_soc_dapm_scheduler) {
-		ret = sof_widget_free_unlocked(sdev, swidget->spipe->pipe_widget);
+		ret = sof_widget_free_unlocked(sdev, spipe->pipe_widget);
 		if (ret < 0 && !err)
 			err = ret;
 	}
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 49+ messages in thread

* [PATCH 09/23] ASoC: SOF: sof-audio: Expose a couple of functions
  2026-09-04  7:31 [PATCH 00/23] ALSA compress / ASoC compress / SOF: Compressed audio support with IPC4 Peter Ujfalusi
                   ` (7 preceding siblings ...)
  2026-09-04  7:31 ` [PATCH 08/23] ASoC: SOF: sof-audio: do not dereference swidget->spipe unconditionally on free Peter Ujfalusi
@ 2026-09-04  7:31 ` Peter Ujfalusi
  2026-09-04  7:31 ` [PATCH 10/23] ASoC: SOF: pcm: Modify the signature of a couple of PCM IPC ops Peter Ujfalusi
                   ` (13 subsequent siblings)
  22 siblings, 0 replies; 49+ messages in thread
From: Peter Ujfalusi @ 2026-09-04  7:31 UTC (permalink / raw)
  To: vkoul, perex, tiwai, lgirdwood, broonie, srinivas.kandagatla
  Cc: linux-sound, kai.vehmanen, yung-chuan.liao, pierre-louis.bossart,
	daniel.baluta, Vijendar.Mukunda

From: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>

These are common functions that will also be needed for the IPC4
compressed support.

Signed-off-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
Reviewed-by: Liam Girdwood <liam.r.girdwood@intel.com>
---
 sound/soc/sof/pcm.c       | 6 +++---
 sound/soc/sof/sof-audio.h | 7 ++++++-
 2 files changed, 9 insertions(+), 4 deletions(-)

diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c
index 941baa48b275..8f59c200b8ec 100644
--- a/sound/soc/sof/pcm.c
+++ b/sound/soc/sof/pcm.c
@@ -66,7 +66,7 @@ void snd_sof_pcm_period_elapsed(struct snd_pcm_substream *substream)
 }
 EXPORT_SYMBOL(snd_sof_pcm_period_elapsed);
 
-static int
+int
 sof_pcm_setup_connected_widgets(struct snd_sof_dev *sdev, struct snd_soc_pcm_runtime *rtd,
 				struct snd_sof_pcm *spcm, struct snd_pcm_hw_params *params,
 				struct snd_sof_platform_stream_params *platform_params, int dir)
@@ -100,8 +100,8 @@ sof_pcm_setup_connected_widgets(struct snd_sof_dev *sdev, struct snd_soc_pcm_run
 	return 0;
 }
 
-static struct snd_sof_widget *snd_sof_find_swidget_by_comp_id(struct snd_sof_dev *sdev,
-							      int comp_id)
+struct snd_sof_widget *snd_sof_find_swidget_by_comp_id(struct snd_sof_dev *sdev,
+						       int comp_id)
 {
 	struct snd_sof_widget *swidget;
 
diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h
index bd5bbaf9ae44..1b3e66037584 100644
--- a/sound/soc/sof/sof-audio.h
+++ b/sound/soc/sof/sof-audio.h
@@ -636,7 +636,12 @@ struct snd_sof_pcm *snd_sof_find_spcm_comp(struct snd_soc_component *scomp,
 					   int *direction);
 void snd_sof_pcm_period_elapsed(struct snd_pcm_substream *substream);
 void snd_sof_pcm_init_elapsed_work(struct work_struct *work);
-
+int sof_pcm_setup_connected_widgets(struct snd_sof_dev *sdev, struct snd_soc_pcm_runtime *rtd,
+				    struct snd_sof_pcm *spcm, struct snd_pcm_hw_params *params,
+				    struct snd_sof_platform_stream_params *platform_params,
+				    int dir);
+struct snd_sof_widget *snd_sof_find_swidget_by_comp_id(struct snd_sof_dev *sdev,
+						       int comp_id);
 /*
  * snd_sof_pcm specific wrappers for dev_dbg() and dev_err() to provide
  * consistent and useful prints.
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 49+ messages in thread

* [PATCH 10/23] ASoC: SOF: pcm: Modify the signature of a couple of PCM IPC ops
  2026-09-04  7:31 [PATCH 00/23] ALSA compress / ASoC compress / SOF: Compressed audio support with IPC4 Peter Ujfalusi
                   ` (8 preceding siblings ...)
  2026-09-04  7:31 ` [PATCH 09/23] ASoC: SOF: sof-audio: Expose a couple of functions Peter Ujfalusi
@ 2026-09-04  7:31 ` Peter Ujfalusi
  2026-09-04  7:31 ` [PATCH 11/23] ASoC: SOF: intel: hda-stream: Clear the current position when releasing stream Peter Ujfalusi
                   ` (12 subsequent siblings)
  22 siblings, 0 replies; 49+ messages in thread
From: Peter Ujfalusi @ 2026-09-04  7:31 UTC (permalink / raw)
  To: vkoul, perex, tiwai, lgirdwood, broonie, srinivas.kandagatla
  Cc: linux-sound, kai.vehmanen, yung-chuan.liao, pierre-louis.bossart,
	daniel.baluta, Vijendar.Mukunda

From: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>

In order to reuse the pipeline triggering logic for compressed support
with IPC4, modify the signature of the trigger and hw_free PCM IPC ops
so that they can be reused.

Signed-off-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
Reviewed-by: Liam Girdwood <liam.r.girdwood@intel.com>
---
 sound/soc/sof/ipc3-pcm.c  | 26 +++++++---------------
 sound/soc/sof/ipc4-pcm.c  | 45 ++++++++++++++++++++-------------------
 sound/soc/sof/pcm.c       |  7 +++---
 sound/soc/sof/sof-audio.h |  7 +++---
 4 files changed, 39 insertions(+), 46 deletions(-)

diff --git a/sound/soc/sof/ipc3-pcm.c b/sound/soc/sof/ipc3-pcm.c
index 1d0c80466f6a..42f93e596598 100644
--- a/sound/soc/sof/ipc3-pcm.c
+++ b/sound/soc/sof/ipc3-pcm.c
@@ -14,23 +14,18 @@
 #include "sof-audio.h"
 
 static int sof_ipc3_pcm_hw_free(struct snd_soc_component *component,
-				struct snd_pcm_substream *substream)
+				struct snd_pcm_substream *substream,
+				struct snd_sof_pcm *spcm, int dir)
 {
 	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
-	struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
 	struct sof_ipc_stream stream;
-	struct snd_sof_pcm *spcm;
-
-	spcm = snd_sof_find_spcm_dai(component, rtd);
-	if (!spcm)
-		return -EINVAL;
 
-	if (!spcm->prepared[substream->stream])
+	if (!spcm->prepared[dir])
 		return 0;
 
 	stream.hdr.size = sizeof(stream);
 	stream.hdr.cmd = SOF_IPC_GLB_STREAM_MSG | SOF_IPC_STREAM_PCM_FREE;
-	stream.comp_id = spcm->stream[substream->stream].comp_id;
+	stream.comp_id = spcm->stream[dir].comp_id;
 
 	/* send IPC to the DSP */
 	return sof_ipc_tx_message_no_reply(sdev->ipc, &stream, sizeof(stream));
@@ -141,20 +136,15 @@ static int sof_ipc3_pcm_hw_params(struct snd_soc_component *component,
 }
 
 static int sof_ipc3_pcm_trigger(struct snd_soc_component *component,
-				struct snd_pcm_substream *substream, int cmd)
+				struct snd_pcm_substream *substream,
+				struct snd_sof_pcm *spcm, int cmd, int dir)
 {
-	struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
 	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
 	struct sof_ipc_stream stream;
-	struct snd_sof_pcm *spcm;
-
-	spcm = snd_sof_find_spcm_dai(component, rtd);
-	if (!spcm)
-		return -EINVAL;
 
 	stream.hdr.size = sizeof(stream);
 	stream.hdr.cmd = SOF_IPC_GLB_STREAM_MSG;
-	stream.comp_id = spcm->stream[substream->stream].comp_id;
+	stream.comp_id = spcm->stream[dir].comp_id;
 
 	switch (cmd) {
 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
@@ -172,7 +162,7 @@ static int sof_ipc3_pcm_trigger(struct snd_soc_component *component,
 		stream.hdr.cmd |= SOF_IPC_STREAM_TRIG_STOP;
 		break;
 	default:
-		spcm_err(spcm, substream->stream, "Unhandled trigger cmd %d\n", cmd);
+		spcm_err(spcm, dir, "Unhandled trigger cmd %d\n", cmd);
 		return -EINVAL;
 	}
 
diff --git a/sound/soc/sof/ipc4-pcm.c b/sound/soc/sof/ipc4-pcm.c
index 9d4be9ef4403..6e8522d03a2b 100644
--- a/sound/soc/sof/ipc4-pcm.c
+++ b/sound/soc/sof/ipc4-pcm.c
@@ -412,28 +412,23 @@ static int sof_ipc4_chain_dma_trigger(struct snd_sof_dev *sdev,
 }
 
 static int sof_ipc4_trigger_pipelines(struct snd_soc_component *component,
-				      struct snd_pcm_substream *substream, int state, int cmd)
+				      struct snd_pcm_substream *substream, int state, int cmd,
+				      struct snd_sof_pcm *spcm, int dir)
 {
 	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
-	struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
 	struct snd_sof_pcm_stream_pipeline_list *pipeline_list;
 	struct sof_ipc4_fw_data *ipc4_data = sdev->private;
 	struct ipc4_pipeline_set_state_data *trigger_list;
 	struct snd_sof_widget *pipe_widget;
 	struct sof_ipc4_pipeline *pipeline;
 	struct snd_sof_pipeline *spipe;
-	struct snd_sof_pcm *spcm;
 	u8 *pipe_priority;
 	int ret;
 	int i;
 
-	spcm = snd_sof_find_spcm_dai(component, rtd);
-	if (!spcm)
-		return -EINVAL;
+	spcm_dbg(spcm, dir, "cmd: %d, state: %d\n", cmd, state);
 
-	spcm_dbg(spcm, substream->stream, "cmd: %d, state: %d\n", cmd, state);
-
-	pipeline_list = &spcm->stream[substream->stream].pipeline_list;
+	pipeline_list = &spcm->stream[dir].pipeline_list;
 	guard(mutex)(&ipc4_data->pipeline_state_mutex);
 
 	/* nothing to trigger if the list is empty */
@@ -454,9 +449,9 @@ static int sof_ipc4_trigger_pipelines(struct snd_soc_component *component,
 	if (pipeline->use_chain_dma) {
 		struct sof_ipc4_timestamp_info *time_info;
 
-		time_info = sof_ipc4_sps_to_time_info(&spcm->stream[substream->stream]);
+		time_info = sof_ipc4_sps_to_time_info(&spcm->stream[dir]);
 
-		ret = sof_ipc4_chain_dma_trigger(sdev, spcm, substream->stream,
+		ret = sof_ipc4_chain_dma_trigger(sdev, spcm, dir,
 						 pipeline_list, state, cmd);
 		if (ret || !time_info)
 			return ret;
@@ -465,12 +460,16 @@ static int sof_ipc4_trigger_pipelines(struct snd_soc_component *component,
 			/*
 			 * Record the DAI position for delay reporting
 			 * To handle multiple pause/resume/xrun we need to add
-			 * the positions to simulate how the firmware behaves
+			 * the positions to simulate how the firmware behaves.
+			 * Chained DMA does not support compress streams. We should
+			 * never get here with compress.
 			 */
-			u64 pos = snd_sof_pcm_get_dai_frame_counter(sdev, component,
-								    substream);
+			if (substream) {
+				u64 pos = snd_sof_pcm_get_dai_frame_counter(sdev, component,
+									    substream);
 
-			time_info->stream_end_offset += pos;
+				time_info->stream_end_offset += pos;
+			}
 		} else if (state == SOF_IPC4_PIPE_RESET) {
 			/* Reset the end offset as the stream is stopped */
 			time_info->stream_end_offset = 0;
@@ -533,7 +532,7 @@ static int sof_ipc4_trigger_pipelines(struct snd_soc_component *component,
 	 */
 	ret = sof_ipc4_set_multi_pipeline_state(sdev, SOF_IPC4_PIPE_PAUSED, trigger_list);
 	if (ret < 0) {
-		spcm_err(spcm, substream->stream, "failed to pause all pipelines\n");
+		spcm_err(spcm, dir, "failed to pause all pipelines\n");
 		/*
 		 * workaround: if the firmware is crashed or the IPC timed out
 		 * while setting the pipeline state we must ignore the error
@@ -566,7 +565,7 @@ static int sof_ipc4_trigger_pipelines(struct snd_soc_component *component,
 		 * Invalidate the stream_start_offset to make sure that it is
 		 * going to be updated if the stream resumes
 		 */
-		time_info = sof_ipc4_sps_to_time_info(&spcm->stream[substream->stream]);
+		time_info = sof_ipc4_sps_to_time_info(&spcm->stream[dir]);
 		if (time_info)
 			time_info->stream_start_offset = SOF_IPC4_INVALID_STREAM_POSITION;
 
@@ -576,7 +575,7 @@ static int sof_ipc4_trigger_pipelines(struct snd_soc_component *component,
 	/* else set the RUNNING/RESET state in the DSP */
 	ret = sof_ipc4_set_multi_pipeline_state(sdev, state, trigger_list);
 	if (ret < 0) {
-		spcm_err(spcm, substream->stream,
+		spcm_err(spcm, dir,
 			 "failed to set final state %d for all pipelines\n",
 			 state);
 		/*
@@ -609,7 +608,8 @@ static int sof_ipc4_trigger_pipelines(struct snd_soc_component *component,
 }
 
 static int sof_ipc4_pcm_trigger(struct snd_soc_component *component,
-				struct snd_pcm_substream *substream, int cmd)
+				struct snd_pcm_substream *substream,
+				struct snd_sof_pcm *spcm, int cmd, int dir)
 {
 	int state;
 
@@ -631,14 +631,15 @@ static int sof_ipc4_pcm_trigger(struct snd_soc_component *component,
 	}
 
 	/* set the pipeline state */
-	return sof_ipc4_trigger_pipelines(component, substream, state, cmd);
+	return sof_ipc4_trigger_pipelines(component, substream, state, cmd, spcm, dir);
 }
 
 static int sof_ipc4_pcm_hw_free(struct snd_soc_component *component,
-				struct snd_pcm_substream *substream)
+				struct snd_pcm_substream *substream,
+				struct snd_sof_pcm *spcm, int dir)
 {
 	/* command is not relevant with RESET, so just pass 0 */
-	return sof_ipc4_trigger_pipelines(component, substream, SOF_IPC4_PIPE_RESET, 0);
+	return sof_ipc4_trigger_pipelines(component, substream, SOF_IPC4_PIPE_RESET, 0, spcm, dir);
 }
 
 static int ipc4_ssp_dai_config_pcm_params_match(struct snd_sof_dev *sdev,
diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c
index 8f59c200b8ec..42738f12fa33 100644
--- a/sound/soc/sof/pcm.c
+++ b/sound/soc/sof/pcm.c
@@ -152,7 +152,7 @@ static int sof_pcm_hw_params(struct snd_soc_component *component,
 	 * between. At least ALSA OSS emulation depends on this.
 	 */
 	if (spcm->prepared[substream->stream] && pcm_ops && pcm_ops->hw_free) {
-		ret = pcm_ops->hw_free(component, substream);
+		ret = pcm_ops->hw_free(component, substream, spcm, substream->stream);
 		if (ret < 0)
 			return ret;
 
@@ -223,7 +223,8 @@ static int sof_pcm_stream_free(struct snd_sof_dev *sdev,
 
 		/* free PCM in the DSP */
 		if (pcm_ops && pcm_ops->hw_free) {
-			ret = pcm_ops->hw_free(sdev->component, substream);
+			ret = pcm_ops->hw_free(sdev->component, substream, spcm,
+					       substream->stream);
 			if (ret < 0) {
 				spcm_err(spcm, substream->stream,
 					 "pcm_ops->hw_free failed %d\n", ret);
@@ -458,7 +459,7 @@ static int sof_pcm_trigger(struct snd_soc_component *component,
 		snd_sof_pcm_platform_trigger(sdev, substream, cmd);
 
 	if (pcm_ops && pcm_ops->trigger)
-		ret = pcm_ops->trigger(component, substream, cmd);
+		ret = pcm_ops->trigger(component, substream, spcm, cmd, substream->stream);
 
 	switch (cmd) {
 	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h
index 1b3e66037584..0610f2ab844d 100644
--- a/sound/soc/sof/sof-audio.h
+++ b/sound/soc/sof/sof-audio.h
@@ -125,9 +125,10 @@ struct sof_ipc_pcm_ops {
 	int (*hw_params)(struct snd_soc_component *component, struct snd_pcm_substream *substream,
 			 struct snd_pcm_hw_params *params,
 			 struct snd_sof_platform_stream_params *platform_params);
-	int (*hw_free)(struct snd_soc_component *component, struct snd_pcm_substream *substream);
-	int (*trigger)(struct snd_soc_component *component,  struct snd_pcm_substream *substream,
-		       int cmd);
+	int (*hw_free)(struct snd_soc_component *component, struct snd_pcm_substream *substream,
+		       struct snd_sof_pcm *spcm, int dir);
+	int (*trigger)(struct snd_soc_component *component, struct snd_pcm_substream *substream,
+		       struct snd_sof_pcm *spcm, int cmd, int dir);
 	int (*dai_link_fixup)(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_params *params);
 	int (*pcm_setup)(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm);
 	void (*pcm_free)(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm);
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 49+ messages in thread

* [PATCH 11/23] ASoC: SOF: intel: hda-stream: Clear the current position when releasing stream
  2026-09-04  7:31 [PATCH 00/23] ALSA compress / ASoC compress / SOF: Compressed audio support with IPC4 Peter Ujfalusi
                   ` (9 preceding siblings ...)
  2026-09-04  7:31 ` [PATCH 10/23] ASoC: SOF: pcm: Modify the signature of a couple of PCM IPC ops Peter Ujfalusi
@ 2026-09-04  7:31 ` Peter Ujfalusi
  2026-09-04  7:31 ` [PATCH 12/23] ASoC: SOF: ops: Add new platform-specific ops for compress Peter Ujfalusi
                   ` (11 subsequent siblings)
  22 siblings, 0 replies; 49+ messages in thread
From: Peter Ujfalusi @ 2026-09-04  7:31 UTC (permalink / raw)
  To: vkoul, perex, tiwai, lgirdwood, broonie, srinivas.kandagatla
  Cc: linux-sound, kai.vehmanen, yung-chuan.liao, pierre-louis.bossart,
	daniel.baluta, Vijendar.Mukunda

From: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>

After the host DMA ID is released, reset the curr_pos to 0 for a clean
start for subsequent stream starts. This is not needed for PCM streams
but for compressed streams.

Signed-off-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
Reviewed-by: Liam Girdwood <liam.r.girdwood@intel.com>
---
 sound/soc/sof/intel/hda-stream.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/sound/soc/sof/intel/hda-stream.c b/sound/soc/sof/intel/hda-stream.c
index c95230487a1b..5097a497188d 100644
--- a/sound/soc/sof/intel/hda-stream.c
+++ b/sound/soc/sof/intel/hda-stream.c
@@ -317,6 +317,7 @@ static int _hda_dsp_stream_put(struct snd_sof_dev *sdev, int direction, int stre
 		if (s->direction == direction && s->stream_tag == stream_tag) {
 			s->opened = false;
 			found = true;
+			s->curr_pos = 0;
 			if (pair)
 				link_stream = hext_stream;
 		} else if (!(hda_stream->flags & SOF_HDA_STREAM_DMI_L1_COMPATIBLE)) {
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 49+ messages in thread

* [PATCH 12/23] ASoC: SOF: ops: Add new platform-specific ops for compress
  2026-09-04  7:31 [PATCH 00/23] ALSA compress / ASoC compress / SOF: Compressed audio support with IPC4 Peter Ujfalusi
                   ` (10 preceding siblings ...)
  2026-09-04  7:31 ` [PATCH 11/23] ASoC: SOF: intel: hda-stream: Clear the current position when releasing stream Peter Ujfalusi
@ 2026-09-04  7:31 ` Peter Ujfalusi
  2026-09-04  7:31 ` [PATCH 13/23] ASoC: SOF: ipc4: Add definition of module data in init_ext object type Peter Ujfalusi
                   ` (10 subsequent siblings)
  22 siblings, 0 replies; 49+ messages in thread
From: Peter Ujfalusi @ 2026-09-04  7:31 UTC (permalink / raw)
  To: vkoul, perex, tiwai, lgirdwood, broonie, srinivas.kandagatla
  Cc: linux-sound, kai.vehmanen, yung-chuan.liao, pierre-louis.bossart,
	daniel.baluta, Vijendar.Mukunda

From: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>

Add new ops in the struct snd_sof_ops for platform-specific ops for
compresssed streams. Also, define and set them for the HDA platforms.

Signed-off-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
Co-developed-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
Reviewed-by: Liam Girdwood <liam.r.girdwood@intel.com>
---
 sound/soc/sof/intel/hda-common-ops.c |   8 ++
 sound/soc/sof/intel/hda-pcm.c        | 142 +++++++++++++++++++++++++++
 sound/soc/sof/intel/hda-stream.c     |  69 +++++++++----
 sound/soc/sof/intel/hda.h            |  15 +++
 sound/soc/sof/ops.h                  |  74 ++++++++++++++
 sound/soc/sof/sof-priv.h             |  12 +++
 6 files changed, 301 insertions(+), 19 deletions(-)

diff --git a/sound/soc/sof/intel/hda-common-ops.c b/sound/soc/sof/intel/hda-common-ops.c
index 746b426b1329..aa91f2b6fd65 100644
--- a/sound/soc/sof/intel/hda-common-ops.c
+++ b/sound/soc/sof/intel/hda-common-ops.c
@@ -57,6 +57,14 @@ const struct snd_sof_dsp_ops sof_hda_common_ops = {
 	.pcm_pointer	= hda_dsp_pcm_pointer,
 	.pcm_ack	= hda_dsp_pcm_ack,
 
+	.compr_open = hda_dsp_compr_open,
+	.compr_hw_params = hda_dsp_compr_hw_params,
+	.compr_hw_free = hda_dsp_stream_compr_hw_free,
+	.compr_close = hda_dsp_compr_close,
+	.compr_trigger = hda_dsp_compr_trigger,
+	.compr_pointer = hda_dsp_compr_pointer,
+	.compr_get_dai_frame_counter = hda_dsp_compr_get_stream_llp,
+
 	.get_dai_frame_counter = hda_dsp_get_stream_llp,
 	.get_host_byte_counter = hda_dsp_get_stream_ldp,
 
diff --git a/sound/soc/sof/intel/hda-pcm.c b/sound/soc/sof/intel/hda-pcm.c
index 16a364072821..83efc9bdf418 100644
--- a/sound/soc/sof/intel/hda-pcm.c
+++ b/sound/soc/sof/intel/hda-pcm.c
@@ -151,6 +151,71 @@ int hda_dsp_pcm_hw_params(struct snd_sof_dev *sdev,
 }
 EXPORT_SYMBOL_NS(hda_dsp_pcm_hw_params, "SND_SOC_SOF_INTEL_HDA_COMMON");
 
+int hda_dsp_compr_hw_params(struct snd_sof_dev *sdev,
+			    struct snd_compr_stream *cstream,
+			    struct snd_compr_params *params,
+			    struct snd_sof_platform_stream_params *platform_params)
+{
+	struct hdac_stream *hstream = cstream->runtime->private_data;
+	struct hdac_ext_stream *hext_stream = stream_to_hdac_ext_stream(hstream);
+	struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
+	struct snd_dma_buffer *dmab;
+	u32 bits, rate;
+	int bps;
+	int ret;
+
+	hstream->cstream = cstream;
+	dmab = cstream->runtime->dma_buffer_p;
+
+	/* Use correct format based on the used codec */
+	switch (params->codec.id) {
+	case SND_AUDIOCODEC_PCM:
+		bps = snd_pcm_format_physical_width((snd_pcm_format_t)params->codec.format);
+		break;
+	case SND_AUDIOCODEC_VORBIS:
+		bps = snd_pcm_format_physical_width(SNDRV_PCM_FORMAT_S16_LE);
+		break;
+	case SND_AUDIOCODEC_FLAC:
+	{
+		struct snd_dec_flac *dec_flac = &params->codec.options.flac_d;
+
+		if (dec_flac->sample_size == 16)
+			bps = snd_pcm_format_physical_width(SNDRV_PCM_FORMAT_S16_LE);
+		else
+			bps = snd_pcm_format_physical_width(SNDRV_PCM_FORMAT_S32_LE);
+		break;
+	}
+	default:
+		bps = snd_pcm_format_physical_width(SNDRV_PCM_FORMAT_S32_LE);
+	}
+
+	if (bps < 0)
+		return bps;
+	bits = hda_dsp_get_bits(sdev, bps);
+	rate = hda_dsp_get_mult_div(sdev, params->codec.sample_rate);
+
+	hstream->format_val = rate | bits | (params->codec.ch_out - 1);
+	hstream->bufsize = cstream->runtime->buffer_size;
+	hstream->period_bytes = cstream->runtime->fragment_size;
+	hstream->no_period_wakeup  = false;
+
+	/* params is not used so pass NULL */
+	dmab = cstream->runtime->dma_buffer_p;
+	ret = hda_dsp_stream_hw_params(sdev, hext_stream, dmab, NULL);
+	if (ret < 0) {
+		dev_err(sdev->dev, "%s: hdac prepare failed: %d\n", __func__, ret);
+		return ret;
+	}
+
+	if (hda)
+		platform_params->no_ipc_position = hda->no_ipc_position;
+
+	platform_params->stream_tag = hstream->stream_tag;
+
+	return 0;
+}
+EXPORT_SYMBOL_NS(hda_dsp_compr_hw_params, "SND_SOC_SOF_INTEL_HDA_COMMON");
+
 /* update SPIB register with appl position */
 int hda_dsp_pcm_ack(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream)
 {
@@ -184,6 +249,16 @@ int hda_dsp_pcm_trigger(struct snd_sof_dev *sdev,
 }
 EXPORT_SYMBOL_NS(hda_dsp_pcm_trigger, "SND_SOC_SOF_INTEL_HDA_COMMON");
 
+int hda_dsp_compr_trigger(struct snd_sof_dev *sdev,
+			  struct snd_compr_stream *cstream, int cmd)
+{
+	struct hdac_stream *hstream = cstream->runtime->private_data;
+	struct hdac_ext_stream *hext_stream = stream_to_hdac_ext_stream(hstream);
+
+	return hda_dsp_stream_trigger(sdev, hext_stream, cmd);
+}
+EXPORT_SYMBOL_NS(hda_dsp_compr_trigger, "SND_SOC_SOF_INTEL_HDA_COMMON");
+
 snd_pcm_uframes_t hda_dsp_pcm_pointer(struct snd_sof_dev *sdev,
 				      struct snd_pcm_substream *substream)
 {
@@ -216,6 +291,20 @@ snd_pcm_uframes_t hda_dsp_pcm_pointer(struct snd_sof_dev *sdev,
 }
 EXPORT_SYMBOL_NS(hda_dsp_pcm_pointer, "SND_SOC_SOF_INTEL_HDA_COMMON");
 
+int hda_dsp_compr_pointer(struct snd_sof_dev *sdev, struct snd_compr_stream *cstream,
+			  struct snd_compr_tstamp64 *tstamp)
+{
+	struct hdac_stream *hstream = cstream->runtime->private_data;
+
+	/* hstream->curr_pos is updated when we receive the ioc */
+	tstamp->copied_total = hstream->curr_pos;
+
+	tstamp->byte_offset = hda_dsp_stream_get_position(hstream, cstream->direction, true);
+
+	return 0;
+}
+EXPORT_SYMBOL_NS(hda_dsp_compr_pointer, "SND_SOC_SOF_INTEL_HDA_COMMON");
+
 int hda_dsp_pcm_open(struct snd_sof_dev *sdev,
 		     struct snd_pcm_substream *substream)
 {
@@ -342,6 +431,41 @@ int hda_dsp_pcm_open(struct snd_sof_dev *sdev,
 }
 EXPORT_SYMBOL_NS(hda_dsp_pcm_open, "SND_SOC_SOF_INTEL_HDA_COMMON");
 
+int hda_dsp_compr_open(struct snd_sof_dev *sdev, struct snd_compr_stream *cstream)
+{
+	struct snd_soc_pcm_runtime *rtd = cstream->private_data;
+	struct snd_soc_component *scomp = sdev->component;
+	struct hdac_ext_stream *dsp_stream;
+	struct snd_sof_pcm *spcm;
+	int direction = cstream->direction;
+
+	spcm = snd_sof_find_spcm_dai(scomp, rtd);
+	if (!spcm) {
+		dev_err(sdev->dev, "%s: can't find PCM with DAI ID %d\n",
+			__func__, rtd->dai_link->id);
+		return -EINVAL;
+	}
+
+	dsp_stream = hda_dsp_stream_get(sdev, direction, 0);
+	if (!dsp_stream) {
+		dev_err(sdev->dev, "%s: no stream available\n", __func__);
+		return -ENODEV;
+	}
+
+	/* binding compr stream to hda stream */
+	cstream->runtime->private_data = &dsp_stream->hstream;
+
+	/*
+	 * Reset the llp cache values (they are used for LLP compensation in
+	 * case the counter is not reset)
+	 */
+	dsp_stream->pplcllpl = 0;
+	dsp_stream->pplcllpu = 0;
+
+	return 0;
+}
+EXPORT_SYMBOL_NS(hda_dsp_compr_open, "SND_SOC_SOF_INTEL_HDA_COMMON");
+
 int hda_dsp_pcm_close(struct snd_sof_dev *sdev,
 		      struct snd_pcm_substream *substream)
 {
@@ -361,3 +485,21 @@ int hda_dsp_pcm_close(struct snd_sof_dev *sdev,
 	return 0;
 }
 EXPORT_SYMBOL_NS(hda_dsp_pcm_close, "SND_SOC_SOF_INTEL_HDA_COMMON");
+
+int hda_dsp_compr_close(struct snd_sof_dev *sdev, struct snd_compr_stream *cstream)
+{
+	struct hdac_stream *hstream = cstream->runtime->private_data;
+	int direction = cstream->direction;
+	int ret;
+
+	ret = hda_dsp_stream_put(sdev, direction, hstream->stream_tag);
+	if (ret)
+		return -ENODEV;
+
+	/* unbinding compress stream to hda stream */
+	hstream->cstream = NULL;
+	cstream->runtime->private_data = NULL;
+
+	return 0;
+}
+EXPORT_SYMBOL_NS(hda_dsp_compr_close, "SND_SOC_SOF_INTEL_HDA_COMMON");
diff --git a/sound/soc/sof/intel/hda-stream.c b/sound/soc/sof/intel/hda-stream.c
index 5097a497188d..ac2a62c4e41f 100644
--- a/sound/soc/sof/intel/hda-stream.c
+++ b/sound/soc/sof/intel/hda-stream.c
@@ -755,13 +755,12 @@ int hda_dsp_stream_hw_params(struct snd_sof_dev *sdev,
 	return ret;
 }
 
-int hda_dsp_stream_hw_free(struct snd_sof_dev *sdev,
-			   struct snd_pcm_substream *substream)
+static int _hda_dsp_stream_hw_free(struct snd_sof_dev *sdev,
+				   struct hdac_stream *hstream)
 {
-	struct hdac_stream *hstream = substream->runtime->private_data;
 	struct hdac_ext_stream *hext_stream = container_of(hstream,
-							 struct hdac_ext_stream,
-							 hstream);
+							   struct hdac_ext_stream,
+							   hstream);
 	int ret;
 
 	ret = hda_dsp_stream_reset(sdev, hstream);
@@ -786,8 +785,21 @@ int hda_dsp_stream_hw_free(struct snd_sof_dev *sdev,
 
 	return 0;
 }
+
+int hda_dsp_stream_hw_free(struct snd_sof_dev *sdev,
+			   struct snd_pcm_substream *substream)
+{
+	return _hda_dsp_stream_hw_free(sdev, substream->runtime->private_data);
+}
 EXPORT_SYMBOL_NS(hda_dsp_stream_hw_free, "SND_SOC_SOF_INTEL_HDA_COMMON");
 
+int hda_dsp_stream_compr_hw_free(struct snd_sof_dev *sdev,
+				 struct snd_compr_stream *cstream)
+{
+	return _hda_dsp_stream_hw_free(sdev, cstream->runtime->private_data);
+}
+EXPORT_SYMBOL_NS(hda_dsp_stream_compr_hw_free, "SND_SOC_SOF_INTEL_HDA_COMMON");
+
 bool hda_dsp_check_stream_irq(struct snd_sof_dev *sdev)
 {
 	struct hdac_bus *bus = sof_to_bus(sdev);
@@ -1162,19 +1174,9 @@ EXPORT_SYMBOL_NS(hda_dsp_stream_get_position, "SND_SOC_SOF_INTEL_HDA_COMMON");
 
 #define merge_u64(u32_u, u32_l) (((u64)(u32_u) << 32) | (u32_l))
 
-/**
- * hda_dsp_get_stream_llp - Retrieve the LLP (Linear Link Position) of the stream
- * @sdev: SOF device
- * @component: ASoC component
- * @substream: PCM substream
- *
- * Returns the raw Linear Link Position value
- */
-u64 hda_dsp_get_stream_llp(struct snd_sof_dev *sdev,
-			   struct snd_soc_component *component,
-			   struct snd_pcm_substream *substream)
+static u64 hda_dsp_get_llp(struct snd_sof_dev *sdev,
+			   struct snd_soc_pcm_runtime *rtd, int dir)
 {
-	struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
 	struct snd_soc_pcm_runtime *be_rtd = NULL;
 	struct hdac_ext_stream *hext_stream;
 	struct snd_soc_dai *cpu_dai;
@@ -1185,7 +1187,7 @@ u64 hda_dsp_get_stream_llp(struct snd_sof_dev *sdev,
 	 * The LLP needs to be read from the Link DMA used for this FE as it is
 	 * allowed to use any combination of Link and Host channels
 	 */
-	for_each_dpcm_be(rtd, substream->stream, dpcm) {
+	for_each_dpcm_be(rtd, dir, dpcm) {
 		if (dpcm->fe != rtd)
 			continue;
 
@@ -1199,7 +1201,7 @@ u64 hda_dsp_get_stream_llp(struct snd_sof_dev *sdev,
 	if (!cpu_dai)
 		return 0;
 
-	hext_stream = snd_soc_dai_get_dma_data(cpu_dai, substream);
+	hext_stream = snd_soc_dai_dma_data_get(cpu_dai, dir);
 	if (!hext_stream)
 		return 0;
 
@@ -1223,8 +1225,37 @@ u64 hda_dsp_get_stream_llp(struct snd_sof_dev *sdev,
 
 	return merge_u64(llp_u, llp_l);
 }
+
+/**
+ * hda_dsp_get_stream_llp - Retrieve the LLP (Linear Link Position) of the stream
+ * @sdev: SOF device
+ * @component: ASoC component
+ * @substream: PCM substream
+ *
+ * Returns the raw Linear Link Position value
+ */
+u64 hda_dsp_get_stream_llp(struct snd_sof_dev *sdev,
+			   struct snd_soc_component *component,
+			   struct snd_pcm_substream *substream)
+{
+	return hda_dsp_get_llp(sdev, snd_soc_substream_to_rtd(substream),
+			       substream->stream);
+}
 EXPORT_SYMBOL_NS(hda_dsp_get_stream_llp, "SND_SOC_SOF_INTEL_HDA_COMMON");
 
+/**
+ * hda_dsp_compr_get_stream_llp - Retrieve the LLP (Linear Link Position) of the stream
+ * @sdev: SOF device
+ * @cstream: Compress stream
+ *
+ * Returns the raw Linear Link Position value
+ */
+u64 hda_dsp_compr_get_stream_llp(struct snd_sof_dev *sdev, struct snd_compr_stream *cstream)
+{
+	return hda_dsp_get_llp(sdev, cstream->private_data, cstream->direction);
+}
+EXPORT_SYMBOL_NS(hda_dsp_compr_get_stream_llp, "SND_SOC_SOF_INTEL_HDA_COMMON");
+
 /**
  * hda_dsp_get_stream_ldp - Retrieve the LDP (Linear DMA Position) of the stream
  * @sdev: SOF device
diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h
index 1609589929a1..701578ec35cf 100644
--- a/sound/soc/sof/intel/hda.h
+++ b/sound/soc/sof/intel/hda.h
@@ -683,6 +683,21 @@ snd_pcm_uframes_t hda_dsp_pcm_pointer(struct snd_sof_dev *sdev,
 				      struct snd_pcm_substream *substream);
 int hda_dsp_pcm_ack(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream);
 
+int hda_dsp_compr_open(struct snd_sof_dev *sdev, struct snd_compr_stream *cstream);
+int hda_dsp_compr_close(struct snd_sof_dev *sdev, struct snd_compr_stream *cstream);
+int hda_dsp_compr_hw_params(struct snd_sof_dev *sdev,
+			    struct snd_compr_stream *cstream,
+			    struct snd_compr_params *params,
+			    struct snd_sof_platform_stream_params *platform_params);
+int hda_dsp_stream_compr_hw_free(struct snd_sof_dev *sdev,
+				 struct snd_compr_stream *cstream);
+int hda_dsp_compr_trigger(struct snd_sof_dev *sdev,
+			  struct snd_compr_stream *cstream, int cmd);
+int hda_dsp_compr_pointer(struct snd_sof_dev *sdev, struct snd_compr_stream *cstream,
+			  struct snd_compr_tstamp64 *tstamp);
+u64 hda_dsp_compr_get_stream_llp(struct snd_sof_dev *sdev,
+				 struct snd_compr_stream *cstream);
+
 /*
  * DSP Stream Operations.
  */
diff --git a/sound/soc/sof/ops.h b/sound/soc/sof/ops.h
index 72af1f4ff620..4afc92490fbe 100644
--- a/sound/soc/sof/ops.h
+++ b/sound/soc/sof/ops.h
@@ -15,6 +15,7 @@
 #include <linux/interrupt.h>
 #include <linux/kernel.h>
 #include <linux/types.h>
+#include <sound/compress_driver.h>
 #include <sound/pcm.h>
 #include "sof-priv.h"
 
@@ -448,6 +449,79 @@ snd_sof_pcm_platform_hw_params(struct snd_sof_dev *sdev,
 	return 0;
 }
 
+static inline int
+snd_sof_compr_platform_open(struct snd_sof_dev *sdev, struct snd_compr_stream *cstream)
+{
+	if (sof_ops(sdev) && sof_ops(sdev)->compr_open)
+		return sof_ops(sdev)->compr_open(sdev, cstream);
+
+	return 0;
+}
+
+/* disconnect pcm substream to a host stream */
+static inline int
+snd_sof_compr_platform_close(struct snd_sof_dev *sdev, struct snd_compr_stream *cstream)
+{
+	if (sof_ops(sdev) && sof_ops(sdev)->compr_close)
+		return sof_ops(sdev)->compr_close(sdev, cstream);
+
+	return 0;
+}
+
+/* host stream hw params */
+static inline int
+snd_sof_compr_platform_hw_params(struct snd_sof_dev *sdev,
+				 struct snd_compr_stream *cstream,
+				 struct snd_compr_params *params,
+				 struct snd_sof_platform_stream_params *platform_params)
+{
+	if (sof_ops(sdev) && sof_ops(sdev)->compr_hw_params)
+		return sof_ops(sdev)->compr_hw_params(sdev, cstream, params, platform_params);
+
+	return 0;
+}
+
+static inline int
+snd_sof_compr_platform_hw_free(struct snd_sof_dev *sdev,
+			       struct snd_compr_stream *cstream)
+{
+	if (sof_ops(sdev) && sof_ops(sdev)->compr_hw_free)
+		return sof_ops(sdev)->compr_hw_free(sdev, cstream);
+
+	return 0;
+}
+
+static inline int
+snd_sof_compr_platform_trigger(struct snd_sof_dev *sdev,
+			       struct snd_compr_stream *cstream, int cmd)
+{
+	if (sof_ops(sdev) && sof_ops(sdev)->compr_trigger)
+		return sof_ops(sdev)->compr_trigger(sdev, cstream, cmd);
+
+	return 0;
+}
+
+static inline int
+snd_sof_compr_platform_pointer(struct snd_sof_dev *sdev,
+			       struct snd_compr_stream *cstream,
+			       struct snd_compr_tstamp64 *tstamp)
+{
+	if (sof_ops(sdev) && sof_ops(sdev)->compr_pointer)
+		return sof_ops(sdev)->compr_pointer(sdev, cstream, tstamp);
+
+	return 0;
+}
+
+static inline u64
+snd_sof_compr_get_dai_frame_counter(struct snd_sof_dev *sdev,
+				    struct snd_compr_stream *cstream)
+{
+	if (sof_ops(sdev) && sof_ops(sdev)->compr_get_dai_frame_counter)
+		return sof_ops(sdev)->compr_get_dai_frame_counter(sdev, cstream);
+
+	return 0;
+}
+
 /* host stream hw free */
 static inline int
 snd_sof_pcm_platform_hw_free(struct snd_sof_dev *sdev,
diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h
index 38753b088fc1..560be58611c0 100644
--- a/sound/soc/sof/sof-priv.h
+++ b/sound/soc/sof/sof-priv.h
@@ -256,6 +256,18 @@ struct snd_sof_dsp_ops {
 	/* pcm ack */
 	int (*pcm_ack)(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream); /* optional */
 
+	int (*compr_open)(struct snd_sof_dev *sdev, struct snd_compr_stream *cstream);
+	int (*compr_close)(struct snd_sof_dev *sdev, struct snd_compr_stream *cstream);
+	int (*compr_hw_params)(struct snd_sof_dev *sdev, struct snd_compr_stream *cstream,
+			       struct snd_compr_params *params,
+			       struct snd_sof_platform_stream_params *platform_params);
+	int (*compr_hw_free)(struct snd_sof_dev *sdev, struct snd_compr_stream *cstream);
+	int (*compr_trigger)(struct snd_sof_dev *sdev, struct snd_compr_stream *cstream,
+			     int cmd);
+	int (*compr_pointer)(struct snd_sof_dev *sdev, struct snd_compr_stream *cstream,
+			     struct snd_compr_tstamp64 *tstamp);
+	u64 (*compr_get_dai_frame_counter)(struct snd_sof_dev *sdev,
+					   struct snd_compr_stream *cstream);
 	/*
 	 * optional callback to retrieve the number of frames left/arrived from/to
 	 * the DSP on the DAI side (link/codec/DMIC/etc).
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 49+ messages in thread

* [PATCH 13/23] ASoC: SOF: ipc4: Add definition of module data in init_ext object type
  2026-09-04  7:31 [PATCH 00/23] ALSA compress / ASoC compress / SOF: Compressed audio support with IPC4 Peter Ujfalusi
                   ` (11 preceding siblings ...)
  2026-09-04  7:31 ` [PATCH 12/23] ASoC: SOF: ops: Add new platform-specific ops for compress Peter Ujfalusi
@ 2026-09-04  7:31 ` Peter Ujfalusi
  2026-09-04  7:31 ` [PATCH 14/23] ASoC: SOF: ipc4-topology: Support init_ext_module_data for process modules Peter Ujfalusi
                   ` (9 subsequent siblings)
  22 siblings, 0 replies; 49+ messages in thread
From: Peter Ujfalusi @ 2026-09-04  7:31 UTC (permalink / raw)
  To: vkoul, perex, tiwai, lgirdwood, broonie, srinivas.kandagatla
  Cc: linux-sound, kai.vehmanen, yung-chuan.liao, pierre-louis.bossart,
	daniel.baluta, Vijendar.Mukunda

The SOF_IPC4_MOD_INIT_DATA_ID_MODULE_DATA type within the module_init_ext
area is module specific init data.

Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
Reviewed-by: Liam Girdwood <liam.r.girdwood@intel.com>
---
 include/sound/sof/ipc4/header.h | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/include/sound/sof/ipc4/header.h b/include/sound/sof/ipc4/header.h
index cea3d9453b84..e5161f0a37e8 100644
--- a/include/sound/sof/ipc4/header.h
+++ b/include/sound/sof/ipc4/header.h
@@ -706,7 +706,8 @@ struct sof_ipc4_module_init_ext_object {
 enum sof_ipc4_mod_init_ext_obj_id {
 	SOF_IPC4_MOD_INIT_DATA_ID_INVALID = 0,
 	SOF_IPC4_MOD_INIT_DATA_ID_DP_DATA,
-	SOF_IPC4_MOD_INIT_DATA_ID_MAX = SOF_IPC4_MOD_INIT_DATA_ID_DP_DATA,
+	SOF_IPC4_MOD_INIT_DATA_ID_MODULE_DATA,
+	SOF_IPC4_MOD_INIT_DATA_ID_MAX = SOF_IPC4_MOD_INIT_DATA_ID_MODULE_DATA,
 };
 
 /* DP module memory configuration data object for object array */
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 49+ messages in thread

* [PATCH 14/23] ASoC: SOF: ipc4-topology: Support init_ext_module_data for process modules
  2026-09-04  7:31 [PATCH 00/23] ALSA compress / ASoC compress / SOF: Compressed audio support with IPC4 Peter Ujfalusi
                   ` (12 preceding siblings ...)
  2026-09-04  7:31 ` [PATCH 13/23] ASoC: SOF: ipc4: Add definition of module data in init_ext object type Peter Ujfalusi
@ 2026-09-04  7:31 ` Peter Ujfalusi
  2026-09-04  7:31 ` [PATCH 15/23] ASoC: SOF: ipc4-pcm: Make the timestamp info usable outside of ipc4-pcm.c Peter Ujfalusi
                   ` (8 subsequent siblings)
  22 siblings, 0 replies; 49+ messages in thread
From: Peter Ujfalusi @ 2026-09-04  7:31 UTC (permalink / raw)
  To: vkoul, perex, tiwai, lgirdwood, broonie, srinivas.kandagatla
  Cc: linux-sound, kai.vehmanen, yung-chuan.liao, pierre-louis.bossart,
	daniel.baluta, Vijendar.Mukunda

Add support for handling init_ext_module_data for process modules, which is
going to be used by decoder and encoder type of process modules.
The support is generic and it can be extended to other type of process
modules or other module types than process with a small update of
sof_ipc4_add_init_ext_module_data() function.

Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
Reviewed-by: Liam Girdwood <liam.r.girdwood@intel.com>
---
 sound/soc/sof/ipc4-topology.c | 101 ++++++++++++++++++++++------------
 sound/soc/sof/ipc4-topology.h |   4 ++
 sound/soc/sof/sof-audio.h     |   1 +
 3 files changed, 72 insertions(+), 34 deletions(-)

diff --git a/sound/soc/sof/ipc4-topology.c b/sound/soc/sof/ipc4-topology.c
index 24a1175dd690..42cf444e847e 100644
--- a/sound/soc/sof/ipc4-topology.c
+++ b/sound/soc/sof/ipc4-topology.c
@@ -3263,22 +3263,56 @@ static void sof_ipc4_add_init_ext_dp_memory_data(struct snd_sof_dev *sdev,
 						 u32 *payload, u32 *ext_pos,
 						 struct sof_ipc4_module_init_ext_object **hdr)
 {
-	/* Add memory_data if comp_domain indicates DP */
-	if (swidget->comp_domain == SOF_COMP_DOMAIN_DP) {
-		struct sof_ipc4_mod_init_ext_dp_memory_data *dp_mem_data;
-
-		*hdr = (struct sof_ipc4_module_init_ext_object *)&payload[*ext_pos];
-		(*hdr)->header =
-			SOF_IPC4_MOD_INIT_EXT_OBJ_ID(SOF_IPC4_MOD_INIT_DATA_ID_DP_DATA) |
-			SOF_IPC4_MOD_INIT_EXT_OBJ_WORDS(DIV_ROUND_UP(sizeof(*dp_mem_data),
-								     sizeof(u32)));
-		*ext_pos += DIV_ROUND_UP(sizeof(**hdr), sizeof(u32));
-		dp_mem_data = (struct sof_ipc4_mod_init_ext_dp_memory_data *)&payload[*ext_pos];
-		dp_mem_data->domain_id = swidget->domain_id;
-		dp_mem_data->stack_bytes = swidget->stack_bytes;
-		dp_mem_data->heap_bytes = swidget->heap_bytes;
-		*ext_pos += DIV_ROUND_UP(sizeof(*dp_mem_data), sizeof(u32));
+	struct sof_ipc4_mod_init_ext_dp_memory_data *dp_mem_data;
+
+	*hdr = (struct sof_ipc4_module_init_ext_object *)&payload[*ext_pos];
+	(*hdr)->header =
+		SOF_IPC4_MOD_INIT_EXT_OBJ_ID(SOF_IPC4_MOD_INIT_DATA_ID_DP_DATA) |
+		SOF_IPC4_MOD_INIT_EXT_OBJ_WORDS(DIV_ROUND_UP(sizeof(*dp_mem_data),
+							     sizeof(u32)));
+	*ext_pos += DIV_ROUND_UP(sizeof(**hdr), sizeof(u32));
+	dp_mem_data = (struct sof_ipc4_mod_init_ext_dp_memory_data *)&payload[*ext_pos];
+	dp_mem_data->domain_id = swidget->domain_id;
+	dp_mem_data->stack_bytes = swidget->stack_bytes;
+	dp_mem_data->heap_bytes = swidget->heap_bytes;
+	*ext_pos += DIV_ROUND_UP(sizeof(*dp_mem_data), sizeof(u32));
+}
+
+static int
+sof_ipc4_add_init_ext_module_data(struct snd_sof_dev *sdev,
+				  struct sof_ipc4_process *process,
+				  u32 *payload, u32 *ext_pos,
+				  struct sof_ipc4_module_init_ext_object **hdr)
+{
+	u32 data_size = process->init_ext_module_size;
+	void *data = process->init_ext_module_data;
+	size_t needed;
+
+	/*
+	 * Unlike the other objects, the module data is of variable size,
+	 * provided by the module which is being set up.
+	 * Make sure that the object fits into the payload buffer before any of
+	 * it is written.
+	 */
+	needed = ((size_t)*ext_pos + DIV_ROUND_UP(sizeof(**hdr), sizeof(u32)) +
+		  DIV_ROUND_UP(data_size, sizeof(u32))) * sizeof(u32);
+	if (needed > sdev->ipc->max_payload_size) {
+		dev_err(sdev->dev,
+			"Max ipc payload size %zu exceeded by module data: %zu\n",
+			sdev->ipc->max_payload_size, needed);
+		return -EINVAL;
 	}
+
+	*hdr = (struct sof_ipc4_module_init_ext_object *)&payload[*ext_pos];
+	(*hdr)->header = SOF_IPC4_MOD_INIT_EXT_OBJ_ID(SOF_IPC4_MOD_INIT_DATA_ID_MODULE_DATA) |
+		SOF_IPC4_MOD_INIT_EXT_OBJ_WORDS(DIV_ROUND_UP(data_size, sizeof(u32)));
+	*ext_pos += DIV_ROUND_UP(sizeof(*(*hdr)), sizeof(u32));
+
+	memcpy(&payload[*ext_pos], data, data_size);
+
+	*ext_pos += DIV_ROUND_UP(data_size, sizeof(u32));
+
+	return 0;
 }
 
 static int sof_ipc4_widget_mod_init_msg_payload(struct snd_sof_dev *sdev,
@@ -3287,17 +3321,17 @@ static int sof_ipc4_widget_mod_init_msg_payload(struct snd_sof_dev *sdev,
 						void *ipc_data, u32 ipc_size,
 						void **new_data)
 {
-	struct sof_ipc4_module_init_ext_init *ext_init;
+	struct sof_ipc4_process *process = swidget->private;
 	struct sof_ipc4_module_init_ext_object *hdr = NULL;
+	struct sof_ipc4_module_init_ext_init *ext_init;
+	bool in_dp_domain = swidget->comp_domain == SOF_COMP_DOMAIN_DP;
+	bool has_ext_data = WIDGET_IS_PROCESS(swidget->id) && process->init_ext_module_size;
 	int new_size;
 	u32 *payload;
 	u32 ext_pos;
+	int ret;
 
-	/*
-	 * Only DP widgets currently add init-ext objects here. Avoid allocating
-	 * a max-sized payload buffer for widgets that will immediately return 0.
-	 */
-	if (swidget->comp_domain != SOF_COMP_DOMAIN_DP)
+	if (!in_dp_domain && !has_ext_data)
 		return 0;
 
 	payload = kzalloc(sdev->ipc->max_payload_size, GFP_KERNEL);
@@ -3306,25 +3340,24 @@ static int sof_ipc4_widget_mod_init_msg_payload(struct snd_sof_dev *sdev,
 
 	/* Add ext_init first and set objects array flag to 1 */
 	ext_init = (struct sof_ipc4_module_init_ext_init *)payload;
+	ext_init->word0 |= SOF_IPC4_MOD_INIT_EXT_OBJ_ARRAY_MASK;
 	ext_pos = DIV_ROUND_UP(sizeof(*ext_init), sizeof(u32));
 
 	/* Add object array objects after ext_init */
+	if (in_dp_domain)
+		sof_ipc4_add_init_ext_dp_memory_data(sdev, swidget, payload,
+						     &ext_pos, &hdr);
 
-	sof_ipc4_add_init_ext_dp_memory_data(sdev, swidget, payload, &ext_pos, &hdr);
-
-	/* Add following object array items here */
-
-	if (!hdr) {
-		/*
-		 * NOTE: Remove this early bail out, when struct
-		 *       sof_ipc4_module_init_ext_init alone has some
-		 *       function.
-		 */
-		kfree(payload);
-		return 0;
+	if (has_ext_data) {
+		ret = sof_ipc4_add_init_ext_module_data(sdev, process, payload,
+							&ext_pos, &hdr);
+		if (ret) {
+			kfree(payload);
+			return ret;
+		}
 	}
 
-	ext_init->word0 |= SOF_IPC4_MOD_INIT_EXT_OBJ_ARRAY_MASK;
+	/* Set last bit for the last object in the array */
 	hdr->header |= SOF_IPC4_MOD_INIT_EXT_OBJ_LAST_MASK;
 
 	/* Calculate final size and check that it fits to max payload size */
diff --git a/sound/soc/sof/ipc4-topology.h b/sound/soc/sof/ipc4-topology.h
index c9fe9f2e4083..5e78f3de092e 100644
--- a/sound/soc/sof/ipc4-topology.h
+++ b/sound/soc/sof/ipc4-topology.h
@@ -524,6 +524,8 @@ struct sof_ipc4_base_module_cfg_ext {
  * @msg: IPC4 message struct containing header and data info
  * @base_config_ext_size: Size of the base config extension data in bytes
  * @init_config: Module init config type (SOF_IPC4_MODULE_INIT_CONFIG_TYPE_*)
+ * @init_ext_module_data: module_data for init_ext object
+ * @init_ext_module_size: size of init_ext_module_data
  */
 struct sof_ipc4_process {
 	struct sof_ipc4_base_module_cfg base_config;
@@ -535,6 +537,8 @@ struct sof_ipc4_process {
 	struct sof_ipc4_msg msg;
 	u32 base_config_ext_size;
 	u32 init_config;
+	void *init_ext_module_data;
+	size_t init_ext_module_size;
 };
 
 bool sof_ipc4_copier_is_single_bitdepth(struct snd_sof_dev *sdev,
diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h
index 0610f2ab844d..151cfa9e6b7c 100644
--- a/sound/soc/sof/sof-audio.h
+++ b/sound/soc/sof/sof-audio.h
@@ -43,6 +43,7 @@
 #define WIDGET_IS_AIF(id) ((id) == snd_soc_dapm_aif_in || (id) == snd_soc_dapm_aif_out)
 #define WIDGET_IS_AIF_OR_DAI(id) (WIDGET_IS_DAI(id) || WIDGET_IS_AIF(id))
 #define WIDGET_IS_COPIER(id) (WIDGET_IS_AIF_OR_DAI(id) || (id) == snd_soc_dapm_buffer)
+#define WIDGET_IS_PROCESS(id) ((id) == snd_soc_dapm_effect)
 
 #define SOF_DAI_PARAM_INTEL_SSP_MCLK		0
 #define SOF_DAI_PARAM_INTEL_SSP_BCLK		1
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 49+ messages in thread

* [PATCH 15/23] ASoC: SOF: ipc4-pcm: Make the timestamp info usable outside of ipc4-pcm.c
  2026-09-04  7:31 [PATCH 00/23] ALSA compress / ASoC compress / SOF: Compressed audio support with IPC4 Peter Ujfalusi
                   ` (13 preceding siblings ...)
  2026-09-04  7:31 ` [PATCH 14/23] ASoC: SOF: ipc4-topology: Support init_ext_module_data for process modules Peter Ujfalusi
@ 2026-09-04  7:31 ` Peter Ujfalusi
  2026-09-04  7:31 ` [PATCH 16/23] ASoC: SOF: ipc4/ipc4-loader: Add SOF_INFO and CODEC_INFO to fw_config_params Peter Ujfalusi
                   ` (7 subsequent siblings)
  22 siblings, 0 replies; 49+ messages in thread
From: Peter Ujfalusi @ 2026-09-04  7:31 UTC (permalink / raw)
  To: vkoul, perex, tiwai, lgirdwood, broonie, srinivas.kandagatla
  Cc: linux-sound, kai.vehmanen, yung-chuan.liao, pierre-louis.bossart,
	daniel.baluta, Vijendar.Mukunda

The support for compressed stream will also need to have access to the
same information which is used for delay reporting for DAI data
progression tracking.

Make the necessary struct and functions to be available and premare them to
be called without a valid substream.

Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
Reviewed-by: Liam Girdwood <liam.r.girdwood@intel.com>
---
 sound/soc/sof/ipc4-pcm.c  | 40 +++++++++------------------------------
 sound/soc/sof/ipc4-priv.h | 31 ++++++++++++++++++++++++++++++
 2 files changed, 40 insertions(+), 31 deletions(-)

diff --git a/sound/soc/sof/ipc4-pcm.c b/sound/soc/sof/ipc4-pcm.c
index 6e8522d03a2b..45cdd5d8fbbf 100644
--- a/sound/soc/sof/ipc4-pcm.c
+++ b/sound/soc/sof/ipc4-pcm.c
@@ -15,29 +15,6 @@
 #include "ipc4-topology.h"
 #include "ipc4-fw-reg.h"
 
-/**
- * struct sof_ipc4_timestamp_info - IPC4 timestamp info
- * @host_copier: the host copier of the pcm stream
- * @dai_copier: the dai copier of the pcm stream
- * @stream_start_offset: reported by fw in memory window (converted to
- *                       frames at host_copier sampling rate)
- * @stream_end_offset: reported by fw in memory window (converted to
- *                     frames at host_copier sampling rate)
- * @llp_offset: llp offset in memory window
- * @delay: Calculated and stored in pointer callback. The stored value is
- *         returned in the delay callback. Expressed in frames at host copier
- *         sampling rate.
- */
-struct sof_ipc4_timestamp_info {
-	struct sof_ipc4_copier *host_copier;
-	struct sof_ipc4_copier *dai_copier;
-	u64 stream_start_offset;
-	u64 stream_end_offset;
-	u32 llp_offset;
-
-	snd_pcm_sframes_t delay;
-};
-
 /**
  * struct sof_ipc4_pcm_stream_priv - IPC4 specific private data
  * @time_info: pointer to time info struct if it is supported, otherwise NULL
@@ -61,7 +38,7 @@ struct sof_ipc4_pcm_stream_priv {
 
 #define DELAY_MAX		(DELAY_BOUNDARY >> 1)
 
-static inline struct sof_ipc4_timestamp_info *
+struct sof_ipc4_timestamp_info *
 sof_ipc4_sps_to_time_info(struct snd_sof_pcm_stream *sps)
 {
 	struct sof_ipc4_pcm_stream_priv *stream_priv = sps->private;
@@ -990,7 +967,7 @@ static int sof_ipc4_pcm_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm
 	return 0;
 }
 
-static void sof_ipc4_build_time_info(struct snd_sof_dev *sdev, struct snd_sof_pcm_stream *sps)
+void sof_ipc4_build_time_info(struct snd_sof_dev *sdev, struct snd_sof_pcm_stream *sps)
 {
 	struct sof_ipc4_copier *host_copier = NULL;
 	struct sof_ipc4_copier *dai_copier = NULL;
@@ -1088,7 +1065,7 @@ static int sof_ipc4_pcm_hw_params(struct snd_soc_component *component,
 	return 0;
 }
 
-static u64 sof_ipc4_frames_dai_to_host(struct sof_ipc4_timestamp_info *time_info, u64 value)
+u64 sof_ipc4_frames_dai_to_host(struct sof_ipc4_timestamp_info *time_info, u64 value)
 {
 	u64 dai_rate, host_rate;
 
@@ -1117,10 +1094,10 @@ static u64 sof_ipc4_frames_dai_to_host(struct sof_ipc4_timestamp_info *time_info
 	return value;
 }
 
-static int sof_ipc4_get_stream_start_offset(struct snd_sof_dev *sdev,
-					    struct snd_pcm_substream *substream,
-					    struct snd_sof_pcm_stream *sps,
-					    struct sof_ipc4_timestamp_info *time_info)
+int sof_ipc4_get_stream_start_offset(struct snd_sof_dev *sdev,
+				     struct snd_pcm_substream *substream,
+				     struct snd_sof_pcm_stream *sps,
+				     struct sof_ipc4_timestamp_info *time_info)
 {
 	struct sof_ipc4_copier *host_copier = time_info->host_copier;
 	struct sof_ipc4_copier *dai_copier = time_info->dai_copier;
@@ -1134,7 +1111,8 @@ static int sof_ipc4_get_stream_start_offset(struct snd_sof_dev *sdev,
 
 	if (host_copier->data.gtw_cfg.node_id == SOF_IPC4_INVALID_NODE_ID) {
 		return -EINVAL;
-	} else if (host_copier->data.gtw_cfg.node_id == SOF_IPC4_CHAIN_DMA_NODE_ID) {
+	} else if (substream &&
+		   host_copier->data.gtw_cfg.node_id == SOF_IPC4_CHAIN_DMA_NODE_ID) {
 		/*
 		 * While the firmware does not support time_info reporting for
 		 * streams using ChainDMA, it is granted that ChainDMA can only
diff --git a/sound/soc/sof/ipc4-priv.h b/sound/soc/sof/ipc4-priv.h
index a8cdf9bc750b..7c0861d63bef 100644
--- a/sound/soc/sof/ipc4-priv.h
+++ b/sound/soc/sof/ipc4-priv.h
@@ -98,6 +98,29 @@ struct sof_ipc4_fw_data {
 	struct mutex pipeline_state_mutex; /* protect pipeline triggers, ref counts and states */
 };
 
+/**
+ * struct sof_ipc4_timestamp_info - IPC4 timestamp info
+ * @host_copier: the host copier of the pcm stream
+ * @dai_copier: the dai copier of the pcm stream
+ * @stream_start_offset: reported by fw in memory window (converted to
+ *                       frames at host_copier sampling rate)
+ * @stream_end_offset: reported by fw in memory window (converted to
+ *                     frames at host_copier sampling rate)
+ * @llp_offset: llp offset in memory window
+ * @delay: Calculated and stored in pointer callback. The stored value is
+ *         returned in the delay callback. Expressed in frames at host copier
+ *         sampling rate.
+ */
+struct sof_ipc4_timestamp_info {
+	struct sof_ipc4_copier *host_copier;
+	struct sof_ipc4_copier *dai_copier;
+	u64 stream_start_offset;
+	u64 stream_end_offset;
+	u32 llp_offset;
+
+	snd_pcm_sframes_t delay;
+};
+
 extern const struct sof_ipc_fw_loader_ops ipc4_loader_ops;
 extern const struct sof_ipc_tplg_ops ipc4_tplg_ops;
 extern const struct sof_ipc_tplg_control_ops tplg_ipc4_control_ops;
@@ -129,4 +152,12 @@ void sof_ipc4_mic_privacy_state_change(struct snd_sof_dev *sdev, bool state);
 enum sof_ipc4_pipeline_state;
 const char *sof_ipc4_pipeline_state_str(enum sof_ipc4_pipeline_state state);
 
+struct sof_ipc4_timestamp_info *sof_ipc4_sps_to_time_info(struct snd_sof_pcm_stream *sps);
+void sof_ipc4_build_time_info(struct snd_sof_dev *sdev, struct snd_sof_pcm_stream *sps);
+int sof_ipc4_get_stream_start_offset(struct snd_sof_dev *sdev,
+				     struct snd_pcm_substream *substream,
+				     struct snd_sof_pcm_stream *sps,
+				     struct sof_ipc4_timestamp_info *time_info);
+u64 sof_ipc4_frames_dai_to_host(struct sof_ipc4_timestamp_info *time_info, u64 value);
+
 #endif
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 49+ messages in thread

* [PATCH 16/23] ASoC: SOF: ipc4/ipc4-loader: Add SOF_INFO and CODEC_INFO to fw_config_params
  2026-09-04  7:31 [PATCH 00/23] ALSA compress / ASoC compress / SOF: Compressed audio support with IPC4 Peter Ujfalusi
                   ` (14 preceding siblings ...)
  2026-09-04  7:31 ` [PATCH 15/23] ASoC: SOF: ipc4-pcm: Make the timestamp info usable outside of ipc4-pcm.c Peter Ujfalusi
@ 2026-09-04  7:31 ` Peter Ujfalusi
  2026-09-04  7:31 ` [PATCH 17/23] ASoC: SOF: ipc4-pcm: Handle COMPR DRAIN triggers as EOS pipeline state Peter Ujfalusi
                   ` (6 subsequent siblings)
  22 siblings, 0 replies; 49+ messages in thread
From: Peter Ujfalusi @ 2026-09-04  7:31 UTC (permalink / raw)
  To: vkoul, perex, tiwai, lgirdwood, broonie, srinivas.kandagatla
  Cc: linux-sound, kai.vehmanen, yung-chuan.liao, pierre-louis.bossart,
	daniel.baluta, Vijendar.Mukunda

SOF_INFO (id == 35) tuple holds tuple structured information about SOF
features.

The first entry in SOF_INFO is the SOF_CODEC_INFO (id == 0) which contains
information about the supported codecs for decode/encode in the booted
firmware.

If present in the fw_config payload, make a copy of it and store it
sof_ipc4_fw_data->codec_info to be used by the compressed code.

Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
Reviewed-by: Liam Girdwood <liam.r.girdwood@intel.com>
---
 include/sound/sof/ipc4/header.h | 18 ++++++++----
 sound/soc/sof/ipc4-loader.c     | 51 +++++++++++++++++++++++++++++++++
 sound/soc/sof/ipc4-priv.h       |  4 +++
 3 files changed, 67 insertions(+), 6 deletions(-)

diff --git a/include/sound/sof/ipc4/header.h b/include/sound/sof/ipc4/header.h
index e5161f0a37e8..dd1e0d1cf0d8 100644
--- a/include/sound/sof/ipc4/header.h
+++ b/include/sound/sof/ipc4/header.h
@@ -436,12 +436,10 @@ enum sof_ipc4_fw_config_params {
 	SOF_IPC4_FW_CFG_RESERVED,
 	SOF_IPC4_FW_CFG_POWER_GATING_POLICY,
 	SOF_IPC4_FW_CFG_ASSERT_MODE,
-	SOF_IPC4_FW_RESERVED1,
-	SOF_IPC4_FW_RESERVED2,
-	SOF_IPC4_FW_RESERVED3,
-	SOF_IPC4_FW_RESERVED4,
-	SOF_IPC4_FW_RESERVED5,
-	SOF_IPC4_FW_CONTEXT_SAVE
+	/* Reserved: 24 - 28 */
+	SOF_IPC4_FW_CONTEXT_SAVE = 29,
+	/* Reserved: 30 - 34 */
+	SOF_IPC4_FW_CFG_SOF_INFO = 35,
 };
 
 struct sof_ipc4_fw_version {
@@ -451,6 +449,14 @@ struct sof_ipc4_fw_version {
 	uint16_t build;
 } __packed;
 
+/*
+ * tuple based array for SOF specific information under SOF_IPC4_FW_CFG_SOF_INFO
+ * tuple of fw_config
+ */
+enum ipc4_fw_sof_info_params {
+	SOF_IPC4_SOF_CODEC_INFO,
+};
+
 /* Payload data for SOF_IPC4_MOD_SET_DX */
 struct sof_ipc4_dx_state_info {
 	/* core(s) to apply the change */
diff --git a/sound/soc/sof/ipc4-loader.c b/sound/soc/sof/ipc4-loader.c
index 07a78cb3c25c..8e0945b37ec8 100644
--- a/sound/soc/sof/ipc4-loader.c
+++ b/sound/soc/sof/ipc4-loader.c
@@ -408,6 +408,52 @@ static int sof_ipc4_validate_firmware(struct snd_sof_dev *sdev)
 	return 0;
 }
 
+static int sof_ipc4_query_sof_info(struct snd_sof_dev *sdev,
+				   void *sof_info_data, u32 sof_info_size)
+{
+	struct sof_ipc4_fw_data *ipc4_data = sdev->private;
+	struct sof_ipc4_tuple *tuple;
+	size_t tuple_size;
+	size_t offset = 0;
+	int ret = 0;
+
+	while (offset < sof_info_size) {
+		if (sof_info_size - offset < sizeof(*tuple)) {
+			dev_err(sdev->dev, "Invalid SOF info tuple header at offset %zu\n", offset);
+			ret = -EINVAL;
+			goto out;
+		}
+
+		tuple = (struct sof_ipc4_tuple *)((u8 *)sof_info_data + offset);
+		tuple_size = sizeof(*tuple) + tuple->size;
+		if (tuple_size < sizeof(*tuple) || tuple_size > sof_info_size - offset) {
+			dev_err(sdev->dev,
+				"Invalid SOF info tuple size %u at offset %zu\n",
+				tuple->size, offset);
+			ret = -EINVAL;
+			goto out;
+		}
+
+		switch (tuple->type) {
+		case SOF_IPC4_SOF_CODEC_INFO:
+			ipc4_data->codec_info = devm_kmemdup(sdev->dev, tuple->value,
+							     tuple->size, GFP_KERNEL);
+			if (!ipc4_data->codec_info) {
+				ret = -ENOMEM;
+				goto out;
+			}
+			break;
+		default:
+			break;
+		}
+
+		offset += tuple_size;
+	}
+
+out:
+	return ret;
+}
+
 int sof_ipc4_query_fw_configuration(struct snd_sof_dev *sdev)
 {
 	struct sof_ipc4_fw_data *ipc4_data = sdev->private;
@@ -483,6 +529,11 @@ int sof_ipc4_query_fw_configuration(struct snd_sof_dev *sdev)
 			 */
 			ipc4_data->libraries_restored = ipc4_data->fw_context_save;
 			break;
+		case SOF_IPC4_FW_CFG_SOF_INFO:
+			ret = sof_ipc4_query_sof_info(sdev, tuple->value, tuple->size);
+			if (ret)
+				goto out;
+			break;
 		default:
 			break;
 		}
diff --git a/sound/soc/sof/ipc4-priv.h b/sound/soc/sof/ipc4-priv.h
index 7c0861d63bef..07df6799c7a8 100644
--- a/sound/soc/sof/ipc4-priv.h
+++ b/sound/soc/sof/ipc4-priv.h
@@ -75,6 +75,8 @@ struct sof_ipc4_fw_library {
  * @fw_context_save: Firmware supports full context save and restore
  * @libraries_restored: The libraries have been retained during firmware boot
  *
+ * @codec_info: Information about the available codecs in booted firmware. The
+ *		data is to be used by the code for compressed support.
  * @load_library: Callback function for platform dependent library loading
  * @pipeline_state_mutex: Mutex to protect pipeline triggers, ref counts, states and deletion
  */
@@ -91,6 +93,8 @@ struct sof_ipc4_fw_data {
 	bool fw_context_save;
 	bool libraries_restored;
 
+	void *codec_info;
+
 	int (*load_library)(struct snd_sof_dev *sdev,
 			    struct sof_ipc4_fw_library *fw_lib, bool reload);
 	void (*intel_configure_mic_privacy)(struct snd_sof_dev *sdev,
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 49+ messages in thread

* [PATCH 17/23] ASoC: SOF: ipc4-pcm: Handle COMPR DRAIN triggers as EOS pipeline state
  2026-09-04  7:31 [PATCH 00/23] ALSA compress / ASoC compress / SOF: Compressed audio support with IPC4 Peter Ujfalusi
                   ` (15 preceding siblings ...)
  2026-09-04  7:31 ` [PATCH 16/23] ASoC: SOF: ipc4/ipc4-loader: Add SOF_INFO and CODEC_INFO to fw_config_params Peter Ujfalusi
@ 2026-09-04  7:31 ` Peter Ujfalusi
  2026-09-04  7:31 ` [PATCH 18/23] ASoC: SOF: ipc4-topology: Set FAST_MODE for host copier in compr mode Peter Ujfalusi
                   ` (5 subsequent siblings)
  22 siblings, 0 replies; 49+ messages in thread
From: Peter Ujfalusi @ 2026-09-04  7:31 UTC (permalink / raw)
  To: vkoul, perex, tiwai, lgirdwood, broonie, srinivas.kandagatla
  Cc: linux-sound, kai.vehmanen, yung-chuan.liao, pierre-louis.bossart,
	daniel.baluta, Vijendar.Mukunda

The DRAIN trigger is received by a compr device when user space wrote all
the data to the buffer and it is waiting for the decoding to be completed.

Set the pipeline state to EOS in firmware so it can expect the stream to
be stopping anytime soon.

Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
Reviewed-by: Liam Girdwood <liam.r.girdwood@intel.com>
---
 sound/soc/sof/ipc4-pcm.c | 19 +++++++++++++++----
 1 file changed, 15 insertions(+), 4 deletions(-)

diff --git a/sound/soc/sof/ipc4-pcm.c b/sound/soc/sof/ipc4-pcm.c
index 45cdd5d8fbbf..543b4b29a056 100644
--- a/sound/soc/sof/ipc4-pcm.c
+++ b/sound/soc/sof/ipc4-pcm.c
@@ -153,7 +153,8 @@ sof_ipc4_add_pipeline_to_trigger_list(struct snd_sof_dev *sdev, int state,
 	struct snd_sof_widget *pipe_widget = spipe->pipe_widget;
 	struct sof_ipc4_pipeline *pipeline = pipe_widget->private;
 
-	if (pipeline->skip_during_fe_trigger && state != SOF_IPC4_PIPE_RESET)
+	if (pipeline->skip_during_fe_trigger && state != SOF_IPC4_PIPE_RESET &&
+	    state != SOF_IPC4_PIPE_EOS)
 		return;
 
 	switch (state) {
@@ -173,7 +174,12 @@ sof_ipc4_add_pipeline_to_trigger_list(struct snd_sof_dev *sdev, int state,
 							  true);
 		break;
 	case SOF_IPC4_PIPE_PAUSED:
-		/* Pause the pipeline only when its started_count is 1 more than paused_count */
+	case SOF_IPC4_PIPE_EOS:
+		/*
+		 * Pause the pipeline only when its started_count is 1 more than
+		 * paused_count.
+		 * Same rule applies to EOS state.
+		 */
 		if (spipe->paused_count == (spipe->started_count - 1))
 			sof_ipc4_add_pipeline_by_priority(trigger_list, pipe_widget, pipe_priority,
 							  true);
@@ -499,8 +505,9 @@ static int sof_ipc4_trigger_pipelines(struct snd_soc_component *component,
 		goto free;
 	}
 
-	/* no need to pause before reset or before pause release */
-	if (state == SOF_IPC4_PIPE_RESET || cmd == SNDRV_PCM_TRIGGER_PAUSE_RELEASE)
+	/* no need to pause before reset, EOS or before pause release */
+	if (state == SOF_IPC4_PIPE_RESET || state == SOF_IPC4_PIPE_EOS ||
+	    cmd == SNDRV_PCM_TRIGGER_PAUSE_RELEASE)
 		goto skip_pause_transition;
 
 	/*
@@ -602,6 +609,10 @@ static int sof_ipc4_pcm_trigger(struct snd_soc_component *component,
 	case SNDRV_PCM_TRIGGER_STOP:
 		state = SOF_IPC4_PIPE_PAUSED;
 		break;
+	case SND_COMPR_TRIGGER_DRAIN:
+	case SND_COMPR_TRIGGER_PARTIAL_DRAIN:
+		state = SOF_IPC4_PIPE_EOS;
+		break;
 	default:
 		dev_err(component->dev, "%s: unhandled trigger cmd %d\n", __func__, cmd);
 		return -EINVAL;
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 49+ messages in thread

* [PATCH 18/23] ASoC: SOF: ipc4-topology: Set FAST_MODE for host copier in compr mode
  2026-09-04  7:31 [PATCH 00/23] ALSA compress / ASoC compress / SOF: Compressed audio support with IPC4 Peter Ujfalusi
                   ` (16 preceding siblings ...)
  2026-09-04  7:31 ` [PATCH 17/23] ASoC: SOF: ipc4-pcm: Handle COMPR DRAIN triggers as EOS pipeline state Peter Ujfalusi
@ 2026-09-04  7:31 ` Peter Ujfalusi
  2026-09-04  7:31 ` [PATCH 19/23] ASoC: SOF: Add support for IPC4 compressed Peter Ujfalusi
                   ` (4 subsequent siblings)
  22 siblings, 0 replies; 49+ messages in thread
From: Peter Ujfalusi @ 2026-09-04  7:31 UTC (permalink / raw)
  To: vkoul, perex, tiwai, lgirdwood, broonie, srinivas.kandagatla
  Cc: linux-sound, kai.vehmanen, yung-chuan.liao, pierre-louis.bossart,
	daniel.baluta, Vijendar.Mukunda

FAST_MODE allows the host DMA to work in opportunistic, free running mode,
which matches with the bitstream nature of compressed devices.

Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
Reviewed-by: Liam Girdwood <liam.r.girdwood@intel.com>
---
 sound/soc/sof/ipc4-topology.c | 3 +++
 sound/soc/sof/ipc4-topology.h | 5 +++++
 2 files changed, 8 insertions(+)

diff --git a/sound/soc/sof/ipc4-topology.c b/sound/soc/sof/ipc4-topology.c
index 42cf444e847e..566913025cc7 100644
--- a/sound/soc/sof/ipc4-topology.c
+++ b/sound/soc/sof/ipc4-topology.c
@@ -744,6 +744,9 @@ static int sof_ipc4_widget_setup_pcm(struct snd_sof_widget *swidget)
 			sps->dsp_max_burst_size_in_ms = 1;
 	}
 
+	if (spcm->pcm.compress)
+		ipc4_copier->data.copier_feature_mask |= BIT(SOF_IPC4_COPIER_FAST_MODE);
+
 skip_gtw_cfg:
 	ipc4_copier->gtw_attr = kzalloc_obj(*ipc4_copier->gtw_attr);
 	if (!ipc4_copier->gtw_attr) {
diff --git a/sound/soc/sof/ipc4-topology.h b/sound/soc/sof/ipc4-topology.h
index 5e78f3de092e..c2949ff979a5 100644
--- a/sound/soc/sof/ipc4-topology.h
+++ b/sound/soc/sof/ipc4-topology.h
@@ -222,6 +222,11 @@ struct sof_copier_gateway_cfg {
 	uint32_t config_data[];
 };
 
+/* bit definition in copier_feature_mask */
+enum sof_ipc4_copier_feature {
+	SOF_IPC4_COPIER_FAST_MODE = 0, /* free running mode of host copier */
+};
+
 /**
  * struct sof_ipc4_copier_data - IPC data for copier
  * @base_config: Base configuration including input audio format
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 49+ messages in thread

* [PATCH 19/23] ASoC: SOF: Add support for IPC4 compressed
  2026-09-04  7:31 [PATCH 00/23] ALSA compress / ASoC compress / SOF: Compressed audio support with IPC4 Peter Ujfalusi
                   ` (17 preceding siblings ...)
  2026-09-04  7:31 ` [PATCH 18/23] ASoC: SOF: ipc4-topology: Set FAST_MODE for host copier in compr mode Peter Ujfalusi
@ 2026-09-04  7:31 ` Peter Ujfalusi
  2026-09-04 20:08   ` Mark Brown
  2026-09-04  7:31 ` [PATCH 20/23] ASoC: SOF: ipc4: Handle compressed drain done notification from firmware Peter Ujfalusi
                   ` (3 subsequent siblings)
  22 siblings, 1 reply; 49+ messages in thread
From: Peter Ujfalusi @ 2026-09-04  7:31 UTC (permalink / raw)
  To: vkoul, perex, tiwai, lgirdwood, broonie, srinivas.kandagatla
  Cc: linux-sound, kai.vehmanen, yung-chuan.liao, pierre-louis.bossart,
	daniel.baluta, Vijendar.Mukunda

Set and define the compressed ops for IPC4.
The initial implementation supports basic features: PAUSE PUSH/RELEASE,
DRAIN and progress reporting.
Tested with PCM, MP3, AAC and VORBIS codec.

Co-developed-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
Signed-off-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
Reviewed-by: Liam Girdwood <liam.r.girdwood@intel.com>
---
 sound/soc/sof/Makefile        |   4 +-
 sound/soc/sof/ipc4-compress.c | 776 ++++++++++++++++++++++++++++++++++
 sound/soc/sof/ipc4-pcm.c      |   3 +
 sound/soc/sof/ipc4-priv.h     |  10 +
 sound/soc/sof/sof-audio.h     |   1 +
 5 files changed, 792 insertions(+), 2 deletions(-)
 create mode 100644 sound/soc/sof/ipc4-compress.c

diff --git a/sound/soc/sof/Makefile b/sound/soc/sof/Makefile
index 3d52f8c4d173..d5d31da5e937 100644
--- a/sound/soc/sof/Makefile
+++ b/sound/soc/sof/Makefile
@@ -8,10 +8,12 @@ snd-sof-y := core.o ops.o loader.o ipc.o pcm.o pm.o debug.o topology.o\
 ifneq ($(CONFIG_SND_SOC_SOF_IPC3),)
 snd-sof-y +=	ipc3.o ipc3-loader.o ipc3-topology.o ipc3-control.o ipc3-pcm.o\
 		ipc3-dtrace.o
+snd-sof-$(CONFIG_SND_SOC_SOF_COMPRESS) += ipc3-compress.o
 endif
 ifneq ($(CONFIG_SND_SOC_SOF_IPC4),)
 snd-sof-y += ipc4.o ipc4-loader.o ipc4-topology.o ipc4-control.o ipc4-pcm.o\
 		ipc4-mtrace.o ipc4-telemetry.o
+snd-sof-$(CONFIG_SND_SOC_SOF_COMPRESS) += ipc4-compress.o
 endif
 
 # SOF client support
@@ -19,8 +21,6 @@ ifneq ($(CONFIG_SND_SOC_SOF_CLIENT),)
 snd-sof-y += sof-client.o
 endif
 
-snd-sof-$(CONFIG_SND_SOC_SOF_COMPRESS) += ipc3-compress.o
-
 snd-sof-pci-y := sof-pci-dev.o
 snd-sof-acpi-y := sof-acpi-dev.o
 snd-sof-of-y := sof-of-dev.o
diff --git a/sound/soc/sof/ipc4-compress.c b/sound/soc/sof/ipc4-compress.c
new file mode 100644
index 000000000000..ffa987143af4
--- /dev/null
+++ b/sound/soc/sof/ipc4-compress.c
@@ -0,0 +1,776 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
+//
+// Copyright 2026 Intel Corporation. All rights reserved.
+//
+#include <sound/soc.h>
+#include <sound/sof.h>
+#include <sound/compress_driver.h>
+#include <sound/pcm_params.h>
+#include "sof-audio.h"
+#include "sof-priv.h"
+#include "sof-utils.h"
+#include "ops.h"
+#include "ipc4-priv.h"
+#include "ipc4-topology.h"
+#include "ipc4-fw-reg.h"
+
+/* Maximum processing size of the decoder/encoder is 2048 bytes */
+#define SOF_IPC4_COMPR_MAX_PROCESSING_SIZE		(SZ_2K)
+
+#define SOF_IPC4_COMPR_MIN_FRAGMENTS			3
+#define SOF_IPC4_COMPR_MAX_FRAGMENT_SIZE		(SZ_128K)
+#define SOF_IPC4_COMPR_MAX_FRAGMENTS			64
+#define SOF_IPC4_COMPR_MIN_BUFFER_SIZE(min_size)	((min_size) * \
+							 SOF_IPC4_COMPR_MIN_FRAGMENTS)
+
+struct sof_ipc4_compr_init_data {
+	struct snd_codec codec;
+	u32 dir;
+} __packed __aligned(4);
+
+static struct sof_ipc4_process *
+sof_ipc4_compr_get_module(struct snd_sof_pcm *spcm, int dir)
+{
+	int id = dir ? snd_soc_dapm_encoder : snd_soc_dapm_decoder;
+	struct snd_sof_pcm_stream *sps = &spcm->stream[dir];
+	struct snd_soc_dapm_widget *widget;
+	int i;
+
+	/* Find the (first) compr module in path */
+	for_each_dapm_widgets(sps->list, i, widget) {
+		struct snd_sof_widget *swidget = widget->dobj.private;
+
+		if (!swidget)
+			continue;
+
+		if (swidget->widget->id == id)
+			return swidget->private;
+	}
+
+	return NULL;
+}
+
+static u32 sof_ipc4_compr_calc_min_fragment_size(struct snd_sof_pcm_stream *sps)
+{
+	u32 host_buffer_estimate;
+
+	/* Estimated host DMA buffer size based on stereo S32_LE, 48KHz */
+	host_buffer_estimate = snd_pcm_format_size(SNDRV_PCM_FORMAT_S32_LE, 2 * 48);
+	host_buffer_estimate *= sps->dsp_max_burst_size_in_ms;
+	/*
+	 * The minimum fragment size must not be smaller than the processing size
+	 * or in case of deep buffer on host side, the host DMA buffer size.
+	 */
+	return max(SOF_IPC4_COMPR_MAX_PROCESSING_SIZE, host_buffer_estimate);
+}
+
+static int sof_ipc4_compr_open(struct snd_soc_component *component,
+			       struct snd_compr_stream *cstream)
+{
+	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
+	struct snd_soc_pcm_runtime *rtd = cstream->private_data;
+	struct snd_sof_pcm *spcm;
+	int dir, ret;
+
+	spcm = snd_sof_find_spcm_dai(component, rtd);
+	if (!spcm)
+		return -EINVAL;
+
+	dir = cstream->direction;
+
+	if (spcm->stream[dir].cstream)
+		return -EBUSY;
+
+	spcm_dbg(spcm, dir, "Entry: open\n");
+
+	ret = snd_sof_compr_platform_open(sdev, cstream);
+	if (ret < 0) {
+		spcm_err(spcm, dir, "platform compress open failed %d\n", ret);
+		return ret;
+	}
+
+	spcm->stream[dir].cstream = cstream;
+	spcm->stream[dir].posn.host_posn = 0;
+	spcm->stream[dir].posn.dai_posn = 0;
+	spcm->prepared[dir] = false;
+	spcm->pending_stop[dir] = false;
+
+	return 0;
+}
+
+static int sof_ipc4_compr_stream_free(struct snd_sof_dev *sdev,
+				      struct snd_sof_pcm *spcm,
+				      struct snd_compr_stream *cstream)
+{
+	const struct sof_ipc_pcm_ops *pcm_ops = sof_ipc_get_ops(sdev, pcm);
+	int dir = cstream->direction;
+	int ret = 0;
+	int err = 0;
+
+	if (spcm->prepared[dir]) {
+		if (spcm->pending_stop[dir])
+			pcm_ops->trigger(sdev->component, NULL, spcm,
+					 SNDRV_PCM_TRIGGER_STOP, dir);
+
+		snd_sof_compr_platform_trigger(sdev, cstream,
+					       SNDRV_PCM_TRIGGER_STOP);
+
+		err = pcm_ops->hw_free(sdev->component, NULL, spcm, dir);
+		if (err < 0)
+			spcm_err(spcm, dir, "pcm_ops->hw_free failed %d\n", err);
+	}
+
+	spcm->prepared[dir] = false;
+	spcm->pending_stop[dir] = false;
+	spcm->stream[dir].cstream = NULL;
+
+	/* reset the DMA */
+	ret = snd_sof_compr_platform_hw_free(sdev, cstream);
+	if (ret < 0) {
+		spcm_err(spcm, dir, "platform hw free failed %d\n", ret);
+		if (!err)
+			err = ret;
+	}
+
+	/* free widget list */
+	ret = sof_widget_list_free(sdev, spcm, dir);
+	if (ret < 0 && err == 0) {
+		spcm_err(spcm, dir, "sof_widget_list_free failed %d\n", ret);
+		err = ret;
+	}
+
+	return err;
+}
+
+static int sof_ipc4_compr_free(struct snd_soc_component *component,
+			       struct snd_compr_stream *cstream)
+{
+	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
+	struct snd_soc_pcm_runtime *rtd = cstream->private_data;
+	struct snd_sof_pcm *spcm;
+	int ret, err;
+
+	spcm = snd_sof_find_spcm_dai(component, rtd);
+	if (!spcm)
+		return -EINVAL;
+
+	spcm_dbg(spcm, cstream->direction, "Entry: free\n");
+
+	ret = sof_ipc4_compr_stream_free(sdev, spcm, cstream);
+
+	/* unprepare and free the list of DAPM widgets */
+	sof_widget_list_unprepare(sdev, spcm, cstream->direction);
+
+	cancel_work_sync(&spcm->stream[cstream->direction].period_elapsed_work);
+
+	snd_compr_free_pages(cstream);
+
+	err = snd_sof_compr_platform_close(sdev, cstream);
+	if (err < 0) {
+		spcm_err(spcm, cstream->direction,
+			 "platform compress close failed %d\n", err);
+		if (!ret)
+			ret = err;
+	}
+
+	return ret;
+}
+
+#define SOF_IPC4_CODEC_INFO_GET_ID(value)	((value) & 0xff)
+#define SOF_IPC4_CODEC_INFO_GET_DIR(value)	(((value) >> 8) & 0xf)
+
+struct sof_ipc4_codec_info_data {
+	u32 count;
+	u32 items[];
+} __packed __aligned(4);
+
+static int sof_ipc4_compr_get_caps(struct snd_soc_component *component,
+				   struct snd_compr_stream *cstream,
+				   struct snd_compr_caps *caps)
+{
+	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
+	struct snd_soc_pcm_runtime *rtd = cstream->private_data;
+	struct sof_ipc4_fw_data *ipc4_data = sdev->private;
+	struct sof_ipc4_codec_info_data *codec_info = ipc4_data->codec_info;
+	int dir = cstream->direction;
+	struct snd_sof_pcm *spcm;
+	int i;
+
+	spcm = snd_sof_find_spcm_dai(component, rtd);
+	if (!spcm)
+		return -EINVAL;
+
+	/* No compress support available in booted firmware */
+	if (!codec_info || !codec_info->count) {
+		spcm_err(spcm, dir,
+			 "Compress is not supported (no codecs available)\n");
+		return -EINVAL;
+	}
+
+	for (i = 0; i < codec_info->count; i++) {
+		int _dir = SOF_IPC4_CODEC_INFO_GET_DIR(codec_info->items[i]);
+
+		if (_dir == dir) {
+			int id = SOF_IPC4_CODEC_INFO_GET_ID(codec_info->items[i]);
+
+			if (caps->num_codecs < ARRAY_SIZE(caps->codecs)) {
+				spcm_dbg(spcm, dir, "codec#%d: %d\n",
+					 caps->num_codecs, id);
+				caps->codecs[caps->num_codecs++] = id;
+			} else {
+				spcm_dbg(spcm, dir, "codec#%d: %d ignored\n",
+					 caps->num_codecs, id);
+			}
+		}
+	}
+
+	caps->direction = dir;
+	caps->min_fragment_size =
+		sof_ipc4_compr_calc_min_fragment_size(&spcm->stream[dir]);
+	caps->max_fragment_size = SOF_IPC4_COMPR_MAX_FRAGMENT_SIZE;
+	if (caps->max_fragment_size < caps->min_fragment_size)
+		caps->max_fragment_size = caps->min_fragment_size;
+
+	caps->min_fragments = SOF_IPC4_COMPR_MIN_FRAGMENTS;
+	caps->max_fragments = SOF_IPC4_COMPR_MAX_FRAGMENTS;
+
+	spcm_dbg(spcm, dir,
+		 "num_codecs: %u, fragment_size: %u-%u, fragments: %u-%u\n",
+		 caps->num_codecs,
+		 caps->min_fragment_size, caps->max_fragment_size,
+		 caps->min_fragments, caps->max_fragments);
+	return 0;
+}
+
+static int sof_ipc4_compr_alloc_pages(struct device *dev,
+				      struct snd_sof_pcm_stream *sps,
+				      struct snd_soc_component *component,
+				      struct snd_compr_stream *cstream)
+{
+	u32 min_fragment_size = sof_ipc4_compr_calc_min_fragment_size(sps);
+	struct snd_compr_runtime *crtd = cstream->runtime;
+	u64 fragments = crtd->buffer_size;
+	int ret;
+
+	if (crtd->buffer_size < SOF_IPC4_COMPR_MIN_BUFFER_SIZE(min_fragment_size)) {
+		dev_err(dev, "%s: Buffer size is too small: %llu (minimum is %u)\n",
+			__func__, crtd->buffer_size,
+			SOF_IPC4_COMPR_MIN_BUFFER_SIZE(min_fragment_size));
+		return -EINVAL;
+	}
+
+	if (crtd->fragment_size < min_fragment_size) {
+		dev_err(dev, "%s: Fragment size is too small: %u (minimum is %u)\n",
+			__func__, crtd->fragment_size, min_fragment_size);
+		return -EINVAL;
+	}
+
+	do_div(fragments, crtd->fragment_size);
+	if (fragments < SOF_IPC4_COMPR_MIN_FRAGMENTS ||
+	    fragments > SOF_IPC4_COMPR_MAX_FRAGMENTS) {
+		dev_err(dev,
+			"%s: Incorrect number of fragments: %llu (valid: %d-%d)\n",
+			__func__, fragments, SOF_IPC4_COMPR_MIN_FRAGMENTS,
+			SOF_IPC4_COMPR_MAX_FRAGMENTS);
+		return -EINVAL;
+	}
+
+	cstream->dma_buffer.dev.type = SNDRV_DMA_TYPE_DEV_SG;
+	cstream->dma_buffer.dev.dev = dev;
+
+	ret = snd_compr_malloc_pages(cstream, crtd->buffer_size);
+	if (ret < 0)
+		return ret;
+
+	ret = snd_sof_compr_create_page_table(component, cstream, crtd->dma_area,
+					      crtd->dma_bytes);
+	if (ret < 0)
+		snd_compr_free_pages(cstream);
+
+	return ret;
+}
+
+static bool
+sof_ipc4_compr_codec_supported(struct snd_sof_dev *sdev, int codec_id, int dir)
+{
+	struct sof_ipc4_fw_data *ipc4_data = sdev->private;
+	struct sof_ipc4_codec_info_data *codec_info = ipc4_data->codec_info;
+	int i;
+
+	/* No compress support available in booted firmware */
+	if (!codec_info || !codec_info->count)
+		return false;
+
+	for (i = 0; i < codec_info->count; i++) {
+		int _dir = SOF_IPC4_CODEC_INFO_GET_DIR(codec_info->items[i]);
+		int _id = SOF_IPC4_CODEC_INFO_GET_ID(codec_info->items[i]);
+
+		if (_dir == dir && codec_id == _id)
+			return true;
+	}
+
+	return false;
+}
+
+static int sof_ipc4_compr_set_params(struct snd_soc_component *component,
+				     struct snd_compr_stream *cstream,
+				     struct snd_compr_params *params)
+{
+	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
+	const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
+	struct sof_ipc4_compr_init_data *compr_data __free(kfree) = NULL;
+	struct snd_soc_pcm_runtime *rtd = cstream->private_data;
+	struct snd_sof_platform_stream_params *platform_params;
+	struct sof_ipc4_timestamp_info *time_info;
+	struct snd_compr_params *compr_params;
+	struct snd_sof_widget *host_swidget;
+	struct sof_ipc4_process *process;
+	struct snd_pcm_hw_params p = {0};
+	struct snd_interval *interval;
+	struct snd_sof_pcm *spcm;
+	struct snd_mask *fmt;
+	int dir = cstream->direction;
+	int ret;
+
+	spcm = snd_sof_find_spcm_dai(component, rtd);
+	if (!spcm)
+		return -EINVAL;
+
+	host_swidget = snd_sof_find_swidget_by_comp_id(sdev, spcm->stream[dir].comp_id);
+	if (!host_swidget) {
+		spcm_err(spcm, dir, "failed to find host widget with comp_id %d\n",
+			 spcm->stream[dir].comp_id);
+		return -ENODEV;
+	}
+
+	if (!sof_ipc4_compr_codec_supported(sdev, params->codec.id, dir)) {
+		spcm_err(spcm, dir, "Unsupported codec id: %u\n", params->codec.id);
+		return -EINVAL;
+	}
+
+	spcm_dbg(spcm, dir,
+		 "codec_id: %u, rate: %u, ch in/out: %u/%u, format: %u/%u\n",
+		 params->codec.id, params->codec.sample_rate, params->codec.ch_in,
+		 params->codec.ch_out, params->codec.format, params->codec.pcm_format);
+
+	if (spcm->prepared[dir]) {
+		/*
+		 * This can only happen if user space re-configures the device
+		 * without closing it, for example after DRAIN completion
+		 */
+		ret = sof_ipc4_compr_stream_free(sdev, spcm, cstream);
+		if (ret)
+			return ret;
+	}
+
+	/* save the compress params */
+	compr_params = &spcm->cparams[dir];
+	memcpy(compr_params, params, sizeof(*params));
+
+	/*
+	 * Force format, rate and channels and use PCM hw_params structure to
+	 * set up the pipelines.
+	 */
+	fmt = hw_param_mask(&p, SNDRV_PCM_HW_PARAM_FORMAT);
+	snd_mask_none(fmt);
+	/* Use correct format based on the used codec */
+	switch (params->codec.id) {
+	case SND_AUDIOCODEC_PCM:
+		snd_mask_set_format(fmt, (snd_pcm_format_t)params->codec.format);
+		break;
+	case SND_AUDIOCODEC_VORBIS:
+		snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S16_LE);
+		break;
+	case SND_AUDIOCODEC_FLAC:
+	{
+		struct snd_dec_flac *dec_flac = &params->codec.options.flac_d;
+
+		if (dec_flac->sample_size == 16)
+			snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S16_LE);
+		else
+			snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S32_LE);
+		break;
+	}
+	default:
+		snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S32_LE);
+	}
+
+	interval = hw_param_interval(&p, SNDRV_PCM_HW_PARAM_CHANNELS);
+	interval->min = compr_params->codec.ch_out;
+	interval->max = compr_params->codec.ch_out;
+
+	interval = hw_param_interval(&p, SNDRV_PCM_HW_PARAM_RATE);
+	interval->min = compr_params->codec.sample_rate;
+	interval->max = compr_params->codec.sample_rate;
+
+	ret = sof_ipc4_compr_alloc_pages(sdev->dev, &spcm->stream[dir],
+					 component, cstream);
+	if (ret < 0)
+		return ret;
+
+	spcm_dbg(spcm, dir,
+		 "buffer_size: %llu, fragment_size: %u (fragments: %u)\n",
+		 cstream->runtime->buffer_size, cstream->runtime->fragment_size,
+		 (u32)cstream->runtime->buffer_size / cstream->runtime->fragment_size);
+
+	interval = hw_param_interval(&p, SNDRV_PCM_HW_PARAM_PERIOD_BYTES);
+	interval->min = cstream->runtime->fragment_size;
+	interval->max = cstream->runtime->fragment_size;
+
+	interval = hw_param_interval(&p, SNDRV_PCM_HW_PARAM_BUFFER_BYTES);
+	interval->min = cstream->runtime->buffer_size;
+	interval->max = cstream->runtime->buffer_size;
+
+	platform_params = &spcm->platform_params[dir];
+	ret = snd_sof_compr_platform_hw_params(sdev, cstream, compr_params,
+					       platform_params);
+	if (ret < 0) {
+		spcm_err(spcm, dir, "platform compress hw params failed\n");
+		goto free_pages;
+	}
+
+	/* set up the list of DAPM widgets if not already done */
+	if (!spcm->stream[dir].list) {
+		ret = sof_pcm_setup_connected_widgets(sdev, rtd, spcm, &p,
+						      platform_params, dir);
+		if (ret < 0)
+			goto free_pages;
+	}
+
+	process = sof_ipc4_compr_get_module(spcm, dir);
+	if (!process) {
+		ret = -EINVAL;
+		goto free_list;
+	}
+
+	compr_data = kzalloc(sizeof(*compr_data), GFP_KERNEL);
+	if (!compr_data) {
+		ret = -ENOMEM;
+		goto free_list;
+	}
+
+	memcpy(&compr_data->codec, &compr_params->codec, sizeof(compr_data->codec));
+	compr_data->dir = dir;
+
+	process->init_ext_module_data = compr_data;
+	process->init_ext_module_size = sizeof(*compr_data);
+
+	/*
+	 * Make sure that the DSP is booted up, which might not be the
+	 * case if the on-demand DSP boot is used
+	 */
+	ret = snd_sof_boot_dsp_firmware(sdev);
+	if (ret)
+		goto clear_init_ext;
+
+	/* set the host DMA ID */
+	if (tplg_ops && tplg_ops->host_config)
+		tplg_ops->host_config(sdev, host_swidget, platform_params);
+
+	/* set up the widgets and pipelines in the DSP */
+	ret = sof_widget_list_setup(sdev, spcm, &p, platform_params, dir);
+	if (ret < 0) {
+		spcm_err(spcm, dir, "widget list set up failed\n");
+		goto clear_init_ext;
+	}
+
+	memcpy(&spcm->params[dir], &p, sizeof(p));
+	spcm->prepared[dir] = true;
+
+	time_info = sof_ipc4_sps_to_time_info(&spcm->stream[dir]);
+	if (time_info) {
+		/* delay calculation supported */
+		time_info->stream_start_offset = SOF_IPC4_INVALID_STREAM_POSITION;
+		time_info->llp_offset = 0;
+
+		sof_ipc4_build_time_info(sdev, &spcm->stream[dir]);
+	}
+
+	process->init_ext_module_data = NULL;
+	process->init_ext_module_size = 0;
+
+	return 0;
+
+clear_init_ext:
+	process->init_ext_module_data = NULL;
+	process->init_ext_module_size = 0;
+
+free_list:
+	sof_widget_list_unprepare(sdev, spcm, dir);
+
+free_pages:
+	snd_compr_free_pages(cstream);
+
+	return ret;
+}
+
+static int sof_ipc4_compr_get_params(struct snd_soc_component *component,
+				     struct snd_compr_stream *cstream,
+				     struct snd_codec *params)
+{
+	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
+	struct snd_soc_pcm_runtime *rtd = cstream->private_data;
+	struct snd_sof_pcm *spcm;
+	/* TODO: we don't query the supported codecs for now, if the
+	 * application asks for an unsupported codec the set_params() will fail.
+	 */
+	spcm = snd_sof_find_spcm_dai(component, rtd);
+	if (!spcm) {
+		dev_err(sdev->dev, "%s: can't find spcm\n", __func__);
+		return -EINVAL;
+	}
+
+	spcm_dbg(spcm, cstream->direction, "Entry: get_params\n");
+
+	memcpy(params, &spcm->cparams[cstream->direction].codec,
+	       sizeof(*params));
+
+	return 0;
+}
+
+static int sof_ipc4_compr_trigger(struct snd_soc_component *component,
+				  struct snd_compr_stream *cstream, int cmd)
+{
+	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
+	struct snd_soc_pcm_runtime *rtd = cstream->private_data;
+	const struct sof_ipc_pcm_ops *pcm_ops = sof_ipc_get_ops(sdev, pcm);
+	struct snd_sof_pcm *spcm;
+	int dir = cstream->direction;
+	bool trigger_platform = false;
+	int ret = 0;
+
+	spcm = snd_sof_find_spcm_dai(component, rtd);
+	if (!spcm) {
+		dev_err(sdev->dev, "%s: can't find spcm\n", __func__);
+		return -EINVAL;
+	}
+
+	spcm->pending_stop[dir] = false;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		trigger_platform = true;
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		break;
+	case SND_COMPR_TRIGGER_DRAIN:
+	case SND_COMPR_TRIGGER_PARTIAL_DRAIN:
+		spcm->pending_stop[dir] = true;
+		break;
+	case SND_COMPR_TRIGGER_NEXT_TRACK:
+		spcm_dbg(spcm, dir, "Unsupported trigger cmd: %d\n", cmd);
+		return -EOPNOTSUPP;
+	default:
+		spcm_dbg(spcm, dir, "Unhandled trigger cmd: %d\n", cmd);
+		return 0;
+	}
+
+	spcm_dbg(spcm, dir, "Entry: trigger (cmd: %d)\n", cmd);
+
+	ret = pcm_ops->trigger(component, NULL, spcm, cmd, dir);
+	if (ret < 0) {
+		spcm_err(spcm, dir, "pcm_ops->trigger failed for cmd %d\n", cmd);
+		return ret;
+	}
+
+	if (!ret && trigger_platform) {
+		ret = snd_sof_compr_platform_trigger(sdev, cstream, cmd);
+		if (ret < 0)
+			spcm_err(spcm, dir,
+				 "platform compress trigger start failed %d\n",
+				 ret);
+	}
+
+	return ret;
+}
+
+static int sof_ipc4_compr_copy_playback(struct snd_soc_component *component,
+					struct snd_compr_stream *cstream,
+					char __user *buf, size_t count)
+{
+	struct snd_compr_runtime *crtd = cstream->runtime;
+	u64 offset, n;
+	void *ptr;
+	int ret;
+
+	div64_u64_rem(crtd->total_bytes_available, crtd->buffer_size, &offset);
+	ptr = crtd->dma_area + offset;
+	n = crtd->buffer_size - offset;
+
+	if (count < n) {
+		ret = copy_from_user(ptr, buf, count);
+	} else {
+		ret = copy_from_user(ptr, buf, n);
+		ret += copy_from_user(crtd->dma_area, buf + n, count - n);
+	}
+
+	return count - ret;
+}
+
+static int sof_ipc4_compr_copy_capture(struct snd_compr_runtime *crtd,
+				       char __user *buf, size_t count)
+{
+	u64 offset, n;
+	void *ptr;
+	int ret;
+
+	div64_u64_rem(crtd->total_bytes_transferred, crtd->buffer_size, &offset);
+	ptr = crtd->dma_area + offset;
+	n = crtd->buffer_size - offset;
+
+	if (count < n) {
+		ret = copy_to_user(buf, ptr, count);
+	} else {
+		ret = copy_to_user(buf, ptr, n);
+		ret += copy_to_user(buf + n, crtd->dma_area, count - n);
+	}
+
+	return count - ret;
+}
+
+static int sof_ipc4_compr_copy(struct snd_soc_component *component,
+			       struct snd_compr_stream *cstream,
+			       char __user *buf, size_t count)
+{
+	struct snd_compr_runtime *crtd = cstream->runtime;
+
+	if (count > crtd->buffer_size)
+		count = crtd->buffer_size;
+
+	if (cstream->direction == SND_COMPRESS_PLAYBACK)
+		return sof_ipc4_compr_copy_playback(component, cstream, buf, count);
+
+	return sof_ipc4_compr_copy_capture(crtd, buf, count);
+}
+
+static int sof_ipc4_compr_pointer(struct snd_soc_component *component,
+				  struct snd_compr_stream *cstream,
+				  struct snd_compr_tstamp64 *tstamp)
+{
+	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
+	struct snd_soc_pcm_runtime *rtd = cstream->private_data;
+	struct sof_ipc4_timestamp_info *time_info;
+	struct snd_pcm_hw_params *params;
+	struct snd_sof_pcm_stream *sps;
+	struct snd_sof_pcm *spcm;
+	u64 dai_cnt = 0;
+	int ret;
+
+	spcm = snd_sof_find_spcm_dai(component, rtd);
+	if (!spcm)
+		return -EINVAL;
+
+	params = &spcm->params[cstream->direction];
+
+	ret = snd_sof_compr_platform_pointer(sdev, cstream, tstamp);
+	if (ret < 0) {
+		spcm_err(spcm, cstream->direction,
+			 "platform compress pointer failed %d\n", ret);
+		return ret;
+	}
+
+	sps = &spcm->stream[cstream->direction];
+	time_info = sof_ipc4_sps_to_time_info(sps);
+	if (!time_info)
+		goto host_only;
+
+	/*
+	 * stream_start_offset is updated to memory window by FW based on
+	 * pipeline statistics and it may be invalid if host query happens before
+	 * the statistics is complete. And it will not change after the first
+	 * initialization.
+	 */
+	if (time_info->stream_start_offset == SOF_IPC4_INVALID_STREAM_POSITION) {
+		ret = sof_ipc4_get_stream_start_offset(sdev, NULL, sps, time_info);
+		if (ret < 0)
+			goto host_only;
+	}
+
+	if (!time_info->llp_offset) {
+		dai_cnt = snd_sof_compr_get_dai_frame_counter(sdev, cstream);
+	} else {
+		struct sof_ipc4_llp_reading_slot llp;
+
+		sof_mailbox_read(sdev, time_info->llp_offset, &llp, sizeof(llp));
+		dai_cnt = ((u64)llp.reading.llp_u << 32) | llp.reading.llp_l;
+	}
+
+	if (dai_cnt) {
+		dai_cnt = sof_ipc4_frames_dai_to_host(time_info, dai_cnt);
+		dai_cnt += time_info->stream_end_offset;
+		if (dai_cnt < time_info->stream_start_offset)
+			dai_cnt = 0;
+		else
+			dai_cnt -= time_info->stream_start_offset;
+	}
+
+host_only:
+	tstamp->sampling_rate = params_rate(params);
+	tstamp->pcm_io_frames = dai_cnt;
+
+	return 0;
+}
+
+void sof_ipc4_compr_drain_done(struct snd_sof_dev *sdev, void *ipc_message)
+{
+	struct sof_ipc4_msg *ipc4_msg = ipc_message;
+	struct sof_ipc4_notify_module_data *ndata = ipc4_msg->data_ptr;
+	struct snd_sof_widget *swidget, *host_swidget;
+	bool widget_found = false;
+	struct snd_sof_pcm *spcm;
+	int dir;
+
+	/* Find the swidget based on ndata->module_id and ndata->instance_id */
+	swidget = sof_ipc4_find_swidget_by_ids(sdev, ndata->module_id,
+					       ndata->instance_id);
+	if (!swidget) {
+		dev_err(sdev->dev, "%s: Failed to find widget for module %u.%u\n",
+			__func__, ndata->module_id, ndata->instance_id);
+		return;
+	}
+
+	if (!swidget->spipe || !swidget->spipe->pipe_widget)
+		return;
+
+	/* Find the swidget of the host copier on the same pipeline */
+	list_for_each_entry(host_swidget, &sdev->widget_list, list) {
+		if (WIDGET_IS_AIF(host_swidget->id) &&
+		    host_swidget->pipeline_id == swidget->pipeline_id) {
+			widget_found = true;
+			break;
+		}
+	}
+
+	if (!widget_found) {
+		dev_err(sdev->dev, "%s: Host widget not found for pipeline: %s\n",
+			__func__, swidget->spipe->pipe_widget->widget->name);
+		return;
+	}
+
+	/* Look up the spcm of the host copier */
+	spcm = snd_sof_find_spcm_comp(sdev->component, host_swidget->comp_id, &dir);
+	if (!spcm) {
+		dev_err(sdev->dev, "%s: Stream cannot be found for %s\n", __func__,
+			host_swidget->widget->name);
+		return;
+	}
+
+	spcm_dbg(spcm, dir, "Entry: EOS done\n");
+
+	if (spcm->stream[dir].cstream)
+		snd_compr_drain_notify(spcm->stream[dir].cstream);
+}
+
+const struct snd_compress_ops sof_ipc4_compressed_ops = {
+	.open		= sof_ipc4_compr_open,
+	.free		= sof_ipc4_compr_free,
+	.get_caps	= sof_ipc4_compr_get_caps,
+	.set_params	= sof_ipc4_compr_set_params,
+	.get_params	= sof_ipc4_compr_get_params,
+	.trigger	= sof_ipc4_compr_trigger,
+	.pointer	= sof_ipc4_compr_pointer,
+	.copy		= sof_ipc4_compr_copy,
+};
diff --git a/sound/soc/sof/ipc4-pcm.c b/sound/soc/sof/ipc4-pcm.c
index 543b4b29a056..5d8fc9b25bcc 100644
--- a/sound/soc/sof/ipc4-pcm.c
+++ b/sound/soc/sof/ipc4-pcm.c
@@ -1336,4 +1336,7 @@ const struct sof_ipc_pcm_ops ipc4_pcm_ops = {
 	.delay = sof_ipc4_pcm_delay,
 	.ipc_first_on_start = true,
 	.platform_stop_during_hw_free = true,
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_COMPRESS)
+	.compress_ops =	&sof_ipc4_compressed_ops,
+#endif
 };
diff --git a/sound/soc/sof/ipc4-priv.h b/sound/soc/sof/ipc4-priv.h
index 07df6799c7a8..b35930c16c6d 100644
--- a/sound/soc/sof/ipc4-priv.h
+++ b/sound/soc/sof/ipc4-priv.h
@@ -130,6 +130,9 @@ extern const struct sof_ipc_tplg_ops ipc4_tplg_ops;
 extern const struct sof_ipc_tplg_control_ops tplg_ipc4_control_ops;
 extern const struct sof_ipc_pcm_ops ipc4_pcm_ops;
 extern const struct sof_ipc_fw_tracing_ops ipc4_mtrace_ops;
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_COMPRESS)
+extern const struct snd_compress_ops sof_ipc4_compressed_ops;
+#endif
 
 int sof_ipc4_set_pipeline_state(struct snd_sof_dev *sdev, u32 instance_id, u32 state);
 int sof_ipc4_mtrace_update_pos(struct snd_sof_dev *sdev, int core);
@@ -164,4 +167,11 @@ int sof_ipc4_get_stream_start_offset(struct snd_sof_dev *sdev,
 				     struct sof_ipc4_timestamp_info *time_info);
 u64 sof_ipc4_frames_dai_to_host(struct sof_ipc4_timestamp_info *time_info, u64 value);
 
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_COMPRESS)
+void sof_ipc4_compr_drain_done(struct snd_sof_dev *sdev, void *ipc_message);
+#else
+static inline void sof_ipc4_compr_drain_done(struct snd_sof_dev *sdev,
+					     void *ipc_message) { }
+#endif
+
 #endif
diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h
index 151cfa9e6b7c..9dff4fea628e 100644
--- a/sound/soc/sof/sof-audio.h
+++ b/sound/soc/sof/sof-audio.h
@@ -358,6 +358,7 @@ struct snd_sof_pcm {
 	struct snd_sof_pcm_stream stream[2];
 	struct list_head list;	/* list in sdev pcm list */
 	struct snd_pcm_hw_params params[2];
+	struct snd_compr_params cparams[2]; /* applicable for compress devices */
 	struct snd_sof_platform_stream_params platform_params[2];
 	bool prepared[2]; /* PCM_PARAMS set successfully */
 	bool setup_done[2]; /* the setup of the SOF PCM device is done */
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 49+ messages in thread

* [PATCH 20/23] ASoC: SOF: ipc4: Handle compressed drain done notification from firmware
  2026-09-04  7:31 [PATCH 00/23] ALSA compress / ASoC compress / SOF: Compressed audio support with IPC4 Peter Ujfalusi
                   ` (18 preceding siblings ...)
  2026-09-04  7:31 ` [PATCH 19/23] ASoC: SOF: Add support for IPC4 compressed Peter Ujfalusi
@ 2026-09-04  7:31 ` Peter Ujfalusi
  2026-09-04  7:31 ` [PATCH 21/23] ASoC: SOF: Intel: Kconfig: Remove redundant IPC version selects Peter Ujfalusi
                   ` (2 subsequent siblings)
  22 siblings, 0 replies; 49+ messages in thread
From: Peter Ujfalusi @ 2026-09-04  7:31 UTC (permalink / raw)
  To: vkoul, perex, tiwai, lgirdwood, broonie, srinivas.kandagatla
  Cc: linux-sound, kai.vehmanen, yung-chuan.liao, pierre-louis.bossart,
	daniel.baluta, Vijendar.Mukunda

The decoder module sends a drain done notification when the last chunk of
the stream after the EOS from host has been decoded.

The notification is a module notification with 0xc0c0 as magic number in
event_id upper 16 bit.

Call sof_ipc4_compr_drain_done() when the notification arrives to handle
it.

Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
Reviewed-by: Liam Girdwood <liam.r.girdwood@intel.com>
---
 include/sound/sof/ipc4/header.h |  3 ++-
 sound/soc/sof/ipc4.c            | 13 +++++++++++--
 2 files changed, 13 insertions(+), 3 deletions(-)

diff --git a/include/sound/sof/ipc4/header.h b/include/sound/sof/ipc4/header.h
index dd1e0d1cf0d8..c0216f4b14c2 100644
--- a/include/sound/sof/ipc4/header.h
+++ b/include/sound/sof/ipc4/header.h
@@ -647,9 +647,10 @@ struct sof_ipc4_notify_module_data {
  * The event_data contains the struct sof_ipc4_control_msg_payload of the control
  * which sent the notification.
  */
-#define SOF_IPC4_NOTIFY_MODULE_EVENTID_ALSA_MAGIC_MASK		GENMASK(31, 16)
+#define SOF_IPC4_NOTIFY_MODULE_EVENTID_SOF_MAGIC_MASK		GENMASK(31, 16)
 #define SOF_IPC4_NOTIFY_MODULE_EVENTID_ALSA_MAGIC_VAL		0xA15A0000
 #define SOF_IPC4_NOTIFY_MODULE_EVENTID_ALSA_PARAMID_MASK	GENMASK(15, 0)
+#define SOF_IPC4_NOTIFY_MODULE_EVENTID_COMPR_MAGIC_VAL		0xC0C00000
 
 /*
  * Macros for creating struct sof_ipc4_module_init_ext_init payload
diff --git a/sound/soc/sof/ipc4.c b/sound/soc/sof/ipc4.c
index e77f73390bd8..b9dd798e59bc 100644
--- a/sound/soc/sof/ipc4.c
+++ b/sound/soc/sof/ipc4.c
@@ -776,12 +776,21 @@ static void sof_ipc4_module_notification_handler(struct snd_sof_dev *sdev,
 	}
 
 	/* Handle ALSA kcontrol notification */
-	if ((data->event_id & SOF_IPC4_NOTIFY_MODULE_EVENTID_ALSA_MAGIC_MASK) ==
-	    SOF_IPC4_NOTIFY_MODULE_EVENTID_ALSA_MAGIC_VAL) {
+	switch (data->event_id & SOF_IPC4_NOTIFY_MODULE_EVENTID_SOF_MAGIC_MASK) {
+	case SOF_IPC4_NOTIFY_MODULE_EVENTID_ALSA_MAGIC_VAL:
+	{
 		const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg;
 
 		if (tplg_ops->control->update)
 			tplg_ops->control->update(sdev, ipc4_msg);
+
+		break;
+	}
+	case SOF_IPC4_NOTIFY_MODULE_EVENTID_COMPR_MAGIC_VAL:
+		sof_ipc4_compr_drain_done(sdev, ipc4_msg);
+		break;
+	default:
+		break;
 	}
 }
 
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 49+ messages in thread

* [PATCH 21/23] ASoC: SOF: Intel: Kconfig: Remove redundant IPC version selects
  2026-09-04  7:31 [PATCH 00/23] ALSA compress / ASoC compress / SOF: Compressed audio support with IPC4 Peter Ujfalusi
                   ` (19 preceding siblings ...)
  2026-09-04  7:31 ` [PATCH 20/23] ASoC: SOF: ipc4: Handle compressed drain done notification from firmware Peter Ujfalusi
@ 2026-09-04  7:31 ` Peter Ujfalusi
  2026-09-04  7:31 ` [PATCH 22/23] ASoC: SOF: Intel: Kconfig: Select compress support for TGL+ platforms Peter Ujfalusi
  2026-09-04  7:31 ` [PATCH 23/23] ASoC: SOF: topology: Add support for decoder and encoder widgets Peter Ujfalusi
  22 siblings, 0 replies; 49+ messages in thread
From: Peter Ujfalusi @ 2026-09-04  7:31 UTC (permalink / raw)
  To: vkoul, perex, tiwai, lgirdwood, broonie, srinivas.kandagatla
  Cc: linux-sound, kai.vehmanen, yung-chuan.liao, pierre-louis.bossart,
	daniel.baluta, Vijendar.Mukunda

There is no need to select IPC3 and IPC4 along with INTEL_CNL as INTEL_CNL
selects both.

Similarly, INTEL_MTL selects IPC4, so there is no need to do that for LNL,
PTL and NVL.

Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
Reviewed-by: Liam Girdwood <liam.r.girdwood@intel.com>
---
 sound/soc/sof/intel/Kconfig | 7 -------
 1 file changed, 7 deletions(-)

diff --git a/sound/soc/sof/intel/Kconfig b/sound/soc/sof/intel/Kconfig
index 915abbef398d..840d5a36b958 100644
--- a/sound/soc/sof/intel/Kconfig
+++ b/sound/soc/sof/intel/Kconfig
@@ -186,8 +186,6 @@ config SND_SOC_SOF_INTEL_ICL
 	tristate
 	select SND_SOC_SOF_HDA_GENERIC
 	select SND_SOC_SOF_INTEL_SOUNDWIRE_LINK_BASELINE
-	select SND_SOC_SOF_IPC3
-	select SND_SOC_SOF_IPC4
 	select SND_SOC_SOF_INTEL_CNL
 
 config SND_SOC_SOF_ICELAKE
@@ -214,8 +212,6 @@ config SND_SOC_SOF_INTEL_TGL
 	tristate
 	select SND_SOC_SOF_HDA_GENERIC
 	select SND_SOC_SOF_INTEL_SOUNDWIRE_LINK_BASELINE
-	select SND_SOC_SOF_IPC3
-	select SND_SOC_SOF_IPC4
 	select SND_SOC_SOF_INTEL_CNL
 
 config SND_SOC_SOF_TIGERLAKE
@@ -268,7 +264,6 @@ config SND_SOC_SOF_INTEL_LNL
 	tristate
 	select SND_SOC_SOF_HDA_GENERIC
 	select SND_SOC_SOF_INTEL_SOUNDWIRE_LINK_BASELINE
-	select SND_SOC_SOF_IPC4
 	select SND_SOC_SOF_INTEL_MTL
 
 config SND_SOC_SOF_LUNARLAKE
@@ -285,7 +280,6 @@ config SND_SOC_SOF_INTEL_PTL
 	tristate
 	select SND_SOC_SOF_HDA_COMMON
 	select SND_SOC_SOF_INTEL_SOUNDWIRE_LINK_BASELINE
-	select SND_SOC_SOF_IPC4
 	select SND_SOC_SOF_INTEL_LNL
 
 config SND_SOC_SOF_PANTHERLAKE
@@ -302,7 +296,6 @@ config SND_SOC_SOF_INTEL_NVL
 	tristate
 	select SND_SOC_SOF_HDA_COMMON
 	select SND_SOC_SOF_INTEL_SOUNDWIRE_LINK_BASELINE
-	select SND_SOC_SOF_IPC4
 	select SND_SOC_SOF_INTEL_PTL
 
 config SND_SOC_SOF_NOVALAKE
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 49+ messages in thread

* [PATCH 22/23] ASoC: SOF: Intel: Kconfig: Select compress support for TGL+ platforms
  2026-09-04  7:31 [PATCH 00/23] ALSA compress / ASoC compress / SOF: Compressed audio support with IPC4 Peter Ujfalusi
                   ` (20 preceding siblings ...)
  2026-09-04  7:31 ` [PATCH 21/23] ASoC: SOF: Intel: Kconfig: Remove redundant IPC version selects Peter Ujfalusi
@ 2026-09-04  7:31 ` Peter Ujfalusi
  2026-09-04  7:31 ` [PATCH 23/23] ASoC: SOF: topology: Add support for decoder and encoder widgets Peter Ujfalusi
  22 siblings, 0 replies; 49+ messages in thread
From: Peter Ujfalusi @ 2026-09-04  7:31 UTC (permalink / raw)
  To: vkoul, perex, tiwai, lgirdwood, broonie, srinivas.kandagatla
  Cc: linux-sound, kai.vehmanen, yung-chuan.liao, pierre-louis.bossart,
	daniel.baluta, Vijendar.Mukunda

Select SOF_COMPRESS for TGL and newer platforms, on Intel devices the
compressed support is available with IPC4 only.

Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
Reviewed-by: Liam Girdwood <liam.r.girdwood@intel.com>
---
 sound/soc/sof/intel/Kconfig | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/sound/soc/sof/intel/Kconfig b/sound/soc/sof/intel/Kconfig
index 840d5a36b958..02b41e4ccab2 100644
--- a/sound/soc/sof/intel/Kconfig
+++ b/sound/soc/sof/intel/Kconfig
@@ -213,6 +213,7 @@ config SND_SOC_SOF_INTEL_TGL
 	select SND_SOC_SOF_HDA_GENERIC
 	select SND_SOC_SOF_INTEL_SOUNDWIRE_LINK_BASELINE
 	select SND_SOC_SOF_INTEL_CNL
+	select SND_SOC_SOF_COMPRESS
 
 config SND_SOC_SOF_TIGERLAKE
 	tristate "SOF support for Tigerlake"
@@ -249,6 +250,7 @@ config SND_SOC_SOF_INTEL_MTL
 	select SND_SOC_SOF_HDA_GENERIC
 	select SND_SOC_SOF_INTEL_SOUNDWIRE_LINK_BASELINE
 	select SND_SOC_SOF_IPC4
+	select SND_SOC_SOF_COMPRESS
 
 config SND_SOC_SOF_METEORLAKE
 	tristate "SOF support for Meteorlake"
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 49+ messages in thread

* [PATCH 23/23] ASoC: SOF: topology: Add support for decoder and encoder widgets
  2026-09-04  7:31 [PATCH 00/23] ALSA compress / ASoC compress / SOF: Compressed audio support with IPC4 Peter Ujfalusi
                   ` (21 preceding siblings ...)
  2026-09-04  7:31 ` [PATCH 22/23] ASoC: SOF: Intel: Kconfig: Select compress support for TGL+ platforms Peter Ujfalusi
@ 2026-09-04  7:31 ` Peter Ujfalusi
  22 siblings, 0 replies; 49+ messages in thread
From: Peter Ujfalusi @ 2026-09-04  7:31 UTC (permalink / raw)
  To: vkoul, perex, tiwai, lgirdwood, broonie, srinivas.kandagatla
  Cc: linux-sound, kai.vehmanen, yung-chuan.liao, pierre-louis.bossart,
	daniel.baluta, Vijendar.Mukunda

From: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>

Decoder and encoder modules fall under process modules in SOF.

Signed-off-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
Co-developed-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
Reviewed-by: Liam Girdwood <liam.r.girdwood@intel.com>
---
 sound/soc/sof/ipc4-topology.c | 11 +++++++++++
 sound/soc/sof/sof-audio.h     |  4 +++-
 sound/soc/sof/topology.c      |  2 ++
 3 files changed, 16 insertions(+), 1 deletion(-)

diff --git a/sound/soc/sof/ipc4-topology.c b/sound/soc/sof/ipc4-topology.c
index 566913025cc7..74a6da9705da 100644
--- a/sound/soc/sof/ipc4-topology.c
+++ b/sound/soc/sof/ipc4-topology.c
@@ -3589,6 +3589,8 @@ static int sof_ipc4_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget
 		msg = &asrc->msg;
 		break;
 	}
+	case snd_soc_dapm_decoder:
+	case snd_soc_dapm_encoder:
 	case snd_soc_dapm_effect:
 	{
 		struct sof_ipc4_process *process = swidget->private;
@@ -4361,6 +4363,15 @@ static const struct sof_ipc_tplg_widget_ops tplg_ipc4_widget_ops[SND_SOC_DAPM_TY
 				process_token_list, ARRAY_SIZE(process_token_list),
 				NULL, sof_ipc4_prepare_process_module,
 				NULL},
+	/* for all practical purposes a decoder is like an effect type widget */
+	[snd_soc_dapm_decoder] = {sof_ipc4_widget_setup_comp_process,
+				  sof_ipc4_widget_free_comp_process,
+				  process_token_list, ARRAY_SIZE(process_token_list),
+				  NULL, sof_ipc4_prepare_process_module, NULL},
+	[snd_soc_dapm_encoder] = {sof_ipc4_widget_setup_comp_process,
+				  sof_ipc4_widget_free_comp_process,
+				  process_token_list, ARRAY_SIZE(process_token_list),
+				  NULL, sof_ipc4_prepare_process_module, NULL},
 };
 
 const struct sof_ipc_tplg_ops ipc4_tplg_ops = {
diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h
index 9dff4fea628e..b60246fd5869 100644
--- a/sound/soc/sof/sof-audio.h
+++ b/sound/soc/sof/sof-audio.h
@@ -43,7 +43,9 @@
 #define WIDGET_IS_AIF(id) ((id) == snd_soc_dapm_aif_in || (id) == snd_soc_dapm_aif_out)
 #define WIDGET_IS_AIF_OR_DAI(id) (WIDGET_IS_DAI(id) || WIDGET_IS_AIF(id))
 #define WIDGET_IS_COPIER(id) (WIDGET_IS_AIF_OR_DAI(id) || (id) == snd_soc_dapm_buffer)
-#define WIDGET_IS_PROCESS(id) ((id) == snd_soc_dapm_effect)
+#define WIDGET_IS_PROCESS(id) ((id) == snd_soc_dapm_effect ||	\
+			       (id) == snd_soc_dapm_decoder ||	\
+			       (id) == snd_soc_dapm_encoder)
 
 #define SOF_DAI_PARAM_INTEL_SSP_MCLK		0
 #define SOF_DAI_PARAM_INTEL_SSP_BCLK		1
diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c
index 8338133899ab..e2cb90133650 100644
--- a/sound/soc/sof/topology.c
+++ b/sound/soc/sof/topology.c
@@ -1530,6 +1530,8 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index,
 		list_add(&dai->list, &sdev->dai_list);
 		swidget->private = dai;
 		break;
+	case snd_soc_dapm_decoder:
+	case snd_soc_dapm_encoder:
 	case snd_soc_dapm_effect:
 		/* check we have some tokens - we need at least process type */
 		if (le32_to_cpu(tw->priv.size) == 0) {
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 49+ messages in thread

* Re: [PATCH 01/23] ALSA: compress: pin card module while stream is open
  2026-09-04  7:31 ` [PATCH 01/23] ALSA: compress: pin card module while stream is open Peter Ujfalusi
@ 2026-09-04  7:50   ` Takashi Iwai
  2026-09-04  8:03     ` Péter Ujfalusi
  0 siblings, 1 reply; 49+ messages in thread
From: Takashi Iwai @ 2026-09-04  7:50 UTC (permalink / raw)
  To: Peter Ujfalusi
  Cc: vkoul, perex, tiwai, lgirdwood, broonie, srinivas.kandagatla,
	linux-sound, kai.vehmanen, yung-chuan.liao, pierre-louis.bossart,
	daniel.baluta, Vijendar.Mukunda

On Fri, 04 Sep 2026 09:31:12 +0200,
Peter Ujfalusi wrote:
> 
> Take a module reference in snd_compr_open() and release it in
> snd_compr_free(). This pins the card driver module for the
> lifetime of an open compress stream and prevents card removal
> while the stream file is still in use.
> 
> Adjust the open() cleanup paths to drop the added module
> reference only when it was acquired, and keep release ordering
> safe by dropping the module reference before freeing stream
> data.
> 
> Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
> Reviewed-by: Liam Girdwood <liam.r.girdwood@intel.com>

Reviewed-by: Takashi Iwai <tiwai@suse.de>

While we're at it: the compress-offload seems missing the call of
snd_card_file_add() and snd_card_file_remove() at open/close, too.
By adding those, the unexpected syscalls are guarded in the core side
after disconnection by replacing file ops.


thanks,

Takashi

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH 02/23] ALSA: compress: stop active streams on disconnect
  2026-09-04  7:31 ` [PATCH 02/23] ALSA: compress: stop active streams on disconnect Peter Ujfalusi
@ 2026-09-04  7:51   ` Takashi Iwai
  0 siblings, 0 replies; 49+ messages in thread
From: Takashi Iwai @ 2026-09-04  7:51 UTC (permalink / raw)
  To: Peter Ujfalusi
  Cc: vkoul, perex, tiwai, lgirdwood, broonie, srinivas.kandagatla,
	linux-sound, kai.vehmanen, yung-chuan.liao, pierre-louis.bossart,
	daniel.baluta, Vijendar.Mukunda

On Fri, 04 Sep 2026 09:31:13 +0200,
Peter Ujfalusi wrote:
> 
> Track open compressed streams per device so disconnect can stop
> active streams and wake waiters before snd_unregister_device().
> 
> This aligns compressed stream teardown with PCM disconnect behavior
> and prevents active userspace streams from running into unregister
> races.
> 
> Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
> Reviewed-by: Liam Girdwood <liam.r.girdwood@intel.com>

Reviewed-by: Takashi Iwai <tiwai@suse.de>


thanks,

Takashi

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH 01/23] ALSA: compress: pin card module while stream is open
  2026-09-04  7:50   ` Takashi Iwai
@ 2026-09-04  8:03     ` Péter Ujfalusi
  0 siblings, 0 replies; 49+ messages in thread
From: Péter Ujfalusi @ 2026-09-04  8:03 UTC (permalink / raw)
  To: Takashi Iwai
  Cc: vkoul, perex, tiwai, lgirdwood, broonie, srinivas.kandagatla,
	linux-sound, kai.vehmanen, yung-chuan.liao, pierre-louis.bossart,
	daniel.baluta, Vijendar.Mukunda



On 04/09/2026 10:50, Takashi Iwai wrote:
> On Fri, 04 Sep 2026 09:31:12 +0200,
> Peter Ujfalusi wrote:
>>
>> Take a module reference in snd_compr_open() and release it in
>> snd_compr_free(). This pins the card driver module for the
>> lifetime of an open compress stream and prevents card removal
>> while the stream file is still in use.
>>
>> Adjust the open() cleanup paths to drop the added module
>> reference only when it was acquired, and keep release ordering
>> safe by dropping the module reference before freeing stream
>> data.
>>
>> Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
>> Reviewed-by: Liam Girdwood <liam.r.girdwood@intel.com>
> 
> Reviewed-by: Takashi Iwai <tiwai@suse.de>
> 
> While we're at it: the compress-offload seems missing the call of
> snd_card_file_add() and snd_card_file_remove() at open/close, too.
> By adding those, the unexpected syscalls are guarded in the core side
> after disconnection by replacing file ops.

You are right, I'll prepare the followup patch and either send it later
or include it in v2 of the series.

-- 
Péter


^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH 04/23] ASoC: soc-compress: Implement trigger FE-BE sequencing as with normal PCMs
  2026-09-04  7:31 ` [PATCH 04/23] ASoC: soc-compress: Implement trigger FE-BE sequencing as with normal PCMs Peter Ujfalusi
@ 2026-09-04 19:45   ` Mark Brown
  2026-09-07  5:25     ` Péter Ujfalusi
  0 siblings, 1 reply; 49+ messages in thread
From: Mark Brown @ 2026-09-04 19:45 UTC (permalink / raw)
  To: Peter Ujfalusi
  Cc: vkoul, perex, tiwai, lgirdwood, srinivas.kandagatla, linux-sound,
	kai.vehmanen, yung-chuan.liao, pierre-louis.bossart,
	daniel.baluta, Vijendar.Mukunda

[-- Attachment #1: Type: text/plain, Size: 1233 bytes --]

On Fri, Sep 04, 2026 at 10:31:15AM +0300, Peter Ujfalusi wrote:

> The FE-BE trigger sequence should be dynamic, similarly how soc-pcm.c
> dpcm_fe_dai_do_trigger() does it.

> +static int soc_compr_trigger_fe(struct snd_compr_stream *cstream, int cmd)
> +{
> +	struct snd_soc_pcm_runtime *fe = cstream->private_data;
>  	int stream = cstream->direction; /* SND_COMPRESS_xxx is same as SNDRV_PCM_STREAM_xxx */
> +	bool fe_first;
>  	int ret;
>  
>  	if (cmd == SND_COMPR_TRIGGER_PARTIAL_DRAIN ||
>  	    cmd == SND_COMPR_TRIGGER_DRAIN)
>  		return snd_soc_component_compr_trigger(cstream, cmd);

What about SND_COMPR_TRIGGER_NEXT_TRACK...

> +	switch (cmd) {
> +	case SNDRV_PCM_TRIGGER_START:
> +	case SNDRV_PCM_TRIGGER_RESUME:
> +	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
> +		ret = soc_compr_trigger_fe_be(cstream, cmd, fe_first);
> +		break;
> +	case SNDRV_PCM_TRIGGER_STOP:
> +	case SNDRV_PCM_TRIGGER_SUSPEND:
> +	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
> +		ret = soc_compr_trigger_fe_be(cstream, cmd, !fe_first);
> +		break;
> +	default:
> +		ret = -EINVAL;
> +		break;

...otherwise it'll end up with -EINVAL instead of getting passed
through.  Also a switch statement would be a bit more idiomatic.

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH 19/23] ASoC: SOF: Add support for IPC4 compressed
  2026-09-04  7:31 ` [PATCH 19/23] ASoC: SOF: Add support for IPC4 compressed Peter Ujfalusi
@ 2026-09-04 20:08   ` Mark Brown
  2026-09-07  5:15     ` Péter Ujfalusi
  0 siblings, 1 reply; 49+ messages in thread
From: Mark Brown @ 2026-09-04 20:08 UTC (permalink / raw)
  To: Peter Ujfalusi
  Cc: vkoul, perex, tiwai, lgirdwood, srinivas.kandagatla, linux-sound,
	kai.vehmanen, yung-chuan.liao, pierre-louis.bossart,
	daniel.baluta, Vijendar.Mukunda

[-- Attachment #1: Type: text/plain, Size: 688 bytes --]

On Fri, Sep 04, 2026 at 10:31:30AM +0300, Peter Ujfalusi wrote:
> Set and define the compressed ops for IPC4.
> The initial implementation supports basic features: PAUSE PUSH/RELEASE,
> DRAIN and progress reporting.
> Tested with PCM, MP3, AAC and VORBIS codec.

> +static int sof_ipc4_compr_set_params(struct snd_soc_component *component,
> +				     struct snd_compr_stream *cstream,
> +				     struct snd_compr_params *params)
> +{

> +	/* Use correct format based on the used codec */
> +	switch (params->codec.id) {
> +	case SND_AUDIOCODEC_PCM:
> +		snd_mask_set_format(fmt, (snd_pcm_format_t)params->codec.format);
> +		break;

Do we validate the value supplied by userspace here?

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH 19/23] ASoC: SOF: Add support for IPC4 compressed
  2026-09-04 20:08   ` Mark Brown
@ 2026-09-07  5:15     ` Péter Ujfalusi
  2026-09-09  6:48       ` Péter Ujfalusi
  0 siblings, 1 reply; 49+ messages in thread
From: Péter Ujfalusi @ 2026-09-07  5:15 UTC (permalink / raw)
  To: Mark Brown
  Cc: vkoul, perex, tiwai, lgirdwood, srinivas.kandagatla, linux-sound,
	kai.vehmanen, yung-chuan.liao, pierre-louis.bossart,
	daniel.baluta, Vijendar.Mukunda



On 04/09/2026 23:08, Mark Brown wrote:
> On Fri, Sep 04, 2026 at 10:31:30AM +0300, Peter Ujfalusi wrote:
>> Set and define the compressed ops for IPC4.
>> The initial implementation supports basic features: PAUSE PUSH/RELEASE,
>> DRAIN and progress reporting.
>> Tested with PCM, MP3, AAC and VORBIS codec.
> 
>> +static int sof_ipc4_compr_set_params(struct snd_soc_component *component,
>> +				     struct snd_compr_stream *cstream,
>> +				     struct snd_compr_params *params)
>> +{
> 
>> +	/* Use correct format based on the used codec */
>> +	switch (params->codec.id) {
>> +	case SND_AUDIOCODEC_PCM:
>> +		snd_mask_set_format(fmt, (snd_pcm_format_t)params->codec.format);
>> +		break;
> 
> Do we validate the value supplied by userspace here?

On SOF core side we do not, but on the Intel side in
hda_dsp_compr_hw_params() we might want to fail in case of S24_3LE,
which is not supported. by hardware.

-- 
Péter


^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH 04/23] ASoC: soc-compress: Implement trigger FE-BE sequencing as with normal PCMs
  2026-09-04 19:45   ` Mark Brown
@ 2026-09-07  5:25     ` Péter Ujfalusi
  2026-09-07 11:26       ` Pierre-Louis Bossart
  0 siblings, 1 reply; 49+ messages in thread
From: Péter Ujfalusi @ 2026-09-07  5:25 UTC (permalink / raw)
  To: Mark Brown
  Cc: vkoul, perex, tiwai, lgirdwood, srinivas.kandagatla, linux-sound,
	kai.vehmanen, yung-chuan.liao, pierre-louis.bossart,
	daniel.baluta, Vijendar.Mukunda



On 04/09/2026 22:45, Mark Brown wrote:
> On Fri, Sep 04, 2026 at 10:31:15AM +0300, Peter Ujfalusi wrote:
> 
>> The FE-BE trigger sequence should be dynamic, similarly how soc-pcm.c
>> dpcm_fe_dai_do_trigger() does it.
> 
>> +static int soc_compr_trigger_fe(struct snd_compr_stream *cstream, int cmd)
>> +{
>> +	struct snd_soc_pcm_runtime *fe = cstream->private_data;
>>  	int stream = cstream->direction; /* SND_COMPRESS_xxx is same as SNDRV_PCM_STREAM_xxx */
>> +	bool fe_first;
>>  	int ret;
>>  
>>  	if (cmd == SND_COMPR_TRIGGER_PARTIAL_DRAIN ||
>>  	    cmd == SND_COMPR_TRIGGER_DRAIN)
>>  		return snd_soc_component_compr_trigger(cstream, cmd);
> 
> What about SND_COMPR_TRIGGER_NEXT_TRACK...

That was already missing in the initial patch which added the DPCM
support for compress.

> 
>> +	switch (cmd) {
>> +	case SNDRV_PCM_TRIGGER_START:
>> +	case SNDRV_PCM_TRIGGER_RESUME:
>> +	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
>> +		ret = soc_compr_trigger_fe_be(cstream, cmd, fe_first);
>> +		break;
>> +	case SNDRV_PCM_TRIGGER_STOP:
>> +	case SNDRV_PCM_TRIGGER_SUSPEND:
>> +	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
>> +		ret = soc_compr_trigger_fe_be(cstream, cmd, !fe_first);
>> +		break;
>> +	default:
>> +		ret = -EINVAL;
>> +		break;
> 
> ...otherwise it'll end up with -EINVAL instead of getting passed
> through.  Also a switch statement would be a bit more idiomatic.

OK, I will add the NEXT_TRACK and convert the if to switch.

-- 
Péter


^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH 04/23] ASoC: soc-compress: Implement trigger FE-BE sequencing as with normal PCMs
  2026-09-07  5:25     ` Péter Ujfalusi
@ 2026-09-07 11:26       ` Pierre-Louis Bossart
  2026-09-07 14:03         ` Péter Ujfalusi
  0 siblings, 1 reply; 49+ messages in thread
From: Pierre-Louis Bossart @ 2026-09-07 11:26 UTC (permalink / raw)
  To: Péter Ujfalusi, Mark Brown
  Cc: vkoul, perex, tiwai, lgirdwood, srinivas.kandagatla, linux-sound,
	kai.vehmanen, yung-chuan.liao, daniel.baluta, Vijendar.Mukunda

On 9/7/26 07:25, Péter Ujfalusi wrote:
> 
> 
> On 04/09/2026 22:45, Mark Brown wrote:
>> On Fri, Sep 04, 2026 at 10:31:15AM +0300, Peter Ujfalusi wrote:
>>
>>> The FE-BE trigger sequence should be dynamic, similarly how soc-pcm.c
>>> dpcm_fe_dai_do_trigger() does it.
>>
>>> +static int soc_compr_trigger_fe(struct snd_compr_stream *cstream, int cmd)
>>> +{
>>> +	struct snd_soc_pcm_runtime *fe = cstream->private_data;
>>>  	int stream = cstream->direction; /* SND_COMPRESS_xxx is same as SNDRV_PCM_STREAM_xxx */
>>> +	bool fe_first;
>>>  	int ret;
>>>  
>>>  	if (cmd == SND_COMPR_TRIGGER_PARTIAL_DRAIN ||
>>>  	    cmd == SND_COMPR_TRIGGER_DRAIN)
>>>  		return snd_soc_component_compr_trigger(cstream, cmd);
>>
>> What about SND_COMPR_TRIGGER_NEXT_TRACK...
> 
> That was already missing in the initial patch which added the DPCM
> support for compress.
> 
>>
>>> +	switch (cmd) {
>>> +	case SNDRV_PCM_TRIGGER_START:
>>> +	case SNDRV_PCM_TRIGGER_RESUME:
>>> +	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
>>> +		ret = soc_compr_trigger_fe_be(cstream, cmd, fe_first);
>>> +		break;
>>> +	case SNDRV_PCM_TRIGGER_STOP:
>>> +	case SNDRV_PCM_TRIGGER_SUSPEND:
>>> +	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
>>> +		ret = soc_compr_trigger_fe_be(cstream, cmd, !fe_first);
>>> +		break;
>>> +	default:
>>> +		ret = -EINVAL;
>>> +		break;
>>
>> ...otherwise it'll end up with -EINVAL instead of getting passed
>> through.  Also a switch statement would be a bit more idiomatic.
> 
> OK, I will add the NEXT_TRACK and convert the if to switch.

ICYMI for testing, NEXT_TRACK and PARTIAL_DRAIN are only enabled in the
'fcplay' application with the '-p 1' argument for gapless playback, not
in the initial cplay application.



^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH 04/23] ASoC: soc-compress: Implement trigger FE-BE sequencing as with normal PCMs
  2026-09-07 11:26       ` Pierre-Louis Bossart
@ 2026-09-07 14:03         ` Péter Ujfalusi
  2026-09-07 16:53           ` Vinod Koul
  0 siblings, 1 reply; 49+ messages in thread
From: Péter Ujfalusi @ 2026-09-07 14:03 UTC (permalink / raw)
  To: Pierre-Louis Bossart, Mark Brown
  Cc: vkoul, perex, tiwai, lgirdwood, srinivas.kandagatla, linux-sound,
	kai.vehmanen, yung-chuan.liao, daniel.baluta, Vijendar.Mukunda



On 07/09/2026 14:26, Pierre-Louis Bossart wrote:
>>> ...otherwise it'll end up with -EINVAL instead of getting passed
>>> through.  Also a switch statement would be a bit more idiomatic.
>>
>> OK, I will add the NEXT_TRACK and convert the if to switch.
> 
> ICYMI for testing, NEXT_TRACK and PARTIAL_DRAIN are only enabled in the
> 'fcplay' application with the '-p 1' argument for gapless playback, not
> in the initial cplay application.

With SOF/Intel/IPC4 we can only support DRAIN, NEXT_TRACK and
PARTIAL_DRAIN are not supported, but there were mismatch between fcplay
and the kernel.
IPC3 does not support DRAIN afaik.

fcplay fix for gaplass and support for restart mode of continuous playback:
https://github.com/alsa-project/tinycompress/pull/33

-- 
Péter


^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH 04/23] ASoC: soc-compress: Implement trigger FE-BE sequencing as with normal PCMs
  2026-09-07 14:03         ` Péter Ujfalusi
@ 2026-09-07 16:53           ` Vinod Koul
  2026-09-07 19:55             ` Pierre-Louis Bossart
  0 siblings, 1 reply; 49+ messages in thread
From: Vinod Koul @ 2026-09-07 16:53 UTC (permalink / raw)
  To: Péter Ujfalusi
  Cc: Pierre-Louis Bossart, Mark Brown, perex, tiwai, lgirdwood,
	srinivas.kandagatla, linux-sound, kai.vehmanen, yung-chuan.liao,
	daniel.baluta, Vijendar.Mukunda

On 07-09-26, 17:03, Péter Ujfalusi wrote:
> 
> 
> On 07/09/2026 14:26, Pierre-Louis Bossart wrote:
> >>> ...otherwise it'll end up with -EINVAL instead of getting passed
> >>> through.  Also a switch statement would be a bit more idiomatic.
> >>
> >> OK, I will add the NEXT_TRACK and convert the if to switch.
> > 
> > ICYMI for testing, NEXT_TRACK and PARTIAL_DRAIN are only enabled in the
> > 'fcplay' application with the '-p 1' argument for gapless playback, not
> > in the initial cplay application.
> 
> With SOF/Intel/IPC4 we can only support DRAIN, NEXT_TRACK and
> PARTIAL_DRAIN are not supported, but there were mismatch between fcplay

Those should be optional always. Gapless support should not be mandatory

> and the kernel.
> IPC3 does not support DRAIN afaik.

That one should be mandatory

-- 
~Vinod

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH 04/23] ASoC: soc-compress: Implement trigger FE-BE sequencing as with normal PCMs
  2026-09-07 16:53           ` Vinod Koul
@ 2026-09-07 19:55             ` Pierre-Louis Bossart
  2026-09-08  6:37               ` Péter Ujfalusi
  2026-09-08  6:52               ` Vinod Koul
  0 siblings, 2 replies; 49+ messages in thread
From: Pierre-Louis Bossart @ 2026-09-07 19:55 UTC (permalink / raw)
  To: Vinod Koul, Péter Ujfalusi
  Cc: Mark Brown, perex, tiwai, lgirdwood, srinivas.kandagatla,
	linux-sound, kai.vehmanen, yung-chuan.liao, daniel.baluta,
	Vijendar.Mukunda

On 9/7/26 18:53, Vinod Koul wrote:
> On 07-09-26, 17:03, Péter Ujfalusi wrote:
>>
>>
>> On 07/09/2026 14:26, Pierre-Louis Bossart wrote:
>>>>> ...otherwise it'll end up with -EINVAL instead of getting passed
>>>>> through.  Also a switch statement would be a bit more idiomatic.
>>>>
>>>> OK, I will add the NEXT_TRACK and convert the if to switch.
>>>
>>> ICYMI for testing, NEXT_TRACK and PARTIAL_DRAIN are only enabled in the
>>> 'fcplay' application with the '-p 1' argument for gapless playback, not
>>> in the initial cplay application.
>>
>> With SOF/Intel/IPC4 we can only support DRAIN, NEXT_TRACK and
>> PARTIAL_DRAIN are not supported, but there were mismatch between fcplay
> 
> Those should be optional always. Gapless support should not be mandatory
well in practice it's a non-negotiable requirement from users... We had
to add this as the request of the Android team, and it's a standard
feature on most players, not a nice-to-have.

Note that the NEXT_TRACK only works if the two consecutive tracks have
the same format, if the platform wants 'perceived' gapless playback with
a x-fade then two devices are required.

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH 04/23] ASoC: soc-compress: Implement trigger FE-BE sequencing as with normal PCMs
  2026-09-07 19:55             ` Pierre-Louis Bossart
@ 2026-09-08  6:37               ` Péter Ujfalusi
  2026-09-08  6:58                 ` Vinod Koul
  2026-09-08  6:52               ` Vinod Koul
  1 sibling, 1 reply; 49+ messages in thread
From: Péter Ujfalusi @ 2026-09-08  6:37 UTC (permalink / raw)
  To: Pierre-Louis Bossart, Vinod Koul
  Cc: Mark Brown, perex, tiwai, lgirdwood, srinivas.kandagatla,
	linux-sound, kai.vehmanen, yung-chuan.liao, daniel.baluta,
	Vijendar.Mukunda



On 07/09/2026 22:55, Pierre-Louis Bossart wrote:
> On 9/7/26 18:53, Vinod Koul wrote:
>> On 07-09-26, 17:03, Péter Ujfalusi wrote:
>>>
>>>
>>> On 07/09/2026 14:26, Pierre-Louis Bossart wrote:
>>>>>> ...otherwise it'll end up with -EINVAL instead of getting passed
>>>>>> through.  Also a switch statement would be a bit more idiomatic.
>>>>>
>>>>> OK, I will add the NEXT_TRACK and convert the if to switch.
>>>>
>>>> ICYMI for testing, NEXT_TRACK and PARTIAL_DRAIN are only enabled in the
>>>> 'fcplay' application with the '-p 1' argument for gapless playback, not
>>>> in the initial cplay application.
>>>
>>> With SOF/Intel/IPC4 we can only support DRAIN, NEXT_TRACK and
>>> PARTIAL_DRAIN are not supported, but there were mismatch between fcplay
>>
>> Those should be optional always. Gapless support should not be mandatory
> well in practice it's a non-negotiable requirement from users... We had
> to add this as the request of the Android team, and it's a standard
> feature on most players, not a nice-to-have.

The only 'documentation' for the gapless (next_track + partial_drain)
that I could find was the fcplay (and that could not have been worked
(metadata and next_track was in wrong order).

But, reading the code it implies that gapless must support anything
thrown at it in any order as for consecutive files:
metadata
next_track
set_codec_params
partial_drain

With IPC4 the NOP strategy works (just streaming the files without
stopping, but they mus be of same format and property), but then you
loose the position, so not so great.

> Note that the NEXT_TRACK only works if the two consecutive tracks have
> the same format, if the platform wants 'perceived' gapless playback with
> a x-fade then two devices are required.

If I can ever test this I might add support for it, it is not clear how
the pointer should behave after partial_drain, we won't stop the DMAs in
this case.
With the cadence module (compress support in fw) the metadata is not
supported, so true gapless is really something which needs more work.

This has not been planned for the initial IPC4 compress support, it is
unclear how it can be done to me atm.

-- 
Péter


^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH 04/23] ASoC: soc-compress: Implement trigger FE-BE sequencing as with normal PCMs
  2026-09-07 19:55             ` Pierre-Louis Bossart
  2026-09-08  6:37               ` Péter Ujfalusi
@ 2026-09-08  6:52               ` Vinod Koul
  1 sibling, 0 replies; 49+ messages in thread
From: Vinod Koul @ 2026-09-08  6:52 UTC (permalink / raw)
  To: Pierre-Louis Bossart
  Cc: Péter Ujfalusi, Mark Brown, perex, tiwai, lgirdwood,
	srinivas.kandagatla, linux-sound, kai.vehmanen, yung-chuan.liao,
	daniel.baluta, Vijendar.Mukunda

On 07-09-26, 21:55, Pierre-Louis Bossart wrote:
> On 9/7/26 18:53, Vinod Koul wrote:
> > On 07-09-26, 17:03, Péter Ujfalusi wrote:
> >>
> >>
> >> On 07/09/2026 14:26, Pierre-Louis Bossart wrote:
> >>>>> ...otherwise it'll end up with -EINVAL instead of getting passed
> >>>>> through.  Also a switch statement would be a bit more idiomatic.
> >>>>
> >>>> OK, I will add the NEXT_TRACK and convert the if to switch.
> >>>
> >>> ICYMI for testing, NEXT_TRACK and PARTIAL_DRAIN are only enabled in the
> >>> 'fcplay' application with the '-p 1' argument for gapless playback, not
> >>> in the initial cplay application.
> >>
> >> With SOF/Intel/IPC4 we can only support DRAIN, NEXT_TRACK and
> >> PARTIAL_DRAIN are not supported, but there were mismatch between fcplay
> > 
> > Those should be optional always. Gapless support should not be mandatory
> well in practice it's a non-negotiable requirement from users... We had
> to add this as the request of the Android team, and it's a standard
> feature on most players, not a nice-to-have.

It may be non-negotiable requirement from user/OS but should not be
mandatory from API :-)

> Note that the NEXT_TRACK only works if the two consecutive tracks have
> the same format, if the platform wants 'perceived' gapless playback with
> a x-fade then two devices are required.

Correct and given the fact the people use apps on this ecosystem, I
would expect the formats will be same for an app

-- 
~Vinod

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH 04/23] ASoC: soc-compress: Implement trigger FE-BE sequencing as with normal PCMs
  2026-09-08  6:37               ` Péter Ujfalusi
@ 2026-09-08  6:58                 ` Vinod Koul
  2026-09-08  8:41                   ` Péter Ujfalusi
  0 siblings, 1 reply; 49+ messages in thread
From: Vinod Koul @ 2026-09-08  6:58 UTC (permalink / raw)
  To: Péter Ujfalusi
  Cc: Pierre-Louis Bossart, Mark Brown, perex, tiwai, lgirdwood,
	srinivas.kandagatla, linux-sound, kai.vehmanen, yung-chuan.liao,
	daniel.baluta, Vijendar.Mukunda

On 08-09-26, 09:37, Péter Ujfalusi wrote:
> 
> 
> On 07/09/2026 22:55, Pierre-Louis Bossart wrote:
> > On 9/7/26 18:53, Vinod Koul wrote:
> >> On 07-09-26, 17:03, Péter Ujfalusi wrote:
> >>>
> >>>
> >>> On 07/09/2026 14:26, Pierre-Louis Bossart wrote:
> >>>>>> ...otherwise it'll end up with -EINVAL instead of getting passed
> >>>>>> through.  Also a switch statement would be a bit more idiomatic.
> >>>>>
> >>>>> OK, I will add the NEXT_TRACK and convert the if to switch.
> >>>>
> >>>> ICYMI for testing, NEXT_TRACK and PARTIAL_DRAIN are only enabled in the
> >>>> 'fcplay' application with the '-p 1' argument for gapless playback, not
> >>>> in the initial cplay application.
> >>>
> >>> With SOF/Intel/IPC4 we can only support DRAIN, NEXT_TRACK and
> >>> PARTIAL_DRAIN are not supported, but there were mismatch between fcplay
> >>
> >> Those should be optional always. Gapless support should not be mandatory
> > well in practice it's a non-negotiable requirement from users... We had
> > to add this as the request of the Android team, and it's a standard
> > feature on most players, not a nice-to-have.
> 
> The only 'documentation' for the gapless (next_track + partial_drain)
> that I could find was the fcplay (and that could not have been worked
> (metadata and next_track was in wrong order).

Please see Documentation/sound/designs/compress-offload.rst, it has a
section on gapless with SM as well.

> But, reading the code it implies that gapless must support anything
> thrown at it in any order as for consecutive files:
> metadata
> next_track
> set_codec_params
> partial_drain

It was supposed to be in a specific sequence. See the sequence in the
documentation.

> With IPC4 the NOP strategy works (just streaming the files without
> stopping, but they mus be of same format and property), but then you
> loose the position, so not so great.
> 
> > Note that the NEXT_TRACK only works if the two consecutive tracks have
> > the same format, if the platform wants 'perceived' gapless playback with
> > a x-fade then two devices are required.

Yes, same format always. Codec can skip the trailing and leading zeros
in that case. two different formats would need code switch/code setting
params, so it wont work and we would need two devices to x-fade as you
rightly pointed out

> If I can ever test this I might add support for it, it is not clear how
> the pointer should behave after partial_drain, we won't stop the DMAs in
> this case.
> With the cadence module (compress support in fw) the metadata is not
> supported, so true gapless is really something which needs more work.
> 
> This has not been planned for the initial IPC4 compress support, it is
> unclear how it can be done to me atm.
> 
> -- 
> Péter

-- 
~Vinod

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH 04/23] ASoC: soc-compress: Implement trigger FE-BE sequencing as with normal PCMs
  2026-09-08  6:58                 ` Vinod Koul
@ 2026-09-08  8:41                   ` Péter Ujfalusi
  2026-09-08 12:06                     ` Pierre-Louis Bossart
  2026-09-08 12:37                     ` Vinod Koul
  0 siblings, 2 replies; 49+ messages in thread
From: Péter Ujfalusi @ 2026-09-08  8:41 UTC (permalink / raw)
  To: Vinod Koul
  Cc: Pierre-Louis Bossart, Mark Brown, perex, tiwai, lgirdwood,
	srinivas.kandagatla, linux-sound, kai.vehmanen, yung-chuan.liao,
	daniel.baluta, Vijendar.Mukunda



On 08/09/2026 09:58, Vinod Koul wrote:
>> The only 'documentation' for the gapless (next_track + partial_drain)
>> that I could find was the fcplay (and that could not have been worked
>> (metadata and next_track was in wrong order).
> 
> Please see Documentation/sound/designs/compress-offload.rst, it has a
> section on gapless with SM as well.

Thanks, for some reason I missed this, looks pretty well defined and
written!

> 
>> But, reading the code it implies that gapless must support anything
>> thrown at it in any order as for consecutive files:
>> metadata
>> next_track
>> set_codec_params
>> partial_drain
> 
> It was supposed to be in a specific sequence. See the sequence in the
> documentation.

The documentation confirms that the fcplay was incorrect:
https://github.com/alsa-project/tinycompress/commit/7d41dc4bcd39a1b2c449a6f87e827bdbd84d1213

>> With IPC4 the NOP strategy works (just streaming the files without
>> stopping, but they mus be of same format and property), but then you
>> loose the position, so not so great.
>>
>>> Note that the NEXT_TRACK only works if the two consecutive tracks have
>>> the same format, if the platform wants 'perceived' gapless playback with
>>> a x-fade then two devices are required.
> 
> Yes, same format always. Codec can skip the trailing and leading zeros
> in that case. two different formats would need code switch/code setting
> params, so it wont work and we would need two devices to x-fade as you
> rightly pointed out

gapless looks to be straight forward fort he first look. What made me
delay the implementation was the fact that I could not find any way in
firmware to support the metadata as one - OK, I can just ignore it.
second: in kernel and in firmware I could not figure out _where_
actually is the end of the current file in the buffer. In kernel we have
circular buffer for the DMA and userspace just fills it and since for
the kernel it is just data, it looks continuous.
The firmware can detect the end of a compressed file, so I can sort of
support the drain and partial_drain but to get this whole thing working
was just not something I could do blindly.
Also: I have assorted ogg/mp3/flac in my music library and I tend to
play them in random, which defeats the gapless.

The gapless support is not covered in this initial version, but can be
added later.

-- 
Péter


^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH 04/23] ASoC: soc-compress: Implement trigger FE-BE sequencing as with normal PCMs
  2026-09-08  8:41                   ` Péter Ujfalusi
@ 2026-09-08 12:06                     ` Pierre-Louis Bossart
  2026-09-08 14:04                       ` Péter Ujfalusi
  2026-09-08 12:37                     ` Vinod Koul
  1 sibling, 1 reply; 49+ messages in thread
From: Pierre-Louis Bossart @ 2026-09-08 12:06 UTC (permalink / raw)
  To: Péter Ujfalusi, Vinod Koul
  Cc: Mark Brown, perex, tiwai, lgirdwood, srinivas.kandagatla,
	linux-sound, kai.vehmanen, yung-chuan.liao, daniel.baluta,
	Vijendar.Mukunda


> gapless looks to be straight forward fort he first look. What made me
> delay the implementation was the fact that I could not find any way in
> firmware to support the metadata as one - OK, I can just ignore it.
> second: in kernel and in firmware I could not figure out _where_
> actually is the end of the current file in the buffer. In kernel we have
> circular buffer for the DMA and userspace just fills it and since for
> the kernel it is just data, it looks continuous.

I don't think the kernel needs to worry about anything. When the next
track is handled, the data is just added after the current write pointer.

IIRC all the accounting needs to be done at the firmware level, where
you have to reconstruct where the next track starts based on the number
of bytes transferred.

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH 04/23] ASoC: soc-compress: Implement trigger FE-BE sequencing as with normal PCMs
  2026-09-08  8:41                   ` Péter Ujfalusi
  2026-09-08 12:06                     ` Pierre-Louis Bossart
@ 2026-09-08 12:37                     ` Vinod Koul
  2026-09-08 14:13                       ` Péter Ujfalusi
  1 sibling, 1 reply; 49+ messages in thread
From: Vinod Koul @ 2026-09-08 12:37 UTC (permalink / raw)
  To: Péter Ujfalusi
  Cc: Pierre-Louis Bossart, Mark Brown, perex, tiwai, lgirdwood,
	srinivas.kandagatla, linux-sound, kai.vehmanen, yung-chuan.liao,
	daniel.baluta, Vijendar.Mukunda

On 08-09-26, 11:41, Péter Ujfalusi wrote:
> 
> 
> On 08/09/2026 09:58, Vinod Koul wrote:
> >> The only 'documentation' for the gapless (next_track + partial_drain)
> >> that I could find was the fcplay (and that could not have been worked
> >> (metadata and next_track was in wrong order).
> > 
> > Please see Documentation/sound/designs/compress-offload.rst, it has a
> > section on gapless with SM as well.
> 
> Thanks, for some reason I missed this, looks pretty well defined and
> written!

Thats okay, glad that you found it useful

> 
> > 
> >> But, reading the code it implies that gapless must support anything
> >> thrown at it in any order as for consecutive files:
> >> metadata
> >> next_track
> >> set_codec_params
> >> partial_drain
> > 
> > It was supposed to be in a specific sequence. See the sequence in the
> > documentation.
> 
> The documentation confirms that the fcplay was incorrect:
> https://github.com/alsa-project/tinycompress/commit/7d41dc4bcd39a1b2c449a6f87e827bdbd84d1213

OKay, feel free to send a patch, we should be consistent with above
documented behaviour

> 
> >> With IPC4 the NOP strategy works (just streaming the files without
> >> stopping, but they mus be of same format and property), but then you
> >> loose the position, so not so great.

I wanted to ask earlier, whats IPC4?

> >>
> >>> Note that the NEXT_TRACK only works if the two consecutive tracks have
> >>> the same format, if the platform wants 'perceived' gapless playback with
> >>> a x-fade then two devices are required.
> > 
> > Yes, same format always. Codec can skip the trailing and leading zeros
> > in that case. two different formats would need code switch/code setting
> > params, so it wont work and we would need two devices to x-fade as you
> > rightly pointed out
> 
> gapless looks to be straight forward fort he first look. What made me
> delay the implementation was the fact that I could not find any way in
> firmware to support the metadata as one - OK, I can just ignore it.
> second: in kernel and in firmware I could not figure out _where_
> actually is the end of the current file in the buffer. In kernel we have
> circular buffer for the DMA and userspace just fills it and since for
> the kernel it is just data, it looks continuous.
> The firmware can detect the end of a compressed file, so I can sort of
> support the drain and partial_drain but to get this whole thing working
> was just not something I could do blindly.

The kernel should view this as a continuous bit stream and wont know
EOF. Only firmware will know about it and with metadata, able to skip
those.

> Also: I have assorted ogg/mp3/flac in my music library and I tend to
> play them in random, which defeats the gapless.

You are a non typical users :-) To test this, we would same track in
album. A typical user would be running spotify with same format :-)

> The gapless support is not covered in this initial version, but can be
> added later.
> 

-- 
~Vinod

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH 04/23] ASoC: soc-compress: Implement trigger FE-BE sequencing as with normal PCMs
  2026-09-08 12:06                     ` Pierre-Louis Bossart
@ 2026-09-08 14:04                       ` Péter Ujfalusi
  0 siblings, 0 replies; 49+ messages in thread
From: Péter Ujfalusi @ 2026-09-08 14:04 UTC (permalink / raw)
  To: Pierre-Louis Bossart, Vinod Koul
  Cc: Mark Brown, perex, tiwai, lgirdwood, srinivas.kandagatla,
	linux-sound, kai.vehmanen, yung-chuan.liao, daniel.baluta,
	Vijendar.Mukunda



On 08/09/2026 15:06, Pierre-Louis Bossart wrote:
> 
>> gapless looks to be straight forward fort he first look. What made me
>> delay the implementation was the fact that I could not find any way in
>> firmware to support the metadata as one - OK, I can just ignore it.
>> second: in kernel and in firmware I could not figure out _where_
>> actually is the end of the current file in the buffer. In kernel we have
>> circular buffer for the DMA and userspace just fills it and since for
>> the kernel it is just data, it looks continuous.
> 
> I don't think the kernel needs to worry about anything. When the next
> track is handled, the data is just added after the current write pointer.

Yep.

> IIRC all the accounting needs to be done at the firmware level, where
> you have to reconstruct where the next track starts based on the number
> of bytes transferred.

right, the fw does not know the size of any of the files, and the
decoder itself is a black box to us.

The kernel can tell the fw that there will be a new file at some point
in near future (depending on the buffer size), but both the kernel and
the fw are in the dark on where that might be.

At this point I don't want to claim that it is supported when I cannot
really test this, thus it is not implemented.

-- 
Péter


^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH 04/23] ASoC: soc-compress: Implement trigger FE-BE sequencing as with normal PCMs
  2026-09-08 12:37                     ` Vinod Koul
@ 2026-09-08 14:13                       ` Péter Ujfalusi
  2026-09-08 14:54                         ` Pierre-Louis Bossart
  2026-09-08 17:12                         ` Vinod Koul
  0 siblings, 2 replies; 49+ messages in thread
From: Péter Ujfalusi @ 2026-09-08 14:13 UTC (permalink / raw)
  To: Vinod Koul
  Cc: Pierre-Louis Bossart, Mark Brown, perex, tiwai, lgirdwood,
	srinivas.kandagatla, linux-sound, kai.vehmanen, yung-chuan.liao,
	daniel.baluta, Vijendar.Mukunda



On 08/09/2026 15:37, Vinod Koul wrote:
>>> It was supposed to be in a specific sequence. See the sequence in the
>>> documentation.
>>
>> The documentation confirms that the fcplay was incorrect:
>> https://github.com/alsa-project/tinycompress/commit/7d41dc4bcd39a1b2c449a6f87e827bdbd84d1213
> 
> OKay, feel free to send a patch, we should be consistent with above
> documented behaviour

It is already applied.

>>>> With IPC4 the NOP strategy works (just streaming the files without
>>>> stopping, but they mus be of same format and property), but then you
>>>> loose the position, so not so great.
> 
> I wanted to ask earlier, whats IPC4?

SOF supports 2 major IPC mode, IPC3 which was used up to TGL family (and
by NXP, AMD and MediaTek) and IPC4 which is from MTL onward (with
unofficial support for TGL).

They have different operating principles and we just got the compress
support for IPC4 (so TGL and up) only on Intel platforms.

NXP is using the compressed with IPC3.

> The kernel should view this as a continuous bit stream and wont know
> EOF. Only firmware will know about it and with metadata, able to skip
> those.

I see, the issue is that I cannot find any way to give the metadata to
the decoder module, it is a black box to us.

Also I'm not sure how the fw could get any idea where one
flac/vorbis/aac/mp3 ends and where it starts, the black box does not
seam to be able to detect this or if it does, it will not emit this
information to outside.

>> Also: I have assorted ogg/mp3/flac in my music library and I tend to
>> play them in random, which defeats the gapless.
> 
> You are a non typical users :-) To test this, we would same track in
> album. A typical user would be running spotify with same format :-)

Yes, Spotify will shuffle, but use the same format mostly for the random
'files', true.

>> The gapless support is not covered in this initial version, but can be
>> added later.
I will likely need to do this at some point, but again, w/o the ability
to test it, it is hard to declare victory ;)

-- 
Péter


^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH 04/23] ASoC: soc-compress: Implement trigger FE-BE sequencing as with normal PCMs
  2026-09-08 14:13                       ` Péter Ujfalusi
@ 2026-09-08 14:54                         ` Pierre-Louis Bossart
  2026-09-09 13:53                           ` Péter Ujfalusi
  2026-09-08 17:12                         ` Vinod Koul
  1 sibling, 1 reply; 49+ messages in thread
From: Pierre-Louis Bossart @ 2026-09-08 14:54 UTC (permalink / raw)
  To: Péter Ujfalusi, Vinod Koul
  Cc: Mark Brown, perex, tiwai, lgirdwood, srinivas.kandagatla,
	linux-sound, kai.vehmanen, yung-chuan.liao, daniel.baluta,
	Vijendar.Mukunda


> I see, the issue is that I cannot find any way to give the metadata to
> the decoder module, it is a black box to us.

The metadata is typically handled *outside* of the decoder library by a
memcpy-based wrapper or modulo pointers.

For example due to the filterbank, the decoder will generate N zero
samples in the first frame. If you know the value of N thanks to the
METADATA ioctl, you can discard the first N samples in the wrapper.

Likewise at the end of the track the wrapper can skip the last M samples
which were padded to make the initial soundtrack a multiple of the frame
size (e.g. 1152 for MP3). the value M is the encoder padding.
> Also I'm not sure how the fw could get any idea where one
> flac/vorbis/aac/mp3 ends and where it starts, the black box does not
> seam to be able to detect this or if it does, it will not emit this
> information to outside.
I must admit I don't fully remember the details on the start/end
position. IIRC this isn't strictly required for elementary streams such
as MP3 or AAC ADTS, you could cat the streams together and that'd be
just fine. you would have a slightly different transition by blending
the two streams in a common filterbank history buffer but that'd be
nearly impossible to detect...
But for streams with a header such as WMA not sure how this might work,
I think you do need to know the buffer position, reset the decoder and
restart from that position.



^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH 04/23] ASoC: soc-compress: Implement trigger FE-BE sequencing as with normal PCMs
  2026-09-08 14:13                       ` Péter Ujfalusi
  2026-09-08 14:54                         ` Pierre-Louis Bossart
@ 2026-09-08 17:12                         ` Vinod Koul
  2026-09-09 14:09                           ` Péter Ujfalusi
  1 sibling, 1 reply; 49+ messages in thread
From: Vinod Koul @ 2026-09-08 17:12 UTC (permalink / raw)
  To: Péter Ujfalusi
  Cc: Pierre-Louis Bossart, Mark Brown, perex, tiwai, lgirdwood,
	srinivas.kandagatla, linux-sound, kai.vehmanen, yung-chuan.liao,
	daniel.baluta, Vijendar.Mukunda

On 08-09-26, 17:13, Péter Ujfalusi wrote:
> 
> >>>> With IPC4 the NOP strategy works (just streaming the files without
> >>>> stopping, but they mus be of same format and property), but then you
> >>>> loose the position, so not so great.
> > 
> > I wanted to ask earlier, whats IPC4?
> 
> SOF supports 2 major IPC mode, IPC3 which was used up to TGL family (and
> by NXP, AMD and MediaTek) and IPC4 which is from MTL onward (with
> unofficial support for TGL).
> 
> They have different operating principles and we just got the compress
> support for IPC4 (so TGL and up) only on Intel platforms.
> 
> NXP is using the compressed with IPC3.

aha, joy of changing IPC with SoC! I dont envy you Peter :-)

> 
> > The kernel should view this as a continuous bit stream and wont know
> > EOF. Only firmware will know about it and with metadata, able to skip
> > those.
> 
> I see, the issue is that I cannot find any way to give the metadata to
> the decoder module, it is a black box to us.
> 
> Also I'm not sure how the fw could get any idea where one
> flac/vorbis/aac/mp3 ends and where it starts, the black box does not
> seam to be able to detect this or if it does, it will not emit this
> information to outside.

It is supposed to be filled by media frameworks that parse the file and
send it down to kernel. That is why in fcplay chose to use libav for
getting information for few formats I had... I might have some fork in
my old pc, I will check if I had something more that could help you.

Feel free to extend it, if this is not working and use something else to
get the metadata.

> >> Also: I have assorted ogg/mp3/flac in my music library and I tend
> >> to play them in random, which defeats the gapless.
> > 
> > You are a non typical users :-) To test this, we would same track in
> > album. A typical user would be running spotify with same format :-)
> 
> Yes, Spotify will shuffle, but use the same format mostly for the
> random 'files', true.
> 
> >> The gapless support is not covered in this initial version, but can
> >> be added later.
> I will likely need to do this at some point, but again, w/o the
> ability to test it, it is hard to declare victory ;)

I was testing on few formats on Qualcomm platforms and this seemed
to work for them... It is not elegant but got the work done for me back
then.

-- 
~Vinod

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH 19/23] ASoC: SOF: Add support for IPC4 compressed
  2026-09-07  5:15     ` Péter Ujfalusi
@ 2026-09-09  6:48       ` Péter Ujfalusi
  0 siblings, 0 replies; 49+ messages in thread
From: Péter Ujfalusi @ 2026-09-09  6:48 UTC (permalink / raw)
  To: Mark Brown
  Cc: vkoul, perex, tiwai, lgirdwood, srinivas.kandagatla, linux-sound,
	kai.vehmanen, yung-chuan.liao, pierre-louis.bossart,
	daniel.baluta, Vijendar.Mukunda



On 07/09/2026 08:15, Péter Ujfalusi wrote:
> 
> 
> On 04/09/2026 23:08, Mark Brown wrote:
>> On Fri, Sep 04, 2026 at 10:31:30AM +0300, Peter Ujfalusi wrote:
>>> Set and define the compressed ops for IPC4.
>>> The initial implementation supports basic features: PAUSE PUSH/RELEASE,
>>> DRAIN and progress reporting.
>>> Tested with PCM, MP3, AAC and VORBIS codec.
>>
>>> +static int sof_ipc4_compr_set_params(struct snd_soc_component *component,
>>> +				     struct snd_compr_stream *cstream,
>>> +				     struct snd_compr_params *params)
>>> +{
>>
>>> +	/* Use correct format based on the used codec */
>>> +	switch (params->codec.id) {
>>> +	case SND_AUDIOCODEC_PCM:
>>> +		snd_mask_set_format(fmt, (snd_pcm_format_t)params->codec.format);
>>> +		break;
>>
>> Do we validate the value supplied by userspace here?
> 
> On SOF core side we do not, but on the Intel side in
> hda_dsp_compr_hw_params() we might want to fail in case of S24_3LE,
> which is not supported. by hardware.

On a second thought, this is not needed either, we will reject formats
based on the topology information

-- 
Péter


^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH 04/23] ASoC: soc-compress: Implement trigger FE-BE sequencing as with normal PCMs
  2026-09-08 14:54                         ` Pierre-Louis Bossart
@ 2026-09-09 13:53                           ` Péter Ujfalusi
  2026-09-09 20:22                             ` Pierre-Louis Bossart
  0 siblings, 1 reply; 49+ messages in thread
From: Péter Ujfalusi @ 2026-09-09 13:53 UTC (permalink / raw)
  To: Pierre-Louis Bossart, Vinod Koul
  Cc: Mark Brown, perex, tiwai, lgirdwood, srinivas.kandagatla,
	linux-sound, kai.vehmanen, yung-chuan.liao, daniel.baluta,
	Vijendar.Mukunda



On 08/09/2026 17:54, Pierre-Louis Bossart wrote:
> 
>> I see, the issue is that I cannot find any way to give the metadata to
>> the decoder module, it is a black box to us.
> 
> The metadata is typically handled *outside* of the decoder library by a
> memcpy-based wrapper or modulo pointers.
> 
> For example due to the filterbank, the decoder will generate N zero
> samples in the first frame. If you know the value of N thanks to the
> METADATA ioctl, you can discard the first N samples in the wrapper.
> 
> Likewise at the end of the track the wrapper can skip the last M samples
> which were padded to make the initial soundtrack a multiple of the frame
> size (e.g. 1152 for MP3). the value M is the encoder padding.

I got this part, but I have no idea how the wrapper could know this.
It can be only _after_ the decoder to do the fade out/in/mixing, but so
far I have not seen any way to actually pin-point the exact time.
And not just exact time, but T minus something to T plus something.

>> Also I'm not sure how the fw could get any idea where one
>> flac/vorbis/aac/mp3 ends and where it starts, the black box does not
>> seam to be able to detect this or if it does, it will not emit this
>> information to outside.
> I must admit I don't fully remember the details on the start/end
> position. IIRC this isn't strictly required for elementary streams such
> as MP3 or AAC ADTS, you could cat the streams together and that'd be
> just fine. you would have a slightly different transition by blending
> the two streams in a common filterbank history buffer but that'd be
> nearly impossible to detect...

and there is still the monitoring of the progress of let's say 6th track
in the stream.

I was planning to revisit this sooner than later when I have a moment of
'free' time, but for this initial series, it is not promised and should
not work at all.

> But for streams with a header such as WMA not sure how this might work,
> I think you do need to know the buffer position, reset the decoder and
> restart from that position.

Luckily no one has such files anymore ;)

-- 
Péter


^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH 04/23] ASoC: soc-compress: Implement trigger FE-BE sequencing as with normal PCMs
  2026-09-08 17:12                         ` Vinod Koul
@ 2026-09-09 14:09                           ` Péter Ujfalusi
  0 siblings, 0 replies; 49+ messages in thread
From: Péter Ujfalusi @ 2026-09-09 14:09 UTC (permalink / raw)
  To: Vinod Koul
  Cc: Pierre-Louis Bossart, Mark Brown, perex, tiwai, lgirdwood,
	srinivas.kandagatla, linux-sound, kai.vehmanen, yung-chuan.liao,
	daniel.baluta, Vijendar.Mukunda



On 08/09/2026 20:12, Vinod Koul wrote:
>> SOF supports 2 major IPC mode, IPC3 which was used up to TGL family (and
>> by NXP, AMD and MediaTek) and IPC4 which is from MTL onward (with
>> unofficial support for TGL).
>>
>> They have different operating principles and we just got the compress
>> support for IPC4 (so TGL and up) only on Intel platforms.
>>
>> NXP is using the compressed with IPC3.
> 
> aha, joy of changing IPC with SoC! I dont envy you Peter :-)

:D Thanks!

>> Also I'm not sure how the fw could get any idea where one
>> flac/vorbis/aac/mp3 ends and where it starts, the black box does not
>> seam to be able to detect this or if it does, it will not emit this
>> information to outside.
> 
> It is supposed to be filled by media frameworks that parse the file and
> send it down to kernel. That is why in fcplay chose to use libav for
> getting information for few formats I had... I might have some fork in
> my old pc, I will check if I had something more that could help you.
> 
> Feel free to extend it, if this is not working and use something else to
> get the metadata.

I think that part is sort of clear, but my hold up atm is that I have
not yet figured out how this can be handled in FW, what it should do.

>>>> Also: I have assorted ogg/mp3/flac in my music library and I tend
>>>> to play them in random, which defeats the gapless.
>>>
>>> You are a non typical users :-) To test this, we would same track in
>>> album. A typical user would be running spotify with same format :-)
>>
>> Yes, Spotify will shuffle, but use the same format mostly for the
>> random 'files', true.
>>
>>>> The gapless support is not covered in this initial version, but can
>>>> be added later.
>> I will likely need to do this at some point, but again, w/o the
>> ability to test it, it is hard to declare victory ;)
> 
> I was testing on few formats on Qualcomm platforms and this seemed
> to work for them... It is not elegant but got the work done for me back
> then.

And this as well, I have 0 platform where I can see this one working in
real life, none of my Android devices support compress, none of my non
Intel Chromebook does, I don't think the QC firmware is open source...

I'm sure it can be done as the design is clear, but the decoder library
that SOF is using (from Cadence) primarily is not written to support the
features of ALSA compress.
It takes time to figure this out to be of a quality that I can put my
SoB on the patch/feature.

-- 
Péter


^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH 04/23] ASoC: soc-compress: Implement trigger FE-BE sequencing as with normal PCMs
  2026-09-09 13:53                           ` Péter Ujfalusi
@ 2026-09-09 20:22                             ` Pierre-Louis Bossart
  0 siblings, 0 replies; 49+ messages in thread
From: Pierre-Louis Bossart @ 2026-09-09 20:22 UTC (permalink / raw)
  To: Péter Ujfalusi, Vinod Koul
  Cc: Mark Brown, perex, tiwai, lgirdwood, srinivas.kandagatla,
	linux-sound, kai.vehmanen, yung-chuan.liao, daniel.baluta,
	Vijendar.Mukunda


>>> I see, the issue is that I cannot find any way to give the metadata to
>>> the decoder module, it is a black box to us.
>>
>> The metadata is typically handled *outside* of the decoder library by a
>> memcpy-based wrapper or modulo pointers.
>>
>> For example due to the filterbank, the decoder will generate N zero
>> samples in the first frame. If you know the value of N thanks to the
>> METADATA ioctl, you can discard the first N samples in the wrapper.
>>
>> Likewise at the end of the track the wrapper can skip the last M samples
>> which were padded to make the initial soundtrack a multiple of the frame
>> size (e.g. 1152 for MP3). the value M is the encoder padding.
> 
> I got this part, but I have no idea how the wrapper could know this.
> It can be only _after_ the decoder to do the fade out/in/mixing, but so
> far I have not seen any way to actually pin-point the exact time.
> And not just exact time, but T minus something to T plus something.

It's not time you need to track but bytes, and the accuracy of the
next-track position is exactly a byte. If you're off by even one byte,
you probably lost a minimum of one frame until the decoder re-syncs for
an elementary stream, and the whole scheme is down the drain if there's
a header.

Also the decoder has to in a wrapper anyways, even without gapless
playback: the fragments used by the compressed framework may contain
more or less than a frame, so the decoder needs to deal with all cases
of 'no enough data available' and 'more than one frame provided'.
Likewise some decoder may not be able to generate data on initial calls,
and need to be called without new input to full internal PCM buffers.

then the logic should be:

(a) On a new track, for the first decoder you skip the first N samples.

(b) The end of track happens when all the bytes have been provided to
the decoder, and the decoder cannot generate more data. At that point
you would discard the last M padding samples.

(c) if there was a NEXT_TRACK, eset the byte counting for the track and
go back to (a)

That's the high level logic. Now for the positioning, I may be wrong but
I think there is a requirement that the NEXT_TRACK is only signaled when
all the bytes for the current track were provided to the driver. So on
the NEXT_TRACK you would need to record the number of bytes provided so
far by userspace, and forward that information to firmware. Then the
next write would be with the content for the next track. It could also
be the PARTIAL_DRAIN that does this, since at that point no new data
should be provided by userspace. Either way, do no write data in the
ring buffer until the firmware knows the total number of bytes in the
current track. If the app doesn't respect that then the accounting will
go sideways.

The last time I saw DSP code for this was in 2013 or so, take this with
a grain of salt. Vinod, if you have a better memory please do chime in...

>>> Also I'm not sure how the fw could get any idea where one
>>> flac/vorbis/aac/mp3 ends and where it starts, the black box does not
>>> seam to be able to detect this or if it does, it will not emit this
>>> information to outside.
>> I must admit I don't fully remember the details on the start/end
>> position. IIRC this isn't strictly required for elementary streams such
>> as MP3 or AAC ADTS, you could cat the streams together and that'd be
>> just fine. you would have a slightly different transition by blending
>> the two streams in a common filterbank history buffer but that'd be
>> nearly impossible to detect...
> 
> and there is still the monitoring of the progress of let's say 6th track
> in the stream.
> 
> I was planning to revisit this sooner than later when I have a moment of
> 'free' time, but for this initial series, it is not promised and should
> not work at all.
> 
>> But for streams with a header such as WMA not sure how this might work,
>> I think you do need to know the buffer position, reset the decoder and
>> restart from that position.
> 
> Luckily no one has such files anymore ;)

Hehe, the Zune music service only stopped in 2017...

^ permalink raw reply	[flat|nested] 49+ messages in thread

end of thread, other threads:[~2026-09-09 20:22 UTC | newest]

Thread overview: 49+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-04  7:31 [PATCH 00/23] ALSA compress / ASoC compress / SOF: Compressed audio support with IPC4 Peter Ujfalusi
2026-09-04  7:31 ` [PATCH 01/23] ALSA: compress: pin card module while stream is open Peter Ujfalusi
2026-09-04  7:50   ` Takashi Iwai
2026-09-04  8:03     ` Péter Ujfalusi
2026-09-04  7:31 ` [PATCH 02/23] ALSA: compress: stop active streams on disconnect Peter Ujfalusi
2026-09-04  7:51   ` Takashi Iwai
2026-09-04  7:31 ` [PATCH 03/23] ASoC: soc-compress: Provide a runtime for the compressed FE substream Peter Ujfalusi
2026-09-04  7:31 ` [PATCH 04/23] ASoC: soc-compress: Implement trigger FE-BE sequencing as with normal PCMs Peter Ujfalusi
2026-09-04 19:45   ` Mark Brown
2026-09-07  5:25     ` Péter Ujfalusi
2026-09-07 11:26       ` Pierre-Louis Bossart
2026-09-07 14:03         ` Péter Ujfalusi
2026-09-07 16:53           ` Vinod Koul
2026-09-07 19:55             ` Pierre-Louis Bossart
2026-09-08  6:37               ` Péter Ujfalusi
2026-09-08  6:58                 ` Vinod Koul
2026-09-08  8:41                   ` Péter Ujfalusi
2026-09-08 12:06                     ` Pierre-Louis Bossart
2026-09-08 14:04                       ` Péter Ujfalusi
2026-09-08 12:37                     ` Vinod Koul
2026-09-08 14:13                       ` Péter Ujfalusi
2026-09-08 14:54                         ` Pierre-Louis Bossart
2026-09-09 13:53                           ` Péter Ujfalusi
2026-09-09 20:22                             ` Pierre-Louis Bossart
2026-09-08 17:12                         ` Vinod Koul
2026-09-09 14:09                           ` Péter Ujfalusi
2026-09-08  6:52               ` Vinod Koul
2026-09-04  7:31 ` [PATCH 05/23] ASoC: soc-compress: Stop running dpcm on free Peter Ujfalusi
2026-09-04  7:31 ` [PATCH 06/23] ASoC: SOF: compress: Rename compress ops with ipc3 prefix Peter Ujfalusi
2026-09-04  7:31 ` [PATCH 07/23] ASoC: SOF: ipc4-pcm: harden pipeline teardown races Peter Ujfalusi
2026-09-04  7:31 ` [PATCH 08/23] ASoC: SOF: sof-audio: do not dereference swidget->spipe unconditionally on free Peter Ujfalusi
2026-09-04  7:31 ` [PATCH 09/23] ASoC: SOF: sof-audio: Expose a couple of functions Peter Ujfalusi
2026-09-04  7:31 ` [PATCH 10/23] ASoC: SOF: pcm: Modify the signature of a couple of PCM IPC ops Peter Ujfalusi
2026-09-04  7:31 ` [PATCH 11/23] ASoC: SOF: intel: hda-stream: Clear the current position when releasing stream Peter Ujfalusi
2026-09-04  7:31 ` [PATCH 12/23] ASoC: SOF: ops: Add new platform-specific ops for compress Peter Ujfalusi
2026-09-04  7:31 ` [PATCH 13/23] ASoC: SOF: ipc4: Add definition of module data in init_ext object type Peter Ujfalusi
2026-09-04  7:31 ` [PATCH 14/23] ASoC: SOF: ipc4-topology: Support init_ext_module_data for process modules Peter Ujfalusi
2026-09-04  7:31 ` [PATCH 15/23] ASoC: SOF: ipc4-pcm: Make the timestamp info usable outside of ipc4-pcm.c Peter Ujfalusi
2026-09-04  7:31 ` [PATCH 16/23] ASoC: SOF: ipc4/ipc4-loader: Add SOF_INFO and CODEC_INFO to fw_config_params Peter Ujfalusi
2026-09-04  7:31 ` [PATCH 17/23] ASoC: SOF: ipc4-pcm: Handle COMPR DRAIN triggers as EOS pipeline state Peter Ujfalusi
2026-09-04  7:31 ` [PATCH 18/23] ASoC: SOF: ipc4-topology: Set FAST_MODE for host copier in compr mode Peter Ujfalusi
2026-09-04  7:31 ` [PATCH 19/23] ASoC: SOF: Add support for IPC4 compressed Peter Ujfalusi
2026-09-04 20:08   ` Mark Brown
2026-09-07  5:15     ` Péter Ujfalusi
2026-09-09  6:48       ` Péter Ujfalusi
2026-09-04  7:31 ` [PATCH 20/23] ASoC: SOF: ipc4: Handle compressed drain done notification from firmware Peter Ujfalusi
2026-09-04  7:31 ` [PATCH 21/23] ASoC: SOF: Intel: Kconfig: Remove redundant IPC version selects Peter Ujfalusi
2026-09-04  7:31 ` [PATCH 22/23] ASoC: SOF: Intel: Kconfig: Select compress support for TGL+ platforms Peter Ujfalusi
2026-09-04  7:31 ` [PATCH 23/23] ASoC: SOF: topology: Add support for decoder and encoder widgets Peter Ujfalusi

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox