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 #define _SET_CLOCK_CNT 2
81 static int (* _set_clock[_SET_CLOCK_CNT])(int y_start_n_stop);
83 static int seeed_voice_card_startup(struct snd_pcm_substream *substream)
85 struct snd_soc_pcm_runtime *rtd = substream->private_data;
86 struct seeed_card_data *priv = snd_soc_card_get_drvdata(rtd->card);
87 struct seeed_dai_props *dai_props =
88 seeed_priv_to_props(priv, rtd->num);
89 struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
92 ret = clk_prepare_enable(dai_props->cpu_dai.clk);
96 ret = clk_prepare_enable(dai_props->codec_dai.clk);
98 clk_disable_unprepare(dai_props->cpu_dai.clk);
100 if (cpu_dai->driver->playback.channels_min) {
101 priv->channels_playback_default = cpu_dai->driver->playback.channels_min;
103 if (cpu_dai->driver->capture.channels_min) {
104 priv->channels_capture_default = cpu_dai->driver->capture.channels_min;
106 cpu_dai->driver->playback.channels_min = priv->channels_playback_override;
107 cpu_dai->driver->playback.channels_max = priv->channels_playback_override;
108 cpu_dai->driver->capture.channels_min = priv->channels_capture_override;
109 cpu_dai->driver->capture.channels_max = priv->channels_capture_override;
114 static void seeed_voice_card_shutdown(struct snd_pcm_substream *substream)
116 struct snd_soc_pcm_runtime *rtd = substream->private_data;
117 struct snd_soc_dai *dai = asoc_rtd_to_codec(rtd, 0);
118 struct seeed_card_data *priv = snd_soc_card_get_drvdata(rtd->card);
119 struct seeed_dai_props *dai_props =
120 seeed_priv_to_props(priv, rtd->num);
121 struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
123 cpu_dai->driver->playback.channels_min = priv->channels_playback_default;
124 cpu_dai->driver->playback.channels_max = priv->channels_playback_default;
125 cpu_dai->driver->capture.channels_min = priv->channels_capture_default;
126 cpu_dai->driver->capture.channels_max = priv->channels_capture_default;
128 clk_disable_unprepare(dai_props->cpu_dai.clk);
130 clk_disable_unprepare(dai_props->codec_dai.clk);
132 if (dai->stream_active[SNDRV_PCM_STREAM_CAPTURE] &&
133 substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
136 /* interrupt environment */
137 if (in_irq() || in_nmi() || in_serving_softirq()) {
139 if (0 != schedule_work(&priv->work_codec_clk)) {
142 if (_set_clock[SNDRV_PCM_STREAM_CAPTURE]) _set_clock[SNDRV_PCM_STREAM_CAPTURE](0);
143 if (_set_clock[SNDRV_PCM_STREAM_PLAYBACK]) _set_clock[SNDRV_PCM_STREAM_PLAYBACK](0);
147 static int seeed_voice_card_hw_params(struct snd_pcm_substream *substream,
148 struct snd_pcm_hw_params *params)
150 struct snd_soc_pcm_runtime *rtd = substream->private_data;
151 struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
152 struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
153 struct seeed_card_data *priv = snd_soc_card_get_drvdata(rtd->card);
154 struct seeed_dai_props *dai_props =
155 seeed_priv_to_props(priv, rtd->num);
156 unsigned int mclk, mclk_fs = 0;
160 mclk_fs = priv->mclk_fs;
161 else if (dai_props->mclk_fs)
162 mclk_fs = dai_props->mclk_fs;
165 mclk = params_rate(params) * mclk_fs;
166 ret = snd_soc_dai_set_sysclk(codec_dai, 0, mclk,
168 if (ret && ret != -ENOTSUPP)
171 ret = snd_soc_dai_set_sysclk(cpu_dai, 0, mclk,
173 if (ret && ret != -ENOTSUPP)
182 int seeed_voice_card_prepare(struct snd_pcm_substream *substream)
184 struct snd_soc_pcm_runtime *rtd = substream->private_data;
185 struct seeed_card_data *priv = snd_soc_card_get_drvdata(rtd->card);
187 if (cancel_work_sync(&priv->work_codec_clk) != 0) {}
189 #if CONFIG_AC10X_TRIG_LOCK
190 /* I know it will degrades performance, but I have no choice */
191 spin_lock_irqsave(&priv->lock, flags);
193 if (_set_clock[SNDRV_PCM_STREAM_CAPTURE]) _set_clock[SNDRV_PCM_STREAM_CAPTURE](1);
194 if (_set_clock[SNDRV_PCM_STREAM_PLAYBACK]) _set_clock[SNDRV_PCM_STREAM_PLAYBACK](1);
195 #if CONFIG_AC10X_TRIG_LOCK
196 spin_unlock_irqrestore(&priv->lock, flags);
202 int seeed_voice_card_register_set_clock(int stream, int (*set_clock)(int)) {
203 if (! _set_clock[stream]) {
204 _set_clock[stream] = set_clock;
208 EXPORT_SYMBOL(seeed_voice_card_register_set_clock);
211 * work_cb_codec_clk: clear audio codec inner clock.
213 static void work_cb_codec_clk(struct work_struct *work)
215 struct seeed_card_data *priv = container_of(work, struct seeed_card_data, work_codec_clk);
218 if (_set_clock[SNDRV_PCM_STREAM_CAPTURE]) {
219 r = r || _set_clock[SNDRV_PCM_STREAM_CAPTURE](0);
221 if (_set_clock[SNDRV_PCM_STREAM_PLAYBACK]) {
222 r = r || _set_clock[SNDRV_PCM_STREAM_PLAYBACK](0);
225 if (r && priv->try_stop++ < TRY_STOP_MAX) {
226 if (0 != schedule_work(&priv->work_codec_clk)) {}
231 static struct snd_soc_ops seeed_voice_card_ops = {
232 .startup = seeed_voice_card_startup,
233 .shutdown = seeed_voice_card_shutdown,
234 .hw_params = seeed_voice_card_hw_params,
235 .prepare = seeed_voice_card_prepare,
238 static int asoc_card_parse_dai(struct device_node *node,
239 struct snd_soc_dai_link_component *dlc,
242 struct of_phandle_args args;
248 ret = of_parse_phandle_with_args(node, DAI, CELL, 0, &args);
252 ret = snd_soc_of_get_dai_name(node, &dlc->dai_name);
256 dlc->of_node = args.np;
259 *is_single_link = !args.args_count;
264 static int asoc_card_init_dai(struct snd_soc_dai *dai,
265 struct asoc_simple_dai *simple_dai)
272 if (simple_dai->sysclk) {
273 ret = snd_soc_dai_set_sysclk(dai, 0, simple_dai->sysclk,
274 simple_dai->clk_direction);
275 if (ret && ret != -ENOTSUPP) {
276 dev_err(dai->dev, "simple-card: set_sysclk error\n");
281 if (simple_dai->slots) {
282 ret = snd_soc_dai_set_tdm_slot(dai,
283 simple_dai->tx_slot_mask,
284 simple_dai->rx_slot_mask,
286 simple_dai->slot_width);
287 if (ret && ret != -ENOTSUPP) {
288 dev_err(dai->dev, "simple-card: set_tdm_slot error\n");
296 static int seeed_voice_card_dai_init(struct snd_soc_pcm_runtime *rtd)
298 struct seeed_card_data *priv = snd_soc_card_get_drvdata(rtd->card);
299 struct snd_soc_dai *codec = asoc_rtd_to_codec(rtd, 0);
300 struct snd_soc_dai *cpu = asoc_rtd_to_cpu(rtd, 0);
301 struct seeed_dai_props *dai_props =
302 seeed_priv_to_props(priv, rtd->num);
305 ret = asoc_card_init_dai(codec, &dai_props->codec_dai);
309 ret = asoc_card_init_dai(cpu, &dai_props->cpu_dai);
316 static int seeed_voice_card_dai_link_of(struct device_node *node,
317 struct seeed_card_data *priv,
319 bool is_top_level_node)
321 struct device *dev = seeed_priv_to_dev(priv);
322 struct snd_soc_dai_link *dai_link = seeed_priv_to_link(priv, idx);
323 struct seeed_dai_props *dai_props = seeed_priv_to_props(priv, idx);
324 struct asoc_simple_dai *cpu_dai = &dai_props->cpu_dai;
325 struct asoc_simple_dai *codec_dai = &dai_props->codec_dai;
326 struct device_node *cpu = NULL;
327 struct device_node *plat = NULL;
328 struct device_node *codec = NULL;
333 /* For single DAI link & old style of DT node */
334 if (is_top_level_node)
337 snprintf(prop, sizeof(prop), "%scpu", prefix);
338 cpu = of_get_child_by_name(node, prop);
340 snprintf(prop, sizeof(prop), "%splat", prefix);
341 plat = of_get_child_by_name(node, prop);
343 snprintf(prop, sizeof(prop), "%scodec", prefix);
344 codec = of_get_child_by_name(node, prop);
346 if (!cpu || !codec) {
348 dev_err(dev, "%s: Can't find %s DT node\n", __func__, prop);
349 goto dai_link_of_err;
352 ret = asoc_simple_parse_daifmt(dev, node, codec,
353 prefix, &dai_link->dai_fmt);
355 goto dai_link_of_err;
357 of_property_read_u32(node, "mclk-fs", &dai_props->mclk_fs);
359 ret = asoc_card_parse_dai(cpu, dai_link->cpus, &single_cpu);
361 goto dai_link_of_err;
364 ret = asoc_card_parse_dai(codec, dai_link->codecs, NULL);
366 goto dai_link_of_err;
368 ret = snd_soc_of_get_dai_link_codecs(dev, codec, dai_link);
370 dev_err(dev, "parse codec info error %d\n", ret);
371 goto dai_link_of_err;
373 dev_dbg(dev, "dai_link num_codecs = %d\n", dai_link->num_codecs);
376 ret = asoc_card_parse_dai(plat, dai_link->platforms, NULL);
378 goto dai_link_of_err;
380 ret = snd_soc_of_parse_tdm_slot(cpu, &cpu_dai->tx_slot_mask,
381 &cpu_dai->rx_slot_mask,
383 &cpu_dai->slot_width);
384 dev_dbg(dev, "cpu_dai : slot,width,tx,rx = %d,%d,%d,%d\n",
385 cpu_dai->slots, cpu_dai->slot_width,
386 cpu_dai->tx_slot_mask, cpu_dai->rx_slot_mask
389 goto dai_link_of_err;
391 ret = snd_soc_of_parse_tdm_slot(codec, &codec_dai->tx_slot_mask,
392 &codec_dai->rx_slot_mask,
394 &codec_dai->slot_width);
396 goto dai_link_of_err;
398 #if LINUX_VERSION_CODE <= KERNEL_VERSION(4,10,0)
399 ret = asoc_simple_parse_clk_cpu(cpu, dai_link, cpu_dai);
401 ret = asoc_simple_parse_clk_cpu(dev, cpu, dai_link, cpu_dai);
404 goto dai_link_of_err;
406 #if LINUX_VERSION_CODE <= KERNEL_VERSION(4,10,0)
407 ret = asoc_simple_parse_clk_codec(codec, dai_link, codec_dai);
409 ret = asoc_simple_parse_clk_codec(dev, codec, dai_link, codec_dai);
412 goto dai_link_of_err;
415 asoc_simple_canonicalize_platform(dai_link);
418 ret = asoc_simple_set_dailink_name(dev, dai_link,
420 dai_link->cpus->dai_name,
422 dai_link->codecs->dai_name
424 dai_link->codecs[0].dai_name
428 goto dai_link_of_err;
430 dai_link->ops = &seeed_voice_card_ops;
431 dai_link->init = seeed_voice_card_dai_init;
433 dev_dbg(dev, "\tname : %s\n", dai_link->stream_name);
434 dev_dbg(dev, "\tformat : %04x\n", dai_link->dai_fmt);
435 dev_dbg(dev, "\tcpu : %s / %d\n",
436 dai_link->cpus->dai_name,
437 dai_props->cpu_dai.sysclk);
438 dev_dbg(dev, "\tcodec : %s / %d\n",
440 dai_link->codecs->dai_name,
442 dai_link->codecs[0].dai_name,
444 dai_props->codec_dai.sysclk);
446 asoc_simple_canonicalize_cpu(dai_link, single_cpu);
455 static int seeed_voice_card_parse_aux_devs(struct device_node *node,
456 struct seeed_card_data *priv)
458 struct device *dev = seeed_priv_to_dev(priv);
459 struct device_node *aux_node;
462 if (!of_find_property(node, PREFIX "aux-devs", &len))
463 return 0; /* Ok to have no aux-devs */
465 n = len / sizeof(__be32);
469 priv->snd_card.aux_dev = devm_kzalloc(dev,
470 n * sizeof(*priv->snd_card.aux_dev), GFP_KERNEL);
471 if (!priv->snd_card.aux_dev)
474 for (i = 0; i < n; i++) {
475 aux_node = of_parse_phandle(node, PREFIX "aux-devs", i);
478 priv->snd_card.aux_dev[i].dlc.of_node = aux_node;
481 priv->snd_card.num_aux_devs = n;
485 static int seeed_voice_card_parse_of(struct device_node *node,
486 struct seeed_card_data *priv)
488 struct device *dev = seeed_priv_to_dev(priv);
489 struct device_node *dai_link;
495 dai_link = of_get_child_by_name(node, PREFIX "dai-link");
497 /* The off-codec widgets */
498 if (of_property_read_bool(node, PREFIX "widgets")) {
499 ret = snd_soc_of_parse_audio_simple_widgets(&priv->snd_card,
506 if (of_property_read_bool(node, PREFIX "routing")) {
507 ret = snd_soc_of_parse_audio_routing(&priv->snd_card,
513 /* Factor to mclk, used in hw_params() */
514 of_property_read_u32(node, PREFIX "mclk-fs", &priv->mclk_fs);
516 /* Single/Muti DAI link(s) & New style of DT node */
518 struct device_node *np = NULL;
521 for_each_child_of_node(node, np) {
522 dev_dbg(dev, "\tlink %d:\n", i);
523 ret = seeed_voice_card_dai_link_of(np, priv,
532 /* For single DAI link & old style of DT node */
533 ret = seeed_voice_card_dai_link_of(node, priv, 0, true);
538 ret = asoc_simple_parse_card_name(&priv->snd_card, PREFIX);
542 ret = seeed_voice_card_parse_aux_devs(node, priv);
544 priv->channels_playback_default = 0;
545 priv->channels_playback_override = 2;
546 priv->channels_capture_default = 0;
547 priv->channels_capture_override = 2;
548 of_property_read_u32(node, PREFIX "channels-playback-default",
549 &priv->channels_playback_default);
550 of_property_read_u32(node, PREFIX "channels-playback-override",
551 &priv->channels_playback_override);
552 of_property_read_u32(node, PREFIX "channels-capture-default",
553 &priv->channels_capture_default);
554 of_property_read_u32(node, PREFIX "channels-capture-override",
555 &priv->channels_capture_override);
558 of_node_put(dai_link);
563 static int seeed_voice_card_probe(struct platform_device *pdev)
565 struct seeed_card_data *priv;
566 struct snd_soc_dai_link *dai_link;
567 struct snd_soc_dai_link_component *compnent;
568 struct seeed_dai_props *dai_props;
569 struct device_node *np = pdev->dev.of_node;
570 struct device *dev = &pdev->dev;
573 /* Get the number of DAI links */
574 if (np && of_get_child_by_name(np, PREFIX "dai-link"))
575 num = of_get_child_count(np);
579 /* Allocate the private data and the DAI link array */
580 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
584 dai_props = devm_kzalloc(dev, sizeof(*dai_props) * num, GFP_KERNEL);
585 dai_link = devm_kzalloc(dev, sizeof(*dai_link) * num, GFP_KERNEL);
586 if (!dai_props || !dai_link)
589 compnent = devm_kzalloc(dev, 3 * sizeof(*compnent), GFP_KERNEL);
593 dai_link->cpus = &compnent[0];
594 dai_link->num_cpus = 1;
595 dai_link->codecs = &compnent[1];
596 dai_link->num_codecs = 1;
597 dai_link->platforms = &compnent[2];
598 dai_link->num_platforms = 1;
600 priv->dai_props = dai_props;
601 priv->dai_link = dai_link;
603 /* Init snd_soc_card */
604 priv->snd_card.owner = THIS_MODULE;
605 priv->snd_card.dev = dev;
606 priv->snd_card.dai_link = priv->dai_link;
607 priv->snd_card.num_links = num;
609 if (np && of_device_is_available(np)) {
610 ret = seeed_voice_card_parse_of(np, priv);
612 if (ret != -EPROBE_DEFER)
613 dev_err(dev, "parse error %d\n", ret);
617 struct seeed_card_info *cinfo;
619 cinfo = dev->platform_data;
621 dev_err(dev, "no info for seeed-voice-card\n");
626 !cinfo->codec_dai.name ||
629 !cinfo->cpu_dai.name) {
630 dev_err(dev, "insufficient seeed_voice_card_info settings\n");
634 priv->snd_card.name = (cinfo->card) ? cinfo->card : cinfo->name;
635 dai_link->name = cinfo->name;
636 dai_link->stream_name = cinfo->name;
637 dai_link->platforms->name = cinfo->platform;
638 dai_link->codecs->dai_name = cinfo->codec;
639 dai_link->cpus->dai_name = cinfo->cpu_dai.name;
640 dai_link->codecs->dai_name = cinfo->codec_dai.name;
641 dai_link->dai_fmt = cinfo->daifmt;
642 dai_link->init = seeed_voice_card_dai_init;
643 memcpy(&priv->dai_props->cpu_dai, &cinfo->cpu_dai,
644 sizeof(priv->dai_props->cpu_dai));
645 memcpy(&priv->dai_props->codec_dai, &cinfo->codec_dai,
646 sizeof(priv->dai_props->codec_dai));
649 snd_soc_card_set_drvdata(&priv->snd_card, priv);
651 #if CONFIG_AC10X_TRIG_LOCK
652 spin_lock_init(&priv->lock);
655 INIT_WORK(&priv->work_codec_clk, work_cb_codec_clk);
657 ret = devm_snd_soc_register_card(&pdev->dev, &priv->snd_card);
662 asoc_simple_clean_reference(&priv->snd_card);
667 static int seeed_voice_card_remove(struct platform_device *pdev)
669 struct snd_soc_card *card = platform_get_drvdata(pdev);
670 struct seeed_card_data *priv = snd_soc_card_get_drvdata(card);
672 if (cancel_work_sync(&priv->work_codec_clk) != 0) {
674 return asoc_simple_clean_reference(card);
677 static const struct of_device_id seeed_voice_of_match[] = {
678 { .compatible = "seeed-voicecard", },
681 MODULE_DEVICE_TABLE(of, seeed_voice_of_match);
683 static struct platform_driver seeed_voice_card = {
685 .name = "seeed-voicecard",
686 .pm = &snd_soc_pm_ops,
687 .of_match_table = seeed_voice_of_match,
689 .probe = seeed_voice_card_probe,
690 .remove = seeed_voice_card_remove,
693 module_platform_driver(seeed_voice_card);
695 MODULE_ALIAS("platform:seeed-voice-card");
696 MODULE_LICENSE("GPL v2");
697 MODULE_DESCRIPTION("ASoC SEEED Voice Card");
698 MODULE_AUTHOR("PeterYang<linsheng.yang@seeed.cc>");