+static void twl4030_codec_mute(struct snd_soc_codec *codec, int mute)
+{
+ struct twl4030_priv *twl4030 = codec->private_data;
+ u8 reg_val;
+
+ if (mute == twl4030->codec_muted)
+ return;
+
+ if (mute) {
+ /* Bypass the reg_cache and mute the volumes
+ * Headset mute is done in it's own event handler
+ * Things to mute: Earpiece, PreDrivL/R, CarkitL/R
+ */
+ reg_val = twl4030_read_reg_cache(codec, TWL4030_REG_EAR_CTL);
+ twl4030_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE,
+ reg_val & (~TWL4030_EAR_GAIN),
+ TWL4030_REG_EAR_CTL);
+
+ reg_val = twl4030_read_reg_cache(codec, TWL4030_REG_PREDL_CTL);
+ twl4030_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE,
+ reg_val & (~TWL4030_PREDL_GAIN),
+ TWL4030_REG_PREDL_CTL);
+ reg_val = twl4030_read_reg_cache(codec, TWL4030_REG_PREDR_CTL);
+ twl4030_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE,
+ reg_val & (~TWL4030_PREDR_GAIN),
+ TWL4030_REG_PREDL_CTL);
+
+ reg_val = twl4030_read_reg_cache(codec, TWL4030_REG_PRECKL_CTL);
+ twl4030_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE,
+ reg_val & (~TWL4030_PRECKL_GAIN),
+ TWL4030_REG_PRECKL_CTL);
+ reg_val = twl4030_read_reg_cache(codec, TWL4030_REG_PRECKR_CTL);
+ twl4030_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE,
+ reg_val & (~TWL4030_PRECKL_GAIN),
+ TWL4030_REG_PRECKR_CTL);
+
+ /* Disable PLL */
+ reg_val = twl4030_read_reg_cache(codec, TWL4030_REG_APLL_CTL);
+ reg_val &= ~TWL4030_APLL_EN;
+ twl4030_write(codec, TWL4030_REG_APLL_CTL, reg_val);
+ } else {
+ /* Restore the volumes
+ * Headset mute is done in it's own event handler
+ * Things to restore: Earpiece, PreDrivL/R, CarkitL/R
+ */
+ twl4030_write(codec, TWL4030_REG_EAR_CTL,
+ twl4030_read_reg_cache(codec, TWL4030_REG_EAR_CTL));
+
+ twl4030_write(codec, TWL4030_REG_PREDL_CTL,
+ twl4030_read_reg_cache(codec, TWL4030_REG_PREDL_CTL));
+ twl4030_write(codec, TWL4030_REG_PREDR_CTL,
+ twl4030_read_reg_cache(codec, TWL4030_REG_PREDR_CTL));
+
+ twl4030_write(codec, TWL4030_REG_PRECKL_CTL,
+ twl4030_read_reg_cache(codec, TWL4030_REG_PRECKL_CTL));
+ twl4030_write(codec, TWL4030_REG_PRECKR_CTL,
+ twl4030_read_reg_cache(codec, TWL4030_REG_PRECKR_CTL));
+
+ /* Enable PLL */
+ reg_val = twl4030_read_reg_cache(codec, TWL4030_REG_APLL_CTL);
+ reg_val |= TWL4030_APLL_EN;
+ twl4030_write(codec, TWL4030_REG_APLL_CTL, reg_val);
+ }
+
+ twl4030->codec_muted = mute;
+}
+
+static void twl4030_power_up(struct snd_soc_codec *codec)
+{
+ struct twl4030_priv *twl4030 = codec->private_data;
+ u8 anamicl, regmisc1, byte;
+ int i = 0;
+
+ if (twl4030->codec_powered)
+ return;
+
+ /* set CODECPDZ to turn on codec */
+ twl4030_codec_enable(codec, 1);
+
+ /* initiate offset cancellation */
+ anamicl = twl4030_read_reg_cache(codec, TWL4030_REG_ANAMICL);
+ twl4030_write(codec, TWL4030_REG_ANAMICL,
+ anamicl | TWL4030_CNCL_OFFSET_START);
+
+ /* wait for offset cancellation to complete */
+ do {
+ /* this takes a little while, so don't slam i2c */
+ udelay(2000);
+ twl4030_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &byte,
+ TWL4030_REG_ANAMICL);
+ } while ((i++ < 100) &&
+ ((byte & TWL4030_CNCL_OFFSET_START) ==
+ TWL4030_CNCL_OFFSET_START));
+
+ /* Make sure that the reg_cache has the same value as the HW */
+ twl4030_write_reg_cache(codec, TWL4030_REG_ANAMICL, byte);
+
+ /* anti-pop when changing analog gain */
+ regmisc1 = twl4030_read_reg_cache(codec, TWL4030_REG_MISC_SET_1);
+ twl4030_write(codec, TWL4030_REG_MISC_SET_1,
+ regmisc1 | TWL4030_SMOOTH_ANAVOL_EN);
+
+ /* toggle CODECPDZ as per TRM */
+ twl4030_codec_enable(codec, 0);
+ twl4030_codec_enable(codec, 1);
+}
+
+/*
+ * Unconditional power down
+ */
+static void twl4030_power_down(struct snd_soc_codec *codec)
+{
+ /* power down */
+ twl4030_codec_enable(codec, 0);
+}
+