From 4484d2af3c42058ef026c11d7ed2fa96ba2e3a4b Mon Sep 17 00:00:00 2001
From: Prabhu Annabathula <prabhu.annabathula@motorola.com>
Date: Tue, 26 Aug 2014 21:05:12 -0500
Subject: [PATCH] ASoC TFA9890: add controls to enable and disable spkr
 protection

when speaker is in handset mode speaker protection should be
disabled as dsp is bypassed. Added controls to have the ability
to enable and disable speaker protection.

Change-Id: Id735dbd28f716fa7de485dcd1477e9dcb378e46a
Signed-off-by: Prabhu Annabathula <prabhu.annabathula@motorola.com>
---
 sound/soc/codecs/tfa9890-core.h |  9 ++++
 sound/soc/codecs/tfa9890.c      | 85 +++++++++++++++++++++++++++++++++
 2 files changed, 94 insertions(+)

diff --git a/sound/soc/codecs/tfa9890-core.h b/sound/soc/codecs/tfa9890-core.h
index 1cca6bd2ce4f..bc2483119174 100644
--- a/sound/soc/codecs/tfa9890-core.h
+++ b/sound/soc/codecs/tfa9890-core.h
@@ -152,6 +152,15 @@
 #define TFA9890_I2S_CHS12		(0x3 << 3)
 #define TFA9890_DOLS_DATAO		(0x7)
 #define TFA9890_DORS_DATAO		(0x7 << 3)
+#define TFA9890_I2SREG_CHSA_MSK (0x3 << 6)
+#define TFA9890_I2SREG_CHSA_VAL (0x2 << 6)
+#define TFA9890_SYS_CTRL2_REG_DCFG_MSK	(0x7800)
+#define TFA9890_SYS_CTRL2_REG_DCFG_VAL	(0x3800)
+#define TFA9890_SYS_CTRL2_REG_SPKR_MSK	(0x3 << 9)
+#define TFA9890_SYS_CTRL_CFE_MSK (0x1 << 2)
+#define TFA9890_BAT_CTL_BSSBY_MSK (0x1 << 15)
+#define TFA9890_SYS_CTRL_DCA_MSK (0x1 << 4)
+
 
 /* enable I2S left channel input */
 #define TFA9890_I2S_LEFT_IN		(0x1 << 3)
diff --git a/sound/soc/codecs/tfa9890.c b/sound/soc/codecs/tfa9890.c
index add13e2804b5..621faafce174 100644
--- a/sound/soc/codecs/tfa9890.c
+++ b/sound/soc/codecs/tfa9890.c
@@ -81,6 +81,7 @@ struct tfa9890_priv {
 	char const *tfa_dev;
 	char const *fw_path;
 	char const *fw_name;
+	int is_spkr_prot_en;
 };
 
 static DEFINE_MUTEX(lr_lock);
@@ -542,6 +543,84 @@ static int tfa9890_put_mode(struct snd_kcontrol *kcontrol,
 }
 
 
