diff options
Diffstat (limited to 'sound/soc/codecs/twl4030.c')
-rw-r--r-- | sound/soc/codecs/twl4030.c | 388 |
1 files changed, 212 insertions, 176 deletions
diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c index b4fcdb01fc49..7b618bbff884 100644 --- a/sound/soc/codecs/twl4030.c +++ b/sound/soc/codecs/twl4030.c | |||
@@ -43,37 +43,37 @@ | |||
43 | */ | 43 | */ |
44 | static const u8 twl4030_reg[TWL4030_CACHEREGNUM] = { | 44 | static const u8 twl4030_reg[TWL4030_CACHEREGNUM] = { |
45 | 0x00, /* this register not used */ | 45 | 0x00, /* this register not used */ |
46 | 0x91, /* REG_CODEC_MODE (0x1) */ | 46 | 0x00, /* REG_CODEC_MODE (0x1) */ |
47 | 0xc3, /* REG_OPTION (0x2) */ | 47 | 0x00, /* REG_OPTION (0x2) */ |
48 | 0x00, /* REG_UNKNOWN (0x3) */ | 48 | 0x00, /* REG_UNKNOWN (0x3) */ |
49 | 0x00, /* REG_MICBIAS_CTL (0x4) */ | 49 | 0x00, /* REG_MICBIAS_CTL (0x4) */ |
50 | 0x20, /* REG_ANAMICL (0x5) */ | 50 | 0x00, /* REG_ANAMICL (0x5) */ |
51 | 0x00, /* REG_ANAMICR (0x6) */ | 51 | 0x00, /* REG_ANAMICR (0x6) */ |
52 | 0x00, /* REG_AVADC_CTL (0x7) */ | 52 | 0x00, /* REG_AVADC_CTL (0x7) */ |
53 | 0x00, /* REG_ADCMICSEL (0x8) */ | 53 | 0x00, /* REG_ADCMICSEL (0x8) */ |
54 | 0x00, /* REG_DIGMIXING (0x9) */ | 54 | 0x00, /* REG_DIGMIXING (0x9) */ |
55 | 0x0c, /* REG_ATXL1PGA (0xA) */ | 55 | 0x0f, /* REG_ATXL1PGA (0xA) */ |
56 | 0x0c, /* REG_ATXR1PGA (0xB) */ | 56 | 0x0f, /* REG_ATXR1PGA (0xB) */ |
57 | 0x00, /* REG_AVTXL2PGA (0xC) */ | 57 | 0x0f, /* REG_AVTXL2PGA (0xC) */ |
58 | 0x00, /* REG_AVTXR2PGA (0xD) */ | 58 | 0x0f, /* REG_AVTXR2PGA (0xD) */ |
59 | 0x00, /* REG_AUDIO_IF (0xE) */ | 59 | 0x00, /* REG_AUDIO_IF (0xE) */ |
60 | 0x00, /* REG_VOICE_IF (0xF) */ | 60 | 0x00, /* REG_VOICE_IF (0xF) */ |
61 | 0x00, /* REG_ARXR1PGA (0x10) */ | 61 | 0x3f, /* REG_ARXR1PGA (0x10) */ |
62 | 0x00, /* REG_ARXL1PGA (0x11) */ | 62 | 0x3f, /* REG_ARXL1PGA (0x11) */ |
63 | 0x6c, /* REG_ARXR2PGA (0x12) */ | 63 | 0x3f, /* REG_ARXR2PGA (0x12) */ |
64 | 0x6c, /* REG_ARXL2PGA (0x13) */ | 64 | 0x3f, /* REG_ARXL2PGA (0x13) */ |
65 | 0x00, /* REG_VRXPGA (0x14) */ | 65 | 0x25, /* REG_VRXPGA (0x14) */ |
66 | 0x00, /* REG_VSTPGA (0x15) */ | 66 | 0x00, /* REG_VSTPGA (0x15) */ |
67 | 0x00, /* REG_VRX2ARXPGA (0x16) */ | 67 | 0x00, /* REG_VRX2ARXPGA (0x16) */ |
68 | 0x00, /* REG_AVDAC_CTL (0x17) */ | 68 | 0x00, /* REG_AVDAC_CTL (0x17) */ |
69 | 0x00, /* REG_ARX2VTXPGA (0x18) */ | 69 | 0x00, /* REG_ARX2VTXPGA (0x18) */ |
70 | 0x00, /* REG_ARXL1_APGA_CTL (0x19) */ | 70 | 0x32, /* REG_ARXL1_APGA_CTL (0x19) */ |
71 | 0x00, /* REG_ARXR1_APGA_CTL (0x1A) */ | 71 | 0x32, /* REG_ARXR1_APGA_CTL (0x1A) */ |
72 | 0x4a, /* REG_ARXL2_APGA_CTL (0x1B) */ | 72 | 0x32, /* REG_ARXL2_APGA_CTL (0x1B) */ |
73 | 0x4a, /* REG_ARXR2_APGA_CTL (0x1C) */ | 73 | 0x32, /* REG_ARXR2_APGA_CTL (0x1C) */ |
74 | 0x00, /* REG_ATX2ARXPGA (0x1D) */ | 74 | 0x00, /* REG_ATX2ARXPGA (0x1D) */ |
75 | 0x00, /* REG_BT_IF (0x1E) */ | 75 | 0x00, /* REG_BT_IF (0x1E) */ |
76 | 0x00, /* REG_BTPGA (0x1F) */ | 76 | 0x55, /* REG_BTPGA (0x1F) */ |
77 | 0x00, /* REG_BTSTPGA (0x20) */ | 77 | 0x00, /* REG_BTSTPGA (0x20) */ |
78 | 0x00, /* REG_EAR_CTL (0x21) */ | 78 | 0x00, /* REG_EAR_CTL (0x21) */ |
79 | 0x00, /* REG_HS_SEL (0x22) */ | 79 | 0x00, /* REG_HS_SEL (0x22) */ |
@@ -85,32 +85,32 @@ static const u8 twl4030_reg[TWL4030_CACHEREGNUM] = { | |||
85 | 0x00, /* REG_PRECKR_CTL (0x28) */ | 85 | 0x00, /* REG_PRECKR_CTL (0x28) */ |
86 | 0x00, /* REG_HFL_CTL (0x29) */ | 86 | 0x00, /* REG_HFL_CTL (0x29) */ |
87 | 0x00, /* REG_HFR_CTL (0x2A) */ | 87 | 0x00, /* REG_HFR_CTL (0x2A) */ |
88 | 0x00, /* REG_ALC_CTL (0x2B) */ | 88 | 0x05, /* REG_ALC_CTL (0x2B) */ |
89 | 0x00, /* REG_ALC_SET1 (0x2C) */ | 89 | 0x00, /* REG_ALC_SET1 (0x2C) */ |
90 | 0x00, /* REG_ALC_SET2 (0x2D) */ | 90 | 0x00, /* REG_ALC_SET2 (0x2D) */ |
91 | 0x00, /* REG_BOOST_CTL (0x2E) */ | 91 | 0x00, /* REG_BOOST_CTL (0x2E) */ |
92 | 0x00, /* REG_SOFTVOL_CTL (0x2F) */ | 92 | 0x00, /* REG_SOFTVOL_CTL (0x2F) */ |
93 | 0x00, /* REG_DTMF_FREQSEL (0x30) */ | 93 | 0x13, /* REG_DTMF_FREQSEL (0x30) */ |
94 | 0x00, /* REG_DTMF_TONEXT1H (0x31) */ | 94 | 0x00, /* REG_DTMF_TONEXT1H (0x31) */ |
95 | 0x00, /* REG_DTMF_TONEXT1L (0x32) */ | 95 | 0x00, /* REG_DTMF_TONEXT1L (0x32) */ |
96 | 0x00, /* REG_DTMF_TONEXT2H (0x33) */ | 96 | 0x00, /* REG_DTMF_TONEXT2H (0x33) */ |
97 | 0x00, /* REG_DTMF_TONEXT2L (0x34) */ | 97 | 0x00, /* REG_DTMF_TONEXT2L (0x34) */ |
98 | 0x00, /* REG_DTMF_TONOFF (0x35) */ | 98 | 0x79, /* REG_DTMF_TONOFF (0x35) */ |
99 | 0x00, /* REG_DTMF_WANONOFF (0x36) */ | 99 | 0x11, /* REG_DTMF_WANONOFF (0x36) */ |
100 | 0x00, /* REG_I2S_RX_SCRAMBLE_H (0x37) */ | 100 | 0x00, /* REG_I2S_RX_SCRAMBLE_H (0x37) */ |
101 | 0x00, /* REG_I2S_RX_SCRAMBLE_M (0x38) */ | 101 | 0x00, /* REG_I2S_RX_SCRAMBLE_M (0x38) */ |
102 | 0x00, /* REG_I2S_RX_SCRAMBLE_L (0x39) */ | 102 | 0x00, /* REG_I2S_RX_SCRAMBLE_L (0x39) */ |
103 | 0x06, /* REG_APLL_CTL (0x3A) */ | 103 | 0x06, /* REG_APLL_CTL (0x3A) */ |
104 | 0x00, /* REG_DTMF_CTL (0x3B) */ | 104 | 0x00, /* REG_DTMF_CTL (0x3B) */ |
105 | 0x00, /* REG_DTMF_PGA_CTL2 (0x3C) */ | 105 | 0x44, /* REG_DTMF_PGA_CTL2 (0x3C) */ |
106 | 0x00, /* REG_DTMF_PGA_CTL1 (0x3D) */ | 106 | 0x69, /* REG_DTMF_PGA_CTL1 (0x3D) */ |
107 | 0x00, /* REG_MISC_SET_1 (0x3E) */ | 107 | 0x00, /* REG_MISC_SET_1 (0x3E) */ |
108 | 0x00, /* REG_PCMBTMUX (0x3F) */ | 108 | 0x00, /* REG_PCMBTMUX (0x3F) */ |
109 | 0x00, /* not used (0x40) */ | 109 | 0x00, /* not used (0x40) */ |
110 | 0x00, /* not used (0x41) */ | 110 | 0x00, /* not used (0x41) */ |
111 | 0x00, /* not used (0x42) */ | 111 | 0x00, /* not used (0x42) */ |
112 | 0x00, /* REG_RX_PATH_SEL (0x43) */ | 112 | 0x00, /* REG_RX_PATH_SEL (0x43) */ |
113 | 0x00, /* REG_VDL_APGA_CTL (0x44) */ | 113 | 0x32, /* REG_VDL_APGA_CTL (0x44) */ |
114 | 0x00, /* REG_VIBRA_CTL (0x45) */ | 114 | 0x00, /* REG_VIBRA_CTL (0x45) */ |
115 | 0x00, /* REG_VIBRA_SET (0x46) */ | 115 | 0x00, /* REG_VIBRA_SET (0x46) */ |
116 | 0x00, /* REG_VIBRA_PWM_SET (0x47) */ | 116 | 0x00, /* REG_VIBRA_PWM_SET (0x47) */ |
@@ -143,6 +143,9 @@ struct twl4030_priv { | |||
143 | u8 earpiece_enabled; | 143 | u8 earpiece_enabled; |
144 | u8 predrivel_enabled, predriver_enabled; | 144 | u8 predrivel_enabled, predriver_enabled; |
145 | u8 carkitl_enabled, carkitr_enabled; | 145 | u8 carkitl_enabled, carkitr_enabled; |
146 | |||
147 | /* Delay needed after enabling the digimic interface */ | ||
148 | unsigned int digimic_delay; | ||
146 | }; | 149 | }; |
147 | 150 | ||
148 | /* | 151 | /* |
@@ -244,58 +247,95 @@ static void twl4030_codec_enable(struct snd_soc_codec *codec, int enable) | |||
244 | udelay(10); | 247 | udelay(10); |
245 | } | 248 | } |
246 | 249 | ||
247 | static void twl4030_init_chip(struct snd_soc_codec *codec) | 250 | static inline void twl4030_check_defaults(struct snd_soc_codec *codec) |
248 | { | 251 | { |
249 | u8 *cache = codec->reg_cache; | 252 | int i, difference = 0; |
250 | int i; | 253 | u8 val; |
254 | |||
255 | dev_dbg(codec->dev, "Checking TWL audio default configuration\n"); | ||
256 | for (i = 1; i <= TWL4030_REG_MISC_SET_2; i++) { | ||
257 | twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &val, i); | ||
258 | if (val != twl4030_reg[i]) { | ||
259 | difference++; | ||
260 | dev_dbg(codec->dev, | ||
261 | "Reg 0x%02x: chip: 0x%02x driver: 0x%02x\n", | ||
262 | i, val, twl4030_reg[i]); | ||
263 | } | ||
264 | } | ||
265 | dev_dbg(codec->dev, "Found %d non maching registers. %s\n", | ||
266 | difference, difference ? "Not OK" : "OK"); | ||
267 | } | ||
251 | 268 | ||
252 | /* clear CODECPDZ prior to setting register defaults */ | 269 | static inline void twl4030_reset_registers(struct snd_soc_codec *codec) |
253 | twl4030_codec_enable(codec, 0); | 270 | { |
271 | int i; | ||
254 | 272 | ||
255 | /* set all audio section registers to reasonable defaults */ | 273 | /* set all audio section registers to reasonable defaults */ |
256 | for (i = TWL4030_REG_OPTION; i <= TWL4030_REG_MISC_SET_2; i++) | 274 | for (i = TWL4030_REG_OPTION; i <= TWL4030_REG_MISC_SET_2; i++) |
257 | if (i != TWL4030_REG_APLL_CTL) | 275 | if (i != TWL4030_REG_APLL_CTL) |
258 | twl4030_write(codec, i, cache[i]); | 276 | twl4030_write(codec, i, twl4030_reg[i]); |
259 | 277 | ||
260 | } | 278 | } |
261 | 279 | ||
262 | static void twl4030_apll_enable(struct snd_soc_codec *codec, int enable) | 280 | static void twl4030_init_chip(struct platform_device *pdev) |
263 | { | 281 | { |
282 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); | ||
283 | struct twl4030_setup_data *setup = socdev->codec_data; | ||
284 | struct snd_soc_codec *codec = socdev->card->codec; | ||
264 | struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec); | 285 | struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec); |
265 | int status = -1; | 286 | u8 reg, byte; |
287 | int i = 0; | ||
266 | 288 | ||
267 | if (enable) { | 289 | /* Check defaults, if instructed before anything else */ |
268 | twl4030->apll_enabled++; | 290 | if (setup && setup->check_defaults) |
269 | if (twl4030->apll_enabled == 1) | 291 | twl4030_check_defaults(codec); |
270 | status = twl4030_codec_enable_resource( | ||
271 | TWL4030_CODEC_RES_APLL); | ||
272 | } else { | ||
273 | twl4030->apll_enabled--; | ||
274 | if (!twl4030->apll_enabled) | ||
275 | status = twl4030_codec_disable_resource( | ||
276 | TWL4030_CODEC_RES_APLL); | ||
277 | } | ||
278 | 292 | ||
279 | if (status >= 0) | 293 | /* Reset registers, if no setup data or if instructed to do so */ |
280 | twl4030_write_reg_cache(codec, TWL4030_REG_APLL_CTL, status); | 294 | if (!setup || (setup && setup->reset_registers)) |
281 | } | 295 | twl4030_reset_registers(codec); |
282 | 296 | ||
283 | static void twl4030_power_up(struct snd_soc_codec *codec) | 297 | /* Refresh APLL_CTL register from HW */ |
284 | { | 298 | twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &byte, |
285 | struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec); | 299 | TWL4030_REG_APLL_CTL); |
286 | u8 anamicl, regmisc1, byte; | 300 | twl4030_write_reg_cache(codec, TWL4030_REG_APLL_CTL, byte); |
287 | int i = 0; | 301 | |
302 | /* anti-pop when changing analog gain */ | ||
303 | reg = twl4030_read_reg_cache(codec, TWL4030_REG_MISC_SET_1); | ||
304 | twl4030_write(codec, TWL4030_REG_MISC_SET_1, | ||
305 | reg | TWL4030_SMOOTH_ANAVOL_EN); | ||
288 | 306 | ||
289 | if (twl4030->codec_powered) | 307 | twl4030_write(codec, TWL4030_REG_OPTION, |
308 | TWL4030_ATXL1_EN | TWL4030_ATXR1_EN | | ||
309 | TWL4030_ARXL2_EN | TWL4030_ARXR2_EN); | ||
310 | |||
311 | /* REG_ARXR2_APGA_CTL reset according to the TRM: 0dB, DA_EN */ | ||
312 | twl4030_write(codec, TWL4030_REG_ARXR2_APGA_CTL, 0x32); | ||
313 | |||
314 | /* Machine dependent setup */ | ||
315 | if (!setup) | ||
290 | return; | 316 | return; |
291 | 317 | ||
292 | /* set CODECPDZ to turn on codec */ | 318 | twl4030->digimic_delay = setup->digimic_delay; |
293 | twl4030_codec_enable(codec, 1); | 319 | |
320 | /* Configuration for headset ramp delay from setup data */ | ||
321 | if (setup->sysclk != twl4030->sysclk) | ||
322 | dev_warn(codec->dev, | ||
323 | "Mismatch in APLL mclk: %u (configured: %u)\n", | ||
324 | setup->sysclk, twl4030->sysclk); | ||
325 | |||
326 | reg = twl4030_read_reg_cache(codec, TWL4030_REG_HS_POPN_SET); | ||
327 | reg &= ~TWL4030_RAMP_DELAY; | ||
328 | reg |= (setup->ramp_delay_value << 2); | ||
329 | twl4030_write_reg_cache(codec, TWL4030_REG_HS_POPN_SET, reg); | ||
294 | 330 | ||
295 | /* initiate offset cancellation */ | 331 | /* initiate offset cancellation */ |
296 | anamicl = twl4030_read_reg_cache(codec, TWL4030_REG_ANAMICL); | 332 | twl4030_codec_enable(codec, 1); |
333 | |||
334 | reg = twl4030_read_reg_cache(codec, TWL4030_REG_ANAMICL); | ||
335 | reg &= ~TWL4030_OFFSET_CNCL_SEL; | ||
336 | reg |= setup->offset_cncl_path; | ||
297 | twl4030_write(codec, TWL4030_REG_ANAMICL, | 337 | twl4030_write(codec, TWL4030_REG_ANAMICL, |
298 | anamicl | TWL4030_CNCL_OFFSET_START); | 338 | reg | TWL4030_CNCL_OFFSET_START); |
299 | 339 | ||
300 | /* wait for offset cancellation to complete */ | 340 | /* wait for offset cancellation to complete */ |
301 | do { | 341 | do { |
@@ -310,23 +350,28 @@ static void twl4030_power_up(struct snd_soc_codec *codec) | |||
310 | /* Make sure that the reg_cache has the same value as the HW */ | 350 | /* Make sure that the reg_cache has the same value as the HW */ |
311 | twl4030_write_reg_cache(codec, TWL4030_REG_ANAMICL, byte); | 351 | twl4030_write_reg_cache(codec, TWL4030_REG_ANAMICL, byte); |
312 | 352 | ||
313 | /* anti-pop when changing analog gain */ | ||
314 | regmisc1 = twl4030_read_reg_cache(codec, TWL4030_REG_MISC_SET_1); | ||
315 | twl4030_write(codec, TWL4030_REG_MISC_SET_1, | ||
316 | regmisc1 | TWL4030_SMOOTH_ANAVOL_EN); | ||
317 | |||
318 | /* toggle CODECPDZ as per TRM */ | ||
319 | twl4030_codec_enable(codec, 0); | 353 | twl4030_codec_enable(codec, 0); |
320 | twl4030_codec_enable(codec, 1); | ||
321 | } | 354 | } |
322 | 355 | ||
323 | /* | 356 | static void twl4030_apll_enable(struct snd_soc_codec *codec, int enable) |
324 | * Unconditional power down | ||
325 | */ | ||
326 | static void twl4030_power_down(struct snd_soc_codec *codec) | ||
327 | { | 357 | { |
328 | /* power down */ | 358 | struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec); |
329 | twl4030_codec_enable(codec, 0); | 359 | int status = -1; |
360 | |||
361 | if (enable) { | ||
362 | twl4030->apll_enabled++; | ||
363 | if (twl4030->apll_enabled == 1) | ||
364 | status = twl4030_codec_enable_resource( | ||
365 | TWL4030_CODEC_RES_APLL); | ||
366 | } else { | ||
367 | twl4030->apll_enabled--; | ||
368 | if (!twl4030->apll_enabled) | ||
369 | status = twl4030_codec_disable_resource( | ||
370 | TWL4030_CODEC_RES_APLL); | ||
371 | } | ||
372 | |||
373 | if (status >= 0) | ||
374 | twl4030_write_reg_cache(codec, TWL4030_REG_APLL_CTL, status); | ||
330 | } | 375 | } |
331 | 376 | ||
332 | /* Earpiece */ | 377 | /* Earpiece */ |
@@ -500,10 +545,11 @@ static const struct snd_kcontrol_new twl4030_dapm_abypassl2_control = | |||
500 | static const struct snd_kcontrol_new twl4030_dapm_abypassv_control = | 545 | static const struct snd_kcontrol_new twl4030_dapm_abypassv_control = |
501 | SOC_DAPM_SINGLE("Switch", TWL4030_REG_VDL_APGA_CTL, 2, 1, 0); | 546 | SOC_DAPM_SINGLE("Switch", TWL4030_REG_VDL_APGA_CTL, 2, 1, 0); |
502 | 547 | ||
503 | /* Digital bypass gain, 0 mutes the bypass */ | 548 | /* Digital bypass gain, mute instead of -30dB */ |
504 | static const unsigned int twl4030_dapm_dbypass_tlv[] = { | 549 | static const unsigned int twl4030_dapm_dbypass_tlv[] = { |
505 | TLV_DB_RANGE_HEAD(2), | 550 | TLV_DB_RANGE_HEAD(3), |
506 | 0, 3, TLV_DB_SCALE_ITEM(-2400, 0, 1), | 551 | 0, 1, TLV_DB_SCALE_ITEM(-3000, 600, 1), |
552 | 2, 3, TLV_DB_SCALE_ITEM(-2400, 0, 0), | ||
507 | 4, 7, TLV_DB_SCALE_ITEM(-1800, 600, 0), | 553 | 4, 7, TLV_DB_SCALE_ITEM(-1800, 600, 0), |
508 | }; | 554 | }; |
509 | 555 | ||
@@ -531,36 +577,6 @@ static const struct snd_kcontrol_new twl4030_dapm_dbypassv_control = | |||
531 | TWL4030_REG_VSTPGA, 0, 0x29, 0, | 577 | TWL4030_REG_VSTPGA, 0, 0x29, 0, |
532 | twl4030_dapm_dbypassv_tlv); | 578 | twl4030_dapm_dbypassv_tlv); |
533 | 579 | ||
534 | static int micpath_event(struct snd_soc_dapm_widget *w, | ||
535 | struct snd_kcontrol *kcontrol, int event) | ||
536 | { | ||
537 | struct soc_enum *e = (struct soc_enum *)w->kcontrols->private_value; | ||
538 | unsigned char adcmicsel, micbias_ctl; | ||
539 | |||
540 | adcmicsel = twl4030_read_reg_cache(w->codec, TWL4030_REG_ADCMICSEL); | ||
541 | micbias_ctl = twl4030_read_reg_cache(w->codec, TWL4030_REG_MICBIAS_CTL); | ||
542 | /* Prepare the bits for the given TX path: | ||
543 | * shift_l == 0: TX1 microphone path | ||
544 | * shift_l == 2: TX2 microphone path */ | ||
545 | if (e->shift_l) { | ||
546 | /* TX2 microphone path */ | ||
547 | if (adcmicsel & TWL4030_TX2IN_SEL) | ||
548 | micbias_ctl |= TWL4030_MICBIAS2_CTL; /* digimic */ | ||
549 | else | ||
550 | micbias_ctl &= ~TWL4030_MICBIAS2_CTL; | ||
551 | } else { | ||
552 | /* TX1 microphone path */ | ||
553 | if (adcmicsel & TWL4030_TX1IN_SEL) | ||
554 | micbias_ctl |= TWL4030_MICBIAS1_CTL; /* digimic */ | ||
555 | else | ||
556 | micbias_ctl &= ~TWL4030_MICBIAS1_CTL; | ||
557 | } | ||
558 | |||
559 | twl4030_write(w->codec, TWL4030_REG_MICBIAS_CTL, micbias_ctl); | ||
560 | |||
561 | return 0; | ||
562 | } | ||
563 | |||
564 | /* | 580 | /* |
565 | * Output PGA builder: | 581 | * Output PGA builder: |
566 | * Handle the muting and unmuting of the given output (turning off the | 582 | * Handle the muting and unmuting of the given output (turning off the |
@@ -814,6 +830,16 @@ static int headsetrpga_event(struct snd_soc_dapm_widget *w, | |||
814 | return 0; | 830 | return 0; |
815 | } | 831 | } |
816 | 832 | ||
833 | static int digimic_event(struct snd_soc_dapm_widget *w, | ||
834 | struct snd_kcontrol *kcontrol, int event) | ||
835 | { | ||
836 | struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(w->codec); | ||
837 | |||
838 | if (twl4030->digimic_delay) | ||
839 | mdelay(twl4030->digimic_delay); | ||
840 | return 0; | ||
841 | } | ||
842 | |||
817 | /* | 843 | /* |
818 | * Some of the gain controls in TWL (mostly those which are associated with | 844 | * Some of the gain controls in TWL (mostly those which are associated with |
819 | * the outputs) are implemented in an interesting way: | 845 | * the outputs) are implemented in an interesting way: |
@@ -1374,14 +1400,10 @@ static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = { | |||
1374 | /* Analog/Digital mic path selection. | 1400 | /* Analog/Digital mic path selection. |
1375 | TX1 Left/Right: either analog Left/Right or Digimic0 | 1401 | TX1 Left/Right: either analog Left/Right or Digimic0 |
1376 | TX2 Left/Right: either analog Left/Right or Digimic1 */ | 1402 | TX2 Left/Right: either analog Left/Right or Digimic1 */ |
1377 | SND_SOC_DAPM_MUX_E("TX1 Capture Route", SND_SOC_NOPM, 0, 0, | 1403 | SND_SOC_DAPM_MUX("TX1 Capture Route", SND_SOC_NOPM, 0, 0, |
1378 | &twl4030_dapm_micpathtx1_control, micpath_event, | 1404 | &twl4030_dapm_micpathtx1_control), |
1379 | SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD| | 1405 | SND_SOC_DAPM_MUX("TX2 Capture Route", SND_SOC_NOPM, 0, 0, |
1380 | SND_SOC_DAPM_POST_REG), | 1406 | &twl4030_dapm_micpathtx2_control), |
1381 | SND_SOC_DAPM_MUX_E("TX2 Capture Route", SND_SOC_NOPM, 0, 0, | ||
1382 | &twl4030_dapm_micpathtx2_control, micpath_event, | ||
1383 | SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD| | ||
1384 | SND_SOC_DAPM_POST_REG), | ||
1385 | 1407 | ||
1386 | /* Analog input mixers for the capture amplifiers */ | 1408 | /* Analog input mixers for the capture amplifiers */ |
1387 | SND_SOC_DAPM_MIXER("Analog Left", | 1409 | SND_SOC_DAPM_MIXER("Analog Left", |
@@ -1398,10 +1420,17 @@ static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = { | |||
1398 | SND_SOC_DAPM_PGA("ADC Physical Right", | 1420 | SND_SOC_DAPM_PGA("ADC Physical Right", |
1399 | TWL4030_REG_AVADC_CTL, 1, 0, NULL, 0), | 1421 | TWL4030_REG_AVADC_CTL, 1, 0, NULL, 0), |
1400 | 1422 | ||
1401 | SND_SOC_DAPM_PGA("Digimic0 Enable", | 1423 | SND_SOC_DAPM_PGA_E("Digimic0 Enable", |
1402 | TWL4030_REG_ADCMICSEL, 1, 0, NULL, 0), | 1424 | TWL4030_REG_ADCMICSEL, 1, 0, NULL, 0, |
1403 | SND_SOC_DAPM_PGA("Digimic1 Enable", | 1425 | digimic_event, SND_SOC_DAPM_POST_PMU), |
1404 | TWL4030_REG_ADCMICSEL, 3, 0, NULL, 0), | 1426 | SND_SOC_DAPM_PGA_E("Digimic1 Enable", |
1427 | TWL4030_REG_ADCMICSEL, 3, 0, NULL, 0, | ||
1428 | digimic_event, SND_SOC_DAPM_POST_PMU), | ||
1429 | |||
1430 | SND_SOC_DAPM_SUPPLY("micbias1 select", TWL4030_REG_MICBIAS_CTL, 5, 0, | ||
1431 | NULL, 0), | ||
1432 | SND_SOC_DAPM_SUPPLY("micbias2 select", TWL4030_REG_MICBIAS_CTL, 6, 0, | ||
1433 | NULL, 0), | ||
1405 | 1434 | ||
1406 | SND_SOC_DAPM_MICBIAS("Mic Bias 1", TWL4030_REG_MICBIAS_CTL, 0, 0), | 1435 | SND_SOC_DAPM_MICBIAS("Mic Bias 1", TWL4030_REG_MICBIAS_CTL, 0, 0), |
1407 | SND_SOC_DAPM_MICBIAS("Mic Bias 2", TWL4030_REG_MICBIAS_CTL, 1, 0), | 1436 | SND_SOC_DAPM_MICBIAS("Mic Bias 2", TWL4030_REG_MICBIAS_CTL, 1, 0), |
@@ -1419,8 +1448,11 @@ static const struct snd_soc_dapm_route intercon[] = { | |||
1419 | /* Supply for the digital part (APLL) */ | 1448 | /* Supply for the digital part (APLL) */ |
1420 | {"Digital Voice Playback Mixer", NULL, "APLL Enable"}, | 1449 | {"Digital Voice Playback Mixer", NULL, "APLL Enable"}, |
1421 | 1450 | ||
1422 | {"Digital R1 Playback Mixer", NULL, "AIF Enable"}, | 1451 | {"DAC Left1", NULL, "AIF Enable"}, |
1423 | {"Digital L1 Playback Mixer", NULL, "AIF Enable"}, | 1452 | {"DAC Right1", NULL, "AIF Enable"}, |
1453 | {"DAC Left2", NULL, "AIF Enable"}, | ||
1454 | {"DAC Right1", NULL, "AIF Enable"}, | ||
1455 | |||
1424 | {"Digital R2 Playback Mixer", NULL, "AIF Enable"}, | 1456 | {"Digital R2 Playback Mixer", NULL, "AIF Enable"}, |
1425 | {"Digital L2 Playback Mixer", NULL, "AIF Enable"}, | 1457 | {"Digital L2 Playback Mixer", NULL, "AIF Enable"}, |
1426 | 1458 | ||
@@ -1491,10 +1523,10 @@ static const struct snd_soc_dapm_route intercon[] = { | |||
1491 | 1523 | ||
1492 | /* outputs */ | 1524 | /* outputs */ |
1493 | /* Must be always connected (for AIF and APLL) */ | 1525 | /* Must be always connected (for AIF and APLL) */ |
1494 | {"Virtual HiFi OUT", NULL, "Digital L1 Playback Mixer"}, | 1526 | {"Virtual HiFi OUT", NULL, "DAC Left1"}, |
1495 | {"Virtual HiFi OUT", NULL, "Digital R1 Playback Mixer"}, | 1527 | {"Virtual HiFi OUT", NULL, "DAC Right1"}, |
1496 | {"Virtual HiFi OUT", NULL, "Digital L2 Playback Mixer"}, | 1528 | {"Virtual HiFi OUT", NULL, "DAC Left2"}, |
1497 | {"Virtual HiFi OUT", NULL, "Digital R2 Playback Mixer"}, | 1529 | {"Virtual HiFi OUT", NULL, "DAC Right2"}, |
1498 | /* Must be always connected (for APLL) */ | 1530 | /* Must be always connected (for APLL) */ |
1499 | {"Virtual Voice OUT", NULL, "Digital Voice Playback Mixer"}, | 1531 | {"Virtual Voice OUT", NULL, "Digital Voice Playback Mixer"}, |
1500 | /* Physical outputs */ | 1532 | /* Physical outputs */ |
@@ -1531,6 +1563,9 @@ static const struct snd_soc_dapm_route intercon[] = { | |||
1531 | {"Digimic0 Enable", NULL, "DIGIMIC0"}, | 1563 | {"Digimic0 Enable", NULL, "DIGIMIC0"}, |
1532 | {"Digimic1 Enable", NULL, "DIGIMIC1"}, | 1564 | {"Digimic1 Enable", NULL, "DIGIMIC1"}, |
1533 | 1565 | ||
1566 | {"DIGIMIC0", NULL, "micbias1 select"}, | ||
1567 | {"DIGIMIC1", NULL, "micbias2 select"}, | ||
1568 | |||
1534 | /* TX1 Left capture path */ | 1569 | /* TX1 Left capture path */ |
1535 | {"TX1 Capture Route", "Analog", "ADC Physical Left"}, | 1570 | {"TX1 Capture Route", "Analog", "ADC Physical Left"}, |
1536 | {"TX1 Capture Route", "Digimic0", "Digimic0 Enable"}, | 1571 | {"TX1 Capture Route", "Digimic0", "Digimic0 Enable"}, |
@@ -1605,10 +1640,10 @@ static int twl4030_set_bias_level(struct snd_soc_codec *codec, | |||
1605 | break; | 1640 | break; |
1606 | case SND_SOC_BIAS_STANDBY: | 1641 | case SND_SOC_BIAS_STANDBY: |
1607 | if (codec->bias_level == SND_SOC_BIAS_OFF) | 1642 | if (codec->bias_level == SND_SOC_BIAS_OFF) |
1608 | twl4030_power_up(codec); | 1643 | twl4030_codec_enable(codec, 1); |
1609 | break; | 1644 | break; |
1610 | case SND_SOC_BIAS_OFF: | 1645 | case SND_SOC_BIAS_OFF: |
1611 | twl4030_power_down(codec); | 1646 | twl4030_codec_enable(codec, 0); |
1612 | break; | 1647 | break; |
1613 | } | 1648 | } |
1614 | codec->bias_level = level; | 1649 | codec->bias_level = level; |
@@ -1794,13 +1829,6 @@ static int twl4030_hw_params(struct snd_pcm_substream *substream, | |||
1794 | return -EINVAL; | 1829 | return -EINVAL; |
1795 | } | 1830 | } |
1796 | 1831 | ||
1797 | if (mode != old_mode) { | ||
1798 | /* change rate and set CODECPDZ */ | ||
1799 | twl4030_codec_enable(codec, 0); | ||
1800 | twl4030_write(codec, TWL4030_REG_CODEC_MODE, mode); | ||
1801 | twl4030_codec_enable(codec, 1); | ||
1802 | } | ||
1803 | |||
1804 | /* sample size */ | 1832 | /* sample size */ |
1805 | old_format = twl4030_read_reg_cache(codec, TWL4030_REG_AUDIO_IF); | 1833 | old_format = twl4030_read_reg_cache(codec, TWL4030_REG_AUDIO_IF); |
1806 | format = old_format; | 1834 | format = old_format; |
@@ -1818,16 +1846,20 @@ static int twl4030_hw_params(struct snd_pcm_substream *substream, | |||
1818 | return -EINVAL; | 1846 | return -EINVAL; |
1819 | } | 1847 | } |
1820 | 1848 | ||
1821 | if (format != old_format) { | 1849 | if (format != old_format || mode != old_mode) { |
1822 | 1850 | if (twl4030->codec_powered) { | |
1823 | /* clear CODECPDZ before changing format (codec requirement) */ | 1851 | /* |
1824 | twl4030_codec_enable(codec, 0); | 1852 | * If the codec is powered, than we need to toggle the |
1825 | 1853 | * codec power. | |
1826 | /* change format */ | 1854 | */ |
1827 | twl4030_write(codec, TWL4030_REG_AUDIO_IF, format); | 1855 | twl4030_codec_enable(codec, 0); |
1828 | 1856 | twl4030_write(codec, TWL4030_REG_CODEC_MODE, mode); | |
1829 | /* set CODECPDZ afterwards */ | 1857 | twl4030_write(codec, TWL4030_REG_AUDIO_IF, format); |
1830 | twl4030_codec_enable(codec, 1); | 1858 | twl4030_codec_enable(codec, 1); |
1859 | } else { | ||
1860 | twl4030_write(codec, TWL4030_REG_CODEC_MODE, mode); | ||
1861 | twl4030_write(codec, TWL4030_REG_AUDIO_IF, format); | ||
1862 | } | ||
1831 | } | 1863 | } |
1832 | 1864 | ||
1833 | /* Store the important parameters for the DAI configuration and set | 1865 | /* Store the important parameters for the DAI configuration and set |
@@ -1877,6 +1909,7 @@ static int twl4030_set_dai_fmt(struct snd_soc_dai *codec_dai, | |||
1877 | unsigned int fmt) | 1909 | unsigned int fmt) |
1878 | { | 1910 | { |
1879 | struct snd_soc_codec *codec = codec_dai->codec; | 1911 | struct snd_soc_codec *codec = codec_dai->codec; |
1912 | struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec); | ||
1880 | u8 old_format, format; | 1913 | u8 old_format, format; |
1881 | 1914 | ||
1882 | /* get format */ | 1915 | /* get format */ |
@@ -1911,15 +1944,17 @@ static int twl4030_set_dai_fmt(struct snd_soc_dai *codec_dai, | |||
1911 | } | 1944 | } |
1912 | 1945 | ||
1913 | if (format != old_format) { | 1946 | if (format != old_format) { |
1914 | 1947 | if (twl4030->codec_powered) { | |
1915 | /* clear CODECPDZ before changing format (codec requirement) */ | 1948 | /* |
1916 | twl4030_codec_enable(codec, 0); | 1949 | * If the codec is powered, than we need to toggle the |
1917 | 1950 | * codec power. | |
1918 | /* change format */ | 1951 | */ |
1919 | twl4030_write(codec, TWL4030_REG_AUDIO_IF, format); | 1952 | twl4030_codec_enable(codec, 0); |
1920 | 1953 | twl4030_write(codec, TWL4030_REG_AUDIO_IF, format); | |
1921 | /* set CODECPDZ afterwards */ | 1954 | twl4030_codec_enable(codec, 1); |
1922 | twl4030_codec_enable(codec, 1); | 1955 | } else { |
1956 | twl4030_write(codec, TWL4030_REG_AUDIO_IF, format); | ||
1957 | } | ||
1923 | } | 1958 | } |
1924 | 1959 | ||
1925 | return 0; | 1960 | return 0; |
@@ -2011,6 +2046,7 @@ static int twl4030_voice_hw_params(struct snd_pcm_substream *substream, | |||
2011 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | 2046 | struct snd_soc_pcm_runtime *rtd = substream->private_data; |
2012 | struct snd_soc_device *socdev = rtd->socdev; | 2047 | struct snd_soc_device *socdev = rtd->socdev; |
2013 | struct snd_soc_codec *codec = socdev->card->codec; | 2048 | struct snd_soc_codec *codec = socdev->card->codec; |
2049 | struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec); | ||
2014 | u8 old_mode, mode; | 2050 | u8 old_mode, mode; |
2015 | 2051 | ||
2016 | /* Enable voice digital filters */ | 2052 | /* Enable voice digital filters */ |
@@ -2035,10 +2071,17 @@ static int twl4030_voice_hw_params(struct snd_pcm_substream *substream, | |||
2035 | } | 2071 | } |
2036 | 2072 | ||
2037 | if (mode != old_mode) { | 2073 | if (mode != old_mode) { |
2038 | /* change rate and set CODECPDZ */ | 2074 | if (twl4030->codec_powered) { |
2039 | twl4030_codec_enable(codec, 0); | 2075 | /* |
2040 | twl4030_write(codec, TWL4030_REG_CODEC_MODE, mode); | 2076 | * If the codec is powered, than we need to toggle the |
2041 | twl4030_codec_enable(codec, 1); | 2077 | * codec power. |
2078 | */ | ||
2079 | twl4030_codec_enable(codec, 0); | ||
2080 | twl4030_write(codec, TWL4030_REG_CODEC_MODE, mode); | ||
2081 | twl4030_codec_enable(codec, 1); | ||
2082 | } else { | ||
2083 | twl4030_write(codec, TWL4030_REG_CODEC_MODE, mode); | ||
2084 | } | ||
2042 | } | 2085 | } |
2043 | 2086 | ||
2044 | return 0; | 2087 | return 0; |
@@ -2068,6 +2111,7 @@ static int twl4030_voice_set_dai_fmt(struct snd_soc_dai *codec_dai, | |||
2068 | unsigned int fmt) | 2111 | unsigned int fmt) |
2069 | { | 2112 | { |
2070 | struct snd_soc_codec *codec = codec_dai->codec; | 2113 | struct snd_soc_codec *codec = codec_dai->codec; |
2114 | struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec); | ||
2071 | u8 old_format, format; | 2115 | u8 old_format, format; |
2072 | 2116 | ||
2073 | /* get format */ | 2117 | /* get format */ |
@@ -2099,10 +2143,17 @@ static int twl4030_voice_set_dai_fmt(struct snd_soc_dai *codec_dai, | |||
2099 | } | 2143 | } |
2100 | 2144 | ||
2101 | if (format != old_format) { | 2145 | if (format != old_format) { |
2102 | /* change format and set CODECPDZ */ | 2146 | if (twl4030->codec_powered) { |
2103 | twl4030_codec_enable(codec, 0); | 2147 | /* |
2104 | twl4030_write(codec, TWL4030_REG_VOICE_IF, format); | 2148 | * If the codec is powered, than we need to toggle the |
2105 | twl4030_codec_enable(codec, 1); | 2149 | * codec power. |
2150 | */ | ||
2151 | twl4030_codec_enable(codec, 0); | ||
2152 | twl4030_write(codec, TWL4030_REG_VOICE_IF, format); | ||
2153 | twl4030_codec_enable(codec, 1); | ||
2154 | } else { | ||
2155 | twl4030_write(codec, TWL4030_REG_VOICE_IF, format); | ||
2156 | } | ||
2106 | } | 2157 | } |
2107 | 2158 | ||
2108 | return 0; | 2159 | return 0; |
@@ -2202,31 +2253,15 @@ static struct snd_soc_codec *twl4030_codec; | |||
2202 | static int twl4030_soc_probe(struct platform_device *pdev) | 2253 | static int twl4030_soc_probe(struct platform_device *pdev) |
2203 | { | 2254 | { |
2204 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); | 2255 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); |
2205 | struct twl4030_setup_data *setup = socdev->codec_data; | ||
2206 | struct snd_soc_codec *codec; | 2256 | struct snd_soc_codec *codec; |
2207 | struct twl4030_priv *twl4030; | ||
2208 | int ret; | 2257 | int ret; |
2209 | 2258 | ||
2210 | BUG_ON(!twl4030_codec); | 2259 | BUG_ON(!twl4030_codec); |
2211 | 2260 | ||
2212 | codec = twl4030_codec; | 2261 | codec = twl4030_codec; |
2213 | twl4030 = snd_soc_codec_get_drvdata(codec); | ||
2214 | socdev->card->codec = codec; | 2262 | socdev->card->codec = codec; |
2215 | 2263 | ||
2216 | /* Configuration for headset ramp delay from setup data */ | 2264 | twl4030_init_chip(pdev); |
2217 | if (setup) { | ||
2218 | unsigned char hs_pop; | ||
2219 | |||
2220 | if (setup->sysclk != twl4030->sysclk) | ||
2221 | dev_warn(&pdev->dev, | ||
2222 | "Mismatch in APLL mclk: %u (configured: %u)\n", | ||
2223 | setup->sysclk, twl4030->sysclk); | ||
2224 | |||
2225 | hs_pop = twl4030_read_reg_cache(codec, TWL4030_REG_HS_POPN_SET); | ||
2226 | hs_pop &= ~TWL4030_RAMP_DELAY; | ||
2227 | hs_pop |= (setup->ramp_delay_value << 2); | ||
2228 | twl4030_write_reg_cache(codec, TWL4030_REG_HS_POPN_SET, hs_pop); | ||
2229 | } | ||
2230 | 2265 | ||
2231 | /* register pcms */ | 2266 | /* register pcms */ |
2232 | ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); | 2267 | ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); |
@@ -2247,6 +2282,8 @@ static int twl4030_soc_remove(struct platform_device *pdev) | |||
2247 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); | 2282 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); |
2248 | struct snd_soc_codec *codec = socdev->card->codec; | 2283 | struct snd_soc_codec *codec = socdev->card->codec; |
2249 | 2284 | ||
2285 | /* Reset registers to their chip default before leaving */ | ||
2286 | twl4030_reset_registers(codec); | ||
2250 | twl4030_set_bias_level(codec, SND_SOC_BIAS_OFF); | 2287 | twl4030_set_bias_level(codec, SND_SOC_BIAS_OFF); |
2251 | snd_soc_free_pcms(socdev); | 2288 | snd_soc_free_pcms(socdev); |
2252 | snd_soc_dapm_free(socdev); | 2289 | snd_soc_dapm_free(socdev); |
@@ -2287,6 +2324,7 @@ static int __devinit twl4030_codec_probe(struct platform_device *pdev) | |||
2287 | codec->read = twl4030_read_reg_cache; | 2324 | codec->read = twl4030_read_reg_cache; |
2288 | codec->write = twl4030_write; | 2325 | codec->write = twl4030_write; |
2289 | codec->set_bias_level = twl4030_set_bias_level; | 2326 | codec->set_bias_level = twl4030_set_bias_level; |
2327 | codec->idle_bias_off = 1; | ||
2290 | codec->dai = twl4030_dai; | 2328 | codec->dai = twl4030_dai; |
2291 | codec->num_dai = ARRAY_SIZE(twl4030_dai); | 2329 | codec->num_dai = ARRAY_SIZE(twl4030_dai); |
2292 | codec->reg_cache_size = sizeof(twl4030_reg); | 2330 | codec->reg_cache_size = sizeof(twl4030_reg); |
@@ -2302,9 +2340,7 @@ static int __devinit twl4030_codec_probe(struct platform_device *pdev) | |||
2302 | 2340 | ||
2303 | /* Set the defaults, and power up the codec */ | 2341 | /* Set the defaults, and power up the codec */ |
2304 | twl4030->sysclk = twl4030_codec_get_mclk() / 1000; | 2342 | twl4030->sysclk = twl4030_codec_get_mclk() / 1000; |
2305 | twl4030_init_chip(codec); | ||
2306 | codec->bias_level = SND_SOC_BIAS_OFF; | 2343 | codec->bias_level = SND_SOC_BIAS_OFF; |
2307 | twl4030_set_bias_level(codec, SND_SOC_BIAS_STANDBY); | ||
2308 | 2344 | ||
2309 | ret = snd_soc_register_codec(codec); | 2345 | ret = snd_soc_register_codec(codec); |
2310 | if (ret != 0) { | 2346 | if (ret != 0) { |
@@ -2322,7 +2358,7 @@ static int __devinit twl4030_codec_probe(struct platform_device *pdev) | |||
2322 | return 0; | 2358 | return 0; |
2323 | 2359 | ||
2324 | error_codec: | 2360 | error_codec: |
2325 | twl4030_power_down(codec); | 2361 | twl4030_codec_enable(codec, 0); |
2326 | kfree(codec->reg_cache); | 2362 | kfree(codec->reg_cache); |
2327 | error_cache: | 2363 | error_cache: |
2328 | kfree(twl4030); | 2364 | kfree(twl4030); |