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

ASoC: audio-graph-card2: add Codec2Codec support

This patch adds Codec2Codec support to audio-graph-card2.
It can use Codec2Codec but very simple case only for now.
It doesn't have "SWITCH" control yet, thus it start automatically
when it was probed, and can't stop, so far.
Thus it needs to be updated around widgets/routing handling,
and you need to understand that it is under experimental.

Codec has SND_SOC_DAPM_INPUT() (= IN) / SND_SOC_DAPM_OUTPUT(= OUT)
widgets in below case.

It is assuming 2channel, S32_LE format for now.
It needs to be updated, too.

It needs "codec2codec" node (= B), needs to have routing (= A),
need to indicate CPU side at links (= X).
ports@0 is for CPU side (= X), port@1 is Codec side (= Y).
It needs to have "rate" (= C)

+--+
| |<-- Codec0 <-- IN
| |--> Codec1 --> OUT
+--+

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

(A) routing = "OUT" ,"DAI1 Playback",
"DAI0 Capture", "IN";

(X) links = <&c2c>;

(B) codec2codec {
ports {
(C) rate = <48000>;
(X) c2c: port@0 { c2cf_ep: endpoint { remote-endpoint = <&codec0_ep>; }; };
(Y) port@1 { c2cb_ep: endpoint { remote-endpoint = <&codec1_ep>; }; };
};
};

