From 920ef83b63f434a13b19ac015ed3163662b2d8d5 Mon Sep 17 00:00:00 2001 From: hhuysqt <1020988872@qq.com> Date: Mon, 30 Mar 2020 12:23:36 +0800 Subject: [PATCH 01/10] Add: compatible to linux kernel 5.3.0 or higher --- seeed-voicecard-v5.3.c | 731 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 731 insertions(+) create mode 100644 seeed-voicecard-v5.3.c diff --git a/seeed-voicecard-v5.3.c b/seeed-voicecard-v5.3.c new file mode 100644 index 0000000..82c9f19 --- /dev/null +++ b/seeed-voicecard-v5.3.c @@ -0,0 +1,731 @@ +/* + * SEEED voice card + * + * (C) Copyright 2017-2018 + * Seeed Technology Co., Ltd. + * + * base on ASoC simple sound card support + * + * Copyright (C) 2012 Renesas Solutions Corp. + * Kuninori Morimoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +/* #undef DEBUG */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ac10x.h" + +/* + * single codec: + * 0 - allow multi codec + * 1 - yes + */ +#define _SINGLE_CODEC 1 + +struct seeed_card_data { + struct snd_soc_card snd_card; + struct seeed_dai_props { + struct asoc_simple_dai cpu_dai; + struct asoc_simple_dai codec_dai; + struct snd_soc_dai_link_component cpus; /* single cpu */ + struct snd_soc_dai_link_component codecs; /* single codec */ + struct snd_soc_dai_link_component platforms; + unsigned int mclk_fs; + } *dai_props; + unsigned int mclk_fs; + unsigned channels_playback_default; + unsigned channels_playback_override; + unsigned channels_capture_default; + unsigned channels_capture_override; + struct snd_soc_dai_link *dai_link; + #if CONFIG_AC10X_TRIG_LOCK + spinlock_t lock; + #endif + struct work_struct work_codec_clk; + #define TRY_STOP_MAX 3 + int try_stop; +}; + +struct seeed_card_info { + const char *name; + const char *card; + const char *codec; + const char *platform; + + unsigned int daifmt; + struct asoc_simple_dai cpu_dai; + struct asoc_simple_dai codec_dai; +}; + +#define seeed_priv_to_dev(priv) ((priv)->snd_card.dev) +#define seeed_priv_to_link(priv, i) ((priv)->snd_card.dai_link + (i)) +#define seeed_priv_to_props(priv, i) ((priv)->dai_props + (i)) + +#define DAI "sound-dai" +#define CELL "#sound-dai-cells" +#define PREFIX "seeed-voice-card," + +static int seeed_voice_card_startup(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct seeed_card_data *priv = snd_soc_card_get_drvdata(rtd->card); + struct seeed_dai_props *dai_props = + seeed_priv_to_props(priv, rtd->num); + int ret; + + ret = clk_prepare_enable(dai_props->cpu_dai.clk); + if (ret) + return ret; + + ret = clk_prepare_enable(dai_props->codec_dai.clk); + if (ret) + clk_disable_unprepare(dai_props->cpu_dai.clk); + + if (rtd->cpu_dai->driver->playback.channels_min) { + priv->channels_playback_default = rtd->cpu_dai->driver->playback.channels_min; + } + if (rtd->cpu_dai->driver->capture.channels_min) { + priv->channels_capture_default = rtd->cpu_dai->driver->capture.channels_min; + } + rtd->cpu_dai->driver->playback.channels_min = priv->channels_playback_override; + rtd->cpu_dai->driver->playback.channels_max = priv->channels_playback_override; + rtd->cpu_dai->driver->capture.channels_min = priv->channels_capture_override; + rtd->cpu_dai->driver->capture.channels_max = priv->channels_capture_override; + + return ret; +} + +static void seeed_voice_card_shutdown(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct seeed_card_data *priv = snd_soc_card_get_drvdata(rtd->card); + struct seeed_dai_props *dai_props = + seeed_priv_to_props(priv, rtd->num); + + rtd->cpu_dai->driver->playback.channels_min = priv->channels_playback_default; + rtd->cpu_dai->driver->playback.channels_max = priv->channels_playback_default; + rtd->cpu_dai->driver->capture.channels_min = priv->channels_capture_default; + rtd->cpu_dai->driver->capture.channels_max = priv->channels_capture_default; + + clk_disable_unprepare(dai_props->cpu_dai.clk); + + clk_disable_unprepare(dai_props->codec_dai.clk); +} + +static int seeed_voice_card_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct seeed_card_data *priv = snd_soc_card_get_drvdata(rtd->card); + struct seeed_dai_props *dai_props = + seeed_priv_to_props(priv, rtd->num); + unsigned int mclk, mclk_fs = 0; + int ret = 0; + + if (priv->mclk_fs) + mclk_fs = priv->mclk_fs; + else if (dai_props->mclk_fs) + mclk_fs = dai_props->mclk_fs; + + if (mclk_fs) { + mclk = params_rate(params) * mclk_fs; + ret = snd_soc_dai_set_sysclk(codec_dai, 0, mclk, + SND_SOC_CLOCK_IN); + if (ret && ret != -ENOTSUPP) + goto err; + + ret = snd_soc_dai_set_sysclk(cpu_dai, 0, mclk, + SND_SOC_CLOCK_OUT); + if (ret && ret != -ENOTSUPP) + goto err; + } + return 0; +err: + return ret; +} + +#define _SET_CLOCK_CNT 2 +static int (* _set_clock[_SET_CLOCK_CNT])(int y_start_n_stop); + +int seeed_voice_card_register_set_clock(int stream, int (*set_clock)(int)) { + if (! _set_clock[stream]) { + _set_clock[stream] = set_clock; + } + return 0; +} +EXPORT_SYMBOL(seeed_voice_card_register_set_clock); + +/* + * work_cb_codec_clk: clear audio codec inner clock. + */ +static void work_cb_codec_clk(struct work_struct *work) +{ + struct seeed_card_data *priv = container_of(work, struct seeed_card_data, work_codec_clk); + int r = 0; + + if (_set_clock[SNDRV_PCM_STREAM_CAPTURE]) { + r = r || _set_clock[SNDRV_PCM_STREAM_CAPTURE](0); + } + if (_set_clock[SNDRV_PCM_STREAM_PLAYBACK]) { + r = r || _set_clock[SNDRV_PCM_STREAM_PLAYBACK](0); + } + + if (r && priv->try_stop++ < TRY_STOP_MAX) { + if (0 != schedule_work(&priv->work_codec_clk)) {} + } + return; +} + +static int seeed_voice_card_trigger(struct snd_pcm_substream *substream, int cmd) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *dai = rtd->codec_dai; + struct seeed_card_data *priv = snd_soc_card_get_drvdata(rtd->card); + #if CONFIG_AC10X_TRIG_LOCK + unsigned long flags; + #endif + int ret = 0; + + dev_dbg(rtd->card->dev, "%s() stream=%s cmd=%d play:%d, capt:%d\n", + __FUNCTION__, snd_pcm_stream_str(substream), cmd, + dai->playback_active, dai->capture_active); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + if (cancel_work_sync(&priv->work_codec_clk) != 0) {} + #if CONFIG_AC10X_TRIG_LOCK + /* I know it will degrades performance, but I have no choice */ + spin_lock_irqsave(&priv->lock, flags); + #endif + if (_set_clock[SNDRV_PCM_STREAM_CAPTURE]) _set_clock[SNDRV_PCM_STREAM_CAPTURE](1); + if (_set_clock[SNDRV_PCM_STREAM_PLAYBACK]) _set_clock[SNDRV_PCM_STREAM_PLAYBACK](1); + #if CONFIG_AC10X_TRIG_LOCK + spin_unlock_irqrestore(&priv->lock, flags); + #endif + break; + + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + /* capture channel resync, if overrun */ + if (dai->capture_active && substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + break; + } + + /* interrupt environment */ + if (in_irq() || in_nmi() || in_serving_softirq()) { + priv->try_stop = 0; + if (0 != schedule_work(&priv->work_codec_clk)) { + } + } else { + if (_set_clock[SNDRV_PCM_STREAM_CAPTURE]) _set_clock[SNDRV_PCM_STREAM_CAPTURE](0); + if (_set_clock[SNDRV_PCM_STREAM_PLAYBACK]) _set_clock[SNDRV_PCM_STREAM_PLAYBACK](0); + } + break; + default: + ret = -EINVAL; + } + + return ret; +} + +static struct snd_soc_ops seeed_voice_card_ops = { + .startup = seeed_voice_card_startup, + .shutdown = seeed_voice_card_shutdown, + .hw_params = seeed_voice_card_hw_params, + .trigger = seeed_voice_card_trigger, +}; + +/** + * add support for kernel >= 5.3.0 + * see patch: + * https://patchwork.kernel.org/patch/10860821/ + * https://patchwork.kernel.org/patch/10860823/ + */ +static int asoc_simple_parse_dai(struct device_node *node, + struct snd_soc_dai_link_component *dlc, + int *is_single_link) +{ + struct of_phandle_args args; + int ret; + + if (!node) + return 0; + + /* + * Get node via "sound-dai = <&phandle port>" + * it will be used as xxx_of_node on soc_bind_dai_link() + */ + ret = of_parse_phandle_with_args(node, DAI, CELL, 0, &args); + if (ret) + return ret; + + ret = snd_soc_of_get_dai_name(node, &dlc->dai_name); + if (ret < 0) + return ret; + + dlc->of_node = args.np; + + if (is_single_link) + *is_single_link = !args.args_count; + + return 0; +} + +static int asoc_simple_init_dai(struct snd_soc_dai *dai, + struct asoc_simple_dai *simple_dai) +{ + int ret; + + if (!simple_dai) + return 0; + + if (simple_dai->sysclk) { + ret = snd_soc_dai_set_sysclk(dai, 0, simple_dai->sysclk, + simple_dai->clk_direction); + if (ret && ret != -ENOTSUPP) { + dev_err(dai->dev, "simple-card: set_sysclk error\n"); + return ret; + } + } + + if (simple_dai->slots) { + ret = snd_soc_dai_set_tdm_slot(dai, + simple_dai->tx_slot_mask, + simple_dai->rx_slot_mask, + simple_dai->slots, + simple_dai->slot_width); + if (ret && ret != -ENOTSUPP) { + dev_err(dai->dev, "simple-card: set_tdm_slot error\n"); + return ret; + } + } + + return 0; +} + +static int seeed_voice_card_dai_init(struct snd_soc_pcm_runtime *rtd) +{ + struct seeed_card_data *priv = snd_soc_card_get_drvdata(rtd->card); + struct snd_soc_dai *codec = rtd->codec_dai; + struct snd_soc_dai *cpu = rtd->cpu_dai; + struct seeed_dai_props *dai_props = + seeed_priv_to_props(priv, rtd->num); + int ret; + + ret = asoc_simple_init_dai(codec, &dai_props->codec_dai); + if (ret < 0) + return ret; + + ret = asoc_simple_init_dai(cpu, &dai_props->cpu_dai); + if (ret < 0) + return ret; + + return 0; +} + +static int seeed_voice_card_dai_link_of(struct device_node *node, + struct seeed_card_data *priv, + int idx, + bool is_top_level_node) +{ + struct device *dev = seeed_priv_to_dev(priv); + struct snd_soc_dai_link *dai_link = seeed_priv_to_link(priv, idx); + struct seeed_dai_props *dai_props = seeed_priv_to_props(priv, idx); + struct asoc_simple_dai *cpu_dai = &dai_props->cpu_dai; + struct asoc_simple_dai *codec_dai = &dai_props->codec_dai; + struct device_node *cpu = NULL; + struct device_node *plat = NULL; + struct device_node *codec = NULL; + char prop[128]; + char *prefix = ""; + int ret, single_cpu; + + /* For single DAI link & old style of DT node */ + if (is_top_level_node) + prefix = PREFIX; + + snprintf(prop, sizeof(prop), "%scpu", prefix); + cpu = of_get_child_by_name(node, prop); + + if (!cpu) { + ret = -EINVAL; + dev_err(dev, "%s: Can't find %s DT node\n", __func__, prop); + goto dai_link_of_err; + } + + snprintf(prop, sizeof(prop), "%splat", prefix); + plat = of_get_child_by_name(node, prop); + + snprintf(prop, sizeof(prop), "%scodec", prefix); + codec = of_get_child_by_name(node, prop); + + if (!codec) { + ret = -EINVAL; + dev_err(dev, "%s: Can't find %s DT node\n", __func__, prop); + goto dai_link_of_err; + } + + ret = asoc_simple_parse_daifmt(dev, node, codec, + prefix, &dai_link->dai_fmt); + if (ret < 0) + goto dai_link_of_err; + + of_property_read_u32(node, "mclk-fs", &dai_props->mclk_fs); + + ret = asoc_simple_parse_cpu(cpu, dai_link, + &single_cpu); + if (ret < 0) + goto dai_link_of_err; + + #if _SINGLE_CODEC + ret = asoc_simple_parse_codec(codec, dai_link); + if (ret < 0) + goto dai_link_of_err; + #else + ret = snd_soc_of_get_dai_link_codecs(dev, codec, dai_link); + if (ret < 0) { + dev_err(dev, "parse codec info error %d\n", ret); + goto dai_link_of_err; + } + dev_dbg(dev, "dai_link num_codecs = %d\n", dai_link->num_codecs); + #endif + + ret = asoc_simple_parse_platform(plat, dai_link); + if (ret < 0) + goto dai_link_of_err; + + ret = snd_soc_of_parse_tdm_slot(cpu, &cpu_dai->tx_slot_mask, + &cpu_dai->rx_slot_mask, + &cpu_dai->slots, + &cpu_dai->slot_width); + dev_dbg(dev, "cpu_dai : slot,width,tx,rx = %d,%d,%d,%d\n", + cpu_dai->slots, cpu_dai->slot_width, + cpu_dai->tx_slot_mask, cpu_dai->rx_slot_mask + ); + if (ret < 0) + goto dai_link_of_err; + + ret = snd_soc_of_parse_tdm_slot(codec, &codec_dai->tx_slot_mask, + &codec_dai->rx_slot_mask, + &codec_dai->slots, + &codec_dai->slot_width); + if (ret < 0) + goto dai_link_of_err; + + #if LINUX_VERSION_CODE <= KERNEL_VERSION(4,10,0) + ret = asoc_simple_card_parse_clk_cpu(cpu, dai_link, cpu_dai); + #else + ret = asoc_simple_parse_clk_cpu(dev, cpu, dai_link, cpu_dai); + #endif + if (ret < 0) + goto dai_link_of_err; + + #if LINUX_VERSION_CODE <= KERNEL_VERSION(4,10,0) + ret = asoc_simple_card_parse_clk_codec(codec, dai_link, codec_dai); + #else + ret = asoc_simple_parse_clk_codec(dev, codec, dai_link, codec_dai); + #endif + if (ret < 0) + goto dai_link_of_err; + + ret = asoc_simple_set_dailink_name(dev, dai_link, + "%s-%s", + dai_link->cpus->dai_name, + #if _SINGLE_CODEC + dai_link->codecs->dai_name + #else + dai_link->codecs[0].dai_name + #endif + ); + if (ret < 0) + goto dai_link_of_err; + + dai_link->ops = &seeed_voice_card_ops; + dai_link->init = seeed_voice_card_dai_init; + + dev_dbg(dev, "\tname : %s\n", dai_link->stream_name); + dev_dbg(dev, "\tformat : %04x\n", dai_link->dai_fmt); + dev_dbg(dev, "\tcpu : %s / %d\n", + dai_link->cpus->dai_name, + dai_props->cpu_dai.sysclk); + dev_dbg(dev, "\tcodec : %s / %d\n", + #if _SINGLE_CODEC + dai_link->codecs->dai_name, + #else + dai_link->codecs[0].dai_name, + #endif + dai_props->codec_dai.sysclk); + + #if _SINGLE_CODEC + asoc_simple_canonicalize_platform(dai_link); + #endif + + asoc_simple_canonicalize_cpu(dai_link, single_cpu); + +dai_link_of_err: + of_node_put(cpu); + of_node_put(codec); + + return ret; +} + +static int seeed_voice_card_parse_aux_devs(struct device_node *node, + struct seeed_card_data *priv) +{ + struct device *dev = seeed_priv_to_dev(priv); + struct device_node *aux_node; + int i, n, len; + + if (!of_find_property(node, PREFIX "aux-devs", &len)) + return 0; /* Ok to have no aux-devs */ + + n = len / sizeof(__be32); + if (n <= 0) + return -EINVAL; + + priv->snd_card.aux_dev = devm_kzalloc(dev, + n * sizeof(*priv->snd_card.aux_dev), GFP_KERNEL); + if (!priv->snd_card.aux_dev) + return -ENOMEM; + + for (i = 0; i < n; i++) { + aux_node = of_parse_phandle(node, PREFIX "aux-devs", i); + if (!aux_node) + return -EINVAL; + priv->snd_card.aux_dev[i].codec_of_node = aux_node; + } + + priv->snd_card.num_aux_devs = n; + return 0; +} + +static int seeed_voice_card_parse_of(struct device_node *node, + struct seeed_card_data *priv) +{ + struct device *dev = seeed_priv_to_dev(priv); + struct device_node *dai_link; + int ret; + + if (!node) + return -EINVAL; + + dai_link = of_get_child_by_name(node, PREFIX "dai-link"); + + /* The off-codec widgets */ + if (of_property_read_bool(node, PREFIX "widgets")) { + ret = snd_soc_of_parse_audio_simple_widgets(&priv->snd_card, + PREFIX "widgets"); + if (ret) + goto card_parse_end; + } + + /* DAPM routes */ + if (of_property_read_bool(node, PREFIX "routing")) { + ret = snd_soc_of_parse_audio_routing(&priv->snd_card, + PREFIX "routing"); + if (ret) + goto card_parse_end; + } + + /* Factor to mclk, used in hw_params() */ + of_property_read_u32(node, PREFIX "mclk-fs", &priv->mclk_fs); + + /* Single/Muti DAI link(s) & New style of DT node */ + if (dai_link) { + struct device_node *np = NULL; + int i = 0; + + for_each_child_of_node(node, np) { + dev_dbg(dev, "\tlink %d:\n", i); + ret = seeed_voice_card_dai_link_of(np, priv, + i, false); + if (ret < 0) { + of_node_put(np); + goto card_parse_end; + } + i++; + } + } else { + /* For single DAI link & old style of DT node */ + ret = seeed_voice_card_dai_link_of(node, priv, 0, true); + if (ret < 0) + goto card_parse_end; + } + + ret = asoc_simple_parse_card_name(&priv->snd_card, PREFIX); + if (ret < 0) + goto card_parse_end; + + ret = seeed_voice_card_parse_aux_devs(node, priv); + + priv->channels_playback_default = 0; + priv->channels_playback_override = 2; + priv->channels_capture_default = 0; + priv->channels_capture_override = 2; + of_property_read_u32(node, PREFIX "channels-playback-default", + &priv->channels_playback_default); + of_property_read_u32(node, PREFIX "channels-playback-override", + &priv->channels_playback_override); + of_property_read_u32(node, PREFIX "channels-capture-default", + &priv->channels_capture_default); + of_property_read_u32(node, PREFIX "channels-capture-override", + &priv->channels_capture_override); + +card_parse_end: + of_node_put(dai_link); + + return ret; +} + +static int seeed_voice_card_probe(struct platform_device *pdev) +{ + struct seeed_card_data *priv; + struct snd_soc_dai_link *dai_link; + struct seeed_dai_props *dai_props; + struct device_node *np = pdev->dev.of_node; + struct device *dev = &pdev->dev; + int num, ret; + + /* Get the number of DAI links */ + if (np && of_get_child_by_name(np, PREFIX "dai-link")) + num = of_get_child_count(np); + else + num = 1; + + /* Allocate the private data and the DAI link array */ + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + dai_props = devm_kzalloc(dev, sizeof(*dai_props) * num, GFP_KERNEL); + dai_link = devm_kzalloc(dev, sizeof(*dai_link) * num, GFP_KERNEL); + if (!dai_props || !dai_link) + return -ENOMEM; + + priv->dai_props = dai_props; + priv->dai_link = dai_link; + + /** + * Only support single template for now + */ + dai_link->cpus = &dai_props->cpus; + dai_link->codecs = &dai_props->codecs; + dai_link->platforms = &dai_props->platforms; + dai_link->num_cpus = + dai_link->num_codecs = + dai_link->num_platforms = 1; + + /* Init snd_soc_card */ + priv->snd_card.owner = THIS_MODULE; + priv->snd_card.dev = dev; + priv->snd_card.dai_link = priv->dai_link; + priv->snd_card.num_links = num; + + if (np && of_device_is_available(np)) { + ret = seeed_voice_card_parse_of(np, priv); + if (ret < 0) { + if (ret != -EPROBE_DEFER) + dev_err(dev, "parse error %d\n", ret); + goto err; + } + } else { + struct seeed_card_info *cinfo; + + cinfo = dev->platform_data; + if (!cinfo) { + dev_err(dev, "no info for seeed-voice-card\n"); + return -EINVAL; + } + + if (!cinfo->name || + !cinfo->codec_dai.name || + !cinfo->codec || + !cinfo->platform || + !cinfo->cpu_dai.name) { + dev_err(dev, "insufficient seeed_voice_card_info settings\n"); + return -EINVAL; + } + + priv->snd_card.name = (cinfo->card) ? cinfo->card : cinfo->name; + dai_link->name = cinfo->name; + dai_link->stream_name = cinfo->name; + dai_link->platforms->name = cinfo->platform; + dai_link->codecs->name = cinfo->codec; + dai_link->cpus->dai_name = cinfo->cpu_dai.name; + dai_link->codecs->dai_name = cinfo->codec_dai.name; + dai_link->dai_fmt = cinfo->daifmt; + dai_link->init = seeed_voice_card_dai_init; + memcpy(&priv->dai_props->cpu_dai, &cinfo->cpu_dai, + sizeof(priv->dai_props->cpu_dai)); + memcpy(&priv->dai_props->codec_dai, &cinfo->codec_dai, + sizeof(priv->dai_props->codec_dai)); + } + + snd_soc_card_set_drvdata(&priv->snd_card, priv); + + #if CONFIG_AC10X_TRIG_LOCK + spin_lock_init(&priv->lock); + #endif + + INIT_WORK(&priv->work_codec_clk, work_cb_codec_clk); + + ret = devm_snd_soc_register_card(&pdev->dev, &priv->snd_card); + if (ret >= 0) + return ret; + +err: + asoc_simple_clean_reference(&priv->snd_card); + + return ret; +} + +static int seeed_voice_card_remove(struct platform_device *pdev) +{ + struct snd_soc_card *card = platform_get_drvdata(pdev); + struct seeed_card_data *priv = snd_soc_card_get_drvdata(card); + + if (cancel_work_sync(&priv->work_codec_clk) != 0) { + } + return asoc_simple_clean_reference(card); +} + +static const struct of_device_id seeed_voice_of_match[] = { + { .compatible = "seeed-voicecard", }, + {}, +}; +MODULE_DEVICE_TABLE(of, seeed_voice_of_match); + +static struct platform_driver seeed_voice_card = { + .driver = { + .name = "seeed-voicecard", + .pm = &snd_soc_pm_ops, + .of_match_table = seeed_voice_of_match, + }, + .probe = seeed_voice_card_probe, + .remove = seeed_voice_card_remove, +}; + +module_platform_driver(seeed_voice_card); + +MODULE_ALIAS("platform:seeed-voice-card"); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("ASoC SEEED Voice Card"); +MODULE_AUTHOR("PeterYang"); From 4396e4f834d97919df166cac6cbc74f288465a93 Mon Sep 17 00:00:00 2001 From: Hin-Tak Leung Date: Wed, 5 Aug 2020 01:42:57 +0100 Subject: [PATCH 02/10] v4.20: support snd_soc_dai_link_component style for codec commit 710af9196ce614ee02185c2ec55e617a71843183 Author: Kuninori Morimoto Date: Fri Aug 31 03:08:24 2018 +0000 ASoC: simple-card: support snd_soc_dai_link_component style for codec Current ASoC is supporting snd_soc_dai_link_component for binding, it is more useful than current legacy style. Currently only codec is supporting it as multicodec (= codecs). CPU will support multi style in the future. We want to have it on Platform too in the future. If all Codec/CPU/Platform are replaced into snd_soc_dai_link_component style, we can remove legacy complex style. This patch supports snd_soc_dai_link_component style for simple-card for codec. --- seeed-voicecard.c | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/seeed-voicecard.c b/seeed-voicecard.c index 82e8893..5436db3 100644 --- a/seeed-voicecard.c +++ b/seeed-voicecard.c @@ -42,6 +42,7 @@ struct seeed_card_data { struct seeed_dai_props { struct asoc_simple_dai cpu_dai; struct asoc_simple_dai codec_dai; + struct snd_soc_dai_link_component codecs; /* single codec */ unsigned int mclk_fs; } *dai_props; unsigned int mclk_fs; @@ -561,7 +562,7 @@ static int seeed_voice_card_probe(struct platform_device *pdev) struct seeed_dai_props *dai_props; struct device_node *np = pdev->dev.of_node; struct device *dev = &pdev->dev; - int num, ret; + int num, ret, i; /* Get the number of DAI links */ if (np && of_get_child_by_name(np, PREFIX "dai-link")) @@ -579,6 +580,17 @@ static int seeed_voice_card_probe(struct platform_device *pdev) if (!dai_props || !dai_link) return -ENOMEM; + /* + * Use snd_soc_dai_link_component instead of legacy style + * It is codec only. but cpu/platform will be supported in the future. + * see + * soc-core.c :: snd_soc_init_multicodec() + */ + for (i = 0; i < num; i++) { + dai_link[i].codecs = &dai_props[i].codecs; + dai_link[i].num_codecs = 1; + } + priv->dai_props = dai_props; priv->dai_link = dai_link; @@ -597,6 +609,7 @@ static int seeed_voice_card_probe(struct platform_device *pdev) } } else { struct seeed_card_info *cinfo; + struct snd_soc_dai_link_component *codecs; cinfo = dev->platform_data; if (!cinfo) { @@ -613,13 +626,15 @@ static int seeed_voice_card_probe(struct platform_device *pdev) return -EINVAL; } + codecs = dai_link->codecs; + codecs->name = cinfo->codec; + codecs->dai_name = cinfo->codec_dai.name; + priv->snd_card.name = (cinfo->card) ? cinfo->card : cinfo->name; dai_link->name = cinfo->name; dai_link->stream_name = cinfo->name; dai_link->platforms->name = cinfo->platform; - dai_link->codecs->name = cinfo->codec; dai_link->cpus->dai_name = cinfo->cpu_dai.name; - dai_link->codecs->dai_name = cinfo->codec_dai.name; dai_link->dai_fmt = cinfo->daifmt; dai_link->init = asoc_simple_dai_init; memcpy(&priv->dai_props->cpu_dai, &cinfo->cpu_dai, From 953fa50f4f1c82fa90846863113790152d9c2131 Mon Sep 17 00:00:00 2001 From: Hin-Tak Leung Date: Wed, 5 Aug 2020 02:07:32 +0100 Subject: [PATCH 03/10] v4.20: support snd_soc_dai_link_component style for platform commit e58f41e41185c6906bd11c73c4e76aa5fc3ea685 Author: Kuninori Morimoto Date: Fri Aug 31 03:10:33 2018 +0000 ASoC: simple-card: support snd_soc_dai_link_component style for platform Current ASoC is supporting snd_soc_dai_link_component for binding, it is more useful than current legacy style. Currently only codec is supporting it as multicodec (= codecs). CPU will support multi style in the future. We want to have it on Platform too in the future. If all Codec/CPU/Platform are replaced into snd_soc_dai_link_component style, we can remove legacy complex style. This patch supports snd_soc_dai_link_component style for simple-card for platform. --- seeed-voicecard.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/seeed-voicecard.c b/seeed-voicecard.c index 5436db3..6a23508 100644 --- a/seeed-voicecard.c +++ b/seeed-voicecard.c @@ -43,6 +43,7 @@ struct seeed_card_data { struct asoc_simple_dai cpu_dai; struct asoc_simple_dai codec_dai; struct snd_soc_dai_link_component codecs; /* single codec */ + struct snd_soc_dai_link_component platform; unsigned int mclk_fs; } *dai_props; unsigned int mclk_fs; @@ -589,6 +590,7 @@ static int seeed_voice_card_probe(struct platform_device *pdev) for (i = 0; i < num; i++) { dai_link[i].codecs = &dai_props[i].codecs; dai_link[i].num_codecs = 1; + dai_link[i].platforms = &dai_props[i].platform; } priv->dai_props = dai_props; @@ -610,6 +612,7 @@ static int seeed_voice_card_probe(struct platform_device *pdev) } else { struct seeed_card_info *cinfo; struct snd_soc_dai_link_component *codecs; + struct snd_soc_dai_link_component *platform; cinfo = dev->platform_data; if (!cinfo) { @@ -630,10 +633,12 @@ static int seeed_voice_card_probe(struct platform_device *pdev) codecs->name = cinfo->codec; codecs->dai_name = cinfo->codec_dai.name; + platform = dai_link->platforms; + platform->name = cinfo->platform; + priv->snd_card.name = (cinfo->card) ? cinfo->card : cinfo->name; dai_link->name = cinfo->name; dai_link->stream_name = cinfo->name; - dai_link->platforms->name = cinfo->platform; dai_link->cpus->dai_name = cinfo->cpu_dai.name; dai_link->dai_fmt = cinfo->daifmt; dai_link->init = asoc_simple_dai_init; From 6afe18224aa7167f3bf7e8e1a6dded9a8a6956a0 Mon Sep 17 00:00:00 2001 From: Hin-Tak Leung Date: Wed, 5 Aug 2020 02:18:23 +0100 Subject: [PATCH 04/10] v5.1: add .num_platform for dai_link commit 910fdcabedd2354d161b1beab6ad7dc7e859651d Author: Kuninori Morimoto Date: Mon Jan 21 09:32:32 2019 +0900 ASoC: soc-core: add .num_platform for dai_link Current snd_soc_dai_link is starting to use snd_soc_dai_link_component (= modern) style for Platform, but it is still assuming single Platform so far. We will need to have multi Platform support in the not far future. Currently only simple card is using it as sound card driver, and other drivers are converted to it from legacy style by snd_soc_init_platform(). To avoid future problem of multi Platform support, let's add num_platforms before it is too late. In the same time, to make it same naming mothed, "platform" should be "platforms". This patch fixup it too. --- seeed-voicecard.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/seeed-voicecard.c b/seeed-voicecard.c index 6a23508..0b0fb8f 100644 --- a/seeed-voicecard.c +++ b/seeed-voicecard.c @@ -43,7 +43,7 @@ struct seeed_card_data { struct asoc_simple_dai cpu_dai; struct asoc_simple_dai codec_dai; struct snd_soc_dai_link_component codecs; /* single codec */ - struct snd_soc_dai_link_component platform; + struct snd_soc_dai_link_component platforms; unsigned int mclk_fs; } *dai_props; unsigned int mclk_fs; @@ -590,7 +590,8 @@ static int seeed_voice_card_probe(struct platform_device *pdev) for (i = 0; i < num; i++) { dai_link[i].codecs = &dai_props[i].codecs; dai_link[i].num_codecs = 1; - dai_link[i].platforms = &dai_props[i].platform; + dai_link[i].platforms = &dai_props[i].platforms; + dai_link[i].num_platforms = 1; } priv->dai_props = dai_props; From 51966603da5054e252f1e20162a684a66762d930 Mon Sep 17 00:00:00 2001 From: Hin-Tak Leung Date: Wed, 5 Aug 2020 02:30:53 +0100 Subject: [PATCH 05/10] v5.3: support snd_soc_dai_link_component style for cpu commit f107294c6422e772773b53dbf802186175b6289e Author: Kuninori Morimoto Date: Thu Jun 6 13:07:35 2019 +0900 ASoC: simple-card: support snd_soc_dai_link_component style for cpu ASoC supports modern style dai_link (= snd_soc_dai_link_component) for CPU. legacy style dai_link (= cpu_dai_name, cpu_name, cpu_of_node) are no longer needed. This patch switches to modern style. --- seeed-voicecard.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/seeed-voicecard.c b/seeed-voicecard.c index 0b0fb8f..2ee7966 100644 --- a/seeed-voicecard.c +++ b/seeed-voicecard.c @@ -42,6 +42,7 @@ struct seeed_card_data { struct seeed_dai_props { struct asoc_simple_dai cpu_dai; struct asoc_simple_dai codec_dai; + struct snd_soc_dai_link_component cpus; /* single cpu */ struct snd_soc_dai_link_component codecs; /* single codec */ struct snd_soc_dai_link_component platforms; unsigned int mclk_fs; @@ -588,6 +589,8 @@ static int seeed_voice_card_probe(struct platform_device *pdev) * soc-core.c :: snd_soc_init_multicodec() */ for (i = 0; i < num; i++) { + dai_link[i].cpus = &dai_props[i].cpus; + dai_link[i].num_cpus = 1; dai_link[i].codecs = &dai_props[i].codecs; dai_link[i].num_codecs = 1; dai_link[i].platforms = &dai_props[i].platforms; @@ -612,6 +615,7 @@ static int seeed_voice_card_probe(struct platform_device *pdev) } } else { struct seeed_card_info *cinfo; + struct snd_soc_dai_link_component *cpus; struct snd_soc_dai_link_component *codecs; struct snd_soc_dai_link_component *platform; @@ -630,6 +634,9 @@ static int seeed_voice_card_probe(struct platform_device *pdev) return -EINVAL; } + cpus = dai_link->cpus; + cpus->dai_name = cinfo->cpu_dai.name; + codecs = dai_link->codecs; codecs->name = cinfo->codec; codecs->dai_name = cinfo->codec_dai.name; @@ -640,7 +647,6 @@ static int seeed_voice_card_probe(struct platform_device *pdev) priv->snd_card.name = (cinfo->card) ? cinfo->card : cinfo->name; dai_link->name = cinfo->name; dai_link->stream_name = cinfo->name; - dai_link->cpus->dai_name = cinfo->cpu_dai.name; dai_link->dai_fmt = cinfo->daifmt; dai_link->init = asoc_simple_dai_init; memcpy(&priv->dai_props->cpu_dai, &cinfo->cpu_dai, From ed094dece557fca856c72b1420c4b978ac42e08d Mon Sep 17 00:00:00 2001 From: Hin-Tak Leung Date: Wed, 5 Aug 2020 03:26:15 +0100 Subject: [PATCH 06/10] v5.2: comments The init code was extracted into asoc_simple_card_init_priv(), with additional comments. commit 65a5056b21202eff7f54243e587183f4bb6ed352 Author: Kuninori Morimoto Date: Wed Mar 20 13:56:26 2019 +0900 ASoC: simple-card-utils: share asoc_simple_card_init_priv() The difference between simple-card / audio-graph are just using OF graph style, or not. In other words, other things should be same. This means, simple-card/audio-graph common functions should be implemented at simple-card-utils, and its own functions should be implemented at each files. Current simple-card / audio-graph are initializing each priv, but it is same operation. This patch adds new asoc_simple_card_init_priv() and initialize priv by same operation. --- seeed-voicecard.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/seeed-voicecard.c b/seeed-voicecard.c index 2ee7966..e8299d5 100644 --- a/seeed-voicecard.c +++ b/seeed-voicecard.c @@ -587,6 +587,10 @@ static int seeed_voice_card_probe(struct platform_device *pdev) * It is codec only. but cpu/platform will be supported in the future. * see * soc-core.c :: snd_soc_init_multicodec() + * + * "platform" might be removed + * see + * simple-card-utils.c :: asoc_simple_canonicalize_platform() */ for (i = 0; i < num; i++) { dai_link[i].cpus = &dai_props[i].cpus; From c2b419c2862d0ba5ead1007d0823c3747d9e8e49 Mon Sep 17 00:00:00 2001 From: Hin-Tak Leung Date: Wed, 5 Aug 2020 04:28:16 +0100 Subject: [PATCH 07/10] Revert "asoc_simple_card_init_dai is not exported, but asoc_simple_dai_init is, and does more" This reverts commit de22f922980be74249d0629be23236cfee1fe56a. That commit was wrong: seeed_priv_to_props() is not identical to simple_priv_props(). Conflicts: seeed-voicecard.c --- seeed-voicecard.c | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/seeed-voicecard.c b/seeed-voicecard.c index e8299d5..6b9bb81 100644 --- a/seeed-voicecard.c +++ b/seeed-voicecard.c @@ -304,6 +304,26 @@ static int asoc_simple_parse_dai(struct device_node *node, return 0; } +static int seeed_voice_card_dai_init(struct snd_soc_pcm_runtime *rtd) +{ + struct seeed_card_data *priv = snd_soc_card_get_drvdata(rtd->card); + struct snd_soc_dai *codec = rtd->codec_dai; + struct snd_soc_dai *cpu = rtd->cpu_dai; + struct seeed_dai_props *dai_props = + seeed_priv_to_props(priv, rtd->num); + int ret; + + ret = asoc_simple_init_dai(codec, &dai_props->codec_dai); + if (ret < 0) + return ret; + + ret = asoc_simple_init_dai(cpu, &dai_props->cpu_dai); + if (ret < 0) + return ret; + + return 0; +} + static int seeed_voice_card_dai_link_of(struct device_node *node, struct seeed_card_data *priv, int idx, @@ -425,7 +445,7 @@ static int seeed_voice_card_dai_link_of(struct device_node *node, goto dai_link_of_err; dai_link->ops = &seeed_voice_card_ops; - dai_link->init = asoc_simple_dai_init; + dai_link->init = seeed_voice_card_dai_init; dev_dbg(dev, "\tname : %s\n", dai_link->stream_name); dev_dbg(dev, "\tformat : %04x\n", dai_link->dai_fmt); @@ -652,7 +672,7 @@ static int seeed_voice_card_probe(struct platform_device *pdev) dai_link->name = cinfo->name; dai_link->stream_name = cinfo->name; dai_link->dai_fmt = cinfo->daifmt; - dai_link->init = asoc_simple_dai_init; + dai_link->init = seeed_voice_card_dai_init; memcpy(&priv->dai_props->cpu_dai, &cinfo->cpu_dai, sizeof(priv->dai_props->cpu_dai)); memcpy(&priv->dai_props->codec_dai, &cinfo->codec_dai, From fcbc3b6d6b4e5b0e517e9bcaf32251f200d57f71 Mon Sep 17 00:00:00 2001 From: Hin-Tak Leung Date: Wed, 5 Aug 2020 04:42:41 +0100 Subject: [PATCH 08/10] copy asoc_simple_card_init_dai() from v5.7:sound/soc/generic/simple-card-utils.c It was made private in v5.2: commit ad934ca8010843482d61fda46786449a9bc99e10 Author: Kuninori Morimoto Date: Wed Mar 20 13:55:52 2019 +0900 ASoC: simple-card-utils: share asoc_simple_dai_init() --- seeed-voicecard.c | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/seeed-voicecard.c b/seeed-voicecard.c index 6b9bb81..a620e55 100644 --- a/seeed-voicecard.c +++ b/seeed-voicecard.c @@ -304,6 +304,38 @@ static int asoc_simple_parse_dai(struct device_node *node, return 0; } +static int asoc_simple_init_dai(struct snd_soc_dai *dai, + struct asoc_simple_dai *simple_dai) +{ + int ret; + + if (!simple_dai) + return 0; + + if (simple_dai->sysclk) { + ret = snd_soc_dai_set_sysclk(dai, 0, simple_dai->sysclk, + simple_dai->clk_direction); + if (ret && ret != -ENOTSUPP) { + dev_err(dai->dev, "simple-card: set_sysclk error\n"); + return ret; + } + } + + if (simple_dai->slots) { + ret = snd_soc_dai_set_tdm_slot(dai, + simple_dai->tx_slot_mask, + simple_dai->rx_slot_mask, + simple_dai->slots, + simple_dai->slot_width); + if (ret && ret != -ENOTSUPP) { + dev_err(dai->dev, "simple-card: set_tdm_slot error\n"); + return ret; + } + } + + return 0; +} + static int seeed_voice_card_dai_init(struct snd_soc_pcm_runtime *rtd) { struct seeed_card_data *priv = snd_soc_card_get_drvdata(rtd->card); From 0be1de9750b18a244244d0ed0ca19d1bc4a1b2b9 Mon Sep 17 00:00:00 2001 From: Hin-Tak Leung Date: Wed, 5 Aug 2020 05:26:01 +0100 Subject: [PATCH 09/10] Revert previous over-zealous "rename asoc_simple_card_xxx() to asoc_simple_()" Two of them should not have been changed, as they were protected and intended for older kernels. --- seeed-voicecard.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/seeed-voicecard.c b/seeed-voicecard.c index a620e55..0ba8b72 100644 --- a/seeed-voicecard.c +++ b/seeed-voicecard.c @@ -445,7 +445,7 @@ static int seeed_voice_card_dai_link_of(struct device_node *node, goto dai_link_of_err; #if LINUX_VERSION_CODE <= KERNEL_VERSION(4,10,0) - ret = asoc_simple_parse_clk_cpu(cpu, dai_link, cpu_dai); + ret = asoc_simple_card_parse_clk_cpu(cpu, dai_link, cpu_dai); #else ret = asoc_simple_parse_clk_cpu(dev, cpu, dai_link, cpu_dai); #endif @@ -453,7 +453,7 @@ static int seeed_voice_card_dai_link_of(struct device_node *node, goto dai_link_of_err; #if LINUX_VERSION_CODE <= KERNEL_VERSION(4,10,0) - ret = asoc_simple_parse_clk_codec(codec, dai_link, codec_dai); + ret = asoc_simple_card_parse_clk_codec(codec, dai_link, codec_dai); #else ret = asoc_simple_parse_clk_codec(dev, codec, dai_link, codec_dai); #endif From aaf0cc2c61a6b7013650eece314c0f56fe9596e7 Mon Sep 17 00:00:00 2001 From: Hin-Tak Leung Date: Wed, 5 Aug 2020 06:05:34 +0100 Subject: [PATCH 10/10] move files in place, to capture hhuysqt <1020988872@qq.com>'s ubuntu-porting work git rm seeed-voicecard.c git mv seeed-voicecard-v5.3.c seeed-voicecard.c --- seeed-voicecard-v5.3.c | 731 ----------------------------------------- seeed-voicecard.c | 135 ++++++-- 2 files changed, 107 insertions(+), 759 deletions(-) delete mode 100644 seeed-voicecard-v5.3.c diff --git a/seeed-voicecard-v5.3.c b/seeed-voicecard-v5.3.c deleted file mode 100644 index 82c9f19..0000000 --- a/seeed-voicecard-v5.3.c +++ /dev/null @@ -1,731 +0,0 @@ -/* - * SEEED voice card - * - * (C) Copyright 2017-2018 - * Seeed Technology Co., Ltd. - * - * base on ASoC simple sound card support - * - * Copyright (C) 2012 Renesas Solutions Corp. - * Kuninori Morimoto - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ -/* #undef DEBUG */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "ac10x.h" - -/* - * single codec: - * 0 - allow multi codec - * 1 - yes - */ -#define _SINGLE_CODEC 1 - -struct seeed_card_data { - struct snd_soc_card snd_card; - struct seeed_dai_props { - struct asoc_simple_dai cpu_dai; - struct asoc_simple_dai codec_dai; - struct snd_soc_dai_link_component cpus; /* single cpu */ - struct snd_soc_dai_link_component codecs; /* single codec */ - struct snd_soc_dai_link_component platforms; - unsigned int mclk_fs; - } *dai_props; - unsigned int mclk_fs; - unsigned channels_playback_default; - unsigned channels_playback_override; - unsigned channels_capture_default; - unsigned channels_capture_override; - struct snd_soc_dai_link *dai_link; - #if CONFIG_AC10X_TRIG_LOCK - spinlock_t lock; - #endif - struct work_struct work_codec_clk; - #define TRY_STOP_MAX 3 - int try_stop; -}; - -struct seeed_card_info { - const char *name; - const char *card; - const char *codec; - const char *platform; - - unsigned int daifmt; - struct asoc_simple_dai cpu_dai; - struct asoc_simple_dai codec_dai; -}; - -#define seeed_priv_to_dev(priv) ((priv)->snd_card.dev) -#define seeed_priv_to_link(priv, i) ((priv)->snd_card.dai_link + (i)) -#define seeed_priv_to_props(priv, i) ((priv)->dai_props + (i)) - -#define DAI "sound-dai" -#define CELL "#sound-dai-cells" -#define PREFIX "seeed-voice-card," - -static int seeed_voice_card_startup(struct snd_pcm_substream *substream) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct seeed_card_data *priv = snd_soc_card_get_drvdata(rtd->card); - struct seeed_dai_props *dai_props = - seeed_priv_to_props(priv, rtd->num); - int ret; - - ret = clk_prepare_enable(dai_props->cpu_dai.clk); - if (ret) - return ret; - - ret = clk_prepare_enable(dai_props->codec_dai.clk); - if (ret) - clk_disable_unprepare(dai_props->cpu_dai.clk); - - if (rtd->cpu_dai->driver->playback.channels_min) { - priv->channels_playback_default = rtd->cpu_dai->driver->playback.channels_min; - } - if (rtd->cpu_dai->driver->capture.channels_min) { - priv->channels_capture_default = rtd->cpu_dai->driver->capture.channels_min; - } - rtd->cpu_dai->driver->playback.channels_min = priv->channels_playback_override; - rtd->cpu_dai->driver->playback.channels_max = priv->channels_playback_override; - rtd->cpu_dai->driver->capture.channels_min = priv->channels_capture_override; - rtd->cpu_dai->driver->capture.channels_max = priv->channels_capture_override; - - return ret; -} - -static void seeed_voice_card_shutdown(struct snd_pcm_substream *substream) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct seeed_card_data *priv = snd_soc_card_get_drvdata(rtd->card); - struct seeed_dai_props *dai_props = - seeed_priv_to_props(priv, rtd->num); - - rtd->cpu_dai->driver->playback.channels_min = priv->channels_playback_default; - rtd->cpu_dai->driver->playback.channels_max = priv->channels_playback_default; - rtd->cpu_dai->driver->capture.channels_min = priv->channels_capture_default; - rtd->cpu_dai->driver->capture.channels_max = priv->channels_capture_default; - - clk_disable_unprepare(dai_props->cpu_dai.clk); - - clk_disable_unprepare(dai_props->codec_dai.clk); -} - -static int seeed_voice_card_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->codec_dai; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - struct seeed_card_data *priv = snd_soc_card_get_drvdata(rtd->card); - struct seeed_dai_props *dai_props = - seeed_priv_to_props(priv, rtd->num); - unsigned int mclk, mclk_fs = 0; - int ret = 0; - - if (priv->mclk_fs) - mclk_fs = priv->mclk_fs; - else if (dai_props->mclk_fs) - mclk_fs = dai_props->mclk_fs; - - if (mclk_fs) { - mclk = params_rate(params) * mclk_fs; - ret = snd_soc_dai_set_sysclk(codec_dai, 0, mclk, - SND_SOC_CLOCK_IN); - if (ret && ret != -ENOTSUPP) - goto err; - - ret = snd_soc_dai_set_sysclk(cpu_dai, 0, mclk, - SND_SOC_CLOCK_OUT); - if (ret && ret != -ENOTSUPP) - goto err; - } - return 0; -err: - return ret; -} - -#define _SET_CLOCK_CNT 2 -static int (* _set_clock[_SET_CLOCK_CNT])(int y_start_n_stop); - -int seeed_voice_card_register_set_clock(int stream, int (*set_clock)(int)) { - if (! _set_clock[stream]) { - _set_clock[stream] = set_clock; - } - return 0; -} -EXPORT_SYMBOL(seeed_voice_card_register_set_clock); - -/* - * work_cb_codec_clk: clear audio codec inner clock. - */ -static void work_cb_codec_clk(struct work_struct *work) -{ - struct seeed_card_data *priv = container_of(work, struct seeed_card_data, work_codec_clk); - int r = 0; - - if (_set_clock[SNDRV_PCM_STREAM_CAPTURE]) { - r = r || _set_clock[SNDRV_PCM_STREAM_CAPTURE](0); - } - if (_set_clock[SNDRV_PCM_STREAM_PLAYBACK]) { - r = r || _set_clock[SNDRV_PCM_STREAM_PLAYBACK](0); - } - - if (r && priv->try_stop++ < TRY_STOP_MAX) { - if (0 != schedule_work(&priv->work_codec_clk)) {} - } - return; -} - -static int seeed_voice_card_trigger(struct snd_pcm_substream *substream, int cmd) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *dai = rtd->codec_dai; - struct seeed_card_data *priv = snd_soc_card_get_drvdata(rtd->card); - #if CONFIG_AC10X_TRIG_LOCK - unsigned long flags; - #endif - int ret = 0; - - dev_dbg(rtd->card->dev, "%s() stream=%s cmd=%d play:%d, capt:%d\n", - __FUNCTION__, snd_pcm_stream_str(substream), cmd, - dai->playback_active, dai->capture_active); - - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: - case SNDRV_PCM_TRIGGER_RESUME: - case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - if (cancel_work_sync(&priv->work_codec_clk) != 0) {} - #if CONFIG_AC10X_TRIG_LOCK - /* I know it will degrades performance, but I have no choice */ - spin_lock_irqsave(&priv->lock, flags); - #endif - if (_set_clock[SNDRV_PCM_STREAM_CAPTURE]) _set_clock[SNDRV_PCM_STREAM_CAPTURE](1); - if (_set_clock[SNDRV_PCM_STREAM_PLAYBACK]) _set_clock[SNDRV_PCM_STREAM_PLAYBACK](1); - #if CONFIG_AC10X_TRIG_LOCK - spin_unlock_irqrestore(&priv->lock, flags); - #endif - break; - - case SNDRV_PCM_TRIGGER_STOP: - case SNDRV_PCM_TRIGGER_SUSPEND: - case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - /* capture channel resync, if overrun */ - if (dai->capture_active && substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - break; - } - - /* interrupt environment */ - if (in_irq() || in_nmi() || in_serving_softirq()) { - priv->try_stop = 0; - if (0 != schedule_work(&priv->work_codec_clk)) { - } - } else { - if (_set_clock[SNDRV_PCM_STREAM_CAPTURE]) _set_clock[SNDRV_PCM_STREAM_CAPTURE](0); - if (_set_clock[SNDRV_PCM_STREAM_PLAYBACK]) _set_clock[SNDRV_PCM_STREAM_PLAYBACK](0); - } - break; - default: - ret = -EINVAL; - } - - return ret; -} - -static struct snd_soc_ops seeed_voice_card_ops = { - .startup = seeed_voice_card_startup, - .shutdown = seeed_voice_card_shutdown, - .hw_params = seeed_voice_card_hw_params, - .trigger = seeed_voice_card_trigger, -}; - -/** - * add support for kernel >= 5.3.0 - * see patch: - * https://patchwork.kernel.org/patch/10860821/ - * https://patchwork.kernel.org/patch/10860823/ - */ -static int asoc_simple_parse_dai(struct device_node *node, - struct snd_soc_dai_link_component *dlc, - int *is_single_link) -{ - struct of_phandle_args args; - int ret; - - if (!node) - return 0; - - /* - * Get node via "sound-dai = <&phandle port>" - * it will be used as xxx_of_node on soc_bind_dai_link() - */ - ret = of_parse_phandle_with_args(node, DAI, CELL, 0, &args); - if (ret) - return ret; - - ret = snd_soc_of_get_dai_name(node, &dlc->dai_name); - if (ret < 0) - return ret; - - dlc->of_node = args.np; - - if (is_single_link) - *is_single_link = !args.args_count; - - return 0; -} - -static int asoc_simple_init_dai(struct snd_soc_dai *dai, - struct asoc_simple_dai *simple_dai) -{ - int ret; - - if (!simple_dai) - return 0; - - if (simple_dai->sysclk) { - ret = snd_soc_dai_set_sysclk(dai, 0, simple_dai->sysclk, - simple_dai->clk_direction); - if (ret && ret != -ENOTSUPP) { - dev_err(dai->dev, "simple-card: set_sysclk error\n"); - return ret; - } - } - - if (simple_dai->slots) { - ret = snd_soc_dai_set_tdm_slot(dai, - simple_dai->tx_slot_mask, - simple_dai->rx_slot_mask, - simple_dai->slots, - simple_dai->slot_width); - if (ret && ret != -ENOTSUPP) { - dev_err(dai->dev, "simple-card: set_tdm_slot error\n"); - return ret; - } - } - - return 0; -} - -static int seeed_voice_card_dai_init(struct snd_soc_pcm_runtime *rtd) -{ - struct seeed_card_data *priv = snd_soc_card_get_drvdata(rtd->card); - struct snd_soc_dai *codec = rtd->codec_dai; - struct snd_soc_dai *cpu = rtd->cpu_dai; - struct seeed_dai_props *dai_props = - seeed_priv_to_props(priv, rtd->num); - int ret; - - ret = asoc_simple_init_dai(codec, &dai_props->codec_dai); - if (ret < 0) - return ret; - - ret = asoc_simple_init_dai(cpu, &dai_props->cpu_dai); - if (ret < 0) - return ret; - - return 0; -} - -static int seeed_voice_card_dai_link_of(struct device_node *node, - struct seeed_card_data *priv, - int idx, - bool is_top_level_node) -{ - struct device *dev = seeed_priv_to_dev(priv); - struct snd_soc_dai_link *dai_link = seeed_priv_to_link(priv, idx); - struct seeed_dai_props *dai_props = seeed_priv_to_props(priv, idx); - struct asoc_simple_dai *cpu_dai = &dai_props->cpu_dai; - struct asoc_simple_dai *codec_dai = &dai_props->codec_dai; - struct device_node *cpu = NULL; - struct device_node *plat = NULL; - struct device_node *codec = NULL; - char prop[128]; - char *prefix = ""; - int ret, single_cpu; - - /* For single DAI link & old style of DT node */ - if (is_top_level_node) - prefix = PREFIX; - - snprintf(prop, sizeof(prop), "%scpu", prefix); - cpu = of_get_child_by_name(node, prop); - - if (!cpu) { - ret = -EINVAL; - dev_err(dev, "%s: Can't find %s DT node\n", __func__, prop); - goto dai_link_of_err; - } - - snprintf(prop, sizeof(prop), "%splat", prefix); - plat = of_get_child_by_name(node, prop); - - snprintf(prop, sizeof(prop), "%scodec", prefix); - codec = of_get_child_by_name(node, prop); - - if (!codec) { - ret = -EINVAL; - dev_err(dev, "%s: Can't find %s DT node\n", __func__, prop); - goto dai_link_of_err; - } - - ret = asoc_simple_parse_daifmt(dev, node, codec, - prefix, &dai_link->dai_fmt); - if (ret < 0) - goto dai_link_of_err; - - of_property_read_u32(node, "mclk-fs", &dai_props->mclk_fs); - - ret = asoc_simple_parse_cpu(cpu, dai_link, - &single_cpu); - if (ret < 0) - goto dai_link_of_err; - - #if _SINGLE_CODEC - ret = asoc_simple_parse_codec(codec, dai_link); - if (ret < 0) - goto dai_link_of_err; - #else - ret = snd_soc_of_get_dai_link_codecs(dev, codec, dai_link); - if (ret < 0) { - dev_err(dev, "parse codec info error %d\n", ret); - goto dai_link_of_err; - } - dev_dbg(dev, "dai_link num_codecs = %d\n", dai_link->num_codecs); - #endif - - ret = asoc_simple_parse_platform(plat, dai_link); - if (ret < 0) - goto dai_link_of_err; - - ret = snd_soc_of_parse_tdm_slot(cpu, &cpu_dai->tx_slot_mask, - &cpu_dai->rx_slot_mask, - &cpu_dai->slots, - &cpu_dai->slot_width); - dev_dbg(dev, "cpu_dai : slot,width,tx,rx = %d,%d,%d,%d\n", - cpu_dai->slots, cpu_dai->slot_width, - cpu_dai->tx_slot_mask, cpu_dai->rx_slot_mask - ); - if (ret < 0) - goto dai_link_of_err; - - ret = snd_soc_of_parse_tdm_slot(codec, &codec_dai->tx_slot_mask, - &codec_dai->rx_slot_mask, - &codec_dai->slots, - &codec_dai->slot_width); - if (ret < 0) - goto dai_link_of_err; - - #if LINUX_VERSION_CODE <= KERNEL_VERSION(4,10,0) - ret = asoc_simple_card_parse_clk_cpu(cpu, dai_link, cpu_dai); - #else - ret = asoc_simple_parse_clk_cpu(dev, cpu, dai_link, cpu_dai); - #endif - if (ret < 0) - goto dai_link_of_err; - - #if LINUX_VERSION_CODE <= KERNEL_VERSION(4,10,0) - ret = asoc_simple_card_parse_clk_codec(codec, dai_link, codec_dai); - #else - ret = asoc_simple_parse_clk_codec(dev, codec, dai_link, codec_dai); - #endif - if (ret < 0) - goto dai_link_of_err; - - ret = asoc_simple_set_dailink_name(dev, dai_link, - "%s-%s", - dai_link->cpus->dai_name, - #if _SINGLE_CODEC - dai_link->codecs->dai_name - #else - dai_link->codecs[0].dai_name - #endif - ); - if (ret < 0) - goto dai_link_of_err; - - dai_link->ops = &seeed_voice_card_ops; - dai_link->init = seeed_voice_card_dai_init; - - dev_dbg(dev, "\tname : %s\n", dai_link->stream_name); - dev_dbg(dev, "\tformat : %04x\n", dai_link->dai_fmt); - dev_dbg(dev, "\tcpu : %s / %d\n", - dai_link->cpus->dai_name, - dai_props->cpu_dai.sysclk); - dev_dbg(dev, "\tcodec : %s / %d\n", - #if _SINGLE_CODEC - dai_link->codecs->dai_name, - #else - dai_link->codecs[0].dai_name, - #endif - dai_props->codec_dai.sysclk); - - #if _SINGLE_CODEC - asoc_simple_canonicalize_platform(dai_link); - #endif - - asoc_simple_canonicalize_cpu(dai_link, single_cpu); - -dai_link_of_err: - of_node_put(cpu); - of_node_put(codec); - - return ret; -} - -static int seeed_voice_card_parse_aux_devs(struct device_node *node, - struct seeed_card_data *priv) -{ - struct device *dev = seeed_priv_to_dev(priv); - struct device_node *aux_node; - int i, n, len; - - if (!of_find_property(node, PREFIX "aux-devs", &len)) - return 0; /* Ok to have no aux-devs */ - - n = len / sizeof(__be32); - if (n <= 0) - return -EINVAL; - - priv->snd_card.aux_dev = devm_kzalloc(dev, - n * sizeof(*priv->snd_card.aux_dev), GFP_KERNEL); - if (!priv->snd_card.aux_dev) - return -ENOMEM; - - for (i = 0; i < n; i++) { - aux_node = of_parse_phandle(node, PREFIX "aux-devs", i); - if (!aux_node) - return -EINVAL; - priv->snd_card.aux_dev[i].codec_of_node = aux_node; - } - - priv->snd_card.num_aux_devs = n; - return 0; -} - -static int seeed_voice_card_parse_of(struct device_node *node, - struct seeed_card_data *priv) -{ - struct device *dev = seeed_priv_to_dev(priv); - struct device_node *dai_link; - int ret; - - if (!node) - return -EINVAL; - - dai_link = of_get_child_by_name(node, PREFIX "dai-link"); - - /* The off-codec widgets */ - if (of_property_read_bool(node, PREFIX "widgets")) { - ret = snd_soc_of_parse_audio_simple_widgets(&priv->snd_card, - PREFIX "widgets"); - if (ret) - goto card_parse_end; - } - - /* DAPM routes */ - if (of_property_read_bool(node, PREFIX "routing")) { - ret = snd_soc_of_parse_audio_routing(&priv->snd_card, - PREFIX "routing"); - if (ret) - goto card_parse_end; - } - - /* Factor to mclk, used in hw_params() */ - of_property_read_u32(node, PREFIX "mclk-fs", &priv->mclk_fs); - - /* Single/Muti DAI link(s) & New style of DT node */ - if (dai_link) { - struct device_node *np = NULL; - int i = 0; - - for_each_child_of_node(node, np) { - dev_dbg(dev, "\tlink %d:\n", i); - ret = seeed_voice_card_dai_link_of(np, priv, - i, false); - if (ret < 0) { - of_node_put(np); - goto card_parse_end; - } - i++; - } - } else { - /* For single DAI link & old style of DT node */ - ret = seeed_voice_card_dai_link_of(node, priv, 0, true); - if (ret < 0) - goto card_parse_end; - } - - ret = asoc_simple_parse_card_name(&priv->snd_card, PREFIX); - if (ret < 0) - goto card_parse_end; - - ret = seeed_voice_card_parse_aux_devs(node, priv); - - priv->channels_playback_default = 0; - priv->channels_playback_override = 2; - priv->channels_capture_default = 0; - priv->channels_capture_override = 2; - of_property_read_u32(node, PREFIX "channels-playback-default", - &priv->channels_playback_default); - of_property_read_u32(node, PREFIX "channels-playback-override", - &priv->channels_playback_override); - of_property_read_u32(node, PREFIX "channels-capture-default", - &priv->channels_capture_default); - of_property_read_u32(node, PREFIX "channels-capture-override", - &priv->channels_capture_override); - -card_parse_end: - of_node_put(dai_link); - - return ret; -} - -static int seeed_voice_card_probe(struct platform_device *pdev) -{ - struct seeed_card_data *priv; - struct snd_soc_dai_link *dai_link; - struct seeed_dai_props *dai_props; - struct device_node *np = pdev->dev.of_node; - struct device *dev = &pdev->dev; - int num, ret; - - /* Get the number of DAI links */ - if (np && of_get_child_by_name(np, PREFIX "dai-link")) - num = of_get_child_count(np); - else - num = 1; - - /* Allocate the private data and the DAI link array */ - priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; - - dai_props = devm_kzalloc(dev, sizeof(*dai_props) * num, GFP_KERNEL); - dai_link = devm_kzalloc(dev, sizeof(*dai_link) * num, GFP_KERNEL); - if (!dai_props || !dai_link) - return -ENOMEM; - - priv->dai_props = dai_props; - priv->dai_link = dai_link; - - /** - * Only support single template for now - */ - dai_link->cpus = &dai_props->cpus; - dai_link->codecs = &dai_props->codecs; - dai_link->platforms = &dai_props->platforms; - dai_link->num_cpus = - dai_link->num_codecs = - dai_link->num_platforms = 1; - - /* Init snd_soc_card */ - priv->snd_card.owner = THIS_MODULE; - priv->snd_card.dev = dev; - priv->snd_card.dai_link = priv->dai_link; - priv->snd_card.num_links = num; - - if (np && of_device_is_available(np)) { - ret = seeed_voice_card_parse_of(np, priv); - if (ret < 0) { - if (ret != -EPROBE_DEFER) - dev_err(dev, "parse error %d\n", ret); - goto err; - } - } else { - struct seeed_card_info *cinfo; - - cinfo = dev->platform_data; - if (!cinfo) { - dev_err(dev, "no info for seeed-voice-card\n"); - return -EINVAL; - } - - if (!cinfo->name || - !cinfo->codec_dai.name || - !cinfo->codec || - !cinfo->platform || - !cinfo->cpu_dai.name) { - dev_err(dev, "insufficient seeed_voice_card_info settings\n"); - return -EINVAL; - } - - priv->snd_card.name = (cinfo->card) ? cinfo->card : cinfo->name; - dai_link->name = cinfo->name; - dai_link->stream_name = cinfo->name; - dai_link->platforms->name = cinfo->platform; - dai_link->codecs->name = cinfo->codec; - dai_link->cpus->dai_name = cinfo->cpu_dai.name; - dai_link->codecs->dai_name = cinfo->codec_dai.name; - dai_link->dai_fmt = cinfo->daifmt; - dai_link->init = seeed_voice_card_dai_init; - memcpy(&priv->dai_props->cpu_dai, &cinfo->cpu_dai, - sizeof(priv->dai_props->cpu_dai)); - memcpy(&priv->dai_props->codec_dai, &cinfo->codec_dai, - sizeof(priv->dai_props->codec_dai)); - } - - snd_soc_card_set_drvdata(&priv->snd_card, priv); - - #if CONFIG_AC10X_TRIG_LOCK - spin_lock_init(&priv->lock); - #endif - - INIT_WORK(&priv->work_codec_clk, work_cb_codec_clk); - - ret = devm_snd_soc_register_card(&pdev->dev, &priv->snd_card); - if (ret >= 0) - return ret; - -err: - asoc_simple_clean_reference(&priv->snd_card); - - return ret; -} - -static int seeed_voice_card_remove(struct platform_device *pdev) -{ - struct snd_soc_card *card = platform_get_drvdata(pdev); - struct seeed_card_data *priv = snd_soc_card_get_drvdata(card); - - if (cancel_work_sync(&priv->work_codec_clk) != 0) { - } - return asoc_simple_clean_reference(card); -} - -static const struct of_device_id seeed_voice_of_match[] = { - { .compatible = "seeed-voicecard", }, - {}, -}; -MODULE_DEVICE_TABLE(of, seeed_voice_of_match); - -static struct platform_driver seeed_voice_card = { - .driver = { - .name = "seeed-voicecard", - .pm = &snd_soc_pm_ops, - .of_match_table = seeed_voice_of_match, - }, - .probe = seeed_voice_card_probe, - .remove = seeed_voice_card_remove, -}; - -module_platform_driver(seeed_voice_card); - -MODULE_ALIAS("platform:seeed-voice-card"); -MODULE_LICENSE("GPL v2"); -MODULE_DESCRIPTION("ASoC SEEED Voice Card"); -MODULE_AUTHOR("PeterYang"); diff --git a/seeed-voicecard.c b/seeed-voicecard.c index f08fbd8..82c9f19 100644 --- a/seeed-voicecard.c +++ b/seeed-voicecard.c @@ -40,6 +40,9 @@ struct seeed_card_data { struct seeed_dai_props { struct asoc_simple_dai cpu_dai; struct asoc_simple_dai codec_dai; + struct snd_soc_dai_link_component cpus; /* single cpu */ + struct snd_soc_dai_link_component codecs; /* single codec */ + struct snd_soc_dai_link_component platforms; unsigned int mclk_fs; } *dai_props; unsigned int mclk_fs; @@ -250,6 +253,74 @@ static struct snd_soc_ops seeed_voice_card_ops = { .trigger = seeed_voice_card_trigger, }; +/** + * add support for kernel >= 5.3.0 + * see patch: + * https://patchwork.kernel.org/patch/10860821/ + * https://patchwork.kernel.org/patch/10860823/ + */ +static int asoc_simple_parse_dai(struct device_node *node, + struct snd_soc_dai_link_component *dlc, + int *is_single_link) +{ + struct of_phandle_args args; + int ret; + + if (!node) + return 0; + + /* + * Get node via "sound-dai = <&phandle port>" + * it will be used as xxx_of_node on soc_bind_dai_link() + */ + ret = of_parse_phandle_with_args(node, DAI, CELL, 0, &args); + if (ret) + return ret; + + ret = snd_soc_of_get_dai_name(node, &dlc->dai_name); + if (ret < 0) + return ret; + + dlc->of_node = args.np; + + if (is_single_link) + *is_single_link = !args.args_count; + + return 0; +} + +static int asoc_simple_init_dai(struct snd_soc_dai *dai, + struct asoc_simple_dai *simple_dai) +{ + int ret; + + if (!simple_dai) + return 0; + + if (simple_dai->sysclk) { + ret = snd_soc_dai_set_sysclk(dai, 0, simple_dai->sysclk, + simple_dai->clk_direction); + if (ret && ret != -ENOTSUPP) { + dev_err(dai->dev, "simple-card: set_sysclk error\n"); + return ret; + } + } + + if (simple_dai->slots) { + ret = snd_soc_dai_set_tdm_slot(dai, + simple_dai->tx_slot_mask, + simple_dai->rx_slot_mask, + simple_dai->slots, + simple_dai->slot_width); + if (ret && ret != -ENOTSUPP) { + dev_err(dai->dev, "simple-card: set_tdm_slot error\n"); + return ret; + } + } + + return 0; +} + static int seeed_voice_card_dai_init(struct snd_soc_pcm_runtime *rtd) { struct seeed_card_data *priv = snd_soc_card_get_drvdata(rtd->card); @@ -259,11 +330,11 @@ static int seeed_voice_card_dai_init(struct snd_soc_pcm_runtime *rtd) seeed_priv_to_props(priv, rtd->num); int ret; - ret = asoc_simple_card_init_dai(codec, &dai_props->codec_dai); + ret = asoc_simple_init_dai(codec, &dai_props->codec_dai); if (ret < 0) return ret; - ret = asoc_simple_card_init_dai(cpu, &dai_props->cpu_dai); + ret = asoc_simple_init_dai(cpu, &dai_props->cpu_dai); if (ret < 0) return ret; @@ -312,20 +383,20 @@ static int seeed_voice_card_dai_link_of(struct device_node *node, goto dai_link_of_err; } - ret = asoc_simple_card_parse_daifmt(dev, node, codec, + ret = asoc_simple_parse_daifmt(dev, node, codec, prefix, &dai_link->dai_fmt); if (ret < 0) goto dai_link_of_err; of_property_read_u32(node, "mclk-fs", &dai_props->mclk_fs); - ret = asoc_simple_card_parse_cpu(cpu, dai_link, - DAI, CELL, &single_cpu); + ret = asoc_simple_parse_cpu(cpu, dai_link, + &single_cpu); if (ret < 0) goto dai_link_of_err; #if _SINGLE_CODEC - ret = asoc_simple_card_parse_codec(codec, dai_link, DAI, CELL); + ret = asoc_simple_parse_codec(codec, dai_link); if (ret < 0) goto dai_link_of_err; #else @@ -337,7 +408,7 @@ static int seeed_voice_card_dai_link_of(struct device_node *node, dev_dbg(dev, "dai_link num_codecs = %d\n", dai_link->num_codecs); #endif - ret = asoc_simple_card_parse_platform(plat, dai_link, DAI, CELL); + ret = asoc_simple_parse_platform(plat, dai_link); if (ret < 0) goto dai_link_of_err; @@ -362,7 +433,7 @@ static int seeed_voice_card_dai_link_of(struct device_node *node, #if LINUX_VERSION_CODE <= KERNEL_VERSION(4,10,0) ret = asoc_simple_card_parse_clk_cpu(cpu, dai_link, cpu_dai); #else - ret = asoc_simple_card_parse_clk_cpu(dev, cpu, dai_link, cpu_dai); + ret = asoc_simple_parse_clk_cpu(dev, cpu, dai_link, cpu_dai); #endif if (ret < 0) goto dai_link_of_err; @@ -370,22 +441,16 @@ static int seeed_voice_card_dai_link_of(struct device_node *node, #if LINUX_VERSION_CODE <= KERNEL_VERSION(4,10,0) ret = asoc_simple_card_parse_clk_codec(codec, dai_link, codec_dai); #else - ret = asoc_simple_card_parse_clk_codec(dev, codec, dai_link, codec_dai); + ret = asoc_simple_parse_clk_codec(dev, codec, dai_link, codec_dai); #endif if (ret < 0) goto dai_link_of_err; - #if _SINGLE_CODEC - ret = asoc_simple_card_canonicalize_dailink(dai_link); - if (ret < 0) - goto dai_link_of_err; - #endif - - ret = asoc_simple_card_set_dailink_name(dev, dai_link, + ret = asoc_simple_set_dailink_name(dev, dai_link, "%s-%s", - dai_link->cpu_dai_name, + dai_link->cpus->dai_name, #if _SINGLE_CODEC - dai_link->codec_dai_name + dai_link->codecs->dai_name #else dai_link->codecs[0].dai_name #endif @@ -399,17 +464,21 @@ static int seeed_voice_card_dai_link_of(struct device_node *node, dev_dbg(dev, "\tname : %s\n", dai_link->stream_name); dev_dbg(dev, "\tformat : %04x\n", dai_link->dai_fmt); dev_dbg(dev, "\tcpu : %s / %d\n", - dai_link->cpu_dai_name, + dai_link->cpus->dai_name, dai_props->cpu_dai.sysclk); dev_dbg(dev, "\tcodec : %s / %d\n", #if _SINGLE_CODEC - dai_link->codec_dai_name, + dai_link->codecs->dai_name, #else dai_link->codecs[0].dai_name, #endif dai_props->codec_dai.sysclk); - asoc_simple_card_canonicalize_cpu(dai_link, single_cpu); + #if _SINGLE_CODEC + asoc_simple_canonicalize_platform(dai_link); + #endif + + asoc_simple_canonicalize_cpu(dai_link, single_cpu); dai_link_of_err: of_node_put(cpu); @@ -501,7 +570,7 @@ static int seeed_voice_card_parse_of(struct device_node *node, goto card_parse_end; } - ret = asoc_simple_card_parse_card_name(&priv->snd_card, PREFIX); + ret = asoc_simple_parse_card_name(&priv->snd_card, PREFIX); if (ret < 0) goto card_parse_end; @@ -554,6 +623,16 @@ static int seeed_voice_card_probe(struct platform_device *pdev) priv->dai_props = dai_props; priv->dai_link = dai_link; + /** + * Only support single template for now + */ + dai_link->cpus = &dai_props->cpus; + dai_link->codecs = &dai_props->codecs; + dai_link->platforms = &dai_props->platforms; + dai_link->num_cpus = + dai_link->num_codecs = + dai_link->num_platforms = 1; + /* Init snd_soc_card */ priv->snd_card.owner = THIS_MODULE; priv->snd_card.dev = dev; @@ -588,10 +667,10 @@ static int seeed_voice_card_probe(struct platform_device *pdev) priv->snd_card.name = (cinfo->card) ? cinfo->card : cinfo->name; dai_link->name = cinfo->name; dai_link->stream_name = cinfo->name; - dai_link->platform_name = cinfo->platform; - dai_link->codec_name = cinfo->codec; - dai_link->cpu_dai_name = cinfo->cpu_dai.name; - dai_link->codec_dai_name = cinfo->codec_dai.name; + dai_link->platforms->name = cinfo->platform; + dai_link->codecs->name = cinfo->codec; + dai_link->cpus->dai_name = cinfo->cpu_dai.name; + dai_link->codecs->dai_name = cinfo->codec_dai.name; dai_link->dai_fmt = cinfo->daifmt; dai_link->init = seeed_voice_card_dai_init; memcpy(&priv->dai_props->cpu_dai, &cinfo->cpu_dai, @@ -613,7 +692,7 @@ static int seeed_voice_card_probe(struct platform_device *pdev) return ret; err: - asoc_simple_card_clean_reference(&priv->snd_card); + asoc_simple_clean_reference(&priv->snd_card); return ret; } @@ -625,7 +704,7 @@ static int seeed_voice_card_remove(struct platform_device *pdev) if (cancel_work_sync(&priv->work_codec_clk) != 0) { } - return asoc_simple_card_clean_reference(card); + return asoc_simple_clean_reference(card); } static const struct of_device_id seeed_voice_of_match[] = {