xref: /linux/sound/soc/sof/pm.c (revision 0432fe32c129780f89fd5426059cb1ddd8e50858)
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
7 //
8 // Author: Liam Girdwood <liam.r.girdwood@linux.intel.com>
9 //
10 
11 #include <linux/module.h>
12 #include "ops.h"
13 #include "sof-priv.h"
14 #include "sof-audio.h"
15 
16 static int override_on_demand_boot = -1;
17 module_param_named(on_demand_boot, override_on_demand_boot, int, 0444);
18 MODULE_PARM_DESC(on_demand_boot, "Force on-demand DSP boot: 0 - disabled, 1 - enabled");
19 
20 /*
21  * Helper function to determine the target DSP state during
22  * system suspend. This function only cares about the device
23  * D-states. Platform-specific substates, if any, should be
24  * handled by the platform-specific parts.
25  */
26 static u32 snd_sof_dsp_power_target(struct snd_sof_dev *sdev)
27 {
28 	u32 target_dsp_state;
29 
30 	switch (sdev->system_suspend_target) {
31 	case SOF_SUSPEND_S5:
32 	case SOF_SUSPEND_S4:
33 		/* DSP should be in D3 if the system is suspending to S3+ */
34 	case SOF_SUSPEND_S3:
35 		/* DSP should be in D3 if the system is suspending to S3 */
36 		target_dsp_state = SOF_DSP_PM_D3;
37 		break;
38 	case SOF_SUSPEND_S0IX:
39 		/*
40 		 * Currently, the only criterion for retaining the DSP in D0
41 		 * is that there are streams that ignored the suspend trigger.
42 		 * Additional criteria such Soundwire clock-stop mode and
43 		 * device suspend latency considerations will be added later.
44 		 */
45 		if (snd_sof_stream_suspend_ignored(sdev))
46 			target_dsp_state = SOF_DSP_PM_D0;
47 		else
48 			target_dsp_state = SOF_DSP_PM_D3;
49 		break;
50 	default:
51 		/* This case would be during runtime suspend */
52 		target_dsp_state = SOF_DSP_PM_D3;
53 		break;
54 	}
55 
56 	return target_dsp_state;
57 }
58 
59 #if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_ENABLE_DEBUGFS_CACHE)
60 static void sof_cache_debugfs(struct snd_sof_dev *sdev)
61 {
62 	struct snd_sof_dfsentry *dfse;
63 
64 	list_for_each_entry(dfse, &sdev->dfsentry_list, list) {
65 
66 		/* nothing to do if debugfs buffer is not IO mem */
67 		if (dfse->type == SOF_DFSENTRY_TYPE_BUF)
68 			continue;
69 
70 		/* cache memory that is only accessible in D0 */
71 		if (dfse->access_type == SOF_DEBUGFS_ACCESS_D0_ONLY)
72 			memcpy_fromio(dfse->cache_buf, dfse->io_mem,
73 				      dfse->size);
74 	}
75 }
76 #endif
77 
78 int snd_sof_boot_dsp_firmware(struct snd_sof_dev *sdev)
79 {
80 	const struct sof_ipc_pm_ops *pm_ops = sof_ipc_get_ops(sdev, pm);
81 	const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
82 	int ret;
83 
84 	guard(mutex)(&sdev->dsp_fw_boot_mutex);
85 
86 	if (sdev->fw_state == SOF_FW_BOOT_COMPLETE) {
87 		/* Firmware already booted, just return */
88 		return 0;
89 	}
90 
91 	dev_dbg(sdev->dev, "Booting DSP firmware\n");
92 
93 	sof_set_fw_state(sdev, SOF_FW_BOOT_PREPARE);
94 
95 	/* load the firmware */
96 	ret = snd_sof_load_firmware(sdev);
97 	if (ret < 0) {
98 		dev_err(sdev->dev, "%s: failed to load DSP firmware: %d\n",
99 			__func__, ret);
100 		sof_set_fw_state(sdev, SOF_FW_BOOT_FAILED);
101 		return ret;
102 	}
103 
104 	sof_set_fw_state(sdev, SOF_FW_BOOT_IN_PROGRESS);
105 
106 	/*
107 	 * Boot the firmware. The FW boot status will be modified
108 	 * in snd_sof_run_firmware() depending on the outcome.
109 	 */
110 	ret = snd_sof_run_firmware(sdev);
111 	if (ret < 0) {
112 		dev_err(sdev->dev, "%s: failed to boot DSP firmware: %d\n",
113 			__func__, ret);
114 		sof_set_fw_state(sdev, SOF_FW_BOOT_FAILED);
115 		return ret;
116 	}
117 
118 	/* resume DMA trace */
119 	ret = sof_fw_trace_resume(sdev);
120 	if (ret < 0) {
121 		/* non fatal */
122 		dev_warn(sdev->dev, "%s: failed to resume trace: %d\n",
123 			 __func__, ret);
124 	}
125 
126 	/* restore pipelines */
127 	if (tplg_ops && tplg_ops->set_up_all_pipelines) {
128 		ret = tplg_ops->set_up_all_pipelines(sdev, false);
129 		if (ret < 0) {
130 			dev_err(sdev->dev, "%s: failed to restore pipeline: %d\n",
131 				__func__, ret);
132 			goto setup_fail;
133 		}
134 	}
135 
136 	/* Notify clients not managed by pm framework about core resume */
137 	sof_resume_clients(sdev);
138 
139 	/* notify DSP of system resume */
140 	if (pm_ops && pm_ops->ctx_restore) {
141 		ret = pm_ops->ctx_restore(sdev);
142 		if (ret < 0)
143 			dev_err(sdev->dev, "%s: ctx_restore IPC failed: %d\n",
144 				__func__, ret);
145 	}
146 
147 setup_fail:
148 #if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_ENABLE_DEBUGFS_CACHE)
149 	if (ret < 0) {
150 		/*
151 		 * Debugfs cannot be read in runtime suspend, so cache
152 		 * the contents upon failure. This allows to capture
153 		 * possible DSP coredump information.
154 		 */
155 		sof_cache_debugfs(sdev);
156 	}
157 #endif
158 
159 	return ret;
160 }
161 EXPORT_SYMBOL(snd_sof_boot_dsp_firmware);
162 
163 static int sof_resume(struct device *dev, bool runtime_resume)
164 {
165 	struct snd_sof_dev *sdev = dev_get_drvdata(dev);
166 	u32 old_state = sdev->dsp_power_state.state;
167 	bool on_demand_boot;
168 	int ret;
169 
170 	/* do nothing if dsp resume callbacks are not set */
171 	if (!runtime_resume && !sof_ops(sdev)->resume)
172 		return 0;
173 
174 	if (runtime_resume && !sof_ops(sdev)->runtime_resume)
175 		return 0;
176 
177 	/* DSP was never successfully started, nothing to resume */
178 	if (sdev->first_boot)
179 		return 0;
180 
181 	/*
182 	 * if the runtime_resume flag is set, call the runtime_resume routine
183 	 * or else call the system resume routine
184 	 */
185 	if (runtime_resume)
186 		ret = snd_sof_dsp_runtime_resume(sdev);
187 	else
188 		ret = snd_sof_dsp_resume(sdev);
189 	if (ret < 0) {
190 		dev_err(sdev->dev,
191 			"error: failed to power up DSP after resume\n");
192 		return ret;
193 	}
194 
195 	if (sdev->dspless_mode_selected) {
196 		sof_set_fw_state(sdev, SOF_DSPLESS_MODE);
197 		return 0;
198 	}
199 
200 	/*
201 	 * Nothing further to be done for platforms that support the low power
202 	 * D0 substate. Resume trace and return when resuming from
203 	 * low-power D0 substate
204 	 */
205 	if (!runtime_resume && sof_ops(sdev)->set_power_state &&
206 	    old_state == SOF_DSP_PM_D0) {
207 		ret = sof_fw_trace_resume(sdev);
208 		if (ret < 0)
209 			/* non fatal */
210 			dev_warn(sdev->dev,
211 				 "failed to enable trace after resume %d\n", ret);
212 		return 0;
213 	}
214 
215 	if (override_on_demand_boot > -1)
216 		on_demand_boot = override_on_demand_boot ? true : false;
217 	else
218 		on_demand_boot = sdev->pdata->desc->on_demand_dsp_boot;
219 
220 	if (on_demand_boot) {
221 		/* Only change the fw_state to PREPARE but skip booting */
222 		sof_set_fw_state(sdev, SOF_FW_BOOT_PREPARE);
223 		return 0;
224 	}
225 
226 	return snd_sof_boot_dsp_firmware(sdev);
227 }
228 
229 static int sof_suspend(struct device *dev, bool runtime_suspend)
230 {
231 	struct snd_sof_dev *sdev = dev_get_drvdata(dev);
232 	const struct sof_ipc_pm_ops *pm_ops = sof_ipc_get_ops(sdev, pm);
233 	const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
234 	pm_message_t pm_state;
235 	u32 target_state = snd_sof_dsp_power_target(sdev);
236 	u32 old_state = sdev->dsp_power_state.state;
237 	int ret;
238 
239 	/* do nothing if dsp suspend callback is not set */
240 	if (!runtime_suspend && !sof_ops(sdev)->suspend)
241 		return 0;
242 
243 	if (runtime_suspend && !sof_ops(sdev)->runtime_suspend)
244 		return 0;
245 
246 	/* we need to tear down pipelines only if the DSP hardware is
247 	 * active, which happens for PCI devices. if the device is
248 	 * suspended, it is brought back to full power and then
249 	 * suspended again
250 	 */
251 	if (tplg_ops && tplg_ops->tear_down_all_pipelines && (old_state == SOF_DSP_PM_D0))
252 		tplg_ops->tear_down_all_pipelines(sdev, false);
253 
254 	if (sdev->fw_state != SOF_FW_BOOT_COMPLETE)
255 		goto suspend;
256 
257 	/* prepare for streams to be resumed properly upon resume */
258 	if (!runtime_suspend) {
259 		ret = snd_sof_dsp_hw_params_upon_resume(sdev);
260 		if (ret < 0) {
261 			dev_err(sdev->dev,
262 				"error: setting hw_params flag during suspend %d\n",
263 				ret);
264 			return ret;
265 		}
266 	}
267 
268 	pm_state.event = target_state;
269 
270 	/* suspend DMA trace */
271 	sof_fw_trace_suspend(sdev, pm_state);
272 
273 	/* Notify clients not managed by pm framework about core suspend */
274 	sof_suspend_clients(sdev, pm_state);
275 
276 	/* Skip to platform-specific suspend if DSP is entering D0 */
277 	if (target_state == SOF_DSP_PM_D0)
278 		goto suspend;
279 
280 #if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_ENABLE_DEBUGFS_CACHE)
281 	/* cache debugfs contents during runtime suspend */
282 	if (runtime_suspend)
283 		sof_cache_debugfs(sdev);
284 #endif
285 	/* notify DSP of upcoming power down */
286 	if (pm_ops && pm_ops->ctx_save) {
287 		ret = pm_ops->ctx_save(sdev);
288 		if (ret == -EBUSY || ret == -EAGAIN) {
289 			/*
290 			 * runtime PM has logic to handle -EBUSY/-EAGAIN so
291 			 * pass these errors up
292 			 */
293 			dev_err(sdev->dev, "ctx_save IPC error during suspend: %d\n", ret);
294 			return ret;
295 		} else if (ret < 0) {
296 			/* FW in unexpected state, continue to power down */
297 			dev_warn(sdev->dev, "ctx_save IPC error: %d, proceeding with suspend\n",
298 				 ret);
299 		}
300 	}
301 
302 suspend:
303 
304 	/* return if the DSP was not probed successfully */
305 	if (sdev->fw_state == SOF_FW_BOOT_NOT_STARTED)
306 		return 0;
307 
308 	/* platform-specific suspend */
309 	if (runtime_suspend)
310 		ret = snd_sof_dsp_runtime_suspend(sdev);
311 	else
312 		ret = snd_sof_dsp_suspend(sdev, target_state);
313 	if (ret < 0)
314 		dev_err(sdev->dev,
315 			"error: failed to power down DSP during suspend %d\n",
316 			ret);
317 
318 	/* Do not reset FW state if DSP is in D0 */
319 	if (target_state == SOF_DSP_PM_D0)
320 		return ret;
321 
322 	/* reset FW state */
323 	sof_set_fw_state(sdev, SOF_FW_BOOT_NOT_STARTED);
324 	sdev->enabled_cores_mask = 0;
325 
326 	return ret;
327 }
328 
329 int snd_sof_dsp_power_down_notify(struct snd_sof_dev *sdev)
330 {
331 	const struct sof_ipc_pm_ops *pm_ops = sof_ipc_get_ops(sdev, pm);
332 
333 	/*
334 	 * Notify DSP of upcoming power down only if the firmware has been
335 	 * booted up
336 	 */
337 	if (sdev->fw_state == SOF_FW_BOOT_COMPLETE && sof_ops(sdev)->remove &&
338 	    pm_ops && pm_ops->ctx_save)
339 		return pm_ops->ctx_save(sdev);
340 
341 	return 0;
342 }
343 
344 int snd_sof_runtime_suspend(struct device *dev)
345 {
346 	return sof_suspend(dev, true);
347 }
348 EXPORT_SYMBOL(snd_sof_runtime_suspend);
349 
350 int snd_sof_runtime_idle(struct device *dev)
351 {
352 	struct snd_sof_dev *sdev = dev_get_drvdata(dev);
353 
354 	return snd_sof_dsp_runtime_idle(sdev);
355 }
356 EXPORT_SYMBOL(snd_sof_runtime_idle);
357 
358 int snd_sof_runtime_resume(struct device *dev)
359 {
360 	return sof_resume(dev, true);
361 }
362 EXPORT_SYMBOL(snd_sof_runtime_resume);
363 
364 int snd_sof_resume(struct device *dev)
365 {
366 	return sof_resume(dev, false);
367 }
368 EXPORT_SYMBOL(snd_sof_resume);
369 
370 int snd_sof_suspend(struct device *dev)
371 {
372 	return sof_suspend(dev, false);
373 }
374 EXPORT_SYMBOL(snd_sof_suspend);
375 
376 int snd_sof_prepare(struct device *dev)
377 {
378 	struct snd_sof_dev *sdev = dev_get_drvdata(dev);
379 	const struct sof_dev_desc *desc = sdev->pdata->desc;
380 
381 	/* will suspend to S3 by default */
382 	sdev->system_suspend_target = SOF_SUSPEND_S3;
383 
384 	/*
385 	 * if the firmware is crashed or boot failed then we try to aim for S3
386 	 * to reboot the firmware
387 	 */
388 	if (sdev->fw_state == SOF_FW_CRASHED ||
389 	    sdev->fw_state == SOF_FW_BOOT_FAILED)
390 		return 0;
391 
392 	if (!desc->use_acpi_target_states)
393 		return 0;
394 
395 #if defined(CONFIG_ACPI)
396 	switch (acpi_target_system_state()) {
397 	case ACPI_STATE_S0:
398 		sdev->system_suspend_target = SOF_SUSPEND_S0IX;
399 		break;
400 	case ACPI_STATE_S1:
401 	case ACPI_STATE_S2:
402 	case ACPI_STATE_S3:
403 		sdev->system_suspend_target = SOF_SUSPEND_S3;
404 		break;
405 	case ACPI_STATE_S4:
406 		sdev->system_suspend_target = SOF_SUSPEND_S4;
407 		break;
408 	case ACPI_STATE_S5:
409 		sdev->system_suspend_target = SOF_SUSPEND_S5;
410 		break;
411 	default:
412 		break;
413 	}
414 #endif
415 
416 	return 0;
417 }
418 EXPORT_SYMBOL(snd_sof_prepare);
419 
420 void snd_sof_complete(struct device *dev)
421 {
422 	struct snd_sof_dev *sdev = dev_get_drvdata(dev);
423 
424 	sdev->system_suspend_target = SOF_SUSPEND_NONE;
425 }
426 EXPORT_SYMBOL(snd_sof_complete);
427