Merge "block: ROW: Fix forced dispatch"
diff --git a/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt b/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt
index cb2c1e1..9743d0d 100644
--- a/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt
+++ b/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt
@@ -54,6 +54,16 @@
 
  - compatible : "qcom,msm-pcm-afe"
 
+* msm-dai-q6-hdmi
+
+Required properties:
+ - compatible : "msm-dai-q6-hdmi"
+ - qcom,msm-dai-q6-dev-id : The hdmi multi channel port ID.
+   It is passed onto the dsp from the apps to form an audio
+   path to the HDMI device. Currently the only supported value
+   is 8, which indicates the rx path used for audio playback
+   on HDMI device.
+
 * msm-dai-q6
 
 [First Level Nodes]
@@ -71,6 +81,7 @@
                             Value is from 16384 to 16393
                             BT SCO port ID value from 12288 to 12289
                             RT Proxy port ID values from 224 to 225 and 240 to 241
+                            FM Rx and TX port ID values from 12292 to 12293
 
 * msm-auxpcm
 
@@ -184,6 +195,11 @@
                 compatible = "qcom,msm-dai-fe";
         };
 
+	qcom,msm-dai-q6-hdmi {
+		compatible = "qcom,msm-dai-q6-hdmi";
+		qcom,msm-dai-q6-dev-id = <8>;
+	};
+
 	qcom,msm-dai-q6 {
 		compatible = "qcom,msm-dai-q6";
 		qcom,msm-dai-q6-sb-0-rx {
@@ -206,6 +222,16 @@
 			qcom,msm-dai-q6-dev-id = <12289>;
 		};
 
+		qcom,msm-dai-q6-int-fm-rx {
+			compatible = "qcom,msm-dai-q6-dev";
+			qcom,msm-dai-q6-dev-id = <12292>;
+		};
+
+		qcom,msm-dai-q6-int-fm-tx {
+			compatible = "qcom,msm-dai-q6-dev";
+			qcom,msm-dai-q6-dev-id = <12293>;
+		};
+
 		qcom,msm-dai-q6-be-afe-pcm-rx {
 			compatible = "qcom,msm-dai-q6-dev";
 			qcom,msm-dai-q6-dev-id = <224>;
diff --git a/arch/arm/boot/dts/msm-pm8941.dtsi b/arch/arm/boot/dts/msm-pm8941.dtsi
index f1e18cf..1b18d25 100644
--- a/arch/arm/boot/dts/msm-pm8941.dtsi
+++ b/arch/arm/boot/dts/msm-pm8941.dtsi
@@ -102,11 +102,12 @@
 			qcom,cxo-freq = <19200000>;
 		};
 
-		pm8941-chg {
+		pm8941_chg: qcom,charger {
 			spmi-dev-container;
 			compatible = "qcom,qpnp-charger";
 			#address-cells = <1>;
 			#size-cells = <1>;
+			status = "disabled";
 
 			qcom,chg-vddmax-mv = <4200>;
 			qcom,chg-vddsafe-mv = <4200>;
@@ -115,6 +116,7 @@
 			qcom,chg-ibatterm-ma = <200>;
 
 			qcom,chg-chgr@1000 {
+				status = "disabled";
 				reg = <0x1000 0x100>;
 				interrupts =	<0x0 0x10 0x0>,
 						<0x0 0x10 0x1>,
@@ -136,6 +138,7 @@
 			};
 
 			qcom,chg-buck@1100 {
+				status = "disabled";
 				reg = <0x1100 0x100>;
 				interrupts =	<0x0 0x11 0x0>,
 						<0x0 0x11 0x1>,
@@ -155,6 +158,7 @@
 			};
 
 			qcom,chg-bat-if@1200 {
+				status = "disabled";
 				reg = <0x1200 0x100>;
 				interrupts =	<0x0 0x12 0x0>,
 						<0x0 0x12 0x1>,
@@ -170,6 +174,7 @@
 			};
 
 			qcom,chg-usb-chgpth@1300 {
+				status = "disabled";
 				reg = <0x1300 0x100>;
 				interrupts =	<0 0x13 0x0>,
 						<0 0x13 0x1>,
@@ -181,6 +186,7 @@
 			};
 
 			qcom,chg-dc-chgpth@1400 {
+				status = "disabled";
 				reg = <0x1400 0x100>;
 				interrupts =	<0x0 0x14 0x0>,
 						<0x0 0x14 0x1>;
@@ -190,6 +196,7 @@
 			};
 
 			qcom,chg-boost@1500 {
+				status = "disabled";
 				reg = <0x1500 0x100>;
 				interrupts =	<0x0 0x15 0x0>,
 						<0x0 0x15 0x1>;
@@ -199,6 +206,7 @@
 			};
 
 			qcom,chg-misc@1600 {
+				status = "disabled";
 				reg = <0x1600 0x100>;
 			};
 		};
@@ -1036,5 +1044,68 @@
 			qcom,label = "wled";
 		};
 
+		pwm@b100 {
+			compatible = "qcom,qpnp-pwm";
+			reg = <0xb100 0x100>,
+			      <0xb042 0x7e>;
+			reg-names = "qpnp-lpg-channel-base", "qpnp-lpg-lut-base";
+			qcom,channel-id = <0>;
+		};
+
+		pwm@b200 {
+			compatible = "qcom,qpnp-pwm";
+			reg = <0xb200 0x100>,
+			      <0xb042 0x7e>;
+			reg-names = "qpnp-lpg-channel-base", "qpnp-lpg-lut-base";
+			qcom,channel-id = <1>;
+		};
+
+		pwm@b300 {
+			compatible = "qcom,qpnp-pwm";
+			reg = <0xb300 0x100>,
+			      <0xb042 0x7e>;
+			reg-names = "qpnp-lpg-channel-base", "qpnp-lpg-lut-base";
+			qcom,channel-id = <2>;
+		};
+
+		pwm@b400 {
+			compatible = "qcom,qpnp-pwm";
+			reg = <0xb400 0x100>,
+			      <0xb042 0x7e>;
+			reg-names = "qpnp-lpg-channel-base", "qpnp-lpg-lut-base";
+			qcom,channel-id = <3>;
+		};
+
+		pwm@b500 {
+			compatible = "qcom,qpnp-pwm";
+			reg = <0xb500 0x100>,
+			      <0xb042 0x7e>;
+			reg-names = "qpnp-lpg-channel-base", "qpnp-lpg-lut-base";
+			qcom,channel-id = <4>;
+		};
+
+		pwm@b600 {
+			compatible = "qcom,qpnp-pwm";
+			reg = <0xb600 0x100>,
+			      <0xb042 0x7e>;
+			reg-names = "qpnp-lpg-channel-base", "qpnp-lpg-lut-base";
+			qcom,channel-id = <5>;
+		};
+
+		pwm@b700 {
+			compatible = "qcom,qpnp-pwm";
+			reg = <0xb700 0x100>,
+			      <0xb042 0x7e>;
+			reg-names = "qpnp-lpg-channel-base", "qpnp-lpg-lut-base";
+			qcom,channel-id = <6>;
+		};
+
+		pwm@b800 {
+			compatible = "qcom,qpnp-pwm";
+			reg = <0xb800 0x100>,
+			      <0xb042 0x7e>;
+			reg-names = "qpnp-lpg-channel-base", "qpnp-lpg-lut-base";
+			qcom,channel-id = <7>;
+		};
 	};
 };
diff --git a/arch/arm/boot/dts/msm8974-cdp.dts b/arch/arm/boot/dts/msm8974-cdp.dts
index 05fcc4f..0e0f6cf 100644
--- a/arch/arm/boot/dts/msm8974-cdp.dts
+++ b/arch/arm/boot/dts/msm8974-cdp.dts
@@ -30,6 +30,10 @@
 		};
 	};
 
+	qcom,hdmi_tx@fd922100 {
+		status = "ok";
+	};
+
 	i2c@f9924000 {
 		atmel_mxt_ts@4a {
 			compatible = "atmel,mxt-ts";
diff --git a/arch/arm/boot/dts/msm8974-fluid.dts b/arch/arm/boot/dts/msm8974-fluid.dts
index b1d467e..891379f 100644
--- a/arch/arm/boot/dts/msm8974-fluid.dts
+++ b/arch/arm/boot/dts/msm8974-fluid.dts
@@ -30,6 +30,10 @@
 		};
 	};
 
+	qcom,hdmi_tx@fd922100 {
+		status = "ok";
+	};
+
 	i2c@f9924000 {
 		atmel_mxt_ts@4a {
 			compatible = "atmel,mxt-ts";
@@ -112,6 +116,7 @@
 
 	gpio_keys {
 		compatible = "gpio-keys";
+		input-name = "gpio-keys";
 
 		camera_snapshot {
 			label = "camera_snapshot";
diff --git a/arch/arm/boot/dts/msm8974-liquid.dts b/arch/arm/boot/dts/msm8974-liquid.dts
index bf4adaf..bc682ea 100644
--- a/arch/arm/boot/dts/msm8974-liquid.dts
+++ b/arch/arm/boot/dts/msm8974-liquid.dts
@@ -54,6 +54,10 @@
 			debounce-interval = <15>;
 		};
 	};
+
+	qcom,hdmi_tx@fd922100 {
+		status = "ok";
+	};
 };
 
 &pm8941_gpios {
diff --git a/arch/arm/boot/dts/msm8974-mtp.dts b/arch/arm/boot/dts/msm8974-mtp.dts
index e0d6ad3..f75ebbe 100644
--- a/arch/arm/boot/dts/msm8974-mtp.dts
+++ b/arch/arm/boot/dts/msm8974-mtp.dts
@@ -30,6 +30,10 @@
 		};
 	};
 
+	qcom,hdmi_tx@fd922100 {
+		status = "disabled";
+	};
+
 	i2c@f9924000 {
 		atmel_mxt_ts@4a {
 			compatible = "atmel,mxt-ts";
@@ -169,6 +173,42 @@
 	cd-gpios = <&msmgpio 62 0x1>;
 };
 
+&usb_otg {
+	qcom,hsusb-otg-otg-control = <2>;
+};
+
+&pm8941_chg {
+	status = "ok";
+
+	qcom,chg-chgr@1000 {
+		status = "ok";
+	};
+
+	qcom,chg-buck@1100 {
+		status = "ok";
+	};
+
+	qcom,chg-bat-if@1200 {
+		status = "ok";
+	};
+
+	qcom,chg-usb-chgpth@1300 {
+		status = "ok";
+	};
+
+	qcom,chg-dc-chgpth@1400 {
+		status = "ok";
+	};
+
+	qcom,chg-boost@1500 {
+		status = "ok";
+	};
+
+	qcom,chg-misc@1600 {
+		status = "ok";
+	};
+};
+
 &pm8941_gpios {
 	gpio@c000 { /* GPIO 1 */
 	};
diff --git a/arch/arm/boot/dts/msm8974.dtsi b/arch/arm/boot/dts/msm8974.dtsi
index 23635de..9d985bf 100644
--- a/arch/arm/boot/dts/msm8974.dtsi
+++ b/arch/arm/boot/dts/msm8974.dtsi
@@ -61,7 +61,7 @@
 		compatible = "qcom,msm-vidc";
 		reg = <0xfdc00000 0xff000>;
 		interrupts = <0 44 0>;
-		vidc-cp-map = <0x1000000 0x40000000>;
+		vidc-cp-map = <0x1000000 0x3f000000>;
 		vidc-ns-map = <0x40000000 0x40000000>;
 		load-freq-tbl = <979200 410000000>,
 			<783360 410000000>,
@@ -94,7 +94,7 @@
 		status = "disabled";
 	};
 
-	usb@f9a55000 {
+	usb_otg: usb@f9a55000 {
 		compatible = "qcom,hsusb-otg";
 		reg = <0xf9a55000 0x400>;
 		interrupts = <0 134 0 0 140 0>;
@@ -704,6 +704,11 @@
 		compatible = "qcom,msm-pcm-afe";
 	};
 
+	qcom,msm-dai-q6-hdmi {
+		compatible = "qcom,msm-dai-q6-hdmi";
+		qcom,msm-dai-q6-dev-id = <8>;
+	};
+
 	qcom,msm-dai-q6 {
 		compatible = "qcom,msm-dai-q6";
 		qcom,msm-dai-q6-sb-0-rx {
@@ -726,6 +731,16 @@
 			qcom,msm-dai-q6-dev-id = <12289>;
 		};
 
+		qcom,msm-dai-q6-int-fm-rx {
+			compatible = "qcom,msm-dai-q6-dev";
+			qcom,msm-dai-q6-dev-id = <12292>;
+		};
+
+		qcom,msm-dai-q6-int-fm-tx {
+			compatible = "qcom,msm-dai-q6-dev";
+			qcom,msm-dai-q6-dev-id = <12293>;
+		};
+
 		qcom,msm-dai-q6-be-afe-pcm-rx {
 			compatible = "qcom,msm-dai-q6-dev";
 			qcom,msm-dai-q6-dev-id = <224>;
diff --git a/arch/arm/boot/dts/msm9625-cdp.dts b/arch/arm/boot/dts/msm9625-cdp.dts
index aa1ec92..6234017 100644
--- a/arch/arm/boot/dts/msm9625-cdp.dts
+++ b/arch/arm/boot/dts/msm9625-cdp.dts
@@ -32,6 +32,14 @@
 	};
 
 	gpio@c300 { /* GPIO 4 */
+		/* ext_2p95v regulator enable config */
+		qcom,mode = <1>; /* Digital output */
+		qcom,output-type = <0>; /* CMOS */
+		qcom,invert = <0>; /* Output low */
+		qcom,out-strength = <1>; /* Low */
+		qcom,vin-sel = <2>; /* PM8019 L11 - 1.8V */
+		qcom,src-select = <0>; /* Constant */
+		qcom,master-en = <1>; /* Enable GPIO */
 	};
 
 	gpio@c400 { /* GPIO 5 */
diff --git a/arch/arm/boot/dts/msm9625-mtp.dts b/arch/arm/boot/dts/msm9625-mtp.dts
index 3ec949f..be57dda 100644
--- a/arch/arm/boot/dts/msm9625-mtp.dts
+++ b/arch/arm/boot/dts/msm9625-mtp.dts
@@ -32,6 +32,14 @@
 	};
 
 	gpio@c300 { /* GPIO 4 */
+		/* ext_2p95v regulator enable config */
+		qcom,mode = <1>; /* Digital output */
+		qcom,output-type = <0>; /* CMOS */
+		qcom,invert = <0>; /* Output low */
+		qcom,out-strength = <1>; /* Low */
+		qcom,vin-sel = <2>; /* PM8019 L11 - 1.8V */
+		qcom,src-select = <0>; /* Constant */
+		qcom,master-en = <1>; /* Enable GPIO */
 	};
 
 	gpio@c400 { /* GPIO 5 */
diff --git a/arch/arm/boot/dts/msm9625-regulator.dtsi b/arch/arm/boot/dts/msm9625-regulator.dtsi
index 1b5b03b..dccc723 100644
--- a/arch/arm/boot/dts/msm9625-regulator.dtsi
+++ b/arch/arm/boot/dts/msm9625-regulator.dtsi
@@ -250,3 +250,12 @@
 		};
 	};
 };
+
+/ {
+	ext_2p95v: regulator-isl80101 {
+		compatible = "regulator-fixed";
+		regulator-name = "ext_2p95v";
+		gpio = <&pm8019_gpios 4 0>;
+		enable-active-high;
+	};
+};
diff --git a/arch/arm/boot/dts/msm9625.dtsi b/arch/arm/boot/dts/msm9625.dtsi
index 3d3f563..8cb7191 100644
--- a/arch/arm/boot/dts/msm9625.dtsi
+++ b/arch/arm/boot/dts/msm9625.dtsi
@@ -173,6 +173,71 @@
 		qcom,i2c-bus-freq = <100000>;
 		qcom,i2c-src-freq = <24000000>;
 	};
+
+	sdcc2: qcom,sdcc@f98a4000 {
+		cell-index = <2>; /* SDC2 SD card slot */
+		compatible = "qcom,msm-sdcc";
+		reg = <0xf98a4000 0x800>,
+		      <0xf98a4800 0x100>,
+		      <0xf9884000 0x7000>;
+		reg-names = "core_mem", "dml_mem", "bam_mem";
+
+		vdd-supply = <&ext_2p95v>;
+
+		vdd-io-supply = <&pm8019_l13>;
+		qcom,sdcc-vdd-io-always_on;
+		qcom,sdcc-vdd-io-lpm_sup;
+		qcom,sdcc-vdd-io-voltage_level = <1800000 2950000>;
+		qcom,sdcc-vdd-io-current_level = <6 22000>;
+
+		qcom,sdcc-pad-pull-on = <0x0 0x3 0x3>;
+		qcom,sdcc-pad-pull-off = <0x0 0x3 0x3>;
+		qcom,sdcc-pad-drv-on = <0x7 0x4 0x4>;
+		qcom,sdcc-pad-drv-off = <0x0 0x0 0x0>;
+
+		qcom,sdcc-clk-rates = <400000 25000000 50000000 100000000 200000000>;
+		qcom,sdcc-sup-voltages = <2950 2950>;
+		qcom,sdcc-bus-width = <4>;
+		qcom,sdcc-xpc;
+		qcom,sdcc-bus-speed-mode = "SDR12", "SDR25", "SDR50", "DDR50", "SDR104";
+		qcom,sdcc-current-limit = <800>;
+
+		interrupt-parent = <&sdcc2>;
+		#address-cells = <0>;
+		interrupts = <0 1 2>;
+		#interrupt-cells = <1>;
+		interrupt-map-mask = <0xffffffff>;
+		interrupt-map = <0 &intc 0 125 0
+				 1 &intc 0 220 0
+				 2 &msmgpio 66 0x3>;
+		interrupt-names = "core_irq", "bam_irq", "status_irq";
+		cd-gpios = <&msmgpio 66 0>;
+	};
+
+	sdcc3: qcom,sdcc@f9864000 {
+		cell-index = <3>; /* SDC3 SDIO slot */
+		compatible = "qcom,msm-sdcc";
+		reg = <0xf9864000 0x800>,
+		      <0xf9864800 0x100>,
+		      <0xf9844000 0x7000>;
+		reg-names = "core_mem", "dml_mem", "bam_mem";
+		interrupts = <0 127 0>, <0 223 0>;
+		interrupt-names = "core_irq", "bam_irq";
+
+		gpios = <&msmgpio 25 0>,
+			<&msmgpio 24 0>,
+			<&msmgpio 16 0>,
+			<&msmgpio 17 0>,
+			<&msmgpio 18 0>,
+			<&msmgpio 19 0>;
+		qcom,sdcc-gpio-names = "CLK", "CMD", "DAT0", "DAT1", "DAT2", "DAT3";
+
+		qcom,sdcc-clk-rates = <400000 25000000 50000000 100000000>;
+		qcom,sdcc-sup-voltages = <2950 2950>;
+		qcom,sdcc-bus-width = <4>;
+		qcom,sdcc-bus-speed-mode = "SDR12", "SDR25", "SDR50", "DDR50";
+		status = "disable";
+	};
 };
 
 /include/ "msm-pm8019-rpm-regulator.dtsi"
diff --git a/arch/arm/configs/msm8974_defconfig b/arch/arm/configs/msm8974_defconfig
index 1230fbe..f0d60c5 100644
--- a/arch/arm/configs/msm8974_defconfig
+++ b/arch/arm/configs/msm8974_defconfig
@@ -278,6 +278,7 @@
 CONFIG_QPNP_BMS=y
 CONFIG_SENSORS_QPNP_ADC_VOLTAGE=y
 CONFIG_SENSORS_QPNP_ADC_CURRENT=y
+CONFIG_QPNP_CHARGER=y
 CONFIG_THERMAL=y
 CONFIG_THERMAL_TSENS8974=y
 CONFIG_WCD9320_CODEC=y
diff --git a/arch/arm/configs/msm9625_defconfig b/arch/arm/configs/msm9625_defconfig
index 740a004..5054247 100644
--- a/arch/arm/configs/msm9625_defconfig
+++ b/arch/arm/configs/msm9625_defconfig
@@ -102,6 +102,7 @@
 CONFIG_SPMI=y
 CONFIG_SPMI_MSM_PMIC_ARB=y
 CONFIG_MSM_QPNP_INT=y
+CONFIG_REGULATOR_FIXED_VOLTAGE=y
 CONFIG_DEBUG_GPIO=y
 CONFIG_GPIO_SYSFS=y
 CONFIG_GPIO_QPNP_PIN=y
diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig
index 7ee0eb2..f8e01b6 100644
--- a/arch/arm/mach-msm/Kconfig
+++ b/arch/arm/mach-msm/Kconfig
@@ -669,6 +669,18 @@
 	help
 	  Support for the Qualcomm MSM8625 Reference Design.
 
+config MACH_QRD_SKUD_PRIME
+	depends on ARCH_MSM8625
+	depends on !MSM_STACKED_MEMORY
+	default y
+	bool "MSM8625 SKUD PRIME"
+	help
+	  Support for the Qualcomm MSM8625 SKUD prime Reference Design.
+	  Add support for a SKUD prime reference design based on MSM8x25
+	  chipset. This device is much closer to a phone than regular form
+	  factor devices, with new touch, display panel and other hardware
+	  configurations.
+
 config MACH_MSM7X30_SURF
        depends on ARCH_MSM7X30
        depends on !MSM_STACKED_MEMORY
diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile
index f073e70..34a81da 100644
--- a/arch/arm/mach-msm/Makefile
+++ b/arch/arm/mach-msm/Makefile
@@ -256,6 +256,7 @@
 obj-$(CONFIG_MACH_MSM8625_SURF) +=  board-msm7x27a.o board-7627a-all.o
 obj-$(CONFIG_MACH_MSM8625_EVB) +=  board-qrd7627a.o board-7627a-all.o
 obj-$(CONFIG_MACH_MSM8625_QRD7) +=  board-qrd7627a.o board-7627a-all.o
+obj-$(CONFIG_MACH_QRD_SKUD_PRIME) +=  board-qrd7627a.o board-7627a-all.o
 obj-$(CONFIG_MACH_MSM8625_FFA) += board-msm7x27a.o board-7627a-all.o
 obj-$(CONFIG_MACH_MSM8625_EVT) += board-msm7x27a.o board-7627a-all.o
 obj-$(CONFIG_ARCH_MSM7X30) += board-msm7x30.o devices-msm7x30.o memory_topology.o
diff --git a/arch/arm/mach-msm/board-9625-gpiomux.c b/arch/arm/mach-msm/board-9625-gpiomux.c
index fe7670b..c4e174b 100644
--- a/arch/arm/mach-msm/board-9625-gpiomux.c
+++ b/arch/arm/mach-msm/board-9625-gpiomux.c
@@ -76,6 +76,82 @@
 
 };
 
+static struct gpiomux_setting sdc3_clk_active_cfg = {
+	.func = GPIOMUX_FUNC_1,
+	.drv = GPIOMUX_DRV_8MA,
+	.pull = GPIOMUX_PULL_NONE,
+};
+
+static struct gpiomux_setting sdc3_cmd_active_cfg = {
+	.func = GPIOMUX_FUNC_1,
+	.drv = GPIOMUX_DRV_8MA,
+	.pull = GPIOMUX_PULL_UP,
+};
+
+static struct gpiomux_setting sdc3_data_0_3_active_cfg = {
+	.func = GPIOMUX_FUNC_6,
+	.drv = GPIOMUX_DRV_8MA,
+	.pull = GPIOMUX_PULL_UP,
+};
+
+static struct gpiomux_setting sdc3_suspended_cfg = {
+	.func = GPIOMUX_FUNC_GPIO,
+	.drv = GPIOMUX_DRV_2MA,
+	.pull = GPIOMUX_PULL_DOWN,
+};
+
+static struct gpiomux_setting sdc3_data_1_suspended_cfg = {
+	.func = GPIOMUX_FUNC_GPIO,
+	.drv = GPIOMUX_DRV_2MA,
+	.pull = GPIOMUX_PULL_UP,
+};
+
+static struct msm_gpiomux_config sdc3_configs[] __initdata = {
+	{
+		.gpio      = 25,
+		.settings = {
+			[GPIOMUX_ACTIVE] = &sdc3_clk_active_cfg,
+			[GPIOMUX_SUSPENDED] = &sdc3_suspended_cfg,
+		},
+	},
+	{
+		.gpio      = 24,
+		.settings = {
+			[GPIOMUX_ACTIVE] = &sdc3_cmd_active_cfg,
+			[GPIOMUX_SUSPENDED] = &sdc3_suspended_cfg,
+		},
+
+	},
+	{
+		.gpio      = 16,
+		.settings = {
+			[GPIOMUX_ACTIVE] = &sdc3_data_0_3_active_cfg,
+			[GPIOMUX_SUSPENDED] = &sdc3_suspended_cfg,
+		},
+	},
+	{
+		.gpio      = 17,
+		.settings = {
+			[GPIOMUX_ACTIVE] = &sdc3_data_0_3_active_cfg,
+			[GPIOMUX_SUSPENDED] = &sdc3_data_1_suspended_cfg,
+		},
+	},
+	{
+		.gpio      = 18,
+		.settings = {
+			[GPIOMUX_ACTIVE] = &sdc3_data_0_3_active_cfg,
+			[GPIOMUX_SUSPENDED] = &sdc3_suspended_cfg,
+		},
+	},
+	{
+		.gpio      = 19,
+		.settings = {
+			[GPIOMUX_ACTIVE] = &sdc3_data_0_3_active_cfg,
+			[GPIOMUX_SUSPENDED] = &sdc3_suspended_cfg,
+		},
+	},
+};
+
 void __init msm9625_init_gpiomux(void)
 {
 	int rc;
@@ -87,4 +163,5 @@
 	}
 
 	msm_gpiomux_install(msm_blsp_configs, ARRAY_SIZE(msm_blsp_configs));
+	msm_gpiomux_install(sdc3_configs, ARRAY_SIZE(sdc3_configs));
 }
diff --git a/arch/arm/mach-msm/board-msm7627a-bt.c b/arch/arm/mach-msm/board-msm7627a-bt.c
index e4edf9b..bcc9645 100644
--- a/arch/arm/mach-msm/board-msm7627a-bt.c
+++ b/arch/arm/mach-msm/board-msm7627a-bt.c
@@ -103,7 +103,8 @@
 	if (machine_is_msm7627a_qrd1())
 		gpio_bt_sys_rest_en = 114;
 	if (machine_is_msm7627a_evb() || machine_is_msm8625_evb()
-				|| machine_is_msm8625_evt())
+				|| machine_is_msm8625_evt()
+				|| machine_is_qrd_skud_prime())
 		gpio_bt_sys_rest_en = 16;
 	if (machine_is_msm8625_qrd7())
 		gpio_bt_sys_rest_en = 88;
@@ -975,6 +976,8 @@
 	int i, rc = 0;
 	struct device *dev;
 
+	if (machine_is_qrd_skud_prime())
+		return;
 
 	gpio_bt_config();
 
diff --git a/arch/arm/mach-msm/board-msm7627a-camera.c b/arch/arm/mach-msm/board-msm7627a-camera.c
index b5f214b..79ad996 100644
--- a/arch/arm/mach-msm/board-msm7627a-camera.c
+++ b/arch/arm/mach-msm/board-msm7627a-camera.c
@@ -403,7 +403,8 @@
 	if (machine_is_msm8625_evb() || machine_is_msm7627a_evb()
 				||  machine_is_msm8625_evt()
 				|| machine_is_msm7627a_qrd3()
-				|| machine_is_msm8625_qrd7()) {
+				|| machine_is_msm8625_qrd7()
+				|| machine_is_qrd_skud_prime()) {
 		sensor_board_info_ov7692.cam_vreg =
 			ov7692_gpio_vreg;
 		sensor_board_info_ov7692.num_vreg =
@@ -420,7 +421,8 @@
 	platform_device_register(&msm_camera_server);
 	if (machine_is_msm8625_surf() || machine_is_msm8625_evb()
 			|| machine_is_msm8625_evt()
-			|| machine_is_msm8625_qrd7()) {
+			|| machine_is_msm8625_qrd7()
+			|| machine_is_qrd_skud_prime()) {
 		platform_device_register(&msm8625_device_csic0);
 		platform_device_register(&msm8625_device_csic1);
 	} else {
@@ -429,7 +431,8 @@
 	}
 	if (machine_is_msm8625_evb()
 			|| machine_is_msm8625_evt()
-			|| machine_is_msm8625_qrd7())
+			|| machine_is_msm8625_qrd7()
+			|| machine_is_qrd_skud_prime())
 		*(int *) msm7x27a_device_clkctl.dev.platform_data = 1;
 	platform_device_register(&msm7x27a_device_clkctl);
 	platform_device_register(&msm7x27a_device_vfe);
@@ -1175,7 +1178,6 @@
 #ifndef CONFIG_MSM_CAMERA_V4L2
 	int rc;
 #endif
-
 	pr_debug("msm7627a_camera_init Entered\n");
 
 	if (machine_is_msm7627a_qrd3() || machine_is_msm8625_qrd7()) {
@@ -1194,7 +1196,8 @@
 	if (machine_is_msm7627a_evb() || machine_is_msm8625_evb()
 			|| machine_is_msm8625_evt()
 			|| machine_is_msm7627a_qrd3()
-			|| machine_is_msm8625_qrd7()) {
+			|| machine_is_msm8625_qrd7()
+			|| machine_is_qrd_skud_prime()) {
 #ifndef CONFIG_MSM_CAMERA_V4L2
 		lcd_camera_power_init();
 #endif
@@ -1210,7 +1213,8 @@
 			|| machine_is_msm8625_evb()
 			|| machine_is_msm8625_evt()
 			|| machine_is_msm7627a_qrd3()
-			|| machine_is_msm8625_qrd7()) {
+			|| machine_is_msm8625_qrd7()
+			|| machine_is_qrd_skud_prime()) {
 		platform_add_devices(camera_devices_evb,
 				ARRAY_SIZE(camera_devices_evb));
 	} else if (machine_is_msm7627a_qrd3())
@@ -1223,7 +1227,8 @@
 					|| !machine_is_msm8625_evb()
 					|| !machine_is_msm8625_evt()
 					|| !machine_is_msm7627a_qrd3()
-					|| !machine_is_msm8625_qrd7())
+					|| !machine_is_msm8625_qrd7()
+					|| !machine_is_qrd_skud_prime())
 		register_i2c_devices();
 #ifndef CONFIG_MSM_CAMERA_V4L2
 	rc = regulator_bulk_get(NULL, ARRAY_SIZE(regs_camera), regs_camera);
@@ -1253,7 +1258,8 @@
 			|| machine_is_msm8625_evb()
 			|| machine_is_msm8625_evt()
 			|| machine_is_msm7627a_qrd3()
-			|| machine_is_msm8625_qrd7()) {
+			|| machine_is_msm8625_qrd7()
+			|| machine_is_qrd_skud_prime()) {
 		pr_debug("machine_is_msm7627a_evb i2c_register_board_info\n");
 		i2c_register_board_info(MSM_GSBI0_QUP_I2C_BUS_ID,
 				i2c_camera_devices_evb,
diff --git a/arch/arm/mach-msm/board-msm7627a-display.c b/arch/arm/mach-msm/board-msm7627a-display.c
index 8213000..1249c7b 100644
--- a/arch/arm/mach-msm/board-msm7627a-display.c
+++ b/arch/arm/mach-msm/board-msm7627a-display.c
@@ -542,7 +542,8 @@
 		if (!strncmp(name, "lcdc_truly_hvga_ips3p2335_pt", 28))
 			ret = 0;
 	} else if (machine_is_msm7627a_evb() || machine_is_msm8625_evb() ||
-			machine_is_msm8625_evt()) {
+			machine_is_msm8625_evt() ||
+			machine_is_qrd_skud_prime()) {
 		if (!strncmp(name, "mipi_cmd_nt35510_wvga", 21))
 			ret = 0;
 	}
@@ -796,7 +797,8 @@
 	if (machine_is_msm7625a_surf() || machine_is_msm7625a_ffa())
 		fb_size = MSM7x25A_MSM_FB_SIZE;
 	else if (machine_is_msm7627a_evb() || machine_is_msm8625_evb()
-						|| machine_is_msm8625_evt())
+						|| machine_is_msm8625_evt()
+						|| machine_is_qrd_skud_prime())
 		fb_size = MSM8x25_MSM_FB_SIZE;
 	else
 		fb_size = MSM_FB_SIZE;
@@ -1017,7 +1019,8 @@
 	if (machine_is_msm7627a_qrd1())
 		rc = msm_fb_dsi_client_qrd1_reset();
 	else if (machine_is_msm7627a_evb() || machine_is_msm8625_evb()
-						|| machine_is_msm8625_evt())
+						|| machine_is_msm8625_evt()
+						|| machine_is_qrd_skud_prime())
 		rc = msm_fb_dsi_client_qrd3_reset();
 	else
 		rc = msm_fb_dsi_client_msm_reset();
@@ -1124,10 +1127,12 @@
 			wmb();
 			lcdc_reset_cfg |= 1;
 			writel_relaxed(lcdc_reset_cfg, lcdc_reset_ptr);
+			msleep(20);
 		} else {
 			gpio_set_value_cansleep(GPIO_LCDC_BRDG_RESET_N, 0);
 			msleep(20);
 			gpio_set_value_cansleep(GPIO_LCDC_BRDG_RESET_N, 1);
+			msleep(20);
 		}
 	} else {
 		gpio_set_value_cansleep(GPIO_LCDC_BRDG_PD, 1);
@@ -1325,7 +1330,8 @@
 	if (machine_is_msm7627a_qrd1())
 		rc = mipi_dsi_panel_qrd1_power(on);
 	else if (machine_is_msm7627a_evb() || machine_is_msm8625_evb()
-						|| machine_is_msm8625_evt())
+						|| machine_is_msm8625_evt()
+						|| machine_is_qrd_skud_prime())
 		rc = mipi_dsi_panel_qrd3_power(on);
 	else
 		rc = mipi_dsi_panel_msm_power(on);
@@ -1389,7 +1395,8 @@
 		platform_add_devices(qrd_fb_devices,
 				ARRAY_SIZE(qrd_fb_devices));
 	} else if (machine_is_msm7627a_evb() || machine_is_msm8625_evb()
-						|| machine_is_msm8625_evt()) {
+					|| machine_is_msm8625_evt()
+					|| machine_is_qrd_skud_prime()) {
 		mipi_NT35510_pdata.bl_lock = 1;
 		mipi_NT35516_pdata.bl_lock = 1;
 		if (disable_splash)
@@ -1421,7 +1428,8 @@
 	msm_fb_register_device("mipi_dsi", &mipi_dsi_pdata);
 #endif
 	if (machine_is_msm7627a_evb() || machine_is_msm8625_evb()
-					|| machine_is_msm8625_evt()) {
+					|| machine_is_msm8625_evt()
+					|| machine_is_qrd_skud_prime()) {
 		gpio_reg_2p85v = regulator_get(&mipi_dsi_device.dev,
 								"lcd_vdd");
 		if (IS_ERR(gpio_reg_2p85v))
diff --git a/arch/arm/mach-msm/board-msm7627a-storage.c b/arch/arm/mach-msm/board-msm7627a-storage.c
index 49ff393..07ff389 100644
--- a/arch/arm/mach-msm/board-msm7627a-storage.c
+++ b/arch/arm/mach-msm/board-msm7627a-storage.c
@@ -378,9 +378,9 @@
 	if (mmc_regulator_init(1, "mmc", 2850000))
 		return;
 	/* 8x25 EVT do not use hw detector */
-	if (!(machine_is_msm8625_evt()))
+	if (!((machine_is_msm8625_evt() || machine_is_qrd_skud_prime())))
 		sdc1_plat_data.status_irq = MSM_GPIO_TO_INT(gpio_sdc1_hw_det);
-	if (machine_is_msm8625_evt())
+	if (machine_is_msm8625_evt() || machine_is_qrd_skud_prime())
 		sdc1_plat_data.status = NULL;
 
 	msm_add_sdcc(1, &sdc1_plat_data);
diff --git a/arch/arm/mach-msm/board-msm7627a-wlan.c b/arch/arm/mach-msm/board-msm7627a-wlan.c
index 79f213e..ab29fc5 100644
--- a/arch/arm/mach-msm/board-msm7627a-wlan.c
+++ b/arch/arm/mach-msm/board-msm7627a-wlan.c
@@ -23,6 +23,7 @@
 
 #define GPIO_WLAN_3V3_EN 119
 static const char *id = "WLAN";
+static bool wlan_powered_up;
 
 enum {
 	WLAN_VREG_S3 = 0,
@@ -52,7 +53,8 @@
 					|| machine_is_msm8625_evb()
 					|| machine_is_msm8625_evt()
 					|| machine_is_msm7627a_qrd3()
-					|| machine_is_msm8625_qrd7())
+					|| machine_is_msm8625_qrd7()
+					|| machine_is_qrd_skud_prime())
 		gpio_wlan_sys_rest_en = 124;
 }
 
@@ -199,6 +201,11 @@
 	int rc = 0;
 	static bool init_done;
 
+	if (wlan_powered_up) {
+		pr_info("WLAN already powered up\n");
+		return 0;
+	}
+
 	if (unlikely(!init_done)) {
 		gpio_wlan_config();
 		rc = qrf6285_init_regs();
@@ -237,7 +244,8 @@
 					|| machine_is_msm8625_evb()
 					|| machine_is_msm8625_evt()
 					|| machine_is_msm7627a_qrd3()
-					|| machine_is_msm8625_qrd7()) {
+					|| machine_is_msm8625_qrd7()
+					|| machine_is_qrd_skud_prime()) {
 		rc = gpio_tlmm_config(GPIO_CFG(gpio_wlan_sys_rest_en, 0,
 					GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL,
 					GPIO_CFG_2MA), GPIO_CFG_ENABLE);
@@ -279,13 +287,17 @@
 	}
 
 	pr_info("WLAN power-up success\n");
+	wlan_powered_up = true;
 	return 0;
 set_clock_fail:
 	setup_wlan_clock(0);
 set_gpio_fail:
 	setup_wlan_gpio(0);
 gpio_fail:
-	gpio_free(gpio_wlan_sys_rest_en);
+	if (!(machine_is_msm7627a_qrd1() || machine_is_msm7627a_evb() ||
+	    machine_is_msm8625_evb() || machine_is_msm8625_evt() ||
+	    machine_is_msm7627a_qrd3() || machine_is_msm8625_qrd7()))
+			gpio_free(gpio_wlan_sys_rest_en);
 qrd_gpio_fail:
 	/* GPIO_WLAN_3V3_EN is only required for the QRD7627a */
 	if (machine_is_msm7627a_qrd1())
@@ -294,6 +306,7 @@
 	wlan_switch_regulators(0);
 out:
 	pr_info("WLAN power-up failed\n");
+	wlan_powered_up = false;
 	return rc;
 }
 
@@ -301,6 +314,11 @@
 {
 	int rc = 0;
 
+	if (!wlan_powered_up) {
+		pr_info("WLAN is not powered up, returning success\n");
+		return 0;
+	}
+
 	/* Disable the A0 clock */
 	rc = setup_wlan_clock(on);
 	if (rc) {
@@ -316,7 +334,8 @@
 					|| machine_is_msm8625_evb()
 					|| machine_is_msm8625_evt()
 					|| machine_is_msm7627a_qrd3()
-					|| machine_is_msm8625_qrd7()) {
+					|| machine_is_msm8625_qrd7()
+					|| machine_is_qrd_skud_prime()) {
 		rc = gpio_tlmm_config(GPIO_CFG(gpio_wlan_sys_rest_en, 0,
 					GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL,
 					GPIO_CFG_2MA), GPIO_CFG_ENABLE);
@@ -327,20 +346,12 @@
 		}
 		gpio_set_value(gpio_wlan_sys_rest_en, 0);
 	} else {
-		rc = gpio_request(gpio_wlan_sys_rest_en, "WLAN_DEEP_SLEEP_N");
-		if (!rc) {
-			rc = setup_wlan_gpio(on);
-			if (rc) {
-				pr_err("%s: setup_wlan_gpio = %d\n",
-					__func__, rc);
-				goto set_gpio_fail;
-			}
-			gpio_free(gpio_wlan_sys_rest_en);
-		} else {
-			pr_err("%s: WLAN sys_rest_en GPIO %d request failed %d\n",
-				__func__, gpio_wlan_sys_rest_en, rc);
-			goto out;
+		rc = setup_wlan_gpio(on);
+		if (rc) {
+			pr_err("%s: setup_wlan_gpio = %d\n", __func__, rc);
+			goto set_gpio_fail;
 		}
+		gpio_free(gpio_wlan_sys_rest_en);
 	}
 
 	/* GPIO_WLAN_3V3_EN is only required for the QRD7627a */
@@ -362,7 +373,7 @@
 			__func__, rc);
 		goto reg_disable;
 	}
-
+	wlan_powered_up = false;
 	pr_info("WLAN power-down success\n");
 	return 0;
 set_clock_fail:
@@ -370,14 +381,16 @@
 set_gpio_fail:
 	setup_wlan_gpio(0);
 gpio_fail:
-	gpio_free(gpio_wlan_sys_rest_en);
+	if (!(machine_is_msm7627a_qrd1() || machine_is_msm7627a_evb() ||
+	    machine_is_msm8625_evb() || machine_is_msm8625_evt() ||
+	    machine_is_msm7627a_qrd3() || machine_is_msm8625_qrd7()))
+			gpio_free(gpio_wlan_sys_rest_en);
 qrd_gpio_fail:
 	/* GPIO_WLAN_3V3_EN is only required for the QRD7627a */
 	if (machine_is_msm7627a_qrd1())
 		gpio_free(GPIO_WLAN_3V3_EN);
 reg_disable:
 	wlan_switch_regulators(0);
-out:
 	pr_info("WLAN power-down failed\n");
 	return rc;
 }
diff --git a/arch/arm/mach-msm/board-qrd7627a.c b/arch/arm/mach-msm/board-qrd7627a.c
index ec8e438..3045f07 100644
--- a/arch/arm/mach-msm/board-qrd7627a.c
+++ b/arch/arm/mach-msm/board-qrd7627a.c
@@ -884,7 +884,8 @@
 static void __init add_platform_devices(void)
 {
 	if (machine_is_msm8625_evb() || machine_is_msm8625_qrd7()
-				|| machine_is_msm8625_evt()) {
+				|| machine_is_msm8625_evt()
+				|| machine_is_qrd_skud_prime()) {
 		platform_add_devices(msm8625_evb_devices,
 				ARRAY_SIZE(msm8625_evb_devices));
 		platform_add_devices(qrd3_devices,
@@ -899,7 +900,8 @@
 				ARRAY_SIZE(qrd3_devices));
 
 	if (machine_is_msm7627a_evb() || machine_is_msm8625_evb()
-				|| machine_is_msm8625_evt())
+				|| machine_is_msm8625_evt()
+				|| machine_is_qrd_skud_prime())
 		platform_add_devices(msm8625_lcd_camera_devices,
 				ARRAY_SIZE(msm8625_lcd_camera_devices));
 	else if (machine_is_msm8625_qrd7())
@@ -1070,3 +1072,13 @@
 	.init_early	= qrd7627a_init_early,
 	.handle_irq	= gic_handle_irq,
 MACHINE_END
+MACHINE_START(QRD_SKUD_PRIME, "QRD MSM8625 SKUD PRIME")
+	.atag_offset	= 0x100,
+	.map_io		= msm8625_map_io,
+	.reserve	= msm8625_reserve,
+	.init_irq	= msm8625_init_irq,
+	.init_machine	= msm_qrd_init,
+	.timer		= &msm_timer,
+	.init_early	= qrd7627a_init_early,
+	.handle_irq	= gic_handle_irq,
+MACHINE_END
diff --git a/arch/arm/mach-msm/clock-9625.c b/arch/arm/mach-msm/clock-9625.c
index 02dd562..69a52e9 100644
--- a/arch/arm/mach-msm/clock-9625.c
+++ b/arch/arm/mach-msm/clock-9625.c
@@ -2014,9 +2014,9 @@
 	CLK_LOOKUP("iface_clk", gcc_sdcc2_ahb_clk.c, "f98a4000.qcom,sdcc"),
 	CLK_LOOKUP("core_clk", gcc_sdcc2_apps_clk.c, "f98a4000.qcom,sdcc"),
 	CLK_LOOKUP("bus_clk",  pnoc_sdcc2_clk.c, "f98a4000.qcom,sdcc"),
-	CLK_LOOKUP("iface_clk", gcc_sdcc3_ahb_clk.c, ""),
-	CLK_LOOKUP("core_clk", gcc_sdcc3_apps_clk.c, ""),
-	CLK_LOOKUP("bus_clk", pnoc_sdcc3_clk.c, ""),
+	CLK_LOOKUP("iface_clk", gcc_sdcc3_ahb_clk.c, "f9864000.qcom,sdcc"),
+	CLK_LOOKUP("core_clk", gcc_sdcc3_apps_clk.c, "f9864000.qcom,sdcc"),
+	CLK_LOOKUP("bus_clk", pnoc_sdcc3_clk.c, "f9864000.qcom,sdcc"),
 
 	CLK_LOOKUP("iface_clk", gcc_usb_hs_ahb_clk.c,     "f9a55000.usb"),
 	CLK_LOOKUP("core_clk", gcc_usb_hs_system_clk.c,   "f9a55000.usb"),
diff --git a/arch/arm/mach-msm/clock.c b/arch/arm/mach-msm/clock.c
index c30bd79..1f12bc7 100644
--- a/arch/arm/mach-msm/clock.c
+++ b/arch/arm/mach-msm/clock.c
@@ -425,6 +425,19 @@
 
 static struct clock_init_data *clk_init_data;
 
+static void init_sibling_lists(struct clk_lookup *clock_tbl, size_t num_clocks)
+{
+	struct clk *clk, *parent;
+	unsigned n;
+
+	for (n = 0; n < num_clocks; n++) {
+		clk = clock_tbl[n].clk;
+		parent = clk_get_parent(clk);
+		if (parent && list_empty(&clk->siblings))
+			list_add(&clk->siblings, &parent->children);
+	}
+}
+
 /**
  * msm_clock_register() - Register additional clock tables
  * @table: Table of clocks
@@ -443,6 +456,7 @@
 	if (!table)
 		return -EINVAL;
 
+	init_sibling_lists(table, size);
 	clkdev_add_table(table, size);
 	clock_debug_register(table, size);
 
@@ -514,11 +528,12 @@
 	unsigned n;
 	struct clk_lookup *clock_tbl;
 	size_t num_clocks;
-	struct clk *clk;
 
 	if (!data)
 		return -EINVAL;
 
+	clock_debug_init();
+
 	clk_init_data = data;
 	if (clk_init_data->pre_init)
 		clk_init_data->pre_init();
@@ -526,14 +541,6 @@
 	clock_tbl = data->table;
 	num_clocks = data->size;
 
-	for (n = 0; n < num_clocks; n++) {
-		struct clk *parent;
-		clk = clock_tbl[n].clk;
-		parent = clk_get_parent(clk);
-		if (parent && list_empty(&clk->siblings))
-			list_add(&clk->siblings, &parent->children);
-	}
-
 	/*
 	 * Detect and preserve initial clock state until clock_late_init() or
 	 * a driver explicitly changes it, whichever is first.
@@ -541,14 +548,11 @@
 	for (n = 0; n < num_clocks; n++)
 		__handoff_clk(clock_tbl[n].clk);
 
-	clkdev_add_table(clock_tbl, num_clocks);
+	msm_clock_register(clock_tbl, num_clocks);
 
 	if (clk_init_data->post_init)
 		clk_init_data->post_init();
 
-	clock_debug_init();
-	clock_debug_register(clock_tbl, num_clocks);
-
 	return 0;
 }
 
diff --git a/arch/arm/mach-msm/lpm_resources.c b/arch/arm/mach-msm/lpm_resources.c
index 364f297..2db92f3 100644
--- a/arch/arm/mach-msm/lpm_resources.c
+++ b/arch/arm/mach-msm/lpm_resources.c
@@ -387,7 +387,7 @@
 static bool msm_lpm_beyond_limits_l2(struct msm_rpmrs_limits *limits)
 {
 	uint32_t l2;
-	bool ret = true;
+	bool ret = false;
 	struct msm_lpm_resource *rs = &msm_lpm_l2;
 
 	if (rs->valid) {
@@ -669,7 +669,7 @@
 	msm_lpm_get_rpm_notif = false;
 	for (i = 0; i < ARRAY_SIZE(msm_lpm_resources); i++) {
 		rs = msm_lpm_resources[i];
-		if (rs->flush)
+		if (rs->valid && rs->flush)
 			rs->flush(notify_rpm);
 	}
 	msm_lpm_get_rpm_notif = true;
diff --git a/arch/arm/tools/mach-types b/arch/arm/tools/mach-types
index d283705..2b31f47 100644
--- a/arch/arm/tools/mach-types
+++ b/arch/arm/tools/mach-types
@@ -1196,3 +1196,4 @@
 msm8625_qrd7		MACH_MSM8625_QRD7	MSM8625_QRD7		4095
 msm8625_ffa		MACH_MSM8625_FFA	MSM8625_FFA		4166
 msm8625_evt		MACH_MSM8625_EVT	MSM8625_EVT		4193
+qrd_skud_prime		MACH_QRD_SKUD_PRIME	QRD_SKUD_PRIME		4393
diff --git a/drivers/iommu/msm_iommu.c b/drivers/iommu/msm_iommu.c
index df66a3a..63a027b 100644
--- a/drivers/iommu/msm_iommu.c
+++ b/drivers/iommu/msm_iommu.c
@@ -1122,7 +1122,12 @@
 		}
 
 		SET_FSR(base, num, fsr);
-		SET_RESUME(base, num, 1);
+		/*
+		 * Only resume fetches if the registered fault handler
+		 * allows it
+		 */
+		if (ret != -EBUSY)
+			SET_RESUME(base, num, 1);
 
 		ret = IRQ_HANDLED;
 	} else
diff --git a/drivers/mfd/wcd9xxx-core.c b/drivers/mfd/wcd9xxx-core.c
index 65537e4..62f1a93 100644
--- a/drivers/mfd/wcd9xxx-core.c
+++ b/drivers/mfd/wcd9xxx-core.c
@@ -1205,9 +1205,6 @@
 		pr_err("%s: error, initializing device failed\n", __func__);
 		goto err_slim_add;
 	}
-
-	wcd9xxx_init_slimslave(wcd9xxx, wcd9xxx_pgd_la);
-
 #ifdef CONFIG_DEBUG_FS
 	debugCodec = wcd9xxx;
 
diff --git a/drivers/mfd/wcd9xxx-slimslave.c b/drivers/mfd/wcd9xxx-slimslave.c
index b4cf435..6e6de37 100644
--- a/drivers/mfd/wcd9xxx-slimslave.c
+++ b/drivers/mfd/wcd9xxx-slimslave.c
@@ -16,44 +16,20 @@
 
 #define WCD9XXX_CHIP_ID_TAIKO 0x00000201
 
-struct wcd9xxx_slim_sch_rx {
-	u32 sph;
-	u32 ch_num;
-	u16 ch_h;
-	u16 grph;
-};
-
-struct wcd9xxx_slim_sch_tx {
-	u32 sph;
-	u32 ch_num;
-	u16 ch_h;
-	u16 grph;
-};
-
 struct wcd9xxx_slim_sch {
-	struct wcd9xxx_slim_sch_rx rx[SLIM_MAX_RX_PORTS];
-	struct wcd9xxx_slim_sch_tx tx[SLIM_MAX_TX_PORTS];
-
-	u16 rx_port_start_offset;
-	u16 num_rx_slave_port;
-	u16 port_ch_0_start_port_id;
-	u16 port_ch_0_end_port_id;
-	u16 pgd_tx_port_ch_1_end_port_id;
 	u16 rx_port_ch_reg_base;
 	u16 port_tx_cfg_reg_base;
 	u16 port_rx_cfg_reg_base;
-	int number_of_tx_slave_dev_ports;
-	int number_of_rx_slave_dev_ports;
 };
 
 static struct wcd9xxx_slim_sch sh_ch;
 
-static int wcd9xxx_alloc_slim_sh_ch_rx(struct wcd9xxx *wcd9xxx,
-				       u8 wcd9xxx_pgd_la);
-static int wcd9xxx_alloc_slim_sh_ch_tx(struct wcd9xxx *wcd9xxx,
-					u8 wcd9xxx_pgd_la);
-static int wcd9xxx_dealloc_slim_sh_ch_rx(struct wcd9xxx *wcd9xxx);
-static int wcd9xxx_dealloc_slim_sh_ch_tx(struct wcd9xxx *wcd9xxx);
+static int wcd9xxx_alloc_slim_sh_ch(struct wcd9xxx *wcd9xxx,
+				    u8 wcd9xxx_pgd_la, u32 cnt,
+				    struct wcd9xxx_ch *channels, u32 path);
+
+static int wcd9xxx_dealloc_slim_sh_ch(struct slim_device *slim,
+				      u32 cnt, struct wcd9xxx_ch *channels);
 
 static int wcd9xxx_configure_ports(struct wcd9xxx *wcd9xxx)
 {
@@ -65,55 +41,27 @@
 	id = cpu_to_be32(id);
 	pr_debug("%s: chip id 0x%08x\n", __func__, id);
 	if (id != WCD9XXX_CHIP_ID_TAIKO) {
-		sh_ch.rx_port_start_offset =
-		    TABLA_SB_PGD_OFFSET_OF_RX_SLAVE_DEV_PORTS;
-		sh_ch.num_rx_slave_port =
-		    TABLA_SB_PGD_MAX_NUMBER_OF_RX_SLAVE_DEV_PORTS;
-		sh_ch.port_ch_0_start_port_id =
-		    TABLA_SB_PGD_RX_PORT_MULTI_CHANNEL_0_START_PORT_ID;
-		sh_ch.port_ch_0_end_port_id =
-		    TABLA_SB_PGD_RX_PORT_MULTI_CHANNEL_0_END_PORT_ID;
-		sh_ch.pgd_tx_port_ch_1_end_port_id =
-		    TABLA_SB_PGD_TX_PORT_MULTI_CHANNEL_1_END_PORT_ID;
-
-		sh_ch.rx_port_ch_reg_base =
-		    0x180 + (TABLA_SB_PGD_OFFSET_OF_RX_SLAVE_DEV_PORTS * 4);
-		sh_ch.port_rx_cfg_reg_base =
-		    0x040 + (TABLA_SB_PGD_OFFSET_OF_RX_SLAVE_DEV_PORTS);
-		sh_ch.port_tx_cfg_reg_base = 0x040;
-
-		sh_ch.number_of_tx_slave_dev_ports =
-		    TABLA_SB_PGD_MAX_NUMBER_OF_TX_SLAVE_DEV_PORTS;
-		sh_ch.number_of_rx_slave_dev_ports =
-		    TABLA_SB_PGD_MAX_NUMBER_OF_RX_SLAVE_DEV_PORTS;
-	} else {
-		sh_ch.rx_port_start_offset =
-		    TAIKO_SB_PGD_OFFSET_OF_RX_SLAVE_DEV_PORTS;
-		sh_ch.num_rx_slave_port =
-		    TAIKO_SB_PGD_MAX_NUMBER_OF_RX_SLAVE_DEV_PORTS;
-		sh_ch.port_ch_0_start_port_id =
-		    TAIKO_SB_PGD_RX_PORT_MULTI_CHANNEL_0_START_PORT_ID;
-		sh_ch.port_ch_0_end_port_id =
-		    TAIKO_SB_PGD_RX_PORT_MULTI_CHANNEL_0_END_PORT_ID;
-		sh_ch.pgd_tx_port_ch_1_end_port_id =
-		    TAIKO_SB_PGD_TX_PORT_MULTI_CHANNEL_1_END_PORT_ID;
-
-		sh_ch.rx_port_ch_reg_base = 0x180;
+		sh_ch.rx_port_ch_reg_base = 0x180 ;
 		sh_ch.port_rx_cfg_reg_base = 0x040;
+		sh_ch.port_tx_cfg_reg_base = 0x040;
+	} else {
+		sh_ch.rx_port_ch_reg_base =
+			0x180 - (TAIKO_SB_PGD_OFFSET_OF_RX_SLAVE_DEV_PORTS * 4);
+		sh_ch.port_rx_cfg_reg_base =
+			0x040 - TAIKO_SB_PGD_OFFSET_OF_RX_SLAVE_DEV_PORTS ;
 		sh_ch.port_tx_cfg_reg_base = 0x050;
-
-		sh_ch.number_of_tx_slave_dev_ports =
-		    TAIKO_SB_PGD_MAX_NUMBER_OF_TX_SLAVE_DEV_PORTS;
-		sh_ch.number_of_rx_slave_dev_ports =
-		    TAIKO_SB_PGD_MAX_NUMBER_OF_RX_SLAVE_DEV_PORTS;
 	}
 
 	return 0;
 }
 
-int wcd9xxx_init_slimslave(struct wcd9xxx *wcd9xxx, u8 wcd9xxx_pgd_la)
+
+int wcd9xxx_init_slimslave(struct wcd9xxx *wcd9xxx, u8 wcd9xxx_pgd_la,
+			   unsigned int tx_num, unsigned int *tx_slot,
+			   unsigned int rx_num, unsigned int *rx_slot)
 {
 	int ret = 0;
+	int i;
 
 	ret = wcd9xxx_configure_ports(wcd9xxx);
 	if (ret) {
@@ -122,125 +70,106 @@
 		goto err;
 	}
 
-	ret = wcd9xxx_alloc_slim_sh_ch_rx(wcd9xxx, wcd9xxx_pgd_la);
-	if (ret) {
-		pr_err("%s: Failed to alloc rx slimbus shared channels\n",
-		       __func__);
-		goto err;
+	if (wcd9xxx->rx_chs) {
+		wcd9xxx->num_rx_port = rx_num;
+		for (i = 0; i < rx_num; i++) {
+			wcd9xxx->rx_chs[i].ch_num = rx_slot[i];
+			INIT_LIST_HEAD(&wcd9xxx->rx_chs[i].list);
+		}
+		ret = wcd9xxx_alloc_slim_sh_ch(wcd9xxx, wcd9xxx_pgd_la,
+						wcd9xxx->num_rx_port,
+						wcd9xxx->rx_chs,
+						SLIM_SINK);
+		if (ret) {
+			pr_err("%s: Failed to alloc %d rx slimbus channels\n",
+				__func__, wcd9xxx->num_rx_port);
+			kfree(wcd9xxx->rx_chs);
+			wcd9xxx->rx_chs = NULL;
+			wcd9xxx->num_rx_port = 0;
+		}
+	} else {
+		pr_err("Not able to allocate memory for %d slimbus rx ports\n",
+			wcd9xxx->num_rx_port);
 	}
-	ret = wcd9xxx_alloc_slim_sh_ch_tx(wcd9xxx, wcd9xxx_pgd_la);
-	if (ret) {
-		pr_err("%s: Failed to alloc tx slimbus shared channels\n",
-		       __func__);
-		goto tx_err;
+
+	if (wcd9xxx->tx_chs) {
+		wcd9xxx->num_tx_port = tx_num;
+		for (i = 0; i < tx_num; i++) {
+			wcd9xxx->tx_chs[i].ch_num = tx_slot[i];
+			INIT_LIST_HEAD(&wcd9xxx->tx_chs[i].list);
+		}
+		ret = wcd9xxx_alloc_slim_sh_ch(wcd9xxx, wcd9xxx_pgd_la,
+						wcd9xxx->num_tx_port,
+						wcd9xxx->tx_chs,
+						SLIM_SRC);
+		if (ret) {
+			pr_err("%s: Failed to alloc %d tx slimbus channels\n",
+				__func__, wcd9xxx->num_tx_port);
+			kfree(wcd9xxx->tx_chs);
+			wcd9xxx->tx_chs = NULL;
+			wcd9xxx->num_tx_port = 0;
+		}
+	} else {
+		pr_err("Not able to allocate memory for %d slimbus tx ports\n",
+			wcd9xxx->num_tx_port);
 	}
+
 	return 0;
-tx_err:
-	wcd9xxx_dealloc_slim_sh_ch_rx(wcd9xxx);
 err:
 	return ret;
 }
 
-
 int wcd9xxx_deinit_slimslave(struct wcd9xxx *wcd9xxx)
 {
-	int ret = 0;
-	ret = wcd9xxx_dealloc_slim_sh_ch_rx(wcd9xxx);
-	if (ret < 0) {
-		pr_err("%s: fail to dealloc rx slim ports\n", __func__);
-		goto err;
+	if (wcd9xxx->num_rx_port) {
+		wcd9xxx_dealloc_slim_sh_ch(wcd9xxx->slim,
+					wcd9xxx->num_rx_port,
+					wcd9xxx->rx_chs);
+		wcd9xxx->num_rx_port = 0;
 	}
-	ret = wcd9xxx_dealloc_slim_sh_ch_tx(wcd9xxx);
-	if (ret < 0) {
-		pr_err("%s: fail to dealloc tx slim ports\n", __func__);
-		goto err;
+	if (wcd9xxx->num_tx_port) {
+		wcd9xxx_dealloc_slim_sh_ch(wcd9xxx->slim,
+					wcd9xxx->num_tx_port,
+					wcd9xxx->tx_chs);
+		wcd9xxx->num_tx_port = 0;
 	}
-err:
-	return ret;
-}
-
-int wcd9xxx_get_channel(struct wcd9xxx *wcd9xxx, unsigned int *rx_ch,
-			unsigned int *tx_ch)
-{
-	int ch_idx = 0;
-	struct wcd9xxx_slim_sch_rx *rx = sh_ch.rx;
-	struct wcd9xxx_slim_sch_tx *tx = sh_ch.tx;
-
-	for (ch_idx = 0; ch_idx < sh_ch.number_of_rx_slave_dev_ports; ch_idx++)
-		rx_ch[ch_idx] = rx[ch_idx].ch_num;
-	for (ch_idx = 0; ch_idx < sh_ch.number_of_tx_slave_dev_ports; ch_idx++)
-		tx_ch[ch_idx] = tx[ch_idx].ch_num;
 	return 0;
 }
 
-static int wcd9xxx_alloc_slim_sh_ch_rx(struct wcd9xxx *wcd9xxx,
-				       u8 wcd9xxx_pgd_la)
+
+static int wcd9xxx_alloc_slim_sh_ch(struct wcd9xxx *wcd9xxx,
+				    u8 wcd9xxx_pgd_la, u32 cnt,
+				    struct wcd9xxx_ch *channels, u32 path)
 {
 	int ret = 0;
-	u8 ch_idx ;
-	u16 slave_port_id = 0;
-	struct wcd9xxx_slim_sch_rx *rx = sh_ch.rx;
+	u32 ch_idx ;
 
-	/*
-	 * DSP requires channel number to be between 128 and 255.
+	/* The slimbus channel allocation seem take longer time
+	 * so do the allocation up front to avoid delay in start of
+	 * playback
 	 */
 	pr_debug("%s: pgd_la[%d]\n", __func__, wcd9xxx_pgd_la);
-	for (ch_idx = 0; ch_idx < sh_ch.number_of_rx_slave_dev_ports;
-	     ch_idx++) {
-		slave_port_id = (ch_idx + sh_ch.rx_port_start_offset);
-		rx[ch_idx].ch_num = slave_port_id + BASE_CH_NUM;
-		ret = slim_get_slaveport(wcd9xxx_pgd_la, slave_port_id,
-					&rx[ch_idx].sph, SLIM_SINK);
+	for (ch_idx = 0; ch_idx < cnt; ch_idx++) {
+		ret = slim_get_slaveport(wcd9xxx_pgd_la,
+					channels[ch_idx].port,
+					&channels[ch_idx].sph, path);
+		pr_debug("%s: pgd_la[%d] channels[%d].port[%d]\n"
+			"channels[%d].sph[%d] path[%d]\n",
+			__func__, wcd9xxx_pgd_la, ch_idx,
+			channels[ch_idx].port,
+			ch_idx, channels[ch_idx].sph, path);
 		if (ret < 0) {
 			pr_err("%s: slave port failure id[%d] ret[%d]\n",
-			       __func__, slave_port_id, ret);
+				__func__, channels[ch_idx].ch_num, ret);
 			goto err;
 		}
 
-		ret = slim_query_ch(wcd9xxx->slim, rx[ch_idx].ch_num,
-				    &rx[ch_idx].ch_h);
+		ret = slim_query_ch(wcd9xxx->slim,
+				    channels[ch_idx].ch_num,
+				    &channels[ch_idx].ch_h);
 		if (ret < 0) {
 			pr_err("%s: slim_query_ch failed ch-num[%d] ret[%d]\n",
-			       __func__, rx[ch_idx].ch_num, ret);
-			goto err;
-		}
-		pr_debug("%s:ch_num=%d ch_h=%d sph=%d la=%d slave_port_id %d\n",
-			 __func__, rx[ch_idx].ch_num, rx[ch_idx].ch_h,
-			 rx[ch_idx].sph, wcd9xxx_pgd_la, slave_port_id);
-	}
-err:
-	return ret;
-}
-
-static int wcd9xxx_alloc_slim_sh_ch_tx(struct wcd9xxx *wcd9xxx,
-				       u8 wcd9xxx_pgd_la)
-{
-	int ret = 0;
-	u8 ch_idx;
-	struct wcd9xxx_slim_sch_tx *tx = sh_ch.tx;
-	u16 slave_port_id = 0;
-
-	pr_debug("%s: pgd_la[%d]\n", __func__, wcd9xxx_pgd_la);
-	/* DSP requires channel number to be between 128 and 255. For RX port
-	 * use channel numbers from 138 to 144, for TX port
-	 * use channel numbers from 128 to 137
-	 */
-	for (ch_idx = 0; ch_idx < sh_ch.number_of_tx_slave_dev_ports;
-	     ch_idx++) {
-		slave_port_id = ch_idx;
-		tx[ch_idx].ch_num = slave_port_id + BASE_CH_NUM;
-		ret = slim_get_slaveport(wcd9xxx_pgd_la, slave_port_id,
-					 &tx[ch_idx].sph, SLIM_SRC);
-		if (ret < 0) {
-			pr_err("%s: slave port failure id[%d] ret[%d]\n",
-			       __func__, slave_port_id, ret);
-			goto err;
-		}
-		ret = slim_query_ch(wcd9xxx->slim, tx[ch_idx].ch_num,
-				    &tx[ch_idx].ch_h);
-		if (ret < 0) {
-			pr_err("%s: slim_query_ch failed ch-num[%d] ret[%d]\n",
-			       __func__, tx[ch_idx].ch_num, ret);
+				__func__, channels[ch_idx].ch_num, ret);
 			goto err;
 		}
 	}
@@ -248,116 +177,46 @@
 	return ret;
 }
 
-static int wcd9xxx_dealloc_slim_sh_ch_rx(struct wcd9xxx *wcd9xxx)
+static int wcd9xxx_dealloc_slim_sh_ch(struct slim_device *slim,
+			u32 cnt, struct wcd9xxx_ch *channels)
 {
 	int idx = 0;
 	int ret = 0;
-	struct wcd9xxx_slim_sch_rx *rx = sh_ch.rx;
 	/* slim_dealloc_ch */
-	for (idx = 0; idx < sh_ch.number_of_rx_slave_dev_ports; idx++) {
-		ret = slim_dealloc_ch(wcd9xxx->slim, rx[idx].ch_h);
+	for (idx = 0; idx < cnt; idx++) {
+		ret = slim_dealloc_ch(slim, channels[idx].ch_h);
 		if (ret < 0) {
 			pr_err("%s: slim_dealloc_ch fail ret[%d] ch_h[%d]\n",
-				__func__, ret, rx[idx].ch_h);
+				__func__, ret, channels[idx].ch_h);
 		}
 	}
-	memset(sh_ch.rx, 0, sizeof(sh_ch.rx));
-	return ret;
-}
-
-static int wcd9xxx_dealloc_slim_sh_ch_tx(struct wcd9xxx *wcd9xxx)
-{
-	int idx = 0;
-	int ret = 0;
-	struct wcd9xxx_slim_sch_tx *tx = sh_ch.tx;
-	/* slim_dealloc_ch */
-	for (idx = 0; idx < sh_ch.number_of_tx_slave_dev_ports; idx++) {
-		ret = slim_dealloc_ch(wcd9xxx->slim, tx[idx].ch_h);
-		if (ret < 0) {
-			pr_err("%s: slim_dealloc_ch fail ret[%d] ch_h[%d]\n",
-				__func__, ret, tx[idx].ch_h);
-		}
-	}
-	memset(sh_ch.tx, 0, sizeof(sh_ch.tx));
 	return ret;
 }
 
 /* Enable slimbus slave device for RX path */
-int wcd9xxx_cfg_slim_sch_rx(struct wcd9xxx *wcd9xxx, unsigned int *ch_num,
-			    unsigned int ch_cnt, unsigned int rate)
+int wcd9xxx_cfg_slim_sch_rx(struct wcd9xxx *wcd9xxx,
+			    struct list_head *wcd9xxx_ch_list,
+			    unsigned int rate, unsigned int bit_width,
+			    u16 *grph)
 {
-	u8 i;
-	u16 grph;
-	u32 sph[SLIM_MAX_RX_PORTS] = {0};
+	u8 ch_cnt = 0;
 	u16 ch_h[SLIM_MAX_RX_PORTS] = {0};
-	u16 slave_port_id;
-	u8  payload_rx = 0, wm_payload = 0;
-	int ret, idx = 0;
-	unsigned short  multi_chan_cfg_reg_addr;
-	struct wcd9xxx_slim_sch_rx *rx = sh_ch.rx;
+	u8  payload = 0;
+	u16 codec_port = 0;
+	int ret;
 	struct slim_ch prop;
+	struct wcd9xxx_ch *rx;
 
 	/* Configure slave interface device */
-	pr_debug("%s: ch_cnt[%d] rate=%d\n", __func__, ch_cnt, rate);
 
-	for (i = 0; i < ch_cnt; i++) {
-		idx = (ch_num[i] - BASE_CH_NUM - sh_ch.rx_port_start_offset);
-		ch_h[i] = rx[idx].ch_h;
-		sph[i] = rx[idx].sph;
-		slave_port_id = idx;
-		pr_debug("%s: idx %d, ch_h %d, sph %d\n",
-			 __func__, idx, ch_h[i], sph[i]);
-		if ((slave_port_id > sh_ch.num_rx_slave_port)) {
-			pr_err("Slimbus: invalid slave port id: %d",
-			       slave_port_id);
-			ret = -EINVAL;
-			goto err;
-		}
-		slave_port_id += sh_ch.rx_port_start_offset;
-		pr_debug("%s: slave_port_id %d\n", __func__, slave_port_id);
-		/* look for the valid port range and chose the
-		 * payload accordingly
-		 */
-		if ((slave_port_id > sh_ch.pgd_tx_port_ch_1_end_port_id) &&
-		    (slave_port_id <= sh_ch.port_ch_0_end_port_id)) {
-			payload_rx = payload_rx |
-				(1 << (slave_port_id -
-				      sh_ch.port_ch_0_start_port_id));
-		} else {
-			ret = -EINVAL;
-			goto err;
-		}
-
-		multi_chan_cfg_reg_addr =
-		    SB_PGD_RX_PORT_MULTI_CHANNEL_0(sh_ch.rx_port_ch_reg_base,
-						   idx);
-		pr_debug("%s: multi_chan_cfg_reg_addr 0x%x\n", __func__,
-			 multi_chan_cfg_reg_addr);
-
-		/* write to interface device */
-		ret = wcd9xxx_interface_reg_write(wcd9xxx,
-						  multi_chan_cfg_reg_addr,
-						  payload_rx);
-		if (ret < 0) {
-			pr_err("%s:Intf-dev fail reg[%d] payload[%d] ret[%d]\n",
-			       __func__, multi_chan_cfg_reg_addr,
-			       payload_rx, ret);
-			goto err;
-		}
-		/* configure the slave port for water mark and enable*/
-		wm_payload = (SLAVE_PORT_WATER_MARK_VALUE <<
-			      SLAVE_PORT_WATER_MARK_SHIFT) + SLAVE_PORT_ENABLE;
-		ret = wcd9xxx_interface_reg_write(
-				wcd9xxx,
-				SB_PGD_PORT_CFG_BYTE_ADDR(
-				    sh_ch.port_rx_cfg_reg_base, idx),
-				wm_payload);
-		if (ret < 0) {
-			pr_err("%s:watermark set failure for port[%d] ret[%d]",
-						__func__, slave_port_id, ret);
-		}
+	list_for_each_entry(rx, wcd9xxx_ch_list, list) {
+		payload |= 1 << rx->shift;
+		ch_h[ch_cnt] = rx->ch_h;
+		ch_cnt++;
+		pr_debug("list ch->ch_h %d ch->sph %d\n", rx->ch_h, rx->sph);
 	}
-
+	pr_debug("%s: ch_cnt[%d] rate=%d WATER_MARK_VAL %d\n",
+		 __func__, ch_cnt, rate, WATER_MARK_VAL);
 	/* slim_define_ch api */
 	prop.prot = SLIM_AUTO_ISO;
 	prop.baser = SLIM_RATE_4000HZ;
@@ -366,130 +225,97 @@
 	prop.ratem = (rate/4000);
 	prop.sampleszbits = 16;
 
-	ret = slim_define_ch(wcd9xxx->slim, &prop, ch_h, ch_cnt, true, &grph);
+	pr_debug("Before slim_define_ch:\n"
+		 "ch_cnt %d,ch_h[0] %d ch_h[1] %d, grph %d\n",
+		 ch_cnt, ch_h[0], ch_h[1], *grph);
+	ret = slim_define_ch(wcd9xxx->slim, &prop, ch_h, ch_cnt,
+			     true, grph);
 	if (ret < 0) {
 		pr_err("%s: slim_define_ch failed ret[%d]\n",
-					__func__, ret);
+		       __func__, ret);
 		goto err;
 	}
-	for (i = 0; i < ch_cnt; i++) {
-		ret = slim_connect_sink(wcd9xxx->slim, &sph[i], 1, ch_h[i]);
+
+	list_for_each_entry(rx, wcd9xxx_ch_list, list) {
+		codec_port = rx->port;
+		pr_debug("%s: codec_port %d rx 0x%x, payload %d\n"
+			 "sh_ch.rx_port_ch_reg_base0 0x%x\n"
+			 "sh_ch.port_rx_cfg_reg_base 0x%x\n",
+			 __func__, codec_port, (u32)rx, payload,
+			 sh_ch.rx_port_ch_reg_base,
+			sh_ch.port_rx_cfg_reg_base);
+
+		/* look for the valid port range and chose the
+		 * payload accordingly
+		 */
+		/* write to interface device */
+		ret = wcd9xxx_interface_reg_write(wcd9xxx,
+				SB_PGD_RX_PORT_MULTI_CHANNEL_0(
+				sh_ch.rx_port_ch_reg_base, codec_port),
+				payload);
+
+		if (ret < 0) {
+			pr_err("%s:Intf-dev fail reg[%d] payload[%d] ret[%d]\n",
+				__func__,
+				SB_PGD_RX_PORT_MULTI_CHANNEL_0(
+				sh_ch.rx_port_ch_reg_base, codec_port),
+				payload, ret);
+			goto err;
+		}
+		/* configure the slave port for water mark and enable*/
+		ret = wcd9xxx_interface_reg_write(wcd9xxx,
+				SB_PGD_PORT_CFG_BYTE_ADDR(
+				sh_ch.port_rx_cfg_reg_base, codec_port),
+				WATER_MARK_VAL);
+		if (ret < 0) {
+			pr_err("%s:watermark set failure for port[%d] ret[%d]",
+				__func__, codec_port, ret);
+		}
+
+		ret = slim_connect_sink(wcd9xxx->slim, &rx->sph, 1, rx->ch_h);
 		if (ret < 0) {
 			pr_err("%s: slim_connect_sink failed ret[%d]\n",
-						__func__, ret);
+				__func__, ret);
 			goto err_close_slim_sch;
 		}
 	}
 	/* slim_control_ch */
-	ret = slim_control_ch(wcd9xxx->slim, grph, SLIM_CH_ACTIVATE, true);
+	ret = slim_control_ch(wcd9xxx->slim, *grph, SLIM_CH_ACTIVATE,
+			      true);
 	if (ret < 0) {
 		pr_err("%s: slim_control_ch failed ret[%d]\n",
-				__func__, ret);
+			__func__, ret);
 		goto err_close_slim_sch;
 	}
-	for (i = 0; i < ch_cnt; i++) {
-		idx = (ch_num[i] - BASE_CH_NUM - sh_ch.rx_port_start_offset);
-		rx[idx].grph = grph;
-	}
 	return 0;
 
 err_close_slim_sch:
 	/*  release all acquired handles */
-	wcd9xxx_close_slim_sch_rx(wcd9xxx, ch_num, ch_cnt);
+	wcd9xxx_close_slim_sch_rx(wcd9xxx, wcd9xxx_ch_list, *grph);
 err:
 	return ret;
 }
 EXPORT_SYMBOL_GPL(wcd9xxx_cfg_slim_sch_rx);
 
 /* Enable slimbus slave device for RX path */
-int wcd9xxx_cfg_slim_sch_tx(struct wcd9xxx *wcd9xxx, unsigned int *ch_num,
-			    unsigned int ch_cnt, unsigned int rate)
+int wcd9xxx_cfg_slim_sch_tx(struct wcd9xxx *wcd9xxx,
+			    struct list_head *wcd9xxx_ch_list,
+			    unsigned int rate, unsigned int bit_width,
+			    u16 *grph)
 {
-	u8 i = 0;
-	u8  payload_tx_0 = 0, payload_tx_1 = 0, wm_payload = 0;
-	u16 grph;
-	u32 sph[SLIM_MAX_TX_PORTS] = {0};
+	u16 ch_cnt = 0;
+	u16 payload = 0;
 	u16 ch_h[SLIM_MAX_TX_PORTS] = {0};
-	u16 idx = 0, slave_port_id;
+	u16 codec_port;
 	int ret = 0;
-	unsigned short multi_chan_cfg_reg_addr;
+	struct wcd9xxx_ch *tx;
 
-	struct wcd9xxx_slim_sch_tx *tx = sh_ch.tx;
 	struct slim_ch prop;
 
-	pr_debug("%s: ch_cnt[%d] rate[%d]\n", __func__, ch_cnt, rate);
-	for (i = 0; i < ch_cnt; i++) {
-		idx = (ch_num[i] - BASE_CH_NUM);
-		ch_h[i] = tx[idx].ch_h;
-		sph[i] = tx[idx].sph;
-		slave_port_id = idx;
-		pr_debug("%s: idx %d, ch_h %d, sph %d, slave_port_id %d\n",
-			 __func__, idx, ch_h[i], sph[i], slave_port_id);
-		if (slave_port_id > sh_ch.number_of_tx_slave_dev_ports) {
-			pr_err("SLIMbus: invalid slave port id: %d",
-			       slave_port_id);
-			ret = -EINVAL;
-			goto err;
-		}
-		/* look for the valid port range and chose the
-		 *  payload accordingly
-		 */
-		if (slave_port_id <=
-		    SB_PGD_TX_PORT_MULTI_CHANNEL_0_END_PORT_ID) {
-			payload_tx_0 = payload_tx_0 | (1 << slave_port_id);
-		} else if (slave_port_id <=
-			   sh_ch.pgd_tx_port_ch_1_end_port_id) {
-			payload_tx_1 = payload_tx_1 |
-			    (1 << (slave_port_id -
-				 SB_PGD_TX_PORT_MULTI_CHANNEL_1_START_PORT_ID));
-		} else {
-			pr_err("%s: slave port id %d error\n", __func__,
-			       slave_port_id);
-			ret = -EINVAL;
-			goto err;
-		}
-		multi_chan_cfg_reg_addr =
-		    SB_PGD_TX_PORT_MULTI_CHANNEL_0(slave_port_id);
-		pr_debug("%s: multi_chan_cfg_reg_addr 0x%x\n", __func__,
-			 multi_chan_cfg_reg_addr);
-		/* write to interface device */
-		ret = wcd9xxx_interface_reg_write(wcd9xxx,
-				multi_chan_cfg_reg_addr,
-				payload_tx_0);
-		if (ret < 0) {
-			pr_err("%s:Intf-dev fail reg[%d] payload[%d] ret[%d]\n",
-			       __func__, multi_chan_cfg_reg_addr, payload_tx_0,
-			       ret);
-			goto err;
-		}
-		multi_chan_cfg_reg_addr =
-		    SB_PGD_TX_PORT_MULTI_CHANNEL_1(slave_port_id);
-		/* ports 8,9 */
-		ret = wcd9xxx_interface_reg_write(wcd9xxx,
-						  multi_chan_cfg_reg_addr,
-						  payload_tx_1);
-		if (ret < 0) {
-			pr_err("%s:Intf-dev fail reg[%d] payload[%d] ret[%d]\n",
-			       __func__, multi_chan_cfg_reg_addr,
-			       payload_tx_1, ret);
-			goto err;
-		}
-		/* configure the slave port for water mark and enable*/
-		wm_payload = (SLAVE_PORT_WATER_MARK_VALUE <<
-			      SLAVE_PORT_WATER_MARK_SHIFT) + SLAVE_PORT_ENABLE;
-		pr_debug("%s: tx_cfg_reg 0x%x wm 0x%x\n", __func__,
-			 SB_PGD_PORT_CFG_BYTE_ADDR(sh_ch.port_tx_cfg_reg_base,
-						   slave_port_id), wm_payload);
-		ret = wcd9xxx_interface_reg_write(
-					wcd9xxx,
-					SB_PGD_PORT_CFG_BYTE_ADDR(
-					    sh_ch.port_tx_cfg_reg_base,
-					    slave_port_id),
-					wm_payload);
-		if (ret < 0) {
-			pr_err("%s: watermark set failure for port[%d] ret[%d]",
-			       __func__, slave_port_id, ret);
-		}
+	list_for_each_entry(tx, wcd9xxx_ch_list, list) {
+		payload |= 1 << tx->shift;
+		ch_h[ch_cnt] = tx->ch_h;
+		ch_cnt++;
 	}
 
 	/* slim_define_ch api */
@@ -499,13 +325,53 @@
 	prop.auxf = SLIM_CH_AUXF_NOT_APPLICABLE;
 	prop.ratem = (rate/4000);
 	prop.sampleszbits = 16;
-	ret = slim_define_ch(wcd9xxx->slim, &prop, ch_h, ch_cnt, true, &grph);
+	ret = slim_define_ch(wcd9xxx->slim, &prop, ch_h, ch_cnt,
+			     true, grph);
 	if (ret < 0) {
-		pr_err("%s: slim_define_ch failed ret[%d]\n", __func__, ret);
+		pr_err("%s: slim_define_ch failed ret[%d]\n",
+		       __func__, ret);
 		goto err;
 	}
-	for (i = 0; i < ch_cnt; i++) {
-		ret = slim_connect_src(wcd9xxx->slim, sph[i], ch_h[i]);
+
+	pr_debug("%s: ch_cnt[%d] rate[%d]\n", __func__, ch_cnt, rate);
+	list_for_each_entry(tx, wcd9xxx_ch_list, list) {
+		codec_port = tx->port;
+		pr_debug("%s: codec_port %d rx 0x%x, payload 0x%x\n",
+			 __func__, codec_port, (u32)tx, payload);
+		/* write to interface device */
+		ret = wcd9xxx_interface_reg_write(wcd9xxx,
+				SB_PGD_TX_PORT_MULTI_CHANNEL_0(codec_port),
+				payload & 0x00FF);
+		if (ret < 0) {
+			pr_err("%s:Intf-dev fail reg[%d] payload[%d] ret[%d]\n",
+				__func__,
+				SB_PGD_TX_PORT_MULTI_CHANNEL_0(codec_port),
+				payload, ret);
+			goto err;
+		}
+		/* ports 8,9 */
+		ret = wcd9xxx_interface_reg_write(wcd9xxx,
+				SB_PGD_TX_PORT_MULTI_CHANNEL_1(codec_port),
+				(payload & 0xFF00)>>8);
+		if (ret < 0) {
+			pr_err("%s:Intf-dev fail reg[%d] payload[%d] ret[%d]\n",
+				__func__,
+				SB_PGD_TX_PORT_MULTI_CHANNEL_1(codec_port),
+				payload, ret);
+			goto err;
+		}
+		/* configure the slave port for water mark and enable*/
+		ret = wcd9xxx_interface_reg_write(wcd9xxx,
+				SB_PGD_PORT_CFG_BYTE_ADDR(
+				sh_ch.port_tx_cfg_reg_base, codec_port),
+				WATER_MARK_VAL);
+		if (ret < 0) {
+			pr_err("%s:watermark set failure for port[%d] ret[%d]",
+				__func__, codec_port, ret);
+		}
+
+		ret = slim_connect_src(wcd9xxx->slim, tx->sph, tx->ch_h);
+
 		if (ret < 0) {
 			pr_err("%s: slim_connect_src failed ret[%d]\n",
 			       __func__, ret);
@@ -513,91 +379,69 @@
 		}
 	}
 	/* slim_control_ch */
-	ret = slim_control_ch(wcd9xxx->slim, grph, SLIM_CH_ACTIVATE, true);
+	ret = slim_control_ch(wcd9xxx->slim, *grph, SLIM_CH_ACTIVATE,
+			      true);
 	if (ret < 0) {
 		pr_err("%s: slim_control_ch failed ret[%d]\n",
-				__func__, ret);
+			__func__, ret);
 		goto err;
 	}
-	for (i = 0; i < ch_cnt; i++) {
-		idx = (ch_num[i] - BASE_CH_NUM);
-		tx[idx].grph = grph;
-	}
 	return 0;
 err:
 	/* release all acquired handles */
-	wcd9xxx_close_slim_sch_tx(wcd9xxx, ch_num, ch_cnt);
+	wcd9xxx_close_slim_sch_tx(wcd9xxx, wcd9xxx_ch_list, *grph);
 	return ret;
 }
 EXPORT_SYMBOL_GPL(wcd9xxx_cfg_slim_sch_tx);
 
-int wcd9xxx_close_slim_sch_rx(struct wcd9xxx *wcd9xxx, unsigned int *ch_num,
-				unsigned int ch_cnt)
+int wcd9xxx_close_slim_sch_rx(struct wcd9xxx *wcd9xxx,
+			      struct list_head *wcd9xxx_ch_list, u16 grph)
 {
-	u16 grph = 0;
-	int i = 0 , idx = 0;
+	u32 sph[SLIM_MAX_RX_PORTS] = {0};
+	int ch_cnt = 0 ;
 	int ret = 0;
-	struct wcd9xxx_slim_sch_rx *rx = sh_ch.rx;
+	struct wcd9xxx_ch *rx;
 
-	pr_debug("%s: ch_cnt[%d]\n", __func__, ch_cnt);
-	for (i = 0; i < ch_cnt; i++) {
-		idx = (ch_num[i] - BASE_CH_NUM - sh_ch.rx_port_start_offset);
-		if (idx < 0) {
-			pr_err("%s: Error:-Invalid index found = %d\n",
-			       __func__, idx);
-			ret = -EINVAL;
-			goto err;
-		}
-		grph = rx[idx].grph;
-		pr_debug("%s: ch_num[%d] %d, idx %d, grph %x\n",
-			 __func__, i, ch_num[i], idx, grph);
-	}
+	list_for_each_entry(rx, wcd9xxx_ch_list, list)
+		sph[ch_cnt++] = rx->sph;
+
+	pr_debug("%s ch_cht %d, sph[0] %d sph[1] %d\n", __func__, ch_cnt,
+		sph[0], sph[1]);
 
 	/* slim_control_ch (REMOVE) */
+	pr_debug("%s before slim_control_ch grph %d\n", __func__, grph);
 	ret = slim_control_ch(wcd9xxx->slim, grph, SLIM_CH_REMOVE, true);
 	if (ret < 0) {
 		pr_err("%s: slim_control_ch failed ret[%d]\n", __func__, ret);
 		goto err;
 	}
-	for (i = 0; i < ch_cnt; i++) {
-		idx = (ch_num[i] - BASE_CH_NUM - sh_ch.rx_port_start_offset);
-		rx[idx].grph = 0;
-	}
 err:
 	return ret;
 }
 EXPORT_SYMBOL_GPL(wcd9xxx_close_slim_sch_rx);
 
-int wcd9xxx_close_slim_sch_tx(struct wcd9xxx *wcd9xxx, unsigned int *ch_num,
-			      unsigned int ch_cnt)
+int wcd9xxx_close_slim_sch_tx(struct wcd9xxx *wcd9xxx,
+			      struct list_head *wcd9xxx_ch_list,
+			      u16 grph)
 {
-	u16 grph = 0;
+	u32 sph[SLIM_MAX_TX_PORTS] = {0};
 	int ret = 0;
-	int i = 0 , idx = 0;
-	struct wcd9xxx_slim_sch_tx *tx = sh_ch.tx;
+	int ch_cnt = 0 ;
+	struct wcd9xxx_ch *tx;
 
-	pr_debug("%s: ch_cnt[%d]\n", __func__, ch_cnt);
-	for (i = 0; i < ch_cnt; i++) {
-		idx = (ch_num[i] - BASE_CH_NUM);
-		if (idx < 0) {
-			pr_err("%s: Error:- Invalid index found = %d\n",
-				__func__, idx);
-			ret = -EINVAL;
-			goto err;
-		}
-		grph = tx[idx].grph;
-	}
+	pr_debug("%s\n", __func__);
+	list_for_each_entry(tx, wcd9xxx_ch_list, list)
+		sph[ch_cnt++] = tx->sph;
+
+	pr_debug("%s ch_cht %d, sph[0] %d sph[1] %d\n",
+		__func__, ch_cnt, sph[0], sph[1]);
 	/* slim_control_ch (REMOVE) */
 	ret = slim_control_ch(wcd9xxx->slim, grph, SLIM_CH_REMOVE, true);
 	if (ret < 0) {
 		pr_err("%s: slim_control_ch failed ret[%d]\n",
-				__func__, ret);
+			__func__, ret);
 		goto err;
 	}
-	for (i = 0; i < ch_cnt; i++) {
-		idx = (ch_num[i] - BASE_CH_NUM);
-		tx[idx].grph = 0;
-	}
 err:
 	return ret;
 }
@@ -607,8 +451,8 @@
 {
 	int ret = 0;
 
-	pr_debug("%s: ch_num[%d]\n", __func__, ch_num);
 	ret = (ch_num - BASE_CH_NUM);
+	pr_debug("%s: ch_num[%d] slave port[%d]\n", __func__, ch_num, ret);
 	if (ret < 0) {
 		pr_err("%s: Error:- Invalid slave port found = %d\n",
 			__func__, ret);
@@ -618,39 +462,16 @@
 }
 EXPORT_SYMBOL_GPL(wcd9xxx_get_slave_port);
 
-int wcd9xxx_disconnect_port(struct wcd9xxx *wcd9xxx, unsigned int *ch_num,
-				unsigned int ch_cnt, unsigned int rx_tx)
+int wcd9xxx_disconnect_port(struct wcd9xxx *wcd9xxx,
+			    struct list_head *wcd9xxx_ch_list, u16 grph)
 {
-	u32 sph[SLIM_MAX_TX_PORTS] = {0};
-	int i = 0 , idx = 0;
+	u32 sph[SLIM_MAX_TX_PORTS + SLIM_MAX_RX_PORTS] = {0};
+	int ch_cnt = 0 ;
 	int ret = 0;
-	struct wcd9xxx_slim_sch_rx *rx = sh_ch.rx;
-	struct wcd9xxx_slim_sch_tx *tx = sh_ch.tx;
+	struct wcd9xxx_ch *slim_ch;
 
-	pr_debug("%s: ch_cnt[%d], rx_tx flag = %d\n", __func__, ch_cnt, rx_tx);
-	for (i = 0; i < ch_cnt; i++) {
-		/* rx_tx will be 1 for rx, 0 for tx */
-		if (rx_tx) {
-			idx = (ch_num[i] - BASE_CH_NUM -
-				sh_ch.rx_port_start_offset);
-			if (idx < 0) {
-				pr_err("%s: Invalid index found for RX = %d\n",
-					__func__, idx);
-				ret = -EINVAL;
-				goto err;
-			}
-			sph[i] = rx[idx].sph;
-		} else {
-			idx = (ch_num[i] - BASE_CH_NUM);
-			if (idx < 0) {
-				pr_err("%s:Invalid index found for TX = %d\n",
-					__func__, idx);
-				ret = -EINVAL;
-				goto err;
-			}
-			sph[i] = tx[idx].sph;
-		}
-	}
+	list_for_each_entry(slim_ch, wcd9xxx_ch_list, list)
+		sph[ch_cnt++] = slim_ch->sph;
 
 	/* slim_disconnect_port */
 	ret = slim_disconnect_ports(wcd9xxx->slim, sph, ch_cnt);
@@ -658,7 +479,6 @@
 		pr_err("%s: slim_disconnect_ports failed ret[%d]\n",
 			__func__, ret);
 	}
-err:
 	return ret;
 }
 EXPORT_SYMBOL_GPL(wcd9xxx_disconnect_port);
diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c
index 6de0a77..740c717 100644
--- a/drivers/net/usb/usbnet.c
+++ b/drivers/net/usb/usbnet.c
@@ -1168,6 +1168,7 @@
 		usb_anchor_urb(urb, &dev->deferred);
 		/* no use to process more packets */
 		netif_stop_queue(net);
+		usb_put_urb(urb);
 		spin_unlock_irqrestore(&dev->txq.lock, flags);
 		netdev_dbg(dev->net, "Delaying transmission for resumption\n");
 		goto deferred;
@@ -1317,6 +1318,8 @@
 
 	cancel_work_sync(&dev->kevent);
 
+	usb_scuttle_anchored_urbs(&dev->deferred);
+
 	if (dev->driver_info->unbind)
 		dev->driver_info->unbind (dev, intf);
 
diff --git a/drivers/platform/msm/qpnp-pwm.c b/drivers/platform/msm/qpnp-pwm.c
index ebe3ad06..a938a45 100644
--- a/drivers/platform/msm/qpnp-pwm.c
+++ b/drivers/platform/msm/qpnp-pwm.c
@@ -107,8 +107,10 @@
 /* LPG Control for RAMP_CONTROL */
 #define QPNP_RAMP_START_MASK			0x01
 
-#define QPNP_ENABLE_LUT(value) (value |= QPNP_RAMP_START_MASK)
-#define QPNP_DISABLE_LUT(value) (value &= ~QPNP_RAMP_START_MASK)
+#define QPNP_ENABLE_LUT_V0(value) (value |= QPNP_RAMP_START_MASK)
+#define QPNP_DISABLE_LUT_V0(value) (value &= ~QPNP_RAMP_START_MASK)
+#define QPNP_ENABLE_LUT_V1(value, id) (value |= BIT(id))
+#define QPNP_DISABLE_LUT_V1(value, id) (value &= ~BIT(id))
 
 /* LPG Control for RAMP_STEP_DURATION_LSB */
 #define QPNP_RAMP_STEP_DURATION_LSB_MASK	0xFF
@@ -154,10 +156,18 @@
 #define PRE_DIVIDE_5		5
 #define PRE_DIVIDE_6		6
 
-#define SPMI_LPG_REG_ADDR_BASE	0x40
-#define SPMI_LPG_REG_ADDR(b, n)	(b + SPMI_LPG_REG_ADDR_BASE + (n))
+#define SPMI_LPG_REG_BASE_OFFSET	0x40
+#define SPMI_LPG_REVISION2_OFFSET	0x1
+#define SPMI_LPG_REV1_RAMP_CONTROL_OFFSET	0x86
+#define SPMI_LPG_REG_ADDR(b, n)	(b + SPMI_LPG_REG_BASE_OFFSET + (n))
 #define SPMI_MAX_BUF_LEN	8
 
+/* LPG revisions */
+enum qpnp_lpg_revision {
+	QPNP_LPG_REVISION_0 = 0x0,
+	QPNP_LPG_REVISION_1 = 0x1,
+};
+
 /* SPMI LPG registers */
 enum qpnp_lpg_registers_list {
 	QPNP_LPG_PATTERN_CONFIG,
@@ -254,6 +264,7 @@
 	struct	mutex		lpg_mutex;
 	struct	qpnp_lpg_config	lpg_config;
 	u8	qpnp_lpg_registers[QPNP_TOTAL_LPG_SPMI_REGISTERS];
+	enum qpnp_lpg_revision	revision;
 };
 
 /* Internal functions */
@@ -812,34 +823,68 @@
 {
 	struct qpnp_lpg_config	*lpg_config = &pwm->chip->lpg_config;
 	struct qpnp_lpg_chip	*chip = pwm->chip;
-	u8			value, mask;
+	u8			value, mask, *reg;
+	u16			addr;
 
 	value = pwm->chip->qpnp_lpg_registers[QPNP_RAMP_CONTROL];
+	reg = &pwm->chip->qpnp_lpg_registers[QPNP_RAMP_CONTROL];
 
-	QPNP_ENABLE_LUT(value);
+	switch (chip->revision) {
+	case QPNP_LPG_REVISION_0:
+		QPNP_ENABLE_LUT_V0(value);
+		mask = QPNP_RAMP_START_MASK;
+		addr = SPMI_LPG_REG_ADDR(lpg_config->base_addr,
+					QPNP_RAMP_CONTROL);
+		break;
+	case QPNP_LPG_REVISION_1:
+		QPNP_ENABLE_LUT_V1(value, pwm->pwm_config.channel_id);
+		mask = BIT(pwm->pwm_config.channel_id);
+		addr = lpg_config->lut_base_addr +
+				SPMI_LPG_REV1_RAMP_CONTROL_OFFSET;
+	default:
+		pr_err("Invalid LPG revision\n");
+		return -EINVAL;
+	}
 
-	mask = QPNP_RAMP_START_MASK;
+	qpnp_lpg_save(reg, mask, value);
 
-	return qpnp_lpg_save_and_write(value, mask,
-		&pwm->chip->qpnp_lpg_registers[QPNP_RAMP_CONTROL],
-		lpg_config->base_addr, QPNP_RAMP_CONTROL, 1, chip);
+	return spmi_ext_register_writel(chip->spmi_dev->ctrl,
+			chip->spmi_dev->sid, addr, reg, 1);
 }
 
-static int qpnp_disable_lut(struct pwm_device *pwm)
+static int qpnp_lpg_disable_lut(struct pwm_device *pwm)
 {
 	struct qpnp_lpg_config	*lpg_config = &pwm->chip->lpg_config;
 	struct qpnp_lpg_chip	*chip = pwm->chip;
-	u8			value, mask;
+	u8			value, mask, *reg;
+	u16			addr;
 
 	value = pwm->chip->qpnp_lpg_registers[QPNP_RAMP_CONTROL];
+	reg = &pwm->chip->qpnp_lpg_registers[QPNP_RAMP_CONTROL];
 
-	QPNP_DISABLE_LUT(value);
+	switch (chip->revision) {
+	case QPNP_LPG_REVISION_0:
+		QPNP_DISABLE_LUT_V0(value);
+		mask = QPNP_RAMP_START_MASK;
+		addr = SPMI_LPG_REG_ADDR(lpg_config->base_addr,
+					QPNP_RAMP_CONTROL);
+		break;
+	case QPNP_LPG_REVISION_1:
+		QPNP_DISABLE_LUT_V1(value, pwm->pwm_config.channel_id);
+		mask = BIT(pwm->pwm_config.channel_id);
+		addr = lpg_config->lut_base_addr +
+			SPMI_LPG_REV1_RAMP_CONTROL_OFFSET;
+		break;
+	default:
+		pr_err("Invalid LPG revision\n");
+		return -EINVAL;
+		break;
+	}
 
-	mask = QPNP_RAMP_START_MASK;
+	qpnp_lpg_save(reg, mask, value);
 
-	return qpnp_lpg_save_and_write(value, mask,
-		&pwm->chip->qpnp_lpg_registers[QPNP_RAMP_CONTROL],
-		lpg_config->base_addr, QPNP_RAMP_CONTROL, 1, chip);
+	return spmi_ext_register_writel(chip->spmi_dev->ctrl,
+			chip->spmi_dev->sid, addr, reg, 1);
 }
 
 static int qpnp_lpg_enable_pwm(struct pwm_device *pwm)
@@ -847,6 +892,7 @@
 	struct qpnp_lpg_config	*lpg_config = &pwm->chip->lpg_config;
 	struct qpnp_lpg_chip	*chip = pwm->chip;
 	u8			value, mask;
+	int			rc;
 
 	value = pwm->chip->qpnp_lpg_registers[QPNP_ENABLE_CONTROL];
 
@@ -854,12 +900,23 @@
 
 	mask = QPNP_EN_PWM_OUTPUT_MASK;
 
-	return qpnp_lpg_save_and_write(value, mask,
+	rc = qpnp_lpg_save_and_write(value, mask,
 		&pwm->chip->qpnp_lpg_registers[QPNP_ENABLE_CONTROL],
 		lpg_config->base_addr, QPNP_ENABLE_CONTROL, 1, chip);
+	if (rc)
+		goto out;
+
+	/*
+	 * Due to LPG hardware bug, in the PWM mode, having enabled PWM,
+	 * We have to write PWM values one more time.
+	 */
+	return qpnp_lpg_save_pwm_value(pwm);
+
+out:
+	return rc;
 }
 
-static int qpnp_disable_pwm(struct pwm_device *pwm)
+static int qpnp_lpg_disable_pwm(struct pwm_device *pwm)
 {
 	struct qpnp_lpg_config	*lpg_config = &pwm->chip->lpg_config;
 	struct qpnp_lpg_chip	*chip = pwm->chip;
@@ -1072,8 +1129,8 @@
 	pwm_config = &pwm->pwm_config;
 
 	if (pwm_config->in_use) {
-		qpnp_disable_pwm(pwm);
-		qpnp_disable_lut(pwm);
+		qpnp_lpg_disable_pwm(pwm);
+		qpnp_lpg_disable_lut(pwm);
 		pwm_config->in_use = 0;
 		pwm_config->lable = NULL;
 	}
@@ -1157,9 +1214,9 @@
 	if (pwm_config->in_use) {
 		if (QPNP_IS_PWM_CONFIG_SELECTED(
 			chip->qpnp_lpg_registers[QPNP_ENABLE_CONTROL]))
-			qpnp_disable_pwm(pwm);
+			qpnp_lpg_disable_pwm(pwm);
 		else
-			qpnp_disable_lut(pwm);
+			qpnp_lpg_disable_lut(pwm);
 	}
 
 	mutex_unlock(&pwm->chip->lpg_mutex);
@@ -1596,6 +1653,19 @@
 
 	id = chip->pwm_dev.pwm_config.channel_id;
 
+	spmi_ext_register_readl(chip->spmi_dev->ctrl,
+		chip->spmi_dev->sid,
+		chip->lpg_config.base_addr + SPMI_LPG_REVISION2_OFFSET,
+		(u8 *) &chip->revision, 1);
+
+	if (chip->revision < QPNP_LPG_REVISION_0 ||
+		chip->revision > QPNP_LPG_REVISION_1) {
+		pr_err("Unknown LPG revision detected, rev:%d\n",
+						chip->revision);
+		rc = -EINVAL;
+		goto failed_insert;
+	}
+
 	rc = radix_tree_insert(&lpg_dev_tree, id, chip);
 
 	if (rc) {
diff --git a/drivers/power/power_supply_core.c b/drivers/power/power_supply_core.c
index 352e60e..24918ca 100644
--- a/drivers/power/power_supply_core.c
+++ b/drivers/power/power_supply_core.c
@@ -51,6 +51,23 @@
  * @psy:	the power supply to control
  * @enable:	sets online property of power supply
  */
+int power_supply_set_present(struct power_supply *psy, bool enable)
+{
+	const union power_supply_propval ret = {enable,};
+
+	if (psy->set_property)
+		return psy->set_property(psy, POWER_SUPPLY_PROP_PRESENT,
+								&ret);
+
+	return -ENXIO;
+}
+EXPORT_SYMBOL_GPL(power_supply_set_present);
+
+/**
+ * power_supply_set_online - set online state of the power supply
+ * @psy:	the power supply to control
+ * @enable:	sets online property of power supply
+ */
 int power_supply_set_online(struct power_supply *psy, bool enable)
 {
 	const union power_supply_propval ret = {enable,};
diff --git a/drivers/power/qpnp-charger.c b/drivers/power/qpnp-charger.c
index dda8976..0185a3e 100644
--- a/drivers/power/qpnp-charger.c
+++ b/drivers/power/qpnp-charger.c
@@ -20,6 +20,7 @@
 #include <linux/of_device.h>
 #include <linux/radix-tree.h>
 #include <linux/interrupt.h>
+#include <linux/delay.h>
 #include <linux/qpnp/qpnp-adc.h>
 #include <linux/power_supply.h>
 #include <linux/bitops.h>
@@ -69,14 +70,17 @@
 #define CHGR_CHG_WDOG_PET			0x64
 #define CHGR_CHG_WDOG_EN			0x65
 #define CHGR_USB_IUSB_MAX			0x44
+#define CHGR_USB_USB_SUSP			0x47
 #define CHGR_USB_ENUM_T_STOP			0x4E
 #define CHGR_CHG_TEMP_THRESH			0x66
 #define CHGR_BAT_IF_PRES_STATUS			0x08
-#define CHGR_BAT_TEMP_STATUS			0x09
+#define CHGR_STATUS				0x09
 #define CHGR_BAT_IF_VCP				0x42
 #define CHGR_BAT_IF_BATFET_CTRL1		0x90
 #define CHGR_MISC_BOOT_DONE			0x42
+#define CHGR_BUCK_COMPARATOR_OVRIDE_3		0xED
 #define MISC_REVISION2				0x01
+#define SEC_ACCESS				0xD0
 
 /* SMBB peripheral subtype values */
 #define REG_OFFSET_PERP_SUBTYPE			0x05
@@ -133,6 +137,9 @@
 /* smbb_misc_interrupts */
 #define TFTWDOG_IRQ			BIT(0)
 
+/* Workaround flags */
+#define CHG_FLAGS_VCP_WA		BIT(0)
+
 /**
  * struct qpnp_chg_chip - device information
  * @dev:			device pointer to access the parent
@@ -156,6 +163,8 @@
  * @usb_psy			power supply to export information to userspace
  * @bms_psy			power supply to export information to userspace
  * @batt_psy:			power supply to export information to userspace
+ * @flags:			flags to activate specific workarounds
+ *				throughout the driver
  *
  */
 struct qpnp_chg_chip {
@@ -185,6 +194,7 @@
 	struct power_supply		*usb_psy;
 	struct power_supply		*bms_psy;
 	struct power_supply		batt_psy;
+	uint32_t			flags;
 };
 
 static struct qpnp_chg_chip *the_chip;
@@ -258,6 +268,7 @@
 	return 0;
 }
 
+#define USB_VALID_BIT	BIT(7)
 static int
 qpnp_chg_is_usb_chg_plugged_in(struct qpnp_chg_chip *chip)
 {
@@ -265,16 +276,16 @@
 	int rc;
 
 	rc = qpnp_chg_read(chip, &usbin_valid_rt_sts,
-				 INT_RT_STS(chip->usb_chgpth_base), 1);
+				 chip->usb_chgpth_base + CHGR_STATUS , 1);
 
 	if (rc) {
 		pr_err("spmi read failed: addr=%03X, rc=%d\n",
-				INT_RT_STS(chip->usb_chgpth_base), rc);
+				chip->usb_chgpth_base + CHGR_STATUS, rc);
 		return rc;
 	}
 	pr_debug("chgr usb sts 0x%x\n", usbin_valid_rt_sts);
 
-	return (usbin_valid_rt_sts & USBIN_VALID_IRQ) ? 1 : 0;
+	return (usbin_valid_rt_sts & USB_VALID_BIT) ? 1 : 0;
 }
 
 static int
@@ -294,7 +305,6 @@
 	return (dcin_valid_rt_sts & DCIN_VALID_IRQ) ? 1 : 0;
 }
 
-#define VCP_IUSBMAX_SETTING_MA			2000
 #define QPNP_CHG_IUSB_MAX_MIN_100		100
 #define QPNP_CHG_IUSB_MAX_MIN_150		150
 #define QPNP_CHG_IUSB_MAX_MIN_MA		200
@@ -303,7 +313,8 @@
 static int
 qpnp_chg_iusbmax_set(struct qpnp_chg_chip *chip, int mA)
 {
-	u8 usb_reg = 0;
+	int rc = 0;
+	u8 usb_reg = 0, temp = 8;
 
 	if (mA == QPNP_CHG_IUSB_MAX_MIN_100) {
 		usb_reg = 0x00;
@@ -323,17 +334,42 @@
 		return -EINVAL;
 	}
 
-	/* Hack for VCP issue make sure IUSBMAX setting
-	 * is at least 2 A to not brown out device */
-	mA = VCP_IUSBMAX_SETTING_MA;
-
 	usb_reg = mA / QPNP_CHG_IUSB_MAX_STEP_MA;
 
+	if (chip->flags & CHG_FLAGS_VCP_WA) {
+		temp = 0xA5;
+		rc =  qpnp_chg_write(chip, &temp,
+			chip->buck_base + SEC_ACCESS, 1);
+		rc =  qpnp_chg_masked_write(chip,
+			chip->buck_base + CHGR_BUCK_COMPARATOR_OVRIDE_3,
+			0x0C, 0x0C, 1);
+	}
+
 	pr_debug("current=%d setting 0x%x\n", mA, usb_reg);
-	return qpnp_chg_write(chip, &usb_reg,
+	rc = qpnp_chg_write(chip, &usb_reg,
 		chip->usb_chgpth_base + CHGR_USB_IUSB_MAX, 1);
-	pr_debug("done\n");
-	return 0;
+
+	if (chip->flags & CHG_FLAGS_VCP_WA) {
+		temp = 0xA5;
+		udelay(200);
+		rc =  qpnp_chg_write(chip, &temp,
+			chip->buck_base + SEC_ACCESS, 1);
+		rc =  qpnp_chg_masked_write(chip,
+			chip->buck_base + CHGR_BUCK_COMPARATOR_OVRIDE_3,
+			0x0C, 0x00, 1);
+	}
+
+	return rc;
+}
+
+#define USB_SUSPEND_BIT	BIT(0)
+static int
+qpnp_chg_usb_suspend_enable(struct qpnp_chg_chip *chip, int enable)
+{
+	return qpnp_chg_masked_write(chip,
+			chip->usb_chgpth_base + CHGR_USB_USB_SUSP,
+			USB_SUSPEND_BIT,
+			enable ? USB_SUSPEND_BIT : 0, 1);
 }
 
 #define ENUM_T_STOP_BIT		BIT(0)
@@ -350,11 +386,6 @@
 		chip->usb_present = usb_present;
 		power_supply_set_present(chip->usb_psy,
 			chip->usb_present);
-	} else if (!(chip->usb_present && usb_present)) {
-			qpnp_chg_masked_write(chip,
-				chip->usb_chgpth_base + CHGR_USB_ENUM_T_STOP,
-				ENUM_T_STOP_BIT,
-				ENUM_T_STOP_BIT, 1);
 	}
 
 	return IRQ_HANDLED;
@@ -368,7 +399,7 @@
 	int rc, usb_present;
 
 	rc = qpnp_chg_masked_write(chip,
-		chip->usb_chgpth_base + CHGR_CHG_FAILED,
+		chip->chgr_base + CHGR_CHG_FAILED,
 		CHGR_CHG_FAILED_BIT,
 		CHGR_CHG_FAILED_BIT, 1);
 	if (rc)
@@ -488,7 +519,7 @@
 	int rc;
 
 	rc = qpnp_chg_read(chip, &batt_health,
-				chip->bat_if_base + CHGR_BAT_TEMP_STATUS, 1);
+				chip->bat_if_base + CHGR_STATUS, 1);
 	if (rc) {
 		pr_err("Couldn't read battery health read failed rc=%d\n", rc);
 		return POWER_SUPPLY_HEALTH_UNKNOWN;
@@ -647,8 +678,13 @@
 		chip->usb_psy->get_property(chip->usb_psy,
 			  POWER_SUPPLY_PROP_CURRENT_MAX, &ret);
 		qpnp_chg_iusbmax_set(chip, ret.intval / 1000);
+		if ((ret.intval / 1000) <= QPNP_CHG_IUSB_MAX_MIN_MA)
+			qpnp_chg_usb_suspend_enable(chip, 1);
+		else
+			qpnp_chg_usb_suspend_enable(chip, 0);
 	} else {
-		qpnp_chg_iusbmax_set(chip, QPNP_CHG_IUSB_MAX_MIN_MA);
+		qpnp_chg_iusbmax_set(chip, QPNP_CHG_IUSB_MAX_MIN_100);
+		qpnp_chg_usb_suspend_enable(chip, 0);
 	}
 
 	pr_debug("end of power supply changed\n");
@@ -863,6 +899,14 @@
 		chip->chgr_base + CHGR_VDD_MAX, 1);
 }
 
+
+static void
+qpnp_chg_setup_flags(struct qpnp_chg_chip *chip)
+{
+	if (chip->revision > 0)
+		chip->flags |= CHG_FLAGS_VCP_WA;
+}
+
 #define WDOG_EN_BIT	BIT(7)
 static int
 qpnp_chg_hwinit(struct qpnp_chg_chip *chip, u8 subtype,
@@ -944,7 +988,7 @@
 	case SMBB_BAT_IF_SUBTYPE:
 		/* HACK: Unlock secure access to override temp comparator */
 		rc = qpnp_chg_masked_write(chip,
-				chip->bat_if_base + 0xD0,
+				chip->bat_if_base + SEC_ACCESS,
 				0xA5, 0xA5, 1);
 		pr_debug("override hot cold\n");
 		rc = qpnp_chg_masked_write(chip,
@@ -980,6 +1024,12 @@
 				return -ENXIO;
 			}
 		}
+
+		rc = qpnp_chg_masked_write(chip,
+			chip->usb_chgpth_base + CHGR_USB_ENUM_T_STOP,
+			ENUM_T_STOP_BIT,
+			ENUM_T_STOP_BIT, 1);
+
 		break;
 	case SMBB_DC_CHGPTH_SUBTYPE:
 		break;
@@ -1199,6 +1249,9 @@
 		goto unregister_dc;
 	}
 
+	/* Turn on appropriate workaround flags */
+	qpnp_chg_setup_flags(chip);
+
 	power_supply_set_present(chip->usb_psy,
 			qpnp_chg_is_usb_chg_plugged_in(chip));
 
diff --git a/drivers/usb/misc/ks_bridge.c b/drivers/usb/misc/ks_bridge.c
index 61e6891..410b5c4 100644
--- a/drivers/usb/misc/ks_bridge.c
+++ b/drivers/usb/misc/ks_bridge.c
@@ -287,6 +287,9 @@
 	unsigned long		flags;
 	struct ks_bridge	*ksb = fp->private_data;
 
+	if (!test_bit(USB_DEV_CONNECTED, &ksb->flags))
+		return -ENODEV;
+
 	pkt = ksb_alloc_data_pkt(count, GFP_KERNEL, ksb);
 	if (IS_ERR(pkt)) {
 		pr_err("unable to allocate data packet");
@@ -570,6 +573,8 @@
 	struct usb_endpoint_descriptor	*ep_desc;
 	int				i;
 	struct ks_bridge		*ksb;
+	unsigned long			flags;
+	struct data_pkt			*pkt;
 
 	ifc_num = ifc->cur_altsetting->desc.bInterfaceNumber;
 
@@ -625,6 +630,23 @@
 
 	dbg_log_event(ksb, "PID-ATT", id->idProduct, 0);
 
+	/*free up stale buffers if any from previous disconnect*/
+	spin_lock_irqsave(&ksb->lock, flags);
+	while (!list_empty(&ksb->to_ks_list)) {
+		pkt = list_first_entry(&ksb->to_ks_list,
+				struct data_pkt, list);
+		list_del_init(&pkt->list);
+		ksb_free_data_pkt(pkt);
+		ksb->alloced_read_pkts--;
+	}
+	while (!list_empty(&ksb->to_mdm_list)) {
+		pkt = list_first_entry(&ksb->to_mdm_list,
+				struct data_pkt, list);
+		list_del_init(&pkt->list);
+		ksb_free_data_pkt(pkt);
+	}
+	spin_unlock_irqrestore(&ksb->lock, flags);
+
 	ksb->fs_dev = (struct miscdevice *)id->driver_info;
 	misc_register(ksb->fs_dev);
 
@@ -674,6 +696,8 @@
 	cancel_work_sync(&ksb->to_mdm_work);
 	cancel_work_sync(&ksb->start_rx_work);
 
+	misc_deregister(ksb->fs_dev);
+
 	usb_kill_anchored_urbs(&ksb->submitted);
 
 	wait_event_interruptible_timeout(
@@ -688,6 +712,7 @@
 				struct data_pkt, list);
 		list_del_init(&pkt->list);
 		ksb_free_data_pkt(pkt);
+		ksb->alloced_read_pkts--;
 	}
 	while (!list_empty(&ksb->to_mdm_list)) {
 		pkt = list_first_entry(&ksb->to_mdm_list,
@@ -697,7 +722,6 @@
 	}
 	spin_unlock_irqrestore(&ksb->lock, flags);
 
-	misc_deregister(ksb->fs_dev);
 	ifc->needs_remote_wakeup = 0;
 	usb_put_dev(ksb->udev);
 	ksb->ifc = NULL;
diff --git a/drivers/usb/otg/msm_otg.c b/drivers/usb/otg/msm_otg.c
index c43f6b6..26f1b84 100644
--- a/drivers/usb/otg/msm_otg.c
+++ b/drivers/usb/otg/msm_otg.c
@@ -39,7 +39,6 @@
 #include <linux/regulator/consumer.h>
 #include <linux/mfd/pm8xxx/pm8921-charger.h>
 #include <linux/mfd/pm8xxx/misc.h>
-#include <linux/power_supply.h>
 #include <linux/mhl_8334.h>
 
 #include <mach/scm.h>
@@ -97,6 +96,7 @@
 static struct power_supply *psy;
 
 static bool aca_id_turned_on;
+static bool legacy_power_supply;
 static inline bool aca_enabled(void)
 {
 #ifdef CONFIG_USB_MSM_ACA
@@ -1116,24 +1116,32 @@
 }
 #endif
 
-static int msm_otg_notify_host_mode(struct msm_otg *motg, bool host_mode)
+static void msm_otg_notify_host_mode(struct msm_otg *motg, bool host_mode)
 {
-	if (!psy)
-		goto psy_not_supported;
+	struct power_supply *usb = psy ? psy : &motg->usb_psy;
 
-	if (host_mode)
-		power_supply_set_scope(psy, POWER_SUPPLY_SCOPE_SYSTEM);
-	else
-		power_supply_set_scope(psy, POWER_SUPPLY_SCOPE_DEVICE);
+	if (!usb) {
+		pr_err("No USB power supply registered!\n");
+		return;
+	}
 
-psy_not_supported:
-	dev_dbg(motg->phy.dev, "Power Supply doesn't support USB charger\n");
-	return -ENXIO;
+	if (psy) {
+		/* legacy support */
+		if (host_mode)
+			power_supply_set_scope(psy, POWER_SUPPLY_SCOPE_SYSTEM);
+		else
+			power_supply_set_scope(psy, POWER_SUPPLY_SCOPE_DEVICE);
+		return;
+	} else {
+		motg->host_mode = host_mode;
+		power_supply_changed(usb);
+	}
 }
 
 static int msm_otg_notify_chg_type(struct msm_otg *motg)
 {
 	static int charger_type;
+	struct power_supply *usb = psy ? psy : &motg->usb_psy;
 
 	/*
 	 * TODO
@@ -1157,40 +1165,44 @@
 	else
 		charger_type = POWER_SUPPLY_TYPE_BATTERY;
 
-	if (!psy) {
+	if (!usb) {
 		pr_err("No USB power supply registered!\n");
 		return -EINVAL;
 	}
 
 	pr_debug("setting usb power supply type %d\n", charger_type);
-	power_supply_set_supply_type(psy, charger_type);
+	power_supply_set_supply_type(usb, charger_type);
 	return 0;
 }
 
 static int msm_otg_notify_power_supply(struct msm_otg *motg, unsigned mA)
 {
+	struct power_supply *usb = psy ? psy : &motg->usb_psy;
 
-	if (!psy)
-		goto psy_not_supported;
-
-	if (motg->cur_power == 0 && mA > 0) {
-		/* Enable charging */
-		if (power_supply_set_online(psy, true))
-			goto psy_not_supported;
-	} else if (motg->cur_power > 0 && mA == 0) {
-		/* Disable charging */
-		if (power_supply_set_online(psy, false))
-			goto psy_not_supported;
-		return 0;
+	if (!usb) {
+		dev_dbg(motg->phy.dev, "no usb power supply registered\n");
+		goto psy_error;
 	}
-	/* Set max current limit */
-	if (power_supply_set_current_limit(psy, 1000*mA))
-		goto psy_not_supported;
 
+	if (motg->cur_power == 0 && mA > 2) {
+		/* Enable charging */
+		if (power_supply_set_online(usb, true))
+			goto psy_error;
+		if (power_supply_set_current_limit(usb, 1000*mA))
+			goto psy_error;
+	} else if (motg->cur_power > 0 && (mA == 0 || mA == 2)) {
+		/* Disable charging */
+		if (power_supply_set_online(usb, false))
+			goto psy_error;
+		/* Set max current limit */
+		if (power_supply_set_current_limit(usb, 0))
+			goto psy_error;
+	}
+	power_supply_changed(usb);
 	return 0;
 
-psy_not_supported:
-	dev_dbg(motg->phy.dev, "Power Supply doesn't support USB charger\n");
+psy_error:
+	dev_dbg(motg->phy.dev, "power supply error when setting property\n");
 	return -ENXIO;
 }
 
@@ -2321,9 +2333,13 @@
 	case OTG_STATE_UNDEFINED:
 		msm_otg_reset(otg->phy);
 		msm_otg_init_sm(motg);
-		psy = power_supply_get_by_name("usb");
-		if (!psy)
-			pr_err("couldn't get usb power supply\n");
+		if (!psy && legacy_power_supply) {
+			psy = power_supply_get_by_name("usb");
+
+			if (!psy)
+				pr_err("couldn't get usb power supply\n");
+		}
+
 		otg->phy->state = OTG_STATE_B_IDLE;
 		if (!test_bit(B_SESS_VLD, &motg->inputs) &&
 				test_bit(ID, &motg->inputs)) {
@@ -3364,6 +3380,71 @@
 	return count;
 }
 
+static int otg_power_get_property_usb(struct power_supply *psy,
+				  enum power_supply_property psp,
+				  union power_supply_propval *val)
+{
+	struct msm_otg *motg = container_of(psy, struct msm_otg,
+								usb_psy);
+	switch (psp) {
+	case POWER_SUPPLY_PROP_SCOPE:
+		if (motg->host_mode)
+			val->intval = POWER_SUPPLY_SCOPE_SYSTEM;
+		else
+			val->intval = POWER_SUPPLY_SCOPE_DEVICE;
+		break;
+	case POWER_SUPPLY_PROP_CURRENT_MAX:
+			val->intval = motg->current_max;
+		break;
+	/* Reflect USB enumeration */
+	case POWER_SUPPLY_PROP_PRESENT:
+	case POWER_SUPPLY_PROP_ONLINE:
+		val->intval = motg->online;
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int otg_power_set_property_usb(struct power_supply *psy,
+				  enum power_supply_property psp,
+				  const union power_supply_propval *val)
+{
+	struct msm_otg *motg = container_of(psy, struct msm_otg,
+								usb_psy);
+
+	switch (psp) {
+	/* Process PMIC notification in PRESENT prop */
+	case POWER_SUPPLY_PROP_PRESENT:
+		msm_otg_set_vbus_state(val->intval);
+		break;
+	/* The ONLINE property reflects if usb has enumerated */
+	case POWER_SUPPLY_PROP_ONLINE:
+		motg->online = val->intval;
+		break;
+	case POWER_SUPPLY_PROP_CURRENT_MAX:
+		motg->current_max = val->intval;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	power_supply_changed(&motg->usb_psy);
+	return 0;
+}
+
+static char *otg_pm_power_supplied_to[] = {
+	"battery",
+};
+
+static enum power_supply_property otg_pm_power_props_usb[] = {
+	POWER_SUPPLY_PROP_PRESENT,
+	POWER_SUPPLY_PROP_ONLINE,
+	POWER_SUPPLY_PROP_CURRENT_MAX,
+	POWER_SUPPLY_PROP_SCOPE,
+};
+
 const struct file_operations msm_otg_bus_fops = {
 	.open = msm_otg_bus_open,
 	.read = seq_read,
@@ -3550,6 +3631,23 @@
 	return retval;
 }
 
+static int msm_otg_register_power_supply(struct platform_device *pdev,
+					struct msm_otg *motg)
+{
+	int ret;
+
+	ret = power_supply_register(&pdev->dev, &motg->usb_psy);
+	if (ret < 0) {
+		dev_err(motg->phy.dev,
+			"%s:power_supply_register usb failed\n",
+			__func__);
+		return ret;
+	}
+
+	legacy_power_supply = false;
+	return 0;
+}
+
 struct msm_otg_platform_data *msm_otg_dt_to_pdata(struct platform_device *pdev)
 {
 	struct device_node *node = pdev->dev.of_node;
@@ -3873,9 +3971,6 @@
 		dev_dbg(&pdev->dev, "mode debugfs file is"
 			"not available\n");
 
-	if (motg->pdata->otg_control == OTG_PMIC_CONTROL)
-		pm8921_charger_register_vbus_sn(&msm_otg_set_vbus_state);
-
 	if (motg->pdata->phy_type == SNPS_28NM_INTEGRATED_PHY) {
 		if (motg->pdata->otg_control == OTG_PMIC_CONTROL &&
 			(!(motg->pdata->mode == USB_OTG) ||
@@ -3905,6 +4000,32 @@
 			debug_bus_voting_enabled = true;
 	}
 
+	motg->usb_psy.name = "usb";
+	motg->usb_psy.type = POWER_SUPPLY_TYPE_USB;
+	motg->usb_psy.supplied_to = otg_pm_power_supplied_to;
+	motg->usb_psy.num_supplicants = ARRAY_SIZE(otg_pm_power_supplied_to);
+	motg->usb_psy.properties = otg_pm_power_props_usb;
+	motg->usb_psy.num_properties = ARRAY_SIZE(otg_pm_power_props_usb);
+	motg->usb_psy.get_property = otg_power_get_property_usb;
+	motg->usb_psy.set_property = otg_power_set_property_usb;
+
+	if (motg->pdata->otg_control == OTG_PMIC_CONTROL) {
+		/* if pm8921 use legacy implementation */
+		if (!pm8921_charger_register_vbus_sn(&msm_otg_set_vbus_state)) {
+			dev_dbg(motg->phy.dev, "%s: legacy support\n",
+				__func__);
+			legacy_power_supply = true;
+		} else {
+			ret = msm_otg_register_power_supply(pdev, motg);
+			if (ret)
+				goto remove_phy;
+		}
+	} else {
+		ret = msm_otg_register_power_supply(pdev, motg);
+		if (ret)
+			goto remove_phy;
+	}
+
 	return 0;
 
 remove_phy:
diff --git a/include/linux/mfd/wcd9xxx/core.h b/include/linux/mfd/wcd9xxx/core.h
index 421d758..8e8caf7 100644
--- a/include/linux/mfd/wcd9xxx/core.h
+++ b/include/linux/mfd/wcd9xxx/core.h
@@ -13,12 +13,13 @@
 #ifndef __MFD_TABLA_CORE_H__
 #define __MFD_TABLA_CORE_H__
 
+#include <linux/types.h>
 #include <linux/interrupt.h>
 #include <linux/pm_qos.h>
 #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
 
@@ -46,6 +47,7 @@
 
 
 enum {
+	/* INTR_REG 0 */
 	WCD9XXX_IRQ_SLIMBUS = 0,
 	WCD9XXX_IRQ_MBHC_REMOVAL,
 	WCD9XXX_IRQ_MBHC_SHORT_TERM,
@@ -54,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,
@@ -62,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,
 };
 
@@ -88,6 +100,44 @@
 	WCD9XXX_PM_ASLEEP,
 };
 
+/*
+ * data structure for Slimbus and I2S channel.
+ * Some of fields are only used in smilbus mode
+ */
+struct wcd9xxx_ch {
+	u32 sph;		/* share channel handle - slimbus only	*/
+	u32 ch_num;		/*
+				 * vitrual channel number, such as 128 -144.
+				 * apply for slimbus only
+				 */
+	u16 ch_h;		/* chanel handle - slimbus only */
+	u16 port;		/*
+				 * tabla port for RX and TX
+				 * such as 0-9 for TX and 10 -16 for RX
+				 * apply for both i2s and slimbus
+				 */
+	u16 shift;		/*
+				 * shift bit for RX and TX
+				 * apply for both i2s and slimbus
+				 */
+	struct list_head list;	/*
+				 * channel link list
+				 * apply for both i2s and slimbus
+				 */
+};
+
+struct wcd9xxx_codec_dai_data {
+	u32 rate;				/* sample rate          */
+	u32 bit_width;				/* sit width 16,24,32   */
+	struct list_head wcd9xxx_ch_list;	/* channel list         */
+	u16 grph;				/* slimbus group handle */
+	u32 ch_mask;
+	wait_queue_head_t dai_wait;
+};
+
+#define WCD9XXX_CH(xport, xshift) \
+	{.port = xport, .shift = xshift}
+
 struct wcd9xxx {
 	struct device *dev;
 	struct slim_device *slim;
@@ -102,7 +152,7 @@
 	int (*read_dev)(struct wcd9xxx *wcd9xxx, unsigned short reg,
 			int bytes, void *dest, bool interface_reg);
 	int (*write_dev)(struct wcd9xxx *wcd9xxx, unsigned short reg,
-			 int bytes, void *src, bool interface_reg);
+			int bytes, void *src, bool interface_reg);
 
 	u32 num_of_supplies;
 	struct regulator_bulk_data *supplies;
@@ -114,9 +164,6 @@
 	struct pm_qos_request pm_qos_req;
 	int wlock_holders;
 
-	int num_rx_port;
-	int num_tx_port;
-
 	u8 idbyte[4];
 
 	unsigned int irq_base;
@@ -125,6 +172,11 @@
 	u8 irq_masks_cache[WCD9XXX_NUM_IRQ_REGS];
 	bool irq_level_high[WCD9XXX_MAX_NUM_IRQS];
 	int num_irqs;
+	/* Slimbus or I2S port */
+	u32 num_rx_port;
+	u32 num_tx_port;
+	struct wcd9xxx_ch *rx_chs;
+	struct wcd9xxx_ch *tx_chs;
 };
 
 int wcd9xxx_reg_read(struct wcd9xxx *wcd9xxx, unsigned short reg);
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-slimslave.h b/include/linux/mfd/wcd9xxx/wcd9xxx-slimslave.h
index 0d5d058..45abd92 100644
--- a/include/linux/mfd/wcd9xxx/wcd9xxx-slimslave.h
+++ b/include/linux/mfd/wcd9xxx/wcd9xxx-slimslave.h
@@ -16,27 +16,6 @@
 #include <linux/slimbus/slimbus.h>
 #include <linux/mfd/wcd9xxx/core.h>
 
-/* Channel numbers to be used for each port */
-enum {
-	SLIM_TX_1   = 128,
-	SLIM_TX_2   = 129,
-	SLIM_TX_3   = 130,
-	SLIM_TX_4   = 131,
-	SLIM_TX_5   = 132,
-	SLIM_TX_6   = 133,
-	SLIM_TX_7   = 134,
-	SLIM_TX_8   = 135,
-	SLIM_TX_9   = 136,
-	SLIM_TX_10  = 137,
-	SLIM_RX_1   = 138,
-	SLIM_RX_2   = 139,
-	SLIM_RX_3   = 140,
-	SLIM_RX_4   = 141,
-	SLIM_RX_5   = 142,
-	SLIM_RX_6   = 143,
-	SLIM_RX_7   = 144,
-	SLIM_MAX    = 145
-};
 
 /*
  *  client is expected to give port ids in the range of
@@ -92,30 +71,42 @@
 /* slave port water mark level
  *   (0: 6bytes, 1: 9bytes, 2: 12 bytes, 3: 15 bytes)
  */
-#define SLAVE_PORT_WATER_MARK_VALUE 2
+#define SLAVE_PORT_WATER_MARK_6BYTES  0
+#define SLAVE_PORT_WATER_MARK_9BYTES  1
+#define SLAVE_PORT_WATER_MARK_12BYTES 2
+#define SLAVE_PORT_WATER_MARK_15BYTES 3
 #define SLAVE_PORT_WATER_MARK_SHIFT 1
 #define SLAVE_PORT_ENABLE           1
 #define SLAVE_PORT_DISABLE          0
-
+#define WATER_MARK_VAL \
+	((SLAVE_PORT_WATER_MARK_12BYTES << SLAVE_PORT_WATER_MARK_SHIFT) | \
+	 (SLAVE_PORT_ENABLE))
 #define BASE_CH_NUM 128
 
 
-int wcd9xxx_init_slimslave(struct wcd9xxx *wcd9xxx, u8 wcd9xxx_pgd_la);
+int wcd9xxx_init_slimslave(struct wcd9xxx *wcd9xxx,
+			   u8 wcd9xxx_pgd_la,
+			   unsigned int tx_num, unsigned int *tx_slot,
+			   unsigned int rx_num, unsigned int *rx_slot);
 
 int wcd9xxx_deinit_slimslave(struct wcd9xxx *wcd9xxx);
 
-int wcd9xxx_cfg_slim_sch_rx(struct wcd9xxx *wcd9xxx, unsigned int *ch_num,
-				unsigned int tot_ch, unsigned int rate);
-int wcd9xxx_cfg_slim_sch_tx(struct wcd9xxx *wcd9xxx, unsigned int *ch_num,
-				unsigned int tot_ch, unsigned int rate);
-int wcd9xxx_close_slim_sch_rx(struct wcd9xxx *wcd9xxx, unsigned int *ch_num,
-				unsigned int tot_ch);
-int wcd9xxx_close_slim_sch_tx(struct wcd9xxx *wcd9xxx, unsigned int *ch_num,
-				unsigned int tot_ch);
+int wcd9xxx_cfg_slim_sch_rx(struct wcd9xxx *wcd9xxx,
+			    struct list_head *wcd9xxx_ch_list,
+			    unsigned int rate, unsigned int bit_width,
+			    u16 *grph);
+int wcd9xxx_cfg_slim_sch_tx(struct wcd9xxx *wcd9xxx,
+			    struct list_head *wcd9xxx_ch_list,
+			    unsigned int rate, unsigned int bit_width,
+				u16 *grph);
+int wcd9xxx_close_slim_sch_rx(struct wcd9xxx *wcd9xxx,
+			      struct list_head *wcd9xxx_ch_list, u16 grph);
+int wcd9xxx_close_slim_sch_tx(struct wcd9xxx *wcd9xxx,
+			      struct list_head *wcd9xxx_ch_list, u16 grph);
 int wcd9xxx_get_channel(struct wcd9xxx *wcd9xxx,
 			unsigned int *rx_ch,
 			unsigned int *tx_ch);
 int wcd9xxx_get_slave_port(unsigned int ch_num);
-int wcd9xxx_disconnect_port(struct wcd9xxx *wcd9xxx, unsigned int *ch_num,
-				unsigned int tot_ch, unsigned int rx_tx);
+int wcd9xxx_disconnect_port(struct wcd9xxx *wcd9xxx,
+			    struct list_head *wcd9xxx_ch_list, u16 grph);
 #endif /* __WCD9310_SLIMSLAVE_H_ */
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/include/linux/power_supply.h b/include/linux/power_supply.h
index 03390b1..730c7b2 100644
--- a/include/linux/power_supply.h
+++ b/include/linux/power_supply.h
@@ -218,6 +218,7 @@
 extern int power_supply_set_battery_charged(struct power_supply *psy);
 extern int power_supply_set_current_limit(struct power_supply *psy, int limit);
 extern int power_supply_set_online(struct power_supply *psy, bool enable);
+extern int power_supply_set_present(struct power_supply *psy, bool enable);
 extern int power_supply_set_scope(struct power_supply *psy, int scope);
 extern int power_supply_set_charge_type(struct power_supply *psy, int type);
 extern int power_supply_set_supply_type(struct power_supply *psy,
@@ -241,6 +242,9 @@
 static inline int power_supply_set_online(struct power_supply *psy,
 							bool enable)
 							{ return -ENOSYS; }
+static inline int power_supply_set_present(struct power_supply *psy,
+							bool enable)
+							{ return -ENOSYS; }
 static inline int power_supply_set_scope(struct power_supply *psy,
 							int scope)
 							{ return -ENOSYS; }
diff --git a/include/linux/usb/msm_hsusb.h b/include/linux/usb/msm_hsusb.h
index 9294c27..a998ac2 100644
--- a/include/linux/usb/msm_hsusb.h
+++ b/include/linux/usb/msm_hsusb.h
@@ -25,7 +25,7 @@
 #include <linux/wakelock.h>
 #include <linux/pm_qos.h>
 #include <linux/hrtimer.h>
-
+#include <linux/power_supply.h>
 /*
  * The following are bit fields describing the usb_request.udc_priv word.
  * These bit fields are set by function drivers that wish to queue
@@ -383,6 +383,10 @@
 	u8 active_tmout;
 	struct hrtimer timer;
 	enum usb_vdd_type vdd_type;
+	struct power_supply usb_psy;
+	unsigned int online;
+	unsigned int host_mode;
+	unsigned int current_max;
 };
 
 struct msm_hsic_host_platform_data {
diff --git a/include/sound/apr_audio-v2.h b/include/sound/apr_audio-v2.h
index d902881..07179e9 100644
--- a/include/sound/apr_audio-v2.h
+++ b/include/sound/apr_audio-v2.h
@@ -1627,7 +1627,7 @@
  * Supported values: #AFE_API_VERSION_HDMI_CONFIG
  */
 
-u16                  dataype;
+u16                  datatype;
 /* data type
  * Supported values:
  * - #LINEAR_PCM_DATA
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/wcd9304.c b/sound/soc/codecs/wcd9304.c
index fa7abb1..9f02134 100644
--- a/sound/soc/codecs/wcd9304.c
+++ b/sound/soc/codecs/wcd9304.c
@@ -45,31 +45,42 @@
 #define NUM_DECIMATORS 4
 #define NUM_INTERPOLATORS 3
 #define BITS_PER_REG 8
+
+enum {
+	AIF1_PB = 0,
+	AIF1_CAP,
+	NUM_CODEC_DAIS,
+};
+
+struct wcd9xxx_ch sitar_rx_chs[SITAR_RX_MAX] = {
+	WCD9XXX_CH(10, 0),
+	WCD9XXX_CH(11, 1),
+	WCD9XXX_CH(12, 2),
+	WCD9XXX_CH(13, 3),
+	WCD9XXX_CH(14, 4)
+};
+
+struct wcd9xxx_ch sitar_tx_chs[SITAR_TX_MAX] = {
+	WCD9XXX_CH(0, 0),
+	WCD9XXX_CH(1, 1),
+	WCD9XXX_CH(2, 2),
+	WCD9XXX_CH(3, 3),
+	WCD9XXX_CH(4, 4),
+};
+
 #define SITAR_CFILT_FAST_MODE 0x00
 #define SITAR_CFILT_SLOW_MODE 0x40
 #define MBHC_FW_READ_ATTEMPTS 15
 #define MBHC_FW_READ_TIMEOUT 2000000
 
+#define SLIM_CLOSE_TIMEOUT 1000
+
 #define SITAR_JACK_MASK (SND_JACK_HEADSET | SND_JACK_OC_HPHL | SND_JACK_OC_HPHR)
 
 #define SITAR_I2S_MASTER_MODE_MASK 0x08
 
 #define SITAR_OCP_ATTEMPT 1
 
-#define AIF1_PB 1
-#define AIF1_CAP 2
-#define NUM_CODEC_DAIS 2
-#define SLIM_CLOSE_TIMEOUT 1000
-
-struct sitar_codec_dai_data {
-	u32 rate;
-	u32 *ch_num;
-	u32 ch_act;
-	u32 ch_tot;
-	u32 ch_mask;
-	wait_queue_head_t dai_wait;
-};
-
 #define SITAR_MCLK_RATE_12288KHZ 12288000
 #define SITAR_MCLK_RATE_9600KHZ 9600000
 
@@ -178,6 +189,11 @@
 	MBHC_STATE_RELEASE,
 };
 
+static const u32 vport_check_table[NUM_CODEC_DAIS] = {
+	0,					/* AIF1_PB */
+	0,					/* AIF1_CAP */
+};
+
 struct sitar_priv {
 	struct snd_soc_codec *codec;
 	u32 mclk_freq;
@@ -240,7 +256,7 @@
 	const struct firmware *mbhc_fw;
 
 	/* num of slim ports required */
-	struct sitar_codec_dai_data dai[NUM_CODEC_DAIS];
+	struct wcd9xxx_codec_dai_data dai[NUM_CODEC_DAIS];
 
 	/* Currently, only used for mbhc purpose, to protect
 	 * concurrent execution of mbhc threaded irq handlers and
@@ -935,6 +951,201 @@
 	SOC_DAPM_SINGLE("Switch", SITAR_A_RX_EAR_EN, 5, 1, 0),
 };
 
+static int slim_tx_vport_validation(u32 dai_id, u32 port_id,
+				    struct sitar_priv *sitar_p)
+{
+	struct wcd9xxx_ch *ch;
+	int ret = 0;
+	int index = 0;
+	u32 vtable = vport_check_table[dai_id];
+	pr_debug("%s: dai_id %u vtable 0x%x port_id %u\n", __func__,
+		 dai_id, vtable, port_id);
+	while (vtable) {
+		if (vtable & 1) {
+			list_for_each_entry(ch,
+				&sitar_p->dai[index].wcd9xxx_ch_list,
+				list) {
+				pr_debug("%s: index %u ch->port%u  vtable 0x%x\n",
+					__func__, index, ch->port, vtable);
+				if (ch->port == port_id) {
+					pr_err("%s: TX%u is used by AIF%u_CAP Mixer\n",
+						__func__, port_id + 1,
+						(index + 1)/2);
+					ret = -EINVAL;
+					break;
+				}
+			}
+		}
+		if (ret)
+			break;
+		index++;
+		vtable = vtable >> 1;
+	}
+	return ret;
+}
+
+static int slim_tx_mixer_get(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+	struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+
+	ucontrol->value.integer.value[0] = widget->value;
+	return 0;
+}
+
+static int slim_tx_mixer_put(struct snd_kcontrol *kcontrol,
+			     struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+	struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+	struct snd_soc_codec *codec = widget->codec;
+	struct sitar_priv *sitar_p = snd_soc_codec_get_drvdata(codec);
+	struct wcd9xxx *core = dev_get_drvdata(codec->dev->parent);
+	struct soc_multi_mixer_control *mixer =
+		((struct soc_multi_mixer_control *)kcontrol->private_value);
+	u32 dai_id = widget->shift;
+	u32 port_id = mixer->shift;
+	u32 enable = ucontrol->value.integer.value[0];
+
+	mutex_lock(&codec->mutex);
+
+	if (sitar_p->intf_type != WCD9XXX_INTERFACE_TYPE_SLIMBUS) {
+		if (dai_id != AIF1_CAP) {
+			dev_err(codec->dev, "%s: invalid AIF for I2C mode\n",
+				__func__);
+			mutex_unlock(&codec->mutex);
+			return -EINVAL;
+		}
+	}
+
+	switch (dai_id) {
+	case AIF1_CAP:
+		if (enable && !(widget->value & 1 << port_id)) {
+			if (slim_tx_vport_validation(dai_id,
+						     port_id, sitar_p)) {
+				pr_info("%s: TX%u is used by other virtual port\n",
+					__func__, port_id + 1);
+				mutex_unlock(&codec->mutex);
+				return -EINVAL;
+			}
+			widget->value |= 1 << port_id;
+			list_add_tail(&core->tx_chs[port_id].list,
+			&sitar_p->dai[dai_id].wcd9xxx_ch_list);
+		} else if (!enable && (widget->value & 1 << port_id)) {
+			widget->value &= ~(1<<port_id);
+			list_del_init(&core->tx_chs[port_id].list);
+		} else {
+			if (enable)
+				pr_info("%s: TX%u port is used by this virtual port\n",
+					__func__, port_id + 1);
+			else
+				pr_info("%s: TX%u port is not used by this virtual port\n",
+					__func__, port_id + 1);
+			/* avoid update power function */
+			mutex_unlock(&codec->mutex);
+			return 0;
+		}
+	break;
+	default:
+		pr_err("Unknown AIF %d\n", dai_id);
+		mutex_unlock(&codec->mutex);
+		return -EINVAL;
+	}
+
+	pr_debug("%s: name %s sname %s updated value %u shift %d\n", __func__,
+		widget->name, widget->sname, widget->value, widget->shift);
+	snd_soc_dapm_mixer_update_power(widget, kcontrol, enable);
+	mutex_unlock(&codec->mutex);
+	return 0;
+}
+
+static int slim_rx_mux_get(struct snd_kcontrol *kcontrol,
+			   struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+	struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+
+	ucontrol->value.enumerated.item[0] = widget->value;
+	return 0;
+}
+
+static const char * const slim_rx_mux_text[] = {
+	"ZERO", "AIF1_PB"
+};
+
+static int slim_rx_mux_put(struct snd_kcontrol *kcontrol,
+			   struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+	struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+	struct snd_soc_codec *codec = widget->codec;
+	struct sitar_priv *sitar_p = snd_soc_codec_get_drvdata(codec);
+	struct wcd9xxx *core = dev_get_drvdata(codec->dev->parent);
+	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+	u32 port_id = widget->shift;
+
+	widget->value = ucontrol->value.enumerated.item[0];
+
+	mutex_lock(&codec->mutex);
+
+	if (sitar_p->intf_type != WCD9XXX_INTERFACE_TYPE_SLIMBUS) {
+		if (widget->value > 1) {
+			dev_err(codec->dev, "%s: invalid AIF for I2C mode\n",
+				__func__);
+			mutex_unlock(&codec->mutex);
+			return -EINVAL;
+		}
+	}
+
+	switch (widget->value) {
+	case 0:
+		list_del_init(&core->rx_chs[port_id].list);
+		break;
+	case 1:
+		list_add_tail(&core->rx_chs[port_id].list,
+			      &sitar_p->dai[AIF1_PB].wcd9xxx_ch_list);
+		break;
+	default:
+		pr_err("Unknown AIF %d\n", widget->value);
+		mutex_unlock(&codec->mutex);
+		return -EINVAL;
+	}
+	snd_soc_dapm_mux_update_power(widget, kcontrol, 1, widget->value, e);
+	mutex_unlock(&codec->mutex);
+	return 0;
+}
+
+static const struct soc_enum slim_rx_mux_enum =
+	SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(slim_rx_mux_text), slim_rx_mux_text);
+
+static const struct snd_kcontrol_new sitar_aif_pb_mux[SITAR_RX_MAX] = {
+	SOC_DAPM_ENUM_EXT("SLIM RX1 MUX", slim_rx_mux_enum,
+			  slim_rx_mux_get, slim_rx_mux_put),
+	SOC_DAPM_ENUM_EXT("SLIM RX2 MUX", slim_rx_mux_enum,
+			  slim_rx_mux_get, slim_rx_mux_put),
+	SOC_DAPM_ENUM_EXT("SLIM RX3 MUX", slim_rx_mux_enum,
+			  slim_rx_mux_get, slim_rx_mux_put),
+	SOC_DAPM_ENUM_EXT("SLIM RX4 MUX", slim_rx_mux_enum,
+			  slim_rx_mux_get, slim_rx_mux_put),
+	SOC_DAPM_ENUM_EXT("SLIM RX5 MUX", slim_rx_mux_enum,
+			  slim_rx_mux_get, slim_rx_mux_put)
+};
+
+static const struct snd_kcontrol_new sitar_aif_cap_mixer[SITAR_TX_MAX] = {
+	SOC_SINGLE_EXT("SLIM TX1", SND_SOC_NOPM, SITAR_TX1, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX2", SND_SOC_NOPM, SITAR_TX2, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX3", SND_SOC_NOPM, SITAR_TX3, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX4", SND_SOC_NOPM, SITAR_TX4, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX5", SND_SOC_NOPM, SITAR_TX5, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+};
+
+
 static void sitar_codec_enable_adc_block(struct snd_soc_codec *codec,
 	int enable)
 {
@@ -1880,15 +2091,27 @@
 	SND_SOC_DAPM_MIXER("DAC1", SITAR_A_RX_EAR_EN, 6, 0, dac1_switch,
 		ARRAY_SIZE(dac1_switch)),
 	SND_SOC_DAPM_SUPPLY("EAR DRIVER", SITAR_A_RX_EAR_EN, 3, 0, NULL, 0),
-	SND_SOC_DAPM_AIF_IN_E("SLIM RX1", "AIF1 Playback", 0, SND_SOC_NOPM, 0,
-				0, sitar_codec_enable_slimrx,
-				SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
-	SND_SOC_DAPM_AIF_IN_E("SLIM RX2", "AIF1 Playback", 0, SND_SOC_NOPM, 0,
-				0, sitar_codec_enable_slimrx,
-				SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
-	SND_SOC_DAPM_AIF_IN("SLIM RX3", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0),
-	SND_SOC_DAPM_AIF_IN("SLIM RX4", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0),
-	SND_SOC_DAPM_AIF_IN("SLIM RX5", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0),
+
+	SND_SOC_DAPM_AIF_IN_E("AIF1 PB", "AIF1 Playback", 0, SND_SOC_NOPM,
+		AIF1_PB, 0, sitar_codec_enable_slimrx,
+		SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_MUX("SLIM RX1 MUX", SND_SOC_NOPM, SITAR_RX1, 0,
+		&sitar_aif_pb_mux[SITAR_RX1]),
+	SND_SOC_DAPM_MUX("SLIM RX2 MUX", SND_SOC_NOPM, SITAR_RX2, 0,
+		&sitar_aif_pb_mux[SITAR_RX2]),
+	SND_SOC_DAPM_MUX("SLIM RX3 MUX", SND_SOC_NOPM, SITAR_RX3, 0,
+		&sitar_aif_pb_mux[SITAR_RX3]),
+	SND_SOC_DAPM_MUX("SLIM RX4 MUX", SND_SOC_NOPM, SITAR_RX4, 0,
+		&sitar_aif_pb_mux[SITAR_RX4]),
+	SND_SOC_DAPM_MUX("SLIM RX5 MUX", SND_SOC_NOPM, SITAR_RX5, 0,
+		&sitar_aif_pb_mux[SITAR_RX5]),
+
+	SND_SOC_DAPM_MIXER("SLIM RX1", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("SLIM RX2", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("SLIM RX3", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("SLIM RX4", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("SLIM RX5", SND_SOC_NOPM, 0, 0, NULL, 0),
 
 	/* Headphone */
 	SND_SOC_DAPM_OUTPUT("HEADPHONE"),
@@ -2026,26 +2249,23 @@
 
 	SND_SOC_DAPM_MUX("ANC1 FB MUX", SND_SOC_NOPM, 0, 0, &anc1_fb_mux),
 
-	SND_SOC_DAPM_MUX("SLIM TX1 MUX", SND_SOC_NOPM, 0, 0, &sb_tx1_mux),
-	SND_SOC_DAPM_MUX("SLIM TX2 MUX", SND_SOC_NOPM, 0, 0, &sb_tx2_mux),
-	SND_SOC_DAPM_MUX("SLIM TX3 MUX", SND_SOC_NOPM, 0, 0, &sb_tx3_mux),
-	SND_SOC_DAPM_MUX("SLIM TX4 MUX", SND_SOC_NOPM, 0, 0, &sb_tx4_mux),
-	SND_SOC_DAPM_MUX("SLIM TX5 MUX", SND_SOC_NOPM, 0, 0, &sb_tx5_mux),
-
-	SND_SOC_DAPM_AIF_OUT_E("SLIM TX1", "AIF1 Capture", 0, SND_SOC_NOPM, 0,
-				0, sitar_codec_enable_slimtx,
+	SND_SOC_DAPM_AIF_OUT_E("AIF1 CAP", "AIF1 Capture", 0, SND_SOC_NOPM,
+				AIF1_CAP, 0, sitar_codec_enable_slimtx,
 				SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
 
-	SND_SOC_DAPM_AIF_OUT_E("SLIM TX2", "AIF1 Capture", 0, SND_SOC_NOPM, 0,
-				0, sitar_codec_enable_slimtx,
-				SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_MIXER("AIF1_CAP Mixer", SND_SOC_NOPM, AIF1_CAP, 0,
+		sitar_aif_cap_mixer, ARRAY_SIZE(sitar_aif_cap_mixer)),
 
-	SND_SOC_DAPM_AIF_OUT_E("SLIM TX3", "AIF1 Capture", 0, SND_SOC_NOPM, 0,
-				0, sitar_codec_enable_slimtx,
-				SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
-	SND_SOC_DAPM_AIF_OUT_E("SLIM TX4", "AIF1 Capture", 0, SND_SOC_NOPM, 0,
-				0, sitar_codec_enable_slimtx,
-				SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_MUX("SLIM TX1 MUX", SND_SOC_NOPM, SITAR_TX1, 0,
+		&sb_tx1_mux),
+	SND_SOC_DAPM_MUX("SLIM TX2 MUX", SND_SOC_NOPM, SITAR_TX2, 0,
+		&sb_tx2_mux),
+	SND_SOC_DAPM_MUX("SLIM TX3 MUX", SND_SOC_NOPM, SITAR_TX3, 0,
+		&sb_tx3_mux),
+	SND_SOC_DAPM_MUX("SLIM TX4 MUX", SND_SOC_NOPM, SITAR_TX3, 0,
+		&sb_tx4_mux),
+	SND_SOC_DAPM_MUX("SLIM TX5 MUX", SND_SOC_NOPM, SITAR_TX3, 0,
+		&sb_tx5_mux),
 
 	SND_SOC_DAPM_AIF_OUT_E("SLIM TX5", "AIF1 Capture", 0, SND_SOC_NOPM, 0,
 				0, sitar_codec_enable_slimtx,
@@ -2086,6 +2306,18 @@
 	{"SLIM TX3", NULL, "TX_I2S_CLK"},
 	{"SLIM TX4", NULL, "TX_I2S_CLK"},
 };
+#define SLIM_MIXER(x) (\
+	{x, "SLIM TX1", "SLIM TX1 MUX"}, \
+	{x, "SLIM TX2", "SLIM TX2 MUX"}, \
+	{x, "SLIM TX3", "SLIM TX3 MUX"}, \
+	{x, "SLIM TX4", "SLIM TX4 MUX"})
+
+
+#define SLIM_MUX(x, y) (\
+	{"SLIM RX1 MUX", x, y}, \
+	{"SLIM RX2 MUX", x, y}, \
+	{"SLIM RX3 MUX", x, y}, \
+	{"SLIM RX4 MUX", x, y})
 
 static const struct snd_soc_dapm_route audio_map[] = {
 	/* Earpiece (RX MIX1) */
@@ -2164,6 +2396,23 @@
 	{"RX3 MIX1", NULL, "ANC"},
 
 	/* SLIMBUS Connections */
+	{"AIF1 CAP", NULL, "AIF1_CAP Mixer"},
+
+	/* SLIM_MIXER("AIF1_CAP Mixer"),*/
+	{"AIF1_CAP Mixer", "SLIM TX1", "SLIM TX1 MUX"},
+	{"AIF1_CAP Mixer", "SLIM TX2", "SLIM TX2 MUX"},
+	{"AIF1_CAP Mixer", "SLIM TX3", "SLIM TX3 MUX"},
+	{"AIF1_CAP Mixer", "SLIM TX4", "SLIM TX4 MUX"},
+	/* SLIM_MUX("AIF1_PB", "AIF1 PB"), */
+	{"SLIM RX1 MUX", "AIF1_PB", "AIF1 PB"},
+	{"SLIM RX2 MUX", "AIF1_PB", "AIF1 PB"},
+	{"SLIM RX3 MUX", "AIF1_PB", "AIF1 PB"},
+	{"SLIM RX4 MUX", "AIF1_PB", "AIF1 PB"},
+
+	{"SLIM RX1", NULL, "SLIM RX1 MUX"},
+	{"SLIM RX2", NULL, "SLIM RX2 MUX"},
+	{"SLIM RX3", NULL, "SLIM RX3 MUX"},
+	{"SLIM RX4", NULL, "SLIM RX4 MUX"},
 
 	/* Slimbus port 5 is non functional in Sitar 1.0 */
 	{"RX1 MIX1 INP1", "RX1", "SLIM RX1"},
@@ -2205,12 +2454,6 @@
 
 
 	/* TX */
-	{"SLIM TX1", NULL, "SLIM TX1 MUX"},
-	{"SLIM TX2", NULL, "SLIM TX2 MUX"},
-	{"SLIM TX3", NULL, "SLIM TX3 MUX"},
-	{"SLIM TX4", NULL, "SLIM TX4 MUX"},
-	{"SLIM TX5", NULL, "SLIM TX5 MUX"},
-
 	{"SLIM TX1 MUX", "DEC1", "DEC1 MUX"},
 	{"SLIM TX2 MUX", "DEC2", "DEC2 MUX"},
 	{"SLIM TX3 MUX", "DEC3", "DEC3 MUX"},
@@ -2321,6 +2564,9 @@
 {
 	int ret;
 
+	if (reg == SND_SOC_NOPM)
+		return 0;
+
 	BUG_ON(reg > SITAR_MAX_REGISTER);
 
 	if (!sitar_volatile(codec, reg)) {
@@ -2338,6 +2584,9 @@
 	unsigned int val;
 	int ret;
 
+	if (reg == SND_SOC_NOPM)
+		return 0;
+
 	BUG_ON(reg > SITAR_MAX_REGISTER);
 
 	if (!sitar_volatile(codec, reg) && sitar_readable(codec, reg) &&
@@ -2585,11 +2834,11 @@
 		return;
 
 	if (dai->id <= NUM_CODEC_DAIS) {
-		if (sitar->dai[dai->id-1].ch_mask) {
+		if (sitar->dai[dai->id].ch_mask) {
 			active = 1;
 			pr_debug("%s(): Codec DAI: chmask[%d] = 0x%x\n",
-				__func__, dai->id-1,
-				sitar->dai[dai->id-1].ch_mask);
+				__func__, dai->id,
+				sitar->dai[dai->id].ch_mask);
 		}
 	}
 
@@ -2703,38 +2952,16 @@
 
 {
 	struct sitar_priv *sitar = snd_soc_codec_get_drvdata(dai->codec);
-	u32 i = 0;
+	struct wcd9xxx *core = dev_get_drvdata(dai->codec->dev->parent);
 	if (!tx_slot && !rx_slot) {
 		pr_err("%s: Invalid\n", __func__);
 		return -EINVAL;
 	}
 	pr_debug("%s: DAI-ID %x %d %d\n", __func__, dai->id, tx_num, rx_num);
 
-	if (dai->id == AIF1_PB) {
-		for (i = 0; i < rx_num; i++) {
-			sitar->dai[dai->id - 1].ch_num[i]  = rx_slot[i];
-			sitar->dai[dai->id - 1].ch_act = 0;
-			sitar->dai[dai->id - 1].ch_tot = rx_num;
-		}
-	} else if (dai->id == AIF1_CAP) {
-		sitar->dai[dai->id - 1].ch_tot = tx_num;
-		/* If all channels are already active,
-		 * Do not reset ch_act flag
-		 */
-		if ((sitar->dai[dai->id - 1].ch_tot != 0)
-			&& (sitar->dai[dai->id - 1].ch_act ==
-				sitar->dai[dai->id - 1].ch_tot)) {
-			pr_info("%s: ch_act = %d, ch_tot = %d\n", __func__,
-				sitar->dai[dai->id - 1].ch_act,
-				sitar->dai[dai->id - 1].ch_tot);
-
-			return 0;
-		}
-		sitar->dai[dai->id - 1].ch_act = 0;
-
-		for (i = 0; i < tx_num; i++)
-			sitar->dai[dai->id - 1].ch_num[i]  = tx_slot[i];
-	}
+	if (sitar->intf_type == WCD9XXX_INTERFACE_TYPE_SLIMBUS)
+		wcd9xxx_init_slimslave(core, core->slim->laddr,
+			tx_num, tx_slot, rx_num, rx_slot);
 	return 0;
 }
 
@@ -2743,33 +2970,38 @@
 			unsigned int *rx_num, unsigned int *rx_slot)
 
 {
-	struct wcd9xxx *sitar = dev_get_drvdata(dai->codec->control_data);
+	struct sitar_priv *sitar_p = snd_soc_codec_get_drvdata(dai->codec);
+	u32 i = 0;
+	struct wcd9xxx_ch *ch;
 
-	u32 cnt = 0;
-	u32 tx_ch[SLIM_MAX_TX_PORTS];
-	u32 rx_ch[SLIM_MAX_RX_PORTS];
-
-	if (!rx_slot && !tx_slot) {
-		pr_err("%s: Invalid\n", __func__);
-		return -EINVAL;
-	}
-	pr_debug("%s: DAI-ID %x\n", __func__, dai->id);
-	/* for virtual port, codec driver needs to do
-	* housekeeping, for now should be ok
-	*/
-	wcd9xxx_get_channel(sitar, rx_ch, tx_ch);
-	if (dai->id == AIF1_PB) {
-		*rx_num = sitar_dai[dai->id - 1].playback.channels_max;
-		while (cnt < *rx_num) {
-			rx_slot[cnt] = rx_ch[cnt];
-			cnt++;
+	switch (dai->id) {
+	case AIF1_PB:
+		if (!rx_slot || !rx_num) {
+			pr_err("%s: Invalid rx_slot 0x%x or rx_num 0x%x\n",
+				__func__, (u32) rx_slot, (u32) rx_num);
+			return -EINVAL;
 		}
-	} else if (dai->id == AIF1_CAP) {
-		*tx_num = sitar_dai[dai->id - 1].capture.channels_max;
-		tx_slot[0] = tx_ch[cnt];
-		tx_slot[1] = tx_ch[4 + cnt];
-		tx_slot[2] = tx_ch[2 + cnt];
-		tx_slot[3] = tx_ch[3 + cnt];
+		list_for_each_entry(ch, &sitar_p->dai[dai->id].wcd9xxx_ch_list,
+				    list) {
+			rx_slot[i++] = ch->ch_num;
+		}
+		*rx_num = i;
+		break;
+	case AIF1_CAP:
+		if (!tx_slot || !tx_num) {
+			pr_err("%s: Invalid tx_slot 0x%x or tx_num 0x%x\n",
+				__func__, (u32) tx_slot, (u32) tx_num);
+			return -EINVAL;
+		}
+		list_for_each_entry(ch, &sitar_p->dai[dai->id].wcd9xxx_ch_list,
+				    list) {
+			tx_slot[i++] = ch->ch_num;
+		}
+		*tx_num = i;
+		break;
+	default:
+		pr_err("%s: Invalid dai %d", __func__, dai->id);
+		return -EINVAL;
 	}
 	return 0;
 }
@@ -2805,7 +3037,7 @@
 		break;
 	default:
 		pr_err("%s: Invalid sampling rate %d\n", __func__,
-				params_rate(params));
+			params_rate(params));
 		return -EINVAL;
 	}
 
@@ -2842,13 +3074,14 @@
 					0x20, 0x00);
 				break;
 			default:
-				pr_err("invalid format\n");
-				break;
+				pr_err("%s: Unsupport format %d\n", __func__,
+					params_format(params));
+				return -EINVAL;
 			}
 			snd_soc_update_bits(codec, SITAR_A_CDC_CLK_TX_I2S_CTL,
 						0x03, tx_fs_rate);
 		} else {
-			sitar->dai[dai->id - 1].rate   = params_rate(params);
+			sitar->dai[dai->id].rate   = params_rate(params);
 		}
 	}
 
@@ -2889,13 +3122,14 @@
 					0x20, 0x00);
 				break;
 			default:
-				pr_err("invalid format\n");
+				pr_err("%s: Unsupport format %d\n", __func__,
+					params_format(params));
 				break;
 			}
 			snd_soc_update_bits(codec, SITAR_A_CDC_CLK_RX_I2S_CTL,
 						0x03, (rx_fs_rate >> 0x05));
 		} else {
-			sitar->dai[dai->id - 1].rate   = params_rate(params);
+			sitar->dai[dai->id].rate   = params_rate(params);
 		}
 	}
 
@@ -2977,30 +3211,30 @@
 static int sitar_codec_enable_chmask(struct sitar_priv *sitar,
 	int event, int index)
 {
-	int ret = 0;
-	u32 k = 0;
+	int  ret = 0;
+	struct wcd9xxx_ch *ch;
+
 	switch (event) {
 	case SND_SOC_DAPM_POST_PMU:
-		for (k = 0; k < sitar->dai[index].ch_tot; k++) {
-			ret = wcd9xxx_get_slave_port(
-						sitar->dai[index].ch_num[k]);
+		list_for_each_entry(ch,
+			&sitar->dai[index].wcd9xxx_ch_list, list) {
+			ret = wcd9xxx_get_slave_port(ch->ch_num);
 			if (ret < 0) {
-				pr_err("%s: Invalid Slave port ID: %d\n",
-					   __func__, ret);
+				pr_err("%s: Invalid slave port ID: %d\n",
+					__func__, ret);
 				ret = -EINVAL;
 				break;
 			}
 			sitar->dai[index].ch_mask |= 1 << ret;
 		}
-		ret = 0;
 		break;
 	case SND_SOC_DAPM_POST_PMD:
 		ret = wait_event_timeout(sitar->dai[index].dai_wait,
 					(sitar->dai[index].ch_mask == 0),
-					msecs_to_jiffies(SLIM_CLOSE_TIMEOUT));
+					 msecs_to_jiffies(SLIM_CLOSE_TIMEOUT));
 		if (!ret) {
-			pr_err("%s: slim close tx/rx timeout\n",
-				   __func__);
+			pr_err("%s: Slim close tx/rx wait timeout\n",
+				__func__);
 			ret = -EINVAL;
 		} else {
 			ret = 0;
@@ -3013,70 +3247,46 @@
 static int sitar_codec_enable_slimrx(struct snd_soc_dapm_widget *w,
 	struct snd_kcontrol *kcontrol, int event)
 {
-	struct wcd9xxx *sitar;
+	struct wcd9xxx *core;
 	struct snd_soc_codec *codec = w->codec;
 	struct sitar_priv *sitar_p = snd_soc_codec_get_drvdata(codec);
-	u32  j = 0;
-	int ret = 0;
-	codec->control_data = dev_get_drvdata(codec->dev->parent);
-	sitar = codec->control_data;
+	int ret  = 0;
+	struct wcd9xxx_codec_dai_data *dai;
+
+	core = dev_get_drvdata(codec->dev->parent);
 
 	/* Execute the callback only if interface type is slimbus */
 	if (sitar_p->intf_type != WCD9XXX_INTERFACE_TYPE_SLIMBUS) {
-		if (event == SND_SOC_DAPM_POST_PMD && (sitar != NULL))
-			sitar_codec_pm_runtime_put(sitar);
+		if (event == SND_SOC_DAPM_POST_PMD && (core != NULL))
+			sitar_codec_pm_runtime_put(core);
 		return 0;
 	}
 
+	dai = &sitar_p->dai[w->shift];
+
 	switch (event) {
 	case SND_SOC_DAPM_POST_PMU:
-		for (j = 0; j < ARRAY_SIZE(sitar_dai); j++) {
-			if (sitar_dai[j].id == AIF1_CAP)
-				continue;
-			if (!strncmp(w->sname,
-				sitar_dai[j].playback.stream_name, 13)) {
-				++sitar_p->dai[j].ch_act;
-				break;
-			}
-		}
-		if (sitar_p->dai[j].ch_act == sitar_p->dai[j].ch_tot) {
-			ret = sitar_codec_enable_chmask(sitar_p, event, j);
-			ret = wcd9xxx_cfg_slim_sch_rx(sitar,
-					sitar_p->dai[j].ch_num,
-					sitar_p->dai[j].ch_tot,
-					sitar_p->dai[j].rate);
-		}
+		ret = sitar_codec_enable_chmask(sitar_p, SND_SOC_DAPM_POST_PMU,
+						w->shift);
+		ret = wcd9xxx_cfg_slim_sch_rx(core, &dai->wcd9xxx_ch_list,
+					      dai->rate, dai->bit_width,
+					      &dai->grph);
 		break;
 	case SND_SOC_DAPM_POST_PMD:
-		for (j = 0; j < ARRAY_SIZE(sitar_dai); j++) {
-			if (sitar_dai[j].id == AIF1_CAP)
-				continue;
-			if (!strncmp(w->sname,
-				sitar_dai[j].playback.stream_name, 13)) {
-				--sitar_p->dai[j].ch_act;
-				break;
-			}
-		}
-		if (!sitar_p->dai[j].ch_act) {
-			wcd9xxx_close_slim_sch_rx(sitar,
-					sitar_p->dai[j].ch_num,
-					sitar_p->dai[j].ch_tot);
-			ret = sitar_codec_enable_chmask(sitar_p, event, j);
-			if (ret < 0) {
-				ret = wcd9xxx_disconnect_port(sitar,
-						sitar_p->dai[j].ch_num,
-						sitar_p->dai[j].ch_tot,
-						1);
+		ret = wcd9xxx_close_slim_sch_rx(core, &dai->wcd9xxx_ch_list,
+						dai->grph);
+		ret = sitar_codec_enable_chmask(sitar_p, SND_SOC_DAPM_POST_PMD,
+						w->shift);
+		if (ret < 0) {
+			ret = wcd9xxx_disconnect_port(core,
+						      &dai->wcd9xxx_ch_list,
+						      dai->grph);
 				pr_info("%s: Disconnect RX port ret = %d\n",
-						__func__, ret);
-			}
-			sitar_p->dai[j].rate = 0;
-			memset(sitar_p->dai[j].ch_num, 0, (sizeof(u32)*
-					sitar_p->dai[j].ch_tot));
-			sitar_p->dai[j].ch_tot = 0;
-			if (sitar != NULL)
-				sitar_codec_pm_runtime_put(sitar);
+					__func__, ret);
 		}
+		if (core != NULL)
+			sitar_codec_pm_runtime_put(core);
+		break;
 	}
 	return ret;
 }
@@ -3084,72 +3294,45 @@
 static int sitar_codec_enable_slimtx(struct snd_soc_dapm_widget *w,
 	struct snd_kcontrol *kcontrol, int event)
 {
-	struct wcd9xxx *sitar;
+	struct wcd9xxx *core;
 	struct snd_soc_codec *codec = w->codec;
 	struct sitar_priv *sitar_p = snd_soc_codec_get_drvdata(codec);
-	/* index to the DAI ID, for now hardcoding */
-	u32  j = 0;
+	struct wcd9xxx_codec_dai_data *dai;
 	int ret = 0;
 
-	codec->control_data = dev_get_drvdata(codec->dev->parent);
-	sitar = codec->control_data;
+	core = dev_get_drvdata(codec->dev->parent);
 
 	/* Execute the callback only if interface type is slimbus */
 	if (sitar_p->intf_type != WCD9XXX_INTERFACE_TYPE_SLIMBUS) {
-		if (event == SND_SOC_DAPM_POST_PMD && (sitar != NULL))
-			sitar_codec_pm_runtime_put(sitar);
+		if (event == SND_SOC_DAPM_POST_PMD && (core != NULL))
+			sitar_codec_pm_runtime_put(core);
 		return 0;
 	}
 
+	dai = &sitar_p->dai[w->shift];
 	switch (event) {
 	case SND_SOC_DAPM_POST_PMU:
-		for (j = 0; j < ARRAY_SIZE(sitar_dai); j++) {
-			if (sitar_dai[j].id == AIF1_PB)
-				continue;
-			if (!strncmp(w->sname,
-				sitar_dai[j].capture.stream_name, 13)) {
-				++sitar_p->dai[j].ch_act;
-				break;
-			}
-		}
-		if (sitar_p->dai[j].ch_act == sitar_p->dai[j].ch_tot) {
-			ret = sitar_codec_enable_chmask(sitar_p, event, j);
-			ret = wcd9xxx_cfg_slim_sch_tx(sitar,
-					sitar_p->dai[j].ch_num,
-					sitar_p->dai[j].ch_tot,
-					sitar_p->dai[j].rate);
-		}
+		ret = sitar_codec_enable_chmask(sitar_p, SND_SOC_DAPM_POST_PMU,
+						w->shift);
+		ret = wcd9xxx_cfg_slim_sch_tx(core, &dai->wcd9xxx_ch_list,
+					      dai->rate, dai->bit_width,
+					      &dai->grph);
 		break;
 	case SND_SOC_DAPM_POST_PMD:
-		for (j = 0; j < ARRAY_SIZE(sitar_dai); j++) {
-			if (sitar_dai[j].id == AIF1_PB)
-				continue;
-			if (!strncmp(w->sname,
-				sitar_dai[j].capture.stream_name, 13)) {
-				--sitar_p->dai[j].ch_act;
-				break;
-			}
+		ret = wcd9xxx_close_slim_sch_tx(core, &dai->wcd9xxx_ch_list,
+						dai->grph);
+		ret = sitar_codec_enable_chmask(sitar_p, SND_SOC_DAPM_POST_PMD,
+						w->shift);
+		if (ret < 0) {
+			ret = wcd9xxx_disconnect_port(core,
+						      &dai->wcd9xxx_ch_list,
+						      dai->grph);
+				pr_info("%s: Disconnect RX port ret = %d\n",
+					__func__, ret);
 		}
-		if (!sitar_p->dai[j].ch_act) {
-			wcd9xxx_close_slim_sch_tx(sitar,
-					sitar_p->dai[j].ch_num,
-					sitar_p->dai[j].ch_tot);
-			ret = sitar_codec_enable_chmask(sitar_p, event, j);
-			if (ret < 0) {
-				ret = wcd9xxx_disconnect_port(sitar,
-						sitar_p->dai[j].ch_num,
-						sitar_p->dai[j].ch_tot,
-						0);
-				pr_info("%s: Disconnect TX port, ret = %d\n",
-						__func__, ret);
-			}
-			sitar_p->dai[j].rate = 0;
-			memset(sitar_p->dai[j].ch_num, 0, (sizeof(u32)*
-					sitar_p->dai[j].ch_tot));
-			sitar_p->dai[j].ch_tot = 0;
-			if (sitar != NULL)
-				sitar_codec_pm_runtime_put(sitar);
-		}
+		if (core != NULL)
+			sitar_codec_pm_runtime_put(core);
+		break;
 	}
 	return ret;
 }
@@ -5063,16 +5246,16 @@
 
 static int sitar_codec_probe(struct snd_soc_codec *codec)
 {
-	struct sitar *control;
+	struct wcd9xxx *core;
 	struct sitar_priv *sitar;
 	struct snd_soc_dapm_context *dapm = &codec->dapm;
 	int ret = 0;
 	int i;
 	u8 sitar_version;
-	int ch_cnt;
+	void *ptr = NULL;
 
 	codec->control_data = dev_get_drvdata(codec->dev->parent);
-	control = codec->control_data;
+	core = codec->control_data;
 
 	sitar = kzalloc(sizeof(struct sitar_priv), GFP_KERNEL);
 	if (!sitar) {
@@ -5121,12 +5304,35 @@
 		ARRAY_SIZE(sitar_snd_controls));
 	snd_soc_dapm_new_controls(dapm, sitar_dapm_widgets,
 		ARRAY_SIZE(sitar_dapm_widgets));
+
+	ptr = kmalloc((sizeof(sitar_rx_chs) +
+		       sizeof(sitar_tx_chs)), GFP_KERNEL);
+	if (!ptr) {
+		pr_err("%s: no mem for slim chan ctl data\n", __func__);
+		ret = -ENOMEM;
+		goto err_nomem_slimch;
+	}
 	if (sitar->intf_type == WCD9XXX_INTERFACE_TYPE_I2C) {
 		snd_soc_dapm_new_controls(dapm, sitar_dapm_i2s_widgets,
 			ARRAY_SIZE(sitar_dapm_i2s_widgets));
 		snd_soc_dapm_add_routes(dapm, audio_i2s_map,
 		ARRAY_SIZE(audio_i2s_map));
+		for (i = 0; i < ARRAY_SIZE(sitar_i2s_dai); i++)
+			INIT_LIST_HEAD(&sitar->dai[i].wcd9xxx_ch_list);
 	}
+	if (sitar->intf_type == WCD9XXX_INTERFACE_TYPE_SLIMBUS) {
+		for (i = 0; i < NUM_CODEC_DAIS; i++) {
+			INIT_LIST_HEAD(&sitar->dai[i].wcd9xxx_ch_list);
+			init_waitqueue_head(&sitar->dai[i].dai_wait);
+		}
+	}
+	core->num_rx_port = SITAR_RX_MAX;
+	core->rx_chs = ptr;
+	memcpy(core->rx_chs, sitar_rx_chs, sizeof(sitar_rx_chs));
+	core->num_tx_port = SITAR_TX_MAX;
+	core->tx_chs = ptr + sizeof(sitar_rx_chs);
+	memcpy(core->tx_chs, sitar_tx_chs, sizeof(sitar_tx_chs));
+
 	snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map));
 
 	sitar_version = snd_soc_read(codec, WCD9XXX_A_CHIP_VERSION);
@@ -5212,22 +5418,6 @@
 	}
 	wcd9xxx_disable_irq(codec->control_data, WCD9XXX_IRQ_HPH_PA_OCPR_FAULT);
 
-	for (i = 0; i < ARRAY_SIZE(sitar_dai); i++) {
-		switch (sitar_dai[i].id) {
-		case AIF1_PB:
-			ch_cnt = sitar_dai[i].playback.channels_max;
-			break;
-		case AIF1_CAP:
-			ch_cnt = sitar_dai[i].capture.channels_max;
-			break;
-		default:
-			continue;
-		}
-		sitar->dai[i].ch_num = kzalloc((sizeof(unsigned int)*
-					ch_cnt), GFP_KERNEL);
-		init_waitqueue_head(&sitar->dai[i].dai_wait);
-	}
-
 	codec->ignore_pmdown_time = 1;
 
 #ifdef CONFIG_DEBUG_FS
@@ -5252,6 +5442,8 @@
 	wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_MBHC_INSERTION,
 			 sitar);
 err_insert_irq:
+	kfree(ptr);
+err_nomem_slimch:
 err_pdata:
 	mutex_destroy(&sitar->codec_resource_lock);
 	kfree(sitar);
@@ -5259,7 +5451,6 @@
 }
 static int sitar_codec_remove(struct snd_soc_codec *codec)
 {
-	int i;
 	struct sitar_priv *sitar = snd_soc_codec_get_drvdata(codec);
 	wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_SLIMBUS, sitar);
 	wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_MBHC_RELEASE, sitar);
@@ -5274,8 +5465,6 @@
 	sitar_codec_enable_bandgap(codec, SITAR_BANDGAP_OFF);
 	if (sitar->mbhc_fw)
 		release_firmware(sitar->mbhc_fw);
-	for (i = 0; i < ARRAY_SIZE(sitar_dai); i++)
-		kfree(sitar->dai[i].ch_num);
 	mutex_destroy(&sitar->codec_resource_lock);
 	kfree(sitar);
 	return 0;
diff --git a/sound/soc/codecs/wcd9304.h b/sound/soc/codecs/wcd9304.h
index 70b3f0b..13336ef 100644
--- a/sound/soc/codecs/wcd9304.h
+++ b/sound/soc/codecs/wcd9304.h
@@ -191,6 +191,26 @@
 extern int sitar_mclk_enable(struct snd_soc_codec *codec, int mclk_enable,
 							 bool dapm);
 
+/* Number of input and output Slimbus ports
+ */
+enum {
+	SITAR_RX1 = 0,
+	SITAR_RX2,
+	SITAR_RX3,
+	SITAR_RX4,
+	SITAR_RX5,
+	SITAR_RX_MAX,
+};
+
+enum {
+	SITAR_TX1 = 0,
+	SITAR_TX2,
+	SITAR_TX3,
+	SITAR_TX4,
+	SITAR_TX5,
+	SITAR_TX_MAX,
+};
+
 extern void *sitar_mbhc_cal_btn_det_mp(const struct sitar_mbhc_btn_detect_cfg
 				       *btn_det,
 				       const enum sitar_mbhc_btn_det_mem mem);
diff --git a/sound/soc/codecs/wcd9310.c b/sound/soc/codecs/wcd9310.c
index b64a6a7..564cad6 100644
--- a/sound/soc/codecs/wcd9310.c
+++ b/sound/soc/codecs/wcd9310.c
@@ -74,25 +74,33 @@
 
 #define TABLA_OCP_ATTEMPT 1
 
-#define AIF1_PB 1
-#define AIF1_CAP 2
-#define AIF2_PB 3
-#define AIF2_CAP 4
-#define AIF3_CAP 5
-#define AIF3_PB  6
-
-#define NUM_CODEC_DAIS 6
-#define TABLA_COMP_DIGITAL_GAIN_OFFSET 3
-
-struct tabla_codec_dai_data {
-	u32 rate;
-	u32 *ch_num;
-	u32 ch_act;
-	u32 ch_tot;
-	u32 ch_mask;
-	wait_queue_head_t dai_wait;
+enum {
+	AIF1_PB = 0,
+	AIF1_CAP,
+	AIF2_PB,
+	AIF2_CAP,
+	AIF3_PB,
+	AIF3_CAP,
+	NUM_CODEC_DAIS,
 };
 
+enum {
+	RX_MIX1_INP_SEL_ZERO = 0,
+	RX_MIX1_INP_SEL_SRC1,
+	RX_MIX1_INP_SEL_SRC2,
+	RX_MIX1_INP_SEL_IIR1,
+	RX_MIX1_INP_SEL_IIR2,
+	RX_MIX1_INP_SEL_RX1,
+	RX_MIX1_INP_SEL_RX2,
+	RX_MIX1_INP_SEL_RX3,
+	RX_MIX1_INP_SEL_RX4,
+	RX_MIX1_INP_SEL_RX5,
+	RX_MIX1_INP_SEL_RX6,
+	RX_MIX1_INP_SEL_RX7,
+};
+
+#define TABLA_COMP_DIGITAL_GAIN_OFFSET 3
+
 #define TABLA_MCLK_RATE_12288KHZ 12288000
 #define TABLA_MCLK_RATE_9600KHZ 9600000
 
@@ -255,6 +263,38 @@
 
 static struct hpf_work tx_hpf_work[NUM_DECIMATORS];
 
+static const struct wcd9xxx_ch tabla_rx_chs[TABLA_RX_MAX] = {
+	WCD9XXX_CH(10, 0),
+	WCD9XXX_CH(11, 1),
+	WCD9XXX_CH(12, 2),
+	WCD9XXX_CH(13, 3),
+	WCD9XXX_CH(14, 4),
+	WCD9XXX_CH(15, 5),
+	WCD9XXX_CH(16, 6)
+};
+
+static const struct wcd9xxx_ch tabla_tx_chs[TABLA_TX_MAX] = {
+	WCD9XXX_CH(0, 0),
+	WCD9XXX_CH(1, 1),
+	WCD9XXX_CH(2, 2),
+	WCD9XXX_CH(3, 3),
+	WCD9XXX_CH(4, 4),
+	WCD9XXX_CH(5, 5),
+	WCD9XXX_CH(6, 6),
+	WCD9XXX_CH(7, 7),
+	WCD9XXX_CH(8, 8),
+	WCD9XXX_CH(9, 9)
+};
+
+static const u32 vport_check_table[NUM_CODEC_DAIS] = {
+	0,					/* AIF1_PB */
+	(1 << AIF2_CAP) | (1 << AIF3_CAP),	/* AIF1_CAP */
+	0,					/* AIF2_PB */
+	(1 << AIF1_CAP) | (1 << AIF3_CAP),	/* AIF2_CAP */
+	0,					/* AIF2_PB */
+	(1 << AIF1_CAP) | (1 << AIF2_CAP),	/* AIF2_CAP */
+};
+
 struct tabla_priv {
 	struct snd_soc_codec *codec;
 	struct tabla_reg_address reg_addr;
@@ -310,7 +350,7 @@
 	const struct firmware *mbhc_fw;
 
 	/* num of slim ports required */
-	struct tabla_codec_dai_data dai[NUM_CODEC_DAIS];
+	struct wcd9xxx_codec_dai_data dai[NUM_CODEC_DAIS];
 
 	/*compander*/
 	int comp_enabled[COMPANDER_MAX];
@@ -1682,6 +1722,238 @@
 static const struct snd_kcontrol_new lineout4_ground_switch =
 	SOC_DAPM_SINGLE("Switch", TABLA_A_RX_LINE_4_DAC_CTL, 6, 1, 0);
 
+static int slim_tx_vport_validation(u32 dai_id, u32 port_id,
+				    struct tabla_priv *tabla_p)
+{
+	struct wcd9xxx_ch *ch;
+	int ret = 0;
+	int index = 0;
+	u32 vtable = vport_check_table[dai_id];
+	pr_debug("%s: dai_id %u vtable 0x%x port_id %u\n", __func__,
+		 dai_id, vtable, port_id);
+	while (vtable) {
+		if (vtable & 1) {
+			list_for_each_entry(ch,
+				&tabla_p->dai[index].wcd9xxx_ch_list,
+				list) {
+				pr_debug("%s: index %u ch->port %u vtable 0x%x\n",
+					__func__, index, ch->port, vtable);
+				if (ch->port == port_id) {
+					pr_err("%s: TX%u is used by AIF%u_CAP Mixer\n",
+						__func__, port_id + 1,
+						(index + 1)/2);
+					ret = -EINVAL;
+					break;
+				}
+			}
+		}
+		if (ret)
+			break;
+		index++;
+		vtable = vtable >> 1;
+	}
+	return ret;
+}
+
+/* virtual port entries */
+static int slim_tx_mixer_get(struct snd_kcontrol *kcontrol,
+			     struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+	struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+
+	ucontrol->value.integer.value[0] = widget->value;
+	return 0;
+}
+
+static int slim_tx_mixer_put(struct snd_kcontrol *kcontrol,
+			     struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+	struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+	struct snd_soc_codec *codec = widget->codec;
+	struct tabla_priv *tabla_p = snd_soc_codec_get_drvdata(codec);
+	struct wcd9xxx *core = dev_get_drvdata(codec->dev->parent);
+	struct soc_multi_mixer_control *mixer =
+		((struct soc_multi_mixer_control *)kcontrol->private_value);
+	u32 dai_id = widget->shift;
+	u32 port_id = mixer->shift;
+	u32 enable = ucontrol->value.integer.value[0];
+
+	pr_debug("%s: wname %s cname %s value %u shift %d item %ld\n", __func__,
+		widget->name, ucontrol->id.name, widget->value, widget->shift,
+		ucontrol->value.integer.value[0]);
+
+	mutex_lock(&codec->mutex);
+	if (tabla_p->intf_type != WCD9XXX_INTERFACE_TYPE_SLIMBUS) {
+		if (dai_id != AIF1_CAP) {
+			dev_err(codec->dev, "%s: invalid AIF for I2C mode\n",
+				__func__);
+			mutex_unlock(&codec->mutex);
+			return -EINVAL;
+		}
+	}
+	switch (dai_id) {
+	case AIF1_CAP:
+	case AIF2_CAP:
+	case AIF3_CAP:
+		/* only add to the list if value not set
+		 */
+		if (enable && !(widget->value & 1 << port_id)) {
+			if (slim_tx_vport_validation(dai_id,
+						     port_id, tabla_p)) {
+				pr_info("%s: TX%u is used by other virtual port\n",
+					__func__, port_id + 1);
+				mutex_unlock(&codec->mutex);
+				return -EINVAL;
+			}
+			widget->value |= 1 << port_id;
+			list_add_tail(&core->tx_chs[port_id].list,
+				      &tabla_p->dai[dai_id].wcd9xxx_ch_list
+				      );
+		} else if (!enable && (widget->value & 1 << port_id)) {
+			widget->value &= ~(1 << port_id);
+			list_del_init(&core->tx_chs[port_id].list);
+		} else {
+			if (enable)
+				pr_info("%s: TX%u port is used by this virtual port\n",
+					__func__, port_id + 1);
+			else
+				pr_info("%s: TX%u port is not used by this virtual port\n",
+					__func__, port_id + 1);
+			/* avoid update power function */
+			mutex_unlock(&codec->mutex);
+			return 0;
+		}
+		break;
+	default:
+		pr_err("Unknown AIF %d\n", dai_id);
+		mutex_unlock(&codec->mutex);
+		return -EINVAL;
+	}
+	pr_debug("%s: name %s sname %s updated value %u shift %d\n", __func__,
+		widget->name, widget->sname, widget->value, widget->shift);
+
+	snd_soc_dapm_mixer_update_power(widget, kcontrol, enable);
+
+	mutex_unlock(&codec->mutex);
+	return 0;
+}
+
+static int slim_rx_mux_get(struct snd_kcontrol *kcontrol,
+			   struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+	struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+
+	ucontrol->value.enumerated.item[0] = widget->value;
+	return 0;
+}
+
+static const char *const slim_rx_mux_text[] = {
+	"ZERO", "AIF1_PB", "AIF2_PB", "AIF3_PB"
+};
+
+static int slim_rx_mux_put(struct snd_kcontrol *kcontrol,
+			   struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+	struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+	struct snd_soc_codec *codec = widget->codec;
+	struct tabla_priv *tabla_p = snd_soc_codec_get_drvdata(codec);
+	struct wcd9xxx *core = dev_get_drvdata(codec->dev->parent);
+	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+	u32 port_id = widget->shift;
+
+	pr_debug("%s: wname %s cname %s value %u shift %d item %u\n", __func__,
+		widget->name, ucontrol->id.name, widget->value, widget->shift,
+		ucontrol->value.enumerated.item[0]);
+
+	widget->value = ucontrol->value.enumerated.item[0];
+
+	mutex_lock(&codec->mutex);
+
+	if (tabla_p->intf_type != WCD9XXX_INTERFACE_TYPE_SLIMBUS) {
+		if (widget->value > 1) {
+			dev_err(codec->dev, "%s: invalid AIF for I2C mode\n",
+				__func__);
+			mutex_unlock(&codec->mutex);
+			return -EINVAL;
+		}
+	}
+	/* value need to match the Virtual port and AIF number
+	 */
+	switch (widget->value) {
+	case 0:
+		list_del_init(&core->rx_chs[port_id].list);
+	break;
+	case 1:
+		list_add_tail(&core->rx_chs[port_id].list,
+			      &tabla_p->dai[AIF1_PB].wcd9xxx_ch_list);
+	break;
+	case 2:
+		list_add_tail(&core->rx_chs[port_id].list,
+			      &tabla_p->dai[AIF2_PB].wcd9xxx_ch_list);
+	break;
+	case 3:
+		list_add_tail(&core->rx_chs[port_id].list,
+			      &tabla_p->dai[AIF3_PB].wcd9xxx_ch_list);
+	break;
+	default:
+		pr_err("Unknown AIF %d\n", widget->value);
+		mutex_unlock(&codec->mutex);
+		return -EINVAL;
+	}
+
+	snd_soc_dapm_mux_update_power(widget, kcontrol, 1, widget->value, e);
+
+	mutex_unlock(&codec->mutex);
+	return 0;
+}
+
+static const struct soc_enum slim_rx_mux_enum =
+	SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(slim_rx_mux_text), slim_rx_mux_text);
+
+static const struct snd_kcontrol_new slim_rx_mux[TABLA_RX_MAX] = {
+	SOC_DAPM_ENUM_EXT("SLIM RX1 Mux", slim_rx_mux_enum,
+			  slim_rx_mux_get, slim_rx_mux_put),
+	SOC_DAPM_ENUM_EXT("SLIM RX2 Mux", slim_rx_mux_enum,
+			  slim_rx_mux_get, slim_rx_mux_put),
+	SOC_DAPM_ENUM_EXT("SLIM RX3 Mux", slim_rx_mux_enum,
+			  slim_rx_mux_get, slim_rx_mux_put),
+	SOC_DAPM_ENUM_EXT("SLIM RX4 Mux", slim_rx_mux_enum,
+			  slim_rx_mux_get, slim_rx_mux_put),
+	SOC_DAPM_ENUM_EXT("SLIM RX5 Mux", slim_rx_mux_enum,
+			  slim_rx_mux_get, slim_rx_mux_put),
+	SOC_DAPM_ENUM_EXT("SLIM RX6 Mux", slim_rx_mux_enum,
+			  slim_rx_mux_get, slim_rx_mux_put),
+	SOC_DAPM_ENUM_EXT("SLIM RX7 Mux", slim_rx_mux_enum,
+			  slim_rx_mux_get, slim_rx_mux_put),
+};
+
+static const struct snd_kcontrol_new aif_cap_mixer[] = {
+	SOC_SINGLE_EXT("SLIM TX1", SND_SOC_NOPM, TABLA_TX1, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX2", SND_SOC_NOPM, TABLA_TX2, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX3", SND_SOC_NOPM, TABLA_TX3, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX4", SND_SOC_NOPM, TABLA_TX4, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX5", SND_SOC_NOPM, TABLA_TX5, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX6", SND_SOC_NOPM, TABLA_TX6, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX7", SND_SOC_NOPM, TABLA_TX7, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX8", SND_SOC_NOPM, TABLA_TX8, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX9", SND_SOC_NOPM, TABLA_TX9, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX10", SND_SOC_NOPM, TABLA_TX10, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+};
+
 static void tabla_codec_enable_adc_block(struct snd_soc_codec *codec,
 					 int enable)
 {
@@ -3122,13 +3394,47 @@
 static const struct snd_soc_dapm_route audio_map[] = {
 	/* SLIMBUS Connections */
 
-	{"SLIM TX1", NULL, "SLIM TX1 MUX"},
-	{"SLIM TX1 MUX", "DEC1", "DEC1 MUX"},
+	{"AIF1 CAP", NULL, "AIF1_CAP Mixer"},
+	{"AIF2 CAP", NULL, "AIF2_CAP Mixer"},
+	{"AIF3 CAP", NULL, "AIF3_CAP Mixer"},
 
-	{"SLIM TX2", NULL, "SLIM TX2 MUX"},
+	/* SLIM_MIXER("AIF1_CAP Mixer"),*/
+	{"AIF1_CAP Mixer", "SLIM TX1", "SLIM TX1 MUX"},
+	{"AIF1_CAP Mixer", "SLIM TX2", "SLIM TX2 MUX"},
+	{"AIF1_CAP Mixer", "SLIM TX3", "SLIM TX3 MUX"},
+	{"AIF1_CAP Mixer", "SLIM TX4", "SLIM TX4 MUX"},
+	{"AIF1_CAP Mixer", "SLIM TX5", "SLIM TX5 MUX"},
+	{"AIF1_CAP Mixer", "SLIM TX6", "SLIM TX6 MUX"},
+	{"AIF1_CAP Mixer", "SLIM TX7", "SLIM TX7 MUX"},
+	{"AIF1_CAP Mixer", "SLIM TX8", "SLIM TX8 MUX"},
+	{"AIF1_CAP Mixer", "SLIM TX9", "SLIM TX9 MUX"},
+	{"AIF1_CAP Mixer", "SLIM TX10", "SLIM TX10 MUX"},
+	/* SLIM_MIXER("AIF2_CAP Mixer"),*/
+	{"AIF2_CAP Mixer", "SLIM TX1", "SLIM TX1 MUX"},
+	{"AIF2_CAP Mixer", "SLIM TX2", "SLIM TX2 MUX"},
+	{"AIF2_CAP Mixer", "SLIM TX3", "SLIM TX3 MUX"},
+	{"AIF2_CAP Mixer", "SLIM TX4", "SLIM TX4 MUX"},
+	{"AIF2_CAP Mixer", "SLIM TX5", "SLIM TX5 MUX"},
+	{"AIF2_CAP Mixer", "SLIM TX6", "SLIM TX6 MUX"},
+	{"AIF2_CAP Mixer", "SLIM TX7", "SLIM TX7 MUX"},
+	{"AIF2_CAP Mixer", "SLIM TX8", "SLIM TX8 MUX"},
+	{"AIF2_CAP Mixer", "SLIM TX9", "SLIM TX9 MUX"},
+	{"AIF2_CAP Mixer", "SLIM TX10", "SLIM TX10 MUX"},
+	/* SLIM_MIXER("AIF3_CAP Mixer"),*/
+	{"AIF3_CAP Mixer", "SLIM TX1", "SLIM TX1 MUX"},
+	{"AIF3_CAP Mixer", "SLIM TX2", "SLIM TX2 MUX"},
+	{"AIF3_CAP Mixer", "SLIM TX3", "SLIM TX3 MUX"},
+	{"AIF3_CAP Mixer", "SLIM TX4", "SLIM TX4 MUX"},
+	{"AIF3_CAP Mixer", "SLIM TX5", "SLIM TX5 MUX"},
+	{"AIF3_CAP Mixer", "SLIM TX6", "SLIM TX6 MUX"},
+	{"AIF3_CAP Mixer", "SLIM TX7", "SLIM TX7 MUX"},
+	{"AIF3_CAP Mixer", "SLIM TX8", "SLIM TX8 MUX"},
+	{"AIF3_CAP Mixer", "SLIM TX9", "SLIM TX9 MUX"},
+	{"AIF3_CAP Mixer", "SLIM TX10", "SLIM TX10 MUX"},
+
+	{"SLIM TX1 MUX", "DEC1", "DEC1 MUX"},
 	{"SLIM TX2 MUX", "DEC2", "DEC2 MUX"},
 
-	{"SLIM TX3", NULL, "SLIM TX3 MUX"},
 	{"SLIM TX3 MUX", "DEC3", "DEC3 MUX"},
 	{"SLIM TX3 MUX", "RMIX1", "RX1 MIX1"},
 	{"SLIM TX3 MUX", "RMIX2", "RX2 MIX1"},
@@ -3138,10 +3444,8 @@
 	{"SLIM TX3 MUX", "RMIX6", "RX6 MIX1"},
 	{"SLIM TX3 MUX", "RMIX7", "RX7 MIX1"},
 
-	{"SLIM TX4", NULL, "SLIM TX4 MUX"},
 	{"SLIM TX4 MUX", "DEC4", "DEC4 MUX"},
 
-	{"SLIM TX5", NULL, "SLIM TX5 MUX"},
 	{"SLIM TX5 MUX", "DEC5", "DEC5 MUX"},
 	{"SLIM TX5 MUX", "RMIX1", "RX1 MIX1"},
 	{"SLIM TX5 MUX", "RMIX2", "RX2 MIX1"},
@@ -3151,10 +3455,8 @@
 	{"SLIM TX5 MUX", "RMIX6", "RX6 MIX1"},
 	{"SLIM TX5 MUX", "RMIX7", "RX7 MIX1"},
 
-	{"SLIM TX6", NULL, "SLIM TX6 MUX"},
 	{"SLIM TX6 MUX", "DEC6", "DEC6 MUX"},
 
-	{"SLIM TX7", NULL, "SLIM TX7 MUX"},
 	{"SLIM TX7 MUX", "DEC1", "DEC1 MUX"},
 	{"SLIM TX7 MUX", "DEC2", "DEC2 MUX"},
 	{"SLIM TX7 MUX", "DEC3", "DEC3 MUX"},
@@ -3173,7 +3475,6 @@
 	{"SLIM TX7 MUX", "RMIX6", "RX6 MIX1"},
 	{"SLIM TX7 MUX", "RMIX7", "RX7 MIX1"},
 
-	{"SLIM TX8", NULL, "SLIM TX8 MUX"},
 	{"SLIM TX8 MUX", "DEC1", "DEC1 MUX"},
 	{"SLIM TX8 MUX", "DEC2", "DEC2 MUX"},
 	{"SLIM TX8 MUX", "DEC3", "DEC3 MUX"},
@@ -3185,7 +3486,6 @@
 	{"SLIM TX8 MUX", "DEC9", "DEC9 MUX"},
 	{"SLIM TX8 MUX", "DEC10", "DEC10 MUX"},
 
-	{"SLIM TX9", NULL, "SLIM TX9 MUX"},
 	{"SLIM TX9 MUX", "DEC1", "DEC1 MUX"},
 	{"SLIM TX9 MUX", "DEC2", "DEC2 MUX"},
 	{"SLIM TX9 MUX", "DEC3", "DEC3 MUX"},
@@ -3197,7 +3497,6 @@
 	{"SLIM TX9 MUX", "DEC9", "DEC9 MUX"},
 	{"SLIM TX9 MUX", "DEC10", "DEC10 MUX"},
 
-	{"SLIM TX10", NULL, "SLIM TX10 MUX"},
 	{"SLIM TX10 MUX", "DEC1", "DEC1 MUX"},
 	{"SLIM TX10 MUX", "DEC2", "DEC2 MUX"},
 	{"SLIM TX10 MUX", "DEC3", "DEC3 MUX"},
@@ -3312,6 +3611,40 @@
 	{"RX3 MIX2", NULL, "RX3 MIX2 INP1"},
 	{"RX3 MIX2", NULL, "RX3 MIX2 INP2"},
 
+	/* SLIM_MUX("AIF1_PB", "AIF1 PB"),*/
+	{"SLIM RX1 MUX", "AIF1_PB", "AIF1 PB"},
+	{"SLIM RX2 MUX", "AIF1_PB", "AIF1 PB"},
+	{"SLIM RX3 MUX", "AIF1_PB", "AIF1 PB"},
+	{"SLIM RX4 MUX", "AIF1_PB", "AIF1 PB"},
+	{"SLIM RX5 MUX", "AIF1_PB", "AIF1 PB"},
+	{"SLIM RX6 MUX", "AIF1_PB", "AIF1 PB"},
+	{"SLIM RX7 MUX", "AIF1_PB", "AIF1 PB"},
+	/* SLIM_MUX("AIF2_PB", "AIF2 PB"),*/
+	{"SLIM RX1 MUX", "AIF2_PB", "AIF2 PB"},
+	{"SLIM RX2 MUX", "AIF2_PB", "AIF2 PB"},
+	{"SLIM RX3 MUX", "AIF2_PB", "AIF2 PB"},
+	{"SLIM RX4 MUX", "AIF2_PB", "AIF2 PB"},
+	{"SLIM RX5 MUX", "AIF2_PB", "AIF2 PB"},
+	{"SLIM RX6 MUX", "AIF2_PB", "AIF2 PB"},
+	{"SLIM RX7 MUX", "AIF2_PB", "AIF2 PB"},
+	/* SLIM_MUX("AIF3_PB", "AIF3 PB"),*/
+	{"SLIM RX1 MUX", "AIF3_PB", "AIF3 PB"},
+	{"SLIM RX2 MUX", "AIF3_PB", "AIF3 PB"},
+	{"SLIM RX3 MUX", "AIF3_PB", "AIF3 PB"},
+	{"SLIM RX4 MUX", "AIF3_PB", "AIF3 PB"},
+	{"SLIM RX5 MUX", "AIF3_PB", "AIF3 PB"},
+	{"SLIM RX6 MUX", "AIF3_PB", "AIF3 PB"},
+	{"SLIM RX7 MUX", "AIF3_PB", "AIF3 PB"},
+
+	{"SLIM RX1", NULL, "SLIM RX1 MUX"},
+	{"SLIM RX2", NULL, "SLIM RX2 MUX"},
+	{"SLIM RX3", NULL, "SLIM RX3 MUX"},
+	{"SLIM RX4", NULL, "SLIM RX4 MUX"},
+	{"SLIM RX5", NULL, "SLIM RX5 MUX"},
+	{"SLIM RX6", NULL, "SLIM RX6 MUX"},
+	{"SLIM RX7", NULL, "SLIM RX7 MUX"},
+
+	/* Mixer control for output path */
 	{"RX1 MIX1 INP1", "RX1", "SLIM RX1"},
 	{"RX1 MIX1 INP1", "RX2", "SLIM RX2"},
 	{"RX1 MIX1 INP1", "RX3", "SLIM RX3"},
@@ -3661,6 +3994,10 @@
 	unsigned int value)
 {
 	int ret;
+
+	if (reg == SND_SOC_NOPM)
+		return 0;
+
 	BUG_ON(reg > TABLA_MAX_REGISTER);
 
 	if (!tabla_volatile(codec, reg)) {
@@ -3678,6 +4015,9 @@
 	unsigned int val;
 	int ret;
 
+	if (reg == SND_SOC_NOPM)
+		return 0;
+
 	BUG_ON(reg > TABLA_MAX_REGISTER);
 
 	if (!tabla_volatile(codec, reg) && tabla_readable(codec, reg) &&
@@ -3801,10 +4141,10 @@
 		return;
 
 	if (dai->id <= NUM_CODEC_DAIS) {
-		if (tabla->dai[dai->id-1].ch_mask) {
+		if (tabla->dai[dai->id].ch_mask) {
 			active = 1;
 			pr_debug("%s(): Codec DAI: chmask[%d] = 0x%x\n",
-			__func__, dai->id-1, tabla->dai[dai->id-1].ch_mask);
+				__func__, dai->id, tabla->dai[dai->id].ch_mask);
 		}
 	}
 
@@ -3925,39 +4265,20 @@
 
 {
 	struct tabla_priv *tabla = snd_soc_codec_get_drvdata(dai->codec);
-	u32 i = 0;
+	struct wcd9xxx *core = dev_get_drvdata(dai->codec->dev->parent);
+
 	if (!tx_slot && !rx_slot) {
 		pr_err("%s: Invalid\n", __func__);
 		return -EINVAL;
 	}
-	pr_debug("%s(): dai_name = %s DAI-ID %x tx_ch %d rx_ch %d\n",
-			__func__, dai->name, dai->id, tx_num, rx_num);
+	pr_debug("%s(): dai_name = %s DAI-ID %x tx_ch %d rx_ch %d\n"
+		 "tabla->intf_type %d\n",
+		 __func__, dai->name, dai->id, tx_num, rx_num,
+		 tabla->intf_type);
 
-	if (dai->id == AIF1_PB || dai->id == AIF2_PB || dai->id == AIF3_PB) {
-		for (i = 0; i < rx_num; i++) {
-			tabla->dai[dai->id - 1].ch_num[i]  = rx_slot[i];
-			tabla->dai[dai->id - 1].ch_act = 0;
-			tabla->dai[dai->id - 1].ch_tot = rx_num;
-		}
-	} else if (dai->id == AIF1_CAP || dai->id == AIF2_CAP ||
-		   dai->id == AIF3_CAP) {
-		tabla->dai[dai->id - 1].ch_tot = tx_num;
-		/* All channels are already active.
-		 * do not reset ch_act flag
-		 */
-		if ((tabla->dai[dai->id - 1].ch_tot != 0)
-			&& (tabla->dai[dai->id - 1].ch_act ==
-			tabla->dai[dai->id - 1].ch_tot)) {
-			pr_info("%s: ch_act = %d, ch_tot = %d\n", __func__,
-				tabla->dai[dai->id - 1].ch_act,
-				tabla->dai[dai->id - 1].ch_tot);
-			return 0;
-		}
-
-		tabla->dai[dai->id - 1].ch_act = 0;
-		for (i = 0; i < tx_num; i++)
-			tabla->dai[dai->id - 1].ch_num[i]  = tx_slot[i];
-	}
+	if (tabla->intf_type == WCD9XXX_INTERFACE_TYPE_SLIMBUS)
+		wcd9xxx_init_slimslave(core, core->slim->laddr,
+				       tx_num, tx_slot, rx_num, rx_slot);
 	return 0;
 }
 
@@ -3966,189 +4287,97 @@
 				unsigned int *rx_num, unsigned int *rx_slot)
 
 {
-	struct wcd9xxx *tabla = dev_get_drvdata(dai->codec->control_data);
+	struct tabla_priv *tabla_p = snd_soc_codec_get_drvdata(dai->codec);
+	u32 i = 0;
+	struct wcd9xxx_ch *ch;
 
-	u32 cnt = 0;
-	u32 tx_ch[SLIM_MAX_TX_PORTS];
-	u32 rx_ch[SLIM_MAX_RX_PORTS];
+	switch (dai->id) {
+	case AIF1_PB:
+	case AIF2_PB:
+	case AIF3_PB:
+		if (!rx_slot || !rx_num) {
+			pr_err("%s: Invalid rx_slot %d or rx_num %d\n",
+				 __func__, (u32) rx_slot, (u32) rx_num);
+			return -EINVAL;
+		}
+		list_for_each_entry(ch, &tabla_p->dai[dai->id].wcd9xxx_ch_list,
+				    list) {
+			rx_slot[i++] = ch->ch_num;
+		}
+		*rx_num = i;
+		break;
+	case AIF1_CAP:
+	case AIF2_CAP:
+	case AIF3_CAP:
+		if (!tx_slot || !tx_num) {
+			pr_err("%s: Invalid tx_slot %d or tx_num %d\n",
+				 __func__, (u32) tx_slot, (u32) tx_num);
+			return -EINVAL;
+		}
+		list_for_each_entry(ch, &tabla_p->dai[dai->id].wcd9xxx_ch_list,
+				    list) {
+			tx_slot[i++] = ch->ch_num;
+		}
+		*tx_num = i;
+		break;
 
-	if (!rx_slot && !tx_slot) {
-		pr_err("%s: Invalid\n", __func__);
-		return -EINVAL;
+	default:
+		pr_err("%s: Invalid DAI ID %x\n", __func__, dai->id);
+		break;
 	}
-
-	/* for virtual port, codec driver needs to do
-	 * housekeeping, for now should be ok
-	 */
-	wcd9xxx_get_channel(tabla, rx_ch, tx_ch);
-	if (dai->id == AIF1_PB) {
-		*rx_num = tabla_dai[dai->id - 1].playback.channels_max;
-		while (cnt < *rx_num) {
-			rx_slot[cnt] = rx_ch[cnt];
-			cnt++;
-		}
-	} else if (dai->id == AIF1_CAP) {
-		*tx_num = tabla_dai[dai->id - 1].capture.channels_max;
-		while (cnt < *tx_num) {
-			tx_slot[cnt] = tx_ch[6 + cnt];
-			cnt++;
-		}
-	} else if (dai->id == AIF2_PB) {
-		*rx_num = tabla_dai[dai->id - 1].playback.channels_max;
-		while (cnt < *rx_num) {
-			rx_slot[cnt] = rx_ch[5 + cnt];
-			cnt++;
-		}
-	} else if (dai->id == AIF2_CAP) {
-		*tx_num = tabla_dai[dai->id - 1].capture.channels_max;
-		tx_slot[0] = tx_ch[cnt];
-		tx_slot[1] = tx_ch[1 + cnt];
-		tx_slot[2] = tx_ch[5 + cnt];
-		tx_slot[3] = tx_ch[3 + cnt];
-
-	} else if (dai->id == AIF3_PB) {
-		*rx_num = tabla_dai[dai->id - 1].playback.channels_max;
-		rx_slot[0] = rx_ch[3];
-		rx_slot[1] = rx_ch[4];
-
-	} else if (dai->id == AIF3_CAP) {
-		*tx_num = tabla_dai[dai->id - 1].capture.channels_max;
-		tx_slot[cnt] = tx_ch[2 + cnt];
-		tx_slot[cnt + 1] = tx_ch[4 + cnt];
-	}
-	pr_debug("%s(): dai_name = %s DAI-ID %x tx_ch %d rx_ch %d\n",
-			__func__, dai->name, dai->id, *tx_num, *rx_num);
-
-
 	return 0;
 }
 
 
-static struct snd_soc_dapm_widget tabla_dapm_aif_in_widgets[] = {
-
-	SND_SOC_DAPM_AIF_IN_E("SLIM RX1", "AIF1 Playback", 0, SND_SOC_NOPM, 1,
-				0, tabla_codec_enable_slimrx,
-				SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
-
-	SND_SOC_DAPM_AIF_IN_E("SLIM RX2", "AIF1 Playback", 0, SND_SOC_NOPM, 2,
-				0, tabla_codec_enable_slimrx,
-				SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
-
-	SND_SOC_DAPM_AIF_IN_E("SLIM RX3", "AIF1 Playback", 0, SND_SOC_NOPM, 3,
-				0, tabla_codec_enable_slimrx,
-				SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
-
-	SND_SOC_DAPM_AIF_IN_E("SLIM RX4", "AIF3 Playback", 0, SND_SOC_NOPM, 4,
-				0, tabla_codec_enable_slimrx,
-				SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
-
-	SND_SOC_DAPM_AIF_IN_E("SLIM RX5", "AIF3 Playback", 0, SND_SOC_NOPM, 5,
-				0, tabla_codec_enable_slimrx,
-				SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
-
-	SND_SOC_DAPM_AIF_IN_E("SLIM RX6", "AIF2 Playback", 0, SND_SOC_NOPM, 6,
-				0, tabla_codec_enable_slimrx,
-				SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
-
-	SND_SOC_DAPM_AIF_IN_E("SLIM RX7", "AIF2 Playback", 0, SND_SOC_NOPM, 7,
-				0, tabla_codec_enable_slimrx,
-				SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
-};
-
-static struct snd_soc_dapm_widget tabla_dapm_aif_out_widgets[] = {
-
-	SND_SOC_DAPM_AIF_OUT_E("SLIM TX1", "AIF2 Capture", 0, SND_SOC_NOPM, 1,
-				0, tabla_codec_enable_slimtx,
-				SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
-
-	SND_SOC_DAPM_AIF_OUT_E("SLIM TX2", "AIF2 Capture", 0, SND_SOC_NOPM, 2,
-				0, tabla_codec_enable_slimtx,
-				SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
-
-	SND_SOC_DAPM_AIF_OUT_E("SLIM TX3", "AIF3 Capture", 0, SND_SOC_NOPM, 3,
-				0, tabla_codec_enable_slimtx,
-				SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
-
-	SND_SOC_DAPM_AIF_OUT_E("SLIM TX4", "AIF2 Capture", 0, SND_SOC_NOPM, 4,
-				0, tabla_codec_enable_slimtx,
-				SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
-
-	SND_SOC_DAPM_AIF_OUT_E("SLIM TX5", "AIF3 Capture", 0, SND_SOC_NOPM, 5,
-				0, tabla_codec_enable_slimtx,
-				SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
-
-	SND_SOC_DAPM_AIF_OUT_E("SLIM TX6", "AIF2 Capture", 0, SND_SOC_NOPM, 6,
-				0, tabla_codec_enable_slimtx,
-				SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
-
-	SND_SOC_DAPM_AIF_OUT_E("SLIM TX7", "AIF1 Capture", 0, SND_SOC_NOPM, 7,
-				0, tabla_codec_enable_slimtx,
-				SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
-
-	SND_SOC_DAPM_AIF_OUT_E("SLIM TX8", "AIF1 Capture", 0, SND_SOC_NOPM, 8,
-				0, tabla_codec_enable_slimtx,
-				SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
-
-	SND_SOC_DAPM_AIF_OUT_E("SLIM TX9", "AIF1 Capture", 0, SND_SOC_NOPM, 9,
-				0, tabla_codec_enable_slimtx,
-				SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
-
-	SND_SOC_DAPM_AIF_OUT_E("SLIM TX10", "AIF1 Capture", 0, SND_SOC_NOPM, 10,
-				0, tabla_codec_enable_slimtx,
-				SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
-};
-
 static int tabla_set_interpolator_rate(struct snd_soc_dai *dai,
-	u8 rx_fs_rate_reg_val, u32 compander_fs, u32 sample_rate)
+				       u8 rx_fs_rate_reg_val,
+				       u32 compander_fs,
+				       u32 sample_rate)
 {
-	u32 i, j;
+	u32 j;
 	u8 rx_mix1_inp;
 	u16 rx_mix_1_reg_1, rx_mix_1_reg_2;
 	u16 rx_fs_reg;
 	u8 rx_mix_1_reg_1_val, rx_mix_1_reg_2_val;
 	struct snd_soc_codec *codec = dai->codec;
+	struct wcd9xxx_ch *ch;
 	struct tabla_priv *tabla = snd_soc_codec_get_drvdata(codec);
-	struct snd_soc_dapm_widget *w = tabla_dapm_aif_in_widgets;
 
-	for (i = 0; i < ARRAY_SIZE(tabla_dapm_aif_in_widgets); i++) {
+	list_for_each_entry(ch, &tabla->dai[dai->id].wcd9xxx_ch_list, list) {
 
-		if (strncmp(dai->driver->playback.stream_name, w[i].sname, 13))
-			continue;
+		rx_mix1_inp = ch->port - RX_MIX1_INP_SEL_RX1;
 
-		rx_mix1_inp = w[i].shift + 4;
-
-		if ((rx_mix1_inp < 0x5) || (rx_mix1_inp > 0xB)) {
-
-			pr_err("%s: Invalid SLIM RX%u port.  widget = %s\n",
-				__func__,  rx_mix1_inp  - 4 , w[i].name);
+		if ((rx_mix1_inp < RX_MIX1_INP_SEL_RX1) ||
+			(rx_mix1_inp > RX_MIX1_INP_SEL_RX7)) {
+			pr_err("%s: Invalid TABLA_RX%u port. Dai ID is %d\n",
+				__func__,  rx_mix1_inp - 5 , dai->id);
 			return -EINVAL;
 		}
 
 		rx_mix_1_reg_1 = TABLA_A_CDC_CONN_RX1_B1_CTL;
 
 		for (j = 0; j < NUM_INTERPOLATORS; j++) {
-
 			rx_mix_1_reg_2 = rx_mix_1_reg_1 + 1;
 
 			rx_mix_1_reg_1_val = snd_soc_read(codec,
-					rx_mix_1_reg_1);
+							  rx_mix_1_reg_1);
 			rx_mix_1_reg_2_val = snd_soc_read(codec,
-					rx_mix_1_reg_2);
+							  rx_mix_1_reg_2);
 
 			if (((rx_mix_1_reg_1_val & 0x0F) == rx_mix1_inp) ||
-			   (((rx_mix_1_reg_1_val >> 4) & 0x0F) == rx_mix1_inp)
-			   || ((rx_mix_1_reg_2_val & 0x0F) == rx_mix1_inp)) {
+			(((rx_mix_1_reg_1_val >> 4) & 0x0F) == rx_mix1_inp) ||
+			((rx_mix_1_reg_2_val & 0x0F) == rx_mix1_inp)) {
 
 				rx_fs_reg = TABLA_A_CDC_RX1_B5_CTL + 8 * j;
 
-				pr_debug("%s: %s connected to RX%u\n", __func__,
-					w[i].name, j + 1);
+				pr_debug("%s: AIF_PB DAI(%d) connected to RX%u\n",
+					__func__, dai->id, j + 1);
 
 				pr_debug("%s: set RX%u sample rate to %u\n",
 					__func__, j + 1, sample_rate);
 
 				snd_soc_update_bits(codec, rx_fs_reg,
-						0xE0, rx_fs_rate_reg_val);
+						    0xE0, rx_fs_rate_reg_val);
 
 				if (comp_rx_path[j] < COMPANDER_MAX)
 					tabla->comp_fs[comp_rx_path[j]]
@@ -4164,26 +4393,26 @@
 }
 
 static int tabla_set_decimator_rate(struct snd_soc_dai *dai,
-	u8 tx_fs_rate_reg_val, u32 sample_rate)
+				    u8 tx_fs_rate_reg_val,
+				    u32 sample_rate)
 {
 	struct snd_soc_codec *codec = dai->codec;
-	struct snd_soc_dapm_widget *w = tabla_dapm_aif_out_widgets;
-
-	u32 i, tx_port;
+	struct wcd9xxx_ch *ch;
+	struct tabla_priv *tabla = snd_soc_codec_get_drvdata(codec);
+	u32 tx_port;
 	u16 tx_port_reg, tx_fs_reg;
 	u8 tx_port_reg_val;
 	s8 decimator;
 
-	for (i = 0; i < ARRAY_SIZE(tabla_dapm_aif_out_widgets); i++) {
+	list_for_each_entry(ch, &tabla->dai[dai->id].wcd9xxx_ch_list, list) {
 
-		if (strncmp(dai->driver->capture.stream_name, w[i].sname, 12))
-			continue;
-
-		tx_port = w[i].shift;
+		tx_port = ch->port + 1;
+		pr_debug("%s: dai->id = %d, tx_port = %d",
+			__func__, dai->id, tx_port);
 
 		if ((tx_port < 1) || (tx_port > NUM_DECIMATORS)) {
-			pr_err("%s: Invalid SLIM TX%u port.  widget = %s\n",
-				__func__, tx_port, w[i].name);
+			pr_err("%s: Invalid SLIM TX%u port. DAI ID is %d\n",
+				__func__, tx_port, dai->id);
 			return -EINVAL;
 		}
 
@@ -4212,38 +4441,38 @@
 		if (decimator) { /* SLIM_TX port has a DEC as input */
 
 			tx_fs_reg = TABLA_A_CDC_TX1_CLK_FS_CTL +
-				8 * (decimator - 1);
+				    8 * (decimator - 1);
 
 			pr_debug("%s: set DEC%u (-> SLIM_TX%u) rate to %u\n",
 				__func__, decimator, tx_port, sample_rate);
 
 			snd_soc_update_bits(codec, tx_fs_reg, 0x07,
-					tx_fs_rate_reg_val);
+					    tx_fs_rate_reg_val);
 
 		} else {
 			if ((tx_port_reg_val >= 0x1) &&
-					(tx_port_reg_val <= 0x7)) {
+			    (tx_port_reg_val <= 0x7)) {
 
 				pr_debug("%s: RMIX%u going to SLIM TX%u\n",
 					__func__, tx_port_reg_val, tx_port);
 
 			} else if  ((tx_port_reg_val >= 0x8) &&
-					(tx_port_reg_val <= 0x11)) {
+				    (tx_port_reg_val <= 0x11)) {
 
 				pr_err("%s: ERROR: Should not be here\n",
-						__func__);
-				pr_err("%s: ERROR: DEC connected to SLIM TX%u\n"
-						, __func__, tx_port);
+					__func__);
+				pr_err("%s: ERROR: DEC connected to SLIM TX%u\n",
+					__func__, tx_port);
 				return -EINVAL;
 
 			} else if (tx_port_reg_val == 0) {
 				pr_debug("%s: no signal to SLIM TX%u\n",
-						__func__, tx_port);
+					__func__, tx_port);
 			} else {
-				pr_err("%s: ERROR: wrong signal to SLIM TX%u\n"
-						, __func__, tx_port);
-				pr_err("%s: ERROR: wrong signal = %u\n"
-						, __func__, tx_port_reg_val);
+				pr_err("%s: ERROR: wrong signal to SLIM TX%u\n",
+					__func__, tx_port);
+				pr_err("%s: ERROR: wrong signal = %u\n",
+					__func__, tx_port_reg_val);
 				return -EINVAL;
 			}
 		}
@@ -4252,8 +4481,8 @@
 }
 
 static int tabla_hw_params(struct snd_pcm_substream *substream,
-		struct snd_pcm_hw_params *params,
-		struct snd_soc_dai *dai)
+			   struct snd_pcm_hw_params *params,
+			   struct snd_soc_dai *dai)
 {
 	struct snd_soc_codec *codec = dai->codec;
 	struct tabla_priv *tabla = snd_soc_codec_get_drvdata(dai->codec);
@@ -4262,8 +4491,8 @@
 	int ret;
 
 	pr_debug("%s: dai_name = %s DAI-ID %x rate %d num_ch %d\n", __func__,
-			dai->name, dai->id, params_rate(params),
-			params_channels(params));
+		 dai->name, dai->id, params_rate(params),
+		 params_channels(params));
 
 	switch (params_rate(params)) {
 	case 8000:
@@ -4298,7 +4527,7 @@
 		break;
 	default:
 		pr_err("%s: Invalid sampling rate %d\n", __func__,
-				params_rate(params));
+			params_rate(params));
 		return -EINVAL;
 	}
 
@@ -4306,10 +4535,10 @@
 	case SNDRV_PCM_STREAM_CAPTURE:
 
 		ret = tabla_set_decimator_rate(dai, tx_fs_rate_reg_val,
-				params_rate(params));
+					       params_rate(params));
 		if (ret < 0) {
 			pr_err("%s: set decimator rate failed %d\n", __func__,
-					ret);
+			       ret);
 			return ret;
 		}
 
@@ -4324,24 +4553,34 @@
 					TABLA_A_CDC_CLK_TX_I2S_CTL, 0x20, 0x00);
 				break;
 			default:
-				pr_err("%s: invalid TX format %u\n", __func__,
-						params_format(params));
+				pr_err("%s: Invalid format %d\n", __func__,
+					params_format(params));
 				return -EINVAL;
 			}
 			snd_soc_update_bits(codec, TABLA_A_CDC_CLK_TX_I2S_CTL,
-					0x07, tx_fs_rate_reg_val);
+					    0x07, tx_fs_rate_reg_val);
 		} else {
-			tabla->dai[dai->id - 1].rate   = params_rate(params);
+			switch (params_format(params)) {
+			case SNDRV_PCM_FORMAT_S16_LE:
+				tabla->dai[dai->id].bit_width = 16;
+				break;
+			default:
+				pr_err("%s: Invalid TX format %d\n", __func__,
+					params_format(params));
+				return -EINVAL;
+			}
+			tabla->dai[dai->id].rate   = params_rate(params);
 		}
 		break;
 
 	case SNDRV_PCM_STREAM_PLAYBACK:
 
 		ret = tabla_set_interpolator_rate(dai, rx_fs_rate_reg_val,
-				compander_fs, params_rate(params));
+						  compander_fs,
+						  params_rate(params));
 		if (ret < 0) {
 			pr_err("%s: set decimator rate failed %d\n", __func__,
-					ret);
+			       ret);
 			return ret;
 		}
 
@@ -4356,20 +4595,29 @@
 					TABLA_A_CDC_CLK_RX_I2S_CTL, 0x20, 0x00);
 				break;
 			default:
-				pr_err("%s: invalid RX format %u\n", __func__,
-						params_format(params));
+				pr_err("%s: Invalid RX format %d\n", __func__,
+					params_format(params));
 				return -EINVAL;
 			}
 			snd_soc_update_bits(codec, TABLA_A_CDC_CLK_RX_I2S_CTL,
 					0x03, (rx_fs_rate_reg_val >> 0x05));
 		} else {
-			tabla->dai[dai->id - 1].rate   = params_rate(params);
+			switch (params_format(params)) {
+			case SNDRV_PCM_FORMAT_S16_LE:
+				tabla->dai[dai->id].bit_width = 16;
+				break;
+			default:
+				pr_err("%s: Invalid format %d\n", __func__,
+					params_format(params));
+				return -EINVAL;
+			}
+			tabla->dai[dai->id].rate = params_rate(params);
 		}
 		break;
 
 	default:
 		pr_err("%s: Invalid stream type %d\n", __func__,
-				substream->stream);
+			substream->stream);
 		return -EINVAL;
 	}
 	return 0;
@@ -4475,7 +4723,7 @@
 static struct snd_soc_dai_driver tabla_i2s_dai[] = {
 	{
 		.name = "tabla_i2s_rx1",
-		.id = 1,
+		.id = AIF1_PB,
 		.playback = {
 			.stream_name = "AIF1 Playback",
 			.rates = WCD9310_RATES,
@@ -4489,7 +4737,7 @@
 	},
 	{
 		.name = "tabla_i2s_tx1",
-		.id = 2,
+		.id = AIF1_CAP,
 		.capture = {
 			.stream_name = "AIF1 Capture",
 			.rates = WCD9310_RATES,
@@ -4504,15 +4752,16 @@
 };
 
 static int tabla_codec_enable_chmask(struct tabla_priv *tabla_p,
-	int event, int index)
+				     int event, int index)
 {
 	int  ret = 0;
-	u32 k = 0;
+	struct wcd9xxx_ch *ch;
+
 	switch (event) {
 	case SND_SOC_DAPM_POST_PMU:
-		for (k = 0; k < tabla_p->dai[index].ch_tot; k++) {
-			ret = wcd9xxx_get_slave_port(
-					tabla_p->dai[index].ch_num[k]);
+		list_for_each_entry(ch,
+			&tabla_p->dai[index].wcd9xxx_ch_list, list) {
+			ret = wcd9xxx_get_slave_port(ch->ch_num);
 			if (ret < 0) {
 				pr_err("%s: Invalid slave port ID: %d\n",
 					__func__, ret);
@@ -4521,7 +4770,6 @@
 			}
 			tabla_p->dai[index].ch_mask |= 1 << ret;
 		}
-		ret = 0;
 		break;
 	case SND_SOC_DAPM_POST_PMD:
 		ret = wait_event_timeout(tabla_p->dai[index].dai_wait,
@@ -4531,191 +4779,134 @@
 			pr_err("%s: Slim close tx/rx wait timeout\n",
 				__func__);
 			ret = -EINVAL;
-		} else
-			ret = 0;
+		}
 		break;
 	}
 	return ret;
 }
 
 static int tabla_codec_enable_slimrx(struct snd_soc_dapm_widget *w,
-	struct snd_kcontrol *kcontrol, int event)
+				     struct snd_kcontrol *kcontrol,
+				     int event)
 {
-	struct wcd9xxx *tabla;
+	struct wcd9xxx *core;
 	struct snd_soc_codec *codec = w->codec;
 	struct tabla_priv *tabla_p = snd_soc_codec_get_drvdata(codec);
-	u32  j = 0;
-	int  ret = 0;
-	codec->control_data = dev_get_drvdata(codec->dev->parent);
-	tabla = codec->control_data;
+	u32  ret = 0;
+	struct wcd9xxx_codec_dai_data *dai;
+
+	core = dev_get_drvdata(codec->dev->parent);
+
+	pr_debug("%s: event called! codec name %s num_dai %d\n"
+		"stream name %s event %d\n",
+		__func__, w->codec->name, w->codec->num_dai,
+		w->sname, event);
 
 	/* Execute the callback only if interface type is slimbus */
 	if (tabla_p->intf_type != WCD9XXX_INTERFACE_TYPE_SLIMBUS) {
-		if (event == SND_SOC_DAPM_POST_PMD && (tabla != NULL) &&
-		    (tabla->dev != NULL) &&
-		    (tabla->dev->parent != NULL)) {
-			pm_runtime_mark_last_busy(tabla->dev->parent);
-			pm_runtime_put(tabla->dev->parent);
+		if (event == SND_SOC_DAPM_POST_PMD && (core != NULL) &&
+		    (core->dev != NULL) &&
+		    (core->dev->parent != NULL)) {
+			pm_runtime_mark_last_busy(core->dev->parent);
+			pm_runtime_put(core->dev->parent);
 		}
 		return 0;
 	}
-
-	pr_debug("%s: %s %d\n", __func__, w->name, event);
+	pr_debug("%s: w->name %s w->shift %d event %d\n",
+		__func__, w->name, w->shift, event);
+	dai = &tabla_p->dai[w->shift];
 
 	switch (event) {
 	case SND_SOC_DAPM_POST_PMU:
-		for (j = 0; j < ARRAY_SIZE(tabla_dai); j++) {
-			if ((tabla_dai[j].id == AIF1_CAP) ||
-			    (tabla_dai[j].id == AIF2_CAP) ||
-			    (tabla_dai[j].id == AIF3_CAP))
-				continue;
-			if (!strncmp(w->sname,
-				tabla_dai[j].playback.stream_name, 13)) {
-				++tabla_p->dai[j].ch_act;
-				break;
-			}
-		}
-		if (tabla_p->dai[j].ch_act == tabla_p->dai[j].ch_tot) {
-			ret = tabla_codec_enable_chmask(tabla_p,
-							SND_SOC_DAPM_POST_PMU,
-							j);
-			ret = wcd9xxx_cfg_slim_sch_rx(tabla,
-					tabla_p->dai[j].ch_num,
-					tabla_p->dai[j].ch_tot,
-					tabla_p->dai[j].rate);
-		}
+		ret = tabla_codec_enable_chmask(tabla_p, SND_SOC_DAPM_POST_PMU,
+						w->shift);
+		ret = wcd9xxx_cfg_slim_sch_rx(core, &dai->wcd9xxx_ch_list,
+					      dai->rate, dai->bit_width,
+					      &dai->grph);
 		break;
 	case SND_SOC_DAPM_POST_PMD:
-		for (j = 0; j < ARRAY_SIZE(tabla_dai); j++) {
-			if ((tabla_dai[j].id == AIF1_CAP) ||
-			    (tabla_dai[j].id == AIF2_CAP) ||
-			    (tabla_dai[j].id == AIF3_CAP))
-				continue;
-			if (!strncmp(w->sname,
-				tabla_dai[j].playback.stream_name, 13)) {
-				if (tabla_p->dai[j].ch_act)
-					--tabla_p->dai[j].ch_act;
-				break;
-			}
+		ret = wcd9xxx_close_slim_sch_rx(core,
+						&dai->wcd9xxx_ch_list,
+						dai->grph);
+		ret = tabla_codec_enable_chmask(tabla_p, SND_SOC_DAPM_POST_PMD,
+						w->shift);
+		if (ret < 0) {
+			ret = wcd9xxx_disconnect_port(core,
+						      &dai->wcd9xxx_ch_list,
+						      dai->grph);
+			pr_info("%s: Disconnect RX port, ret = %d\n",
+				__func__, ret);
 		}
-		if (!tabla_p->dai[j].ch_act) {
-			ret = wcd9xxx_close_slim_sch_rx(tabla,
-						tabla_p->dai[j].ch_num,
-						tabla_p->dai[j].ch_tot);
-			ret = tabla_codec_enable_chmask(tabla_p,
-							SND_SOC_DAPM_POST_PMD,
-							j);
-			if (ret < 0) {
-				ret = wcd9xxx_disconnect_port(tabla,
-							tabla_p->dai[j].ch_num,
-							tabla_p->dai[j].ch_tot,
-							1);
-				pr_info("%s: Disconnect RX port ret = %d\n",
-					__func__, ret);
-			}
-			tabla_p->dai[j].rate = 0;
-			memset(tabla_p->dai[j].ch_num, 0, (sizeof(u32)*
-				tabla_p->dai[j].ch_tot));
-			tabla_p->dai[j].ch_tot = 0;
-
-			if ((tabla != NULL) &&
-			    (tabla->dev != NULL) &&
-			    (tabla->dev->parent != NULL)) {
-				pm_runtime_mark_last_busy(tabla->dev->parent);
-				pm_runtime_put(tabla->dev->parent);
-			}
+		if ((core != NULL) &&
+			(core->dev != NULL) &&
+			(core->dev->parent != NULL)) {
+			pm_runtime_mark_last_busy(core->dev->parent);
+			pm_runtime_put(core->dev->parent);
 		}
+		break;
 	}
+
 	return ret;
 }
 
 static int tabla_codec_enable_slimtx(struct snd_soc_dapm_widget *w,
-	struct snd_kcontrol *kcontrol, int event)
+				     struct snd_kcontrol *kcontrol,
+				     int event)
 {
-	struct wcd9xxx *tabla;
+	struct wcd9xxx *core;
 	struct snd_soc_codec *codec = w->codec;
 	struct tabla_priv *tabla_p = snd_soc_codec_get_drvdata(codec);
-	/* index to the DAI ID, for now hardcoding */
-	u32  j = 0;
-	int  ret = 0;
+	u32  ret = 0;
+	struct wcd9xxx_codec_dai_data *dai;
 
-	codec->control_data = dev_get_drvdata(codec->dev->parent);
-	tabla = codec->control_data;
+	core = dev_get_drvdata(codec->dev->parent);
+
+	pr_debug("%s: event called! codec name %s num_dai %d\n"
+		 "stream name %s\n", __func__, w->codec->name,
+		 w->codec->num_dai, w->sname);
 
 	/* Execute the callback only if interface type is slimbus */
 	if (tabla_p->intf_type != WCD9XXX_INTERFACE_TYPE_SLIMBUS) {
-		if (event == SND_SOC_DAPM_POST_PMD && (tabla != NULL) &&
-		    (tabla->dev != NULL) &&
-		    (tabla->dev->parent != NULL)) {
-			pm_runtime_mark_last_busy(tabla->dev->parent);
-			pm_runtime_put(tabla->dev->parent);
+		if (event == SND_SOC_DAPM_POST_PMD && (core != NULL) &&
+		    (core->dev != NULL) &&
+		    (core->dev->parent != NULL)) {
+			pm_runtime_mark_last_busy(core->dev->parent);
+			pm_runtime_put(core->dev->parent);
 		}
 		return 0;
 	}
 
 	pr_debug("%s(): %s %d\n", __func__, w->name, event);
 
+	dai = &tabla_p->dai[w->shift];
 	switch (event) {
 	case SND_SOC_DAPM_POST_PMU:
-		for (j = 0; j < ARRAY_SIZE(tabla_dai); j++) {
-			if (tabla_dai[j].id == AIF1_PB ||
-				tabla_dai[j].id == AIF2_PB ||
-				tabla_dai[j].id == AIF3_PB)
-				continue;
-			if (!strncmp(w->sname,
-				tabla_dai[j].capture.stream_name, 13)) {
-				++tabla_p->dai[j].ch_act;
-				break;
-			}
-		}
-		if (tabla_p->dai[j].ch_act == tabla_p->dai[j].ch_tot) {
-			ret = tabla_codec_enable_chmask(tabla_p,
-							SND_SOC_DAPM_POST_PMU,
-							j);
-			ret = wcd9xxx_cfg_slim_sch_tx(tabla,
-						tabla_p->dai[j].ch_num,
-						tabla_p->dai[j].ch_tot,
-						tabla_p->dai[j].rate);
-		}
+		ret = tabla_codec_enable_chmask(tabla_p, SND_SOC_DAPM_POST_PMU,
+						w->shift);
+		ret = wcd9xxx_cfg_slim_sch_tx(core, &dai->wcd9xxx_ch_list,
+					      dai->rate,
+					      dai->bit_width,
+					      &dai->grph);
 		break;
 	case SND_SOC_DAPM_POST_PMD:
-		for (j = 0; j < ARRAY_SIZE(tabla_dai); j++) {
-			if (tabla_dai[j].id == AIF1_PB ||
-				tabla_dai[j].id == AIF2_PB ||
-				tabla_dai[j].id == AIF3_PB)
-				continue;
-			if (!strncmp(w->sname,
-				tabla_dai[j].capture.stream_name, 13)) {
-				--tabla_p->dai[j].ch_act;
-				break;
-			}
+		ret = wcd9xxx_close_slim_sch_tx(core, &dai->wcd9xxx_ch_list,
+						dai->grph);
+		ret = tabla_codec_enable_chmask(tabla_p, SND_SOC_DAPM_POST_PMD,
+						w->shift);
+		if (ret < 0) {
+			ret = wcd9xxx_disconnect_port(core,
+						      &dai->wcd9xxx_ch_list,
+						      dai->grph);
+			pr_info("%s: Disconnect TX port, ret = %d\n",
+				__func__, ret);
 		}
-		if (!tabla_p->dai[j].ch_act) {
-			ret = wcd9xxx_close_slim_sch_tx(tabla,
-						tabla_p->dai[j].ch_num,
-						tabla_p->dai[j].ch_tot);
-			ret = tabla_codec_enable_chmask(tabla_p,
-						SND_SOC_DAPM_POST_PMD,
-						j);
-			if (ret < 0) {
-				ret = wcd9xxx_disconnect_port(tabla,
-						tabla_p->dai[j].ch_num,
-						tabla_p->dai[j].ch_tot, 0);
-				pr_info("%s: Disconnect TX port, ret = %d\n",
-					__func__, ret);
-			}
-
-			tabla_p->dai[j].rate = 0;
-			memset(tabla_p->dai[j].ch_num, 0, (sizeof(u32)*
-					tabla_p->dai[j].ch_tot));
-			tabla_p->dai[j].ch_tot = 0;
-			if ((tabla != NULL) &&
-			    (tabla->dev != NULL) &&
-			    (tabla->dev->parent != NULL)) {
-				pm_runtime_mark_last_busy(tabla->dev->parent);
-				pm_runtime_put(tabla->dev->parent);
-			}
+		if ((core != NULL) &&
+			(core->dev != NULL) &&
+			(core->dev->parent != NULL)) {
+			pm_runtime_mark_last_busy(core->dev->parent);
+			pm_runtime_put(core->dev->parent);
 		}
+		break;
 	}
 	return ret;
 }
@@ -4734,6 +4925,38 @@
 	SND_SOC_DAPM_MIXER("DAC1", SND_SOC_NOPM, 0, 0, dac1_switch,
 		ARRAY_SIZE(dac1_switch)),
 
+	SND_SOC_DAPM_AIF_IN_E("AIF1 PB", "AIF1 Playback", 0, SND_SOC_NOPM,
+				AIF1_PB, 0, tabla_codec_enable_slimrx,
+				SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_AIF_IN_E("AIF2 PB", "AIF2 Playback", 0, SND_SOC_NOPM,
+				AIF2_PB, 0, tabla_codec_enable_slimrx,
+				SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_AIF_IN_E("AIF3 PB", "AIF3 Playback", 0, SND_SOC_NOPM,
+				AIF3_PB, 0, tabla_codec_enable_slimrx,
+				SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_MUX("SLIM RX1 MUX", SND_SOC_NOPM, TABLA_RX1, 0,
+				&slim_rx_mux[TABLA_RX1]),
+	SND_SOC_DAPM_MUX("SLIM RX2 MUX", SND_SOC_NOPM, TABLA_RX2, 0,
+				&slim_rx_mux[TABLA_RX2]),
+	SND_SOC_DAPM_MUX("SLIM RX3 MUX", SND_SOC_NOPM, TABLA_RX3, 0,
+				&slim_rx_mux[TABLA_RX3]),
+	SND_SOC_DAPM_MUX("SLIM RX4 MUX", SND_SOC_NOPM, TABLA_RX4, 0,
+				&slim_rx_mux[TABLA_RX4]),
+	SND_SOC_DAPM_MUX("SLIM RX5 MUX", SND_SOC_NOPM, TABLA_RX5, 0,
+				&slim_rx_mux[TABLA_RX5]),
+	SND_SOC_DAPM_MUX("SLIM RX6 MUX", SND_SOC_NOPM, TABLA_RX6, 0,
+				&slim_rx_mux[TABLA_RX6]),
+	SND_SOC_DAPM_MUX("SLIM RX7 MUX", SND_SOC_NOPM, TABLA_RX7, 0,
+				&slim_rx_mux[TABLA_RX7]),
+
+	SND_SOC_DAPM_MIXER("SLIM RX1", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("SLIM RX2", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("SLIM RX3", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("SLIM RX4", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("SLIM RX5", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("SLIM RX6", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("SLIM RX7", SND_SOC_NOPM, 0, 0, NULL, 0),
 	/* Headphone */
 	SND_SOC_DAPM_OUTPUT("HEADPHONE"),
 	SND_SOC_DAPM_PGA_E("HPHL", TABLA_A_RX_HPH_CNP_EN, 5, 0, NULL, 0,
@@ -5013,16 +5236,47 @@
 		tabla_codec_enable_adc, SND_SOC_DAPM_PRE_PMU |
 		SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
 
-	SND_SOC_DAPM_MUX("SLIM TX1 MUX", SND_SOC_NOPM, 0, 0, &sb_tx1_mux),
-	SND_SOC_DAPM_MUX("SLIM TX2 MUX", SND_SOC_NOPM, 0, 0, &sb_tx2_mux),
-	SND_SOC_DAPM_MUX("SLIM TX3 MUX", SND_SOC_NOPM, 0, 0, &sb_tx3_mux),
-	SND_SOC_DAPM_MUX("SLIM TX4 MUX", SND_SOC_NOPM, 0, 0, &sb_tx4_mux),
-	SND_SOC_DAPM_MUX("SLIM TX5 MUX", SND_SOC_NOPM, 0, 0, &sb_tx5_mux),
-	SND_SOC_DAPM_MUX("SLIM TX6 MUX", SND_SOC_NOPM, 0, 0, &sb_tx6_mux),
-	SND_SOC_DAPM_MUX("SLIM TX7 MUX", SND_SOC_NOPM, 0, 0, &sb_tx7_mux),
-	SND_SOC_DAPM_MUX("SLIM TX8 MUX", SND_SOC_NOPM, 0, 0, &sb_tx8_mux),
-	SND_SOC_DAPM_MUX("SLIM TX9 MUX", SND_SOC_NOPM, 0, 0, &sb_tx9_mux),
-	SND_SOC_DAPM_MUX("SLIM TX10 MUX", SND_SOC_NOPM, 0, 0, &sb_tx10_mux),
+	SND_SOC_DAPM_AIF_OUT_E("AIF1 CAP", "AIF1 Capture", 0, SND_SOC_NOPM,
+		AIF1_CAP, 0, tabla_codec_enable_slimtx,
+		SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_AIF_OUT_E("AIF2 CAP", "AIF2 Capture", 0, SND_SOC_NOPM,
+		AIF2_CAP, 0, tabla_codec_enable_slimtx,
+		SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_AIF_OUT_E("AIF3 CAP", "AIF3 Capture", 0, SND_SOC_NOPM,
+		AIF3_CAP, 0, tabla_codec_enable_slimtx,
+		SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_MIXER("AIF1_CAP Mixer", SND_SOC_NOPM, AIF1_CAP, 0,
+		aif_cap_mixer, ARRAY_SIZE(aif_cap_mixer)),
+
+	SND_SOC_DAPM_MIXER("AIF2_CAP Mixer", SND_SOC_NOPM, AIF2_CAP, 0,
+		aif_cap_mixer, ARRAY_SIZE(aif_cap_mixer)),
+
+	SND_SOC_DAPM_MIXER("AIF3_CAP Mixer", SND_SOC_NOPM, AIF3_CAP, 0,
+		aif_cap_mixer, ARRAY_SIZE(aif_cap_mixer)),
+
+	SND_SOC_DAPM_MUX("SLIM TX1 MUX", SND_SOC_NOPM, TABLA_TX1, 0,
+		&sb_tx1_mux),
+	SND_SOC_DAPM_MUX("SLIM TX2 MUX", SND_SOC_NOPM, TABLA_TX2, 0,
+		&sb_tx2_mux),
+	SND_SOC_DAPM_MUX("SLIM TX3 MUX", SND_SOC_NOPM, TABLA_TX3, 0,
+		&sb_tx3_mux),
+	SND_SOC_DAPM_MUX("SLIM TX4 MUX", SND_SOC_NOPM, TABLA_TX4, 0,
+		&sb_tx4_mux),
+	SND_SOC_DAPM_MUX("SLIM TX5 MUX", SND_SOC_NOPM, TABLA_TX5, 0,
+		&sb_tx5_mux),
+	SND_SOC_DAPM_MUX("SLIM TX6 MUX", SND_SOC_NOPM, TABLA_TX6, 0,
+		&sb_tx6_mux),
+	SND_SOC_DAPM_MUX("SLIM TX7 MUX", SND_SOC_NOPM, TABLA_TX7, 0,
+		&sb_tx7_mux),
+	SND_SOC_DAPM_MUX("SLIM TX8 MUX", SND_SOC_NOPM, TABLA_TX8, 0,
+		&sb_tx8_mux),
+	SND_SOC_DAPM_MUX("SLIM TX9 MUX", SND_SOC_NOPM, TABLA_TX9, 0,
+		&sb_tx9_mux),
+	SND_SOC_DAPM_MUX("SLIM TX10 MUX", SND_SOC_NOPM, TABLA_TX10, 0,
+		&sb_tx10_mux),
 
 	/* Digital Mic Inputs */
 	SND_SOC_DAPM_ADC_E("DMIC1", NULL, SND_SOC_NOPM, 0, 0,
@@ -7608,7 +7862,6 @@
 	int i, j, port_id, k, ch_mask_temp;
 	unsigned long slimbus_value;
 	u8 val;
-
 	for (i = 0; i < WCD9XXX_SLIM_NUM_PORT_REG; i++) {
 		slimbus_value = wcd9xxx_interface_reg_read(codec->control_data,
 			TABLA_SLIM_PGD_PORT_INT_STATUS0 + i);
@@ -7622,17 +7875,18 @@
 				pr_err_ratelimited("underflow error on port %x,"
 					" value %x\n", i*8 + j, val);
 			if (val & 0x4) {
-				pr_debug("%s: port %x disconnect value %x\n",
-					__func__, i*8 + j, val);
 				port_id = i*8 + j;
 				for (k = 0; k < ARRAY_SIZE(tabla_dai); k++) {
 					ch_mask_temp = 1 << port_id;
+					pr_debug("%s: tabla_p->dai[%d].ch_mask = 0x%x\n",
+						 __func__, k,
+						 tabla_p->dai[k].ch_mask);
 					if (ch_mask_temp &
 						tabla_p->dai[k].ch_mask) {
 						tabla_p->dai[k].ch_mask &=
-								~ch_mask_temp;
+							~ch_mask_temp;
 					if (!tabla_p->dai[k].ch_mask)
-							wake_up(
+						wake_up(
 						&tabla_p->dai[k].dai_wait);
 					}
 				}
@@ -8114,7 +8368,7 @@
 	struct snd_soc_dapm_context *dapm = &codec->dapm;
 	int ret = 0;
 	int i;
-	int ch_cnt;
+	void *ptr = NULL;
 
 	codec->control_data = dev_get_drvdata(codec->dev->parent);
 	control = codec->control_data;
@@ -8174,8 +8428,6 @@
 		goto err_pdata;
 	}
 
-//	snd_soc_add_codec_controls(codec, tabla_snd_controls,
-//			     ARRAY_SIZE(tabla_snd_controls));
 	if (TABLA_IS_1_X(control->version))
 		snd_soc_add_codec_controls(codec, tabla_1_x_snd_controls,
 				     ARRAY_SIZE(tabla_1_x_snd_controls));
@@ -8183,15 +8435,6 @@
 		snd_soc_add_codec_controls(codec, tabla_2_higher_snd_controls,
 				     ARRAY_SIZE(tabla_2_higher_snd_controls));
 
-//	snd_soc_dapm_new_controls(dapm, tabla_dapm_widgets,
-//				  ARRAY_SIZE(tabla_dapm_widgets));
-
-	snd_soc_dapm_new_controls(dapm, tabla_dapm_aif_in_widgets,
-				  ARRAY_SIZE(tabla_dapm_aif_in_widgets));
-
-	snd_soc_dapm_new_controls(dapm, tabla_dapm_aif_out_widgets,
-				  ARRAY_SIZE(tabla_dapm_aif_out_widgets));
-
 	if (TABLA_IS_1_X(control->version))
 		snd_soc_dapm_new_controls(dapm, tabla_1_x_dapm_widgets,
 					  ARRAY_SIZE(tabla_1_x_dapm_widgets));
@@ -8199,13 +8442,35 @@
 		snd_soc_dapm_new_controls(dapm, tabla_2_higher_dapm_widgets,
 				    ARRAY_SIZE(tabla_2_higher_dapm_widgets));
 
+
+	ptr = kmalloc((sizeof(tabla_rx_chs) +
+		       sizeof(tabla_tx_chs)), GFP_KERNEL);
+	if (!ptr) {
+		pr_err("%s: no mem for slim chan ctl data\n", __func__);
+		ret = -ENOMEM;
+		goto err_nomem_slimch;
+	}
 	if (tabla->intf_type == WCD9XXX_INTERFACE_TYPE_I2C) {
 		snd_soc_dapm_new_controls(dapm, tabla_dapm_i2s_widgets,
 			ARRAY_SIZE(tabla_dapm_i2s_widgets));
 		snd_soc_dapm_add_routes(dapm, audio_i2s_map,
 			ARRAY_SIZE(audio_i2s_map));
+		for (i = 0; i < ARRAY_SIZE(tabla_i2s_dai); i++)
+			INIT_LIST_HEAD(&tabla->dai[i].wcd9xxx_ch_list);
+	} else if (tabla->intf_type == WCD9XXX_INTERFACE_TYPE_SLIMBUS) {
+		for (i = 0; i < NUM_CODEC_DAIS; i++) {
+			INIT_LIST_HEAD(&tabla->dai[i].wcd9xxx_ch_list);
+			init_waitqueue_head(&tabla->dai[i].dai_wait);
+		}
 	}
-//	snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map));
+
+	control->num_rx_port = TABLA_RX_MAX;
+	control->rx_chs = ptr;
+	memcpy(control->rx_chs, tabla_rx_chs, sizeof(tabla_rx_chs));
+	control->num_tx_port = TABLA_TX_MAX;
+	control->tx_chs = ptr + sizeof(tabla_rx_chs);
+	memcpy(control->tx_chs, tabla_tx_chs, sizeof(tabla_tx_chs));
+
 
 	if (TABLA_IS_1_X(control->version)) {
 		snd_soc_dapm_add_routes(dapm, tabla_1_x_lineout_2_to_4_map,
@@ -8302,33 +8567,6 @@
 		       "tabla_gpio_irq_resend");
 	tabla->gpio_irq_resend = false;
 
-	for (i = 0; i < ARRAY_SIZE(tabla_dai); i++) {
-		switch (tabla_dai[i].id) {
-		case AIF1_PB:
-			ch_cnt = tabla_dai[i].playback.channels_max;
-			break;
-		case AIF1_CAP:
-			ch_cnt = tabla_dai[i].capture.channels_max;
-			break;
-		case AIF2_PB:
-			ch_cnt = tabla_dai[i].playback.channels_max;
-			break;
-		case AIF2_CAP:
-			ch_cnt = tabla_dai[i].capture.channels_max;
-			break;
-		case AIF3_PB:
-			ch_cnt = tabla_dai[i].playback.channels_max;
-			break;
-		case AIF3_CAP:
-			ch_cnt = tabla_dai[i].capture.channels_max;
-			break;
-		default:
-			continue;
-		}
-		tabla->dai[i].ch_num = kzalloc((sizeof(unsigned int)*
-					ch_cnt), GFP_KERNEL);
-		init_waitqueue_head(&tabla->dai[i].dai_wait);
-	}
 
 #ifdef CONFIG_DEBUG_FS
 	if (ret == 0) {
@@ -8360,13 +8598,14 @@
 			 tabla);
 err_insert_irq:
 err_pdata:
+	kfree(ptr);
+err_nomem_slimch:
 	mutex_destroy(&tabla->codec_resource_lock);
 	kfree(tabla);
 	return ret;
 }
 static int tabla_codec_remove(struct snd_soc_codec *codec)
 {
-	int i;
 	struct tabla_priv *tabla = snd_soc_codec_get_drvdata(codec);
 
 	wake_lock_destroy(&tabla->irq_resend_wlock);
@@ -8384,8 +8623,6 @@
 	tabla_codec_enable_bandgap(codec, TABLA_BANDGAP_OFF);
 	if (tabla->mbhc_fw)
 		release_firmware(tabla->mbhc_fw);
-	for (i = 0; i < ARRAY_SIZE(tabla_dai); i++)
-		kfree(tabla->dai[i].ch_num);
 	mutex_destroy(&tabla->codec_resource_lock);
 #ifdef CONFIG_DEBUG_FS
 	debugfs_remove(tabla->debugfs_poke);
diff --git a/sound/soc/codecs/wcd9310.h b/sound/soc/codecs/wcd9310.h
index 4c9f8b4..98c1835 100644
--- a/sound/soc/codecs/wcd9310.h
+++ b/sound/soc/codecs/wcd9310.h
@@ -252,3 +252,29 @@
 				 sizeof(cfg_ptr->_alpha[0]))))
 
 
+/* Number of input and output Slimbus port */
+enum {
+	TABLA_RX1 = 0,
+	TABLA_RX2,
+	TABLA_RX3,
+	TABLA_RX4,
+	TABLA_RX5,
+	TABLA_RX6,
+	TABLA_RX7,
+	TABLA_RX_MAX,
+};
+
+enum {
+	TABLA_TX1 = 0,
+	TABLA_TX2,
+	TABLA_TX3,
+	TABLA_TX4,
+	TABLA_TX5,
+	TABLA_TX6,
+	TABLA_TX7,
+	TABLA_TX8,
+	TABLA_TX9,
+	TABLA_TX10,
+	TABLA_TX_MAX,
+};
+
diff --git a/sound/soc/codecs/wcd9320.c b/sound/soc/codecs/wcd9320.c
index d187ea5..886e4d3 100644
--- a/sound/soc/codecs/wcd9320.c
+++ b/sound/soc/codecs/wcd9320.c
@@ -33,81 +33,46 @@
 #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
-
-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_TX_PORT_NUMBER	16
 
 #define TAIKO_I2S_MASTER_MODE_MASK 0x08
 
-#define TAIKO_OCP_ATTEMPT 1
-
-#define AIF1_PB 1
-#define AIF1_CAP 2
-#define AIF2_PB 3
-#define AIF2_CAP 4
-#define AIF3_CAP 5
-#define AIF3_PB  6
-
-#define NUM_CODEC_DAIS 6
-#define TAIKO_COMP_DIGITAL_GAIN_OFFSET 3
-
-struct taiko_codec_dai_data {
-	u32 rate;
-	u32 *ch_num;
-	u32 ch_act;
-	u32 ch_tot;
+enum {
+	AIF1_PB = 0,
+	AIF1_CAP,
+	AIF2_PB,
+	AIF2_CAP,
+	AIF3_PB,
+	AIF3_CAP,
+	NUM_CODEC_DAIS,
 };
 
-#define TAIKO_MCLK_RATE_12288KHZ 12288000
-#define TAIKO_MCLK_RATE_9600KHZ 9600000
+enum {
+	RX_MIX1_INP_SEL_ZERO = 0,
+	RX_MIX1_INP_SEL_SRC1,
+	RX_MIX1_INP_SEL_SRC2,
+	RX_MIX1_INP_SEL_IIR1,
+	RX_MIX1_INP_SEL_IIR2,
+	RX_MIX1_INP_SEL_RX1,
+	RX_MIX1_INP_SEL_RX2,
+	RX_MIX1_INP_SEL_RX3,
+	RX_MIX1_INP_SEL_RX4,
+	RX_MIX1_INP_SEL_RX5,
+	RX_MIX1_INP_SEL_RX6,
+	RX_MIX1_INP_SEL_RX7,
+	RX_MIX1_INP_SEL_AUXRX,
+};
 
-#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)
+#define TAIKO_COMP_DIGITAL_GAIN_OFFSET 3
 
 static const DECLARE_TLV_DB_SCALE(digital_gain, 0, 1, 0);
 static const DECLARE_TLV_DB_SCALE(line_gain, 0, 7, 1);
@@ -115,21 +80,6 @@
 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,
@@ -162,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;
@@ -237,62 +127,65 @@
 
 static struct hpf_work tx_hpf_work[NUM_DECIMATORS];
 
+static const struct wcd9xxx_ch taiko_rx_chs[TAIKO_RX_MAX] = {
+	WCD9XXX_CH(16, 0),
+	WCD9XXX_CH(17, 1),
+	WCD9XXX_CH(18, 2),
+	WCD9XXX_CH(19, 3),
+	WCD9XXX_CH(20, 4),
+	WCD9XXX_CH(21, 5),
+	WCD9XXX_CH(22, 6),
+	WCD9XXX_CH(23, 7),
+	WCD9XXX_CH(24, 8),
+	WCD9XXX_CH(25, 9),
+	WCD9XXX_CH(26, 10),
+	WCD9XXX_CH(27, 11),
+	WCD9XXX_CH(28, 12),
+};
+
+static const struct wcd9xxx_ch taiko_tx_chs[TAIKO_TX_MAX] = {
+	WCD9XXX_CH(0, 0),
+	WCD9XXX_CH(1, 1),
+	WCD9XXX_CH(2, 2),
+	WCD9XXX_CH(3, 3),
+	WCD9XXX_CH(4, 4),
+	WCD9XXX_CH(5, 5),
+	WCD9XXX_CH(6, 6),
+	WCD9XXX_CH(7, 7),
+	WCD9XXX_CH(8, 8),
+	WCD9XXX_CH(9, 9),
+	WCD9XXX_CH(10, 10),
+	WCD9XXX_CH(11, 11),
+	WCD9XXX_CH(12, 12),
+	WCD9XXX_CH(13, 13),
+	WCD9XXX_CH(14, 14),
+	WCD9XXX_CH(15, 15),
+};
+
+static const u32 vport_check_table[NUM_CODEC_DAIS] = {
+	0,					/* AIF1_PB */
+	(1 << AIF2_CAP) | (1 << AIF3_CAP),	/* AIF1_CAP */
+	0,					/* AIF2_PB */
+	(1 << AIF1_CAP) | (1 << AIF3_CAP),	/* AIF2_CAP */
+	0,					/* AIF2_PB */
+	(1 << AIF1_CAP) | (1 << AIF2_CAP),	/* AIF2_CAP */
+};
+
 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 taiko_codec_dai_data dai[NUM_CODEC_DAIS];
+	struct wcd9xxx_codec_dai_data  dai[NUM_CODEC_DAIS];
 
 	/*compander*/
 	int comp_enabled[COMPANDER_MAX];
@@ -303,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,
@@ -1604,6 +1480,240 @@
 static const struct snd_kcontrol_new lineout4_ground_switch =
 	SOC_DAPM_SINGLE("Switch", TAIKO_A_RX_LINE_4_DAC_CTL, 6, 1, 0);
 
+static int slim_tx_vport_validation(u32 dai_id, u32 port_id,
+				    struct taiko_priv *taiko_p)
+{
+	struct wcd9xxx_ch *ch;
+	int ret = 0;
+	int index = 0;
+	u32 vtable = vport_check_table[dai_id];
+	pr_debug("%s: dai_id %u vtable 0x%x port_id %u\n", __func__,
+		 dai_id, vtable, port_id);
+	while (vtable) {
+		if (vtable & 1) {
+			list_for_each_entry(ch,
+				&taiko_p->dai[index].wcd9xxx_ch_list,
+				list) {
+				pr_debug("%s: index %u ch->port %u vtable 0x%x\n",
+					__func__, index, ch->port, vtable);
+				if (ch->port == port_id) {
+					pr_err("%s: TX%u is used by AIF%u_CAP Mixer\n",
+						__func__, port_id + 1,
+						(index + 1)/2);
+					ret = -EINVAL;
+					break;
+				}
+			}
+		}
+		if (ret)
+			break;
+		index++;
+		vtable = vtable >> 1;
+	}
+	return ret;
+}
+
+/* virtual port entries */
+static int slim_tx_mixer_get(struct snd_kcontrol *kcontrol,
+			     struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+	struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+
+	ucontrol->value.integer.value[0] = widget->value;
+	return 0;
+}
+
+static int slim_tx_mixer_put(struct snd_kcontrol *kcontrol,
+			     struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+	struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+	struct snd_soc_codec *codec = widget->codec;
+	struct taiko_priv *taiko_p = snd_soc_codec_get_drvdata(codec);
+	struct wcd9xxx *core = dev_get_drvdata(codec->dev->parent);
+	struct soc_multi_mixer_control *mixer =
+		((struct soc_multi_mixer_control *)kcontrol->private_value);
+	u32 dai_id = widget->shift;
+	u32 port_id = mixer->shift;
+	u32 enable = ucontrol->value.integer.value[0];
+
+
+	pr_debug("%s: wname %s cname %s value %u shift %d item %ld\n", __func__,
+		widget->name, ucontrol->id.name, widget->value, widget->shift,
+		ucontrol->value.integer.value[0]);
+
+	mutex_lock(&codec->mutex);
+
+	if (taiko_p->intf_type != WCD9XXX_INTERFACE_TYPE_SLIMBUS) {
+		if (dai_id != AIF1_CAP) {
+			dev_err(codec->dev, "%s: invalid AIF for I2C mode\n",
+				__func__);
+			mutex_unlock(&codec->mutex);
+			return -EINVAL;
+		}
+	}
+	switch (dai_id) {
+	case AIF1_CAP:
+	case AIF2_CAP:
+	case AIF3_CAP:
+		/* only add to the list if value not set
+		 */
+		if (enable && !(widget->value & 1 << port_id)) {
+			if (slim_tx_vport_validation(dai_id,
+						     port_id, taiko_p)) {
+				pr_info("%s: TX%u is used by other virtual port\n",
+					__func__, port_id + 1);
+				mutex_unlock(&codec->mutex);
+				return -EINVAL;
+			}
+			widget->value |= 1 << port_id;
+			list_add_tail(&core->tx_chs[port_id].list,
+				      &taiko_p->dai[dai_id].wcd9xxx_ch_list
+				      );
+		} else if (!enable && (widget->value & 1 << port_id)) {
+			widget->value &= ~(1 << port_id);
+			list_del_init(&core->tx_chs[port_id].list);
+		} else {
+			if (enable)
+				pr_info("%s: TX%u port is used by this virtual port\n",
+					__func__, port_id + 1);
+			else
+				pr_info("%s: TX%u port is not used by this virtual port\n",
+					__func__, port_id + 1);
+			/* avoid update power function */
+			mutex_unlock(&codec->mutex);
+			return 0;
+		}
+		break;
+	default:
+		pr_err("Unknown AIF %d\n", dai_id);
+		mutex_unlock(&codec->mutex);
+		return -EINVAL;
+	}
+	pr_debug("%s: name %s sname %s updated value %u shift %d\n", __func__,
+		widget->name, widget->sname, widget->value, widget->shift);
+
+	snd_soc_dapm_mixer_update_power(widget, kcontrol, enable);
+
+	mutex_unlock(&codec->mutex);
+	return 0;
+}
+
+static int slim_rx_mux_get(struct snd_kcontrol *kcontrol,
+			   struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+	struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+
+	ucontrol->value.enumerated.item[0] = widget->value;
+	return 0;
+}
+
+static const char *const slim_rx_mux_text[] = {
+	"ZERO", "AIF1_PB", "AIF2_PB", "AIF3_PB"
+};
+
+static int slim_rx_mux_put(struct snd_kcontrol *kcontrol,
+			   struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+	struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+	struct snd_soc_codec *codec = widget->codec;
+	struct taiko_priv *taiko_p = snd_soc_codec_get_drvdata(codec);
+	struct wcd9xxx *core = dev_get_drvdata(codec->dev->parent);
+	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+	u32 port_id = widget->shift;
+
+	pr_debug("%s: wname %s cname %s value %u shift %d item %ld\n", __func__,
+		widget->name, ucontrol->id.name, widget->value, widget->shift,
+		ucontrol->value.integer.value[0]);
+
+	widget->value = ucontrol->value.enumerated.item[0];
+
+	mutex_lock(&codec->mutex);
+
+	if (taiko_p->intf_type != WCD9XXX_INTERFACE_TYPE_SLIMBUS) {
+		if (widget->value > 1) {
+			dev_err(codec->dev, "%s: invalid AIF for I2C mode\n",
+				__func__);
+			mutex_unlock(&codec->mutex);
+			return -EINVAL;
+		}
+	}
+	/* value need to match the Virtual port and AIF number
+	 */
+	switch (widget->value) {
+	case 0:
+		list_del_init(&core->rx_chs[port_id].list);
+	break;
+	case 1:
+		list_add_tail(&core->rx_chs[port_id].list,
+			      &taiko_p->dai[AIF1_PB].wcd9xxx_ch_list);
+	break;
+	case 2:
+		list_add_tail(&core->rx_chs[port_id].list,
+			      &taiko_p->dai[AIF2_PB].wcd9xxx_ch_list);
+	break;
+	case 3:
+		list_add_tail(&core->rx_chs[port_id].list,
+			      &taiko_p->dai[AIF3_PB].wcd9xxx_ch_list);
+	break;
+	default:
+		pr_err("Unknown AIF %d\n", widget->value);
+		mutex_unlock(&codec->mutex);
+		return -EINVAL;
+	}
+
+	snd_soc_dapm_mux_update_power(widget, kcontrol, 1, widget->value, e);
+
+	mutex_unlock(&codec->mutex);
+	return 0;
+}
+
+static const struct soc_enum slim_rx_mux_enum =
+	SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(slim_rx_mux_text), slim_rx_mux_text);
+
+static const struct snd_kcontrol_new slim_rx_mux[TAIKO_RX_MAX] = {
+	SOC_DAPM_ENUM_EXT("SLIM RX1 Mux", slim_rx_mux_enum,
+			  slim_rx_mux_get, slim_rx_mux_put),
+	SOC_DAPM_ENUM_EXT("SLIM RX2 Mux", slim_rx_mux_enum,
+			  slim_rx_mux_get, slim_rx_mux_put),
+	SOC_DAPM_ENUM_EXT("SLIM RX3 Mux", slim_rx_mux_enum,
+			  slim_rx_mux_get, slim_rx_mux_put),
+	SOC_DAPM_ENUM_EXT("SLIM RX4 Mux", slim_rx_mux_enum,
+			  slim_rx_mux_get, slim_rx_mux_put),
+	SOC_DAPM_ENUM_EXT("SLIM RX5 Mux", slim_rx_mux_enum,
+			  slim_rx_mux_get, slim_rx_mux_put),
+	SOC_DAPM_ENUM_EXT("SLIM RX6 Mux", slim_rx_mux_enum,
+			  slim_rx_mux_get, slim_rx_mux_put),
+	SOC_DAPM_ENUM_EXT("SLIM RX7 Mux", slim_rx_mux_enum,
+			  slim_rx_mux_get, slim_rx_mux_put),
+};
+
+static const struct snd_kcontrol_new aif_cap_mixer[] = {
+	SOC_SINGLE_EXT("SLIM TX1", SND_SOC_NOPM, TAIKO_TX1, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX2", SND_SOC_NOPM, TAIKO_TX2, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX3", SND_SOC_NOPM, TAIKO_TX3, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX4", SND_SOC_NOPM, TAIKO_TX4, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX5", SND_SOC_NOPM, TAIKO_TX5, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX6", SND_SOC_NOPM, TAIKO_TX6, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX7", SND_SOC_NOPM, TAIKO_TX7, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX8", SND_SOC_NOPM, TAIKO_TX8, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX9", SND_SOC_NOPM, TAIKO_TX9, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX10", SND_SOC_NOPM, TAIKO_TX10, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+};
+
 static void taiko_codec_enable_adc_block(struct snd_soc_codec *codec,
 					 int enable)
 {
@@ -1670,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)
 {
@@ -1847,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;
@@ -2099,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__);
@@ -2459,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);
@@ -2478,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);
@@ -2505,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;
 	}
 
@@ -2707,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;
@@ -2738,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);
@@ -2823,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),
@@ -2965,14 +2449,48 @@
 
 static const struct snd_soc_dapm_route audio_map[] = {
 	/* SLIMBUS Connections */
+	{"AIF1 CAP", NULL, "AIF1_CAP Mixer"},
+	{"AIF2 CAP", NULL, "AIF2_CAP Mixer"},
+	{"AIF3 CAP", NULL, "AIF3_CAP Mixer"},
 
-	{"SLIM TX1", NULL, "SLIM TX1 MUX"},
+	/* SLIM_MIXER("AIF1_CAP Mixer"),*/
+	{"AIF1_CAP Mixer", "SLIM TX1", "SLIM TX1 MUX"},
+	{"AIF1_CAP Mixer", "SLIM TX2", "SLIM TX2 MUX"},
+	{"AIF1_CAP Mixer", "SLIM TX3", "SLIM TX3 MUX"},
+	{"AIF1_CAP Mixer", "SLIM TX4", "SLIM TX4 MUX"},
+	{"AIF1_CAP Mixer", "SLIM TX5", "SLIM TX5 MUX"},
+	{"AIF1_CAP Mixer", "SLIM TX6", "SLIM TX6 MUX"},
+	{"AIF1_CAP Mixer", "SLIM TX7", "SLIM TX7 MUX"},
+	{"AIF1_CAP Mixer", "SLIM TX8", "SLIM TX8 MUX"},
+	{"AIF1_CAP Mixer", "SLIM TX9", "SLIM TX9 MUX"},
+	{"AIF1_CAP Mixer", "SLIM TX10", "SLIM TX10 MUX"},
+	/* SLIM_MIXER("AIF2_CAP Mixer"),*/
+	{"AIF2_CAP Mixer", "SLIM TX1", "SLIM TX1 MUX"},
+	{"AIF2_CAP Mixer", "SLIM TX2", "SLIM TX2 MUX"},
+	{"AIF2_CAP Mixer", "SLIM TX3", "SLIM TX3 MUX"},
+	{"AIF2_CAP Mixer", "SLIM TX4", "SLIM TX4 MUX"},
+	{"AIF2_CAP Mixer", "SLIM TX5", "SLIM TX5 MUX"},
+	{"AIF2_CAP Mixer", "SLIM TX6", "SLIM TX6 MUX"},
+	{"AIF2_CAP Mixer", "SLIM TX7", "SLIM TX7 MUX"},
+	{"AIF2_CAP Mixer", "SLIM TX8", "SLIM TX8 MUX"},
+	{"AIF2_CAP Mixer", "SLIM TX9", "SLIM TX9 MUX"},
+	{"AIF2_CAP Mixer", "SLIM TX10", "SLIM TX10 MUX"},
+	/* SLIM_MIXER("AIF3_CAP Mixer"),*/
+	{"AIF3_CAP Mixer", "SLIM TX1", "SLIM TX1 MUX"},
+	{"AIF3_CAP Mixer", "SLIM TX2", "SLIM TX2 MUX"},
+	{"AIF3_CAP Mixer", "SLIM TX3", "SLIM TX3 MUX"},
+	{"AIF3_CAP Mixer", "SLIM TX4", "SLIM TX4 MUX"},
+	{"AIF3_CAP Mixer", "SLIM TX5", "SLIM TX5 MUX"},
+	{"AIF3_CAP Mixer", "SLIM TX6", "SLIM TX6 MUX"},
+	{"AIF3_CAP Mixer", "SLIM TX7", "SLIM TX7 MUX"},
+	{"AIF3_CAP Mixer", "SLIM TX8", "SLIM TX8 MUX"},
+	{"AIF3_CAP Mixer", "SLIM TX9", "SLIM TX9 MUX"},
+	{"AIF3_CAP Mixer", "SLIM TX10", "SLIM TX10 MUX"},
+
 	{"SLIM TX1 MUX", "DEC1", "DEC1 MUX"},
 
-	{"SLIM TX2", NULL, "SLIM TX2 MUX"},
 	{"SLIM TX2 MUX", "DEC2", "DEC2 MUX"},
 
-	{"SLIM TX3", NULL, "SLIM TX3 MUX"},
 	{"SLIM TX3 MUX", "DEC3", "DEC3 MUX"},
 	{"SLIM TX3 MUX", "RMIX1", "RX1 MIX1"},
 	{"SLIM TX3 MUX", "RMIX2", "RX2 MIX1"},
@@ -2982,10 +2500,8 @@
 	{"SLIM TX3 MUX", "RMIX6", "RX6 MIX1"},
 	{"SLIM TX3 MUX", "RMIX7", "RX7 MIX1"},
 
-	{"SLIM TX4", NULL, "SLIM TX4 MUX"},
 	{"SLIM TX4 MUX", "DEC4", "DEC4 MUX"},
 
-	{"SLIM TX5", NULL, "SLIM TX5 MUX"},
 	{"SLIM TX5 MUX", "DEC5", "DEC5 MUX"},
 	{"SLIM TX5 MUX", "RMIX1", "RX1 MIX1"},
 	{"SLIM TX5 MUX", "RMIX2", "RX2 MIX1"},
@@ -2995,10 +2511,8 @@
 	{"SLIM TX5 MUX", "RMIX6", "RX6 MIX1"},
 	{"SLIM TX5 MUX", "RMIX7", "RX7 MIX1"},
 
-	{"SLIM TX6", NULL, "SLIM TX6 MUX"},
 	{"SLIM TX6 MUX", "DEC6", "DEC6 MUX"},
 
-	{"SLIM TX7", NULL, "SLIM TX7 MUX"},
 	{"SLIM TX7 MUX", "DEC1", "DEC1 MUX"},
 	{"SLIM TX7 MUX", "DEC2", "DEC2 MUX"},
 	{"SLIM TX7 MUX", "DEC3", "DEC3 MUX"},
@@ -3017,7 +2531,6 @@
 	{"SLIM TX7 MUX", "RMIX6", "RX6 MIX1"},
 	{"SLIM TX7 MUX", "RMIX7", "RX7 MIX1"},
 
-	{"SLIM TX8", NULL, "SLIM TX8 MUX"},
 	{"SLIM TX8 MUX", "DEC1", "DEC1 MUX"},
 	{"SLIM TX8 MUX", "DEC2", "DEC2 MUX"},
 	{"SLIM TX8 MUX", "DEC3", "DEC3 MUX"},
@@ -3029,7 +2542,6 @@
 	{"SLIM TX8 MUX", "DEC9", "DEC9 MUX"},
 	{"SLIM TX8 MUX", "DEC10", "DEC10 MUX"},
 
-	{"SLIM TX9", NULL, "SLIM TX9 MUX"},
 	{"SLIM TX9 MUX", "DEC1", "DEC1 MUX"},
 	{"SLIM TX9 MUX", "DEC2", "DEC2 MUX"},
 	{"SLIM TX9 MUX", "DEC3", "DEC3 MUX"},
@@ -3041,7 +2553,6 @@
 	{"SLIM TX9 MUX", "DEC9", "DEC9 MUX"},
 	{"SLIM TX9 MUX", "DEC10", "DEC10 MUX"},
 
-	{"SLIM TX10", NULL, "SLIM TX10 MUX"},
 	{"SLIM TX10 MUX", "DEC1", "DEC1 MUX"},
 	{"SLIM TX10 MUX", "DEC2", "DEC2 MUX"},
 	{"SLIM TX10 MUX", "DEC3", "DEC3 MUX"},
@@ -3172,6 +2683,39 @@
 	{"RX7 MIX2", NULL, "RX7 MIX2 INP1"},
 	{"RX7 MIX2", NULL, "RX7 MIX2 INP2"},
 
+	/* SLIM_MUX("AIF1_PB", "AIF1 PB"),*/
+	{"SLIM RX1 MUX", "AIF1_PB", "AIF1 PB"},
+	{"SLIM RX2 MUX", "AIF1_PB", "AIF1 PB"},
+	{"SLIM RX3 MUX", "AIF1_PB", "AIF1 PB"},
+	{"SLIM RX4 MUX", "AIF1_PB", "AIF1 PB"},
+	{"SLIM RX5 MUX", "AIF1_PB", "AIF1 PB"},
+	{"SLIM RX6 MUX", "AIF1_PB", "AIF1 PB"},
+	{"SLIM RX7 MUX", "AIF1_PB", "AIF1 PB"},
+	/* SLIM_MUX("AIF2_PB", "AIF2 PB"),*/
+	{"SLIM RX1 MUX", "AIF2_PB", "AIF2 PB"},
+	{"SLIM RX2 MUX", "AIF2_PB", "AIF2 PB"},
+	{"SLIM RX3 MUX", "AIF2_PB", "AIF2 PB"},
+	{"SLIM RX4 MUX", "AIF2_PB", "AIF2 PB"},
+	{"SLIM RX5 MUX", "AIF2_PB", "AIF2 PB"},
+	{"SLIM RX6 MUX", "AIF2_PB", "AIF2 PB"},
+	{"SLIM RX7 MUX", "AIF2_PB", "AIF2 PB"},
+	/* SLIM_MUX("AIF3_PB", "AIF3 PB"),*/
+	{"SLIM RX1 MUX", "AIF3_PB", "AIF3 PB"},
+	{"SLIM RX2 MUX", "AIF3_PB", "AIF3 PB"},
+	{"SLIM RX3 MUX", "AIF3_PB", "AIF3 PB"},
+	{"SLIM RX4 MUX", "AIF3_PB", "AIF3 PB"},
+	{"SLIM RX5 MUX", "AIF3_PB", "AIF3 PB"},
+	{"SLIM RX6 MUX", "AIF3_PB", "AIF3 PB"},
+	{"SLIM RX7 MUX", "AIF3_PB", "AIF3 PB"},
+
+	{"SLIM RX1", NULL, "SLIM RX1 MUX"},
+	{"SLIM RX2", NULL, "SLIM RX2 MUX"},
+	{"SLIM RX3", NULL, "SLIM RX3 MUX"},
+	{"SLIM RX4", NULL, "SLIM RX4 MUX"},
+	{"SLIM RX5", NULL, "SLIM RX5 MUX"},
+	{"SLIM RX6", NULL, "SLIM RX6 MUX"},
+	{"SLIM RX7", NULL, "SLIM RX7 MUX"},
+
 	{"RX1 MIX1 INP1", "RX1", "SLIM RX1"},
 	{"RX1 MIX1 INP1", "RX2", "SLIM RX2"},
 	{"RX1 MIX1 INP1", "RX3", "SLIM RX3"},
@@ -3440,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;
 }
 
@@ -3448,6 +2995,10 @@
 	unsigned int value)
 {
 	int ret;
+
+	if (reg == SND_SOC_NOPM)
+		return 0;
+
 	BUG_ON(reg > TAIKO_MAX_REGISTER);
 
 	if (!taiko_volatile(codec, reg)) {
@@ -3465,6 +3016,9 @@
 	unsigned int val;
 	int ret;
 
+	if (reg == SND_SOC_NOPM)
+		return 0;
+
 	BUG_ON(reg > TAIKO_MAX_REGISTER);
 
 	if (!taiko_volatile(codec, reg) && taiko_readable(codec, reg) &&
@@ -3481,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)
 {
@@ -3588,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;
 }
 
@@ -3690,90 +3137,221 @@
 
 {
 	struct taiko_priv *taiko = snd_soc_codec_get_drvdata(dai->codec);
-	u32 i = 0;
+	struct wcd9xxx *core = dev_get_drvdata(dai->codec->dev->parent);
 	if (!tx_slot && !rx_slot) {
 		pr_err("%s: Invalid\n", __func__);
 		return -EINVAL;
 	}
-	pr_debug("%s(): dai_name = %s DAI-ID %x tx_ch %d rx_ch %d\n",
-			__func__, dai->name, dai->id, tx_num, rx_num);
+	pr_debug("%s(): dai_name = %s DAI-ID %x tx_ch %d rx_ch %d\n"
+		 "taiko->intf_type %d\n",
+		 __func__, dai->name, dai->id, tx_num, rx_num,
+		 taiko->intf_type);
 
-	if (dai->id == AIF1_PB || dai->id == AIF2_PB || dai->id == AIF3_PB) {
-		for (i = 0; i < rx_num; i++) {
-			taiko->dai[dai->id - 1].ch_num[i]  = rx_slot[i];
-			taiko->dai[dai->id - 1].ch_act = 0;
-			taiko->dai[dai->id - 1].ch_tot = rx_num;
+	if (taiko->intf_type == WCD9XXX_INTERFACE_TYPE_SLIMBUS)
+		wcd9xxx_init_slimslave(core, core->slim->laddr,
+				       tx_num, tx_slot, rx_num, rx_slot);
+	return 0;
+}
+
+static int taiko_get_channel_map(struct snd_soc_dai *dai,
+				 unsigned int *tx_num, unsigned int *tx_slot,
+				 unsigned int *rx_num, unsigned int *rx_slot)
+
+{
+	struct taiko_priv *taiko_p = snd_soc_codec_get_drvdata(dai->codec);
+	u32 i = 0;
+	struct wcd9xxx_ch *ch;
+
+	switch (dai->id) {
+	case AIF1_PB:
+	case AIF2_PB:
+	case AIF3_PB:
+		if (!rx_slot || !rx_num) {
+			pr_err("%s: Invalid rx_slot %d or rx_num %d\n",
+				 __func__, (u32) rx_slot, (u32) rx_num);
+			return -EINVAL;
 		}
-	} else if (dai->id == AIF1_CAP || dai->id == AIF2_CAP ||
-		   dai->id == AIF3_CAP) {
-		for (i = 0; i < tx_num; i++) {
-			taiko->dai[dai->id - 1].ch_num[i]  = tx_slot[i];
-			taiko->dai[dai->id - 1].ch_act = 0;
-			taiko->dai[dai->id - 1].ch_tot = tx_num;
+		list_for_each_entry(ch, &taiko_p->dai[dai->id].wcd9xxx_ch_list,
+				    list) {
+			pr_debug("%s: tx_slot[%d] %d, ch->ch_num %d\n",
+				 __func__, i, tx_slot[i], ch->ch_num);
+			rx_slot[i++] = ch->ch_num;
+		}
+		pr_debug("%s: rx_num %d\n", __func__, i);
+		*rx_num = i;
+		break;
+	case AIF1_CAP:
+	case AIF2_CAP:
+	case AIF3_CAP:
+		if (!tx_slot || !tx_num) {
+			pr_err("%s: Invalid tx_slot %d or tx_num %d\n",
+				 __func__, (u32) tx_slot, (u32) tx_num);
+			return -EINVAL;
+		}
+		list_for_each_entry(ch, &taiko_p->dai[dai->id].wcd9xxx_ch_list,
+				    list) {
+			pr_debug("%s: tx_slot[%d] %d, ch->ch_num %d\n",
+				 __func__, i, tx_slot[i], ch->ch_num);
+			tx_slot[i++] = ch->ch_num;
+		}
+		pr_debug("%s: tx_num %d\n", __func__, i);
+		*tx_num = i;
+		break;
+
+	default:
+		pr_err("%s: Invalid DAI ID %x\n", __func__, dai->id);
+		break;
+	}
+
+	return 0;
+}
+
+static int taiko_set_interpolator_rate(struct snd_soc_dai *dai,
+	u8 rx_fs_rate_reg_val, u32 compander_fs, u32 sample_rate)
+{
+	u32 j;
+	u8 rx_mix1_inp;
+	u16 rx_mix_1_reg_1, rx_mix_1_reg_2;
+	u16 rx_fs_reg;
+	u8 rx_mix_1_reg_1_val, rx_mix_1_reg_2_val;
+	struct snd_soc_codec *codec = dai->codec;
+	struct wcd9xxx_ch *ch;
+	struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
+
+	list_for_each_entry(ch, &taiko->dai[dai->id].wcd9xxx_ch_list, list) {
+		/* for RX port starting from 16 instead of 10 like tabla */
+		rx_mix1_inp = ch->port + RX_MIX1_INP_SEL_RX1 -
+			      TAIKO_TX_PORT_NUMBER;
+		if ((rx_mix1_inp < RX_MIX1_INP_SEL_RX1) ||
+			(rx_mix1_inp > RX_MIX1_INP_SEL_RX7)) {
+			pr_err("%s: Invalid TAIKO_RX%u port. Dai ID is %d\n",
+				__func__,  rx_mix1_inp - 5 , dai->id);
+			return -EINVAL;
+		}
+
+		rx_mix_1_reg_1 = TAIKO_A_CDC_CONN_RX1_B1_CTL;
+
+		for (j = 0; j < NUM_INTERPOLATORS; j++) {
+			rx_mix_1_reg_2 = rx_mix_1_reg_1 + 1;
+
+			rx_mix_1_reg_1_val = snd_soc_read(codec,
+							  rx_mix_1_reg_1);
+			rx_mix_1_reg_2_val = snd_soc_read(codec,
+							  rx_mix_1_reg_2);
+
+			if (((rx_mix_1_reg_1_val & 0x0F) == rx_mix1_inp) ||
+			    (((rx_mix_1_reg_1_val >> 4) & 0x0F)
+				== rx_mix1_inp) ||
+			    ((rx_mix_1_reg_2_val & 0x0F) == rx_mix1_inp)) {
+
+				rx_fs_reg = TAIKO_A_CDC_RX1_B5_CTL + 8 * j;
+
+				pr_debug("%s: AIF_PB DAI(%d) connected to RX%u\n",
+					__func__, dai->id, j + 1);
+
+				pr_debug("%s: set RX%u sample rate to %u\n",
+					__func__, j + 1, sample_rate);
+
+				snd_soc_update_bits(codec, rx_fs_reg,
+						0xE0, rx_fs_rate_reg_val);
+
+				if (comp_rx_path[j] < COMPANDER_MAX)
+					taiko->comp_fs[comp_rx_path[j]]
+					= compander_fs;
+			}
+			if (j <= 2)
+				rx_mix_1_reg_1 += 3;
+			else
+				rx_mix_1_reg_1 += 2;
 		}
 	}
 	return 0;
 }
 
-static int taiko_get_channel_map(struct snd_soc_dai *dai,
-				unsigned int *tx_num, unsigned int *tx_slot,
-				unsigned int *rx_num, unsigned int *rx_slot)
-
+static int taiko_set_decimator_rate(struct snd_soc_dai *dai,
+	u8 tx_fs_rate_reg_val, u32 sample_rate)
 {
-	struct wcd9xxx *taiko = dev_get_drvdata(dai->codec->control_data);
+	struct snd_soc_codec *codec = dai->codec;
+	struct wcd9xxx_ch *ch;
+	struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
+	u32 tx_port;
+	u16 tx_port_reg, tx_fs_reg;
+	u8 tx_port_reg_val;
+	s8 decimator;
 
-	u32 cnt = 0;
-	u32 tx_ch[SLIM_MAX_TX_PORTS];
-	u32 rx_ch[SLIM_MAX_RX_PORTS];
+	list_for_each_entry(ch, &taiko->dai[dai->id].wcd9xxx_ch_list, list) {
 
-	if (!rx_slot && !tx_slot) {
-		pr_err("%s: Invalid\n", __func__);
-		return -EINVAL;
+		tx_port = ch->port + 1;
+		pr_debug("%s: dai->id = %d, tx_port = %d",
+			__func__, dai->id, tx_port);
+
+		if ((tx_port < 1) || (tx_port > NUM_DECIMATORS)) {
+			pr_err("%s: Invalid SLIM TX%u port. DAI ID is %d\n",
+				__func__, tx_port, dai->id);
+			return -EINVAL;
+		}
+
+		tx_port_reg = TAIKO_A_CDC_CONN_TX_SB_B1_CTL + (tx_port - 1);
+		tx_port_reg_val =  snd_soc_read(codec, tx_port_reg);
+
+		decimator = 0;
+
+		if ((tx_port >= 1) && (tx_port <= 6)) {
+
+			tx_port_reg_val =  tx_port_reg_val & 0x0F;
+			if (tx_port_reg_val == 0x8)
+				decimator = tx_port;
+
+		} else if ((tx_port >= 7) && (tx_port <= NUM_DECIMATORS)) {
+
+			tx_port_reg_val =  tx_port_reg_val & 0x1F;
+
+			if ((tx_port_reg_val >= 0x8) &&
+			    (tx_port_reg_val <= 0x11)) {
+
+				decimator = (tx_port_reg_val - 0x8) + 1;
+			}
+		}
+
+		if (decimator) { /* SLIM_TX port has a DEC as input */
+
+			tx_fs_reg = TAIKO_A_CDC_TX1_CLK_FS_CTL +
+				    8 * (decimator - 1);
+
+			pr_debug("%s: set DEC%u (-> SLIM_TX%u) rate to %u\n",
+				__func__, decimator, tx_port, sample_rate);
+
+			snd_soc_update_bits(codec, tx_fs_reg, 0x07,
+					    tx_fs_rate_reg_val);
+
+		} else {
+			if ((tx_port_reg_val >= 0x1) &&
+			    (tx_port_reg_val <= 0x7)) {
+
+				pr_debug("%s: RMIX%u going to SLIM TX%u\n",
+					__func__, tx_port_reg_val, tx_port);
+
+			} else if  ((tx_port_reg_val >= 0x8) &&
+				    (tx_port_reg_val <= 0x11)) {
+
+				pr_err("%s: ERROR: Should not be here\n",
+				       __func__);
+				pr_err("%s: ERROR: DEC connected to SLIM TX%u\n",
+					__func__, tx_port);
+				return -EINVAL;
+
+			} else if (tx_port_reg_val == 0) {
+				pr_debug("%s: no signal to SLIM TX%u\n",
+					__func__, tx_port);
+			} else {
+				pr_err("%s: ERROR: wrong signal to SLIM TX%u\n",
+					__func__, tx_port);
+				pr_err("%s: ERROR: wrong signal = %u\n",
+					__func__, tx_port_reg_val);
+				return -EINVAL;
+			}
+		}
 	}
-
-	/* for virtual port, codec driver needs to do
-	 * housekeeping, for now should be ok
-	 */
-	wcd9xxx_get_channel(taiko, rx_ch, tx_ch);
-	if (dai->id == AIF1_PB) {
-		*rx_num = taiko_dai[dai->id - 1].playback.channels_max;
-		while (cnt < *rx_num) {
-			rx_slot[cnt] = rx_ch[cnt];
-			cnt++;
-		}
-	} else if (dai->id == AIF1_CAP) {
-		*tx_num = taiko_dai[dai->id - 1].capture.channels_max;
-		while (cnt < *tx_num) {
-			tx_slot[cnt] = tx_ch[6 + cnt];
-			cnt++;
-		}
-	} else if (dai->id == AIF2_PB) {
-		*rx_num = taiko_dai[dai->id - 1].playback.channels_max;
-		while (cnt < *rx_num) {
-			rx_slot[cnt] = rx_ch[5 + cnt];
-			cnt++;
-		}
-	} else if (dai->id == AIF2_CAP) {
-		*tx_num = taiko_dai[dai->id - 1].capture.channels_max;
-		tx_slot[0] = tx_ch[cnt];
-		tx_slot[1] = tx_ch[1 + cnt];
-		tx_slot[2] = tx_ch[5 + cnt];
-		tx_slot[3] = tx_ch[3 + cnt];
-
-	} else if (dai->id == AIF3_PB) {
-		*rx_num = taiko_dai[dai->id - 1].playback.channels_max;
-		rx_slot[0] = rx_ch[3];
-		rx_slot[1] = rx_ch[4];
-
-	} else if (dai->id == AIF3_CAP) {
-		*tx_num = taiko_dai[dai->id - 1].capture.channels_max;
-		tx_slot[cnt] = tx_ch[2 + cnt];
-		tx_slot[cnt + 1] = tx_ch[4 + cnt];
-	}
-	pr_debug("%s(): dai_name = %s DAI-ID %x tx_ch %d rx_ch %d\n",
-			__func__, dai->name, dai->id, *tx_num, *rx_num);
-
-
 	return 0;
 }
 
@@ -3783,10 +3361,9 @@
 {
 	struct snd_soc_codec *codec = dai->codec;
 	struct taiko_priv *taiko = snd_soc_codec_get_drvdata(dai->codec);
-	u8 path, shift;
-	u16 tx_fs_reg, rx_fs_reg;
-	u8 tx_fs_rate, rx_fs_rate, rx_state, tx_state;
+	u8 tx_fs_rate, rx_fs_rate;
 	u32 compander_fs;
+	int ret;
 
 	pr_debug("%s: dai_name = %s DAI-ID %x rate %d num_ch %d\n", __func__,
 		 dai->name, dai->id, params_rate(params),
@@ -3825,37 +3402,20 @@
 		break;
 	default:
 		pr_err("%s: Invalid sampling rate %d\n", __func__,
-				params_rate(params));
+			params_rate(params));
 		return -EINVAL;
 	}
 
-
-	/**
-	 * If current dai is a tx dai, set sample rate to
-	 * all the txfe paths that are currently not active
-	 */
-	if ((dai->id == AIF1_CAP) || (dai->id == AIF2_CAP) ||
-	    (dai->id == AIF3_CAP)) {
-
-		tx_state = snd_soc_read(codec,
-				TAIKO_A_CDC_CLK_TX_CLK_EN_B1_CTL);
-
-		for (path = 1, shift = 0;
-				path <= NUM_DECIMATORS; path++, shift++) {
-
-			if (path == BITS_PER_REG + 1) {
-				shift = 0;
-				tx_state = snd_soc_read(codec,
-					TAIKO_A_CDC_CLK_TX_CLK_EN_B2_CTL);
-			}
-
-			if (!(tx_state & (1 << shift))) {
-				tx_fs_reg = TAIKO_A_CDC_TX1_CLK_FS_CTL
-						+ (BITS_PER_REG*(path-1));
-				snd_soc_update_bits(codec, tx_fs_reg,
-							0x07, tx_fs_rate);
-			}
+	switch (substream->stream) {
+	case SNDRV_PCM_STREAM_CAPTURE:
+		ret = taiko_set_decimator_rate(dai, tx_fs_rate,
+					       params_rate(params));
+		if (ret < 0) {
+			pr_err("%s: set decimator rate failed %d\n", __func__,
+				ret);
+			return ret;
 		}
+
 		if (taiko->intf_type == WCD9XXX_INTERFACE_TYPE_I2C) {
 			switch (params_format(params)) {
 			case SNDRV_PCM_FORMAT_S16_LE:
@@ -3873,37 +3433,20 @@
 				break;
 			}
 			snd_soc_update_bits(codec, TAIKO_A_CDC_CLK_TX_I2S_CTL,
-						0x07, tx_fs_rate);
+					    0x07, tx_fs_rate);
 		} else {
-			taiko->dai[dai->id - 1].rate   = params_rate(params);
+			taiko->dai[dai->id].rate   = params_rate(params);
 		}
-	}
-	/**
-	 * TODO: Need to handle case where same RX chain takes 2 or more inputs
-	 * with varying sample rates
-	 */
+		break;
 
-	/**
-	 * If current dai is a rx dai, set sample rate to
-	 * all the rx paths that are currently not active
-	 */
-	if (dai->id == AIF1_PB || dai->id == AIF2_PB || dai->id == AIF3_PB) {
-
-		rx_state = snd_soc_read(codec,
-			TAIKO_A_CDC_CLK_RX_B1_CTL);
-
-		for (path = 1, shift = 0;
-				path <= NUM_INTERPOLATORS; path++, shift++) {
-
-			if (!(rx_state & (1 << shift))) {
-				rx_fs_reg = TAIKO_A_CDC_RX1_B5_CTL
-						+ (BITS_PER_REG*(path-1));
-				snd_soc_update_bits(codec, rx_fs_reg,
-						0xE0, rx_fs_rate);
-				if (comp_rx_path[shift] < COMPANDER_MAX)
-					taiko->comp_fs[comp_rx_path[shift]]
-					= compander_fs;
-			}
+	case SNDRV_PCM_STREAM_PLAYBACK:
+		ret = taiko_set_interpolator_rate(dai, rx_fs_rate,
+						  compander_fs,
+						  params_rate(params));
+		if (ret < 0) {
+			pr_err("%s: set decimator rate failed %d\n", __func__,
+				ret);
+			return ret;
 		}
 		if (taiko->intf_type == WCD9XXX_INTERFACE_TYPE_I2C) {
 			switch (params_format(params)) {
@@ -3922,10 +3465,15 @@
 				break;
 			}
 			snd_soc_update_bits(codec, TAIKO_A_CDC_CLK_RX_I2S_CTL,
-						0x03, (rx_fs_rate >> 0x05));
+					    0x03, (rx_fs_rate >> 0x05));
 		} else {
-			taiko->dai[dai->id - 1].rate   = params_rate(params);
+			taiko->dai[dai->id].rate   = params_rate(params);
 		}
+		break;
+	default:
+		pr_err("%s: Invalid stream type %d\n", __func__,
+			substream->stream);
+		return -EINVAL;
 	}
 
 	return 0;
@@ -4031,7 +3579,7 @@
 static struct snd_soc_dai_driver taiko_i2s_dai[] = {
 	{
 		.name = "taiko_i2s_rx1",
-		.id = 1,
+		.id = AIF1_PB,
 		.playback = {
 			.stream_name = "AIF1 Playback",
 			.rates = WCD9320_RATES,
@@ -4045,7 +3593,7 @@
 	},
 	{
 		.name = "taiko_i2s_tx1",
-		.id = 2,
+		.id = AIF1_CAP,
 		.capture = {
 			.stream_name = "AIF1 Capture",
 			.rates = WCD9320_RATES,
@@ -4060,125 +3608,77 @@
 };
 
 static int taiko_codec_enable_slimrx(struct snd_soc_dapm_widget *w,
-	struct snd_kcontrol *kcontrol, int event)
+				     struct snd_kcontrol *kcontrol,
+				     int event)
 {
-	struct wcd9xxx *taiko;
+	struct wcd9xxx *core;
 	struct snd_soc_codec *codec = w->codec;
 	struct taiko_priv *taiko_p = snd_soc_codec_get_drvdata(codec);
-	u32  j = 0;
 	u32  ret = 0;
-	codec->control_data = dev_get_drvdata(codec->dev->parent);
-	taiko = codec->control_data;
+	struct wcd9xxx_codec_dai_data *dai;
+
+	core = dev_get_drvdata(codec->dev->parent);
+
+	pr_debug("%s: event called! codec name %s num_dai %d\n"
+		"stream name %s event %d\n",
+		__func__, w->codec->name, w->codec->num_dai, w->sname, event);
+
 	/* Execute the callback only if interface type is slimbus */
 	if (taiko_p->intf_type != WCD9XXX_INTERFACE_TYPE_SLIMBUS)
 		return 0;
 
-	pr_debug("%s: %s %d\n", __func__, w->name, event);
+	dai = &taiko_p->dai[w->shift];
+	pr_debug("%s: w->name %s w->shift %d event %d\n",
+		 __func__, w->name, w->shift, event);
 
 	switch (event) {
 	case SND_SOC_DAPM_POST_PMU:
-		for (j = 0; j < ARRAY_SIZE(taiko_dai); j++) {
-			if ((taiko_dai[j].id == AIF1_CAP) ||
-			    (taiko_dai[j].id == AIF2_CAP) ||
-			    (taiko_dai[j].id == AIF3_CAP))
-				continue;
-			if (!strncmp(w->sname,
-				taiko_dai[j].playback.stream_name, 13)) {
-				++taiko_p->dai[j].ch_act;
-				break;
-			}
-		}
-		if (taiko_p->dai[j].ch_act == taiko_p->dai[j].ch_tot)
-			ret = wcd9xxx_cfg_slim_sch_rx(taiko,
-					taiko_p->dai[j].ch_num,
-					taiko_p->dai[j].ch_tot,
-					taiko_p->dai[j].rate);
+		ret = wcd9xxx_cfg_slim_sch_rx(core, &dai->wcd9xxx_ch_list,
+					      dai->rate, dai->bit_width,
+					      &dai->grph);
 		break;
 	case SND_SOC_DAPM_POST_PMD:
-		for (j = 0; j < ARRAY_SIZE(taiko_dai); j++) {
-			if ((taiko_dai[j].id == AIF1_CAP) ||
-			    (taiko_dai[j].id == AIF2_CAP) ||
-			    (taiko_dai[j].id == AIF3_CAP))
-				continue;
-			if (!strncmp(w->sname,
-				taiko_dai[j].playback.stream_name, 13)) {
-				--taiko_p->dai[j].ch_act;
-				break;
-			}
-		}
-		if (!taiko_p->dai[j].ch_act) {
-			ret = wcd9xxx_close_slim_sch_rx(taiko,
-						taiko_p->dai[j].ch_num,
-						taiko_p->dai[j].ch_tot);
-			usleep_range(15000, 15000);
-			taiko_p->dai[j].rate = 0;
-			memset(taiko_p->dai[j].ch_num, 0, (sizeof(u32)*
-					taiko_p->dai[j].ch_tot));
-			taiko_p->dai[j].ch_tot = 0;
-		}
+		ret = wcd9xxx_close_slim_sch_rx(core, &dai->wcd9xxx_ch_list,
+						dai->grph);
+		usleep_range(15000, 15000);
+		break;
 	}
 	return ret;
 }
 
 static int taiko_codec_enable_slimtx(struct snd_soc_dapm_widget *w,
-	struct snd_kcontrol *kcontrol, int event)
+				     struct snd_kcontrol *kcontrol,
+				     int event)
 {
-	struct wcd9xxx *taiko;
+	struct wcd9xxx *core;
 	struct snd_soc_codec *codec = w->codec;
 	struct taiko_priv *taiko_p = snd_soc_codec_get_drvdata(codec);
-	/* index to the DAI ID, for now hardcoding */
-	u32  j = 0;
 	u32  ret = 0;
+	struct wcd9xxx_codec_dai_data *dai;
 
-	codec->control_data = dev_get_drvdata(codec->dev->parent);
-	taiko = codec->control_data;
+	core = dev_get_drvdata(codec->dev->parent);
+
+	pr_debug("%s: event called! codec name %s num_dai %d stream name %s\n",
+		__func__, w->codec->name, w->codec->num_dai, w->sname);
 
 	/* Execute the callback only if interface type is slimbus */
 	if (taiko_p->intf_type != WCD9XXX_INTERFACE_TYPE_SLIMBUS)
 		return 0;
 
-	pr_debug("%s(): %s %d\n", __func__, w->name, event);
+	pr_debug("%s(): w->name %s event %d w->shift %d\n",
+		__func__, w->name, event, w->shift);
 
+	dai = &taiko_p->dai[w->shift];
 	switch (event) {
 	case SND_SOC_DAPM_POST_PMU:
-		for (j = 0; j < ARRAY_SIZE(taiko_dai); j++) {
-			if (taiko_dai[j].id == AIF1_PB ||
-				taiko_dai[j].id == AIF2_PB ||
-				taiko_dai[j].id == AIF3_PB)
-				continue;
-			if (!strncmp(w->sname,
-				taiko_dai[j].capture.stream_name, 13)) {
-				++taiko_p->dai[j].ch_act;
-				break;
-			}
-		}
-		if (taiko_p->dai[j].ch_act == taiko_p->dai[j].ch_tot)
-			ret = wcd9xxx_cfg_slim_sch_tx(taiko,
-						taiko_p->dai[j].ch_num,
-						taiko_p->dai[j].ch_tot,
-						taiko_p->dai[j].rate);
+		ret = wcd9xxx_cfg_slim_sch_tx(core, &dai->wcd9xxx_ch_list,
+					      dai->rate, dai->bit_width,
+					      &dai->grph);
 		break;
 	case SND_SOC_DAPM_POST_PMD:
-		for (j = 0; j < ARRAY_SIZE(taiko_dai); j++) {
-			if (taiko_dai[j].id == AIF1_PB ||
-				taiko_dai[j].id == AIF2_PB ||
-				taiko_dai[j].id == AIF3_PB)
-				continue;
-			if (!strncmp(w->sname,
-				taiko_dai[j].capture.stream_name, 13)) {
-				--taiko_p->dai[j].ch_act;
-				break;
-			}
-		}
-		if (!taiko_p->dai[j].ch_act) {
-			ret = wcd9xxx_close_slim_sch_tx(taiko,
-						taiko_p->dai[j].ch_num,
-						taiko_p->dai[j].ch_tot);
-			taiko_p->dai[j].rate = 0;
-			memset(taiko_p->dai[j].ch_num, 0, (sizeof(u32)*
-					taiko_p->dai[j].ch_tot));
-			taiko_p->dai[j].ch_tot = 0;
-		}
+		ret = wcd9xxx_close_slim_sch_tx(core, &dai->wcd9xxx_ch_list,
+						dai->grph);
+		break;
 	}
 	return ret;
 }
@@ -4218,29 +3718,38 @@
 	SND_SOC_DAPM_MIXER("DAC1", TAIKO_A_RX_EAR_EN, 6, 0, dac1_switch,
 		ARRAY_SIZE(dac1_switch)),
 
-	SND_SOC_DAPM_AIF_IN_E("SLIM RX1", "AIF1 Playback", 0, SND_SOC_NOPM, 0,
-				0, taiko_codec_enable_slimrx,
+	SND_SOC_DAPM_AIF_IN_E("AIF1 PB", "AIF1 Playback", 0, SND_SOC_NOPM,
+				AIF1_PB, 0, taiko_codec_enable_slimrx,
 				SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
-	SND_SOC_DAPM_AIF_IN_E("SLIM RX2", "AIF1 Playback", 0, SND_SOC_NOPM, 0,
-				0, taiko_codec_enable_slimrx,
+	SND_SOC_DAPM_AIF_IN_E("AIF2 PB", "AIF2 Playback", 0, SND_SOC_NOPM,
+				AIF2_PB, 0, taiko_codec_enable_slimrx,
 				SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
-	SND_SOC_DAPM_AIF_IN_E("SLIM RX3", "AIF1 Playback", 0, SND_SOC_NOPM, 0,
-				0, taiko_codec_enable_slimrx,
+	SND_SOC_DAPM_AIF_IN_E("AIF3 PB", "AIF3 Playback", 0, SND_SOC_NOPM,
+				AIF3_PB, 0, taiko_codec_enable_slimrx,
 				SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
 
-	SND_SOC_DAPM_AIF_IN_E("SLIM RX4", "AIF3 Playback", 0, SND_SOC_NOPM, 0,
-				0, taiko_codec_enable_slimrx,
-				SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
-	SND_SOC_DAPM_AIF_IN_E("SLIM RX5", "AIF3 Playback", 0, SND_SOC_NOPM, 0,
-				0, taiko_codec_enable_slimrx,
-				SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_MUX("SLIM RX1 MUX", SND_SOC_NOPM, TAIKO_RX1, 0,
+				&slim_rx_mux[TAIKO_RX1]),
+	SND_SOC_DAPM_MUX("SLIM RX2 MUX", SND_SOC_NOPM, TAIKO_RX2, 0,
+				&slim_rx_mux[TAIKO_RX2]),
+	SND_SOC_DAPM_MUX("SLIM RX3 MUX", SND_SOC_NOPM, TAIKO_RX3, 0,
+				&slim_rx_mux[TAIKO_RX3]),
+	SND_SOC_DAPM_MUX("SLIM RX4 MUX", SND_SOC_NOPM, TAIKO_RX4, 0,
+				&slim_rx_mux[TAIKO_RX4]),
+	SND_SOC_DAPM_MUX("SLIM RX5 MUX", SND_SOC_NOPM, TAIKO_RX5, 0,
+				&slim_rx_mux[TAIKO_RX5]),
+	SND_SOC_DAPM_MUX("SLIM RX6 MUX", SND_SOC_NOPM, TAIKO_RX6, 0,
+				&slim_rx_mux[TAIKO_RX6]),
+	SND_SOC_DAPM_MUX("SLIM RX7 MUX", SND_SOC_NOPM, TAIKO_RX7, 0,
+				&slim_rx_mux[TAIKO_RX7]),
 
-	SND_SOC_DAPM_AIF_IN_E("SLIM RX6", "AIF2 Playback", 0, SND_SOC_NOPM, 0,
-				0, taiko_codec_enable_slimrx,
-				SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
-	SND_SOC_DAPM_AIF_IN_E("SLIM RX7", "AIF2 Playback", 0, SND_SOC_NOPM, 0,
-				0, taiko_codec_enable_slimrx,
-				SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_MIXER("SLIM RX1", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("SLIM RX2", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("SLIM RX3", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("SLIM RX4", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("SLIM RX5", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("SLIM RX6", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("SLIM RX7", SND_SOC_NOPM, 0, 0, NULL, 0),
 
 	/* Headphone */
 	SND_SOC_DAPM_OUTPUT("HEADPHONE"),
@@ -4541,55 +4050,47 @@
 		taiko_codec_enable_adc, SND_SOC_DAPM_PRE_PMU |
 		SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
 
-	SND_SOC_DAPM_MUX("SLIM TX1 MUX", SND_SOC_NOPM, 0, 0, &sb_tx1_mux),
-	SND_SOC_DAPM_AIF_OUT_E("SLIM TX1", "AIF2 Capture", 0, SND_SOC_NOPM, 0,
-				0, taiko_codec_enable_slimtx,
-				SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_AIF_OUT_E("AIF1 CAP", "AIF1 Capture", 0, SND_SOC_NOPM,
+		AIF1_CAP, 0, taiko_codec_enable_slimtx,
+		SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
 
-	SND_SOC_DAPM_MUX("SLIM TX2 MUX", SND_SOC_NOPM, 0, 0, &sb_tx2_mux),
-	SND_SOC_DAPM_AIF_OUT_E("SLIM TX2", "AIF2 Capture", 0, SND_SOC_NOPM, 0,
-				0, taiko_codec_enable_slimtx,
-				SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_AIF_OUT_E("AIF2 CAP", "AIF2 Capture", 0, SND_SOC_NOPM,
+		AIF2_CAP, 0, taiko_codec_enable_slimtx,
+		SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
 
-	SND_SOC_DAPM_MUX("SLIM TX3 MUX", SND_SOC_NOPM, 0, 0, &sb_tx3_mux),
-	SND_SOC_DAPM_AIF_OUT_E("SLIM TX3", "AIF3 Capture", 0, SND_SOC_NOPM, 0,
-				0, taiko_codec_enable_slimtx,
-				SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_AIF_OUT_E("AIF3 CAP", "AIF3 Capture", 0, SND_SOC_NOPM,
+		AIF3_CAP, 0, taiko_codec_enable_slimtx,
+		SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
 
-	SND_SOC_DAPM_MUX("SLIM TX4 MUX", SND_SOC_NOPM, 0, 0, &sb_tx4_mux),
-	SND_SOC_DAPM_AIF_OUT_E("SLIM TX4", "AIF2 Capture", 0, SND_SOC_NOPM, 0,
-				0, taiko_codec_enable_slimtx,
-				SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_MIXER("AIF1_CAP Mixer", SND_SOC_NOPM, AIF1_CAP, 0,
+		aif_cap_mixer, ARRAY_SIZE(aif_cap_mixer)),
 
-	SND_SOC_DAPM_MUX("SLIM TX5 MUX", SND_SOC_NOPM, 0, 0, &sb_tx5_mux),
-	SND_SOC_DAPM_AIF_OUT_E("SLIM TX5", "AIF3 Capture", 0, SND_SOC_NOPM, 0,
-				0, taiko_codec_enable_slimtx,
-				SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_MIXER("AIF2_CAP Mixer", SND_SOC_NOPM, AIF2_CAP, 0,
+		aif_cap_mixer, ARRAY_SIZE(aif_cap_mixer)),
 
-	SND_SOC_DAPM_MUX("SLIM TX6 MUX", SND_SOC_NOPM, 0, 0, &sb_tx6_mux),
-	SND_SOC_DAPM_AIF_OUT_E("SLIM TX6", "AIF2 Capture", 0, SND_SOC_NOPM, 0,
-				0, taiko_codec_enable_slimtx,
-				SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_MIXER("AIF3_CAP Mixer", SND_SOC_NOPM, AIF3_CAP, 0,
+		aif_cap_mixer, ARRAY_SIZE(aif_cap_mixer)),
 
-	SND_SOC_DAPM_MUX("SLIM TX7 MUX", SND_SOC_NOPM, 0, 0, &sb_tx7_mux),
-	SND_SOC_DAPM_AIF_OUT_E("SLIM TX7", "AIF1 Capture", 0, SND_SOC_NOPM, 0,
-				0, taiko_codec_enable_slimtx,
-				SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
-
-	SND_SOC_DAPM_MUX("SLIM TX8 MUX", SND_SOC_NOPM, 0, 0, &sb_tx8_mux),
-	SND_SOC_DAPM_AIF_OUT_E("SLIM TX8", "AIF1 Capture", 0, SND_SOC_NOPM, 0,
-				0, taiko_codec_enable_slimtx,
-				SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
-
-	SND_SOC_DAPM_MUX("SLIM TX9 MUX", SND_SOC_NOPM, 0, 0, &sb_tx9_mux),
-	SND_SOC_DAPM_AIF_OUT_E("SLIM TX9", "AIF1 Capture", NULL, SND_SOC_NOPM,
-			0, 0, taiko_codec_enable_slimtx,
-			SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
-
-	SND_SOC_DAPM_MUX("SLIM TX10 MUX", SND_SOC_NOPM, 0, 0, &sb_tx10_mux),
-	SND_SOC_DAPM_AIF_OUT_E("SLIM TX10", "AIF1 Capture", NULL, SND_SOC_NOPM,
-			0, 0, taiko_codec_enable_slimtx,
-			SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_MUX("SLIM TX1 MUX", SND_SOC_NOPM, TAIKO_TX1, 0,
+		&sb_tx1_mux),
+	SND_SOC_DAPM_MUX("SLIM TX2 MUX", SND_SOC_NOPM, TAIKO_TX2, 0,
+		&sb_tx2_mux),
+	SND_SOC_DAPM_MUX("SLIM TX3 MUX", SND_SOC_NOPM, TAIKO_TX3, 0,
+		&sb_tx3_mux),
+	SND_SOC_DAPM_MUX("SLIM TX4 MUX", SND_SOC_NOPM, TAIKO_TX4, 0,
+		&sb_tx4_mux),
+	SND_SOC_DAPM_MUX("SLIM TX5 MUX", SND_SOC_NOPM, TAIKO_TX5, 0,
+		&sb_tx5_mux),
+	SND_SOC_DAPM_MUX("SLIM TX6 MUX", SND_SOC_NOPM, TAIKO_TX6, 0,
+		&sb_tx6_mux),
+	SND_SOC_DAPM_MUX("SLIM TX7 MUX", SND_SOC_NOPM, TAIKO_TX7, 0,
+		&sb_tx7_mux),
+	SND_SOC_DAPM_MUX("SLIM TX8 MUX", SND_SOC_NOPM, TAIKO_TX8, 0,
+		&sb_tx8_mux),
+	SND_SOC_DAPM_MUX("SLIM TX9 MUX", SND_SOC_NOPM, TAIKO_TX9, 0,
+		&sb_tx9_mux),
+	SND_SOC_DAPM_MUX("SLIM TX10 MUX", SND_SOC_NOPM, TAIKO_TX10, 0,
+		&sb_tx10_mux),
 
 	/* Digital Mic Inputs */
 	SND_SOC_DAPM_ADC_E("DMIC1", NULL, SND_SOC_NOPM, 0, 0,
@@ -4653,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)
@@ -6874,8 +4180,8 @@
 		}
 		wcd9xxx_interface_reg_write(codec->control_data,
 			TAIKO_SLIM_PGD_PORT_INT_CLR0 + i, 0xFF);
-	}
 
+	}
 	return IRQ_HANDLED;
 }
 
@@ -6906,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 */
@@ -6961,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;
@@ -6979,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) {
@@ -7261,234 +4558,52 @@
 				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;
-	int ch_cnt;
+	void *ptr = NULL;
 
 	codec->control_data = dev_get_drvdata(codec->dev->parent);
 	control = codec->control_data;
@@ -7507,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);
@@ -7550,86 +4658,57 @@
 		goto err_pdata;
 	}
 
+	ptr = kmalloc((sizeof(taiko_rx_chs) +
+		       sizeof(taiko_tx_chs)), GFP_KERNEL);
+	if (!ptr) {
+		pr_err("%s: no mem for slim chan ctl data\n", __func__);
+		ret = -ENOMEM;
+		goto err_nomem_slimch;
+	}
+
 	if (taiko->intf_type == WCD9XXX_INTERFACE_TYPE_I2C) {
 		snd_soc_dapm_new_controls(dapm, taiko_dapm_i2s_widgets,
 			ARRAY_SIZE(taiko_dapm_i2s_widgets));
 		snd_soc_dapm_add_routes(dapm, audio_i2s_map,
 			ARRAY_SIZE(audio_i2s_map));
+		for (i = 0; i < ARRAY_SIZE(taiko_i2s_dai); i++)
+			INIT_LIST_HEAD(&taiko->dai[i].wcd9xxx_ch_list);
+	} else if (taiko->intf_type == WCD9XXX_INTERFACE_TYPE_SLIMBUS) {
+		for (i = 0; i < NUM_CODEC_DAIS; i++) {
+			INIT_LIST_HEAD(&taiko->dai[i].wcd9xxx_ch_list);
+			init_waitqueue_head(&taiko->dai[i].dai_wait);
+		}
 	}
 
+	control->num_rx_port = TAIKO_RX_MAX;
+	control->rx_chs = ptr;
+	memcpy(control->rx_chs, taiko_rx_chs, sizeof(taiko_rx_chs));
+	control->num_tx_port = TAIKO_TX_MAX;
+	control->tx_chs = ptr + sizeof(taiko_rx_chs);
+	memcpy(control->tx_chs, taiko_tx_chs, sizeof(taiko_tx_chs));
+
 	snd_soc_dapm_sync(dapm);
 
 	(void) taiko_setup_irqs(taiko);
 
-	for (i = 0; i < ARRAY_SIZE(taiko_dai); i++) {
-		switch (taiko_dai[i].id) {
-		case AIF1_PB:
-			ch_cnt = taiko_dai[i].playback.channels_max;
-			break;
-		case AIF1_CAP:
-			ch_cnt = taiko_dai[i].capture.channels_max;
-			break;
-		case AIF2_PB:
-			ch_cnt = taiko_dai[i].playback.channels_max;
-			break;
-		case AIF2_CAP:
-			ch_cnt = taiko_dai[i].capture.channels_max;
-			break;
-		case AIF3_PB:
-			ch_cnt = taiko_dai[i].playback.channels_max;
-			break;
-		case AIF3_CAP:
-			ch_cnt = taiko_dai[i].capture.channels_max;
-			break;
-		default:
-			continue;
-		}
-		taiko->dai[i].ch_num = kzalloc((sizeof(unsigned int)*
-					ch_cnt), GFP_KERNEL);
-	}
-
-#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:
-	mutex_destroy(&taiko->codec_resource_lock);
+	kfree(ptr);
+err_nomem_slimch:
 	kfree(taiko);
 	return ret;
 }
 static int taiko_codec_remove(struct snd_soc_codec *codec)
 {
-	int i;
 	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);
-	for (i = 0; i < ARRAY_SIZE(taiko_dai); i++)
-		kfree(taiko->dai[i].ch_num);
-	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;
 }
@@ -7667,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 7ca8ff0..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,130 +46,50 @@
 	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,
+	TAIKO_RX2,
+	TAIKO_RX3,
+	TAIKO_RX4,
+	TAIKO_RX5,
+	TAIKO_RX6,
+	TAIKO_RX7,
+	TAIKO_RX8,
+	TAIKO_RX9,
+	TAIKO_RX10,
+	TAIKO_RX11,
+	TAIKO_RX12,
+	TAIKO_RX13,
+	TAIKO_RX_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 *);
+enum {
+	TAIKO_TX1 = 0,
+	TAIKO_TX2,
+	TAIKO_TX3,
+	TAIKO_TX4,
+	TAIKO_TX5,
+	TAIKO_TX6,
+	TAIKO_TX7,
+	TAIKO_TX8,
+	TAIKO_TX9,
+	TAIKO_TX10,
+	TAIKO_TX11,
+	TAIKO_TX12,
+	TAIKO_TX13,
+	TAIKO_TX14,
+	TAIKO_TX15,
+	TAIKO_TX16,
+	TAIKO_TX_MAX,
 };
 
-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;
@@ -189,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/apq8064.c b/sound/soc/msm/apq8064.c
index 7894368..1316a0e 100644
--- a/sound/soc/msm/apq8064.c
+++ b/sound/soc/msm/apq8064.c
@@ -859,7 +859,6 @@
 	unsigned int rx_ch_cnt = 0, tx_ch_cnt = 0;
 	unsigned int num_tx_ch = 0;
 
-
 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
 
 		pr_debug("%s: rx_0_ch=%d\n", __func__, msm_slim_0_rx_ch);
@@ -877,25 +876,7 @@
 			pr_err("%s: failed to set cpu chan map\n", __func__);
 			goto end;
 		}
-		ret = snd_soc_dai_set_channel_map(codec_dai, 0, 0,
-				msm_slim_0_rx_ch, rx_ch);
-		if (ret < 0) {
-			pr_err("%s: failed to set codec channel map\n",
-								__func__);
-			goto end;
-		}
 	} else {
-
-		if (codec_dai->id  == 2)
-			num_tx_ch =  msm_slim_0_tx_ch;
-		else if (codec_dai->id == 5) {
-			/* DAI 5 is used for external EC reference from codec.
-			 * Since Rx is fed as reference for EC, the config of
-			 * this DAI is based on that of the Rx path.
-			 */
-			num_tx_ch =  msm_slim_0_rx_ch;
-		}
-
 		pr_debug("%s: %s_tx_dai_id_%d_ch=%d\n", __func__,
 			codec_dai->name, codec_dai->id, num_tx_ch);
 
@@ -905,6 +886,19 @@
 			pr_err("%s: failed to get codec chan map\n", __func__);
 			goto end;
 		}
+		/* For tabla_tx1 case */
+		if (codec_dai->id  == 1)
+			num_tx_ch =  msm_slim_0_tx_ch;
+		/* For tabla_tx3 case */
+		else if (codec_dai->id == 4) {
+			/* DAI 5 is used for external EC reference from codec.
+			 * Since Rx is fed as reference for EC, the config of
+			 * this DAI is based on that of the Rx path.
+			 */
+			num_tx_ch =  msm_slim_0_rx_ch;
+		} else {
+			num_tx_ch = tx_ch_cnt;
+		}
 
 		ret = snd_soc_dai_set_channel_map(cpu_dai,
 				num_tx_ch, tx_ch, 0 , 0);
@@ -912,15 +906,6 @@
 			pr_err("%s: failed to set cpu chan map\n", __func__);
 			goto end;
 		}
-		ret = snd_soc_dai_set_channel_map(codec_dai,
-				num_tx_ch, tx_ch, 0, 0);
-		if (ret < 0) {
-			pr_err("%s: failed to set codec channel map\n",
-								__func__);
-			goto end;
-		}
-
-
 	}
 end:
 	return ret;
@@ -965,13 +950,6 @@
 			pr_err("%s: failed to set cpu chan map\n", __func__);
 			goto end;
 		}
-		ret = snd_soc_dai_set_channel_map(codec_dai, 0, 0,
-				num_rx_ch, rx_ch);
-		if (ret < 0) {
-			pr_err("%s: failed to set codec channel map\n",
-								__func__);
-			goto end;
-		}
 	} else {
 
 		num_tx_ch =  params_channels(params);
@@ -992,13 +970,6 @@
 			pr_err("%s: failed to set cpu chan map\n", __func__);
 			goto end;
 		}
-		ret = snd_soc_dai_set_channel_map(codec_dai,
-				num_tx_ch, tx_ch, 0, 0);
-		if (ret < 0) {
-			pr_err("%s: failed to set codec channel map\n",
-								__func__);
-			goto end;
-		}
 	}
 end:
 	return ret;
@@ -1128,14 +1099,14 @@
 	struct snd_soc_codec *codec = rtd->codec;
 	struct snd_soc_dapm_context *dapm = &codec->dapm;
 	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+	struct snd_soc_dai *codec_dai = rtd->codec_dai;
+	unsigned int rx_ch[TABLA_RX_MAX] = {138, 139, 140, 141, 142, 143, 144};
+	unsigned int tx_ch[TABLA_TX_MAX] = {128, 129, 130, 131, 132, 133, 134,
+					    135, 136, 137};
+
 
 	pr_debug("%s(), dev_name%s\n", __func__, dev_name(cpu_dai->dev));
 
-	/*if (machine_is_msm_liquid()) {
-		top_spk_pamp_gpio = (PM8921_GPIO_PM_TO_SYS(19));
-		bottom_spk_pamp_gpio = (PM8921_GPIO_PM_TO_SYS(18));
-	}*/
-
 	snd_soc_dapm_new_controls(dapm, apq8064_dapm_widgets,
 				ARRAY_SIZE(apq8064_dapm_widgets));
 
@@ -1221,6 +1192,8 @@
 	mbhc_cfg.read_fw_bin = apq8064_hs_detect_use_firmware;
 
 	err = tabla_hs_detect(codec, &mbhc_cfg);
+	snd_soc_dai_set_channel_map(codec_dai, ARRAY_SIZE(tx_ch),
+				    tx_ch, ARRAY_SIZE(rx_ch), rx_ch);
 
 	return err;
 }
diff --git a/sound/soc/msm/mdm9615.c b/sound/soc/msm/mdm9615.c
index 57b846c..5a47efe 100644
--- a/sound/soc/msm/mdm9615.c
+++ b/sound/soc/msm/mdm9615.c
@@ -915,13 +915,6 @@
 			pr_err("%s: failed to set cpu chan map\n", __func__);
 			goto end;
 		}
-		ret = snd_soc_dai_set_channel_map(codec_dai, 0, 0,
-				mdm9615_slim_0_rx_ch, rx_ch);
-		if (ret < 0) {
-			pr_err("%s: failed to set codec channel map\n",
-								__func__);
-			goto end;
-		}
 	} else {
 		ret = snd_soc_dai_get_channel_map(codec_dai,
 				&tx_ch_cnt, tx_ch, &rx_ch_cnt , rx_ch);
@@ -935,13 +928,6 @@
 			pr_err("%s: failed to set cpu chan map\n", __func__);
 			goto end;
 		}
-		ret = snd_soc_dai_set_channel_map(codec_dai,
-				mdm9615_slim_0_tx_ch, tx_ch, 0, 0);
-		if (ret < 0) {
-			pr_err("%s: failed to set codec channel map\n",
-								__func__);
-			goto end;
-		}
 	}
 end:
 	return ret;
@@ -1588,6 +1574,15 @@
 		.vin_sel = 2,
 		.inv_int_pol = 0,
 	};
+	struct snd_soc_dai *codec_dai = rtd->codec_dai;
+
+	/* Tabla SLIMBUS configuration
+	 * RX1, RX2, RX3, RX4, RX5, RX6, RX7
+	 * TX1, TX2, TX3, TX4, TX5, TX6, TX7, TX8, TX9, TX10
+	 */
+	unsigned int rx_ch[TABLA_RX_MAX] = {138, 139, 140, 141, 142, 143, 144};
+	unsigned int tx_ch[TABLA_TX_MAX]  = {128, 129, 130, 131, 132, 133, 134,
+					     135, 136, 137};
 
 	pr_debug("%s(), dev_name%s\n", __func__, dev_name(cpu_dai->dev));
 
@@ -1639,6 +1634,10 @@
 
 	err = tabla_hs_detect(codec, &mbhc_cfg);
 
+	snd_soc_dai_set_channel_map(codec_dai, ARRAY_SIZE(tx_ch),
+				    tx_ch, ARRAY_SIZE(rx_ch), rx_ch);
+
+
 	return err;
 }
 
diff --git a/sound/soc/msm/mpq8064.c b/sound/soc/msm/mpq8064.c
index 011b798..23703f2 100644
--- a/sound/soc/msm/mpq8064.c
+++ b/sound/soc/msm/mpq8064.c
@@ -724,13 +724,6 @@
 			pr_err("%s: failed to set cpu chan map\n", __func__);
 			goto end;
 		}
-		ret = snd_soc_dai_set_channel_map(codec_dai, 0, 0,
-				msm_slim_0_rx_ch, rx_ch);
-		if (ret < 0) {
-			pr_err("%s: failed to set codec channel map\n",
-								__func__);
-			goto end;
-		}
 	} else {
 		ret = snd_soc_dai_get_channel_map(codec_dai,
 				&tx_ch_cnt, tx_ch, &rx_ch_cnt , rx_ch);
@@ -744,14 +737,6 @@
 			pr_err("%s: failed to set cpu chan map\n", __func__);
 			goto end;
 		}
-		ret = snd_soc_dai_set_channel_map(codec_dai,
-				msm_slim_0_tx_ch, tx_ch, 0, 0);
-		if (ret < 0) {
-			pr_err("%s: failed to set codec channel map\n",
-								__func__);
-			goto end;
-		}
-
 
 	}
 end:
@@ -764,6 +749,10 @@
 	struct snd_soc_codec *codec = rtd->codec;
 	struct snd_soc_dapm_context *dapm = &codec->dapm;
 	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+	struct snd_soc_dai *codec_dai = rtd->codec_dai;
+	unsigned int rx_ch[TABLA_RX_MAX] = {138, 139, 140, 141, 142, 143, 144};
+	unsigned int tx_ch[TABLA_TX_MAX] = {128, 129, 130, 131, 132, 133, 134,
+					    135, 136, 137};
 
 	pr_debug("%s(), dev_name%s\n", __func__, dev_name(cpu_dai->dev));
 
@@ -798,6 +787,8 @@
 	codec_clk = clk_get(cpu_dai->dev, "osr_clk");
 
 	err = tabla_hs_detect(codec, &mbhc_cfg);
+	snd_soc_dai_set_channel_map(codec_dai, ARRAY_SIZE(tx_ch),
+				    tx_ch, ARRAY_SIZE(rx_ch), rx_ch);
 
 	return err;
 }
diff --git a/sound/soc/msm/msm8930.c b/sound/soc/msm/msm8930.c
index 4725e8e..76b7597 100644
--- a/sound/soc/msm/msm8930.c
+++ b/sound/soc/msm/msm8930.c
@@ -621,13 +621,6 @@
 			pr_err("%s: failed to set cpu chan map\n", __func__);
 			goto end;
 		}
-		ret = snd_soc_dai_set_channel_map(codec_dai, 0, 0,
-				msm8930_slim_0_rx_ch, rx_ch);
-		if (ret < 0) {
-			pr_err("%s: failed to set codec channel map\n",
-							       __func__);
-			goto end;
-		}
 	} else {
 		ret = snd_soc_dai_get_channel_map(codec_dai,
 				&tx_ch_cnt, tx_ch, &rx_ch_cnt , rx_ch);
@@ -641,14 +634,6 @@
 			pr_err("%s: failed to set cpu chan map\n", __func__);
 			goto end;
 		}
-		ret = snd_soc_dai_set_channel_map(codec_dai,
-				msm8930_slim_0_tx_ch, tx_ch, 0, 0);
-		if (ret < 0) {
-			pr_err("%s: failed to set codec channel map\n",
-							       __func__);
-			goto end;
-		}
-
 	}
 end:
 	return ret;
@@ -660,6 +645,14 @@
 	struct snd_soc_codec *codec = rtd->codec;
 	struct snd_soc_dapm_context *dapm = &codec->dapm;
 	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+	struct snd_soc_dai *codec_dai = rtd->codec_dai;
+
+	/* Tabla SLIMBUS configuration
+	 * RX1, RX2, RX3, RX4, RX5
+	 * TX1, TX2, TX3, TX4, TX5
+	 */
+	unsigned int rx_ch[SITAR_RX_MAX] = {138, 139, 140, 141, 142};
+	unsigned int tx_ch[SITAR_TX_MAX]  = {128, 129, 130, 131, 132};
 
 	pr_debug("%s()\n", __func__);
 
@@ -690,6 +683,9 @@
 	}
 	codec_clk = clk_get(cpu_dai->dev, "osr_clk");
 
+	snd_soc_dai_set_channel_map(codec_dai, ARRAY_SIZE(tx_ch),
+				    tx_ch, ARRAY_SIZE(rx_ch), rx_ch);
+
 	mbhc_cfg.gpio = 37;
 	mbhc_cfg.gpio_irq = gpio_to_irq(mbhc_cfg.gpio);
 	sitar_hs_detect(codec, &mbhc_cfg);
diff --git a/sound/soc/msm/msm8960.c b/sound/soc/msm/msm8960.c
index 6ec44bf..da7a129 100644
--- a/sound/soc/msm/msm8960.c
+++ b/sound/soc/msm/msm8960.c
@@ -777,13 +777,6 @@
 			pr_err("%s: failed to set cpu chan map\n", __func__);
 			goto end;
 		}
-		ret = snd_soc_dai_set_channel_map(codec_dai, 0, 0,
-				msm8960_slim_0_rx_ch, rx_ch);
-		if (ret < 0) {
-			pr_err("%s: failed to set codec channel map\n",
-								__func__);
-			goto end;
-		}
 	} else {
 
 		pr_debug("%s: %s  tx_dai_id = %d  num_ch = %d\n", __func__,
@@ -801,13 +794,6 @@
 			pr_err("%s: failed to set cpu chan map\n", __func__);
 			goto end;
 		}
-		ret = snd_soc_dai_set_channel_map(codec_dai,
-				msm8960_slim_0_tx_ch, tx_ch, 0, 0);
-		if (ret < 0) {
-			pr_err("%s: failed to set codec channel map\n",
-								__func__);
-			goto end;
-		}
 	}
 end:
 	return ret;
@@ -845,13 +831,6 @@
 			pr_err("%s: failed to set cpu chan map\n", __func__);
 			goto end;
 		}
-		ret = snd_soc_dai_set_channel_map(codec_dai, 0, 0,
-				num_rx_ch, rx_ch);
-		if (ret < 0) {
-			pr_err("%s: failed to set codec channel map\n",
-								__func__);
-			goto end;
-		}
 	} else {
 		num_tx_ch =  params_channels(params);
 
@@ -871,13 +850,6 @@
 			pr_err("%s: failed to set cpu chan map\n", __func__);
 			goto end;
 		}
-		ret = snd_soc_dai_set_channel_map(codec_dai,
-				num_tx_ch, tx_ch, 0, 0);
-		if (ret < 0) {
-			pr_err("%s: failed to set codec channel map\n",
-								__func__);
-			goto end;
-		}
 	}
 end:
 	return ret;
@@ -896,6 +868,15 @@
 		.vin_sel = 2,
 		.inv_int_pol = 0,
 	};
+	struct snd_soc_dai *codec_dai = rtd->codec_dai;
+
+	/* Tabla SLIMBUS configuration
+	 * RX1, RX2, RX3, RX4, RX5, RX6, RX7
+	 * TX1, TX2, TX3, TX4, TX5, TX6, TX7, TX8
+	 */
+	unsigned int rx_ch[TABLA_RX_MAX] = {138, 139, 140, 141, 142, 143, 144};
+	unsigned int tx_ch[TABLA_TX_MAX]  = {128, 129, 130, 131, 132, 133, 134,
+					     135, 136, 137};
 
 	pr_debug("%s(), dev_name%s\n", __func__, dev_name(cpu_dai->dev));
 
@@ -958,6 +939,8 @@
 	mbhc_cfg.read_fw_bin = hs_detect_use_firmware;
 
 	err = tabla_hs_detect(codec, &mbhc_cfg);
+	snd_soc_dai_set_channel_map(codec_dai, ARRAY_SIZE(tx_ch),
+				    tx_ch, ARRAY_SIZE(rx_ch), rx_ch);
 
 	return err;
 }
diff --git a/sound/soc/msm/msm8974.c b/sound/soc/msm/msm8974.c
index f462299..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;
@@ -524,6 +537,23 @@
 	return 0;
 }
 
+static int msm8974_hdmi_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+					struct snd_pcm_hw_params *params)
+{
+	struct snd_interval *rate = hw_param_interval(params,
+					SNDRV_PCM_HW_PARAM_RATE);
+
+	struct snd_interval *channels = hw_param_interval(params,
+					SNDRV_PCM_HW_PARAM_CHANNELS);
+
+	pr_debug("%s channels->min %u channels->max %u ()\n", __func__,
+			channels->min, channels->max);
+
+	rate->min = rate->max = 48000;
+
+	return 0;
+}
+
 static int msm_aux_pcm_get_gpios(void)
 {
 	int ret = 0;
@@ -639,6 +669,18 @@
 	return 0;
 }
 
+static int msm_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+				struct snd_pcm_hw_params *params)
+{
+	struct snd_interval *rate = hw_param_interval(params,
+					SNDRV_PCM_HW_PARAM_RATE);
+
+	pr_debug("%s()\n", __func__);
+	rate->min = rate->max = 48000;
+
+	return 0;
+}
+
 static const struct soc_enum msm_snd_enum[] = {
 	SOC_ENUM_SINGLE_EXT(2, spk_function),
 	SOC_ENUM_SINGLE_EXT(2, slim0_rx_ch_text),
@@ -660,6 +702,19 @@
 	struct snd_soc_codec *codec = rtd->codec;
 	struct snd_soc_dapm_context *dapm = &codec->dapm;
 	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+	struct snd_soc_dai *codec_dai = rtd->codec_dai;
+
+	/* Taiko SLIMBUS configuration
+	 * RX1, RX2, RX3, RX4, RX5, RX6, RX7, RX8, RX9, RX10, RX11, RX12, RX13
+	 * TX1, TX2, TX3, TX4, TX5, TX6, TX7, TX8, TX9, TX10, TX11, TX12, TX13
+	 * TX14, TX15, TX16
+	 */
+	unsigned int rx_ch[TAIKO_RX_MAX] = {144, 145, 146, 147, 148, 149, 150,
+					    151, 152, 153, 154, 155, 156};
+	unsigned int tx_ch[TAIKO_TX_MAX]  = {128, 129, 130, 131, 132, 133,
+					     134, 135, 136, 137, 138, 139,
+					     140, 141, 142, 143};
+
 
 	pr_info("%s(), dev_name%s\n", __func__, dev_name(cpu_dai->dev));
 
@@ -685,21 +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;
 }
@@ -711,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)
 {
@@ -737,20 +864,8 @@
 			pr_err("%s: failed to set cpu chan map\n", __func__);
 			goto end;
 		}
-		ret = snd_soc_dai_set_channel_map(codec_dai, 0, 0,
-						  msm_slim_0_rx_ch, rx_ch);
-		if (ret < 0) {
-			pr_err("%s: failed to set codec channel map\n",
-			       __func__);
-			goto end;
-		}
 	} else {
 
-		if (codec_dai->id == 2)
-			user_set_tx_ch = msm_slim_0_tx_ch;
-		else if (codec_dai->id == 4)
-			user_set_tx_ch = params_channels(params);
-
 		pr_debug("%s: %s_tx_dai_id_%d_ch=%d\n", __func__,
 			 codec_dai->name, codec_dai->id, user_set_tx_ch);
 
@@ -760,19 +875,24 @@
 			pr_err("%s: failed to get codec chan map\n", __func__);
 			goto end;
 		}
+		/* For tabla_tx1 case */
+		if (codec_dai->id == 1)
+			user_set_tx_ch = msm_slim_0_tx_ch;
+		/* For tabla_tx2 case */
+		else if (codec_dai->id == 3)
+			user_set_tx_ch = params_channels(params);
+		else
+			user_set_tx_ch = tx_ch_cnt;
+
+		pr_debug("%s: msm_slim_0_tx_ch(%d)user_set_tx_ch(%d)tx_ch_cnt(%d)\n",
+			 __func__, msm_slim_0_tx_ch, user_set_tx_ch, tx_ch_cnt);
+
 		ret = snd_soc_dai_set_channel_map(cpu_dai,
 						  user_set_tx_ch, tx_ch, 0 , 0);
 		if (ret < 0) {
 			pr_err("%s: failed to set cpu chan map\n", __func__);
 			goto end;
 		}
-		ret = snd_soc_dai_set_channel_map(codec_dai,
-						  user_set_tx_ch, tx_ch, 0, 0);
-		if (ret < 0) {
-			pr_err("%s: failed to set codec channel map\n",
-			       __func__);
-			goto end;
-		}
 	}
 end:
 	return ret;
@@ -974,6 +1094,30 @@
 		.be_id = MSM_BACKEND_DAI_INT_BT_SCO_TX,
 		.be_hw_params_fixup = msm_btsco_be_hw_params_fixup,
 	},
+	{
+		.name = LPASS_BE_INT_FM_RX,
+		.stream_name = "Internal FM Playback",
+		.cpu_dai_name = "msm-dai-q6-dev.12292",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-rx",
+		.no_pcm = 1,
+		.be_id = MSM_BACKEND_DAI_INT_FM_RX,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		/* this dainlink has playback support */
+		.ignore_pmdown_time = 1,
+	},
+	{
+		.name = LPASS_BE_INT_FM_TX,
+		.stream_name = "Internal FM Capture",
+		.cpu_dai_name = "msm-dai-q6-dev.12293",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-tx",
+		.no_pcm = 1,
+		.be_id = MSM_BACKEND_DAI_INT_FM_TX,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+	},
 	/* Backend AFE DAI Links */
 	{
 		.name = LPASS_BE_AFE_PCM_RX,
@@ -999,6 +1143,34 @@
 		.be_id = MSM_BACKEND_DAI_AFE_PCM_TX,
 		.be_hw_params_fixup = msm_proxy_be_hw_params_fixup,
 	},
+	/* HDMI Hostless */
+	{
+		.name = "HDMI_RX_HOSTLESS",
+		.stream_name = "HDMI_RX_HOSTLESS",
+		.cpu_dai_name = "HDMI_HOSTLESS",
+		.platform_name = "msm-pcm-hostless",
+		.dynamic = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			SND_SOC_DPCM_TRIGGER_POST},
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+	},
+	/* HDMI BACK END DAI Link */
+	{
+		.name = LPASS_BE_HDMI,
+		.stream_name = "HDMI Playback",
+		.cpu_dai_name = "msm-dai-q6-hdmi.8",
+		.platform_name = "msm-pcm-routing",
+		.codec_name     = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-rx",
+		.no_pcm = 1,
+		.be_id = MSM_BACKEND_DAI_HDMI_RX,
+		.be_hw_params_fixup = msm8974_hdmi_be_hw_params_fixup,
+		.ignore_pmdown_time = 1,
+	},
 	/* AUX PCM Backend DAI Links */
 	{
 		.name = LPASS_BE_AUXPCM_RX,
diff --git a/sound/soc/msm/qdsp6v2/Makefile b/sound/soc/msm/qdsp6v2/Makefile
index acb073d..1d11907 100644
--- a/sound/soc/msm/qdsp6v2/Makefile
+++ b/sound/soc/msm/qdsp6v2/Makefile
@@ -1,5 +1,5 @@
 snd-soc-qdsp6v2-objs += msm-dai-q6-v2.o msm-pcm-q6-v2.o msm-pcm-routing-v2.o msm-compr-q6-v2.o  msm-multi-ch-pcm-q6-v2.o
-snd-soc-qdsp6v2-objs += msm-pcm-lpa-v2.o msm-pcm-afe-v2.o msm-pcm-voip-v2.o msm-pcm-voice-v2.o
+snd-soc-qdsp6v2-objs += msm-pcm-lpa-v2.o msm-pcm-afe-v2.o msm-pcm-voip-v2.o msm-pcm-voice-v2.o msm-dai-q6-hdmi-v2.o
 obj-$(CONFIG_SND_SOC_QDSP6V2) += snd-soc-qdsp6v2.o
 obj-y += q6adm.o q6afe.o q6asm.o q6audio-v2.o q6voice.o q6core.o
 ocmem-audio-objs += audio_ocmem.o
diff --git a/sound/soc/msm/qdsp6v2/msm-dai-q6-hdmi-v2.c b/sound/soc/msm/qdsp6v2/msm-dai-q6-hdmi-v2.c
new file mode 100644
index 0000000..f80b589
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/msm-dai-q6-hdmi-v2.c
@@ -0,0 +1,320 @@
+/* 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/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/bitops.h>
+#include <linux/slab.h>
+#include <linux/of_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/apr_audio-v2.h>
+#include <sound/q6afe-v2.h>
+#include <sound/msm-dai-q6-v2.h>
+#include <mach/msm_hdmi_audio.h>
+
+
+enum {
+	STATUS_PORT_STARTED, /* track if AFE port has started */
+	STATUS_MAX
+};
+
+struct msm_dai_q6_hdmi_dai_data {
+	DECLARE_BITMAP(status_mask, STATUS_MAX);
+	u32 rate;
+	u32 channels;
+	union afe_port_config port_config;
+};
+
+static int msm_dai_q6_hdmi_format_put(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+
+	struct msm_dai_q6_hdmi_dai_data *dai_data = kcontrol->private_data;
+	int value = ucontrol->value.integer.value[0];
+	dai_data->port_config.hdmi_multi_ch.datatype = value;
+	pr_debug("%s: value = %d\n", __func__, value);
+	return 0;
+}
+
+static int msm_dai_q6_hdmi_format_get(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+
+	struct msm_dai_q6_hdmi_dai_data *dai_data = kcontrol->private_data;
+	ucontrol->value.integer.value[0] =
+		dai_data->port_config.hdmi_multi_ch.datatype;
+	return 0;
+}
+
+
+/* HDMI format field for AFE_PORT_MULTI_CHAN_HDMI_AUDIO_IF_CONFIG command
+ *  0: linear PCM
+ *  1: non-linear PCM
+ */
+static const char * const hdmi_format[] = {
+	"LPCM",
+	"Compr"
+};
+
+static const struct soc_enum hdmi_config_enum[] = {
+	SOC_ENUM_SINGLE_EXT(2, hdmi_format),
+};
+
+static const struct snd_kcontrol_new hdmi_config_controls[] = {
+	SOC_ENUM_EXT("HDMI RX Format", hdmi_config_enum[0],
+				 msm_dai_q6_hdmi_format_get,
+				 msm_dai_q6_hdmi_format_put),
+};
+
+/* Current implementation assumes hw_param is called once
+ * This may not be the case but what to do when ADM and AFE
+ * port are already opened and parameter changes
+ */
+static int msm_dai_q6_hdmi_hw_params(struct snd_pcm_substream *substream,
+				struct snd_pcm_hw_params *params,
+				struct snd_soc_dai *dai)
+{
+	struct msm_dai_q6_hdmi_dai_data *dai_data = dev_get_drvdata(dai->dev);
+	u32 channel_allocation = 0;
+	u32 level_shift  = 0; /* 0dB */
+	bool down_mix = FALSE;
+
+	dai_data->channels = params_channels(params);
+	dai_data->rate = params_rate(params);
+	dai_data->port_config.hdmi_multi_ch.reserved = 0;
+	dai_data->port_config.hdmi_multi_ch.hdmi_cfg_minor_version = 1;
+	dai_data->port_config.hdmi_multi_ch.sample_rate = dai_data->rate;
+	dai_data->port_config.hdmi_multi_ch.bit_width = 16;
+
+	switch (dai_data->channels) {
+	case 2:
+		channel_allocation  = 0;
+		hdmi_msm_audio_info_setup(1, MSM_HDMI_AUDIO_CHANNEL_2,
+				channel_allocation, level_shift, down_mix);
+		dai_data->port_config.hdmi_multi_ch.channel_allocation =
+			channel_allocation;
+		break;
+	case 6:
+		channel_allocation  = 0x0B;
+		hdmi_msm_audio_info_setup(1, MSM_HDMI_AUDIO_CHANNEL_6,
+				channel_allocation, level_shift, down_mix);
+		dai_data->port_config.hdmi_multi_ch.channel_allocation =
+				channel_allocation;
+		break;
+	case 8:
+		channel_allocation  = 0x1F;
+		hdmi_msm_audio_info_setup(1, MSM_HDMI_AUDIO_CHANNEL_8,
+				channel_allocation, level_shift, down_mix);
+		dai_data->port_config.hdmi_multi_ch.channel_allocation =
+				channel_allocation;
+		break;
+	default:
+		dev_err(dai->dev, "invalid Channels = %u\n",
+				dai_data->channels);
+		return -EINVAL;
+	}
+	dev_dbg(dai->dev, "%s() minor version: %u samplerate: %u bitwidth: %u num_ch = %u channel_allocation = %u datatype = %d\n",
+		 __func__,
+		dai_data->port_config.hdmi_multi_ch.hdmi_cfg_minor_version,
+		dai_data->port_config.hdmi_multi_ch.sample_rate,
+		dai_data->port_config.hdmi_multi_ch.bit_width,
+		dai_data->channels,
+		dai_data->port_config.hdmi_multi_ch.channel_allocation,
+		dai_data->port_config.hdmi_multi_ch.datatype);
+
+	return 0;
+}
+
+
+static void msm_dai_q6_hdmi_shutdown(struct snd_pcm_substream *substream,
+				struct snd_soc_dai *dai)
+{
+	struct msm_dai_q6_hdmi_dai_data *dai_data = dev_get_drvdata(dai->dev);
+	int rc = 0;
+
+	if (!test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) {
+		pr_info("%s:  afe port not started. dai_data->status_mask = %ld\n",
+		 __func__, *dai_data->status_mask);
+		return;
+	}
+
+	rc = afe_close(dai->id); /* can block */
+
+	if (IS_ERR_VALUE(rc))
+		dev_err(dai->dev, "fail to close AFE port\n");
+
+	pr_debug("%s: dai_data->status_mask = %ld\n", __func__,
+			*dai_data->status_mask);
+
+	clear_bit(STATUS_PORT_STARTED, dai_data->status_mask);
+}
+
+
+static int msm_dai_q6_hdmi_prepare(struct snd_pcm_substream *substream,
+		struct snd_soc_dai *dai)
+{
+	struct msm_dai_q6_hdmi_dai_data *dai_data = dev_get_drvdata(dai->dev);
+	int rc = 0;
+
+	if (!test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) {
+		rc = afe_port_start(dai->id, &dai_data->port_config,
+				    dai_data->rate);
+		if (IS_ERR_VALUE(rc))
+			dev_err(dai->dev, "fail to open AFE port %x\n",
+				dai->id);
+		else
+			set_bit(STATUS_PORT_STARTED,
+				dai_data->status_mask);
+	}
+
+	return rc;
+}
+
+static int msm_dai_q6_hdmi_dai_probe(struct snd_soc_dai *dai)
+{
+	struct msm_dai_q6_hdmi_dai_data *dai_data;
+	const struct snd_kcontrol_new *kcontrol;
+	int rc = 0;
+
+	dai_data = kzalloc(sizeof(struct msm_dai_q6_hdmi_dai_data),
+		GFP_KERNEL);
+
+	if (!dai_data) {
+		dev_err(dai->dev, "DAI-%d: fail to allocate dai data\n",
+		dai->id);
+		rc = -ENOMEM;
+	} else
+		dev_set_drvdata(dai->dev, dai_data);
+
+	kcontrol = &hdmi_config_controls[0];
+
+	rc = snd_ctl_add(dai->card->snd_card,
+					 snd_ctl_new1(kcontrol, dai_data));
+	return rc;
+}
+
+static int msm_dai_q6_hdmi_dai_remove(struct snd_soc_dai *dai)
+{
+	struct msm_dai_q6_hdmi_dai_data *dai_data;
+	int rc;
+
+	dai_data = dev_get_drvdata(dai->dev);
+
+	/* If AFE port is still up, close it */
+	if (test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) {
+		rc = afe_close(dai->id); /* can block */
+
+		if (IS_ERR_VALUE(rc))
+			dev_err(dai->dev, "fail to close AFE port\n");
+
+		clear_bit(STATUS_PORT_STARTED, dai_data->status_mask);
+	}
+	kfree(dai_data);
+	snd_soc_unregister_dai(dai->dev);
+
+	return 0;
+}
+
+static struct snd_soc_dai_ops msm_dai_q6_hdmi_ops = {
+	.prepare	= msm_dai_q6_hdmi_prepare,
+	.hw_params	= msm_dai_q6_hdmi_hw_params,
+	.shutdown	= msm_dai_q6_hdmi_shutdown,
+};
+
+static struct snd_soc_dai_driver msm_dai_q6_hdmi_hdmi_rx_dai = {
+	.playback = {
+		.rates = SNDRV_PCM_RATE_48000,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE,
+		.channels_min = 2,
+		.channels_max = 6,
+		.rate_max =     48000,
+		.rate_min =	48000,
+	},
+	.ops = &msm_dai_q6_hdmi_ops,
+	.probe = msm_dai_q6_hdmi_dai_probe,
+	.remove = msm_dai_q6_hdmi_dai_remove,
+};
+
+
+/* To do: change to register DAIs as batch */
+static __devinit int msm_dai_q6_hdmi_dev_probe(struct platform_device *pdev)
+{
+	int rc, id;
+	const char *q6_dev_id = "qcom,msm-dai-q6-dev-id";
+
+	rc = of_property_read_u32(pdev->dev.of_node, q6_dev_id, &id);
+	if (rc) {
+		dev_err(&pdev->dev,
+			"%s: missing %s in dt node\n", __func__, q6_dev_id);
+		return rc;
+	}
+
+	pdev->id = id;
+	dev_set_name(&pdev->dev, "%s.%d", "msm-dai-q6-hdmi", id);
+
+	pr_debug("%s: dev name %s, id:%d\n", __func__,
+			dev_name(&pdev->dev), pdev->id);
+
+	switch (pdev->id) {
+	case HDMI_RX:
+		rc = snd_soc_register_dai(&pdev->dev,
+				&msm_dai_q6_hdmi_hdmi_rx_dai);
+		break;
+	default:
+		dev_err(&pdev->dev, "invalid device ID %d\n", pdev->id);
+		rc = -ENODEV;
+		break;
+	}
+	return rc;
+}
+
+static __devexit int msm_dai_q6_hdmi_dev_remove(struct platform_device *pdev)
+{
+	snd_soc_unregister_dai(&pdev->dev);
+	return 0;
+}
+
+static const struct of_device_id msm_dai_q6_hdmi_dt_match[] = {
+	{.compatible = "qcom,msm-dai-q6-hdmi"},
+	{}
+};
+MODULE_DEVICE_TABLE(of, msm_dai_q6_hdmi_dt_match);
+
+static struct platform_driver msm_dai_q6_hdmi_driver = {
+	.probe  = msm_dai_q6_hdmi_dev_probe,
+	.remove = msm_dai_q6_hdmi_dev_remove,
+	.driver = {
+		.name = "msm-dai-q6-hdmi",
+		.owner = THIS_MODULE,
+		.of_match_table = msm_dai_q6_hdmi_dt_match,
+	},
+};
+
+static int __init msm_dai_q6_hdmi_init(void)
+{
+	return platform_driver_register(&msm_dai_q6_hdmi_driver);
+}
+module_init(msm_dai_q6_hdmi_init);
+
+static void __exit msm_dai_q6_hdmi_exit(void)
+{
+	platform_driver_unregister(&msm_dai_q6_hdmi_driver);
+}
+module_exit(msm_dai_q6_hdmi_exit);
+
+/* Module information */
+MODULE_DESCRIPTION("MSM DSP HDMI DAI driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c
index dacd59c..354dece 100644
--- a/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c
@@ -899,6 +899,36 @@
 	.remove = msm_dai_q6_dai_remove,
 };
 
+static struct snd_soc_dai_driver msm_dai_q6_fm_rx_dai = {
+	.playback = {
+		.rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+		SNDRV_PCM_RATE_16000,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE,
+		.channels_min = 2,
+		.channels_max = 2,
+		.rate_max = 48000,
+		.rate_min = 8000,
+	},
+	.ops = &msm_dai_q6_ops,
+	.probe = msm_dai_q6_dai_probe,
+	.remove = msm_dai_q6_dai_remove,
+};
+
+static struct snd_soc_dai_driver msm_dai_q6_fm_tx_dai = {
+	.capture = {
+		.rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+		SNDRV_PCM_RATE_16000,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE,
+		.channels_min = 2,
+		.channels_max = 2,
+		.rate_max = 48000,
+		.rate_min = 8000,
+	},
+	.ops = &msm_dai_q6_ops,
+	.probe = msm_dai_q6_dai_probe,
+	.remove = msm_dai_q6_dai_remove,
+};
+
 static int __devinit msm_auxpcm_dev_probe(struct platform_device *pdev)
 {
 	int id;
@@ -1158,6 +1188,12 @@
 		rc = snd_soc_register_dai(&pdev->dev,
 					&msm_dai_q6_bt_sco_tx_dai);
 		break;
+	case INT_FM_RX:
+		rc = snd_soc_register_dai(&pdev->dev, &msm_dai_q6_fm_rx_dai);
+		break;
+	case INT_FM_TX:
+		rc = snd_soc_register_dai(&pdev->dev, &msm_dai_q6_fm_tx_dai);
+		break;
 	case RT_PROXY_DAI_001_RX:
 	case RT_PROXY_DAI_002_RX:
 		rc = snd_soc_register_dai(&pdev->dev, &msm_dai_q6_afe_rx_dai);
diff --git a/sound/soc/msm/qdsp6v2/q6afe.c b/sound/soc/msm/qdsp6v2/q6afe.c
index f0465a5..4819e0a 100644
--- a/sound/soc/msm/qdsp6v2/q6afe.c
+++ b/sound/soc/msm/qdsp6v2/q6afe.c
@@ -344,6 +344,8 @@
 		break;
 	case INT_BT_SCO_RX:
 	case INT_BT_SCO_TX:
+	case INT_FM_RX:
+	case INT_FM_TX:
 		cfg_type = AFE_PARAM_ID_INTERNAL_BT_FM_CONFIG;
 		break;
 	default: