From: Kuninori Morimoto Date: Tue, 23 Aug 2016 01:35:02 +0000 (+0000) Subject: ASoC: rsrc-card: rename rsrc-card to simple-scu-card phase3 X-Git-Url: http://drtracing.org/?a=commitdiff_plain;h=d12c6216c4a58f9fd2a58bc489783a095d84b2fc;p=deliverable%2Flinux.git ASoC: rsrc-card: rename rsrc-card to simple-scu-card phase3 rsrc-card which is using DPCM feature was created for Renesas sound. But not only Renesas, but many SoC can use this driver, because it is based on simple-card driver. To use it as more open driver, rsrc-card will be renamed to simple-scu-card. In order to easy patch review, as 3rd step, this patch moves rsrc-card driver to generic folder. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- diff --git a/Documentation/devicetree/bindings/sound/renesas,rsrc-card.txt b/Documentation/devicetree/bindings/sound/renesas,rsrc-card.txt deleted file mode 100644 index ecb4a249fe6e..000000000000 --- a/Documentation/devicetree/bindings/sound/renesas,rsrc-card.txt +++ /dev/null @@ -1,80 +0,0 @@ -ASoC simple SCU Sound Card - -Simple-Card specifies audio DAI connections of SoC <-> codec. - -Required properties: - -- compatible : "simple-scu-audio-card" - "renesas,rsrc-card" - -Optional properties: - -- simple-audio-card,name : User specified audio sound card name, one string - property. -- simple-audio-card,cpu : CPU sub-node -- simple-audio-card,codec : CODEC sub-node - -Optional subnode properties: - -- simple-audio-card,format : CPU/CODEC common audio format. - "i2s", "right_j", "left_j" , "dsp_a" - "dsp_b", "ac97", "pdm", "msb", "lsb" -- simple-audio-card,frame-master : Indicates dai-link frame master. - phandle to a cpu or codec subnode. -- simple-audio-card,bitclock-master : Indicates dai-link bit clock master. - phandle to a cpu or codec subnode. -- simple-audio-card,bitclock-inversion : bool property. Add this if the - dai-link uses bit clock inversion. -- simple-audio-card,frame-inversion : bool property. Add this if the - dai-link uses frame clock inversion. -- simple-audio-card,convert-rate : platform specified sampling rate convert -- simple-audio-card,convert-channels : platform specified converted channel size (2 - 8 ch) -- simple-audio-card,prefix : see audio-routing -- simple-audio-card,routing : A list of the connections between audio components. - Each entry is a pair of strings, the first being the connection's sink, - the second being the connection's source. Valid names for sources. - use audio-prefix if some components is using same sink/sources naming. - it can be used if compatible was "renesas,rsrc-card"; - -Required CPU/CODEC subnodes properties: - -- sound-dai : phandle and port of CPU/CODEC - -Optional CPU/CODEC subnodes properties: - -- clocks / system-clock-frequency : specify subnode's clock if needed. - it can be specified via "clocks" if system has - clock node (= common clock), or "system-clock-frequency" - (if system doens't support common clock) - If a clock is specified, it is - enabled with clk_prepare_enable() - in dai startup() and disabled with - clk_disable_unprepare() in dai - shutdown(). - -Example - -sound { - compatible = "simple-scu-audio-card"; - - simple-audio-card,name = "rsnd-ak4643"; - simple-audio-card,format = "left_j"; - simple-audio-card,format = "left_j"; - simple-audio-card,bitclock-master = <&sndcodec>; - simple-audio-card,frame-master = <&sndcodec>; - - simple-audio-card,convert-rate = <48000>; /* see audio_clk_a */ - - simple-audio-card,prefix = "ak4642"; - simple-audio-card,routing = "ak4642 Playback", "DAI0 Playback", - "DAI0 Capture", "ak4642 Capture"; - - sndcpu: simple-audio-card,cpu { - sound-dai = <&rcar_sound>; - }; - - sndcodec: simple-audio-card,codec { - sound-dai = <&ak4643>; - system-clock-frequency = <11289600>; - }; -}; diff --git a/Documentation/devicetree/bindings/sound/simple-scu-card.txt b/Documentation/devicetree/bindings/sound/simple-scu-card.txt new file mode 100644 index 000000000000..ecb4a249fe6e --- /dev/null +++ b/Documentation/devicetree/bindings/sound/simple-scu-card.txt @@ -0,0 +1,80 @@ +ASoC simple SCU Sound Card + +Simple-Card specifies audio DAI connections of SoC <-> codec. + +Required properties: + +- compatible : "simple-scu-audio-card" + "renesas,rsrc-card" + +Optional properties: + +- simple-audio-card,name : User specified audio sound card name, one string + property. +- simple-audio-card,cpu : CPU sub-node +- simple-audio-card,codec : CODEC sub-node + +Optional subnode properties: + +- simple-audio-card,format : CPU/CODEC common audio format. + "i2s", "right_j", "left_j" , "dsp_a" + "dsp_b", "ac97", "pdm", "msb", "lsb" +- simple-audio-card,frame-master : Indicates dai-link frame master. + phandle to a cpu or codec subnode. +- simple-audio-card,bitclock-master : Indicates dai-link bit clock master. + phandle to a cpu or codec subnode. +- simple-audio-card,bitclock-inversion : bool property. Add this if the + dai-link uses bit clock inversion. +- simple-audio-card,frame-inversion : bool property. Add this if the + dai-link uses frame clock inversion. +- simple-audio-card,convert-rate : platform specified sampling rate convert +- simple-audio-card,convert-channels : platform specified converted channel size (2 - 8 ch) +- simple-audio-card,prefix : see audio-routing +- simple-audio-card,routing : A list of the connections between audio components. + Each entry is a pair of strings, the first being the connection's sink, + the second being the connection's source. Valid names for sources. + use audio-prefix if some components is using same sink/sources naming. + it can be used if compatible was "renesas,rsrc-card"; + +Required CPU/CODEC subnodes properties: + +- sound-dai : phandle and port of CPU/CODEC + +Optional CPU/CODEC subnodes properties: + +- clocks / system-clock-frequency : specify subnode's clock if needed. + it can be specified via "clocks" if system has + clock node (= common clock), or "system-clock-frequency" + (if system doens't support common clock) + If a clock is specified, it is + enabled with clk_prepare_enable() + in dai startup() and disabled with + clk_disable_unprepare() in dai + shutdown(). + +Example + +sound { + compatible = "simple-scu-audio-card"; + + simple-audio-card,name = "rsnd-ak4643"; + simple-audio-card,format = "left_j"; + simple-audio-card,format = "left_j"; + simple-audio-card,bitclock-master = <&sndcodec>; + simple-audio-card,frame-master = <&sndcodec>; + + simple-audio-card,convert-rate = <48000>; /* see audio_clk_a */ + + simple-audio-card,prefix = "ak4642"; + simple-audio-card,routing = "ak4642 Playback", "DAI0 Playback", + "DAI0 Capture", "ak4642 Capture"; + + sndcpu: simple-audio-card,cpu { + sound-dai = <&rcar_sound>; + }; + + sndcodec: simple-audio-card,codec { + sound-dai = <&ak4643>; + system-clock-frequency = <11289600>; + }; +}; diff --git a/sound/soc/generic/Kconfig b/sound/soc/generic/Kconfig index c01c5dd68601..2d0ff037c5a1 100644 --- a/sound/soc/generic/Kconfig +++ b/sound/soc/generic/Kconfig @@ -6,3 +6,10 @@ config SND_SIMPLE_CARD select SND_SIMPLE_CARD_UTILS help This option enables generic simple sound card support + +config SND_SIMPLE_SCU_CARD + tristate "ASoC Simple SCU sound card support" + select SND_SIMPLE_CARD_UTILS + help + This option enables generic simple SCU sound card support. + It supports DPCM of multi CPU single Codec ststem. diff --git a/sound/soc/generic/Makefile b/sound/soc/generic/Makefile index 2d53c8d70705..ee750f3023ba 100644 --- a/sound/soc/generic/Makefile +++ b/sound/soc/generic/Makefile @@ -1,5 +1,7 @@ snd-soc-simple-card-utils-objs := simple-card-utils.o snd-soc-simple-card-objs := simple-card.o +snd-soc-simple-scu-card-objs := simple-scu-card.o obj-$(CONFIG_SND_SIMPLE_CARD_UTILS) += snd-soc-simple-card-utils.o obj-$(CONFIG_SND_SIMPLE_CARD) += snd-soc-simple-card.o +obj-$(CONFIG_SND_SIMPLE_SCU_CARD) += snd-soc-simple-scu-card.o diff --git a/sound/soc/generic/simple-scu-card.c b/sound/soc/generic/simple-scu-card.c new file mode 100644 index 000000000000..3fa19084e326 --- /dev/null +++ b/sound/soc/generic/simple-scu-card.c @@ -0,0 +1,361 @@ +/* + * ASoC simple SCU sound card support + * + * Copyright (C) 2015 Renesas Solutions Corp. + * Kuninori Morimoto + * + * based on ${LINUX}/sound/soc/generic/simple-card.c + * + * 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. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct asoc_simple_card_of_data { + const char *prefix; + const struct snd_soc_dapm_route *routes; + int num_routes; +}; + +static const struct of_device_id asoc_simple_card_of_match[] = { + { .compatible = "renesas,rsrc-card", }, + { .compatible = "simple-scu-audio-card", }, + {}, +}; +MODULE_DEVICE_TABLE(of, asoc_simple_card_of_match); + +#define IDX_CPU 0 +#define IDX_CODEC 1 +struct asoc_simple_card_priv { + struct snd_soc_card snd_card; + struct snd_soc_codec_conf codec_conf; + struct asoc_simple_dai *dai_props; + struct snd_soc_dai_link *dai_link; + u32 convert_rate; + u32 convert_channels; +}; + +#define simple_priv_to_dev(priv) ((priv)->snd_card.dev) +#define simple_priv_to_link(priv, i) ((priv)->snd_card.dai_link + (i)) +#define simple_priv_to_props(priv, i) ((priv)->dai_props + (i)) + +#define DAI "sound-dai" +#define CELL "#sound-dai-cells" +#define PREFIX "simple-audio-card," + +static int asoc_simple_card_startup(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct asoc_simple_card_priv *priv = snd_soc_card_get_drvdata(rtd->card); + struct asoc_simple_dai *dai_props = + simple_priv_to_props(priv, rtd->num); + + return clk_prepare_enable(dai_props->clk); +} + +static void asoc_simple_card_shutdown(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct asoc_simple_card_priv *priv = snd_soc_card_get_drvdata(rtd->card); + struct asoc_simple_dai *dai_props = + simple_priv_to_props(priv, rtd->num); + + clk_disable_unprepare(dai_props->clk); +} + +static struct snd_soc_ops asoc_simple_card_ops = { + .startup = asoc_simple_card_startup, + .shutdown = asoc_simple_card_shutdown, +}; + +static int asoc_simple_card_dai_init(struct snd_soc_pcm_runtime *rtd) +{ + struct asoc_simple_card_priv *priv = snd_soc_card_get_drvdata(rtd->card); + struct snd_soc_dai *dai; + struct snd_soc_dai_link *dai_link; + struct asoc_simple_dai *dai_props; + int num = rtd->num; + + dai_link = simple_priv_to_link(priv, num); + dai_props = simple_priv_to_props(priv, num); + dai = dai_link->dynamic ? + rtd->cpu_dai : + rtd->codec_dai; + + return asoc_simple_card_init_dai(dai, dai_props); +} + +static int asoc_simple_card_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct asoc_simple_card_priv *priv = snd_soc_card_get_drvdata(rtd->card); + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + + if (priv->convert_rate) + rate->min = + rate->max = priv->convert_rate; + + if (priv->convert_channels) + channels->min = + channels->max = priv->convert_channels; + + return 0; +} + +static int asoc_simple_card_parse_links(struct device_node *np, + struct asoc_simple_card_priv *priv, + int idx, bool is_fe) +{ + struct device *dev = simple_priv_to_dev(priv); + struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, idx); + struct asoc_simple_dai *dai_props = simple_priv_to_props(priv, idx); + int ret; + + /* Parse TDM slot */ + ret = snd_soc_of_parse_tdm_slot(np, + &dai_props->tx_slot_mask, + &dai_props->rx_slot_mask, + &dai_props->slots, + &dai_props->slot_width); + if (ret) + return ret; + + if (is_fe) { + int is_single_links = 0; + + /* BE is dummy */ + dai_link->codec_of_node = NULL; + dai_link->codec_dai_name = "snd-soc-dummy-dai"; + dai_link->codec_name = "snd-soc-dummy"; + + /* FE settings */ + dai_link->dynamic = 1; + dai_link->dpcm_merged_format = 1; + + ret = asoc_simple_card_parse_cpu(np, dai_link, DAI, CELL, + &is_single_links); + if (ret) + return ret; + + ret = asoc_simple_card_parse_clk_cpu(np, dai_link, dai_props); + if (ret < 0) + return ret; + + ret = asoc_simple_card_set_dailink_name(dev, dai_link, + "fe.%s", + dai_link->cpu_dai_name); + if (ret < 0) + return ret; + + asoc_simple_card_canonicalize_cpu(dai_link, is_single_links); + } else { + /* FE is dummy */ + dai_link->cpu_of_node = NULL; + dai_link->cpu_dai_name = "snd-soc-dummy-dai"; + dai_link->cpu_name = "snd-soc-dummy"; + + /* BE settings */ + dai_link->no_pcm = 1; + dai_link->be_hw_params_fixup = asoc_simple_card_be_hw_params_fixup; + + ret = asoc_simple_card_parse_codec(np, dai_link, DAI, CELL); + if (ret < 0) + return ret; + + ret = asoc_simple_card_parse_clk_codec(np, dai_link, dai_props); + if (ret < 0) + return ret; + + ret = asoc_simple_card_set_dailink_name(dev, dai_link, + "be.%s", + dai_link->codec_dai_name); + if (ret < 0) + return ret; + + snd_soc_of_parse_audio_prefix(&priv->snd_card, + &priv->codec_conf, + dai_link->codec_of_node, + PREFIX "prefix"); + } + + ret = asoc_simple_card_canonicalize_dailink(dai_link); + if (ret < 0) + return ret; + + dai_link->dpcm_playback = 1; + dai_link->dpcm_capture = 1; + dai_link->ops = &asoc_simple_card_ops; + dai_link->init = asoc_simple_card_dai_init; + + dev_dbg(dev, "\t%s / %04x / %d\n", + dai_link->name, + dai_link->dai_fmt, + dai_props->sysclk); + + return 0; +} + +static int asoc_simple_card_dai_link_of(struct device_node *node, + struct asoc_simple_card_priv *priv) +{ + struct device *dev = simple_priv_to_dev(priv); + struct snd_soc_dai_link *dai_link; + struct device_node *np; + unsigned int daifmt = 0; + int ret, i; + bool is_fe; + + /* find 1st codec */ + i = 0; + for_each_child_of_node(node, np) { + dai_link = simple_priv_to_link(priv, i); + + if (strcmp(np->name, PREFIX "codec") == 0) { + ret = asoc_simple_card_parse_daifmt(dev, node, np, + PREFIX, &daifmt); + if (ret < 0) + return ret; + break; + } + i++; + } + + i = 0; + for_each_child_of_node(node, np) { + dai_link = simple_priv_to_link(priv, i); + dai_link->dai_fmt = daifmt; + + is_fe = false; + if (strcmp(np->name, PREFIX "cpu") == 0) + is_fe = true; + + ret = asoc_simple_card_parse_links(np, priv, i, is_fe); + if (ret < 0) + return ret; + i++; + } + + return 0; +} + +static int asoc_simple_card_parse_of(struct device_node *node, + struct asoc_simple_card_priv *priv, + struct device *dev) +{ + struct asoc_simple_dai *props; + struct snd_soc_dai_link *links; + int ret; + int num; + + if (!node) + return -EINVAL; + + num = of_get_child_count(node); + props = devm_kzalloc(dev, sizeof(*props) * num, GFP_KERNEL); + links = devm_kzalloc(dev, sizeof(*links) * num, GFP_KERNEL); + if (!props || !links) + return -ENOMEM; + + priv->dai_props = props; + priv->dai_link = links; + + /* 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; + priv->snd_card.codec_conf = &priv->codec_conf; + priv->snd_card.num_configs = 1; + + ret = snd_soc_of_parse_audio_routing(&priv->snd_card, PREFIX "routing"); + if (ret < 0) + return ret; + + /* sampling rate convert */ + of_property_read_u32(node, PREFIX "convert-rate", &priv->convert_rate); + + /* channels transfer */ + of_property_read_u32(node, PREFIX "convert-channels", &priv->convert_channels); + + ret = asoc_simple_card_dai_link_of(node, priv); + if (ret < 0) + return ret; + + ret = asoc_simple_card_parse_card_name(&priv->snd_card, PREFIX); + if (ret < 0) + return ret; + + dev_dbg(dev, "New card: %s\n", + priv->snd_card.name ? priv->snd_card.name : ""); + dev_dbg(dev, "convert_rate %d\n", priv->convert_rate); + dev_dbg(dev, "convert_channels %d\n", priv->convert_channels); + + return 0; +} + +static int asoc_simple_card_probe(struct platform_device *pdev) +{ + struct asoc_simple_card_priv *priv; + struct device_node *np = pdev->dev.of_node; + struct device *dev = &pdev->dev; + int ret; + + /* Allocate the private data */ + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + ret = asoc_simple_card_parse_of(np, priv, dev); + if (ret < 0) { + if (ret != -EPROBE_DEFER) + dev_err(dev, "parse error %d\n", ret); + goto err; + } + + snd_soc_card_set_drvdata(&priv->snd_card, priv); + + ret = devm_snd_soc_register_card(&pdev->dev, &priv->snd_card); + if (ret >= 0) + return ret; +err: + asoc_simple_card_clean_reference(&priv->snd_card); + + return ret; +} + +static int asoc_simple_card_remove(struct platform_device *pdev) +{ + struct snd_soc_card *card = platform_get_drvdata(pdev); + + return asoc_simple_card_clean_reference(card); +} + +static struct platform_driver asoc_simple_card = { + .driver = { + .name = "simple-scu-audio-card", + .of_match_table = asoc_simple_card_of_match, + }, + .probe = asoc_simple_card_probe, + .remove = asoc_simple_card_remove, +}; + +module_platform_driver(asoc_simple_card); + +MODULE_ALIAS("platform:asoc-simple-scu-card"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("ASoC Simple SCU Sound Card"); +MODULE_AUTHOR("Kuninori Morimoto "); diff --git a/sound/soc/sh/Kconfig b/sound/soc/sh/Kconfig index 9311f119feb5..6db6405d952f 100644 --- a/sound/soc/sh/Kconfig +++ b/sound/soc/sh/Kconfig @@ -42,12 +42,6 @@ config SND_SOC_RCAR help This option enables R-Car SRU/SCU/SSIU/SSI sound support -config SND_SOC_RSRC_CARD - tristate "Renesas Sampling Rate Convert Sound Card" - select SND_SIMPLE_CARD_UTILS - help - This option enables simple sound if you need sampling rate convert - ## ## Boards ## diff --git a/sound/soc/sh/rcar/Makefile b/sound/soc/sh/rcar/Makefile index a89ddf758695..9c3d5aed99d1 100644 --- a/sound/soc/sh/rcar/Makefile +++ b/sound/soc/sh/rcar/Makefile @@ -1,5 +1,2 @@ snd-soc-rcar-objs := core.o gen.o dma.o adg.o ssi.o ssiu.o src.o ctu.o mix.o dvc.o cmd.o obj-$(CONFIG_SND_SOC_RCAR) += snd-soc-rcar.o - -snd-soc-rsrc-card-objs := rsrc-card.o -obj-$(CONFIG_SND_SOC_RSRC_CARD) += snd-soc-rsrc-card.o diff --git a/sound/soc/sh/rcar/rsrc-card.c b/sound/soc/sh/rcar/rsrc-card.c deleted file mode 100644 index 3fa19084e326..000000000000 --- a/sound/soc/sh/rcar/rsrc-card.c +++ /dev/null @@ -1,361 +0,0 @@ -/* - * ASoC simple SCU sound card support - * - * Copyright (C) 2015 Renesas Solutions Corp. - * Kuninori Morimoto - * - * based on ${LINUX}/sound/soc/generic/simple-card.c - * - * 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. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -struct asoc_simple_card_of_data { - const char *prefix; - const struct snd_soc_dapm_route *routes; - int num_routes; -}; - -static const struct of_device_id asoc_simple_card_of_match[] = { - { .compatible = "renesas,rsrc-card", }, - { .compatible = "simple-scu-audio-card", }, - {}, -}; -MODULE_DEVICE_TABLE(of, asoc_simple_card_of_match); - -#define IDX_CPU 0 -#define IDX_CODEC 1 -struct asoc_simple_card_priv { - struct snd_soc_card snd_card; - struct snd_soc_codec_conf codec_conf; - struct asoc_simple_dai *dai_props; - struct snd_soc_dai_link *dai_link; - u32 convert_rate; - u32 convert_channels; -}; - -#define simple_priv_to_dev(priv) ((priv)->snd_card.dev) -#define simple_priv_to_link(priv, i) ((priv)->snd_card.dai_link + (i)) -#define simple_priv_to_props(priv, i) ((priv)->dai_props + (i)) - -#define DAI "sound-dai" -#define CELL "#sound-dai-cells" -#define PREFIX "simple-audio-card," - -static int asoc_simple_card_startup(struct snd_pcm_substream *substream) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct asoc_simple_card_priv *priv = snd_soc_card_get_drvdata(rtd->card); - struct asoc_simple_dai *dai_props = - simple_priv_to_props(priv, rtd->num); - - return clk_prepare_enable(dai_props->clk); -} - -static void asoc_simple_card_shutdown(struct snd_pcm_substream *substream) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct asoc_simple_card_priv *priv = snd_soc_card_get_drvdata(rtd->card); - struct asoc_simple_dai *dai_props = - simple_priv_to_props(priv, rtd->num); - - clk_disable_unprepare(dai_props->clk); -} - -static struct snd_soc_ops asoc_simple_card_ops = { - .startup = asoc_simple_card_startup, - .shutdown = asoc_simple_card_shutdown, -}; - -static int asoc_simple_card_dai_init(struct snd_soc_pcm_runtime *rtd) -{ - struct asoc_simple_card_priv *priv = snd_soc_card_get_drvdata(rtd->card); - struct snd_soc_dai *dai; - struct snd_soc_dai_link *dai_link; - struct asoc_simple_dai *dai_props; - int num = rtd->num; - - dai_link = simple_priv_to_link(priv, num); - dai_props = simple_priv_to_props(priv, num); - dai = dai_link->dynamic ? - rtd->cpu_dai : - rtd->codec_dai; - - return asoc_simple_card_init_dai(dai, dai_props); -} - -static int asoc_simple_card_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, - struct snd_pcm_hw_params *params) -{ - struct asoc_simple_card_priv *priv = snd_soc_card_get_drvdata(rtd->card); - struct snd_interval *rate = hw_param_interval(params, - SNDRV_PCM_HW_PARAM_RATE); - struct snd_interval *channels = hw_param_interval(params, - SNDRV_PCM_HW_PARAM_CHANNELS); - - if (priv->convert_rate) - rate->min = - rate->max = priv->convert_rate; - - if (priv->convert_channels) - channels->min = - channels->max = priv->convert_channels; - - return 0; -} - -static int asoc_simple_card_parse_links(struct device_node *np, - struct asoc_simple_card_priv *priv, - int idx, bool is_fe) -{ - struct device *dev = simple_priv_to_dev(priv); - struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, idx); - struct asoc_simple_dai *dai_props = simple_priv_to_props(priv, idx); - int ret; - - /* Parse TDM slot */ - ret = snd_soc_of_parse_tdm_slot(np, - &dai_props->tx_slot_mask, - &dai_props->rx_slot_mask, - &dai_props->slots, - &dai_props->slot_width); - if (ret) - return ret; - - if (is_fe) { - int is_single_links = 0; - - /* BE is dummy */ - dai_link->codec_of_node = NULL; - dai_link->codec_dai_name = "snd-soc-dummy-dai"; - dai_link->codec_name = "snd-soc-dummy"; - - /* FE settings */ - dai_link->dynamic = 1; - dai_link->dpcm_merged_format = 1; - - ret = asoc_simple_card_parse_cpu(np, dai_link, DAI, CELL, - &is_single_links); - if (ret) - return ret; - - ret = asoc_simple_card_parse_clk_cpu(np, dai_link, dai_props); - if (ret < 0) - return ret; - - ret = asoc_simple_card_set_dailink_name(dev, dai_link, - "fe.%s", - dai_link->cpu_dai_name); - if (ret < 0) - return ret; - - asoc_simple_card_canonicalize_cpu(dai_link, is_single_links); - } else { - /* FE is dummy */ - dai_link->cpu_of_node = NULL; - dai_link->cpu_dai_name = "snd-soc-dummy-dai"; - dai_link->cpu_name = "snd-soc-dummy"; - - /* BE settings */ - dai_link->no_pcm = 1; - dai_link->be_hw_params_fixup = asoc_simple_card_be_hw_params_fixup; - - ret = asoc_simple_card_parse_codec(np, dai_link, DAI, CELL); - if (ret < 0) - return ret; - - ret = asoc_simple_card_parse_clk_codec(np, dai_link, dai_props); - if (ret < 0) - return ret; - - ret = asoc_simple_card_set_dailink_name(dev, dai_link, - "be.%s", - dai_link->codec_dai_name); - if (ret < 0) - return ret; - - snd_soc_of_parse_audio_prefix(&priv->snd_card, - &priv->codec_conf, - dai_link->codec_of_node, - PREFIX "prefix"); - } - - ret = asoc_simple_card_canonicalize_dailink(dai_link); - if (ret < 0) - return ret; - - dai_link->dpcm_playback = 1; - dai_link->dpcm_capture = 1; - dai_link->ops = &asoc_simple_card_ops; - dai_link->init = asoc_simple_card_dai_init; - - dev_dbg(dev, "\t%s / %04x / %d\n", - dai_link->name, - dai_link->dai_fmt, - dai_props->sysclk); - - return 0; -} - -static int asoc_simple_card_dai_link_of(struct device_node *node, - struct asoc_simple_card_priv *priv) -{ - struct device *dev = simple_priv_to_dev(priv); - struct snd_soc_dai_link *dai_link; - struct device_node *np; - unsigned int daifmt = 0; - int ret, i; - bool is_fe; - - /* find 1st codec */ - i = 0; - for_each_child_of_node(node, np) { - dai_link = simple_priv_to_link(priv, i); - - if (strcmp(np->name, PREFIX "codec") == 0) { - ret = asoc_simple_card_parse_daifmt(dev, node, np, - PREFIX, &daifmt); - if (ret < 0) - return ret; - break; - } - i++; - } - - i = 0; - for_each_child_of_node(node, np) { - dai_link = simple_priv_to_link(priv, i); - dai_link->dai_fmt = daifmt; - - is_fe = false; - if (strcmp(np->name, PREFIX "cpu") == 0) - is_fe = true; - - ret = asoc_simple_card_parse_links(np, priv, i, is_fe); - if (ret < 0) - return ret; - i++; - } - - return 0; -} - -static int asoc_simple_card_parse_of(struct device_node *node, - struct asoc_simple_card_priv *priv, - struct device *dev) -{ - struct asoc_simple_dai *props; - struct snd_soc_dai_link *links; - int ret; - int num; - - if (!node) - return -EINVAL; - - num = of_get_child_count(node); - props = devm_kzalloc(dev, sizeof(*props) * num, GFP_KERNEL); - links = devm_kzalloc(dev, sizeof(*links) * num, GFP_KERNEL); - if (!props || !links) - return -ENOMEM; - - priv->dai_props = props; - priv->dai_link = links; - - /* 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; - priv->snd_card.codec_conf = &priv->codec_conf; - priv->snd_card.num_configs = 1; - - ret = snd_soc_of_parse_audio_routing(&priv->snd_card, PREFIX "routing"); - if (ret < 0) - return ret; - - /* sampling rate convert */ - of_property_read_u32(node, PREFIX "convert-rate", &priv->convert_rate); - - /* channels transfer */ - of_property_read_u32(node, PREFIX "convert-channels", &priv->convert_channels); - - ret = asoc_simple_card_dai_link_of(node, priv); - if (ret < 0) - return ret; - - ret = asoc_simple_card_parse_card_name(&priv->snd_card, PREFIX); - if (ret < 0) - return ret; - - dev_dbg(dev, "New card: %s\n", - priv->snd_card.name ? priv->snd_card.name : ""); - dev_dbg(dev, "convert_rate %d\n", priv->convert_rate); - dev_dbg(dev, "convert_channels %d\n", priv->convert_channels); - - return 0; -} - -static int asoc_simple_card_probe(struct platform_device *pdev) -{ - struct asoc_simple_card_priv *priv; - struct device_node *np = pdev->dev.of_node; - struct device *dev = &pdev->dev; - int ret; - - /* Allocate the private data */ - priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; - - ret = asoc_simple_card_parse_of(np, priv, dev); - if (ret < 0) { - if (ret != -EPROBE_DEFER) - dev_err(dev, "parse error %d\n", ret); - goto err; - } - - snd_soc_card_set_drvdata(&priv->snd_card, priv); - - ret = devm_snd_soc_register_card(&pdev->dev, &priv->snd_card); - if (ret >= 0) - return ret; -err: - asoc_simple_card_clean_reference(&priv->snd_card); - - return ret; -} - -static int asoc_simple_card_remove(struct platform_device *pdev) -{ - struct snd_soc_card *card = platform_get_drvdata(pdev); - - return asoc_simple_card_clean_reference(card); -} - -static struct platform_driver asoc_simple_card = { - .driver = { - .name = "simple-scu-audio-card", - .of_match_table = asoc_simple_card_of_match, - }, - .probe = asoc_simple_card_probe, - .remove = asoc_simple_card_remove, -}; - -module_platform_driver(asoc_simple_card); - -MODULE_ALIAS("platform:asoc-simple-scu-card"); -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("ASoC Simple SCU Sound Card"); -MODULE_AUTHOR("Kuninori Morimoto ");