aboutsummaryrefslogtreecommitdiffstats
path: root/sound
diff options
context:
space:
mode:
authorKuninori Morimoto <kuninori.morimoto.gx@renesas.com>2015-03-26 00:01:27 -0400
committerMark Brown <broonie@kernel.org>2015-03-27 18:58:20 -0400
commit415f1cb29d3be865b034b528058c7115bc262f43 (patch)
treec7a3bc6d6afb50daa7afaad03eabe863eea9807d /sound
parent2f4b1e6bb25899e7d21e1764abcfb23f14250535 (diff)
ASoC: rsrc-card: add Renesas sampling rate convert sound card support
Renesas sound card has "sampling rate convert" feature which should be implemented via DPCM. But, sound card driver point of view, it is difficult to add this DPCM feature on simple-card driver. Especially, DT binding support is very difficult. This patch implements DPCM feature on DT as Renesas specific sound card. This new driver is copied from current simple-card driver. Main difference between simple-card and this driver are... 1. removed unused feature from simple-card 2. removed driver named prefix from DT property 3. CPU will be FE, CODEC will be BE with snd-soc-dummy 4. it supports sampling rate convert via .be_hw_params_fixup 5. board specific routing is implemented in driver Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> Signed-off-by: Mark Brown <broonie@kernel.org>
Diffstat (limited to 'sound')
-rw-r--r--sound/soc/sh/Kconfig5
-rw-r--r--sound/soc/sh/rcar/Makefile5
-rw-r--r--sound/soc/sh/rcar/rsrc-card.c489
3 files changed, 498 insertions, 1 deletions
diff --git a/sound/soc/sh/Kconfig b/sound/soc/sh/Kconfig
index 80245b6eebd6..2b3030415573 100644
--- a/sound/soc/sh/Kconfig
+++ b/sound/soc/sh/Kconfig
@@ -41,6 +41,11 @@ config SND_SOC_RCAR
41 help 41 help
42 This option enables R-Car SUR/SCU/SSIU/SSI sound support 42 This option enables R-Car SUR/SCU/SSIU/SSI sound support
43 43
44config SND_SOC_RSRC_CARD
45 tristate "Renesas Sampling Rate Convert Sound Card"
46 help
47 This option enables simple sound if you need sampling rate convert
48
44## 49##
45## Boards 50## Boards
46## 51##
diff --git a/sound/soc/sh/rcar/Makefile b/sound/soc/sh/rcar/Makefile
index 7b204925b8c5..f1b445173fba 100644
--- a/sound/soc/sh/rcar/Makefile
+++ b/sound/soc/sh/rcar/Makefile
@@ -1,2 +1,5 @@
1snd-soc-rcar-objs := core.o gen.o dma.o src.o adg.o ssi.o dvc.o 1snd-soc-rcar-objs := core.o gen.o dma.o src.o adg.o ssi.o dvc.o
2obj-$(CONFIG_SND_SOC_RCAR) += snd-soc-rcar.o \ No newline at end of file 2obj-$(CONFIG_SND_SOC_RCAR) += snd-soc-rcar.o
3
4snd-soc-rsrc-card-objs := rsrc-card.o
5obj-$(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
new file mode 100644
index 000000000000..3baeab726bc3
--- /dev/null
+++ b/sound/soc/sh/rcar/rsrc-card.c
@@ -0,0 +1,489 @@
1/*
2 * Renesas Sampling Rate Convert Sound Card for DPCM
3 *
4 * Copyright (C) 2015 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/module.h>
16#include <linux/of.h>
17#include <linux/of_device.h>
18#include <linux/platform_device.h>
19#include <linux/string.h>
20#include <sound/jack.h>
21#include <sound/soc.h>
22#include <sound/soc-dai.h>
23
24struct rsrc_card_of_data {
25 const char *prefix;
26 const struct snd_soc_dapm_route *routes;
27 int num_routes;
28};
29
30static const struct snd_soc_dapm_route routes_ssi0_ak4642[] = {
31 {"ak4642 Playback", NULL, "DAI0 Playback"},
32 {"DAI0 Capture", NULL, "ak4642 Capture"},
33};
34
35static const struct rsrc_card_of_data routes_of_ssi0_ak4642 = {
36 .prefix = "ak4642",
37 .routes = routes_ssi0_ak4642,
38 .num_routes = ARRAY_SIZE(routes_ssi0_ak4642),
39};
40
41static const struct of_device_id rsrc_card_of_match[] = {
42 { .compatible = "renesas,rsrc-card,lager", .data = &routes_of_ssi0_ak4642 },
43 { .compatible = "renesas,rsrc-card,koelsch", .data = &routes_of_ssi0_ak4642 },
44 {},
45};
46MODULE_DEVICE_TABLE(of, rsrc_card_of_match);
47
48struct rsrc_card_dai {
49 const char *name;
50 unsigned int fmt;
51 unsigned int sysclk;
52 struct clk *clk;
53};
54
55#define RSRC_FB_NUM 2 /* FE/BE */
56#define IDX_CPU 0
57#define IDX_CODEC 1
58struct rsrc_card_priv {
59 struct snd_soc_card snd_card;
60 struct rsrc_card_dai_props {
61 struct rsrc_card_dai cpu_dai;
62 struct rsrc_card_dai codec_dai;
63 } dai_props[RSRC_FB_NUM];
64 struct snd_soc_codec_conf codec_conf;
65 struct snd_soc_dai_link dai_link[RSRC_FB_NUM];
66};
67
68#define rsrc_priv_to_dev(priv) ((priv)->snd_card.dev)
69#define rsrc_priv_to_link(priv, i) ((priv)->snd_card.dai_link + i)
70#define rsrc_priv_to_props(priv, i) ((priv)->dai_props + i)
71#define rsrc_dev_to_of_data(dev) (of_match_device(rsrc_card_of_match, (dev))->data)
72
73static int rsrc_card_startup(struct snd_pcm_substream *substream)
74{
75 struct snd_soc_pcm_runtime *rtd = substream->private_data;
76 struct rsrc_card_priv *priv = snd_soc_card_get_drvdata(rtd->card);
77 struct rsrc_card_dai_props *dai_props =
78 &priv->dai_props[rtd - rtd->card->rtd];
79 int ret;
80
81 ret = clk_prepare_enable(dai_props->cpu_dai.clk);
82 if (ret)
83 return ret;
84
85 ret = clk_prepare_enable(dai_props->codec_dai.clk);
86 if (ret)
87 clk_disable_unprepare(dai_props->cpu_dai.clk);
88
89 return ret;
90}
91
92static void rsrc_card_shutdown(struct snd_pcm_substream *substream)
93{
94 struct snd_soc_pcm_runtime *rtd = substream->private_data;
95 struct rsrc_card_priv *priv = snd_soc_card_get_drvdata(rtd->card);
96 struct rsrc_card_dai_props *dai_props =
97 &priv->dai_props[rtd - rtd->card->rtd];
98
99 clk_disable_unprepare(dai_props->cpu_dai.clk);
100
101 clk_disable_unprepare(dai_props->codec_dai.clk);
102}
103
104static struct snd_soc_ops rsrc_card_ops = {
105 .startup = rsrc_card_startup,
106 .shutdown = rsrc_card_shutdown,
107};
108
109static int __rsrc_card_dai_init(struct snd_soc_dai *dai,
110 struct rsrc_card_dai *set)
111{
112 int ret;
113
114 if (set->fmt) {
115 ret = snd_soc_dai_set_fmt(dai, set->fmt);
116 if (ret && ret != -ENOTSUPP) {
117 dev_err(dai->dev, "set_fmt error\n");
118 goto err;
119 }
120 }
121
122 if (set->sysclk) {
123 ret = snd_soc_dai_set_sysclk(dai, 0, set->sysclk, 0);
124 if (ret && ret != -ENOTSUPP) {
125 dev_err(dai->dev, "set_sysclk error\n");
126 goto err;
127 }
128 }
129
130 ret = 0;
131
132err:
133 return ret;
134}
135
136static int rsrc_card_dai_init(struct snd_soc_pcm_runtime *rtd)
137{
138 struct rsrc_card_priv *priv = snd_soc_card_get_drvdata(rtd->card);
139 struct snd_soc_dai *codec = rtd->codec_dai;
140 struct snd_soc_dai *cpu = rtd->cpu_dai;
141 struct rsrc_card_dai_props *dai_props;
142 int num, ret;
143
144 num = rtd - rtd->card->rtd;
145 dai_props = &priv->dai_props[num];
146 ret = __rsrc_card_dai_init(codec, &dai_props->codec_dai);
147 if (ret < 0)
148 return ret;
149
150 ret = __rsrc_card_dai_init(cpu, &dai_props->cpu_dai);
151 if (ret < 0)
152 return ret;
153
154 return 0;
155}
156
157static int
158rsrc_card_sub_parse_of(struct rsrc_card_priv *priv,
159 struct device_node *np,
160 struct rsrc_card_dai *dai,
161 struct snd_soc_dai_link *dai_link,
162 int *args_count)
163{
164 struct device *dev = rsrc_priv_to_dev(priv);
165 const struct rsrc_card_of_data *of_data = rsrc_dev_to_of_data(dev);
166 struct of_phandle_args args;
167 struct device_node **p_node;
168 struct clk *clk;
169 const char **dai_name;
170 const char **name;
171 u32 val;
172 int ret;
173
174 if (args_count) {
175 p_node = &dai_link->cpu_of_node;
176 dai_name = &dai_link->cpu_dai_name;
177 name = &dai_link->cpu_name;
178 } else {
179 p_node = &dai_link->codec_of_node;
180 dai_name = &dai_link->codec_dai_name;
181 name = &dai_link->codec_name;
182 }
183
184 if (!np) {
185 /* use snd-soc-dummy */
186 *p_node = NULL;
187 *dai_name = "snd-soc-dummy-dai";
188 *name = "snd-soc-dummy";
189 return 0;
190 }
191
192 /*
193 * Get node via "sound-dai = <&phandle port>"
194 * it will be used as xxx_of_node on soc_bind_dai_link()
195 */
196 ret = of_parse_phandle_with_args(np, "sound-dai",
197 "#sound-dai-cells", 0, &args);
198 if (ret)
199 return ret;
200
201 *p_node = args.np;
202
203 /* Get dai->name */
204 ret = snd_soc_of_get_dai_name(np, dai_name);
205 if (ret < 0)
206 return ret;
207
208 /*
209 * FIXME
210 *
211 * rsrc assumes DPCM playback/capture
212 */
213 dai_link->dpcm_playback = 1;
214 dai_link->dpcm_capture = 1;
215
216 if (args_count) {
217 *args_count = args.args_count;
218 dai_link->dynamic = 1;
219 } else {
220 dai_link->no_pcm = 1;
221 priv->codec_conf.of_node = (*p_node);
222 priv->codec_conf.name_prefix = of_data->prefix;
223 }
224
225 /*
226 * Parse dai->sysclk come from "clocks = <&xxx>"
227 * (if system has common clock)
228 * or "system-clock-frequency = <xxx>"
229 * or device's module clock.
230 */
231 if (of_property_read_bool(np, "clocks")) {
232 clk = of_clk_get(np, 0);
233 if (IS_ERR(clk)) {
234 ret = PTR_ERR(clk);
235 return ret;
236 }
237
238 dai->sysclk = clk_get_rate(clk);
239 dai->clk = clk;
240 } else if (!of_property_read_u32(np, "system-clock-frequency", &val)) {
241 dai->sysclk = val;
242 } else {
243 clk = of_clk_get(args.np, 0);
244 if (!IS_ERR(clk))
245 dai->sysclk = clk_get_rate(clk);
246 }
247
248 return 0;
249}
250
251static int rsrc_card_parse_daifmt(struct device_node *node,
252 struct rsrc_card_priv *priv,
253 struct device_node *codec,
254 int idx)
255{
256 struct device_node *bitclkmaster = NULL;
257 struct device_node *framemaster = NULL;
258 struct rsrc_card_dai_props *dai_props = rsrc_priv_to_props(priv, idx);
259 struct rsrc_card_dai *cpu_dai = &dai_props->cpu_dai;
260 struct rsrc_card_dai *codec_dai = &dai_props->codec_dai;
261 unsigned int daifmt;
262
263 daifmt = snd_soc_of_parse_daifmt(node, NULL,
264 &bitclkmaster, &framemaster);
265 daifmt &= ~SND_SOC_DAIFMT_MASTER_MASK;
266
267 if (!bitclkmaster && !framemaster)
268 return -EINVAL;
269
270 if (codec == bitclkmaster)
271 daifmt |= (codec == framemaster) ?
272 SND_SOC_DAIFMT_CBM_CFM : SND_SOC_DAIFMT_CBM_CFS;
273 else
274 daifmt |= (codec == framemaster) ?
275 SND_SOC_DAIFMT_CBS_CFM : SND_SOC_DAIFMT_CBS_CFS;
276
277 cpu_dai->fmt = daifmt;
278 codec_dai->fmt = daifmt;
279
280 of_node_put(bitclkmaster);
281 of_node_put(framemaster);
282
283 return 0;
284}
285
286static int rsrc_card_dai_link_of(struct device_node *node,
287 struct rsrc_card_priv *priv,
288 int idx)
289{
290 struct device *dev = rsrc_priv_to_dev(priv);
291 struct snd_soc_dai_link *dai_link = rsrc_priv_to_link(priv, idx);
292 struct rsrc_card_dai_props *dai_props = rsrc_priv_to_props(priv, idx);
293 struct device_node *cpu = NULL;
294 struct device_node *codec = NULL;
295 char *name;
296 char prop[128];
297 int ret, cpu_args;
298
299 cpu = of_get_child_by_name(node, "cpu");
300 codec = of_get_child_by_name(node, "codec");
301
302 if (!cpu || !codec) {
303 ret = -EINVAL;
304 dev_err(dev, "%s: Can't find %s DT node\n", __func__, prop);
305 goto dai_link_of_err;
306 }
307
308 ret = rsrc_card_parse_daifmt(node, priv, codec, idx);
309 if (ret < 0)
310 goto dai_link_of_err;
311
312 ret = rsrc_card_sub_parse_of(priv, (idx == IDX_CPU) ? cpu : NULL,
313 &dai_props->cpu_dai,
314 dai_link,
315 &cpu_args);
316 if (ret < 0)
317 goto dai_link_of_err;
318
319 ret = rsrc_card_sub_parse_of(priv, (idx == IDX_CODEC) ? codec : NULL,
320 &dai_props->codec_dai,
321 dai_link,
322 NULL);
323 if (ret < 0)
324 goto dai_link_of_err;
325
326 if (!dai_link->cpu_dai_name || !dai_link->codec_dai_name) {
327 ret = -EINVAL;
328 goto dai_link_of_err;
329 }
330
331 /* Simple Card assumes platform == cpu */
332 dai_link->platform_of_node = dai_link->cpu_of_node;
333
334 /* DAI link name is created from CPU/CODEC dai name */
335 name = devm_kzalloc(dev,
336 strlen(dai_link->cpu_dai_name) +
337 strlen(dai_link->codec_dai_name) + 2,
338 GFP_KERNEL);
339 if (!name) {
340 ret = -ENOMEM;
341 goto dai_link_of_err;
342 }
343
344 sprintf(name, "%s-%s", dai_link->cpu_dai_name,
345 dai_link->codec_dai_name);
346 dai_link->name = dai_link->stream_name = name;
347 dai_link->ops = &rsrc_card_ops;
348 dai_link->init = rsrc_card_dai_init;
349
350 dev_dbg(dev, "\tname : %s\n", dai_link->stream_name);
351 dev_dbg(dev, "\tcpu : %s / %04x / %d\n",
352 dai_link->cpu_dai_name,
353 dai_props->cpu_dai.fmt,
354 dai_props->cpu_dai.sysclk);
355 dev_dbg(dev, "\tcodec : %s / %04x / %d\n",
356 dai_link->codec_dai_name,
357 dai_props->codec_dai.fmt,
358 dai_props->codec_dai.sysclk);
359
360 /*
361 * In soc_bind_dai_link() will check cpu name after
362 * of_node matching if dai_link has cpu_dai_name.
363 * but, it will never match if name was created by
364 * fmt_single_name() remove cpu_dai_name if cpu_args
365 * was 0. See:
366 * fmt_single_name()
367 * fmt_multiple_name()
368 */
369 if (!cpu_args)
370 dai_link->cpu_dai_name = NULL;
371
372dai_link_of_err:
373 of_node_put(cpu);
374 of_node_put(codec);
375
376 return ret;
377}
378
379static int rsrc_card_parse_of(struct device_node *node,
380 struct rsrc_card_priv *priv)
381{
382 struct device *dev = rsrc_priv_to_dev(priv);
383 const struct rsrc_card_of_data *of_data = rsrc_dev_to_of_data(dev);
384 int ret;
385 int i;
386
387 if (!node)
388 return -EINVAL;
389
390 /* Parse the card name from DT */
391 snd_soc_of_parse_card_name(&priv->snd_card, "card-name");
392
393 /* DAPM routes */
394 priv->snd_card.of_dapm_routes = of_data->routes;
395 priv->snd_card.num_of_dapm_routes = of_data->num_routes;
396
397 dev_dbg(dev, "New rsrc-audio-card: %s\n", priv->snd_card.name ?
398 priv->snd_card.name : "");
399
400 /* FE/BE */
401 for (i = 0; i < RSRC_FB_NUM; i++) {
402 ret = rsrc_card_dai_link_of(node, priv, i);
403 if (ret < 0)
404 return ret;
405 }
406
407 if (!priv->snd_card.name)
408 priv->snd_card.name = priv->snd_card.dai_link->name;
409
410 return 0;
411}
412
413/* Decrease the reference count of the device nodes */
414static int rsrc_card_unref(struct snd_soc_card *card)
415{
416 struct snd_soc_dai_link *dai_link;
417 int num_links;
418
419 for (num_links = 0, dai_link = card->dai_link;
420 num_links < card->num_links;
421 num_links++, dai_link++) {
422 of_node_put(dai_link->cpu_of_node);
423 of_node_put(dai_link->codec_of_node);
424 }
425 return 0;
426}
427
428static int rsrc_card_probe(struct platform_device *pdev)
429{
430 struct rsrc_card_priv *priv;
431 struct snd_soc_dai_link *dai_link;
432 struct device_node *np = pdev->dev.of_node;
433 struct device *dev = &pdev->dev;
434 int ret;
435
436 /* Allocate the private data */
437 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
438 if (!priv)
439 return -ENOMEM;
440
441 /* Init snd_soc_card */
442 priv->snd_card.owner = THIS_MODULE;
443 priv->snd_card.dev = dev;
444 dai_link = priv->dai_link;
445 priv->snd_card.dai_link = dai_link;
446 priv->snd_card.num_links = RSRC_FB_NUM;
447 priv->snd_card.codec_conf = &priv->codec_conf;
448 priv->snd_card.num_configs = 1;
449
450 ret = rsrc_card_parse_of(np, priv);
451 if (ret < 0) {
452 if (ret != -EPROBE_DEFER)
453 dev_err(dev, "parse error %d\n", ret);
454 goto err;
455 }
456
457 snd_soc_card_set_drvdata(&priv->snd_card, priv);
458
459 ret = devm_snd_soc_register_card(&pdev->dev, &priv->snd_card);
460 if (ret >= 0)
461 return ret;
462err:
463 rsrc_card_unref(&priv->snd_card);
464
465 return ret;
466}
467
468static int rsrc_card_remove(struct platform_device *pdev)
469{
470 struct snd_soc_card *card = platform_get_drvdata(pdev);
471
472 return rsrc_card_unref(card);
473}
474
475static struct platform_driver rsrc_card = {
476 .driver = {
477 .name = "renesas-src-audio-card",
478 .of_match_table = rsrc_card_of_match,
479 },
480 .probe = rsrc_card_probe,
481 .remove = rsrc_card_remove,
482};
483
484module_platform_driver(rsrc_card);
485
486MODULE_ALIAS("platform:renesas-src-audio-card");
487MODULE_LICENSE("GPL");
488MODULE_DESCRIPTION("Renesas Sampling Rate Convert Sound Card");
489MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");