diff options
-rw-r--r-- | sound/soc/codecs/twl4030.c | 72 |
1 files changed, 56 insertions, 16 deletions
diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c index 6f5d4af20052..bf59b8a4d1d7 100644 --- a/sound/soc/codecs/twl4030.c +++ b/sound/soc/codecs/twl4030.c | |||
@@ -135,9 +135,11 @@ struct twl4030_priv { | |||
135 | 135 | ||
136 | unsigned int sysclk; | 136 | unsigned int sysclk; |
137 | 137 | ||
138 | /* Headset output state handling */ | 138 | /* Output (with associated amp) states */ |
139 | unsigned int hsl_enabled; | 139 | u8 hsl_enabled, hsr_enabled; |
140 | unsigned int hsr_enabled; | 140 | u8 earpiece_enabled; |
141 | u8 predrivel_enabled, predriver_enabled; | ||
142 | u8 carkitl_enabled, carkitr_enabled; | ||
141 | }; | 143 | }; |
142 | 144 | ||
143 | /* | 145 | /* |
@@ -173,12 +175,47 @@ static inline void twl4030_write_reg_cache(struct snd_soc_codec *codec, | |||
173 | static int twl4030_write(struct snd_soc_codec *codec, | 175 | static int twl4030_write(struct snd_soc_codec *codec, |
174 | unsigned int reg, unsigned int value) | 176 | unsigned int reg, unsigned int value) |
175 | { | 177 | { |
178 | struct twl4030_priv *twl4030 = codec->private_data; | ||
179 | int write_to_reg = 0; | ||
180 | |||
176 | twl4030_write_reg_cache(codec, reg, value); | 181 | twl4030_write_reg_cache(codec, reg, value); |
177 | if (likely(reg < TWL4030_REG_SW_SHADOW)) | 182 | if (likely(reg < TWL4030_REG_SW_SHADOW)) { |
178 | return twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, value, | 183 | /* Decide if the given register can be written */ |
179 | reg); | 184 | switch (reg) { |
180 | else | 185 | case TWL4030_REG_EAR_CTL: |
181 | return 0; | 186 | if (twl4030->earpiece_enabled) |
187 | write_to_reg = 1; | ||
188 | break; | ||
189 | case TWL4030_REG_PREDL_CTL: | ||
190 | if (twl4030->predrivel_enabled) | ||
191 | write_to_reg = 1; | ||
192 | break; | ||
193 | case TWL4030_REG_PREDR_CTL: | ||
194 | if (twl4030->predriver_enabled) | ||
195 | write_to_reg = 1; | ||
196 | break; | ||
197 | case TWL4030_REG_PRECKL_CTL: | ||
198 | if (twl4030->carkitl_enabled) | ||
199 | write_to_reg = 1; | ||
200 | break; | ||
201 | case TWL4030_REG_PRECKR_CTL: | ||
202 | if (twl4030->carkitr_enabled) | ||
203 | write_to_reg = 1; | ||
204 | break; | ||
205 | case TWL4030_REG_HS_GAIN_SET: | ||
206 | if (twl4030->hsl_enabled || twl4030->hsr_enabled) | ||
207 | write_to_reg = 1; | ||
208 | break; | ||
209 | default: | ||
210 | /* All other register can be written */ | ||
211 | write_to_reg = 1; | ||
212 | break; | ||
213 | } | ||
214 | if (write_to_reg) | ||
215 | return twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, | ||
216 | value, reg); | ||
217 | } | ||
218 | return 0; | ||
182 | } | 219 | } |
183 | 220 | ||
184 | static void twl4030_codec_enable(struct snd_soc_codec *codec, int enable) | 221 | static void twl4030_codec_enable(struct snd_soc_codec *codec, int enable) |
@@ -525,26 +562,26 @@ static int micpath_event(struct snd_soc_dapm_widget *w, | |||
525 | * Output PGA builder: | 562 | * Output PGA builder: |
526 | * Handle the muting and unmuting of the given output (turning off the | 563 | * Handle the muting and unmuting of the given output (turning off the |
527 | * amplifier associated with the output pin) | 564 | * amplifier associated with the output pin) |
528 | * On mute bypass the reg_cache and mute the volume | 565 | * On mute bypass the reg_cache and write 0 to the register |
529 | * On unmute: restore the register content | 566 | * On unmute: restore the register content from the reg_cache |
530 | * Outputs handled in this way: Earpiece, PreDrivL/R, CarkitL/R | 567 | * Outputs handled in this way: Earpiece, PreDrivL/R, CarkitL/R |
531 | */ | 568 | */ |
532 | #define TWL4030_OUTPUT_PGA(pin_name, reg, mask) \ | 569 | #define TWL4030_OUTPUT_PGA(pin_name, reg, mask) \ |
533 | static int pin_name##pga_event(struct snd_soc_dapm_widget *w, \ | 570 | static int pin_name##pga_event(struct snd_soc_dapm_widget *w, \ |
534 | struct snd_kcontrol *kcontrol, int event) \ | 571 | struct snd_kcontrol *kcontrol, int event) \ |
535 | { \ | 572 | { \ |
536 | u8 reg_val; \ | 573 | struct twl4030_priv *twl4030 = w->codec->private_data; \ |
537 | \ | 574 | \ |
538 | switch (event) { \ | 575 | switch (event) { \ |
539 | case SND_SOC_DAPM_POST_PMU: \ | 576 | case SND_SOC_DAPM_POST_PMU: \ |
577 | twl4030->pin_name##_enabled = 1; \ | ||
540 | twl4030_write(w->codec, reg, \ | 578 | twl4030_write(w->codec, reg, \ |
541 | twl4030_read_reg_cache(w->codec, reg)); \ | 579 | twl4030_read_reg_cache(w->codec, reg)); \ |
542 | break; \ | 580 | break; \ |
543 | case SND_SOC_DAPM_POST_PMD: \ | 581 | case SND_SOC_DAPM_POST_PMD: \ |
544 | reg_val = twl4030_read_reg_cache(w->codec, reg); \ | 582 | twl4030->pin_name##_enabled = 0; \ |
545 | twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, \ | 583 | twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, \ |
546 | reg_val & (~mask), \ | 584 | 0, reg); \ |
547 | reg); \ | ||
548 | break; \ | 585 | break; \ |
549 | } \ | 586 | } \ |
550 | return 0; \ | 587 | return 0; \ |
@@ -664,7 +701,10 @@ static void headset_ramp(struct snd_soc_codec *codec, int ramp) | |||
664 | /* Headset ramp-up according to the TRM */ | 701 | /* Headset ramp-up according to the TRM */ |
665 | hs_pop |= TWL4030_VMID_EN; | 702 | hs_pop |= TWL4030_VMID_EN; |
666 | twl4030_write(codec, TWL4030_REG_HS_POPN_SET, hs_pop); | 703 | twl4030_write(codec, TWL4030_REG_HS_POPN_SET, hs_pop); |
667 | twl4030_write(codec, TWL4030_REG_HS_GAIN_SET, hs_gain); | 704 | /* Actually write to the register */ |
705 | twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, | ||
706 | hs_gain, | ||
707 | TWL4030_REG_HS_GAIN_SET); | ||
668 | hs_pop |= TWL4030_RAMP_EN; | 708 | hs_pop |= TWL4030_RAMP_EN; |
669 | twl4030_write(codec, TWL4030_REG_HS_POPN_SET, hs_pop); | 709 | twl4030_write(codec, TWL4030_REG_HS_POPN_SET, hs_pop); |
670 | /* Wait ramp delay time + 1, so the VMID can settle */ | 710 | /* Wait ramp delay time + 1, so the VMID can settle */ |