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