Merge "ASoC: wcd9xxx: refactor common components out of wcd9xxx CODEC drivers"
diff --git a/include/linux/mfd/wcd9xxx/core.h b/include/linux/mfd/wcd9xxx/core.h
index cf57a64..8e8caf7 100644
--- a/include/linux/mfd/wcd9xxx/core.h
+++ b/include/linux/mfd/wcd9xxx/core.h
@@ -19,7 +19,7 @@
 #include <linux/platform_device.h>
 #include <linux/of_irq.h>
 
-#define WCD9XXX_NUM_IRQ_REGS 3
+#define WCD9XXX_NUM_IRQ_REGS 4
 
 #define WCD9XXX_SLIM_NUM_PORT_REG 3
 
@@ -47,6 +47,7 @@
 
 
 enum {
+	/* INTR_REG 0 */
 	WCD9XXX_IRQ_SLIMBUS = 0,
 	WCD9XXX_IRQ_MBHC_REMOVAL,
 	WCD9XXX_IRQ_MBHC_SHORT_TERM,
@@ -55,6 +56,7 @@
 	WCD9XXX_IRQ_MBHC_POTENTIAL,
 	WCD9XXX_IRQ_MBHC_INSERTION,
 	WCD9XXX_IRQ_BG_PRECHARGE,
+	/* INTR_REG 1 */
 	WCD9XXX_IRQ_PA1_STARTUP,
 	WCD9XXX_IRQ_PA2_STARTUP,
 	WCD9XXX_IRQ_PA3_STARTUP,
@@ -63,12 +65,21 @@
 	WCD9XXX_IRQ_MICBIAS1_PRECHARGE,
 	WCD9XXX_IRQ_MICBIAS2_PRECHARGE,
 	WCD9XXX_IRQ_MICBIAS3_PRECHARGE,
+	/* INTR_REG 2 */
 	WCD9XXX_IRQ_HPH_PA_OCPL_FAULT,
 	WCD9XXX_IRQ_HPH_PA_OCPR_FAULT,
 	WCD9XXX_IRQ_EAR_PA_OCPL_FAULT,
 	WCD9XXX_IRQ_HPH_L_PA_STARTUP,
 	WCD9XXX_IRQ_HPH_R_PA_STARTUP,
 	WCD9XXX_IRQ_EAR_PA_STARTUP,
+	WCD9XXX_IRQ_RESERVED_0,
+	WCD9XXX_IRQ_RESERVED_1,
+	/* INTR_REG 3 */
+	WCD9XXX_IRQ_MAD_AUDIO,
+	WCD9XXX_IRQ_MAD_BEACON,
+	WCD9XXX_IRQ_MAD_ULTRASOUND,
+	WCD9XXX_IRQ_SPEAKER_CLIPPING,
+	WCD9XXX_IRQ_MBHC_JACK_SWITCH,
 	WCD9XXX_NUM_IRQS,
 };
 
diff --git a/include/linux/mfd/wcd9xxx/pdata.h b/include/linux/mfd/wcd9xxx/pdata.h
index e831f0b..a7ca417 100644
--- a/include/linux/mfd/wcd9xxx/pdata.h
+++ b/include/linux/mfd/wcd9xxx/pdata.h
@@ -28,6 +28,12 @@
 #define SITAR_CFILT2_SEL 0x1
 #define SITAR_CFILT3_SEL 0x2
 
+#define WCD9XXX_LDOH_1P95_V 0x0
+#define WCD9XXX_LDOH_2P35_V 0x1
+#define WCD9XXX_LDOH_2P75_V 0x2
+#define WCD9XXX_LDOH_2P85_V 0x3
+#define WCD9XXX_LDOH_3P0_V 0x3
+
 #define TABLA_LDOH_1P95_V 0x0
 #define TABLA_LDOH_2P35_V 0x1
 #define TABLA_LDOH_2P75_V 0x2
@@ -37,16 +43,6 @@
 #define TABLA_CFILT2_SEL 0x1
 #define TABLA_CFILT3_SEL 0x2
 
-#define TAIKO_CFILT1_SEL 0x0
-#define TAIKO_CFILT2_SEL 0x1
-#define TAIKO_CFILT3_SEL 0x2
-
-#define TAIKO_LDOH_1P95_V 0x0
-#define TAIKO_LDOH_2P35_V 0x1
-#define TAIKO_LDOH_2P75_V 0x2
-#define TAIKO_LDOH_2P85_V 0x3
-
-
 #define MAX_AMIC_CHANNEL 7
 
 #define TABLA_OCP_300_MA 0x0
diff --git a/include/linux/mfd/wcd9xxx/wcd9xxx_registers.h b/include/linux/mfd/wcd9xxx/wcd9xxx_registers.h
index 357f400..4b7a32c 100644
--- a/include/linux/mfd/wcd9xxx/wcd9xxx_registers.h
+++ b/include/linux/mfd/wcd9xxx/wcd9xxx_registers.h
@@ -16,29 +16,29 @@
 #define WCD9XXX_A_CHIP_CTL			(0x00)
 #define WCD9XXX_A_CHIP_CTL__POR			(0x00000000)
 #define WCD9XXX_A_CHIP_STATUS			(0x01)
-#define WCD9XXX_A_CHIP_STATUS__POR			(0x00000000)
-#define WCD9XXX_A_CHIP_ID_BYTE_0			(0x04)
-#define WCD9XXX_A_CHIP_ID_BYTE_0__POR			(0x00000000)
-#define WCD9XXX_A_CHIP_ID_BYTE_1			(0x05)
-#define WCD9XXX_A_CHIP_ID_BYTE_1__POR			(0x00000000)
-#define WCD9XXX_A_CHIP_ID_BYTE_2			(0x06)
-#define WCD9XXX_A_CHIP_ID_BYTE_2__POR			(0x00000000)
-#define WCD9XXX_A_CHIP_ID_BYTE_3			(0x07)
-#define WCD9XXX_A_CHIP_ID_BYTE_3__POR			(0x00000001)
+#define WCD9XXX_A_CHIP_STATUS__POR		(0x00000000)
+#define WCD9XXX_A_CHIP_ID_BYTE_0		(0x04)
+#define WCD9XXX_A_CHIP_ID_BYTE_0__POR		(0x00000000)
+#define WCD9XXX_A_CHIP_ID_BYTE_1		(0x05)
+#define WCD9XXX_A_CHIP_ID_BYTE_1__POR		(0x00000000)
+#define WCD9XXX_A_CHIP_ID_BYTE_2		(0x06)
+#define WCD9XXX_A_CHIP_ID_BYTE_2__POR		(0x00000000)
+#define WCD9XXX_A_CHIP_ID_BYTE_3		(0x07)
+#define WCD9XXX_A_CHIP_ID_BYTE_3__POR		(0x00000001)
 #define WCD9XXX_A_CHIP_VERSION			(0x08)
-#define WCD9XXX_A_CHIP_VERSION__POR			(0x00000020)
+#define WCD9XXX_A_CHIP_VERSION__POR		(0x00000020)
 #define WCD9XXX_A_SB_VERSION			(0x09)
-#define WCD9XXX_A_SB_VERSION__POR			(0x00000010)
+#define WCD9XXX_A_SB_VERSION__POR		(0x00000010)
 #define WCD9XXX_A_SLAVE_ID_1			(0x0C)
-#define WCD9XXX_A_SLAVE_ID_1__POR			(0x00000077)
+#define WCD9XXX_A_SLAVE_ID_1__POR		(0x00000077)
 #define WCD9XXX_A_SLAVE_ID_2			(0x0D)
-#define WCD9XXX_A_SLAVE_ID_2__POR			(0x00000066)
+#define WCD9XXX_A_SLAVE_ID_2__POR		(0x00000066)
 #define WCD9XXX_A_SLAVE_ID_3			(0x0E)
-#define WCD9XXX_A_SLAVE_ID_3__POR			(0x00000055)
+#define WCD9XXX_A_SLAVE_ID_3__POR		(0x00000055)
 #define WCD9XXX_A_CDC_CTL			(0x80)
 #define WCD9XXX_A_CDC_CTL__POR			(0x00000000)
 #define WCD9XXX_A_LEAKAGE_CTL			(0x88)
-#define WCD9XXX_A_LEAKAGE_CTL__POR			(0x00000004)
+#define WCD9XXX_A_LEAKAGE_CTL__POR		(0x00000004)
 #define WCD9XXX_A_INTR_MODE			(0x90)
 #define WCD9XXX_A_INTR_MASK0			(0x94)
 #define WCD9XXX_A_INTR_STATUS0			(0x98)
@@ -46,5 +46,161 @@
 #define WCD9XXX_A_INTR_LEVEL0			(0xA0)
 #define WCD9XXX_A_INTR_LEVEL1			(0xA1)
 #define WCD9XXX_A_INTR_LEVEL2			(0xA2)
+#define WCD9XXX_A_RX_HPH_CNP_EN			(0x1AB)
+#define WCD9XXX_A_RX_HPH_CNP_EN__POR		(0x80)
+#define WCD9XXX_A_RX_HPH_CNP_EN			(0x1AB)
+#define WCD9XXX_A_RX_HPH_CNP_EN__POR		(0x80)
+#define WCD9XXX_A_BIAS_CENTRAL_BG_CTL		(0x101)
+#define WCD9XXX_A_BIAS_CENTRAL_BG_CTL__POR	(0x50)
+#define WCD9XXX_A_CLK_BUFF_EN1			(0x108)
+#define WCD9XXX_A_CLK_BUFF_EN1__POR		(0x04)
+#define WCD9XXX_A_CLK_BUFF_EN2			(0x109)
+#define WCD9XXX_A_CLK_BUFF_EN2__POR		(0x02)
+#define WCD9XXX_A_RX_COM_BIAS			(0x1A2)
+#define WCD9XXX_A_RX_COM_BIAS__POR		(0x00)
+#define WCD9XXX_A_RC_OSC_FREQ			(0x1FA)
+#define WCD9XXX_A_RC_OSC_FREQ__POR		(0x46)
+#define WCD9XXX_A_BIAS_OSC_BG_CTL		(0x105)
+#define WCD9XXX_A_BIAS_OSC_BG_CTL__POR		(0x16)
+#define WCD9XXX_A_RC_OSC_TEST			(0x1FB)
+#define WCD9XXX_A_RC_OSC_TEST__POR		(0x0A)
+#define WCD9XXX_A_CDC_CLK_MCLK_CTL		(0x311)
+#define WCD9XXX_A_CDC_CLK_MCLK_CTL__POR		(0x00)
+
+#define WCD9XXX_A_CDC_MBHC_EN_CTL		(0x3C0)
+#define WCD9XXX_A_CDC_MBHC_EN_CTL__POR		(0x00)
+#define WCD9XXX_A_CDC_MBHC_FIR_B1_CFG		(0x3C1)
+#define WCD9XXX_A_CDC_MBHC_FIR_B1_CFG__POR	(0x00)
+#define WCD9XXX_A_CDC_MBHC_FIR_B2_CFG		(0x3C2)
+#define WCD9XXX_A_CDC_MBHC_FIR_B2_CFG__POR	(0x06)
+#define WCD9XXX_A_CDC_MBHC_TIMER_B1_CTL		(0x3C3)
+#define WCD9XXX_A_CDC_MBHC_TIMER_B1_CTL__POR	(0x03)
+#define WCD9XXX_A_CDC_MBHC_TIMER_B2_CTL		(0x3C4)
+#define WCD9XXX_A_CDC_MBHC_TIMER_B2_CTL__POR	(0x09)
+#define WCD9XXX_A_CDC_MBHC_TIMER_B3_CTL		(0x3C5)
+#define WCD9XXX_A_CDC_MBHC_TIMER_B3_CTL__POR	(0x1E)
+#define WCD9XXX_A_CDC_MBHC_TIMER_B4_CTL		(0x3C6)
+#define WCD9XXX_A_CDC_MBHC_TIMER_B4_CTL__POR	(0x45)
+#define WCD9XXX_A_CDC_MBHC_TIMER_B5_CTL		(0x3C7)
+#define WCD9XXX_A_CDC_MBHC_TIMER_B5_CTL__POR	(0x04)
+#define WCD9XXX_A_CDC_MBHC_TIMER_B6_CTL		(0x3C8)
+#define WCD9XXX_A_CDC_MBHC_TIMER_B6_CTL__POR	(0x78)
+#define WCD9XXX_A_CDC_MBHC_B1_STATUS		(0x3C9)
+#define WCD9XXX_A_CDC_MBHC_B1_STATUS__POR	(0x00)
+#define WCD9XXX_A_CDC_MBHC_B2_STATUS		(0x3CA)
+#define WCD9XXX_A_CDC_MBHC_B2_STATUS__POR	(0x00)
+#define WCD9XXX_A_CDC_MBHC_B3_STATUS		(0x3CB)
+#define WCD9XXX_A_CDC_MBHC_B3_STATUS__POR	(0x00)
+#define WCD9XXX_A_CDC_MBHC_B4_STATUS		(0x3CC)
+#define WCD9XXX_A_CDC_MBHC_B4_STATUS__POR	(0x00)
+#define WCD9XXX_A_CDC_MBHC_B5_STATUS		(0x3CD)
+#define WCD9XXX_A_CDC_MBHC_B5_STATUS__POR	(0x00)
+#define WCD9XXX_A_CDC_MBHC_B1_CTL		(0x3CE)
+#define WCD9XXX_A_CDC_MBHC_B1_CTL__POR		(0xC0)
+#define WCD9XXX_A_CDC_MBHC_B2_CTL		(0x3CF)
+#define WCD9XXX_A_CDC_MBHC_B2_CTL__POR		(0x5D)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B1_CTL		(0x3D0)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B1_CTL__POR	(0x00)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B2_CTL		(0x3D1)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B2_CTL__POR	(0x00)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B3_CTL		(0x3D2)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B3_CTL__POR	(0x00)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B4_CTL		(0x3D3)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B4_CTL__POR	(0x00)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B5_CTL		(0x3D4)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B5_CTL__POR	(0x00)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B6_CTL		(0x3D5)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B6_CTL__POR	(0x00)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B7_CTL		(0x3D6)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B7_CTL__POR	(0xFF)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B8_CTL		(0x3D7)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B8_CTL__POR	(0x07)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B9_CTL		(0x3D8)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B9_CTL__POR	(0xFF)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B10_CTL		(0x3D9)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B10_CTL__POR	(0x7F)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B11_CTL		(0x3DA)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B11_CTL__POR	(0x00)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B12_CTL		(0x3DB)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B12_CTL__POR	(0x80)
+#define WCD9XXX_A_CDC_MBHC_CLK_CTL		(0x3DC)
+#define WCD9XXX_A_CDC_MBHC_CLK_CTL__POR		(0x00)
+#define WCD9XXX_A_CDC_MBHC_INT_CTL		(0x3DD)
+#define WCD9XXX_A_CDC_MBHC_INT_CTL__POR		(0x00)
+#define WCD9XXX_A_CDC_MBHC_DEBUG_CTL		(0x3DE)
+#define WCD9XXX_A_CDC_MBHC_DEBUG_CTL__POR	(0x00)
+#define WCD9XXX_A_CDC_MBHC_SPARE		(0x3DF)
+#define WCD9XXX_A_CDC_MBHC_SPARE__POR		(0x00)
+#define WCD9XXX_A_MBHC_SCALING_MUX_1		(0x14E)
+#define WCD9XXX_A_MBHC_SCALING_MUX_1__POR	(0x00)
+#define WCD9XXX_A_RX_HPH_OCP_CTL		(0x1AA)
+#define WCD9XXX_A_RX_HPH_OCP_CTL__POR		(0x68)
+#define WCD9XXX_A_MICB_1_CTL			(0x12B)
+#define WCD9XXX_A_MICB_1_CTL__POR		(0x16)
+#define WCD9XXX_A_MICB_1_INT_RBIAS		(0x12C)
+#define WCD9XXX_A_MICB_1_INT_RBIAS__POR		(0x24)
+#define WCD9XXX_A_MICB_1_MBHC			(0x12D)
+#define WCD9XXX_A_MICB_1_MBHC__POR		(0x01)
+#define WCD9XXX_A_MICB_CFILT_2_CTL		(0x12E)
+#define WCD9XXX_A_MICB_CFILT_2_CTL__POR		(0x40)
+#define WCD9XXX_A_MICB_CFILT_2_VAL		(0x12F)
+#define WCD9XXX_A_MICB_CFILT_2_VAL__POR		(0x80)
+#define WCD9XXX_A_MICB_CFILT_2_PRECHRG		(0x130)
+#define WCD9XXX_A_MICB_CFILT_2_PRECHRG__POR	(0x38)
+#define WCD9XXX_A_MICB_2_CTL			(0x131)
+#define WCD9XXX_A_MICB_2_CTL__POR		(0x16)
+#define WCD9XXX_A_MICB_2_INT_RBIAS		(0x132)
+#define WCD9XXX_A_MICB_2_INT_RBIAS__POR		(0x24)
+#define WCD9XXX_A_MICB_2_MBHC			(0x133)
+#define WCD9XXX_A_MICB_2_MBHC__POR		(0x02)
+#define WCD9XXX_A_MICB_CFILT_3_CTL		(0x134)
+#define WCD9XXX_A_MICB_CFILT_3_CTL__POR		(0x40)
+#define WCD9XXX_A_MICB_CFILT_3_VAL		(0x135)
+#define WCD9XXX_A_MICB_CFILT_3_VAL__POR		(0x80)
+#define WCD9XXX_A_MICB_CFILT_3_PRECHRG		(0x136)
+#define WCD9XXX_A_MICB_CFILT_3_PRECHRG__POR	(0x38)
+#define WCD9XXX_A_MICB_3_CTL			(0x137)
+#define WCD9XXX_A_MICB_3_CTL__POR		(0x16)
+#define WCD9XXX_A_MICB_3_INT_RBIAS		(0x138)
+#define WCD9XXX_A_MICB_3_INT_RBIAS__POR		(0x24)
+#define WCD9XXX_A_MICB_3_MBHC			(0x139)
+#define WCD9XXX_A_MICB_3_MBHC__POR		(0x00)
+#define WCD9XXX_A_MICB_4_CTL			(0x13D)
+#define WCD9XXX_A_MICB_4_CTL__POR		(0x16)
+#define WCD9XXX_A_MICB_4_INT_RBIAS		(0x13E)
+#define WCD9XXX_A_MICB_4_INT_RBIAS__POR		(0x24)
+#define WCD9XXX_A_MICB_4_MBHC			(0x13F)
+#define WCD9XXX_A_MICB_4_MBHC__POR		(0x01)
+#define WCD9XXX_A_MICB_CFILT_1_VAL		(0x129)
+#define WCD9XXX_A_MICB_CFILT_1_VAL__POR		(0x80)
+#define WCD9XXX_A_MBHC_HPH			(0x1FE)
+#define WCD9XXX_A_MBHC_HPH__POR			(0x44)
+#define WCD9XXX_A_RX_HPH_CNP_WG_TIME		(0x1AD)
+#define WCD9XXX_A_RX_HPH_CNP_WG_TIME__POR	(0x2A)
+#define WCD9XXX_A_RX_HPH_R_DAC_CTL		(0x1B7)
+#define WCD9XXX_A_RX_HPH_R_DAC_CTL__POR		(0x00)
+#define WCD9XXX_A_RX_HPH_L_DAC_CTL		(0x1B1)
+#define WCD9XXX_A_RX_HPH_L_DAC_CTL__POR		(0x00)
+#define WCD9XXX_A_TX_7_MBHC_EN			(0x171)
+#define WCD9XXX_A_TX_7_MBHC_EN__POR		(0x0C)
+#define WCD9XXX_A_PIN_CTL_OE0			(0x010)
+#define WCD9XXX_A_PIN_CTL_OE0__POR		(0x00)
+#define WCD9XXX_A_PIN_CTL_OE1			(0x011)
+#define WCD9XXX_A_PIN_CTL_OE1__POR		(0x00)
+#define WCD9XXX_A_MICB_CFILT_1_CTL		(0x128)
+#define WCD9XXX_A_LDO_H_MODE_1			(0x110)
+#define WCD9XXX_A_LDO_H_MODE_1__POR		(0x65)
+#define WCD9XXX_A_MICB_CFILT_1_CTL__POR		(0x40)
+#define WCD9XXX_A_TX_7_MBHC_TEST_CTL		(0x174)
+#define WCD9XXX_A_TX_7_MBHC_TEST_CTL__POR	(0x38)
+#define WCD9XXX_A_MBHC_SCALING_MUX_2		(0x14F)
+#define WCD9XXX_A_MBHC_SCALING_MUX_2__POR	(0x80)
+#define WCD9XXX_A_TX_COM_BIAS			(0x14C)
+#define WCD9XXX_A_TX_COM_BIAS__POR		(0xF0)
+
+#define WCD9XXX_A_MBHC_INSERT_DETECT		(0x14A) /* TAIKO and later */
+#define WCD9XXX_A_MBHC_INSERT_DETECT__POR	(0x00)
+#define WCD9XXX_A_MBHC_INSERT_DET_STATUS	(0x14B) /* TAIKO and later */
+#define WCD9XXX_A_MBHC_INSERT_DET_STATUS__POR	(0x00)
 
 #endif
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 5aabfee..1c9d86b 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -51,7 +51,7 @@
 snd-soc-wcd9304-objs := wcd9304.o wcd9304-tables.o
 snd-soc-wcd9310-objs := wcd9310.o wcd9310-tables.o
 snd-soc-cs8427-objs := cs8427.o
-snd-soc-wcd9320-objs := wcd9320.o wcd9320-tables.o
+snd-soc-wcd9320-objs := wcd9xxx-resmgr.o wcd9320.o wcd9320-tables.o wcd9xxx-mbhc.o
 snd-soc-wl1273-objs := wl1273.o
 snd-soc-wm1250-ev1-objs := wm1250-ev1.o
 snd-soc-wm2000-objs := wm2000.o
diff --git a/sound/soc/codecs/wcd9320.c b/sound/soc/codecs/wcd9320.c
index 0c36c4a..886e4d3 100644
--- a/sound/soc/codecs/wcd9320.c
+++ b/sound/soc/codecs/wcd9320.c
@@ -33,37 +33,19 @@
 #include <linux/kernel.h>
 #include <linux/gpio.h>
 #include "wcd9320.h"
+#include "wcd9xxx-resmgr.h"
 
 #define WCD9320_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\
 			SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |\
 			SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000)
 
-
 #define NUM_DECIMATORS 10
 #define NUM_INTERPOLATORS 7
 #define BITS_PER_REG 8
-#define TAIKO_CFILT_FAST_MODE 0x00
-#define TAIKO_CFILT_SLOW_MODE 0x40
-#define MBHC_FW_READ_ATTEMPTS 15
-#define MBHC_FW_READ_TIMEOUT 2000000
 #define TAIKO_TX_PORT_NUMBER	16
 
-enum {
-	MBHC_USE_HPHL_TRIGGER = 1,
-	MBHC_USE_MB_TRIGGER = 2
-};
-
-#define MBHC_NUM_DCE_PLUG_DETECT 3
-#define NUM_ATTEMPTS_INSERT_DETECT 25
-#define NUM_ATTEMPTS_TO_REPORT 5
-
-#define TAIKO_JACK_MASK (SND_JACK_HEADSET | SND_JACK_OC_HPHL | \
-			 SND_JACK_OC_HPHR | SND_JACK_UNSUPPORTED)
-
 #define TAIKO_I2S_MASTER_MODE_MASK 0x08
 
-#define TAIKO_OCP_ATTEMPT 1
-
 enum {
 	AIF1_PB = 0,
 	AIF1_CAP,
@@ -92,56 +74,12 @@
 
 #define TAIKO_COMP_DIGITAL_GAIN_OFFSET 3
 
-#define TAIKO_MCLK_RATE_12288KHZ 12288000
-#define TAIKO_MCLK_RATE_9600KHZ 9600000
-
-#define TAIKO_FAKE_INS_THRESHOLD_MS 2500
-#define TAIKO_FAKE_REMOVAL_MIN_PERIOD_MS 50
-
-#define TAIKO_MBHC_BUTTON_MIN 0x8000
-
-#define TAIKO_MBHC_FAKE_INSERT_LOW 10
-#define TAIKO_MBHC_FAKE_INSERT_HIGH 80
-#define TAIKO_MBHC_FAKE_INS_HIGH_NO_GPIO 150
-
-#define TAIKO_MBHC_STATUS_REL_DETECTION 0x0C
-
-#define TAIKO_MBHC_GPIO_REL_DEBOUNCE_TIME_MS 200
-
-#define TAIKO_MBHC_FAKE_INS_DELTA_MV 200
-#define TAIKO_MBHC_FAKE_INS_DELTA_SCALED_MV 300
-
-#define TAIKO_HS_DETECT_PLUG_TIME_MS (5 * 1000)
-#define TAIKO_HS_DETECT_PLUG_INERVAL_MS 100
-
-#define TAIKO_GPIO_IRQ_DEBOUNCE_TIME_US 5000
-
-#define TAIKO_MBHC_GND_MIC_SWAP_THRESHOLD 2
-
-#define TAIKO_ACQUIRE_LOCK(x) do { mutex_lock(&x); } while (0)
-#define TAIKO_RELEASE_LOCK(x) do { mutex_unlock(&x); } while (0)
-
 static const DECLARE_TLV_DB_SCALE(digital_gain, 0, 1, 0);
 static const DECLARE_TLV_DB_SCALE(line_gain, 0, 7, 1);
 static const DECLARE_TLV_DB_SCALE(analog_gain, 0, 25, 1);
 static struct snd_soc_dai_driver taiko_dai[];
 static const DECLARE_TLV_DB_SCALE(aux_pga_gain, 0, 2, 0);
 
-enum taiko_bandgap_type {
-	TAIKO_BANDGAP_OFF = 0,
-	TAIKO_BANDGAP_AUDIO_MODE,
-	TAIKO_BANDGAP_MBHC_MODE,
-};
-
-struct mbhc_micbias_regs {
-	u16 cfilt_val;
-	u16 cfilt_ctl;
-	u16 mbhc_reg;
-	u16 int_rbias;
-	u16 ctl_reg;
-	u8 cfilt_sel;
-};
-
 /* Codec supports 2 IIR filters */
 enum {
 	IIR1 = 0,
@@ -174,72 +112,12 @@
 	COMPANDER_FS_MAX,
 };
 
-/* Flags to track of PA and DAC state.
- * PA and DAC should be tracked separately as AUXPGA loopback requires
- * only PA to be turned on without DAC being on. */
-enum taiko_priv_ack_flags {
-	TAIKO_HPHL_PA_OFF_ACK = 0,
-	TAIKO_HPHR_PA_OFF_ACK,
-	TAIKO_HPHL_DAC_OFF_ACK,
-	TAIKO_HPHR_DAC_OFF_ACK
-};
-
-
 struct comp_sample_dependent_params {
 	u32 peak_det_timeout;
 	u32 rms_meter_div_fact;
 	u32 rms_meter_resamp_fact;
 };
 
-/* Data used by MBHC */
-struct mbhc_internal_cal_data {
-	u16 dce_z;
-	u16 dce_mb;
-	u16 sta_z;
-	u16 sta_mb;
-	u32 t_sta_dce;
-	u32 t_dce;
-	u32 t_sta;
-	u32 micb_mv;
-	u16 v_ins_hu;
-	u16 v_ins_h;
-	u16 v_b1_hu;
-	u16 v_b1_h;
-	u16 v_b1_huc;
-	u16 v_brh;
-	u16 v_brl;
-	u16 v_no_mic;
-	u8 npoll;
-	u8 nbounce_wait;
-	s16 adj_v_hs_max;
-	u16 adj_v_ins_hu;
-	u16 adj_v_ins_h;
-	s16 v_inval_ins_low;
-	s16 v_inval_ins_high;
-};
-
-struct taiko_reg_address {
-	u16 micb_4_ctl;
-	u16 micb_4_int_rbias;
-	u16 micb_4_mbhc;
-};
-
-enum taiko_mbhc_plug_type {
-	PLUG_TYPE_INVALID = -1,
-	PLUG_TYPE_NONE,
-	PLUG_TYPE_HEADSET,
-	PLUG_TYPE_HEADPHONE,
-	PLUG_TYPE_HIGH_HPH,
-	PLUG_TYPE_GND_MIC_SWAP,
-};
-
-enum taiko_mbhc_state {
-	MBHC_STATE_NONE = -1,
-	MBHC_STATE_POTENTIAL,
-	MBHC_STATE_POTENTIAL_RECOVERY,
-	MBHC_STATE_RELEASE,
-};
-
 struct hpf_work {
 	struct taiko_priv *taiko;
 	u32 decimator;
@@ -295,58 +173,17 @@
 
 struct taiko_priv {
 	struct snd_soc_codec *codec;
-	struct taiko_reg_address reg_addr;
 	u32 adc_count;
-	u32 cfilt1_cnt;
-	u32 cfilt2_cnt;
-	u32 cfilt3_cnt;
 	u32 rx_bias_count;
 	s32 dmic_1_2_clk_cnt;
 	s32 dmic_3_4_clk_cnt;
 	s32 dmic_5_6_clk_cnt;
 
-	enum taiko_bandgap_type bandgap_type;
-	bool mclk_enabled;
-	bool clock_active;
-	bool config_mode_active;
-	bool mbhc_polling_active;
-	unsigned long mbhc_fake_ins_start;
-	int buttons_pressed;
-	enum taiko_mbhc_state mbhc_state;
-	struct taiko_mbhc_config mbhc_cfg;
-	struct mbhc_internal_cal_data mbhc_data;
-
-	struct wcd9xxx_pdata *pdata;
 	u32 anc_slot;
 
-	bool no_mic_headset_override;
-	/* Delayed work to report long button press */
-	struct delayed_work mbhc_btn_dwork;
-
-	struct mbhc_micbias_regs mbhc_bias_regs;
-	bool mbhc_micbias_switched;
-
-	/* track PA/DAC state */
-	unsigned long hph_pa_dac_state;
-
 	/*track taiko interface type*/
 	u8 intf_type;
 
-	u32 hph_status; /* track headhpone status */
-	/* define separate work for left and right headphone OCP to avoid
-	 * additional checking on which OCP event to report so no locking
-	 * to ensure synchronization is required
-	 */
-	struct work_struct hphlocp_work; /* reporting left hph ocp off */
-	struct work_struct hphrocp_work; /* reporting right hph ocp off */
-
-	u8 hphlocp_cnt; /* headphone left ocp retry */
-	u8 hphrocp_cnt; /* headphone right ocp retry */
-
-	/* Work to perform MBHC Firmware Read */
-	struct delayed_work mbhc_firmware_dwork;
-	const struct firmware *mbhc_fw;
-
 	/* num of slim ports required */
 	struct wcd9xxx_codec_dai_data  dai[NUM_CODEC_DAIS];
 
@@ -359,29 +196,12 @@
 	u8 aux_l_gain;
 	u8 aux_r_gain;
 
-	struct delayed_work mbhc_insert_dwork;
-	unsigned long mbhc_last_resume; /* in jiffies */
-
-	u8 current_plug;
-	struct work_struct hs_correct_plug_work;
-	bool hs_detect_work_stop;
-	bool hs_polling_irq_prepared;
-	bool lpi_enabled; /* low power insertion detection */
-	bool in_gpio_handler;
-	/* Currently, only used for mbhc purpose, to protect
-	 * concurrent execution of mbhc threaded irq handlers and
-	 * kill race between DAPM and MBHC.But can serve as a
-	 * general lock to protect codec resource
-	 */
-	struct mutex codec_resource_lock;
-
-#ifdef CONFIG_DEBUG_FS
-	struct dentry *debugfs_poke;
-	struct dentry *debugfs_mbhc;
-#endif
+	/* resmgr module */
+	struct wcd9xxx_resmgr resmgr;
+	/* mbhc module */
+	struct wcd9xxx_mbhc mbhc;
 };
 
-
 static const u32 comp_shift[] = {
 	0,
 	2,
@@ -1960,173 +1780,6 @@
 	return 0;
 }
 
-static void taiko_codec_enable_audio_mode_bandgap(struct snd_soc_codec *codec)
-{
-	snd_soc_update_bits(codec, TAIKO_A_BIAS_CENTRAL_BG_CTL, 0x80,
-		0x80);
-	snd_soc_update_bits(codec, TAIKO_A_BIAS_CENTRAL_BG_CTL, 0x04,
-		0x04);
-	snd_soc_update_bits(codec, TAIKO_A_BIAS_CENTRAL_BG_CTL, 0x01,
-		0x01);
-	usleep_range(1000, 1000);
-	snd_soc_update_bits(codec, TAIKO_A_BIAS_CENTRAL_BG_CTL, 0x80,
-		0x00);
-}
-
-static void taiko_codec_enable_bandgap(struct snd_soc_codec *codec,
-	enum taiko_bandgap_type choice)
-{
-	struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
-
-	/* TODO lock resources accessed by audio streams and threaded
-	 * interrupt handlers
-	 */
-
-	pr_debug("%s, choice is %d, current is %d\n", __func__, choice,
-		taiko->bandgap_type);
-
-	if (taiko->bandgap_type == choice)
-		return;
-
-	if ((taiko->bandgap_type == TAIKO_BANDGAP_OFF) &&
-		(choice == TAIKO_BANDGAP_AUDIO_MODE)) {
-		taiko_codec_enable_audio_mode_bandgap(codec);
-	} else if (choice == TAIKO_BANDGAP_MBHC_MODE) {
-		/* bandgap mode becomes fast,
-		 * mclk should be off or clk buff source souldn't be VBG
-		 * Let's turn off mclk always */
-		WARN_ON(snd_soc_read(codec, TAIKO_A_CLK_BUFF_EN2) & (1 << 2));
-		snd_soc_update_bits(codec, TAIKO_A_BIAS_CENTRAL_BG_CTL, 0x2,
-			0x2);
-		snd_soc_update_bits(codec, TAIKO_A_BIAS_CENTRAL_BG_CTL, 0x80,
-			0x80);
-		snd_soc_update_bits(codec, TAIKO_A_BIAS_CENTRAL_BG_CTL, 0x4,
-			0x4);
-		snd_soc_update_bits(codec, TAIKO_A_BIAS_CENTRAL_BG_CTL, 0x01,
-			0x01);
-		usleep_range(1000, 1000);
-		snd_soc_update_bits(codec, TAIKO_A_BIAS_CENTRAL_BG_CTL, 0x80,
-			0x00);
-	} else if ((taiko->bandgap_type == TAIKO_BANDGAP_MBHC_MODE) &&
-		(choice == TAIKO_BANDGAP_AUDIO_MODE)) {
-		snd_soc_write(codec, TAIKO_A_BIAS_CENTRAL_BG_CTL, 0x00);
-		usleep_range(100, 100);
-		taiko_codec_enable_audio_mode_bandgap(codec);
-	} else if (choice == TAIKO_BANDGAP_OFF) {
-		snd_soc_write(codec, TAIKO_A_BIAS_CENTRAL_BG_CTL, 0x50);
-	} else {
-		pr_err("%s: Error, Invalid bandgap settings\n", __func__);
-	}
-	taiko->bandgap_type = choice;
-}
-
-static void taiko_codec_disable_clock_block(struct snd_soc_codec *codec)
-{
-	struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
-	pr_debug("%s\n", __func__);
-	snd_soc_update_bits(codec, TAIKO_A_CLK_BUFF_EN2, 0x04, 0x00);
-	usleep_range(50, 50);
-	snd_soc_update_bits(codec, TAIKO_A_CLK_BUFF_EN2, 0x02, 0x02);
-	snd_soc_update_bits(codec, TAIKO_A_CLK_BUFF_EN1, 0x01, 0x00);
-	usleep_range(50, 50);
-	taiko->clock_active = false;
-}
-
-static int taiko_codec_mclk_index(const struct taiko_priv *taiko)
-{
-	if (taiko->mbhc_cfg.mclk_rate == TAIKO_MCLK_RATE_12288KHZ)
-		return 0;
-	else if (taiko->mbhc_cfg.mclk_rate == TAIKO_MCLK_RATE_9600KHZ)
-		return 1;
-	else {
-		BUG_ON(1);
-		return -EINVAL;
-	}
-}
-
-static void taiko_enable_rx_bias(struct snd_soc_codec *codec, u32  enable)
-{
-	struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
-
-	if (enable) {
-		taiko->rx_bias_count++;
-		if (taiko->rx_bias_count == 1)
-			snd_soc_update_bits(codec, TAIKO_A_RX_COM_BIAS,
-				0x80, 0x80);
-	} else {
-		taiko->rx_bias_count--;
-		if (!taiko->rx_bias_count)
-			snd_soc_update_bits(codec, TAIKO_A_RX_COM_BIAS,
-				0x80, 0x00);
-	}
-}
-
-static int taiko_codec_enable_config_mode(struct snd_soc_codec *codec,
-	int enable)
-{
-	struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
-
-	pr_debug("%s: enable = %d\n", __func__, enable);
-	if (enable) {
-
-		snd_soc_update_bits(codec, TAIKO_A_RC_OSC_FREQ, 0x10, 0);
-		/* bandgap mode to fast */
-		snd_soc_write(codec, TAIKO_A_BIAS_OSC_BG_CTL, 0x17);
-		usleep_range(5, 5);
-		snd_soc_update_bits(codec, TAIKO_A_RC_OSC_FREQ, 0x80,
-				    0x80);
-		snd_soc_update_bits(codec, TAIKO_A_RC_OSC_TEST, 0x80,
-				    0x80);
-		usleep_range(10, 10);
-		snd_soc_update_bits(codec, TAIKO_A_RC_OSC_TEST, 0x80, 0);
-		usleep_range(10000, 10000);
-		snd_soc_update_bits(codec, TAIKO_A_CLK_BUFF_EN1, 0x08, 0x08);
-
-	} else {
-		snd_soc_update_bits(codec, TAIKO_A_BIAS_OSC_BG_CTL, 0x1,
-				    0);
-		snd_soc_update_bits(codec, TAIKO_A_RC_OSC_FREQ, 0x80, 0);
-		/* clk source to ext clk and clk buff ref to VBG */
-		snd_soc_update_bits(codec, TAIKO_A_CLK_BUFF_EN1, 0x0C, 0x04);
-	}
-	taiko->config_mode_active = enable ? true : false;
-
-	return 0;
-}
-
-static int taiko_codec_enable_clock_block(struct snd_soc_codec *codec,
-					  int config_mode)
-{
-	struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
-
-	pr_debug("%s: config_mode = %d\n", __func__, config_mode);
-
-	/* transit to RCO requires mclk off */
-	WARN_ON(snd_soc_read(codec, TAIKO_A_CLK_BUFF_EN2) & (1 << 2));
-	if (config_mode) {
-		/* enable RCO and switch to it */
-		taiko_codec_enable_config_mode(codec, 1);
-		snd_soc_write(codec, TAIKO_A_CLK_BUFF_EN2, 0x02);
-		usleep_range(1000, 1000);
-	} else {
-		/* switch to MCLK */
-		snd_soc_update_bits(codec, TAIKO_A_CLK_BUFF_EN1, 0x08, 0x00);
-
-		if (taiko->mbhc_polling_active)
-			snd_soc_write(codec, TAIKO_A_CLK_BUFF_EN2, 0x02);
-		taiko_codec_enable_config_mode(codec, 0);
-	}
-
-	snd_soc_update_bits(codec, TAIKO_A_CLK_BUFF_EN1, 0x01, 0x01);
-	snd_soc_update_bits(codec, TAIKO_A_CLK_BUFF_EN2, 0x02, 0x00);
-	/* on MCLK */
-	snd_soc_update_bits(codec, TAIKO_A_CLK_BUFF_EN2, 0x04, 0x04);
-	snd_soc_update_bits(codec, TAIKO_A_CDC_CLK_MCLK_CTL, 0x01, 0x01);
-	usleep_range(50, 50);
-	taiko->clock_active = true;
-	return 0;
-}
-
 static int taiko_codec_enable_aux_pga(struct snd_soc_dapm_widget *w,
 	struct snd_kcontrol *kcontrol, int event)
 {
@@ -2137,32 +1790,22 @@
 
 	switch (event) {
 	case SND_SOC_DAPM_PRE_PMU:
-		taiko_codec_enable_bandgap(codec, TAIKO_BANDGAP_AUDIO_MODE);
-		taiko_enable_rx_bias(codec, 1);
-
-		if (taiko->aux_pga_cnt++ == 1
-			&& !taiko->mclk_enabled) {
-			taiko_codec_enable_clock_block(codec, 1);
-			pr_debug("AUX PGA enabled RC osc\n");
-		}
+		WCD9XXX_BCL_LOCK(&taiko->resmgr);
+		wcd9xxx_resmgr_get_bandgap(&taiko->resmgr,
+					   WCD9XXX_BANDGAP_AUDIO_MODE);
+		/* AUX PGA requires RCO or MCLK */
+		wcd9xxx_resmgr_get_clk_block(&taiko->resmgr, WCD9XXX_CLK_RCO);
+		wcd9xxx_resmgr_enable_rx_bias(&taiko->resmgr, 1);
+		WCD9XXX_BCL_UNLOCK(&taiko->resmgr);
 		break;
 
 	case SND_SOC_DAPM_POST_PMD:
-		taiko_enable_rx_bias(codec, 0);
-
-		if (taiko->aux_pga_cnt-- == 0) {
-			if (taiko->mbhc_polling_active)
-				taiko_codec_enable_bandgap(codec,
-					TAIKO_BANDGAP_MBHC_MODE);
-			else
-				taiko_codec_enable_bandgap(codec,
-					TAIKO_BANDGAP_OFF);
-
-			if (!taiko->mclk_enabled &&
-				!taiko->mbhc_polling_active) {
-				taiko_codec_enable_clock_block(codec, 0);
-			}
-		}
+		WCD9XXX_BCL_LOCK(&taiko->resmgr);
+		wcd9xxx_resmgr_enable_rx_bias(&taiko->resmgr, 0);
+		wcd9xxx_resmgr_put_bandgap(&taiko->resmgr,
+					   WCD9XXX_BANDGAP_AUDIO_MODE);
+		wcd9xxx_resmgr_put_clk_block(&taiko->resmgr, WCD9XXX_CLK_RCO);
+		WCD9XXX_BCL_UNLOCK(&taiko->resmgr);
 		break;
 	}
 	return 0;
@@ -2389,358 +2032,47 @@
 	return 0;
 }
 
-/* called under codec_resource_lock acquisition */
-static void taiko_codec_start_hs_polling(struct snd_soc_codec *codec)
-{
-	struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
-	int mbhc_state = taiko->mbhc_state;
-
-	pr_debug("%s: enter\n", __func__);
-	if (!taiko->mbhc_polling_active) {
-		pr_debug("Polling is not active, do not start polling\n");
-		return;
-	}
-	snd_soc_write(codec, TAIKO_A_MBHC_SCALING_MUX_1, 0x84);
-
-	if (!taiko->no_mic_headset_override) {
-		if (mbhc_state == MBHC_STATE_POTENTIAL) {
-			pr_debug("%s recovering MBHC state macine\n", __func__);
-			taiko->mbhc_state = MBHC_STATE_POTENTIAL_RECOVERY;
-			/* set to max button press threshold */
-			snd_soc_write(codec, TAIKO_A_CDC_MBHC_VOLT_B2_CTL,
-				      0x7F);
-			snd_soc_write(codec, TAIKO_A_CDC_MBHC_VOLT_B1_CTL,
-				      0xFF);
-			snd_soc_write(codec, TAIKO_A_CDC_MBHC_VOLT_B4_CTL,
-				       0x7F);
-			snd_soc_write(codec, TAIKO_A_CDC_MBHC_VOLT_B3_CTL,
-				      0xFF);
-			/* set to max */
-			snd_soc_write(codec, TAIKO_A_CDC_MBHC_VOLT_B6_CTL,
-				      0x7F);
-			snd_soc_write(codec, TAIKO_A_CDC_MBHC_VOLT_B5_CTL,
-				      0xFF);
-		}
-	}
-
-	snd_soc_write(codec, TAIKO_A_CDC_MBHC_EN_CTL, 0x1);
-	snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_CLK_CTL, 0x8, 0x0);
-	snd_soc_write(codec, TAIKO_A_CDC_MBHC_EN_CTL, 0x1);
-	pr_debug("%s: leave\n", __func__);
-}
-
-/* called under codec_resource_lock acquisition */
-static void taiko_codec_pause_hs_polling(struct snd_soc_codec *codec)
-{
-	struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
-
-	pr_debug("%s: enter\n", __func__);
-	if (!taiko->mbhc_polling_active) {
-		pr_debug("polling not active, nothing to pause\n");
-		return;
-	}
-
-	snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_CLK_CTL, 0x8, 0x8);
-	pr_debug("%s: leave\n", __func__);
-}
-
-static void taiko_codec_switch_cfilt_mode(struct snd_soc_codec *codec, int mode)
-{
-	struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
-	u8 reg_mode_val, cur_mode_val;
-	bool mbhc_was_polling = false;
-
-	if (mode)
-		reg_mode_val = TAIKO_CFILT_FAST_MODE;
-	else
-		reg_mode_val = TAIKO_CFILT_SLOW_MODE;
-
-	cur_mode_val = snd_soc_read(codec,
-					taiko->mbhc_bias_regs.cfilt_ctl) & 0x40;
-
-	if (cur_mode_val != reg_mode_val) {
-		TAIKO_ACQUIRE_LOCK(taiko->codec_resource_lock);
-		if (taiko->mbhc_polling_active) {
-			taiko_codec_pause_hs_polling(codec);
-			mbhc_was_polling = true;
-		}
-		snd_soc_update_bits(codec,
-			taiko->mbhc_bias_regs.cfilt_ctl, 0x40, reg_mode_val);
-		if (mbhc_was_polling)
-			taiko_codec_start_hs_polling(codec);
-		TAIKO_RELEASE_LOCK(taiko->codec_resource_lock);
-		pr_debug("%s: CFILT mode change (%x to %x)\n", __func__,
-			cur_mode_val, reg_mode_val);
-	} else {
-		pr_debug("%s: CFILT Value is already %x\n",
-			__func__, cur_mode_val);
-	}
-}
-
-static void taiko_codec_update_cfilt_usage(struct snd_soc_codec *codec,
-					   u8 cfilt_sel, int inc)
-{
-	struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
-	u32 *cfilt_cnt_ptr = NULL;
-	u16 micb_cfilt_reg;
-
-	switch (cfilt_sel) {
-	case TAIKO_CFILT1_SEL:
-		cfilt_cnt_ptr = &taiko->cfilt1_cnt;
-		micb_cfilt_reg = TAIKO_A_MICB_CFILT_1_CTL;
-		break;
-	case TAIKO_CFILT2_SEL:
-		cfilt_cnt_ptr = &taiko->cfilt2_cnt;
-		micb_cfilt_reg = TAIKO_A_MICB_CFILT_2_CTL;
-		break;
-	case TAIKO_CFILT3_SEL:
-		cfilt_cnt_ptr = &taiko->cfilt3_cnt;
-		micb_cfilt_reg = TAIKO_A_MICB_CFILT_3_CTL;
-		break;
-	default:
-		return; /* should not happen */
-	}
-
-	if (inc) {
-		if (!(*cfilt_cnt_ptr)++) {
-			/* Switch CFILT to slow mode if MBHC CFILT being used */
-			if (cfilt_sel == taiko->mbhc_bias_regs.cfilt_sel)
-				taiko_codec_switch_cfilt_mode(codec, 0);
-
-			snd_soc_update_bits(codec, micb_cfilt_reg, 0x80, 0x80);
-		}
-	} else {
-		/* check if count not zero, decrement
-		 * then check if zero, go ahead disable cfilter
-		 */
-		if ((*cfilt_cnt_ptr) && !--(*cfilt_cnt_ptr)) {
-			snd_soc_update_bits(codec, micb_cfilt_reg, 0x80, 0);
-
-			/* Switch CFILT to fast mode if MBHC CFILT being used */
-			if (cfilt_sel == taiko->mbhc_bias_regs.cfilt_sel)
-				taiko_codec_switch_cfilt_mode(codec, 1);
-		}
-	}
-}
-
-static int taiko_find_k_value(unsigned int ldoh_v, unsigned int cfilt_mv)
-{
-	int rc = -EINVAL;
-	unsigned min_mv, max_mv;
-
-	switch (ldoh_v) {
-	case TAIKO_LDOH_1P95_V:
-		min_mv = 160;
-		max_mv = 1800;
-		break;
-	case TAIKO_LDOH_2P35_V:
-		min_mv = 200;
-		max_mv = 2200;
-		break;
-	case TAIKO_LDOH_2P75_V:
-		min_mv = 240;
-		max_mv = 2600;
-		break;
-	case TAIKO_LDOH_2P85_V:
-		min_mv = 250;
-		max_mv = 2700;
-		break;
-	default:
-		goto done;
-	}
-
-	if (cfilt_mv < min_mv || cfilt_mv > max_mv)
-		goto done;
-
-	for (rc = 4; rc <= 44; rc++) {
-		min_mv = max_mv * (rc) / 44;
-		if (min_mv >= cfilt_mv) {
-			rc -= 4;
-			break;
-		}
-	}
-done:
-	return rc;
-}
-
-static bool taiko_is_hph_pa_on(struct snd_soc_codec *codec)
-{
-	u8 hph_reg_val = 0;
-	hph_reg_val = snd_soc_read(codec, TAIKO_A_RX_HPH_CNP_EN);
-
-	return (hph_reg_val & 0x30) ? true : false;
-}
-
-static bool taiko_is_hph_dac_on(struct snd_soc_codec *codec, int left)
-{
-	u8 hph_reg_val = 0;
-	if (left)
-		hph_reg_val = snd_soc_read(codec,
-					   TAIKO_A_RX_HPH_L_DAC_CTL);
-	else
-		hph_reg_val = snd_soc_read(codec,
-					   TAIKO_A_RX_HPH_R_DAC_CTL);
-
-	return (hph_reg_val & 0xC0) ? true : false;
-}
-
-static void taiko_turn_onoff_override(struct snd_soc_codec *codec, bool on)
-{
-	snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_B1_CTL, 0x04, on << 2);
-}
-
-/* called under codec_resource_lock acquisition */
-static void taiko_codec_drive_v_to_micbias(struct snd_soc_codec *codec,
-					   int usec)
-{
-	int cfilt_k_val;
-	bool set = true;
-	struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
-
-	if (taiko->mbhc_data.micb_mv != VDDIO_MICBIAS_MV &&
-	    taiko->mbhc_micbias_switched) {
-		pr_debug("%s: set mic V to micbias V\n", __func__);
-		snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_CLK_CTL, 0x2, 0x2);
-		taiko_turn_onoff_override(codec, true);
-		while (1) {
-			cfilt_k_val = taiko_find_k_value(
-						taiko->pdata->micbias.ldoh_v,
-						set ? taiko->mbhc_data.micb_mv :
-						      VDDIO_MICBIAS_MV);
-			snd_soc_update_bits(codec,
-					    taiko->mbhc_bias_regs.cfilt_val,
-					    0xFC, (cfilt_k_val << 2));
-			if (!set)
-				break;
-			usleep_range(usec, usec);
-			set = false;
-		}
-		taiko_turn_onoff_override(codec, false);
-	}
-}
-
-/* called under codec_resource_lock acquisition */
-static void __taiko_codec_switch_micbias(struct snd_soc_codec *codec,
-					 int vddio_switch, bool restartpolling,
-					 bool checkpolling)
-{
-	int cfilt_k_val;
-	bool override;
-	struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
-
-	if (vddio_switch && !taiko->mbhc_micbias_switched &&
-	    (!checkpolling || taiko->mbhc_polling_active)) {
-		if (restartpolling)
-			taiko_codec_pause_hs_polling(codec);
-		override = snd_soc_read(codec, TAIKO_A_CDC_MBHC_B1_CTL) & 0x04;
-		if (!override)
-			taiko_turn_onoff_override(codec, true);
-		/* Adjust threshold if Mic Bias voltage changes */
-		if (taiko->mbhc_data.micb_mv != VDDIO_MICBIAS_MV) {
-			cfilt_k_val = taiko_find_k_value(
-						   taiko->pdata->micbias.ldoh_v,
-						   VDDIO_MICBIAS_MV);
-			usleep_range(10000, 10000);
-			snd_soc_update_bits(codec,
-					    taiko->mbhc_bias_regs.cfilt_val,
-					    0xFC, (cfilt_k_val << 2));
-			usleep_range(10000, 10000);
-			snd_soc_write(codec, TAIKO_A_CDC_MBHC_VOLT_B1_CTL,
-				      taiko->mbhc_data.adj_v_ins_hu & 0xFF);
-			snd_soc_write(codec, TAIKO_A_CDC_MBHC_VOLT_B2_CTL,
-				      (taiko->mbhc_data.adj_v_ins_hu >> 8) &
-				       0xFF);
-			pr_debug("%s: Programmed MBHC thresholds to VDDIO\n",
-				 __func__);
-		}
-
-		/* enable MIC BIAS Switch to VDDIO */
-		snd_soc_update_bits(codec, taiko->mbhc_bias_regs.mbhc_reg,
-				    0x80, 0x80);
-		snd_soc_update_bits(codec, taiko->mbhc_bias_regs.mbhc_reg,
-				    0x10, 0x00);
-		if (!override)
-			taiko_turn_onoff_override(codec, false);
-		if (restartpolling)
-			taiko_codec_start_hs_polling(codec);
-
-		taiko->mbhc_micbias_switched = true;
-		pr_debug("%s: VDDIO switch enabled\n", __func__);
-	} else if (!vddio_switch && taiko->mbhc_micbias_switched) {
-		if ((!checkpolling || taiko->mbhc_polling_active) &&
-		    restartpolling)
-			taiko_codec_pause_hs_polling(codec);
-		/* Reprogram thresholds */
-		if (taiko->mbhc_data.micb_mv != VDDIO_MICBIAS_MV) {
-			cfilt_k_val = taiko_find_k_value(
-						   taiko->pdata->micbias.ldoh_v,
-						   taiko->mbhc_data.micb_mv);
-			snd_soc_update_bits(codec,
-					    taiko->mbhc_bias_regs.cfilt_val,
-					    0xFC, (cfilt_k_val << 2));
-			usleep_range(10000, 10000);
-			snd_soc_write(codec, TAIKO_A_CDC_MBHC_VOLT_B1_CTL,
-				      taiko->mbhc_data.v_ins_hu & 0xFF);
-			snd_soc_write(codec, TAIKO_A_CDC_MBHC_VOLT_B2_CTL,
-				      (taiko->mbhc_data.v_ins_hu >> 8) & 0xFF);
-			pr_debug("%s: Programmed MBHC thresholds to MICBIAS\n",
-				 __func__);
-		}
-
-		/* Disable MIC BIAS Switch to VDDIO */
-		snd_soc_update_bits(codec, taiko->mbhc_bias_regs.mbhc_reg,
-				    0x80, 0x00);
-		snd_soc_update_bits(codec, taiko->mbhc_bias_regs.mbhc_reg,
-				    0x10, 0x00);
-
-		if ((!checkpolling || taiko->mbhc_polling_active) &&
-		    restartpolling)
-			taiko_codec_start_hs_polling(codec);
-
-		taiko->mbhc_micbias_switched = false;
-		pr_debug("%s: VDDIO switch disabled\n", __func__);
-	}
-}
-
-static void taiko_codec_switch_micbias(struct snd_soc_codec *codec,
-				       int vddio_switch)
-{
-	return __taiko_codec_switch_micbias(codec, vddio_switch, true, true);
-}
-
 static int taiko_codec_enable_micbias(struct snd_soc_dapm_widget *w,
 	struct snd_kcontrol *kcontrol, int event)
 {
 	struct snd_soc_codec *codec = w->codec;
 	struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
 	u16 micb_int_reg;
-	int micb_line;
 	u8 cfilt_sel_val = 0;
 	char *internal1_text = "Internal1";
 	char *internal2_text = "Internal2";
 	char *internal3_text = "Internal3";
+	enum wcd9xxx_notify_event e_post_off, e_pre_on, e_post_on;
 
 	pr_debug("%s %d\n", __func__, event);
 	switch (w->reg) {
 	case TAIKO_A_MICB_1_CTL:
 		micb_int_reg = TAIKO_A_MICB_1_INT_RBIAS;
-		cfilt_sel_val = taiko->pdata->micbias.bias1_cfilt_sel;
-		micb_line = TAIKO_MICBIAS1;
+		cfilt_sel_val = taiko->resmgr.pdata->micbias.bias1_cfilt_sel;
+		e_pre_on = WCD9XXX_EVENT_PRE_MICBIAS_1_ON;
+		e_post_on = WCD9XXX_EVENT_POST_MICBIAS_1_ON;
+		e_post_off = WCD9XXX_EVENT_POST_MICBIAS_1_OFF;
 		break;
 	case TAIKO_A_MICB_2_CTL:
 		micb_int_reg = TAIKO_A_MICB_2_INT_RBIAS;
-		cfilt_sel_val = taiko->pdata->micbias.bias2_cfilt_sel;
-		micb_line = TAIKO_MICBIAS2;
+		cfilt_sel_val = taiko->resmgr.pdata->micbias.bias2_cfilt_sel;
+		e_pre_on = WCD9XXX_EVENT_PRE_MICBIAS_2_ON;
+		e_post_on = WCD9XXX_EVENT_POST_MICBIAS_2_ON;
+		e_post_off = WCD9XXX_EVENT_POST_MICBIAS_2_OFF;
 		break;
 	case TAIKO_A_MICB_3_CTL:
 		micb_int_reg = TAIKO_A_MICB_3_INT_RBIAS;
-		cfilt_sel_val = taiko->pdata->micbias.bias3_cfilt_sel;
-		micb_line = TAIKO_MICBIAS3;
+		cfilt_sel_val = taiko->resmgr.pdata->micbias.bias3_cfilt_sel;
+		e_pre_on = WCD9XXX_EVENT_PRE_MICBIAS_3_ON;
+		e_post_on = WCD9XXX_EVENT_POST_MICBIAS_3_ON;
+		e_post_off = WCD9XXX_EVENT_POST_MICBIAS_3_OFF;
 		break;
 	case TAIKO_A_MICB_4_CTL:
-		micb_int_reg = taiko->reg_addr.micb_4_int_rbias;
-		cfilt_sel_val = taiko->pdata->micbias.bias4_cfilt_sel;
-		micb_line = TAIKO_MICBIAS4;
+		micb_int_reg = taiko->resmgr.reg_addr->micb_4_int_rbias;
+		cfilt_sel_val = taiko->resmgr.pdata->micbias.bias4_cfilt_sel;
+		e_pre_on = WCD9XXX_EVENT_PRE_MICBIAS_4_ON;
+		e_post_on = WCD9XXX_EVENT_POST_MICBIAS_4_ON;
+		e_post_off = WCD9XXX_EVENT_POST_MICBIAS_4_OFF;
 		break;
 	default:
 		pr_err("%s: Error, invalid micbias register\n", __func__);
@@ -2749,15 +2081,12 @@
 
 	switch (event) {
 	case SND_SOC_DAPM_PRE_PMU:
-		/* Decide whether to switch the micbias for MBHC */
-		if (w->reg == taiko->mbhc_bias_regs.ctl_reg) {
-			TAIKO_ACQUIRE_LOCK(taiko->codec_resource_lock);
-			taiko_codec_switch_micbias(codec, 0);
-			TAIKO_RELEASE_LOCK(taiko->codec_resource_lock);
-		}
+		/* Let MBHC module know so micbias switch to be off */
+		wcd9xxx_resmgr_notifier_call(&taiko->resmgr, e_pre_on);
 
 		snd_soc_update_bits(codec, w->reg, 0x0E, 0x0A);
-		taiko_codec_update_cfilt_usage(codec, cfilt_sel_val, 1);
+		/* Get cfilt */
+		wcd9xxx_resmgr_cfilt_get(&taiko->resmgr, cfilt_sel_val);
 
 		if (strnstr(w->name, internal1_text, 30))
 			snd_soc_update_bits(codec, micb_int_reg, 0xE0, 0xE0);
@@ -2768,25 +2097,13 @@
 
 		break;
 	case SND_SOC_DAPM_POST_PMU:
-
 		usleep_range(20000, 20000);
-
-		if (taiko->mbhc_polling_active &&
-		    taiko->mbhc_cfg.micbias == micb_line) {
-			TAIKO_ACQUIRE_LOCK(taiko->codec_resource_lock);
-			taiko_codec_pause_hs_polling(codec);
-			taiko_codec_start_hs_polling(codec);
-			TAIKO_RELEASE_LOCK(taiko->codec_resource_lock);
-		}
+		/* Let MBHC module know so micbias is on */
+		wcd9xxx_resmgr_notifier_call(&taiko->resmgr, e_post_on);
 		break;
-
 	case SND_SOC_DAPM_POST_PMD:
-		if ((w->reg == taiko->mbhc_bias_regs.ctl_reg) &&
-		    taiko_is_hph_pa_on(codec)) {
-			TAIKO_ACQUIRE_LOCK(taiko->codec_resource_lock);
-			taiko_codec_switch_micbias(codec, 1);
-			TAIKO_RELEASE_LOCK(taiko->codec_resource_lock);
-		}
+		/* Let MBHC module know so micbias switch to be off */
+		wcd9xxx_resmgr_notifier_call(&taiko->resmgr, e_post_off);
 
 		if (strnstr(w->name, internal1_text, 30))
 			snd_soc_update_bits(codec, micb_int_reg, 0x80, 0x00);
@@ -2795,7 +2112,8 @@
 		else if (strnstr(w->name, internal3_text, 30))
 			snd_soc_update_bits(codec, micb_int_reg, 0x2, 0x0);
 
-		taiko_codec_update_cfilt_usage(codec, cfilt_sel_val, 0);
+		/* Put cfilt */
+		wcd9xxx_resmgr_cfilt_put(&taiko->resmgr, cfilt_sel_val);
 		break;
 	}
 
@@ -2997,15 +2315,16 @@
 	struct snd_kcontrol *kcontrol, int event)
 {
 	struct snd_soc_codec *codec = w->codec;
+	struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
 
 	pr_debug("%s %d\n", __func__, event);
 
 	switch (event) {
 	case SND_SOC_DAPM_PRE_PMU:
-		taiko_enable_rx_bias(codec, 1);
+		wcd9xxx_resmgr_enable_rx_bias(&taiko->resmgr, 1);
 		break;
 	case SND_SOC_DAPM_POST_PMD:
-		taiko_enable_rx_bias(codec, 0);
+		wcd9xxx_resmgr_enable_rx_bias(&taiko->resmgr, 0);
 		break;
 	}
 	return 0;
@@ -3028,83 +2347,32 @@
 	return 0;
 }
 
-static void taiko_snd_soc_jack_report(struct taiko_priv *taiko,
-				      struct snd_soc_jack *jack, int status,
-				      int mask)
-{
-	/* XXX: wake_lock_timeout()? */
-	snd_soc_jack_report_no_dapm(jack, status, mask);
-}
-
-static void hphocp_off_report(struct taiko_priv *taiko,
-	u32 jack_status, int irq)
-{
-	struct snd_soc_codec *codec;
-	if (!taiko) {
-		pr_err("%s: Bad taiko private data\n", __func__);
-		return;
-	}
-
-	pr_debug("%s: clear ocp status %x\n", __func__, jack_status);
-	codec = taiko->codec;
-	if (taiko->hph_status & jack_status) {
-		taiko->hph_status &= ~jack_status;
-		if (taiko->mbhc_cfg.headset_jack)
-			taiko_snd_soc_jack_report(taiko,
-						  taiko->mbhc_cfg.headset_jack,
-						  taiko->hph_status,
-						  TAIKO_JACK_MASK);
-		snd_soc_update_bits(codec, TAIKO_A_RX_HPH_OCP_CTL, 0x10, 0x00);
-		snd_soc_update_bits(codec, TAIKO_A_RX_HPH_OCP_CTL, 0x10, 0x10);
-		/* reset retry counter as PA is turned off signifying
-		 * start of new OCP detection session
-		 */
-		if (WCD9XXX_IRQ_HPH_PA_OCPL_FAULT)
-			taiko->hphlocp_cnt = 0;
-		else
-			taiko->hphrocp_cnt = 0;
-		wcd9xxx_enable_irq(codec->control_data, irq);
-	}
-}
-
-static void hphlocp_off_report(struct work_struct *work)
-{
-	struct taiko_priv *taiko = container_of(work, struct taiko_priv,
-						hphlocp_work);
-	hphocp_off_report(taiko, SND_JACK_OC_HPHL,
-			  WCD9XXX_IRQ_HPH_PA_OCPL_FAULT);
-}
-
-static void hphrocp_off_report(struct work_struct *work)
-{
-	struct taiko_priv *taiko = container_of(work, struct taiko_priv,
-						hphrocp_work);
-	hphocp_off_report(taiko, SND_JACK_OC_HPHR,
-			  WCD9XXX_IRQ_HPH_PA_OCPR_FAULT);
-}
-
 static int taiko_hph_pa_event(struct snd_soc_dapm_widget *w,
-	struct snd_kcontrol *kcontrol, int event)
+			      struct snd_kcontrol *kcontrol, int event)
 {
 	struct snd_soc_codec *codec = w->codec;
 	struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
-	u8 mbhc_micb_ctl_val;
+	enum wcd9xxx_notify_event e_pre_on, e_post_off;
+
 	pr_debug("%s: %s event = %d\n", __func__, w->name, event);
+	if (w->shift == 5) {
+		e_pre_on = WCD9XXX_EVENT_PRE_HPHR_PA_ON;
+		e_post_off = WCD9XXX_EVENT_POST_HPHR_PA_OFF;
+	} else if (w->shift == 4) {
+		e_pre_on = WCD9XXX_EVENT_PRE_HPHL_PA_ON;
+		e_post_off = WCD9XXX_EVENT_POST_HPHL_PA_OFF;
+	} else {
+		pr_err("%s: Invalid w->shift %d\n", __func__, w->shift);
+		return -EINVAL;
+	}
 
 	switch (event) {
 	case SND_SOC_DAPM_PRE_PMU:
-		mbhc_micb_ctl_val = snd_soc_read(codec,
-				taiko->mbhc_bias_regs.ctl_reg);
-
-		if (!(mbhc_micb_ctl_val & 0x80)) {
-			TAIKO_ACQUIRE_LOCK(taiko->codec_resource_lock);
-			taiko_codec_switch_micbias(codec, 1);
-			TAIKO_RELEASE_LOCK(taiko->codec_resource_lock);
-		}
+		/* Let MBHC module know PA is turning on */
+		wcd9xxx_resmgr_notifier_call(&taiko->resmgr, e_pre_on);
 		break;
 
 	case SND_SOC_DAPM_POST_PMU:
-
 		usleep_range(10000, 10000);
 
 		snd_soc_update_bits(codec, TAIKO_A_BUCK_MODE_5, 0x02, 0x00);
@@ -3113,100 +2381,26 @@
 		snd_soc_update_bits(codec, TAIKO_A_BUCK_MODE_3, 0x08, 0x00);
 
 		usleep_range(10, 10);
-
 		break;
 
 	case SND_SOC_DAPM_POST_PMD:
-		/* schedule work is required because at the time HPH PA DAPM
+		/* Let MBHC module know PA turned off */
+		wcd9xxx_resmgr_notifier_call(&taiko->resmgr, e_post_off);
+
+		/*
+		 * schedule work is required because at the time HPH PA DAPM
 		 * event callback is called by DAPM framework, CODEC dapm mutex
 		 * would have been locked while snd_soc_jack_report also
 		 * attempts to acquire same lock.
 		 */
-		if (w->shift == 5) {
-			clear_bit(TAIKO_HPHL_PA_OFF_ACK,
-				  &taiko->hph_pa_dac_state);
-			clear_bit(TAIKO_HPHL_DAC_OFF_ACK,
-				  &taiko->hph_pa_dac_state);
-			if (taiko->hph_status & SND_JACK_OC_HPHL)
-				schedule_work(&taiko->hphlocp_work);
-		} else if (w->shift == 4) {
-			clear_bit(TAIKO_HPHR_PA_OFF_ACK,
-				  &taiko->hph_pa_dac_state);
-			clear_bit(TAIKO_HPHR_DAC_OFF_ACK,
-				  &taiko->hph_pa_dac_state);
-			if (taiko->hph_status & SND_JACK_OC_HPHR)
-				schedule_work(&taiko->hphrocp_work);
-		}
-
-		TAIKO_ACQUIRE_LOCK(taiko->codec_resource_lock);
-		taiko_codec_switch_micbias(codec, 0);
-		TAIKO_RELEASE_LOCK(taiko->codec_resource_lock);
-
 		pr_debug("%s: sleep 10 ms after %s PA disable.\n", __func__,
-				w->name);
+			 w->name);
 		usleep_range(10000, 10000);
 		break;
 	}
 	return 0;
 }
 
-static void taiko_get_mbhc_micbias_regs(struct snd_soc_codec *codec,
-					struct mbhc_micbias_regs *micbias_regs)
-{
-	struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
-	unsigned int cfilt;
-
-	switch (taiko->mbhc_cfg.micbias) {
-	case TAIKO_MICBIAS1:
-		cfilt = taiko->pdata->micbias.bias1_cfilt_sel;
-		micbias_regs->mbhc_reg = TAIKO_A_MICB_1_MBHC;
-		micbias_regs->int_rbias = TAIKO_A_MICB_1_INT_RBIAS;
-		micbias_regs->ctl_reg = TAIKO_A_MICB_1_CTL;
-		break;
-	case TAIKO_MICBIAS2:
-		cfilt = taiko->pdata->micbias.bias2_cfilt_sel;
-		micbias_regs->mbhc_reg = TAIKO_A_MICB_2_MBHC;
-		micbias_regs->int_rbias = TAIKO_A_MICB_2_INT_RBIAS;
-		micbias_regs->ctl_reg = TAIKO_A_MICB_2_CTL;
-		break;
-	case TAIKO_MICBIAS3:
-		cfilt = taiko->pdata->micbias.bias3_cfilt_sel;
-		micbias_regs->mbhc_reg = TAIKO_A_MICB_3_MBHC;
-		micbias_regs->int_rbias = TAIKO_A_MICB_3_INT_RBIAS;
-		micbias_regs->ctl_reg = TAIKO_A_MICB_3_CTL;
-		break;
-	case TAIKO_MICBIAS4:
-		cfilt = taiko->pdata->micbias.bias4_cfilt_sel;
-		micbias_regs->mbhc_reg = taiko->reg_addr.micb_4_mbhc;
-		micbias_regs->int_rbias = taiko->reg_addr.micb_4_int_rbias;
-		micbias_regs->ctl_reg = taiko->reg_addr.micb_4_ctl;
-		break;
-	default:
-		/* Should never reach here */
-		pr_err("%s: Invalid MIC BIAS for MBHC\n", __func__);
-		return;
-	}
-
-	micbias_regs->cfilt_sel = cfilt;
-
-	switch (cfilt) {
-	case TAIKO_CFILT1_SEL:
-		micbias_regs->cfilt_val = TAIKO_A_MICB_CFILT_1_VAL;
-		micbias_regs->cfilt_ctl = TAIKO_A_MICB_CFILT_1_CTL;
-		taiko->mbhc_data.micb_mv = taiko->pdata->micbias.cfilt1_mv;
-		break;
-	case TAIKO_CFILT2_SEL:
-		micbias_regs->cfilt_val = TAIKO_A_MICB_CFILT_2_VAL;
-		micbias_regs->cfilt_ctl = TAIKO_A_MICB_CFILT_2_CTL;
-		taiko->mbhc_data.micb_mv = taiko->pdata->micbias.cfilt2_mv;
-		break;
-	case TAIKO_CFILT3_SEL:
-		micbias_regs->cfilt_val = TAIKO_A_MICB_CFILT_3_VAL;
-		micbias_regs->cfilt_ctl = TAIKO_A_MICB_CFILT_3_CTL;
-		taiko->mbhc_data.micb_mv = taiko->pdata->micbias.cfilt3_mv;
-		break;
-	}
-}
 static const struct snd_soc_dapm_widget taiko_dapm_i2s_widgets[] = {
 	SND_SOC_DAPM_SUPPLY("RX_I2S_CLK", TAIKO_A_CDC_CLK_RX_I2S_CTL,
 	4, 0, NULL, 0),
@@ -3790,6 +2984,9 @@
 	if (reg == TAIKO_A_RX_HPH_L_STATUS || reg == TAIKO_A_RX_HPH_R_STATUS)
 		return 1;
 
+	if (reg == TAIKO_A_MBHC_INSERT_DET_STATUS)
+		return 1;
+
 	return 0;
 }
 
@@ -3838,79 +3035,6 @@
 	return val;
 }
 
-static s16 taiko_get_current_v_ins(struct taiko_priv *taiko, bool hu)
-{
-	s16 v_ins;
-	if ((taiko->mbhc_data.micb_mv != VDDIO_MICBIAS_MV) &&
-	    taiko->mbhc_micbias_switched)
-		v_ins = hu ? (s16)taiko->mbhc_data.adj_v_ins_hu :
-			     (s16)taiko->mbhc_data.adj_v_ins_h;
-	else
-		v_ins = hu ? (s16)taiko->mbhc_data.v_ins_hu :
-			     (s16)taiko->mbhc_data.v_ins_h;
-	return v_ins;
-}
-
-static s16 taiko_get_current_v_hs_max(struct taiko_priv *taiko)
-{
-	s16 v_hs_max;
-	struct taiko_mbhc_plug_type_cfg *plug_type;
-
-	plug_type = TAIKO_MBHC_CAL_PLUG_TYPE_PTR(taiko->mbhc_cfg.calibration);
-	if ((taiko->mbhc_data.micb_mv != VDDIO_MICBIAS_MV) &&
-	    taiko->mbhc_micbias_switched)
-		v_hs_max = taiko->mbhc_data.adj_v_hs_max;
-	else
-		v_hs_max = plug_type->v_hs_max;
-	return v_hs_max;
-}
-
-static void taiko_codec_calibrate_hs_polling(struct snd_soc_codec *codec)
-{
-	u8 *n_ready, *n_cic;
-	struct taiko_mbhc_btn_detect_cfg *btn_det;
-	struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
-	const s16 v_ins_hu = taiko_get_current_v_ins(taiko, true);
-
-	btn_det = TAIKO_MBHC_CAL_BTN_DET_PTR(taiko->mbhc_cfg.calibration);
-
-	snd_soc_write(codec, TAIKO_A_CDC_MBHC_VOLT_B1_CTL,
-		      v_ins_hu & 0xFF);
-	snd_soc_write(codec, TAIKO_A_CDC_MBHC_VOLT_B2_CTL,
-		      (v_ins_hu >> 8) & 0xFF);
-
-	snd_soc_write(codec, TAIKO_A_CDC_MBHC_VOLT_B3_CTL,
-		      taiko->mbhc_data.v_b1_hu & 0xFF);
-	snd_soc_write(codec, TAIKO_A_CDC_MBHC_VOLT_B4_CTL,
-		      (taiko->mbhc_data.v_b1_hu >> 8) & 0xFF);
-
-	snd_soc_write(codec, TAIKO_A_CDC_MBHC_VOLT_B5_CTL,
-		      taiko->mbhc_data.v_b1_h & 0xFF);
-	snd_soc_write(codec, TAIKO_A_CDC_MBHC_VOLT_B6_CTL,
-		      (taiko->mbhc_data.v_b1_h >> 8) & 0xFF);
-
-	snd_soc_write(codec, TAIKO_A_CDC_MBHC_VOLT_B9_CTL,
-		      taiko->mbhc_data.v_brh & 0xFF);
-	snd_soc_write(codec, TAIKO_A_CDC_MBHC_VOLT_B10_CTL,
-		      (taiko->mbhc_data.v_brh >> 8) & 0xFF);
-
-	snd_soc_write(codec, TAIKO_A_CDC_MBHC_VOLT_B11_CTL,
-		      taiko->mbhc_data.v_brl & 0xFF);
-	snd_soc_write(codec, TAIKO_A_CDC_MBHC_VOLT_B12_CTL,
-		      (taiko->mbhc_data.v_brl >> 8) & 0xFF);
-
-	n_ready = taiko_mbhc_cal_btn_det_mp(btn_det, TAIKO_BTN_DET_N_READY);
-	snd_soc_write(codec, TAIKO_A_CDC_MBHC_TIMER_B1_CTL,
-		      n_ready[taiko_codec_mclk_index(taiko)]);
-	snd_soc_write(codec, TAIKO_A_CDC_MBHC_TIMER_B2_CTL,
-		      taiko->mbhc_data.npoll);
-	snd_soc_write(codec, TAIKO_A_CDC_MBHC_TIMER_B3_CTL,
-		      taiko->mbhc_data.nbounce_wait);
-	n_cic = taiko_mbhc_cal_btn_det_mp(btn_det, TAIKO_BTN_DET_N_CIC);
-	snd_soc_write(codec, TAIKO_A_CDC_MBHC_TIMER_B6_CTL,
-		      n_cic[taiko_codec_mclk_index(taiko)]);
-}
-
 static int taiko_startup(struct snd_pcm_substream *substream,
 		struct snd_soc_dai *dai)
 {
@@ -3945,54 +3069,20 @@
 
 	pr_debug("%s: mclk_enable = %u, dapm = %d\n", __func__, mclk_enable,
 		 dapm);
-	if (dapm)
-		TAIKO_ACQUIRE_LOCK(taiko->codec_resource_lock);
+
+	WCD9XXX_BCL_LOCK(&taiko->resmgr);
 	if (mclk_enable) {
-		taiko->mclk_enabled = true;
-
-		if (taiko->mbhc_polling_active) {
-			taiko_codec_pause_hs_polling(codec);
-			taiko_codec_disable_clock_block(codec);
-			taiko_codec_enable_bandgap(codec,
-						   TAIKO_BANDGAP_AUDIO_MODE);
-			taiko_codec_enable_clock_block(codec, 0);
-			taiko_codec_calibrate_hs_polling(codec);
-			taiko_codec_start_hs_polling(codec);
-		} else {
-			taiko_codec_disable_clock_block(codec);
-			taiko_codec_enable_bandgap(codec,
-						   TAIKO_BANDGAP_AUDIO_MODE);
-			taiko_codec_enable_clock_block(codec, 0);
-		}
+		wcd9xxx_resmgr_get_bandgap(&taiko->resmgr,
+					   WCD9XXX_BANDGAP_AUDIO_MODE);
+		wcd9xxx_resmgr_get_clk_block(&taiko->resmgr, WCD9XXX_CLK_MCLK);
 	} else {
-
-		if (!taiko->mclk_enabled) {
-			if (dapm)
-				TAIKO_RELEASE_LOCK(taiko->codec_resource_lock);
-			pr_err("Error, MCLK already diabled\n");
-			return -EINVAL;
-		}
-		taiko->mclk_enabled = false;
-
-		if (taiko->mbhc_polling_active) {
-			taiko_codec_pause_hs_polling(codec);
-			taiko_codec_disable_clock_block(codec);
-			taiko_codec_enable_bandgap(codec,
-						   TAIKO_BANDGAP_MBHC_MODE);
-			taiko_enable_rx_bias(codec, 1);
-			taiko_codec_enable_clock_block(codec, 1);
-			taiko_codec_calibrate_hs_polling(codec);
-			taiko_codec_start_hs_polling(codec);
-			snd_soc_update_bits(codec, TAIKO_A_CLK_BUFF_EN1,
-					0x05, 0x01);
-		} else {
-			taiko_codec_disable_clock_block(codec);
-			taiko_codec_enable_bandgap(codec,
-						   TAIKO_BANDGAP_OFF);
-		}
+		/* Put clock and BG */
+		wcd9xxx_resmgr_put_clk_block(&taiko->resmgr, WCD9XXX_CLK_MCLK);
+		wcd9xxx_resmgr_put_bandgap(&taiko->resmgr,
+					   WCD9XXX_BANDGAP_AUDIO_MODE);
 	}
-	if (dapm)
-		TAIKO_RELEASE_LOCK(taiko->codec_resource_lock);
+	WCD9XXX_BCL_UNLOCK(&taiko->resmgr);
+
 	return 0;
 }
 
@@ -5064,2201 +4154,6 @@
 
 };
 
-static short taiko_codec_read_sta_result(struct snd_soc_codec *codec)
-{
-	u8 bias_msb, bias_lsb;
-	short bias_value;
-
-	bias_msb = snd_soc_read(codec, TAIKO_A_CDC_MBHC_B3_STATUS);
-	bias_lsb = snd_soc_read(codec, TAIKO_A_CDC_MBHC_B2_STATUS);
-	bias_value = (bias_msb << 8) | bias_lsb;
-	return bias_value;
-}
-
-static short taiko_codec_read_dce_result(struct snd_soc_codec *codec)
-{
-	u8 bias_msb, bias_lsb;
-	short bias_value;
-
-	bias_msb = snd_soc_read(codec, TAIKO_A_CDC_MBHC_B5_STATUS);
-	bias_lsb = snd_soc_read(codec, TAIKO_A_CDC_MBHC_B4_STATUS);
-	bias_value = (bias_msb << 8) | bias_lsb;
-	return bias_value;
-}
-
-static void taiko_turn_onoff_rel_detection(struct snd_soc_codec *codec, bool on)
-{
-	snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_B1_CTL, 0x02, on << 1);
-}
-
-static short __taiko_codec_sta_dce(struct snd_soc_codec *codec, int dce,
-				   bool override_bypass, bool noreldetection)
-{
-	short bias_value;
-	struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
-
-	wcd9xxx_disable_irq(codec->control_data, WCD9XXX_IRQ_MBHC_POTENTIAL);
-	if (noreldetection)
-		taiko_turn_onoff_rel_detection(codec, false);
-
-	/* Turn on the override */
-	if (!override_bypass)
-		snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_B1_CTL, 0x4, 0x4);
-	if (dce) {
-		snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_CLK_CTL, 0x8, 0x8);
-		snd_soc_write(codec, TAIKO_A_CDC_MBHC_EN_CTL, 0x4);
-		snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_CLK_CTL, 0x8, 0x0);
-		usleep_range(taiko->mbhc_data.t_sta_dce,
-			     taiko->mbhc_data.t_sta_dce);
-		snd_soc_write(codec, TAIKO_A_CDC_MBHC_EN_CTL, 0x4);
-		usleep_range(taiko->mbhc_data.t_dce,
-			     taiko->mbhc_data.t_dce);
-		bias_value = taiko_codec_read_dce_result(codec);
-	} else {
-		snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_CLK_CTL, 0x8, 0x8);
-		snd_soc_write(codec, TAIKO_A_CDC_MBHC_EN_CTL, 0x2);
-		snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_CLK_CTL, 0x8, 0x0);
-		usleep_range(taiko->mbhc_data.t_sta_dce,
-			     taiko->mbhc_data.t_sta_dce);
-		snd_soc_write(codec, TAIKO_A_CDC_MBHC_EN_CTL, 0x2);
-		usleep_range(taiko->mbhc_data.t_sta,
-			     taiko->mbhc_data.t_sta);
-		bias_value = taiko_codec_read_sta_result(codec);
-		snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_CLK_CTL, 0x8, 0x8);
-		snd_soc_write(codec, TAIKO_A_CDC_MBHC_EN_CTL, 0x0);
-	}
-	/* Turn off the override after measuring mic voltage */
-	if (!override_bypass)
-		snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_B1_CTL, 0x04, 0x00);
-
-	if (noreldetection)
-		taiko_turn_onoff_rel_detection(codec, true);
-	wcd9xxx_enable_irq(codec->control_data, WCD9XXX_IRQ_MBHC_POTENTIAL);
-
-	return bias_value;
-}
-
-static short taiko_codec_sta_dce(struct snd_soc_codec *codec, int dce,
-				 bool norel)
-{
-	return __taiko_codec_sta_dce(codec, dce, false, norel);
-}
-
-/* called only from interrupt which is under codec_resource_lock acquisition */
-static short taiko_codec_setup_hs_polling(struct snd_soc_codec *codec)
-{
-	struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
-	short bias_value;
-	u8 cfilt_mode;
-
-	pr_debug("%s: enter, mclk_enabled %d\n", __func__, taiko->mclk_enabled);
-	if (!taiko->mbhc_cfg.calibration) {
-		pr_err("Error, no taiko calibration\n");
-		return -ENODEV;
-	}
-
-	if (!taiko->mclk_enabled) {
-		taiko_codec_disable_clock_block(codec);
-		taiko_codec_enable_bandgap(codec, TAIKO_BANDGAP_MBHC_MODE);
-		taiko_enable_rx_bias(codec, 1);
-		taiko_codec_enable_clock_block(codec, 1);
-	}
-
-	snd_soc_update_bits(codec, TAIKO_A_CLK_BUFF_EN1, 0x05, 0x01);
-
-	/* Make sure CFILT is in fast mode, save current mode */
-	cfilt_mode = snd_soc_read(codec, taiko->mbhc_bias_regs.cfilt_ctl);
-	snd_soc_update_bits(codec, taiko->mbhc_bias_regs.cfilt_ctl, 0x70, 0x00);
-
-	snd_soc_update_bits(codec, taiko->mbhc_bias_regs.ctl_reg, 0x1F, 0x16);
-
-	snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_CLK_CTL, 0x2, 0x2);
-	snd_soc_write(codec, TAIKO_A_MBHC_SCALING_MUX_1, 0x84);
-
-	snd_soc_update_bits(codec, TAIKO_A_TX_7_MBHC_EN, 0x80, 0x80);
-	snd_soc_update_bits(codec, TAIKO_A_TX_7_MBHC_EN, 0x1F, 0x1C);
-	snd_soc_update_bits(codec, TAIKO_A_TX_7_MBHC_TEST_CTL, 0x40, 0x40);
-
-	snd_soc_update_bits(codec, TAIKO_A_TX_7_MBHC_EN, 0x80, 0x00);
-	snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_CLK_CTL, 0x8, 0x8);
-	snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_CLK_CTL, 0x8, 0x00);
-
-	snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_B1_CTL, 0x2, 0x2);
-	snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_CLK_CTL, 0x8, 0x8);
-
-	taiko_codec_calibrate_hs_polling(codec);
-
-	/* don't flip override */
-	bias_value = __taiko_codec_sta_dce(codec, 1, true, true);
-	snd_soc_update_bits(codec, taiko->mbhc_bias_regs.cfilt_ctl, 0x40,
-			    cfilt_mode);
-	snd_soc_update_bits(codec, TAIKO_A_MBHC_HPH, 0x13, 0x00);
-
-	return bias_value;
-}
-
-static int taiko_cancel_btn_work(struct taiko_priv *taiko)
-{
-	int r = 0;
-	struct wcd9xxx *core = dev_get_drvdata(taiko->codec->dev->parent);
-
-	if (cancel_delayed_work_sync(&taiko->mbhc_btn_dwork)) {
-		/* if scheduled mbhc_btn_dwork is canceled from here,
-		* we have to unlock from here instead btn_work */
-		wcd9xxx_unlock_sleep(core);
-		r = 1;
-	}
-	return r;
-}
-
-/* called under codec_resource_lock acquisition */
-void taiko_set_and_turnoff_hph_padac(struct snd_soc_codec *codec)
-{
-	struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
-	u8 wg_time;
-
-	wg_time = snd_soc_read(codec, TAIKO_A_RX_HPH_CNP_WG_TIME) ;
-	wg_time += 1;
-
-	/* If headphone PA is on, check if userspace receives
-	 * removal event to sync-up PA's state */
-	if (taiko_is_hph_pa_on(codec)) {
-		pr_debug("%s PA is on, setting PA_OFF_ACK\n", __func__);
-		set_bit(TAIKO_HPHL_PA_OFF_ACK, &taiko->hph_pa_dac_state);
-		set_bit(TAIKO_HPHR_PA_OFF_ACK, &taiko->hph_pa_dac_state);
-	} else {
-		pr_debug("%s PA is off\n", __func__);
-	}
-
-	if (taiko_is_hph_dac_on(codec, 1))
-		set_bit(TAIKO_HPHL_DAC_OFF_ACK, &taiko->hph_pa_dac_state);
-	if (taiko_is_hph_dac_on(codec, 0))
-		set_bit(TAIKO_HPHR_DAC_OFF_ACK, &taiko->hph_pa_dac_state);
-
-	snd_soc_update_bits(codec, TAIKO_A_RX_HPH_CNP_EN, 0x30, 0x00);
-	snd_soc_update_bits(codec, TAIKO_A_RX_HPH_L_DAC_CTL,
-			    0xC0, 0x00);
-	snd_soc_update_bits(codec, TAIKO_A_RX_HPH_R_DAC_CTL,
-			    0xC0, 0x00);
-	usleep_range(wg_time * 1000, wg_time * 1000);
-}
-
-static void taiko_clr_and_turnon_hph_padac(struct taiko_priv *taiko)
-{
-	bool pa_turned_on = false;
-	struct snd_soc_codec *codec = taiko->codec;
-	u8 wg_time;
-
-	wg_time = snd_soc_read(codec, TAIKO_A_RX_HPH_CNP_WG_TIME) ;
-	wg_time += 1;
-
-	if (test_and_clear_bit(TAIKO_HPHR_DAC_OFF_ACK,
-			       &taiko->hph_pa_dac_state)) {
-		pr_debug("%s: HPHR clear flag and enable DAC\n", __func__);
-		snd_soc_update_bits(taiko->codec, TAIKO_A_RX_HPH_R_DAC_CTL,
-				    0xC0, 0xC0);
-	}
-	if (test_and_clear_bit(TAIKO_HPHL_DAC_OFF_ACK,
-			       &taiko->hph_pa_dac_state)) {
-		pr_debug("%s: HPHL clear flag and enable DAC\n", __func__);
-		snd_soc_update_bits(taiko->codec, TAIKO_A_RX_HPH_L_DAC_CTL,
-				    0xC0, 0xC0);
-	}
-
-	if (test_and_clear_bit(TAIKO_HPHR_PA_OFF_ACK,
-			       &taiko->hph_pa_dac_state)) {
-		pr_debug("%s: HPHR clear flag and enable PA\n", __func__);
-		snd_soc_update_bits(taiko->codec, TAIKO_A_RX_HPH_CNP_EN, 0x10,
-				    1 << 4);
-		pa_turned_on = true;
-	}
-	if (test_and_clear_bit(TAIKO_HPHL_PA_OFF_ACK,
-			       &taiko->hph_pa_dac_state)) {
-		pr_debug("%s: HPHL clear flag and enable PA\n", __func__);
-		snd_soc_update_bits(taiko->codec, TAIKO_A_RX_HPH_CNP_EN, 0x20,
-				    1 << 5);
-		pa_turned_on = true;
-	}
-
-	if (pa_turned_on) {
-		pr_debug("%s: PA was turned off by MBHC and not by DAPM\n",
-				__func__);
-		usleep_range(wg_time * 1000, wg_time * 1000);
-	}
-}
-
-/* called under codec_resource_lock acquisition */
-static void taiko_codec_report_plug(struct snd_soc_codec *codec, int insertion,
-				    enum snd_jack_types jack_type)
-{
-	struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
-
-	if (!insertion) {
-		/* Report removal */
-		taiko->hph_status &= ~jack_type;
-		if (taiko->mbhc_cfg.headset_jack) {
-			/* cancel possibly scheduled btn work and
-			* report release if we reported button press */
-			if (taiko_cancel_btn_work(taiko)) {
-				pr_debug("%s: button press is canceled\n",
-					__func__);
-			} else if (taiko->buttons_pressed) {
-				pr_debug("%s: release of button press%d\n",
-					  __func__, jack_type);
-				taiko_snd_soc_jack_report(taiko,
-						 taiko->mbhc_cfg.button_jack, 0,
-						 taiko->buttons_pressed);
-				taiko->buttons_pressed &=
-							~TAIKO_JACK_BUTTON_MASK;
-			}
-			pr_debug("%s: Reporting removal %d(%x)\n", __func__,
-				 jack_type, taiko->hph_status);
-			taiko_snd_soc_jack_report(taiko,
-						  taiko->mbhc_cfg.headset_jack,
-						  taiko->hph_status,
-						  TAIKO_JACK_MASK);
-		}
-		taiko_set_and_turnoff_hph_padac(codec);
-		hphocp_off_report(taiko, SND_JACK_OC_HPHR,
-				  WCD9XXX_IRQ_HPH_PA_OCPR_FAULT);
-		hphocp_off_report(taiko, SND_JACK_OC_HPHL,
-				  WCD9XXX_IRQ_HPH_PA_OCPL_FAULT);
-		taiko->current_plug = PLUG_TYPE_NONE;
-		taiko->mbhc_polling_active = false;
-	} else {
-		/* Report insertion */
-		taiko->hph_status |= jack_type;
-
-		if (jack_type == SND_JACK_HEADPHONE)
-			taiko->current_plug = PLUG_TYPE_HEADPHONE;
-		else if (jack_type == SND_JACK_UNSUPPORTED)
-			taiko->current_plug = PLUG_TYPE_GND_MIC_SWAP;
-		else if (jack_type == SND_JACK_HEADSET) {
-			taiko->mbhc_polling_active = true;
-			taiko->current_plug = PLUG_TYPE_HEADSET;
-		}
-		if (taiko->mbhc_cfg.headset_jack) {
-			pr_debug("%s: Reporting insertion %d(%x)\n", __func__,
-				 jack_type, taiko->hph_status);
-			taiko_snd_soc_jack_report(taiko,
-						  taiko->mbhc_cfg.headset_jack,
-						  taiko->hph_status,
-						  TAIKO_JACK_MASK);
-		}
-		taiko_clr_and_turnon_hph_padac(taiko);
-	}
-}
-
-static int taiko_codec_enable_hs_detect(struct snd_soc_codec *codec,
-					int insertion, int trigger,
-					bool padac_off)
-{
-	struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
-	int central_bias_enabled = 0;
-	const struct taiko_mbhc_general_cfg *generic =
-	    TAIKO_MBHC_CAL_GENERAL_PTR(taiko->mbhc_cfg.calibration);
-	const struct taiko_mbhc_plug_detect_cfg *plug_det =
-	    TAIKO_MBHC_CAL_PLUG_DET_PTR(taiko->mbhc_cfg.calibration);
-
-	if (!taiko->mbhc_cfg.calibration) {
-		pr_err("Error, no taiko calibration\n");
-		return -EINVAL;
-	}
-
-	snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_INT_CTL, 0x1, 0);
-
-	/* Make sure mic bias and Mic line schmitt trigger
-	 * are turned OFF
-	 */
-	snd_soc_update_bits(codec, taiko->mbhc_bias_regs.ctl_reg, 0x01, 0x01);
-	snd_soc_update_bits(codec, taiko->mbhc_bias_regs.mbhc_reg, 0x90, 0x00);
-
-	if (insertion) {
-		taiko_codec_switch_micbias(codec, 0);
-
-		/* DAPM can manipulate PA/DAC bits concurrently */
-		if (padac_off == true)
-			taiko_set_and_turnoff_hph_padac(codec);
-
-		if (trigger & MBHC_USE_HPHL_TRIGGER) {
-			/* Enable HPH Schmitt Trigger */
-			snd_soc_update_bits(codec, TAIKO_A_MBHC_HPH, 0x11,
-					    0x11);
-			snd_soc_update_bits(codec, TAIKO_A_MBHC_HPH, 0x0C,
-					    plug_det->hph_current << 2);
-			snd_soc_update_bits(codec, TAIKO_A_MBHC_HPH, 0x02,
-					    0x02);
-		}
-		if (trigger & MBHC_USE_MB_TRIGGER) {
-			/* enable the mic line schmitt trigger */
-			snd_soc_update_bits(codec,
-					    taiko->mbhc_bias_regs.mbhc_reg,
-					    0x60, plug_det->mic_current << 5);
-			snd_soc_update_bits(codec,
-					    taiko->mbhc_bias_regs.mbhc_reg,
-					    0x80, 0x80);
-			usleep_range(plug_det->t_mic_pid, plug_det->t_mic_pid);
-			snd_soc_update_bits(codec,
-					    taiko->mbhc_bias_regs.ctl_reg, 0x01,
-					    0x00);
-			snd_soc_update_bits(codec,
-					    taiko->mbhc_bias_regs.mbhc_reg,
-					    0x10, 0x10);
-		}
-
-		/* setup for insetion detection */
-		snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_INT_CTL, 0x2, 0);
-	} else {
-		pr_debug("setup for removal detection\n");
-		/* Make sure the HPH schmitt trigger is OFF */
-		snd_soc_update_bits(codec, TAIKO_A_MBHC_HPH, 0x12, 0x00);
-
-		/* enable the mic line schmitt trigger */
-		snd_soc_update_bits(codec, taiko->mbhc_bias_regs.ctl_reg,
-				    0x01, 0x00);
-		snd_soc_update_bits(codec, taiko->mbhc_bias_regs.mbhc_reg, 0x60,
-				    plug_det->mic_current << 5);
-		snd_soc_update_bits(codec, taiko->mbhc_bias_regs.mbhc_reg,
-			0x80, 0x80);
-		usleep_range(plug_det->t_mic_pid, plug_det->t_mic_pid);
-		snd_soc_update_bits(codec, taiko->mbhc_bias_regs.mbhc_reg,
-			0x10, 0x10);
-
-		/* Setup for low power removal detection */
-		snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_INT_CTL, 0x2, 0x2);
-	}
-
-	if (snd_soc_read(codec, TAIKO_A_CDC_MBHC_B1_CTL) & 0x4) {
-		/* called called by interrupt */
-		if (!(taiko->clock_active)) {
-			taiko_codec_enable_config_mode(codec, 1);
-			snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_B1_CTL,
-				0x06, 0);
-			usleep_range(generic->t_shutdown_plug_rem,
-				     generic->t_shutdown_plug_rem);
-			taiko_codec_enable_config_mode(codec, 0);
-		} else
-			snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_B1_CTL,
-				0x06, 0);
-	}
-
-	snd_soc_update_bits(codec, taiko->mbhc_bias_regs.int_rbias, 0x80, 0);
-
-	/* If central bandgap disabled */
-	if (!(snd_soc_read(codec, TAIKO_A_PIN_CTL_OE1) & 1)) {
-		snd_soc_update_bits(codec, TAIKO_A_PIN_CTL_OE1, 0x3, 0x3);
-		usleep_range(generic->t_bg_fast_settle,
-			     generic->t_bg_fast_settle);
-		central_bias_enabled = 1;
-	}
-
-	/* If LDO_H disabled */
-	if (snd_soc_read(codec, TAIKO_A_PIN_CTL_OE0) & 0x80) {
-		snd_soc_update_bits(codec, TAIKO_A_PIN_CTL_OE0, 0x10, 0);
-		snd_soc_update_bits(codec, TAIKO_A_PIN_CTL_OE0, 0x80, 0x80);
-		usleep_range(generic->t_ldoh, generic->t_ldoh);
-		snd_soc_update_bits(codec, TAIKO_A_PIN_CTL_OE0, 0x80, 0);
-
-		if (central_bias_enabled)
-			snd_soc_update_bits(codec, TAIKO_A_PIN_CTL_OE1, 0x1, 0);
-	}
-
-	snd_soc_update_bits(codec, taiko->reg_addr.micb_4_mbhc, 0x3,
-			    taiko->mbhc_cfg.micbias);
-
-	wcd9xxx_enable_irq(codec->control_data, WCD9XXX_IRQ_MBHC_INSERTION);
-	snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_INT_CTL, 0x1, 0x1);
-	return 0;
-}
-
-static u16 taiko_codec_v_sta_dce(struct snd_soc_codec *codec, bool dce,
-				 s16 vin_mv)
-{
-	struct taiko_priv *taiko;
-	s16 diff, zero;
-	u32 mb_mv, in;
-	u16 value;
-
-	taiko = snd_soc_codec_get_drvdata(codec);
-	mb_mv = taiko->mbhc_data.micb_mv;
-
-	if (mb_mv == 0) {
-		pr_err("%s: Mic Bias voltage is set to zero\n", __func__);
-		return -EINVAL;
-	}
-
-	if (dce) {
-		diff = (taiko->mbhc_data.dce_mb) - (taiko->mbhc_data.dce_z);
-		zero = (taiko->mbhc_data.dce_z);
-	} else {
-		diff = (taiko->mbhc_data.sta_mb) - (taiko->mbhc_data.sta_z);
-		zero = (taiko->mbhc_data.sta_z);
-	}
-	in = (u32) diff * vin_mv;
-
-	value = (u16) (in / mb_mv) + zero;
-	return value;
-}
-
-static s32 taiko_codec_sta_dce_v(struct snd_soc_codec *codec, s8 dce,
-				 u16 bias_value)
-{
-	struct taiko_priv *taiko;
-	s16 value, z, mb;
-	s32 mv;
-
-	taiko = snd_soc_codec_get_drvdata(codec);
-	value = bias_value;
-	if (dce) {
-		z = (taiko->mbhc_data.dce_z);
-		mb = (taiko->mbhc_data.dce_mb);
-		mv = (value - z) * (s32)taiko->mbhc_data.micb_mv / (mb - z);
-	} else {
-		z = (taiko->mbhc_data.sta_z);
-		mb = (taiko->mbhc_data.sta_mb);
-		mv = (value - z) * (s32)taiko->mbhc_data.micb_mv / (mb - z);
-	}
-
-	return mv;
-}
-
-static void btn_lpress_fn(struct work_struct *work)
-{
-	struct delayed_work *delayed_work;
-	struct taiko_priv *taiko;
-	short bias_value;
-	int dce_mv, sta_mv;
-	struct wcd9xxx *core;
-
-	pr_debug("%s:\n", __func__);
-
-	delayed_work = to_delayed_work(work);
-	taiko = container_of(delayed_work, struct taiko_priv, mbhc_btn_dwork);
-	core = dev_get_drvdata(taiko->codec->dev->parent);
-
-	if (taiko) {
-		if (taiko->mbhc_cfg.button_jack) {
-			bias_value = taiko_codec_read_sta_result(taiko->codec);
-			sta_mv = taiko_codec_sta_dce_v(taiko->codec, 0,
-						bias_value);
-			bias_value = taiko_codec_read_dce_result(taiko->codec);
-			dce_mv = taiko_codec_sta_dce_v(taiko->codec, 1,
-						bias_value);
-			pr_debug("%s: Reporting long button press event\n",
-				__func__);
-			pr_debug("%s: STA: %d, DCE: %d\n", __func__, sta_mv,
-					dce_mv);
-			taiko_snd_soc_jack_report(taiko,
-						  taiko->mbhc_cfg.button_jack,
-						  taiko->buttons_pressed,
-						  taiko->buttons_pressed);
-		}
-	} else {
-		pr_err("%s: Bad taiko private data\n", __func__);
-	}
-
-	pr_debug("%s: leave\n", __func__);
-	wcd9xxx_unlock_sleep(core);
-}
-
-void taiko_mbhc_cal(struct snd_soc_codec *codec)
-{
-	struct taiko_priv *taiko;
-	struct taiko_mbhc_btn_detect_cfg *btn_det;
-	u8 cfilt_mode, bg_mode;
-	u8 ncic, nmeas, navg;
-	u32 mclk_rate;
-	u32 dce_wait, sta_wait;
-	u8 *n_cic;
-	void *calibration;
-
-	taiko = snd_soc_codec_get_drvdata(codec);
-	calibration = taiko->mbhc_cfg.calibration;
-
-	wcd9xxx_disable_irq(codec->control_data, WCD9XXX_IRQ_MBHC_POTENTIAL);
-	taiko_turn_onoff_rel_detection(codec, false);
-
-	/* First compute the DCE / STA wait times
-	 * depending on tunable parameters.
-	 * The value is computed in microseconds
-	 */
-	btn_det = TAIKO_MBHC_CAL_BTN_DET_PTR(calibration);
-	n_cic = taiko_mbhc_cal_btn_det_mp(btn_det, TAIKO_BTN_DET_N_CIC);
-	ncic = n_cic[taiko_codec_mclk_index(taiko)];
-	nmeas = TAIKO_MBHC_CAL_BTN_DET_PTR(calibration)->n_meas;
-	navg = TAIKO_MBHC_CAL_GENERAL_PTR(calibration)->mbhc_navg;
-	mclk_rate = taiko->mbhc_cfg.mclk_rate;
-	dce_wait = (1000 * 512 * ncic * (nmeas + 1)) / (mclk_rate / 1000);
-	sta_wait = (1000 * 128 * (navg + 1)) / (mclk_rate / 1000);
-
-	taiko->mbhc_data.t_dce = dce_wait;
-	taiko->mbhc_data.t_sta = sta_wait;
-
-	/* LDOH and CFILT are already configured during pdata handling.
-	 * Only need to make sure CFILT and bandgap are in Fast mode.
-	 * Need to restore defaults once calculation is done.
-	 */
-	cfilt_mode = snd_soc_read(codec, taiko->mbhc_bias_regs.cfilt_ctl);
-	snd_soc_update_bits(codec, taiko->mbhc_bias_regs.cfilt_ctl, 0x40, 0x00);
-	bg_mode = snd_soc_update_bits(codec, TAIKO_A_BIAS_CENTRAL_BG_CTL, 0x02,
-				      0x02);
-
-	/* Micbias, CFILT, LDOH, MBHC MUX mode settings
-	 * to perform ADC calibration
-	 */
-	snd_soc_update_bits(codec, taiko->mbhc_bias_regs.ctl_reg, 0x60,
-			    taiko->mbhc_cfg.micbias << 5);
-	snd_soc_update_bits(codec, taiko->mbhc_bias_regs.ctl_reg, 0x01, 0x00);
-	snd_soc_update_bits(codec, TAIKO_A_LDO_H_MODE_1, 0x60, 0x60);
-	snd_soc_write(codec, TAIKO_A_TX_7_MBHC_TEST_CTL, 0x78);
-	snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_B1_CTL, 0x04, 0x04);
-
-	/* DCE measurement for 0 volts */
-	snd_soc_write(codec, TAIKO_A_CDC_MBHC_CLK_CTL, 0x0A);
-	snd_soc_write(codec, TAIKO_A_CDC_MBHC_EN_CTL, 0x04);
-	snd_soc_write(codec, TAIKO_A_CDC_MBHC_CLK_CTL, 0x02);
-	snd_soc_write(codec, TAIKO_A_MBHC_SCALING_MUX_1, 0x81);
-	usleep_range(100, 100);
-	snd_soc_write(codec, TAIKO_A_CDC_MBHC_EN_CTL, 0x04);
-	usleep_range(taiko->mbhc_data.t_dce, taiko->mbhc_data.t_dce);
-	taiko->mbhc_data.dce_z = taiko_codec_read_dce_result(codec);
-
-	/* DCE measurment for MB voltage */
-	snd_soc_write(codec, TAIKO_A_CDC_MBHC_CLK_CTL, 0x0A);
-	snd_soc_write(codec, TAIKO_A_CDC_MBHC_CLK_CTL, 0x02);
-	snd_soc_write(codec, TAIKO_A_MBHC_SCALING_MUX_1, 0x82);
-	usleep_range(100, 100);
-	snd_soc_write(codec, TAIKO_A_CDC_MBHC_EN_CTL, 0x04);
-	usleep_range(taiko->mbhc_data.t_dce, taiko->mbhc_data.t_dce);
-	taiko->mbhc_data.dce_mb = taiko_codec_read_dce_result(codec);
-
-	/* Sta measuremnt for 0 volts */
-	snd_soc_write(codec, TAIKO_A_CDC_MBHC_CLK_CTL, 0x0A);
-	snd_soc_write(codec, TAIKO_A_CDC_MBHC_EN_CTL, 0x02);
-	snd_soc_write(codec, TAIKO_A_CDC_MBHC_CLK_CTL, 0x02);
-	snd_soc_write(codec, TAIKO_A_MBHC_SCALING_MUX_1, 0x81);
-	usleep_range(100, 100);
-	snd_soc_write(codec, TAIKO_A_CDC_MBHC_EN_CTL, 0x02);
-	usleep_range(taiko->mbhc_data.t_sta, taiko->mbhc_data.t_sta);
-	taiko->mbhc_data.sta_z = taiko_codec_read_sta_result(codec);
-
-	/* STA Measurement for MB Voltage */
-	snd_soc_write(codec, TAIKO_A_MBHC_SCALING_MUX_1, 0x82);
-	usleep_range(100, 100);
-	snd_soc_write(codec, TAIKO_A_CDC_MBHC_EN_CTL, 0x02);
-	usleep_range(taiko->mbhc_data.t_sta, taiko->mbhc_data.t_sta);
-	taiko->mbhc_data.sta_mb = taiko_codec_read_sta_result(codec);
-
-	/* Restore default settings. */
-	snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_B1_CTL, 0x04, 0x00);
-	snd_soc_update_bits(codec, taiko->mbhc_bias_regs.cfilt_ctl, 0x40,
-			    cfilt_mode);
-	snd_soc_update_bits(codec, TAIKO_A_BIAS_CENTRAL_BG_CTL, 0x02, bg_mode);
-
-	snd_soc_write(codec, TAIKO_A_MBHC_SCALING_MUX_1, 0x84);
-	usleep_range(100, 100);
-
-	wcd9xxx_enable_irq(codec->control_data, WCD9XXX_IRQ_MBHC_POTENTIAL);
-	taiko_turn_onoff_rel_detection(codec, true);
-}
-
-void *taiko_mbhc_cal_btn_det_mp(const struct taiko_mbhc_btn_detect_cfg *btn_det,
-				const enum taiko_mbhc_btn_det_mem mem)
-{
-	void *ret = &btn_det->_v_btn_low;
-
-	switch (mem) {
-	case TAIKO_BTN_DET_GAIN:
-		ret += sizeof(btn_det->_n_cic);
-	case TAIKO_BTN_DET_N_CIC:
-		ret += sizeof(btn_det->_n_ready);
-	case TAIKO_BTN_DET_N_READY:
-		ret += sizeof(btn_det->_v_btn_high[0]) * btn_det->num_btn;
-	case TAIKO_BTN_DET_V_BTN_HIGH:
-		ret += sizeof(btn_det->_v_btn_low[0]) * btn_det->num_btn;
-	case TAIKO_BTN_DET_V_BTN_LOW:
-		/* do nothing */
-		break;
-	default:
-		ret = NULL;
-	}
-
-	return ret;
-}
-
-static s16 taiko_scale_v_micb_vddio(struct taiko_priv *taiko, int v,
-				    bool tovddio)
-{
-	int r;
-	int vddio_k, mb_k;
-	vddio_k = taiko_find_k_value(taiko->pdata->micbias.ldoh_v,
-				     VDDIO_MICBIAS_MV);
-	mb_k = taiko_find_k_value(taiko->pdata->micbias.ldoh_v,
-				  taiko->mbhc_data.micb_mv);
-	if (tovddio)
-		r = v * vddio_k / mb_k;
-	else
-		r = v * mb_k / vddio_k;
-	return r;
-}
-
-static void taiko_mbhc_calc_thres(struct snd_soc_codec *codec)
-{
-	struct taiko_priv *taiko;
-	s16 btn_mv = 0, btn_delta_mv;
-	struct taiko_mbhc_btn_detect_cfg *btn_det;
-	struct taiko_mbhc_plug_type_cfg *plug_type;
-	u16 *btn_high;
-	u8 *n_ready;
-	int i;
-
-	taiko = snd_soc_codec_get_drvdata(codec);
-	btn_det = TAIKO_MBHC_CAL_BTN_DET_PTR(taiko->mbhc_cfg.calibration);
-	plug_type = TAIKO_MBHC_CAL_PLUG_TYPE_PTR(taiko->mbhc_cfg.calibration);
-
-	n_ready = taiko_mbhc_cal_btn_det_mp(btn_det, TAIKO_BTN_DET_N_READY);
-	if (taiko->mbhc_cfg.mclk_rate == TAIKO_MCLK_RATE_12288KHZ) {
-		taiko->mbhc_data.npoll = 4;
-		taiko->mbhc_data.nbounce_wait = 30;
-	} else if (taiko->mbhc_cfg.mclk_rate == TAIKO_MCLK_RATE_9600KHZ) {
-		taiko->mbhc_data.npoll = 7;
-		taiko->mbhc_data.nbounce_wait = 23;
-	}
-
-	taiko->mbhc_data.t_sta_dce = ((1000 * 256) /
-				      (taiko->mbhc_cfg.mclk_rate / 1000) *
-				      n_ready[taiko_codec_mclk_index(taiko)]) +
-				     10;
-	taiko->mbhc_data.v_ins_hu =
-	    taiko_codec_v_sta_dce(codec, STA, plug_type->v_hs_max);
-	taiko->mbhc_data.v_ins_h =
-	    taiko_codec_v_sta_dce(codec, DCE, plug_type->v_hs_max);
-
-	taiko->mbhc_data.v_inval_ins_low = TAIKO_MBHC_FAKE_INSERT_LOW;
-	if (taiko->mbhc_cfg.gpio)
-		taiko->mbhc_data.v_inval_ins_high =
-		    TAIKO_MBHC_FAKE_INSERT_HIGH;
-	else
-		taiko->mbhc_data.v_inval_ins_high =
-		    TAIKO_MBHC_FAKE_INS_HIGH_NO_GPIO;
-
-	if (taiko->mbhc_data.micb_mv != VDDIO_MICBIAS_MV) {
-		taiko->mbhc_data.adj_v_hs_max =
-		    taiko_scale_v_micb_vddio(taiko, plug_type->v_hs_max, true);
-		taiko->mbhc_data.adj_v_ins_hu =
-		    taiko_codec_v_sta_dce(codec, STA,
-					  taiko->mbhc_data.adj_v_hs_max);
-		taiko->mbhc_data.adj_v_ins_h =
-		    taiko_codec_v_sta_dce(codec, DCE,
-					  taiko->mbhc_data.adj_v_hs_max);
-		taiko->mbhc_data.v_inval_ins_low =
-		    taiko_scale_v_micb_vddio(taiko,
-					     taiko->mbhc_data.v_inval_ins_low,
-					     false);
-		taiko->mbhc_data.v_inval_ins_high =
-		    taiko_scale_v_micb_vddio(taiko,
-					     taiko->mbhc_data.v_inval_ins_high,
-					     false);
-	}
-
-	btn_high = taiko_mbhc_cal_btn_det_mp(btn_det, TAIKO_BTN_DET_V_BTN_HIGH);
-	for (i = 0; i < btn_det->num_btn; i++)
-		btn_mv = btn_high[i] > btn_mv ? btn_high[i] : btn_mv;
-
-	taiko->mbhc_data.v_b1_h = taiko_codec_v_sta_dce(codec, DCE, btn_mv);
-	btn_delta_mv = btn_mv + btn_det->v_btn_press_delta_sta;
-	taiko->mbhc_data.v_b1_hu =
-	    taiko_codec_v_sta_dce(codec, STA, btn_delta_mv);
-
-	btn_delta_mv = btn_mv + btn_det->v_btn_press_delta_cic;
-
-	taiko->mbhc_data.v_b1_huc =
-	    taiko_codec_v_sta_dce(codec, DCE, btn_delta_mv);
-
-	taiko->mbhc_data.v_brh = taiko->mbhc_data.v_b1_h;
-	taiko->mbhc_data.v_brl = TAIKO_MBHC_BUTTON_MIN;
-
-	taiko->mbhc_data.v_no_mic =
-	    taiko_codec_v_sta_dce(codec, STA, plug_type->v_no_mic);
-}
-
-void taiko_mbhc_init(struct snd_soc_codec *codec)
-{
-	struct taiko_priv *taiko;
-	struct taiko_mbhc_general_cfg *generic;
-	struct taiko_mbhc_btn_detect_cfg *btn_det;
-	int n;
-	u8 *n_cic, *gain;
-
-	taiko = snd_soc_codec_get_drvdata(codec);
-	generic = TAIKO_MBHC_CAL_GENERAL_PTR(taiko->mbhc_cfg.calibration);
-	btn_det = TAIKO_MBHC_CAL_BTN_DET_PTR(taiko->mbhc_cfg.calibration);
-
-	for (n = 0; n < 8; n++) {
-			snd_soc_update_bits(codec,
-					    TAIKO_A_CDC_MBHC_FIR_B1_CFG,
-					    0x07, n);
-			snd_soc_write(codec, TAIKO_A_CDC_MBHC_FIR_B2_CFG,
-				      btn_det->c[n]);
-	}
-
-	snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_B2_CTL, 0x07,
-			    btn_det->nc);
-
-	n_cic = taiko_mbhc_cal_btn_det_mp(btn_det, TAIKO_BTN_DET_N_CIC);
-	snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_TIMER_B6_CTL, 0xFF,
-			    n_cic[taiko_codec_mclk_index(taiko)]);
-
-	gain = taiko_mbhc_cal_btn_det_mp(btn_det, TAIKO_BTN_DET_GAIN);
-	snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_B2_CTL, 0x78,
-			    gain[taiko_codec_mclk_index(taiko)] << 3);
-
-	snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_TIMER_B4_CTL, 0x70,
-			    generic->mbhc_nsa << 4);
-
-	snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_TIMER_B4_CTL, 0x0F,
-			    btn_det->n_meas);
-
-	snd_soc_write(codec, TAIKO_A_CDC_MBHC_TIMER_B5_CTL, generic->mbhc_navg);
-
-	snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_B1_CTL, 0x80, 0x80);
-
-	snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_B1_CTL, 0x78,
-			    btn_det->mbhc_nsc << 3);
-
-	snd_soc_update_bits(codec, taiko->reg_addr.micb_4_mbhc, 0x03,
-			    TAIKO_MICBIAS2);
-
-	snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_B1_CTL, 0x02, 0x02);
-
-	snd_soc_update_bits(codec, TAIKO_A_MBHC_SCALING_MUX_2, 0xF0, 0xF0);
-}
-
-static bool taiko_mbhc_fw_validate(const struct firmware *fw)
-{
-	u32 cfg_offset;
-	struct taiko_mbhc_imped_detect_cfg *imped_cfg;
-	struct taiko_mbhc_btn_detect_cfg *btn_cfg;
-
-	if (fw->size < TAIKO_MBHC_CAL_MIN_SIZE)
-		return false;
-
-	/* previous check guarantees that there is enough fw data up
-	 * to num_btn
-	 */
-	btn_cfg = TAIKO_MBHC_CAL_BTN_DET_PTR(fw->data);
-	cfg_offset = (u32) ((void *) btn_cfg - (void *) fw->data);
-	if (fw->size < (cfg_offset + TAIKO_MBHC_CAL_BTN_SZ(btn_cfg)))
-		return false;
-
-	/* previous check guarantees that there is enough fw data up
-	 * to start of impedance detection configuration
-	 */
-	imped_cfg = TAIKO_MBHC_CAL_IMPED_DET_PTR(fw->data);
-	cfg_offset = (u32) ((void *) imped_cfg - (void *) fw->data);
-
-	if (fw->size < (cfg_offset + TAIKO_MBHC_CAL_IMPED_MIN_SZ))
-		return false;
-
-	if (fw->size < (cfg_offset + TAIKO_MBHC_CAL_IMPED_SZ(imped_cfg)))
-		return false;
-
-	return true;
-}
-
-/* called under codec_resource_lock acquisition */
-static int taiko_determine_button(const struct taiko_priv *priv,
-				  const s32 micmv)
-{
-	s16 *v_btn_low, *v_btn_high;
-	struct taiko_mbhc_btn_detect_cfg *btn_det;
-	int i, btn = -1;
-
-	btn_det = TAIKO_MBHC_CAL_BTN_DET_PTR(priv->mbhc_cfg.calibration);
-	v_btn_low = taiko_mbhc_cal_btn_det_mp(btn_det, TAIKO_BTN_DET_V_BTN_LOW);
-	v_btn_high = taiko_mbhc_cal_btn_det_mp(btn_det,
-				TAIKO_BTN_DET_V_BTN_HIGH);
-
-	for (i = 0; i < btn_det->num_btn; i++) {
-		if ((v_btn_low[i] <= micmv) && (v_btn_high[i] >= micmv)) {
-			btn = i;
-			break;
-		}
-	}
-
-	if (btn == -1)
-		pr_debug("%s: couldn't find button number for mic mv %d\n",
-			 __func__, micmv);
-
-	return btn;
-}
-
-static int taiko_get_button_mask(const int btn)
-{
-	int mask = 0;
-	switch (btn) {
-	case 0:
-		mask = SND_JACK_BTN_0;
-		break;
-	case 1:
-		mask = SND_JACK_BTN_1;
-		break;
-	case 2:
-		mask = SND_JACK_BTN_2;
-		break;
-	case 3:
-		mask = SND_JACK_BTN_3;
-		break;
-	case 4:
-		mask = SND_JACK_BTN_4;
-		break;
-	case 5:
-		mask = SND_JACK_BTN_5;
-		break;
-	case 6:
-		mask = SND_JACK_BTN_6;
-		break;
-	case 7:
-		mask = SND_JACK_BTN_7;
-		break;
-	}
-	return mask;
-}
-
-static irqreturn_t taiko_dce_handler(int irq, void *data)
-{
-	int i, mask;
-	short dce, sta;
-	s32 mv, mv_s, stamv_s;
-	bool vddio;
-	int btn = -1, meas = 0;
-	struct taiko_priv *priv = data;
-	const struct taiko_mbhc_btn_detect_cfg *d =
-	    TAIKO_MBHC_CAL_BTN_DET_PTR(priv->mbhc_cfg.calibration);
-	short btnmeas[d->n_btn_meas + 1];
-	struct snd_soc_codec *codec = priv->codec;
-	struct wcd9xxx *core = dev_get_drvdata(priv->codec->dev->parent);
-	int n_btn_meas = d->n_btn_meas;
-	u8 mbhc_status = snd_soc_read(codec, TAIKO_A_CDC_MBHC_B1_STATUS) & 0x3E;
-
-	pr_debug("%s: enter\n", __func__);
-
-	TAIKO_ACQUIRE_LOCK(priv->codec_resource_lock);
-	if (priv->mbhc_state == MBHC_STATE_POTENTIAL_RECOVERY) {
-		pr_debug("%s: mbhc is being recovered, skip button press\n",
-			 __func__);
-		goto done;
-	}
-
-	priv->mbhc_state = MBHC_STATE_POTENTIAL;
-
-	if (!priv->mbhc_polling_active) {
-		pr_warn("%s: mbhc polling is not active, skip button press\n",
-			__func__);
-		goto done;
-	}
-
-	dce = taiko_codec_read_dce_result(codec);
-	mv = taiko_codec_sta_dce_v(codec, 1, dce);
-
-	/* If GPIO interrupt already kicked in, ignore button press */
-	if (priv->in_gpio_handler) {
-		pr_debug("%s: GPIO State Changed, ignore button press\n",
-			 __func__);
-		btn = -1;
-		goto done;
-	}
-
-	vddio = (priv->mbhc_data.micb_mv != VDDIO_MICBIAS_MV &&
-		 priv->mbhc_micbias_switched);
-	mv_s = vddio ? taiko_scale_v_micb_vddio(priv, mv, false) : mv;
-
-	if (mbhc_status != TAIKO_MBHC_STATUS_REL_DETECTION) {
-		if (priv->mbhc_last_resume &&
-		    !time_after(jiffies, priv->mbhc_last_resume + HZ)) {
-			pr_debug("%s: Button is already released shortly after resume\n",
-				__func__);
-			n_btn_meas = 0;
-		} else {
-			pr_debug("%s: Button is already released without resume",
-				__func__);
-			sta = taiko_codec_read_sta_result(codec);
-			stamv_s = taiko_codec_sta_dce_v(codec, 0, sta);
-			if (vddio)
-				stamv_s = taiko_scale_v_micb_vddio(priv,
-								   stamv_s,
-								   false);
-			btn = taiko_determine_button(priv, mv_s);
-			if (btn != taiko_determine_button(priv, stamv_s))
-				btn = -1;
-			goto done;
-		}
-	}
-
-	/* determine pressed button */
-	btnmeas[meas++] = taiko_determine_button(priv, mv_s);
-	pr_debug("%s: meas %d - DCE %d,%d,%d button %d\n", __func__,
-		 meas - 1, dce, mv, mv_s, btnmeas[meas - 1]);
-	if (n_btn_meas == 0)
-		btn = btnmeas[0];
-	for (; ((d->n_btn_meas) && (meas < (d->n_btn_meas + 1))); meas++) {
-		dce = taiko_codec_sta_dce(codec, 1, false);
-		mv = taiko_codec_sta_dce_v(codec, 1, dce);
-		mv_s = vddio ? taiko_scale_v_micb_vddio(priv, mv, false) : mv;
-
-		btnmeas[meas] = taiko_determine_button(priv, mv_s);
-		pr_debug("%s: meas %d - DCE %d,%d,%d button %d\n",
-			 __func__, meas, dce, mv, mv_s, btnmeas[meas]);
-		/* if large enough measurements are collected,
-		 * start to check if last all n_btn_con measurements were
-		 * in same button low/high range */
-		if (meas + 1 >= d->n_btn_con) {
-			for (i = 0; i < d->n_btn_con; i++)
-				if ((btnmeas[meas] < 0) ||
-				    (btnmeas[meas] != btnmeas[meas - i]))
-					break;
-			if (i == d->n_btn_con) {
-				/* button pressed */
-				btn = btnmeas[meas];
-				break;
-			} else if ((n_btn_meas - meas) < (d->n_btn_con - 1)) {
-				/* if left measurements are less than n_btn_con,
-				 * it's impossible to find button number */
-				break;
-			}
-		}
-	}
-
-	if (btn >= 0) {
-		if (priv->in_gpio_handler) {
-			pr_debug(
-			"%s: GPIO already triggered, ignore button press\n",
-			__func__);
-			goto done;
-		}
-		mask = taiko_get_button_mask(btn);
-		priv->buttons_pressed |= mask;
-		wcd9xxx_lock_sleep(core);
-		if (schedule_delayed_work(&priv->mbhc_btn_dwork,
-					  msecs_to_jiffies(400)) == 0) {
-			WARN(1, "Button pressed twice without release event\n");
-			wcd9xxx_unlock_sleep(core);
-		}
-	} else {
-		pr_debug("%s: bogus button press, too short press?\n",
-			 __func__);
-	}
-
- done:
-	pr_debug("%s: leave\n", __func__);
-	TAIKO_RELEASE_LOCK(priv->codec_resource_lock);
-	return IRQ_HANDLED;
-}
-
-static int taiko_is_fake_press(struct taiko_priv *priv)
-{
-	int i;
-	int r = 0;
-	struct snd_soc_codec *codec = priv->codec;
-	const int dces = MBHC_NUM_DCE_PLUG_DETECT;
-	s16 mb_v, v_ins_hu, v_ins_h;
-
-	v_ins_hu = taiko_get_current_v_ins(priv, true);
-	v_ins_h = taiko_get_current_v_ins(priv, false);
-
-	for (i = 0; i < dces; i++) {
-		usleep_range(10000, 10000);
-		if (i == 0) {
-			mb_v = taiko_codec_sta_dce(codec, 0, true);
-			pr_debug("%s: STA[0]: %d,%d\n", __func__, mb_v,
-				 taiko_codec_sta_dce_v(codec, 0, mb_v));
-			if (mb_v < (s16)priv->mbhc_data.v_b1_hu ||
-			    mb_v > v_ins_hu) {
-				r = 1;
-				break;
-			}
-		} else {
-			mb_v = taiko_codec_sta_dce(codec, 1, true);
-			pr_debug("%s: DCE[%d]: %d,%d\n", __func__, i, mb_v,
-				 taiko_codec_sta_dce_v(codec, 1, mb_v));
-			if (mb_v < (s16)priv->mbhc_data.v_b1_h ||
-			    mb_v > v_ins_h) {
-				r = 1;
-				break;
-			}
-		}
-	}
-
-	return r;
-}
-
-static irqreturn_t taiko_release_handler(int irq, void *data)
-{
-	int ret;
-	struct taiko_priv *priv = data;
-	struct snd_soc_codec *codec = priv->codec;
-
-	pr_debug("%s: enter\n", __func__);
-
-	TAIKO_ACQUIRE_LOCK(priv->codec_resource_lock);
-	priv->mbhc_state = MBHC_STATE_RELEASE;
-
-	taiko_codec_drive_v_to_micbias(codec, 10000);
-
-	if (priv->buttons_pressed & TAIKO_JACK_BUTTON_MASK) {
-		ret = taiko_cancel_btn_work(priv);
-		if (ret == 0) {
-			pr_debug("%s: Reporting long button release event\n",
-				 __func__);
-			if (priv->mbhc_cfg.button_jack)
-				taiko_snd_soc_jack_report(priv,
-						  priv->mbhc_cfg.button_jack, 0,
-						  priv->buttons_pressed);
-		} else {
-			if (taiko_is_fake_press(priv)) {
-				pr_debug("%s: Fake button press interrupt\n",
-					 __func__);
-			} else if (priv->mbhc_cfg.button_jack) {
-				if (priv->in_gpio_handler) {
-					pr_debug("%s: GPIO kicked in, ignore\n",
-						 __func__);
-				} else {
-					pr_debug(
-					"%s: Reporting short button press and release\n",
-					 __func__);
-					taiko_snd_soc_jack_report(priv,
-						     priv->mbhc_cfg.button_jack,
-						     priv->buttons_pressed,
-						     priv->buttons_pressed);
-					taiko_snd_soc_jack_report(priv,
-						  priv->mbhc_cfg.button_jack, 0,
-						  priv->buttons_pressed);
-				}
-			}
-		}
-
-		priv->buttons_pressed &= ~TAIKO_JACK_BUTTON_MASK;
-	}
-
-	taiko_codec_calibrate_hs_polling(codec);
-
-	if (priv->mbhc_cfg.gpio)
-		msleep(TAIKO_MBHC_GPIO_REL_DEBOUNCE_TIME_MS);
-
-	taiko_codec_start_hs_polling(codec);
-
-	pr_debug("%s: leave\n", __func__);
-	TAIKO_RELEASE_LOCK(priv->codec_resource_lock);
-	return IRQ_HANDLED;
-}
-
-static void taiko_codec_shutdown_hs_removal_detect(struct snd_soc_codec *codec)
-{
-	struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
-	const struct taiko_mbhc_general_cfg *generic =
-	    TAIKO_MBHC_CAL_GENERAL_PTR(taiko->mbhc_cfg.calibration);
-
-	if (!taiko->mclk_enabled && !taiko->mbhc_polling_active)
-		taiko_codec_enable_config_mode(codec, 1);
-
-	snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_CLK_CTL, 0x2, 0x2);
-	snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_B1_CTL, 0x6, 0x0);
-
-	snd_soc_update_bits(codec, taiko->mbhc_bias_regs.mbhc_reg, 0x80, 0x00);
-
-	usleep_range(generic->t_shutdown_plug_rem,
-		     generic->t_shutdown_plug_rem);
-
-	snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_CLK_CTL, 0xA, 0x8);
-	if (!taiko->mclk_enabled && !taiko->mbhc_polling_active)
-		taiko_codec_enable_config_mode(codec, 0);
-
-	snd_soc_write(codec, TAIKO_A_MBHC_SCALING_MUX_1, 0x00);
-}
-
-static void taiko_codec_cleanup_hs_polling(struct snd_soc_codec *codec)
-{
-	struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
-
-	taiko_codec_shutdown_hs_removal_detect(codec);
-
-	if (!taiko->mclk_enabled) {
-		taiko_codec_disable_clock_block(codec);
-		taiko_codec_enable_bandgap(codec, TAIKO_BANDGAP_OFF);
-	}
-
-	taiko->mbhc_polling_active = false;
-	taiko->mbhc_state = MBHC_STATE_NONE;
-}
-
-static irqreturn_t taiko_hphl_ocp_irq(int irq, void *data)
-{
-	struct taiko_priv *taiko = data;
-	struct snd_soc_codec *codec;
-
-	pr_info("%s: received HPHL OCP irq\n", __func__);
-
-	if (taiko) {
-		codec = taiko->codec;
-		if (taiko->hphlocp_cnt++ < TAIKO_OCP_ATTEMPT) {
-			pr_info("%s: retry\n", __func__);
-			snd_soc_update_bits(codec, TAIKO_A_RX_HPH_OCP_CTL, 0x10,
-					    0x00);
-			snd_soc_update_bits(codec, TAIKO_A_RX_HPH_OCP_CTL, 0x10,
-					    0x10);
-		} else {
-			wcd9xxx_disable_irq(codec->control_data,
-					  WCD9XXX_IRQ_HPH_PA_OCPL_FAULT);
-			taiko->hphlocp_cnt = 0;
-			taiko->hph_status |= SND_JACK_OC_HPHL;
-			if (taiko->mbhc_cfg.headset_jack)
-				taiko_snd_soc_jack_report(taiko,
-						   taiko->mbhc_cfg.headset_jack,
-						   taiko->hph_status,
-						   TAIKO_JACK_MASK);
-		}
-	} else {
-		pr_err("%s: Bad taiko private data\n", __func__);
-	}
-
-	return IRQ_HANDLED;
-}
-
-static irqreturn_t taiko_hphr_ocp_irq(int irq, void *data)
-{
-	struct taiko_priv *taiko = data;
-	struct snd_soc_codec *codec;
-
-	pr_info("%s: received HPHR OCP irq\n", __func__);
-
-	if (taiko) {
-		codec = taiko->codec;
-		if (taiko->hphrocp_cnt++ < TAIKO_OCP_ATTEMPT) {
-			pr_info("%s: retry\n", __func__);
-			snd_soc_update_bits(codec, TAIKO_A_RX_HPH_OCP_CTL, 0x10,
-					    0x00);
-			snd_soc_update_bits(codec, TAIKO_A_RX_HPH_OCP_CTL, 0x10,
-					    0x10);
-		} else {
-			wcd9xxx_disable_irq(codec->control_data,
-					  WCD9XXX_IRQ_HPH_PA_OCPR_FAULT);
-			taiko->hphrocp_cnt = 0;
-			taiko->hph_status |= SND_JACK_OC_HPHR;
-			if (taiko->mbhc_cfg.headset_jack)
-				taiko_snd_soc_jack_report(taiko,
-						   taiko->mbhc_cfg.headset_jack,
-						   taiko->hph_status,
-						   TAIKO_JACK_MASK);
-		}
-	} else {
-		pr_err("%s: Bad taiko private data\n", __func__);
-	}
-
-	return IRQ_HANDLED;
-}
-
-static bool taiko_is_inval_ins_range(struct snd_soc_codec *codec,
-				     s32 mic_volt, bool highhph, bool *highv)
-{
-	struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
-	bool invalid = false;
-	s16 v_hs_max;
-
-	/* Perform this check only when the high voltage headphone
-	 * needs to be considered as invalid
-	 */
-	v_hs_max = taiko_get_current_v_hs_max(taiko);
-	*highv = mic_volt > v_hs_max;
-	if (!highhph && *highv)
-		invalid = true;
-	else if (mic_volt < taiko->mbhc_data.v_inval_ins_high &&
-		 (mic_volt > taiko->mbhc_data.v_inval_ins_low))
-		invalid = true;
-
-	return invalid;
-}
-
-static bool taiko_is_inval_ins_delta(struct snd_soc_codec *codec,
-				     int mic_volt, int mic_volt_prev,
-				     int threshold)
-{
-	return abs(mic_volt - mic_volt_prev) > threshold;
-}
-
-/* called under codec_resource_lock acquisition */
-void taiko_find_plug_and_report(struct snd_soc_codec *codec,
-				enum taiko_mbhc_plug_type plug_type)
-{
-	struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
-
-	if (plug_type == PLUG_TYPE_HEADPHONE &&
-	    taiko->current_plug == PLUG_TYPE_NONE) {
-		/* Nothing was reported previously
-		 * report a headphone or unsupported
-		 */
-		taiko_codec_report_plug(codec, 1, SND_JACK_HEADPHONE);
-		taiko_codec_cleanup_hs_polling(codec);
-	} else if (plug_type == PLUG_TYPE_GND_MIC_SWAP) {
-		if (taiko->current_plug == PLUG_TYPE_HEADSET)
-			taiko_codec_report_plug(codec, 0, SND_JACK_HEADSET);
-		else if (taiko->current_plug == PLUG_TYPE_HEADPHONE)
-			taiko_codec_report_plug(codec, 0, SND_JACK_HEADPHONE);
-
-		taiko_codec_report_plug(codec, 1, SND_JACK_UNSUPPORTED);
-		taiko_codec_cleanup_hs_polling(codec);
-	} else if (plug_type == PLUG_TYPE_HEADSET) {
-		/* If Headphone was reported previously, this will
-		 * only report the mic line
-		 */
-		taiko_codec_report_plug(codec, 1, SND_JACK_HEADSET);
-		msleep(100);
-		taiko_codec_start_hs_polling(codec);
-	} else if (plug_type == PLUG_TYPE_HIGH_HPH) {
-		if (taiko->current_plug == PLUG_TYPE_NONE)
-			taiko_codec_report_plug(codec, 1, SND_JACK_HEADPHONE);
-		taiko_codec_cleanup_hs_polling(codec);
-		pr_debug("setup mic trigger for further detection\n");
-		taiko->lpi_enabled = true;
-		taiko_codec_enable_hs_detect(codec, 1,
-					     MBHC_USE_MB_TRIGGER |
-					     MBHC_USE_HPHL_TRIGGER,
-					     false);
-	} else {
-		WARN(1, "Unexpected current plug_type %d, plug_type %d\n",
-		     taiko->current_plug, plug_type);
-	}
-}
-
-/* should be called under interrupt context that hold suspend */
-static void taiko_schedule_hs_detect_plug(struct taiko_priv *taiko)
-{
-	pr_debug("%s: scheduling taiko_hs_correct_gpio_plug\n", __func__);
-	taiko->hs_detect_work_stop = false;
-	wcd9xxx_lock_sleep(taiko->codec->control_data);
-	schedule_work(&taiko->hs_correct_plug_work);
-}
-
-/* called under codec_resource_lock acquisition */
-static void taiko_cancel_hs_detect_plug(struct taiko_priv *taiko)
-{
-	pr_debug("%s: canceling hs_correct_plug_work\n", __func__);
-	taiko->hs_detect_work_stop = true;
-	wmb();
-	TAIKO_RELEASE_LOCK(taiko->codec_resource_lock);
-	if (cancel_work_sync(&taiko->hs_correct_plug_work)) {
-		pr_debug("%s: hs_correct_plug_work is canceled\n", __func__);
-		wcd9xxx_unlock_sleep(taiko->codec->control_data);
-	}
-	TAIKO_ACQUIRE_LOCK(taiko->codec_resource_lock);
-}
-
-static bool taiko_hs_gpio_level_remove(struct taiko_priv *taiko)
-{
-	return (gpio_get_value_cansleep(taiko->mbhc_cfg.gpio) !=
-		taiko->mbhc_cfg.gpio_level_insert);
-}
-
-/* called under codec_resource_lock acquisition */
-static void taiko_codec_hphr_gnd_switch(struct snd_soc_codec *codec, bool on)
-{
-	snd_soc_update_bits(codec, TAIKO_A_MBHC_HPH, 0x01, on);
-	if (on)
-		usleep_range(5000, 5000);
-}
-
-/* called under codec_resource_lock acquisition and mbhc override = 1 */
-static enum taiko_mbhc_plug_type
-taiko_codec_get_plug_type(struct snd_soc_codec *codec, bool highhph)
-{
-	int i;
-	bool gndswitch, vddioswitch;
-	int scaled;
-	struct taiko_mbhc_plug_type_cfg *plug_type_ptr;
-	struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
-	const bool vddio = (taiko->mbhc_data.micb_mv != VDDIO_MICBIAS_MV);
-	int num_det = (MBHC_NUM_DCE_PLUG_DETECT + vddio);
-	enum taiko_mbhc_plug_type plug_type[num_det];
-	s16 mb_v[num_det];
-	s32 mic_mv[num_det];
-	bool inval;
-	bool highdelta;
-	bool ahighv = false, highv;
-
-	/* make sure override is on */
-	WARN_ON(!(snd_soc_read(codec, TAIKO_A_CDC_MBHC_B1_CTL) & 0x04));
-
-	/* GND and MIC swap detection requires at least 2 rounds of DCE */
-	BUG_ON(num_det < 2);
-
-	plug_type_ptr =
-	    TAIKO_MBHC_CAL_PLUG_TYPE_PTR(taiko->mbhc_cfg.calibration);
-
-	plug_type[0] = PLUG_TYPE_INVALID;
-
-	/* performs DCEs for N times
-	 * 1st: check if voltage is in invalid range
-	 * 2nd - N-2nd: check voltage range and delta
-	 * N-1st: check voltage range, delta with HPHR GND switch
-	 * Nth: check voltage range with VDDIO switch if micbias V != vddio V*/
-	for (i = 0; i < num_det; i++) {
-		gndswitch = (i == (num_det - 1 - vddio));
-		vddioswitch = (vddio && ((i == num_det - 1) ||
-					 (i == num_det - 2)));
-		if (i == 0) {
-			mb_v[i] = taiko_codec_setup_hs_polling(codec);
-			mic_mv[i] = taiko_codec_sta_dce_v(codec, 1 , mb_v[i]);
-			inval = taiko_is_inval_ins_range(codec, mic_mv[i],
-							 highhph, &highv);
-			ahighv |= highv;
-			scaled = mic_mv[i];
-		} else {
-			if (vddioswitch)
-				__taiko_codec_switch_micbias(taiko->codec, 1,
-							     false, false);
-			if (gndswitch)
-				taiko_codec_hphr_gnd_switch(codec, true);
-			mb_v[i] = __taiko_codec_sta_dce(codec, 1, true, true);
-			mic_mv[i] = taiko_codec_sta_dce_v(codec, 1 , mb_v[i]);
-			if (vddioswitch)
-				scaled = taiko_scale_v_micb_vddio(taiko,
-								  mic_mv[i],
-								  false);
-			else
-				scaled = mic_mv[i];
-			/* !gndswitch & vddioswitch means the previous DCE
-			 * was done with gndswitch, don't compare with DCE
-			 * with gndswitch */
-			highdelta = taiko_is_inval_ins_delta(codec, scaled,
-					mic_mv[i - !gndswitch - vddioswitch],
-					TAIKO_MBHC_FAKE_INS_DELTA_SCALED_MV);
-			inval = (taiko_is_inval_ins_range(codec, mic_mv[i],
-							  highhph, &highv) ||
-				 highdelta);
-			ahighv |= highv;
-			if (gndswitch)
-				taiko_codec_hphr_gnd_switch(codec, false);
-			if (vddioswitch)
-				__taiko_codec_switch_micbias(taiko->codec, 0,
-							     false, false);
-			/* claim UNSUPPORTED plug insertion when
-			 * good headset is detected but HPHR GND switch makes
-			 * delta difference */
-			if (i == (num_det - 2) && highdelta && !ahighv)
-				plug_type[0] = PLUG_TYPE_GND_MIC_SWAP;
-			else if (i == (num_det - 1) && inval)
-				plug_type[0] = PLUG_TYPE_INVALID;
-		}
-		pr_debug("%s: DCE #%d, %04x, V %d, scaled V %d, GND %d, VDDIO %d, inval %d\n",
-			__func__, i + 1, mb_v[i] & 0xffff, mic_mv[i], scaled,
-			gndswitch, vddioswitch, inval);
-		/* don't need to run further DCEs */
-		if (ahighv && inval)
-			break;
-		mic_mv[i] = scaled;
-	}
-
-	for (i = 0; (plug_type[0] != PLUG_TYPE_GND_MIC_SWAP && !inval) &&
-		     i < num_det; i++) {
-		/*
-		 * If we are here, means none of the all
-		 * measurements are fake, continue plug type detection.
-		 * If all three measurements do not produce same
-		 * plug type, restart insertion detection
-		 */
-		if (mic_mv[i] < plug_type_ptr->v_no_mic) {
-			plug_type[i] = PLUG_TYPE_HEADPHONE;
-			pr_debug("%s: Detect attempt %d, detected Headphone\n",
-				 __func__, i);
-		} else if (highhph && (mic_mv[i] > plug_type_ptr->v_hs_max)) {
-			plug_type[i] = PLUG_TYPE_HIGH_HPH;
-			pr_debug(
-			"%s: Detect attempt %d, detected High Headphone\n",
-			__func__, i);
-		} else {
-			plug_type[i] = PLUG_TYPE_HEADSET;
-			pr_debug("%s: Detect attempt %d, detected Headset\n",
-				 __func__, i);
-		}
-
-		if (i > 0 && (plug_type[i - 1] != plug_type[i])) {
-			pr_err("%s: Detect attempt %d and %d are not same",
-			       __func__, i - 1, i);
-			plug_type[0] = PLUG_TYPE_INVALID;
-			inval = true;
-			break;
-		}
-	}
-
-	pr_debug("%s: Detected plug type %d\n", __func__, plug_type[0]);
-	return plug_type[0];
-}
-
-static void taiko_hs_correct_gpio_plug(struct work_struct *work)
-{
-	struct taiko_priv *taiko;
-	struct snd_soc_codec *codec;
-	int retry = 0, pt_gnd_mic_swap_cnt = 0;
-	bool correction = false;
-	enum taiko_mbhc_plug_type plug_type;
-	unsigned long timeout;
-
-	taiko = container_of(work, struct taiko_priv, hs_correct_plug_work);
-	codec = taiko->codec;
-
-	pr_debug("%s: enter\n", __func__);
-	taiko->mbhc_cfg.mclk_cb_fn(codec, 1, false);
-
-	/* Keep override on during entire plug type correction work.
-	 *
-	 * This is okay under the assumption that any GPIO irqs which use
-	 * MBHC block cancel and sync this work so override is off again
-	 * prior to GPIO interrupt handler's MBHC block usage.
-	 * Also while this correction work is running, we can guarantee
-	 * DAPM doesn't use any MBHC block as this work only runs with
-	 * headphone detection.
-	 */
-	taiko_turn_onoff_override(codec, true);
-
-	timeout = jiffies + msecs_to_jiffies(TAIKO_HS_DETECT_PLUG_TIME_MS);
-	while (!time_after(jiffies, timeout)) {
-		++retry;
-		rmb();
-		if (taiko->hs_detect_work_stop) {
-			pr_debug("%s: stop requested\n", __func__);
-			break;
-		}
-
-		msleep(TAIKO_HS_DETECT_PLUG_INERVAL_MS);
-		if (taiko_hs_gpio_level_remove(taiko)) {
-			pr_debug("%s: GPIO value is low\n", __func__);
-			break;
-		}
-
-		/* can race with removal interrupt */
-		TAIKO_ACQUIRE_LOCK(taiko->codec_resource_lock);
-		plug_type = taiko_codec_get_plug_type(codec, true);
-		TAIKO_RELEASE_LOCK(taiko->codec_resource_lock);
-
-		if (plug_type == PLUG_TYPE_INVALID) {
-			pr_debug("Invalid plug in attempt # %d\n", retry);
-			if (retry == NUM_ATTEMPTS_TO_REPORT &&
-			    taiko->current_plug == PLUG_TYPE_NONE) {
-				taiko_codec_report_plug(codec, 1,
-							SND_JACK_HEADPHONE);
-			}
-		} else if (plug_type == PLUG_TYPE_HEADPHONE) {
-			pr_debug("Good headphone detected, continue polling mic\n");
-			if (taiko->current_plug == PLUG_TYPE_NONE)
-				taiko_codec_report_plug(codec, 1,
-							SND_JACK_HEADPHONE);
-		} else {
-			if (plug_type == PLUG_TYPE_GND_MIC_SWAP) {
-				pt_gnd_mic_swap_cnt++;
-				if (pt_gnd_mic_swap_cnt <
-				    TAIKO_MBHC_GND_MIC_SWAP_THRESHOLD)
-					continue;
-				else if (pt_gnd_mic_swap_cnt >
-					 TAIKO_MBHC_GND_MIC_SWAP_THRESHOLD) {
-					/* This is due to GND/MIC switch didn't
-					 * work,  Report unsupported plug */
-				} else if (taiko->mbhc_cfg.swap_gnd_mic) {
-					/* if switch is toggled, check again,
-					 * otherwise report unsupported plug */
-					if (taiko->mbhc_cfg.swap_gnd_mic(codec))
-						continue;
-				}
-			} else
-				pt_gnd_mic_swap_cnt = 0;
-
-			TAIKO_ACQUIRE_LOCK(taiko->codec_resource_lock);
-			/* Turn off override */
-			taiko_turn_onoff_override(codec, false);
-			/* The valid plug also includes PLUG_TYPE_GND_MIC_SWAP
-			 */
-			taiko_find_plug_and_report(codec, plug_type);
-			TAIKO_RELEASE_LOCK(taiko->codec_resource_lock);
-			pr_debug("Attempt %d found correct plug %d\n", retry,
-				 plug_type);
-			correction = true;
-			break;
-		}
-	}
-
-	/* Turn off override */
-	if (!correction)
-		taiko_turn_onoff_override(codec, false);
-
-	taiko->mbhc_cfg.mclk_cb_fn(codec, 0, false);
-	pr_debug("%s: leave\n", __func__);
-	/* unlock sleep */
-	wcd9xxx_unlock_sleep(taiko->codec->control_data);
-}
-
-/* called under codec_resource_lock acquisition */
-static void taiko_codec_decide_gpio_plug(struct snd_soc_codec *codec)
-{
-	enum taiko_mbhc_plug_type plug_type;
-	struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
-
-	pr_debug("%s: enter\n", __func__);
-
-	taiko_turn_onoff_override(codec, true);
-	plug_type = taiko_codec_get_plug_type(codec, true);
-	taiko_turn_onoff_override(codec, false);
-
-	if (taiko_hs_gpio_level_remove(taiko)) {
-		pr_debug("%s: GPIO value is low when determining plug\n",
-			 __func__);
-		return;
-	}
-
-	if (plug_type == PLUG_TYPE_INVALID ||
-	    plug_type == PLUG_TYPE_GND_MIC_SWAP) {
-		taiko_schedule_hs_detect_plug(taiko);
-	} else if (plug_type == PLUG_TYPE_HEADPHONE) {
-		taiko_codec_report_plug(codec, 1, SND_JACK_HEADPHONE);
-
-		taiko_schedule_hs_detect_plug(taiko);
-	} else {
-		pr_debug("%s: Valid plug found, determine plug type %d\n",
-			 __func__, plug_type);
-		taiko_find_plug_and_report(codec, plug_type);
-	}
-}
-
-/* called under codec_resource_lock acquisition */
-static void taiko_codec_detect_plug_type(struct snd_soc_codec *codec)
-{
-	enum taiko_mbhc_plug_type plug_type;
-	struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
-	const struct taiko_mbhc_plug_detect_cfg *plug_det =
-	    TAIKO_MBHC_CAL_PLUG_DET_PTR(taiko->mbhc_cfg.calibration);
-
-	/* Turn on the override,
-	 * taiko_codec_setup_hs_polling requires override on */
-	taiko_turn_onoff_override(codec, true);
-
-	if (plug_det->t_ins_complete > 20)
-		msleep(plug_det->t_ins_complete);
-	else
-		usleep_range(plug_det->t_ins_complete * 1000,
-			     plug_det->t_ins_complete * 1000);
-
-	if (taiko->mbhc_cfg.gpio) {
-		/* Turn off the override */
-		taiko_turn_onoff_override(codec, false);
-		if (taiko_hs_gpio_level_remove(taiko))
-			pr_debug(
-			"%s: GPIO value is low when determining plug\n",
-			__func__);
-		else
-			taiko_codec_decide_gpio_plug(codec);
-		return;
-	}
-
-	plug_type = taiko_codec_get_plug_type(codec, false);
-	taiko_turn_onoff_override(codec, false);
-
-	if (plug_type == PLUG_TYPE_INVALID) {
-		pr_debug("%s: Invalid plug type detected\n", __func__);
-		snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_B1_CTL, 0x02, 0x02);
-		taiko_codec_cleanup_hs_polling(codec);
-		taiko_codec_enable_hs_detect(codec, 1,
-					     MBHC_USE_MB_TRIGGER |
-					     MBHC_USE_HPHL_TRIGGER, false);
-	} else if (plug_type == PLUG_TYPE_GND_MIC_SWAP) {
-		pr_debug("%s: GND-MIC swapped plug type detected\n", __func__);
-		taiko_codec_report_plug(codec, 1, SND_JACK_UNSUPPORTED);
-		taiko_codec_cleanup_hs_polling(codec);
-		taiko_codec_enable_hs_detect(codec, 0, 0, false);
-	} else if (plug_type == PLUG_TYPE_HEADPHONE) {
-		pr_debug("%s: Headphone Detected\n", __func__);
-		taiko_codec_report_plug(codec, 1, SND_JACK_HEADPHONE);
-		taiko_codec_cleanup_hs_polling(codec);
-		taiko_codec_enable_hs_detect(codec, 0, 0, false);
-	} else if (plug_type == PLUG_TYPE_HEADSET) {
-		pr_debug("%s: Headset detected\n", __func__);
-		taiko_codec_report_plug(codec, 1, SND_JACK_HEADSET);
-
-		/* avoid false button press detect */
-		msleep(50);
-		taiko_codec_start_hs_polling(codec);
-	}
-}
-
-/* called only from interrupt which is under codec_resource_lock acquisition */
-static void taiko_hs_insert_irq_gpio(struct taiko_priv *priv, bool is_removal)
-{
-	struct snd_soc_codec *codec = priv->codec;
-
-	if (!is_removal) {
-		pr_debug("%s: MIC trigger insertion interrupt\n", __func__);
-
-		rmb();
-		if (priv->lpi_enabled)
-			msleep(100);
-
-		rmb();
-		if (!priv->lpi_enabled) {
-			pr_debug("%s: lpi is disabled\n", __func__);
-		} else if (gpio_get_value_cansleep(priv->mbhc_cfg.gpio) ==
-			   priv->mbhc_cfg.gpio_level_insert) {
-			pr_debug(
-			"%s: Valid insertion, detect plug type\n", __func__);
-			taiko_codec_decide_gpio_plug(codec);
-		} else {
-			pr_debug(
-			"%s: Invalid insertion stop plug detection\n",
-			__func__);
-		}
-	} else {
-		pr_err("%s: GPIO used, invalid MBHC Removal\n", __func__);
-	}
-}
-
-/* called only from interrupt which is under codec_resource_lock acquisition */
-static void taiko_hs_insert_irq_nogpio(struct taiko_priv *priv, bool is_removal,
-				       bool is_mb_trigger)
-{
-	int ret;
-	struct snd_soc_codec *codec = priv->codec;
-	struct wcd9xxx *core = dev_get_drvdata(priv->codec->dev->parent);
-
-	if (is_removal) {
-		/* cancel possiblely running hs detect work */
-		taiko_cancel_hs_detect_plug(priv);
-
-		/*
-		 * If headphone is removed while playback is in progress,
-		 * it is possible that micbias will be switched to VDDIO.
-		 */
-		taiko_codec_switch_micbias(codec, 0);
-		if (priv->current_plug == PLUG_TYPE_HEADPHONE)
-			taiko_codec_report_plug(codec, 0, SND_JACK_HEADPHONE);
-		else if (priv->current_plug == PLUG_TYPE_GND_MIC_SWAP)
-			taiko_codec_report_plug(codec, 0, SND_JACK_UNSUPPORTED);
-		else
-			WARN(1, "%s: Unexpected current plug type %d\n",
-			     __func__, priv->current_plug);
-		taiko_codec_shutdown_hs_removal_detect(codec);
-		taiko_codec_enable_hs_detect(codec, 1,
-					     MBHC_USE_MB_TRIGGER |
-					     MBHC_USE_HPHL_TRIGGER,
-					     true);
-	} else if (is_mb_trigger && !is_removal) {
-		pr_debug("%s: Waiting for Headphone left trigger\n",
-			__func__);
-		wcd9xxx_lock_sleep(core);
-		if (schedule_delayed_work(&priv->mbhc_insert_dwork,
-					  usecs_to_jiffies(1000000)) == 0) {
-			pr_err("%s: mbhc_insert_dwork is already scheduled\n",
-			       __func__);
-			wcd9xxx_unlock_sleep(core);
-		}
-		taiko_codec_enable_hs_detect(codec, 1, MBHC_USE_HPHL_TRIGGER,
-					     false);
-	} else  {
-		ret = cancel_delayed_work(&priv->mbhc_insert_dwork);
-		if (ret != 0) {
-			pr_debug(
-			"%s: Complete plug insertion, Detecting plug type\n",
-			__func__);
-			taiko_codec_detect_plug_type(codec);
-			wcd9xxx_unlock_sleep(core);
-		} else {
-			wcd9xxx_enable_irq(codec->control_data,
-					   WCD9XXX_IRQ_MBHC_INSERTION);
-			pr_err("%s: Error detecting plug insertion\n",
-			       __func__);
-		}
-	}
-}
-
-static irqreturn_t taiko_hs_insert_irq(int irq, void *data)
-{
-	bool is_mb_trigger, is_removal;
-	struct taiko_priv *priv = data;
-	struct snd_soc_codec *codec = priv->codec;
-
-	pr_debug("%s: enter\n", __func__);
-	TAIKO_ACQUIRE_LOCK(priv->codec_resource_lock);
-	wcd9xxx_disable_irq(codec->control_data, WCD9XXX_IRQ_MBHC_INSERTION);
-
-	is_mb_trigger = !!(snd_soc_read(codec, priv->mbhc_bias_regs.mbhc_reg) &
-					0x10);
-	is_removal = !!(snd_soc_read(codec, TAIKO_A_CDC_MBHC_INT_CTL) & 0x02);
-	snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_INT_CTL, 0x03, 0x00);
-
-	/* Turn off both HPH and MIC line schmitt triggers */
-	snd_soc_update_bits(codec, priv->mbhc_bias_regs.mbhc_reg, 0x90, 0x00);
-	snd_soc_update_bits(codec, TAIKO_A_MBHC_HPH, 0x13, 0x00);
-	snd_soc_update_bits(codec, priv->mbhc_bias_regs.ctl_reg, 0x01, 0x00);
-
-	if (priv->mbhc_cfg.gpio)
-		taiko_hs_insert_irq_gpio(priv, is_removal);
-	else
-		taiko_hs_insert_irq_nogpio(priv, is_removal, is_mb_trigger);
-
-	TAIKO_RELEASE_LOCK(priv->codec_resource_lock);
-	return IRQ_HANDLED;
-}
-
-static bool is_valid_mic_voltage(struct snd_soc_codec *codec, s32 mic_mv)
-{
-	struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
-	const struct taiko_mbhc_plug_type_cfg *plug_type =
-	    TAIKO_MBHC_CAL_PLUG_TYPE_PTR(taiko->mbhc_cfg.calibration);
-	const s16 v_hs_max = taiko_get_current_v_hs_max(taiko);
-
-	return (!(mic_mv > 10 && mic_mv < 80) && (mic_mv > plug_type->v_no_mic)
-		&& (mic_mv < v_hs_max)) ? true : false;
-}
-
-/* called under codec_resource_lock acquisition
- * returns true if mic voltage range is back to normal insertion
- * returns false either if timedout or removed */
-static bool taiko_hs_remove_settle(struct snd_soc_codec *codec)
-{
-	int i;
-	bool timedout, settled = false;
-	s32 mic_mv[MBHC_NUM_DCE_PLUG_DETECT];
-	short mb_v[MBHC_NUM_DCE_PLUG_DETECT];
-	unsigned long retry = 0, timeout;
-	struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
-	const s16 v_hs_max = taiko_get_current_v_hs_max(taiko);
-
-	timeout = jiffies + msecs_to_jiffies(TAIKO_HS_DETECT_PLUG_TIME_MS);
-	while (!(timedout = time_after(jiffies, timeout))) {
-		retry++;
-		if (taiko->mbhc_cfg.gpio && taiko_hs_gpio_level_remove(taiko)) {
-			pr_debug("%s: GPIO indicates removal\n", __func__);
-			break;
-		}
-
-		if (taiko->mbhc_cfg.gpio) {
-			if (retry > 1)
-				msleep(250);
-			else
-				msleep(50);
-		}
-
-		if (taiko->mbhc_cfg.gpio && taiko_hs_gpio_level_remove(taiko)) {
-			pr_debug("%s: GPIO indicates removal\n", __func__);
-			break;
-		}
-
-		for (i = 0; i < MBHC_NUM_DCE_PLUG_DETECT; i++) {
-			mb_v[i] = taiko_codec_sta_dce(codec, 1,  true);
-			mic_mv[i] = taiko_codec_sta_dce_v(codec, 1 , mb_v[i]);
-			pr_debug("%s : DCE run %lu, mic_mv = %d(%x)\n",
-				 __func__, retry, mic_mv[i], mb_v[i]);
-		}
-
-		if (taiko->mbhc_cfg.gpio && taiko_hs_gpio_level_remove(taiko)) {
-			pr_debug("%s: GPIO indicates removal\n", __func__);
-			break;
-		}
-
-		if (taiko->current_plug == PLUG_TYPE_NONE) {
-			pr_debug("%s : headset/headphone is removed\n",
-				 __func__);
-			break;
-		}
-
-		for (i = 0; i < MBHC_NUM_DCE_PLUG_DETECT; i++)
-			if (!is_valid_mic_voltage(codec, mic_mv[i]))
-				break;
-
-		if (i == MBHC_NUM_DCE_PLUG_DETECT) {
-			pr_debug("%s: MIC voltage settled\n", __func__);
-			settled = true;
-			msleep(200);
-			break;
-		}
-
-		/* only for non-GPIO remove irq */
-		if (!taiko->mbhc_cfg.gpio) {
-			for (i = 0; i < MBHC_NUM_DCE_PLUG_DETECT; i++)
-				if (mic_mv[i] < v_hs_max)
-					break;
-			if (i == MBHC_NUM_DCE_PLUG_DETECT) {
-				pr_debug("%s: Headset is removed\n", __func__);
-				break;
-			}
-		}
-	}
-
-	if (timedout)
-		pr_debug("%s: Microphone did not settle in %d seconds\n",
-			 __func__, TAIKO_HS_DETECT_PLUG_TIME_MS);
-	return settled;
-}
-
-/* called only from interrupt which is under codec_resource_lock acquisition */
-static void taiko_hs_remove_irq_gpio(struct taiko_priv *priv)
-{
-	struct snd_soc_codec *codec = priv->codec;
-
-	if (taiko_hs_remove_settle(codec))
-		taiko_codec_start_hs_polling(codec);
-	pr_debug("%s: remove settle done\n", __func__);
-}
-
-/* called only from interrupt which is under codec_resource_lock acquisition */
-static void taiko_hs_remove_irq_nogpio(struct taiko_priv *priv)
-{
-	short bias_value;
-	bool removed = true;
-	struct snd_soc_codec *codec = priv->codec;
-	const struct taiko_mbhc_general_cfg *generic =
-	    TAIKO_MBHC_CAL_GENERAL_PTR(priv->mbhc_cfg.calibration);
-	int min_us = TAIKO_FAKE_REMOVAL_MIN_PERIOD_MS * 1000;
-
-	if (priv->current_plug != PLUG_TYPE_HEADSET) {
-		pr_debug("%s(): Headset is not inserted, ignore removal\n",
-			 __func__);
-		snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_CLK_CTL,
-				    0x08, 0x08);
-		return;
-	}
-
-	usleep_range(generic->t_shutdown_plug_rem,
-		     generic->t_shutdown_plug_rem);
-
-	do {
-		bias_value = taiko_codec_sta_dce(codec, 1,  true);
-		pr_debug("%s: DCE %d,%d, %d us left\n", __func__, bias_value,
-			 taiko_codec_sta_dce_v(codec, 1, bias_value), min_us);
-		if (bias_value < taiko_get_current_v_ins(priv, false)) {
-			pr_debug("%s: checking false removal\n", __func__);
-			msleep(500);
-			removed = !taiko_hs_remove_settle(codec);
-			pr_debug("%s: headset %sactually removed\n", __func__,
-				 removed ? "" : "not ");
-			break;
-		}
-		min_us -= priv->mbhc_data.t_dce;
-	} while (min_us > 0);
-
-	if (removed) {
-		/* cancel possiblely running hs detect work */
-		taiko_cancel_hs_detect_plug(priv);
-		/*
-		 * If this removal is not false, first check the micbias
-		 * switch status and switch it to LDOH if it is already
-		 * switched to VDDIO.
-		 */
-		taiko_codec_switch_micbias(codec, 0);
-
-		taiko_codec_report_plug(codec, 0, SND_JACK_HEADSET);
-		taiko_codec_cleanup_hs_polling(codec);
-		taiko_codec_enable_hs_detect(codec, 1,
-					     MBHC_USE_MB_TRIGGER |
-					     MBHC_USE_HPHL_TRIGGER,
-					     true);
-	} else {
-		taiko_codec_start_hs_polling(codec);
-	}
-}
-
-static irqreturn_t taiko_hs_remove_irq(int irq, void *data)
-{
-	struct taiko_priv *priv = data;
-	bool vddio;
-	pr_debug("%s: enter, removal interrupt\n", __func__);
-
-	TAIKO_ACQUIRE_LOCK(priv->codec_resource_lock);
-	vddio = (priv->mbhc_data.micb_mv != VDDIO_MICBIAS_MV &&
-		 priv->mbhc_micbias_switched);
-	if (vddio)
-		__taiko_codec_switch_micbias(priv->codec, 0, false, true);
-
-	if (priv->mbhc_cfg.gpio)
-		taiko_hs_remove_irq_gpio(priv);
-	else
-		taiko_hs_remove_irq_nogpio(priv);
-
-	/* if driver turned off vddio switch and headset is not removed,
-	 * turn on the vddio switch back, if headset is removed then vddio
-	 * switch is off by time now and shouldn't be turn on again from here */
-	if (vddio && priv->current_plug == PLUG_TYPE_HEADSET)
-		__taiko_codec_switch_micbias(priv->codec, 1, true, true);
-	TAIKO_RELEASE_LOCK(priv->codec_resource_lock);
-
-	return IRQ_HANDLED;
-}
-
-static void taiko_mbhc_insert_work(struct work_struct *work)
-{
-	struct delayed_work *dwork;
-	struct taiko_priv *taiko;
-	struct snd_soc_codec *codec;
-	struct wcd9xxx *taiko_core;
-
-	dwork = to_delayed_work(work);
-	taiko = container_of(dwork, struct taiko_priv, mbhc_insert_dwork);
-	codec = taiko->codec;
-	taiko_core = dev_get_drvdata(codec->dev->parent);
-
-	pr_debug("%s:\n", __func__);
-
-	/* Turn off both HPH and MIC line schmitt triggers */
-	snd_soc_update_bits(codec, taiko->mbhc_bias_regs.mbhc_reg, 0x90, 0x00);
-	snd_soc_update_bits(codec, TAIKO_A_MBHC_HPH, 0x13, 0x00);
-	snd_soc_update_bits(codec, taiko->mbhc_bias_regs.ctl_reg, 0x01, 0x00);
-	wcd9xxx_disable_irq_sync(codec->control_data,
-				 WCD9XXX_IRQ_MBHC_INSERTION);
-	taiko_codec_detect_plug_type(codec);
-	wcd9xxx_unlock_sleep(taiko_core);
-}
-
-static void taiko_hs_gpio_handler(struct snd_soc_codec *codec)
-{
-	bool insert;
-	struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
-	bool is_removed = false;
-
-	pr_debug("%s: enter\n", __func__);
-
-	taiko->in_gpio_handler = true;
-	/* Wait here for debounce time */
-	usleep_range(TAIKO_GPIO_IRQ_DEBOUNCE_TIME_US,
-		     TAIKO_GPIO_IRQ_DEBOUNCE_TIME_US);
-
-	TAIKO_ACQUIRE_LOCK(taiko->codec_resource_lock);
-
-	/* cancel pending button press */
-	if (taiko_cancel_btn_work(taiko))
-		pr_debug("%s: button press is canceled\n", __func__);
-
-	insert = (gpio_get_value_cansleep(taiko->mbhc_cfg.gpio) ==
-		  taiko->mbhc_cfg.gpio_level_insert);
-	if ((taiko->current_plug == PLUG_TYPE_NONE) && insert) {
-		taiko->lpi_enabled = false;
-		wmb();
-
-		/* cancel detect plug */
-		taiko_cancel_hs_detect_plug(taiko);
-
-		/* Disable Mic Bias pull down and HPH Switch to GND */
-		snd_soc_update_bits(codec, taiko->mbhc_bias_regs.ctl_reg, 0x01,
-				    0x00);
-		snd_soc_update_bits(codec, TAIKO_A_MBHC_HPH, 0x01, 0x00);
-		taiko_codec_detect_plug_type(codec);
-	} else if ((taiko->current_plug != PLUG_TYPE_NONE) && !insert) {
-		taiko->lpi_enabled = false;
-		wmb();
-
-		/* cancel detect plug */
-		taiko_cancel_hs_detect_plug(taiko);
-
-		if (taiko->current_plug == PLUG_TYPE_HEADPHONE) {
-			taiko_codec_report_plug(codec, 0, SND_JACK_HEADPHONE);
-			is_removed = true;
-		} else if (taiko->current_plug == PLUG_TYPE_GND_MIC_SWAP) {
-			taiko_codec_report_plug(codec, 0, SND_JACK_UNSUPPORTED);
-			is_removed = true;
-		} else if (taiko->current_plug == PLUG_TYPE_HEADSET) {
-			taiko_codec_pause_hs_polling(codec);
-			taiko_codec_cleanup_hs_polling(codec);
-			taiko_codec_report_plug(codec, 0, SND_JACK_HEADSET);
-			is_removed = true;
-		}
-
-		if (is_removed) {
-			/* Enable Mic Bias pull down and HPH Switch to GND */
-			snd_soc_update_bits(codec,
-					    taiko->mbhc_bias_regs.ctl_reg, 0x01,
-					    0x01);
-			snd_soc_update_bits(codec, TAIKO_A_MBHC_HPH, 0x01,
-					    0x01);
-			/* Make sure mic trigger is turned off */
-			snd_soc_update_bits(codec,
-					    taiko->mbhc_bias_regs.ctl_reg,
-					    0x01, 0x01);
-			snd_soc_update_bits(codec,
-					    taiko->mbhc_bias_regs.mbhc_reg,
-					    0x90, 0x00);
-			/* Reset MBHC State Machine */
-			snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_CLK_CTL,
-					    0x08, 0x08);
-			snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_CLK_CTL,
-					    0x08, 0x00);
-			/* Turn off override */
-			taiko_turn_onoff_override(codec, false);
-		}
-	}
-
-	taiko->in_gpio_handler = false;
-	TAIKO_RELEASE_LOCK(taiko->codec_resource_lock);
-	pr_debug("%s: leave\n", __func__);
-}
-
-static irqreturn_t taiko_mechanical_plug_detect_irq(int irq, void *data)
-{
-	int r = IRQ_HANDLED;
-	struct snd_soc_codec *codec = data;
-
-	if (unlikely(wcd9xxx_lock_sleep(codec->control_data) == false)) {
-		pr_warn("%s: failed to hold suspend\n", __func__);
-		r = IRQ_NONE;
-	} else {
-		taiko_hs_gpio_handler(codec);
-		wcd9xxx_unlock_sleep(codec->control_data);
-	}
-
-	return r;
-}
-
-static int taiko_mbhc_init_and_calibrate(struct taiko_priv *taiko)
-{
-	int ret = 0;
-	struct snd_soc_codec *codec = taiko->codec;
-
-	taiko->mbhc_cfg.mclk_cb_fn(codec, 1, false);
-	taiko_mbhc_init(codec);
-	taiko_mbhc_cal(codec);
-	taiko_mbhc_calc_thres(codec);
-	taiko->mbhc_cfg.mclk_cb_fn(codec, 0, false);
-	taiko_codec_calibrate_hs_polling(codec);
-	if (!taiko->mbhc_cfg.gpio) {
-		ret = taiko_codec_enable_hs_detect(codec, 1,
-						   MBHC_USE_MB_TRIGGER |
-						   MBHC_USE_HPHL_TRIGGER,
-						   false);
-
-		if (IS_ERR_VALUE(ret))
-			pr_err("%s: Failed to setup MBHC detection\n",
-			       __func__);
-	} else {
-		/* Enable Mic Bias pull down and HPH Switch to GND */
-		snd_soc_update_bits(codec, taiko->mbhc_bias_regs.ctl_reg,
-				    0x01, 0x01);
-		snd_soc_update_bits(codec, TAIKO_A_MBHC_HPH, 0x01, 0x01);
-		INIT_WORK(&taiko->hs_correct_plug_work,
-			  taiko_hs_correct_gpio_plug);
-	}
-
-	if (!IS_ERR_VALUE(ret)) {
-		snd_soc_update_bits(codec, TAIKO_A_RX_HPH_OCP_CTL, 0x10, 0x10);
-		wcd9xxx_enable_irq(codec->control_data,
-				 WCD9XXX_IRQ_HPH_PA_OCPL_FAULT);
-		wcd9xxx_enable_irq(codec->control_data,
-				 WCD9XXX_IRQ_HPH_PA_OCPR_FAULT);
-
-		if (taiko->mbhc_cfg.gpio) {
-			ret = request_threaded_irq(taiko->mbhc_cfg.gpio_irq,
-					       NULL,
-					       taiko_mechanical_plug_detect_irq,
-					       (IRQF_TRIGGER_RISING |
-						IRQF_TRIGGER_FALLING),
-					       "taiko-gpio", codec);
-			if (!IS_ERR_VALUE(ret)) {
-				ret = enable_irq_wake(taiko->mbhc_cfg.gpio_irq);
-				/* Bootup time detection */
-				taiko_hs_gpio_handler(codec);
-			}
-		}
-	}
-
-	return ret;
-}
-
-static void mbhc_fw_read(struct work_struct *work)
-{
-	struct delayed_work *dwork;
-	struct taiko_priv *taiko;
-	struct snd_soc_codec *codec;
-	const struct firmware *fw;
-	int ret = -1, retry = 0;
-
-	dwork = to_delayed_work(work);
-	taiko = container_of(dwork, struct taiko_priv, mbhc_firmware_dwork);
-	codec = taiko->codec;
-
-	while (retry < MBHC_FW_READ_ATTEMPTS) {
-		retry++;
-		pr_info("%s:Attempt %d to request MBHC firmware\n",
-			__func__, retry);
-		ret = request_firmware(&fw, "wcd9320/wcd9320_mbhc.bin",
-					codec->dev);
-
-		if (ret != 0) {
-			usleep_range(MBHC_FW_READ_TIMEOUT,
-				     MBHC_FW_READ_TIMEOUT);
-		} else {
-			pr_info("%s: MBHC Firmware read succesful\n", __func__);
-			break;
-		}
-	}
-
-	if (ret != 0) {
-		pr_err("%s: Cannot load MBHC firmware use default cal\n",
-			__func__);
-	} else if (taiko_mbhc_fw_validate(fw) == false) {
-		pr_err("%s: Invalid MBHC cal data size use default cal\n",
-			 __func__);
-		release_firmware(fw);
-	} else {
-		taiko->mbhc_cfg.calibration = (void *)fw->data;
-		taiko->mbhc_fw = fw;
-	}
-
-	(void) taiko_mbhc_init_and_calibrate(taiko);
-}
-
-int taiko_hs_detect(struct snd_soc_codec *codec,
-		    const struct taiko_mbhc_config *cfg)
-{
-	struct taiko_priv *taiko;
-	int rc = 0;
-
-	if (!codec) {
-		pr_err("%s: no codec\n", __func__);
-		return -EINVAL;
-	}
-
-	if (!cfg->calibration) {
-		pr_warn("%s: mbhc is not configured\n", __func__);
-		return 0;
-	}
-
-	if (cfg->mclk_rate != TAIKO_MCLK_RATE_12288KHZ) {
-		if (cfg->mclk_rate == TAIKO_MCLK_RATE_9600KHZ)
-			pr_err("Error: clock rate %dHz is not yet supported\n",
-			       cfg->mclk_rate);
-		else
-			pr_err("Error: unsupported clock rate %d\n",
-			       cfg->mclk_rate);
-		return -EINVAL;
-	}
-
-	taiko = snd_soc_codec_get_drvdata(codec);
-	taiko->mbhc_cfg = *cfg;
-	taiko->in_gpio_handler = false;
-	taiko->current_plug = PLUG_TYPE_NONE;
-	taiko->lpi_enabled = false;
-	taiko_get_mbhc_micbias_regs(codec, &taiko->mbhc_bias_regs);
-
-	/* Put CFILT in fast mode by default */
-	snd_soc_update_bits(codec, taiko->mbhc_bias_regs.cfilt_ctl,
-			    0x40, TAIKO_CFILT_FAST_MODE);
-	INIT_DELAYED_WORK(&taiko->mbhc_firmware_dwork, mbhc_fw_read);
-	INIT_DELAYED_WORK(&taiko->mbhc_btn_dwork, btn_lpress_fn);
-	INIT_WORK(&taiko->hphlocp_work, hphlocp_off_report);
-	INIT_WORK(&taiko->hphrocp_work, hphrocp_off_report);
-	INIT_DELAYED_WORK(&taiko->mbhc_insert_dwork, taiko_mbhc_insert_work);
-
-	if (!taiko->mbhc_cfg.read_fw_bin)
-		rc = taiko_mbhc_init_and_calibrate(taiko);
-	else
-		schedule_delayed_work(&taiko->mbhc_firmware_dwork,
-				      usecs_to_jiffies(MBHC_FW_READ_TIMEOUT));
-
-	return rc;
-}
-EXPORT_SYMBOL_GPL(taiko_hs_detect);
-
 static unsigned long slimbus_value;
 
 static irqreturn_t taiko_slimbus_irq(int irq, void *data)
@@ -7285,8 +4180,8 @@
 		}
 		wcd9xxx_interface_reg_write(codec->control_data,
 			TAIKO_SLIM_PGD_PORT_INT_CLR0 + i, 0xFF);
-	}
 
+	}
 	return IRQ_HANDLED;
 }
 
@@ -7317,7 +4212,6 @@
 	/** end of Ear PA load 32 */
 };
 
-
 static const struct taiko_reg_mask_val taiko_1_0_class_h_hph[] = {
 
 	/* CLASS-H HPH  IDLE_THRESHOLD Table */
@@ -7372,7 +4266,7 @@
 static int taiko_handle_pdata(struct taiko_priv *taiko)
 {
 	struct snd_soc_codec *codec = taiko->codec;
-	struct wcd9xxx_pdata *pdata = taiko->pdata;
+	struct wcd9xxx_pdata *pdata = taiko->resmgr.pdata;
 	int k1, k2, k3, rc = 0;
 	u8 leg_mode, txfe_bypass, txfe_buff, flag;
 	u8 i = 0, j = 0;
@@ -7390,46 +4284,38 @@
 	flag = pdata->amic_settings.use_pdata;
 
 	/* Make sure settings are correct */
-	if ((pdata->micbias.ldoh_v > TAIKO_LDOH_2P85_V) ||
-	    (pdata->micbias.bias1_cfilt_sel > TAIKO_CFILT3_SEL) ||
-	    (pdata->micbias.bias2_cfilt_sel > TAIKO_CFILT3_SEL) ||
-	    (pdata->micbias.bias3_cfilt_sel > TAIKO_CFILT3_SEL) ||
-	    (pdata->micbias.bias4_cfilt_sel > TAIKO_CFILT3_SEL)) {
+	if ((pdata->micbias.ldoh_v > WCD9XXX_LDOH_3P0_V) ||
+	    (pdata->micbias.bias1_cfilt_sel > WCD9XXX_CFILT3_SEL) ||
+	    (pdata->micbias.bias2_cfilt_sel > WCD9XXX_CFILT3_SEL) ||
+	    (pdata->micbias.bias3_cfilt_sel > WCD9XXX_CFILT3_SEL) ||
+	    (pdata->micbias.bias4_cfilt_sel > WCD9XXX_CFILT3_SEL)) {
 		rc = -EINVAL;
 		goto done;
 	}
-
 	/* figure out k value */
-	k1 = taiko_find_k_value(pdata->micbias.ldoh_v,
-		pdata->micbias.cfilt1_mv);
-	k2 = taiko_find_k_value(pdata->micbias.ldoh_v,
-		pdata->micbias.cfilt2_mv);
-	k3 = taiko_find_k_value(pdata->micbias.ldoh_v,
-		pdata->micbias.cfilt3_mv);
+	k1 = wcd9xxx_resmgr_get_k_val(&taiko->resmgr, pdata->micbias.cfilt1_mv);
+	k2 = wcd9xxx_resmgr_get_k_val(&taiko->resmgr, pdata->micbias.cfilt2_mv);
+	k3 = wcd9xxx_resmgr_get_k_val(&taiko->resmgr, pdata->micbias.cfilt3_mv);
 
 	if (IS_ERR_VALUE(k1) || IS_ERR_VALUE(k2) || IS_ERR_VALUE(k3)) {
 		rc = -EINVAL;
 		goto done;
 	}
-
 	/* Set voltage level and always use LDO */
 	snd_soc_update_bits(codec, TAIKO_A_LDO_H_MODE_1, 0x0C,
-		(pdata->micbias.ldoh_v << 2));
+			    (pdata->micbias.ldoh_v << 2));
 
-	snd_soc_update_bits(codec, TAIKO_A_MICB_CFILT_1_VAL, 0xFC,
-		(k1 << 2));
-	snd_soc_update_bits(codec, TAIKO_A_MICB_CFILT_2_VAL, 0xFC,
-		(k2 << 2));
-	snd_soc_update_bits(codec, TAIKO_A_MICB_CFILT_3_VAL, 0xFC,
-		(k3 << 2));
+	snd_soc_update_bits(codec, TAIKO_A_MICB_CFILT_1_VAL, 0xFC, (k1 << 2));
+	snd_soc_update_bits(codec, TAIKO_A_MICB_CFILT_2_VAL, 0xFC, (k2 << 2));
+	snd_soc_update_bits(codec, TAIKO_A_MICB_CFILT_3_VAL, 0xFC, (k3 << 2));
 
 	snd_soc_update_bits(codec, TAIKO_A_MICB_1_CTL, 0x60,
-		(pdata->micbias.bias1_cfilt_sel << 5));
+			    (pdata->micbias.bias1_cfilt_sel << 5));
 	snd_soc_update_bits(codec, TAIKO_A_MICB_2_CTL, 0x60,
-		(pdata->micbias.bias2_cfilt_sel << 5));
+			    (pdata->micbias.bias2_cfilt_sel << 5));
 	snd_soc_update_bits(codec, TAIKO_A_MICB_3_CTL, 0x60,
-		(pdata->micbias.bias3_cfilt_sel << 5));
-	snd_soc_update_bits(codec, taiko->reg_addr.micb_4_ctl, 0x60,
+			    (pdata->micbias.bias3_cfilt_sel << 5));
+	snd_soc_update_bits(codec, taiko->resmgr.reg_addr->micb_4_ctl, 0x60,
 			    (pdata->micbias.bias4_cfilt_sel << 5));
 
 	for (i = 0; i < 6; j++, i += 2) {
@@ -7672,230 +4558,48 @@
 				taiko_codec_reg_init_val[i].val);
 }
 
-static void taiko_update_reg_address(struct taiko_priv *priv)
-{
-	struct taiko_reg_address *reg_addr = &priv->reg_addr;
-	reg_addr->micb_4_mbhc = TAIKO_A_MICB_4_MBHC;
-	reg_addr->micb_4_int_rbias = TAIKO_A_MICB_4_INT_RBIAS;
-	reg_addr->micb_4_ctl = TAIKO_A_MICB_4_CTL;
-
-}
-
-#ifdef CONFIG_DEBUG_FS
-static int codec_debug_open(struct inode *inode, struct file *file)
-{
-	file->private_data = inode->i_private;
-	return 0;
-}
-
-static ssize_t codec_debug_write(struct file *filp,
-	const char __user *ubuf, size_t cnt, loff_t *ppos)
-{
-	char lbuf[32];
-	char *buf;
-	int rc;
-	struct taiko_priv *taiko = filp->private_data;
-
-	if (cnt > sizeof(lbuf) - 1)
-		return -EINVAL;
-
-	rc = copy_from_user(lbuf, ubuf, cnt);
-	if (rc)
-		return -EFAULT;
-
-	lbuf[cnt] = '\0';
-	buf = (char *)lbuf;
-	taiko->no_mic_headset_override = (*strsep(&buf, " ") == '0') ?
-					     false : true;
-	return rc;
-}
-
-static ssize_t codec_mbhc_debug_read(struct file *file, char __user *buf,
-				     size_t count, loff_t *pos)
-{
-	const int size = 768;
-	char buffer[size];
-	int n = 0;
-	struct taiko_priv *taiko = file->private_data;
-	struct snd_soc_codec *codec = taiko->codec;
-	const struct mbhc_internal_cal_data *p = &taiko->mbhc_data;
-	const s16 v_ins_hu_cur = taiko_get_current_v_ins(taiko, true);
-	const s16 v_ins_h_cur = taiko_get_current_v_ins(taiko, false);
-
-	n = scnprintf(buffer, size - n, "dce_z = %x(%dmv)\n",  p->dce_z,
-		     taiko_codec_sta_dce_v(codec, 1, p->dce_z));
-	n += scnprintf(buffer + n, size - n, "dce_mb = %x(%dmv)\n",
-		       p->dce_mb, taiko_codec_sta_dce_v(codec, 1, p->dce_mb));
-	n += scnprintf(buffer + n, size - n, "sta_z = %x(%dmv)\n",
-		       p->sta_z, taiko_codec_sta_dce_v(codec, 0, p->sta_z));
-	n += scnprintf(buffer + n, size - n, "sta_mb = %x(%dmv)\n",
-		       p->sta_mb, taiko_codec_sta_dce_v(codec, 0, p->sta_mb));
-	n += scnprintf(buffer + n, size - n, "t_dce = %x\n",  p->t_dce);
-	n += scnprintf(buffer + n, size - n, "t_sta = %x\n",  p->t_sta);
-	n += scnprintf(buffer + n, size - n, "micb_mv = %dmv\n",
-		       p->micb_mv);
-	n += scnprintf(buffer + n, size - n, "v_ins_hu = %x(%dmv)%s\n",
-		       p->v_ins_hu,
-		       taiko_codec_sta_dce_v(codec, 0, p->v_ins_hu),
-		       p->v_ins_hu == v_ins_hu_cur ? "*" : "");
-	n += scnprintf(buffer + n, size - n, "v_ins_h = %x(%dmv)%s\n",
-		       p->v_ins_h, taiko_codec_sta_dce_v(codec, 1, p->v_ins_h),
-		       p->v_ins_h == v_ins_h_cur ? "*" : "");
-	n += scnprintf(buffer + n, size - n, "adj_v_ins_hu = %x(%dmv)%s\n",
-		       p->adj_v_ins_hu,
-		       taiko_codec_sta_dce_v(codec, 0, p->adj_v_ins_hu),
-		       p->adj_v_ins_hu == v_ins_hu_cur ? "*" : "");
-	n += scnprintf(buffer + n, size - n, "adj_v_ins_h = %x(%dmv)%s\n",
-		       p->adj_v_ins_h,
-		       taiko_codec_sta_dce_v(codec, 1, p->adj_v_ins_h),
-		       p->adj_v_ins_h == v_ins_h_cur ? "*" : "");
-	n += scnprintf(buffer + n, size - n, "v_b1_hu = %x(%dmv)\n",
-		       p->v_b1_hu, taiko_codec_sta_dce_v(codec, 0, p->v_b1_hu));
-	n += scnprintf(buffer + n, size - n, "v_b1_h = %x(%dmv)\n",
-		       p->v_b1_h, taiko_codec_sta_dce_v(codec, 1, p->v_b1_h));
-	n += scnprintf(buffer + n, size - n, "v_b1_huc = %x(%dmv)\n",
-		       p->v_b1_huc,
-		       taiko_codec_sta_dce_v(codec, 1, p->v_b1_huc));
-	n += scnprintf(buffer + n, size - n, "v_brh = %x(%dmv)\n",
-		       p->v_brh, taiko_codec_sta_dce_v(codec, 1, p->v_brh));
-	n += scnprintf(buffer + n, size - n, "v_brl = %x(%dmv)\n",  p->v_brl,
-		       taiko_codec_sta_dce_v(codec, 0, p->v_brl));
-	n += scnprintf(buffer + n, size - n, "v_no_mic = %x(%dmv)\n",
-		       p->v_no_mic,
-		       taiko_codec_sta_dce_v(codec, 0, p->v_no_mic));
-	n += scnprintf(buffer + n, size - n, "npoll = %d\n",  p->npoll);
-	n += scnprintf(buffer + n, size - n, "nbounce_wait = %d\n",
-		       p->nbounce_wait);
-	n += scnprintf(buffer + n, size - n, "v_inval_ins_low = %d\n",
-		       p->v_inval_ins_low);
-	n += scnprintf(buffer + n, size - n, "v_inval_ins_high = %d\n",
-		       p->v_inval_ins_high);
-	if (taiko->mbhc_cfg.gpio)
-		n += scnprintf(buffer + n, size - n, "GPIO insert = %d\n",
-			       taiko_hs_gpio_level_remove(taiko));
-	buffer[n] = 0;
-
-	return simple_read_from_buffer(buf, count, pos, buffer, n);
-}
-
-static const struct file_operations codec_debug_ops = {
-	.open = codec_debug_open,
-	.write = codec_debug_write,
-};
-
-static const struct file_operations codec_mbhc_debug_ops = {
-	.open = codec_debug_open,
-	.read = codec_mbhc_debug_read,
-};
-#endif
-
 static int taiko_setup_irqs(struct taiko_priv *taiko)
 {
-	int ret;
 	int i;
+	int ret = 0;
 	struct snd_soc_codec *codec = taiko->codec;
 
-	ret = wcd9xxx_request_irq(codec->control_data,
-				  WCD9XXX_IRQ_MBHC_INSERTION,
-				  taiko_hs_insert_irq, "Headset insert detect",
-				  taiko);
-	if (ret) {
-		pr_err("%s: Failed to request irq %d\n", __func__,
-		       WCD9XXX_IRQ_MBHC_INSERTION);
-		goto err_insert_irq;
-	}
-	wcd9xxx_disable_irq(codec->control_data,
-			    WCD9XXX_IRQ_MBHC_INSERTION);
-
-	ret = wcd9xxx_request_irq(codec->control_data, WCD9XXX_IRQ_MBHC_REMOVAL,
-				  taiko_hs_remove_irq, "Headset remove detect",
-				  taiko);
-	if (ret) {
-		pr_err("%s: Failed to request irq %d\n", __func__,
-			WCD9XXX_IRQ_MBHC_REMOVAL);
-		goto err_remove_irq;
-	}
-
-	ret = wcd9xxx_request_irq(codec->control_data,
-				  WCD9XXX_IRQ_MBHC_POTENTIAL,
-				  taiko_dce_handler, "DC Estimation detect",
-				  taiko);
-	if (ret) {
-		pr_err("%s: Failed to request irq %d\n", __func__,
-		       WCD9XXX_IRQ_MBHC_POTENTIAL);
-		goto err_potential_irq;
-	}
-
-	ret = wcd9xxx_request_irq(codec->control_data, WCD9XXX_IRQ_MBHC_RELEASE,
-				 taiko_release_handler, "Button Release detect",
-				 taiko);
-	if (ret) {
-		pr_err("%s: Failed to request irq %d\n", __func__,
-		       WCD9XXX_IRQ_MBHC_RELEASE);
-		goto err_release_irq;
-	}
-
 	ret = wcd9xxx_request_irq(codec->control_data, WCD9XXX_IRQ_SLIMBUS,
 				  taiko_slimbus_irq, "SLIMBUS Slave", taiko);
 	if (ret) {
 		pr_err("%s: Failed to request irq %d\n", __func__,
 		       WCD9XXX_IRQ_SLIMBUS);
-		goto err_slimbus_irq;
+		goto exit;
 	}
 
 	for (i = 0; i < WCD9XXX_SLIM_NUM_PORT_REG; i++)
 		wcd9xxx_interface_reg_write(codec->control_data,
-					   TAIKO_SLIM_PGD_PORT_INT_EN0 + i,
-					   0xFF);
-
-	ret = wcd9xxx_request_irq(codec->control_data,
-				  WCD9XXX_IRQ_HPH_PA_OCPL_FAULT,
-				  taiko_hphl_ocp_irq,
-				  "HPH_L OCP detect", taiko);
-	if (ret) {
-		pr_err("%s: Failed to request irq %d\n", __func__,
-		       WCD9XXX_IRQ_HPH_PA_OCPL_FAULT);
-		goto err_hphl_ocp_irq;
-	}
-	wcd9xxx_disable_irq(codec->control_data, WCD9XXX_IRQ_HPH_PA_OCPL_FAULT);
-
-	ret = wcd9xxx_request_irq(codec->control_data,
-				  WCD9XXX_IRQ_HPH_PA_OCPR_FAULT,
-				  taiko_hphr_ocp_irq,
-				  "HPH_R OCP detect", taiko);
-	if (ret) {
-		pr_err("%s: Failed to request irq %d\n", __func__,
-		       WCD9XXX_IRQ_HPH_PA_OCPR_FAULT);
-		goto err_hphr_ocp_irq;
-	}
-	wcd9xxx_disable_irq(codec->control_data, WCD9XXX_IRQ_HPH_PA_OCPR_FAULT);
-
-	return 0;
-
-err_hphr_ocp_irq:
-	wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_HPH_PA_OCPL_FAULT,
-			 taiko);
-err_hphl_ocp_irq:
-	wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_SLIMBUS, taiko);
-err_slimbus_irq:
-	wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_MBHC_RELEASE, taiko);
-err_release_irq:
-	wcd9xxx_free_irq(codec->control_data,
-			 WCD9XXX_IRQ_MBHC_POTENTIAL, taiko);
-err_potential_irq:
-	wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_MBHC_REMOVAL, taiko);
-err_remove_irq:
-	wcd9xxx_free_irq(codec->control_data,
-			 WCD9XXX_IRQ_MBHC_INSERTION, taiko);
-err_insert_irq:
-
+					    TAIKO_SLIM_PGD_PORT_INT_EN0 + i,
+					    0xFF);
+exit:
 	return ret;
 }
 
+int taiko_hs_detect(struct snd_soc_codec *codec,
+		    struct wcd9xxx_mbhc_config *mbhc_cfg)
+{
+	struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
+	return wcd9xxx_mbhc_start(&taiko->mbhc, mbhc_cfg);
+}
+EXPORT_SYMBOL_GPL(taiko_hs_detect);
+
+static struct wcd9xxx_reg_address taiko_reg_address = {
+	.micb_4_mbhc = TAIKO_A_MICB_4_MBHC,
+	.micb_4_int_rbias = TAIKO_A_MICB_4_INT_RBIAS,
+	.micb_4_ctl = TAIKO_A_MICB_4_CTL,
+};
+
 static int taiko_codec_probe(struct snd_soc_codec *codec)
 {
 	struct wcd9xxx *control;
 	struct taiko_priv *taiko;
+	struct wcd9xxx_pdata *pdata;
+	struct wcd9xxx *wcd9xxx;
 	struct snd_soc_dapm_context *dapm = &codec->dapm;
 	int ret = 0;
 	int i;
@@ -7918,41 +4622,34 @@
 			tx_hpf_corner_freq_callback);
 	}
 
-	/* Make sure mbhc micbias register addresses are zeroed out */
-	memset(&taiko->mbhc_bias_regs, 0,
-		sizeof(struct mbhc_micbias_regs));
-	taiko->mbhc_micbias_switched = false;
-
-	/* Make sure mbhc intenal calibration data is zeroed out */
-	memset(&taiko->mbhc_data, 0,
-		sizeof(struct mbhc_internal_cal_data));
-	taiko->mbhc_data.t_sta_dce = DEFAULT_DCE_STA_WAIT;
-	taiko->mbhc_data.t_dce = DEFAULT_DCE_WAIT;
-	taiko->mbhc_data.t_sta = DEFAULT_STA_WAIT;
 	snd_soc_codec_set_drvdata(codec, taiko);
 
-	taiko->mclk_enabled = false;
-	taiko->bandgap_type = TAIKO_BANDGAP_OFF;
-	taiko->clock_active = false;
-	taiko->config_mode_active = false;
-	taiko->mbhc_polling_active = false;
-	taiko->mbhc_fake_ins_start = 0;
-	taiko->no_mic_headset_override = false;
-	taiko->hs_polling_irq_prepared = false;
-	mutex_init(&taiko->codec_resource_lock);
+	/* codec resmgr module init */
+	wcd9xxx = codec->control_data;
+	pdata = dev_get_platdata(codec->dev->parent);
+	ret = wcd9xxx_resmgr_init(&taiko->resmgr, codec, wcd9xxx, pdata,
+				  &taiko_reg_address);
+	if (ret) {
+		pr_err("%s: wcd9xxx init failed %d\n", __func__, ret);
+		return ret;
+	}
+
+	/* init and start mbhc */
+	ret = wcd9xxx_mbhc_init(&taiko->mbhc, &taiko->resmgr, codec);
+	if (ret) {
+		pr_err("%s: mbhc init failed %d\n", __func__, ret);
+		return ret;
+	}
+
 	taiko->codec = codec;
-	taiko->mbhc_state = MBHC_STATE_NONE;
-	taiko->mbhc_last_resume = 0;
 	for (i = 0; i < COMPANDER_MAX; i++) {
 		taiko->comp_enabled[i] = 0;
 		taiko->comp_fs[i] = COMPANDER_FS_48KHZ;
 	}
-	taiko->pdata = dev_get_platdata(codec->dev->parent);
 	taiko->intf_type = wcd9xxx_get_intf_type();
 	taiko->aux_pga_cnt = 0;
 	taiko->aux_l_gain = 0x1F;
 	taiko->aux_r_gain = 0x1F;
-	taiko_update_reg_address(taiko);
 	taiko_update_reg_defaults(codec);
 	taiko_codec_init_reg(codec);
 	ret = taiko_handle_pdata(taiko);
@@ -7994,48 +4691,24 @@
 
 	(void) taiko_setup_irqs(taiko);
 
-
-#ifdef CONFIG_DEBUG_FS
-	if (ret == 0) {
-		taiko->debugfs_poke =
-		    debugfs_create_file("TRRS", S_IFREG | S_IRUGO, NULL, taiko,
-					&codec_debug_ops);
-		taiko->debugfs_mbhc =
-		    debugfs_create_file("taiko_mbhc", S_IFREG | S_IRUGO,
-					NULL, taiko, &codec_mbhc_debug_ops);
-	}
-#endif
 	codec->ignore_pmdown_time = 1;
 	return ret;
 
 err_pdata:
 	kfree(ptr);
 err_nomem_slimch:
-	mutex_destroy(&taiko->codec_resource_lock);
 	kfree(taiko);
 	return ret;
 }
 static int taiko_codec_remove(struct snd_soc_codec *codec)
 {
 	struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
-	wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_SLIMBUS, taiko);
-	wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_MBHC_RELEASE, taiko);
-	wcd9xxx_free_irq(codec->control_data,
-			 WCD9XXX_IRQ_MBHC_POTENTIAL, taiko);
-	wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_MBHC_REMOVAL, taiko);
-	wcd9xxx_free_irq(codec->control_data,
-			 WCD9XXX_IRQ_MBHC_INSERTION, taiko);
-	TAIKO_ACQUIRE_LOCK(taiko->codec_resource_lock);
-	taiko_codec_disable_clock_block(codec);
-	TAIKO_RELEASE_LOCK(taiko->codec_resource_lock);
-	taiko_codec_enable_bandgap(codec, TAIKO_BANDGAP_OFF);
-	if (taiko->mbhc_fw)
-		release_firmware(taiko->mbhc_fw);
-	mutex_destroy(&taiko->codec_resource_lock);
-#ifdef CONFIG_DEBUG_FS
-	debugfs_remove(taiko->debugfs_poke);
-	debugfs_remove(taiko->debugfs_mbhc);
-#endif
+
+	/* cleanup MBHC */
+	wcd9xxx_mbhc_deinit(&taiko->mbhc);
+	/* cleanup resmgr */
+	wcd9xxx_resmgr_deinit(&taiko->resmgr);
+
 	kfree(taiko);
 	return 0;
 }
@@ -8073,7 +4746,8 @@
 	struct platform_device *pdev = to_platform_device(dev);
 	struct taiko_priv *taiko = platform_get_drvdata(pdev);
 	dev_dbg(dev, "%s: system resume\n", __func__);
-	taiko->mbhc_last_resume = jiffies;
+	/* Notify */
+	wcd9xxx_resmgr_notifier_call(&taiko->resmgr, WCD9XXX_EVENT_POST_RESUME);
 	return 0;
 }
 
diff --git a/sound/soc/codecs/wcd9320.h b/sound/soc/codecs/wcd9320.h
index 6f4b0cf..7bc5a57 100644
--- a/sound/soc/codecs/wcd9320.h
+++ b/sound/soc/codecs/wcd9320.h
@@ -15,6 +15,8 @@
 #include <sound/soc.h>
 #include <sound/jack.h>
 #include <linux/mfd/wcd9xxx/wcd9xxx-slimslave.h>
+#include "wcd9xxx-mbhc.h"
+#include "wcd9xxx-resmgr.h"
 
 #define TAIKO_NUM_REGISTERS 0x400
 #define TAIKO_MAX_REGISTER (TAIKO_NUM_REGISTERS-1)
@@ -22,27 +24,13 @@
 
 #define TAIKO_REG_VAL(reg, val)		{reg, 0, val}
 
-#define DEFAULT_DCE_STA_WAIT 55
-#define DEFAULT_DCE_WAIT 60000
-#define DEFAULT_STA_WAIT 5000
-#define VDDIO_MICBIAS_MV 1800
-
-#define STA 0
-#define DCE 1
-
-#define TAIKO_JACK_BUTTON_MASK (SND_JACK_BTN_0 | SND_JACK_BTN_1 | \
-				SND_JACK_BTN_2 | SND_JACK_BTN_3 | \
-				SND_JACK_BTN_4 | SND_JACK_BTN_5 | \
-				SND_JACK_BTN_6 | SND_JACK_BTN_7)
-
 extern const u8 taiko_reg_readable[TAIKO_CACHE_SIZE];
 extern const u8 taiko_reset_reg_defaults[TAIKO_CACHE_SIZE];
-
-enum taiko_micbias_num {
-	TAIKO_MICBIAS1 = 0,
-	TAIKO_MICBIAS2,
-	TAIKO_MICBIAS3,
-	TAIKO_MICBIAS4,
+struct taiko_codec_dai_data {
+	u32 rate;
+	u32 *ch_num;
+	u32 ch_act;
+	u32 ch_tot;
 };
 
 enum taiko_pid_current {
@@ -58,26 +46,12 @@
 	u8	val;
 };
 
-enum taiko_mbhc_clk_freq {
-	TAIKO_MCLK_12P2MHZ = 0,
-	TAIKO_MCLK_9P6MHZ,
-	TAIKO_NUM_CLK_FREQS,
-};
-
 enum taiko_mbhc_analog_pwr_cfg {
 	TAIKO_ANALOG_PWR_COLLAPSED = 0,
 	TAIKO_ANALOG_PWR_ON,
 	TAIKO_NUM_ANALOG_PWR_CONFIGS,
 };
 
-enum taiko_mbhc_btn_det_mem {
-	TAIKO_BTN_DET_V_BTN_LOW,
-	TAIKO_BTN_DET_V_BTN_HIGH,
-	TAIKO_BTN_DET_N_READY,
-	TAIKO_BTN_DET_N_CIC,
-	TAIKO_BTN_DET_GAIN
-};
-
 /* Number of input and output Slimbus port */
 enum {
 	TAIKO_RX1 = 0,
@@ -116,110 +90,6 @@
 	TAIKO_TX_MAX,
 };
 
-struct taiko_mbhc_general_cfg {
-	u8 t_ldoh;
-	u8 t_bg_fast_settle;
-	u8 t_shutdown_plug_rem;
-	u8 mbhc_nsa;
-	u8 mbhc_navg;
-	u8 v_micbias_l;
-	u8 v_micbias;
-	u8 mbhc_reserved;
-	u16 settle_wait;
-	u16 t_micbias_rampup;
-	u16 t_micbias_rampdown;
-	u16 t_supply_bringup;
-} __packed;
-
-struct taiko_mbhc_plug_detect_cfg {
-	u32 mic_current;
-	u32 hph_current;
-	u16 t_mic_pid;
-	u16 t_ins_complete;
-	u16 t_ins_retry;
-	u16 v_removal_delta;
-	u8 micbias_slow_ramp;
-	u8 reserved0;
-	u8 reserved1;
-	u8 reserved2;
-} __packed;
-
-struct taiko_mbhc_plug_type_cfg {
-	u8 av_detect;
-	u8 mono_detect;
-	u8 num_ins_tries;
-	u8 reserved0;
-	s16 v_no_mic;
-	s16 v_av_min;
-	s16 v_av_max;
-	s16 v_hs_min;
-	s16 v_hs_max;
-	u16 reserved1;
-} __packed;
-
-
-struct taiko_mbhc_btn_detect_cfg {
-	s8 c[8];
-	u8 nc;
-	u8 n_meas;
-	u8 mbhc_nsc;
-	u8 n_btn_meas;
-	u8 n_btn_con;
-	u8 num_btn;
-	u8 reserved0;
-	u8 reserved1;
-	u16 t_poll;
-	u16 t_bounce_wait;
-	u16 t_rel_timeout;
-	s16 v_btn_press_delta_sta;
-	s16 v_btn_press_delta_cic;
-	u16 t_btn0_timeout;
-	s16 _v_btn_low[0]; /* v_btn_low[num_btn] */
-	s16 _v_btn_high[0]; /* v_btn_high[num_btn] */
-	u8 _n_ready[TAIKO_NUM_CLK_FREQS];
-	u8 _n_cic[TAIKO_NUM_CLK_FREQS];
-	u8 _gain[TAIKO_NUM_CLK_FREQS];
-} __packed;
-
-struct taiko_mbhc_imped_detect_cfg {
-	u8 _hs_imped_detect;
-	u8 _n_rload;
-	u8 _hph_keep_on;
-	u8 _repeat_rload_calc;
-	u16 _t_dac_ramp_time;
-	u16 _rhph_high;
-	u16 _rhph_low;
-	u16 _rload[0]; /* rload[n_rload] */
-	u16 _alpha[0]; /* alpha[n_rload] */
-	u16 _beta[3];
-} __packed;
-
-struct taiko_mbhc_config {
-	struct snd_soc_jack *headset_jack;
-	struct snd_soc_jack *button_jack;
-	bool read_fw_bin;
-	/* void* calibration contains:
-	 *  struct taiko_mbhc_general_cfg generic;
-	 *  struct taiko_mbhc_plug_detect_cfg plug_det;
-	 *  struct taiko_mbhc_plug_type_cfg plug_type;
-	 *  struct taiko_mbhc_btn_detect_cfg btn_det;
-	 *  struct taiko_mbhc_imped_detect_cfg imped_det;
-	 * Note: various size depends on btn_det->num_btn
-	 */
-	void *calibration;
-	enum taiko_micbias_num micbias;
-	int (*mclk_cb_fn) (struct snd_soc_codec*, int, bool);
-	unsigned int mclk_rate;
-	unsigned int gpio;
-	unsigned int gpio_irq;
-	int gpio_level_insert;
-	/* swap_gnd_mic returns true if extern GND/MIC swap switch toggled */
-	bool (*swap_gnd_mic) (struct snd_soc_codec *);
-};
-
-extern int taiko_hs_detect(struct snd_soc_codec *codec,
-			   const struct taiko_mbhc_config *cfg);
-
 struct anc_header {
 	u32 reserved[3];
 	u32 num_anc_slots;
@@ -227,64 +97,7 @@
 
 extern int taiko_mclk_enable(struct snd_soc_codec *codec, int mclk_enable,
 			     bool dapm);
-
-extern void *taiko_mbhc_cal_btn_det_mp(const struct taiko_mbhc_btn_detect_cfg
-				       *btn_det,
-				       const enum taiko_mbhc_btn_det_mem mem);
-
-#define TAIKO_MBHC_CAL_SIZE(buttons, rload) ( \
-	sizeof(enum taiko_micbias_num) + \
-	sizeof(struct taiko_mbhc_general_cfg) + \
-	sizeof(struct taiko_mbhc_plug_detect_cfg) + \
-	    ((sizeof(s16) + sizeof(s16)) * buttons) + \
-	sizeof(struct taiko_mbhc_plug_type_cfg) + \
-	sizeof(struct taiko_mbhc_btn_detect_cfg) + \
-	sizeof(struct taiko_mbhc_imped_detect_cfg) + \
-	    ((sizeof(u16) + sizeof(u16)) * rload) \
-	)
-
-#define TAIKO_MBHC_CAL_GENERAL_PTR(cali) ( \
-	    (struct taiko_mbhc_general_cfg *) cali)
-#define TAIKO_MBHC_CAL_PLUG_DET_PTR(cali) ( \
-	    (struct taiko_mbhc_plug_detect_cfg *) \
-	    &(TAIKO_MBHC_CAL_GENERAL_PTR(cali)[1]))
-#define TAIKO_MBHC_CAL_PLUG_TYPE_PTR(cali) ( \
-	    (struct taiko_mbhc_plug_type_cfg *) \
-	    &(TAIKO_MBHC_CAL_PLUG_DET_PTR(cali)[1]))
-#define TAIKO_MBHC_CAL_BTN_DET_PTR(cali) ( \
-	    (struct taiko_mbhc_btn_detect_cfg *) \
-	    &(TAIKO_MBHC_CAL_PLUG_TYPE_PTR(cali)[1]))
-#define TAIKO_MBHC_CAL_IMPED_DET_PTR(cali) ( \
-	    (struct taiko_mbhc_imped_detect_cfg *) \
-	    (((void *)&TAIKO_MBHC_CAL_BTN_DET_PTR(cali)[1]) + \
-	     (TAIKO_MBHC_CAL_BTN_DET_PTR(cali)->num_btn * \
-	      (sizeof(TAIKO_MBHC_CAL_BTN_DET_PTR(cali)->_v_btn_low[0]) + \
-	       sizeof(TAIKO_MBHC_CAL_BTN_DET_PTR(cali)->_v_btn_high[0])))) \
-	)
-
-/* minimum size of calibration data assuming there is only one button and
- * one rload.
- */
-#define TAIKO_MBHC_CAL_MIN_SIZE ( \
-	sizeof(struct taiko_mbhc_general_cfg) + \
-	sizeof(struct taiko_mbhc_plug_detect_cfg) + \
-	sizeof(struct taiko_mbhc_plug_type_cfg) + \
-	sizeof(struct taiko_mbhc_btn_detect_cfg) + \
-	sizeof(struct taiko_mbhc_imped_detect_cfg) + \
-	(sizeof(u16) * 2))
-
-#define TAIKO_MBHC_CAL_BTN_SZ(cfg_ptr) ( \
-	    sizeof(struct taiko_mbhc_btn_detect_cfg) + \
-	    (cfg_ptr->num_btn * (sizeof(cfg_ptr->_v_btn_low[0]) + \
-				 sizeof(cfg_ptr->_v_btn_high[0]))))
-
-#define TAIKO_MBHC_CAL_IMPED_MIN_SZ ( \
-	    sizeof(struct taiko_mbhc_imped_detect_cfg) + \
-	    sizeof(u16) * 2)
-
-#define TAIKO_MBHC_CAL_IMPED_SZ(cfg_ptr) ( \
-	    sizeof(struct taiko_mbhc_imped_detect_cfg) + \
-	    (cfg_ptr->_n_rload * (sizeof(cfg_ptr->_rload[0]) + \
-				 sizeof(cfg_ptr->_alpha[0]))))
+extern int taiko_hs_detect(struct snd_soc_codec *codec,
+			   struct wcd9xxx_mbhc_config *mbhc_cfg);
 
 #endif
diff --git a/sound/soc/codecs/wcd9xxx-mbhc.c b/sound/soc/codecs/wcd9xxx-mbhc.c
new file mode 100644
index 0000000..16d415b
--- /dev/null
+++ b/sound/soc/codecs/wcd9xxx-mbhc.c
@@ -0,0 +1,3113 @@
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/firmware.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/device.h>
+#include <linux/printk.h>
+#include <linux/ratelimit.h>
+#include <linux/debugfs.h>
+#include <linux/mfd/wcd9xxx/core.h>
+#include <linux/mfd/wcd9xxx/wcd9xxx_registers.h>
+#include <linux/mfd/wcd9xxx/wcd9320_registers.h>
+#include <linux/mfd/wcd9xxx/pdata.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include <linux/pm_runtime.h>
+#include <linux/kernel.h>
+#include <linux/gpio.h>
+#include "wcd9320.h"
+#include "wcd9xxx-mbhc.h"
+#include "wcd9xxx-resmgr.h"
+
+#define WCD9XXX_JACK_MASK (SND_JACK_HEADSET | SND_JACK_OC_HPHL | \
+			   SND_JACK_OC_HPHR | SND_JACK_UNSUPPORTED)
+#define WCD9XXX_JACK_BUTTON_MASK (SND_JACK_BTN_0 | SND_JACK_BTN_1 | \
+				  SND_JACK_BTN_2 | SND_JACK_BTN_3 | \
+				  SND_JACK_BTN_4 | SND_JACK_BTN_5 | \
+				  SND_JACK_BTN_6 | SND_JACK_BTN_7)
+
+#define NUM_DCE_PLUG_DETECT 3
+#define NUM_ATTEMPTS_INSERT_DETECT 25
+#define NUM_ATTEMPTS_TO_REPORT 5
+
+#define FAKE_INS_LOW 10
+#define FAKE_INS_HIGH 80
+#define FAKE_INS_HIGH_NO_SWCH 150
+#define FAKE_REMOVAL_MIN_PERIOD_MS 50
+#define FAKE_INS_DELTA_SCALED_MV 300
+
+#define BUTTON_MIN 0x8000
+#define STATUS_REL_DETECTION 0x0C
+
+#define HS_DETECT_PLUG_TIME_MS (5 * 1000)
+#define HS_DETECT_PLUG_INERVAL_MS 100
+#define SWCH_REL_DEBOUNCE_TIME_MS 50
+#define SWCH_IRQ_DEBOUNCE_TIME_US 5000
+
+#define GND_MIC_SWAP_THRESHOLD 2
+#define OCP_ATTEMPT 1
+
+#define FW_READ_ATTEMPTS 15
+#define FW_READ_TIMEOUT 2000000
+
+#define BUTTON_POLLING_SUPPORTED false
+
+#define MCLK_RATE_12288KHZ 12288000
+#define MCLK_RATE_9600KHZ 9600000
+#define WCD9XXX_RCO_CLK_RATE MCLK_RATE_12288KHZ
+
+#define DEFAULT_DCE_STA_WAIT 55
+#define DEFAULT_DCE_WAIT 60000
+#define DEFAULT_STA_WAIT 5000
+
+#define VDDIO_MICBIAS_MV 1800
+
+enum meas_type {
+	STA = 0,
+	DCE,
+};
+
+enum {
+	MBHC_USE_HPHL_TRIGGER = 1,
+	MBHC_USE_MB_TRIGGER = 2
+};
+
+/*
+ * Flags to track of PA and DAC state.
+ * PA and DAC should be tracked separately as AUXPGA loopback requires
+ * only PA to be turned on without DAC being on.
+ */
+enum pa_dac_ack_flags {
+	WCD9XXX_HPHL_PA_OFF_ACK = 0,
+	WCD9XXX_HPHR_PA_OFF_ACK,
+	WCD9XXX_HPHL_DAC_OFF_ACK,
+	WCD9XXX_HPHR_DAC_OFF_ACK
+};
+
+static bool wcd9xxx_mbhc_polling(struct wcd9xxx_mbhc *mbhc)
+{
+	return mbhc->polling_active;
+}
+
+static void wcd9xxx_turn_onoff_override(struct snd_soc_codec *codec, bool on)
+{
+	snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x04, on << 2);
+}
+
+/* called under codec_resource_lock acquisition */
+static void wcd9xxx_pause_hs_polling(struct wcd9xxx_mbhc *mbhc)
+{
+	struct snd_soc_codec *codec = mbhc->codec;
+
+	pr_debug("%s: enter\n", __func__);
+	if (!mbhc->polling_active) {
+		pr_debug("polling not active, nothing to pause\n");
+		return;
+	}
+
+	/* Soft reset MBHC block */
+	snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8, 0x8);
+	pr_debug("%s: leave\n", __func__);
+}
+
+/* called under codec_resource_lock acquisition */
+static void wcd9xxx_start_hs_polling(struct wcd9xxx_mbhc *mbhc)
+{
+	struct snd_soc_codec *codec = mbhc->codec;
+	int mbhc_state = mbhc->mbhc_state;
+
+	pr_debug("%s: enter\n", __func__);
+	if (!mbhc->polling_active) {
+		pr_debug("Polling is not active, do not start polling\n");
+		return;
+	}
+	snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x84);
+
+	if (!mbhc->no_mic_headset_override &&
+	    mbhc_state == MBHC_STATE_POTENTIAL) {
+		pr_debug("%s recovering MBHC state macine\n", __func__);
+		mbhc->mbhc_state = MBHC_STATE_POTENTIAL_RECOVERY;
+		/* set to max button press threshold */
+		snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B2_CTL, 0x7F);
+		snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B1_CTL, 0xFF);
+		snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B4_CTL, 0x7F);
+		snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B3_CTL, 0xFF);
+		/* set to max */
+		snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B6_CTL, 0x7F);
+		snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B5_CTL, 0xFF);
+	}
+
+	snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x1);
+	snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8, 0x0);
+	snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x1);
+	pr_debug("%s: leave\n", __func__);
+}
+
+/* called under codec_resource_lock acquisition */
+static void __wcd9xxx_switch_micbias(struct wcd9xxx_mbhc *mbhc,
+				     int vddio_switch, bool restartpolling,
+				     bool checkpolling)
+{
+	int cfilt_k_val;
+	bool override;
+	struct snd_soc_codec *codec;
+
+	codec = mbhc->codec;
+
+	if (vddio_switch && !mbhc->mbhc_micbias_switched &&
+	    (!checkpolling || mbhc->polling_active)) {
+		if (restartpolling)
+			wcd9xxx_pause_hs_polling(mbhc);
+		override = snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B1_CTL) &
+			   0x04;
+		if (!override)
+			wcd9xxx_turn_onoff_override(codec, true);
+		/* Adjust threshold if Mic Bias voltage changes */
+		if (mbhc->mbhc_data.micb_mv != VDDIO_MICBIAS_MV) {
+			cfilt_k_val = wcd9xxx_resmgr_get_k_val(mbhc->resmgr,
+							      VDDIO_MICBIAS_MV);
+			usleep_range(10000, 10000);
+			snd_soc_update_bits(codec,
+					mbhc->mbhc_bias_regs.cfilt_val,
+					0xFC, (cfilt_k_val << 2));
+			usleep_range(10000, 10000);
+			snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B1_CTL,
+				      mbhc->mbhc_data.adj_v_ins_hu & 0xFF);
+			snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B2_CTL,
+				      (mbhc->mbhc_data.adj_v_ins_hu >> 8) &
+				      0xFF);
+			pr_debug("%s: Programmed MBHC thresholds to VDDIO\n",
+				 __func__);
+		}
+
+		/* Enable MIC BIAS Switch to VDDIO */
+		snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg,
+				    0x80, 0x80);
+		snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg,
+				    0x10, 0x00);
+		if (!override)
+			wcd9xxx_turn_onoff_override(codec, false);
+		if (restartpolling)
+			wcd9xxx_start_hs_polling(mbhc);
+
+		mbhc->mbhc_micbias_switched = true;
+		pr_debug("%s: VDDIO switch enabled\n", __func__);
+	} else if (!vddio_switch && mbhc->mbhc_micbias_switched) {
+		if ((!checkpolling || mbhc->polling_active) &&
+		    restartpolling)
+			wcd9xxx_pause_hs_polling(mbhc);
+		/* Reprogram thresholds */
+		if (mbhc->mbhc_data.micb_mv != VDDIO_MICBIAS_MV) {
+			cfilt_k_val =
+			    wcd9xxx_resmgr_get_k_val(mbhc->resmgr,
+						     mbhc->mbhc_data.micb_mv);
+			snd_soc_update_bits(codec,
+					mbhc->mbhc_bias_regs.cfilt_val,
+					0xFC, (cfilt_k_val << 2));
+			usleep_range(10000, 10000);
+			snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B1_CTL,
+					mbhc->mbhc_data.v_ins_hu & 0xFF);
+			snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B2_CTL,
+					(mbhc->mbhc_data.v_ins_hu >> 8) & 0xFF);
+			pr_debug("%s: Programmed MBHC thresholds to MICBIAS\n",
+					__func__);
+		}
+
+		/* Disable MIC BIAS Switch to VDDIO */
+		snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg, 0x80,
+				    0x00);
+		snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg, 0x10,
+				    0x00);
+
+		if ((!checkpolling || mbhc->polling_active) && restartpolling)
+			wcd9xxx_start_hs_polling(mbhc);
+
+		mbhc->mbhc_micbias_switched = false;
+		pr_debug("%s: VDDIO switch disabled\n", __func__);
+	}
+}
+
+static void wcd9xxx_switch_micbias(struct wcd9xxx_mbhc *mbhc, int vddio_switch)
+{
+	return __wcd9xxx_switch_micbias(mbhc, vddio_switch, true, true);
+}
+
+static s16 wcd9xxx_get_current_v_ins(struct wcd9xxx_mbhc *mbhc, bool hu)
+{
+	s16 v_ins;
+	if ((mbhc->mbhc_data.micb_mv != VDDIO_MICBIAS_MV) &&
+	    mbhc->mbhc_micbias_switched)
+		v_ins = hu ? (s16)mbhc->mbhc_data.adj_v_ins_hu :
+			(s16)mbhc->mbhc_data.adj_v_ins_h;
+	else
+		v_ins = hu ? (s16)mbhc->mbhc_data.v_ins_hu :
+			(s16)mbhc->mbhc_data.v_ins_h;
+	return v_ins;
+}
+
+void *wcd9xxx_mbhc_cal_btn_det_mp(
+			    const struct wcd9xxx_mbhc_btn_detect_cfg *btn_det,
+			    const enum wcd9xxx_mbhc_btn_det_mem mem)
+{
+	void *ret = &btn_det->_v_btn_low;
+
+	switch (mem) {
+	case MBHC_BTN_DET_GAIN:
+		ret += sizeof(btn_det->_n_cic);
+	case MBHC_BTN_DET_N_CIC:
+		ret += sizeof(btn_det->_n_ready);
+	case MBHC_BTN_DET_N_READY:
+		ret += sizeof(btn_det->_v_btn_high[0]) * btn_det->num_btn;
+	case MBHC_BTN_DET_V_BTN_HIGH:
+		ret += sizeof(btn_det->_v_btn_low[0]) * btn_det->num_btn;
+	case MBHC_BTN_DET_V_BTN_LOW:
+		/* do nothing */
+		break;
+	default:
+		ret = NULL;
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(wcd9xxx_mbhc_cal_btn_det_mp);
+
+static void wcd9xxx_calibrate_hs_polling(struct wcd9xxx_mbhc *mbhc)
+{
+	struct snd_soc_codec *codec = mbhc->codec;
+	const s16 v_ins_hu = wcd9xxx_get_current_v_ins(mbhc, true);
+
+	snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B1_CTL, v_ins_hu & 0xFF);
+	snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B2_CTL,
+		      (v_ins_hu >> 8) & 0xFF);
+
+	snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B3_CTL,
+		      mbhc->mbhc_data.v_b1_hu & 0xFF);
+	snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B4_CTL,
+		      (mbhc->mbhc_data.v_b1_hu >> 8) & 0xFF);
+
+	snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B5_CTL,
+		      mbhc->mbhc_data.v_b1_h & 0xFF);
+	snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B6_CTL,
+		      (mbhc->mbhc_data.v_b1_h >> 8) & 0xFF);
+
+	snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B9_CTL,
+		      mbhc->mbhc_data.v_brh & 0xFF);
+	snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B10_CTL,
+		      (mbhc->mbhc_data.v_brh >> 8) & 0xFF);
+
+	snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B11_CTL,
+		      mbhc->mbhc_data.v_brl & 0xFF);
+	snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B12_CTL,
+		      (mbhc->mbhc_data.v_brl >> 8) & 0xFF);
+}
+
+static void wcd9xxx_codec_switch_cfilt_mode(struct wcd9xxx_mbhc *mbhc,
+					    bool fast)
+{
+	struct snd_soc_codec *codec = mbhc->codec;
+	u8 reg_mode_val, cur_mode_val;
+
+	if (fast)
+		reg_mode_val = WCD9XXX_CFILT_FAST_MODE;
+	else
+		reg_mode_val = WCD9XXX_CFILT_SLOW_MODE;
+
+	cur_mode_val =
+	    snd_soc_read(codec, mbhc->mbhc_bias_regs.cfilt_ctl) & 0x40;
+
+	if (cur_mode_val != reg_mode_val) {
+		if (mbhc->polling_active)
+			wcd9xxx_pause_hs_polling(mbhc);
+		snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.cfilt_ctl, 0x40,
+				    reg_mode_val);
+		if (mbhc->polling_active)
+			wcd9xxx_start_hs_polling(mbhc);
+		pr_debug("%s: CFILT mode change (%x to %x)\n", __func__,
+			cur_mode_val, reg_mode_val);
+	} else {
+		pr_debug("%s: CFILT Value is already %x\n",
+			__func__, cur_mode_val);
+	}
+}
+
+static void wcd9xxx_jack_report(struct snd_soc_jack *jack, int status, int mask)
+{
+	snd_soc_jack_report_no_dapm(jack, status, mask);
+}
+
+static void __hphocp_off_report(struct wcd9xxx_mbhc *mbhc, u32 jack_status,
+				int irq)
+{
+	struct snd_soc_codec *codec;
+
+	pr_debug("%s: clear ocp status %x\n", __func__, jack_status);
+	codec = mbhc->codec;
+	if (mbhc->hph_status & jack_status) {
+		mbhc->hph_status &= ~jack_status;
+		wcd9xxx_jack_report(&mbhc->headset_jack,
+				    mbhc->hph_status, WCD9XXX_JACK_MASK);
+		snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_OCP_CTL, 0x10,
+				    0x00);
+		snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_OCP_CTL, 0x10,
+				    0x10);
+		/*
+		 * reset retry counter as PA is turned off signifying
+		 * start of new OCP detection session
+		 */
+		if (WCD9XXX_IRQ_HPH_PA_OCPL_FAULT)
+			mbhc->hphlocp_cnt = 0;
+		else
+			mbhc->hphrocp_cnt = 0;
+		wcd9xxx_enable_irq(codec->control_data, irq);
+	}
+}
+
+static void hphrocp_off_report(struct wcd9xxx_mbhc *mbhc, u32 jack_status)
+{
+	__hphocp_off_report(mbhc, SND_JACK_OC_HPHR,
+			    WCD9XXX_IRQ_HPH_PA_OCPR_FAULT);
+}
+
+static void hphlocp_off_report(struct wcd9xxx_mbhc *mbhc, u32 jack_status)
+{
+	__hphocp_off_report(mbhc, SND_JACK_OC_HPHL,
+			    WCD9XXX_IRQ_HPH_PA_OCPL_FAULT);
+}
+
+static void wcd9xxx_get_mbhc_micbias_regs(struct wcd9xxx_mbhc *mbhc,
+					struct mbhc_micbias_regs *micbias_regs)
+{
+	unsigned int cfilt;
+	struct wcd9xxx_pdata *pdata = mbhc->resmgr->pdata;
+
+	switch (mbhc->mbhc_cfg->micbias) {
+	case MBHC_MICBIAS1:
+		cfilt = pdata->micbias.bias1_cfilt_sel;
+		micbias_regs->mbhc_reg = WCD9XXX_A_MICB_1_MBHC;
+		micbias_regs->int_rbias = WCD9XXX_A_MICB_1_INT_RBIAS;
+		micbias_regs->ctl_reg = WCD9XXX_A_MICB_1_CTL;
+		break;
+	case MBHC_MICBIAS2:
+		cfilt = pdata->micbias.bias2_cfilt_sel;
+		micbias_regs->mbhc_reg = WCD9XXX_A_MICB_2_MBHC;
+		micbias_regs->int_rbias = WCD9XXX_A_MICB_2_INT_RBIAS;
+		micbias_regs->ctl_reg = WCD9XXX_A_MICB_2_CTL;
+		break;
+	case MBHC_MICBIAS3:
+		cfilt = pdata->micbias.bias3_cfilt_sel;
+		micbias_regs->mbhc_reg = WCD9XXX_A_MICB_3_MBHC;
+		micbias_regs->int_rbias = WCD9XXX_A_MICB_3_INT_RBIAS;
+		micbias_regs->ctl_reg = WCD9XXX_A_MICB_3_CTL;
+		break;
+	case MBHC_MICBIAS4:
+		cfilt = pdata->micbias.bias4_cfilt_sel;
+		micbias_regs->mbhc_reg = mbhc->resmgr->reg_addr->micb_4_mbhc;
+		micbias_regs->int_rbias =
+		    mbhc->resmgr->reg_addr->micb_4_int_rbias;
+		micbias_regs->ctl_reg = mbhc->resmgr->reg_addr->micb_4_ctl;
+		break;
+	default:
+		/* Should never reach here */
+		pr_err("%s: Invalid MIC BIAS for MBHC\n", __func__);
+		return;
+	}
+
+	micbias_regs->cfilt_sel = cfilt;
+
+	switch (cfilt) {
+	case WCD9XXX_CFILT1_SEL:
+		micbias_regs->cfilt_val = WCD9XXX_A_MICB_CFILT_1_VAL;
+		micbias_regs->cfilt_ctl = WCD9XXX_A_MICB_CFILT_1_CTL;
+		mbhc->mbhc_data.micb_mv =
+		    mbhc->resmgr->pdata->micbias.cfilt1_mv;
+		break;
+	case WCD9XXX_CFILT2_SEL:
+		micbias_regs->cfilt_val = WCD9XXX_A_MICB_CFILT_2_VAL;
+		micbias_regs->cfilt_ctl = WCD9XXX_A_MICB_CFILT_2_CTL;
+		mbhc->mbhc_data.micb_mv =
+		    mbhc->resmgr->pdata->micbias.cfilt2_mv;
+		break;
+	case WCD9XXX_CFILT3_SEL:
+		micbias_regs->cfilt_val = WCD9XXX_A_MICB_CFILT_3_VAL;
+		micbias_regs->cfilt_ctl = WCD9XXX_A_MICB_CFILT_3_CTL;
+		mbhc->mbhc_data.micb_mv =
+		    mbhc->resmgr->pdata->micbias.cfilt3_mv;
+		break;
+	}
+}
+
+static void wcd9xxx_clr_and_turnon_hph_padac(struct wcd9xxx_mbhc *mbhc)
+{
+	bool pa_turned_on = false;
+	struct snd_soc_codec *codec = mbhc->codec;
+	u8 wg_time;
+
+	wg_time = snd_soc_read(codec, WCD9XXX_A_RX_HPH_CNP_WG_TIME) ;
+	wg_time += 1;
+
+	if (test_and_clear_bit(WCD9XXX_HPHR_DAC_OFF_ACK,
+			       &mbhc->hph_pa_dac_state)) {
+		pr_debug("%s: HPHR clear flag and enable DAC\n", __func__);
+		snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_R_DAC_CTL,
+				    0xC0, 0xC0);
+	}
+	if (test_and_clear_bit(WCD9XXX_HPHL_DAC_OFF_ACK,
+				&mbhc->hph_pa_dac_state)) {
+		pr_debug("%s: HPHL clear flag and enable DAC\n", __func__);
+		snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_L_DAC_CTL,
+				    0xC0, 0xC0);
+	}
+
+	if (test_and_clear_bit(WCD9XXX_HPHR_PA_OFF_ACK,
+			       &mbhc->hph_pa_dac_state)) {
+		pr_debug("%s: HPHR clear flag and enable PA\n", __func__);
+		snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_CNP_EN, 0x10,
+				    1 << 4);
+		pa_turned_on = true;
+	}
+	if (test_and_clear_bit(WCD9XXX_HPHL_PA_OFF_ACK,
+			       &mbhc->hph_pa_dac_state)) {
+		pr_debug("%s: HPHL clear flag and enable PA\n", __func__);
+		snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_CNP_EN, 0x20, 1
+				    << 5);
+		pa_turned_on = true;
+	}
+
+	if (pa_turned_on) {
+		pr_debug("%s: PA was turned off by MBHC and not by DAPM\n",
+			 __func__);
+		usleep_range(wg_time * 1000, wg_time * 1000);
+	}
+}
+
+static int wcd9xxx_cancel_btn_work(struct wcd9xxx_mbhc *mbhc)
+{
+	int r;
+	r = cancel_delayed_work_sync(&mbhc->mbhc_btn_dwork);
+	if (r)
+		/* if scheduled mbhc.mbhc_btn_dwork is canceled from here,
+		 * we have to unlock from here instead btn_work */
+		wcd9xxx_unlock_sleep(mbhc->resmgr->core);
+	return r;
+}
+
+static bool wcd9xxx_is_hph_dac_on(struct snd_soc_codec *codec, int left)
+{
+	u8 hph_reg_val = 0;
+	if (left)
+		hph_reg_val = snd_soc_read(codec, WCD9XXX_A_RX_HPH_L_DAC_CTL);
+	else
+		hph_reg_val = snd_soc_read(codec, WCD9XXX_A_RX_HPH_R_DAC_CTL);
+
+	return (hph_reg_val & 0xC0) ? true : false;
+}
+
+static bool wcd9xxx_is_hph_pa_on(struct snd_soc_codec *codec)
+{
+	u8 hph_reg_val = 0;
+	hph_reg_val = snd_soc_read(codec, WCD9XXX_A_RX_HPH_CNP_EN);
+
+	return (hph_reg_val & 0x30) ? true : false;
+}
+
+/* called under codec_resource_lock acquisition */
+static void wcd9xxx_set_and_turnoff_hph_padac(struct wcd9xxx_mbhc *mbhc)
+{
+	u8 wg_time;
+	struct snd_soc_codec *codec = mbhc->codec;
+
+	wg_time = snd_soc_read(codec, WCD9XXX_A_RX_HPH_CNP_WG_TIME);
+	wg_time += 1;
+
+	/* If headphone PA is on, check if userspace receives
+	 * removal event to sync-up PA's state */
+	if (wcd9xxx_is_hph_pa_on(codec)) {
+		pr_debug("%s PA is on, setting PA_OFF_ACK\n", __func__);
+		set_bit(WCD9XXX_HPHL_PA_OFF_ACK, &mbhc->hph_pa_dac_state);
+		set_bit(WCD9XXX_HPHR_PA_OFF_ACK, &mbhc->hph_pa_dac_state);
+	} else {
+		pr_debug("%s PA is off\n", __func__);
+	}
+
+	if (wcd9xxx_is_hph_dac_on(codec, 1))
+		set_bit(WCD9XXX_HPHL_DAC_OFF_ACK, &mbhc->hph_pa_dac_state);
+	if (wcd9xxx_is_hph_dac_on(codec, 0))
+		set_bit(WCD9XXX_HPHR_DAC_OFF_ACK, &mbhc->hph_pa_dac_state);
+
+	snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_CNP_EN, 0x30, 0x00);
+	snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_L_DAC_CTL, 0xC0, 0x00);
+	snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_R_DAC_CTL, 0xC0, 0x00);
+	usleep_range(wg_time * 1000, wg_time * 1000);
+}
+
+static void wcd9xxx_insert_detect_setup(struct wcd9xxx_mbhc *mbhc, bool ins)
+{
+	if (!mbhc->mbhc_cfg->insert_detect)
+		return;
+	pr_debug("%s: Setting up %s detection\n", __func__,
+		 ins ? "insert" : "removal");
+	/* Enable interrupt and insertion detection */
+	snd_soc_write(mbhc->codec, WCD9XXX_A_MBHC_INSERT_DETECT,
+		      (0x69 | (ins ? (1 << 1) : 0)));
+}
+
+/* called under codec_resource_lock acquisition */
+static void wcd9xxx_report_plug(struct wcd9xxx_mbhc *mbhc, int insertion,
+				enum snd_jack_types jack_type)
+{
+	WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
+
+	if (!insertion) {
+		/* Report removal */
+		mbhc->hph_status &= ~jack_type;
+		/*
+		 * cancel possibly scheduled btn work and
+		 * report release if we reported button press
+		 */
+		if (wcd9xxx_cancel_btn_work(mbhc))
+			pr_debug("%s: button press is canceled\n", __func__);
+		else if (mbhc->buttons_pressed) {
+			pr_debug("%s: release of button press%d\n",
+				 __func__, jack_type);
+			wcd9xxx_jack_report(&mbhc->button_jack, 0,
+					    mbhc->buttons_pressed);
+			mbhc->buttons_pressed &=
+				~WCD9XXX_JACK_BUTTON_MASK;
+		}
+		pr_debug("%s: Reporting removal %d(%x)\n", __func__,
+			 jack_type, mbhc->hph_status);
+		wcd9xxx_jack_report(&mbhc->headset_jack, mbhc->hph_status,
+				    WCD9XXX_JACK_MASK);
+		wcd9xxx_set_and_turnoff_hph_padac(mbhc);
+		hphrocp_off_report(mbhc, SND_JACK_OC_HPHR);
+		hphlocp_off_report(mbhc, SND_JACK_OC_HPHL);
+		mbhc->current_plug = PLUG_TYPE_NONE;
+		mbhc->polling_active = false;
+	} else {
+		/* Report insertion */
+		mbhc->hph_status |= jack_type;
+
+		if (jack_type == SND_JACK_HEADPHONE) {
+			mbhc->current_plug = PLUG_TYPE_HEADPHONE;
+		} else if (jack_type == SND_JACK_UNSUPPORTED) {
+			mbhc->current_plug = PLUG_TYPE_GND_MIC_SWAP;
+		} else if (jack_type == SND_JACK_HEADSET) {
+			mbhc->polling_active = BUTTON_POLLING_SUPPORTED;
+			mbhc->current_plug = PLUG_TYPE_HEADSET;
+		}
+		pr_debug("%s: Reporting insertion %d(%x)\n", __func__,
+			 jack_type, mbhc->hph_status);
+		wcd9xxx_jack_report(&mbhc->headset_jack,
+				    mbhc->hph_status, WCD9XXX_JACK_MASK);
+		wcd9xxx_clr_and_turnon_hph_padac(mbhc);
+	}
+	/* Setup insert detect */
+	wcd9xxx_insert_detect_setup(mbhc, !insertion);
+}
+
+/* should be called under interrupt context that hold suspend */
+static void wcd9xxx_schedule_hs_detect_plug(struct wcd9xxx_mbhc *mbhc,
+					    struct work_struct *work)
+{
+	pr_debug("%s: scheduling wcd9xxx_correct_swch_plug\n", __func__);
+	WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
+	mbhc->hs_detect_work_stop = false;
+	wcd9xxx_lock_sleep(mbhc->resmgr->core);
+	schedule_work(work);
+}
+
+/* called under codec_resource_lock acquisition */
+static void wcd9xxx_cancel_hs_detect_plug(struct wcd9xxx_mbhc *mbhc,
+					 struct work_struct *work)
+{
+	pr_debug("%s: Canceling correct_plug_swch\n", __func__);
+	WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
+	mbhc->hs_detect_work_stop = true;
+	wmb();
+	WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
+	if (cancel_work_sync(work)) {
+		pr_debug("%s: correct_plug_swch is canceled\n",
+			 __func__);
+		wcd9xxx_unlock_sleep(mbhc->resmgr->core);
+	}
+	WCD9XXX_BCL_LOCK(mbhc->resmgr);
+}
+
+static s16 wcd9xxx_get_current_v_hs_max(struct wcd9xxx_mbhc *mbhc)
+{
+	s16 v_hs_max;
+	struct wcd9xxx_mbhc_plug_type_cfg *plug_type;
+
+	plug_type = WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(mbhc->mbhc_cfg->calibration);
+	if ((mbhc->mbhc_data.micb_mv != VDDIO_MICBIAS_MV) &&
+	    mbhc->mbhc_micbias_switched)
+		v_hs_max = mbhc->mbhc_data.adj_v_hs_max;
+	else
+		v_hs_max = plug_type->v_hs_max;
+	return v_hs_max;
+}
+
+static bool wcd9xxx_is_inval_ins_range(struct wcd9xxx_mbhc *mbhc,
+				     s32 mic_volt, bool highhph, bool *highv)
+{
+	s16 v_hs_max;
+	bool invalid = false;
+
+	/* Perform this check only when the high voltage headphone
+	 * needs to be considered as invalid
+	 */
+	v_hs_max = wcd9xxx_get_current_v_hs_max(mbhc);
+	*highv = mic_volt > v_hs_max;
+	if (!highhph && *highv)
+		invalid = true;
+	else if (mic_volt < mbhc->mbhc_data.v_inval_ins_high &&
+		 (mic_volt > mbhc->mbhc_data.v_inval_ins_low))
+		invalid = true;
+
+	return invalid;
+}
+
+static short wcd9xxx_read_sta_result(struct snd_soc_codec *codec)
+{
+	u8 bias_msb, bias_lsb;
+	short bias_value;
+
+	bias_msb = snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B3_STATUS);
+	bias_lsb = snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B2_STATUS);
+	bias_value = (bias_msb << 8) | bias_lsb;
+	return bias_value;
+}
+
+static short wcd9xxx_read_dce_result(struct snd_soc_codec *codec)
+{
+	u8 bias_msb, bias_lsb;
+	short bias_value;
+
+	bias_msb = snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B5_STATUS);
+	bias_lsb = snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B4_STATUS);
+	bias_value = (bias_msb << 8) | bias_lsb;
+	return bias_value;
+}
+
+static void wcd9xxx_turn_onoff_rel_detection(struct snd_soc_codec *codec,
+					     bool on)
+{
+	snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x02, on << 1);
+}
+
+static short __wcd9xxx_codec_sta_dce(struct wcd9xxx_mbhc *mbhc, int dce,
+				     bool override_bypass, bool noreldetection)
+{
+	short bias_value;
+	struct snd_soc_codec *codec = mbhc->codec;
+
+	wcd9xxx_disable_irq(mbhc->resmgr->core, WCD9XXX_IRQ_MBHC_POTENTIAL);
+	if (noreldetection)
+		wcd9xxx_turn_onoff_rel_detection(codec, false);
+
+	/* Turn on the override */
+	if (!override_bypass)
+		snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x4, 0x4);
+	if (dce) {
+		snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8,
+				    0x8);
+		snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x4);
+		snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8,
+				    0x0);
+		usleep_range(mbhc->mbhc_data.t_sta_dce,
+			     mbhc->mbhc_data.t_sta_dce);
+		snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x4);
+		usleep_range(mbhc->mbhc_data.t_dce, mbhc->mbhc_data.t_dce);
+		bias_value = wcd9xxx_read_dce_result(codec);
+	} else {
+		snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8,
+				    0x8);
+		snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x2);
+		snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8,
+				    0x0);
+		usleep_range(mbhc->mbhc_data.t_sta_dce,
+			     mbhc->mbhc_data.t_sta_dce);
+		snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x2);
+		usleep_range(mbhc->mbhc_data.t_sta,
+			     mbhc->mbhc_data.t_sta);
+		bias_value = wcd9xxx_read_sta_result(codec);
+		snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8,
+				    0x8);
+		snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x0);
+	}
+	/* Turn off the override after measuring mic voltage */
+	if (!override_bypass)
+		snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x04,
+				    0x00);
+
+	if (noreldetection)
+		wcd9xxx_turn_onoff_rel_detection(codec, true);
+	wcd9xxx_enable_irq(mbhc->resmgr->core, WCD9XXX_IRQ_MBHC_POTENTIAL);
+
+	return bias_value;
+}
+
+static short wcd9xxx_codec_sta_dce(struct wcd9xxx_mbhc *mbhc, int dce,
+				   bool norel)
+{
+	return __wcd9xxx_codec_sta_dce(mbhc, dce, false, norel);
+}
+
+static s32 wcd9xxx_codec_sta_dce_v(struct wcd9xxx_mbhc *mbhc, s8 dce,
+				   u16 bias_value)
+{
+	s16 value, z, mb;
+	s32 mv;
+
+	value = bias_value;
+	if (dce) {
+		z = (mbhc->mbhc_data.dce_z);
+		mb = (mbhc->mbhc_data.dce_mb);
+		mv = (value - z) * (s32)mbhc->mbhc_data.micb_mv / (mb - z);
+	} else {
+		z = (mbhc->mbhc_data.sta_z);
+		mb = (mbhc->mbhc_data.sta_mb);
+		mv = (value - z) * (s32)mbhc->mbhc_data.micb_mv / (mb - z);
+	}
+
+	return mv;
+}
+
+/* called only from interrupt which is under codec_resource_lock acquisition */
+static short wcd9xxx_mbhc_setup_hs_polling(struct wcd9xxx_mbhc *mbhc)
+{
+	struct snd_soc_codec *codec = mbhc->codec;
+	short bias_value;
+	u8 cfilt_mode;
+
+	WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
+
+	pr_debug("%s: enter\n", __func__);
+	if (!mbhc->mbhc_cfg->calibration) {
+		pr_err("%s: Error, no calibration exists\n", __func__);
+		return -ENODEV;
+	}
+
+	/*
+	 * Request BG and clock.
+	 * These will be released by wcd9xxx_cleanup_hs_polling
+	 */
+	wcd9xxx_resmgr_get_bandgap(mbhc->resmgr, WCD9XXX_BANDGAP_MBHC_MODE);
+	wcd9xxx_resmgr_get_clk_block(mbhc->resmgr, WCD9XXX_CLK_RCO);
+
+	snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN1, 0x05, 0x01);
+
+	/* Make sure CFILT is in fast mode, save current mode */
+	cfilt_mode = snd_soc_read(codec, mbhc->mbhc_bias_regs.cfilt_ctl);
+	snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.cfilt_ctl, 0x70, 0x00);
+
+	snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x1F, 0x16);
+
+	snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x2, 0x2);
+	snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x84);
+
+	snd_soc_update_bits(codec, WCD9XXX_A_TX_7_MBHC_EN, 0x80, 0x80);
+	snd_soc_update_bits(codec, WCD9XXX_A_TX_7_MBHC_EN, 0x1F, 0x1C);
+	snd_soc_update_bits(codec, WCD9XXX_A_TX_7_MBHC_TEST_CTL, 0x40, 0x40);
+
+	snd_soc_update_bits(codec, WCD9XXX_A_TX_7_MBHC_EN, 0x80, 0x00);
+	snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8, 0x8);
+	snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8, 0x00);
+
+	snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x2, 0x2);
+	snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8, 0x8);
+
+	wcd9xxx_calibrate_hs_polling(mbhc);
+
+	/* don't flip override */
+	bias_value = __wcd9xxx_codec_sta_dce(mbhc, 1, true, true);
+	snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.cfilt_ctl, 0x40,
+			    cfilt_mode);
+	snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x13, 0x00);
+
+	return bias_value;
+}
+
+static void wcd9xxx_shutdown_hs_removal_detect(struct wcd9xxx_mbhc *mbhc)
+{
+	struct snd_soc_codec *codec = mbhc->codec;
+	const struct wcd9xxx_mbhc_general_cfg *generic =
+	    WCD9XXX_MBHC_CAL_GENERAL_PTR(mbhc->mbhc_cfg->calibration);
+
+	/* Need MBHC clock */
+	wcd9xxx_resmgr_get_clk_block(mbhc->resmgr, WCD9XXX_CLK_RCO);
+
+	snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x2, 0x2);
+	snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x6, 0x0);
+	snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg, 0x80, 0x00);
+
+	usleep_range(generic->t_shutdown_plug_rem,
+		     generic->t_shutdown_plug_rem);
+
+	snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0xA, 0x8);
+
+	/* Put requested CLK back */
+	wcd9xxx_resmgr_put_clk_block(mbhc->resmgr, WCD9XXX_CLK_RCO);
+
+	snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x00);
+}
+
+static void wcd9xxx_cleanup_hs_polling(struct wcd9xxx_mbhc *mbhc)
+{
+	WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
+
+	wcd9xxx_shutdown_hs_removal_detect(mbhc);
+
+	/* Release clock and BG requested by wcd9xxx_mbhc_setup_hs_polling */
+	wcd9xxx_resmgr_put_clk_block(mbhc->resmgr, WCD9XXX_CLK_RCO);
+	wcd9xxx_resmgr_put_bandgap(mbhc->resmgr, WCD9XXX_BANDGAP_MBHC_MODE);
+
+	mbhc->polling_active = false;
+	mbhc->mbhc_state = MBHC_STATE_NONE;
+}
+
+static s16 scale_v_micb_vddio(struct wcd9xxx_mbhc *mbhc, int v, bool tovddio)
+{
+	int r;
+	int vddio_k, mb_k;
+	vddio_k = wcd9xxx_resmgr_get_k_val(mbhc->resmgr, VDDIO_MICBIAS_MV);
+	mb_k = wcd9xxx_resmgr_get_k_val(mbhc->resmgr, mbhc->mbhc_data.micb_mv);
+	if (tovddio)
+		r = v * vddio_k / mb_k;
+	else
+		r = v * mb_k / vddio_k;
+	return r;
+}
+
+/* called under codec_resource_lock acquisition */
+static void wcd9xxx_codec_hphr_gnd_switch(struct snd_soc_codec *codec, bool on)
+{
+	snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x01, on);
+	if (on)
+		usleep_range(5000, 5000);
+}
+
+static bool wcd9xxx_is_inval_ins_delta(struct snd_soc_codec *codec,
+				       int mic_volt, int mic_volt_prev,
+				       int threshold)
+{
+	return abs(mic_volt - mic_volt_prev) > threshold;
+}
+
+/* called under codec_resource_lock acquisition and mbhc override = 1 */
+static enum wcd9xxx_mbhc_plug_type
+wcd9xxx_codec_get_plug_type(struct wcd9xxx_mbhc *mbhc, bool highhph)
+{
+	int i;
+	bool gndswitch, vddioswitch;
+	int scaled;
+	struct wcd9xxx_mbhc_plug_type_cfg *plug_type_ptr;
+	struct snd_soc_codec *codec = mbhc->codec;
+	const bool vddio = (mbhc->mbhc_data.micb_mv != VDDIO_MICBIAS_MV);
+	int num_det = (NUM_DCE_PLUG_DETECT + vddio);
+	enum wcd9xxx_mbhc_plug_type plug_type[num_det];
+	s16 mb_v[num_det];
+	s32 mic_mv[num_det];
+	bool inval;
+	bool highdelta;
+	bool ahighv = false, highv;
+
+	WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
+
+	/* make sure override is on */
+	WARN_ON(!(snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B1_CTL) & 0x04));
+
+	/* GND and MIC swap detection requires at least 2 rounds of DCE */
+	BUG_ON(num_det < 2);
+
+	plug_type_ptr =
+		WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(mbhc->mbhc_cfg->calibration);
+
+	plug_type[0] = PLUG_TYPE_INVALID;
+
+	/* performs DCEs for N times
+	 * 1st: check if voltage is in invalid range
+	 * 2nd - N-2nd: check voltage range and delta
+	 * N-1st: check voltage range, delta with HPHR GND switch
+	 * Nth: check voltage range with VDDIO switch if micbias V != vddio V*/
+	for (i = 0; i < num_det; i++) {
+		gndswitch = (i == (num_det - 1 - vddio));
+		vddioswitch = (vddio && ((i == num_det - 1) ||
+					(i == num_det - 2)));
+		if (i == 0) {
+			mb_v[i] = wcd9xxx_mbhc_setup_hs_polling(mbhc);
+			mic_mv[i] = wcd9xxx_codec_sta_dce_v(mbhc, 1 , mb_v[i]);
+			inval = wcd9xxx_is_inval_ins_range(mbhc, mic_mv[i],
+					highhph, &highv);
+			ahighv |= highv;
+			scaled = mic_mv[i];
+		} else {
+			if (vddioswitch)
+				__wcd9xxx_switch_micbias(mbhc, 1,
+							     false, false);
+			if (gndswitch)
+				wcd9xxx_codec_hphr_gnd_switch(codec, true);
+			mb_v[i] = __wcd9xxx_codec_sta_dce(mbhc, 1, true, true);
+			mic_mv[i] = wcd9xxx_codec_sta_dce_v(mbhc, 1 , mb_v[i]);
+			if (vddioswitch)
+				scaled = scale_v_micb_vddio(mbhc, mic_mv[i],
+							    false);
+			else
+				scaled = mic_mv[i];
+			/* !gndswitch & vddioswitch means the previous DCE
+			 * was done with gndswitch, don't compare with DCE
+			 * with gndswitch */
+			highdelta = wcd9xxx_is_inval_ins_delta(codec, scaled,
+					mic_mv[i - !gndswitch - vddioswitch],
+					FAKE_INS_DELTA_SCALED_MV);
+			inval = (wcd9xxx_is_inval_ins_range(mbhc, mic_mv[i],
+						highhph, &highv) ||
+					highdelta);
+			ahighv |= highv;
+			if (gndswitch)
+				wcd9xxx_codec_hphr_gnd_switch(codec, false);
+			if (vddioswitch)
+				__wcd9xxx_switch_micbias(mbhc, 0,
+							     false, false);
+			/* claim UNSUPPORTED plug insertion when
+			 * good headset is detected but HPHR GND switch makes
+			 * delta difference */
+			if (i == (num_det - 2) && highdelta && !ahighv)
+				plug_type[0] = PLUG_TYPE_GND_MIC_SWAP;
+			else if (i == (num_det - 1) && inval)
+				plug_type[0] = PLUG_TYPE_INVALID;
+		}
+		pr_debug("%s: DCE #%d, %04x, V %d, scaled V %d, GND %d, VDDIO %d, inval %d\n",
+			 __func__, i + 1, mb_v[i] & 0xffff, mic_mv[i], scaled,
+			 gndswitch, vddioswitch, inval);
+		/* don't need to run further DCEs */
+		if (ahighv && inval)
+			break;
+		mic_mv[i] = scaled;
+	}
+
+	for (i = 0; (plug_type[0] != PLUG_TYPE_GND_MIC_SWAP && !inval) &&
+		    (i < num_det); i++) {
+		/*
+		 * If we are here, means none of the all
+		 * measurements are fake, continue plug type detection.
+		 * If all three measurements do not produce same
+		 * plug type, restart insertion detection
+		 */
+		if (mic_mv[i] < plug_type_ptr->v_no_mic) {
+			plug_type[i] = PLUG_TYPE_HEADPHONE;
+			pr_debug("%s: Detect attempt %d, detected Headphone\n",
+				 __func__, i);
+		} else if (highhph && (mic_mv[i] > plug_type_ptr->v_hs_max)) {
+			plug_type[i] = PLUG_TYPE_HIGH_HPH;
+			pr_debug("%s: Detect attempt %d, detected High Headphone\n",
+				 __func__, i);
+		} else {
+			plug_type[i] = PLUG_TYPE_HEADSET;
+			pr_debug("%s: Detect attempt %d, detected Headset\n",
+					__func__, i);
+		}
+
+		if (i > 0 && (plug_type[i - 1] != plug_type[i])) {
+			pr_err("%s: Detect attempt %d and %d are not same",
+			       __func__, i - 1, i);
+			plug_type[0] = PLUG_TYPE_INVALID;
+			inval = true;
+			break;
+		}
+	}
+
+	pr_debug("%s: Detected plug type %d\n", __func__, plug_type[0]);
+	return plug_type[0];
+}
+
+static bool wcd9xxx_swch_level_remove(struct wcd9xxx_mbhc *mbhc)
+{
+	if (mbhc->mbhc_cfg->gpio)
+		return (gpio_get_value_cansleep(mbhc->mbhc_cfg->gpio) !=
+			mbhc->mbhc_cfg->gpio_level_insert);
+	else if (mbhc->mbhc_cfg->insert_detect)
+		return snd_soc_read(mbhc->codec,
+				    WCD9XXX_A_MBHC_INSERT_DET_STATUS) &
+				    (1 << 2);
+	else
+		WARN(1, "Invalid jack detection configuration\n");
+
+	return true;
+}
+
+static bool is_clk_active(struct snd_soc_codec *codec)
+{
+	return !!(snd_soc_read(codec, WCD9XXX_A_CDC_CLK_MCLK_CTL) & 0x05);
+}
+
+static int wcd9xxx_enable_hs_detect(struct wcd9xxx_mbhc *mbhc,
+				    int insertion, int trigger, bool padac_off)
+{
+	struct snd_soc_codec *codec = mbhc->codec;
+	int central_bias_enabled = 0;
+	const struct wcd9xxx_mbhc_general_cfg *generic =
+	    WCD9XXX_MBHC_CAL_GENERAL_PTR(mbhc->mbhc_cfg->calibration);
+	const struct wcd9xxx_mbhc_plug_detect_cfg *plug_det =
+	    WCD9XXX_MBHC_CAL_PLUG_DET_PTR(mbhc->mbhc_cfg->calibration);
+
+	if (!mbhc->mbhc_cfg->calibration) {
+		pr_err("Error, no wcd9xxx calibration\n");
+		return -EINVAL;
+	}
+
+	snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_INT_CTL, 0x1, 0);
+
+	/*
+	 * Make sure mic bias and Mic line schmitt trigger
+	 * are turned OFF
+	 */
+	snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01, 0x01);
+	snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg, 0x90, 0x00);
+
+	if (insertion) {
+		wcd9xxx_switch_micbias(mbhc, 0);
+
+		/* DAPM can manipulate PA/DAC bits concurrently */
+		if (padac_off == true)
+			wcd9xxx_set_and_turnoff_hph_padac(mbhc);
+
+		if (trigger & MBHC_USE_HPHL_TRIGGER) {
+			/* Enable HPH Schmitt Trigger */
+			snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x11,
+					0x11);
+			snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x0C,
+					plug_det->hph_current << 2);
+			snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x02,
+					0x02);
+		}
+		if (trigger & MBHC_USE_MB_TRIGGER) {
+			/* enable the mic line schmitt trigger */
+			snd_soc_update_bits(codec,
+					mbhc->mbhc_bias_regs.mbhc_reg,
+					0x60, plug_det->mic_current << 5);
+			snd_soc_update_bits(codec,
+					mbhc->mbhc_bias_regs.mbhc_reg,
+					0x80, 0x80);
+			usleep_range(plug_det->t_mic_pid, plug_det->t_mic_pid);
+			snd_soc_update_bits(codec,
+					mbhc->mbhc_bias_regs.ctl_reg, 0x01,
+					0x00);
+			snd_soc_update_bits(codec,
+					mbhc->mbhc_bias_regs.mbhc_reg,
+					0x10, 0x10);
+		}
+
+		/* setup for insetion detection */
+		snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_INT_CTL, 0x2, 0);
+	} else {
+		pr_debug("setup for removal detection\n");
+		/* Make sure the HPH schmitt trigger is OFF */
+		snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x12, 0x00);
+
+		/* enable the mic line schmitt trigger */
+		snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg,
+				    0x01, 0x00);
+		snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg, 0x60,
+				    plug_det->mic_current << 5);
+		snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg,
+				    0x80, 0x80);
+		usleep_range(plug_det->t_mic_pid, plug_det->t_mic_pid);
+		snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg,
+				    0x10, 0x10);
+
+		/* Setup for low power removal detection */
+		snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_INT_CTL, 0x2,
+				    0x2);
+	}
+
+	if (snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B1_CTL) & 0x4) {
+		/* called by interrupt */
+		if (!is_clk_active(codec)) {
+			wcd9xxx_resmgr_enable_config_mode(codec, 1);
+			snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL,
+					0x06, 0);
+			usleep_range(generic->t_shutdown_plug_rem,
+					generic->t_shutdown_plug_rem);
+			wcd9xxx_resmgr_enable_config_mode(codec, 0);
+		} else
+			snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL,
+					0x06, 0);
+	}
+
+	snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.int_rbias, 0x80, 0);
+
+	/* If central bandgap disabled */
+	if (!(snd_soc_read(codec, WCD9XXX_A_PIN_CTL_OE1) & 1)) {
+		snd_soc_update_bits(codec, WCD9XXX_A_PIN_CTL_OE1, 0x3, 0x3);
+		usleep_range(generic->t_bg_fast_settle,
+			     generic->t_bg_fast_settle);
+		central_bias_enabled = 1;
+	}
+
+	/* If LDO_H disabled */
+	if (snd_soc_read(codec, WCD9XXX_A_PIN_CTL_OE0) & 0x80) {
+		snd_soc_update_bits(codec, WCD9XXX_A_PIN_CTL_OE0, 0x10, 0);
+		snd_soc_update_bits(codec, WCD9XXX_A_PIN_CTL_OE0, 0x80, 0x80);
+		usleep_range(generic->t_ldoh, generic->t_ldoh);
+		snd_soc_update_bits(codec, WCD9XXX_A_PIN_CTL_OE0, 0x80, 0);
+
+		if (central_bias_enabled)
+			snd_soc_update_bits(codec, WCD9XXX_A_PIN_CTL_OE1, 0x1,
+					    0);
+	}
+
+	snd_soc_update_bits(codec, mbhc->resmgr->reg_addr->micb_4_mbhc, 0x3,
+			    mbhc->mbhc_cfg->micbias);
+
+	wcd9xxx_enable_irq(mbhc->resmgr->core, WCD9XXX_IRQ_MBHC_INSERTION);
+	snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_INT_CTL, 0x1, 0x1);
+
+	return 0;
+}
+
+/* called under codec_resource_lock acquisition */
+static void wcd9xxx_find_plug_and_report(struct wcd9xxx_mbhc *mbhc,
+					 enum wcd9xxx_mbhc_plug_type plug_type)
+{
+	WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
+
+	if (plug_type == PLUG_TYPE_HEADPHONE &&
+	    mbhc->current_plug == PLUG_TYPE_NONE) {
+		/*
+		 * Nothing was reported previously
+		 * report a headphone or unsupported
+		 */
+		wcd9xxx_report_plug(mbhc, 1, SND_JACK_HEADPHONE);
+		wcd9xxx_cleanup_hs_polling(mbhc);
+	} else if (plug_type == PLUG_TYPE_GND_MIC_SWAP) {
+		if (mbhc->current_plug == PLUG_TYPE_HEADSET)
+			wcd9xxx_report_plug(mbhc, 0, SND_JACK_HEADSET);
+		else if (mbhc->current_plug == PLUG_TYPE_HEADPHONE)
+			wcd9xxx_report_plug(mbhc, 0, SND_JACK_HEADPHONE);
+
+		wcd9xxx_report_plug(mbhc, 1, SND_JACK_UNSUPPORTED);
+		wcd9xxx_cleanup_hs_polling(mbhc);
+	} else if (plug_type == PLUG_TYPE_HEADSET) {
+		/*
+		 * If Headphone was reported previously, this will
+		 * only report the mic line
+		 */
+		wcd9xxx_report_plug(mbhc, 1, SND_JACK_HEADSET);
+		msleep(100);
+		wcd9xxx_start_hs_polling(mbhc);
+	} else if (plug_type == PLUG_TYPE_HIGH_HPH) {
+		if (mbhc->current_plug == PLUG_TYPE_NONE)
+			wcd9xxx_report_plug(mbhc, 1, SND_JACK_HEADPHONE);
+		wcd9xxx_cleanup_hs_polling(mbhc);
+		pr_debug("setup mic trigger for further detection\n");
+		mbhc->lpi_enabled = true;
+		wcd9xxx_enable_hs_detect(mbhc, 1, MBHC_USE_MB_TRIGGER |
+						  MBHC_USE_HPHL_TRIGGER, false);
+	} else {
+		WARN(1, "Unexpected current plug_type %d, plug_type %d\n",
+		     mbhc->current_plug, plug_type);
+	}
+}
+
+/* called under codec_resource_lock acquisition */
+static void wcd9xxx_mbhc_decide_swch_plug(struct wcd9xxx_mbhc *mbhc)
+{
+	enum wcd9xxx_mbhc_plug_type plug_type;
+	struct snd_soc_codec *codec = mbhc->codec;
+
+	pr_debug("%s: enter\n", __func__);
+
+	WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
+
+	wcd9xxx_turn_onoff_override(codec, true);
+	plug_type = wcd9xxx_codec_get_plug_type(mbhc, true);
+	wcd9xxx_turn_onoff_override(codec, false);
+
+	if (wcd9xxx_swch_level_remove(mbhc)) {
+		pr_debug("%s: Switch level is low when determining plug\n",
+			 __func__);
+		return;
+	}
+
+	if (plug_type == PLUG_TYPE_INVALID ||
+	    plug_type == PLUG_TYPE_GND_MIC_SWAP) {
+		wcd9xxx_schedule_hs_detect_plug(mbhc,
+						&mbhc->correct_plug_swch);
+	} else if (plug_type == PLUG_TYPE_HEADPHONE) {
+		wcd9xxx_report_plug(mbhc, 1, SND_JACK_HEADPHONE);
+		wcd9xxx_schedule_hs_detect_plug(mbhc,
+						&mbhc->correct_plug_swch);
+	} else {
+		pr_debug("%s: Valid plug found, determine plug type %d\n",
+			 __func__, plug_type);
+		wcd9xxx_find_plug_and_report(mbhc, plug_type);
+	}
+}
+
+/* called under codec_resource_lock acquisition */
+static void wcd9xxx_mbhc_detect_plug_type(struct wcd9xxx_mbhc *mbhc)
+{
+	struct snd_soc_codec *codec = mbhc->codec;
+	const struct wcd9xxx_mbhc_plug_detect_cfg *plug_det =
+		WCD9XXX_MBHC_CAL_PLUG_DET_PTR(mbhc->mbhc_cfg->calibration);
+
+	WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
+
+	/*
+	 * Turn on the override,
+	 * wcd9xxx_mbhc_setup_hs_polling requires override on
+	 */
+	wcd9xxx_turn_onoff_override(codec, true);
+	if (plug_det->t_ins_complete > 20)
+		msleep(plug_det->t_ins_complete);
+	else
+		usleep_range(plug_det->t_ins_complete * 1000,
+			     plug_det->t_ins_complete * 1000);
+	/* Turn off the override */
+	wcd9xxx_turn_onoff_override(codec, false);
+
+	if (wcd9xxx_swch_level_remove(mbhc))
+		pr_debug("%s: Switch level low when determining plug\n",
+			 __func__);
+	else
+		wcd9xxx_mbhc_decide_swch_plug(mbhc);
+}
+
+/* called only from interrupt which is under codec_resource_lock acquisition */
+static void wcd9xxx_hs_insert_irq_swch(struct wcd9xxx_mbhc *mbhc,
+				       bool is_removal)
+{
+	if (!is_removal) {
+		pr_debug("%s: MIC trigger insertion interrupt\n", __func__);
+
+		rmb();
+		if (mbhc->lpi_enabled)
+			msleep(100);
+
+		rmb();
+		if (!mbhc->lpi_enabled) {
+			pr_debug("%s: lpi is disabled\n", __func__);
+		} else if (!wcd9xxx_swch_level_remove(mbhc)) {
+			pr_debug("%s: Valid insertion, detect plug type\n",
+				 __func__);
+			wcd9xxx_mbhc_decide_swch_plug(mbhc);
+		} else {
+			pr_debug("%s: Invalid insertion stop plug detection\n",
+				 __func__);
+		}
+	} else {
+		pr_err("%s: Switch IRQ used, invalid MBHC Removal\n", __func__);
+	}
+}
+
+static bool is_valid_mic_voltage(struct wcd9xxx_mbhc *mbhc, s32 mic_mv)
+{
+	const struct wcd9xxx_mbhc_plug_type_cfg *plug_type =
+	    WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(mbhc->mbhc_cfg->calibration);
+	const s16 v_hs_max = wcd9xxx_get_current_v_hs_max(mbhc);
+
+	return (!(mic_mv > 10 && mic_mv < 80) && (mic_mv > plug_type->v_no_mic)
+		&& (mic_mv < v_hs_max)) ? true : false;
+}
+
+/*
+ * called under codec_resource_lock acquisition
+ * returns true if mic voltage range is back to normal insertion
+ * returns false either if timedout or removed
+ */
+static bool wcd9xxx_hs_remove_settle(struct wcd9xxx_mbhc *mbhc)
+{
+	int i;
+	bool timedout, settled = false;
+	s32 mic_mv[NUM_DCE_PLUG_DETECT];
+	short mb_v[NUM_DCE_PLUG_DETECT];
+	unsigned long retry = 0, timeout;
+
+	timeout = jiffies + msecs_to_jiffies(HS_DETECT_PLUG_TIME_MS);
+	while (!(timedout = time_after(jiffies, timeout))) {
+		retry++;
+		if (wcd9xxx_swch_level_remove(mbhc)) {
+			pr_debug("%s: Switch indicates removal\n", __func__);
+			break;
+		}
+
+		if (retry > 1)
+			msleep(250);
+		else
+			msleep(50);
+
+		if (wcd9xxx_swch_level_remove(mbhc)) {
+			pr_debug("%s: Switch indicates removal\n", __func__);
+			break;
+		}
+
+		for (i = 0; i < NUM_DCE_PLUG_DETECT; i++) {
+			mb_v[i] = wcd9xxx_codec_sta_dce(mbhc, 1,  true);
+			mic_mv[i] = wcd9xxx_codec_sta_dce_v(mbhc, 1 , mb_v[i]);
+			pr_debug("%s : DCE run %lu, mic_mv = %d(%x)\n",
+				 __func__, retry, mic_mv[i], mb_v[i]);
+		}
+
+		if (wcd9xxx_swch_level_remove(mbhc)) {
+			pr_debug("%s: Switcn indicates removal\n", __func__);
+			break;
+		}
+
+		if (mbhc->current_plug == PLUG_TYPE_NONE) {
+			pr_debug("%s : headset/headphone is removed\n",
+				 __func__);
+			break;
+		}
+
+		for (i = 0; i < NUM_DCE_PLUG_DETECT; i++)
+			if (!is_valid_mic_voltage(mbhc, mic_mv[i]))
+				break;
+
+		if (i == NUM_DCE_PLUG_DETECT) {
+			pr_debug("%s: MIC voltage settled\n", __func__);
+			settled = true;
+			msleep(200);
+			break;
+		}
+	}
+
+	if (timedout)
+		pr_debug("%s: Microphone did not settle in %d seconds\n",
+			 __func__, HS_DETECT_PLUG_TIME_MS);
+	return settled;
+}
+
+/* called only from interrupt which is under codec_resource_lock acquisition */
+static void wcd9xxx_hs_remove_irq_swch(struct wcd9xxx_mbhc *mbhc)
+{
+	if (wcd9xxx_hs_remove_settle(mbhc))
+		wcd9xxx_start_hs_polling(mbhc);
+	pr_debug("%s: remove settle done\n", __func__);
+}
+
+static irqreturn_t wcd9xxx_hs_remove_irq(int irq, void *data)
+{
+	bool vddio;
+	struct wcd9xxx_mbhc *mbhc = data;
+
+	pr_debug("%s: enter, removal interrupt\n", __func__);
+	WCD9XXX_BCL_LOCK(mbhc->resmgr);
+	vddio = (mbhc->mbhc_data.micb_mv != VDDIO_MICBIAS_MV &&
+		 mbhc->mbhc_micbias_switched);
+	if (vddio)
+		__wcd9xxx_switch_micbias(mbhc, 0, false, true);
+
+	wcd9xxx_hs_remove_irq_swch(mbhc);
+
+	/*
+	 * if driver turned off vddio switch and headset is not removed,
+	 * turn on the vddio switch back, if headset is removed then vddio
+	 * switch is off by time now and shouldn't be turn on again from here
+	 */
+	if (vddio && mbhc->current_plug == PLUG_TYPE_HEADSET)
+		__wcd9xxx_switch_micbias(mbhc, 1, true, true);
+	WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t wcd9xxx_hs_insert_irq(int irq, void *data)
+{
+	bool is_mb_trigger, is_removal;
+	struct wcd9xxx_mbhc *mbhc = data;
+	struct snd_soc_codec *codec = mbhc->codec;
+
+	pr_debug("%s: enter\n", __func__);
+	WCD9XXX_BCL_LOCK(mbhc->resmgr);
+	wcd9xxx_disable_irq(codec->control_data, WCD9XXX_IRQ_MBHC_INSERTION);
+
+	is_mb_trigger = !!(snd_soc_read(codec, mbhc->mbhc_bias_regs.mbhc_reg) &
+			   0x10);
+	is_removal = !!(snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_INT_CTL) & 0x02);
+	snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_INT_CTL, 0x03, 0x00);
+
+	/* Turn off both HPH and MIC line schmitt triggers */
+	snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg, 0x90, 0x00);
+	snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x13, 0x00);
+	snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01, 0x00);
+
+	wcd9xxx_hs_insert_irq_swch(mbhc, is_removal);
+
+	WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
+	return IRQ_HANDLED;
+}
+
+static void wcd9xxx_btn_lpress_fn(struct work_struct *work)
+{
+	struct delayed_work *dwork;
+	short bias_value;
+	int dce_mv, sta_mv;
+	struct wcd9xxx_mbhc *mbhc;
+
+	pr_debug("%s:\n", __func__);
+
+	dwork = to_delayed_work(work);
+	mbhc = container_of(dwork, struct wcd9xxx_mbhc, mbhc_btn_dwork);
+
+	bias_value = wcd9xxx_read_sta_result(mbhc->codec);
+	sta_mv = wcd9xxx_codec_sta_dce_v(mbhc, 0, bias_value);
+
+	bias_value = wcd9xxx_read_dce_result(mbhc->codec);
+	dce_mv = wcd9xxx_codec_sta_dce_v(mbhc, 1, bias_value);
+	pr_debug("%s: STA: %d, DCE: %d\n", __func__, sta_mv, dce_mv);
+
+	pr_debug("%s: Reporting long button press event\n", __func__);
+	wcd9xxx_jack_report(&mbhc->button_jack, mbhc->buttons_pressed,
+			    mbhc->buttons_pressed);
+
+	pr_debug("%s: leave\n", __func__);
+	wcd9xxx_unlock_sleep(mbhc->resmgr->core);
+}
+
+static void wcd9xxx_mbhc_insert_work(struct work_struct *work)
+{
+	struct delayed_work *dwork;
+	struct wcd9xxx_mbhc *mbhc;
+	struct snd_soc_codec *codec;
+	struct wcd9xxx *core;
+
+	dwork = to_delayed_work(work);
+	mbhc = container_of(dwork, struct wcd9xxx_mbhc, mbhc_insert_dwork);
+	codec = mbhc->codec;
+	core = mbhc->resmgr->core;
+
+	pr_debug("%s:\n", __func__);
+
+	/* Turn off both HPH and MIC line schmitt triggers */
+	snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg, 0x90, 0x00);
+	snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x13, 0x00);
+	snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01, 0x00);
+	wcd9xxx_disable_irq_sync(core, WCD9XXX_IRQ_MBHC_INSERTION);
+	wcd9xxx_mbhc_detect_plug_type(mbhc);
+	wcd9xxx_unlock_sleep(core);
+}
+
+static bool wcd9xxx_mbhc_fw_validate(const struct firmware *fw)
+{
+	u32 cfg_offset;
+	struct wcd9xxx_mbhc_imped_detect_cfg *imped_cfg;
+	struct wcd9xxx_mbhc_btn_detect_cfg *btn_cfg;
+
+	if (fw->size < WCD9XXX_MBHC_CAL_MIN_SIZE)
+		return false;
+
+	/*
+	 * Previous check guarantees that there is enough fw data up
+	 * to num_btn
+	 */
+	btn_cfg = WCD9XXX_MBHC_CAL_BTN_DET_PTR(fw->data);
+	cfg_offset = (u32) ((void *) btn_cfg - (void *) fw->data);
+	if (fw->size < (cfg_offset + WCD9XXX_MBHC_CAL_BTN_SZ(btn_cfg)))
+		return false;
+
+	/*
+	 * Previous check guarantees that there is enough fw data up
+	 * to start of impedance detection configuration
+	 */
+	imped_cfg = WCD9XXX_MBHC_CAL_IMPED_DET_PTR(fw->data);
+	cfg_offset = (u32) ((void *) imped_cfg - (void *) fw->data);
+
+	if (fw->size < (cfg_offset + WCD9XXX_MBHC_CAL_IMPED_MIN_SZ))
+		return false;
+
+	if (fw->size < (cfg_offset + WCD9XXX_MBHC_CAL_IMPED_SZ(imped_cfg)))
+		return false;
+
+	return true;
+}
+
+static u16 wcd9xxx_codec_v_sta_dce(struct wcd9xxx_mbhc *mbhc,
+				   enum meas_type dce, s16 vin_mv)
+{
+	s16 diff, zero;
+	u32 mb_mv, in;
+	u16 value;
+
+	mb_mv = mbhc->mbhc_data.micb_mv;
+
+	if (mb_mv == 0) {
+		pr_err("%s: Mic Bias voltage is set to zero\n", __func__);
+		return -EINVAL;
+	}
+
+	if (dce) {
+		diff = (mbhc->mbhc_data.dce_mb) - (mbhc->mbhc_data.dce_z);
+		zero = (mbhc->mbhc_data.dce_z);
+	} else {
+		diff = (mbhc->mbhc_data.sta_mb) - (mbhc->mbhc_data.sta_z);
+		zero = (mbhc->mbhc_data.sta_z);
+	}
+	in = (u32) diff * vin_mv;
+
+	value = (u16) (in / mb_mv) + zero;
+	return value;
+}
+
+static void wcd9xxx_mbhc_calc_thres(struct wcd9xxx_mbhc *mbhc)
+{
+	struct snd_soc_codec *codec;
+	s16 btn_mv = 0, btn_delta_mv;
+	struct wcd9xxx_mbhc_btn_detect_cfg *btn_det;
+	struct wcd9xxx_mbhc_plug_type_cfg *plug_type;
+	u16 *btn_high;
+	int i;
+
+	pr_debug("%s: enter\n", __func__);
+	codec = mbhc->codec;
+	btn_det = WCD9XXX_MBHC_CAL_BTN_DET_PTR(mbhc->mbhc_cfg->calibration);
+	plug_type = WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(mbhc->mbhc_cfg->calibration);
+
+	mbhc->mbhc_data.v_ins_hu =
+	    wcd9xxx_codec_v_sta_dce(mbhc, STA, plug_type->v_hs_max);
+	mbhc->mbhc_data.v_ins_h =
+	    wcd9xxx_codec_v_sta_dce(mbhc, DCE, plug_type->v_hs_max);
+
+	mbhc->mbhc_data.v_inval_ins_low = FAKE_INS_LOW;
+	mbhc->mbhc_data.v_inval_ins_high = FAKE_INS_HIGH;
+
+	if (mbhc->mbhc_data.micb_mv != VDDIO_MICBIAS_MV) {
+		mbhc->mbhc_data.adj_v_hs_max =
+		    scale_v_micb_vddio(mbhc, plug_type->v_hs_max, true);
+		mbhc->mbhc_data.adj_v_ins_hu =
+		    wcd9xxx_codec_v_sta_dce(mbhc, STA,
+					    mbhc->mbhc_data.adj_v_hs_max);
+		mbhc->mbhc_data.adj_v_ins_h =
+		    wcd9xxx_codec_v_sta_dce(mbhc, DCE,
+					    mbhc->mbhc_data.adj_v_hs_max);
+		mbhc->mbhc_data.v_inval_ins_low =
+		    scale_v_micb_vddio(mbhc, mbhc->mbhc_data.v_inval_ins_low,
+				       false);
+		mbhc->mbhc_data.v_inval_ins_high =
+		    scale_v_micb_vddio(mbhc, mbhc->mbhc_data.v_inval_ins_high,
+				       false);
+	}
+
+	btn_high = wcd9xxx_mbhc_cal_btn_det_mp(btn_det,
+					       MBHC_BTN_DET_V_BTN_HIGH);
+	for (i = 0; i < btn_det->num_btn; i++)
+		btn_mv = btn_high[i] > btn_mv ? btn_high[i] : btn_mv;
+
+	mbhc->mbhc_data.v_b1_h = wcd9xxx_codec_v_sta_dce(mbhc, DCE, btn_mv);
+	btn_delta_mv = btn_mv + btn_det->v_btn_press_delta_sta;
+	mbhc->mbhc_data.v_b1_hu =
+	    wcd9xxx_codec_v_sta_dce(mbhc, STA, btn_delta_mv);
+
+	btn_delta_mv = btn_mv + btn_det->v_btn_press_delta_cic;
+
+	mbhc->mbhc_data.v_b1_huc =
+	    wcd9xxx_codec_v_sta_dce(mbhc, DCE, btn_delta_mv);
+
+	mbhc->mbhc_data.v_brh = mbhc->mbhc_data.v_b1_h;
+	mbhc->mbhc_data.v_brl = BUTTON_MIN;
+
+	mbhc->mbhc_data.v_no_mic =
+	    wcd9xxx_codec_v_sta_dce(mbhc, STA, plug_type->v_no_mic);
+	pr_debug("%s: leave\n", __func__);
+}
+
+static void wcd9xxx_onoff_ext_mclk(struct wcd9xxx_mbhc *mbhc, bool on)
+{
+	/*
+	 * XXX: {codec}_mclk_enable holds WCD9XXX_BCL_LOCK,
+	 * therefore wcd9xxx_onoff_ext_mclk caller SHOULDN'T hold
+	 * WCD9XXX_BCL_LOCK when it calls wcd9xxx_onoff_ext_mclk()
+	 */
+	 mbhc->mbhc_cfg->mclk_cb_fn(mbhc->codec, on, false);
+}
+
+static void wcd9xxx_correct_swch_plug(struct work_struct *work)
+{
+	struct wcd9xxx_mbhc *mbhc;
+	struct snd_soc_codec *codec;
+	enum wcd9xxx_mbhc_plug_type plug_type;
+	unsigned long timeout;
+	int retry = 0, pt_gnd_mic_swap_cnt = 0;
+	bool correction = false;
+
+	pr_debug("%s: enter\n", __func__);
+
+	mbhc = container_of(work, struct wcd9xxx_mbhc, correct_plug_swch);
+	codec = mbhc->codec;
+
+	wcd9xxx_onoff_ext_mclk(mbhc, true);
+
+	/*
+	 * Keep override on during entire plug type correction work.
+	 *
+	 * This is okay under the assumption that any switch irqs which use
+	 * MBHC block cancel and sync this work so override is off again
+	 * prior to switch interrupt handler's MBHC block usage.
+	 * Also while this correction work is running, we can guarantee
+	 * DAPM doesn't use any MBHC block as this work only runs with
+	 * headphone detection.
+	 */
+	wcd9xxx_turn_onoff_override(codec, true);
+
+	timeout = jiffies + msecs_to_jiffies(HS_DETECT_PLUG_TIME_MS);
+	while (!time_after(jiffies, timeout)) {
+		++retry;
+		rmb();
+		if (mbhc->hs_detect_work_stop) {
+			pr_debug("%s: stop requested\n", __func__);
+			break;
+		}
+
+		msleep(HS_DETECT_PLUG_INERVAL_MS);
+		if (wcd9xxx_swch_level_remove(mbhc)) {
+			pr_debug("%s: Switch level is low\n", __func__);
+			break;
+		}
+
+		/* can race with removal interrupt */
+		WCD9XXX_BCL_LOCK(mbhc->resmgr);
+		plug_type = wcd9xxx_codec_get_plug_type(mbhc, true);
+		WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
+
+		if (plug_type == PLUG_TYPE_INVALID) {
+			pr_debug("Invalid plug in attempt # %d\n", retry);
+			if (retry == NUM_ATTEMPTS_TO_REPORT &&
+			    mbhc->current_plug == PLUG_TYPE_NONE) {
+				wcd9xxx_report_plug(mbhc, 1,
+						    SND_JACK_HEADPHONE);
+			}
+		} else if (plug_type == PLUG_TYPE_HEADPHONE) {
+			pr_debug("Good headphone detected, continue polling\n");
+			if (mbhc->current_plug == PLUG_TYPE_NONE)
+				wcd9xxx_report_plug(mbhc, 1,
+						    SND_JACK_HEADPHONE);
+		} else {
+			if (plug_type == PLUG_TYPE_GND_MIC_SWAP) {
+				pt_gnd_mic_swap_cnt++;
+				if (pt_gnd_mic_swap_cnt <
+				    GND_MIC_SWAP_THRESHOLD)
+					continue;
+				else if (pt_gnd_mic_swap_cnt >
+					 GND_MIC_SWAP_THRESHOLD) {
+					/*
+					 * This is due to GND/MIC switch didn't
+					 * work,  Report unsupported plug
+					 */
+				} else if (mbhc->mbhc_cfg->swap_gnd_mic) {
+					/*
+					 * if switch is toggled, check again,
+					 * otherwise report unsupported plug
+					 */
+					if (mbhc->mbhc_cfg->swap_gnd_mic(codec))
+						continue;
+				}
+			} else
+				pt_gnd_mic_swap_cnt = 0;
+
+			WCD9XXX_BCL_LOCK(mbhc->resmgr);
+			/* Turn off override */
+			wcd9xxx_turn_onoff_override(codec, false);
+			/*
+			 * The valid plug also includes PLUG_TYPE_GND_MIC_SWAP
+			 */
+			wcd9xxx_find_plug_and_report(mbhc, plug_type);
+			WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
+			pr_debug("Attempt %d found correct plug %d\n", retry,
+				 plug_type);
+			correction = true;
+			break;
+		}
+	}
+
+	/* Turn off override */
+	if (!correction)
+		wcd9xxx_turn_onoff_override(codec, false);
+
+	wcd9xxx_onoff_ext_mclk(mbhc, false);
+	pr_debug("%s: leave\n", __func__);
+	/* unlock sleep */
+	wcd9xxx_unlock_sleep(mbhc->resmgr->core);
+}
+
+static void wcd9xxx_swch_irq_handler(struct wcd9xxx_mbhc *mbhc)
+{
+	bool insert;
+	bool is_removed = false;
+	struct snd_soc_codec *codec = mbhc->codec;
+
+	pr_debug("%s: enter\n", __func__);
+
+	mbhc->in_swch_irq_handler = true;
+	/* Wait here for debounce time */
+	usleep_range(SWCH_IRQ_DEBOUNCE_TIME_US, SWCH_IRQ_DEBOUNCE_TIME_US);
+
+	WCD9XXX_BCL_LOCK(mbhc->resmgr);
+
+	/* cancel pending button press */
+	if (wcd9xxx_cancel_btn_work(mbhc))
+		pr_debug("%s: button press is canceled\n", __func__);
+
+	insert = !wcd9xxx_swch_level_remove(mbhc);
+	pr_debug("%s: Current plug type %d, insert %d\n", __func__,
+		 mbhc->current_plug, insert);
+	if ((mbhc->current_plug == PLUG_TYPE_NONE) && insert) {
+		mbhc->lpi_enabled = false;
+		wmb();
+
+		/* cancel detect plug */
+		wcd9xxx_cancel_hs_detect_plug(mbhc,
+					      &mbhc->correct_plug_swch);
+
+		/* Disable Mic Bias pull down and HPH Switch to GND */
+		snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01,
+				    0x00);
+		snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x01, 0x00);
+		wcd9xxx_mbhc_detect_plug_type(mbhc);
+	} else if ((mbhc->current_plug != PLUG_TYPE_NONE) && !insert) {
+		mbhc->lpi_enabled = false;
+		wmb();
+
+		/* cancel detect plug */
+		wcd9xxx_cancel_hs_detect_plug(mbhc,
+					      &mbhc->correct_plug_swch);
+
+		if (mbhc->current_plug == PLUG_TYPE_HEADPHONE) {
+			wcd9xxx_report_plug(mbhc, 0, SND_JACK_HEADPHONE);
+			is_removed = true;
+		} else if (mbhc->current_plug == PLUG_TYPE_GND_MIC_SWAP) {
+			wcd9xxx_report_plug(mbhc, 0, SND_JACK_UNSUPPORTED);
+			is_removed = true;
+		} else if (mbhc->current_plug == PLUG_TYPE_HEADSET) {
+			wcd9xxx_pause_hs_polling(mbhc);
+			wcd9xxx_cleanup_hs_polling(mbhc);
+			wcd9xxx_report_plug(mbhc, 0, SND_JACK_HEADSET);
+			is_removed = true;
+		}
+
+		if (is_removed) {
+			/* Enable Mic Bias pull down and HPH Switch to GND */
+			snd_soc_update_bits(codec,
+					mbhc->mbhc_bias_regs.ctl_reg, 0x01,
+					0x01);
+			snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x01,
+					0x01);
+			/* Make sure mic trigger is turned off */
+			snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg,
+					    0x01, 0x01);
+			snd_soc_update_bits(codec,
+					    mbhc->mbhc_bias_regs.mbhc_reg,
+					    0x90, 0x00);
+			/* Reset MBHC State Machine */
+			snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL,
+					    0x08, 0x08);
+			snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL,
+					    0x08, 0x00);
+			/* Turn off override */
+			wcd9xxx_turn_onoff_override(codec, false);
+		}
+	}
+
+	mbhc->in_swch_irq_handler = false;
+	WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
+	pr_debug("%s: leave\n", __func__);
+}
+
+static irqreturn_t wcd9xxx_mech_plug_detect_irq(int irq, void *data)
+{
+	int r = IRQ_HANDLED;
+	struct wcd9xxx_mbhc *mbhc = data;
+
+	pr_debug("%s: enter\n", __func__);
+	if (unlikely(wcd9xxx_lock_sleep(mbhc->resmgr->core) == false)) {
+		pr_warn("%s: failed to hold suspend\n", __func__);
+		r = IRQ_NONE;
+	} else {
+		/* Call handler */
+		wcd9xxx_swch_irq_handler(mbhc);
+		wcd9xxx_unlock_sleep(mbhc->resmgr->core);
+	}
+
+	pr_debug("%s: leave %d\n", __func__, r);
+	return r;
+}
+
+/* called under codec_resource_lock acquisition */
+static void wcd9xxx_codec_drive_v_to_micbias(struct wcd9xxx_mbhc *mbhc,
+					     int usec)
+{
+	int cfilt_k_val;
+	bool set = true;
+
+	if (mbhc->mbhc_data.micb_mv != VDDIO_MICBIAS_MV &&
+	    mbhc->mbhc_micbias_switched) {
+		pr_debug("%s: set mic V to micbias V\n", __func__);
+		snd_soc_update_bits(mbhc->codec, WCD9XXX_A_CDC_MBHC_CLK_CTL,
+				    0x2, 0x2);
+		wcd9xxx_turn_onoff_override(mbhc->codec, true);
+		while (1) {
+			cfilt_k_val =
+			    wcd9xxx_resmgr_get_k_val(mbhc->resmgr,
+						set ? mbhc->mbhc_data.micb_mv :
+						VDDIO_MICBIAS_MV);
+			snd_soc_update_bits(mbhc->codec,
+					    mbhc->mbhc_bias_regs.cfilt_val,
+					    0xFC, (cfilt_k_val << 2));
+			if (!set)
+				break;
+			usleep_range(usec, usec);
+			set = false;
+		}
+		wcd9xxx_turn_onoff_override(mbhc->codec, false);
+	}
+}
+
+static int wcd9xxx_is_fake_press(struct wcd9xxx_mbhc *mbhc)
+{
+	int i;
+	int r = 0;
+	const int dces = NUM_DCE_PLUG_DETECT;
+	s16 mb_v, v_ins_hu, v_ins_h;
+
+	v_ins_hu = wcd9xxx_get_current_v_ins(mbhc, true);
+	v_ins_h = wcd9xxx_get_current_v_ins(mbhc, false);
+
+	for (i = 0; i < dces; i++) {
+		usleep_range(10000, 10000);
+		if (i == 0) {
+			mb_v = wcd9xxx_codec_sta_dce(mbhc, 0, true);
+			pr_debug("%s: STA[0]: %d,%d\n", __func__, mb_v,
+				 wcd9xxx_codec_sta_dce_v(mbhc, 0, mb_v));
+			if (mb_v < (s16)mbhc->mbhc_data.v_b1_hu ||
+			    mb_v > v_ins_hu) {
+				r = 1;
+				break;
+			}
+		} else {
+			mb_v = wcd9xxx_codec_sta_dce(mbhc, 1, true);
+			pr_debug("%s: DCE[%d]: %d,%d\n", __func__, i, mb_v,
+				 wcd9xxx_codec_sta_dce_v(mbhc, 1, mb_v));
+			if (mb_v < (s16)mbhc->mbhc_data.v_b1_h ||
+			    mb_v > v_ins_h) {
+				r = 1;
+				break;
+			}
+		}
+	}
+
+	return r;
+}
+
+/* called under codec_resource_lock acquisition */
+static int wcd9xxx_determine_button(const struct wcd9xxx_mbhc *mbhc,
+				  const s32 micmv)
+{
+	s16 *v_btn_low, *v_btn_high;
+	struct wcd9xxx_mbhc_btn_detect_cfg *btn_det;
+	int i, btn = -1;
+
+	btn_det = WCD9XXX_MBHC_CAL_BTN_DET_PTR(mbhc->mbhc_cfg->calibration);
+	v_btn_low = wcd9xxx_mbhc_cal_btn_det_mp(btn_det,
+						MBHC_BTN_DET_V_BTN_LOW);
+	v_btn_high = wcd9xxx_mbhc_cal_btn_det_mp(btn_det,
+						 MBHC_BTN_DET_V_BTN_HIGH);
+
+	for (i = 0; i < btn_det->num_btn; i++) {
+		if ((v_btn_low[i] <= micmv) && (v_btn_high[i] >= micmv)) {
+			btn = i;
+			break;
+		}
+	}
+
+	if (btn == -1)
+		pr_debug("%s: couldn't find button number for mic mv %d\n",
+			 __func__, micmv);
+
+	return btn;
+}
+
+static int wcd9xxx_get_button_mask(const int btn)
+{
+	int mask = 0;
+	switch (btn) {
+	case 0:
+		mask = SND_JACK_BTN_0;
+		break;
+	case 1:
+		mask = SND_JACK_BTN_1;
+		break;
+	case 2:
+		mask = SND_JACK_BTN_2;
+		break;
+	case 3:
+		mask = SND_JACK_BTN_3;
+		break;
+	case 4:
+		mask = SND_JACK_BTN_4;
+		break;
+	case 5:
+		mask = SND_JACK_BTN_5;
+		break;
+	case 6:
+		mask = SND_JACK_BTN_6;
+		break;
+	case 7:
+		mask = SND_JACK_BTN_7;
+		break;
+	}
+	return mask;
+}
+
+irqreturn_t wcd9xxx_dce_handler(int irq, void *data)
+{
+	int i, mask;
+	short dce, sta;
+	s32 mv, mv_s, stamv_s;
+	bool vddio;
+	u8 mbhc_status;
+	int btn = -1, meas = 0;
+	struct wcd9xxx_mbhc *mbhc = data;
+	const struct wcd9xxx_mbhc_btn_detect_cfg *d =
+	    WCD9XXX_MBHC_CAL_BTN_DET_PTR(mbhc->mbhc_cfg->calibration);
+	short btnmeas[d->n_btn_meas + 1];
+	struct snd_soc_codec *codec = mbhc->codec;
+	struct wcd9xxx *core = mbhc->resmgr->core;
+	int n_btn_meas = d->n_btn_meas;
+
+	pr_debug("%s: enter\n", __func__);
+
+	WCD9XXX_BCL_LOCK(mbhc->resmgr);
+	mbhc_status = snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B1_STATUS) & 0x3E;
+
+	if (mbhc->mbhc_state == MBHC_STATE_POTENTIAL_RECOVERY) {
+		pr_debug("%s: mbhc is being recovered, skip button press\n",
+			 __func__);
+		goto done;
+	}
+
+	mbhc->mbhc_state = MBHC_STATE_POTENTIAL;
+
+	if (!mbhc->polling_active) {
+		pr_warn("%s: mbhc polling is not active, skip button press\n",
+			__func__);
+		goto done;
+	}
+
+	dce = wcd9xxx_read_dce_result(codec);
+	mv = wcd9xxx_codec_sta_dce_v(mbhc, 1, dce);
+
+	/* If switch nterrupt already kicked in, ignore button press */
+	if (mbhc->in_swch_irq_handler) {
+		pr_debug("%s: Swtich level changed, ignore button press\n",
+			 __func__);
+		btn = -1;
+		goto done;
+	}
+
+	/* Measure scaled HW DCE */
+	vddio = (mbhc->mbhc_data.micb_mv != VDDIO_MICBIAS_MV &&
+		 mbhc->mbhc_micbias_switched);
+	mv_s = vddio ? scale_v_micb_vddio(mbhc, mv, false) : mv;
+
+	/* Measure scaled HW STA */
+	sta = wcd9xxx_read_sta_result(codec);
+	stamv_s = wcd9xxx_codec_sta_dce_v(mbhc, 0, sta);
+	if (vddio)
+		stamv_s = scale_v_micb_vddio(mbhc, stamv_s, false);
+	if (mbhc_status != STATUS_REL_DETECTION) {
+		if (mbhc->mbhc_last_resume &&
+		    !time_after(jiffies, mbhc->mbhc_last_resume + HZ)) {
+			pr_debug("%s: Button is released after resume\n",
+				__func__);
+			n_btn_meas = 0;
+		} else {
+			pr_debug("%s: Button is released without resume",
+				 __func__);
+			btn = wcd9xxx_determine_button(mbhc, mv_s);
+			if (btn != wcd9xxx_determine_button(mbhc, stamv_s))
+				btn = -1;
+			goto done;
+		}
+	}
+
+	pr_debug("%s: Meas HW - STA 0x%x,%d,%d\n", __func__,
+		 sta & 0xFFFF, wcd9xxx_codec_sta_dce_v(mbhc, 0, sta), stamv_s);
+
+	/* determine pressed button */
+	btnmeas[meas++] = wcd9xxx_determine_button(mbhc, mv_s);
+	pr_debug("%s: Meas HW - DCE 0x%x,%d,%d button %d\n", __func__,
+		 dce & 0xFFFF, mv, mv_s, btnmeas[meas - 1]);
+	if (n_btn_meas == 0)
+		btn = btnmeas[0];
+	for (; ((d->n_btn_meas) && (meas < (d->n_btn_meas + 1))); meas++) {
+		dce = wcd9xxx_codec_sta_dce(mbhc, 1, false);
+		mv = wcd9xxx_codec_sta_dce_v(mbhc, 1, dce);
+		mv_s = vddio ? scale_v_micb_vddio(mbhc, mv, false) : mv;
+
+		btnmeas[meas] = wcd9xxx_determine_button(mbhc, mv_s);
+		pr_debug("%s: Meas %d - DCE 0x%x,%d,%d button %d\n",
+			 __func__, meas, dce & 0xFFFF, mv, mv_s, btnmeas[meas]);
+		/*
+		 * if large enough measurements are collected,
+		 * start to check if last all n_btn_con measurements were
+		 * in same button low/high range
+		 */
+		if (meas + 1 >= d->n_btn_con) {
+			for (i = 0; i < d->n_btn_con; i++)
+				if ((btnmeas[meas] < 0) ||
+				    (btnmeas[meas] != btnmeas[meas - i]))
+					break;
+			if (i == d->n_btn_con) {
+				/* button pressed */
+				btn = btnmeas[meas];
+				break;
+			} else if ((n_btn_meas - meas) < (d->n_btn_con - 1)) {
+				/*
+				 * if left measurements are less than n_btn_con,
+				 * it's impossible to find button number
+				 */
+				break;
+			}
+		}
+	}
+
+	if (btn >= 0) {
+		if (mbhc->in_swch_irq_handler) {
+			pr_debug(
+			"%s: Switch irq triggered, ignore button press\n",
+			__func__);
+			goto done;
+		}
+		mask = wcd9xxx_get_button_mask(btn);
+		mbhc->buttons_pressed |= mask;
+		wcd9xxx_lock_sleep(core);
+		if (schedule_delayed_work(&mbhc->mbhc_btn_dwork,
+					  msecs_to_jiffies(400)) == 0) {
+			WARN(1, "Button pressed twice without release event\n");
+			wcd9xxx_unlock_sleep(core);
+		}
+	} else {
+		pr_debug("%s: bogus button press, too short press?\n",
+			 __func__);
+	}
+
+ done:
+	pr_debug("%s: leave\n", __func__);
+	WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t wcd9xxx_release_handler(int irq, void *data)
+{
+	int ret;
+	struct wcd9xxx_mbhc *mbhc = data;
+
+	pr_debug("%s: enter\n", __func__);
+	WCD9XXX_BCL_LOCK(mbhc->resmgr);
+	mbhc->mbhc_state = MBHC_STATE_RELEASE;
+
+	wcd9xxx_codec_drive_v_to_micbias(mbhc, 10000);
+
+	if (mbhc->buttons_pressed & WCD9XXX_JACK_BUTTON_MASK) {
+		ret = wcd9xxx_cancel_btn_work(mbhc);
+		if (ret == 0) {
+			pr_debug("%s: Reporting long button release event\n",
+				 __func__);
+			wcd9xxx_jack_report(&mbhc->button_jack, 0,
+					    mbhc->buttons_pressed);
+		} else {
+			if (wcd9xxx_is_fake_press(mbhc)) {
+				pr_debug("%s: Fake button press interrupt\n",
+					 __func__);
+			} else {
+				if (mbhc->in_swch_irq_handler) {
+					pr_debug("%s: Switch irq kicked in, ignore\n",
+						 __func__);
+				} else {
+					pr_debug("%s: Reporting btn press\n",
+						 __func__);
+					wcd9xxx_jack_report(&mbhc->button_jack,
+							 mbhc->buttons_pressed,
+							 mbhc->buttons_pressed);
+					pr_debug("%s: Reporting btn release\n",
+						 __func__);
+					wcd9xxx_jack_report(&mbhc->button_jack,
+						      0, mbhc->buttons_pressed);
+				}
+			}
+		}
+
+		mbhc->buttons_pressed &= ~WCD9XXX_JACK_BUTTON_MASK;
+	}
+
+	wcd9xxx_calibrate_hs_polling(mbhc);
+
+	msleep(SWCH_REL_DEBOUNCE_TIME_MS);
+	wcd9xxx_start_hs_polling(mbhc);
+
+	pr_debug("%s: leave\n", __func__);
+	WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t wcd9xxx_hphl_ocp_irq(int irq, void *data)
+{
+	struct wcd9xxx_mbhc *mbhc = data;
+	struct snd_soc_codec *codec;
+
+	pr_info("%s: received HPHL OCP irq\n", __func__);
+
+	if (mbhc) {
+		codec = mbhc->codec;
+		if (mbhc->hphlocp_cnt++ < OCP_ATTEMPT) {
+			pr_info("%s: retry\n", __func__);
+			snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_OCP_CTL,
+					    0x10, 0x00);
+			snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_OCP_CTL,
+					    0x10, 0x10);
+		} else {
+			wcd9xxx_disable_irq(codec->control_data,
+					  WCD9XXX_IRQ_HPH_PA_OCPL_FAULT);
+			mbhc->hphlocp_cnt = 0;
+			mbhc->hph_status |= SND_JACK_OC_HPHL;
+			wcd9xxx_jack_report(&mbhc->headset_jack,
+					    mbhc->hph_status,
+					    WCD9XXX_JACK_MASK);
+		}
+	} else {
+		pr_err("%s: Bad wcd9xxx private data\n", __func__);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t wcd9xxx_hphr_ocp_irq(int irq, void *data)
+{
+	struct wcd9xxx_mbhc *mbhc = data;
+	struct snd_soc_codec *codec;
+
+	pr_info("%s: received HPHR OCP irq\n", __func__);
+	codec = mbhc->codec;
+	if (mbhc->hphrocp_cnt++ < OCP_ATTEMPT) {
+		pr_info("%s: retry\n", __func__);
+		snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_OCP_CTL, 0x10,
+				    0x00);
+		snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_OCP_CTL, 0x10,
+				    0x10);
+	} else {
+		wcd9xxx_disable_irq(mbhc->resmgr->core,
+				    WCD9XXX_IRQ_HPH_PA_OCPR_FAULT);
+		mbhc->hphrocp_cnt = 0;
+		mbhc->hph_status |= SND_JACK_OC_HPHR;
+		wcd9xxx_jack_report(&mbhc->headset_jack,
+				    mbhc->hph_status, WCD9XXX_JACK_MASK);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static int wcd9xxx_acdb_mclk_index(const int rate)
+{
+	if (rate == MCLK_RATE_12288KHZ)
+		return 0;
+	else if (rate == MCLK_RATE_9600KHZ)
+		return 1;
+	else {
+		BUG_ON(1);
+		return -EINVAL;
+	}
+}
+
+static void wcd9xxx_update_mbhc_clk_rate(struct wcd9xxx_mbhc *mbhc, u32 rate)
+{
+	u32 dce_wait, sta_wait;
+	u8 ncic, nmeas, navg;
+	void *calibration;
+	u8 *n_cic, *n_ready;
+	struct wcd9xxx_mbhc_btn_detect_cfg *btn_det;
+	u8 npoll = 4, nbounce_wait = 30;
+	struct snd_soc_codec *codec = mbhc->codec;
+	int idx = wcd9xxx_acdb_mclk_index(rate);
+	int idxmclk = wcd9xxx_acdb_mclk_index(mbhc->mbhc_cfg->mclk_rate);
+
+	pr_debug("%s: Updating clock rate dependents, rate = %u\n", __func__,
+		 rate);
+	calibration = mbhc->mbhc_cfg->calibration;
+
+	/*
+	 * First compute the DCE / STA wait times depending on tunable
+	 * parameters. The value is computed in microseconds
+	 */
+	btn_det = WCD9XXX_MBHC_CAL_BTN_DET_PTR(calibration);
+	n_ready = wcd9xxx_mbhc_cal_btn_det_mp(btn_det, MBHC_BTN_DET_N_READY);
+	n_cic = wcd9xxx_mbhc_cal_btn_det_mp(btn_det, MBHC_BTN_DET_N_CIC);
+	nmeas = WCD9XXX_MBHC_CAL_BTN_DET_PTR(calibration)->n_meas;
+	navg = WCD9XXX_MBHC_CAL_GENERAL_PTR(calibration)->mbhc_navg;
+
+	/* ncic stays with the same what we had during calibration */
+	ncic = n_cic[idxmclk];
+	dce_wait = (1000 * 512 * ncic * (nmeas + 1)) / (rate / 1000);
+	sta_wait = (1000 * 128 * (navg + 1)) / (rate / 1000);
+	mbhc->mbhc_data.t_dce = dce_wait;
+	mbhc->mbhc_data.t_sta = sta_wait;
+	mbhc->mbhc_data.t_sta_dce = ((1000 * 256) / (rate / 1000) *
+				     n_ready[idx]) + 10;
+
+	snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_TIMER_B1_CTL, n_ready[idx]);
+	snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_TIMER_B6_CTL, ncic);
+
+	if (rate == MCLK_RATE_12288KHZ) {
+		npoll = 4;
+		nbounce_wait = 30;
+	} else if (rate == MCLK_RATE_9600KHZ) {
+		npoll = 3;
+		nbounce_wait = 23;
+	}
+
+	snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_TIMER_B2_CTL, npoll);
+	snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_TIMER_B3_CTL, nbounce_wait);
+	pr_debug("%s: leave\n", __func__);
+}
+
+static void wcd9xxx_mbhc_cal(struct wcd9xxx_mbhc *mbhc)
+{
+	u8 cfilt_mode, bg_mode;
+	struct snd_soc_codec *codec = mbhc->codec;
+
+	pr_debug("%s: enter\n", __func__);
+	wcd9xxx_disable_irq(codec->control_data, WCD9XXX_IRQ_MBHC_POTENTIAL);
+	wcd9xxx_turn_onoff_rel_detection(codec, false);
+
+	/* t_dce and t_sta are updated by wcd9xxx_update_mbhc_clk_rate() */
+	WARN_ON(!mbhc->mbhc_data.t_dce);
+	WARN_ON(!mbhc->mbhc_data.t_sta);
+
+	/*
+	 * LDOH and CFILT are already configured during pdata handling.
+	 * Only need to make sure CFILT and bandgap are in Fast mode.
+	 * Need to restore defaults once calculation is done.
+	 */
+	cfilt_mode = snd_soc_read(codec, mbhc->mbhc_bias_regs.cfilt_ctl);
+	snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.cfilt_ctl, 0x40, 0x00);
+	bg_mode = snd_soc_update_bits(codec, WCD9XXX_A_BIAS_CENTRAL_BG_CTL,
+				      0x02, 0x02);
+
+	/*
+	 * Micbias, CFILT, LDOH, MBHC MUX mode settings
+	 * to perform ADC calibration
+	 */
+	snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x60,
+			    mbhc->mbhc_cfg->micbias << 5);
+	snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01, 0x00);
+	snd_soc_update_bits(codec, WCD9XXX_A_LDO_H_MODE_1, 0x60, 0x60);
+	snd_soc_write(codec, WCD9XXX_A_TX_7_MBHC_TEST_CTL, 0x78);
+	snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x04, 0x04);
+
+	/* DCE measurement for 0 volts */
+	snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x0A);
+	snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x04);
+	snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x02);
+	snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x81);
+	usleep_range(100, 100);
+	snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x04);
+	usleep_range(mbhc->mbhc_data.t_dce, mbhc->mbhc_data.t_dce);
+	mbhc->mbhc_data.dce_z = wcd9xxx_read_dce_result(codec);
+
+	/* DCE measurment for MB voltage */
+	snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x0A);
+	snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x02);
+	snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x82);
+	usleep_range(100, 100);
+	snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x04);
+	usleep_range(mbhc->mbhc_data.t_dce, mbhc->mbhc_data.t_dce);
+	mbhc->mbhc_data.dce_mb = wcd9xxx_read_dce_result(codec);
+
+	/* STA measuremnt for 0 volts */
+	snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x0A);
+	snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x02);
+	snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x02);
+	snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x81);
+	usleep_range(100, 100);
+	snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x02);
+	usleep_range(mbhc->mbhc_data.t_sta, mbhc->mbhc_data.t_sta);
+	mbhc->mbhc_data.sta_z = wcd9xxx_read_sta_result(codec);
+
+	/* STA Measurement for MB Voltage */
+	snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x82);
+	usleep_range(100, 100);
+	snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x02);
+	usleep_range(mbhc->mbhc_data.t_sta, mbhc->mbhc_data.t_sta);
+	mbhc->mbhc_data.sta_mb = wcd9xxx_read_sta_result(codec);
+
+	/* Restore default settings. */
+	snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x04, 0x00);
+	snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.cfilt_ctl, 0x40,
+			    cfilt_mode);
+	snd_soc_update_bits(codec, WCD9XXX_A_BIAS_CENTRAL_BG_CTL, 0x02,
+			    bg_mode);
+
+	snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x84);
+	usleep_range(100, 100);
+
+	wcd9xxx_enable_irq(codec->control_data, WCD9XXX_IRQ_MBHC_POTENTIAL);
+	wcd9xxx_turn_onoff_rel_detection(codec, true);
+	pr_debug("%s: leave\n", __func__);
+}
+
+static void wcd9xxx_mbhc_setup(struct wcd9xxx_mbhc *mbhc)
+{
+	int n;
+	u8 *gain;
+	struct wcd9xxx_mbhc_general_cfg *generic;
+	struct wcd9xxx_mbhc_btn_detect_cfg *btn_det;
+	struct snd_soc_codec *codec = mbhc->codec;
+	const int idx = wcd9xxx_acdb_mclk_index(mbhc->mbhc_cfg->mclk_rate);
+
+	pr_debug("%s: enter\n", __func__);
+	generic = WCD9XXX_MBHC_CAL_GENERAL_PTR(mbhc->mbhc_cfg->calibration);
+	btn_det = WCD9XXX_MBHC_CAL_BTN_DET_PTR(mbhc->mbhc_cfg->calibration);
+
+	for (n = 0; n < 8; n++) {
+		snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_FIR_B1_CFG,
+				    0x07, n);
+		snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_FIR_B2_CFG,
+			      btn_det->c[n]);
+	}
+
+	snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B2_CTL, 0x07,
+			    btn_det->nc);
+
+	snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_TIMER_B4_CTL, 0x70,
+			    generic->mbhc_nsa << 4);
+
+	snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_TIMER_B4_CTL, 0x0F,
+			    btn_det->n_meas);
+
+	snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_TIMER_B5_CTL,
+		      generic->mbhc_navg);
+
+	snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x80, 0x80);
+
+	snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x78,
+			    btn_det->mbhc_nsc << 3);
+
+	snd_soc_update_bits(codec, mbhc->resmgr->reg_addr->micb_4_mbhc, 0x03,
+			    MBHC_MICBIAS2);
+
+	snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x02, 0x02);
+
+	snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_2, 0xF0, 0xF0);
+
+	gain = wcd9xxx_mbhc_cal_btn_det_mp(btn_det, MBHC_BTN_DET_GAIN);
+	snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B2_CTL, 0x78,
+			    gain[idx] << 3);
+
+	pr_debug("%s: leave\n", __func__);
+}
+
+static int wcd9xxx_setup_jack_detect_irq(struct wcd9xxx_mbhc *mbhc)
+{
+	int ret = 0;
+	void *core = mbhc->resmgr->core;
+
+	if (mbhc->mbhc_cfg->gpio) {
+		ret = request_threaded_irq(mbhc->mbhc_cfg->gpio_irq, NULL,
+					   wcd9xxx_mech_plug_detect_irq,
+					   (IRQF_TRIGGER_RISING |
+					    IRQF_TRIGGER_FALLING |
+					    IRQF_DISABLED),
+					   "headset detect", mbhc);
+		if (ret) {
+			pr_err("%s: Failed to request gpio irq %d\n", __func__,
+			       mbhc->mbhc_cfg->gpio_irq);
+		} else {
+			ret = enable_irq_wake(mbhc->mbhc_cfg->gpio_irq);
+			if (ret)
+				pr_err("%s: Failed to enable wake up irq %d\n",
+				       __func__, mbhc->mbhc_cfg->gpio_irq);
+		}
+	} else if (mbhc->mbhc_cfg->insert_detect) {
+		/* Enable HPHL_10K_SW */
+		snd_soc_update_bits(mbhc->codec, WCD9XXX_A_RX_HPH_OCP_CTL,
+				    1 << 1, 1 << 1);
+		ret = wcd9xxx_request_irq(core, WCD9XXX_IRQ_MBHC_JACK_SWITCH,
+					  wcd9xxx_mech_plug_detect_irq,
+					  "Jack Detect",
+					  mbhc);
+		if (ret)
+			pr_err("%s: Failed to request insert detect irq %d\n",
+			       __func__, WCD9XXX_IRQ_MBHC_JACK_SWITCH);
+	}
+
+	return ret;
+}
+
+static int wcd9xxx_init_and_calibrate(struct wcd9xxx_mbhc *mbhc)
+{
+	int ret = 0;
+	struct snd_soc_codec *codec = mbhc->codec;
+
+	pr_debug("%s: enter\n", __func__);
+
+	/* Enable MCLK during calibration */
+	wcd9xxx_onoff_ext_mclk(mbhc, true);
+	wcd9xxx_mbhc_setup(mbhc);
+	wcd9xxx_mbhc_cal(mbhc);
+	wcd9xxx_mbhc_calc_thres(mbhc);
+	wcd9xxx_onoff_ext_mclk(mbhc, false);
+	wcd9xxx_calibrate_hs_polling(mbhc);
+
+	/* Enable Mic Bias pull down and HPH Switch to GND */
+	snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01, 0x01);
+	snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x01, 0x01);
+	INIT_WORK(&mbhc->correct_plug_swch, wcd9xxx_correct_swch_plug);
+
+	if (!IS_ERR_VALUE(ret)) {
+		snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_OCP_CTL, 0x10,
+				    0x10);
+		wcd9xxx_enable_irq(codec->control_data,
+				   WCD9XXX_IRQ_HPH_PA_OCPL_FAULT);
+		wcd9xxx_enable_irq(codec->control_data,
+				   WCD9XXX_IRQ_HPH_PA_OCPR_FAULT);
+
+		/* Initialize mechanical mbhc */
+		ret = wcd9xxx_setup_jack_detect_irq(mbhc);
+
+		if (!ret && mbhc->mbhc_cfg->gpio) {
+			/* Requested with IRQF_DISABLED */
+			enable_irq(mbhc->mbhc_cfg->gpio_irq);
+
+			/* Bootup time detection */
+			wcd9xxx_swch_irq_handler(mbhc);
+		} else if (!ret && mbhc->mbhc_cfg->insert_detect) {
+			pr_debug("%s: Setting up codec own insert detection\n",
+				 __func__);
+			/* Setup for insertion detection */
+			wcd9xxx_insert_detect_setup(mbhc, true);
+		}
+	}
+
+	pr_debug("%s: leave\n", __func__);
+
+	return ret;
+}
+
+static void wcd9xxx_mbhc_fw_read(struct work_struct *work)
+{
+	struct delayed_work *dwork;
+	struct wcd9xxx_mbhc *mbhc;
+	struct snd_soc_codec *codec;
+	const struct firmware *fw;
+	int ret = -1, retry = 0;
+
+	dwork = to_delayed_work(work);
+	mbhc = container_of(dwork, struct wcd9xxx_mbhc, mbhc_firmware_dwork);
+	codec = mbhc->codec;
+
+	while (retry < FW_READ_ATTEMPTS) {
+		retry++;
+		pr_info("%s:Attempt %d to request MBHC firmware\n",
+			__func__, retry);
+		ret = request_firmware(&fw, "wcd9320/wcd9320_mbhc.bin",
+				       codec->dev);
+
+		if (ret != 0) {
+			usleep_range(FW_READ_TIMEOUT, FW_READ_TIMEOUT);
+		} else {
+			pr_info("%s: MBHC Firmware read succesful\n", __func__);
+			break;
+		}
+	}
+
+	if (ret != 0) {
+		pr_err("%s: Cannot load MBHC firmware use default cal\n",
+		       __func__);
+	} else if (wcd9xxx_mbhc_fw_validate(fw) == false) {
+		pr_err("%s: Invalid MBHC cal data size use default cal\n",
+		       __func__);
+		release_firmware(fw);
+	} else {
+		mbhc->mbhc_cfg->calibration = (void *)fw->data;
+		mbhc->mbhc_fw = fw;
+	}
+
+	(void) wcd9xxx_init_and_calibrate(mbhc);
+}
+
+#ifdef CONFIG_DEBUG_FS
+ssize_t codec_mbhc_debug_read(struct file *file, char __user *buf,
+			      size_t count, loff_t *pos)
+{
+	const int size = 768;
+	char buffer[size];
+	int n = 0;
+	struct wcd9xxx_mbhc *mbhc = file->private_data;
+	const struct mbhc_internal_cal_data *p = &mbhc->mbhc_data;
+	const s16 v_ins_hu_cur = wcd9xxx_get_current_v_ins(mbhc, true);
+	const s16 v_ins_h_cur = wcd9xxx_get_current_v_ins(mbhc, false);
+
+	n = scnprintf(buffer, size - n, "dce_z = %x(%dmv)\n",  p->dce_z,
+		      wcd9xxx_codec_sta_dce_v(mbhc, 1, p->dce_z));
+	n += scnprintf(buffer + n, size - n, "dce_mb = %x(%dmv)\n",
+		       p->dce_mb, wcd9xxx_codec_sta_dce_v(mbhc, 1, p->dce_mb));
+	n += scnprintf(buffer + n, size - n, "sta_z = %x(%dmv)\n",
+		       p->sta_z, wcd9xxx_codec_sta_dce_v(mbhc, 0, p->sta_z));
+	n += scnprintf(buffer + n, size - n, "sta_mb = %x(%dmv)\n",
+		       p->sta_mb, wcd9xxx_codec_sta_dce_v(mbhc, 0, p->sta_mb));
+	n += scnprintf(buffer + n, size - n, "t_dce = %x\n",  p->t_dce);
+	n += scnprintf(buffer + n, size - n, "t_sta = %x\n",  p->t_sta);
+	n += scnprintf(buffer + n, size - n, "micb_mv = %dmv\n",
+		       p->micb_mv);
+	n += scnprintf(buffer + n, size - n, "v_ins_hu = %x(%dmv)%s\n",
+		       p->v_ins_hu,
+		       wcd9xxx_codec_sta_dce_v(mbhc, 0, p->v_ins_hu),
+		       p->v_ins_hu == v_ins_hu_cur ? "*" : "");
+	n += scnprintf(buffer + n, size - n, "v_ins_h = %x(%dmv)%s\n",
+		       p->v_ins_h, wcd9xxx_codec_sta_dce_v(mbhc, 1, p->v_ins_h),
+		       p->v_ins_h == v_ins_h_cur ? "*" : "");
+	n += scnprintf(buffer + n, size - n, "adj_v_ins_hu = %x(%dmv)%s\n",
+		       p->adj_v_ins_hu,
+		       wcd9xxx_codec_sta_dce_v(mbhc, 0, p->adj_v_ins_hu),
+		       p->adj_v_ins_hu == v_ins_hu_cur ? "*" : "");
+	n += scnprintf(buffer + n, size - n, "adj_v_ins_h = %x(%dmv)%s\n",
+		       p->adj_v_ins_h,
+		       wcd9xxx_codec_sta_dce_v(mbhc, 1, p->adj_v_ins_h),
+		       p->adj_v_ins_h == v_ins_h_cur ? "*" : "");
+	n += scnprintf(buffer + n, size - n, "v_b1_hu = %x(%dmv)\n",
+		       p->v_b1_hu,
+		       wcd9xxx_codec_sta_dce_v(mbhc, 0, p->v_b1_hu));
+	n += scnprintf(buffer + n, size - n, "v_b1_h = %x(%dmv)\n",
+		       p->v_b1_h, wcd9xxx_codec_sta_dce_v(mbhc, 1, p->v_b1_h));
+	n += scnprintf(buffer + n, size - n, "v_b1_huc = %x(%dmv)\n",
+		       p->v_b1_huc,
+		       wcd9xxx_codec_sta_dce_v(mbhc, 1, p->v_b1_huc));
+	n += scnprintf(buffer + n, size - n, "v_brh = %x(%dmv)\n",
+		       p->v_brh, wcd9xxx_codec_sta_dce_v(mbhc, 1, p->v_brh));
+	n += scnprintf(buffer + n, size - n, "v_brl = %x(%dmv)\n",  p->v_brl,
+		       wcd9xxx_codec_sta_dce_v(mbhc, 0, p->v_brl));
+	n += scnprintf(buffer + n, size - n, "v_no_mic = %x(%dmv)\n",
+		       p->v_no_mic,
+		       wcd9xxx_codec_sta_dce_v(mbhc, 0, p->v_no_mic));
+	n += scnprintf(buffer + n, size - n, "v_inval_ins_low = %d\n",
+		       p->v_inval_ins_low);
+	n += scnprintf(buffer + n, size - n, "v_inval_ins_high = %d\n",
+		       p->v_inval_ins_high);
+	n += scnprintf(buffer + n, size - n, "Insert detect insert = %d\n",
+		       !wcd9xxx_swch_level_remove(mbhc));
+	buffer[n] = 0;
+
+	return simple_read_from_buffer(buf, count, pos, buffer, n);
+}
+
+static int codec_debug_open(struct inode *inode, struct file *file)
+{
+	file->private_data = inode->i_private;
+	return 0;
+}
+
+static ssize_t codec_debug_write(struct file *filp,
+				 const char __user *ubuf, size_t cnt,
+				 loff_t *ppos)
+{
+	char lbuf[32];
+	char *buf;
+	int rc;
+	struct wcd9xxx_mbhc *mbhc = filp->private_data;
+
+	if (cnt > sizeof(lbuf) - 1)
+		return -EINVAL;
+
+	rc = copy_from_user(lbuf, ubuf, cnt);
+	if (rc)
+		return -EFAULT;
+
+	lbuf[cnt] = '\0';
+	buf = (char *)lbuf;
+	mbhc->no_mic_headset_override = (*strsep(&buf, " ") == '0') ?
+					     false : true;
+	return rc;
+}
+
+static const struct file_operations mbhc_trrs_debug_ops = {
+	.open = codec_debug_open,
+	.write = codec_debug_write,
+};
+
+static const struct file_operations mbhc_debug_ops = {
+	.open = codec_debug_open,
+	.read = codec_mbhc_debug_read,
+};
+
+static void wcd9xxx_init_debugfs(struct wcd9xxx_mbhc *mbhc)
+{
+	mbhc->debugfs_poke =
+	    debugfs_create_file("TRRS", S_IFREG | S_IRUGO, NULL, mbhc,
+				&mbhc_trrs_debug_ops);
+	mbhc->debugfs_mbhc =
+	    debugfs_create_file("wcd9xxx_mbhc", S_IFREG | S_IRUGO,
+				NULL, mbhc, &mbhc_debug_ops);
+}
+
+static void wcd9xxx_cleanup_debugfs(struct wcd9xxx_mbhc *mbhc)
+{
+	debugfs_remove(mbhc->debugfs_poke);
+	debugfs_remove(mbhc->debugfs_mbhc);
+}
+#else
+static void wcd9xxx_init_debugfs(struct wcd9xxx_mbhc *mbhc)
+{
+}
+
+static void wcd9xxx_cleanup_debugfs(struct wcd9xxx_mbhc *mbhc)
+{
+}
+#endif
+
+int wcd9xxx_mbhc_start(struct wcd9xxx_mbhc *mbhc,
+		       struct wcd9xxx_mbhc_config *mbhc_cfg)
+{
+	int rc = 0;
+	struct snd_soc_codec *codec = mbhc->codec;
+
+	pr_debug("%s: enter\n", __func__);
+
+	if (!codec) {
+		pr_err("%s: no codec\n", __func__);
+		return -EINVAL;
+	}
+
+	if (mbhc_cfg->mclk_rate != MCLK_RATE_12288KHZ &&
+	    mbhc_cfg->mclk_rate != MCLK_RATE_9600KHZ) {
+		pr_err("Error: unsupported clock rate %d\n",
+		       mbhc_cfg->mclk_rate);
+		return -EINVAL;
+	}
+
+	/* Save mbhc config */
+	mbhc->mbhc_cfg = mbhc_cfg;
+
+	/* Get HW specific mbhc registers' address */
+	wcd9xxx_get_mbhc_micbias_regs(mbhc, &mbhc->mbhc_bias_regs);
+
+	/* Put CFILT in fast mode by default */
+	snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.cfilt_ctl,
+			    0x40, WCD9XXX_CFILT_FAST_MODE);
+
+	if (!mbhc->mbhc_cfg->read_fw_bin)
+		rc = wcd9xxx_init_and_calibrate(mbhc);
+	else
+		schedule_delayed_work(&mbhc->mbhc_firmware_dwork,
+				      usecs_to_jiffies(FW_READ_TIMEOUT));
+
+	pr_debug("%s: leave %d\n", __func__, rc);
+	return rc;
+}
+EXPORT_SYMBOL_GPL(wcd9xxx_mbhc_start);
+
+static enum wcd9xxx_micbias_num
+wcd9xxx_event_to_micbias(const enum wcd9xxx_notify_event event)
+{
+	enum wcd9xxx_micbias_num ret;
+	switch (event) {
+	case WCD9XXX_EVENT_PRE_MICBIAS_1_ON:
+		ret = MBHC_MICBIAS1;
+	case WCD9XXX_EVENT_PRE_MICBIAS_2_ON:
+		ret = MBHC_MICBIAS2;
+	case WCD9XXX_EVENT_PRE_MICBIAS_3_ON:
+		ret = MBHC_MICBIAS3;
+	case WCD9XXX_EVENT_PRE_MICBIAS_4_ON:
+		ret = MBHC_MICBIAS4;
+	default:
+		ret = MBHC_MICBIAS_INVALID;
+	}
+	return ret;
+}
+
+static int wcd9xxx_event_to_cfilt(const enum wcd9xxx_notify_event event)
+{
+	int ret;
+	switch (event) {
+	case WCD9XXX_EVENT_PRE_CFILT_1_OFF:
+	case WCD9XXX_EVENT_POST_CFILT_1_OFF:
+	case WCD9XXX_EVENT_PRE_CFILT_1_ON:
+	case WCD9XXX_EVENT_POST_CFILT_1_ON:
+		ret = WCD9XXX_CFILT1_SEL;
+		break;
+	case WCD9XXX_EVENT_PRE_CFILT_2_OFF:
+	case WCD9XXX_EVENT_POST_CFILT_2_OFF:
+	case WCD9XXX_EVENT_PRE_CFILT_2_ON:
+	case WCD9XXX_EVENT_POST_CFILT_2_ON:
+		ret = WCD9XXX_CFILT2_SEL;
+		break;
+	case WCD9XXX_EVENT_PRE_CFILT_3_OFF:
+	case WCD9XXX_EVENT_POST_CFILT_3_OFF:
+	case WCD9XXX_EVENT_PRE_CFILT_3_ON:
+	case WCD9XXX_EVENT_POST_CFILT_3_ON:
+		ret = WCD9XXX_CFILT3_SEL;
+		break;
+	default:
+		ret = -1;
+	}
+	return ret;
+}
+
+static int wcd9xxx_get_mbhc_cfilt_sel(struct wcd9xxx_mbhc *mbhc)
+{
+	int cfilt;
+	const struct wcd9xxx_pdata *pdata = mbhc->resmgr->pdata;
+
+	switch (mbhc->mbhc_cfg->micbias) {
+	case MBHC_MICBIAS1:
+		cfilt = pdata->micbias.bias1_cfilt_sel;
+		break;
+	case MBHC_MICBIAS2:
+		cfilt = pdata->micbias.bias2_cfilt_sel;
+		break;
+	case MBHC_MICBIAS3:
+		cfilt = pdata->micbias.bias3_cfilt_sel;
+		break;
+	case MBHC_MICBIAS4:
+		cfilt = pdata->micbias.bias4_cfilt_sel;
+		break;
+	default:
+		cfilt = MBHC_MICBIAS_INVALID;
+		break;
+	}
+	return cfilt;
+}
+
+static int wcd9xxx_event_notify(struct notifier_block *self, unsigned long val,
+				void *data)
+{
+	int ret = 0;
+	struct wcd9xxx_mbhc *mbhc = ((struct wcd9xxx_resmgr *)data)->mbhc;
+	struct snd_soc_codec *codec = mbhc->codec;
+	enum wcd9xxx_notify_event event = (enum wcd9xxx_notify_event)val;
+
+	pr_debug("%s: enter event %s(%d)\n", __func__,
+		 wcd9xxx_get_event_string(event), event);
+
+	switch (event) {
+	/* MICBIAS usage change */
+	case WCD9XXX_EVENT_PRE_MICBIAS_1_ON:
+	case WCD9XXX_EVENT_PRE_MICBIAS_2_ON:
+	case WCD9XXX_EVENT_PRE_MICBIAS_3_ON:
+	case WCD9XXX_EVENT_PRE_MICBIAS_4_ON:
+		if (mbhc->mbhc_cfg->micbias == wcd9xxx_event_to_micbias(event))
+			wcd9xxx_switch_micbias(mbhc, 0);
+		break;
+	case WCD9XXX_EVENT_POST_MICBIAS_1_ON:
+	case WCD9XXX_EVENT_POST_MICBIAS_2_ON:
+	case WCD9XXX_EVENT_POST_MICBIAS_3_ON:
+	case WCD9XXX_EVENT_POST_MICBIAS_4_ON:
+		if (mbhc->mbhc_cfg->micbias ==
+		    wcd9xxx_event_to_micbias(event) &&
+		    wcd9xxx_mbhc_polling(mbhc)) {
+			/* if polling is on, restart it */
+			wcd9xxx_pause_hs_polling(mbhc);
+			wcd9xxx_start_hs_polling(mbhc);
+		}
+		break;
+	case WCD9XXX_EVENT_POST_MICBIAS_1_OFF:
+	case WCD9XXX_EVENT_POST_MICBIAS_2_OFF:
+	case WCD9XXX_EVENT_POST_MICBIAS_3_OFF:
+	case WCD9XXX_EVENT_POST_MICBIAS_4_OFF:
+		if (mbhc->mbhc_cfg->micbias ==
+		    wcd9xxx_event_to_micbias(event) &&
+		    wcd9xxx_is_hph_pa_on(codec))
+			wcd9xxx_switch_micbias(mbhc, 1);
+		break;
+	/* PA usage change */
+	case WCD9XXX_EVENT_PRE_HPHL_PA_ON:
+		if (!(snd_soc_read(codec, mbhc->mbhc_bias_regs.ctl_reg & 0x80)))
+			/* if micbias is enabled, switch to vddio */
+			wcd9xxx_switch_micbias(mbhc, 1);
+		break;
+	case WCD9XXX_EVENT_PRE_HPHR_PA_ON:
+		/* Not used now */
+		break;
+	case WCD9XXX_EVENT_POST_HPHL_PA_OFF:
+		/* if HPH PAs are off, report OCP and switch back to CFILT */
+		clear_bit(WCD9XXX_HPHL_PA_OFF_ACK, &mbhc->hph_pa_dac_state);
+		clear_bit(WCD9XXX_HPHL_DAC_OFF_ACK, &mbhc->hph_pa_dac_state);
+		if (mbhc->hph_status & SND_JACK_OC_HPHL)
+			hphlocp_off_report(mbhc, SND_JACK_OC_HPHL);
+		wcd9xxx_switch_micbias(mbhc, 0);
+		break;
+	case WCD9XXX_EVENT_POST_HPHR_PA_OFF:
+		/* if HPH PAs are off, report OCP and switch back to CFILT */
+		clear_bit(WCD9XXX_HPHR_PA_OFF_ACK, &mbhc->hph_pa_dac_state);
+		clear_bit(WCD9XXX_HPHR_DAC_OFF_ACK, &mbhc->hph_pa_dac_state);
+		if (mbhc->hph_status & SND_JACK_OC_HPHR)
+			hphrocp_off_report(mbhc, SND_JACK_OC_HPHL);
+		wcd9xxx_switch_micbias(mbhc, 0);
+		break;
+	/* Clock usage change */
+	case WCD9XXX_EVENT_PRE_MCLK_ON:
+		break;
+	case WCD9XXX_EVENT_POST_MCLK_ON:
+		/* Change to lower TxAAF frequency */
+		snd_soc_update_bits(codec, WCD9XXX_A_TX_COM_BIAS, 1 << 4,
+				    1 << 4);
+		/* Re-calibrate clock rate dependent values */
+		wcd9xxx_update_mbhc_clk_rate(mbhc, mbhc->mbhc_cfg->mclk_rate);
+		/* If clock source changes, stop and restart polling */
+		if (wcd9xxx_mbhc_polling(mbhc)) {
+			wcd9xxx_calibrate_hs_polling(mbhc);
+			wcd9xxx_start_hs_polling(mbhc);
+		}
+		break;
+	case WCD9XXX_EVENT_PRE_MCLK_OFF:
+		/* If clock source changes, stop and restart polling */
+		if (wcd9xxx_mbhc_polling(mbhc))
+			wcd9xxx_pause_hs_polling(mbhc);
+		break;
+	case WCD9XXX_EVENT_POST_MCLK_OFF:
+		break;
+	case WCD9XXX_EVENT_PRE_RCO_ON:
+		break;
+	case WCD9XXX_EVENT_POST_RCO_ON:
+		/* Change to higher TxAAF frequency */
+		snd_soc_update_bits(codec, WCD9XXX_A_TX_COM_BIAS, 1 << 4,
+				    0 << 4);
+		/* Re-calibrate clock rate dependent values */
+		wcd9xxx_update_mbhc_clk_rate(mbhc, WCD9XXX_RCO_CLK_RATE);
+		/* If clock source changes, stop and restart polling */
+		if (wcd9xxx_mbhc_polling(mbhc)) {
+			wcd9xxx_calibrate_hs_polling(mbhc);
+			wcd9xxx_start_hs_polling(mbhc);
+		}
+		break;
+	case WCD9XXX_EVENT_PRE_RCO_OFF:
+		/* If clock source changes, stop and restart polling */
+		if (wcd9xxx_mbhc_polling(mbhc))
+			wcd9xxx_pause_hs_polling(mbhc);
+		break;
+	case WCD9XXX_EVENT_POST_RCO_OFF:
+		break;
+	/* CFILT usage change */
+	case WCD9XXX_EVENT_PRE_CFILT_1_ON:
+	case WCD9XXX_EVENT_PRE_CFILT_2_ON:
+	case WCD9XXX_EVENT_PRE_CFILT_3_ON:
+		if (wcd9xxx_get_mbhc_cfilt_sel(mbhc) ==
+		    wcd9xxx_event_to_cfilt(event))
+			/*
+			 * Switch CFILT to slow mode if MBHC CFILT is being
+			 * used.
+			 */
+			wcd9xxx_codec_switch_cfilt_mode(mbhc, false);
+		break;
+	case WCD9XXX_EVENT_POST_CFILT_1_OFF:
+	case WCD9XXX_EVENT_POST_CFILT_2_OFF:
+	case WCD9XXX_EVENT_POST_CFILT_3_OFF:
+		if (wcd9xxx_get_mbhc_cfilt_sel(mbhc) ==
+		    wcd9xxx_event_to_cfilt(event))
+			/*
+			 * Switch CFILT to fast mode if MBHC CFILT is not
+			 * used anymore.
+			 */
+			wcd9xxx_codec_switch_cfilt_mode(mbhc, true);
+		break;
+	/* System resume */
+	case WCD9XXX_EVENT_POST_RESUME:
+		mbhc->mbhc_last_resume = jiffies;
+		break;
+	/* BG mode chage */
+	case WCD9XXX_EVENT_PRE_BG_OFF:
+	case WCD9XXX_EVENT_POST_BG_OFF:
+	case WCD9XXX_EVENT_PRE_BG_AUDIO_ON:
+	case WCD9XXX_EVENT_POST_BG_AUDIO_ON:
+	case WCD9XXX_EVENT_PRE_BG_MBHC_ON:
+	case WCD9XXX_EVENT_POST_BG_MBHC_ON:
+		/* Not used for now */
+		break;
+	default:
+		WARN(1, "Unknown event %d\n", event);
+		ret = -EINVAL;
+	}
+
+	pr_debug("%s: leave\n", __func__);
+
+	return 0;
+}
+
+/*
+ * wcd9xxx_mbhc_init : initialize MBHC internal structures.
+ *
+ * NOTE: mbhc->mbhc_cfg is not YET configure so shouldn't be used
+ */
+int wcd9xxx_mbhc_init(struct wcd9xxx_mbhc *mbhc, struct wcd9xxx_resmgr *resmgr,
+		      struct snd_soc_codec *codec)
+{
+	int ret;
+	void *core;
+
+	pr_debug("%s: enter\n", __func__);
+	memset(&mbhc->mbhc_bias_regs, 0, sizeof(struct mbhc_micbias_regs));
+	memset(&mbhc->mbhc_data, 0, sizeof(struct mbhc_internal_cal_data));
+
+	mbhc->mbhc_data.t_sta_dce = DEFAULT_DCE_STA_WAIT;
+	mbhc->mbhc_data.t_dce = DEFAULT_DCE_WAIT;
+	mbhc->mbhc_data.t_sta = DEFAULT_STA_WAIT;
+	mbhc->mbhc_micbias_switched = false;
+	mbhc->polling_active = false;
+	mbhc->mbhc_state = MBHC_STATE_NONE;
+	mbhc->in_swch_irq_handler = false;
+	mbhc->current_plug = PLUG_TYPE_NONE;
+	mbhc->lpi_enabled = false;
+	mbhc->no_mic_headset_override = false;
+	mbhc->mbhc_last_resume = 0;
+	mbhc->codec = codec;
+	mbhc->resmgr = resmgr;
+	mbhc->resmgr->mbhc = mbhc;
+
+	ret = snd_soc_jack_new(codec, "Headset Jack", WCD9XXX_JACK_MASK,
+			       &mbhc->headset_jack);
+	if (ret) {
+		pr_err("%s: Failed to create new jack\n", __func__);
+		return ret;
+	}
+
+	ret = snd_soc_jack_new(codec, "Button Jack", WCD9XXX_JACK_BUTTON_MASK,
+			       &mbhc->button_jack);
+	if (ret) {
+		pr_err("Failed to create new jack\n");
+		return ret;
+	}
+
+	mbhc->mbhc_cfg = kzalloc(sizeof(*mbhc->mbhc_cfg), GFP_KERNEL);
+	if (!mbhc->mbhc_cfg)
+		return -ENOMEM;
+
+	INIT_DELAYED_WORK(&mbhc->mbhc_firmware_dwork, wcd9xxx_mbhc_fw_read);
+	INIT_DELAYED_WORK(&mbhc->mbhc_btn_dwork, wcd9xxx_btn_lpress_fn);
+	INIT_DELAYED_WORK(&mbhc->mbhc_insert_dwork, wcd9xxx_mbhc_insert_work);
+
+	/* Register event notifier */
+	mbhc->nblock.notifier_call = wcd9xxx_event_notify;
+	ret = wcd9xxx_resmgr_register_notifier(mbhc->resmgr, &mbhc->nblock);
+	if (ret) {
+		pr_err("%s: Failed to register notifier %d\n", __func__, ret);
+		return ret;
+	}
+
+	wcd9xxx_init_debugfs(mbhc);
+
+	core = mbhc->resmgr->core;
+	ret = wcd9xxx_request_irq(core, WCD9XXX_IRQ_MBHC_INSERTION,
+				  wcd9xxx_hs_insert_irq,
+				  "Headset insert detect", mbhc);
+	if (ret) {
+		pr_err("%s: Failed to request irq %d\n", __func__,
+		       WCD9XXX_IRQ_MBHC_INSERTION);
+		goto err_insert_irq;
+	}
+	wcd9xxx_disable_irq(core, WCD9XXX_IRQ_MBHC_INSERTION);
+
+	ret = wcd9xxx_request_irq(core, WCD9XXX_IRQ_MBHC_REMOVAL,
+				  wcd9xxx_hs_remove_irq,
+				  "Headset remove detect", mbhc);
+	if (ret) {
+		pr_err("%s: Failed to request irq %d\n", __func__,
+			WCD9XXX_IRQ_MBHC_REMOVAL);
+		goto err_remove_irq;
+	}
+
+	ret = wcd9xxx_request_irq(core, WCD9XXX_IRQ_MBHC_POTENTIAL,
+				  wcd9xxx_dce_handler, "DC Estimation detect",
+				  mbhc);
+	if (ret) {
+		pr_err("%s: Failed to request irq %d\n", __func__,
+		       WCD9XXX_IRQ_MBHC_POTENTIAL);
+		goto err_potential_irq;
+	}
+
+	ret = wcd9xxx_request_irq(core, WCD9XXX_IRQ_MBHC_RELEASE,
+				  wcd9xxx_release_handler,
+				  "Button Release detect", mbhc);
+	if (ret) {
+		pr_err("%s: Failed to request irq %d\n", __func__,
+			WCD9XXX_IRQ_MBHC_RELEASE);
+		goto err_release_irq;
+	}
+
+	ret = wcd9xxx_request_irq(core, WCD9XXX_IRQ_HPH_PA_OCPL_FAULT,
+				  wcd9xxx_hphl_ocp_irq, "HPH_L OCP detect",
+				  mbhc);
+	if (ret) {
+		pr_err("%s: Failed to request irq %d\n", __func__,
+		       WCD9XXX_IRQ_HPH_PA_OCPL_FAULT);
+		goto err_hphl_ocp_irq;
+	}
+	wcd9xxx_disable_irq(core, WCD9XXX_IRQ_HPH_PA_OCPL_FAULT);
+
+	ret = wcd9xxx_request_irq(core, WCD9XXX_IRQ_HPH_PA_OCPR_FAULT,
+				  wcd9xxx_hphr_ocp_irq, "HPH_R OCP detect",
+				  mbhc);
+	if (ret) {
+		pr_err("%s: Failed to request irq %d\n", __func__,
+		       WCD9XXX_IRQ_HPH_PA_OCPR_FAULT);
+		goto err_hphr_ocp_irq;
+	}
+	wcd9xxx_disable_irq(codec->control_data, WCD9XXX_IRQ_HPH_PA_OCPR_FAULT);
+
+	pr_debug("%s: leave ret %d\n", __func__, ret);
+	return ret;
+
+err_hphr_ocp_irq:
+	wcd9xxx_free_irq(core, WCD9XXX_IRQ_HPH_PA_OCPL_FAULT, mbhc);
+err_hphl_ocp_irq:
+	wcd9xxx_free_irq(core, WCD9XXX_IRQ_MBHC_RELEASE, mbhc);
+err_release_irq:
+	wcd9xxx_free_irq(core, WCD9XXX_IRQ_MBHC_POTENTIAL, mbhc);
+err_potential_irq:
+	wcd9xxx_free_irq(core, WCD9XXX_IRQ_MBHC_REMOVAL, mbhc);
+err_remove_irq:
+	wcd9xxx_free_irq(core, WCD9XXX_IRQ_MBHC_INSERTION, mbhc);
+err_insert_irq:
+	wcd9xxx_resmgr_unregister_notifier(mbhc->resmgr, &mbhc->nblock);
+
+	pr_debug("%s: leave ret %d\n", __func__, ret);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(wcd9xxx_mbhc_init);
+
+void wcd9xxx_mbhc_deinit(struct wcd9xxx_mbhc *mbhc)
+{
+	void *cdata = mbhc->codec->control_data;
+
+	wcd9xxx_free_irq(cdata, WCD9XXX_IRQ_SLIMBUS, mbhc);
+	wcd9xxx_free_irq(cdata, WCD9XXX_IRQ_MBHC_RELEASE, mbhc);
+	wcd9xxx_free_irq(cdata, WCD9XXX_IRQ_MBHC_POTENTIAL, mbhc);
+	wcd9xxx_free_irq(cdata, WCD9XXX_IRQ_MBHC_REMOVAL, mbhc);
+	wcd9xxx_free_irq(cdata, WCD9XXX_IRQ_MBHC_INSERTION, mbhc);
+
+	if (mbhc->mbhc_fw)
+		release_firmware(mbhc->mbhc_fw);
+
+	wcd9xxx_resmgr_unregister_notifier(mbhc->resmgr, &mbhc->nblock);
+
+	wcd9xxx_cleanup_debugfs(mbhc);
+
+	kfree(mbhc->mbhc_cfg);
+}
+EXPORT_SYMBOL_GPL(wcd9xxx_mbhc_deinit);
+
+MODULE_DESCRIPTION("wcd9xxx MBHC module");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/wcd9xxx-mbhc.h b/sound/soc/codecs/wcd9xxx-mbhc.h
new file mode 100644
index 0000000..0934b5e
--- /dev/null
+++ b/sound/soc/codecs/wcd9xxx-mbhc.h
@@ -0,0 +1,314 @@
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#ifndef __WCD9XXX_MBHC_H__
+#define __WCD9XXX_MBHC_H__
+
+#include "wcd9xxx-resmgr.h"
+
+#define WCD9XXX_CFILT_FAST_MODE 0x00
+#define WCD9XXX_CFILT_SLOW_MODE 0x40
+
+struct mbhc_micbias_regs {
+	u16 cfilt_val;
+	u16 cfilt_ctl;
+	u16 mbhc_reg;
+	u16 int_rbias;
+	u16 ctl_reg;
+	u8 cfilt_sel;
+};
+
+/* Data used by MBHC */
+struct mbhc_internal_cal_data {
+	u16 dce_z;
+	u16 dce_mb;
+	u16 sta_z;
+	u16 sta_mb;
+	u32 t_sta_dce;
+	u32 t_dce;
+	u32 t_sta;
+	u32 micb_mv;
+	u16 v_ins_hu;
+	u16 v_ins_h;
+	u16 v_b1_hu;
+	u16 v_b1_h;
+	u16 v_b1_huc;
+	u16 v_brh;
+	u16 v_brl;
+	u16 v_no_mic;
+	s16 adj_v_hs_max;
+	u16 adj_v_ins_hu;
+	u16 adj_v_ins_h;
+	s16 v_inval_ins_low;
+	s16 v_inval_ins_high;
+};
+
+enum wcd9xxx_mbhc_plug_type {
+	PLUG_TYPE_INVALID = -1,
+	PLUG_TYPE_NONE,
+	PLUG_TYPE_HEADSET,
+	PLUG_TYPE_HEADPHONE,
+	PLUG_TYPE_HIGH_HPH,
+	PLUG_TYPE_GND_MIC_SWAP,
+};
+
+enum wcd9xxx_micbias_num {
+	MBHC_MICBIAS_INVALID = -1,
+	MBHC_MICBIAS1,
+	MBHC_MICBIAS2,
+	MBHC_MICBIAS3,
+	MBHC_MICBIAS4,
+};
+
+enum wcd9xxx_mbhc_state {
+	MBHC_STATE_NONE = -1,
+	MBHC_STATE_POTENTIAL,
+	MBHC_STATE_POTENTIAL_RECOVERY,
+	MBHC_STATE_RELEASE,
+};
+
+enum wcd9xxx_mbhc_btn_det_mem {
+	MBHC_BTN_DET_V_BTN_LOW,
+	MBHC_BTN_DET_V_BTN_HIGH,
+	MBHC_BTN_DET_N_READY,
+	MBHC_BTN_DET_N_CIC,
+	MBHC_BTN_DET_GAIN
+};
+
+enum wcd9xxx_mbhc_clk_freq {
+	TAIKO_MCLK_12P2MHZ = 0,
+	TAIKO_MCLK_9P6MHZ,
+	TAIKO_NUM_CLK_FREQS,
+};
+
+struct wcd9xxx_mbhc_general_cfg {
+	u8 t_ldoh;
+	u8 t_bg_fast_settle;
+	u8 t_shutdown_plug_rem;
+	u8 mbhc_nsa;
+	u8 mbhc_navg;
+	u8 v_micbias_l;
+	u8 v_micbias;
+	u8 mbhc_reserved;
+	u16 settle_wait;
+	u16 t_micbias_rampup;
+	u16 t_micbias_rampdown;
+	u16 t_supply_bringup;
+} __packed;
+
+struct wcd9xxx_mbhc_plug_detect_cfg {
+	u32 mic_current;
+	u32 hph_current;
+	u16 t_mic_pid;
+	u16 t_ins_complete;
+	u16 t_ins_retry;
+	u16 v_removal_delta;
+	u8 micbias_slow_ramp;
+	u8 reserved0;
+	u8 reserved1;
+	u8 reserved2;
+} __packed;
+
+struct wcd9xxx_mbhc_plug_type_cfg {
+	u8 av_detect;
+	u8 mono_detect;
+	u8 num_ins_tries;
+	u8 reserved0;
+	s16 v_no_mic;
+	s16 v_av_min;
+	s16 v_av_max;
+	s16 v_hs_min;
+	s16 v_hs_max;
+	u16 reserved1;
+} __packed;
+
+struct wcd9xxx_mbhc_btn_detect_cfg {
+	s8 c[8];
+	u8 nc;
+	u8 n_meas;
+	u8 mbhc_nsc;
+	u8 n_btn_meas;
+	u8 n_btn_con;
+	u8 num_btn;
+	u8 reserved0;
+	u8 reserved1;
+	u16 t_poll;
+	u16 t_bounce_wait;
+	u16 t_rel_timeout;
+	s16 v_btn_press_delta_sta;
+	s16 v_btn_press_delta_cic;
+	u16 t_btn0_timeout;
+	s16 _v_btn_low[0]; /* v_btn_low[num_btn] */
+	s16 _v_btn_high[0]; /* v_btn_high[num_btn] */
+	u8 _n_ready[TAIKO_NUM_CLK_FREQS];
+	u8 _n_cic[TAIKO_NUM_CLK_FREQS];
+	u8 _gain[TAIKO_NUM_CLK_FREQS];
+} __packed;
+
+struct wcd9xxx_mbhc_imped_detect_cfg {
+	u8 _hs_imped_detect;
+	u8 _n_rload;
+	u8 _hph_keep_on;
+	u8 _repeat_rload_calc;
+	u16 _t_dac_ramp_time;
+	u16 _rhph_high;
+	u16 _rhph_low;
+	u16 _rload[0]; /* rload[n_rload] */
+	u16 _alpha[0]; /* alpha[n_rload] */
+	u16 _beta[3];
+} __packed;
+
+struct wcd9xxx_mbhc_config {
+	bool read_fw_bin;
+	/*
+	 * void* calibration contains:
+	 *  struct wcd9xxx_mbhc_general_cfg generic;
+	 *  struct wcd9xxx_mbhc_plug_detect_cfg plug_det;
+	 *  struct wcd9xxx_mbhc_plug_type_cfg plug_type;
+	 *  struct wcd9xxx_mbhc_btn_detect_cfg btn_det;
+	 *  struct wcd9xxx_mbhc_imped_detect_cfg imped_det;
+	 * Note: various size depends on btn_det->num_btn
+	 */
+	void *calibration;
+	enum wcd9xxx_micbias_num micbias;
+	int (*mclk_cb_fn) (struct snd_soc_codec*, int, bool);
+	unsigned int mclk_rate;
+	unsigned int gpio;
+	unsigned int gpio_irq;
+	int gpio_level_insert;
+	bool insert_detect; /* codec has own MBHC_INSERT_DETECT */
+	/* swap_gnd_mic returns true if extern GND/MIC swap switch toggled */
+	bool (*swap_gnd_mic) (struct snd_soc_codec *);
+};
+
+struct wcd9xxx_mbhc {
+	bool polling_active;
+	/* Delayed work to report long button press */
+	struct delayed_work mbhc_btn_dwork;
+	int buttons_pressed;
+	enum wcd9xxx_mbhc_state mbhc_state;
+	struct wcd9xxx_mbhc_config *mbhc_cfg;
+
+	struct mbhc_internal_cal_data mbhc_data;
+
+	struct mbhc_micbias_regs mbhc_bias_regs;
+	bool mbhc_micbias_switched;
+
+	u32 hph_status; /* track headhpone status */
+	u8 hphlocp_cnt; /* headphone left ocp retry */
+	u8 hphrocp_cnt; /* headphone right ocp retry */
+
+	/* Work to perform MBHC Firmware Read */
+	struct delayed_work mbhc_firmware_dwork;
+	const struct firmware *mbhc_fw;
+
+	struct delayed_work mbhc_insert_dwork;
+
+	u8 current_plug;
+	struct work_struct correct_plug_swch;
+	/*
+	 * Work to perform polling on microphone voltage
+	 * in order to correct plug type once plug type
+	 * is detected as headphone
+	 */
+	struct work_struct correct_plug_noswch;
+	bool hs_detect_work_stop;
+
+	bool lpi_enabled; /* low power insertion detection */
+	bool in_swch_irq_handler;
+
+	struct wcd9xxx_resmgr *resmgr;
+	struct snd_soc_codec *codec;
+
+	bool no_mic_headset_override;
+
+	/* track PA/DAC state */
+	unsigned long hph_pa_dac_state;
+
+	unsigned long mbhc_last_resume; /* in jiffies */
+
+	bool insert_detect_level_insert;
+
+	struct snd_soc_jack headset_jack;
+	struct snd_soc_jack button_jack;
+
+	struct notifier_block nblock;
+
+#ifdef CONFIG_DEBUG_FS
+	struct dentry *debugfs_poke;
+	struct dentry *debugfs_mbhc;
+#endif
+};
+
+#define WCD9XXX_MBHC_CAL_SIZE(buttons, rload) ( \
+	sizeof(enum wcd9xxx_micbias_num) + \
+	sizeof(struct wcd9xxx_mbhc_general_cfg) + \
+	sizeof(struct wcd9xxx_mbhc_plug_detect_cfg) + \
+	    ((sizeof(s16) + sizeof(s16)) * buttons) + \
+	sizeof(struct wcd9xxx_mbhc_plug_type_cfg) + \
+	sizeof(struct wcd9xxx_mbhc_btn_detect_cfg) + \
+	sizeof(struct wcd9xxx_mbhc_imped_detect_cfg) + \
+	    ((sizeof(u16) + sizeof(u16)) * rload) \
+	)
+
+#define WCD9XXX_MBHC_CAL_GENERAL_PTR(cali) ( \
+	    (struct wcd9xxx_mbhc_general_cfg *) cali)
+#define WCD9XXX_MBHC_CAL_PLUG_DET_PTR(cali) ( \
+	    (struct wcd9xxx_mbhc_plug_detect_cfg *) \
+	    &(WCD9XXX_MBHC_CAL_GENERAL_PTR(cali)[1]))
+#define WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(cali) ( \
+	    (struct wcd9xxx_mbhc_plug_type_cfg *) \
+	    &(WCD9XXX_MBHC_CAL_PLUG_DET_PTR(cali)[1]))
+#define WCD9XXX_MBHC_CAL_BTN_DET_PTR(cali) ( \
+	    (struct wcd9xxx_mbhc_btn_detect_cfg *) \
+	    &(WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(cali)[1]))
+#define WCD9XXX_MBHC_CAL_IMPED_DET_PTR(cali) ( \
+	    (struct wcd9xxx_mbhc_imped_detect_cfg *) \
+	    (((void *)&WCD9XXX_MBHC_CAL_BTN_DET_PTR(cali)[1]) + \
+	     (WCD9XXX_MBHC_CAL_BTN_DET_PTR(cali)->num_btn * \
+	      (sizeof(WCD9XXX_MBHC_CAL_BTN_DET_PTR(cali)->_v_btn_low[0]) + \
+	       sizeof(WCD9XXX_MBHC_CAL_BTN_DET_PTR(cali)->_v_btn_high[0])))) \
+	)
+
+/* minimum size of calibration data assuming there is only one button and
+ * one rload.
+ */
+#define WCD9XXX_MBHC_CAL_MIN_SIZE ( \
+	    sizeof(struct wcd9xxx_mbhc_general_cfg) + \
+	    sizeof(struct wcd9xxx_mbhc_plug_detect_cfg) + \
+	    sizeof(struct wcd9xxx_mbhc_plug_type_cfg) + \
+	    sizeof(struct wcd9xxx_mbhc_btn_detect_cfg) + \
+	    sizeof(struct wcd9xxx_mbhc_imped_detect_cfg) + \
+	    (sizeof(u16) * 2) \
+	)
+
+#define WCD9XXX_MBHC_CAL_BTN_SZ(cfg_ptr) ( \
+	    sizeof(struct wcd9xxx_mbhc_btn_detect_cfg) + \
+	    (cfg_ptr->num_btn * (sizeof(cfg_ptr->_v_btn_low[0]) + \
+				 sizeof(cfg_ptr->_v_btn_high[0]))))
+
+#define WCD9XXX_MBHC_CAL_IMPED_MIN_SZ ( \
+	    sizeof(struct wcd9xxx_mbhc_imped_detect_cfg) + sizeof(u16) * 2)
+
+#define WCD9XXX_MBHC_CAL_IMPED_SZ(cfg_ptr) ( \
+	    sizeof(struct wcd9xxx_mbhc_imped_detect_cfg) + \
+	    (cfg_ptr->_n_rload * \
+	     (sizeof(cfg_ptr->_rload[0]) + sizeof(cfg_ptr->_alpha[0]))))
+
+int wcd9xxx_mbhc_start(struct wcd9xxx_mbhc *mbhc,
+		       struct wcd9xxx_mbhc_config *mbhc_cfg);
+int wcd9xxx_mbhc_init(struct wcd9xxx_mbhc *mbhc, struct wcd9xxx_resmgr *resmgr,
+		      struct snd_soc_codec *codec);
+void wcd9xxx_mbhc_deinit(struct wcd9xxx_mbhc *mbhc);
+void *wcd9xxx_mbhc_cal_btn_det_mp(
+			    const struct wcd9xxx_mbhc_btn_detect_cfg *btn_det,
+			    const enum wcd9xxx_mbhc_btn_det_mem mem);
+#endif /* __WCD9XXX_MBHC_H__ */
diff --git a/sound/soc/codecs/wcd9xxx-resmgr.c b/sound/soc/codecs/wcd9xxx-resmgr.c
new file mode 100644
index 0000000..5dfa41c
--- /dev/null
+++ b/sound/soc/codecs/wcd9xxx-resmgr.c
@@ -0,0 +1,654 @@
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/firmware.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/device.h>
+#include <linux/printk.h>
+#include <linux/ratelimit.h>
+#include <linux/debugfs.h>
+#include <linux/mfd/wcd9xxx/core.h>
+#include <linux/mfd/wcd9xxx/wcd9xxx_registers.h>
+#include <linux/mfd/wcd9xxx/wcd9320_registers.h>
+#include <linux/mfd/wcd9xxx/pdata.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include <linux/pm_runtime.h>
+#include <linux/kernel.h>
+#include <linux/gpio.h>
+#include "wcd9xxx-resmgr.h"
+
+static char wcd9xxx_event_string[][64] = {
+	"WCD9XXX_EVENT_INVALID",
+
+	"WCD9XXX_EVENT_PRE_RCO_ON",
+	"WCD9XXX_EVENT_POST_RCO_ON",
+	"WCD9XXX_EVENT_PRE_RCO_OFF",
+	"WCD9XXX_EVENT_POST_RCO_OFF",
+
+	"WCD9XXX_EVENT_PRE_MCLK_ON",
+	"WCD9XXX_EVENT_POST_MCLK_ON",
+	"WCD9XXX_EVENT_PRE_MCLK_OFF",
+	"WCD9XXX_EVENT_POST_MCLK_OFF",
+
+	"WCD9XXX_EVENT_PRE_BG_OFF",
+	"WCD9XXX_EVENT_POST_BG_OFF",
+	"WCD9XXX_EVENT_PRE_BG_AUDIO_ON",
+	"WCD9XXX_EVENT_POST_BG_AUDIO_ON",
+	"WCD9XXX_EVENT_PRE_BG_MBHC_ON",
+	"WCD9XXX_EVENT_POST_BG_MBHC_ON",
+
+	"WCD9XXX_EVENT_PRE_MICBIAS_1_OFF",
+	"WCD9XXX_EVENT_POST_MICBIAS_1_OFF",
+	"WCD9XXX_EVENT_PRE_MICBIAS_2_OFF",
+	"WCD9XXX_EVENT_POST_MICBIAS_2_OFF",
+	"WCD9XXX_EVENT_PRE_MICBIAS_3_OFF",
+	"WCD9XXX_EVENT_POST_MICBIAS_3_OFF",
+	"WCD9XXX_EVENT_PRE_MICBIAS_4_OFF",
+	"WCD9XXX_EVENT_POST_MICBIAS_4_OFF",
+	"WCD9XXX_EVENT_PRE_MICBIAS_1_ON",
+	"WCD9XXX_EVENT_POST_MICBIAS_1_ON",
+	"WCD9XXX_EVENT_PRE_MICBIAS_2_ON",
+	"WCD9XXX_EVENT_POST_MICBIAS_2_ON",
+	"WCD9XXX_EVENT_PRE_MICBIAS_3_ON",
+	"WCD9XXX_EVENT_POST_MICBIAS_3_ON",
+	"WCD9XXX_EVENT_PRE_MICBIAS_4_ON",
+	"WCD9XXX_EVENT_POST_MICBIAS_4_ON",
+
+	"WCD9XXX_EVENT_PRE_CFILT_1_OFF",
+	"WCD9XXX_EVENT_POST_CFILT_1_OFF",
+	"WCD9XXX_EVENT_PRE_CFILT_2_OFF",
+	"WCD9XXX_EVENT_POST_CFILT_2_OFF",
+	"WCD9XXX_EVENT_PRE_CFILT_3_OFF",
+	"WCD9XXX_EVENT_POST_CFILT_3_OFF",
+	"WCD9XXX_EVENT_PRE_CFILT_1_ON",
+	"WCD9XXX_EVENT_POST_CFILT_1_ON",
+	"WCD9XXX_EVENT_PRE_CFILT_2_ON",
+	"WCD9XXX_EVENT_POST_CFILT_2_ON",
+	"WCD9XXX_EVENT_PRE_CFILT_3_ON",
+	"WCD9XXX_EVENT_POST_CFILT_3_ON",
+
+	"WCD9XXX_EVENT_PRE_HPHL_PA_ON",
+	"WCD9XXX_EVENT_POST_HPHL_PA_OFF",
+	"WCD9XXX_EVENT_PRE_HPHR_PA_ON",
+	"WCD9XXX_EVENT_POST_HPHR_PA_OFF",
+
+	"WCD9XXX_EVENT_POST_RESUME",
+
+	"WCD9XXX_EVENT_LAST",
+};
+
+static enum wcd9xxx_clock_type wcd9xxx_save_clock(struct wcd9xxx_resmgr
+						  *resmgr);
+static void wcd9xxx_restore_clock(struct wcd9xxx_resmgr *resmgr,
+				  enum wcd9xxx_clock_type type);
+
+const char *wcd9xxx_get_event_string(enum wcd9xxx_notify_event type)
+{
+	return wcd9xxx_event_string[type];
+}
+
+void wcd9xxx_resmgr_notifier_call(struct wcd9xxx_resmgr *resmgr,
+				  const enum wcd9xxx_notify_event e)
+{
+	pr_debug("%s: notifier call event %d\n", __func__, e);
+	blocking_notifier_call_chain(&resmgr->notifier, e, resmgr);
+}
+
+static void wcd9xxx_codec_disable_bg(struct wcd9xxx_resmgr *resmgr)
+{
+	/* Notify bg mode change */
+	wcd9xxx_resmgr_notifier_call(resmgr, WCD9XXX_EVENT_PRE_BG_OFF);
+	/* Disable bg */
+	snd_soc_write(resmgr->codec, WCD9XXX_A_BIAS_CENTRAL_BG_CTL, 0x00);
+	usleep_range(100, 100);
+	/* Notify bg mode change */
+	wcd9xxx_resmgr_notifier_call(resmgr, WCD9XXX_EVENT_POST_BG_OFF);
+}
+
+static void wcd9xxx_codec_enable_bg_audio(struct wcd9xxx_resmgr *resmgr)
+{
+	struct snd_soc_codec *codec = resmgr->codec;
+
+	/* Notify bandgap mode change */
+	wcd9xxx_resmgr_notifier_call(resmgr, WCD9XXX_EVENT_PRE_BG_AUDIO_ON);
+	/* Enable bg */
+	snd_soc_update_bits(codec, WCD9XXX_A_BIAS_CENTRAL_BG_CTL, 0x80, 0x80);
+	snd_soc_update_bits(codec, WCD9XXX_A_BIAS_CENTRAL_BG_CTL, 0x04, 0x04);
+	snd_soc_update_bits(codec, WCD9XXX_A_BIAS_CENTRAL_BG_CTL, 0x01, 0x01);
+	usleep_range(1000, 1000);
+	snd_soc_update_bits(codec, WCD9XXX_A_BIAS_CENTRAL_BG_CTL, 0x80, 0x00);
+	/* Notify bandgap mode change */
+	wcd9xxx_resmgr_notifier_call(resmgr, WCD9XXX_EVENT_POST_BG_AUDIO_ON);
+}
+
+static void wcd9xxx_enable_bg_mbhc(struct wcd9xxx_resmgr *resmgr)
+{
+	struct snd_soc_codec *codec = resmgr->codec;
+
+	/* Notify bandgap mode change */
+	wcd9xxx_resmgr_notifier_call(resmgr, WCD9XXX_EVENT_PRE_BG_MBHC_ON);
+
+	/*
+	 * bandgap mode becomes fast,
+	 * mclk should be off or clk buff source souldn't be VBG
+	 * Let's turn off mclk always
+	 */
+	WARN_ON(snd_soc_read(codec, WCD9XXX_A_CLK_BUFF_EN2) & (1 << 2));
+	snd_soc_update_bits(codec, WCD9XXX_A_BIAS_CENTRAL_BG_CTL, 0x2, 0x2);
+	snd_soc_update_bits(codec, WCD9XXX_A_BIAS_CENTRAL_BG_CTL, 0x80, 0x80);
+	snd_soc_update_bits(codec, WCD9XXX_A_BIAS_CENTRAL_BG_CTL, 0x4, 0x4);
+	snd_soc_update_bits(codec, WCD9XXX_A_BIAS_CENTRAL_BG_CTL, 0x01, 0x01);
+	usleep_range(1000, 1000);
+	snd_soc_update_bits(codec, WCD9XXX_A_BIAS_CENTRAL_BG_CTL, 0x80, 0x00);
+
+	/* Notify bandgap mode change */
+	wcd9xxx_resmgr_notifier_call(resmgr, WCD9XXX_EVENT_POST_BG_MBHC_ON);
+}
+
+static void wcd9xxx_disable_bg(struct wcd9xxx_resmgr *resmgr)
+{
+	struct snd_soc_codec *codec = resmgr->codec;
+	snd_soc_write(codec, WCD9XXX_A_BIAS_CENTRAL_BG_CTL, 0x00);
+}
+
+static void wcd9xxx_disable_clock_block(struct wcd9xxx_resmgr *resmgr)
+{
+	struct snd_soc_codec *codec = resmgr->codec;
+
+	pr_debug("%s: enter\n", __func__);
+	WCD9XXX_BCL_ASSERT_LOCKED(resmgr);
+
+	/* Notify */
+	if (resmgr->clk_type == WCD9XXX_CLK_RCO)
+		wcd9xxx_resmgr_notifier_call(resmgr, WCD9XXX_EVENT_PRE_RCO_OFF);
+	else
+		wcd9xxx_resmgr_notifier_call(resmgr,
+					     WCD9XXX_EVENT_PRE_MCLK_OFF);
+	/* Disable clock */
+	snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN2, 0x04, 0x00);
+	usleep_range(50, 50);
+	snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN2, 0x02, 0x02);
+	snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN1, 0x05, 0x00);
+	usleep_range(50, 50);
+	/* Notify */
+	if (resmgr->clk_type == WCD9XXX_CLK_RCO)
+		wcd9xxx_resmgr_notifier_call(resmgr,
+					     WCD9XXX_EVENT_POST_RCO_OFF);
+	else
+		wcd9xxx_resmgr_notifier_call(resmgr,
+					     WCD9XXX_EVENT_POST_MCLK_OFF);
+	pr_debug("%s: leave\n", __func__);
+}
+
+/*
+ * wcd9xxx_resmgr_get_bandgap : Vote for bandgap ref
+ * choice : WCD9XXX_BANDGAP_AUDIO_MODE, WCD9XXX_BANDGAP_MBHC_MODE
+ */
+void wcd9xxx_resmgr_get_bandgap(struct wcd9xxx_resmgr *resmgr,
+				const enum wcd9xxx_bandgap_type choice)
+{
+	enum wcd9xxx_clock_type clock_save;
+
+	pr_debug("%s: enter, wants %d\n", __func__, choice);
+
+	WCD9XXX_BCL_ASSERT_LOCKED(resmgr);
+	switch (choice) {
+	case WCD9XXX_BANDGAP_AUDIO_MODE:
+		resmgr->bg_audio_users++;
+		if (resmgr->bg_audio_users == 1 && resmgr->bg_mbhc_users) {
+			/*
+			 * Current bg is MBHC mode, about to switch to
+			 * audio mode.
+			 */
+			WARN_ON(resmgr->bandgap_type !=
+				WCD9XXX_BANDGAP_MBHC_MODE);
+
+			/* BG mode can be changed only with clock off */
+			clock_save = wcd9xxx_save_clock(resmgr);
+			/* Swtich BG mode */
+			wcd9xxx_codec_disable_bg(resmgr);
+			wcd9xxx_codec_enable_bg_audio(resmgr);
+			/* restore clock */
+			wcd9xxx_restore_clock(resmgr, clock_save);
+		} else if (resmgr->bg_audio_users == 1) {
+			/* currently off, just enable it */
+			WARN_ON(resmgr->bandgap_type != WCD9XXX_BANDGAP_OFF);
+			wcd9xxx_codec_enable_bg_audio(resmgr);
+		}
+		resmgr->bandgap_type = WCD9XXX_BANDGAP_AUDIO_MODE;
+		break;
+	case WCD9XXX_BANDGAP_MBHC_MODE:
+		resmgr->bg_mbhc_users++;
+		if (resmgr->bandgap_type == WCD9XXX_BANDGAP_MBHC_MODE ||
+		    resmgr->bandgap_type == WCD9XXX_BANDGAP_AUDIO_MODE)
+			/* do nothing */
+			break;
+
+		/* bg mode can be changed only with clock off */
+		clock_save = wcd9xxx_save_clock(resmgr);
+		/* enable bg with MBHC mode */
+		wcd9xxx_enable_bg_mbhc(resmgr);
+		/* restore clock */
+		wcd9xxx_restore_clock(resmgr, clock_save);
+		/* save current mode */
+		resmgr->bandgap_type = WCD9XXX_BANDGAP_MBHC_MODE;
+		break;
+	default:
+		pr_err("%s: Error, Invalid bandgap settings\n", __func__);
+		break;
+	}
+
+	pr_debug("%s: bg users audio %d, mbhc %d\n", __func__,
+		 resmgr->bg_audio_users, resmgr->bg_mbhc_users);
+}
+
+/*
+ * wcd9xxx_resmgr_put_bandgap : Unvote bandgap ref that has been voted
+ * choice : WCD9XXX_BANDGAP_AUDIO_MODE, WCD9XXX_BANDGAP_MBHC_MODE
+ */
+void wcd9xxx_resmgr_put_bandgap(struct wcd9xxx_resmgr *resmgr,
+				enum wcd9xxx_bandgap_type choice)
+{
+	enum wcd9xxx_clock_type clock_save;
+
+	pr_debug("%s: enter choice %d\n", __func__, choice);
+
+	WCD9XXX_BCL_ASSERT_LOCKED(resmgr);
+	switch (choice) {
+	case WCD9XXX_BANDGAP_AUDIO_MODE:
+		if (--resmgr->bg_audio_users == 0) {
+			if (resmgr->bg_mbhc_users) {
+				/* bg mode can be changed only with clock off */
+				clock_save = wcd9xxx_save_clock(resmgr);
+				/* switch to MBHC mode */
+				wcd9xxx_enable_bg_mbhc(resmgr);
+				/* restore clock */
+				wcd9xxx_restore_clock(resmgr, clock_save);
+				resmgr->bandgap_type =
+				    WCD9XXX_BANDGAP_MBHC_MODE;
+			} else {
+				/* turn off */
+				wcd9xxx_disable_bg(resmgr);
+				resmgr->bandgap_type = WCD9XXX_BANDGAP_OFF;
+			}
+		}
+		break;
+	case WCD9XXX_BANDGAP_MBHC_MODE:
+		WARN(resmgr->bandgap_type == WCD9XXX_BANDGAP_OFF,
+		     "Unexpected bandgap type %d\n", resmgr->bandgap_type);
+		if (--resmgr->bg_mbhc_users == 0 &&
+		    resmgr->bandgap_type == WCD9XXX_BANDGAP_MBHC_MODE) {
+			wcd9xxx_disable_bg(resmgr);
+			resmgr->bandgap_type = WCD9XXX_BANDGAP_OFF;
+		}
+		break;
+	default:
+		pr_err("%s: Error, Invalid bandgap settings\n", __func__);
+		break;
+	}
+
+	pr_debug("%s: bg users audio %d, mbhc %d\n", __func__,
+		 resmgr->bg_audio_users, resmgr->bg_mbhc_users);
+}
+
+void wcd9xxx_resmgr_enable_rx_bias(struct wcd9xxx_resmgr *resmgr, u32 enable)
+{
+	struct snd_soc_codec *codec = resmgr->codec;
+
+	if (enable) {
+		resmgr->rx_bias_count++;
+		if (resmgr->rx_bias_count == 1)
+			snd_soc_update_bits(codec, WCD9XXX_A_RX_COM_BIAS,
+					    0x80, 0x80);
+	} else {
+		resmgr->rx_bias_count--;
+		if (!resmgr->rx_bias_count)
+			snd_soc_update_bits(codec, WCD9XXX_A_RX_COM_BIAS,
+					    0x80, 0x00);
+	}
+}
+
+int wcd9xxx_resmgr_enable_config_mode(struct snd_soc_codec *codec, int enable)
+{
+	pr_debug("%s: enable = %d\n", __func__, enable);
+	if (enable) {
+		snd_soc_update_bits(codec, WCD9XXX_A_RC_OSC_FREQ, 0x10, 0);
+		/* bandgap mode to fast */
+		snd_soc_write(codec, WCD9XXX_A_BIAS_OSC_BG_CTL, 0x17);
+		usleep_range(5, 5);
+		snd_soc_update_bits(codec, WCD9XXX_A_RC_OSC_FREQ, 0x80, 0x80);
+		snd_soc_update_bits(codec, WCD9XXX_A_RC_OSC_TEST, 0x80, 0x80);
+		usleep_range(10, 10);
+		snd_soc_update_bits(codec, WCD9XXX_A_RC_OSC_TEST, 0x80, 0);
+		usleep_range(10000, 10000);
+		snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN1, 0x08, 0x08);
+	} else {
+		snd_soc_update_bits(codec, WCD9XXX_A_BIAS_OSC_BG_CTL, 0x1, 0);
+		snd_soc_update_bits(codec, WCD9XXX_A_RC_OSC_FREQ, 0x80, 0);
+		/* clk source to ext clk and clk buff ref to VBG */
+		snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN1, 0x0C, 0x04);
+	}
+
+	return 0;
+}
+
+static void wcd9xxx_enable_clock_block(struct wcd9xxx_resmgr *resmgr,
+				       int config_mode)
+{
+	struct snd_soc_codec *codec = resmgr->codec;
+
+	pr_debug("%s: config_mode = %d\n", __func__, config_mode);
+	/* transit to RCO requires mclk off */
+	WARN_ON(snd_soc_read(codec, WCD9XXX_A_CLK_BUFF_EN2) & (1 << 2));
+	if (config_mode) {
+		/* Notify */
+		wcd9xxx_resmgr_notifier_call(resmgr, WCD9XXX_EVENT_PRE_RCO_ON);
+		/* enable RCO and switch to it */
+		wcd9xxx_resmgr_enable_config_mode(codec, 1);
+		snd_soc_write(codec, WCD9XXX_A_CLK_BUFF_EN2, 0x02);
+		usleep_range(1000, 1000);
+	} else {
+		/* Notify */
+		wcd9xxx_resmgr_notifier_call(resmgr, WCD9XXX_EVENT_PRE_MCLK_ON);
+		/* switch to MCLK */
+		snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN1, 0x08, 0x00);
+		/* if RCO is enabled, switch from it */
+		if (snd_soc_read(codec, WCD9XXX_A_RC_OSC_FREQ) & 0x80) {
+			snd_soc_write(codec, WCD9XXX_A_CLK_BUFF_EN2, 0x02);
+			wcd9xxx_resmgr_enable_config_mode(codec, 0);
+		}
+	}
+
+	snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN1, 0x01, 0x01);
+	snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN2, 0x02, 0x00);
+
+	/* on MCLK */
+	snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN2, 0x04, 0x04);
+	snd_soc_update_bits(codec, WCD9XXX_A_CDC_CLK_MCLK_CTL, 0x01, 0x01);
+	usleep_range(50, 50);
+
+	/* Notify */
+	if (config_mode)
+		wcd9xxx_resmgr_notifier_call(resmgr,
+					     WCD9XXX_EVENT_POST_RCO_ON);
+	else
+		wcd9xxx_resmgr_notifier_call(resmgr,
+					     WCD9XXX_EVENT_POST_MCLK_ON);
+}
+
+/*
+ * disable clock and return previous clock state
+ */
+static enum wcd9xxx_clock_type wcd9xxx_save_clock(struct wcd9xxx_resmgr *resmgr)
+{
+	WCD9XXX_BCL_ASSERT_LOCKED(resmgr);
+	if (resmgr->clk_type != WCD9XXX_CLK_OFF)
+		wcd9xxx_disable_clock_block(resmgr);
+	return resmgr->clk_type != WCD9XXX_CLK_OFF;
+}
+
+static void wcd9xxx_restore_clock(struct wcd9xxx_resmgr *resmgr,
+				  enum wcd9xxx_clock_type type)
+{
+	if (type != WCD9XXX_CLK_OFF)
+		wcd9xxx_enable_clock_block(resmgr, type == WCD9XXX_CLK_RCO);
+}
+
+void wcd9xxx_resmgr_get_clk_block(struct wcd9xxx_resmgr *resmgr,
+				  enum wcd9xxx_clock_type type)
+{
+	pr_debug("%s: current %d, requested %d, rco_users %d, mclk_users %d\n",
+		 __func__, resmgr->clk_type, type,
+		 resmgr->clk_rco_users, resmgr->clk_mclk_users);
+	WCD9XXX_BCL_ASSERT_LOCKED(resmgr);
+	switch (type) {
+	case WCD9XXX_CLK_RCO:
+		if (++resmgr->clk_rco_users == 1 &&
+		    resmgr->clk_type == WCD9XXX_CLK_OFF) {
+			/* enable RCO and switch to it */
+			wcd9xxx_enable_clock_block(resmgr, 1);
+			resmgr->clk_type = WCD9XXX_CLK_RCO;
+		}
+		break;
+	case WCD9XXX_CLK_MCLK:
+		if (++resmgr->clk_mclk_users == 1 &&
+		    resmgr->clk_type == WCD9XXX_CLK_OFF) {
+			/* switch to MCLK */
+			wcd9xxx_enable_clock_block(resmgr, 0);
+			resmgr->clk_type = WCD9XXX_CLK_MCLK;
+		} else if (resmgr->clk_mclk_users == 1 &&
+			   resmgr->clk_type == WCD9XXX_CLK_RCO) {
+			/* if RCO is enabled, switch from it */
+			WARN_ON(!(snd_soc_read(resmgr->codec,
+					       WCD9XXX_A_RC_OSC_FREQ) & 0x80));
+			/* disable clock block */
+			wcd9xxx_disable_clock_block(resmgr);
+			/* switch to RCO */
+			wcd9xxx_enable_clock_block(resmgr, 0);
+			resmgr->clk_type = WCD9XXX_CLK_MCLK;
+		}
+		break;
+	default:
+		pr_err("%s: Error, Invalid clock get request %d\n", __func__,
+		       type);
+		break;
+	}
+	pr_debug("%s: leave\n", __func__);
+}
+
+void wcd9xxx_resmgr_put_clk_block(struct wcd9xxx_resmgr *resmgr,
+				  enum wcd9xxx_clock_type type)
+{
+	pr_debug("%s: current %d, put %d\n", __func__, resmgr->clk_type, type);
+
+	WCD9XXX_BCL_ASSERT_LOCKED(resmgr);
+	switch (type) {
+	case WCD9XXX_CLK_RCO:
+		if (--resmgr->clk_rco_users == 0 &&
+		    resmgr->clk_type == WCD9XXX_CLK_RCO) {
+			wcd9xxx_disable_clock_block(resmgr);
+			resmgr->clk_type = WCD9XXX_CLK_OFF;
+		}
+		break;
+	case WCD9XXX_CLK_MCLK:
+		if (--resmgr->clk_mclk_users == 0 &&
+		    resmgr->clk_rco_users == 0) {
+			wcd9xxx_disable_clock_block(resmgr);
+			resmgr->clk_type = WCD9XXX_CLK_OFF;
+		} else if (resmgr->clk_mclk_users == 0 &&
+			   resmgr->clk_rco_users) {
+			/* disable clock */
+			wcd9xxx_disable_clock_block(resmgr);
+			/* switch to RCO */
+			wcd9xxx_enable_clock_block(resmgr, 1);
+			resmgr->clk_type = WCD9XXX_CLK_RCO;
+		}
+		break;
+	default:
+		pr_err("%s: Error, Invalid clock get request %d\n", __func__,
+		       type);
+		break;
+	}
+	WARN_ON(resmgr->clk_rco_users < 0);
+	WARN_ON(resmgr->clk_mclk_users < 0);
+
+	pr_debug("%s: new rco_users %d, mclk_users %d\n", __func__,
+		 resmgr->clk_rco_users, resmgr->clk_mclk_users);
+}
+
+static void wcd9xxx_resmgr_update_cfilt_usage(struct wcd9xxx_resmgr *resmgr,
+					      enum wcd9xxx_cfilt_sel cfilt_sel,
+					      bool inc)
+{
+	u16 micb_cfilt_reg;
+	enum wcd9xxx_notify_event e_pre_on, e_post_off;
+	struct snd_soc_codec *codec = resmgr->codec;
+
+	switch (cfilt_sel) {
+	case WCD9XXX_CFILT1_SEL:
+		micb_cfilt_reg = WCD9XXX_A_MICB_CFILT_1_CTL;
+		e_pre_on = WCD9XXX_EVENT_PRE_CFILT_1_ON;
+		e_post_off = WCD9XXX_EVENT_POST_CFILT_1_OFF;
+		break;
+	case WCD9XXX_CFILT2_SEL:
+		micb_cfilt_reg = WCD9XXX_A_MICB_CFILT_2_CTL;
+		e_pre_on = WCD9XXX_EVENT_PRE_CFILT_2_ON;
+		e_post_off = WCD9XXX_EVENT_POST_CFILT_2_OFF;
+		break;
+	case WCD9XXX_CFILT3_SEL:
+		micb_cfilt_reg = WCD9XXX_A_MICB_CFILT_3_CTL;
+		e_pre_on = WCD9XXX_EVENT_PRE_CFILT_3_ON;
+		e_post_off = WCD9XXX_EVENT_POST_CFILT_3_OFF;
+		break;
+	default:
+		WARN(1, "Invalid CFILT selection %d\n", cfilt_sel);
+		return; /* should not happen */
+	}
+
+	if (inc) {
+		if ((resmgr->cfilt_users[cfilt_sel]++) == 0) {
+			/* Notify */
+			wcd9xxx_resmgr_notifier_call(resmgr, e_pre_on);
+			/* Enable CFILT */
+			snd_soc_update_bits(codec, micb_cfilt_reg, 0x80, 0x80);
+		}
+	} else {
+		/*
+		 * Check if count not zero, decrease
+		 * then check if zero, go ahead disable cfilter
+		 */
+		WARN(resmgr->cfilt_users[cfilt_sel] == 0,
+		     "Invalid CFILT use count 0\n");
+		if ((--resmgr->cfilt_users[cfilt_sel]) == 0) {
+			/* Disable CFILT */
+			snd_soc_update_bits(codec, micb_cfilt_reg, 0x80, 0);
+			/* Notify MBHC so MBHC can switch CFILT to fast mode */
+			wcd9xxx_resmgr_notifier_call(resmgr, e_post_off);
+		}
+	}
+}
+
+void wcd9xxx_resmgr_cfilt_get(struct wcd9xxx_resmgr *resmgr,
+			      enum wcd9xxx_cfilt_sel cfilt_sel)
+{
+	return wcd9xxx_resmgr_update_cfilt_usage(resmgr, cfilt_sel, true);
+}
+
+void wcd9xxx_resmgr_cfilt_put(struct wcd9xxx_resmgr *resmgr,
+			      enum wcd9xxx_cfilt_sel cfilt_sel)
+{
+	return wcd9xxx_resmgr_update_cfilt_usage(resmgr, cfilt_sel, false);
+}
+
+int wcd9xxx_resmgr_get_k_val(struct wcd9xxx_resmgr *resmgr,
+			     unsigned int cfilt_mv)
+{
+	int rc = -EINVAL;
+	unsigned int ldoh_v = resmgr->pdata->micbias.ldoh_v;
+	unsigned min_mv, max_mv;
+
+	switch (ldoh_v) {
+	case WCD9XXX_LDOH_1P95_V:
+		min_mv = 160;
+		max_mv = 1800;
+		break;
+	case WCD9XXX_LDOH_2P35_V:
+		min_mv = 200;
+		max_mv = 2200;
+		break;
+	case WCD9XXX_LDOH_2P75_V:
+		min_mv = 240;
+		max_mv = 2600;
+		break;
+	case WCD9XXX_LDOH_3P0_V:
+		min_mv = 260;
+		max_mv = 2875;
+		break;
+	default:
+		goto done;
+	}
+
+	if (cfilt_mv < min_mv || cfilt_mv > max_mv)
+		goto done;
+
+	for (rc = 4; rc <= 44; rc++) {
+		min_mv = max_mv * (rc) / 44;
+		if (min_mv >= cfilt_mv) {
+			rc -= 4;
+			break;
+		}
+	}
+done:
+	return rc;
+}
+
+int wcd9xxx_resmgr_register_notifier(struct wcd9xxx_resmgr *resmgr,
+				     struct notifier_block *nblock)
+{
+	return blocking_notifier_chain_register(&resmgr->notifier, nblock);
+}
+
+int wcd9xxx_resmgr_unregister_notifier(struct wcd9xxx_resmgr *resmgr,
+				       struct notifier_block *nblock)
+{
+	return blocking_notifier_chain_unregister(&resmgr->notifier, nblock);
+}
+
+int wcd9xxx_resmgr_init(struct wcd9xxx_resmgr *resmgr,
+			struct snd_soc_codec *codec,
+			struct wcd9xxx *wcd9xxx,
+			struct wcd9xxx_pdata *pdata,
+			struct wcd9xxx_reg_address *reg_addr)
+{
+	WARN(ARRAY_SIZE(wcd9xxx_event_string) != WCD9XXX_EVENT_LAST + 1,
+	     "Event string table isn't up to date!, %d != %d\n",
+	     ARRAY_SIZE(wcd9xxx_event_string), WCD9XXX_EVENT_LAST + 1);
+
+	resmgr->bandgap_type = WCD9XXX_BANDGAP_OFF;
+	resmgr->codec = codec;
+	/* This gives access of core handle to lock/unlock suspend */
+	resmgr->core = wcd9xxx;
+	resmgr->pdata = pdata;
+	resmgr->reg_addr = reg_addr;
+
+	BLOCKING_INIT_NOTIFIER_HEAD(&resmgr->notifier);
+
+	mutex_init(&resmgr->codec_resource_lock);
+
+	return 0;
+}
+
+void wcd9xxx_resmgr_deinit(struct wcd9xxx_resmgr *resmgr)
+{
+	mutex_destroy(&resmgr->codec_resource_lock);
+}
+
+void wcd9xxx_resmgr_bcl_lock(struct wcd9xxx_resmgr *resmgr)
+{
+	mutex_lock(&resmgr->codec_resource_lock);
+}
+
+void wcd9xxx_resmgr_bcl_unlock(struct wcd9xxx_resmgr *resmgr)
+{
+	mutex_unlock(&resmgr->codec_resource_lock);
+}
+
+MODULE_DESCRIPTION("wcd9xxx resmgr module");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/wcd9xxx-resmgr.h b/sound/soc/codecs/wcd9xxx-resmgr.h
new file mode 100644
index 0000000..2d04102
--- /dev/null
+++ b/sound/soc/codecs/wcd9xxx-resmgr.h
@@ -0,0 +1,191 @@
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#ifndef __WCD9XXX_COMMON_H__
+#define __WCD9XXX_COMMON_H__
+
+#include <linux/notifier.h>
+#include <linux/mfd/wcd9xxx/wcd9xxx_registers.h>
+
+enum wcd9xxx_bandgap_type {
+	WCD9XXX_BANDGAP_OFF,
+	WCD9XXX_BANDGAP_AUDIO_MODE,
+	WCD9XXX_BANDGAP_MBHC_MODE,
+};
+
+enum wcd9xxx_clock_type {
+	WCD9XXX_CLK_OFF,
+	WCD9XXX_CLK_RCO,
+	WCD9XXX_CLK_MCLK,
+};
+
+enum wcd9xxx_cfilt_sel {
+	WCD9XXX_CFILT1_SEL,
+	WCD9XXX_CFILT2_SEL,
+	WCD9XXX_CFILT3_SEL,
+	WCD9XXX_NUM_OF_CFILT,
+};
+
+struct wcd9xxx_reg_address {
+	u16 micb_4_ctl;
+	u16 micb_4_int_rbias;
+	u16 micb_4_mbhc;
+};
+
+enum wcd9xxx_notify_event {
+	WCD9XXX_EVENT_INVALID,
+
+	WCD9XXX_EVENT_PRE_RCO_ON,
+	WCD9XXX_EVENT_POST_RCO_ON,
+	WCD9XXX_EVENT_PRE_RCO_OFF,
+	WCD9XXX_EVENT_POST_RCO_OFF,
+
+	WCD9XXX_EVENT_PRE_MCLK_ON,
+	WCD9XXX_EVENT_POST_MCLK_ON,
+	WCD9XXX_EVENT_PRE_MCLK_OFF,
+	WCD9XXX_EVENT_POST_MCLK_OFF,
+
+	WCD9XXX_EVENT_PRE_BG_OFF,
+	WCD9XXX_EVENT_POST_BG_OFF,
+	WCD9XXX_EVENT_PRE_BG_AUDIO_ON,
+	WCD9XXX_EVENT_POST_BG_AUDIO_ON,
+	WCD9XXX_EVENT_PRE_BG_MBHC_ON,
+	WCD9XXX_EVENT_POST_BG_MBHC_ON,
+
+	WCD9XXX_EVENT_PRE_MICBIAS_1_OFF,
+	WCD9XXX_EVENT_POST_MICBIAS_1_OFF,
+	WCD9XXX_EVENT_PRE_MICBIAS_2_OFF,
+	WCD9XXX_EVENT_POST_MICBIAS_2_OFF,
+	WCD9XXX_EVENT_PRE_MICBIAS_3_OFF,
+	WCD9XXX_EVENT_POST_MICBIAS_3_OFF,
+	WCD9XXX_EVENT_PRE_MICBIAS_4_OFF,
+	WCD9XXX_EVENT_POST_MICBIAS_4_OFF,
+	WCD9XXX_EVENT_PRE_MICBIAS_1_ON,
+	WCD9XXX_EVENT_POST_MICBIAS_1_ON,
+	WCD9XXX_EVENT_PRE_MICBIAS_2_ON,
+	WCD9XXX_EVENT_POST_MICBIAS_2_ON,
+	WCD9XXX_EVENT_PRE_MICBIAS_3_ON,
+	WCD9XXX_EVENT_POST_MICBIAS_3_ON,
+	WCD9XXX_EVENT_PRE_MICBIAS_4_ON,
+	WCD9XXX_EVENT_POST_MICBIAS_4_ON,
+
+	WCD9XXX_EVENT_PRE_CFILT_1_OFF,
+	WCD9XXX_EVENT_POST_CFILT_1_OFF,
+	WCD9XXX_EVENT_PRE_CFILT_2_OFF,
+	WCD9XXX_EVENT_POST_CFILT_2_OFF,
+	WCD9XXX_EVENT_PRE_CFILT_3_OFF,
+	WCD9XXX_EVENT_POST_CFILT_3_OFF,
+	WCD9XXX_EVENT_PRE_CFILT_1_ON,
+	WCD9XXX_EVENT_POST_CFILT_1_ON,
+	WCD9XXX_EVENT_PRE_CFILT_2_ON,
+	WCD9XXX_EVENT_POST_CFILT_2_ON,
+	WCD9XXX_EVENT_PRE_CFILT_3_ON,
+	WCD9XXX_EVENT_POST_CFILT_3_ON,
+
+	WCD9XXX_EVENT_PRE_HPHL_PA_ON,
+	WCD9XXX_EVENT_POST_HPHL_PA_OFF,
+	WCD9XXX_EVENT_PRE_HPHR_PA_ON,
+	WCD9XXX_EVENT_POST_HPHR_PA_OFF,
+
+	WCD9XXX_EVENT_POST_RESUME,
+
+	WCD9XXX_EVENT_LAST,
+};
+
+struct wcd9xxx_resmgr {
+	struct snd_soc_codec *codec;
+	struct wcd9xxx *core;
+
+	u32 rx_bias_count;
+
+	enum wcd9xxx_bandgap_type bandgap_type;
+	u16 bg_audio_users;
+	u16 bg_mbhc_users;
+
+	enum wcd9xxx_clock_type clk_type;
+	u16 clk_rco_users;
+	u16 clk_mclk_users;
+
+	/* cfilt users per cfilts */
+	u16 cfilt_users[WCD9XXX_NUM_OF_CFILT];
+
+	struct wcd9xxx_reg_address *reg_addr;
+
+	struct wcd9xxx_pdata *pdata;
+
+	struct blocking_notifier_head notifier;
+	/* Notifier needs mbhc pointer with resmgr */
+	struct wcd9xxx_mbhc *mbhc;
+
+	/*
+	 * Currently, only used for mbhc purpose, to protect
+	 * concurrent execution of mbhc threaded irq handlers and
+	 * kill race between DAPM and MBHC. But can serve as a
+	 * general lock to protect codec resource
+	 */
+	struct mutex codec_resource_lock;
+};
+
+int wcd9xxx_resmgr_init(struct wcd9xxx_resmgr *resmgr,
+			struct snd_soc_codec *codec,
+			struct wcd9xxx *wcd9xxx,
+			struct wcd9xxx_pdata *pdata,
+			struct wcd9xxx_reg_address *reg_addr);
+void wcd9xxx_resmgr_deinit(struct wcd9xxx_resmgr *resmgr);
+
+int wcd9xxx_resmgr_enable_config_mode(struct snd_soc_codec *codec, int enable);
+
+void wcd9xxx_resmgr_enable_rx_bias(struct wcd9xxx_resmgr *resmgr, u32 enable);
+void wcd9xxx_resmgr_get_clk_block(struct wcd9xxx_resmgr *resmgr,
+				  enum wcd9xxx_clock_type type);
+void wcd9xxx_resmgr_put_clk_block(struct wcd9xxx_resmgr *resmgr,
+				  enum wcd9xxx_clock_type type);
+void wcd9xxx_resmgr_get_bandgap(struct wcd9xxx_resmgr *resmgr,
+				const enum wcd9xxx_bandgap_type choice);
+void wcd9xxx_resmgr_put_bandgap(struct wcd9xxx_resmgr *resmgr,
+				enum wcd9xxx_bandgap_type choice);
+void wcd9xxx_resmgr_cfilt_get(struct wcd9xxx_resmgr *resmgr,
+			      enum wcd9xxx_cfilt_sel cfilt_sel);
+void wcd9xxx_resmgr_cfilt_put(struct wcd9xxx_resmgr *resmgr,
+			      enum wcd9xxx_cfilt_sel cfilt_sel);
+
+void wcd9xxx_resmgr_bcl_lock(struct wcd9xxx_resmgr *resmgr);
+#define WCD9XXX_BCL_LOCK(resmgr)			\
+{							\
+	pr_debug("%s: Acquiring BCL\n", __func__);	\
+	wcd9xxx_resmgr_bcl_lock(resmgr);			\
+	pr_debug("%s: Acquiring BCL done\n", __func__);	\
+}
+
+void wcd9xxx_resmgr_bcl_unlock(struct wcd9xxx_resmgr *resmgr);
+#define WCD9XXX_BCL_UNLOCK(resmgr)			\
+{							\
+	pr_debug("%s: Release BCL\n", __func__);	\
+	wcd9xxx_resmgr_bcl_unlock(resmgr);			\
+}
+
+#define WCD9XXX_BCL_ASSERT_LOCKED(resmgr)		\
+{							\
+	WARN_ONCE(!mutex_is_locked(&resmgr->codec_resource_lock), \
+		  "%s: BCL should have acquired\n", __func__); \
+}
+
+const char *wcd9xxx_get_event_string(enum wcd9xxx_notify_event type);
+int wcd9xxx_resmgr_get_k_val(struct wcd9xxx_resmgr *resmgr,
+			     unsigned int cfilt_mv);
+int wcd9xxx_resmgr_register_notifier(struct wcd9xxx_resmgr *resmgr,
+				     struct notifier_block *nblock);
+int wcd9xxx_resmgr_unregister_notifier(struct wcd9xxx_resmgr *resmgr,
+				       struct notifier_block *nblock);
+void wcd9xxx_resmgr_notifier_call(struct wcd9xxx_resmgr *resmgr,
+				  const enum wcd9xxx_notify_event e);
+
+#endif /* __WCD9XXX_COMMON_H__ */
diff --git a/sound/soc/msm/msm8974.c b/sound/soc/msm/msm8974.c
index f44a147..e65d83f 100644
--- a/sound/soc/msm/msm8974.c
+++ b/sound/soc/msm/msm8974.c
@@ -52,10 +52,26 @@
 #define GPIO_AUX_PCM_SYNC 45
 #define GPIO_AUX_PCM_CLK 46
 
-#define TABLA_EXT_CLK_RATE 12288000
+#define WCD9XXX_MBHC_DEF_BUTTONS 8
+#define WCD9XXX_MBHC_DEF_RLOADS 5
+#define TAIKO_EXT_CLK_RATE 9600000
 
-#define TABLA_MBHC_DEF_BUTTONS 8
-#define TABLA_MBHC_DEF_RLOADS 5
+void *def_taiko_mbhc_cal(void);
+static int msm_snd_enable_codec_ext_clk(struct snd_soc_codec *codec, int enable,
+					bool dapm);
+
+static struct wcd9xxx_mbhc_config mbhc_cfg = {
+	.read_fw_bin = false,
+	.calibration = NULL,
+	.micbias = MBHC_MICBIAS2,
+	.mclk_cb_fn = msm_snd_enable_codec_ext_clk,
+	.mclk_rate = TAIKO_EXT_CLK_RATE,
+	.gpio = 0,
+	.gpio_irq = 0,
+	.gpio_level_insert = 1,
+	.insert_detect = true,
+	.swap_gnd_mic = NULL,
+};
 
 struct msm8974_asoc_mach_data {
 	int mclk_gpio;
@@ -83,9 +99,6 @@
 static int msm_btsco_rate = BTSCO_RATE_8KHZ;
 static int msm_btsco_ch = 1;
 
-static struct snd_soc_jack hs_jack;
-static struct snd_soc_jack button_jack;
-
 static struct mutex cdc_mclk_mutex;
 static struct q_clkdiv *codec_clk;
 static int clk_users;
@@ -317,7 +330,7 @@
 	return 0;
 }
 
-static int msm8974_enable_codec_ext_clk(struct snd_soc_codec *codec, int enable,
+static int msm_snd_enable_codec_ext_clk(struct snd_soc_codec *codec, int enable,
 					bool dapm)
 {
 	int ret = 0;
@@ -366,9 +379,9 @@
 
 	switch (event) {
 	case SND_SOC_DAPM_PRE_PMU:
-		return msm8974_enable_codec_ext_clk(w->codec, 1, true);
+		return msm_snd_enable_codec_ext_clk(w->codec, 1, true);
 	case SND_SOC_DAPM_POST_PMD:
-		return msm8974_enable_codec_ext_clk(w->codec, 0, true);
+		return msm_snd_enable_codec_ext_clk(w->codec, 0, true);
 	}
 
 	return 0;
@@ -727,24 +740,15 @@
 
 	snd_soc_dapm_sync(dapm);
 
-	err = snd_soc_jack_new(codec, "Headset Jack",
-			       (SND_JACK_HEADSET | SND_JACK_OC_HPHL |
-				SND_JACK_OC_HPHR | SND_JACK_UNSUPPORTED),
-			       &hs_jack);
-	if (err) {
-		pr_err("failed to create new jack\n");
-		return err;
-	}
-
 	snd_soc_dai_set_channel_map(codec_dai, ARRAY_SIZE(tx_ch),
 				    tx_ch, ARRAY_SIZE(rx_ch), rx_ch);
 
-	err = snd_soc_jack_new(codec, "Button Jack",
-			       TAIKO_JACK_BUTTON_MASK, &button_jack);
-	if (err) {
-		pr_err("failed to create new jack\n");
-		return err;
-	}
+	/* start mbhc */
+	mbhc_cfg.calibration = def_taiko_mbhc_cal();
+	if (mbhc_cfg.calibration)
+		err = taiko_hs_detect(codec, &mbhc_cfg);
+	else
+		err = -ENOMEM;
 
 	return err;
 }
@@ -756,6 +760,84 @@
 	return 0;
 }
 
+void *def_taiko_mbhc_cal(void)
+{
+	void *taiko_cal;
+	struct wcd9xxx_mbhc_btn_detect_cfg *btn_cfg;
+	u16 *btn_low, *btn_high;
+	u8 *n_ready, *n_cic, *gain;
+
+	taiko_cal = kzalloc(WCD9XXX_MBHC_CAL_SIZE(WCD9XXX_MBHC_DEF_BUTTONS,
+						WCD9XXX_MBHC_DEF_RLOADS),
+			    GFP_KERNEL);
+	if (!taiko_cal) {
+		pr_err("%s: out of memory\n", __func__);
+		return NULL;
+	}
+
+#define S(X, Y) ((WCD9XXX_MBHC_CAL_GENERAL_PTR(taiko_cal)->X) = (Y))
+	S(t_ldoh, 100);
+	S(t_bg_fast_settle, 100);
+	S(t_shutdown_plug_rem, 255);
+	S(mbhc_nsa, 4);
+	S(mbhc_navg, 4);
+#undef S
+#define S(X, Y) ((WCD9XXX_MBHC_CAL_PLUG_DET_PTR(taiko_cal)->X) = (Y))
+	S(mic_current, TAIKO_PID_MIC_5_UA);
+	S(hph_current, TAIKO_PID_MIC_5_UA);
+	S(t_mic_pid, 100);
+	S(t_ins_complete, 250);
+	S(t_ins_retry, 200);
+#undef S
+#define S(X, Y) ((WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(taiko_cal)->X) = (Y))
+	S(v_no_mic, 30);
+	S(v_hs_max, 2400);
+#undef S
+#define S(X, Y) ((WCD9XXX_MBHC_CAL_BTN_DET_PTR(taiko_cal)->X) = (Y))
+	S(c[0], 62);
+	S(c[1], 124);
+	S(nc, 1);
+	S(n_meas, 3);
+	S(mbhc_nsc, 11);
+	S(n_btn_meas, 1);
+	S(n_btn_con, 2);
+	S(num_btn, WCD9XXX_MBHC_DEF_BUTTONS);
+	S(v_btn_press_delta_sta, 100);
+	S(v_btn_press_delta_cic, 50);
+#undef S
+	btn_cfg = WCD9XXX_MBHC_CAL_BTN_DET_PTR(taiko_cal);
+	btn_low = wcd9xxx_mbhc_cal_btn_det_mp(btn_cfg, MBHC_BTN_DET_V_BTN_LOW);
+	btn_high = wcd9xxx_mbhc_cal_btn_det_mp(btn_cfg,
+					       MBHC_BTN_DET_V_BTN_HIGH);
+	btn_low[0] = -50;
+	btn_high[0] = 34;
+	btn_low[1] = 35;
+	btn_high[1] = 52;
+	btn_low[2] = 53;
+	btn_high[2] = 94;
+	btn_low[3] = 95;
+	btn_high[3] = 133;
+	btn_low[4] = 134;
+	btn_high[4] = 171;
+	btn_low[5] = 172;
+	btn_high[5] = 208;
+	btn_low[6] = 209;
+	btn_high[6] = 244;
+	btn_low[7] = 245;
+	btn_high[7] = 330;
+	n_ready = wcd9xxx_mbhc_cal_btn_det_mp(btn_cfg, MBHC_BTN_DET_N_READY);
+	n_ready[0] = 80;
+	n_ready[1] = 68;
+	n_cic = wcd9xxx_mbhc_cal_btn_det_mp(btn_cfg, MBHC_BTN_DET_N_CIC);
+	n_cic[0] = 60;
+	n_cic[1] = 47;
+	gain = wcd9xxx_mbhc_cal_btn_det_mp(btn_cfg, MBHC_BTN_DET_GAIN);
+	gain[0] = 11;
+	gain[1] = 9;
+
+	return taiko_cal;
+}
+
 static int msm_snd_hw_params(struct snd_pcm_substream *substream,
 			     struct snd_pcm_hw_params *params)
 {