File Index
Files and directories
Sign in
FILE

audio-graph-card.c

File preview with syntax highlighting.

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