|
@@ -135,9 +135,11 @@ struct twl4030_priv {
|
|
|
|
|
|
unsigned int sysclk;
|
|
|
|
|
|
- /* Headset output state handling */
|
|
|
- unsigned int hsl_enabled;
|
|
|
- unsigned int hsr_enabled;
|
|
|
+ /* Output (with associated amp) states */
|
|
|
+ u8 hsl_enabled, hsr_enabled;
|
|
|
+ u8 earpiece_enabled;
|
|
|
+ u8 predrivel_enabled, predriver_enabled;
|
|
|
+ u8 carkitl_enabled, carkitr_enabled;
|
|
|
};
|
|
|
|
|
|
/*
|
|
@@ -173,12 +175,47 @@ static inline void twl4030_write_reg_cache(struct snd_soc_codec *codec,
|
|
|
static int twl4030_write(struct snd_soc_codec *codec,
|
|
|
unsigned int reg, unsigned int value)
|
|
|
{
|
|
|
+ struct twl4030_priv *twl4030 = codec->private_data;
|
|
|
+ int write_to_reg = 0;
|
|
|
+
|
|
|
twl4030_write_reg_cache(codec, reg, value);
|
|
|
- if (likely(reg < TWL4030_REG_SW_SHADOW))
|
|
|
- return twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, value,
|
|
|
- reg);
|
|
|
- else
|
|
|
- return 0;
|
|
|
+ if (likely(reg < TWL4030_REG_SW_SHADOW)) {
|
|
|
+ /* Decide if the given register can be written */
|
|
|
+ switch (reg) {
|
|
|
+ case TWL4030_REG_EAR_CTL:
|
|
|
+ if (twl4030->earpiece_enabled)
|
|
|
+ write_to_reg = 1;
|
|
|
+ break;
|
|
|
+ case TWL4030_REG_PREDL_CTL:
|
|
|
+ if (twl4030->predrivel_enabled)
|
|
|
+ write_to_reg = 1;
|
|
|
+ break;
|
|
|
+ case TWL4030_REG_PREDR_CTL:
|
|
|
+ if (twl4030->predriver_enabled)
|
|
|
+ write_to_reg = 1;
|
|
|
+ break;
|
|
|
+ case TWL4030_REG_PRECKL_CTL:
|
|
|
+ if (twl4030->carkitl_enabled)
|
|
|
+ write_to_reg = 1;
|
|
|
+ break;
|
|
|
+ case TWL4030_REG_PRECKR_CTL:
|
|
|
+ if (twl4030->carkitr_enabled)
|
|
|
+ write_to_reg = 1;
|
|
|
+ break;
|
|
|
+ case TWL4030_REG_HS_GAIN_SET:
|
|
|
+ if (twl4030->hsl_enabled || twl4030->hsr_enabled)
|
|
|
+ write_to_reg = 1;
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ /* All other register can be written */
|
|
|
+ write_to_reg = 1;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ if (write_to_reg)
|
|
|
+ return twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE,
|
|
|
+ value, reg);
|
|
|
+ }
|
|
|
+ return 0;
|
|
|
}
|
|
|
|
|
|
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,
|
|
|
* Output PGA builder:
|
|
|
* Handle the muting and unmuting of the given output (turning off the
|
|
|
* amplifier associated with the output pin)
|
|
|
- * On mute bypass the reg_cache and mute the volume
|
|
|
- * On unmute: restore the register content
|
|
|
+ * On mute bypass the reg_cache and write 0 to the register
|
|
|
+ * On unmute: restore the register content from the reg_cache
|
|
|
* Outputs handled in this way: Earpiece, PreDrivL/R, CarkitL/R
|
|
|
*/
|
|
|
#define TWL4030_OUTPUT_PGA(pin_name, reg, mask) \
|
|
|
static int pin_name##pga_event(struct snd_soc_dapm_widget *w, \
|
|
|
struct snd_kcontrol *kcontrol, int event) \
|
|
|
{ \
|
|
|
- u8 reg_val; \
|
|
|
+ struct twl4030_priv *twl4030 = w->codec->private_data; \
|
|
|
\
|
|
|
switch (event) { \
|
|
|
case SND_SOC_DAPM_POST_PMU: \
|
|
|
+ twl4030->pin_name##_enabled = 1; \
|
|
|
twl4030_write(w->codec, reg, \
|
|
|
twl4030_read_reg_cache(w->codec, reg)); \
|
|
|
break; \
|
|
|
case SND_SOC_DAPM_POST_PMD: \
|
|
|
- reg_val = twl4030_read_reg_cache(w->codec, reg); \
|
|
|
- twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, \
|
|
|
- reg_val & (~mask), \
|
|
|
- reg); \
|
|
|
+ twl4030->pin_name##_enabled = 0; \
|
|
|
+ twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, \
|
|
|
+ 0, reg); \
|
|
|
break; \
|
|
|
} \
|
|
|
return 0; \
|
|
@@ -664,7 +701,10 @@ static void headset_ramp(struct snd_soc_codec *codec, int ramp)
|
|
|
/* Headset ramp-up according to the TRM */
|
|
|
hs_pop |= TWL4030_VMID_EN;
|
|
|
twl4030_write(codec, TWL4030_REG_HS_POPN_SET, hs_pop);
|
|
|
- twl4030_write(codec, TWL4030_REG_HS_GAIN_SET, hs_gain);
|
|
|
+ /* Actually write to the register */
|
|
|
+ twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE,
|
|
|
+ hs_gain,
|
|
|
+ TWL4030_REG_HS_GAIN_SET);
|
|
|
hs_pop |= TWL4030_RAMP_EN;
|
|
|
twl4030_write(codec, TWL4030_REG_HS_POPN_SET, hs_pop);
|
|
|
/* Wait ramp delay time + 1, so the VMID can settle */
|