1 | // SPDX-License-Identifier: GPL-2.0 |
2 | // |
3 | // ASoC simple sound card support |
4 | // |
5 | // Copyright (C) 2012 Renesas Solutions Corp. |
6 | // Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> |
7 | |
8 | #include <linux/clk.h> |
9 | #include <linux/device.h> |
10 | #include <linux/module.h> |
11 | #include <linux/of.h> |
12 | #include <linux/of_platform.h> |
13 | #include <linux/platform_device.h> |
14 | #include <linux/string.h> |
15 | #include <sound/simple_card.h> |
16 | #include <sound/soc-dai.h> |
17 | #include <sound/soc.h> |
18 | |
19 | #define DPCM_SELECTABLE 1 |
20 | |
21 | #define DAI "sound-dai" |
22 | #define CELL "#sound-dai-cells" |
23 | #define PREFIX "simple-audio-card," |
24 | |
25 | static const struct snd_soc_ops simple_ops = { |
26 | .startup = simple_util_startup, |
27 | .shutdown = simple_util_shutdown, |
28 | .hw_params = simple_util_hw_params, |
29 | }; |
30 | |
31 | static int simple_parse_platform(struct device_node *node, struct snd_soc_dai_link_component *dlc) |
32 | { |
33 | struct of_phandle_args args; |
34 | int ret; |
35 | |
36 | if (!node) |
37 | return 0; |
38 | |
39 | /* |
40 | * Get node via "sound-dai = <&phandle port>" |
41 | * it will be used as xxx_of_node on soc_bind_dai_link() |
42 | */ |
43 | ret = of_parse_phandle_with_args(np: node, DAI, CELL, index: 0, out_args: &args); |
44 | if (ret) |
45 | return ret; |
46 | |
47 | /* dai_name is not required and may not exist for plat component */ |
48 | |
49 | dlc->of_node = args.np; |
50 | |
51 | return 0; |
52 | } |
53 | |
54 | static int simple_parse_dai(struct device *dev, |
55 | struct device_node *node, |
56 | struct snd_soc_dai_link_component *dlc, |
57 | int *is_single_link) |
58 | { |
59 | struct of_phandle_args args; |
60 | struct snd_soc_dai *dai; |
61 | int ret; |
62 | |
63 | if (!node) |
64 | return 0; |
65 | |
66 | /* |
67 | * Get node via "sound-dai = <&phandle port>" |
68 | * it will be used as xxx_of_node on soc_bind_dai_link() |
69 | */ |
70 | ret = of_parse_phandle_with_args(np: node, DAI, CELL, index: 0, out_args: &args); |
71 | if (ret) |
72 | return ret; |
73 | |
74 | /* |
75 | * Try to find from DAI args |
76 | */ |
77 | dai = snd_soc_get_dai_via_args(dai_args: &args); |
78 | if (dai) { |
79 | dlc->dai_name = snd_soc_dai_name_get(dai); |
80 | dlc->dai_args = snd_soc_copy_dai_args(dev, args: &args); |
81 | if (!dlc->dai_args) |
82 | return -ENOMEM; |
83 | |
84 | goto parse_dai_end; |
85 | } |
86 | |
87 | /* |
88 | * FIXME |
89 | * |
90 | * Here, dlc->dai_name is pointer to CPU/Codec DAI name. |
91 | * If user unbinded CPU or Codec driver, but not for Sound Card, |
92 | * dlc->dai_name is keeping unbinded CPU or Codec |
93 | * driver's pointer. |
94 | * |
95 | * If user re-bind CPU or Codec driver again, ALSA SoC will try |
96 | * to rebind Card via snd_soc_try_rebind_card(), but because of |
97 | * above reason, it might can't bind Sound Card. |
98 | * Because Sound Card is pointing to released dai_name pointer. |
99 | * |
100 | * To avoid this rebind Card issue, |
101 | * 1) It needs to alloc memory to keep dai_name eventhough |
102 | * CPU or Codec driver was unbinded, or |
103 | * 2) user need to rebind Sound Card everytime |
104 | * if he unbinded CPU or Codec. |
105 | */ |
106 | ret = snd_soc_get_dlc(args: &args, dlc); |
107 | if (ret < 0) |
108 | return ret; |
109 | |
110 | parse_dai_end: |
111 | if (is_single_link) |
112 | *is_single_link = !args.args_count; |
113 | |
114 | return 0; |
115 | } |
116 | |
117 | static void simple_parse_convert(struct device *dev, |
118 | struct device_node *np, |
119 | struct simple_util_data *adata) |
120 | { |
121 | struct device_node *top = dev->of_node; |
122 | struct device_node *node = of_get_parent(node: np); |
123 | |
124 | simple_util_parse_convert(np: top, PREFIX, data: adata); |
125 | simple_util_parse_convert(np: node, PREFIX, data: adata); |
126 | simple_util_parse_convert(np: node, NULL, data: adata); |
127 | simple_util_parse_convert(np, NULL, data: adata); |
128 | |
129 | of_node_put(node); |
130 | } |
131 | |
132 | static void simple_parse_mclk_fs(struct device_node *top, |
133 | struct device_node *np, |
134 | struct simple_dai_props *props, |
135 | char *prefix) |
136 | { |
137 | struct device_node *node = of_get_parent(node: np); |
138 | char prop[128]; |
139 | |
140 | snprintf(buf: prop, size: sizeof(prop), fmt: "%smclk-fs" , PREFIX); |
141 | of_property_read_u32(np: top, propname: prop, out_value: &props->mclk_fs); |
142 | |
143 | snprintf(buf: prop, size: sizeof(prop), fmt: "%smclk-fs" , prefix); |
144 | of_property_read_u32(np: node, propname: prop, out_value: &props->mclk_fs); |
145 | of_property_read_u32(np, propname: prop, out_value: &props->mclk_fs); |
146 | |
147 | of_node_put(node); |
148 | } |
149 | |
150 | static int simple_parse_node(struct simple_util_priv *priv, |
151 | struct device_node *np, |
152 | struct link_info *li, |
153 | char *prefix, |
154 | int *cpu) |
155 | { |
156 | struct device *dev = simple_priv_to_dev(priv); |
157 | struct device_node *top = dev->of_node; |
158 | struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, li->link); |
159 | struct simple_dai_props *dai_props = simple_priv_to_props(priv, li->link); |
160 | struct snd_soc_dai_link_component *dlc; |
161 | struct simple_util_dai *dai; |
162 | int ret; |
163 | |
164 | if (cpu) { |
165 | dlc = snd_soc_link_to_cpu(link: dai_link, n: 0); |
166 | dai = simple_props_to_dai_cpu(dai_props, 0); |
167 | } else { |
168 | dlc = snd_soc_link_to_codec(link: dai_link, n: 0); |
169 | dai = simple_props_to_dai_codec(dai_props, 0); |
170 | } |
171 | |
172 | simple_parse_mclk_fs(top, np, props: dai_props, prefix); |
173 | |
174 | ret = simple_parse_dai(dev, node: np, dlc, is_single_link: cpu); |
175 | if (ret) |
176 | return ret; |
177 | |
178 | ret = simple_util_parse_clk(dev, node: np, simple_dai: dai, dlc); |
179 | if (ret) |
180 | return ret; |
181 | |
182 | ret = simple_util_parse_tdm(np, dai); |
183 | if (ret) |
184 | return ret; |
185 | |
186 | return 0; |
187 | } |
188 | |
189 | static int simple_link_init(struct simple_util_priv *priv, |
190 | struct device_node *node, |
191 | struct device_node *codec, |
192 | struct link_info *li, |
193 | char *prefix, char *name) |
194 | { |
195 | struct device *dev = simple_priv_to_dev(priv); |
196 | struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, li->link); |
197 | int ret; |
198 | |
199 | ret = simple_util_parse_daifmt(dev, node, codec, |
200 | prefix, retfmt: &dai_link->dai_fmt); |
201 | if (ret < 0) |
202 | return 0; |
203 | |
204 | dai_link->init = simple_util_dai_init; |
205 | dai_link->ops = &simple_ops; |
206 | |
207 | return simple_util_set_dailink_name(dev, dai_link, fmt: name); |
208 | } |
209 | |
210 | static int simple_dai_link_of_dpcm(struct simple_util_priv *priv, |
211 | struct device_node *np, |
212 | struct device_node *codec, |
213 | struct link_info *li, |
214 | bool is_top) |
215 | { |
216 | struct device *dev = simple_priv_to_dev(priv); |
217 | struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, li->link); |
218 | struct simple_dai_props *dai_props = simple_priv_to_props(priv, li->link); |
219 | struct device_node *top = dev->of_node; |
220 | struct device_node *node = of_get_parent(node: np); |
221 | char *prefix = "" ; |
222 | char dai_name[64]; |
223 | int ret; |
224 | |
225 | dev_dbg(dev, "link_of DPCM (%pOF)\n" , np); |
226 | |
227 | /* For single DAI link & old style of DT node */ |
228 | if (is_top) |
229 | prefix = PREFIX; |
230 | |
231 | if (li->cpu) { |
232 | struct snd_soc_dai_link_component *cpus = snd_soc_link_to_cpu(link: dai_link, n: 0); |
233 | struct snd_soc_dai_link_component *platforms = snd_soc_link_to_platform(link: dai_link, n: 0); |
234 | int is_single_links = 0; |
235 | |
236 | /* Codec is dummy */ |
237 | |
238 | /* FE settings */ |
239 | dai_link->dynamic = 1; |
240 | dai_link->dpcm_merged_format = 1; |
241 | |
242 | ret = simple_parse_node(priv, np, li, prefix, cpu: &is_single_links); |
243 | if (ret < 0) |
244 | goto out_put_node; |
245 | |
246 | snprintf(buf: dai_name, size: sizeof(dai_name), fmt: "fe.%s" , cpus->dai_name); |
247 | |
248 | simple_util_canonicalize_cpu(cpus, is_single_links); |
249 | simple_util_canonicalize_platform(platforms, cpus); |
250 | } else { |
251 | struct snd_soc_dai_link_component *codecs = snd_soc_link_to_codec(link: dai_link, n: 0); |
252 | struct snd_soc_codec_conf *cconf; |
253 | |
254 | /* CPU is dummy */ |
255 | |
256 | /* BE settings */ |
257 | dai_link->no_pcm = 1; |
258 | dai_link->be_hw_params_fixup = simple_util_be_hw_params_fixup; |
259 | |
260 | cconf = simple_props_to_codec_conf(dai_props, 0); |
261 | |
262 | ret = simple_parse_node(priv, np, li, prefix, NULL); |
263 | if (ret < 0) |
264 | goto out_put_node; |
265 | |
266 | snprintf(buf: dai_name, size: sizeof(dai_name), fmt: "be.%s" , codecs->dai_name); |
267 | |
268 | /* check "prefix" from top node */ |
269 | snd_soc_of_parse_node_prefix(np: top, codec_conf: cconf, of_node: codecs->of_node, |
270 | PREFIX "prefix" ); |
271 | snd_soc_of_parse_node_prefix(np: node, codec_conf: cconf, of_node: codecs->of_node, |
272 | propname: "prefix" ); |
273 | snd_soc_of_parse_node_prefix(np, codec_conf: cconf, of_node: codecs->of_node, |
274 | propname: "prefix" ); |
275 | } |
276 | |
277 | simple_parse_convert(dev, np, adata: &dai_props->adata); |
278 | |
279 | snd_soc_dai_link_set_capabilities(dai_link); |
280 | |
281 | ret = simple_link_init(priv, node, codec, li, prefix, name: dai_name); |
282 | |
283 | out_put_node: |
284 | li->link++; |
285 | |
286 | of_node_put(node); |
287 | return ret; |
288 | } |
289 | |
290 | static int simple_dai_link_of(struct simple_util_priv *priv, |
291 | struct device_node *np, |
292 | struct device_node *codec, |
293 | struct link_info *li, |
294 | bool is_top) |
295 | { |
296 | struct device *dev = simple_priv_to_dev(priv); |
297 | struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, li->link); |
298 | struct snd_soc_dai_link_component *cpus = snd_soc_link_to_cpu(link: dai_link, n: 0); |
299 | struct snd_soc_dai_link_component *codecs = snd_soc_link_to_codec(link: dai_link, n: 0); |
300 | struct snd_soc_dai_link_component *platforms = snd_soc_link_to_platform(link: dai_link, n: 0); |
301 | struct device_node *cpu = NULL; |
302 | struct device_node *node = NULL; |
303 | struct device_node *plat = NULL; |
304 | char dai_name[64]; |
305 | char prop[128]; |
306 | char *prefix = "" ; |
307 | int ret, single_cpu = 0; |
308 | |
309 | cpu = np; |
310 | node = of_get_parent(node: np); |
311 | |
312 | dev_dbg(dev, "link_of (%pOF)\n" , node); |
313 | |
314 | /* For single DAI link & old style of DT node */ |
315 | if (is_top) |
316 | prefix = PREFIX; |
317 | |
318 | snprintf(buf: prop, size: sizeof(prop), fmt: "%splat" , prefix); |
319 | plat = of_get_child_by_name(node, name: prop); |
320 | |
321 | ret = simple_parse_node(priv, np: cpu, li, prefix, cpu: &single_cpu); |
322 | if (ret < 0) |
323 | goto dai_link_of_err; |
324 | |
325 | ret = simple_parse_node(priv, np: codec, li, prefix, NULL); |
326 | if (ret < 0) |
327 | goto dai_link_of_err; |
328 | |
329 | ret = simple_parse_platform(node: plat, dlc: platforms); |
330 | if (ret < 0) |
331 | goto dai_link_of_err; |
332 | |
333 | snprintf(buf: dai_name, size: sizeof(dai_name), |
334 | fmt: "%s-%s" , cpus->dai_name, codecs->dai_name); |
335 | |
336 | simple_util_canonicalize_cpu(cpus, is_single_links: single_cpu); |
337 | simple_util_canonicalize_platform(platforms, cpus); |
338 | |
339 | ret = simple_link_init(priv, node, codec, li, prefix, name: dai_name); |
340 | |
341 | dai_link_of_err: |
342 | of_node_put(node: plat); |
343 | of_node_put(node); |
344 | |
345 | li->link++; |
346 | |
347 | return ret; |
348 | } |
349 | |
350 | static int __simple_for_each_link(struct simple_util_priv *priv, |
351 | struct link_info *li, |
352 | int (*func_noml)(struct simple_util_priv *priv, |
353 | struct device_node *np, |
354 | struct device_node *codec, |
355 | struct link_info *li, bool is_top), |
356 | int (*func_dpcm)(struct simple_util_priv *priv, |
357 | struct device_node *np, |
358 | struct device_node *codec, |
359 | struct link_info *li, bool is_top)) |
360 | { |
361 | struct device *dev = simple_priv_to_dev(priv); |
362 | struct device_node *top = dev->of_node; |
363 | struct device_node *node; |
364 | struct device_node *add_devs; |
365 | uintptr_t dpcm_selectable = (uintptr_t)of_device_get_match_data(dev); |
366 | bool is_top = 0; |
367 | int ret = 0; |
368 | |
369 | /* Check if it has dai-link */ |
370 | node = of_get_child_by_name(node: top, PREFIX "dai-link" ); |
371 | if (!node) { |
372 | node = of_node_get(node: top); |
373 | is_top = 1; |
374 | } |
375 | |
376 | add_devs = of_get_child_by_name(node: top, PREFIX "additional-devs" ); |
377 | |
378 | /* loop for all dai-link */ |
379 | do { |
380 | struct simple_util_data adata; |
381 | struct device_node *codec; |
382 | struct device_node *plat; |
383 | struct device_node *np; |
384 | int num = of_get_child_count(np: node); |
385 | |
386 | /* Skip additional-devs node */ |
387 | if (node == add_devs) { |
388 | node = of_get_next_child(node: top, prev: node); |
389 | continue; |
390 | } |
391 | |
392 | /* get codec */ |
393 | codec = of_get_child_by_name(node, name: is_top ? |
394 | PREFIX "codec" : "codec" ); |
395 | if (!codec) { |
396 | ret = -ENODEV; |
397 | goto error; |
398 | } |
399 | /* get platform */ |
400 | plat = of_get_child_by_name(node, name: is_top ? |
401 | PREFIX "plat" : "plat" ); |
402 | |
403 | /* get convert-xxx property */ |
404 | memset(&adata, 0, sizeof(adata)); |
405 | for_each_child_of_node(node, np) { |
406 | if (np == add_devs) |
407 | continue; |
408 | simple_parse_convert(dev, np, adata: &adata); |
409 | } |
410 | |
411 | /* loop for all CPU/Codec node */ |
412 | for_each_child_of_node(node, np) { |
413 | if (plat == np || add_devs == np) |
414 | continue; |
415 | /* |
416 | * It is DPCM |
417 | * if it has many CPUs, |
418 | * or has convert-xxx property |
419 | */ |
420 | if (dpcm_selectable && |
421 | (num > 2 || simple_util_is_convert_required(data: &adata))) { |
422 | /* |
423 | * np |
424 | * |1(CPU)|0(Codec) li->cpu |
425 | * CPU |Pass |return |
426 | * Codec |return|Pass |
427 | */ |
428 | if (li->cpu != (np == codec)) |
429 | ret = func_dpcm(priv, np, codec, li, is_top); |
430 | /* else normal sound */ |
431 | } else { |
432 | /* |
433 | * np |
434 | * |1(CPU)|0(Codec) li->cpu |
435 | * CPU |Pass |return |
436 | * Codec |return|return |
437 | */ |
438 | if (li->cpu && (np != codec)) |
439 | ret = func_noml(priv, np, codec, li, is_top); |
440 | } |
441 | |
442 | if (ret < 0) { |
443 | of_node_put(node: codec); |
444 | of_node_put(node: plat); |
445 | of_node_put(node: np); |
446 | goto error; |
447 | } |
448 | } |
449 | |
450 | of_node_put(node: codec); |
451 | of_node_put(node: plat); |
452 | node = of_get_next_child(node: top, prev: node); |
453 | } while (!is_top && node); |
454 | |
455 | error: |
456 | of_node_put(node: add_devs); |
457 | of_node_put(node); |
458 | return ret; |
459 | } |
460 | |
461 | static int simple_for_each_link(struct simple_util_priv *priv, |
462 | struct link_info *li, |
463 | int (*func_noml)(struct simple_util_priv *priv, |
464 | struct device_node *np, |
465 | struct device_node *codec, |
466 | struct link_info *li, bool is_top), |
467 | int (*func_dpcm)(struct simple_util_priv *priv, |
468 | struct device_node *np, |
469 | struct device_node *codec, |
470 | struct link_info *li, bool is_top)) |
471 | { |
472 | int ret; |
473 | /* |
474 | * Detect all CPU first, and Detect all Codec 2nd. |
475 | * |
476 | * In Normal sound case, all DAIs are detected |
477 | * as "CPU-Codec". |
478 | * |
479 | * In DPCM sound case, |
480 | * all CPUs are detected as "CPU-dummy", and |
481 | * all Codecs are detected as "dummy-Codec". |
482 | * To avoid random sub-device numbering, |
483 | * detect "dummy-Codec" in last; |
484 | */ |
485 | for (li->cpu = 1; li->cpu >= 0; li->cpu--) { |
486 | ret = __simple_for_each_link(priv, li, func_noml, func_dpcm); |
487 | if (ret < 0) |
488 | break; |
489 | } |
490 | |
491 | return ret; |
492 | } |
493 | |
494 | static void simple_depopulate_aux(void *data) |
495 | { |
496 | struct simple_util_priv *priv = data; |
497 | |
498 | of_platform_depopulate(simple_priv_to_dev(priv)); |
499 | } |
500 | |
501 | static int simple_populate_aux(struct simple_util_priv *priv) |
502 | { |
503 | struct device *dev = simple_priv_to_dev(priv); |
504 | struct device_node *node; |
505 | int ret; |
506 | |
507 | node = of_get_child_by_name(node: dev->of_node, PREFIX "additional-devs" ); |
508 | if (!node) |
509 | return 0; |
510 | |
511 | ret = of_platform_populate(root: node, NULL, NULL, parent: dev); |
512 | of_node_put(node); |
513 | if (ret) |
514 | return ret; |
515 | |
516 | return devm_add_action_or_reset(dev, simple_depopulate_aux, priv); |
517 | } |
518 | |
519 | static int simple_parse_of(struct simple_util_priv *priv, struct link_info *li) |
520 | { |
521 | struct snd_soc_card *card = simple_priv_to_card(priv); |
522 | int ret; |
523 | |
524 | ret = simple_util_parse_widgets(card, PREFIX); |
525 | if (ret < 0) |
526 | return ret; |
527 | |
528 | ret = simple_util_parse_routing(card, PREFIX); |
529 | if (ret < 0) |
530 | return ret; |
531 | |
532 | ret = simple_util_parse_pin_switches(card, PREFIX); |
533 | if (ret < 0) |
534 | return ret; |
535 | |
536 | /* Single/Muti DAI link(s) & New style of DT node */ |
537 | memset(li, 0, sizeof(*li)); |
538 | ret = simple_for_each_link(priv, li, |
539 | func_noml: simple_dai_link_of, |
540 | func_dpcm: simple_dai_link_of_dpcm); |
541 | if (ret < 0) |
542 | return ret; |
543 | |
544 | ret = simple_util_parse_card_name(card, PREFIX); |
545 | if (ret < 0) |
546 | return ret; |
547 | |
548 | ret = simple_populate_aux(priv); |
549 | if (ret < 0) |
550 | return ret; |
551 | |
552 | ret = snd_soc_of_parse_aux_devs(card, PREFIX "aux-devs" ); |
553 | |
554 | return ret; |
555 | } |
556 | |
557 | static int simple_count_noml(struct simple_util_priv *priv, |
558 | struct device_node *np, |
559 | struct device_node *codec, |
560 | struct link_info *li, bool is_top) |
561 | { |
562 | if (li->link >= SNDRV_MAX_LINKS) { |
563 | struct device *dev = simple_priv_to_dev(priv); |
564 | |
565 | dev_err(dev, "too many links\n" ); |
566 | return -EINVAL; |
567 | } |
568 | |
569 | /* |
570 | * DON'T REMOVE platforms |
571 | * |
572 | * Some CPU might be using soc-generic-dmaengine-pcm. This means CPU and Platform |
573 | * are different Component, but are sharing same component->dev. |
574 | * Simple Card had been supported it without special Platform selection. |
575 | * We need platforms here. |
576 | * |
577 | * In case of no Platform, it will be Platform == CPU, but Platform will be |
578 | * ignored by snd_soc_rtd_add_component(). |
579 | * |
580 | * see |
581 | * simple-card-utils.c :: simple_util_canonicalize_platform() |
582 | */ |
583 | li->num[li->link].cpus = 1; |
584 | li->num[li->link].platforms = 1; |
585 | |
586 | li->num[li->link].codecs = 1; |
587 | |
588 | li->link += 1; |
589 | |
590 | return 0; |
591 | } |
592 | |
593 | static int simple_count_dpcm(struct simple_util_priv *priv, |
594 | struct device_node *np, |
595 | struct device_node *codec, |
596 | struct link_info *li, bool is_top) |
597 | { |
598 | if (li->link >= SNDRV_MAX_LINKS) { |
599 | struct device *dev = simple_priv_to_dev(priv); |
600 | |
601 | dev_err(dev, "too many links\n" ); |
602 | return -EINVAL; |
603 | } |
604 | |
605 | if (li->cpu) { |
606 | /* |
607 | * DON'T REMOVE platforms |
608 | * see |
609 | * simple_count_noml() |
610 | */ |
611 | li->num[li->link].cpus = 1; |
612 | li->num[li->link].platforms = 1; |
613 | |
614 | li->link++; /* CPU-dummy */ |
615 | } else { |
616 | li->num[li->link].codecs = 1; |
617 | |
618 | li->link++; /* dummy-Codec */ |
619 | } |
620 | |
621 | return 0; |
622 | } |
623 | |
624 | static int simple_get_dais_count(struct simple_util_priv *priv, |
625 | struct link_info *li) |
626 | { |
627 | struct device *dev = simple_priv_to_dev(priv); |
628 | struct device_node *top = dev->of_node; |
629 | |
630 | /* |
631 | * link_num : number of links. |
632 | * CPU-Codec / CPU-dummy / dummy-Codec |
633 | * dais_num : number of DAIs |
634 | * ccnf_num : number of codec_conf |
635 | * same number for "dummy-Codec" |
636 | * |
637 | * ex1) |
638 | * CPU0 --- Codec0 link : 5 |
639 | * CPU1 --- Codec1 dais : 7 |
640 | * CPU2 -/ ccnf : 1 |
641 | * CPU3 --- Codec2 |
642 | * |
643 | * => 5 links = 2xCPU-Codec + 2xCPU-dummy + 1xdummy-Codec |
644 | * => 7 DAIs = 4xCPU + 3xCodec |
645 | * => 1 ccnf = 1xdummy-Codec |
646 | * |
647 | * ex2) |
648 | * CPU0 --- Codec0 link : 5 |
649 | * CPU1 --- Codec1 dais : 6 |
650 | * CPU2 -/ ccnf : 1 |
651 | * CPU3 -/ |
652 | * |
653 | * => 5 links = 1xCPU-Codec + 3xCPU-dummy + 1xdummy-Codec |
654 | * => 6 DAIs = 4xCPU + 2xCodec |
655 | * => 1 ccnf = 1xdummy-Codec |
656 | * |
657 | * ex3) |
658 | * CPU0 --- Codec0 link : 6 |
659 | * CPU1 -/ dais : 6 |
660 | * CPU2 --- Codec1 ccnf : 2 |
661 | * CPU3 -/ |
662 | * |
663 | * => 6 links = 0xCPU-Codec + 4xCPU-dummy + 2xdummy-Codec |
664 | * => 6 DAIs = 4xCPU + 2xCodec |
665 | * => 2 ccnf = 2xdummy-Codec |
666 | * |
667 | * ex4) |
668 | * CPU0 --- Codec0 (convert-rate) link : 3 |
669 | * CPU1 --- Codec1 dais : 4 |
670 | * ccnf : 1 |
671 | * |
672 | * => 3 links = 1xCPU-Codec + 1xCPU-dummy + 1xdummy-Codec |
673 | * => 4 DAIs = 2xCPU + 2xCodec |
674 | * => 1 ccnf = 1xdummy-Codec |
675 | */ |
676 | if (!top) { |
677 | li->num[0].cpus = 1; |
678 | li->num[0].codecs = 1; |
679 | li->num[0].platforms = 1; |
680 | |
681 | li->link = 1; |
682 | return 0; |
683 | } |
684 | |
685 | return simple_for_each_link(priv, li, |
686 | func_noml: simple_count_noml, |
687 | func_dpcm: simple_count_dpcm); |
688 | } |
689 | |
690 | static int simple_soc_probe(struct snd_soc_card *card) |
691 | { |
692 | struct simple_util_priv *priv = snd_soc_card_get_drvdata(card); |
693 | int ret; |
694 | |
695 | ret = simple_util_init_hp(card, &priv->hp_jack, PREFIX); |
696 | if (ret < 0) |
697 | return ret; |
698 | |
699 | ret = simple_util_init_mic(card, &priv->mic_jack, PREFIX); |
700 | if (ret < 0) |
701 | return ret; |
702 | |
703 | ret = simple_util_init_aux_jacks(priv, PREFIX); |
704 | if (ret < 0) |
705 | return ret; |
706 | |
707 | return 0; |
708 | } |
709 | |
710 | static int simple_probe(struct platform_device *pdev) |
711 | { |
712 | struct simple_util_priv *priv; |
713 | struct device *dev = &pdev->dev; |
714 | struct device_node *np = dev->of_node; |
715 | struct snd_soc_card *card; |
716 | struct link_info *li; |
717 | int ret; |
718 | |
719 | /* Allocate the private data and the DAI link array */ |
720 | priv = devm_kzalloc(dev, size: sizeof(*priv), GFP_KERNEL); |
721 | if (!priv) |
722 | return -ENOMEM; |
723 | |
724 | card = simple_priv_to_card(priv); |
725 | card->owner = THIS_MODULE; |
726 | card->dev = dev; |
727 | card->probe = simple_soc_probe; |
728 | card->driver_name = "simple-card" ; |
729 | |
730 | li = devm_kzalloc(dev, size: sizeof(*li), GFP_KERNEL); |
731 | if (!li) |
732 | return -ENOMEM; |
733 | |
734 | ret = simple_get_dais_count(priv, li); |
735 | if (ret < 0) |
736 | return ret; |
737 | |
738 | if (!li->link) |
739 | return -EINVAL; |
740 | |
741 | ret = simple_util_init_priv(priv, li); |
742 | if (ret < 0) |
743 | return ret; |
744 | |
745 | if (np && of_device_is_available(device: np)) { |
746 | |
747 | ret = simple_parse_of(priv, li); |
748 | if (ret < 0) { |
749 | dev_err_probe(dev, err: ret, fmt: "parse error\n" ); |
750 | goto err; |
751 | } |
752 | |
753 | } else { |
754 | struct simple_util_info *cinfo; |
755 | struct snd_soc_dai_link_component *cpus; |
756 | struct snd_soc_dai_link_component *codecs; |
757 | struct snd_soc_dai_link_component *platform; |
758 | struct snd_soc_dai_link *dai_link = priv->dai_link; |
759 | struct simple_dai_props *dai_props = priv->dai_props; |
760 | |
761 | ret = -EINVAL; |
762 | |
763 | cinfo = dev->platform_data; |
764 | if (!cinfo) { |
765 | dev_err(dev, "no info for asoc-simple-card\n" ); |
766 | goto err; |
767 | } |
768 | |
769 | if (!cinfo->name || |
770 | !cinfo->codec_dai.name || |
771 | !cinfo->codec || |
772 | !cinfo->platform || |
773 | !cinfo->cpu_dai.name) { |
774 | dev_err(dev, "insufficient simple_util_info settings\n" ); |
775 | goto err; |
776 | } |
777 | |
778 | cpus = dai_link->cpus; |
779 | cpus->dai_name = cinfo->cpu_dai.name; |
780 | |
781 | codecs = dai_link->codecs; |
782 | codecs->name = cinfo->codec; |
783 | codecs->dai_name = cinfo->codec_dai.name; |
784 | |
785 | platform = dai_link->platforms; |
786 | platform->name = cinfo->platform; |
787 | |
788 | card->name = (cinfo->card) ? cinfo->card : cinfo->name; |
789 | dai_link->name = cinfo->name; |
790 | dai_link->stream_name = cinfo->name; |
791 | dai_link->dai_fmt = cinfo->daifmt; |
792 | dai_link->init = simple_util_dai_init; |
793 | memcpy(dai_props->cpu_dai, &cinfo->cpu_dai, |
794 | sizeof(*dai_props->cpu_dai)); |
795 | memcpy(dai_props->codec_dai, &cinfo->codec_dai, |
796 | sizeof(*dai_props->codec_dai)); |
797 | } |
798 | |
799 | snd_soc_card_set_drvdata(card, data: priv); |
800 | |
801 | simple_util_debug_info(priv); |
802 | |
803 | ret = devm_snd_soc_register_card(dev, card); |
804 | if (ret < 0) |
805 | goto err; |
806 | |
807 | devm_kfree(dev, p: li); |
808 | return 0; |
809 | err: |
810 | simple_util_clean_reference(card); |
811 | |
812 | return ret; |
813 | } |
814 | |
815 | static const struct of_device_id simple_of_match[] = { |
816 | { .compatible = "simple-audio-card" , }, |
817 | { .compatible = "simple-scu-audio-card" , |
818 | .data = (void *)DPCM_SELECTABLE }, |
819 | {}, |
820 | }; |
821 | MODULE_DEVICE_TABLE(of, simple_of_match); |
822 | |
823 | static struct platform_driver simple_card = { |
824 | .driver = { |
825 | .name = "asoc-simple-card" , |
826 | .pm = &snd_soc_pm_ops, |
827 | .of_match_table = simple_of_match, |
828 | }, |
829 | .probe = simple_probe, |
830 | .remove_new = simple_util_remove, |
831 | }; |
832 | |
833 | module_platform_driver(simple_card); |
834 | |
835 | MODULE_ALIAS("platform:asoc-simple-card" ); |
836 | MODULE_LICENSE("GPL v2" ); |
837 | MODULE_DESCRIPTION("ASoC Simple Sound Card" ); |
838 | MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>" ); |
839 | |