1
2
3
4
5
6
7
8
9
10
11#include "ops.h"
12#include "sof-priv.h"
13
14static int sof_restore_kcontrols(struct snd_sof_dev *sdev)
15{
16 struct snd_sof_control *scontrol;
17 int ipc_cmd, ctrl_type;
18 int ret = 0;
19
20
21 list_for_each_entry(scontrol, &sdev->kcontrol_list, list) {
22
23 scontrol->readback_offset = 0;
24
25
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,
33 ipc_cmd, ctrl_type,
34 scontrol->cmd,
35 true);
36 break;
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,
41 ipc_cmd, ctrl_type,
42 scontrol->cmd,
43 true);
44 break;
45
46 default:
47 break;
48 }
49
50 if (ret < 0) {
51 dev_err(sdev->dev,
52 "error: failed kcontrol value set for widget: %d\n",
53 scontrol->comp_id);
54
55 return ret;
56 }
57 }
58
59 return 0;
60}
61
62static int sof_restore_pipelines(struct snd_sof_dev *sdev)
63{
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;
70 int ret;
71
72
73 list_for_each_entry_reverse(swidget, &sdev->widget_list, list) {
74 struct sof_ipc_comp_reply r;
75
76
77 if (!swidget->private)
78 continue;
79
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),
88 &r, sizeof(r));
89 break;
90 case snd_soc_dapm_scheduler:
91
92
93
94
95
96
97
98 pipeline = swidget->private;
99 ret = sof_load_pipeline_ipc(sdev, pipeline, &r);
100 break;
101 default:
102 hdr = swidget->private;
103 ret = sof_ipc_tx_message(sdev->ipc, hdr->cmd,
104 swidget->private, hdr->size,
105 &r, sizeof(r));
106 break;
107 }
108 if (ret < 0) {
109 dev_err(sdev->dev,
110 "error: failed to load widget type %d with ID: %d\n",
111 swidget->widget->id, swidget->comp_id);
112
113 return ret;
114 }
115 }
116
117
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;
121
122
123 if (!sroute->private)
124 continue;
125
126 connect = sroute->private;
127
128
129 ret = sof_ipc_tx_message(sdev->ipc,
130 connect->hdr.cmd,
131 connect, sizeof(*connect),
132 &reply, sizeof(reply));
133 if (ret < 0) {
134 dev_err(sdev->dev,
135 "error: failed to load route sink %s control %s source %s\n",
136 sroute->route->sink,
137 sroute->route->control ? sroute->route->control
138 : "none",
139 sroute->route->source);
140
141 return ret;
142 }
143 }
144
145
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;
149
150 if (!config) {
151 dev_err(sdev->dev, "error: no config for DAI %s\n",
152 dai->name);
153 continue;
154 }
155
156
157
158
159
160
161
162 if (config->type == SOF_DAI_INTEL_HDA)
163 config->hda.link_dma_ch = DMA_CHAN_INVALID;
164
165 ret = sof_ipc_tx_message(sdev->ipc,
166 config->hdr.cmd, config,
167 config->hdr.size,
168 &reply, sizeof(reply));
169
170 if (ret < 0) {
171 dev_err(sdev->dev,
172 "error: failed to set dai config for %s\n",
173 dai->name);
174
175 return ret;
176 }
177 }
178
179
180 list_for_each_entry(swidget, &sdev->widget_list, list) {
181 switch (swidget->id) {
182 case snd_soc_dapm_scheduler:
183 swidget->complete =
184 snd_sof_complete_pipeline(sdev, swidget);
185 break;
186 default:
187 break;
188 }
189 }
190
191
192 ret = sof_restore_kcontrols(sdev);
193 if (ret < 0)
194 dev_err(sdev->dev,
195 "error: restoring kcontrols after resume\n");
196
197 return ret;
198}
199
200static int sof_send_pm_ctx_ipc(struct snd_sof_dev *sdev, int cmd)
201{
202 struct sof_ipc_pm_ctx pm_ctx;
203 struct sof_ipc_reply reply;
204
205 memset(&pm_ctx, 0, sizeof(pm_ctx));
206
207
208 pm_ctx.hdr.size = sizeof(pm_ctx);
209 pm_ctx.hdr.cmd = SOF_IPC_GLB_PM_MSG | cmd;
210
211
212 return sof_ipc_tx_message(sdev->ipc, pm_ctx.hdr.cmd, &pm_ctx,
213 sizeof(pm_ctx), &reply, sizeof(reply));
214}
215
216static int sof_set_hw_params_upon_resume(struct snd_sof_dev *sdev)
217{
218 struct snd_pcm_substream *substream;
219 struct snd_sof_pcm *spcm;
220 snd_pcm_state_t state;
221 int dir;
222
223
224
225
226
227
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)
232 continue;
233
234 state = substream->runtime->status->state;
235 if (state == SNDRV_PCM_STATE_SUSPENDED)
236 spcm->prepared[dir] = false;
237 }
238 }
239
240
241 return snd_sof_dsp_hw_params_upon_resume(sdev);
242}
243
244#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_ENABLE_DEBUGFS_CACHE)
245static void sof_cache_debugfs(struct snd_sof_dev *sdev)
246{
247 struct snd_sof_dfsentry *dfse;
248
249 list_for_each_entry(dfse, &sdev->dfsentry_list, list) {
250
251
252 if (dfse->type == SOF_DFSENTRY_TYPE_BUF)
253 continue;
254
255
256 if (dfse->access_type == SOF_DEBUGFS_ACCESS_D0_ONLY)
257 memcpy_fromio(dfse->cache_buf, dfse->io_mem,
258 dfse->size);
259 }
260}
261#endif
262
263static int sof_resume(struct device *dev, bool runtime_resume)
264{
265 struct snd_sof_dev *sdev = dev_get_drvdata(dev);
266 int ret;
267
268
269 if (!sof_ops(sdev)->resume || !sof_ops(sdev)->runtime_resume)
270 return 0;
271
272
273
274
275
276 if (runtime_resume)
277 ret = snd_sof_dsp_runtime_resume(sdev);
278 else
279 ret = snd_sof_dsp_resume(sdev);
280 if (ret < 0) {
281 dev_err(sdev->dev,
282 "error: failed to power up DSP after resume\n");
283 return ret;
284 }
285
286
287 ret = snd_sof_load_firmware(sdev);
288 if (ret < 0) {
289 dev_err(sdev->dev,
290 "error: failed to load DSP firmware after resume %d\n",
291 ret);
292 return ret;
293 }
294
295
296 ret = snd_sof_run_firmware(sdev);
297 if (ret < 0) {
298 dev_err(sdev->dev,
299 "error: failed to boot DSP firmware after resume %d\n",
300 ret);
301 return ret;
302 }
303
304
305 ret = snd_sof_init_trace_ipc(sdev);
306 if (ret < 0) {
307
308 dev_warn(sdev->dev,
309 "warning: failed to init trace after resume %d\n",
310 ret);
311 }
312
313
314 ret = sof_restore_pipelines(sdev);
315 if (ret < 0) {
316 dev_err(sdev->dev,
317 "error: failed to restore pipeline after resume %d\n",
318 ret);
319 return ret;
320 }
321
322
323 ret = sof_send_pm_ctx_ipc(sdev, SOF_IPC_PM_CTX_RESTORE);
324 if (ret < 0)
325 dev_err(sdev->dev,
326 "error: ctx_restore ipc error during resume %d\n",
327 ret);
328
329
330 sdev->d0_substate = SOF_DSP_D0I0;
331
332 return ret;
333}
334
335static int sof_suspend(struct device *dev, bool runtime_suspend)
336{
337 struct snd_sof_dev *sdev = dev_get_drvdata(dev);
338 int ret;
339
340
341 if (!sof_ops(sdev)->suspend)
342 return 0;
343
344
345 snd_sof_release_trace(sdev);
346
347
348 if (!runtime_suspend) {
349 ret = sof_set_hw_params_upon_resume(sdev);
350 if (ret < 0) {
351 dev_err(sdev->dev,
352 "error: setting hw_params flag during suspend %d\n",
353 ret);
354 return ret;
355 }
356 }
357
358#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_ENABLE_DEBUGFS_CACHE)
359
360 if (runtime_suspend)
361 sof_cache_debugfs(sdev);
362#endif
363
364 ret = sof_send_pm_ctx_ipc(sdev, SOF_IPC_PM_CTX_SAVE);
365 if (ret == -EBUSY || ret == -EAGAIN) {
366
367
368
369
370 dev_err(sdev->dev,
371 "error: ctx_save ipc error during suspend %d\n",
372 ret);
373 return ret;
374 } else if (ret < 0) {
375
376 dev_warn(sdev->dev,
377 "ctx_save ipc error %d, proceeding with suspend\n",
378 ret);
379 }
380
381
382 if (runtime_suspend)
383 ret = snd_sof_dsp_runtime_suspend(sdev);
384 else
385 ret = snd_sof_dsp_suspend(sdev);
386 if (ret < 0)
387 dev_err(sdev->dev,
388 "error: failed to power down DSP during suspend %d\n",
389 ret);
390
391 return ret;
392}
393
394int snd_sof_runtime_suspend(struct device *dev)
395{
396 return sof_suspend(dev, true);
397}
398EXPORT_SYMBOL(snd_sof_runtime_suspend);
399
400int snd_sof_runtime_idle(struct device *dev)
401{
402 struct snd_sof_dev *sdev = dev_get_drvdata(dev);
403
404 return snd_sof_dsp_runtime_idle(sdev);
405}
406EXPORT_SYMBOL(snd_sof_runtime_idle);
407
408int snd_sof_runtime_resume(struct device *dev)
409{
410 return sof_resume(dev, true);
411}
412EXPORT_SYMBOL(snd_sof_runtime_resume);
413
414int snd_sof_set_d0_substate(struct snd_sof_dev *sdev,
415 enum sof_d0_substate d0_substate)
416{
417 int ret;
418
419 if (sdev->d0_substate == d0_substate)
420 return 0;
421
422
423 ret = snd_sof_dsp_set_power_state(sdev, d0_substate);
424 if (ret < 0)
425 return ret;
426
427
428 sdev->d0_substate = d0_substate;
429
430 return 0;
431}
432EXPORT_SYMBOL(snd_sof_set_d0_substate);
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466int snd_sof_resume(struct device *dev)
467{
468 struct snd_sof_dev *sdev = dev_get_drvdata(dev);
469 int ret;
470
471 if (snd_sof_dsp_d0i3_on_suspend(sdev)) {
472
473 dev_dbg(sdev->dev, "DSP will exit from D0i3...\n");
474 ret = snd_sof_set_d0_substate(sdev, SOF_DSP_D0I0);
475 if (ret == -ENOTSUPP) {
476
477 dev_dbg(sdev->dev, "D0i3 not supported, fall back to resume from D3...\n");
478 goto d3_resume;
479 } else if (ret < 0) {
480 dev_err(sdev->dev, "error: failed to exit from D0I3 %d\n",
481 ret);
482 return ret;
483 }
484
485
486 return snd_sof_dsp_resume(sdev);
487 }
488
489d3_resume:
490
491 return sof_resume(dev, false);
492}
493EXPORT_SYMBOL(snd_sof_resume);
494
495int snd_sof_suspend(struct device *dev)
496{
497 struct snd_sof_dev *sdev = dev_get_drvdata(dev);
498 int ret;
499
500 if (snd_sof_dsp_d0i3_on_suspend(sdev)) {
501
502 dev_dbg(sdev->dev, "DSP is trying to enter D0i3...\n");
503 ret = snd_sof_set_d0_substate(sdev, SOF_DSP_D0I3);
504 if (ret == -ENOTSUPP) {
505
506 dev_dbg(sdev->dev, "D0i3 not supported, fall back to D3...\n");
507 goto d3_suspend;
508 } else if (ret < 0) {
509 dev_err(sdev->dev, "error: failed to enter D0I3, %d\n",
510 ret);
511 return ret;
512 }
513
514
515 return snd_sof_dsp_suspend(sdev);
516 }
517
518d3_suspend:
519
520 return sof_suspend(dev, false);
521}
522EXPORT_SYMBOL(snd_sof_suspend);
523
524int snd_sof_prepare(struct device *dev)
525{
526 struct snd_sof_dev *sdev = dev_get_drvdata(dev);
527
528#if defined(CONFIG_ACPI)
529 sdev->s0_suspend = acpi_target_system_state() == ACPI_STATE_S0;
530#else
531
532 sdev->s0_suspend = false;
533#endif
534
535 return 0;
536}
537EXPORT_SYMBOL(snd_sof_prepare);
538
539void snd_sof_complete(struct device *dev)
540{
541 struct snd_sof_dev *sdev = dev_get_drvdata(dev);
542
543 sdev->s0_suspend = false;
544}
545EXPORT_SYMBOL(snd_sof_complete);
546