Linux Audio

Check our new training course

Loading...
v6.2
  1// SPDX-License-Identifier: GPL-2.0
  2//
  3// ASoC audio graph sound card support
  4//
  5// Copyright (C) 2016 Renesas Solutions Corp.
  6// Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
  7//
  8// based on ${LINUX}/sound/soc/generic/simple-card.c
  9
 
 10#include <linux/clk.h>
 11#include <linux/device.h>
 12#include <linux/gpio.h>
 13#include <linux/gpio/consumer.h>
 14#include <linux/module.h>
 15#include <linux/of.h>
 16#include <linux/of_device.h>
 17#include <linux/of_gpio.h>
 18#include <linux/of_graph.h>
 19#include <linux/platform_device.h>
 20#include <linux/string.h>
 21#include <sound/graph_card.h>
 22
 23#define DPCM_SELECTABLE 1
 24
 
 
 
 
 
 
 
 
 
 
 
 
 25static int graph_outdrv_event(struct snd_soc_dapm_widget *w,
 26			      struct snd_kcontrol *kcontrol,
 27			      int event)
 28{
 29	struct snd_soc_dapm_context *dapm = w->dapm;
 30	struct asoc_simple_priv *priv = snd_soc_card_get_drvdata(dapm->card);
 31
 32	switch (event) {
 33	case SND_SOC_DAPM_POST_PMU:
 34		gpiod_set_value_cansleep(priv->pa_gpio, 1);
 35		break;
 36	case SND_SOC_DAPM_PRE_PMD:
 37		gpiod_set_value_cansleep(priv->pa_gpio, 0);
 38		break;
 39	default:
 40		return -EINVAL;
 41	}
 42
 43	return 0;
 44}
 45
 46static const struct snd_soc_dapm_widget graph_dapm_widgets[] = {
 47	SND_SOC_DAPM_OUT_DRV_E("Amplifier", SND_SOC_NOPM,
 48			       0, 0, NULL, 0, graph_outdrv_event,
 49			       SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
 50};
 51
 52static const struct snd_soc_ops graph_ops = {
 53	.startup	= asoc_simple_startup,
 54	.shutdown	= asoc_simple_shutdown,
 55	.hw_params	= asoc_simple_hw_params,
 56};
 57
 58static int graph_get_dai_id(struct device_node *ep)
 59{
 60	struct device_node *node;
 61	struct device_node *endpoint;
 62	struct of_endpoint info;
 63	int i, id;
 64	const u32 *reg;
 65	int ret;
 66
 67	/* use driver specified DAI ID if exist */
 68	ret = snd_soc_get_dai_id(ep);
 69	if (ret != -ENOTSUPP)
 70		return ret;
 71
 72	/* use endpoint/port reg if exist */
 73	ret = of_graph_parse_endpoint(ep, &info);
 74	if (ret == 0) {
 75		/*
 76		 * Because it will count port/endpoint if it doesn't have "reg".
 77		 * But, we can't judge whether it has "no reg", or "reg = <0>"
 78		 * only of_graph_parse_endpoint().
 79		 * We need to check "reg" property
 80		 */
 81		if (of_get_property(ep,   "reg", NULL))
 82			return info.id;
 83
 84		node = of_get_parent(ep);
 85		reg = of_get_property(node, "reg", NULL);
 86		of_node_put(node);
 87		if (reg)
 88			return info.port;
 89	}
 90	node = of_graph_get_port_parent(ep);
 91
 92	/*
 93	 * Non HDMI sound case, counting port/endpoint on its DT
 94	 * is enough. Let's count it.
 95	 */
 96	i = 0;
 97	id = -1;
 98	for_each_endpoint_of_node(node, endpoint) {
 99		if (endpoint == ep)
100			id = i;
101		i++;
102	}
103
104	of_node_put(node);
105
106	if (id < 0)
107		return -ENODEV;
108
109	return id;
110}
111
112static bool soc_component_is_pcm(struct snd_soc_dai_link_component *dlc)
113{
114	struct snd_soc_dai *dai = snd_soc_find_dai_with_mutex(dlc);
115
116	if (dai && (dai->component->driver->pcm_construct ||
117		    dai->driver->pcm_new))
118		return true;
119
120	return false;
121}
122
123static int asoc_simple_parse_dai(struct device_node *ep,
124				 struct snd_soc_dai_link_component *dlc,
125				 int *is_single_link)
126{
127	struct device_node *node;
128	struct of_phandle_args args;
129	int ret;
130
131	if (!ep)
132		return 0;
133
134	node = of_graph_get_port_parent(ep);
135
136	/* Get dai->name */
137	args.np		= node;
138	args.args[0]	= graph_get_dai_id(ep);
139	args.args_count	= (of_graph_get_endpoint_count(node) > 1);
140
141	/*
142	 * FIXME
143	 *
144	 * Here, dlc->dai_name is pointer to CPU/Codec DAI name.
145	 * If user unbinded CPU or Codec driver, but not for Sound Card,
146	 * dlc->dai_name is keeping unbinded CPU or Codec
147	 * driver's pointer.
148	 *
149	 * If user re-bind CPU or Codec driver again, ALSA SoC will try
150	 * to rebind Card via snd_soc_try_rebind_card(), but because of
151	 * above reason, it might can't bind Sound Card.
152	 * Because Sound Card is pointing to released dai_name pointer.
153	 *
154	 * To avoid this rebind Card issue,
155	 * 1) It needs to alloc memory to keep dai_name eventhough
156	 *    CPU or Codec driver was unbinded, or
157	 * 2) user need to rebind Sound Card everytime
158	 *    if he unbinded CPU or Codec.
159	 */
160	ret = snd_soc_get_dai_name(&args, &dlc->dai_name);
161	if (ret < 0) {
162		of_node_put(node);
163		return ret;
164	}
165
166	dlc->of_node = node;
167
168	if (is_single_link)
169		*is_single_link = of_graph_get_endpoint_count(node) == 1;
170
171	return 0;
172}
173
174static void graph_parse_convert(struct device *dev,
175				struct device_node *ep,
176				struct asoc_simple_data *adata)
177{
178	struct device_node *top = dev->of_node;
179	struct device_node *port = of_get_parent(ep);
180	struct device_node *ports = of_get_parent(port);
181	struct device_node *node = of_graph_get_port_parent(ep);
182
183	asoc_simple_parse_convert(top,   NULL,   adata);
184	if (of_node_name_eq(ports, "ports"))
185		asoc_simple_parse_convert(ports, NULL, adata);
186	asoc_simple_parse_convert(port,  NULL,   adata);
187	asoc_simple_parse_convert(ep,    NULL,   adata);
188
189	of_node_put(port);
190	of_node_put(ports);
191	of_node_put(node);
192}
193
194static void graph_parse_mclk_fs(struct device_node *top,
195				struct device_node *ep,
196				struct simple_dai_props *props)
197{
198	struct device_node *port	= of_get_parent(ep);
199	struct device_node *ports	= of_get_parent(port);
200
201	of_property_read_u32(top,	"mclk-fs", &props->mclk_fs);
202	if (of_node_name_eq(ports, "ports"))
203		of_property_read_u32(ports, "mclk-fs", &props->mclk_fs);
204	of_property_read_u32(port,	"mclk-fs", &props->mclk_fs);
205	of_property_read_u32(ep,	"mclk-fs", &props->mclk_fs);
206
207	of_node_put(port);
208	of_node_put(ports);
209}
210
211static int graph_parse_node(struct asoc_simple_priv *priv,
212			    struct device_node *ep,
213			    struct link_info *li,
214			    int *cpu)
215{
216	struct device *dev = simple_priv_to_dev(priv);
217	struct device_node *top = dev->of_node;
218	struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, li->link);
219	struct simple_dai_props *dai_props = simple_priv_to_props(priv, li->link);
220	struct snd_soc_dai_link_component *dlc;
221	struct asoc_simple_dai *dai;
222	int ret;
223
224	if (cpu) {
225		dlc = asoc_link_to_cpu(dai_link, 0);
226		dai = simple_props_to_dai_cpu(dai_props, 0);
227	} else {
228		dlc = asoc_link_to_codec(dai_link, 0);
229		dai = simple_props_to_dai_codec(dai_props, 0);
230	}
231
232	graph_parse_mclk_fs(top, ep, dai_props);
233
234	ret = asoc_simple_parse_dai(ep, dlc, cpu);
235	if (ret < 0)
236		return ret;
237
238	ret = asoc_simple_parse_tdm(ep, dai);
239	if (ret < 0)
240		return ret;
241
242	ret = asoc_simple_parse_clk(dev, ep, dai, dlc);
243	if (ret < 0)
244		return ret;
245
246	return 0;
247}
248
249static int graph_link_init(struct asoc_simple_priv *priv,
250			   struct device_node *cpu_ep,
251			   struct device_node *codec_ep,
252			   struct link_info *li,
253			   char *name)
254{
255	struct device *dev = simple_priv_to_dev(priv);
 
256	struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, li->link);
 
 
 
 
 
 
 
 
257	int ret;
258
259	ret = asoc_simple_parse_daifmt(dev, cpu_ep, codec_ep,
260				       NULL, &dai_link->dai_fmt);
261	if (ret < 0)
262		return ret;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
263
264	dai_link->init		= asoc_simple_dai_init;
 
 
 
 
 
 
265	dai_link->ops		= &graph_ops;
266	if (priv->ops)
267		dai_link->ops	= priv->ops;
268
269	return asoc_simple_set_dailink_name(dev, dai_link, name);
 
 
 
 
 
 
 
