From patchwork Tue Nov 4 10:21:46 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jean-Francois Moine X-Patchwork-Id: 5225001 Return-Path: X-Original-To: patchwork-alsa-devel@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.19.201]) by patchwork2.web.kernel.org (Postfix) with ESMTP id A9FD6C11AC for ; Tue, 4 Nov 2014 10:32:31 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 878CA201EF for ; Tue, 4 Nov 2014 10:32:30 +0000 (UTC) Received: from alsa0.perex.cz (alsa0.perex.cz [77.48.224.243]) by mail.kernel.org (Postfix) with ESMTP id 1B5DD20166 for ; Tue, 4 Nov 2014 10:32:29 +0000 (UTC) Received: by alsa0.perex.cz (Postfix, from userid 1000) id 1E86B2612D2; Tue, 4 Nov 2014 11:32:28 +0100 (CET) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Spam-Level: X-Spam-Status: No, score=-1.9 required=5.0 tests=BAYES_00,FREEMAIL_FROM, RCVD_IN_DNSWL_NONE, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 Received: from alsa0.perex.cz (localhost [IPv6:::1]) by alsa0.perex.cz (Postfix) with ESMTP id 1AAFB2614A6; Tue, 4 Nov 2014 11:30:36 +0100 (CET) X-Original-To: alsa-devel@alsa-project.org Delivered-To: alsa-devel@alsa-project.org Received: by alsa0.perex.cz (Postfix, from userid 1000) id 06F2926148A; Tue, 4 Nov 2014 11:30:31 +0100 (CET) Received: from smtp4-g21.free.fr (smtp4-g21.free.fr [212.27.42.4]) by alsa0.perex.cz (Postfix) with ESMTP id 559BF26085B for ; Tue, 4 Nov 2014 11:29:45 +0100 (CET) Received: from localhost (unknown [IPv6:2a01:e35:2f5c:9de0:212:bfff:fe1e:9ce4]) by smtp4-g21.free.fr (Postfix) with ESMTP id DD1C74C80D9; Tue, 4 Nov 2014 11:27:41 +0100 (CET) X-Mailbox-Line: From 53ef077d2174f8eb5b861d6cebe7c9e732f06d52 Mon Sep 17 00:00:00 2001 Message-Id: <53ef077d2174f8eb5b861d6cebe7c9e732f06d52.1415096705.git.moinejf@free.fr> In-Reply-To: References: From: Jean-Francois Moine Date: Tue, 4 Nov 2014 11:21:46 +0100 To: Mark Brown Cc: devicetree@vger.kernel.org, alsa-devel@alsa-project.org, Jyri Sarha , Xiubo Li , Benoit Cousson , Kuninori Morimoto Subject: [alsa-devel] [PATCH v3 2/2] ASoC: simple-card: add multi-CODECs in DT X-BeenThere: alsa-devel@alsa-project.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: "Alsa-devel mailing list for ALSA developers - http://www.alsa-project.org" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: alsa-devel-bounces@alsa-project.org Sender: alsa-devel-bounces@alsa-project.org X-Virus-Scanned: ClamAV using ClamSMTP This patch allows many CODECs per link to be defined in the device tree. Signed-off-by: Jean-Francois Moine --- .../devicetree/bindings/sound/simple-card.txt | 28 ++--- sound/soc/generic/simple-card.c | 135 +++++++++++++-------- 2 files changed, 94 insertions(+), 69 deletions(-) diff --git a/Documentation/devicetree/bindings/sound/simple-card.txt b/Documentation/devicetree/bindings/sound/simple-card.txt index c3cba60..6cb1816 100644 --- a/Documentation/devicetree/bindings/sound/simple-card.txt +++ b/Documentation/devicetree/bindings/sound/simple-card.txt @@ -65,7 +65,11 @@ properties should also be placed in the codec node if needed. Required CPU/CODEC subnodes properties: -- sound-dai : phandle and port of CPU/CODEC +either + - sound-dai : phandle and port of CPU/CODEC +or + - sound-dais : list of phandle and port of CODECs + Optional CPU/CODEC subnodes properties: @@ -119,37 +123,29 @@ sh_fsi2: sh_fsi2@ec230000 { interrupts = <0 146 0x4>; }; -Example 2 - many DAI links: +Example 2 - many DAI links and multi-CODECs: sound { compatible = "simple-audio-card"; simple-audio-card,name = "Cubox Audio"; - simple-audio-card,dai-link@0 { /* I2S - HDMI */ + simple-audio-card,dai-link@0 { /* S/PDIF - HDMI & S/PDIF */ format = "i2s"; cpu { - sound-dai = <&audio1 0>; - }; - codec { - sound-dai = <&tda998x 0>; - }; - }; - - simple-audio-card,dai-link@1 { /* S/PDIF - HDMI */ - cpu { sound-dai = <&audio1 1>; }; codec { - sound-dai = <&tda998x 1>; + sound-dais = <&hdmi 0>, <&spdif_codec>; }; }; - simple-audio-card,dai-link@2 { /* S/PDIF - S/PDIF */ + simple-audio-card,dai-link@1 { /* I2S - HDMI */ + format = "i2s"; cpu { - sound-dai = <&audio1 1>; + sound-dai = <&audio1 0>; }; codec { - sound-dai = <&spdif_codec>; + sound-dai = <&hdmi 1>; }; }; }; diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c index cd49d50..ed3b125 100644 --- a/sound/soc/generic/simple-card.c +++ b/sound/soc/generic/simple-card.c @@ -172,34 +172,12 @@ static int asoc_simple_card_dai_init(struct snd_soc_pcm_runtime *rtd) static int asoc_simple_card_sub_parse_of(struct device_node *np, struct asoc_simple_dai *dai, - struct device_node **p_node, - const char **name, - int *args_count) + const struct device_node *dai_np) { - struct of_phandle_args args; struct clk *clk; u32 val; int ret; - /* - * 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(np, "sound-dai", - "#sound-dai-cells", 0, &args); - if (ret) - return ret; - - *p_node = args.np; - - if (args_count) - *args_count = args.args_count; - - /* Get dai->name */ - ret = snd_soc_of_get_dai_name(np, name); - if (ret < 0) - return ret; - /* Parse TDM slot */ ret = snd_soc_of_parse_tdm_slot(np, &dai->slots, &dai->slot_width); if (ret) @@ -222,7 +200,7 @@ asoc_simple_card_sub_parse_of(struct device_node *np, } else if (!of_property_read_u32(np, "system-clock-frequency", &val)) { dai->sysclk = val; } else { - clk = of_clk_get(args.np, 0); + clk = of_clk_get((struct device_node *) dai_np, 0); if (!IS_ERR(clk)) dai->sysclk = clk_get_rate(clk); } @@ -232,7 +210,6 @@ asoc_simple_card_sub_parse_of(struct device_node *np, static int asoc_simple_card_parse_daifmt(struct device_node *node, struct simple_card_data *priv, - struct device_node *cpu, struct device_node *codec, char *prefix, int idx) { @@ -285,62 +262,99 @@ static int asoc_simple_card_dai_link_of(struct device_node *node, struct device *dev = simple_priv_to_dev(priv); struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, idx); struct simple_dai_props *dai_props = simple_priv_to_props(priv, idx); - struct device_node *cpu = NULL; - struct device_node *codec = NULL; + struct device_node *np = NULL; + struct snd_soc_dai_link_component *component; + struct of_phandle_args args; char *name; char prop[128]; char *prefix = ""; - int ret, cpu_args; + int ret, cpu_args, i, num_codecs; /* For single DAI link & old style of DT node */ if (is_top_level_node) prefix = "simple-audio-card,"; snprintf(prop, sizeof(prop), "%scpu", prefix); - cpu = of_get_child_by_name(node, prop); - - snprintf(prop, sizeof(prop), "%scodec", prefix); - codec = of_get_child_by_name(node, prop); - - if (!cpu || !codec) { + np = of_get_child_by_name(node, prop); + if (!np) { ret = -EINVAL; dev_err(dev, "%s: Can't find %s DT node\n", __func__, prop); goto dai_link_of_err; } - ret = asoc_simple_card_parse_daifmt(node, priv, - cpu, codec, prefix, idx); - if (ret < 0) + /* Get the CPU node and name */ + ret = of_parse_phandle_with_args(np, "sound-dai", + "#sound-dai-cells", 0, &args); + if (ret) goto dai_link_of_err; + dai_link->cpu_of_node = args.np; + cpu_args = args.args_count; - ret = asoc_simple_card_sub_parse_of(cpu, &dai_props->cpu_dai, - &dai_link->cpu_of_node, - &dai_link->cpu_dai_name, - &cpu_args); + ret = snd_soc_of_get_dai_name(np, &dai_link->cpu_dai_name); if (ret < 0) goto dai_link_of_err; - ret = asoc_simple_card_sub_parse_of(codec, &dai_props->codec_dai, - &dai_link->codec_of_node, - &dai_link->codec_dai_name, NULL); + ret = asoc_simple_card_sub_parse_of(np, &dai_props->cpu_dai, + dai_link->cpu_of_node); if (ret < 0) goto dai_link_of_err; + of_node_put(np); - if (!dai_link->cpu_dai_name || !dai_link->codec_dai_name) { + snprintf(prop, sizeof(prop), "%scodec", prefix); + np = of_get_child_by_name(node, prop); + if (!np) { ret = -EINVAL; + dev_err(dev, "%s: Can't find %s DT node\n", __func__, prop); + goto dai_link_of_err; + } + + /* Get the node and name of the CODEC(s) */ + name = "sound-dai"; + num_codecs = of_count_phandle_with_args(np, name, + "#sound-dai-cells"); + if (num_codecs <= 0) { + name = "sound-dais"; + num_codecs = of_count_phandle_with_args(np, name, + "#sound-dai-cells"); + if (num_codecs <= 0) { + ret = -EINVAL; + dev_err(dev, "No CODEC DAI phandle\n"); + goto dai_link_of_err; + } + } + component = devm_kzalloc(dev, + sizeof *component * num_codecs, + GFP_KERNEL); + if (!component) { + ret = -ENOMEM; goto dai_link_of_err; } + dai_link->codecs = component; + dai_link->num_codecs = num_codecs; + ret = snd_soc_of_get_dai_link_codecs(np, name, dai_link); + if (ret < 0) + goto dai_link_of_err; + + ret = asoc_simple_card_sub_parse_of(np, &dai_props->codec_dai, + dai_link->codecs[0].of_node); + if (ret < 0) + goto dai_link_of_err; /* Simple Card assumes platform == cpu */ dai_link->platform_of_node = dai_link->cpu_of_node; + ret = asoc_simple_card_parse_daifmt(node, priv, + np, prefix, idx); + if (ret < 0) + goto dai_link_of_err; + /* DAI link name is created from CPU/CODEC dai name */ name = devm_kzalloc(dev, strlen(dai_link->cpu_dai_name) + - strlen(dai_link->codec_dai_name) + 2, + strlen(dai_link->codecs[0].dai_name) + 2, GFP_KERNEL); sprintf(name, "%s-%s", dai_link->cpu_dai_name, - dai_link->codec_dai_name); + dai_link->codecs[0].dai_name); dai_link->name = dai_link->stream_name = name; dai_link->ops = &asoc_simple_card_ops; dai_link->init = asoc_simple_card_dai_init; @@ -351,7 +365,7 @@ static int asoc_simple_card_dai_link_of(struct device_node *node, dai_props->cpu_dai.fmt, dai_props->cpu_dai.sysclk); dev_dbg(dev, "\tcodec : %s / %04x / %d\n", - dai_link->codec_dai_name, + dai_link->codecs[0].dai_name, dai_props->codec_dai.fmt, dai_props->codec_dai.sysclk); @@ -366,11 +380,20 @@ static int asoc_simple_card_dai_link_of(struct device_node *node, */ if (!cpu_args) dai_link->cpu_dai_name = NULL; + goto out; dai_link_of_err: - of_node_put(cpu); - of_node_put(codec); + for (i = 0, component = dai_link->codecs; + i < dai_link->num_codecs; + i++, component++) { + if (!component->of_node) + break; + of_node_put((struct device_node *) component->of_node); + component->of_node = NULL; + } +out: + of_node_put(np); return ret; } @@ -457,16 +480,22 @@ static int asoc_simple_card_unref(struct platform_device *pdev) { struct snd_soc_card *card = platform_get_drvdata(pdev); struct snd_soc_dai_link *dai_link; + struct snd_soc_dai_link_component *component; struct device_node *np; - int num_links; + int num_links, i; for (num_links = 0, dai_link = card->dai_link; num_links < card->num_links; num_links++, dai_link++) { np = (struct device_node *) dai_link->cpu_of_node; of_node_put(np); - np = (struct device_node *) dai_link->codec_of_node; - of_node_put(np); + for (i = 0, component = dai_link->codecs; + i < dai_link->num_codecs; + i++, component++) { + if (!component->of_node) + break; + of_node_put((struct device_node *) component->of_node); + } } return 0; }