1452 lines · c
1// SPDX-License-Identifier: GPL-2.02//3// ASoC Audio Graph Card2 support4//5// Copyright (C) 2020 Renesas Electronics Corp.6// Copyright (C) 2020 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>7//8// based on ${LINUX}/sound/soc/generic/audio-graph-card.c9#include <linux/clk.h>10#include <linux/device.h>11#include <linux/gpio/consumer.h>12#include <linux/module.h>13#include <linux/of.h>14#include <linux/of_graph.h>15#include <linux/platform_device.h>16#include <linux/string.h>17#include <sound/graph_card.h>18 19/************************************20 daifmt21 ************************************22 ports {23 format = "left_j";24 port@0 {25 bitclock-master;26 sample0: endpoint@0 {27 frame-master;28 };29 sample1: endpoint@1 {30 format = "i2s";31 };32 };33 ...34 };35 36 You can set daifmt at ports/port/endpoint.37 It uses *latest* format, and *share* master settings.38 In above case,39 sample0: left_j, bitclock-master, frame-master40 sample1: i2s, bitclock-master41 42 If there was no settings, *Codec* will be43 bitclock/frame provider as default.44 see45 graph_parse_daifmt().46 47 "format" property is no longer needed on DT if both CPU/Codec drivers are48 supporting snd_soc_dai_ops :: .auto_selectable_formats.49 see50 snd_soc_runtime_get_dai_fmt()51 52 sample driver53 linux/sound/soc/sh/rcar/core.c54 linux/sound/soc/codecs/ak4613.c55 linux/sound/soc/codecs/pcm3168a.c56 linux/sound/soc/soc-utils.c57 linux/sound/soc/generic/test-component.c58 59 ************************************60 Normal Audio-Graph61 ************************************62 63 CPU <---> Codec64 65 sound {66 compatible = "audio-graph-card2";67 links = <&cpu>;68 };69 70 CPU {71 cpu: port {72 bitclock-master;73 frame-master;74 cpu_ep: endpoint { remote-endpoint = <&codec_ep>; }; };75 };76 77 Codec {78 port { codec_ep: endpoint { remote-endpoint = <&cpu_ep>; }; };79 };80 81 ************************************82 Multi-CPU/Codec83 ************************************84 85It has link connection part (= X,x) and list part (= A,B,a,b).86"links" is connection part of CPU side (= @).87 88 +----+ +---+89 CPU1 --|A X| <-@----> |x a|-- Codec190 CPU2 --|B | | b|-- Codec291 +----+ +---+92 93 sound {94 compatible = "audio-graph-card2";95 96(@) links = <&mcpu>;97 98 multi {99 ports@0 {100(@) mcpu: port@0 { mcpu0_ep: endpoint { remote-endpoint = <&mcodec0_ep>; }; }; // (X) to pair101 port@1 { mcpu1_ep: endpoint { remote-endpoint = <&cpu1_ep>; }; }; // (A) Multi Element102 port@2 { mcpu2_ep: endpoint { remote-endpoint = <&cpu2_ep>; }; }; // (B) Multi Element103 };104 ports@1 {105 port@0 { mcodec0_ep: endpoint { remote-endpoint = <&mcpu0_ep>; }; }; // (x) to pair106 port@1 { mcodec1_ep: endpoint { remote-endpoint = <&codec1_ep>; }; }; // (a) Multi Element107 port@2 { mcodec2_ep: endpoint { remote-endpoint = <&codec2_ep>; }; }; // (b) Multi Element108 };109 };110 };111 112 CPU {113 ports {114 bitclock-master;115 frame-master;116 port@0 { cpu1_ep: endpoint { remote-endpoint = <&mcpu1_ep>; }; };117 port@1 { cpu2_ep: endpoint { remote-endpoint = <&mcpu2_ep>; }; };118 };119 };120 121 Codec {122 ports {123 port@0 { codec1_ep: endpoint { remote-endpoint = <&mcodec1_ep>; }; };124 port@1 { codec2_ep: endpoint { remote-endpoint = <&mcodec2_ep>; }; };125 };126 };127 128 ************************************129 DPCM130 ************************************131 132 DSP133 ************134 PCM0 <--> * fe0 be0 * <--> DAI0: Codec Headset135 PCM1 <--> * fe1 be1 * <--> DAI1: Codec Speakers136 PCM2 <--> * fe2 be2 * <--> DAI2: MODEM137 PCM3 <--> * fe3 be3 * <--> DAI3: BT138 * be4 * <--> DAI4: DMIC139 * be5 * <--> DAI5: FM140 ************141 142 sound {143 compatible = "audio-graph-card2";144 145 // indicate routing146 routing = "xxx Playback", "xxx Playback",147 "xxx Playback", "xxx Playback",148 "xxx Playback", "xxx Playback";149 150 // indicate all Front-End, Back-End151 links = <&fe0, &fe1, ...,152 &be0, &be1, ...>;153 154 dpcm {155 // Front-End156 ports@0 {157 fe0: port@0 { fe0_ep: endpoint { remote-endpoint = <&pcm0_ep>; }; };158 fe1: port@1 { fe1_ep: endpoint { remote-endpoint = <&pcm1_ep>; }; };159 ...160 };161 // Back-End162 ports@1 {163 be0: port@0 { be0_ep: endpoint { remote-endpoint = <&dai0_ep>; }; };164 be1: port@1 { be1_ep: endpoint { remote-endpoint = <&dai1_ep>; }; };165 ...166 };167 };168 };169 170 CPU {171 ports {172 bitclock-master;173 frame-master;174 port@0 { pcm0_ep: endpoint { remote-endpoint = <&fe0_ep>; }; };175 port@1 { pcm1_ep: endpoint { remote-endpoint = <&fe1_ep>; }; };176 ...177 };178 };179 180 Codec {181 ports {182 port@0 { dai0_ep: endpoint { remote-endpoint = <&be0_ep>; }; };183 port@1 { dai1_ep: endpoint { remote-endpoint = <&be1_ep>; }; };184 ...185 };186 };187 188 ************************************189 Codec to Codec190 ************************************191 192 +--+193 | |<-- Codec0 <- IN194 | |--> Codec1 -> OUT195 +--+196 197 sound {198 compatible = "audio-graph-card2";199 200 routing = "OUT" ,"DAI1 Playback",201 "DAI0 Capture", "IN";202 203 links = <&c2c>;204 205 codec2codec {206 ports {207 rate = <48000>;208 c2c: port@0 { c2cf_ep: endpoint { remote-endpoint = <&codec0_ep>; }; };209 port@1 { c2cb_ep: endpoint { remote-endpoint = <&codec1_ep>; }; };210 };211 };212 213 Codec {214 ports {215 port@0 {216 bitclock-master;217 frame-master;218 codec0_ep: endpoint { remote-endpoint = <&c2cf_ep>; }; };219 port@1 { codec1_ep: endpoint { remote-endpoint = <&c2cb_ep>; }; };220 };221 };222 223*/224 225enum graph_type {226 GRAPH_NORMAL,227 GRAPH_DPCM,228 GRAPH_C2C,229 230 GRAPH_MULTI, /* don't use ! Use this only in __graph_get_type() */231};232 233#define GRAPH_NODENAME_MULTI "multi"234#define GRAPH_NODENAME_DPCM "dpcm"235#define GRAPH_NODENAME_C2C "codec2codec"236 237#define port_to_endpoint(port) of_get_child_by_name(port, "endpoint")238 239#define ep_to_port(ep) of_get_parent(ep)240static struct device_node *port_to_ports(struct device_node *port)241{242 struct device_node *ports = of_get_parent(port);243 244 if (!of_node_name_eq(ports, "ports")) {245 of_node_put(ports);246 return NULL;247 }248 return ports;249}250 251static enum graph_type __graph_get_type(struct device_node *lnk)252{253 struct device_node *np, *parent_np;254 enum graph_type ret;255 256 /*257 * target {258 * ports {259 * => lnk: port@0 { ... };260 * port@1 { ... };261 * };262 * };263 */264 np = of_get_parent(lnk);265 if (of_node_name_eq(np, "ports")) {266 parent_np = of_get_parent(np);267 of_node_put(np);268 np = parent_np;269 }270 271 if (of_node_name_eq(np, GRAPH_NODENAME_MULTI)) {272 ret = GRAPH_MULTI;273 fw_devlink_purge_absent_suppliers(&np->fwnode);274 goto out_put;275 }276 277 if (of_node_name_eq(np, GRAPH_NODENAME_DPCM)) {278 ret = GRAPH_DPCM;279 fw_devlink_purge_absent_suppliers(&np->fwnode);280 goto out_put;281 }282 283 if (of_node_name_eq(np, GRAPH_NODENAME_C2C)) {284 ret = GRAPH_C2C;285 fw_devlink_purge_absent_suppliers(&np->fwnode);286 goto out_put;287 }288 289 ret = GRAPH_NORMAL;290 291out_put:292 of_node_put(np);293 return ret;294 295}296 297static enum graph_type graph_get_type(struct simple_util_priv *priv,298 struct device_node *lnk)299{300 enum graph_type type = __graph_get_type(lnk);301 302 /* GRAPH_MULTI here means GRAPH_NORMAL */303 if (type == GRAPH_MULTI)304 type = GRAPH_NORMAL;305 306#ifdef DEBUG307 {308 struct device *dev = simple_priv_to_dev(priv);309 const char *str = "Normal";310 311 switch (type) {312 case GRAPH_DPCM:313 if (graph_util_is_ports0(lnk))314 str = "DPCM Front-End";315 else316 str = "DPCM Back-End";317 break;318 case GRAPH_C2C:319 str = "Codec2Codec";320 break;321 default:322 break;323 }324 325 dev_dbg(dev, "%pOF (%s)", lnk, str);326 }327#endif328 return type;329}330 331static int graph_lnk_is_multi(struct device_node *lnk)332{333 return __graph_get_type(lnk) == GRAPH_MULTI;334}335 336static struct device_node *graph_get_next_multi_ep(struct device_node **port)337{338 struct device_node *ports = port_to_ports(*port);339 struct device_node *ep = NULL;340 struct device_node *rep = NULL;341 342 /*343 * multi {344 * ports {345 * => lnk: port@0 { ... }; // to pair346 * port@1 { ep { ... = rep0 } }; // Multi Element347 * port@2 { ep { ... = rep1 } }; // Multi Element348 * ...349 * };350 * };351 *352 * xxx {353 * port@0 { rep0 };354 * port@1 { rep1 };355 * };356 */357 do {358 *port = of_get_next_child(ports, *port);359 if (!*port)360 break;361 } while (!of_node_name_eq(*port, "port"));362 363 if (*port) {364 ep = port_to_endpoint(*port);365 rep = of_graph_get_remote_endpoint(ep);366 }367 368 of_node_put(ep);369 of_node_put(ports);370 371 return rep;372}373 374static const struct snd_soc_ops graph_ops = {375 .startup = simple_util_startup,376 .shutdown = simple_util_shutdown,377 .hw_params = simple_util_hw_params,378};379 380static void graph_parse_convert(struct device_node *ep,381 struct simple_dai_props *props)382{383 struct device_node *port = ep_to_port(ep);384 struct device_node *ports = port_to_ports(port);385 struct simple_util_data *adata = &props->adata;386 387 simple_util_parse_convert(ports, NULL, adata);388 simple_util_parse_convert(port, NULL, adata);389 simple_util_parse_convert(ep, NULL, adata);390 391 of_node_put(port);392 of_node_put(ports);393}394 395static int __graph_parse_node(struct simple_util_priv *priv,396 enum graph_type gtype,397 struct device_node *ep,398 struct link_info *li,399 int is_cpu, int idx)400{401 struct device *dev = simple_priv_to_dev(priv);402 struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, li->link);403 struct simple_dai_props *dai_props = simple_priv_to_props(priv, li->link);404 struct snd_soc_dai_link_component *dlc;405 struct simple_util_dai *dai;406 int ret, is_single_links = 0;407 408 if (is_cpu) {409 dlc = snd_soc_link_to_cpu(dai_link, idx);410 dai = simple_props_to_dai_cpu(dai_props, idx);411 } else {412 dlc = snd_soc_link_to_codec(dai_link, idx);413 dai = simple_props_to_dai_codec(dai_props, idx);414 }415 416 ret = graph_util_parse_dai(dev, ep, dlc, &is_single_links);417 if (ret < 0)418 return ret;419 420 ret = simple_util_parse_tdm(ep, dai);421 if (ret < 0)422 return ret;423 424 ret = simple_util_parse_tdm_width_map(dev, ep, dai);425 if (ret < 0)426 return ret;427 428 ret = simple_util_parse_clk(dev, ep, dai, dlc);429 if (ret < 0)430 return ret;431 432 /*433 * set DAI Name434 */435 if (!dai_link->name) {436 struct snd_soc_dai_link_component *cpus = dlc;437 struct snd_soc_dai_link_component *codecs = snd_soc_link_to_codec(dai_link, idx);438 char *cpu_multi = "";439 char *codec_multi = "";440 441 if (dai_link->num_cpus > 1)442 cpu_multi = "_multi";443 if (dai_link->num_codecs > 1)444 codec_multi = "_multi";445 446 switch (gtype) {447 case GRAPH_NORMAL:448 /* run is_cpu only. see audio_graph2_link_normal() */449 if (is_cpu)450 simple_util_set_dailink_name(dev, dai_link, "%s%s-%s%s",451 cpus->dai_name, cpu_multi,452 codecs->dai_name, codec_multi);453 break;454 case GRAPH_DPCM:455 if (is_cpu)456 simple_util_set_dailink_name(dev, dai_link, "fe.%pOFP.%s%s",457 cpus->of_node, cpus->dai_name, cpu_multi);458 else459 simple_util_set_dailink_name(dev, dai_link, "be.%pOFP.%s%s",460 codecs->of_node, codecs->dai_name, codec_multi);461 break;462 case GRAPH_C2C:463 /* run is_cpu only. see audio_graph2_link_c2c() */464 if (is_cpu)465 simple_util_set_dailink_name(dev, dai_link, "c2c.%s%s-%s%s",466 cpus->dai_name, cpu_multi,467 codecs->dai_name, codec_multi);468 break;469 default:470 break;471 }472 }473 474 /*475 * Check "prefix" from top node476 * if DPCM-BE case477 */478 if (!is_cpu && gtype == GRAPH_DPCM) {479 struct snd_soc_dai_link_component *codecs = snd_soc_link_to_codec(dai_link, idx);480 struct snd_soc_codec_conf *cconf = simple_props_to_codec_conf(dai_props, idx);481 struct device_node *rport = ep_to_port(ep);482 struct device_node *rports = port_to_ports(rport);483 484 snd_soc_of_parse_node_prefix(rports, cconf, codecs->of_node, "prefix");485 snd_soc_of_parse_node_prefix(rport, cconf, codecs->of_node, "prefix");486 487 of_node_put(rport);488 of_node_put(rports);489 }490 491 if (is_cpu) {492 struct snd_soc_dai_link_component *cpus = dlc;493 struct snd_soc_dai_link_component *platforms = snd_soc_link_to_platform(dai_link, idx);494 495 simple_util_canonicalize_cpu(cpus, is_single_links);496 simple_util_canonicalize_platform(platforms, cpus);497 }498 499 return 0;500}501 502static int graph_parse_node_multi_nm(struct snd_soc_dai_link *dai_link,503 int *nm_idx, int cpu_idx,504 struct device_node *mcpu_port)505{506 /*507 * +---+ +---+508 * | X|<-@------->|x |509 * | | | |510 * cpu0 <--|A 1|<--------->|4 a|-> codec0511 * cpu1 <--|B 2|<-----+--->|5 b|-> codec1512 * cpu2 <--|C 3|<----/ +---+513 * +---+514 *515 * multi {516 * ports {517 * port@0 { mcpu_top_ep {... = mcodec_ep; }; }; // (X) to pair518 * <mcpu_port> port@1 { mcpu0_ep { ... = cpu0_ep; }; // (A) Multi Element519 * mcpu0_ep_0 { ... = mcodec0_ep_0; }; }; // (1) connected Codec520 * port@2 { mcpu1_ep { ... = cpu1_ep; }; // (B) Multi Element521 * mcpu1_ep_0 { ... = mcodec1_ep_0; }; }; // (2) connected Codec522 * port@3 { mcpu2_ep { ... = cpu2_ep; }; // (C) Multi Element523 * mcpu2_ep_0 { ... = mcodec1_ep_1; }; }; // (3) connected Codec524 * };525 *526 * ports {527 * port@0 { mcodec_top_ep {... = mcpu_ep; }; }; // (x) to pair528 * <mcodec_port>port@1 { mcodec0_ep { ... = codec0_ep; }; // (a) Multi Element529 * mcodec0_ep_0 { ... = mcpu0_ep_0; }; }; // (4) connected CPU530 * port@2 { mcodec1_ep { ... = codec1_ep; }; // (b) Multi Element531 * mcodec1_ep_0 { ... = mcpu1_ep_0; }; // (5) connected CPU532 * mcodec1_ep_1 { ... = mcpu2_ep_0; }; }; // (5) connected CPU533 * };534 * };535 */536 struct device_node *mcpu_ep = port_to_endpoint(mcpu_port);537 struct device_node *mcpu_ep_n = mcpu_ep;538 struct device_node *mcpu_port_top = of_get_next_child(port_to_ports(mcpu_port), NULL);539 struct device_node *mcpu_ep_top = port_to_endpoint(mcpu_port_top);540 struct device_node *mcodec_ep_top = of_graph_get_remote_endpoint(mcpu_ep_top);541 struct device_node *mcodec_port_top = ep_to_port(mcodec_ep_top);542 struct device_node *mcodec_ports = port_to_ports(mcodec_port_top);543 int nm_max = max(dai_link->num_cpus, dai_link->num_codecs);544 int ret = -EINVAL;545 546 if (cpu_idx > dai_link->num_cpus)547 goto mcpu_err;548 549 while (1) {550 struct device_node *mcodec_ep_n;551 struct device_node *mcodec_port_i;552 struct device_node *mcodec_port;553 int codec_idx;554 555 if (*nm_idx > nm_max)556 break;557 558 mcpu_ep_n = of_get_next_child(mcpu_port, mcpu_ep_n);559 if (!mcpu_ep_n) {560 ret = 0;561 break;562 }563 564 mcodec_ep_n = of_graph_get_remote_endpoint(mcpu_ep_n);565 mcodec_port = ep_to_port(mcodec_ep_n);566 567 if (mcodec_ports != port_to_ports(mcodec_port))568 goto mcpu_err;569 570 codec_idx = 0;571 mcodec_port_i = of_get_next_child(mcodec_ports, NULL);572 while (1) {573 if (codec_idx > dai_link->num_codecs)574 goto mcodec_err;575 576 mcodec_port_i = of_get_next_child(mcodec_ports, mcodec_port_i);577 578 if (!mcodec_port_i)579 goto mcodec_err;580 581 if (mcodec_port_i == mcodec_port)582 break;583 584 codec_idx++;585 }586 587 dai_link->ch_maps[*nm_idx].cpu = cpu_idx;588 dai_link->ch_maps[*nm_idx].codec = codec_idx;589 590 (*nm_idx)++;591 592 of_node_put(mcodec_port_i);593mcodec_err:594 of_node_put(mcodec_port);595 of_node_put(mcpu_ep_n);596 of_node_put(mcodec_ep_n);597 }598mcpu_err:599 of_node_put(mcpu_ep);600 of_node_put(mcpu_port_top);601 of_node_put(mcpu_ep_top);602 of_node_put(mcodec_ep_top);603 of_node_put(mcodec_port_top);604 of_node_put(mcodec_ports);605 606 return ret;607}608 609static int graph_parse_node_multi(struct simple_util_priv *priv,610 enum graph_type gtype,611 struct device_node *port,612 struct link_info *li, int is_cpu)613{614 struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, li->link);615 struct device *dev = simple_priv_to_dev(priv);616 struct device_node *ep;617 int ret = -ENOMEM;618 int nm_idx = 0;619 int nm_max = max(dai_link->num_cpus, dai_link->num_codecs);620 621 /*622 * create ch_maps if CPU:Codec = N:M623 * DPCM is out of scope624 */625 if (gtype != GRAPH_DPCM && !dai_link->ch_maps &&626 dai_link->num_cpus > 1 && dai_link->num_codecs > 1 &&627 dai_link->num_cpus != dai_link->num_codecs) {628 629 dai_link->ch_maps = devm_kcalloc(dev, nm_max,630 sizeof(struct snd_soc_dai_link_ch_map), GFP_KERNEL);631 if (!dai_link->ch_maps)632 goto multi_err;633 }634 635 for (int idx = 0;; idx++) {636 /*637 * multi {638 * ports {639 * <port> port@0 { ... }; // to pair640 * port@1 { mcpu1_ep { ... = cpu1_ep };}; // Multi Element641 * port@2 { mcpu2_ep { ... = cpu2_ep };}; // Multi Element642 * };643 * };644 *645 * cpu {646 * ports {647 * <ep> port@0 { cpu1_ep { ... = mcpu1_ep };};648 * };649 * };650 */651 ep = graph_get_next_multi_ep(&port);652 if (!ep)653 break;654 655 ret = __graph_parse_node(priv, gtype, ep, li, is_cpu, idx);656 of_node_put(ep);657 if (ret < 0)658 goto multi_err;659 660 /* CPU:Codec = N:M */661 if (is_cpu && dai_link->ch_maps) {662 ret = graph_parse_node_multi_nm(dai_link, &nm_idx, idx, port);663 if (ret < 0)664 goto multi_err;665 }666 }667 668 if (is_cpu && dai_link->ch_maps && (nm_idx != nm_max))669 ret = -EINVAL;670 671multi_err:672 return ret;673}674 675static int graph_parse_node_single(struct simple_util_priv *priv,676 enum graph_type gtype,677 struct device_node *port,678 struct link_info *li, int is_cpu)679{680 struct device_node *ep = port_to_endpoint(port);681 int ret = __graph_parse_node(priv, gtype, ep, li, is_cpu, 0);682 683 of_node_put(ep);684 685 return ret;686}687 688static int graph_parse_node(struct simple_util_priv *priv,689 enum graph_type gtype,690 struct device_node *port,691 struct link_info *li, int is_cpu)692{693 if (graph_lnk_is_multi(port))694 return graph_parse_node_multi(priv, gtype, port, li, is_cpu);695 else696 return graph_parse_node_single(priv, gtype, port, li, is_cpu);697}698 699static void graph_parse_daifmt(struct device_node *node,700 unsigned int *daifmt, unsigned int *bit_frame)701{702 unsigned int fmt;703 704 if (!node)705 return;706 707 /*708 * see also above "daifmt" explanation709 * and samples.710 */711 712 /*713 * ports {714 * (A)715 * port {716 * (B)717 * endpoint {718 * (C)719 * };720 * };721 * };722 * };723 */724 725 /*726 * clock_provider:727 *728 * It can be judged it is provider729 * if (A) or (B) or (C) has bitclock-master / frame-master flag.730 *731 * use "or"732 */733 *bit_frame |= snd_soc_daifmt_parse_clock_provider_as_bitmap(node, NULL);734 735#define update_daifmt(name) \736 if (!(*daifmt & SND_SOC_DAIFMT_##name##_MASK) && \737 (fmt & SND_SOC_DAIFMT_##name##_MASK)) \738 *daifmt |= fmt & SND_SOC_DAIFMT_##name##_MASK739 740 /*741 * format742 *743 * This function is called by (C) -> (B) -> (A) order.744 * Set if applicable part was not yet set.745 */746 fmt = snd_soc_daifmt_parse_format(node, NULL);747 update_daifmt(FORMAT);748 update_daifmt(CLOCK);749 update_daifmt(INV);750}751 752static void graph_link_init(struct simple_util_priv *priv,753 struct device_node *lnk,754 struct device_node *port_cpu,755 struct device_node *port_codec,756 struct link_info *li,757 int is_cpu_node)758{759 struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, li->link);760 struct simple_dai_props *dai_props = simple_priv_to_props(priv, li->link);761 struct device_node *ep_cpu, *ep_codec;762 struct device_node *ports_cpu, *ports_codec;763 unsigned int daifmt = 0, daiclk = 0;764 bool playback_only = 0, capture_only = 0;765 enum snd_soc_trigger_order trigger_start = SND_SOC_TRIGGER_ORDER_DEFAULT;766 enum snd_soc_trigger_order trigger_stop = SND_SOC_TRIGGER_ORDER_DEFAULT;767 unsigned int bit_frame = 0;768 769 of_node_get(port_cpu);770 if (graph_lnk_is_multi(port_cpu)) {771 ep_cpu = graph_get_next_multi_ep(&port_cpu);772 of_node_put(port_cpu);773 port_cpu = ep_to_port(ep_cpu);774 } else {775 ep_cpu = port_to_endpoint(port_cpu);776 }777 ports_cpu = port_to_ports(port_cpu);778 779 of_node_get(port_codec);780 if (graph_lnk_is_multi(port_codec)) {781 ep_codec = graph_get_next_multi_ep(&port_codec);782 of_node_put(port_cpu);783 port_codec = ep_to_port(ep_codec);784 } else {785 ep_codec = port_to_endpoint(port_codec);786 }787 ports_codec = port_to_ports(port_codec);788 789 790 graph_parse_daifmt(ep_cpu, &daifmt, &bit_frame);791 graph_parse_daifmt(ep_codec, &daifmt, &bit_frame);792 graph_parse_daifmt(port_cpu, &daifmt, &bit_frame);793 graph_parse_daifmt(port_codec, &daifmt, &bit_frame);794 graph_parse_daifmt(ports_cpu, &daifmt, &bit_frame);795 graph_parse_daifmt(ports_codec, &daifmt, &bit_frame);796 graph_parse_daifmt(lnk, &daifmt, &bit_frame);797 798 graph_util_parse_link_direction(lnk, &playback_only, &capture_only);799 graph_util_parse_link_direction(ports_cpu, &playback_only, &capture_only);800 graph_util_parse_link_direction(ports_codec, &playback_only, &capture_only);801 graph_util_parse_link_direction(port_cpu, &playback_only, &capture_only);802 graph_util_parse_link_direction(port_codec, &playback_only, &capture_only);803 graph_util_parse_link_direction(ep_cpu, &playback_only, &capture_only);804 graph_util_parse_link_direction(ep_codec, &playback_only, &capture_only);805 806 of_property_read_u32(lnk, "mclk-fs", &dai_props->mclk_fs);807 of_property_read_u32(ports_cpu, "mclk-fs", &dai_props->mclk_fs);808 of_property_read_u32(ports_codec, "mclk-fs", &dai_props->mclk_fs);809 of_property_read_u32(port_cpu, "mclk-fs", &dai_props->mclk_fs);810 of_property_read_u32(port_codec, "mclk-fs", &dai_props->mclk_fs);811 of_property_read_u32(ep_cpu, "mclk-fs", &dai_props->mclk_fs);812 of_property_read_u32(ep_codec, "mclk-fs", &dai_props->mclk_fs);813 814 graph_util_parse_trigger_order(priv, lnk, &trigger_start, &trigger_stop);815 graph_util_parse_trigger_order(priv, ports_cpu, &trigger_start, &trigger_stop);816 graph_util_parse_trigger_order(priv, ports_codec, &trigger_start, &trigger_stop);817 graph_util_parse_trigger_order(priv, port_cpu, &trigger_start, &trigger_stop);818 graph_util_parse_trigger_order(priv, port_cpu, &trigger_start, &trigger_stop);819 graph_util_parse_trigger_order(priv, ep_cpu, &trigger_start, &trigger_stop);820 graph_util_parse_trigger_order(priv, ep_codec, &trigger_start, &trigger_stop);821 822 /*823 * convert bit_frame824 * We need to flip clock_provider if it was CPU node,825 * because it is Codec base.826 */827 daiclk = snd_soc_daifmt_clock_provider_from_bitmap(bit_frame);828 if (is_cpu_node)829 daiclk = snd_soc_daifmt_clock_provider_flipped(daiclk);830 831 dai_link->playback_only = playback_only;832 dai_link->capture_only = capture_only;833 834 dai_link->trigger_start = trigger_start;835 dai_link->trigger_stop = trigger_stop;836 837 dai_link->dai_fmt = daifmt | daiclk;838 dai_link->init = simple_util_dai_init;839 dai_link->ops = &graph_ops;840 if (priv->ops)841 dai_link->ops = priv->ops;842 843 of_node_put(ports_cpu);844 of_node_put(ports_codec);845 of_node_put(port_cpu);846 of_node_put(port_codec);847 of_node_put(ep_cpu);848 of_node_put(ep_codec);849}850 851int audio_graph2_link_normal(struct simple_util_priv *priv,852 struct device_node *lnk,853 struct link_info *li)854{855 struct device_node *cpu_port = lnk;856 struct device_node *cpu_ep = port_to_endpoint(cpu_port);857 struct device_node *codec_port = of_graph_get_remote_port(cpu_ep);858 int ret;859 860 /*861 * call Codec first.862 * see863 * __graph_parse_node() :: DAI Naming864 */865 ret = graph_parse_node(priv, GRAPH_NORMAL, codec_port, li, 0);866 if (ret < 0)867 goto err;868 869 /*870 * call CPU, and set DAI Name871 */872 ret = graph_parse_node(priv, GRAPH_NORMAL, cpu_port, li, 1);873 if (ret < 0)874 goto err;875 876 graph_link_init(priv, lnk, cpu_port, codec_port, li, 1);877err:878 of_node_put(codec_port);879 of_node_put(cpu_ep);880 881 return ret;882}883EXPORT_SYMBOL_GPL(audio_graph2_link_normal);884 885int audio_graph2_link_dpcm(struct simple_util_priv *priv,886 struct device_node *lnk,887 struct link_info *li)888{889 struct device_node *ep = port_to_endpoint(lnk);890 struct device_node *rep = of_graph_get_remote_endpoint(ep);891 struct device_node *cpu_port = NULL;892 struct device_node *codec_port = NULL;893 struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, li->link);894 struct simple_dai_props *dai_props = simple_priv_to_props(priv, li->link);895 int is_cpu = graph_util_is_ports0(lnk);896 int ret;897 898 if (is_cpu) {899 cpu_port = of_graph_get_remote_port(ep); /* rport */900 901 /*902 * dpcm {903 * // Front-End904 * ports@0 {905 * => lnk: port@0 { ep: { ... = rep }; };906 * ...907 * };908 * // Back-End909 * ports@0 {910 * ...911 * };912 * };913 *914 * CPU {915 * rports: ports {916 * rport: port@0 { rep: { ... = ep } };917 * }918 * }919 */920 /*921 * setup CPU here, Codec is already set as dummy.922 * see923 * simple_util_init_priv()924 */925 dai_link->dynamic = 1;926 dai_link->dpcm_merged_format = 1;927 928 ret = graph_parse_node(priv, GRAPH_DPCM, cpu_port, li, 1);929 if (ret)930 goto err;931 932 } else {933 codec_port = of_graph_get_remote_port(ep); /* rport */934 935 /*936 * dpcm {937 * // Front-End938 * ports@0 {939 * ...940 * };941 * // Back-End942 * ports@0 {943 * => lnk: port@0 { ep: { ... = rep; }; };944 * ...945 * };946 * };947 *948 * Codec {949 * rports: ports {950 * rport: port@0 { rep: { ... = ep; }; };951 * }952 * }953 */954 /*955 * setup Codec here, CPU is already set as dummy.956 * see957 * simple_util_init_priv()958 */959 960 /* BE settings */961 dai_link->no_pcm = 1;962 dai_link->be_hw_params_fixup = simple_util_be_hw_params_fixup;963 964 ret = graph_parse_node(priv, GRAPH_DPCM, codec_port, li, 0);965 if (ret < 0)966 goto err;967 }968 969 graph_parse_convert(ep, dai_props); /* at node of <dpcm> */970 graph_parse_convert(rep, dai_props); /* at node of <CPU/Codec> */971 972 graph_link_init(priv, lnk, cpu_port, codec_port, li, is_cpu);973err:974 of_node_put(ep);975 of_node_put(rep);976 of_node_put(cpu_port);977 of_node_put(codec_port);978 979 return ret;980}981EXPORT_SYMBOL_GPL(audio_graph2_link_dpcm);982 983int audio_graph2_link_c2c(struct simple_util_priv *priv,984 struct device_node *lnk,985 struct link_info *li)986{987 struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, li->link);988 struct device_node *port0, *port1, *ports;989 struct device_node *codec0_port, *codec1_port;990 struct device_node *ep0, *ep1;991 u32 val = 0;992 int ret = -EINVAL;993 994 /*995 * codec2codec {996 * ports {997 * rate = <48000>;998 * => lnk: port@0 { c2c0_ep: { ... = codec0_ep; }; };999 * port@1 { c2c1_ep: { ... = codec1_ep; }; };1000 * };1001 * };1002 *1003 * Codec {1004 * ports {1005 * port@0 { codec0_ep: ... }; };1006 * port@1 { codec1_ep: ... }; };1007 * };1008 * };1009 */1010 of_node_get(lnk);1011 port0 = lnk;1012 ports = port_to_ports(port0);1013 port1 = of_get_next_child(ports, lnk);1014 1015 /*1016 * Card2 can use original Codec2Codec settings if DT has.1017 * It will use default settings if no settings on DT.1018 * see1019 * simple_util_init_for_codec2codec()1020 *1021 * Add more settings here if needed1022 */1023 of_property_read_u32(ports, "rate", &val);1024 if (val) {1025 struct device *dev = simple_priv_to_dev(priv);1026 struct snd_soc_pcm_stream *c2c_conf;1027 1028 c2c_conf = devm_kzalloc(dev, sizeof(*c2c_conf), GFP_KERNEL);1029 if (!c2c_conf)1030 goto err1;1031 1032 c2c_conf->formats = SNDRV_PCM_FMTBIT_S32_LE; /* update ME */1033 c2c_conf->rates = SNDRV_PCM_RATE_8000_384000;1034 c2c_conf->rate_min =1035 c2c_conf->rate_max = val;1036 c2c_conf->channels_min =1037 c2c_conf->channels_max = 2; /* update ME */1038 1039 dai_link->c2c_params = c2c_conf;1040 dai_link->num_c2c_params = 1;1041 }1042 1043 ep0 = port_to_endpoint(port0);1044 ep1 = port_to_endpoint(port1);1045 1046 codec0_port = of_graph_get_remote_port(ep0);1047 codec1_port = of_graph_get_remote_port(ep1);1048 1049 /*1050 * call Codec first.1051 * see1052 * __graph_parse_node() :: DAI Naming1053 */1054 ret = graph_parse_node(priv, GRAPH_C2C, codec1_port, li, 0);1055 if (ret < 0)1056 goto err2;1057 1058 /*1059 * call CPU, and set DAI Name1060 */1061 ret = graph_parse_node(priv, GRAPH_C2C, codec0_port, li, 1);1062 if (ret < 0)1063 goto err2;1064 1065 graph_link_init(priv, lnk, codec0_port, codec1_port, li, 1);1066err2:1067 of_node_put(ep0);1068 of_node_put(ep1);1069 of_node_put(codec0_port);1070 of_node_put(codec1_port);1071err1:1072 of_node_put(ports);1073 of_node_put(port0);1074 of_node_put(port1);1075 1076 return ret;1077}1078EXPORT_SYMBOL_GPL(audio_graph2_link_c2c);1079 1080static int graph_link(struct simple_util_priv *priv,1081 struct graph2_custom_hooks *hooks,1082 enum graph_type gtype,1083 struct device_node *lnk,1084 struct link_info *li)1085{1086 struct device *dev = simple_priv_to_dev(priv);1087 GRAPH2_CUSTOM func = NULL;1088 int ret = -EINVAL;1089 1090 switch (gtype) {1091 case GRAPH_NORMAL:1092 if (hooks && hooks->custom_normal)1093 func = hooks->custom_normal;1094 else1095 func = audio_graph2_link_normal;1096 break;1097 case GRAPH_DPCM:1098 if (hooks && hooks->custom_dpcm)1099 func = hooks->custom_dpcm;1100 else1101 func = audio_graph2_link_dpcm;1102 break;1103 case GRAPH_C2C:1104 if (hooks && hooks->custom_c2c)1105 func = hooks->custom_c2c;1106 else1107 func = audio_graph2_link_c2c;1108 break;1109 default:1110 break;1111 }1112 1113 if (!func) {1114 dev_err(dev, "non supported gtype (%d)\n", gtype);1115 goto err;1116 }1117 1118 ret = func(priv, lnk, li);1119 if (ret < 0)1120 goto err;1121 1122 li->link++;1123err:1124 return ret;1125}1126 1127static int graph_counter(struct device_node *lnk)1128{1129 /*1130 * Multi CPU / Codec1131 *1132 * multi {1133 * ports {1134 * => lnk: port@0 { ... }; // to pair1135 * port@1 { ... }; // Multi Element1136 * port@2 { ... }; // Multi Element1137 * ...1138 * };1139 * };1140 *1141 * ignore first lnk part1142 */1143 if (graph_lnk_is_multi(lnk)) {1144 struct device_node *ports = port_to_ports(lnk);1145 1146 /*1147 * CPU/Codec = N:M case has many endpoints.1148 * We can't use of_graph_get_endpoint_count() here1149 */1150 return of_get_child_count(ports) - 1;1151 }1152 /*1153 * Single CPU / Codec1154 */1155 else1156 return 1;1157}1158 1159static int graph_count_normal(struct simple_util_priv *priv,1160 struct device_node *lnk,1161 struct link_info *li)1162{1163 struct device_node *cpu_port = lnk;1164 struct device_node *cpu_ep = port_to_endpoint(cpu_port);1165 struct device_node *codec_port = of_graph_get_remote_port(cpu_ep);1166 1167 /*1168 * CPU {1169 * => lnk: port { endpoint { .. }; };1170 * };1171 */1172 /*1173 * DON'T REMOVE platforms1174 * see1175 * simple-card.c :: simple_count_noml()1176 */1177 li->num[li->link].cpus =1178 li->num[li->link].platforms = graph_counter(cpu_port);1179 1180 li->num[li->link].codecs = graph_counter(codec_port);1181 1182 of_node_put(cpu_ep);1183 of_node_put(codec_port);1184 1185 return 0;1186}1187 1188static int graph_count_dpcm(struct simple_util_priv *priv,1189 struct device_node *lnk,1190 struct link_info *li)1191{1192 struct device_node *ep = port_to_endpoint(lnk);1193 struct device_node *rport = of_graph_get_remote_port(ep);1194 1195 /*1196 * dpcm {1197 * // Front-End1198 * ports@0 {1199 * => lnk: port@0 { endpoint { ... }; };1200 * ...1201 * };1202 * // Back-End1203 * ports@1 {1204 * => lnk: port@0 { endpoint { ... }; };1205 * ...1206 * };1207 * };1208 */1209 1210 if (graph_util_is_ports0(lnk)) {1211 /*1212 * DON'T REMOVE platforms1213 * see1214 * simple-card.c :: simple_count_noml()1215 */1216 li->num[li->link].cpus = graph_counter(rport); /* FE */1217 li->num[li->link].platforms = graph_counter(rport);1218 } else {1219 li->num[li->link].codecs = graph_counter(rport); /* BE */1220 }1221 1222 of_node_put(ep);1223 of_node_put(rport);1224 1225 return 0;1226}1227 1228static int graph_count_c2c(struct simple_util_priv *priv,1229 struct device_node *lnk,1230 struct link_info *li)1231{1232 struct device_node *ports = port_to_ports(lnk);1233 struct device_node *port0 = lnk;1234 struct device_node *port1 = of_get_next_child(ports, of_node_get(lnk));1235 struct device_node *ep0 = port_to_endpoint(port0);1236 struct device_node *ep1 = port_to_endpoint(port1);1237 struct device_node *codec0 = of_graph_get_remote_port(ep0);1238 struct device_node *codec1 = of_graph_get_remote_port(ep1);1239 1240 /*1241 * codec2codec {1242 * ports {1243 * => lnk: port@0 { endpoint { ... }; };1244 * port@1 { endpoint { ... }; };1245 * };1246 * };1247 */1248 /*1249 * DON'T REMOVE platforms1250 * see1251 * simple-card.c :: simple_count_noml()1252 */1253 li->num[li->link].cpus =1254 li->num[li->link].platforms = graph_counter(codec0);1255 1256 li->num[li->link].codecs = graph_counter(codec1);1257 1258 of_node_put(ports);1259 of_node_put(port1);1260 of_node_put(ep0);1261 of_node_put(ep1);1262 of_node_put(codec0);1263 of_node_put(codec1);1264 1265 return 0;1266}1267 1268static int graph_count(struct simple_util_priv *priv,1269 struct graph2_custom_hooks *hooks,1270 enum graph_type gtype,1271 struct device_node *lnk,1272 struct link_info *li)1273{1274 struct device *dev = simple_priv_to_dev(priv);1275 GRAPH2_CUSTOM func = NULL;1276 int ret = -EINVAL;1277 1278 if (li->link >= SNDRV_MAX_LINKS) {1279 dev_err(dev, "too many links\n");1280 return ret;1281 }1282 1283 switch (gtype) {1284 case GRAPH_NORMAL:1285 func = graph_count_normal;1286 break;1287 case GRAPH_DPCM:1288 func = graph_count_dpcm;1289 break;1290 case GRAPH_C2C:1291 func = graph_count_c2c;1292 break;1293 default:1294 break;1295 }1296 1297 if (!func) {1298 dev_err(dev, "non supported gtype (%d)\n", gtype);1299 goto err;1300 }1301 1302 ret = func(priv, lnk, li);1303 if (ret < 0)1304 goto err;1305 1306 li->link++;1307err:1308 return ret;1309}1310 1311static int graph_for_each_link(struct simple_util_priv *priv,1312 struct graph2_custom_hooks *hooks,1313 struct link_info *li,1314 int (*func)(struct simple_util_priv *priv,1315 struct graph2_custom_hooks *hooks,1316 enum graph_type gtype,1317 struct device_node *lnk,1318 struct link_info *li))1319{1320 struct of_phandle_iterator it;1321 struct device *dev = simple_priv_to_dev(priv);1322 struct device_node *node = dev->of_node;1323 struct device_node *lnk;1324 enum graph_type gtype;1325 int rc, ret;1326 1327 /* loop for all listed CPU port */1328 of_for_each_phandle(&it, rc, node, "links", NULL, 0) {1329 lnk = it.node;1330 1331 gtype = graph_get_type(priv, lnk);1332 1333 ret = func(priv, hooks, gtype, lnk, li);1334 if (ret < 0)1335 return ret;1336 }1337 1338 return 0;1339}1340 1341int audio_graph2_parse_of(struct simple_util_priv *priv, struct device *dev,1342 struct graph2_custom_hooks *hooks)1343{1344 struct snd_soc_card *card = simple_priv_to_card(priv);1345 int ret;1346 1347 struct link_info *li __free(kfree) = kzalloc(sizeof(*li), GFP_KERNEL);1348 if (!li)1349 return -ENOMEM;1350 1351 card->probe = graph_util_card_probe;1352 card->owner = THIS_MODULE;1353 card->dev = dev;1354 1355 if ((hooks) && (hooks)->hook_pre) {1356 ret = (hooks)->hook_pre(priv);1357 if (ret < 0)1358 goto err;1359 }1360 1361 ret = graph_for_each_link(priv, hooks, li, graph_count);1362 if (!li->link)1363 ret = -EINVAL;1364 if (ret < 0)1365 goto err;1366 1367 ret = simple_util_init_priv(priv, li);1368 if (ret < 0)1369 goto err;1370 1371 priv->pa_gpio = devm_gpiod_get_optional(dev, "pa", GPIOD_OUT_LOW);1372 if (IS_ERR(priv->pa_gpio)) {1373 ret = PTR_ERR(priv->pa_gpio);1374 dev_err(dev, "failed to get amplifier gpio: %d\n", ret);1375 goto err;1376 }1377 1378 ret = simple_util_parse_widgets(card, NULL);1379 if (ret < 0)1380 goto err;1381 1382 ret = simple_util_parse_routing(card, NULL);1383 if (ret < 0)1384 goto err;1385 1386 memset(li, 0, sizeof(*li));1387 ret = graph_for_each_link(priv, hooks, li, graph_link);1388 if (ret < 0)1389 goto err;1390 1391 ret = simple_util_parse_card_name(card, NULL);1392 if (ret < 0)1393 goto err;1394 1395 snd_soc_card_set_drvdata(card, priv);1396 1397 if ((hooks) && (hooks)->hook_post) {1398 ret = (hooks)->hook_post(priv);1399 if (ret < 0)1400 goto err;1401 }1402 1403 simple_util_debug_info(priv);1404 1405 ret = snd_soc_of_parse_aux_devs(card, "aux-devs");1406 if (ret < 0)1407 goto err;1408 1409 ret = devm_snd_soc_register_card(dev, card);1410err:1411 if (ret < 0)1412 dev_err_probe(dev, ret, "parse error\n");1413 1414 return ret;1415}1416EXPORT_SYMBOL_GPL(audio_graph2_parse_of);1417 1418static int graph_probe(struct platform_device *pdev)1419{1420 struct simple_util_priv *priv;1421 struct device *dev = &pdev->dev;1422 1423 /* Allocate the private data and the DAI link array */1424 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);1425 if (!priv)1426 return -ENOMEM;1427 1428 return audio_graph2_parse_of(priv, dev, NULL);1429}1430 1431static const struct of_device_id graph_of_match[] = {1432 { .compatible = "audio-graph-card2", },1433 {},1434};1435MODULE_DEVICE_TABLE(of, graph_of_match);1436 1437static struct platform_driver graph_card = {1438 .driver = {1439 .name = "asoc-audio-graph-card2",1440 .pm = &snd_soc_pm_ops,1441 .of_match_table = graph_of_match,1442 },1443 .probe = graph_probe,1444 .remove = simple_util_remove,1445};1446module_platform_driver(graph_card);1447 1448MODULE_ALIAS("platform:asoc-audio-graph-card2");1449MODULE_LICENSE("GPL v2");1450MODULE_DESCRIPTION("ASoC Audio Graph Card2");1451MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");1452