Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ASoC: audio-graph-card2: add DPCM support

This patch adds DPCM support to audio-graph-card2.
It uses "dpcm" node (= D), needs to have routing (= A),
need to indicate both FE/BE at links (= B, C).
dpcm ports@0 is for FE (= B), port@1 is for BE (= C).
remote-endpoint can use both Single/Multi connection.

DSP
************
PCM0 <--> * fe0 be0 * <--> DAI0: Codec Headset
PCM1 <--> * fe1 be1 * <--> DAI1: Codec Speakers
PCM2 <--> * fe2 be2 * <--> DAI2: MODEM
PCM3 <--> * fe3 be3 * <--> DAI3: BT
* be4 * <--> DAI4: DMIC
* be5 * <--> DAI5: FM
************

sound {
compatible = "audio-graph-card2";

// indicate routing
(A) routing = "xxx Playback", "xxx Playback",
"xxx Playback", "xxx Playback",
"xxx Playback", "xxx Playback";

// indicate all Front-End, Back-End in DPCM case
(B) links = <&fe0, &fe1, ...
(C) &be0, &be1, ...

(D) dpcm {
// Front-End
ports@0 {
(B) fe0: port@0 { fe0_ep: endpoint { remote-endpoint = <&pcm0_ep>; }; };
(B) fe1: port@1 { fe1_ep: endpoint { remote-endpoint = <&pcm1_ep>; }; };
...
};
// Back-End
ports@1 {
(C) be0: port@0 { be0_ep: endpoint { remote-endpoint = <&dai0_ep>; }; };
(C) be1: port@1 { be1_ep: endpoint { remote-endpoint = <&dai1_ep>; }; };
...
};
};
};

CPU {
ports {
bitclock-master;
frame-master;
port@0 { pcm0_ep: endpoint { remote-endpoint = <&fe0_ep>; }; };
port@1 { pcm1_ep: endpoint { remote-endpoint = <&fe1_ep>; }; };
...
};
};

Codec {
ports {
port@0 { dai0_ep: endpoint { remote-endpoint = <&be0_ep>; }; };
port@1 { dai1_ep: endpoint { remote-endpoint = <&be1_ep>; }; };
...
};
};

Link: https://lore.kernel.org/r/87k0xszlep.wl-kuninori.morimoto.gx@renesas.com
Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
Link: https://lore.kernel.org/r/87zgrelu4v.wl-kuninori.morimoto.gx@renesas.com
Signed-off-by: Mark Brown <broonie@kernel.org>

authored by

Kuninori Morimoto and committed by
Mark Brown
f03beb55 c8c74939

