1 // SPDX-License-Identifier: GPL-2.0 2 // 3 // ASoC audio graph sound card support 4 // 5 // Copyright (C) 2016 Renesas Solutions Corp. 6 // Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> 7 // 8 // based on ${LINUX}/sound/soc/generic/simple-card.c 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 27 static 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 48 static 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 54 static 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 60 static 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 /* use driver specified DAI ID if exist */ 70 ret = snd_soc_get_dai_id(ep); 71 if (ret != -ENOTSUPP) 72 return ret; 73 74 /* use endpoint/port reg if exist */ 75 ret = of_graph_parse_endpoint(ep, &info); 76 if (ret == 0) { 77 /* 78 * Because it will count port/endpoint if it doesn't have "reg". 79 * But, we can't judge whether it has "no reg", or "reg = <0>" 80 * only of_graph_parse_endpoint(). 81 * We need to check "reg" property 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 * Non HDMI sound case, counting port/endpoint on its DT 96 * is enough. Let's count it. 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 114 static 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 /* Get dai->name */ 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 ret = snd_soc_get_dai_name(&args, &dlc->dai_name); 133 if (ret < 0) 134 return ret; 135 136 dlc->of_node = node; 137 138 if (is_single_link) 139 *is_single_link = of_graph_get_endpoint_count(node) == 1; 140 141 return 0; 142 } 143 144 static void graph_parse_convert(struct device *dev, 145 struct device_node *ep, 146 struct asoc_simple_data *adata) 147 { 148 struct device_node *top = dev->of_node; 149 struct device_node *port = of_get_parent(ep); 150 struct device_node *ports = of_get_parent(port); 151 struct device_node *node = of_graph_get_port_parent(ep); 152 153 asoc_simple_parse_convert(dev, top, NULL, adata); 154 asoc_simple_parse_convert(dev, node, PREFIX, adata); 155 asoc_simple_parse_convert(dev, ports, NULL, adata); 156 asoc_simple_parse_convert(dev, port, NULL, adata); 157 asoc_simple_parse_convert(dev, ep, NULL, adata); 158 159 of_node_put(port); 160 of_node_put(ports); 161 of_node_put(node); 162 } 163 164 static void graph_parse_mclk_fs(struct device_node *top, 165 struct device_node *ep, 166 struct simple_dai_props *props) 167 { 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 of_property_read_u32(top, "mclk-fs", &props->mclk_fs); 173 of_property_read_u32(ports, "mclk-fs", &props->mclk_fs); 174 of_property_read_u32(port, "mclk-fs", &props->mclk_fs); 175 of_property_read_u32(ep, "mclk-fs", &props->mclk_fs); 176 177 of_node_put(port); 178 of_node_put(ports); 179 of_node_put(node); 180 } 181 182 static int graph_dai_link_of_dpcm(struct asoc_simple_priv *priv, 183 struct device_node *cpu_ep, 184 struct device_node *codec_ep, 185 struct link_info *li, 186 int dup_codec) 187 { 188 struct device *dev = simple_priv_to_dev(priv); 189 struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, li->link); 190 struct simple_dai_props *dai_props = simple_priv_to_props(priv, li->link); 191 struct device_node *top = dev->of_node; 192 struct device_node *ep = li->cpu ? cpu_ep : codec_ep; 193 struct device_node *port; 194 struct device_node *ports; 195 struct device_node *node; 196 struct asoc_simple_dai *dai; 197 struct snd_soc_dai_link_component *cpus = dai_link->cpus; 198 struct snd_soc_dai_link_component *codecs = dai_link->codecs; 199 int ret; 200 201 /* Do it all CPU endpoint, and 1st Codec endpoint */ 202 if (!li->cpu && dup_codec) 203 return 0; 204 205 port = of_get_parent(ep); 206 ports = of_get_parent(port); 207 node = of_graph_get_port_parent(ep); 208 209 li->link++; 210 211 dev_dbg(dev, "link_of DPCM (%pOF)\n", ep); 212 213 if (li->cpu) { 214 int is_single_links = 0; 215 216 /* BE is dummy */ 217 codecs->of_node = NULL; 218 codecs->dai_name = "snd-soc-dummy-dai"; 219 codecs->name = "snd-soc-dummy"; 220 221 /* FE settings */ 222 dai_link->dynamic = 1; 223 dai_link->dpcm_merged_format = 1; 224 225 dai = 226 dai_props->cpu_dai = &priv->dais[li->dais++]; 227 228 ret = asoc_simple_parse_cpu(ep, dai_link, &is_single_links); 229 if (ret) 230 goto out_put_node; 231 232 ret = asoc_simple_parse_clk_cpu(dev, ep, dai_link, dai); 233 if (ret < 0) 234 goto out_put_node; 235 236 ret = asoc_simple_set_dailink_name(dev, dai_link, 237 "fe.%s", 238 cpus->dai_name); 239 if (ret < 0) 240 goto out_put_node; 241 242 /* card->num_links includes Codec */ 243 asoc_simple_canonicalize_cpu(dai_link, is_single_links); 244 } else { 245 struct snd_soc_codec_conf *cconf; 246 247 /* FE is dummy */ 248 cpus->of_node = NULL; 249 cpus->dai_name = "snd-soc-dummy-dai"; 250 cpus->name = "snd-soc-dummy"; 251 252 /* BE settings */ 253 dai_link->no_pcm = 1; 254 dai_link->be_hw_params_fixup = asoc_simple_be_hw_params_fixup; 255 256 dai = 257 dai_props->codec_dai = &priv->dais[li->dais++]; 258 259 cconf = 260 dai_props->codec_conf = &priv->codec_conf[li->conf++]; 261 262 ret = asoc_simple_parse_codec(ep, dai_link); 263 if (ret < 0) 264 goto out_put_node; 265 266 ret = asoc_simple_parse_clk_codec(dev, ep, dai_link, dai); 267 if (ret < 0) 268 goto out_put_node; 269 270 ret = asoc_simple_set_dailink_name(dev, dai_link, 271 "be.%s", 272 codecs->dai_name); 273 if (ret < 0) 274 goto out_put_node; 275 276 /* check "prefix" from top node */ 277 snd_soc_of_parse_node_prefix(top, cconf, codecs->of_node, 278 "prefix"); 279 snd_soc_of_parse_node_prefix(node, cconf, codecs->of_node, 280 PREFIX "prefix"); 281 snd_soc_of_parse_node_prefix(ports, cconf, codecs->of_node, 282 "prefix"); 283 snd_soc_of_parse_node_prefix(port, cconf, codecs->of_node, 284 "prefix"); 285 } 286 287 graph_parse_convert(dev, ep, &dai_props->adata); 288 graph_parse_mclk_fs(top, ep, dai_props); 289 290 asoc_simple_canonicalize_platform(dai_link); 291 292 ret = asoc_simple_parse_tdm(ep, dai); 293 if (ret) 294 goto out_put_node; 295 296 ret = asoc_simple_parse_daifmt(dev, cpu_ep, codec_ep, 297 NULL, &dai_link->dai_fmt); 298 if (ret < 0) 299 goto out_put_node; 300 301 dai_link->dpcm_playback = 1; 302 dai_link->dpcm_capture = 1; 303 dai_link->ops = &graph_ops; 304 dai_link->init = asoc_simple_dai_init; 305 306 out_put_node: 307 of_node_put(ports); 308 of_node_put(port); 309 of_node_put(node); 310 return ret; 311 } 312 313 static int graph_dai_link_of(struct asoc_simple_priv *priv, 314 struct device_node *cpu_ep, 315 struct device_node *codec_ep, 316 struct link_info *li) 317 { 318 struct device *dev = simple_priv_to_dev(priv); 319 struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, li->link); 320 struct simple_dai_props *dai_props = simple_priv_to_props(priv, li->link); 321 struct device_node *top = dev->of_node; 322 struct asoc_simple_dai *cpu_dai; 323 struct asoc_simple_dai *codec_dai; 324 int ret, single_cpu; 325 326 /* Do it only CPU turn */ 327 if (!li->cpu) 328 return 0; 329 330 dev_dbg(dev, "link_of (%pOF)\n", cpu_ep); 331 332 li->link++; 333 334 cpu_dai = 335 dai_props->cpu_dai = &priv->dais[li->dais++]; 336 codec_dai = 337 dai_props->codec_dai = &priv->dais[li->dais++]; 338 339 /* Factor to mclk, used in hw_params() */ 340 graph_parse_mclk_fs(top, cpu_ep, dai_props); 341 graph_parse_mclk_fs(top, codec_ep, dai_props); 342 343 ret = asoc_simple_parse_daifmt(dev, cpu_ep, codec_ep, 344 NULL, &dai_link->dai_fmt); 345 if (ret < 0) 346 return ret; 347 348 ret = asoc_simple_parse_cpu(cpu_ep, dai_link, &single_cpu); 349 if (ret < 0) 350 return ret; 351 352 ret = asoc_simple_parse_codec(codec_ep, dai_link); 353 if (ret < 0) 354 return ret; 355 356 ret = asoc_simple_parse_tdm(cpu_ep, cpu_dai); 357 if (ret < 0) 358 return ret; 359 360 ret = asoc_simple_parse_tdm(codec_ep, codec_dai); 361 if (ret < 0) 362 return ret; 363 364 ret = asoc_simple_parse_clk_cpu(dev, cpu_ep, dai_link, cpu_dai); 365 if (ret < 0) 366 return ret; 367 368 ret = asoc_simple_parse_clk_codec(dev, codec_ep, dai_link, codec_dai); 369 if (ret < 0) 370 return ret; 371 372 ret = asoc_simple_set_dailink_name(dev, dai_link, 373 "%s-%s", 374 dai_link->cpus->dai_name, 375 dai_link->codecs->dai_name); 376 if (ret < 0) 377 return ret; 378 379 dai_link->ops = &graph_ops; 380 dai_link->init = asoc_simple_dai_init; 381 382 asoc_simple_canonicalize_cpu(dai_link, single_cpu); 383 asoc_simple_canonicalize_platform(dai_link); 384 385 return 0; 386 } 387 388 static int graph_for_each_link(struct asoc_simple_priv *priv, 389 struct link_info *li, 390 int (*func_noml)(struct asoc_simple_priv *priv, 391 struct device_node *cpu_ep, 392 struct device_node *codec_ep, 393 struct link_info *li), 394 int (*func_dpcm)(struct asoc_simple_priv *priv, 395 struct device_node *cpu_ep, 396 struct device_node *codec_ep, 397 struct link_info *li, int dup_codec)) 398 { 399 struct of_phandle_iterator it; 400 struct device *dev = simple_priv_to_dev(priv); 401 struct device_node *node = dev->of_node; 402 struct device_node *cpu_port; 403 struct device_node *cpu_ep; 404 struct device_node *codec_ep; 405 struct device_node *codec_port; 406 struct device_node *codec_port_old = NULL; 407 struct asoc_simple_data adata; 408 uintptr_t dpcm_selectable = (uintptr_t)of_device_get_match_data(dev); 409 int rc, ret; 410 411 /* loop for all listed CPU port */ 412 of_for_each_phandle(&it, rc, node, "dais", NULL, 0) { 413 cpu_port = it.node; 414 cpu_ep = NULL; 415 416 /* loop for all CPU endpoint */ 417 while (1) { 418 cpu_ep = of_get_next_child(cpu_port, cpu_ep); 419 if (!cpu_ep) 420 break; 421 422 /* get codec */ 423 codec_ep = of_graph_get_remote_endpoint(cpu_ep); 424 codec_port = of_get_parent(codec_ep); 425 426 /* get convert-xxx property */ 427 memset(&adata, 0, sizeof(adata)); 428 graph_parse_convert(dev, codec_ep, &adata); 429 graph_parse_convert(dev, cpu_ep, &adata); 430 431 /* 432 * It is DPCM 433 * if Codec port has many endpoints, 434 * or has convert-xxx property 435 */ 436 if (dpcm_selectable && 437 ((of_get_child_count(codec_port) > 1) || 438 adata.convert_rate || adata.convert_channels)) 439 ret = func_dpcm(priv, cpu_ep, codec_ep, li, 440 (codec_port_old == codec_port)); 441 /* else normal sound */ 442 else 443 ret = func_noml(priv, cpu_ep, codec_ep, li); 444 445 of_node_put(codec_ep); 446 of_node_put(codec_port); 447 448 if (ret < 0) 449 return ret; 450 451 codec_port_old = codec_port; 452 } 453 } 454 455 return 0; 456 } 457 458 static int graph_parse_of(struct asoc_simple_priv *priv) 459 { 460 struct snd_soc_card *card = simple_priv_to_card(priv); 461 struct link_info li; 462 int ret; 463 464 ret = asoc_simple_parse_widgets(card, NULL); 465 if (ret < 0) 466 return ret; 467 468 ret = asoc_simple_parse_routing(card, NULL); 469 if (ret < 0) 470 return ret; 471 472 memset(&li, 0, sizeof(li)); 473 for (li.cpu = 1; li.cpu >= 0; li.cpu--) { 474 /* 475 * Detect all CPU first, and Detect all Codec 2nd. 476 * 477 * In Normal sound case, all DAIs are detected 478 * as "CPU-Codec". 479 * 480 * In DPCM sound case, 481 * all CPUs are detected as "CPU-dummy", and 482 * all Codecs are detected as "dummy-Codec". 483 * To avoid random sub-device numbering, 484 * detect "dummy-Codec" in last; 485 */ 486 ret = graph_for_each_link(priv, &li, 487 graph_dai_link_of, 488 graph_dai_link_of_dpcm); 489 if (ret < 0) 490 return ret; 491 } 492 493 return asoc_simple_parse_card_name(card, NULL); 494 } 495 496 static int graph_count_noml(struct asoc_simple_priv *priv, 497 struct device_node *cpu_ep, 498 struct device_node *codec_ep, 499 struct link_info *li) 500 { 501 struct device *dev = simple_priv_to_dev(priv); 502 503 li->link += 1; /* 1xCPU-Codec */ 504 li->dais += 2; /* 1xCPU + 1xCodec */ 505 506 dev_dbg(dev, "Count As Normal\n"); 507 508 return 0; 509 } 510 511 static int graph_count_dpcm(struct asoc_simple_priv *priv, 512 struct device_node *cpu_ep, 513 struct device_node *codec_ep, 514 struct link_info *li, 515 int dup_codec) 516 { 517 struct device *dev = simple_priv_to_dev(priv); 518 519 li->link++; /* 1xCPU-dummy */ 520 li->dais++; /* 1xCPU */ 521 522 if (!dup_codec) { 523 li->link++; /* 1xdummy-Codec */ 524 li->conf++; /* 1xdummy-Codec */ 525 li->dais++; /* 1xCodec */ 526 } 527 528 dev_dbg(dev, "Count As DPCM\n"); 529 530 return 0; 531 } 532 533 static void graph_get_dais_count(struct asoc_simple_priv *priv, 534 struct link_info *li) 535 { 536 struct device *dev = simple_priv_to_dev(priv); 537 538 /* 539 * link_num : number of links. 540 * CPU-Codec / CPU-dummy / dummy-Codec 541 * dais_num : number of DAIs 542 * ccnf_num : number of codec_conf 543 * same number for "dummy-Codec" 544 * 545 * ex1) 546 * CPU0 --- Codec0 link : 5 547 * CPU1 --- Codec1 dais : 7 548 * CPU2 -/ ccnf : 1 549 * CPU3 --- Codec2 550 * 551 * => 5 links = 2xCPU-Codec + 2xCPU-dummy + 1xdummy-Codec 552 * => 7 DAIs = 4xCPU + 3xCodec 553 * => 1 ccnf = 1xdummy-Codec 554 * 555 * ex2) 556 * CPU0 --- Codec0 link : 5 557 * CPU1 --- Codec1 dais : 6 558 * CPU2 -/ ccnf : 1 559 * CPU3 -/ 560 * 561 * => 5 links = 1xCPU-Codec + 3xCPU-dummy + 1xdummy-Codec 562 * => 6 DAIs = 4xCPU + 2xCodec 563 * => 1 ccnf = 1xdummy-Codec 564 * 565 * ex3) 566 * CPU0 --- Codec0 link : 6 567 * CPU1 -/ dais : 6 568 * CPU2 --- Codec1 ccnf : 2 569 * CPU3 -/ 570 * 571 * => 6 links = 0xCPU-Codec + 4xCPU-dummy + 2xdummy-Codec 572 * => 6 DAIs = 4xCPU + 2xCodec 573 * => 2 ccnf = 2xdummy-Codec 574 * 575 * ex4) 576 * CPU0 --- Codec0 (convert-rate) link : 3 577 * CPU1 --- Codec1 dais : 4 578 * ccnf : 1 579 * 580 * => 3 links = 1xCPU-Codec + 1xCPU-dummy + 1xdummy-Codec 581 * => 4 DAIs = 2xCPU + 2xCodec 582 * => 1 ccnf = 1xdummy-Codec 583 */ 584 graph_for_each_link(priv, li, 585 graph_count_noml, 586 graph_count_dpcm); 587 dev_dbg(dev, "link %d, dais %d, ccnf %d\n", 588 li->link, li->dais, li->conf); 589 } 590 591 static int graph_card_probe(struct snd_soc_card *card) 592 { 593 struct asoc_simple_priv *priv = snd_soc_card_get_drvdata(card); 594 int ret; 595 596 ret = asoc_simple_init_hp(card, &priv->hp_jack, NULL); 597 if (ret < 0) 598 return ret; 599 600 ret = asoc_simple_init_mic(card, &priv->mic_jack, NULL); 601 if (ret < 0) 602 return ret; 603 604 return 0; 605 } 606 607 static int graph_probe(struct platform_device *pdev) 608 { 609 struct asoc_simple_priv *priv; 610 struct device *dev = &pdev->dev; 611 struct snd_soc_card *card; 612 struct link_info li; 613 int ret; 614 615 /* Allocate the private data and the DAI link array */ 616 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 617 if (!priv) 618 return -ENOMEM; 619 620 card = simple_priv_to_card(priv); 621 card->owner = THIS_MODULE; 622 card->dev = dev; 623 card->dapm_widgets = graph_dapm_widgets; 624 card->num_dapm_widgets = ARRAY_SIZE(graph_dapm_widgets); 625 card->probe = graph_card_probe; 626 627 memset(&li, 0, sizeof(li)); 628 graph_get_dais_count(priv, &li); 629 if (!li.link || !li.dais) 630 return -EINVAL; 631 632 ret = asoc_simple_init_priv(priv, &li); 633 if (ret < 0) 634 return ret; 635 636 priv->pa_gpio = devm_gpiod_get_optional(dev, "pa", GPIOD_OUT_LOW); 637 if (IS_ERR(priv->pa_gpio)) { 638 ret = PTR_ERR(priv->pa_gpio); 639 dev_err(dev, "failed to get amplifier gpio: %d\n", ret); 640 return ret; 641 } 642 643 ret = graph_parse_of(priv); 644 if (ret < 0) { 645 if (ret != -EPROBE_DEFER) 646 dev_err(dev, "parse error %d\n", ret); 647 goto err; 648 } 649 650 snd_soc_card_set_drvdata(card, priv); 651 652 asoc_simple_debug_info(priv); 653 654 ret = devm_snd_soc_register_card(dev, card); 655 if (ret < 0) 656 goto err; 657 658 return 0; 659 err: 660 asoc_simple_clean_reference(card); 661 662 return ret; 663 } 664 665 static int graph_remove(struct platform_device *pdev) 666 { 667 struct snd_soc_card *card = platform_get_drvdata(pdev); 668 669 return asoc_simple_clean_reference(card); 670 } 671 672 static const struct of_device_id graph_of_match[] = { 673 { .compatible = "audio-graph-card", }, 674 { .compatible = "audio-graph-scu-card", 675 .data = (void *)DPCM_SELECTABLE }, 676 {}, 677 }; 678 MODULE_DEVICE_TABLE(of, graph_of_match); 679 680 static struct platform_driver graph_card = { 681 .driver = { 682 .name = "asoc-audio-graph-card", 683 .pm = &snd_soc_pm_ops, 684 .of_match_table = graph_of_match, 685 }, 686 .probe = graph_probe, 687 .remove = graph_remove, 688 }; 689 module_platform_driver(graph_card); 690 691 MODULE_ALIAS("platform:asoc-audio-graph-card"); 692 MODULE_LICENSE("GPL v2"); 693 MODULE_DESCRIPTION("ASoC Audio Graph Sound Card"); 694 MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>"); 695