xref: /linux/sound/soc/generic/audio-graph-card.c (revision c532de5a67a70f8533d495f8f2aaa9a0491c3ad0)
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)
24 static 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 
35 static 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 
56 static 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 
62 static 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 
68 static 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 
79 static 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 
98 static 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 
133 static 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);
191 init_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 
200 static 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 
289 static 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 
327 static 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 
349 static 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_child_of_node_scoped(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 
415 static 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 
448 static 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 
477 static 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 
510 static 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 
564 int 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 
623 err:
624 	simple_util_clean_reference(card);
625 
626 	return dev_err_probe(dev, ret, "parse error\n");
627 }
628 EXPORT_SYMBOL_GPL(audio_graph_parse_of);
629 
630 static 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 
652 static 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 };
658 MODULE_DEVICE_TABLE(of, graph_of_match);
659 
660 static 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 };
669 module_platform_driver(graph_card);
670 
671 MODULE_ALIAS("platform:asoc-audio-graph-card");
672 MODULE_LICENSE("GPL v2");
673 MODULE_DESCRIPTION("ASoC Audio Graph Sound Card");
674 MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");
675