diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index 8d9d40145ad02c..fe656d29f772a1 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -58,6 +58,13 @@ int hda_ctrl_dai_widget_setup(struct snd_soc_dapm_widget *w) return -EINVAL; } + /* DAI already configured, reset it before reconfiguring it */ + if (sof_dai->configured) { + ret = hda_ctrl_dai_widget_free(w); + if (ret < 0) + return ret; + } + config = &sof_dai->dai_config[sof_dai->current_config]; /* diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index cde3f65c43780b..56db927390c98b 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -100,9 +100,8 @@ void snd_sof_pcm_period_elapsed(struct snd_pcm_substream *substream) } EXPORT_SYMBOL(snd_sof_pcm_period_elapsed); -static int sof_pcm_dsp_pcm_free(struct snd_pcm_substream *substream, - struct snd_sof_dev *sdev, - struct snd_sof_pcm *spcm) +int sof_pcm_dsp_pcm_free(struct snd_pcm_substream *substream, struct snd_sof_dev *sdev, + struct snd_sof_pcm *spcm) { struct sof_ipc_stream stream; struct sof_ipc_reply reply; diff --git a/sound/soc/sof/pm.c b/sound/soc/sof/pm.c index c9e46f13b1f438..e65f4f4d6df943 100644 --- a/sound/soc/sof/pm.c +++ b/sound/soc/sof/pm.c @@ -191,7 +191,7 @@ static int sof_suspend(struct device *dev, bool runtime_suspend) if (sdev->fw_state != SOF_FW_BOOT_COMPLETE) goto suspend; - /* set restore_stream for all streams during system suspend */ + /* prepare for streams to be resumed properly upon resume */ if (!runtime_suspend) { ret = sof_set_hw_params_upon_resume(sdev->dev); if (ret < 0) { diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c index a870c72461f212..fb7b5f9b9a9eea 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -718,6 +718,55 @@ int sof_set_up_pipelines(struct snd_sof_dev *sdev, bool verify) return 0; } +/* + * Free the PCM, its associated widgets and set the prepared flag to false for all PCMs that + * did not get suspended(ex: paused streams) so the widgets can be set up again during resume. + */ +static int sof_tear_down_left_over_pipelines(struct snd_sof_dev *sdev) +{ + struct snd_sof_widget *swidget; + struct snd_sof_pcm *spcm; + int dir, ret; + + /* + * free all PCMs and their associated DAPM widgets if their connected DAPM widget + * list is not NULL. This should only be true for paused streams at this point. + * This is equivalent to the handling of FE DAI suspend trigger for running streams. + */ + list_for_each_entry(spcm, &sdev->pcm_list, list) + for_each_pcm_streams(dir) { + struct snd_pcm_substream *substream = spcm->stream[dir].substream; + + if (!substream || !substream->runtime) + continue; + + if (spcm->stream[dir].list) { + ret = sof_pcm_dsp_pcm_free(substream, sdev, spcm); + if (ret < 0) + return ret; + + ret = sof_widget_list_free(sdev, spcm, dir); + if (ret < 0) { + dev_err(sdev->dev, "failed to free widgets during suspend\n"); + return ret; + } + } + } + + /* + * free any left over DAI widgets. This is equivalent to the handling of suspend trigger + * for the BE DAI for running streams. + */ + list_for_each_entry(swidget, &sdev->widget_list, list) + if (WIDGET_IS_DAI(swidget->id) && swidget->use_count == 1) { + ret = sof_widget_free(sdev, swidget); + if (ret < 0) + return ret; + } + + return 0; +} + /* * For older firmware, this function doesn't free widgets for static pipelines during suspend. * It only resets use_count for all widgets. @@ -732,8 +781,8 @@ int sof_tear_down_pipelines(struct snd_sof_dev *sdev, bool verify) /* * This function is called during suspend and for one-time topology verification during * first boot. In both cases, there is no need to protect swidget->use_count and - * sroute->setup because during suspend all streams are suspended and during topology - * loading the sound card unavailable to open PCMs. + * sroute->setup because during suspend all running streams are suspended and during + * topology loading the sound card unavailable to open PCMs. */ list_for_each_entry(swidget, &sdev->widget_list, list) { if (swidget->dynamic_pipeline_widget) @@ -752,6 +801,18 @@ int sof_tear_down_pipelines(struct snd_sof_dev *sdev, bool verify) return ret; } + /* + * Tear down all pipelines associated with PCMs that did not get suspended + * and unset the prepare flag so that they can be set up again during resume. + */ + if (!verify) { + ret = sof_tear_down_left_over_pipelines(sdev); + if (ret < 0) { + dev_err(sdev->dev, "failed to tear down paused pipelines\n"); + return ret; + } + } + list_for_each_entry(sroute, &sdev->route_list, list) sroute->setup = false; diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h index 389d56ac3aba52..1c4f59d347177a 100644 --- a/sound/soc/sof/sof-audio.h +++ b/sound/soc/sof/sof-audio.h @@ -265,4 +265,6 @@ int sof_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget); /* PCM */ int sof_widget_list_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, int dir); int sof_widget_list_free(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, int dir); +int sof_pcm_dsp_pcm_free(struct snd_pcm_substream *substream, struct snd_sof_dev *sdev, + struct snd_sof_pcm *spcm); #endif