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/simple_card_utils.h>
22 
23 struct graph_card_data {
24 	struct snd_soc_card snd_card;
25 	struct graph_dai_props {
26 		struct asoc_simple_dai *cpu_dai;
27 		struct asoc_simple_dai *codec_dai;
28 		struct snd_soc_dai_link_component codecs; /* single codec */
29 		struct snd_soc_dai_link_component platform;
30 		struct asoc_simple_card_data adata;
31 		struct snd_soc_codec_conf *codec_conf;
32 		unsigned int mclk_fs;
33 	} *dai_props;
34 	struct asoc_simple_jack hp_jack;
35 	struct asoc_simple_jack mic_jack;
36 	struct snd_soc_dai_link *dai_link;
37 	struct asoc_simple_dai *dais;
38 	struct snd_soc_codec_conf *codec_conf;
39 	struct gpio_desc *pa_gpio;
40 };
41 
42 #define graph_priv_to_card(priv) (&(priv)->snd_card)
43 #define graph_priv_to_props(priv, i) ((priv)->dai_props + (i))
44 #define graph_priv_to_dev(priv) (graph_priv_to_card(priv)->dev)
45 #define graph_priv_to_link(priv, i) (graph_priv_to_card(priv)->dai_link + (i))
46 
47 #define PREFIX	"audio-graph-card,"
48 
49 static int asoc_graph_card_outdrv_event(struct snd_soc_dapm_widget *w,
50 					struct snd_kcontrol *kcontrol,
51 					int event)
52 {
53 	struct snd_soc_dapm_context *dapm = w->dapm;
54 	struct graph_card_data *priv = snd_soc_card_get_drvdata(dapm->card);
55 
56 	switch (event) {
57 	case SND_SOC_DAPM_POST_PMU:
58 		gpiod_set_value_cansleep(priv->pa_gpio, 1);
59 		break;
60 	case SND_SOC_DAPM_PRE_PMD:
61 		gpiod_set_value_cansleep(priv->pa_gpio, 0);
62 		break;
63 	default:
64 		return -EINVAL;
65 	}
66 
67 	return 0;
68 }
69 
70 static const struct snd_soc_dapm_widget asoc_graph_card_dapm_widgets[] = {
71 	SND_SOC_DAPM_OUT_DRV_E("Amplifier", SND_SOC_NOPM,
72 			       0, 0, NULL, 0, asoc_graph_card_outdrv_event,
73 			       SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
74 };
75 
76 static int asoc_graph_card_startup(struct snd_pcm_substream *substream)
77 {
78 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
79 	struct graph_card_data *priv = snd_soc_card_get_drvdata(rtd->card);
80 	struct graph_dai_props *dai_props = graph_priv_to_props(priv, rtd->num);
81 	int ret;
82 
83 	ret = asoc_simple_card_clk_enable(dai_props->cpu_dai);
84 	if (ret)
85 		return ret;
86 
87 	ret = asoc_simple_card_clk_enable(dai_props->codec_dai);
88 	if (ret)
89 		asoc_simple_card_clk_disable(dai_props->cpu_dai);
90 
91 	return ret;
92 }
93 
94 static void asoc_graph_card_shutdown(struct snd_pcm_substream *substream)
95 {
96 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
97 	struct graph_card_data *priv = snd_soc_card_get_drvdata(rtd->card);
98 	struct graph_dai_props *dai_props = graph_priv_to_props(priv, rtd->num);
99 
100 	asoc_simple_card_clk_disable(dai_props->cpu_dai);
101 
102 	asoc_simple_card_clk_disable(dai_props->codec_dai);
103 }
104 
105 static int asoc_graph_card_hw_params(struct snd_pcm_substream *substream,
106 				     struct snd_pcm_hw_params *params)
107 {
108 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
109 	struct snd_soc_dai *codec_dai = rtd->codec_dai;
110 	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
111 	struct graph_card_data *priv = snd_soc_card_get_drvdata(rtd->card);
112 	struct graph_dai_props *dai_props = graph_priv_to_props(priv, rtd->num);
113 	unsigned int mclk, mclk_fs = 0;
114 	int ret = 0;
115 
116 	if (dai_props->mclk_fs)
117 		mclk_fs = dai_props->mclk_fs;
118 
119 	if (mclk_fs) {
120 		mclk = params_rate(params) * mclk_fs;
121 		ret = snd_soc_dai_set_sysclk(codec_dai, 0, mclk,
122 					     SND_SOC_CLOCK_IN);
123 		if (ret && ret != -ENOTSUPP)
124 			goto err;
125 
126 		ret = snd_soc_dai_set_sysclk(cpu_dai, 0, mclk,
127 					     SND_SOC_CLOCK_OUT);
128 		if (ret && ret != -ENOTSUPP)
129 			goto err;
130 	}
131 	return 0;
132 err:
133 	return ret;
134 }
135 
136 static const struct snd_soc_ops asoc_graph_card_ops = {
137 	.startup = asoc_graph_card_startup,
138 	.shutdown = asoc_graph_card_shutdown,
139 	.hw_params = asoc_graph_card_hw_params,
140 };
141 
142 static int asoc_graph_card_dai_init(struct snd_soc_pcm_runtime *rtd)
143 {
144 	struct graph_card_data *priv =	snd_soc_card_get_drvdata(rtd->card);
145 	struct graph_dai_props *dai_props = graph_priv_to_props(priv, rtd->num);
146 	int ret = 0;
147 
148 	ret = asoc_simple_card_init_dai(rtd->codec_dai,
149 					dai_props->codec_dai);
150 	if (ret < 0)
151 		return ret;
152 
153 	ret = asoc_simple_card_init_dai(rtd->cpu_dai,
154 					dai_props->cpu_dai);
155 	if (ret < 0)
156 		return ret;
157 
158 	return 0;
159 }
160 
161 static int asoc_graph_card_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
162 					      struct snd_pcm_hw_params *params)
163 {
164 	struct graph_card_data *priv = snd_soc_card_get_drvdata(rtd->card);
165 	struct graph_dai_props *dai_props = graph_priv_to_props(priv, rtd->num);
166 
167 	asoc_simple_card_convert_fixup(&dai_props->adata, params);
168 
169 	return 0;
170 }
171 
172 static int asoc_graph_card_dai_link_of_dpcm(struct device_node *top,
173 					    struct device_node *cpu_ep,
174 					    struct device_node *codec_ep,
175 					    struct graph_card_data *priv,
176 					    int *dai_idx, int link_idx,
177 					    int *conf_idx, int is_cpu)
178 {
179 	struct device *dev = graph_priv_to_dev(priv);
180 	struct snd_soc_dai_link *dai_link = graph_priv_to_link(priv, link_idx);
181 	struct graph_dai_props *dai_props = graph_priv_to_props(priv, link_idx);
182 	struct device_node *ep = is_cpu ? cpu_ep : codec_ep;
183 	struct device_node *port = of_get_parent(ep);
184 	struct device_node *ports = of_get_parent(port);
185 	struct device_node *node = of_graph_get_port_parent(ep);
186 	struct asoc_simple_dai *dai;
187 	struct snd_soc_dai_link_component *codecs = dai_link->codecs;
188 	int ret;
189 
190 	dev_dbg(dev, "link_of DPCM (for %s)\n", is_cpu ? "CPU" : "Codec");
191 
192 	of_property_read_u32(top,   "mclk-fs", &dai_props->mclk_fs);
193 	of_property_read_u32(ports, "mclk-fs", &dai_props->mclk_fs);
194 	of_property_read_u32(port,  "mclk-fs", &dai_props->mclk_fs);
195 	of_property_read_u32(ep,    "mclk-fs", &dai_props->mclk_fs);
196 
197 	asoc_simple_card_parse_convert(dev, top,   NULL,   &dai_props->adata);
198 	asoc_simple_card_parse_convert(dev, node,  PREFIX, &dai_props->adata);
199 	asoc_simple_card_parse_convert(dev, ports, NULL,   &dai_props->adata);
200 	asoc_simple_card_parse_convert(dev, port,  NULL,   &dai_props->adata);
201 	asoc_simple_card_parse_convert(dev, ep,    NULL,   &dai_props->adata);
202 
203 	of_node_put(ports);
204 	of_node_put(port);
205 
206 	if (is_cpu) {
207 
208 		/* BE is dummy */
209 		codecs->of_node		= NULL;
210 		codecs->dai_name	= "snd-soc-dummy-dai";
211 		codecs->name		= "snd-soc-dummy";
212 
213 		/* FE settings */
214 		dai_link->dynamic		= 1;
215 		dai_link->dpcm_merged_format	= 1;
216 
217 		dai =
218 		dai_props->cpu_dai	= &priv->dais[(*dai_idx)++];
219 
220 		ret = asoc_simple_card_parse_graph_cpu(ep, dai_link);
221 		if (ret)
222 			return ret;
223 
224 		ret = asoc_simple_card_parse_clk_cpu(dev, ep, dai_link, dai);
225 		if (ret < 0)
226 			return ret;
227 
228 		ret = asoc_simple_card_set_dailink_name(dev, dai_link,
229 							"fe.%s",
230 							dai_link->cpu_dai_name);
231 		if (ret < 0)
232 			return ret;
233 
234 		/* card->num_links includes Codec */
235 		asoc_simple_card_canonicalize_cpu(dai_link,
236 			of_graph_get_endpoint_count(dai_link->cpu_of_node) == 1);
237 	} else {
238 		struct snd_soc_codec_conf *cconf;
239 
240 		/* FE is dummy */
241 		dai_link->cpu_of_node		= NULL;
242 		dai_link->cpu_dai_name		= "snd-soc-dummy-dai";
243 		dai_link->cpu_name		= "snd-soc-dummy";
244 
245 		/* BE settings */
246 		dai_link->no_pcm		= 1;
247 		dai_link->be_hw_params_fixup	= asoc_graph_card_be_hw_params_fixup;
248 
249 		dai =
250 		dai_props->codec_dai	= &priv->dais[(*dai_idx)++];
251 
252 		cconf =
253 		dai_props->codec_conf	= &priv->codec_conf[(*conf_idx)++];
254 
255 		ret = asoc_simple_card_parse_graph_codec(ep, dai_link);
256 		if (ret < 0)
257 			return ret;
258 
259 		ret = asoc_simple_card_parse_clk_codec(dev, ep, dai_link, dai);
260 		if (ret < 0)
261 			return ret;
262 
263 		ret = asoc_simple_card_set_dailink_name(dev, dai_link,
264 							"be.%s",
265 							codecs->dai_name);
266 		if (ret < 0)
267 			return ret;
268 
269 		/* check "prefix" from top node */
270 		snd_soc_of_parse_node_prefix(top, cconf, codecs->of_node,
271 					      "prefix");
272 		snd_soc_of_parse_node_prefix(node, cconf, codecs->of_node,
273 					     PREFIX "prefix");
274 		snd_soc_of_parse_node_prefix(ports, cconf, codecs->of_node,
275 					     "prefix");
276 		snd_soc_of_parse_node_prefix(port, cconf, codecs->of_node,
277 					     "prefix");
278 	}
279 
280 	ret = asoc_simple_card_of_parse_tdm(ep, dai);
281 	if (ret)
282 		return ret;
283 
284 	ret = asoc_simple_card_canonicalize_dailink(dai_link);
285 	if (ret < 0)
286 		return ret;
287 
288 	ret = asoc_simple_card_parse_daifmt(dev, cpu_ep, codec_ep,
289 					    NULL, &dai_link->dai_fmt);
290 	if (ret < 0)
291 		return ret;
292 
293 	dai_link->dpcm_playback		= 1;
294 	dai_link->dpcm_capture		= 1;
295 	dai_link->ops			= &asoc_graph_card_ops;
296 	dai_link->init			= asoc_graph_card_dai_init;
297 
298 	return 0;
299 }
300 
301 static int asoc_graph_card_dai_link_of(struct device_node *top,
302 					struct device_node *cpu_ep,
303 					struct device_node *codec_ep,
304 					struct graph_card_data *priv,
305 					int *dai_idx, int link_idx)
306 {
307 	struct device *dev = graph_priv_to_dev(priv);
308 	struct snd_soc_dai_link *dai_link = graph_priv_to_link(priv, link_idx);
309 	struct graph_dai_props *dai_props = graph_priv_to_props(priv, link_idx);
310 	struct device_node *cpu_port = of_get_parent(cpu_ep);
311 	struct device_node *codec_port = of_get_parent(codec_ep);
312 	struct device_node *cpu_ports = of_get_parent(cpu_port);
313 	struct device_node *codec_ports = of_get_parent(codec_port);
314 	struct asoc_simple_dai *cpu_dai;
315 	struct asoc_simple_dai *codec_dai;
316 	int ret;
317 
318 	dev_dbg(dev, "link_of\n");
319 
320 	cpu_dai			=
321 	dai_props->cpu_dai	= &priv->dais[(*dai_idx)++];
322 	codec_dai		=
323 	dai_props->codec_dai	= &priv->dais[(*dai_idx)++];
324 
325 	/* Factor to mclk, used in hw_params() */
326 	of_property_read_u32(top,         "mclk-fs", &dai_props->mclk_fs);
327 	of_property_read_u32(cpu_ports,   "mclk-fs", &dai_props->mclk_fs);
328 	of_property_read_u32(codec_ports, "mclk-fs", &dai_props->mclk_fs);
329 	of_property_read_u32(cpu_port,    "mclk-fs", &dai_props->mclk_fs);
330 	of_property_read_u32(codec_port,  "mclk-fs", &dai_props->mclk_fs);
331 	of_property_read_u32(cpu_ep,      "mclk-fs", &dai_props->mclk_fs);
332 	of_property_read_u32(codec_ep,    "mclk-fs", &dai_props->mclk_fs);
333 	of_node_put(cpu_port);
334 	of_node_put(cpu_ports);
335 	of_node_put(codec_port);
336 	of_node_put(codec_ports);
337 
338 	ret = asoc_simple_card_parse_daifmt(dev, cpu_ep, codec_ep,
339 					    NULL, &dai_link->dai_fmt);
340 	if (ret < 0)
341 		return ret;
342 
343 	ret = asoc_simple_card_parse_graph_cpu(cpu_ep, dai_link);
344 	if (ret < 0)
345 		return ret;
346 
347 	ret = asoc_simple_card_parse_graph_codec(codec_ep, dai_link);
348 	if (ret < 0)
349 		return ret;
350 
351 	ret = asoc_simple_card_of_parse_tdm(cpu_ep, cpu_dai);
352 	if (ret < 0)
353 		return ret;
354 
355 	ret = asoc_simple_card_of_parse_tdm(codec_ep, codec_dai);
356 	if (ret < 0)
357 		return ret;
358 
359 	ret = asoc_simple_card_parse_clk_cpu(dev, cpu_ep, dai_link, cpu_dai);
360 	if (ret < 0)
361 		return ret;
362 
363 	ret = asoc_simple_card_parse_clk_codec(dev, codec_ep, dai_link, codec_dai);
364 	if (ret < 0)
365 		return ret;
366 
367 	ret = asoc_simple_card_canonicalize_dailink(dai_link);
368 	if (ret < 0)
369 		return ret;
370 
371 	ret = asoc_simple_card_set_dailink_name(dev, dai_link,
372 						"%s-%s",
373 						dai_link->cpu_dai_name,
374 						dai_link->codecs->dai_name);
375 	if (ret < 0)
376 		return ret;
377 
378 	dai_link->ops = &asoc_graph_card_ops;
379 	dai_link->init = asoc_graph_card_dai_init;
380 
381 	asoc_simple_card_canonicalize_cpu(dai_link,
382 		of_graph_get_endpoint_count(dai_link->cpu_of_node) == 1);
383 
384 	return 0;
385 }
386 
387 static int asoc_graph_card_parse_of(struct graph_card_data *priv)
388 {
389 	struct of_phandle_iterator it;
390 	struct device *dev = graph_priv_to_dev(priv);
391 	struct snd_soc_card *card = graph_priv_to_card(priv);
392 	struct device_node *top = dev->of_node;
393 	struct device_node *node = top;
394 	struct device_node *cpu_port;
395 	struct device_node *cpu_ep		= NULL;
396 	struct device_node *codec_ep		= NULL;
397 	struct device_node *codec_port		= NULL;
398 	struct device_node *codec_port_old	= NULL;
399 	int rc, ret;
400 	int link_idx, dai_idx, conf_idx;
401 	int cpu;
402 
403 	ret = asoc_simple_card_of_parse_widgets(card, NULL);
404 	if (ret < 0)
405 		return ret;
406 
407 	ret = asoc_simple_card_of_parse_routing(card, NULL);
408 	if (ret < 0)
409 		return ret;
410 
411 	link_idx	= 0;
412 	dai_idx		= 0;
413 	conf_idx	= 0;
414 	codec_port_old	= NULL;
415 	for (cpu = 1; cpu >= 0; cpu--) {
416 		/*
417 		 * Detect all CPU first, and Detect all Codec 2nd.
418 		 *
419 		 * In Normal sound case, all DAIs are detected
420 		 * as "CPU-Codec".
421 		 *
422 		 * In DPCM sound case,
423 		 * all CPUs   are detected as "CPU-dummy", and
424 		 * all Codecs are detected as "dummy-Codec".
425 		 * To avoid random sub-device numbering,
426 		 * detect "dummy-Codec" in last;
427 		 */
428 		of_for_each_phandle(&it, rc, node, "dais", NULL, 0) {
429 			cpu_port = it.node;
430 			cpu_ep	 = NULL;
431 			while (1) {
432 				cpu_ep = of_get_next_child(cpu_port, cpu_ep);
433 				if (!cpu_ep)
434 					break;
435 
436 				codec_ep   = of_graph_get_remote_endpoint(cpu_ep);
437 				codec_port = of_get_parent(codec_ep);
438 
439 				of_node_put(codec_ep);
440 				of_node_put(codec_port);
441 
442 				dev_dbg(dev, "%pOFf <-> %pOFf\n", cpu_ep, codec_ep);
443 
444 				if (of_get_child_count(codec_port) > 1) {
445 					/*
446 					 * for DPCM sound
447 					 */
448 					if (!cpu) {
449 						if (codec_port_old == codec_port)
450 							continue;
451 						codec_port_old = codec_port;
452 					}
453 					ret = asoc_graph_card_dai_link_of_dpcm(
454 						top, cpu_ep, codec_ep, priv,
455 						&dai_idx, link_idx++,
456 						&conf_idx, cpu);
457 				} else if (cpu) {
458 					/*
459 					 * for Normal sound
460 					 */
461 					ret = asoc_graph_card_dai_link_of(
462 						top, cpu_ep, codec_ep, priv,
463 						&dai_idx, link_idx++);
464 				}
465 				if (ret < 0)
466 					return ret;
467 			}
468 		}
469 	}
470 
471 	return asoc_simple_card_parse_card_name(card, NULL);
472 }
473 
474 static void asoc_graph_get_dais_count(struct device *dev,
475 				      int *link_num,
476 				      int *dais_num,
477 				      int *ccnf_num)
478 {
479 	struct of_phandle_iterator it;
480 	struct device_node *node = dev->of_node;
481 	struct device_node *cpu_port;
482 	struct device_node *cpu_ep;
483 	struct device_node *codec_ep;
484 	struct device_node *codec_port;
485 	struct device_node *codec_port_old;
486 	struct device_node *codec_port_old2;
487 	int rc;
488 
489 	/*
490 	 * link_num :	number of links.
491 	 *		CPU-Codec / CPU-dummy / dummy-Codec
492 	 * dais_num :	number of DAIs
493 	 * ccnf_num :	number of codec_conf
494 	 *		same number for "dummy-Codec"
495 	 *
496 	 * ex1)
497 	 * CPU0 --- Codec0	link : 5
498 	 * CPU1 --- Codec1	dais : 7
499 	 * CPU2 -/		ccnf : 1
500 	 * CPU3 --- Codec2
501 	 *
502 	 *	=> 5 links = 2xCPU-Codec + 2xCPU-dummy + 1xdummy-Codec
503 	 *	=> 7 DAIs  = 4xCPU + 3xCodec
504 	 *	=> 1 ccnf  = 1xdummy-Codec
505 	 *
506 	 * ex2)
507 	 * CPU0 --- Codec0	link : 5
508 	 * CPU1 --- Codec1	dais : 6
509 	 * CPU2 -/		ccnf : 1
510 	 * CPU3 -/
511 	 *
512 	 *	=> 5 links = 1xCPU-Codec + 3xCPU-dummy + 1xdummy-Codec
513 	 *	=> 6 DAIs  = 4xCPU + 2xCodec
514 	 *	=> 1 ccnf  = 1xdummy-Codec
515 	 *
516 	 * ex3)
517 	 * CPU0 --- Codec0	link : 6
518 	 * CPU1 -/		dais : 6
519 	 * CPU2 --- Codec1	ccnf : 2
520 	 * CPU3 -/
521 	 *
522 	 *	=> 6 links = 0xCPU-Codec + 4xCPU-dummy + 2xdummy-Codec
523 	 *	=> 6 DAIs  = 4xCPU + 2xCodec
524 	 *	=> 2 ccnf  = 2xdummy-Codec
525 	 */
526 	codec_port_old = NULL;
527 	codec_port_old2 = NULL;
528 	of_for_each_phandle(&it, rc, node, "dais", NULL, 0) {
529 		cpu_port = it.node;
530 		cpu_ep	 = NULL;
531 		while (1) {
532 			cpu_ep = of_get_next_child(cpu_port, cpu_ep);
533 			if (!cpu_ep)
534 				break;
535 
536 			codec_ep = of_graph_get_remote_endpoint(cpu_ep);
537 			codec_port = of_get_parent(codec_ep);
538 
539 			of_node_put(codec_ep);
540 			of_node_put(codec_port);
541 
542 			(*link_num)++;
543 			(*dais_num)++;
544 
545 			if (codec_port_old == codec_port) {
546 				if (codec_port_old2 != codec_port_old) {
547 					(*link_num)++;
548 					(*ccnf_num)++;
549 				}
550 
551 				codec_port_old2 = codec_port_old;
552 				continue;
553 			}
554 
555 			(*dais_num)++;
556 			codec_port_old = codec_port;
557 		}
558 	}
559 }
560 
561 static int asoc_graph_soc_card_probe(struct snd_soc_card *card)
562 {
563 	struct graph_card_data *priv = snd_soc_card_get_drvdata(card);
564 	int ret;
565 
566 	ret = asoc_simple_card_init_hp(card, &priv->hp_jack, NULL);
567 	if (ret < 0)
568 		return ret;
569 
570 	ret = asoc_simple_card_init_mic(card, &priv->mic_jack, NULL);
571 	if (ret < 0)
572 		return ret;
573 
574 	return 0;
575 }
576 
577 static int asoc_graph_card_probe(struct platform_device *pdev)
578 {
579 	struct graph_card_data *priv;
580 	struct snd_soc_dai_link *dai_link;
581 	struct graph_dai_props *dai_props;
582 	struct asoc_simple_dai *dais;
583 	struct device *dev = &pdev->dev;
584 	struct snd_soc_card *card;
585 	struct snd_soc_codec_conf *cconf;
586 	int lnum = 0, dnum = 0, cnum = 0;
587 	int ret, i;
588 
589 	/* Allocate the private data and the DAI link array */
590 	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
591 	if (!priv)
592 		return -ENOMEM;
593 
594 	asoc_graph_get_dais_count(dev, &lnum, &dnum, &cnum);
595 	if (!lnum || !dnum)
596 		return -EINVAL;
597 
598 	dai_props = devm_kcalloc(dev, lnum, sizeof(*dai_props), GFP_KERNEL);
599 	dai_link  = devm_kcalloc(dev, lnum, sizeof(*dai_link),  GFP_KERNEL);
600 	dais      = devm_kcalloc(dev, dnum, sizeof(*dais),      GFP_KERNEL);
601 	cconf     = devm_kcalloc(dev, cnum, sizeof(*cconf),     GFP_KERNEL);
602 	if (!dai_props || !dai_link || !dais)
603 		return -ENOMEM;
604 
605 	/*
606 	 * Use snd_soc_dai_link_component instead of legacy style
607 	 * It is codec only. but cpu/platform will be supported in the future.
608 	 * see
609 	 *	soc-core.c :: snd_soc_init_multicodec()
610 	 */
611 	for (i = 0; i < lnum; i++) {
612 		dai_link[i].codecs	= &dai_props[i].codecs;
613 		dai_link[i].num_codecs	= 1;
614 		dai_link[i].platform	= &dai_props[i].platform;
615 	}
616 
617 	priv->pa_gpio = devm_gpiod_get_optional(dev, "pa", GPIOD_OUT_LOW);
618 	if (IS_ERR(priv->pa_gpio)) {
619 		ret = PTR_ERR(priv->pa_gpio);
620 		dev_err(dev, "failed to get amplifier gpio: %d\n", ret);
621 		return ret;
622 	}
623 
624 	priv->dai_props			= dai_props;
625 	priv->dai_link			= dai_link;
626 	priv->dais			= dais;
627 	priv->codec_conf		= cconf;
628 
629 	/* Init snd_soc_card */
630 	card = graph_priv_to_card(priv);
631 	card->owner		= THIS_MODULE;
632 	card->dev		= dev;
633 	card->dai_link		= dai_link;
634 	card->num_links		= lnum;
635 	card->dapm_widgets	= asoc_graph_card_dapm_widgets;
636 	card->num_dapm_widgets	= ARRAY_SIZE(asoc_graph_card_dapm_widgets);
637 	card->probe		= asoc_graph_soc_card_probe;
638 	card->codec_conf	= cconf;
639 	card->num_configs	= cnum;
640 
641 	ret = asoc_graph_card_parse_of(priv);
642 	if (ret < 0) {
643 		if (ret != -EPROBE_DEFER)
644 			dev_err(dev, "parse error %d\n", ret);
645 		goto err;
646 	}
647 
648 	snd_soc_card_set_drvdata(card, priv);
649 
650 	ret = devm_snd_soc_register_card(dev, card);
651 	if (ret < 0)
652 		goto err;
653 
654 	return 0;
655 err:
656 	asoc_simple_card_clean_reference(card);
657 
658 	return ret;
659 }
660 
661 static int asoc_graph_card_remove(struct platform_device *pdev)
662 {
663 	struct snd_soc_card *card = platform_get_drvdata(pdev);
664 
665 	return asoc_simple_card_clean_reference(card);
666 }
667 
668 static const struct of_device_id asoc_graph_of_match[] = {
669 	{ .compatible = "audio-graph-card", },
670 	{ .compatible = "audio-graph-scu-card", },
671 	{},
672 };
673 MODULE_DEVICE_TABLE(of, asoc_graph_of_match);
674 
675 static struct platform_driver asoc_graph_card = {
676 	.driver = {
677 		.name = "asoc-audio-graph-card",
678 		.pm = &snd_soc_pm_ops,
679 		.of_match_table = asoc_graph_of_match,
680 	},
681 	.probe = asoc_graph_card_probe,
682 	.remove = asoc_graph_card_remove,
683 };
684 module_platform_driver(asoc_graph_card);
685 
686 MODULE_ALIAS("platform:asoc-audio-graph-card");
687 MODULE_LICENSE("GPL v2");
688 MODULE_DESCRIPTION("ASoC Audio Graph Sound Card");
689 MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");
690