1 // SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
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.
6 // Copyright(c) 2018 Intel Corporation. All rights reserved.
8 // Author: Liam Girdwood <liam.r.girdwood@linux.intel.com>
14 static int sof_restore_kcontrols(struct snd_sof_dev
*sdev
)
16 struct snd_sof_control
*scontrol
;
17 int ipc_cmd
, ctrl_type
;
20 /* restore kcontrol values */
21 list_for_each_entry(scontrol
, &sdev
->kcontrol_list
, list
) {
22 /* reset readback offset for scontrol after resuming */
23 scontrol
->readback_offset
= 0;
25 /* notify DSP of kcontrol values */
26 switch (scontrol
->cmd
) {
27 case SOF_CTRL_CMD_VOLUME
:
28 case SOF_CTRL_CMD_ENUM
:
29 case SOF_CTRL_CMD_SWITCH
:
30 ipc_cmd
= SOF_IPC_COMP_SET_VALUE
;
31 ctrl_type
= SOF_CTRL_TYPE_VALUE_CHAN_SET
;
32 ret
= snd_sof_ipc_set_get_comp_data(sdev
->ipc
, scontrol
,
37 case SOF_CTRL_CMD_BINARY
:
38 ipc_cmd
= SOF_IPC_COMP_SET_DATA
;
39 ctrl_type
= SOF_CTRL_TYPE_DATA_SET
;
40 ret
= snd_sof_ipc_set_get_comp_data(sdev
->ipc
, scontrol
,
52 "error: failed kcontrol value set for widget: %d\n",
62 static int sof_restore_pipelines(struct snd_sof_dev
*sdev
)
64 struct snd_sof_widget
*swidget
;
65 struct snd_sof_route
*sroute
;
66 struct sof_ipc_pipe_new
*pipeline
;
67 struct snd_sof_dai
*dai
;
68 struct sof_ipc_comp_dai
*comp_dai
;
69 struct sof_ipc_cmd_hdr
*hdr
;
72 /* restore pipeline components */
73 list_for_each_entry_reverse(swidget
, &sdev
->widget_list
, list
) {
74 struct sof_ipc_comp_reply r
;
76 /* skip if there is no private data */
77 if (!swidget
->private)
80 switch (swidget
->id
) {
81 case snd_soc_dapm_dai_in
:
82 case snd_soc_dapm_dai_out
:
83 dai
= swidget
->private;
84 comp_dai
= &dai
->comp_dai
;
85 ret
= sof_ipc_tx_message(sdev
->ipc
,
86 comp_dai
->comp
.hdr
.cmd
,
87 comp_dai
, sizeof(*comp_dai
),
90 case snd_soc_dapm_scheduler
:
93 * During suspend, all DSP cores are powered off.
94 * Therefore upon resume, create the pipeline comp
95 * and power up the core that the pipeline is
98 pipeline
= swidget
->private;
99 ret
= sof_load_pipeline_ipc(sdev
, pipeline
, &r
);
102 hdr
= swidget
->private;
103 ret
= sof_ipc_tx_message(sdev
->ipc
, hdr
->cmd
,
104 swidget
->private, hdr
->size
,
110 "error: failed to load widget type %d with ID: %d\n",
111 swidget
->widget
->id
, swidget
->comp_id
);
117 /* restore pipeline connections */
118 list_for_each_entry_reverse(sroute
, &sdev
->route_list
, list
) {
119 struct sof_ipc_pipe_comp_connect
*connect
;
120 struct sof_ipc_reply reply
;
122 /* skip if there's no private data */
123 if (!sroute
->private)
126 connect
= sroute
->private;
129 ret
= sof_ipc_tx_message(sdev
->ipc
,
131 connect
, sizeof(*connect
),
132 &reply
, sizeof(reply
));
135 "error: failed to load route sink %s control %s source %s\n",
137 sroute
->route
->control
? sroute
->route
->control
139 sroute
->route
->source
);
145 /* restore dai links */
146 list_for_each_entry_reverse(dai
, &sdev
->dai_list
, list
) {
147 struct sof_ipc_reply reply
;
148 struct sof_ipc_dai_config
*config
= dai
->dai_config
;
151 dev_err(sdev
->dev
, "error: no config for DAI %s\n",
157 * The link DMA channel would be invalidated for running
158 * streams but not for streams that were in the PAUSED
159 * state during suspend. So invalidate it here before setting
160 * the dai config in the DSP.
162 if (config
->type
== SOF_DAI_INTEL_HDA
)
163 config
->hda
.link_dma_ch
= DMA_CHAN_INVALID
;
165 ret
= sof_ipc_tx_message(sdev
->ipc
,
166 config
->hdr
.cmd
, config
,
168 &reply
, sizeof(reply
));
172 "error: failed to set dai config for %s\n",
179 /* complete pipeline */
180 list_for_each_entry(swidget
, &sdev
->widget_list
, list
) {
181 switch (swidget
->id
) {
182 case snd_soc_dapm_scheduler
:
184 snd_sof_complete_pipeline(sdev
, swidget
);
191 /* restore pipeline kcontrols */
192 ret
= sof_restore_kcontrols(sdev
);
195 "error: restoring kcontrols after resume\n");
200 static int sof_send_pm_ipc(struct snd_sof_dev
*sdev
, int cmd
)
202 struct sof_ipc_pm_ctx pm_ctx
;
203 struct sof_ipc_reply reply
;
205 memset(&pm_ctx
, 0, sizeof(pm_ctx
));
207 /* configure ctx save ipc message */
208 pm_ctx
.hdr
.size
= sizeof(pm_ctx
);
209 pm_ctx
.hdr
.cmd
= SOF_IPC_GLB_PM_MSG
| cmd
;
211 /* send ctx save ipc to dsp */
212 return sof_ipc_tx_message(sdev
->ipc
, pm_ctx
.hdr
.cmd
, &pm_ctx
,
213 sizeof(pm_ctx
), &reply
, sizeof(reply
));
216 static int sof_set_hw_params_upon_resume(struct snd_sof_dev
*sdev
)
218 struct snd_pcm_substream
*substream
;
219 struct snd_sof_pcm
*spcm
;
220 snd_pcm_state_t state
;
224 * SOF requires hw_params to be set-up internally upon resume.
225 * So, set the flag to indicate this for those streams that
226 * have been suspended.
228 list_for_each_entry(spcm
, &sdev
->pcm_list
, list
) {
229 for (dir
= 0; dir
<= SNDRV_PCM_STREAM_CAPTURE
; dir
++) {
230 substream
= spcm
->stream
[dir
].substream
;
231 if (!substream
|| !substream
->runtime
)
234 state
= substream
->runtime
->status
->state
;
235 if (state
== SNDRV_PCM_STATE_SUSPENDED
)
236 spcm
->prepared
[dir
] = false;
240 /* set internal flag for BE */
241 return snd_sof_dsp_hw_params_upon_resume(sdev
);
244 #if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_ENABLE_DEBUGFS_CACHE)
245 static void sof_cache_debugfs(struct snd_sof_dev
*sdev
)
247 struct snd_sof_dfsentry
*dfse
;
249 list_for_each_entry(dfse
, &sdev
->dfsentry_list
, list
) {
251 /* nothing to do if debugfs buffer is not IO mem */
252 if (dfse
->type
== SOF_DFSENTRY_TYPE_BUF
)
255 /* cache memory that is only accessible in D0 */
256 if (dfse
->access_type
== SOF_DEBUGFS_ACCESS_D0_ONLY
)
257 memcpy_fromio(dfse
->cache_buf
, dfse
->io_mem
,
263 static int sof_resume(struct device
*dev
, bool runtime_resume
)
265 struct snd_sof_dev
*sdev
= dev_get_drvdata(dev
);
268 /* do nothing if dsp resume callbacks are not set */
269 if (!sof_ops(sdev
)->resume
|| !sof_ops(sdev
)->runtime_resume
)
273 * if the runtime_resume flag is set, call the runtime_resume routine
274 * or else call the system resume routine
277 ret
= snd_sof_dsp_runtime_resume(sdev
);
279 ret
= snd_sof_dsp_resume(sdev
);
282 "error: failed to power up DSP after resume\n");
286 /* load the firmware */
287 ret
= snd_sof_load_firmware(sdev
);
290 "error: failed to load DSP firmware after resume %d\n",
295 /* boot the firmware */
296 ret
= snd_sof_run_firmware(sdev
);
299 "error: failed to boot DSP firmware after resume %d\n",
304 /* resume DMA trace, only need send ipc */
305 ret
= snd_sof_init_trace_ipc(sdev
);
309 "warning: failed to init trace after resume %d\n",
313 /* restore pipelines */
314 ret
= sof_restore_pipelines(sdev
);
317 "error: failed to restore pipeline after resume %d\n",
322 /* notify DSP of system resume */
323 ret
= sof_send_pm_ipc(sdev
, SOF_IPC_PM_CTX_RESTORE
);
326 "error: ctx_restore ipc error during resume %d\n",
332 static int sof_suspend(struct device
*dev
, bool runtime_suspend
)
334 struct snd_sof_dev
*sdev
= dev_get_drvdata(dev
);
337 /* do nothing if dsp suspend callback is not set */
338 if (!sof_ops(sdev
)->suspend
)
342 snd_sof_release_trace(sdev
);
344 /* set restore_stream for all streams during system suspend */
345 if (!runtime_suspend
) {
346 ret
= sof_set_hw_params_upon_resume(sdev
);
349 "error: setting hw_params flag during suspend %d\n",
355 #if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_ENABLE_DEBUGFS_CACHE)
356 /* cache debugfs contents during runtime suspend */
358 sof_cache_debugfs(sdev
);
360 /* notify DSP of upcoming power down */
361 ret
= sof_send_pm_ipc(sdev
, SOF_IPC_PM_CTX_SAVE
);
362 if (ret
== -EBUSY
|| ret
== -EAGAIN
) {
364 * runtime PM has logic to handle -EBUSY/-EAGAIN so
365 * pass these errors up
368 "error: ctx_save ipc error during suspend %d\n",
371 } else if (ret
< 0) {
372 /* FW in unexpected state, continue to power down */
374 "ctx_save ipc error %d, proceeding with suspend\n",
378 /* power down all DSP cores */
380 ret
= snd_sof_dsp_runtime_suspend(sdev
);
382 ret
= snd_sof_dsp_suspend(sdev
);
385 "error: failed to power down DSP during suspend %d\n",
391 int snd_sof_runtime_suspend(struct device
*dev
)
393 return sof_suspend(dev
, true);
395 EXPORT_SYMBOL(snd_sof_runtime_suspend
);
397 int snd_sof_runtime_idle(struct device
*dev
)
399 struct snd_sof_dev
*sdev
= dev_get_drvdata(dev
);
401 return snd_sof_dsp_runtime_idle(sdev
);
403 EXPORT_SYMBOL(snd_sof_runtime_idle
);
405 int snd_sof_runtime_resume(struct device
*dev
)
407 return sof_resume(dev
, true);
409 EXPORT_SYMBOL(snd_sof_runtime_resume
);
411 int snd_sof_resume(struct device
*dev
)
413 return sof_resume(dev
, false);
415 EXPORT_SYMBOL(snd_sof_resume
);
417 int snd_sof_suspend(struct device
*dev
)
419 return sof_suspend(dev
, false);
421 EXPORT_SYMBOL(snd_sof_suspend
);