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>");
v5.14.15
  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		return ret;
 
163
164	dlc->of_node = node;
165
166	if (is_single_link)
167		*is_single_link = of_graph_get_endpoint_count(node) == 1;
168
169	return 0;
170}
171
172static void graph_parse_convert(struct device *dev,
173				struct device_node *ep,
174				struct asoc_simple_data *adata)
175{
176	struct device_node *top = dev->of_node;
177	struct device_node *port = of_get_parent(ep);
178	struct device_node *ports = of_get_parent(port);
179	struct device_node *node = of_graph_get_port_parent(ep);
180
181	asoc_simple_parse_convert(top,   NULL,   adata);
182	if (of_node_name_eq(ports, "ports"))
183		asoc_simple_parse_convert(ports, NULL, adata);
184	asoc_simple_parse_convert(port,  NULL,   adata);
185	asoc_simple_parse_convert(ep,    NULL,   adata);
186
187	of_node_put(port);
188	of_node_put(ports);
189	of_node_put(node);
190}
191
192static void graph_parse_mclk_fs(struct device_node *top,
193				struct device_node *ep,
194				struct simple_dai_props *props)
195{
196	struct device_node *port	= of_get_parent(ep);
197	struct device_node *ports	= of_get_parent(port);
198
199	of_property_read_u32(top,	"mclk-fs", &props->mclk_fs);
200	if (of_node_name_eq(ports, "ports"))
201		of_property_read_u32(ports, "mclk-fs", &props->mclk_fs);
202	of_property_read_u32(port,	"mclk-fs", &props->mclk_fs);
203	of_property_read_u32(ep,	"mclk-fs", &props->mclk_fs);
204
205	of_node_put(port);
206	of_node_put(ports);
207}
208
209static int graph_parse_node(struct asoc_simple_priv *priv,
210			    struct device_node *ep,
211			    struct link_info *li,
212			    int *cpu)
213{
214	struct device *dev = simple_priv_to_dev(priv);
215	struct device_node *top = dev->of_node;
216	struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, li->link);
217	struct simple_dai_props *dai_props = simple_priv_to_props(priv, li->link);
218	struct snd_soc_dai_link_component *dlc;
219	struct asoc_simple_dai *dai;
220	int ret;
221
222	if (cpu) {
223		dlc = asoc_link_to_cpu(dai_link, 0);
224		dai = simple_props_to_dai_cpu(dai_props, 0);
225	} else {
226		dlc = asoc_link_to_codec(dai_link, 0);
227		dai = simple_props_to_dai_codec(dai_props, 0);
228	}
229
230	graph_parse_mclk_fs(top, ep, dai_props);
231
232	ret = asoc_simple_parse_dai(ep, dlc, cpu);
233	if (ret < 0)
234		return ret;
235
236	ret = asoc_simple_parse_tdm(ep, dai);
237	if (ret < 0)
238		return ret;
239
240	ret = asoc_simple_parse_clk(dev, ep, dai, dlc);
241	if (ret < 0)
242		return ret;
243
244	return 0;
245}
246
247static int graph_link_init(struct asoc_simple_priv *priv,
248			   struct device_node *cpu_ep,
249			   struct device_node *codec_ep,
250			   struct link_info *li,
251			   char *name)
252{
253	struct device *dev = simple_priv_to_dev(priv);
254	struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, li->link);
255	int ret;
256
257	ret = asoc_simple_parse_daifmt(dev, cpu_ep, codec_ep,
258				       NULL, &dai_link->dai_fmt);
259	if (ret < 0)
260		return ret;
261
262	dai_link->init		= asoc_simple_dai_init;
263	dai_link->ops		= &graph_ops;
264	if (priv->ops)
265		dai_link->ops	= priv->ops;
266
267	return asoc_simple_set_dailink_name(dev, dai_link, name);
268}
269
270static int graph_dai_link_of_dpcm(struct asoc_simple_priv *priv,
271				  struct device_node *cpu_ep,
272				  struct device_node *codec_ep,
273				  struct link_info *li)
274{
275	struct device *dev = simple_priv_to_dev(priv);
276	struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, li->link);
277	struct simple_dai_props *dai_props = simple_priv_to_props(priv, li->link);
278	struct device_node *top = dev->of_node;
279	struct device_node *ep = li->cpu ? cpu_ep : codec_ep;
280	char dai_name[64];
281	int ret;
282
283	dev_dbg(dev, "link_of DPCM (%pOF)\n", ep);
284
285	if (li->cpu) {
286		struct snd_soc_card *card = simple_priv_to_card(priv);
287		struct snd_soc_dai_link_component *cpus = asoc_link_to_cpu(dai_link, 0);
288		struct snd_soc_dai_link_component *platforms = asoc_link_to_platform(dai_link, 0);
289		int is_single_links = 0;
290
291		/* Codec is dummy */
292
293		/* FE settings */
294		dai_link->dynamic		= 1;
295		dai_link->dpcm_merged_format	= 1;
296
297		ret = graph_parse_node(priv, cpu_ep, li, &is_single_links);
298		if (ret)
299			return ret;
300
301		snprintf(dai_name, sizeof(dai_name),
302			 "fe.%pOFP.%s", cpus->of_node, cpus->dai_name);
303		/*
304		 * In BE<->BE connections it is not required to create
305		 * PCM devices at CPU end of the dai link and thus 'no_pcm'
306		 * flag needs to be set. It is useful when there are many
307		 * BE components and some of these have to be connected to
308		 * form a valid audio path.
309		 *
310		 * For example: FE <-> BE1 <-> BE2 <-> ... <-> BEn where
311		 * there are 'n' BE components in the path.
312		 */
313		if (card->component_chaining && !soc_component_is_pcm(cpus))
314			dai_link->no_pcm = 1;
 
 
315
316		asoc_simple_canonicalize_cpu(cpus, is_single_links);
317		asoc_simple_canonicalize_platform(platforms, cpus);
318	} else {
319		struct snd_soc_codec_conf *cconf = simple_props_to_codec_conf(dai_props, 0);
320		struct snd_soc_dai_link_component *codecs = asoc_link_to_codec(dai_link, 0);
321		struct device_node *port;
322		struct device_node *ports;
323
324		/* CPU is dummy */
325
326		/* BE settings */
327		dai_link->no_pcm		= 1;
328		dai_link->be_hw_params_fixup	= asoc_simple_be_hw_params_fixup;
329
330		ret = graph_parse_node(priv, codec_ep, li, NULL);
331		if (ret < 0)
332			return ret;
333
334		snprintf(dai_name, sizeof(dai_name),
335			 "be.%pOFP.%s", codecs->of_node, codecs->dai_name);
336
337		/* check "prefix" from top node */
338		port = of_get_parent(ep);
339		ports = of_get_parent(port);
340		snd_soc_of_parse_node_prefix(top, cconf, codecs->of_node,
341					      "prefix");
342		if (of_node_name_eq(ports, "ports"))
343			snd_soc_of_parse_node_prefix(ports, cconf, codecs->of_node, "prefix");
344		snd_soc_of_parse_node_prefix(port, cconf, codecs->of_node,
345					     "prefix");
346
347		of_node_put(ports);
348		of_node_put(port);
349	}
350
351	graph_parse_convert(dev, ep, &dai_props->adata);
352
353	snd_soc_dai_link_set_capabilities(dai_link);
354
355	ret = graph_link_init(priv, cpu_ep, codec_ep, li, dai_name);
356
357	li->link++;
358
359	return ret;
360}
361
362static int graph_dai_link_of(struct asoc_simple_priv *priv,
363			     struct device_node *cpu_ep,
364			     struct device_node *codec_ep,
365			     struct link_info *li)
366{
367	struct device *dev = simple_priv_to_dev(priv);
368	struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, li->link);
369	struct snd_soc_dai_link_component *cpus = asoc_link_to_cpu(dai_link, 0);
370	struct snd_soc_dai_link_component *codecs = asoc_link_to_codec(dai_link, 0);
371	struct snd_soc_dai_link_component *platforms = asoc_link_to_platform(dai_link, 0);
372	char dai_name[64];
373	int ret, is_single_links = 0;
374
375	dev_dbg(dev, "link_of (%pOF)\n", cpu_ep);
376
377	ret = graph_parse_node(priv, cpu_ep, li, &is_single_links);
378	if (ret < 0)
379		return ret;
380
381	ret = graph_parse_node(priv, codec_ep, li, NULL);
382	if (ret < 0)
383		return ret;
384
385	snprintf(dai_name, sizeof(dai_name),
386		 "%s-%s", cpus->dai_name, codecs->dai_name);
387
388	asoc_simple_canonicalize_cpu(cpus, is_single_links);
389	asoc_simple_canonicalize_platform(platforms, cpus);
390
391	ret = graph_link_init(priv, cpu_ep, codec_ep, li, dai_name);
392	if (ret < 0)
393		return ret;
394
395	li->link++;
396
397	return 0;
398}
399
400static inline bool parse_as_dpcm_link(struct asoc_simple_priv *priv,
401				      struct device_node *codec_port,
402				      struct asoc_simple_data *adata)
403{
404	if (priv->force_dpcm)
405		return true;
406
407	if (!priv->dpcm_selectable)
408		return false;
409
410	/*
411	 * It is DPCM
412	 * if Codec port has many endpoints,
413	 * or has convert-xxx property
414	 */
415	if ((of_get_child_count(codec_port) > 1) ||
416	    (adata->convert_rate || adata->convert_channels))
417		return true;
418
419	return false;
420}
421
422static int __graph_for_each_link(struct asoc_simple_priv *priv,
423			struct link_info *li,
424			int (*func_noml)(struct asoc_simple_priv *priv,
425					 struct device_node *cpu_ep,
426					 struct device_node *codec_ep,
427					 struct link_info *li),
428			int (*func_dpcm)(struct asoc_simple_priv *priv,
429					 struct device_node *cpu_ep,
430					 struct device_node *codec_ep,
431					 struct link_info *li))
432{
433	struct of_phandle_iterator it;
434	struct device *dev = simple_priv_to_dev(priv);
435	struct device_node *node = dev->of_node;
436	struct device_node *cpu_port;
437	struct device_node *cpu_ep;
438	struct device_node *codec_ep;
439	struct device_node *codec_port;
440	struct device_node *codec_port_old = NULL;
441	struct asoc_simple_data adata;
442	int rc, ret = 0;
443
444	/* loop for all listed CPU port */
445	of_for_each_phandle(&it, rc, node, "dais", NULL, 0) {
446		cpu_port = it.node;
447		cpu_ep	 = NULL;
448
449		/* loop for all CPU endpoint */
450		while (1) {
451			cpu_ep = of_get_next_child(cpu_port, cpu_ep);
452			if (!cpu_ep)
453				break;
454
455			/* get codec */
456			codec_ep = of_graph_get_remote_endpoint(cpu_ep);
457			codec_port = of_get_parent(codec_ep);
458
459			/* get convert-xxx property */
460			memset(&adata, 0, sizeof(adata));
461			graph_parse_convert(dev, codec_ep, &adata);
462			graph_parse_convert(dev, cpu_ep,   &adata);
463
464			/* check if link requires DPCM parsing */
465			if (parse_as_dpcm_link(priv, codec_port, &adata)) {
466				/*
467				 * Codec endpoint can be NULL for pluggable audio HW.
468				 * Platform DT can populate the Codec endpoint depending on the
469				 * plugged HW.
470				 */
471				/* Do it all CPU endpoint, and 1st Codec endpoint */
472				if (li->cpu ||
473				    ((codec_port_old != codec_port) && codec_ep))
474					ret = func_dpcm(priv, cpu_ep, codec_ep, li);
475			/* else normal sound */
476			} else {
477				if (li->cpu)
478					ret = func_noml(priv, cpu_ep, codec_ep, li);
479			}
480
481			of_node_put(codec_ep);
482			of_node_put(codec_port);
483
484			if (ret < 0)
 
485				return ret;
 
486
487			codec_port_old = codec_port;
488		}
489	}
490
491	return 0;
492}
493
494static int graph_for_each_link(struct asoc_simple_priv *priv,
495			       struct link_info *li,
496			       int (*func_noml)(struct asoc_simple_priv *priv,
497						struct device_node *cpu_ep,
498						struct device_node *codec_ep,
499						struct link_info *li),
500			       int (*func_dpcm)(struct asoc_simple_priv *priv,
501						struct device_node *cpu_ep,
502						struct device_node *codec_ep,
503						struct link_info *li))
504{
505	int ret;
506	/*
507	 * Detect all CPU first, and Detect all Codec 2nd.
508	 *
509	 * In Normal sound case, all DAIs are detected
510	 * as "CPU-Codec".
511	 *
512	 * In DPCM sound case,
513	 * all CPUs   are detected as "CPU-dummy", and
514	 * all Codecs are detected as "dummy-Codec".
515	 * To avoid random sub-device numbering,
516	 * detect "dummy-Codec" in last;
517	 */
518	for (li->cpu = 1; li->cpu >= 0; li->cpu--) {
519		ret = __graph_for_each_link(priv, li, func_noml, func_dpcm);
520		if (ret < 0)
521			break;
522	}
523
524	return ret;
525}
526
527static int graph_get_dais_count(struct asoc_simple_priv *priv,
528				struct link_info *li);
529
530int audio_graph_parse_of(struct asoc_simple_priv *priv, struct device *dev)
531{
532	struct snd_soc_card *card = simple_priv_to_card(priv);
533	struct link_info *li;
534	int ret;
535
536	li = devm_kzalloc(dev, sizeof(*li), GFP_KERNEL);
537	if (!li)
538		return -ENOMEM;
539
540	card->owner = THIS_MODULE;
541	card->dev = dev;
542
543	ret = graph_get_dais_count(priv, li);
544	if (ret < 0)
545		return ret;
546
547	if (!li->link)
548		return -EINVAL;
549
550	ret = asoc_simple_init_priv(priv, li);
551	if (ret < 0)
552		return ret;
553
554	priv->pa_gpio = devm_gpiod_get_optional(dev, "pa", GPIOD_OUT_LOW);
555	if (IS_ERR(priv->pa_gpio)) {
556		ret = PTR_ERR(priv->pa_gpio);
557		dev_err(dev, "failed to get amplifier gpio: %d\n", ret);
558		return ret;
559	}
560
561	ret = asoc_simple_parse_widgets(card, NULL);
562	if (ret < 0)
563		return ret;
564
565	ret = asoc_simple_parse_routing(card, NULL);
566	if (ret < 0)
567		return ret;
568
569	memset(li, 0, sizeof(*li));
570	ret = graph_for_each_link(priv, li,
571				  graph_dai_link_of,
572				  graph_dai_link_of_dpcm);
573	if (ret < 0)
574		goto err;
575
576	ret = asoc_simple_parse_card_name(card, NULL);
577	if (ret < 0)
578		goto err;
579
580	snd_soc_card_set_drvdata(card, priv);
581
582	asoc_simple_debug_info(priv);
583
584	ret = devm_snd_soc_register_card(dev, card);
585	if (ret < 0)
586		goto err;
587
588	devm_kfree(dev, li);
589	return 0;
590
591err:
592	asoc_simple_clean_reference(card);
593
594	if (ret != -EPROBE_DEFER)
595		dev_err(dev, "parse error %d\n", ret);
596
597	return ret;
598}
599EXPORT_SYMBOL_GPL(audio_graph_parse_of);
600
601static int graph_count_noml(struct asoc_simple_priv *priv,
602			    struct device_node *cpu_ep,
603			    struct device_node *codec_ep,
604			    struct link_info *li)
605{
606	struct device *dev = simple_priv_to_dev(priv);
607
608	if (li->link >= SNDRV_MAX_LINKS) {
609		dev_err(dev, "too many links\n");
610		return -EINVAL;
611	}
612
613	li->num[li->link].cpus		= 1;
614	li->num[li->link].codecs	= 1;
615	li->num[li->link].platforms     = 1;
616
617	li->link += 1; /* 1xCPU-Codec */
618
619	dev_dbg(dev, "Count As Normal\n");
620
621	return 0;
622}
623
624static int graph_count_dpcm(struct asoc_simple_priv *priv,
625			    struct device_node *cpu_ep,
626			    struct device_node *codec_ep,
627			    struct link_info *li)
628{
629	struct device *dev = simple_priv_to_dev(priv);
630
631	if (li->link >= SNDRV_MAX_LINKS) {
632		dev_err(dev, "too many links\n");
633		return -EINVAL;
634	}
635
636	if (li->cpu) {
637		li->num[li->link].cpus		= 1;
638		li->num[li->link].platforms     = 1;
639
640		li->link++; /* 1xCPU-dummy */
641	} else {
642		li->num[li->link].codecs	= 1;
643
644		li->link++; /* 1xdummy-Codec */
645	}
646
647	dev_dbg(dev, "Count As DPCM\n");
648
649	return 0;
650}
651
652static int graph_get_dais_count(struct asoc_simple_priv *priv,
653				struct link_info *li)
654{
655	/*
656	 * link_num :	number of links.
657	 *		CPU-Codec / CPU-dummy / dummy-Codec
658	 * dais_num :	number of DAIs
659	 * ccnf_num :	number of codec_conf
660	 *		same number for "dummy-Codec"
661	 *
662	 * ex1)
663	 * CPU0 --- Codec0	link : 5
664	 * CPU1 --- Codec1	dais : 7
665	 * CPU2 -/		ccnf : 1
666	 * CPU3 --- Codec2
667	 *
668	 *	=> 5 links = 2xCPU-Codec + 2xCPU-dummy + 1xdummy-Codec
669	 *	=> 7 DAIs  = 4xCPU + 3xCodec
670	 *	=> 1 ccnf  = 1xdummy-Codec
671	 *
672	 * ex2)
673	 * CPU0 --- Codec0	link : 5
674	 * CPU1 --- Codec1	dais : 6
675	 * CPU2 -/		ccnf : 1
676	 * CPU3 -/
677	 *
678	 *	=> 5 links = 1xCPU-Codec + 3xCPU-dummy + 1xdummy-Codec
679	 *	=> 6 DAIs  = 4xCPU + 2xCodec
680	 *	=> 1 ccnf  = 1xdummy-Codec
681	 *
682	 * ex3)
683	 * CPU0 --- Codec0	link : 6
684	 * CPU1 -/		dais : 6
685	 * CPU2 --- Codec1	ccnf : 2
686	 * CPU3 -/
687	 *
688	 *	=> 6 links = 0xCPU-Codec + 4xCPU-dummy + 2xdummy-Codec
689	 *	=> 6 DAIs  = 4xCPU + 2xCodec
690	 *	=> 2 ccnf  = 2xdummy-Codec
691	 *
692	 * ex4)
693	 * CPU0 --- Codec0 (convert-rate)	link : 3
694	 * CPU1 --- Codec1			dais : 4
695	 *					ccnf : 1
696	 *
697	 *	=> 3 links = 1xCPU-Codec + 1xCPU-dummy + 1xdummy-Codec
698	 *	=> 4 DAIs  = 2xCPU + 2xCodec
699	 *	=> 1 ccnf  = 1xdummy-Codec
700	 */
701	return graph_for_each_link(priv, li,
702				   graph_count_noml,
703				   graph_count_dpcm);
704}
705
706static int graph_probe(struct platform_device *pdev)
707{
708	struct asoc_simple_priv *priv;
709	struct device *dev = &pdev->dev;
710	struct snd_soc_card *card;
711
712	/* Allocate the private data and the DAI link array */
713	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
714	if (!priv)
715		return -ENOMEM;
716
717	card = simple_priv_to_card(priv);
718	card->dapm_widgets	= graph_dapm_widgets;
719	card->num_dapm_widgets	= ARRAY_SIZE(graph_dapm_widgets);
720	card->probe		= asoc_graph_card_probe;
721
722	if (of_device_get_match_data(dev))
723		priv->dpcm_selectable = 1;
724
725	return audio_graph_parse_of(priv, dev);
726}
727
728static const struct of_device_id graph_of_match[] = {
729	{ .compatible = "audio-graph-card", },
730	{ .compatible = "audio-graph-scu-card",
731	  .data = (void *)DPCM_SELECTABLE },
732	{},
733};
734MODULE_DEVICE_TABLE(of, graph_of_match);
735
736static struct platform_driver graph_card = {
737	.driver = {
738		.name = "asoc-audio-graph-card",
739		.pm = &snd_soc_pm_ops,
740		.of_match_table = graph_of_match,
741	},
742	.probe = graph_probe,
743	.remove = asoc_simple_remove,
744};
745module_platform_driver(graph_card);
746
747MODULE_ALIAS("platform:asoc-audio-graph-card");
748MODULE_LICENSE("GPL v2");
749MODULE_DESCRIPTION("ASoC Audio Graph Sound Card");
750MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");