blob: 08c33e9b96cee278a025ab6ee804a4c3a2e4abe8 [file] [log] [blame]
Steve Sakomancc175572008-10-30 21:35:26 -07001/*
2 * ALSA SoC TWL4030 codec driver
3 *
4 * Author: Steve Sakoman, <steve@sakoman.com>
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * version 2 as published by the Free Software Foundation.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
18 * 02110-1301 USA
19 *
20 */
21
22#include <linux/module.h>
23#include <linux/moduleparam.h>
24#include <linux/init.h>
25#include <linux/delay.h>
26#include <linux/pm.h>
27#include <linux/i2c.h>
28#include <linux/platform_device.h>
29#include <linux/i2c/twl4030.h>
30#include <sound/core.h>
31#include <sound/pcm.h>
32#include <sound/pcm_params.h>
33#include <sound/soc.h>
34#include <sound/soc-dapm.h>
35#include <sound/initval.h>
Peter Ujfalusic10b82cf2008-11-24 13:49:35 +020036#include <sound/tlv.h>
Steve Sakomancc175572008-10-30 21:35:26 -070037
38#include "twl4030.h"
39
40/*
41 * twl4030 register cache & default register settings
42 */
43static const u8 twl4030_reg[TWL4030_CACHEREGNUM] = {
44 0x00, /* this register not used */
45 0x93, /* REG_CODEC_MODE (0x1) */
46 0xc3, /* REG_OPTION (0x2) */
47 0x00, /* REG_UNKNOWN (0x3) */
48 0x00, /* REG_MICBIAS_CTL (0x4) */
Grazvydas Ignotas5920b452008-12-02 20:48:58 +020049 0x20, /* REG_ANAMICL (0x5) */
50 0x00, /* REG_ANAMICR (0x6) */
51 0x00, /* REG_AVADC_CTL (0x7) */
Steve Sakomancc175572008-10-30 21:35:26 -070052 0x00, /* REG_ADCMICSEL (0x8) */
53 0x00, /* REG_DIGMIXING (0x9) */
54 0x0c, /* REG_ATXL1PGA (0xA) */
55 0x0c, /* REG_ATXR1PGA (0xB) */
56 0x00, /* REG_AVTXL2PGA (0xC) */
57 0x00, /* REG_AVTXR2PGA (0xD) */
58 0x01, /* REG_AUDIO_IF (0xE) */
59 0x00, /* REG_VOICE_IF (0xF) */
60 0x00, /* REG_ARXR1PGA (0x10) */
61 0x00, /* REG_ARXL1PGA (0x11) */
62 0x6c, /* REG_ARXR2PGA (0x12) */
63 0x6c, /* REG_ARXL2PGA (0x13) */
64 0x00, /* REG_VRXPGA (0x14) */
65 0x00, /* REG_VSTPGA (0x15) */
66 0x00, /* REG_VRX2ARXPGA (0x16) */
67 0x0c, /* REG_AVDAC_CTL (0x17) */
68 0x00, /* REG_ARX2VTXPGA (0x18) */
69 0x00, /* REG_ARXL1_APGA_CTL (0x19) */
70 0x00, /* REG_ARXR1_APGA_CTL (0x1A) */
71 0x4b, /* REG_ARXL2_APGA_CTL (0x1B) */
72 0x4b, /* REG_ARXR2_APGA_CTL (0x1C) */
73 0x00, /* REG_ATX2ARXPGA (0x1D) */
74 0x00, /* REG_BT_IF (0x1E) */
75 0x00, /* REG_BTPGA (0x1F) */
76 0x00, /* REG_BTSTPGA (0x20) */
77 0x00, /* REG_EAR_CTL (0x21) */
78 0x24, /* REG_HS_SEL (0x22) */
79 0x0a, /* REG_HS_GAIN_SET (0x23) */
80 0x00, /* REG_HS_POPN_SET (0x24) */
81 0x00, /* REG_PREDL_CTL (0x25) */
82 0x00, /* REG_PREDR_CTL (0x26) */
83 0x00, /* REG_PRECKL_CTL (0x27) */
84 0x00, /* REG_PRECKR_CTL (0x28) */
85 0x00, /* REG_HFL_CTL (0x29) */
86 0x00, /* REG_HFR_CTL (0x2A) */
87 0x00, /* REG_ALC_CTL (0x2B) */
88 0x00, /* REG_ALC_SET1 (0x2C) */
89 0x00, /* REG_ALC_SET2 (0x2D) */
90 0x00, /* REG_BOOST_CTL (0x2E) */
Peter Ujfalusif8d05bd2008-11-24 08:25:45 +020091 0x00, /* REG_SOFTVOL_CTL (0x2F) */
Steve Sakomancc175572008-10-30 21:35:26 -070092 0x00, /* REG_DTMF_FREQSEL (0x30) */
93 0x00, /* REG_DTMF_TONEXT1H (0x31) */
94 0x00, /* REG_DTMF_TONEXT1L (0x32) */
95 0x00, /* REG_DTMF_TONEXT2H (0x33) */
96 0x00, /* REG_DTMF_TONEXT2L (0x34) */
97 0x00, /* REG_DTMF_TONOFF (0x35) */
98 0x00, /* REG_DTMF_WANONOFF (0x36) */
99 0x00, /* REG_I2S_RX_SCRAMBLE_H (0x37) */
100 0x00, /* REG_I2S_RX_SCRAMBLE_M (0x38) */
101 0x00, /* REG_I2S_RX_SCRAMBLE_L (0x39) */
102 0x16, /* REG_APLL_CTL (0x3A) */
103 0x00, /* REG_DTMF_CTL (0x3B) */
104 0x00, /* REG_DTMF_PGA_CTL2 (0x3C) */
105 0x00, /* REG_DTMF_PGA_CTL1 (0x3D) */
106 0x00, /* REG_MISC_SET_1 (0x3E) */
107 0x00, /* REG_PCMBTMUX (0x3F) */
108 0x00, /* not used (0x40) */
109 0x00, /* not used (0x41) */
110 0x00, /* not used (0x42) */
111 0x00, /* REG_RX_PATH_SEL (0x43) */
112 0x00, /* REG_VDL_APGA_CTL (0x44) */
113 0x00, /* REG_VIBRA_CTL (0x45) */
114 0x00, /* REG_VIBRA_SET (0x46) */
115 0x00, /* REG_VIBRA_PWM_SET (0x47) */
116 0x00, /* REG_ANAMIC_GAIN (0x48) */
117 0x00, /* REG_MISC_SET_2 (0x49) */
118};
119
120/*
121 * read twl4030 register cache
122 */
123static inline unsigned int twl4030_read_reg_cache(struct snd_soc_codec *codec,
124 unsigned int reg)
125{
126 u8 *cache = codec->reg_cache;
127
128 return cache[reg];
129}
130
131/*
132 * write twl4030 register cache
133 */
134static inline void twl4030_write_reg_cache(struct snd_soc_codec *codec,
135 u8 reg, u8 value)
136{
137 u8 *cache = codec->reg_cache;
138
139 if (reg >= TWL4030_CACHEREGNUM)
140 return;
141 cache[reg] = value;
142}
143
144/*
145 * write to the twl4030 register space
146 */
147static int twl4030_write(struct snd_soc_codec *codec,
148 unsigned int reg, unsigned int value)
149{
150 twl4030_write_reg_cache(codec, reg, value);
151 return twl4030_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, value, reg);
152}
153
154static void twl4030_clear_codecpdz(struct snd_soc_codec *codec)
155{
156 u8 mode;
157
158 mode = twl4030_read_reg_cache(codec, TWL4030_REG_CODEC_MODE);
159 twl4030_write(codec, TWL4030_REG_CODEC_MODE,
160 mode & ~TWL4030_CODECPDZ);
161
162 /* REVISIT: this delay is present in TI sample drivers */
163 /* but there seems to be no TRM requirement for it */
164 udelay(10);
165}
166
167static void twl4030_set_codecpdz(struct snd_soc_codec *codec)
168{
169 u8 mode;
170
171 mode = twl4030_read_reg_cache(codec, TWL4030_REG_CODEC_MODE);
172 twl4030_write(codec, TWL4030_REG_CODEC_MODE,
173 mode | TWL4030_CODECPDZ);
174
175 /* REVISIT: this delay is present in TI sample drivers */
176 /* but there seems to be no TRM requirement for it */
177 udelay(10);
178}
179
180static void twl4030_init_chip(struct snd_soc_codec *codec)
181{
182 int i;
183
184 /* clear CODECPDZ prior to setting register defaults */
185 twl4030_clear_codecpdz(codec);
186
187 /* set all audio section registers to reasonable defaults */
188 for (i = TWL4030_REG_OPTION; i <= TWL4030_REG_MISC_SET_2; i++)
189 twl4030_write(codec, i, twl4030_reg[i]);
190
191}
192
Peter Ujfalusi5e98a462008-12-09 12:35:47 +0200193/* Earpiece */
194static const char *twl4030_earpiece_texts[] =
195 {"Off", "DACL1", "DACL2", "Invalid",
196 "DACR1"};
197
198static const struct soc_enum twl4030_earpiece_enum =
199 SOC_ENUM_SINGLE(TWL4030_REG_EAR_CTL, 1,
200 ARRAY_SIZE(twl4030_earpiece_texts),
201 twl4030_earpiece_texts);
202
203static const struct snd_kcontrol_new twl4030_dapm_earpiece_control =
204SOC_DAPM_ENUM("Route", twl4030_earpiece_enum);
205
Peter Ujfalusi2a6f5c582008-12-09 12:35:48 +0200206/* PreDrive Left */
207static const char *twl4030_predrivel_texts[] =
208 {"Off", "DACL1", "DACL2", "Invalid",
209 "DACR2"};
210
211static const struct soc_enum twl4030_predrivel_enum =
212 SOC_ENUM_SINGLE(TWL4030_REG_PREDL_CTL, 1,
213 ARRAY_SIZE(twl4030_predrivel_texts),
214 twl4030_predrivel_texts);
215
216static const struct snd_kcontrol_new twl4030_dapm_predrivel_control =
217SOC_DAPM_ENUM("Route", twl4030_predrivel_enum);
218
219/* PreDrive Right */
220static const char *twl4030_predriver_texts[] =
221 {"Off", "DACR1", "DACR2", "Invalid",
222 "DACL2"};
223
224static const struct soc_enum twl4030_predriver_enum =
225 SOC_ENUM_SINGLE(TWL4030_REG_PREDR_CTL, 1,
226 ARRAY_SIZE(twl4030_predriver_texts),
227 twl4030_predriver_texts);
228
229static const struct snd_kcontrol_new twl4030_dapm_predriver_control =
230SOC_DAPM_ENUM("Route", twl4030_predriver_enum);
231
Peter Ujfalusidfad21a2008-12-09 12:35:49 +0200232/* Headset Left */
233static const char *twl4030_hsol_texts[] =
234 {"Off", "DACL1", "DACL2"};
235
236static const struct soc_enum twl4030_hsol_enum =
237 SOC_ENUM_SINGLE(TWL4030_REG_HS_SEL, 1,
238 ARRAY_SIZE(twl4030_hsol_texts),
239 twl4030_hsol_texts);
240
241static const struct snd_kcontrol_new twl4030_dapm_hsol_control =
242SOC_DAPM_ENUM("Route", twl4030_hsol_enum);
243
244/* Headset Right */
245static const char *twl4030_hsor_texts[] =
246 {"Off", "DACR1", "DACR2"};
247
248static const struct soc_enum twl4030_hsor_enum =
249 SOC_ENUM_SINGLE(TWL4030_REG_HS_SEL, 4,
250 ARRAY_SIZE(twl4030_hsor_texts),
251 twl4030_hsor_texts);
252
253static const struct snd_kcontrol_new twl4030_dapm_hsor_control =
254SOC_DAPM_ENUM("Route", twl4030_hsor_enum);
255
Peter Ujfalusi5152d8c2008-12-09 12:35:50 +0200256/* Carkit Left */
257static const char *twl4030_carkitl_texts[] =
258 {"Off", "DACL1", "DACL2"};
259
260static const struct soc_enum twl4030_carkitl_enum =
261 SOC_ENUM_SINGLE(TWL4030_REG_PRECKL_CTL, 1,
262 ARRAY_SIZE(twl4030_carkitl_texts),
263 twl4030_carkitl_texts);
264
265static const struct snd_kcontrol_new twl4030_dapm_carkitl_control =
266SOC_DAPM_ENUM("Route", twl4030_carkitl_enum);
267
268/* Carkit Right */
269static const char *twl4030_carkitr_texts[] =
270 {"Off", "DACR1", "DACR2"};
271
272static const struct soc_enum twl4030_carkitr_enum =
273 SOC_ENUM_SINGLE(TWL4030_REG_PRECKR_CTL, 1,
274 ARRAY_SIZE(twl4030_carkitr_texts),
275 twl4030_carkitr_texts);
276
277static const struct snd_kcontrol_new twl4030_dapm_carkitr_control =
278SOC_DAPM_ENUM("Route", twl4030_carkitr_enum);
279
Peter Ujfalusie8ff9c42008-12-09 12:35:46 +0200280static int outmixer_event(struct snd_soc_dapm_widget *w,
281 struct snd_kcontrol *kcontrol, int event)
282{
283 struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
284 int ret = 0;
285 int val;
286
287 switch (e->reg) {
288 case TWL4030_REG_PREDL_CTL:
289 case TWL4030_REG_PREDR_CTL:
290 case TWL4030_REG_EAR_CTL:
291 val = w->value >> e->shift_l;
292 if (val == 3) {
293 printk(KERN_WARNING
294 "Invalid MUX setting for register 0x%02x (%d)\n",
295 e->reg, val);
296 ret = -1;
297 }
298 break;
299 }
300
301 return ret;
302}
303
Peter Ujfalusic10b82cf2008-11-24 13:49:35 +0200304/*
Peter Ujfalusib0bd53a2008-11-24 13:49:38 +0200305 * Some of the gain controls in TWL (mostly those which are associated with
306 * the outputs) are implemented in an interesting way:
307 * 0x0 : Power down (mute)
308 * 0x1 : 6dB
309 * 0x2 : 0 dB
310 * 0x3 : -6 dB
311 * Inverting not going to help with these.
312 * Custom volsw and volsw_2r get/put functions to handle these gain bits.
313 */
314#define SOC_DOUBLE_TLV_TWL4030(xname, xreg, shift_left, shift_right, xmax,\
315 xinvert, tlv_array) \
316{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname),\
317 .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\
318 SNDRV_CTL_ELEM_ACCESS_READWRITE,\
319 .tlv.p = (tlv_array), \
320 .info = snd_soc_info_volsw, \
321 .get = snd_soc_get_volsw_twl4030, \
322 .put = snd_soc_put_volsw_twl4030, \
323 .private_value = (unsigned long)&(struct soc_mixer_control) \
324 {.reg = xreg, .shift = shift_left, .rshift = shift_right,\
325 .max = xmax, .invert = xinvert} }
326#define SOC_DOUBLE_R_TLV_TWL4030(xname, reg_left, reg_right, xshift, xmax,\
327 xinvert, tlv_array) \
328{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname),\
329 .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\
330 SNDRV_CTL_ELEM_ACCESS_READWRITE,\
331 .tlv.p = (tlv_array), \
332 .info = snd_soc_info_volsw_2r, \
333 .get = snd_soc_get_volsw_r2_twl4030,\
334 .put = snd_soc_put_volsw_r2_twl4030, \
335 .private_value = (unsigned long)&(struct soc_mixer_control) \
336 {.reg = reg_left, .rreg = reg_right, .shift = xshift, \
337 .max = xmax, .invert = xinvert} }
338#define SOC_SINGLE_TLV_TWL4030(xname, xreg, xshift, xmax, xinvert, tlv_array) \
339 SOC_DOUBLE_TLV_TWL4030(xname, xreg, xshift, xshift, xmax, \
340 xinvert, tlv_array)
341
342static int snd_soc_get_volsw_twl4030(struct snd_kcontrol *kcontrol,
343 struct snd_ctl_elem_value *ucontrol)
344{
345 struct soc_mixer_control *mc =
346 (struct soc_mixer_control *)kcontrol->private_value;
347 struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
348 unsigned int reg = mc->reg;
349 unsigned int shift = mc->shift;
350 unsigned int rshift = mc->rshift;
351 int max = mc->max;
352 int mask = (1 << fls(max)) - 1;
353
354 ucontrol->value.integer.value[0] =
355 (snd_soc_read(codec, reg) >> shift) & mask;
356 if (ucontrol->value.integer.value[0])
357 ucontrol->value.integer.value[0] =
358 max + 1 - ucontrol->value.integer.value[0];
359
360 if (shift != rshift) {
361 ucontrol->value.integer.value[1] =
362 (snd_soc_read(codec, reg) >> rshift) & mask;
363 if (ucontrol->value.integer.value[1])
364 ucontrol->value.integer.value[1] =
365 max + 1 - ucontrol->value.integer.value[1];
366 }
367
368 return 0;
369}
370
371static int snd_soc_put_volsw_twl4030(struct snd_kcontrol *kcontrol,
372 struct snd_ctl_elem_value *ucontrol)
373{
374 struct soc_mixer_control *mc =
375 (struct soc_mixer_control *)kcontrol->private_value;
376 struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
377 unsigned int reg = mc->reg;
378 unsigned int shift = mc->shift;
379 unsigned int rshift = mc->rshift;
380 int max = mc->max;
381 int mask = (1 << fls(max)) - 1;
382 unsigned short val, val2, val_mask;
383
384 val = (ucontrol->value.integer.value[0] & mask);
385
386 val_mask = mask << shift;
387 if (val)
388 val = max + 1 - val;
389 val = val << shift;
390 if (shift != rshift) {
391 val2 = (ucontrol->value.integer.value[1] & mask);
392 val_mask |= mask << rshift;
393 if (val2)
394 val2 = max + 1 - val2;
395 val |= val2 << rshift;
396 }
397 return snd_soc_update_bits(codec, reg, val_mask, val);
398}
399
400static int snd_soc_get_volsw_r2_twl4030(struct snd_kcontrol *kcontrol,
401 struct snd_ctl_elem_value *ucontrol)
402{
403 struct soc_mixer_control *mc =
404 (struct soc_mixer_control *)kcontrol->private_value;
405 struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
406 unsigned int reg = mc->reg;
407 unsigned int reg2 = mc->rreg;
408 unsigned int shift = mc->shift;
409 int max = mc->max;
410 int mask = (1<<fls(max))-1;
411
412 ucontrol->value.integer.value[0] =
413 (snd_soc_read(codec, reg) >> shift) & mask;
414 ucontrol->value.integer.value[1] =
415 (snd_soc_read(codec, reg2) >> shift) & mask;
416
417 if (ucontrol->value.integer.value[0])
418 ucontrol->value.integer.value[0] =
419 max + 1 - ucontrol->value.integer.value[0];
420 if (ucontrol->value.integer.value[1])
421 ucontrol->value.integer.value[1] =
422 max + 1 - ucontrol->value.integer.value[1];
423
424 return 0;
425}
426
427static int snd_soc_put_volsw_r2_twl4030(struct snd_kcontrol *kcontrol,
428 struct snd_ctl_elem_value *ucontrol)
429{
430 struct soc_mixer_control *mc =
431 (struct soc_mixer_control *)kcontrol->private_value;
432 struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
433 unsigned int reg = mc->reg;
434 unsigned int reg2 = mc->rreg;
435 unsigned int shift = mc->shift;
436 int max = mc->max;
437 int mask = (1 << fls(max)) - 1;
438 int err;
439 unsigned short val, val2, val_mask;
440
441 val_mask = mask << shift;
442 val = (ucontrol->value.integer.value[0] & mask);
443 val2 = (ucontrol->value.integer.value[1] & mask);
444
445 if (val)
446 val = max + 1 - val;
447 if (val2)
448 val2 = max + 1 - val2;
449
450 val = val << shift;
451 val2 = val2 << shift;
452
453 err = snd_soc_update_bits(codec, reg, val_mask, val);
454 if (err < 0)
455 return err;
456
457 err = snd_soc_update_bits(codec, reg2, val_mask, val2);
458 return err;
459}
460
Grazvydas Ignotas5920b452008-12-02 20:48:58 +0200461static int twl4030_get_left_input(struct snd_kcontrol *kcontrol,
462 struct snd_ctl_elem_value *ucontrol)
463{
464 struct snd_soc_codec *codec = kcontrol->private_data;
465 u8 reg = twl4030_read_reg_cache(codec, TWL4030_REG_ANAMICL);
466 int result = 0;
467
468 /* one bit must be set a time */
469 reg &= TWL4030_CKMIC_EN | TWL4030_AUXL_EN | TWL4030_HSMIC_EN
470 | TWL4030_MAINMIC_EN;
471 if (reg != 0) {
472 result++;
473 while ((reg & 1) == 0) {
474 result++;
475 reg >>= 1;
476 }
477 }
478
479 ucontrol->value.integer.value[0] = result;
480 return 0;
481}
482
483static int twl4030_put_left_input(struct snd_kcontrol *kcontrol,
484 struct snd_ctl_elem_value *ucontrol)
485{
486 struct snd_soc_codec *codec = kcontrol->private_data;
487 int value = ucontrol->value.integer.value[0];
488 u8 anamicl, micbias, avadc_ctl;
489
490 anamicl = twl4030_read_reg_cache(codec, TWL4030_REG_ANAMICL);
491 anamicl &= ~(TWL4030_CKMIC_EN | TWL4030_AUXL_EN | TWL4030_HSMIC_EN
492 | TWL4030_MAINMIC_EN);
493 micbias = twl4030_read_reg_cache(codec, TWL4030_REG_MICBIAS_CTL);
494 micbias &= ~(TWL4030_HSMICBIAS_EN | TWL4030_MICBIAS1_EN);
495 avadc_ctl = twl4030_read_reg_cache(codec, TWL4030_REG_AVADC_CTL);
496
497 switch (value) {
498 case 1:
499 anamicl |= TWL4030_MAINMIC_EN;
500 micbias |= TWL4030_MICBIAS1_EN;
501 break;
502 case 2:
503 anamicl |= TWL4030_HSMIC_EN;
504 micbias |= TWL4030_HSMICBIAS_EN;
505 break;
506 case 3:
507 anamicl |= TWL4030_AUXL_EN;
508 break;
509 case 4:
510 anamicl |= TWL4030_CKMIC_EN;
511 break;
512 default:
513 break;
514 }
515
516 /* If some input is selected, enable amp and ADC */
517 if (value != 0) {
518 anamicl |= TWL4030_MICAMPL_EN;
519 avadc_ctl |= TWL4030_ADCL_EN;
520 } else {
521 anamicl &= ~TWL4030_MICAMPL_EN;
522 avadc_ctl &= ~TWL4030_ADCL_EN;
523 }
524
525 twl4030_write(codec, TWL4030_REG_ANAMICL, anamicl);
526 twl4030_write(codec, TWL4030_REG_MICBIAS_CTL, micbias);
527 twl4030_write(codec, TWL4030_REG_AVADC_CTL, avadc_ctl);
528
529 return 1;
530}
531
532static int twl4030_get_right_input(struct snd_kcontrol *kcontrol,
533 struct snd_ctl_elem_value *ucontrol)
534{
535 struct snd_soc_codec *codec = kcontrol->private_data;
536 u8 reg = twl4030_read_reg_cache(codec, TWL4030_REG_ANAMICR);
537 int value = 0;
538
539 reg &= TWL4030_SUBMIC_EN|TWL4030_AUXR_EN;
540 switch (reg) {
541 case TWL4030_SUBMIC_EN:
542 value = 1;
543 break;
544 case TWL4030_AUXR_EN:
545 value = 2;
546 break;
547 default:
548 break;
549 }
550
551 ucontrol->value.integer.value[0] = value;
552 return 0;
553}
554
555static int twl4030_put_right_input(struct snd_kcontrol *kcontrol,
556 struct snd_ctl_elem_value *ucontrol)
557{
558 struct snd_soc_codec *codec = kcontrol->private_data;
559 int value = ucontrol->value.integer.value[0];
560 u8 anamicr, micbias, avadc_ctl;
561
562 anamicr = twl4030_read_reg_cache(codec, TWL4030_REG_ANAMICR);
563 anamicr &= ~(TWL4030_SUBMIC_EN|TWL4030_AUXR_EN);
564 micbias = twl4030_read_reg_cache(codec, TWL4030_REG_MICBIAS_CTL);
565 micbias &= ~TWL4030_MICBIAS2_EN;
566 avadc_ctl = twl4030_read_reg_cache(codec, TWL4030_REG_AVADC_CTL);
567
568 switch (value) {
569 case 1:
570 anamicr |= TWL4030_SUBMIC_EN;
571 micbias |= TWL4030_MICBIAS2_EN;
572 break;
573 case 2:
574 anamicr |= TWL4030_AUXR_EN;
575 break;
576 default:
577 break;
578 }
579
580 if (value != 0) {
581 anamicr |= TWL4030_MICAMPR_EN;
582 avadc_ctl |= TWL4030_ADCR_EN;
583 } else {
584 anamicr &= ~TWL4030_MICAMPR_EN;
585 avadc_ctl &= ~TWL4030_ADCR_EN;
586 }
587
588 twl4030_write(codec, TWL4030_REG_ANAMICR, anamicr);
589 twl4030_write(codec, TWL4030_REG_MICBIAS_CTL, micbias);
590 twl4030_write(codec, TWL4030_REG_AVADC_CTL, avadc_ctl);
591
592 return 1;
593}
594
595static const char *twl4030_left_in_sel[] = {
596 "None",
597 "Main Mic",
598 "Headset Mic",
599 "Line In",
600 "Carkit Mic",
601};
602
603static const char *twl4030_right_in_sel[] = {
604 "None",
605 "Sub Mic",
606 "Line In",
607};
608
609static const struct soc_enum twl4030_left_input_mux =
610 SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(twl4030_left_in_sel),
611 twl4030_left_in_sel);
612
613static const struct soc_enum twl4030_right_input_mux =
614 SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(twl4030_right_in_sel),
615 twl4030_right_in_sel);
616
Peter Ujfalusib0bd53a2008-11-24 13:49:38 +0200617/*
Peter Ujfalusic10b82cf2008-11-24 13:49:35 +0200618 * FGAIN volume control:
619 * from -62 to 0 dB in 1 dB steps (mute instead of -63 dB)
620 */
Peter Ujfalusid889a722008-12-01 10:03:46 +0200621static DECLARE_TLV_DB_SCALE(digital_fine_tlv, -6300, 100, 1);
Peter Ujfalusic10b82cf2008-11-24 13:49:35 +0200622
Peter Ujfalusi0d33ea02008-11-24 13:49:36 +0200623/*
624 * CGAIN volume control:
625 * 0 dB to 12 dB in 6 dB steps
626 * value 2 and 3 means 12 dB
627 */
Peter Ujfalusid889a722008-12-01 10:03:46 +0200628static DECLARE_TLV_DB_SCALE(digital_coarse_tlv, 0, 600, 0);
629
630/*
631 * Analog playback gain
632 * -24 dB to 12 dB in 2 dB steps
633 */
634static DECLARE_TLV_DB_SCALE(analog_tlv, -2400, 200, 0);
Peter Ujfalusi0d33ea02008-11-24 13:49:36 +0200635
Peter Ujfalusi381a22b2008-12-01 10:03:45 +0200636/*
Peter Ujfalusi42902392008-12-01 10:03:47 +0200637 * Gain controls tied to outputs
638 * -6 dB to 6 dB in 6 dB steps (mute instead of -12)
639 */
640static DECLARE_TLV_DB_SCALE(output_tvl, -1200, 600, 1);
641
642/*
Peter Ujfalusi381a22b2008-12-01 10:03:45 +0200643 * Capture gain after the ADCs
644 * from 0 dB to 31 dB in 1 dB steps
645 */
646static DECLARE_TLV_DB_SCALE(digital_capture_tlv, 0, 100, 0);
647
Grazvydas Ignotas5920b452008-12-02 20:48:58 +0200648/*
649 * Gain control for input amplifiers
650 * 0 dB to 30 dB in 6 dB steps
651 */
652static DECLARE_TLV_DB_SCALE(input_gain_tlv, 0, 600, 0);
653
Steve Sakomancc175572008-10-30 21:35:26 -0700654static const struct snd_kcontrol_new twl4030_snd_controls[] = {
Peter Ujfalusid889a722008-12-01 10:03:46 +0200655 /* Common playback gain controls */
656 SOC_DOUBLE_R_TLV("DAC1 Digital Fine Playback Volume",
657 TWL4030_REG_ARXL1PGA, TWL4030_REG_ARXR1PGA,
658 0, 0x3f, 0, digital_fine_tlv),
659 SOC_DOUBLE_R_TLV("DAC2 Digital Fine Playback Volume",
660 TWL4030_REG_ARXL2PGA, TWL4030_REG_ARXR2PGA,
661 0, 0x3f, 0, digital_fine_tlv),
662
663 SOC_DOUBLE_R_TLV("DAC1 Digital Coarse Playback Volume",
664 TWL4030_REG_ARXL1PGA, TWL4030_REG_ARXR1PGA,
665 6, 0x2, 0, digital_coarse_tlv),
666 SOC_DOUBLE_R_TLV("DAC2 Digital Coarse Playback Volume",
667 TWL4030_REG_ARXL2PGA, TWL4030_REG_ARXR2PGA,
668 6, 0x2, 0, digital_coarse_tlv),
669
670 SOC_DOUBLE_R_TLV("DAC1 Analog Playback Volume",
671 TWL4030_REG_ARXL1_APGA_CTL, TWL4030_REG_ARXR1_APGA_CTL,
672 3, 0x12, 1, analog_tlv),
673 SOC_DOUBLE_R_TLV("DAC2 Analog Playback Volume",
674 TWL4030_REG_ARXL2_APGA_CTL, TWL4030_REG_ARXR2_APGA_CTL,
675 3, 0x12, 1, analog_tlv),
Peter Ujfalusi44c55872008-12-09 08:45:44 +0200676 SOC_DOUBLE_R("DAC1 Analog Playback Switch",
677 TWL4030_REG_ARXL1_APGA_CTL, TWL4030_REG_ARXR1_APGA_CTL,
678 1, 1, 0),
679 SOC_DOUBLE_R("DAC2 Analog Playback Switch",
680 TWL4030_REG_ARXL2_APGA_CTL, TWL4030_REG_ARXR2_APGA_CTL,
681 1, 1, 0),
Peter Ujfalusi381a22b2008-12-01 10:03:45 +0200682
Peter Ujfalusi42902392008-12-01 10:03:47 +0200683 /* Separate output gain controls */
684 SOC_DOUBLE_R_TLV_TWL4030("PreDriv Playback Volume",
685 TWL4030_REG_PREDL_CTL, TWL4030_REG_PREDR_CTL,
686 4, 3, 0, output_tvl),
687
688 SOC_DOUBLE_TLV_TWL4030("Headset Playback Volume",
689 TWL4030_REG_HS_GAIN_SET, 0, 2, 3, 0, output_tvl),
690
691 SOC_DOUBLE_R_TLV_TWL4030("Carkit Playback Volume",
692 TWL4030_REG_PRECKL_CTL, TWL4030_REG_PRECKR_CTL,
693 4, 3, 0, output_tvl),
694
695 SOC_SINGLE_TLV_TWL4030("Earpiece Playback Volume",
696 TWL4030_REG_EAR_CTL, 4, 3, 0, output_tvl),
697
Peter Ujfalusi381a22b2008-12-01 10:03:45 +0200698 /* Common capture gain controls */
699 SOC_DOUBLE_R_TLV("Capture Volume",
700 TWL4030_REG_ATXL1PGA, TWL4030_REG_ATXR1PGA,
701 0, 0x1f, 0, digital_capture_tlv),
Grazvydas Ignotas5920b452008-12-02 20:48:58 +0200702
703 SOC_DOUBLE_TLV("Input Boost Volume", TWL4030_REG_ANAMIC_GAIN,
704 0, 3, 5, 0, input_gain_tlv),
705
706 /* Input source controls */
707 SOC_ENUM_EXT("Left Input Source", twl4030_left_input_mux,
708 twl4030_get_left_input, twl4030_put_left_input),
709 SOC_ENUM_EXT("Right Input Source", twl4030_right_input_mux,
710 twl4030_get_right_input, twl4030_put_right_input),
Steve Sakomancc175572008-10-30 21:35:26 -0700711};
712
713/* add non dapm controls */
714static int twl4030_add_controls(struct snd_soc_codec *codec)
715{
716 int err, i;
717
718 for (i = 0; i < ARRAY_SIZE(twl4030_snd_controls); i++) {
719 err = snd_ctl_add(codec->card,
720 snd_soc_cnew(&twl4030_snd_controls[i],
721 codec, NULL));
722 if (err < 0)
723 return err;
724 }
725
726 return 0;
727}
728
729static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = {
730 SND_SOC_DAPM_INPUT("INL"),
731 SND_SOC_DAPM_INPUT("INR"),
732
733 SND_SOC_DAPM_OUTPUT("OUTL"),
734 SND_SOC_DAPM_OUTPUT("OUTR"),
Peter Ujfalusi5e98a462008-12-09 12:35:47 +0200735 SND_SOC_DAPM_OUTPUT("EARPIECE"),
Peter Ujfalusi2a6f5c582008-12-09 12:35:48 +0200736 SND_SOC_DAPM_OUTPUT("PREDRIVEL"),
737 SND_SOC_DAPM_OUTPUT("PREDRIVER"),
Peter Ujfalusidfad21a2008-12-09 12:35:49 +0200738 SND_SOC_DAPM_OUTPUT("HSOL"),
739 SND_SOC_DAPM_OUTPUT("HSOR"),
Steve Sakomancc175572008-10-30 21:35:26 -0700740
Peter Ujfalusi53b50472008-12-09 08:45:43 +0200741 /* DACs */
742 SND_SOC_DAPM_DAC("DACR1", "Right Front Playback",
743 TWL4030_REG_AVDAC_CTL, 0, 0),
744 SND_SOC_DAPM_DAC("DACL1", "Left Front Playback",
745 TWL4030_REG_AVDAC_CTL, 1, 0),
746 SND_SOC_DAPM_DAC("DACR2", "Right Rear Playback",
747 TWL4030_REG_AVDAC_CTL, 2, 0),
748 SND_SOC_DAPM_DAC("DACL2", "Left Rear Playback",
749 TWL4030_REG_AVDAC_CTL, 3, 0),
Steve Sakomancc175572008-10-30 21:35:26 -0700750
Peter Ujfalusi44c55872008-12-09 08:45:44 +0200751 /* Analog PGAs */
752 SND_SOC_DAPM_PGA("ARXR1_APGA", TWL4030_REG_ARXR1_APGA_CTL,
753 0, 0, NULL, 0),
754 SND_SOC_DAPM_PGA("ARXL1_APGA", TWL4030_REG_ARXL1_APGA_CTL,
755 0, 0, NULL, 0),
756 SND_SOC_DAPM_PGA("ARXR2_APGA", TWL4030_REG_ARXR2_APGA_CTL,
757 0, 0, NULL, 0),
758 SND_SOC_DAPM_PGA("ARXL2_APGA", TWL4030_REG_ARXL2_APGA_CTL,
759 0, 0, NULL, 0),
760
Peter Ujfalusi5e98a462008-12-09 12:35:47 +0200761 /* Output MUX controls */
762 /* Earpiece */
763 SND_SOC_DAPM_MUX_E("Earpiece Mux", SND_SOC_NOPM, 0, 0,
764 &twl4030_dapm_earpiece_control, outmixer_event,
765 SND_SOC_DAPM_PRE_REG),
Peter Ujfalusi2a6f5c582008-12-09 12:35:48 +0200766 /* PreDrivL/R */
767 SND_SOC_DAPM_MUX_E("PredriveL Mux", SND_SOC_NOPM, 0, 0,
768 &twl4030_dapm_predrivel_control, outmixer_event,
769 SND_SOC_DAPM_PRE_REG),
770 SND_SOC_DAPM_MUX_E("PredriveR Mux", SND_SOC_NOPM, 0, 0,
771 &twl4030_dapm_predriver_control, outmixer_event,
772 SND_SOC_DAPM_PRE_REG),
Peter Ujfalusidfad21a2008-12-09 12:35:49 +0200773 /* HeadsetL/R */
774 SND_SOC_DAPM_MUX("HeadsetL Mux", SND_SOC_NOPM, 0, 0,
775 &twl4030_dapm_hsol_control),
776 SND_SOC_DAPM_MUX("HeadsetR Mux", SND_SOC_NOPM, 0, 0,
777 &twl4030_dapm_hsor_control),
Peter Ujfalusi5152d8c2008-12-09 12:35:50 +0200778 /* CarkitL/R */
779 SND_SOC_DAPM_MUX("CarkitL Mux", SND_SOC_NOPM, 0, 0,
780 &twl4030_dapm_carkitl_control),
781 SND_SOC_DAPM_MUX("CarkitR Mux", SND_SOC_NOPM, 0, 0,
782 &twl4030_dapm_carkitr_control),
Peter Ujfalusi5e98a462008-12-09 12:35:47 +0200783
Steve Sakomancc175572008-10-30 21:35:26 -0700784 SND_SOC_DAPM_ADC("ADCL", "Left Capture", SND_SOC_NOPM, 0, 0),
785 SND_SOC_DAPM_ADC("ADCR", "Right Capture", SND_SOC_NOPM, 0, 0),
786};
787
788static const struct snd_soc_dapm_route intercon[] = {
Peter Ujfalusi44c55872008-12-09 08:45:44 +0200789 {"ARXL1_APGA", NULL, "DACL1"},
790 {"ARXR1_APGA", NULL, "DACR1"},
791 {"ARXL2_APGA", NULL, "DACL2"},
792 {"ARXR2_APGA", NULL, "DACR2"},
793
Peter Ujfalusi5e98a462008-12-09 12:35:47 +0200794 /* Internal playback routings */
795 /* Earpiece */
796 {"Earpiece Mux", "DACL1", "ARXL1_APGA"},
797 {"Earpiece Mux", "DACL2", "ARXL2_APGA"},
798 {"Earpiece Mux", "DACR1", "ARXR1_APGA"},
Peter Ujfalusi2a6f5c582008-12-09 12:35:48 +0200799 /* PreDrivL */
800 {"PredriveL Mux", "DACL1", "ARXL1_APGA"},
801 {"PredriveL Mux", "DACL2", "ARXL2_APGA"},
802 {"PredriveL Mux", "DACR2", "ARXR2_APGA"},
803 /* PreDrivR */
804 {"PredriveR Mux", "DACR1", "ARXR1_APGA"},
805 {"PredriveR Mux", "DACR2", "ARXR2_APGA"},
806 {"PredriveR Mux", "DACL2", "ARXL2_APGA"},
Peter Ujfalusidfad21a2008-12-09 12:35:49 +0200807 /* HeadsetL */
808 {"HeadsetL Mux", "DACL1", "ARXL1_APGA"},
809 {"HeadsetL Mux", "DACL2", "ARXL2_APGA"},
810 /* HeadsetR */
811 {"HeadsetR Mux", "DACR1", "ARXR1_APGA"},
812 {"HeadsetR Mux", "DACR2", "ARXR2_APGA"},
Peter Ujfalusi5152d8c2008-12-09 12:35:50 +0200813 /* CarkitL */
814 {"CarkitL Mux", "DACL1", "ARXL1_APGA"},
815 {"CarkitL Mux", "DACL2", "ARXL2_APGA"},
816 /* CarkitR */
817 {"CarkitR Mux", "DACR1", "ARXR1_APGA"},
818 {"CarkitR Mux", "DACR2", "ARXR2_APGA"},
Peter Ujfalusi5e98a462008-12-09 12:35:47 +0200819
Steve Sakomancc175572008-10-30 21:35:26 -0700820 /* outputs */
Peter Ujfalusi44c55872008-12-09 08:45:44 +0200821 {"OUTL", NULL, "ARXL2_APGA"},
822 {"OUTR", NULL, "ARXR2_APGA"},
Peter Ujfalusi5e98a462008-12-09 12:35:47 +0200823 {"EARPIECE", NULL, "Earpiece Mux"},
Peter Ujfalusi2a6f5c582008-12-09 12:35:48 +0200824 {"PREDRIVEL", NULL, "PredriveL Mux"},
825 {"PREDRIVER", NULL, "PredriveR Mux"},
Peter Ujfalusidfad21a2008-12-09 12:35:49 +0200826 {"HSOL", NULL, "HeadsetL Mux"},
827 {"HSOR", NULL, "HeadsetR Mux"},
Peter Ujfalusi5152d8c2008-12-09 12:35:50 +0200828 {"CARKITL", NULL, "CarkitL Mux"},
829 {"CARKITR", NULL, "CarkitR Mux"},
Steve Sakomancc175572008-10-30 21:35:26 -0700830
831 /* inputs */
832 {"ADCL", NULL, "INL"},
833 {"ADCR", NULL, "INR"},
834};
835
836static int twl4030_add_widgets(struct snd_soc_codec *codec)
837{
838 snd_soc_dapm_new_controls(codec, twl4030_dapm_widgets,
839 ARRAY_SIZE(twl4030_dapm_widgets));
840
841 snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon));
842
843 snd_soc_dapm_new_widgets(codec);
844 return 0;
845}
846
847static void twl4030_power_up(struct snd_soc_codec *codec)
848{
849 u8 anamicl, regmisc1, byte, popn, hsgain;
850 int i = 0;
851
852 /* set CODECPDZ to turn on codec */
853 twl4030_set_codecpdz(codec);
854
855 /* initiate offset cancellation */
856 anamicl = twl4030_read_reg_cache(codec, TWL4030_REG_ANAMICL);
857 twl4030_write(codec, TWL4030_REG_ANAMICL,
858 anamicl | TWL4030_CNCL_OFFSET_START);
859
860 /* wait for offset cancellation to complete */
861 do {
862 /* this takes a little while, so don't slam i2c */
863 udelay(2000);
864 twl4030_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &byte,
865 TWL4030_REG_ANAMICL);
866 } while ((i++ < 100) &&
867 ((byte & TWL4030_CNCL_OFFSET_START) ==
868 TWL4030_CNCL_OFFSET_START));
869
870 /* anti-pop when changing analog gain */
871 regmisc1 = twl4030_read_reg_cache(codec, TWL4030_REG_MISC_SET_1);
872 twl4030_write(codec, TWL4030_REG_MISC_SET_1,
873 regmisc1 | TWL4030_SMOOTH_ANAVOL_EN);
874
875 /* toggle CODECPDZ as per TRM */
876 twl4030_clear_codecpdz(codec);
877 twl4030_set_codecpdz(codec);
878
879 /* program anti-pop with bias ramp delay */
880 popn = twl4030_read_reg_cache(codec, TWL4030_REG_HS_POPN_SET);
881 popn &= TWL4030_RAMP_DELAY;
882 popn |= TWL4030_RAMP_DELAY_645MS;
883 twl4030_write(codec, TWL4030_REG_HS_POPN_SET, popn);
884 popn |= TWL4030_VMID_EN;
885 twl4030_write(codec, TWL4030_REG_HS_POPN_SET, popn);
886
887 /* enable output stage and gain setting */
888 hsgain = TWL4030_HSR_GAIN_0DB | TWL4030_HSL_GAIN_0DB;
889 twl4030_write(codec, TWL4030_REG_HS_GAIN_SET, hsgain);
890
891 /* enable anti-pop ramp */
892 popn |= TWL4030_RAMP_EN;
893 twl4030_write(codec, TWL4030_REG_HS_POPN_SET, popn);
894}
895
896static void twl4030_power_down(struct snd_soc_codec *codec)
897{
898 u8 popn, hsgain;
899
900 /* disable anti-pop ramp */
901 popn = twl4030_read_reg_cache(codec, TWL4030_REG_HS_POPN_SET);
902 popn &= ~TWL4030_RAMP_EN;
903 twl4030_write(codec, TWL4030_REG_HS_POPN_SET, popn);
904
905 /* disable output stage and gain setting */
906 hsgain = TWL4030_HSR_GAIN_PWR_DOWN | TWL4030_HSL_GAIN_PWR_DOWN;
907 twl4030_write(codec, TWL4030_REG_HS_GAIN_SET, hsgain);
908
909 /* disable bias out */
910 popn &= ~TWL4030_VMID_EN;
911 twl4030_write(codec, TWL4030_REG_HS_POPN_SET, popn);
912
913 /* power down */
914 twl4030_clear_codecpdz(codec);
915}
916
917static int twl4030_set_bias_level(struct snd_soc_codec *codec,
918 enum snd_soc_bias_level level)
919{
920 switch (level) {
921 case SND_SOC_BIAS_ON:
922 twl4030_power_up(codec);
923 break;
924 case SND_SOC_BIAS_PREPARE:
925 /* TODO: develop a twl4030_prepare function */
926 break;
927 case SND_SOC_BIAS_STANDBY:
928 /* TODO: develop a twl4030_standby function */
929 twl4030_power_down(codec);
930 break;
931 case SND_SOC_BIAS_OFF:
932 twl4030_power_down(codec);
933 break;
934 }
935 codec->bias_level = level;
936
937 return 0;
938}
939
940static int twl4030_hw_params(struct snd_pcm_substream *substream,
Mark Browndee89c42008-11-18 22:11:38 +0000941 struct snd_pcm_hw_params *params,
942 struct snd_soc_dai *dai)
Steve Sakomancc175572008-10-30 21:35:26 -0700943{
944 struct snd_soc_pcm_runtime *rtd = substream->private_data;
945 struct snd_soc_device *socdev = rtd->socdev;
946 struct snd_soc_codec *codec = socdev->codec;
947 u8 mode, old_mode, format, old_format;
948
949
950 /* bit rate */
951 old_mode = twl4030_read_reg_cache(codec,
952 TWL4030_REG_CODEC_MODE) & ~TWL4030_CODECPDZ;
953 mode = old_mode & ~TWL4030_APLL_RATE;
954
955 switch (params_rate(params)) {
956 case 8000:
957 mode |= TWL4030_APLL_RATE_8000;
958 break;
959 case 11025:
960 mode |= TWL4030_APLL_RATE_11025;
961 break;
962 case 12000:
963 mode |= TWL4030_APLL_RATE_12000;
964 break;
965 case 16000:
966 mode |= TWL4030_APLL_RATE_16000;
967 break;
968 case 22050:
969 mode |= TWL4030_APLL_RATE_22050;
970 break;
971 case 24000:
972 mode |= TWL4030_APLL_RATE_24000;
973 break;
974 case 32000:
975 mode |= TWL4030_APLL_RATE_32000;
976 break;
977 case 44100:
978 mode |= TWL4030_APLL_RATE_44100;
979 break;
980 case 48000:
981 mode |= TWL4030_APLL_RATE_48000;
982 break;
983 default:
984 printk(KERN_ERR "TWL4030 hw params: unknown rate %d\n",
985 params_rate(params));
986 return -EINVAL;
987 }
988
989 if (mode != old_mode) {
990 /* change rate and set CODECPDZ */
991 twl4030_write(codec, TWL4030_REG_CODEC_MODE, mode);
992 twl4030_set_codecpdz(codec);
993 }
994
995 /* sample size */
996 old_format = twl4030_read_reg_cache(codec, TWL4030_REG_AUDIO_IF);
997 format = old_format;
998 format &= ~TWL4030_DATA_WIDTH;
999 switch (params_format(params)) {
1000 case SNDRV_PCM_FORMAT_S16_LE:
1001 format |= TWL4030_DATA_WIDTH_16S_16W;
1002 break;
1003 case SNDRV_PCM_FORMAT_S24_LE:
1004 format |= TWL4030_DATA_WIDTH_32S_24W;
1005 break;
1006 default:
1007 printk(KERN_ERR "TWL4030 hw params: unknown format %d\n",
1008 params_format(params));
1009 return -EINVAL;
1010 }
1011
1012 if (format != old_format) {
1013
1014 /* clear CODECPDZ before changing format (codec requirement) */
1015 twl4030_clear_codecpdz(codec);
1016
1017 /* change format */
1018 twl4030_write(codec, TWL4030_REG_AUDIO_IF, format);
1019
1020 /* set CODECPDZ afterwards */
1021 twl4030_set_codecpdz(codec);
1022 }
1023 return 0;
1024}
1025
1026static int twl4030_set_dai_sysclk(struct snd_soc_dai *codec_dai,
1027 int clk_id, unsigned int freq, int dir)
1028{
1029 struct snd_soc_codec *codec = codec_dai->codec;
1030 u8 infreq;
1031
1032 switch (freq) {
1033 case 19200000:
1034 infreq = TWL4030_APLL_INFREQ_19200KHZ;
1035 break;
1036 case 26000000:
1037 infreq = TWL4030_APLL_INFREQ_26000KHZ;
1038 break;
1039 case 38400000:
1040 infreq = TWL4030_APLL_INFREQ_38400KHZ;
1041 break;
1042 default:
1043 printk(KERN_ERR "TWL4030 set sysclk: unknown rate %d\n",
1044 freq);
1045 return -EINVAL;
1046 }
1047
1048 infreq |= TWL4030_APLL_EN;
1049 twl4030_write(codec, TWL4030_REG_APLL_CTL, infreq);
1050
1051 return 0;
1052}
1053
1054static int twl4030_set_dai_fmt(struct snd_soc_dai *codec_dai,
1055 unsigned int fmt)
1056{
1057 struct snd_soc_codec *codec = codec_dai->codec;
1058 u8 old_format, format;
1059
1060 /* get format */
1061 old_format = twl4030_read_reg_cache(codec, TWL4030_REG_AUDIO_IF);
1062 format = old_format;
1063
1064 /* set master/slave audio interface */
1065 switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
1066 case SND_SOC_DAIFMT_CBM_CFM:
1067 format &= ~(TWL4030_AIF_SLAVE_EN);
Grazvydas Ignotase18c94d2008-11-05 23:51:05 +02001068 format &= ~(TWL4030_CLK256FS_EN);
Steve Sakomancc175572008-10-30 21:35:26 -07001069 break;
1070 case SND_SOC_DAIFMT_CBS_CFS:
Steve Sakomancc175572008-10-30 21:35:26 -07001071 format |= TWL4030_AIF_SLAVE_EN;
Grazvydas Ignotase18c94d2008-11-05 23:51:05 +02001072 format |= TWL4030_CLK256FS_EN;
Steve Sakomancc175572008-10-30 21:35:26 -07001073 break;
1074 default:
1075 return -EINVAL;
1076 }
1077
1078 /* interface format */
1079 format &= ~TWL4030_AIF_FORMAT;
1080 switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
1081 case SND_SOC_DAIFMT_I2S:
1082 format |= TWL4030_AIF_FORMAT_CODEC;
1083 break;
1084 default:
1085 return -EINVAL;
1086 }
1087
1088 if (format != old_format) {
1089
1090 /* clear CODECPDZ before changing format (codec requirement) */
1091 twl4030_clear_codecpdz(codec);
1092
1093 /* change format */
1094 twl4030_write(codec, TWL4030_REG_AUDIO_IF, format);
1095
1096 /* set CODECPDZ afterwards */
1097 twl4030_set_codecpdz(codec);
1098 }
1099
1100 return 0;
1101}
1102
Jarkko Nikulabbba9442008-11-12 17:05:41 +02001103#define TWL4030_RATES (SNDRV_PCM_RATE_8000_48000)
Steve Sakomancc175572008-10-30 21:35:26 -07001104#define TWL4030_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FORMAT_S24_LE)
1105
1106struct snd_soc_dai twl4030_dai = {
1107 .name = "twl4030",
1108 .playback = {
1109 .stream_name = "Playback",
1110 .channels_min = 2,
1111 .channels_max = 2,
1112 .rates = TWL4030_RATES,
1113 .formats = TWL4030_FORMATS,},
1114 .capture = {
1115 .stream_name = "Capture",
1116 .channels_min = 2,
1117 .channels_max = 2,
1118 .rates = TWL4030_RATES,
1119 .formats = TWL4030_FORMATS,},
1120 .ops = {
1121 .hw_params = twl4030_hw_params,
Steve Sakomancc175572008-10-30 21:35:26 -07001122 .set_sysclk = twl4030_set_dai_sysclk,
1123 .set_fmt = twl4030_set_dai_fmt,
1124 }
1125};
1126EXPORT_SYMBOL_GPL(twl4030_dai);
1127
1128static int twl4030_suspend(struct platform_device *pdev, pm_message_t state)
1129{
1130 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
1131 struct snd_soc_codec *codec = socdev->codec;
1132
1133 twl4030_set_bias_level(codec, SND_SOC_BIAS_OFF);
1134
1135 return 0;
1136}
1137
1138static int twl4030_resume(struct platform_device *pdev)
1139{
1140 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
1141 struct snd_soc_codec *codec = socdev->codec;
1142
1143 twl4030_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
1144 twl4030_set_bias_level(codec, codec->suspend_bias_level);
1145 return 0;
1146}
1147
1148/*
1149 * initialize the driver
1150 * register the mixer and dsp interfaces with the kernel
1151 */
1152
1153static int twl4030_init(struct snd_soc_device *socdev)
1154{
1155 struct snd_soc_codec *codec = socdev->codec;
1156 int ret = 0;
1157
1158 printk(KERN_INFO "TWL4030 Audio Codec init \n");
1159
1160 codec->name = "twl4030";
1161 codec->owner = THIS_MODULE;
1162 codec->read = twl4030_read_reg_cache;
1163 codec->write = twl4030_write;
1164 codec->set_bias_level = twl4030_set_bias_level;
1165 codec->dai = &twl4030_dai;
1166 codec->num_dai = 1;
1167 codec->reg_cache_size = sizeof(twl4030_reg);
1168 codec->reg_cache = kmemdup(twl4030_reg, sizeof(twl4030_reg),
1169 GFP_KERNEL);
1170 if (codec->reg_cache == NULL)
1171 return -ENOMEM;
1172
1173 /* register pcms */
1174 ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
1175 if (ret < 0) {
1176 printk(KERN_ERR "twl4030: failed to create pcms\n");
1177 goto pcm_err;
1178 }
1179
1180 twl4030_init_chip(codec);
1181
1182 /* power on device */
1183 twl4030_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
1184
1185 twl4030_add_controls(codec);
1186 twl4030_add_widgets(codec);
1187
Mark Brown968a6022008-11-28 11:49:07 +00001188 ret = snd_soc_init_card(socdev);
Steve Sakomancc175572008-10-30 21:35:26 -07001189 if (ret < 0) {
1190 printk(KERN_ERR "twl4030: failed to register card\n");
1191 goto card_err;
1192 }
1193
1194 return ret;
1195
1196card_err:
1197 snd_soc_free_pcms(socdev);
1198 snd_soc_dapm_free(socdev);
1199pcm_err:
1200 kfree(codec->reg_cache);
1201 return ret;
1202}
1203
1204static struct snd_soc_device *twl4030_socdev;
1205
1206static int twl4030_probe(struct platform_device *pdev)
1207{
1208 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
1209 struct snd_soc_codec *codec;
1210
1211 codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
1212 if (codec == NULL)
1213 return -ENOMEM;
1214
1215 socdev->codec = codec;
1216 mutex_init(&codec->mutex);
1217 INIT_LIST_HEAD(&codec->dapm_widgets);
1218 INIT_LIST_HEAD(&codec->dapm_paths);
1219
1220 twl4030_socdev = socdev;
1221 twl4030_init(socdev);
1222
1223 return 0;
1224}
1225
1226static int twl4030_remove(struct platform_device *pdev)
1227{
1228 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
1229 struct snd_soc_codec *codec = socdev->codec;
1230
1231 printk(KERN_INFO "TWL4030 Audio Codec remove\n");
1232 kfree(codec);
1233
1234 return 0;
1235}
1236
1237struct snd_soc_codec_device soc_codec_dev_twl4030 = {
1238 .probe = twl4030_probe,
1239 .remove = twl4030_remove,
1240 .suspend = twl4030_suspend,
1241 .resume = twl4030_resume,
1242};
1243EXPORT_SYMBOL_GPL(soc_codec_dev_twl4030);
1244
1245MODULE_DESCRIPTION("ASoC TWL4030 codec driver");
1246MODULE_AUTHOR("Steve Sakoman");
1247MODULE_LICENSE("GPL");