blob: 1f698adde506c9314dc4a4c3c68edc0535542326 [file] [log] [blame]
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00001// 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
13struct axg_card {
14 struct snd_soc_card card;
15 void **link_data;
16};
17
18struct axg_dai_link_tdm_mask {
19 u32 tx;
20 u32 rx;
21};
22
23struct 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
David Brazdil0f672f62019-12-10 10:32:29 +000032/*
33 * Base params for the codec to codec links
34 * Those will be over-written by the CPU side of the link
35 */
36static 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
Andrew Scullb4b6d4a2019-01-02 15:54:55 +000044#define PREFIX "amlogic,"
45
46static 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
70static 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
93static int axg_card_set_link_name(struct snd_soc_card *card,
94 struct snd_soc_dai_link *link,
David Brazdil0f672f62019-12-10 10:32:29 +000095 struct device_node *node,
Andrew Scullb4b6d4a2019-01-02 15:54:55 +000096 const char *prefix)
97{
98 char *name = devm_kasprintf(card->dev, GFP_KERNEL, "%s.%s",
David Brazdil0f672f62019-12-10 10:32:29 +000099 prefix, node->full_name);
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000100 if (!name)
101 return -ENOMEM;
102
103 link->name = name;
104 link->stream_name = name;
105
106 return 0;
107}
108
109static 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;
David Brazdil0f672f62019-12-10 10:32:29 +0000113 struct snd_soc_dai_link_component *codec;
114 struct snd_soc_aux_dev *aux;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000115 int i, j;
116
117 if (card->dai_link) {
David Brazdil0f672f62019-12-10 10:32:29 +0000118 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);
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000123 }
124 }
125
126 if (card->aux_dev) {
David Brazdil0f672f62019-12-10 10:32:29 +0000127 for_each_card_pre_auxs(card, i, aux)
128 of_node_put(aux->dlc.of_node);
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000129 }
130
131 kfree(card->dai_link);
132 kfree(priv->link_data);
133}
134
135static 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
David Brazdil0f672f62019-12-10 10:32:29 +0000161 for_each_card_pre_auxs(card, i, aux) {
162 aux->dlc.of_node =
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000163 of_parse_phandle(node, "audio-aux-devs", i);
David Brazdil0f672f62019-12-10 10:32:29 +0000164 if (!aux->dlc.of_node)
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000165 return -EINVAL;
166 }
167
168 return 0;
169}
170
171static 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
David Brazdil0f672f62019-12-10 10:32:29 +0000185 for_each_rtd_codec_dai(rtd, i, codec_dai) {
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000186 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
201static const struct snd_soc_ops axg_card_tdm_be_ops = {
202 .hw_params = axg_card_tdm_be_hw_params,
203};
204
205static 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
David Brazdil0f672f62019-12-10 10:32:29 +0000213 for_each_rtd_codec_dai(rtd, i, codec_dai) {
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000214 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
235static 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
253static 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;
David Brazdil0f672f62019-12-10 10:32:29 +0000259 struct snd_soc_dai_link_component *dlc;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000260 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
David Brazdil0f672f62019-12-10 10:32:29 +0000273 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
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000282 lb->stream_name = lb->name;
David Brazdil0f672f62019-12-10 10:32:29 +0000283 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";
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000287 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 */
David Brazdil0f672f62019-12-10 10:32:29 +0000299 of_node_get(lb->cpus->of_node);
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000300
301 /* Let add_links continue where it should */
302 *index += 1;
303
304 return 0;
305}
306
307static 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
333static 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
396static 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
423static 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;
David Brazdil0f672f62019-12-10 10:32:29 +0000441 link->dai_fmt = axg_card_parse_daifmt(node, link->cpus->of_node);
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000442
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
465static 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
David Brazdil0f672f62019-12-10 10:32:29 +0000502 ret = axg_card_set_link_name(card, link, node, "be");
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000503 if (ret)
David Brazdil0f672f62019-12-10 10:32:29 +0000504 dev_err(card->dev, "error setting %pOFn link name\n", np);
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000505
506 return ret;
507}
508
509static int axg_card_set_fe_link(struct snd_soc_card *card,
510 struct snd_soc_dai_link *link,
David Brazdil0f672f62019-12-10 10:32:29 +0000511 struct device_node *node,
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000512 bool is_playback)
513{
David Brazdil0f672f62019-12-10 10:32:29 +0000514 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
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000523 link->dynamic = 1;
524 link->dpcm_merged_format = 1;
525 link->dpcm_merged_chan = 1;
526 link->dpcm_merged_rate = 1;
David Brazdil0f672f62019-12-10 10:32:29 +0000527 link->codecs->dai_name = "snd-soc-dummy-dai";
528 link->codecs->name = "snd-soc-dummy";
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000529
530 if (is_playback)
531 link->dpcm_playback = 1;
532 else
533 link->dpcm_capture = 1;
534
David Brazdil0f672f62019-12-10 10:32:29 +0000535 return axg_card_set_link_name(card, link, node, "fe");
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000536}
537
538static int axg_card_cpu_is_capture_fe(struct device_node *np)
539{
540 return of_device_is_compatible(np, PREFIX "axg-toddr");
541}
542
543static int axg_card_cpu_is_playback_fe(struct device_node *np)
544{
545 return of_device_is_compatible(np, PREFIX "axg-frddr");
546}
547
548static 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
David Brazdil0f672f62019-12-10 10:32:29 +0000553static int axg_card_cpu_is_codec(struct device_node *np)
554{
555 return of_device_is_compatible(np, PREFIX "g12a-tohdmitx");
556}
557
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000558static 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];
David Brazdil0f672f62019-12-10 10:32:29 +0000562 struct snd_soc_dai_link_component *cpu;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000563 int ret;
564
David Brazdil0f672f62019-12-10 10:32:29 +0000565 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);
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000574 if (ret)
575 return ret;
576
David Brazdil0f672f62019-12-10 10:32:29 +0000577 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);
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000581 else
582 ret = axg_card_set_be_link(card, dai_link, np);
583
584 if (ret)
585 return ret;
586
David Brazdil0f672f62019-12-10 10:32:29 +0000587 if (axg_card_cpu_is_tdm_iface(dai_link->cpus->of_node))
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000588 ret = axg_card_parse_tdm(card, np, index);
David Brazdil0f672f62019-12-10 10:32:29 +0000589 else if (axg_card_cpu_is_codec(dai_link->cpus->of_node))
590 dai_link->params = &codec_params;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000591
592 return ret;
593}
594
595static 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
626static 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
639static const struct of_device_id axg_card_of_match[] = {
640 { .compatible = "amlogic,axg-sound-card", },
641 {}
642};
643MODULE_DEVICE_TABLE(of, axg_card_of_match);
644
645static 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
693out_err:
694 axg_card_clean_references(priv);
695 return ret;
696}
697
698static 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
707static 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};
715module_platform_driver(axg_card_pdrv);
716
717MODULE_DESCRIPTION("Amlogic AXG ALSA machine driver");
718MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
719MODULE_LICENSE("GPL v2");