Pierre-Louis Bossart | e149ca2 | 2020-05-01 09:58:50 -0500 | [diff] [blame] | 1 | // SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) |
Liam Girdwood | 8920153c | 2019-04-12 11:05:16 -0500 | [diff] [blame] | 2 | // |
| 3 | // This file is provided under a dual BSD/GPLv2 license. When using or |
| 4 | // redistributing this file, you may do so under either license. |
| 5 | // |
| 6 | // Copyright(c) 2018 Intel Corporation. All rights reserved. |
| 7 | // |
| 8 | // Author: Liam Girdwood <liam.r.girdwood@linux.intel.com> |
| 9 | // |
| 10 | |
| 11 | #include "ops.h" |
| 12 | #include "sof-priv.h" |
Ranjani Sridharan | ee1e79b | 2019-12-04 15:15:51 -0600 | [diff] [blame] | 13 | #include "sof-audio.h" |
Liam Girdwood | 8920153c | 2019-04-12 11:05:16 -0500 | [diff] [blame] | 14 | |
Ranjani Sridharan | 700d167 | 2020-01-29 16:07:21 -0600 | [diff] [blame] | 15 | /* |
| 16 | * Helper function to determine the target DSP state during |
| 17 | * system suspend. This function only cares about the device |
| 18 | * D-states. Platform-specific substates, if any, should be |
| 19 | * handled by the platform-specific parts. |
| 20 | */ |
| 21 | static u32 snd_sof_dsp_power_target(struct snd_sof_dev *sdev) |
| 22 | { |
| 23 | u32 target_dsp_state; |
| 24 | |
| 25 | switch (sdev->system_suspend_target) { |
| 26 | case SOF_SUSPEND_S3: |
| 27 | /* DSP should be in D3 if the system is suspending to S3 */ |
| 28 | target_dsp_state = SOF_DSP_PM_D3; |
| 29 | break; |
| 30 | case SOF_SUSPEND_S0IX: |
| 31 | /* |
| 32 | * Currently, the only criterion for retaining the DSP in D0 |
| 33 | * is that there are streams that ignored the suspend trigger. |
| 34 | * Additional criteria such Soundwire clock-stop mode and |
| 35 | * device suspend latency considerations will be added later. |
| 36 | */ |
| 37 | if (snd_sof_stream_suspend_ignored(sdev)) |
| 38 | target_dsp_state = SOF_DSP_PM_D0; |
| 39 | else |
| 40 | target_dsp_state = SOF_DSP_PM_D3; |
| 41 | break; |
| 42 | default: |
| 43 | /* This case would be during runtime suspend */ |
| 44 | target_dsp_state = SOF_DSP_PM_D3; |
| 45 | break; |
| 46 | } |
| 47 | |
| 48 | return target_dsp_state; |
| 49 | } |
| 50 | |
Keyon Jie | 7c7eba2 | 2019-10-25 17:41:08 -0500 | [diff] [blame] | 51 | static int sof_send_pm_ctx_ipc(struct snd_sof_dev *sdev, int cmd) |
Liam Girdwood | 8920153c | 2019-04-12 11:05:16 -0500 | [diff] [blame] | 52 | { |
| 53 | struct sof_ipc_pm_ctx pm_ctx; |
| 54 | struct sof_ipc_reply reply; |
| 55 | |
| 56 | memset(&pm_ctx, 0, sizeof(pm_ctx)); |
| 57 | |
| 58 | /* configure ctx save ipc message */ |
| 59 | pm_ctx.hdr.size = sizeof(pm_ctx); |
| 60 | pm_ctx.hdr.cmd = SOF_IPC_GLB_PM_MSG | cmd; |
| 61 | |
| 62 | /* send ctx save ipc to dsp */ |
| 63 | return sof_ipc_tx_message(sdev->ipc, pm_ctx.hdr.cmd, &pm_ctx, |
| 64 | sizeof(pm_ctx), &reply, sizeof(reply)); |
| 65 | } |
| 66 | |
Liam Girdwood | 8920153c | 2019-04-12 11:05:16 -0500 | [diff] [blame] | 67 | #if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_ENABLE_DEBUGFS_CACHE) |
| 68 | static void sof_cache_debugfs(struct snd_sof_dev *sdev) |
| 69 | { |
| 70 | struct snd_sof_dfsentry *dfse; |
| 71 | |
| 72 | list_for_each_entry(dfse, &sdev->dfsentry_list, list) { |
| 73 | |
| 74 | /* nothing to do if debugfs buffer is not IO mem */ |
| 75 | if (dfse->type == SOF_DFSENTRY_TYPE_BUF) |
| 76 | continue; |
| 77 | |
| 78 | /* cache memory that is only accessible in D0 */ |
| 79 | if (dfse->access_type == SOF_DEBUGFS_ACCESS_D0_ONLY) |
| 80 | memcpy_fromio(dfse->cache_buf, dfse->io_mem, |
| 81 | dfse->size); |
| 82 | } |
| 83 | } |
| 84 | #endif |
| 85 | |
| 86 | static int sof_resume(struct device *dev, bool runtime_resume) |
| 87 | { |
| 88 | struct snd_sof_dev *sdev = dev_get_drvdata(dev); |
Ranjani Sridharan | 61e285c | 2020-01-29 16:07:22 -0600 | [diff] [blame] | 89 | u32 old_state = sdev->dsp_power_state.state; |
Liam Girdwood | 8920153c | 2019-04-12 11:05:16 -0500 | [diff] [blame] | 90 | int ret; |
| 91 | |
| 92 | /* do nothing if dsp resume callbacks are not set */ |
Daniel Baluta | c26fde3 | 2020-05-15 16:59:51 +0300 | [diff] [blame^] | 93 | if (!runtime_resume && !sof_ops(sdev)->resume) |
| 94 | return 0; |
| 95 | |
| 96 | if (runtime_resume && !sof_ops(sdev)->runtime_resume) |
Liam Girdwood | 8920153c | 2019-04-12 11:05:16 -0500 | [diff] [blame] | 97 | return 0; |
| 98 | |
Pierre-Louis Bossart | 410e5e5 | 2020-01-24 15:36:21 -0600 | [diff] [blame] | 99 | /* DSP was never successfully started, nothing to resume */ |
| 100 | if (sdev->first_boot) |
| 101 | return 0; |
| 102 | |
Liam Girdwood | 8920153c | 2019-04-12 11:05:16 -0500 | [diff] [blame] | 103 | /* |
| 104 | * if the runtime_resume flag is set, call the runtime_resume routine |
| 105 | * or else call the system resume routine |
| 106 | */ |
| 107 | if (runtime_resume) |
| 108 | ret = snd_sof_dsp_runtime_resume(sdev); |
| 109 | else |
| 110 | ret = snd_sof_dsp_resume(sdev); |
| 111 | if (ret < 0) { |
| 112 | dev_err(sdev->dev, |
| 113 | "error: failed to power up DSP after resume\n"); |
| 114 | return ret; |
| 115 | } |
| 116 | |
Ranjani Sridharan | 61e285c | 2020-01-29 16:07:22 -0600 | [diff] [blame] | 117 | /* Nothing further to do if resuming from a low-power D0 substate */ |
| 118 | if (!runtime_resume && old_state == SOF_DSP_PM_D0) |
Ranjani Sridharan | fb9a811 | 2020-01-29 16:07:19 -0600 | [diff] [blame] | 119 | return 0; |
| 120 | |
Ranjani Sridharan | 6ca5cec | 2019-12-17 18:26:09 -0600 | [diff] [blame] | 121 | sdev->fw_state = SOF_FW_BOOT_PREPARE; |
| 122 | |
Liam Girdwood | 8920153c | 2019-04-12 11:05:16 -0500 | [diff] [blame] | 123 | /* load the firmware */ |
| 124 | ret = snd_sof_load_firmware(sdev); |
| 125 | if (ret < 0) { |
| 126 | dev_err(sdev->dev, |
| 127 | "error: failed to load DSP firmware after resume %d\n", |
| 128 | ret); |
| 129 | return ret; |
| 130 | } |
| 131 | |
Ranjani Sridharan | 6ca5cec | 2019-12-17 18:26:09 -0600 | [diff] [blame] | 132 | sdev->fw_state = SOF_FW_BOOT_IN_PROGRESS; |
| 133 | |
| 134 | /* |
| 135 | * Boot the firmware. The FW boot status will be modified |
| 136 | * in snd_sof_run_firmware() depending on the outcome. |
| 137 | */ |
Liam Girdwood | 8920153c | 2019-04-12 11:05:16 -0500 | [diff] [blame] | 138 | ret = snd_sof_run_firmware(sdev); |
| 139 | if (ret < 0) { |
| 140 | dev_err(sdev->dev, |
| 141 | "error: failed to boot DSP firmware after resume %d\n", |
| 142 | ret); |
| 143 | return ret; |
| 144 | } |
| 145 | |
| 146 | /* resume DMA trace, only need send ipc */ |
| 147 | ret = snd_sof_init_trace_ipc(sdev); |
| 148 | if (ret < 0) { |
| 149 | /* non fatal */ |
| 150 | dev_warn(sdev->dev, |
| 151 | "warning: failed to init trace after resume %d\n", |
| 152 | ret); |
| 153 | } |
| 154 | |
| 155 | /* restore pipelines */ |
Ranjani Sridharan | ee1e79b | 2019-12-04 15:15:51 -0600 | [diff] [blame] | 156 | ret = sof_restore_pipelines(sdev->dev); |
Liam Girdwood | 8920153c | 2019-04-12 11:05:16 -0500 | [diff] [blame] | 157 | if (ret < 0) { |
| 158 | dev_err(sdev->dev, |
| 159 | "error: failed to restore pipeline after resume %d\n", |
| 160 | ret); |
| 161 | return ret; |
| 162 | } |
| 163 | |
| 164 | /* notify DSP of system resume */ |
Keyon Jie | 7c7eba2 | 2019-10-25 17:41:08 -0500 | [diff] [blame] | 165 | ret = sof_send_pm_ctx_ipc(sdev, SOF_IPC_PM_CTX_RESTORE); |
Liam Girdwood | 8920153c | 2019-04-12 11:05:16 -0500 | [diff] [blame] | 166 | if (ret < 0) |
| 167 | dev_err(sdev->dev, |
| 168 | "error: ctx_restore ipc error during resume %d\n", |
| 169 | ret); |
| 170 | |
| 171 | return ret; |
| 172 | } |
| 173 | |
| 174 | static int sof_suspend(struct device *dev, bool runtime_suspend) |
| 175 | { |
| 176 | struct snd_sof_dev *sdev = dev_get_drvdata(dev); |
Ranjani Sridharan | 61e285c | 2020-01-29 16:07:22 -0600 | [diff] [blame] | 177 | u32 target_state = 0; |
Liam Girdwood | 8920153c | 2019-04-12 11:05:16 -0500 | [diff] [blame] | 178 | int ret; |
| 179 | |
| 180 | /* do nothing if dsp suspend callback is not set */ |
Daniel Baluta | c26fde3 | 2020-05-15 16:59:51 +0300 | [diff] [blame^] | 181 | if (!runtime_suspend && !sof_ops(sdev)->suspend) |
| 182 | return 0; |
| 183 | |
| 184 | if (runtime_suspend && !sof_ops(sdev)->runtime_suspend) |
Liam Girdwood | 8920153c | 2019-04-12 11:05:16 -0500 | [diff] [blame] | 185 | return 0; |
| 186 | |
Ranjani Sridharan | 6ca5cec | 2019-12-17 18:26:09 -0600 | [diff] [blame] | 187 | if (sdev->fw_state != SOF_FW_BOOT_COMPLETE) |
Ranjani Sridharan | fb9a811 | 2020-01-29 16:07:19 -0600 | [diff] [blame] | 188 | goto suspend; |
Liam Girdwood | 8920153c | 2019-04-12 11:05:16 -0500 | [diff] [blame] | 189 | |
| 190 | /* set restore_stream for all streams during system suspend */ |
Ranjani Sridharan | 7077a07 | 2019-06-12 12:23:38 -0500 | [diff] [blame] | 191 | if (!runtime_suspend) { |
Ranjani Sridharan | ee1e79b | 2019-12-04 15:15:51 -0600 | [diff] [blame] | 192 | ret = sof_set_hw_params_upon_resume(sdev->dev); |
Ranjani Sridharan | 7077a07 | 2019-06-12 12:23:38 -0500 | [diff] [blame] | 193 | if (ret < 0) { |
| 194 | dev_err(sdev->dev, |
| 195 | "error: setting hw_params flag during suspend %d\n", |
| 196 | ret); |
| 197 | return ret; |
| 198 | } |
| 199 | } |
Liam Girdwood | 8920153c | 2019-04-12 11:05:16 -0500 | [diff] [blame] | 200 | |
Ranjani Sridharan | 61e285c | 2020-01-29 16:07:22 -0600 | [diff] [blame] | 201 | target_state = snd_sof_dsp_power_target(sdev); |
Ranjani Sridharan | fb9a811 | 2020-01-29 16:07:19 -0600 | [diff] [blame] | 202 | |
Ranjani Sridharan | 61e285c | 2020-01-29 16:07:22 -0600 | [diff] [blame] | 203 | /* Skip to platform-specific suspend if DSP is entering D0 */ |
| 204 | if (target_state == SOF_DSP_PM_D0) |
Ranjani Sridharan | fb9a811 | 2020-01-29 16:07:19 -0600 | [diff] [blame] | 205 | goto suspend; |
Ranjani Sridharan | fb9a811 | 2020-01-29 16:07:19 -0600 | [diff] [blame] | 206 | |
| 207 | /* release trace */ |
| 208 | snd_sof_release_trace(sdev); |
| 209 | |
Liam Girdwood | 8920153c | 2019-04-12 11:05:16 -0500 | [diff] [blame] | 210 | #if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_ENABLE_DEBUGFS_CACHE) |
| 211 | /* cache debugfs contents during runtime suspend */ |
| 212 | if (runtime_suspend) |
| 213 | sof_cache_debugfs(sdev); |
| 214 | #endif |
| 215 | /* notify DSP of upcoming power down */ |
Keyon Jie | 7c7eba2 | 2019-10-25 17:41:08 -0500 | [diff] [blame] | 216 | ret = sof_send_pm_ctx_ipc(sdev, SOF_IPC_PM_CTX_SAVE); |
Kai Vehmanen | e2eba55 | 2019-06-12 11:57:04 -0500 | [diff] [blame] | 217 | if (ret == -EBUSY || ret == -EAGAIN) { |
| 218 | /* |
| 219 | * runtime PM has logic to handle -EBUSY/-EAGAIN so |
| 220 | * pass these errors up |
| 221 | */ |
Liam Girdwood | 8920153c | 2019-04-12 11:05:16 -0500 | [diff] [blame] | 222 | dev_err(sdev->dev, |
| 223 | "error: ctx_save ipc error during suspend %d\n", |
| 224 | ret); |
| 225 | return ret; |
Kai Vehmanen | e2eba55 | 2019-06-12 11:57:04 -0500 | [diff] [blame] | 226 | } else if (ret < 0) { |
| 227 | /* FW in unexpected state, continue to power down */ |
| 228 | dev_warn(sdev->dev, |
| 229 | "ctx_save ipc error %d, proceeding with suspend\n", |
| 230 | ret); |
Liam Girdwood | 8920153c | 2019-04-12 11:05:16 -0500 | [diff] [blame] | 231 | } |
| 232 | |
Ranjani Sridharan | fb9a811 | 2020-01-29 16:07:19 -0600 | [diff] [blame] | 233 | suspend: |
Ranjani Sridharan | 6ca5cec | 2019-12-17 18:26:09 -0600 | [diff] [blame] | 234 | |
| 235 | /* return if the DSP was not probed successfully */ |
| 236 | if (sdev->fw_state == SOF_FW_BOOT_NOT_STARTED) |
| 237 | return 0; |
| 238 | |
Ranjani Sridharan | fb9a811 | 2020-01-29 16:07:19 -0600 | [diff] [blame] | 239 | /* platform-specific suspend */ |
Liam Girdwood | 8920153c | 2019-04-12 11:05:16 -0500 | [diff] [blame] | 240 | if (runtime_suspend) |
Fred Oh | 1c38c92 | 2019-07-22 09:13:50 -0500 | [diff] [blame] | 241 | ret = snd_sof_dsp_runtime_suspend(sdev); |
Liam Girdwood | 8920153c | 2019-04-12 11:05:16 -0500 | [diff] [blame] | 242 | else |
Ranjani Sridharan | 61e285c | 2020-01-29 16:07:22 -0600 | [diff] [blame] | 243 | ret = snd_sof_dsp_suspend(sdev, target_state); |
Liam Girdwood | 8920153c | 2019-04-12 11:05:16 -0500 | [diff] [blame] | 244 | if (ret < 0) |
| 245 | dev_err(sdev->dev, |
| 246 | "error: failed to power down DSP during suspend %d\n", |
| 247 | ret); |
| 248 | |
Ranjani Sridharan | 61e285c | 2020-01-29 16:07:22 -0600 | [diff] [blame] | 249 | /* Do not reset FW state if DSP is in D0 */ |
| 250 | if (target_state == SOF_DSP_PM_D0) |
Ranjani Sridharan | fb9a811 | 2020-01-29 16:07:19 -0600 | [diff] [blame] | 251 | return ret; |
| 252 | |
Ranjani Sridharan | 6ca5cec | 2019-12-17 18:26:09 -0600 | [diff] [blame] | 253 | /* reset FW state */ |
| 254 | sdev->fw_state = SOF_FW_BOOT_NOT_STARTED; |
| 255 | |
Liam Girdwood | 8920153c | 2019-04-12 11:05:16 -0500 | [diff] [blame] | 256 | return ret; |
| 257 | } |
| 258 | |
| 259 | int snd_sof_runtime_suspend(struct device *dev) |
| 260 | { |
| 261 | return sof_suspend(dev, true); |
| 262 | } |
| 263 | EXPORT_SYMBOL(snd_sof_runtime_suspend); |
| 264 | |
Kai Vehmanen | 62fde97 | 2019-07-02 16:24:27 +0300 | [diff] [blame] | 265 | int snd_sof_runtime_idle(struct device *dev) |
| 266 | { |
| 267 | struct snd_sof_dev *sdev = dev_get_drvdata(dev); |
| 268 | |
| 269 | return snd_sof_dsp_runtime_idle(sdev); |
| 270 | } |
| 271 | EXPORT_SYMBOL(snd_sof_runtime_idle); |
| 272 | |
Liam Girdwood | 8920153c | 2019-04-12 11:05:16 -0500 | [diff] [blame] | 273 | int snd_sof_runtime_resume(struct device *dev) |
| 274 | { |
| 275 | return sof_resume(dev, true); |
| 276 | } |
| 277 | EXPORT_SYMBOL(snd_sof_runtime_resume); |
| 278 | |
Liam Girdwood | 8920153c | 2019-04-12 11:05:16 -0500 | [diff] [blame] | 279 | int snd_sof_resume(struct device *dev) |
| 280 | { |
| 281 | return sof_resume(dev, false); |
| 282 | } |
| 283 | EXPORT_SYMBOL(snd_sof_resume); |
| 284 | |
| 285 | int snd_sof_suspend(struct device *dev) |
| 286 | { |
| 287 | return sof_suspend(dev, false); |
| 288 | } |
| 289 | EXPORT_SYMBOL(snd_sof_suspend); |
Keyon Jie | 0b50b3b | 2019-10-25 17:41:17 -0500 | [diff] [blame] | 290 | |
| 291 | int snd_sof_prepare(struct device *dev) |
| 292 | { |
| 293 | struct snd_sof_dev *sdev = dev_get_drvdata(dev); |
| 294 | |
| 295 | #if defined(CONFIG_ACPI) |
Ranjani Sridharan | 043ae13 | 2020-01-29 16:07:20 -0600 | [diff] [blame] | 296 | if (acpi_target_system_state() == ACPI_STATE_S0) |
| 297 | sdev->system_suspend_target = SOF_SUSPEND_S0IX; |
| 298 | else |
| 299 | sdev->system_suspend_target = SOF_SUSPEND_S3; |
Keyon Jie | 0b50b3b | 2019-10-25 17:41:17 -0500 | [diff] [blame] | 300 | #else |
| 301 | /* will suspend to S3 by default */ |
Ranjani Sridharan | 043ae13 | 2020-01-29 16:07:20 -0600 | [diff] [blame] | 302 | sdev->system_suspend_target = SOF_SUSPEND_S3; |
Keyon Jie | 0b50b3b | 2019-10-25 17:41:17 -0500 | [diff] [blame] | 303 | #endif |
| 304 | |
| 305 | return 0; |
| 306 | } |
| 307 | EXPORT_SYMBOL(snd_sof_prepare); |
| 308 | |
| 309 | void snd_sof_complete(struct device *dev) |
| 310 | { |
| 311 | struct snd_sof_dev *sdev = dev_get_drvdata(dev); |
| 312 | |
Ranjani Sridharan | 043ae13 | 2020-01-29 16:07:20 -0600 | [diff] [blame] | 313 | sdev->system_suspend_target = SOF_SUSPEND_NONE; |
Keyon Jie | 0b50b3b | 2019-10-25 17:41:17 -0500 | [diff] [blame] | 314 | } |
| 315 | EXPORT_SYMBOL(snd_sof_complete); |