xref: /openbmc/linux/sound/soc/meson/axg-card.c (revision 81de3bf3)
1 // SPDX-License-Identifier: (GPL-2.0 OR MIT)
2 //
3 // Copyright (c) 2018 BayLibre, SAS.
4 // Author: Jerome Brunet <jbrunet@baylibre.com>
5 
6 #include <linux/module.h>
7 #include <linux/of_platform.h>
8 #include <sound/soc.h>
9 #include <sound/soc-dai.h>
10 
11 #include "axg-tdm.h"
12 
13 struct axg_card {
14 	struct snd_soc_card card;
15 	void **link_data;
16 };
17 
18 struct axg_dai_link_tdm_mask {
19 	u32 tx;
20 	u32 rx;
21 };
22 
23 struct axg_dai_link_tdm_data {
24 	unsigned int mclk_fs;
25 	unsigned int slots;
26 	unsigned int slot_width;
27 	u32 *tx_mask;
28 	u32 *rx_mask;
29 	struct axg_dai_link_tdm_mask *codec_masks;
30 };
31 
32 /*
33  * Base params for the codec to codec links
34  * Those will be over-written by the CPU side of the link
35  */
36 static const struct snd_soc_pcm_stream codec_params = {
37 	.formats = SNDRV_PCM_FMTBIT_S24_LE,
38 	.rate_min = 5525,
39 	.rate_max = 192000,
40 	.channels_min = 1,
41 	.channels_max = 8,
42 };
43 
44 #define PREFIX "amlogic,"
45 
46 static int axg_card_reallocate_links(struct axg_card *priv,
47 				     unsigned int num_links)
48 {
49 	struct snd_soc_dai_link *links;
50 	void **ldata;
51 
52 	links = krealloc(priv->card.dai_link,
53 			 num_links * sizeof(*priv->card.dai_link),
54 			 GFP_KERNEL | __GFP_ZERO);
55 	ldata = krealloc(priv->link_data,
56 			 num_links * sizeof(*priv->link_data),
57 			 GFP_KERNEL | __GFP_ZERO);
58 
59 	if (!links || !ldata) {
60 		dev_err(priv->card.dev, "failed to allocate links\n");
61 		return -ENOMEM;
62 	}
63 
64 	priv->card.dai_link = links;
65 	priv->link_data = ldata;
66 	priv->card.num_links = num_links;
67 	return 0;
68 }
69 
70 static int axg_card_parse_dai(struct snd_soc_card *card,
71 			      struct device_node *node,
72 			      struct device_node **dai_of_node,
73 			      const char **dai_name)
74 {
75 	struct of_phandle_args args;
76 	int ret;
77 
78 	if (!dai_name || !dai_of_node || !node)
79 		return -EINVAL;
80 
81 	ret = of_parse_phandle_with_args(node, "sound-dai",
82 					 "#sound-dai-cells", 0, &args);
83 	if (ret) {
84 		if (ret != -EPROBE_DEFER)
85 			dev_err(card->dev, "can't parse dai %d\n", ret);
86 		return ret;
87 	}
88 	*dai_of_node = args.np;
89 
90 	return snd_soc_get_dai_name(&args, dai_name);
91 }
92 
93 static int axg_card_set_link_name(struct snd_soc_card *card,
94 				  struct snd_soc_dai_link *link,
95 				  struct device_node *node,
96 				  const char *prefix)
97 {
98 	char *name = devm_kasprintf(card->dev, GFP_KERNEL, "%s.%s",
99 				    prefix, node->full_name);
100 	if (!name)
101 		return -ENOMEM;
102 
103 	link->name = name;
104 	link->stream_name = name;
105 
106 	return 0;
107 }
108 
109 static void axg_card_clean_references(struct axg_card *priv)
110 {
111 	struct snd_soc_card *card = &priv->card;
112 	struct snd_soc_dai_link *link;
113 	struct snd_soc_dai_link_component *codec;
114 	struct snd_soc_aux_dev *aux;
115 	int i, j;
116 
117 	if (card->dai_link) {
118 		for_each_card_prelinks(card, i, link) {
119 			if (link->cpus)
120 				of_node_put(link->cpus->of_node);
121 			for_each_link_codecs(link, j, codec)
122 				of_node_put(codec->of_node);
123 		}
124 	}
125 
126 	if (card->aux_dev) {
127 		for_each_card_pre_auxs(card, i, aux)
128 			of_node_put(aux->dlc.of_node);
129 	}
130 
131 	kfree(card->dai_link);
132 	kfree(priv->link_data);
133 }
134 
135 static int axg_card_add_aux_devices(struct snd_soc_card *card)
136 {
137 	struct device_node *node = card->dev->of_node;
138 	struct snd_soc_aux_dev *aux;
139 	int num, i;
140 
141 	num = of_count_phandle_with_args(node, "audio-aux-devs", NULL);
142 	if (num == -ENOENT) {
143 		/*
144 		 * It is ok to have no auxiliary devices but for this card it
145 		 * is a strange situtation. Let's warn the about it.
146 		 */
147 		dev_warn(card->dev, "card has no auxiliary devices\n");
148 		return 0;
149 	} else if (num < 0) {
150 		dev_err(card->dev, "error getting auxiliary devices: %d\n",
151 			num);
152 		return num;
153 	}
154 
155 	aux = devm_kcalloc(card->dev, num, sizeof(*aux), GFP_KERNEL);
156 	if (!aux)
157 		return -ENOMEM;
158 	card->aux_dev = aux;
159 	card->num_aux_devs = num;
160 
161 	for_each_card_pre_auxs(card, i, aux) {
162 		aux->dlc.of_node =
163 			of_parse_phandle(node, "audio-aux-devs", i);
164 		if (!aux->dlc.of_node)
165 			return -EINVAL;
166 	}
167 
168 	return 0;
169 }
170 
171 static int axg_card_tdm_be_hw_params(struct snd_pcm_substream *substream,
172 				     struct snd_pcm_hw_params *params)
173 {
174 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
175 	struct axg_card *priv = snd_soc_card_get_drvdata(rtd->card);
176 	struct axg_dai_link_tdm_data *be =
177 		(struct axg_dai_link_tdm_data *)priv->link_data[rtd->num];
178 	struct snd_soc_dai *codec_dai;
179 	unsigned int mclk;
180 	int ret, i;
181 
182 	if (be->mclk_fs) {
183 		mclk = params_rate(params) * be->mclk_fs;
184 
185 		for_each_rtd_codec_dai(rtd, i, codec_dai) {
186 			ret = snd_soc_dai_set_sysclk(codec_dai, 0, mclk,
187 						     SND_SOC_CLOCK_IN);
188 			if (ret && ret != -ENOTSUPP)
189 				return ret;
190 		}
191 
192 		ret = snd_soc_dai_set_sysclk(rtd->cpu_dai, 0, mclk,
193 					     SND_SOC_CLOCK_OUT);
194 		if (ret && ret != -ENOTSUPP)
195 			return ret;
196 	}
197 
198 	return 0;
199 }
200 
201 static const struct snd_soc_ops axg_card_tdm_be_ops = {
202 	.hw_params = axg_card_tdm_be_hw_params,
203 };
204 
205 static int axg_card_tdm_dai_init(struct snd_soc_pcm_runtime *rtd)
206 {
207 	struct axg_card *priv = snd_soc_card_get_drvdata(rtd->card);
208 	struct axg_dai_link_tdm_data *be =
209 		(struct axg_dai_link_tdm_data *)priv->link_data[rtd->num];
210 	struct snd_soc_dai *codec_dai;
211 	int ret, i;
212 
213 	for_each_rtd_codec_dai(rtd, i, codec_dai) {
214 		ret = snd_soc_dai_set_tdm_slot(codec_dai,
215 					       be->codec_masks[i].tx,
216 					       be->codec_masks[i].rx,
217 					       be->slots, be->slot_width);
218 		if (ret && ret != -ENOTSUPP) {
219 			dev_err(codec_dai->dev,
220 				"setting tdm link slots failed\n");
221 			return ret;
222 		}
223 	}
224 
225 	ret = axg_tdm_set_tdm_slots(rtd->cpu_dai, be->tx_mask, be->rx_mask,
226 				    be->slots, be->slot_width);
227 	if (ret) {
228 		dev_err(rtd->cpu_dai->dev, "setting tdm link slots failed\n");
229 		return ret;
230 	}
231 
232 	return 0;
233 }
234 
235 static int axg_card_tdm_dai_lb_init(struct snd_soc_pcm_runtime *rtd)
236 {
237 	struct axg_card *priv = snd_soc_card_get_drvdata(rtd->card);
238 	struct axg_dai_link_tdm_data *be =
239 		(struct axg_dai_link_tdm_data *)priv->link_data[rtd->num];
240 	int ret;
241 
242 	/* The loopback rx_mask is the pad tx_mask */
243 	ret = axg_tdm_set_tdm_slots(rtd->cpu_dai, NULL, be->tx_mask,
244 				    be->slots, be->slot_width);
245 	if (ret) {
246 		dev_err(rtd->cpu_dai->dev, "setting tdm link slots failed\n");
247 		return ret;
248 	}
249 
250 	return 0;
251 }
252 
253 static int axg_card_add_tdm_loopback(struct snd_soc_card *card,
254 				     int *index)
255 {
256 	struct axg_card *priv = snd_soc_card_get_drvdata(card);
257 	struct snd_soc_dai_link *pad = &card->dai_link[*index];
258 	struct snd_soc_dai_link *lb;
259 	struct snd_soc_dai_link_component *dlc;
260 	int ret;
261 
262 	/* extend links */
263 	ret = axg_card_reallocate_links(priv, card->num_links + 1);
264 	if (ret)
265 		return ret;
266 
267 	lb = &card->dai_link[*index + 1];
268 
269 	lb->name = kasprintf(GFP_KERNEL, "%s-lb", pad->name);
270 	if (!lb->name)
271 		return -ENOMEM;
272 
273 	dlc = devm_kzalloc(card->dev, 2 * sizeof(*dlc), GFP_KERNEL);
274 	if (!dlc)
275 		return -ENOMEM;
276 
277 	lb->cpus = &dlc[0];
278 	lb->codecs = &dlc[1];
279 	lb->num_cpus = 1;
280 	lb->num_codecs = 1;
281 
282 	lb->stream_name = lb->name;
283 	lb->cpus->of_node = pad->cpus->of_node;
284 	lb->cpus->dai_name = "TDM Loopback";
285 	lb->codecs->name = "snd-soc-dummy";
286 	lb->codecs->dai_name = "snd-soc-dummy-dai";
287 	lb->dpcm_capture = 1;
288 	lb->no_pcm = 1;
289 	lb->ops = &axg_card_tdm_be_ops;
290 	lb->init = axg_card_tdm_dai_lb_init;
291 
292 	/* Provide the same link data to the loopback */
293 	priv->link_data[*index + 1] = priv->link_data[*index];
294 
295 	/*
296 	 * axg_card_clean_references() will iterate over this link,
297 	 * make sure the node count is balanced
298 	 */
299 	of_node_get(lb->cpus->of_node);
300 
301 	/* Let add_links continue where it should */
302 	*index += 1;
303 
304 	return 0;
305 }
306 
307 static unsigned int axg_card_parse_daifmt(struct device_node *node,
308 					  struct device_node *cpu_node)
309 {
310 	struct device_node *bitclkmaster = NULL;
311 	struct device_node *framemaster = NULL;
312 	unsigned int daifmt;
313 
314 	daifmt = snd_soc_of_parse_daifmt(node, PREFIX,
315 					 &bitclkmaster, &framemaster);
316 	daifmt &= ~SND_SOC_DAIFMT_MASTER_MASK;
317 
318 	/* If no master is provided, default to cpu master */
319 	if (!bitclkmaster || bitclkmaster == cpu_node) {
320 		daifmt |= (!framemaster || framemaster == cpu_node) ?
321 			SND_SOC_DAIFMT_CBS_CFS : SND_SOC_DAIFMT_CBS_CFM;
322 	} else {
323 		daifmt |= (!framemaster || framemaster == cpu_node) ?
324 			SND_SOC_DAIFMT_CBM_CFS : SND_SOC_DAIFMT_CBM_CFM;
325 	}
326 
327 	of_node_put(bitclkmaster);
328 	of_node_put(framemaster);
329 
330 	return daifmt;
331 }
332 
333 static int axg_card_parse_cpu_tdm_slots(struct snd_soc_card *card,
334 					struct snd_soc_dai_link *link,
335 					struct device_node *node,
336 					struct axg_dai_link_tdm_data *be)
337 {
338 	char propname[32];
339 	u32 tx, rx;
340 	int i;
341 
342 	be->tx_mask = devm_kcalloc(card->dev, AXG_TDM_NUM_LANES,
343 				   sizeof(*be->tx_mask), GFP_KERNEL);
344 	be->rx_mask = devm_kcalloc(card->dev, AXG_TDM_NUM_LANES,
345 				   sizeof(*be->rx_mask), GFP_KERNEL);
346 	if (!be->tx_mask || !be->rx_mask)
347 		return -ENOMEM;
348 
349 	for (i = 0, tx = 0; i < AXG_TDM_NUM_LANES; i++) {
350 		snprintf(propname, 32, "dai-tdm-slot-tx-mask-%d", i);
351 		snd_soc_of_get_slot_mask(node, propname, &be->tx_mask[i]);
352 		tx = max(tx, be->tx_mask[i]);
353 	}
354 
355 	/* Disable playback is the interface has no tx slots */
356 	if (!tx)
357 		link->dpcm_playback = 0;
358 
359 	for (i = 0, rx = 0; i < AXG_TDM_NUM_LANES; i++) {
360 		snprintf(propname, 32, "dai-tdm-slot-rx-mask-%d", i);
361 		snd_soc_of_get_slot_mask(node, propname, &be->rx_mask[i]);
362 		rx = max(rx, be->rx_mask[i]);
363 	}
364 
365 	/* Disable capture is the interface has no rx slots */
366 	if (!rx)
367 		link->dpcm_capture = 0;
368 
369 	/* ... but the interface should at least have one of them */
370 	if (!tx && !rx) {
371 		dev_err(card->dev, "tdm link has no cpu slots\n");
372 		return -EINVAL;
373 	}
374 
375 	of_property_read_u32(node, "dai-tdm-slot-num", &be->slots);
376 	if (!be->slots) {
377 		/*
378 		 * If the slot number is not provided, set it such as it
379 		 * accommodates the largest mask
380 		 */
381 		be->slots = fls(max(tx, rx));
382 	} else if (be->slots < fls(max(tx, rx)) || be->slots > 32) {
383 		/*
384 		 * Error if the slots can't accommodate the largest mask or
385 		 * if it is just too big
386 		 */
387 		dev_err(card->dev, "bad slot number\n");
388 		return -EINVAL;
389 	}
390 
391 	of_property_read_u32(node, "dai-tdm-slot-width", &be->slot_width);
392 
393 	return 0;
394 }
395 
396 static int axg_card_parse_codecs_masks(struct snd_soc_card *card,
397 				       struct snd_soc_dai_link *link,
398 				       struct device_node *node,
399 				       struct axg_dai_link_tdm_data *be)
400 {
401 	struct axg_dai_link_tdm_mask *codec_mask;
402 	struct device_node *np;
403 
404 	codec_mask = devm_kcalloc(card->dev, link->num_codecs,
405 				  sizeof(*codec_mask), GFP_KERNEL);
406 	if (!codec_mask)
407 		return -ENOMEM;
408 
409 	be->codec_masks = codec_mask;
410 
411 	for_each_child_of_node(node, np) {
412 		snd_soc_of_get_slot_mask(np, "dai-tdm-slot-rx-mask",
413 					 &codec_mask->rx);
414 		snd_soc_of_get_slot_mask(np, "dai-tdm-slot-tx-mask",
415 					 &codec_mask->tx);
416 
417 		codec_mask++;
418 	}
419 
420 	return 0;
421 }
422 
423 static int axg_card_parse_tdm(struct snd_soc_card *card,
424 			      struct device_node *node,
425 			      int *index)
426 {
427 	struct axg_card *priv = snd_soc_card_get_drvdata(card);
428 	struct snd_soc_dai_link *link = &card->dai_link[*index];
429 	struct axg_dai_link_tdm_data *be;
430 	int ret;
431 
432 	/* Allocate tdm link parameters */
433 	be = devm_kzalloc(card->dev, sizeof(*be), GFP_KERNEL);
434 	if (!be)
435 		return -ENOMEM;
436 	priv->link_data[*index] = be;
437 
438 	/* Setup tdm link */
439 	link->ops = &axg_card_tdm_be_ops;
440 	link->init = axg_card_tdm_dai_init;
441 	link->dai_fmt = axg_card_parse_daifmt(node, link->cpus->of_node);
442 
443 	of_property_read_u32(node, "mclk-fs", &be->mclk_fs);
444 
445 	ret = axg_card_parse_cpu_tdm_slots(card, link, node, be);
446 	if (ret) {
447 		dev_err(card->dev, "error parsing tdm link slots\n");
448 		return ret;
449 	}
450 
451 	ret = axg_card_parse_codecs_masks(card, link, node, be);
452 	if (ret)
453 		return ret;
454 
455 	/* Add loopback if the pad dai has playback */
456 	if (link->dpcm_playback) {
457 		ret = axg_card_add_tdm_loopback(card, index);
458 		if (ret)
459 			return ret;
460 	}
461 
462 	return 0;
463 }
464 
465 static int axg_card_set_be_link(struct snd_soc_card *card,
466 				struct snd_soc_dai_link *link,
467 				struct device_node *node)
468 {
469 	struct snd_soc_dai_link_component *codec;
470 	struct device_node *np;
471 	int ret, num_codecs;
472 
473 	link->no_pcm = 1;
474 	link->dpcm_playback = 1;
475 	link->dpcm_capture = 1;
476 
477 	num_codecs = of_get_child_count(node);
478 	if (!num_codecs) {
479 		dev_err(card->dev, "be link %s has no codec\n",
480 			node->full_name);
481 		return -EINVAL;
482 	}
483 
484 	codec = devm_kcalloc(card->dev, num_codecs, sizeof(*codec), GFP_KERNEL);
485 	if (!codec)
486 		return -ENOMEM;
487 
488 	link->codecs = codec;
489 	link->num_codecs = num_codecs;
490 
491 	for_each_child_of_node(node, np) {
492 		ret = axg_card_parse_dai(card, np, &codec->of_node,
493 					 &codec->dai_name);
494 		if (ret) {
495 			of_node_put(np);
496 			return ret;
497 		}
498 
499 		codec++;
500 	}
501 
502 	ret = axg_card_set_link_name(card, link, node, "be");
503 	if (ret)
504 		dev_err(card->dev, "error setting %pOFn link name\n", np);
505 
506 	return ret;
507 }
508 
509 static int axg_card_set_fe_link(struct snd_soc_card *card,
510 				struct snd_soc_dai_link *link,
511 				struct device_node *node,
512 				bool is_playback)
513 {
514 	struct snd_soc_dai_link_component *codec;
515 
516 	codec = devm_kzalloc(card->dev, sizeof(*codec), GFP_KERNEL);
517 	if (!codec)
518 		return -ENOMEM;
519 
520 	link->codecs = codec;
521 	link->num_codecs = 1;
522 
523 	link->dynamic = 1;
524 	link->dpcm_merged_format = 1;
525 	link->dpcm_merged_chan = 1;
526 	link->dpcm_merged_rate = 1;
527 	link->codecs->dai_name = "snd-soc-dummy-dai";
528 	link->codecs->name = "snd-soc-dummy";
529 
530 	if (is_playback)
531 		link->dpcm_playback = 1;
532 	else
533 		link->dpcm_capture = 1;
534 
535 	return axg_card_set_link_name(card, link, node, "fe");
536 }
537 
538 static int axg_card_cpu_is_capture_fe(struct device_node *np)
539 {
540 	return of_device_is_compatible(np, PREFIX "axg-toddr");
541 }
542 
543 static int axg_card_cpu_is_playback_fe(struct device_node *np)
544 {
545 	return of_device_is_compatible(np, PREFIX "axg-frddr");
546 }
547 
548 static int axg_card_cpu_is_tdm_iface(struct device_node *np)
549 {
550 	return of_device_is_compatible(np, PREFIX "axg-tdm-iface");
551 }
552 
553 static int axg_card_cpu_is_codec(struct device_node *np)
554 {
555 	return of_device_is_compatible(np, PREFIX "g12a-tohdmitx");
556 }
557 
558 static int axg_card_add_link(struct snd_soc_card *card, struct device_node *np,
559 			     int *index)
560 {
561 	struct snd_soc_dai_link *dai_link = &card->dai_link[*index];
562 	struct snd_soc_dai_link_component *cpu;
563 	int ret;
564 
565 	cpu = devm_kzalloc(card->dev, sizeof(*cpu), GFP_KERNEL);
566 	if (!cpu)
567 		return -ENOMEM;
568 
569 	dai_link->cpus = cpu;
570 	dai_link->num_cpus = 1;
571 
572 	ret = axg_card_parse_dai(card, np, &dai_link->cpus->of_node,
573 				 &dai_link->cpus->dai_name);
574 	if (ret)
575 		return ret;
576 
577 	if (axg_card_cpu_is_playback_fe(dai_link->cpus->of_node))
578 		ret = axg_card_set_fe_link(card, dai_link, np, true);
579 	else if (axg_card_cpu_is_capture_fe(dai_link->cpus->of_node))
580 		ret = axg_card_set_fe_link(card, dai_link, np, false);
581 	else
582 		ret = axg_card_set_be_link(card, dai_link, np);
583 
584 	if (ret)
585 		return ret;
586 
587 	if (axg_card_cpu_is_tdm_iface(dai_link->cpus->of_node))
588 		ret = axg_card_parse_tdm(card, np, index);
589 	else if (axg_card_cpu_is_codec(dai_link->cpus->of_node))
590 		dai_link->params = &codec_params;
591 
592 	return ret;
593 }
594 
595 static int axg_card_add_links(struct snd_soc_card *card)
596 {
597 	struct axg_card *priv = snd_soc_card_get_drvdata(card);
598 	struct device_node *node = card->dev->of_node;
599 	struct device_node *np;
600 	int num, i, ret;
601 
602 	num = of_get_child_count(node);
603 	if (!num) {
604 		dev_err(card->dev, "card has no links\n");
605 		return -EINVAL;
606 	}
607 
608 	ret = axg_card_reallocate_links(priv, num);
609 	if (ret)
610 		return ret;
611 
612 	i = 0;
613 	for_each_child_of_node(node, np) {
614 		ret = axg_card_add_link(card, np, &i);
615 		if (ret) {
616 			of_node_put(np);
617 			return ret;
618 		}
619 
620 		i++;
621 	}
622 
623 	return 0;
624 }
625 
626 static int axg_card_parse_of_optional(struct snd_soc_card *card,
627 				      const char *propname,
628 				      int (*func)(struct snd_soc_card *c,
629 						  const char *p))
630 {
631 	/* If property is not provided, don't fail ... */
632 	if (!of_property_read_bool(card->dev->of_node, propname))
633 		return 0;
634 
635 	/* ... but do fail if it is provided and the parsing fails */
636 	return func(card, propname);
637 }
638 
639 static const struct of_device_id axg_card_of_match[] = {
640 	{ .compatible = "amlogic,axg-sound-card", },
641 	{}
642 };
643 MODULE_DEVICE_TABLE(of, axg_card_of_match);
644 
645 static int axg_card_probe(struct platform_device *pdev)
646 {
647 	struct device *dev = &pdev->dev;
648 	struct axg_card *priv;
649 	int ret;
650 
651 	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
652 	if (!priv)
653 		return -ENOMEM;
654 
655 	platform_set_drvdata(pdev, priv);
656 	snd_soc_card_set_drvdata(&priv->card, priv);
657 
658 	priv->card.owner = THIS_MODULE;
659 	priv->card.dev = dev;
660 
661 	ret = snd_soc_of_parse_card_name(&priv->card, "model");
662 	if (ret < 0)
663 		return ret;
664 
665 	ret = axg_card_parse_of_optional(&priv->card, "audio-routing",
666 					 snd_soc_of_parse_audio_routing);
667 	if (ret) {
668 		dev_err(dev, "error while parsing routing\n");
669 		return ret;
670 	}
671 
672 	ret = axg_card_parse_of_optional(&priv->card, "audio-widgets",
673 					 snd_soc_of_parse_audio_simple_widgets);
674 	if (ret) {
675 		dev_err(dev, "error while parsing widgets\n");
676 		return ret;
677 	}
678 
679 	ret = axg_card_add_links(&priv->card);
680 	if (ret)
681 		goto out_err;
682 
683 	ret = axg_card_add_aux_devices(&priv->card);
684 	if (ret)
685 		goto out_err;
686 
687 	ret = devm_snd_soc_register_card(dev, &priv->card);
688 	if (ret)
689 		goto out_err;
690 
691 	return 0;
692 
693 out_err:
694 	axg_card_clean_references(priv);
695 	return ret;
696 }
697 
698 static int axg_card_remove(struct platform_device *pdev)
699 {
700 	struct axg_card *priv = platform_get_drvdata(pdev);
701 
702 	axg_card_clean_references(priv);
703 
704 	return 0;
705 }
706 
707 static struct platform_driver axg_card_pdrv = {
708 	.probe = axg_card_probe,
709 	.remove = axg_card_remove,
710 	.driver = {
711 		.name = "axg-sound-card",
712 		.of_match_table = axg_card_of_match,
713 	},
714 };
715 module_platform_driver(axg_card_pdrv);
716 
717 MODULE_DESCRIPTION("Amlogic AXG ALSA machine driver");
718 MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
719 MODULE_LICENSE("GPL v2");
720