Skip to content
Open
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
7b5e378
ALSA: compress: pin card module while stream is open
ujfalusi Apr 24, 2026
833c866
ALSA: compress: stop active streams on disconnect
ujfalusi Apr 24, 2026
d2b7772
ASoC: soc-pcm: Allocate be_substream->runtime for compressed streams
ujfalusi Jan 13, 2026
2ef7744
ASoC: soc-compress: Implement trigger FE-BE sequencing as with normal…
ujfalusi Jan 27, 2026
7353773
ASoC: soc-compress: Stop running dpcm on free
ujfalusi Feb 3, 2026
4374059
ASoC: SOF: compress: Rename compress ops with ipc3 prefix
ranj063 Dec 9, 2025
21a4a41
ASoC: SOF: ipc4-pcm: harden pipeline teardown races
ujfalusi Apr 24, 2026
62b3c06
ASoC: SOF: sof-audio: harden recursive widget free walk
ujfalusi Apr 24, 2026
b08cd5b
ASoC: SOF: sof-audio: Expose a couple of functions
ranj063 Dec 9, 2025
d593f24
ASoC: SOF: pcm: Modify the signature of a couple of PCM IPC ops
ranj063 Dec 9, 2025
8858114
ASoC: SOF: intel: hda-stream: Clear the current position when releasi…
ranj063 Dec 9, 2025
44f6804
ASoC: SOF: ops: Add new platform-specific ops for compress
ranj063 Dec 9, 2025
7cb092d
ASoC: SOF: ipc4: Add definition of module data in init_ext object type
ujfalusi Jan 12, 2026
c66366d
ASoC: SOF: ipc4-topology: Support init_ext_module_data for process mo…
ujfalusi Jan 13, 2026
fe34bc6
ASoC: SOF: ipc4-pcm: Make the timestamp info usable outside of ipc4-p…
ujfalusi Jan 13, 2026
d687d55
ASoC: SOF: ipc4/ipc4-loader: Add SOF_INFO and CODEC_INFO to fw_config…
ujfalusi Jan 14, 2026
35b91c3
ASoC: SOF: ipc4-pcm: Handle COMPR DRAIN triggers as EOS pipeline state
ujfalusi Jan 30, 2026
7a9d9db
ASoC: SOF: ipc4-topology: Set FAST_MODE for host copier in compr mode
ujfalusi Jan 30, 2026
26cd3d4
ASoC: SOF: Add support for IPC4 compressed
ujfalusi Dec 9, 2025
2e7efc9
ASoC: SOF: ipc4: Handle compressed drain done notification from firmware
ujfalusi Jan 30, 2026
7e6b721
ASoC: SOF: Intel: Kconfig: Remove redundant IPC version selects
ujfalusi Dec 31, 2025
c565084
ASoC: SOF: Intel: Kconfig: Select compress support for TGL+ platforms
ujfalusi Dec 31, 2025
bfb5234
ASoC: SOF: topology: Add support for decoder and encoder widgets
ranj063 Dec 12, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions include/sound/compress_driver.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/
Expand All @@ -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
Expand Down
3 changes: 2 additions & 1 deletion include/sound/sof/ipc4/header.h
Original file line number Diff line number Diff line change
Expand Up @@ -681,7 +681,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 ext_init object array */
Expand Down
77 changes: 58 additions & 19 deletions sound/core/compress_offload.c
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
#endif

struct snd_compr_file {
struct list_head list;
unsigned long caps;
struct snd_compr_stream stream;
};
Expand Down Expand Up @@ -75,11 +76,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;
Expand All @@ -101,17 +102,23 @@ static int snd_compr_open(struct inode *inode, struct file *f)
return -ENODEV;
}

if (!try_module_get(compr->card->module)) {
snd_card_unref(compr->card);
return -EFAULT;
}

if (dirn != compr->direction) {
pr_err("this device doesn't support this direction\n");
snd_card_unref(compr->card);
return -EINVAL;
ret = -EINVAL;
goto __error;
}

data = kzalloc(sizeof(*data), GFP_KERNEL);
if (!data) {
snd_card_unref(compr->card);
return -ENOMEM;
ret = -ENOMEM;
goto __error;
}
INIT_LIST_HEAD(&data->list);

INIT_DELAYED_WORK(&data->stream.error_work, error_delayed_work);

Expand All @@ -121,9 +128,8 @@ static int snd_compr_open(struct inode *inode, struct file *f)
data->stream.device = compr;
runtime = kzalloc(sizeof(*runtime), GFP_KERNEL);
if (!runtime) {
kfree(data);
snd_card_unref(compr->card);
return -ENOMEM;
ret = -ENOMEM;
goto __error;
}
runtime->state = SNDRV_PCM_STATE_OPEN;
init_waitqueue_head(&runtime->sleep);
Expand All @@ -132,11 +138,17 @@ 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) {
kfree(runtime);
kfree(data);
module_put(compr->card->module);
}
snd_card_unref(compr->card);
return ret;
Expand All @@ -146,24 +158,31 @@ 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);

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;
Expand Down Expand Up @@ -1427,8 +1446,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;
}
Expand Down Expand Up @@ -1536,6 +1574,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);

