Commit | Line | Data |
---|---|---|
74930bb6 GG |
1 | /* |
2 | * neo1973_wm8753.c -- SoC audio for Neo1973 | |
3 | * | |
4 | * Copyright 2007 Wolfson Microelectronics PLC. | |
5 | * Author: Graeme Gregory | |
6 | * graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.com | |
7 | * | |
8 | * This program is free software; you can redistribute it and/or modify it | |
9 | * under the terms of the GNU General Public License as published by the | |
10 | * Free Software Foundation; either version 2 of the License, or (at your | |
11 | * option) any later version. | |
12 | * | |
74930bb6 GG |
13 | */ |
14 | ||
15 | #include <linux/module.h> | |
16 | #include <linux/moduleparam.h> | |
17 | #include <linux/timer.h> | |
18 | #include <linux/interrupt.h> | |
19 | #include <linux/platform_device.h> | |
20 | #include <linux/i2c.h> | |
74930bb6 GG |
21 | #include <sound/core.h> |
22 | #include <sound/pcm.h> | |
23 | #include <sound/soc.h> | |
a2e31a59 | 24 | #include <sound/tlv.h> |
74930bb6 | 25 | |
fb2aa074 | 26 | #include <asm/mach-types.h> |
74930bb6 | 27 | #include <asm/hardware/scoop.h> |
a09e64fb RK |
28 | #include <mach/regs-clock.h> |
29 | #include <mach/regs-gpio.h> | |
30 | #include <mach/hardware.h> | |
8ba02ace | 31 | #include <linux/io.h> |
a09e64fb | 32 | #include <mach/spi-gpio.h> |
aa9673cf | 33 | |
8150bc88 | 34 | #include <plat/regs-iis.h> |
aa9673cf | 35 | |
74930bb6 GG |
36 | #include "../codecs/wm8753.h" |
37 | #include "lm4857.h" | |
d3ff5a3e | 38 | #include "s3c-dma.h" |
74930bb6 GG |
39 | #include "s3c24xx-i2s.h" |
40 | ||
41 | /* define the scenarios */ | |
42 | #define NEO_AUDIO_OFF 0 | |
43 | #define NEO_GSM_CALL_AUDIO_HANDSET 1 | |
44 | #define NEO_GSM_CALL_AUDIO_HEADSET 2 | |
45 | #define NEO_GSM_CALL_AUDIO_BLUETOOTH 3 | |
46 | #define NEO_STEREO_TO_SPEAKERS 4 | |
47 | #define NEO_STEREO_TO_HEADPHONES 5 | |
48 | #define NEO_CAPTURE_HANDSET 6 | |
49 | #define NEO_CAPTURE_HEADSET 7 | |
50 | #define NEO_CAPTURE_BLUETOOTH 8 | |
51 | ||
87506549 | 52 | static struct snd_soc_card neo1973; |
74930bb6 GG |
53 | static struct i2c_client *i2c; |
54 | ||
55 | static int neo1973_hifi_hw_params(struct snd_pcm_substream *substream, | |
56 | struct snd_pcm_hw_params *params) | |
57 | { | |
58 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | |
f0fba2ad LG |
59 | struct snd_soc_dai *codec_dai = rtd->codec_dai; |
60 | struct snd_soc_dai *cpu_dai = rtd->cpu_dai; | |
74930bb6 GG |
61 | unsigned int pll_out = 0, bclk = 0; |
62 | int ret = 0; | |
63 | unsigned long iis_clkrate; | |
64 | ||
ee7d4767 | 65 | pr_debug("Entered %s\n", __func__); |
1894c59f | 66 | |
74930bb6 GG |
67 | iis_clkrate = s3c24xx_i2s_get_clockrate(); |
68 | ||
69 | switch (params_rate(params)) { | |
70 | case 8000: | |
71 | case 16000: | |
72 | pll_out = 12288000; | |
73 | break; | |
74 | case 48000: | |
75 | bclk = WM8753_BCLK_DIV_4; | |
76 | pll_out = 12288000; | |
77 | break; | |
78 | case 96000: | |
79 | bclk = WM8753_BCLK_DIV_2; | |
80 | pll_out = 12288000; | |
81 | break; | |
82 | case 11025: | |
83 | bclk = WM8753_BCLK_DIV_16; | |
84 | pll_out = 11289600; | |
85 | break; | |
86 | case 22050: | |
87 | bclk = WM8753_BCLK_DIV_8; | |
88 | pll_out = 11289600; | |
89 | break; | |
90 | case 44100: | |
91 | bclk = WM8753_BCLK_DIV_4; | |
92 | pll_out = 11289600; | |
93 | break; | |
94 | case 88200: | |
95 | bclk = WM8753_BCLK_DIV_2; | |
96 | pll_out = 11289600; | |
97 | break; | |
98 | } | |
99 | ||
100 | /* set codec DAI configuration */ | |
64105cfd | 101 | ret = snd_soc_dai_set_fmt(codec_dai, |
74930bb6 GG |
102 | SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | |
103 | SND_SOC_DAIFMT_CBM_CFM); | |
104 | if (ret < 0) | |
105 | return ret; | |
106 | ||
107 | /* set cpu DAI configuration */ | |
64105cfd | 108 | ret = snd_soc_dai_set_fmt(cpu_dai, |
74930bb6 GG |
109 | SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | |
110 | SND_SOC_DAIFMT_CBM_CFM); | |
111 | if (ret < 0) | |
112 | return ret; | |
113 | ||
114 | /* set the codec system clock for DAC and ADC */ | |
64105cfd | 115 | ret = snd_soc_dai_set_sysclk(codec_dai, WM8753_MCLK, pll_out, |
74930bb6 GG |
116 | SND_SOC_CLOCK_IN); |
117 | if (ret < 0) | |
118 | return ret; | |
119 | ||
120 | /* set MCLK division for sample rate */ | |
64105cfd | 121 | ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_MCLK, |
8ba02ace | 122 | S3C2410_IISMOD_32FS); |
74930bb6 GG |
123 | if (ret < 0) |
124 | return ret; | |
125 | ||
126 | /* set codec BCLK division for sample rate */ | |
64105cfd | 127 | ret = snd_soc_dai_set_clkdiv(codec_dai, WM8753_BCLKDIV, bclk); |
74930bb6 GG |
128 | if (ret < 0) |
129 | return ret; | |
130 | ||
131 | /* set prescaler division for sample rate */ | |
64105cfd | 132 | ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_PRESCALER, |
8ba02ace | 133 | S3C24XX_PRESCALE(4, 4)); |
74930bb6 GG |
134 | if (ret < 0) |
135 | return ret; | |
136 | ||
137 | /* codec PLL input is PCLK/4 */ | |
85488037 | 138 | ret = snd_soc_dai_set_pll(codec_dai, WM8753_PLL1, 0, |
74930bb6 GG |
139 | iis_clkrate / 4, pll_out); |
140 | if (ret < 0) | |
141 | return ret; | |
142 | ||
143 | return 0; | |
144 | } | |
145 | ||
146 | static int neo1973_hifi_hw_free(struct snd_pcm_substream *substream) | |
147 | { | |
148 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | |
f0fba2ad | 149 | struct snd_soc_dai *codec_dai = rtd->codec_dai; |
74930bb6 | 150 | |
ee7d4767 | 151 | pr_debug("Entered %s\n", __func__); |
1894c59f | 152 | |
74930bb6 | 153 | /* disable the PLL */ |
140318aa | 154 | return snd_soc_dai_set_pll(codec_dai, WM8753_PLL1, 0, 0, 0); |
74930bb6 GG |
155 | } |
156 | ||
157 | /* | |
158 | * Neo1973 WM8753 HiFi DAI opserations. | |
159 | */ | |
160 | static struct snd_soc_ops neo1973_hifi_ops = { | |
161 | .hw_params = neo1973_hifi_hw_params, | |
162 | .hw_free = neo1973_hifi_hw_free, | |
163 | }; | |
164 | ||
165 | static int neo1973_voice_hw_params(struct snd_pcm_substream *substream, | |
166 | struct snd_pcm_hw_params *params) | |
167 | { | |
168 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | |
f0fba2ad | 169 | struct snd_soc_dai *codec_dai = rtd->codec_dai; |
74930bb6 GG |
170 | unsigned int pcmdiv = 0; |
171 | int ret = 0; | |
172 | unsigned long iis_clkrate; | |
173 | ||
ee7d4767 | 174 | pr_debug("Entered %s\n", __func__); |
1894c59f | 175 | |
74930bb6 GG |
176 | iis_clkrate = s3c24xx_i2s_get_clockrate(); |
177 | ||
178 | if (params_rate(params) != 8000) | |
179 | return -EINVAL; | |
180 | if (params_channels(params) != 1) | |
181 | return -EINVAL; | |
182 | ||
183 | pcmdiv = WM8753_PCM_DIV_6; /* 2.048 MHz */ | |
184 | ||
185 | /* todo: gg check mode (DSP_B) against CSR datasheet */ | |
186 | /* set codec DAI configuration */ | |
64105cfd | 187 | ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_DSP_B | |
74930bb6 GG |
188 | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS); |
189 | if (ret < 0) | |
190 | return ret; | |
191 | ||
192 | /* set the codec system clock for DAC and ADC */ | |
64105cfd | 193 | ret = snd_soc_dai_set_sysclk(codec_dai, WM8753_PCMCLK, 12288000, |
74930bb6 GG |
194 | SND_SOC_CLOCK_IN); |
195 | if (ret < 0) | |
196 | return ret; | |
197 | ||
198 | /* set codec PCM division for sample rate */ | |
64105cfd | 199 | ret = snd_soc_dai_set_clkdiv(codec_dai, WM8753_PCMDIV, pcmdiv); |
74930bb6 GG |
200 | if (ret < 0) |
201 | return ret; | |
202 | ||
fa2eb005 | 203 | /* configure and enable PLL for 12.288MHz output */ |
140318aa | 204 | ret = snd_soc_dai_set_pll(codec_dai, WM8753_PLL2, 0, |
74930bb6 GG |
205 | iis_clkrate / 4, 12288000); |
206 | if (ret < 0) | |
207 | return ret; | |
208 | ||
209 | return 0; | |
210 | } | |
211 | ||
212 | static int neo1973_voice_hw_free(struct snd_pcm_substream *substream) | |
213 | { | |
214 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | |
f0fba2ad | 215 | struct snd_soc_dai *codec_dai = rtd->codec_dai; |
74930bb6 | 216 | |
ee7d4767 | 217 | pr_debug("Entered %s\n", __func__); |
1894c59f | 218 | |
74930bb6 | 219 | /* disable the PLL */ |
140318aa | 220 | return snd_soc_dai_set_pll(codec_dai, WM8753_PLL2, 0, 0, 0); |
74930bb6 GG |
221 | } |
222 | ||
223 | static struct snd_soc_ops neo1973_voice_ops = { | |
224 | .hw_params = neo1973_voice_hw_params, | |
225 | .hw_free = neo1973_voice_hw_free, | |
226 | }; | |
227 | ||
8ba02ace | 228 | static int neo1973_scenario; |
74930bb6 GG |
229 | |
230 | static int neo1973_get_scenario(struct snd_kcontrol *kcontrol, | |
231 | struct snd_ctl_elem_value *ucontrol) | |
232 | { | |
233 | ucontrol->value.integer.value[0] = neo1973_scenario; | |
234 | return 0; | |
235 | } | |
236 | ||
237 | static int set_scenario_endpoints(struct snd_soc_codec *codec, int scenario) | |
238 | { | |
ce6120cc LG |
239 | struct snd_soc_dapm_context *dapm = &codec->dapm; |
240 | ||
ee7d4767 | 241 | pr_debug("Entered %s\n", __func__); |
1894c59f | 242 | |
8ba02ace | 243 | switch (neo1973_scenario) { |
74930bb6 | 244 | case NEO_AUDIO_OFF: |
ce6120cc LG |
245 | snd_soc_dapm_disable_pin(dapm, "Audio Out"); |
246 | snd_soc_dapm_disable_pin(dapm, "GSM Line Out"); | |
247 | snd_soc_dapm_disable_pin(dapm, "GSM Line In"); | |
248 | snd_soc_dapm_disable_pin(dapm, "Headset Mic"); | |
249 | snd_soc_dapm_disable_pin(dapm, "Call Mic"); | |
74930bb6 GG |
250 | break; |
251 | case NEO_GSM_CALL_AUDIO_HANDSET: | |
ce6120cc LG |
252 | snd_soc_dapm_enable_pin(dapm, "Audio Out"); |
253 | snd_soc_dapm_enable_pin(dapm, "GSM Line Out"); | |
254 | snd_soc_dapm_enable_pin(dapm, "GSM Line In"); | |
255 | snd_soc_dapm_disable_pin(dapm, "Headset Mic"); | |
256 | snd_soc_dapm_enable_pin(dapm, "Call Mic"); | |
74930bb6 GG |
257 | break; |
258 | case NEO_GSM_CALL_AUDIO_HEADSET: | |
ce6120cc LG |
259 | snd_soc_dapm_enable_pin(dapm, "Audio Out"); |
260 | snd_soc_dapm_enable_pin(dapm, "GSM Line Out"); | |
261 | snd_soc_dapm_enable_pin(dapm, "GSM Line In"); | |
262 | snd_soc_dapm_enable_pin(dapm, "Headset Mic"); | |
263 | snd_soc_dapm_disable_pin(dapm, "Call Mic"); | |
74930bb6 GG |
264 | break; |
265 | case NEO_GSM_CALL_AUDIO_BLUETOOTH: | |
ce6120cc LG |
266 | snd_soc_dapm_disable_pin(dapm, "Audio Out"); |
267 | snd_soc_dapm_enable_pin(dapm, "GSM Line Out"); | |
268 | snd_soc_dapm_enable_pin(dapm, "GSM Line In"); | |
269 | snd_soc_dapm_disable_pin(dapm, "Headset Mic"); | |
270 | snd_soc_dapm_disable_pin(dapm, "Call Mic"); | |
74930bb6 GG |
271 | break; |
272 | case NEO_STEREO_TO_SPEAKERS: | |
ce6120cc LG |
273 | snd_soc_dapm_enable_pin(dapm, "Audio Out"); |
274 | snd_soc_dapm_disable_pin(dapm, "GSM Line Out"); | |
275 | snd_soc_dapm_disable_pin(dapm, "GSM Line In"); | |
276 | snd_soc_dapm_disable_pin(dapm, "Headset Mic"); | |
277 | snd_soc_dapm_disable_pin(dapm, "Call Mic"); | |
74930bb6 GG |
278 | break; |
279 | case NEO_STEREO_TO_HEADPHONES: | |
ce6120cc LG |
280 | snd_soc_dapm_enable_pin(dapm, "Audio Out"); |
281 | snd_soc_dapm_disable_pin(dapm, "GSM Line Out"); | |
282 | snd_soc_dapm_disable_pin(dapm, "GSM Line In"); | |
283 | snd_soc_dapm_disable_pin(dapm, "Headset Mic"); | |
284 | snd_soc_dapm_disable_pin(dapm, "Call Mic"); | |
74930bb6 GG |
285 | break; |
286 | case NEO_CAPTURE_HANDSET: | |
ce6120cc LG |
287 | snd_soc_dapm_disable_pin(dapm, "Audio Out"); |
288 | snd_soc_dapm_disable_pin(dapm, "GSM Line Out"); | |
289 | snd_soc_dapm_disable_pin(dapm, "GSM Line In"); | |
290 | snd_soc_dapm_disable_pin(dapm, "Headset Mic"); | |
291 | snd_soc_dapm_enable_pin(dapm, "Call Mic"); | |
74930bb6 GG |
292 | break; |
293 | case NEO_CAPTURE_HEADSET: | |
ce6120cc LG |
294 | snd_soc_dapm_disable_pin(dapm, "Audio Out"); |
295 | snd_soc_dapm_disable_pin(dapm, "GSM Line Out"); | |
296 | snd_soc_dapm_disable_pin(dapm, "GSM Line In"); | |
297 | snd_soc_dapm_enable_pin(dapm, "Headset Mic"); | |
298 | snd_soc_dapm_disable_pin(dapm, "Call Mic"); | |
74930bb6 GG |
299 | break; |
300 | case NEO_CAPTURE_BLUETOOTH: | |
ce6120cc LG |
301 | snd_soc_dapm_disable_pin(dapm, "Audio Out"); |
302 | snd_soc_dapm_disable_pin(dapm, "GSM Line Out"); | |
303 | snd_soc_dapm_disable_pin(dapm, "GSM Line In"); | |
304 | snd_soc_dapm_disable_pin(dapm, "Headset Mic"); | |
305 | snd_soc_dapm_disable_pin(dapm, "Call Mic"); | |
74930bb6 GG |
306 | break; |
307 | default: | |
ce6120cc LG |
308 | snd_soc_dapm_disable_pin(dapm, "Audio Out"); |
309 | snd_soc_dapm_disable_pin(dapm, "GSM Line Out"); | |
310 | snd_soc_dapm_disable_pin(dapm, "GSM Line In"); | |
311 | snd_soc_dapm_disable_pin(dapm, "Headset Mic"); | |
312 | snd_soc_dapm_disable_pin(dapm, "Call Mic"); | |
74930bb6 GG |
313 | } |
314 | ||
ce6120cc | 315 | snd_soc_dapm_sync(dapm); |
74930bb6 GG |
316 | |
317 | return 0; | |
318 | } | |
319 | ||
320 | static int neo1973_set_scenario(struct snd_kcontrol *kcontrol, | |
321 | struct snd_ctl_elem_value *ucontrol) | |
322 | { | |
323 | struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); | |
324 | ||
ee7d4767 | 325 | pr_debug("Entered %s\n", __func__); |
1894c59f | 326 | |
74930bb6 GG |
327 | if (neo1973_scenario == ucontrol->value.integer.value[0]) |
328 | return 0; | |
329 | ||
330 | neo1973_scenario = ucontrol->value.integer.value[0]; | |
331 | set_scenario_endpoints(codec, neo1973_scenario); | |
332 | return 1; | |
333 | } | |
334 | ||
335 | static u8 lm4857_regs[4] = {0x00, 0x40, 0x80, 0xC0}; | |
336 | ||
337 | static void lm4857_write_regs(void) | |
338 | { | |
ee7d4767 | 339 | pr_debug("Entered %s\n", __func__); |
1894c59f | 340 | |
74930bb6 GG |
341 | if (i2c_master_send(i2c, lm4857_regs, 4) != 4) |
342 | printk(KERN_ERR "lm4857: i2c write failed\n"); | |
343 | } | |
344 | ||
345 | static int lm4857_get_reg(struct snd_kcontrol *kcontrol, | |
346 | struct snd_ctl_elem_value *ucontrol) | |
347 | { | |
236e6723 MB |
348 | struct soc_mixer_control *mc = |
349 | (struct soc_mixer_control *)kcontrol->private_value; | |
350 | int reg = mc->reg; | |
351 | int shift = mc->shift; | |
352 | int mask = mc->max; | |
74930bb6 | 353 | |
ee7d4767 | 354 | pr_debug("Entered %s\n", __func__); |
1894c59f | 355 | |
74930bb6 GG |
356 | ucontrol->value.integer.value[0] = (lm4857_regs[reg] >> shift) & mask; |
357 | return 0; | |
358 | } | |
359 | ||
360 | static int lm4857_set_reg(struct snd_kcontrol *kcontrol, | |
361 | struct snd_ctl_elem_value *ucontrol) | |
362 | { | |
236e6723 MB |
363 | struct soc_mixer_control *mc = |
364 | (struct soc_mixer_control *)kcontrol->private_value; | |
365 | int reg = mc->reg; | |
366 | int shift = mc->shift; | |
367 | int mask = mc->max; | |
74930bb6 | 368 | |
8ba02ace | 369 | if (((lm4857_regs[reg] >> shift) & mask) == |
74930bb6 GG |
370 | ucontrol->value.integer.value[0]) |
371 | return 0; | |
372 | ||
8ba02ace | 373 | lm4857_regs[reg] &= ~(mask << shift); |
74930bb6 GG |
374 | lm4857_regs[reg] |= ucontrol->value.integer.value[0] << shift; |
375 | lm4857_write_regs(); | |
376 | return 1; | |
377 | } | |
378 | ||
379 | static int lm4857_get_mode(struct snd_kcontrol *kcontrol, | |
380 | struct snd_ctl_elem_value *ucontrol) | |
381 | { | |
382 | u8 value = lm4857_regs[LM4857_CTRL] & 0x0F; | |
383 | ||
ee7d4767 | 384 | pr_debug("Entered %s\n", __func__); |
1894c59f | 385 | |
74930bb6 GG |
386 | if (value) |
387 | value -= 5; | |
388 | ||
389 | ucontrol->value.integer.value[0] = value; | |
390 | return 0; | |
391 | } | |
392 | ||
393 | static int lm4857_set_mode(struct snd_kcontrol *kcontrol, | |
394 | struct snd_ctl_elem_value *ucontrol) | |
395 | { | |
396 | u8 value = ucontrol->value.integer.value[0]; | |
397 | ||
ee7d4767 | 398 | pr_debug("Entered %s\n", __func__); |
1894c59f | 399 | |
74930bb6 GG |
400 | if (value) |
401 | value += 5; | |
402 | ||
403 | if ((lm4857_regs[LM4857_CTRL] & 0x0F) == value) | |
404 | return 0; | |
405 | ||
406 | lm4857_regs[LM4857_CTRL] &= 0xF0; | |
407 | lm4857_regs[LM4857_CTRL] |= value; | |
408 | lm4857_write_regs(); | |
409 | return 1; | |
410 | } | |
411 | ||
412 | static const struct snd_soc_dapm_widget wm8753_dapm_widgets[] = { | |
413 | SND_SOC_DAPM_LINE("Audio Out", NULL), | |
414 | SND_SOC_DAPM_LINE("GSM Line Out", NULL), | |
415 | SND_SOC_DAPM_LINE("GSM Line In", NULL), | |
416 | SND_SOC_DAPM_MIC("Headset Mic", NULL), | |
417 | SND_SOC_DAPM_MIC("Call Mic", NULL), | |
418 | }; | |
419 | ||
420 | ||
8f3112d7 | 421 | static const struct snd_soc_dapm_route dapm_routes[] = { |
74930bb6 GG |
422 | |
423 | /* Connections to the lm4857 amp */ | |
424 | {"Audio Out", NULL, "LOUT1"}, | |
425 | {"Audio Out", NULL, "ROUT1"}, | |
426 | ||
427 | /* Connections to the GSM Module */ | |
428 | {"GSM Line Out", NULL, "MONO1"}, | |
429 | {"GSM Line Out", NULL, "MONO2"}, | |
430 | {"RXP", NULL, "GSM Line In"}, | |
431 | {"RXN", NULL, "GSM Line In"}, | |
432 | ||
433 | /* Connections to Headset */ | |
434 | {"MIC1", NULL, "Mic Bias"}, | |
435 | {"Mic Bias", NULL, "Headset Mic"}, | |
436 | ||
437 | /* Call Mic */ | |
438 | {"MIC2", NULL, "Mic Bias"}, | |
439 | {"MIC2N", NULL, "Mic Bias"}, | |
440 | {"Mic Bias", NULL, "Call Mic"}, | |
441 | ||
442 | /* Connect the ALC pins */ | |
443 | {"ACIN", NULL, "ACOP"}, | |
74930bb6 GG |
444 | }; |
445 | ||
446 | static const char *lm4857_mode[] = { | |
447 | "Off", | |
448 | "Call Speaker", | |
449 | "Stereo Speakers", | |
450 | "Stereo Speakers + Headphones", | |
451 | "Headphones" | |
452 | }; | |
453 | ||
454 | static const struct soc_enum lm4857_mode_enum[] = { | |
455 | SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(lm4857_mode), lm4857_mode), | |
456 | }; | |
457 | ||
458 | static const char *neo_scenarios[] = { | |
459 | "Off", | |
460 | "GSM Handset", | |
461 | "GSM Headset", | |
462 | "GSM Bluetooth", | |
463 | "Speakers", | |
464 | "Headphones", | |
465 | "Capture Handset", | |
466 | "Capture Headset", | |
467 | "Capture Bluetooth" | |
468 | }; | |
469 | ||
470 | static const struct soc_enum neo_scenario_enum[] = { | |
8ba02ace | 471 | SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(neo_scenarios), neo_scenarios), |
74930bb6 GG |
472 | }; |
473 | ||
a2e31a59 MM |
474 | static const DECLARE_TLV_DB_SCALE(stereo_tlv, -4050, 150, 0); |
475 | static const DECLARE_TLV_DB_SCALE(mono_tlv, -3450, 150, 0); | |
476 | ||
74930bb6 | 477 | static const struct snd_kcontrol_new wm8753_neo1973_controls[] = { |
a2e31a59 MM |
478 | SOC_SINGLE_EXT_TLV("Amp Left Playback Volume", LM4857_LVOL, 0, 31, 0, |
479 | lm4857_get_reg, lm4857_set_reg, stereo_tlv), | |
480 | SOC_SINGLE_EXT_TLV("Amp Right Playback Volume", LM4857_RVOL, 0, 31, 0, | |
481 | lm4857_get_reg, lm4857_set_reg, stereo_tlv), | |
482 | SOC_SINGLE_EXT_TLV("Amp Mono Playback Volume", LM4857_MVOL, 0, 31, 0, | |
483 | lm4857_get_reg, lm4857_set_reg, mono_tlv), | |
74930bb6 GG |
484 | SOC_ENUM_EXT("Amp Mode", lm4857_mode_enum[0], |
485 | lm4857_get_mode, lm4857_set_mode), | |
486 | SOC_ENUM_EXT("Neo Mode", neo_scenario_enum[0], | |
487 | neo1973_get_scenario, neo1973_set_scenario), | |
488 | SOC_SINGLE_EXT("Amp Spk 3D Playback Switch", LM4857_LVOL, 5, 1, 0, | |
489 | lm4857_get_reg, lm4857_set_reg), | |
490 | SOC_SINGLE_EXT("Amp HP 3d Playback Switch", LM4857_RVOL, 5, 1, 0, | |
491 | lm4857_get_reg, lm4857_set_reg), | |
492 | SOC_SINGLE_EXT("Amp Fast Wakeup Playback Switch", LM4857_CTRL, 5, 1, 0, | |
493 | lm4857_get_reg, lm4857_set_reg), | |
494 | SOC_SINGLE_EXT("Amp Earpiece 6dB Playback Switch", LM4857_CTRL, 4, 1, 0, | |
495 | lm4857_get_reg, lm4857_set_reg), | |
496 | }; | |
497 | ||
498 | /* | |
499 | * This is an example machine initialisation for a wm8753 connected to a | |
500 | * neo1973 II. It is missing logic to detect hp/mic insertions and logic | |
501 | * to re-route the audio in such an event. | |
502 | */ | |
f0fba2ad | 503 | static int neo1973_wm8753_init(struct snd_soc_pcm_runtime *rtd) |
74930bb6 | 504 | { |
f0fba2ad | 505 | struct snd_soc_codec *codec = rtd->codec; |
ce6120cc | 506 | struct snd_soc_dapm_context *dapm = &codec->dapm; |
eb5f6d75 | 507 | int err; |
74930bb6 | 508 | |
ee7d4767 | 509 | pr_debug("Entered %s\n", __func__); |
1894c59f | 510 | |
74930bb6 | 511 | /* set up NC codec pins */ |
ce6120cc LG |
512 | snd_soc_dapm_nc_pin(dapm, "LOUT2"); |
513 | snd_soc_dapm_nc_pin(dapm, "ROUT2"); | |
514 | snd_soc_dapm_nc_pin(dapm, "OUT3"); | |
515 | snd_soc_dapm_nc_pin(dapm, "OUT4"); | |
516 | snd_soc_dapm_nc_pin(dapm, "LINE1"); | |
517 | snd_soc_dapm_nc_pin(dapm, "LINE2"); | |
74930bb6 | 518 | |
74930bb6 | 519 | /* Add neo1973 specific widgets */ |
ce6120cc | 520 | snd_soc_dapm_new_controls(dapm, wm8753_dapm_widgets, |
8f3112d7 | 521 | ARRAY_SIZE(wm8753_dapm_widgets)); |
74930bb6 | 522 | |
e8089948 JB |
523 | /* set endpoints to default mode */ |
524 | set_scenario_endpoints(codec, NEO_AUDIO_OFF); | |
525 | ||
74930bb6 | 526 | /* add neo1973 specific controls */ |
eb5f6d75 PZ |
527 | err = snd_soc_add_controls(codec, wm8753_neo1973_controls, |
528 | ARRAY_SIZE(8753_neo1973_controls)); | |
529 | if (err < 0) | |
530 | return err; | |
74930bb6 | 531 | |
8f3112d7 | 532 | /* set up neo1973 specific audio routes */ |
ce6120cc | 533 | err = snd_soc_dapm_add_routes(dapm, dapm_routes, |
8f3112d7 | 534 | ARRAY_SIZE(dapm_routes)); |
74930bb6 | 535 | |
ce6120cc | 536 | snd_soc_dapm_sync(dapm); |
74930bb6 GG |
537 | return 0; |
538 | } | |
539 | ||
540 | /* | |
541 | * BT Codec DAI | |
542 | */ | |
1992a6fb | 543 | static struct snd_soc_dai bt_dai = { |
f0fba2ad | 544 | .name = "bluetooth-dai", |
74930bb6 GG |
545 | .playback = { |
546 | .channels_min = 1, | |
547 | .channels_max = 1, | |
548 | .rates = SNDRV_PCM_RATE_8000, | |
549 | .formats = SNDRV_PCM_FMTBIT_S16_LE,}, | |
550 | .capture = { | |
551 | .channels_min = 1, | |
552 | .channels_max = 1, | |
553 | .rates = SNDRV_PCM_RATE_8000, | |
554 | .formats = SNDRV_PCM_FMTBIT_S16_LE,}, | |
555 | }; | |
556 | ||
557 | static struct snd_soc_dai_link neo1973_dai[] = { | |
558 | { /* Hifi Playback - for similatious use with voice below */ | |
559 | .name = "WM8753", | |
560 | .stream_name = "WM8753 HiFi", | |
f0fba2ad LG |
561 | .platform_name = "s3c24xx-pcm-audio", |
562 | .cpu_dai_name = "s3c24xx-i2s", | |
563 | .codec_dai_name = "wm8753-hifi", | |
564 | .codec_name = "wm8753-codec.0-0x1a", | |
74930bb6 GG |
565 | .init = neo1973_wm8753_init, |
566 | .ops = &neo1973_hifi_ops, | |
567 | }, | |
568 | { /* Voice via BT */ | |
569 | .name = "Bluetooth", | |
570 | .stream_name = "Voice", | |
f0fba2ad LG |
571 | .platform_name = "s3c24xx-pcm-audio", |
572 | .cpu_dai_name = "bluetooth-dai", | |
573 | .codec_dai_name = "wm8753-voice", | |
574 | .codec_name = "wm8753-codec.0-0x1a", | |
74930bb6 GG |
575 | .ops = &neo1973_voice_ops, |
576 | }, | |
577 | }; | |
578 | ||
87506549 | 579 | static struct snd_soc_card neo1973 = { |
74930bb6 GG |
580 | .name = "neo1973", |
581 | .dai_link = neo1973_dai, | |
582 | .num_links = ARRAY_SIZE(neo1973_dai), | |
583 | }; | |
584 | ||
b6471305 JD |
585 | static int lm4857_i2c_probe(struct i2c_client *client, |
586 | const struct i2c_device_id *id) | |
74930bb6 | 587 | { |
ee7d4767 | 588 | pr_debug("Entered %s\n", __func__); |
1894c59f | 589 | |
f9d1ab39 JB |
590 | i2c = client; |
591 | ||
74930bb6 | 592 | lm4857_write_regs(); |
74930bb6 GG |
593 | return 0; |
594 | } | |
595 | ||
b6471305 | 596 | static int lm4857_i2c_remove(struct i2c_client *client) |
74930bb6 | 597 | { |
ee7d4767 | 598 | pr_debug("Entered %s\n", __func__); |
1894c59f | 599 | |
f9d1ab39 JB |
600 | i2c = NULL; |
601 | ||
b6471305 | 602 | return 0; |
74930bb6 GG |
603 | } |
604 | ||
fd403dc8 GG |
605 | static u8 lm4857_state; |
606 | ||
607 | static int lm4857_suspend(struct i2c_client *dev, pm_message_t state) | |
608 | { | |
ee7d4767 | 609 | pr_debug("Entered %s\n", __func__); |
1894c59f | 610 | |
fd403dc8 GG |
611 | dev_dbg(&dev->dev, "lm4857_suspend\n"); |
612 | lm4857_state = lm4857_regs[LM4857_CTRL] & 0xf; | |
613 | if (lm4857_state) { | |
614 | lm4857_regs[LM4857_CTRL] &= 0xf0; | |
615 | lm4857_write_regs(); | |
616 | } | |
617 | return 0; | |
618 | } | |
619 | ||
620 | static int lm4857_resume(struct i2c_client *dev) | |
621 | { | |
ee7d4767 | 622 | pr_debug("Entered %s\n", __func__); |
1894c59f | 623 | |
fd403dc8 GG |
624 | if (lm4857_state) { |
625 | lm4857_regs[LM4857_CTRL] |= (lm4857_state & 0x0f); | |
626 | lm4857_write_regs(); | |
627 | } | |
628 | return 0; | |
629 | } | |
630 | ||
631 | static void lm4857_shutdown(struct i2c_client *dev) | |
632 | { | |
ee7d4767 | 633 | pr_debug("Entered %s\n", __func__); |
1894c59f | 634 | |
fd403dc8 GG |
635 | dev_dbg(&dev->dev, "lm4857_shutdown\n"); |
636 | lm4857_regs[LM4857_CTRL] &= 0xf0; | |
637 | lm4857_write_regs(); | |
638 | } | |
639 | ||
b6471305 | 640 | static const struct i2c_device_id lm4857_i2c_id[] = { |
df20cf92 | 641 | { "neo1973_lm4857", 0 }, |
b6471305 JD |
642 | { } |
643 | }; | |
644 | ||
74930bb6 GG |
645 | static struct i2c_driver lm4857_i2c_driver = { |
646 | .driver = { | |
647 | .name = "LM4857 I2C Amp", | |
648 | .owner = THIS_MODULE, | |
649 | }, | |
fd403dc8 GG |
650 | .suspend = lm4857_suspend, |
651 | .resume = lm4857_resume, | |
652 | .shutdown = lm4857_shutdown, | |
b6471305 JD |
653 | .probe = lm4857_i2c_probe, |
654 | .remove = lm4857_i2c_remove, | |
655 | .id_table = lm4857_i2c_id, | |
74930bb6 GG |
656 | }; |
657 | ||
658 | static struct platform_device *neo1973_snd_device; | |
659 | ||
660 | static int __init neo1973_init(void) | |
661 | { | |
662 | int ret; | |
663 | ||
ee7d4767 | 664 | pr_debug("Entered %s\n", __func__); |
1894c59f | 665 | |
fb2aa074 MB |
666 | if (!machine_is_neo1973_gta01()) { |
667 | printk(KERN_INFO | |
668 | "Only GTA01 hardware supported by ASoC driver\n"); | |
669 | return -ENODEV; | |
670 | } | |
671 | ||
74930bb6 GG |
672 | neo1973_snd_device = platform_device_alloc("soc-audio", -1); |
673 | if (!neo1973_snd_device) | |
674 | return -ENOMEM; | |
675 | ||
f0fba2ad | 676 | platform_set_drvdata(neo1973_snd_device, &neo1973); |
74930bb6 GG |
677 | ret = platform_device_add(neo1973_snd_device); |
678 | ||
d280289e | 679 | if (ret) { |
74930bb6 | 680 | platform_device_put(neo1973_snd_device); |
d280289e JD |
681 | return ret; |
682 | } | |
74930bb6 | 683 | |
f9d1ab39 JB |
684 | ret = i2c_add_driver(&lm4857_i2c_driver); |
685 | ||
b6471305 | 686 | if (ret != 0) |
d280289e | 687 | platform_device_unregister(neo1973_snd_device); |
74930bb6 GG |
688 | |
689 | return ret; | |
690 | } | |
691 | ||
692 | static void __exit neo1973_exit(void) | |
693 | { | |
ee7d4767 | 694 | pr_debug("Entered %s\n", __func__); |
1894c59f | 695 | |
6b9a9b32 | 696 | i2c_del_driver(&lm4857_i2c_driver); |
74930bb6 GG |
697 | platform_device_unregister(neo1973_snd_device); |
698 | } | |
699 | ||
700 | module_init(neo1973_init); | |
701 | module_exit(neo1973_exit); | |
702 | ||
703 | /* Module information */ | |
443590e6 | 704 | MODULE_AUTHOR("Graeme Gregory, graeme@openmoko.org, www.openmoko.org"); |
74930bb6 GG |
705 | MODULE_DESCRIPTION("ALSA SoC WM8753 Neo1973"); |
706 | MODULE_LICENSE("GPL"); |