4 * (C) Copyright 2017-2018
5 * Seeed Technology Co., Ltd. <www.seeedstudio.com>
7 * base on ASoC simple sound card support
9 * Copyright (C) 2012 Renesas Solutions Corp.
10 * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
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.
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>
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"
35 * 0 - allow multi codec
38 #define _SINGLE_CODEC 1
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;
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
56 struct work_struct work_codec_clk;
57 #define TRY_STOP_MAX 3
61 struct seeed_card_info {
68 struct asoc_simple_dai cpu_dai;
69 struct asoc_simple_dai codec_dai;
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))
76 #define DAI "sound-dai"
77 #define CELL "#sound-dai-cells"
78 #define PREFIX "seeed-voice-card,"
80 static int seeed_voice_card_startup(struct snd_pcm_substream *substream)
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);
88 ret = clk_prepare_enable(dai_props->cpu_dai.clk);
92 ret = clk_prepare_enable(dai_props->codec_dai.clk);
94 clk_disable_unprepare(dai_props->cpu_dai.clk);
96 if (rtd->cpu_dai->driver->playback.channels_min) {
97 priv->channels_playback_default = rtd->cpu_dai->driver->playback.channels_min;
99 if (rtd->cpu_dai->driver->capture.channels_min) {
100 priv->channels_capture_default = rtd->cpu_dai->driver->capture.channels_min;
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;
110 static void seeed_voice_card_shutdown(struct snd_pcm_substream *substream)
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);
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;
122 clk_disable_unprepare(dai_props->cpu_dai.clk);
124 clk_disable_unprepare(dai_props->codec_dai.clk);
127 static int seeed_voice_card_hw_params(struct snd_pcm_substream *substream,
128 struct snd_pcm_hw_params *params)
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;
140 mclk_fs = priv->mclk_fs;
141 else if (dai_props->mclk_fs)
142 mclk_fs = dai_props->mclk_fs;
145 mclk = params_rate(params) * mclk_fs;
146 ret = snd_soc_dai_set_sysclk(codec_dai, 0, mclk,
148 if (ret && ret != -ENOTSUPP)
151 ret = snd_soc_dai_set_sysclk(cpu_dai, 0, mclk,
153 if (ret && ret != -ENOTSUPP)
161 #define _SET_CLOCK_CNT 2
162 static int (* _set_clock[_SET_CLOCK_CNT])(int y_start_n_stop);
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;
170 EXPORT_SYMBOL(seeed_voice_card_register_set_clock);
173 * work_cb_codec_clk: clear audio codec inner clock.
175 static void work_cb_codec_clk(struct work_struct *work)
177 struct seeed_card_data *priv = container_of(work, struct seeed_card_data, work_codec_clk);
180 if (_set_clock[SNDRV_PCM_STREAM_CAPTURE]) {
181 r = r || _set_clock[SNDRV_PCM_STREAM_CAPTURE](0);
183 if (_set_clock[SNDRV_PCM_STREAM_PLAYBACK]) {
184 r = r || _set_clock[SNDRV_PCM_STREAM_PLAYBACK](0);
187 if (r && priv->try_stop++ < TRY_STOP_MAX) {
188 if (0 != schedule_work(&priv->work_codec_clk)) {}
193 static int seeed_voice_card_trigger(struct snd_pcm_substream *substream, int cmd)
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
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);
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);
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);
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) {
231 /* interrupt environment */
232 if (in_irq() || in_nmi() || in_serving_softirq()) {
234 if (0 != schedule_work(&priv->work_codec_clk)) {
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);
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,
255 static int seeed_voice_card_dai_init(struct snd_soc_pcm_runtime *rtd)
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);
264 ret = asoc_simple_card_init_dai(codec, &dai_props->codec_dai);
268 ret = asoc_simple_card_init_dai(cpu, &dai_props->cpu_dai);
275 static int seeed_voice_card_dai_link_of(struct device_node *node,
276 struct seeed_card_data *priv,
278 bool is_top_level_node)
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;
292 /* For single DAI link & old style of DT node */
293 if (is_top_level_node)
296 snprintf(prop, sizeof(prop), "%scpu", prefix);
297 cpu = of_get_child_by_name(node, prop);
299 snprintf(prop, sizeof(prop), "%splat", prefix);
300 plat = of_get_child_by_name(node, prop);
302 snprintf(prop, sizeof(prop), "%scodec", prefix);
303 codec = of_get_child_by_name(node, prop);
305 if (!cpu || !codec) {
307 dev_err(dev, "%s: Can't find %s DT node\n", __func__, prop);
308 goto dai_link_of_err;
311 ret = asoc_simple_card_parse_daifmt(dev, node, codec,
312 prefix, &dai_link->dai_fmt);
314 goto dai_link_of_err;
316 of_property_read_u32(node, "mclk-fs", &dai_props->mclk_fs);
318 ret = asoc_simple_card_parse_cpu(cpu, dai_link,
319 DAI, CELL, &single_cpu);
321 goto dai_link_of_err;
324 ret = asoc_simple_card_parse_codec(codec, dai_link, DAI, CELL);
326 goto dai_link_of_err;
328 ret = snd_soc_of_get_dai_link_codecs(dev, codec, dai_link);
330 dev_err(dev, "parse codec info error %d\n", ret);
331 goto dai_link_of_err;
333 dev_dbg(dev, "dai_link num_codecs = %d\n", dai_link->num_codecs);
336 ret = asoc_simple_card_parse_platform(plat, dai_link, DAI, CELL);
338 goto dai_link_of_err;
340 ret = snd_soc_of_parse_tdm_slot(cpu, &cpu_dai->tx_slot_mask,
341 &cpu_dai->rx_slot_mask,
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
349 goto dai_link_of_err;
351 ret = snd_soc_of_parse_tdm_slot(codec, &codec_dai->tx_slot_mask,
352 &codec_dai->rx_slot_mask,
354 &codec_dai->slot_width);
356 goto dai_link_of_err;
358 #if LINUX_VERSION_CODE <= KERNEL_VERSION(4,10,0)
359 ret = asoc_simple_card_parse_clk_cpu(cpu, dai_link, cpu_dai);
361 ret = asoc_simple_card_parse_clk_cpu(dev, cpu, dai_link, cpu_dai);
364 goto dai_link_of_err;
366 #if LINUX_VERSION_CODE <= KERNEL_VERSION(4,10,0)
367 ret = asoc_simple_card_parse_clk_codec(codec, dai_link, codec_dai);
369 ret = asoc_simple_card_parse_clk_codec(dev, codec, dai_link, codec_dai);
372 goto dai_link_of_err;
375 ret = asoc_simple_card_canonicalize_dailink(dai_link);
377 goto dai_link_of_err;
380 ret = asoc_simple_card_set_dailink_name(dev, dai_link,
382 dai_link->cpu_dai_name,
384 dai_link->codec_dai_name
386 dai_link->codecs[0].dai_name
390 goto dai_link_of_err;
392 dai_link->ops = &seeed_voice_card_ops;
393 dai_link->init = seeed_voice_card_dai_init;
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",
402 dai_link->codec_dai_name,
404 dai_link->codecs[0].dai_name,
406 dai_props->codec_dai.sysclk);
408 asoc_simple_card_canonicalize_cpu(dai_link, single_cpu);
417 static int seeed_voice_card_parse_aux_devs(struct device_node *node,
418 struct seeed_card_data *priv)
420 struct device *dev = seeed_priv_to_dev(priv);
421 struct device_node *aux_node;
424 if (!of_find_property(node, PREFIX "aux-devs", &len))
425 return 0; /* Ok to have no aux-devs */
427 n = len / sizeof(__be32);
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)
436 for (i = 0; i < n; i++) {
437 aux_node = of_parse_phandle(node, PREFIX "aux-devs", i);
440 priv->snd_card.aux_dev[i].codec_of_node = aux_node;
443 priv->snd_card.num_aux_devs = n;
447 static int seeed_voice_card_parse_of(struct device_node *node,
448 struct seeed_card_data *priv)
450 struct device *dev = seeed_priv_to_dev(priv);
451 struct device_node *dai_link;
457 dai_link = of_get_child_by_name(node, PREFIX "dai-link");
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,
468 if (of_property_read_bool(node, PREFIX "routing")) {
469 ret = snd_soc_of_parse_audio_routing(&priv->snd_card,
475 /* Factor to mclk, used in hw_params() */
476 of_property_read_u32(node, PREFIX "mclk-fs", &priv->mclk_fs);
478 /* Single/Muti DAI link(s) & New style of DT node */
480 struct device_node *np = NULL;
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,
494 /* For single DAI link & old style of DT node */
495 ret = seeed_voice_card_dai_link_of(node, priv, 0, true);
500 ret = asoc_simple_card_parse_card_name(&priv->snd_card, PREFIX);
504 ret = seeed_voice_card_parse_aux_devs(node, priv);
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);
520 of_node_put(dai_link);
525 static int seeed_voice_card_probe(struct platform_device *pdev)
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;
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);
540 /* Allocate the private data and the DAI link array */
541 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
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)
550 priv->dai_props = dai_props;
551 priv->dai_link = dai_link;
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;
559 if (np && of_device_is_available(np)) {
560 ret = seeed_voice_card_parse_of(np, priv);
562 if (ret != -EPROBE_DEFER)
563 dev_err(dev, "parse error %d\n", ret);
567 struct seeed_card_info *cinfo;
569 cinfo = dev->platform_data;
571 dev_err(dev, "no info for seeed-voice-card\n");
576 !cinfo->codec_dai.name ||
579 !cinfo->cpu_dai.name) {
580 dev_err(dev, "insufficient seeed_voice_card_info settings\n");
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));
599 snd_soc_card_set_drvdata(&priv->snd_card, priv);
601 #if CONFIG_AC10X_TRIG_LOCK
602 spin_lock_init(&priv->lock);
605 INIT_WORK(&priv->work_codec_clk, work_cb_codec_clk);
607 ret = devm_snd_soc_register_card(&pdev->dev, &priv->snd_card);
612 asoc_simple_card_clean_reference(&priv->snd_card);
617 static int seeed_voice_card_remove(struct platform_device *pdev)
619 struct snd_soc_card *card = platform_get_drvdata(pdev);
620 struct seeed_card_data *priv = snd_soc_card_get_drvdata(card);
622 if (cancel_work_sync(&priv->work_codec_clk) != 0) {
624 return asoc_simple_card_clean_reference(card);
627 static const struct of_device_id seeed_voice_of_match[] = {
628 { .compatible = "seeed-voicecard", },
631 MODULE_DEVICE_TABLE(of, seeed_voice_of_match);
633 static struct platform_driver seeed_voice_card = {
635 .name = "seeed-voicecard",
636 .pm = &snd_soc_pm_ops,
637 .of_match_table = seeed_voice_of_match,
639 .probe = seeed_voice_card_probe,
640 .remove = seeed_voice_card_remove,
643 module_platform_driver(seeed_voice_card);
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>");