Merge remote-tracking branch 'asoc/topic/tlv320aic3x' into asoc-next
diff --git a/Documentation/devicetree/bindings/sound/tlv320aic3x.txt b/Documentation/devicetree/bindings/sound/tlv320aic3x.txt
index e7b98f4..f47c3f5 100644
--- a/Documentation/devicetree/bindings/sound/tlv320aic3x.txt
+++ b/Documentation/devicetree/bindings/sound/tlv320aic3x.txt
@@ -11,6 +11,12 @@
 
 - gpio-reset - gpio pin number used for codec reset
 - ai3x-gpio-func - <array of 2 int> - AIC3X_GPIO1 & AIC3X_GPIO2 Functionality
+- ai3x-micbias-vg - MicBias Voltage required.
+	1 - MICBIAS output is powered to 2.0V,
+	2 - MICBIAS output is powered to 2.5V,
+	3 - MICBIAS output is connected to AVDD,
+	If this node is not mentioned or if the value is incorrect, then MicBias
+	is powered down.
 
 Example:
 
diff --git a/include/sound/tlv320aic3x.h b/include/sound/tlv320aic3x.h
index ffd9bc7..9407fd0 100644
--- a/include/sound/tlv320aic3x.h
+++ b/include/sound/tlv320aic3x.h
@@ -46,6 +46,13 @@
 	AIC3X_GPIO2_FUNC_BUTTON_PRESS_IRQ	= 15
 };
 
+enum aic3x_micbias_voltage {
+	AIC3X_MICBIAS_OFF = 0,
+	AIC3X_MICBIAS_2_0V = 1,
+	AIC3X_MICBIAS_2_5V = 2,
+	AIC3X_MICBIAS_AVDDV = 3,
+};
+
 struct aic3x_setup_data {
 	unsigned int gpio_func[2];
 };
@@ -53,6 +60,9 @@
 struct aic3x_pdata {
 	int gpio_reset; /* < 0 if not used */
 	struct aic3x_setup_data *setup;
+
+	/* Selects the micbias voltage */
+	enum aic3x_micbias_voltage micbias_vg;
 };
 
 #endif
diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c
index 4989143..65d09d6 100644
--- a/sound/soc/codecs/tlv320aic3x.c
+++ b/sound/soc/codecs/tlv320aic3x.c
@@ -85,6 +85,9 @@
 #define AIC3X_MODEL_33 1
 #define AIC3X_MODEL_3007 2
 	u16 model;
+
+	/* Selects the micbias voltage */
+	enum aic3x_micbias_voltage micbias_vg;
 };
 
 /*
@@ -195,6 +198,37 @@
 	return ret;
 }
 
+/*
+ * mic bias power on/off share the same register bits with
+ * output voltage of mic bias. when power on mic bias, we
+ * need reclaim it to voltage value.
+ * 0x0 = Powered off
+ * 0x1 = MICBIAS output is powered to 2.0V,
+ * 0x2 = MICBIAS output is powered to 2.5V
+ * 0x3 = MICBIAS output is connected to AVDD
+ */
+static int mic_bias_event(struct snd_soc_dapm_widget *w,
+	struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_codec *codec = w->codec;
+	struct aic3x_priv *aic3x = snd_soc_codec_get_drvdata(codec);
+
+	switch (event) {
+	case SND_SOC_DAPM_POST_PMU:
+		/* change mic bias voltage to user defined */
+		snd_soc_update_bits(codec, MICBIAS_CTRL,
+				MICBIAS_LEVEL_MASK,
+				aic3x->micbias_vg << MICBIAS_LEVEL_SHIFT);
+		break;
+
+	case SND_SOC_DAPM_PRE_PMD:
+		snd_soc_update_bits(codec, MICBIAS_CTRL,
+				MICBIAS_LEVEL_MASK, 0);
+		break;
+	}
+	return 0;
+}
+
 static const char *aic3x_left_dac_mux[] = { "DAC_L1", "DAC_L3", "DAC_L2" };
 static const char *aic3x_right_dac_mux[] = { "DAC_R1", "DAC_R3", "DAC_R2" };
 static const char *aic3x_left_hpcom_mux[] =