+257
+3
include/sound/graph_card.h
··· 17 17 int (*hook_pre)(struct asoc_simple_priv *priv); 18 18 int (*hook_post)(struct asoc_simple_priv *priv); 19 19 GRAPH2_CUSTOM custom_normal; 20 + GRAPH2_CUSTOM custom_dpcm; 20 21 }; 21 22 22 23 int audio_graph_parse_of(struct asoc_simple_priv *priv, struct device *dev); ··· 26 25 27 26 int audio_graph2_link_normal(struct asoc_simple_priv *priv, 28 27 struct device_node *lnk, struct link_info *li); 28 + int audio_graph2_link_dpcm(struct asoc_simple_priv *priv, 29 + struct device_node *lnk, struct link_info *li); 29 30 30 31 #endif /* __GRAPH_CARD_H */
+254
sound/soc/generic/audio-graph-card2.c
··· 116 116 }; 117 117 }; 118 118 119 + ************************************ 120 + DPCM 121 + ************************************ 122 + 123 + DSP 124 + ************ 125 + PCM0 <--> * fe0 be0 * <--> DAI0: Codec Headset 126 + PCM1 <--> * fe1 be1 * <--> DAI1: Codec Speakers 127 + PCM2 <--> * fe2 be2 * <--> DAI2: MODEM 128 + PCM3 <--> * fe3 be3 * <--> DAI3: BT 129 + * be4 * <--> DAI4: DMIC 130 + * be5 * <--> DAI5: FM 131 + ************ 132 + 133 + sound { 134 + compatible = "audio-graph-card2"; 135 + 136 + // indicate routing 137 + routing = "xxx Playback", "xxx Playback", 138 + "xxx Playback", "xxx Playback", 139 + "xxx Playback", "xxx Playback"; 140 + 141 + // indicate all Front-End, Back-End 142 + links = <&fe0, &fe1, ..., 143 + &be0, &be1, ...>; 144 + 145 + dpcm { 146 + // Front-End 147 + ports@0 { 148 + fe0: port@0 { fe0_ep: endpoint { remote-endpoint = <&pcm0_ep>; }; }; 149 + fe1: port@1 { fe1_ep: endpoint { remote-endpoint = <&pcm1_ep>; }; }; 150 + ... 151 + }; 152 + // Back-End 153 + ports@1 { 154 + be0: port@0 { be0_ep: endpoint { remote-endpoint = <&dai0_ep>; }; }; 155 + be1: port@1 { be1_ep: endpoint { remote-endpoint = <&dai1_ep>; }; }; 156 + ... 157 + }; 158 + }; 159 + }; 160 + 161 + CPU { 162 + ports { 163 + bitclock-master; 164 + frame-master; 165 + port@0 { pcm0_ep: endpoint { remote-endpoint = <&fe0_ep>; }; }; 166 + port@1 { pcm1_ep: endpoint { remote-endpoint = <&fe1_ep>; }; }; 167 + ... 168 + }; 169 + }; 170 + 171 + Codec { 172 + ports { 173 + port@0 { dai0_ep: endpoint { remote-endpoint = <&be0_ep>; }; }; 174 + port@1 { dai1_ep: endpoint { remote-endpoint = <&be1_ep>; }; }; 175 + ... 176 + }; 177 + }; 178 + 119 179 */ 120 180 121 181 enum graph_type { 122 182 GRAPH_NORMAL, 183 + GRAPH_DPCM, 123 184 124 185 GRAPH_MULTI, /* don't use ! Use this only in __graph_get_type() */ 125 186 }; 126 187 127 188 #define GRAPH_NODENAME_MULTI "multi" 189 + #define GRAPH_NODENAME_DPCM "dpcm" 128 190 129 191 #define port_to_endpoint(port) of_get_child_by_name(port, "endpoint") 130 192 ··· 209 147 if (of_node_name_eq(np, GRAPH_NODENAME_MULTI)) 210 148 return GRAPH_MULTI; 211 149 150 + if (of_node_name_eq(np, GRAPH_NODENAME_DPCM)) 151 + return GRAPH_DPCM; 152 + 212 153 return GRAPH_NORMAL; 213 154 } 214 155 ··· 228 163 { 229 164 struct device *dev = simple_priv_to_dev(priv); 230 165 const char *str = "Normal"; 166 + 167 + switch (type) { 168 + case GRAPH_DPCM: 169 + if (asoc_graph_is_ports0(lnk)) 170 + str = "DPCM Front-End"; 171 + else 172 + str = "DPCM Back-End"; 173 + break; 174 + default: 175 + break; 176 + } 231 177 232 178 dev_dbg(dev, "%pOF (%s)", lnk, str); 233 179 } ··· 398 322 return 0; 399 323 } 400 324 325 + static void graph_parse_convert(struct device_node *ep, 326 + struct simple_dai_props *props) 327 + { 328 + struct device_node *port = of_get_parent(ep); 329 + struct device_node *ports = of_get_parent(port); 330 + struct asoc_simple_data *adata = &props->adata; 331 + 332 + if (of_node_name_eq(ports, "ports")) 333 + asoc_simple_parse_convert(ports, NULL, adata); 334 + asoc_simple_parse_convert(port, NULL, adata); 335 + asoc_simple_parse_convert(ep, NULL, adata); 336 + 337 + of_node_put(port); 338 + of_node_put(ports); 339 + } 340 + 401 341 static void graph_parse_mclk_fs(struct device_node *ep, 402 342 struct simple_dai_props *props) 403 343 { ··· 486 394 cpus->dai_name, cpu_multi, 487 395 codecs->dai_name, codec_multi); 488 396 break; 397 + case GRAPH_DPCM: 398 + if (is_cpu) 399 + asoc_simple_set_dailink_name(dev, dai_link, "fe.%pOFP.%s%s", 400 + cpus->of_node, cpus->dai_name, cpu_multi); 401 + else 402 + asoc_simple_set_dailink_name(dev, dai_link, "be.%pOFP.%s%s", 403 + codecs->of_node, codecs->dai_name, codec_multi); 404 + break; 489 405 default: 490 406 break; 491 407 } 408 + } 409 + 410 + /* 411 + * Check "prefix" from top node 412 + * if DPCM-BE case 413 + */ 414 + if (!is_cpu && gtype == GRAPH_DPCM) { 415 + struct snd_soc_dai_link_component *codecs = asoc_link_to_codec(dai_link, idx); 416 + struct snd_soc_codec_conf *cconf = simple_props_to_codec_conf(dai_props, idx); 417 + struct device_node *rport = of_get_parent(ep); 418 + struct device_node *rports = of_get_parent(rport); 419 + 420 + if (of_node_name_eq(rports, "ports")) 421 + snd_soc_of_parse_node_prefix(rports, cconf, codecs->of_node, "prefix"); 422 + snd_soc_of_parse_node_prefix(rport, cconf, codecs->of_node, "prefix"); 423 + 424 + of_node_put(rport); 425 + of_node_put(rports); 492 426 } 493 427 494 428 if (is_cpu) { ··· 700 582 } 701 583 EXPORT_SYMBOL_GPL(audio_graph2_link_normal); 702 584 585 + int audio_graph2_link_dpcm(struct asoc_simple_priv *priv, 586 + struct device_node *lnk, 587 + struct link_info *li) 588 + { 589 + struct device_node *ep = port_to_endpoint(lnk); 590 + struct device_node *rep = of_graph_get_remote_endpoint(ep); 591 + struct device_node *rport = of_graph_get_remote_port(ep); 592 + struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, li->link); 593 + struct simple_dai_props *dai_props = simple_priv_to_props(priv, li->link); 594 + int is_cpu = asoc_graph_is_ports0(lnk); 595 + int ret; 596 + 597 + if (is_cpu) { 598 + /* 599 + * dpcm { 600 + * // Front-End 601 + * ports@0 { 602 + * => lnk: port@0 { ep: { ... = rep }; }; 603 + * ... 604 + * }; 605 + * // Back-End 606 + * ports@0 { 607 + * ... 608 + * }; 609 + * }; 610 + * 611 + * CPU { 612 + * rports: ports { 613 + * rport: port@0 { rep: { ... = ep } }; 614 + * } 615 + * } 616 + */ 617 + /* 618 + * setup CPU here, Codec is already set as dummy. 619 + * see 620 + * asoc_simple_init_priv() 621 + */ 622 + dai_link->dynamic = 1; 623 + dai_link->dpcm_merged_format = 1; 624 + 625 + ret = graph_parse_node(priv, GRAPH_DPCM, rport, li, 1); 626 + if (ret) 627 + goto err; 628 + } else { 629 + /* 630 + * dpcm { 631 + * // Front-End 632 + * ports@0 { 633 + * ... 634 + * }; 635 + * // Back-End 636 + * ports@0 { 637 + * => lnk: port@0 { ep: { ... = rep; }; }; 638 + * ... 639 + * }; 640 + * }; 641 + * 642 + * Codec { 643 + * rports: ports { 644 + * rport: port@0 { rep: { ... = ep; }; }; 645 + * } 646 + * } 647 + */ 648 + /* 649 + * setup Codec here, CPU is already set as dummy. 650 + * see 651 + * asoc_simple_init_priv() 652 + */ 653 + 654 + /* BE settings */ 655 + dai_link->no_pcm = 1; 656 + dai_link->be_hw_params_fixup = asoc_simple_be_hw_params_fixup; 657 + 658 + ret = graph_parse_node(priv, GRAPH_DPCM, rport, li, 0); 659 + if (ret < 0) 660 + goto err; 661 + } 662 + 663 + graph_parse_convert(rep, dai_props); 664 + 665 + snd_soc_dai_link_set_capabilities(dai_link); 666 + 667 + graph_link_init(priv, rport, li, is_cpu); 668 + err: 669 + of_node_put(ep); 670 + of_node_put(rep); 671 + of_node_put(rport); 672 + 673 + return ret; 674 + } 675 + EXPORT_SYMBOL_GPL(audio_graph2_link_dpcm); 676 + 703 677 static int graph_link(struct asoc_simple_priv *priv, 704 678 struct graph2_custom_hooks *hooks, 705 679 enum graph_type gtype, ··· 808 598 func = hooks->custom_normal; 809 599 else 810 600 func = audio_graph2_link_normal; 601 + break; 602 + case GRAPH_DPCM: 603 + if (hooks && hooks->custom_dpcm) 604 + func = hooks->custom_dpcm; 605 + else 606 + func = audio_graph2_link_dpcm; 811 607 break; 812 608 default: 813 609 break; ··· 881 665 return 0; 882 666 } 883 667 668 + static int graph_count_dpcm(struct asoc_simple_priv *priv, 669 + struct device_node *lnk, 670 + struct link_info *li) 671 + { 672 + struct device_node *ep = port_to_endpoint(lnk); 673 + struct device_node *rport = of_graph_get_remote_port(ep); 674 + 675 + /* 676 + * dpcm { 677 + * // Front-End 678 + * ports@0 { 679 + * => lnk: port@0 { endpoint { ... }; }; 680 + * ... 681 + * }; 682 + * // Back-End 683 + * ports@1 { 684 + * => lnk: port@0 { endpoint { ... }; }; 685 + * ... 686 + * }; 687 + * }; 688 + */ 689 + 690 + if (asoc_graph_is_ports0(lnk)) { 691 + li->num[li->link].cpus = graph_counter(rport); /* FE */ 692 + li->num[li->link].platforms = graph_counter(rport); 693 + } else { 694 + li->num[li->link].codecs = graph_counter(rport); /* BE */ 695 + } 696 + 697 + of_node_put(ep); 698 + of_node_put(rport); 699 + 700 + return 0; 701 + } 702 + 884 703 static int graph_count(struct asoc_simple_priv *priv, 885 704 struct graph2_custom_hooks *hooks, 886 705 enum graph_type gtype, ··· 934 683 switch (gtype) { 935 684 case GRAPH_NORMAL: 936 685 func = graph_count_normal; 686 + break; 687 + case GRAPH_DPCM: 688 + func = graph_count_dpcm; 937 689 break; 938 690 default: 939 691 break;