From 2aa2a5ead0ee0a358bf80a2984a641d1bf2adc2a Mon Sep 17 00:00:00 2001 From: Kai Vehmanen Date: Fri, 9 Dec 2022 13:45:28 +0200 Subject: ASoC: SOF: Intel: pci-tgl: unblock S5 entry if DMA stop has failed" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 Signed-off-by: Archana Patni Signed-off-by: Kai Vehmanen Reviewed-by: Pierre-Louis Bossart Reviewed-by: Péter Ujfalusi Reviewed-by: Ranjani Sridharan Link: https://lore.kernel.org/r/20221209114529.3909192-2-kai.vehmanen@linux.intel.com Signed-off-by: Mark Brown --- 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(-) (limited to 'sound') 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 */ -- cgit v1.2.3 From 44fda61d2bcfb74a942df93959e083a4e8eff75f Mon Sep 17 00:00:00 2001 From: Kai Vehmanen Date: Fri, 9 Dec 2022 13:45:29 +0200 Subject: ASoC: SOF: Revert: "core: unregister clients and machine drivers in .shutdown" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The unregister machine drivers call is not safe to do when kexec is used. Kexec-lite gets blocked with following backtrace: [ 84.943749] Freezing user space processes ... (elapsed 0.111 seconds) done. [ 246.784446] INFO: task kexec-lite:5123 blocked for more than 122 seconds. [ 246.819035] Call Trace: [ 246.821782] [ 246.824186] __schedule+0x5f9/0x1263 [ 246.828231] schedule+0x87/0xc5 [ 246.831779] snd_card_disconnect_sync+0xb5/0x127 ... [ 246.889249] snd_sof_device_shutdown+0xb4/0x150 [ 246.899317] pci_device_shutdown+0x37/0x61 [ 246.903990] device_shutdown+0x14c/0x1d6 [ 246.908391] kernel_kexec+0x45/0xb9 This reverts commit 83bfc7e793b555291785136c3ae86abcdc046887. Reported-by: Ricardo Ribalda Cc: Ricardo Ribalda Signed-off-by: Kai Vehmanen Reviewed-by: Pierre-Louis Bossart Reviewed-by: Péter Ujfalusi Reviewed-by: Ranjani Sridharan Link: https://lore.kernel.org/r/20221209114529.3909192-3-kai.vehmanen@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/core.c | 9 --------- 1 file changed, 9 deletions(-) (limited to 'sound') diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c index 3e6141d03770..625977a29d8a 100644 --- a/sound/soc/sof/core.c +++ b/sound/soc/sof/core.c @@ -475,19 +475,10 @@ EXPORT_SYMBOL(snd_sof_device_remove); int snd_sof_device_shutdown(struct device *dev) { struct snd_sof_dev *sdev = dev_get_drvdata(dev); - struct snd_sof_pdata *pdata = sdev->pdata; if (IS_ENABLED(CONFIG_SND_SOC_SOF_PROBE_WORK_QUEUE)) cancel_work_sync(&sdev->probe_work); - /* - * make sure clients and machine driver(s) are unregistered to force - * all userspace devices to be closed prior to the DSP shutdown sequence - */ - sof_unregister_clients(sdev); - - snd_sof_machine_unregister(sdev, pdata); - if (sdev->fw_state == SOF_FW_BOOT_COMPLETE) return snd_sof_shutdown(sdev); -- cgit v1.2.3