aboutsummaryrefslogtreecommitdiffstats
path: root/sound/soc/s3c24xx
diff options
context:
space:
mode:
authorMark Brown <broonie@opensource.wolfsonmicro.com>2009-02-28 12:33:52 -0500
committerMark Brown <broonie@opensource.wolfsonmicro.com>2009-05-23 06:06:11 -0400
commit606689e9f8c52b537c85fd4bc1ba5ce7b3ee2678 (patch)
tree925b96dcaa830bffe69048f741703503c4095077 /sound/soc/s3c24xx
parent0154724d487586241c1ad57cfd348ed2ff2274e2 (diff)
ASoC: Add Openmoko Neo FreeRunner (GTA02) audio driver
This driver supports the audio subsystem on the Openmoko Neo FreeRunner smartphone, often known by its codename GTA02. The system has a WM8753 connected to a Samsung S3C2442 with an external GPIO controlled speaker amplifier. The driver was originally written by Graeme Gregory and has recieved contributions from Openmoko, myself and members of the Openmoko community. For much of this time the primary Openmoko kernel maintainer was Andy Green. Signed-off-by: Graeme Gregory <graeme@openmoko.com> Signed-off-by: Andy Green <andy@openmoko.com> Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Diffstat (limited to 'sound/soc/s3c24xx')
-rw-r--r--sound/soc/s3c24xx/Kconfig9
-rw-r--r--sound/soc/s3c24xx/Makefile2
-rw-r--r--sound/soc/s3c24xx/neo1973_gta02_wm8753.c481
3 files changed, 492 insertions, 0 deletions
diff --git a/sound/soc/s3c24xx/Kconfig b/sound/soc/s3c24xx/Kconfig
index df494d1e346f..d1ed0f537c3a 100644
--- a/sound/soc/s3c24xx/Kconfig
+++ b/sound/soc/s3c24xx/Kconfig
@@ -38,6 +38,15 @@ config SND_S3C24XX_SOC_NEO1973_WM8753
38 Say Y if you want to add support for SoC audio on smdk2440 38 Say Y if you want to add support for SoC audio on smdk2440
39 with the WM8753. 39 with the WM8753.
40 40
41config SND_S3C24XX_SOC_NEO1973_GTA02_WM8753
42 tristate "Audio support for the Openmoko Neo FreeRunner (GTA02)"
43 depends on SND_S3C24XX_SOC && MACH_NEO1973_GTA02
44 select SND_S3C24XX_SOC_I2S
45 select SND_SOC_WM8753
46 help
47 This driver provides audio support for the Openmoko Neo FreeRunner
48 smartphone.
49
41config SND_S3C24XX_SOC_JIVE_WM8750 50config SND_S3C24XX_SOC_JIVE_WM8750
42 tristate "SoC I2S Audio support for Jive" 51 tristate "SoC I2S Audio support for Jive"
43 depends on SND_S3C24XX_SOC && MACH_JIVE 52 depends on SND_S3C24XX_SOC && MACH_JIVE
diff --git a/sound/soc/s3c24xx/Makefile b/sound/soc/s3c24xx/Makefile
index 07a93a2ebe5f..eb219b016499 100644
--- a/sound/soc/s3c24xx/Makefile
+++ b/sound/soc/s3c24xx/Makefile
@@ -16,12 +16,14 @@ obj-$(CONFIG_SND_S3C_I2SV2_SOC) += snd-soc-s3c-i2s-v2.o
16# S3C24XX Machine Support 16# S3C24XX Machine Support
17snd-soc-jive-wm8750-objs := jive_wm8750.o 17snd-soc-jive-wm8750-objs := jive_wm8750.o
18snd-soc-neo1973-wm8753-objs := neo1973_wm8753.o 18snd-soc-neo1973-wm8753-objs := neo1973_wm8753.o
19snd-soc-neo1973-gta02-wm8753-objs := neo1973_gta02_wm8753.o
19snd-soc-smdk2443-wm9710-objs := smdk2443_wm9710.o 20snd-soc-smdk2443-wm9710-objs := smdk2443_wm9710.o
20snd-soc-ln2440sbc-alc650-objs := ln2440sbc_alc650.o 21snd-soc-ln2440sbc-alc650-objs := ln2440sbc_alc650.o
21snd-soc-s3c24xx-uda134x-objs := s3c24xx_uda134x.o 22snd-soc-s3c24xx-uda134x-objs := s3c24xx_uda134x.o
22 23
23obj-$(CONFIG_SND_S3C24XX_SOC_JIVE_WM8750) += snd-soc-jive-wm8750.o 24obj-$(CONFIG_SND_S3C24XX_SOC_JIVE_WM8750) += snd-soc-jive-wm8750.o
24obj-$(CONFIG_SND_S3C24XX_SOC_NEO1973_WM8753) += snd-soc-neo1973-wm8753.o 25obj-$(CONFIG_SND_S3C24XX_SOC_NEO1973_WM8753) += snd-soc-neo1973-wm8753.o
26obj-$(CONFIG_SND_S3C24XX_SOC_NEO1973_GTA02_WM8753) += snd-soc-neo1973-gta02-wm8753.o
25obj-$(CONFIG_SND_S3C24XX_SOC_SMDK2443_WM9710) += snd-soc-smdk2443-wm9710.o 27obj-$(CONFIG_SND_S3C24XX_SOC_SMDK2443_WM9710) += snd-soc-smdk2443-wm9710.o
26obj-$(CONFIG_SND_S3C24XX_SOC_LN2440SBC_ALC650) += snd-soc-ln2440sbc-alc650.o 28obj-$(CONFIG_SND_S3C24XX_SOC_LN2440SBC_ALC650) += snd-soc-ln2440sbc-alc650.o
27obj-$(CONFIG_SND_S3C24XX_SOC_S3C24XX_UDA134X) += snd-soc-s3c24xx-uda134x.o 29obj-$(CONFIG_SND_S3C24XX_SOC_S3C24XX_UDA134X) += snd-soc-s3c24xx-uda134x.o
diff --git a/sound/soc/s3c24xx/neo1973_gta02_wm8753.c b/sound/soc/s3c24xx/neo1973_gta02_wm8753.c
new file mode 100644
index 000000000000..1358f6fe8201
--- /dev/null
+++ b/sound/soc/s3c24xx/neo1973_gta02_wm8753.c
@@ -0,0 +1,481 @@
1/*
2 * neo1973_gta02_wm8753.c -- SoC audio for Neo1973
3 *
4 * Copyright 2007 Openmoko Inc
5 * Author: Graeme Gregory <graeme@openmoko.org>
6 * Copyright 2007 Wolfson Microelectronics PLC.
7 * Author: Graeme Gregory <linux@wolfsonmicro.com>
8 * Copyright 2009 Wolfson Microelectronics
9 *
10 * This program is free software; you can redistribute it and/or modify it
11 * under the terms of the GNU General Public License as published by the
12 * Free Software Foundation; either version 2 of the License, or (at your
13 * option) any later version.
14 */
15
16#include <linux/module.h>
17#include <linux/moduleparam.h>
18#include <linux/timer.h>
19#include <linux/interrupt.h>
20#include <linux/platform_device.h>
21#include <sound/core.h>
22#include <sound/pcm.h>
23#include <sound/soc.h>
24#include <sound/soc-dapm.h>
25
26#include <asm/mach-types.h>
27
28#include <plat/regs-iis.h>
29
30#include <mach/regs-clock.h>
31#include <mach/regs-gpio.h>
32#include <mach/hardware.h>
33#include <asm/io.h>
34#include <mach/regs-gpioj.h>
35#include <mach/gta02.h>
36#include "../codecs/wm8753.h"
37#include "s3c24xx-pcm.h"
38#include "s3c24xx-i2s.h"
39
40static struct snd_soc_card neo1973_gta02;
41
42static int neo1973_gta02_hifi_hw_params(struct snd_pcm_substream *substream,
43 struct snd_pcm_hw_params *params)
44{
45 struct snd_soc_pcm_runtime *rtd = substream->private_data;
46 struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
47 struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
48 unsigned int pll_out = 0, bclk = 0;
49 int ret = 0;
50 unsigned long iis_clkrate;
51
52 iis_clkrate = s3c24xx_i2s_get_clockrate();
53
54 switch (params_rate(params)) {
55 case 8000:
56 case 16000:
57 pll_out = 12288000;
58 break;
59 case 48000:
60 bclk = WM8753_BCLK_DIV_4;
61 pll_out = 12288000;
62 break;
63 case 96000:
64 bclk = WM8753_BCLK_DIV_2;
65 pll_out = 12288000;
66 break;
67 case 11025:
68 bclk = WM8753_BCLK_DIV_16;
69 pll_out = 11289600;
70 break;
71 case 22050:
72 bclk = WM8753_BCLK_DIV_8;
73 pll_out = 11289600;
74 break;
75 case 44100:
76 bclk = WM8753_BCLK_DIV_4;
77 pll_out = 11289600;
78 break;
79 case 88200:
80 bclk = WM8753_BCLK_DIV_2;
81 pll_out = 11289600;
82 break;
83 }
84
85 /* set codec DAI configuration */
86 ret = snd_soc_dai_set_fmt(codec_dai,
87 SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
88 SND_SOC_DAIFMT_CBM_CFM);
89 if (ret < 0)
90 return ret;
91
92 /* set cpu DAI configuration */
93 ret = snd_soc_dai_set_fmt(cpu_dai,
94 SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
95 SND_SOC_DAIFMT_CBM_CFM);
96 if (ret < 0)
97 return ret;
98
99 /* set the codec system clock for DAC and ADC */
100 ret = snd_soc_dai_set_sysclk(codec_dai, WM8753_MCLK, pll_out,
101 SND_SOC_CLOCK_IN);
102 if (ret < 0)
103 return ret;
104
105 /* set MCLK division for sample rate */
106 ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_MCLK,
107 S3C2410_IISMOD_32FS);
108 if (ret < 0)
109 return ret;
110
111 /* set codec BCLK division for sample rate */
112 ret = snd_soc_dai_set_clkdiv(codec_dai,
113 WM8753_BCLKDIV, bclk);
114 if (ret < 0)
115 return ret;
116
117 /* set prescaler division for sample rate */
118 ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_PRESCALER,
119 S3C24XX_PRESCALE(4, 4));
120 if (ret < 0)
121 return ret;
122
123 /* codec PLL input is PCLK/4 */
124 ret = snd_soc_dai_set_pll(codec_dai, WM8753_PLL1,
125 iis_clkrate / 4, pll_out);
126 if (ret < 0)
127 return ret;
128
129 return 0;
130}
131
132static int neo1973_gta02_hifi_hw_free(struct snd_pcm_substream *substream)
133{
134 struct snd_soc_pcm_runtime *rtd = substream->private_data;
135 struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
136
137 /* disable the PLL */
138 return snd_soc_dai_set_pll(codec_dai, WM8753_PLL1, 0, 0);
139}
140
141/*
142 * Neo1973 WM8753 HiFi DAI opserations.
143 */
144static struct snd_soc_ops neo1973_gta02_hifi_ops = {
145 .hw_params = neo1973_gta02_hifi_hw_params,
146 .hw_free = neo1973_gta02_hifi_hw_free,
147};
148
149static int neo1973_gta02_voice_hw_params(
150 struct snd_pcm_substream *substream,
151 struct snd_pcm_hw_params *params)
152{
153 struct snd_soc_pcm_runtime *rtd = substream->private_data;
154 struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
155 unsigned int pcmdiv = 0;
156 int ret = 0;
157 unsigned long iis_clkrate;
158
159 iis_clkrate = s3c24xx_i2s_get_clockrate();
160
161 if (params_rate(params) != 8000)
162 return -EINVAL;
163 if (params_channels(params) != 1)
164 return -EINVAL;
165
166 pcmdiv = WM8753_PCM_DIV_6; /* 2.048 MHz */
167
168 /* todo: gg check mode (DSP_B) against CSR datasheet */
169 /* set codec DAI configuration */
170 ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_DSP_B |
171 SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
172 if (ret < 0)
173 return ret;
174
175 /* set the codec system clock for DAC and ADC */
176 ret = snd_soc_dai_set_sysclk(codec_dai, WM8753_PCMCLK,
177 12288000, SND_SOC_CLOCK_IN);
178 if (ret < 0)
179 return ret;
180
181 /* set codec PCM division for sample rate */
182 ret = snd_soc_dai_set_clkdiv(codec_dai, WM8753_PCMDIV,
183 pcmdiv);
184 if (ret < 0)
185 return ret;
186
187 /* configue and enable PLL for 12.288MHz output */
188 ret = snd_soc_dai_set_pll(codec_dai, WM8753_PLL2,
189 iis_clkrate / 4, 12288000);
190 if (ret < 0)
191 return ret;
192
193 return 0;
194}
195
196static int neo1973_gta02_voice_hw_free(struct snd_pcm_substream *substream)
197{
198 struct snd_soc_pcm_runtime *rtd = substream->private_data;
199 struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
200
201 /* disable the PLL */
202 return snd_soc_dai_set_pll(codec_dai, WM8753_PLL2, 0, 0);
203}
204
205static struct snd_soc_ops neo1973_gta02_voice_ops = {
206 .hw_params = neo1973_gta02_voice_hw_params,
207 .hw_free = neo1973_gta02_voice_hw_free,
208};
209
210#define LM4853_AMP 1
211#define LM4853_SPK 2
212
213static u8 lm4853_state;
214
215/* This has no effect, it exists only to maintain compatibility with
216 * existing ALSA state files.
217 */
218static int lm4853_set_state(struct snd_kcontrol *kcontrol,
219 struct snd_ctl_elem_value *ucontrol)
220{
221 int val = ucontrol->value.integer.value[0];
222
223 if (val)
224 lm4853_state |= LM4853_AMP;
225 else
226 lm4853_state &= ~LM4853_AMP;
227
228 return 0;
229}
230
231static int lm4853_get_state(struct snd_kcontrol *kcontrol,
232 struct snd_ctl_elem_value *ucontrol)
233{
234 ucontrol->value.integer.value[0] = lm4853_state & LM4853_AMP;
235
236 return 0;
237}
238
239static int lm4853_set_spk(struct snd_kcontrol *kcontrol,
240 struct snd_ctl_elem_value *ucontrol)
241{
242 int val = ucontrol->value.integer.value[0];
243
244 if (val) {
245 lm4853_state |= LM4853_SPK;
246 s3c2410_gpio_setpin(GTA02_GPIO_HP_IN, 0);
247 } else {
248 lm4853_state &= ~LM4853_SPK;
249 s3c2410_gpio_setpin(GTA02_GPIO_HP_IN, 1);
250 }
251
252 return 0;
253}
254
255static int lm4853_get_spk(struct snd_kcontrol *kcontrol,
256 struct snd_ctl_elem_value *ucontrol)
257{
258 ucontrol->value.integer.value[0] = (lm4853_state & LM4853_SPK) >> 1;
259
260 return 0;
261}
262
263static int lm4853_event(struct snd_soc_dapm_widget *w,
264 struct snd_kcontrol *k,
265 int event)
266{
267 if (SND_SOC_DAPM_EVENT_ON(event))
268 s3c2410_gpio_setpin(GTA02_GPIO_AMP_SHUT, 0);
269
270 if (SND_SOC_DAPM_EVENT_OFF(event))
271 s3c2410_gpio_setpin(GTA02_GPIO_AMP_SHUT, 1);
272
273 return 0;
274}
275
276static const struct snd_soc_dapm_widget wm8753_dapm_widgets[] = {
277 SND_SOC_DAPM_SPK("Stereo Out", lm4853_event),
278 SND_SOC_DAPM_LINE("GSM Line Out", NULL),
279 SND_SOC_DAPM_LINE("GSM Line In", NULL),
280 SND_SOC_DAPM_MIC("Headset Mic", NULL),
281 SND_SOC_DAPM_MIC("Handset Mic", NULL),
282 SND_SOC_DAPM_SPK("Handset Spk", NULL),
283};
284
285
286/* example machine audio_mapnections */
287static const struct snd_soc_dapm_route audio_map[] = {
288
289 /* Connections to the lm4853 amp */
290 {"Stereo Out", NULL, "LOUT1"},
291 {"Stereo Out", NULL, "ROUT1"},
292
293 /* Connections to the GSM Module */
294 {"GSM Line Out", NULL, "MONO1"},
295 {"GSM Line Out", NULL, "MONO2"},
296 {"RXP", NULL, "GSM Line In"},
297 {"RXN", NULL, "GSM Line In"},
298
299 /* Connections to Headset */
300 {"MIC1", NULL, "Mic Bias"},
301 {"Mic Bias", NULL, "Headset Mic"},
302
303 /* Call Mic */
304 {"MIC2", NULL, "Mic Bias"},
305 {"MIC2N", NULL, "Mic Bias"},
306 {"Mic Bias", NULL, "Handset Mic"},
307
308 /* Call Speaker */
309 {"Handset Spk", NULL, "LOUT2"},
310 {"Handset Spk", NULL, "ROUT2"},
311
312 /* Connect the ALC pins */
313 {"ACIN", NULL, "ACOP"},
314};
315
316static const struct snd_kcontrol_new wm8753_neo1973_gta02_controls[] = {
317 SOC_DAPM_PIN_SWITCH("Stereo Out"),
318 SOC_DAPM_PIN_SWITCH("GSM Line Out"),
319 SOC_DAPM_PIN_SWITCH("GSM Line In"),
320 SOC_DAPM_PIN_SWITCH("Headset Mic"),
321 SOC_DAPM_PIN_SWITCH("Handset Mic"),
322 SOC_DAPM_PIN_SWITCH("Handset Spk"),
323
324 /* This has no effect, it exists only to maintain compatibility with
325 * existing ALSA state files.
326 */
327 SOC_SINGLE_EXT("Amp State Switch", 6, 0, 1, 0,
328 lm4853_get_state,
329 lm4853_set_state),
330 SOC_SINGLE_EXT("Amp Spk Switch", 7, 0, 1, 0,
331 lm4853_get_spk,
332 lm4853_set_spk),
333};
334
335/*
336 * This is an example machine initialisation for a wm8753 connected to a
337 * neo1973 GTA02.
338 */
339static int neo1973_gta02_wm8753_init(struct snd_soc_codec *codec)
340{
341 int i, err;
342
343 /* set up NC codec pins */
344 snd_soc_dapm_nc_pin(codec, "OUT3");
345 snd_soc_dapm_nc_pin(codec, "OUT4");
346 snd_soc_dapm_nc_pin(codec, "LINE1");
347 snd_soc_dapm_nc_pin(codec, "LINE2");
348
349 /* Add neo1973 gta02 specific widgets */
350 snd_soc_dapm_new_controls(codec, wm8753_dapm_widgets,
351 ARRAY_SIZE(wm8753_dapm_widgets));
352
353 /* add neo1973 gta02 specific controls */
354 for (i = 0; i < ARRAY_SIZE(wm8753_neo1973_gta02_controls); i++) {
355 err = snd_ctl_add(codec->card,
356 snd_soc_cnew(&wm8753_neo1973_gta02_controls[i],
357 codec, NULL));
358 if (err < 0)
359 return err;
360 }
361
362 /* set up neo1973 gta02 specific audio path audio_map */
363 snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
364
365 /* set endpoints to default off mode */
366 snd_soc_dapm_disable_pin(codec, "Stereo Out");
367 snd_soc_dapm_disable_pin(codec, "GSM Line Out");
368 snd_soc_dapm_disable_pin(codec, "GSM Line In");
369 snd_soc_dapm_disable_pin(codec, "Headset Mic");
370 snd_soc_dapm_disable_pin(codec, "Handset Mic");
371 snd_soc_dapm_disable_pin(codec, "Handset Spk");
372
373 snd_soc_dapm_sync(codec);
374
375 return 0;
376}
377
378/*
379 * BT Codec DAI
380 */
381static struct snd_soc_dai bt_dai = {
382 .name = "Bluetooth",
383 .id = 0,
384 .playback = {
385 .channels_min = 1,
386 .channels_max = 1,
387 .rates = SNDRV_PCM_RATE_8000,
388 .formats = SNDRV_PCM_FMTBIT_S16_LE,},
389 .capture = {
390 .channels_min = 1,
391 .channels_max = 1,
392 .rates = SNDRV_PCM_RATE_8000,
393 .formats = SNDRV_PCM_FMTBIT_S16_LE,},
394};
395
396static struct snd_soc_dai_link neo1973_gta02_dai[] = {
397{ /* Hifi Playback - for similatious use with voice below */
398 .name = "WM8753",
399 .stream_name = "WM8753 HiFi",
400 .cpu_dai = &s3c24xx_i2s_dai,
401 .codec_dai = &wm8753_dai[WM8753_DAI_HIFI],
402 .init = neo1973_gta02_wm8753_init,
403 .ops = &neo1973_gta02_hifi_ops,
404},
405{ /* Voice via BT */
406 .name = "Bluetooth",
407 .stream_name = "Voice",
408 .cpu_dai = &bt_dai,
409 .codec_dai = &wm8753_dai[WM8753_DAI_VOICE],
410 .ops = &neo1973_gta02_voice_ops,
411},
412};
413
414static struct snd_soc_card neo1973_gta02 = {
415 .name = "neo1973-gta02",
416 .platform = &s3c24xx_soc_platform,
417 .dai_link = neo1973_gta02_dai,
418 .num_links = ARRAY_SIZE(neo1973_gta02_dai),
419};
420
421static struct snd_soc_device neo1973_gta02_snd_devdata = {
422 .card = &neo1973_gta02,
423 .codec_dev = &soc_codec_dev_wm8753,
424};
425
426static struct platform_device *neo1973_gta02_snd_device;
427
428static int __init neo1973_gta02_init(void)
429{
430 int ret;
431
432 if (!machine_is_neo1973_gta02()) {
433 printk(KERN_INFO
434 "Only GTA02 is supported by this ASoC driver\n");
435 return -ENODEV;
436 }
437
438 /* register bluetooth DAI here */
439 ret = snd_soc_register_dai(&bt_dai);
440 if (ret)
441 return ret;
442
443 neo1973_gta02_snd_device = platform_device_alloc("soc-audio", -1);
444 if (!neo1973_gta02_snd_device)
445 return -ENOMEM;
446
447 platform_set_drvdata(neo1973_gta02_snd_device,
448 &neo1973_gta02_snd_devdata);
449 neo1973_gta02_snd_devdata.dev = &neo1973_gta02_snd_device->dev;
450 ret = platform_device_add(neo1973_gta02_snd_device);
451
452 if (ret) {
453 platform_device_put(neo1973_gta02_snd_device);
454 return ret;
455 }
456
457 /* Initialise GPIOs used by amp */
458 s3c2410_gpio_cfgpin(GTA02_GPIO_HP_IN, S3C2410_GPIO_OUTPUT);
459 s3c2410_gpio_cfgpin(GTA02_GPIO_AMP_SHUT, S3C2410_GPIO_OUTPUT);
460
461 /* Amp off by default */
462 s3c2410_gpio_setpin(GTA02_GPIO_AMP_SHUT, 1);
463
464 /* Speaker off by default */
465 s3c2410_gpio_setpin(GTA02_GPIO_HP_IN, 1);
466
467 return ret;
468}
469module_init(neo1973_gta02_init);
470
471static void __exit neo1973_gta02_exit(void)
472{
473 snd_soc_unregister_dai(&bt_dai);
474 platform_device_unregister(neo1973_gta02_snd_device);
475}
476module_exit(neo1973_gta02_exit);
477
478/* Module information */
479MODULE_AUTHOR("Graeme Gregory, graeme@openmoko.org");
480MODULE_DESCRIPTION("ALSA SoC WM8753 Neo1973 GTA02");
481MODULE_LICENSE("GPL");