From 45faa871f195d290a660aa970aa18a66dc0eb062 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Tue, 6 Oct 2026 21:56:12 +0800 Subject: [PATCH 1/6] soundwire: stream: add support for ignoring system suspend Some streams, such as those used for wake-on-voice, must remain active during system suspend. Add an ignore_suspend flag to sdw_stream_runtime and export sdw_bus_ignore_suspend() so bus drivers can check whether any stream on the bus requires it to remain active. Signed-off-by: Bard Liao --- drivers/soundwire/stream.c | 18 ++++++++++++++++++ include/linux/soundwire/sdw.h | 3 +++ 2 files changed, 21 insertions(+) diff --git a/drivers/soundwire/stream.c b/drivers/soundwire/stream.c index b4f68356708fe4..d46ae22957789a 100644 --- a/drivers/soundwire/stream.c +++ b/drivers/soundwire/stream.c @@ -1679,6 +1679,24 @@ 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->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 1b03bfad2e6bbe..50960ab8baf3b0 100644 --- a/include/linux/soundwire/sdw.h +++ b/include/linux/soundwire/sdw.h @@ -976,6 +976,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; @@ -984,6 +985,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); From af7c4f378133e1194c25803c9304a5d87050faf2 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Tue, 6 Oct 2026 22:06:50 +0800 Subject: [PATCH 2/6] soundwire: Intel: preserve buses for ignore-suspend streams Honor the SoundWire bus ignore-suspend state in the Intel drivers. Preserve the master across system suspend when a stream requires the bus to remain active. Signed-off-by: Bard Liao --- drivers/soundwire/intel.c | 3 +++ drivers/soundwire/intel_ace2x.c | 3 +++ drivers/soundwire/intel_auxdevice.c | 9 +++++++++ 3 files changed, 15 insertions(+) diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index af65214836b4d0..2064e64c2c5455 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -953,6 +953,9 @@ static int intel_component_dais_suspend(struct snd_soc_component *component) struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai); struct sdw_cdns_dai_runtime *dai_runtime; + if (sdw_bus_ignore_suspend(&cdns->bus)) + continue; + dai_runtime = cdns->dai_runtime_array[dai->id]; if (dai_runtime) diff --git a/drivers/soundwire/intel_ace2x.c b/drivers/soundwire/intel_ace2x.c index 642b33ab552606..717531d737f586 100644 --- a/drivers/soundwire/intel_ace2x.c +++ b/drivers/soundwire/intel_ace2x.c @@ -952,6 +952,9 @@ static int intel_component_dais_suspend(struct snd_soc_component *component) struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai); struct sdw_cdns_dai_runtime *dai_runtime; + if (sdw_bus_ignore_suspend(&cdns->bus)) + continue; + dai_runtime = cdns->dai_runtime_array[dai->id]; if (dai_runtime) diff --git a/drivers/soundwire/intel_auxdevice.c b/drivers/soundwire/intel_auxdevice.c index 901a71262094f9..fd12035c38ee3c 100644 --- a/drivers/soundwire/intel_auxdevice.c +++ b/drivers/soundwire/intel_auxdevice.c @@ -647,6 +647,9 @@ static int __maybe_unused intel_suspend(struct device *dev) return 0; } + if (sdw_bus_ignore_suspend(bus)) + return 0; + /* Prevent runtime PM from racing with the code below. */ pm_runtime_disable(dev); @@ -734,6 +737,12 @@ static int __maybe_unused intel_resume(struct device *dev) return 0; } + if (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); From b59fe7972e45c6dfc61be399b9f23776fc4ab0d0 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Tue, 6 Oct 2026 21:49:39 +0800 Subject: [PATCH 3/6] ASoC: soc_sdw_utils: preserve streams that ignore suspend Do not disable a SoundWire stream on suspend when it is marked to remain active. If the stream is already enabled on resume, no further action is needed. Signed-off-by: Bard Liao --- sound/soc/sdw_utils/soc_sdw_utils.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/sound/soc/sdw_utils/soc_sdw_utils.c b/sound/soc/sdw_utils/soc_sdw_utils.c index 14aee10b40ba7c..e3ee8f385952f0 100644 --- a/sound/soc/sdw_utils/soc_sdw_utils.c +++ b/sound/soc/sdw_utils/soc_sdw_utils.c @@ -1902,6 +1902,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 +1927,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; From ac7fed982137d63e79a28fa819c2161eb7464bfa Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Tue, 6 Oct 2026 22:10:20 +0800 Subject: [PATCH 4/6] ASoC: SOF: Intel: keep SoundWire streams active for D0i3-compatible PCMs Keep a SoundWire stream active during system suspend when it is used by a WoV stream. 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. Signed-off-by: Bard Liao --- sound/soc/sof/intel/hda-common-ops.c | 1 + sound/soc/sof/intel/hda.c | 75 ++++++++++++++++++++++++++++ sound/soc/sof/intel/hda.h | 3 ++ sound/soc/sof/ops.h | 9 ++++ sound/soc/sof/pcm.c | 20 ++++++++ sound/soc/sof/sof-priv.h | 4 ++ 6 files changed, 112 insertions(+) diff --git a/sound/soc/sof/intel/hda-common-ops.c b/sound/soc/sof/intel/hda-common-ops.c index aa91f2b6fd657b..f65b0a34fe3b6d 100644 --- a/sound/soc/sof/intel/hda-common-ops.c +++ b/sound/soc/sof/intel/hda-common-ops.c @@ -54,6 +54,7 @@ const struct snd_sof_dsp_ops sof_hda_common_ops = { .pcm_hw_params = hda_dsp_pcm_hw_params, .pcm_hw_free = hda_dsp_stream_hw_free, .pcm_trigger = hda_dsp_pcm_trigger, + .pcm_set_sdw_ignore_suspend = hda_dsp_set_sdw_ignore_suspend, .pcm_pointer = hda_dsp_pcm_pointer, .pcm_ack = hda_dsp_pcm_ack, diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index e1815bfe358b5d..38898f94f6c800 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -58,6 +58,76 @@ static int sdw_clock_stop_quirks = SDW_INTEL_CLK_STOP_BUS_RESET; module_param(sdw_clock_stop_quirks, int, 0444); MODULE_PARM_DESC(sdw_clock_stop_quirks, "SOF SoundWire clock stop quirks"); +void hda_dsp_set_sdw_ignore_suspend(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream, + bool active) +{ + struct snd_soc_pcm_runtime *fe = snd_soc_substream_to_rtd(substream); + struct snd_soc_dpcm *be_link; + int dir = substream->stream; + + if (dir != SNDRV_PCM_STREAM_CAPTURE) + return; + + for_each_dpcm_be(fe, dir, be_link) { + struct snd_soc_pcm_runtime *be = be_link->be; + struct snd_soc_dpcm *fe_link; + struct snd_soc_dai *dai; + bool ignore_suspend = false; + int i; + + for_each_dpcm_fe(be, dir, fe_link) { + struct snd_soc_pcm_runtime *other_fe = fe_link->fe; + struct snd_pcm_substream *fe_substream; + struct snd_sof_pcm *spcm; + bool fe_active; + + if (fe_link->state == SND_SOC_DPCM_LINK_STATE_FREE) + continue; + + fe_substream = snd_soc_dpcm_get_substream(other_fe, dir); + if (!fe_substream || !fe_substream->runtime) + continue; + + spcm = snd_sof_find_spcm_dai(sdev->component, other_fe); + if (!spcm || !spcm->stream[dir].d0i3_compatible || + spcm->stream[dir].dsp_max_burst_size_in_ms > 1) + continue; + + if (other_fe == fe) + fe_active = active; + else + fe_active = spcm->stream[dir].suspend_ignored || + other_fe->dpcm[dir].state == SND_SOC_DPCM_STATE_START; + + if (fe_active) { + ignore_suspend = true; + break; + } + } + + for_each_rtd_cpu_dais(be, i, dai) { + struct snd_soc_dapm_widget *w; + struct snd_sof_widget *swidget; + struct snd_sof_dai *sof_dai; + struct sdw_stream_runtime *sdw_stream; + + w = snd_soc_dai_get_widget(dai, dir); + if (!w || !w->dobj.private) + continue; + + swidget = w->dobj.private; + sof_dai = swidget->private; + if (!sof_dai || sof_dai->type != SOF_DAI_INTEL_ALH) + continue; + + sdw_stream = snd_soc_dai_get_stream(dai, dir); + if (!IS_ERR_OR_NULL(sdw_stream)) + sdw_stream->ignore_suspend = ignore_suspend; + } + } +} + static int sdw_params_stream(struct device *dev, struct sdw_intel_stream_params_data *params_data) { @@ -70,6 +140,7 @@ static int sdw_params_stream(struct device *dev, d->name); return -EINVAL; } + data.dai_index = (params_data->link_id << 8) | d->id; data.dai_data = params_data->alh_stream_id; data.dai_node_id = data.dai_data; @@ -416,6 +487,10 @@ static inline bool hda_dsp_sdw_check_mic_privacy_irq(struct snd_sof_dev *sdev) static inline void hda_dsp_sdw_process_mic_privacy(struct snd_sof_dev *sdev) { } +void hda_dsp_set_sdw_ignore_suspend(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream, + bool active) { } + #endif /* IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL_SOUNDWIRE) */ /* pre fw run operations */ diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h index bc11519427649f..4f4fb64f3d35aa 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -680,6 +680,9 @@ int hda_dsp_stream_hw_free(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream); int hda_dsp_pcm_trigger(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream, int cmd); +void hda_dsp_set_sdw_ignore_suspend(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream, + bool active); 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); diff --git a/sound/soc/sof/ops.h b/sound/soc/sof/ops.h index 19605de680a319..736b2ba37fb7e8 100644 --- a/sound/soc/sof/ops.h +++ b/sound/soc/sof/ops.h @@ -544,6 +544,15 @@ snd_sof_pcm_platform_trigger(struct snd_sof_dev *sdev, return 0; } +static inline void +snd_sof_pcm_platform_set_sdw_ignore_suspend(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream, + bool active) +{ + if (sof_ops(sdev) && sof_ops(sdev)->pcm_set_sdw_ignore_suspend) + sof_ops(sdev)->pcm_set_sdw_ignore_suspend(sdev, substream, active); +} + /* Firmware loading. If the fw_filename argument is NULL, the firmware file set in the * default profile will be loaded. Otherwise, it must contain the full path to the file. */ diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index 0161c0ae8bbdb4..3adaa18819d6a3 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -303,6 +303,7 @@ static int sof_pcm_hw_free(struct snd_soc_component *component, spcm_dbg(spcm, substream->stream, "Entry: hw_free\n"); + snd_sof_pcm_platform_set_sdw_ignore_suspend(sdev, substream, false); ret = sof_pcm_stream_free(sdev, substream, spcm, substream->stream, true); /* unprepare and free the list of DAPM widgets */ @@ -422,6 +423,7 @@ static int sof_pcm_trigger(struct snd_soc_component *component, * remained enabled in D0ix. */ spcm->stream[substream->stream].suspend_ignored = false; + snd_sof_pcm_platform_set_sdw_ignore_suspend(sdev, substream, true); return 0; } @@ -431,6 +433,7 @@ static int sof_pcm_trigger(struct snd_soc_component *component, case SNDRV_PCM_TRIGGER_RESUME: if (spcm->stream[substream->stream].suspend_ignored) { spcm->stream[substream->stream].suspend_ignored = false; + snd_sof_pcm_platform_set_sdw_ignore_suspend(sdev, substream, true); return 0; } spcm_err(spcm, substream->stream, "RESUME trigger is not supported\n"); @@ -449,6 +452,7 @@ static int sof_pcm_trigger(struct snd_soc_component *component, spcm->stream[substream->stream].d0i3_compatible && spcm->stream[substream->stream].dsp_max_burst_size_in_ms <= 1) { spcm->stream[substream->stream].suspend_ignored = true; + snd_sof_pcm_platform_set_sdw_ignore_suspend(sdev, substream, true); return 0; } /* On suspend the DMA must be stopped in DSPless mode */ @@ -503,6 +507,21 @@ static int sof_pcm_trigger(struct snd_soc_component *component, if (!ret && reset_hw_params) ret = sof_pcm_stream_free(sdev, substream, spcm, substream->stream, false); + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + snd_sof_pcm_platform_set_sdw_ignore_suspend(sdev, substream, !ret); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + snd_sof_pcm_platform_set_sdw_ignore_suspend(sdev, substream, false); + break; + default: + break; + } + return ret; } @@ -626,6 +645,7 @@ static int sof_pcm_close(struct snd_soc_component *component, spcm_dbg(spcm, substream->stream, "Entry: close\n"); + snd_sof_pcm_platform_set_sdw_ignore_suspend(sdev, substream, false); err = snd_sof_pcm_platform_close(sdev, substream); if (err < 0) { spcm_err(spcm, substream->stream, diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index 83e4d83cc27cbb..6b2d00173a54d6 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -252,6 +252,10 @@ struct snd_sof_dsp_ops { int (*pcm_trigger)(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream, int cmd); /* optional */ + /* update SoundWire BE ignore_suspend state from an SOF FE trigger */ + void (*pcm_set_sdw_ignore_suspend)(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream, + bool active); /* optional */ /* host stream pointer */ snd_pcm_uframes_t (*pcm_pointer)(struct snd_sof_dev *sdev, From 3bf19fb07201af5a7343a95ed1ba220d07a7fe72 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Fri, 2 Oct 2026 17:42:38 +0800 Subject: [PATCH 5/6] ASoC: SOF: preserve DAPM widgets for WoV PCMs The SOF DSP remains active for wake on voice streams, but DAPM may still power down widgets on the PCM path. Mark the connected endpoint widgets to ignore suspend so DAPM keeps the path powered. Track the widgets whose ignore_suspend flag was set by the PCM in a per-spcm list with a refcount. A widget is only taken over if its ignore_suspend flag was not already set, and the flag is cleared only when its refcount drops to 0. Entries are kept for reuse and freed when the PCM is unloaded. Signed-off-by: Bard Liao --- sound/soc/sof/core.c | 8 +++++ sound/soc/sof/pcm.c | 74 ++++++++++++++++++++++++++++++++++++++++ sound/soc/sof/sof-priv.h | 8 +++++ 3 files changed, 90 insertions(+) diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c index 1f38fe03e49395..49520db269e0ca 100644 --- a/sound/soc/sof/core.c +++ b/sound/soc/sof/core.c @@ -690,6 +690,7 @@ int snd_sof_device_probe(struct device *dev, struct snd_sof_pdata *plat_data) INIT_LIST_HEAD(&sdev->dai_list); INIT_LIST_HEAD(&sdev->dai_link_list); INIT_LIST_HEAD(&sdev->route_list); + INIT_LIST_HEAD(&sdev->ignore_suspend_widget_list); INIT_LIST_HEAD(&sdev->ipc_client_list); INIT_LIST_HEAD(&sdev->ipc_rx_handler_list); INIT_LIST_HEAD(&sdev->fw_state_handler_list); @@ -751,6 +752,7 @@ int snd_sof_device_remove(struct device *dev) { struct snd_sof_dev *sdev = dev_get_drvdata(dev); struct snd_sof_pdata *pdata = sdev->pdata; + struct snd_sof_ignore_suspend_widget *isw, *tmp; int ret; bool aborted = false; @@ -770,6 +772,12 @@ int snd_sof_device_remove(struct device *dev) */ snd_sof_machine_unregister(sdev, pdata); + /* topology is unloaded by now, free any leftover ignore_suspend widget entries */ + list_for_each_entry_safe(isw, tmp, &sdev->ignore_suspend_widget_list, list) { + list_del(&isw->list); + kfree(isw); + } + /* * Balance the runtime pm usage count in case we are faced with an * exception and we forcably prevented D3 power state to preserve diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index 3adaa18819d6a3..c5160cce00ec7c 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -66,6 +66,76 @@ void snd_sof_pcm_period_elapsed(struct snd_pcm_substream *substream) } EXPORT_SYMBOL(snd_sof_pcm_period_elapsed); +static void sof_pcm_update_widget_ignore_suspend(struct snd_sof_dev *sdev, + struct snd_soc_dapm_widget *widget, + bool ignore_suspend) +{ + struct snd_sof_ignore_suspend_widget *isw; + + list_for_each_entry(isw, &sdev->ignore_suspend_widget_list, list) { + if (isw->widget != widget) + continue; + + if (ignore_suspend) { + if (!isw->refcount++) + widget->ignore_suspend = 1; + return; + } + + if (isw->refcount && !--isw->refcount) + widget->ignore_suspend = 0; + return; + } + + /* leave widgets already marked ignore_suspend by others untouched */ + if (!ignore_suspend || widget->ignore_suspend) + return; + + isw = kzalloc(sizeof(*isw), GFP_KERNEL); + if (!isw) + return; + + isw->widget = widget; + isw->refcount = 1; + list_add_tail(&isw->list, &sdev->ignore_suspend_widget_list); + widget->ignore_suspend = 1; +} + +static void sof_pcm_set_widget_ignore_suspend(struct snd_sof_dev *sdev, + struct snd_soc_pcm_runtime *rtd, + struct snd_sof_pcm *spcm, int dir, + bool ignore_suspend) +{ + struct snd_soc_dai *dai; + int j; + + for_each_rtd_cpu_dais(rtd, j, dai) { + struct snd_soc_dapm_widget_list *connected_widgets; + struct snd_soc_dapm_widget *widget; + int connected, i; + + connected = snd_soc_dapm_dai_get_connected_widgets(dai, dir, + &connected_widgets, + NULL); + if (connected >= 0) { + for_each_dapm_widgets(connected_widgets, i, widget) { + if (!widget->is_ep) + continue; + + dev_dbg(sdev->dev, + "WoV PCM %s ignore suspend for widget %s\n", + spcm->pcm.caps[dir].name, widget->name); + sof_pcm_update_widget_ignore_suspend(sdev, widget, ignore_suspend); + } + snd_soc_dapm_dai_free_widgets(&connected_widgets); + } else { + dev_dbg(sdev->dev, + "failed to enumerate D0i3-compatible PCM %s widgets: %d\n", + spcm->pcm.caps[dir].name, connected); + } + } +} + 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, @@ -424,6 +494,7 @@ static int sof_pcm_trigger(struct snd_soc_component *component, */ spcm->stream[substream->stream].suspend_ignored = false; snd_sof_pcm_platform_set_sdw_ignore_suspend(sdev, substream, true); + sof_pcm_set_widget_ignore_suspend(sdev, rtd, spcm, substream->stream, false); return 0; } @@ -434,6 +505,7 @@ static int sof_pcm_trigger(struct snd_soc_component *component, if (spcm->stream[substream->stream].suspend_ignored) { spcm->stream[substream->stream].suspend_ignored = false; snd_sof_pcm_platform_set_sdw_ignore_suspend(sdev, substream, true); + sof_pcm_set_widget_ignore_suspend(sdev, rtd, spcm, substream->stream, false); return 0; } spcm_err(spcm, substream->stream, "RESUME trigger is not supported\n"); @@ -453,6 +525,8 @@ static int sof_pcm_trigger(struct snd_soc_component *component, spcm->stream[substream->stream].dsp_max_burst_size_in_ms <= 1) { spcm->stream[substream->stream].suspend_ignored = true; snd_sof_pcm_platform_set_sdw_ignore_suspend(sdev, substream, true); + /* Set ignore_suspend to the DAPM widgets */ + sof_pcm_set_widget_ignore_suspend(sdev, rtd, spcm, substream->stream, true); return 0; } /* On suspend the DMA must be stopped in DSPless mode */ diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index 6b2d00173a54d6..27361f414e5ab6 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -411,6 +411,13 @@ struct snd_sof_dfsentry { }; }; +/* DAPM widget whose ignore_suspend flag was set by a WoV PCM */ +struct snd_sof_ignore_suspend_widget { + struct snd_soc_dapm_widget *widget; + unsigned int refcount; + struct list_head list; +}; + /* Debug mapping for any DSP memory or registers that can used for debug */ struct snd_sof_debugfs_map { const char *name; @@ -657,6 +664,7 @@ struct snd_sof_dev { struct list_head dai_list; struct list_head dai_link_list; struct list_head route_list; + struct list_head ignore_suspend_widget_list; /* list of widgets marked ignore_suspend by WoV PCMs */ struct snd_soc_component *component; u32 enabled_cores_mask; /* keep track of enabled cores */ bool led_present; From 1c8169255b990fe431fa624c279a03d0025c13ba Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Fri, 16 Oct 2026 16:10:41 +0800 Subject: [PATCH 6/6] ASoC: soc_sdw_utils: Skip prepare for enabled SoundWire streams Multiple front-end streams may share a SoundWire back-end, so a prepare request can arrive after the shared stream is already enabled. Return success without calling sdw_prepare_stream() again in that case. Signed-off-by: Bard Liao --- sound/soc/sdw_utils/soc_sdw_utils.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sound/soc/sdw_utils/soc_sdw_utils.c b/sound/soc/sdw_utils/soc_sdw_utils.c index e3ee8f385952f0..75d5e4cf876cb2 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");