Skip to content

Commit

Permalink
ASoC: SOF: sof-pcm/pm: Stop paused streams before the system suspend
Browse files Browse the repository at this point in the history
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 stopped state.

Explanation of the change:
Streams moved to SUSPENDED state from PAUSED without trigger. If a stream
does not support RESUME then on system resume the RESUME trigger is not
sent, the stream's state and suspended_state remains untouched.
When the user space releases the pause then the core will reject this
because the state of the stream is _not_ PAUSED, it is still SUSPENDED.

From this point user space will do the normal (hw_params) prepare and
START, PAUSE_RELEASE trigger will not be sent by the core after the
system has resumed.

Link: thesofproject#5035
Signed-off-by: Peter Ujfalusi <[email protected]>
  • Loading branch information
ujfalusi committed Nov 6, 2024
1 parent 541a998 commit e5409bb
Show file tree
Hide file tree
Showing 3 changed files with 90 additions and 0 deletions.
77 changes: 77 additions & 0 deletions sound/soc/sof/pcm.c
Original file line number Diff line number Diff line change
Expand Up @@ -691,6 +691,83 @@ static snd_pcm_sframes_t sof_pcm_delay(struct snd_soc_component *component,
return 0;
}

static int sof_pcm_trigger_suspended_paused_streams(struct snd_sof_dev *sdev,
int cmd)
{
struct snd_pcm_substream *substream;
struct snd_pcm_runtime *runtime;
struct snd_sof_pcm *spcm;
int dir, ret;

list_for_each_entry(spcm, &sdev->pcm_list, list) {
for_each_pcm_streams(dir) {
substream = spcm->stream[dir].substream;
if (!substream || !substream->runtime)
continue;

/*
* The stream supports RESUME, it is expected that it
* is handling the corner case of suspending while
* a stream is paused
*/
runtime = substream->runtime;
if (runtime->info & SNDRV_PCM_INFO_RESUME)
continue;

/* Only send the trigger to a paused and suspended stream */
if (runtime->state != SNDRV_PCM_STATE_SUSPENDED ||
runtime->suspended_state != SNDRV_PCM_STATE_PAUSED)
continue;

ret = substream->ops->trigger(substream, cmd);
if (ret) {
dev_err(sdev->dev,
"%s: trigger %d failed for stream %d, dir: %d\n",
__func__, cmd, spcm->pcm.pcm_id, dir);
return ret;
}
}
}

return 0;
}

int sof_pcm_stop_paused_on_suspend(struct snd_sof_dev *sdev)
{
int ret;

/*
* Handle the corner case of system suspend while at least one stream is
* paused.
* Paused streams will not receive the SUSPEND triggers, they are
* 'silently' moved to SUSPENDED state.
*
* The workaround for the corner case is applicable for streams not
* supporting RESUME.
*
* First we need to move (trigger) the paused streams to RUNNING state,
* then we need to stop them
*
* Explanation: Streams moved to SUSPENDED state from PAUSED without
* trigger. If a stream does not support RESUME then on system resume
* the RESUME trigger is not sent, the stream's state and suspended_state
* remains untouched. When the user space releases the pause then the
* core will reject this because the state of the stream is _not_ PAUSED,
* it is still SUSPENDED.
* From this point user space will do the normal (hw_params) prepare and
* START, PAUSE_RELEASE trigger will not be sent by the core after the
* system has resumed.
*/
ret = sof_pcm_trigger_suspended_paused_streams(sdev,
SNDRV_PCM_TRIGGER_PAUSE_RELEASE);
if (ret)
return ret;

return sof_pcm_trigger_suspended_paused_streams(sdev,
SNDRV_PCM_TRIGGER_STOP);
}
EXPORT_SYMBOL(sof_pcm_stop_paused_on_suspend);

void snd_sof_new_platform_drv(struct snd_sof_dev *sdev)
{
struct snd_soc_component_driver *pd = &sdev->plat_drv;
Expand Down
11 changes: 11 additions & 0 deletions sound/soc/sof/pm.c
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,17 @@ 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 special handling of paused streams
* For more details, see the comment section in
* sof_pcm_stop_paused_on_suspend)(
*/
if (!runtime_suspend) {
ret = sof_pcm_stop_paused_on_suspend(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
Expand Down
2 changes: 2 additions & 0 deletions sound/soc/sof/sof-priv.h
Original file line number Diff line number Diff line change
Expand Up @@ -720,6 +720,8 @@ void snd_sof_complete(struct device *dev);

void snd_sof_new_platform_drv(struct snd_sof_dev *sdev);

int sof_pcm_stop_paused_on_suspend(struct snd_sof_dev *sdev);

/*
* Compress support
*/
Expand Down

0 comments on commit e5409bb

Please sign in to comment.