Expand Down
81 changes: 72 additions & 9 deletions sound/soc/soc-compress.c
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,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);
Expand Down Expand Up @@ -273,31 +283,84 @@ static int soc_compr_trigger(struct snd_compr_stream *cstream, int cmd)
return ret;
}

static int soc_compr_trigger_fe(struct snd_compr_stream *cstream, int cmd)
static int soc_compr_trigger_fe_be(struct snd_compr_stream *cstream, int cmd,
bool fe_first)
{
struct snd_soc_pcm_runtime *fe = cstream->private_data;
struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(fe, 0);
int ret;

if (fe_first) {
dev_dbg(fe->dev, "ASoC: pre trigger FE %s cmd %d\n",
fe->dai_link->name, cmd);

ret = snd_soc_dai_compr_trigger(cpu_dai, cstream, cmd);
if (ret < 0)
goto out;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could just return ret in all of these goto cases

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

or rather:

- return ret;
+ return snd_soc_ret(fe->dev, ret, "trigger FE cmd: %d failed\n", cmd);


ret = snd_soc_component_compr_trigger(cstream, cmd);
if (ret < 0)
goto out;

ret = dpcm_be_dai_trigger(fe, cstream->direction, cmd);
} else {
dev_dbg(fe->dev, "ASoC: post trigger FE %s cmd %d\n",
fe->dai_link->name, cmd);

ret = dpcm_be_dai_trigger(fe, cstream->direction, cmd);
if (ret < 0)
goto out;

ret = snd_soc_dai_compr_trigger(cpu_dai, cstream, cmd);
if (ret < 0)
goto out;

ret = snd_soc_component_compr_trigger(cstream, cmd);
}

out:
return snd_soc_ret(fe->dev, ret, "trigger FE cmd: %d failed\n", cmd);
}

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

if (cmd == SND_COMPR_TRIGGER_PARTIAL_DRAIN ||
cmd == SND_COMPR_TRIGGER_DRAIN)
return snd_soc_component_compr_trigger(cstream, cmd);

if (fe->dai_link->trigger[stream] == SND_SOC_DPCM_TRIGGER_POST)
fe_first = false;
else
fe_first = true;

snd_soc_card_mutex_lock(fe->card);

ret = snd_soc_dai_compr_trigger(cpu_dai, cstream, cmd);
if (ret < 0)
goto out;
fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE;

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

ret = snd_soc_component_compr_trigger(cstream, cmd);
if (ret < 0)
goto out;

fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE;

ret = dpcm_be_dai_trigger(fe, stream, cmd);

switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
Expand Down
13 changes: 12 additions & 1 deletion sound/soc/soc-pcm.c
Original file line number Diff line number Diff line change
Expand Up @@ -1671,6 +1671,8 @@ void dpcm_be_dai_stop(struct snd_soc_pcm_runtime *fe, int stream,
}

__soc_pcm_close(be, be_substream);
if (fe->fe_compr)
kfree(be_substream->runtime);
be_substream->runtime = NULL;
be->dpcm[stream].state = SND_SOC_DPCM_STATE_CLOSE;
}
Expand Down Expand Up @@ -1718,7 +1720,16 @@ int dpcm_be_dai_startup(struct snd_soc_pcm_runtime *fe, int stream)
dev_dbg(be->dev, "ASoC: open %s BE %s\n",
snd_pcm_direction_name(stream), be->dai_link->name);

be_substream->runtime = fe_substream->runtime;
if (!fe->fe_compr) {
be_substream->runtime = fe_substream->runtime;
} else {
be_substream->runtime = kzalloc(sizeof(*be_substream->runtime), GFP_KERNEL);
Comment thread
lgirdwood marked this conversation as resolved.
if (!be_substream->runtime) {
err = -ENOMEM;
goto unwind;
}
}

err = __soc_pcm_open(be, be_substream);
if (err < 0) {
be->dpcm[stream].users--;
Expand Down
2 changes: 1 addition & 1 deletion sound/soc/sof/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
10 changes: 7 additions & 3 deletions sound/soc/sof/core.c
Original file line number Diff line number Diff line change
Expand Up @@ -469,11 +469,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;
}

Expand All @@ -498,6 +499,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);

/*
* skip loading/booting firmware and registering the machine driver when DSP OPS testing
* is enabled with IPC4. Normal audio operations will be unavailable in this mode.
Expand Down
8 changes: 8 additions & 0 deletions sound/soc/sof/intel/hda-common-ops.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,

Expand Down
Loading