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-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"); 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; }; 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 42738f12fa3390..129c698d87beba 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -428,18 +428,29 @@ 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 - * 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/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) 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; };