1 // SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) 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" 13 #include "sof-audio.h" 14 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_S5: 27 case SOF_SUSPEND_S4: 28 /* DSP should be in D3 if the system is suspending to S3+ */ 29 case SOF_SUSPEND_S3: 30 /* DSP should be in D3 if the system is suspending to S3 */ 31 target_dsp_state = SOF_DSP_PM_D3; 32 break; 33 case SOF_SUSPEND_S0IX: 34 /* 35 * Currently, the only criterion for retaining the DSP in D0 36 * is that there are streams that ignored the suspend trigger. 37 * Additional criteria such Soundwire clock-stop mode and 38 * device suspend latency considerations will be added later. 39 */ 40 if (snd_sof_stream_suspend_ignored(sdev)) 41 target_dsp_state = SOF_DSP_PM_D0; 42 else 43 target_dsp_state = SOF_DSP_PM_D3; 44 break; 45 default: 46 /* This case would be during runtime suspend */ 47 target_dsp_state = SOF_DSP_PM_D3; 48 break; 49 } 50 51 return target_dsp_state; 52 } 53 54 #if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_ENABLE_DEBUGFS_CACHE) 55 static void sof_cache_debugfs(struct snd_sof_dev *sdev) 56 { 57 struct snd_sof_dfsentry *dfse; 58 59 list_for_each_entry(dfse, &sdev->dfsentry_list, list) { 60 61 /* nothing to do if debugfs buffer is not IO mem */ 62 if (dfse->type == SOF_DFSENTRY_TYPE_BUF) 63 continue; 64 65 /* cache memory that is only accessible in D0 */ 66 if (dfse->access_type == SOF_DEBUGFS_ACCESS_D0_ONLY) 67 memcpy_fromio(dfse->cache_buf, dfse->io_mem, 68 dfse->size); 69 } 70 } 71 #endif 72 73 static int sof_resume(struct device *dev, bool runtime_resume) 74 { 75 struct snd_sof_dev *sdev = dev_get_drvdata(dev); 76 const struct sof_ipc_pm_ops *pm_ops = sof_ipc_get_ops(sdev, pm); 77 const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); 78 u32 old_state = sdev->dsp_power_state.state; 79 int ret; 80 81 /* do nothing if dsp resume callbacks are not set */ 82 if (!runtime_resume && !sof_ops(sdev)->resume) 83 return 0; 84 85 if (runtime_resume && !sof_ops(sdev)->runtime_resume) 86 return 0; 87 88 /* DSP was never successfully started, nothing to resume */ 89 if (sdev->first_boot) 90 return 0; 91 92 /* 93 * if the runtime_resume flag is set, call the runtime_resume routine 94 * or else call the system resume routine 95 */ 96 if (runtime_resume) 97 ret = snd_sof_dsp_runtime_resume(sdev); 98 else 99 ret = snd_sof_dsp_resume(sdev); 100 if (ret < 0) { 101 dev_err(sdev->dev, 102 "error: failed to power up DSP after resume\n"); 103 return ret; 104 } 105 106 /* 107 * Nothing further to be done for platforms that support the low power 108 * D0 substate. Resume trace and return when resuming from 109 * low-power D0 substate 110 */ 111 if (!runtime_resume && sof_ops(sdev)->set_power_state && 112 old_state == SOF_DSP_PM_D0) { 113 ret = sof_fw_trace_resume(sdev); 114 if (ret < 0) 115 /* non fatal */ 116 dev_warn(sdev->dev, 117 "failed to enable trace after resume %d\n", ret); 118 return 0; 119 } 120 121 sof_set_fw_state(sdev, SOF_FW_BOOT_PREPARE); 122 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 sof_set_fw_state(sdev, SOF_FW_BOOT_FAILED); 130 return ret; 131 } 132 133 sof_set_fw_state(sdev, SOF_FW_BOOT_IN_PROGRESS); 134 135 /* 136 * Boot the firmware. The FW boot status will be modified 137 * in snd_sof_run_firmware() depending on the outcome. 138 */ 139 ret = snd_sof_run_firmware(sdev); 140 if (ret < 0) { 141 dev_err(sdev->dev, 142 "error: failed to boot DSP firmware after resume %d\n", 143 ret); 144 sof_set_fw_state(sdev, SOF_FW_BOOT_FAILED); 145 return ret; 146 } 147 148 /* resume DMA trace */ 149 ret = sof_fw_trace_resume(sdev); 150 if (ret < 0) { 151 /* non fatal */ 152 dev_warn(sdev->dev, 153 "warning: failed to init trace after resume %d\n", 154 ret); 155 } 156 157 /* restore pipelines */ 158 if (tplg_ops && tplg_ops->set_up_all_pipelines) { 159 ret = tplg_ops->set_up_all_pipelines(sdev, false); 160 if (ret < 0) { 161 dev_err(sdev->dev, "Failed to restore pipeline after resume %d\n", ret); 162 return ret; 163 } 164 } 165 166 /* Notify clients not managed by pm framework about core resume */ 167 sof_resume_clients(sdev); 168 169 /* notify DSP of system resume */ 170 if (pm_ops && pm_ops->ctx_restore) { 171 ret = pm_ops->ctx_restore(sdev); 172 if (ret < 0) 173 dev_err(sdev->dev, "ctx_restore IPC error during resume: %d\n", ret); 174 } 175 176 return ret; 177 } 178 179 static int sof_suspend(struct device *dev, bool runtime_suspend) 180 { 181 struct snd_sof_dev *sdev = dev_get_drvdata(dev); 182 const struct sof_ipc_pm_ops *pm_ops = sof_ipc_get_ops(sdev, pm); 183 const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); 184 pm_message_t pm_state; 185 u32 target_state = snd_sof_dsp_power_target(sdev); 186 int ret; 187 188 /* do nothing if dsp suspend callback is not set */ 189 if (!runtime_suspend && !sof_ops(sdev)->suspend) 190 return 0; 191 192 if (runtime_suspend && !sof_ops(sdev)->runtime_suspend) 193 return 0; 194 195 if (tplg_ops && tplg_ops->tear_down_all_pipelines) 196 tplg_ops->tear_down_all_pipelines(sdev, false); 197 198 if (sdev->fw_state != SOF_FW_BOOT_COMPLETE) 199 goto suspend; 200 201 /* prepare for streams to be resumed properly upon resume */ 202 if (!runtime_suspend) { 203 ret = snd_sof_dsp_hw_params_upon_resume(sdev); 204 if (ret < 0) { 205 dev_err(sdev->dev, 206 "error: setting hw_params flag during suspend %d\n", 207 ret); 208 return ret; 209 } 210 } 211 212 pm_state.event = target_state; 213 214 /* Skip to platform-specific suspend if DSP is entering D0 */ 215 if (target_state == SOF_DSP_PM_D0) { 216 sof_fw_trace_suspend(sdev, pm_state); 217 /* Notify clients not managed by pm framework about core suspend */ 218 sof_suspend_clients(sdev, pm_state); 219 goto suspend; 220 } 221 222 /* suspend DMA trace */ 223 sof_fw_trace_suspend(sdev, pm_state); 224 225 /* Notify clients not managed by pm framework about core suspend */ 226 sof_suspend_clients(sdev, pm_state); 227 228 #if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_ENABLE_DEBUGFS_CACHE) 229 /* cache debugfs contents during runtime suspend */ 230 if (runtime_suspend) 231 sof_cache_debugfs(sdev); 232 #endif 233 /* notify DSP of upcoming power down */ 234 if (pm_ops && pm_ops->ctx_save) { 235 ret = pm_ops->ctx_save(sdev); 236 if (ret == -EBUSY || ret == -EAGAIN) { 237 /* 238 * runtime PM has logic to handle -EBUSY/-EAGAIN so 239 * pass these errors up 240 */ 241 dev_err(sdev->dev, "ctx_save IPC error during suspend: %d\n", ret); 242 return ret; 243 } else if (ret < 0) { 244 /* FW in unexpected state, continue to power down */ 245 dev_warn(sdev->dev, "ctx_save IPC error: %d, proceeding with suspend\n", 246 ret); 247 } 248 } 249 250 suspend: 251 252 /* return if the DSP was not probed successfully */ 253 if (sdev->fw_state == SOF_FW_BOOT_NOT_STARTED) 254 return 0; 255 256 /* platform-specific suspend */ 257 if (runtime_suspend) 258 ret = snd_sof_dsp_runtime_suspend(sdev); 259 else 260 ret = snd_sof_dsp_suspend(sdev, target_state); 261 if (ret < 0) 262 dev_err(sdev->dev, 263 "error: failed to power down DSP during suspend %d\n", 264 ret); 265 266 /* Do not reset FW state if DSP is in D0 */ 267 if (target_state == SOF_DSP_PM_D0) 268 return ret; 269 270 /* reset FW state */ 271 sof_set_fw_state(sdev, SOF_FW_BOOT_NOT_STARTED); 272 sdev->enabled_cores_mask = 0; 273 274 return ret; 275 } 276 277 int snd_sof_dsp_power_down_notify(struct snd_sof_dev *sdev) 278 { 279 const struct sof_ipc_pm_ops *pm_ops = sof_ipc_get_ops(sdev, pm); 280 281 /* Notify DSP of upcoming power down */ 282 if (sof_ops(sdev)->remove && pm_ops && pm_ops->ctx_save) 283 return pm_ops->ctx_save(sdev); 284 285 return 0; 286 } 287 288 int snd_sof_runtime_suspend(struct device *dev) 289 { 290 return sof_suspend(dev, true); 291 } 292 EXPORT_SYMBOL(snd_sof_runtime_suspend); 293 294 int snd_sof_runtime_idle(struct device *dev) 295 { 296 struct snd_sof_dev *sdev = dev_get_drvdata(dev); 297 298 return snd_sof_dsp_runtime_idle(sdev); 299 } 300 EXPORT_SYMBOL(snd_sof_runtime_idle); 301 302 int snd_sof_runtime_resume(struct device *dev) 303 { 304 return sof_resume(dev, true); 305 } 306 EXPORT_SYMBOL(snd_sof_runtime_resume); 307 308 int snd_sof_resume(struct device *dev) 309 { 310 return sof_resume(dev, false); 311 } 312 EXPORT_SYMBOL(snd_sof_resume); 313 314 int snd_sof_suspend(struct device *dev) 315 { 316 return sof_suspend(dev, false); 317 } 318 EXPORT_SYMBOL(snd_sof_suspend); 319 320 int snd_sof_prepare(struct device *dev) 321 { 322 struct snd_sof_dev *sdev = dev_get_drvdata(dev); 323 const struct sof_dev_desc *desc = sdev->pdata->desc; 324 325 /* will suspend to S3 by default */ 326 sdev->system_suspend_target = SOF_SUSPEND_S3; 327 328 /* 329 * if the firmware is crashed or boot failed then we try to aim for S3 330 * to reboot the firmware 331 */ 332 if (sdev->fw_state == SOF_FW_CRASHED || 333 sdev->fw_state == SOF_FW_BOOT_FAILED) 334 return 0; 335 336 if (!desc->use_acpi_target_states) 337 return 0; 338 339 #if defined(CONFIG_ACPI) 340 switch (acpi_target_system_state()) { 341 case ACPI_STATE_S0: 342 sdev->system_suspend_target = SOF_SUSPEND_S0IX; 343 break; 344 case ACPI_STATE_S1: 345 case ACPI_STATE_S2: 346 case ACPI_STATE_S3: 347 sdev->system_suspend_target = SOF_SUSPEND_S3; 348 break; 349 case ACPI_STATE_S4: 350 sdev->system_suspend_target = SOF_SUSPEND_S4; 351 break; 352 case ACPI_STATE_S5: 353 sdev->system_suspend_target = SOF_SUSPEND_S5; 354 break; 355 default: 356 break; 357 } 358 #endif 359 360 return 0; 361 } 362 EXPORT_SYMBOL(snd_sof_prepare); 363 364 void snd_sof_complete(struct device *dev) 365 { 366 struct snd_sof_dev *sdev = dev_get_drvdata(dev); 367 368 sdev->system_suspend_target = SOF_SUSPEND_NONE; 369 } 370 EXPORT_SYMBOL(snd_sof_complete); 371