1
2
3
4
5
6
7
8
9
10
11#include "sof-audio.h"
12#include "ops.h"
13
14
15
16
17
18bool snd_sof_dsp_only_d0i3_compatible_stream_active(struct snd_sof_dev *sdev)
19{
20 struct snd_pcm_substream *substream;
21 struct snd_sof_pcm *spcm;
22 bool d0i3_compatible_active = false;
23 int dir;
24
25 list_for_each_entry(spcm, &sdev->pcm_list, list) {
26 for_each_pcm_streams(dir) {
27 substream = spcm->stream[dir].substream;
28 if (!substream || !substream->runtime)
29 continue;
30
31
32
33
34
35
36 if (!spcm->stream[dir].d0i3_compatible)
37 return false;
38
39 d0i3_compatible_active = true;
40 }
41 }
42
43 return d0i3_compatible_active;
44}
45EXPORT_SYMBOL(snd_sof_dsp_only_d0i3_compatible_stream_active);
46
47bool snd_sof_stream_suspend_ignored(struct snd_sof_dev *sdev)
48{
49 struct snd_sof_pcm *spcm;
50
51 list_for_each_entry(spcm, &sdev->pcm_list, list) {
52 if (spcm->stream[SNDRV_PCM_STREAM_PLAYBACK].suspend_ignored ||
53 spcm->stream[SNDRV_PCM_STREAM_CAPTURE].suspend_ignored)
54 return true;
55 }
56
57 return false;
58}
59
60int sof_set_hw_params_upon_resume(struct device *dev)
61{
62 struct snd_sof_dev *sdev = dev_get_drvdata(dev);
63 struct snd_pcm_substream *substream;
64 struct snd_sof_pcm *spcm;
65 snd_pcm_state_t state;
66 int dir;
67
68
69
70
71
72
73 list_for_each_entry(spcm, &sdev->pcm_list, list) {
74 for_each_pcm_streams(dir) {
75
76
77
78
79 if (spcm->stream[dir].suspend_ignored)
80 continue;
81
82 substream = spcm->stream[dir].substream;
83 if (!substream || !substream->runtime)
84 continue;
85
86 state = substream->runtime->status->state;
87 if (state == SNDRV_PCM_STATE_SUSPENDED)
88 spcm->prepared[dir] = false;
89 }
90 }
91
92
93 return snd_sof_dsp_hw_params_upon_resume(sdev);
94}
95
96static int sof_restore_kcontrols(struct device *dev)
97{
98 struct snd_sof_dev *sdev = dev_get_drvdata(dev);
99 struct snd_sof_control *scontrol;
100 int ipc_cmd, ctrl_type;
101 int ret = 0;
102
103
104 list_for_each_entry(scontrol, &sdev->kcontrol_list, list) {
105
106 scontrol->readback_offset = 0;
107
108
109 switch (scontrol->cmd) {
110 case SOF_CTRL_CMD_VOLUME:
111 case SOF_CTRL_CMD_ENUM:
112 case SOF_CTRL_CMD_SWITCH:
113 ipc_cmd = SOF_IPC_COMP_SET_VALUE;
114 ctrl_type = SOF_CTRL_TYPE_VALUE_CHAN_SET;
115 ret = snd_sof_ipc_set_get_comp_data(scontrol,
116 ipc_cmd, ctrl_type,
117 scontrol->cmd,
118 true);
119 break;
120 case SOF_CTRL_CMD_BINARY:
121 ipc_cmd = SOF_IPC_COMP_SET_DATA;
122 ctrl_type = SOF_CTRL_TYPE_DATA_SET;
123 ret = snd_sof_ipc_set_get_comp_data(scontrol,
124 ipc_cmd, ctrl_type,
125 scontrol->cmd,
126 true);
127 break;
128
129 default:
130 break;
131 }
132
133 if (ret < 0) {
134 dev_err(dev,
135 "error: failed kcontrol value set for widget: %d\n",
136 scontrol->comp_id);
137
138 return ret;
139 }
140 }
141
142 return 0;
143}
144
145const struct sof_ipc_pipe_new *snd_sof_pipeline_find(struct snd_sof_dev *sdev,
146 int pipeline_id)
147{
148 const struct snd_sof_widget *swidget;
149
150 list_for_each_entry(swidget, &sdev->widget_list, list)
151 if (swidget->id == snd_soc_dapm_scheduler) {
152 const struct sof_ipc_pipe_new *pipeline =
153 swidget->private;
154 if (pipeline->pipeline_id == pipeline_id)
155 return pipeline;
156 }
157
158 return NULL;
159}
160
161int sof_restore_pipelines(struct device *dev)
162{
163 struct snd_sof_dev *sdev = dev_get_drvdata(dev);
164 struct snd_sof_widget *swidget;
165 struct snd_sof_route *sroute;
166 struct sof_ipc_pipe_new *pipeline;
167 struct snd_sof_dai *dai;
168 struct sof_ipc_cmd_hdr *hdr;
169 struct sof_ipc_comp *comp;
170 size_t ipc_size;
171 int ret;
172
173
174 list_for_each_entry_reverse(swidget, &sdev->widget_list, list) {
175 struct sof_ipc_comp_reply r;
176
177
178 if (!swidget->private)
179 continue;
180
181 ret = sof_pipeline_core_enable(sdev, swidget);
182 if (ret < 0) {
183 dev_err(dev,
184 "error: failed to enable target core: %d\n",
185 ret);
186
187 return ret;
188 }
189
190 switch (swidget->id) {
191 case snd_soc_dapm_dai_in:
192 case snd_soc_dapm_dai_out:
193 ipc_size = sizeof(struct sof_ipc_comp_dai) +
194 sizeof(struct sof_ipc_comp_ext);
195 comp = kzalloc(ipc_size, GFP_KERNEL);
196 if (!comp)
197 return -ENOMEM;
198
199 dai = swidget->private;
200 memcpy(comp, &dai->comp_dai,
201 sizeof(struct sof_ipc_comp_dai));
202
203
204 memcpy((u8 *)comp + sizeof(struct sof_ipc_comp_dai),
205 &swidget->comp_ext, sizeof(swidget->comp_ext));
206
207 ret = sof_ipc_tx_message(sdev->ipc, comp->hdr.cmd,
208 comp, ipc_size,
209 &r, sizeof(r));
210 kfree(comp);
211 break;
212 case snd_soc_dapm_scheduler:
213
214
215
216
217
218
219
220 pipeline = swidget->private;
221 ret = sof_load_pipeline_ipc(dev, pipeline, &r);
222 break;
223 default:
224 hdr = swidget->private;
225 ret = sof_ipc_tx_message(sdev->ipc, hdr->cmd,
226 swidget->private, hdr->size,
227 &r, sizeof(r));
228 break;
229 }
230 if (ret < 0) {
231 dev_err(dev,
232 "error: failed to load widget type %d with ID: %d\n",
233 swidget->widget->id, swidget->comp_id);
234
235 return ret;
236 }
237 }
238
239
240 list_for_each_entry_reverse(sroute, &sdev->route_list, list) {
241 struct sof_ipc_pipe_comp_connect *connect;
242 struct sof_ipc_reply reply;
243
244
245 if (!sroute->private)
246 continue;
247
248 connect = sroute->private;
249
250
251 ret = sof_ipc_tx_message(sdev->ipc,
252 connect->hdr.cmd,
253 connect, sizeof(*connect),
254 &reply, sizeof(reply));
255 if (ret < 0) {
256 dev_err(dev,
257 "error: failed to load route sink %s control %s source %s\n",
258 sroute->route->sink,
259 sroute->route->control ? sroute->route->control
260 : "none",
261 sroute->route->source);
262
263 return ret;
264 }
265 }
266
267
268 list_for_each_entry_reverse(dai, &sdev->dai_list, list) {
269 struct sof_ipc_reply reply;
270 struct sof_ipc_dai_config *config = &dai->dai_config[dai->current_config];
271
272 if (!config) {
273 dev_err(dev, "error: no config for DAI %s\n",
274 dai->name);
275 continue;
276 }
277
278
279
280
281
282
283
284 if (config->type == SOF_DAI_INTEL_HDA)
285 config->hda.link_dma_ch = DMA_CHAN_INVALID;
286
287 ret = sof_ipc_tx_message(sdev->ipc,
288 config->hdr.cmd, config,
289 config->hdr.size,
290 &reply, sizeof(reply));
291
292 if (ret < 0) {
293 dev_err(dev,
294 "error: failed to set dai config for %s\n",
295 dai->name);
296
297 return ret;
298 }
299 }
300
301
302 list_for_each_entry(swidget, &sdev->widget_list, list) {
303 switch (swidget->id) {
304 case snd_soc_dapm_scheduler:
305 swidget->complete =
306 snd_sof_complete_pipeline(dev, swidget);
307 break;
308 default:
309 break;
310 }
311 }
312
313
314 ret = sof_restore_kcontrols(dev);
315 if (ret < 0)
316 dev_err(dev,
317 "error: restoring kcontrols after resume\n");
318
319 return ret;
320}
321
322
323
324
325
326struct snd_sof_pcm *snd_sof_find_spcm_name(struct snd_soc_component *scomp,
327 const char *name)
328{
329 struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
330 struct snd_sof_pcm *spcm;
331
332 list_for_each_entry(spcm, &sdev->pcm_list, list) {
333
334 if (strcmp(spcm->pcm.dai_name, name) == 0)
335 return spcm;
336
337
338 if (*spcm->pcm.caps[0].name &&
339 !strcmp(spcm->pcm.caps[0].name, name))
340 return spcm;
341
342
343 if (*spcm->pcm.caps[1].name &&
344 !strcmp(spcm->pcm.caps[1].name, name))
345 return spcm;
346 }
347
348 return NULL;
349}
350
351struct snd_sof_pcm *snd_sof_find_spcm_comp(struct snd_soc_component *scomp,
352 unsigned int comp_id,
353 int *direction)
354{
355 struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
356 struct snd_sof_pcm *spcm;
357 int dir;
358
359 list_for_each_entry(spcm, &sdev->pcm_list, list) {
360 for_each_pcm_streams(dir) {
361 if (spcm->stream[dir].comp_id == comp_id) {
362 *direction = dir;
363 return spcm;
364 }
365 }
366 }
367
368 return NULL;
369}
370
371struct snd_sof_pcm *snd_sof_find_spcm_pcm_id(struct snd_soc_component *scomp,
372 unsigned int pcm_id)
373{
374 struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
375 struct snd_sof_pcm *spcm;
376
377 list_for_each_entry(spcm, &sdev->pcm_list, list) {
378 if (le32_to_cpu(spcm->pcm.pcm_id) == pcm_id)
379 return spcm;
380 }
381
382 return NULL;
383}
384
385struct snd_sof_widget *snd_sof_find_swidget(struct snd_soc_component *scomp,
386 const char *name)
387{
388 struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
389 struct snd_sof_widget *swidget;
390
391 list_for_each_entry(swidget, &sdev->widget_list, list) {
392 if (strcmp(name, swidget->widget->name) == 0)
393 return swidget;
394 }
395
396 return NULL;
397}
398
399
400struct snd_sof_widget *
401snd_sof_find_swidget_sname(struct snd_soc_component *scomp,
402 const char *pcm_name, int dir)
403{
404 struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
405 struct snd_sof_widget *swidget;
406 enum snd_soc_dapm_type type;
407
408 if (dir == SNDRV_PCM_STREAM_PLAYBACK)
409 type = snd_soc_dapm_aif_in;
410 else
411 type = snd_soc_dapm_aif_out;
412
413 list_for_each_entry(swidget, &sdev->widget_list, list) {
414 if (!strcmp(pcm_name, swidget->widget->sname) &&
415 swidget->id == type)
416 return swidget;
417 }
418
419 return NULL;
420}
421
422struct snd_sof_dai *snd_sof_find_dai(struct snd_soc_component *scomp,
423 const char *name)
424{
425 struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
426 struct snd_sof_dai *dai;
427
428 list_for_each_entry(dai, &sdev->dai_list, list) {
429 if (dai->name && (strcmp(name, dai->name) == 0))
430 return dai;
431 }
432
433 return NULL;
434}
435
436
437
438
439
440int sof_dai_get_mclk(struct snd_soc_pcm_runtime *rtd)
441{
442 struct snd_soc_component *component =
443 snd_soc_rtdcom_lookup(rtd, SOF_AUDIO_PCM_DRV_NAME);
444 struct snd_sof_dai *dai =
445 snd_sof_find_dai(component, (char *)rtd->dai_link->name);
446
447
448 if (!dai || !dai->dai_config)
449 return 0;
450
451 switch (dai->dai_config->type) {
452 case SOF_DAI_INTEL_SSP:
453 return dai->dai_config->ssp.mclk_rate;
454 default:
455
456 dev_err(rtd->dev, "mclk for dai_config->type %d not supported yet!\n",
457 dai->dai_config->type);
458 return -EINVAL;
459 }
460}
461EXPORT_SYMBOL(sof_dai_get_mclk);
462
463
464
465
466int sof_machine_check(struct snd_sof_dev *sdev)
467{
468 struct snd_sof_pdata *sof_pdata = sdev->pdata;
469 const struct sof_dev_desc *desc = sof_pdata->desc;
470 struct snd_soc_acpi_mach *mach;
471
472 if (!IS_ENABLED(CONFIG_SND_SOC_SOF_FORCE_NOCODEC_MODE)) {
473
474
475 snd_sof_machine_select(sdev);
476 if (sof_pdata->machine) {
477 snd_sof_set_mach_params(sof_pdata->machine, sdev);
478 return 0;
479 }
480
481 if (!IS_ENABLED(CONFIG_SND_SOC_SOF_NOCODEC)) {
482 dev_err(sdev->dev, "error: no matching ASoC machine driver found - aborting probe\n");
483 return -ENODEV;
484 }
485 } else {
486 dev_warn(sdev->dev, "Force to use nocodec mode\n");
487 }
488
489
490 dev_warn(sdev->dev, "Using nocodec machine driver\n");
491 mach = devm_kzalloc(sdev->dev, sizeof(*mach), GFP_KERNEL);
492 if (!mach)
493 return -ENOMEM;
494
495 mach->drv_name = "sof-nocodec";
496 sof_pdata->tplg_filename = desc->nocodec_tplg_filename;
497
498 sof_pdata->machine = mach;
499 snd_sof_set_mach_params(sof_pdata->machine, sdev);
500
501 return 0;
502}
503EXPORT_SYMBOL(sof_machine_check);
504
505int sof_machine_register(struct snd_sof_dev *sdev, void *pdata)
506{
507 struct snd_sof_pdata *plat_data = pdata;
508 const char *drv_name;
509 const void *mach;
510 int size;
511
512 drv_name = plat_data->machine->drv_name;
513 mach = plat_data->machine;
514 size = sizeof(*plat_data->machine);
515
516
517 plat_data->pdev_mach =
518 platform_device_register_data(sdev->dev, drv_name,
519 PLATFORM_DEVID_NONE, mach, size);
520 if (IS_ERR(plat_data->pdev_mach))
521 return PTR_ERR(plat_data->pdev_mach);
522
523 dev_dbg(sdev->dev, "created machine %s\n",
524 dev_name(&plat_data->pdev_mach->dev));
525
526 return 0;
527}
528EXPORT_SYMBOL(sof_machine_register);
529
530void sof_machine_unregister(struct snd_sof_dev *sdev, void *pdata)
531{
532 struct snd_sof_pdata *plat_data = pdata;
533
534 if (!IS_ERR_OR_NULL(plat_data->pdev_mach))
535 platform_device_unregister(plat_data->pdev_mach);
536}
537EXPORT_SYMBOL(sof_machine_unregister);
538