pm.c 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388
  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 <[email protected]>
  9. //
  10. #include "ops.h"
  11. #include "sof-priv.h"
  12. #include "sof-audio.h"
  13. /*
  14. * Helper function to determine the target DSP state during
  15. * system suspend. This function only cares about the device
  16. * D-states. Platform-specific substates, if any, should be
  17. * handled by the platform-specific parts.
  18. */
  19. static u32 snd_sof_dsp_power_target(struct snd_sof_dev *sdev)
  20. {
  21. u32 target_dsp_state;
  22. switch (sdev->system_suspend_target) {
  23. case SOF_SUSPEND_S5:
  24. case SOF_SUSPEND_S4:
  25. /* DSP should be in D3 if the system is suspending to S3+ */
  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. return target_dsp_state;
  48. }
  49. #if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_ENABLE_DEBUGFS_CACHE)
  50. static void sof_cache_debugfs(struct snd_sof_dev *sdev)
  51. {
  52. struct snd_sof_dfsentry *dfse;
  53. list_for_each_entry(dfse, &sdev->dfsentry_list, list) {
  54. /* nothing to do if debugfs buffer is not IO mem */
  55. if (dfse->type == SOF_DFSENTRY_TYPE_BUF)
  56. continue;
  57. /* cache memory that is only accessible in D0 */
  58. if (dfse->access_type == SOF_DEBUGFS_ACCESS_D0_ONLY)
  59. memcpy_fromio(dfse->cache_buf, dfse->io_mem,
  60. dfse->size);
  61. }
  62. }
  63. #endif
  64. static int sof_resume(struct device *dev, bool runtime_resume)
  65. {
  66. struct snd_sof_dev *sdev = dev_get_drvdata(dev);
  67. const struct sof_ipc_pm_ops *pm_ops = sdev->ipc->ops->pm;
  68. const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg;
  69. u32 old_state = sdev->dsp_power_state.state;
  70. int ret;
  71. /* do nothing if dsp resume callbacks are not set */
  72. if (!runtime_resume && !sof_ops(sdev)->resume)
  73. return 0;
  74. if (runtime_resume && !sof_ops(sdev)->runtime_resume)
  75. return 0;
  76. /* DSP was never successfully started, nothing to resume */
  77. if (sdev->first_boot)
  78. return 0;
  79. /*
  80. * if the runtime_resume flag is set, call the runtime_resume routine
  81. * or else call the system resume routine
  82. */
  83. if (runtime_resume)
  84. ret = snd_sof_dsp_runtime_resume(sdev);
  85. else
  86. ret = snd_sof_dsp_resume(sdev);
  87. if (ret < 0) {
  88. dev_err(sdev->dev,
  89. "error: failed to power up DSP after resume\n");
  90. return ret;
  91. }
  92. /*
  93. * Nothing further to be done for platforms that support the low power
  94. * D0 substate. Resume trace and return when resuming from
  95. * low-power D0 substate
  96. */
  97. if (!runtime_resume && sof_ops(sdev)->set_power_state &&
  98. old_state == SOF_DSP_PM_D0) {
  99. ret = sof_fw_trace_resume(sdev);
  100. if (ret < 0)
  101. /* non fatal */
  102. dev_warn(sdev->dev,
  103. "failed to enable trace after resume %d\n", ret);
  104. return 0;
  105. }
  106. sof_set_fw_state(sdev, SOF_FW_BOOT_PREPARE);
  107. /* load the firmware */
  108. ret = snd_sof_load_firmware(sdev);
  109. if (ret < 0) {
  110. dev_err(sdev->dev,
  111. "error: failed to load DSP firmware after resume %d\n",
  112. ret);
  113. sof_set_fw_state(sdev, SOF_FW_BOOT_FAILED);
  114. return ret;
  115. }
  116. sof_set_fw_state(sdev, SOF_FW_BOOT_IN_PROGRESS);
  117. /*
  118. * Boot the firmware. The FW boot status will be modified
  119. * in snd_sof_run_firmware() depending on the outcome.
  120. */
  121. ret = snd_sof_run_firmware(sdev);
  122. if (ret < 0) {
  123. dev_err(sdev->dev,
  124. "error: failed to boot DSP firmware after resume %d\n",
  125. ret);
  126. sof_set_fw_state(sdev, SOF_FW_BOOT_FAILED);
  127. return ret;
  128. }
  129. /* resume DMA trace */
  130. ret = sof_fw_trace_resume(sdev);
  131. if (ret < 0) {
  132. /* non fatal */
  133. dev_warn(sdev->dev,
  134. "warning: failed to init trace after resume %d\n",
  135. ret);
  136. }
  137. /* restore pipelines */
  138. if (tplg_ops->set_up_all_pipelines) {
  139. ret = tplg_ops->set_up_all_pipelines(sdev, false);
  140. if (ret < 0) {
  141. dev_err(sdev->dev, "Failed to restore pipeline after resume %d\n", ret);
  142. goto setup_fail;
  143. }
  144. }
  145. /* Notify clients not managed by pm framework about core resume */
  146. sof_resume_clients(sdev);
  147. /* notify DSP of system resume */
  148. if (pm_ops && pm_ops->ctx_restore) {
  149. ret = pm_ops->ctx_restore(sdev);
  150. if (ret < 0)
  151. dev_err(sdev->dev, "ctx_restore IPC error during resume: %d\n", ret);
  152. }
  153. setup_fail:
  154. #if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_ENABLE_DEBUGFS_CACHE)
  155. if (ret < 0) {
  156. /*
  157. * Debugfs cannot be read in runtime suspend, so cache
  158. * the contents upon failure. This allows to capture
  159. * possible DSP coredump information.
  160. */
  161. sof_cache_debugfs(sdev);
  162. }
  163. #endif
  164. return ret;
  165. }
  166. static int sof_suspend(struct device *dev, bool runtime_suspend)
  167. {
  168. struct snd_sof_dev *sdev = dev_get_drvdata(dev);
  169. const struct sof_ipc_pm_ops *pm_ops = sdev->ipc->ops->pm;
  170. const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg;
  171. pm_message_t pm_state;
  172. u32 target_state = snd_sof_dsp_power_target(sdev);
  173. u32 old_state = sdev->dsp_power_state.state;
  174. int ret;
  175. /* do nothing if dsp suspend callback is not set */
  176. if (!runtime_suspend && !sof_ops(sdev)->suspend)
  177. return 0;
  178. if (runtime_suspend && !sof_ops(sdev)->runtime_suspend)
  179. return 0;
  180. /* we need to tear down pipelines only if the DSP hardware is
  181. * active, which happens for PCI devices. if the device is
  182. * suspended, it is brought back to full power and then
  183. * suspended again
  184. */
  185. if (tplg_ops && tplg_ops->tear_down_all_pipelines && (old_state == SOF_DSP_PM_D0))
  186. tplg_ops->tear_down_all_pipelines(sdev, false);
  187. if (sdev->fw_state != SOF_FW_BOOT_COMPLETE)
  188. goto suspend;
  189. /* prepare for streams to be resumed properly upon resume */
  190. if (!runtime_suspend) {
  191. ret = snd_sof_dsp_hw_params_upon_resume(sdev);
  192. if (ret < 0) {
  193. dev_err(sdev->dev,
  194. "error: setting hw_params flag during suspend %d\n",
  195. ret);
  196. return ret;
  197. }
  198. }
  199. pm_state.event = target_state;
  200. /* Skip to platform-specific suspend if DSP is entering D0 */
  201. if (target_state == SOF_DSP_PM_D0) {
  202. sof_fw_trace_suspend(sdev, pm_state);
  203. /* Notify clients not managed by pm framework about core suspend */
  204. sof_suspend_clients(sdev, pm_state);
  205. goto suspend;
  206. }
  207. /* suspend DMA trace */
  208. sof_fw_trace_suspend(sdev, pm_state);
  209. /* Notify clients not managed by pm framework about core suspend */
  210. sof_suspend_clients(sdev, pm_state);
  211. #if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_ENABLE_DEBUGFS_CACHE)
  212. /* cache debugfs contents during runtime suspend */
  213. if (runtime_suspend)
  214. sof_cache_debugfs(sdev);
  215. #endif
  216. /* notify DSP of upcoming power down */
  217. if (pm_ops && pm_ops->ctx_save) {
  218. ret = pm_ops->ctx_save(sdev);
  219. if (ret == -EBUSY || ret == -EAGAIN) {
  220. /*
  221. * runtime PM has logic to handle -EBUSY/-EAGAIN so
  222. * pass these errors up
  223. */
  224. dev_err(sdev->dev, "ctx_save IPC error during suspend: %d\n", ret);
  225. return ret;
  226. } else if (ret < 0) {
  227. /* FW in unexpected state, continue to power down */
  228. dev_warn(sdev->dev, "ctx_save IPC error: %d, proceeding with suspend\n",
  229. ret);
  230. }
  231. }
  232. suspend:
  233. /* return if the DSP was not probed successfully */
  234. if (sdev->fw_state == SOF_FW_BOOT_NOT_STARTED)
  235. return 0;
  236. /* platform-specific suspend */
  237. if (runtime_suspend)
  238. ret = snd_sof_dsp_runtime_suspend(sdev);
  239. else
  240. ret = snd_sof_dsp_suspend(sdev, target_state);
  241. if (ret < 0)
  242. dev_err(sdev->dev,
  243. "error: failed to power down DSP during suspend %d\n",
  244. ret);
  245. /* Do not reset FW state if DSP is in D0 */
  246. if (target_state == SOF_DSP_PM_D0)
  247. return ret;
  248. /* reset FW state */
  249. sof_set_fw_state(sdev, SOF_FW_BOOT_NOT_STARTED);
  250. sdev->enabled_cores_mask = 0;
  251. return ret;
  252. }
  253. int snd_sof_dsp_power_down_notify(struct snd_sof_dev *sdev)
  254. {
  255. const struct sof_ipc_pm_ops *pm_ops = sdev->ipc->ops->pm;
  256. /* Notify DSP of upcoming power down */
  257. if (sof_ops(sdev)->remove && pm_ops && pm_ops->ctx_save)
  258. return pm_ops->ctx_save(sdev);
  259. return 0;
  260. }
  261. int snd_sof_runtime_suspend(struct device *dev)
  262. {
  263. return sof_suspend(dev, true);
  264. }
  265. EXPORT_SYMBOL(snd_sof_runtime_suspend);
  266. int snd_sof_runtime_idle(struct device *dev)
  267. {
  268. struct snd_sof_dev *sdev = dev_get_drvdata(dev);
  269. return snd_sof_dsp_runtime_idle(sdev);
  270. }
  271. EXPORT_SYMBOL(snd_sof_runtime_idle);
  272. int snd_sof_runtime_resume(struct device *dev)
  273. {
  274. return sof_resume(dev, true);
  275. }
  276. EXPORT_SYMBOL(snd_sof_runtime_resume);
  277. int snd_sof_resume(struct device *dev)
  278. {
  279. return sof_resume(dev, false);
  280. }
  281. EXPORT_SYMBOL(snd_sof_resume);
  282. int snd_sof_suspend(struct device *dev)
  283. {
  284. return sof_suspend(dev, false);
  285. }
  286. EXPORT_SYMBOL(snd_sof_suspend);
  287. int snd_sof_prepare(struct device *dev)
  288. {
  289. struct snd_sof_dev *sdev = dev_get_drvdata(dev);
  290. const struct sof_dev_desc *desc = sdev->pdata->desc;
  291. /* will suspend to S3 by default */
  292. sdev->system_suspend_target = SOF_SUSPEND_S3;
  293. /*
  294. * if the firmware is crashed or boot failed then we try to aim for S3
  295. * to reboot the firmware
  296. */
  297. if (sdev->fw_state == SOF_FW_CRASHED ||
  298. sdev->fw_state == SOF_FW_BOOT_FAILED)
  299. return 0;
  300. if (!desc->use_acpi_target_states)
  301. return 0;
  302. #if defined(CONFIG_ACPI)
  303. switch (acpi_target_system_state()) {
  304. case ACPI_STATE_S0:
  305. sdev->system_suspend_target = SOF_SUSPEND_S0IX;
  306. break;
  307. case ACPI_STATE_S1:
  308. case ACPI_STATE_S2:
  309. case ACPI_STATE_S3:
  310. sdev->system_suspend_target = SOF_SUSPEND_S3;
  311. break;
  312. case ACPI_STATE_S4:
  313. sdev->system_suspend_target = SOF_SUSPEND_S4;
  314. break;
  315. case ACPI_STATE_S5:
  316. sdev->system_suspend_target = SOF_SUSPEND_S5;
  317. break;
  318. default:
  319. break;
  320. }
  321. #endif
  322. return 0;
  323. }
  324. EXPORT_SYMBOL(snd_sof_prepare);
  325. void snd_sof_complete(struct device *dev)
  326. {
  327. struct snd_sof_dev *sdev = dev_get_drvdata(dev);
  328. sdev->system_suspend_target = SOF_SUSPEND_NONE;
  329. }
  330. EXPORT_SYMBOL(snd_sof_complete);