aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKuninori Morimoto <kuninori.morimoto.gx@renesas.com>2017-04-19 21:36:08 -0400
committerMark Brown <broonie@kernel.org>2017-05-17 05:21:21 -0400
commit2692c1c63c29cad3bec090c3e6ed9e692ca66683 (patch)
tree6ad6474be82a80a91964a7ee49f5ecea0b111235
parent2d4e31de5bb2b5fbdbcd8a3bfec0eae0bd4ca409 (diff)
ASoC: add audio-graph-card support
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 new Audio Graph Card which is OF-graph base of simple-card Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> Signed-off-by: Mark Brown <broonie@kernel.org>
-rw-r--r--sound/soc/generic/Kconfig8
-rw-r--r--sound/soc/generic/Makefile2
-rw-r--r--sound/soc/generic/audio-graph-card.c308
3 files changed, 318 insertions, 0 deletions
diff --git a/sound/soc/generic/Kconfig b/sound/soc/generic/Kconfig
index d023959b8cd6..121a48e8bb7d 100644
--- a/sound/soc/generic/Kconfig
+++ b/sound/soc/generic/Kconfig
@@ -14,3 +14,11 @@ config SND_SIMPLE_SCU_CARD
14 help 14 help
15 This option enables generic simple SCU sound card support. 15 This option enables generic simple SCU sound card support.
16 It supports DPCM of multi CPU single Codec system. 16 It supports DPCM of multi CPU single Codec system.
17
18config SND_AUDIO_GRAPH_CARD
19 tristate "ASoC Audio Graph sound card support"
20 depends on OF
21 select SND_SIMPLE_CARD_UTILS
22 help
23 This option enables generic simple simple sound card support
24 with OF-graph DT bindings.
diff --git a/sound/soc/generic/Makefile b/sound/soc/generic/Makefile
index ee750f3023ba..670068f257b9 100644
--- a/sound/soc/generic/Makefile
+++ b/sound/soc/generic/Makefile
@@ -1,7 +1,9 @@
1snd-soc-simple-card-utils-objs := simple-card-utils.o 1snd-soc-simple-card-utils-objs := simple-card-utils.o
2snd-soc-simple-card-objs := simple-card.o 2snd-soc-simple-card-objs := simple-card.o
3snd-soc-simple-scu-card-objs := simple-scu-card.o 3snd-soc-simple-scu-card-objs := simple-scu-card.o
4snd-soc-audio-graph-card-objs := audio-graph-card.o
4 5
5obj-$(CONFIG_SND_SIMPLE_CARD_UTILS) += snd-soc-simple-card-utils.o 6obj-$(CONFIG_SND_SIMPLE_CARD_UTILS) += snd-soc-simple-card-utils.o
6obj-$(CONFIG_SND_SIMPLE_CARD) += snd-soc-simple-card.o 7obj-$(CONFIG_SND_SIMPLE_CARD) += snd-soc-simple-card.o
7obj-$(CONFIG_SND_SIMPLE_SCU_CARD) += snd-soc-simple-scu-card.o 8obj-$(CONFIG_SND_SIMPLE_SCU_CARD) += snd-soc-simple-scu-card.o
9obj-$(CONFIG_SND_AUDIO_GRAPH_CARD) += snd-soc-audio-graph-card.o
diff --git a/sound/soc/generic/audio-graph-card.c b/sound/soc/generic/audio-graph-card.c
new file mode 100644
index 000000000000..07e010d38596
--- /dev/null
+++ b/sound/soc/generic/audio-graph-card.c
@@ -0,0 +1,308 @@
1/*
2 * ASoC audio graph sound card support
3 *
4 * Copyright (C) 2016 Renesas Solutions Corp.
5 * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
6 *
7 * based on ${LINUX}/sound/soc/generic/simple-card.c
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License version 2 as
11 * published by the Free Software Foundation.
12 */
13#include <linux/clk.h>
14#include <linux/device.h>
15#include <linux/gpio.h>
16#include <linux/module.h>
17#include <linux/of.h>
18#include <linux/of_device.h>
19#include <linux/of_gpio.h>
20#include <linux/of_graph.h>
21#include <linux/platform_device.h>
22#include <linux/string.h>
23#include <sound/jack.h>
24#include <sound/simple_card_utils.h>
25
26struct graph_card_data {
27 struct snd_soc_card snd_card;
28 struct graph_dai_props {
29 struct asoc_simple_dai cpu_dai;
30 struct asoc_simple_dai codec_dai;
31 } *dai_props;
32 struct snd_soc_dai_link *dai_link;
33};
34
35#define graph_priv_to_card(priv) (&(priv)->snd_card)
36#define graph_priv_to_props(priv, i) ((priv)->dai_props + (i))
37#define graph_priv_to_dev(priv) (graph_priv_to_card(priv)->dev)
38#define graph_priv_to_link(priv, i) (graph_priv_to_card(priv)->dai_link + (i))
39
40static int asoc_graph_card_startup(struct snd_pcm_substream *substream)
41{
42 struct snd_soc_pcm_runtime *rtd = substream->private_data;
43 struct graph_card_data *priv = snd_soc_card_get_drvdata(rtd->card);
44 struct graph_dai_props *dai_props = graph_priv_to_props(priv, rtd->num);
45 int ret;
46
47 ret = clk_prepare_enable(dai_props->cpu_dai.clk);
48 if (ret)
49 return ret;
50
51 ret = clk_prepare_enable(dai_props->codec_dai.clk);
52 if (ret)
53 clk_disable_unprepare(dai_props->cpu_dai.clk);
54
55 return ret;
56}
57
58static void asoc_graph_card_shutdown(struct snd_pcm_substream *substream)
59{
60 struct snd_soc_pcm_runtime *rtd = substream->private_data;
61 struct graph_card_data *priv = snd_soc_card_get_drvdata(rtd->card);
62 struct graph_dai_props *dai_props = graph_priv_to_props(priv, rtd->num);
63
64 clk_disable_unprepare(dai_props->cpu_dai.clk);
65
66 clk_disable_unprepare(dai_props->codec_dai.clk);
67}
68
69static struct snd_soc_ops asoc_graph_card_ops = {
70 .startup = asoc_graph_card_startup,
71 .shutdown = asoc_graph_card_shutdown,
72};
73
74static int asoc_graph_card_dai_init(struct snd_soc_pcm_runtime *rtd)
75{
76 struct graph_card_data *priv = snd_soc_card_get_drvdata(rtd->card);
77 struct snd_soc_dai *codec = rtd->codec_dai;
78 struct snd_soc_dai *cpu = rtd->cpu_dai;
79 struct graph_dai_props *dai_props =
80 graph_priv_to_props(priv, rtd->num);
81 int ret;
82
83 ret = asoc_simple_card_init_dai(codec, &dai_props->codec_dai);
84 if (ret < 0)
85 return ret;
86
87 ret = asoc_simple_card_init_dai(cpu, &dai_props->cpu_dai);
88 if (ret < 0)
89 return ret;
90
91 return 0;
92}
93
94static int asoc_graph_card_dai_link_of(struct device_node *cpu_port,
95 struct graph_card_data *priv,
96 int idx)
97{
98 struct device *dev = graph_priv_to_dev(priv);
99 struct snd_soc_dai_link *dai_link = graph_priv_to_link(priv, idx);
100 struct graph_dai_props *dai_props = graph_priv_to_props(priv, idx);
101 struct asoc_simple_dai *cpu_dai = &dai_props->cpu_dai;
102 struct asoc_simple_dai *codec_dai = &dai_props->codec_dai;
103 struct snd_soc_card *card = graph_priv_to_card(priv);
104 struct device_node *cpu_ep = of_get_next_child(cpu_port, NULL);
105 struct device_node *codec_ep = of_graph_get_remote_endpoint(cpu_ep);
106 struct device_node *rcpu_ep = of_graph_get_remote_endpoint(codec_ep);
107 int ret;
108
109 if (rcpu_ep != cpu_ep) {
110 dev_err(dev, "remote-endpoint missmatch (%s/%s/%s)\n",
111 cpu_ep->name, codec_ep->name, rcpu_ep->name);
112 ret = -EINVAL;
113 goto dai_link_of_err;
114 }
115
116 ret = asoc_simple_card_parse_daifmt(dev, cpu_ep, codec_ep,
117 NULL, &dai_link->dai_fmt);
118 if (ret < 0)
119 goto dai_link_of_err;
120
121 /*
122 * we need to consider "mclk-fs" around here
123 * see simple-card
124 */
125
126 ret = asoc_simple_card_parse_graph_cpu(cpu_ep, dai_link);
127 if (ret < 0)
128 goto dai_link_of_err;
129
130 ret = asoc_simple_card_parse_graph_codec(codec_ep, dai_link);
131 if (ret < 0)
132 goto dai_link_of_err;
133
134 ret = snd_soc_of_parse_tdm_slot(cpu_ep,
135 &cpu_dai->tx_slot_mask,
136 &cpu_dai->rx_slot_mask,
137 &cpu_dai->slots,
138 &cpu_dai->slot_width);
139 if (ret < 0)
140 goto dai_link_of_err;
141
142 ret = snd_soc_of_parse_tdm_slot(codec_ep,
143 &codec_dai->tx_slot_mask,
144 &codec_dai->rx_slot_mask,
145 &codec_dai->slots,
146 &codec_dai->slot_width);
147 if (ret < 0)
148 goto dai_link_of_err;
149
150 ret = asoc_simple_card_parse_clk_cpu(dev, cpu_ep, dai_link, cpu_dai);
151 if (ret < 0)
152 goto dai_link_of_err;
153
154 ret = asoc_simple_card_parse_clk_codec(dev, codec_ep, dai_link, codec_dai);
155 if (ret < 0)
156 goto dai_link_of_err;
157
158 ret = asoc_simple_card_canonicalize_dailink(dai_link);
159 if (ret < 0)
160 goto dai_link_of_err;
161
162 ret = asoc_simple_card_set_dailink_name(dev, dai_link,
163 "%s-%s",
164 dai_link->cpu_dai_name,
165 dai_link->codec_dai_name);
166 if (ret < 0)
167 goto dai_link_of_err;
168
169 dai_link->ops = &asoc_graph_card_ops;
170 dai_link->init = asoc_graph_card_dai_init;
171
172 dev_dbg(dev, "\tname : %s\n", dai_link->stream_name);
173 dev_dbg(dev, "\tformat : %04x\n", dai_link->dai_fmt);
174 dev_dbg(dev, "\tcpu : %s / %d\n",
175 dai_link->cpu_dai_name,
176 cpu_dai->sysclk);
177 dev_dbg(dev, "\tcodec : %s / %d\n",
178 dai_link->codec_dai_name,
179 codec_dai->sysclk);
180
181 asoc_simple_card_canonicalize_cpu(dai_link,
182 card->num_links == 1);
183
184dai_link_of_err:
185 of_node_put(cpu_ep);
186 of_node_put(rcpu_ep);
187 of_node_put(codec_ep);
188
189 return ret;
190}
191
192static int asoc_graph_card_parse_of(struct graph_card_data *priv)
193{
194 struct of_phandle_iterator it;
195 struct device *dev = graph_priv_to_dev(priv);
196 struct snd_soc_card *card = graph_priv_to_card(priv);
197 struct device_node *node = dev->of_node;
198 int rc, idx = 0;
199 int ret;
200
201 /*
202 * we need to consider "widgets", "routing", "mclk-fs" around here
203 * see simple-card
204 */
205
206 of_for_each_phandle(&it, rc, node, "dais", NULL, 0) {
207 ret = asoc_graph_card_dai_link_of(it.node, priv, idx++);
208 of_node_put(it.node);
209 if (ret < 0)
210 return ret;
211 }
212
213 return asoc_simple_card_parse_card_name(card, NULL);
214}
215
216static int asoc_graph_get_dais_count(struct device *dev)
217{
218 struct of_phandle_iterator it;
219 struct device_node *node = dev->of_node;
220 int count = 0;
221 int rc;
222
223 of_for_each_phandle(&it, rc, node, "dais", NULL, 0) {
224 count++;
225 of_node_put(it.node);
226 }
227
228 return count;
229}
230
231static int asoc_graph_card_probe(struct platform_device *pdev)
232{
233 struct graph_card_data *priv;
234 struct snd_soc_dai_link *dai_link;
235 struct graph_dai_props *dai_props;
236 struct device *dev = &pdev->dev;
237 struct snd_soc_card *card;
238 int num, ret;
239
240 /* Allocate the private data and the DAI link array */
241 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
242 if (!priv)
243 return -ENOMEM;
244
245 num = asoc_graph_get_dais_count(dev);
246 if (num == 0)
247 return -EINVAL;
248
249 dai_props = devm_kzalloc(dev, sizeof(*dai_props) * num, GFP_KERNEL);
250 dai_link = devm_kzalloc(dev, sizeof(*dai_link) * num, GFP_KERNEL);
251 if (!dai_props || !dai_link)
252 return -ENOMEM;
253
254 priv->dai_props = dai_props;
255 priv->dai_link = dai_link;
256
257 /* Init snd_soc_card */
258 card = graph_priv_to_card(priv);
259 card->owner = THIS_MODULE;
260 card->dev = dev;
261 card->dai_link = dai_link;
262 card->num_links = num;
263
264 ret = asoc_graph_card_parse_of(priv);
265 if (ret < 0) {
266 if (ret != -EPROBE_DEFER)
267 dev_err(dev, "parse error %d\n", ret);
268 goto err;
269 }
270
271 snd_soc_card_set_drvdata(card, priv);
272
273 ret = devm_snd_soc_register_card(dev, card);
274 if (ret >= 0)
275 return ret;
276err:
277 asoc_simple_card_clean_reference(card);
278
279 return ret;
280}
281
282static int asoc_graph_card_remove(struct platform_device *pdev)
283{
284 struct snd_soc_card *card = platform_get_drvdata(pdev);
285
286 return asoc_simple_card_clean_reference(card);
287}
288
289static const struct of_device_id asoc_graph_of_match[] = {
290 { .compatible = "audio-graph-card", },
291 {},
292};
293MODULE_DEVICE_TABLE(of, asoc_graph_of_match);
294
295static struct platform_driver asoc_graph_card = {
296 .driver = {
297 .name = "asoc-audio-graph-card",
298 .of_match_table = asoc_graph_of_match,
299 },
300 .probe = asoc_graph_card_probe,
301 .remove = asoc_graph_card_remove,
302};
303module_platform_driver(asoc_graph_card);
304
305MODULE_ALIAS("platform:asoc-audio-graph-card");
306MODULE_LICENSE("GPL v2");
307MODULE_DESCRIPTION("ASoC Audio Graph Sound Card");
308MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");