ASoC: bells: Provide additional parameterisation
[deliverable/linux.git] / sound / soc / samsung / bells.c
1 /*
2 * Bells audio support
3 *
4 * Copyright 2012 Wolfson Microelectronics
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2 of the License, or (at your
9 * option) any later version.
10 */
11
12 #include <sound/soc.h>
13 #include <sound/soc-dapm.h>
14 #include <sound/jack.h>
15 #include <linux/gpio.h>
16 #include <linux/module.h>
17
18 #include "../codecs/wm5102.h"
19 #include "../codecs/wm9081.h"
20
21 /* BCLK2 is fixed at this currently */
22 #define BCLK2_RATE (64 * 8000)
23
24 /*
25 * Expect a 24.576MHz crystal if one is fitted (the driver will function
26 * if this is not fitted).
27 */
28 #define MCLK_RATE 24576000
29
30 #define SYS_AUDIO_RATE 44100
31 #define SYS_MCLK_RATE (SYS_AUDIO_RATE * 256)
32
33 #define DAI_AP_DSP 0
34 #define DAI_DSP_CODEC 1
35 #define DAI_CODEC_CP 2
36 #define DAI_CODEC_SUB 3
37
38 struct bells_drvdata {
39 int sysclk_rate;
40 int asyncclk_rate;
41 };
42
43 static struct bells_drvdata wm5102_drvdata = {
44 .sysclk_rate = 45158400,
45 .asyncclk_rate = 49152000,
46 };
47
48 static struct bells_drvdata wm5110_drvdata = {
49 .sysclk_rate = 135475200,
50 .asyncclk_rate = 147456000,
51 };
52
53 static int bells_set_bias_level(struct snd_soc_card *card,
54 struct snd_soc_dapm_context *dapm,
55 enum snd_soc_bias_level level)
56 {
57 struct snd_soc_dai *codec_dai = card->rtd[DAI_DSP_CODEC].codec_dai;
58 struct snd_soc_codec *codec = codec_dai->codec;
59 struct bells_drvdata *bells = card->drvdata;
60 int ret;
61
62 if (dapm->dev != codec_dai->dev)
63 return 0;
64
65 switch (level) {
66 case SND_SOC_BIAS_PREPARE:
67 if (dapm->bias_level != SND_SOC_BIAS_STANDBY)
68 break;
69
70 ret = snd_soc_codec_set_pll(codec, WM5102_FLL1,
71 ARIZONA_FLL_SRC_MCLK1,
72 MCLK_RATE,
73 bells->sysclk_rate);
74 if (ret < 0)
75 pr_err("Failed to start FLL: %d\n", ret);
76
77 if (bells->asyncclk_rate) {
78 ret = snd_soc_codec_set_pll(codec, WM5102_FLL2,
79 ARIZONA_FLL_SRC_AIF2BCLK,
80 BCLK2_RATE,
81 bells->asyncclk_rate);
82 if (ret < 0)
83 pr_err("Failed to start FLL: %d\n", ret);
84 }
85 break;
86
87 default:
88 break;
89 }
90
91 return 0;
92 }
93
94 static int bells_set_bias_level_post(struct snd_soc_card *card,
95 struct snd_soc_dapm_context *dapm,
96 enum snd_soc_bias_level level)
97 {
98 struct snd_soc_dai *codec_dai = card->rtd[DAI_DSP_CODEC].codec_dai;
99 struct snd_soc_codec *codec = codec_dai->codec;
100 struct bells_drvdata *bells = card->drvdata;
101 int ret;
102
103 if (dapm->dev != codec_dai->dev)
104 return 0;
105
106 switch (level) {
107 case SND_SOC_BIAS_STANDBY:
108 ret = snd_soc_codec_set_pll(codec, WM5102_FLL1, 0, 0, 0);
109 if (ret < 0) {
110 pr_err("Failed to stop FLL: %d\n", ret);
111 return ret;
112 }
113
114 if (bells->asyncclk_rate) {
115 ret = snd_soc_codec_set_pll(codec, WM5102_FLL2,
116 0, 0, 0);
117 if (ret < 0) {
118 pr_err("Failed to stop FLL: %d\n", ret);
119 return ret;
120 }
121 }
122 break;
123
124 default:
125 break;
126 }
127
128 dapm->bias_level = level;
129
130 return 0;
131 }
132
133 static int bells_late_probe(struct snd_soc_card *card)
134 {
135 struct bells_drvdata *bells = card->drvdata;
136 struct snd_soc_codec *wm0010 = card->rtd[DAI_AP_DSP].codec;
137 struct snd_soc_codec *codec = card->rtd[DAI_DSP_CODEC].codec;
138 struct snd_soc_dai *aif1_dai = card->rtd[DAI_DSP_CODEC].codec_dai;
139 struct snd_soc_dai *aif2_dai;
140 struct snd_soc_dai *aif3_dai;
141 struct snd_soc_dai *wm9081_dai;
142 int ret;
143
144 ret = snd_soc_codec_set_sysclk(codec, ARIZONA_CLK_SYSCLK,
145 ARIZONA_CLK_SRC_FLL1,
146 bells->sysclk_rate,
147 SND_SOC_CLOCK_IN);
148 if (ret != 0) {
149 dev_err(codec->dev, "Failed to set SYSCLK: %d\n", ret);
150 return ret;
151 }
152
153 ret = snd_soc_codec_set_sysclk(wm0010, 0, 0, SYS_MCLK_RATE, 0);
154 if (ret != 0) {
155 dev_err(wm0010->dev, "Failed to set WM0010 clock: %d\n", ret);
156 return ret;
157 }
158
159 ret = snd_soc_dai_set_sysclk(aif1_dai, ARIZONA_CLK_SYSCLK, 0, 0);
160 if (ret != 0)
161 dev_err(aif1_dai->dev, "Failed to set AIF1 clock: %d\n", ret);
162
163 ret = snd_soc_codec_set_sysclk(codec, ARIZONA_CLK_OPCLK, 0,
164 SYS_MCLK_RATE, SND_SOC_CLOCK_OUT);
165 if (ret != 0)
166 dev_err(codec->dev, "Failed to set OPCLK: %d\n", ret);
167
168 if (card->num_rtd == DAI_CODEC_CP)
169 return 0;
170
171 ret = snd_soc_codec_set_sysclk(codec, ARIZONA_CLK_ASYNCCLK,
172 ARIZONA_CLK_SRC_FLL2,
173 bells->asyncclk_rate,
174 SND_SOC_CLOCK_IN);
175 if (ret != 0) {
176 dev_err(codec->dev, "Failed to set ASYNCCLK: %d\n", ret);
177 return ret;
178 }
179
180 aif2_dai = card->rtd[DAI_CODEC_CP].cpu_dai;
181
182 ret = snd_soc_dai_set_sysclk(aif2_dai, ARIZONA_CLK_ASYNCCLK, 0, 0);
183 if (ret != 0) {
184 dev_err(aif2_dai->dev, "Failed to set AIF2 clock: %d\n", ret);
185 return ret;
186 }
187
188 if (card->num_rtd == DAI_CODEC_SUB)
189 return 0;
190
191 aif3_dai = card->rtd[DAI_CODEC_SUB].cpu_dai;
192 wm9081_dai = card->rtd[DAI_CODEC_SUB].codec_dai;
193
194 ret = snd_soc_dai_set_sysclk(aif3_dai, ARIZONA_CLK_SYSCLK, 0, 0);
195 if (ret != 0) {
196 dev_err(aif1_dai->dev, "Failed to set AIF1 clock: %d\n", ret);
197 return ret;
198 }
199
200 ret = snd_soc_codec_set_sysclk(wm9081_dai->codec, WM9081_SYSCLK_MCLK,
201 0, SYS_MCLK_RATE, 0);
202 if (ret != 0) {
203 dev_err(wm9081_dai->dev, "Failed to set MCLK: %d\n", ret);
204 return ret;
205 }
206
207 return 0;
208 }
209
210 static const struct snd_soc_pcm_stream baseband_params = {
211 .formats = SNDRV_PCM_FMTBIT_S32_LE,
212 .rate_min = 8000,
213 .rate_max = 8000,
214 .channels_min = 2,
215 .channels_max = 2,
216 };
217
218 static const struct snd_soc_pcm_stream sub_params = {
219 .formats = SNDRV_PCM_FMTBIT_S32_LE,
220 .rate_min = SYS_AUDIO_RATE,
221 .rate_max = SYS_AUDIO_RATE,
222 .channels_min = 2,
223 .channels_max = 2,
224 };
225
226 static struct snd_soc_dai_link bells_dai_wm5102[] = {
227 {
228 .name = "CPU-DSP",
229 .stream_name = "CPU-DSP",
230 .cpu_dai_name = "samsung-i2s.0",
231 .codec_dai_name = "wm0010-sdi1",
232 .platform_name = "samsung-audio",
233 .codec_name = "spi0.0",
234 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
235 | SND_SOC_DAIFMT_CBM_CFM,
236 },
237 {
238 .name = "DSP-CODEC",
239 .stream_name = "DSP-CODEC",
240 .cpu_dai_name = "wm0010-sdi2",
241 .codec_dai_name = "wm5102-aif1",
242 .codec_name = "wm5102-codec",
243 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
244 | SND_SOC_DAIFMT_CBM_CFM,
245 .params = &sub_params,
246 .ignore_suspend = 1,
247 },
248 {
249 .name = "Baseband",
250 .stream_name = "Baseband",
251 .cpu_dai_name = "wm5102-aif2",
252 .codec_dai_name = "wm1250-ev1",
253 .codec_name = "wm1250-ev1.1-0027",
254 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
255 | SND_SOC_DAIFMT_CBM_CFM,
256 .ignore_suspend = 1,
257 .params = &baseband_params,
258 },
259 {
260 .name = "Sub",
261 .stream_name = "Sub",
262 .cpu_dai_name = "wm5102-aif3",
263 .codec_dai_name = "wm9081-hifi",
264 .codec_name = "wm9081.1-006c",
265 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
266 | SND_SOC_DAIFMT_CBS_CFS,
267 .ignore_suspend = 1,
268 .params = &sub_params,
269 },
270 };
271
272 static struct snd_soc_dai_link bells_dai_wm5110[] = {
273 {
274 .name = "CPU-DSP",
275 .stream_name = "CPU-DSP",
276 .cpu_dai_name = "samsung-i2s.0",
277 .codec_dai_name = "wm0010-sdi1",
278 .platform_name = "samsung-audio",
279 .codec_name = "spi0.0",
280 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
281 | SND_SOC_DAIFMT_CBM_CFM,
282 },
283 {
284 .name = "DSP-CODEC",
285 .stream_name = "DSP-CODEC",
286 .cpu_dai_name = "wm0010-sdi2",
287 .codec_dai_name = "wm5110-aif1",
288 .codec_name = "wm5110-codec",
289 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
290 | SND_SOC_DAIFMT_CBM_CFM,
291 .params = &sub_params,
292 .ignore_suspend = 1,
293 },
294 {
295 .name = "Baseband",
296 .stream_name = "Baseband",
297 .cpu_dai_name = "wm5110-aif2",
298 .codec_dai_name = "wm1250-ev1",
299 .codec_name = "wm1250-ev1.1-0027",
300 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
301 | SND_SOC_DAIFMT_CBM_CFM,
302 .ignore_suspend = 1,
303 .params = &baseband_params,
304 },
305 {
306 .name = "Sub",
307 .stream_name = "Sub",
308 .cpu_dai_name = "wm5110-aif3",
309 .codec_dai_name = "wm9081-hifi",
310 .codec_name = "wm9081.1-006c",
311 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
312 | SND_SOC_DAIFMT_CBS_CFS,
313 .ignore_suspend = 1,
314 .params = &sub_params,
315 },
316 };
317
318 static struct snd_soc_codec_conf bells_codec_conf[] = {
319 {
320 .dev_name = "wm9081.1-006c",
321 .name_prefix = "Sub",
322 },
323 };
324
325 static struct snd_soc_dapm_route bells_routes[] = {
326 { "Sub CLK_SYS", NULL, "OPCLK" },
327 };
328
329 static struct snd_soc_card bells_cards[] = {
330 {
331 .name = "Bells WM5102",
332 .owner = THIS_MODULE,
333 .dai_link = bells_dai_wm5102,
334 .num_links = ARRAY_SIZE(bells_dai_wm5102),
335 .codec_conf = bells_codec_conf,
336 .num_configs = ARRAY_SIZE(bells_codec_conf),
337
338 .late_probe = bells_late_probe,
339
340 .dapm_routes = bells_routes,
341 .num_dapm_routes = ARRAY_SIZE(bells_routes),
342
343 .set_bias_level = bells_set_bias_level,
344 .set_bias_level_post = bells_set_bias_level_post,
345
346 .drvdata = &wm5102_drvdata,
347 },
348 {
349 .name = "Bells WM5110",
350 .owner = THIS_MODULE,
351 .dai_link = bells_dai_wm5110,
352 .num_links = ARRAY_SIZE(bells_dai_wm5110),
353 .codec_conf = bells_codec_conf,
354 .num_configs = ARRAY_SIZE(bells_codec_conf),
355
356 .late_probe = bells_late_probe,
357
358 .dapm_routes = bells_routes,
359 .num_dapm_routes = ARRAY_SIZE(bells_routes),
360
361 .set_bias_level = bells_set_bias_level,
362 .set_bias_level_post = bells_set_bias_level_post,
363
364 .drvdata = &wm5110_drvdata,
365 },
366 };
367
368
369 static __devinit int bells_probe(struct platform_device *pdev)
370 {
371 int ret;
372
373 bells_cards[pdev->id].dev = &pdev->dev;
374
375 ret = snd_soc_register_card(&bells_cards[pdev->id]);
376 if (ret) {
377 dev_err(&pdev->dev,
378 "snd_soc_register_card(%s) failed: %d\n",
379 bells_cards[pdev->id].name, ret);
380 return ret;
381 }
382
383 return 0;
384 }
385
386 static int __devexit bells_remove(struct platform_device *pdev)
387 {
388 snd_soc_unregister_card(&bells_cards[pdev->id]);
389
390 return 0;
391 }
392
393 static struct platform_driver bells_driver = {
394 .driver = {
395 .name = "bells",
396 .owner = THIS_MODULE,
397 .pm = &snd_soc_pm_ops,
398 },
399 .probe = bells_probe,
400 .remove = __devexit_p(bells_remove),
401 };
402
403 module_platform_driver(bells_driver);
404
405 MODULE_DESCRIPTION("Bells audio support");
406 MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
407 MODULE_LICENSE("GPL");
408 MODULE_ALIAS("platform:bells");
This page took 0.07774 seconds and 5 git commands to generate.