This source file includes following definitions.
- snd_sof_find_spcm_name
- snd_sof_find_spcm_comp
- snd_sof_find_spcm_pcm_id
- snd_sof_find_swidget
- snd_sof_find_swidget_sname
- snd_sof_find_dai
- snd_sof_get_status
- snd_sof_create_page_table
- sof_machine_check
- sof_probe_continue
- sof_probe_work
- snd_sof_device_probe
- snd_sof_device_remove
1
2
3
4
5
6
7
8
9
10
11 #include <linux/firmware.h>
12 #include <linux/module.h>
13 #include <asm/unaligned.h>
14 #include <sound/soc.h>
15 #include <sound/sof.h>
16 #include "sof-priv.h"
17 #include "ops.h"
18
19
20 #define TIMEOUT_DEFAULT_IPC_MS 500
21 #define TIMEOUT_DEFAULT_BOOT_MS 2000
22
23
24
25
26
27 struct snd_sof_pcm *snd_sof_find_spcm_name(struct snd_sof_dev *sdev,
28 const char *name)
29 {
30 struct snd_sof_pcm *spcm;
31
32 list_for_each_entry(spcm, &sdev->pcm_list, list) {
33
34 if (strcmp(spcm->pcm.dai_name, name) == 0)
35 return spcm;
36
37
38 if (*spcm->pcm.caps[0].name &&
39 !strcmp(spcm->pcm.caps[0].name, name))
40 return spcm;
41
42
43 if (*spcm->pcm.caps[1].name &&
44 !strcmp(spcm->pcm.caps[1].name, name))
45 return spcm;
46 }
47
48 return NULL;
49 }
50
51 struct snd_sof_pcm *snd_sof_find_spcm_comp(struct snd_sof_dev *sdev,
52 unsigned int comp_id,
53 int *direction)
54 {
55 struct snd_sof_pcm *spcm;
56
57 list_for_each_entry(spcm, &sdev->pcm_list, list) {
58 if (spcm->stream[SNDRV_PCM_STREAM_PLAYBACK].comp_id == comp_id) {
59 *direction = SNDRV_PCM_STREAM_PLAYBACK;
60 return spcm;
61 }
62 if (spcm->stream[SNDRV_PCM_STREAM_CAPTURE].comp_id == comp_id) {
63 *direction = SNDRV_PCM_STREAM_CAPTURE;
64 return spcm;
65 }
66 }
67
68 return NULL;
69 }
70
71 struct snd_sof_pcm *snd_sof_find_spcm_pcm_id(struct snd_sof_dev *sdev,
72 unsigned int pcm_id)
73 {
74 struct snd_sof_pcm *spcm;
75
76 list_for_each_entry(spcm, &sdev->pcm_list, list) {
77 if (le32_to_cpu(spcm->pcm.pcm_id) == pcm_id)
78 return spcm;
79 }
80
81 return NULL;
82 }
83
84 struct snd_sof_widget *snd_sof_find_swidget(struct snd_sof_dev *sdev,
85 const char *name)
86 {
87 struct snd_sof_widget *swidget;
88
89 list_for_each_entry(swidget, &sdev->widget_list, list) {
90 if (strcmp(name, swidget->widget->name) == 0)
91 return swidget;
92 }
93
94 return NULL;
95 }
96
97
98 struct snd_sof_widget *snd_sof_find_swidget_sname(struct snd_sof_dev *sdev,
99 const char *pcm_name, int dir)
100 {
101 struct snd_sof_widget *swidget;
102 enum snd_soc_dapm_type type;
103
104 if (dir == SNDRV_PCM_STREAM_PLAYBACK)
105 type = snd_soc_dapm_aif_in;
106 else
107 type = snd_soc_dapm_aif_out;
108
109 list_for_each_entry(swidget, &sdev->widget_list, list) {
110 if (!strcmp(pcm_name, swidget->widget->sname) && swidget->id == type)
111 return swidget;
112 }
113
114 return NULL;
115 }
116
117 struct snd_sof_dai *snd_sof_find_dai(struct snd_sof_dev *sdev,
118 const char *name)
119 {
120 struct snd_sof_dai *dai;
121
122 list_for_each_entry(dai, &sdev->dai_list, list) {
123 if (dai->name && (strcmp(name, dai->name) == 0))
124 return dai;
125 }
126
127 return NULL;
128 }
129
130
131
132
133
134 struct sof_panic_msg {
135 u32 id;
136 const char *msg;
137 };
138
139
140 static const struct sof_panic_msg panic_msg[] = {
141 {SOF_IPC_PANIC_MEM, "out of memory"},
142 {SOF_IPC_PANIC_WORK, "work subsystem init failed"},
143 {SOF_IPC_PANIC_IPC, "IPC subsystem init failed"},
144 {SOF_IPC_PANIC_ARCH, "arch init failed"},
145 {SOF_IPC_PANIC_PLATFORM, "platform init failed"},
146 {SOF_IPC_PANIC_TASK, "scheduler init failed"},
147 {SOF_IPC_PANIC_EXCEPTION, "runtime exception"},
148 {SOF_IPC_PANIC_DEADLOCK, "deadlock"},
149 {SOF_IPC_PANIC_STACK, "stack overflow"},
150 {SOF_IPC_PANIC_IDLE, "can't enter idle"},
151 {SOF_IPC_PANIC_WFI, "invalid wait state"},
152 {SOF_IPC_PANIC_ASSERT, "assertion failed"},
153 };
154
155
156
157
158
159
160 void snd_sof_get_status(struct snd_sof_dev *sdev, u32 panic_code,
161 u32 tracep_code, void *oops,
162 struct sof_ipc_panic_info *panic_info,
163 void *stack, size_t stack_words)
164 {
165 u32 code;
166 int i;
167
168
169 if ((panic_code & SOF_IPC_PANIC_MAGIC_MASK) != SOF_IPC_PANIC_MAGIC) {
170 dev_err(sdev->dev, "error: unexpected fault 0x%8.8x trace 0x%8.8x\n",
171 panic_code, tracep_code);
172 return;
173 }
174
175 code = panic_code & (SOF_IPC_PANIC_MAGIC_MASK | SOF_IPC_PANIC_CODE_MASK);
176
177 for (i = 0; i < ARRAY_SIZE(panic_msg); i++) {
178 if (panic_msg[i].id == code) {
179 dev_err(sdev->dev, "error: %s\n", panic_msg[i].msg);
180 dev_err(sdev->dev, "error: trace point %8.8x\n",
181 tracep_code);
182 goto out;
183 }
184 }
185
186
187 dev_err(sdev->dev, "error: unknown reason %8.8x\n", panic_code);
188 dev_err(sdev->dev, "error: trace point %8.8x\n", tracep_code);
189
190 out:
191 dev_err(sdev->dev, "error: panic at %s:%d\n",
192 panic_info->filename, panic_info->linenum);
193 sof_oops(sdev, oops);
194 sof_stack(sdev, oops, stack, stack_words);
195 }
196 EXPORT_SYMBOL(snd_sof_get_status);
197
198
199
200
201
202
203
204
205 int snd_sof_create_page_table(struct snd_sof_dev *sdev,
206 struct snd_dma_buffer *dmab,
207 unsigned char *page_table, size_t size)
208 {
209 int i, pages;
210
211 pages = snd_sgbuf_aligned_pages(size);
212
213 dev_dbg(sdev->dev, "generating page table for %p size 0x%zx pages %d\n",
214 dmab->area, size, pages);
215
216 for (i = 0; i < pages; i++) {
217
218
219
220
221
222
223
224 u32 idx = (5 * i) >> 1;
225 u32 pfn = snd_sgbuf_get_addr(dmab, i * PAGE_SIZE) >> PAGE_SHIFT;
226 u8 *pg_table;
227
228 dev_vdbg(sdev->dev, "pfn i %i idx %d pfn %x\n", i, idx, pfn);
229
230 pg_table = (u8 *)(page_table + idx);
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246 if (i & 1)
247 put_unaligned_le32((pg_table[0] & 0xf) | pfn << 4,
248 pg_table);
249 else
250 put_unaligned_le32(pfn, pg_table);
251 }
252
253 return pages;
254 }
255
256
257
258
259 static int sof_machine_check(struct snd_sof_dev *sdev)
260 {
261 struct snd_sof_pdata *plat_data = sdev->pdata;
262 #if IS_ENABLED(CONFIG_SND_SOC_SOF_NOCODEC)
263 struct snd_soc_acpi_mach *machine;
264 int ret;
265 #endif
266
267 if (plat_data->machine)
268 return 0;
269
270 #if !IS_ENABLED(CONFIG_SND_SOC_SOF_NOCODEC)
271 dev_err(sdev->dev, "error: no matching ASoC machine driver found - aborting probe\n");
272 return -ENODEV;
273 #else
274
275 dev_warn(sdev->dev, "No ASoC machine driver found - using nocodec\n");
276 machine = devm_kzalloc(sdev->dev, sizeof(*machine), GFP_KERNEL);
277 if (!machine)
278 return -ENOMEM;
279
280 ret = sof_nocodec_setup(sdev->dev, plat_data, machine,
281 plat_data->desc, plat_data->desc->ops);
282 if (ret < 0)
283 return ret;
284
285 plat_data->machine = machine;
286
287 return 0;
288 #endif
289 }
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331 static int sof_probe_continue(struct snd_sof_dev *sdev)
332 {
333 struct snd_sof_pdata *plat_data = sdev->pdata;
334 const char *drv_name;
335 const void *mach;
336 int size;
337 int ret;
338
339
340 ret = snd_sof_probe(sdev);
341 if (ret < 0) {
342 dev_err(sdev->dev, "error: failed to probe DSP %d\n", ret);
343 return ret;
344 }
345
346 sdev->fw_state = SOF_FW_BOOT_PREPARE;
347
348
349 ret = sof_machine_check(sdev);
350 if (ret < 0) {
351 dev_err(sdev->dev, "error: failed to get machine info %d\n",
352 ret);
353 goto dbg_err;
354 }
355
356
357 snd_sof_new_platform_drv(sdev);
358
359
360 ret = snd_sof_dbg_init(sdev);
361 if (ret < 0) {
362
363
364
365
366
367 dev_err(sdev->dev, "error: failed to init DSP trace/debug %d\n",
368 ret);
369 goto dbg_err;
370 }
371
372
373 sdev->ipc = snd_sof_ipc_init(sdev);
374 if (!sdev->ipc) {
375 dev_err(sdev->dev, "error: failed to init DSP IPC %d\n", ret);
376 goto ipc_err;
377 }
378
379
380 ret = snd_sof_load_firmware(sdev);
381 if (ret < 0) {
382 dev_err(sdev->dev, "error: failed to load DSP firmware %d\n",
383 ret);
384 goto fw_load_err;
385 }
386
387 sdev->fw_state = SOF_FW_BOOT_IN_PROGRESS;
388
389
390
391
392
393 ret = snd_sof_run_firmware(sdev);
394 if (ret < 0) {
395 dev_err(sdev->dev, "error: failed to boot DSP firmware %d\n",
396 ret);
397 goto fw_run_err;
398 }
399
400
401 ret = snd_sof_init_trace(sdev);
402 if (ret < 0) {
403
404 dev_warn(sdev->dev,
405 "warning: failed to initialize trace %d\n", ret);
406 }
407
408
409 sdev->first_boot = false;
410
411
412 ret = devm_snd_soc_register_component(sdev->dev, &sdev->plat_drv,
413 sof_ops(sdev)->drv,
414 sof_ops(sdev)->num_drv);
415 if (ret < 0) {
416 dev_err(sdev->dev,
417 "error: failed to register DSP DAI driver %d\n", ret);
418 goto fw_trace_err;
419 }
420
421 drv_name = plat_data->machine->drv_name;
422 mach = (const void *)plat_data->machine;
423 size = sizeof(*plat_data->machine);
424
425
426 plat_data->pdev_mach =
427 platform_device_register_data(sdev->dev, drv_name,
428 PLATFORM_DEVID_NONE, mach, size);
429
430 if (IS_ERR(plat_data->pdev_mach)) {
431 ret = PTR_ERR(plat_data->pdev_mach);
432 goto fw_trace_err;
433 }
434
435 dev_dbg(sdev->dev, "created machine %s\n",
436 dev_name(&plat_data->pdev_mach->dev));
437
438 if (plat_data->sof_probe_complete)
439 plat_data->sof_probe_complete(sdev->dev);
440
441 return 0;
442
443 fw_trace_err:
444 snd_sof_free_trace(sdev);
445 fw_run_err:
446 snd_sof_fw_unload(sdev);
447 fw_load_err:
448 snd_sof_ipc_free(sdev);
449 ipc_err:
450 snd_sof_free_debug(sdev);
451 dbg_err:
452 snd_sof_remove(sdev);
453
454
455 sdev->fw_state = SOF_FW_BOOT_NOT_STARTED;
456 sdev->first_boot = true;
457
458 return ret;
459 }
460
461 static void sof_probe_work(struct work_struct *work)
462 {
463 struct snd_sof_dev *sdev =
464 container_of(work, struct snd_sof_dev, probe_work);
465 int ret;
466
467 ret = sof_probe_continue(sdev);
468 if (ret < 0) {
469
470 dev_err(sdev->dev, "error: %s failed err: %d\n", __func__, ret);
471 }
472 }
473
474 int snd_sof_device_probe(struct device *dev, struct snd_sof_pdata *plat_data)
475 {
476 struct snd_sof_dev *sdev;
477
478 sdev = devm_kzalloc(dev, sizeof(*sdev), GFP_KERNEL);
479 if (!sdev)
480 return -ENOMEM;
481
482
483 sdev->dev = dev;
484
485 sdev->pdata = plat_data;
486 sdev->first_boot = true;
487 sdev->fw_state = SOF_FW_BOOT_NOT_STARTED;
488 dev_set_drvdata(dev, sdev);
489
490
491 if (!sof_ops(sdev) || !sof_ops(sdev)->probe || !sof_ops(sdev)->run ||
492 !sof_ops(sdev)->block_read || !sof_ops(sdev)->block_write ||
493 !sof_ops(sdev)->send_msg || !sof_ops(sdev)->load_firmware ||
494 !sof_ops(sdev)->ipc_msg_data || !sof_ops(sdev)->ipc_pcm_params)
495 return -EINVAL;
496
497 INIT_LIST_HEAD(&sdev->pcm_list);
498 INIT_LIST_HEAD(&sdev->kcontrol_list);
499 INIT_LIST_HEAD(&sdev->widget_list);
500 INIT_LIST_HEAD(&sdev->dai_list);
501 INIT_LIST_HEAD(&sdev->route_list);
502 spin_lock_init(&sdev->ipc_lock);
503 spin_lock_init(&sdev->hw_lock);
504
505 if (IS_ENABLED(CONFIG_SND_SOC_SOF_PROBE_WORK_QUEUE))
506 INIT_WORK(&sdev->probe_work, sof_probe_work);
507
508
509 if (plat_data->desc->ipc_timeout == 0)
510 sdev->ipc_timeout = TIMEOUT_DEFAULT_IPC_MS;
511 else
512 sdev->ipc_timeout = plat_data->desc->ipc_timeout;
513 if (plat_data->desc->boot_timeout == 0)
514 sdev->boot_timeout = TIMEOUT_DEFAULT_BOOT_MS;
515 else
516 sdev->boot_timeout = plat_data->desc->boot_timeout;
517
518 if (IS_ENABLED(CONFIG_SND_SOC_SOF_PROBE_WORK_QUEUE)) {
519 schedule_work(&sdev->probe_work);
520 return 0;
521 }
522
523 return sof_probe_continue(sdev);
524 }
525 EXPORT_SYMBOL(snd_sof_device_probe);
526
527 int snd_sof_device_remove(struct device *dev)
528 {
529 struct snd_sof_dev *sdev = dev_get_drvdata(dev);
530 struct snd_sof_pdata *pdata = sdev->pdata;
531
532 if (IS_ENABLED(CONFIG_SND_SOC_SOF_PROBE_WORK_QUEUE))
533 cancel_work_sync(&sdev->probe_work);
534
535 if (sdev->fw_state > SOF_FW_BOOT_NOT_STARTED) {
536 snd_sof_fw_unload(sdev);
537 snd_sof_ipc_free(sdev);
538 snd_sof_free_debug(sdev);
539 snd_sof_free_trace(sdev);
540 }
541
542
543
544
545
546
547 if (!IS_ERR_OR_NULL(pdata->pdev_mach))
548 platform_device_unregister(pdata->pdev_mach);
549
550
551
552
553
554
555
556 if (sdev->fw_state > SOF_FW_BOOT_NOT_STARTED)
557 snd_sof_remove(sdev);
558
559
560 release_firmware(pdata->fw);
561 pdata->fw = NULL;
562
563 return 0;
564 }
565 EXPORT_SYMBOL(snd_sof_device_remove);
566
567 MODULE_AUTHOR("Liam Girdwood");
568 MODULE_DESCRIPTION("Sound Open Firmware (SOF) Core");
569 MODULE_LICENSE("Dual BSD/GPL");
570 MODULE_ALIAS("platform:sof-audio");