+static int tfa9890_dsp_bypass_put(struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct tfa9890_priv *tfa9890 = snd_soc_codec_get_drvdata(codec);
+	u16 val;
+
+	mutex_lock(&lr_lock);
+	if (ucontrol->value.integer.value[0]) {
+		/* Set CHSA to bypass DSP */
+		val = snd_soc_read(codec, TFA9890_I2S_CTL_REG);
+		val &= ~(TFA9890_I2SREG_CHSA_MSK);
+		snd_soc_write(codec, TFA9890_I2S_CTL_REG, val);
+
+		/* Set DCDC compensation to off and set impedance as 8ohm */
+		val = snd_soc_read(codec, TFA9890_SYS_CTL2_REG);
+		val &= ~(TFA9890_SYS_CTRL2_REG_DCFG_MSK);
+		val |= TFA9890_SYS_CTRL2_REG_SPKR_MSK;
+		snd_soc_write(codec, TFA9890_SYS_CTL2_REG, val);
+
+		/* Set DCDC to follower mode and disable coolflux  */
+		val = snd_soc_read(codec, TFA9890_SYS_CTL1_REG);
+		val &= ~(TFA9890_SYS_CTRL_DCA_MSK);
+		val &= ~(TFA9890_SYS_CTRL_CFE_MSK);
+		snd_soc_write(codec, TFA9890_SYS_CTL1_REG, val);
+
+		/* Bypass battery safeguard */
+		val = snd_soc_read(codec, TFA9890_BATT_CTL_REG);
+		val |= TFA9890_BAT_CTL_BSSBY_MSK;
+		snd_soc_write(codec, TFA9890_BATT_CTL_REG, val);
+
+		tfa9890->is_spkr_prot_en = 1;
+		pr_debug("%s: codec dsp bypassed %d\n", codec->name,
+			tfa9890->is_spkr_prot_en);
+	} else {
+		/* Set CHSA to enable DSP */
+		val = snd_soc_read(codec, TFA9890_I2S_CTL_REG);
+		val |= (TFA9890_I2SREG_CHSA_VAL);
+		snd_soc_write(codec, TFA9890_I2S_CTL_REG, val);
+
+		/* Set DCDC compensation to default 100% and
+		 * Set impedance to be defined by DSP
+		 */
+		val = snd_soc_read(codec, TFA9890_SYS_CTL2_REG);
+		val |= (TFA9890_SYS_CTRL2_REG_DCFG_VAL);
+		val &= ~TFA9890_SYS_CTRL2_REG_SPKR_MSK;
+		snd_soc_write(codec, TFA9890_SYS_CTL2_REG, val);
+
+		/* Set DCDC to active mode and enable Coolflux */
+		val = snd_soc_read(codec, TFA9890_SYS_CTL1_REG);
+		val |= (TFA9890_SYS_CTRL_DCA_MSK);
+		val |= (TFA9890_SYS_CTRL_CFE_MSK);
+		snd_soc_write(codec, TFA9890_SYS_CTL1_REG, val);
+
+		/* Enable battery safeguard */
+		val = snd_soc_read(codec, TFA9890_BATT_CTL_REG);
+		val &= ~(TFA9890_BAT_CTL_BSSBY_MSK);
+		snd_soc_write(codec, TFA9890_BATT_CTL_REG, val);
+
+		tfa9890->is_spkr_prot_en = 0;
+		pr_debug("%s: codec dsp unbypassed %d\n", codec->name,
+			tfa9890->is_spkr_prot_en);
+	}
+	mutex_unlock(&lr_lock);
+	return 1;
+}
+
+static int tfa9890_dsp_bypass_get(struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct tfa9890_priv *tfa9890 = snd_soc_codec_get_drvdata(codec);
+
+	ucontrol->value.integer.value[0] = tfa9890->is_spkr_prot_en;
+
+	return 0;
+}
+
 static const struct soc_enum tfa9890_mode_enum[] = {
 	SOC_ENUM_SINGLE_EXT(2, tfa9890_mode),
 };
@@ -555,6 +634,9 @@ static const struct snd_kcontrol_new tfa9890_left_snd_controls[] = {
 	/* val 1 for left channel, 2 for right and 3 for (l+r)/2 */
 	SOC_SINGLE("BOOST Left Ch Select", TFA9890_I2S_CTL_REG,
 			3, 0x3, 0),
+	SOC_SINGLE_EXT("BOOST ENABLE Spkr Left Prot", 0 , 0, 1,
+				 0, tfa9890_dsp_bypass_get,
+					tfa9890_dsp_bypass_put),
 };
 
 /* bit 6,7 : 01 - DSP bypassed, 10 - DSP used */
@@ -584,6 +666,9 @@ static const struct snd_kcontrol_new tfa9890_right_snd_controls[] = {
 	/* val 1 for left channel, 2 for right and 3 for (l+r)/2 */
 	SOC_SINGLE("BOOST Right Ch Select", TFA9890_I2S_CTL_REG,
 			3, 0x3, 0),
+	SOC_SINGLE_EXT("BOOST ENABLE Spkr Right Prot", 0, 0, 1,
+				 0, tfa9890_dsp_bypass_get,
+					tfa9890_dsp_bypass_put),
 };
 
 /* bit 6,7 : 01 - DSP bypassed, 10 - DSP used */
-- 
GitLab