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 "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   */
snd_sof_dsp_power_target(struct snd_sof_dev * sdev)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)
sof_cache_debugfs(struct snd_sof_dev * sdev)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  
sof_resume(struct device * dev,bool runtime_resume)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  	if (sdev->dspless_mode_selected) {
107  		sof_set_fw_state(sdev, SOF_DSPLESS_MODE);
108  		return 0;
109  	}
110  
111  	/*
112  	 * Nothing further to be done for platforms that support the low power
113  	 * D0 substate. Resume trace and return when resuming from
114  	 * low-power D0 substate
115  	 */
116  	if (!runtime_resume && sof_ops(sdev)->set_power_state &&
117  	    old_state == SOF_DSP_PM_D0) {
118  		ret = sof_fw_trace_resume(sdev);
119  		if (ret < 0)
120  			/* non fatal */
121  			dev_warn(sdev->dev,
122  				 "failed to enable trace after resume %d\n", ret);
123  		return 0;
124  	}
125  
126  	sof_set_fw_state(sdev, SOF_FW_BOOT_PREPARE);
127  
128  	/* load the firmware */
129  	ret = snd_sof_load_firmware(sdev);
130  	if (ret < 0) {
131  		dev_err(sdev->dev,
132  			"error: failed to load DSP firmware after resume %d\n",
133  			ret);
134  		sof_set_fw_state(sdev, SOF_FW_BOOT_FAILED);
135  		return ret;
136  	}
137  
138  	sof_set_fw_state(sdev, SOF_FW_BOOT_IN_PROGRESS);
139  
140  	/*
141  	 * Boot the firmware. The FW boot status will be modified
142  	 * in snd_sof_run_firmware() depending on the outcome.
143  	 */
144  	ret = snd_sof_run_firmware(sdev);
145  	if (ret < 0) {
146  		dev_err(sdev->dev,
147  			"error: failed to boot DSP firmware after resume %d\n",
148  			ret);
149  		sof_set_fw_state(sdev, SOF_FW_BOOT_FAILED);
150  		return ret;
151  	}
152  
153  	/* resume DMA trace */
154  	ret = sof_fw_trace_resume(sdev);
155  	if (ret < 0) {
156  		/* non fatal */
157  		dev_warn(sdev->dev,
158  			 "warning: failed to init trace after resume %d\n",
159  			 ret);
160  	}
161  
162  	/* restore pipelines */
163  	if (tplg_ops && tplg_ops->set_up_all_pipelines) {
164  		ret = tplg_ops->set_up_all_pipelines(sdev, false);
165  		if (ret < 0) {
166  			dev_err(sdev->dev, "Failed to restore pipeline after resume %d\n", ret);
167  			goto setup_fail;
168  		}
169  	}
170  
171  	/* Notify clients not managed by pm framework about core resume */
172  	sof_resume_clients(sdev);
173  
174  	/* notify DSP of system resume */
175  	if (pm_ops && pm_ops->ctx_restore) {
176  		ret = pm_ops->ctx_restore(sdev);
177  		if (ret < 0)
178  			dev_err(sdev->dev, "ctx_restore IPC error during resume: %d\n", ret);
179  	}
180  
181  setup_fail:
182  #if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_ENABLE_DEBUGFS_CACHE)
183  	if (ret < 0) {
184  		/*
185  		 * Debugfs cannot be read in runtime suspend, so cache
186  		 * the contents upon failure. This allows to capture
187  		 * possible DSP coredump information.
188  		 */
189  		sof_cache_debugfs(sdev);
190  	}
191  #endif
192  
193  	return ret;
194  }
195  
sof_suspend(struct device * dev,bool runtime_suspend)196  static int sof_suspend(struct device *dev, bool runtime_suspend)
197  {
198  	struct snd_sof_dev *sdev = dev_get_drvdata(dev);
199  	const struct sof_ipc_pm_ops *pm_ops = sof_ipc_get_ops(sdev, pm);
200  	const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
201  	pm_message_t pm_state;
202  	u32 target_state = snd_sof_dsp_power_target(sdev);
203  	u32 old_state = sdev->dsp_power_state.state;
204  	int ret;
205  
206  	/* do nothing if dsp suspend callback is not set */
207  	if (!runtime_suspend && !sof_ops(sdev)->suspend)
208  		return 0;
209  
210  	if (runtime_suspend && !sof_ops(sdev)->runtime_suspend)
211  		return 0;
212  
213  	/* we need to tear down pipelines only if the DSP hardware is
214  	 * active, which happens for PCI devices. if the device is
215  	 * suspended, it is brought back to full power and then
216  	 * suspended again
217  	 */
218  	if (tplg_ops && tplg_ops->tear_down_all_pipelines && (old_state == SOF_DSP_PM_D0))
219  		tplg_ops->tear_down_all_pipelines(sdev, false);
220  
221  	if (sdev->fw_state != SOF_FW_BOOT_COMPLETE)
222  		goto suspend;
223  
224  	/* prepare for streams to be resumed properly upon resume */
225  	if (!runtime_suspend) {
226  		ret = snd_sof_dsp_hw_params_upon_resume(sdev);
227  		if (ret < 0) {
228  			dev_err(sdev->dev,
229  				"error: setting hw_params flag during suspend %d\n",
230  				ret);
231  			return ret;
232  		}
233  	}
234  
235  	pm_state.event = target_state;
236  
237  	/* suspend DMA trace */
238  	sof_fw_trace_suspend(sdev, pm_state);
239  
240  	/* Notify clients not managed by pm framework about core suspend */
241  	sof_suspend_clients(sdev, pm_state);
242  
243  	/* Skip to platform-specific suspend if DSP is entering D0 */
244  	if (target_state == SOF_DSP_PM_D0)
245  		goto suspend;
246  
247  #if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_ENABLE_DEBUGFS_CACHE)
248  	/* cache debugfs contents during runtime suspend */
249  	if (runtime_suspend)
250  		sof_cache_debugfs(sdev);
251  #endif
252  	/* notify DSP of upcoming power down */
253  	if (pm_ops && pm_ops->ctx_save) {
254  		ret = pm_ops->ctx_save(sdev);
255  		if (ret == -EBUSY || ret == -EAGAIN) {
256  			/*
257  			 * runtime PM has logic to handle -EBUSY/-EAGAIN so
258  			 * pass these errors up
259  			 */
260  			dev_err(sdev->dev, "ctx_save IPC error during suspend: %d\n", ret);
261  			return ret;
262  		} else if (ret < 0) {
263  			/* FW in unexpected state, continue to power down */
264  			dev_warn(sdev->dev, "ctx_save IPC error: %d, proceeding with suspend\n",
265  				 ret);
266  		}
267  	}
268  
269  suspend:
270  
271  	/* return if the DSP was not probed successfully */
272  	if (sdev->fw_state == SOF_FW_BOOT_NOT_STARTED)
273  		return 0;
274  
275  	/* platform-specific suspend */
276  	if (runtime_suspend)
277  		ret = snd_sof_dsp_runtime_suspend(sdev);
278  	else
279  		ret = snd_sof_dsp_suspend(sdev, target_state);
280  	if (ret < 0)
281  		dev_err(sdev->dev,
282  			"error: failed to power down DSP during suspend %d\n",
283  			ret);
284  
285  	/* Do not reset FW state if DSP is in D0 */
286  	if (target_state == SOF_DSP_PM_D0)
287  		return ret;
288  
289  	/* reset FW state */
290  	sof_set_fw_state(sdev, SOF_FW_BOOT_NOT_STARTED);
291  	sdev->enabled_cores_mask = 0;
292  
293  	return ret;
294  }
295  
snd_sof_dsp_power_down_notify(struct snd_sof_dev * sdev)296  int snd_sof_dsp_power_down_notify(struct snd_sof_dev *sdev)
297  {
298  	const struct sof_ipc_pm_ops *pm_ops = sof_ipc_get_ops(sdev, pm);
299  
300  	/* Notify DSP of upcoming power down */
301  	if (sof_ops(sdev)->remove && pm_ops && pm_ops->ctx_save)
302  		return pm_ops->ctx_save(sdev);
303  
304  	return 0;
305  }
306  
snd_sof_runtime_suspend(struct device * dev)307  int snd_sof_runtime_suspend(struct device *dev)
308  {
309  	return sof_suspend(dev, true);
310  }
311  EXPORT_SYMBOL(snd_sof_runtime_suspend);
312  
snd_sof_runtime_idle(struct device * dev)313  int snd_sof_runtime_idle(struct device *dev)
314  {
315  	struct snd_sof_dev *sdev = dev_get_drvdata(dev);
316  
317  	return snd_sof_dsp_runtime_idle(sdev);
318  }
319  EXPORT_SYMBOL(snd_sof_runtime_idle);
320  
snd_sof_runtime_resume(struct device * dev)321  int snd_sof_runtime_resume(struct device *dev)
322  {
323  	return sof_resume(dev, true);
324  }
325  EXPORT_SYMBOL(snd_sof_runtime_resume);
326  
snd_sof_resume(struct device * dev)327  int snd_sof_resume(struct device *dev)
328  {
329  	return sof_resume(dev, false);
330  }
331  EXPORT_SYMBOL(snd_sof_resume);
332  
snd_sof_suspend(struct device * dev)333  int snd_sof_suspend(struct device *dev)
334  {
335  	return sof_suspend(dev, false);
336  }
337  EXPORT_SYMBOL(snd_sof_suspend);
338  
snd_sof_prepare(struct device * dev)339  int snd_sof_prepare(struct device *dev)
340  {
341  	struct snd_sof_dev *sdev = dev_get_drvdata(dev);
342  	const struct sof_dev_desc *desc = sdev->pdata->desc;
343  
344  	/* will suspend to S3 by default */
345  	sdev->system_suspend_target = SOF_SUSPEND_S3;
346  
347  	/*
348  	 * if the firmware is crashed or boot failed then we try to aim for S3
349  	 * to reboot the firmware
350  	 */
351  	if (sdev->fw_state == SOF_FW_CRASHED ||
352  	    sdev->fw_state == SOF_FW_BOOT_FAILED)
353  		return 0;
354  
355  	if (!desc->use_acpi_target_states)
356  		return 0;
357  
358  #if defined(CONFIG_ACPI)
359  	switch (acpi_target_system_state()) {
360  	case ACPI_STATE_S0:
361  		sdev->system_suspend_target = SOF_SUSPEND_S0IX;
362  		break;
363  	case ACPI_STATE_S1:
364  	case ACPI_STATE_S2:
365  	case ACPI_STATE_S3:
366  		sdev->system_suspend_target = SOF_SUSPEND_S3;
367  		break;
368  	case ACPI_STATE_S4:
369  		sdev->system_suspend_target = SOF_SUSPEND_S4;
370  		break;
371  	case ACPI_STATE_S5:
372  		sdev->system_suspend_target = SOF_SUSPEND_S5;
373  		break;
374  	default:
375  		break;
376  	}
377  #endif
378  
379  	return 0;
380  }
381  EXPORT_SYMBOL(snd_sof_prepare);
382  
snd_sof_complete(struct device * dev)383  void snd_sof_complete(struct device *dev)
384  {
385  	struct snd_sof_dev *sdev = dev_get_drvdata(dev);
386  
387  	sdev->system_suspend_target = SOF_SUSPEND_NONE;
388  }
389  EXPORT_SYMBOL(snd_sof_complete);
390