ASoC: ac108: Adds ac108 codec and machine code
[platform/kernel/linux-rpi.git] / sound / soc / seeed / seeed-voicecard.c
1 /*
2  * SEEED voice card
3  *
4  * (C) Copyright 2017-2018
5  * Seeed Technology Co., Ltd. <www.seeedstudio.com>
6  *
7  * base on ASoC simple sound card support
8  *
9  * Copyright (C) 2012 Renesas Solutions Corp.
10  * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
11  *
12  * This program is free software; you can redistribute it and/or modify
13  * it under the terms of the GNU General Public License version 2 as
14  * published by the Free Software Foundation.
15  */
16 /* #undef DEBUG */
17 #include <linux/version.h>
18 #include <linux/clk.h>
19 #include <linux/device.h>
20 #include <linux/gpio.h>
21 #include <linux/module.h>
22 #include <linux/of.h>
23 #include <linux/of_gpio.h>
24 #include <linux/platform_device.h>
25 #include <linux/string.h>
26 #include <sound/soc.h>
27 #include <sound/soc-dai.h>
28 #include <sound/simple_card_utils.h>
29 //#include <sound/soc/codecs/ac10x.h>
30 #include "../codecs/ac10x.h"
31
32
33 /*
34  * single codec:
35  *      0 - allow multi codec
36  *      1 - yes
37  */
38 #define _SINGLE_CODEC           1
39
40 struct seeed_card_data {
41         struct snd_soc_card snd_card;
42         struct seeed_dai_props {
43                 struct asoc_simple_dai cpu_dai;
44                 struct asoc_simple_dai codec_dai;
45                 unsigned int mclk_fs;
46         } *dai_props;
47         unsigned int mclk_fs;
48         unsigned channels_playback_default;
49         unsigned channels_playback_override;
50         unsigned channels_capture_default;
51         unsigned channels_capture_override;
52         struct snd_soc_dai_link *dai_link;
53         #if CONFIG_AC10X_TRIG_LOCK
54         spinlock_t lock;
55         #endif
56         struct work_struct work_codec_clk;
57         #define TRY_STOP_MAX    3
58         int try_stop;
59 };
60
61 struct seeed_card_info {
62         const char *name;
63         const char *card;
64         const char *codec;
65         const char *platform;
66
67         unsigned int daifmt;
68         struct asoc_simple_dai cpu_dai;
69         struct asoc_simple_dai codec_dai;
70 };
71
72 #define seeed_priv_to_dev(priv) ((priv)->snd_card.dev)
73 #define seeed_priv_to_link(priv, i) ((priv)->snd_card.dai_link + (i))
74 #define seeed_priv_to_props(priv, i) ((priv)->dai_props + (i))
75
76 #define DAI     "sound-dai"
77 #define CELL    "#sound-dai-cells"
78 #define PREFIX  "seeed-voice-card,"
79
80 static int seeed_voice_card_startup(struct snd_pcm_substream *substream)
81 {
82         struct snd_soc_pcm_runtime *rtd = substream->private_data;
83         struct seeed_card_data *priv =  snd_soc_card_get_drvdata(rtd->card);
84         struct seeed_dai_props *dai_props =
85                 seeed_priv_to_props(priv, rtd->num);
86         int ret;
87
88         ret = clk_prepare_enable(dai_props->cpu_dai.clk);
89         if (ret)
90                 return ret;
91
92         ret = clk_prepare_enable(dai_props->codec_dai.clk);
93         if (ret)
94                 clk_disable_unprepare(dai_props->cpu_dai.clk);
95
96         if (rtd->cpu_dai->driver->playback.channels_min) {
97                 priv->channels_playback_default = rtd->cpu_dai->driver->playback.channels_min;
98         }
99         if (rtd->cpu_dai->driver->capture.channels_min) {
100                 priv->channels_capture_default = rtd->cpu_dai->driver->capture.channels_min;
101         }
102         rtd->cpu_dai->driver->playback.channels_min = priv->channels_playback_override;
103         rtd->cpu_dai->driver->playback.channels_max = priv->channels_playback_override;
104         rtd->cpu_dai->driver->capture.channels_min = priv->channels_capture_override;
105         rtd->cpu_dai->driver->capture.channels_max = priv->channels_capture_override;
106
107         return ret;
108 }
109
110 static void seeed_voice_card_shutdown(struct snd_pcm_substream *substream)
111 {
112         struct snd_soc_pcm_runtime *rtd = substream->private_data;
113         struct seeed_card_data *priv =  snd_soc_card_get_drvdata(rtd->card);
114         struct seeed_dai_props *dai_props =
115                 seeed_priv_to_props(priv, rtd->num);
116
117         rtd->cpu_dai->driver->playback.channels_min = priv->channels_playback_default;
118         rtd->cpu_dai->driver->playback.channels_max = priv->channels_playback_default;
119         rtd->cpu_dai->driver->capture.channels_min = priv->channels_capture_default;
120         rtd->cpu_dai->driver->capture.channels_max = priv->channels_capture_default;
121
122         clk_disable_unprepare(dai_props->cpu_dai.clk);
123
124         clk_disable_unprepare(dai_props->codec_dai.clk);
125 }
126
127 static int seeed_voice_card_hw_params(struct snd_pcm_substream *substream,
128                                       struct snd_pcm_hw_params *params)
129 {
130         struct snd_soc_pcm_runtime *rtd = substream->private_data;
131         struct snd_soc_dai *codec_dai = rtd->codec_dai;
132         struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
133         struct seeed_card_data *priv = snd_soc_card_get_drvdata(rtd->card);
134         struct seeed_dai_props *dai_props =
135                 seeed_priv_to_props(priv, rtd->num);
136         unsigned int mclk, mclk_fs = 0;
137         int ret = 0;
138
139         if (priv->mclk_fs)
140                 mclk_fs = priv->mclk_fs;
141         else if (dai_props->mclk_fs)
142                 mclk_fs = dai_props->mclk_fs;
143
144         if (mclk_fs) {
145                 mclk = params_rate(params) * mclk_fs;
146                 ret = snd_soc_dai_set_sysclk(codec_dai, 0, mclk,
147                                              SND_SOC_CLOCK_IN);
148                 if (ret && ret != -ENOTSUPP)
149                         goto err;
150
151                 ret = snd_soc_dai_set_sysclk(cpu_dai, 0, mclk,
152                                              SND_SOC_CLOCK_OUT);
153                 if (ret && ret != -ENOTSUPP)
154                         goto err;
155         }
156         return 0;
157 err:
158         return ret;
159 }
160
161 #define _SET_CLOCK_CNT          2
162 static int (* _set_clock[_SET_CLOCK_CNT])(int y_start_n_stop);
163
164 int seeed_voice_card_register_set_clock(int stream, int (*set_clock)(int)) {
165         if (! _set_clock[stream]) {
166                 _set_clock[stream] = set_clock;
167         }
168         return 0;
169 }
170 EXPORT_SYMBOL(seeed_voice_card_register_set_clock);
171
172 /*
173  * work_cb_codec_clk: clear audio codec inner clock.
174  */
175 static void work_cb_codec_clk(struct work_struct *work)
176 {
177         struct seeed_card_data *priv = container_of(work, struct seeed_card_data, work_codec_clk);
178         int r = 0;
179
180         if (_set_clock[SNDRV_PCM_STREAM_CAPTURE]) {
181                 r = r || _set_clock[SNDRV_PCM_STREAM_CAPTURE](0);
182         }
183         if (_set_clock[SNDRV_PCM_STREAM_PLAYBACK]) {
184                 r = r || _set_clock[SNDRV_PCM_STREAM_PLAYBACK](0);
185         }
186
187         if (r && priv->try_stop++ < TRY_STOP_MAX) {
188                 if (0 != schedule_work(&priv->work_codec_clk)) {}
189         }
190         return;
191 }
192
193 static int seeed_voice_card_trigger(struct snd_pcm_substream *substream, int cmd)
194 {
195         struct snd_soc_pcm_runtime *rtd = substream->private_data;
196         struct snd_soc_dai *dai = rtd->codec_dai;
197         struct seeed_card_data *priv = snd_soc_card_get_drvdata(rtd->card);
198         #if CONFIG_AC10X_TRIG_LOCK
199         unsigned long flags;
200         #endif
201         int ret = 0;
202
203         dev_dbg(rtd->card->dev, "%s() stream=%s  cmd=%d play:%d, capt:%d\n",
204                 __FUNCTION__, snd_pcm_stream_str(substream), cmd,
205                 dai->playback_active, dai->capture_active);
206
207         switch (cmd) {
208         case SNDRV_PCM_TRIGGER_START:
209         case SNDRV_PCM_TRIGGER_RESUME:
210         case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
211                 if (cancel_work_sync(&priv->work_codec_clk) != 0) {}
212                 #if CONFIG_AC10X_TRIG_LOCK
213                 /* I know it will degrades performance, but I have no choice */
214                 spin_lock_irqsave(&priv->lock, flags);
215                 #endif
216                 if (_set_clock[SNDRV_PCM_STREAM_CAPTURE]) _set_clock[SNDRV_PCM_STREAM_CAPTURE](1);
217                 if (_set_clock[SNDRV_PCM_STREAM_PLAYBACK]) _set_clock[SNDRV_PCM_STREAM_PLAYBACK](1);
218                 #if CONFIG_AC10X_TRIG_LOCK
219                 spin_unlock_irqrestore(&priv->lock, flags);
220                 #endif
221                 break;
222
223         case SNDRV_PCM_TRIGGER_STOP:
224         case SNDRV_PCM_TRIGGER_SUSPEND:
225         case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
226                 /* capture channel resync, if overrun */
227                 if (dai->capture_active && substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
228                         break;
229                 }
230
231                 /* interrupt environment */
232                 if (in_irq() || in_nmi() || in_serving_softirq()) {
233                         priv->try_stop = 0;
234                         if (0 != schedule_work(&priv->work_codec_clk)) {
235                         }
236                 } else {
237                         if (_set_clock[SNDRV_PCM_STREAM_CAPTURE]) _set_clock[SNDRV_PCM_STREAM_CAPTURE](0);
238                         if (_set_clock[SNDRV_PCM_STREAM_PLAYBACK]) _set_clock[SNDRV_PCM_STREAM_PLAYBACK](0);
239                 }
240                 break;
241         default:
242                 ret = -EINVAL;
243         }
244
245         return ret;
246 }
247
248 static struct snd_soc_ops seeed_voice_card_ops = {
249         .startup = seeed_voice_card_startup,
250         .shutdown = seeed_voice_card_shutdown,
251         .hw_params = seeed_voice_card_hw_params,
252         .trigger = seeed_voice_card_trigger,
253 };
254
255 static int seeed_voice_card_dai_init(struct snd_soc_pcm_runtime *rtd)
256 {
257         struct seeed_card_data *priv =  snd_soc_card_get_drvdata(rtd->card);
258         struct snd_soc_dai *codec = rtd->codec_dai;
259         struct snd_soc_dai *cpu = rtd->cpu_dai;
260         struct seeed_dai_props *dai_props =
261                 seeed_priv_to_props(priv, rtd->num);
262         int ret;
263
264         ret = asoc_simple_card_init_dai(codec, &dai_props->codec_dai);
265         if (ret < 0)
266                 return ret;
267
268         ret = asoc_simple_card_init_dai(cpu, &dai_props->cpu_dai);
269         if (ret < 0)
270                 return ret;
271
272         return 0;
273 }
274
275 static int seeed_voice_card_dai_link_of(struct device_node *node,
276                                         struct seeed_card_data *priv,
277                                         int idx,
278                                         bool is_top_level_node)
279 {
280         struct device *dev = seeed_priv_to_dev(priv);
281         struct snd_soc_dai_link *dai_link = seeed_priv_to_link(priv, idx);
282         struct seeed_dai_props *dai_props = seeed_priv_to_props(priv, idx);
283         struct asoc_simple_dai *cpu_dai = &dai_props->cpu_dai;
284         struct asoc_simple_dai *codec_dai = &dai_props->codec_dai;
285         struct device_node *cpu = NULL;
286         struct device_node *plat = NULL;
287         struct device_node *codec = NULL;
288         char prop[128];
289         char *prefix = "";
290         int ret, single_cpu;
291
292         /* For single DAI link & old style of DT node */
293         if (is_top_level_node)
294                 prefix = PREFIX;
295
296         snprintf(prop, sizeof(prop), "%scpu", prefix);
297         cpu = of_get_child_by_name(node, prop);
298
299         snprintf(prop, sizeof(prop), "%splat", prefix);
300         plat = of_get_child_by_name(node, prop);
301
302         snprintf(prop, sizeof(prop), "%scodec", prefix);
303         codec = of_get_child_by_name(node, prop);
304
305         if (!cpu || !codec) {
306                 ret = -EINVAL;
307                 dev_err(dev, "%s: Can't find %s DT node\n", __func__, prop);
308                 goto dai_link_of_err;
309         }
310
311         ret = asoc_simple_card_parse_daifmt(dev, node, codec,
312                                             prefix, &dai_link->dai_fmt);
313         if (ret < 0)
314                 goto dai_link_of_err;
315
316         of_property_read_u32(node, "mclk-fs", &dai_props->mclk_fs);
317
318         ret = asoc_simple_card_parse_cpu(cpu, dai_link,
319                                          DAI, CELL, &single_cpu);
320         if (ret < 0)
321                 goto dai_link_of_err;
322
323         #if _SINGLE_CODEC
324         ret = asoc_simple_card_parse_codec(codec, dai_link, DAI, CELL);
325         if (ret < 0)
326                 goto dai_link_of_err;
327         #else
328         ret = snd_soc_of_get_dai_link_codecs(dev, codec, dai_link);
329         if (ret < 0) {
330                 dev_err(dev, "parse codec info error %d\n", ret);
331                 goto dai_link_of_err;
332         }
333         dev_dbg(dev, "dai_link num_codecs = %d\n", dai_link->num_codecs);
334         #endif
335
336         ret = asoc_simple_card_parse_platform(plat, dai_link, DAI, CELL);
337         if (ret < 0)
338                 goto dai_link_of_err;
339
340         ret = snd_soc_of_parse_tdm_slot(cpu,    &cpu_dai->tx_slot_mask,
341                                                 &cpu_dai->rx_slot_mask,
342                                                 &cpu_dai->slots,
343                                                 &cpu_dai->slot_width);
344         dev_dbg(dev, "cpu_dai : slot,width,tx,rx = %d,%d,%d,%d\n", 
345                         cpu_dai->slots, cpu_dai->slot_width,
346                         cpu_dai->tx_slot_mask, cpu_dai->rx_slot_mask
347                         );
348         if (ret < 0)
349                 goto dai_link_of_err;
350
351         ret = snd_soc_of_parse_tdm_slot(codec,  &codec_dai->tx_slot_mask,
352                                                 &codec_dai->rx_slot_mask,
353                                                 &codec_dai->slots,
354                                                 &codec_dai->slot_width);
355         if (ret < 0)
356                 goto dai_link_of_err;
357
358         #if LINUX_VERSION_CODE <= KERNEL_VERSION(4,10,0)
359         ret = asoc_simple_card_parse_clk_cpu(cpu, dai_link, cpu_dai);
360         #else
361         ret = asoc_simple_card_parse_clk_cpu(dev, cpu, dai_link, cpu_dai);
362         #endif
363         if (ret < 0)
364                 goto dai_link_of_err;
365
366         #if LINUX_VERSION_CODE <= KERNEL_VERSION(4,10,0)
367         ret = asoc_simple_card_parse_clk_codec(codec, dai_link, codec_dai);
368         #else
369         ret = asoc_simple_card_parse_clk_codec(dev, codec, dai_link, codec_dai);
370         #endif
371         if (ret < 0)
372                 goto dai_link_of_err;
373
374         #if _SINGLE_CODEC
375         ret = asoc_simple_card_canonicalize_dailink(dai_link);
376         if (ret < 0)
377                 goto dai_link_of_err;
378         #endif
379
380         ret = asoc_simple_card_set_dailink_name(dev, dai_link,
381                                                 "%s-%s",
382                                                 dai_link->cpu_dai_name,
383                                                 #if _SINGLE_CODEC
384                                                 dai_link->codec_dai_name
385                                                 #else
386                                                 dai_link->codecs[0].dai_name
387                                                 #endif
388         );
389         if (ret < 0)
390                 goto dai_link_of_err;
391
392         dai_link->ops = &seeed_voice_card_ops;
393         dai_link->init = seeed_voice_card_dai_init;
394
395         dev_dbg(dev, "\tname : %s\n", dai_link->stream_name);
396         dev_dbg(dev, "\tformat : %04x\n", dai_link->dai_fmt);
397         dev_dbg(dev, "\tcpu : %s / %d\n",
398                 dai_link->cpu_dai_name,
399                 dai_props->cpu_dai.sysclk);
400         dev_dbg(dev, "\tcodec : %s / %d\n",
401                 #if _SINGLE_CODEC
402                 dai_link->codec_dai_name,
403                 #else
404                 dai_link->codecs[0].dai_name,
405                 #endif
406                 dai_props->codec_dai.sysclk);
407
408         asoc_simple_card_canonicalize_cpu(dai_link, single_cpu);
409
410 dai_link_of_err:
411         of_node_put(cpu);
412         of_node_put(codec);
413
414         return ret;
415 }
416
417 static int seeed_voice_card_parse_aux_devs(struct device_node *node,
418                                            struct seeed_card_data *priv)
419 {
420         struct device *dev = seeed_priv_to_dev(priv);
421         struct device_node *aux_node;
422         int i, n, len;
423
424         if (!of_find_property(node, PREFIX "aux-devs", &len))
425                 return 0;               /* Ok to have no aux-devs */
426
427         n = len / sizeof(__be32);
428         if (n <= 0)
429                 return -EINVAL;
430
431         priv->snd_card.aux_dev = devm_kzalloc(dev,
432                         n * sizeof(*priv->snd_card.aux_dev), GFP_KERNEL);
433         if (!priv->snd_card.aux_dev)
434                 return -ENOMEM;
435
436         for (i = 0; i < n; i++) {
437                 aux_node = of_parse_phandle(node, PREFIX "aux-devs", i);
438                 if (!aux_node)
439                         return -EINVAL;
440                 priv->snd_card.aux_dev[i].codec_of_node = aux_node;
441         }
442
443         priv->snd_card.num_aux_devs = n;
444         return 0;
445 }
446
447 static int seeed_voice_card_parse_of(struct device_node *node,
448                                      struct seeed_card_data *priv)
449 {
450         struct device *dev = seeed_priv_to_dev(priv);
451         struct device_node *dai_link;
452         int ret;
453
454         if (!node)
455                 return -EINVAL;
456
457         dai_link = of_get_child_by_name(node, PREFIX "dai-link");
458
459         /* The off-codec widgets */
460         if (of_property_read_bool(node, PREFIX "widgets")) {
461                 ret = snd_soc_of_parse_audio_simple_widgets(&priv->snd_card,
462                                         PREFIX "widgets");
463                 if (ret)
464                         goto card_parse_end;
465         }
466
467         /* DAPM routes */
468         if (of_property_read_bool(node, PREFIX "routing")) {
469                 ret = snd_soc_of_parse_audio_routing(&priv->snd_card,
470                                         PREFIX "routing");
471                 if (ret)
472                         goto card_parse_end;
473         }
474
475         /* Factor to mclk, used in hw_params() */
476         of_property_read_u32(node, PREFIX "mclk-fs", &priv->mclk_fs);
477
478         /* Single/Muti DAI link(s) & New style of DT node */
479         if (dai_link) {
480                 struct device_node *np = NULL;
481                 int i = 0;
482
483                 for_each_child_of_node(node, np) {
484                         dev_dbg(dev, "\tlink %d:\n", i);
485                         ret = seeed_voice_card_dai_link_of(np, priv,
486                                                            i, false);
487                         if (ret < 0) {
488                                 of_node_put(np);
489                                 goto card_parse_end;
490                         }
491                         i++;
492                 }
493         } else {
494                 /* For single DAI link & old style of DT node */
495                 ret = seeed_voice_card_dai_link_of(node, priv, 0, true);
496                 if (ret < 0)
497                         goto card_parse_end;
498         }
499
500         ret = asoc_simple_card_parse_card_name(&priv->snd_card, PREFIX);
501         if (ret < 0)
502                 goto card_parse_end;
503
504         ret = seeed_voice_card_parse_aux_devs(node, priv);
505
506         priv->channels_playback_default  = 0;
507         priv->channels_playback_override = 2;
508         priv->channels_capture_default   = 0;
509         priv->channels_capture_override  = 2;
510         of_property_read_u32(node, PREFIX "channels-playback-default",
511                                     &priv->channels_playback_default);
512         of_property_read_u32(node, PREFIX "channels-playback-override",
513                                     &priv->channels_playback_override);
514         of_property_read_u32(node, PREFIX "channels-capture-default",
515                                     &priv->channels_capture_default);
516         of_property_read_u32(node, PREFIX "channels-capture-override",
517                                     &priv->channels_capture_override);
518
519 card_parse_end:
520         of_node_put(dai_link);
521
522         return ret;
523 }
524
525 static int seeed_voice_card_probe(struct platform_device *pdev)
526 {
527         struct seeed_card_data *priv;
528         struct snd_soc_dai_link *dai_link;
529         struct seeed_dai_props *dai_props;
530         struct device_node *np = pdev->dev.of_node;
531         struct device *dev = &pdev->dev;
532         int num, ret;
533
534         /* Get the number of DAI links */
535         if (np && of_get_child_by_name(np, PREFIX "dai-link"))
536                 num = of_get_child_count(np);
537         else
538                 num = 1;
539
540         /* Allocate the private data and the DAI link array */
541         priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
542         if (!priv)
543                 return -ENOMEM;
544
545         dai_props = devm_kzalloc(dev, sizeof(*dai_props) * num, GFP_KERNEL);
546         dai_link  = devm_kzalloc(dev, sizeof(*dai_link)  * num, GFP_KERNEL);
547         if (!dai_props || !dai_link)
548                 return -ENOMEM;
549
550         priv->dai_props                 = dai_props;
551         priv->dai_link                  = dai_link;
552
553         /* Init snd_soc_card */
554         priv->snd_card.owner            = THIS_MODULE;
555         priv->snd_card.dev              = dev;
556         priv->snd_card.dai_link         = priv->dai_link;
557         priv->snd_card.num_links        = num;
558
559         if (np && of_device_is_available(np)) {
560                 ret = seeed_voice_card_parse_of(np, priv);
561                 if (ret < 0) {
562                         if (ret != -EPROBE_DEFER)
563                                 dev_err(dev, "parse error %d\n", ret);
564                         goto err;
565                 }
566         } else {
567                 struct seeed_card_info *cinfo;
568
569                 cinfo = dev->platform_data;
570                 if (!cinfo) {
571                         dev_err(dev, "no info for seeed-voice-card\n");
572                         return -EINVAL;
573                 }
574
575                 if (!cinfo->name ||
576                     !cinfo->codec_dai.name ||
577                     !cinfo->codec ||
578                     !cinfo->platform ||
579                     !cinfo->cpu_dai.name) {
580                         dev_err(dev, "insufficient seeed_voice_card_info settings\n");
581                         return -EINVAL;
582                 }
583
584                 priv->snd_card.name     = (cinfo->card) ? cinfo->card : cinfo->name;
585                 dai_link->name          = cinfo->name;
586                 dai_link->stream_name   = cinfo->name;
587                 dai_link->platform_name = cinfo->platform;
588                 dai_link->codec_name    = cinfo->codec;
589                 dai_link->cpu_dai_name  = cinfo->cpu_dai.name;
590                 dai_link->codec_dai_name = cinfo->codec_dai.name;
591                 dai_link->dai_fmt       = cinfo->daifmt;
592                 dai_link->init          = seeed_voice_card_dai_init;
593                 memcpy(&priv->dai_props->cpu_dai, &cinfo->cpu_dai,
594                                         sizeof(priv->dai_props->cpu_dai));
595                 memcpy(&priv->dai_props->codec_dai, &cinfo->codec_dai,
596                                         sizeof(priv->dai_props->codec_dai));
597         }
598
599         snd_soc_card_set_drvdata(&priv->snd_card, priv);
600
601         #if CONFIG_AC10X_TRIG_LOCK
602         spin_lock_init(&priv->lock);
603         #endif
604
605         INIT_WORK(&priv->work_codec_clk, work_cb_codec_clk);
606
607         ret = devm_snd_soc_register_card(&pdev->dev, &priv->snd_card);
608         if (ret >= 0)
609                 return ret;
610
611 err:
612         asoc_simple_card_clean_reference(&priv->snd_card);
613
614         return ret;
615 }
616
617 static int seeed_voice_card_remove(struct platform_device *pdev)
618 {
619         struct snd_soc_card *card = platform_get_drvdata(pdev);
620         struct seeed_card_data *priv = snd_soc_card_get_drvdata(card);
621
622         if (cancel_work_sync(&priv->work_codec_clk) != 0) {
623         }
624         return asoc_simple_card_clean_reference(card);
625 }
626
627 static const struct of_device_id seeed_voice_of_match[] = {
628         { .compatible = "seeed-voicecard", },
629         {},
630 };
631 MODULE_DEVICE_TABLE(of, seeed_voice_of_match);
632
633 static struct platform_driver seeed_voice_card = {
634         .driver = {
635                 .name = "seeed-voicecard",
636                 .pm = &snd_soc_pm_ops,
637                 .of_match_table = seeed_voice_of_match,
638         },
639         .probe = seeed_voice_card_probe,
640         .remove = seeed_voice_card_remove,
641 };
642
643 module_platform_driver(seeed_voice_card);
644
645 MODULE_ALIAS("platform:seeed-voice-card");
646 MODULE_LICENSE("GPL v2");
647 MODULE_DESCRIPTION("ASoC SEEED Voice Card");
648 MODULE_AUTHOR("PeterYang<linsheng.yang@seeed.cc>");