diff options
-rw-r--r-- | sound/soc/samsung/Kconfig | 8 | ||||
-rw-r--r-- | sound/soc/samsung/Makefile | 2 | ||||
-rw-r--r-- | sound/soc/samsung/bells.c | 346 |
3 files changed, 356 insertions, 0 deletions
diff --git a/sound/soc/samsung/Kconfig b/sound/soc/samsung/Kconfig index fe3995ce9b38..fb5600083612 100644 --- a/sound/soc/samsung/Kconfig +++ b/sound/soc/samsung/Kconfig | |||
@@ -199,6 +199,14 @@ config SND_SOC_TOBERMORY | |||
199 | select SND_SAMSUNG_I2S | 199 | select SND_SAMSUNG_I2S |
200 | select SND_SOC_WM8962 | 200 | select SND_SOC_WM8962 |
201 | 201 | ||
202 | config SND_SOC_BELLS | ||
203 | tristate "Audio support for Wolfson Bells" | ||
204 | depends on SND_SOC_SAMSUNG && MACH_WLF_CRAGG_6410 | ||
205 | select SND_SAMSUNG_I2S | ||
206 | select SND_SOC_WM5102 | ||
207 | select SND_SOC_WM5110 | ||
208 | select SND_SOC_WM9081 | ||
209 | |||
202 | config SND_SOC_LOWLAND | 210 | config SND_SOC_LOWLAND |
203 | tristate "Audio support for Wolfson Lowland" | 211 | tristate "Audio support for Wolfson Lowland" |
204 | depends on SND_SOC_SAMSUNG && MACH_WLF_CRAGG_6410 | 212 | depends on SND_SOC_SAMSUNG && MACH_WLF_CRAGG_6410 |
diff --git a/sound/soc/samsung/Makefile b/sound/soc/samsung/Makefile index 9d03beb40c86..709f6059ad67 100644 --- a/sound/soc/samsung/Makefile +++ b/sound/soc/samsung/Makefile | |||
@@ -42,6 +42,7 @@ snd-soc-speyside-objs := speyside.o | |||
42 | snd-soc-tobermory-objs := tobermory.o | 42 | snd-soc-tobermory-objs := tobermory.o |
43 | snd-soc-lowland-objs := lowland.o | 43 | snd-soc-lowland-objs := lowland.o |
44 | snd-soc-littlemill-objs := littlemill.o | 44 | snd-soc-littlemill-objs := littlemill.o |
45 | snd-soc-bells-objs := bells.o | ||
45 | 46 | ||
46 | obj-$(CONFIG_SND_SOC_SAMSUNG_JIVE_WM8750) += snd-soc-jive-wm8750.o | 47 | obj-$(CONFIG_SND_SOC_SAMSUNG_JIVE_WM8750) += snd-soc-jive-wm8750.o |
47 | obj-$(CONFIG_SND_SOC_SAMSUNG_NEO1973_WM8753) += snd-soc-neo1973-wm8753.o | 48 | obj-$(CONFIG_SND_SOC_SAMSUNG_NEO1973_WM8753) += snd-soc-neo1973-wm8753.o |
@@ -65,3 +66,4 @@ obj-$(CONFIG_SND_SOC_SPEYSIDE) += snd-soc-speyside.o | |||
65 | obj-$(CONFIG_SND_SOC_TOBERMORY) += snd-soc-tobermory.o | 66 | obj-$(CONFIG_SND_SOC_TOBERMORY) += snd-soc-tobermory.o |
66 | obj-$(CONFIG_SND_SOC_LOWLAND) += snd-soc-lowland.o | 67 | obj-$(CONFIG_SND_SOC_LOWLAND) += snd-soc-lowland.o |
67 | obj-$(CONFIG_SND_SOC_LITTLEMILL) += snd-soc-littlemill.o | 68 | obj-$(CONFIG_SND_SOC_LITTLEMILL) += snd-soc-littlemill.o |
69 | obj-$(CONFIG_SND_SOC_BELLS) += snd-soc-bells.o | ||
diff --git a/sound/soc/samsung/bells.c b/sound/soc/samsung/bells.c new file mode 100644 index 000000000000..5dc10dfc0d42 --- /dev/null +++ b/sound/soc/samsung/bells.c | |||
@@ -0,0 +1,346 @@ | |||
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 | /* | ||
22 | * 44.1kHz based clocks for the SYSCLK domain, use a very high clock | ||
23 | * to allow all the DSP functionality to be enabled if desired. | ||
24 | */ | ||
25 | #define SYSCLK_RATE (44100 * 1024) | ||
26 | |||
27 | /* 48kHz based clocks for the ASYNC domain */ | ||
28 | #define ASYNCCLK_RATE (48000 * 512) | ||
29 | |||
30 | /* BCLK2 is fixed at this currently */ | ||
31 | #define BCLK2_RATE (64 * 8000) | ||
32 | |||
33 | /* | ||
34 | * Expect a 24.576MHz crystal if one is fitted (the driver will function | ||
35 | * if this is not fitted). | ||
36 | */ | ||
37 | #define MCLK_RATE 24576000 | ||
38 | |||
39 | #define WM9081_AUDIO_RATE 44100 | ||
40 | #define WM9081_MCLK_RATE (WM9081_AUDIO_RATE * 256) | ||
41 | |||
42 | static int bells_set_bias_level(struct snd_soc_card *card, | ||
43 | struct snd_soc_dapm_context *dapm, | ||
44 | enum snd_soc_bias_level level) | ||
45 | { | ||
46 | struct snd_soc_dai *codec_dai = card->rtd[0].codec_dai; | ||
47 | struct snd_soc_codec *codec = codec_dai->codec; | ||
48 | int ret; | ||
49 | |||
50 | if (dapm->dev != codec_dai->dev) | ||
51 | return 0; | ||
52 | |||
53 | switch (level) { | ||
54 | case SND_SOC_BIAS_PREPARE: | ||
55 | if (dapm->bias_level == SND_SOC_BIAS_STANDBY) { | ||
56 | ret = snd_soc_codec_set_pll(codec, WM5102_FLL1, | ||
57 | ARIZONA_FLL_SRC_MCLK1, | ||
58 | MCLK_RATE, | ||
59 | SYSCLK_RATE); | ||
60 | if (ret < 0) | ||
61 | pr_err("Failed to start FLL: %d\n", ret); | ||
62 | |||
63 | ret = snd_soc_codec_set_pll(codec, WM5102_FLL2, | ||
64 | ARIZONA_FLL_SRC_AIF2BCLK, | ||
65 | BCLK2_RATE, | ||
66 | ASYNCCLK_RATE); | ||
67 | if (ret < 0) | ||
68 | pr_err("Failed to start FLL: %d\n", ret); | ||
69 | } | ||
70 | break; | ||
71 | |||
72 | default: | ||
73 | break; | ||
74 | } | ||
75 | |||
76 | return 0; | ||
77 | } | ||
78 | |||
79 | static int bells_set_bias_level_post(struct snd_soc_card *card, | ||
80 | struct snd_soc_dapm_context *dapm, | ||
81 | enum snd_soc_bias_level level) | ||
82 | { | ||
83 | struct snd_soc_dai *codec_dai = card->rtd[0].codec_dai; | ||
84 | struct snd_soc_codec *codec = codec_dai->codec; | ||
85 | int ret; | ||
86 | |||
87 | if (dapm->dev != codec_dai->dev) | ||
88 | return 0; | ||
89 | |||
90 | switch (level) { | ||
91 | case SND_SOC_BIAS_STANDBY: | ||
92 | ret = snd_soc_codec_set_pll(codec, WM5102_FLL1, 0, 0, 0); | ||
93 | if (ret < 0) { | ||
94 | pr_err("Failed to stop FLL: %d\n", ret); | ||
95 | return ret; | ||
96 | } | ||
97 | |||
98 | ret = snd_soc_codec_set_pll(codec, WM5102_FLL2, 0, 0, 0); | ||
99 | if (ret < 0) { | ||
100 | pr_err("Failed to stop FLL: %d\n", ret); | ||
101 | return ret; | ||
102 | } | ||
103 | break; | ||
104 | |||
105 | default: | ||
106 | break; | ||
107 | } | ||
108 | |||
109 | dapm->bias_level = level; | ||
110 | |||
111 | return 0; | ||
112 | } | ||
113 | |||
114 | static int bells_late_probe(struct snd_soc_card *card) | ||
115 | { | ||
116 | struct snd_soc_codec *codec = card->rtd[0].codec; | ||
117 | struct snd_soc_dai *aif1_dai = card->rtd[0].codec_dai; | ||
118 | struct snd_soc_dai *aif2_dai = card->rtd[1].cpu_dai; | ||
119 | struct snd_soc_dai *aif3_dai = card->rtd[2].cpu_dai; | ||
120 | struct snd_soc_dai *wm9081_dai = card->rtd[2].codec_dai; | ||
121 | int ret; | ||
122 | |||
123 | ret = snd_soc_dai_set_sysclk(aif1_dai, ARIZONA_CLK_SYSCLK, 0, 0); | ||
124 | if (ret != 0) { | ||
125 | dev_err(aif1_dai->dev, "Failed to set AIF1 clock: %d\n", ret); | ||
126 | return ret; | ||
127 | } | ||
128 | |||
129 | ret = snd_soc_dai_set_sysclk(aif2_dai, ARIZONA_CLK_ASYNCCLK, 0, 0); | ||
130 | if (ret != 0) { | ||
131 | dev_err(aif2_dai->dev, "Failed to set AIF2 clock: %d\n", ret); | ||
132 | return ret; | ||
133 | } | ||
134 | |||
135 | ret = snd_soc_dai_set_sysclk(aif3_dai, ARIZONA_CLK_SYSCLK, 0, 0); | ||
136 | if (ret != 0) { | ||
137 | dev_err(aif1_dai->dev, "Failed to set AIF1 clock: %d\n", ret); | ||
138 | return ret; | ||
139 | } | ||
140 | |||
141 | ret = snd_soc_codec_set_sysclk(codec, ARIZONA_CLK_SYSCLK, | ||
142 | ARIZONA_CLK_SRC_FLL1, SYSCLK_RATE, | ||
143 | SND_SOC_CLOCK_IN); | ||
144 | if (ret != 0) { | ||
145 | dev_err(codec->dev, "Failed to set SYSCLK: %d\n", ret); | ||
146 | return ret; | ||
147 | } | ||
148 | |||
149 | ret = snd_soc_codec_set_sysclk(codec, ARIZONA_CLK_OPCLK, 0, | ||
150 | WM9081_MCLK_RATE, SND_SOC_CLOCK_OUT); | ||
151 | if (ret != 0) { | ||
152 | dev_err(codec->dev, "Failed to set OPCLK: %d\n", ret); | ||
153 | return ret; | ||
154 | } | ||
155 | |||
156 | ret = snd_soc_codec_set_sysclk(codec, ARIZONA_CLK_ASYNCCLK, | ||
157 | ARIZONA_CLK_SRC_FLL2, ASYNCCLK_RATE, | ||
158 | SND_SOC_CLOCK_IN); | ||
159 | if (ret != 0) { | ||
160 | dev_err(codec->dev, "Failed to set SYSCLK: %d\n", ret); | ||
161 | return ret; | ||
162 | } | ||
163 | |||
164 | ret = snd_soc_codec_set_sysclk(wm9081_dai->codec, WM9081_SYSCLK_MCLK, | ||
165 | 0, WM9081_MCLK_RATE, 0); | ||
166 | if (ret != 0) { | ||
167 | dev_err(wm9081_dai->dev, "Failed to set MCLK: %d\n", ret); | ||
168 | return ret; | ||
169 | } | ||
170 | |||
171 | return 0; | ||
172 | } | ||
173 | |||
174 | static const struct snd_soc_pcm_stream baseband_params = { | ||
175 | .formats = SNDRV_PCM_FMTBIT_S32_LE, | ||
176 | .rate_min = 8000, | ||
177 | .rate_max = 8000, | ||
178 | .channels_min = 2, | ||
179 | .channels_max = 2, | ||
180 | }; | ||
181 | |||
182 | static const struct snd_soc_pcm_stream sub_params = { | ||
183 | .formats = SNDRV_PCM_FMTBIT_S32_LE, | ||
184 | .rate_min = WM9081_AUDIO_RATE, | ||
185 | .rate_max = WM9081_AUDIO_RATE, | ||
186 | .channels_min = 2, | ||
187 | .channels_max = 2, | ||
188 | }; | ||
189 | |||
190 | static struct snd_soc_dai_link bells_dai_wm5102[] = { | ||
191 | { | ||
192 | .name = "CPU", | ||
193 | .stream_name = "CPU", | ||
194 | .cpu_dai_name = "samsung-i2s.0", | ||
195 | .codec_dai_name = "wm5102-aif1", | ||
196 | .platform_name = "samsung-audio", | ||
197 | .codec_name = "wm5102-codec", | ||
198 | .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | ||
199 | | SND_SOC_DAIFMT_CBM_CFM, | ||
200 | }, | ||
201 | { | ||
202 | .name = "Baseband", | ||
203 | .stream_name = "Baseband", | ||
204 | .cpu_dai_name = "wm5102-aif2", | ||
205 | .codec_dai_name = "wm1250-ev1", | ||
206 | .codec_name = "wm1250-ev1.1-0027", | ||
207 | .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | ||
208 | | SND_SOC_DAIFMT_CBM_CFM, | ||
209 | .ignore_suspend = 1, | ||
210 | .params = &baseband_params, | ||
211 | }, | ||
212 | { | ||
213 | .name = "Sub", | ||
214 | .stream_name = "Sub", | ||
215 | .cpu_dai_name = "wm5102-aif3", | ||
216 | .codec_dai_name = "wm9081-hifi", | ||
217 | .codec_name = "wm9081.1-006c", | ||
218 | .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | ||
219 | | SND_SOC_DAIFMT_CBS_CFS, | ||
220 | .ignore_suspend = 1, | ||
221 | .params = &sub_params, | ||
222 | }, | ||
223 | }; | ||
224 | |||
225 | static struct snd_soc_dai_link bells_dai_wm5110[] = { | ||
226 | { | ||
227 | .name = "CPU", | ||
228 | .stream_name = "CPU", | ||
229 | .cpu_dai_name = "samsung-i2s.0", | ||
230 | .codec_dai_name = "wm5110-aif1", | ||
231 | .platform_name = "samsung-audio", | ||
232 | .codec_name = "wm5110-codec", | ||
233 | .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | ||
234 | | SND_SOC_DAIFMT_CBM_CFM, | ||
235 | }, | ||
236 | { | ||
237 | .name = "Baseband", | ||
238 | .stream_name = "Baseband", | ||
239 | .cpu_dai_name = "wm5110-aif2", | ||
240 | .codec_dai_name = "wm1250-ev1", | ||
241 | .codec_name = "wm1250-ev1.1-0027", | ||
242 | .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | ||
243 | | SND_SOC_DAIFMT_CBM_CFM, | ||
244 | .ignore_suspend = 1, | ||
245 | .params = &baseband_params, | ||
246 | }, | ||
247 | { | ||
248 | .name = "Sub", | ||
249 | .stream_name = "Sub", | ||
250 | .cpu_dai_name = "wm5102-aif3", | ||
251 | .codec_dai_name = "wm9081-hifi", | ||
252 | .codec_name = "wm9081.1-006c", | ||
253 | .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | ||
254 | | SND_SOC_DAIFMT_CBS_CFS, | ||
255 | .ignore_suspend = 1, | ||
256 | .params = &sub_params, | ||
257 | }, | ||
258 | }; | ||
259 | |||
260 | static struct snd_soc_codec_conf bells_codec_conf[] = { | ||
261 | { | ||
262 | .dev_name = "wm9081.1-006c", | ||
263 | .name_prefix = "Sub", | ||
264 | }, | ||
265 | }; | ||
266 | |||
267 | static struct snd_soc_dapm_route bells_routes[] = { | ||
268 | { "Sub CLK_SYS", NULL, "OPCLK" }, | ||
269 | }; | ||
270 | |||
271 | static struct snd_soc_card bells_cards[] = { | ||
272 | { | ||
273 | .name = "Bells WM5102", | ||
274 | .owner = THIS_MODULE, | ||
275 | .dai_link = bells_dai_wm5102, | ||
276 | .num_links = ARRAY_SIZE(bells_dai_wm5102), | ||
277 | .codec_conf = bells_codec_conf, | ||
278 | .num_configs = ARRAY_SIZE(bells_codec_conf), | ||
279 | |||
280 | .late_probe = bells_late_probe, | ||
281 | |||
282 | .dapm_routes = bells_routes, | ||
283 | .num_dapm_routes = ARRAY_SIZE(bells_routes), | ||
284 | |||
285 | .set_bias_level = bells_set_bias_level, | ||
286 | .set_bias_level_post = bells_set_bias_level_post, | ||
287 | }, | ||
288 | { | ||
289 | .name = "Bells WM5110", | ||
290 | .owner = THIS_MODULE, | ||
291 | .dai_link = bells_dai_wm5110, | ||
292 | .num_links = ARRAY_SIZE(bells_dai_wm5110), | ||
293 | .codec_conf = bells_codec_conf, | ||
294 | .num_configs = ARRAY_SIZE(bells_codec_conf), | ||
295 | |||
296 | .late_probe = bells_late_probe, | ||
297 | |||
298 | .dapm_routes = bells_routes, | ||
299 | .num_dapm_routes = ARRAY_SIZE(bells_routes), | ||
300 | |||
301 | .set_bias_level = bells_set_bias_level, | ||
302 | .set_bias_level_post = bells_set_bias_level_post, | ||
303 | }, | ||
304 | }; | ||
305 | |||
306 | |||
307 | static __devinit int bells_probe(struct platform_device *pdev) | ||
308 | { | ||
309 | int ret; | ||
310 | |||
311 | bells_cards[pdev->id].dev = &pdev->dev; | ||
312 | |||
313 | ret = snd_soc_register_card(&bells_cards[pdev->id]); | ||
314 | if (ret) { | ||
315 | dev_err(&pdev->dev, | ||
316 | "snd_soc_register_card(%s) failed: %d\n", | ||
317 | bells_cards[pdev->id].name, ret); | ||
318 | return ret; | ||
319 | } | ||
320 | |||
321 | return 0; | ||
322 | } | ||
323 | |||
324 | static int __devexit bells_remove(struct platform_device *pdev) | ||
325 | { | ||
326 | snd_soc_unregister_card(&bells_cards[pdev->id]); | ||
327 | |||
328 | return 0; | ||
329 | } | ||
330 | |||
331 | static struct platform_driver bells_driver = { | ||
332 | .driver = { | ||
333 | .name = "bells", | ||
334 | .owner = THIS_MODULE, | ||
335 | .pm = &snd_soc_pm_ops, | ||
336 | }, | ||
337 | .probe = bells_probe, | ||
338 | .remove = __devexit_p(bells_remove), | ||
339 | }; | ||
340 | |||
341 | module_platform_driver(bells_driver); | ||
342 | |||
343 | MODULE_DESCRIPTION("Bells audio support"); | ||
344 | MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); | ||
345 | MODULE_LICENSE("GPL"); | ||
346 | MODULE_ALIAS("platform:bells"); | ||