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 | fc907cc | 2020-05-26 15:36:34 -0500 | [diff] [blame] | 117 | /* |
| 118 | * Nothing further to be done for platforms that support the low power |
| 119 | * D0 substate. |
| 120 | */ |
| 121 | if (!runtime_resume && sof_ops(sdev)->set_power_state && |
| 122 | old_state == SOF_DSP_PM_D0) |
Ranjani Sridharan | fb9a811 | 2020-01-29 16:07:19 -0600 | [diff] [blame] | 123 | return 0; |
| 124 | |
Ranjani Sridharan | 6ca5cec | 2019-12-17 18:26:09 -0600 | [diff] [blame] | 125 | sdev->fw_state = SOF_FW_BOOT_PREPARE; |
| 126 | |
Liam Girdwood | 8920153c | 2019-04-12 11:05:16 -0500 | [diff] [blame] | 127 | /* load the firmware */ |
| 128 | ret = snd_sof_load_firmware(sdev); |
| 129 | if (ret < 0) { |
| 130 | dev_err(sdev->dev, |
| 131 | "error: failed to load DSP firmware after resume %d\n", |
| 132 | ret); |
| 133 | return ret; |
| 134 | } |
| 135 | |
Ranjani Sridharan | 6ca5cec | 2019-12-17 18:26:09 -0600 | [diff] [blame] | 136 | sdev->fw_state = SOF_FW_BOOT_IN_PROGRESS; |
| 137 | |
| 138 | /* |
| 139 | * Boot the firmware. The FW boot status will be modified |
| 140 | * in snd_sof_run_firmware() depending on the outcome. |
| 141 | */ |
Liam Girdwood | 8920153c | 2019-04-12 11:05:16 -0500 | [diff] [blame] | 142 | ret = snd_sof_run_firmware(sdev); |
| 143 | if (ret < 0) { |
| 144 | dev_err(sdev->dev, |
| 145 | "error: failed to boot DSP firmware after resume %d\n", |
| 146 | ret); |
| 147 | return ret; |
| 148 | } |
| 149 | |
| 150 | /* resume DMA trace, only need send ipc */ |
| 151 | ret = snd_sof_init_trace_ipc(sdev); |
| 152 | if (ret < 0) { |
| 153 | /* non fatal */ |
| 154 | dev_warn(sdev->dev, |
| 155 | "warning: failed to init trace after resume %d\n", |
| 156 | ret); |
| 157 | } |
| 158 | |
| 159 | /* restore pipelines */ |
Ranjani Sridharan | ee1e79b | 2019-12-04 15:15:51 -0600 | [diff] [blame] | 160 | ret = sof_restore_pipelines(sdev->dev); |
Liam Girdwood | 8920153c | 2019-04-12 11:05:16 -0500 | [diff] [blame] | 161 | if (ret < 0) { |
| 162 | dev_err(sdev->dev, |
| 163 | "error: failed to restore pipeline after resume %d\n", |
| 164 | ret); |
| 165 | return ret; |
| 166 | } |
| 167 | |
| 168 | /* notify DSP of system resume */ |
Keyon Jie | 7c7eba2 | 2019-10-25 17:41:08 -0500 | [diff] [blame] | 169 | ret = sof_send_pm_ctx_ipc(sdev, SOF_IPC_PM_CTX_RESTORE); |
Liam Girdwood | 8920153c | 2019-04-12 11:05:16 -0500 | [diff] [blame] | 170 | if (ret < 0) |
| 171 | dev_err(sdev->dev, |
| 172 | "error: ctx_restore ipc error during resume %d\n", |
| 173 | ret); |
| 174 | |
| 175 | return ret; |
| 176 | } |
| 177 | |
| 178 | static int sof_suspend(struct device *dev, bool runtime_suspend) |
| 179 | { |
| 180 | struct snd_sof_dev *sdev = dev_get_drvdata(dev); |
Ranjani Sridharan | 61e285c | 2020-01-29 16:07:22 -0600 | [diff] [blame] | 181 | u32 target_state = 0; |
Liam Girdwood | 8920153c | 2019-04-12 11:05:16 -0500 | [diff] [blame] | 182 | int ret; |
| 183 | |
| 184 | /* do nothing if dsp suspend callback is not set */ |
Daniel Baluta | c26fde3 | 2020-05-15 16:59:51 +0300 | [diff] [blame] | 185 | if (!runtime_suspend && !sof_ops(sdev)->suspend) |
| 186 | return 0; |
| 187 | |
| 188 | if (runtime_suspend && !sof_ops(sdev)->runtime_suspend) |
Liam Girdwood | 8920153c | 2019-04-12 11:05:16 -0500 | [diff] [blame] | 189 | return 0; |
| 190 | |
Ranjani Sridharan | 6ca5cec | 2019-12-17 18:26:09 -0600 | [diff] [blame] | 191 | if (sdev->fw_state != SOF_FW_BOOT_COMPLETE) |
Ranjani Sridharan | fb9a811 | 2020-01-29 16:07:19 -0600 | [diff] [blame] | 192 | goto suspend; |
Liam Girdwood | 8920153c | 2019-04-12 11:05:16 -0500 | [diff] [blame] | 193 | |
Ranjani Sridharan | a1ce6e4 | 2021-09-28 10:40:30 +0300 | [diff] [blame^] | 194 | /* prepare for streams to be resumed properly upon resume */ |
Ranjani Sridharan | 7077a07 | 2019-06-12 12:23:38 -0500 | [diff] [blame] | 195 | if (!runtime_suspend) { |
Ranjani Sridharan | ee1e79b | 2019-12-04 15:15:51 -0600 | [diff] [blame] | 196 | ret = sof_set_hw_params_upon_resume(sdev->dev); |
Ranjani Sridharan | 7077a07 | 2019-06-12 12:23:38 -0500 | [diff] [blame] | 197 | if (ret < 0) { |
| 198 | dev_err(sdev->dev, |
| 199 | "error: setting hw_params flag during suspend %d\n", |
| 200 | ret); |
| 201 | return ret; |
| 202 | } |
| 203 | } |
Liam Girdwood | 8920153c | 2019-04-12 11:05:16 -0500 | [diff] [blame] | 204 | |
Ranjani Sridharan | 61e285c | 2020-01-29 16:07:22 -0600 | [diff] [blame] | 205 | target_state = snd_sof_dsp_power_target(sdev); |
Ranjani Sridharan | fb9a811 | 2020-01-29 16:07:19 -0600 | [diff] [blame] | 206 | |
Ranjani Sridharan | 61e285c | 2020-01-29 16:07:22 -0600 | [diff] [blame] | 207 | /* Skip to platform-specific suspend if DSP is entering D0 */ |
| 208 | if (target_state == SOF_DSP_PM_D0) |
Ranjani Sridharan | fb9a811 | 2020-01-29 16:07:19 -0600 | [diff] [blame] | 209 | goto suspend; |
Ranjani Sridharan | fb9a811 | 2020-01-29 16:07:19 -0600 | [diff] [blame] | 210 | |
| 211 | /* release trace */ |
| 212 | snd_sof_release_trace(sdev); |
| 213 | |
Liam Girdwood | 8920153c | 2019-04-12 11:05:16 -0500 | [diff] [blame] | 214 | #if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_ENABLE_DEBUGFS_CACHE) |
| 215 | /* cache debugfs contents during runtime suspend */ |
| 216 | if (runtime_suspend) |
| 217 | sof_cache_debugfs(sdev); |
| 218 | #endif |
| 219 | /* notify DSP of upcoming power down */ |
Keyon Jie | 7c7eba2 | 2019-10-25 17:41:08 -0500 | [diff] [blame] | 220 | ret = sof_send_pm_ctx_ipc(sdev, SOF_IPC_PM_CTX_SAVE); |
Kai Vehmanen | e2eba55 | 2019-06-12 11:57:04 -0500 | [diff] [blame] | 221 | if (ret == -EBUSY || ret == -EAGAIN) { |
| 222 | /* |
| 223 | * runtime PM has logic to handle -EBUSY/-EAGAIN so |
| 224 | * pass these errors up |
| 225 | */ |
Liam Girdwood | 8920153c | 2019-04-12 11:05:16 -0500 | [diff] [blame] | 226 | dev_err(sdev->dev, |
| 227 | "error: ctx_save ipc error during suspend %d\n", |
| 228 | ret); |
| 229 | return ret; |
Kai Vehmanen | e2eba55 | 2019-06-12 11:57:04 -0500 | [diff] [blame] | 230 | } else if (ret < 0) { |
| 231 | /* FW in unexpected state, continue to power down */ |
| 232 | dev_warn(sdev->dev, |
| 233 | "ctx_save ipc error %d, proceeding with suspend\n", |
| 234 | ret); |
Liam Girdwood | 8920153c | 2019-04-12 11:05:16 -0500 | [diff] [blame] | 235 | } |
| 236 | |
Ranjani Sridharan | fb9a811 | 2020-01-29 16:07:19 -0600 | [diff] [blame] | 237 | suspend: |
Ranjani Sridharan | 6ca5cec | 2019-12-17 18:26:09 -0600 | [diff] [blame] | 238 | |
| 239 | /* return if the DSP was not probed successfully */ |
| 240 | if (sdev->fw_state == SOF_FW_BOOT_NOT_STARTED) |
| 241 | return 0; |
| 242 | |
Ranjani Sridharan | fb9a811 | 2020-01-29 16:07:19 -0600 | [diff] [blame] | 243 | /* platform-specific suspend */ |
Liam Girdwood | 8920153c | 2019-04-12 11:05:16 -0500 | [diff] [blame] | 244 | if (runtime_suspend) |
Fred Oh | 1c38c92 | 2019-07-22 09:13:50 -0500 | [diff] [blame] | 245 | ret = snd_sof_dsp_runtime_suspend(sdev); |
Liam Girdwood | 8920153c | 2019-04-12 11:05:16 -0500 | [diff] [blame] | 246 | else |
Ranjani Sridharan | 61e285c | 2020-01-29 16:07:22 -0600 | [diff] [blame] | 247 | ret = snd_sof_dsp_suspend(sdev, target_state); |
Liam Girdwood | 8920153c | 2019-04-12 11:05:16 -0500 | [diff] [blame] | 248 | if (ret < 0) |
| 249 | dev_err(sdev->dev, |
| 250 | "error: failed to power down DSP during suspend %d\n", |
| 251 | ret); |
| 252 | |
Ranjani Sridharan | 61e285c | 2020-01-29 16:07:22 -0600 | [diff] [blame] | 253 | /* Do not reset FW state if DSP is in D0 */ |
| 254 | if (target_state == SOF_DSP_PM_D0) |
Ranjani Sridharan | fb9a811 | 2020-01-29 16:07:19 -0600 | [diff] [blame] | 255 | return ret; |
| 256 | |
Ranjani Sridharan | 6ca5cec | 2019-12-17 18:26:09 -0600 | [diff] [blame] | 257 | /* reset FW state */ |
| 258 | sdev->fw_state = SOF_FW_BOOT_NOT_STARTED; |
Kai Vehmanen | b640e8a | 2021-05-28 17:43:30 +0300 | [diff] [blame] | 259 | sdev->enabled_cores_mask = 0; |
Ranjani Sridharan | 6ca5cec | 2019-12-17 18:26:09 -0600 | [diff] [blame] | 260 | |
Liam Girdwood | 8920153c | 2019-04-12 11:05:16 -0500 | [diff] [blame] | 261 | return ret; |
| 262 | } |
| 263 | |
Marcin Rajwa | 3541aef | 2020-05-15 16:59:52 +0300 | [diff] [blame] | 264 | int snd_sof_dsp_power_down_notify(struct snd_sof_dev *sdev) |
| 265 | { |
| 266 | /* Notify DSP of upcoming power down */ |
| 267 | if (sof_ops(sdev)->remove) |
| 268 | return sof_send_pm_ctx_ipc(sdev, SOF_IPC_PM_CTX_SAVE); |
| 269 | |
| 270 | return 0; |
| 271 | } |
| 272 | |
Liam Girdwood | 8920153c | 2019-04-12 11:05:16 -0500 | [diff] [blame] | 273 | int snd_sof_runtime_suspend(struct device *dev) |
| 274 | { |
| 275 | return sof_suspend(dev, true); |
| 276 | } |
| 277 | EXPORT_SYMBOL(snd_sof_runtime_suspend); |
| 278 | |
Kai Vehmanen | 62fde97 | 2019-07-02 16:24:27 +0300 | [diff] [blame] | 279 | int snd_sof_runtime_idle(struct device *dev) |
| 280 | { |
| 281 | struct snd_sof_dev *sdev = dev_get_drvdata(dev); |
| 282 | |
| 283 | return snd_sof_dsp_runtime_idle(sdev); |
| 284 | } |
| 285 | EXPORT_SYMBOL(snd_sof_runtime_idle); |
| 286 | |
Liam Girdwood | 8920153c | 2019-04-12 11:05:16 -0500 | [diff] [blame] | 287 | int snd_sof_runtime_resume(struct device *dev) |
| 288 | { |
| 289 | return sof_resume(dev, true); |
| 290 | } |
| 291 | EXPORT_SYMBOL(snd_sof_runtime_resume); |
| 292 | |
Liam Girdwood | 8920153c | 2019-04-12 11:05:16 -0500 | [diff] [blame] | 293 | int snd_sof_resume(struct device *dev) |
| 294 | { |
| 295 | return sof_resume(dev, false); |
| 296 | } |
| 297 | EXPORT_SYMBOL(snd_sof_resume); |
| 298 | |
| 299 | int snd_sof_suspend(struct device *dev) |
| 300 | { |
| 301 | return sof_suspend(dev, false); |
| 302 | } |
| 303 | EXPORT_SYMBOL(snd_sof_suspend); |
Keyon Jie | 0b50b3b | 2019-10-25 17:41:17 -0500 | [diff] [blame] | 304 | |
| 305 | int snd_sof_prepare(struct device *dev) |
| 306 | { |
| 307 | struct snd_sof_dev *sdev = dev_get_drvdata(dev); |
Daniel Baluta | 43437d0 | 2020-09-21 13:50:38 +0300 | [diff] [blame] | 308 | const struct sof_dev_desc *desc = sdev->pdata->desc; |
| 309 | |
| 310 | /* will suspend to S3 by default */ |
| 311 | sdev->system_suspend_target = SOF_SUSPEND_S3; |
| 312 | |
| 313 | if (!desc->use_acpi_target_states) |
| 314 | return 0; |
Keyon Jie | 0b50b3b | 2019-10-25 17:41:17 -0500 | [diff] [blame] | 315 | |
| 316 | #if defined(CONFIG_ACPI) |
Ranjani Sridharan | 043ae13 | 2020-01-29 16:07:20 -0600 | [diff] [blame] | 317 | if (acpi_target_system_state() == ACPI_STATE_S0) |
| 318 | sdev->system_suspend_target = SOF_SUSPEND_S0IX; |
Keyon Jie | 0b50b3b | 2019-10-25 17:41:17 -0500 | [diff] [blame] | 319 | #endif |
| 320 | |
| 321 | return 0; |
| 322 | } |
| 323 | EXPORT_SYMBOL(snd_sof_prepare); |
| 324 | |
| 325 | void snd_sof_complete(struct device *dev) |
| 326 | { |
| 327 | struct snd_sof_dev *sdev = dev_get_drvdata(dev); |
| 328 | |
Ranjani Sridharan | 043ae13 | 2020-01-29 16:07:20 -0600 | [diff] [blame] | 329 | sdev->system_suspend_target = SOF_SUSPEND_NONE; |
Keyon Jie | 0b50b3b | 2019-10-25 17:41:17 -0500 | [diff] [blame] | 330 | } |
| 331 | EXPORT_SYMBOL(snd_sof_complete); |