~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/sound/soc/meson/axg-card.c

Version: ~ [ linux-5.6.3 ] ~ [ linux-5.5.16 ] ~ [ linux-5.4.31 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.114 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.175 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.218 ] ~ [ linux-4.8.17 ] ~ [ linux-4.7.10 ] ~ [ linux-4.6.7 ] ~ [ linux-4.5.7 ] ~ [ linux-4.4.218 ] ~ [ linux-4.3.6 ] ~ [ linux-4.2.8 ] ~ [ linux-4.1.52 ] ~ [ linux-4.0.9 ] ~ [ linux-3.19.8 ] ~ [ linux-3.18.140 ] ~ [ linux-3.17.8 ] ~ [ linux-3.16.82 ] ~ [ linux-3.15.10 ] ~ [ linux-3.14.79 ] ~ [ linux-3.13.11 ] ~ [ linux-3.12.74 ] ~ [ linux-3.11.10 ] ~ [ linux-3.10.108 ] ~ [ linux-3.9.11 ] ~ [ linux-3.8.13 ] ~ [ linux-3.7.10 ] ~ [ linux-3.6.11 ] ~ [ linux-3.5.7 ] ~ [ linux-3.4.113 ] ~ [ linux-3.3.8 ] ~ [ linux-3.2.102 ] ~ [ linux-3.1.10 ] ~ [ linux-3.0.101 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.5 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

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

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | Wiki (Japanese) | Wiki (English) | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

osdn.jp