* [PATCH v2 01/24] ALSA: compress: pin card module while stream is open
2026-09-09 9:09 [PATCH v2 00/24] ALSA compress / ASoC compress / SOF: Compressed audio support with IPC4 Peter Ujfalusi
@ 2026-09-09 9:09 ` Peter Ujfalusi
2026-09-09 15:12 ` Takashi Iwai
2026-09-09 9:09 ` [PATCH v2 02/24] ALSA: compress: register the open file with the card Peter Ujfalusi
` (23 subsequent siblings)
24 siblings, 1 reply; 47+ messages in thread
From: Peter Ujfalusi @ 2026-09-09 9:09 UTC (permalink / raw)
To: vkoul, perex, tiwai, lgirdwood, broonie, srinivas.kandagatla
Cc: linux-sound, kai.vehmanen, yung-chuan.liao, pierre-louis.bossart,
daniel.baluta
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>
---
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 7c397b1c9231..ba6cdfe64246 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_obj(*data);
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_obj(*runtime);
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] 47+ messages in thread* Re: [PATCH v2 01/24] ALSA: compress: pin card module while stream is open
2026-09-09 9:09 ` [PATCH v2 01/24] ALSA: compress: pin card module while stream is open Peter Ujfalusi
@ 2026-09-09 15:12 ` Takashi Iwai
0 siblings, 0 replies; 47+ messages in thread
From: Takashi Iwai @ 2026-09-09 15:12 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
On Wed, 09 Sep 2026 11:09:26 +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>
In case this going through Mark's tree:
Reviewed-by: Takashi Iwai <tiwai@suse.de>
thanks,
Takashi
^ permalink raw reply [flat|nested] 47+ messages in thread
* [PATCH v2 02/24] ALSA: compress: register the open file with the card
2026-09-09 9:09 [PATCH v2 00/24] ALSA compress / ASoC compress / SOF: Compressed audio support with IPC4 Peter Ujfalusi
2026-09-09 9:09 ` [PATCH v2 01/24] ALSA: compress: pin card module while stream is open Peter Ujfalusi
@ 2026-09-09 9:09 ` Peter Ujfalusi
2026-09-09 15:12 ` Takashi Iwai
2026-09-09 9:09 ` [PATCH v2 03/24] ALSA: compress: stop active streams on disconnect Peter Ujfalusi
` (22 subsequent siblings)
24 siblings, 1 reply; 47+ messages in thread
From: Peter Ujfalusi @ 2026-09-09 9:09 UTC (permalink / raw)
To: vkoul, perex, tiwai, lgirdwood, broonie, srinivas.kandagatla
Cc: linux-sound, kai.vehmanen, yung-chuan.liao, pierre-louis.bossart,
daniel.baluta
The compress-offload device is the only ALSA character device family
which does not add its open files to the card's file list. Without it
the core cannot replace the file operations on disconnect, so syscalls
issued after the card is gone still reach the driver, and a card freed
with snd_card_free_when_closed() does not wait for the open compress
streams.
Call snd_card_file_add() at open and snd_card_file_remove() at release,
following the ordering used by the PCM, control, rawmidi and hwdep
devices.
Suggested-by: Takashi Iwai <tiwai@suse.de>
Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
---
sound/core/compress_offload.c | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/sound/core/compress_offload.c b/sound/core/compress_offload.c
index ba6cdfe64246..7b720603a9a8 100644
--- a/sound/core/compress_offload.c
+++ b/sound/core/compress_offload.c
@@ -101,7 +101,14 @@ static int snd_compr_open(struct inode *inode, struct file *f)
return -ENODEV;
}
+ ret = snd_card_file_add(compr->card, f);
+ if (ret < 0) {
+ snd_card_unref(compr->card);
+ return ret;
+ }
+
if (!try_module_get(compr->card->module)) {
+ snd_card_file_remove(compr->card, f);
snd_card_unref(compr->card);
return -EFAULT;
}
@@ -144,6 +151,7 @@ static int snd_compr_open(struct inode *inode, struct file *f)
kfree(runtime);
kfree(data);
module_put(compr->card->module);
+ snd_card_file_remove(compr->card, f);
}
snd_card_unref(compr->card);
return ret;
@@ -174,6 +182,7 @@ static int snd_compr_free(struct inode *inode, struct file *f)
module_put(data->stream.device->card->module);
kfree(data->stream.runtime);
kfree(data);
+ snd_card_file_remove(compr->card, f);
return 0;
}
--
2.55.0
^ permalink raw reply related [flat|nested] 47+ messages in thread* Re: [PATCH v2 02/24] ALSA: compress: register the open file with the card
2026-09-09 9:09 ` [PATCH v2 02/24] ALSA: compress: register the open file with the card Peter Ujfalusi
@ 2026-09-09 15:12 ` Takashi Iwai
0 siblings, 0 replies; 47+ messages in thread
From: Takashi Iwai @ 2026-09-09 15:12 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
On Wed, 09 Sep 2026 11:09:27 +0200,
Peter Ujfalusi wrote:
>
> The compress-offload device is the only ALSA character device family
> which does not add its open files to the card's file list. Without it
> the core cannot replace the file operations on disconnect, so syscalls
> issued after the card is gone still reach the driver, and a card freed
> with snd_card_free_when_closed() does not wait for the open compress
> streams.
>
> Call snd_card_file_add() at open and snd_card_file_remove() at release,
> following the ordering used by the PCM, control, rawmidi and hwdep
> devices.
>
> Suggested-by: Takashi Iwai <tiwai@suse.de>
> Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
Reviewed-by: Takashi Iwai <tiwai@suse.de>
thanks,
Takashi
^ permalink raw reply [flat|nested] 47+ messages in thread
* [PATCH v2 03/24] ALSA: compress: stop active streams on disconnect
2026-09-09 9:09 [PATCH v2 00/24] ALSA compress / ASoC compress / SOF: Compressed audio support with IPC4 Peter Ujfalusi
2026-09-09 9:09 ` [PATCH v2 01/24] ALSA: compress: pin card module while stream is open Peter Ujfalusi
2026-09-09 9:09 ` [PATCH v2 02/24] ALSA: compress: register the open file with the card Peter Ujfalusi
@ 2026-09-09 9:09 ` Peter Ujfalusi
2026-09-09 9:09 ` [PATCH v2 04/24] ASoC: soc-compress: Provide a runtime for the compressed FE substream Peter Ujfalusi
` (21 subsequent siblings)
24 siblings, 0 replies; 47+ messages in thread
From: Peter Ujfalusi @ 2026-09-09 9:09 UTC (permalink / raw)
To: vkoul, perex, tiwai, lgirdwood, broonie, srinivas.kandagatla
Cc: linux-sound, kai.vehmanen, yung-chuan.liao, pierre-louis.bossart,
daniel.baluta
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>
---
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 7b720603a9a8..89435e4394ef 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;
};
@@ -124,6 +125,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);
@@ -143,8 +145,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) {
@@ -161,17 +166,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);
@@ -1449,8 +1460,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;
}
@@ -1558,6 +1588,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] 47+ messages in thread* [PATCH v2 04/24] ASoC: soc-compress: Provide a runtime for the compressed FE substream
2026-09-09 9:09 [PATCH v2 00/24] ALSA compress / ASoC compress / SOF: Compressed audio support with IPC4 Peter Ujfalusi
` (2 preceding siblings ...)
2026-09-09 9:09 ` [PATCH v2 03/24] ALSA: compress: stop active streams on disconnect Peter Ujfalusi
@ 2026-09-09 9:09 ` Peter Ujfalusi
2026-09-09 9:09 ` [PATCH v2 05/24] ASoC: soc-compress: Implement trigger FE-BE sequencing as with normal PCMs Peter Ujfalusi
` (20 subsequent siblings)
24 siblings, 0 replies; 47+ messages in thread
From: Peter Ujfalusi @ 2026-09-09 9:09 UTC (permalink / raw)
To: vkoul, perex, tiwai, lgirdwood, broonie, srinivas.kandagatla
Cc: linux-sound, kai.vehmanen, yung-chuan.liao, pierre-louis.bossart,
daniel.baluta
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] 47+ messages in thread* [PATCH v2 05/24] ASoC: soc-compress: Implement trigger FE-BE sequencing as with normal PCMs
2026-09-09 9:09 [PATCH v2 00/24] ALSA compress / ASoC compress / SOF: Compressed audio support with IPC4 Peter Ujfalusi
` (3 preceding siblings ...)
2026-09-09 9:09 ` [PATCH v2 04/24] ASoC: soc-compress: Provide a runtime for the compressed FE substream Peter Ujfalusi
@ 2026-09-09 9:09 ` Peter Ujfalusi
2026-09-09 9:09 ` [PATCH v2 06/24] ASoC: soc-compress: Stop running dpcm on free Peter Ujfalusi
` (19 subsequent siblings)
24 siblings, 0 replies; 47+ messages in thread
From: Peter Ujfalusi @ 2026-09-09 9:09 UTC (permalink / raw)
To: vkoul, perex, tiwai, lgirdwood, broonie, srinivas.kandagatla
Cc: linux-sound, kai.vehmanen, yung-chuan.liao, pierre-louis.bossart,
daniel.baluta
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 | 81 ++++++++++++++++++++++++++++++++++------
1 file changed, 70 insertions(+), 11 deletions(-)
diff --git a/sound/soc/soc-compress.c b/sound/soc/soc-compress.c
index e5a70c9b3d31..e9f6ab8c1678 100644
--- a/sound/soc/soc-compress.c
+++ b/sound/soc/soc-compress.c
@@ -380,31 +380,90 @@ 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)
+ /* FE only triggers */
+ switch (cmd) {
+ case SND_COMPR_TRIGGER_PARTIAL_DRAIN:
+ case SND_COMPR_TRIGGER_NEXT_TRACK:
+ case SND_COMPR_TRIGGER_DRAIN:
return snd_soc_component_compr_trigger(cstream, cmd);
+ default:
+ break;
+ }
+
+ 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] 47+ messages in thread* [PATCH v2 06/24] ASoC: soc-compress: Stop running dpcm on free
2026-09-09 9:09 [PATCH v2 00/24] ALSA compress / ASoC compress / SOF: Compressed audio support with IPC4 Peter Ujfalusi
` (4 preceding siblings ...)
2026-09-09 9:09 ` [PATCH v2 05/24] ASoC: soc-compress: Implement trigger FE-BE sequencing as with normal PCMs Peter Ujfalusi
@ 2026-09-09 9:09 ` Peter Ujfalusi
2026-09-09 9:09 ` [PATCH v2 07/24] ASoC: SOF: compress: Rename compress ops with ipc3 prefix Peter Ujfalusi
` (18 subsequent siblings)
24 siblings, 0 replies; 47+ messages in thread
From: Peter Ujfalusi @ 2026-09-09 9:09 UTC (permalink / raw)
To: vkoul, perex, tiwai, lgirdwood, broonie, srinivas.kandagatla
Cc: linux-sound, kai.vehmanen, yung-chuan.liao, pierre-louis.bossart,
daniel.baluta
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 e9f6ab8c1678..3e1c11c788ef 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] 47+ messages in thread* [PATCH v2 07/24] ASoC: SOF: compress: Rename compress ops with ipc3 prefix
2026-09-09 9:09 [PATCH v2 00/24] ALSA compress / ASoC compress / SOF: Compressed audio support with IPC4 Peter Ujfalusi
` (5 preceding siblings ...)
2026-09-09 9:09 ` [PATCH v2 06/24] ASoC: soc-compress: Stop running dpcm on free Peter Ujfalusi
@ 2026-09-09 9:09 ` Peter Ujfalusi
2026-09-09 12:34 ` Pierre-Louis Bossart
2026-09-09 9:09 ` [PATCH v2 08/24] ASoC: SOF: ipc4-pcm: harden pipeline teardown races Peter Ujfalusi
` (17 subsequent siblings)
24 siblings, 1 reply; 47+ messages in thread
From: Peter Ujfalusi @ 2026-09-09 9:09 UTC (permalink / raw)
To: vkoul, perex, tiwai, lgirdwood, broonie, srinivas.kandagatla
Cc: linux-sound, kai.vehmanen, yung-chuan.liao, pierre-louis.bossart,
daniel.baluta
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] 47+ messages in thread* Re: [PATCH v2 07/24] ASoC: SOF: compress: Rename compress ops with ipc3 prefix
2026-09-09 9:09 ` [PATCH v2 07/24] ASoC: SOF: compress: Rename compress ops with ipc3 prefix Peter Ujfalusi
@ 2026-09-09 12:34 ` Pierre-Louis Bossart
2026-09-10 14:13 ` Péter Ujfalusi
0 siblings, 1 reply; 47+ messages in thread
From: Pierre-Louis Bossart @ 2026-09-09 12:34 UTC (permalink / raw)
To: Peter Ujfalusi, vkoul, perex, tiwai, lgirdwood, broonie,
srinivas.kandagatla
Cc: linux-sound, kai.vehmanen, yung-chuan.liao, daniel.baluta
On 9/9/26 11:09, Peter Ujfalusi wrote:
> 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%)
>
this looks like a trivial code move:
> -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);
> -}
...
> +#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
but then there is also the addition of new ops and renames.
This patch does a lot of things, most of them trivial but overall it's a
lot to review.
maybe split this patch in two with the code move in a first patch, then
the file rename and introduction of new ops?
^ permalink raw reply [flat|nested] 47+ messages in thread* Re: [PATCH v2 07/24] ASoC: SOF: compress: Rename compress ops with ipc3 prefix
2026-09-09 12:34 ` Pierre-Louis Bossart
@ 2026-09-10 14:13 ` Péter Ujfalusi
0 siblings, 0 replies; 47+ messages in thread
From: Péter Ujfalusi @ 2026-09-10 14:13 UTC (permalink / raw)
To: Pierre-Louis Bossart, vkoul, perex, tiwai, lgirdwood, broonie,
srinivas.kandagatla
Cc: linux-sound, kai.vehmanen, yung-chuan.liao, daniel.baluta
On 09/09/2026 15:34, Pierre-Louis Bossart wrote:
> On 9/9/26 11:09, Peter Ujfalusi wrote:
>> 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%)
>>
>
> this looks like a trivial code move:
Yes, it is mostly just that.
...>> +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
>
> but then there is also the addition of new ops and renames.
>
> This patch does a lot of things, most of them trivial but overall it's a
> lot to review.
>
> maybe split this patch in two with the code move in a first patch, then
> the file rename and introduction of new ops?
OK, I will split it up, move these first then rename and the mandatory
changes along in core as second patch.
--
Péter
^ permalink raw reply [flat|nested] 47+ messages in thread
* [PATCH v2 08/24] ASoC: SOF: ipc4-pcm: harden pipeline teardown races
2026-09-09 9:09 [PATCH v2 00/24] ALSA compress / ASoC compress / SOF: Compressed audio support with IPC4 Peter Ujfalusi
` (6 preceding siblings ...)
2026-09-09 9:09 ` [PATCH v2 07/24] ASoC: SOF: compress: Rename compress ops with ipc3 prefix Peter Ujfalusi
@ 2026-09-09 9:09 ` Peter Ujfalusi
2026-09-09 12:37 ` Pierre-Louis Bossart
2026-09-09 9:09 ` [PATCH v2 09/24] ASoC: SOF: sof-audio: do not dereference swidget->spipe unconditionally on free Peter Ujfalusi
` (16 subsequent siblings)
24 siblings, 1 reply; 47+ messages in thread
From: Peter Ujfalusi @ 2026-09-09 9:09 UTC (permalink / raw)
To: vkoul, perex, tiwai, lgirdwood, broonie, srinivas.kandagatla
Cc: linux-sound, kai.vehmanen, yung-chuan.liao, pierre-louis.bossart,
daniel.baluta
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] 47+ messages in thread* Re: [PATCH v2 08/24] ASoC: SOF: ipc4-pcm: harden pipeline teardown races
2026-09-09 9:09 ` [PATCH v2 08/24] ASoC: SOF: ipc4-pcm: harden pipeline teardown races Peter Ujfalusi
@ 2026-09-09 12:37 ` Pierre-Louis Bossart
0 siblings, 0 replies; 47+ messages in thread
From: Pierre-Louis Bossart @ 2026-09-09 12:37 UTC (permalink / raw)
To: Peter Ujfalusi, vkoul, perex, tiwai, lgirdwood, broonie,
srinivas.kandagatla
Cc: linux-sound, kai.vehmanen, yung-chuan.liao, daniel.baluta
On 9/9/26 11:09, Peter Ujfalusi wrote:
> 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(-)
not clear to me what this has to do with compress-offload?
Can this be moved earlier in this patchset, or even submitted separately?
> 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);
^ permalink raw reply [flat|nested] 47+ messages in thread
* [PATCH v2 09/24] ASoC: SOF: sof-audio: do not dereference swidget->spipe unconditionally on free
2026-09-09 9:09 [PATCH v2 00/24] ALSA compress / ASoC compress / SOF: Compressed audio support with IPC4 Peter Ujfalusi
` (7 preceding siblings ...)
2026-09-09 9:09 ` [PATCH v2 08/24] ASoC: SOF: ipc4-pcm: harden pipeline teardown races Peter Ujfalusi
@ 2026-09-09 9:09 ` Peter Ujfalusi
2026-09-09 12:39 ` Pierre-Louis Bossart
2026-09-09 9:09 ` [PATCH v2 10/24] ASoC: SOF: sof-audio: Expose a couple of functions Peter Ujfalusi
` (15 subsequent siblings)
24 siblings, 1 reply; 47+ messages in thread
From: Peter Ujfalusi @ 2026-09-09 9:09 UTC (permalink / raw)
To: vkoul, perex, tiwai, lgirdwood, broonie, srinivas.kandagatla
Cc: linux-sound, kai.vehmanen, yung-chuan.liao, pierre-louis.bossart,
daniel.baluta
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] 47+ messages in thread* Re: [PATCH v2 09/24] ASoC: SOF: sof-audio: do not dereference swidget->spipe unconditionally on free
2026-09-09 9:09 ` [PATCH v2 09/24] ASoC: SOF: sof-audio: do not dereference swidget->spipe unconditionally on free Peter Ujfalusi
@ 2026-09-09 12:39 ` Pierre-Louis Bossart
0 siblings, 0 replies; 47+ messages in thread
From: Pierre-Louis Bossart @ 2026-09-09 12:39 UTC (permalink / raw)
To: Peter Ujfalusi, vkoul, perex, tiwai, lgirdwood, broonie,
srinivas.kandagatla
Cc: linux-sound, kai.vehmanen, yung-chuan.liao, daniel.baluta
On 9/9/26 11:09, Peter Ujfalusi wrote:
> 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(-)
same, this doesn't seem directly related to compresss offload. Maybe
it's required because of new transitions?
I'd move this earlier or submit as a cleanup separately to reduce the
volume of reviews (24 patches!)
> 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;
> }
^ permalink raw reply [flat|nested] 47+ messages in thread
* [PATCH v2 10/24] ASoC: SOF: sof-audio: Expose a couple of functions
2026-09-09 9:09 [PATCH v2 00/24] ALSA compress / ASoC compress / SOF: Compressed audio support with IPC4 Peter Ujfalusi
` (8 preceding siblings ...)
2026-09-09 9:09 ` [PATCH v2 09/24] ASoC: SOF: sof-audio: do not dereference swidget->spipe unconditionally on free Peter Ujfalusi
@ 2026-09-09 9:09 ` Peter Ujfalusi
2026-09-09 12:41 ` Pierre-Louis Bossart
2026-09-09 9:09 ` [PATCH v2 11/24] ASoC: SOF: pcm: Modify the signature of a couple of PCM IPC ops Peter Ujfalusi
` (14 subsequent siblings)
24 siblings, 1 reply; 47+ messages in thread
From: Peter Ujfalusi @ 2026-09-09 9:09 UTC (permalink / raw)
To: vkoul, perex, tiwai, lgirdwood, broonie, srinivas.kandagatla
Cc: linux-sound, kai.vehmanen, yung-chuan.liao, pierre-louis.bossart,
daniel.baluta
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] 47+ messages in thread* Re: [PATCH v2 10/24] ASoC: SOF: sof-audio: Expose a couple of functions
2026-09-09 9:09 ` [PATCH v2 10/24] ASoC: SOF: sof-audio: Expose a couple of functions Peter Ujfalusi
@ 2026-09-09 12:41 ` Pierre-Louis Bossart
0 siblings, 0 replies; 47+ messages in thread
From: Pierre-Louis Bossart @ 2026-09-09 12:41 UTC (permalink / raw)
To: Peter Ujfalusi, vkoul, perex, tiwai, lgirdwood, broonie,
srinivas.kandagatla
Cc: linux-sound, kai.vehmanen, yung-chuan.liao, daniel.baluta
On 9/9/26 11:09, Peter Ujfalusi wrote:
> 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(-)
same, move to separate trivial preparation patchset
> 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.
^ permalink raw reply [flat|nested] 47+ messages in thread
* [PATCH v2 11/24] ASoC: SOF: pcm: Modify the signature of a couple of PCM IPC ops
2026-09-09 9:09 [PATCH v2 00/24] ALSA compress / ASoC compress / SOF: Compressed audio support with IPC4 Peter Ujfalusi
` (9 preceding siblings ...)
2026-09-09 9:09 ` [PATCH v2 10/24] ASoC: SOF: sof-audio: Expose a couple of functions Peter Ujfalusi
@ 2026-09-09 9:09 ` Peter Ujfalusi
2026-09-09 12:43 ` Pierre-Louis Bossart
2026-09-09 9:09 ` [PATCH v2 12/24] ASoC: SOF: intel: hda-stream: Clear the current position when releasing stream Peter Ujfalusi
` (13 subsequent siblings)
24 siblings, 1 reply; 47+ messages in thread
From: Peter Ujfalusi @ 2026-09-09 9:09 UTC (permalink / raw)
To: vkoul, perex, tiwai, lgirdwood, broonie, srinivas.kandagatla
Cc: linux-sound, kai.vehmanen, yung-chuan.liao, pierre-louis.bossart,
daniel.baluta
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] 47+ messages in thread* Re: [PATCH v2 11/24] ASoC: SOF: pcm: Modify the signature of a couple of PCM IPC ops
2026-09-09 9:09 ` [PATCH v2 11/24] ASoC: SOF: pcm: Modify the signature of a couple of PCM IPC ops Peter Ujfalusi
@ 2026-09-09 12:43 ` Pierre-Louis Bossart
0 siblings, 0 replies; 47+ messages in thread
From: Pierre-Louis Bossart @ 2026-09-09 12:43 UTC (permalink / raw)
To: Peter Ujfalusi, vkoul, perex, tiwai, lgirdwood, broonie,
srinivas.kandagatla
Cc: linux-sound, kai.vehmanen, yung-chuan.liao, daniel.baluta
On 9/9/26 11:09, Peter Ujfalusi wrote:
> 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(-)
also a prerequisite patch that should be submitted separately before the
bigger changes.
>
> 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);
^ permalink raw reply [flat|nested] 47+ messages in thread
* [PATCH v2 12/24] ASoC: SOF: intel: hda-stream: Clear the current position when releasing stream
2026-09-09 9:09 [PATCH v2 00/24] ALSA compress / ASoC compress / SOF: Compressed audio support with IPC4 Peter Ujfalusi
` (10 preceding siblings ...)
2026-09-09 9:09 ` [PATCH v2 11/24] ASoC: SOF: pcm: Modify the signature of a couple of PCM IPC ops Peter Ujfalusi
@ 2026-09-09 9:09 ` Peter Ujfalusi
2026-09-09 12:45 ` Pierre-Louis Bossart
2026-09-09 9:09 ` [PATCH v2 13/24] ASoC: SOF: ops: Add new platform-specific ops for compress Peter Ujfalusi
` (12 subsequent siblings)
24 siblings, 1 reply; 47+ messages in thread
From: Peter Ujfalusi @ 2026-09-09 9:09 UTC (permalink / raw)
To: vkoul, perex, tiwai, lgirdwood, broonie, srinivas.kandagatla
Cc: linux-sound, kai.vehmanen, yung-chuan.liao, pierre-louis.bossart,
daniel.baluta
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] 47+ messages in thread* Re: [PATCH v2 12/24] ASoC: SOF: intel: hda-stream: Clear the current position when releasing stream
2026-09-09 9:09 ` [PATCH v2 12/24] ASoC: SOF: intel: hda-stream: Clear the current position when releasing stream Peter Ujfalusi
@ 2026-09-09 12:45 ` Pierre-Louis Bossart
2026-09-11 6:31 ` Péter Ujfalusi
0 siblings, 1 reply; 47+ messages in thread
From: Pierre-Louis Bossart @ 2026-09-09 12:45 UTC (permalink / raw)
To: Peter Ujfalusi, vkoul, perex, tiwai, lgirdwood, broonie,
srinivas.kandagatla
Cc: linux-sound, kai.vehmanen, yung-chuan.liao, daniel.baluta
On 9/9/26 11:09, Peter Ujfalusi wrote:
> 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(+)
move to preparation patch to reduce the volume of this patchset?
> 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)) {
^ permalink raw reply [flat|nested] 47+ messages in thread* Re: [PATCH v2 12/24] ASoC: SOF: intel: hda-stream: Clear the current position when releasing stream
2026-09-09 12:45 ` Pierre-Louis Bossart
@ 2026-09-11 6:31 ` Péter Ujfalusi
0 siblings, 0 replies; 47+ messages in thread
From: Péter Ujfalusi @ 2026-09-11 6:31 UTC (permalink / raw)
To: Pierre-Louis Bossart, vkoul, perex, tiwai, lgirdwood, broonie,
srinivas.kandagatla
Cc: linux-sound, kai.vehmanen, yung-chuan.liao, daniel.baluta
On 09/09/2026 15:45, Pierre-Louis Bossart wrote:
> On 9/9/26 11:09, Peter Ujfalusi wrote:
>> 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(+)
>
> move to preparation patch to reduce the volume of this patchset?
The reason I kept these together instead of sending them separately is
that on their own they lack the context: all of them are needed to get
compress working via IPC4 in a more open ecosystem.
The ALSA and soc-compress patches alone can be NAKed on the grounds that
things work right now - that's my assumption at least.
The SOF preparatory patches are in the same bucket: things work fine,
why do the churn?
In the context of compress they make sense, as there are several system
level differences between how PCM and compress work and the code we have
is perfectly fine as long as it is confined to PCM.
Splitting also raises a dependency tracking question between the series.
I can do the split if you prefer, but the flip side is that the core
changes would land without the users that justify them. FWIW the series
has been tested with IPC3 as well, so the soc-compress changes are not
Intel/IPC4 only in practice.
>
>> 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)) {
>
--
Péter
^ permalink raw reply [flat|nested] 47+ messages in thread
* [PATCH v2 13/24] ASoC: SOF: ops: Add new platform-specific ops for compress
2026-09-09 9:09 [PATCH v2 00/24] ALSA compress / ASoC compress / SOF: Compressed audio support with IPC4 Peter Ujfalusi
` (11 preceding siblings ...)
2026-09-09 9:09 ` [PATCH v2 12/24] ASoC: SOF: intel: hda-stream: Clear the current position when releasing stream Peter Ujfalusi
@ 2026-09-09 9:09 ` Peter Ujfalusi
2026-09-09 14:23 ` Pierre-Louis Bossart
2026-09-09 9:09 ` [PATCH v2 14/24] ASoC: SOF: ipc4: Add definition of module data in init_ext object type Peter Ujfalusi
` (11 subsequent siblings)
24 siblings, 1 reply; 47+ messages in thread
From: Peter Ujfalusi @ 2026-09-09 9:09 UTC (permalink / raw)
To: vkoul, perex, tiwai, lgirdwood, broonie, srinivas.kandagatla
Cc: linux-sound, kai.vehmanen, yung-chuan.liao, pierre-louis.bossart,
daniel.baluta
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 = ¶ms->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] 47+ messages in thread* Re: [PATCH v2 13/24] ASoC: SOF: ops: Add new platform-specific ops for compress
2026-09-09 9:09 ` [PATCH v2 13/24] ASoC: SOF: ops: Add new platform-specific ops for compress Peter Ujfalusi
@ 2026-09-09 14:23 ` Pierre-Louis Bossart
2026-09-10 14:25 ` Péter Ujfalusi
0 siblings, 1 reply; 47+ messages in thread
From: Pierre-Louis Bossart @ 2026-09-09 14:23 UTC (permalink / raw)
To: Peter Ujfalusi, vkoul, perex, tiwai, lgirdwood, broonie,
srinivas.kandagatla
Cc: linux-sound, kai.vehmanen, yung-chuan.liao, daniel.baluta
On 9/9/26 11:09, Peter Ujfalusi wrote:
> 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(-)
this is added 'too-early', there are additional changes in
topology/timestamping that could be applied earlier. Maybe move later
when actual IPC4 compressed support is added?
>
> 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 = ¶ms->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).
^ permalink raw reply [flat|nested] 47+ messages in thread* Re: [PATCH v2 13/24] ASoC: SOF: ops: Add new platform-specific ops for compress
2026-09-09 14:23 ` Pierre-Louis Bossart
@ 2026-09-10 14:25 ` Péter Ujfalusi
0 siblings, 0 replies; 47+ messages in thread
From: Péter Ujfalusi @ 2026-09-10 14:25 UTC (permalink / raw)
To: Pierre-Louis Bossart, vkoul, perex, tiwai, lgirdwood, broonie,
srinivas.kandagatla
Cc: linux-sound, kai.vehmanen, yung-chuan.liao, daniel.baluta
On 09/09/2026 17:23, Pierre-Louis Bossart wrote:
> On 9/9/26 11:09, Peter Ujfalusi wrote:
>> 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(-)
>
> this is added 'too-early', there are additional changes in
> topology/timestamping that could be applied earlier. Maybe move later
> when actual IPC4 compressed support is added?
It depends on the definition of too-early, but sure, I can move it up
just before the ipc4-compress patch.
This actually marked the 'substance' patches in SOF territory to add the
compress support for IPC4.
--
Péter
^ permalink raw reply [flat|nested] 47+ messages in thread
* [PATCH v2 14/24] ASoC: SOF: ipc4: Add definition of module data in init_ext object type
2026-09-09 9:09 [PATCH v2 00/24] ALSA compress / ASoC compress / SOF: Compressed audio support with IPC4 Peter Ujfalusi
` (12 preceding siblings ...)
2026-09-09 9:09 ` [PATCH v2 13/24] ASoC: SOF: ops: Add new platform-specific ops for compress Peter Ujfalusi
@ 2026-09-09 9:09 ` Peter Ujfalusi
2026-09-09 12:49 ` Pierre-Louis Bossart
2026-09-09 9:09 ` [PATCH v2 15/24] ASoC: SOF: ipc4-topology: Support init_ext_module_data for process modules Peter Ujfalusi
` (10 subsequent siblings)
24 siblings, 1 reply; 47+ messages in thread
From: Peter Ujfalusi @ 2026-09-09 9:09 UTC (permalink / raw)
To: vkoul, perex, tiwai, lgirdwood, broonie, srinivas.kandagatla
Cc: linux-sound, kai.vehmanen, yung-chuan.liao, pierre-louis.bossart,
daniel.baluta
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>
---
| 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
--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] 47+ messages in thread* Re: [PATCH v2 14/24] ASoC: SOF: ipc4: Add definition of module data in init_ext object type
2026-09-09 9:09 ` [PATCH v2 14/24] ASoC: SOF: ipc4: Add definition of module data in init_ext object type Peter Ujfalusi
@ 2026-09-09 12:49 ` Pierre-Louis Bossart
0 siblings, 0 replies; 47+ messages in thread
From: Pierre-Louis Bossart @ 2026-09-09 12:49 UTC (permalink / raw)
To: Peter Ujfalusi, vkoul, perex, tiwai, lgirdwood, broonie,
srinivas.kandagatla
Cc: linux-sound, kai.vehmanen, yung-chuan.liao, daniel.baluta
On 9/9/26 11:09, Peter Ujfalusi wrote:
> The SOF_IPC4_MOD_INIT_DATA_ID_MODULE_DATA type within the module_init_ext
> area is module specific init data.
Not sure if this is cryptic or hollow...
Maybe reword as "add a new IPC4 module-specific object type to do xyz">
> 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 */
^ permalink raw reply [flat|nested] 47+ messages in thread
* [PATCH v2 15/24] ASoC: SOF: ipc4-topology: Support init_ext_module_data for process modules
2026-09-09 9:09 [PATCH v2 00/24] ALSA compress / ASoC compress / SOF: Compressed audio support with IPC4 Peter Ujfalusi
` (13 preceding siblings ...)
2026-09-09 9:09 ` [PATCH v2 14/24] ASoC: SOF: ipc4: Add definition of module data in init_ext object type Peter Ujfalusi
@ 2026-09-09 9:09 ` Peter Ujfalusi
2026-09-09 9:09 ` [PATCH v2 16/24] ASoC: SOF: ipc4-pcm: Make the timestamp info usable outside of ipc4-pcm.c Peter Ujfalusi
` (9 subsequent siblings)
24 siblings, 0 replies; 47+ messages in thread
From: Peter Ujfalusi @ 2026-09-09 9:09 UTC (permalink / raw)
To: vkoul, perex, tiwai, lgirdwood, broonie, srinivas.kandagatla
Cc: linux-sound, kai.vehmanen, yung-chuan.liao, pierre-louis.bossart,
daniel.baluta
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] 47+ messages in thread* [PATCH v2 16/24] ASoC: SOF: ipc4-pcm: Make the timestamp info usable outside of ipc4-pcm.c
2026-09-09 9:09 [PATCH v2 00/24] ALSA compress / ASoC compress / SOF: Compressed audio support with IPC4 Peter Ujfalusi
` (14 preceding siblings ...)
2026-09-09 9:09 ` [PATCH v2 15/24] ASoC: SOF: ipc4-topology: Support init_ext_module_data for process modules Peter Ujfalusi
@ 2026-09-09 9:09 ` Peter Ujfalusi
2026-09-09 9:09 ` [PATCH v2 17/24] ASoC: SOF: ipc4/ipc4-loader: Add SOF_INFO and CODEC_INFO to fw_config_params Peter Ujfalusi
` (8 subsequent siblings)
24 siblings, 0 replies; 47+ messages in thread
From: Peter Ujfalusi @ 2026-09-09 9:09 UTC (permalink / raw)
To: vkoul, perex, tiwai, lgirdwood, broonie, srinivas.kandagatla
Cc: linux-sound, kai.vehmanen, yung-chuan.liao, pierre-louis.bossart,
daniel.baluta
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] 47+ messages in thread* [PATCH v2 17/24] ASoC: SOF: ipc4/ipc4-loader: Add SOF_INFO and CODEC_INFO to fw_config_params
2026-09-09 9:09 [PATCH v2 00/24] ALSA compress / ASoC compress / SOF: Compressed audio support with IPC4 Peter Ujfalusi
` (15 preceding siblings ...)
2026-09-09 9:09 ` [PATCH v2 16/24] ASoC: SOF: ipc4-pcm: Make the timestamp info usable outside of ipc4-pcm.c Peter Ujfalusi
@ 2026-09-09 9:09 ` Peter Ujfalusi
2026-09-09 9:09 ` [PATCH v2 18/24] ASoC: SOF: ipc4-pcm: Handle COMPR DRAIN triggers as EOS pipeline state Peter Ujfalusi
` (7 subsequent siblings)
24 siblings, 0 replies; 47+ messages in thread
From: Peter Ujfalusi @ 2026-09-09 9:09 UTC (permalink / raw)
To: vkoul, perex, tiwai, lgirdwood, broonie, srinivas.kandagatla
Cc: linux-sound, kai.vehmanen, yung-chuan.liao, pierre-louis.bossart,
daniel.baluta
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>
---
| 18 ++++++++----
sound/soc/sof/ipc4-loader.c | 51 +++++++++++++++++++++++++++++++++
sound/soc/sof/ipc4-priv.h | 4 +++
3 files changed, 67 insertions(+), 6 deletions(-)
--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] 47+ messages in thread* [PATCH v2 18/24] ASoC: SOF: ipc4-pcm: Handle COMPR DRAIN triggers as EOS pipeline state
2026-09-09 9:09 [PATCH v2 00/24] ALSA compress / ASoC compress / SOF: Compressed audio support with IPC4 Peter Ujfalusi
` (16 preceding siblings ...)
2026-09-09 9:09 ` [PATCH v2 17/24] ASoC: SOF: ipc4/ipc4-loader: Add SOF_INFO and CODEC_INFO to fw_config_params Peter Ujfalusi
@ 2026-09-09 9:09 ` Peter Ujfalusi
2026-09-09 9:09 ` [PATCH v2 19/24] ASoC: SOF: ipc4-topology: Set FAST_MODE for host copier in compr mode Peter Ujfalusi
` (6 subsequent siblings)
24 siblings, 0 replies; 47+ messages in thread
From: Peter Ujfalusi @ 2026-09-09 9:09 UTC (permalink / raw)
To: vkoul, perex, tiwai, lgirdwood, broonie, srinivas.kandagatla
Cc: linux-sound, kai.vehmanen, yung-chuan.liao, pierre-louis.bossart,
daniel.baluta
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] 47+ messages in thread* [PATCH v2 19/24] ASoC: SOF: ipc4-topology: Set FAST_MODE for host copier in compr mode
2026-09-09 9:09 [PATCH v2 00/24] ALSA compress / ASoC compress / SOF: Compressed audio support with IPC4 Peter Ujfalusi
` (17 preceding siblings ...)
2026-09-09 9:09 ` [PATCH v2 18/24] ASoC: SOF: ipc4-pcm: Handle COMPR DRAIN triggers as EOS pipeline state Peter Ujfalusi
@ 2026-09-09 9:09 ` Peter Ujfalusi
2026-09-09 13:21 ` Pierre-Louis Bossart
2026-09-09 9:09 ` [PATCH v2 20/24] ASoC: SOF: Add support for IPC4 compressed Peter Ujfalusi
` (5 subsequent siblings)
24 siblings, 1 reply; 47+ messages in thread
From: Peter Ujfalusi @ 2026-09-09 9:09 UTC (permalink / raw)
To: vkoul, perex, tiwai, lgirdwood, broonie, srinivas.kandagatla
Cc: linux-sound, kai.vehmanen, yung-chuan.liao, pierre-louis.bossart,
daniel.baluta
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] 47+ messages in thread* Re: [PATCH v2 19/24] ASoC: SOF: ipc4-topology: Set FAST_MODE for host copier in compr mode
2026-09-09 9:09 ` [PATCH v2 19/24] ASoC: SOF: ipc4-topology: Set FAST_MODE for host copier in compr mode Peter Ujfalusi
@ 2026-09-09 13:21 ` Pierre-Louis Bossart
2026-09-10 14:49 ` Péter Ujfalusi
0 siblings, 1 reply; 47+ messages in thread
From: Pierre-Louis Bossart @ 2026-09-09 13:21 UTC (permalink / raw)
To: Peter Ujfalusi, vkoul, perex, tiwai, lgirdwood, broonie,
srinivas.kandagatla
Cc: linux-sound, kai.vehmanen, yung-chuan.liao, daniel.baluta
On 9/9/26 11:09, Peter Ujfalusi wrote:
> FAST_MODE allows the host DMA to work in opportunistic, free running mode,
> which matches with the bitstream nature of compressed devices.
Does it?
My memory of the 'opportunistic' behavior was that the DMA would try to
use every possible window to send to/from the DSP, so that the bus would
be idle for longer periods of time overall.
That would make sense as well for PCM, no?
Maybe this is the right thing to do for compressed data, I just wonder
why it is NOT the right thing to do for PCM...
>
> 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
^ permalink raw reply [flat|nested] 47+ messages in thread* Re: [PATCH v2 19/24] ASoC: SOF: ipc4-topology: Set FAST_MODE for host copier in compr mode
2026-09-09 13:21 ` Pierre-Louis Bossart
@ 2026-09-10 14:49 ` Péter Ujfalusi
2026-09-11 19:08 ` Pierre-Louis Bossart
0 siblings, 1 reply; 47+ messages in thread
From: Péter Ujfalusi @ 2026-09-10 14:49 UTC (permalink / raw)
To: Pierre-Louis Bossart, vkoul, perex, tiwai, lgirdwood, broonie,
srinivas.kandagatla
Cc: linux-sound, kai.vehmanen, yung-chuan.liao, daniel.baluta
On 09/09/2026 16:21, Pierre-Louis Bossart wrote:
> On 9/9/26 11:09, Peter Ujfalusi wrote:
>> FAST_MODE allows the host DMA to work in opportunistic, free running mode,
>> which matches with the bitstream nature of compressed devices.
>
> Does it?
Yes, it does.
> My memory of the 'opportunistic' behavior was that the DMA would try to
> use every possible window to send to/from the DSP, so that the bus would
> be idle for longer periods of time overall.
The FAST_MODE detaches the host copier form the sampling rate completely
and will 'allow' prolonged periods w/o new data to be available. The
IPC4 definition of the feature bit says:
/* If FAST_MODE bit is set in CopierModuleCfg::copier_feature_mask then
copier is able to transfer more than ibs. This bit shall be set only
if all sinks are connected to data processing queue.
*/
> That would make sense as well for PCM, no?
We have similar mode for PCMs with Deep Bufferr, but that is a different
mechanism: it only makes the DMA move in bigger chunks, expressed in ms
(SOF_TKN_INTEL_COPIER_DEEP_BUFFER_DMA_MS -> dma_buffer_size), so it is
still bound by the sampling rate checks. It is not opportunistic.
> Maybe this is the right thing to do for compressed data, I just wonder
> why it is NOT the right thing to do for PCM...
Basically FAST_MODE is a free run mode of the host copier. A PCM stream
has a sampling rate the host side can and should be paced by, a
compressed stream is a bitstream where the host side has no such reference.
>>
>> 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
>
--
Péter
^ permalink raw reply [flat|nested] 47+ messages in thread* Re: [PATCH v2 19/24] ASoC: SOF: ipc4-topology: Set FAST_MODE for host copier in compr mode
2026-09-10 14:49 ` Péter Ujfalusi
@ 2026-09-11 19:08 ` Pierre-Louis Bossart
0 siblings, 0 replies; 47+ messages in thread
From: Pierre-Louis Bossart @ 2026-09-11 19:08 UTC (permalink / raw)
To: Péter Ujfalusi, vkoul, perex, tiwai, lgirdwood, broonie,
srinivas.kandagatla
Cc: linux-sound, kai.vehmanen, yung-chuan.liao, daniel.baluta
>>> FAST_MODE allows the host DMA to work in opportunistic, free running mode,
>>> which matches with the bitstream nature of compressed devices.
>>
>> Does it?
>
> Yes, it does.
>
>> My memory of the 'opportunistic' behavior was that the DMA would try to
>> use every possible window to send to/from the DSP, so that the bus would
>> be idle for longer periods of time overall.
>
> The FAST_MODE detaches the host copier form the sampling rate completely
> and will 'allow' prolonged periods w/o new data to be available. The
> IPC4 definition of the feature bit says:
> /* If FAST_MODE bit is set in CopierModuleCfg::copier_feature_mask then
> copier is able to transfer more than ibs. This bit shall be set only
> if all sinks are connected to data processing queue.
> */
>> That would make sense as well for PCM, no?
>
> We have similar mode for PCMs with Deep Bufferr, but that is a different
> mechanism: it only makes the DMA move in bigger chunks, expressed in ms
> (SOF_TKN_INTEL_COPIER_DEEP_BUFFER_DMA_MS -> dma_buffer_size), so it is
> still bound by the sampling rate checks. It is not opportunistic.
>> Maybe this is the right thing to do for compressed data, I just wonder
>> why it is NOT the right thing to do for PCM...
>
> Basically FAST_MODE is a free run mode of the host copier. A PCM stream
> has a sampling rate the host side can and should be paced by, a
> compressed stream is a bitstream where the host side has no such reference.
For the host-dsp transfers, there is no reason at all for the DMAs to be
paced by the PCM rate...It's only for the DSP-interface DMAs that it
makes sense.
^ permalink raw reply [flat|nested] 47+ messages in thread
* [PATCH v2 20/24] ASoC: SOF: Add support for IPC4 compressed
2026-09-09 9:09 [PATCH v2 00/24] ALSA compress / ASoC compress / SOF: Compressed audio support with IPC4 Peter Ujfalusi
` (18 preceding siblings ...)
2026-09-09 9:09 ` [PATCH v2 19/24] ASoC: SOF: ipc4-topology: Set FAST_MODE for host copier in compr mode Peter Ujfalusi
@ 2026-09-09 9:09 ` Peter Ujfalusi
2026-09-09 9:09 ` [PATCH v2 21/24] ASoC: SOF: ipc4: Handle compressed drain done notification from firmware Peter Ujfalusi
` (4 subsequent siblings)
24 siblings, 0 replies; 47+ messages in thread
From: Peter Ujfalusi @ 2026-09-09 9:09 UTC (permalink / raw)
To: vkoul, perex, tiwai, lgirdwood, broonie, srinivas.kandagatla
Cc: linux-sound, kai.vehmanen, yung-chuan.liao, pierre-louis.bossart,
daniel.baluta
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 = ¶ms->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] 47+ messages in thread* [PATCH v2 21/24] ASoC: SOF: ipc4: Handle compressed drain done notification from firmware
2026-09-09 9:09 [PATCH v2 00/24] ALSA compress / ASoC compress / SOF: Compressed audio support with IPC4 Peter Ujfalusi
` (19 preceding siblings ...)
2026-09-09 9:09 ` [PATCH v2 20/24] ASoC: SOF: Add support for IPC4 compressed Peter Ujfalusi
@ 2026-09-09 9:09 ` Peter Ujfalusi
2026-09-09 13:28 ` Pierre-Louis Bossart
2026-09-09 9:09 ` [PATCH v2 22/24] ASoC: SOF: Intel: Kconfig: Remove redundant IPC version selects Peter Ujfalusi
` (3 subsequent siblings)
24 siblings, 1 reply; 47+ messages in thread
From: Peter Ujfalusi @ 2026-09-09 9:09 UTC (permalink / raw)
To: vkoul, perex, tiwai, lgirdwood, broonie, srinivas.kandagatla
Cc: linux-sound, kai.vehmanen, yung-chuan.liao, pierre-louis.bossart,
daniel.baluta
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>
---
| 3 ++-
sound/soc/sof/ipc4.c | 13 +++++++++++--
2 files changed, 13 insertions(+), 3 deletions(-)
--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] 47+ messages in thread* Re: [PATCH v2 21/24] ASoC: SOF: ipc4: Handle compressed drain done notification from firmware
2026-09-09 9:09 ` [PATCH v2 21/24] ASoC: SOF: ipc4: Handle compressed drain done notification from firmware Peter Ujfalusi
@ 2026-09-09 13:28 ` Pierre-Louis Bossart
2026-09-10 15:08 ` Péter Ujfalusi
0 siblings, 1 reply; 47+ messages in thread
From: Pierre-Louis Bossart @ 2026-09-09 13:28 UTC (permalink / raw)
To: Peter Ujfalusi, vkoul, perex, tiwai, lgirdwood, broonie,
srinivas.kandagatla
Cc: linux-sound, kai.vehmanen, yung-chuan.liao, daniel.baluta
On 9/9/26 11:09, Peter Ujfalusi wrote:
> 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)
not clear why you have to rename the MASK but not the VAL?
> #define SOF_IPC4_NOTIFY_MODULE_EVENTID_ALSA_MAGIC_VAL 0xA15A0000
> #define SOF_IPC4_NOTIFY_MODULE_EVENTID_ALSA_PARAMID_MASK GENMASK(15, 0)
<dad joke> always stay away from paramid schemes </dad joke>
> +#define SOF_IPC4_NOTIFY_MODULE_EVENTID_COMPR_MAGIC_VAL 0xC0C00000
maybe just add this one and avoid the confusing rename above?
>
> /*
> * 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;
> }
> }
>
^ permalink raw reply [flat|nested] 47+ messages in thread* Re: [PATCH v2 21/24] ASoC: SOF: ipc4: Handle compressed drain done notification from firmware
2026-09-09 13:28 ` Pierre-Louis Bossart
@ 2026-09-10 15:08 ` Péter Ujfalusi
0 siblings, 0 replies; 47+ messages in thread
From: Péter Ujfalusi @ 2026-09-10 15:08 UTC (permalink / raw)
To: Pierre-Louis Bossart, vkoul, perex, tiwai, lgirdwood, broonie,
srinivas.kandagatla
Cc: linux-sound, kai.vehmanen, yung-chuan.liao, daniel.baluta
On 09/09/2026 16:28, Pierre-Louis Bossart wrote:
> On 9/9/26 11:09, Peter Ujfalusi wrote:
>> 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)
>
> not clear why you have to rename the MASK but not the VAL?
I only had the ALSA notification, but now the same mechanism can carry
different notification, We still have the ALSA (volume, switch,enum,
bytes change) notification - ALSA 0xA15A, and the COMPR notification -
0xC0C0
>> #define SOF_IPC4_NOTIFY_MODULE_EVENTID_ALSA_MAGIC_VAL 0xA15A0000
>> #define SOF_IPC4_NOTIFY_MODULE_EVENTID_ALSA_PARAMID_MASK GENMASK(15, 0)
>
> <dad joke> always stay away from paramid schemes </dad joke>
I think it is illegal in most countries, we are in the gray area ;)
>> +#define SOF_IPC4_NOTIFY_MODULE_EVENTID_COMPR_MAGIC_VAL 0xC0C00000
>
> maybe just add this one and avoid the confusing rename above?
But then when checking the magic it is not quite right..
data->event_id & ALSA_MAGIC_MASK == ALSA_MAGIC_VAL
data->event_id & ALSA_MAGIC_MASK == COMPR_MAGIC_VAL
These are SOF notifications, and should not be tied to ALSA.
>
>>
>> /*
>> * 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;
>> }
>> }
>>
>
--
Péter
^ permalink raw reply [flat|nested] 47+ messages in thread
* [PATCH v2 22/24] ASoC: SOF: Intel: Kconfig: Remove redundant IPC version selects
2026-09-09 9:09 [PATCH v2 00/24] ALSA compress / ASoC compress / SOF: Compressed audio support with IPC4 Peter Ujfalusi
` (20 preceding siblings ...)
2026-09-09 9:09 ` [PATCH v2 21/24] ASoC: SOF: ipc4: Handle compressed drain done notification from firmware Peter Ujfalusi
@ 2026-09-09 9:09 ` Peter Ujfalusi
2026-09-09 13:29 ` Pierre-Louis Bossart
2026-09-09 9:09 ` [PATCH v2 23/24] ASoC: SOF: Intel: Kconfig: Select compress support for TGL+ platforms Peter Ujfalusi
` (2 subsequent siblings)
24 siblings, 1 reply; 47+ messages in thread
From: Peter Ujfalusi @ 2026-09-09 9:09 UTC (permalink / raw)
To: vkoul, perex, tiwai, lgirdwood, broonie, srinivas.kandagatla
Cc: linux-sound, kai.vehmanen, yung-chuan.liao, pierre-louis.bossart,
daniel.baluta
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] 47+ messages in thread* Re: [PATCH v2 22/24] ASoC: SOF: Intel: Kconfig: Remove redundant IPC version selects
2026-09-09 9:09 ` [PATCH v2 22/24] ASoC: SOF: Intel: Kconfig: Remove redundant IPC version selects Peter Ujfalusi
@ 2026-09-09 13:29 ` Pierre-Louis Bossart
0 siblings, 0 replies; 47+ messages in thread
From: Pierre-Louis Bossart @ 2026-09-09 13:29 UTC (permalink / raw)
To: Peter Ujfalusi, vkoul, perex, tiwai, lgirdwood, broonie,
srinivas.kandagatla
Cc: linux-sound, kai.vehmanen, yung-chuan.liao, daniel.baluta
On 9/9/26 11:09, Peter Ujfalusi wrote:
> 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(-)
that's a cleanup patch that can be submitted separately, before all this
compressed stuff.
> 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
^ permalink raw reply [flat|nested] 47+ messages in thread
* [PATCH v2 23/24] ASoC: SOF: Intel: Kconfig: Select compress support for TGL+ platforms
2026-09-09 9:09 [PATCH v2 00/24] ALSA compress / ASoC compress / SOF: Compressed audio support with IPC4 Peter Ujfalusi
` (21 preceding siblings ...)
2026-09-09 9:09 ` [PATCH v2 22/24] ASoC: SOF: Intel: Kconfig: Remove redundant IPC version selects Peter Ujfalusi
@ 2026-09-09 9:09 ` Peter Ujfalusi
2026-09-09 13:37 ` Pierre-Louis Bossart
2026-09-09 9:09 ` [PATCH v2 24/24] ASoC: SOF: topology: Add support for decoder and encoder widgets Peter Ujfalusi
2026-09-09 13:42 ` [PATCH v2 00/24] ALSA compress / ASoC compress / SOF: Compressed audio support with IPC4 Pierre-Louis Bossart
24 siblings, 1 reply; 47+ messages in thread
From: Peter Ujfalusi @ 2026-09-09 9:09 UTC (permalink / raw)
To: vkoul, perex, tiwai, lgirdwood, broonie, srinivas.kandagatla
Cc: linux-sound, kai.vehmanen, yung-chuan.liao, pierre-louis.bossart,
daniel.baluta
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] 47+ messages in thread* Re: [PATCH v2 23/24] ASoC: SOF: Intel: Kconfig: Select compress support for TGL+ platforms
2026-09-09 9:09 ` [PATCH v2 23/24] ASoC: SOF: Intel: Kconfig: Select compress support for TGL+ platforms Peter Ujfalusi
@ 2026-09-09 13:37 ` Pierre-Louis Bossart
2026-09-10 14:57 ` Péter Ujfalusi
0 siblings, 1 reply; 47+ messages in thread
From: Pierre-Louis Bossart @ 2026-09-09 13:37 UTC (permalink / raw)
To: Peter Ujfalusi, vkoul, perex, tiwai, lgirdwood, broonie,
srinivas.kandagatla
Cc: linux-sound, kai.vehmanen, yung-chuan.liao, daniel.baluta
On 9/9/26 11:09, Peter Ujfalusi wrote:
> Select SOF_COMPRESS for TGL and newer platforms, on Intel devices the
> compressed support is available with IPC4 only.
that wording is confusing.
TGL default is IPC3, official IPC4 support started with MTL, didn't it?
> 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"
^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: [PATCH v2 23/24] ASoC: SOF: Intel: Kconfig: Select compress support for TGL+ platforms
2026-09-09 13:37 ` Pierre-Louis Bossart
@ 2026-09-10 14:57 ` Péter Ujfalusi
0 siblings, 0 replies; 47+ messages in thread
From: Péter Ujfalusi @ 2026-09-10 14:57 UTC (permalink / raw)
To: Pierre-Louis Bossart, vkoul, perex, tiwai, lgirdwood, broonie,
srinivas.kandagatla
Cc: linux-sound, kai.vehmanen, yung-chuan.liao, daniel.baluta
On 09/09/2026 16:37, Pierre-Louis Bossart wrote:
> On 9/9/26 11:09, Peter Ujfalusi wrote:
>> Select SOF_COMPRESS for TGL and newer platforms, on Intel devices the
>> compressed support is available with IPC4 only.
>
> that wording is confusing.
>
> TGL default is IPC3, official IPC4 support started with MTL, didn't it?
Yes, officially it is from MTL, but I worked on TGL for most of the time
until got access to the actual decoder blobs and a machine where I can
deploy the self built firmware.
The compress works on TGL IPC4 with ffmpeg in firmware ;)
Extremely experimental thing for now.
I think I will leave it like this and reconsider if TGL IPC4 is decided
to be dropped.
>
>> 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"
>
--
Péter
^ permalink raw reply [flat|nested] 47+ messages in thread
* [PATCH v2 24/24] ASoC: SOF: topology: Add support for decoder and encoder widgets
2026-09-09 9:09 [PATCH v2 00/24] ALSA compress / ASoC compress / SOF: Compressed audio support with IPC4 Peter Ujfalusi
` (22 preceding siblings ...)
2026-09-09 9:09 ` [PATCH v2 23/24] ASoC: SOF: Intel: Kconfig: Select compress support for TGL+ platforms Peter Ujfalusi
@ 2026-09-09 9:09 ` Peter Ujfalusi
2026-09-09 13:42 ` [PATCH v2 00/24] ALSA compress / ASoC compress / SOF: Compressed audio support with IPC4 Pierre-Louis Bossart
24 siblings, 0 replies; 47+ messages in thread
From: Peter Ujfalusi @ 2026-09-09 9:09 UTC (permalink / raw)
To: vkoul, perex, tiwai, lgirdwood, broonie, srinivas.kandagatla
Cc: linux-sound, kai.vehmanen, yung-chuan.liao, pierre-louis.bossart,
daniel.baluta
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] 47+ messages in thread* Re: [PATCH v2 00/24] ALSA compress / ASoC compress / SOF: Compressed audio support with IPC4
2026-09-09 9:09 [PATCH v2 00/24] ALSA compress / ASoC compress / SOF: Compressed audio support with IPC4 Peter Ujfalusi
` (23 preceding siblings ...)
2026-09-09 9:09 ` [PATCH v2 24/24] ASoC: SOF: topology: Add support for decoder and encoder widgets Peter Ujfalusi
@ 2026-09-09 13:42 ` Pierre-Louis Bossart
24 siblings, 0 replies; 47+ messages in thread
From: Pierre-Louis Bossart @ 2026-09-09 13:42 UTC (permalink / raw)
To: Peter Ujfalusi, vkoul, perex, tiwai, lgirdwood, broonie,
srinivas.kandagatla
Cc: linux-sound, kai.vehmanen, yung-chuan.liao, daniel.baluta
On 9/9/26 11:09, Peter Ujfalusi wrote:
> Hi,
>
> Changes since v1:
> - Add patch to snd_card_file_add/remove for compress
> - Hanlde the SND_COMPR_TRIGGER_NEXT_TRACK in soc_compr_trigger_fe()
>
> Cover letter:
>
> 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.
It's a rather large and broad patchset. I would break it up in two or
three, you have quite a few cleanup/prerequisite patches that could be
submitted earlier. Then add the ALSA/ASOC changes that might impact
others. And last SOF support for Intel IPC4 platforms when all the
required patches are in.
Otherwise nice job, no major issues, just nit-picks and minor comments.
Thanks Peter!
> 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 (18):
> ALSA: compress: pin card module while stream is open
> ALSA: compress: register the open file with the card
> 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 | 86 +-
> sound/soc/soc-compress.c | 200 ++++-
> 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, 1790 insertions(+), 309 deletions(-)
> rename sound/soc/sof/{compress.c => ipc3-compress.c} (66%)
> create mode 100644 sound/soc/sof/ipc4-compress.c
>
^ permalink raw reply [flat|nested] 47+ messages in thread