diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index af65214836b4d0..a159ca8d869a56 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -952,11 +953,19 @@ static int intel_component_dais_suspend(struct snd_soc_component *component) for_each_component_dais(component, dai) { struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai); struct sdw_cdns_dai_runtime *dai_runtime; + struct sdw_stream_runtime *stream; dai_runtime = cdns->dai_runtime_array[dai->id]; + if (!dai_runtime) + continue; - if (dai_runtime) - dai_runtime->suspended = true; + stream = dai_runtime->stream; + if (pm_suspend_target_state == PM_SUSPEND_TO_IDLE && + stream && stream->ignore_suspend && + stream->state == SDW_STREAM_ENABLED) + continue; + + dai_runtime->suspended = true; } return 0; diff --git a/drivers/soundwire/intel_ace2x.c b/drivers/soundwire/intel_ace2x.c index 642b33ab552606..39ffe722812aba 100644 --- a/drivers/soundwire/intel_ace2x.c +++ b/drivers/soundwire/intel_ace2x.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -951,11 +952,19 @@ static int intel_component_dais_suspend(struct snd_soc_component *component) for_each_component_dais(component, dai) { struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai); struct sdw_cdns_dai_runtime *dai_runtime; + struct sdw_stream_runtime *stream; dai_runtime = cdns->dai_runtime_array[dai->id]; + if (!dai_runtime) + continue; - if (dai_runtime) - dai_runtime->suspended = true; + stream = dai_runtime->stream; + if (pm_suspend_target_state == PM_SUSPEND_TO_IDLE && + stream && stream->ignore_suspend && + stream->state == SDW_STREAM_ENABLED) + continue; + + dai_runtime->suspended = true; } return 0; diff --git a/drivers/soundwire/intel_auxdevice.c b/drivers/soundwire/intel_auxdevice.c index 5983fbc5725a26..76c7af462ac80e 100644 --- a/drivers/soundwire/intel_auxdevice.c +++ b/drivers/soundwire/intel_auxdevice.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -582,7 +583,7 @@ static int __maybe_unused intel_pm_prepare(struct device *dev) struct sdw_intel *sdw = cdns_to_intel(cdns); struct sdw_bus *bus = &cdns->bus; u32 clock_stop_quirks; - int ret; + int i, ret; if (bus->prop.hw_disabled || !sdw->startup_done) { dev_dbg(dev, "SoundWire master %d is disabled or not-started, ignoring\n", @@ -633,6 +634,34 @@ static int __maybe_unused intel_pm_prepare(struct device *dev) dev_err(dev, "%s: intel_resume_child_device failed: %d\n", __func__, ret); } + /* + * Revoke retention from an earlier S0ix before any controller or codec + * suspends. An already-suspended PCM will not issue another trigger. + */ + if (pm_suspend_target_state != PM_SUSPEND_TO_IDLE) { + for (i = 0; i < cdns->pcm.num_pdi; i++) { + struct sdw_cdns_dai_runtime *dai_runtime = cdns->dai_runtime_array[i]; + struct sdw_stream_runtime *stream; + + if (!dai_runtime || !dai_runtime->stream) + continue; + + stream = dai_runtime->stream; + if (!stream->ignore_suspend) + continue; + + if (stream->state == SDW_STREAM_ENABLED) { + ret = sdw_disable_stream(stream); + if (ret) { + dev_err(dev, "failed to disable retained stream %s: %d\n", + stream->name, ret); + return ret; + } + } + stream->ignore_suspend = false; + } + } + return 0; } @@ -650,6 +679,14 @@ static int __maybe_unused intel_suspend(struct device *dev) return 0; } + /* + * ignore_suspend is set by the PCM trigger for S0ix and may remain set + * if a later sleep transition does not issue another PCM trigger. + */ + if (pm_suspend_target_state == PM_SUSPEND_TO_IDLE && + sdw_bus_ignore_suspend(bus)) + return 0; + /* Prevent runtime PM from racing with the code below. */ pm_runtime_disable(dev); @@ -737,6 +774,13 @@ static int __maybe_unused intel_resume(struct device *dev) return 0; } + if (pm_suspend_target_state == PM_SUSPEND_TO_IDLE && + sdw_bus_ignore_suspend(bus)) { + dev_dbg(dev, "SoundWire master %d remained active during system suspend\n", + bus->link_id); + return 0; + } + ret = sdw_intel_link_power_up(sdw); if (ret) { dev_err(dev, "%s failed: %d\n", __func__, ret); diff --git a/drivers/soundwire/stream.c b/drivers/soundwire/stream.c index c158bf91e020f9..76e5c0369ca04f 100644 --- a/drivers/soundwire/stream.c +++ b/drivers/soundwire/stream.c @@ -1688,6 +1688,25 @@ int sdw_enable_stream(struct sdw_stream_runtime *stream) } EXPORT_SYMBOL(sdw_enable_stream); +bool sdw_bus_ignore_suspend(struct sdw_bus *bus) +{ + struct sdw_master_runtime *m_rt; + bool ret = false; + + mutex_lock(&bus->bus_lock); + list_for_each_entry(m_rt, &bus->m_rt_list, bus_node) { + if (m_rt->stream->state == SDW_STREAM_ENABLED && + m_rt->stream->ignore_suspend) { + ret = true; + break; + } + } + mutex_unlock(&bus->bus_lock); + + return ret; +} +EXPORT_SYMBOL(sdw_bus_ignore_suspend); + static int _sdw_disable_stream(struct sdw_stream_runtime *stream) { struct sdw_master_runtime *m_rt; diff --git a/include/linux/soundwire/sdw.h b/include/linux/soundwire/sdw.h index 326db122bbe6ea..ac9755d11daa2d 100644 --- a/include/linux/soundwire/sdw.h +++ b/include/linux/soundwire/sdw.h @@ -978,6 +978,7 @@ struct sdw_stream_params { * @master_list: List of Master runtime(s) in this stream. * master_list can contain only one m_rt per Master instance * for a stream + * @ignore_suspend: stream keeps running during system suspend */ struct sdw_stream_runtime { const char *name; @@ -986,6 +987,7 @@ struct sdw_stream_runtime { enum sdw_stream_type type; int m_rt_count; struct list_head master_list; + bool ignore_suspend; }; /** @@ -1086,6 +1088,7 @@ int sdw_stream_remove_master(struct sdw_bus *bus, int sdw_startup_stream(void *sdw_substream); int sdw_prepare_stream(struct sdw_stream_runtime *stream); int sdw_enable_stream(struct sdw_stream_runtime *stream); +bool sdw_bus_ignore_suspend(struct sdw_bus *bus); int sdw_disable_stream(struct sdw_stream_runtime *stream); int sdw_deprepare_stream(struct sdw_stream_runtime *stream); void sdw_shutdown_stream(void *sdw_substream); diff --git a/sound/soc/sdw_utils/soc_sdw_utils.c b/sound/soc/sdw_utils/soc_sdw_utils.c index 7a19de75f32b53..556284f48a8913 100644 --- a/sound/soc/sdw_utils/soc_sdw_utils.c +++ b/sound/soc/sdw_utils/soc_sdw_utils.c @@ -1880,6 +1880,9 @@ int asoc_sdw_prepare(struct snd_pcm_substream *substream) return PTR_ERR(sdw_stream); } + if (sdw_stream->state == SDW_STREAM_ENABLED) + return 0; + return sdw_prepare_stream(sdw_stream); } EXPORT_SYMBOL_NS(asoc_sdw_prepare, "SND_SOC_SDW_UTILS"); @@ -1902,6 +1905,10 @@ int asoc_sdw_trigger(struct snd_pcm_substream *substream, int cmd) switch (cmd) { case SNDRV_PCM_TRIGGER_RESUME: + if (sdw_stream->state == SDW_STREAM_ENABLED) { + ret = 0; + break; + } /* * The peripherals lose their port configuration when the * controller is power-gated during system suspend, and an @@ -1923,10 +1930,12 @@ int asoc_sdw_trigger(struct snd_pcm_substream *substream, int cmd) break; case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_STOP: ret = sdw_disable_stream(sdw_stream); break; + case SNDRV_PCM_TRIGGER_SUSPEND: + ret = sdw_stream->ignore_suspend ? 0 : sdw_disable_stream(sdw_stream); + break; default: ret = -EINVAL; break; @@ -2005,6 +2014,16 @@ int asoc_sdw_hw_free(struct snd_pcm_substream *substream) return PTR_ERR(sdw_stream); } + if (sdw_stream->state == SDW_STREAM_ENABLED) { + int ret; + + ret = sdw_disable_stream(sdw_stream); + if (ret) { + dev_err(rtd->dev, "failed to disable SoundWire stream: %d\n", ret); + return ret; + } + } + return sdw_deprepare_stream(sdw_stream); } EXPORT_SYMBOL_NS(asoc_sdw_hw_free, "SND_SOC_SDW_UTILS"); diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 74be5e20949337..c28d9e1f68bf1a 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -36,6 +36,7 @@ #include #include #include +#include #include #include "soc-internal.h" @@ -1415,18 +1416,62 @@ static int dapm_new_dai_link(struct snd_soc_dapm_widget *w) * the ALSA card - when we are suspending the ALSA state for the card * is set to D3. */ +static bool dapm_be_has_active_ignore_suspend_fe(struct snd_soc_dapm_widget *widget) +{ + struct snd_soc_pcm_runtime *be; + struct snd_soc_dpcm *dpcm; + struct snd_soc_dai *be_dai, *fe_dai; + struct snd_soc_dapm_widget *fe_widget; + int i, j, stream; + + if (widget->id == snd_soc_dapm_dai_in) + stream = SNDRV_PCM_STREAM_PLAYBACK; + else if (widget->id == snd_soc_dapm_dai_out) + stream = SNDRV_PCM_STREAM_CAPTURE; + else + return false; + + /* DPCM client links are stable while the card is in system suspend. */ + for_each_card_rtds(widget->dapm->card, be) { + if (!be->dai_link->no_pcm) + continue; + + for_each_rtd_dais(be, i, be_dai) { + if (snd_soc_dai_get_widget(be_dai, stream) != widget) + continue; + + for_each_dpcm_fe(be, stream, dpcm) { + if (dpcm->state == SND_SOC_DPCM_LINK_STATE_FREE) + continue; + + for_each_rtd_cpu_dais(dpcm->fe, j, fe_dai) { + fe_widget = snd_soc_dai_get_widget(fe_dai, stream); + if (fe_widget && fe_widget->active && + fe_widget->ignore_suspend) + return true; + } + } + } + } + + return false; +} + static int dapm_suspend_check(struct snd_soc_dapm_widget *widget) { struct device *dev = snd_soc_dapm_to_dev(widget->dapm); int level = snd_power_get_state(widget->dapm->card->snd_card); + bool ignore_suspend; switch (level) { case SNDRV_CTL_POWER_D3hot: case SNDRV_CTL_POWER_D3cold: - if (widget->ignore_suspend) + ignore_suspend = widget->ignore_suspend || + dapm_be_has_active_ignore_suspend_fe(widget); + if (ignore_suspend) dev_dbg(dev, "ASoC: %s ignoring suspend\n", widget->name); - return widget->ignore_suspend; + return ignore_suspend; default: return 1; } diff --git a/sound/soc/sof/intel/hda-dai-ops.c b/sound/soc/sof/intel/hda-dai-ops.c index f0be42048db33c..c002c234c45797 100644 --- a/sound/soc/sof/intel/hda-dai-ops.c +++ b/sound/soc/sof/intel/hda-dai-ops.c @@ -580,6 +580,10 @@ static int hda_ipc3_post_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *c { struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(cpu_dai, substream->stream); + if (sdev->fw_state != SOF_FW_BOOT_COMPLETE && + (cmd == SNDRV_PCM_TRIGGER_STOP || cmd == SNDRV_PCM_TRIGGER_SUSPEND)) + return 0; + switch (cmd) { case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_STOP: diff --git a/sound/soc/sof/intel/hda-dai.c b/sound/soc/sof/intel/hda-dai.c index 7a8875a885ab34..bf84f870863820 100644 --- a/sound/soc/sof/intel/hda-dai.c +++ b/sound/soc/sof/intel/hda-dai.c @@ -282,6 +282,25 @@ static int __maybe_unused hda_dai_hw_params(struct snd_pcm_substream *substream, return hda_dai_hw_params_data(substream, params, dai, &data, flags); } +/* + * A retained WoV FE that is being resumed has already consumed its + * suspend_ignored flag when the FE is triggered before the BE, but its + * DPCM state is not updated until the trigger completes. Its PCM is still + * SUSPENDED during the trigger. + */ +static bool hda_dai_fe_is_resuming(struct snd_soc_pcm_runtime *fe) +{ + struct snd_pcm_substream *fe_substream; + + if (fe->dpcm[SNDRV_PCM_STREAM_CAPTURE].runtime_update != SND_SOC_DPCM_UPDATE_FE) + return false; + + fe_substream = snd_soc_dpcm_get_substream(fe, SNDRV_PCM_STREAM_CAPTURE); + + return fe_substream && fe_substream->runtime && + fe_substream->runtime->state == SNDRV_PCM_STATE_SUSPENDED; +} + 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); @@ -292,22 +311,44 @@ static bool hda_dai_has_wov_fe(struct snd_sof_dev *sdev, struct snd_pcm_substrea 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. + * Only retain the BE for a connected WoV capture FE that is running, + * has already ignored its suspend trigger or is being resumed. 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)) + spcm->stream[SNDRV_PCM_STREAM_CAPTURE].suspend_ignored || + hda_dai_fe_is_resuming(dpcm->fe))) return true; } return false; } +static void hda_dai_set_sdw_ignore_suspend(struct snd_soc_dai *dai, int dir, + bool ignore_suspend) +{ + struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(dai, dir); + struct sdw_stream_runtime *sdw_stream; + struct snd_sof_widget *swidget; + struct snd_sof_dai *sof_dai; + + if (!w || !w->dobj.private) + return; + + swidget = w->dobj.private; + sof_dai = swidget->private; + if (!sof_dai || sof_dai->type != SOF_DAI_INTEL_ALH) + return; + + sdw_stream = snd_soc_dai_get_stream(dai, dir); + if (!IS_ERR_OR_NULL(sdw_stream)) + sdw_stream->ignore_suspend = ignore_suspend; +} + /* * In contrast to IPC3, the dai trigger in IPC4 mixes pipeline state changes * (over IPC channel) and DMA state change (direct host register changes). @@ -319,6 +360,7 @@ static int __maybe_unused hda_dai_trigger(struct snd_pcm_substream *substream, i struct hdac_ext_stream *hext_stream; struct sof_intel_hda_stream *hda_stream; struct snd_sof_dev *sdev; + bool skip_fw_ipc; int ret; if (!ops) { @@ -330,13 +372,20 @@ static int __maybe_unused hda_dai_trigger(struct snd_pcm_substream *substream, i dai->name, substream->stream); sdev = dai_to_sdev(substream, dai); + /* + * Firmware IPC is unavailable after a crash, but STOP/SUSPEND must + * still clean up the host-side DMA resources below. + */ + skip_fw_ipc = sdev->fw_state != SOF_FW_BOOT_COMPLETE && + (cmd == SNDRV_PCM_TRIGGER_STOP || cmd == SNDRV_PCM_TRIGGER_SUSPEND); 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) { + if ((cmd == SNDRV_PCM_TRIGGER_START || cmd == SNDRV_PCM_TRIGGER_RESUME) && + hda_stream->suspend_ignored) { hda_stream->suspend_ignored = false; return 0; } @@ -353,7 +402,7 @@ static int __maybe_unused hda_dai_trigger(struct snd_pcm_substream *substream, i return 0; } - if (ops->pre_trigger) { + if (ops->pre_trigger && !skip_fw_ipc) { ret = ops->pre_trigger(sdev, dai, substream, cmd); if (ret < 0) return ret; @@ -691,6 +740,17 @@ EXPORT_SYMBOL_NS(sdw_hda_dai_hw_free, "SND_SOC_SOF_INTEL_HDA_COMMON"); int sdw_hda_dai_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *cpu_dai) { + struct snd_sof_dev *sdev = dai_to_sdev(substream, cpu_dai); + bool retain = false; + + if (cmd == SNDRV_PCM_TRIGGER_SUSPEND) + retain = sdev->system_suspend_target == SOF_SUSPEND_S0IX && + hda_dai_has_wov_fe(sdev, substream); + + if (cmd == SNDRV_PCM_TRIGGER_SUSPEND || cmd == SNDRV_PCM_TRIGGER_STOP || + cmd == SNDRV_PCM_TRIGGER_START || cmd == SNDRV_PCM_TRIGGER_RESUME) + hda_dai_set_sdw_ignore_suspend(cpu_dai, substream->stream, retain); + return hda_dai_trigger(substream, cmd, cpu_dai); } EXPORT_SYMBOL_NS(sdw_hda_dai_trigger, "SND_SOC_SOF_INTEL_HDA_COMMON"); diff --git a/sound/soc/sof/intel/hda-dsp.c b/sound/soc/sof/intel/hda-dsp.c index ec49aeaa9bd7af..0c5eb34dbeedaa 100644 --- a/sound/soc/sof/intel/hda-dsp.c +++ b/sound/soc/sof/intel/hda-dsp.c @@ -852,6 +852,17 @@ static int hda_suspend(struct snd_sof_dev *sdev, bool runtime_suspend) hda_dsp_ctrl_ppcap_enable(sdev, false); hda_dsp_ctrl_ppcap_int_enable(sdev, false); skip_dsp: + if (!runtime_suspend) { + struct hdac_stream *s; + + /* Do not carry WoV retention state across a system D3 transition. */ + list_for_each_entry(s, &bus->stream_list, list) { + struct sof_intel_hda_stream *hda_stream; + + hda_stream = hstream_to_sof_hda_stream(stream_to_hdac_ext_stream(s)); + hda_stream->suspend_ignored = false; + } + } /* disable hda bus irq and streams */ hda_dsp_ctrl_stop_chip(sdev); diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index 0161c0ae8bbdb4..9a608d3eeedf97 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -66,6 +66,37 @@ void snd_sof_pcm_period_elapsed(struct snd_pcm_substream *substream) } EXPORT_SYMBOL(snd_sof_pcm_period_elapsed); +static int sof_pcm_set_dai_ignore_suspend(struct snd_soc_pcm_runtime *rtd, int dir) +{ + struct snd_soc_dai *dai = snd_soc_rtd_to_cpu(rtd, 0); + struct snd_soc_dapm_widget *widget = snd_soc_dai_get_widget(dai, dir); + struct snd_soc_dapm_widget_list *list; + int i, ret; + + if (!widget) + return -ENODEV; + + ret = snd_soc_dapm_dai_get_connected_widgets(dai, dir, &list, NULL); + if (ret < 0) + return ret; + + ret = -ENODEV; + snd_soc_dapm_mutex_lock(rtd->card); + for (i = 0; i < list->num_widgets; i++) { + if (list->widgets[i]->is_ep & SND_SOC_DAPM_EP_SOURCE) { + list->widgets[i]->ignore_suspend = 1; + ret = 0; + } + } + if (!ret) + widget->ignore_suspend = 1; + snd_soc_dapm_mutex_unlock(rtd->card); + + snd_soc_dapm_dai_free_widgets(&list); + + return ret; +} + 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, @@ -216,6 +247,19 @@ static int sof_pcm_stream_free(struct snd_sof_dev *sdev, int err = 0; if (spcm->prepared[substream->stream]) { + if (spcm->stream[dir].suspend_ignored && pcm_ops && pcm_ops->trigger) { + ret = pcm_ops->trigger(sdev->component, substream, spcm, + SNDRV_PCM_TRIGGER_STOP, dir); + if (ret < 0) { + spcm_err(spcm, substream->stream, + "retained stream stop failed %d\n", ret); + err = ret; + } + if (!pcm_ops->platform_stop_during_hw_free) + snd_sof_pcm_platform_trigger(sdev, substream, + SNDRV_PCM_TRIGGER_STOP); + } + /* stop DMA first if needed */ if (pcm_ops && pcm_ops->platform_stop_during_hw_free) snd_sof_pcm_platform_trigger(sdev, substream, @@ -236,6 +280,8 @@ static int sof_pcm_stream_free(struct snd_sof_dev *sdev, spcm->pending_stop[substream->stream] = false; } + spcm->stream[dir].suspend_ignored = false; + /* reset the DMA */ ret = snd_sof_pcm_platform_hw_free(sdev, substream); if (ret < 0) { @@ -269,8 +315,7 @@ int sof_pcm_free_all_streams(struct snd_sof_dev *sdev) for_each_pcm_streams(dir) { substream = spcm->stream[dir].substream; - if (!substream || !substream->runtime || - spcm->stream[dir].suspend_ignored) + if (!substream || !substream->runtime) continue; if (spcm->stream[dir].list) { @@ -337,12 +382,12 @@ static int sof_pcm_prepare(struct snd_soc_component *component, spcm_dbg(spcm, substream->stream, "Entry: prepare\n"); if (spcm->prepared[substream->stream]) { + /* a retained stream is still running, nothing to rebuild */ if (!spcm->pending_stop[substream->stream]) return 0; /* - * this case should be reached in case of xruns where we absolutely - * want to free-up and reset all PCM/DMA resources + * Reset PCM/DMA resources after an xrun. */ ret = sof_pcm_stream_free(sdev, substream, spcm, substream->stream, true); if (ret < 0) @@ -448,8 +493,12 @@ static int sof_pcm_trigger(struct snd_soc_component *component, 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; + if (!sof_pcm_set_dai_ignore_suspend(rtd, substream->stream)) { + spcm->stream[substream->stream].suspend_ignored = true; + return 0; + } + + spcm_dbg(spcm, substream->stream, "no mic endpoint to retain for WoV\n"); } /* On suspend the DMA must be stopped in DSPless mode */ if (sdev->dspless_mode_selected) @@ -499,6 +548,10 @@ static int sof_pcm_trigger(struct snd_soc_component *component, break; } + if (!ret && (cmd == SNDRV_PCM_TRIGGER_STOP || + cmd == SNDRV_PCM_TRIGGER_PAUSE_PUSH)) + spcm->stream[substream->stream].suspend_ignored = false; + /* free PCM if reset_hw_params is set and the STOP IPC is successful */ if (!ret && reset_hw_params) ret = sof_pcm_stream_free(sdev, substream, spcm, substream->stream, false); @@ -636,6 +689,7 @@ static int sof_pcm_close(struct snd_soc_component *component, */ } + spcm->stream[substream->stream].suspend_ignored = false; spcm->stream[substream->stream].substream = NULL; return 0;