1
2
3
4
5
6
7
8
9
10#include <linux/clk.h>
11#include <linux/device.h>
12#include <linux/gpio.h>
13#include <linux/gpio/consumer.h>
14#include <linux/module.h>
15#include <linux/of.h>
16#include <linux/of_device.h>
17#include <linux/of_gpio.h>
18#include <linux/of_graph.h>
19#include <linux/platform_device.h>
20#include <linux/string.h>
21#include <sound/graph_card.h>
22
23#define DPCM_SELECTABLE 1
24
25static int graph_outdrv_event(struct snd_soc_dapm_widget *w,
26 struct snd_kcontrol *kcontrol,
27 int event)
28{
29 struct snd_soc_dapm_context *dapm = w->dapm;
30 struct asoc_simple_priv *priv = snd_soc_card_get_drvdata(dapm->card);
31
32 switch (event) {
33 case SND_SOC_DAPM_POST_PMU:
34 gpiod_set_value_cansleep(priv->pa_gpio, 1);
35 break;
36 case SND_SOC_DAPM_PRE_PMD:
37 gpiod_set_value_cansleep(priv->pa_gpio, 0);
38 break;
39 default:
40 return -EINVAL;
41 }
42
43 return 0;
44}
45
46static const struct snd_soc_dapm_widget graph_dapm_widgets[] = {
47 SND_SOC_DAPM_OUT_DRV_E("Amplifier", SND_SOC_NOPM,
48 0, 0, NULL, 0, graph_outdrv_event,
49 SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
50};
51
52static const struct snd_soc_ops graph_ops = {
53 .startup = asoc_simple_startup,
54 .shutdown = asoc_simple_shutdown,
55 .hw_params = asoc_simple_hw_params,
56};
57
58static int graph_get_dai_id(struct device_node *ep)
59{
60 struct device_node *node;
61 struct device_node *endpoint;
62 struct of_endpoint info;
63 int i, id;
64 const u32 *reg;
65 int ret;
66
67
68 ret = snd_soc_get_dai_id(ep);
69 if (ret != -ENOTSUPP)
70 return ret;
71
72
73 ret = of_graph_parse_endpoint(ep, &info);
74 if (ret == 0) {
75
76
77
78
79
80
81 if (of_get_property(ep, "reg", NULL))
82 return info.id;
83
84 node = of_get_parent(ep);
85 reg = of_get_property(node, "reg", NULL);
86 of_node_put(node);
87 if (reg)
88 return info.port;
89 }
90 node = of_graph_get_port_parent(ep);
91
92
93
94
95
96 i = 0;
97 id = -1;
98 for_each_endpoint_of_node(node, endpoint) {
99 if (endpoint == ep)
100 id = i;
101 i++;
102 }
103
104 of_node_put(node);
105
106 if (id < 0)
107 return -ENODEV;
108
109 return id;
110}
111
112static bool soc_component_is_pcm(struct snd_soc_dai_link_component *dlc)
113{
114 struct snd_soc_dai *dai = snd_soc_find_dai_with_mutex(dlc);
115
116 if (dai && (dai->component->driver->pcm_construct ||
117 dai->driver->pcm_new))
118 return true;
119
120 return false;
121}
122
123static int asoc_simple_parse_dai(struct device_node *ep,
124 struct snd_soc_dai_link_component *dlc,
125 int *is_single_link)
126{
127 struct device_node *node;
128 struct of_phandle_args args;
129 int ret;
130
131 if (!ep)
132 return 0;
133
134 node = of_graph_get_port_parent(ep);
135
136
137 args.np = node;
138 args.args[0] = graph_get_dai_id(ep);
139 args.args_count = (of_graph_get_endpoint_count(node) > 1);
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160 ret = snd_soc_get_dai_name(&args, &dlc->dai_name);
161 if (ret < 0)
162 return ret;
163
164 dlc->of_node = node;
165
166 if (is_single_link)
167 *is_single_link = of_graph_get_endpoint_count(node) == 1;
168
169 return 0;
170}
171
172static void graph_parse_convert(struct device *dev,
173 struct device_node *ep,
174 struct asoc_simple_data *adata)
175{
176 struct device_node *top = dev->of_node;
177 struct device_node *port = of_get_parent(ep);
178 struct device_node *ports = of_get_parent(port);
179 struct device_node *node = of_graph_get_port_parent(ep);
180
181 asoc_simple_parse_convert(top, NULL, adata);
182 if (of_node_name_eq(ports, "ports"))
183 asoc_simple_parse_convert(ports, NULL, adata);
184 asoc_simple_parse_convert(port, NULL, adata);
185 asoc_simple_parse_convert(ep, NULL, adata);
186
187 of_node_put(port);
188 of_node_put(ports);
189 of_node_put(node);
190}
191
192static void graph_parse_mclk_fs(struct device_node *top,
193 struct device_node *ep,
194 struct simple_dai_props *props)
195{
196 struct device_node *port = of_get_parent(ep);
197 struct device_node *ports = of_get_parent(port);
198
199 of_property_read_u32(top, "mclk-fs", &props->mclk_fs);
200 if (of_node_name_eq(ports, "ports"))
201 of_property_read_u32(ports, "mclk-fs", &props->mclk_fs);
202 of_property_read_u32(port, "mclk-fs", &props->mclk_fs);
203 of_property_read_u32(ep, "mclk-fs", &props->mclk_fs);
204
205 of_node_put(port);
206 of_node_put(ports);
207}
208
209static int graph_parse_node(struct asoc_simple_priv *priv,
210 struct device_node *ep,
211 struct link_info *li,
212 int *cpu)
213{
214 struct device *dev = simple_priv_to_dev(priv);
215 struct device_node *top = dev->of_node;
216 struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, li->link);
217 struct simple_dai_props *dai_props = simple_priv_to_props(priv, li->link);
218 struct snd_soc_dai_link_component *dlc;
219 struct asoc_simple_dai *dai;
220 int ret;
221
222 if (cpu) {
223 dlc = asoc_link_to_cpu(dai_link, 0);
224 dai = simple_props_to_dai_cpu(dai_props, 0);
225 } else {
226 dlc = asoc_link_to_codec(dai_link, 0);
227 dai = simple_props_to_dai_codec(dai_props, 0);
228 }
229
230 graph_parse_mclk_fs(top, ep, dai_props);
231
232 ret = asoc_simple_parse_dai(ep, dlc, cpu);
233 if (ret < 0)
234 return ret;
235
236 ret = asoc_simple_parse_tdm(ep, dai);
237 if (ret < 0)
238 return ret;
239
240 ret = asoc_simple_parse_clk(dev, ep, dai, dlc);
241 if (ret < 0)
242 return ret;
243
244 return 0;
245}
246
247static int graph_link_init(struct asoc_simple_priv *priv,
248 struct device_node *cpu_ep,
249 struct device_node *codec_ep,
250 struct link_info *li,
251 char *name)
252{
253 struct device *dev = simple_priv_to_dev(priv);
254 struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, li->link);
255 int ret;
256
257 ret = asoc_simple_parse_daifmt(dev, cpu_ep, codec_ep,
258 NULL, &dai_link->dai_fmt);
259 if (ret < 0)
260 return ret;
261
262 dai_link->init = asoc_simple_dai_init;
263 dai_link->ops = &graph_ops;
264 if (priv->ops)
265 dai_link->ops = priv->ops;
266
267 return asoc_simple_set_dailink_name(dev, dai_link, name);
268}
269
270static int graph_dai_link_of_dpcm(struct asoc_simple_priv *priv,
271 struct device_node *cpu_ep,
272 struct device_node *codec_ep,
273 struct link_info *li)
274{
275 struct device *dev = simple_priv_to_dev(priv);
276 struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, li->link);
277 struct simple_dai_props *dai_props = simple_priv_to_props(priv, li->link);
278 struct device_node *top = dev->of_node;
279 struct device_node *ep = li->cpu ? cpu_ep : codec_ep;
280 char dai_name[64];
281 int ret;
282
283 dev_dbg(dev, "link_of DPCM (%pOF)\n", ep);
284
285 if (li->cpu) {
286 struct snd_soc_card *card = simple_priv_to_card(priv);
287 struct snd_soc_dai_link_component *cpus = asoc_link_to_cpu(dai_link, 0);
288 int is_single_links = 0;
289
290
291
292
293 dai_link->dynamic = 1;
294 dai_link->dpcm_merged_format = 1;
295
296 ret = graph_parse_node(priv, cpu_ep, li, &is_single_links);
297 if (ret)
298 return ret;
299
300 snprintf(dai_name, sizeof(dai_name),
301 "fe.%pOFP.%s", cpus->of_node, cpus->dai_name);
302
303
304
305
306
307
308
309
310
311
312 if (card->component_chaining && !soc_component_is_pcm(cpus))
313 dai_link->no_pcm = 1;
314
315 asoc_simple_canonicalize_cpu(cpus, is_single_links);
316 } else {
317 struct snd_soc_codec_conf *cconf = simple_props_to_codec_conf(dai_props, 0);
318 struct snd_soc_dai_link_component *codecs = asoc_link_to_codec(dai_link, 0);
319 struct device_node *port;
320 struct device_node *ports;
321
322
323
324
325 dai_link->no_pcm = 1;
326 dai_link->be_hw_params_fixup = asoc_simple_be_hw_params_fixup;
327
328 ret = graph_parse_node(priv, codec_ep, li, NULL);
329 if (ret < 0)
330 return ret;
331
332 snprintf(dai_name, sizeof(dai_name),
333 "be.%pOFP.%s", codecs->of_node, codecs->dai_name);
334
335
336 port = of_get_parent(ep);
337 ports = of_get_parent(port);
338 snd_soc_of_parse_node_prefix(top, cconf, codecs->of_node,
339 "prefix");
340 if (of_node_name_eq(ports, "ports"))
341 snd_soc_of_parse_node_prefix(ports, cconf, codecs->of_node, "prefix");
342 snd_soc_of_parse_node_prefix(port, cconf, codecs->of_node,
343 "prefix");
344
345 of_node_put(ports);
346 of_node_put(port);
347 }
348
349 graph_parse_convert(dev, ep, &dai_props->adata);
350
351 snd_soc_dai_link_set_capabilities(dai_link);
352
353 ret = graph_link_init(priv, cpu_ep, codec_ep, li, dai_name);
354
355 li->link++;
356
357 return ret;
358}
359
360static int graph_dai_link_of(struct asoc_simple_priv *priv,
361 struct device_node *cpu_ep,
362 struct device_node *codec_ep,
363 struct link_info *li)
364{
365 struct device *dev = simple_priv_to_dev(priv);
366 struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, li->link);
367 struct snd_soc_dai_link_component *cpus = asoc_link_to_cpu(dai_link, 0);
368 struct snd_soc_dai_link_component *codecs = asoc_link_to_codec(dai_link, 0);
369 char dai_name[64];
370 int ret, is_single_links = 0;
371
372 dev_dbg(dev, "link_of (%pOF)\n", cpu_ep);
373
374 ret = graph_parse_node(priv, cpu_ep, li, &is_single_links);
375 if (ret < 0)
376 return ret;
377
378 ret = graph_parse_node(priv, codec_ep, li, NULL);
379 if (ret < 0)
380 return ret;
381
382 snprintf(dai_name, sizeof(dai_name),
383 "%s-%s", cpus->dai_name, codecs->dai_name);
384
385 asoc_simple_canonicalize_cpu(cpus, is_single_links);
386
387 ret = graph_link_init(priv, cpu_ep, codec_ep, li, dai_name);
388 if (ret < 0)
389 return ret;
390
391 li->link++;
392
393 return 0;
394}
395
396static inline bool parse_as_dpcm_link(struct asoc_simple_priv *priv,
397 struct device_node *codec_port,
398 struct asoc_simple_data *adata)
399{
400 if (priv->force_dpcm)
401 return true;
402
403 if (!priv->dpcm_selectable)
404 return false;
405
406
407
408
409
410
411 if ((of_get_child_count(codec_port) > 1) ||
412 (adata->convert_rate || adata->convert_channels))
413 return true;
414
415 return false;
416}
417
418static int __graph_for_each_link(struct asoc_simple_priv *priv,
419 struct link_info *li,
420 int (*func_noml)(struct asoc_simple_priv *priv,
421 struct device_node *cpu_ep,
422 struct device_node *codec_ep,
423 struct link_info *li),
424 int (*func_dpcm)(struct asoc_simple_priv *priv,
425 struct device_node *cpu_ep,
426 struct device_node *codec_ep,
427 struct link_info *li))
428{
429 struct of_phandle_iterator it;
430 struct device *dev = simple_priv_to_dev(priv);
431 struct device_node *node = dev->of_node;
432 struct device_node *cpu_port;
433 struct device_node *cpu_ep;
434 struct device_node *codec_ep;
435 struct device_node *codec_port;
436 struct device_node *codec_port_old = NULL;
437 struct asoc_simple_data adata;
438 int rc, ret = 0;
439
440
441 of_for_each_phandle(&it, rc, node, "dais", NULL, 0) {
442 cpu_port = it.node;
443 cpu_ep = NULL;
444
445
446 while (1) {
447 cpu_ep = of_get_next_child(cpu_port, cpu_ep);
448 if (!cpu_ep)
449 break;
450
451
452 codec_ep = of_graph_get_remote_endpoint(cpu_ep);
453 codec_port = of_get_parent(codec_ep);
454
455
456 memset(&adata, 0, sizeof(adata));
457 graph_parse_convert(dev, codec_ep, &adata);
458 graph_parse_convert(dev, cpu_ep, &adata);
459
460
461 if (parse_as_dpcm_link(priv, codec_port, &adata)) {
462
463
464
465
466
467
468 if (li->cpu ||
469 ((codec_port_old != codec_port) && codec_ep))
470 ret = func_dpcm(priv, cpu_ep, codec_ep, li);
471
472 } else {
473 if (li->cpu)
474 ret = func_noml(priv, cpu_ep, codec_ep, li);
475 }
476
477 of_node_put(codec_ep);
478 of_node_put(codec_port);
479
480 if (ret < 0)
481 return ret;
482
483 codec_port_old = codec_port;
484 }
485 }
486
487 return 0;
488}
489
490static int graph_for_each_link(struct asoc_simple_priv *priv,
491 struct link_info *li,
492 int (*func_noml)(struct asoc_simple_priv *priv,
493 struct device_node *cpu_ep,
494 struct device_node *codec_ep,
495 struct link_info *li),
496 int (*func_dpcm)(struct asoc_simple_priv *priv,
497 struct device_node *cpu_ep,
498 struct device_node *codec_ep,
499 struct link_info *li))
500{
501 int ret;
502
503
504
505
506
507
508
509
510
511
512
513
514 for (li->cpu = 1; li->cpu >= 0; li->cpu--) {
515 ret = __graph_for_each_link(priv, li, func_noml, func_dpcm);
516 if (ret < 0)
517 break;
518 }
519
520 return ret;
521}
522
523static int graph_get_dais_count(struct asoc_simple_priv *priv,
524 struct link_info *li);
525
526int audio_graph_parse_of(struct asoc_simple_priv *priv, struct device *dev)
527{
528 struct snd_soc_card *card = simple_priv_to_card(priv);
529 struct link_info *li;
530 int ret;
531
532 li = devm_kzalloc(dev, sizeof(*li), GFP_KERNEL);
533 if (!li)
534 return -ENOMEM;
535
536 card->owner = THIS_MODULE;
537 card->dev = dev;
538
539 ret = graph_get_dais_count(priv, li);
540 if (ret < 0)
541 return ret;
542
543 if (!li->link)
544 return -EINVAL;
545
546 ret = asoc_simple_init_priv(priv, li);
547 if (ret < 0)
548 return ret;
549
550 priv->pa_gpio = devm_gpiod_get_optional(dev, "pa", GPIOD_OUT_LOW);
551 if (IS_ERR(priv->pa_gpio)) {
552 ret = PTR_ERR(priv->pa_gpio);
553 dev_err(dev, "failed to get amplifier gpio: %d\n", ret);
554 return ret;
555 }
556
557 ret = asoc_simple_parse_widgets(card, NULL);
558 if (ret < 0)
559 return ret;
560
561 ret = asoc_simple_parse_routing(card, NULL);
562 if (ret < 0)
563 return ret;
564
565 memset(li, 0, sizeof(*li));
566 ret = graph_for_each_link(priv, li,
567 graph_dai_link_of,
568 graph_dai_link_of_dpcm);
569 if (ret < 0)
570 goto err;
571
572 ret = asoc_simple_parse_card_name(card, NULL);
573 if (ret < 0)
574 goto err;
575
576 snd_soc_card_set_drvdata(card, priv);
577
578 asoc_simple_debug_info(priv);
579
580 ret = devm_snd_soc_register_card(dev, card);
581 if (ret < 0)
582 goto err;
583
584 devm_kfree(dev, li);
585 return 0;
586
587err:
588 asoc_simple_clean_reference(card);
589
590 if (ret != -EPROBE_DEFER)
591 dev_err(dev, "parse error %d\n", ret);
592
593 return ret;
594}
595EXPORT_SYMBOL_GPL(audio_graph_parse_of);
596
597static int graph_count_noml(struct asoc_simple_priv *priv,
598 struct device_node *cpu_ep,
599 struct device_node *codec_ep,
600 struct link_info *li)
601{
602 struct device *dev = simple_priv_to_dev(priv);
603
604 if (li->link >= SNDRV_MAX_LINKS) {
605 dev_err(dev, "too many links\n");
606 return -EINVAL;
607 }
608
609 li->num[li->link].cpus = 1;
610 li->num[li->link].codecs = 1;
611
612 li->link += 1;
613
614 dev_dbg(dev, "Count As Normal\n");
615
616 return 0;
617}
618
619static int graph_count_dpcm(struct asoc_simple_priv *priv,
620 struct device_node *cpu_ep,
621 struct device_node *codec_ep,
622 struct link_info *li)
623{
624 struct device *dev = simple_priv_to_dev(priv);
625
626 if (li->link >= SNDRV_MAX_LINKS) {
627 dev_err(dev, "too many links\n");
628 return -EINVAL;
629 }
630
631 if (li->cpu) {
632 li->num[li->link].cpus = 1;
633
634 li->link++;
635 } else {
636 li->num[li->link].codecs = 1;
637
638 li->link++;
639 }
640
641 dev_dbg(dev, "Count As DPCM\n");
642
643 return 0;
644}
645
646static int graph_get_dais_count(struct asoc_simple_priv *priv,
647 struct link_info *li)
648{
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695 return graph_for_each_link(priv, li,
696 graph_count_noml,
697 graph_count_dpcm);
698}
699
700static int graph_probe(struct platform_device *pdev)
701{
702 struct asoc_simple_priv *priv;
703 struct device *dev = &pdev->dev;
704 struct snd_soc_card *card;
705
706
707 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
708 if (!priv)
709 return -ENOMEM;
710
711 card = simple_priv_to_card(priv);
712 card->dapm_widgets = graph_dapm_widgets;
713 card->num_dapm_widgets = ARRAY_SIZE(graph_dapm_widgets);
714 card->probe = asoc_graph_card_probe;
715
716 if (of_device_get_match_data(dev))
717 priv->dpcm_selectable = 1;
718
719 return audio_graph_parse_of(priv, dev);
720}
721
722static const struct of_device_id graph_of_match[] = {
723 { .compatible = "audio-graph-card", },
724 { .compatible = "audio-graph-scu-card",
725 .data = (void *)DPCM_SELECTABLE },
726 {},
727};
728MODULE_DEVICE_TABLE(of, graph_of_match);
729
730static struct platform_driver graph_card = {
731 .driver = {
732 .name = "asoc-audio-graph-card",
733 .pm = &snd_soc_pm_ops,
734 .of_match_table = graph_of_match,
735 },
736 .probe = graph_probe,
737 .remove = asoc_simple_remove,
738};
739module_platform_driver(graph_card);
740
741MODULE_ALIAS("platform:asoc-audio-graph-card");
742MODULE_LICENSE("GPL v2");
743MODULE_DESCRIPTION("ASoC Audio Graph Sound Card");
744MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");
745