270}
271
272static int graph_dai_link_of_dpcm(struct asoc_simple_priv *priv,
273				  struct device_node *cpu_ep,
274				  struct device_node *codec_ep,
275				  struct link_info *li)
276{
277	struct device *dev = simple_priv_to_dev(priv);
278	struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, li->link);
279	struct simple_dai_props *dai_props = simple_priv_to_props(priv, li->link);
280	struct device_node *top = dev->of_node;
281	struct device_node *ep = li->cpu ? cpu_ep : codec_ep;
282	char dai_name[64];
283	int ret;
284
285	dev_dbg(dev, "link_of DPCM (%pOF)\n", ep);
286
287	if (li->cpu) {
288		struct snd_soc_card *card = simple_priv_to_card(priv);
289		struct snd_soc_dai_link_component *cpus = asoc_link_to_cpu(dai_link, 0);
290		struct snd_soc_dai_link_component *platforms = asoc_link_to_platform(dai_link, 0);
291		int is_single_links = 0;
292
293		/* Codec is dummy */
294
295		/* FE settings */
296		dai_link->dynamic		= 1;
297		dai_link->dpcm_merged_format	= 1;
298
299		ret = graph_parse_node(priv, cpu_ep, li, &is_single_links);
300		if (ret)
301			return ret;
302
303		snprintf(dai_name, sizeof(dai_name),
304			 "fe.%pOFP.%s", cpus->of_node, cpus->dai_name);
305		/*
306		 * In BE<->BE connections it is not required to create
307		 * PCM devices at CPU end of the dai link and thus 'no_pcm'
308		 * flag needs to be set. It is useful when there are many
309		 * BE components and some of these have to be connected to
310		 * form a valid audio path.
311		 *
312		 * For example: FE <-> BE1 <-> BE2 <-> ... <-> BEn where
313		 * there are 'n' BE components in the path.
314		 */
315		if (card->component_chaining && !soc_component_is_pcm(cpus)) {
316			dai_link->no_pcm = 1;
317			dai_link->be_hw_params_fixup = asoc_simple_be_hw_params_fixup;
318		}
319
320		asoc_simple_canonicalize_cpu(cpus, is_single_links);
321		asoc_simple_canonicalize_platform(platforms, cpus);
322	} else {
323		struct snd_soc_codec_conf *cconf = simple_props_to_codec_conf(dai_props, 0);
324		struct snd_soc_dai_link_component *codecs = asoc_link_to_codec(dai_link, 0);
325		struct device_node *port;
326		struct device_node *ports;
327
328		/* CPU is dummy */
329
330		/* BE settings */
331		dai_link->no_pcm		= 1;
332		dai_link->be_hw_params_fixup	= asoc_simple_be_hw_params_fixup;
333
334		ret = graph_parse_node(priv, codec_ep, li, NULL);
335		if (ret < 0)
336			return ret;
337
338		snprintf(dai_name, sizeof(dai_name),
339			 "be.%pOFP.%s", codecs->of_node, codecs->dai_name);
340
341		/* check "prefix" from top node */
342		port = of_get_parent(ep);
343		ports = of_get_parent(port);
344		snd_soc_of_parse_node_prefix(top, cconf, codecs->of_node,
345					      "prefix");
346		if (of_node_name_eq(ports, "ports"))
347			snd_soc_of_parse_node_prefix(ports, cconf, codecs->of_node, "prefix");
348		snd_soc_of_parse_node_prefix(port, cconf, codecs->of_node,
349					     "prefix");
350
351		of_node_put(ports);
352		of_node_put(port);
353	}
354
355	graph_parse_convert(dev, ep, &dai_props->adata);
356
357	snd_soc_dai_link_set_capabilities(dai_link);
358
359	ret = graph_link_init(priv, cpu_ep, codec_ep, li, dai_name);
360
361	li->link++;
362
363	return ret;
364}
365
366static int graph_dai_link_of(struct asoc_simple_priv *priv,
367			     struct device_node *cpu_ep,
368			     struct device_node *codec_ep,
369			     struct link_info *li)
370{
371	struct device *dev = simple_priv_to_dev(priv);
372	struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, li->link);
373	struct snd_soc_dai_link_component *cpus = asoc_link_to_cpu(dai_link, 0);
374	struct snd_soc_dai_link_component *codecs = asoc_link_to_codec(dai_link, 0);
375	struct snd_soc_dai_link_component *platforms = asoc_link_to_platform(dai_link, 0);
376	char dai_name[64];
377	int ret, is_single_links = 0;
378
379	dev_dbg(dev, "link_of (%pOF)\n", cpu_ep);
380
381	ret = graph_parse_node(priv, cpu_ep, li, &is_single_links);
382	if (ret < 0)
383		return ret;
384
385	ret = graph_parse_node(priv, codec_ep, li, NULL);
386	if (ret < 0)
387		return ret;
388
389	snprintf(dai_name, sizeof(dai_name),
390		 "%s-%s", cpus->dai_name, codecs->dai_name);
391
392	asoc_simple_canonicalize_cpu(cpus, is_single_links);
393	asoc_simple_canonicalize_platform(platforms, cpus);
394
395	ret = graph_link_init(priv, cpu_ep, codec_ep, li, dai_name);
396	if (ret < 0)
397		return ret;
398
399	li->link++;
400
401	return 0;
402}
403
404static inline bool parse_as_dpcm_link(struct asoc_simple_priv *priv,
405				      struct device_node *codec_port,
406				      struct asoc_simple_data *adata)
407{
408	if (priv->force_dpcm)
409		return true;
410
411	if (!priv->dpcm_selectable)
412		return false;
413
414	/*
415	 * It is DPCM
416	 * if Codec port has many endpoints,
417	 * or has convert-xxx property
418	 */
419	if ((of_get_child_count(codec_port) > 1) ||
420	    asoc_simple_is_convert_required(adata))
421		return true;
422
423	return false;
424}
425
426static int __graph_for_each_link(struct asoc_simple_priv *priv,
427			struct link_info *li,
428			int (*func_noml)(struct asoc_simple_priv *priv,
429					 struct device_node *cpu_ep,
430					 struct device_node *codec_ep,
431					 struct link_info *li),
432			int (*func_dpcm)(struct asoc_simple_priv *priv,
433					 struct device_node *cpu_ep,
434					 struct device_node *codec_ep,
435					 struct link_info *li))
436{
437	struct of_phandle_iterator it;
438	struct device *dev = simple_priv_to_dev(priv);
439	struct device_node *node = dev->of_node;
440	struct device_node *cpu_port;
441	struct device_node *cpu_ep;
442	struct device_node *codec_ep;
443	struct device_node *codec_port;
444	struct device_node *codec_port_old = NULL;
445	struct asoc_simple_data adata;
446	int rc, ret = 0;
447
448	/* loop for all listed CPU port */
449	of_for_each_phandle(&it, rc, node, "dais", NULL, 0) {
450		cpu_port = it.node;
451		cpu_ep	 = NULL;
452
453		/* loop for all CPU endpoint */
454		while (1) {
455			cpu_ep = of_get_next_child(cpu_port, cpu_ep);
456			if (!cpu_ep)
457				break;
458
459			/* get codec */
460			codec_ep = of_graph_get_remote_endpoint(cpu_ep);
461			codec_port = of_get_parent(codec_ep);
462
463			/* get convert-xxx property */
464			memset(&adata, 0, sizeof(adata));
465			graph_parse_convert(dev, codec_ep, &adata);
466			graph_parse_convert(dev, cpu_ep,   &adata);
467
468			/* check if link requires DPCM parsing */
469			if (parse_as_dpcm_link(priv, codec_port, &adata)) {
470				/*
471				 * Codec endpoint can be NULL for pluggable audio HW.
472				 * Platform DT can populate the Codec endpoint depending on the
473				 * plugged HW.
474				 */
475				/* Do it all CPU endpoint, and 1st Codec endpoint */
476				if (li->cpu ||
477				    ((codec_port_old != codec_port) && codec_ep))
478					ret = func_dpcm(priv, cpu_ep, codec_ep, li);
479			/* else normal sound */
480			} else {
481				if (li->cpu)
482					ret = func_noml(priv, cpu_ep, codec_ep, li);
483			}
484
485			of_node_put(codec_ep);
486			of_node_put(codec_port);
487
488			if (ret < 0) {
489				of_node_put(cpu_ep);
490				return ret;
491			}
492
493			codec_port_old = codec_port;
494		}
495	}
496
497	return 0;
498}
499
500static int graph_for_each_link(struct asoc_simple_priv *priv,
501			       struct link_info *li,
502			       int (*func_noml)(struct asoc_simple_priv *priv,
503						struct device_node *cpu_ep,
504						struct device_node *codec_ep,
505						struct link_info *li),
506			       int (*func_dpcm)(struct asoc_simple_priv *priv,
507						struct device_node *cpu_ep,
508						struct device_node *codec_ep,
509						struct link_info *li))
510{
511	int ret;
512	/*
513	 * Detect all CPU first, and Detect all Codec 2nd.
514	 *
515	 * In Normal sound case, all DAIs are detected
516	 * as "CPU-Codec".
517	 *
518	 * In DPCM sound case,
519	 * all CPUs   are detected as "CPU-dummy", and
520	 * all Codecs are detected as "dummy-Codec".
521	 * To avoid random sub-device numbering,
522	 * detect "dummy-Codec" in last;
523	 */
524	for (li->cpu = 1; li->cpu >= 0; li->cpu--) {
525		ret = __graph_for_each_link(priv, li, func_noml, func_dpcm);
526		if (ret < 0)
527			break;
528	}
529
530	return ret;
531}
532
533static int graph_get_dais_count(struct asoc_simple_priv *priv,
534				struct link_info *li);
535
536int audio_graph_parse_of(struct asoc_simple_priv *priv, struct device *dev)
537{
538	struct snd_soc_card *card = simple_priv_to_card(priv);
539	struct link_info *li;
540	int ret;
541
542	li = devm_kzalloc(dev, sizeof(*li), GFP_KERNEL);
543	if (!li)
544		return -ENOMEM;
545
546	card->owner = THIS_MODULE;
547	card->dev = dev;
548
549	ret = graph_get_dais_count(priv, li);
550	if (ret < 0)
551		return ret;
552
553	if (!li->link)
554		return -EINVAL;
555
556	ret = asoc_simple_init_priv(priv, li);
557	if (ret < 0)
558		return ret;
559
560	priv->pa_gpio = devm_gpiod_get_optional(dev, "pa", GPIOD_OUT_LOW);
561	if (IS_ERR(priv->pa_gpio)) {
562		ret = PTR_ERR(priv->pa_gpio);
563		dev_err(dev, "failed to get amplifier gpio: %d\n", ret);
564		return ret;
565	}
566
567	ret = asoc_simple_parse_widgets(card, NULL);
568	if (ret < 0)
569		return ret;
570
571	ret = asoc_simple_parse_routing(card, NULL);
572	if (ret < 0)
573		return ret;
574
575	memset(li, 0, sizeof(*li));
576	ret = graph_for_each_link(priv, li,
577				  graph_dai_link_of,
578				  graph_dai_link_of_dpcm);
579	if (ret < 0)
580		goto err;
581
582	ret = asoc_simple_parse_card_name(card, NULL);
583	if (ret < 0)
584		goto err;
585
586	snd_soc_card_set_drvdata(card, priv);
587
588	asoc_simple_debug_info(priv);
589
590	ret = devm_snd_soc_register_card(dev, card);
591	if (ret < 0)
592		goto err;
593
594	devm_kfree(dev, li);
595	return 0;
596
597err:
598	asoc_simple_clean_reference(card);
599
600	return dev_err_probe(dev, ret, "parse error\n");
601}
602EXPORT_SYMBOL_GPL(audio_graph_parse_of);
603
604static int graph_count_noml(struct asoc_simple_priv *priv,
605			    struct device_node *cpu_ep,
606			    struct device_node *codec_ep,
607			    struct link_info *li)
608{
609	struct device *dev = simple_priv_to_dev(priv);
610
611	if (li->link >= SNDRV_MAX_LINKS) {
612		dev_err(dev, "too many links\n");
613		return -EINVAL;
614	}
615
 
 
 
 
 
616	li->num[li->link].cpus		= 1;
617	li->num[li->link].codecs	= 1;
618	li->num[li->link].platforms     = 1;
619
 
 
620	li->link += 1; /* 1xCPU-Codec */
621
622	dev_dbg(dev, "Count As Normal\n");
623
624	return 0;
625}
626
627static int graph_count_dpcm(struct asoc_simple_priv *priv,
628			    struct device_node *cpu_ep,
629			    struct device_node *codec_ep,
630			    struct link_info *li)
631{
632	struct device *dev = simple_priv_to_dev(priv);
633
634	if (li->link >= SNDRV_MAX_LINKS) {
635		dev_err(dev, "too many links\n");
636		return -EINVAL;
637	}
638
639	if (li->cpu) {
 
 
 
 
 
640		li->num[li->link].cpus		= 1;
641		li->num[li->link].platforms     = 1;
642
643		li->link++; /* 1xCPU-dummy */
644	} else {
645		li->num[li->link].codecs	= 1;
646
647		li->link++; /* 1xdummy-Codec */
648	}
649
650	dev_dbg(dev, "Count As DPCM\n");
651
652	return 0;
653}
654
655static int graph_get_dais_count(struct asoc_simple_priv *priv,
656				struct link_info *li)
657{
658	/*
659	 * link_num :	number of links.
660	 *		CPU-Codec / CPU-dummy / dummy-Codec
661	 * dais_num :	number of DAIs
662	 * ccnf_num :	number of codec_conf
663	 *		same number for "dummy-Codec"
664	 *
665	 * ex1)
666	 * CPU0 --- Codec0	link : 5
667	 * CPU1 --- Codec1	dais : 7
668	 * CPU2 -/		ccnf : 1
669	 * CPU3 --- Codec2
670	 *
671	 *	=> 5 links = 2xCPU-Codec + 2xCPU-dummy + 1xdummy-Codec
672	 *	=> 7 DAIs  = 4xCPU + 3xCodec
673	 *	=> 1 ccnf  = 1xdummy-Codec
674	 *
675	 * ex2)
676	 * CPU0 --- Codec0	link : 5
677	 * CPU1 --- Codec1	dais : 6
678	 * CPU2 -/		ccnf : 1
679	 * CPU3 -/
680	 *
681	 *	=> 5 links = 1xCPU-Codec + 3xCPU-dummy + 1xdummy-Codec
682	 *	=> 6 DAIs  = 4xCPU + 2xCodec
683	 *	=> 1 ccnf  = 1xdummy-Codec
684	 *
685	 * ex3)
686	 * CPU0 --- Codec0	link : 6
687	 * CPU1 -/		dais : 6
688	 * CPU2 --- Codec1	ccnf : 2
689	 * CPU3 -/
690	 *
691	 *	=> 6 links = 0xCPU-Codec + 4xCPU-dummy + 2xdummy-Codec
692	 *	=> 6 DAIs  = 4xCPU + 2xCodec
693	 *	=> 2 ccnf  = 2xdummy-Codec
694	 *
695	 * ex4)
696	 * CPU0 --- Codec0 (convert-rate)	link : 3
697	 * CPU1 --- Codec1			dais : 4
698	 *					ccnf : 1
699	 *
700	 *	=> 3 links = 1xCPU-Codec + 1xCPU-dummy + 1xdummy-Codec
701	 *	=> 4 DAIs  = 2xCPU + 2xCodec
702	 *	=> 1 ccnf  = 1xdummy-Codec
703	 */
704	return graph_for_each_link(priv, li,
705				   graph_count_noml,
706				   graph_count_dpcm);
707}
708
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
709static int graph_probe(struct platform_device *pdev)
710{
711	struct asoc_simple_priv *priv;
712	struct device *dev = &pdev->dev;
713	struct snd_soc_card *card;
714
715	/* Allocate the private data and the DAI link array */
716	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
717	if (!priv)
718		return -ENOMEM;
719
720	card = simple_priv_to_card(priv);
721	card->dapm_widgets	= graph_dapm_widgets;
722	card->num_dapm_widgets	= ARRAY_SIZE(graph_dapm_widgets);
723	card->probe		= asoc_graph_card_probe;
724
725	if (of_device_get_match_data(dev))
726		priv->dpcm_selectable = 1;
727
728	return audio_graph_parse_of(priv, dev);
729}
730
731static const struct of_device_id graph_of_match[] = {
732	{ .compatible = "audio-graph-card", },
733	{ .compatible = "audio-graph-scu-card",
734	  .data = (void *)DPCM_SELECTABLE },
735	{},
736};
737MODULE_DEVICE_TABLE(of, graph_of_match);
738
739static struct platform_driver graph_card = {
740	.driver = {
741		.name = "asoc-audio-graph-card",
742		.pm = &snd_soc_pm_ops,
743		.of_match_table = graph_of_match,
744	},
745	.probe = graph_probe,
746	.remove = asoc_simple_remove,
747};
748module_platform_driver(graph_card);
749
750MODULE_ALIAS("platform:asoc-audio-graph-card");
751MODULE_LICENSE("GPL v2");
752MODULE_DESCRIPTION("ASoC Audio Graph Sound Card");
753MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");
v6.13.7
  1// SPDX-License-Identifier: GPL-2.0
  2//
  3// ASoC audio graph sound card support
  4//
  5// Copyright (C) 2016 Renesas Solutions Corp.
  6// Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
  7//
  8// based on ${LINUX}/sound/soc/generic/simple-card.c
  9
 10#include <linux/cleanup.h>
 11#include <linux/clk.h>
 12#include <linux/device.h>
 
 13#include <linux/gpio/consumer.h>
 14#include <linux/module.h>
 15#include <linux/of.h>
 
 
 16#include <linux/of_graph.h>
 17#include <linux/platform_device.h>
 18#include <linux/string.h>
 19#include <sound/graph_card.h>
 20
 21#define DPCM_SELECTABLE 1
 22
 23#define ep_to_port(ep)	of_get_parent(ep)
 24static struct device_node *port_to_ports(struct device_node *port)
 25{
 26	struct device_node *ports = of_get_parent(port);
 27
 28	if (!of_node_name_eq(ports, "ports")) {
 29		of_node_put(ports);
 30		return NULL;
 31	}
 32	return ports;
 33}
 34
 35static int graph_outdrv_event(struct snd_soc_dapm_widget *w,
 36			      struct snd_kcontrol *kcontrol,
 37			      int event)
 38{
 39	struct snd_soc_dapm_context *dapm = w->dapm;
 40	struct simple_util_priv *priv = snd_soc_card_get_drvdata(dapm->card);
 41
 42	switch (event) {
 43	case SND_SOC_DAPM_POST_PMU:
 44		gpiod_set_value_cansleep(priv->pa_gpio, 1);
 45		break;
 46	case SND_SOC_DAPM_PRE_PMD:
 47		gpiod_set_value_cansleep(priv->pa_gpio, 0);
 48		break;
 49	default:
 50		return -EINVAL;
 51	}
 52
 53	return 0;
 54}
 55
 56static const struct snd_soc_dapm_widget graph_dapm_widgets[] = {
 57	SND_SOC_DAPM_OUT_DRV_E("Amplifier", SND_SOC_NOPM,
 58			       0, 0, NULL, 0, graph_outdrv_event,
 59			       SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
 60};
 61
 62static const struct snd_soc_ops graph_ops = {
 63	.startup	= simple_util_startup,
 64	.shutdown	= simple_util_shutdown,
 65	.hw_params	= simple_util_hw_params,
 66};
 67
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 68static bool soc_component_is_pcm(struct snd_soc_dai_link_component *dlc)
 69{
 70	struct snd_soc_dai *dai = snd_soc_find_dai_with_mutex(dlc);
 71
 72	if (dai && (dai->component->driver->pcm_construct ||
 73		    (dai->driver->ops && dai->driver->ops->pcm_new)))
 74		return true;
 75
 76	return false;
 77}
 78
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 79static void graph_parse_convert(struct device *dev,
 80				struct device_node *ep,
 81				struct simple_util_data *adata)
 82{
 83	struct device_node *top = dev->of_node;
 84	struct device_node *port = ep_to_port(ep);
 85	struct device_node *ports = port_to_ports(port);
 86	struct device_node *node = of_graph_get_port_parent(ep);
 87
 88	simple_util_parse_convert(top,   NULL,   adata);
 89	simple_util_parse_convert(ports, NULL,   adata);
 90	simple_util_parse_convert(port,  NULL,   adata);
 91	simple_util_parse_convert(ep,    NULL,   adata);
 
 92
 93	of_node_put(port);
 94	of_node_put(ports);
 95	of_node_put(node);
 96}
 97
 98static int graph_parse_node(struct simple_util_priv *priv,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 99			    struct device_node *ep,
100			    struct link_info *li,
101			    int *cpu)
102{
103	struct device *dev = simple_priv_to_dev(priv);
 
104	struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, li->link);
105	struct simple_dai_props *dai_props = simple_priv_to_props(priv, li->link);
106	struct snd_soc_dai_link_component *dlc;
107	struct simple_util_dai *dai;
108	int ret;
109
110	if (cpu) {
111		dlc = snd_soc_link_to_cpu(dai_link, 0);
112		dai = simple_props_to_dai_cpu(dai_props, 0);
113	} else {
114		dlc = snd_soc_link_to_codec(dai_link, 0);
115		dai = simple_props_to_dai_codec(dai_props, 0);
116	}
117
118	ret = graph_util_parse_dai(dev, ep, dlc, cpu);
 
 
119	if (ret < 0)
120		return ret;
121
122	ret = simple_util_parse_tdm(ep, dai);
123	if (ret < 0)
124		return ret;
125
126	ret = simple_util_parse_clk(dev, ep, dai, dlc);
127	if (ret < 0)
128		return ret;
129
130	return 0;
131}
132
133static int graph_link_init(struct simple_util_priv *priv,
134			   struct device_node *ep_cpu,
135			   struct device_node *ep_codec,
136			   struct link_info *li,
137			   char *name)
138{
139	struct device *dev = simple_priv_to_dev(priv);
140	struct device_node *top = dev->of_node;
141	struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, li->link);
142	struct simple_dai_props *dai_props = simple_priv_to_props(priv, li->link);
143	struct device_node *port_cpu = ep_to_port(ep_cpu);
144	struct device_node *port_codec = ep_to_port(ep_codec);
145	struct device_node *ports_cpu = port_to_ports(port_cpu);
146	struct device_node *ports_codec = port_to_ports(port_codec);
147	enum snd_soc_trigger_order trigger_start = SND_SOC_TRIGGER_ORDER_DEFAULT;
148	enum snd_soc_trigger_order trigger_stop  = SND_SOC_TRIGGER_ORDER_DEFAULT;
149	bool playback_only = 0, capture_only = 0;
150	int ret;
151
152	ret = simple_util_parse_daifmt(dev, ep_cpu, ep_codec,
153				       NULL, &dai_link->dai_fmt);
154	if (ret < 0)
155		goto init_end;
156
157	graph_util_parse_link_direction(top,		&playback_only, &capture_only);
158	graph_util_parse_link_direction(port_cpu,	&playback_only, &capture_only);
159	graph_util_parse_link_direction(port_codec,	&playback_only, &capture_only);
160	graph_util_parse_link_direction(ep_cpu,		&playback_only, &capture_only);
161	graph_util_parse_link_direction(ep_codec,	&playback_only, &capture_only);
162
163	of_property_read_u32(top,		"mclk-fs", &dai_props->mclk_fs);
164	of_property_read_u32(ports_cpu,		"mclk-fs", &dai_props->mclk_fs);
165	of_property_read_u32(ports_codec,	"mclk-fs", &dai_props->mclk_fs);
166	of_property_read_u32(port_cpu,		"mclk-fs", &dai_props->mclk_fs);
167	of_property_read_u32(port_codec,	"mclk-fs", &dai_props->mclk_fs);
168	of_property_read_u32(ep_cpu,		"mclk-fs", &dai_props->mclk_fs);
169	of_property_read_u32(ep_codec,		"mclk-fs", &dai_props->mclk_fs);
170
171	graph_util_parse_trigger_order(priv, top,		&trigger_start, &trigger_stop);
172	graph_util_parse_trigger_order(priv, ports_cpu,		&trigger_start, &trigger_stop);
173	graph_util_parse_trigger_order(priv, ports_codec,	&trigger_start, &trigger_stop);
174	graph_util_parse_trigger_order(priv, port_cpu,		&trigger_start, &trigger_stop);
175	graph_util_parse_trigger_order(priv, port_cpu,		&trigger_start, &trigger_stop);
176	graph_util_parse_trigger_order(priv, ep_cpu,		&trigger_start, &trigger_stop);
177	graph_util_parse_trigger_order(priv, ep_codec,		&trigger_start, &trigger_stop);
178
179	dai_link->playback_only	= playback_only;
180	dai_link->capture_only	= capture_only;
181
182	dai_link->trigger_start	= trigger_start;
183	dai_link->trigger_stop	= trigger_stop;
184
185	dai_link->init		= simple_util_dai_init;
186	dai_link->ops		= &graph_ops;
187	if (priv->ops)
188		dai_link->ops	= priv->ops;
189
190	ret = simple_util_set_dailink_name(dev, dai_link, name);
191init_end:
192	of_node_put(ports_cpu);
193	of_node_put(ports_codec);
194	of_node_put(port_cpu);
195	of_node_put(port_codec);
196
197	return ret;
198}
199
200static int graph_dai_link_of_dpcm(struct simple_util_priv *priv,
201				  struct device_node *cpu_ep,
202				  struct device_node *codec_ep,
203				  struct link_info *li)
204{
205	struct device *dev = simple_priv_to_dev(priv);
206	struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, li->link);
207	struct simple_dai_props *dai_props = simple_priv_to_props(priv, li->link);
208	struct device_node *top = dev->of_node;
209	struct device_node *ep = li->cpu ? cpu_ep : codec_ep;
210	char dai_name[64];
211	int ret;
212
213	dev_dbg(dev, "link_of DPCM (%pOF)\n", ep);
214
215	if (li->cpu) {
216		struct snd_soc_card *card = simple_priv_to_card(priv);
217		struct snd_soc_dai_link_component *cpus = snd_soc_link_to_cpu(dai_link, 0);
218		struct snd_soc_dai_link_component *platforms = snd_soc_link_to_platform(dai_link, 0);
219		int is_single_links = 0;
220
221		/* Codec is dummy */
222
223		/* FE settings */
224		dai_link->dynamic		= 1;
225		dai_link->dpcm_merged_format	= 1;
226
227		ret = graph_parse_node(priv, cpu_ep, li, &is_single_links);
228		if (ret)
229			return ret;
230
231		snprintf(dai_name, sizeof(dai_name),
232			 "fe.%pOFP.%s", cpus->of_node, cpus->dai_name);
233		/*
234		 * In BE<->BE connections it is not required to create
235		 * PCM devices at CPU end of the dai link and thus 'no_pcm'
236		 * flag needs to be set. It is useful when there are many
237		 * BE components and some of these have to be connected to
238		 * form a valid audio path.
239		 *
240		 * For example: FE <-> BE1 <-> BE2 <-> ... <-> BEn where
241		 * there are 'n' BE components in the path.
242		 */
243		if (card->component_chaining && !soc_component_is_pcm(cpus)) {
244			dai_link->no_pcm = 1;
245			dai_link->be_hw_params_fixup = simple_util_be_hw_params_fixup;
246		}
247
248		simple_util_canonicalize_cpu(cpus, is_single_links);
249		simple_util_canonicalize_platform(platforms, cpus);
250	} else {
251		struct snd_soc_codec_conf *cconf = simple_props_to_codec_conf(dai_props, 0);
252		struct snd_soc_dai_link_component *codecs = snd_soc_link_to_codec(dai_link, 0);
253		struct device_node *port;
254		struct device_node *ports;
255
256		/* CPU is dummy */
257
258		/* BE settings */
259		dai_link->no_pcm		= 1;
260		dai_link->be_hw_params_fixup	= simple_util_be_hw_params_fixup;
261
262		ret = graph_parse_node(priv, codec_ep, li, NULL);
263		if (ret < 0)
264			return ret;
265
266		snprintf(dai_name, sizeof(dai_name),
267			 "be.%pOFP.%s", codecs->of_node, codecs->dai_name);
268
269		/* check "prefix" from top node */
270		port  = ep_to_port(ep);
271		ports = port_to_ports(port);
272		snd_soc_of_parse_node_prefix(top,   cconf, codecs->of_node, "prefix");
273		snd_soc_of_parse_node_prefix(ports, cconf, codecs->of_node, "prefix");
274		snd_soc_of_parse_node_prefix(port,  cconf, codecs->of_node, "prefix");
 
 
 
275
276		of_node_put(ports);
277		of_node_put(port);
278	}
279
280	graph_parse_convert(dev, ep, &dai_props->adata);
281
 
 
282	ret = graph_link_init(priv, cpu_ep, codec_ep, li, dai_name);
283
284	li->link++;
285
286	return ret;
287}
288
289static int graph_dai_link_of(struct simple_util_priv *priv,
290			     struct device_node *cpu_ep,
291			     struct device_node *codec_ep,
292			     struct link_info *li)
293{
294	struct device *dev = simple_priv_to_dev(priv);
295	struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, li->link);
296	struct snd_soc_dai_link_component *cpus = snd_soc_link_to_cpu(dai_link, 0);
297	struct snd_soc_dai_link_component *codecs = snd_soc_link_to_codec(dai_link, 0);
298	struct snd_soc_dai_link_component *platforms = snd_soc_link_to_platform(dai_link, 0);
299	char dai_name[64];
300	int ret, is_single_links = 0;
301
302	dev_dbg(dev, "link_of (%pOF)\n", cpu_ep);
303
304	ret = graph_parse_node(priv, cpu_ep, li, &is_single_links);
305	if (ret < 0)
306		return ret;
307
308	ret = graph_parse_node(priv, codec_ep, li, NULL);
309	if (ret < 0)
310		return ret;
311
312	snprintf(dai_name, sizeof(dai_name),
313		 "%s-%s", cpus->dai_name, codecs->dai_name);
314
315	simple_util_canonicalize_cpu(cpus, is_single_links);
316	simple_util_canonicalize_platform(platforms, cpus);
317
318	ret = graph_link_init(priv, cpu_ep, codec_ep, li, dai_name);
319	if (ret < 0)
320		return ret;
321
322	li->link++;
323
324	return 0;
325}
326
327static inline bool parse_as_dpcm_link(struct simple_util_priv *priv,
328				      struct device_node *codec_port,
329				      struct simple_util_data *adata)
330{
331	if (priv->force_dpcm)
332		return true;
333
334	if (!priv->dpcm_selectable)
335		return false;
336
337	/*
338	 * It is DPCM
339	 * if Codec port has many endpoints,
340	 * or has convert-xxx property
341	 */
342	if ((of_get_child_count(codec_port) > 1) ||
343	    simple_util_is_convert_required(adata))
344		return true;
345
346	return false;
347}
348
349static int __graph_for_each_link(struct simple_util_priv *priv,
350			struct link_info *li,
351			int (*func_noml)(struct simple_util_priv *priv,
352					 struct device_node *cpu_ep,
353					 struct device_node *codec_ep,
354					 struct link_info *li),
355			int (*func_dpcm)(struct simple_util_priv *priv,
356					 struct device_node *cpu_ep,
357					 struct device_node *codec_ep,
358					 struct link_info *li))
359{
360	struct of_phandle_iterator it;
361	struct device *dev = simple_priv_to_dev(priv);
362	struct device_node *node = dev->of_node;
363	struct device_node *cpu_port;
 
364	struct device_node *codec_ep;
365	struct device_node *codec_port;
366	struct device_node *codec_port_old = NULL;
367	struct simple_util_data adata;
368	int rc, ret = 0;
369
370	/* loop for all listed CPU port */
371	of_for_each_phandle(&it, rc, node, "dais", NULL, 0) {
372		cpu_port = it.node;
 
373
374		/* loop for all CPU endpoint */
375		for_each_of_graph_port_endpoint(cpu_port, cpu_ep) {
 
 
 
 
376			/* get codec */
377			codec_ep = of_graph_get_remote_endpoint(cpu_ep);
378			codec_port = ep_to_port(codec_ep);
379
380			/* get convert-xxx property */
381			memset(&adata, 0, sizeof(adata));
382			graph_parse_convert(dev, codec_ep, &adata);
383			graph_parse_convert(dev, cpu_ep,   &adata);
384
385			/* check if link requires DPCM parsing */
386			if (parse_as_dpcm_link(priv, codec_port, &adata)) {
387				/*
388				 * Codec endpoint can be NULL for pluggable audio HW.
389				 * Platform DT can populate the Codec endpoint depending on the
390				 * plugged HW.
391				 */
392				/* Do it all CPU endpoint, and 1st Codec endpoint */
393				if (li->cpu ||
394				    ((codec_port_old != codec_port) && codec_ep))
395					ret = func_dpcm(priv, cpu_ep, codec_ep, li);
396			/* else normal sound */
397			} else {
398				if (li->cpu)
399					ret = func_noml(priv, cpu_ep, codec_ep, li);
400			}
401
402			of_node_put(codec_ep);
403			of_node_put(codec_port);
404
405			if (ret < 0)
 
406				return ret;
 
407
408			codec_port_old = codec_port;
409		}
410	}
411
412	return 0;
413}
414
415static int graph_for_each_link(struct simple_util_priv *priv,
416			       struct link_info *li,
417			       int (*func_noml)(struct simple_util_priv *priv,
418						struct device_node *cpu_ep,
419						struct device_node *codec_ep,
420						struct link_info *li),
421			       int (*func_dpcm)(struct simple_util_priv *priv,
422						struct device_node *cpu_ep,
423						struct device_node *codec_ep,
424						struct link_info *li))
425{
426	int ret;
427	/*
428	 * Detect all CPU first, and Detect all Codec 2nd.
429	 *
430	 * In Normal sound case, all DAIs are detected
431	 * as "CPU-Codec".
432	 *
433	 * In DPCM sound case,
434	 * all CPUs   are detected as "CPU-dummy", and
435	 * all Codecs are detected as "dummy-Codec".
436	 * To avoid random sub-device numbering,
437	 * detect "dummy-Codec" in last;
438	 */
439	for (li->cpu = 1; li->cpu >= 0; li->cpu--) {
440		ret = __graph_for_each_link(priv, li, func_noml, func_dpcm);
441		if (ret < 0)
442			break;
443	}
444
445	return ret;
446}
447
448static int graph_count_noml(struct simple_util_priv *priv,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
449			    struct device_node *cpu_ep,
450			    struct device_node *codec_ep,
451			    struct link_info *li)
452{
453	struct device *dev = simple_priv_to_dev(priv);
454
455	if (li->link >= SNDRV_MAX_LINKS) {
456		dev_err(dev, "too many links\n");
457		return -EINVAL;
458	}
459
460	/*
461	 * DON'T REMOVE platforms
462	 * see
463	 *	simple-card.c :: simple_count_noml()
464	 */
465	li->num[li->link].cpus		= 1;
 
466	li->num[li->link].platforms     = 1;
467
468	li->num[li->link].codecs	= 1;
469
470	li->link += 1; /* 1xCPU-Codec */
471
472	dev_dbg(dev, "Count As Normal\n");
473
474	return 0;
475}
476
477static int graph_count_dpcm(struct simple_util_priv *priv,
478			    struct device_node *cpu_ep,
479			    struct device_node *codec_ep,
480			    struct link_info *li)
481{
482	struct device *dev = simple_priv_to_dev(priv);
483
484	if (li->link >= SNDRV_MAX_LINKS) {
485		dev_err(dev, "too many links\n");
486		return -EINVAL;
487	}
488
489	if (li->cpu) {
490		/*
491		 * DON'T REMOVE platforms
492		 * see
493		 *	simple-card.c :: simple_count_noml()
494		 */
495		li->num[li->link].cpus		= 1;
496		li->num[li->link].platforms     = 1;
497
498		li->link++; /* 1xCPU-dummy */
499	} else {
500		li->num[li->link].codecs	= 1;
501
502		li->link++; /* 1xdummy-Codec */
503	}
504
505	dev_dbg(dev, "Count As DPCM\n");
506
507	return 0;
508}
509
510static int graph_get_dais_count(struct simple_util_priv *priv,
511				struct link_info *li)
512{
513	/*
514	 * link_num :	number of links.
515	 *		CPU-Codec / CPU-dummy / dummy-Codec
516	 * dais_num :	number of DAIs
517	 * ccnf_num :	number of codec_conf
518	 *		same number for "dummy-Codec"
519	 *
520	 * ex1)
521	 * CPU0 --- Codec0	link : 5
522	 * CPU1 --- Codec1	dais : 7
523	 * CPU2 -/		ccnf : 1
524	 * CPU3 --- Codec2
525	 *
526	 *	=> 5 links = 2xCPU-Codec + 2xCPU-dummy + 1xdummy-Codec
527	 *	=> 7 DAIs  = 4xCPU + 3xCodec
528	 *	=> 1 ccnf  = 1xdummy-Codec
529	 *
530	 * ex2)
531	 * CPU0 --- Codec0	link : 5
532	 * CPU1 --- Codec1	dais : 6
533	 * CPU2 -/		ccnf : 1
534	 * CPU3 -/
535	 *
536	 *	=> 5 links = 1xCPU-Codec + 3xCPU-dummy + 1xdummy-Codec
537	 *	=> 6 DAIs  = 4xCPU + 2xCodec
538	 *	=> 1 ccnf  = 1xdummy-Codec
539	 *
540	 * ex3)
541	 * CPU0 --- Codec0	link : 6
542	 * CPU1 -/		dais : 6
543	 * CPU2 --- Codec1	ccnf : 2
544	 * CPU3 -/
545	 *
546	 *	=> 6 links = 0xCPU-Codec + 4xCPU-dummy + 2xdummy-Codec
547	 *	=> 6 DAIs  = 4xCPU + 2xCodec
548	 *	=> 2 ccnf  = 2xdummy-Codec
549	 *
550	 * ex4)
551	 * CPU0 --- Codec0 (convert-rate)	link : 3
552	 * CPU1 --- Codec1			dais : 4
553	 *					ccnf : 1
554	 *
555	 *	=> 3 links = 1xCPU-Codec + 1xCPU-dummy + 1xdummy-Codec
556	 *	=> 4 DAIs  = 2xCPU + 2xCodec
557	 *	=> 1 ccnf  = 1xdummy-Codec
558	 */
559	return graph_for_each_link(priv, li,
560				   graph_count_noml,
561				   graph_count_dpcm);
562}
563
564int audio_graph_parse_of(struct simple_util_priv *priv, struct device *dev)
565{
566	struct snd_soc_card *card = simple_priv_to_card(priv);
567	int ret;
568
569	struct link_info *li __free(kfree) = kzalloc(sizeof(*li), GFP_KERNEL);
570	if (!li)
571		return -ENOMEM;
572
573	card->owner = THIS_MODULE;
574	card->dev = dev;
575
576	ret = graph_get_dais_count(priv, li);
577	if (ret < 0)
578		return ret;
579
580	if (!li->link)
581		return -EINVAL;
582
583	ret = simple_util_init_priv(priv, li);
584	if (ret < 0)
585		return ret;
586
587	priv->pa_gpio = devm_gpiod_get_optional(dev, "pa", GPIOD_OUT_LOW);
588	if (IS_ERR(priv->pa_gpio)) {
589		ret = PTR_ERR(priv->pa_gpio);
590		dev_err(dev, "failed to get amplifier gpio: %d\n", ret);
591		return ret;
592	}
593
594	ret = simple_util_parse_widgets(card, NULL);
595	if (ret < 0)
596		return ret;
597
598	ret = simple_util_parse_routing(card, NULL);
599	if (ret < 0)
600		return ret;
601
602	memset(li, 0, sizeof(*li));
603	ret = graph_for_each_link(priv, li,
604				  graph_dai_link_of,
605				  graph_dai_link_of_dpcm);
606	if (ret < 0)
607		goto err;
608
609	ret = simple_util_parse_card_name(card, NULL);
610	if (ret < 0)
611		goto err;
612
613	snd_soc_card_set_drvdata(card, priv);
614
615	simple_util_debug_info(priv);
616
617	ret = devm_snd_soc_register_card(dev, card);
618	if (ret < 0)
619		goto err;
620
621	return 0;
622
623err:
624	simple_util_clean_reference(card);
625
626	return dev_err_probe(dev, ret, "parse error\n");
627}
628EXPORT_SYMBOL_GPL(audio_graph_parse_of);
629
630static int graph_probe(struct platform_device *pdev)
631{
632	struct simple_util_priv *priv;
633	struct device *dev = &pdev->dev;
634	struct snd_soc_card *card;
635
636	/* Allocate the private data and the DAI link array */
637	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
638	if (!priv)
639		return -ENOMEM;
640
641	card = simple_priv_to_card(priv);
642	card->dapm_widgets	= graph_dapm_widgets;
643	card->num_dapm_widgets	= ARRAY_SIZE(graph_dapm_widgets);
644	card->probe		= graph_util_card_probe;
645
646	if (of_device_get_match_data(dev))
647		priv->dpcm_selectable = 1;
648
649	return audio_graph_parse_of(priv, dev);
650}
651
652static const struct of_device_id graph_of_match[] = {
653	{ .compatible = "audio-graph-card", },
654	{ .compatible = "audio-graph-scu-card",
655	  .data = (void *)DPCM_SELECTABLE },
656	{},
657};
658MODULE_DEVICE_TABLE(of, graph_of_match);
659
660static struct platform_driver graph_card = {
661	.driver = {
662		.name = "asoc-audio-graph-card",
663		.pm = &snd_soc_pm_ops,
664		.of_match_table = graph_of_match,
665	},
666	.probe = graph_probe,
667	.remove = simple_util_remove,
668};
669module_platform_driver(graph_card);
670
671MODULE_ALIAS("platform:asoc-audio-graph-card");
672MODULE_LICENSE("GPL v2");
673MODULE_DESCRIPTION("ASoC Audio Graph Sound Card");
674MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");