brintos

brintos / linux-shallow public Read only

0
0
Text · 34.3 KiB · 93eee40 Raw
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