From 1652d79928350d6b93cc0d89e57468d66490960c Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 23 Sep 2026 14:20:09 +0300 Subject: [PATCH 1/4] ASoC: SOF: pcm/pm: Fix WoV flow during system suspend / resume When a capture stream for WoV is active during suspend, we must not tear down the pipelines as they must remain active while the system is suspended. In order to the WoV to work with system suspend, the PCM must have SNDRV_PCM_INFO_RESUME set so applications will not try to re-start the stream due to not supported resume trigger. However on RESUME trigger there is nothing to do for the VoW PCM as it was left running, but since system RESUME is not supported by default, for other streams which have suspend_ignored=false we need to return error for userspace to restart the stream. Signed-off-by: Peter Ujfalusi --- sound/soc/sof/pcm.c | 7 +++++++ sound/soc/sof/pm.c | 3 ++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index 42738f12fa3390..6a7acbb69fb4a4 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -428,6 +428,13 @@ static int sof_pcm_trigger(struct snd_soc_component *component, if (pcm_ops && pcm_ops->ipc_first_on_start) ipc_first = true; break; + case SNDRV_PCM_TRIGGER_RESUME: + if (spcm->stream[substream->stream].suspend_ignored) { + spcm->stream[substream->stream].suspend_ignored = false; + return 0; + } + spcm_err(spcm, substream->stream, "RESUME trigger is not supported\n"); + return -EINVAL; case SNDRV_PCM_TRIGGER_SUSPEND: /* * If DSP D0I3 is allowed during S0iX, set the suspend_ignored flag for diff --git a/sound/soc/sof/pm.c b/sound/soc/sof/pm.c index 1d71d483588eb1..b5d239b12e59d2 100644 --- a/sound/soc/sof/pm.c +++ b/sound/soc/sof/pm.c @@ -248,7 +248,8 @@ static int sof_suspend(struct device *dev, bool runtime_suspend) * suspended, it is brought back to full power and then * suspended again */ - if (tplg_ops && tplg_ops->tear_down_all_pipelines && (old_state == SOF_DSP_PM_D0)) + if (tplg_ops && tplg_ops->tear_down_all_pipelines && (old_state == SOF_DSP_PM_D0) && + (target_state != SOF_DSP_PM_D0)) tplg_ops->tear_down_all_pipelines(sdev, false); if (sdev->fw_state != SOF_FW_BOOT_COMPLETE) From 9e47005aadf18e79853bbf652a0a3fbff2a76f32 Mon Sep 17 00:00:00 2001 From: Jyri Sarha Date: Thu, 13 Aug 2026 22:29:06 +0300 Subject: [PATCH 2/4] ASoC: SOF: Add definition of a WoV stream and drop d0i3_supported_in_s0ix Historically the CAPTURE_COMPATIBLE_D0I3 have been added to mark the WoV stream during IPC3 era. For symmetry the PLAYBACK_COMPATIBLE_D0I3 token was added as well. Later IPC4 declared that WoV is not supported and started to use the playback token to mark Deep Buffer streams (host can enter lower power state) and after that using this example a Deep Buffer support for capture was added - again, keeping the WoV unsupported by IPC4. To lift the WoV block for IPC4 and keeping the IPC3 support intact the definition of WoV stream is: a capture stream, CAPTURE_COMPATIBLE_D0I3 is set for the PCM, it is not a Deep Buffer stream. With this rule we can clearly identify the WoV stream and we can tell it apart from Deep Buffer capture. If Deep Buffer will be needed for WoV then we need bigger changes in firmware, topology (new token) and kernel. Co-Developed by: Peter Ujfalusi Signed-off-by: Peter Ujfalusi Signed-off-by: Jyri Sarha --- sound/soc/sof/ipc3-pcm.c | 1 - sound/soc/sof/pcm.c | 16 ++++++++++------ sound/soc/sof/sof-audio.h | 2 -- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/sound/soc/sof/ipc3-pcm.c b/sound/soc/sof/ipc3-pcm.c index 42f93e596598fd..b95e888c0adb84 100644 --- a/sound/soc/sof/ipc3-pcm.c +++ b/sound/soc/sof/ipc3-pcm.c @@ -431,7 +431,6 @@ const struct sof_ipc_pcm_ops ipc3_pcm_ops = { .trigger = sof_ipc3_pcm_trigger, .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/pcm.c b/sound/soc/sof/pcm.c index 6a7acbb69fb4a4..129c698d87beba 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -437,16 +437,20 @@ static int sof_pcm_trigger(struct snd_soc_component *component, return -EINVAL; case SNDRV_PCM_TRIGGER_SUSPEND: /* - * If DSP D0I3 is allowed during S0iX, set the suspend_ignored flag for - * D0I3-compatible streams to keep the firmware pipeline running + * Set the suspend_ignored flag for D0I3-compatible streams used + * for WoV to keep the firmware pipeline running. + * WoV streams can be indetified by: + * They are capture streams and + * They have the d0i3_compatible flag set and + * They don't use Deep Buffer */ - if (pcm_ops && pcm_ops->d0i3_supported_in_s0ix && - sdev->system_suspend_target == SOF_SUSPEND_S0IX && - spcm->stream[substream->stream].d0i3_compatible) { + if (sdev->system_suspend_target == SOF_SUSPEND_S0IX && + substream->stream == SNDRV_PCM_STREAM_CAPTURE && + spcm->stream[substream->stream].d0i3_compatible && + spcm->stream[substream->stream].dsp_max_burst_size_in_ms <= 1) { spcm->stream[substream->stream].suspend_ignored = true; return 0; } - /* On suspend the DMA must be stopped in DSPless mode */ if (sdev->dspless_mode_selected) reset_hw_params = true; diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h index b60246fd5869b1..0d3650f7a67002 100644 --- a/sound/soc/sof/sof-audio.h +++ b/sound/soc/sof/sof-audio.h @@ -121,7 +121,6 @@ struct snd_sof_dai_config_data { * triggers. The FW keeps the host DMA running in this case and * 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 { @@ -143,7 +142,6 @@ struct sof_ipc_pcm_ops { bool reset_hw_params_during_stop; bool ipc_first_on_start; bool platform_stop_during_hw_free; - bool d0i3_supported_in_s0ix; const struct snd_compress_ops *compress_ops; }; From 8d544a039f5e7031952242086c92e5758a2a2ec9 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Mon, 5 Oct 2026 09:31:29 +0300 Subject: [PATCH 3/4] ASoC: SOF: Intel: hda-dai: Keep BEs used for WoV enabled during system suspend When HD linkDMA is used for the BE side (HDA or any DAI type in case of AC2+) we need to keep the linkDMA side active also to have working WoV. With ACE1/CAVS2.5 this is not an issue as the FE core code would trigger all pipleines. Add NOP handling of SUSPEND and RESUME triggers for BEs used with WoV FE and skip the trigger for these to keep the stream enabled. Signed-off-by: Peter Ujfalusi --- sound/soc/sof/intel/hda-dai.c | 49 ++++++++++++++++++++++++++++++++++- sound/soc/sof/intel/hda.h | 1 + 2 files changed, 49 insertions(+), 1 deletion(-) diff --git a/sound/soc/sof/intel/hda-dai.c b/sound/soc/sof/intel/hda-dai.c index c8729b6d82bd4b..7a8875a885ab34 100644 --- a/sound/soc/sof/intel/hda-dai.c +++ b/sound/soc/sof/intel/hda-dai.c @@ -135,6 +135,9 @@ hda_link_dma_cleanup(struct snd_pcm_substream *substream, if (!hlink) return -EINVAL; + hda_stream = hstream_to_sof_hda_stream(hext_stream); + hda_stream->suspend_ignored = false; + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { stream_tag = hdac_stream(hext_stream)->stream_tag; snd_hdac_ext_bus_link_clear_stream_id(hlink, stream_tag); @@ -157,7 +160,6 @@ hda_link_dma_cleanup(struct snd_pcm_substream *substream, hext_stream->link_prepared = 0; /* free the host DMA channel reserved by hostless streams */ - hda_stream = hstream_to_sof_hda_stream(hext_stream); hda_stream->host_reserved = 0; return 0; @@ -280,6 +282,32 @@ static int __maybe_unused hda_dai_hw_params(struct snd_pcm_substream *substream, return hda_dai_hw_params_data(substream, params, dai, &data, flags); } +static bool hda_dai_has_wov_fe(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); + struct snd_soc_dpcm *dpcm; + struct snd_sof_pcm *spcm; + + if (substream->stream != SNDRV_PCM_STREAM_CAPTURE) + return false; + + /* + * Only retain the BE for a connected WoV capture FE that is running + * or has already ignored its suspend trigger. An idle WoV FE may + * still be connected to the BE. + */ + for_each_dpcm_fe(rtd, SNDRV_PCM_STREAM_CAPTURE, dpcm) { + spcm = snd_sof_find_spcm_dai(sdev->component, dpcm->fe); + if (spcm && spcm->stream[SNDRV_PCM_STREAM_CAPTURE].d0i3_compatible && + spcm->stream[SNDRV_PCM_STREAM_CAPTURE].dsp_max_burst_size_in_ms <= 1 && + (dpcm->fe->dpcm[SNDRV_PCM_STREAM_CAPTURE].state == SND_SOC_DPCM_STATE_START || + spcm->stream[SNDRV_PCM_STREAM_CAPTURE].suspend_ignored)) + return true; + } + + return false; +} + /* * In contrast to IPC3, the dai trigger in IPC4 mixes pipeline state changes * (over IPC channel) and DMA state change (direct host register changes). @@ -289,6 +317,7 @@ static int __maybe_unused hda_dai_trigger(struct snd_pcm_substream *substream, i { const struct hda_dai_widget_dma_ops *ops = hda_dai_get_ops(substream, dai); struct hdac_ext_stream *hext_stream; + struct sof_intel_hda_stream *hda_stream; struct snd_sof_dev *sdev; int ret; @@ -305,6 +334,24 @@ static int __maybe_unused hda_dai_trigger(struct snd_pcm_substream *substream, i hext_stream = ops->get_hext_stream(sdev, dai, substream); if (!hext_stream) return -EINVAL; + hda_stream = hstream_to_sof_hda_stream(hext_stream); + + if (cmd == SNDRV_PCM_TRIGGER_RESUME && hda_stream->suspend_ignored) { + hda_stream->suspend_ignored = false; + return 0; + } + + /* + * Set the suspend_ignored flag for BE which is used with + * D0I3-compatible streams used for WoV to keep the firmware pipeline + * running. + */ + if (cmd == SNDRV_PCM_TRIGGER_SUSPEND && + sdev->system_suspend_target == SOF_SUSPEND_S0IX && + hda_dai_has_wov_fe(sdev, substream)) { + hda_stream->suspend_ignored = true; + return 0; + } if (ops->pre_trigger) { ret = ops->pre_trigger(sdev, dai, substream, cmd); diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h index 701578ec35cf4d..bc11519427649f 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -601,6 +601,7 @@ struct sof_intel_hda_stream { struct hdac_ext_stream hext_stream; struct sof_intel_stream sof_intel_stream; int host_reserved; /* reserve host DMA channel */ + bool suspend_ignored; u32 flags; struct completion ioc; }; From b321732fcec2583c4db1f4b76014854e33fd9a75 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 23 Sep 2026 14:22:20 +0300 Subject: [PATCH 4/4] ASoC: SOF: Intel:hda-pcm: Set SNDRV_PCM_INFO_RESUME for VoW streams VoW streams can be identified by: They are capture streams, the d0i3_compatible flag is set and they are not using Deep Buffer. For the Wake on Voice to work the SNDRV_PCM_INFO_RESUME flag must be set for the PCM. On system suspend the DSP will be left enabled, pipelines running and on resume there will be no action needed to be done. Signed-off-by: Peter Ujfalusi --- sound/soc/sof/intel/hda-pcm.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/sound/soc/sof/intel/hda-pcm.c b/sound/soc/sof/intel/hda-pcm.c index 83efc9bdf418af..70abff12fef0ee 100644 --- a/sound/soc/sof/intel/hda-pcm.c +++ b/sound/soc/sof/intel/hda-pcm.c @@ -352,6 +352,20 @@ int hda_dsp_pcm_open(struct snd_sof_dev *sdev, spcm->stream[substream->stream].d0i3_compatible) flags |= SOF_HDA_STREAM_DMI_L1_COMPATIBLE; + /* + * Set the RESUME supported flag for WoV streams. The core will ignore + * the trigger but applications must not try to restart the WoV stream + * due to not supported RESUME. + * WoV streams can be indetified by: + * They are capture streams and + * They have the d0i3_compatible flag set and + * They don't use Deep Buffer + */ + if (direction == SNDRV_PCM_STREAM_CAPTURE && + spcm->stream[substream->stream].d0i3_compatible && + spcm->stream[substream->stream].dsp_max_burst_size_in_ms <= 1) + runtime->hw.info |= SNDRV_PCM_INFO_RESUME; + dsp_stream = hda_dsp_stream_get(sdev, direction, flags); if (!dsp_stream) { dev_err(sdev->dev, "error: no stream available\n");