Message ID | 20221209114529.3909192-2-kai.vehmanen@linux.intel.com (mailing list archive) |
---|---|
State | Accepted |
Commit | 2aa2a5ead0ee0a358bf80a2984a641d1bf2adc2a |
Headers | show |
Series | ASoC: SOF: remove unregister calls from shutdown | expand |
Hi kai nit: I think you have an extra " at the end of the subject Thanks for the patch! On Fri, 9 Dec 2022 at 12:46, Kai Vehmanen <kai.vehmanen@linux.intel.com> wrote: > > If system shutdown has not been completed cleanly, it is possible the > DMA stream shutdown has not been done, or was not clean. > > If this is the case, Intel TGL/ADL HDA platforms may fail to shutdown > cleanly due to pending HDA DMA transactions. To avoid this, detect this > scenario in the shutdown callback, and perform an additional controller > reset. This has been tested to unblock S5 entry if this condition is > hit. > > Co-developed-by: Archana Patni <archana.patni@intel.com> > Signed-off-by: Archana Patni <archana.patni@intel.com> > Signed-off-by: Kai Vehmanen <kai.vehmanen@linux.intel.com> > Reviewed-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com> > Reviewed-by: Péter Ujfalusi <peter.ujfalusi@linux.intel.com> > Reviewed-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com> Tested-by: Ricardo Ribalda <ribalda@chromium.org> > --- > sound/soc/sof/intel/hda-dsp.c | 72 +++++++++++++++++++++++++++++++++++ > sound/soc/sof/intel/hda.h | 1 + > sound/soc/sof/intel/tgl.c | 2 +- > 3 files changed, 74 insertions(+), 1 deletion(-) > > diff --git a/sound/soc/sof/intel/hda-dsp.c b/sound/soc/sof/intel/hda-dsp.c > index 5fa29df54b42..b4eacae8564c 100644 > --- a/sound/soc/sof/intel/hda-dsp.c > +++ b/sound/soc/sof/intel/hda-dsp.c > @@ -878,6 +878,78 @@ int hda_dsp_suspend(struct snd_sof_dev *sdev, u32 target_state) > return snd_sof_dsp_set_power_state(sdev, &target_dsp_state); > } > > +static unsigned int hda_dsp_check_for_dma_streams(struct snd_sof_dev *sdev) > +{ > + struct hdac_bus *bus = sof_to_bus(sdev); > + struct hdac_stream *s; > + unsigned int active_streams = 0; > + int sd_offset; > + u32 val; > + > + list_for_each_entry(s, &bus->stream_list, list) { > + sd_offset = SOF_STREAM_SD_OFFSET(s); > + val = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, > + sd_offset); > + if (val & SOF_HDA_SD_CTL_DMA_START) > + active_streams |= BIT(s->index); > + } > + > + return active_streams; > +} > + > +static int hda_dsp_s5_quirk(struct snd_sof_dev *sdev) > +{ > + int ret; > + > + /* > + * Do not assume a certain timing between the prior > + * suspend flow, and running of this quirk function. > + * This is needed if the controller was just put > + * to reset before calling this function. > + */ > + usleep_range(500, 1000); > + > + /* > + * Take controller out of reset to flush DMA > + * transactions. > + */ > + ret = hda_dsp_ctrl_link_reset(sdev, false); > + if (ret < 0) > + return ret; > + > + usleep_range(500, 1000); > + > + /* Restore state for shutdown, back to reset */ > + ret = hda_dsp_ctrl_link_reset(sdev, true); > + if (ret < 0) > + return ret; > + > + return ret; > +} > + > +int hda_dsp_shutdown_dma_flush(struct snd_sof_dev *sdev) > +{ > + unsigned int active_streams; > + int ret, ret2; > + > + /* check if DMA cleanup has been successful */ > + active_streams = hda_dsp_check_for_dma_streams(sdev); > + > + sdev->system_suspend_target = SOF_SUSPEND_S3; > + ret = snd_sof_suspend(sdev->dev); > + > + if (active_streams) { > + dev_warn(sdev->dev, > + "There were active DSP streams (%#x) at shutdown, trying to recover\n", > + active_streams); > + ret2 = hda_dsp_s5_quirk(sdev); > + if (ret2 < 0) > + dev_err(sdev->dev, "shutdown recovery failed (%d)\n", ret2); > + } > + > + return ret; > +} > + > int hda_dsp_shutdown(struct snd_sof_dev *sdev) > { > sdev->system_suspend_target = SOF_SUSPEND_S3; > diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h > index 022ce80968dd..caccaf8fba9c 100644 > --- a/sound/soc/sof/intel/hda.h > +++ b/sound/soc/sof/intel/hda.h > @@ -592,6 +592,7 @@ int hda_dsp_resume(struct snd_sof_dev *sdev); > int hda_dsp_runtime_suspend(struct snd_sof_dev *sdev); > int hda_dsp_runtime_resume(struct snd_sof_dev *sdev); > int hda_dsp_runtime_idle(struct snd_sof_dev *sdev); > +int hda_dsp_shutdown_dma_flush(struct snd_sof_dev *sdev); > int hda_dsp_shutdown(struct snd_sof_dev *sdev); > int hda_dsp_set_hw_params_upon_resume(struct snd_sof_dev *sdev); > void hda_dsp_dump(struct snd_sof_dev *sdev, u32 flags); > diff --git a/sound/soc/sof/intel/tgl.c b/sound/soc/sof/intel/tgl.c > index 30f2f49ee149..58ac3a46e6a7 100644 > --- a/sound/soc/sof/intel/tgl.c > +++ b/sound/soc/sof/intel/tgl.c > @@ -60,7 +60,7 @@ int sof_tgl_ops_init(struct snd_sof_dev *sdev) > memcpy(&sof_tgl_ops, &sof_hda_common_ops, sizeof(struct snd_sof_dsp_ops)); > > /* probe/remove/shutdown */ > - sof_tgl_ops.shutdown = hda_dsp_shutdown; > + sof_tgl_ops.shutdown = hda_dsp_shutdown_dma_flush; > > if (sdev->pdata->ipc_type == SOF_IPC) { > /* doorbell */ > -- > 2.38.1 >
diff --git a/sound/soc/sof/intel/hda-dsp.c b/sound/soc/sof/intel/hda-dsp.c index 5fa29df54b42..b4eacae8564c 100644 --- a/sound/soc/sof/intel/hda-dsp.c +++ b/sound/soc/sof/intel/hda-dsp.c @@ -878,6 +878,78 @@ int hda_dsp_suspend(struct snd_sof_dev *sdev, u32 target_state) return snd_sof_dsp_set_power_state(sdev, &target_dsp_state); } +static unsigned int hda_dsp_check_for_dma_streams(struct snd_sof_dev *sdev) +{ + struct hdac_bus *bus = sof_to_bus(sdev); + struct hdac_stream *s; + unsigned int active_streams = 0; + int sd_offset; + u32 val; + + list_for_each_entry(s, &bus->stream_list, list) { + sd_offset = SOF_STREAM_SD_OFFSET(s); + val = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, + sd_offset); + if (val & SOF_HDA_SD_CTL_DMA_START) + active_streams |= BIT(s->index); + } + + return active_streams; +} + +static int hda_dsp_s5_quirk(struct snd_sof_dev *sdev) +{ + int ret; + + /* + * Do not assume a certain timing between the prior + * suspend flow, and running of this quirk function. + * This is needed if the controller was just put + * to reset before calling this function. + */ + usleep_range(500, 1000); + + /* + * Take controller out of reset to flush DMA + * transactions. + */ + ret = hda_dsp_ctrl_link_reset(sdev, false); + if (ret < 0) + return ret; + + usleep_range(500, 1000); + + /* Restore state for shutdown, back to reset */ + ret = hda_dsp_ctrl_link_reset(sdev, true); + if (ret < 0) + return ret; + + return ret; +} + +int hda_dsp_shutdown_dma_flush(struct snd_sof_dev *sdev) +{ + unsigned int active_streams; + int ret, ret2; + + /* check if DMA cleanup has been successful */ + active_streams = hda_dsp_check_for_dma_streams(sdev); + + sdev->system_suspend_target = SOF_SUSPEND_S3; + ret = snd_sof_suspend(sdev->dev); + + if (active_streams) { + dev_warn(sdev->dev, + "There were active DSP streams (%#x) at shutdown, trying to recover\n", + active_streams); + ret2 = hda_dsp_s5_quirk(sdev); + if (ret2 < 0) + dev_err(sdev->dev, "shutdown recovery failed (%d)\n", ret2); + } + + return ret; +} + int hda_dsp_shutdown(struct snd_sof_dev *sdev) { sdev->system_suspend_target = SOF_SUSPEND_S3; diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h index 022ce80968dd..caccaf8fba9c 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -592,6 +592,7 @@ int hda_dsp_resume(struct snd_sof_dev *sdev); int hda_dsp_runtime_suspend(struct snd_sof_dev *sdev); int hda_dsp_runtime_resume(struct snd_sof_dev *sdev); int hda_dsp_runtime_idle(struct snd_sof_dev *sdev); +int hda_dsp_shutdown_dma_flush(struct snd_sof_dev *sdev); int hda_dsp_shutdown(struct snd_sof_dev *sdev); int hda_dsp_set_hw_params_upon_resume(struct snd_sof_dev *sdev); void hda_dsp_dump(struct snd_sof_dev *sdev, u32 flags); diff --git a/sound/soc/sof/intel/tgl.c b/sound/soc/sof/intel/tgl.c index 30f2f49ee149..58ac3a46e6a7 100644 --- a/sound/soc/sof/intel/tgl.c +++ b/sound/soc/sof/intel/tgl.c @@ -60,7 +60,7 @@ int sof_tgl_ops_init(struct snd_sof_dev *sdev) memcpy(&sof_tgl_ops, &sof_hda_common_ops, sizeof(struct snd_sof_dsp_ops)); /* probe/remove/shutdown */ - sof_tgl_ops.shutdown = hda_dsp_shutdown; + sof_tgl_ops.shutdown = hda_dsp_shutdown_dma_flush; if (sdev->pdata->ipc_type == SOF_IPC) { /* doorbell */