From 5ba35f2dbfc614de2db975f4720481b9fdba4016 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 5 Jun 2024 12:52:28 +0300 Subject: [PATCH] ASoC: SOF: sof-pcm/pm: On system suspend, move the paused streams to suspended state Paused streams will not receive a suspend trigger, they will be marked by ALSA core as suspended and it's state is saved. Since the pause stream is not in a fully stopped state, for example DMA might be still enabled (just the trigger source is removed/disabled) we need to make sure that the hardware is ready to handle the suspend. This involves a bit more than just stopping a DMA since we also need to communicate with the firmware in a delicate sequence to follow IP programming flows. To make things a bit more challenging, these flows are different between IPC versions due to the fact that they use different messages to implement the same functionality. To avoid adding yet another path, callbacks and sequencing for handling the corner case of suspending while a stream is paused, and do this for each IPC versions and platforms, we can move the stream back to running just to put it to suspended state. In this way we will essentially reduce the suspend cases to two: no audio and running audio (the paused audio becomes a running audio case), cutting down on the probability of misaligned handling of cases. Link: https://github.com/thesofproject/linux/issues/5035 --- sound/soc/sof/pcm.c | 58 ++++++++++++++++++++++++++++++++++++++++ sound/soc/sof/pm.c | 10 +++++++ sound/soc/sof/sof-priv.h | 1 + 3 files changed, 69 insertions(+) diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index baad4c1445aa71..58cc8927ad78f2 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -689,6 +689,64 @@ static snd_pcm_sframes_t sof_pcm_delay(struct snd_soc_component *component, return 0; } +int sof_pcm_suspend_paused(struct snd_sof_dev *sdev) +{ + struct snd_pcm_substream *substream; + struct snd_sof_pcm *spcm; + int dir, ret; + + /* + * Search for streams which was in paused state at suspend to properly + * suspend it. + * Due to the differences in flows between IPC versions it is preferred + * to use the high level pcm ops to move the stream to running then to + * suspended instead of trying to hand-replicate the flows. + */ + list_for_each_entry(spcm, &sdev->pcm_list, list) { + for_each_pcm_streams(dir) { + substream = spcm->stream[dir].substream; + if (!substream || !substream->runtime) + continue; + + /* + * Paused streams will not receive a suspend trigger + * but moved to suspended state. + */ + if (!(substream->runtime->state == SNDRV_PCM_STATE_SUSPENDED && + substream->runtime->suspended_state == SNDRV_PCM_STATE_PAUSED)) + continue; + + /* + * The stream needs to be released and then suspended to + * make sure that it is in a state where it can handle + * the system suspend + */ + ret = substream->ops->trigger(substream, + SNDRV_PCM_TRIGGER_PAUSE_RELEASE); + if (ret) { + dev_err(sdev->dev, + "%s: PAUSE_RELEASE failed for %s (id: %d): %d\n", + __func__, substream->name, + spcm->pcm.pcm_id, ret); + return ret; + } + + ret = substream->ops->trigger(substream, + SNDRV_PCM_TRIGGER_SUSPEND); + if (ret) { + dev_err(sdev->dev, + "%s: SUSPEND failed for %s (id: %d): %d\n", + __func__, substream->name, + spcm->pcm.pcm_id, ret); + return ret; + } + } + } + + return 0; +} +EXPORT_SYMBOL(sof_pcm_suspend_paused); + void snd_sof_new_platform_drv(struct snd_sof_dev *sdev) { struct snd_soc_component_driver *pd = &sdev->plat_drv; diff --git a/sound/soc/sof/pm.c b/sound/soc/sof/pm.c index 8e3bcf602beb31..58fcafda2e0fda 100644 --- a/sound/soc/sof/pm.c +++ b/sound/soc/sof/pm.c @@ -210,6 +210,16 @@ static int sof_suspend(struct device *dev, bool runtime_suspend) if (runtime_suspend && !sof_ops(sdev)->runtime_suspend) return 0; + /* + * On system suspend we need to properly suspend paused streams since + * they have not receiving a suspend trigger. + */ + if (!runtime_suspend) { + ret = sof_pcm_suspend_paused(sdev); + if (ret < 0) + return ret; + } + /* we need to tear down pipelines only if the DSP hardware is * active, which happens for PCI devices. if the device is * suspended, it is brought back to full power and then diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index b40ab412ed50ba..662d9a0b3d2bb0 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -703,6 +703,7 @@ int snd_sof_prepare(struct device *dev); void snd_sof_complete(struct device *dev); void snd_sof_new_platform_drv(struct snd_sof_dev *sdev); +int sof_pcm_suspend_paused(struct snd_sof_dev *sdev); /* * Compress support