From mboxrd@z Thu Jan 1 00:00:00 1970 MIME-Version: 1.0 In-Reply-To: <87poim7md3.wl%kuninori.morimoto.gx@renesas.com> References: <87zihq7mja.wl%kuninori.morimoto.gx@renesas.com> <87poim7md3.wl%kuninori.morimoto.gx@renesas.com> Date: Fri, 24 Feb 2017 08:45:15 -0600 Message-ID: Subject: Re: [PATCH v2 7/7] ASoC: add simple-graph-card support From: Rob Herring Content-Type: text/plain; charset=UTF-8 To: Kuninori Morimoto Cc: Mark Brown , Linux-ALSA , Simon , Linux-DT List-ID: On Sun, Feb 12, 2017 at 10:23 PM, Kuninori Morimoto wrote: > > From: Kuninori Morimoto > > OF-graph base DT binding are used on V4L2, and ALSA SoC is using > different style of DT today. Now ALSA SoC supports simple-card driver > for generic/simple sound card. > In the future, V4L2 / ALSA will support HDMI, and then, DT bindings > between V4L2 / ALSA should be merged. > This patch adds OF-graph base DT binding with simple-card style > > Signed-off-by: Kuninori Morimoto > --- > v1 -> v2 > > - no change > > sound/soc/generic/Kconfig | 7 + > sound/soc/generic/Makefile | 2 + > sound/soc/generic/simple-graph-card.c | 303 ++++++++++++++++++++++++++++++++++ > 3 files changed, 312 insertions(+) > create mode 100644 sound/soc/generic/simple-graph-card.c > > diff --git a/sound/soc/generic/Kconfig b/sound/soc/generic/Kconfig > index d023959..efefabd 100644 > --- a/sound/soc/generic/Kconfig > +++ b/sound/soc/generic/Kconfig > @@ -14,3 +14,10 @@ config SND_SIMPLE_SCU_CARD > help > This option enables generic simple SCU sound card support. > It supports DPCM of multi CPU single Codec system. > + > +config SND_SIMPLE_GRAPH_CARD > + tristate "ASoC Simple Graph sound card support" > + depends on OF > + select SND_SIMPLE_CARD_UTILS > + help > + This option enables generic simple Graph sound card support > diff --git a/sound/soc/generic/Makefile b/sound/soc/generic/Makefile > index ee750f3..94eb6f1 100644 > --- a/sound/soc/generic/Makefile > +++ b/sound/soc/generic/Makefile > @@ -1,7 +1,9 @@ > 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 > +snd-soc-simple-graph-card-objs := simple-graph-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 > +obj-$(CONFIG_SND_SIMPLE_GRAPH_CARD) += snd-soc-simple-graph-card.o > diff --git a/sound/soc/generic/simple-graph-card.c b/sound/soc/generic/simple-graph-card.c > new file mode 100644 > index 0000000..700717b > --- /dev/null > +++ b/sound/soc/generic/simple-graph-card.c > @@ -0,0 +1,303 @@ > +/* > + * ASoC simple graph sound card support > + * > + * Copyright (C) 2016 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 > +#include > + > +struct simple_card_data { > + struct snd_soc_card snd_card; > + struct simple_dai_props { > + struct asoc_simple_dai cpu_dai; > + struct asoc_simple_dai codec_dai; > + } *dai_props; > + struct snd_soc_dai_link *dai_link; > +}; > + > +#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 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 simple_card_data *priv = snd_soc_card_get_drvdata(rtd->card); > + struct simple_dai_props *dai_props = > + simple_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); > + > + return ret; > +} > + > +static void asoc_simple_card_shutdown(struct snd_pcm_substream *substream) > +{ > + struct snd_soc_pcm_runtime *rtd = substream->private_data; > + struct simple_card_data *priv = snd_soc_card_get_drvdata(rtd->card); > + struct simple_dai_props *dai_props = > + simple_priv_to_props(priv, rtd->num); > + > + clk_disable_unprepare(dai_props->cpu_dai.clk); > + > + clk_disable_unprepare(dai_props->codec_dai.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 simple_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 simple_dai_props *dai_props = > + simple_priv_to_props(priv, rtd->num); > + int ret; > + > + ret = asoc_simple_card_init_dai(codec, &dai_props->codec_dai); > + if (ret < 0) > + return ret; > + > + ret = asoc_simple_card_init_dai(cpu, &dai_props->cpu_dai); > + if (ret < 0) > + return ret; > + > + return 0; > +} > + > +static int asoc_simple_card_dai_link_of(struct device_node *cpu_port, I think the sound card node should point to dai's rather than ports. > + struct simple_card_data *priv, > + int idx) > +{ > + 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 asoc_simple_dai *cpu_dai = &dai_props->cpu_dai; > + struct asoc_simple_dai *codec_dai = &dai_props->codec_dai; > + struct device_node *cpu_ep = of_get_next_child(cpu_port, NULL); > + struct device_node *codec_ep = of_graph_get_remote_endpoint(cpu_ep); > + struct device_node *rcpu_ep = of_graph_get_remote_endpoint(codec_ep); > + int ret; > + > + if (rcpu_ep != cpu_ep) { You don't need this check. The kernel's job is not DT validation. > + dev_err(dev, "remote-endpoint missmatch (%s/%s/%s)\n", > + cpu_ep->name, codec_ep->name, rcpu_ep->name); > + return -EINVAL; > + } > + > + ret = asoc_simple_card_parse_daifmt(dev, cpu_ep, codec_ep, > + PREFIX, &dai_link->dai_fmt); > + if (ret < 0) > + goto dai_link_of_err; > + > + /* > + * we need to consider "mclk-fs" around here > + * see simple-card > + */ > + > + ret = asoc_simple_card_parse_graph_cpu(cpu_ep, dai_link); > + if (ret < 0) > + goto dai_link_of_err; > + > + ret = asoc_simple_card_parse_graph_codec(codec_ep, dai_link); > + if (ret < 0) > + goto dai_link_of_err; > + > + ret = snd_soc_of_parse_tdm_slot(cpu_ep, > + &cpu_dai->tx_slot_mask, > + &cpu_dai->rx_slot_mask, > + &cpu_dai->slots, > + &cpu_dai->slot_width); > + if (ret < 0) > + goto dai_link_of_err; > + > + ret = snd_soc_of_parse_tdm_slot(codec_ep, > + &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; > + > + ret = asoc_simple_card_parse_clk_cpu(dev, cpu_ep, dai_link, cpu_dai); > + if (ret < 0) > + goto dai_link_of_err; > + > + ret = asoc_simple_card_parse_clk_codec(dev, codec_ep, dai_link, codec_dai); > + if (ret < 0) > + goto dai_link_of_err; > + > + ret = asoc_simple_card_canonicalize_dailink(dai_link); > + if (ret < 0) > + goto dai_link_of_err; > + > + ret = asoc_simple_card_set_dailink_name(dev, dai_link, > + "%s-%s", > + dai_link->cpu_dai_name, > + dai_link->codec_dai_name); > + if (ret < 0) > + goto dai_link_of_err; > + > + dai_link->ops = &asoc_simple_card_ops; > + dai_link->init = asoc_simple_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->cpu_dai_name, > + dai_props->cpu_dai.sysclk); > + dev_dbg(dev, "\tcodec : %s / %d\n", > + dai_link->codec_dai_name, > + dai_props->codec_dai.sysclk); > + > + asoc_simple_card_canonicalize_cpu(dai_link, > + priv->snd_card.num_links == 1); > + > +dai_link_of_err: > + of_node_put(codec_ep); > + > + return ret; > +} > + > +static int asoc_simple_card_parse_of(struct device *dev, > + struct simple_card_data *priv) > +{ > + struct device_node *node = dev->of_node; > + struct snd_soc_card *card = &priv->snd_card; > + struct device_node *np; > + int idx = 0; > + int ret; > + > + /* > + * we need to consider "widgets", "routing", "mclk-fs" around here > + * see simple-card > + */ > + > + while ((np = of_parse_phandle(node, "dais", idx))) { > + ret = asoc_simple_card_dai_link_of(np, priv, idx++); > + if (ret < 0) > + return ret; > + } > + > + return asoc_simple_card_parse_card_name(card, PREFIX); > +} > + > +static int asoc_simple_get_dais_count(struct device *dev) > +{ > + struct device_node *node = dev->of_node; > + int count = 0; > + > + if (of_get_property(node, "dais", NULL)) { > + while (of_parse_phandle(node, "dais", count)) > + count++; > + } else { > + count = 1; > + } > + > + return count; > +} > + > +static int asoc_simple_card_probe(struct platform_device *pdev) > +{ > + struct simple_card_data *priv; > + struct snd_soc_dai_link *dai_link; > + struct simple_dai_props *dai_props; > + struct device *dev = &pdev->dev; > + int num, ret; > + > + /* Allocate the private data and the DAI link array */ > + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); > + if (!priv) > + return -ENOMEM; > + > + num = asoc_simple_get_dais_count(dev); > + > + 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; > + > + /* Init snd_soc_card */ > + priv->snd_card.owner = THIS_MODULE; > + priv->snd_card.dev = dev; > + priv->snd_card.dai_link = dai_link; > + priv->snd_card.num_links = num; > + > + ret = asoc_simple_card_parse_of(dev, priv); > + 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(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); > + struct simple_card_data *priv = snd_soc_card_get_drvdata(card); > + > + return asoc_simple_card_clean_reference(&priv->snd_card); > +} > + > +static const struct of_device_id asoc_simple_of_match[] = { > + { .compatible = "asoc-simple-graph-card", }, > + {}, > +}; > +MODULE_DEVICE_TABLE(of, asoc_simple_of_match); > + > +static struct platform_driver asoc_simple_card = { > + .driver = { > + .name = "asoc-simple-graph-card", > + .of_match_table = asoc_simple_of_match, > + }, > + .probe = asoc_simple_card_probe, > + .remove = asoc_simple_card_remove, > +}; > +module_platform_driver(asoc_simple_card); > + > +MODULE_ALIAS("platform:asoc-simple-graph-card"); > +MODULE_LICENSE("GPL v2"); > +MODULE_DESCRIPTION("ASoC Simple Graph Sound Card"); > +MODULE_AUTHOR("Kuninori Morimoto "); > -- > 1.9.1 >