Codec {
ports {
port@0 {
bitclock-master;
frame-master;
codec0_ep: endpoint { remote-endpoint = <&c2cf_ep>; }; };
port@1 { codec1_ep: endpoint { remote-endpoint = <&c2cb_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/87y26ylu4a.wl-kuninori.morimoto.gx@renesas.com
Signed-off-by: Mark Brown <broonie@kernel.org>

authored by

Kuninori Morimoto and committed by
Mark Brown
c3a15c92 f03beb55

+184
+3
include/sound/graph_card.h
··· 18 18 int (*hook_post)(struct asoc_simple_priv *priv); 19 19 GRAPH2_CUSTOM custom_normal; 20 20 GRAPH2_CUSTOM custom_dpcm; 21 + GRAPH2_CUSTOM custom_c2c; 21 22 }; 22 23 23 24 int audio_graph_parse_of(struct asoc_simple_priv *priv, struct device *dev); ··· 29 28 struct device_node *lnk, struct link_info *li); 30 29 int audio_graph2_link_dpcm(struct asoc_simple_priv *priv, 31 30 struct device_node *lnk, struct link_info *li); 31 + int audio_graph2_link_c2c(struct asoc_simple_priv *priv, 32 + struct device_node *lnk, struct link_info *li); 32 33 33 34 #endif /* __GRAPH_CARD_H */
+181
sound/soc/generic/audio-graph-card2.c
··· 176 176 }; 177 177 }; 178 178 179 + ************************************ 180 + Codec to Codec 181 + ************************************ 182 + 183 + +--+ 184 + | |<-- Codec0 <- IN 185 + | |--> Codec1 -> OUT 186 + +--+ 187 + 188 + sound { 189 + compatible = "audio-graph-card2"; 190 + 191 + routing = "OUT" ,"DAI1 Playback", 192 + "DAI0 Capture", "IN"; 193 + 194 + links = <&c2c>; 195 + 196 + codec2codec { 197 + ports { 198 + rate = <48000>; 199 + c2c: port@0 { c2cf_ep: endpoint { remote-endpoint = <&codec0_ep>; }; }; 200 + port@1 { c2cb_ep: endpoint { remote-endpoint = <&codec1_ep>; }; }; 201 + }; 202 + }; 203 + 204 + Codec { 205 + ports { 206 + port@0 { 207 + bitclock-master; 208 + frame-master; 209 + codec0_ep: endpoint { remote-endpoint = <&c2cf_ep>; }; }; 210 + port@1 { codec1_ep: endpoint { remote-endpoint = <&c2cb_ep>; }; }; 211 + }; 212 + }; 213 + 179 214 */ 180 215 181 216 enum graph_type { 182 217 GRAPH_NORMAL, 183 218 GRAPH_DPCM, 219 + GRAPH_C2C, 184 220 185 221 GRAPH_MULTI, /* don't use ! Use this only in __graph_get_type() */ 186 222 }; 187 223 188 224 #define GRAPH_NODENAME_MULTI "multi" 189 225 #define GRAPH_NODENAME_DPCM "dpcm" 226 + #define GRAPH_NODENAME_C2C "codec2codec" 190 227 191 228 #define port_to_endpoint(port) of_get_child_by_name(port, "endpoint") 192 229 ··· 249 212 if (of_node_name_eq(np, GRAPH_NODENAME_DPCM)) 250 213 return GRAPH_DPCM; 251 214 215 + if (of_node_name_eq(np, GRAPH_NODENAME_C2C)) 216 + return GRAPH_C2C; 217 + 252 218 return GRAPH_NORMAL; 253 219 } 254 220 ··· 275 235 str = "DPCM Front-End"; 276 236 else 277 237 str = "DPCM Back-End"; 238 + break; 239 + case GRAPH_C2C: 240 + str = "Codec2Codec"; 278 241 break; 279 242 default: 280 243 break; ··· 536 493 else 537 494 asoc_simple_set_dailink_name(dev, dai_link, "be.%pOFP.%s%s", 538 495 codecs->of_node, codecs->dai_name, codec_multi); 496 + break; 497 + case GRAPH_C2C: 498 + /* run is_cpu only. see audio_graph2_link_c2c() */ 499 + if (is_cpu) 500 + asoc_simple_set_dailink_name(dev, dai_link, "c2c.%s%s-%s%s", 501 + cpus->dai_name, cpu_multi, 502 + codecs->dai_name, codec_multi); 539 503 break; 540 504 default: 541 505 break; ··· 842 792 } 843 793 EXPORT_SYMBOL_GPL(audio_graph2_link_dpcm); 844 794 795 + int audio_graph2_link_c2c(struct asoc_simple_priv *priv, 796 + struct device_node *lnk, 797 + struct link_info *li) 798 + { 799 + struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, li->link); 800 + struct simple_dai_props *dai_props = simple_priv_to_props(priv, li->link); 801 + struct snd_soc_pcm_stream *c2c_conf = dai_props->c2c_conf; 802 + struct device_node *port0, *port1, *ports; 803 + struct device_node *codec0_port, *codec1_port; 804 + struct device_node *ep0, *ep1; 805 + u32 val; 806 + int ret = -EINVAL; 807 + 808 + /* 809 + * codec2codec { 810 + * ports { 811 + * rate = <48000>; 812 + * => lnk: port@0 { c2c0_ep: { ... = codec0_ep; }; }; 813 + * port@1 { c2c1_ep: { ... = codec1_ep; }; }; 814 + * }; 815 + * }; 816 + * 817 + * Codec { 818 + * ports { 819 + * port@0 { codec0_ep: ... }; }; 820 + * port@1 { codec1_ep: ... }; }; 821 + * }; 822 + * }; 823 + */ 824 + of_node_get(lnk); 825 + port0 = lnk; 826 + ports = of_get_parent(port0); 827 + port1 = of_get_next_child(ports, lnk); 828 + 829 + if (!of_get_property(ports, "rate", &val)) { 830 + struct device *dev = simple_priv_to_dev(priv); 831 + 832 + dev_err(dev, "Codec2Codec needs rate settings\n"); 833 + goto err1; 834 + } 835 + 836 + c2c_conf->formats = SNDRV_PCM_FMTBIT_S32_LE; /* update ME */ 837 + c2c_conf->rate_min = 838 + c2c_conf->rate_max = val; 839 + c2c_conf->channels_min = 840 + c2c_conf->channels_max = 2; /* update ME */ 841 + dai_link->params = c2c_conf; 842 + 843 + ep0 = port_to_endpoint(port0); 844 + ep1 = port_to_endpoint(port1); 845 + 846 + codec0_port = of_graph_get_remote_port(ep0); 847 + codec1_port = of_graph_get_remote_port(ep1); 848 + 849 + /* 850 + * call Codec first. 851 + * see 852 + * __graph_parse_node() :: DAI Naming 853 + */ 854 + ret = graph_parse_node(priv, GRAPH_C2C, codec1_port, li, 0); 855 + if (ret < 0) 856 + goto err2; 857 + 858 + /* 859 + * call CPU, and set DAI Name 860 + */ 861 + ret = graph_parse_node(priv, GRAPH_C2C, codec0_port, li, 1); 862 + if (ret < 0) 863 + goto err2; 864 + 865 + graph_link_init(priv, codec0_port, li, 1); 866 + err2: 867 + of_node_put(ep0); 868 + of_node_put(ep1); 869 + of_node_put(codec0_port); 870 + of_node_put(codec1_port); 871 + err1: 872 + of_node_put(ports); 873 + of_node_put(port0); 874 + of_node_put(port1); 875 + 876 + return ret; 877 + } 878 + EXPORT_SYMBOL_GPL(audio_graph2_link_c2c); 879 + 845 880 static int graph_link(struct asoc_simple_priv *priv, 846 881 struct graph2_custom_hooks *hooks, 847 882 enum graph_type gtype, ··· 949 814 func = hooks->custom_dpcm; 950 815 else 951 816 func = audio_graph2_link_dpcm; 817 + break; 818 + case GRAPH_C2C: 819 + if (hooks && hooks->custom_c2c) 820 + func = hooks->custom_c2c; 821 + else 822 + func = audio_graph2_link_c2c; 952 823 break; 953 824 default: 954 825 break; ··· 1057 916 return 0; 1058 917 } 1059 918 919 + static int graph_count_c2c(struct asoc_simple_priv *priv, 920 + struct device_node *lnk, 921 + struct link_info *li) 922 + { 923 + struct device_node *ports = of_get_parent(lnk); 924 + struct device_node *port0 = lnk; 925 + struct device_node *port1 = of_get_next_child(ports, lnk); 926 + struct device_node *ep0 = port_to_endpoint(port0); 927 + struct device_node *ep1 = port_to_endpoint(port1); 928 + struct device_node *codec0 = of_graph_get_remote_port(ep0); 929 + struct device_node *codec1 = of_graph_get_remote_port(ep1); 930 + 931 + of_node_get(lnk); 932 + 933 + /* 934 + * codec2codec { 935 + * ports { 936 + * => lnk: port@0 { endpoint { ... }; }; 937 + * port@1 { endpoint { ... }; }; 938 + * }; 939 + * }; 940 + */ 941 + li->num[li->link].cpus = 942 + li->num[li->link].platforms = graph_counter(codec0); 943 + li->num[li->link].codecs = graph_counter(codec1); 944 + li->num[li->link].c2c = 1; 945 + 946 + of_node_put(ports); 947 + of_node_put(port1); 948 + of_node_put(ep0); 949 + of_node_put(ep1); 950 + of_node_put(codec0); 951 + of_node_put(codec1); 952 + 953 + return 0; 954 + } 955 + 1060 956 static int graph_count(struct asoc_simple_priv *priv, 1061 957 struct graph2_custom_hooks *hooks, 1062 958 enum graph_type gtype, ··· 1115 937 break; 1116 938 case GRAPH_DPCM: 1117 939 func = graph_count_dpcm; 940 + break; 941 + case GRAPH_C2C: 942 + func = graph_count_c2c; 1118 943 break; 1119 944 default: 1120 945 break;