@@ -596,12 +630,9 @@
 			 AIC3X_ASD_INTF_CTRLA, 0, 3, 3, 0),
 
 	/* Mic Bias */
-	SND_SOC_DAPM_REG(snd_soc_dapm_micbias, "Mic Bias 2V",
-			 MICBIAS_CTRL, 6, 3, 1, 0),
-	SND_SOC_DAPM_REG(snd_soc_dapm_micbias, "Mic Bias 2.5V",
-			 MICBIAS_CTRL, 6, 3, 2, 0),
-	SND_SOC_DAPM_REG(snd_soc_dapm_micbias, "Mic Bias AVDD",
-			 MICBIAS_CTRL, 6, 3, 3, 0),
+	SND_SOC_DAPM_SUPPLY("Mic Bias", MICBIAS_CTRL, 6, 0,
+			 mic_bias_event,
+			 SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
 
 	/* Output mixers */
 	SND_SOC_DAPM_MIXER("Left Line Mixer", SND_SOC_NOPM, 0, 0,
@@ -1386,6 +1417,24 @@
 	if (aic3x->model == AIC3X_MODEL_3007)
 		snd_soc_add_codec_controls(codec, &aic3x_classd_amp_gain_ctrl, 1);
 
+	/* set mic bias voltage */
+	switch (aic3x->micbias_vg) {
+	case AIC3X_MICBIAS_2_0V:
+	case AIC3X_MICBIAS_2_5V:
+	case AIC3X_MICBIAS_AVDDV:
+		snd_soc_update_bits(codec, MICBIAS_CTRL,
+				    MICBIAS_LEVEL_MASK,
+				    (aic3x->micbias_vg) << MICBIAS_LEVEL_SHIFT);
+		break;
+	case AIC3X_MICBIAS_OFF:
+		/*
+		 * noting to do. target won't enter here. This is just to avoid
+		 * compile time warning "warning: enumeration value
+		 * 'AIC3X_MICBIAS_OFF' not handled in switch"
+		 */
+		break;
+	}
+
 	aic3x_add_widgets(codec);
 	list_add(&aic3x->list, &reset_list);
 
@@ -1461,6 +1510,7 @@
 	struct aic3x_setup_data *ai3x_setup;
 	struct device_node *np = i2c->dev.of_node;
 	int ret;
+	u32 value;
 
 	aic3x = devm_kzalloc(&i2c->dev, sizeof(struct aic3x_priv), GFP_KERNEL);
 	if (aic3x == NULL) {
@@ -1474,6 +1524,7 @@
 	if (pdata) {
 		aic3x->gpio_reset = pdata->gpio_reset;
 		aic3x->setup = pdata->setup;
+		aic3x->micbias_vg = pdata->micbias_vg;
 	} else if (np) {
 		ai3x_setup = devm_kzalloc(&i2c->dev, sizeof(*ai3x_setup),
 								GFP_KERNEL);
@@ -1493,6 +1544,26 @@
 			aic3x->setup = ai3x_setup;
 		}
 
+		if (!of_property_read_u32(np, "ai3x-micbias-vg", &value)) {
+			switch (value) {
+			case 1 :
+				aic3x->micbias_vg = AIC3X_MICBIAS_2_0V;
+				break;
+			case 2 :
+				aic3x->micbias_vg = AIC3X_MICBIAS_2_5V;
+				break;
+			case 3 :
+				aic3x->micbias_vg = AIC3X_MICBIAS_AVDDV;
+				break;
+			default :
+				aic3x->micbias_vg = AIC3X_MICBIAS_OFF;
+				dev_err(&i2c->dev, "Unsuitable MicBias voltage "
+							"found in DT\n");
+			}
+		} else {
+			aic3x->micbias_vg = AIC3X_MICBIAS_OFF;
+		}
+
 	} else {
 		aic3x->gpio_reset = -1;
 	}
diff --git a/sound/soc/codecs/tlv320aic3x.h b/sound/soc/codecs/tlv320aic3x.h
index 6db3c41..e521ac3 100644
--- a/sound/soc/codecs/tlv320aic3x.h
+++ b/sound/soc/codecs/tlv320aic3x.h
@@ -238,6 +238,10 @@
 /* Default input volume */
 #define DEFAULT_GAIN    0x20
 
+/* MICBIAS Control Register */
+#define MICBIAS_LEVEL_SHIFT	(6)
+#define MICBIAS_LEVEL_MASK	(3 << 6)
+
 /* headset detection / button API */
 
 /* The AIC3x supports detection of stereo headsets (GND + left + right signal)
diff --git a/sound/soc/davinci/davinci-evm.c b/sound/soc/davinci/davinci-evm.c
index d55e647..484b22c 100644
--- a/sound/soc/davinci/davinci-evm.c
+++ b/sound/soc/davinci/davinci-evm.c
@@ -116,9 +116,9 @@
 	{"Line Out", NULL, "RLOUT"},
 
 	/* Mic connected to (MIC3L | MIC3R) */
-	{"MIC3L", NULL, "Mic Bias 2V"},
-	{"MIC3R", NULL, "Mic Bias 2V"},
-	{"Mic Bias 2V", NULL, "Mic Jack"},
+	{"MIC3L", NULL, "Mic Bias"},
+	{"MIC3R", NULL, "Mic Bias"},
+	{"Mic Bias", NULL, "Mic Jack"},
 
 	/* Line In connected to (LINE1L | LINE2L), (LINE1R | LINE2R) */
 	{"LINE1L", NULL, "Line In"},
diff --git a/sound/soc/omap/n810.c b/sound/soc/omap/n810.c
index 230b8c14..ee7cd53 100644
--- a/sound/soc/omap/n810.c
+++ b/sound/soc/omap/n810.c
@@ -230,8 +230,8 @@
 	{"Ext Spk", NULL, "LLOUT"},
 	{"Ext Spk", NULL, "RLOUT"},
 
-	{"DMic Rate 64", NULL, "Mic Bias 2V"},
-	{"Mic Bias 2V", NULL, "DMic"},
+	{"DMic Rate 64", NULL, "Mic Bias"},
+	{"Mic Bias", NULL, "DMic"},
 };
 
 static const char *spk_function[] = {"Off", "On"};
diff --git a/sound/soc/omap/rx51.c b/sound/soc/omap/rx51.c
index d921ddb..3cd5257 100644
--- a/sound/soc/omap/rx51.c
+++ b/sound/soc/omap/rx51.c
@@ -248,16 +248,16 @@
 	{"FM Transmitter", NULL, "LLOUT"},
 	{"FM Transmitter", NULL, "RLOUT"},
 
-	{"DMic Rate 64", NULL, "Mic Bias 2V"},
-	{"Mic Bias 2V", NULL, "DMic"},
+	{"DMic Rate 64", NULL, "Mic Bias"},
+	{"Mic Bias", NULL, "DMic"},
 };
 
 static const struct snd_soc_dapm_route audio_mapb[] = {
 	{"b LINE2R", NULL, "MONO_LOUT"},
 	{"Earphone", NULL, "b HPLOUT"},
 
-	{"LINE1L", NULL, "b Mic Bias 2.5V"},
-	{"b Mic Bias 2.5V", NULL, "HS Mic"}
+	{"LINE1L", NULL, "b Mic Bias"},
+	{"b Mic Bias", NULL, "HS Mic"}
 };
 
 static const char *spk_function[] = {"Off", "On"};