diff options
author | Takashi Iwai <tiwai@suse.de> | 2010-05-31 12:12:41 -0400 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2010-05-31 12:12:41 -0400 |
commit | c876ae3eb27f15620b3489d0377c2f9486cc71a0 (patch) | |
tree | 15692e9610717133204662c11fc09a69b9595f43 /sound/soc/codecs/twl4030.c | |
parent | 67a3e12b05e055c0415c556a315a3d3eb637e29e (diff) | |
parent | a3a29b55c70cefaac0d6fda170ccc85bd10e78bf (diff) |
Merge branch 'for-2.6.36' of git://git.kernel.org/pub/scm/linux/kernel/git/lrg/asoc-2.6 into topic/asoc
Diffstat (limited to 'sound/soc/codecs/twl4030.c')
-rw-r--r-- | sound/soc/codecs/twl4030.c | 291 |
1 files changed, 166 insertions, 125 deletions
diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c index b4fcdb01fc49..8d36bfa20552 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) */ |
@@ -244,58 +244,93 @@ static void twl4030_codec_enable(struct snd_soc_codec *codec, int enable) | |||
244 | udelay(10); | 244 | udelay(10); |
245 | } | 245 | } |
246 | 246 | ||
247 | static void twl4030_init_chip(struct snd_soc_codec *codec) | 247 | static inline void twl4030_check_defaults(struct snd_soc_codec *codec) |
248 | { | 248 | { |
249 | u8 *cache = codec->reg_cache; | 249 | int i, difference = 0; |
250 | int i; | 250 | u8 val; |
251 | |||
252 | dev_dbg(codec->dev, "Checking TWL audio default configuration\n"); | ||
253 | for (i = 1; i <= TWL4030_REG_MISC_SET_2; i++) { | ||
254 | twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &val, i); | ||
255 | if (val != twl4030_reg[i]) { | ||
256 | difference++; | ||
257 | dev_dbg(codec->dev, | ||
258 | "Reg 0x%02x: chip: 0x%02x driver: 0x%02x\n", | ||
259 | i, val, twl4030_reg[i]); | ||
260 | } | ||
261 | } | ||
262 | dev_dbg(codec->dev, "Found %d non maching registers. %s\n", | ||
263 | difference, difference ? "Not OK" : "OK"); | ||
264 | } | ||
251 | 265 | ||
252 | /* clear CODECPDZ prior to setting register defaults */ | 266 | static inline void twl4030_reset_registers(struct snd_soc_codec *codec) |
253 | twl4030_codec_enable(codec, 0); | 267 | { |
268 | int i; | ||
254 | 269 | ||
255 | /* set all audio section registers to reasonable defaults */ | 270 | /* set all audio section registers to reasonable defaults */ |
256 | for (i = TWL4030_REG_OPTION; i <= TWL4030_REG_MISC_SET_2; i++) | 271 | for (i = TWL4030_REG_OPTION; i <= TWL4030_REG_MISC_SET_2; i++) |
257 | if (i != TWL4030_REG_APLL_CTL) | 272 | if (i != TWL4030_REG_APLL_CTL) |
258 | twl4030_write(codec, i, cache[i]); | 273 | twl4030_write(codec, i, twl4030_reg[i]); |
259 | 274 | ||
260 | } | 275 | } |
261 | 276 | ||
262 | static void twl4030_apll_enable(struct snd_soc_codec *codec, int enable) | 277 | static void twl4030_init_chip(struct platform_device *pdev) |
263 | { | 278 | { |
279 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); | ||
280 | struct twl4030_setup_data *setup = socdev->codec_data; | ||
281 | struct snd_soc_codec *codec = socdev->card->codec; | ||
264 | struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec); | 282 | struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec); |
265 | int status = -1; | 283 | u8 reg, byte; |
284 | int i = 0; | ||
266 | 285 | ||
267 | if (enable) { | 286 | /* Check defaults, if instructed before anything else */ |
268 | twl4030->apll_enabled++; | 287 | if (setup && setup->check_defaults) |
269 | if (twl4030->apll_enabled == 1) | 288 | 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 | 289 | ||
279 | if (status >= 0) | 290 | /* Reset registers, if no setup data or if instructed to do so */ |
280 | twl4030_write_reg_cache(codec, TWL4030_REG_APLL_CTL, status); | 291 | if (!setup || (setup && setup->reset_registers)) |
281 | } | 292 | twl4030_reset_registers(codec); |
282 | 293 | ||
283 | static void twl4030_power_up(struct snd_soc_codec *codec) | 294 | /* Refresh APLL_CTL register from HW */ |
284 | { | 295 | twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &byte, |
285 | struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec); | 296 | TWL4030_REG_APLL_CTL); |
286 | u8 anamicl, regmisc1, byte; | 297 | twl4030_write_reg_cache(codec, TWL4030_REG_APLL_CTL, byte); |
287 | int i = 0; | ||
288 | 298 | ||
289 | if (twl4030->codec_powered) | 299 | /* anti-pop when changing analog gain */ |
300 | reg = twl4030_read_reg_cache(codec, TWL4030_REG_MISC_SET_1); | ||
301 | twl4030_write(codec, TWL4030_REG_MISC_SET_1, | ||
302 | reg | TWL4030_SMOOTH_ANAVOL_EN); | ||
303 | |||
304 | twl4030_write(codec, TWL4030_REG_OPTION, | ||
305 | TWL4030_ATXL1_EN | TWL4030_ATXR1_EN | | ||
306 | TWL4030_ARXL2_EN | TWL4030_ARXR2_EN); | ||
307 | |||
308 | /* REG_ARXR2_APGA_CTL reset according to the TRM: 0dB, DA_EN */ | ||
309 | twl4030_write(codec, TWL4030_REG_ARXR2_APGA_CTL, 0x32); | ||
310 | |||
311 | /* Machine dependent setup */ | ||
312 | if (!setup) | ||
290 | return; | 313 | return; |
291 | 314 | ||
292 | /* set CODECPDZ to turn on codec */ | 315 | /* Configuration for headset ramp delay from setup data */ |
293 | twl4030_codec_enable(codec, 1); | 316 | if (setup->sysclk != twl4030->sysclk) |
317 | dev_warn(codec->dev, | ||
318 | "Mismatch in APLL mclk: %u (configured: %u)\n", | ||
319 | setup->sysclk, twl4030->sysclk); | ||
320 | |||
321 | reg = twl4030_read_reg_cache(codec, TWL4030_REG_HS_POPN_SET); | ||
322 | reg &= ~TWL4030_RAMP_DELAY; | ||
323 | reg |= (setup->ramp_delay_value << 2); | ||
324 | twl4030_write_reg_cache(codec, TWL4030_REG_HS_POPN_SET, reg); | ||
294 | 325 | ||
295 | /* initiate offset cancellation */ | 326 | /* initiate offset cancellation */ |
296 | anamicl = twl4030_read_reg_cache(codec, TWL4030_REG_ANAMICL); | 327 | twl4030_codec_enable(codec, 1); |
328 | |||
329 | reg = twl4030_read_reg_cache(codec, TWL4030_REG_ANAMICL); | ||
330 | reg &= ~TWL4030_OFFSET_CNCL_SEL; | ||
331 | reg |= setup->offset_cncl_path; | ||
297 | twl4030_write(codec, TWL4030_REG_ANAMICL, | 332 | twl4030_write(codec, TWL4030_REG_ANAMICL, |
298 | anamicl | TWL4030_CNCL_OFFSET_START); | 333 | reg | TWL4030_CNCL_OFFSET_START); |
299 | 334 | ||
300 | /* wait for offset cancellation to complete */ | 335 | /* wait for offset cancellation to complete */ |
301 | do { | 336 | do { |
@@ -310,23 +345,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 */ | 345 | /* Make sure that the reg_cache has the same value as the HW */ |
311 | twl4030_write_reg_cache(codec, TWL4030_REG_ANAMICL, byte); | 346 | twl4030_write_reg_cache(codec, TWL4030_REG_ANAMICL, byte); |
312 | 347 | ||
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); | 348 | twl4030_codec_enable(codec, 0); |
320 | twl4030_codec_enable(codec, 1); | ||
321 | } | 349 | } |
322 | 350 | ||
323 | /* | 351 | 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 | { | 352 | { |
328 | /* power down */ | 353 | struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec); |
329 | twl4030_codec_enable(codec, 0); | 354 | int status = -1; |
355 | |||
356 | if (enable) { | ||
357 | twl4030->apll_enabled++; | ||
358 | if (twl4030->apll_enabled == 1) | ||
359 | status = twl4030_codec_enable_resource( | ||
360 | TWL4030_CODEC_RES_APLL); | ||
361 | } else { | ||
362 | twl4030->apll_enabled--; | ||
363 | if (!twl4030->apll_enabled) | ||
364 | status = twl4030_codec_disable_resource( | ||
365 | TWL4030_CODEC_RES_APLL); | ||
366 | } | ||
367 | |||
368 | if (status >= 0) | ||
369 | twl4030_write_reg_cache(codec, TWL4030_REG_APLL_CTL, status); | ||
330 | } | 370 | } |
331 | 371 | ||
332 | /* Earpiece */ | 372 | /* Earpiece */ |
@@ -1605,10 +1645,10 @@ static int twl4030_set_bias_level(struct snd_soc_codec *codec, | |||
1605 | break; | 1645 | break; |
1606 | case SND_SOC_BIAS_STANDBY: | 1646 | case SND_SOC_BIAS_STANDBY: |
1607 | if (codec->bias_level == SND_SOC_BIAS_OFF) | 1647 | if (codec->bias_level == SND_SOC_BIAS_OFF) |
1608 | twl4030_power_up(codec); | 1648 | twl4030_codec_enable(codec, 1); |
1609 | break; | 1649 | break; |
1610 | case SND_SOC_BIAS_OFF: | 1650 | case SND_SOC_BIAS_OFF: |
1611 | twl4030_power_down(codec); | 1651 | twl4030_codec_enable(codec, 0); |
1612 | break; | 1652 | break; |
1613 | } | 1653 | } |
1614 | codec->bias_level = level; | 1654 | codec->bias_level = level; |
@@ -1794,13 +1834,6 @@ static int twl4030_hw_params(struct snd_pcm_substream *substream, | |||
1794 | return -EINVAL; | 1834 | return -EINVAL; |
1795 | } | 1835 | } |
1796 | 1836 | ||
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 */ | 1837 | /* sample size */ |
1805 | old_format = twl4030_read_reg_cache(codec, TWL4030_REG_AUDIO_IF); | 1838 | old_format = twl4030_read_reg_cache(codec, TWL4030_REG_AUDIO_IF); |
1806 | format = old_format; | 1839 | format = old_format; |
@@ -1818,16 +1851,20 @@ static int twl4030_hw_params(struct snd_pcm_substream *substream, | |||
1818 | return -EINVAL; | 1851 | return -EINVAL; |
1819 | } | 1852 | } |
1820 | 1853 | ||
1821 | if (format != old_format) { | 1854 | if (format != old_format || mode != old_mode) { |
1822 | 1855 | if (twl4030->codec_powered) { | |
1823 | /* clear CODECPDZ before changing format (codec requirement) */ | 1856 | /* |
1824 | twl4030_codec_enable(codec, 0); | 1857 | * If the codec is powered, than we need to toggle the |
1825 | 1858 | * codec power. | |
1826 | /* change format */ | 1859 | */ |
1827 | twl4030_write(codec, TWL4030_REG_AUDIO_IF, format); | 1860 | twl4030_codec_enable(codec, 0); |
1828 | 1861 | twl4030_write(codec, TWL4030_REG_CODEC_MODE, mode); | |
1829 | /* set CODECPDZ afterwards */ | 1862 | twl4030_write(codec, TWL4030_REG_AUDIO_IF, format); |
1830 | twl4030_codec_enable(codec, 1); | 1863 | twl4030_codec_enable(codec, 1); |
1864 | } else { | ||
1865 | twl4030_write(codec, TWL4030_REG_CODEC_MODE, mode); | ||
1866 | twl4030_write(codec, TWL4030_REG_AUDIO_IF, format); | ||
1867 | } | ||
1831 | } | 1868 | } |
1832 | 1869 | ||
1833 | /* Store the important parameters for the DAI configuration and set | 1870 | /* Store the important parameters for the DAI configuration and set |
@@ -1877,6 +1914,7 @@ static int twl4030_set_dai_fmt(struct snd_soc_dai *codec_dai, | |||
1877 | unsigned int fmt) | 1914 | unsigned int fmt) |
1878 | { | 1915 | { |
1879 | struct snd_soc_codec *codec = codec_dai->codec; | 1916 | struct snd_soc_codec *codec = codec_dai->codec; |
1917 | struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec); | ||
1880 | u8 old_format, format; | 1918 | u8 old_format, format; |
1881 | 1919 | ||
1882 | /* get format */ | 1920 | /* get format */ |
@@ -1911,15 +1949,17 @@ static int twl4030_set_dai_fmt(struct snd_soc_dai *codec_dai, | |||
1911 | } | 1949 | } |
1912 | 1950 | ||
1913 | if (format != old_format) { | 1951 | if (format != old_format) { |
1914 | 1952 | if (twl4030->codec_powered) { | |
1915 | /* clear CODECPDZ before changing format (codec requirement) */ | 1953 | /* |
1916 | twl4030_codec_enable(codec, 0); | 1954 | * If the codec is powered, than we need to toggle the |
1917 | 1955 | * codec power. | |
1918 | /* change format */ | 1956 | */ |
1919 | twl4030_write(codec, TWL4030_REG_AUDIO_IF, format); | 1957 | twl4030_codec_enable(codec, 0); |
1920 | 1958 | twl4030_write(codec, TWL4030_REG_AUDIO_IF, format); | |
1921 | /* set CODECPDZ afterwards */ | 1959 | twl4030_codec_enable(codec, 1); |
1922 | twl4030_codec_enable(codec, 1); | 1960 | } else { |
1961 | twl4030_write(codec, TWL4030_REG_AUDIO_IF, format); | ||
1962 | } | ||
1923 | } | 1963 | } |
1924 | 1964 | ||
1925 | return 0; | 1965 | return 0; |
@@ -2011,6 +2051,7 @@ static int twl4030_voice_hw_params(struct snd_pcm_substream *substream, | |||
2011 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | 2051 | struct snd_soc_pcm_runtime *rtd = substream->private_data; |
2012 | struct snd_soc_device *socdev = rtd->socdev; | 2052 | struct snd_soc_device *socdev = rtd->socdev; |
2013 | struct snd_soc_codec *codec = socdev->card->codec; | 2053 | struct snd_soc_codec *codec = socdev->card->codec; |
2054 | struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec); | ||
2014 | u8 old_mode, mode; | 2055 | u8 old_mode, mode; |
2015 | 2056 | ||
2016 | /* Enable voice digital filters */ | 2057 | /* Enable voice digital filters */ |
@@ -2035,10 +2076,17 @@ static int twl4030_voice_hw_params(struct snd_pcm_substream *substream, | |||
2035 | } | 2076 | } |
2036 | 2077 | ||
2037 | if (mode != old_mode) { | 2078 | if (mode != old_mode) { |
2038 | /* change rate and set CODECPDZ */ | 2079 | if (twl4030->codec_powered) { |
2039 | twl4030_codec_enable(codec, 0); | 2080 | /* |
2040 | twl4030_write(codec, TWL4030_REG_CODEC_MODE, mode); | 2081 | * If the codec is powered, than we need to toggle the |
2041 | twl4030_codec_enable(codec, 1); | 2082 | * codec power. |
2083 | */ | ||
2084 | twl4030_codec_enable(codec, 0); | ||
2085 | twl4030_write(codec, TWL4030_REG_CODEC_MODE, mode); | ||
2086 | twl4030_codec_enable(codec, 1); | ||
2087 | } else { | ||
2088 | twl4030_write(codec, TWL4030_REG_CODEC_MODE, mode); | ||
2089 | } | ||
2042 | } | 2090 | } |
2043 | 2091 | ||
2044 | return 0; | 2092 | return 0; |
@@ -2068,6 +2116,7 @@ static int twl4030_voice_set_dai_fmt(struct snd_soc_dai *codec_dai, | |||
2068 | unsigned int fmt) | 2116 | unsigned int fmt) |
2069 | { | 2117 | { |
2070 | struct snd_soc_codec *codec = codec_dai->codec; | 2118 | struct snd_soc_codec *codec = codec_dai->codec; |
2119 | struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec); | ||
2071 | u8 old_format, format; | 2120 | u8 old_format, format; |
2072 | 2121 | ||
2073 | /* get format */ | 2122 | /* get format */ |
@@ -2099,10 +2148,17 @@ static int twl4030_voice_set_dai_fmt(struct snd_soc_dai *codec_dai, | |||
2099 | } | 2148 | } |
2100 | 2149 | ||
2101 | if (format != old_format) { | 2150 | if (format != old_format) { |
2102 | /* change format and set CODECPDZ */ | 2151 | if (twl4030->codec_powered) { |
2103 | twl4030_codec_enable(codec, 0); | 2152 | /* |
2104 | twl4030_write(codec, TWL4030_REG_VOICE_IF, format); | 2153 | * If the codec is powered, than we need to toggle the |
2105 | twl4030_codec_enable(codec, 1); | 2154 | * codec power. |
2155 | */ | ||
2156 | twl4030_codec_enable(codec, 0); | ||
2157 | twl4030_write(codec, TWL4030_REG_VOICE_IF, format); | ||
2158 | twl4030_codec_enable(codec, 1); | ||
2159 | } else { | ||
2160 | twl4030_write(codec, TWL4030_REG_VOICE_IF, format); | ||
2161 | } | ||
2106 | } | 2162 | } |
2107 | 2163 | ||
2108 | return 0; | 2164 | return 0; |
@@ -2202,31 +2258,15 @@ static struct snd_soc_codec *twl4030_codec; | |||
2202 | static int twl4030_soc_probe(struct platform_device *pdev) | 2258 | static int twl4030_soc_probe(struct platform_device *pdev) |
2203 | { | 2259 | { |
2204 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); | 2260 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); |
2205 | struct twl4030_setup_data *setup = socdev->codec_data; | ||
2206 | struct snd_soc_codec *codec; | 2261 | struct snd_soc_codec *codec; |
2207 | struct twl4030_priv *twl4030; | ||
2208 | int ret; | 2262 | int ret; |
2209 | 2263 | ||
2210 | BUG_ON(!twl4030_codec); | 2264 | BUG_ON(!twl4030_codec); |
2211 | 2265 | ||
2212 | codec = twl4030_codec; | 2266 | codec = twl4030_codec; |
2213 | twl4030 = snd_soc_codec_get_drvdata(codec); | ||
2214 | socdev->card->codec = codec; | 2267 | socdev->card->codec = codec; |
2215 | 2268 | ||
2216 | /* Configuration for headset ramp delay from setup data */ | 2269 | 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 | 2270 | ||
2231 | /* register pcms */ | 2271 | /* register pcms */ |
2232 | ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); | 2272 | ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); |
@@ -2247,6 +2287,8 @@ static int twl4030_soc_remove(struct platform_device *pdev) | |||
2247 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); | 2287 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); |
2248 | struct snd_soc_codec *codec = socdev->card->codec; | 2288 | struct snd_soc_codec *codec = socdev->card->codec; |
2249 | 2289 | ||
2290 | /* Reset registers to their chip default before leaving */ | ||
2291 | twl4030_reset_registers(codec); | ||
2250 | twl4030_set_bias_level(codec, SND_SOC_BIAS_OFF); | 2292 | twl4030_set_bias_level(codec, SND_SOC_BIAS_OFF); |
2251 | snd_soc_free_pcms(socdev); | 2293 | snd_soc_free_pcms(socdev); |
2252 | snd_soc_dapm_free(socdev); | 2294 | snd_soc_dapm_free(socdev); |
@@ -2287,6 +2329,7 @@ static int __devinit twl4030_codec_probe(struct platform_device *pdev) | |||
2287 | codec->read = twl4030_read_reg_cache; | 2329 | codec->read = twl4030_read_reg_cache; |
2288 | codec->write = twl4030_write; | 2330 | codec->write = twl4030_write; |
2289 | codec->set_bias_level = twl4030_set_bias_level; | 2331 | codec->set_bias_level = twl4030_set_bias_level; |
2332 | codec->idle_bias_off = 1; | ||
2290 | codec->dai = twl4030_dai; | 2333 | codec->dai = twl4030_dai; |
2291 | codec->num_dai = ARRAY_SIZE(twl4030_dai); | 2334 | codec->num_dai = ARRAY_SIZE(twl4030_dai); |
2292 | codec->reg_cache_size = sizeof(twl4030_reg); | 2335 | codec->reg_cache_size = sizeof(twl4030_reg); |
@@ -2302,9 +2345,7 @@ static int __devinit twl4030_codec_probe(struct platform_device *pdev) | |||
2302 | 2345 | ||
2303 | /* Set the defaults, and power up the codec */ | 2346 | /* Set the defaults, and power up the codec */ |
2304 | twl4030->sysclk = twl4030_codec_get_mclk() / 1000; | 2347 | twl4030->sysclk = twl4030_codec_get_mclk() / 1000; |
2305 | twl4030_init_chip(codec); | ||
2306 | codec->bias_level = SND_SOC_BIAS_OFF; | 2348 | codec->bias_level = SND_SOC_BIAS_OFF; |
2307 | twl4030_set_bias_level(codec, SND_SOC_BIAS_STANDBY); | ||
2308 | 2349 | ||
2309 | ret = snd_soc_register_codec(codec); | 2350 | ret = snd_soc_register_codec(codec); |
2310 | if (ret != 0) { | 2351 | if (ret != 0) { |
@@ -2322,7 +2363,7 @@ static int __devinit twl4030_codec_probe(struct platform_device *pdev) | |||
2322 | return 0; | 2363 | return 0; |
2323 | 2364 | ||
2324 | error_codec: | 2365 | error_codec: |
2325 | twl4030_power_down(codec); | 2366 | twl4030_codec_enable(codec, 0); |
2326 | kfree(codec->reg_cache); | 2367 | kfree(codec->reg_cache); |
2327 | error_cache: | 2368 | error_cache: |
2328 | kfree(twl4030); | 2369 | kfree(twl4030); |