diff --git a/sound/soc/sof/pm.c b/sound/soc/sof/pm.c index e65f4f4d6df943..ebd143488b8cec 100644 --- a/sound/soc/sof/pm.c +++ b/sound/soc/sof/pm.c @@ -12,42 +12,6 @@ #include "sof-priv.h" #include "sof-audio.h" -/* - * Helper function to determine the target DSP state during - * system suspend. This function only cares about the device - * D-states. Platform-specific substates, if any, should be - * handled by the platform-specific parts. - */ -static u32 snd_sof_dsp_power_target(struct snd_sof_dev *sdev) -{ - u32 target_dsp_state; - - switch (sdev->system_suspend_target) { - case SOF_SUSPEND_S3: - /* DSP should be in D3 if the system is suspending to S3 */ - target_dsp_state = SOF_DSP_PM_D3; - break; - case SOF_SUSPEND_S0IX: - /* - * Currently, the only criterion for retaining the DSP in D0 - * is that there are streams that ignored the suspend trigger. - * Additional criteria such Soundwire clock-stop mode and - * device suspend latency considerations will be added later. - */ - if (snd_sof_stream_suspend_ignored(sdev)) - target_dsp_state = SOF_DSP_PM_D0; - else - target_dsp_state = SOF_DSP_PM_D3; - break; - default: - /* This case would be during runtime suspend */ - target_dsp_state = SOF_DSP_PM_D3; - break; - } - - return target_dsp_state; -} - static int sof_send_pm_ctx_ipc(struct snd_sof_dev *sdev, int cmd) { struct sof_ipc_pm_ctx pm_ctx; @@ -116,11 +80,18 @@ static int sof_resume(struct device *dev, bool runtime_resume) /* * Nothing further to be done for platforms that support the low power - * D0 substate. + * D0 substate, and we need to re-enable the DMA trace if it was stopped */ if (!runtime_resume && sof_ops(sdev)->set_power_state && - old_state == SOF_DSP_PM_D0) + old_state == SOF_DSP_PM_D0) { + ret = snd_sof_enable_trace(sdev); + if (ret < 0) + /* non fatal */ + dev_warn(sdev->dev, + "failed to enable trace after resume %d\n", + ret); return 0; + } sdev->fw_state = SOF_FW_BOOT_PREPARE; @@ -147,8 +118,8 @@ static int sof_resume(struct device *dev, bool runtime_resume) return ret; } - /* resume DMA trace, only need send ipc */ - ret = snd_sof_init_trace_ipc(sdev); + /* Re-enable DMA trace */ + ret = snd_sof_enable_trace(sdev); if (ret < 0) { /* non fatal */ dev_warn(sdev->dev, @@ -210,9 +181,6 @@ static int sof_suspend(struct device *dev, bool runtime_suspend) sof_tear_down_pipelines(sdev, false); - /* release trace */ - snd_sof_release_trace(sdev); - #if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_ENABLE_DEBUGFS_CACHE) /* cache debugfs contents during runtime suspend */ if (runtime_suspend) @@ -238,6 +206,9 @@ static int sof_suspend(struct device *dev, bool runtime_suspend) suspend: + /* release trace */ + snd_sof_release_trace(sdev); + /* return if the DSP was not probed successfully */ if (sdev->fw_state == SOF_FW_BOOT_NOT_STARTED) return 0; diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h index 1c4f59d347177a..d09e84e369edef 100644 --- a/sound/soc/sof/sof-audio.h +++ b/sound/soc/sof/sof-audio.h @@ -255,6 +255,42 @@ int sof_set_hw_params_upon_resume(struct device *dev); bool snd_sof_stream_suspend_ignored(struct snd_sof_dev *sdev); bool snd_sof_dsp_only_d0i3_compatible_stream_active(struct snd_sof_dev *sdev); +/* + * Helper function to determine the target DSP state during + * system suspend. This function only cares about the device + * D-states. Platform-specific substates, if any, should be + * handled by the platform-specific parts. + */ +static inline u32 snd_sof_dsp_power_target(struct snd_sof_dev *sdev) +{ + u32 target_dsp_state; + + switch (sdev->system_suspend_target) { + case SOF_SUSPEND_S3: + /* DSP should be in D3 if the system is suspending to S3 */ + target_dsp_state = SOF_DSP_PM_D3; + break; + case SOF_SUSPEND_S0IX: + /* + * Currently, the only criterion for retaining the DSP in D0 + * is that there are streams that ignored the suspend trigger. + * Additional criteria such Soundwire clock-stop mode and + * device suspend latency considerations will be added later. + */ + if (snd_sof_stream_suspend_ignored(sdev)) + target_dsp_state = SOF_DSP_PM_D0; + else + target_dsp_state = SOF_DSP_PM_D3; + break; + default: + /* This case would be during runtime suspend */ + target_dsp_state = SOF_DSP_PM_D3; + break; + } + + return target_dsp_state; +} + /* Machine driver enumeration */ int sof_machine_register(struct snd_sof_dev *sdev, void *pdata); void sof_machine_unregister(struct snd_sof_dev *sdev, void *pdata); diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index 4f7017ef7d5aeb..2a59156372a1bd 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -384,6 +384,12 @@ enum snd_sof_fw_state { SOF_FW_BOOT_COMPLETE, }; +enum sof_dtrace_state { + SOF_DTRACE_DISABLED = 0, + SOF_DTRACE_STOPPED, + SOF_DTRACE_ENABLED, +}; + /* * SOF Device Level. */ @@ -475,9 +481,9 @@ struct snd_sof_dev { wait_queue_head_t trace_sleep; u32 host_offset; bool dtrace_is_supported; /* set with Kconfig or module parameter */ - bool dtrace_is_enabled; bool dtrace_error; bool dtrace_draining; + enum sof_dtrace_state dtrace_state; bool msi_enabled; @@ -572,7 +578,7 @@ void snd_sof_get_status(struct snd_sof_dev *sdev, u32 panic_code, u32 tracep_code, void *oops, struct sof_ipc_panic_info *panic_info, void *stack, size_t stack_words); -int snd_sof_init_trace_ipc(struct snd_sof_dev *sdev); +int snd_sof_enable_trace(struct snd_sof_dev *sdev); void snd_sof_handle_fw_exception(struct snd_sof_dev *sdev); int snd_sof_dbg_memory_info_init(struct snd_sof_dev *sdev); int snd_sof_debugfs_add_region_item_iomem(struct snd_sof_dev *sdev, diff --git a/sound/soc/sof/trace.c b/sound/soc/sof/trace.c index e3afc3dac7d17a..d53fb79c44f0cb 100644 --- a/sound/soc/sof/trace.c +++ b/sound/soc/sof/trace.c @@ -11,6 +11,7 @@ #include #include #include "sof-priv.h" +#include "sof-audio.h" #include "ops.h" #define TRACE_FILTER_ELEMENTS_PER_ENTRY 4 @@ -262,7 +263,7 @@ static size_t sof_wait_trace_avail(struct snd_sof_dev *sdev, if (ret) return ret; - if (!sdev->dtrace_is_enabled && sdev->dtrace_draining) { + if (sdev->dtrace_state != SOF_DTRACE_ENABLED && sdev->dtrace_draining) { /* * tracing has ended and all traces have been * read by client, return EOF @@ -338,7 +339,7 @@ static int sof_dfsentry_trace_release(struct inode *inode, struct file *file) struct snd_sof_dev *sdev = dfse->sdev; /* avoid duplicate traces at next open */ - if (!sdev->dtrace_is_enabled) + if (sdev->dtrace_state != SOF_DTRACE_ENABLED) sdev->host_offset = 0; return 0; @@ -378,7 +379,7 @@ static int trace_debugfs_create(struct snd_sof_dev *sdev) return 0; } -int snd_sof_init_trace_ipc(struct snd_sof_dev *sdev) +int snd_sof_enable_trace(struct snd_sof_dev *sdev) { struct sof_ipc_fw_ready *ready = &sdev->fw_ready; struct sof_ipc_fw_version *v = &ready->version; @@ -389,9 +390,12 @@ int snd_sof_init_trace_ipc(struct snd_sof_dev *sdev) if (!sdev->dtrace_is_supported) return 0; - if (sdev->dtrace_is_enabled || !sdev->dma_trace_pages) + if (sdev->dtrace_state == SOF_DTRACE_ENABLED || !sdev->dma_trace_pages) return -EINVAL; + if (sdev->dtrace_state == SOF_DTRACE_STOPPED) + goto start_only; + /* set IPC parameters */ params.hdr.cmd = SOF_IPC_GLB_TRACE_MSG; /* PARAMS_EXT is only supported from ABI 3.7.0 onwards */ @@ -429,6 +433,7 @@ int snd_sof_init_trace_ipc(struct snd_sof_dev *sdev) goto trace_release; } +start_only: ret = snd_sof_dma_trace_trigger(sdev, SNDRV_PCM_TRIGGER_START); if (ret < 0) { dev_err(sdev->dev, @@ -436,7 +441,7 @@ int snd_sof_init_trace_ipc(struct snd_sof_dev *sdev) goto trace_release; } - sdev->dtrace_is_enabled = true; + sdev->dtrace_state = SOF_DTRACE_ENABLED; return 0; @@ -453,7 +458,7 @@ int snd_sof_init_trace(struct snd_sof_dev *sdev) return 0; /* set false before start initialization */ - sdev->dtrace_is_enabled = false; + sdev->dtrace_state = SOF_DTRACE_DISABLED; /* allocate trace page table buffer */ ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, sdev->dev, @@ -491,7 +496,7 @@ int snd_sof_init_trace(struct snd_sof_dev *sdev) init_waitqueue_head(&sdev->trace_sleep); - ret = snd_sof_init_trace_ipc(sdev); + ret = snd_sof_enable_trace(sdev); if (ret < 0) goto table_err; @@ -511,7 +516,8 @@ int snd_sof_trace_update_pos(struct snd_sof_dev *sdev, if (!sdev->dtrace_is_supported) return 0; - if (sdev->dtrace_is_enabled && sdev->host_offset != posn->host_offset) { + if (sdev->dtrace_state == SOF_DTRACE_ENABLED && + sdev->host_offset != posn->host_offset) { sdev->host_offset = posn->host_offset; wake_up(&sdev->trace_sleep); } @@ -530,7 +536,7 @@ void snd_sof_trace_notify_for_error(struct snd_sof_dev *sdev) if (!sdev->dtrace_is_supported) return; - if (sdev->dtrace_is_enabled) { + if (sdev->dtrace_state == SOF_DTRACE_ENABLED) { sdev->dtrace_error = true; wake_up(&sdev->trace_sleep); } @@ -540,8 +546,9 @@ EXPORT_SYMBOL(snd_sof_trace_notify_for_error); void snd_sof_release_trace(struct snd_sof_dev *sdev) { int ret; + bool only_stop; - if (!sdev->dtrace_is_supported || !sdev->dtrace_is_enabled) + if (!sdev->dtrace_is_supported || sdev->dtrace_state == SOF_DTRACE_DISABLED) return; ret = snd_sof_dma_trace_trigger(sdev, SNDRV_PCM_TRIGGER_STOP); @@ -549,12 +556,20 @@ void snd_sof_release_trace(struct snd_sof_dev *sdev) dev_err(sdev->dev, "error: snd_sof_dma_trace_trigger: stop: %d\n", ret); + only_stop = (snd_sof_dsp_power_target(sdev) == SOF_DSP_PM_D0); + if (only_stop) { + sdev->dtrace_state = SOF_DTRACE_STOPPED; + goto out; + } + ret = snd_sof_dma_trace_release(sdev); if (ret < 0) dev_err(sdev->dev, "error: fail in snd_sof_dma_trace_release %d\n", ret); - sdev->dtrace_is_enabled = false; + sdev->dtrace_state = SOF_DTRACE_DISABLED; + +out: sdev->dtrace_draining = true; wake_up(&sdev->trace_sleep); }