Merge "ARM: dts: msm: Remove reset configuration for pm8226"
diff --git a/Documentation/devicetree/bindings/arm/msm/cpr-regulator.txt b/Documentation/devicetree/bindings/arm/msm/cpr-regulator.txt
index b489f7a..4e3abc2 100644
--- a/Documentation/devicetree/bindings/arm/msm/cpr-regulator.txt
+++ b/Documentation/devicetree/bindings/arm/msm/cpr-regulator.txt
@@ -136,6 +136,8 @@
 					is present, and vise versa.
 - qcom,cpr-enable:		Present: CPR enabled by default.
 				Not Present: CPR disable by default.
+- qcom,use-tz-api:		Present: CPR reads efuse parameters through trustzone API.
+				Not Present: CPR reads efuse parameters directly.
 
 
 Example:
diff --git a/Documentation/devicetree/bindings/arm/msm/rpm-master-stats.txt b/Documentation/devicetree/bindings/arm/msm/rpm-master-stats.txt
new file mode 100644
index 0000000..0239674
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/msm/rpm-master-stats.txt
@@ -0,0 +1,29 @@
+* RPM Master Stats
+
+RPM maintains each master data in RPM message RAM at a specific
+offset. It tells about the individual masters information at
+any given time like "number of active cores in sub system",
+"number of shutdowns" and "wakeup reason for SS" etc. These stats
+can be show to the user using the debugfs interface of the kernel.
+To achieve this device tree node has been added and it will hold
+the address of the RPM RAM from where master stats are read.
+Added version number to distinguish the type of data structure
+being read from the RAM for different targets.
+
+The required properties for rpm-master-stats are:
+
+- compatible: "qcom,rpm-master-stats".
+- reg: The address on the RPM RAM from where stats are read.
+- qcom,masters: Each master name.
+- qcom,master-offset: Offset required to access each master stats area.
+- qcom,master-stats-version: Version number.
+
+Example:
+
+qcom,rpm-stats@fc428150 {
+		compatible = "qcom,rpm-stats";
+		reg = <0xfc428150 0x1000>;
+		qcom,masters = "APSS", "MPSS", "LPSS", "PRONTO";
+		qcom,master-offset = <2560>;
+		qcom,master-stats-version = <2>;
+};
diff --git a/Documentation/devicetree/bindings/arm/msm/spm-v2.txt b/Documentation/devicetree/bindings/arm/msm/spm-v2.txt
index a2d8359..d9a0d59 100644
--- a/Documentation/devicetree/bindings/arm/msm/spm-v2.txt
+++ b/Documentation/devicetree/bindings/arm/msm/spm-v2.txt
@@ -42,6 +42,10 @@
 - qcom,saw2-spm-cmd-spc: The Standalone PC command sequence
 - qcom,saw2-spm-cmd-pc: The Power Collapse command sequence
 - qcom,saw2-spm-cmd-gdhs: L2 GDHS command sequence
+- qcom,L2-spm-is-apcs-master: Boolean indicates if the target uses L2 SAW to
+	control the gang rail. If this is not specified the driver assumes
+	that the cpus run with their own separate rails and each cpu's spm
+	talks to its regulator.
 
 Example:
 	qcom,spm@f9089000 {
diff --git a/Documentation/devicetree/bindings/batterydata/batterydata.txt b/Documentation/devicetree/bindings/batterydata/batterydata.txt
index 985fb4c..8fbb1bd 100644
--- a/Documentation/devicetree/bindings/batterydata/batterydata.txt
+++ b/Documentation/devicetree/bindings/batterydata/batterydata.txt
@@ -22,7 +22,10 @@
 - qcom,v-cutoff-uv : The cutoff voltage of the battery at which the device
 			should shutdown gracefully.
 - qcom,chg-term-ua : The termination charging current of the battery.
-- qcom,batt-id-kohm : The battery id resistance of the battery.
+- qcom,batt-id-kohm : The battery id resistance of the battery. It can be
+			used as an array which could support multiple IDs for one battery
+			module when the ID resistance of some battery modules goes across
+			several ranges.
 
 Profile data node required subnodes:
 - qcom,fcc-temp-lut : An 1-dimensional lookup table node that encodes
diff --git a/Documentation/devicetree/bindings/input/touchscreen/ft5x06-ts.txt b/Documentation/devicetree/bindings/input/touchscreen/ft5x06-ts.txt
index ce60d8d..c563067e 100644
--- a/Documentation/devicetree/bindings/input/touchscreen/ft5x06-ts.txt
+++ b/Documentation/devicetree/bindings/input/touchscreen/ft5x06-ts.txt
@@ -45,6 +45,8 @@
  - focaltech,fw-delay-readid-ms : specify the read id delay in ms for firmware upgrade
  - focaltech,fw-delay-era-flsh-ms : specify the erase flash delay in ms for firmware upgrade
  - focaltech,fw-auto-cal	: specify whether calibration is needed after firmware upgrade
+ - focaltech,fw-vkey-support	: specify if virtual keys are supported through firmware
+
 Example:
 	i2c@f9923000{
 		focaltech@38{
diff --git a/Documentation/devicetree/bindings/leds/leds-qpnp.txt b/Documentation/devicetree/bindings/leds/leds-qpnp.txt
index 9524360..f7e50eb 100644
--- a/Documentation/devicetree/bindings/leds/leds-qpnp.txt
+++ b/Documentation/devicetree/bindings/leds/leds-qpnp.txt
@@ -45,9 +45,9 @@
 - qcom,saftey-timer: include for safety timer use, otherwise watchdog timer will be used
 - linux,default-trigger: trigger the led from external modules such as display
 - qcom,default-state:  default state of the led, should be "on" or "off"
-- qcom,torch-enable: set flash led to torch mode
-- flash_boost-supply: SMBB regulator for LED flash mode
-- torch_boost-supply: SMBB regulator for LED torch mode
+- qcom,torch-enable: set flash led to torch mode functionality and triggers software workaround for torch if hardware does not support
+- flash-boost-supply: SMBB regulator for LED flash mode
+- torch-boost-supply: SMBB regulator for LED torch mode
 
 RGB Led is a tri-colored led, Red, Blue & Green.
 
@@ -222,8 +222,8 @@
 	qcom,leds@d300 {
 			compatible = "qcom,leds-qpnp";
 			status = "okay";
-			flash_boost-supply = <&pm8941_chg_boost>;
-			torch_boost-supply = <&pm8941_boost>;
+			flash-boost-supply = <&pm8941_chg_boost>;
+			torch-boost-supply = <&pm8941_boost>;
 			qcom,flash_0 {
 				qcom,max-current = <1000>;
 				qcom,default-state = "off";
@@ -238,6 +238,7 @@
 				linux,name = "led:flash_0";
 				qcom,current = <625>;
 				qcom,id = <1>;
+				qcom,no-torch-module;
 			};
 	};
 
diff --git a/Documentation/devicetree/bindings/mmc/sdhci-msm.txt b/Documentation/devicetree/bindings/mmc/sdhci-msm.txt
index 013d56e..d502f78 100644
--- a/Documentation/devicetree/bindings/mmc/sdhci-msm.txt
+++ b/Documentation/devicetree/bindings/mmc/sdhci-msm.txt
@@ -73,7 +73,8 @@
 	- qcom,pad-pull-off - Suspend pull configuration for sdc tlmm pins.
 	- qcom,pad-drv-on - Active drive strength configuration for sdc tlmm pins.
 	- qcom,pad-drv-off - Suspend drive strength configuration for sdc tlmm pins.
-	Tlmm pins are specified as <clk cmd data>
+	Tlmm pins are specified as <clk cmd data> and starting with eMMC5.0 as
+	<clk cmd data rclk>
 
 	- qcom,bus-bw-vectors-bps: specifies array of throughput values in
 	Bytes/sec. The values in the array are determined according to
diff --git a/Documentation/devicetree/bindings/power/qpnp-charger.txt b/Documentation/devicetree/bindings/power/qpnp-charger.txt
index a696746..b1c4ebb 100644
--- a/Documentation/devicetree/bindings/power/qpnp-charger.txt
+++ b/Documentation/devicetree/bindings/power/qpnp-charger.txt
@@ -181,6 +181,9 @@
 			 - regulator-name:	A string used as a descriptive name
 						for the boost regulator.
 
+			qcom,batfet:
+			 - regulator-name:	A string used as a descriptive name
+						for the batfet regulator.
 Example:
 	pm8941-chg {
 		spmi-dev-container;
@@ -309,6 +312,10 @@
 		regulator-name = "8941_smbb_boost";
 	};
 
+	&pm8941_chg_batif  {
+		regulator-name = "batfet";
+	};
+
 	&pm8941_chg_otg {
 		regulator-name = "8941_smbb_otg";
 	};
diff --git a/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt b/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt
index b618597..6e4f7fc 100644
--- a/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt
+++ b/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt
@@ -434,7 +434,8 @@
 			    amplifier.
 
 - qcom,headset-jack-type-NO: Adjust GPIO level based on the headset jack type.
-
+- qcom,tapan-codec-9302: Indicates that this device node is for WCD9302 audio
+			    codec.
 
 * APQ8074 ASoC Machine driver
 
@@ -664,6 +665,7 @@
 - qcom,cdc-us-euro-gpios : GPIO on which gnd/mic swap signal is coming.
 - qcom,cdc-lineout-spkr-gpios : GPIO which controls external PAs to enable Lineout1/2 speaker
 - qcom,cdc-vdd-spkr-gpios : GPIO which controls PA for VDD speaker
+- qcom,headset-jack-type-NC: Set if the headset jack type is NC (Normally Closed)
 
 Example:
 
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 69b2cd2..58fc698 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -290,6 +290,14 @@
 	 timer counter page should be mapped by the kernel.  User-space apps
 	 will read directly from the page at this address.
 
+config ARCH_RANDOM
+	bool "SOC specific random number generation"
+	help
+	 Allow the kernel to use an architecture specific implementation for
+	 random number generation
+
+	 If unsure, say N
+
 source "init/Kconfig"
 
 source "kernel/Kconfig.freezer"
diff --git a/arch/arm/boot/dts/apq8026-v1-mtp.dts b/arch/arm/boot/dts/apq8026-v1-mtp.dts
index 7900ddf..87a0271 100644
--- a/arch/arm/boot/dts/apq8026-v1-mtp.dts
+++ b/arch/arm/boot/dts/apq8026-v1-mtp.dts
@@ -22,8 +22,8 @@
 };
 
 &cci {
-	/* Rotate rear camera to 0 degrees */
+	/* Rotate rear camera to 180 degrees */
 	qcom,camera@6f {
-	qcom,mount-angle = <0>;
+	qcom,mount-angle = <180>;
 	};
 };
diff --git a/arch/arm/boot/dts/batterydata-qrd-4v2-1300mah.dtsi b/arch/arm/boot/dts/batterydata-qrd-4v2-1300mah.dtsi
new file mode 100644
index 0000000..103da50
--- /dev/null
+++ b/arch/arm/boot/dts/batterydata-qrd-4v2-1300mah.dtsi
@@ -0,0 +1,105 @@
+/* Copyright (c) 2013, 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.
+ */
+
+qcom,qrd-4v2-1300mah-data {
+	qcom,fcc-mah = <1300>;
+	qcom,default-rbatt-mohm = <172>;
+	qcom,rbatt-capacitive-mohm = <0>;
+	qcom,flat-ocv-threshold-uv = <3800000>;
+	qcom,max-voltage-uv = <4200000>;
+	qcom,v-cutoff-uv = <3400000>;
+	qcom,chg-term-ua = <100000>;
+	qcom,batt-id-kohm = <100>;
+
+	qcom,rbatt-sf-lut {
+		qcom,lut-col-legend = <(-20) 0 25 40 60>;
+		qcom,lut-row-legend = <100 95 90 85 80>,
+				<75 70 65 60 55>,
+				<50 45 40 35 30>,
+				<25 20 15 10 9>,
+				<8 7 6 5 4>,
+				<3 2 1 0>;
+		qcom,lut-data = <604 192 100 79 71>,
+				<605 192 100 79 71>,
+				<641 205 103 81 72>,
+				<641 221 108 84 75>,
+				<622 238 115 87 77>,
+				<612 254 123 92 79>,
+				<605 252 137 96 83>,
+				<607 219 154 104 87>,
+				<613 202 135 109 89>,
+				<626 200 106 90 77>,
+				<656 201 101 82 75>,
+				<684 204 100 84 77>,
+				<710 211 100 85 79>,
+				<747 224 106 89 82>,
+				<806 241 116 90 80>,
+				<905 260 119 87 77>,
+				<1046 291 113 87 77>,
+				<1309 329 116 90 79>,
+				<1476 300 126 97 83>,
+				<1598 311 127 98 84>,
+				<1771 323 130 99 85>,
+				<1984 342 136 101 86>,
+				<2438 368 140 101 86>,
+				<3381 388 137 100 84>,
+				<4913 414 141 99 86>,
+				<6979 468 155 104 90>,
+				<9968 565 192 113 98>,
+				<16163 833 350 140 120>,
+				<36511 6483 4872 472 1095>;
+	};
+
+	qcom,fcc-temp-lut {
+		qcom,lut-col-legend = <(-20) 0 25 40 60>;
+		qcom,lut-data = <1343 1353 1408 1345 1342>;
+	};
+
+	qcom,pc-temp-ocv-lut {
+		qcom,lut-col-legend = <(-20) 0 25 40 60>;
+		qcom,lut-row-legend = <100 95 90 85 80>,
+				<75 70 65 60 55>,
+				<50 45 40 35 30>,
+				<25 20 15 10 9>,
+				<8 7 6 5 4>,
+				<3 2 1 0>;
+		qcom,lut-data = <4177 4174 4199 4167 4162>,
+				<4107 4112 4141 4109 4106>,
+				<4058 4064 4091 4061 4059>,
+				<3996 4015 4044 4017 4015>,
+				<3947 3975 4001 3978 3976>,
+				<3909 3939 3962 3943 3940>,
+				<3874 3901 3926 3911 3907>,
+				<3845 3858 3892 3882 3878>,
+				<3821 3826 3851 3849 3846>,
+				<3801 3804 3815 3810 3808>,
+				<3788 3789 3793 3789 3787>,
+				<3778 3780 3778 3776 3773>,
+				<3769 3776 3770 3767 3764>,
+				<3757 3772 3766 3762 3757>,
+				<3740 3765 3762 3754 3744>,
+				<3714 3747 3750 3739 3724>,
+				<3668 3706 3717 3710 3697>,
+				<3602 3644 3662 3662 3654>,
+				<3533 3571 3601 3607 3605>,
+				<3518 3557 3583 3592 3590>,
+				<3500 3543 3565 3576 3574>,
+				<3478 3528 3546 3559 3557>,
+				<3451 3506 3521 3538 3534>,
+				<3417 3473 3481 3505 3496>,
+				<3377 3423 3424 3454 3444>,
+				<3327 3361 3351 3391 3380>,
+				<3261 3279 3258 3310 3297>,
+				<3165 3165 3138 3198 3182>,
+				<3000 3000 3000 3000 3000>;
+	};
+};
diff --git a/arch/arm/boot/dts/batterydata-qrd-4v2-2000mah.dtsi b/arch/arm/boot/dts/batterydata-qrd-4v2-2000mah.dtsi
new file mode 100644
index 0000000..f24513c
--- /dev/null
+++ b/arch/arm/boot/dts/batterydata-qrd-4v2-2000mah.dtsi
@@ -0,0 +1,105 @@
+/* Copyright (c) 2013, 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.
+ */
+
+qcom,qrd-4v2-2000mah-data {
+	qcom,fcc-mah = <2000>;
+	qcom,default-rbatt-mohm = <138>;
+	qcom,rbatt-capacitive-mohm = <0>;
+	qcom,flat-ocv-threshold-uv = <3800000>;
+	qcom,max-voltage-uv = <4200000>;
+	qcom,v-cutoff-uv = <3400000>;
+	qcom,chg-term-ua = <100000>;
+	qcom,batt-id-kohm = <130 115>;
+
+	qcom,rbatt-sf-lut {
+		qcom,lut-col-legend = <(-20) 0 25 40 60>;
+		qcom,lut-row-legend = <100 95 90 85 80>,
+				<75 70 65 60 55>,
+				<50 45 40 35 30>,
+				<25 20 15 10 9>,
+				<8 7 6 5 4>,
+				<3 2 1 0>;
+		qcom,lut-data = <1191 250 100 78 69>,
+				<1192 250 100 78 69>,
+				<1242 263 102 80 70>,
+				<1192 277 105 81 71>,
+				<1202 294 108 83 72>,
+				<1205 308 113 86 73>,
+				<1234 313 118 90 76>,
+				<1255 294 131 96 80>,
+				<1284 276 139 103 84>,
+				<1321 271 115 89 74>,
+				<1363 273 104 81 72>,
+				<1400 283 103 79 71>,
+				<1404 295 105 81 71>,
+				<1428 313 108 84 73>,
+				<1489 341 112 87 74>,
+				<1615 374 115 84 73>,
+				<1813 404 117 85 73>,
+				<2121 387 127 89 76>,
+				<2310 366 115 88 77>,
+				<2543 386 116 89 77>,
+				<2879 415 118 92 78>,
+				<3633 457 122 93 78>,
+				<5901 507 128 94 79>,
+				<9939 561 131 93 79>,
+				<16631 634 129 94 78>,
+				<26968 794 134 97 81>,
+				<42610 1203 145 102 85>,
+				<64024 2628 174 115 94>,
+				<115224 17430 577 620 4155>;
+	};
+
+	qcom,fcc-temp-lut {
+		qcom,lut-col-legend = <(-20) 0 25 40 60>;
+		qcom,lut-data = <2024 2033 2035 2031 2027>;
+	};
+
+	qcom,pc-temp-ocv-lut {
+		qcom,lut-col-legend = <(-20) 0 25 40 60>;
+		qcom,lut-row-legend = <100 95 90 85 80>,
+				<75 70 65 60 55>,
+				<50 45 40 35 30>,
+				<25 20 15 10 9>,
+				<8 7 6 5 4>,
+				<3 2 1 0>;
+		qcom,lut-data = <4179 4177 4173 4170 4164>,
+				<4097 4107 4108 4106 4104>,
+				<4040 4060 4059 4057 4054>,
+				<3974 4007 4012 4012 4009>,
+				<3929 3968 3972 3972 3969>,
+				<3889 3933 3937 3935 3933>,
+				<3852 3896 3905 3904 3900>,
+				<3822 3859 3876 3875 3872>,
+				<3797 3827 3846 3847 3844>,
+				<3777 3803 3809 3809 3806>,
+				<3758 3787 3788 3786 3784>,
+				<3740 3779 3777 3774 3771>,
+				<3717 3774 3771 3768 3763>,
+				<3685 3767 3767 3764 3758>,
+				<3649 3757 3761 3756 3749>,
+				<3613 3734 3747 3738 3724>,
+				<3578 3689 3711 3706 3694>,
+				<3538 3615 3649 3648 3643>,
+				<3483 3543 3564 3568 3567>,
+				<3470 3533 3553 3556 3556>,
+				<3453 3522 3542 3545 3545>,
+				<3434 3509 3531 3534 3533>,
+				<3414 3492 3519 3521 3519>,
+				<3388 3467 3498 3499 3492>,
+				<3358 3428 3457 3459 3447>,
+				<3321 3374 3397 3400 3387>,
+				<3268 3300 3318 3323 3307>,
+				<3182 3188 3207 3213 3191>,
+				<3000 3000 3000 3000 3000>;
+	};
+};
diff --git a/arch/arm/boot/dts/batterydata-qrd-4v35-2000mah.dtsi b/arch/arm/boot/dts/batterydata-qrd-4v35-2000mah.dtsi
new file mode 100644
index 0000000..b45f0f8
--- /dev/null
+++ b/arch/arm/boot/dts/batterydata-qrd-4v35-2000mah.dtsi
@@ -0,0 +1,109 @@
+/* Copyright (c) 2013, 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.
+ */
+
+qcom,qrd-4v35-2000mAh-data {
+	qcom,fcc-mah = <2000>;
+	qcom,default-rbatt-mohm = <172>;
+	qcom,rbatt-capacitive-mohm = <0>;
+	qcom,flat-ocv-threshold-uv = <3800000>;
+	qcom,max-voltage-uv = <4350000>;
+	qcom,v-cutoff-uv = <3400000>;
+	qcom,chg-term-ua = <100000>;
+	qcom,batt-id-kohm = <200>;
+
+	qcom,rbatt-sf-lut {
+		qcom,lut-col-legend = <(-20) 0 25 40 60>;
+		qcom,lut-row-legend = <100 95 90 85 80>,
+				<75 70 65 60 55>,
+				<50 45 40 35 30>,
+				<25 20 16 13 11>,
+				<10 9 8 7 6>,
+				<5 4 3 2 1>;
+		qcom,lut-data = <2422 324 100 79 72>,
+				<2417 325 100 79 71>,
+				<2344 327 100 80 72>,
+				<2416 336 102 81 73>,
+				<2072 354 107 82 73>,
+				<1961 372 113 84 75>,
+				<1929 341 118 87 77>,
+				<1929 321 130 93 80>,
+				<2041 306 140 104 85>,
+				<2202 292 119 96 83>,
+				<2374 290 98 80 73>,
+				<2550 292 98 79 72>,
+				<2727 294 99 81 73>,
+				<2904 303 100 82 75>,
+				<3091 323 100 81 73>,
+				<3278 348 100 80 73>,
+				<3470 376 99 79 72>,
+				<3627 386 100 79 72>,
+				<3672 398 100 80 71>,
+				<3812 424 100 80 73>,
+				<3895 443 101 80 73>,
+				<3985 465 102 82 75>,
+				<4094 497 105 83 76>,
+				<4211 533 109 85 79>,
+				<4335 579 113 87 80>,
+				<4505 612 113 85 76>,
+				<4693 643 113 86 77>,
+				<4930 712 120 90 81>,
+				<5283 835 145 111 107>,
+				<10293 15765 5566 6904 2547>;
+	};
+
+	qcom,fcc-temp-lut {
+		qcom,lut-col-legend = <(-20) 0 25 40 60>;
+		qcom,lut-data = <2096 2124 2121 2118 2103>;
+	};
+
+	qcom,pc-temp-ocv-lut {
+		qcom,lut-col-legend = <(-20) 0 25 40 60>;
+		qcom,lut-row-legend = <100 95 90 85 80>,
+				<75 70 65 60 55>,
+				<50 45 40 35 30>,
+				<25 20 16 13 11>,
+				<10 9 8 7 6>,
+				<5 4 3 2 1>,
+				<0>;
+		qcom,lut-data = <4340 4340 4335 4330 4323>,
+				<4217 4260 4265 4263 4258>,
+				<4135 4203 4207 4205 4201>,
+				<4084 4150 4152 4150 4146>,
+				<3992 4101 4101 4097 4093>,
+				<3934 4049 4051 4046 4044>,
+				<3889 3974 3995 3998 3999>,
+				<3852 3926 3958 3961 3959>,
+				<3832 3892 3921 3923 3921>,
+				<3819 3859 3874 3877 3877>,
+				<3807 3831 3838 3838 3838>,
+				<3796 3809 3815 3815 3814>,
+				<3784 3792 3797 3797 3796>,
+				<3770 3780 3783 3782 3781>,
+				<3754 3770 3772 3769 3764>,
+				<3737 3758 3763 3754 3742>,
+				<3717 3737 3744 3735 3720>,
+				<3700 3713 3718 3710 3696>,
+				<3687 3701 3692 3683 3671>,
+				<3674 3695 3689 3681 3669>,
+				<3667 3692 3688 3680 3669>,
+				<3659 3690 3687 3680 3668>,
+				<3649 3687 3685 3678 3667>,
+				<3636 3683 3683 3676 3664>,
+				<3618 3674 3679 3671 3658>,
+				<3596 3652 3663 3652 3632>,
+				<3566 3611 3620 3606 3584>,
+				<3522 3547 3555 3540 3517>,
+				<3460 3449 3461 3446 3424>,
+				<3356 3282 3312 3299 3273>,
+				<3000 3000 3000 3000 3000>;
+	};
+};
diff --git a/arch/arm/boot/dts/batterydata-qrd-4v35-2500mah.dtsi b/arch/arm/boot/dts/batterydata-qrd-4v35-2500mah.dtsi
new file mode 100644
index 0000000..e3540f0
--- /dev/null
+++ b/arch/arm/boot/dts/batterydata-qrd-4v35-2500mah.dtsi
@@ -0,0 +1,105 @@
+/* Copyright (c) 2013, 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.
+ */
+
+qcom,qrd-4v35-2500mAh-data {
+	qcom,fcc-mah = <2500>;
+	qcom,default-rbatt-mohm = <198>;
+	qcom,rbatt-capacitive-mohm = <0>;
+	qcom,flat-ocv-threshold-uv = <3800000>;
+	qcom,max-voltage-uv = <4350000>;
+	qcom,v-cutoff-uv = <3400000>;
+	qcom,chg-term-ua = <100000>;
+	qcom,batt-id-kohm = <470>;
+
+	qcom,rbatt-sf-lut {
+		qcom,lut-col-legend = <(-20) 0 25 40 60>;
+		qcom,lut-row-legend = <100 95 90 85 80>,
+				<75 70 65 60 55>,
+				<50 45 40 35 30>,
+				<25 20 15 10 9>,
+				<8 7 6 5 4>,
+				<3 2 1 0>;
+		qcom,lut-data = <1809 428 100 64 57>,
+				<1805 428 100 64 57>,
+				<1674 443 103 65 57>,
+				<1611 447 108 66 58>,
+				<1407 474 113 69 59>,
+				<1379 462 122 74 61>,
+				<1335 381 124 74 63>,
+				<1346 388 135 79 66>,
+				<1410 382 123 82 68>,
+				<1479 372 101 68 60>,
+				<1542 363 100 66 59>,
+				<1605 368 101 66 60>,
+				<1665 406 102 68 61>,
+				<1722 460 103 70 61>,
+				<1779 525 103 68 61>,
+				<1840 593 104 66 59>,
+				<1914 669 104 65 58>,
+				<2016 779 104 65 59>,
+				<1955 827 107 67 60>,
+				<2006 855 112 69 61>,
+				<2057 882 116 71 63>,
+				<2103 925 123 72 66>,
+				<2162 955 128 73 62>,
+				<2218 991 123 69 61>,
+				<2286 1035 122 69 62>,
+				<2382 1083 130 72 65>,
+				<2780 1158 153 83 81>,
+				<5073 1464 724 393 147>,
+				<46882 49515 42109 55595 11697>;
+	};
+
+	qcom,fcc-temp-lut {
+		qcom,lut-col-legend = <(-20) 0 25 40 60>;
+		qcom,lut-data = <2578 2580 2590 2584 2573>;
+	};
+
+	qcom,pc-temp-ocv-lut {
+		qcom,lut-col-legend = <(-20) 0 25 40 60>;
+		qcom,lut-row-legend = <100 95 90 85 80>,
+				<75 70 65 60 55>,
+				<50 45 40 35 30>,
+				<25 20 15 10 9>,
+				<8 7 6 5 4>,
+				<3 2 1 0>;
+		qcom,lut-data = <4326 4323 4321 4319 4311>,
+				<4192 4235 4253 4254 4249>,
+				<4110 4175 4198 4199 4194>,
+				<4058 4121 4146 4146 4141>,
+				<3952 4076 4096 4096 4091>,
+				<3906 4017 4052 4048 4043>,
+				<3859 3942 3990 3997 3999>,
+				<3828 3905 3954 3962 3959>,
+				<3813 3869 3911 3918 3916>,
+				<3799 3836 3866 3870 3868>,
+				<3786 3808 3837 3840 3840>,
+				<3772 3788 3815 3818 3817>,
+				<3758 3777 3796 3799 3798>,
+				<3743 3765 3780 3783 3781>,
+				<3726 3751 3767 3767 3761>,
+				<3707 3733 3753 3749 3736>,
+				<3684 3713 3731 3727 3713>,
+				<3654 3697 3699 3697 3685>,
+				<3616 3678 3683 3680 3669>,
+				<3605 3673 3682 3679 3667>,
+				<3594 3667 3680 3678 3666>,
+				<3578 3660 3676 3675 3662>,
+				<3561 3647 3666 3667 3647>,
+				<3541 3628 3637 3640 3610>,
+				<3514 3598 3586 3590 3556>,
+				<3478 3550 3515 3523 3485>,
+				<3425 3476 3414 3429 3387>,
+				<3330 3350 3272 3276 3222>,
+				<3000 3000 3000 3000 3000>;
+	};
+};
diff --git a/arch/arm/boot/dts/dsi-panel-nt35521-720p-video.dtsi b/arch/arm/boot/dts/dsi-panel-nt35521-720p-video.dtsi
index 5b7cc79..0b3fec4 100644
--- a/arch/arm/boot/dts/dsi-panel-nt35521-720p-video.dtsi
+++ b/arch/arm/boot/dts/dsi-panel-nt35521-720p-video.dtsi
@@ -61,7 +61,7 @@
 				29 01 00 00 00 00 03 D6 44 44
 				29 01 00 00 00 00 0D D7 00 00 00 00 00 00 00 00 00 00 00 00
 				29 01 00 00 00 00 0E D8 00 00 00 00 00 00 00 00 00 00 00 00 00
-				29 01 00 00 00 00 03 D9 00 28
+				29 01 00 00 00 00 03 D9 03 06
 				29 01 00 00 00 00 03 E5 00 FF
 				29 01 00 00 00 00 05 E6 F3 EC E7 DF
 				29 01 00 00 00 00 0B E7 F3 D9 CC CD B3 A6 99 99 99 95
diff --git a/arch/arm/boot/dts/dsi-v2-panel-hx8379a-wvga-video.dtsi b/arch/arm/boot/dts/dsi-v2-panel-hx8379a-wvga-video.dtsi
index f3f8c63..b2aeed3 100644
--- a/arch/arm/boot/dts/dsi-v2-panel-hx8379a-wvga-video.dtsi
+++ b/arch/arm/boot/dts/dsi-v2-panel-hx8379a-wvga-video.dtsi
@@ -21,7 +21,7 @@
 		qcom,mdss-pan-res = <480 800>;
 		qcom,mdss-pan-bpp = <24>;
 		qcom,mdss-pan-dest = "display_1";
-		qcom,mdss-pan-porch-values = <90 17 90 2 3 11>;
+		qcom,mdss-pan-porch-values = <100 40 70 6 4 6>;
 		qcom,mdss-pan-underflow-clr = <0xff>;
 		qcom,mdss-pan-bl-levels = <1 255>;
 		qcom,mdss-pan-bl-ctrl = "bl_ctrl_wled";
@@ -41,8 +41,8 @@
 		qcom,mdss-pan-dsi-dma-tr = <0x04>;
 		qcom,mdss-pan-dsi-frame-rate = <60>;
 		qcom,panel-phy-regulatorSettings =[02 08 05 00 20 03];
-		qcom,panel-phy-timingSettings = [5D 12 0C  00 33 39
-						 10 16 15  03 04 00];
+		qcom,panel-phy-timingSettings = [75 1A 11  00 3D 45
+						 15 1D 1C  03 04 00];
 		qcom,panel-phy-strengthCtrl = [ff 06];
 		qcom,panel-phy-bistCtrl = [03 03 00 00 0f 00];
 		qcom,panel-phy-laneConfig =
@@ -54,34 +54,32 @@
 
 		qcom,on-cmds-dsi-state = "DSI_LP_MODE";
 		qcom,panel-on-cmds = [
-					29 01 00 00 01 04
+					39 01 00 00 00 04
 						B9 FF 83 79
-					23 01 00 00 01 02
-						BA 51
-					29 01 00 00 01 14
+					39 01 00 00 00 03
+						BA 51 93
+					39 01 00 00 00 14
 						B1 00 50 44
 						EA 8D 08 11
-						0F 0F 24 2C
+						11 11 27 2F
 						9A 1A 42 0B
 						6E F1 00 E6
-					29 01 00 00 01 0e
+					39 01 00 00 00 0E
 						B2 00 00 3C
 						08 04 19 22
 						00 FF 08 04
 						19 20
-					29 01 00 00 01 20
-						B4 80 08 00
+					39 01 00 00 00 20
+						B4 82 08 00
 						32 10 03 32
 						13 70 32 10
 						08 37 01 28
-						05 37 08 3C
-						20 44 44 08
+						07 37 08 3C
+						08 44 44 08
 						00 40 08 28
 						08 30 30 04
-					23 01 00 00 01 02
-						cc 02
-					29 01 00 00 01 30
-						D5 00 00 08
+					39 01 00 00 00 30
+						D5 00 00 0A
 						00 01 05 00
 						03 00 88 88
 						88 88 23 01
@@ -93,18 +91,20 @@
 						88 88 88 88
 						88 88 00 00
 						00 00 00 00
-					29 01 00 00 01 24
-						E0 79 00 00
-						02 1C 1F 33
-						28 3E 07 0E
-						0F 15 17 16
-						16 13 19 00
-						00 02 1C 1F
-						33 28 3E 07
-						0E 0F 15 17
-						16 16 13 19
-					29 01 00 00 01 05
-						B6 00 A6 00 A6
+					39 01 00 00 00 24
+						E0 79 05 0F
+						14 26 29 3F
+						2B 44 04 0E
+						12 15 18 16
+						16 12 15 05
+						0F 14 26 29
+						3F 2B 44 04
+						0E 12 15 18
+						16 16 12 15
+					23 01 00 00 00 02
+						cc 02
+					39 01 00 00 00 05
+						B6 00 9C 00 9C
 					05 01 00 00 96 02
 						11 00
 					05 01 00 00 78 02
diff --git a/arch/arm/boot/dts/msm-pm8226.dtsi b/arch/arm/boot/dts/msm-pm8226.dtsi
index c9e3407..5642d4d 100644
--- a/arch/arm/boot/dts/msm-pm8226.dtsi
+++ b/arch/arm/boot/dts/msm-pm8226.dtsi
@@ -822,43 +822,55 @@
 			status = "disabled";
 		};
 
-                qcom,leds@d300 {
-                        compatible = "qcom,leds-qpnp";
-                        status = "okay";
-                        reg = <0xd300 0x100>;
-                        label = "flash";
-			flash_boost-supply = <&pm8226_chg_boost>;
-                        pm8226_flash0: qcom,flash_0 {
-                                qcom,max-current = <1000>;
-                                qcom,default-state = "off";
-                                qcom,headroom = <0>;
-                                qcom,duration = <1280>;
-                                qcom,clamp-curr = <200>;
-                                qcom,startup-dly = <1>;
-                                qcom,safety-timer;
-                                label = "flash";
-                                linux,default-trigger =
-                                        "flash0_trigger";
-                                qcom,id = <1>;
-                                linux,name = "led:flash_0";
-                                qcom,current = <625>;
-                        };
+		qcom,leds@d300 {
+			compatible = "qcom,leds-qpnp";
+			status = "okay";
+			reg = <0xd300 0x100>;
+			label = "flash";
+			flash-boost-supply = <&pm8226_chg_boost>;
+			pm8226_flash0: qcom,flash_0 {
+				qcom,max-current = <1000>;
+				qcom,default-state = "off";
+				qcom,headroom = <0>;
+				qcom,duration = <1280>;
+				qcom,clamp-curr = <200>;
+				qcom,startup-dly = <1>;
+				qcom,safety-timer;
+				label = "flash";
+				linux,default-trigger =
+						"flash0_trigger";
+				qcom,id = <1>;
+				linux,name = "led:flash_0";
+				qcom,current = <625>;
+			};
 
-                        pm8226_flash1: qcom,flash_1 {
-                                qcom,max-current = <1000>;
-                                qcom,default-state = "off";
-                                qcom,headroom = <0>;
-                                qcom,duration = <1280>;
-                                qcom,clamp-curr = <200>;
-                                qcom,startup-dly = <1>;
-                                qcom,safety-timer;
-                                linux,default-trigger =
-                                        "flash1_trigger";
-                                label = "flash";
-                                qcom,id = <2>;
-                                linux,name = "led:flash_1";
-                                qcom,current = <625>;
-                        };
+			pm8226_flash1: qcom,flash_1 {
+				qcom,max-current = <1000>;
+				qcom,default-state = "off";
+				qcom,headroom = <0>;
+				qcom,duration = <1280>;
+				qcom,clamp-curr = <200>;
+				qcom,startup-dly = <1>;
+				qcom,safety-timer;
+				linux,default-trigger =
+						"flash1_trigger";
+				label = "flash";
+				qcom,id = <2>;
+				linux,name = "led:flash_1";
+				qcom,current = <625>;
+			};
+
+			pm8226_torch: qcom,flash_torch {
+				qcom,max-current = <200>;
+				qcom,default-state = "off";
+				linux,default-trigger =
+						"torch_trigger";
+				label = "flash";
+				qcom,id = <1>;
+				linux,name = "led:flash_torch";
+				qcom,current = <120>;
+				qcom,torch-enable;
+			};
                 };
 	};
 };
diff --git a/arch/arm/boot/dts/msm-pm8941.dtsi b/arch/arm/boot/dts/msm-pm8941.dtsi
index ed5756f..583cd3c 100644
--- a/arch/arm/boot/dts/msm-pm8941.dtsi
+++ b/arch/arm/boot/dts/msm-pm8941.dtsi
@@ -1253,8 +1253,8 @@
 		compatible = "qcom,leds-qpnp";
 		reg = <0xd300 0x100>;
 		label = "flash";
-		flash_boost-supply = <&pm8941_chg_boost>;
-		torch_boost-supply = <&pm8941_boost>;
+		flash-boost-supply = <&pm8941_chg_boost>;
+		torch-boost-supply = <&pm8941_boost>;
 	};
 
 	qcom,leds@d400 {
diff --git a/arch/arm/boot/dts/msm8226-camera-sensor-cdp.dtsi b/arch/arm/boot/dts/msm8226-camera-sensor-cdp.dtsi
index f807814..5d7a7ec 100644
--- a/arch/arm/boot/dts/msm8226-camera-sensor-cdp.dtsi
+++ b/arch/arm/boot/dts/msm8226-camera-sensor-cdp.dtsi
@@ -18,6 +18,7 @@
 		compatible = "qcom,camera-led-flash";
 		qcom,flash-type = <1>;
 		qcom,flash-source = <&pm8226_flash0 &pm8226_flash1>;
+		qcom,torch-source = <&pm8226_torch>;
 	};
 };
 
@@ -38,7 +39,7 @@
 		qcom,csid-sd-index = <0>;
 		qcom,actuator-src = <&actuator0>;
 		qcom,led-flash-src = <&led_flash0>;
-		qcom,mount-angle = <0>;
+		qcom,mount-angle = <180>;
 		qcom,sensor-name = "ov8825";
 		cam_vdig-supply = <&pm8226_l5>;
 		cam_vana-supply = <&pm8226_l19>;
diff --git a/arch/arm/boot/dts/msm8226-camera-sensor-mtp.dtsi b/arch/arm/boot/dts/msm8226-camera-sensor-mtp.dtsi
index 56e8a09..5d1e1c8 100644
--- a/arch/arm/boot/dts/msm8226-camera-sensor-mtp.dtsi
+++ b/arch/arm/boot/dts/msm8226-camera-sensor-mtp.dtsi
@@ -18,6 +18,7 @@
 		compatible = "qcom,camera-led-flash";
 		qcom,flash-type = <1>;
 		qcom,flash-source = <&pm8226_flash0 &pm8226_flash1>;
+		qcom,torch-source = <&pm8226_torch>;
 	};
 };
 
@@ -38,7 +39,7 @@
 		qcom,csid-sd-index = <0>;
 		qcom,actuator-src = <&actuator0>;
 		qcom,led-flash-src = <&led_flash0>;
-		qcom,mount-angle = <90>;
+		qcom,mount-angle = <270>;
 		qcom,sensor-name = "ov8825";
 		cam_vdig-supply = <&pm8226_l5>;
 		cam_vana-supply = <&pm8226_l19>;
diff --git a/arch/arm/boot/dts/msm8226-camera-sensor-qrd.dtsi b/arch/arm/boot/dts/msm8226-camera-sensor-qrd.dtsi
index fb24a25..5822a4a 100644
--- a/arch/arm/boot/dts/msm8226-camera-sensor-qrd.dtsi
+++ b/arch/arm/boot/dts/msm8226-camera-sensor-qrd.dtsi
@@ -18,6 +18,7 @@
 		compatible = "qcom,camera-led-flash";
 		qcom,flash-type = <1>;
 		qcom,flash-source = <&pm8226_flash0 &pm8226_flash1>;
+		qcom,torch-source = <&pm8226_torch>;
 	};
 };
 
@@ -30,6 +31,79 @@
 		qcom,cci-master = <0>;
 	};
 
+	eeprom0: qcom,eeprom@6c {
+		cell-index = <0>;
+		reg = <0x6c 0x0>;
+		qcom,eeprom-name = "truly_cm7700";
+		compatible = "qcom,eeprom";
+		qcom,slave-addr = <0x6c>;
+		qcom,cci-master = <0>;
+		qcom,num-blocks = <9>;
+		qcom,page0 = <1 0x0100 2 0x01 1 1>;
+		qcom,poll0 = <0 0x0 2 0 1 1>;
+		qcom,mem0 = <0 0x0 2 0 1 0>;
+		qcom,page1 = <1 0x3d84 2 0x8 1 1>;
+		qcom,pageen1 = <1 0x3d81 2 0x01 1 10>;
+		qcom,poll1 = <0 0x0 2 0 1 1>;
+		qcom,mem1 = <32 0x3d00 2 0 1 0>;
+		qcom,page2 = <1 0x3d84 2 0x9 1 1>;
+		qcom,pageen2 = <1 0x3d81 2 0x01 1 10>;
+		qcom,poll2 = <0 0x0 2 0 1 1>;
+		qcom,mem2 = <32 0x3d00 2 0 1 0>;
+		qcom,page3 = <1 0x3d84 2 0xa 1 1>;
+		qcom,pageen3 = <1 0x3d81 2 0x01 1 10>;
+		qcom,poll3 = <0 0x0 2 0 1 1>;
+		qcom,mem3 = <32 0x3d00 2 0 1 0>;
+		qcom,page4 = <1 0x3d84 2 0xb 1 1>;
+		qcom,pageen4 = <1 0x3d81 2 0x01 1 10>;
+		qcom,poll4 = <0 0x0 2 0 1 1>;
+		qcom,mem4 = <32 0x3d00 2 0 1 0>;
+		qcom,page5 = <1 0x3d84 2 0xc 1 1>;
+		qcom,pageen5 = <1 0x3d81 2 0x01 1 10>;
+		qcom,poll5 = <0 0x0 2 0 1 1>;
+		qcom,mem5 = <32 0x3d00 2 0 1 0>;
+		qcom,page6 = <1 0x3d84 2 0xd 1 1>;
+		qcom,pageen6 = <1 0x3d81 2 0x01 1 10>;
+		qcom,poll6 = <0 0x0 2 0 1 1>;
+		qcom,mem6 = <32 0x3d00 2 0 1 0>;
+		qcom,page7 = <1 0x3d84 2 0xe 1 1>;
+		qcom,pageen7 = <1 0x3d81 2 0x01 1 10>;
+		qcom,poll7 = <0 0x0 2 0 1 1>;
+		qcom,mem7 = <32 0x3d00 2 0 1 0>;
+		qcom,page8 = <1 0x3d84 2 0xf 1 1>;
+		qcom,pageen8 = <1 0x3d81 2 0x01 1 10>;
+		qcom,poll8 = <0 0x0 2 0 1 1>;
+		qcom,mem8 = <32 0x3d00 2 0 1 0>;
+
+		cam_vdig-supply = <&pm8226_l5>;
+		cam_vio-supply = <&pm8226_lvs1>;
+		qcom,cam-vreg-name = "cam_vdig", "cam_vio";
+		qcom,cam-vreg-type = <0 1>;
+		qcom,cam-vreg-min-voltage = <1200000 0>;
+		qcom,cam-vreg-max-voltage = <1200000 0>;
+		qcom,cam-vreg-op-mode = <200000 0>;
+		qcom,gpio-no-mux = <0>;
+		gpios = <&msmgpio 26 0>,
+			<&msmgpio 37 0>,
+			<&msmgpio 36 0>;
+		qcom,gpio-reset = <1>;
+		qcom,gpio-standby = <2>;
+		qcom,gpio-req-tbl-num = <0 1 2>;
+		qcom,gpio-req-tbl-flags = <1 0 0>;
+		qcom,gpio-req-tbl-label = "CAMIF_MCLK",
+			"CAM_RESET1",
+			"CAM_STANDBY";
+		qcom,cam-power-seq-type = "sensor_vreg",
+			"sensor_vreg", "sensor_clk",
+			"sensor_gpio", "sensor_gpio";
+		qcom,cam-power-seq-val = "cam_vdig",
+			"cam_vio", "sensor_cam_mclk",
+			"sensor_gpio_reset",
+			"sensor_gpio_standby";
+		qcom,cam-power-seq-cfg-val = <1 1 24000000 1 1>;
+		qcom,cam-power-seq-delay = <1 1 5 5 10>;
+	};
+
 	qcom,camera@6f {
 		compatible = "qcom,ov8825";
 		reg = <0x6f>;
@@ -38,7 +112,8 @@
 		qcom,csid-sd-index = <0>;
 		qcom,actuator-src = <&actuator0>;
 		qcom,led-flash-src = <&led_flash0>;
-		qcom,mount-angle = <270>;
+		qcom,eeprom-src = <&eeprom0>;
+		qcom,mount-angle = <90>;
 		qcom,sensor-name = "ov8825";
 		cam_vdig-supply = <&pm8226_l5>;
 		cam_vana-supply = <&pm8226_l19>;
diff --git a/arch/arm/boot/dts/msm8226-cdp.dtsi b/arch/arm/boot/dts/msm8226-cdp.dtsi
index e44ce07..253ce3f 100644
--- a/arch/arm/boot/dts/msm8226-cdp.dtsi
+++ b/arch/arm/boot/dts/msm8226-cdp.dtsi
@@ -35,8 +35,6 @@
 			synaptics,irq-gpio = <&msmgpio 17 0x2008>;
 			synaptics,button-map = <139 102 158>;
 			synaptics,i2c-pull-up;
-			synaptics,power-down;
-			synaptics,disable-gpios;
 		};
 	};
 
@@ -112,6 +110,24 @@
 		qcom,cdc-vdd-spkr-gpios = <&pm8226_gpios 2 0>;
 		qcom,headset-jack-type-NO;
 	};
+
+	sound-9302 {
+		qcom,audio-routing =
+			"RX_BIAS", "MCLK",
+			"LDO_H", "MCLK",
+			"SPK_OUT", "MCLK",
+			"SPK_OUT", "EXT_VDD_SPKR",
+			"AMIC1", "MIC BIAS1 External",
+			"MIC BIAS1 External", "Handset Mic",
+			"AMIC2", "MIC BIAS2 External",
+			"MIC BIAS2 External", "Headset Mic",
+			"AMIC3", "MIC BIAS1 External",
+			"MIC BIAS1 External", "Handset Mic";
+
+		qcom,cdc-mclk-gpios = <&pm8226_gpios 1 0>;
+		qcom,cdc-vdd-spkr-gpios = <&pm8226_gpios 2 0>;
+		qcom,headset-jack-type-NO;
+	};
 };
 
 &sdcc1 {
@@ -258,7 +274,7 @@
 			qcom,led_mpp_4 {
 				label = "mpp";
 				linux,name = "green";
-				linux,default-trigger = "none";
+				linux,default-trigger = "battery-full";
 				qcom,default-state = "off";
 				qcom,max-current = <40>;
 				qcom,current-setting = <5>;
@@ -284,7 +300,7 @@
 			qcom,led_mpp_6 {
 				label = "mpp";
 				linux,name = "red";
-				linux,default-trigger = "none";
+				linux,default-trigger = "battery-charging";
 				qcom,default-state = "off";
 				qcom,max-current = <40>;
 				qcom,current-setting = <5>;
@@ -326,6 +342,12 @@
 				qcom,id = <0>;
 			};
 		};
+
+		qcom,vibrator@c000 {
+			status = "okay";
+			qcom,vib-timeout-ms = <15000>;
+			qcom,vib-vtg-level-mV = <3100>;
+		};
 	};
 };
 
diff --git a/arch/arm/boot/dts/msm8226-mtp.dtsi b/arch/arm/boot/dts/msm8226-mtp.dtsi
index 3138b06..68621fc 100644
--- a/arch/arm/boot/dts/msm8226-mtp.dtsi
+++ b/arch/arm/boot/dts/msm8226-mtp.dtsi
@@ -35,8 +35,6 @@
 			synaptics,irq-gpio = <&msmgpio 17 0x2008>;
 			synaptics,button-map = <139 102 158>;
 			synaptics,i2c-pull-up;
-			synaptics,power-down;
-			synaptics,disable-gpios;
 		};
 	};
 
@@ -106,14 +104,31 @@
 			"MIC BIAS1 External", "Handset Mic",
 			"AMIC2", "MIC BIAS2 External",
 			"MIC BIAS2 External", "Headset Mic",
-			"AMIC3", "MIC BIAS1 External",
-			"MIC BIAS1 External", "ANCRight Headset Mic",
 			"AMIC4", "MIC BIAS2 External",
+			"MIC BIAS2 External", "ANCRight Headset Mic",
+			"AMIC5", "MIC BIAS2 External",
 			"MIC BIAS2 External", "ANCLeft Headset Mic";
 
 		qcom,cdc-mclk-gpios = <&pm8226_gpios 1 0>;
 		qcom,cdc-vdd-spkr-gpios = <&pm8226_gpios 2 0>;
 	};
+
+	sound-9302 {
+		qcom,audio-routing =
+			"RX_BIAS", "MCLK",
+			"LDO_H", "MCLK",
+			"SPK_OUT", "MCLK",
+			"SPK_OUT", "EXT_VDD_SPKR",
+			"AMIC1", "MIC BIAS1 Internal1",
+			"MIC BIAS1 Internal1", "Handset Mic",
+			"AMIC2", "MIC BIAS2 External",
+			"MIC BIAS2 External", "Headset Mic",
+			"AMIC3", "MIC BIAS1 External",
+			"MIC BIAS1 External", "Handset Mic";
+
+		qcom,cdc-mclk-gpios = <&pm8226_gpios 1 0>;
+		qcom,cdc-vdd-spkr-gpios = <&pm8226_gpios 2 0>;
+	};
 };
 
 &usb_otg {
@@ -262,7 +277,7 @@
 			qcom,led_mpp_4 {
 				label = "mpp";
 				linux,name = "green";
-				linux,default-trigger = "none";
+				linux,default-trigger = "battery-full";
 				qcom,default-state = "off";
 				qcom,max-current = <40>;
 				qcom,current-setting = <5>;
@@ -288,7 +303,7 @@
 			qcom,led_mpp_6 {
 				label = "mpp";
 				linux,name = "red";
-				linux,default-trigger = "none";
+				linux,default-trigger = "battery-charging";
 				qcom,default-state = "off";
 				qcom,max-current = <40>;
 				qcom,current-setting = <5>;
@@ -334,6 +349,12 @@
 				qcom,id = <0>;
 			};
 		};
+
+		qcom,vibrator@c000 {
+			status = "okay";
+			qcom,vib-timeout-ms = <15000>;
+			qcom,vib-vtg-level-mV = <3100>;
+		};
 	};
 };
 
@@ -447,6 +468,11 @@
 
 &pm8226_bms {
 	status = "ok";
+	qcom,enable-fcc-learning;
+	qcom,min-fcc-learning-soc = <20>;
+	qcom,min-fcc-ocv-pc = <30>;
+	qcom,min-fcc-learning-samples = <5>;
+	qcom,fcc-resolution = <10>;
 };
 
 &pm8226_chg {
diff --git a/arch/arm/boot/dts/msm8226-qrd.dtsi b/arch/arm/boot/dts/msm8226-qrd.dtsi
index 8ab517a..88c44e6 100644
--- a/arch/arm/boot/dts/msm8226-qrd.dtsi
+++ b/arch/arm/boot/dts/msm8226-qrd.dtsi
@@ -30,8 +30,6 @@
 			synaptics,irq-gpio = <&msmgpio 17 0x2008>;
 			synaptics,button-map = <139 102 158>;
 			synaptics,i2c-pull-up;
-			synaptics,power-down;
-			synaptics,disable-gpios;
 		};
 		focaltech@38 {
 			compatible = "focaltech,5x06";
@@ -117,15 +115,33 @@
 			"MIC BIAS1 External", "Handset Mic",
 			"AMIC2", "MIC BIAS2 External",
 			"MIC BIAS2 External", "Headset Mic",
-			"AMIC3", "MIC BIAS1 External",
-			"MIC BIAS1 External", "ANCRight Headset Mic",
 			"AMIC4", "MIC BIAS2 External",
+			"MIC BIAS2 External", "ANCRight Headset Mic",
+			"AMIC5", "MIC BIAS2 External",
 			"MIC BIAS2 External", "ANCLeft Headset Mic";
 
 		qcom,cdc-mclk-gpios = <&pm8226_gpios 1 0>;
 		qcom,cdc-vdd-spkr-gpios = <&pm8226_gpios 2 0>;
 		qcom,cdc-us-euro-gpios = <&msmgpio 69 0>;
 	};
+
+	sound-9302 {
+		qcom,audio-routing =
+			"RX_BIAS", "MCLK",
+			"LDO_H", "MCLK",
+			"SPK_OUT", "MCLK",
+			"SPK_OUT", "EXT_VDD_SPKR",
+			"AMIC1", "MIC BIAS1 External",
+			"MIC BIAS1 External", "Handset Mic",
+			"AMIC2", "MIC BIAS2 External",
+			"MIC BIAS2 External", "Headset Mic",
+			"AMIC3", "MIC BIAS1 External",
+			"MIC BIAS1 External", "Handset Mic";
+
+		qcom,cdc-mclk-gpios = <&pm8226_gpios 1 0>;
+		qcom,cdc-vdd-spkr-gpios = <&pm8226_gpios 2 0>;
+		qcom,cdc-us-euro-gpios = <&msmgpio 69 0>;
+	};
 };
 
 &sdcc1 {
@@ -344,16 +360,28 @@
 	};
 };
 
+/ {
+	qrd_batterydata: qcom,battery-data {
+		qcom,rpull-up-kohm = <100>;
+		qcom,vref-batt-therm = <1800000>;
+
+		/include/ "batterydata-qrd-4v35-2000mah.dtsi"
+	};
+};
+
 &pm8226_bms {
 	status = "okay";
-	qcom,batt-type = <4>;
-	qcom,max-voltage-uv = <4350000>;
+	qcom,enable-fcc-learning;
+	qcom,min-fcc-learning-soc = <20>;
+	qcom,min-fcc-ocv-pc = <30>;
+	qcom,min-fcc-learning-samples = <5>;
+	qcom,fcc-resolution = <10>;
+	qcom,battery-data = <&qrd_batterydata>;
 };
 
 &pm8226_chg {
 	status = "okay";
-	qcom,vddmax-mv = <4350>;
-	qcom,vddsafe-mv = <4380>;
+	qcom,battery-data = <&qrd_batterydata>;
 	qcom,tchg-mins = <240>;
 };
 
diff --git a/arch/arm/boot/dts/msm8226-v1-mtp.dts b/arch/arm/boot/dts/msm8226-v1-mtp.dts
index 6f03dca..c32adbe 100644
--- a/arch/arm/boot/dts/msm8226-v1-mtp.dts
+++ b/arch/arm/boot/dts/msm8226-v1-mtp.dts
@@ -25,8 +25,8 @@
 };
 
 &cci {
-	/* Rotate rear camera to 0 degrees */
+	/* Rotate rear camera to 180 degrees */
 	qcom,camera@6f {
-	qcom,mount-angle = <0>;
+	qcom,mount-angle = <180>;
 	};
 };
diff --git a/arch/arm/boot/dts/msm8226-v1-pm.dtsi b/arch/arm/boot/dts/msm8226-v1-pm.dtsi
index 827cdff..97b75c4 100644
--- a/arch/arm/boot/dts/msm8226-v1-pm.dtsi
+++ b/arch/arm/boot/dts/msm8226-v1-pm.dtsi
@@ -99,6 +99,7 @@
 		qcom,saw2-spm-cmd-pc = [00 32 b0 10 e0 d0 6b c0 42 f0
 				11 07 01 b0 50 4e 02 02 c0 d0 12 e0 6b 02 32
 				50 f0 0f]; /*APCS_PMIC_OFF_L2RAM_OFF*/
+		qcom,L2-spm-is-apcs-master;
 	};
 
 	qcom,lpm-levels {
@@ -179,7 +180,7 @@
 			<0xff 176>, /* o_wcss_apss_smsm_irq */
 			<0xff 177>, /* o_wcss_apss_wlan_data_xfer_done */
 			<0xff 178>, /* o_wcss_apss_wlan_rx_data_avail */
-			<0xff 179>, /* o_wcss_apss_asic_intr
+			<0xff 179>, /* o_wcss_apss_asic_intr */
 			<0xff 181>, /* o_wcss_apss_wdog_bite_and_reset_rdy */
 			<0xff 188>, /* lpass_irq_out_apcs(0) */
 			<0xff 189>, /* lpass_irq_out_apcs(1) */
@@ -285,4 +286,12 @@
 		reg = <0xfc000000 0x1a0000>;
 		qcom,start-offset = <0x190010>;
 	};
+
+	qcom,rpm-master-stats@fc428150 {
+		compatible = "qcom,rpm-master-stats";
+		reg = <0xfc428150 0x3200>;
+		qcom,masters = "APSS", "MPSS", "LPSS", "PRONTO";
+		qcom,master-stats-version = <2>;
+		qcom,master-offset = <2560>;
+	};
 };
diff --git a/arch/arm/boot/dts/msm8226-v1-qrd-skuf.dts b/arch/arm/boot/dts/msm8226-v1-qrd-skuf.dts
index 1cbf00d..e08cbc9 100644
--- a/arch/arm/boot/dts/msm8226-v1-qrd-skuf.dts
+++ b/arch/arm/boot/dts/msm8226-v1-qrd-skuf.dts
@@ -113,6 +113,12 @@
 			status = "disabled";
 		};
 	};
+
+	qcom,pm8226@1 {
+		qcom,leds@d800 {
+			status = "disabled";
+		};
+	};
 };
 
 &pm8226_mpps {
@@ -159,3 +165,18 @@
 		qcom,fast-avg-setup = <0>;
 	};
 };
+
+&qrd_batterydata {
+	qcom,rpull-up-kohm = <100>;
+	qcom,vref-batt-therm = <1800000>;
+
+	/include/ "batterydata-qrd-4v35-2500mah.dtsi"
+};
+
+&pm8226_bms {
+	qcom,battery-data = <&qrd_batterydata>;
+};
+
+&pm8226_chg {
+	qcom,battery-data = <&qrd_batterydata>;
+};
diff --git a/arch/arm/boot/dts/msm8226-v2-pm.dtsi b/arch/arm/boot/dts/msm8226-v2-pm.dtsi
index 03064f2..92b204e 100644
--- a/arch/arm/boot/dts/msm8226-v2-pm.dtsi
+++ b/arch/arm/boot/dts/msm8226-v2-pm.dtsi
@@ -101,6 +101,7 @@
 		qcom,saw2-spm-cmd-pc = [00 32 b0 10 e0 d0 6b c0 42 f0
 				11 07 01 b0 50 4e 02 02 c0 d0 12 e0 6b 02 32
 				50 f0 0f]; /*APCS_PMIC_OFF_L2RAM_OFF*/
+		qcom,L2-spm-is-apcs-master;
 	};
 
 	qcom,lpm-levels {
@@ -181,7 +182,7 @@
 			<0xff 176>, /* o_wcss_apss_smsm_irq */
 			<0xff 177>, /* o_wcss_apss_wlan_data_xfer_done */
 			<0xff 178>, /* o_wcss_apss_wlan_rx_data_avail */
-			<0xff 179>, /* o_wcss_apss_asic_intr
+			<0xff 179>, /* o_wcss_apss_asic_intr */
 			<0xff 181>, /* o_wcss_apss_wdog_bite_and_reset_rdy */
 			<0xff 188>, /* lpass_irq_out_apcs(0) */
 			<0xff 189>, /* lpass_irq_out_apcs(1) */
@@ -287,4 +288,12 @@
 		reg = <0xfc000000 0x1a0000>;
 		qcom,start-offset = <0x190010>;
 	};
+
+	qcom,rpm-master-stats@fc428150 {
+		compatible = "qcom,rpm-master-stats";
+		reg = <0xfc428150 0x3200>;
+		qcom,masters = "APSS", "MPSS", "LPSS", "PRONTO";
+		qcom,master-stats-version = <2>;
+		qcom,master-offset = <2560>;
+	};
 };
diff --git a/arch/arm/boot/dts/msm8226-v2-qrd-dvt.dts b/arch/arm/boot/dts/msm8226-v2-qrd-dvt.dts
index 9e98681..7aadd71 100644
--- a/arch/arm/boot/dts/msm8226-v2-qrd-dvt.dts
+++ b/arch/arm/boot/dts/msm8226-v2-qrd-dvt.dts
@@ -35,3 +35,7 @@
 &pm8226_bms {
         qcom,use-external-rsense;
 };
+
+&pm8226_iadc {
+	qcom,rsense = <10000000>;
+};
diff --git a/arch/arm/boot/dts/msm8226-v2-qrd-skuf.dts b/arch/arm/boot/dts/msm8226-v2-qrd-skuf.dts
index fba41e5..da38a88 100644
--- a/arch/arm/boot/dts/msm8226-v2-qrd-skuf.dts
+++ b/arch/arm/boot/dts/msm8226-v2-qrd-skuf.dts
@@ -114,6 +114,12 @@
 			status = "disabled";
 		};
 	};
+
+	qcom,pm8226@1 {
+		qcom,leds@d800 {
+			status = "disabled";
+		};
+	};
 };
 
 &pm8226_mpps {
@@ -161,8 +167,20 @@
 	};
 };
 
+&qrd_batterydata {
+	qcom,rpull-up-kohm = <100>;
+	qcom,vref-batt-therm = <1800000>;
+
+	/include/ "batterydata-qrd-4v35-2500mah.dtsi"
+};
+
 &pm8226_bms {
-        qcom,use-external-rsense;
+	qcom,use-external-rsense;
+	qcom,battery-data = <&qrd_batterydata>;
+};
+
+&pm8226_chg {
+	qcom,battery-data = <&qrd_batterydata>;
 };
 
 &pm8226_iadc {
diff --git a/arch/arm/boot/dts/msm8226.dtsi b/arch/arm/boot/dts/msm8226.dtsi
index da52984..0ae0fc6 100644
--- a/arch/arm/boot/dts/msm8226.dtsi
+++ b/arch/arm/boot/dts/msm8226.dtsi
@@ -331,13 +331,9 @@
 			qcom,cdc-vdd-px-voltage = <1800000 1800000>;
 			qcom,cdc-vdd-px-current = <25000>;
 
-			cdc-vdd-a-1p2v-supply = <&pm8226_l4>;
-			qcom,cdc-vdd-a-1p2v-voltage = <1200000 1200000>;
-			qcom,cdc-vdd-a-1p2v-current = <10000>;
-
 			cdc-vdd-cx-supply = <&pm8226_l4>;
 			qcom,cdc-vdd-cx-voltage = <1200000 1200000>;
-			qcom,cdc-vdd-cx-current = <10000>;
+			qcom,cdc-vdd-cx-current = <2000>;
 
 			cdc-vdd-buckhelper-supply = <&pm8226_l25>;
 			qcom,cdc-vdd-buckhelper-voltage = <1775000 2125000>;
@@ -345,7 +341,6 @@
 
 			qcom,cdc-static-supplies = "cdc-vdd-h",
 						   "cdc-vdd-px",
-						   "cdc-vdd-a-1p2v",
 						   "cdc-vdd-cx";
 
 			qcom,cdc-cp-supplies = "cdc-vdd-buck",
@@ -382,6 +377,18 @@
 		qcom,prim-auxpcm-gpio-set = "prim-gpio-prim";
 	};
 
+	sound-9302 {
+		compatible = "qcom,msm8226-audio-tapan";
+		qcom,model = "msm8226-tapan9302-snd-card";
+		qcom,tapan-mclk-clk-freq = <9600000>;
+		qcom,prim-auxpcm-gpio-clk  = <&msmgpio 63 0>;
+		qcom,prim-auxpcm-gpio-sync = <&msmgpio 64 0>;
+		qcom,prim-auxpcm-gpio-din  = <&msmgpio 65 0>;
+		qcom,prim-auxpcm-gpio-dout = <&msmgpio 66 0>;
+		qcom,prim-auxpcm-gpio-set = "prim-gpio-prim";
+		qcom,tapan-codec-9302;
+	};
+
 	qcom,msm-pcm {
 		compatible = "qcom,msm-pcm-dsp";
 		qcom,msm-pcm-dsp-id = <0>;
diff --git a/arch/arm/boot/dts/msm8610-cdp.dtsi b/arch/arm/boot/dts/msm8610-cdp.dtsi
index bfccb78..8403dfd 100644
--- a/arch/arm/boot/dts/msm8610-cdp.dtsi
+++ b/arch/arm/boot/dts/msm8610-cdp.dtsi
@@ -140,6 +140,11 @@
 	msm8x10_wcd_codec@0d{
 		compatible = "qcom,msm8x10-wcd-i2c";
 		reg = <0x0d>;
+
+		interrupt-parent = <&wcd9xxx_intc>;
+		interrupts = <0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
+			      17 18 19 20 21 22 23>;
+
 		cdc-vdda-cp-supply = <&pm8110_s4>;
 		qcom,cdc-vdda-cp-voltage = <1800000 2150000>;
 		qcom,cdc-vdda-cp-current = <650000>;
@@ -217,10 +222,9 @@
 				qcom,source-sel = <8>;
 				qcom,mode-ctrl = <0x10>;
 				qcom,pwm-channel = <0>;
-				qcom,pwm-us = <14>;
+				qcom,pwm-us = <27>;
 				qcom,vin-ctrl = <0x03>;
 				qcom,mode = "pwm";
-				qcom,min-brightness = <19>;
 			};
 		};
 	};
@@ -238,8 +242,6 @@
 
 &sdhc_1 {
 	vdd-supply = <&pm8110_l17>;
-	qcom,vdd-always-on;
-	qcom,vdd-lpm-sup;
 	qcom,vdd-voltage-level = <2900000 2900000>;
 	qcom,vdd-current-level = <200 400000>;
 
diff --git a/arch/arm/boot/dts/msm8610-mtp.dtsi b/arch/arm/boot/dts/msm8610-mtp.dtsi
index 349c8f7..7e80d19 100644
--- a/arch/arm/boot/dts/msm8610-mtp.dtsi
+++ b/arch/arm/boot/dts/msm8610-mtp.dtsi
@@ -178,6 +178,11 @@
 	msm8x10_wcd_codec@0d{
 		compatible = "qcom,msm8x10-wcd-i2c";
 		reg = <0x0d>;
+
+		interrupt-parent = <&wcd9xxx_intc>;
+		interrupts = <0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
+			      17 18 19 20 21 22 23>;
+
 		cdc-vdda-cp-supply = <&pm8110_s4>;
 		qcom,cdc-vdda-cp-voltage = <1800000 2150000>;
 		qcom,cdc-vdda-cp-current = <650000>;
@@ -255,10 +260,9 @@
 				qcom,source-sel = <8>;
 				qcom,mode-ctrl = <0x10>;
 				qcom,pwm-channel = <0>;
-				qcom,pwm-us = <14>;
+				qcom,pwm-us = <27>;
 				qcom,vin-ctrl = <0x03>;
 				qcom,mode = "pwm";
-				qcom,min-brightness = <19>;
 			};
 		};
 	};
@@ -276,8 +280,6 @@
 
 &sdhc_1 {
 	vdd-supply = <&pm8110_l17>;
-	qcom,vdd-always-on;
-	qcom,vdd-lpm-sup;
 	qcom,vdd-voltage-level = <2900000 2900000>;
 	qcom,vdd-current-level = <200 400000>;
 
diff --git a/arch/arm/boot/dts/msm8610-qrd-camera-sensor.dtsi b/arch/arm/boot/dts/msm8610-qrd-camera-sensor.dtsi
index e73573a..bdcb285 100644
--- a/arch/arm/boot/dts/msm8610-qrd-camera-sensor.dtsi
+++ b/arch/arm/boot/dts/msm8610-qrd-camera-sensor.dtsi
@@ -18,7 +18,7 @@
                 qcom,slave-id = <0x40 0x04 0xc0>;
                 qcom,csiphy-sd-index = <0>;
                 qcom,csid-sd-index = <0>;
-                qcom,mount-angle = <270>;
+                qcom,mount-angle = <90>;
                 qcom,sensor-name = "hi256";
                 cam_vdig-supply = <&pm8110_l2>;
                 cam_vana-supply = <&pm8110_l19>;
diff --git a/arch/arm/boot/dts/msm8610-qrd.dtsi b/arch/arm/boot/dts/msm8610-qrd.dtsi
index 49068b5..1688890 100644
--- a/arch/arm/boot/dts/msm8610-qrd.dtsi
+++ b/arch/arm/boot/dts/msm8610-qrd.dtsi
@@ -149,6 +149,11 @@
 	msm8x10_wcd_codec@0d{
 		compatible = "qcom,msm8x10-wcd-i2c";
 		reg = <0x0d>;
+
+		interrupt-parent = <&wcd9xxx_intc>;
+		interrupts = <0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
+			     17 18 19 20 21 22 23>;
+
 		cdc-vdda-cp-supply = <&pm8110_s4>;
 		qcom,cdc-vdda-cp-voltage = <1800000 2150000>;
 		qcom,cdc-vdda-cp-current = <650000>;
@@ -226,10 +231,9 @@
 				qcom,source-sel = <8>;
 				qcom,mode-ctrl = <0x10>;
 				qcom,pwm-channel = <0>;
-				qcom,pwm-us = <14>;
+				qcom,pwm-us = <27>;
 				qcom,vin-ctrl = <0x03>;
 				qcom,mode = "pwm";
-				qcom,min-brightness = <19>;
 			};
 		};
 	};
@@ -247,8 +251,6 @@
 
 &sdhc_1 {
 	vdd-supply = <&pm8110_l17>;
-	qcom,vdd-always-on;
-	qcom,vdd-lpm-sup;
 	qcom,vdd-voltage-level = <2900000 2900000>;
 	qcom,vdd-current-level = <200 400000>;
 
@@ -361,3 +363,7 @@
 		qcom,scale-function = <6>;
 	};
 };
+
+&android_usb {
+	qcom,android-usb-cdrom;
+};
diff --git a/arch/arm/boot/dts/msm8610-regulator.dtsi b/arch/arm/boot/dts/msm8610-regulator.dtsi
index 34cbd99..b74d487 100644
--- a/arch/arm/boot/dts/msm8610-regulator.dtsi
+++ b/arch/arm/boot/dts/msm8610-regulator.dtsi
@@ -79,6 +79,7 @@
 		qcom,cpr-fuse-redun-ro-sel = <44 26 29>;
 
 		qcom,cpr-enable;
+		qcom,use-tz-api;
 	};
 };
 
diff --git a/arch/arm/boot/dts/msm8610-sim.dts b/arch/arm/boot/dts/msm8610-sim.dts
index 7c57fe6..33176b9 100644
--- a/arch/arm/boot/dts/msm8610-sim.dts
+++ b/arch/arm/boot/dts/msm8610-sim.dts
@@ -30,6 +30,11 @@
 	msm8x10_wcd_codec@0d{
 		compatible = "qcom,msm8x10-wcd-i2c";
 		reg = <0x0d>;
+
+		interrupt-parent = <&wcd9xxx_intc>;
+		interrupts = <0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
+			      17 18 19 20 21 22 23>;
+
 		cdc-vdda-cp-supply = <&pm8110_s4>;
 		qcom,cdc-vdda-cp-voltage = <2150000 2150000>;
 		qcom,cdc-vdda-cp-current = <650000>;
diff --git a/arch/arm/boot/dts/msm8610-pm.dtsi b/arch/arm/boot/dts/msm8610-v1-pm.dtsi
similarity index 95%
rename from arch/arm/boot/dts/msm8610-pm.dtsi
rename to arch/arm/boot/dts/msm8610-v1-pm.dtsi
index beeeed3..ded517f 100644
--- a/arch/arm/boot/dts/msm8610-pm.dtsi
+++ b/arch/arm/boot/dts/msm8610-v1-pm.dtsi
@@ -99,6 +99,7 @@
 		qcom,saw2-spm-cmd-pc = [00 32 b0 10 e0 d0 6b c0 42 f0
 				11 07 01 b0 50 4e 02 02 c0 d0 12 e0 6b 02 32
 				50 f0 0f]; /*APCS_PMIC_OFF_L2RAM_OFF*/
+		qcom,L2-spm-is-apcs-master;
 	};
 
 	qcom,lpm-levels {
@@ -179,9 +180,9 @@
 			<0xff 176>, /* o_wcss_apss_smsm_irq */
 			<0xff 177>, /* o_wcss_apss_wlan_data_xfer_done */
 			<0xff 178>, /* o_wcss_apss_wlan_rx_data_avail */
-			<0xff 179>, /* o_wcss_apss_asic_intr
+			<0xff 179>, /* o_wcss_apss_asic_intr */
 			<0xff 181>, /* o_wcss_apss_wdog_bite_and_reset_rdy */
-			<0xff 161>, /* lpass_irq_out_spare[4] /
+			<0xff 161>, /* lpass_irq_out_spare[4] */
 			<0xff 162>, /* lpass_irq_out_spare[5]*/
 			<0xff 234>, /* lpass_irq_out_spare[6]*/
 			<0xff 235>, /* lpass_irq_out_spare[7]*/
@@ -285,4 +286,12 @@
 		reg = <0xfc000000 0x1a0000>;
 		qcom,start-offset = <0x190010>;
 	};
+
+	qcom,rpm-master-stats@fc428150 {
+		compatible = "qcom,rpm-master-stats";
+		reg = <0xfc428150 0x3200>;
+		qcom,masters = "APSS", "MPSS", "LPSS", "PRONTO";
+		qcom,master-stats-version = <2>;
+		qcom,master-offset = <2560>;
+	};
 };
diff --git a/arch/arm/boot/dts/msm8610-v1-qrd-skuab.dts b/arch/arm/boot/dts/msm8610-v1-qrd-skuab.dts
index b4559ff..d7a446d 100644
--- a/arch/arm/boot/dts/msm8610-v1-qrd-skuab.dts
+++ b/arch/arm/boot/dts/msm8610-v1-qrd-skuab.dts
@@ -79,6 +79,23 @@
 			fsl,irq-gpio = <&msmgpio 81 0x00>;
 			fsl,sensors-position = <1>;
 		};
+		stk@48 {
+			compatible = "stk,stk3x1x";
+			reg = <0x48>;
+			interrupt-parent = <&msmgpio>;
+			interrupts = <80 0x2>;
+			vdd-supply = <&pm8110_l19>;
+			vio-supply = <&pm8110_l14>;
+			stk,irq-gpio = <&msmgpio 80 0x02>;
+			stk,transmittance = <100>;
+			stk,state-reg = <0x00>;
+			stk,psctrl-reg = <0x71>;
+			stk,alsctrl-reg = <0x3e>;
+			stk,ledctrl-reg = <0xFF>;
+			stk,wait-reg = <0x07>;
+			stk,ps-thdh = <150>;
+			stk,ps-thdl = <100>;
+		};
 	};
 
 	usb@f9a55000 {
diff --git a/arch/arm/boot/dts/msm8610-v1.dtsi b/arch/arm/boot/dts/msm8610-v1.dtsi
index 5052b96..6050a75 100644
--- a/arch/arm/boot/dts/msm8610-v1.dtsi
+++ b/arch/arm/boot/dts/msm8610-v1.dtsi
@@ -17,3 +17,4 @@
  */
 
 /include/ "msm8610.dtsi"
+/include/ "msm8610-v1-pm.dtsi"
diff --git a/arch/arm/boot/dts/msm8610-pm.dtsi b/arch/arm/boot/dts/msm8610-v2-pm.dtsi
similarity index 88%
copy from arch/arm/boot/dts/msm8610-pm.dtsi
copy to arch/arm/boot/dts/msm8610-v2-pm.dtsi
index beeeed3..68841bc 100644
--- a/arch/arm/boot/dts/msm8610-pm.dtsi
+++ b/arch/arm/boot/dts/msm8610-v2-pm.dtsi
@@ -24,7 +24,7 @@
 		qcom,saw2-spm-cmd-wfi = [60 03 60 0b 0f];
 		qcom,saw2-spm-cmd-spc = [20 10 80 30 90 5b 60 03 60 3b 76 76
 				0b 94 5b 80 10 26 30 0f];
-		qcom,saw2-spm-cmd-pc = [20 10 80 30 90 5b 60 03 60 3b 76 76
+		qcom,saw2-spm-cmd-pc = [20 10 80 30 90 5b 60 07 60 3b 76 76
 				0b 94 5b 80 10 26 30 0f];
 	};
 
@@ -41,7 +41,7 @@
 		qcom,saw2-spm-cmd-wfi = [60 03 60 0b 0f];
 		qcom,saw2-spm-cmd-spc = [20 10 80 30 90 5b 60 03 60 3b 76 76
 				0b 94 5b 80 10 26 30 0f];
-		qcom,saw2-spm-cmd-pc = [20 10 80 30 90 5b 60 03 60 3b 76 76
+		qcom,saw2-spm-cmd-pc = [20 10 80 30 90 5b 60 07 60 3b 76 76
 				0b 94 5b 80 10 26 30 0f];
 		};
 
@@ -58,7 +58,7 @@
 		qcom,saw2-spm-cmd-wfi = [60 03 60 0b 0f];
 		qcom,saw2-spm-cmd-spc = [00 20 10 80 30 90 5b 60 03 60 3b 76 76
 				0b 94 5b 80 10 06 26 30 0f];
-		qcom,saw2-spm-cmd-pc = [00 20 10 80 30 90 5b 60 03 60 3b 76 76
+		qcom,saw2-spm-cmd-pc = [00 20 10 80 30 90 5b 60 07 60 3b 76 76
 				0b 94 5b 80 10 06 26 30 0f];
 	};
 
@@ -75,7 +75,7 @@
 		qcom,saw2-spm-cmd-wfi = [60 03 60 0b 0f];
 		qcom,saw2-spm-cmd-spc = [20 10 80 30 90 5b 60 03 60 3b 76 76
 				0b 94 5b 80 10 26 30 0f];
-		qcom,saw2-spm-cmd-pc = [20 10 80 30 90 5b 60 03 60 3b 76 76
+		qcom,saw2-spm-cmd-pc = [20 10 80 30 90 5b 60 07 60 3b 76 76
 				0b 94 5b 80 10 26 30 0f];
 	};
 
@@ -96,9 +96,12 @@
 		qcom,phase-port = <0x1>;
 		qcom,pfm-port = <0x2>;
 		qcom,saw2-spm-cmd-ret = [00 03 00 0f];
+		qcom,saw2-spm-cmd-gdhs = [00 20 32 6b c0 e0 d0 42 07 50
+				4e 02 02 d0 e0 c0 22 6b 02 32 50 0f];
 		qcom,saw2-spm-cmd-pc = [00 32 b0 10 e0 d0 6b c0 42 f0
 				11 07 01 b0 50 4e 02 02 c0 d0 12 e0 6b 02 32
 				50 f0 0f]; /*APCS_PMIC_OFF_L2RAM_OFF*/
+		qcom,L2-spm-is-apcs-master;
 	};
 
 	qcom,lpm-levels {
@@ -130,7 +133,7 @@
 		qcom,lpm-level@2 {
 			reg = <0x2>;
 			qcom,mode = "pc";
-			qcom,l2 = "l2_cache_retention";
+			qcom,l2 = "l2_cache_gdhs";
 			qcom,latency-us = <20000>;
 			qcom,ss-power = <138>;
 			qcom,energy-overhead = <1208400>;
@@ -208,43 +211,45 @@
 
 		qcom,gpio-parent = <&msmgpio>;
 		qcom,gpio-map = <3  1>,
-			<4  5 >,
-			<5  9 >,
-			<6  14>,
-			<7  15>,
-			<8  32>,
-			<9  33>,
-			<10  34>,
-			<11  35>,
-			<12  41>,
-			<13  42>,
-			<14  72>,
-			<15  73>,
-			<16  74>,
-			<17  75>,
-			<18  76>,
-			<19  77>,
-			<20  78>,
-			<21  79>,
-			<22  80>,
-			<23  81>,
-			<24  82>,
-			<25  83>,
-			<26  84>,
-			<27  85>,
-			<28  87>,
-			<29  90>,
-			<30  91>,
-			<31  92>,
-			<32  93>,
-			<33  94>,
-			<34  95>,
-			<35  96>,
-			<36  97>,
-			<37  98>,
-			<38  99>,
-			<39  100>,
-			<40  101>;
+			<4  4 >,
+			<5  5 >,
+			<6  9 >,
+			<7  13>,
+			<8  17>,
+			<9  21>,
+			<10  27>,
+			<11  29>,
+			<12  31>,
+			<13  33>,
+			<14  35>,
+			<15  37>,
+			<16  38>,
+			<17  39>,
+			<18  41>,
+			<19  46>,
+			<20  48>,
+			<21  49>,
+			<22  50>,
+			<23  51>,
+			<24  52>,
+			<25  54>,
+			<26  62>,
+			<27  63>,
+			<28  64>,
+			<29  65>,
+			<30  66>,
+			<31  67>,
+			<32  68>,
+			<33  69>,
+			<34  71>,
+			<35  72>,
+			<36  106>,
+			<37  107>,
+			<38  108>,
+			<39  109>,
+			<40  110>,
+			<54  111>,
+			<55  113>;
 	};
 
 	qcom,pm-8x60@fe805664 {
diff --git a/arch/arm/boot/dts/msm8610-v2.dtsi b/arch/arm/boot/dts/msm8610-v2.dtsi
index 5052b96..89d8f74 100644
--- a/arch/arm/boot/dts/msm8610-v2.dtsi
+++ b/arch/arm/boot/dts/msm8610-v2.dtsi
@@ -17,3 +17,4 @@
  */
 
 /include/ "msm8610.dtsi"
+/include/ "msm8610-v2-pm.dtsi"
diff --git a/arch/arm/boot/dts/msm8610.dtsi b/arch/arm/boot/dts/msm8610.dtsi
index efa68b9..89a7cff 100644
--- a/arch/arm/boot/dts/msm8610.dtsi
+++ b/arch/arm/boot/dts/msm8610.dtsi
@@ -40,7 +40,6 @@
 /include/ "msm8610-gpu.dtsi"
 /include/ "msm-gdsc.dtsi"
 /include/ "msm8610-coresight.dtsi"
-/include/ "msm8610-pm.dtsi"
 /include/ "msm8610-smp2p.dtsi"
 /include/ "msm8610-bus.dtsi"
 /include/ "msm8610-mdss.dtsi"
@@ -70,6 +69,15 @@
 		qcom,direct-connect-irqs = <8>;
 	};
 
+	wcd9xxx_intc: wcd9xxx_irq {
+		compatible = "qcom,wcd9xxx-irq";
+		interrupt-controller;
+		#interrupt-cells = <1>;
+		interrupt-parent = <&intc>;
+		interrupts = <0 31 0>;
+		interrupt-names = "cdc-int";
+	};
+
 	qcom,mpm2-sleep-counter@fc4a3000 {
 		compatible = "qcom,mpm2-sleep-counter";
 		reg = <0xfc4a3000 0x1000>;
@@ -241,7 +249,7 @@
 				<87 512 60000 960000>;
 	};
 
-	android_usb@fe8050c8 {
+	android_usb: android_usb@fe8050c8 {
 		compatible = "qcom,android-usb";
 		reg = <0xfe8050c8 0xc8>;
 		qcom,android-usb-swfi-latency = <1>;
@@ -258,8 +266,6 @@
 		interrupt-names = "core_irq", "bam_irq";
 
 		vdd-supply = <&pm8110_l17>;
-		qcom,vdd-always-on;
-		qcom,vdd-lpm-sup;
 		qcom,vdd-voltage-level = <2900000 2900000>;
 		qcom,vdd-current-level = <9000 400000>;
 
diff --git a/arch/arm/boot/dts/msm8926-qrd-skug.dts b/arch/arm/boot/dts/msm8926-qrd-skug.dts
index 557e0c8..d0f231d 100644
--- a/arch/arm/boot/dts/msm8926-qrd-skug.dts
+++ b/arch/arm/boot/dts/msm8926-qrd-skug.dts
@@ -20,3 +20,18 @@
 	qcom,board-id = <11 5>;
 	qcom,msm-id = <200 0>;
 };
+
+&qrd_batterydata {
+	qcom,rpull-up-kohm = <100>;
+	qcom,vref-batt-therm = <1800000>;
+
+	/include/ "batterydata-qrd-4v2-2000mah.dtsi"
+};
+
+&pm8226_bms {
+	qcom,battery-data = <&qrd_batterydata>;
+};
+
+&pm8226_chg {
+	qcom,battery-data = <&qrd_batterydata>;
+};
diff --git a/arch/arm/boot/dts/msm8926.dtsi b/arch/arm/boot/dts/msm8926.dtsi
index 9159ba2..4b96b66 100644
--- a/arch/arm/boot/dts/msm8926.dtsi
+++ b/arch/arm/boot/dts/msm8926.dtsi
@@ -17,6 +17,7 @@
  */
 
 /include/ "msm8226.dtsi"
+/include/ "msm8226-v2-pm.dtsi"
 
 / {
 	model = "Qualcomm MSM 8926";
diff --git a/arch/arm/boot/dts/msm8974-v1-pm.dtsi b/arch/arm/boot/dts/msm8974-v1-pm.dtsi
index 7362b64..0115d89 100644
--- a/arch/arm/boot/dts/msm8974-v1-pm.dtsi
+++ b/arch/arm/boot/dts/msm8974-v1-pm.dtsi
@@ -127,6 +127,7 @@
 		qcom,saw2-spm-cmd-gdhs = [00 20 32 42 07 44 22 50 02 32 50 0f];
 		qcom,saw2-spm-cmd-pc = [00 10 32 b0 11 42 07 01 b0 12 44
 				50 02 32 50 0f];
+		qcom,L2-spm-is-apcs-master;
 	};
 
 	qcom,lpm-levels {
@@ -217,7 +218,7 @@
 			<0xff 176>, /* o_wcss_apss_smsm_irq */
 			<0xff 177>, /* o_wcss_apss_wlan_data_xfer_done */
 			<0xff 178>, /* o_wcss_apss_wlan_rx_data_avail */
-			<0xff 179>, /* o_wcss_apss_asic_intr
+			<0xff 179>, /* o_wcss_apss_asic_intr */
 
 			<0xff 188>, /* lpass_irq_out_apcs(0) */
 			<0xff 189>, /* lpass_irq_out_apcs(1) */
@@ -315,4 +316,13 @@
 		reg-names = "phys_addr_base";
 		qcom,sleep-stats-version = <2>;
 	};
+
+	qcom,rpm-master-stats@fc428150 {
+		compatible = "qcom,rpm-master-stats";
+		reg = <0xfc428150 0x3200>;
+		qcom,masters = "APSS", "MPSS", "LPSS", "PRONTO";
+		qcom,master-stats-version = <2>;
+		qcom,master-offset = <2560>;
+	};
+
 };
diff --git a/arch/arm/boot/dts/msm8974-v2-pm.dtsi b/arch/arm/boot/dts/msm8974-v2-pm.dtsi
index 1235c6e..07a92dc 100644
--- a/arch/arm/boot/dts/msm8974-v2-pm.dtsi
+++ b/arch/arm/boot/dts/msm8974-v2-pm.dtsi
@@ -123,6 +123,7 @@
 		qcom,saw2-spm-cmd-gdhs = [00 32 42 07 44 50 02 32 50 0f];
 		qcom,saw2-spm-cmd-pc = [00 10 32 b0 11 42 07 01 b0 12 44
 				50 02 32 50 0f];
+		qcom,L2-spm-is-apcs-master;
 	};
 
 	qcom,lpm-levels {
@@ -345,4 +346,12 @@
 		reg = <0xfc000000 0x1a0000>;
 		qcom,start-offset = <0x190010>;
 	};
+
+	qcom,rpm-master-stats@fc428150 {
+		compatible = "qcom,rpm-master-stats";
+		reg = <0xfc428150 0x3200>;
+		qcom,masters = "APSS", "MPSS", "LPSS", "PRONTO";
+		qcom,master-stats-version = <2>;
+		qcom,master-offset = <2560>;
+	};
 };
diff --git a/arch/arm/boot/dts/msm8974pro-ac-mtp.dts b/arch/arm/boot/dts/msm8974pro-ac-mtp.dts
index 862dbee..237c9f9 100644
--- a/arch/arm/boot/dts/msm8974pro-ac-mtp.dts
+++ b/arch/arm/boot/dts/msm8974pro-ac-mtp.dts
@@ -123,3 +123,12 @@
 		qcom,thermal-node;
 	};
 };
+
+&sdhc_1 {
+	reg = <0xf9824900 0x1a0>, <0xf9824000 0x800>;
+	qcom,clk-rates = <400000 20000000 25000000 50000000 100000000 200000000 400000000>;
+	qcom,bus-speed-mode = "HS400_1p8v", "HS200_1p8v", "DDR_1p8v";
+
+	qcom,pad-pull-on = <0x0 0x3 0x3 0x1>; /* no-pull, pull-up, pull-up, pull-down */
+	qcom,pad-pull-off = <0x0 0x3 0x3 0x1>; /* no-pull, pull-up, pull-up, pull-down */
+};
diff --git a/arch/arm/boot/dts/msm9625-pm.dtsi b/arch/arm/boot/dts/msm9625-pm.dtsi
index 7989f2b..1e6cdf2 100644
--- a/arch/arm/boot/dts/msm9625-pm.dtsi
+++ b/arch/arm/boot/dts/msm9625-pm.dtsi
@@ -192,4 +192,12 @@
 		reg = <0xfc000000 0x1a0000>;
 		qcom,start-offset = <0x190010>;
 	};
+
+	qcom,rpm-master-stats@fc428150 {
+		compatible = "qcom,rpm-master-stats";
+		reg = <0xfc428150 0x3200>;
+		qcom,masters = "APSS", "MPSS", "LPSS";
+		qcom,master-stats-version = <2>;
+		qcom,master-offset = <2560>;
+	};
 };
diff --git a/arch/arm/configs/msm8226-perf_defconfig b/arch/arm/configs/msm8226-perf_defconfig
index 47b425c..63fd82b 100644
--- a/arch/arm/configs/msm8226-perf_defconfig
+++ b/arch/arm/configs/msm8226-perf_defconfig
@@ -60,7 +60,7 @@
 CONFIG_MSM_WATCHDOG_V2=y
 CONFIG_MSM_MEMORY_DUMP=y
 CONFIG_MSM_DLOAD_MODE=y
-CONFIG_MSM_ADSP_LOADER=m
+CONFIG_MSM_ADSP_LOADER=y
 CONFIG_MSM_OCMEM=y
 CONFIG_MSM_OCMEM_LOCAL_POWER_CTRL=y
 CONFIG_MSM_OCMEM_DEBUG=y
@@ -290,6 +290,7 @@
 # CONFIG_MSM_CAMERA is not set
 CONFIG_OV8825=y
 CONFIG_OV12830=y
+CONFIG_IMX135=y
 CONFIG_MSM_CAMERA_SENSOR=y
 CONFIG_MSM_EEPROM=y
 CONFIG_MSM_CPP=y
diff --git a/arch/arm/configs/msm8226_defconfig b/arch/arm/configs/msm8226_defconfig
index 97c3664..7d1562b 100644
--- a/arch/arm/configs/msm8226_defconfig
+++ b/arch/arm/configs/msm8226_defconfig
@@ -60,7 +60,7 @@
 CONFIG_MSM_WATCHDOG_V2=y
 CONFIG_MSM_MEMORY_DUMP=y
 CONFIG_MSM_DLOAD_MODE=y
-CONFIG_MSM_ADSP_LOADER=m
+CONFIG_MSM_ADSP_LOADER=y
 CONFIG_MSM_OCMEM=y
 CONFIG_MSM_OCMEM_LOCAL_POWER_CTRL=y
 CONFIG_MSM_OCMEM_DEBUG=y
@@ -294,6 +294,7 @@
 # CONFIG_MSM_CAMERA is not set
 CONFIG_OV8825=y
 CONFIG_OV12830=y
+CONFIG_IMX135=y
 CONFIG_MSM_CAMERA_SENSOR=y
 CONFIG_MSM_EEPROM=y
 CONFIG_MSM_CPP=y
diff --git a/arch/arm/configs/msm8610-perf_defconfig b/arch/arm/configs/msm8610-perf_defconfig
index 8591812..2b76ba8 100644
--- a/arch/arm/configs/msm8610-perf_defconfig
+++ b/arch/arm/configs/msm8610-perf_defconfig
@@ -62,7 +62,7 @@
 CONFIG_MSM_WATCHDOG_V2=y
 CONFIG_MSM_MEMORY_DUMP=y
 CONFIG_MSM_DLOAD_MODE=y
-CONFIG_MSM_ADSP_LOADER=m
+CONFIG_MSM_ADSP_LOADER=y
 CONFIG_MSM_OCMEM=y
 CONFIG_MSM_OCMEM_LOCAL_POWER_CTRL=y
 CONFIG_MSM_OCMEM_DEBUG=y
@@ -71,7 +71,6 @@
 CONFIG_SENSORS_ADSP=y
 CONFIG_MSM_ENABLE_WDOG_DEBUG_CONTROL=y
 CONFIG_MSM_BOOT_STATS=y
-CONFIG_STRICT_MEMORY_RWX=y
 CONFIG_NO_HZ=y
 CONFIG_HIGH_RES_TIMERS=y
 CONFIG_SMP=y
diff --git a/arch/arm/configs/msm8610_defconfig b/arch/arm/configs/msm8610_defconfig
index 20938d5..aec5ce2 100644
--- a/arch/arm/configs/msm8610_defconfig
+++ b/arch/arm/configs/msm8610_defconfig
@@ -59,7 +59,7 @@
 CONFIG_MSM_WATCHDOG_V2=y
 CONFIG_MSM_MEMORY_DUMP=y
 CONFIG_MSM_DLOAD_MODE=y
-CONFIG_MSM_ADSP_LOADER=m
+CONFIG_MSM_ADSP_LOADER=y
 CONFIG_MSM_OCMEM=y
 CONFIG_MSM_OCMEM_LOCAL_POWER_CTRL=y
 CONFIG_MSM_OCMEM_DEBUG=y
diff --git a/arch/arm/configs/msm8974-perf_defconfig b/arch/arm/configs/msm8974-perf_defconfig
index 7901e93..6ad1b12 100644
--- a/arch/arm/configs/msm8974-perf_defconfig
+++ b/arch/arm/configs/msm8974-perf_defconfig
@@ -68,7 +68,7 @@
 CONFIG_MSM_WATCHDOG_V2=y
 CONFIG_MSM_MEMORY_DUMP=y
 CONFIG_MSM_DLOAD_MODE=y
-CONFIG_MSM_ADSP_LOADER=m
+CONFIG_MSM_ADSP_LOADER=y
 CONFIG_MSM_OCMEM=y
 CONFIG_MSM_OCMEM_LOCAL_POWER_CTRL=y
 CONFIG_MSM_OCMEM_DEBUG=y
diff --git a/arch/arm/configs/msm8974_defconfig b/arch/arm/configs/msm8974_defconfig
index 8520f23..4937a64 100644
--- a/arch/arm/configs/msm8974_defconfig
+++ b/arch/arm/configs/msm8974_defconfig
@@ -68,7 +68,7 @@
 CONFIG_MSM_WATCHDOG_V2=y
 CONFIG_MSM_MEMORY_DUMP=y
 CONFIG_MSM_DLOAD_MODE=y
-CONFIG_MSM_ADSP_LOADER=m
+CONFIG_MSM_ADSP_LOADER=y
 CONFIG_MSM_OCMEM=y
 CONFIG_MSM_OCMEM_LOCAL_POWER_CTRL=y
 CONFIG_MSM_OCMEM_DEBUG=y
diff --git a/arch/arm/include/asm/archrandom.h b/arch/arm/include/asm/archrandom.h
new file mode 100644
index 0000000..5530d45
--- /dev/null
+++ b/arch/arm/include/asm/archrandom.h
@@ -0,0 +1,20 @@
+/* Copyright (c) 2013, 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 ARM_ASM_ARCHRANDOM_H
+#define ARM_ASM_ARCHRANDOM_H
+
+extern int arch_get_random_long(unsigned long *v);
+extern int arch_get_random_int(unsigned int *v);
+
+#endif
diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig
index 5c0d2cf..87e3e5b 100644
--- a/arch/arm/mach-msm/Kconfig
+++ b/arch/arm/mach-msm/Kconfig
@@ -2626,7 +2626,7 @@
 config MSM_ADSP_LOADER
         tristate "ADSP loader support"
         select SND_SOC_MSM_APRV2_INTF
-	depends on MSM_AUDIO_QDSP6V2 && m
+	depends on MSM_AUDIO_QDSP6V2
         help
           Enable ADSP image loader.
 	  The ADSP loader brings ADSP out of reset
diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile
index 12ffa66..89eb589 100644
--- a/arch/arm/mach-msm/Makefile
+++ b/arch/arm/mach-msm/Makefile
@@ -431,3 +431,4 @@
 
 obj-$(CONFIG_WALL_CLK) += wallclk.o
 obj-$(CONFIG_WALL_CLK_SYSFS) += wallclk_sysfs.o
+obj-$(CONFIG_ARCH_RANDOM) += early_random.o
diff --git a/arch/arm/mach-msm/board-8610-gpiomux.c b/arch/arm/mach-msm/board-8610-gpiomux.c
index bfd8e38..dc9c13d 100644
--- a/arch/arm/mach-msm/board-8610-gpiomux.c
+++ b/arch/arm/mach-msm/board-8610-gpiomux.c
@@ -511,6 +511,62 @@
 	},
 };
 
+static struct gpiomux_setting interrupt_gpio_active = {
+	.func = GPIOMUX_FUNC_GPIO,
+	.drv = GPIOMUX_DRV_6MA,
+	.pull = GPIOMUX_PULL_UP,
+};
+
+static struct gpiomux_setting interrupt_gpio_suspend_pullup = {
+	.func = GPIOMUX_FUNC_GPIO,
+	.drv = GPIOMUX_DRV_6MA,
+	.pull = GPIOMUX_PULL_UP,
+};
+
+static struct gpiomux_setting interrupt_gpio_suspend_pulldown = {
+	.func = GPIOMUX_FUNC_GPIO,
+	.drv = GPIOMUX_DRV_6MA,
+	.pull = GPIOMUX_PULL_UP,
+};
+
+static struct msm_gpiomux_config msm_interrupt_configs[] __initdata = {
+	{
+		.gpio = 77,	/* NFC_IRQ */
+		.settings = {
+			[GPIOMUX_ACTIVE]    = &interrupt_gpio_active,
+			[GPIOMUX_SUSPENDED] = &interrupt_gpio_suspend_pullup,
+		},
+	},
+	{
+		.gpio = 78,	/*ETH_INT */
+		.settings = {
+			[GPIOMUX_ACTIVE]    = &interrupt_gpio_active,
+			[GPIOMUX_SUSPENDED] = &interrupt_gpio_suspend_pullup,
+		},
+	},
+	{
+		.gpio = 80,	/*ALSP_INT */
+		.settings = {
+			[GPIOMUX_ACTIVE]    = &interrupt_gpio_active,
+			[GPIOMUX_SUSPENDED] = &interrupt_gpio_suspend_pullup,
+		},
+	},
+	{
+		.gpio = 81,	/*ACCEL_INT1 */
+		.settings = {
+			[GPIOMUX_ACTIVE]    = &interrupt_gpio_active,
+			[GPIOMUX_SUSPENDED] = &interrupt_gpio_suspend_pulldown,
+		},
+	},
+	{
+		.gpio = 82,	/*ACCEL_INT2 */
+		.settings = {
+			[GPIOMUX_ACTIVE]    = &interrupt_gpio_active,
+			[GPIOMUX_SUSPENDED] = &interrupt_gpio_suspend_pulldown,
+		},
+	},
+};
+
 void __init msm8610_init_gpiomux(void)
 {
 	int rc;
@@ -539,4 +595,7 @@
 	msm_gpiomux_install(msm_sensor_configs, ARRAY_SIZE(msm_sensor_configs));
 	msm_gpiomux_install(msm_gpio_int_configs,
 			ARRAY_SIZE(msm_gpio_int_configs));
+	if (of_board_is_qrd())
+		msm_gpiomux_install(msm_interrupt_configs,
+			ARRAY_SIZE(msm_interrupt_configs));
 }
diff --git a/arch/arm/mach-msm/clock-8226.c b/arch/arm/mach-msm/clock-8226.c
index 8a79687..c4097ca 100644
--- a/arch/arm/mach-msm/clock-8226.c
+++ b/arch/arm/mach-msm/clock-8226.c
@@ -3400,6 +3400,10 @@
 	/* eeprom clocks */
 	CLK_LOOKUP("cam_src_clk", mclk0_clk_src.c, "6c.qcom,eeprom"),
 	CLK_LOOKUP("cam_clk", camss_mclk0_clk.c, "6c.qcom,eeprom"),
+	CLK_LOOKUP("cam_src_clk", mclk0_clk_src.c, "18.qcom,eeprom"),
+	CLK_LOOKUP("cam_clk", camss_mclk0_clk.c, "18.qcom,eeprom"),
+	CLK_LOOKUP("cam_src_clk", mclk0_clk_src.c, "6b.qcom,eeprom"),
+	CLK_LOOKUP("cam_clk", camss_mclk0_clk.c, "6b.qcom,eeprom"),
 
 	/* CCI clocks */
 	CLK_LOOKUP("camss_top_ahb_clk", camss_top_ahb_clk.c,
diff --git a/arch/arm/mach-msm/clock-8610.c b/arch/arm/mach-msm/clock-8610.c
index f8c206b..92dab97 100644
--- a/arch/arm/mach-msm/clock-8610.c
+++ b/arch/arm/mach-msm/clock-8610.c
@@ -1551,7 +1551,6 @@
 	F_END,
 };
 
-static struct branch_clk mmss_mmssnoc_axi_clk;
 static struct rcg_clk axi_clk_src = {
 	.cmd_rcgr_reg = AXI_CMD_RCGR,
 	.set_rate = set_rate_hid,
@@ -1563,7 +1562,6 @@
 		.ops = &clk_ops_rcg,
 		VDD_DIG_FMAX_MAP2(LOW, 100000000, NOMINAL, 200000000),
 		CLK_INIT(axi_clk_src.c),
-		.depends = &mmss_mmssnoc_axi_clk.c
 	},
 };
 
@@ -2265,6 +2263,7 @@
 	},
 };
 
+static struct branch_clk mmss_mmssnoc_axi_clk;
 static struct branch_clk mdp_axi_clk = {
 	.cbcr_reg = MDP_AXI_CBCR,
 	.base = &virt_bases[MMSS_BASE],
@@ -2275,6 +2274,7 @@
 		.dbg_name = "mdp_axi_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(mdp_axi_clk.c),
+		.depends = &mmss_mmssnoc_axi_clk.c,
 	},
 };
 
@@ -2330,6 +2330,7 @@
 	.has_sibling = 1,
 	.base = &virt_bases[MMSS_BASE],
 	.c = {
+		.parent = &axi_clk_src.c,
 		.dbg_name = "mmss_mmssnoc_axi_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(mmss_mmssnoc_axi_clk.c),
@@ -2345,6 +2346,7 @@
 		.dbg_name = "mmss_s0_axi_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(mmss_s0_axi_clk.c),
+		.depends = &mmss_mmssnoc_axi_clk.c,
 	},
 };
 
@@ -2421,6 +2423,7 @@
 		.dbg_name = "vfe_axi_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(vfe_axi_clk.c),
+		.depends = &mmss_mmssnoc_axi_clk.c,
 	},
 };
 
diff --git a/arch/arm/mach-msm/cpr-regulator.c b/arch/arm/mach-msm/cpr-regulator.c
index 60a62ec..b69b155 100644
--- a/arch/arm/mach-msm/cpr-regulator.c
+++ b/arch/arm/mach-msm/cpr-regulator.c
@@ -28,6 +28,7 @@
 #include <linux/regulator/driver.h>
 #include <linux/regulator/of_regulator.h>
 #include <linux/regulator/cpr-regulator.h>
+#include <mach/scm.h>
 
 /* Register Offsets for RB-CPR and Bit Definitions */
 
@@ -153,6 +154,9 @@
 	u32		pvs_bin;
 	u32		process;
 
+	/* Control parameter to read efuse parameters by trustzone API */
+	bool		use_tz_api;
+
 	/* APC voltage regulator */
 	struct regulator	*vdd_apc;
 
@@ -223,6 +227,45 @@
 			pr_debug(message, ##__VA_ARGS__); \
 	} while (0)
 
+
+static u64 cpr_read_efuse_row(struct cpr_regulator *cpr_vreg, u32 row_num)
+{
+	int rc;
+	u64 efuse_bits;
+	struct cpr_read_req {
+		u32 row_address;
+		int addr_type;
+	} req;
+
+	struct cpr_read_rsp {
+		u32 row_data[2];
+		u32 status;
+	} rsp;
+
+	if (cpr_vreg->use_tz_api != true) {
+		efuse_bits = readll_relaxed(cpr_vreg->efuse_base
+			+ row_num * BYTES_PER_FUSE_ROW);
+		return efuse_bits;
+	}
+
+	req.row_address = cpr_vreg->efuse_addr + row_num * BYTES_PER_FUSE_ROW;
+	req.addr_type = 0;
+	efuse_bits = 0;
+
+	rc = scm_call(SCM_SVC_FUSE, SCM_FUSE_READ,
+			&req, sizeof(req), &rsp, sizeof(rsp));
+
+	if (rc) {
+		pr_err("read row %d failed, err code = %d", row_num, rc);
+	} else {
+		efuse_bits = ((u64)(rsp.row_data[1]) << 32) +
+				(u64)rsp.row_data[0];
+	}
+
+	return efuse_bits;
+}
+
+
 static bool cpr_is_allowed(struct cpr_regulator *cpr_vreg)
 {
 	if (cpr_vreg->cpr_fuse_disable || !cpr_enable)
@@ -933,18 +976,17 @@
 static int __devinit cpr_is_fuse_redundant(struct cpr_regulator *cpr_vreg,
 					 u32 redun_sel[4])
 {
-	u32 fuse_bits;
+	u64 fuse_bits;
 	int redundant;
 
-	fuse_bits = readl_relaxed(cpr_vreg->efuse_base
-				  + redun_sel[0] * BYTES_PER_FUSE_ROW);
+	fuse_bits = cpr_read_efuse_row(cpr_vreg, redun_sel[0]);
 	fuse_bits = (fuse_bits >> redun_sel[1]) & ((1 << redun_sel[2]) - 1);
 	if (fuse_bits == redun_sel[3])
 		redundant = 1;
 	else
 		redundant = 0;
 
-	pr_info("[row:%d] = 0x%x @%d:%d = %d?: redundant=%d\n",
+	pr_info("[row:%d] = 0x%llx @%d:%d = %d?: redundant=%d\n",
 		redun_sel[0], fuse_bits,
 		redun_sel[1], redun_sel[2], redun_sel[3], redundant);
 	return redundant;
@@ -954,7 +996,7 @@
 			       struct cpr_regulator *cpr_vreg)
 {
 	struct device_node *of_node = pdev->dev.of_node;
-	u32 efuse_bits;
+	u64 efuse_bits;
 	int rc, process;
 	u32 pvs_fuse[3], pvs_fuse_redun_sel[4];
 	bool redundant;
@@ -986,8 +1028,8 @@
 	}
 
 	/* Construct PVS process # from the efuse bits */
-	efuse_bits = readl_relaxed(cpr_vreg->efuse_base +
-				   pvs_fuse[0] * BYTES_PER_FUSE_ROW);
+
+	efuse_bits = cpr_read_efuse_row(cpr_vreg, pvs_fuse[0]);
 	cpr_vreg->pvs_bin = (efuse_bits >> pvs_fuse[1]) &
 				   ((1 << pvs_fuse[2]) - 1);
 
@@ -1001,7 +1043,7 @@
 	}
 
 	process = cpr_vreg->pvs_bin_process[cpr_vreg->pvs_bin];
-	pr_info("[row:%d] = 0x%08X, n_bits=%d, bin=%d (%d)\n",
+	pr_info("[row:%d] = 0x%llX, n_bits=%d, bin=%d (%d)\n",
 		pvs_fuse[0], efuse_bits, pvs_fuse[2],
 		cpr_vreg->pvs_bin, process);
 
@@ -1147,8 +1189,7 @@
 	}
 
 	/* Read the control bits of eFuse */
-	fuse_bits = readll_relaxed(cpr_vreg->efuse_base
-				   + cpr_fuse_row * BYTES_PER_FUSE_ROW);
+	fuse_bits = cpr_read_efuse_row(cpr_vreg, cpr_fuse_row);
 	pr_info("[row:%d] = 0x%llx\n", cpr_fuse_row, fuse_bits);
 
 	if (redundant) {
@@ -1175,8 +1216,8 @@
 					  &temp_row, rc);
 			if (rc)
 				return rc;
-			fuse_bits_2 = readll_relaxed(cpr_vreg->efuse_base
-					+ temp_row * BYTES_PER_FUSE_ROW);
+
+			fuse_bits_2 = cpr_read_efuse_row(cpr_vreg, temp_row);
 			pr_info("[original row:%d] = 0x%llx\n",
 				temp_row, fuse_bits_2);
 		}
@@ -1391,6 +1432,7 @@
 		pr_err("efuse_addr missing: res=%p\n", res);
 		return -EINVAL;
 	}
+
 	cpr_vreg->efuse_addr = res->start;
 	len = res->end - res->start + 1;
 
@@ -1402,6 +1444,12 @@
 				cpr_vreg->efuse_addr);
 		return -EINVAL;
 	}
+
+	if (of_property_read_bool(pdev->dev.of_node, "qcom,use-tz-api"))
+		cpr_vreg->use_tz_api = true;
+	else
+		cpr_vreg->use_tz_api = false;
+
 	return 0;
 }
 
diff --git a/arch/arm/mach-msm/devices-8064.c b/arch/arm/mach-msm/devices-8064.c
index 4daccb1..2fd1bf3 100644
--- a/arch/arm/mach-msm/devices-8064.c
+++ b/arch/arm/mach-msm/devices-8064.c
@@ -2634,11 +2634,12 @@
 
 static struct msm_rpm_master_stats_platform_data msm_rpm_master_stat_pdata = {
 	.masters = master_names,
-	.nomasters = ARRAY_SIZE(master_names),
+	.num_masters = ARRAY_SIZE(master_names),
+	.master_offset = 32,
 };
 
 struct platform_device apq8064_rpm_master_stat_device = {
-	.name = "msm_rpm_master_stat",
+	.name = "msm_rpm_master_stats",
 	.id = -1,
 	.num_resources	= ARRAY_SIZE(resources_rpm_master_stats),
 	.resource	= resources_rpm_master_stats,
diff --git a/arch/arm/mach-msm/devices-8930.c b/arch/arm/mach-msm/devices-8930.c
index e2a57f9..c4fe0df 100644
--- a/arch/arm/mach-msm/devices-8930.c
+++ b/arch/arm/mach-msm/devices-8930.c
@@ -610,11 +610,12 @@
 
 static struct msm_rpm_master_stats_platform_data msm_rpm_master_stat_pdata = {
 	.masters = master_names,
-	.nomasters = ARRAY_SIZE(master_names),
+	.num_masters = ARRAY_SIZE(master_names),
+	.master_offset = 32,
 };
 
 struct platform_device msm8930_rpm_master_stat_device = {
-	.name = "msm_rpm_master_stat",
+	.name = "msm_rpm_master_stats",
 	.id = -1,
 	.num_resources	= ARRAY_SIZE(resources_rpm_master_stats),
 	.resource	= resources_rpm_master_stats,
diff --git a/arch/arm/mach-msm/devices-8960.c b/arch/arm/mach-msm/devices-8960.c
index 71f58a6..6664e17 100644
--- a/arch/arm/mach-msm/devices-8960.c
+++ b/arch/arm/mach-msm/devices-8960.c
@@ -4018,11 +4018,12 @@
 
 static struct msm_rpm_master_stats_platform_data msm_rpm_master_stat_pdata = {
 	.masters = master_names,
-	.nomasters = ARRAY_SIZE(master_names),
+	.num_masters = ARRAY_SIZE(master_names),
+	.master_offset = 32,
 };
 
 struct platform_device msm8960_rpm_master_stat_device = {
-	.name = "msm_rpm_master_stat",
+	.name = "msm_rpm_master_stats",
 	.id = -1,
 	.num_resources	= ARRAY_SIZE(resources_rpm_master_stats),
 	.resource	= resources_rpm_master_stats,
diff --git a/arch/arm/mach-msm/devices-9615.c b/arch/arm/mach-msm/devices-9615.c
index 483d8b3..6a48646 100644
--- a/arch/arm/mach-msm/devices-9615.c
+++ b/arch/arm/mach-msm/devices-9615.c
@@ -1431,11 +1431,12 @@
 
 static struct msm_rpm_master_stats_platform_data msm_rpm_master_stat_pdata = {
 	.masters = master_names,
-	.nomasters = ARRAY_SIZE(master_names),
+	.num_masters = ARRAY_SIZE(master_names),
+	.master_offset = 32,
 };
 
 struct platform_device msm9615_rpm_master_stat_device = {
-	.name = "msm_rpm_master_stat",
+	.name = "msm_rpm_master_stats",
 	.id = -1,
 	.num_resources	= ARRAY_SIZE(resources_rpm_master_stats),
 	.resource	= resources_rpm_master_stats,
diff --git a/arch/arm/mach-msm/early_random.c b/arch/arm/mach-msm/early_random.c
new file mode 100644
index 0000000..e315b86
--- /dev/null
+++ b/arch/arm/mach-msm/early_random.c
@@ -0,0 +1,83 @@
+/* Copyright (c) 2013, 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/kernel.h>
+
+#include <mach/scm.h>
+
+#include <asm/io.h>
+#include <asm/cacheflush.h>
+
+#define TZ_SVC_CRYPTO	10
+#define PRNG_CMD_ID	0x01
+
+static int use_arch_random = 1;
+struct tz_prng_data {
+	uint8_t		*out_buf;
+	uint32_t	out_buf_sz;
+} __packed;
+
+DEFINE_SCM_BUFFER(common_scm_buf)
+DEFINE_MUTEX(arch_random_lock);
+#define RANDOM_BUFFER_SIZE	PAGE_SIZE
+char random_buffer[RANDOM_BUFFER_SIZE] __aligned(PAGE_SIZE);
+
+int arch_get_random_common(void *v, size_t size)
+{
+	struct tz_prng_data data;
+	int ret;
+	u32 resp;
+
+	if (!use_arch_random)
+		return 0;
+
+	if (size > sizeof(random_buffer))
+		return 0;
+
+	mutex_lock(&arch_random_lock);
+	data.out_buf = (uint8_t *) virt_to_phys(random_buffer);
+	data.out_buf_sz = size;
+
+	ret = scm_call_noalloc(TZ_SVC_CRYPTO, PRNG_CMD_ID, &data,
+			sizeof(data), &resp, sizeof(resp),
+			common_scm_buf, SCM_BUFFER_SIZE(common_scm_buf));
+	if (!ret) {
+		dmac_inv_range(random_buffer, random_buffer +
+						RANDOM_BUFFER_SIZE);
+		outer_inv_range(
+			(unsigned long) virt_to_phys(random_buffer),
+			(unsigned long) virt_to_phys(random_buffer) +
+						RANDOM_BUFFER_SIZE);
+		memcpy(v, random_buffer, size);
+	}
+	mutex_unlock(&arch_random_lock);
+	return !ret;
+}
+
+int arch_get_random_long(unsigned long *v)
+{
+	return arch_get_random_common(v, sizeof(unsigned long));
+}
+
+int arch_get_random_int(unsigned int *v)
+{
+	return arch_get_random_common(v, sizeof(unsigned int));
+}
+
+int arch_random_init(void)
+{
+	use_arch_random = 0;
+
+	return 0;
+}
+module_init(arch_random_init);
diff --git a/arch/arm/mach-msm/include/mach/gpio.h b/arch/arm/mach-msm/include/mach/gpio.h
index bfcce73..45be31f 100644
--- a/arch/arm/mach-msm/include/mach/gpio.h
+++ b/arch/arm/mach-msm/include/mach/gpio.h
@@ -1,6 +1,6 @@
 /*
  * Copyright (C) 2007 Google, Inc.
- * Copyright (c) 2009-2012, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2009-2013, The Linux Foundation. All rights reserved.
  * Author: Mike Lockwood <lockwood@android.com>
  *
  * This software is licensed under the terms of the GNU General Public
@@ -188,6 +188,7 @@
 	TLMM_PULL_SDC1_CLK,
 	TLMM_PULL_SDC1_CMD,
 	TLMM_PULL_SDC1_DATA,
+	TLMM_PULL_SDC1_RCLK,
 };
 
 #if defined(CONFIG_GPIO_MSM_V2) || defined(CONFIG_GPIO_MSM_V3)
diff --git a/arch/arm/mach-msm/include/mach/scm.h b/arch/arm/mach-msm/include/mach/scm.h
index 4186603..9d186ce 100644
--- a/arch/arm/mach-msm/include/mach/scm.h
+++ b/arch/arm/mach-msm/include/mach/scm.h
@@ -26,10 +26,24 @@
 #define SCM_SVC_ES			0x10
 #define SCM_SVC_TZSCHEDULER		0xFC
 
+#define SCM_FUSE_READ			0x7
+
+#define DEFINE_SCM_BUFFER(__n) \
+static char __n[PAGE_SIZE] __aligned(PAGE_SIZE);
+
+#define SCM_BUFFER_SIZE(__buf)	sizeof(__buf)
+
+#define SCM_BUFFER_PHYS(__buf)	virt_to_phys(__buf)
+
 #ifdef CONFIG_MSM_SCM
 extern int scm_call(u32 svc_id, u32 cmd_id, const void *cmd_buf, size_t cmd_len,
 		void *resp_buf, size_t resp_len);
 
+extern int scm_call_noalloc(u32 svc_id, u32 cmd_id, const void *cmd_buf,
+		size_t cmd_len, void *resp_buf, size_t resp_len,
+		void *scm_buf, size_t scm_buf_size);
+
+
 extern s32 scm_call_atomic1(u32 svc, u32 cmd, u32 arg1);
 extern s32 scm_call_atomic2(u32 svc, u32 cmd, u32 arg1, u32 arg2);
 extern s32 scm_call_atomic3(u32 svc, u32 cmd, u32 arg1, u32 arg2, u32 arg3);
@@ -50,6 +64,13 @@
 	return 0;
 }
 
+static inline int scm_call_noalloc(u32 svc_id, u32 cmd_id,
+		const void *cmd_buf, size_t cmd_len, void *resp_buf,
+		size_t resp_len, void *scm_buf, size_t scm_buf_size)
+{
+	return 0;
+}
+
 static inline s32 scm_call_atomic1(u32 svc, u32 cmd, u32 arg1)
 {
 	return 0;
diff --git a/arch/arm/mach-msm/krait-regulator.c b/arch/arm/mach-msm/krait-regulator.c
index 4b2259e..4fee0ae 100644
--- a/arch/arm/mach-msm/krait-regulator.c
+++ b/arch/arm/mach-msm/krait-regulator.c
@@ -760,7 +760,7 @@
 
 	setpoint = DIV_ROUND_UP(uV, LV_RANGE_STEP);
 
-	rc = msm_spm_apcs_set_vdd(setpoint);
+	rc = msm_spm_set_vdd(0, setpoint); /* value of CPU is don't care */
 	if (rc < 0)
 		pr_err("could not set %duV setpt = 0x%x rc = %d\n",
 				uV, setpoint, rc);
diff --git a/arch/arm/mach-msm/qdsp6v2/adsp-loader.c b/arch/arm/mach-msm/qdsp6v2/adsp-loader.c
index e74fdf9..0506e7e 100644
--- a/arch/arm/mach-msm/qdsp6v2/adsp-loader.c
+++ b/arch/arm/mach-msm/qdsp6v2/adsp-loader.c
@@ -20,39 +20,60 @@
 #include <mach/subsystem_restart.h>
 #include <mach/qdsp6v2/apr.h>
 #include <linux/of_device.h>
+#include <linux/sysfs.h>
 
 #define Q6_PIL_GET_DELAY_MS 100
+#define BOOT_CMD 1
+
+static ssize_t adsp_boot_store(struct kobject *kobj,
+	struct kobj_attribute *attr,
+	const char *buf, size_t count);
 
 struct adsp_loader_private {
 	void *pil_h;
+	struct kobject *boot_adsp_obj;
+	struct attribute_group *attr_group;
 };
 
-static int adsp_loader_probe(struct platform_device *pdev)
+static struct kobj_attribute adsp_boot_attribute =
+	__ATTR(boot, 0220, NULL, adsp_boot_store);
+
+static struct attribute *attrs[] = {
+	&adsp_boot_attribute.attr,
+	NULL,
+};
+
+static struct platform_device *adsp_private;
+
+static void adsp_loader_do(struct platform_device *pdev)
 {
-	struct adsp_loader_private *priv;
-	int rc = 0;
+
+	struct adsp_loader_private *priv = NULL;
+
 	const char *adsp_dt = "qcom,adsp-state";
+	int rc = 0;
 	u32 adsp_state;
 
 	rc = of_property_read_u32(pdev->dev.of_node, adsp_dt, &adsp_state);
 	if (rc) {
 		dev_err(&pdev->dev,
 			"%s: ADSP state = %x\n", __func__, adsp_state);
-		return rc;
+		return;
 	}
 
 	if (adsp_state == APR_SUBSYS_DOWN) {
-		priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
-		if (!priv)
-			return -ENOMEM;
+		if (pdev) {
+			priv = platform_get_drvdata(pdev);
+		} else {
+			pr_err("%s: Private data get failed\n", __func__);
+			goto fail;
+		}
 
-		platform_set_drvdata(pdev, priv);
 
 		priv->pil_h = subsystem_get("adsp");
 		if (IS_ERR(priv->pil_h)) {
-			pr_err("%s: pil get adsp failed, error:%d\n",
-				__func__, rc);
-			devm_kfree(&pdev->dev, priv);
+			pr_err("%s: pil get failed,\n",
+				__func__);
 			goto fail;
 		}
 
@@ -60,25 +81,123 @@
 		apr_set_q6_state(APR_SUBSYS_LOADED);
 	} else if (adsp_state == APR_SUBSYS_LOADED) {
 		dev_dbg(&pdev->dev,
-			"%s:MDM9x25 ADSP state = %x\n", __func__, adsp_state);
+			"%s: ADSP state = %x\n", __func__, adsp_state);
 		apr_set_q6_state(APR_SUBSYS_LOADED);
 	}
 
-	/* Query for MMPM API */
 
 	pr_info("%s: Q6/ADSP image is loaded\n", __func__);
+	return;
 fail:
-	return rc;
+
+	pr_err("%s: Q6/ADSP image loading failed\n", __func__);
+	return;
+}
+
+
+static ssize_t adsp_boot_store(struct kobject *kobj,
+	struct kobj_attribute *attr,
+	const char *buf,
+	size_t count)
+{
+	int boot = 0;
+	sscanf(buf, "%du", &boot);
+
+	if (boot == BOOT_CMD) {
+		pr_debug("%s:going to call adsp_loader_do", __func__);
+		adsp_loader_do(adsp_private);
+	}
+	return count;
+}
+
+static int adsp_loader_init_sysfs(struct platform_device *pdev)
+{
+	int ret = -EINVAL;
+	struct adsp_loader_private *priv = NULL;
+	adsp_private = NULL;
+
+	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv) {
+		pr_err("%s: memory alloc failed\n", __func__);
+		ret = -ENOMEM;
+		goto error_return;
+	}
+
+	platform_set_drvdata(pdev, priv);
+
+	priv->pil_h = NULL;
+	priv->boot_adsp_obj = NULL;
+	priv->attr_group = devm_kzalloc(&pdev->dev,
+				sizeof(*(priv->attr_group)),
+				GFP_KERNEL);
+	if (!priv->attr_group) {
+		pr_err("%s: malloc attr_group failed\n",
+						__func__);
+		ret = -ENOMEM;
+		goto error_return;
+	}
+
+	priv->attr_group->attrs = attrs;
+
+	priv->boot_adsp_obj = kobject_create_and_add("boot_adsp", kernel_kobj);
+	if (!priv->boot_adsp_obj) {
+		pr_err("%s: sysfs create and add failed\n",
+						__func__);
+		ret = -ENOMEM;
+		goto error_return;
+	}
+
+	ret = sysfs_create_group(priv->boot_adsp_obj, priv->attr_group);
+	if (ret) {
+		pr_err("%s: sysfs create group failed %d\n", \
+							__func__, ret);
+		goto error_return;
+	}
+
+	adsp_private = pdev;
+
+	return 0;
+
+error_return:
+
+	if (priv->boot_adsp_obj) {
+		kobject_del(priv->boot_adsp_obj);
+		priv->boot_adsp_obj = NULL;
+	}
+
+	return ret;
 }
 
 static int adsp_loader_remove(struct platform_device *pdev)
 {
-	struct adsp_loader_private *priv;
+	struct adsp_loader_private *priv = NULL;
 
 	priv = platform_get_drvdata(pdev);
-	if (priv != NULL)
+
+	if (!priv)
+		return 0;
+
+	if (priv->pil_h) {
 		subsystem_put(priv->pil_h);
-	pr_info("%s: Q6/ADSP image is unloaded\n", __func__);
+		priv->pil_h = NULL;
+	}
+
+	if (priv->boot_adsp_obj) {
+		sysfs_remove_group(priv->boot_adsp_obj, priv->attr_group);
+		kobject_del(priv->boot_adsp_obj);
+		priv->boot_adsp_obj = NULL;
+	}
+
+	return 0;
+}
+
+static int adsp_loader_probe(struct platform_device *pdev)
+{
+	int ret = adsp_loader_init_sysfs(pdev);
+	if (ret != 0) {
+		pr_err("%s: Error in initing sysfs\n", __func__);
+		return ret;
+	}
 
 	return 0;
 }
diff --git a/arch/arm/mach-msm/qdsp6v2/audio_aac.c b/arch/arm/mach-msm/qdsp6v2/audio_aac.c
index f4eb318..caeb79d 100644
--- a/arch/arm/mach-msm/qdsp6v2/audio_aac.c
+++ b/arch/arm/mach-msm/qdsp6v2/audio_aac.c
@@ -262,6 +262,10 @@
 		goto fail;
 	}
 	rc = audio_aio_open(audio, file);
+	if (rc < 0) {
+		pr_err("audio_aio_open rc=%d\n", rc);
+		goto fail;
+	}
 
 #ifdef CONFIG_DEBUG_FS
 	snprintf(name, sizeof name, "msm_aac_%04x", audio->ac->session);
diff --git a/arch/arm/mach-msm/qdsp6v2/audio_amrnb.c b/arch/arm/mach-msm/qdsp6v2/audio_amrnb.c
index 056a161..fc023c1 100644
--- a/arch/arm/mach-msm/qdsp6v2/audio_amrnb.c
+++ b/arch/arm/mach-msm/qdsp6v2/audio_amrnb.c
@@ -2,7 +2,7 @@
  *
  * Copyright (C) 2008 Google, Inc.
  * Copyright (C) 2008 HTC Corporation
- * Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
  *
  * This software is licensed under the terms of the GNU General Public
  * License version 2, as published by the Free Software Foundation, and
@@ -120,6 +120,10 @@
 		goto fail;
 	}
 	rc = audio_aio_open(audio, file);
+	if (rc < 0) {
+		pr_err("audio_aio_open rc=%d\n", rc);
+		goto fail;
+	}
 
 #ifdef CONFIG_DEBUG_FS
 	snprintf(name, sizeof name, "msm_amrnb_%04x", audio->ac->session);
diff --git a/arch/arm/mach-msm/qdsp6v2/audio_amrwb.c b/arch/arm/mach-msm/qdsp6v2/audio_amrwb.c
index 54ca4e8..256da4d 100644
--- a/arch/arm/mach-msm/qdsp6v2/audio_amrwb.c
+++ b/arch/arm/mach-msm/qdsp6v2/audio_amrwb.c
@@ -2,7 +2,7 @@
  *
  * Copyright (C) 2008 Google, Inc.
  * Copyright (C) 2008 HTC Corporation
- * Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
  *
  * This software is licensed under the terms of the GNU General Public
  * License version 2, as published by the Free Software Foundation, and
@@ -122,6 +122,10 @@
 		goto fail;
 	}
 	rc = audio_aio_open(audio, file);
+	if (rc < 0) {
+		pr_err("audio_aio_open rc=%d\n", rc);
+		goto fail;
+	}
 
 #ifdef CONFIG_DEBUG_FS
 	snprintf(name, sizeof name, "msm_amrwb_%04x", audio->ac->session);
diff --git a/arch/arm/mach-msm/qdsp6v2/audio_amrwbplus.c b/arch/arm/mach-msm/qdsp6v2/audio_amrwbplus.c
index 2889c14..902e06d 100644
--- a/arch/arm/mach-msm/qdsp6v2/audio_amrwbplus.c
+++ b/arch/arm/mach-msm/qdsp6v2/audio_amrwbplus.c
@@ -2,7 +2,7 @@
  *
  * Copyright (C) 2008 Google, Inc.
  * Copyright (C) 2008 HTC Corporation
- * Copyright (c) 2010-2012, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2010-2013, The Linux Foundation. All rights reserved.
  *
  * This software is licensed under the terms of the GNU General Public
  * License version 2, as published by the Free Software Foundation, and
@@ -199,6 +199,10 @@
 		goto fail;
 	}
 	rc = audio_aio_open(audio, file);
+	if (rc < 0) {
+		pr_err("audio_aio_open rc=%d\n", rc);
+		goto fail;
+	}
 
 	config_debug_fs(audio);
 	pr_debug("%s: AMRWBPLUS dec success mode[%d]session[%d]\n", __func__,
diff --git a/arch/arm/mach-msm/qdsp6v2/audio_evrc.c b/arch/arm/mach-msm/qdsp6v2/audio_evrc.c
index 261642c..3498e69 100644
--- a/arch/arm/mach-msm/qdsp6v2/audio_evrc.c
+++ b/arch/arm/mach-msm/qdsp6v2/audio_evrc.c
@@ -2,7 +2,7 @@
  *
  * Copyright (C) 2008 Google, Inc.
  * Copyright (C) 2008 HTC Corporation
- * Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
  *
  * This software is licensed under the terms of the GNU General Public
  * License version 2, as published by the Free Software Foundation, and
@@ -128,6 +128,10 @@
 		goto fail;
 	}
 	rc = audio_aio_open(audio, file);
+	if (rc < 0) {
+		pr_err("audio_aio_open rc=%d\n", rc);
+		goto fail;
+	}
 
 #ifdef CONFIG_DEBUG_FS
 	snprintf(name, sizeof name, "msm_evrc_%04x", audio->ac->session);
diff --git a/arch/arm/mach-msm/qdsp6v2/audio_mp3.c b/arch/arm/mach-msm/qdsp6v2/audio_mp3.c
index ebcca3c..d2f0270 100644
--- a/arch/arm/mach-msm/qdsp6v2/audio_mp3.c
+++ b/arch/arm/mach-msm/qdsp6v2/audio_mp3.c
@@ -128,6 +128,10 @@
 		goto fail;
 	}
 	rc = audio_aio_open(audio, file);
+	if (rc < 0) {
+		pr_err("audio_aio_open rc=%d\n", rc);
+		goto fail;
+	}
 
 #ifdef CONFIG_DEBUG_FS
 	snprintf(name, sizeof name, "msm_mp3_%04x", audio->ac->session);
diff --git a/arch/arm/mach-msm/qdsp6v2/audio_multi_aac.c b/arch/arm/mach-msm/qdsp6v2/audio_multi_aac.c
index 8153145..0a8ce8e 100644
--- a/arch/arm/mach-msm/qdsp6v2/audio_multi_aac.c
+++ b/arch/arm/mach-msm/qdsp6v2/audio_multi_aac.c
@@ -282,6 +282,10 @@
 		goto fail;
 	}
 	rc = audio_aio_open(audio, file);
+	if (rc < 0) {
+		pr_err("audio_aio_open rc=%d\n", rc);
+		goto fail;
+	}
 
 #ifdef CONFIG_DEBUG_FS
 	snprintf(name, sizeof name, "msm_multi_aac_%04x", audio->ac->session);
diff --git a/arch/arm/mach-msm/qdsp6v2/audio_qcelp.c b/arch/arm/mach-msm/qdsp6v2/audio_qcelp.c
index 69a9fcb..4993226 100644
--- a/arch/arm/mach-msm/qdsp6v2/audio_qcelp.c
+++ b/arch/arm/mach-msm/qdsp6v2/audio_qcelp.c
@@ -2,7 +2,7 @@
  *
  * Copyright (C) 2008 Google, Inc.
  * Copyright (C) 2008 HTC Corporation
- * Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
  *
  * This software is licensed under the terms of the GNU General Public
  * License version 2, as published by the Free Software Foundation, and
@@ -133,6 +133,10 @@
 		goto fail;
 	}
 	rc = audio_aio_open(audio, file);
+	if (rc < 0) {
+		pr_err("audio_aio_open rc=%d\n", rc);
+		goto fail;
+	}
 
 #ifdef CONFIG_DEBUG_FS
 	snprintf(name, sizeof name, "msm_qcelp_%04x", audio->ac->session);
diff --git a/arch/arm/mach-msm/qdsp6v2/audio_utils_aio.c b/arch/arm/mach-msm/qdsp6v2/audio_utils_aio.c
index 8baac01..a1463bc 100644
--- a/arch/arm/mach-msm/qdsp6v2/audio_utils_aio.c
+++ b/arch/arm/mach-msm/qdsp6v2/audio_utils_aio.c
@@ -1095,6 +1095,7 @@
 	int rc = 0;
 	int i;
 	struct audio_aio_event *e_node = NULL;
+	struct list_head *ptr, *next;
 
 	/* Settings will be re-config at AUDIO_SET_CONFIG,
 	 * but at least we need to have initial config
@@ -1147,28 +1148,35 @@
 		else {
 			pr_err("%s[%p]:event pkt alloc failed\n",
 				__func__, audio);
-			break;
+			rc = -ENOMEM;
+			goto cleanup;
 		}
 	}
 	audio->client = msm_audio_ion_client_create(UINT_MAX,
 						    "Audio_Dec_Client");
 	if (IS_ERR_OR_NULL(audio->client)) {
 		pr_err("Unable to create ION client\n");
-		rc = -EACCES;
-		goto fail;
+		rc = -ENOMEM;
+		goto cleanup;
 	}
 	pr_debug("Ion client create in audio_aio_open %p", audio->client);
 
 	rc = register_volume_listener(audio);
 	if (rc < 0)
-		goto fail;
+		goto ion_cleanup;
 
 	return 0;
-
+ion_cleanup:
+	msm_audio_ion_client_destroy(audio->client);
+	audio->client = NULL;
+cleanup:
+	list_for_each_safe(ptr, next, &audio->free_event_queue) {
+		e_node = list_first_entry(&audio->free_event_queue,
+				   struct audio_aio_event, list);
+		list_del(&e_node->list);
+		kfree(e_node);
+	}
 fail:
-	q6asm_audio_client_free(audio->ac);
-	kfree(audio->codec_cfg);
-	kfree(audio);
 	return rc;
 }
 
diff --git a/arch/arm/mach-msm/qdsp6v2/audio_wma.c b/arch/arm/mach-msm/qdsp6v2/audio_wma.c
index 1d1da1a..5e3de86 100644
--- a/arch/arm/mach-msm/qdsp6v2/audio_wma.c
+++ b/arch/arm/mach-msm/qdsp6v2/audio_wma.c
@@ -2,7 +2,7 @@
  *
  * Copyright (C) 2008 Google, Inc.
  * Copyright (C) 2008 HTC Corporation
- * Copyright (c) 2009-2012, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2009-2013, The Linux Foundation. All rights reserved.
  *
  * This software is licensed under the terms of the GNU General Public
  * License version 2, as published by the Free Software Foundation, and
@@ -167,6 +167,10 @@
 		goto fail;
 	}
 	rc = audio_aio_open(audio, file);
+	if (rc < 0) {
+		pr_err("audio_aio_open rc=%d\n", rc);
+		goto fail;
+	}
 
 #ifdef CONFIG_DEBUG_FS
 	snprintf(name, sizeof name, "msm_wma_%04x", audio->ac->session);
diff --git a/arch/arm/mach-msm/qdsp6v2/audio_wmapro.c b/arch/arm/mach-msm/qdsp6v2/audio_wmapro.c
index 2b2d772..ce49cac 100644
--- a/arch/arm/mach-msm/qdsp6v2/audio_wmapro.c
+++ b/arch/arm/mach-msm/qdsp6v2/audio_wmapro.c
@@ -2,7 +2,7 @@
  *
  * Copyright (C) 2008 Google, Inc.
  * Copyright (C) 2008 HTC Corporation
- * Copyright (c) 2009-2012, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2009-2013, The Linux Foundation. All rights reserved.
  *
  * This software is licensed under the terms of the GNU General Public
  * License version 2, as published by the Free Software Foundation, and
@@ -227,6 +227,10 @@
 		goto fail;
 	}
 	rc = audio_aio_open(audio, file);
+	if (rc < 0) {
+		pr_err("audio_aio_open rc=%d\n", rc);
+		goto fail;
+	}
 
 #ifdef CONFIG_DEBUG_FS
 	snprintf(name, sizeof name, "msm_wmapro_%04x", audio->ac->session);
diff --git a/arch/arm/mach-msm/rpm_master_stat.c b/arch/arm/mach-msm/rpm_master_stat.c
index 49a1039..3e1789f 100644
--- a/arch/arm/mach-msm/rpm_master_stat.c
+++ b/arch/arm/mach-msm/rpm_master_stat.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2013, 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
@@ -22,39 +22,57 @@
 #include <linux/slab.h>
 #include <linux/types.h>
 #include <linux/mm.h>
+#include <linux/of.h>
 #include <asm/uaccess.h>
 
 #include <mach/msm_iomap.h>
 #include "rpm_stats.h"
-#define MSG_RAM_SIZE_PER_MASTER	32
 
-enum {
-	NUMSHUTDOWNS,
-	ACTIVECORES,
-	MASTER_ID_MAX,
-};
+#define RPM_MASTERS_BUF_LEN 400
 
-static char *msm_rpm_master_stats_id_labels[MASTER_ID_MAX] = {
-	[NUMSHUTDOWNS] = "num_shutdowns",
-	[ACTIVECORES] = "active_cores",
-};
+#define SNPRINTF(buf, size, format, ...) \
+	do { \
+		if (size > 0) { \
+			int ret; \
+			ret = snprintf(buf, size, format, ## __VA_ARGS__); \
+			if (ret > size) { \
+				buf += size; \
+				size = 0; \
+			} else { \
+				buf += ret; \
+				size -= ret; \
+			} \
+		} \
+	} while (0)
 
+#define GET_MASTER_NAME(a, prvdata) \
+	((a >= prvdata->num_masters) ? "Invalid Master Name" : \
+	 prvdata->master_names[a])
+
+#define GET_FIELD(a) ((strnstr(#a, ".", 80) + 1))
 
 struct msm_rpm_master_stats {
-	unsigned long numshutdowns;
-	unsigned long active_cores;
+	uint32_t active_cores;
+	uint32_t numshutdowns;
+	uint64_t shutdown_req;
+	uint64_t wakeup_ind;
+	uint64_t bringup_req;
+	uint64_t bringup_ack;
+	uint32_t wakeup_reason; /* 0 = rude wakeup, 1 = scheduled wakeup */
+	uint32_t last_sleep_transition_duration;
+	uint32_t last_wake_transition_duration;
 };
 
 struct msm_rpm_master_stats_private_data {
 	void __iomem *reg_base;
 	u32 len;
 	char **master_names;
-	u32 nomasters;
-	char buf[256];
+	u32 num_masters;
+	char buf[RPM_MASTERS_BUF_LEN];
 	struct msm_rpm_master_stats_platform_data *platform_data;
 };
 
-static int msm_rpm_master_stats_file_close(struct inode *inode,
+int msm_rpm_master_stats_file_close(struct inode *inode,
 		struct file *file)
 {
 	struct msm_rpm_master_stats_private_data *private = file->private_data;
@@ -67,53 +85,138 @@
 }
 
 static int msm_rpm_master_copy_stats(
-		struct msm_rpm_master_stats_private_data *pdata)
+		struct msm_rpm_master_stats_private_data *prvdata)
 {
 	struct msm_rpm_master_stats record;
-	static int nomasters;
-	int count;
+	struct msm_rpm_master_stats_platform_data *pdata;
+	static int master_cnt;
+	int count, j = 0;
+	char *buf;
 	static DEFINE_MUTEX(msm_rpm_master_stats_mutex);
-	int j = 0;
 
 	mutex_lock(&msm_rpm_master_stats_mutex);
-	/*
-	 * iterrate possible nomasters times.
-	 * 8960, 8064 have 5 masters.
-	 * 8930 has 4 masters.
-	 * 9x15 has 3 masters.
-	 */
-	if (nomasters > pdata->nomasters - 1) {
-		nomasters = 0;
+
+	/* Iterate possible number of masters */
+	if (master_cnt > prvdata->num_masters - 1) {
+		master_cnt = 0;
 		mutex_unlock(&msm_rpm_master_stats_mutex);
 		return 0;
 	}
 
-	record.numshutdowns = readl_relaxed(pdata->reg_base +
-			(nomasters * MSG_RAM_SIZE_PER_MASTER));
-	record.active_cores = readl_relaxed(pdata->reg_base +
-				(nomasters * MSG_RAM_SIZE_PER_MASTER + 4));
+	pdata = prvdata->platform_data;
+	count = RPM_MASTERS_BUF_LEN;
+	buf = prvdata->buf;
 
-	count = snprintf(pdata->buf, sizeof(pdata->buf),
-		"%s\n\t%s:%lu\n\t%s:%lu\n",
-		pdata->master_names[nomasters],
-		msm_rpm_master_stats_id_labels[0],
-		record.numshutdowns,
-		msm_rpm_master_stats_id_labels[1],
-		record.active_cores);
+	if (prvdata->platform_data->version == 2) {
+		SNPRINTF(buf, count, "%s\n",
+				GET_MASTER_NAME(master_cnt, prvdata));
 
-	j = find_first_bit(&record.active_cores, BITS_PER_LONG);
+		record.shutdown_req = readll_relaxed(prvdata->reg_base +
+			(master_cnt * pdata->master_offset +
+			offsetof(struct msm_rpm_master_stats, shutdown_req)));
+
+		SNPRINTF(buf, count, "\t%s:0x%llX\n",
+			GET_FIELD(record.shutdown_req),
+			record.shutdown_req);
+
+		record.wakeup_ind = readll_relaxed(prvdata->reg_base +
+			(master_cnt * pdata->master_offset +
+			offsetof(struct msm_rpm_master_stats, wakeup_ind)));
+
+		SNPRINTF(buf, count, "\t%s:0x%llX\n",
+			GET_FIELD(record.wakeup_ind),
+			record.wakeup_ind);
+
+		record.bringup_req = readll_relaxed(prvdata->reg_base +
+			(master_cnt * pdata->master_offset +
+			offsetof(struct msm_rpm_master_stats, bringup_req)));
+
+		SNPRINTF(buf, count, "\t%s:0x%llX\n",
+			GET_FIELD(record.bringup_req),
+			record.bringup_req);
+
+		record.bringup_ack = readll_relaxed(prvdata->reg_base +
+			(master_cnt * pdata->master_offset +
+			offsetof(struct msm_rpm_master_stats, bringup_ack)));
+
+		SNPRINTF(buf, count, "\t%s:0x%llX\n",
+			GET_FIELD(record.bringup_ack),
+			record.bringup_ack);
+
+		record.last_sleep_transition_duration =
+				readl_relaxed(prvdata->reg_base +
+				(master_cnt * pdata->master_offset +
+				offsetof(struct msm_rpm_master_stats,
+				last_sleep_transition_duration)));
+
+		SNPRINTF(buf, count, "\t%s:0x%x\n",
+			GET_FIELD(record.last_sleep_transition_duration),
+			record.last_sleep_transition_duration);
+
+		record.last_wake_transition_duration =
+				readl_relaxed(prvdata->reg_base +
+				(master_cnt * pdata->master_offset +
+				offsetof(struct msm_rpm_master_stats,
+				last_wake_transition_duration)));
+
+		SNPRINTF(buf, count, "\t%s:0x%x\n",
+			GET_FIELD(record.last_wake_transition_duration),
+			record.last_wake_transition_duration);
+
+		record.wakeup_reason = readl_relaxed(prvdata->reg_base +
+					(master_cnt * pdata->master_offset +
+					offsetof(struct msm_rpm_master_stats,
+					wakeup_reason)));
+
+		SNPRINTF(buf, count, "\t%s:0x%x\n",
+			GET_FIELD(record.wakeup_reason),
+			record.wakeup_reason);
+
+		record.numshutdowns = readl_relaxed(prvdata->reg_base +
+			(master_cnt * pdata->master_offset +
+			 offsetof(struct msm_rpm_master_stats, numshutdowns)));
+
+		SNPRINTF(buf, count, "\t%s:0x%x\n",
+			GET_FIELD(record.numshutdowns),
+			record.numshutdowns);
+
+		record.active_cores = readl_relaxed(prvdata->reg_base +
+			(master_cnt * pdata->master_offset) +
+			offsetof(struct msm_rpm_master_stats, active_cores));
+
+		SNPRINTF(buf, count, "\t%s:0x%x\n",
+			GET_FIELD(record.active_cores),
+			record.active_cores);
+	} else {
+		SNPRINTF(buf, count, "%s\n",
+				GET_MASTER_NAME(master_cnt, prvdata));
+
+		record.numshutdowns = readl_relaxed(prvdata->reg_base +
+				(master_cnt * pdata->master_offset) + 0x0);
+
+		SNPRINTF(buf, count, "\t%s:0x%0x\n",
+			GET_FIELD(record.numshutdowns),
+			record.numshutdowns);
+
+		record.active_cores = readl_relaxed(prvdata->reg_base +
+				(master_cnt * pdata->master_offset) + 0x4);
+
+		SNPRINTF(buf, count, "\t%s:0x%0x\n",
+			GET_FIELD(record.active_cores),
+			record.active_cores);
+	}
+
+	j = find_first_bit((unsigned long *)&record.active_cores,
+							BITS_PER_LONG);
 	while (j < BITS_PER_LONG) {
-		count += snprintf(pdata->buf + count,
-			sizeof(pdata->buf) - count,
-			"\t\tcore%d\n", j);
-		j = find_next_bit(&record.active_cores,
+		SNPRINTF(buf, count, "\t\tcore%d\n", j);
+		j = find_next_bit((unsigned long *)&record.active_cores,
 				BITS_PER_LONG, j + 1);
 	}
 
-
-	nomasters++;
+	master_cnt++;
 	mutex_unlock(&msm_rpm_master_stats_mutex);
-	return count;
+	return RPM_MASTERS_BUF_LEN - count;
 }
 
 static int msm_rpm_master_stats_file_read(struct file *file, char __user *bufu,
@@ -151,7 +254,7 @@
 	pdata = inode->i_private;
 
 	file->private_data =
-		kmalloc(sizeof(struct msm_rpm_master_stats_private_data),
+		kzalloc(sizeof(struct msm_rpm_master_stats_private_data),
 			GFP_KERNEL);
 
 	if (!file->private_data)
@@ -159,7 +262,7 @@
 	prvdata = file->private_data;
 
 	prvdata->reg_base = ioremap(pdata->phys_addr_base,
-		pdata->phys_size);
+						pdata->phys_size);
 	if (!prvdata->reg_base) {
 		kfree(file->private_data);
 		prvdata = NULL;
@@ -170,7 +273,7 @@
 	}
 
 	prvdata->len = 0;
-	prvdata->nomasters = pdata->nomasters;
+	prvdata->num_masters = pdata->num_masters;
 	prvdata->master_names = pdata->masters;
 	prvdata->platform_data = pdata;
 	return 0;
@@ -184,27 +287,108 @@
 	.llseek   = no_llseek,
 };
 
+static struct msm_rpm_master_stats_platform_data
+			*msm_rpm_master_populate_pdata(struct device *dev)
+{
+	struct msm_rpm_master_stats_platform_data *pdata;
+	struct device_node *node = dev->of_node;
+	int rc = 0, i;
+
+	pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
+	if (!pdata) {
+		dev_err(dev, "could not allocate memory for platform data\n");
+		goto err;
+	}
+
+	rc = of_property_read_u32(node, "qcom,master-stats-version",
+							&pdata->version);
+	if (rc) {
+		dev_err(dev, "master-stats-version missing rc=%d\n", rc);
+		goto err;
+	}
+
+	rc = of_property_read_u32(node, "qcom,master-offset",
+							&pdata->master_offset);
+	if (rc) {
+		dev_err(dev, "master-offset missing rc=%d\n", rc);
+		goto err;
+	}
+
+	pdata->num_masters = of_property_count_strings(node, "qcom,masters");
+	if (pdata->num_masters < 0) {
+		dev_err(dev, "Failed to get number of masters =%d\n",
+						pdata->num_masters);
+		goto err;
+	}
+
+	pdata->masters = devm_kzalloc(dev, sizeof(char *) * pdata->num_masters,
+								GFP_KERNEL);
+	if (!pdata->masters) {
+		dev_err(dev, "%s:Failed to allocated memory\n", __func__);
+		goto err;
+	}
+
+	/*
+	 * Read master names from DT
+	 */
+	for (i = 0; i < pdata->num_masters; i++) {
+		const char *master_name;
+		of_property_read_string_index(node, "qcom,masters",
+							i, &master_name);
+		pdata->masters[i] = devm_kzalloc(dev, sizeof(char) *
+				strlen(master_name) + 1, GFP_KERNEL);
+		if (!pdata->masters[i]) {
+			dev_err(dev, "%s:Failed to get memory\n", __func__);
+			goto err;
+		}
+		strlcpy(pdata->masters[i], master_name,
+					strlen(master_name) + 1);
+	}
+	return pdata;
+err:
+	return NULL;
+}
+
 static  int __devinit msm_rpm_master_stats_probe(struct platform_device *pdev)
 {
 	struct dentry *dent;
 	struct msm_rpm_master_stats_platform_data *pdata;
-	struct resource *res;
+	struct resource *res = NULL;
 
-	pdata = pdev->dev.platform_data;
-	if (!pdata)
+	if (!pdev)
 		return -EINVAL;
 
+	if (pdev->dev.of_node)
+		pdata = msm_rpm_master_populate_pdata(&pdev->dev);
+	else
+		pdata = pdev->dev.platform_data;
+
+	if (!pdata) {
+		dev_err(&pdev->dev, "%s: Unable to get pdata\n", __func__);
+		return -ENOMEM;
+	}
+
 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+	if (!res) {
+		dev_err(&pdev->dev,
+			"%s: Failed to get IO resource from platform device",
+			__func__);
+		return -ENXIO;
+	}
+
 	pdata->phys_addr_base = res->start;
 	pdata->phys_size = resource_size(res);
 
 	dent = debugfs_create_file("rpm_master_stats", S_IRUGO, NULL,
-			pdev->dev.platform_data, &msm_rpm_master_stats_fops);
+					pdata, &msm_rpm_master_stats_fops);
 
 	if (!dent) {
-		pr_err("%s: ERROR debugfs_create_file failed\n", __func__);
+		dev_err(&pdev->dev, "%s: ERROR debugfs_create_file failed\n",
+								__func__);
 		return -ENOMEM;
 	}
+
 	platform_set_drvdata(pdev, dent);
 	return 0;
 }
@@ -219,12 +403,18 @@
 	return 0;
 }
 
+static struct of_device_id rpm_master_table[] = {
+	{.compatible = "qcom,rpm-master-stats"},
+	{},
+};
+
 static struct platform_driver msm_rpm_master_stats_driver = {
 	.probe	= msm_rpm_master_stats_probe,
 	.remove = __devexit_p(msm_rpm_master_stats_remove),
 	.driver = {
-		.name = "msm_rpm_master_stat",
+		.name = "msm_rpm_master_stats",
 		.owner = THIS_MODULE,
+		.of_match_table = rpm_master_table,
 	},
 };
 
diff --git a/arch/arm/mach-msm/rpm_stats.h b/arch/arm/mach-msm/rpm_stats.h
index c1dfe34..34c1b99 100644
--- a/arch/arm/mach-msm/rpm_stats.h
+++ b/arch/arm/mach-msm/rpm_stats.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2011-2013, 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
@@ -31,9 +31,11 @@
 	 * it allocates 256 bytes for this use.
 	 * No of masters differs for different targets.
 	 * Based on the number of masters, linux rpm stat
-	 * driver reads (32 * nomasters) bytes to display
+	 * driver reads (32 * num_masters) bytes to display
 	 * master stats.
 	 */
-	 u32 nomasters;
+	 s32 num_masters;
+	 u32 master_offset;
+	 u32 version;
 };
 #endif
diff --git a/arch/arm/mach-msm/scm.c b/arch/arm/mach-msm/scm.c
index e9f44e3..266b759 100644
--- a/arch/arm/mach-msm/scm.c
+++ b/arch/arm/mach-msm/scm.c
@@ -31,6 +31,9 @@
 
 static DEFINE_MUTEX(scm_lock);
 
+#define SCM_BUF_LEN(__cmd_size, __resp_size)	\
+	(sizeof(struct scm_command) + sizeof(struct scm_response) + \
+		__cmd_size + __resp_size)
 /**
  * struct scm_command - one SCM command buffer
  * @len: total available memory for command and response
@@ -76,42 +79,6 @@
 };
 
 /**
- * alloc_scm_command() - Allocate an SCM command
- * @cmd_size: size of the command buffer
- * @resp_size: size of the response buffer
- *
- * Allocate an SCM command, including enough room for the command
- * and response headers as well as the command and response buffers.
- *
- * Returns a valid &scm_command on success or %NULL if the allocation fails.
- */
-static struct scm_command *alloc_scm_command(size_t cmd_size, size_t resp_size)
-{
-	struct scm_command *cmd;
-	size_t len = sizeof(*cmd) + sizeof(struct scm_response) + cmd_size +
-		resp_size;
-
-	cmd = kzalloc(PAGE_ALIGN(len), GFP_KERNEL);
-	if (cmd) {
-		cmd->len = len;
-		cmd->buf_offset = offsetof(struct scm_command, buf);
-		cmd->resp_hdr_offset = cmd->buf_offset + cmd_size;
-	}
-	return cmd;
-}
-
-/**
- * free_scm_command() - Free an SCM command
- * @cmd: command to free
- *
- * Free an SCM command.
- */
-static inline void free_scm_command(struct scm_command *cmd)
-{
-	kfree(cmd);
-}
-
-/**
  * scm_command_to_response() - Get a pointer to a scm_response
  * @cmd: command
  *
@@ -224,6 +191,92 @@
 }
 
 /**
+ * scm_call_common() - Send an SCM command
+ * @svc_id: service identifier
+ * @cmd_id: command identifier
+ * @cmd_buf: command buffer
+ * @cmd_len: length of the command buffer
+ * @resp_buf: response buffer
+ * @resp_len: length of the response buffer
+ * @scm_buf: internal scm structure used for passing data
+ * @scm_buf_len: length of the internal scm structure
+ *
+ * Core function to scm call. Initializes the given cmd structure with
+ * appropriate values and makes the actual scm call. Validation of cmd
+ * pointer and length must occur in the calling function.
+ *
+ * Returns the appropriate error code from the scm call
+ */
+
+static int scm_call_common(u32 svc_id, u32 cmd_id, const void *cmd_buf,
+				size_t cmd_len, void *resp_buf, size_t resp_len,
+				struct scm_command *scm_buf,
+				size_t scm_buf_length)
+{
+	int ret;
+	struct scm_response *rsp;
+	unsigned long start, end;
+
+	scm_buf->len = scm_buf_length;
+	scm_buf->buf_offset = offsetof(struct scm_command, buf);
+	scm_buf->resp_hdr_offset = scm_buf->buf_offset + cmd_len;
+	scm_buf->id = (svc_id << 10) | cmd_id;
+
+	if (cmd_buf)
+		memcpy(scm_get_command_buffer(scm_buf), cmd_buf, cmd_len);
+
+	mutex_lock(&scm_lock);
+	ret = __scm_call(scm_buf);
+	mutex_unlock(&scm_lock);
+	if (ret)
+		return ret;
+
+	rsp = scm_command_to_response(scm_buf);
+	start = (unsigned long)rsp;
+
+	do {
+		scm_inv_range(start, start + sizeof(*rsp));
+	} while (!rsp->is_complete);
+
+	end = (unsigned long)scm_get_response_buffer(rsp) + resp_len;
+	scm_inv_range(start, end);
+
+	if (resp_buf)
+		memcpy(resp_buf, scm_get_response_buffer(rsp), resp_len);
+
+	return ret;
+}
+
+/**
+ * scm_call_noalloc - Send an SCM command
+ *
+ * Same as scm_call except clients pass in a buffer (@scm_buf) to be used for
+ * scm internal structures. The buffer should be allocated with
+ * DEFINE_SCM_BUFFER to account for the proper alignment and size.
+ */
+int scm_call_noalloc(u32 svc_id, u32 cmd_id, const void *cmd_buf,
+		size_t cmd_len, void *resp_buf, size_t resp_len,
+		void *scm_buf, size_t scm_buf_len)
+{
+	int ret;
+	size_t len = SCM_BUF_LEN(cmd_len, resp_len);
+
+	if (cmd_len > scm_buf_len || resp_len > scm_buf_len ||
+	    len > scm_buf_len)
+		return -EINVAL;
+
+	if (!IS_ALIGNED((unsigned long)scm_buf, PAGE_SIZE))
+		return -EINVAL;
+
+	memset(scm_buf, 0, scm_buf_len);
+
+	ret = scm_call_common(svc_id, cmd_id, cmd_buf, cmd_len, resp_buf,
+				resp_len, scm_buf, len);
+	return ret;
+
+}
+
+/**
  * scm_call() - Send an SCM command
  * @svc_id: service identifier
  * @cmd_id: command identifier
@@ -244,39 +297,20 @@
 int scm_call(u32 svc_id, u32 cmd_id, const void *cmd_buf, size_t cmd_len,
 		void *resp_buf, size_t resp_len)
 {
-	int ret;
 	struct scm_command *cmd;
-	struct scm_response *rsp;
-	unsigned long start, end;
+	int ret;
+	size_t len = SCM_BUF_LEN(cmd_len, resp_len);
 
-	cmd = alloc_scm_command(cmd_len, resp_len);
+	if (cmd_len > len || resp_len > len)
+		return -EINVAL;
+
+	cmd = kzalloc(PAGE_ALIGN(len), GFP_KERNEL);
 	if (!cmd)
 		return -ENOMEM;
 
-	cmd->id = (svc_id << 10) | cmd_id;
-	if (cmd_buf)
-		memcpy(scm_get_command_buffer(cmd), cmd_buf, cmd_len);
-
-	mutex_lock(&scm_lock);
-	ret = __scm_call(cmd);
-	mutex_unlock(&scm_lock);
-	if (ret)
-		goto out;
-
-	rsp = scm_command_to_response(cmd);
-	start = (unsigned long)rsp;
-
-	do {
-		scm_inv_range(start, start + sizeof(*rsp));
-	} while (!rsp->is_complete);
-
-	end = (unsigned long)scm_get_response_buffer(rsp) + resp_len;
-	scm_inv_range(start, end);
-
-	if (resp_buf)
-		memcpy(resp_buf, scm_get_response_buffer(rsp), resp_len);
-out:
-	free_scm_command(cmd);
+	ret = scm_call_common(svc_id, cmd_id, cmd_buf, cmd_len, resp_buf,
+				resp_len, cmd, len);
+	kfree(cmd);
 	return ret;
 }
 EXPORT_SYMBOL(scm_call);
diff --git a/arch/arm/mach-msm/socinfo.c b/arch/arm/mach-msm/socinfo.c
index 9479492..2f615f7 100644
--- a/arch/arm/mach-msm/socinfo.c
+++ b/arch/arm/mach-msm/socinfo.c
@@ -85,6 +85,7 @@
 	[PLATFORM_SUBTYPE_SKUF] = "SKUF",
 	[PLATFORM_SUBTYPE_SKUAB] = "SKUAB",
 	[PLATFORM_SUBTYPE_SKUG] = "SKUG",
+	[PLATFORM_SUBTYPE_QRD_INVALID] = "INVALID",
 };
 
 enum {
@@ -668,7 +669,7 @@
 		if (hw_subtype >= PLATFORM_SUBTYPE_QRD_INVALID) {
 			pr_err("%s: Invalid hardware platform sub type for qrd found\n",
 				__func__);
-			hw_subtype = PLATFORM_SUBTYPE_QRD;
+			hw_subtype = PLATFORM_SUBTYPE_QRD_INVALID;
 		}
 		return snprintf(buf, PAGE_SIZE, "%-.32s\n",
 					qrd_hw_platform_subtype[hw_subtype]);
@@ -790,6 +791,16 @@
 {
 	uint32_t hw_subtype;
 	hw_subtype = socinfo_get_platform_subtype();
+	if (HW_PLATFORM_QRD == socinfo_get_platform_type()) {
+		if (hw_subtype >= PLATFORM_SUBTYPE_QRD_INVALID) {
+			pr_err("%s: Invalid hardware platform sub type for qrd found\n",
+				__func__);
+			hw_subtype = PLATFORM_SUBTYPE_QRD_INVALID;
+		}
+		return snprintf(buf, PAGE_SIZE, "%-.32s\n",
+					qrd_hw_platform_subtype[hw_subtype]);
+	}
+
 	return snprintf(buf, PAGE_SIZE, "%-.32s\n",
 		hw_platform_subtype[hw_subtype]);
 }
diff --git a/arch/arm/mach-msm/spm-regulator.c b/arch/arm/mach-msm/spm-regulator.c
index 244a779..08ac56a 100644
--- a/arch/arm/mach-msm/spm-regulator.c
+++ b/arch/arm/mach-msm/spm-regulator.c
@@ -120,7 +120,7 @@
 			return rc;
 	}
 
-	rc = msm_spm_apcs_set_vdd(vreg->vlevel);
+	rc = msm_spm_set_vdd(0, vreg->vlevel); /* value of CPU is don't care */
 	if (rc) {
 		pr_err("%s: msm_spm_set_vdd failed %d\n", vreg->rdesc.name, rc);
 		return rc;
diff --git a/arch/arm/mach-msm/spm.h b/arch/arm/mach-msm/spm.h
index 77b7bf7..3207011 100644
--- a/arch/arm/mach-msm/spm.h
+++ b/arch/arm/mach-msm/spm.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2010-2013, 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
@@ -150,7 +150,6 @@
 /* Public functions */
 
 int msm_spm_l2_set_low_power_mode(unsigned int mode, bool notify_rpm);
-int msm_spm_apcs_set_vdd(unsigned int vlevel);
 int msm_spm_apcs_set_phase(unsigned int phase_cnt);
 int msm_spm_enable_fts_lpm(uint32_t mode);
 
@@ -166,20 +165,17 @@
 {
 	return -ENOSYS;
 }
+
 static inline int msm_spm_l2_init(struct msm_spm_platform_data *data)
 {
 	return -ENOSYS;
 }
+
 static inline void msm_spm_l2_reinit(void)
 {
 	/* empty */
 }
 
-static inline int msm_spm_apcs_set_vdd(unsigned int vlevel)
-{
-	return -ENOSYS;
-}
-
 static inline int msm_spm_apcs_set_phase(unsigned int phase_cnt)
 {
 	return -ENOSYS;
diff --git a/arch/arm/mach-msm/spm_devices.c b/arch/arm/mach-msm/spm_devices.c
index fc05fce..3ac1348 100644
--- a/arch/arm/mach-msm/spm_devices.c
+++ b/arch/arm/mach-msm/spm_devices.c
@@ -48,18 +48,29 @@
 
 static struct msm_spm_device msm_spm_l2_device;
 static DEFINE_PER_CPU_SHARED_ALIGNED(struct msm_spm_device, msm_cpu_spm_device);
-
+static bool msm_spm_L2_apcs_master;
 
 static void msm_spm_smp_set_vdd(void *data)
 {
 	struct msm_spm_device *dev;
 	struct msm_spm_vdd_info *info = (struct msm_spm_vdd_info *)data;
 
-	dev = &per_cpu(msm_cpu_spm_device, info->cpu);
+	if (msm_spm_L2_apcs_master)
+		dev = &msm_spm_l2_device;
+	else
+		dev = &per_cpu(msm_cpu_spm_device, info->cpu);
+
 	if (!dev->initialized)
 		return;
+
+	if (msm_spm_L2_apcs_master)
+		get_cpu();
+
 	dev->cpu_vdd = info->vlevel;
 	info->err = msm_spm_drv_set_vdd(&dev->reg_data, info->vlevel);
+
+	if (msm_spm_L2_apcs_master)
+		put_cpu();
 }
 
 /**
@@ -76,7 +87,8 @@
 	info.vlevel = vlevel;
 	info.err = -ENODEV;
 
-	if ((smp_processor_id() != cpu) && cpu_online(cpu)) {
+	if (!msm_spm_L2_apcs_master && (smp_processor_id() != cpu) &&
+			cpu_online(cpu)) {
 		/**
 		 * We do not want to set the voltage of another core from
 		 * this core, as its possible that we may race the vdd change
@@ -111,7 +123,10 @@
 {
 	struct msm_spm_device *dev;
 
-	dev = &per_cpu(msm_cpu_spm_device, cpu);
+	if (msm_spm_L2_apcs_master)
+		dev = &msm_spm_l2_device;
+	else
+		dev = &per_cpu(msm_cpu_spm_device, cpu);
 	return dev->cpu_vdd;
 }
 EXPORT_SYMBOL(msm_spm_get_vdd);
@@ -293,18 +308,6 @@
 EXPORT_SYMBOL(msm_spm_l2_reinit);
 
 /**
- * msm_spm_apcs_set_vdd(): Set Apps processor core sub-system voltage
- * @vlevel: Encoded PMIC data.
- */
-int msm_spm_apcs_set_vdd(unsigned int vlevel)
-{
-	if (!msm_spm_l2_device.initialized)
-		return -ENXIO;
-	return msm_spm_drv_set_vdd(&msm_spm_l2_device.reg_data, vlevel);
-}
-EXPORT_SYMBOL(msm_spm_apcs_set_vdd);
-
-/**
  * msm_spm_apcs_set_phase(): Set number of SMPS phases.
  * phase_cnt: Number of phases to be set active
  */
@@ -468,6 +471,10 @@
 		ret = of_property_read_u32(node, key, &val);
 		if (!ret)
 			spm_data.pfm_port = val;
+
+		key = "qcom,L2-spm-is-apcs-master";
+		msm_spm_L2_apcs_master =
+			of_property_read_bool(pdev->dev.of_node, key);
 	}
 
 	for (i = 0; i < ARRAY_SIZE(spm_of_data); i++) {
diff --git a/drivers/char/diag/diag_dci.c b/drivers/char/diag/diag_dci.c
index 1deee5c..97dc26d 100644
--- a/drivers/char/diag/diag_dci.c
+++ b/drivers/char/diag/diag_dci.c
@@ -209,9 +209,16 @@
 		event_id_packet = *(uint16_t *)(buf + temp_len);
 		event_id = event_id_packet & 0x0FFF; /* extract 12 bits */
 		if (event_id_packet & 0x8000) {
+			/* The packet has the two smallest byte of the
+			 * timestamp
+			 */
 			timestamp_len = 2;
-			memset(timestamp, 0, 8);
 		} else {
+			/* The packet has the full timestamp. The first event
+			 * will always have full timestamp. Save it in the
+			 * timestamp buffer and use it for subsequent events if
+			 * necessary.
+			 */
 			timestamp_len = 8;
 			memcpy(timestamp, buf + temp_len + 2, timestamp_len);
 		}
diff --git a/drivers/char/diag/diag_masks.c b/drivers/char/diag/diag_masks.c
index 755f0a1..9c27180 100644
--- a/drivers/char/diag/diag_masks.c
+++ b/drivers/char/diag/diag_masks.c
@@ -28,7 +28,6 @@
 
 #define ALL_EQUIP_ID		100
 #define ALL_SSID		-1
-#define MAX_SSID_PER_RANGE	100
 
 #define FEATURE_MASK_LEN_BYTES		2
 
@@ -135,6 +134,7 @@
 	uint8_t *ptr = driver->msg_masks;
 	uint8_t *ptr_buffer_start = &(*(driver->msg_masks));
 	uint8_t *ptr_buffer_end = &(*(driver->msg_masks)) + MSG_MASK_SIZE;
+	uint32_t copy_len = (end - start + 1) * sizeof(int);
 
 	mutex_lock(&driver->diagchar_mutex);
 	/* First SSID can be zero : So check that last is non-zero */
@@ -153,11 +153,19 @@
 				actual_last = end;
 				*(uint32_t *)(actual_last_ptr) = end;
 			}
+			if (actual_last-first >= MAX_SSID_PER_RANGE) {
+				pr_err("diag: In %s, truncating ssid range, %d-%d to max allowed: %d",
+						__func__, first, actual_last,
+						MAX_SSID_PER_RANGE);
+				copy_len = MAX_SSID_PER_RANGE;
+				actual_last = first + MAX_SSID_PER_RANGE;
+				*(uint32_t *)actual_last_ptr = actual_last;
+			}
 			if (CHK_OVERFLOW(ptr_buffer_start, ptr, ptr_buffer_end,
-					  (((end - start)+1)*4))) {
+								copy_len)) {
 				pr_debug("diag: update ssid start %d, end %d\n",
 								 start, end);
-				memcpy(ptr, buf , ((end - start)+1)*4);
+				memcpy(ptr, buf, copy_len);
 			} else
 				pr_alert("diag: Not enough space MSG_MASK\n");
 			found = 1;
@@ -311,11 +319,11 @@
 		return;
 	}
 
+	diag_send_feature_mask_update(smd_info);
 	diag_send_msg_mask_update(smd_info->ch, ALL_SSID, ALL_SSID,
 						smd_info->peripheral);
 	diag_send_log_mask_update(smd_info->ch, ALL_EQUIP_ID);
 	diag_send_event_mask_update(smd_info->ch, diag_event_num_bytes);
-	diag_send_feature_mask_update(smd_info);
 
 	if (smd_info->notify_context == SMD_EVENT_OPEN)
 		diag_send_diag_mode_update_by_smd(smd_info,
@@ -493,6 +501,16 @@
 		case DIAG_CTRL_MASK_VALID:
 			driver->msg_mask->msg_mask_size = actual_last -
 								first + 1;
+			/* Limit the msg_mask_size to MAX_SSID_PER_RANGE */
+			if (driver->msg_mask->msg_mask_size >
+							MAX_SSID_PER_RANGE) {
+				pr_err("diag: in %s, Invalid msg mask size %d, max: %d",
+					__func__,
+				       driver->msg_mask->msg_mask_size,
+				       MAX_SSID_PER_RANGE);
+				driver->msg_mask->msg_mask_size =
+							MAX_SSID_PER_RANGE;
+			}
 			memcpy(buf+header_size, ptr,
 				 4 * (driver->msg_mask->msg_mask_size));
 			break;
diff --git a/drivers/char/diag/diagchar.h b/drivers/char/diag/diagchar.h
index 45314d9..e5acb6c 100644
--- a/drivers/char/diag/diagchar.h
+++ b/drivers/char/diag/diagchar.h
@@ -64,6 +64,8 @@
 #define NUM_MEMORY_POOLS	4
 #endif
 
+#define MAX_SSID_PER_RANGE	200
+
 #define MODEM_DATA		0
 #define LPASS_DATA		1
 #define WCNSS_DATA		2
@@ -73,7 +75,13 @@
 #define HSIC_2_DATA		6
 #define SMUX_DATA		10
 #define APPS_PROC		1
-#define MSG_MASK_SIZE 10000
+/*
+ * Each row contains First (uint32_t), Last (uint32_t), Actual
+ * last (uint32_t) values along with the range of SSIDs
+ * (MAX_SSID_PER_RANGE*uint32_t).
+ * And there are MSG_MASK_TBL_CNT rows.
+ */
+#define MSG_MASK_SIZE		((MAX_SSID_PER_RANGE+3) * 4 * MSG_MASK_TBL_CNT)
 #define LOG_MASK_SIZE 8000
 #define EVENT_MASK_SIZE 1000
 #define USER_SPACE_DATA 8192
diff --git a/drivers/char/diag/diagchar_core.c b/drivers/char/diag/diagchar_core.c
index 6cc18da..19ff6f4 100644
--- a/drivers/char/diag/diagchar_core.c
+++ b/drivers/char/diag/diagchar_core.c
@@ -1274,17 +1274,6 @@
 						data->write_ptr_1->length);
 					data->in_busy_1 = 0;
 				}
-				if (data->in_busy_2 == 1) {
-					num_data++;
-					/*Copy the length of data being passed*/
-					COPY_USER_SPACE_OR_EXIT(buf+ret,
-						(data->write_ptr_2->length), 4);
-					/*Copy the actual data being passed*/
-					COPY_USER_SPACE_OR_EXIT(buf+ret,
-						*(data->buf_in_2),
-						data->write_ptr_2->length);
-					data->in_busy_2 = 0;
-				}
 			}
 		}
 #ifdef CONFIG_DIAG_SDIO_PIPE
diff --git a/drivers/char/diag/diagchar_hdlc.c b/drivers/char/diag/diagchar_hdlc.c
index 2369c4d..7b24591 100644
--- a/drivers/char/diag/diagchar_hdlc.c
+++ b/drivers/char/diag/diagchar_hdlc.c
@@ -18,6 +18,7 @@
 #include <linux/fs.h>
 #include <linux/device.h>
 #include <linux/uaccess.h>
+#include <linux/ratelimit.h>
 #include <linux/crc-ccitt.h>
 #include "diagchar_hdlc.h"
 #include "diagchar.h"
@@ -234,3 +235,37 @@
 
 	return pkt_bnd;
 }
+
+int crc_check(uint8_t *buf, uint16_t len)
+{
+	uint16_t crc = CRC_16_L_SEED;
+	uint8_t sent_crc[2] = {0, 0};
+
+	/*
+	 * The minimum length of a valid incoming packet is 4. 1 byte
+	 * of data and 3 bytes for CRC
+	 */
+	if (!buf || len < 4) {
+		pr_err_ratelimited("diag: In %s, invalid packet or length, buf: 0x%x, len: %d",
+				   __func__, (int)buf, len);
+		return -EIO;
+	}
+
+	/*
+	 * Run CRC check for the original input. Skip the last 3 CRC
+	 * bytes
+	 */
+	crc = crc_ccitt(crc, buf, len-3);
+	crc ^= CRC_16_L_SEED;
+
+	/* Check the computed CRC against the original CRC bytes. */
+	sent_crc[0] = buf[len-3];
+	sent_crc[1] = buf[len-2];
+	if (crc != *((uint16_t *)sent_crc)) {
+		pr_debug("diag: In %s, crc mismatch. expected: %x, sent %x.\n",
+				__func__, crc, *((uint16_t *)sent_crc));
+		return -EIO;
+	}
+
+	return 0;
+}
diff --git a/drivers/char/diag/diagchar_hdlc.h b/drivers/char/diag/diagchar_hdlc.h
index e3378ac..2ba46f5 100644
--- a/drivers/char/diag/diagchar_hdlc.h
+++ b/drivers/char/diag/diagchar_hdlc.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008-2009, 2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2008-2009, 2012-2013, 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
@@ -53,6 +53,8 @@
 
 int diag_hdlc_decode(struct diag_hdlc_decode_type *hdlc);
 
+int crc_check(uint8_t *buf, uint16_t len);
+
 #define ESC_CHAR     0x7D
 #define ESC_MASK     0x20
 
diff --git a/drivers/char/diag/diagfwd.c b/drivers/char/diag/diagfwd.c
index a1f6b2c..1e8b0ba 100644
--- a/drivers/char/diag/diagfwd.c
+++ b/drivers/char/diag/diagfwd.c
@@ -1514,7 +1514,7 @@
 void diag_process_hdlc(void *data, unsigned len)
 {
 	struct diag_hdlc_decode_type hdlc;
-	int ret, type = 0;
+	int ret, type = 0, crc_chk = 0;
 
 	mutex_lock(&driver->diag_hdlc_mutex);
 
@@ -1528,6 +1528,16 @@
 	hdlc.escaping = 0;
 
 	ret = diag_hdlc_decode(&hdlc);
+	if (ret) {
+		crc_chk = crc_check(hdlc.dest_ptr, hdlc.dest_idx);
+		if (crc_chk) {
+			/* CRC check failed. */
+			pr_err_ratelimited("diag: In %s, bad CRC. Dropping packet\n",
+								__func__);
+			mutex_unlock(&driver->diag_hdlc_mutex);
+			return;
+		}
+	}
 
 	/*
 	 * If the message is 3 bytes or less in length then the message is
@@ -1550,9 +1560,8 @@
 			return;
 		}
 	} else if (driver->debug_flag) {
-		printk(KERN_ERR "Packet dropped due to bad HDLC coding/CRC"
-				" errors or partial packet received, packet"
-				" length = %d\n", len);
+		pr_err("diag: In %s, partial packet received, dropping packet, len: %d\n",
+								__func__, len);
 		print_hex_dump(KERN_DEBUG, "Dropped Packet Data: ", 16, 1,
 					   DUMP_PREFIX_ADDRESS, data, len, 1);
 		driver->debug_flag = 0;
diff --git a/drivers/coresight/coresight-tmc.c b/drivers/coresight/coresight-tmc.c
index d500c0a..3225817 100644
--- a/drivers/coresight/coresight-tmc.c
+++ b/drivers/coresight/coresight-tmc.c
@@ -45,6 +45,8 @@
 #define tmc_writel(drvdata, val, off)	__raw_writel((val), drvdata->base + off)
 #define tmc_readl(drvdata, off)		__raw_readl(drvdata->base + off)
 
+#define tmc_readl_no_log(drvdata, off)	__raw_readl_no_log(drvdata->base + off)
+
 #define TMC_LOCK(drvdata)						\
 do {									\
 	mb();								\
@@ -651,7 +653,7 @@
 	bufp = drvdata->buf;
 	while (1) {
 		for (i = 0; i < memwords; i++) {
-			read_data = tmc_readl(drvdata, TMC_RRD);
+			read_data = tmc_readl_no_log(drvdata, TMC_RRD);
 			if (read_data == 0xFFFFFFFF)
 				goto out;
 			memcpy(bufp, &read_data, BYTES_PER_WORD);
diff --git a/drivers/cpufreq/cpufreq_ondemand.c b/drivers/cpufreq/cpufreq_ondemand.c
index 1f60c65..e6b2a3c 100644
--- a/drivers/cpufreq/cpufreq_ondemand.c
+++ b/drivers/cpufreq/cpufreq_ondemand.c
@@ -586,8 +586,8 @@
 
 	dbs_tuners_ins.powersave_bias = input;
 
-	mutex_lock(&dbs_mutex);
 	get_online_cpus();
+	mutex_lock(&dbs_mutex);
 
 	if (!bypass) {
 		if (reenable_timer) {
@@ -663,8 +663,8 @@
 		}
 	}
 
-	put_online_cpus();
 	mutex_unlock(&dbs_mutex);
+	put_online_cpus();
 
 	return count;
 }
diff --git a/drivers/gpio/gpio-msm-common.c b/drivers/gpio/gpio-msm-common.c
index 3115628..3d18809 100644
--- a/drivers/gpio/gpio-msm-common.c
+++ b/drivers/gpio/gpio-msm-common.c
@@ -84,6 +84,7 @@
 	{SDC1_HDRV_PULL_CTL, 13}, /* TLMM_PULL_SDC1_CLK  */
 	{SDC1_HDRV_PULL_CTL, 11}, /* TLMM_PULL_SDC1_CMD  */
 	{SDC1_HDRV_PULL_CTL, 9},  /* TLMM_PULL_SDC1_DATA */
+	{SDC1_HDRV_PULL_CTL, 15}, /* TLMM_PULL_SDC1_RCLK  */
 };
 
 /*
diff --git a/drivers/gpu/ion/ion_iommu_heap.c b/drivers/gpu/ion/ion_iommu_heap.c
index 1ea3cd2..d9e9e09 100644
--- a/drivers/gpu/ion/ion_iommu_heap.c
+++ b/drivers/gpu/ion/ion_iommu_heap.c
@@ -136,12 +136,18 @@
 	return NULL;
 }
 
-static int ion_iommu_buffer_zero(struct ion_iommu_priv_data *data)
+static int ion_iommu_buffer_zero(struct ion_iommu_priv_data *data,
+				bool is_cached)
 {
-	int i, j;
+	int i, j, k;
 	unsigned int npages_to_vmap;
 	unsigned int total_pages;
 	void *ptr = NULL;
+	/*
+	 * It's cheaper just to use writecombine memory and skip the
+	 * cache vs. using a cache memory and trying to flush it afterwards
+	 */
+	pgprot_t pgprot = pgprot_writecombine(pgprot_kernel);
 
 	/*
 	 * As an optimization, we manually zero out all of the
@@ -161,7 +167,7 @@
 		for (j = 0; j < MAX_VMAP_RETRIES && npages_to_vmap;
 			++j) {
 			ptr = vmap(&data->pages[i], npages_to_vmap,
-					VM_IOREMAP, pgprot_kernel);
+					VM_IOREMAP, pgprot);
 			if (ptr)
 				break;
 			else
@@ -171,6 +177,20 @@
 			return -ENOMEM;
 
 		memset(ptr, 0, npages_to_vmap * PAGE_SIZE);
+		if (is_cached) {
+			/*
+			 * invalidate the cache to pick up the zeroing
+			 */
+			for (k = 0; k < npages_to_vmap; k++) {
+				void *p = kmap_atomic(data->pages[i + k]);
+				phys_addr_t phys = page_to_phys(
+							data->pages[i + k]);
+
+				dmac_inv_range(p, p + PAGE_SIZE);
+				outer_inv_range(phys, phys + PAGE_SIZE);
+				kunmap_atomic(p);
+			}
+		}
 		vunmap(ptr);
 	}
 
@@ -269,7 +289,7 @@
 
 
 		if (flags & ION_FLAG_POOL_FORCE_ALLOC) {
-			ret = ion_iommu_buffer_zero(data);
+			ret = ion_iommu_buffer_zero(data, ION_IS_CACHED(flags));
 			if (ret) {
 				pr_err("Couldn't vmap the pages for zeroing\n");
 				goto err3;
@@ -328,7 +348,7 @@
 		return;
 
 	if (!(buffer->flags & ION_FLAG_POOL_FORCE_ALLOC))
-		ion_iommu_buffer_zero(data);
+		ion_iommu_buffer_zero(data, ION_IS_CACHED(buffer->flags));
 
 	for_each_sg(table->sgl, sg, table->nents, i) {
 		int order = get_order(sg_dma_len(sg));
diff --git a/drivers/gpu/msm/a3xx_reg.h b/drivers/gpu/msm/a3xx_reg.h
index 0c398c4..b5945da 100644
--- a/drivers/gpu/msm/a3xx_reg.h
+++ b/drivers/gpu/msm/a3xx_reg.h
@@ -240,6 +240,7 @@
 #define A3XX_PC_PERFCOUNTER1_SELECT 0xC49
 #define A3XX_PC_PERFCOUNTER2_SELECT 0xC4A
 #define A3XX_PC_PERFCOUNTER3_SELECT 0xC4B
+#define A3XX_GRAS_TSE_DEBUG_ECO 0xC81
 #define A3XX_GRAS_PERFCOUNTER0_SELECT 0xC88
 #define A3XX_GRAS_PERFCOUNTER1_SELECT 0xC89
 #define A3XX_GRAS_PERFCOUNTER2_SELECT 0xC8A
@@ -269,8 +270,10 @@
 #define A3XX_GRAS_CL_USER_PLANE_Z5 0xCB6
 #define A3XX_GRAS_CL_USER_PLANE_W5 0xCB7
 #define A3XX_RB_GMEM_BASE_ADDR 0xCC0
+#define A3XX_RB_DEBUG_ECO_CONTROLS_ADDR 0xCC1
 #define A3XX_RB_PERFCOUNTER0_SELECT   0xCC6
 #define A3XX_RB_PERFCOUNTER1_SELECT   0xCC7
+#define A3XX_RB_FRAME_BUFFER_DIMENSION 0xCE0
 #define A3XX_HLSQ_PERFCOUNTER0_SELECT 0xE00
 #define A3XX_HLSQ_PERFCOUNTER1_SELECT 0xE01
 #define A3XX_HLSQ_PERFCOUNTER2_SELECT 0xE02
@@ -308,6 +311,9 @@
 #define A3XX_GRAS_CL_CLIP_CNTL 0x2040
 #define A3XX_GRAS_CL_GB_CLIP_ADJ 0x2044
 #define A3XX_GRAS_CL_VPORT_XOFFSET 0x2048
+#define A3XX_GRAS_CL_VPORT_XSCALE 0x2049
+#define A3XX_GRAS_CL_VPORT_YOFFSET 0x204A
+#define A3XX_GRAS_CL_VPORT_YSCALE 0x204B
 #define A3XX_GRAS_CL_VPORT_ZOFFSET 0x204C
 #define A3XX_GRAS_CL_VPORT_ZSCALE 0x204D
 #define A3XX_GRAS_SU_POINT_MINMAX 0x2068
@@ -323,30 +329,75 @@
 #define A3XX_RB_MODE_CONTROL 0x20C0
 #define A3XX_RB_RENDER_CONTROL 0x20C1
 #define A3XX_RB_MSAA_CONTROL 0x20C2
+#define A3XX_RB_ALPHA_REFERENCE 0x20C3
 #define A3XX_RB_MRT_CONTROL0 0x20C4
 #define A3XX_RB_MRT_BUF_INFO0 0x20C5
+#define A3XX_RB_MRT_BUF_BASE0 0x20C6
 #define A3XX_RB_MRT_BLEND_CONTROL0 0x20C7
+#define A3XX_RB_MRT_CONTROL1 0x20C8
+#define A3XX_RB_MRT_BUF_INFO1 0x20C9
+#define A3XX_RB_MRT_BUF_BASE1 0x20CA
 #define A3XX_RB_MRT_BLEND_CONTROL1 0x20CB
+#define A3XX_RB_MRT_CONTROL2 0x20CC
+#define A3XX_RB_MRT_BUF_INFO2 0x20CD
+#define A3XX_RB_MRT_BUF_BASE2 0x20CE
 #define A3XX_RB_MRT_BLEND_CONTROL2 0x20CF
+#define A3XX_RB_MRT_CONTROL3 0x20D0
+#define A3XX_RB_MRT_BUF_INFO3 0x20D1
+#define A3XX_RB_MRT_BUF_BASE3 0x20D2
 #define A3XX_RB_MRT_BLEND_CONTROL3 0x20D3
 #define A3XX_RB_BLEND_RED 0x20E4
+#define A3XX_RB_BLEND_GREEN 0x20E5
+#define A3XX_RB_BLEND_BLUE 0x20E6
+#define A3XX_RB_BLEND_ALPHA 0x20E7
+#define A3XX_RB_CLEAR_COLOR_DW0 0x20E8
+#define A3XX_RB_CLEAR_COLOR_DW1 0x20E9
+#define A3XX_RB_CLEAR_COLOR_DW2 0x20EA
+#define A3XX_RB_CLEAR_COLOR_DW3 0x20EB
 #define A3XX_RB_COPY_CONTROL 0x20EC
+#define A3XX_RB_COPY_DEST_BASE 0x20ED
+#define A3XX_RB_COPY_DEST_PITCH 0x20EE
 #define A3XX_RB_COPY_DEST_INFO 0x20EF
 #define A3XX_RB_DEPTH_CONTROL 0x2100
+#define A3XX_RB_DEPTH_CLEAR 0x2101
+#define A3XX_RB_DEPTH_BUF_INFO 0x2102
+#define A3XX_RB_DEPTH_BUF_PITCH 0x2103
 #define A3XX_RB_STENCIL_CONTROL 0x2104
+#define A3XX_RB_STENCIL_CLEAR 0x2105
+#define A3XX_RB_STENCIL_BUF_INFO 0x2106
+#define A3XX_RB_STENCIL_BUF_PITCH 0x2107
+#define A3XX_RB_STENCIL_REF_MASK 0x2108
+#define A3XX_RB_STENCIL_REF_MASK_BF 0x2109
+#define A3XX_RB_LRZ_VSC_CONTROL 0x210C
+#define A3XX_RB_WINDOW_OFFSET 0x210E
+#define A3XX_RB_SAMPLE_COUNT_CONTROL 0x2110
+#define A3XX_RB_SAMPLE_COUNT_ADDR 0x2111
+#define A3XX_RB_Z_CLAMP_MIN 0x2114
+#define A3XX_RB_Z_CLAMP_MAX 0x2115
 #define A3XX_PC_VSTREAM_CONTROL 0x21E4
 #define A3XX_PC_VERTEX_REUSE_BLOCK_CNTL 0x21EA
 #define A3XX_PC_PRIM_VTX_CNTL 0x21EC
 #define A3XX_PC_RESTART_INDEX 0x21ED
 #define A3XX_HLSQ_CONTROL_0_REG 0x2200
+#define A3XX_HLSQ_CONTROL_1_REG 0x2201
+#define A3XX_HLSQ_CONTROL_2_REG 0x2202
+#define A3XX_HLSQ_CONTROL_3_REG 0x2203
 #define A3XX_HLSQ_VS_CONTROL_REG 0x2204
+#define A3XX_HLSQ_FS_CONTROL_REG 0x2205
+#define A3XX_HLSQ_CONST_VSPRESV_RANGE_REG 0x2206
 #define A3XX_HLSQ_CONST_FSPRESV_RANGE_REG 0x2207
 #define A3XX_HLSQ_CL_NDRANGE_0_REG 0x220A
+#define A3XX_HLSQ_CL_NDRANGE_1_REG 0x220B
 #define A3XX_HLSQ_CL_NDRANGE_2_REG 0x220C
+#define A3XX_HLSQ_CL_NDRANGE_3_REG 0x220D
+#define A3XX_HLSQ_CL_NDRANGE_4_REG 0x220E
+#define A3XX_HLSQ_CL_NDRANGE_5_REG 0x220F
+#define A3XX_HLSQ_CL_NDRANGE_6_REG 0x2210
 #define A3XX_HLSQ_CL_CONTROL_0_REG 0x2211
 #define A3XX_HLSQ_CL_CONTROL_1_REG 0x2212
 #define A3XX_HLSQ_CL_KERNEL_CONST_REG 0x2214
 #define A3XX_HLSQ_CL_KERNEL_GROUP_X_REG 0x2215
+#define A3XX_HLSQ_CL_KERNEL_GROUP_Y_REG 0x2216
 #define A3XX_HLSQ_CL_KERNEL_GROUP_Z_REG 0x2217
 #define A3XX_HLSQ_CL_WG_OFFSET_REG 0x221A
 #define A3XX_VFD_CONTROL_0 0x2240
@@ -363,10 +414,21 @@
 #define A3XX_SP_VS_CTRL_REG0 0x22C4
 #define A3XX_SP_VS_CTRL_REG1 0x22C5
 #define A3XX_SP_VS_PARAM_REG 0x22C6
+#define A3XX_SP_VS_OUT_REG_0 0x22C7
+#define A3XX_SP_VS_OUT_REG_1 0x22C8
+#define A3XX_SP_VS_OUT_REG_2 0x22C9
+#define A3XX_SP_VS_OUT_REG_3 0x22CA
+#define A3XX_SP_VS_OUT_REG_4 0x22CB
+#define A3XX_SP_VS_OUT_REG_5 0x22CC
+#define A3XX_SP_VS_OUT_REG_6 0x22CD
 #define A3XX_SP_VS_OUT_REG_7 0x22CE
 #define A3XX_SP_VS_VPC_DST_REG_0 0x22D0
+#define A3XX_SP_VS_VPC_DST_REG_1 0x22D1
+#define A3XX_SP_VS_VPC_DST_REG_2 0x22D2
+#define A3XX_SP_VS_VPC_DST_REG_3 0x22D3
 #define A3XX_SP_VS_OBJ_OFFSET_REG 0x22D4
 #define A3XX_SP_VS_OBJ_START_REG 0x22D5
+#define A3XX_SP_VS_PVT_MEM_PARAM_REG 0x22D6
 #define A3XX_SP_VS_PVT_MEM_ADDR_REG 0x22D7
 #define A3XX_SP_VS_PVT_MEM_SIZE_REG 0x22D8
 #define A3XX_SP_VS_LENGTH_REG 0x22DF
@@ -374,13 +436,19 @@
 #define A3XX_SP_FS_CTRL_REG1 0x22E1
 #define A3XX_SP_FS_OBJ_OFFSET_REG 0x22E2
 #define A3XX_SP_FS_OBJ_START_REG 0x22E3
+#define A3XX_SP_FS_PVT_MEM_PARAM_REG 0x22E4
 #define A3XX_SP_FS_PVT_MEM_ADDR_REG 0x22E5
 #define A3XX_SP_FS_PVT_MEM_SIZE_REG 0x22E6
 #define A3XX_SP_FS_FLAT_SHAD_MODE_REG_0 0x22E8
 #define A3XX_SP_FS_FLAT_SHAD_MODE_REG_1 0x22E9
 #define A3XX_SP_FS_OUTPUT_REG 0x22EC
 #define A3XX_SP_FS_MRT_REG_0 0x22F0
+#define A3XX_SP_FS_MRT_REG_1 0x22F1
+#define A3XX_SP_FS_MRT_REG_2 0x22F2
+#define A3XX_SP_FS_MRT_REG_3 0x22F3
 #define A3XX_SP_FS_IMAGE_OUTPUT_REG_0 0x22F4
+#define A3XX_SP_FS_IMAGE_OUTPUT_REG_1 0x22F5
+#define A3XX_SP_FS_IMAGE_OUTPUT_REG_2 0x22F6
 #define A3XX_SP_FS_IMAGE_OUTPUT_REG_3 0x22F7
 #define A3XX_SP_FS_LENGTH_REG 0x22FF
 #define A3XX_TPL1_TP_VS_TEX_OFFSET 0x2340
diff --git a/drivers/gpu/msm/adreno.c b/drivers/gpu/msm/adreno.c
index b964620..2a6fe62 100644
--- a/drivers/gpu/msm/adreno.c
+++ b/drivers/gpu/msm/adreno.c
@@ -71,6 +71,8 @@
 	 | (MMU_CONFIG << MH_MMU_CONFIG__TC_R_CLNT_BEHAVIOR__SHIFT)	\
 	 | (MMU_CONFIG << MH_MMU_CONFIG__PA_W_CLNT_BEHAVIOR__SHIFT))
 
+#define KGSL_LOG_LEVEL_DEFAULT 3
+
 static const struct kgsl_functable adreno_functable;
 
 static struct adreno_device device_3d0 = {
@@ -100,6 +102,13 @@
 		.iomemname = KGSL_3D0_REG_MEMORY,
 		.shadermemname = KGSL_3D0_SHADER_MEMORY,
 		.ftbl = &adreno_functable,
+		.cmd_log = KGSL_LOG_LEVEL_DEFAULT,
+		.ctxt_log = KGSL_LOG_LEVEL_DEFAULT,
+		.drv_log = KGSL_LOG_LEVEL_DEFAULT,
+		.mem_log = KGSL_LOG_LEVEL_DEFAULT,
+		.pwr_log = KGSL_LOG_LEVEL_DEFAULT,
+		.ft_log = KGSL_LOG_LEVEL_DEFAULT,
+		.pm_dump_enable = 0,
 	},
 	.gmem_base = 0,
 	.gmem_size = SZ_256K,
@@ -117,13 +126,7 @@
  * If the values of these registers are same after
  * KGSL_TIMEOUT_PART time, GPU hang is reported in
  * kernel log.
- * *****ALERT******ALERT********ALERT*************
- * Order of registers below is important, registers
- * from LONG_IB_DETECT_REG_INDEX_START to
- * LONG_IB_DETECT_REG_INDEX_END are used in long ib detection.
  */
-#define LONG_IB_DETECT_REG_INDEX_START 1
-#define LONG_IB_DETECT_REG_INDEX_END 5
 
 unsigned int ft_detect_regs[FT_DETECT_REGS_COUNT];
 
@@ -631,36 +634,44 @@
 
 	kgsl_mmu_unmap(pagetable, &adreno_dev->profile.shared_buffer);
 
+	kgsl_mmu_unmap(pagetable, &adreno_dev->pwron_fixup);
+
 	kgsl_mmu_unmap(pagetable, &device->mmu.setstate_memory);
 }
 
 static int adreno_setup_pt(struct kgsl_device *device,
 			struct kgsl_pagetable *pagetable)
 {
-	int result = 0;
+	int result;
 	struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
 	struct adreno_ringbuffer *rb = &adreno_dev->ringbuffer;
 
 	result = kgsl_mmu_map_global(pagetable, &rb->buffer_desc);
-	if (result)
-		goto error;
 
-	result = kgsl_mmu_map_global(pagetable, &rb->memptrs_desc);
-	if (result)
-		goto unmap_buffer_desc;
+	if (!result)
+		result = kgsl_mmu_map_global(pagetable, &rb->memptrs_desc);
 
-	result = kgsl_mmu_map_global(pagetable, &device->memstore);
-	if (result)
-		goto unmap_memptrs_desc;
+	if (!result)
+		result = kgsl_mmu_map_global(pagetable, &device->memstore);
 
-	result = kgsl_mmu_map_global(pagetable,
-					&adreno_dev->profile.shared_buffer);
-	if (result)
-		goto unmap_profile_shared;
+	if (!result)
+		result = kgsl_mmu_map_global(pagetable,
+			&adreno_dev->profile.shared_buffer);
 
-	result = kgsl_mmu_map_global(pagetable, &device->mmu.setstate_memory);
-	if (result)
-		goto unmap_memstore_desc;
+	if (!result)
+		result = kgsl_mmu_map_global(pagetable,
+			&adreno_dev->pwron_fixup);
+
+
+	if (!result)
+		result = kgsl_mmu_map_global(pagetable,
+			&device->mmu.setstate_memory);
+
+	if (result) {
+		/* On error clean up what we have wrought */
+		adreno_cleanup_pt(device, pagetable);
+		return result;
+	}
 
 	/*
 	 * Set the mpu end to the last "normal" global memory we use.
@@ -669,22 +680,8 @@
 	 */
 	device->mh.mpu_range = device->mmu.setstate_memory.gpuaddr +
 				device->mmu.setstate_memory.size;
-	return result;
 
-unmap_profile_shared:
-	kgsl_mmu_unmap(pagetable, &adreno_dev->profile.shared_buffer);
-
-unmap_memstore_desc:
-	kgsl_mmu_unmap(pagetable, &device->memstore);
-
-unmap_memptrs_desc:
-	kgsl_mmu_unmap(pagetable, &rb->memptrs_desc);
-
-unmap_buffer_desc:
-	kgsl_mmu_unmap(pagetable, &rb->buffer_desc);
-
-error:
-	return result;
+	return 0;
 }
 
 static unsigned int _adreno_iommu_setstate_v0(struct kgsl_device *device,
@@ -1693,6 +1690,10 @@
 	/* Power down the device */
 	kgsl_pwrctrl_disable(device);
 
+	/* Certain targets need the fixup.  You know who you are */
+	if (adreno_is_a330v2(adreno_dev))
+		adreno_a3xx_pwron_fixup_init(adreno_dev);
+
 	return 0;
 }
 
@@ -1715,6 +1716,9 @@
 	/* Power up the device */
 	kgsl_pwrctrl_enable(device);
 
+	/* Set the bit to indicate that we've just powered on */
+	set_bit(ADRENO_DEVICE_PWRON, &adreno_dev->priv);
+
 	/* Set up a2xx special case */
 	if (adreno_is_a2xx(adreno_dev)) {
 		/*
@@ -3326,6 +3330,9 @@
 	if (kgsl_gpuaddr_in_memdesc(&device->memstore, gpuaddr, size))
 		return &device->memstore;
 
+	if (kgsl_gpuaddr_in_memdesc(&adreno_dev->pwron_fixup, gpuaddr, size))
+		return &adreno_dev->pwron_fixup;
+
 	if (kgsl_gpuaddr_in_memdesc(&device->mmu.setstate_memory, gpuaddr,
 					size))
 		return &device->mmu.setstate_memory;
@@ -3553,7 +3560,6 @@
 	struct adreno_ringbuffer *rb = &adreno_dev->ringbuffer;
 	unsigned int curr_reg_val[FT_DETECT_REGS_COUNT];
 	unsigned int fast_hang_detected = 1;
-	unsigned int long_ib_detected = 1;
 	unsigned int i;
 	static unsigned long next_hang_detect_time;
 	static unsigned int prev_global_ts;
@@ -3566,9 +3572,6 @@
 	if (!adreno_dev->fast_hang_detect)
 		fast_hang_detected = 0;
 
-	if (!adreno_dev->long_ib_detect)
-		long_ib_detected = 0;
-
 	if (!(adreno_dev->ringbuffer.flags & KGSL_FLAGS_STARTED))
 		return 0;
 
@@ -3653,17 +3656,8 @@
 			}
 		}
 		for (i = 0; i < FT_DETECT_REGS_COUNT; i++) {
-			if (curr_reg_val[i] != prev_reg_val[i]) {
+			if (curr_reg_val[i] != prev_reg_val[i])
 				fast_hang_detected = 0;
-
-				/* Check for long IB here */
-				if ((i >=
-					LONG_IB_DETECT_REG_INDEX_START)
-					&&
-					(i <=
-					LONG_IB_DETECT_REG_INDEX_END))
-					long_ib_detected = 0;
-			}
 		}
 
 		if (fast_hang_detected) {
@@ -3685,15 +3679,12 @@
 			pid_name, curr_context->ib_gpu_time_used,
 			curr_global_ts+1);
 
-			if ((long_ib_detected) &&
+			if ((adreno_dev->long_ib_detect) &&
 				(!(curr_context->flags &
-				 CTXT_FLAGS_NO_FAULT_TOLERANCE))) {
-				curr_context->ib_gpu_time_used +=
-					KGSL_TIMEOUT_PART;
-				if (curr_context->ib_gpu_time_used >
-					KGSL_TIMEOUT_LONG_IB_DETECTION) {
-					if (adreno_dev->long_ib_ts !=
-						curr_global_ts) {
+				 CTXT_FLAGS_NO_FAULT_TOLERANCE)) &&
+				(curr_context->ib_gpu_time_used >
+					KGSL_TIMEOUT_LONG_IB_DETECTION) &&
+				(adreno_dev->long_ib_ts != curr_global_ts)) {
 						KGSL_FT_ERR(device,
 						"Proc %s, ctxt_id %d ts %d"
 						"used GPU for %d ms long ib "
@@ -3710,8 +3701,6 @@
 						curr_context->ib_gpu_time_used =
 								0;
 						return 1;
-					}
-				}
 			}
 		}
 	} else {
diff --git a/drivers/gpu/msm/adreno.h b/drivers/gpu/msm/adreno.h
index 72f15e7..9a070a6 100644
--- a/drivers/gpu/msm/adreno.h
+++ b/drivers/gpu/msm/adreno.h
@@ -40,6 +40,7 @@
 #define KGSL_CMD_FLAGS_INTERNAL_ISSUE	0x00000002
 #define KGSL_CMD_FLAGS_GET_INT		0x00000004
 #define KGSL_CMD_FLAGS_PROFILE		0x00000008
+#define KGSL_CMD_FLAGS_PWRON_FIXUP	0x00000010
 #define KGSL_CMD_FLAGS_EOF	        0x00000100
 
 /* Command identifiers */
@@ -52,6 +53,7 @@
 #define KGSL_NOP_IB_IDENTIFIER	        0x20F20F20
 #define KGSL_START_OF_PROFILE_IDENTIFIER	0x2DEFADE1
 #define KGSL_END_OF_PROFILE_IDENTIFIER	0x2DEFADE2
+#define KGSL_PWRON_FIXUP_IDENTIFIER	0x2AFAFAFA
 
 #ifdef CONFIG_MSM_SCM
 #define ADRENO_DEFAULT_PWRSCALE_POLICY  (&kgsl_pwrscale_policy_tz)
@@ -100,6 +102,7 @@
 
 struct adreno_device {
 	struct kgsl_device dev;    /* Must be first field in this struct */
+	unsigned long priv;
 	unsigned int chip_id;
 	enum adreno_gpurev gpurev;
 	unsigned long gmem_base;
@@ -136,6 +139,19 @@
 	unsigned int ocmem_base;
 	unsigned int gpu_cycles;
 	struct adreno_profile profile;
+	struct kgsl_memdesc pwron_fixup;
+	unsigned int pwron_fixup_dwords;
+};
+
+/**
+ * enum adreno_device_flags - Private flags for the adreno_device
+ * @ADRENO_DEVICE_PWRON - Set during init after a power collapse
+ * @ADRENO_DEVICE_PWRON_FIXUP - Set if the target requires the shader fixup
+ * after power collapse
+ */
+enum adreno_device_flags {
+	ADRENO_DEVICE_PWRON = 0,
+	ADRENO_DEVICE_PWRON_FIXUP = 1,
 };
 
 #define PERFCOUNTER_FLAG_NONE 0x0
@@ -436,6 +452,7 @@
 
 int adreno_soft_reset(struct kgsl_device *device);
 
+int adreno_a3xx_pwron_fixup_init(struct adreno_device *adreno_dev);
 
 static inline int adreno_is_a200(struct adreno_device *adreno_dev)
 {
diff --git a/drivers/gpu/msm/adreno_a3xx.c b/drivers/gpu/msm/adreno_a3xx.c
index d96965c..f9110ea 100644
--- a/drivers/gpu/msm/adreno_a3xx.c
+++ b/drivers/gpu/msm/adreno_a3xx.c
@@ -2510,6 +2510,478 @@
 	}
 }
 
+static const unsigned int _a3xx_pwron_fixup_fs_instructions[] = {
+	0x00000000, 0x302CC300, 0x00000000, 0x302CC304,
+	0x00000000, 0x302CC308, 0x00000000, 0x302CC30C,
+	0x00000000, 0x302CC310, 0x00000000, 0x302CC314,
+	0x00000000, 0x302CC318, 0x00000000, 0x302CC31C,
+	0x00000000, 0x302CC320, 0x00000000, 0x302CC324,
+	0x00000000, 0x302CC328, 0x00000000, 0x302CC32C,
+	0x00000000, 0x302CC330, 0x00000000, 0x302CC334,
+	0x00000000, 0x302CC338, 0x00000000, 0x302CC33C,
+	0x00000000, 0x00000400, 0x00020000, 0x63808003,
+	0x00060004, 0x63828007, 0x000A0008, 0x6384800B,
+	0x000E000C, 0x6386800F, 0x00120010, 0x63888013,
+	0x00160014, 0x638A8017, 0x001A0018, 0x638C801B,
+	0x001E001C, 0x638E801F, 0x00220020, 0x63908023,
+	0x00260024, 0x63928027, 0x002A0028, 0x6394802B,
+	0x002E002C, 0x6396802F, 0x00320030, 0x63988033,
+	0x00360034, 0x639A8037, 0x003A0038, 0x639C803B,
+	0x003E003C, 0x639E803F, 0x00000000, 0x00000400,
+	0x00000003, 0x80D60003, 0x00000007, 0x80D60007,
+	0x0000000B, 0x80D6000B, 0x0000000F, 0x80D6000F,
+	0x00000013, 0x80D60013, 0x00000017, 0x80D60017,
+	0x0000001B, 0x80D6001B, 0x0000001F, 0x80D6001F,
+	0x00000023, 0x80D60023, 0x00000027, 0x80D60027,
+	0x0000002B, 0x80D6002B, 0x0000002F, 0x80D6002F,
+	0x00000033, 0x80D60033, 0x00000037, 0x80D60037,
+	0x0000003B, 0x80D6003B, 0x0000003F, 0x80D6003F,
+	0x00000000, 0x03000000, 0x00000000, 0x00000000,
+};
+
+/**
+ * adreno_a3xx_pwron_fixup_init() - Initalize a special command buffer to run a
+ * post-power collapse shader workaround
+ * @adreno_dev: Pointer to a adreno_device struct
+ *
+ * Some targets require a special workaround shader to be executed after
+ * power-collapse.  Construct the IB once at init time and keep it
+ * handy
+ *
+ * Returns: 0 on success or negative on error
+ */
+int adreno_a3xx_pwron_fixup_init(struct adreno_device *adreno_dev)
+{
+	unsigned int *cmds;
+	int count = ARRAY_SIZE(_a3xx_pwron_fixup_fs_instructions);
+	int ret;
+
+	/* Return if the fixup is already in place */
+	if (test_bit(ADRENO_DEVICE_PWRON_FIXUP, &adreno_dev->priv))
+		return 0;
+
+	ret = kgsl_allocate_contiguous(&adreno_dev->pwron_fixup, PAGE_SIZE);
+
+	if (ret)
+		return ret;
+
+	adreno_dev->pwron_fixup.flags |= KGSL_MEMFLAGS_GPUREADONLY;
+
+	cmds = adreno_dev->pwron_fixup.hostptr;
+
+	*cmds++ = cp_type0_packet(A3XX_UCHE_CACHE_INVALIDATE0_REG, 2);
+	*cmds++ = 0x00000000;
+	*cmds++ = 0x90000000;
+	*cmds++ = cp_type3_packet(CP_WAIT_FOR_IDLE, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type3_packet(CP_REG_RMW, 3);
+	*cmds++ = A3XX_RBBM_CLOCK_CTL;
+	*cmds++ = 0xFFFCFFFF;
+	*cmds++ = 0x00010000;
+	*cmds++ = cp_type3_packet(CP_WAIT_FOR_IDLE, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_HLSQ_CONTROL_0_REG, 1);
+	*cmds++ = 0x1E000150;
+	*cmds++ = cp_type3_packet(CP_WAIT_FOR_IDLE, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2);
+	*cmds++ = CP_REG(A3XX_HLSQ_CONTROL_0_REG);
+	*cmds++ = 0x1E000150;
+	*cmds++ = cp_type3_packet(CP_WAIT_FOR_IDLE, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_HLSQ_CONTROL_0_REG, 1);
+	*cmds++ = 0x1E000150;
+	*cmds++ = cp_type0_packet(A3XX_HLSQ_CONTROL_1_REG, 1);
+	*cmds++ = 0x00000040;
+	*cmds++ = cp_type0_packet(A3XX_HLSQ_CONTROL_2_REG, 1);
+	*cmds++ = 0x80000000;
+	*cmds++ = cp_type0_packet(A3XX_HLSQ_CONTROL_3_REG, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_HLSQ_VS_CONTROL_REG, 1);
+	*cmds++ = 0x00000001;
+	*cmds++ = cp_type0_packet(A3XX_HLSQ_FS_CONTROL_REG, 1);
+	*cmds++ = 0x0D001002;
+	*cmds++ = cp_type0_packet(A3XX_HLSQ_CONST_VSPRESV_RANGE_REG, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_HLSQ_CONST_FSPRESV_RANGE_REG, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_HLSQ_CL_NDRANGE_0_REG, 1);
+	*cmds++ = 0x00401101;
+	*cmds++ = cp_type0_packet(A3XX_HLSQ_CL_NDRANGE_1_REG, 1);
+	*cmds++ = 0x00000400;
+	*cmds++ = cp_type0_packet(A3XX_HLSQ_CL_NDRANGE_2_REG, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_HLSQ_CL_NDRANGE_3_REG, 1);
+	*cmds++ = 0x00000001;
+	*cmds++ = cp_type0_packet(A3XX_HLSQ_CL_NDRANGE_4_REG, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_HLSQ_CL_NDRANGE_5_REG, 1);
+	*cmds++ = 0x00000001;
+	*cmds++ = cp_type0_packet(A3XX_HLSQ_CL_NDRANGE_6_REG, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_HLSQ_CL_CONTROL_0_REG, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_HLSQ_CL_CONTROL_1_REG, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_HLSQ_CL_KERNEL_CONST_REG, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_HLSQ_CL_KERNEL_GROUP_X_REG, 1);
+	*cmds++ = 0x00000010;
+	*cmds++ = cp_type0_packet(A3XX_HLSQ_CL_KERNEL_GROUP_Y_REG, 1);
+	*cmds++ = 0x00000001;
+	*cmds++ = cp_type0_packet(A3XX_HLSQ_CL_KERNEL_GROUP_Z_REG, 1);
+	*cmds++ = 0x00000001;
+	*cmds++ = cp_type0_packet(A3XX_HLSQ_CL_WG_OFFSET_REG, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_SP_SP_CTRL_REG, 1);
+	*cmds++ = 0x00040000;
+	*cmds++ = cp_type0_packet(A3XX_SP_VS_CTRL_REG0, 1);
+	*cmds++ = 0x0000000A;
+	*cmds++ = cp_type0_packet(A3XX_SP_VS_CTRL_REG1, 1);
+	*cmds++ = 0x00000001;
+	*cmds++ = cp_type0_packet(A3XX_SP_VS_PARAM_REG, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_SP_VS_OUT_REG_0, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_SP_VS_OUT_REG_1, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_SP_VS_OUT_REG_2, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_SP_VS_OUT_REG_3, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_SP_VS_OUT_REG_4, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_SP_VS_OUT_REG_5, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_SP_VS_OUT_REG_6, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_SP_VS_OUT_REG_7, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_SP_VS_VPC_DST_REG_0, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_SP_VS_VPC_DST_REG_1, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_SP_VS_VPC_DST_REG_2, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_SP_VS_VPC_DST_REG_3, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_SP_VS_OBJ_OFFSET_REG, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_SP_VS_OBJ_START_REG, 1);
+	*cmds++ = 0x00000004;
+	*cmds++ = cp_type0_packet(A3XX_SP_VS_PVT_MEM_PARAM_REG, 1);
+	*cmds++ = 0x04008001;
+	*cmds++ = cp_type0_packet(A3XX_SP_VS_PVT_MEM_ADDR_REG, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_SP_VS_PVT_MEM_SIZE_REG, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_SP_VS_LENGTH_REG, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_SP_FS_CTRL_REG0, 1);
+	*cmds++ = 0x0DB0400A;
+	*cmds++ = cp_type0_packet(A3XX_SP_FS_CTRL_REG1, 1);
+	*cmds++ = 0x00300402;
+	*cmds++ = cp_type0_packet(A3XX_SP_FS_OBJ_OFFSET_REG, 1);
+	*cmds++ = 0x00010000;
+	*cmds++ = cp_type0_packet(A3XX_SP_FS_OBJ_START_REG, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_SP_FS_PVT_MEM_PARAM_REG, 1);
+	*cmds++ = 0x04008001;
+	*cmds++ = cp_type0_packet(A3XX_SP_FS_PVT_MEM_ADDR_REG, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_SP_FS_PVT_MEM_SIZE_REG, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_SP_FS_FLAT_SHAD_MODE_REG_0, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_SP_FS_FLAT_SHAD_MODE_REG_1, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_SP_FS_OUTPUT_REG, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_SP_FS_MRT_REG_0, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_SP_FS_MRT_REG_1, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_SP_FS_MRT_REG_2, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_SP_FS_MRT_REG_3, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_SP_FS_IMAGE_OUTPUT_REG_0, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_SP_FS_IMAGE_OUTPUT_REG_1, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_SP_FS_IMAGE_OUTPUT_REG_2, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_SP_FS_IMAGE_OUTPUT_REG_3, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_SP_FS_LENGTH_REG, 1);
+	*cmds++ = 0x0000000D;
+	*cmds++ = cp_type0_packet(A3XX_GRAS_CL_CLIP_CNTL, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_GRAS_CL_GB_CLIP_ADJ, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_GRAS_CL_VPORT_XOFFSET, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_GRAS_CL_VPORT_XSCALE, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_GRAS_CL_VPORT_YOFFSET, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_GRAS_CL_VPORT_YSCALE, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_GRAS_CL_VPORT_ZOFFSET, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_GRAS_CL_VPORT_ZSCALE, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_GRAS_CL_USER_PLANE_X0, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_GRAS_CL_USER_PLANE_Y0, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_GRAS_CL_USER_PLANE_Z0, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_GRAS_CL_USER_PLANE_W0, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_GRAS_CL_USER_PLANE_X1, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_GRAS_CL_USER_PLANE_Y1, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_GRAS_CL_USER_PLANE_Z1, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_GRAS_CL_USER_PLANE_W1, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_GRAS_CL_USER_PLANE_X2, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_GRAS_CL_USER_PLANE_Y2, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_GRAS_CL_USER_PLANE_Z2, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_GRAS_CL_USER_PLANE_W2, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_GRAS_CL_USER_PLANE_X3, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_GRAS_CL_USER_PLANE_Y3, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_GRAS_CL_USER_PLANE_Z3, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_GRAS_CL_USER_PLANE_W3, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_GRAS_CL_USER_PLANE_X4, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_GRAS_CL_USER_PLANE_Y4, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_GRAS_CL_USER_PLANE_Z4, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_GRAS_CL_USER_PLANE_W4, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_GRAS_CL_USER_PLANE_X5, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_GRAS_CL_USER_PLANE_Y5, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_GRAS_CL_USER_PLANE_Z5, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_GRAS_CL_USER_PLANE_W5, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_GRAS_SU_POINT_MINMAX, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_GRAS_SU_POINT_SIZE, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_GRAS_SU_POLY_OFFSET_OFFSET, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_GRAS_SU_POLY_OFFSET_SCALE, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_GRAS_SU_MODE_CONTROL, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_GRAS_SC_CONTROL, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_GRAS_SC_SCREEN_SCISSOR_TL, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_GRAS_SC_SCREEN_SCISSOR_BR, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_GRAS_SC_WINDOW_SCISSOR_BR, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_GRAS_SC_WINDOW_SCISSOR_TL, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_GRAS_TSE_DEBUG_ECO, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_GRAS_PERFCOUNTER0_SELECT, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_GRAS_PERFCOUNTER1_SELECT, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_GRAS_PERFCOUNTER2_SELECT, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_GRAS_PERFCOUNTER3_SELECT, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_RB_MODE_CONTROL, 1);
+	*cmds++ = 0x00008000;
+	*cmds++ = cp_type0_packet(A3XX_RB_RENDER_CONTROL, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_RB_MSAA_CONTROL, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_RB_ALPHA_REFERENCE, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_RB_MRT_CONTROL0, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_RB_MRT_CONTROL1, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_RB_MRT_CONTROL2, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_RB_MRT_CONTROL3, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_RB_MRT_BUF_INFO0, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_RB_MRT_BUF_INFO1, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_RB_MRT_BUF_INFO2, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_RB_MRT_BUF_INFO3, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_RB_MRT_BUF_BASE0, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_RB_MRT_BUF_BASE1, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_RB_MRT_BUF_BASE2, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_RB_MRT_BUF_BASE3, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_RB_MRT_BLEND_CONTROL0, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_RB_MRT_BLEND_CONTROL1, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_RB_MRT_BLEND_CONTROL2, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_RB_MRT_BLEND_CONTROL3, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_RB_BLEND_RED, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_RB_BLEND_GREEN, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_RB_BLEND_BLUE, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_RB_BLEND_ALPHA, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_RB_CLEAR_COLOR_DW0, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_RB_CLEAR_COLOR_DW1, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_RB_CLEAR_COLOR_DW2, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_RB_CLEAR_COLOR_DW3, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_RB_COPY_CONTROL, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_RB_COPY_DEST_BASE, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_RB_COPY_DEST_PITCH, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_RB_COPY_DEST_INFO, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_RB_DEPTH_CONTROL, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_RB_DEPTH_CLEAR, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_RB_DEPTH_BUF_INFO, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_RB_DEPTH_BUF_PITCH, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_RB_STENCIL_CONTROL, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_RB_STENCIL_CLEAR, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_RB_STENCIL_BUF_INFO, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_RB_STENCIL_BUF_PITCH, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_RB_STENCIL_REF_MASK, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_RB_STENCIL_REF_MASK_BF, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_RB_LRZ_VSC_CONTROL, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_RB_WINDOW_OFFSET, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_RB_SAMPLE_COUNT_CONTROL, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_RB_SAMPLE_COUNT_ADDR, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_RB_Z_CLAMP_MIN, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_RB_Z_CLAMP_MAX, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_RB_GMEM_BASE_ADDR, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_RB_DEBUG_ECO_CONTROLS_ADDR, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_RB_PERFCOUNTER0_SELECT, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_RB_PERFCOUNTER1_SELECT, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_RB_FRAME_BUFFER_DIMENSION, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type3_packet(CP_LOAD_STATE, 4);
+	*cmds++ = (1 << CP_LOADSTATE_DSTOFFSET_SHIFT) |
+		(0 << CP_LOADSTATE_STATESRC_SHIFT) |
+		(6 << CP_LOADSTATE_STATEBLOCKID_SHIFT) |
+		(1 << CP_LOADSTATE_NUMOFUNITS_SHIFT);
+	*cmds++ = (1 << CP_LOADSTATE_STATETYPE_SHIFT) |
+		(0 << CP_LOADSTATE_EXTSRCADDR_SHIFT);
+	*cmds++ = 0x00400000;
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type3_packet(CP_LOAD_STATE, 4);
+	*cmds++ = (2 << CP_LOADSTATE_DSTOFFSET_SHIFT) |
+		(6 << CP_LOADSTATE_STATEBLOCKID_SHIFT) |
+		(1 << CP_LOADSTATE_NUMOFUNITS_SHIFT);
+	*cmds++ = (1 << CP_LOADSTATE_STATETYPE_SHIFT);
+	*cmds++ = 0x00400220;
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type3_packet(CP_LOAD_STATE, 4);
+	*cmds++ = (6 << CP_LOADSTATE_STATEBLOCKID_SHIFT) |
+		(1 << CP_LOADSTATE_NUMOFUNITS_SHIFT);
+	*cmds++ = (1 << CP_LOADSTATE_STATETYPE_SHIFT);
+	*cmds++ = 0x00000000;
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type3_packet(CP_LOAD_STATE, 2 + count);
+	*cmds++ = (6 << CP_LOADSTATE_STATEBLOCKID_SHIFT) |
+		(13 << CP_LOADSTATE_NUMOFUNITS_SHIFT);
+	*cmds++ = 0x00000000;
+
+	memcpy(cmds, _a3xx_pwron_fixup_fs_instructions, count << 2);
+
+	cmds += count;
+
+	*cmds++ = cp_type3_packet(CP_EXEC_CL, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type3_packet(CP_WAIT_FOR_IDLE, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_HLSQ_CL_CONTROL_0_REG, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type3_packet(CP_WAIT_FOR_IDLE, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type0_packet(A3XX_HLSQ_CONTROL_0_REG, 1);
+	*cmds++ = 0x1E000150;
+	*cmds++ = cp_type3_packet(CP_WAIT_FOR_IDLE, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2);
+	*cmds++ = CP_REG(A3XX_HLSQ_CONTROL_0_REG);
+	*cmds++ = 0x1E000050;
+	*cmds++ = cp_type3_packet(CP_WAIT_FOR_IDLE, 1);
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type3_packet(CP_REG_RMW, 3);
+	*cmds++ = A3XX_RBBM_CLOCK_CTL;
+	*cmds++ = 0xFFFCFFFF;
+	*cmds++ = 0x00000000;
+	*cmds++ = cp_type3_packet(CP_WAIT_FOR_IDLE, 1);
+	*cmds++ = 0x00000000;
+
+	/*
+	 * Remember the number of dwords in the command buffer for when we
+	 * program the indirect buffer call in the ringbuffer
+	 */
+	adreno_dev->pwron_fixup_dwords =
+		(cmds - (unsigned int *) adreno_dev->pwron_fixup.hostptr);
+
+	/* Mark the flag in ->priv to show that we have the fix */
+	set_bit(ADRENO_DEVICE_PWRON_FIXUP, &adreno_dev->priv);
+	return 0;
+}
+
 static int a3xx_rb_init(struct adreno_device *adreno_dev,
 			 struct adreno_ringbuffer *rb)
 {
diff --git a/drivers/gpu/msm/adreno_pm4types.h b/drivers/gpu/msm/adreno_pm4types.h
index f449870..d1e2b43 100644
--- a/drivers/gpu/msm/adreno_pm4types.h
+++ b/drivers/gpu/msm/adreno_pm4types.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2002,2007-2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2002,2007-2013, 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
@@ -177,6 +177,8 @@
 /* Load a buffer with pre-fetch enabled */
 #define CP_INDIRECT_BUFFER_PFE 0x3F
 
+#define CP_EXEC_CL 0x31
+
 #define CP_LOADSTATE_DSTOFFSET_SHIFT 0x00000000
 #define CP_LOADSTATE_STATESRC_SHIFT 0x00000010
 #define CP_LOADSTATE_STATEBLOCKID_SHIFT 0x00000013
diff --git a/drivers/gpu/msm/adreno_ringbuffer.c b/drivers/gpu/msm/adreno_ringbuffer.c
index b8cf21f..ceff923 100644
--- a/drivers/gpu/msm/adreno_ringbuffer.c
+++ b/drivers/gpu/msm/adreno_ringbuffer.c
@@ -590,7 +590,7 @@
 	 * the _addcmds call since it is allocating additional ringbuffer
 	 * command space.
 	 */
-	profile_ready = !adreno_is_a2xx(adreno_dev) &&
+	profile_ready = !adreno_is_a2xx(adreno_dev) && context &&
 		adreno_profile_assignments_ready(&adreno_dev->profile) &&
 		!(flags & KGSL_CMD_FLAGS_INTERNAL_ISSUE);
 
@@ -644,6 +644,10 @@
 	if (profile_ready)
 		total_sizedwords += 6;   /* space for pre_ib and post_ib */
 
+	/* Add space for the power on shader fixup if we need it */
+	if (flags & KGSL_CMD_FLAGS_PWRON_FIXUP)
+		total_sizedwords += 5;
+
 	ringcmds = adreno_ringbuffer_allocspace(rb, context, total_sizedwords);
 	if (!ringcmds)
 		return -ENOSPC;
@@ -660,6 +664,18 @@
 				KGSL_CMD_INTERNAL_IDENTIFIER);
 	}
 
+	if (flags & KGSL_CMD_FLAGS_PWRON_FIXUP) {
+		GSL_RB_WRITE(rb->device, ringcmds, rcmd_gpu, cp_nop_packet(1));
+		GSL_RB_WRITE(rb->device, ringcmds, rcmd_gpu,
+				KGSL_PWRON_FIXUP_IDENTIFIER);
+		GSL_RB_WRITE(rb->device, ringcmds, rcmd_gpu,
+			CP_HDR_INDIRECT_BUFFER_PFD);
+		GSL_RB_WRITE(rb->device, ringcmds, rcmd_gpu,
+			adreno_dev->pwron_fixup.gpuaddr);
+		GSL_RB_WRITE(rb->device, ringcmds, rcmd_gpu,
+			adreno_dev->pwron_fixup_dwords);
+	}
+
 	/* Add any IB required for profiling if it is enabled */
 	if (profile_ready)
 		adreno_profile_preib_processing(rb->device, context->base.id,
@@ -1143,6 +1159,10 @@
 
 	adreno_drawctxt_switch(adreno_dev, drawctxt, flags);
 
+	if (test_and_clear_bit(ADRENO_DEVICE_PWRON, &adreno_dev->priv) &&
+		test_bit(ADRENO_DEVICE_PWRON_FIXUP, &adreno_dev->priv))
+			flags |= KGSL_CMD_FLAGS_PWRON_FIXUP;
+
 	if (drawctxt->flags & CTXT_FLAGS_USER_GENERATED_TS) {
 		if (timestamp_cmp(drawctxt->timestamp, *timestamp) >= 0) {
 			KGSL_DRV_ERR(device,
@@ -1158,7 +1178,7 @@
 
 	ret = adreno_ringbuffer_addcmds(&adreno_dev->ringbuffer,
 					drawctxt,
-					(flags & KGSL_CMD_FLAGS_EOF),
+					flags,
 					&link[0], (cmds - link));
 	if (ret)
 		goto done;
diff --git a/drivers/gpu/msm/kgsl_debugfs.c b/drivers/gpu/msm/kgsl_debugfs.c
index 9ab8d22..e623515 100644
--- a/drivers/gpu/msm/kgsl_debugfs.c
+++ b/drivers/gpu/msm/kgsl_debugfs.c
@@ -19,7 +19,6 @@
 #include "kgsl_sharedmem.h"
 
 /*default log levels is error for everything*/
-#define KGSL_LOG_LEVEL_DEFAULT 3
 #define KGSL_LOG_LEVEL_MAX     7
 
 struct dentry *kgsl_debugfs_dir;
@@ -180,13 +179,6 @@
 	if (!device->d_debugfs || IS_ERR(device->d_debugfs))
 		return;
 
-	device->cmd_log = KGSL_LOG_LEVEL_DEFAULT;
-	device->ctxt_log = KGSL_LOG_LEVEL_DEFAULT;
-	device->drv_log = KGSL_LOG_LEVEL_DEFAULT;
-	device->mem_log = KGSL_LOG_LEVEL_DEFAULT;
-	device->pwr_log = KGSL_LOG_LEVEL_DEFAULT;
-	device->ft_log = KGSL_LOG_LEVEL_DEFAULT;
-
 	debugfs_create_file("log_level_cmd", 0644, device->d_debugfs, device,
 			    &cmd_log_fops);
 	debugfs_create_file("log_level_ctxt", 0644, device->d_debugfs, device,
@@ -215,7 +207,6 @@
 			    &pm_regs_enabled_fops);
 	debugfs_create_file("ib_enabled", 0644, pm_d_debugfs, device,
 				    &pm_ib_enabled_fops);
-	device->pm_dump_enable = 0;
 	debugfs_create_file("enable", 0644, pm_d_debugfs, device,
 				    &pm_enabled_fops);
 
diff --git a/drivers/gpu/msm/kgsl_pwrctrl.c b/drivers/gpu/msm/kgsl_pwrctrl.c
index 1a95761..6faf0bf 100644
--- a/drivers/gpu/msm/kgsl_pwrctrl.c
+++ b/drivers/gpu/msm/kgsl_pwrctrl.c
@@ -1442,6 +1442,8 @@
 	case KGSL_STATE_ACTIVE:
 		kgsl_pwrctrl_request_state(device, KGSL_STATE_NONE);
 		break;
+	case KGSL_STATE_INIT:
+		break;
 	default:
 		KGSL_PWR_WARN(device, "unhandled state %s\n",
 				kgsl_pwrstate_to_str(device->state));
@@ -1537,10 +1539,13 @@
 
 	if ((atomic_read(&device->active_cnt) == 0) &&
 		(device->state != KGSL_STATE_ACTIVE)) {
-		mutex_unlock(&device->mutex);
-		wait_for_completion(&device->hwaccess_gate);
-		wait_for_completion(&device->ft_gate);
-		mutex_lock(&device->mutex);
+
+		if (device->state != KGSL_STATE_DUMP_AND_FT) {
+			mutex_unlock(&device->mutex);
+			wait_for_completion(&device->hwaccess_gate);
+			wait_for_completion(&device->ft_gate);
+			mutex_lock(&device->mutex);
+		}
 
 		/* Stop the idle timer */
 		del_timer_sync(&device->idle_timer);
diff --git a/drivers/gpu/msm/z180.c b/drivers/gpu/msm/z180.c
index 883417f..103a751 100644
--- a/drivers/gpu/msm/z180.c
+++ b/drivers/gpu/msm/z180.c
@@ -124,6 +124,8 @@
 	| (MMU_CONFIG << MH_MMU_CONFIG__TC_R_CLNT_BEHAVIOR__SHIFT)   \
 	| (MMU_CONFIG << MH_MMU_CONFIG__PA_W_CLNT_BEHAVIOR__SHIFT))
 
+#define KGSL_LOG_LEVEL_DEFAULT 3
+
 static const struct kgsl_functable z180_functable;
 
 static struct z180_device device_2d0 = {
@@ -149,6 +151,13 @@
 		},
 		.iomemname = KGSL_2D0_REG_MEMORY,
 		.ftbl = &z180_functable,
+		.cmd_log = KGSL_LOG_LEVEL_DEFAULT,
+		.ctxt_log = KGSL_LOG_LEVEL_DEFAULT,
+		.drv_log = KGSL_LOG_LEVEL_DEFAULT,
+		.mem_log = KGSL_LOG_LEVEL_DEFAULT,
+		.pwr_log = KGSL_LOG_LEVEL_DEFAULT,
+		.ft_log = KGSL_LOG_LEVEL_DEFAULT,
+		.pm_dump_enable = 0,
 	},
 	.cmdwin_lock = __SPIN_LOCK_INITIALIZER(device_2d1.cmdwin_lock),
 };
diff --git a/drivers/input/misc/cm36283.c b/drivers/input/misc/cm36283.c
index 6280013..a9f8140 100644
--- a/drivers/input/misc/cm36283.c
+++ b/drivers/input/misc/cm36283.c
@@ -27,15 +27,15 @@
 #include <linux/err.h>
 #include <linux/gpio.h>
 #include <linux/miscdevice.h>
-#include <linux/lightsensor.h>
 #include <linux/slab.h>
-#include <asm/uaccess.h>
-#include <asm/mach-types.h>
-#include <linux/cm36283.h>
-#include <linux/capella_cm3602.h>
-#include <asm/setup.h>
+#include <linux/regulator/consumer.h>
 #include <linux/wakelock.h>
 #include <linux/jiffies.h>
+#include <linux/cm36283.h>
+
+#include <asm/uaccess.h>
+#include <asm/mach-types.h>
+#include <asm/setup.h>
 
 #define D(x...) pr_info(x)
 
@@ -47,6 +47,12 @@
 #define CONTROL_ALS                   0x01
 #define CONTROL_PS                    0x02
 
+/* POWER SUPPLY VOLTAGE RANGE */
+#define CM36283_VDD_MIN_UV	2700000
+#define CM36283_VDD_MAX_UV	3300000
+#define CM36283_VI2C_MIN_UV	1750000
+#define CM36283_VI2C_MAX_UV	1950000
+
 static int record_init_fail = 0;
 static void sensor_irq_do_work(struct work_struct *work);
 static DECLARE_WORK(sensor_irq_work, sensor_irq_do_work);
@@ -95,6 +101,9 @@
 
 	uint16_t ls_cmd;
 	uint8_t record_clear_int_fail;
+
+	struct regulator *vdd;
+	struct regulator *vio;
 };
 struct cm36283_info *lp_info;
 int fLevel=-1;
@@ -1625,6 +1634,135 @@
   return ret;
 }
 
+static int cm36283_power_set(struct cm36283_info *info, bool on)
+{
+	int rc;
+
+	if (on) {
+		info->vdd = regulator_get(&info->i2c_client->dev, "vdd");
+		if (IS_ERR(info->vdd)) {
+			rc = PTR_ERR(info->vdd);
+			dev_err(&info->i2c_client->dev,
+				"Regulator get failed vdd rc=%d\n", rc);
+			goto err_vdd_get;
+		}
+
+		if (regulator_count_voltages(info->vdd) > 0) {
+			rc = regulator_set_voltage(info->vdd,
+					CM36283_VDD_MIN_UV, CM36283_VDD_MAX_UV);
+			if (rc) {
+				dev_err(&info->i2c_client->dev,
+					"Regulator set failed vdd rc=%d\n", rc);
+				goto err_vdd_set_vtg;
+			}
+		}
+
+		info->vio = regulator_get(&info->i2c_client->dev, "vio");
+		if (IS_ERR(info->vio)) {
+			rc = PTR_ERR(info->vio);
+			dev_err(&info->i2c_client->dev,
+				"Regulator get failed vio rc=%d\n", rc);
+			goto err_vio_get;
+		}
+
+		if (regulator_count_voltages(info->vio) > 0) {
+			rc = regulator_set_voltage(info->vio,
+				CM36283_VI2C_MIN_UV, CM36283_VI2C_MAX_UV);
+			if (rc) {
+				dev_err(&info->i2c_client->dev,
+				"Regulator set failed vio rc=%d\n", rc);
+				goto err_vio_set_vtg;
+			}
+		}
+
+		rc = regulator_enable(info->vdd);
+		if (rc) {
+			dev_err(&info->i2c_client->dev,
+				"Regulator vdd enable failed rc=%d\n", rc);
+			goto err_vdd_ena;
+		}
+
+		rc = regulator_enable(info->vio);
+		if (rc) {
+			dev_err(&info->i2c_client->dev,
+				"Regulator vio enable failed rc=%d\n", rc);
+			goto err_vio_ena;
+		}
+
+	} else {
+		rc = regulator_disable(info->vdd);
+		if (rc) {
+			dev_err(&info->i2c_client->dev,
+				"Regulator vdd disable failed rc=%d\n", rc);
+			return rc;
+		}
+		if (regulator_count_voltages(info->vdd) > 0)
+			regulator_set_voltage(info->vdd, 0, CM36283_VDD_MAX_UV);
+
+		regulator_put(info->vdd);
+
+		rc = regulator_disable(info->vio);
+		if (rc) {
+			dev_err(&info->i2c_client->dev,
+				"Regulator vio disable failed rc=%d\n", rc);
+			return rc;
+		}
+		if (regulator_count_voltages(info->vio) > 0)
+			regulator_set_voltage(info->vio, 0,
+					CM36283_VI2C_MAX_UV);
+
+		regulator_put(info->vio);
+	}
+
+	return 0;
+
+err_vio_ena:
+	regulator_disable(info->vdd);
+err_vdd_ena:
+	if (regulator_count_voltages(info->vio) > 0)
+		regulator_set_voltage(info->vio, 0, CM36283_VI2C_MAX_UV);
+err_vio_set_vtg:
+	regulator_put(info->vio);
+err_vio_get:
+	if (regulator_count_voltages(info->vdd) > 0)
+		regulator_set_voltage(info->vdd, 0, CM36283_VDD_MAX_UV);
+err_vdd_set_vtg:
+	regulator_put(info->vdd);
+err_vdd_get:
+	return rc;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int cm36283_suspend(struct device *dev)
+{
+	struct cm36283_info *lpi = lp_info;
+
+	if (lpi->als_enable) {
+		lightsensor_disable(lpi);
+		lpi->als_enable = 1;
+	}
+	cm36283_power_set(lpi, 0);
+
+	return 0;
+}
+
+static int cm36283_resume(struct device *dev)
+{
+	struct cm36283_info *lpi = lp_info;
+
+	cm36283_power_set(lpi, 1);
+
+	if (lpi->als_enable) {
+		cm36283_setup(lpi);
+		lightsensor_setup(lpi);
+		psensor_setup(lpi);
+		lightsensor_enable(lpi);
+	}
+	return 0;
+}
+#endif
+
+static UNIVERSAL_DEV_PM_OPS(cm36283_pm, cm36283_suspend, cm36283_resume, NULL);
 
 static const struct i2c_device_id cm36283_i2c_id[] = {
 	{CM36283_I2C_NAME, 0},
@@ -1637,6 +1775,7 @@
 	.driver = {
 		.name = CM36283_I2C_NAME,
 		.owner = THIS_MODULE,
+		.pm = &cm36283_pm,
 	},
 };
 
diff --git a/drivers/input/touchscreen/ft5x06_ts.c b/drivers/input/touchscreen/ft5x06_ts.c
index 6044512..2f9ea10 100644
--- a/drivers/input/touchscreen/ft5x06_ts.c
+++ b/drivers/input/touchscreen/ft5x06_ts.c
@@ -41,7 +41,7 @@
 #define FT_SUSPEND_LEVEL 1
 #endif
 
-#define FT_DRIVER_VERSION	0x01
+#define FT_DRIVER_VERSION	0x02
 
 #define FT_META_REGS		3
 #define FT_ONE_TCH_LEN		6
@@ -69,6 +69,9 @@
 #define FT_REG_THGROUP		0x80
 #define FT_REG_ECC		0xCC
 #define FT_REG_RESET_FW		0x07
+#define FT_REG_FW_MAJ_VER	0xB1
+#define FT_REG_FW_MIN_VER	0xB2
+#define FT_REG_FW_SUB_MIN_VER	0xB3
 
 /* power register bits*/
 #define FT_PMODE_ACTIVE		0x00
@@ -107,7 +110,12 @@
 
 #define FT_FW_MIN_SIZE		8
 #define FT_FW_MAX_SIZE		32768
-#define FT_FW_FILE_VER(x)	((x)->data[(x)->size - 2])
+
+/* Firmware file is not supporting minor and sub minor so use 0 */
+#define FT_FW_FILE_MAJ_VER(x)	((x)->data[(x)->size - 2])
+#define FT_FW_FILE_MIN_VER(x)	0
+#define FT_FW_FILE_SUB_MIN_VER(x) 0
+
 #define FT_FW_CHECK(x)		\
 	(((x)->data[(x)->size - 8] ^ (x)->data[(x)->size - 6]) == 0xFF \
 	&& (((x)->data[(x)->size - 7] ^ (x)->data[(x)->size - 5]) == 0xFF \
@@ -134,7 +142,8 @@
 
 #define FT_INFO_MAX_LEN		512
 
-#define FT_STORE_TS_INFO(buf, id, name, max_tch, group_id, fw_name, fw_ver) \
+#define FT_STORE_TS_INFO(buf, id, name, max_tch, group_id, fw_vkey_support, \
+			fw_name, fw_maj, fw_min, fw_sub_min) \
 			snprintf(buf, FT_INFO_MAX_LEN, \
 				"controller\t= focaltech\n" \
 				"model\t\t= 0x%x\n" \
@@ -142,10 +151,12 @@
 				"max_touches\t= %d\n" \
 				"drv_ver\t\t= 0x%x\n" \
 				"group_id\t= 0x%x\n" \
+				"fw_vkey_support\t= %s\n" \
 				"fw_name\t\t= %s\n" \
-				"fw_ver\t\t= 0x%x\n", id, name, \
+				"fw_ver\t\t= %d.%d.%d\n", id, name, \
 				max_tch, FT_DRIVER_VERSION, group_id, \
-				fw_name, fw_ver)
+				fw_vkey_support, fw_name, fw_maj, fw_min, \
+				fw_sub_min)
 
 #define FT_DEBUG_DIR_NAME	"ts_debug"
 
@@ -164,6 +175,7 @@
 	char *ts_info;
 	u8 *tch_data;
 	u32 tch_data_len;
+	u8 fw_ver[3];
 #if defined(CONFIG_FB)
 	struct notifier_block fb_notif;
 #elif defined(CONFIG_HAS_EARLYSUSPEND)
@@ -246,6 +258,31 @@
 	return ft5x06_i2c_read(client, &addr, 1, val, 1);
 }
 
+static void ft5x06_update_fw_ver(struct ft5x06_ts_data *data)
+{
+	struct i2c_client *client = data->client;
+	u8 reg_addr;
+	int err;
+
+	reg_addr = FT_REG_FW_MAJ_VER;
+	err = ft5x06_i2c_read(client, &reg_addr, 1, &data->fw_ver[0], 1);
+	if (err < 0)
+		dev_err(&client->dev, "fw major version read failed");
+
+	reg_addr = FT_REG_FW_MIN_VER;
+	err = ft5x06_i2c_read(client, &reg_addr, 1, &data->fw_ver[1], 1);
+	if (err < 0)
+		dev_err(&client->dev, "fw minor version read failed");
+
+	reg_addr = FT_REG_FW_SUB_MIN_VER;
+	err = ft5x06_i2c_read(client, &reg_addr, 1, &data->fw_ver[2], 1);
+	if (err < 0)
+		dev_err(&client->dev, "fw sub minor version read failed");
+
+	dev_info(&client->dev, "Firmware version = %d.%d.%d\n",
+		data->fw_ver[0], data->fw_ver[1], data->fw_ver[2]);
+}
+
 static irqreturn_t ft5x06_ts_interrupt(int irq, void *dev_id)
 {
 	struct ft5x06_ts_data *data = dev_id;
@@ -297,8 +334,10 @@
 			input_report_abs(ip_dev, ABS_MT_POSITION_X, x);
 			input_report_abs(ip_dev, ABS_MT_POSITION_Y, y);
 			input_report_abs(ip_dev, ABS_MT_PRESSURE, pressure);
-		} else
+		} else {
 			input_mt_report_slot_state(ip_dev, MT_TOOL_FINGER, 0);
+			input_report_abs(ip_dev, ABS_MT_PRESSURE, 0);
+		}
 	}
 
 	if (update_input) {
@@ -746,7 +785,8 @@
 	struct ft5x06_ts_data *data = dev_get_drvdata(dev);
 	const struct firmware *fw = NULL;
 	int rc;
-	u8 val = 0;
+	u8 fw_file_maj, fw_file_min, fw_file_sub_min;
+	bool fw_upgrade = false;
 
 	rc = request_firmware(&fw, data->fw_name, dev);
 	if (rc < 0) {
@@ -761,27 +801,38 @@
 		goto rel_fw;
 	}
 
-	/* check firmware version */
-	rc = ft5x0x_read_reg(data->client, FT_REG_FW_VER, &val);
-	if (rc < 0) {
-		dev_err(dev, "Get firmware version failed\n");
-		goto rel_fw;
-	}
+	fw_file_maj = FT_FW_FILE_MAJ_VER(fw);
+	fw_file_min = FT_FW_FILE_MIN_VER(fw);
+	fw_file_sub_min = FT_FW_FILE_SUB_MIN_VER(fw);
 
-	if (val == FT_FW_FILE_VER(fw) && !force) {
-		dev_err(dev, "No need to update (0x%x)\n", val);
+	dev_info(dev, "Current firmware: %d.%d.%d", data->fw_ver[0],
+				data->fw_ver[1], data->fw_ver[2]);
+	dev_info(dev, "New firmware: %d.%d.%d", fw_file_maj,
+				fw_file_min, fw_file_sub_min);
+
+	if (force) {
+		fw_upgrade = true;
+	} else if (data->fw_ver[0] == fw_file_maj) {
+			if (data->fw_ver[1] < fw_file_min)
+				fw_upgrade = true;
+			else if (data->fw_ver[2] < fw_file_sub_min)
+				fw_upgrade = true;
+			else
+				dev_info(dev, "No need to upgrade\n");
+	} else
+		dev_info(dev, "Firmware versions do not match\n");
+
+	if (!fw_upgrade) {
+		dev_info(dev, "Exiting fw upgrade...\n");
 		rc = -EFAULT;
 		goto rel_fw;
 	}
 
-	dev_info(dev, "upgrade to fw ver 0x%x from 0x%x\n",
-					FT_FW_FILE_VER(fw), val);
-
 	/* start firmware upgrade */
 	if (FT_FW_CHECK(fw)) {
 		rc = ft5x06_fw_upgrade_start(data->client, fw->data, fw->size);
 		if (rc < 0)
-			dev_err(dev, "update failed (%d)\n", rc);
+			dev_err(dev, "update failed (%d). try later...\n", rc);
 		else if (data->pdata->info.auto_cal)
 			ft5x06_auto_cal(data->client);
 	} else {
@@ -789,9 +840,13 @@
 		rc = -EIO;
 	}
 
+	ft5x06_update_fw_ver(data);
+
 	FT_STORE_TS_INFO(data->ts_info, data->family_id, data->pdata->name,
 			data->pdata->num_max_touches, data->pdata->group_id,
-			data->pdata->fw_name, FT_FW_FILE_VER(fw));
+			data->pdata->fw_vkey_support ? "yes" : "no",
+			data->pdata->fw_name, data->fw_ver[0],
+			data->fw_ver[1], data->fw_ver[2]);
 rel_fw:
 	release_firmware(fw);
 	return rc;
@@ -819,6 +874,11 @@
 	if (rc != 0)
 		return rc;
 
+	if (data->suspended) {
+		dev_info(dev, "In suspend state, try again later...\n");
+		return size;
+	}
+
 	mutex_lock(&data->input_dev->mutex);
 	if (!data->loading_fw  && val) {
 		data->loading_fw = true;
@@ -1184,6 +1244,9 @@
 	pdata->info.auto_cal = of_property_read_bool(np,
 					"focaltech,fw-auto-cal");
 
+	pdata->fw_vkey_support = of_property_read_bool(np,
+						"focaltech,fw-vkey-support");
+
 	rc = of_property_read_u32(np, "focaltech,family-id", &temp_val);
 	if (!rc)
 		pdata->family_id = temp_val;
@@ -1478,16 +1541,13 @@
 
 	dev_dbg(&client->dev, "touch threshold = %d\n", reg_value * 4);
 
-	reg_addr = FT_REG_FW_VER;
-	err = ft5x06_i2c_read(client, &reg_addr, 1, &reg_value, 1);
-	if (err < 0)
-		dev_err(&client->dev, "version read failed");
-
-	dev_info(&client->dev, "Firmware version = 0x%x\n", reg_value);
+	ft5x06_update_fw_ver(data);
 
 	FT_STORE_TS_INFO(data->ts_info, data->family_id, data->pdata->name,
 			data->pdata->num_max_touches, data->pdata->group_id,
-			data->pdata->fw_name, reg_value);
+			data->pdata->fw_vkey_support ? "yes" : "no",
+			data->pdata->fw_name, data->fw_ver[0],
+			data->fw_ver[1], data->fw_ver[2]);
 
 #if defined(CONFIG_FB)
 	data->fb_notif.notifier_call = fb_notifier_callback;
diff --git a/drivers/input/touchscreen/synaptics_fw_update.c b/drivers/input/touchscreen/synaptics_fw_update.c
index 2a5fea7..385947f 100644
--- a/drivers/input/touchscreen/synaptics_fw_update.c
+++ b/drivers/input/touchscreen/synaptics_fw_update.c
@@ -32,14 +32,12 @@
 #define SHOW_PROGRESS
 #define MAX_FIRMWARE_ID_LEN 10
 #define FORCE_UPDATE false
+#define DO_LOCKDOWN false
 #define INSIDE_FIRMWARE_UPDATE
 
 #define FW_IMAGE_OFFSET 0x100
-
-#define BOOTLOADER_ID_OFFSET 0
-#define FLASH_PROPERTIES_OFFSET 2
-#define BLOCK_SIZE_OFFSET 3
-#define FW_BLOCK_COUNT_OFFSET 5
+/* 0 to ignore flash block check to speed up flash time */
+#define CHECK_FLASH_BLOCK_STATUS 1
 
 #define REG_MAP (1 << 0)
 #define UNLOCKED (1 << 1)
@@ -49,9 +47,6 @@
 #define HAS_DISP_CONFIG (1 << 5)
 #define HAS_CTRL1 (1 << 6)
 
-#define BLOCK_NUMBER_OFFSET 0
-#define BLOCK_DATA_OFFSET 2
-
 #define RMI4_INFO_MAX_LEN	200
 
 #define RMI4_STORE_TS_INFO(buf, id, rev, fw_ver) \
@@ -70,6 +65,7 @@
 enum flash_command {
 	CMD_WRITE_FW_BLOCK		= 0x2,
 	CMD_ERASE_ALL			= 0x3,
+	CMD_WRITE_LOCKDOWN_BLOCK	= 0x4,
 	CMD_READ_CONFIG_BLOCK	= 0x5,
 	CMD_WRITE_CONFIG_BLOCK	= 0x6,
 	CMD_ERASE_CONFIG		= 0x7,
@@ -91,6 +87,23 @@
 	OPTION_CONTAIN_BOOTLOADER = 1,
 };
 
+enum flash_offset {
+	OFFSET_BOOTLOADER_ID,
+	OFFSET_FLASH_PROPERTIES,
+	OFFSET_BLOCK_SIZE,
+	OFFSET_FW_BLOCK_COUNT,
+	OFFSET_BLOCK_NUMBER,
+	OFFSET_BLOCK_DATA,
+	OFFSET_FLASH_CONTROL,
+	OFFSET_FLASH_STATUS
+};
+
+enum flash_update_mode {
+	NORMAL = 1,
+	FORCE = 2,
+	LOCKDOWN = 8
+};
+
 #define SLEEP_MODE_NORMAL (0x00)
 #define SLEEP_MODE_SENSOR_SLEEP (0x01)
 #define SLEEP_MODE_RESERVED0 (0x02)
@@ -101,9 +114,7 @@
 #define ERASE_WAIT_MS (5 * 1000)
 #define RESET_WAIT_MS (500)
 
-#define POLLING_MODE 0
-
-#define SLEEP_TIME_US 50
+#define SLEEP_TIME_US 100
 
 static int fwu_wait_for_idle(int timeout_ms);
 
@@ -141,7 +152,8 @@
 	};
 };
 
-struct image_header {
+struct image_content {
+	bool is_contain_build_info;
 	unsigned int checksum;
 	unsigned int image_size;
 	unsigned int config_size;
@@ -152,7 +164,10 @@
 	u16 package_id;
 	u16 package_revision_id;
 	unsigned int firmware_id;
-	bool is_contain_build_info;
+	const unsigned char *firmware_data;
+	const unsigned char *config_data;
+	const unsigned char *lockdown_data;
+	unsigned short lockdown_block_count;
 };
 
 struct pdt_properties {
@@ -194,11 +209,28 @@
 
 struct f34_flash_control {
 	union {
+	/* version 0 */
 		struct {
-			unsigned char command:4;
+			unsigned char command_v0:4;
 			unsigned char status:3;
 			unsigned char program_enabled:1;
 		} __packed;
+	/* version 1 */
+		struct {
+			unsigned char command_v1:6;
+			unsigned char reserved:2;
+		} __packed;
+		unsigned char data[1];
+	};
+};
+
+struct f34_flash_status {
+	union {
+		struct {
+			unsigned char status:6;
+			unsigned char reserved:1;
+			unsigned char program_enabled:1;
+		} __packed;
 		unsigned char data[1];
 	};
 };
@@ -222,6 +254,9 @@
 struct synaptics_rmi4_fwu_handle {
 	bool initialized;
 	bool force_update;
+	bool do_lockdown;
+	bool interrupt_flag;
+	bool polling_mode;
 	char product_id[SYNAPTICS_RMI4_PRODUCT_ID_SIZE + 1];
 	unsigned int image_size;
 	unsigned int data_pos;
@@ -233,31 +268,33 @@
 	unsigned char *read_config_buf;
 	const unsigned char *firmware_data;
 	const unsigned char *config_data;
+	const unsigned char *lockdown_data;
 	unsigned short block_size;
 	unsigned short fw_block_count;
 	unsigned short config_block_count;
+	unsigned short lockdown_block_count;
 	unsigned short perm_config_block_count;
 	unsigned short bl_config_block_count;
 	unsigned short disp_config_block_count;
 	unsigned short config_size;
 	unsigned short config_area;
-	unsigned short addr_f34_flash_control;
 	unsigned short addr_f01_interrupt_register;
+	const unsigned char *data_buffer;
 	struct synaptics_rmi4_fn_desc f01_fd;
 	struct synaptics_rmi4_fn_desc f34_fd;
 	struct synaptics_rmi4_exp_fn_ptr *fn_ptr;
 	struct synaptics_rmi4_data *rmi4_data;
-	struct f34_flash_control flash_control;
 	struct f34_flash_properties flash_properties;
 	struct workqueue_struct *fwu_workqueue;
 	struct delayed_work fwu_work;
-	char firmware_name[NAME_BUFFER_SIZE];
+	char image_name[NAME_BUFFER_SIZE];
+	struct image_content image_content;
 	char *ts_info;
 };
 
 static struct synaptics_rmi4_fwu_handle *fwu;
 
-static struct completion remove_complete;
+DECLARE_COMPLETION(fwu_remove_complete);
 
 static unsigned int extract_uint(const unsigned char *ptr)
 {
@@ -295,42 +332,80 @@
 		pkg_id[3] << 8 | pkg_id[2], build_id);
 }
 
-static void parse_header(struct image_header *header,
-		const unsigned char *fw_image)
+static void parse_header(void)
 {
-	struct image_header_data *data = (struct image_header_data *)fw_image;
-	header->checksum = extract_uint(data->file_checksum);
-	header->bootloader_version = data->bootloader_version;
-	header->image_size = extract_uint(data->firmware_size);
-	header->config_size = extract_uint(data->config_size);
-	memcpy(header->product_id, data->product_id,
+	struct image_content *img = &fwu->image_content;
+	struct image_header_data *data =
+		(struct image_header_data *)fwu->data_buffer;
+	img->checksum = extract_uint(data->file_checksum);
+	img->bootloader_version = data->bootloader_version;
+	img->image_size = extract_uint(data->firmware_size);
+	img->config_size = extract_uint(data->config_size);
+	memcpy(img->product_id, data->product_id,
 		sizeof(data->product_id));
-	header->product_id[sizeof(data->product_id)] = 0;
+	img->product_id[sizeof(data->product_id)] = 0;
 
-	memcpy(header->product_info, data->product_info,
+	img->product_id[sizeof(data->product_info)] = 0;
+	memcpy(img->product_info, data->product_info,
 		sizeof(data->product_info));
 
-	header->is_contain_build_info =
+	img->is_contain_build_info =
 		(data->options_firmware_id == (1 << OPTION_BUILD_INFO));
-	if (header->is_contain_build_info) {
-		header->package_id = (data->pkg_id_rev_msb << 8) |
+
+	if (img->is_contain_build_info) {
+		img->firmware_id = extract_uint(data->firmware_id);
+		img->package_id = (data->pkg_id_rev_msb << 8) |
 				data->pkg_id_lsb;
-		header->package_revision_id = (data->pkg_id_rev_msb << 8) |
+		img->package_revision_id = (data->pkg_id_rev_msb << 8) |
 				data->pkg_id_rev_lsb;
 		dev_info(&fwu->rmi4_data->i2c_client->dev,
 			"%s Package ID %d Rev %d\n", __func__,
-			header->package_id, header->package_revision_id);
+			img->package_id, img->package_revision_id);
 
-		header->firmware_id = extract_uint(data->firmware_id);
+		img->firmware_id = extract_uint(data->firmware_id);
 		dev_info(&fwu->rmi4_data->i2c_client->dev,
 			"%s Firwmare build id %d\n", __func__,
-			header->firmware_id);
+			img->firmware_id);
 	}
 
 	dev_dbg(&fwu->rmi4_data->i2c_client->dev,
 		"Firwmare size %d, config size %d\n",
-		header->image_size,
-		header->config_size);
+		img->image_size,
+		img->config_size);
+
+	/* get UI firmware offset */
+	if (img->image_size)
+		img->firmware_data = fwu->data_buffer + FW_IMAGE_OFFSET;
+	/* get config offset*/
+	if (img->config_size)
+		img->config_data = fwu->data_buffer + FW_IMAGE_OFFSET +
+				img->image_size;
+	/* get lockdown offset*/
+	switch (img->bootloader_version) {
+	case 3:
+	case 4:
+		img->lockdown_block_count = 4;
+		break;
+	case 5:
+	case 6:
+		img->lockdown_block_count = 5;
+		break;
+	default:
+		dev_warn(&fwu->rmi4_data->i2c_client->dev,
+			"%s: Not support lockdown in " \
+			"bootloader version V%d\n",
+			__func__, img->bootloader_version);
+		img->lockdown_data = NULL;
+	}
+
+	img->lockdown_data = fwu->data_buffer +
+				FW_IMAGE_OFFSET -
+				img->lockdown_block_count * fwu->block_size;
+
+	fwu->lockdown_block_count = img->lockdown_block_count;
+	fwu->lockdown_data = img->lockdown_data;
+	fwu->config_data = img->config_data;
+	fwu->firmware_data = img->firmware_data;
 	return;
 }
 
@@ -352,6 +427,62 @@
 	return 0;
 }
 
+static unsigned short fwu_get_address(enum flash_offset type)
+{
+	int offset;
+	unsigned short addr = 0;
+	struct i2c_client *i2c_client = fwu->rmi4_data->i2c_client;
+
+	switch (type) {
+	case OFFSET_BOOTLOADER_ID:
+		offset = 0;
+		addr = fwu->f34_fd.query_base_addr + offset;
+		break;
+	case OFFSET_FLASH_PROPERTIES:
+		offset = ((fwu->f34_fd.version == 0) ? 2 : 1);
+		addr = fwu->f34_fd.query_base_addr + offset;
+		break;
+	case OFFSET_BLOCK_SIZE:
+		offset = ((fwu->f34_fd.version == 0) ? 3 : 2);
+		addr = fwu->f34_fd.query_base_addr + offset;
+		break;
+	case OFFSET_FW_BLOCK_COUNT:
+		offset = ((fwu->f34_fd.version == 0) ? 5 : 3);
+		addr = fwu->f34_fd.query_base_addr + offset;
+		break;
+	case OFFSET_BLOCK_NUMBER:
+		offset = 0;
+		addr = fwu->f34_fd.data_base_addr + offset;
+		break;
+	case OFFSET_BLOCK_DATA:
+		offset = ((fwu->f34_fd.version == 0) ? 2 : 1);
+		addr = fwu->f34_fd.data_base_addr + offset;
+		break;
+	case OFFSET_FLASH_CONTROL:
+		offset = ((fwu->f34_fd.version == 0) ?
+			2 + (fwu->block_size) : 2);
+		addr = fwu->f34_fd.data_base_addr + offset;
+		break;
+	case OFFSET_FLASH_STATUS:
+		if (fwu->f34_fd.version == 1) {
+			offset = 3;
+			addr = fwu->f34_fd.data_base_addr + offset;
+		} else if (fwu->f34_fd.version == 0) {
+			dev_warn(&i2c_client->dev,
+			"%s: F$34 version 0 does not contain " \
+			"flash status register\n",
+			__func__);
+		}
+		break;
+	default:
+		dev_err(&i2c_client->dev,
+			"%s: Unknown flash offset (%d)\n",
+			__func__, type);
+		break;
+	}
+	return addr;
+}
+
 static int fwu_read_f34_queries(void)
 {
 	int retval;
@@ -360,7 +491,7 @@
 	struct i2c_client *i2c_client = fwu->rmi4_data->i2c_client;
 
 	retval = fwu->fn_ptr->read(fwu->rmi4_data,
-			fwu->f34_fd.query_base_addr + BOOTLOADER_ID_OFFSET,
+			fwu_get_address(OFFSET_BOOTLOADER_ID),
 			fwu->bootloader_id,
 			sizeof(fwu->bootloader_id));
 	if (retval < 0) {
@@ -371,7 +502,7 @@
 	}
 
 	retval = fwu->fn_ptr->read(fwu->rmi4_data,
-			fwu->f34_fd.query_base_addr + FLASH_PROPERTIES_OFFSET,
+			fwu_get_address(OFFSET_FLASH_PROPERTIES),
 			fwu->flash_properties.data,
 			sizeof(fwu->flash_properties.data));
 	if (retval < 0) {
@@ -397,7 +528,7 @@
 		count += 2;
 
 	retval = fwu->fn_ptr->read(fwu->rmi4_data,
-			fwu->f34_fd.query_base_addr + BLOCK_SIZE_OFFSET,
+			fwu_get_address(OFFSET_BLOCK_SIZE),
 			buf,
 			2);
 	if (retval < 0) {
@@ -410,13 +541,13 @@
 	batohs(&fwu->block_size, &(buf[0]));
 
 	retval = fwu->fn_ptr->read(fwu->rmi4_data,
-			fwu->f34_fd.query_base_addr + FW_BLOCK_COUNT_OFFSET,
+			fwu_get_address(OFFSET_FW_BLOCK_COUNT),
 			buf,
 			count);
 	if (retval < 0) {
 		dev_err(&i2c_client->dev,
-				"%s: Failed to read block count info\n",
-				__func__);
+			"%s: Failed to read block count info\n",
+			__func__);
 		return retval;
 	}
 
@@ -438,9 +569,6 @@
 	if (fwu->flash_properties.has_display_config)
 		batohs(&fwu->disp_config_block_count, &(buf[count]));
 
-	fwu->addr_f34_flash_control = fwu->f34_fd.data_base_addr +
-					BLOCK_DATA_OFFSET +
-					fwu->block_size;
 	return 0;
 }
 
@@ -461,18 +589,36 @@
 	return interrupt_status;
 }
 
-static int fwu_read_f34_flash_status(void)
+static int fwu_read_f34_flash_status(unsigned char *status)
 {
 	int retval;
-	retval = fwu->fn_ptr->read(fwu->rmi4_data,
-			fwu->addr_f34_flash_control,
-			fwu->flash_control.data,
-			sizeof(fwu->flash_control.data));
-	if (retval < 0) {
-		dev_err(&fwu->rmi4_data->i2c_client->dev,
+	struct f34_flash_control flash_control;
+	struct f34_flash_status flash_status;
+
+	if (fwu->f34_fd.version == 1) {
+		retval = fwu->fn_ptr->read(fwu->rmi4_data,
+			fwu_get_address(OFFSET_FLASH_STATUS),
+			flash_status.data,
+			sizeof(flash_status.data));
+		if (retval < 0) {
+			dev_err(&fwu->rmi4_data->i2c_client->dev,
 				"%s: Failed to read flash status\n",
 				__func__);
-		return retval;
+			return -EIO;
+		}
+		*status = flash_status.status;
+	} else {
+		retval = fwu->fn_ptr->read(fwu->rmi4_data,
+			fwu_get_address(OFFSET_FLASH_CONTROL),
+			flash_control.data,
+			sizeof(flash_control.data));
+		if (retval < 0) {
+			dev_err(&fwu->rmi4_data->i2c_client->dev,
+				"%s: Failed to read flash status\n",
+				__func__);
+			return -EIO;
+		}
+		*status = flash_control.status;
 	}
 	return 0;
 }
@@ -492,22 +638,27 @@
 				__func__);
 		return retval;
 	}
+
+	fwu->polling_mode = false;
+
 	return 0;
 }
 
 static int fwu_write_f34_command(unsigned char cmd)
 {
 	int retval;
+	struct f34_flash_control flash_control;
 
-	fwu->flash_control.data[0] = cmd;
+	flash_control.data[0] = cmd;
+	fwu->interrupt_flag = false;
 	retval = fwu->fn_ptr->write(fwu->rmi4_data,
-			fwu->addr_f34_flash_control,
-			fwu->flash_control.data,
-			sizeof(fwu->flash_control.data));
+			fwu_get_address(OFFSET_FLASH_CONTROL),
+			flash_control.data,
+			sizeof(flash_control.data));
 	if (retval < 0) {
 		dev_err(&fwu->rmi4_data->i2c_client->dev,
 				"%s: Failed to write command 0x%02x\n",
-				__func__, fwu->flash_control.data[0]);
+				__func__, flash_control.data[0]);
 		return retval;
 	}
 	return 0;
@@ -518,18 +669,21 @@
 	int count = 0;
 	int timeout_count = ((timeout_ms * 1000) / SLEEP_TIME_US) + 1;
 	do {
-		#if POLLING_MODE
-		fwu_read_f34_flash_status();
-		#endif
-		if (fwu->flash_control.command == 0x00)
+		if (fwu->interrupt_flag)
 			return 0;
-
-		usleep_range(SLEEP_TIME_US, SLEEP_TIME_US + 100);
+		if (fwu->polling_mode)
+			if (fwu->intr_mask & fwu_read_interrupt_status())
+				return 0;
+		usleep_range(SLEEP_TIME_US, SLEEP_TIME_US + 1);
 	} while (count++ < timeout_count);
 
-	fwu_read_f34_flash_status();
-	if (fwu->flash_control.command == 0x00)
+	if (fwu->intr_mask & fwu_read_interrupt_status()) {
+		fwu->polling_mode = true;
+		dev_info(&fwu->rmi4_data->i2c_client->dev,
+			"%s: Switch to polling mode\n",
+			__func__);
 		return 0;
+	}
 
 	dev_err(&fwu->rmi4_data->i2c_client->dev,
 			"%s: Timed out waiting for idle status\n",
@@ -538,7 +692,7 @@
 	return -ETIMEDOUT;
 }
 
-static enum flash_area fwu_go_nogo(struct image_header *header)
+static enum flash_area fwu_go_nogo(void)
 {
 	int retval = 0;
 	int index = 0;
@@ -554,30 +708,53 @@
 	enum flash_area flash_area = NONE;
 	struct i2c_client *i2c_client = fwu->rmi4_data->i2c_client;
 	struct f01_device_status f01_device_status;
+	struct image_content *img = &fwu->image_content;
 
 	if (fwu->force_update) {
 		flash_area = UI_FIRMWARE;
 		goto exit;
 	}
 
-	if (header->is_contain_build_info) {
+	if (img->is_contain_build_info) {
 		/* if package id does not match, do not update firmware */
 		fwu->fn_ptr->read(fwu->rmi4_data,
 					fwu->f01_fd.query_base_addr + 17,
 					pkg_id,
 					sizeof(pkg_id));
 
-		if (header->package_id != ((pkg_id[1] << 8) | pkg_id[0])) {
+		if (img->package_id != ((pkg_id[1] << 8) | pkg_id[0])) {
 			flash_area = MISMATCH;
 			goto exit;
 		}
-		if (header->package_revision_id !=
+		if (img->package_revision_id !=
 				((pkg_id[3] << 8) | pkg_id[2])) {
 			flash_area = MISMATCH;
 			goto exit;
 		}
 	}
 
+	/* check firmware size */
+	if (fwu->fw_block_count*fwu->block_size != img->image_size) {
+		dev_err(&i2c_client->dev,
+			"%s: firmware size of device (%d) != .img (%d)\n",
+			__func__,
+			fwu->config_block_count * fwu->block_size,
+			img->image_size);
+		flash_area = NONE;
+		goto exit;
+	}
+
+	/* check config size */
+	if (fwu->config_block_count*fwu->block_size != img->config_size) {
+		dev_err(&i2c_client->dev,
+			"%s: config size of device (%d) != .img (%d)\n",
+			__func__,
+			fwu->config_block_count * fwu->block_size,
+			img->config_size);
+		flash_area = NONE;
+		goto exit;
+	}
+
 	retval = fwu_read_f01_device_status(&f01_device_status);
 	if (retval < 0) {
 		flash_area = NONE;
@@ -608,14 +785,21 @@
 	deviceFirmwareID = extract_uint(firmware_id);
 
 	/* .img firmware id */
-	if (header->is_contain_build_info) {
+	if (img->is_contain_build_info) {
 		dev_err(&i2c_client->dev,
 			"%s: Image option contains build info.\n",
 			__func__);
-		imageFirmwareID = header->firmware_id;
+		imageFirmwareID = img->firmware_id;
 	} else {
-		strptr = strnstr(fwu->firmware_name, "PR",
-				sizeof(fwu->firmware_name));
+		if (!fwu->image_name) {
+			dev_info(&i2c_client->dev,
+				"%s: Unknown image file name\n",
+				__func__);
+			flash_area = UI_FIRMWARE;
+			goto exit;
+		}
+		strptr = strnstr(fwu->image_name, "PR",
+				sizeof(fwu->image_name));
 		if (!strptr) {
 			dev_err(&i2c_client->dev,
 				"No valid PR number (PRxxxxxxx)" \
@@ -771,21 +955,41 @@
 		unsigned char command)
 {
 	int retval;
+	unsigned char flash_status;
 	unsigned char block_offset[] = {0, 0};
 	unsigned short block_num;
+	unsigned short addr_block_data = fwu_get_address(OFFSET_BLOCK_DATA);
+	unsigned short addr_block_num = fwu_get_address(OFFSET_BLOCK_NUMBER);
 	struct i2c_client *i2c_client = fwu->rmi4_data->i2c_client;
 #ifdef SHOW_PROGRESS
-	unsigned int progress = (command == CMD_WRITE_CONFIG_BLOCK) ?
-				10 : 100;
+	unsigned int progress;
+	unsigned char command_str[10];
+	switch (command) {
+	case CMD_WRITE_CONFIG_BLOCK:
+		progress = 10;
+		strlcpy(command_str, "config", 10);
+		break;
+	case CMD_WRITE_FW_BLOCK:
+		progress = 100;
+		strlcpy(command_str, "firmware", 10);
+		break;
+	case CMD_WRITE_LOCKDOWN_BLOCK:
+		progress = 1;
+		strlcpy(command_str, "lockdown", 10);
+		break;
+	default:
+		progress = 1;
+		strlcpy(command_str, "unknown", 10);
+		break;
+	}
 #endif
 
 	dev_dbg(&i2c_client->dev,
 			"%s: Start to update %s blocks\n",
 			__func__,
-			command == CMD_WRITE_CONFIG_BLOCK ?
-			"config" : "firmware");
+			command_str);
 	retval = fwu->fn_ptr->write(fwu->rmi4_data,
-			fwu->f34_fd.data_base_addr + BLOCK_NUMBER_OFFSET,
+			addr_block_num,
 			block_offset,
 			sizeof(block_offset));
 	if (retval < 0) {
@@ -801,12 +1005,11 @@
 			dev_info(&i2c_client->dev,
 					"%s: update %s %3d / %3d\n",
 					__func__,
-					command == CMD_WRITE_CONFIG_BLOCK ?
-					"config" : "firmware",
+					command_str,
 					block_num, block_cnt);
 #endif
 		retval = fwu->fn_ptr->write(fwu->rmi4_data,
-			fwu->f34_fd.data_base_addr + BLOCK_DATA_OFFSET,
+			addr_block_data,
 			block_ptr,
 			fwu->block_size);
 		if (retval < 0) {
@@ -832,21 +1035,28 @@
 			return retval;
 		}
 
-		if (fwu->flash_control.status != 0x00) {
+		#if CHECK_FLASH_BLOCK_STATUS
+		retval = fwu_read_f34_flash_status(&flash_status);
+		if (retval < 0) {
 			dev_err(&i2c_client->dev,
-					"%s: Flash block %d failed, status 0x%02X\n",
-					__func__, block_num, retval);
+					"%s: Failed to read flash status (block %d)\n",
+					__func__, block_num);
 			return retval;
 		}
-
+		if (flash_status != 0x00) {
+			dev_err(&i2c_client->dev,
+				"%s: Flash block %d failed, status 0x%02X\n",
+				__func__, block_num, flash_status);
+			return -EINVAL;
+		}
+		#endif
 		block_ptr += fwu->block_size;
 	}
 #ifdef SHOW_PROGRESS
 	dev_info(&i2c_client->dev,
 			"%s: update %s %3d / %3d\n",
 			__func__,
-			command == CMD_WRITE_CONFIG_BLOCK ?
-			"config" : "firmware",
+			command_str,
 			block_cnt, block_cnt);
 #endif
 	return 0;
@@ -864,6 +1074,12 @@
 		fwu->config_block_count, CMD_WRITE_CONFIG_BLOCK);
 }
 
+static int fwu_write_lockdown_block(void)
+{
+	return fwu_write_blocks((unsigned char *)fwu->lockdown_data,
+		fwu->lockdown_block_count, CMD_WRITE_LOCKDOWN_BLOCK);
+}
+
 static int fwu_write_bootloader_id(void)
 {
 	int retval;
@@ -874,7 +1090,7 @@
 			fwu->bootloader_id[1]);
 
 	retval = fwu->fn_ptr->write(fwu->rmi4_data,
-			fwu->f34_fd.data_base_addr + BLOCK_DATA_OFFSET,
+			fwu_get_address(OFFSET_BLOCK_DATA),
 			fwu->bootloader_id,
 			sizeof(fwu->bootloader_id));
 	if (retval < 0) {
@@ -887,7 +1103,7 @@
 	return 0;
 }
 
-static int fwu_enter_flash_prog(void)
+static int fwu_enter_flash_prog(bool force)
 {
 	int retval;
 	struct f01_device_status f01_device_status;
@@ -961,63 +1177,7 @@
 				__func__);
 		return retval;
 	}
-
-	return retval;
-}
-
-static int fwu_do_reflash(void)
-{
-	int retval;
-
-	retval = fwu_enter_flash_prog();
-	if (retval < 0)
-		return retval;
-
-	dev_dbg(&fwu->rmi4_data->i2c_client->dev,
-			"%s: Entered flash prog mode\n",
-			__func__);
-
-	retval = fwu_write_bootloader_id();
-	if (retval < 0)
-		return retval;
-
-	dev_dbg(&fwu->rmi4_data->i2c_client->dev,
-			"%s: Bootloader ID written\n",
-			__func__);
-
-	retval = fwu_write_f34_command(CMD_ERASE_ALL);
-	if (retval < 0)
-		return retval;
-
-	dev_dbg(&fwu->rmi4_data->i2c_client->dev,
-			"%s: Erase all command written\n",
-			__func__);
-
-	retval = fwu_wait_for_idle(ERASE_WAIT_MS);
-	if (retval < 0)
-		return retval;
-
-	if (fwu->flash_control.status != 0x00) {
-		dev_err(&fwu->rmi4_data->i2c_client->dev,
-				"%s: Erase all command failed, status 0x%02X\n",
-				__func__, retval);
-		return -1;
-	}
-
-	if (fwu->firmware_data) {
-		retval = fwu_write_firmware();
-		if (retval < 0)
-			return retval;
-		pr_notice("%s: Firmware programmed\n", __func__);
-	}
-
-	if (fwu->config_data) {
-		retval = fwu_write_configuration();
-		if (retval < 0)
-			return retval;
-		pr_notice("%s: Configuration programmed\n", __func__);
-	}
-
+	fwu->polling_mode = false;
 	return retval;
 }
 
@@ -1025,7 +1185,7 @@
 {
 	int retval;
 
-	retval = fwu_enter_flash_prog();
+	retval = fwu_enter_flash_prog(false);
 	if (retval < 0)
 		return retval;
 
@@ -1087,42 +1247,38 @@
 static int fwu_start_write_config(void)
 {
 	int retval;
-	struct image_header header;
+	int block_count;
 
 	switch (fwu->config_area) {
 	case UI_CONFIG_AREA:
+		block_count = fwu->config_block_count;
 		break;
 	case PERM_CONFIG_AREA:
 		if (!fwu->flash_properties.has_perm_config)
 			return -EINVAL;
+		block_count = fwu->perm_config_block_count;
 		break;
 	case BL_CONFIG_AREA:
 		if (!fwu->flash_properties.has_bl_config)
 			return -EINVAL;
+		block_count = fwu->bl_config_block_count;
 		break;
 	case DISP_CONFIG_AREA:
 		if (!fwu->flash_properties.has_display_config)
 			return -EINVAL;
+		block_count = fwu->disp_config_block_count;
 		break;
 	default:
 		return -EINVAL;
 	}
 
-	if (fwu->ext_data_source)
-		fwu->config_data = fwu->ext_data_source;
-	else
-		return -EINVAL;
-
-	if (fwu->config_area == UI_CONFIG_AREA) {
-		parse_header(&header, fwu->ext_data_source);
-
-		if (header.config_size) {
-			fwu->config_data = fwu->ext_data_source +
-					FW_IMAGE_OFFSET +
-					header.image_size;
-		} else {
-			return -EINVAL;
-		}
+	if (fwu->image_size == block_count*fwu->block_size) {
+		dev_info(&fwu->rmi4_data->i2c_client->dev,
+				"%s: write config from config file\n",
+				__func__);
+		fwu->config_data = fwu->data_buffer;
+	} else {
+		parse_header();
 	}
 
 	pr_notice("%s: Start of write config process\n", __func__);
@@ -1141,6 +1297,58 @@
 	return retval;
 }
 
+static int fwu_do_write_lockdown(bool reset)
+{
+	int retval;
+
+	pr_notice("%s: Start of lockdown process\n", __func__);
+
+	retval = fwu_enter_flash_prog(false);
+	if (retval < 0)
+		return retval;
+
+	dev_dbg(&fwu->rmi4_data->i2c_client->dev,
+			"%s: Entered flash prog mode\n",
+			__func__);
+
+	if (fwu->flash_properties.unlocked == 0) {
+		dev_err(&fwu->rmi4_data->i2c_client->dev,
+			"%s: Device has been locked!\n",
+			__func__);
+		if (reset)
+			goto exit;
+		else
+			return -EINVAL;
+	}
+
+	retval = fwu_write_lockdown_block();
+	if (retval < 0)
+		return retval;
+
+	dev_dbg(&fwu->rmi4_data->i2c_client->dev,
+			"%s:Lockdown device\n",
+			__func__);
+
+exit:
+	if (reset)
+		retval = fwu->rmi4_data->reset_device(fwu->rmi4_data);
+	else
+		retval = fwu_enter_flash_prog(true);
+
+	if (retval < 0)
+		return retval;
+
+	pr_notice("%s: End of lockdown process\n", __func__);
+
+	return retval;
+}
+
+static int fwu_start_write_lockdown(void)
+{
+	parse_header();
+	return fwu_do_write_lockdown(true);
+}
+
 static int fwu_do_read_config(void)
 {
 	int retval;
@@ -1149,14 +1357,6 @@
 	unsigned short block_count;
 	unsigned short index = 0;
 
-	retval = fwu_enter_flash_prog();
-	if (retval < 0)
-		goto exit;
-
-	dev_dbg(&fwu->rmi4_data->i2c_client->dev,
-			"%s: Entered flash prog mode\n",
-			__func__);
-
 	switch (fwu->config_area) {
 	case UI_CONFIG_AREA:
 		block_count = fwu->config_block_count;
@@ -1195,7 +1395,7 @@
 	block_offset[1] |= (fwu->config_area << 5);
 
 	retval = fwu->fn_ptr->write(fwu->rmi4_data,
-			fwu->f34_fd.data_base_addr + BLOCK_NUMBER_OFFSET,
+			fwu_get_address(OFFSET_BLOCK_NUMBER),
 			block_offset,
 			sizeof(block_offset));
 	if (retval < 0) {
@@ -1223,7 +1423,7 @@
 		}
 
 		retval = fwu->fn_ptr->read(fwu->rmi4_data,
-				fwu->f34_fd.data_base_addr + BLOCK_DATA_OFFSET,
+				fwu_get_address(OFFSET_BLOCK_DATA),
 				&fwu->read_config_buf[index],
 				fwu->block_size);
 		if (retval < 0) {
@@ -1237,7 +1437,71 @@
 	}
 
 exit:
-	fwu->rmi4_data->reset_device(fwu->rmi4_data);
+	return retval;
+}
+
+static int fwu_do_reflash(void)
+{
+	int retval;
+	unsigned char flash_status;
+
+	if (fwu->do_lockdown) {
+		retval = fwu_do_write_lockdown(false);
+		if (retval < 0)
+			dev_warn(&fwu->rmi4_data->i2c_client->dev,
+			"%s: Skip lockdown process.\n",
+			__func__);
+	}
+	retval = fwu_enter_flash_prog(false);
+	if (retval < 0)
+		return retval;
+	dev_dbg(&fwu->rmi4_data->i2c_client->dev,
+			"%s: Entered flash prog mode\n",
+			__func__);
+
+	retval = fwu_write_bootloader_id();
+	if (retval < 0)
+		return retval;
+
+	dev_dbg(&fwu->rmi4_data->i2c_client->dev,
+			"%s: Bootloader ID written\n",
+			__func__);
+
+	retval = fwu_write_f34_command(CMD_ERASE_ALL);
+	if (retval < 0)
+		return retval;
+
+	dev_dbg(&fwu->rmi4_data->i2c_client->dev,
+			"%s: Erase all command written\n",
+			__func__);
+
+	retval = fwu_wait_for_idle(ERASE_WAIT_MS);
+	if (retval < 0)
+		return retval;
+
+	retval = fwu_read_f34_flash_status(&flash_status);
+	if (retval < 0)
+		return retval;
+	if (flash_status != 0x00) {
+		dev_err(&fwu->rmi4_data->i2c_client->dev,
+				"%s: Erase all command failed, status 0x%02X\n",
+				__func__, flash_status);
+		return -EINVAL;
+	}
+
+	if (fwu->firmware_data) {
+		retval = fwu_write_firmware();
+		if (retval < 0)
+			return retval;
+		pr_notice("%s: Firmware programmed\n", __func__);
+	}
+
+	if (fwu->config_data) {
+		retval = fwu_write_configuration();
+		if (retval < 0)
+			return retval;
+		pr_notice("%s: Configuration programmed\n", __func__);
+	}
 
 	return retval;
 }
@@ -1245,45 +1509,47 @@
 static int fwu_start_reflash(void)
 {
 	int retval = 0;
-	struct image_header header;
-	const unsigned char *fw_image;
 	const struct firmware *fw_entry = NULL;
 	struct f01_device_status f01_device_status;
 	enum flash_area flash_area;
 
 	pr_notice("%s: Start of reflash process\n", __func__);
 
-	if (strnlen(fwu->rmi4_data->fw_image_name, NAME_BUFFER_SIZE) == 0) {
-		dev_err(&fwu->rmi4_data->i2c_client->dev,
-			"Firmware image name not given, skipping update\n");
-		return 0;
-	}
-
-	if (strnlen(fwu->rmi4_data->fw_image_name, NAME_BUFFER_SIZE) ==
-		NAME_BUFFER_SIZE) {
-		dev_err(&fwu->rmi4_data->i2c_client->dev,
-			"Firmware image name exceeds max length (%d), " \
-			"skipping update\n", NAME_BUFFER_SIZE);
-		return 0;
-	}
-
 	if (fwu->ext_data_source)
-		fw_image = fwu->ext_data_source;
+		dev_info(&fwu->rmi4_data->i2c_client->dev,
+				"%s Load .img file from commandline.\n",
+				__func__);
 	else {
-		snprintf(fwu->firmware_name, NAME_BUFFER_SIZE, "%s",
+		if (strnlen(fwu->rmi4_data->fw_image_name,
+				NAME_BUFFER_SIZE) == 0) {
+			dev_err(&fwu->rmi4_data->i2c_client->dev,
+				"Firmware image name not given, "\
+				"skipping update\n");
+			return 0;
+		}
+
+		if (strnlen(fwu->rmi4_data->fw_image_name, NAME_BUFFER_SIZE) ==
+			NAME_BUFFER_SIZE) {
+			dev_err(&fwu->rmi4_data->i2c_client->dev,
+				"Firmware image name exceeds max length " \
+				"(%d), skipping update\n", NAME_BUFFER_SIZE);
+			return 0;
+		}
+
+		snprintf(fwu->image_name, NAME_BUFFER_SIZE, "%s",
 			fwu->rmi4_data->fw_image_name);
 		dev_info(&fwu->rmi4_data->i2c_client->dev,
 			"%s: Requesting firmware image %s\n",
-			__func__, fwu->firmware_name);
+			__func__, fwu->image_name);
 
 		retval = request_firmware(&fw_entry,
-				fwu->firmware_name,
+				fwu->image_name,
 				&fwu->rmi4_data->i2c_client->dev);
 		if (retval != 0) {
 			dev_err(&fwu->rmi4_data->i2c_client->dev,
 					"%s: Firmware image %s not available\n",
 					__func__,
-					fwu->firmware_name);
+					fwu->image_name);
 			return -EINVAL;
 		}
 
@@ -1291,22 +1557,20 @@
 				"%s: Firmware image size = %d\n",
 				__func__, fw_entry->size);
 
-		fw_image = fw_entry->data;
+		fwu->data_buffer = fw_entry->data;
 	}
 
-	parse_header(&header, fw_image);
+	parse_header();
+	flash_area = fwu_go_nogo();
 
-	if (header.image_size)
-		fwu->firmware_data = fw_image + FW_IMAGE_OFFSET;
-	if (header.config_size) {
-		fwu->config_data = fw_image + FW_IMAGE_OFFSET +
-				header.image_size;
+	if (fwu->rmi4_data->sensor_sleep) {
+		dev_err(&fwu->rmi4_data->i2c_client->dev,
+			"%s: Sensor sleeping\n",
+			__func__);
+		retval = -ENODEV;
+		goto exit;
 	}
-
-	if (fwu->ext_data_source)
-		flash_area = UI_FIRMWARE;
-	else
-		flash_area = fwu_go_nogo(&header);
+	fwu->rmi4_data->stay_awake = true;
 
 	switch (flash_area) {
 	case NONE:
@@ -1326,14 +1590,14 @@
 		dev_err(&fwu->rmi4_data->i2c_client->dev,
 				"%s: Unknown flash area\n",
 				__func__);
+		retval = -EINVAL;
 		goto exit;
 	}
 
-	if (retval < 0) {
+	if (retval < 0)
 		dev_err(&fwu->rmi4_data->i2c_client->dev,
 				"%s: Failed to do reflash\n",
 				__func__);
-	}
 
 	/* reset device */
 	fwu_reset_device();
@@ -1353,8 +1617,6 @@
 		dev_info(&fwu->rmi4_data->i2c_client->dev,
 				"%s: Device is in flash prog mode 0x%02X\n",
 				__func__, f01_device_status.status_code);
-		retval = 0;
-		goto exit;
 	}
 
 exit:
@@ -1362,10 +1624,11 @@
 		release_firmware(fw_entry);
 
 	pr_notice("%s: End of reflash process\n", __func__);
+	fwu->rmi4_data->stay_awake = false;
 	return retval;
 }
 
-int synaptics_fw_updater(unsigned char *fw_data)
+int synaptics_fw_updater(void)
 {
 	int retval;
 
@@ -1383,7 +1646,6 @@
 		return -EBUSY;
 	}
 
-	fwu->ext_data_source = fw_data;
 	fwu->config_area = UI_CONFIG_AREA;
 
 	retval = fwu_start_reflash();
@@ -1421,12 +1683,13 @@
 			(const void *)buf,
 			count);
 
+	fwu->data_buffer = fwu->ext_data_source;
 	fwu->data_pos += count;
 
 	return count;
 }
 
-static ssize_t fwu_sysfs_fw_name_store(struct device *dev,
+static ssize_t fwu_sysfs_image_name_store(struct device *dev,
 		struct device_attribute *attr, const char *buf, size_t count)
 {
 	struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
@@ -1450,7 +1713,7 @@
 	return count;
 }
 
-static ssize_t fwu_sysfs_fw_name_show(struct device *dev,
+static ssize_t fwu_sysfs_image_name_show(struct device *dev,
 		struct device_attribute *attr, char *buf)
 {
 	if (strnlen(fwu->rmi4_data->fw_image_name, NAME_BUFFER_SIZE) > 0)
@@ -1476,9 +1739,11 @@
 		retval = -EINVAL;
 		goto exit;
 	}
+	if (LOCKDOWN)
+		fwu->do_lockdown = true;
 
 	fwu->force_update = true;
-	retval = synaptics_fw_updater(fwu->ext_data_source);
+	retval = synaptics_fw_updater();
 	if (retval < 0) {
 		dev_err(&rmi4_data->i2c_client->dev,
 				"%s: Failed to do reflash\n",
@@ -1491,6 +1756,8 @@
 exit:
 	kfree(fwu->ext_data_source);
 	fwu->ext_data_source = NULL;
+	fwu->force_update = FORCE_UPDATE;
+	fwu->do_lockdown = DO_LOCKDOWN;
 	return retval;
 }
 
@@ -1506,15 +1773,58 @@
 		goto exit;
 	}
 
+	if (input & LOCKDOWN) {
+		fwu->do_lockdown = true;
+		input &= ~LOCKDOWN;
+	}
+
+	if ((input != NORMAL) && (input != FORCE)) {
+		retval = -EINVAL;
+		goto exit;
+	}
+
+	if (input == FORCE)
+		fwu->force_update = true;
+
+	retval = synaptics_fw_updater();
+	if (retval < 0) {
+		dev_err(&rmi4_data->i2c_client->dev,
+				"%s: Failed to do reflash\n",
+				__func__);
+		goto exit;
+	}
+
+	retval = count;
+
+exit:
+	kfree(fwu->ext_data_source);
+	fwu->ext_data_source = NULL;
+	fwu->force_update = FORCE_UPDATE;
+	fwu->do_lockdown = DO_LOCKDOWN;
+	return retval;
+}
+
+static ssize_t fwu_sysfs_write_lockdown_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	int retval;
+	unsigned int input;
+	struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
+
+	if (sscanf(buf, "%u", &input) != 1) {
+		retval = -EINVAL;
+		goto exit;
+	}
+
 	if (input != 1) {
 		retval = -EINVAL;
 		goto exit;
 	}
 
-	retval = synaptics_fw_updater(fwu->ext_data_source);
+	retval = fwu_start_write_lockdown();
 	if (retval < 0) {
 		dev_err(&rmi4_data->i2c_client->dev,
-				"%s: Failed to do reflash\n",
+				"%s: Failed to write lockdown block\n",
 				__func__);
 		goto exit;
 	}
@@ -1524,6 +1834,8 @@
 exit:
 	kfree(fwu->ext_data_source);
 	fwu->ext_data_source = NULL;
+	fwu->force_update = FORCE_UPDATE;
+	fwu->do_lockdown = DO_LOCKDOWN;
 	return retval;
 }
 
@@ -1714,7 +2026,7 @@
 		unsigned char intr_mask)
 {
 	if (fwu->intr_mask & intr_mask)
-		fwu_read_f34_flash_status();
+		fwu->interrupt_flag = true;
 
 	return;
 }
@@ -1731,8 +2043,8 @@
 
 static struct device_attribute attrs[] = {
 	__ATTR(fw_name, S_IRUGO | S_IWUSR | S_IWGRP,
-			fwu_sysfs_fw_name_show,
-			fwu_sysfs_fw_name_store),
+			fwu_sysfs_image_name_show,
+			fwu_sysfs_image_name_store),
 	__ATTR(force_update_fw, S_IRUGO | S_IWUSR | S_IWGRP,
 			synaptics_rmi4_show_error,
 			fwu_sysfs_force_reflash_store),
@@ -1742,6 +2054,9 @@
 	__ATTR(writeconfig, S_IRUGO | S_IWUSR | S_IWGRP,
 			synaptics_rmi4_show_error,
 			fwu_sysfs_write_config_store),
+	__ATTR(writelockdown, S_IWUGO,
+			synaptics_rmi4_show_error,
+			fwu_sysfs_write_lockdown_store),
 	__ATTR(readconfig, S_IRUGO | S_IWUSR | S_IWGRP,
 			synaptics_rmi4_show_error,
 			fwu_sysfs_read_config_store),
@@ -1851,6 +2166,9 @@
 
 	fwu->initialized = true;
 	fwu->force_update = FORCE_UPDATE;
+	fwu->do_lockdown = DO_LOCKDOWN;
+	fwu->initialized = true;
+	fwu->polling_mode = false;
 
 	retval = sysfs_create_bin_file(&rmi4_data->i2c_client->dev.kobj,
 			&dev_attr_data);
@@ -1900,24 +2218,23 @@
 			msecs_to_jiffies(1000));
 #endif
 
-	init_completion(&remove_complete);
-
 	return 0;
 exit_free_ts_info:
 	debugfs_remove(temp);
 exit_remove_attrs:
-for (attr_count--; attr_count >= 0; attr_count--) {
-	sysfs_remove_file(&rmi4_data->input_dev->dev.kobj,
-			&attrs[attr_count].attr);
-}
+	for (attr_count--; attr_count >= 0; attr_count--) {
+		sysfs_remove_file(&rmi4_data->input_dev->dev.kobj,
+				&attrs[attr_count].attr);
+	}
 
-sysfs_remove_bin_file(&rmi4_data->input_dev->dev.kobj, &dev_attr_data);
+	sysfs_remove_bin_file(&rmi4_data->input_dev->dev.kobj, &dev_attr_data);
 
 exit_free_mem:
 	kfree(fwu->fn_ptr);
 
 exit_free_fwu:
 	kfree(fwu);
+	fwu = NULL;
 
 exit:
 	return 0;
@@ -1934,10 +2251,11 @@
 				&attrs[attr_count].attr);
 	}
 
+	kfree(fwu->read_config_buf);
 	kfree(fwu->fn_ptr);
 	kfree(fwu);
 
-	complete(&remove_complete);
+	complete(&fwu_remove_complete);
 
 	return;
 }
@@ -1957,7 +2275,7 @@
 			synaptics_rmi4_fwu_init,
 			synaptics_rmi4_fwu_remove,
 			synaptics_rmi4_fwu_attn);
-	wait_for_completion(&remove_complete);
+	wait_for_completion(&fwu_remove_complete);
 	return;
 }
 
diff --git a/drivers/input/touchscreen/synaptics_i2c_rmi4.c b/drivers/input/touchscreen/synaptics_i2c_rmi4.c
index 0b234ce..f8ab5f4 100644
--- a/drivers/input/touchscreen/synaptics_i2c_rmi4.c
+++ b/drivers/input/touchscreen/synaptics_i2c_rmi4.c
@@ -93,6 +93,8 @@
 #define RMI4_I2C_LPM_LOAD_UA	10
 
 #define RMI4_GPIO_SLEEP_LOW_US 10000
+#define F12_FINGERS_TO_SUPPORT 10
+#define MAX_F11_TOUCH_WIDTH 15
 
 static int synaptics_rmi4_i2c_read(struct synaptics_rmi4_data *rmi4_data,
 		unsigned short addr, unsigned char *data,
@@ -168,6 +170,131 @@
 	};
 };
 
+struct synaptics_rmi4_f12_query_5 {
+	union {
+		struct {
+			unsigned char size_of_query6;
+			struct {
+				unsigned char ctrl0_is_present:1;
+				unsigned char ctrl1_is_present:1;
+				unsigned char ctrl2_is_present:1;
+				unsigned char ctrl3_is_present:1;
+				unsigned char ctrl4_is_present:1;
+				unsigned char ctrl5_is_present:1;
+				unsigned char ctrl6_is_present:1;
+				unsigned char ctrl7_is_present:1;
+			} __packed;
+			struct {
+				unsigned char ctrl8_is_present:1;
+				unsigned char ctrl9_is_present:1;
+				unsigned char ctrl10_is_present:1;
+				unsigned char ctrl11_is_present:1;
+				unsigned char ctrl12_is_present:1;
+				unsigned char ctrl13_is_present:1;
+				unsigned char ctrl14_is_present:1;
+				unsigned char ctrl15_is_present:1;
+			} __packed;
+			struct {
+				unsigned char ctrl16_is_present:1;
+				unsigned char ctrl17_is_present:1;
+				unsigned char ctrl18_is_present:1;
+				unsigned char ctrl19_is_present:1;
+				unsigned char ctrl20_is_present:1;
+				unsigned char ctrl21_is_present:1;
+				unsigned char ctrl22_is_present:1;
+				unsigned char ctrl23_is_present:1;
+			} __packed;
+			struct {
+				unsigned char ctrl24_is_present:1;
+				unsigned char ctrl25_is_present:1;
+				unsigned char ctrl26_is_present:1;
+				unsigned char ctrl27_is_present:1;
+				unsigned char ctrl28_is_present:1;
+				unsigned char ctrl29_is_present:1;
+				unsigned char ctrl30_is_present:1;
+				unsigned char ctrl31_is_present:1;
+			} __packed;
+		};
+		unsigned char data[5];
+	};
+};
+
+struct synaptics_rmi4_f12_query_8 {
+	union {
+		struct {
+			unsigned char size_of_query9;
+			struct {
+				unsigned char data0_is_present:1;
+				unsigned char data1_is_present:1;
+				unsigned char data2_is_present:1;
+				unsigned char data3_is_present:1;
+				unsigned char data4_is_present:1;
+				unsigned char data5_is_present:1;
+				unsigned char data6_is_present:1;
+				unsigned char data7_is_present:1;
+			} __packed;
+			struct {
+				unsigned char data8_is_present:1;
+				unsigned char data9_is_present:1;
+				unsigned char data10_is_present:1;
+				unsigned char data11_is_present:1;
+				unsigned char data12_is_present:1;
+				unsigned char data13_is_present:1;
+				unsigned char data14_is_present:1;
+				unsigned char data15_is_present:1;
+			} __packed;
+		};
+		unsigned char data[3];
+	};
+};
+
+struct synaptics_rmi4_f12_ctrl_8 {
+	union {
+		struct {
+			unsigned char max_x_coord_lsb;
+			unsigned char max_x_coord_msb;
+			unsigned char max_y_coord_lsb;
+			unsigned char max_y_coord_msb;
+			unsigned char rx_pitch_lsb;
+			unsigned char rx_pitch_msb;
+			unsigned char tx_pitch_lsb;
+			unsigned char tx_pitch_msb;
+			unsigned char low_rx_clip;
+			unsigned char high_rx_clip;
+			unsigned char low_tx_clip;
+			unsigned char high_tx_clip;
+			unsigned char num_of_rx;
+			unsigned char num_of_tx;
+		};
+		unsigned char data[14];
+	};
+};
+
+struct synaptics_rmi4_f12_ctrl_23 {
+	union {
+		struct {
+			unsigned char obj_type_enable;
+			unsigned char max_reported_objects;
+		};
+		unsigned char data[2];
+	};
+};
+
+struct synaptics_rmi4_f12_finger_data {
+	unsigned char object_type_and_status;
+	unsigned char x_lsb;
+	unsigned char x_msb;
+	unsigned char y_lsb;
+	unsigned char y_msb;
+#ifdef REPORT_2D_Z
+	unsigned char z;
+#endif
+#ifdef REPORT_2D_W
+	unsigned char wx;
+	unsigned char wy;
+#endif
+};
+
 struct synaptics_rmi4_f1a_query {
 	union {
 		struct {
@@ -223,6 +350,13 @@
 	struct synaptics_rmi4_f1a_control button_control;
 };
 
+struct synaptics_rmi4_f12_extra_data {
+	unsigned char data1_offset;
+	unsigned char data15_offset;
+	unsigned char data15_size;
+	unsigned char data15_data[(F12_FINGERS_TO_SUPPORT + 7) / 8];
+};
+
 struct synaptics_rmi4_exp_fn {
 	enum exp_fn fn_type;
 	bool inserted;
@@ -360,8 +494,8 @@
 	retval = synaptics_rmi4_reset_device(rmi4_data);
 	if (retval < 0) {
 		dev_err(dev,
-				"%s: Failed to issue reset command, error = %d\n",
-				__func__, retval);
+			"%s: Failed to issue reset command, error = %d\n",
+			__func__, retval);
 		return retval;
 	}
 
@@ -817,10 +951,7 @@
 	if (!touch_count)
 		input_mt_sync(rmi4_data->input_dev);
 #else
-	/* sync after groups of events */
-	#ifdef KERNEL_ABOVE_3_7
-	input_mt_sync_frame(rmi4_data->input_dev);
-	#endif
+	input_mt_report_pointer_emulation(rmi4_data->input_dev, false);
 #endif
 
 	input_sync(rmi4_data->input_dev);
@@ -828,6 +959,125 @@
 	return touch_count;
 }
 
+ /**
+ * synaptics_rmi4_f12_abs_report()
+ *
+ * Called by synaptics_rmi4_report_touch() when valid Function $12
+ * finger data has been detected.
+ *
+ * This function reads the Function $12 data registers, determines the
+ * status of each finger supported by the Function, processes any
+ * necessary coordinate manipulation, reports the finger data to
+ * the input subsystem, and returns the number of fingers detected.
+ */
+static int synaptics_rmi4_f12_abs_report(struct synaptics_rmi4_data *rmi4_data,
+		struct synaptics_rmi4_fn *fhandler)
+{
+	int retval;
+	unsigned char touch_count = 0; /* number of touch points */
+	unsigned char finger;
+	unsigned char fingers_to_process;
+	unsigned char finger_status;
+	unsigned char size_of_2d_data;
+	unsigned short data_addr;
+	int x;
+	int y;
+	int wx;
+	int wy;
+	struct synaptics_rmi4_f12_extra_data *extra_data;
+	struct synaptics_rmi4_f12_finger_data *data;
+	struct synaptics_rmi4_f12_finger_data *finger_data;
+
+	fingers_to_process = fhandler->num_of_data_points;
+	data_addr = fhandler->full_addr.data_base;
+	extra_data = (struct synaptics_rmi4_f12_extra_data *)fhandler->extra;
+	size_of_2d_data = sizeof(struct synaptics_rmi4_f12_finger_data);
+
+	retval = synaptics_rmi4_i2c_read(rmi4_data,
+			data_addr + extra_data->data1_offset,
+			(unsigned char *)fhandler->data,
+			fingers_to_process * size_of_2d_data);
+	if (retval < 0)
+		return 0;
+
+	data = (struct synaptics_rmi4_f12_finger_data *)fhandler->data;
+
+	for (finger = 0; finger < fingers_to_process; finger++) {
+		finger_data = data + finger;
+		finger_status = finger_data->object_type_and_status & MASK_2BIT;
+
+		/*
+		 * Each 2-bit finger status field represents the following:
+		 * 00 = finger not present
+		 * 01 = finger present and data accurate
+		 * 10 = finger present but data may be inaccurate
+		 * 11 = reserved
+		 */
+#ifdef TYPE_B_PROTOCOL
+		input_mt_slot(rmi4_data->input_dev, finger);
+		input_mt_report_slot_state(rmi4_data->input_dev,
+				MT_TOOL_FINGER, finger_status != 0);
+#endif
+
+		if (finger_status) {
+			x = (finger_data->x_msb << 8) | (finger_data->x_lsb);
+			y = (finger_data->y_msb << 8) | (finger_data->y_lsb);
+#ifdef REPORT_2D_W
+			wx = finger_data->wx;
+			wy = finger_data->wy;
+#endif
+
+			if (rmi4_data->board->x_flip)
+				x = rmi4_data->sensor_max_x - x;
+			if (rmi4_data->board->y_flip)
+				y = rmi4_data->sensor_max_y - y;
+
+			dev_dbg(&rmi4_data->i2c_client->dev,
+					"%s: Finger %d:\n"
+					"status = 0x%02x\n"
+					"x = %d\n"
+					"y = %d\n"
+					"wx = %d\n"
+					"wy = %d\n",
+					__func__, finger,
+					finger_status,
+					x, y, wx, wy);
+
+			input_report_key(rmi4_data->input_dev,
+					BTN_TOUCH, 1);
+			input_report_key(rmi4_data->input_dev,
+					BTN_TOOL_FINGER, 1);
+			input_report_abs(rmi4_data->input_dev,
+					ABS_MT_POSITION_X, x);
+			input_report_abs(rmi4_data->input_dev,
+					ABS_MT_POSITION_Y, y);
+#ifdef REPORT_2D_W
+			input_report_abs(rmi4_data->input_dev,
+					ABS_MT_TOUCH_MAJOR, max(wx, wy));
+			input_report_abs(rmi4_data->input_dev,
+					ABS_MT_TOUCH_MINOR, min(wx, wy));
+#endif
+#ifndef TYPE_B_PROTOCOL
+			input_mt_sync(rmi4_data->input_dev);
+#endif
+			touch_count++;
+		}
+	}
+
+	input_report_key(rmi4_data->input_dev,
+			BTN_TOUCH, touch_count > 0);
+	input_report_key(rmi4_data->input_dev,
+			BTN_TOOL_FINGER, touch_count > 0);
+#ifndef TYPE_B_PROTOCOL
+	if (!touch_count)
+		input_mt_sync(rmi4_data->input_dev);
+#endif
+	input_mt_report_pointer_emulation(rmi4_data->input_dev, false);
+	input_sync(rmi4_data->input_dev);
+
+	return touch_count;
+}
+
 static void synaptics_rmi4_f1a_report(struct synaptics_rmi4_data *rmi4_data,
 		struct synaptics_rmi4_fn *fhandler)
 {
@@ -955,6 +1205,16 @@
 			rmi4_data->fingers_on_2d = false;
 		break;
 
+	case SYNAPTICS_RMI4_F12:
+		touch_count_2d = synaptics_rmi4_f12_abs_report(rmi4_data,
+				fhandler);
+
+		if (touch_count_2d)
+			rmi4_data->fingers_on_2d = true;
+		else
+			rmi4_data->fingers_on_2d = false;
+		break;
+
 	case SYNAPTICS_RMI4_F1A:
 		synaptics_rmi4_f1a_report(rmi4_data, fhandler);
 		break;
@@ -1045,6 +1305,7 @@
 	return IRQ_HANDLED;
 }
 
+#ifdef CONFIG_OF
 static int synaptics_rmi4_parse_dt(struct device *dev,
 				struct synaptics_rmi4_platform_data *rmi4_pdata)
 {
@@ -1127,6 +1388,13 @@
 	}
 	return 0;
 }
+#else
+static inline int synaptics_rmi4_parse_dt(struct device *dev,
+				struct synaptics_rmi4_platform_data *rmi4_pdata)
+{
+	return 0;
+}
+#endif
 
  /**
  * synaptics_rmi4_irq_enable()
@@ -1237,6 +1505,8 @@
 			rmi4_data->sensor_max_x,
 			rmi4_data->sensor_max_y);
 
+	rmi4_data->max_touch_width = MAX_F11_TOUCH_WIDTH;
+
 	fhandler->intr_reg_num = (intr_count + 7) / 8;
 	if (fhandler->intr_reg_num != 0)
 		fhandler->intr_reg_num -= 1;
@@ -1257,6 +1527,212 @@
 	return retval;
 }
 
+static int synaptics_rmi4_f12_set_enables(struct synaptics_rmi4_data *rmi4_data,
+		unsigned short ctrl28)
+{
+	int retval;
+	static unsigned short ctrl_28_address;
+
+	if (ctrl28)
+		ctrl_28_address = ctrl28;
+
+	retval = synaptics_rmi4_i2c_write(rmi4_data,
+			ctrl_28_address,
+			&rmi4_data->report_enable,
+			sizeof(rmi4_data->report_enable));
+	if (retval < 0)
+		return retval;
+
+	return retval;
+}
+
+ /**
+ * synaptics_rmi4_f12_init()
+ *
+ * Called by synaptics_rmi4_query_device().
+ *
+ * This funtion parses information from the Function 12 registers and
+ * determines the number of fingers supported, offset to the data1
+ * register, x and y data ranges, offset to the associated interrupt
+ * status register, interrupt bit mask, and allocates memory resources
+ * for finger data acquisition.
+ */
+static int synaptics_rmi4_f12_init(struct synaptics_rmi4_data *rmi4_data,
+		struct synaptics_rmi4_fn *fhandler,
+		struct synaptics_rmi4_fn_desc *fd,
+		unsigned int intr_count)
+{
+	int retval;
+	unsigned char ii;
+	unsigned char intr_offset;
+	unsigned char size_of_2d_data;
+	unsigned char size_of_query8;
+	unsigned char ctrl_8_offset;
+	unsigned char ctrl_23_offset;
+	unsigned char ctrl_28_offset;
+	unsigned char num_of_fingers;
+	struct synaptics_rmi4_f12_extra_data *extra_data;
+	struct synaptics_rmi4_f12_query_5 query_5;
+	struct synaptics_rmi4_f12_query_8 query_8;
+	struct synaptics_rmi4_f12_ctrl_8 ctrl_8;
+	struct synaptics_rmi4_f12_ctrl_23 ctrl_23;
+
+	fhandler->fn_number = fd->fn_number;
+	fhandler->num_of_data_sources = fd->intr_src_count;
+	fhandler->extra = kmalloc(sizeof(*extra_data), GFP_KERNEL);
+	extra_data = (struct synaptics_rmi4_f12_extra_data *)fhandler->extra;
+	size_of_2d_data = sizeof(struct synaptics_rmi4_f12_finger_data);
+
+	retval = synaptics_rmi4_i2c_read(rmi4_data,
+			fhandler->full_addr.query_base + 5,
+			query_5.data,
+			sizeof(query_5.data));
+	if (retval < 0)
+		return retval;
+
+	ctrl_8_offset = query_5.ctrl0_is_present +
+			query_5.ctrl1_is_present +
+			query_5.ctrl2_is_present +
+			query_5.ctrl3_is_present +
+			query_5.ctrl4_is_present +
+			query_5.ctrl5_is_present +
+			query_5.ctrl6_is_present +
+			query_5.ctrl7_is_present;
+
+	ctrl_23_offset = ctrl_8_offset +
+			query_5.ctrl8_is_present +
+			query_5.ctrl9_is_present +
+			query_5.ctrl10_is_present +
+			query_5.ctrl11_is_present +
+			query_5.ctrl12_is_present +
+			query_5.ctrl13_is_present +
+			query_5.ctrl14_is_present +
+			query_5.ctrl15_is_present +
+			query_5.ctrl16_is_present +
+			query_5.ctrl17_is_present +
+			query_5.ctrl18_is_present +
+			query_5.ctrl19_is_present +
+			query_5.ctrl20_is_present +
+			query_5.ctrl21_is_present +
+			query_5.ctrl22_is_present;
+
+	ctrl_28_offset = ctrl_23_offset +
+			query_5.ctrl23_is_present +
+			query_5.ctrl24_is_present +
+			query_5.ctrl25_is_present +
+			query_5.ctrl26_is_present +
+			query_5.ctrl27_is_present;
+
+	retval = synaptics_rmi4_i2c_read(rmi4_data,
+			fhandler->full_addr.ctrl_base + ctrl_23_offset,
+			ctrl_23.data,
+			sizeof(ctrl_23.data));
+	if (retval < 0)
+		return retval;
+
+	/* Maximum number of fingers supported */
+	fhandler->num_of_data_points = min(ctrl_23.max_reported_objects,
+			(unsigned char)F12_FINGERS_TO_SUPPORT);
+
+	num_of_fingers = fhandler->num_of_data_points;
+	rmi4_data->num_of_fingers = num_of_fingers;
+
+	retval = synaptics_rmi4_i2c_read(rmi4_data,
+			fhandler->full_addr.query_base + 7,
+			&size_of_query8,
+			sizeof(size_of_query8));
+	if (retval < 0)
+		return retval;
+
+	retval = synaptics_rmi4_i2c_read(rmi4_data,
+			fhandler->full_addr.query_base + 8,
+			query_8.data,
+			size_of_query8);
+	if (retval < 0)
+		return retval;
+
+	/* Determine the presence of the Data0 register */
+	extra_data->data1_offset = query_8.data0_is_present;
+
+	if ((size_of_query8 >= 3) && (query_8.data15_is_present)) {
+		extra_data->data15_offset = query_8.data0_is_present +
+				query_8.data1_is_present +
+				query_8.data2_is_present +
+				query_8.data3_is_present +
+				query_8.data4_is_present +
+				query_8.data5_is_present +
+				query_8.data6_is_present +
+				query_8.data7_is_present +
+				query_8.data8_is_present +
+				query_8.data9_is_present +
+				query_8.data10_is_present +
+				query_8.data11_is_present +
+				query_8.data12_is_present +
+				query_8.data13_is_present +
+				query_8.data14_is_present;
+		extra_data->data15_size = (num_of_fingers + 7) / 8;
+	} else {
+		extra_data->data15_size = 0;
+	}
+
+	rmi4_data->report_enable = RPT_DEFAULT;
+#ifdef REPORT_2D_Z
+	rmi4_data->report_enable |= RPT_Z;
+#endif
+#ifdef REPORT_2D_W
+	rmi4_data->report_enable |= (RPT_WX | RPT_WY);
+#endif
+
+	retval = synaptics_rmi4_f12_set_enables(rmi4_data,
+			fhandler->full_addr.ctrl_base + ctrl_28_offset);
+	if (retval < 0)
+		return retval;
+
+	retval = synaptics_rmi4_i2c_read(rmi4_data,
+			fhandler->full_addr.ctrl_base + ctrl_8_offset,
+			ctrl_8.data,
+			sizeof(ctrl_8.data));
+	if (retval < 0)
+		return retval;
+
+	/* Maximum x and y */
+	rmi4_data->sensor_max_x =
+			((unsigned short)ctrl_8.max_x_coord_lsb << 0) |
+			((unsigned short)ctrl_8.max_x_coord_msb << 8);
+	rmi4_data->sensor_max_y =
+			((unsigned short)ctrl_8.max_y_coord_lsb << 0) |
+			((unsigned short)ctrl_8.max_y_coord_msb << 8);
+	dev_dbg(&rmi4_data->i2c_client->dev,
+			"%s: Function %02x max x = %d max y = %d\n",
+			__func__, fhandler->fn_number,
+			rmi4_data->sensor_max_x,
+			rmi4_data->sensor_max_y);
+
+	rmi4_data->num_of_rx = ctrl_8.num_of_rx;
+	rmi4_data->num_of_tx = ctrl_8.num_of_tx;
+	rmi4_data->max_touch_width = max(rmi4_data->num_of_rx,
+			rmi4_data->num_of_tx);
+
+	fhandler->intr_reg_num = (intr_count + 7) / 8;
+	if (fhandler->intr_reg_num != 0)
+		fhandler->intr_reg_num -= 1;
+
+	/* Set an enable bit for each data source */
+	intr_offset = intr_count % 8;
+	fhandler->intr_mask = 0;
+	for (ii = intr_offset;
+			ii < ((fd->intr_src_count & MASK_3BIT) +
+			intr_offset);
+			ii++)
+		fhandler->intr_mask |= 1 << ii;
+
+	/* Allocate memory for finger data storage space */
+	fhandler->data_size = num_of_fingers * size_of_2d_data;
+	fhandler->data = kmalloc(fhandler->data_size, GFP_KERNEL);
+
+	return retval;
+}
+
 static int synaptics_rmi4_f1a_alloc_mem(struct synaptics_rmi4_data *rmi4_data,
 		struct synaptics_rmi4_fn *fhandler)
 {
@@ -1601,6 +2077,26 @@
 					return retval;
 				break;
 
+			case SYNAPTICS_RMI4_F12:
+				if (rmi_fd.intr_src_count == 0)
+					break;
+
+				retval = synaptics_rmi4_alloc_fh(&fhandler,
+						&rmi_fd, page_number);
+				if (retval < 0) {
+					dev_err(&rmi4_data->i2c_client->dev,
+							"%s: Failed to alloc for F%d\n",
+							__func__,
+							rmi_fd.fn_number);
+					return retval;
+				}
+
+				retval = synaptics_rmi4_f12_init(rmi4_data,
+						fhandler, &rmi_fd, intr_count);
+				if (retval < 0)
+					return retval;
+				break;
+
 			case SYNAPTICS_RMI4_F1A:
 				if (rmi_fd.intr_src_count == 0)
 					break;
@@ -1744,6 +2240,7 @@
 {
 	int retval;
 	struct synaptics_rmi4_fn *fhandler;
+	struct synaptics_rmi4_fn *next_fhandler;
 	struct synaptics_rmi4_device_info *rmi;
 
 	rmi = &(rmi4_data->rmi4_mod_info);
@@ -1757,11 +2254,14 @@
 	}
 
 	if (!list_empty(&rmi->support_fn_list)) {
-		list_for_each_entry(fhandler, &rmi->support_fn_list, link) {
+		list_for_each_entry_safe(fhandler, next_fhandler,
+					&rmi->support_fn_list, link) {
 			if (fhandler->fn_number == SYNAPTICS_RMI4_F1A)
 				synaptics_rmi4_f1a_kfree(fhandler);
-			else
+			else {
 				kfree(fhandler->data);
+				kfree(fhandler->extra);
+			}
 			kfree(fhandler);
 		}
 	}
@@ -2125,6 +2625,7 @@
 	unsigned char attr_count;
 	struct synaptics_rmi4_f1a_handle *f1a;
 	struct synaptics_rmi4_fn *fhandler;
+	struct synaptics_rmi4_fn *next_fhandler;
 	struct synaptics_rmi4_data *rmi4_data;
 	struct synaptics_rmi4_device_info *rmi;
 	struct synaptics_rmi4_platform_data *platform_data =
@@ -2260,7 +2761,10 @@
 #ifdef REPORT_2D_W
 	input_set_abs_params(rmi4_data->input_dev,
 			ABS_MT_TOUCH_MAJOR, 0,
-			MAX_ABS_MT_TOUCH_MAJOR, 0, 0);
+			rmi4_data->max_touch_width, 0, 0);
+	input_set_abs_params(rmi4_data->input_dev,
+			ABS_MT_TOUCH_MINOR, 0,
+			rmi4_data->max_touch_width, 0, 0);
 #endif
 
 #ifdef TYPE_B_PROTOCOL
@@ -2381,11 +2885,14 @@
 
 err_register_input:
 	if (!list_empty(&rmi->support_fn_list)) {
-		list_for_each_entry(fhandler, &rmi->support_fn_list, link) {
+		list_for_each_entry_safe(fhandler, next_fhandler,
+					&rmi->support_fn_list, link) {
 			if (fhandler->fn_number == SYNAPTICS_RMI4_F1A)
 				synaptics_rmi4_f1a_kfree(fhandler);
-			else
+			else {
 				kfree(fhandler->data);
+				kfree(fhandler->extra);
+			}
 			kfree(fhandler);
 		}
 	}
@@ -2421,6 +2928,7 @@
 {
 	unsigned char attr_count;
 	struct synaptics_rmi4_fn *fhandler;
+	struct synaptics_rmi4_fn *next_fhandler;
 	struct synaptics_rmi4_data *rmi4_data = i2c_get_clientdata(client);
 	struct synaptics_rmi4_device_info *rmi;
 
@@ -2444,11 +2952,14 @@
 	input_unregister_device(rmi4_data->input_dev);
 
 	if (!list_empty(&rmi->support_fn_list)) {
-		list_for_each_entry(fhandler, &rmi->support_fn_list, link) {
+		list_for_each_entry_safe(fhandler, next_fhandler,
+					&rmi->support_fn_list, link) {
 			if (fhandler->fn_number == SYNAPTICS_RMI4_F1A)
 				synaptics_rmi4_f1a_kfree(fhandler);
-			else
+			else {
 				kfree(fhandler->data);
+				kfree(fhandler->extra);
+			}
 			kfree(fhandler);
 		}
 	}
@@ -2591,6 +3102,11 @@
 			container_of(h, struct synaptics_rmi4_data,
 			early_suspend);
 
+	if (rmi4_data->stay_awake)
+		rmi4_data->staying_awake = true;
+	else
+		rmi4_data->staying_awake = false;
+
 	rmi4_data->touch_stopped = true;
 	wake_up(&rmi4_data->wait);
 	synaptics_rmi4_irq_enable(rmi4_data, false);
@@ -2617,6 +3133,9 @@
 			container_of(h, struct synaptics_rmi4_data,
 			early_suspend);
 
+	if (rmi4_data->staying_awake)
+		return;
+
 	if (rmi4_data->full_pm_cycle)
 		synaptics_rmi4_resume(&(rmi4_data->input_dev->dev));
 
@@ -2761,6 +3280,12 @@
 	struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev);
 	int retval;
 
+	if (rmi4_data->stay_awake) {
+		rmi4_data->staying_awake = true;
+		return 0;
+	} else
+		rmi4_data->staying_awake = false;
+
 	if (rmi4_data->suspended) {
 		dev_info(dev, "Already in suspend state\n");
 		return 0;
@@ -2812,6 +3337,9 @@
 	struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev);
 	int retval;
 
+	if (rmi4_data->staying_awake)
+		return 0;
+
 	if (!rmi4_data->suspended) {
 		dev_info(dev, "Already in awake state\n");
 		return 0;
diff --git a/drivers/input/touchscreen/synaptics_i2c_rmi4.h b/drivers/input/touchscreen/synaptics_i2c_rmi4.h
index 5f6d6ce..677a2fe 100644
--- a/drivers/input/touchscreen/synaptics_i2c_rmi4.h
+++ b/drivers/input/touchscreen/synaptics_i2c_rmi4.h
@@ -45,6 +45,7 @@
 
 #define SYNAPTICS_RMI4_F01 (0x01)
 #define SYNAPTICS_RMI4_F11 (0x11)
+#define SYNAPTICS_RMI4_F12 (0x12)
 #define SYNAPTICS_RMI4_F1A (0x1a)
 #define SYNAPTICS_RMI4_F34 (0x34)
 #define SYNAPTICS_RMI4_F54 (0x54)
@@ -69,7 +70,7 @@
 #define MASK_2BIT 0x03
 #define MASK_1BIT 0x01
 
-#define NAME_BUFFER_SIZE 128
+#define NAME_BUFFER_SIZE 256
 
 /*
  * struct synaptics_rmi4_fn_desc - function descriptor fields in PDT
@@ -85,9 +86,12 @@
 	unsigned char cmd_base_addr;
 	unsigned char ctrl_base_addr;
 	unsigned char data_base_addr;
-	unsigned char intr_src_count;
+	unsigned char intr_src_count:3;
+	unsigned char reserved_b3_b4:2;
+	unsigned char version:2;
+	unsigned char reserved_b7:1;
 	unsigned char fn_number;
-};
+} __packed;
 
 /*
  * synaptics_rmi4_fn_full_addr - full 16-bit base addresses
@@ -129,6 +133,7 @@
 	struct list_head link;
 	int data_size;
 	void *data;
+	void *extra;
 };
 
 /*
@@ -214,6 +219,8 @@
 	unsigned char num_of_rx;
 	unsigned char num_of_tx;
 	unsigned char num_of_fingers;
+	unsigned char max_touch_width;
+	unsigned char report_enable;
 	unsigned char intr_mask[MAX_INTR_REGISTERS];
 	unsigned short num_of_intr_regs;
 	unsigned short f01_query_base_addr;
@@ -232,6 +239,8 @@
 	bool fw_updating;
 	bool suspended;
 	wait_queue_head_t wait;
+	bool stay_awake;
+	bool staying_awake;
 	int (*i2c_read)(struct synaptics_rmi4_data *pdata, unsigned short addr,
 			unsigned char *data, unsigned short length);
 	int (*i2c_write)(struct synaptics_rmi4_data *pdata, unsigned short addr,
diff --git a/drivers/leds/leds-qpnp.c b/drivers/leds/leds-qpnp.c
index a1453ae..5a405be 100644
--- a/drivers/leds/leds-qpnp.c
+++ b/drivers/leds/leds-qpnp.c
@@ -95,6 +95,7 @@
 #define FLASH_LED_UNLOCK_SECURE(base)	(base + 0xD0)
 #define FLASH_LED_TORCH(base)		(base + 0xE4)
 #define FLASH_FAULT_DETECT(base)	(base + 0x51)
+#define FLASH_PERIPHERAL_SUBTYPE(base)	(base + 0x05)
 
 #define FLASH_MAX_LEVEL			0x4F
 #define TORCH_MAX_LEVEL			0x0F
@@ -119,13 +120,13 @@
 #define FLASH_ENABLE_MODULE_MASK	0x80
 #define FLASH_DISABLE_ALL		0x00
 #define FLASH_ENABLE_MASK		0xE0
-#define FLASH_ENABLE_LED_0		0x40
-#define FLASH_ENABLE_LED_1		0x20
+#define FLASH_ENABLE_LED_0		0xC0
+#define FLASH_ENABLE_LED_1		0xA0
 #define FLASH_INIT_MASK			0xE0
 #define	FLASH_SELFCHECK_ENABLE		0x80
 
 #define FLASH_STROBE_SW			0xC0
-#define FLASH_STROBE_HW			0xC4
+#define FLASH_STROBE_HW			0x04
 #define FLASH_STROBE_MASK		0xC7
 #define FLASH_LED_0_OUTPUT		0x80
 #define FLASH_LED_1_OUTPUT		0x40
@@ -144,6 +145,9 @@
 #define FLASH_UNLOCK_SECURE		0xA5
 #define FLASH_SECURE_MASK		0xFF
 
+#define FLASH_SUBTYPE_DUAL		0x01
+#define FLASH_SUBTYPE_SINGLE		0x02
+
 #define LED_TRIGGER_DEFAULT		"none"
 
 #define RGB_LED_SRC_SEL(base)		(base + 0x45)
@@ -366,6 +370,7 @@
  *  @trigger_flash - trigger flash
  *  @startup_dly - startup delay for flash
  *  @strobe_type - select between sw and hw strobe
+ *  @peripheral_subtype - module peripheral subtype
  *  @current_addr - address to write for current
  *  @second_addr - address of secondary flash to be written
  *  @safety_timer - enable safety timer or watchdog timer
@@ -385,6 +390,7 @@
 	u8	trigger_flash;
 	u8	startup_dly;
 	u8	strobe_type;
+	u8	peripheral_subtype;
 	u16	current_addr;
 	u16	second_addr;
 	bool	safety_timer;
@@ -720,6 +726,16 @@
 	if (regulator_on && led->flash_cfg->flash_on) {
 		for (i = 0; i < led->num_leds; i++) {
 			if (led_array[i].flash_cfg->flash_reg_get) {
+				rc = qpnp_led_masked_write(led,
+					FLASH_ENABLE_CONTROL(led->base),
+					FLASH_ENABLE_MASK,
+					FLASH_DISABLE_ALL);
+				if (rc) {
+					dev_err(&led->spmi_dev->dev,
+						"Enable reg write failed(%d)\n",
+						rc);
+				}
+
 				rc = regulator_disable(led_array[i].flash_cfg->\
 							flash_boost_reg);
 				if (rc) {
@@ -757,6 +773,13 @@
 
 regulator_turn_off:
 	if (led->flash_cfg->torch_on) {
+		rc = qpnp_led_masked_write(led,	FLASH_ENABLE_CONTROL(led->base),
+				FLASH_ENABLE_MODULE_MASK, FLASH_DISABLE_ALL);
+		if (rc) {
+			dev_err(&led->spmi_dev->dev,
+				"Enable reg write failed(%d)\n", rc);
+		}
+
 		rc = regulator_disable(led->flash_cfg->torch_boost_reg);
 		if (rc) {
 			dev_err(&led->spmi_dev->dev,
@@ -780,20 +803,43 @@
 		led->flash_cfg->current_prgm =
 			(val * FLASH_MAX_LEVEL / led->max_current);
 
-	led->flash_cfg->current_prgm =
-		led->flash_cfg->current_prgm >> FLASH_CURRENT_PRGM_SHIFT;
-	if (!led->flash_cfg->current_prgm)
-		led->flash_cfg->current_prgm = FLASH_CURRENT_PRGM_MIN;
-
 	/* Set led current */
 	if (val > 0) {
 		if (led->flash_cfg->torch_enable) {
-			rc = qpnp_torch_regulator_operate(led, true);
-			if (rc) {
-				dev_err(&led->spmi_dev->dev,
+			if (led->flash_cfg->peripheral_subtype ==
+							FLASH_SUBTYPE_DUAL) {
+				rc = qpnp_torch_regulator_operate(led, true);
+				if (rc) {
+					dev_err(&led->spmi_dev->dev,
 					"Torch regulator operate failed(%d)\n",
 					rc);
-				return rc;
+					return rc;
+				}
+			} else if (led->flash_cfg->peripheral_subtype ==
+							FLASH_SUBTYPE_SINGLE) {
+				rc = qpnp_flash_regulator_operate(led, true);
+				if (rc) {
+					dev_err(&led->spmi_dev->dev,
+					"Flash regulator operate failed(%d)\n",
+					rc);
+					goto error_flash_set;
+				}
+
+				/*
+				 * Write 0x80 to MODULE_ENABLE before writing
+				 * 0xE0 in order to avoid a hardware bug caused
+				 * by register value going from 0x00 to 0xE0.
+				 */
+				rc = qpnp_led_masked_write(led,
+					FLASH_ENABLE_CONTROL(led->base),
+					FLASH_ENABLE_MODULE_MASK,
+					FLASH_ENABLE_MODULE);
+				if (rc) {
+					dev_err(&led->spmi_dev->dev,
+						"Enable reg write failed(%d)\n",
+						rc);
+					return rc;
+				}
 			}
 
 			rc = qpnp_led_masked_write(led,
@@ -802,7 +848,7 @@
 			if (rc) {
 				dev_err(&led->spmi_dev->dev,
 					"Secure reg write failed(%d)\n", rc);
-				goto error_torch_set;
+				goto error_reg_write;
 			}
 
 			rc = qpnp_led_masked_write(led,
@@ -811,7 +857,7 @@
 			if (rc) {
 				dev_err(&led->spmi_dev->dev,
 					"Torch reg write failed(%d)\n", rc);
-				goto error_torch_set;
+				goto error_reg_write;
 			}
 
 			rc = qpnp_led_masked_write(led,
@@ -821,7 +867,7 @@
 			if (rc) {
 				dev_err(&led->spmi_dev->dev,
 					"Current reg write failed(%d)\n", rc);
-				goto error_torch_set;
+				goto error_reg_write;
 			}
 
 			rc = qpnp_led_masked_write(led,
@@ -832,7 +878,7 @@
 				dev_err(&led->spmi_dev->dev,
 					"2nd Current reg write failed(%d)\n",
 					rc);
-				goto error_torch_set;
+				goto error_reg_write;
 			}
 
 			qpnp_led_masked_write(led, FLASH_MAX_CURR(led->base),
@@ -842,16 +888,29 @@
 				dev_err(&led->spmi_dev->dev,
 					"Max current reg write failed(%d)\n",
 					rc);
-				goto error_torch_set;
+				goto error_reg_write;
 			}
 
 			rc = qpnp_led_masked_write(led,
 				FLASH_ENABLE_CONTROL(led->base),
-				FLASH_ENABLE_MASK, FLASH_ENABLE_MODULE);
+				FLASH_ENABLE_MASK,
+				led->flash_cfg->enable_module);
 			if (rc) {
 				dev_err(&led->spmi_dev->dev,
-					"Enable reg write failed(%d)\n", rc);
-				goto error_torch_set;
+					"Enable reg write failed(%d)\n",
+					rc);
+				goto error_reg_write;
+			}
+
+			rc = qpnp_led_masked_write(led,
+				FLASH_LED_STROBE_CTRL(led->base),
+				led->flash_cfg->trigger_flash,
+				led->flash_cfg->trigger_flash);
+			if (rc) {
+				dev_err(&led->spmi_dev->dev,
+					"LED %d strobe reg write failed(%d)\n",
+					led->id, rc);
+				goto error_reg_write;
 			}
 		} else {
 			rc = qpnp_flash_regulator_operate(led, true);
@@ -897,16 +956,19 @@
 				goto error_flash_set;
 			}
 
-			/* Write 0x80 to MODULE_ENABLE before writing 0xE0
-			 * in order to avoid reg value goes from 0x00 to
-			 * 0xE0. This causes a hardware bug.
+			/*
+			 * Write 0x80 to MODULE_ENABLE before writing
+			 * 0xE0 in order to avoid a hardware bug caused
+			 * by register value going from 0x00 to 0xE0.
 			 */
 			rc = qpnp_led_masked_write(led,
 				FLASH_ENABLE_CONTROL(led->base),
-				FLASH_ENABLE_MODULE_MASK, FLASH_ENABLE_MODULE);
+				FLASH_ENABLE_MODULE_MASK,
+				FLASH_ENABLE_MODULE);
 			if (rc) {
 				dev_err(&led->spmi_dev->dev,
-					"Enable reg write failed(%d)\n", rc);
+					"Enable reg write failed(%d)\n",
+					rc);
 				goto error_flash_set;
 			}
 
@@ -921,57 +983,45 @@
 			}
 
 			rc = qpnp_led_masked_write(led,
-				led->flash_cfg->second_addr,
-				FLASH_CURRENT_MASK,
-				led->flash_cfg->current_prgm);
-			if (rc) {
-				dev_err(&led->spmi_dev->dev,
-					"2nd Current reg write failed(%d)\n",
-					rc);
-				goto error_flash_set;
-			}
-
-			rc = qpnp_led_masked_write(led,
 				FLASH_ENABLE_CONTROL(led->base),
-				FLASH_ENABLE_MASK, FLASH_ENABLE_ALL);
+				led->flash_cfg->enable_module,
+				led->flash_cfg->enable_module);
 			if (rc) {
 				dev_err(&led->spmi_dev->dev,
 					"Enable reg write failed(%d)\n", rc);
 				goto error_flash_set;
 			}
-		}
 
-		if (!led->flash_cfg->strobe_type) {
-			rc = qpnp_led_masked_write(led,
-				FLASH_LED_STROBE_CTRL(led->base),
-				FLASH_STROBE_MASK, FLASH_STROBE_SW);
-			if (rc) {
-				dev_err(&led->spmi_dev->dev,
+			if (!led->flash_cfg->strobe_type) {
+				rc = qpnp_led_masked_write(led,
+					FLASH_LED_STROBE_CTRL(led->base),
+					led->flash_cfg->trigger_flash,
+					led->flash_cfg->trigger_flash);
+				if (rc) {
+					dev_err(&led->spmi_dev->dev,
 					"LED %d strobe reg write failed(%d)\n",
 					led->id, rc);
-				if (led->flash_cfg->torch_enable)
-					goto error_torch_set;
-				else
 					goto error_flash_set;
-			}
-		} else {
-			rc = qpnp_led_masked_write(led,
-				FLASH_LED_STROBE_CTRL(led->base),
-				FLASH_STROBE_MASK, FLASH_STROBE_HW);
-			if (rc) {
-				dev_err(&led->spmi_dev->dev,
+				}
+			} else {
+				rc = qpnp_led_masked_write(led,
+					FLASH_LED_STROBE_CTRL(led->base),
+					(led->flash_cfg->trigger_flash |
+					FLASH_STROBE_HW),
+					(led->flash_cfg->trigger_flash |
+					FLASH_STROBE_HW));
+				if (rc) {
+					dev_err(&led->spmi_dev->dev,
 					"LED %d strobe reg write failed(%d)\n",
 					led->id, rc);
-				if (led->flash_cfg->torch_enable)
-					goto error_torch_set;
-				else
 					goto error_flash_set;
+				}
 			}
 		}
 	} else {
 		rc = qpnp_led_masked_write(led,
 			FLASH_LED_STROBE_CTRL(led->base),
-			FLASH_STROBE_MASK,
+			led->flash_cfg->trigger_flash,
 			FLASH_DISABLE_ALL);
 		if (rc) {
 			dev_err(&led->spmi_dev->dev,
@@ -982,18 +1032,6 @@
 				goto error_flash_set;
 		}
 
-		rc = qpnp_led_masked_write(led, FLASH_ENABLE_CONTROL(led->base),
-			FLASH_ENABLE_MASK,
-			FLASH_DISABLE_ALL);
-		if (rc) {
-			dev_err(&led->spmi_dev->dev,
-				"Enable reg write failed(%d)\n", rc);
-			if (led->flash_cfg->torch_enable)
-				goto error_torch_set;
-			else
-				goto error_flash_set;
-		}
-
 		if (led->flash_cfg->torch_enable) {
 			rc = qpnp_led_masked_write(led,
 				FLASH_LED_UNLOCK_SECURE(led->base),
@@ -1014,14 +1052,40 @@
 				goto error_torch_set;
 			}
 
-			rc = qpnp_torch_regulator_operate(led, false);
-			if (rc) {
-				dev_err(&led->spmi_dev->dev,
-					"Torch regulator operate failed(%d)\n",
-					rc);
-				return rc;
+			if (led->flash_cfg->peripheral_subtype ==
+							FLASH_SUBTYPE_DUAL) {
+				rc = qpnp_torch_regulator_operate(led, false);
+				if (rc) {
+					dev_err(&led->spmi_dev->dev,
+						"Torch regulator operate failed(%d)\n",
+						rc);
+					return rc;
+				}
+			} else if (led->flash_cfg->peripheral_subtype ==
+							FLASH_SUBTYPE_SINGLE) {
+				rc = qpnp_flash_regulator_operate(led, false);
+				if (rc) {
+					dev_err(&led->spmi_dev->dev,
+						"Flash regulator operate failed(%d)\n",
+						rc);
+					return rc;
+				}
 			}
 		} else {
+			rc = qpnp_led_masked_write(led,
+				FLASH_ENABLE_CONTROL(led->base),
+				led->flash_cfg->enable_module &
+				~FLASH_ENABLE_MODULE_MASK,
+				FLASH_DISABLE_ALL);
+			if (rc) {
+				dev_err(&led->spmi_dev->dev,
+					"Enable reg write failed(%d)\n", rc);
+				if (led->flash_cfg->torch_enable)
+					goto error_torch_set;
+				else
+					goto error_flash_set;
+			}
+
 			rc = qpnp_flash_regulator_operate(led, false);
 			if (rc) {
 				dev_err(&led->spmi_dev->dev,
@@ -1036,6 +1100,10 @@
 
 	return 0;
 
+error_reg_write:
+	if (led->flash_cfg->peripheral_subtype == FLASH_SUBTYPE_SINGLE)
+		goto error_flash_set;
+
 error_torch_set:
 	error = qpnp_torch_regulator_operate(led, false);
 	if (error) {
@@ -2465,61 +2533,85 @@
 		return -ENOMEM;
 	}
 
-	if (led->id == QPNP_ID_FLASH1_LED0) {
-		led->flash_cfg->enable_module = FLASH_ENABLE_ALL;
-		led->flash_cfg->current_addr = FLASH_LED_0_CURR(led->base);
-		led->flash_cfg->second_addr = FLASH_LED_1_CURR(led->base);
-		led->flash_cfg->trigger_flash = FLASH_LED_0_OUTPUT;
-		if (!*reg_set) {
-			led->flash_cfg->flash_boost_reg =
-				regulator_get(&led->spmi_dev->dev,
-							"flash_boost");
-			if (IS_ERR(led->flash_cfg->flash_boost_reg)) {
-				rc = PTR_ERR(led->flash_cfg->flash_boost_reg);
-				dev_err(&led->spmi_dev->dev,
-					"Regulator get failed(%d)\n", rc);
-				goto error_get_flash_reg;
-			}
-			led->flash_cfg->flash_reg_get = true;
-			*reg_set = true;
-		} else
-			led->flash_cfg->flash_reg_get = false;
-	} else if (led->id == QPNP_ID_FLASH1_LED1) {
-		led->flash_cfg->enable_module = FLASH_ENABLE_ALL;
-		led->flash_cfg->current_addr = FLASH_LED_1_CURR(led->base);
-		led->flash_cfg->second_addr = FLASH_LED_0_CURR(led->base);
-		led->flash_cfg->trigger_flash = FLASH_LED_1_OUTPUT;
-		if (!*reg_set) {
-			led->flash_cfg->flash_boost_reg =
-					regulator_get(&led->spmi_dev->dev,
-								"flash_boost");
-			if (IS_ERR(led->flash_cfg->flash_boost_reg)) {
-				rc = PTR_ERR(led->flash_cfg->flash_boost_reg);
-				dev_err(&led->spmi_dev->dev,
-					"Regulator get failed(%d)\n", rc);
-				goto error_get_flash_reg;
-			}
-			led->flash_cfg->flash_reg_get = true;
-			*reg_set = true;
-		} else
-			led->flash_cfg->flash_reg_get = false;
-	} else {
-		dev_err(&led->spmi_dev->dev, "Unknown flash LED name given\n");
-		return -EINVAL;
+	rc = spmi_ext_register_readl(led->spmi_dev->ctrl, led->spmi_dev->sid,
+			FLASH_PERIPHERAL_SUBTYPE(led->base),
+			&led->flash_cfg->peripheral_subtype, 1);
+	if (rc) {
+		dev_err(&led->spmi_dev->dev,
+			"Unable to read from addr=%x, rc(%d)\n",
+			FLASH_PERIPHERAL_SUBTYPE(led->base), rc);
 	}
 
 	led->flash_cfg->torch_enable =
 		of_property_read_bool(node, "qcom,torch-enable");
 
-	if (led->flash_cfg->torch_enable) {
-		led->flash_cfg->torch_boost_reg =
-			regulator_get(&led->spmi_dev->dev, "torch_boost");
-		if (IS_ERR(led->flash_cfg->torch_boost_reg)) {
-			rc = PTR_ERR(led->flash_cfg->torch_boost_reg);
-			dev_err(&led->spmi_dev->dev,
-				"Torch regulator get failed(%d)\n", rc);
-			goto error_get_torch_reg;
+	if (led->id == QPNP_ID_FLASH1_LED0) {
+		led->flash_cfg->enable_module = FLASH_ENABLE_LED_0;
+		led->flash_cfg->current_addr = FLASH_LED_0_CURR(led->base);
+		led->flash_cfg->trigger_flash = FLASH_LED_0_OUTPUT;
+		if (!*reg_set) {
+			led->flash_cfg->flash_boost_reg =
+				regulator_get(&led->spmi_dev->dev,
+							"flash-boost");
+			if (IS_ERR(led->flash_cfg->flash_boost_reg)) {
+				rc = PTR_ERR(led->flash_cfg->flash_boost_reg);
+				dev_err(&led->spmi_dev->dev,
+					"Regulator get failed(%d)\n", rc);
+				goto error_get_flash_reg;
+			}
+			led->flash_cfg->flash_reg_get = true;
+			*reg_set = true;
+		} else
+			led->flash_cfg->flash_reg_get = false;
+
+		if (led->flash_cfg->torch_enable) {
+			led->flash_cfg->second_addr =
+						FLASH_LED_1_CURR(led->base);
 		}
+	} else if (led->id == QPNP_ID_FLASH1_LED1) {
+		led->flash_cfg->enable_module = FLASH_ENABLE_LED_1;
+		led->flash_cfg->current_addr = FLASH_LED_1_CURR(led->base);
+		led->flash_cfg->trigger_flash = FLASH_LED_1_OUTPUT;
+		if (!*reg_set) {
+			led->flash_cfg->flash_boost_reg =
+					regulator_get(&led->spmi_dev->dev,
+								"flash-boost");
+			if (IS_ERR(led->flash_cfg->flash_boost_reg)) {
+				rc = PTR_ERR(led->flash_cfg->flash_boost_reg);
+				dev_err(&led->spmi_dev->dev,
+					"Regulator get failed(%d)\n", rc);
+				goto error_get_flash_reg;
+			}
+			led->flash_cfg->flash_reg_get = true;
+			*reg_set = true;
+		} else
+			led->flash_cfg->flash_reg_get = false;
+
+		if (led->flash_cfg->torch_enable) {
+			led->flash_cfg->second_addr =
+						FLASH_LED_0_CURR(led->base);
+		}
+	} else {
+		dev_err(&led->spmi_dev->dev, "Unknown flash LED name given\n");
+		return -EINVAL;
+	}
+
+	if (led->flash_cfg->torch_enable) {
+		if (of_find_property(of_get_parent(node), "torch-boost-supply",
+									NULL)) {
+			led->flash_cfg->torch_boost_reg =
+				regulator_get(&led->spmi_dev->dev,
+								"torch-boost");
+			if (IS_ERR(led->flash_cfg->torch_boost_reg)) {
+				rc = PTR_ERR(led->flash_cfg->torch_boost_reg);
+				dev_err(&led->spmi_dev->dev,
+					"Torch regulator get failed(%d)\n", rc);
+				goto error_get_torch_reg;
+			}
+			led->flash_cfg->enable_module = FLASH_ENABLE_MODULE;
+		} else
+			led->flash_cfg->enable_module = FLASH_ENABLE_ALL;
+		led->flash_cfg->trigger_flash = FLASH_STROBE_SW;
 	}
 
 	rc = of_property_read_u32(node, "qcom,current", &val);
@@ -3018,7 +3110,7 @@
 			}
 		} else if (strncmp(led_label, "flash", sizeof("flash"))
 				== 0) {
-			if (!of_find_property(node, "flash_boost-supply", NULL))
+			if (!of_find_property(node, "flash-boost-supply", NULL))
 				regulator_probe = true;
 			rc = qpnp_get_config_flash(led, temp, &regulator_probe);
 			if (rc < 0) {
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.c
index 0840e30..b479857 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.c
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.c
@@ -23,8 +23,16 @@
 	struct msm_isp_buffer *buf;
 	uint32_t pingpong_bit = 0;
 	uint32_t bufq_handle = stream_info->bufq_handle;
-	uint32_t stats_pingpong_offset =
-		STATS_IDX(stream_info->stream_handle) +
+	uint32_t stats_pingpong_offset;
+
+	if (STATS_IDX(stream_info->stream_handle) >=
+			vfe_dev->hw_info->stats_hw_info->num_stats_type) {
+		pr_err("%s Invalid stats index %d", __func__,
+				STATS_IDX(stream_info->stream_handle));
+		return -EINVAL;
+	}
+
+	stats_pingpong_offset = STATS_IDX(stream_info->stream_handle) +
 		vfe_dev->hw_info->stats_hw_info->stats_ping_pong_offset;
 
 	pingpong_bit = (~(pingpong_status >> stats_pingpong_offset) & 0x1);
@@ -151,10 +159,9 @@
 	stats_idx = vfe_dev->hw_info->vfe_ops.stats_ops.
 		get_stats_idx(stream_req_cmd->stats_type);
 
-	if ((stats_idx > MSM_ISP_STATS_MAX) ||
-		(stats_idx == -EINVAL)) {
-		pr_err("%s: Stats idx Error\n", __func__);
-		return rc;
+	if (stats_idx >= vfe_dev->hw_info->stats_hw_info->num_stats_type) {
+		pr_err("%s Invalid stats index %d", __func__, stats_idx);
+		return -EINVAL;
 	}
 
 	stream_info = &stats_data->stream_info[stats_idx];
@@ -209,9 +216,10 @@
 	}
 
 	stats_idx = STATS_IDX(stream_req_cmd->stream_handle);
-	if (stats_idx > MSM_ISP_STATS_MAX) {
-		pr_err("%s: Stats idx Error\n", __func__);
-		return rc;
+
+	if (stats_idx >= vfe_dev->hw_info->stats_hw_info->num_stats_type) {
+		pr_err("%s Invalid stats index %d", __func__, stats_idx);
+		return -EINVAL;
 	}
 
 	stream_info = &stats_data->stream_info[stats_idx];
@@ -242,9 +250,9 @@
 	int stats_idx = STATS_IDX(stream_release_cmd->stream_handle);
 	struct msm_vfe_stats_stream *stream_info = NULL;
 
-	if (stats_idx > MSM_ISP_STATS_MAX) {
-		pr_err("%s: Stats idx Error\n", __func__);
-		return rc;
+	if (stats_idx >= vfe_dev->hw_info->stats_hw_info->num_stats_type) {
+		pr_err("%s Invalid stats index %d", __func__, stats_idx);
+		return -EINVAL;
 	}
 
 	stream_info = &stats_data->stream_info[stats_idx];
@@ -379,6 +387,12 @@
 	struct msm_vfe_stats_shared_data *stats_data = &vfe_dev->stats_data;
 	for (i = 0; i < stream_cfg_cmd->num_streams; i++) {
 		idx = STATS_IDX(stream_cfg_cmd->stream_handle[i]);
+
+		if (idx >= vfe_dev->hw_info->stats_hw_info->num_stats_type) {
+			pr_err("%s Invalid stats index %d", __func__, idx);
+			return -EINVAL;
+		}
+
 		stream_info = &stats_data->stream_info[idx];
 		if (stream_info->stream_handle !=
 				stream_cfg_cmd->stream_handle[i]) {
@@ -423,6 +437,12 @@
 	struct msm_vfe_stats_shared_data *stats_data = &vfe_dev->stats_data;
 	for (i = 0; i < stream_cfg_cmd->num_streams; i++) {
 		idx = STATS_IDX(stream_cfg_cmd->stream_handle[i]);
+
+		if (idx >= vfe_dev->hw_info->stats_hw_info->num_stats_type) {
+			pr_err("%s Invalid stats index %d", __func__, idx);
+			return -EINVAL;
+		}
+
 		stream_info = &stats_data->stream_info[idx];
 		if (stream_info->stream_handle !=
 				stream_cfg_cmd->stream_handle[i]) {
@@ -453,6 +473,12 @@
 
 	for (i = 0; i < stream_cfg_cmd->num_streams; i++) {
 		idx = STATS_IDX(stream_cfg_cmd->stream_handle[i]);
+
+		if (idx >= vfe_dev->hw_info->stats_hw_info->num_stats_type) {
+			pr_err("%s Invalid stats index %d", __func__, idx);
+			return -EINVAL;
+		}
+
 		stream_info = &stats_data->stream_info[idx];
 		msm_isp_deinit_stats_ping_pong_reg(vfe_dev, stream_info);
 	}
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c
index de5a16d..b024569 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c
@@ -802,6 +802,8 @@
 void msm_isp_process_error_info(struct vfe_device *vfe_dev)
 {
 	int i;
+	uint8_t num_stats_type =
+		vfe_dev->hw_info->stats_hw_info->num_stats_type;
 	struct msm_vfe_error_info *error_info = &vfe_dev->error_info;
 	static DEFINE_RATELIMIT_STATE(rs,
 		DEFAULT_RATELIMIT_INTERVAL, DEFAULT_RATELIMIT_BURST);
@@ -825,7 +827,7 @@
 				error_info->stream_framedrop_count[i] = 0;
 			}
 		}
-		for (i = 0; i < MSM_ISP_STATS_MAX; i++) {
+		for (i = 0; i < num_stats_type; i++) {
 			if (error_info->stats_framedrop_count[i] != 0 &&
 				__ratelimit(&rs_stats)) {
 				pr_err("%s: Stats stream[%d]: dropped %d frames\n",
diff --git a/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c b/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c
index 6b05840..2b963a4 100644
--- a/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c
+++ b/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c
@@ -40,7 +40,7 @@
 #define ISPIF_INTF_CMD_DISABLE_IMMEDIATELY    0x02
 
 #define ISPIF_TIMEOUT_SLEEP_US                1000
-#define ISPIF_TIMEOUT_ALL_US                500000
+#define ISPIF_TIMEOUT_ALL_US               1000000
 
 #undef CDBG
 #ifdef CONFIG_MSMB_CAMERA_DEBUG
diff --git a/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c b/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c
index 8c8570d..344dc71 100644
--- a/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c
+++ b/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c
@@ -1318,7 +1318,8 @@
 
 	fw_version_1_2_x = 0;
 	if ((cpp_dev->hw_info.cpp_hw_version == CPP_HW_VERSION_1_1_0) ||
-		(cpp_dev->hw_info.cpp_hw_version == CPP_HW_VERSION_1_1_1))
+		(cpp_dev->hw_info.cpp_hw_version == CPP_HW_VERSION_1_1_1) ||
+		(cpp_dev->hw_info.cpp_hw_version == CPP_HW_VERSION_2_0_0))
 		fw_version_1_2_x = 2;
 
 	for (i = 0; i < num_stripes; i++) {
diff --git a/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.h b/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.h
index 796bede..cf22e6c 100644
--- a/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.h
+++ b/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.h
@@ -28,6 +28,7 @@
 **/
 #define CPP_HW_VERSION_1_1_0  0x10010000
 #define CPP_HW_VERSION_1_1_1  0x10010001
+#define CPP_HW_VERSION_2_0_0  0x20000000
 
 #define MAX_ACTIVE_CPP_INSTANCE 8
 #define MAX_CPP_PROCESSING_FRAME 2
diff --git a/drivers/media/platform/msm/camera_v2/pproc/vpe/msm_vpe.c b/drivers/media/platform/msm/camera_v2/pproc/vpe/msm_vpe.c
index d302131..3aaff78 100644
--- a/drivers/media/platform/msm/camera_v2/pproc/vpe/msm_vpe.c
+++ b/drivers/media/platform/msm/camera_v2/pproc/vpe/msm_vpe.c
@@ -1323,6 +1323,11 @@
 		struct msm_vpe_buff_queue_info_t *buff_queue_info;
 
 		VPE_DBG("VIDIOC_MSM_VPE_DEQUEUE_STREAM_BUFF_INFO\n");
+		if (ioctl_ptr->len != sizeof(uint32_t)) {
+			pr_err("%s:%d Invalid len\n", __func__, __LINE__);
+			mutex_unlock(&vpe_dev->mutex);
+			return -EINVAL;
+		}
 
 		rc = (copy_from_user(&identity,
 				(void __user *)ioctl_ptr->ioctl_ptr,
diff --git a/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.c b/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.c
index 7f4f231..17c5a14 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.c
+++ b/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.c
@@ -41,6 +41,9 @@
 
 /* Max bytes that can be read per CCI read transaction */
 #define CCI_READ_MAX 12
+#define CCI_I2C_READ_MAX_RETRIES 3
+#define CCI_I2C_MAX_READ 8192
+#define CCI_I2C_MAX_WRITE 8192
 
 static struct v4l2_subdev *g_cci_subdev;
 
@@ -87,36 +90,6 @@
 	return;
 }
 
-static int32_t msm_cci_i2c_config_sync_timer(struct v4l2_subdev *sd,
-	struct msm_camera_cci_ctrl *c_ctrl)
-{
-	struct cci_device *cci_dev;
-	cci_dev = v4l2_get_subdevdata(sd);
-	msm_camera_io_w(c_ctrl->cci_info->cid, cci_dev->base +
-		CCI_SET_CID_SYNC_TIMER_0_ADDR + (c_ctrl->cci_info->cid * 0x4));
-	return 0;
-}
-
-static int32_t msm_cci_i2c_set_freq(struct v4l2_subdev *sd,
-	struct msm_camera_cci_ctrl *c_ctrl)
-{
-	struct cci_device *cci_dev;
-	uint32_t val;
-	cci_dev = v4l2_get_subdevdata(sd);
-	val = c_ctrl->cci_info->freq;
-	msm_camera_io_w(val, cci_dev->base + CCI_I2C_M0_SCL_CTL_ADDR +
-		c_ctrl->cci_info->cci_i2c_master*0x100);
-	msm_camera_io_w(val, cci_dev->base + CCI_I2C_M0_SDA_CTL_0_ADDR +
-		c_ctrl->cci_info->cci_i2c_master*0x100);
-	msm_camera_io_w(val, cci_dev->base + CCI_I2C_M0_SDA_CTL_1_ADDR +
-		c_ctrl->cci_info->cci_i2c_master*0x100);
-	msm_camera_io_w(val, cci_dev->base + CCI_I2C_M0_SDA_CTL_2_ADDR +
-		c_ctrl->cci_info->cci_i2c_master*0x100);
-	msm_camera_io_w(val, cci_dev->base + CCI_I2C_M0_MISC_CTL_ADDR +
-		c_ctrl->cci_info->cci_i2c_master*0x100);
-	return 0;
-}
-
 static void msm_cci_flush_queue(struct cci_device *cci_dev,
 	enum cci_i2c_master_t master)
 {
@@ -213,8 +186,29 @@
 	uint16_t cmd_size = i2c_msg->size;
 	struct msm_camera_i2c_reg_conf *i2c_cmd = i2c_msg->reg_conf_tbl;
 	enum cci_i2c_master_t master = c_ctrl->cci_info->cci_i2c_master;
+
+	if (i2c_cmd == NULL) {
+		pr_err("%s:%d Failed line\n", __func__,
+			__LINE__);
+		return -EINVAL;
+	}
+
+	if ((!cmd_size) || (cmd_size > CCI_I2C_MAX_WRITE)) {
+		pr_err("%s:%d Failed line\n", __func__, __LINE__);
+		return -EINVAL;
+	}
+
 	CDBG("%s addr type %d data type %d\n", __func__,
 		i2c_msg->addr_type, i2c_msg->data_type);
+
+	if (i2c_msg->addr_type >= MSM_CAMERA_I2C_ADDR_TYPE_MAX) {
+		pr_err("%s failed line %d\n", __func__, __LINE__);
+		return -EINVAL;
+	}
+	if (i2c_msg->data_type >= MSM_CAMERA_I2C_DATA_TYPE_MAX) {
+		pr_err("%s failed line %d\n", __func__, __LINE__);
+		return -EINVAL;
+	}
 	/* assume total size within the max queue */
 	while (cmd_size) {
 		CDBG("%s cmd_size %d addr 0x%x data 0x%x", __func__,
@@ -321,6 +315,18 @@
 		goto ERROR;
 	}
 
+	if (c_ctrl->cci_info->retries > CCI_I2C_READ_MAX_RETRIES) {
+		pr_err("%s:%d More than max retries\n", __func__,
+			__LINE__);
+		goto ERROR;
+	}
+
+	if (read_cfg->data == NULL) {
+		pr_err("%s:%d Data ptr is NULL\n", __func__,
+			__LINE__);
+		goto ERROR;
+	}
+
 	CDBG("%s master %d, queue %d\n", __func__, master, queue);
 	CDBG("%s set param sid 0x%x retries %d id_map %d\n", __func__,
 		c_ctrl->cci_info->sid, c_ctrl->cci_info->retries,
@@ -341,6 +347,11 @@
 		goto ERROR;
 	}
 
+	if (read_cfg->addr_type >= MSM_CAMERA_I2C_ADDR_TYPE_MAX) {
+		CDBG("%s failed line %d\n", __func__, __LINE__);
+		goto ERROR;
+	}
+
 	if (read_cfg->addr_type == MSM_CAMERA_I2C_BYTE_ADDR)
 		val = CCI_I2C_WRITE_DISABLE_P_CMD | (read_cfg->addr_type << 4) |
 			((read_cfg->addr & 0xFF) << 8);
@@ -454,9 +465,14 @@
 		return -EINVAL;
 	}
 
+	if (c_ctrl->cci_info->cci_i2c_master > MASTER_MAX) {
+		pr_err("%s:%d Invalid I2C master addr\n", __func__, __LINE__);
+		return -EINVAL;
+	}
+
 	master = c_ctrl->cci_info->cci_i2c_master;
 	read_cfg = &c_ctrl->cfg.cci_i2c_read_cfg;
-	if (!read_cfg->num_byte) {
+	if ((!read_cfg->num_byte) || (read_cfg->num_byte > CCI_I2C_MAX_READ)) {
 		pr_err("%s:%d read num bytes 0\n", __func__, __LINE__);
 		rc = -EINVAL;
 		goto ERROR;
@@ -494,6 +510,10 @@
 	enum cci_i2c_master_t master;
 	enum cci_i2c_queue_t queue = QUEUE_0;
 	cci_dev = v4l2_get_subdevdata(sd);
+	if (c_ctrl->cci_info->cci_i2c_master > MASTER_MAX) {
+		pr_err("%s:%d Invalid I2C master addr\n", __func__, __LINE__);
+		return -EINVAL;
+	}
 	master = c_ctrl->cci_info->cci_i2c_master;
 	CDBG("%s master %d, queue %d\n", __func__, master, queue);
 	CDBG("%s set param sid 0x%x retries %d id_map %d\n", __func__,
@@ -514,6 +534,11 @@
 			__LINE__, rc);
 		goto ERROR;
 	}
+	if (c_ctrl->cci_info->retries > CCI_I2C_READ_MAX_RETRIES) {
+		pr_err("%s:%d More than max retries\n", __func__,
+			__LINE__);
+		goto ERROR;
+	}
 
 	val = CCI_I2C_SET_PARAM_CMD | c_ctrl->cci_info->sid << 4 |
 		c_ctrl->cci_info->retries << 16 |
@@ -533,7 +558,11 @@
 		goto ERROR;
 	}
 
-	msm_cci_data_queue(cci_dev, c_ctrl, queue);
+	rc = msm_cci_data_queue(cci_dev, c_ctrl, queue);
+	if (rc < 0) {
+		CDBG("%s failed line %d\n", __func__, __LINE__);
+		goto ERROR;
+	}
 	val = CCI_I2C_UNLOCK_CMD;
 	CDBG("%s:%d CCI_I2C_UNLOCK_CMD\n", __func__, __LINE__);
 	rc = msm_cci_write_i2c_queue(cci_dev, val, master, queue);
@@ -622,7 +651,7 @@
 	if (rc < 0) {
 		cci_dev->ref_count--;
 		CDBG("%s: request gpio failed\n", __func__);
-		goto clk_enable_failed;
+		goto request_gpio_failed;
 	}
 
 	rc = msm_cam_clk_enable(&cci_dev->pdev->dev, cci_clk_info,
@@ -647,7 +676,7 @@
 			 __func__, __LINE__);
 		if (rc == 0)
 			rc = -ETIMEDOUT;
-		return rc;
+		goto reset_complete_failed;
 	}
 	msm_cci_set_clk_param(cci_dev);
 	msm_camera_io_w(CCI_IRQ_MASK_0_RMSK,
@@ -658,7 +687,14 @@
 	cci_dev->cci_state = CCI_STATE_ENABLED;
 	return 0;
 
+reset_complete_failed:
+	disable_irq(cci_dev->irq->start);
+	msm_cam_clk_enable(&cci_dev->pdev->dev, cci_clk_info,
+		cci_dev->cci_clk, ARRAY_SIZE(cci_clk_info), 0);
 clk_enable_failed:
+	msm_camera_request_gpio_table(cci_dev->cci_gpio_tbl,
+		cci_dev->cci_gpio_tbl_size, 0);
+request_gpio_failed:
 	return rc;
 }
 
@@ -703,14 +739,6 @@
 	case MSM_CCI_RELEASE:
 		rc = msm_cci_release(sd);
 		break;
-	case MSM_CCI_SET_SID:
-		break;
-	case MSM_CCI_SET_FREQ:
-		rc = msm_cci_i2c_set_freq(sd, cci_ctrl);
-		break;
-	case MSM_CCI_SET_SYNC_CID:
-		rc = msm_cci_i2c_config_sync_timer(sd, cci_ctrl);
-		break;
 	case MSM_CCI_I2C_READ:
 		rc = msm_cci_i2c_read_bytes(sd, cci_ctrl);
 		break;
@@ -809,7 +837,10 @@
 		rc = msm_cci_config(sd, arg);
 		break;
 	case MSM_SD_SHUTDOWN: {
-		return rc;
+		struct msm_camera_cci_ctrl ctrl_cmd;
+		ctrl_cmd.cmd = MSM_CCI_RELEASE;
+		rc = msm_cci_config(sd, &ctrl_cmd);
+		break;
 	}
 	default:
 		rc = -ENOIOCTLCMD;
diff --git a/drivers/media/platform/msm/camera_v2/sensor/eeprom/msm_eeprom.c b/drivers/media/platform/msm/camera_v2/sensor/eeprom/msm_eeprom.c
index 33bee58..1407f12 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/eeprom/msm_eeprom.c
+++ b/drivers/media/platform/msm/camera_v2/sensor/eeprom/msm_eeprom.c
@@ -233,7 +233,7 @@
 				       struct device_node *of)
 {
 	int i, rc = 0;
-	char property[12];
+	char property[14];
 	uint32_t count = 6;
 	struct msm_eeprom_board_info *eb = e_ctrl->eboard_info;
 
diff --git a/drivers/media/platform/msm/camera_v2/sensor/flash/adp1660.c b/drivers/media/platform/msm/camera_v2/sensor/flash/adp1660.c
index 2a41ab1..aad8158 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/flash/adp1660.c
+++ b/drivers/media/platform/msm/camera_v2/sensor/flash/adp1660.c
@@ -43,7 +43,8 @@
 
 static struct msm_camera_i2c_reg_array adp1660_low_array[] = {
 	{0x08, 0x04},
-	{0x01, 0xBA},
+	{0x06, 0x1E},
+	{0x01, 0xBD},
 	{0x0f, 0x01},
 };
 
@@ -52,7 +53,7 @@
 	{0x06, 0x3C},
 	{0x09, 0x28},
 	{0x0f, 0x01},
-	{0x01, 0xBA},
+	{0x01, 0xBD},
 };
 
 static void __exit msm_flash_adp1660_i2c_remove(void)
diff --git a/drivers/media/platform/msm/camera_v2/sensor/flash/msm_led_flash.c b/drivers/media/platform/msm/camera_v2/sensor/flash/msm_led_flash.c
index 50e5a0f..2de17c9 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/flash/msm_led_flash.c
+++ b/drivers/media/platform/msm/camera_v2/sensor/flash/msm_led_flash.c
@@ -42,6 +42,9 @@
 		return fctrl->func_tbl->flash_get_subdev_id(fctrl, argp);
 	case VIDIOC_MSM_FLASH_LED_DATA_CFG:
 		return fctrl->func_tbl->flash_led_config(fctrl, argp);
+	case MSM_SD_SHUTDOWN:
+		*(int *)argp = MSM_CAMERA_LED_RELEASE;
+		return fctrl->func_tbl->flash_led_config(fctrl, argp);
 	default:
 		pr_err("invalid cmd %d\n", cmd);
 		return -ENOIOCTLCMD;
diff --git a/drivers/media/platform/msm/camera_v2/sensor/gc0339.c b/drivers/media/platform/msm/camera_v2/sensor/gc0339.c
index 790dd28..8288ad0 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/gc0339.c
+++ b/drivers/media/platform/msm/camera_v2/sensor/gc0339.c
@@ -254,9 +254,12 @@
 				0);
 			break;
 		case SENSOR_GPIO:
-			gpio_set_value_cansleep(
-				data->gpio_conf->gpio_num_info->gpio_num
-				[power_setting->seq_val], GPIOF_OUT_INIT_LOW);
+			if (data->gpio_conf->gpio_num_info->gpio_num
+				[power_setting->seq_val])
+				gpio_set_value_cansleep(
+					data->gpio_conf->gpio_num_info->gpio_num
+					[power_setting->seq_val],
+					GPIOF_OUT_INIT_LOW);
 			break;
 		case SENSOR_VREG:
 			msm_camera_config_single_vreg(s_ctrl->dev,
@@ -322,9 +325,12 @@
 					SENSOR_GPIO_MAX);
 				continue;
 			}
-			gpio_set_value_cansleep(
-				data->gpio_conf->gpio_num_info->gpio_num
-				[power_setting->seq_val], GPIOF_OUT_INIT_LOW);
+			if (data->gpio_conf->gpio_num_info->gpio_num
+				[power_setting->seq_val])
+				gpio_set_value_cansleep(
+					data->gpio_conf->gpio_num_info->gpio_num
+					[power_setting->seq_val],
+					GPIOF_OUT_INIT_LOW);
 			break;
 		case SENSOR_VREG:
 			if (power_setting->seq_val >= CAM_VREG_MAX) {
diff --git a/drivers/media/platform/msm/camera_v2/sensor/hi256.c b/drivers/media/platform/msm/camera_v2/sensor/hi256.c
index 73226ed..3288e9c 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/hi256.c
+++ b/drivers/media/platform/msm/camera_v2/sensor/hi256.c
@@ -151,7 +151,7 @@
 
 	{0x03, 0x00},
 	{0x10, 0x13},
-	{0x11, 0x93},
+	{0x11, 0x90}, /* no H/V flip */
 	{0x12, 0x00},
 	{0x0b, 0xaa},
 	{0x0c, 0xaa},
diff --git a/drivers/media/platform/msm/camera_v2/sensor/msm_sensor.c b/drivers/media/platform/msm/camera_v2/sensor/msm_sensor.c
index 6764db6..1724db1 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/msm_sensor.c
+++ b/drivers/media/platform/msm/camera_v2/sensor/msm_sensor.c
@@ -967,6 +967,7 @@
 	struct msm_sensor_power_setting_array *power_setting_array = NULL;
 	struct msm_sensor_power_setting *power_setting = NULL;
 	struct msm_camera_sensor_board_info *data = s_ctrl->sensordata;
+	s_ctrl->stop_setting_valid = 0;
 
 	CDBG("%s:%d\n", __func__, __LINE__);
 	power_setting_array = &s_ctrl->power_setting_array;
@@ -1271,9 +1272,8 @@
 	case VIDIOC_MSM_SENSOR_GET_AF_STATUS:
 		return msm_sensor_get_af_status(s_ctrl, argp);
 	case VIDIOC_MSM_SENSOR_RELEASE:
-		msm_sensor_stop_stream(s_ctrl);
-		return 0;
 	case MSM_SD_SHUTDOWN:
+		msm_sensor_stop_stream(s_ctrl);
 		return 0;
 	default:
 		return -ENOIOCTLCMD;
diff --git a/drivers/media/platform/msm/vidc/hfi_packetization.c b/drivers/media/platform/msm/vidc/hfi_packetization.c
index ef2c12a..1dba2b6 100644
--- a/drivers/media/platform/msm/vidc/hfi_packetization.c
+++ b/drivers/media/platform/msm/vidc/hfi_packetization.c
@@ -32,6 +32,7 @@
 		HFI_H264_PROFILE_CONSTRAINED_BASE,
 	[ilog2(HAL_H264_PROFILE_CONSTRAINED_HIGH)] =
 		HFI_H264_PROFILE_CONSTRAINED_HIGH,
+	[ilog2(HAL_VPX_PROFILE_VERSION_1)] = HFI_VPX_PROFILE_VERSION_1,
 };
 
 static int entropy_mode[] = {
diff --git a/drivers/media/platform/msm/vidc/hfi_response_handler.c b/drivers/media/platform/msm/vidc/hfi_response_handler.c
index 653ba46..8350dde 100644
--- a/drivers/media/platform/msm/vidc/hfi_response_handler.c
+++ b/drivers/media/platform/msm/vidc/hfi_response_handler.c
@@ -78,6 +78,26 @@
 	return vidc_err;
 }
 
+static int sanitize_session_pkt(struct list_head *sessions,
+		struct hal_session *sess, struct mutex *session_lock)
+{
+	struct hal_session *session;
+	int invalid = 1;
+	if (session_lock) {
+		mutex_lock(session_lock);
+		list_for_each_entry(session, sessions, list) {
+			if (session == sess) {
+				invalid = 0;
+				break;
+			}
+		}
+		mutex_unlock(session_lock);
+	}
+	if (invalid)
+		dprintk(VIDC_WARN, "Invalid session from FW: %p\n", sess);
+	return invalid;
+}
+
 static void hfi_process_sess_evt_seq_changed(
 		msm_vidc_callback callback, u32 device_id,
 		struct hfi_msg_event_notify_packet *pkt)
@@ -1115,9 +1135,11 @@
 
 u32 hfi_process_msg_packet(
 		msm_vidc_callback callback, u32 device_id,
-		struct vidc_hal_msg_pkt_hdr *msg_hdr)
+		struct vidc_hal_msg_pkt_hdr *msg_hdr,
+		struct list_head *sessions, struct mutex *session_lock)
 {
 	u32 rc = 0;
+	struct hal_session *sess;
 	if (!callback || !msg_hdr || msg_hdr->size <
 		VIDC_IFACEQ_MIN_PKT_SIZE) {
 		dprintk(VIDC_ERR, "hal_process_msg_packet:bad"
@@ -1126,10 +1148,19 @@
 		return rc;
 	}
 
+#define SANITIZE_SESSION_PKT(msg_pkt) ({ \
+		sess = (struct hal_session *) \
+				(((struct vidc_hal_session_cmd_pkt *) \
+				msg_pkt)->session_id); \
+		if (sanitize_session_pkt(sessions, sess, session_lock)) \
+			break; \
+	})
+
 	dprintk(VIDC_INFO, "Received: 0x%x in ", msg_hdr->packet);
 	rc = (u32) msg_hdr->packet;
 	switch (msg_hdr->packet) {
 	case HFI_MSG_EVENT_NOTIFY:
+		SANITIZE_SESSION_PKT(msg_hdr);
 		hfi_process_event_notify(callback, device_id,
 			(struct hfi_msg_event_notify_packet *) msg_hdr);
 		break;
@@ -1141,6 +1172,7 @@
 	case HFI_MSG_SYS_IDLE:
 		break;
 	case HFI_MSG_SYS_SESSION_INIT_DONE:
+		SANITIZE_SESSION_PKT(msg_hdr);
 		hfi_process_session_init_done(callback, device_id,
 			(struct hfi_msg_sys_session_init_done_packet *)
 					msg_hdr);
@@ -1151,44 +1183,53 @@
 			msg_hdr);
 		break;
 	case HFI_MSG_SYS_SESSION_END_DONE:
+		SANITIZE_SESSION_PKT(msg_hdr);
 		hfi_process_session_end_done(callback, device_id,
 			(struct hfi_msg_sys_session_end_done_packet *)
 					msg_hdr);
 		break;
 	case HFI_MSG_SESSION_LOAD_RESOURCES_DONE:
+		SANITIZE_SESSION_PKT(msg_hdr);
 		hfi_process_session_load_res_done(callback, device_id,
 			(struct hfi_msg_session_load_resources_done_packet *)
 					msg_hdr);
 		break;
 	case HFI_MSG_SESSION_START_DONE:
+		SANITIZE_SESSION_PKT(msg_hdr);
 		hfi_process_session_start_done(callback, device_id,
 			(struct hfi_msg_session_start_done_packet *)
 					msg_hdr);
 		break;
 	case HFI_MSG_SESSION_STOP_DONE:
+		SANITIZE_SESSION_PKT(msg_hdr);
 		hfi_process_session_stop_done(callback, device_id,
 			(struct hfi_msg_session_stop_done_packet *)
 					msg_hdr);
 		break;
 	case HFI_MSG_SESSION_EMPTY_BUFFER_DONE:
+		SANITIZE_SESSION_PKT(msg_hdr);
 		hfi_process_session_etb_done(callback, device_id,
 			(struct hfi_msg_session_empty_buffer_done_packet *)
 					msg_hdr);
 		break;
 	case HFI_MSG_SESSION_FILL_BUFFER_DONE:
+		SANITIZE_SESSION_PKT(msg_hdr);
 		hfi_process_session_ftb_done(callback, device_id, msg_hdr);
 		break;
 	case HFI_MSG_SESSION_FLUSH_DONE:
+		SANITIZE_SESSION_PKT(msg_hdr);
 		hfi_process_session_flush_done(callback, device_id,
 			(struct hfi_msg_session_flush_done_packet *)
 					msg_hdr);
 		break;
 	case HFI_MSG_SESSION_PROPERTY_INFO:
+		SANITIZE_SESSION_PKT(msg_hdr);
 		hfi_process_session_prop_info(callback, device_id,
 			(struct hfi_msg_session_property_info_packet *)
 					msg_hdr);
 		break;
 	case HFI_MSG_SESSION_RELEASE_RESOURCES_DONE:
+		SANITIZE_SESSION_PKT(msg_hdr);
 		hfi_process_session_rel_res_done(callback, device_id,
 			(struct hfi_msg_session_release_resources_done_packet *)
 					msg_hdr);
@@ -1199,18 +1240,21 @@
 			msg_hdr);
 		break;
 	case HFI_MSG_SESSION_GET_SEQUENCE_HEADER_DONE:
+		SANITIZE_SESSION_PKT(msg_hdr);
 		hfi_process_session_get_seq_hdr_done(
 			callback, device_id, (struct
 			hfi_msg_session_get_sequence_header_done_packet*)
 			msg_hdr);
 		break;
 	case HFI_MSG_SESSION_RELEASE_BUFFERS_DONE:
+		SANITIZE_SESSION_PKT(msg_hdr);
 		hfi_process_session_rel_buf_done(
 			callback, device_id, (struct
 			hfi_msg_session_release_buffers_done_packet*)
 			msg_hdr);
 		break;
 	case HFI_MSG_SYS_SESSION_ABORT_DONE:
+		SANITIZE_SESSION_PKT(msg_hdr);
 		hfi_process_session_abort_done(callback, device_id, (struct
 			hfi_msg_sys_session_abort_done_packet*) msg_hdr);
 		break;
@@ -1218,5 +1262,6 @@
 		dprintk(VIDC_DBG, "UNKNOWN_MSG_TYPE : %d", msg_hdr->packet);
 		break;
 	}
+#undef SANITIZE_SESSION_PKT
 	return rc;
 }
diff --git a/drivers/media/platform/msm/vidc/msm_venc.c b/drivers/media/platform/msm/vidc/msm_venc.c
index cddca74..dcf5a0e 100644
--- a/drivers/media/platform/msm/vidc/msm_venc.c
+++ b/drivers/media/platform/msm/vidc/msm_venc.c
@@ -87,6 +87,14 @@
 	"High Latency",
 };
 
+static const char *const vp8_profile_level[] = {
+	"Unused",
+	"0.0",
+	"1.0",
+	"2.0",
+	"3.0",
+};
+
 static const char *const mpeg_video_vidc_extradata[] = {
 	"Extradata none",
 	"Extradata MB Quantization",
@@ -125,7 +133,8 @@
 	MSM_VENC_CTRL_CLUSTER_INTRA_REFRESH = 1 << 7,
 	MSM_VENC_CTRL_CLUSTER_BITRATE = 1 << 8,
 	MSM_VENC_CTRL_CLUSTER_TIMING = 1 << 9,
-	MSM_VENC_CTRL_CLUSTER_MAX = 1 << 10,
+	MSM_VENC_CTRL_CLUSTER_VP8_PROFILE_LEVEL = 1 << 10,
+	MSM_VENC_CTRL_CLUSTER_MAX = 1 << 11,
 };
 
 static struct msm_vidc_ctrl msm_venc_ctrls[] = {
@@ -362,6 +371,21 @@
 		.cluster = MSM_VENC_CTRL_CLUSTER_H263_PROFILE_LEVEL,
 	},
 	{
+		.id = V4L2_CID_MPEG_VIDC_VIDEO_VP8_PROFILE_LEVEL,
+		.name = "VP8 Profile Level",
+		.type = V4L2_CTRL_TYPE_MENU,
+		.minimum = V4L2_MPEG_VIDC_VIDEO_VP8_UNUSED,
+		.maximum = V4L2_MPEG_VIDC_VIDEO_VP8_VERSION_1,
+		.default_value = V4L2_MPEG_VIDC_VIDEO_VP8_VERSION_0,
+		.menu_skip_mask = ~(
+		(1 << V4L2_MPEG_VIDC_VIDEO_VP8_UNUSED) |
+		(1 << V4L2_MPEG_VIDC_VIDEO_VP8_VERSION_0) |
+		(1 << V4L2_MPEG_VIDC_VIDEO_VP8_VERSION_1)
+		),
+		.qmenu = vp8_profile_level,
+		.cluster = MSM_VENC_CTRL_CLUSTER_VP8_PROFILE_LEVEL,
+	},
+	{
 		.id = V4L2_CID_MPEG_VIDC_VIDEO_ROTATION,
 		.name = "Rotation",
 		.type = V4L2_CTRL_TYPE_MENU,
@@ -1171,6 +1195,21 @@
 		default:
 			goto unknown_value;
 		}
+	case V4L2_CID_MPEG_VIDC_VIDEO_VP8_PROFILE_LEVEL:
+		switch (value) {
+		case V4L2_MPEG_VIDC_VIDEO_VP8_VERSION_0:
+			return HAL_VPX_PROFILE_VERSION_0;
+		case V4L2_MPEG_VIDC_VIDEO_VP8_VERSION_1:
+			return HAL_VPX_PROFILE_VERSION_1;
+		case V4L2_MPEG_VIDC_VIDEO_VP8_VERSION_2:
+			return HAL_VPX_PROFILE_VERSION_2;
+		case V4L2_MPEG_VIDC_VIDEO_VP8_VERSION_3:
+			return HAL_VPX_PROFILE_VERSION_3;
+		case V4L2_MPEG_VIDC_VIDEO_VP8_UNUSED:
+			return HAL_VPX_PROFILE_UNUSED;
+		default:
+			goto unknown_value;
+		}
 	}
 
 unknown_value:
@@ -1477,6 +1516,15 @@
 				ctrl->val);
 		pdata = &profile_level;
 		break;
+	case V4L2_CID_MPEG_VIDC_VIDEO_VP8_PROFILE_LEVEL:
+		property_id =
+			HAL_PARAM_PROFILE_LEVEL_CURRENT;
+		profile_level.profile = venc_v4l2_to_hal(
+				V4L2_CID_MPEG_VIDC_VIDEO_VP8_PROFILE_LEVEL,
+				ctrl->val);
+		profile_level.level = HAL_VPX_PROFILE_UNUSED;
+		pdata = &profile_level;
+		break;
 	case V4L2_CID_MPEG_VIDC_VIDEO_ROTATION:
 		property_id =
 			HAL_CONFIG_VPE_OPERATIONS;
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_common.c b/drivers/media/platform/msm/vidc/msm_vidc_common.c
index 36a2658..91fc9d0 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_common.c
+++ b/drivers/media/platform/msm/vidc/msm_vidc_common.c
@@ -526,8 +526,15 @@
 {
 	struct msm_vidc_cb_cmd_done *response = data;
 	struct msm_vidc_inst *inst;
-	if (response)
+	if (response) {
 		inst = (struct msm_vidc_inst *)response->session_id;
+		if (response->status) {
+			dprintk(VIDC_ERR,
+				"Load resource response from FW : 0x%x",
+				response->status);
+			msm_comm_generate_session_error(inst);
+		}
+	}
 	else
 		dprintk(VIDC_ERR,
 			"Failed to get valid response for load resource\n");
@@ -817,10 +824,18 @@
 		(u32)fill_buf_done->packet_buffer1);
 	if (vb) {
 		vb->v4l2_planes[0].bytesused = fill_buf_done->filled_len1;
+		vb->v4l2_planes[0].data_offset = fill_buf_done->offset1;
 		vb->v4l2_planes[0].reserved[2] = fill_buf_done->start_x_coord;
 		vb->v4l2_planes[0].reserved[3] = fill_buf_done->start_y_coord;
 		vb->v4l2_planes[0].reserved[4] = fill_buf_done->frame_width;
 		vb->v4l2_planes[0].reserved[5] = fill_buf_done->frame_height;
+		if (vb->v4l2_planes[0].data_offset > vb->v4l2_planes[0].length)
+			dprintk(VIDC_INFO, "fbd:data_offset overflow length\n");
+		if (vb->v4l2_planes[0].bytesused > vb->v4l2_planes[0].length)
+			dprintk(VIDC_INFO, "fbd:bytesused overflow length\n");
+		if ((u8 *)vb->v4l2_planes[0].m.userptr !=
+			response->input_done.packet_buffer)
+			dprintk(VIDC_INFO, "fbd:Unexpected buffer address\n");
 		if (!(fill_buf_done->flags1 &
 			HAL_BUFFERFLAG_TIMESTAMPINVALID) &&
 			fill_buf_done->filled_len1) {
@@ -873,8 +888,9 @@
 			msm_vidc_debugfs_update(inst,
 				MSM_VIDC_DEBUGFS_EVENT_FBD);
 
-		dprintk(VIDC_DBG, "Filled length = %d; flags %x\n",
+		dprintk(VIDC_DBG, "Filled length = %d; offset = %d; flags %x\n",
 				vb->v4l2_planes[0].bytesused,
+				vb->v4l2_planes[0].data_offset,
 				vb->v4l2_buf.flags);
 		mutex_lock(&inst->bufq[CAPTURE_PORT].lock);
 		vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
@@ -901,6 +917,7 @@
 				vb = list_first_entry(&q->vb2_bufq.queued_list,
 					struct vb2_buffer, queued_entry);
 				vb->v4l2_planes[0].bytesused = 0;
+				vb->v4l2_planes[0].data_offset = 0;
 				vb->v4l2_buf.flags |= V4L2_BUF_FLAG_EOS;
 				mutex_lock(&q->lock);
 				vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
@@ -932,12 +949,14 @@
 	}
 
 	vb->v4l2_planes[0].bytesused = fill_buf_done->filled_len1;
+	vb->v4l2_planes[0].data_offset = fill_buf_done->offset1;
 
 	vb->v4l2_buf.flags = V4L2_QCOM_BUF_FLAG_CODECCONFIG;
 	vb->v4l2_buf.timestamp = ns_to_timeval(0);
 
-	dprintk(VIDC_DBG, "Filled length = %d; flags %x\n",
+	dprintk(VIDC_DBG, "Filled length = %d; offset = %d; flags %x\n",
 				vb->v4l2_planes[0].bytesused,
+				vb->v4l2_planes[0].data_offset,
 				vb->v4l2_buf.flags);
 	mutex_lock(&inst->bufq[CAPTURE_PORT].lock);
 	vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
@@ -2341,6 +2360,7 @@
 					queued_entry);
 			if (vb) {
 				vb->v4l2_planes[0].bytesused = 0;
+				vb->v4l2_planes[0].data_offset = 0;
 				mutex_lock(&inst->bufq[CAPTURE_PORT].lock);
 				vb2_buffer_done(vb,
 						VB2_BUF_STATE_DONE);
@@ -2358,6 +2378,7 @@
 					queued_entry);
 			if (vb) {
 				vb->v4l2_planes[0].bytesused = 0;
+				vb->v4l2_planes[0].data_offset = 0;
 				mutex_lock(&inst->bufq[OUTPUT_PORT].lock);
 				vb2_buffer_done(vb,
 						VB2_BUF_STATE_DONE);
diff --git a/drivers/media/platform/msm/vidc/q6_hfi.c b/drivers/media/platform/msm/vidc/q6_hfi.c
index 44c9613..7c99ec3 100644
--- a/drivers/media/platform/msm/vidc/q6_hfi.c
+++ b/drivers/media/platform/msm/vidc/q6_hfi.c
@@ -184,7 +184,8 @@
 		if (!rc)
 			hfi_process_msg_packet(device->callback,
 				device->device_id,
-				(struct vidc_hal_msg_pkt_hdr *) packet);
+				(struct vidc_hal_msg_pkt_hdr *) packet,
+				&device->sess_head, &device->session_lock);
 	} while (!rc);
 
 	if (rc != -ENODATA)
@@ -483,6 +484,7 @@
 	}
 
 	INIT_LIST_HEAD(&dev->sess_head);
+	mutex_init(&dev->session_lock);
 
 	if (!dev->event_queue.buffer) {
 		rc = q6_init_event_queue(dev);
@@ -566,7 +568,9 @@
 		rc = -EBADE;
 		goto err_session_init;
 	}
+	mutex_lock(&dev->session_lock);
 	list_add_tail(&new_session->list, &dev->sess_head);
+	mutex_unlock(&dev->session_lock);
 	return new_session;
 
 err_session_init:
@@ -629,7 +633,11 @@
 	sess_close = session;
 	dprintk(VIDC_DBG, "deleted the session: 0x%x",
 			sess_close->session_id);
+	mutex_lock(&((struct q6_hfi_device *)
+			sess_close->device)->session_lock);
 	list_del(&sess_close->list);
+	mutex_unlock(&((struct q6_hfi_device *)
+			sess_close->device)->session_lock);
 	kfree(sess_close);
 	return 0;
 }
diff --git a/drivers/media/platform/msm/vidc/q6_hfi.h b/drivers/media/platform/msm/vidc/q6_hfi.h
index 67aed5a..5f48a51 100644
--- a/drivers/media/platform/msm/vidc/q6_hfi.h
+++ b/drivers/media/platform/msm/vidc/q6_hfi.h
@@ -52,6 +52,7 @@
 	struct q6_resources resources;
 	struct msm_vidc_platform_resources *res;
 	void *apr;
+	struct mutex session_lock;
 };
 
 struct q6_apr_cmd_sys_init_packet {
diff --git a/drivers/media/platform/msm/vidc/venus_hfi.c b/drivers/media/platform/msm/vidc/venus_hfi.c
index 5416210..99c51bf 100644
--- a/drivers/media/platform/msm/vidc/venus_hfi.c
+++ b/drivers/media/platform/msm/vidc/venus_hfi.c
@@ -1045,6 +1045,7 @@
 	INIT_LIST_HEAD(&dev->sess_head);
 	mutex_init(&dev->read_lock);
 	mutex_init(&dev->write_lock);
+	mutex_init(&dev->session_lock);
 	venus_hfi_set_registers(dev);
 
 	if (!dev->hal_client) {
@@ -1484,7 +1485,10 @@
 	else if (session_type == 2)
 		new_session->is_decoder = 1;
 	new_session->device = dev;
+
+	mutex_lock(&dev->session_lock);
 	list_add_tail(&new_session->list, &dev->sess_head);
+	mutex_unlock(&dev->session_lock);
 
 	if (create_pkt_cmd_sys_session_init(&pkt, (u32)new_session,
 			session_type, codec_type)) {
@@ -1554,7 +1558,11 @@
 	sess_close = session;
 	dprintk(VIDC_DBG, "deleted the session: 0x%p",
 			sess_close);
+	mutex_lock(&((struct venus_hfi_device *)
+			sess_close->device)->session_lock);
 	list_del(&sess_close->list);
+	mutex_unlock(&((struct venus_hfi_device *)
+			sess_close->device)->session_lock);
 	kfree(sess_close);
 	return 0;
 }
@@ -1985,7 +1993,8 @@
 		while (!venus_hfi_iface_msgq_read(device, packet)) {
 			rc = hfi_process_msg_packet(device->callback,
 				device->device_id,
-				(struct vidc_hal_msg_pkt_hdr *) packet);
+				(struct vidc_hal_msg_pkt_hdr *) packet,
+				&device->sess_head, &device->session_lock);
 			if (rc == HFI_MSG_EVENT_NOTIFY)
 				venus_hfi_process_msg_event_notify(
 					device, (void *)packet);
diff --git a/drivers/media/platform/msm/vidc/venus_hfi.h b/drivers/media/platform/msm/vidc/venus_hfi.h
index a59a053..44cdf31 100644
--- a/drivers/media/platform/msm/vidc/venus_hfi.h
+++ b/drivers/media/platform/msm/vidc/venus_hfi.h
@@ -188,6 +188,7 @@
 	struct mutex read_lock;
 	struct mutex write_lock;
 	struct mutex clock_lock;
+	struct mutex session_lock;
 	msm_vidc_callback callback;
 	struct vidc_mem_addr iface_q_table;
 	struct vidc_mem_addr qdss;
diff --git a/drivers/media/platform/msm/vidc/vidc_hfi.h b/drivers/media/platform/msm/vidc/vidc_hfi.h
index 874738b..5059273 100644
--- a/drivers/media/platform/msm/vidc/vidc_hfi.h
+++ b/drivers/media/platform/msm/vidc/vidc_hfi.h
@@ -832,6 +832,7 @@
 };
 
 u32 hfi_process_msg_packet(msm_vidc_callback callback,
-		u32 device_id, struct vidc_hal_msg_pkt_hdr *msg_hdr);
+		u32 device_id, struct vidc_hal_msg_pkt_hdr *msg_hdr,
+		struct list_head *sessions, struct mutex *session_lock);
 #endif
 
diff --git a/drivers/media/platform/msm/wfd/wfd-ioctl.c b/drivers/media/platform/msm/wfd/wfd-ioctl.c
index 58e008d..e3c9b2a 100644
--- a/drivers/media/platform/msm/wfd/wfd-ioctl.c
+++ b/drivers/media/platform/msm/wfd/wfd-ioctl.c
@@ -134,10 +134,18 @@
 
 static void wfd_vidbuf_wait_prepare(struct vb2_queue *q)
 {
+	struct file *priv_data = (struct file *)(q->drv_priv);
+	struct wfd_inst *inst = file_to_inst(priv_data);
+
+	mutex_unlock(&inst->vb2_lock);
 }
 
 static void wfd_vidbuf_wait_finish(struct vb2_queue *q)
 {
+	struct file *priv_data = (struct file *)(q->drv_priv);
+	struct wfd_inst *inst = file_to_inst(priv_data);
+
+	mutex_lock(&inst->vb2_lock);
 }
 
 static unsigned long wfd_enc_addr_to_mdp_addr(struct wfd_inst *inst,
@@ -1026,10 +1034,9 @@
 
 	WFD_MSG_DBG("Waiting to dequeue buffer\n");
 
-	/* XXX: If we switch to non-blocking mode in the future,
-	 * we'll need to lock this with vb2_lock */
-	rc = vb2_dqbuf(&inst->vid_bufq, b, false /* blocking */);
-
+	mutex_lock(&inst->vb2_lock);
+	rc = vb2_dqbuf(&inst->vid_bufq, b, false);
+	mutex_unlock(&inst->vb2_lock);
 	if (rc)
 		WFD_MSG_ERR("Failed to dequeue buffer\n");
 	else
diff --git a/drivers/media/radio/radio-iris.c b/drivers/media/radio/radio-iris.c
index de6f1d5..da8f6b8 100644
--- a/drivers/media/radio/radio-iris.c
+++ b/drivers/media/radio/radio-iris.c
@@ -3209,7 +3209,7 @@
 			}
 			break;
 		case FM_TRANS:
-			if (!is_enable_tx_possible(radio) != 0)
+			if (is_enable_tx_possible(radio) != 0)
 				return -EINVAL;
 			radio->mode = FM_TRANS_TURNING_ON;
 			retval = hci_cmd(HCI_FM_ENABLE_TRANS_CMD,
diff --git a/drivers/media/video/v4l2-ctrls.c b/drivers/media/video/v4l2-ctrls.c
index 3222ea0..4a8d974 100644
--- a/drivers/media/video/v4l2-ctrls.c
+++ b/drivers/media/video/v4l2-ctrls.c
@@ -595,6 +595,8 @@
 	case V4L2_CID_MPEG_VIDC_VIDEO_AIR_MBS: return "Intra Refresh AIR MBS";
 	case V4L2_CID_MPEG_VIDC_VIDEO_AIR_REF: return "Intra Refresh AIR REF";
 	case V4L2_CID_MPEG_VIDC_VIDEO_CIR_MBS: return "Intra Refresh CIR MBS";
+	case V4L2_CID_MPEG_VIDC_VIDEO_VP8_PROFILE_LEVEL:
+		return "VP8 Profile Level";
 
 	/* CAMERA controls */
 	/* Keep the order of the 'case's the same as in videodev2.h! */
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index c99bed1..75a76c0 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -75,11 +75,14 @@
 obj-$(CONFIG_MCP_UCB1200)	+= ucb1x00-core.o
 obj-$(CONFIG_MCP_UCB1200_TS)	+= ucb1x00-ts.o
 
-obj-$(CONFIG_WCD9310_CODEC)       += wcd9xxx-core.o wcd9xxx-irq.o wcd9xxx-slimslave.o
-obj-$(CONFIG_WCD9304_CODEC)       += wcd9xxx-core.o wcd9xxx-irq.o wcd9xxx-slimslave.o
-obj-$(CONFIG_WCD9320_CODEC)       += wcd9xxx-core.o wcd9xxx-irq.o wcd9xxx-slimslave.o
-obj-$(CONFIG_WCD9306_CODEC)       += wcd9xxx-core.o wcd9xxx-irq.o wcd9xxx-slimslave.o
-
+obj-$(CONFIG_WCD9310_CODEC)	+= wcd9xxx-core.o wcd9xxx-irq.o \
+						wcd9xxx-slimslave.o wcd9xxx-core-resource.o
+obj-$(CONFIG_WCD9304_CODEC)	+= wcd9xxx-core.o wcd9xxx-irq.o \
+						wcd9xxx-slimslave.o wcd9xxx-core-resource.o
+obj-$(CONFIG_WCD9320_CODEC)	+= wcd9xxx-core.o wcd9xxx-irq.o wcd9xxx-slimslave.o\
+						wcd9xxx-core-resource.o
+obj-$(CONFIG_WCD9306_CODEC)	+= wcd9xxx-core.o wcd9xxx-irq.o\
+						wcd9xxx-slimslave.o wcd9xxx-core-resource.o
 
 ifeq ($(CONFIG_SA1100_ASSABET),y)
 obj-$(CONFIG_MCP_UCB1200)	+= ucb1x00-assabet.o
diff --git a/drivers/mfd/wcd9xxx-core-resource.c b/drivers/mfd/wcd9xxx-core-resource.c
new file mode 100644
index 0000000..1791d72
--- /dev/null
+++ b/drivers/mfd/wcd9xxx-core-resource.c
@@ -0,0 +1,194 @@
+/* Copyright (c) 2013, 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/kernel.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/wait.h>
+#include <linux/sched.h>
+#include <linux/mfd/wcd9xxx/core-resource.h>
+
+
+static enum wcd9xxx_intf_status wcd9xxx_intf = -1;
+
+int wcd9xxx_core_irq_init(
+	struct wcd9xxx_core_resource *wcd9xxx_core_res)
+{
+	int ret = 0;
+
+	if (wcd9xxx_core_res->irq != 1) {
+		ret = wcd9xxx_irq_init(wcd9xxx_core_res);
+		if (ret)
+			pr_err("IRQ initialization failed\n");
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL(wcd9xxx_core_irq_init);
+
+int wcd9xxx_initialize_irq(
+	struct wcd9xxx_core_resource *wcd9xxx_core_res,
+	unsigned int irq,
+	unsigned int irq_base)
+{
+	wcd9xxx_core_res->irq = irq;
+	wcd9xxx_core_res->irq_base = irq_base;
+
+	return 0;
+}
+EXPORT_SYMBOL(wcd9xxx_initialize_irq);
+
+int wcd9xxx_core_res_init(
+	struct wcd9xxx_core_resource *wcd9xxx_core_res,
+	int num_irqs, int num_irq_regs,
+	int (*codec_read)(struct wcd9xxx_core_resource*, unsigned short),
+	int (*codec_write)(struct wcd9xxx_core_resource*, unsigned short, u8),
+	int (*codec_bulk_read) (struct wcd9xxx_core_resource*, unsigned short,
+							int, u8*))
+{
+	mutex_init(&wcd9xxx_core_res->pm_lock);
+	wcd9xxx_core_res->wlock_holders = 0;
+	wcd9xxx_core_res->pm_state = WCD9XXX_PM_SLEEPABLE;
+	init_waitqueue_head(&wcd9xxx_core_res->pm_wq);
+	pm_qos_add_request(&wcd9xxx_core_res->pm_qos_req,
+				PM_QOS_CPU_DMA_LATENCY,
+				PM_QOS_DEFAULT_VALUE);
+
+	wcd9xxx_core_res->codec_reg_read = codec_read;
+	wcd9xxx_core_res->codec_reg_write = codec_write;
+	wcd9xxx_core_res->codec_bulk_read = codec_bulk_read;
+	wcd9xxx_core_res->num_irqs = num_irqs;
+	wcd9xxx_core_res->num_irq_regs = num_irq_regs;
+
+	pr_info("%s: num_irqs = %d, num_irq_regs = %d\n",
+			__func__, wcd9xxx_core_res->num_irqs,
+			wcd9xxx_core_res->num_irq_regs);
+
+	return 0;
+}
+EXPORT_SYMBOL(wcd9xxx_core_res_init);
+
+void wcd9xxx_core_res_deinit(struct wcd9xxx_core_resource *wcd9xxx_core_res)
+{
+	pm_qos_remove_request(&wcd9xxx_core_res->pm_qos_req);
+	mutex_destroy(&wcd9xxx_core_res->pm_lock);
+	wcd9xxx_core_res->codec_reg_read = NULL;
+	wcd9xxx_core_res->codec_reg_write = NULL;
+	wcd9xxx_core_res->codec_bulk_read = NULL;
+}
+EXPORT_SYMBOL(wcd9xxx_core_res_deinit);
+
+enum wcd9xxx_pm_state wcd9xxx_pm_cmpxchg(
+		struct wcd9xxx_core_resource *wcd9xxx_core_res,
+		enum wcd9xxx_pm_state o,
+		enum wcd9xxx_pm_state n)
+{
+	enum wcd9xxx_pm_state old;
+	mutex_lock(&wcd9xxx_core_res->pm_lock);
+	old = wcd9xxx_core_res->pm_state;
+	if (old == o)
+		wcd9xxx_core_res->pm_state = n;
+	mutex_unlock(&wcd9xxx_core_res->pm_lock);
+	return old;
+}
+EXPORT_SYMBOL(wcd9xxx_pm_cmpxchg);
+
+int wcd9xxx_core_res_suspend(
+	struct wcd9xxx_core_resource *wcd9xxx_core_res,
+	pm_message_t pmesg)
+{
+	int ret = 0;
+
+	pr_debug("%s: enter\n", __func__);
+	/*
+	 * pm_qos_update_request() can be called after this suspend chain call
+	 * started. thus suspend can be called while lock is being held
+	 */
+	mutex_lock(&wcd9xxx_core_res->pm_lock);
+	if (wcd9xxx_core_res->pm_state == WCD9XXX_PM_SLEEPABLE) {
+		pr_debug("%s: suspending system, state %d, wlock %d\n",
+			 __func__, wcd9xxx_core_res->pm_state,
+			 wcd9xxx_core_res->wlock_holders);
+		wcd9xxx_core_res->pm_state = WCD9XXX_PM_ASLEEP;
+	} else if (wcd9xxx_core_res->pm_state == WCD9XXX_PM_AWAKE) {
+		/*
+		 * unlock to wait for pm_state == WCD9XXX_PM_SLEEPABLE
+		 * then set to WCD9XXX_PM_ASLEEP
+		 */
+		pr_debug("%s: waiting to suspend system, state %d, wlock %d\n",
+			 __func__, wcd9xxx_core_res->pm_state,
+			 wcd9xxx_core_res->wlock_holders);
+		mutex_unlock(&wcd9xxx_core_res->pm_lock);
+		if (!(wait_event_timeout(wcd9xxx_core_res->pm_wq,
+					 wcd9xxx_pm_cmpxchg(wcd9xxx_core_res,
+						  WCD9XXX_PM_SLEEPABLE,
+						  WCD9XXX_PM_ASLEEP) ==
+							WCD9XXX_PM_SLEEPABLE,
+					 HZ))) {
+			pr_debug("%s: suspend failed state %d, wlock %d\n",
+				 __func__, wcd9xxx_core_res->pm_state,
+				 wcd9xxx_core_res->wlock_holders);
+			ret = -EBUSY;
+		} else {
+			pr_debug("%s: done, state %d, wlock %d\n", __func__,
+				 wcd9xxx_core_res->pm_state,
+				 wcd9xxx_core_res->wlock_holders);
+		}
+		mutex_lock(&wcd9xxx_core_res->pm_lock);
+	} else if (wcd9xxx_core_res->pm_state == WCD9XXX_PM_ASLEEP) {
+		pr_warn("%s: system is already suspended, state %d, wlock %dn",
+			__func__, wcd9xxx_core_res->pm_state,
+			wcd9xxx_core_res->wlock_holders);
+	}
+	mutex_unlock(&wcd9xxx_core_res->pm_lock);
+
+	return ret;
+}
+EXPORT_SYMBOL(wcd9xxx_core_res_suspend);
+
+int wcd9xxx_core_res_resume(
+	struct wcd9xxx_core_resource *wcd9xxx_core_res)
+{
+	int ret = 0;
+
+	pr_debug("%s: enter\n", __func__);
+	mutex_lock(&wcd9xxx_core_res->pm_lock);
+	if (wcd9xxx_core_res->pm_state == WCD9XXX_PM_ASLEEP) {
+		pr_debug("%s: resuming system, state %d, wlock %d\n", __func__,
+				wcd9xxx_core_res->pm_state,
+				wcd9xxx_core_res->wlock_holders);
+		wcd9xxx_core_res->pm_state = WCD9XXX_PM_SLEEPABLE;
+	} else {
+		pr_warn("%s: system is already awake, state %d wlock %d\n",
+				__func__, wcd9xxx_core_res->pm_state,
+				wcd9xxx_core_res->wlock_holders);
+	}
+	mutex_unlock(&wcd9xxx_core_res->pm_lock);
+	wake_up_all(&wcd9xxx_core_res->pm_wq);
+
+	return ret;
+}
+EXPORT_SYMBOL(wcd9xxx_core_res_resume);
+
+enum wcd9xxx_intf_status wcd9xxx_get_intf_type(void)
+{
+	return wcd9xxx_intf;
+}
+EXPORT_SYMBOL(wcd9xxx_get_intf_type);
+
+void wcd9xxx_set_intf_type(enum wcd9xxx_intf_status intf_status)
+{
+	wcd9xxx_intf = intf_status;
+}
+EXPORT_SYMBOL(wcd9xxx_set_intf_type);
diff --git a/drivers/mfd/wcd9xxx-core.c b/drivers/mfd/wcd9xxx-core.c
index 9c35a55..d718c40 100644
--- a/drivers/mfd/wcd9xxx-core.c
+++ b/drivers/mfd/wcd9xxx-core.c
@@ -19,6 +19,7 @@
 #include <linux/mfd/core.h>
 #include <linux/mfd/wcd9xxx/wcd9xxx-slimslave.h>
 #include <linux/mfd/wcd9xxx/core.h>
+#include <linux/mfd/wcd9xxx/core-resource.h>
 #include <linux/mfd/wcd9xxx/pdata.h>
 #include <linux/mfd/wcd9xxx/wcd9xxx_registers.h>
 
@@ -45,6 +46,9 @@
 #define WCD9XXX_I2C_DIGITAL_1	2
 #define WCD9XXX_I2C_DIGITAL_2	3
 
+#define ONDEMAND_REGULATOR true
+#define STATIC_REGULATOR (!ONDEMAND_REGULATOR)
+
 /* Number of return values needs to be checked for each
  * registration of Slimbus of I2C bus for each codec
  */
@@ -65,7 +69,6 @@
 static struct wcd9xxx_pdata *wcd9xxx_populate_dt_pdata(struct device *dev);
 
 struct wcd9xxx_i2c wcd9xxx_modules[MAX_WCD9XXX_DEVICE];
-static int wcd9xxx_intf = -1;
 
 static int wcd9xxx_read(struct wcd9xxx *wcd9xxx, unsigned short reg,
 		       int bytes, void *dest, bool interface_reg)
@@ -89,7 +92,9 @@
 
 	return 0;
 }
-int wcd9xxx_reg_read(struct wcd9xxx *wcd9xxx, unsigned short reg)
+static int __wcd9xxx_reg_read(
+	struct wcd9xxx *wcd9xxx,
+	unsigned short reg)
 {
 	u8 val;
 	int ret;
@@ -103,7 +108,16 @@
 	else
 		return val;
 }
-EXPORT_SYMBOL_GPL(wcd9xxx_reg_read);
+
+int wcd9xxx_reg_read(
+	struct wcd9xxx_core_resource *core_res,
+	unsigned short reg)
+{
+	struct wcd9xxx *wcd9xxx = (struct wcd9xxx *) core_res->parent;
+	return __wcd9xxx_reg_read(wcd9xxx, reg);
+
+}
+EXPORT_SYMBOL(wcd9xxx_reg_read);
 
 static int wcd9xxx_write(struct wcd9xxx *wcd9xxx, unsigned short reg,
 			int bytes, void *src, bool interface_reg)
@@ -122,8 +136,9 @@
 	return wcd9xxx->write_dev(wcd9xxx, reg, bytes, src, interface_reg);
 }
 
-int wcd9xxx_reg_write(struct wcd9xxx *wcd9xxx, unsigned short reg,
-		     u8 val)
+static int __wcd9xxx_reg_write(
+	struct wcd9xxx *wcd9xxx,
+	unsigned short reg, u8 val)
 {
 	int ret;
 
@@ -133,7 +148,15 @@
 
 	return ret;
 }
-EXPORT_SYMBOL_GPL(wcd9xxx_reg_write);
+
+int wcd9xxx_reg_write(
+	struct wcd9xxx_core_resource *core_res,
+	unsigned short reg, u8 val)
+{
+	struct wcd9xxx *wcd9xxx = (struct wcd9xxx *) core_res->parent;
+	return __wcd9xxx_reg_write(wcd9xxx, reg, val);
+}
+EXPORT_SYMBOL(wcd9xxx_reg_write);
 
 static u8 wcd9xxx_pgd_la;
 static u8 wcd9xxx_inf_la;
@@ -152,7 +175,7 @@
 	else
 		return val;
 }
-EXPORT_SYMBOL_GPL(wcd9xxx_interface_reg_read);
+EXPORT_SYMBOL(wcd9xxx_interface_reg_read);
 
 int wcd9xxx_interface_reg_write(struct wcd9xxx *wcd9xxx, unsigned short reg,
 		     u8 val)
@@ -165,37 +188,54 @@
 
 	return ret;
 }
-EXPORT_SYMBOL_GPL(wcd9xxx_interface_reg_write);
+EXPORT_SYMBOL(wcd9xxx_interface_reg_write);
 
-int wcd9xxx_bulk_read(struct wcd9xxx *wcd9xxx, unsigned short reg,
-		     int count, u8 *buf)
+static int __wcd9xxx_bulk_read(
+	struct wcd9xxx *wcd9xxx,
+	unsigned short reg,
+	int count, u8 *buf)
 {
 	int ret;
 
 	mutex_lock(&wcd9xxx->io_lock);
-
 	ret = wcd9xxx_read(wcd9xxx, reg, count, buf, false);
-
 	mutex_unlock(&wcd9xxx->io_lock);
 
 	return ret;
 }
-EXPORT_SYMBOL_GPL(wcd9xxx_bulk_read);
 
-int wcd9xxx_bulk_write(struct wcd9xxx *wcd9xxx, unsigned short reg,
+int wcd9xxx_bulk_read(
+	struct wcd9xxx_core_resource *core_res,
+	unsigned short reg,
+	int count, u8 *buf)
+{
+	struct wcd9xxx *wcd9xxx =
+			(struct wcd9xxx *) core_res->parent;
+	return __wcd9xxx_bulk_read(wcd9xxx, reg, count, buf);
+}
+EXPORT_SYMBOL(wcd9xxx_bulk_read);
+
+static int __wcd9xxx_bulk_write(struct wcd9xxx *wcd9xxx, unsigned short reg,
 		     int count, u8 *buf)
 {
 	int ret;
 
 	mutex_lock(&wcd9xxx->io_lock);
-
 	ret = wcd9xxx_write(wcd9xxx, reg, count, buf, false);
-
 	mutex_unlock(&wcd9xxx->io_lock);
 
 	return ret;
 }
-EXPORT_SYMBOL_GPL(wcd9xxx_bulk_write);
+
+int wcd9xxx_bulk_write(
+	struct wcd9xxx_core_resource *core_res,
+	unsigned short reg, int count, u8 *buf)
+{
+	struct wcd9xxx *wcd9xxx =
+			(struct wcd9xxx *) core_res->parent;
+	return __wcd9xxx_bulk_write(wcd9xxx, reg, count, buf);
+}
+EXPORT_SYMBOL(wcd9xxx_bulk_write);
 
 static int wcd9xxx_slim_read_device(struct wcd9xxx *wcd9xxx, unsigned short reg,
 				int bytes, void *dest, bool interface)
@@ -334,19 +374,19 @@
 
 static void wcd9xxx_bring_up(struct wcd9xxx *wcd9xxx)
 {
-	wcd9xxx_reg_write(wcd9xxx, WCD9XXX_A_LEAKAGE_CTL, 0x4);
-	wcd9xxx_reg_write(wcd9xxx, WCD9XXX_A_CDC_CTL, 0);
+	__wcd9xxx_reg_write(wcd9xxx, WCD9XXX_A_LEAKAGE_CTL, 0x4);
+	__wcd9xxx_reg_write(wcd9xxx, WCD9XXX_A_CDC_CTL, 0);
 	usleep_range(5000, 5000);
-	wcd9xxx_reg_write(wcd9xxx, WCD9XXX_A_CDC_CTL, 3);
-	wcd9xxx_reg_write(wcd9xxx, WCD9XXX_A_LEAKAGE_CTL, 3);
+	__wcd9xxx_reg_write(wcd9xxx, WCD9XXX_A_CDC_CTL, 3);
+	__wcd9xxx_reg_write(wcd9xxx, WCD9XXX_A_LEAKAGE_CTL, 3);
 }
 
 static void wcd9xxx_bring_down(struct wcd9xxx *wcd9xxx)
 {
-	wcd9xxx_reg_write(wcd9xxx, WCD9XXX_A_LEAKAGE_CTL, 0x7);
-	wcd9xxx_reg_write(wcd9xxx, WCD9XXX_A_LEAKAGE_CTL, 0x6);
-	wcd9xxx_reg_write(wcd9xxx, WCD9XXX_A_LEAKAGE_CTL, 0xe);
-	wcd9xxx_reg_write(wcd9xxx, WCD9XXX_A_LEAKAGE_CTL, 0x8);
+	__wcd9xxx_reg_write(wcd9xxx, WCD9XXX_A_LEAKAGE_CTL, 0x7);
+	__wcd9xxx_reg_write(wcd9xxx, WCD9XXX_A_LEAKAGE_CTL, 0x6);
+	__wcd9xxx_reg_write(wcd9xxx, WCD9XXX_A_LEAKAGE_CTL, 0xe);
+	__wcd9xxx_reg_write(wcd9xxx, WCD9XXX_A_LEAKAGE_CTL, 0x8);
 }
 
 static int wcd9xxx_reset(struct wcd9xxx *wcd9xxx)
@@ -385,13 +425,13 @@
 	int i, rc;
 	const struct wcd9xxx_codec_type *c, *d = NULL;
 
-	rc = wcd9xxx_bulk_read(wcd9xxx, WCD9XXX_A_CHIP_ID_BYTE_0,
+	rc = __wcd9xxx_bulk_read(wcd9xxx, WCD9XXX_A_CHIP_ID_BYTE_0,
 			       sizeof(wcd9xxx->id_minor),
 			       (u8 *)&wcd9xxx->id_minor);
 	if (rc < 0)
 		goto exit;
 
-	rc = wcd9xxx_bulk_read(wcd9xxx, WCD9XXX_A_CHIP_ID_BYTE_2,
+	rc = __wcd9xxx_bulk_read(wcd9xxx, WCD9XXX_A_CHIP_ID_BYTE_2,
 			       sizeof(wcd9xxx->id_major),
 			       (u8 *)&wcd9xxx->id_major);
 	if (rc < 0)
@@ -431,7 +471,8 @@
 		if (d->version > -1) {
 			*version = d->version;
 		} else {
-			rc = wcd9xxx_reg_read(wcd9xxx, WCD9XXX_A_CHIP_VERSION);
+			rc = __wcd9xxx_reg_read(wcd9xxx,
+							WCD9XXX_A_CHIP_VERSION);
 			if (rc < 0) {
 				d = NULL;
 				goto exit;
@@ -447,24 +488,94 @@
 	return d;
 }
 
+static int wcd9xxx_num_irq_regs(const struct wcd9xxx *wcd9xxx)
+{
+	return (wcd9xxx->codec_type->num_irqs / 8) +
+		((wcd9xxx->codec_type->num_irqs % 8) ? 1 : 0);
+}
+
+/*
+ * Interrupt table for v1 corresponds to newer version
+ * codecs (wcd9304 and wcd9310)
+ */
+static const struct intr_data intr_tbl_v1[] = {
+	{WCD9XXX_IRQ_SLIMBUS, false},
+	{WCD9XXX_IRQ_MBHC_INSERTION, true},
+	{WCD9XXX_IRQ_MBHC_POTENTIAL, true},
+	{WCD9XXX_IRQ_MBHC_RELEASE, true},
+	{WCD9XXX_IRQ_MBHC_PRESS, true},
+	{WCD9XXX_IRQ_MBHC_SHORT_TERM, true},
+	{WCD9XXX_IRQ_MBHC_REMOVAL, true},
+	{WCD9XXX_IRQ_BG_PRECHARGE, false},
+	{WCD9XXX_IRQ_PA1_STARTUP, false},
+	{WCD9XXX_IRQ_PA2_STARTUP, false},
+	{WCD9XXX_IRQ_PA3_STARTUP, false},
+	{WCD9XXX_IRQ_PA4_STARTUP, false},
+	{WCD9XXX_IRQ_PA5_STARTUP, false},
+	{WCD9XXX_IRQ_MICBIAS1_PRECHARGE, false},
+	{WCD9XXX_IRQ_MICBIAS2_PRECHARGE, false},
+	{WCD9XXX_IRQ_MICBIAS3_PRECHARGE, false},
+	{WCD9XXX_IRQ_HPH_PA_OCPL_FAULT, false},
+	{WCD9XXX_IRQ_HPH_PA_OCPR_FAULT, false},
+	{WCD9XXX_IRQ_EAR_PA_OCPL_FAULT, false},
+	{WCD9XXX_IRQ_HPH_L_PA_STARTUP, false},
+	{WCD9XXX_IRQ_HPH_R_PA_STARTUP, false},
+	{WCD9320_IRQ_EAR_PA_STARTUP, false},
+	{WCD9XXX_IRQ_RESERVED_0, false},
+	{WCD9XXX_IRQ_RESERVED_1, false},
+};
+
+/*
+ * Interrupt table for v2 corresponds to newer version
+ * codecs (wcd9320 and wcd9306)
+ */
+static const struct intr_data intr_tbl_v2[] = {
+	{WCD9XXX_IRQ_SLIMBUS, false},
+	{WCD9XXX_IRQ_MBHC_INSERTION, true},
+	{WCD9XXX_IRQ_MBHC_POTENTIAL, true},
+	{WCD9XXX_IRQ_MBHC_RELEASE, true},
+	{WCD9XXX_IRQ_MBHC_PRESS, true},
+	{WCD9XXX_IRQ_MBHC_SHORT_TERM, true},
+	{WCD9XXX_IRQ_MBHC_REMOVAL, true},
+	{WCD9320_IRQ_MBHC_JACK_SWITCH, true},
+	{WCD9306_IRQ_MBHC_JACK_SWITCH, true},
+	{WCD9XXX_IRQ_BG_PRECHARGE, false},
+	{WCD9XXX_IRQ_PA1_STARTUP, false},
+	{WCD9XXX_IRQ_PA2_STARTUP, false},
+	{WCD9XXX_IRQ_PA3_STARTUP, false},
+	{WCD9XXX_IRQ_PA4_STARTUP, false},
+	{WCD9XXX_IRQ_PA5_STARTUP, false},
+	{WCD9XXX_IRQ_MICBIAS1_PRECHARGE, false},
+	{WCD9XXX_IRQ_MICBIAS2_PRECHARGE, false},
+	{WCD9XXX_IRQ_MICBIAS3_PRECHARGE, false},
+	{WCD9XXX_IRQ_HPH_PA_OCPL_FAULT, false},
+	{WCD9XXX_IRQ_HPH_PA_OCPR_FAULT, false},
+	{WCD9XXX_IRQ_EAR_PA_OCPL_FAULT, false},
+	{WCD9XXX_IRQ_HPH_L_PA_STARTUP, false},
+	{WCD9XXX_IRQ_HPH_R_PA_STARTUP, false},
+	{WCD9320_IRQ_EAR_PA_STARTUP, false},
+	{WCD9XXX_IRQ_RESERVED_0, false},
+	{WCD9XXX_IRQ_RESERVED_1, false},
+	{WCD9XXX_IRQ_MAD_AUDIO, false},
+	{WCD9XXX_IRQ_MAD_BEACON, false},
+	{WCD9XXX_IRQ_MAD_ULTRASOUND, false},
+	{WCD9XXX_IRQ_SPEAKER_CLIPPING, false},
+	{WCD9XXX_IRQ_VBAT_MONITOR_ATTACK, false},
+	{WCD9XXX_IRQ_VBAT_MONITOR_RELEASE, false},
+	{WCD9XXX_IRQ_RESERVED_2, false},
+};
+
 static int wcd9xxx_device_init(struct wcd9xxx *wcd9xxx)
 {
-	int ret;
+	int ret = 0;
 	u8 version;
 	const struct wcd9xxx_codec_type *found;
+	struct wcd9xxx_core_resource *core_res = &wcd9xxx->core_res;
 
 	mutex_init(&wcd9xxx->io_lock);
 	mutex_init(&wcd9xxx->xfer_lock);
 
-	mutex_init(&wcd9xxx->pm_lock);
-	wcd9xxx->wlock_holders = 0;
-	wcd9xxx->pm_state = WCD9XXX_PM_SLEEPABLE;
-	init_waitqueue_head(&wcd9xxx->pm_wq);
-	pm_qos_add_request(&wcd9xxx->pm_qos_req, PM_QOS_CPU_DMA_LATENCY,
-				PM_QOS_DEFAULT_VALUE);
-
 	dev_set_drvdata(wcd9xxx->dev, wcd9xxx);
-
 	wcd9xxx_bring_up(wcd9xxx);
 
 	found = wcd9xxx_check_codec_type(wcd9xxx, &version);
@@ -476,27 +587,46 @@
 		wcd9xxx->version = version;
 	}
 
-	if (wcd9xxx->irq != -1) {
-		ret = wcd9xxx_irq_init(wcd9xxx);
-		if (ret) {
-			pr_err("IRQ initialization failed\n");
-			goto err;
-		}
+	core_res->parent = wcd9xxx;
+	core_res->dev = wcd9xxx->dev;
+
+	if (wcd9xxx->codec_type->id_major == TABLA_MAJOR
+		|| wcd9xxx->codec_type->id_major == SITAR_MAJOR) {
+		core_res->intr_table = intr_tbl_v1;
+		core_res->intr_table_size = ARRAY_SIZE(intr_tbl_v1);
+	} else {
+		core_res->intr_table = intr_tbl_v2;
+		core_res->intr_table_size = ARRAY_SIZE(intr_tbl_v2);
 	}
 
+	wcd9xxx_core_res_init(&wcd9xxx->core_res,
+				wcd9xxx->codec_type->num_irqs,
+				wcd9xxx_num_irq_regs(wcd9xxx),
+				wcd9xxx_reg_read, wcd9xxx_reg_write,
+				wcd9xxx_bulk_read);
+
+	if (wcd9xxx_core_irq_init(&wcd9xxx->core_res))
+		goto err;
+
 	ret = mfd_add_devices(wcd9xxx->dev, -1, found->dev, found->size,
 			      NULL, 0);
 	if (ret != 0) {
 		dev_err(wcd9xxx->dev, "Failed to add children: %d\n", ret);
 		goto err_irq;
 	}
+
+	ret = device_init_wakeup(wcd9xxx->dev, true);
+	if (ret) {
+		dev_err(wcd9xxx->dev, "Device wakeup init failed: %d\n", ret);
+		goto err_irq;
+	}
+
 	return ret;
 err_irq:
-	wcd9xxx_irq_exit(wcd9xxx);
+	wcd9xxx_irq_exit(&wcd9xxx->core_res);
 err:
 	wcd9xxx_bring_down(wcd9xxx);
-	pm_qos_remove_request(&wcd9xxx->pm_qos_req);
-	mutex_destroy(&wcd9xxx->pm_lock);
+	wcd9xxx_core_res_deinit(&wcd9xxx->core_res);
 	mutex_destroy(&wcd9xxx->io_lock);
 	mutex_destroy(&wcd9xxx->xfer_lock);
 	return ret;
@@ -504,14 +634,14 @@
 
 static void wcd9xxx_device_exit(struct wcd9xxx *wcd9xxx)
 {
-	wcd9xxx_irq_exit(wcd9xxx);
+	device_init_wakeup(wcd9xxx->dev, false);
+	wcd9xxx_irq_exit(&wcd9xxx->core_res);
 	wcd9xxx_bring_down(wcd9xxx);
 	wcd9xxx_free_reset(wcd9xxx);
-	mutex_destroy(&wcd9xxx->pm_lock);
-	pm_qos_remove_request(&wcd9xxx->pm_qos_req);
+	wcd9xxx_core_res_deinit(&wcd9xxx->core_res);
 	mutex_destroy(&wcd9xxx->io_lock);
 	mutex_destroy(&wcd9xxx->xfer_lock);
-	if (wcd9xxx_intf == WCD9XXX_INTERFACE_TYPE_SLIMBUS)
+	if (wcd9xxx_get_intf_type() == WCD9XXX_INTERFACE_TYPE_SLIMBUS)
 		slim_remove_device(wcd9xxx->slim_slave);
 	kfree(wcd9xxx);
 }
@@ -736,13 +866,6 @@
 	kfree(wcd9xxx->supplies);
 }
 
-enum wcd9xxx_intf_status wcd9xxx_get_intf_type(void)
-{
-	return wcd9xxx_intf;
-}
-
-EXPORT_SYMBOL_GPL(wcd9xxx_get_intf_type);
-
 struct wcd9xxx_i2c *get_i2c_wcd9xxx_device_info(u16 reg)
 {
 	u16 mask = 0x0f00;
@@ -894,13 +1017,16 @@
 	int ret = 0;
 	int wcd9xx_index = 0;
 	struct device *dev;
+	int intf_type;
 
-	pr_debug("%s: interface status %d\n", __func__, wcd9xxx_intf);
-	if (wcd9xxx_intf == WCD9XXX_INTERFACE_TYPE_SLIMBUS) {
+	intf_type = wcd9xxx_get_intf_type();
+
+	pr_debug("%s: interface status %d\n", __func__, intf_type);
+	if (intf_type == WCD9XXX_INTERFACE_TYPE_SLIMBUS) {
 		dev_dbg(&client->dev, "%s:Codec is detected in slimbus mode\n",
 			__func__);
 		return -ENODEV;
-	} else if (wcd9xxx_intf == WCD9XXX_INTERFACE_TYPE_I2C) {
+	} else if (intf_type == WCD9XXX_INTERFACE_TYPE_I2C) {
 		ret = wcd9xxx_i2c_get_client_index(client, &wcd9xx_index);
 		if (ret != 0)
 			dev_err(&client->dev, "%s: I2C set codec I2C\n"
@@ -912,7 +1038,7 @@
 			wcd9xxx_modules[wcd9xx_index].client = client;
 		}
 		return ret;
-	} else if (wcd9xxx_intf == WCD9XXX_INTERFACE_TYPE_PROBING) {
+	} else if (intf_type == WCD9XXX_INTERFACE_TYPE_PROBING) {
 		dev = &client->dev;
 		if (client->dev.of_node) {
 			dev_dbg(&client->dev, "%s:Platform data\n"
@@ -979,10 +1105,9 @@
 		wcd9xxx_modules[wcd9xx_index].client = client;
 		wcd9xxx->read_dev = wcd9xxx_i2c_read;
 		wcd9xxx->write_dev = wcd9xxx_i2c_write;
-		if (!wcd9xxx->dev->of_node) {
-			wcd9xxx->irq = pdata->irq;
-			wcd9xxx->irq_base = pdata->irq_base;
-		}
+		if (!wcd9xxx->dev->of_node)
+			wcd9xxx_initialize_irq(&wcd9xxx->core_res,
+					pdata->irq, pdata->irq_base);
 
 		ret = wcd9xxx_device_init(wcd9xxx);
 		if (ret) {
@@ -998,7 +1123,7 @@
 		if (val != wcd9xxx->codec_type->i2c_chip_status)
 			pr_err("%s: unknown chip status 0x%x\n", __func__, val);
 
-		wcd9xxx_intf = WCD9XXX_INTERFACE_TYPE_I2C;
+		wcd9xxx_set_intf_type(WCD9XXX_INTERFACE_TYPE_I2C);
 
 		return ret;
 	} else
@@ -1277,17 +1402,18 @@
 	}
 
 	ret = wcd9xxx_process_supplies(dev, pdata, static_prop_name,
-				static_cnt, false, 0);
+				static_cnt, STATIC_REGULATOR, 0);
 	if (ret)
 		goto err;
 
 	ret = wcd9xxx_process_supplies(dev, pdata, ond_prop_name,
-				ond_cnt, true, static_cnt);
+				ond_cnt, ONDEMAND_REGULATOR, static_cnt);
 	if (ret)
 		goto err;
 
 	ret = wcd9xxx_process_supplies(dev, pdata, cp_supplies_name,
-				cp_supplies_cnt, false, static_cnt + ond_cnt);
+				cp_supplies_cnt, ONDEMAND_REGULATOR,
+				static_cnt + ond_cnt);
 	if (ret)
 		goto err;
 
@@ -1380,8 +1506,11 @@
 	struct wcd9xxx *wcd9xxx;
 	struct wcd9xxx_pdata *pdata;
 	int ret = 0;
+	int intf_type;
 
-	if (wcd9xxx_intf == WCD9XXX_INTERFACE_TYPE_I2C) {
+	intf_type = wcd9xxx_get_intf_type();
+
+	if (intf_type == WCD9XXX_INTERFACE_TYPE_I2C) {
 		dev_dbg(&slim->dev, "%s:Codec is detected in I2C mode\n",
 			__func__);
 		return -ENODEV;
@@ -1459,10 +1588,9 @@
 	wcd9xxx->write_dev = wcd9xxx_slim_write_device;
 	wcd9xxx_pgd_la = wcd9xxx->slim->laddr;
 	wcd9xxx->slim_slave = &pdata->slimbus_slave_device;
-	if (!wcd9xxx->dev->of_node) {
-		wcd9xxx->irq = pdata->irq;
-		wcd9xxx->irq_base = pdata->irq_base;
-	}
+	if (!wcd9xxx->dev->of_node)
+		wcd9xxx_initialize_irq(&wcd9xxx->core_res,
+					pdata->irq, pdata->irq_base);
 
 	ret = slim_add_device(slim->ctrl, wcd9xxx->slim_slave);
 	if (ret) {
@@ -1480,7 +1608,7 @@
 		goto err_slim_add;
 	}
 	wcd9xxx_inf_la = wcd9xxx->slim_slave->laddr;
-	wcd9xxx_intf = WCD9XXX_INTERFACE_TYPE_SLIMBUS;
+	wcd9xxx_set_intf_type(WCD9XXX_INTERFACE_TYPE_SLIMBUS);
 
 	ret = wcd9xxx_device_init(wcd9xxx);
 	if (ret) {
@@ -1534,29 +1662,10 @@
 	return 0;
 }
 
-static int wcd9xxx_resume(struct wcd9xxx *wcd9xxx)
-{
-	int ret = 0;
-
-	pr_debug("%s: enter\n", __func__);
-	mutex_lock(&wcd9xxx->pm_lock);
-	if (wcd9xxx->pm_state == WCD9XXX_PM_ASLEEP) {
-		pr_debug("%s: resuming system, state %d, wlock %d\n", __func__,
-			 wcd9xxx->pm_state, wcd9xxx->wlock_holders);
-		wcd9xxx->pm_state = WCD9XXX_PM_SLEEPABLE;
-	} else {
-		pr_warn("%s: system is already awake, state %d wlock %d\n",
-			__func__, wcd9xxx->pm_state, wcd9xxx->wlock_holders);
-	}
-	mutex_unlock(&wcd9xxx->pm_lock);
-	wake_up_all(&wcd9xxx->pm_wq);
-
-	return ret;
-}
-
 static int wcd9xxx_device_up(struct wcd9xxx *wcd9xxx)
 {
 	int ret = 0;
+	struct wcd9xxx_core_resource *wcd9xxx_res = &wcd9xxx->core_res;
 
 	if (wcd9xxx->slim_device_bootup) {
 		wcd9xxx->slim_device_bootup = false;
@@ -1567,86 +1676,60 @@
 		pr_err("%s: Resetting Codec failed\n", __func__);
 
 	wcd9xxx_bring_up(wcd9xxx);
-	wcd9xxx->post_reset(wcd9xxx);
+	ret = wcd9xxx_irq_init(wcd9xxx_res);
+	if (ret) {
+		pr_err("%s: wcd9xx_irq_init failed : %d\n", __func__, ret);
+	} else {
+		if (wcd9xxx->post_reset)
+			ret = wcd9xxx->post_reset(wcd9xxx);
+	}
 	return ret;
 }
 
 static int wcd9xxx_slim_device_up(struct slim_device *sldev)
 {
 	struct wcd9xxx *wcd9xxx = slim_get_devicedata(sldev);
+	dev_dbg(wcd9xxx->dev, "%s: device up\n", __func__);
 	return wcd9xxx_device_up(wcd9xxx);
 }
 
+static int wcd9xxx_slim_device_down(struct slim_device *sldev)
+{
+	struct wcd9xxx *wcd9xxx = slim_get_devicedata(sldev);
+
+	wcd9xxx_irq_exit(&wcd9xxx->core_res);
+	if (wcd9xxx->dev_down)
+		wcd9xxx->dev_down(wcd9xxx);
+	dev_dbg(wcd9xxx->dev, "%s: device down\n", __func__);
+	return 0;
+}
+
 static int wcd9xxx_slim_resume(struct slim_device *sldev)
 {
 	struct wcd9xxx *wcd9xxx = slim_get_devicedata(sldev);
-	return wcd9xxx_resume(wcd9xxx);
+	return wcd9xxx_core_res_resume(&wcd9xxx->core_res);
 }
 
 static int wcd9xxx_i2c_resume(struct i2c_client *i2cdev)
 {
 	struct wcd9xxx *wcd9xxx = dev_get_drvdata(&i2cdev->dev);
 	if (wcd9xxx)
-		return wcd9xxx_resume(wcd9xxx);
+		return wcd9xxx_core_res_resume(&wcd9xxx->core_res);
 	else
 		return 0;
 }
 
-static int wcd9xxx_suspend(struct wcd9xxx *wcd9xxx, pm_message_t pmesg)
-{
-	int ret = 0;
-
-	pr_debug("%s: enter\n", __func__);
-	/*
-	 * pm_qos_update_request() can be called after this suspend chain call
-	 * started. thus suspend can be called while lock is being held
-	 */
-	mutex_lock(&wcd9xxx->pm_lock);
-	if (wcd9xxx->pm_state == WCD9XXX_PM_SLEEPABLE) {
-		pr_debug("%s: suspending system, state %d, wlock %d\n",
-			 __func__, wcd9xxx->pm_state, wcd9xxx->wlock_holders);
-		wcd9xxx->pm_state = WCD9XXX_PM_ASLEEP;
-	} else if (wcd9xxx->pm_state == WCD9XXX_PM_AWAKE) {
-		/* unlock to wait for pm_state == WCD9XXX_PM_SLEEPABLE
-		 * then set to WCD9XXX_PM_ASLEEP */
-		pr_debug("%s: waiting to suspend system, state %d, wlock %d\n",
-			 __func__, wcd9xxx->pm_state, wcd9xxx->wlock_holders);
-		mutex_unlock(&wcd9xxx->pm_lock);
-		if (!(wait_event_timeout(wcd9xxx->pm_wq,
-					 wcd9xxx_pm_cmpxchg(wcd9xxx,
-						  WCD9XXX_PM_SLEEPABLE,
-						  WCD9XXX_PM_ASLEEP) ==
-							WCD9XXX_PM_SLEEPABLE,
-					 HZ))) {
-			pr_debug("%s: suspend failed state %d, wlock %d\n",
-				 __func__, wcd9xxx->pm_state,
-				 wcd9xxx->wlock_holders);
-			ret = -EBUSY;
-		} else {
-			pr_debug("%s: done, state %d, wlock %d\n", __func__,
-				 wcd9xxx->pm_state, wcd9xxx->wlock_holders);
-		}
-		mutex_lock(&wcd9xxx->pm_lock);
-	} else if (wcd9xxx->pm_state == WCD9XXX_PM_ASLEEP) {
-		pr_warn("%s: system is already suspended, state %d, wlock %dn",
-			__func__, wcd9xxx->pm_state, wcd9xxx->wlock_holders);
-	}
-	mutex_unlock(&wcd9xxx->pm_lock);
-
-	return ret;
-}
-
 static int wcd9xxx_slim_suspend(struct slim_device *sldev, pm_message_t pmesg)
 {
 	struct wcd9xxx *wcd9xxx = slim_get_devicedata(sldev);
-	return wcd9xxx_suspend(wcd9xxx, pmesg);
+	return wcd9xxx_core_res_suspend(&wcd9xxx->core_res, pmesg);
 }
 
 static int wcd9xxx_i2c_suspend(struct i2c_client *i2cdev, pm_message_t pmesg)
 {
 	struct wcd9xxx *wcd9xxx = dev_get_drvdata(&i2cdev->dev);
 	if (wcd9xxx)
-		return wcd9xxx_suspend(wcd9xxx, pmesg);
+		return wcd9xxx_core_res_suspend(&wcd9xxx->core_res, pmesg);
 	else
 		return 0;
 }
@@ -1733,6 +1816,7 @@
 	.resume = wcd9xxx_slim_resume,
 	.suspend = wcd9xxx_slim_suspend,
 	.device_up = wcd9xxx_slim_device_up,
+	.device_down = wcd9xxx_slim_device_down,
 };
 
 static const struct slim_device_id tapan_slimtest_id[] = {
@@ -1751,6 +1835,7 @@
 	.resume = wcd9xxx_slim_resume,
 	.suspend = wcd9xxx_slim_suspend,
 	.device_up = wcd9xxx_slim_device_up,
+	.device_down = wcd9xxx_slim_device_down,
 };
 
 static struct i2c_device_id wcd9xxx_id_table[] = {
@@ -1800,7 +1885,7 @@
 	int ret[NUM_WCD9XXX_REG_RET];
 	int i = 0;
 
-	wcd9xxx_intf = WCD9XXX_INTERFACE_TYPE_PROBING;
+	wcd9xxx_set_intf_type(WCD9XXX_INTERFACE_TYPE_PROBING);
 
 	ret[0] = slim_driver_register(&tabla_slim_driver);
 	if (ret[0])
@@ -1844,6 +1929,7 @@
 
 static void __exit wcd9xxx_exit(void)
 {
+	wcd9xxx_set_intf_type(WCD9XXX_INTERFACE_TYPE_PROBING);
 }
 module_exit(wcd9xxx_exit);
 
diff --git a/drivers/mfd/wcd9xxx-irq.c b/drivers/mfd/wcd9xxx-irq.c
index 062351d..dc32efd 100644
--- a/drivers/mfd/wcd9xxx-irq.c
+++ b/drivers/mfd/wcd9xxx-irq.c
@@ -15,10 +15,9 @@
 #include <linux/sched.h>
 #include <linux/irq.h>
 #include <linux/mfd/core.h>
-#include <linux/mfd/wcd9xxx/core.h>
+#include <linux/mfd/wcd9xxx/core-resource.h>
 #include <linux/mfd/wcd9xxx/wcd9xxx_registers.h>
 #include <linux/mfd/wcd9xxx/wcd9310_registers.h>
-#include <linux/mfd/wcd9xxx/wcd9xxx-slimslave.h>
 #include <linux/delay.h>
 #include <linux/irqdomain.h>
 #include <linux/interrupt.h>
@@ -40,57 +39,76 @@
 };
 #endif
 
-static int virq_to_phyirq(struct wcd9xxx *wcd9xxx, int virq);
-static int phyirq_to_virq(struct wcd9xxx *wcd9xxx, int irq);
-static unsigned int wcd9xxx_irq_get_upstream_irq(struct wcd9xxx *wcd9xxx);
-static void wcd9xxx_irq_put_upstream_irq(struct wcd9xxx *wcd9xxx);
-static int wcd9xxx_map_irq(struct wcd9xxx *wcd9xxx, int irq);
+static int virq_to_phyirq(
+	struct wcd9xxx_core_resource *wcd9xxx_res, int virq);
+static int phyirq_to_virq(
+	struct wcd9xxx_core_resource *wcd9xxx_res, int irq);
+static unsigned int wcd9xxx_irq_get_upstream_irq(
+	struct wcd9xxx_core_resource *wcd9xxx_res);
+static void wcd9xxx_irq_put_upstream_irq(
+	struct wcd9xxx_core_resource *wcd9xxx_res);
+static int wcd9xxx_map_irq(
+	struct wcd9xxx_core_resource *wcd9xxx_res, int irq);
 
 static void wcd9xxx_irq_lock(struct irq_data *data)
 {
-	struct wcd9xxx *wcd9xxx = irq_data_get_irq_chip_data(data);
-	mutex_lock(&wcd9xxx->irq_lock);
+	struct wcd9xxx_core_resource *wcd9xxx_res =
+			irq_data_get_irq_chip_data(data);
+	mutex_lock(&wcd9xxx_res->irq_lock);
 }
 
 static void wcd9xxx_irq_sync_unlock(struct irq_data *data)
 {
-	struct wcd9xxx *wcd9xxx = irq_data_get_irq_chip_data(data);
+	struct wcd9xxx_core_resource *wcd9xxx_res =
+			irq_data_get_irq_chip_data(data);
 	int i;
 
-	if (ARRAY_SIZE(wcd9xxx->irq_masks_cur) > WCD9XXX_NUM_IRQ_REGS ||
-		ARRAY_SIZE(wcd9xxx->irq_masks_cache) > WCD9XXX_NUM_IRQ_REGS) {
+	if ((ARRAY_SIZE(wcd9xxx_res->irq_masks_cur) >
+			WCD9XXX_MAX_IRQ_REGS) ||
+		(ARRAY_SIZE(wcd9xxx_res->irq_masks_cache) >
+			WCD9XXX_MAX_IRQ_REGS)) {
 			pr_err("%s: Array Size out of bound\n", __func__);
 			 return;
 	}
+	if (!wcd9xxx_res->codec_reg_write) {
+		pr_err("%s: Codec reg write callback function not defined\n",
+				__func__);
+		return;
+	}
 
-	for (i = 0; i < ARRAY_SIZE(wcd9xxx->irq_masks_cur); i++) {
+	for (i = 0; i < ARRAY_SIZE(wcd9xxx_res->irq_masks_cur); i++) {
 		/* If there's been a change in the mask write it back
 		 * to the hardware.
 		 */
-		if (wcd9xxx->irq_masks_cur[i] != wcd9xxx->irq_masks_cache[i]) {
-			wcd9xxx->irq_masks_cache[i] = wcd9xxx->irq_masks_cur[i];
-			wcd9xxx_reg_write(wcd9xxx,
+		if (wcd9xxx_res->irq_masks_cur[i] !=
+					wcd9xxx_res->irq_masks_cache[i]) {
+
+			wcd9xxx_res->irq_masks_cache[i] =
+					wcd9xxx_res->irq_masks_cur[i];
+			wcd9xxx_res->codec_reg_write(wcd9xxx_res,
 					  WCD9XXX_A_INTR_MASK0 + i,
-					  wcd9xxx->irq_masks_cur[i]);
+					  wcd9xxx_res->irq_masks_cur[i]);
 		}
 	}
 
-	mutex_unlock(&wcd9xxx->irq_lock);
+	mutex_unlock(&wcd9xxx_res->irq_lock);
 }
 
 static void wcd9xxx_irq_enable(struct irq_data *data)
 {
-	struct wcd9xxx *wcd9xxx = irq_data_get_irq_chip_data(data);
-	int wcd9xxx_irq = virq_to_phyirq(wcd9xxx, data->irq);
-	wcd9xxx->irq_masks_cur[BIT_BYTE(wcd9xxx_irq)] &=
+	struct wcd9xxx_core_resource *wcd9xxx_res =
+			irq_data_get_irq_chip_data(data);
+	int wcd9xxx_irq = virq_to_phyirq(wcd9xxx_res, data->irq);
+	wcd9xxx_res->irq_masks_cur[BIT_BYTE(wcd9xxx_irq)] &=
 		~(BYTE_BIT_MASK(wcd9xxx_irq));
 }
 
 static void wcd9xxx_irq_disable(struct irq_data *data)
 {
-	struct wcd9xxx *wcd9xxx = irq_data_get_irq_chip_data(data);
-	int wcd9xxx_irq = virq_to_phyirq(wcd9xxx, data->irq);
-	wcd9xxx->irq_masks_cur[BIT_BYTE(wcd9xxx_irq)]
+	struct wcd9xxx_core_resource *wcd9xxx_res =
+			irq_data_get_irq_chip_data(data);
+	int wcd9xxx_irq = virq_to_phyirq(wcd9xxx_res, data->irq);
+	wcd9xxx_res->irq_masks_cur[BIT_BYTE(wcd9xxx_irq)]
 		|= BYTE_BIT_MASK(wcd9xxx_irq);
 }
 
@@ -108,21 +126,8 @@
 	.irq_mask = wcd9xxx_irq_mask,
 };
 
-enum wcd9xxx_pm_state wcd9xxx_pm_cmpxchg(struct wcd9xxx *wcd9xxx,
-		enum wcd9xxx_pm_state o,
-		enum wcd9xxx_pm_state n)
-{
-	enum wcd9xxx_pm_state old;
-	mutex_lock(&wcd9xxx->pm_lock);
-	old = wcd9xxx->pm_state;
-	if (old == o)
-		wcd9xxx->pm_state = n;
-	mutex_unlock(&wcd9xxx->pm_lock);
-	return old;
-}
-EXPORT_SYMBOL_GPL(wcd9xxx_pm_cmpxchg);
-
-bool wcd9xxx_lock_sleep(struct wcd9xxx *wcd9xxx)
+bool wcd9xxx_lock_sleep(
+	struct wcd9xxx_core_resource *wcd9xxx_res)
 {
 	enum wcd9xxx_pm_state os;
 
@@ -138,164 +143,154 @@
 	 * As interrupt line is still active, codec will have another IRQ to
 	 * retry shortly.
 	 */
-	mutex_lock(&wcd9xxx->pm_lock);
-	if (wcd9xxx->wlock_holders++ == 0) {
+	mutex_lock(&wcd9xxx_res->pm_lock);
+	if (wcd9xxx_res->wlock_holders++ == 0) {
 		pr_debug("%s: holding wake lock\n", __func__);
-		pm_qos_update_request(&wcd9xxx->pm_qos_req,
+		pm_qos_update_request(&wcd9xxx_res->pm_qos_req,
 				      msm_cpuidle_get_deep_idle_latency());
 	}
-	mutex_unlock(&wcd9xxx->pm_lock);
-	if (!wait_event_timeout(wcd9xxx->pm_wq,
-			((os = wcd9xxx_pm_cmpxchg(wcd9xxx, WCD9XXX_PM_SLEEPABLE,
-						WCD9XXX_PM_AWAKE)) ==
-						    WCD9XXX_PM_SLEEPABLE ||
-			 (os == WCD9XXX_PM_AWAKE)),
+	mutex_unlock(&wcd9xxx_res->pm_lock);
+	os = wcd9xxx_pm_cmpxchg(wcd9xxx_res,
+					WCD9XXX_PM_SLEEPABLE,
+					WCD9XXX_PM_AWAKE);
+	if (!wait_event_timeout(wcd9xxx_res->pm_wq,
+			(os  == WCD9XXX_PM_SLEEPABLE ||
+			 os == WCD9XXX_PM_AWAKE),
 			msecs_to_jiffies(WCD9XXX_SYSTEM_RESUME_TIMEOUT_MS))) {
 		pr_warn("%s: system didn't resume within %dms, s %d, w %d\n",
 			__func__,
-			WCD9XXX_SYSTEM_RESUME_TIMEOUT_MS, wcd9xxx->pm_state,
-			wcd9xxx->wlock_holders);
-		wcd9xxx_unlock_sleep(wcd9xxx);
+			WCD9XXX_SYSTEM_RESUME_TIMEOUT_MS, wcd9xxx_res->pm_state,
+			wcd9xxx_res->wlock_holders);
+		wcd9xxx_unlock_sleep(wcd9xxx_res);
 		return false;
 	}
-	wake_up_all(&wcd9xxx->pm_wq);
+	wake_up_all(&wcd9xxx_res->pm_wq);
 	return true;
 }
-EXPORT_SYMBOL_GPL(wcd9xxx_lock_sleep);
+EXPORT_SYMBOL(wcd9xxx_lock_sleep);
 
-void wcd9xxx_unlock_sleep(struct wcd9xxx *wcd9xxx)
+void wcd9xxx_unlock_sleep(
+	struct wcd9xxx_core_resource *wcd9xxx_res)
 {
-	mutex_lock(&wcd9xxx->pm_lock);
-	if (--wcd9xxx->wlock_holders == 0) {
+	mutex_lock(&wcd9xxx_res->pm_lock);
+	if (--wcd9xxx_res->wlock_holders == 0) {
 		pr_debug("%s: releasing wake lock pm_state %d -> %d\n",
-			 __func__, wcd9xxx->pm_state, WCD9XXX_PM_SLEEPABLE);
+			 __func__, wcd9xxx_res->pm_state, WCD9XXX_PM_SLEEPABLE);
 		/*
 		 * if wcd9xxx_lock_sleep failed, pm_state would be still
 		 * WCD9XXX_PM_ASLEEP, don't overwrite
 		 */
-		if (likely(wcd9xxx->pm_state == WCD9XXX_PM_AWAKE))
-			wcd9xxx->pm_state = WCD9XXX_PM_SLEEPABLE;
-		pm_qos_update_request(&wcd9xxx->pm_qos_req,
+		if (likely(wcd9xxx_res->pm_state == WCD9XXX_PM_AWAKE))
+			wcd9xxx_res->pm_state = WCD9XXX_PM_SLEEPABLE;
+		pm_qos_update_request(&wcd9xxx_res->pm_qos_req,
 				PM_QOS_DEFAULT_VALUE);
 	}
-	mutex_unlock(&wcd9xxx->pm_lock);
-	wake_up_all(&wcd9xxx->pm_wq);
+	mutex_unlock(&wcd9xxx_res->pm_lock);
+	wake_up_all(&wcd9xxx_res->pm_wq);
 }
-EXPORT_SYMBOL_GPL(wcd9xxx_unlock_sleep);
+EXPORT_SYMBOL(wcd9xxx_unlock_sleep);
 
-void wcd9xxx_nested_irq_lock(struct wcd9xxx *wcd9xxx)
+void wcd9xxx_nested_irq_lock(struct wcd9xxx_core_resource *wcd9xxx_res)
 {
-	mutex_lock(&wcd9xxx->nested_irq_lock);
+	mutex_lock(&wcd9xxx_res->nested_irq_lock);
 }
 
-void wcd9xxx_nested_irq_unlock(struct wcd9xxx *wcd9xxx)
+void wcd9xxx_nested_irq_unlock(struct wcd9xxx_core_resource *wcd9xxx_res)
 {
-	mutex_unlock(&wcd9xxx->nested_irq_lock);
+	mutex_unlock(&wcd9xxx_res->nested_irq_lock);
 }
 
-static bool wcd9xxx_is_mbhc_irq(struct wcd9xxx *wcd9xxx, int irqbit)
-{
-	if ((irqbit <= WCD9XXX_IRQ_MBHC_INSERTION) &&
-	    (irqbit >= WCD9XXX_IRQ_MBHC_REMOVAL))
-		return true;
-	else if (wcd9xxx->codec_type->id_major == TAIKO_MAJOR &&
-		 irqbit == WCD9320_IRQ_MBHC_JACK_SWITCH)
-		return true;
-	else if (wcd9xxx->codec_type->id_major == TAPAN_MAJOR &&
-		 irqbit == WCD9306_IRQ_MBHC_JACK_SWITCH)
-		return true;
-	else
-		return false;
-}
 
-static void wcd9xxx_irq_dispatch(struct wcd9xxx *wcd9xxx, int irqbit)
+static void wcd9xxx_irq_dispatch(struct wcd9xxx_core_resource *wcd9xxx_res,
+			struct intr_data *irqdata)
 {
-	if (wcd9xxx_is_mbhc_irq(wcd9xxx, irqbit)) {
-		wcd9xxx_nested_irq_lock(wcd9xxx);
-		wcd9xxx_reg_write(wcd9xxx, WCD9XXX_A_INTR_CLEAR0 +
-					   BIT_BYTE(irqbit),
-				  BYTE_BIT_MASK(irqbit));
-		if (wcd9xxx_get_intf_type() == WCD9XXX_INTERFACE_TYPE_I2C)
-			wcd9xxx_reg_write(wcd9xxx, WCD9XXX_A_INTR_MODE, 0x02);
-		handle_nested_irq(phyirq_to_virq(wcd9xxx, irqbit));
-		wcd9xxx_nested_irq_unlock(wcd9xxx);
-	} else {
-		wcd9xxx_nested_irq_lock(wcd9xxx);
-		handle_nested_irq(phyirq_to_virq(wcd9xxx, irqbit));
-		wcd9xxx_reg_write(wcd9xxx, WCD9XXX_A_INTR_CLEAR0 +
-					   BIT_BYTE(irqbit),
-				  BYTE_BIT_MASK(irqbit));
-		if (wcd9xxx_get_intf_type() == WCD9XXX_INTERFACE_TYPE_I2C)
-			wcd9xxx_reg_write(wcd9xxx, WCD9XXX_A_INTR_MODE, 0x02);
-		wcd9xxx_nested_irq_unlock(wcd9xxx);
+	int irqbit = irqdata->intr_num;
+	if (!wcd9xxx_res->codec_reg_write) {
+		pr_err("%s: codec read/write callback not defined\n",
+			   __func__);
+		return;
 	}
-}
 
-static int wcd9xxx_num_irq_regs(const struct wcd9xxx *wcd9xxx)
-{
-	return (wcd9xxx->codec_type->num_irqs / 8) +
-		((wcd9xxx->codec_type->num_irqs % 8) ? 1 : 0);
+	if (irqdata->clear_first) {
+		wcd9xxx_nested_irq_lock(wcd9xxx_res);
+		wcd9xxx_res->codec_reg_write(wcd9xxx_res,
+				WCD9XXX_A_INTR_CLEAR0 + BIT_BYTE(irqbit),
+				BYTE_BIT_MASK(irqbit));
+
+		if (wcd9xxx_get_intf_type() == WCD9XXX_INTERFACE_TYPE_I2C)
+			wcd9xxx_res->codec_reg_write(wcd9xxx_res,
+						WCD9XXX_A_INTR_MODE, 0x02);
+		handle_nested_irq(phyirq_to_virq(wcd9xxx_res, irqbit));
+		wcd9xxx_nested_irq_unlock(wcd9xxx_res);
+	} else {
+		wcd9xxx_nested_irq_lock(wcd9xxx_res);
+		handle_nested_irq(phyirq_to_virq(wcd9xxx_res, irqbit));
+		wcd9xxx_res->codec_reg_write(wcd9xxx_res,
+				WCD9XXX_A_INTR_CLEAR0 + BIT_BYTE(irqbit),
+				BYTE_BIT_MASK(irqbit));
+		if (wcd9xxx_get_intf_type() == WCD9XXX_INTERFACE_TYPE_I2C)
+			wcd9xxx_res->codec_reg_write(wcd9xxx_res,
+						WCD9XXX_A_INTR_MODE, 0x02);
+
+		wcd9xxx_nested_irq_unlock(wcd9xxx_res);
+	}
 }
 
 static irqreturn_t wcd9xxx_irq_thread(int irq, void *data)
 {
 	int ret;
 	int i;
+	struct intr_data irqdata;
 	char linebuf[128];
-	struct wcd9xxx *wcd9xxx = data;
-	int num_irq_regs = wcd9xxx_num_irq_regs(wcd9xxx);
-	u8 status[num_irq_regs], status1[num_irq_regs];
 	static DEFINE_RATELIMIT_STATE(ratelimit, 5 * HZ, 1);
+	struct wcd9xxx_core_resource *wcd9xxx_res = data;
+	int num_irq_regs = wcd9xxx_res->num_irq_regs;
+	u8 status[num_irq_regs], status1[num_irq_regs];
 
-	if (unlikely(wcd9xxx_lock_sleep(wcd9xxx) == false)) {
-		dev_err(wcd9xxx->dev, "Failed to hold suspend\n");
+	if (unlikely(wcd9xxx_lock_sleep(wcd9xxx_res) == false)) {
+		dev_err(wcd9xxx_res->dev, "Failed to hold suspend\n");
 		return IRQ_NONE;
 	}
-	ret = wcd9xxx_bulk_read(wcd9xxx, WCD9XXX_A_INTR_STATUS0,
+
+	if (!wcd9xxx_res->codec_bulk_read) {
+		dev_err(wcd9xxx_res->dev,
+				"%s: Codec Bulk Register read callback not supplied\n",
+			   __func__);
+		goto err_disable_irq;
+	}
+
+	ret = wcd9xxx_res->codec_bulk_read(wcd9xxx_res,
+				WCD9XXX_A_INTR_STATUS0,
 				num_irq_regs, status);
+
 	if (ret < 0) {
-		dev_err(wcd9xxx->dev, "Failed to read interrupt status: %d\n",
-			ret);
-		dev_err(wcd9xxx->dev, "Disable irq %d\n", wcd9xxx->irq);
-		disable_irq_wake(wcd9xxx->irq);
-		disable_irq_nosync(wcd9xxx->irq);
-		wcd9xxx_unlock_sleep(wcd9xxx);
-		return IRQ_NONE;
+		dev_err(wcd9xxx_res->dev,
+				"Failed to read interrupt status: %d\n", ret);
+		goto err_disable_irq;
 	}
 
 	/* Apply masking */
 	for (i = 0; i < num_irq_regs; i++)
-		status[i] &= ~wcd9xxx->irq_masks_cur[i];
+		status[i] &= ~wcd9xxx_res->irq_masks_cur[i];
 
 	memcpy(status1, status, sizeof(status1));
 
 	/* Find out which interrupt was triggered and call that interrupt's
 	 * handler function
-	 */
-	if (status[BIT_BYTE(WCD9XXX_IRQ_SLIMBUS)] &
-	    BYTE_BIT_MASK(WCD9XXX_IRQ_SLIMBUS)) {
-		wcd9xxx_irq_dispatch(wcd9xxx, WCD9XXX_IRQ_SLIMBUS);
-		status1[BIT_BYTE(WCD9XXX_IRQ_SLIMBUS)] &=
-		    ~BYTE_BIT_MASK(WCD9XXX_IRQ_SLIMBUS);
-	}
-
-	/* Since codec has only one hardware irq line which is shared by
+	 *
+	 * Since codec has only one hardware irq line which is shared by
 	 * codec's different internal interrupts, so it's possible master irq
 	 * handler dispatches multiple nested irq handlers after breaking
-	 * order.  Dispatch MBHC interrupts order to follow MBHC state
-	 * machine's order */
-	for (i = WCD9XXX_IRQ_MBHC_INSERTION;
-	     i >= WCD9XXX_IRQ_MBHC_REMOVAL; i--) {
-		if (status[BIT_BYTE(i)] & BYTE_BIT_MASK(i)) {
-			wcd9xxx_irq_dispatch(wcd9xxx, i);
-			status1[BIT_BYTE(i)] &= ~BYTE_BIT_MASK(i);
-		}
-	}
-	for (i = WCD9XXX_IRQ_BG_PRECHARGE; i < wcd9xxx->codec_type->num_irqs;
-	     i++) {
-		if (status[BIT_BYTE(i)] & BYTE_BIT_MASK(i)) {
-			wcd9xxx_irq_dispatch(wcd9xxx, i);
-			status1[BIT_BYTE(i)] &= ~BYTE_BIT_MASK(i);
+	 * order.  Dispatch interrupts in the order that is maintained by
+	 * the interrupt table.
+	 */
+	for (i = 0; i < wcd9xxx_res->intr_table_size; i++) {
+		irqdata = wcd9xxx_res->intr_table[i];
+		if (status[BIT_BYTE(irqdata.intr_num)] &
+			BYTE_BIT_MASK(irqdata.intr_num)) {
+			wcd9xxx_irq_dispatch(wcd9xxx_res, &irqdata);
+			status1[BIT_BYTE(irqdata.intr_num)] &=
+					~BYTE_BIT_MASK(irqdata.intr_num);
 		}
 	}
 
@@ -319,45 +314,58 @@
 		}
 
 		memset(status, 0xff, num_irq_regs);
-		wcd9xxx_bulk_write(wcd9xxx, WCD9XXX_A_INTR_CLEAR0,
+		wcd9xxx_bulk_write(wcd9xxx_res, WCD9XXX_A_INTR_CLEAR0,
 				   num_irq_regs, status);
 		if (wcd9xxx_get_intf_type() == WCD9XXX_INTERFACE_TYPE_I2C)
-			wcd9xxx_reg_write(wcd9xxx, WCD9XXX_A_INTR_MODE, 0x02);
+			wcd9xxx_reg_write(wcd9xxx_res,
+					WCD9XXX_A_INTR_MODE, 0x02);
 	}
-	wcd9xxx_unlock_sleep(wcd9xxx);
+	wcd9xxx_unlock_sleep(wcd9xxx_res);
 
 	return IRQ_HANDLED;
+
+err_disable_irq:
+		dev_err(wcd9xxx_res->dev,
+				"Disable irq %d\n", wcd9xxx_res->irq);
+
+		disable_irq_wake(wcd9xxx_res->irq);
+		disable_irq_nosync(wcd9xxx_res->irq);
+		wcd9xxx_unlock_sleep(wcd9xxx_res);
+		return IRQ_NONE;
 }
 
-void wcd9xxx_free_irq(struct wcd9xxx *wcd9xxx, int irq, void *data)
+void wcd9xxx_free_irq(struct wcd9xxx_core_resource *wcd9xxx_res,
+			int irq, void *data)
 {
-	free_irq(phyirq_to_virq(wcd9xxx, irq), data);
+	free_irq(phyirq_to_virq(wcd9xxx_res, irq), data);
 }
 
-void wcd9xxx_enable_irq(struct wcd9xxx *wcd9xxx, int irq)
+void wcd9xxx_enable_irq(struct wcd9xxx_core_resource *wcd9xxx_res, int irq)
 {
-	enable_irq(phyirq_to_virq(wcd9xxx, irq));
+	enable_irq(phyirq_to_virq(wcd9xxx_res, irq));
 }
 
-void wcd9xxx_disable_irq(struct wcd9xxx *wcd9xxx, int irq)
+void wcd9xxx_disable_irq(struct wcd9xxx_core_resource *wcd9xxx_res, int irq)
 {
-	disable_irq_nosync(phyirq_to_virq(wcd9xxx, irq));
+	disable_irq_nosync(phyirq_to_virq(wcd9xxx_res, irq));
 }
 
-void wcd9xxx_disable_irq_sync(struct wcd9xxx *wcd9xxx, int irq)
+void wcd9xxx_disable_irq_sync(
+			struct wcd9xxx_core_resource *wcd9xxx_res, int irq)
 {
-	disable_irq(phyirq_to_virq(wcd9xxx, irq));
+	disable_irq(phyirq_to_virq(wcd9xxx_res, irq));
 }
 
-static int wcd9xxx_irq_setup_downstream_irq(struct wcd9xxx *wcd9xxx)
+static int wcd9xxx_irq_setup_downstream_irq(
+			struct wcd9xxx_core_resource *wcd9xxx_res)
 {
 	int irq, virq, ret;
 
 	pr_debug("%s: enter\n", __func__);
 
-	for (irq = 0; irq < wcd9xxx->codec_type->num_irqs; irq++) {
+	for (irq = 0; irq < wcd9xxx_res->num_irqs; irq++) {
 		/* Map OF irq */
-		virq = wcd9xxx_map_irq(wcd9xxx, irq);
+		virq = wcd9xxx_map_irq(wcd9xxx_res, irq);
 		pr_debug("%s: irq %d -> %d\n", __func__, irq, virq);
 		if (virq == NO_IRQ) {
 			pr_err("%s, No interrupt specifier for irq %d\n",
@@ -365,14 +373,14 @@
 			return NO_IRQ;
 		}
 
-		ret = irq_set_chip_data(virq, wcd9xxx);
+		ret = irq_set_chip_data(virq, wcd9xxx_res);
 		if (ret) {
 			pr_err("%s: Failed to configure irq %d (%d)\n",
 			       __func__, irq, ret);
 			return ret;
 		}
 
-		if (wcd9xxx->irq_level_high[irq])
+		if (wcd9xxx_res->irq_level_high[irq])
 			irq_set_chip_and_handler(virq, &wcd9xxx_irq_chip,
 						 handle_level_irq);
 		else
@@ -387,91 +395,100 @@
 	return 0;
 }
 
-int wcd9xxx_irq_init(struct wcd9xxx *wcd9xxx)
+int wcd9xxx_irq_init(struct wcd9xxx_core_resource *wcd9xxx_res)
 {
 	int i, ret;
-	u8 irq_level[wcd9xxx_num_irq_regs(wcd9xxx)];
+	u8 irq_level[wcd9xxx_res->num_irq_regs];
 
-	mutex_init(&wcd9xxx->irq_lock);
-	mutex_init(&wcd9xxx->nested_irq_lock);
+	mutex_init(&wcd9xxx_res->irq_lock);
+	mutex_init(&wcd9xxx_res->nested_irq_lock);
 
-	wcd9xxx->irq = wcd9xxx_irq_get_upstream_irq(wcd9xxx);
-	if (!wcd9xxx->irq) {
+	wcd9xxx_res->irq = wcd9xxx_irq_get_upstream_irq(wcd9xxx_res);
+	if (!wcd9xxx_res->irq) {
 		pr_warn("%s: irq driver is not yet initialized\n", __func__);
-		mutex_destroy(&wcd9xxx->irq_lock);
-		mutex_destroy(&wcd9xxx->nested_irq_lock);
+		mutex_destroy(&wcd9xxx_res->irq_lock);
+		mutex_destroy(&wcd9xxx_res->nested_irq_lock);
 		return -EPROBE_DEFER;
 	}
-	pr_debug("%s: probed irq %d\n", __func__, wcd9xxx->irq);
+	pr_debug("%s: probed irq %d\n", __func__, wcd9xxx_res->irq);
 
 	/* Setup downstream IRQs */
-	ret = wcd9xxx_irq_setup_downstream_irq(wcd9xxx);
+	ret = wcd9xxx_irq_setup_downstream_irq(wcd9xxx_res);
 	if (ret) {
 		pr_err("%s: Failed to setup downstream IRQ\n", __func__);
-		wcd9xxx_irq_put_upstream_irq(wcd9xxx);
-		mutex_destroy(&wcd9xxx->irq_lock);
-		mutex_destroy(&wcd9xxx->nested_irq_lock);
+		wcd9xxx_irq_put_upstream_irq(wcd9xxx_res);
+		mutex_destroy(&wcd9xxx_res->irq_lock);
+		mutex_destroy(&wcd9xxx_res->nested_irq_lock);
 		return ret;
 	}
 
 	/* All other wcd9xxx interrupts are edge triggered */
-	wcd9xxx->irq_level_high[0] = true;
+	wcd9xxx_res->irq_level_high[0] = true;
 
 	/* mask all the interrupts */
-	memset(irq_level, 0, wcd9xxx_num_irq_regs(wcd9xxx));
-	for (i = 0; i < wcd9xxx->codec_type->num_irqs; i++) {
-		wcd9xxx->irq_masks_cur[BIT_BYTE(i)] |= BYTE_BIT_MASK(i);
-		wcd9xxx->irq_masks_cache[BIT_BYTE(i)] |= BYTE_BIT_MASK(i);
+	memset(irq_level, 0, wcd9xxx_res->num_irq_regs);
+	for (i = 0; i < wcd9xxx_res->num_irqs; i++) {
+		wcd9xxx_res->irq_masks_cur[BIT_BYTE(i)] |= BYTE_BIT_MASK(i);
+		wcd9xxx_res->irq_masks_cache[BIT_BYTE(i)] |= BYTE_BIT_MASK(i);
 		irq_level[BIT_BYTE(i)] |=
-		    wcd9xxx->irq_level_high[i] << (i % BITS_PER_BYTE);
+		    wcd9xxx_res->irq_level_high[i] << (i % BITS_PER_BYTE);
 	}
 
-	for (i = 0; i < wcd9xxx_num_irq_regs(wcd9xxx); i++) {
+	if (!wcd9xxx_res->codec_reg_write) {
+		dev_err(wcd9xxx_res->dev,
+				"%s: Codec Register write callback not defined\n",
+			   __func__);
+		ret = -EINVAL;
+		goto fail_irq_init;
+	}
+
+	for (i = 0; i < wcd9xxx_res->num_irq_regs; i++) {
 		/* Initialize interrupt mask and level registers */
-		wcd9xxx_reg_write(wcd9xxx, WCD9XXX_A_INTR_LEVEL0 + i,
-				  irq_level[i]);
-		wcd9xxx_reg_write(wcd9xxx, WCD9XXX_A_INTR_MASK0 + i,
-				  wcd9xxx->irq_masks_cur[i]);
+		wcd9xxx_res->codec_reg_write(wcd9xxx_res,
+					WCD9XXX_A_INTR_LEVEL0 + i,
+					irq_level[i]);
+		wcd9xxx_res->codec_reg_write(wcd9xxx_res,
+					WCD9XXX_A_INTR_MASK0 + i,
+					wcd9xxx_res->irq_masks_cur[i]);
 	}
 
-	ret = request_threaded_irq(wcd9xxx->irq, NULL, wcd9xxx_irq_thread,
+	ret = request_threaded_irq(wcd9xxx_res->irq, NULL, wcd9xxx_irq_thread,
 				   IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
-				   "wcd9xxx", wcd9xxx);
+				   "wcd9xxx", wcd9xxx_res);
 	if (ret != 0)
-		dev_err(wcd9xxx->dev, "Failed to request IRQ %d: %d\n",
-			wcd9xxx->irq, ret);
+		dev_err(wcd9xxx_res->dev, "Failed to request IRQ %d: %d\n",
+			wcd9xxx_res->irq, ret);
 	else {
-		ret = enable_irq_wake(wcd9xxx->irq);
-		if (ret == 0) {
-			ret = device_init_wakeup(wcd9xxx->dev, 1);
-			if (ret) {
-				dev_err(wcd9xxx->dev, "Failed to init device"
-					"wakeup : %d\n", ret);
-				disable_irq_wake(wcd9xxx->irq);
-			}
-		} else
-			dev_err(wcd9xxx->dev, "Failed to set wake interrupt on"
-				" IRQ %d: %d\n", wcd9xxx->irq, ret);
+		ret = enable_irq_wake(wcd9xxx_res->irq);
 		if (ret)
-			free_irq(wcd9xxx->irq, wcd9xxx);
+			dev_err(wcd9xxx_res->dev,
+				"Failed to set wake interrupt on IRQ %d: %d\n",
+				wcd9xxx_res->irq, ret);
+		if (ret)
+			free_irq(wcd9xxx_res->irq, wcd9xxx_res);
 	}
 
-	if (ret) {
-		pr_err("%s: Failed to init wcd9xxx irq\n", __func__);
-		wcd9xxx_irq_put_upstream_irq(wcd9xxx);
-		mutex_destroy(&wcd9xxx->irq_lock);
-		mutex_destroy(&wcd9xxx->nested_irq_lock);
-	}
+	if (ret)
+		goto fail_irq_init;
 
 	return ret;
+
+fail_irq_init:
+	dev_err(wcd9xxx_res->dev,
+			"%s: Failed to init wcd9xxx irq\n", __func__);
+	wcd9xxx_irq_put_upstream_irq(wcd9xxx_res);
+	mutex_destroy(&wcd9xxx_res->irq_lock);
+	mutex_destroy(&wcd9xxx_res->nested_irq_lock);
+	return ret;
 }
 
-int wcd9xxx_request_irq(struct wcd9xxx *wcd9xxx, int irq, irq_handler_t handler,
+int wcd9xxx_request_irq(struct wcd9xxx_core_resource *wcd9xxx_res,
+			int irq, irq_handler_t handler,
 			const char *name, void *data)
 {
 	int virq;
 
-	virq = phyirq_to_virq(wcd9xxx, irq);
+	virq = phyirq_to_virq(wcd9xxx_res, irq);
 
 	/*
 	 * ARM needs us to explicitly flag the IRQ as valid
@@ -487,43 +504,52 @@
 				    name, data);
 }
 
-void wcd9xxx_irq_exit(struct wcd9xxx *wcd9xxx)
+void wcd9xxx_irq_exit(struct wcd9xxx_core_resource *wcd9xxx_res)
 {
-	if (wcd9xxx->irq) {
-		disable_irq_wake(wcd9xxx->irq);
-		free_irq(wcd9xxx->irq, wcd9xxx);
+	dev_dbg(wcd9xxx_res->dev, "%s: Cleaning up irq %d\n", __func__,
+		wcd9xxx_res->irq);
+
+	if (wcd9xxx_res->irq) {
+		disable_irq_wake(wcd9xxx_res->irq);
+		free_irq(wcd9xxx_res->irq, wcd9xxx_res);
 		/* Release parent's of node */
-		wcd9xxx_irq_put_upstream_irq(wcd9xxx);
-		device_init_wakeup(wcd9xxx->dev, 0);
+		wcd9xxx_irq_put_upstream_irq(wcd9xxx_res);
 	}
-	mutex_destroy(&wcd9xxx->irq_lock);
-	mutex_destroy(&wcd9xxx->nested_irq_lock);
+	mutex_destroy(&wcd9xxx_res->irq_lock);
+	mutex_destroy(&wcd9xxx_res->nested_irq_lock);
 }
 
 #ifndef CONFIG_OF
-static int phyirq_to_virq(struct wcd9xxx *wcd9xxx, int offset)
+static int phyirq_to_virq(
+	struct wcd9xxx_core_resource *wcd9xxx_res,
+	int offset)
 {
-	return wcd9xxx->irq_base + offset;
+	return wcd9xxx_res->irq_base + offset;
 }
 
-static int virq_to_phyirq(struct wcd9xxx *wcd9xxx, int virq)
+static int virq_to_phyirq(
+	struct wcd9xxx_core_resource *wcd9xxx_res,
+	int virq)
 {
-	return virq - wcd9xxx->irq_base;
+	return virq - wcd9xxx_res->irq_base;
 }
 
-static unsigned int wcd9xxx_irq_get_upstream_irq(struct wcd9xxx *wcd9xxx)
+static unsigned int wcd9xxx_irq_get_upstream_irq(
+	struct wcd9xxx_core_resource *wcd9xxx_res)
 {
-	return wcd9xxx->irq;
+	return wcd9xxx_res->irq;
 }
 
-static void wcd9xxx_irq_put_upstream_irq(struct wcd9xxx *wcd9xxx)
+static void wcd9xxx_irq_put_upstream_irq(
+	struct wcd9xxx_core_resource *wcd9xxx_res)
 {
 	/* Do nothing */
 }
 
-static int wcd9xxx_map_irq(struct wcd9xxx *wcd9xxx, int irq)
+static int wcd9xxx_map_irq(
+	struct wcd9xxx_core_resource *wcd9xxx_core_res, int irq)
 {
-	return phyirq_to_virq(wcd9xxx, irq);
+	return phyirq_to_virq(wcd9xxx_core_res, irq);
 }
 #else
 int __init wcd9xxx_irq_of_init(struct device_node *node,
@@ -555,12 +581,12 @@
 }
 
 static struct wcd9xxx_irq_drv_data *
-wcd9xxx_get_irq_drv_d(const struct wcd9xxx *wcd9xxx)
+wcd9xxx_get_irq_drv_d(const struct wcd9xxx_core_resource *wcd9xxx_res)
 {
 	struct device_node *pnode;
 	struct irq_domain *domain;
 
-	pnode = of_irq_find_parent(wcd9xxx->dev->of_node);
+	pnode = of_irq_find_parent(wcd9xxx_res->dev->of_node);
 	/* Shouldn't happen */
 	if (unlikely(!pnode))
 		return NULL;
@@ -569,11 +595,11 @@
 	return (struct wcd9xxx_irq_drv_data *)domain->host_data;
 }
 
-static int phyirq_to_virq(struct wcd9xxx *wcd9xxx, int offset)
+static int phyirq_to_virq(struct wcd9xxx_core_resource *wcd9xxx_res, int offset)
 {
 	struct wcd9xxx_irq_drv_data *data;
 
-	data = wcd9xxx_get_irq_drv_d(wcd9xxx);
+	data = wcd9xxx_get_irq_drv_d(wcd9xxx_res);
 	if (!data) {
 		pr_warn("%s: not registered to interrupt controller\n",
 			__func__);
@@ -582,21 +608,22 @@
 	return irq_linear_revmap(data->domain, offset);
 }
 
-static int virq_to_phyirq(struct wcd9xxx *wcd9xxx, int virq)
+static int virq_to_phyirq(struct wcd9xxx_core_resource *wcd9xxx_res, int virq)
 {
 	struct irq_data *irq_data = irq_get_irq_data(virq);
 	return irq_data->hwirq;
 }
 
-static unsigned int wcd9xxx_irq_get_upstream_irq(struct wcd9xxx *wcd9xxx)
+static unsigned int wcd9xxx_irq_get_upstream_irq(
+				struct wcd9xxx_core_resource *wcd9xxx_res)
 {
 	struct wcd9xxx_irq_drv_data *data;
 
 	/* Hold parent's of node */
-	if (!of_node_get(of_irq_find_parent(wcd9xxx->dev->of_node)))
+	if (!of_node_get(of_irq_find_parent(wcd9xxx_res->dev->of_node)))
 		return -EINVAL;
 
-	data = wcd9xxx_get_irq_drv_d(wcd9xxx);
+	data = wcd9xxx_get_irq_drv_d(wcd9xxx_res);
 	if (!data) {
 		pr_err("%s: interrupt controller is not registerd\n", __func__);
 		return 0;
@@ -606,15 +633,16 @@
 	return data->irq;
 }
 
-static void wcd9xxx_irq_put_upstream_irq(struct wcd9xxx *wcd9xxx)
+static void wcd9xxx_irq_put_upstream_irq(
+			struct wcd9xxx_core_resource *wcd9xxx_res)
 {
 	/* Hold parent's of node */
-	of_node_put(of_irq_find_parent(wcd9xxx->dev->of_node));
+	of_node_put(of_irq_find_parent(wcd9xxx_res->dev->of_node));
 }
 
-static int wcd9xxx_map_irq(struct wcd9xxx *wcd9xxx, int irq)
+static int wcd9xxx_map_irq(struct wcd9xxx_core_resource *wcd9xxx_res, int irq)
 {
-	return of_irq_to_resource(wcd9xxx->dev->of_node, irq, NULL);
+	return of_irq_to_resource(wcd9xxx_res->dev->of_node, irq, NULL);
 }
 
 static int __devinit wcd9xxx_irq_probe(struct platform_device *pdev)
diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
index da07947..6ef389c 100644
--- a/drivers/mmc/card/block.c
+++ b/drivers/mmc/card/block.c
@@ -1640,6 +1640,13 @@
 		 ext_csd[EXT_CSD_CORRECTLY_PRG_SECTORS_NUM + 2] << 16 |
 		 ext_csd[EXT_CSD_CORRECTLY_PRG_SECTORS_NUM + 3] << 24);
 
+	/*
+	 * skip packed command header (1 sector) included by the counter but not
+	 * actually written to the NAND
+	 */
+	if (correctly_done >= card->ext_csd.data_sector_size)
+		correctly_done -= card->ext_csd.data_sector_size;
+
 	list_for_each_entry(prq, &mq_rq->packed_list, queuelist) {
 		if ((correctly_done - (int)blk_rq_bytes(prq)) < 0) {
 			/* prq is not successfull */
@@ -2993,11 +3000,6 @@
 	return ret;
 }
 
-#define CID_MANFID_SANDISK	0x2
-#define CID_MANFID_TOSHIBA	0x11
-#define CID_MANFID_MICRON	0x13
-#define CID_MANFID_SAMSUNG	0x15
-
 static const struct mmc_fixup blk_fixups[] =
 {
 	MMC_FIXUP("SEM02G", CID_MANFID_SANDISK, 0x100, add_quirk,
diff --git a/drivers/mmc/card/mmc_block_test.c b/drivers/mmc/card/mmc_block_test.c
index e9ac2fc..39296ef 100644
--- a/drivers/mmc/card/mmc_block_test.c
+++ b/drivers/mmc/card/mmc_block_test.c
@@ -2867,6 +2867,7 @@
 {
 	int ret = 0;
 	int i;
+	int num_requests = TEST_MAX_REQUESTS / 2;
 
 	td->test_count = 0;
 	mbtd->completed_req_count = 0;
@@ -2876,15 +2877,15 @@
 		     td->wr_rd_next_req_id);
 
 	do {
-		for (i = 0; i < TEST_MAX_REQUESTS; i++) {
+		for (i = 0; i < num_requests; i++) {
 			/*
 			 * since our requests come from a pool containing 128
 			 * requests, we don't want to exhaust this quantity,
-			 * therefore we add up to TEST_MAX_REQUESTS (which
+			 * therefore we add up to num_requests (which
 			 * includes a safety margin) and then call the mmc layer
 			 * to fetch them
 			 */
-			if (td->test_count > TEST_MAX_REQUESTS)
+			if (td->test_count > num_requests)
 				break;
 
 			ret = test_iosched_add_wr_rd_test_req(0, WRITE,
diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c
index 507cd5b..8986829 100644
--- a/drivers/mmc/card/queue.c
+++ b/drivers/mmc/card/queue.c
@@ -264,7 +264,7 @@
 
 	if ((host->caps2 & MMC_CAP2_STOP_REQUEST) &&
 			host->ops->stop_request &&
-			mq->card->ext_csd.hpi)
+			mq->card->ext_csd.hpi_en)
 		blk_urgent_request(mq->queue, mmc_urgent_request);
 
 	memset(&mq->mqrq_cur, 0, sizeof(mq->mqrq_cur));
@@ -285,7 +285,9 @@
 	if (mmc_can_erase(card))
 		mmc_queue_setup_discard(mq->queue, card);
 
-	if ((mmc_can_sanitize(card) && (host->caps2 & MMC_CAP2_SANITIZE)))
+	/* Don't enable Sanitize if HPI is not supported */
+	if ((mmc_can_sanitize(card) && (host->caps2 & MMC_CAP2_SANITIZE) &&
+	    card->ext_csd.hpi_en))
 		mmc_queue_setup_sanitize(mq->queue);
 
 #ifdef CONFIG_MMC_BLOCK_BOUNCE
diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c
index b140510..58ba933 100644
--- a/drivers/mmc/core/bus.c
+++ b/drivers/mmc/core/bus.c
@@ -397,10 +397,11 @@
 			mmc_card_ddr_mode(card) ? "DDR " : "",
 			type);
 	} else {
-		pr_info("%s: new %s%s%s%s%s card at address %04x\n",
+		pr_info("%s: new %s%s%s%s%s%s card at address %04x\n",
 			mmc_hostname(card->host),
 			mmc_card_uhs(card) ? "ultra high speed " :
 			(mmc_card_highspeed(card) ? "high speed " : ""),
+			(mmc_card_hs400(card) ? "HS400 " : ""),
 			(mmc_card_hs200(card) ? "HS200 " : ""),
 			mmc_card_ddr_mode(card) ? "DDR " : "",
 			uhs_bus_speed_mode, type, card->rca);
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index c5e8021..91efb12 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -607,7 +607,7 @@
 	int err = 0;
 	u32 status;
 
-	if (!host->ops->stop_request || !card->ext_csd.hpi) {
+	if (!host->ops->stop_request || !card->ext_csd.hpi_en) {
 		pr_warn("%s: host ops stop_request() or HPI not supported\n",
 				mmc_hostname(host));
 		return -ENOTSUPP;
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
index 90d9826..e1609cf 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -58,6 +58,17 @@
 		__res & __mask;						\
 	})
 
+static const struct mmc_fixup mmc_fixups[] = {
+	/*
+	 * Certain Hynix eMMC 4.41 cards might get broken when HPI feature
+	 * is used so disable the HPI feature for such buggy cards.
+	 */
+	MMC_FIXUP_EXT_CSD_REV(CID_NAME_ANY, CID_MANFID_HYNIX,
+			      0x014a, add_quirk, MMC_QUIRK_BROKEN_HPI, 5),
+
+	END_FIXUP
+};
+
 /*
  * Given the decoded CSD structure, decode the raw CID to our CID structure.
  */
@@ -263,6 +274,12 @@
 			card_type & EXT_CSD_CARD_TYPE_SDR_1_2V))
 		hs_max_dtr = MMC_HS200_MAX_DTR;
 
+	if ((caps2 & MMC_CAP2_HS400_1_8V &&
+			card_type & EXT_CSD_CARD_TYPE_HS400_1_8V) ||
+	    (caps2 & MMC_CAP2_HS400_1_2V &&
+			card_type & EXT_CSD_CARD_TYPE_HS400_1_2V))
+		hs_max_dtr = MMC_HS400_MAX_DTR;
+
 	card->ext_csd.hs_max_dtr = hs_max_dtr;
 	card->ext_csd.card_type = card_type;
 }
@@ -301,6 +318,9 @@
 		goto out;
 	}
 
+	/* fixup device after ext_csd revision field is updated */
+	mmc_fixup_device(card, mmc_fixups);
+
 	card->ext_csd.raw_sectors[0] = ext_csd[EXT_CSD_SEC_CNT + 0];
 	card->ext_csd.raw_sectors[1] = ext_csd[EXT_CSD_SEC_CNT + 1];
 	card->ext_csd.raw_sectors[2] = ext_csd[EXT_CSD_SEC_CNT + 2];
@@ -465,8 +485,28 @@
 	}
 
 	if (card->ext_csd.rev >= 5) {
-		/* check whether the eMMC card supports BKOPS */
-		if (ext_csd[EXT_CSD_BKOPS_SUPPORT] & 0x1) {
+		/* check whether the eMMC card supports HPI */
+		if ((ext_csd[EXT_CSD_HPI_FEATURES] & 0x1) &&
+				!(card->quirks & MMC_QUIRK_BROKEN_HPI)) {
+			card->ext_csd.hpi = 1;
+			if (ext_csd[EXT_CSD_HPI_FEATURES] & 0x2)
+				card->ext_csd.hpi_cmd = MMC_STOP_TRANSMISSION;
+			else
+				card->ext_csd.hpi_cmd = MMC_SEND_STATUS;
+			/*
+			 * Indicate the maximum timeout to close
+			 * a command interrupted by HPI
+			 */
+			card->ext_csd.out_of_int_time =
+				ext_csd[EXT_CSD_OUT_OF_INTERRUPT_TIME] * 10;
+		}
+
+		/*
+		 * check whether the eMMC card supports BKOPS.
+		 * If HPI is not supported then BKOPs shouldn't be enabled.
+		 */
+		if ((ext_csd[EXT_CSD_BKOPS_SUPPORT] & 0x1) &&
+		    card->ext_csd.hpi) {
 			card->ext_csd.bkops = 1;
 			card->ext_csd.bkops_en = ext_csd[EXT_CSD_BKOPS_EN];
 			card->ext_csd.raw_bkops_status =
@@ -486,21 +526,6 @@
 		pr_info("%s: BKOPS_EN bit = %d\n",
 			mmc_hostname(card->host), card->ext_csd.bkops_en);
 
-		/* check whether the eMMC card supports HPI */
-		if (ext_csd[EXT_CSD_HPI_FEATURES] & 0x1) {
-			card->ext_csd.hpi = 1;
-			if (ext_csd[EXT_CSD_HPI_FEATURES] & 0x2)
-				card->ext_csd.hpi_cmd =	MMC_STOP_TRANSMISSION;
-			else
-				card->ext_csd.hpi_cmd = MMC_SEND_STATUS;
-			/*
-			 * Indicate the maximum timeout to close
-			 * a command interrupted by HPI
-			 */
-			card->ext_csd.out_of_int_time =
-				ext_csd[EXT_CSD_OUT_OF_INTERRUPT_TIME] * 10;
-		}
-
 		card->ext_csd.rel_param = ext_csd[EXT_CSD_WR_REL_PARAM];
 		card->ext_csd.rst_n_function = ext_csd[EXT_CSD_RST_N_FUNCTION];
 
@@ -719,7 +744,9 @@
 				EXT_CSD_PWR_CL_52_195 :
 				EXT_CSD_PWR_CL_DDR_52_195;
 		else if (host->ios.clock <= 200000000)
-			index = EXT_CSD_PWR_CL_200_195;
+			index = (bus_width <= EXT_CSD_BUS_WIDTH_8) ?
+				EXT_CSD_PWR_CL_200_195 :
+				EXT_CSD_PWR_CL_DDR_200_195;
 		break;
 	case MMC_VDD_27_28:
 	case MMC_VDD_28_29:
@@ -737,7 +764,9 @@
 				EXT_CSD_PWR_CL_52_360 :
 				EXT_CSD_PWR_CL_DDR_52_360;
 		else if (host->ios.clock <= 200000000)
-			index = EXT_CSD_PWR_CL_200_360;
+			index = (bus_width <= EXT_CSD_BUS_WIDTH_8) ?
+				EXT_CSD_PWR_CL_200_360 :
+				EXT_CSD_PWR_CL_DDR_200_360;
 		break;
 	default:
 		pr_warning("%s: Voltage range not supported "
@@ -766,75 +795,385 @@
 }
 
 /*
- * Selects the desired buswidth and switch to the HS200 mode
- * if bus width set without error
+ * Select the correct bus width supported by both host and card
  */
-static int mmc_select_hs200(struct mmc_card *card)
+static int mmc_select_bus_width(struct mmc_card *card, int ddr, u8 *ext_csd)
 {
-	int idx, err = 0;
 	struct mmc_host *host;
-	static unsigned ext_csd_bits[] = {
-		EXT_CSD_BUS_WIDTH_4,
-		EXT_CSD_BUS_WIDTH_8,
+	static unsigned ext_csd_bits[][2] = {
+		{ EXT_CSD_BUS_WIDTH_8, EXT_CSD_DDR_BUS_WIDTH_8 },
+		{ EXT_CSD_BUS_WIDTH_4, EXT_CSD_DDR_BUS_WIDTH_4 },
+		{ EXT_CSD_BUS_WIDTH_1, EXT_CSD_BUS_WIDTH_1 },
 	};
 	static unsigned bus_widths[] = {
-		MMC_BUS_WIDTH_4,
 		MMC_BUS_WIDTH_8,
+		MMC_BUS_WIDTH_4,
+		MMC_BUS_WIDTH_1
 	};
-
-	BUG_ON(!card);
+	unsigned idx, bus_width = 0;
+	int err = 0;
 
 	host = card->host;
 
+	if ((card->csd.mmca_vsn < CSD_SPEC_VER_4) ||
+	    !(host->caps & (MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA)))
+		goto out;
+
+	if (host->caps & MMC_CAP_8_BIT_DATA)
+		idx = 0;
+	else
+		idx = 1;
+
+	for (; idx < ARRAY_SIZE(bus_widths); idx++) {
+		bus_width = bus_widths[idx];
+		if (bus_width == MMC_BUS_WIDTH_1)
+			ddr = 0; /* no DDR for 1-bit width */
+		err = mmc_select_powerclass(card, ext_csd_bits[idx][0],
+					    ext_csd);
+		if (err)
+			pr_warning("%s: power class selection to " \
+				   "bus width %d failed\n",
+				   mmc_hostname(host),
+				   1 << bus_width);
+
+		err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
+				 EXT_CSD_BUS_WIDTH,
+				 ext_csd_bits[idx][0],
+				 card->ext_csd.generic_cmd6_time);
+		if (!err) {
+			mmc_set_bus_width(host, bus_width);
+
+			/*
+			 * If controller can't handle bus width test,
+			 * compare ext_csd previously read in 1 bit mode
+			 * against ext_csd at new bus width
+			 */
+			if (!(host->caps & MMC_CAP_BUS_WIDTH_TEST))
+				err = mmc_compare_ext_csds(card, bus_width);
+			else
+				err = mmc_bus_test(card, bus_width);
+			if (!err)
+				break;
+		}
+	}
+
+	if (!err && ddr) {
+		err = mmc_select_powerclass(card, ext_csd_bits[idx][1],
+					    ext_csd);
+		if (err)
+			pr_warning("%s: power class selection to " \
+				   "bus width %d ddr %d failed\n",
+				   mmc_hostname(host),
+				   1 << bus_width, ddr);
+			err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
+					 EXT_CSD_BUS_WIDTH,
+					 ext_csd_bits[idx][1],
+					 card->ext_csd.generic_cmd6_time);
+	}
+
+out:
+	return err;
+}
+
+/*
+ * Switch to HighSpeed mode and select wide bus if supported
+ */
+static int mmc_select_hs(struct mmc_card *card, u8 *ext_csd)
+{
+	int err = 0;
+	struct mmc_host *host;
+
+	host = card->host;
+
+	if (!(host->caps & MMC_CAP_MMC_HIGHSPEED) ||
+		!(card->ext_csd.card_type & EXT_CSD_CARD_TYPE_52)) {
+		err = -EOPNOTSUPP;
+		goto out;
+	}
+
+	err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
+				EXT_CSD_HS_TIMING, 1,
+				card->ext_csd.generic_cmd6_time);
+
+	if (err && err != -EBADMSG)
+		goto out;
+
+	mmc_card_set_highspeed(card);
+	mmc_set_timing(host, MMC_TIMING_MMC_HS);
+	mmc_set_clock(host, MMC_HIGH_52_MAX_DTR);
+
+	err = mmc_select_bus_width(card, 0, ext_csd);
+
+out:
+	if (err && err != -EOPNOTSUPP)
+		pr_warning("%s: Switch to HighSpeed mode failed (err:%d)\n",
+				mmc_hostname(host), err);
+	return err;
+}
+
+/*
+ * Select the desired buswidth and switch to HighSpeed DDR mode
+ * if bus width set without error
+ */
+static int mmc_select_hsddr(struct mmc_card *card, u8 *ext_csd)
+{
+	int ddr = 0, err = 0;
+	struct mmc_host *host;
+
+	host = card->host;
+
+	if (!(host->caps & MMC_CAP_HSDDR) ||
+		!(card->ext_csd.card_type & EXT_CSD_CARD_TYPE_DDR_52)) {
+		err = -EOPNOTSUPP;
+		goto out;
+	}
+
+	err = mmc_select_hs(card, ext_csd);
+	if (err)
+		goto out;
+	mmc_card_clr_highspeed(card);
+
+	if ((card->ext_csd.card_type & EXT_CSD_CARD_TYPE_DDR_1_8V)
+		&& ((host->caps & (MMC_CAP_1_8V_DDR |
+		     MMC_CAP_UHS_DDR50))
+			== (MMC_CAP_1_8V_DDR | MMC_CAP_UHS_DDR50)))
+			ddr = MMC_1_8V_DDR_MODE;
+	else if ((card->ext_csd.card_type & EXT_CSD_CARD_TYPE_DDR_1_2V)
+		&& ((host->caps & (MMC_CAP_1_2V_DDR |
+		     MMC_CAP_UHS_DDR50))
+			== (MMC_CAP_1_2V_DDR | MMC_CAP_UHS_DDR50)))
+			ddr = MMC_1_2V_DDR_MODE;
+
+	err = mmc_select_bus_width(card, ddr, ext_csd);
+	if (err)
+		goto out;
+
+	if (host->ios.bus_width == MMC_BUS_WIDTH_1) {
+		pr_err("%s: failed to switch to wide bus\n",
+			mmc_hostname(host));
+		goto out;
+	}
+
+	/*
+	 * eMMC cards can support 3.3V to 1.2V i/o (vccq)
+	 * signaling.
+	 *
+	 * EXT_CSD_CARD_TYPE_DDR_1_8V means 3.3V or 1.8V vccq.
+	 *
+	 * 1.8V vccq at 3.3V core voltage (vcc) is not required
+	 * in the JEDEC spec for DDR.
+	 *
+	 * Do not force change in vccq since we are obviously
+	 * working and no change to vccq is needed.
+	 *
+	 * WARNING: eMMC rules are NOT the same as SD DDR
+	 */
+	if (ddr == MMC_1_2V_DDR_MODE) {
+		err = mmc_set_signal_voltage(host,
+			MMC_SIGNAL_VOLTAGE_120, 0);
+		if (err)
+			goto out;
+	}
+	mmc_card_set_ddr_mode(card);
+	mmc_set_timing(host, MMC_TIMING_UHS_DDR50);
+	mmc_set_bus_width(host, host->ios.bus_width);
+
+out:
+	if (err && err != -EOPNOTSUPP)
+		pr_warning("%s: Switch to HighSpeed DDR mode failed (err:%d)\n",
+				mmc_hostname(host), err);
+	return err;
+}
+
+/*
+ * Select the desired buswidth and switch to HS200 mode
+ * if bus width set without error
+ */
+static int mmc_select_hs200(struct mmc_card *card, u8 *ext_csd)
+{
+	int err = 0;
+	struct mmc_host *host;
+
+	host = card->host;
+
+	if (!(host->caps2 & MMC_CAP2_HS200) ||
+		!(card->ext_csd.card_type & EXT_CSD_CARD_TYPE_HS200)) {
+		err = -EOPNOTSUPP;
+		goto out;
+	}
+
 	if (card->ext_csd.card_type & EXT_CSD_CARD_TYPE_SDR_1_2V &&
 	    host->caps2 & MMC_CAP2_HS200_1_2V_SDR)
 		if (mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_120, 0))
 			err = mmc_set_signal_voltage(host,
 						     MMC_SIGNAL_VOLTAGE_180, 0);
-
 	/* If fails try again during next card power cycle */
 	if (err)
-		goto err;
-
-	idx = (host->caps & MMC_CAP_8_BIT_DATA) ? 1 : 0;
+		goto out;
 
 	/*
-	 * Unlike SD, MMC cards dont have a configuration register to notify
-	 * supported bus width. So bus test command should be run to identify
-	 * the supported bus width or compare the ext csd values of current
-	 * bus width and ext csd values of 1 bit mode read earlier.
+	 * For devices supporting HS200 mode, the bus width has
+	 * to be set before executing the tuning function. If
+	 * set before tuning, then device will respond with CRC
+	 * errors for responses on CMD line. So for HS200 the
+	 * sequence will be
+	 * 1. set bus width 4bit / 8 bit (1 bit not supported)
+	 * 2. switch to HS200 mode
+	 * 3. set the clock to > 52Mhz <=200MHz and
+	 * 4. execute tuning for HS200
 	 */
-	for (; idx >= 0; idx--) {
+	err = mmc_select_bus_width(card, 0, ext_csd);
+	if (err) {
+		pr_err("%s: select bus width failed\n",
+			mmc_hostname(host));
+		goto out;
+	}
 
-		/*
-		 * Host is capable of 8bit transfer, then switch
-		 * the device to work in 8bit transfer mode. If the
-		 * mmc switch command returns error then switch to
-		 * 4bit transfer mode. On success set the corresponding
-		 * bus width on the host.
-		 */
-		err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
-				 EXT_CSD_BUS_WIDTH,
-				 ext_csd_bits[idx],
-				 card->ext_csd.generic_cmd6_time);
-		if (err)
-			continue;
-
-		mmc_set_bus_width(card->host, bus_widths[idx]);
-
-		if (!(host->caps & MMC_CAP_BUS_WIDTH_TEST))
-			err = mmc_compare_ext_csds(card, bus_widths[idx]);
-		else
-			err = mmc_bus_test(card, bus_widths[idx]);
-		if (!err)
-			break;
+	if (host->ios.bus_width == MMC_BUS_WIDTH_1) {
+		pr_err("%s: failed to switch to wide bus\n",
+			mmc_hostname(host));
+		goto out;
 	}
 
 	/* switch to HS200 mode if bus width set successfully */
-	if (!err)
-		err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
-				 EXT_CSD_HS_TIMING, 2, 0);
-err:
+	err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
+				EXT_CSD_HS_TIMING, 2, 0);
+
+	if (err && err != -EBADMSG) {
+		pr_err("%s: HS200 switch failed\n",
+			mmc_hostname(host));
+		goto out;
+	}
+
+	/*
+	 * When HS200 activation is performed as part of HS400 selection
+	 * set the timing appropriately
+	 */
+	if (mmc_card_hs400(card))
+		mmc_set_timing(host, MMC_TIMING_MMC_HS400);
+	else
+		mmc_set_timing(host, MMC_TIMING_MMC_HS200);
+
+	mmc_set_clock(host, MMC_HS200_MAX_DTR);
+
+	if (host->ops->execute_tuning) {
+		mmc_host_clk_hold(host);
+		err = host->ops->execute_tuning(host,
+				MMC_SEND_TUNING_BLOCK_HS200);
+		mmc_host_clk_release(host);
+	}
+	if (err) {
+		pr_warning("%s: tuning execution failed\n",
+			   mmc_hostname(host));
+		goto out;
+	}
+	mmc_card_set_hs200(card);
+
+out:
+	if (err && err != -EOPNOTSUPP)
+		pr_warning("%s: Switch to HS200 mode failed (err:%d)\n",
+				mmc_hostname(host), err);
+	return err;
+}
+
+static int mmc_select_hs400(struct mmc_card *card, u8 *ext_csd)
+{
+	int err = 0;
+	struct mmc_host *host;
+
+	host = card->host;
+
+	if (!(host->caps2 & MMC_CAP2_HS400) ||
+		!(card->ext_csd.card_type & EXT_CSD_CARD_TYPE_HS400)) {
+		err = -EOPNOTSUPP;
+		goto out;
+	}
+
+	/*
+	 * eMMC5.0 spec doesn't allow switching to HS400 mode from
+	 * HS200 mode directly. Hence follow these steps to switch
+	 * to HS400 mode:
+	 *	Enable HS200 mode
+	 *	Enable HighSpeed mode (The clk should be low enough
+	 *		to enable HighSpeed mode) - HS_TIMING is 0x1
+	 *	Enable DDR mode (Set bus width to 8-bit DDR)
+	 *	Enable HS400 mode (Set HS_TIMING to 0x3 and change
+	 *		frequency to <= 200MHz)
+	 *	Perform tuning if required
+	 */
+	mmc_card_set_hs400(card);
+	err = mmc_select_hs200(card, ext_csd);
+	if (err)
+		goto out;
+	mmc_card_clr_hs200(card);
+
+	if ((card->ext_csd.card_type & EXT_CSD_CARD_TYPE_HS400_1_2V)
+	    && (host->caps2 & MMC_CAP2_HS400_1_2V))
+		if (mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_120, 0))
+				err = mmc_set_signal_voltage(host,
+						MMC_SIGNAL_VOLTAGE_180, 0);
+	/* If fails try again during next card power cycle */
+	if (err)
+		goto out;
+
+	/*
+	 * Lower the clock and adjust the timing to be able
+	 * to switch to HighSpeed mode
+	 */
+	mmc_set_timing(host, MMC_TIMING_LEGACY);
+	mmc_set_clock(host, MMC_HIGH_26_MAX_DTR);
+
+	err = mmc_select_hs(card, ext_csd);
+	if (err)
+		goto out;
+	mmc_card_clr_highspeed(card);
+
+	/* Switch to 8-bit DDR mode */
+	err = mmc_select_hsddr(card, ext_csd);
+	if (err)
+		goto out;
+	mmc_card_clr_ddr_mode(card);
+
+	/*
+	 * In HS400 mode only DDR 8-bit bus width is allowed.
+	 */
+	if (host->ios.bus_width != MMC_BUS_WIDTH_8) {
+		pr_err("%s: failed to switch to 8-bit bus width\n",
+			mmc_hostname(host));
+		goto out;
+	}
+
+	/* Switch to HS400 mode if bus width set successfully */
+	err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
+				 EXT_CSD_HS_TIMING, 3, 0);
+	if (err && err != -EBADMSG) {
+		pr_err("%s: Setting HS_TIMING to HS400 failed (err:%d)\n",
+			mmc_hostname(host), err);
+		goto out;
+	}
+
+	mmc_set_timing(host, MMC_TIMING_MMC_HS400);
+	mmc_set_clock(host, MMC_HS400_MAX_DTR);
+
+	if (host->ops->execute_tuning) {
+		mmc_host_clk_hold(host);
+		err = host->ops->execute_tuning(host,
+				MMC_SEND_TUNING_BLOCK_HS400);
+		mmc_host_clk_release(host);
+	}
+	if (err) {
+		pr_err("%s: tuning execution failed (err:%d)\n",
+			   mmc_hostname(host), err);
+		goto out;
+	}
+	mmc_card_set_hs400(card);
+
+out:
+	if (err && err != -EOPNOTSUPP) {
+		pr_warning("%s: Switch to HS400 mode failed (err:%d)\n",
+				mmc_hostname(host), err);
+		mmc_card_clr_hs400(card);
+	}
 	return err;
 }
 
@@ -869,7 +1208,8 @@
 	}
 
 	if (mmc_card_highspeed(card) || mmc_card_hs200(card)
-			|| mmc_card_ddr_mode(card)) {
+			|| mmc_card_ddr_mode(card)
+			|| mmc_card_hs400(card)) {
 		if (*freq > card->ext_csd.hs_max_dtr)
 			*freq = card->ext_csd.hs_max_dtr;
 	} else if (*freq > card->csd.max_dtr) {
@@ -881,7 +1221,8 @@
 
 	mmc_set_clock(host, (unsigned int) (*freq));
 
-	if (mmc_card_hs200(card) && card->host->ops->execute_tuning) {
+	if ((mmc_card_hs400(card) || mmc_card_hs200(card))
+		&& card->host->ops->execute_tuning) {
 		/*
 		 * We try to probe host driver for tuning for any
 		 * frequency, it is host driver responsibility to
@@ -919,6 +1260,35 @@
 }
 
 /*
+ * Activate highest bus speed mode supported by both host and card.
+ * On failure activate the next supported highest bus speed mode.
+ */
+static int mmc_select_bus_speed(struct mmc_card *card, u8 *ext_csd)
+{
+	int err = 0;
+
+	BUG_ON(!card);
+
+	if (!mmc_select_hs400(card, ext_csd))
+		goto out;
+	if (!mmc_select_hs200(card, ext_csd))
+		goto out;
+	if (!mmc_select_hsddr(card, ext_csd))
+		goto out;
+	if (!mmc_select_hs(card, ext_csd))
+		goto out;
+
+	/*
+	 * Select the default speed and wide bus if supported
+	 */
+	mmc_set_clock(card->host, card->csd.max_dtr);
+	err = mmc_select_bus_width(card, 0, ext_csd);
+
+out:
+	return err;
+}
+
+/*
  * Handle the detection and initialisation of a card.
  *
  * In the case of a resume, "oldcard" will contain the card
@@ -928,9 +1298,8 @@
 	struct mmc_card *oldcard)
 {
 	struct mmc_card *card;
-	int err, ddr = 0;
+	int err = 0;
 	u32 cid[4];
-	unsigned int max_dtr;
 	u32 rocr;
 	u8 *ext_csd = NULL;
 
@@ -1132,209 +1501,11 @@
 	}
 
 	/*
-	 * Activate high speed (if supported)
+	 * Activate highest bus speed mode supported by both host and card.
 	 */
-	if (card->ext_csd.hs_max_dtr != 0) {
-		err = 0;
-		if (card->ext_csd.hs_max_dtr > 52000000 &&
-		    host->caps2 & MMC_CAP2_HS200)
-			err = mmc_select_hs200(card);
-		else if	(host->caps & MMC_CAP_MMC_HIGHSPEED)
-			err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
-					 EXT_CSD_HS_TIMING, 1,
-					 card->ext_csd.generic_cmd6_time);
-
-		if (err && err != -EBADMSG)
-			goto free_card;
-
-		if (err) {
-			pr_warning("%s: switch to highspeed failed\n",
-			       mmc_hostname(card->host));
-			err = 0;
-		} else {
-			if (card->ext_csd.hs_max_dtr > 52000000 &&
-			    host->caps2 & MMC_CAP2_HS200) {
-				mmc_card_set_hs200(card);
-				mmc_set_timing(card->host,
-					       MMC_TIMING_MMC_HS200);
-			} else {
-				mmc_card_set_highspeed(card);
-				mmc_set_timing(card->host, MMC_TIMING_MMC_HS);
-			}
-		}
-	}
-
-	/*
-	 * Compute bus speed.
-	 */
-	max_dtr = (unsigned int)-1;
-
-	if (mmc_card_highspeed(card) || mmc_card_hs200(card)) {
-		if (max_dtr > card->ext_csd.hs_max_dtr)
-			max_dtr = card->ext_csd.hs_max_dtr;
-	} else if (max_dtr > card->csd.max_dtr) {
-		max_dtr = card->csd.max_dtr;
-	}
-
-	mmc_set_clock(host, max_dtr);
-
-	/*
-	 * Indicate DDR mode (if supported).
-	 */
-	if (mmc_card_highspeed(card)) {
-		if ((card->ext_csd.card_type & EXT_CSD_CARD_TYPE_DDR_1_8V)
-			&& ((host->caps & (MMC_CAP_1_8V_DDR |
-			     MMC_CAP_UHS_DDR50))
-				== (MMC_CAP_1_8V_DDR | MMC_CAP_UHS_DDR50)))
-				ddr = MMC_1_8V_DDR_MODE;
-		else if ((card->ext_csd.card_type & EXT_CSD_CARD_TYPE_DDR_1_2V)
-			&& ((host->caps & (MMC_CAP_1_2V_DDR |
-			     MMC_CAP_UHS_DDR50))
-				== (MMC_CAP_1_2V_DDR | MMC_CAP_UHS_DDR50)))
-				ddr = MMC_1_2V_DDR_MODE;
-	}
-
-	/*
-	 * Indicate HS200 SDR mode (if supported).
-	 */
-	if (mmc_card_hs200(card)) {
-		u32 ext_csd_bits;
-		u32 bus_width = card->host->ios.bus_width;
-
-		/*
-		 * For devices supporting HS200 mode, the bus width has
-		 * to be set before executing the tuning function. If
-		 * set before tuning, then device will respond with CRC
-		 * errors for responses on CMD line. So for HS200 the
-		 * sequence will be
-		 * 1. set bus width 4bit / 8 bit (1 bit not supported)
-		 * 2. switch to HS200 mode
-		 * 3. set the clock to > 52Mhz <=200MHz and
-		 * 4. execute tuning for HS200
-		 */
-		if ((host->caps2 & MMC_CAP2_HS200) &&
-		    card->host->ops->execute_tuning) {
-			mmc_host_clk_hold(card->host);
-			err = card->host->ops->execute_tuning(card->host,
-				MMC_SEND_TUNING_BLOCK_HS200);
-			mmc_host_clk_release(card->host);
-		}
-		if (err) {
-			pr_warning("%s: tuning execution failed\n",
-				   mmc_hostname(card->host));
-			goto err;
-		}
-
-		ext_csd_bits = (bus_width == MMC_BUS_WIDTH_8) ?
-				EXT_CSD_BUS_WIDTH_8 : EXT_CSD_BUS_WIDTH_4;
-		err = mmc_select_powerclass(card, ext_csd_bits, ext_csd);
-		if (err)
-			pr_warning("%s: power class selection to bus width %d"
-				   " failed\n", mmc_hostname(card->host),
-				   1 << bus_width);
-	}
-
-	/*
-	 * Activate wide bus and DDR (if supported).
-	 */
-	if (!mmc_card_hs200(card) &&
-	    (card->csd.mmca_vsn >= CSD_SPEC_VER_4) &&
-	    (host->caps & (MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA))) {
-		static unsigned ext_csd_bits[][2] = {
-			{ EXT_CSD_BUS_WIDTH_8, EXT_CSD_DDR_BUS_WIDTH_8 },
-			{ EXT_CSD_BUS_WIDTH_4, EXT_CSD_DDR_BUS_WIDTH_4 },
-			{ EXT_CSD_BUS_WIDTH_1, EXT_CSD_BUS_WIDTH_1 },
-		};
-		static unsigned bus_widths[] = {
-			MMC_BUS_WIDTH_8,
-			MMC_BUS_WIDTH_4,
-			MMC_BUS_WIDTH_1
-		};
-		unsigned idx, bus_width = 0;
-
-		if (host->caps & MMC_CAP_8_BIT_DATA)
-			idx = 0;
-		else
-			idx = 1;
-		for (; idx < ARRAY_SIZE(bus_widths); idx++) {
-			bus_width = bus_widths[idx];
-			if (bus_width == MMC_BUS_WIDTH_1)
-				ddr = 0; /* no DDR for 1-bit width */
-			err = mmc_select_powerclass(card, ext_csd_bits[idx][0],
-						    ext_csd);
-			if (err)
-				pr_warning("%s: power class selection to "
-					   "bus width %d failed\n",
-					   mmc_hostname(card->host),
-					   1 << bus_width);
-
-			err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
-					 EXT_CSD_BUS_WIDTH,
-					 ext_csd_bits[idx][0],
-					 card->ext_csd.generic_cmd6_time);
-			if (!err) {
-				mmc_set_bus_width(card->host, bus_width);
-
-				/*
-				 * If controller can't handle bus width test,
-				 * compare ext_csd previously read in 1 bit mode
-				 * against ext_csd at new bus width
-				 */
-				if (!(host->caps & MMC_CAP_BUS_WIDTH_TEST))
-					err = mmc_compare_ext_csds(card,
-						bus_width);
-				else
-					err = mmc_bus_test(card, bus_width);
-				if (!err)
-					break;
-			}
-		}
-
-		if (!err && ddr) {
-			err = mmc_select_powerclass(card, ext_csd_bits[idx][1],
-						    ext_csd);
-			if (err)
-				pr_warning("%s: power class selection to "
-					   "bus width %d ddr %d failed\n",
-					   mmc_hostname(card->host),
-					   1 << bus_width, ddr);
-
-			err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
-					 EXT_CSD_BUS_WIDTH,
-					 ext_csd_bits[idx][1],
-					 card->ext_csd.generic_cmd6_time);
-		}
-		if (err) {
-			pr_warning("%s: switch to bus width %d ddr %d "
-				"failed\n", mmc_hostname(card->host),
-				1 << bus_width, ddr);
-			goto free_card;
-		} else if (ddr) {
-			/*
-			 * eMMC cards can support 3.3V to 1.2V i/o (vccq)
-			 * signaling.
-			 *
-			 * EXT_CSD_CARD_TYPE_DDR_1_8V means 3.3V or 1.8V vccq.
-			 *
-			 * 1.8V vccq at 3.3V core voltage (vcc) is not required
-			 * in the JEDEC spec for DDR.
-			 *
-			 * Do not force change in vccq since we are obviously
-			 * working and no change to vccq is needed.
-			 *
-			 * WARNING: eMMC rules are NOT the same as SD DDR
-			 */
-			if (ddr == MMC_1_2V_DDR_MODE) {
-				err = mmc_set_signal_voltage(host,
-					MMC_SIGNAL_VOLTAGE_120, 0);
-				if (err)
-					goto err;
-			}
-			mmc_card_set_ddr_mode(card);
-			mmc_set_timing(card->host, MMC_TIMING_UHS_DDR50);
-			mmc_set_bus_width(card->host, bus_width);
-		}
-	}
+	err = mmc_select_bus_speed(card, ext_csd);
+	if (err)
+		goto free_card;
 
 	/*
 	 * Enable HPI feature (if supported)
@@ -1356,9 +1527,10 @@
 	/*
 	 * If cache size is higher than 0, this indicates
 	 * the existence of cache and it can be turned on.
+	 * If HPI is not supported then cache shouldn't be enabled.
 	 */
 	if ((host->caps2 & MMC_CAP2_CACHE_CTRL) &&
-			card->ext_csd.cache_size > 0) {
+	    (card->ext_csd.cache_size > 0) && card->ext_csd.hpi_en) {
 		err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
 				EXT_CSD_CACHE_CTRL, 1,
 				card->ext_csd.generic_cmd6_time);
diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c
index 164c418..32e126b 100644
--- a/drivers/mmc/core/mmc_ops.c
+++ b/drivers/mmc/core/mmc_ops.c
@@ -598,7 +598,7 @@
 	unsigned int opcode;
 	int err;
 
-	if (!card->ext_csd.hpi) {
+	if (!card->ext_csd.hpi_en) {
 		pr_warning("%s: Card didn't support HPI command\n",
 			   mmc_hostname(card->host));
 		return -EINVAL;
diff --git a/drivers/mmc/core/quirks.c b/drivers/mmc/core/quirks.c
index 59f0340..4407d91 100644
--- a/drivers/mmc/core/quirks.c
+++ b/drivers/mmc/core/quirks.c
@@ -107,6 +107,8 @@
 		    (f->name == CID_NAME_ANY ||
 		     !strncmp(f->name, card->cid.prod_name,
 			      sizeof(card->cid.prod_name))) &&
+		    (f->ext_csd_rev == EXT_CSD_REV_ANY ||
+		     f->ext_csd_rev == card->ext_csd.rev) &&
 		    (f->cis_vendor == card->cis.vendor ||
 		     f->cis_vendor == (u16) SDIO_ANY_ID) &&
 		    (f->cis_device == card->cis.device ||
diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c
index 33dfad2..a7ce6ae 100644
--- a/drivers/mmc/host/sdhci-msm.c
+++ b/drivers/mmc/host/sdhci-msm.c
@@ -45,6 +45,7 @@
 #define SDHCI_VER_100		0x2B
 #define CORE_HC_MODE		0x78
 #define HC_MODE_EN		0x1
+#define FF_CLK_SW_RST_DIS	(1 << 13)
 
 #define CORE_POWER		0x0
 #define CORE_SW_RST		(1 << 7)
@@ -67,20 +68,57 @@
 #define INT_MASK		0xF
 #define MAX_PHASES		16
 
-#define CORE_DLL_LOCK		(1 << 7)
+#define CORE_DLL_CONFIG		0x100
+#define CORE_CMD_DAT_TRACK_SEL	(1 << 0)
 #define CORE_DLL_EN		(1 << 16)
 #define CORE_CDR_EN		(1 << 17)
 #define CORE_CK_OUT_EN		(1 << 18)
 #define CORE_CDR_EXT_EN		(1 << 19)
 #define CORE_DLL_PDN		(1 << 29)
 #define CORE_DLL_RST		(1 << 30)
-#define CORE_DLL_CONFIG		0x100
-#define CORE_DLL_TEST_CTL	0x104
+
 #define CORE_DLL_STATUS		0x108
+#define CORE_DLL_LOCK		(1 << 7)
 
 #define CORE_VENDOR_SPEC	0x10C
 #define CORE_CLK_PWRSAVE	(1 << 1)
+#define CORE_HC_MCLK_SEL_DFLT	(2 << 8)
+#define CORE_HC_MCLK_SEL_HS400	(3 << 8)
+#define CORE_HC_MCLK_SEL_MASK	(3 << 8)
 #define CORE_IO_PAD_PWR_SWITCH	(1 << 16)
+#define CORE_HC_SELECT_IN_EN	(1 << 18)
+#define CORE_HC_SELECT_IN_HS400	(6 << 19)
+#define CORE_HC_SELECT_IN_MASK	(7 << 19)
+
+#define CORE_VENDOR_SPEC_ADMA_ERR_ADDR0	0x114
+#define CORE_VENDOR_SPEC_ADMA_ERR_ADDR1	0x118
+
+#define CORE_CSR_CDC_CTLR_CFG0		0x130
+#define CORE_SW_TRIG_FULL_CALIB		(1 << 16)
+#define CORE_HW_AUTOCAL_ENA		(1 << 17)
+
+#define CORE_CSR_CDC_CTLR_CFG1		0x134
+#define CORE_CSR_CDC_CAL_TIMER_CFG0	0x138
+#define CORE_TIMER_ENA			(1 << 16)
+
+#define CORE_CSR_CDC_CAL_TIMER_CFG1	0x13C
+#define CORE_CSR_CDC_REFCOUNT_CFG	0x140
+#define CORE_CSR_CDC_COARSE_CAL_CFG	0x144
+#define CORE_CDC_OFFSET_CFG		0x14C
+#define CORE_CSR_CDC_DELAY_CFG		0x150
+#define CORE_CDC_SLAVE_DDA_CFG		0x160
+#define CORE_CSR_CDC_STATUS0		0x164
+#define CORE_CALIBRATION_DONE		(1 << 0)
+
+#define CORE_CDC_ERROR_CODE_MASK	0x7000000
+
+#define CORE_CSR_CDC_GEN_CFG		0x178
+#define CORE_CDC_SWITCH_BYPASS_OFF	(1 << 0)
+#define CORE_CDC_SWITCH_RC_EN		(1 << 1)
+
+#define CORE_DDR_200_CFG		0x184
+#define CORE_CDC_T4_DLY_SEL		(1 << 0)
+#define CORE_START_CDC_TRAFFIC		(1 << 6)
 
 #define CORE_MCI_DATA_CTRL	0x2C
 #define CORE_MCI_DPSM_ENABLE	(1 << 0)
@@ -114,6 +152,10 @@
 #define SDHCI_MSM_MAX_SEGMENTS  (1 << 13)
 #define SDHCI_MSM_MMC_CLK_GATE_DELAY	200 /* msecs */
 
+#define CORE_FREQ_100MHZ	(100 * 1000 * 1000)
+
+#define INVALID_TUNING_PHASE	-1
+
 static const u32 tuning_block_64[] = {
 	0x00FF0FFF, 0xCCC3CCFF, 0xFFCC3CC3, 0xEFFEFFFE,
 	0xDDFFDFFF, 0xFBFFFBFF, 0xFF7FFFBF, 0xEFBDF777,
@@ -260,6 +302,8 @@
 	struct clk	 *clk;     /* main SD/MMC bus clock */
 	struct clk	 *pclk;    /* SDHC peripheral bus clock */
 	struct clk	 *bus_clk; /* SDHC bus voter clock */
+	struct clk	 *ff_clk; /* CDC calibration fixed feedback clock */
+	struct clk	 *sleep_clk; /* CDC calibration sleep clock */
 	atomic_t clks_on; /* Set if clocks are enabled */
 	struct sdhci_msm_pltfm_data *pdata;
 	struct mmc_host  *mmc;
@@ -270,6 +314,9 @@
 	struct sdhci_msm_bus_vote msm_bus_vote;
 	struct device_attribute	polling;
 	u32 clk_rate; /* Keeps track of current clock rate that is set */
+	bool tuning_done;
+	bool calibration_done;
+	u8 saved_tuning_phase;
 };
 
 enum vdd_io_level {
@@ -373,8 +420,8 @@
  * Find out the greatest range of consecuitive selected
  * DLL clock output phases that can be used as sampling
  * setting for SD3.0 UHS-I card read operation (in SDR104
- * timing mode) or for eMMC4.5 card read operation (in HS200
- * timing mode).
+ * timing mode) or for eMMC4.5 card read operation (in
+ * HS400/HS200 timing mode).
  * Select the 3/4 of the range and configure the DLL with the
  * selected DLL clock output phase.
  */
@@ -595,6 +642,137 @@
 	return rc;
 }
 
+static int sdhci_msm_cdclp533_calibration(struct sdhci_host *host)
+{
+	u32 wait_cnt;
+	int ret = 0;
+	int cdc_err = 0;
+	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+	struct sdhci_msm_host *msm_host = pltfm_host->priv;
+
+	pr_debug("%s: Enter %s\n", mmc_hostname(host->mmc), __func__);
+
+	/*
+	 * Retuning in HS400 (DDR mode) will fail, just reset the
+	 * tuning block and restore the saved tuning phase.
+	 */
+	ret = msm_init_cm_dll(host);
+	if (ret)
+		goto out;
+
+	/* Set the selected phase in delay line hw block */
+	ret = msm_config_cm_dll_phase(host, msm_host->saved_tuning_phase);
+	if (ret)
+		goto out;
+
+	/* Write 1 to CMD_DAT_TRACK_SEL field in DLL_CONFIG */
+	writel_relaxed((readl_relaxed(host->ioaddr + CORE_DLL_CONFIG)
+			| CORE_CMD_DAT_TRACK_SEL),
+			host->ioaddr + CORE_DLL_CONFIG);
+
+	/* Write 0 to CDC_T4_DLY_SEL field in VENDOR_SPEC_DDR200_CFG */
+	writel_relaxed((readl_relaxed(host->ioaddr + CORE_DDR_200_CFG)
+			& ~CORE_CDC_T4_DLY_SEL),
+			host->ioaddr + CORE_DDR_200_CFG);
+
+	/* Write 0 to CDC_SWITCH_BYPASS_OFF field in CORE_CSR_CDC_GEN_CFG */
+	writel_relaxed((readl_relaxed(host->ioaddr + CORE_CSR_CDC_GEN_CFG)
+			& ~CORE_CDC_SWITCH_BYPASS_OFF),
+			host->ioaddr + CORE_CSR_CDC_GEN_CFG);
+
+	/* Write 1 to CDC_SWITCH_RC_EN field in CORE_CSR_CDC_GEN_CFG */
+	writel_relaxed((readl_relaxed(host->ioaddr + CORE_CSR_CDC_GEN_CFG)
+			| CORE_CDC_SWITCH_RC_EN),
+			host->ioaddr + CORE_CSR_CDC_GEN_CFG);
+
+	/* Write 0 to START_CDC_TRAFFIC field in CORE_DDR200_CFG */
+	writel_relaxed((readl_relaxed(host->ioaddr + CORE_DDR_200_CFG)
+			& ~CORE_START_CDC_TRAFFIC),
+			host->ioaddr + CORE_DDR_200_CFG);
+
+	/*
+	 * Perform CDC Register Initialization Sequence
+	 *
+	 * CORE_CSR_CDC_CTLR_CFG0	0x11800EC
+	 * CORE_CSR_CDC_CTLR_CFG1	0x3011111
+	 * CORE_CSR_CDC_CAL_TIMER_CFG0	0x1201000
+	 * CORE_CSR_CDC_CAL_TIMER_CFG1	0x4
+	 * CORE_CSR_CDC_REFCOUNT_CFG	0xCB732020
+	 * CORE_CSR_CDC_COARSE_CAL_CFG	0xB19
+	 * CORE_CSR_CDC_DELAY_CFG	0x3AC
+	 * CORE_CDC_OFFSET_CFG		0x0
+	 * CORE_CDC_SLAVE_DDA_CFG	0x16334
+	 */
+
+	writel_relaxed(0x11800EC, host->ioaddr + CORE_CSR_CDC_CTLR_CFG0);
+	writel_relaxed(0x3011111, host->ioaddr + CORE_CSR_CDC_CTLR_CFG1);
+	writel_relaxed(0x1201000, host->ioaddr + CORE_CSR_CDC_CAL_TIMER_CFG0);
+	writel_relaxed(0x4, host->ioaddr + CORE_CSR_CDC_CAL_TIMER_CFG1);
+	writel_relaxed(0xCB732020, host->ioaddr + CORE_CSR_CDC_REFCOUNT_CFG);
+	writel_relaxed(0xB19, host->ioaddr + CORE_CSR_CDC_COARSE_CAL_CFG);
+	writel_relaxed(0x3AC, host->ioaddr + CORE_CSR_CDC_DELAY_CFG);
+	writel_relaxed(0x0, host->ioaddr + CORE_CDC_OFFSET_CFG);
+	writel_relaxed(0x16334, host->ioaddr + CORE_CDC_SLAVE_DDA_CFG);
+
+	/* CDC HW Calibration */
+
+	/* Write 1 to SW_TRIG_FULL_CALIB field in CORE_CSR_CDC_CTLR_CFG0 */
+	writel_relaxed((readl_relaxed(host->ioaddr + CORE_CSR_CDC_CTLR_CFG0)
+			| CORE_SW_TRIG_FULL_CALIB),
+			host->ioaddr + CORE_CSR_CDC_CTLR_CFG0);
+
+	/* Write 0 to SW_TRIG_FULL_CALIB field in CORE_CSR_CDC_CTLR_CFG0 */
+	writel_relaxed((readl_relaxed(host->ioaddr + CORE_CSR_CDC_CTLR_CFG0)
+			& ~CORE_SW_TRIG_FULL_CALIB),
+			host->ioaddr + CORE_CSR_CDC_CTLR_CFG0);
+
+	/* Write 1 to HW_AUTOCAL_ENA field in CORE_CSR_CDC_CTLR_CFG0 */
+	writel_relaxed((readl_relaxed(host->ioaddr + CORE_CSR_CDC_CTLR_CFG0)
+			| CORE_HW_AUTOCAL_ENA),
+			host->ioaddr + CORE_CSR_CDC_CTLR_CFG0);
+
+	/* Write 1 to TIMER_ENA field in CORE_CSR_CDC_CAL_TIMER_CFG0 */
+	writel_relaxed((readl_relaxed(host->ioaddr +
+			CORE_CSR_CDC_CAL_TIMER_CFG0) | CORE_TIMER_ENA),
+			host->ioaddr + CORE_CSR_CDC_CAL_TIMER_CFG0);
+
+	mb();
+
+	/* Poll on CALIBRATION_DONE field in CORE_CSR_CDC_STATUS0 to be 1 */
+	wait_cnt = 50;
+	while (!(readl_relaxed(host->ioaddr + CORE_CSR_CDC_STATUS0)
+			& CORE_CALIBRATION_DONE)) {
+		/* max. wait for 50us sec for CALIBRATION_DONE bit to be set */
+		if (--wait_cnt == 0) {
+			pr_err("%s: %s: CDC Calibration was not completed\n",
+				mmc_hostname(host->mmc), __func__);
+			ret = -ETIMEDOUT;
+			goto out;
+		}
+		/* wait for 1us before polling again */
+		udelay(1);
+	}
+
+	/* Verify CDC_ERROR_CODE field in CORE_CSR_CDC_STATUS0 is 0 */
+	cdc_err = readl_relaxed(host->ioaddr + CORE_CSR_CDC_STATUS0)
+			& CORE_CDC_ERROR_CODE_MASK;
+	if (cdc_err) {
+		pr_err("%s: %s: CDC Error Code %d\n",
+			mmc_hostname(host->mmc), __func__, cdc_err);
+		ret = -EINVAL;
+		goto out;
+	}
+
+	/* Write 1 to START_CDC_TRAFFIC field in CORE_DDR200_CFG */
+	writel_relaxed((readl_relaxed(host->ioaddr + CORE_DDR_200_CFG)
+			| CORE_START_CDC_TRAFFIC),
+			host->ioaddr + CORE_DDR_200_CFG);
+out:
+	pr_debug("%s: Exit %s, ret:%d\n", mmc_hostname(host->mmc),
+			__func__, ret);
+	return ret;
+}
+
 int sdhci_msm_execute_tuning(struct sdhci_host *host, u32 opcode)
 {
 	unsigned long flags;
@@ -605,20 +783,36 @@
 	int rc;
 	struct mmc_host *mmc = host->mmc;
 	struct mmc_ios	ios = host->mmc->ios;
+	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+	struct sdhci_msm_host *msm_host = pltfm_host->priv;
 
 	/*
-	 * Tuning is required for SDR104 and HS200 cards and if clock frequency
-	 * is greater than 100MHz in these modes.
+	 * Tuning is required for SDR104, HS200 and HS400 cards and
+	 * if clock frequency is greater than 100MHz in these modes.
 	 */
-	if (host->clock <= (100 * 1000 * 1000) ||
-		!(ios.timing == MMC_TIMING_MMC_HS200 ||
-		ios.timing == MMC_TIMING_UHS_SDR104))
+	if (host->clock <= CORE_FREQ_100MHZ ||
+		!((ios.timing == MMC_TIMING_MMC_HS400) ||
+		(ios.timing == MMC_TIMING_MMC_HS200) ||
+		(ios.timing == MMC_TIMING_UHS_SDR104)))
 		return 0;
 
 	pr_debug("%s: Enter %s\n", mmc_hostname(mmc), __func__);
+
+	/* CDCLP533 HW calibration is only required for HS400 mode*/
+	if (msm_host->tuning_done && !msm_host->calibration_done &&
+		(mmc->ios.timing == MMC_TIMING_MMC_HS400)) {
+		rc = sdhci_msm_cdclp533_calibration(host);
+		spin_lock_irqsave(&host->lock, flags);
+		if (!rc)
+			msm_host->calibration_done = true;
+		spin_unlock_irqrestore(&host->lock, flags);
+		goto out;
+	}
+
 	spin_lock_irqsave(&host->lock, flags);
 
-	if ((opcode == MMC_SEND_TUNING_BLOCK_HS200) &&
+	if (((opcode == MMC_SEND_TUNING_BLOCK_HS400) ||
+		(opcode == MMC_SEND_TUNING_BLOCK_HS200)) &&
 		(mmc->ios.bus_width == MMC_BUS_WIDTH_8)) {
 		tuning_block_pattern = tuning_block_128;
 		size = sizeof(tuning_block_128);
@@ -690,6 +884,7 @@
 		rc = msm_config_cm_dll_phase(host, phase);
 		if (rc)
 			goto kfree;
+		msm_host->saved_tuning_phase = phase;
 		pr_debug("%s: %s: finally setting the tuning phase to %d\n",
 				mmc_hostname(mmc), __func__, phase);
 	} else {
@@ -704,7 +899,11 @@
 kfree:
 	kfree(data_buf);
 out:
-	pr_debug("%s: Exit %s\n", mmc_hostname(mmc), __func__);
+	spin_lock_irqsave(&host->lock, flags);
+	if (!rc)
+		msm_host->tuning_done = true;
+	spin_unlock_irqrestore(&host->lock, flags);
+	pr_debug("%s: Exit %s, err(%d)\n", mmc_hostname(mmc), __func__, rc);
 	return rc;
 }
 
@@ -803,7 +1002,7 @@
 		goto out;
 	}
 	sz = *len = *len / sizeof(*arr);
-	if (sz <= 0 || (size > 0 && (sz != size))) {
+	if (sz <= 0 || (size > 0 && (sz > size))) {
 		dev_err(dev, "%s invalid size\n", prop_name);
 		ret = -EINVAL;
 		goto out;
@@ -929,9 +1128,9 @@
 		ret = -ENOMEM;
 		goto out;
 	}
-	pull_data->size = 3; /* array size for clk, cmd, data */
+	pull_data->size = 4; /* array size for clk, cmd, data and rclk */
 
-	/* Allocate on, off configs for clk, cmd, data */
+	/* Allocate on, off configs for clk, cmd, data and rclk */
 	pull = devm_kzalloc(dev, 2 * pull_data->size *\
 			sizeof(struct sdhci_msm_pad_pull), GFP_KERNEL);
 	if (!pull) {
@@ -1214,7 +1413,11 @@
 		if (!name)
 			continue;
 
-		if (!strncmp(name, "HS200_1p8v", sizeof("HS200_1p8v")))
+		if (!strncmp(name, "HS400_1p8v", sizeof("HS400_1p8v")))
+			pdata->caps2 |= MMC_CAP2_HS400_1_8V;
+		else if (!strncmp(name, "HS400_1p2v", sizeof("HS400_1p2v")))
+			pdata->caps2 |= MMC_CAP2_HS400_1_2V;
+		else if (!strncmp(name, "HS200_1p8v", sizeof("HS200_1p8v")))
 			pdata->caps2 |= MMC_CAP2_HS200_1_8V_SDR;
 		else if (!strncmp(name, "HS200_1p2v", sizeof("HS200_1p2v")))
 			pdata->caps2 |= MMC_CAP2_HS200_1_2V_SDR;
@@ -2044,6 +2247,22 @@
 				mmc_hostname(host->mmc), __func__, rc);
 			goto disable_pclk;
 		}
+		if (!IS_ERR(msm_host->ff_clk)) {
+			rc = clk_prepare_enable(msm_host->ff_clk);
+			if (rc) {
+				pr_err("%s: %s: failed to enable the ff_clk with error %d\n",
+					mmc_hostname(host->mmc), __func__, rc);
+				goto disable_clk;
+			}
+		}
+		if (!IS_ERR(msm_host->sleep_clk)) {
+			rc = clk_prepare_enable(msm_host->sleep_clk);
+			if (rc) {
+				pr_err("%s: %s: failed to enable the sleep_clk with error %d\n",
+					mmc_hostname(host->mmc), __func__, rc);
+				goto disable_ff_clk;
+			}
+		}
 		mb();
 
 	} else if (!enable && atomic_read(&msm_host->clks_on)) {
@@ -2051,6 +2270,10 @@
 				mmc_hostname(host->mmc));
 		sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL);
 		mb();
+		if (!IS_ERR_OR_NULL(msm_host->sleep_clk))
+			clk_disable_unprepare(msm_host->sleep_clk);
+		if (!IS_ERR_OR_NULL(msm_host->ff_clk))
+			clk_disable_unprepare(msm_host->ff_clk);
 		clk_disable_unprepare(msm_host->clk);
 		if (!IS_ERR(msm_host->pclk))
 			clk_disable_unprepare(msm_host->pclk);
@@ -2061,6 +2284,12 @@
 	}
 	atomic_set(&msm_host->clks_on, enable);
 	goto out;
+disable_ff_clk:
+	if (!IS_ERR_OR_NULL(msm_host->ff_clk))
+		clk_disable_unprepare(msm_host->ff_clk);
+disable_clk:
+	if (!IS_ERR_OR_NULL(msm_host->clk))
+		clk_disable_unprepare(msm_host->clk);
 disable_pclk:
 	if (!IS_ERR_OR_NULL(msm_host->pclk))
 		clk_disable_unprepare(msm_host->pclk);
@@ -2093,17 +2322,79 @@
 		return;
 
 	sup_clock = sdhci_msm_get_sup_clk_rate(host, clock);
-	if (curr_ios.timing == MMC_TIMING_UHS_DDR50) {
+	if ((curr_ios.timing == MMC_TIMING_UHS_DDR50) ||
+		(curr_ios.timing == MMC_TIMING_MMC_HS400)) {
 		/*
 		 * The SDHC requires internal clock frequency to be double the
 		 * actual clock that will be set for DDR mode. The controller
-		 * uses the faster clock(100MHz) for some of its parts and send
-		 * the actual required clock (50MHz) to the card.
+		 * uses the faster clock(100/400MHz) for some of its parts and
+		 * send the actual required clock (50/200MHz) to the card.
 		 */
 		ddr_clock = clock * 2;
 		sup_clock = sdhci_msm_get_sup_clk_rate(host,
 				ddr_clock);
 	}
+
+	/*
+	 * In general all timing modes are controlled via UHS mode select in
+	 * Host Control2 register. eMMC specific HS200/HS400 doesn't have
+	 * their respective modes defined here, hence we use these values.
+	 *
+	 * HS200 - SDR104 (Since they both are equivalent in functionality)
+	 * HS400 - This involves multiple configurations
+	 *		Initially SDR104 - when tuning is required as HS200
+	 *		Then when switching to DDR @ 400MHz (HS400) we use
+	 *		the vendor specific HC_SELECT_IN to control the mode.
+	 *
+	 * In addition to controlling the modes we also need to select the
+	 * correct input clock for DLL depending on the mode.
+	 *
+	 * HS400 - divided clock (free running MCLK/2)
+	 * All other modes - default (free running MCLK)
+	 */
+	if (curr_ios.timing == MMC_TIMING_MMC_HS400) {
+		/* Select the divided clock (free running MCLK/2) */
+		writel_relaxed(((readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC)
+					& ~CORE_HC_MCLK_SEL_MASK)
+					| CORE_HC_MCLK_SEL_HS400),
+					host->ioaddr + CORE_VENDOR_SPEC);
+		/*
+		 * Select HS400 mode using the HC_SELECT_IN from VENDOR SPEC
+		 * register
+		 */
+		if (msm_host->tuning_done && !msm_host->calibration_done) {
+			/*
+			 * Write 0x6 to HC_SELECT_IN and 1 to HC_SELECT_IN_EN
+			 * field in VENDOR_SPEC_FUNC
+			 */
+			writel_relaxed((readl_relaxed(host->ioaddr + \
+					CORE_VENDOR_SPEC)
+					| CORE_HC_SELECT_IN_HS400
+					| CORE_HC_SELECT_IN_EN),
+					host->ioaddr + CORE_VENDOR_SPEC);
+		}
+	} else {
+		/* Select the default clock (free running MCLK) */
+		writel_relaxed(((readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC)
+					& ~CORE_HC_MCLK_SEL_MASK)
+					| CORE_HC_MCLK_SEL_DFLT),
+					host->ioaddr + CORE_VENDOR_SPEC);
+
+		/*
+		 * Disable HC_SELECT_IN to be able to use the UHS mode select
+		 * configuration from Host Control2 register for all other
+		 * modes.
+		 *
+		 * Write 0 to HC_SELECT_IN and HC_SELECT_IN_EN field
+		 * in VENDOR_SPEC_FUNC
+		 */
+		writel_relaxed((readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC)
+				& ~CORE_HC_SELECT_IN_EN
+				& ~CORE_HC_SELECT_IN_MASK),
+				host->ioaddr + CORE_VENDOR_SPEC);
+	}
+	mb();
+
 	if (sup_clock != msm_host->clk_rate) {
 		pr_debug("%s: %s: setting clk rate to %u\n",
 				mmc_hostname(host->mmc), __func__, sup_clock);
@@ -2127,12 +2418,16 @@
 static int sdhci_msm_set_uhs_signaling(struct sdhci_host *host,
 					unsigned int uhs)
 {
+	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+	struct sdhci_msm_host *msm_host = pltfm_host->priv;
 	u16 ctrl_2;
 
 	ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
 	/* Select Bus Speed Mode for host */
 	ctrl_2 &= ~SDHCI_CTRL_UHS_MASK;
-	if (uhs == MMC_TIMING_MMC_HS200)
+	if (uhs == MMC_TIMING_MMC_HS400)
+		ctrl_2 |= SDHCI_CTRL_UHS_SDR104;
+	else if (uhs == MMC_TIMING_MMC_HS200)
 		ctrl_2 |= SDHCI_CTRL_UHS_SDR104;
 	else if (uhs == MMC_TIMING_UHS_SDR12)
 		ctrl_2 |= SDHCI_CTRL_UHS_SDR12;
@@ -2150,11 +2445,36 @@
 	 * provide feedback clock, the mode selection can be any value less
 	 * than 3'b011 in bits [2:0] of HOST CONTROL2 register.
 	 */
-	if (host->clock <= (100 * 1000 * 1000) &&
-			(uhs == MMC_TIMING_MMC_HS200 ||
-			uhs == MMC_TIMING_UHS_SDR104))
-		ctrl_2 &= ~SDHCI_CTRL_UHS_MASK;
+	if (host->clock <= CORE_FREQ_100MHZ) {
+		if ((uhs == MMC_TIMING_MMC_HS400) ||
+		    (uhs == MMC_TIMING_MMC_HS200) ||
+		    (uhs == MMC_TIMING_UHS_SDR104))
+			ctrl_2 &= ~SDHCI_CTRL_UHS_MASK;
 
+		/*
+		 * Make sure DLL is disabled when not required
+		 *
+		 * Write 1 to DLL_RST bit of DLL_CONFIG register
+		 */
+		writel_relaxed((readl_relaxed(host->ioaddr + CORE_DLL_CONFIG)
+				| CORE_DLL_RST),
+				host->ioaddr + CORE_DLL_CONFIG);
+
+		/* Write 1 to DLL_PDN bit of DLL_CONFIG register */
+		writel_relaxed((readl_relaxed(host->ioaddr + CORE_DLL_CONFIG)
+				| CORE_DLL_PDN),
+				host->ioaddr + CORE_DLL_CONFIG);
+		mb();
+
+		/*
+		 * The DLL needs to be restored and CDCLP533 recalibrated
+		 * when the clock frequency is set back to 400MHz.
+		 */
+		msm_host->calibration_done = false;
+	}
+
+	pr_debug("%s: %s-clock:%u uhs mode:%u ctrl_2:0x%x\n",
+		mmc_hostname(host->mmc), __func__, host->clock, uhs, ctrl_2);
 	sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2);
 
 	return 0;
@@ -2321,9 +2641,27 @@
 	msm_host->clk_rate = sdhci_msm_get_min_clock(host);
 	atomic_set(&msm_host->clks_on, 1);
 
+	/* Setup CDC calibration fixed feedback clock */
+	msm_host->ff_clk = devm_clk_get(&pdev->dev, "cal_clk");
+	if (!IS_ERR(msm_host->ff_clk)) {
+		ret = clk_prepare_enable(msm_host->ff_clk);
+		if (ret)
+			goto clk_disable;
+	}
+
+	/* Setup CDC calibration sleep clock */
+	msm_host->sleep_clk = devm_clk_get(&pdev->dev, "sleep_clk");
+	if (!IS_ERR(msm_host->sleep_clk)) {
+		ret = clk_prepare_enable(msm_host->sleep_clk);
+		if (ret)
+			goto ff_clk_disable;
+	}
+
+	msm_host->saved_tuning_phase = INVALID_TUNING_PHASE;
+
 	ret = sdhci_msm_bus_register(msm_host, pdev);
 	if (ret)
-		goto clk_disable;
+		goto sleep_clk_disable;
 
 	if (msm_host->msm_bus_vote.client_handle)
 		INIT_DELAYED_WORK(&msm_host->msm_bus_vote.vote_work,
@@ -2368,6 +2706,10 @@
 	/* Set HC_MODE_EN bit in HC_MODE register */
 	writel_relaxed(HC_MODE_EN, (msm_host->core_mem + CORE_HC_MODE));
 
+	/* Set FF_CLK_SW_RST_DIS bit in HC_MODE register */
+	writel_relaxed(readl_relaxed(msm_host->core_mem + CORE_HC_MODE) |
+			FF_CLK_SW_RST_DIS, msm_host->core_mem + CORE_HC_MODE);
+
 	/*
 	 * CORE_SW_RST above may trigger power irq if previous status of PWRCTL
 	 * was either BUS_ON or IO_HIGH_V. So before we enable the power irq
@@ -2554,6 +2896,12 @@
 	if (msm_host->msm_bus_vote.client_handle)
 		sdhci_msm_bus_cancel_work_and_set_vote(host, 0);
 	sdhci_msm_bus_unregister(msm_host);
+sleep_clk_disable:
+	if (!IS_ERR(msm_host->sleep_clk))
+		clk_disable_unprepare(msm_host->sleep_clk);
+ff_clk_disable:
+	if (!IS_ERR(msm_host->ff_clk))
+		clk_disable_unprepare(msm_host->ff_clk);
 clk_disable:
 	if (!IS_ERR(msm_host->clk))
 		clk_disable_unprepare(msm_host->clk);
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 578cc14..f9f3802 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -1129,6 +1129,7 @@
 
 	/* CMD19 is special in that the Data Present Select should be set */
 	if (cmd->data || cmd->opcode == MMC_SEND_TUNING_BLOCK ||
+	    cmd->opcode == MMC_SEND_TUNING_BLOCK_HS400 ||
 	    cmd->opcode == MMC_SEND_TUNING_BLOCK_HS200)
 		flags |= SDHCI_CMD_DATA;
 
@@ -1631,7 +1632,8 @@
 		unsigned int clock;
 
 		/* In case of UHS-I modes, set High Speed Enable */
-		if ((ios->timing == MMC_TIMING_MMC_HS200) ||
+		if ((ios->timing == MMC_TIMING_MMC_HS400) ||
+		    (ios->timing == MMC_TIMING_MMC_HS200) ||
 		    (ios->timing == MMC_TIMING_UHS_SDR50) ||
 		    (ios->timing == MMC_TIMING_UHS_SDR104) ||
 		    (ios->timing == MMC_TIMING_UHS_DDR50) ||
@@ -1686,7 +1688,9 @@
 			ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
 			/* Select Bus Speed Mode for host */
 			ctrl_2 &= ~SDHCI_CTRL_UHS_MASK;
-			if (ios->timing == MMC_TIMING_MMC_HS200)
+			if (ios->timing == MMC_TIMING_MMC_HS400)
+				ctrl_2 |= SDHCI_CTRL_HS_SDR200;
+			else if (ios->timing == MMC_TIMING_MMC_HS200)
 				ctrl_2 |= SDHCI_CTRL_HS_SDR200;
 			else if (ios->timing == MMC_TIMING_UHS_SDR12)
 				ctrl_2 |= SDHCI_CTRL_UHS_SDR12;
@@ -1982,12 +1986,13 @@
 	 * The Host Controller needs tuning only in case of SDR104 mode
 	 * and for SDR50 mode when Use Tuning for SDR50 is set in the
 	 * Capabilities register.
-	 * If the Host Controller supports the HS200 mode then the
+	 * If the Host Controller supports the HS400/HS200 mode then the
 	 * tuning function has to be executed.
 	 */
 	if ((((ctrl & SDHCI_CTRL_UHS_MASK) == SDHCI_CTRL_UHS_SDR50) &&
 	    (host->flags & SDHCI_SDR50_NEEDS_TUNING)) ||
-	     (host->flags & SDHCI_HS200_NEEDS_TUNING))
+	     (host->flags & SDHCI_HS200_NEEDS_TUNING) ||
+	     (host->flags & SDHCI_HS400_NEEDS_TUNING))
 		requires_tuning_nonuhs = true;
 
 	if (((ctrl & SDHCI_CTRL_UHS_MASK) == SDHCI_CTRL_UHS_SDR104) ||
@@ -2050,7 +2055,8 @@
 		 * block to the Host Controller. So we set the block size
 		 * to 64 here.
 		 */
-		if (cmd.opcode == MMC_SEND_TUNING_BLOCK_HS200) {
+		if ((cmd.opcode == MMC_SEND_TUNING_BLOCK_HS400) ||
+		    (cmd.opcode == MMC_SEND_TUNING_BLOCK_HS200)) {
 			if (mmc->ios.bus_width == MMC_BUS_WIDTH_8)
 				sdhci_writew(host, SDHCI_MAKE_BLKSZ(7, 128),
 					     SDHCI_BLOCK_SIZE);
@@ -2451,7 +2457,8 @@
 		host->cmd->error = -EILSEQ;
 
 	if (host->quirks2 & SDHCI_QUIRK2_IGNORE_CMDCRC_FOR_TUNING) {
-		if ((host->cmd->opcode == MMC_SEND_TUNING_BLOCK_HS200) ||
+		if ((host->cmd->opcode == MMC_SEND_TUNING_BLOCK_HS400) ||
+			(host->cmd->opcode == MMC_SEND_TUNING_BLOCK_HS200) ||
 			(host->cmd->opcode == MMC_SEND_TUNING_BLOCK)) {
 			if (intmask & SDHCI_INT_CRC) {
 				sdhci_reset(host, SDHCI_RESET_CMD);
@@ -2488,7 +2495,8 @@
 	}
 
 	if (host->quirks2 & SDHCI_QUIRK2_IGNORE_CMDCRC_FOR_TUNING) {
-		if ((host->cmd->opcode == MMC_SEND_TUNING_BLOCK_HS200) ||
+		if ((host->cmd->opcode == MMC_SEND_TUNING_BLOCK_HS400) ||
+			(host->cmd->opcode == MMC_SEND_TUNING_BLOCK_HS200) ||
 			(host->cmd->opcode == MMC_SEND_TUNING_BLOCK)) {
 			if (intmask & SDHCI_INT_CRC) {
 				sdhci_finish_command(host);
@@ -2536,7 +2544,8 @@
 	if (intmask & SDHCI_INT_DATA_AVAIL) {
 		command = SDHCI_GET_CMD(sdhci_readw(host, SDHCI_COMMAND));
 		if (command == MMC_SEND_TUNING_BLOCK ||
-		    command == MMC_SEND_TUNING_BLOCK_HS200) {
+		    command == MMC_SEND_TUNING_BLOCK_HS200 ||
+		    command == MMC_SEND_TUNING_BLOCK_HS400) {
 			host->tuning_done = 1;
 			wake_up(&host->buf_ready_int);
 			return;
@@ -2585,7 +2594,8 @@
 		    (host->quirks2 & SDHCI_QUIRK2_IGNORE_CMDCRC_FOR_TUNING)) {
 			command = SDHCI_GET_CMD(sdhci_readw(host,
 							    SDHCI_COMMAND));
-			if ((command != MMC_SEND_TUNING_BLOCK_HS200) &&
+			if ((command != MMC_SEND_TUNING_BLOCK_HS400) &&
+			    (command != MMC_SEND_TUNING_BLOCK_HS200) &&
 			    (command != MMC_SEND_TUNING_BLOCK))
 				pr_msg = true;
 		} else {
@@ -3217,6 +3227,10 @@
 	if (mmc->caps2 & MMC_CAP2_HS200)
 		host->flags |= SDHCI_HS200_NEEDS_TUNING;
 
+	/* Does the host need tuning for HS400? */
+	if (mmc->caps2 & MMC_CAP2_HS400)
+		host->flags |= SDHCI_HS400_NEEDS_TUNING;
+
 	/* Driver Type(s) (A, C, D) supported by the host */
 	if (caps[1] & SDHCI_DRIVER_TYPE_A)
 		mmc->caps |= MMC_CAP_DRIVER_TYPE_A;
diff --git a/drivers/of/of_batterydata.c b/drivers/of/of_batterydata.c
index 977a1e0..2061408 100644
--- a/drivers/of/of_batterydata.c
+++ b/drivers/of/of_batterydata.c
@@ -155,6 +155,35 @@
 	return 0;
 }
 
+static int of_batterydata_read_batt_id_kohm(const struct device_node *np,
+				const char *propname, struct batt_ids *batt_ids)
+{
+	struct property *prop;
+	const __be32 *data;
+	int num, i, *id_kohm = batt_ids->kohm;
+
+	prop = of_find_property(np, "qcom,batt-id-kohm", NULL);
+	if (!prop) {
+		pr_err("%s: No battery id resistor found\n", np->name);
+		return -EINVAL;
+	} else if (!prop->value) {
+		pr_err("%s: No battery id resistor value found, np->name\n",
+						np->name);
+		return -ENODATA;
+	} else if (prop->length > MAX_BATT_ID_NUM * sizeof(__be32)) {
+		pr_err("%s: Too many battery id resistors\n", np->name);
+		return -EINVAL;
+	}
+
+	num = prop->length/sizeof(__be32);
+	batt_ids->num = num;
+	data = prop->value;
+	for (i = 0; i < num; i++)
+		*id_kohm++ = be32_to_cpup(data++);
+
+	return 0;
+}
+
 #define OF_PROP_READ(property, qpnp_dt_property, node, rc, optional)	\
 do {									\
 	if (rc)								\
@@ -172,6 +201,7 @@
 } while (0)
 
 static int of_batterydata_load_battery_data(struct device_node *node,
+				int best_id_kohm,
 				struct bms_battery_data *batt_data)
 {
 	int rc;
@@ -197,7 +227,6 @@
 			"default-rbatt-mohm", node, rc, false);
 	OF_PROP_READ(batt_data->rbatt_capacitive_mohm,
 			"rbatt-capacitive-mohm", node, rc, false);
-	OF_PROP_READ(batt_data->batt_id_kohm, "batt-id-kohm", node, rc, false);
 	OF_PROP_READ(batt_data->flat_ocv_threshold_uv,
 			"flat-ocv-threshold", node, rc, true);
 	OF_PROP_READ(batt_data->max_voltage_uv,
@@ -205,6 +234,8 @@
 	OF_PROP_READ(batt_data->cutoff_uv, "v-cutoff-uv", node, rc, true);
 	OF_PROP_READ(batt_data->iterm_ua, "chg-term-ua", node, rc, true);
 
+	batt_data->batt_id_kohm = best_id_kohm;
+
 	return rc;
 }
 
@@ -229,8 +260,9 @@
 				int batt_id_uv)
 {
 	struct device_node *node, *best_node;
-	uint32_t id_kohm;
-	int delta, best_delta, batt_id_kohm, rpull_up_kohm, vadc_vdd_uv, rc = 0;
+	struct batt_ids batt_ids;
+	int delta, best_delta, batt_id_kohm, rpull_up_kohm,
+		vadc_vdd_uv, best_id_kohm, i, rc = 0;
 
 	node = batterydata_container_node;
 	OF_PROP_READ(rpull_up_kohm, "rpull-up-kohm", node, rc, false);
@@ -242,18 +274,24 @@
 					rpull_up_kohm, vadc_vdd_uv);
 	best_node = NULL;
 	best_delta = 0;
+	best_id_kohm = 0;
 
 	/*
 	 * Find the battery data with a battery id resistor closest to this one
 	 */
 	for_each_child_of_node(batterydata_container_node, node) {
-		rc = of_property_read_u32(node, "qcom,batt-id-kohm", &id_kohm);
+		rc = of_batterydata_read_batt_id_kohm(node,
+						"qcom,batt-id-kohm",
+						&batt_ids);
 		if (rc)
 			continue;
-		delta = abs((int)id_kohm - batt_id_kohm);
-		if (delta < best_delta || !best_node) {
-			best_node = node;
-			best_delta = delta;
+		for (i = 0; i < batt_ids.num; i++) {
+			delta = abs(batt_ids.kohm[i] - batt_id_kohm);
+			if (delta < best_delta || !best_node) {
+				best_node = node;
+				best_delta = delta;
+				best_id_kohm = batt_ids.kohm[i];
+			}
 		}
 	}
 
@@ -262,7 +300,8 @@
 		return -ENODATA;
 	}
 
-	return of_batterydata_load_battery_data(best_node, batt_data);
+	return of_batterydata_load_battery_data(best_node,
+					best_id_kohm, batt_data);
 }
 
 MODULE_LICENSE("GPL v2");
diff --git a/drivers/platform/msm/qpnp-pwm.c b/drivers/platform/msm/qpnp-pwm.c
index 2fdc427..b77c826 100644
--- a/drivers/platform/msm/qpnp-pwm.c
+++ b/drivers/platform/msm/qpnp-pwm.c
@@ -31,6 +31,8 @@
 #define QPNP_LPG_CHANNEL_BASE	"qpnp-lpg-channel-base"
 #define QPNP_LPG_LUT_BASE	"qpnp-lpg-lut-base"
 
+#define QPNP_PWM_MODE_ONLY_SUB_TYPE	0x0B
+
 /* LPG Control for LPG_PATTERN_CONFIG */
 #define QPNP_RAMP_DIRECTION_SHIFT	4
 #define QPNP_RAMP_DIRECTION_MASK	0x10
@@ -51,10 +53,13 @@
 #define QPNP_SET_PWM_CLK_SUB_TYPE(val, clk, pwm_size) \
 do { \
 	val = (clk + 1) & QPNP_PWM_FREQ_CLK_SELECT_MASK_SUB_TYPE; \
-	val |= ((pwm_size > 6 ? QPNP_PWM_SIZE_9_BIT_SUB_TYPE : 0) << \
-		QPNP_PWM_SIZE_SHIFT_SUB_TYPE) & QPNP_PWM_SIZE_MASK_SUB_TYPE; \
+	val |= (((pwm_size > 6 ? QPNP_PWM_SIZE_9_BIT_SUB_TYPE : 0) << \
+		QPNP_PWM_SIZE_SHIFT_SUB_TYPE) & QPNP_PWM_SIZE_MASK_SUB_TYPE); \
 } while (0)
 
+#define QPNP_GET_PWM_SIZE_SUB_TYPE(reg) ((reg & QPNP_PWM_SIZE_MASK_SUB_TYPE) \
+				>> QPNP_PWM_SIZE_SHIFT_SUB_TYPE)
+
 #define QPNP_PWM_SIZE_SHIFT			4
 #define QPNP_PWM_SIZE_MASK			0x30
 #define QPNP_PWM_FREQ_CLK_SELECT_MASK		0x03
@@ -361,6 +366,9 @@
 #define QPNP_DISABLE_LPG_MODE		qpnp_set_control(0, 0, 0, 0, 1)
 #define QPNP_IS_PWM_CONFIG_SELECTED(val) (val & QPNP_PWM_SRC_SELECT_MASK)
 
+#define QPNP_ENABLE_PWM_MODE_ONLY_SUB_TYPE			0x80
+#define QPNP_DISABLE_PWM_MODE_ONLY_SUB_TYPE			0x0
+#define QPNP_PWM_MODE_ONLY_ENABLE_DISABLE_MASK_SUB_TYPE	0x80
 
 static inline void qpnp_convert_to_lut_flags(int *flags,
 				struct qpnp_lut_config *l_config)
@@ -581,14 +589,16 @@
 	struct qpnp_lpg_chip	*chip = pwm->chip;
 	struct qpnp_pwm_config	*pwm_config = &pwm->pwm_config;
 
-	if (chip->sub_type == 0x0B)
+	if (chip->sub_type == QPNP_PWM_MODE_ONLY_SUB_TYPE) {
 		QPNP_SET_PWM_CLK_SUB_TYPE(val, pwm_config->period.clk,
 				pwm_config->period.pwm_size);
-	else
+		mask = QPNP_PWM_SIZE_MASK_SUB_TYPE |
+				QPNP_PWM_FREQ_CLK_SELECT_MASK_SUB_TYPE;
+	} else {
 		QPNP_SET_PWM_CLK(val, pwm_config->period.clk,
 				pwm_config->period.pwm_size);
-
-	mask = QPNP_PWM_SIZE_MASK | QPNP_PWM_FREQ_CLK_SELECT_MASK;
+		mask = QPNP_PWM_SIZE_MASK | QPNP_PWM_FREQ_CLK_SELECT_MASK;
+	}
 
 	qpnp_lpg_save(&chip->qpnp_lpg_registers[QPNP_LPG_PWM_SIZE_CLK],
 							mask, val);
@@ -612,9 +622,14 @@
 	struct qpnp_lpg_config	*lpg_config = &chip->lpg_config;
 	int rc;
 
-	pwm_size = QPNP_GET_PWM_SIZE(
+	if (chip->sub_type == QPNP_PWM_MODE_ONLY_SUB_TYPE)
+		pwm_size = QPNP_GET_PWM_SIZE_SUB_TYPE(
+			chip->qpnp_lpg_registers[QPNP_LPG_PWM_SIZE_CLK]) ?
+				QPNP_MAX_PWM_BIT_SIZE : QPNP_MIN_PWM_BIT_SIZE;
+	else
+		pwm_size = QPNP_GET_PWM_SIZE(
 			chip->qpnp_lpg_registers[QPNP_LPG_PWM_SIZE_CLK]) +
-			QPNP_MIN_PWM_BIT_SIZE;
+				QPNP_MIN_PWM_BIT_SIZE;
 
 	max_pwm_value = (1 << pwm_size) - 1;
 
@@ -643,13 +658,14 @@
 	if (rc)
 		return rc;
 
-	if (chip->sub_type == 0x0B) {
+	if (chip->sub_type == QPNP_PWM_MODE_ONLY_SUB_TYPE) {
 		value = QPNP_PWM_SYNC_VALUE & QPNP_PWM_SYNC_MASK;
 		rc = spmi_ext_register_writel(chip->spmi_dev->ctrl,
 			chip->spmi_dev->sid,
 			SPMI_LPG_REG_ADDR(lpg_config->base_addr,
 			SPMI_LPG_PWM_SYNC), &value, 1);
 	}
+
 	return rc;
 }
 
@@ -710,6 +726,9 @@
 	struct qpnp_lpg_chip	*chip = pwm->chip;
 	u8			value, mask;
 
+	if (chip->sub_type == QPNP_PWM_MODE_ONLY_SUB_TYPE)
+		return 0;
+
 	value = QPNP_ENABLE_PWM_CONTROL;
 
 	mask = QPNP_EN_PWM_HIGH_MASK | QPNP_EN_PWM_LO_MASK |
@@ -1007,14 +1026,24 @@
 	u8			value, mask;
 	int			rc;
 
-	if (state == QPNP_PWM_ENABLE)
-		value = qpnp_enable_pwm_mode(&pwm->pwm_config);
-	else
-		value = QPNP_DISABLE_PWM_MODE;
+	if (chip->sub_type == QPNP_PWM_MODE_ONLY_SUB_TYPE) {
+		if (state == QPNP_PWM_ENABLE)
+			value = QPNP_ENABLE_PWM_MODE_ONLY_SUB_TYPE;
+		else
+			value = QPNP_DISABLE_PWM_MODE_ONLY_SUB_TYPE;
 
-	mask = QPNP_EN_PWM_HIGH_MASK | QPNP_EN_PWM_LO_MASK |
-		QPNP_EN_PWM_OUTPUT_MASK | QPNP_PWM_SRC_SELECT_MASK |
-					QPNP_PWM_EN_RAMP_GEN_MASK;
+		mask = QPNP_PWM_MODE_ONLY_ENABLE_DISABLE_MASK_SUB_TYPE;
+	} else {
+		if (state == QPNP_PWM_ENABLE)
+			value = qpnp_enable_pwm_mode(&pwm->pwm_config);
+		else
+			value = QPNP_DISABLE_PWM_MODE;
+
+		mask = QPNP_EN_PWM_HIGH_MASK | QPNP_EN_PWM_LO_MASK |
+			QPNP_EN_PWM_OUTPUT_MASK | QPNP_PWM_SRC_SELECT_MASK |
+				QPNP_PWM_EN_RAMP_GEN_MASK;
+	}
+
 
 	rc = qpnp_lpg_save_and_write(value, mask,
 		&pwm->chip->qpnp_lpg_registers[QPNP_ENABLE_CONTROL],
@@ -1164,7 +1193,8 @@
 	spin_lock_irqsave(&pwm->chip->lpg_lock, flags);
 
 	if (QPNP_IS_PWM_CONFIG_SELECTED(
-		chip->qpnp_lpg_registers[QPNP_ENABLE_CONTROL])) {
+		chip->qpnp_lpg_registers[QPNP_ENABLE_CONTROL]) ||
+			chip->flags & QPNP_PWM_LUT_NOT_SUPPORTED) {
 		rc = qpnp_lpg_configure_pwm_state(pwm, QPNP_PWM_ENABLE);
 	} else if (!(chip->flags & QPNP_PWM_LUT_NOT_SUPPORTED)) {
 			rc = qpnp_lpg_configure_lut_state(pwm, QPNP_LUT_ENABLE);
@@ -1329,7 +1359,8 @@
 
 	if (pwm_config->in_use) {
 		if (QPNP_IS_PWM_CONFIG_SELECTED(
-			chip->qpnp_lpg_registers[QPNP_ENABLE_CONTROL])) {
+			chip->qpnp_lpg_registers[QPNP_ENABLE_CONTROL]) ||
+				chip->flags & QPNP_PWM_LUT_NOT_SUPPORTED) {
 			rc = qpnp_lpg_configure_pwm_state(pwm,
 						QPNP_PWM_DISABLE);
 		} else if (!(chip->flags & QPNP_PWM_LUT_NOT_SUPPORTED)) {
diff --git a/drivers/power/qpnp-bms.c b/drivers/power/qpnp-bms.c
index a87ade3..639d88c 100644
--- a/drivers/power/qpnp-bms.c
+++ b/drivers/power/qpnp-bms.c
@@ -23,6 +23,7 @@
 #include <linux/spmi.h>
 #include <linux/rtc.h>
 #include <linux/delay.h>
+#include <linux/sched.h>
 #include <linux/qpnp/qpnp-adc.h>
 #include <linux/qpnp/power-on.h>
 #include <linux/of_batterydata.h>
@@ -143,6 +144,7 @@
 	bool				bms_psy_registered;
 	struct power_supply		*batt_psy;
 	struct spmi_device		*spmi;
+	wait_queue_head_t		bms_wait_queue;
 	u16				base;
 	u16				iadc_base;
 
@@ -1682,6 +1684,7 @@
 #define SOC_CATCHUP_SEC_PER_PERCENT	60
 #define MAX_CATCHUP_SOC	(SOC_CATCHUP_SEC_MAX / SOC_CATCHUP_SEC_PER_PERCENT)
 #define SOC_CHANGE_PER_SEC		5
+#define REPORT_SOC_WAIT_MS		10000
 static int report_cc_based_soc(struct qpnp_bms_chip *chip)
 {
 	int soc, soc_change;
@@ -1693,6 +1696,18 @@
 	int rc;
 	bool charging, charging_since_last_report;
 
+	rc = wait_event_interruptible_timeout(chip->bms_wait_queue,
+			chip->calculated_soc != -EINVAL,
+			round_jiffies_relative(msecs_to_jiffies
+			(REPORT_SOC_WAIT_MS)));
+
+	if (rc == 0 && chip->calculated_soc == -EINVAL) {
+		pr_debug("calculate soc timed out\n");
+	} else if (rc == -ERESTARTSYS) {
+		pr_err("Wait for SoC interrupted.\n");
+		return rc;
+	}
+
 	rc = qpnp_vadc_read(chip->vadc_dev, LR_MUX1_BATT_THERM, &result);
 
 	if (rc) {
@@ -2300,6 +2315,7 @@
 				params.delta_time_s);
 	}
 	mutex_unlock(&chip->last_soc_mutex);
+	wake_up_interruptible(&chip->bms_wait_queue);
 
 	if (new_calculated_soc != previous_soc && chip->bms_psy_registered) {
 		power_supply_changed(&chip->bms_psy);
@@ -3048,6 +3064,8 @@
 	int status = get_battery_status(chip);
 
 	if (chip->battery_status != status) {
+		pr_debug("status = %d, shadow status = %d\n",
+				status, chip->battery_status);
 		if (status == POWER_SUPPLY_STATUS_CHARGING) {
 			pr_debug("charging started\n");
 			charging_began(chip);
@@ -3843,6 +3861,7 @@
 	mutex_init(&chip->vbat_monitor_mutex);
 	mutex_init(&chip->soc_invalidation_mutex);
 	mutex_init(&chip->last_soc_mutex);
+	init_waitqueue_head(&chip->bms_wait_queue);
 
 	warm_reset = qpnp_pon_is_warm_reset();
 	rc = warm_reset;
diff --git a/drivers/power/qpnp-charger.c b/drivers/power/qpnp-charger.c
index 2ad0926..1a79ad5 100644
--- a/drivers/power/qpnp-charger.c
+++ b/drivers/power/qpnp-charger.c
@@ -86,6 +86,7 @@
 #define CHGR_STATUS				0x09
 #define CHGR_BAT_IF_VCP				0x42
 #define CHGR_BAT_IF_BATFET_CTRL1		0x90
+#define CHGR_BAT_IF_SPARE			0xDF
 #define CHGR_MISC_BOOT_DONE			0x42
 #define CHGR_BUCK_PSTG_CTRL			0x73
 #define CHGR_BUCK_COMPARATOR_OVRIDE_1		0xEB
@@ -96,6 +97,7 @@
 #define USB_CHG_GONE_REV_BST			0xED
 #define BUCK_VCHG_OV				0x77
 #define BUCK_TEST_SMBC_MODES			0xE6
+#define BUCK_CTRL_TRIM1				0xF1
 #define SEC_ACCESS				0xD0
 #define BAT_IF_VREF_BAT_THM_CTRL		0x4A
 #define BAT_IF_BPD_CTRL				0x48
@@ -208,7 +210,7 @@
 #define POWER_STAGE_WA			BIT(2)
 
 struct qpnp_chg_irq {
-	unsigned int		irq;
+	int		irq;
 	unsigned long		disabled;
 };
 
@@ -287,7 +289,6 @@
 	struct qpnp_chg_irq		chg_failed;
 	struct qpnp_chg_irq		chg_vbatdet_lo;
 	struct qpnp_chg_irq		batt_pres;
-	struct qpnp_chg_irq		vchg_loop;
 	struct qpnp_chg_irq		batt_temp_ok;
 	bool				bat_is_cool;
 	bool				bat_is_warm;
@@ -309,6 +310,7 @@
 	int				prev_usb_max_ma;
 	int				set_vddmax_mv;
 	int				delta_vddmax_mv;
+	u8				trim_center;
 	unsigned int			warm_bat_mv;
 	unsigned int			cool_bat_mv;
 	unsigned int			resume_delta_mv;
@@ -338,9 +340,10 @@
 	struct work_struct		adc_disable_work;
 	struct delayed_work		arb_stop_work;
 	struct delayed_work		eoc_work;
-	struct wake_lock		eoc_wake_lock;
+	struct work_struct		soc_check_work;
 	struct qpnp_chg_regulator	otg_vreg;
 	struct qpnp_chg_regulator	boost_vreg;
+	struct qpnp_chg_regulator	batfet_vreg;
 	struct qpnp_vadc_chip		*vadc_dev;
 	struct qpnp_adc_tm_chip		*adc_tm_dev;
 	struct mutex			jeita_configure_lock;
@@ -968,17 +971,6 @@
 	qpnp_adc_tm_disable_chan_meas(chip->adc_tm_dev, &chip->adc_param);
 }
 
-static irqreturn_t
-qpnp_chg_buck_vchg_loop_irq_handler(int irq, void *_chip)
-{
-	struct qpnp_chg_chip *chip = _chip;
-
-	if (chip->bat_if_base)
-		power_supply_changed(&chip->batt_psy);
-
-	return IRQ_HANDLED;
-}
-
 #define EOC_CHECK_PERIOD_MS	10000
 static irqreturn_t
 qpnp_chg_vbatdet_lo_irq_handler(int irq, void *_chip)
@@ -997,17 +989,20 @@
 	if (!chip->charging_disabled && (chg_sts & FAST_CHG_ON_IRQ)) {
 		schedule_delayed_work(&chip->eoc_work,
 			msecs_to_jiffies(EOC_CHECK_PERIOD_MS));
-		wake_lock(&chip->eoc_wake_lock);
-		qpnp_chg_disable_irq(&chip->chg_vbatdet_lo);
-	} else {
-		qpnp_chg_charge_en(chip, !chip->charging_disabled);
+		pm_stay_awake(chip->dev);
 	}
+	qpnp_chg_disable_irq(&chip->chg_vbatdet_lo);
 
+	pr_debug("psy changed usb_psy\n");
 	power_supply_changed(chip->usb_psy);
-	if (chip->dc_chgpth_base)
+	if (chip->dc_chgpth_base) {
+		pr_debug("psy changed dc_psy\n");
 		power_supply_changed(&chip->dc_psy);
-	if (chip->bat_if_base)
+	}
+	if (chip->bat_if_base) {
+		pr_debug("psy changed batt_psy\n");
 		power_supply_changed(&chip->batt_psy);
+	}
 	return IRQ_HANDLED;
 }
 
@@ -1054,6 +1049,72 @@
 	return IRQ_HANDLED;
 }
 
+#define QPNP_CHG_VDDMAX_MIN		3400
+#define QPNP_CHG_V_MIN_MV		3240
+#define QPNP_CHG_V_MAX_MV		4500
+#define QPNP_CHG_V_STEP_MV		10
+#define QPNP_CHG_BUCK_TRIM1_STEP	10
+#define QPNP_CHG_BUCK_VDD_TRIM_MASK	0xF0
+static int
+qpnp_chg_vddmax_and_trim_set(struct qpnp_chg_chip *chip,
+		int voltage, int trim_mv)
+{
+	int rc, trim_set;
+	u8 vddmax = 0, trim = 0;
+
+	if (voltage < QPNP_CHG_VDDMAX_MIN
+			|| voltage > QPNP_CHG_V_MAX_MV) {
+		pr_err("bad mV=%d asked to set\n", voltage);
+		return -EINVAL;
+	}
+
+	vddmax = (voltage - QPNP_CHG_V_MIN_MV) / QPNP_CHG_V_STEP_MV;
+	rc = qpnp_chg_write(chip, &vddmax, chip->chgr_base + CHGR_VDD_MAX, 1);
+	if (rc) {
+		pr_err("Failed to write vddmax: %d\n", rc);
+		return rc;
+	}
+
+	rc = qpnp_chg_masked_write(chip,
+		chip->buck_base + SEC_ACCESS,
+		0xFF,
+		0xA5, 1);
+	if (rc) {
+		pr_err("failed to write SEC_ACCESS rc=%d\n", rc);
+		return rc;
+	}
+	trim_set = clamp((int)chip->trim_center
+			+ (trim_mv / QPNP_CHG_BUCK_TRIM1_STEP),
+			0, 0xF);
+	trim = (u8)trim_set << 4;
+	rc = qpnp_chg_masked_write(chip,
+		chip->buck_base + BUCK_CTRL_TRIM1,
+		QPNP_CHG_BUCK_VDD_TRIM_MASK,
+		trim, 1);
+	if (rc) {
+		pr_err("Failed to write buck trim1: %d\n", rc);
+		return rc;
+	}
+	pr_debug("voltage=%d+%d setting vddmax: %02x, trim: %02x\n",
+			voltage, trim_mv, vddmax, trim);
+	return 0;
+}
+
+/* JEITA compliance logic */
+static void
+qpnp_chg_set_appropriate_vddmax(struct qpnp_chg_chip *chip)
+{
+	if (chip->bat_is_cool)
+		qpnp_chg_vddmax_and_trim_set(chip, chip->cool_bat_mv,
+				chip->delta_vddmax_mv);
+	else if (chip->bat_is_warm)
+		qpnp_chg_vddmax_and_trim_set(chip, chip->warm_bat_mv,
+				chip->delta_vddmax_mv);
+	else
+		qpnp_chg_vddmax_and_trim_set(chip, chip->max_voltage_mv,
+				chip->delta_vddmax_mv);
+}
+
 #define ENUM_T_STOP_BIT		BIT(0)
 static irqreturn_t
 qpnp_chg_usb_usbin_valid_irq_handler(int irq, void *_chip)
@@ -1073,12 +1134,22 @@
 	if (chip->usb_present ^ usb_present) {
 		chip->usb_present = usb_present;
 		if (!usb_present) {
+			if (!qpnp_chg_is_dc_chg_plugged_in(chip)) {
+				chip->delta_vddmax_mv = 0;
+				qpnp_chg_set_appropriate_vddmax(chip);
+			}
 			qpnp_chg_usb_suspend_enable(chip, 1);
-			chip->chg_done = false;
+			if (!qpnp_chg_is_dc_chg_plugged_in(chip))
+				chip->chg_done = false;
 			chip->prev_usb_max_ma = -EINVAL;
 		} else {
+			if (!qpnp_chg_is_dc_chg_plugged_in(chip)) {
+				chip->delta_vddmax_mv = 0;
+				qpnp_chg_set_appropriate_vddmax(chip);
+			}
 			schedule_delayed_work(&chip->eoc_work,
 				msecs_to_jiffies(EOC_CHECK_PERIOD_MS));
+			schedule_work(&chip->soc_check_work);
 		}
 
 		power_supply_set_present(chip->usb_psy, chip->usb_present);
@@ -1096,6 +1167,7 @@
 	batt_temp_good = qpnp_chg_is_batt_temp_ok(chip);
 	pr_debug("batt-temp triggered: %d\n", batt_temp_good);
 
+	pr_debug("psy changed batt_psy\n");
 	power_supply_changed(&chip->batt_psy);
 	return IRQ_HANDLED;
 }
@@ -1111,14 +1183,16 @@
 
 	if (chip->batt_present ^ batt_present) {
 		chip->batt_present = batt_present;
+		pr_debug("psy changed batt_psy\n");
 		power_supply_changed(&chip->batt_psy);
+		pr_debug("psy changed usb_psy\n");
 		power_supply_changed(chip->usb_psy);
 
-		if (chip->cool_bat_decidegc && chip->warm_bat_decidegc
+		if ((chip->cool_bat_decidegc || chip->warm_bat_decidegc)
 						&& batt_present) {
 			pr_debug("enabling vadc notifications\n");
 			schedule_work(&chip->adc_measure_work);
-		} else if (chip->cool_bat_decidegc && chip->warm_bat_decidegc
+		} else if ((chip->cool_bat_decidegc || chip->warm_bat_decidegc)
 				&& !batt_present) {
 			schedule_work(&chip->adc_disable_work);
 			pr_debug("disabling vadc notifications\n");
@@ -1139,12 +1213,22 @@
 
 	if (chip->dc_present ^ dc_present) {
 		chip->dc_present = dc_present;
-		if (!dc_present)
+		if (!dc_present && !qpnp_chg_is_usb_chg_plugged_in(chip)) {
+			chip->delta_vddmax_mv = 0;
+			qpnp_chg_set_appropriate_vddmax(chip);
 			chip->chg_done = false;
-		else
+		} else {
+			if (!qpnp_chg_is_usb_chg_plugged_in(chip)) {
+				chip->delta_vddmax_mv = 0;
+				qpnp_chg_set_appropriate_vddmax(chip);
+			}
 			schedule_delayed_work(&chip->eoc_work,
 				msecs_to_jiffies(EOC_CHECK_PERIOD_MS));
+			schedule_work(&chip->soc_check_work);
+		}
+		pr_debug("psy changed dc_psy\n");
 		power_supply_changed(&chip->dc_psy);
+		pr_debug("psy changed batt_psy\n");
 		power_supply_changed(&chip->batt_psy);
 	}
 
@@ -1167,11 +1251,16 @@
 	if (rc)
 		pr_err("Failed to write chg_fail clear bit!\n");
 
-	if (chip->bat_if_base)
+	if (chip->bat_if_base) {
+		pr_debug("psy changed batt_psy\n");
 		power_supply_changed(&chip->batt_psy);
+	}
+	pr_debug("psy changed usb_psy\n");
 	power_supply_changed(chip->usb_psy);
-	if (chip->dc_chgpth_base)
+	if (chip->dc_chgpth_base) {
+		pr_debug("psy changed dc_psy\n");
 		power_supply_changed(&chip->dc_psy);
+	}
 	return IRQ_HANDLED;
 }
 
@@ -1183,8 +1272,10 @@
 	pr_debug("TRKL IRQ triggered\n");
 
 	chip->chg_done = false;
-	if (chip->bat_if_base)
+	if (chip->bat_if_base) {
+		pr_debug("psy changed batt_psy\n");
 		power_supply_changed(&chip->batt_psy);
+	}
 
 	return IRQ_HANDLED;
 }
@@ -1202,21 +1293,31 @@
 
 	pr_debug("FAST_CHG IRQ triggered\n");
 	chip->chg_done = false;
-	if (chip->bat_if_base)
+	if (chip->bat_if_base) {
+		pr_debug("psy changed batt_psy\n");
 		power_supply_changed(&chip->batt_psy);
+	}
+
+	pr_debug("psy changed usb_psy\n");
 	power_supply_changed(chip->usb_psy);
-	if (chip->dc_chgpth_base)
+
+	if (chip->dc_chgpth_base) {
+		pr_debug("psy changed dc_psy\n");
 		power_supply_changed(&chip->dc_psy);
+	}
+
 	if (chip->resuming_charging) {
 		chip->resuming_charging = false;
 		qpnp_chg_set_appropriate_vbatdet(chip);
 	}
 
+	if (!chip->charging_disabled) {
+		schedule_delayed_work(&chip->eoc_work,
+			msecs_to_jiffies(EOC_CHECK_PERIOD_MS));
+		pm_stay_awake(chip->dev);
+	}
+
 	qpnp_chg_enable_irq(&chip->chg_vbatdet_lo);
-	if (chgr_sts & FAST_CHG_ON_IRQ)
-		qpnp_chg_enable_irq(&chip->vchg_loop);
-	else
-		qpnp_chg_disable_irq(&chip->vchg_loop);
 
 	return IRQ_HANDLED;
 }
@@ -1568,19 +1669,26 @@
 get_prop_capacity(struct qpnp_chg_chip *chip)
 {
 	union power_supply_propval ret = {0,};
+	int battery_status, charger_in;
 
 	if (chip->use_default_batt_values || !get_prop_batt_present(chip))
 		return DEFAULT_CAPACITY;
 
 	if (chip->bms_psy) {
 		chip->bms_psy->get_property(chip->bms_psy,
-			  POWER_SUPPLY_PROP_CAPACITY, &ret);
-		if (get_prop_batt_status(chip) == POWER_SUPPLY_STATUS_FULL
+				POWER_SUPPLY_PROP_CAPACITY, &ret);
+		battery_status = get_prop_batt_status(chip);
+		charger_in = qpnp_chg_is_usb_chg_plugged_in(chip) ||
+			qpnp_chg_is_dc_chg_plugged_in(chip);
+
+		if (battery_status != POWER_SUPPLY_STATUS_CHARGING
+				&& charger_in
 				&& !chip->resuming_charging
 				&& !chip->charging_disabled
 				&& chip->soc_resume_limit
 				&& ret.intval <= chip->soc_resume_limit) {
-			pr_debug("resuming charging at %d%% soc\n", ret.intval);
+			pr_debug("resuming charging at %d%% soc\n",
+					ret.intval);
 			chip->resuming_charging = true;
 			qpnp_chg_set_appropriate_vbatdet(chip);
 			qpnp_chg_charge_en(chip, !chip->charging_disabled);
@@ -1703,6 +1811,7 @@
 
 skip_set_iusb_max:
 	pr_debug("end of power supply changed\n");
+	pr_debug("psy changed batt_psy\n");
 	power_supply_changed(&chip->batt_psy);
 }
 
@@ -1899,9 +2008,6 @@
 			QPNP_CHG_TCHG_MASK, temp, 1);
 }
 
-#define QPNP_CHG_V_MIN_MV	3240
-#define QPNP_CHG_V_MAX_MV	4500
-#define QPNP_CHG_V_STEP_MV	10
 static int
 qpnp_chg_vddsafe_set(struct qpnp_chg_chip *chip, int voltage)
 {
@@ -1918,25 +2024,6 @@
 		chip->chgr_base + CHGR_VDD_SAFE, 1);
 }
 
-#define QPNP_CHG_VDDMAX_MIN	3400
-static int
-qpnp_chg_vddmax_set(struct qpnp_chg_chip *chip, int voltage)
-{
-	u8 temp = 0;
-
-	if (voltage < QPNP_CHG_VDDMAX_MIN
-			|| voltage > QPNP_CHG_V_MAX_MV) {
-		pr_err("bad mV=%d asked to set\n", voltage);
-		return -EINVAL;
-	}
-	chip->set_vddmax_mv = voltage + chip->delta_vddmax_mv;
-
-	temp = (chip->set_vddmax_mv - QPNP_CHG_V_MIN_MV) / QPNP_CHG_V_STEP_MV;
-
-	pr_debug("voltage=%d setting %02x\n", chip->set_vddmax_mv, temp);
-	return qpnp_chg_write(chip, &temp, chip->chgr_base + CHGR_VDD_MAX, 1);
-}
-
 #define BOOST_MIN_UV	4200000
 #define BOOST_MAX_UV	5500000
 #define BOOST_STEP_UV	50000
@@ -1979,18 +2066,6 @@
 	return BOOST_MIN_UV + ((boost_reg - BOOST_MIN) * BOOST_STEP_UV);
 }
 
-/* JEITA compliance logic */
-static void
-qpnp_chg_set_appropriate_vddmax(struct qpnp_chg_chip *chip)
-{
-	if (chip->bat_is_cool)
-		qpnp_chg_vddmax_set(chip, chip->cool_bat_mv);
-	else if (chip->bat_is_warm)
-		qpnp_chg_vddmax_set(chip, chip->warm_bat_mv);
-	else
-		qpnp_chg_vddmax_set(chip, chip->max_voltage_mv);
-}
-
 static void
 qpnp_chg_set_appropriate_battery_current(struct qpnp_chg_chip *chip)
 {
@@ -2249,22 +2324,80 @@
 	.list_voltage		= qpnp_chg_regulator_boost_list_voltage,
 };
 
-#define MIN_DELTA_MV_TO_INCREASE_VDD_MAX	13
-#define MAX_DELTA_VDD_MAX_MV			30
+#define BATFET_LPM_MASK		0xC0
+#define BATFET_LPM		0x40
+#define BATFET_NO_LPM		0x00
+static int
+qpnp_chg_regulator_batfet_enable(struct regulator_dev *rdev)
+{
+	struct qpnp_chg_chip *chip = rdev_get_drvdata(rdev);
+	int rc;
+
+	rc = qpnp_chg_masked_write(chip,
+			chip->bat_if_base + CHGR_BAT_IF_SPARE,
+			BATFET_LPM_MASK, BATFET_NO_LPM, 1);
+	if (rc)
+		pr_err("failed to write to batt_if rc=%d\n", rc);
+	return rc;
+}
+
+static int
+qpnp_chg_regulator_batfet_disable(struct regulator_dev *rdev)
+{
+	struct qpnp_chg_chip *chip = rdev_get_drvdata(rdev);
+	int rc;
+
+	rc = qpnp_chg_masked_write(chip,
+			chip->bat_if_base + CHGR_BAT_IF_SPARE,
+			BATFET_LPM_MASK, BATFET_LPM, 1);
+	if (rc)
+		pr_err("failed to write to batt_if rc=%d\n", rc);
+	return rc;
+}
+
+static int
+qpnp_chg_regulator_batfet_is_enabled(struct regulator_dev *rdev)
+{
+	struct qpnp_chg_chip *chip = rdev_get_drvdata(rdev);
+	int rc;
+	u8 reg;
+
+	rc = qpnp_chg_read(chip, &reg,
+				chip->bat_if_base + CHGR_BAT_IF_SPARE, 1);
+	if (rc) {
+		pr_err("failed to read batt_if rc=%d\n", rc);
+		return rc;
+	}
+
+	if (reg && BATFET_LPM_MASK == BATFET_NO_LPM)
+		return 1;
+
+	return 0;
+}
+
+static struct regulator_ops qpnp_chg_batfet_vreg_ops = {
+	.enable			= qpnp_chg_regulator_batfet_enable,
+	.disable		= qpnp_chg_regulator_batfet_disable,
+	.is_enabled		= qpnp_chg_regulator_batfet_is_enabled,
+};
+
+#define MIN_DELTA_MV_TO_INCREASE_VDD_MAX	8
+#define MAX_DELTA_VDD_MAX_MV			80
+#define VDD_MAX_CENTER_OFFSET			4
 static void
 qpnp_chg_adjust_vddmax(struct qpnp_chg_chip *chip, int vbat_mv)
 {
 	int delta_mv, closest_delta_mv, sign;
 
-	delta_mv = chip->max_voltage_mv - vbat_mv;
+	delta_mv = chip->max_voltage_mv - VDD_MAX_CENTER_OFFSET - vbat_mv;
 	if (delta_mv > 0 && delta_mv < MIN_DELTA_MV_TO_INCREASE_VDD_MAX) {
 		pr_debug("vbat is not low enough to increase vdd\n");
 		return;
 	}
 
 	sign = delta_mv > 0 ? 1 : -1;
-	closest_delta_mv = ((delta_mv + sign * QPNP_CHG_V_STEP_MV / 2)
-			/ QPNP_CHG_V_STEP_MV) * QPNP_CHG_V_STEP_MV;
+	closest_delta_mv = ((delta_mv + sign * QPNP_CHG_BUCK_TRIM1_STEP / 2)
+			/ QPNP_CHG_BUCK_TRIM1_STEP) * QPNP_CHG_BUCK_TRIM1_STEP;
 	pr_debug("max_voltage = %d, vbat_mv = %d, delta_mv = %d, closest = %d\n",
 			chip->max_voltage_mv, vbat_mv,
 			delta_mv, closest_delta_mv);
@@ -2275,6 +2408,7 @@
 }
 
 #define CONSECUTIVE_COUNT	3
+#define VBATDET_MAX_ERR_MV	50
 static void
 qpnp_eoc_work(struct work_struct *work)
 {
@@ -2282,10 +2416,12 @@
 	struct qpnp_chg_chip *chip = container_of(dwork,
 				struct qpnp_chg_chip, eoc_work);
 	static int count;
+	static int vbat_low_count;
 	int ibat_ma, vbat_mv, rc = 0;
 	u8 batt_sts = 0, buck_sts = 0, chg_sts = 0;
+	bool vbat_lower_than_vbatdet;
 
-	wake_lock(&chip->eoc_wake_lock);
+	pm_stay_awake(chip->dev);
 	qpnp_chg_charge_en(chip, !chip->charging_disabled);
 
 	rc = qpnp_chg_read(chip, &batt_sts, INT_RT_STS(chip->bat_if_base), 1);
@@ -2323,11 +2459,24 @@
 		pr_debug("ibat_ma = %d vbat_mv = %d term_current_ma = %d\n",
 				ibat_ma, vbat_mv, chip->term_current);
 
-		if ((!(chg_sts & VBAT_DET_LOW_IRQ)) && (vbat_mv <
-			(chip->max_voltage_mv - chip->resume_delta_mv))) {
-			pr_debug("woke up too early\n");
-			qpnp_chg_enable_irq(&chip->chg_vbatdet_lo);
-			goto stop_eoc;
+		vbat_lower_than_vbatdet = !(chg_sts & VBAT_DET_LOW_IRQ);
+		if (vbat_lower_than_vbatdet && vbat_mv <
+				(chip->max_voltage_mv - chip->resume_delta_mv
+				 - VBATDET_MAX_ERR_MV)) {
+			vbat_low_count++;
+			pr_debug("woke up too early vbat_mv = %d, max_mv = %d, resume_mv = %d tolerance_mv = %d low_count = %d\n",
+					vbat_mv, chip->max_voltage_mv,
+					chip->resume_delta_mv,
+					VBATDET_MAX_ERR_MV, vbat_low_count);
+			if (vbat_low_count >= CONSECUTIVE_COUNT) {
+				pr_debug("woke up too early stopping\n");
+				qpnp_chg_enable_irq(&chip->chg_vbatdet_lo);
+				goto stop_eoc;
+			} else {
+				goto check_again_later;
+			}
+		} else {
+			vbat_low_count = 0;
 		}
 
 		if (buck_sts & VDD_LOOP_IRQ)
@@ -2345,8 +2494,15 @@
 		} else {
 			if (count == CONSECUTIVE_COUNT) {
 				pr_info("End of Charging\n");
-				qpnp_chg_charge_en(chip, 0);
+				chip->delta_vddmax_mv = 0;
+				qpnp_chg_set_appropriate_vddmax(chip);
 				chip->chg_done = true;
+				qpnp_chg_charge_en(chip, 0);
+				/* sleep for a second before enabling */
+				msleep(2000);
+				qpnp_chg_charge_en(chip,
+						!chip->charging_disabled);
+				pr_debug("psy changed batt_psy\n");
 				power_supply_changed(&chip->batt_psy);
 				qpnp_chg_enable_irq(&chip->chg_vbatdet_lo);
 				goto stop_eoc;
@@ -2357,16 +2513,27 @@
 		}
 	} else {
 		pr_debug("not charging\n");
-			goto stop_eoc;
+		goto stop_eoc;
 	}
 
+check_again_later:
 	schedule_delayed_work(&chip->eoc_work,
 		msecs_to_jiffies(EOC_CHECK_PERIOD_MS));
 	return;
 
 stop_eoc:
+	vbat_low_count = 0;
 	count = 0;
-	wake_unlock(&chip->eoc_wake_lock);
+	pm_relax(chip->dev);
+}
+
+static void
+qpnp_chg_soc_check_work(struct work_struct *work)
+{
+	struct qpnp_chg_chip *chip = container_of(work,
+				struct qpnp_chg_chip, soc_check_work);
+
+	get_prop_capacity(chip);
 }
 
 #define HYSTERISIS_DECIDEGC 20
@@ -2737,6 +2904,7 @@
 		return -EINVAL;
 	}
 
+	pr_debug("psy changed dc_psy\n");
 	power_supply_changed(&chip->dc_psy);
 	return rc;
 }
@@ -2759,8 +2927,17 @@
 		break;
 	case POWER_SUPPLY_PROP_CHARGING_ENABLED:
 		chip->charging_disabled = !(val->intval);
-		qpnp_chg_charge_en(chip, !chip->charging_disabled);
-		qpnp_chg_force_run_on_batt(chip, chip->charging_disabled);
+		if (chip->charging_disabled) {
+			/* disable charging */
+			qpnp_chg_charge_en(chip, !chip->charging_disabled);
+			qpnp_chg_force_run_on_batt(chip,
+						chip->charging_disabled);
+		} else {
+			/* enable charging */
+			qpnp_chg_force_run_on_batt(chip,
+					chip->charging_disabled);
+			qpnp_chg_charge_en(chip, !chip->charging_disabled);
+		}
 		break;
 	case POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL:
 		qpnp_batt_system_temp_level_set(chip, val->intval);
@@ -2775,6 +2952,7 @@
 		return -EINVAL;
 	}
 
+	pr_debug("psy changed batt_psy\n");
 	power_supply_changed(&chip->batt_psy);
 	return rc;
 }
@@ -2908,7 +3086,7 @@
 			rc |= devm_request_irq(chip->dev,
 				chip->chg_vbatdet_lo.irq,
 				qpnp_chg_vbatdet_lo_irq_handler,
-				IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+				IRQF_TRIGGER_RISING,
 				"vbat-det-lo", chip);
 			if (rc < 0) {
 				pr_err("Can't request %d vbat-det-lo: %d\n",
@@ -2966,24 +3144,6 @@
 		case SMBB_BUCK_SUBTYPE:
 		case SMBBP_BUCK_SUBTYPE:
 		case SMBCL_BUCK_SUBTYPE:
-			chip->vchg_loop.irq = spmi_get_irq_byname(spmi,
-						spmi_resource, "vchg-loop");
-			if (chip->vchg_loop.irq < 0) {
-				pr_err("Unable to get vchg-loop irq\n");
-				return rc;
-			}
-			rc = devm_request_irq(chip->dev, chip->vchg_loop.irq,
-				qpnp_chg_buck_vchg_loop_irq_handler,
-				IRQF_TRIGGER_RISING,
-				"vchg-loop", chip);
-			if (rc < 0) {
-				pr_err("Can't request %d vchg-loop irq: %d\n",
-						chip->vchg_loop.irq, rc);
-				return rc;
-			}
-
-			enable_irq_wake(chip->vchg_loop.irq);
-			qpnp_chg_disable_irq(&chip->vchg_loop);
 			break;
 
 		case SMBB_USB_CHGPTH_SUBTYPE:
@@ -3129,11 +3289,6 @@
 			pr_debug("failed setting  min_voltage rc=%d\n", rc);
 			return rc;
 		}
-		rc = qpnp_chg_vddmax_set(chip, chip->max_voltage_mv);
-		if (rc) {
-			pr_debug("failed setting max_voltage rc=%d\n", rc);
-			return rc;
-		}
 		rc = qpnp_chg_vddsafe_set(chip, chip->safe_voltage_mv);
 		if (rc) {
 			pr_debug("failed setting safe_voltage rc=%d\n", rc);
@@ -3193,6 +3348,15 @@
 			pr_debug("failed to enable IR drop comp rc=%d\n", rc);
 			return rc;
 		}
+
+		rc = qpnp_chg_read(chip, &chip->trim_center,
+				chip->buck_base + BUCK_CTRL_TRIM1, 1);
+		if (rc) {
+			pr_debug("failed to read trim center rc=%d\n", rc);
+			return rc;
+		}
+		chip->trim_center >>= 4;
+		pr_debug("trim center = %02x\n", chip->trim_center);
 		break;
 	case SMBB_BAT_IF_SUBTYPE:
 	case SMBBP_BAT_IF_SUBTYPE:
@@ -3230,6 +3394,32 @@
 			pr_debug("failed to force on VREF_BAT_THM rc=%d\n", rc);
 			return rc;
 		}
+
+		init_data = of_get_regulator_init_data(chip->dev,
+					       spmi_resource->of_node);
+
+		if (init_data->constraints.name) {
+			rdesc			= &(chip->batfet_vreg.rdesc);
+			rdesc->owner		= THIS_MODULE;
+			rdesc->type		= REGULATOR_VOLTAGE;
+			rdesc->ops		= &qpnp_chg_batfet_vreg_ops;
+			rdesc->name		= init_data->constraints.name;
+
+			init_data->constraints.valid_ops_mask
+				|= REGULATOR_CHANGE_STATUS;
+
+			chip->batfet_vreg.rdev = regulator_register(rdesc,
+					chip->dev, init_data, chip,
+					spmi_resource->of_node);
+			if (IS_ERR(chip->batfet_vreg.rdev)) {
+				rc = PTR_ERR(chip->batfet_vreg.rdev);
+				chip->batfet_vreg.rdev = NULL;
+				if (rc != -EPROBE_DEFER)
+					pr_err("batfet reg failed, rc=%d\n",
+							rc);
+				return rc;
+			}
+		}
 		break;
 	case SMBB_USB_CHGPTH_SUBTYPE:
 	case SMBBP_USB_CHGPTH_SUBTYPE:
@@ -3435,7 +3625,7 @@
 	}
 
 	/* Look up JEITA compliance parameters if cool and warm temp provided */
-	if (chip->cool_bat_decidegc && chip->warm_bat_decidegc) {
+	if (chip->cool_bat_decidegc || chip->warm_bat_decidegc) {
 		chip->adc_tm_dev = qpnp_get_adc_tm(chip->dev, "chg");
 		if (IS_ERR(chip->adc_tm_dev)) {
 			rc = PTR_ERR(chip->adc_tm_dev);
@@ -3573,11 +3763,11 @@
 				if (rc != -EPROBE_DEFER)
 					pr_err("vadc property missing\n");
 				goto fail_chg_enable;
+			}
 
 			rc = qpnp_chg_load_battery_data(chip);
 			if (rc)
 				goto fail_chg_enable;
-			}
 		}
 	}
 
@@ -3637,16 +3827,6 @@
 				0xff,
 				0x00, 1);
 
-			rc = qpnp_chg_masked_write(chip,
-				chip->buck_base + SEC_ACCESS,
-				0xFF,
-				0xA5, 1);
-
-			rc = qpnp_chg_masked_write(chip,
-				chip->buck_base + BUCK_TEST_SMBC_MODES,
-				0xFF,
-				0x80, 1);
-
 			if (chip->duty_cycle_100p) {
 				rc = qpnp_buck_set_100_duty_cycle_enable(chip,
 						1);
@@ -3748,10 +3928,9 @@
 			qpnp_bat_if_adc_disable_work);
 	}
 
-	wake_lock_init(&chip->eoc_wake_lock,
-		WAKE_LOCK_SUSPEND, "qpnp-chg-eoc-lock");
 	INIT_DELAYED_WORK(&chip->eoc_work, qpnp_eoc_work);
 	INIT_DELAYED_WORK(&chip->arb_stop_work, qpnp_arb_stop_work);
+	INIT_WORK(&chip->soc_check_work, qpnp_chg_soc_check_work);
 
 	if (chip->dc_chgpth_base) {
 		chip->dc_psy.name = "qpnp-dc";
@@ -3787,7 +3966,7 @@
 		}
 	}
 
-	if (chip->cool_bat_decidegc && chip->warm_bat_decidegc
+	if ((chip->cool_bat_decidegc || chip->warm_bat_decidegc)
 							&& chip->bat_if_base) {
 		chip->adc_param.low_temp = chip->cool_bat_decidegc;
 		chip->adc_param.high_temp = chip->warm_bat_decidegc;
@@ -3823,8 +4002,8 @@
 		goto unregister_dc_psy;
 	}
 
-	qpnp_chg_usb_usbin_valid_irq_handler(USBIN_VALID_IRQ, chip);
-	qpnp_chg_dc_dcin_valid_irq_handler(DCIN_VALID_IRQ, chip);
+	qpnp_chg_usb_usbin_valid_irq_handler(chip->usbin_valid.irq, chip);
+	qpnp_chg_dc_dcin_valid_irq_handler(chip->dcin_valid.irq, chip);
 	power_supply_set_present(chip->usb_psy,
 			qpnp_chg_is_usb_chg_plugged_in(chip));
 
@@ -3861,7 +4040,7 @@
 qpnp_charger_remove(struct spmi_device *spmi)
 {
 	struct qpnp_chg_chip *chip = dev_get_drvdata(&spmi->dev);
-	if (chip->cool_bat_decidegc && chip->warm_bat_decidegc
+	if ((chip->cool_bat_decidegc || chip->warm_bat_decidegc)
 						&& chip->batt_present) {
 		qpnp_adc_tm_disable_chan_meas(chip->adc_tm_dev,
 							&chip->adc_param);
diff --git a/drivers/rtc/alarm.c b/drivers/rtc/alarm.c
index e318ecf..1648cba 100644
--- a/drivers/rtc/alarm.c
+++ b/drivers/rtc/alarm.c
@@ -25,6 +25,7 @@
 
 #include <asm/mach/time.h>
 
+#define ALARM_DELTA 120
 #define ANDROID_ALARM_PRINT_ERROR (1U << 0)
 #define ANDROID_ALARM_PRINT_INIT_STATUS (1U << 1)
 #define ANDROID_ALARM_PRINT_TSET (1U << 2)
@@ -454,7 +455,7 @@
 			rtc_delta.tv_sec, rtc_delta.tv_nsec);
 		if (rtc_current_time + 1 >= rtc_alarm_time) {
 			pr_alarm(SUSPEND, "alarm about to go off\n");
-			memset(&rtc_alarm, 0, sizeof(rtc_alarm));
+			rtc_time_to_tm(0, &rtc_alarm.time);
 			rtc_alarm.enabled = 0;
 			rtc_set_alarm(alarm_rtc_dev, &rtc_alarm);
 
@@ -479,7 +480,7 @@
 
 	pr_alarm(SUSPEND, "alarm_resume(%p)\n", pdev);
 
-	memset(&alarm, 0, sizeof(alarm));
+	rtc_time_to_tm(0, &alarm.time);
 	alarm.enabled = 0;
 	rtc_set_alarm(alarm_rtc_dev, &alarm);
 
@@ -512,6 +513,15 @@
 	rtc_tm_to_time(&rtc_time, &rtc_secs);
 	alarm_delta = wall_time.tv_sec - rtc_secs;
 	alarm_time = power_on_alarm - alarm_delta;
+
+	/*
+	 * Substract ALARM_DELTA from actual alarm time
+	 * to powerup the device before actual alarm
+	 * expiration.
+	 */
+	if ((alarm_time - ALARM_DELTA) > rtc_secs)
+		alarm_time -= ALARM_DELTA;
+
 	if (alarm_time <= rtc_secs)
 		goto disable_alarm;
 
diff --git a/drivers/slimbus/slim-msm-ngd.c b/drivers/slimbus/slim-msm-ngd.c
index 205bf37..0c9959c 100644
--- a/drivers/slimbus/slim-msm-ngd.c
+++ b/drivers/slimbus/slim-msm-ngd.c
@@ -998,7 +998,6 @@
 		container_of(qmi, struct msm_slim_ctrl, qmi);
 	struct slim_controller *ctrl = &dev->ctrl;
 	struct slim_device *sbdev;
-	int i;
 
 	ngd_slim_enable(dev, false);
 	/* disconnect BAM pipes */
@@ -1007,14 +1006,9 @@
 	if (dev->use_tx_msgqs == MSM_MSGQ_ENABLED)
 		dev->use_tx_msgqs = MSM_MSGQ_DOWN;
 	msm_slim_sps_exit(dev, false);
-	mutex_lock(&ctrl->m_ctrl);
 	/* device up should be called again after SSR */
 	list_for_each_entry(sbdev, &ctrl->devs, dev_list)
-		sbdev->notified = false;
-	/* invalidate logical addresses */
-	for (i = 0; i < ctrl->num_dev; i++)
-		ctrl->addrt[i].valid = false;
-	mutex_unlock(&ctrl->m_ctrl);
+		slim_report_absent(sbdev);
 	pr_info("SLIM ADSP SSR (DOWN) done");
 }
 
diff --git a/drivers/slimbus/slimbus.c b/drivers/slimbus/slimbus.c
index 201470f..b074289 100644
--- a/drivers/slimbus/slimbus.c
+++ b/drivers/slimbus/slimbus.c
@@ -286,20 +286,41 @@
 	.release	= slim_dev_release,
 };
 
-static void slim_report_present(struct work_struct *work)
+static void slim_report(struct work_struct *work)
 {
 	u8 laddr;
-	int ret;
+	int ret, i;
 	struct slim_driver *sbdrv;
 	struct slim_device *sbdev =
 			container_of(work, struct slim_device, wd);
-	if (sbdev->notified || !sbdev->dev.driver)
+	struct slim_controller *ctrl = sbdev->ctrl;
+	if (!sbdev->dev.driver)
+		return;
+	/* check if device-up or down needs to be called */
+	mutex_lock(&ctrl->m_ctrl);
+	/* address no longer valid, means device reported absent */
+	for (i = 0; i < ctrl->num_dev; i++) {
+		if (sbdev->laddr == ctrl->addrt[i].laddr &&
+			ctrl->addrt[i].valid == false &&
+			sbdev->notified)
+			break;
+	}
+	mutex_unlock(&ctrl->m_ctrl);
+	sbdrv = to_slim_driver(sbdev->dev.driver);
+	if (i < ctrl->num_dev) {
+		sbdev->notified = false;
+		if (sbdrv->device_down)
+			sbdrv->device_down(sbdev);
+		return;
+	}
+	if (sbdev->notified)
 		return;
 	ret = slim_get_logical_addr(sbdev, sbdev->e_addr, 6, &laddr);
-	sbdrv = to_slim_driver(sbdev->dev.driver);
-	if (!ret && sbdrv->device_up) {
-		sbdev->notified = true;
-		sbdrv->device_up(sbdev);
+	if (!ret) {
+		if (sbdrv)
+			sbdev->notified = true;
+		if (sbdrv->device_up)
+			sbdrv->device_up(sbdev);
 	}
 }
 
@@ -322,7 +343,7 @@
 	INIT_LIST_HEAD(&sbdev->mark_define);
 	INIT_LIST_HEAD(&sbdev->mark_suspend);
 	INIT_LIST_HEAD(&sbdev->mark_removal);
-	INIT_WORK(&sbdev->wd, slim_report_present);
+	INIT_WORK(&sbdev->wd, slim_report);
 	mutex_lock(&ctrl->m_ctrl);
 	list_add_tail(&sbdev->dev_list, &ctrl->devs);
 	mutex_unlock(&ctrl->m_ctrl);
@@ -604,6 +625,31 @@
 EXPORT_SYMBOL_GPL(slim_add_numbered_controller);
 
 /*
+ * slim_report_absent: Controller calls this function when a device
+ *	reports absent, OR when the device cannot be communicated with
+ * @sbdev: Device that cannot be reached, or sent report absent
+ */
+void slim_report_absent(struct slim_device *sbdev)
+{
+	struct slim_controller *ctrl;
+	int i;
+	if (!sbdev)
+		return;
+	ctrl = sbdev->ctrl;
+	if (!ctrl)
+		return;
+	/* invalidate logical addresses */
+	mutex_lock(&ctrl->m_ctrl);
+	for (i = 0; i < ctrl->num_dev; i++) {
+		if (sbdev->laddr == ctrl->addrt[i].laddr)
+			ctrl->addrt[i].valid = false;
+	}
+	mutex_unlock(&ctrl->m_ctrl);
+	queue_work(ctrl->wq, &sbdev->wd);
+}
+EXPORT_SYMBOL(slim_report_absent);
+
+/*
  * slim_msg_response: Deliver Message response received from a device to the
  *	framework.
  * @ctrl: Controller handle
diff --git a/drivers/spi/spi_qsd.c b/drivers/spi/spi_qsd.c
index 25b4b5e..4512d02 100644
--- a/drivers/spi/spi_qsd.c
+++ b/drivers/spi/spi_qsd.c
@@ -2664,7 +2664,6 @@
 struct msm_spi_platform_data * __init msm_spi_dt_to_pdata(
 			struct platform_device *pdev, struct msm_spi *dd)
 {
-	int i;
 	struct msm_spi_platform_data *pdata;
 
 	pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
@@ -2725,10 +2724,6 @@
 			pdata->use_bam = false;
 		}
 	}
-
-	for (i = 0; i < ARRAY_SIZE(spi_cs_rsrcs); ++i)
-		dd->cs_gpios[i].valid = (dd->cs_gpios[i].gpio_num >= 0);
-
 	return pdata;
 }
 
@@ -2834,10 +2829,12 @@
 						i + ARRAY_SIZE(spi_rsrcs));
 			dd->cs_gpios[i].gpio_num = resource ?
 							resource->start : -1;
-			dd->cs_gpios[i].valid = 0;
 		}
 	}
 
+	for (i = 0; i < ARRAY_SIZE(spi_cs_rsrcs); ++i)
+		dd->cs_gpios[i].valid = 0;
+
 	dd->pdata = pdata;
 	resource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	if (!resource) {
diff --git a/drivers/usb/gadget/android.c b/drivers/usb/gadget/android.c
index 38b1967..9b38ed3 100644
--- a/drivers/usb/gadget/android.c
+++ b/drivers/usb/gadget/android.c
@@ -388,22 +388,30 @@
 	}
 }
 
-static void android_enable(struct android_dev *dev)
+static int android_enable(struct android_dev *dev)
 {
 	struct usb_composite_dev *cdev = dev->cdev;
 	struct android_configuration *conf;
+	int err = 0;
 
 	if (WARN_ON(!dev->disable_depth))
-		return;
+		return err;
 
 	if (--dev->disable_depth == 0) {
 
-		list_for_each_entry(conf, &dev->configs, list_item)
-			usb_add_config(cdev, &conf->usb_config,
+		list_for_each_entry(conf, &dev->configs, list_item) {
+			err = usb_add_config(cdev, &conf->usb_config,
 						android_bind_config);
-
+			if (err < 0) {
+				pr_err("%s: usb_add_config failed : err: %d\n",
+						__func__, err);
+				return err;
+			}
+		}
 		usb_gadget_connect(cdev->gadget);
 	}
+
+	return err;
 }
 
 static void android_disable(struct android_dev *dev)
@@ -2130,7 +2138,18 @@
 	list_for_each_entry(f_holder, &conf->enabled_functions, enabled_list) {
 		ret = f_holder->f->bind_config(f_holder->f, c);
 		if (ret) {
-			pr_err("%s: %s failed", __func__, f_holder->f->name);
+			pr_err("%s: %s failed\n", __func__, f_holder->f->name);
+			while (!list_empty(&c->functions)) {
+				struct usb_function		*f;
+
+				f = list_first_entry(&c->functions,
+					struct usb_function, list);
+				list_del(&f->list);
+				if (f->unbind)
+					f->unbind(c, f);
+			}
+			if (c->unbind)
+				c->unbind(c);
 			return ret;
 		}
 	}
@@ -2347,7 +2366,7 @@
 	int enabled = 0;
 	bool audio_enabled = false;
 	static DEFINE_RATELIMIT_STATE(rl, 10*HZ, 1);
-
+	int err = 0;
 
 	if (!cdev)
 		return -ENODEV;
@@ -2382,7 +2401,14 @@
 			}
 		if (audio_enabled)
 			msleep(100);
-		android_enable(dev);
+		err = android_enable(dev);
+		if (err < 0) {
+			pr_err("%s: android_enable failed\n", __func__);
+			dev->connected = 0;
+			dev->enabled = false;
+			mutex_unlock(&dev->mutex);
+			return size;
+		}
 		dev->enabled = true;
 	} else if (!enabled && dev->enabled) {
 		android_disable(dev);
diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c
index e068484..e4fb26a 100644
--- a/drivers/usb/gadget/composite.c
+++ b/drivers/usb/gadget/composite.c
@@ -824,6 +824,11 @@
 
 	spin_lock_irqsave(&cdev->lock, flags);
 
+	if (WARN_ON(!config->cdev)) {
+		spin_unlock_irqrestore(&cdev->lock, flags);
+		return 0;
+	}
+
 	if (cdev->config == config)
 		reset_config(cdev);
 
diff --git a/drivers/video/msm/mdss/mdss_fb.c b/drivers/video/msm/mdss/mdss_fb.c
index 24d4723..a4a2af4 100644
--- a/drivers/video/msm/mdss/mdss_fb.c
+++ b/drivers/video/msm/mdss/mdss_fb.c
@@ -322,6 +322,7 @@
 	if (pdata->next)
 		mfd->split_display = true;
 	mfd->mdp = *mdp_instance;
+	INIT_LIST_HEAD(&mfd->proc_list);
 
 	mutex_init(&mfd->lock);
 	mutex_init(&mfd->bl_lock);
@@ -1101,14 +1102,32 @@
 static int mdss_fb_open(struct fb_info *info, int user)
 {
 	struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
+	struct mdss_fb_proc_info *pinfo = NULL;
 	int result;
+	int pid = current->tgid;
+
+	list_for_each_entry(pinfo, &mfd->proc_list, list) {
+		if (pinfo->pid == pid)
+			break;
+	}
+
+	if ((pinfo == NULL) || (pinfo->pid != pid)) {
+		pinfo = kmalloc(sizeof(*pinfo), GFP_KERNEL);
+		if (!pinfo) {
+			pr_err("unable to alloc process info\n");
+			return -ENOMEM;
+		}
+		pinfo->pid = pid;
+		pinfo->ref_cnt = 0;
+		list_add(&pinfo->list, &mfd->proc_list);
+		pr_debug("new process entry pid=%d\n", pinfo->pid);
+	}
 
 	result = pm_runtime_get_sync(info->dev);
 
 	if (result < 0)
 		pr_err("pm_runtime: fail to wake up\n");
 
-
 	if (!mfd->ref_cnt) {
 		result = mdss_fb_blank_sub(FB_BLANK_UNBLANK, info,
 					   mfd->op_enable);
@@ -1120,6 +1139,7 @@
 		}
 	}
 
+	pinfo->ref_cnt++;
 	mfd->ref_cnt++;
 	return 0;
 }
@@ -1127,7 +1147,9 @@
 static int mdss_fb_release(struct fb_info *info, int user)
 {
 	struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
+	struct mdss_fb_proc_info *pinfo = NULL;
 	int ret = 0;
+	int pid = current->tgid;
 
 	if (!mfd->ref_cnt) {
 		pr_info("try to close unopened fb %d!\n", mfd->index);
@@ -1137,6 +1159,31 @@
 	mdss_fb_pan_idle(mfd);
 	mfd->ref_cnt--;
 
+	list_for_each_entry(pinfo, &mfd->proc_list, list) {
+		if (pinfo->pid == pid)
+			break;
+	}
+
+	if (!pinfo || (pinfo->pid != pid)) {
+		pr_warn("unable to find process info for fb%d pid=%d\n",
+				mfd->index, pid);
+	} else {
+		pr_debug("found process entry pid=%d ref=%d\n",
+				pinfo->pid, pinfo->ref_cnt);
+
+		pinfo->ref_cnt--;
+		if (pinfo->ref_cnt == 0) {
+			if (mfd->mdp.release_fnc) {
+				ret = mfd->mdp.release_fnc(mfd);
+				if (ret)
+					pr_err("error releasing fb%d pid=%d\n",
+						mfd->index, pinfo->pid);
+			}
+			list_del(&pinfo->list);
+			kfree(pinfo);
+		}
+	}
+
 	if (!mfd->ref_cnt) {
 		ret = mdss_fb_blank_sub(FB_BLANK_POWERDOWN, info,
 				       mfd->op_enable);
diff --git a/drivers/video/msm/mdss/mdss_fb.h b/drivers/video/msm/mdss/mdss_fb.h
index 030fd67..65218c0 100644
--- a/drivers/video/msm/mdss/mdss_fb.h
+++ b/drivers/video/msm/mdss/mdss_fb.h
@@ -61,6 +61,8 @@
 	int (*init_fnc)(struct msm_fb_data_type *mfd);
 	int (*on_fnc)(struct msm_fb_data_type *mfd);
 	int (*off_fnc)(struct msm_fb_data_type *mfd);
+	/* called to release resources associated to the process */
+	int (*release_fnc)(struct msm_fb_data_type *mfd);
 	int (*kickoff_fnc)(struct msm_fb_data_type *mfd);
 	int (*ioctl_handler)(struct msm_fb_data_type *mfd, u32 cmd, void *arg);
 	void (*dma_fnc)(struct msm_fb_data_type *mfd);
@@ -81,6 +83,12 @@
 					/ (2 * max_bright);\
 					} while (0)
 
+struct mdss_fb_proc_info {
+	int pid;
+	u32 ref_cnt;
+	struct list_head list;
+};
+
 struct msm_fb_data_type {
 	u32 key;
 	u32 index;
@@ -148,6 +156,7 @@
 	u32 is_power_setting;
 
 	u32 dcm_state;
+	struct list_head proc_list;
 };
 
 struct msm_fb_backup_type {
diff --git a/drivers/video/msm/mdss/mdss_io_util.c b/drivers/video/msm/mdss/mdss_io_util.c
index c862e78..6a1e7f7c 100644
--- a/drivers/video/msm/mdss/mdss_io_util.c
+++ b/drivers/video/msm/mdss/mdss_io_util.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2013, 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
@@ -211,7 +211,8 @@
 					in_vreg[i].vreg_name, rc);
 				goto vreg_set_opt_mode_fail;
 			}
-			msleep(in_vreg[i].pre_on_sleep);
+			if (in_vreg[i].pre_on_sleep)
+				msleep(in_vreg[i].pre_on_sleep);
 			rc = regulator_set_optimum_mode(in_vreg[i].vreg,
 				in_vreg[i].enable_load);
 			if (rc < 0) {
@@ -221,7 +222,8 @@
 				goto vreg_set_opt_mode_fail;
 			}
 			rc = regulator_enable(in_vreg[i].vreg);
-			msleep(in_vreg[i].post_on_sleep);
+			if (in_vreg[i].post_on_sleep)
+				msleep(in_vreg[i].post_on_sleep);
 			if (rc < 0) {
 				DEV_ERR("%pS->%s: %s enable failed\n",
 					__builtin_return_address(0), __func__,
@@ -232,11 +234,13 @@
 	} else {
 		for (i = num_vreg-1; i >= 0; i--)
 			if (regulator_is_enabled(in_vreg[i].vreg)) {
-				msleep(in_vreg[i].pre_off_sleep);
+				if (in_vreg[i].pre_off_sleep)
+					msleep(in_vreg[i].pre_off_sleep);
 				regulator_set_optimum_mode(in_vreg[i].vreg,
 					in_vreg[i].disable_load);
 				regulator_disable(in_vreg[i].vreg);
-				msleep(in_vreg[i].post_off_sleep);
+				if (in_vreg[i].post_off_sleep)
+					msleep(in_vreg[i].post_off_sleep);
 			}
 	}
 	return rc;
@@ -246,11 +250,13 @@
 
 vreg_set_opt_mode_fail:
 	for (i--; i >= 0; i--) {
-		msleep(in_vreg[i].pre_off_sleep);
+		if (in_vreg[i].pre_off_sleep)
+			msleep(in_vreg[i].pre_off_sleep);
 		regulator_set_optimum_mode(in_vreg[i].vreg,
 			in_vreg[i].disable_load);
 		regulator_disable(in_vreg[i].vreg);
-		msleep(in_vreg[i].post_off_sleep);
+		if (in_vreg[i].post_off_sleep)
+			msleep(in_vreg[i].post_off_sleep);
 	}
 
 	return rc;
diff --git a/drivers/video/msm/mdss/mdss_mdp.c b/drivers/video/msm/mdss/mdss_mdp.c
index 0a41ef8..44d81c8 100644
--- a/drivers/video/msm/mdss/mdss_mdp.c
+++ b/drivers/video/msm/mdss/mdss_mdp.c
@@ -166,9 +166,10 @@
 
 	spin_lock(&mdss_lock);
 	hw = mdss_irq_handlers[hw_ndx];
+	spin_unlock(&mdss_lock);
+
 	if (hw)
 		rc = hw->irq_handler(irq, hw->ptr);
-	spin_unlock(&mdss_lock);
 
 	return rc;
 }
diff --git a/drivers/video/msm/mdss/mdss_mdp.h b/drivers/video/msm/mdss/mdss_mdp.h
index 91257f2..08849c8 100644
--- a/drivers/video/msm/mdss/mdss_mdp.h
+++ b/drivers/video/msm/mdss/mdss_mdp.h
@@ -315,6 +315,7 @@
 	u32 ftch_id;
 	atomic_t ref_cnt;
 	u32 play_cnt;
+	int pid;
 
 	u32 flags;
 	u32 bwc_mode;
@@ -363,7 +364,6 @@
 };
 
 struct mdss_overlay_private {
-	int vsync_pending;
 	ktime_t vsync_time;
 	struct sysfs_dirent *vsync_event_sd;
 	int borderfill_enable;
@@ -378,6 +378,7 @@
 	struct list_head overlay_list;
 	struct list_head pipes_used;
 	struct list_head pipes_cleanup;
+	struct list_head rot_proc_list;
 	bool mixer_swap;
 };
 
diff --git a/drivers/video/msm/mdss/mdss_mdp_intf_video.c b/drivers/video/msm/mdss/mdss_mdp_intf_video.c
index 6fb8883..3f3e51c 100644
--- a/drivers/video/msm/mdss/mdss_mdp_intf_video.c
+++ b/drivers/video/msm/mdss/mdss_mdp_intf_video.c
@@ -479,9 +479,9 @@
 		mdp_video_write(ctx, MDSS_MDP_REG_INTF_TIMING_ENGINE_EN, 1);
 		wmb();
 
-		rc = wait_for_completion_interruptible_timeout(&ctx->vsync_comp,
+		rc = wait_for_completion_timeout(&ctx->vsync_comp,
 				usecs_to_jiffies(VSYNC_TIMEOUT_US));
-		WARN(rc <= 0, "timeout (%d) enabling timegen on ctl=%d\n",
+		WARN(rc == 0, "timeout (%d) enabling timegen on ctl=%d\n",
 				rc, ctl->num);
 
 		ctx->timegen_en = true;
diff --git a/drivers/video/msm/mdss/mdss_mdp_intf_writeback.c b/drivers/video/msm/mdss/mdss_mdp_intf_writeback.c
index 0a37573..ff977a9 100644
--- a/drivers/video/msm/mdss/mdss_mdp_intf_writeback.c
+++ b/drivers/video/msm/mdss/mdss_mdp_intf_writeback.c
@@ -345,12 +345,12 @@
 	if (ctx->comp_cnt == 0)
 		return rc;
 
-	rc = wait_for_completion_interruptible_timeout(&ctx->wb_comp,
+	rc = wait_for_completion_timeout(&ctx->wb_comp,
 			KOFF_TIMEOUT);
 	mdss_mdp_set_intr_callback(ctx->intr_type, ctx->intf_num,
 		NULL, NULL);
 
-	if (rc <= 0) {
+	if (rc == 0) {
 		rc = -ENODEV;
 		WARN(1, "writeback kickoff timed out (%d) ctl=%d\n",
 						rc, ctl->num);
diff --git a/drivers/video/msm/mdss/mdss_mdp_overlay.c b/drivers/video/msm/mdss/mdss_mdp_overlay.c
index 938cb1f..1a5a44b 100644
--- a/drivers/video/msm/mdss/mdss_mdp_overlay.c
+++ b/drivers/video/msm/mdss/mdss_mdp_overlay.c
@@ -241,6 +241,8 @@
 
 	if (req->id == MSMFB_NEW_REQUEST) {
 		rot = mdss_mdp_rotator_session_alloc();
+		rot->pid = current->tgid;
+		list_add(&rot->list, &mdp5_data->rot_proc_list);
 
 		if (!rot) {
 			pr_err("unable to allocate rotator session\n");
@@ -439,6 +441,7 @@
 		mutex_unlock(&mfd->lock);
 		pipe->mixer = mixer;
 		pipe->mfd = mfd;
+		pipe->pid = current->tgid;
 		pipe->play_cnt = 0;
 	} else {
 		pipe = mdss_mdp_pipe_get(mdp5_data->mdata, req->id);
@@ -902,6 +905,7 @@
 				continue;
 			}
 			mutex_lock(&mfd->lock);
+			pipe->pid = 0;
 			if (!list_empty(&pipe->used_list)) {
 				list_del_init(&pipe->used_list);
 				list_add(&pipe->cleanup_list,
@@ -952,6 +956,10 @@
 		if (rot) {
 			mdss_mdp_overlay_free_buf(&rot->src_buf);
 			mdss_mdp_overlay_free_buf(&rot->dst_buf);
+
+			rot->pid = 0;
+			if (!list_empty(&rot->list))
+				list_del_init(&rot->list);
 			ret = mdss_mdp_rotator_release(rot);
 		}
 	} else {
@@ -964,18 +972,31 @@
 	return ret;
 }
 
-static int mdss_mdp_overlay_release_all(struct msm_fb_data_type *mfd)
+/**
+ * mdss_mdp_overlay_release_all() - release any overlays associated with fb dev
+ * @mfd:	Msm frame buffer structure associated with fb device
+ *
+ * Release any resources allocated by calling process, this can be called
+ * on fb_release to release any overlays/rotator sessions left open.
+ */
+static int __mdss_mdp_overlay_release_all(struct msm_fb_data_type *mfd)
 {
 	struct mdss_mdp_pipe *pipe;
+	struct mdss_mdp_rotator_session *rot, *tmp;
 	struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd);
 	u32 unset_ndx = 0;
 	int cnt = 0;
+	int pid = current->tgid;
+
+	pr_debug("releasing all resources for fb%d pid=%d\n", mfd->index, pid);
 
 	mutex_lock(&mdp5_data->ov_lock);
 	mutex_lock(&mfd->lock);
 	list_for_each_entry(pipe, &mdp5_data->pipes_used, used_list) {
-		unset_ndx |= pipe->ndx;
-		cnt++;
+		if (pipe->pid == pid) {
+			unset_ndx |= pipe->ndx;
+			cnt++;
+		}
 	}
 
 	if (cnt == 0 && !list_empty(&mdp5_data->pipes_cleanup)) {
@@ -995,6 +1016,14 @@
 	if (cnt)
 		mfd->mdp.kickoff_fnc(mfd);
 
+	list_for_each_entry_safe(rot, tmp, &mdp5_data->rot_proc_list, list) {
+		if (rot->pid == pid) {
+			if (!list_empty(&rot->list))
+				list_del_init(&rot->list);
+			mdss_mdp_rotator_release(rot);
+		}
+	}
+
 	return 0;
 }
 
@@ -1372,14 +1401,13 @@
 
 int mdss_mdp_overlay_vsync_ctrl(struct msm_fb_data_type *mfd, int en)
 {
-	struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd);
 	struct mdss_mdp_ctl *ctl = mfd_to_ctl(mfd);
 	int rc;
 
 	if (!ctl)
 		return -ENODEV;
 	if (!ctl->add_vsync_handler || !ctl->remove_vsync_handler)
-		return -ENOTSUPP;
+		return -EOPNOTSUPP;
 
 	rc = mutex_lock_interruptible(&ctl->lock);
 	if (rc)
@@ -1388,9 +1416,8 @@
 	if (!ctl->power_on) {
 		pr_debug("fb%d vsync pending first update en=%d\n",
 				mfd->index, en);
-		mdp5_data->vsync_pending = en;
 		mutex_unlock(&ctl->lock);
-		return 0;
+		return -EPERM;
 	}
 
 	pr_debug("fb%d vsync en=%d\n", mfd->index, en);
@@ -2009,11 +2036,7 @@
 	if (IS_ERR_VALUE(rc)) {
 		pr_err("Failed to turn on fb%d\n", mfd->index);
 		mdss_mdp_overlay_off(mfd);
-	} else if (mdp5_data->vsync_pending) {
-		mdp5_data->vsync_pending = 0;
-		mdss_mdp_overlay_vsync_ctrl(mfd, mdp5_data->vsync_pending);
 	}
-
 	return rc;
 }
 
@@ -2022,6 +2045,7 @@
 	int rc;
 	struct mdss_overlay_private *mdp5_data;
 	struct mdss_mdp_mixer *mixer;
+	int need_cleanup;
 
 	if (!mfd)
 		return -ENODEV;
@@ -2049,18 +2073,13 @@
 	if (mixer)
 		mixer->cursor_enabled = 0;
 
-	if (!mfd->ref_cnt) {
-		mdss_mdp_overlay_release_all(mfd);
-	} else {
-		int need_cleanup;
-		mutex_lock(&mfd->lock);
-		need_cleanup = !list_empty(&mdp5_data->pipes_cleanup);
-		mutex_unlock(&mfd->lock);
+	mutex_lock(&mfd->lock);
+	need_cleanup = !list_empty(&mdp5_data->pipes_cleanup);
+	mutex_unlock(&mfd->lock);
 
-		if (need_cleanup) {
-			pr_debug("cleaning up some pipes\n");
-			mdss_mdp_overlay_kickoff(mfd);
-		}
+	if (need_cleanup) {
+		pr_debug("cleaning up pipes on fb%d\n", mfd->index);
+		mdss_mdp_overlay_kickoff(mfd);
 	}
 
 	rc = mdss_mdp_ctl_stop(mdp5_data->ctl);
@@ -2105,6 +2124,7 @@
 
 	mdp5_interface->on_fnc = mdss_mdp_overlay_on;
 	mdp5_interface->off_fnc = mdss_mdp_overlay_off;
+	mdp5_interface->release_fnc = __mdss_mdp_overlay_release_all;
 	mdp5_interface->do_histogram = NULL;
 	mdp5_interface->cursor_update = mdss_mdp_hw_cursor_update;
 	mdp5_interface->dma_fnc = mdss_mdp_overlay_pan_display;
@@ -2121,6 +2141,7 @@
 
 	INIT_LIST_HEAD(&mdp5_data->pipes_used);
 	INIT_LIST_HEAD(&mdp5_data->pipes_cleanup);
+	INIT_LIST_HEAD(&mdp5_data->rot_proc_list);
 	mutex_init(&mdp5_data->ov_lock);
 	mdp5_data->hw_refresh = true;
 	mdp5_data->overlay_play_enable = true;
diff --git a/drivers/video/msm/mdss/mdss_mdp_pp.c b/drivers/video/msm/mdss/mdss_mdp_pp.c
index ebbf9e7..95eb381 100644
--- a/drivers/video/msm/mdss/mdss_mdp_pp.c
+++ b/drivers/video/msm/mdss/mdss_mdp_pp.c
@@ -3331,7 +3331,7 @@
 	unsigned int ptr;
 	ptr = (unsigned int) addr;
 	/* if request is outside the MDP reg-map or is not aligned 4 */
-	if (ptr == 0x0 || ptr > 0x5138 || ptr % 0x4)
+	if (ptr > 0x5138 || ptr % 0x4)
 		goto end;
 	if (ptr >= 0x100 && ptr <= 0x5138) {
 		/* if ptr is in dspp range */
@@ -3386,7 +3386,8 @@
 				ret = 1;
 		else if (ptr == 0x2234 || ptr == 0x1e34 || ptr == 0x2634)
 				ret = 1;
-	}
+	} else if (ptr == 0x0)
+		ret = 1;
 end:
 	return ret;
 }
diff --git a/drivers/video/msm/mdss/mdss_mdp_rotator.h b/drivers/video/msm/mdss/mdss_mdp_rotator.h
index 43c9e6a..74eeeeb 100644
--- a/drivers/video/msm/mdss/mdss_mdp_rotator.h
+++ b/drivers/video/msm/mdss/mdss_mdp_rotator.h
@@ -23,6 +23,7 @@
 	u32 session_id;
 	u32 ref_cnt;
 	u32 params_changed;
+	int pid;
 
 	u32 format;
 	u32 flags;
@@ -43,6 +44,7 @@
 	struct mdss_mdp_data dst_buf;
 
 	struct list_head head;
+	struct list_head list;
 	struct mdss_mdp_rotator_session *next;
 };
 
diff --git a/drivers/video/msm/mdss/mdss_mdp_wb.c b/drivers/video/msm/mdss/mdss_mdp_wb.c
index 0c74137..72288c2 100644
--- a/drivers/video/msm/mdss/mdss_mdp_wb.c
+++ b/drivers/video/msm/mdss/mdss_mdp_wb.c
@@ -533,11 +533,11 @@
 		goto kickoff_fail;
 	}
 
-	ret = wait_for_completion_interruptible_timeout(&comp, KOFF_TIMEOUT);
-	if (ret <= 0) {
+	ret = wait_for_completion_timeout(&comp, KOFF_TIMEOUT);
+	if (ret == 0)
 		WARN(1, "wfd kick off time out=%d ctl=%d", ret, ctl->num);
+	else
 		ret = 0;
-	}
 
 	if (wb && node) {
 		mutex_lock(&wb->lock);
diff --git a/include/linux/batterydata-lib.h b/include/linux/batterydata-lib.h
index fe2d86f..ff38eb6 100644
--- a/include/linux/batterydata-lib.h
+++ b/include/linux/batterydata-lib.h
@@ -26,6 +26,8 @@
 
 #define MAX_SINGLE_LUT_COLS	20
 
+#define MAX_BATT_ID_NUM		4
+
 struct single_row_lut {
 	int x[MAX_SINGLE_LUT_COLS];
 	int y[MAX_SINGLE_LUT_COLS];
@@ -69,6 +71,11 @@
 	int ocv[PC_TEMP_ROWS][PC_TEMP_COLS];
 };
 
+struct batt_ids {
+	int kohm[MAX_BATT_ID_NUM];
+	int num;
+};
+
 enum battery_type {
 	BATT_UNKNOWN = 0,
 	BATT_PALLADIUM,
@@ -99,7 +106,7 @@
  * @cutoff_uv:		cutoff voltage of the battery
  * @iterm_ua:		termination current of the battery when charging
  *			to 100%
- * @batt_id_kohm:	battery id resistor value
+ * @batt_id_kohm:	the best matched battery id resistor value
  */
 
 struct bms_battery_data {
diff --git a/include/linux/cm36283.h b/include/linux/cm36283.h
index cccd5ee..362b709 100644
--- a/include/linux/cm36283.h
+++ b/include/linux/cm36283.h
@@ -17,6 +17,8 @@
 #ifndef __LINUX_CM36283_H
 #define __LINUX_CM36283_H
 
+#include <linux/bitops.h>
+
 #define CM36283_I2C_NAME "cm36283"
 
 /* Define Slave Address*/
@@ -102,6 +104,19 @@
 #define INT_FLAG_PS_IF_CLOSE         (1<<9)
 #define INT_FLAG_PS_IF_AWAY          (1<<8)  
 
+#define LS_PWR_ON		BIT(0)
+#define PS_PWR_ON		BIT(1)
+
+#define CAPELLA_CM3602_IOCTL_MAGIC 'c'
+#define CAPELLA_CM3602_IOCTL_GET_ENABLED \
+	_IOR(CAPELLA_CM3602_IOCTL_MAGIC, 1, int *)
+#define CAPELLA_CM3602_IOCTL_ENABLE \
+	_IOW(CAPELLA_CM3602_IOCTL_MAGIC, 2, int *)
+
+#define LIGHTSENSOR_IOCTL_MAGIC 'l'
+#define LIGHTSENSOR_IOCTL_GET_ENABLED _IOR(LIGHTSENSOR_IOCTL_MAGIC, 1, int *)
+#define LIGHTSENSOR_IOCTL_ENABLE _IOW(LIGHTSENSOR_IOCTL_MAGIC, 2, int *)
+
 extern unsigned int ps_kparam1;
 extern unsigned int ps_kparam2;
 
diff --git a/include/linux/input/ft5x06_ts.h b/include/linux/input/ft5x06_ts.h
index 4da38a4..149133e 100644
--- a/include/linux/input/ft5x06_ts.h
+++ b/include/linux/input/ft5x06_ts.h
@@ -55,6 +55,7 @@
 	u32 hard_rst_dly;
 	u32 soft_rst_dly;
 	u32 num_max_touches;
+	bool fw_vkey_support;
 	bool no_force_update;
 	bool i2c_pull_up;
 	int (*power_init) (bool);
diff --git a/include/linux/mfd/wcd9xxx/core-resource.h b/include/linux/mfd/wcd9xxx/core-resource.h
new file mode 100644
index 0000000..442496e
--- /dev/null
+++ b/include/linux/mfd/wcd9xxx/core-resource.h
@@ -0,0 +1,137 @@
+/* Copyright (c) 2013, 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 __MFD_CORE_RESOURCE_H__
+#define __MFD_CORE_RESOURCE_H__
+
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/pm_qos.h>
+
+#define WCD9XXX_MAX_IRQ_REGS 4
+#define WCD9XXX_MAX_NUM_IRQS (WCD9XXX_MAX_IRQ_REGS * 8)
+
+struct intr_data {
+	int intr_num;
+	bool clear_first;
+};
+
+enum wcd9xxx_pm_state {
+	WCD9XXX_PM_SLEEPABLE,
+	WCD9XXX_PM_AWAKE,
+	WCD9XXX_PM_ASLEEP,
+};
+
+enum wcd9xxx_intf_status {
+	WCD9XXX_INTERFACE_TYPE_PROBING,
+	WCD9XXX_INTERFACE_TYPE_SLIMBUS,
+	WCD9XXX_INTERFACE_TYPE_I2C,
+};
+
+struct wcd9xxx_core_resource {
+	struct mutex irq_lock;
+	struct mutex nested_irq_lock;
+
+	enum wcd9xxx_pm_state pm_state;
+	struct mutex pm_lock;
+	/* pm_wq notifies change of pm_state */
+	wait_queue_head_t pm_wq;
+	struct pm_qos_request pm_qos_req;
+	int wlock_holders;
+
+
+	/* holds the table of interrupts per codec */
+	const struct intr_data *intr_table;
+	int intr_table_size;
+	unsigned int irq_base;
+	unsigned int irq;
+	u8 irq_masks_cur[WCD9XXX_MAX_IRQ_REGS];
+	u8 irq_masks_cache[WCD9XXX_MAX_IRQ_REGS];
+	bool irq_level_high[WCD9XXX_MAX_NUM_IRQS];
+	int num_irqs;
+	int num_irq_regs;
+
+	/* Callback functions to read/write codec registers */
+	int (*codec_reg_read) (struct wcd9xxx_core_resource *,
+				unsigned short);
+	int (*codec_reg_write) (struct wcd9xxx_core_resource *,
+				unsigned short, u8);
+	int (*codec_bulk_read) (struct wcd9xxx_core_resource *,
+				unsigned short, int, u8 *);
+
+	/* Pointer to parent container data structure */
+	void *parent;
+
+	struct device *dev;
+};
+
+extern int wcd9xxx_core_res_init(
+	struct wcd9xxx_core_resource*,
+	int, int,
+	int (*codec_read)(struct wcd9xxx_core_resource *, unsigned short),
+	int (*codec_write)(struct wcd9xxx_core_resource *, unsigned short, u8),
+	int (*codec_bulk_read) (struct wcd9xxx_core_resource *, unsigned short,
+							int, u8 *));
+
+extern void wcd9xxx_core_res_deinit(
+	struct wcd9xxx_core_resource *);
+
+extern int wcd9xxx_core_res_suspend(
+	struct wcd9xxx_core_resource *,
+	pm_message_t);
+
+extern int wcd9xxx_core_res_resume(
+	struct wcd9xxx_core_resource *);
+
+extern int wcd9xxx_core_irq_init(
+	struct wcd9xxx_core_resource*);
+
+extern int wcd9xxx_initialize_irq(
+	struct wcd9xxx_core_resource*,
+	unsigned int,
+	unsigned int);
+
+enum wcd9xxx_intf_status wcd9xxx_get_intf_type(void);
+void wcd9xxx_set_intf_type(enum wcd9xxx_intf_status);
+
+bool wcd9xxx_lock_sleep(struct wcd9xxx_core_resource *);
+void wcd9xxx_unlock_sleep(struct wcd9xxx_core_resource *);
+void wcd9xxx_nested_irq_lock(struct wcd9xxx_core_resource *);
+void wcd9xxx_nested_irq_unlock(struct wcd9xxx_core_resource *);
+enum wcd9xxx_pm_state wcd9xxx_pm_cmpxchg(
+			struct wcd9xxx_core_resource *,
+			enum wcd9xxx_pm_state,
+			enum wcd9xxx_pm_state);
+
+int wcd9xxx_request_irq(struct wcd9xxx_core_resource *, int,
+			irq_handler_t, const char *, void *);
+
+void wcd9xxx_free_irq(struct wcd9xxx_core_resource *, int, void*);
+void wcd9xxx_enable_irq(struct wcd9xxx_core_resource *, int);
+void wcd9xxx_disable_irq(struct wcd9xxx_core_resource *, int);
+void wcd9xxx_disable_irq_sync(struct wcd9xxx_core_resource *, int);
+int wcd9xxx_reg_read(struct wcd9xxx_core_resource *,
+					 unsigned short);
+int wcd9xxx_reg_write(struct wcd9xxx_core_resource *,
+					  unsigned short, u8);
+int wcd9xxx_bulk_read(struct wcd9xxx_core_resource *,
+					unsigned short, int, u8 *);
+int wcd9xxx_bulk_write(struct wcd9xxx_core_resource*,
+					 unsigned short, int, u8*);
+int wcd9xxx_irq_init(struct wcd9xxx_core_resource *);
+void wcd9xxx_irq_exit(struct wcd9xxx_core_resource *);
+int wcd9xxx_core_res_resume(
+	struct wcd9xxx_core_resource *);
+int wcd9xxx_core_res_suspend(
+	struct wcd9xxx_core_resource *,
+	pm_message_t);
+#endif
diff --git a/include/linux/mfd/wcd9xxx/core.h b/include/linux/mfd/wcd9xxx/core.h
index e688bd9..c2ad2b4 100644
--- a/include/linux/mfd/wcd9xxx/core.h
+++ b/include/linux/mfd/wcd9xxx/core.h
@@ -14,12 +14,10 @@
 #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>
+#include <linux/mfd/wcd9xxx/core-resource.h>
 
-#define WCD9XXX_NUM_IRQ_REGS 4
 
 #define WCD9XXX_SLIM_NUM_PORT_REG 3
 #define TABLA_VERSION_1_0	0
@@ -90,6 +88,7 @@
 	WCD9XXX_IRQ_VBAT_MONITOR_ATTACK,
 	WCD9XXX_IRQ_VBAT_MONITOR_RELEASE,
 	WCD9XXX_NUM_IRQS,
+	WCD9XXX_IRQ_RESERVED_2 = WCD9XXX_NUM_IRQS,
 };
 
 enum {
@@ -99,17 +98,6 @@
 	TAPAN_NUM_IRQS = WCD9XXX_NUM_IRQS,
 };
 
-
-#define MAX(X, Y) (((int)X) >= ((int)Y) ? (X) : (Y))
-#define WCD9XXX_MAX_NUM_IRQS (MAX(MAX(TABLA_NUM_IRQS, SITAR_NUM_IRQS), \
-				  TAIKO_NUM_IRQS))
-
-enum wcd9xxx_pm_state {
-	WCD9XXX_PM_SLEEPABLE,
-	WCD9XXX_PM_AWAKE,
-	WCD9XXX_PM_ASLEEP,
-};
-
 /*
  * data structure for Slimbus and I2S channel.
  * Some of fields are only used in smilbus mode
@@ -145,12 +133,6 @@
 	wait_queue_head_t dai_wait;
 };
 
-enum wcd9xxx_intf_status {
-	WCD9XXX_INTERFACE_TYPE_PROBING,
-	WCD9XXX_INTERFACE_TYPE_SLIMBUS,
-	WCD9XXX_INTERFACE_TYPE_I2C,
-};
-
 #define WCD9XXX_CH(xport, xshift) \
 	{.port = xport, .shift = xshift}
 
@@ -178,8 +160,6 @@
 	struct slim_device *slim_slave;
 	struct mutex io_lock;
 	struct mutex xfer_lock;
-	struct mutex irq_lock;
-	struct mutex nested_irq_lock;
 	u8 version;
 
 	int reset_gpio;
@@ -188,6 +168,7 @@
 			int bytes, void *dest, bool interface_reg);
 	int (*write_dev)(struct wcd9xxx *wcd9xxx, unsigned short reg,
 			int bytes, void *src, bool interface_reg);
+	int (*dev_down)(struct wcd9xxx *wcd9xxx);
 	int (*post_reset)(struct wcd9xxx *wcd9xxx);
 
 	void *ssr_priv;
@@ -196,21 +177,11 @@
 	u32 num_of_supplies;
 	struct regulator_bulk_data *supplies;
 
-	enum wcd9xxx_pm_state pm_state;
-	struct mutex pm_lock;
-	/* pm_wq notifies change of pm_state */
-	wait_queue_head_t pm_wq;
-	struct pm_qos_request pm_qos_req;
-	int wlock_holders;
+	struct wcd9xxx_core_resource core_res;
 
 	u16 id_minor;
 	u16 id_major;
 
-	unsigned int irq_base;
-	unsigned int irq;
-	u8 irq_masks_cur[WCD9XXX_NUM_IRQ_REGS];
-	u8 irq_masks_cache[WCD9XXX_NUM_IRQ_REGS];
-	bool irq_level_high[WCD9XXX_MAX_NUM_IRQS];
 	/* Slimbus or I2S port */
 	u32 num_rx_port;
 	u32 num_tx_port;
@@ -221,36 +192,11 @@
 	const struct wcd9xxx_codec_type *codec_type;
 };
 
-int wcd9xxx_reg_read(struct wcd9xxx *wcd9xxx, unsigned short reg);
-int wcd9xxx_reg_write(struct wcd9xxx *wcd9xxx, unsigned short reg,
-		u8 val);
 int wcd9xxx_interface_reg_read(struct wcd9xxx *wcd9xxx, unsigned short reg);
 int wcd9xxx_interface_reg_write(struct wcd9xxx *wcd9xxx, unsigned short reg,
 		u8 val);
-int wcd9xxx_bulk_read(struct wcd9xxx *wcd9xxx, unsigned short reg,
-			int count, u8 *buf);
-int wcd9xxx_bulk_write(struct wcd9xxx *wcd9xxx, unsigned short reg,
-			int count, u8 *buf);
-int wcd9xxx_irq_init(struct wcd9xxx *wcd9xxx);
-void wcd9xxx_irq_exit(struct wcd9xxx *wcd9xxx);
 int wcd9xxx_get_logical_addresses(u8 *pgd_la, u8 *inf_la);
-enum wcd9xxx_intf_status wcd9xxx_get_intf_type(void);
 
-bool wcd9xxx_lock_sleep(struct wcd9xxx *wcd9xxx);
-void wcd9xxx_unlock_sleep(struct wcd9xxx *wcd9xxx);
-void wcd9xxx_nested_irq_lock(struct wcd9xxx *wcd9xxx);
-void wcd9xxx_nested_irq_unlock(struct wcd9xxx *wcd9xxx);
-enum wcd9xxx_pm_state wcd9xxx_pm_cmpxchg(struct wcd9xxx *wcd9xxx,
-				enum wcd9xxx_pm_state o,
-				enum wcd9xxx_pm_state n);
-
-int wcd9xxx_request_irq(struct wcd9xxx *wcd9xxx, int irq,
-			irq_handler_t handler, const char *name, void *data);
-
-void wcd9xxx_free_irq(struct wcd9xxx *wcd9xxx, int irq, void *data);
-void wcd9xxx_enable_irq(struct wcd9xxx *wcd9xxx, int irq);
-void wcd9xxx_disable_irq(struct wcd9xxx *wcd9xxx, int irq);
-void wcd9xxx_disable_irq_sync(struct wcd9xxx *wcd9xxx, int irq);
 #if defined(CONFIG_WCD9310_CODEC) || \
 	defined(CONFIG_WCD9304_CODEC) || \
 	defined(CONFIG_WCD9320_CODEC) || \
@@ -264,4 +210,16 @@
 	return 0;
 }
 #endif	/* CONFIG_OF */
+static inline void wcd9xxx_reg_update(struct wcd9xxx *core,
+				      unsigned short reg,
+				      u8 mask, u8 val)
+{
+	u8 reg_val;
+
+	if (core) {
+		reg_val = wcd9xxx_reg_read(&core->core_res, reg);
+		reg_val = (reg_val & ~mask) | (val & mask);
+		wcd9xxx_reg_write(&core->core_res, reg, reg_val);
+	}
+}
 #endif
diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
index 2cb297e..9d15908 100644
--- a/include/linux/mmc/card.h
+++ b/include/linux/mmc/card.h
@@ -67,6 +67,7 @@
 #define MMC_HIGH_52_MAX_DTR	52000000
 #define MMC_HIGH_DDR_MAX_DTR	52000000
 #define MMC_HS200_MAX_DTR	200000000
+#define MMC_HS400_MAX_DTR	200000000
 	unsigned int		sectors;
 	unsigned int		card_type;
 	unsigned int		hc_erase_size;		/* In sectors */
@@ -329,6 +330,7 @@
 #define MMC_CARD_SDXC		(1<<6)		/* card is SDXC */
 #define MMC_CARD_REMOVED	(1<<7)		/* card has been removed */
 #define MMC_STATE_HIGHSPEED_200	(1<<8)		/* card is in HS200 mode */
+#define MMC_STATE_HIGHSPEED_400	(1<<9)		/* card is in HS400 mode */
 #define MMC_STATE_DOING_BKOPS	(1<<10)		/* card is doing BKOPS */
 #define MMC_STATE_NEED_BKOPS	(1<<11)		/* card needs to do BKOPS */
 	unsigned int		quirks; 	/* card quirks */
@@ -347,6 +349,8 @@
 #define MMC_QUIRK_SEC_ERASE_TRIM_BROKEN (1<<10)	/* Skip secure for erase/trim */
 						/* byte mode */
 #define MMC_QUIRK_INAND_DATA_TIMEOUT  (1<<8)    /* For incorrect data timeout */
+/* To avoid eMMC device getting broken permanently due to HPI feature */
+#define MMC_QUIRK_BROKEN_HPI (1 << 11)
 
 	unsigned int		erase_size;	/* erase size in sectors */
  	unsigned int		erase_shift;	/* if erase unit is power 2 */
@@ -426,6 +430,8 @@
 
 	/* SDIO-specfic fields. You can use SDIO_ANY_ID here of course */
 	u16 cis_vendor, cis_device;
+	/* for MMC cards */
+	unsigned int ext_csd_rev;
 
 	void (*vendor_fixup)(struct mmc_card *card, int data);
 	int data;
@@ -435,11 +441,19 @@
 #define CID_OEMID_ANY ((unsigned short) -1)
 #define CID_NAME_ANY (NULL)
 
+#define EXT_CSD_REV_ANY (-1u)
+
+#define CID_MANFID_SANDISK	0x2
+#define CID_MANFID_TOSHIBA	0x11
+#define CID_MANFID_MICRON	0x13
+#define CID_MANFID_SAMSUNG	0x15
+#define CID_MANFID_HYNIX	0x90
+
 #define END_FIXUP { 0 }
 
 #define _FIXUP_EXT(_name, _manfid, _oemid, _rev_start, _rev_end,	\
 		   _cis_vendor, _cis_device,				\
-		   _fixup, _data)					\
+		   _fixup, _data, _ext_csd_rev)				\
 	{						   \
 		.name = (_name),			   \
 		.manfid = (_manfid),			   \
@@ -450,23 +464,30 @@
 		.cis_device = (_cis_device),		   \
 		.vendor_fixup = (_fixup),		   \
 		.data = (_data),			   \
+		.ext_csd_rev = (_ext_csd_rev),		   \
 	 }
 
 #define MMC_FIXUP_REV(_name, _manfid, _oemid, _rev_start, _rev_end,	\
-		      _fixup, _data)					\
+		      _fixup, _data, _ext_csd_rev)			\
 	_FIXUP_EXT(_name, _manfid,					\
 		   _oemid, _rev_start, _rev_end,			\
 		   SDIO_ANY_ID, SDIO_ANY_ID,				\
-		   _fixup, _data)					\
+		   _fixup, _data, _ext_csd_rev)				\
 
-#define MMC_FIXUP(_name, _manfid, _oemid, _fixup, _data) \
-	MMC_FIXUP_REV(_name, _manfid, _oemid, 0, -1ull, _fixup, _data)
+#define MMC_FIXUP(_name, _manfid, _oemid, _fixup, _data)		\
+	MMC_FIXUP_REV(_name, _manfid, _oemid, 0, -1ull, _fixup, _data,	\
+		      EXT_CSD_REV_ANY)
+
+#define MMC_FIXUP_EXT_CSD_REV(_name, _manfid, _oemid, _fixup, _data,	\
+			      _ext_csd_rev)				\
+	MMC_FIXUP_REV(_name, _manfid, _oemid, 0, -1ull, _fixup, _data,	\
+		      _ext_csd_rev)
 
 #define SDIO_FIXUP(_vendor, _device, _fixup, _data)			\
 	_FIXUP_EXT(CID_NAME_ANY, CID_MANFID_ANY,			\
 		    CID_OEMID_ANY, 0, -1ull,				\
 		   _vendor, _device,					\
-		   _fixup, _data)					\
+		   _fixup, _data, EXT_CSD_REV_ANY)			\
 
 #define cid_rev(hwrev, fwrev, year, month)	\
 	(((u64) hwrev) << 40 |                  \
@@ -502,6 +523,7 @@
 #define mmc_card_readonly(c)	((c)->state & MMC_STATE_READONLY)
 #define mmc_card_highspeed(c)	((c)->state & MMC_STATE_HIGHSPEED)
 #define mmc_card_hs200(c)	((c)->state & MMC_STATE_HIGHSPEED_200)
+#define mmc_card_hs400(c)	((c)->state & MMC_STATE_HIGHSPEED_400)
 #define mmc_card_blockaddr(c)	((c)->state & MMC_STATE_BLOCKADDR)
 #define mmc_card_ddr_mode(c)	((c)->state & MMC_STATE_HIGHSPEED_DDR)
 #define mmc_card_uhs(c)		((c)->state & MMC_STATE_ULTRAHIGHSPEED)
@@ -514,9 +536,14 @@
 #define mmc_card_set_present(c)	((c)->state |= MMC_STATE_PRESENT)
 #define mmc_card_set_readonly(c) ((c)->state |= MMC_STATE_READONLY)
 #define mmc_card_set_highspeed(c) ((c)->state |= MMC_STATE_HIGHSPEED)
+#define mmc_card_clr_highspeed(c) ((c)->state &= ~MMC_STATE_HIGHSPEED)
 #define mmc_card_set_hs200(c)	((c)->state |= MMC_STATE_HIGHSPEED_200)
+#define mmc_card_clr_hs200(c)	((c)->state &= ~MMC_STATE_HIGHSPEED_200)
+#define mmc_card_set_hs400(c)	((c)->state |= MMC_STATE_HIGHSPEED_400)
+#define mmc_card_clr_hs400(c)	((c)->state &= ~MMC_STATE_HIGHSPEED_400)
 #define mmc_card_set_blockaddr(c) ((c)->state |= MMC_STATE_BLOCKADDR)
 #define mmc_card_set_ddr_mode(c) ((c)->state |= MMC_STATE_HIGHSPEED_DDR)
+#define mmc_card_clr_ddr_mode(c) ((c)->state &= ~MMC_STATE_HIGHSPEED_DDR)
 #define mmc_card_set_uhs(c) ((c)->state |= MMC_STATE_ULTRAHIGHSPEED)
 #define mmc_sd_card_set_uhs(c) ((c)->state |= MMC_STATE_ULTRAHIGHSPEED)
 #define mmc_card_set_ext_capacity(c) ((c)->state |= MMC_CARD_SDXC)
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index dbafdfc..e1dbd21 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -61,6 +61,7 @@
 #define MMC_TIMING_UHS_SDR104	4
 #define MMC_TIMING_UHS_DDR50	5
 #define MMC_TIMING_MMC_HS200	6
+#define MMC_TIMING_MMC_HS400	7
 
 #define MMC_SDR_MODE		0
 #define MMC_1_2V_DDR_MODE	1
@@ -245,6 +246,7 @@
 						/* DDR mode at 1.8V */
 #define MMC_CAP_1_2V_DDR	(1 << 12)	/* can support */
 						/* DDR mode at 1.2V */
+#define MMC_CAP_HSDDR		(MMC_CAP_1_8V_DDR | MMC_CAP_1_2V_DDR)
 #define MMC_CAP_POWER_OFF_CARD	(1 << 13)	/* Can power off after boot */
 #define MMC_CAP_BUS_WIDTH_TEST	(1 << 14)	/* CMD14/CMD19 bus width ok */
 #define MMC_CAP_UHS_SDR12	(1 << 15)	/* Host supports UHS SDR12 mode */
@@ -295,6 +297,11 @@
 #define MMC_CAP2_CORE_RUNTIME_PM (1 << 19)
 /* Allows Asynchronous SDIO irq while card is in 4-bit mode */
 #define MMC_CAP2_ASYNC_SDIO_IRQ_4BIT_MODE (1 << 20)
+
+#define MMC_CAP2_HS400_1_8V	(1 << 21)        /* can support */
+#define MMC_CAP2_HS400_1_2V	(1 << 22)        /* can support */
+#define MMC_CAP2_HS400		(MMC_CAP2_HS400_1_8V | \
+				 MMC_CAP2_HS400_1_2V)
 	mmc_pm_flag_t		pm_caps;	/* supported pm features */
 
 	int			clk_requests;	/* internal reference counter */
diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h
index de145d6..764beec 100644
--- a/include/linux/mmc/mmc.h
+++ b/include/linux/mmc/mmc.h
@@ -52,6 +52,7 @@
 #define MMC_READ_MULTIPLE_BLOCK  18   /* adtc [31:0] data addr   R1  */
 #define MMC_SEND_TUNING_BLOCK    19   /* adtc                    R1  */
 #define MMC_SEND_TUNING_BLOCK_HS200	21	/* adtc R1  */
+#define MMC_SEND_TUNING_BLOCK_HS400	MMC_SEND_TUNING_BLOCK_HS200
 
   /* class 3 */
 #define MMC_WRITE_DAT_UNTIL_STOP 20   /* adtc [31:0] data addr   R1  */
@@ -328,6 +329,8 @@
 #define EXT_CSD_POWER_OFF_LONG_TIME	247	/* RO */
 #define EXT_CSD_GENERIC_CMD6_TIME	248	/* RO */
 #define EXT_CSD_CACHE_SIZE		249	/* RO, 4 bytes */
+#define EXT_CSD_PWR_CL_DDR_200_195	253	/* RO */
+#define EXT_CSD_PWR_CL_DDR_200_360	254	/* RO */
 #define EXT_CSD_TAG_UNIT_SIZE		498	/* RO */
 #define EXT_CSD_DATA_TAG_SUPPORT	499	/* RO */
 #define EXT_CSD_MAX_PACKED_WRITES	500	/* RO */
@@ -359,7 +362,7 @@
 
 #define EXT_CSD_CARD_TYPE_26	(1<<0)	/* Card can run at 26MHz */
 #define EXT_CSD_CARD_TYPE_52	(1<<1)	/* Card can run at 52MHz */
-#define EXT_CSD_CARD_TYPE_MASK	0x3F	/* Mask out reserved bits */
+#define EXT_CSD_CARD_TYPE_MASK	0xFF	/* Mask out reserved bits */
 #define EXT_CSD_CARD_TYPE_DDR_1_8V  (1<<2)   /* Card can run at 52MHz */
 					     /* DDR mode @1.8V or 3V I/O */
 #define EXT_CSD_CARD_TYPE_DDR_1_2V  (1<<3)   /* Card can run at 52MHz */
@@ -369,6 +372,14 @@
 #define EXT_CSD_CARD_TYPE_SDR_1_8V	(1<<4)	/* Card can run at 200MHz */
 #define EXT_CSD_CARD_TYPE_SDR_1_2V	(1<<5)	/* Card can run at 200MHz */
 						/* SDR mode @1.2V I/O */
+#define EXT_CSD_CARD_TYPE_HS200		(EXT_CSD_CARD_TYPE_SDR_1_8V  \
+					| EXT_CSD_CARD_TYPE_SDR_1_2V)
+#define EXT_CSD_CARD_TYPE_HS400_1_8V	(1<<6)	/* Card can run at 200MHz */
+							/* DDR mode @1.8V I/O */
+#define EXT_CSD_CARD_TYPE_HS400_1_2V	(1<<7)	/* Card can run at 200MHz */
+							/* DDR mode @1.2V I/O */
+#define EXT_CSD_CARD_TYPE_HS400		(EXT_CSD_CARD_TYPE_HS400_1_8V  \
+					| EXT_CSD_CARD_TYPE_HS400_1_2V)
 
 #define EXT_CSD_BUS_WIDTH_1	0	/* Card is in 1 bit mode */
 #define EXT_CSD_BUS_WIDTH_4	1	/* Card is in 4 bit mode */
diff --git a/include/linux/mmc/sdhci.h b/include/linux/mmc/sdhci.h
index 424b1d9..186fff1 100644
--- a/include/linux/mmc/sdhci.h
+++ b/include/linux/mmc/sdhci.h
@@ -190,6 +190,7 @@
 #define SDHCI_PV_ENABLED	(1<<8)	/* Preset value enabled */
 #define SDHCI_SDIO_IRQ_ENABLED	(1<<9)	/* SDIO irq enabled */
 #define SDHCI_HS200_NEEDS_TUNING (1<<10)	/* HS200 needs tuning */
+#define SDHCI_HS400_NEEDS_TUNING (1<<11)	/* HS400 needs tuning */
 
 	unsigned int version;	/* SDHCI spec. version */
 
diff --git a/include/linux/msm_mdp.h b/include/linux/msm_mdp.h
index fab9301..73b8014 100644
--- a/include/linux/msm_mdp.h
+++ b/include/linux/msm_mdp.h
@@ -126,6 +126,14 @@
 	MDP_BGR_888,      /* BGR 888 */
 	MDP_Y_CBCR_H2V2_VENUS,
 	MDP_BGRX_8888,   /* BGRX 8888 */
+	MDP_RGBA_8888_TILE,	/* RGBA 8888 in tile format */
+	MDP_ARGB_8888_TILE,	/* ARGB 8888 in tile format */
+	MDP_ABGR_8888_TILE,	/* ABGR 8888 in tile format */
+	MDP_BGRA_8888_TILE,	/* BGRA 8888 in tile format */
+	MDP_RGBX_8888_TILE,	/* RGBX 8888 in tile format */
+	MDP_XRGB_8888_TILE,	/* XRGB 8888 in tile format */
+	MDP_XBGR_8888_TILE,	/* XBGR 8888 in tile format */
+	MDP_BGRX_8888_TILE,	/* BGRX 8888 in tile format */
 	MDP_IMGTYPE_LIMIT,
 	MDP_RGB_BORDERFILL,	/* border fill pipe */
 	MDP_FB_FORMAT = MDP_IMGTYPE2_START,    /* framebuffer format */
diff --git a/include/linux/slimbus/slimbus.h b/include/linux/slimbus/slimbus.h
index 132135e..cba4394 100644
--- a/include/linux/slimbus/slimbus.h
+++ b/include/linux/slimbus/slimbus.h
@@ -581,6 +581,11 @@
  * @shutdown: Standard shutdown callback used during powerdown/halt.
  * @suspend: Standard suspend callback used during system suspend
  * @resume: Standard resume callback used during system resume
+ * @device_up: This callback is called when the device reports present and
+ *		gets a logical address assigned to it
+ * @device_down: This callback is called when device reports absent, or the
+ *		bus goes down. Device will report present when bus is up and
+ *		device_up callback will be called again when that happens
  * @driver: Slimbus device drivers should initialize name and owner field of
  *	this structure
  * @id_table: List of slimbus devices supported by this driver
@@ -593,6 +598,8 @@
 					pm_message_t pmesg);
 	int				(*resume)(struct slim_device *sldev);
 	int				(*device_up)(struct slim_device *sldev);
+	int				(*device_down)
+						(struct slim_device *sldev);
 
 	struct device_driver		driver;
 	const struct slim_device_id	*id_table;
@@ -1022,6 +1029,13 @@
 				u8 e_len, u8 *laddr, bool valid);
 
 /*
+ * slim_report_absent: Controller calls this function when a device
+ *	reports absent, OR when the device cannot be communicated with
+ * @sbdev: Device that cannot be reached, or that sent report absent
+ */
+void slim_report_absent(struct slim_device *sbdev);
+
+/*
  * slim_msg_response: Deliver Message response received from a device to the
  *	framework.
  * @ctrl: Controller handle
diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h
index 101325e..f6b93ff 100644
--- a/include/linux/videodev2.h
+++ b/include/linux/videodev2.h
@@ -1874,6 +1874,16 @@
 	V4L2_MPEG_VIDC_FRAME_ASSEMBLY_ENABLE	= 1,
 };
 
+#define V4L2_CID_MPEG_VIDC_VIDEO_VP8_PROFILE_LEVEL \
+		(V4L2_CID_MPEG_MSM_VIDC_BASE+32)
+enum v4l2_mpeg_vidc_video_vp8_profile_level {
+	V4L2_MPEG_VIDC_VIDEO_VP8_UNUSED,
+	V4L2_MPEG_VIDC_VIDEO_VP8_VERSION_0,
+	V4L2_MPEG_VIDC_VIDEO_VP8_VERSION_1,
+	V4L2_MPEG_VIDC_VIDEO_VP8_VERSION_2,
+	V4L2_MPEG_VIDC_VIDEO_VP8_VERSION_3,
+};
+
 /*  Camera class control IDs */
 #define V4L2_CID_CAMERA_CLASS_BASE 	(V4L2_CTRL_CLASS_CAMERA | 0x900)
 #define V4L2_CID_CAMERA_CLASS 		(V4L2_CTRL_CLASS_CAMERA | 1)
diff --git a/include/media/msm_cam_sensor.h b/include/media/msm_cam_sensor.h
index 15391d8..540fd2c 100644
--- a/include/media/msm_cam_sensor.h
+++ b/include/media/msm_cam_sensor.h
@@ -52,6 +52,7 @@
 	MSM_CAMERA_I2C_BYTE_ADDR = 1,
 	MSM_CAMERA_I2C_WORD_ADDR,
 	MSM_CAMERA_I2C_3B_ADDR,
+	MSM_CAMERA_I2C_ADDR_TYPE_MAX,
 };
 
 enum msm_camera_i2c_data_type {
@@ -62,6 +63,7 @@
 	MSM_CAMERA_I2C_SET_WORD_MASK,
 	MSM_CAMERA_I2C_UNSET_WORD_MASK,
 	MSM_CAMERA_I2C_SET_BYTE_WRITE_MASK_DATA,
+	MSM_CAMERA_I2C_DATA_TYPE_MAX,
 };
 
 enum msm_sensor_power_seq_type_t {
diff --git a/include/sound/core.h b/include/sound/core.h
index bc05668..5b9969e 100644
--- a/include/sound/core.h
+++ b/include/sound/core.h
@@ -134,6 +134,9 @@
 	wait_queue_head_t shutdown_sleep;
 	struct device *dev;		/* device assigned to this card */
 	struct device *card_dev;	/* cardX object for sysfs */
+	int offline;			/* if this sound card is offline */
+	unsigned long offline_change;
+	wait_queue_head_t offline_poll_wait;
 
 #ifdef CONFIG_PM
 	unsigned int power_state;	/* power state */
@@ -295,6 +298,8 @@
 int snd_component_add(struct snd_card *card, const char *component);
 int snd_card_file_add(struct snd_card *card, struct file *file);
 int snd_card_file_remove(struct snd_card *card, struct file *file);
+void snd_card_change_online_state(struct snd_card *card, int online);
+bool snd_card_is_online_state(struct snd_card *card);
 
 #define snd_card_set_dev(card, devptr) ((card)->dev = (devptr))
 
diff --git a/include/sound/soc.h b/include/sound/soc.h
index 7886e84..b1e536d 100644
--- a/include/sound/soc.h
+++ b/include/sound/soc.h
@@ -426,6 +426,9 @@
 	struct snd_ac97_bus_ops *ops, int num);
 void snd_soc_free_ac97_codec(struct snd_soc_codec *codec);
 
+void snd_soc_card_change_online_state(struct snd_soc_card *soc_card,
+				      int online);
+
 /*
  *Controls
  */
@@ -967,7 +970,6 @@
 	enum snd_soc_pcm_subclass pcm_subclass;
 	struct snd_pcm_ops ops;
 
-	unsigned int complete:1;
 	unsigned int dev_registered:1;
 
 	/* Dynamic PCM BE runtime data */
diff --git a/init/main.c b/init/main.c
index 737ab05..b2fc496 100644
--- a/init/main.c
+++ b/init/main.c
@@ -477,11 +477,6 @@
 	smp_setup_processor_id();
 	debug_objects_early_init();
 
-	/*
-	 * Set up the the initial canary ASAP:
-	 */
-	boot_init_stack_canary();
-
 	cgroup_init_early();
 
 	local_irq_disable();
@@ -496,6 +491,10 @@
 	page_address_init();
 	printk(KERN_NOTICE "%s", linux_banner);
 	setup_arch(&command_line);
+	/*
+	 * Set up the the initial canary ASAP:
+	 */
+	boot_init_stack_canary();
 	mm_init_owner(&init_mm, &init_task);
 	mm_init_cpumask(&init_mm);
 	setup_command_line(command_line);
diff --git a/kernel/sched/core.c b/kernel/sched/core.c
index d9c4b64..5256d44 100644
--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -5202,9 +5202,6 @@
 	 */
 	rq->stop = NULL;
 
-	/* Ensure any throttled groups are reachable by pick_next_task */
-	unthrottle_offline_cfs_rqs(rq);
-
 	for ( ; ; ) {
 		/*
 		 * There's this thread running, bail when that's the only
diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
index 5d6ab86..2e98983 100644
--- a/kernel/sched/fair.c
+++ b/kernel/sched/fair.c
@@ -2060,7 +2060,7 @@
 	hrtimer_cancel(&cfs_b->slack_timer);
 }
 
-void unthrottle_offline_cfs_rqs(struct rq *rq)
+static void unthrottle_offline_cfs_rqs(struct rq *rq)
 {
 	struct cfs_rq *cfs_rq;
 
@@ -2114,7 +2114,7 @@
 	return NULL;
 }
 static inline void destroy_cfs_bandwidth(struct cfs_bandwidth *cfs_b) {}
-void unthrottle_offline_cfs_rqs(struct rq *rq) {}
+static inline void unthrottle_offline_cfs_rqs(struct rq *rq) {}
 
 #endif /* CONFIG_CFS_BANDWIDTH */
 
@@ -5186,6 +5186,9 @@
 static void rq_offline_fair(struct rq *rq)
 {
 	update_sysctl();
+
+	/* Ensure any throttled groups are reachable by pick_next_task */
+	unthrottle_offline_cfs_rqs(rq);
 }
 
 #endif /* CONFIG_SMP */
diff --git a/kernel/sched/rt.c b/kernel/sched/rt.c
index 8f32475..be427c5 100644
--- a/kernel/sched/rt.c
+++ b/kernel/sched/rt.c
@@ -685,6 +685,7 @@
 		 * runtime - in which case borrowing doesn't make sense.
 		 */
 		rt_rq->rt_runtime = RUNTIME_INF;
+		rt_rq->rt_throttled = 0;
 		raw_spin_unlock(&rt_rq->rt_runtime_lock);
 		raw_spin_unlock(&rt_b->rt_runtime_lock);
 	}
diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h
index 5370bcb..55f6d9c 100644
--- a/kernel/sched/sched.h
+++ b/kernel/sched/sched.h
@@ -1155,7 +1155,6 @@
 
 extern void init_cfs_rq(struct cfs_rq *cfs_rq);
 extern void init_rt_rq(struct rt_rq *rt_rq, struct rq *rq);
-extern void unthrottle_offline_cfs_rqs(struct rq *rq);
 
 extern void account_cfs_bandwidth_used(int enabled, int was_enabled);
 
diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c
index 90430b7..1604bf2 100644
--- a/net/core/rtnetlink.c
+++ b/net/core/rtnetlink.c
@@ -494,8 +494,10 @@
 	}
 	if (ops->fill_info) {
 		data = nla_nest_start(skb, IFLA_INFO_DATA);
-		if (data == NULL)
+		if (data == NULL) {
+			err = -EMSGSIZE;
 			goto err_cancel_link;
+		}
 		err = ops->fill_info(skb, dev);
 		if (err < 0)
 			goto err_cancel_data;
@@ -1059,7 +1061,7 @@
 	rcu_read_lock();
 	cb->seq = net->dev_base_seq;
 
-	if (nlmsg_parse(cb->nlh, sizeof(struct rtgenmsg), tb, IFLA_MAX,
+	if (nlmsg_parse(cb->nlh, sizeof(struct ifinfomsg), tb, IFLA_MAX,
 			ifla_policy) >= 0) {
 
 		if (tb[IFLA_EXT_MASK])
@@ -1902,7 +1904,7 @@
 	u32 ext_filter_mask = 0;
 	u16 min_ifinfo_dump_size = 0;
 
-	if (nlmsg_parse(nlh, sizeof(struct rtgenmsg), tb, IFLA_MAX,
+	if (nlmsg_parse(nlh, sizeof(struct ifinfomsg), tb, IFLA_MAX,
 			ifla_policy) >= 0) {
 		if (tb[IFLA_EXT_MASK])
 			ext_filter_mask = nla_get_u32(tb[IFLA_EXT_MASK]);
diff --git a/net/core/sock.c b/net/core/sock.c
index b2e14c0..0f8402e 100644
--- a/net/core/sock.c
+++ b/net/core/sock.c
@@ -1600,6 +1600,11 @@
 	gfp_t gfp_mask;
 	long timeo;
 	int err;
+	int npages = (data_len + (PAGE_SIZE - 1)) >> PAGE_SHIFT;
+
+	err = -EMSGSIZE;
+	if (npages > MAX_SKB_FRAGS)
+		goto failure;
 
 	gfp_mask = sk->sk_allocation;
 	if (gfp_mask & __GFP_WAIT)
@@ -1618,14 +1623,12 @@
 		if (atomic_read(&sk->sk_wmem_alloc) < sk->sk_sndbuf) {
 			skb = alloc_skb(header_len, gfp_mask);
 			if (skb) {
-				int npages;
 				int i;
 
 				/* No pages, we're done... */
 				if (!data_len)
 					break;
 
-				npages = (data_len + (PAGE_SIZE - 1)) >> PAGE_SHIFT;
 				skb->truesize += data_len;
 				skb_shinfo(skb)->nr_frags = npages;
 				for (i = 0; i < npages; i++) {
diff --git a/net/wireless/db.txt b/net/wireless/db.txt
index a2fc3a0..c5861b8 100644
--- a/net/wireless/db.txt
+++ b/net/wireless/db.txt
@@ -1,17 +1,799 @@
-#
-# This file is a placeholder to prevent accidental build breakage if someone
-# enables CONFIG_CFG80211_INTERNAL_REGDB.  Almost no one actually needs to
-# enable that build option.
-#
-# You should be using CRDA instead.  It is even better if you use the CRDA
-# package provided by your distribution, since they will probably keep it
-# up-to-date on your behalf.
-#
-# If you _really_ intend to use CONFIG_CFG80211_INTERNAL_REGDB then you will
-# need to replace this file with one containing appropriately formatted
-# regulatory rules that cover the regulatory domains you will be using.  Your
-# best option is to extract the db.txt file from the wireless-regdb git
-# repository:
-#
-#   git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-regdb.git
-#
+# This is the world regulatory domain
+country 00:
+	(2402 - 2472 @ 40), (3, 20)
+	# Channel 12 - 13.
+	(2457 - 2482 @ 40), (3, 20), PASSIVE-SCAN, NO-IBSS
+	# Channel 14. Only JP enables this and for 802.11b only
+	(2474 - 2494 @ 20), (3, 20), PASSIVE-SCAN, NO-IBSS, NO-OFDM
+	# Channel 36 - 48
+	(5170 - 5250 @ 40), (3, 20), PASSIVE-SCAN, NO-IBSS
+	# NB: 5260 MHz - 5700 MHz requies DFS
+	# Channel 149 - 165
+	(5735 - 5835 @ 40), (3, 20), PASSIVE-SCAN, NO-IBSS
+
+
+country AD:
+	# 60 gHz band channels 1-4, ref: Etsi En 302 567
+	(57240 - 65880 @ 2160), (N/A, 40), NO-OUTDOOR
+
+country AE:
+	(2402 - 2482 @ 40), (N/A, 20)
+	(5170 - 5250 @ 40), (N/A, 20)
+	(5250 - 5330 @ 40), (N/A, 20), DFS
+	(5490 - 5710 @ 40), (N/A, 27), DFS
+
+country AL:
+	(2402 - 2482 @ 20), (N/A, 20)
+
+country AM:
+	(2402 - 2482 @ 40), (N/A, 20)
+	(5170 - 5250 @ 20), (N/A, 18)
+	(5250 - 5330 @ 20), (N/A, 18), DFS
+
+country AN:
+	(2402 - 2482 @ 40), (N/A, 20)
+	(5170 - 5250 @ 40), (N/A, 20)
+	(5250 - 5330 @ 40), (N/A, 20), DFS
+	(5490 - 5710 @ 40), (N/A, 27), DFS
+
+country AR:
+	(2402 - 2482 @ 40), (N/A, 20)
+	(5170 - 5250 @ 40), (3, 17)
+	(5250 - 5330 @ 40), (3, 20), DFS
+	(5490 - 5710 @ 40), (3, 20), DFS
+	(5735 - 5835 @ 40), (3, 30)
+
+country AT: DFS-ETSI
+	(2402 - 2482 @ 40), (N/A, 20)
+	(5170 - 5250 @ 40), (N/A, 20)
+	(5250 - 5330 @ 40), (N/A, 20), DFS
+	(5490 - 5710 @ 40), (N/A, 27), DFS
+	# 60 gHz band channels 1-4, ref: Etsi En 302 567
+	(57240 - 65880 @ 2160), (N/A, 40), NO-OUTDOOR
+
+country AU:
+	(2402 - 2482 @ 40), (N/A, 20)
+	(5170 - 5250 @ 40), (3, 23)
+	(5250 - 5330 @ 40), (3, 23), DFS
+	(5735 - 5835 @ 40), (3, 30)
+
+country AW:
+	(2402 - 2482 @ 40), (N/A, 20)
+	(5170 - 5250 @ 40), (N/A, 20)
+	(5250 - 5330 @ 40), (N/A, 20), DFS
+	(5490 - 5710 @ 40), (N/A, 27), DFS
+
+country AZ:
+	(2402 - 2482 @ 40), (N/A, 20)
+	(5170 - 5250 @ 40), (N/A, 18)
+	(5250 - 5330 @ 40), (N/A, 18), DFS
+
+country BA: DFS-ETSI
+	(2402 - 2482 @ 40), (N/A, 20)
+	(5170 - 5250 @ 40), (N/A, 20)
+	(5250 - 5330 @ 40), (N/A, 20), DFS
+	(5490 - 5710 @ 40), (N/A, 27), DFS
+	# 60 gHz band channels 1-4, ref: Etsi En 302 567
+	(57240 - 65880 @ 2160), (N/A, 40), NO-OUTDOOR
+
+country BB:
+	(2402 - 2482 @ 40), (N/A, 20)
+	(5170 - 5250 @ 40), (3, 23)
+	(5250 - 5330 @ 40), (3, 23), DFS
+	(5735 - 5835 @ 40), (3, 30)
+
+country BD:
+	(2402 - 2482 @ 40), (N/A, 20)
+
+country BE: DFS-ETSI
+	(2402 - 2482 @ 40), (N/A, 20)
+	(5170 - 5250 @ 40), (N/A, 20)
+	(5250 - 5330 @ 40), (N/A, 20), DFS
+	(5490 - 5710 @ 40), (N/A, 27), DFS
+	# 60 gHz band channels 1-4, ref: Etsi En 302 567
+	(57240 - 65880 @ 2160), (N/A, 40), NO-OUTDOOR
+
+country BG: DFS-ETSI
+	(2402 - 2482 @ 40), (N/A, 20)
+	(5170 - 5250 @ 40), (N/A, 23)
+	(5250 - 5290 @ 40), (N/A, 23), DFS
+	(5490 - 5710 @ 40), (N/A, 30), DFS
+	# 60 gHz band channels 1-4, ref: Etsi En 302 567
+	(57240 - 65880 @ 2160), (N/A, 40), NO-OUTDOOR
+
+country BH:
+	(2402 - 2482 @ 40), (N/A, 20)
+	(5170 - 5250 @ 20), (N/A, 20)
+	(5250 - 5330 @ 20), (N/A, 20), DFS
+	(5735 - 5835 @ 20), (N/A, 20)
+
+country BL:
+	(2402 - 2482 @ 40), (N/A, 20)
+	(5170 - 5250 @ 40), (N/A, 18)
+	(5250 - 5330 @ 40), (N/A, 18), DFS
+
+country BN:
+	(2402 - 2482 @ 40), (N/A, 20)
+	(5170 - 5250 @ 40), (N/A, 20)
+	(5250 - 5330 @ 40), (N/A, 20), DFS
+	(5735 - 5835 @ 40), (N/A, 30)
+
+country BO:
+	(2402 - 2482 @ 40), (N/A, 30)
+	(5735 - 5835 @ 40), (N/A, 30)
+
+country BR:
+	(2402 - 2482 @ 40), (N/A, 20)
+	(5170 - 5250 @ 40), (3, 17)
+	(5250 - 5330 @ 40), (3, 20), DFS
+	(5490 - 5710 @ 40), (3, 20), DFS
+	(5735 - 5835 @ 40), (3, 30)
+
+country BY:
+	(2402 - 2482 @ 40), (N/A, 20)
+	(5170 - 5250 @ 40), (N/A, 20)
+	(5250 - 5330 @ 40), (N/A, 20), DFS
+	(5490 - 5710 @ 40), (N/A, 27), DFS
+
+country BZ:
+	(2402 - 2482 @ 40), (N/A, 30)
+	(5735 - 5835 @ 40), (N/A, 30)
+
+country CA:
+	(2402 - 2472 @ 40), (3, 27)
+	(5170 - 5250 @ 40), (3, 17)
+	(5250 - 5330 @ 40), (3, 20), DFS
+	(5490 - 5710 @ 40), (3, 20), DFS
+	(5735 - 5835 @ 40), (3, 30)
+
+country CH: DFS-ETSI
+	(2402 - 2482 @ 40), (N/A, 20)
+	(5170 - 5250 @ 40), (N/A, 20)
+	(5250 - 5330 @ 40), (N/A, 20), DFS
+	(5490 - 5710 @ 40), (N/A, 27), DFS
+	# 60 gHz band channels 1-4, ref: Etsi En 302 567
+	(57240 - 65880 @ 2160), (N/A, 40), NO-OUTDOOR
+
+country CL:
+	(2402 - 2482 @ 40), (N/A, 20)
+	(5170 - 5250 @ 40), (N/A, 20)
+	(5250 - 5330 @ 40), (N/A, 20), DFS
+	(5735 - 5835 @ 40), (N/A, 20)
+
+country CN:
+	(2402 - 2482 @ 40), (N/A, 20)
+	(5735 - 5835 @ 40), (N/A, 30)
+	# 60 gHz band channels 1,4: 28dBm, channels 2,3: 44dBm
+	# ref: http://www.miit.gov.cn/n11293472/n11505629/n11506593/n11960250/n11960606/n11960700/n12330791.files/n12330790.pdf
+	(57240 - 59400 @ 2160), (N/A, 28)
+	(59400 - 63720 @ 2160), (N/A, 44)
+	(63720 - 65880 @ 2160), (N/A, 28)
+
+country CO:
+	(2402 - 2472 @ 40), (3, 27)
+	(5170 - 5250 @ 40), (3, 17)
+	(5250 - 5330 @ 40), (3, 23), DFS
+	(5735 - 5835 @ 40), (3, 30)
+
+country CR:
+	(2402 - 2482 @ 40), (N/A, 20)
+	(5170 - 5250 @ 20), (3, 17)
+	(5250 - 5330 @ 20), (3, 23), DFS
+	(5735 - 5835 @ 20), (3, 30)
+
+country CS:
+	(2402 - 2482 @ 40), (N/A, 20)
+	(5170 - 5250 @ 40), (N/A, 20)
+	(5250 - 5330 @ 40), (N/A, 20), DFS
+	(5490 - 5710 @ 40), (N/A, 27), DFS
+
+country CY: DFS-ETSI
+	(2402 - 2482 @ 40), (N/A, 20)
+	(5170 - 5250 @ 40), (N/A, 20)
+	(5250 - 5330 @ 40), (N/A, 20), DFS
+	(5490 - 5710 @ 40), (N/A, 27), DFS
+	# 60 gHz band channels 1-4, ref: Etsi En 302 567
+	(57240 - 65880 @ 2160), (N/A, 40), NO-OUTDOOR
+
+# Data from http://www.ctu.eu/164/download/VOR/VOR-12-08-2005-34.pdf
+# and http://www.ctu.eu/164/download/VOR/VOR-12-05-2007-6-AN.pdf
+# Power at 5250 - 5350 MHz and 5470 - 5725 MHz can be doubled if TPC is
+# implemented.
+country CZ: DFS-ETSI
+	(2400 - 2483.5 @ 40), (N/A, 100 mW)
+	(5150 - 5250 @ 40), (N/A, 200 mW), NO-OUTDOOR
+	(5250 - 5350 @ 40), (N/A, 100 mW), NO-OUTDOOR, DFS
+	(5470 - 5725 @ 40), (N/A, 500 mW), DFS
+	# 60 gHz band channels 1-4, ref: Etsi En 302 567
+	(57240 - 65880 @ 2160), (N/A, 40), NO-OUTDOOR
+
+# Data from "Frequenznutzungsplan" (as published in April 2008), downloaded from
+# http://www.bundesnetzagentur.de/cae/servlet/contentblob/38448/publicationFile/2659/Frequenznutzungsplan2008_Id17448pdf.pdf
+# For the 5GHz range also see
+# http://www.bundesnetzagentur.de/cae/servlet/contentblob/38216/publicationFile/6579/WLAN5GHzVfg7_2010_28042010pdf.pdf
+# The values have been reduced by a factor of 2 (3db) for non TPC devices
+# (in other words: devices with TPC can use twice the tx power of this table).
+# Note that the docs do not require TPC for 5150--5250; the reduction to
+# 100mW thus is not strictly required -- however the conservative 100mW
+# limit is used here as the non-interference with radar and satellite
+# apps relies on the attenuation by the building walls only in the
+# absence of DFS; the neighbour countries have 100mW limit here as well.
+
+country DE: DFS-ETSI
+	# entries 279004 and 280006
+	(2400 - 2483.5 @ 40), (N/A, 100 mW)
+	# entry 303005
+	(5150 - 5250 @ 40), (N/A, 100 mW), NO-OUTDOOR
+	# entries 304002 and 305002
+	(5250 - 5350 @ 40), (N/A, 100 mW), NO-OUTDOOR, DFS
+	# entries 308002, 309001 and 310003
+	(5470 - 5725 @ 40), (N/A, 500 mW), DFS
+	# 60 gHz band channels 1-4, ref: Etsi En 302 567
+	(57240 - 65880 @ 2160), (N/A, 40), NO-OUTDOOR
+
+country DK: DFS-ETSI
+	(2402 - 2482 @ 40), (N/A, 20)
+	(5170 - 5250 @ 40), (N/A, 20)
+	(5250 - 5330 @ 40), (N/A, 20), DFS
+	(5490 - 5710 @ 40), (N/A, 27), DFS
+	# 60 gHz band channels 1-4, ref: Etsi En 302 567
+	(57240 - 65880 @ 2160), (N/A, 40), NO-OUTDOOR
+
+country DO:
+	(2402 - 2472 @ 40), (3, 27)
+	(5170 - 5250 @ 40), (3, 17)
+	(5250 - 5330 @ 40), (3, 23), DFS
+	(5735 - 5835 @ 40), (3, 30)
+
+country DZ:
+	(2402 - 2482 @ 40), (N/A, 20)
+
+country EC:
+	(2402 - 2482 @ 40), (N/A, 20)
+	(5170 - 5250 @ 20), (3, 17)
+	(5250 - 5330 @ 20), (3, 23), DFS
+	(5735 - 5835 @ 20), (3, 30)
+
+country EE: DFS-ETSI
+	(2402 - 2482 @ 40), (N/A, 20)
+	(5170 - 5250 @ 40), (N/A, 20)
+	(5250 - 5330 @ 40), (N/A, 20), DFS
+	(5490 - 5710 @ 40), (N/A, 27), DFS
+	# 60 gHz band channels 1-4, ref: Etsi En 302 567
+	(57240 - 65880 @ 2160), (N/A, 40), NO-OUTDOOR
+
+country EG:
+	(2402 - 2482 @ 40), (N/A, 20)
+	(5170 - 5250 @ 20), (N/A, 20)
+	(5250 - 5330 @ 20), (N/A, 20), DFS
+
+country ES: DFS-ETSI
+	(2400 - 2483.5 @ 40), (N/A, 100 mW)
+	(5150 - 5250 @ 40), (N/A, 100 mW), NO-OUTDOOR
+	(5250 - 5350 @ 40), (N/A, 100 mW), NO-OUTDOOR, DFS
+	(5470 - 5725 @ 40), (N/A, 500 mW), DFS
+	# 60 gHz band channels 1-4, ref: Etsi En 302 567
+	(57240 - 65880 @ 2160), (N/A, 40), NO-OUTDOOR
+
+country FI: DFS-ETSI
+	(2402 - 2482 @ 40), (N/A, 20)
+	(5170 - 5250 @ 40), (N/A, 20)
+	(5250 - 5330 @ 40), (N/A, 20), DFS
+	(5490 - 5710 @ 40), (N/A, 27), DFS
+	# 60 gHz band channels 1-4, ref: Etsi En 302 567
+	(57240 - 65880 @ 2160), (N/A, 40), NO-OUTDOOR
+
+country FR: DFS-ETSI
+	(2402 - 2482 @ 40), (N/A, 20)
+	(5170 - 5250 @ 40), (N/A, 20)
+	(5250 - 5330 @ 40), (N/A, 20), DFS
+	(5490 - 5710 @ 40), (N/A, 27), DFS
+	# 60 gHz band channels 1-4, ref: Etsi En 302 567
+	(57240 - 65880 @ 2160), (N/A, 40), NO-OUTDOOR
+
+country GE:
+	(2402 - 2482 @ 40), (N/A, 20)
+	(5170 - 5250 @ 40), (N/A, 18)
+	(5250 - 5330 @ 40), (N/A, 18), DFS
+	# 60 gHz band channels 1-4, ref: Etsi En 302 567
+	(57240 - 65880 @ 2160), (N/A, 40), NO-OUTDOOR
+
+country GB: DFS-ETSI
+	(2402 - 2482 @ 40), (N/A, 20)
+	(5170 - 5250 @ 40), (N/A, 20)
+	(5250 - 5330 @ 40), (N/A, 20), DFS
+	(5490 - 5710 @ 40), (N/A, 27), DFS
+	# 60 gHz band channels 1-4, ref: Etsi En 302 567
+	(57240 - 65880 @ 2160), (N/A, 40), NO-OUTDOOR
+
+country GD:
+	(2402 - 2472 @ 40), (3, 27)
+	(5170 - 5250 @ 40), (3, 17)
+	(5250 - 5330 @ 40), (3, 20), DFS
+	(5490 - 5710 @ 40), (3, 20), DFS
+	(5735 - 5835 @ 40), (3, 30)
+
+country GR: DFS-ETSI
+	(2402 - 2482 @ 40), (N/A, 20)
+	(5170 - 5250 @ 40), (N/A, 20)
+	(5250 - 5330 @ 40), (N/A, 20), DFS
+	(5490 - 5710 @ 40), (N/A, 27), DFS
+	# 60 gHz band channels 1-4, ref: Etsi En 302 567
+	(57240 - 65880 @ 2160), (N/A, 40), NO-OUTDOOR
+
+country GL: DFS-ETSI
+	(2402 - 2482 @ 40), (N/A, 20)
+	(5170 - 5250 @ 20), (N/A, 20)
+	(5250 - 5330 @ 20), (N/A, 20), DFS
+	(5490 - 5710 @ 20), (N/A, 27), DFS
+
+country GT:
+	(2402 - 2472 @ 40), (3, 27)
+	(5170 - 5250 @ 40), (3, 17)
+	(5250 - 5330 @ 40), (3, 23), DFS
+	(5735 - 5835 @ 40), (3, 30)
+
+country GU:
+	(2402 - 2472 @ 40), (3, 27)
+	(5170 - 5250 @ 20), (3, 17)
+	(5250 - 5330 @ 20), (3, 23), DFS
+	(5735 - 5835 @ 20), (3, 30)
+
+country HN:
+	(2402 - 2482 @ 40), (N/A, 20)
+	(5170 - 5250 @ 40), (3, 17)
+	(5250 - 5330 @ 40), (3, 20), DFS
+	(5490 - 5710 @ 40), (3, 20), DFS
+	(5735 - 5835 @ 40), (3, 30)
+
+country HK:
+	(2402 - 2482 @ 40), (N/A, 20)
+	(5170 - 5250 @ 40), (3, 17)
+	(5250 - 5330 @ 40), (3, 20), DFS
+	(5490 - 5710 @ 40), (3, 20), DFS
+	(5735 - 5835 @ 40), (3, 30)
+
+country HR: DFS-ETSI
+	(2402 - 2482 @ 40), (N/A, 20)
+	(5170 - 5250 @ 40), (N/A, 20)
+	(5250 - 5330 @ 40), (N/A, 20), DFS
+	(5490 - 5710 @ 40), (N/A, 27), DFS
+	# 60 gHz band channels 1-4, ref: Etsi En 302 567
+	(57240 - 65880 @ 2160), (N/A, 40), NO-OUTDOOR
+
+country HT:
+	(2402 - 2482 @ 40), (N/A, 20)
+	(5170 - 5250 @ 40), (N/A, 20)
+	(5250 - 5330 @ 40), (N/A, 20), DFS
+	(5490 - 5710 @ 40), (N/A, 27), DFS
+
+country HU: DFS-ETSI
+	(2402 - 2482 @ 40), (N/A, 20)
+	(5170 - 5250 @ 40), (N/A, 20)
+	(5250 - 5330 @ 40), (N/A, 20), DFS
+	(5490 - 5710 @ 40), (N/A, 27), DFS
+	# 60 gHz band channels 1-4, ref: Etsi En 302 567
+	(57240 - 65880 @ 2160), (N/A, 40), NO-OUTDOOR
+
+country ID:
+	(2402 - 2482 @ 40), (N/A, 20)
+
+country IE: DFS-ETSI
+	(2402 - 2482 @ 40), (N/A, 20)
+	(5170 - 5250 @ 40), (N/A, 20)
+	(5250 - 5330 @ 40), (N/A, 20), DFS
+	(5490 - 5710 @ 40), (N/A, 27), DFS
+	# 60 gHz band channels 1-4, ref: Etsi En 302 567
+	(57240 - 65880 @ 2160), (N/A, 40), NO-OUTDOOR
+
+country IL:
+	(2402 - 2482 @ 40), (N/A, 20)
+	(5150 - 5250 @ 40), (N/A, 200 mW), NO-OUTDOOR
+	(5250 - 5350 @ 40), (N/A, 200 mW), NO-OUTDOOR, DFS
+
+country IN:
+	(2402 - 2482 @ 40), (N/A, 20)
+	(5170 - 5250 @ 40), (N/A, 20)
+	(5250 - 5330 @ 40), (N/A, 20), DFS
+	(5735 - 5835 @ 40), (N/A, 20)
+
+country IS: DFS-ETSI
+	(2402 - 2482 @ 40), (N/A, 20)
+	(5170 - 5250 @ 40), (N/A, 20)
+	(5250 - 5330 @ 40), (N/A, 20), DFS
+	(5490 - 5710 @ 40), (N/A, 27), DFS
+	# 60 gHz band channels 1-4, ref: Etsi En 302 567
+	(57240 - 65880 @ 2160), (N/A, 40), NO-OUTDOOR
+
+country IR:
+	(2402 - 2482 @ 40), (N/A, 20)
+	(5735 - 5835 @ 40), (N/A, 30)
+
+country IT: DFS-ETSI
+	(2402 - 2482 @ 40), (N/A, 20)
+	(5170 - 5250 @ 40), (N/A, 20)
+	(5250 - 5330 @ 40), (N/A, 20), DFS
+	(5490 - 5710 @ 40), (N/A, 27), DFS
+	# 60 gHz band channels 1-4, ref: Etsi En 302 567
+	(57240 - 65880 @ 2160), (N/A, 40), NO-OUTDOOR
+
+country JM:
+	(2402 - 2482 @ 40), (N/A, 20)
+	(5170 - 5250 @ 40), (3, 17)
+	(5250 - 5330 @ 40), (3, 20), DFS
+	(5490 - 5710 @ 40), (3, 20), DFS
+	(5735 - 5835 @ 40), (3, 30)
+
+country JP:
+	(2402 - 2482 @ 40), (N/A, 20)
+	(2474 - 2494 @ 20), (N/A, 20), NO-OFDM
+	(4910 - 4990 @ 40), (N/A, 23)
+	(5030 - 5090 @ 40), (N/A, 23)
+	(5170 - 5250 @ 40), (N/A, 20)
+	(5250 - 5330 @ 40), (N/A, 20), DFS
+	(5490 - 5710 @ 40), (N/A, 23), DFS
+
+country JO:
+	(2402 - 2482 @ 40), (N/A, 20)
+	(5170 - 5250 @ 40), (N/A, 18)
+
+country KE:
+	(2402 - 2482 @ 40), (N/A, 20)
+	(5735 - 5835 @ 40), (N/A, 30)
+
+country KH:
+	(2402 - 2482 @ 40), (N/A, 20)
+	(5170 - 5250 @ 40), (N/A, 20)
+	(5250 - 5330 @ 40), (N/A, 20), DFS
+	(5490 - 5710 @ 40), (N/A, 27), DFS
+
+country KP:
+	(2402 - 2482 @ 40), (N/A, 20)
+	(5170 - 5330 @ 40), (3, 20)
+	(5160 - 5250 @ 40), (3, 20), DFS
+	(5490 - 5630 @ 40), (3, 30), DFS
+	(5735 - 5815 @ 40), (3, 30)
+
+country KR:
+	(2402 - 2482 @ 20), (N/A, 20)
+	(5170 - 5250 @ 20), (3, 20)
+	(5250 - 5330 @ 20), (3, 20), DFS
+	(5490 - 5630 @ 20), (3, 30), DFS
+	(5735 - 5815 @ 20), (3, 30)
+
+country KW:
+	(2402 - 2482 @ 40), (N/A, 20)
+	(5170 - 5250 @ 40), (N/A, 20)
+	(5250 - 5330 @ 40), (N/A, 20), DFS
+
+country KZ:
+	(2402 - 2482 @ 40), (N/A, 20)
+
+country LB:
+	(2402 - 2482 @ 40), (N/A, 20)
+	(5735 - 5835 @ 40), (N/A, 30)
+
+country LI: DFS-ETSI
+	(2402 - 2482 @ 40), (N/A, 20)
+	(5170 - 5250 @ 40), (N/A, 20)
+	(5250 - 5330 @ 40), (N/A, 20), DFS
+	(5490 - 5710 @ 40), (N/A, 27), DFS
+
+country LK:
+	(2402 - 2482 @ 40), (N/A, 20)
+	(5170 - 5250 @ 20), (3, 17)
+	(5250 - 5330 @ 20), (3, 20), DFS
+	(5490 - 5710 @ 20), (3, 20), DFS
+	(5735 - 5835 @ 20), (3, 30)
+
+country LT: DFS-ETSI
+	(2402 - 2482 @ 40), (N/A, 20)
+	(5170 - 5250 @ 40), (N/A, 20)
+	(5250 - 5330 @ 40), (N/A, 20), DFS
+	(5490 - 5710 @ 40), (N/A, 27), DFS
+	# 60 gHz band channels 1-4, ref: Etsi En 302 567
+	(57240 - 65880 @ 2160), (N/A, 40), NO-OUTDOOR
+
+country LU: DFS-ETSI
+	(2402 - 2482 @ 40), (N/A, 20)
+	(5170 - 5250 @ 40), (N/A, 20)
+	(5250 - 5330 @ 40), (N/A, 20), DFS
+	(5490 - 5710 @ 40), (N/A, 27), DFS
+	# 60 gHz band channels 1-4, ref: Etsi En 302 567
+	(57240 - 65880 @ 2160), (N/A, 40), NO-OUTDOOR
+
+country LV: DFS-ETSI
+	(2402 - 2482 @ 40), (N/A, 20)
+	(5170 - 5250 @ 40), (N/A, 20)
+	(5250 - 5330 @ 40), (N/A, 20), DFS
+	(5490 - 5710 @ 40), (N/A, 27), DFS
+	# 60 gHz band channels 1-4, ref: Etsi En 302 567
+	(57240 - 65880 @ 2160), (N/A, 40), NO-OUTDOOR
+
+country MC: DFS-ETSI
+	(2402 - 2482 @ 40), (N/A, 20)
+	(5170 - 5250 @ 40), (N/A, 18)
+	(5250 - 5330 @ 40), (N/A, 18), DFS
+
+country MA:
+	(2402 - 2482 @ 40), (N/A, 20)
+
+country MO:
+	(2402 - 2482 @ 40), (N/A, 20)
+	(5170 - 5250 @ 40), (3, 23)
+	(5250 - 5330 @ 40), (3, 23), DFS
+	(5735 - 5835 @ 40), (3, 30)
+
+country MK: DFS-ETSI
+	(2402 - 2482 @ 40), (N/A, 20)
+	(5170 - 5250 @ 40), (N/A, 20)
+	(5250 - 5330 @ 40), (N/A, 20), DFS
+	(5490 - 5710 @ 40), (N/A, 27), DFS
+	# 60 gHz band channels 1-4, ref: Etsi En 302 567
+	(57240 - 65880 @ 2160), (N/A, 40), NO-OUTDOOR
+
+country MT: DFS-ETSI
+	(2402 - 2482 @ 40), (N/A, 20)
+	(5170 - 5250 @ 40), (N/A, 20)
+	(5250 - 5330 @ 40), (N/A, 20), DFS
+	(5490 - 5710 @ 40), (N/A, 27), DFS
+	# 60 gHz band channels 1-4, ref: Etsi En 302 567
+	(57240 - 65880 @ 2160), (N/A, 40), NO-OUTDOOR
+
+country MY:
+	(2402 - 2482 @ 40), (N/A, 20)
+	(5250 - 5330 @ 40), (N/A, 30), DFS
+	(5735 - 5835 @ 40), (N/A, 30)
+
+country MX:
+	(2402 - 2472 @ 40), (3, 27)
+	(5170 - 5250 @ 40), (3, 17)
+	(5250 - 5330 @ 40), (3, 23), DFS
+	(5735 - 5835 @ 40), (3, 30)
+
+country NL: DFS-ETSI
+	(2402 - 2482 @ 40), (N/A, 20)
+	(5170 - 5250 @ 40), (N/A, 20), NO-OUTDOOR
+	(5250 - 5330 @ 40), (N/A, 20), NO-OUTDOOR, DFS
+	(5490 - 5710 @ 40), (N/A, 27), DFS
+	# 60 gHz band channels 1-4, ref: Etsi En 302 567
+	(57240 - 65880 @ 2160), (N/A, 40), NO-OUTDOOR
+
+country NO: DFS-ETSI
+	(2402 - 2482 @ 40), (N/A, 20)
+	(5170 - 5250 @ 40), (N/A, 20)
+	(5250 - 5330 @ 40), (N/A, 20), DFS
+	(5490 - 5710 @ 40), (N/A, 27), DFS
+	# 60 gHz band channels 1-4, ref: Etsi En 302 567
+	(57240 - 65880 @ 2160), (N/A, 40), NO-OUTDOOR
+
+country NP:
+	(2402 - 2482 @ 40), (N/A, 20)
+	(5735 - 5835 @ 40), (N/A, 30)
+
+country NZ:
+	(2402 - 2482 @ 40), (N/A, 30)
+	(5170 - 5250 @ 20), (3, 23)
+	(5250 - 5330 @ 20), (3, 23), DFS
+	(5735 - 5835 @ 20), (3, 30)
+
+country OM:
+	(2402 - 2482 @ 40), (N/A, 20)
+	(5170 - 5250 @ 40), (3, 17)
+	(5250 - 5330 @ 40), (3, 20), DFS
+	(5490 - 5710 @ 40), (3, 20), DFS
+	(5735 - 5835 @ 40), (3, 30)
+
+country PA:
+	(2402 - 2472 @ 40), (3, 27)
+	(5170 - 5250 @ 40), (3, 17)
+	(5250 - 5330 @ 40), (3, 23), DFS
+	(5735 - 5835 @ 40), (3, 30)
+
+country PE:
+	(2402 - 2482 @ 40), (N/A, 20)
+	(5735 - 5835 @ 40), (N/A, 30)
+
+country PG:
+	(2402 - 2482 @ 40), (N/A, 20)
+	(5170 - 5250 @ 40), (3, 17)
+	(5250 - 5330 @ 40), (3, 23), DFS
+	(5735 - 5835 @ 40), (3, 30)
+
+country PH:
+	(2402 - 2482 @ 40), (N/A, 20)
+	(5735 - 5835 @ 40), (N/A, 30)
+
+country PK:
+	(2402 - 2482 @ 40), (N/A, 20)
+	(5735 - 5835 @ 40), (N/A, 30)
+
+country PL: DFS-ETSI
+	(2402 - 2482 @ 40), (N/A, 20)
+	(5170 - 5250 @ 40), (N/A, 20)
+	(5250 - 5330 @ 40), (N/A, 20), DFS
+	(5490 - 5710 @ 40), (N/A, 27), DFS
+	# 60 gHz band channels 1-4, ref: Etsi En 302 567
+	(57240 - 65880 @ 2160), (N/A, 40), NO-OUTDOOR
+
+country PT: DFS-ETSI
+	(2402 - 2482 @ 40), (N/A, 20)
+	(5170 - 5250 @ 40), (N/A, 20)
+	(5250 - 5330 @ 40), (N/A, 20), DFS
+	(5490 - 5710 @ 40), (N/A, 27), DFS
+	# 60 gHz band channels 1-4, ref: Etsi En 302 567
+	(57240 - 65880 @ 2160), (N/A, 40), NO-OUTDOOR
+
+country PR:
+	(2402 - 2472 @ 40), (3, 27)
+	(5170 - 5250 @ 40), (3, 17)
+	(5250 - 5330 @ 40), (3, 23), DFS
+	(5735 - 5835 @ 40), (3, 30)
+
+country QA:
+	(2402 - 2482 @ 40), (N/A, 20)
+	(5735 - 5835 @ 40), (N/A, 30)
+
+country RO: DFS-ETSI
+	(2402 - 2482 @ 40), (N/A, 20)
+	(5170 - 5250 @ 40), (N/A, 20)
+	(5250 - 5330 @ 40), (N/A, 20), DFS
+	(5490 - 5710 @ 40), (N/A, 27), DFS
+	# 60 gHz band channels 1-4, ref: Etsi En 302 567
+	(57240 - 65880 @ 2160), (N/A, 40), NO-OUTDOOR
+
+
+# Source:
+# http://www.ratel.rs/upload/documents/Plan_namene/Plan_namene-sl_glasnik.pdf
+country RS:
+	(2400 - 2483.5 @ 40), (N/A, 100 mW)
+	(5150 - 5350 @ 40), (N/A, 200 mW), NO-OUTDOOR
+	(5470 - 5725 @ 20), (3, 1000 mW), DFS
+	# 60 gHz band channels 1-4, ref: Etsi En 302 567
+	(57240 - 65880 @ 2160), (N/A, 40), NO-OUTDOOR
+
+country RU:
+	(2402 - 2482 @ 40), (N/A, 20)
+	(5735 - 5835 @ 20), (N/A, 30)
+
+country RW:
+	(2402 - 2482 @ 40), (N/A, 20)
+	(5735 - 5835 @ 40), (N/A, 30)
+
+country SA:
+	(2402 - 2482 @ 40), (N/A, 20)
+	(5170 - 5250 @ 20), (3, 23)
+	(5250 - 5330 @ 20), (3, 23), DFS
+	(5735 - 5835 @ 20), (3, 30)
+
+country SE: DFS-ETSI
+	(2402 - 2482 @ 40), (N/A, 20)
+	(5170 - 5250 @ 40), (N/A, 20)
+	(5250 - 5330 @ 40), (N/A, 20), DFS
+	(5490 - 5710 @ 40), (N/A, 27), DFS
+	# 60 gHz band channels 1-4, ref: Etsi En 302 567
+	(57240 - 65880 @ 2160), (N/A, 40), NO-OUTDOOR
+
+country SG:
+	(2402 - 2482 @ 40), (N/A, 20)
+	(5170 - 5250 @ 40), (N/A, 20)
+	(5250 - 5330 @ 40), (N/A, 20), DFS
+	(5735 - 5835 @ 40), (N/A, 20)
+
+country SI: DFS-ETSI
+	(2402 - 2482 @ 40), (N/A, 20)
+	(5170 - 5250 @ 40), (N/A, 20)
+	(5250 - 5330 @ 40), (N/A, 20), DFS
+	(5490 - 5710 @ 40), (N/A, 27), DFS
+	# 60 gHz band channels 1-4, ref: Etsi En 302 567
+	(57240 - 65880 @ 2160), (N/A, 40), NO-OUTDOOR
+
+country SK: DFS-ETSI
+	(2402 - 2482 @ 40), (N/A, 20)
+	(5170 - 5250 @ 40), (N/A, 20)
+	(5250 - 5330 @ 40), (N/A, 20), DFS
+	(5490 - 5710 @ 40), (N/A, 27), DFS
+	# 60 gHz band channels 1-4, ref: Etsi En 302 567
+	(57240 - 65880 @ 2160), (N/A, 40), NO-OUTDOOR
+
+country SV:
+	(2402 - 2482 @ 40), (N/A, 20)
+	(5170 - 5250 @ 20), (3, 17)
+	(5250 - 5330 @ 20), (3, 23), DFS
+	(5735 - 5835 @ 20), (3, 30)
+
+country SY:
+	(2402 - 2482 @ 40), (N/A, 20)
+
+country TW:
+	(2402 - 2472 @ 40), (3, 27)
+	(5270 - 5330 @ 40), (3, 17), DFS
+	(5735 - 5815 @ 40), (3, 30)
+
+country TH:
+	(2402 - 2482 @ 40), (N/A, 20)
+	(5170 - 5250 @ 40), (3, 17)
+	(5250 - 5330 @ 40), (3, 20), DFS
+	(5490 - 5710 @ 40), (3, 20), DFS
+	(5735 - 5835 @ 40), (3, 30)
+
+country TT:
+	(2402 - 2482 @ 40), (N/A, 20)
+	(5170 - 5250 @ 40), (3, 17)
+	(5250 - 5330 @ 40), (3, 20), DFS
+	(5490 - 5710 @ 40), (3, 20), DFS
+	(5735 - 5835 @ 40), (3, 30)
+
+country TN:
+	(2402 - 2482 @ 40), (N/A, 20)
+	(5170 - 5250 @ 20), (N/A, 20)
+	(5250 - 5330 @ 20), (N/A, 20), DFS
+
+country TR: DFS-ETSI
+	(2402 - 2482 @ 40), (N/A, 20)
+	(5170 - 5250 @ 20), (N/A, 20)
+	(5250 - 5330 @ 20), (N/A, 20), DFS
+	# 60 gHz band channels 1-4, ref: Etsi En 302 567
+	(57240 - 65880 @ 2160), (N/A, 40), NO-OUTDOOR
+ 
+# Source:
+# #914 / 06 Sep 2007: http://www.ucrf.gov.ua/uk/doc/nkrz/1196068874
+# #1174 / 23 Oct 2008: http://www.nkrz.gov.ua/uk/activities/ruling/1225269361
+# (appendix 8)
+# Listed 5GHz range is a lowest common denominator for all related
+# rules in the referenced laws. Such a range is used because of
+# disputable definitions there.
+country UA:
+	(2400 - 2483.5 @ 40), (N/A, 20), NO-OUTDOOR
+	(5150 - 5350 @ 40), (N/A, 20), NO-OUTDOOR
+	# 60 gHz band channels 1-4, ref: Etsi En 302 567
+	(57240 - 65880 @ 2160), (N/A, 40), NO-OUTDOOR
+
+country US: DFS-FCC
+	(2402 - 2472 @ 40), (3, 27)
+	(5170 - 5250 @ 40), (3, 17)
+	(5250 - 5330 @ 40), (3, 20), DFS
+	(5490 - 5600 @ 40), (3, 20), DFS
+	(5650 - 5710 @ 40), (3, 20), DFS
+	(5735 - 5835 @ 40), (3, 30)
+	# 60g band
+	# reference: http://cfr.regstoday.com/47cfr15.aspx#47_CFR_15p255
+	# channels 1,2,3, EIRP=40dBm(43dBm peak)
+	(57240 - 63720 @ 2160), (N/A, 40)
+
+country UY:
+	(2402 - 2482 @ 40), (N/A, 20)
+	(5170 - 5250 @ 40), (3, 17)
+	(5250 - 5330 @ 40), (3, 20), DFS
+	(5490 - 5710 @ 40), (3, 20), DFS
+	(5735 - 5835 @ 40), (3, 30)
+
+country UZ:
+	(2402 - 2472 @ 40), (3, 27)
+	(5170 - 5250 @ 40), (3, 17)
+	(5250 - 5330 @ 40), (3, 20), DFS
+	(5490 - 5710 @ 40), (3, 20), DFS
+	(5735 - 5835 @ 40), (3, 30)
+
+country VE:
+	(2402 - 2482 @ 40), (N/A, 20)
+	(5735 - 5815 @ 40), (N/A, 23)
+
+country VN:
+	(2402 - 2482 @ 40), (N/A, 20)
+	(5170 - 5250 @ 40), (N/A, 20)
+	(5250 - 5330 @ 40), (N/A, 20), DFS
+
+country YE:
+	(2402 - 2482 @ 40), (N/A, 20)
+
+country ZA:
+	(2402 - 2482 @ 40), (N/A, 20)
+	(5170 - 5250 @ 40), (3, 17)
+	(5250 - 5330 @ 40), (3, 20), DFS
+	(5490 - 5710 @ 40), (3, 20), DFS
+	(5735 - 5835 @ 40), (3, 30)
+
+country ZW:
+	(2402 - 2482 @ 40), (N/A, 20)
+
diff --git a/sound/core/init.c b/sound/core/init.c
index f300bd3..6559a40 100644
--- a/sound/core/init.c
+++ b/sound/core/init.c
@@ -55,6 +55,8 @@
 module_param_array(slots, charp, NULL, 0444);
 MODULE_PARM_DESC(slots, "Module names assigned to the slots.");
 
+#define SND_CARD_STATE_MAX_LEN 16
+
 /* return non-zero if the given index is reserved for the given
  * module via slots option
  */
@@ -104,10 +106,40 @@
 	snd_iprintf(buffer, "%s\n", entry->card->id);
 }
 
+static int snd_card_state_read(struct snd_info_entry *entry,
+			       void *file_private_data, struct file *file,
+			       char __user *buf, size_t count, loff_t pos)
+{
+	int len;
+	char buffer[SND_CARD_STATE_MAX_LEN];
+
+	/* make sure offline is updated prior to wake up */
+	rmb();
+	len = snprintf(buffer, sizeof(buffer), "%s\n",
+		       entry->card->offline ? "OFFLINE" : "ONLINE");
+	return simple_read_from_buffer(buf, count, &pos, buffer, len);
+}
+
+static unsigned int snd_card_state_poll(struct snd_info_entry *entry,
+					void *private_data, struct file *file,
+					poll_table *wait)
+{
+	poll_wait(file, &entry->card->offline_poll_wait, wait);
+	if (xchg(&entry->card->offline_change, 0))
+		return POLLIN | POLLPRI | POLLRDNORM;
+	else
+		return 0;
+}
+
+static struct snd_info_entry_ops snd_card_state_proc_ops = {
+	.read = snd_card_state_read,
+	.poll = snd_card_state_poll,
+};
+
 static inline int init_info_for_card(struct snd_card *card)
 {
 	int err;
-	struct snd_info_entry *entry;
+	struct snd_info_entry *entry, *entry_state;
 
 	if ((err = snd_info_card_register(card)) < 0) {
 		snd_printd("unable to create card info\n");
@@ -123,6 +155,24 @@
 		entry = NULL;
 	}
 	card->proc_id = entry;
+
+	entry_state = snd_info_create_card_entry(card, "state",
+						 card->proc_root);
+	if (entry_state == NULL) {
+		snd_printd("unable to create card entry state\n");
+		card->proc_id = NULL;
+		return err;
+	}
+	entry_state->size = SND_CARD_STATE_MAX_LEN;
+	entry_state->content = SNDRV_INFO_CONTENT_DATA;
+	entry_state->c.ops = &snd_card_state_proc_ops;
+	err = snd_info_register(entry_state);
+	if (err < 0) {
+		snd_printd("unable to register card entry state\n");
+		card->proc_id = NULL;
+		return err;
+	}
+
 	return 0;
 }
 #else /* !CONFIG_PROC_FS */
@@ -216,6 +266,7 @@
 	mutex_init(&card->power_lock);
 	init_waitqueue_head(&card->power_sleep);
 #endif
+	init_waitqueue_head(&card->offline_poll_wait);
 	/* the control interface cannot be accessed from the user space until */
 	/* snd_cards_bitmask and snd_cards are set with snd_card_register */
 	err = snd_ctl_create(card);
@@ -909,6 +960,35 @@
 
 EXPORT_SYMBOL(snd_card_file_remove);
 
+/**
+ * snd_card_change_online_state - mark card's online/offline state
+ * @card: Card to mark
+ * @online: whether online of offline
+ *
+ * Mutes the DAI DAC.
+ */
+void snd_card_change_online_state(struct snd_card *card, int online)
+{
+	snd_printd("snd card %s state change %d -> %d\n",
+		   card->shortname, !card->offline, online);
+	card->offline = !online;
+	/* make sure offline is updated prior to wake up */
+	wmb();
+	xchg(&card->offline_change, 1);
+	wake_up_interruptible(&card->offline_poll_wait);
+}
+EXPORT_SYMBOL(snd_card_change_online_state);
+
+/**
+ * snd_card_is_online_state - return true if card is online state
+ * @card: Card to query
+ */
+bool snd_card_is_online_state(struct snd_card *card)
+{
+	return !card->offline;
+}
+EXPORT_SYMBOL(snd_card_is_online_state);
+
 #ifdef CONFIG_PM
 /**
  *  snd_power_wait - wait until the power-state is changed.
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index c0469e3..56cd5e6 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -165,7 +165,7 @@
 obj-$(CONFIG_SND_SOC_CS8427)	+= snd-soc-cs8427.o
 obj-$(CONFIG_SND_SOC_WCD9320)	+= snd-soc-wcd9320.o
 obj-$(CONFIG_SND_SOC_WCD9306)	+= snd-soc-wcd9306.o wcd9xxx-resmgr.o wcd9xxx-mbhc.o
-obj-$(CONFIG_SND_SOC_MSM8X10_WCD)	+= snd-soc-msm8x10-wcd.o wcd9xxx-resmgr.o
+obj-$(CONFIG_SND_SOC_MSM8X10_WCD)	+= snd-soc-msm8x10-wcd.o wcd9xxx-resmgr.o wcd9xxx-mbhc.o
 obj-$(CONFIG_SND_SOC_WL1273)	+= snd-soc-wl1273.o
 obj-$(CONFIG_SND_SOC_WM1250_EV1) += snd-soc-wm1250-ev1.o
 obj-$(CONFIG_SND_SOC_WM2000)	+= snd-soc-wm2000.o
diff --git a/sound/soc/codecs/msm8x10-wcd.c b/sound/soc/codecs/msm8x10-wcd.c
index 66b0094..bd4f926 100644
--- a/sound/soc/codecs/msm8x10-wcd.c
+++ b/sound/soc/codecs/msm8x10-wcd.c
@@ -27,7 +27,6 @@
 #include <linux/i2c.h>
 #include <linux/of_gpio.h>
 #include <linux/regulator/consumer.h>
-#include <linux/mfd/wcd9xxx/core.h>
 #include <linux/mfd/wcd9xxx/pdata.h>
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
@@ -62,6 +61,7 @@
 #define MAX_MSM8X10_WCD_DEVICE	4
 #define CODEC_DT_MAX_PROP_SIZE	40
 #define MAX_ON_DEMAND_SUPPLY_NAME_LENGTH 64
+#define HELICON_MCLK_CLK_9P6MHZ				9600000
 
 enum {
 	MSM8X10_WCD_I2C_TOP_LEVEL = 0,
@@ -160,15 +160,14 @@
 	u32 adc_count;
 	u32 rx_bias_count;
 	s32 dmic_1_2_clk_cnt;
-	enum msm8x10_wcd_bandgap_type bandgap_type;
-	bool mclk_enabled;
-	bool clock_active;
-	bool config_mode_active;
-	bool mbhc_polling_active;
 	struct on_demand_supply on_demand_list[ON_DEMAND_SUPPLIES_MAX];
-	struct mutex codec_resource_lock;
 	/* resmgr module */
 	struct wcd9xxx_resmgr resmgr;
+	/* mbhc module */
+	struct wcd9xxx_mbhc mbhc;
+
+	struct delayed_work hs_detect_work;
+	struct wcd9xxx_mbhc_config *mbhc_cfg;
 };
 
 static unsigned short rx_digital_gain_reg[] = {
@@ -344,12 +343,21 @@
 	return msm8x10_wcd_i2c_write_device(reg, src, bytes);
 }
 
-static int msm8x10_wcd_reg_read(struct msm8x10_wcd *msm8x10_wcd,
-				u16 reg, unsigned int *val)
+static unsigned short msm8x10_wcd_mask_reg(unsigned short reg)
+{
+	if (reg >= 0x3C0 && reg <= 0x3DF)
+		reg = reg & 0x00FF;
+	return reg;
+}
+
+static int __msm8x10_wcd_reg_read(struct msm8x10_wcd *msm8x10_wcd,
+				unsigned short reg)
 {
 	int ret = -EINVAL;
 	u8 temp;
 
+	reg = msm8x10_wcd_mask_reg(reg);
+
 	/* check if use I2C interface for Helicon or AHB for Dino */
 	mutex_lock(&msm8x10_wcd->io_lock);
 	if (MSM8X10_WCD_IS_HELICON_REG(reg))
@@ -357,16 +365,62 @@
 	else if (MSM8X10_WCD_IS_DINO_REG(reg))
 		ret = msm8x10_wcd_abh_read_device(msm8x10_wcd, reg, 1, &temp);
 	mutex_unlock(&msm8x10_wcd->io_lock);
-	*val = temp;
+
+	if (ret < 0) {
+		dev_err(msm8x10_wcd->dev,
+				"%s: codec read failed for reg 0x%x\n",
+				__func__, reg);
+		return ret;
+	} else {
+		dev_dbg(msm8x10_wcd->dev, "Read 0x%02x from 0x%x\n",
+				temp, reg);
+	}
+
+	return temp;
+}
+
+int msm8x10_wcd_reg_read(struct wcd9xxx_core_resource *core_res,
+				unsigned short reg)
+{
+	struct msm8x10_wcd *msm8x10_wcd = core_res->parent;
+	return __msm8x10_wcd_reg_read(msm8x10_wcd, reg);
+}
+EXPORT_SYMBOL(msm8x10_wcd_reg_read);
+
+static int __msm8x10_wcd_bulk_read(struct msm8x10_wcd *msm8x10_wcd,
+		unsigned short reg, int count, u8 *buf)
+{
+	int ret = -EINVAL;
+	mutex_lock(&msm8x10_wcd->io_lock);
+	if (MSM8X10_WCD_IS_HELICON_REG(reg))
+		ret = msm8x10_wcd_i2c_read(reg, count, buf);
+	else if (MSM8X10_WCD_IS_DINO_REG(reg))
+		ret = msm8x10_wcd_abh_read_device(msm8x10_wcd, reg,
+						count, buf);
+	mutex_unlock(&msm8x10_wcd->io_lock);
+
+	if (ret < 0)
+		dev_err(msm8x10_wcd->dev,
+				"%s: codec bulk read failed\n", __func__);
 	return ret;
 }
 
+int msm8x10_wcd_bulk_read(struct wcd9xxx_core_resource *core_res,
+			unsigned short reg, int count, u8 *buf)
+{
+	struct msm8x10_wcd *msm8x10_wcd =
+				(struct msm8x10_wcd *) core_res->parent;
+	return __msm8x10_wcd_bulk_read(msm8x10_wcd, reg, count, buf);
+}
+EXPORT_SYMBOL(msm8x10_wcd_bulk_read);
 
-static int msm8x10_wcd_reg_write(struct msm8x10_wcd *msm8x10_wcd, u16  reg,
-				 u8 val)
+static int __msm8x10_wcd_reg_write(struct msm8x10_wcd *msm8x10_wcd,
+			unsigned short reg, u8 val)
 {
 	int ret = -EINVAL;
 
+	reg = msm8x10_wcd_mask_reg(reg);
+
 	/* check if use I2C interface for Helicon or AHB for Dino */
 	mutex_lock(&msm8x10_wcd->io_lock);
 	if (MSM8X10_WCD_IS_HELICON_REG(reg))
@@ -375,9 +429,26 @@
 		ret = msm8x10_wcd_abh_write_device(msm8x10_wcd, reg, &val, 1);
 	mutex_unlock(&msm8x10_wcd->io_lock);
 
+	if (ret < 0)
+		dev_err(msm8x10_wcd->dev,
+				"%s: codec write to reg 0x%x failed\n",
+				__func__, reg);
+	else
+		dev_dbg(msm8x10_wcd->dev,
+				"%s: Codec reg 0x%x written with value 0x%x\n",
+				__func__, reg, val);
+
 	return ret;
 }
 
+int msm8x10_wcd_reg_write(struct wcd9xxx_core_resource *core_res,
+	unsigned short reg, u8 val)
+{
+	struct msm8x10_wcd *msm8x10_wcd = core_res->parent;
+	return __msm8x10_wcd_reg_write(msm8x10_wcd, reg, val);
+}
+EXPORT_SYMBOL(msm8x10_wcd_reg_write);
+
 static bool msm8x10_wcd_is_digital_gain_register(unsigned int reg)
 {
 	bool rtn = false;
@@ -451,7 +522,7 @@
 				reg, ret);
 	}
 
-	return msm8x10_wcd_reg_write(codec->control_data, reg, (u8)value);
+	return __msm8x10_wcd_reg_write(codec->control_data, reg, (u8)value);
 }
 
 static unsigned int msm8x10_wcd_read(struct snd_soc_codec *codec,
@@ -477,7 +548,7 @@
 				reg, ret);
 	}
 
-	ret = msm8x10_wcd_reg_read(codec->control_data, reg, &val);
+	val = __msm8x10_wcd_reg_read(codec->control_data, reg);
 	return val;
 }
 
@@ -1531,7 +1602,6 @@
 			snd_soc_update_bits(codec, micb_int_reg, 0x10, 0x10);
 		else if (strnstr(w->name, internal3_text, 30))
 			snd_soc_update_bits(codec, micb_int_reg, 0x2, 0x2);
-		snd_soc_update_bits(codec, w->reg, 0x1, 0x0);
 		break;
 	case SND_SOC_DAPM_POST_PMU:
 		usleep_range(20000, 20100);
@@ -1544,7 +1614,6 @@
 		else if (strnstr(w->name, internal3_text, 30))
 			snd_soc_update_bits(codec, micb_int_reg, 0x2, 0x0);
 
-		snd_soc_update_bits(codec, w->reg, 0x1, 0x1);
 		break;
 	}
 	return 0;
@@ -1796,12 +1865,6 @@
 		/* Let MBHC module know PA turned off */
 		wcd9xxx_resmgr_notifier_call(&msm8x10_wcd->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.
-		 */
 		dev_dbg(codec->dev,
 			"%s: sleep 10 ms after %s PA disable.\n", __func__,
 			w->name);
@@ -1981,88 +2044,6 @@
 		substream->name, substream->stream);
 }
 
-static int msm8x10_wcd_codec_enable_clock_block(struct snd_soc_codec *codec,
-						int enable)
-{
-	if (enable) {
-		snd_soc_update_bits(codec, MSM8X10_WCD_A_CDC_CLK_MCLK_CTL,
-				    0x01, 0x01);
-		snd_soc_update_bits(codec, MSM8X10_WCD_A_CDC_CLK_PDM_CTL,
-				    0x03, 0x03);
-		snd_soc_update_bits(codec, MSM8X10_WCD_A_CDC_TOP_CLK_CTL,
-				    0x0f, 0x0d);
-	} else {
-		snd_soc_update_bits(codec, MSM8X10_WCD_A_CDC_TOP_CLK_CTL,
-				    0x0f, 0x00);
-		snd_soc_update_bits(codec, MSM8X10_WCD_A_CDC_CLK_PDM_CTL,
-				    0x03, 0x00);
-	}
-	return 0;
-}
-
-static void msm8x10_wcd_codec_enable_audio_mode_bandgap(struct snd_soc_codec
-							*codec)
-{
-	snd_soc_update_bits(codec, MSM8X10_WCD_A_BIAS_CENTRAL_BG_CTL, 0x80,
-		0x80);
-	snd_soc_update_bits(codec, MSM8X10_WCD_A_BIAS_CENTRAL_BG_CTL, 0x04,
-		0x04);
-	snd_soc_update_bits(codec, MSM8X10_WCD_A_BIAS_CENTRAL_BG_CTL, 0x01,
-		0x01);
-	usleep_range(1000, 1000);
-	snd_soc_update_bits(codec, MSM8X10_WCD_A_BIAS_CENTRAL_BG_CTL, 0x80,
-		0x00);
-}
-
-static void msm8x10_wcd_codec_enable_bandgap(struct snd_soc_codec *codec,
-	enum msm8x10_wcd_bandgap_type choice)
-{
-	struct msm8x10_wcd_priv *msm8x10_wcd = snd_soc_codec_get_drvdata(codec);
-
-	/* TODO lock resources accessed by audio streams and threaded
-	 * interrupt handlers
-	 */
-
-	dev_dbg(codec->dev, "%s, choice is %d, current is %d\n",
-		__func__, choice,
-		msm8x10_wcd->bandgap_type);
-
-	if (msm8x10_wcd->bandgap_type == choice)
-		return;
-
-	if ((msm8x10_wcd->bandgap_type == MSM8X10_WCD_BANDGAP_OFF) &&
-		(choice == MSM8X10_WCD_BANDGAP_AUDIO_MODE)) {
-		msm8x10_wcd_codec_enable_audio_mode_bandgap(codec);
-	} else if (choice == MSM8X10_WCD_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 */
-		snd_soc_update_bits(codec, MSM8X10_WCD_A_BIAS_CENTRAL_BG_CTL,
-				    0x2, 0x2);
-		snd_soc_update_bits(codec, MSM8X10_WCD_A_BIAS_CENTRAL_BG_CTL,
-				    0x80, 0x80);
-		snd_soc_update_bits(codec, MSM8X10_WCD_A_BIAS_CENTRAL_BG_CTL,
-				    0x4, 0x4);
-		snd_soc_update_bits(codec, MSM8X10_WCD_A_BIAS_CENTRAL_BG_CTL,
-				    0x01, 0x01);
-		usleep_range(1000, 1000);
-		snd_soc_update_bits(codec, MSM8X10_WCD_A_BIAS_CENTRAL_BG_CTL,
-				    0x80, 0x00);
-	} else if ((msm8x10_wcd->bandgap_type ==
-		    MSM8X10_WCD_BANDGAP_MBHC_MODE) &&
-		    (choice == MSM8X10_WCD_BANDGAP_AUDIO_MODE)) {
-		snd_soc_write(codec, MSM8X10_WCD_A_BIAS_CENTRAL_BG_CTL, 0x50);
-		usleep_range(100, 100);
-		msm8x10_wcd_codec_enable_audio_mode_bandgap(codec);
-	} else if (choice == MSM8X10_WCD_BANDGAP_OFF) {
-		snd_soc_write(codec, MSM8X10_WCD_A_BIAS_CENTRAL_BG_CTL, 0x50);
-	} else {
-		dev_err(codec->dev,
-			"%s: Error, Invalid bandgap settings\n", __func__);
-	}
-	msm8x10_wcd->bandgap_type = choice;
-}
-
 int msm8x10_wcd_mclk_enable(struct snd_soc_codec *codec,
 			    int mclk_enable, bool dapm)
 {
@@ -2070,28 +2051,21 @@
 
 	dev_dbg(codec->dev, "%s: mclk_enable = %u, dapm = %d\n",
 		__func__, mclk_enable, dapm);
-	if (dapm)
-		MSM8X10_WCD_ACQUIRE_LOCK(msm8x10_wcd->codec_resource_lock);
+
+	WCD9XXX_BG_CLK_LOCK(&msm8x10_wcd->resmgr);
+
 	if (mclk_enable) {
-		msm8x10_wcd->mclk_enabled = true;
-		msm8x10_wcd_codec_enable_bandgap(codec,
-					   MSM8X10_WCD_BANDGAP_AUDIO_MODE);
-		msm8x10_wcd_codec_enable_clock_block(codec, 1);
+		wcd9xxx_resmgr_get_bandgap(&msm8x10_wcd->resmgr,
+				WCD9XXX_BANDGAP_AUDIO_MODE);
+		wcd9xxx_resmgr_get_clk_block(&msm8x10_wcd->resmgr,
+				WCD9XXX_CLK_MCLK);
 	} else {
-		if (!msm8x10_wcd->mclk_enabled) {
-			if (dapm)
-				MSM8X10_WCD_RELEASE_LOCK(
-				  msm8x10_wcd->codec_resource_lock);
-			dev_err(codec->dev, "Error, MCLK already diabled\n");
-			return -EINVAL;
-		}
-		msm8x10_wcd->mclk_enabled = false;
-		msm8x10_wcd_codec_enable_clock_block(codec, 0);
-		msm8x10_wcd_codec_enable_bandgap(codec,
-						   MSM8X10_WCD_BANDGAP_OFF);
+		wcd9xxx_resmgr_put_clk_block(&msm8x10_wcd->resmgr,
+					WCD9XXX_CLK_MCLK);
+		wcd9xxx_resmgr_put_bandgap(&msm8x10_wcd->resmgr,
+					WCD9XXX_BANDGAP_AUDIO_MODE);
 	}
-	if (dapm)
-		MSM8X10_WCD_RELEASE_LOCK(msm8x10_wcd->codec_resource_lock);
+	WCD9XXX_BG_CLK_UNLOCK(&msm8x10_wcd->resmgr);
 	return 0;
 }
 
@@ -2513,7 +2487,7 @@
 	MSM8X10_WCD_REG_VAL(MSM8X10_WCD_A_BIAS_CURR_CTL_2, 0x04),
 	MSM8X10_WCD_REG_VAL(MSM8X10_WCD_A_MICB_CFILT_1_VAL, 0x60),
 	/* Enable pulldown to reduce leakage */
-	MSM8X10_WCD_REG_VAL(MSM8X10_WCD_A_MICB_1_CTL, 0x83),
+	MSM8X10_WCD_REG_VAL(MSM8X10_WCD_A_MICB_1_CTL, 0x82),
 	MSM8X10_WCD_REG_VAL(MSM8X10_WCD_A_TX_COM_BIAS, 0xE0),
 	MSM8X10_WCD_REG_VAL(MSM8X10_WCD_A_TX_1_EN, 0x32),
 	MSM8X10_WCD_REG_VAL(MSM8X10_WCD_A_TX_2_EN, 0x32),
@@ -2527,6 +2501,10 @@
 
 	/* Always set TXD_CLK_EN bit to reduce the leakage */
 	MSM8X10_WCD_REG_VAL(MSM8X10_WCD_A_CDC_DIG_CLK_CTL, 0x10),
+
+	/* Always disable clock gating for MCLK to mbhc clock gate */
+	MSM8X10_WCD_REG_VAL(MSM8X10_WCD_A_CDC_ANA_CLK_CTL, 0x20),
+	MSM8X10_WCD_REG_VAL(MSM8X10_WCD_A_CDC_DIG_CLK_CTL, 0x10),
 };
 
 static void msm8x10_wcd_update_reg_defaults(struct snd_soc_codec *codec)
@@ -2577,9 +2555,118 @@
 				    msm8x10_wcd_codec_reg_init_val[i].val);
 }
 
-int msm8x10_wcd_hs_detect(struct snd_soc_codec *codec,
-		    struct msm8x10_wcd_mbhc_config *mbhc_cfg)
+static void msm8x10_wcd_enable_mux_bias_block(
+		struct snd_soc_codec *codec)
 {
+	snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_1,
+				0x80, 0x00);
+}
+
+static void msm8x10_wcd_put_cfilt_fast_mode(
+	struct snd_soc_codec *codec,
+	struct wcd9xxx_mbhc *mbhc)
+{
+	snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.cfilt_ctl,
+			0x30, 0x30);
+}
+
+static void msm8x10_wcd_codec_specific_cal_setup(
+	struct snd_soc_codec *codec,
+	struct wcd9xxx_mbhc *mbhc)
+{
+	snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL,
+			0x04, 0x04);
+	snd_soc_update_bits(codec, WCD9XXX_A_TX_7_MBHC_EN,
+			0xE0, 0xE0);
+}
+
+static int msm8x10_wcd_get_jack_detect_irq(
+		struct snd_soc_codec *codec)
+{
+	return MSM8X10_WCD_IRQ_MBHC_HS_DET;
+}
+
+static struct wcd9xxx_cfilt_mode msm8x10_wcd_switch_cfilt_mode(
+	struct wcd9xxx_mbhc *mbhc, bool fast)
+{
+	struct snd_soc_codec *codec = mbhc->codec;
+	struct wcd9xxx_cfilt_mode cfilt_mode;
+
+	if (fast)
+		cfilt_mode.reg_mode_val = WCD9XXX_CFILT_EXT_PRCHG_EN;
+	else
+		cfilt_mode.reg_mode_val = WCD9XXX_CFILT_EXT_PRCHG_DSBL;
+
+	cfilt_mode.cur_mode_val =
+		snd_soc_read(codec, mbhc->mbhc_bias_regs.cfilt_ctl) & 0x30;
+	cfilt_mode.reg_mask = 0x30;
+	return cfilt_mode;
+}
+
+static void msm8x10_wcd_select_cfilt(struct snd_soc_codec *codec,
+	struct wcd9xxx_mbhc *mbhc)
+{
+	snd_soc_update_bits(codec,
+			mbhc->mbhc_bias_regs.ctl_reg, 0x60, 0x00);
+}
+
+static void msm8x10_wcd_free_irq(struct wcd9xxx_mbhc *mbhc)
+{
+	struct msm8x10_wcd *msm8x10_wcd = mbhc->codec->control_data;
+	struct wcd9xxx_core_resource *core_res =
+			&msm8x10_wcd->wcd9xxx_res;
+	wcd9xxx_free_irq(core_res, MSM8X10_WCD_IRQ_MBHC_HS_DET, mbhc);
+}
+
+enum wcd9xxx_cdc_type msm8x10_wcd_get_cdc_type(void)
+{
+	return WCD9XXX_CDC_TYPE_HELICON;
+}
+
+static void msm8x10_wcd_mbhc_clk_gate(struct snd_soc_codec *codec,
+		bool on)
+{
+	snd_soc_update_bits(codec, MSM8X10_WCD_A_CDC_TOP_CLK_CTL, 0x10, 0x10);
+}
+
+static const struct wcd9xxx_mbhc_cb mbhc_cb = {
+	.enable_mux_bias_block = msm8x10_wcd_enable_mux_bias_block,
+	.cfilt_fast_mode = msm8x10_wcd_put_cfilt_fast_mode,
+	.codec_specific_cal = msm8x10_wcd_codec_specific_cal_setup,
+	.jack_detect_irq = msm8x10_wcd_get_jack_detect_irq,
+	.switch_cfilt_mode = msm8x10_wcd_switch_cfilt_mode,
+	.select_cfilt = msm8x10_wcd_select_cfilt,
+	.free_irq = msm8x10_wcd_free_irq,
+	.get_cdc_type = msm8x10_wcd_get_cdc_type,
+	.enable_clock_gate = msm8x10_wcd_mbhc_clk_gate,
+};
+
+static void delayed_hs_detect_fn(struct work_struct *work)
+{
+	struct delayed_work *delayed_work;
+	struct msm8x10_wcd_priv *wcd_priv;
+
+	delayed_work = to_delayed_work(work);
+	wcd_priv = container_of(delayed_work, struct msm8x10_wcd_priv,
+				hs_detect_work);
+
+	if (!wcd_priv) {
+		pr_err("%s: Invalid private data for codec\n", __func__);
+		return;
+	}
+
+	wcd9xxx_mbhc_start(&wcd_priv->mbhc, wcd_priv->mbhc_cfg);
+}
+
+
+int msm8x10_wcd_hs_detect(struct snd_soc_codec *codec,
+		    struct wcd9xxx_mbhc_config *mbhc_cfg)
+{
+	struct msm8x10_wcd_priv *wcd = snd_soc_codec_get_drvdata(codec);
+
+	wcd->mbhc_cfg = mbhc_cfg;
+	schedule_delayed_work(&wcd->hs_detect_work,
+			msecs_to_jiffies(5000));
 	return 0;
 }
 EXPORT_SYMBOL_GPL(msm8x10_wcd_hs_detect);
@@ -2659,10 +2746,14 @@
 {
 	struct msm8x10_wcd_priv *msm8x10_wcd_priv;
 	struct msm8x10_wcd *msm8x10_wcd;
-	int i;
+	struct wcd9xxx_core_resource *core_res;
+	int i, ret = 0;
+
 	dev_dbg(codec->dev, "%s()\n", __func__);
 
-	msm8x10_wcd_priv = kzalloc(sizeof(struct msm8x10_wcd_priv), GFP_KERNEL);
+	msm8x10_wcd_priv = devm_kzalloc(codec->dev,
+			sizeof(struct msm8x10_wcd_priv), GFP_KERNEL);
+
 	if (!msm8x10_wcd_priv) {
 		dev_err(codec->dev, "Failed to allocate private data\n");
 		return -ENOMEM;
@@ -2683,6 +2774,21 @@
 	msm8x10_wcd = codec->control_data;
 	msm8x10_wcd->pdino_base = ioremap(MSM8X10_DINO_CODEC_BASE_ADDR,
 					  MSM8X10_DINO_CODEC_REG_SIZE);
+	INIT_DELAYED_WORK(&msm8x10_wcd_priv->hs_detect_work,
+			delayed_hs_detect_fn);
+
+	/* codec resmgr module init */
+	msm8x10_wcd = codec->control_data;
+	core_res = &msm8x10_wcd->wcd9xxx_res;
+	ret = wcd9xxx_resmgr_init(&msm8x10_wcd_priv->resmgr,
+				codec, core_res, NULL, NULL,
+				WCD9XXX_CDC_TYPE_HELICON);
+	if (ret) {
+		dev_err(codec->dev,
+				"%s: wcd9xxx init failed %d\n",
+				__func__, ret);
+		goto exit_probe;
+	}
 
 	msm8x10_wcd_bringup(codec);
 	msm8x10_wcd_codec_init_reg(codec);
@@ -2697,12 +2803,15 @@
 				codec->control_data,
 				on_demand_supply_name[ON_DEMAND_MICBIAS]);
 	atomic_set(&msm8x10_wcd_priv->on_demand_list[ON_DEMAND_MICBIAS].ref, 0);
-	msm8x10_wcd_priv->mclk_enabled = false;
-	msm8x10_wcd_priv->bandgap_type = MSM8X10_WCD_BANDGAP_OFF;
-	msm8x10_wcd_priv->clock_active = false;
-	msm8x10_wcd_priv->config_mode_active = false;
-	msm8x10_wcd_priv->mbhc_polling_active = false;
-	mutex_init(&msm8x10_wcd_priv->codec_resource_lock);
+
+	ret = wcd9xxx_mbhc_init(&msm8x10_wcd_priv->mbhc,
+				&msm8x10_wcd_priv->resmgr,
+				codec, NULL, &mbhc_cb,
+				HELICON_MCLK_CLK_9P6MHZ, false);
+	if (ret) {
+		pr_err("%s: Failed to initialize mbhc\n", __func__);
+		goto exit_probe;
+	}
 
 	registered_codec = codec;
 	adsp_state_notifier =
@@ -2715,13 +2824,16 @@
 		return -ENOMEM;
 	}
 	return 0;
+
+exit_probe:
+	return ret;
+
 }
 
 static int msm8x10_wcd_codec_remove(struct snd_soc_codec *codec)
 {
 	struct msm8x10_wcd_priv *pwcd_priv = snd_soc_codec_get_drvdata(codec);
 	struct msm8x10_wcd *msm8x10_wcd = pwcd_priv->codec->control_data;
-
 	pwcd_priv->on_demand_list[ON_DEMAND_CP].supply = NULL;
 	atomic_set(&pwcd_priv->on_demand_list[ON_DEMAND_CP].ref, 0);
 	pwcd_priv->on_demand_list[ON_DEMAND_MICBIAS].supply = NULL;
@@ -2920,13 +3032,38 @@
 {
 	mutex_init(&msm8x10->io_lock);
 	mutex_init(&msm8x10->xfer_lock);
-	mutex_init(&msm8x10->pm_lock);
-	msm8x10->wlock_holders = 0;
 	msm8x10_wcd_pads_config();
 	msm8x10_wcd_clk_init();
 	return 0;
 }
 
+static struct intr_data interrupt_table[] = {
+	{MSM8X10_WCD_IRQ_MBHC_INSERTION, true},
+	{MSM8X10_WCD_IRQ_MBHC_POTENTIAL, true},
+	{MSM8X10_WCD_IRQ_MBHC_RELEASE, true},
+	{MSM8X10_WCD_IRQ_MBHC_PRESS, true},
+	{MSM8X10_WCD_IRQ_MBHC_SHORT_TERM, true},
+	{MSM8X10_WCD_IRQ_MBHC_REMOVAL, true},
+	{MSM8X10_WCD_IRQ_MBHC_HS_DET, true},
+	{MSM8X10_WCD_IRQ_RESERVED_0, false},
+	{MSM8X10_WCD_IRQ_PA_STARTUP, false},
+	{MSM8X10_WCD_IRQ_BG_PRECHARGE, false},
+	{MSM8X10_WCD_IRQ_RESERVED_1, false},
+	{MSM8X10_WCD_IRQ_EAR_PA_OCPL_FAULT, false},
+	{MSM8X10_WCD_IRQ_EAR_PA_STARTUP, false},
+	{MSM8X10_WCD_IRQ_SPKR_PA_OCPL_FAULT, false},
+	{MSM8X10_WCD_IRQ_SPKR_CLIP_FAULT, false},
+	{MSM8X10_WCD_IRQ_RESERVED_2, false},
+	{MSM8X10_WCD_IRQ_HPH_L_PA_STARTUP, false},
+	{MSM8X10_WCD_IRQ_HPH_R_PA_STARTUP, false},
+	{MSM8X10_WCD_IRQ_HPH_PA_OCPL_FAULT, false},
+	{MSM8X10_WCD_IRQ_HPH_PA_OCPR_FAULT, false},
+	{MSM8X10_WCD_IRQ_RESERVED_3, false},
+	{MSM8X10_WCD_IRQ_RESERVED_4, false},
+	{MSM8X10_WCD_IRQ_RESERVED_5, false},
+	{MSM8X10_WCD_IRQ_RESERVED_6, false},
+};
+
 static int __devinit msm8x10_wcd_i2c_probe(struct i2c_client *client,
 			const struct i2c_device_id *id)
 {
@@ -2936,6 +3073,7 @@
 	static int device_id;
 	struct device *dev;
 	enum apr_subsys_state q6_state;
+	struct wcd9xxx_core_resource *core_res;
 
 	dev_dbg(&client->dev, "%s(%d):slave addr = 0x%x device_id = %d\n",
 		__func__, __LINE__,  client->addr, device_id);
@@ -2995,8 +3133,8 @@
 	}
 
 	msm8x10->dev = &client->dev;
-	msm8x10->read_dev = msm8x10_wcd_reg_read;
-	msm8x10->write_dev = msm8x10_wcd_reg_write;
+	msm8x10->read_dev = __msm8x10_wcd_reg_read;
+	msm8x10->write_dev = __msm8x10_wcd_reg_write;
 	ret = msm8x10_wcd_init_supplies(msm8x10, pdata);
 	if (ret) {
 		dev_err(&client->dev, "%s: Fail to enable Codec supplies\n",
@@ -3020,15 +3158,37 @@
 		goto err_supplies;
 	}
 	dev_set_drvdata(&client->dev, msm8x10);
+	core_res = &msm8x10->wcd9xxx_res;
+	core_res->parent = msm8x10;
+	core_res->dev = msm8x10->dev;
+	core_res->intr_table = interrupt_table;
+	core_res->intr_table_size = ARRAY_SIZE(interrupt_table);
+
+	wcd9xxx_core_res_init(core_res,
+					MSM8X10_WCD_NUM_IRQS,
+					MSM8X10_WCD_NUM_IRQ_REGS,
+					msm8x10_wcd_reg_read,
+					msm8x10_wcd_reg_write,
+					msm8x10_wcd_bulk_read);
+	if (wcd9xxx_core_irq_init(core_res)) {
+		dev_err(msm8x10->dev,
+				"%s: irq initialization failed\n", __func__);
+	} else {
+		dev_info(msm8x10->dev,
+				"%s: irq initialization passed\n", __func__);
+	}
+
 	ret = snd_soc_register_codec(&client->dev, &soc_codec_dev_msm8x10_wcd,
 				     msm8x10_wcd_i2s_dai,
 				     ARRAY_SIZE(msm8x10_wcd_i2s_dai));
-	if (ret)
+	if (ret) {
 		dev_err(&client->dev,
 			"%s:snd_soc_register_codec failed with error %d\n",
 			__func__, ret);
-	else
+	} else {
+		wcd9xxx_set_intf_type(WCD9XXX_INTERFACE_TYPE_I2C);
 		goto rtn;
+	}
 
 err_supplies:
 	msm8x10_wcd_disable_supplies(msm8x10, pdata);
@@ -3040,7 +3200,6 @@
 
 static void msm8x10_wcd_device_exit(struct msm8x10_wcd *msm8x10)
 {
-	mutex_destroy(&msm8x10->pm_lock);
 	mutex_destroy(&msm8x10->io_lock);
 	mutex_destroy(&msm8x10->xfer_lock);
 	kfree(msm8x10);
@@ -3084,6 +3243,7 @@
 	int ret;
 
 	pr_debug("%s:\n", __func__);
+	wcd9xxx_set_intf_type(WCD9XXX_INTERFACE_TYPE_PROBING);
 	ret = i2c_add_driver(&msm8x10_wcd_i2c_driver);
 	if (ret != 0)
 		pr_err("%s: Failed to add msm8x10 wcd I2C driver - error %d\n",
diff --git a/sound/soc/codecs/msm8x10-wcd.h b/sound/soc/codecs/msm8x10-wcd.h
index 44cdab9..5f67cba 100644
--- a/sound/soc/codecs/msm8x10-wcd.h
+++ b/sound/soc/codecs/msm8x10-wcd.h
@@ -168,31 +168,6 @@
 	MSM8X10_WCD_MICBIAS1 = 0,
 };
 
-struct msm8x10_wcd_mbhc_config {
-	struct snd_soc_jack *headset_jack;
-	struct snd_soc_jack *button_jack;
-	bool read_fw_bin;
-	/*
-	 * void* calibration contains:
-	 *  struct msm8x10_wcd_mbhc_general_cfg generic;
-	 *  struct msm8x10_wcd_mbhc_plug_detect_cfg plug_det;
-	 *  struct msm8x10_wcd_mbhc_plug_type_cfg plug_type;
-	 *  struct msm8x10_wcd_mbhc_btn_detect_cfg btn_det;
-	 *  struct msm8x10_wcd_mbhc_imped_detect_cfg imped_det;
-	 * Note: various size depends on btn_det->num_btn
-	 */
-	void *calibration;
-	enum msm8x10_wcd_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 detect_extn_cable;
-	/* swap_gnd_mic returns true if extern GND/MIC swap switch toggled */
-	bool (*swap_gnd_mic) (struct snd_soc_codec *);
-};
-
 enum msm8x10_wcd_pm_state {
 	MSM8X10_WCD_PM_SLEEPABLE,
 	MSM8X10_WCD_PM_AWAKE,
@@ -203,40 +178,29 @@
 	struct device *dev;
 	struct mutex io_lock;
 	struct mutex xfer_lock;
-	struct mutex irq_lock;
 	u8 version;
 
 	int reset_gpio;
 	int (*read_dev)(struct msm8x10_wcd *msm8x10,
-			unsigned short reg, unsigned int *val);
+			unsigned short reg);
 	int (*write_dev)(struct msm8x10_wcd *msm8x10,
 			 unsigned short reg, u8 val);
 
 	u32 num_of_supplies;
 	struct regulator_bulk_data *supplies;
 
-	enum msm8x10_wcd_pm_state pm_state;
-	struct mutex pm_lock;
-	/* pm_wq notifies change of pm_state */
-	wait_queue_head_t pm_wq;
-	struct pm_qos_request pm_qos_req;
-	int wlock_holders;
-
 	u8 idbyte[4];
 
-	unsigned int irq_base;
-	unsigned int irq;
-	u8 irq_masks_cur[MSM8X10_WCD_NUM_IRQ_REGS];
-	u8 irq_masks_cache[MSM8X10_WCD_NUM_IRQ_REGS];
-	bool irq_level_high[MSM8X10_WCD_NUM_IRQS];
 	int num_irqs;
 	u32 mclk_rate;
 	char __iomem *pdino_base;
+
+	struct wcd9xxx_core_resource wcd9xxx_res;
 };
 
 extern int msm8x10_wcd_mclk_enable(struct snd_soc_codec *codec, int mclk_enable,
 			     bool dapm);
 extern int msm8x10_wcd_hs_detect(struct snd_soc_codec *codec,
-			   struct msm8x10_wcd_mbhc_config *mbhc_cfg);
+			struct wcd9xxx_mbhc_config *mbhc_cfg);
 
 #endif
diff --git a/sound/soc/codecs/wcd9304.c b/sound/soc/codecs/wcd9304.c
index 58ea22d..a68722c 100644
--- a/sound/soc/codecs/wcd9304.c
+++ b/sound/soc/codecs/wcd9304.c
@@ -3961,8 +3961,10 @@
 {
 	short bias_value;
 	struct sitar_priv *sitar = snd_soc_codec_get_drvdata(codec);
+	struct wcd9xxx *core = codec->control_data;
+	struct wcd9xxx_core_resource *core_res = &core->core_res;
 
-	wcd9xxx_disable_irq(codec->control_data, WCD9XXX_IRQ_MBHC_POTENTIAL);
+	wcd9xxx_disable_irq(core_res, WCD9XXX_IRQ_MBHC_POTENTIAL);
 	if (noreldetection)
 		sitar_turn_onoff_rel_detection(codec, false);
 
@@ -4103,11 +4105,12 @@
 {
 	int r = 0;
 	struct wcd9xxx *core = dev_get_drvdata(sitar->codec->dev->parent);
+	struct wcd9xxx_core_resource *core_res = &core->core_res;
 
 	if (cancel_delayed_work_sync(&sitar->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);
+		wcd9xxx_unlock_sleep(core_res);
 		r = 1;
 	}
 	return r;
@@ -4171,12 +4174,14 @@
 	short bias_value;
 	int dce_mv, sta_mv;
 	struct wcd9xxx *core;
+	struct wcd9xxx_core_resource *core_res;
 
 	pr_debug("%s:\n", __func__);
 
 	delayed_work = to_delayed_work(work);
 	sitar = container_of(delayed_work, struct sitar_priv, mbhc_btn_dwork);
 	core = dev_get_drvdata(sitar->codec->dev->parent);
+	core_res = &core->core_res;
 
 	if (sitar) {
 		if (sitar->mbhc_cfg.button_jack) {
@@ -4198,7 +4203,7 @@
 	}
 
 	pr_debug("%s: leave\n", __func__);
-	wcd9xxx_unlock_sleep(core);
+	wcd9xxx_unlock_sleep(core_res);
 }
 
 
@@ -4212,11 +4217,13 @@
 	u32 dce_wait, sta_wait;
 	u8 *n_cic;
 	void *calibration;
+	struct wcd9xxx *core = codec->control_data;
+	struct wcd9xxx_core_resource *core_res = &core->core_res;
 
 	sitar = snd_soc_codec_get_drvdata(codec);
 	calibration = sitar->mbhc_cfg.calibration;
 
-	wcd9xxx_disable_irq(codec->control_data, WCD9XXX_IRQ_MBHC_POTENTIAL);
+	wcd9xxx_disable_irq(core_res, WCD9XXX_IRQ_MBHC_POTENTIAL);
 	sitar_turn_onoff_rel_detection(codec, false);
 
 	/* First compute the DCE / STA wait times
@@ -4707,22 +4714,28 @@
 /* should be called under interrupt context that hold suspend */
 static void sitar_schedule_hs_detect_plug(struct sitar_priv *sitar)
 {
+	struct wcd9xxx *core = sitar->codec->control_data;
+	struct wcd9xxx_core_resource *core_res = &core->core_res;
+
 	pr_debug("%s: scheduling sitar_hs_correct_gpio_plug\n", __func__);
 	sitar->hs_detect_work_stop = false;
-	wcd9xxx_lock_sleep(sitar->codec->control_data);
+	wcd9xxx_lock_sleep(core_res);
 	schedule_work(&sitar->hs_correct_plug_work);
 }
 
 /* called under codec_resource_lock acquisition */
 static void sitar_cancel_hs_detect_plug(struct sitar_priv *sitar)
 {
+	struct wcd9xxx *core = sitar->codec->control_data;
+	struct wcd9xxx_core_resource *core_res = &core->core_res;
+
 	pr_debug("%s: canceling hs_correct_plug_work\n", __func__);
 	sitar->hs_detect_work_stop = true;
 	wmb();
 	SITAR_RELEASE_LOCK(sitar->codec_resource_lock);
 	if (cancel_work_sync(&sitar->hs_correct_plug_work)) {
 		pr_debug("%s: hs_correct_plug_work is canceled\n", __func__);
-		wcd9xxx_unlock_sleep(sitar->codec->control_data);
+		wcd9xxx_unlock_sleep(core_res);
 	}
 	SITAR_ACQUIRE_LOCK(sitar->codec_resource_lock);
 }
@@ -4737,9 +4750,13 @@
 	short mb_v[MBHC_NUM_DCE_PLUG_DETECT];
 	enum sitar_mbhc_plug_type plug_type[MBHC_NUM_DCE_PLUG_DETECT];
 	unsigned long timeout;
+	struct wcd9xxx *core;
+	struct wcd9xxx_core_resource *core_res;
 
 	sitar = container_of(work, struct sitar_priv, hs_correct_plug_work);
 	codec = sitar->codec;
+	core = sitar->codec->control_data;
+	core_res = &core->core_res;
 
 	pr_debug("%s: enter\n", __func__);
 	sitar->mbhc_cfg.mclk_cb_fn(codec, 1, false);
@@ -4815,7 +4832,7 @@
 	sitar->mbhc_cfg.mclk_cb_fn(codec, 0, false);
 	pr_debug("%s: leave\n", __func__);
 	/* unlock sleep */
-	wcd9xxx_unlock_sleep(sitar->codec->control_data);
+	wcd9xxx_unlock_sleep(core_res);
 }
 
 /* called under codec_resource_lock acquisition */
@@ -4966,8 +4983,10 @@
 {
 	int r = IRQ_HANDLED;
 	struct snd_soc_codec *codec = data;
+	struct wcd9xxx *core = codec->control_data;
+	struct wcd9xxx_core_resource *core_res = &core->core_res;
 
-	if (unlikely(wcd9xxx_lock_sleep(codec->control_data) == false)) {
+	if (unlikely(wcd9xxx_lock_sleep(core_res) == false)) {
 		pr_warn("%s(): Failed to hold suspend\n", __func__);
 		r = IRQ_NONE;
 	} else {
@@ -5181,6 +5200,7 @@
 	short btnmeas[d->n_btn_meas + 1];
 	struct snd_soc_codec *codec = priv->codec;
 	struct wcd9xxx *core = dev_get_drvdata(priv->codec->dev->parent);
+	struct wcd9xxx_core_resource *core_res = &core->core_res;
 	int n_btn_meas = d->n_btn_meas;
 	u8 mbhc_status = snd_soc_read(codec, SITAR_A_CDC_MBHC_B1_STATUS) & 0x3E;
 
@@ -5271,12 +5291,12 @@
 		}
 		mask = sitar_get_button_mask(btn);
 		priv->buttons_pressed |= mask;
-		wcd9xxx_lock_sleep(core);
+		wcd9xxx_lock_sleep(core_res);
 		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);
+			wcd9xxx_unlock_sleep(core_res);
 		}
 	} else {
 		pr_debug("%s: bogus button press, too short press?\n",
@@ -5386,11 +5406,16 @@
 {
 	struct sitar_priv *sitar = data;
 	struct snd_soc_codec *codec;
+	struct wcd9xxx *core;
+	struct wcd9xxx_core_resource *core_res;
 
 	pr_info("%s: received HPHL OCP irq\n", __func__);
 
 	if (sitar) {
 		codec = sitar->codec;
+		core = codec->control_data;
+		core_res = &core->core_res;
+
 		if ((sitar->hphlocp_cnt < SITAR_OCP_ATTEMPT) &&
 		    (!sitar->hphrocp_cnt)) {
 			pr_info("%s: retry\n", __func__);
@@ -5400,7 +5425,7 @@
 			snd_soc_update_bits(codec, SITAR_A_RX_HPH_OCP_CTL, 0x10,
 					    0x10);
 		} else {
-			wcd9xxx_disable_irq(codec->control_data,
+			wcd9xxx_disable_irq(core_res,
 					    WCD9XXX_IRQ_HPH_PA_OCPL_FAULT);
 			sitar->hph_status |= SND_JACK_OC_HPHL;
 			if (sitar->mbhc_cfg.headset_jack)
@@ -5420,11 +5445,16 @@
 {
 	struct sitar_priv *sitar = data;
 	struct snd_soc_codec *codec;
+	struct wcd9xxx *core;
+	struct wcd9xxx_core_resource *core_res;
 
 	pr_info("%s: received HPHR OCP irq\n", __func__);
 
 	if (sitar) {
 		codec = sitar->codec;
+		core = codec->control_data;
+		core_res = &core->core_res;
+
 		if ((sitar->hphrocp_cnt < SITAR_OCP_ATTEMPT) &&
 		    (!sitar->hphlocp_cnt)) {
 			pr_info("%s: retry\n", __func__);
@@ -5434,7 +5464,7 @@
 			snd_soc_update_bits(codec, SITAR_A_RX_HPH_OCP_CTL, 0x10,
 					   0x10);
 		} else {
-			wcd9xxx_disable_irq(codec->control_data,
+			wcd9xxx_disable_irq(core_res,
 					    WCD9XXX_IRQ_HPH_PA_OCPR_FAULT);
 			sitar->hph_status |= SND_JACK_OC_HPHR;
 			if (sitar->mbhc_cfg.headset_jack)
@@ -5454,10 +5484,12 @@
 {
 	struct sitar_priv *priv = data;
 	struct snd_soc_codec *codec = priv->codec;
+	struct wcd9xxx *core = codec->control_data;
+	struct wcd9xxx_core_resource *core_res = &core->core_res;
 
 	pr_debug("%s: enter\n", __func__);
 	SITAR_ACQUIRE_LOCK(priv->codec_resource_lock);
-	wcd9xxx_disable_irq(codec->control_data, WCD9XXX_IRQ_MBHC_INSERTION);
+	wcd9xxx_disable_irq(core_res, WCD9XXX_IRQ_MBHC_INSERTION);
 
 	snd_soc_update_bits(codec, SITAR_A_CDC_MBHC_INT_CTL, 0x03, 0x00);
 
@@ -5847,9 +5879,11 @@
 	int i;
 	u8 sitar_version;
 	void *ptr = NULL;
+	struct wcd9xxx_core_resource *core_res;
 
 	codec->control_data = dev_get_drvdata(codec->dev->parent);
 	core = codec->control_data;
+	core_res = &core->core_res;
 
 	sitar = kzalloc(sizeof(struct sitar_priv), GFP_KERNEL);
 	if (!sitar) {
@@ -5951,7 +5985,7 @@
 	snd_soc_dapm_sync(dapm);
 
 
-	ret = wcd9xxx_request_irq(codec->control_data,
+	ret = wcd9xxx_request_irq(core_res,
 				  WCD9XXX_IRQ_MBHC_INSERTION,
 		sitar_hs_insert_irq, "Headset insert detect", sitar);
 	if (ret) {
@@ -5959,9 +5993,9 @@
 		       WCD9XXX_IRQ_MBHC_INSERTION);
 		goto err_insert_irq;
 	}
-	wcd9xxx_disable_irq(codec->control_data, WCD9XXX_IRQ_MBHC_INSERTION);
+	wcd9xxx_disable_irq(core_res, WCD9XXX_IRQ_MBHC_INSERTION);
 
-	ret = wcd9xxx_request_irq(codec->control_data,
+	ret = wcd9xxx_request_irq(core_res,
 				  WCD9XXX_IRQ_MBHC_REMOVAL,
 		sitar_hs_remove_irq, "Headset remove detect", sitar);
 	if (ret) {
@@ -5970,7 +6004,7 @@
 		goto err_remove_irq;
 	}
 
-	ret = wcd9xxx_request_irq(codec->control_data,
+	ret = wcd9xxx_request_irq(core_res,
 				  WCD9XXX_IRQ_MBHC_POTENTIAL,
 		sitar_dce_handler, "DC Estimation detect", sitar);
 	if (ret) {
@@ -5979,7 +6013,7 @@
 		goto err_potential_irq;
 	}
 
-	ret = wcd9xxx_request_irq(codec->control_data,
+	ret = wcd9xxx_request_irq(core_res,
 				  WCD9XXX_IRQ_MBHC_RELEASE,
 				  sitar_release_handler,
 				  "Button Release detect", sitar);
@@ -5989,7 +6023,7 @@
 		goto err_release_irq;
 	}
 
-	ret = wcd9xxx_request_irq(codec->control_data, WCD9XXX_IRQ_SLIMBUS,
+	ret = wcd9xxx_request_irq(core_res, WCD9XXX_IRQ_SLIMBUS,
 				  sitar_slimbus_irq, "SLIMBUS Slave", sitar);
 	if (ret) {
 		pr_err("%s: Failed to request irq %d\n", __func__,
@@ -6002,7 +6036,7 @@
 			SITAR_SLIM_PGD_PORT_INT_EN0 + i, 0xFF);
 
 
-	ret = wcd9xxx_request_irq(codec->control_data,
+	ret = wcd9xxx_request_irq(core_res,
 				  WCD9XXX_IRQ_HPH_PA_OCPL_FAULT,
 				  sitar_hphl_ocp_irq,
 				  "HPH_L OCP detect", sitar);
@@ -6011,10 +6045,10 @@
 		       WCD9XXX_IRQ_HPH_PA_OCPL_FAULT);
 		goto err_hphl_ocp_irq;
 	}
-	wcd9xxx_disable_irq(codec->control_data,
+	wcd9xxx_disable_irq(core_res,
 			    WCD9XXX_IRQ_HPH_PA_OCPL_FAULT);
 
-	ret = wcd9xxx_request_irq(codec->control_data,
+	ret = wcd9xxx_request_irq(core_res,
 				  WCD9XXX_IRQ_HPH_PA_OCPR_FAULT,
 				  sitar_hphr_ocp_irq, "HPH_R OCP detect",
 				  sitar);
@@ -6023,7 +6057,7 @@
 		       WCD9XXX_IRQ_HPH_PA_OCPR_FAULT);
 		goto err_hphr_ocp_irq;
 	}
-	wcd9xxx_disable_irq(codec->control_data, WCD9XXX_IRQ_HPH_PA_OCPR_FAULT);
+	wcd9xxx_disable_irq(core_res, WCD9XXX_IRQ_HPH_PA_OCPR_FAULT);
 
 	codec->ignore_pmdown_time = 1;
 
@@ -6034,19 +6068,19 @@
 	return ret;
 
 err_hphr_ocp_irq:
-	wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_HPH_PA_OCPL_FAULT,
+	wcd9xxx_free_irq(core_res, WCD9XXX_IRQ_HPH_PA_OCPL_FAULT,
 			 sitar);
 err_hphl_ocp_irq:
-	wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_SLIMBUS, sitar);
+	wcd9xxx_free_irq(core_res, WCD9XXX_IRQ_SLIMBUS, sitar);
 err_slimbus_irq:
-	wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_MBHC_RELEASE, sitar);
+	wcd9xxx_free_irq(core_res, WCD9XXX_IRQ_MBHC_RELEASE, sitar);
 err_release_irq:
-	wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_MBHC_POTENTIAL,
+	wcd9xxx_free_irq(core_res, WCD9XXX_IRQ_MBHC_POTENTIAL,
 			 sitar);
 err_potential_irq:
-	wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_MBHC_REMOVAL, sitar);
+	wcd9xxx_free_irq(core_res, WCD9XXX_IRQ_MBHC_REMOVAL, sitar);
 err_remove_irq:
-	wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_MBHC_INSERTION,
+	wcd9xxx_free_irq(core_res, WCD9XXX_IRQ_MBHC_INSERTION,
 			 sitar);
 err_insert_irq:
 	kfree(ptr);
@@ -6059,12 +6093,15 @@
 static int sitar_codec_remove(struct snd_soc_codec *codec)
 {
 	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);
-	wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_MBHC_POTENTIAL,
+	struct wcd9xxx *core = codec->control_data;
+	struct wcd9xxx_core_resource *core_res = &core->core_res;
+
+	wcd9xxx_free_irq(core_res, WCD9XXX_IRQ_SLIMBUS, sitar);
+	wcd9xxx_free_irq(core_res, WCD9XXX_IRQ_MBHC_RELEASE, sitar);
+	wcd9xxx_free_irq(core_res, WCD9XXX_IRQ_MBHC_POTENTIAL,
 			 sitar);
-	wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_MBHC_REMOVAL, sitar);
-	wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_MBHC_INSERTION,
+	wcd9xxx_free_irq(core_res, WCD9XXX_IRQ_MBHC_REMOVAL, sitar);
+	wcd9xxx_free_irq(core_res, WCD9XXX_IRQ_MBHC_INSERTION,
 			 sitar);
 	SITAR_ACQUIRE_LOCK(sitar->codec_resource_lock);
 	sitar_codec_disable_clock_block(codec);
diff --git a/sound/soc/codecs/wcd9306.c b/sound/soc/codecs/wcd9306.c
index fcc264c..fa44b72 100644
--- a/sound/soc/codecs/wcd9306.c
+++ b/sound/soc/codecs/wcd9306.c
@@ -42,6 +42,13 @@
 #define TAPAN_HPH_PA_SETTLE_COMP_ON 3000
 #define TAPAN_HPH_PA_SETTLE_COMP_OFF 13000
 
+#define TAPAN_VDD_CX_OPTIMAL_UA 10000
+#define TAPAN_VDD_CX_SLEEP_UA 2000
+
+static struct regulator *tapan_codec_find_regulator(
+	struct snd_soc_codec *codec,
+	const char *name);
+
 static atomic_t kp_tapan_priv;
 static int spkr_drv_wrnd_param_set(const char *val,
 				   const struct kernel_param *kp);
@@ -59,6 +66,9 @@
 			SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |\
 			SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000)
 
+#define WCD9302_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\
+			SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000)
+
 #define NUM_DECIMATORS 4
 #define NUM_INTERPOLATORS 4
 #define BITS_PER_REG 8
@@ -966,7 +976,7 @@
 static const struct snd_kcontrol_new class_h_dsm_mux =
 	SOC_DAPM_ENUM("CLASS_H_DSM MUX Mux", class_h_dsm_enum);
 
-static const struct snd_kcontrol_new tapan_snd_controls[] = {
+static const struct snd_kcontrol_new tapan_common_snd_controls[] = {
 
 	SOC_ENUM_EXT("EAR PA Gain", tapan_ear_pa_gain_enum[0],
 		tapan_pa_gain_get, tapan_pa_gain_put),
@@ -988,25 +998,17 @@
 	SOC_SINGLE_TLV("ADC2 Volume", TAPAN_A_TX_2_EN, 2, 19, 0, analog_gain),
 	SOC_SINGLE_TLV("ADC3 Volume", TAPAN_A_TX_3_EN, 2, 19, 0, analog_gain),
 	SOC_SINGLE_TLV("ADC4 Volume", TAPAN_A_TX_4_EN, 2, 19, 0, analog_gain),
-	SOC_SINGLE_TLV("ADC5 Volume", TAPAN_A_TX_5_EN, 2, 19, 0, analog_gain),
-
 	SOC_SINGLE_S8_TLV("RX1 Digital Volume", TAPAN_A_CDC_RX1_VOL_CTL_B2_CTL,
 		-84, 40, digital_gain),
 	SOC_SINGLE_S8_TLV("RX2 Digital Volume", TAPAN_A_CDC_RX2_VOL_CTL_B2_CTL,
 		-84, 40, digital_gain),
 	SOC_SINGLE_S8_TLV("RX3 Digital Volume", TAPAN_A_CDC_RX3_VOL_CTL_B2_CTL,
 		-84, 40, digital_gain),
-	SOC_SINGLE_S8_TLV("RX4 Digital Volume", TAPAN_A_CDC_RX4_VOL_CTL_B2_CTL,
-		-84, 40, digital_gain),
 
 	SOC_SINGLE_S8_TLV("DEC1 Volume", TAPAN_A_CDC_TX1_VOL_CTL_GAIN, -84, 40,
 		digital_gain),
 	SOC_SINGLE_S8_TLV("DEC2 Volume", TAPAN_A_CDC_TX2_VOL_CTL_GAIN, -84, 40,
 		digital_gain),
-	SOC_SINGLE_S8_TLV("DEC3 Volume", TAPAN_A_CDC_TX3_VOL_CTL_GAIN, -84, 40,
-		digital_gain),
-	SOC_SINGLE_S8_TLV("DEC4 Volume", TAPAN_A_CDC_TX4_VOL_CTL_GAIN, -84, 40,
-		digital_gain),
 
 	SOC_SINGLE_S8_TLV("IIR1 INP1 Volume", TAPAN_A_CDC_IIR1_GAIN_B1_CTL, -84,
 		40, digital_gain),
@@ -1017,10 +1019,6 @@
 	SOC_SINGLE_S8_TLV("IIR1 INP4 Volume", TAPAN_A_CDC_IIR1_GAIN_B4_CTL, -84,
 		40, digital_gain),
 
-	SOC_SINGLE_EXT("ANC Slot", SND_SOC_NOPM, 0, 100, 0, tapan_get_anc_slot,
-		tapan_put_anc_slot),
-	SOC_ENUM_EXT("ANC Function", tapan_anc_func_enum, tapan_get_anc_func,
-		tapan_put_anc_func),
 	SOC_ENUM("TX1 HPF cut off", cf_dec1_enum),
 	SOC_ENUM("TX2 HPF cut off", cf_dec2_enum),
 	SOC_ENUM("TX3 HPF cut off", cf_dec3_enum),
@@ -1034,12 +1032,10 @@
 	SOC_SINGLE("RX1 HPF Switch", TAPAN_A_CDC_RX1_B5_CTL, 2, 1, 0),
 	SOC_SINGLE("RX2 HPF Switch", TAPAN_A_CDC_RX2_B5_CTL, 2, 1, 0),
 	SOC_SINGLE("RX3 HPF Switch", TAPAN_A_CDC_RX3_B5_CTL, 2, 1, 0),
-	SOC_SINGLE("RX4 HPF Switch", TAPAN_A_CDC_RX4_B5_CTL, 2, 1, 0),
 
 	SOC_ENUM("RX1 HPF cut off", cf_rxmix1_enum),
 	SOC_ENUM("RX2 HPF cut off", cf_rxmix2_enum),
 	SOC_ENUM("RX3 HPF cut off", cf_rxmix3_enum),
-	SOC_ENUM("RX4 HPF cut off", cf_rxmix4_enum),
 
 	SOC_SINGLE_EXT("IIR1 Enable Band1", IIR1, BAND1, 1, 0,
 	tapan_get_iir_enable_audio_mixer, tapan_put_iir_enable_audio_mixer),
@@ -1082,6 +1078,23 @@
 	tapan_get_iir_band_audio_mixer, tapan_put_iir_band_audio_mixer),
 	SOC_SINGLE_MULTI_EXT("IIR2 Band5", IIR2, BAND5, 255, 0, 5,
 	tapan_get_iir_band_audio_mixer, tapan_put_iir_band_audio_mixer),
+};
+
+static const struct snd_kcontrol_new tapan_9306_snd_controls[] = {
+	SOC_SINGLE_TLV("ADC5 Volume", TAPAN_A_TX_5_EN, 2, 19, 0, analog_gain),
+
+	SOC_SINGLE_S8_TLV("RX4 Digital Volume", TAPAN_A_CDC_RX4_VOL_CTL_B2_CTL,
+		-84, 40, digital_gain),
+	SOC_SINGLE_S8_TLV("DEC3 Volume", TAPAN_A_CDC_TX3_VOL_CTL_GAIN, -84, 40,
+		digital_gain),
+	SOC_SINGLE_S8_TLV("DEC4 Volume", TAPAN_A_CDC_TX4_VOL_CTL_GAIN, -84, 40,
+		digital_gain),
+	SOC_SINGLE_EXT("ANC Slot", SND_SOC_NOPM, 0, 100, 0, tapan_get_anc_slot,
+		tapan_put_anc_slot),
+	SOC_ENUM_EXT("ANC Function", tapan_anc_func_enum, tapan_get_anc_func,
+		tapan_put_anc_func),
+	SOC_SINGLE("RX4 HPF Switch", TAPAN_A_CDC_RX4_B5_CTL, 2, 1, 0),
+	SOC_ENUM("RX4 HPF cut off", cf_rxmix4_enum),
 
 	SOC_SINGLE_EXT("COMP0 Switch", SND_SOC_NOPM, COMPANDER_0, 1, 0,
 		       tapan_get_compander, tapan_set_compander),
@@ -1089,7 +1102,6 @@
 		       tapan_get_compander, tapan_set_compander),
 	SOC_SINGLE_EXT("COMP2 Switch", SND_SOC_NOPM, COMPANDER_2, 1, 0,
 		       tapan_get_compander, tapan_set_compander),
-
 };
 
 static const char * const rx_1_2_mix1_text[] = {
@@ -1106,6 +1118,14 @@
 	"ZERO", "SRC1", "SRC2", "IIR1", "IIR2"
 };
 
+static const char * const rx_rdac3_text[] = {
+	"DEM1", "DEM2"
+};
+
+static const char * const rx_rdac4_text[] = {
+	"DEM3", "DEM2"
+};
+
 static const char * const rx_rdac5_text[] = {
 	"DEM4", "DEM3_INV"
 };
@@ -1212,6 +1232,12 @@
 static const struct soc_enum rx4_mix2_inp2_chain_enum =
 	SOC_ENUM_SINGLE(TAPAN_A_CDC_CONN_RX4_B3_CTL, 3, 5, rx_mix2_text);
 
+static const struct soc_enum rx_rdac3_enum =
+	SOC_ENUM_SINGLE(TAPAN_A_CDC_CONN_RX2_B2_CTL, 4, 2, rx_rdac3_text);
+
+static const struct soc_enum rx_rdac4_enum =
+	SOC_ENUM_SINGLE(TAPAN_A_CDC_CONN_MISC, 1, 2, rx_rdac4_text);
+
 static const struct soc_enum rx_rdac5_enum =
 	SOC_ENUM_SINGLE(TAPAN_A_CDC_CONN_MISC, 2, 2, rx_rdac5_text);
 
@@ -1301,6 +1327,12 @@
 static const struct snd_kcontrol_new rx4_mix2_inp2_mux =
 	SOC_DAPM_ENUM("RX4 MIX2 INP2 Mux", rx4_mix2_inp2_chain_enum);
 
+static const struct snd_kcontrol_new rx_dac3_mux =
+	SOC_DAPM_ENUM("RDAC3 MUX Mux", rx_rdac3_enum);
+
+static const struct snd_kcontrol_new rx_dac4_mux =
+	SOC_DAPM_ENUM("RDAC4 MUX Mux", rx_rdac4_enum);
+
 static const struct snd_kcontrol_new rx_dac5_mux =
 	SOC_DAPM_ENUM("RDAC5 MUX Mux", rx_rdac5_enum);
 
@@ -2515,94 +2547,24 @@
 	{"SLIM TX2 MUX", NULL, "I2S_CLK"},
 };
 
-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_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"},
-	/* 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"},
-	/* 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"},
-
-	{"SLIM TX1 MUX", "DEC1", "DEC1 MUX"},
-	{"SLIM TX1 MUX", "DEC2", "DEC2 MUX"},
+static const struct snd_soc_dapm_route wcd9306_map[] = {
+	{"SLIM TX1 MUX", "RMIX4", "RX4 MIX1"},
+	{"SLIM TX2 MUX", "RMIX4", "RX4 MIX1"},
+	{"SLIM TX3 MUX", "RMIX4", "RX4 MIX1"},
+	{"SLIM TX4 MUX", "RMIX4", "RX4 MIX1"},
+	{"SLIM TX5 MUX", "RMIX4", "RX4 MIX1"},
 	{"SLIM TX1 MUX", "DEC3", "DEC3 MUX"},
 	{"SLIM TX1 MUX", "DEC4", "DEC4 MUX"},
-	{"SLIM TX1 MUX", "RMIX1", "RX1 MIX1"},
-	{"SLIM TX1 MUX", "RMIX2", "RX2 MIX1"},
-	{"SLIM TX1 MUX", "RMIX3", "RX3 MIX1"},
-	{"SLIM TX1 MUX", "RMIX4", "RX4 MIX1"},
-
-	{"SLIM TX2 MUX", "DEC1", "DEC1 MUX"},
-	{"SLIM TX2 MUX", "DEC2", "DEC2 MUX"},
 	{"SLIM TX2 MUX", "DEC3", "DEC3 MUX"},
 	{"SLIM TX2 MUX", "DEC4", "DEC4 MUX"},
-	{"SLIM TX2 MUX", "RMIX1", "RX1 MIX1"},
-	{"SLIM TX2 MUX", "RMIX2", "RX2 MIX1"},
-	{"SLIM TX2 MUX", "RMIX3", "RX3 MIX1"},
-	{"SLIM TX2 MUX", "RMIX4", "RX4 MIX1"},
-
 	{"SLIM TX3 MUX", "DEC3", "DEC3 MUX"},
-	{"SLIM TX3 MUX", "RMIX1", "RX1 MIX1"},
-	{"SLIM TX3 MUX", "RMIX2", "RX2 MIX1"},
-	{"SLIM TX3 MUX", "RMIX3", "RX3 MIX1"},
-	{"SLIM TX3 MUX", "RMIX4", "RX4 MIX1"},
-
 	{"SLIM TX4 MUX", "DEC4", "DEC4 MUX"},
-	{"SLIM TX4 MUX", "RMIX1", "RX1 MIX1"},
-	{"SLIM TX4 MUX", "RMIX2", "RX2 MIX1"},
-	{"SLIM TX4 MUX", "RMIX3", "RX3 MIX1"},
-	{"SLIM TX4 MUX", "RMIX4", "RX4 MIX1"},
-
-	{"SLIM TX5 MUX", "DEC1", "DEC1 MUX"},
-	{"SLIM TX5 MUX", "RMIX1", "RX1 MIX1"},
-	{"SLIM TX5 MUX", "RMIX2", "RX2 MIX1"},
-	{"SLIM TX5 MUX", "RMIX3", "RX3 MIX1"},
-	{"SLIM TX5 MUX", "RMIX4", "RX4 MIX1"},
-
-	/* Earpiece (RX MIX1) */
-	{"EAR", NULL, "EAR PA"},
-	{"EAR PA", NULL, "EAR_PA_MIXER"},
-	{"EAR_PA_MIXER", NULL, "DAC1"},
-	{"DAC1", NULL, "RX_BIAS"},
-	{"DAC1", NULL, "CDC_CP_VDD"},
-
 
 	{"ANC EAR", NULL, "ANC EAR PA"},
 	{"ANC EAR PA", NULL, "EAR_PA_MIXER"},
 	{"ANC1 FB MUX", "EAR_HPH_L", "RX1 MIX2"},
 	{"ANC1 FB MUX", "EAR_LINE_1", "RX2 MIX2"},
 
-	/* Headset (RX MIX1 and RX MIX2) */
-	{"HEADPHONE", NULL, "HPHL"},
-	{"HEADPHONE", NULL, "HPHR"},
-
-	{"HPHL", NULL, "HPHL_PA_MIXER"},
-	{"HPHL_PA_MIXER", NULL, "HPHL DAC"},
-	{"HPHL DAC", NULL, "RX_BIAS"},
-	{"HPHL DAC", NULL, "CDC_CP_VDD"},
-
-	{"HPHR", NULL, "HPHR_PA_MIXER"},
-	{"HPHR_PA_MIXER", NULL, "HPHR DAC"},
-	{"HPHR DAC", NULL, "RX_BIAS"},
-	{"HPHR DAC", NULL, "CDC_CP_VDD"},
-
 	{"ANC HEADPHONE", NULL, "ANC HPHL"},
 	{"ANC HEADPHONE", NULL, "ANC HPHR"},
 
@@ -2630,6 +2592,150 @@
 
 	{"ANC HPHR", NULL, "CDC_CONN"},
 
+	{"RDAC5 MUX", "DEM4", "RX4 MIX2"},
+	{"SPK DAC", "Switch", "RX4 MIX2"},
+
+	{"RX1 MIX2", NULL, "ANC1 MUX"},
+	{"RX2 MIX2", NULL, "ANC2 MUX"},
+
+	{"RX1 MIX1", NULL, "COMP1_CLK"},
+	{"RX2 MIX1", NULL, "COMP1_CLK"},
+	{"RX3 MIX1", NULL, "COMP2_CLK"},
+	{"RX4 MIX1", NULL, "COMP0_CLK"},
+
+	{"RX4 MIX1", NULL, "RX4 MIX1 INP1"},
+	{"RX4 MIX1", NULL, "RX4 MIX1 INP2"},
+	{"RX4 MIX2", NULL, "RX4 MIX1"},
+	{"RX4 MIX2", NULL, "RX4 MIX2 INP1"},
+	{"RX4 MIX2", NULL, "RX4 MIX2 INP2"},
+
+	{"RX4 MIX1 INP1", "RX1", "SLIM RX1"},
+	{"RX4 MIX1 INP1", "RX2", "SLIM RX2"},
+	{"RX4 MIX1 INP1", "RX3", "SLIM RX3"},
+	{"RX4 MIX1 INP1", "RX4", "SLIM RX4"},
+	{"RX4 MIX1 INP1", "RX5", "SLIM RX5"},
+	{"RX4 MIX1 INP1", "IIR1", "IIR1"},
+	{"RX4 MIX1 INP2", "RX1", "SLIM RX1"},
+	{"RX4 MIX1 INP2", "RX2", "SLIM RX2"},
+	{"RX4 MIX1 INP2", "RX3", "SLIM RX3"},
+	{"RX4 MIX1 INP2", "RX5", "SLIM RX5"},
+	{"RX4 MIX1 INP2", "RX4", "SLIM RX4"},
+	{"RX4 MIX1 INP2", "IIR1", "IIR1"},
+	{"RX4 MIX2 INP1", "IIR1", "IIR1"},
+	{"RX4 MIX2 INP2", "IIR1", "IIR1"},
+
+	{"DEC1 MUX", "DMIC3", "DMIC3"},
+	{"DEC1 MUX", "DMIC4", "DMIC4"},
+	{"DEC2 MUX", "DMIC3", "DMIC3"},
+	{"DEC2 MUX", "DMIC4", "DMIC4"},
+
+	{"DEC3 MUX", "ADC1", "ADC1"},
+	{"DEC3 MUX", "ADC2", "ADC2"},
+	{"DEC3 MUX", "ADC3", "ADC3"},
+	{"DEC3 MUX", "ADC4", "ADC4"},
+	{"DEC3 MUX", "ADC5", "ADC5"},
+	{"DEC3 MUX", "DMIC1", "DMIC1"},
+	{"DEC3 MUX", "DMIC2", "DMIC2"},
+	{"DEC3 MUX", "DMIC3", "DMIC3"},
+	{"DEC3 MUX", "DMIC4", "DMIC4"},
+	{"DEC3 MUX", NULL, "CDC_CONN"},
+
+	{"DEC4 MUX", "ADC1", "ADC1"},
+	{"DEC4 MUX", "ADC2", "ADC2"},
+	{"DEC4 MUX", "ADC3", "ADC3"},
+	{"DEC4 MUX", "ADC4", "ADC4"},
+	{"DEC4 MUX", "ADC5", "ADC5"},
+	{"DEC4 MUX", "DMIC1", "DMIC1"},
+	{"DEC4 MUX", "DMIC2", "DMIC2"},
+	{"DEC4 MUX", "DMIC3", "DMIC3"},
+	{"DEC4 MUX", "DMIC4", "DMIC4"},
+	{"DEC4 MUX", NULL, "CDC_CONN"},
+
+	{"ADC5", NULL, "AMIC5"},
+
+	{"AUX_PGA_Left", NULL, "AMIC5"},
+
+	{"IIR1 INP1 MUX", "DEC3", "DEC3 MUX"},
+	{"IIR1 INP1 MUX", "DEC4", "DEC4 MUX"},
+
+	{"MIC BIAS3 Internal1", NULL, "LDO_H"},
+	{"MIC BIAS3 Internal2", NULL, "LDO_H"},
+	{"MIC BIAS3 External", NULL, "LDO_H"},
+};
+
+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_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"},
+	/* 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"},
+	/* 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"},
+
+	{"SLIM TX1 MUX", "DEC1", "DEC1 MUX"},
+	{"SLIM TX1 MUX", "DEC2", "DEC2 MUX"},
+	{"SLIM TX1 MUX", "RMIX1", "RX1 MIX1"},
+	{"SLIM TX1 MUX", "RMIX2", "RX2 MIX1"},
+	{"SLIM TX1 MUX", "RMIX3", "RX3 MIX1"},
+
+	{"SLIM TX2 MUX", "DEC1", "DEC1 MUX"},
+	{"SLIM TX2 MUX", "DEC2", "DEC2 MUX"},
+	{"SLIM TX2 MUX", "RMIX1", "RX1 MIX1"},
+	{"SLIM TX2 MUX", "RMIX2", "RX2 MIX1"},
+	{"SLIM TX2 MUX", "RMIX3", "RX3 MIX1"},
+
+	{"SLIM TX3 MUX", "RMIX1", "RX1 MIX1"},
+	{"SLIM TX3 MUX", "RMIX2", "RX2 MIX1"},
+	{"SLIM TX3 MUX", "RMIX3", "RX3 MIX1"},
+
+	{"SLIM TX4 MUX", "RMIX1", "RX1 MIX1"},
+	{"SLIM TX4 MUX", "RMIX2", "RX2 MIX1"},
+	{"SLIM TX4 MUX", "RMIX3", "RX3 MIX1"},
+
+	{"SLIM TX5 MUX", "DEC1", "DEC1 MUX"},
+	{"SLIM TX5 MUX", "RMIX1", "RX1 MIX1"},
+	{"SLIM TX5 MUX", "RMIX2", "RX2 MIX1"},
+	{"SLIM TX5 MUX", "RMIX3", "RX3 MIX1"},
+
+	/* Earpiece (RX MIX1) */
+	{"EAR", NULL, "EAR PA"},
+	{"EAR PA", NULL, "EAR_PA_MIXER"},
+	{"EAR_PA_MIXER", NULL, "DAC1"},
+	{"DAC1", NULL, "RX_BIAS"},
+	{"DAC1", NULL, "CDC_CP_VDD"},
+
+
+	/* Headset (RX MIX1 and RX MIX2) */
+	{"HEADPHONE", NULL, "HPHL"},
+	{"HEADPHONE", NULL, "HPHR"},
+
+	{"HPHL", NULL, "HPHL_PA_MIXER"},
+	{"HPHL_PA_MIXER", NULL, "HPHL DAC"},
+	{"HPHL DAC", NULL, "RX_BIAS"},
+	{"HPHL DAC", NULL, "CDC_CP_VDD"},
+
+	{"HPHR", NULL, "HPHR_PA_MIXER"},
+	{"HPHR_PA_MIXER", NULL, "HPHR DAC"},
+	{"HPHR DAC", NULL, "RX_BIAS"},
+	{"HPHR DAC", NULL, "CDC_CP_VDD"},
+
+
 	{"DAC1", "Switch", "CLASS_H_DSM MUX"},
 	{"HPHL DAC", "Switch", "CLASS_H_DSM MUX"},
 	{"HPHR DAC", NULL, "RX2 CHAIN"},
@@ -2640,36 +2746,28 @@
 
 	{"LINEOUT1 PA", NULL, "LINEOUT1_PA_MIXER"},
 	{"LINEOUT1_PA_MIXER", NULL, "LINEOUT1 DAC"},
-
 	{"LINEOUT2 PA", NULL, "LINEOUT2_PA_MIXER"},
 	{"LINEOUT2_PA_MIXER", NULL, "LINEOUT2 DAC"},
 
 	{"LINEOUT1 DAC", NULL, "RX3 MIX1"},
 
 	{"RDAC5 MUX", "DEM3_INV", "RX3 MIX1"},
-	{"RDAC5 MUX", "DEM4", "RX4 MIX2"},
-
 	{"LINEOUT2 DAC", NULL, "RDAC5 MUX"},
 
 	{"SPK PA", NULL, "SPK DAC"},
-	{"SPK DAC", "Switch", "RX4 MIX2"},
 	{"SPK DAC", NULL, "VDD_SPKDRV"},
 
 	{"RX1 CHAIN", NULL, "RX1 MIX2"},
 	{"RX2 CHAIN", NULL, "RX2 MIX2"},
 	{"CLASS_H_DSM MUX", "RX_HPHL", "RX1 CHAIN"},
-	{"RX1 MIX2", NULL, "ANC1 MUX"},
-	{"RX2 MIX2", NULL, "ANC2 MUX"},
 
 	{"LINEOUT1 DAC", NULL, "RX_BIAS"},
 	{"LINEOUT2 DAC", NULL, "RX_BIAS"},
 	{"LINEOUT1 DAC", NULL, "CDC_CP_VDD"},
 	{"LINEOUT2 DAC", NULL, "CDC_CP_VDD"},
 
-	{"RX1 MIX1", NULL, "COMP1_CLK"},
-	{"RX2 MIX1", NULL, "COMP1_CLK"},
-	{"RX3 MIX1", NULL, "COMP2_CLK"},
-	{"RX4 MIX1", NULL, "COMP0_CLK"},
+	{"RDAC3 MUX", "DEM2", "RX2 MIX1"},
+	{"RDAC3 MUX", "DEM1", "RX1 CHAIN"},
 
 	{"RX1 MIX1", NULL, "RX1 MIX1 INP1"},
 	{"RX1 MIX1", NULL, "RX1 MIX1 INP2"},
@@ -2678,17 +2776,12 @@
 	{"RX2 MIX1", NULL, "RX2 MIX1 INP2"},
 	{"RX3 MIX1", NULL, "RX3 MIX1 INP1"},
 	{"RX3 MIX1", NULL, "RX3 MIX1 INP2"},
-	{"RX4 MIX1", NULL, "RX4 MIX1 INP1"},
-	{"RX4 MIX1", NULL, "RX4 MIX1 INP2"},
 	{"RX1 MIX2", NULL, "RX1 MIX1"},
 	{"RX1 MIX2", NULL, "RX1 MIX2 INP1"},
 	{"RX1 MIX2", NULL, "RX1 MIX2 INP2"},
 	{"RX2 MIX2", NULL, "RX2 MIX1"},
 	{"RX2 MIX2", NULL, "RX2 MIX2 INP1"},
 	{"RX2 MIX2", NULL, "RX2 MIX2 INP2"},
-	{"RX4 MIX2", NULL, "RX4 MIX1"},
-	{"RX4 MIX2", NULL, "RX4 MIX2 INP1"},
-	{"RX4 MIX2", NULL, "RX4 MIX2 INP2"},
 
 	/* SLIM_MUX("AIF1_PB", "AIF1 PB"),*/
 	{"SLIM RX1 MUX", "AIF1_PB", "AIF1 PB"},
@@ -2756,25 +2849,11 @@
 	{"RX3 MIX1 INP2", "RX4", "SLIM RX4"},
 	{"RX3 MIX1 INP2", "RX5", "SLIM RX5"},
 	{"RX3 MIX1 INP2", "IIR1", "IIR1"},
-	{"RX4 MIX1 INP1", "RX1", "SLIM RX1"},
-	{"RX4 MIX1 INP1", "RX2", "SLIM RX2"},
-	{"RX4 MIX1 INP1", "RX3", "SLIM RX3"},
-	{"RX4 MIX1 INP1", "RX4", "SLIM RX4"},
-	{"RX4 MIX1 INP1", "RX5", "SLIM RX5"},
-	{"RX4 MIX1 INP1", "IIR1", "IIR1"},
-	{"RX4 MIX1 INP2", "RX1", "SLIM RX1"},
-	{"RX4 MIX1 INP2", "RX2", "SLIM RX2"},
-	{"RX4 MIX1 INP2", "RX3", "SLIM RX3"},
-	{"RX4 MIX1 INP2", "RX5", "SLIM RX5"},
-	{"RX4 MIX1 INP2", "RX4", "SLIM RX4"},
-	{"RX4 MIX1 INP2", "IIR1", "IIR1"},
 
 	{"RX1 MIX2 INP1", "IIR1", "IIR1"},
 	{"RX1 MIX2 INP2", "IIR1", "IIR1"},
 	{"RX2 MIX2 INP1", "IIR1", "IIR1"},
 	{"RX2 MIX2 INP2", "IIR1", "IIR1"},
-	{"RX4 MIX2 INP1", "IIR1", "IIR1"},
-	{"RX4 MIX2 INP2", "IIR1", "IIR1"},
 
 	/* Decimator Inputs */
 	{"DEC1 MUX", "ADC1", "ADC1"},
@@ -2783,8 +2862,6 @@
 	{"DEC1 MUX", "ADC4", "ADC4"},
 	{"DEC1 MUX", "DMIC1", "DMIC1"},
 	{"DEC1 MUX", "DMIC2", "DMIC2"},
-	{"DEC1 MUX", "DMIC3", "DMIC3"},
-	{"DEC1 MUX", "DMIC4", "DMIC4"},
 	{"DEC1 MUX", NULL, "CDC_CONN"},
 
 	{"DEC2 MUX", "ADC1", "ADC1"},
@@ -2793,38 +2870,13 @@
 	{"DEC2 MUX", "ADC4", "ADC4"},
 	{"DEC2 MUX", "DMIC1", "DMIC1"},
 	{"DEC2 MUX", "DMIC2", "DMIC2"},
-	{"DEC2 MUX", "DMIC3", "DMIC3"},
-	{"DEC2 MUX", "DMIC4", "DMIC4"},
 	{"DEC2 MUX", NULL, "CDC_CONN"},
 
-	{"DEC3 MUX", "ADC1", "ADC1"},
-	{"DEC3 MUX", "ADC2", "ADC2"},
-	{"DEC3 MUX", "ADC3", "ADC3"},
-	{"DEC3 MUX", "ADC4", "ADC4"},
-	{"DEC3 MUX", "ADC5", "ADC5"},
-	{"DEC3 MUX", "DMIC1", "DMIC1"},
-	{"DEC3 MUX", "DMIC2", "DMIC2"},
-	{"DEC3 MUX", "DMIC3", "DMIC3"},
-	{"DEC3 MUX", "DMIC4", "DMIC4"},
-	{"DEC3 MUX", NULL, "CDC_CONN"},
-
-	{"DEC4 MUX", "ADC1", "ADC1"},
-	{"DEC4 MUX", "ADC2", "ADC2"},
-	{"DEC4 MUX", "ADC3", "ADC3"},
-	{"DEC4 MUX", "ADC4", "ADC4"},
-	{"DEC4 MUX", "ADC5", "ADC5"},
-	{"DEC4 MUX", "DMIC1", "DMIC1"},
-	{"DEC4 MUX", "DMIC2", "DMIC2"},
-	{"DEC4 MUX", "DMIC3", "DMIC3"},
-	{"DEC4 MUX", "DMIC4", "DMIC4"},
-	{"DEC4 MUX", NULL, "CDC_CONN"},
-
 	/* ADC Connections */
 	{"ADC1", NULL, "AMIC1"},
 	{"ADC2", NULL, "AMIC2"},
 	{"ADC3", NULL, "AMIC3"},
 	{"ADC4", NULL, "AMIC4"},
-	{"ADC5", NULL, "AMIC5"},
 
 	/* AUX PGA Connections */
 	{"EAR_PA_MIXER", "AUX_PGA_L Switch", "AUX_PGA_Left"},
@@ -2832,13 +2884,10 @@
 	{"HPHR_PA_MIXER", "AUX_PGA_R Switch", "AUX_PGA_Right"},
 	{"LINEOUT1_PA_MIXER", "AUX_PGA_L Switch", "AUX_PGA_Left"},
 	{"LINEOUT2_PA_MIXER", "AUX_PGA_R Switch", "AUX_PGA_Right"},
-	{"AUX_PGA_Left", NULL, "AMIC5"},
 
 	{"IIR1", NULL, "IIR1 INP1 MUX"},
 	{"IIR1 INP1 MUX", "DEC1", "DEC1 MUX"},
 	{"IIR1 INP1 MUX", "DEC2", "DEC2 MUX"},
-	{"IIR1 INP1 MUX", "DEC3", "DEC3 MUX"},
-	{"IIR1 INP1 MUX", "DEC4", "DEC4 MUX"},
 
 	{"MIC BIAS1 Internal1", NULL, "LDO_H"},
 	{"MIC BIAS1 Internal2", NULL, "LDO_H"},
@@ -2847,9 +2896,17 @@
 	{"MIC BIAS2 Internal2", NULL, "LDO_H"},
 	{"MIC BIAS2 Internal3", NULL, "LDO_H"},
 	{"MIC BIAS2 External", NULL, "LDO_H"},
-	{"MIC BIAS3 Internal1", NULL, "LDO_H"},
-	{"MIC BIAS3 Internal2", NULL, "LDO_H"},
-	{"MIC BIAS3 External", NULL, "LDO_H"},
+};
+
+static const struct snd_soc_dapm_route wcd9302_map[] = {
+	{"SPK DAC", "Switch", "RX3 MIX1"},
+
+	{"RDAC4 MUX", "DEM3", "RX3 MIX1"},
+	{"RDAC4 MUX", "DEM2", "RX2 CHAIN"},
+	{"LINEOUT1 DAC", NULL, "RDAC4 MUX"},
+
+	{"RDAC5 MUX", "DEM4", "RX3 MIX1"},
+	{"RDAC5 MUX", "DEM3_INV", "RDAC4 MUX"},
 };
 
 static int tapan_readable(struct snd_soc_codec *ssc, unsigned int reg)
@@ -2920,6 +2977,7 @@
 	unsigned int value)
 {
 	int ret;
+	struct wcd9xxx *wcd9xxx = codec->control_data;
 
 	if (reg == SND_SOC_NOPM)
 		return 0;
@@ -2933,13 +2991,14 @@
 				reg, ret);
 	}
 
-	return wcd9xxx_reg_write(codec->control_data, reg, value);
+	return wcd9xxx_reg_write(&wcd9xxx->core_res, reg, value);
 }
 static unsigned int tapan_read(struct snd_soc_codec *codec,
 				unsigned int reg)
 {
 	unsigned int val;
 	int ret;
+	struct wcd9xxx *wcd9xxx = codec->control_data;
 
 	if (reg == SND_SOC_NOPM)
 		return 0;
@@ -2956,7 +3015,7 @@
 				reg, ret);
 	}
 
-	val = wcd9xxx_reg_read(codec->control_data, reg);
+	val = wcd9xxx_reg_read(&wcd9xxx->core_res, reg);
 	return val;
 }
 
@@ -2988,6 +3047,28 @@
 	}
 }
 
+static void tapan_set_vdd_cx_current(struct snd_soc_codec *codec,
+			int current_uA)
+{
+	struct regulator *cx_regulator;
+	int ret;
+
+	cx_regulator  = tapan_codec_find_regulator(codec,
+				"cdc-vdd-cx");
+
+	if (!cx_regulator) {
+		dev_err(codec->dev, "%s: Regulator %s not defined\n",
+			__func__, "cdc-vdd-cx-supply");
+		return;
+	}
+
+	ret = regulator_set_optimum_mode(cx_regulator, current_uA);
+	if (ret < 0)
+		dev_err(codec->dev,
+			"%s: Failed to set vdd_cx current to %d\n",
+			__func__, current_uA);
+}
+
 int tapan_mclk_enable(struct snd_soc_codec *codec, int mclk_enable, bool dapm)
 {
 	struct tapan_priv *tapan = snd_soc_codec_get_drvdata(codec);
@@ -2997,6 +3078,7 @@
 
 	WCD9XXX_BG_CLK_LOCK(&tapan->resmgr);
 	if (mclk_enable) {
+		tapan_set_vdd_cx_current(codec, TAPAN_VDD_CX_OPTIMAL_UA);
 		wcd9xxx_resmgr_get_bandgap(&tapan->resmgr,
 					   WCD9XXX_BANDGAP_AUDIO_MODE);
 		wcd9xxx_resmgr_get_clk_block(&tapan->resmgr, WCD9XXX_CLK_MCLK);
@@ -3005,6 +3087,8 @@
 		wcd9xxx_resmgr_put_clk_block(&tapan->resmgr, WCD9XXX_CLK_MCLK);
 		wcd9xxx_resmgr_put_bandgap(&tapan->resmgr,
 					   WCD9XXX_BANDGAP_AUDIO_MODE);
+		/* Set the vdd cx power rail sleep mode current */
+		tapan_set_vdd_cx_current(codec, TAPAN_VDD_CX_SLEEP_UA);
 	}
 	WCD9XXX_BG_CLK_UNLOCK(&tapan->resmgr);
 
@@ -3439,6 +3523,93 @@
 	.get_channel_map = tapan_get_channel_map,
 };
 
+static struct snd_soc_dai_driver tapan9302_dai[] = {
+	{
+		.name = "tapan9302_rx1",
+		.id = AIF1_PB,
+		.playback = {
+			.stream_name = "AIF1 Playback",
+			.rates = WCD9302_RATES,
+			.formats = TAPAN_FORMATS,
+			.rate_max = 48000,
+			.rate_min = 8000,
+			.channels_min = 1,
+			.channels_max = 2,
+		},
+		.ops = &tapan_dai_ops,
+	},
+	{
+		.name = "tapan9302_tx1",
+		.id = AIF1_CAP,
+		.capture = {
+			.stream_name = "AIF1 Capture",
+			.rates = WCD9302_RATES,
+			.formats = TAPAN_FORMATS,
+			.rate_max = 48000,
+			.rate_min = 8000,
+			.channels_min = 1,
+			.channels_max = 4,
+		},
+		.ops = &tapan_dai_ops,
+	},
+	{
+		.name = "tapan9302_rx2",
+		.id = AIF2_PB,
+		.playback = {
+			.stream_name = "AIF2 Playback",
+			.rates = WCD9302_RATES,
+			.formats = TAPAN_FORMATS,
+			.rate_min = 8000,
+			.rate_max = 48000,
+			.channels_min = 1,
+			.channels_max = 2,
+		},
+		.ops = &tapan_dai_ops,
+	},
+	{
+		.name = "tapan9302_tx2",
+		.id = AIF2_CAP,
+		.capture = {
+			.stream_name = "AIF2 Capture",
+			.rates = WCD9302_RATES,
+			.formats = TAPAN_FORMATS,
+			.rate_max = 48000,
+			.rate_min = 8000,
+			.channels_min = 1,
+			.channels_max = 4,
+		},
+		.ops = &tapan_dai_ops,
+	},
+	{
+		.name = "tapan9302_tx3",
+		.id = AIF3_CAP,
+		.capture = {
+			.stream_name = "AIF3 Capture",
+			.rates = WCD9302_RATES,
+			.formats = TAPAN_FORMATS,
+			.rate_max = 48000,
+			.rate_min = 8000,
+			.channels_min = 1,
+			.channels_max = 2,
+		},
+		.ops = &tapan_dai_ops,
+	},
+	{
+		.name = "tapan9302_rx3",
+		.id = AIF3_PB,
+		.playback = {
+			.stream_name = "AIF3 Playback",
+			.rates = WCD9302_RATES,
+			.formats = TAPAN_FORMATS,
+			.rate_min = 8000,
+			.rate_max = 48000,
+			.channels_min = 1,
+			.channels_max = 2,
+		},
+		.ops = &tapan_dai_ops,
+	},
+};
+
 static struct snd_soc_dai_driver tapan_dai[] = {
 	{
 		.name = "tapan_rx1",
@@ -3844,10 +4015,93 @@
 	return 0;
 }
 
+static const struct snd_soc_dapm_widget tapan_9306_dapm_widgets[] = {
+	/* RX4 MIX1 mux inputs */
+	SND_SOC_DAPM_MUX("RX4 MIX1 INP1", SND_SOC_NOPM, 0, 0,
+		&rx4_mix1_inp1_mux),
+	SND_SOC_DAPM_MUX("RX4 MIX1 INP2", SND_SOC_NOPM, 0, 0,
+		&rx4_mix1_inp2_mux),
+	SND_SOC_DAPM_MUX("RX4 MIX1 INP3", SND_SOC_NOPM, 0, 0,
+		&rx4_mix1_inp2_mux),
+
+	/* RX4 MIX2 mux inputs */
+	SND_SOC_DAPM_MUX("RX4 MIX2 INP1", SND_SOC_NOPM, 0, 0,
+		&rx4_mix2_inp1_mux),
+	SND_SOC_DAPM_MUX("RX4 MIX2 INP2", SND_SOC_NOPM, 0, 0,
+		&rx4_mix2_inp2_mux),
+
+	SND_SOC_DAPM_MIXER("RX4 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+	SND_SOC_DAPM_MIXER_E("RX4 MIX2", TAPAN_A_CDC_CLK_RX_B1_CTL, 3, 0, NULL,
+		0, tapan_codec_enable_interpolator, SND_SOC_DAPM_PRE_PMU |
+		SND_SOC_DAPM_POST_PMU),
+
+	SND_SOC_DAPM_MUX_E("DEC3 MUX", TAPAN_A_CDC_CLK_TX_CLK_EN_B1_CTL, 2, 0,
+		&dec3_mux, tapan_codec_enable_dec,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+		SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_MUX_E("DEC4 MUX", TAPAN_A_CDC_CLK_TX_CLK_EN_B1_CTL, 3, 0,
+		&dec4_mux, tapan_codec_enable_dec,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+		SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_SUPPLY("COMP0_CLK", SND_SOC_NOPM, 0, 0,
+		tapan_config_compander, SND_SOC_DAPM_PRE_PMU |
+		SND_SOC_DAPM_PRE_PMD),
+	SND_SOC_DAPM_SUPPLY("COMP1_CLK", SND_SOC_NOPM, 1, 0,
+		tapan_config_compander, SND_SOC_DAPM_PRE_PMU |
+		SND_SOC_DAPM_PRE_PMD),
+	SND_SOC_DAPM_SUPPLY("COMP2_CLK", SND_SOC_NOPM, 2, 0,
+		tapan_config_compander, SND_SOC_DAPM_PRE_PMU |
+		SND_SOC_DAPM_PRE_PMD),
+
+	SND_SOC_DAPM_INPUT("AMIC5"),
+	SND_SOC_DAPM_ADC_E("ADC5", NULL, TAPAN_A_TX_5_EN, 7, 0,
+		tapan_codec_enable_adc, SND_SOC_DAPM_POST_PMU),
+
+	SND_SOC_DAPM_MUX("ANC1 MUX", SND_SOC_NOPM, 0, 0, &anc1_mux),
+	SND_SOC_DAPM_MUX("ANC2 MUX", SND_SOC_NOPM, 0, 0, &anc2_mux),
+
+	SND_SOC_DAPM_OUTPUT("ANC HEADPHONE"),
+	SND_SOC_DAPM_PGA_E("ANC HPHL", SND_SOC_NOPM, 5, 0, NULL, 0,
+		tapan_codec_enable_anc_hph,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD |
+		SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_POST_PMU),
+	SND_SOC_DAPM_PGA_E("ANC HPHR", SND_SOC_NOPM, 4, 0, NULL, 0,
+		tapan_codec_enable_anc_hph, SND_SOC_DAPM_PRE_PMU |
+		SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+		SND_SOC_DAPM_POST_PMU),
+	SND_SOC_DAPM_OUTPUT("ANC EAR"),
+	SND_SOC_DAPM_PGA_E("ANC EAR PA", SND_SOC_NOPM, 0, 0, NULL, 0,
+		tapan_codec_enable_anc_ear,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD |
+		SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_MUX("ANC1 FB MUX", SND_SOC_NOPM, 0, 0, &anc1_fb_mux),
+
+	SND_SOC_DAPM_MICBIAS_E("MIC BIAS3 External", TAPAN_A_MICB_3_CTL, 7, 0,
+		tapan_codec_enable_micbias, SND_SOC_DAPM_PRE_PMU |
+		SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_MICBIAS_E("MIC BIAS3 Internal1", TAPAN_A_MICB_3_CTL, 7, 0,
+		tapan_codec_enable_micbias, SND_SOC_DAPM_PRE_PMU |
+		SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_MICBIAS_E("MIC BIAS3 Internal2", TAPAN_A_MICB_3_CTL, 7, 0,
+		tapan_codec_enable_micbias, SND_SOC_DAPM_PRE_PMU |
+		SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_ADC_E("DMIC3", NULL, SND_SOC_NOPM, 0, 0,
+		tapan_codec_enable_dmic, SND_SOC_DAPM_PRE_PMU |
+		SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_ADC_E("DMIC4", NULL, SND_SOC_NOPM, 0, 0,
+		tapan_codec_enable_dmic, SND_SOC_DAPM_PRE_PMU |
+		SND_SOC_DAPM_POST_PMD),
+};
+
 /* Todo: Have seperate dapm widgets for I2S and Slimbus.
  * Might Need to have callbacks registered only for slimbus
  */
-static const struct snd_soc_dapm_widget tapan_dapm_widgets[] = {
+static const struct snd_soc_dapm_widget tapan_common_dapm_widgets[] = {
 
 	SND_SOC_DAPM_AIF_IN_E("AIF1 PB", "AIF1 Playback", 0, SND_SOC_NOPM,
 				AIF1_PB, 0, tapan_codec_enable_slimrx,
@@ -3901,14 +4155,6 @@
 	SND_SOC_DAPM_MUX("RX3 MIX1 INP3", SND_SOC_NOPM, 0, 0,
 		&rx3_mix1_inp2_mux),
 
-	/* RX4 MIX1 mux inputs */
-	SND_SOC_DAPM_MUX("RX4 MIX1 INP1", SND_SOC_NOPM, 0, 0,
-		&rx4_mix1_inp1_mux),
-	SND_SOC_DAPM_MUX("RX4 MIX1 INP2", SND_SOC_NOPM, 0, 0,
-		&rx4_mix1_inp2_mux),
-	SND_SOC_DAPM_MUX("RX4 MIX1 INP3", SND_SOC_NOPM, 0, 0,
-		&rx4_mix1_inp2_mux),
-
 	/* RX1 MIX2 mux inputs */
 	SND_SOC_DAPM_MUX("RX1 MIX2 INP1", SND_SOC_NOPM, 0, 0,
 		&rx1_mix2_inp1_mux),
@@ -3921,16 +4167,8 @@
 	SND_SOC_DAPM_MUX("RX2 MIX2 INP2", SND_SOC_NOPM, 0, 0,
 		&rx2_mix2_inp2_mux),
 
-	/* RX4 MIX2 mux inputs */
-	SND_SOC_DAPM_MUX("RX4 MIX2 INP1", SND_SOC_NOPM, 0, 0,
-		&rx4_mix2_inp1_mux),
-	SND_SOC_DAPM_MUX("RX4 MIX2 INP2", SND_SOC_NOPM, 0, 0,
-		&rx4_mix2_inp2_mux),
-
-
 	SND_SOC_DAPM_MIXER("RX1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0),
 	SND_SOC_DAPM_MIXER("RX2 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0),
-	SND_SOC_DAPM_MIXER("RX4 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0),
 
 	SND_SOC_DAPM_MIXER_E("RX1 MIX2", TAPAN_A_CDC_CLK_RX_B1_CTL, 0, 0, NULL,
 		0, tapan_codec_enable_interpolator, SND_SOC_DAPM_PRE_PMU |
@@ -3941,9 +4179,6 @@
 	SND_SOC_DAPM_MIXER_E("RX3 MIX1", TAPAN_A_CDC_CLK_RX_B1_CTL, 2, 0, NULL,
 		0, tapan_codec_enable_interpolator, SND_SOC_DAPM_PRE_PMU |
 		SND_SOC_DAPM_POST_PMU),
-	SND_SOC_DAPM_MIXER_E("RX4 MIX2", TAPAN_A_CDC_CLK_RX_B1_CTL, 3, 0, NULL,
-		0, tapan_codec_enable_interpolator, SND_SOC_DAPM_PRE_PMU |
-		SND_SOC_DAPM_POST_PMU),
 
 	SND_SOC_DAPM_MIXER("RX1 CHAIN", TAPAN_A_CDC_RX1_B6_CTL, 5, 0,
 						NULL, 0),
@@ -4004,6 +4239,13 @@
 	SND_SOC_DAPM_MUX("RDAC5 MUX", SND_SOC_NOPM, 0, 0,
 		&rx_dac5_mux),
 
+	/* LINEOUT1*/
+	SND_SOC_DAPM_MUX("RDAC4 MUX", SND_SOC_NOPM, 0, 0,
+		&rx_dac4_mux),
+
+	SND_SOC_DAPM_MUX("RDAC3 MUX", SND_SOC_NOPM, 0, 0,
+		&rx_dac3_mux),
+
 	SND_SOC_DAPM_DAC_E("LINEOUT2 DAC", NULL, TAPAN_A_RX_LINE_2_DAC_CTL, 7, 0
 		, tapan_lineout_dac_event,
 		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
@@ -4066,29 +4308,9 @@
 		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
 		SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
 
-	SND_SOC_DAPM_MUX_E("DEC3 MUX", TAPAN_A_CDC_CLK_TX_CLK_EN_B1_CTL, 2, 0,
-		&dec3_mux, tapan_codec_enable_dec,
-		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
-		SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
-
-	SND_SOC_DAPM_MUX_E("DEC4 MUX", TAPAN_A_CDC_CLK_TX_CLK_EN_B1_CTL, 3, 0,
-		&dec4_mux, tapan_codec_enable_dec,
-		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
-		SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
-
 	SND_SOC_DAPM_SUPPLY("LDO_H", TAPAN_A_LDO_H_MODE_1, 7, 0,
 		tapan_codec_enable_ldo_h, SND_SOC_DAPM_POST_PMU),
 
-	SND_SOC_DAPM_SUPPLY("COMP0_CLK", SND_SOC_NOPM, 0, 0,
-		tapan_config_compander, SND_SOC_DAPM_PRE_PMU |
-		SND_SOC_DAPM_PRE_PMD),
-	SND_SOC_DAPM_SUPPLY("COMP1_CLK", SND_SOC_NOPM, 1, 0,
-		tapan_config_compander, SND_SOC_DAPM_PRE_PMU |
-		SND_SOC_DAPM_PRE_PMD),
-	SND_SOC_DAPM_SUPPLY("COMP2_CLK", SND_SOC_NOPM, 2, 0,
-		tapan_config_compander, SND_SOC_DAPM_PRE_PMU |
-		SND_SOC_DAPM_PRE_PMD),
-
 	SND_SOC_DAPM_INPUT("AMIC1"),
 	SND_SOC_DAPM_MICBIAS_E("MIC BIAS1 External", TAPAN_A_MICB_1_CTL, 7, 0,
 		tapan_codec_enable_micbias, SND_SOC_DAPM_PRE_PMU |
@@ -4117,29 +4339,6 @@
 		tapan_codec_enable_adc, SND_SOC_DAPM_PRE_PMU |
 		SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
 
-	SND_SOC_DAPM_INPUT("AMIC5"),
-	SND_SOC_DAPM_ADC_E("ADC5", NULL, TAPAN_A_TX_5_EN, 7, 0,
-		tapan_codec_enable_adc, SND_SOC_DAPM_POST_PMU),
-
-	SND_SOC_DAPM_MUX("ANC1 MUX", SND_SOC_NOPM, 0, 0, &anc1_mux),
-	SND_SOC_DAPM_MUX("ANC2 MUX", SND_SOC_NOPM, 0, 0, &anc2_mux),
-
-	SND_SOC_DAPM_OUTPUT("ANC HEADPHONE"),
-	SND_SOC_DAPM_PGA_E("ANC HPHL", SND_SOC_NOPM, 5, 0, NULL, 0,
-		tapan_codec_enable_anc_hph,
-		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD |
-		SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_POST_PMU),
-	SND_SOC_DAPM_PGA_E("ANC HPHR", SND_SOC_NOPM, 4, 0, NULL, 0,
-		tapan_codec_enable_anc_hph, SND_SOC_DAPM_PRE_PMU |
-		SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
-		SND_SOC_DAPM_POST_PMU),
-	SND_SOC_DAPM_OUTPUT("ANC EAR"),
-	SND_SOC_DAPM_PGA_E("ANC EAR PA", SND_SOC_NOPM, 0, 0, NULL, 0,
-		tapan_codec_enable_anc_ear,
-		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD |
-		SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
-	SND_SOC_DAPM_MUX("ANC1 FB MUX", SND_SOC_NOPM, 0, 0, &anc1_fb_mux),
-
 	SND_SOC_DAPM_INPUT("AMIC2"),
 	SND_SOC_DAPM_MICBIAS_E("MIC BIAS2 External", TAPAN_A_MICB_2_CTL, 7, 0,
 		tapan_codec_enable_micbias, SND_SOC_DAPM_PRE_PMU |
@@ -4153,15 +4352,6 @@
 	SND_SOC_DAPM_MICBIAS_E("MIC BIAS2 Internal3", TAPAN_A_MICB_2_CTL, 7, 0,
 		tapan_codec_enable_micbias, SND_SOC_DAPM_PRE_PMU |
 		SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
-	SND_SOC_DAPM_MICBIAS_E("MIC BIAS3 External", TAPAN_A_MICB_3_CTL, 7, 0,
-		tapan_codec_enable_micbias, SND_SOC_DAPM_PRE_PMU |
-		SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
-	SND_SOC_DAPM_MICBIAS_E("MIC BIAS3 Internal1", TAPAN_A_MICB_3_CTL, 7, 0,
-		tapan_codec_enable_micbias, SND_SOC_DAPM_PRE_PMU |
-		SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
-	SND_SOC_DAPM_MICBIAS_E("MIC BIAS3 Internal2", TAPAN_A_MICB_3_CTL, 7, 0,
-		tapan_codec_enable_micbias, SND_SOC_DAPM_PRE_PMU |
-		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, tapan_codec_enable_slimtx,
@@ -4184,14 +4374,6 @@
 		tapan_codec_enable_dmic, SND_SOC_DAPM_PRE_PMU |
 		SND_SOC_DAPM_POST_PMD),
 
-	SND_SOC_DAPM_ADC_E("DMIC3", NULL, SND_SOC_NOPM, 0, 0,
-		tapan_codec_enable_dmic, SND_SOC_DAPM_PRE_PMU |
-		SND_SOC_DAPM_POST_PMD),
-
-	SND_SOC_DAPM_ADC_E("DMIC4", NULL, SND_SOC_NOPM, 0, 0,
-		tapan_codec_enable_dmic, SND_SOC_DAPM_PRE_PMU |
-		SND_SOC_DAPM_POST_PMD),
-
 	/* Sidetone */
 	SND_SOC_DAPM_MUX("IIR1 INP1 MUX", SND_SOC_NOPM, 0, 0, &iir1_inp1_mux),
 	SND_SOC_DAPM_PGA("IIR1", TAPAN_A_CDC_CLK_SD_CTL, 0, 0, NULL, 0),
@@ -4635,8 +4817,10 @@
 {
 	int ret = 0;
 	struct snd_soc_codec *codec = tapan->codec;
+	struct wcd9xxx *wcd9xxx = codec->control_data;
+	struct wcd9xxx_core_resource *core_res = &wcd9xxx->core_res;
 
-	ret = wcd9xxx_request_irq(codec->control_data, WCD9XXX_IRQ_SLIMBUS,
+	ret = wcd9xxx_request_irq(core_res, WCD9XXX_IRQ_SLIMBUS,
 				  tapan_slimbus_irq, "SLIMBUS Slave", tapan);
 	if (ret)
 		pr_err("%s: Failed to request irq %d\n", __func__,
@@ -4650,7 +4834,9 @@
 static void tapan_cleanup_irqs(struct tapan_priv *tapan)
 {
 	struct snd_soc_codec *codec = tapan->codec;
-	wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_SLIMBUS, tapan);
+	struct wcd9xxx *wcd9xxx = codec->control_data;
+	struct wcd9xxx_core_resource *core_res = &wcd9xxx->core_res;
+	wcd9xxx_free_irq(core_res, WCD9XXX_IRQ_SLIMBUS, tapan);
 }
 
 
@@ -4694,6 +4880,8 @@
 
 	cfilt_mode.cur_mode_val =
 		snd_soc_read(codec, mbhc->mbhc_bias_regs.cfilt_ctl) & 0x30;
+	cfilt_mode.reg_mask = 0x30;
+
 	return cfilt_mode;
 }
 
@@ -4705,8 +4893,15 @@
 
 static void tapan_free_irq(struct wcd9xxx_mbhc *mbhc)
 {
-	void *cdata = mbhc->codec->control_data;
-	wcd9xxx_free_irq(cdata, WCD9306_IRQ_MBHC_JACK_SWITCH, mbhc);
+	struct wcd9xxx *wcd9xxx = mbhc->codec->control_data;
+	struct wcd9xxx_core_resource *core_res =
+			&wcd9xxx->core_res;
+	wcd9xxx_free_irq(core_res, WCD9306_IRQ_MBHC_JACK_SWITCH, mbhc);
+}
+
+enum wcd9xxx_cdc_type tapan_get_cdc_type(void)
+{
+	return WCD9XXX_CDC_TYPE_TAPAN;
 }
 
 static const struct wcd9xxx_mbhc_cb mbhc_cb = {
@@ -4717,6 +4912,7 @@
 	.switch_cfilt_mode = tapan_codec_switch_cfilt_mode,
 	.select_cfilt = tapan_select_cfilt,
 	.free_irq = tapan_free_irq,
+	.get_cdc_type = tapan_get_cdc_type,
 };
 
 int tapan_hs_detect(struct snd_soc_codec *codec,
@@ -4725,7 +4921,17 @@
 	struct tapan_priv *tapan = snd_soc_codec_get_drvdata(codec);
 	return wcd9xxx_mbhc_start(&tapan->mbhc, mbhc_cfg);
 }
-EXPORT_SYMBOL_GPL(tapan_hs_detect);
+EXPORT_SYMBOL(tapan_hs_detect);
+
+static int tapan_device_down(struct wcd9xxx *wcd9xxx)
+{
+	struct snd_soc_codec *codec;
+
+	codec = (struct snd_soc_codec *)(wcd9xxx->ssr_priv);
+	snd_soc_card_change_online_state(codec->card, 0);
+
+	return 0;
+}
 
 static int tapan_post_reset_cb(struct wcd9xxx *wcd9xxx)
 {
@@ -4736,8 +4942,10 @@
 
 	codec = (struct snd_soc_codec *)(wcd9xxx->ssr_priv);
 	tapan = snd_soc_codec_get_drvdata(codec);
-	mutex_lock(&codec->mutex);
 
+	snd_soc_card_change_online_state(codec->card, 1);
+
+	mutex_lock(&codec->mutex);
 	if (codec->reg_def_copy) {
 		pr_debug("%s: Update ASOC cache", __func__);
 		kfree(codec->reg_cache);
@@ -4777,6 +4985,12 @@
 		pr_err("%s: mbhc init failed %d\n", __func__, ret);
 	else
 		wcd9xxx_mbhc_start(&tapan->mbhc, tapan->mbhc.mbhc_cfg);
+
+	tapan_cleanup_irqs(tapan);
+	ret = tapan_setup_irqs(tapan);
+	if (ret)
+		pr_err("%s: Failed to setup irq: %d\n", __func__, ret);
+
 	mutex_unlock(&codec->mutex);
 	return ret;
 }
@@ -4785,9 +4999,12 @@
 };
 
 static int wcd9xxx_ssr_register(struct wcd9xxx *control,
-		int (*post_reset_cb)(struct wcd9xxx *wcd9xxx), void *priv)
+				int (*device_down_cb)(struct wcd9xxx *wcd9xxx),
+				int (*device_up_cb)(struct wcd9xxx *wcd9xxx),
+				void *priv)
 {
-	control->post_reset = post_reset_cb;
+	control->dev_down = device_down_cb;
+	control->post_reset = device_up_cb;
 	control->ssr_priv = priv;
 	return 0;
 }
@@ -4807,6 +5024,82 @@
 	return NULL;
 }
 
+static void tapan_enable_config_rco(struct wcd9xxx *core, bool enable)
+{
+	struct wcd9xxx_core_resource *core_res = &core->core_res;
+
+	if (enable) {
+		/* Enable RC Oscillator */
+		wcd9xxx_reg_update(core, WCD9XXX_A_RC_OSC_FREQ, 0x10, 0x00);
+		wcd9xxx_reg_write(core_res, WCD9XXX_A_BIAS_OSC_BG_CTL, 0x17);
+		usleep_range(5, 5);
+		wcd9xxx_reg_update(core, WCD9XXX_A_RC_OSC_FREQ, 0x80, 0x80);
+		wcd9xxx_reg_update(core, WCD9XXX_A_RC_OSC_TEST, 0x80, 0x80);
+		usleep_range(10, 10);
+		wcd9xxx_reg_update(core, WCD9XXX_A_RC_OSC_TEST, 0x80, 0x00);
+		usleep_range(20, 20);
+		wcd9xxx_reg_update(core, WCD9XXX_A_CLK_BUFF_EN1, 0x08, 0x08);
+		/* Enable MCLK and wait 1ms till it gets enabled */
+		wcd9xxx_reg_write(core_res, WCD9XXX_A_CLK_BUFF_EN2, 0x02);
+		usleep_range(1000, 1000);
+		/* Enable CLK BUFF and wait for 1.2ms */
+		wcd9xxx_reg_update(core, WCD9XXX_A_CLK_BUFF_EN1, 0x01, 0x01);
+		usleep_range(1000, 1200);
+
+		wcd9xxx_reg_update(core, WCD9XXX_A_CLK_BUFF_EN2, 0x02, 0x00);
+		wcd9xxx_reg_update(core, WCD9XXX_A_CLK_BUFF_EN2, 0x04, 0x04);
+		wcd9xxx_reg_update(core, WCD9XXX_A_CDC_CLK_MCLK_CTL,
+				   0x01, 0x01);
+		usleep_range(50, 50);
+	} else {
+		wcd9xxx_reg_update(core, WCD9XXX_A_CLK_BUFF_EN2, 0x04, 0x00);
+		usleep_range(50, 50);
+		wcd9xxx_reg_update(core, WCD9XXX_A_CLK_BUFF_EN2, 0x02, 0x02);
+		wcd9xxx_reg_update(core, WCD9XXX_A_CLK_BUFF_EN1, 0x05, 0x00);
+		usleep_range(50, 50);
+	}
+
+}
+
+static bool tapan_check_wcd9306(struct device *cdc_dev, bool sensed)
+{
+	struct wcd9xxx *core = dev_get_drvdata(cdc_dev->parent);
+	u8 reg_val;
+	bool ret = true;
+	unsigned long timeout;
+	bool timedout;
+	struct wcd9xxx_core_resource *core_res = &core->core_res;
+
+	if (!core) {
+		dev_err(cdc_dev, "%s: core not initialized\n", __func__);
+		return -EINVAL;
+	}
+
+	tapan_enable_config_rco(core, 1);
+
+	if (sensed == false) {
+		reg_val = wcd9xxx_reg_read(core_res, TAPAN_A_QFUSE_CTL);
+		wcd9xxx_reg_write(core_res, TAPAN_A_QFUSE_CTL,
+					(reg_val | 0x03));
+	}
+
+	timeout = jiffies + HZ;
+	do {
+		if ((wcd9xxx_reg_read(core_res, TAPAN_A_QFUSE_STATUS)))
+			break;
+	} while (!(timedout = time_after(jiffies, timeout)));
+
+	if (wcd9xxx_reg_read(core_res, TAPAN_A_QFUSE_DATA_OUT1) ||
+	    wcd9xxx_reg_read(core_res, TAPAN_A_QFUSE_DATA_OUT2)) {
+		dev_info(cdc_dev, "%s: wcd9302 detected\n", __func__);
+		ret = false;
+	} else
+		dev_info(cdc_dev, "%s: wcd9306 detected\n", __func__);
+
+	tapan_enable_config_rco(core, 0);
+	return ret;
+};
+
 static int tapan_codec_probe(struct snd_soc_codec *codec)
 {
 	struct wcd9xxx *control;
@@ -4817,11 +5110,13 @@
 	int ret = 0;
 	int i, rco_clk_rate;
 	void *ptr = NULL;
+	struct wcd9xxx_core_resource *core_res;
 
 	codec->control_data = dev_get_drvdata(codec->dev->parent);
 	control = codec->control_data;
 
-	wcd9xxx_ssr_register(control, tapan_post_reset_cb, (void *)codec);
+	wcd9xxx_ssr_register(control, tapan_device_down,
+			     tapan_post_reset_cb, (void *)codec);
 
 	dev_info(codec->dev, "%s()\n", __func__);
 
@@ -4841,9 +5136,10 @@
 
 	/* codec resmgr module init */
 	wcd9xxx = codec->control_data;
+	core_res = &wcd9xxx->core_res;
 	pdata = dev_get_platdata(codec->dev->parent);
-	ret = wcd9xxx_resmgr_init(&tapan->resmgr, codec, wcd9xxx, pdata,
-				  &tapan_reg_address);
+	ret = wcd9xxx_resmgr_init(&tapan->resmgr, codec, core_res, pdata,
+				  &tapan_reg_address, WCD9XXX_CDC_TYPE_TAPAN);
 	if (ret) {
 		pr_err("%s: wcd9xxx init failed %d\n", __func__, ret);
 		return ret;
@@ -4924,6 +5220,18 @@
 		}
 	}
 
+	if (tapan_check_wcd9306(codec->dev, false) == true) {
+		snd_soc_add_codec_controls(codec, tapan_9306_snd_controls,
+					   ARRAY_SIZE(tapan_9306_snd_controls));
+		snd_soc_dapm_new_controls(dapm, tapan_9306_dapm_widgets,
+					  ARRAY_SIZE(tapan_9306_dapm_widgets));
+		snd_soc_dapm_add_routes(dapm, wcd9306_map,
+					ARRAY_SIZE(wcd9306_map));
+	} else {
+		snd_soc_dapm_add_routes(dapm, wcd9302_map,
+					ARRAY_SIZE(wcd9302_map));
+	}
+
 	control->num_rx_port = TAPAN_RX_MAX;
 	control->rx_chs = ptr;
 	memcpy(control->rx_chs, tapan_rx_chs, sizeof(tapan_rx_chs));
@@ -5000,10 +5308,10 @@
 	.reg_cache_default = tapan_reset_reg_defaults,
 	.reg_word_size = 1,
 
-	.controls = tapan_snd_controls,
-	.num_controls = ARRAY_SIZE(tapan_snd_controls),
-	.dapm_widgets = tapan_dapm_widgets,
-	.num_dapm_widgets = ARRAY_SIZE(tapan_dapm_widgets),
+	.controls = tapan_common_snd_controls,
+	.num_controls = ARRAY_SIZE(tapan_common_snd_controls),
+	.dapm_widgets = tapan_common_dapm_widgets,
+	.num_dapm_widgets = ARRAY_SIZE(tapan_common_dapm_widgets),
 	.dapm_routes = audio_map,
 	.num_dapm_routes = ARRAY_SIZE(audio_map),
 };
@@ -5034,12 +5342,35 @@
 static int __devinit tapan_probe(struct platform_device *pdev)
 {
 	int ret = 0;
-	if (wcd9xxx_get_intf_type() == WCD9XXX_INTERFACE_TYPE_SLIMBUS)
-		ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_tapan,
-			tapan_dai, ARRAY_SIZE(tapan_dai));
-	else if (wcd9xxx_get_intf_type() == WCD9XXX_INTERFACE_TYPE_I2C)
-		ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_tapan,
-			tapan_i2s_dai, ARRAY_SIZE(tapan_i2s_dai));
+	bool is_wcd9306;
+
+	is_wcd9306 = tapan_check_wcd9306(&pdev->dev, false);
+	if (is_wcd9306 < 0) {
+		dev_info(&pdev->dev, "%s: cannot find codec type, default to 9306\n",
+			 __func__);
+		is_wcd9306 = true;
+	}
+
+	if (!is_wcd9306) {
+		if (wcd9xxx_get_intf_type() == WCD9XXX_INTERFACE_TYPE_SLIMBUS)
+			ret = snd_soc_register_codec(&pdev->dev,
+				&soc_codec_dev_tapan,
+				tapan9302_dai, ARRAY_SIZE(tapan9302_dai));
+		else if (wcd9xxx_get_intf_type() == WCD9XXX_INTERFACE_TYPE_I2C)
+			ret = snd_soc_register_codec(&pdev->dev,
+				&soc_codec_dev_tapan,
+				tapan_i2s_dai, ARRAY_SIZE(tapan_i2s_dai));
+	} else {
+		if (wcd9xxx_get_intf_type() == WCD9XXX_INTERFACE_TYPE_SLIMBUS)
+			ret = snd_soc_register_codec(&pdev->dev,
+				&soc_codec_dev_tapan,
+				tapan_dai, ARRAY_SIZE(tapan_dai));
+		else if (wcd9xxx_get_intf_type() == WCD9XXX_INTERFACE_TYPE_I2C)
+			ret = snd_soc_register_codec(&pdev->dev,
+				&soc_codec_dev_tapan,
+				tapan_i2s_dai, ARRAY_SIZE(tapan_i2s_dai));
+	}
+
 	return ret;
 }
 static int __devexit tapan_remove(struct platform_device *pdev)
diff --git a/sound/soc/codecs/wcd9310.c b/sound/soc/codecs/wcd9310.c
index 212924fd..673b634 100644
--- a/sound/soc/codecs/wcd9310.c
+++ b/sound/soc/codecs/wcd9310.c
@@ -5949,11 +5949,12 @@
 {
 	int r = 0;
 	struct wcd9xxx *core = dev_get_drvdata(tabla->codec->dev->parent);
+	struct wcd9xxx_core_resource *core_res = &core->core_res;
 
 	if (cancel_delayed_work_sync(&tabla->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);
+		wcd9xxx_unlock_sleep(core_res);
 		r = 1;
 	}
 	return r;
@@ -6332,12 +6333,14 @@
 	short bias_value;
 	int dce_mv, sta_mv;
 	struct wcd9xxx *core;
+	struct wcd9xxx_core_resource *core_res;
 
 	pr_debug("%s:\n", __func__);
 
 	delayed_work = to_delayed_work(work);
 	tabla = container_of(delayed_work, struct tabla_priv, mbhc_btn_dwork);
 	core = dev_get_drvdata(tabla->codec->dev->parent);
+	core_res = &core->core_res;
 
 	if (tabla) {
 		if (tabla->mbhc_cfg.button_jack) {
@@ -6360,7 +6363,7 @@
 	}
 
 	pr_debug("%s: leave\n", __func__);
-	wcd9xxx_unlock_sleep(core);
+	wcd9xxx_unlock_sleep(core_res);
 }
 
 static u16 tabla_get_cfilt_reg(struct snd_soc_codec *codec, u8 cfilt)
@@ -6816,6 +6819,7 @@
 	short btnmeas[d->n_btn_meas + 1];
 	struct snd_soc_codec *codec = priv->codec;
 	struct wcd9xxx *core = dev_get_drvdata(priv->codec->dev->parent);
+	struct wcd9xxx_core_resource *core_res = &core->core_res;
 	int n_btn_meas = d->n_btn_meas;
 	u8 mbhc_status = snd_soc_read(codec, TABLA_A_CDC_MBHC_B1_STATUS) & 0x3E;
 
@@ -6917,12 +6921,12 @@
 		tabla_mbhc_set_rel_thres(codec, btn_high[btn]);
 		mask = tabla_get_button_mask(btn);
 		priv->buttons_pressed |= mask;
-		wcd9xxx_lock_sleep(core);
+		wcd9xxx_lock_sleep(core_res);
 		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);
+			wcd9xxx_unlock_sleep(core_res);
 		}
 	} else {
 		pr_debug("%s: bogus button press, too short press?\n",
@@ -7248,9 +7252,11 @@
 static void tabla_schedule_hs_detect_plug(struct tabla_priv *tabla,
 	struct work_struct *correct_plug_work)
 {
+	struct wcd9xxx *core = tabla->codec->control_data;
+	struct wcd9xxx_core_resource *core_res = &core->core_res;
 	pr_debug("%s: scheduling tabla_hs_correct_gpio_plug\n", __func__);
 	tabla->hs_detect_work_stop = false;
-	wcd9xxx_lock_sleep(tabla->codec->control_data);
+	wcd9xxx_lock_sleep(core_res);
 	schedule_work(correct_plug_work);
 }
 
@@ -7258,13 +7264,15 @@
 static void tabla_cancel_hs_detect_plug(struct tabla_priv *tabla,
 		struct work_struct *correct_plug_work)
 {
+	struct wcd9xxx *core = tabla->codec->control_data;
+	struct wcd9xxx_core_resource *core_res = &core->core_res;
 	pr_debug("%s: canceling hs_correct_plug_work\n", __func__);
 	tabla->hs_detect_work_stop = true;
 	wmb();
 	TABLA_RELEASE_LOCK(tabla->codec_resource_lock);
 	if (cancel_work_sync(correct_plug_work)) {
 		pr_debug("%s: hs_correct_plug_work is canceled\n", __func__);
-		wcd9xxx_unlock_sleep(tabla->codec->control_data);
+		wcd9xxx_unlock_sleep(core_res);
 	}
 	TABLA_ACQUIRE_LOCK(tabla->codec_resource_lock);
 }
@@ -7470,9 +7478,13 @@
 	bool correction = false;
 	enum tabla_mbhc_plug_type plug_type = PLUG_TYPE_INVALID;
 	unsigned long timeout;
+	struct wcd9xxx *core;
+	struct wcd9xxx_core_resource *core_res;
 
 	tabla = container_of(work, struct tabla_priv, hs_correct_plug_work);
 	codec = tabla->codec;
+	core = tabla->codec->control_data;
+	core_res = &core->core_res;
 
 	pr_debug("%s: enter\n", __func__);
 	tabla->mbhc_cfg.mclk_cb_fn(codec, 1, false);
@@ -7581,7 +7593,7 @@
 	pr_debug("%s: leave current_plug(%d)\n",
 		 __func__, tabla->current_plug);
 	/* unlock sleep */
-	wcd9xxx_unlock_sleep(tabla->codec->control_data);
+	wcd9xxx_unlock_sleep(core_res);
 }
 
 /* called under codec_resource_lock acquisition */
@@ -7738,6 +7750,7 @@
 	int ret;
 	struct snd_soc_codec *codec = priv->codec;
 	struct wcd9xxx *core = dev_get_drvdata(priv->codec->dev->parent);
+	struct wcd9xxx_core_resource *core_res = &core->core_res;
 	struct tabla_priv *tabla = snd_soc_codec_get_drvdata(codec);
 
 	/* Cancel possibly running hs_detect_work */
@@ -7766,12 +7779,12 @@
 	} else if (is_mb_trigger && !is_removal) {
 		pr_debug("%s: Waiting for Headphone left trigger\n",
 			__func__);
-		wcd9xxx_lock_sleep(core);
+		wcd9xxx_lock_sleep(core_res);
 		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);
+			wcd9xxx_unlock_sleep(core_res);
 		}
 		tabla_codec_enable_hs_detect(codec, 1, MBHC_USE_HPHL_TRIGGER,
 					     false);
@@ -7781,7 +7794,7 @@
 			pr_debug("%s: Complete plug insertion, Detecting plug "
 				 "type\n", __func__);
 			tabla_codec_detect_plug_type(codec);
-			wcd9xxx_unlock_sleep(core);
+			wcd9xxx_unlock_sleep(core_res);
 		} else {
 			wcd9xxx_enable_irq(codec->control_data,
 					   WCD9XXX_IRQ_MBHC_INSERTION);
@@ -8058,11 +8071,13 @@
 	struct tabla_priv *tabla;
 	struct snd_soc_codec *codec;
 	struct wcd9xxx *tabla_core;
+	struct wcd9xxx_core_resource *core_res;
 
 	dwork = to_delayed_work(work);
 	tabla = container_of(dwork, struct tabla_priv, mbhc_insert_dwork);
 	codec = tabla->codec;
 	tabla_core = dev_get_drvdata(codec->dev->parent);
+	core_res = &tabla_core->core_res;
 
 	pr_debug("%s:\n", __func__);
 
@@ -8073,7 +8088,7 @@
 	wcd9xxx_disable_irq_sync(codec->control_data,
 				 WCD9XXX_IRQ_MBHC_INSERTION);
 	tabla_codec_detect_plug_type(codec);
-	wcd9xxx_unlock_sleep(tabla_core);
+	wcd9xxx_unlock_sleep(core_res);
 }
 
 static void tabla_hs_gpio_handler(struct snd_soc_codec *codec)
@@ -8090,7 +8105,7 @@
 	usleep_range(TABLA_GPIO_IRQ_DEBOUNCE_TIME_US,
 		     TABLA_GPIO_IRQ_DEBOUNCE_TIME_US);
 
-	wcd9xxx_nested_irq_lock(core);
+	wcd9xxx_nested_irq_lock(&core->core_res);
 	TABLA_ACQUIRE_LOCK(tabla->codec_resource_lock);
 
 	/* cancel pending button press */
@@ -8163,7 +8178,7 @@
 
 	tabla->in_gpio_handler = false;
 	TABLA_RELEASE_LOCK(tabla->codec_resource_lock);
-	wcd9xxx_nested_irq_unlock(core);
+	wcd9xxx_nested_irq_unlock(&core->core_res);
 	pr_debug("%s: leave\n", __func__);
 }
 
@@ -8172,8 +8187,10 @@
 	int r = IRQ_HANDLED;
 	struct snd_soc_codec *codec = data;
 	struct tabla_priv *tabla = snd_soc_codec_get_drvdata(codec);
+	struct wcd9xxx *core = codec->control_data;
+	struct wcd9xxx_core_resource *core_res = &core->core_res;
 
-	if (unlikely(wcd9xxx_lock_sleep(codec->control_data) == false)) {
+	if (unlikely(wcd9xxx_lock_sleep(core_res) == false)) {
 		pr_warn("%s: failed to hold suspend\n", __func__);
 		/*
 		 * Give up this IRQ for now and resend this IRQ so IRQ can be
@@ -8186,7 +8203,7 @@
 		r = IRQ_NONE;
 	} else {
 		tabla_hs_gpio_handler(codec);
-		wcd9xxx_unlock_sleep(codec->control_data);
+		wcd9xxx_unlock_sleep(core_res);
 	}
 
 	return r;
@@ -8200,6 +8217,8 @@
 	int retry = 0;
 	enum tabla_mbhc_plug_type plug_type;
 	bool is_headset = false;
+	struct wcd9xxx *core;
+	struct wcd9xxx_core_resource *core_res;
 
 	pr_debug("%s(): Poll Microphone voltage for %d seconds\n",
 			 __func__, TABLA_HS_DETECT_PLUG_TIME_MS / 1000);
@@ -8207,6 +8226,8 @@
 	tabla = container_of(work, struct tabla_priv,
 						 hs_correct_plug_work_nogpio);
 	codec = tabla->codec;
+	core = codec->control_data;
+	core_res = &core->core_res;
 
 	/* Make sure the MBHC mux is connected to MIC Path */
 	snd_soc_write(codec, TABLA_A_MBHC_SCALING_MUX_1, 0x84);
@@ -8261,7 +8282,7 @@
 		tabla_codec_cleanup_hs_polling(codec);
 		tabla_codec_enable_hs_detect(codec, 0, 0, false);
 	}
-	wcd9xxx_unlock_sleep(codec->control_data);
+	wcd9xxx_unlock_sleep(core_res);
 }
 
 static int tabla_mbhc_init_and_calibrate(struct tabla_priv *tabla)
@@ -8933,9 +8954,11 @@
 	int ret = 0;
 	int i;
 	void *ptr = NULL;
+	struct wcd9xxx_core_resource *core_res;
 
 	codec->control_data = dev_get_drvdata(codec->dev->parent);
 	control = codec->control_data;
+	core_res = &control->core_res;
 
 	tabla = kzalloc(sizeof(struct tabla_priv), GFP_KERNEL);
 	if (!tabla) {
@@ -9049,7 +9072,7 @@
 
 	snd_soc_dapm_sync(dapm);
 
-	ret = wcd9xxx_request_irq(codec->control_data,
+	ret = wcd9xxx_request_irq(core_res,
 				  WCD9XXX_IRQ_MBHC_INSERTION,
 		tabla_hs_insert_irq, "Headset insert detect", tabla);
 	if (ret) {
@@ -9057,9 +9080,9 @@
 		       WCD9XXX_IRQ_MBHC_INSERTION);
 		goto err_insert_irq;
 	}
-	wcd9xxx_disable_irq(codec->control_data, WCD9XXX_IRQ_MBHC_INSERTION);
+	wcd9xxx_disable_irq(core_res, WCD9XXX_IRQ_MBHC_INSERTION);
 
-	ret = wcd9xxx_request_irq(codec->control_data,
+	ret = wcd9xxx_request_irq(core_res,
 				  WCD9XXX_IRQ_MBHC_REMOVAL,
 				  tabla_hs_remove_irq,
 				  "Headset remove detect", tabla);
@@ -9069,7 +9092,7 @@
 		goto err_remove_irq;
 	}
 
-	ret = wcd9xxx_request_irq(codec->control_data,
+	ret = wcd9xxx_request_irq(core_res,
 				  WCD9XXX_IRQ_MBHC_POTENTIAL,
 				  tabla_dce_handler, "DC Estimation detect",
 				  tabla);
@@ -9079,7 +9102,7 @@
 		goto err_potential_irq;
 	}
 
-	ret = wcd9xxx_request_irq(codec->control_data, WCD9XXX_IRQ_MBHC_RELEASE,
+	ret = wcd9xxx_request_irq(core_res, WCD9XXX_IRQ_MBHC_RELEASE,
 				  tabla_release_handler,
 				  "Button Release detect", tabla);
 	if (ret) {
@@ -9088,7 +9111,7 @@
 		goto err_release_irq;
 	}
 
-	ret = wcd9xxx_request_irq(codec->control_data, WCD9XXX_IRQ_SLIMBUS,
+	ret = wcd9xxx_request_irq(core_res, WCD9XXX_IRQ_SLIMBUS,
 				  tabla_slimbus_irq, "SLIMBUS Slave", tabla);
 	if (ret) {
 		pr_err("%s: Failed to request irq %d\n", __func__,
@@ -9097,10 +9120,10 @@
 	}
 
 	for (i = 0; i < WCD9XXX_SLIM_NUM_PORT_REG; i++)
-		wcd9xxx_interface_reg_write(codec->control_data,
-			TABLA_SLIM_PGD_PORT_INT_EN0 + i, 0xFF);
+		wcd9xxx_interface_reg_write(control,
+				TABLA_SLIM_PGD_PORT_INT_EN0 + i, 0xFF);
 
-	ret = wcd9xxx_request_irq(codec->control_data,
+	ret = wcd9xxx_request_irq(core_res,
 				  WCD9XXX_IRQ_HPH_PA_OCPL_FAULT,
 				  tabla_hphl_ocp_irq,
 				  "HPH_L OCP detect", tabla);
@@ -9109,9 +9132,9 @@
 		       WCD9XXX_IRQ_HPH_PA_OCPL_FAULT);
 		goto err_hphl_ocp_irq;
 	}
-	wcd9xxx_disable_irq(codec->control_data, WCD9XXX_IRQ_HPH_PA_OCPL_FAULT);
+	wcd9xxx_disable_irq(core_res, WCD9XXX_IRQ_HPH_PA_OCPL_FAULT);
 
-	ret = wcd9xxx_request_irq(codec->control_data,
+	ret = wcd9xxx_request_irq(core_res,
 				  WCD9XXX_IRQ_HPH_PA_OCPR_FAULT,
 				  tabla_hphr_ocp_irq,
 				  "HPH_R OCP detect", tabla);
@@ -9120,7 +9143,7 @@
 		       WCD9XXX_IRQ_HPH_PA_OCPR_FAULT);
 		goto err_hphr_ocp_irq;
 	}
-	wcd9xxx_disable_irq(codec->control_data, WCD9XXX_IRQ_HPH_PA_OCPR_FAULT);
+	wcd9xxx_disable_irq(core_res, WCD9XXX_IRQ_HPH_PA_OCPR_FAULT);
 
 	/*
 	 * Register suspend lock and notifier to resend edge triggered
@@ -9151,19 +9174,19 @@
 	return ret;
 
 err_hphr_ocp_irq:
-	wcd9xxx_free_irq(codec->control_data,
+	wcd9xxx_free_irq(core_res,
 			WCD9XXX_IRQ_HPH_PA_OCPL_FAULT, tabla);
 err_hphl_ocp_irq:
-	wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_SLIMBUS, tabla);
+	wcd9xxx_free_irq(core_res, WCD9XXX_IRQ_SLIMBUS, tabla);
 err_slimbus_irq:
-	wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_MBHC_RELEASE, tabla);
+	wcd9xxx_free_irq(core_res, WCD9XXX_IRQ_MBHC_RELEASE, tabla);
 err_release_irq:
-	wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_MBHC_POTENTIAL,
+	wcd9xxx_free_irq(core_res, WCD9XXX_IRQ_MBHC_POTENTIAL,
 			 tabla);
 err_potential_irq:
-	wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_MBHC_REMOVAL, tabla);
+	wcd9xxx_free_irq(core_res, WCD9XXX_IRQ_MBHC_REMOVAL, tabla);
 err_remove_irq:
-	wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_MBHC_INSERTION,
+	wcd9xxx_free_irq(core_res, WCD9XXX_IRQ_MBHC_INSERTION,
 			 tabla);
 err_insert_irq:
 err_pdata:
diff --git a/sound/soc/codecs/wcd9320.c b/sound/soc/codecs/wcd9320.c
index c27e085..6e500f5 100644
--- a/sound/soc/codecs/wcd9320.c
+++ b/sound/soc/codecs/wcd9320.c
@@ -4051,6 +4051,7 @@
 	unsigned int value)
 {
 	int ret;
+	struct wcd9xxx *wcd9xxx = codec->control_data;
 
 	if (reg == SND_SOC_NOPM)
 		return 0;
@@ -4064,7 +4065,7 @@
 				reg, ret);
 	}
 
-	return wcd9xxx_reg_write(codec->control_data, reg, value);
+	return wcd9xxx_reg_write(&wcd9xxx->core_res, reg, value);
 }
 static unsigned int taiko_read(struct snd_soc_codec *codec,
 				unsigned int reg)
@@ -4072,6 +4073,8 @@
 	unsigned int val;
 	int ret;
 
+	struct wcd9xxx *wcd9xxx = codec->control_data;
+
 	if (reg == SND_SOC_NOPM)
 		return 0;
 
@@ -4087,7 +4090,7 @@
 				reg, ret);
 	}
 
-	val = wcd9xxx_reg_read(codec->control_data, reg);
+	val = wcd9xxx_reg_read(&wcd9xxx->core_res, reg);
 	return val;
 }
 
@@ -6151,8 +6154,11 @@
 {
 	int ret = 0;
 	struct snd_soc_codec *codec = taiko->codec;
+	struct wcd9xxx *wcd9xxx = codec->control_data;
+	struct wcd9xxx_core_resource *core_res =
+				&wcd9xxx->core_res;
 
-	ret = wcd9xxx_request_irq(codec->control_data, WCD9XXX_IRQ_SLIMBUS,
+	ret = wcd9xxx_request_irq(core_res, WCD9XXX_IRQ_SLIMBUS,
 				  taiko_slimbus_irq, "SLIMBUS Slave", taiko);
 	if (ret)
 		pr_err("%s: Failed to request irq %d\n", __func__,
@@ -6166,8 +6172,11 @@
 static void taiko_cleanup_irqs(struct taiko_priv *taiko)
 {
 	struct snd_soc_codec *codec = taiko->codec;
+	struct wcd9xxx *wcd9xxx = codec->control_data;
+	struct wcd9xxx_core_resource *core_res =
+				&wcd9xxx->core_res;
 
-	wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_SLIMBUS, taiko);
+	wcd9xxx_free_irq(core_res, WCD9XXX_IRQ_SLIMBUS, taiko);
 }
 
 int taiko_hs_detect(struct snd_soc_codec *codec,
@@ -6180,7 +6189,7 @@
 		taiko->mbhc_started = true;
 	return rc;
 }
-EXPORT_SYMBOL_GPL(taiko_hs_detect);
+EXPORT_SYMBOL(taiko_hs_detect);
 
 void taiko_event_register(
 	int (*machine_event_cb)(struct snd_soc_codec *codec,
@@ -6190,7 +6199,7 @@
 	struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
 	taiko->machine_codec_event_cb = machine_event_cb;
 }
-EXPORT_SYMBOL_GPL(taiko_event_register);
+EXPORT_SYMBOL(taiko_event_register);
 
 static void taiko_init_slim_slave_cfg(struct snd_soc_codec *codec)
 {
@@ -6212,6 +6221,16 @@
 	pr_debug("%s: slimbus logical address 0x%llx\n", __func__, eaddr);
 }
 
+static int taiko_device_down(struct wcd9xxx *wcd9xxx)
+{
+	struct snd_soc_codec *codec;
+
+	codec = (struct snd_soc_codec *)(wcd9xxx->ssr_priv);
+	snd_soc_card_change_online_state(codec->card, 0);
+
+	return 0;
+}
+
 static int taiko_post_reset_cb(struct wcd9xxx *wcd9xxx)
 {
 	int ret = 0;
@@ -6221,8 +6240,10 @@
 
 	codec = (struct snd_soc_codec *)(wcd9xxx->ssr_priv);
 	taiko = snd_soc_codec_get_drvdata(codec);
-	mutex_lock(&codec->mutex);
 
+	snd_soc_card_change_online_state(codec->card, 1);
+
+	mutex_lock(&codec->mutex);
 	if (codec->reg_def_copy) {
 		pr_debug("%s: Update ASOC cache", __func__);
 		kfree(codec->reg_cache);
@@ -6267,6 +6288,11 @@
 	}
 	taiko->machine_codec_event_cb(codec, WCD9XXX_CODEC_EVENT_CODEC_UP);
 
+	taiko_cleanup_irqs(taiko);
+	ret = taiko_setup_irqs(taiko);
+	if (ret)
+		pr_err("%s: Failed to setup irq: %d\n", __func__, ret);
+
 	mutex_unlock(&codec->mutex);
 	return ret;
 }
@@ -6309,9 +6335,12 @@
 };
 
 static int wcd9xxx_ssr_register(struct wcd9xxx *control,
-		int (*post_reset_cb)(struct wcd9xxx *wcd9xxx), void *priv)
+				int (*device_down_cb)(struct wcd9xxx *wcd9xxx),
+				int (*device_up_cb)(struct wcd9xxx *wcd9xxx),
+				void *priv)
 {
-	control->post_reset = post_reset_cb;
+	control->dev_down = device_down_cb;
+	control->post_reset = device_up_cb;
 	control->ssr_priv = priv;
 	return 0;
 }
@@ -6392,11 +6421,13 @@
 	int i, rco_clk_rate;
 	void *ptr = NULL;
 	struct wcd9xxx *core = dev_get_drvdata(codec->dev->parent);
+	struct wcd9xxx_core_resource *core_res;
 
 	codec->control_data = dev_get_drvdata(codec->dev->parent);
 	control = codec->control_data;
 
-	wcd9xxx_ssr_register(control, taiko_post_reset_cb, (void *)codec);
+	wcd9xxx_ssr_register(control, taiko_device_down,
+			     taiko_post_reset_cb, (void *)codec);
 
 	dev_info(codec->dev, "%s()\n", __func__);
 
@@ -6416,9 +6447,10 @@
 
 	/* codec resmgr module init */
 	wcd9xxx = codec->control_data;
+	core_res = &wcd9xxx->core_res;
 	pdata = dev_get_platdata(codec->dev->parent);
-	ret = wcd9xxx_resmgr_init(&taiko->resmgr, codec, wcd9xxx, pdata,
-				  &taiko_reg_address);
+	ret = wcd9xxx_resmgr_init(&taiko->resmgr, codec, core_res, pdata,
+				  &taiko_reg_address, WCD9XXX_CDC_TYPE_TAIKO);
 	if (ret) {
 		pr_err("%s: wcd9xxx init failed %d\n", __func__, ret);
 		goto err_init;
diff --git a/sound/soc/codecs/wcd9xxx-mbhc.c b/sound/soc/codecs/wcd9xxx-mbhc.c
index 7820cd0..903b239 100644
--- a/sound/soc/codecs/wcd9xxx-mbhc.c
+++ b/sound/soc/codecs/wcd9xxx-mbhc.c
@@ -20,6 +20,7 @@
 #include <linux/debugfs.h>
 #include <linux/list.h>
 #include <linux/mfd/wcd9xxx/core.h>
+#include <linux/mfd/wcd9xxx/core-resource.h>
 #include <linux/mfd/wcd9xxx/wcd9xxx_registers.h>
 #include <linux/mfd/wcd9xxx/wcd9320_registers.h>
 #include <linux/mfd/wcd9xxx/pdata.h>
@@ -257,6 +258,17 @@
 	pr_debug("%s: leave\n", __func__);
 }
 
+static int __wcd9xxx_resmgr_get_k_val(struct wcd9xxx_mbhc *mbhc,
+		unsigned int cfilt_mv)
+{
+	if (mbhc->mbhc_cb &&
+			mbhc->mbhc_cb->get_cdc_type() ==
+					WCD9XXX_CDC_TYPE_HELICON)
+		return 0x18;
+
+	return wcd9xxx_resmgr_get_k_val(mbhc->resmgr, cfilt_mv);
+}
+
 /*
  * called under codec_resource_lock acquisition
  * return old status
@@ -290,7 +302,7 @@
 			wcd9xxx_turn_onoff_override(codec, true);
 		/* Adjust threshold if Mic Bias voltage changes */
 		if (d->micb_mv != VDDIO_MICBIAS_MV) {
-			cfilt_k_val = wcd9xxx_resmgr_get_k_val(mbhc->resmgr,
+			cfilt_k_val = __wcd9xxx_resmgr_get_k_val(mbhc,
 							      VDDIO_MICBIAS_MV);
 			usleep_range(10000, 10000);
 			snd_soc_update_bits(codec,
@@ -342,7 +354,7 @@
 		/* Reprogram thresholds */
 		if (d->micb_mv != VDDIO_MICBIAS_MV) {
 			cfilt_k_val =
-			    wcd9xxx_resmgr_get_k_val(mbhc->resmgr,
+			    __wcd9xxx_resmgr_get_k_val(mbhc,
 						     d->micb_mv);
 			snd_soc_update_bits(codec,
 					mbhc->mbhc_bias_regs.cfilt_val,
@@ -452,7 +464,7 @@
 
 	return ret;
 }
-EXPORT_SYMBOL_GPL(wcd9xxx_mbhc_cal_btn_det_mp);
+EXPORT_SYMBOL(wcd9xxx_mbhc_cal_btn_det_mp);
 
 static void wcd9xxx_calibrate_hs_polling(struct wcd9xxx_mbhc *mbhc)
 {
@@ -487,34 +499,36 @@
 {
 	struct snd_soc_codec *codec = mbhc->codec;
 	struct wcd9xxx_cfilt_mode cfilt_mode;
-	u8 reg_mode_val, cur_mode_val;
 
 	if (mbhc->mbhc_cb && mbhc->mbhc_cb->switch_cfilt_mode) {
 		cfilt_mode = mbhc->mbhc_cb->switch_cfilt_mode(mbhc, fast);
-		reg_mode_val = cfilt_mode.reg_mode_val;
-		cur_mode_val = cfilt_mode.cur_mode_val;
 	} else {
 		if (fast)
-			reg_mode_val = WCD9XXX_CFILT_FAST_MODE;
+			cfilt_mode.reg_mode_val = WCD9XXX_CFILT_FAST_MODE;
 		else
-			reg_mode_val = WCD9XXX_CFILT_SLOW_MODE;
+			cfilt_mode.reg_mode_val = WCD9XXX_CFILT_SLOW_MODE;
 
-		cur_mode_val =
+		cfilt_mode.reg_mask = 0x40;
+		cfilt_mode.cur_mode_val =
 		    snd_soc_read(codec, mbhc->mbhc_bias_regs.cfilt_ctl) & 0x40;
 	}
-	if (cur_mode_val != reg_mode_val) {
+
+	if (cfilt_mode.cur_mode_val
+			!= cfilt_mode.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);
+					cfilt_mode.reg_mask,
+					cfilt_mode.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);
+			cfilt_mode.cur_mode_val,
+			cfilt_mode.reg_mode_val);
 	} else {
 		pr_debug("%s: CFILT Value is already %x\n",
-			 __func__, cur_mode_val);
+			 __func__, cfilt_mode.cur_mode_val);
 	}
 }
 
@@ -556,7 +570,7 @@
 			mbhc->hphlocp_cnt = 0;
 		else
 			mbhc->hphrocp_cnt = 0;
-		wcd9xxx_enable_irq(codec->control_data, irq);
+		wcd9xxx_enable_irq(mbhc->resmgr->core_res, irq);
 	}
 }
 
@@ -578,6 +592,18 @@
 	unsigned int cfilt;
 	struct wcd9xxx_pdata *pdata = mbhc->resmgr->pdata;
 
+	if (mbhc->mbhc_cb &&
+			mbhc->mbhc_cb->get_cdc_type() ==
+					WCD9XXX_CDC_TYPE_HELICON) {
+		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;
+		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 = 1800;
+		return;
+	}
+
 	switch (mbhc->mbhc_cfg->micbias) {
 	case MBHC_MICBIAS1:
 		cfilt = pdata->micbias.bias1_cfilt_sel;
@@ -685,7 +711,7 @@
 	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);
+		wcd9xxx_unlock_sleep(mbhc->resmgr->core_res);
 	return r;
 }
 
@@ -836,8 +862,10 @@
 			pr_debug("%s: Enabling micbias\n", __func__);
 			mbhc->micbias_enable_cb(mbhc->codec, true);
 		}
+
 		if (mbhc->impedance_detect)
 			wcd9xxx_detect_impedance(mbhc, &mbhc->zl, &mbhc->zr);
+
 		pr_debug("%s: Reporting insertion %d(%x)\n", __func__,
 			 jack_type, mbhc->hph_status);
 		wcd9xxx_jack_report(mbhc, &mbhc->headset_jack,
@@ -857,7 +885,7 @@
 	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);
+	wcd9xxx_lock_sleep(mbhc->resmgr->core_res);
 	schedule_work(work);
 }
 
@@ -873,7 +901,7 @@
 	if (cancel_work_sync(work)) {
 		pr_debug("%s: correct_plug_swch is canceled\n",
 			 __func__);
-		wcd9xxx_unlock_sleep(mbhc->resmgr->core);
+		wcd9xxx_unlock_sleep(mbhc->resmgr->core_res);
 	}
 	WCD9XXX_BCL_LOCK(mbhc->resmgr);
 }
@@ -882,8 +910,8 @@
 {
 	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);
+	vddio_k = __wcd9xxx_resmgr_get_k_val(mbhc, VDDIO_MICBIAS_MV);
+	mb_k = __wcd9xxx_resmgr_get_k_val(mbhc, mbhc->mbhc_data.micb_mv);
 	if (tovddio)
 		r = v * (vddio_k + 4) / (mb_k + 4);
 	else
@@ -939,7 +967,7 @@
 	short bias_value;
 	struct snd_soc_codec *codec = mbhc->codec;
 
-	wcd9xxx_disable_irq(mbhc->resmgr->core, WCD9XXX_IRQ_MBHC_POTENTIAL);
+	wcd9xxx_disable_irq(mbhc->resmgr->core_res, WCD9XXX_IRQ_MBHC_POTENTIAL);
 	if (noreldetection)
 		wcd9xxx_turn_onoff_rel_detection(codec, false);
 
@@ -980,7 +1008,7 @@
 
 	if (noreldetection)
 		wcd9xxx_turn_onoff_rel_detection(codec, true);
-	wcd9xxx_enable_irq(mbhc->resmgr->core, WCD9XXX_IRQ_MBHC_POTENTIAL);
+	wcd9xxx_enable_irq(mbhc->resmgr->core_res, WCD9XXX_IRQ_MBHC_POTENTIAL);
 
 	return bias_value;
 }
@@ -1796,12 +1824,12 @@
 	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);
+			wcd9xxx_resmgr_enable_config_mode(mbhc->resmgr, 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);
+			wcd9xxx_resmgr_enable_config_mode(mbhc->resmgr, 0);
 		} else
 			snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL,
 					0x06, 0);
@@ -1833,7 +1861,7 @@
 		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);
+	wcd9xxx_enable_irq(mbhc->resmgr->core_res, WCD9XXX_IRQ_MBHC_INSERTION);
 	snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_INT_CTL, 0x1, 0x1);
 	pr_debug("%s: leave\n", __func__);
 
@@ -2297,7 +2325,7 @@
 
 	pr_debug("%s: enter\n", __func__);
 	WCD9XXX_BCL_LOCK(mbhc->resmgr);
-	wcd9xxx_disable_irq(codec->control_data, WCD9XXX_IRQ_MBHC_INSERTION);
+	wcd9xxx_disable_irq(mbhc->resmgr->core_res, WCD9XXX_IRQ_MBHC_INSERTION);
 
 	is_mb_trigger = !!(snd_soc_read(codec, mbhc->mbhc_bias_regs.mbhc_reg) &
 			   0x10);
@@ -2343,7 +2371,7 @@
 			    mbhc->buttons_pressed);
 
 	pr_debug("%s: leave\n", __func__);
-	wcd9xxx_unlock_sleep(mbhc->resmgr->core);
+	wcd9xxx_unlock_sleep(mbhc->resmgr->core_res);
 }
 
 static void wcd9xxx_mbhc_insert_work(struct work_struct *work)
@@ -2351,12 +2379,12 @@
 	struct delayed_work *dwork;
 	struct wcd9xxx_mbhc *mbhc;
 	struct snd_soc_codec *codec;
-	struct wcd9xxx *core;
+	struct wcd9xxx_core_resource *core_res;
 
 	dwork = to_delayed_work(work);
 	mbhc = container_of(dwork, struct wcd9xxx_mbhc, mbhc_insert_dwork);
 	codec = mbhc->codec;
-	core = mbhc->resmgr->core;
+	core_res = mbhc->resmgr->core_res;
 
 	pr_debug("%s:\n", __func__);
 
@@ -2364,9 +2392,9 @@
 	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_disable_irq_sync(core_res, WCD9XXX_IRQ_MBHC_INSERTION);
 	wcd9xxx_mbhc_detect_plug_type(mbhc);
-	wcd9xxx_unlock_sleep(core);
+	wcd9xxx_unlock_sleep(core_res);
 }
 
 static bool wcd9xxx_mbhc_fw_validate(const struct firmware *fw)
@@ -2702,7 +2730,7 @@
 	}
 	pr_debug("%s: leave current_plug(%d)\n", __func__, mbhc->current_plug);
 	/* unlock sleep */
-	wcd9xxx_unlock_sleep(mbhc->resmgr->core);
+	wcd9xxx_unlock_sleep(mbhc->resmgr->core_res);
 }
 
 static void wcd9xxx_swch_irq_handler(struct wcd9xxx_mbhc *mbhc)
@@ -2797,13 +2825,13 @@
 	struct wcd9xxx_mbhc *mbhc = data;
 
 	pr_debug("%s: enter\n", __func__);
-	if (unlikely(wcd9xxx_lock_sleep(mbhc->resmgr->core) == false)) {
+	if (unlikely(wcd9xxx_lock_sleep(mbhc->resmgr->core_res) == 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);
+		wcd9xxx_unlock_sleep(mbhc->resmgr->core_res);
 	}
 
 	pr_debug("%s: leave %d\n", __func__, r);
@@ -2979,7 +3007,7 @@
 	short dce[d->n_btn_meas + 1], sta;
 	s32 mv[d->n_btn_meas + 1], mv_s[d->n_btn_meas + 1];
 	struct snd_soc_codec *codec = mbhc->codec;
-	struct wcd9xxx *core = mbhc->resmgr->core;
+	struct wcd9xxx_core_resource *core_res = mbhc->resmgr->core_res;
 	int n_btn_meas = d->n_btn_meas;
 	void *calibration = mbhc->mbhc_cfg->calibration;
 
@@ -3119,11 +3147,11 @@
 
 		mask = wcd9xxx_get_button_mask(btn);
 		mbhc->buttons_pressed |= mask;
-		wcd9xxx_lock_sleep(core);
+		wcd9xxx_lock_sleep(core_res);
 		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);
+			wcd9xxx_unlock_sleep(core_res);
 		}
 	} else {
 		pr_debug("%s: bogus button press, too short press?\n",
@@ -3207,7 +3235,7 @@
 			snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_OCP_CTL,
 					    0x10, 0x10);
 		} else {
-			wcd9xxx_disable_irq(codec->control_data,
+			wcd9xxx_disable_irq(mbhc->resmgr->core_res,
 					  WCD9XXX_IRQ_HPH_PA_OCPL_FAULT);
 			mbhc->hph_status |= SND_JACK_OC_HPHL;
 			wcd9xxx_jack_report(mbhc, &mbhc->headset_jack,
@@ -3237,7 +3265,7 @@
 		snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_OCP_CTL, 0x10,
 				    0x10);
 	} else {
-		wcd9xxx_disable_irq(mbhc->resmgr->core,
+		wcd9xxx_disable_irq(mbhc->resmgr->core_res,
 				    WCD9XXX_IRQ_HPH_PA_OCPR_FAULT);
 		mbhc->hph_status |= SND_JACK_OC_HPHR;
 		wcd9xxx_jack_report(mbhc, &mbhc->headset_jack,
@@ -3318,7 +3346,7 @@
 	struct snd_soc_codec *codec = mbhc->codec;
 
 	pr_debug("%s: enter\n", __func__);
-	wcd9xxx_disable_irq(codec->control_data, WCD9XXX_IRQ_MBHC_POTENTIAL);
+	wcd9xxx_disable_irq(mbhc->resmgr->core_res, WCD9XXX_IRQ_MBHC_POTENTIAL);
 	wcd9xxx_turn_onoff_rel_detection(codec, false);
 
 	/* t_dce and t_sta are updated by wcd9xxx_update_mbhc_clk_rate() */
@@ -3449,7 +3477,7 @@
 				    0x80, 0x80);
 	usleep_range(100, 100);
 
-	wcd9xxx_enable_irq(codec->control_data, WCD9XXX_IRQ_MBHC_POTENTIAL);
+	wcd9xxx_enable_irq(mbhc->resmgr->core_res, WCD9XXX_IRQ_MBHC_POTENTIAL);
 	wcd9xxx_turn_onoff_rel_detection(codec, true);
 
 	pr_debug("%s: leave\n", __func__);
@@ -3492,9 +3520,14 @@
 	snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x78,
 			    btn_det->mbhc_nsc << 3);
 
-	if (mbhc->resmgr->reg_addr->micb_4_mbhc)
-		snd_soc_update_bits(codec, mbhc->resmgr->reg_addr->micb_4_mbhc,
-				    0x03, MBHC_MICBIAS2);
+	if (mbhc->mbhc_cb &&
+			mbhc->mbhc_cb->get_cdc_type() !=
+					WCD9XXX_CDC_TYPE_HELICON) {
+		if (mbhc->resmgr->reg_addr->micb_4_mbhc)
+			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);
 
@@ -3510,8 +3543,8 @@
 static int wcd9xxx_setup_jack_detect_irq(struct wcd9xxx_mbhc *mbhc)
 {
 	int ret = 0;
-	void *core = mbhc->resmgr->core;
 	struct snd_soc_codec *codec = mbhc->codec;
+	void *core_res = mbhc->resmgr->core_res;
 	int jack_irq;
 
 	if (mbhc->mbhc_cb && mbhc->mbhc_cb->jack_detect_irq)
@@ -3540,7 +3573,7 @@
 		snd_soc_update_bits(mbhc->codec, WCD9XXX_A_RX_HPH_OCP_CTL,
 				    1 << 1, 1 << 1);
 
-		ret = wcd9xxx_request_irq(core, jack_irq,
+		ret = wcd9xxx_request_irq(core_res, jack_irq,
 					  wcd9xxx_mech_plug_detect_irq,
 					  "Jack Detect",
 					  mbhc);
@@ -3575,9 +3608,9 @@
 	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_enable_irq(mbhc->resmgr->core_res,
 				   WCD9XXX_IRQ_HPH_PA_OCPL_FAULT);
-		wcd9xxx_enable_irq(codec->control_data,
+		wcd9xxx_enable_irq(mbhc->resmgr->core_res,
 				   WCD9XXX_IRQ_HPH_PA_OCPR_FAULT);
 
 		/* Initialize mechanical mbhc */
@@ -3798,6 +3831,14 @@
 		snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.cfilt_ctl,
 		0x40, WCD9XXX_CFILT_FAST_MODE);
 
+	/*
+	 * If codec has specific clock gating for MBHC,
+	 * remove the clock gate
+	 */
+	if (mbhc->mbhc_cb &&
+			mbhc->mbhc_cb->enable_clock_gate)
+		mbhc->mbhc_cb->enable_clock_gate(mbhc->codec, true);
+
 	if (!mbhc->mbhc_cfg->read_fw_bin)
 		rc = wcd9xxx_init_and_calibrate(mbhc);
 	else
@@ -3807,7 +3848,7 @@
 	pr_debug("%s: leave %d\n", __func__, rc);
 	return rc;
 }
-EXPORT_SYMBOL_GPL(wcd9xxx_mbhc_start);
+EXPORT_SYMBOL(wcd9xxx_mbhc_start);
 
 static enum wcd9xxx_micbias_num
 wcd9xxx_event_to_micbias(const enum wcd9xxx_notify_event event)
@@ -4374,7 +4415,7 @@
 		      bool impedance_det_en)
 {
 	int ret;
-	void *core;
+	void *core_res;
 
 	pr_debug("%s: enter\n", __func__);
 	memset(&mbhc->mbhc_bias_regs, 0, sizeof(struct mbhc_micbias_regs));
@@ -4441,18 +4482,18 @@
 
 	wcd9xxx_init_debugfs(mbhc);
 
-	core = mbhc->resmgr->core;
-	ret = wcd9xxx_request_irq(core, WCD9XXX_IRQ_MBHC_INSERTION,
+	core_res = mbhc->resmgr->core_res;
+	ret = wcd9xxx_request_irq(core_res, 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);
+		pr_err("%s: Failed to request irq %d, ret = %d\n", __func__,
+		       WCD9XXX_IRQ_MBHC_INSERTION, ret);
 		goto err_insert_irq;
 	}
-	wcd9xxx_disable_irq(core, WCD9XXX_IRQ_MBHC_INSERTION);
+	wcd9xxx_disable_irq(core_res, WCD9XXX_IRQ_MBHC_INSERTION);
 
-	ret = wcd9xxx_request_irq(core, WCD9XXX_IRQ_MBHC_REMOVAL,
+	ret = wcd9xxx_request_irq(core_res, WCD9XXX_IRQ_MBHC_REMOVAL,
 				  wcd9xxx_hs_remove_irq,
 				  "Headset remove detect", mbhc);
 	if (ret) {
@@ -4461,7 +4502,7 @@
 		goto err_remove_irq;
 	}
 
-	ret = wcd9xxx_request_irq(core, WCD9XXX_IRQ_MBHC_POTENTIAL,
+	ret = wcd9xxx_request_irq(core_res, WCD9XXX_IRQ_MBHC_POTENTIAL,
 				  wcd9xxx_dce_handler, "DC Estimation detect",
 				  mbhc);
 	if (ret) {
@@ -4470,7 +4511,7 @@
 		goto err_potential_irq;
 	}
 
-	ret = wcd9xxx_request_irq(core, WCD9XXX_IRQ_MBHC_RELEASE,
+	ret = wcd9xxx_request_irq(core_res, WCD9XXX_IRQ_MBHC_RELEASE,
 				  wcd9xxx_release_handler,
 				  "Button Release detect", mbhc);
 	if (ret) {
@@ -4479,7 +4520,7 @@
 		goto err_release_irq;
 	}
 
-	ret = wcd9xxx_request_irq(core, WCD9XXX_IRQ_HPH_PA_OCPL_FAULT,
+	ret = wcd9xxx_request_irq(core_res, WCD9XXX_IRQ_HPH_PA_OCPL_FAULT,
 				  wcd9xxx_hphl_ocp_irq, "HPH_L OCP detect",
 				  mbhc);
 	if (ret) {
@@ -4487,9 +4528,9 @@
 		       WCD9XXX_IRQ_HPH_PA_OCPL_FAULT);
 		goto err_hphl_ocp_irq;
 	}
-	wcd9xxx_disable_irq(core, WCD9XXX_IRQ_HPH_PA_OCPL_FAULT);
+	wcd9xxx_disable_irq(core_res, WCD9XXX_IRQ_HPH_PA_OCPL_FAULT);
 
-	ret = wcd9xxx_request_irq(core, WCD9XXX_IRQ_HPH_PA_OCPR_FAULT,
+	ret = wcd9xxx_request_irq(core_res, WCD9XXX_IRQ_HPH_PA_OCPR_FAULT,
 				  wcd9xxx_hphr_ocp_irq, "HPH_R OCP detect",
 				  mbhc);
 	if (ret) {
@@ -4497,7 +4538,7 @@
 		       WCD9XXX_IRQ_HPH_PA_OCPR_FAULT);
 		goto err_hphr_ocp_irq;
 	}
-	wcd9xxx_disable_irq(codec->control_data, WCD9XXX_IRQ_HPH_PA_OCPR_FAULT);
+	wcd9xxx_disable_irq(core_res, WCD9XXX_IRQ_HPH_PA_OCPR_FAULT);
 
 	wcd9xxx_regmgr_cond_register(resmgr, 1 << WCD9XXX_COND_HPH_MIC |
 					     1 << WCD9XXX_COND_HPH);
@@ -4506,43 +4547,44 @@
 	return ret;
 
 err_hphr_ocp_irq:
-	wcd9xxx_free_irq(core, WCD9XXX_IRQ_HPH_PA_OCPL_FAULT, mbhc);
+	wcd9xxx_free_irq(core_res, WCD9XXX_IRQ_HPH_PA_OCPL_FAULT, mbhc);
 err_hphl_ocp_irq:
-	wcd9xxx_free_irq(core, WCD9XXX_IRQ_MBHC_RELEASE, mbhc);
+	wcd9xxx_free_irq(core_res, WCD9XXX_IRQ_MBHC_RELEASE, mbhc);
 err_release_irq:
-	wcd9xxx_free_irq(core, WCD9XXX_IRQ_MBHC_POTENTIAL, mbhc);
+	wcd9xxx_free_irq(core_res, WCD9XXX_IRQ_MBHC_POTENTIAL, mbhc);
 err_potential_irq:
-	wcd9xxx_free_irq(core, WCD9XXX_IRQ_MBHC_REMOVAL, mbhc);
+	wcd9xxx_free_irq(core_res, WCD9XXX_IRQ_MBHC_REMOVAL, mbhc);
 err_remove_irq:
-	wcd9xxx_free_irq(core, WCD9XXX_IRQ_MBHC_INSERTION, mbhc);
+	wcd9xxx_free_irq(core_res, 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);
+EXPORT_SYMBOL(wcd9xxx_mbhc_init);
 
 void wcd9xxx_mbhc_deinit(struct wcd9xxx_mbhc *mbhc)
 {
-	void *cdata = mbhc->codec->control_data;
+	struct wcd9xxx_core_resource *core_res =
+				mbhc->resmgr->core_res;
 
 	wcd9xxx_regmgr_cond_deregister(mbhc->resmgr, 1 << WCD9XXX_COND_HPH_MIC |
 						     1 << WCD9XXX_COND_HPH);
 
-	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);
+	wcd9xxx_free_irq(core_res, WCD9XXX_IRQ_MBHC_RELEASE, mbhc);
+	wcd9xxx_free_irq(core_res, WCD9XXX_IRQ_MBHC_POTENTIAL, mbhc);
+	wcd9xxx_free_irq(core_res, WCD9XXX_IRQ_MBHC_REMOVAL, mbhc);
+	wcd9xxx_free_irq(core_res, WCD9XXX_IRQ_MBHC_INSERTION, mbhc);
 
 	if (mbhc->mbhc_cb && mbhc->mbhc_cb->free_irq)
 		mbhc->mbhc_cb->free_irq(mbhc);
 	else
-		wcd9xxx_free_irq(cdata, WCD9320_IRQ_MBHC_JACK_SWITCH,
+		wcd9xxx_free_irq(core_res, WCD9320_IRQ_MBHC_JACK_SWITCH,
 				 mbhc);
 
-	wcd9xxx_free_irq(cdata, WCD9XXX_IRQ_HPH_PA_OCPL_FAULT, mbhc);
-	wcd9xxx_free_irq(cdata, WCD9XXX_IRQ_HPH_PA_OCPR_FAULT, mbhc);
+	wcd9xxx_free_irq(core_res, WCD9XXX_IRQ_HPH_PA_OCPL_FAULT, mbhc);
+	wcd9xxx_free_irq(core_res, WCD9XXX_IRQ_HPH_PA_OCPR_FAULT, mbhc);
 
 	if (mbhc->mbhc_fw)
 		release_firmware(mbhc->mbhc_fw);
@@ -4551,7 +4593,7 @@
 
 	wcd9xxx_cleanup_debugfs(mbhc);
 }
-EXPORT_SYMBOL_GPL(wcd9xxx_mbhc_deinit);
+EXPORT_SYMBOL(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
index 88c911f..104c488 100644
--- a/sound/soc/codecs/wcd9xxx-mbhc.h
+++ b/sound/soc/codecs/wcd9xxx-mbhc.h
@@ -224,6 +224,7 @@
 struct wcd9xxx_cfilt_mode {
 	u8 reg_mode_val;
 	u8 cur_mode_val;
+	u8 reg_mask;
 };
 
 struct wcd9xxx_mbhc_cb {
@@ -236,6 +237,8 @@
 							bool);
 	void (*select_cfilt) (struct snd_soc_codec *, struct wcd9xxx_mbhc *);
 	void (*free_irq) (struct wcd9xxx_mbhc *);
+	enum wcd9xxx_cdc_type (*get_cdc_type) (void);
+	void (*enable_clock_gate) (struct snd_soc_codec *, bool);
 };
 
 struct wcd9xxx_mbhc {
diff --git a/sound/soc/codecs/wcd9xxx-resmgr.c b/sound/soc/codecs/wcd9xxx-resmgr.c
index 9633cc0..95244c0 100644
--- a/sound/soc/codecs/wcd9xxx-resmgr.c
+++ b/sound/soc/codecs/wcd9xxx-resmgr.c
@@ -33,6 +33,7 @@
 #include <linux/kernel.h>
 #include <linux/gpio.h>
 #include "wcd9xxx-resmgr.h"
+#include "msm8x10_wcd_registers.h"
 
 static char wcd9xxx_event_string[][64] = {
 	"WCD9XXX_EVENT_INVALID",
@@ -168,7 +169,9 @@
 	 * 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));
+	if (resmgr->codec_type != WCD9XXX_CDC_TYPE_HELICON)
+		WARN_ON(snd_soc_read(codec, WCD9XXX_A_CLK_BUFF_EN2) & (1 << 2));
+
 	wcd9xxx_enable_bg(resmgr);
 	/* Notify bandgap mode change */
 	wcd9xxx_resmgr_notifier_call(resmgr, WCD9XXX_EVENT_POST_BG_MBHC_ON);
@@ -188,11 +191,13 @@
 		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);
+	if (resmgr->codec_type != WCD9XXX_CDC_TYPE_HELICON) {
+		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,
@@ -375,8 +380,10 @@
 	}
 }
 
-int wcd9xxx_resmgr_enable_config_mode(struct snd_soc_codec *codec, int enable)
+int wcd9xxx_resmgr_enable_config_mode(struct wcd9xxx_resmgr *resmgr, int enable)
 {
+	struct snd_soc_codec *codec = resmgr->codec;
+
 	pr_debug("%s: enable = %d\n", __func__, enable);
 	if (enable) {
 		snd_soc_update_bits(codec, WCD9XXX_A_RC_OSC_FREQ, 0x10, 0);
@@ -388,10 +395,20 @@
 		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);
+		if (resmgr->codec_type != WCD9XXX_CDC_TYPE_HELICON)
+			snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN1,
+					0x08, 0x08);
+		else
+			snd_soc_update_bits(codec,
+					MSM8X10_WCD_A_CDC_TOP_CLK_CTL,
+					0x20, 0x20);
 	} 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);
+		if (resmgr->codec_type == WCD9XXX_CDC_TYPE_HELICON)
+			snd_soc_update_bits(codec,
+					MSM8X10_WCD_A_CDC_TOP_CLK_CTL,
+					0x20, 0x00);
 	}
 
 	return 0;
@@ -403,38 +420,60 @@
 	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 (resmgr->codec_type != WCD9XXX_CDC_TYPE_HELICON)
+		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);
+		wcd9xxx_resmgr_enable_config_mode(resmgr, 1);
+		if (resmgr->codec_type != WCD9XXX_CDC_TYPE_HELICON)
+			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 (resmgr->codec_type != WCD9XXX_CDC_TYPE_HELICON) {
+			snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN1,
+				0x08, 0x00);
+		} else {
+			snd_soc_update_bits(codec,
+					MSM8X10_WCD_A_CDC_CLK_PDM_CTL,
+					0x03, 0x03);
+			snd_soc_update_bits(codec,
+					MSM8X10_WCD_A_CDC_TOP_CLK_CTL,
+					0x0f, 0x0d);
+		}
 		/* 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);
+			if (resmgr->codec_type != WCD9XXX_CDC_TYPE_HELICON)
+				snd_soc_write(codec, WCD9XXX_A_CLK_BUFF_EN2,
+							  0x02);
+			wcd9xxx_resmgr_enable_config_mode(resmgr, 0);
 		}
 		/* clk source to ext clk and clk buff ref to VBG */
-		snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN1, 0x0C, 0x04);
+		if (resmgr->codec_type != WCD9XXX_CDC_TYPE_HELICON)
+			snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN1,
+				0x0C, 0x04);
 	}
 
-	snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN1, 0x01, 0x01);
-	/* sleep time required by codec hardware to enable clock buffer */
-	usleep_range(1000, 1200);
-
-	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);
+	if (resmgr->codec_type != WCD9XXX_CDC_TYPE_HELICON) {
+		snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN1, 0x01, 0x01);
+		/* sleep required by codec hardware to enable clock buffer */
+		usleep_range(1000, 1200);
+		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);
+	} else {
+		snd_soc_update_bits(codec, MSM8X10_WCD_A_CDC_CLK_MCLK_CTL,
+				0x01, 0x01);
+	}
 	usleep_range(50, 50);
 
 	/* Notify */
@@ -493,7 +532,7 @@
 					       WCD9XXX_A_RC_OSC_FREQ) & 0x80));
 			/* disable clock block */
 			wcd9xxx_disable_clock_block(resmgr);
-			/* switch to RCO */
+			/* switch to MCLK */
 			wcd9xxx_enable_clock_block(resmgr, 0);
 			resmgr->clk_type = WCD9XXX_CLK_MCLK;
 		}
@@ -797,9 +836,10 @@
 
 int wcd9xxx_resmgr_init(struct wcd9xxx_resmgr *resmgr,
 			struct snd_soc_codec *codec,
-			struct wcd9xxx *wcd9xxx,
+			struct wcd9xxx_core_resource *core_res,
 			struct wcd9xxx_pdata *pdata,
-			struct wcd9xxx_reg_address *reg_addr)
+			struct wcd9xxx_reg_address *reg_addr,
+			enum wcd9xxx_cdc_type cdc_type)
 {
 	WARN(ARRAY_SIZE(wcd9xxx_event_string) != WCD9XXX_EVENT_LAST + 1,
 	     "Event string table isn't up to date!, %d != %d\n",
@@ -807,8 +847,9 @@
 
 	resmgr->bandgap_type = WCD9XXX_BANDGAP_OFF;
 	resmgr->codec = codec;
+	resmgr->codec_type = cdc_type;
 	/* This gives access of core handle to lock/unlock suspend */
-	resmgr->core = wcd9xxx;
+	resmgr->core_res = core_res;
 	resmgr->pdata = pdata;
 	resmgr->reg_addr = reg_addr;
 
diff --git a/sound/soc/codecs/wcd9xxx-resmgr.h b/sound/soc/codecs/wcd9xxx-resmgr.h
index e6a8f5d..7fb5820 100644
--- a/sound/soc/codecs/wcd9xxx-resmgr.h
+++ b/sound/soc/codecs/wcd9xxx-resmgr.h
@@ -13,6 +13,7 @@
 #define __WCD9XXX_COMMON_H__
 
 #include <linux/notifier.h>
+#include <linux/mfd/wcd9xxx/core-resource.h>
 #include <linux/mfd/wcd9xxx/wcd9xxx_registers.h>
 
 enum wcd9xxx_bandgap_type {
@@ -21,6 +22,13 @@
 	WCD9XXX_BANDGAP_MBHC_MODE,
 };
 
+enum wcd9xxx_cdc_type {
+	WCD9XXX_CDC_TYPE_INVALID = 0,
+	WCD9XXX_CDC_TYPE_TAIKO,
+	WCD9XXX_CDC_TYPE_TAPAN,
+	WCD9XXX_CDC_TYPE_HELICON,
+};
+
 enum wcd9xxx_clock_type {
 	WCD9XXX_CLK_OFF,
 	WCD9XXX_CLK_RCO,
@@ -102,7 +110,7 @@
 
 struct wcd9xxx_resmgr {
 	struct snd_soc_codec *codec;
-	struct wcd9xxx *core;
+	struct wcd9xxx_core_resource *core_res;
 
 	u32 rx_bias_count;
 
@@ -146,16 +154,20 @@
 	 */
 	struct mutex codec_resource_lock;
 	struct mutex codec_bg_clk_lock;
+
+	enum wcd9xxx_cdc_type codec_type;
 };
 
 int wcd9xxx_resmgr_init(struct wcd9xxx_resmgr *resmgr,
 			struct snd_soc_codec *codec,
-			struct wcd9xxx *wcd9xxx,
+			struct wcd9xxx_core_resource *core_res,
 			struct wcd9xxx_pdata *pdata,
-			struct wcd9xxx_reg_address *reg_addr);
+			struct wcd9xxx_reg_address *reg_addr,
+			enum wcd9xxx_cdc_type cdc_type);
 void wcd9xxx_resmgr_deinit(struct wcd9xxx_resmgr *resmgr);
 
-int wcd9xxx_resmgr_enable_config_mode(struct snd_soc_codec *codec, int enable);
+int wcd9xxx_resmgr_enable_config_mode(struct wcd9xxx_resmgr *resmgr,
+				int enable);
 
 void wcd9xxx_resmgr_enable_rx_bias(struct wcd9xxx_resmgr *resmgr, u32 enable);
 void wcd9xxx_resmgr_get_clk_block(struct wcd9xxx_resmgr *resmgr,
diff --git a/sound/soc/msm/msm8226.c b/sound/soc/msm/msm8226.c
index 19d717e..55a5e57 100644
--- a/sound/soc/msm/msm8226.c
+++ b/sound/soc/msm/msm8226.c
@@ -904,7 +904,7 @@
 };
 
 /* Digital audio interface glue - connects codec <---> CPU */
-static struct snd_soc_dai_link msm8226_dai[] = {
+static struct snd_soc_dai_link msm8226_common_dai[] = {
 	/* FrontEnd DAI Links */
 	{
 		.name = "MSM8226 Media1",
@@ -1289,6 +1289,61 @@
 		.ops = &msm_auxpcm_be_ops,
 		.ignore_suspend = 1
 	},
+	/* Incall Record Uplink BACK END DAI Link */
+	{
+		.name = LPASS_BE_INCALL_RECORD_TX,
+		.stream_name = "Voice Uplink Capture",
+		.cpu_dai_name = "msm-dai-q6-dev.32772",
+		.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_INCALL_RECORD_TX,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.ignore_suspend = 1,
+	},
+	/* Incall Record Downlink BACK END DAI Link */
+	{
+		.name = LPASS_BE_INCALL_RECORD_RX,
+		.stream_name = "Voice Downlink Capture",
+		.cpu_dai_name = "msm-dai-q6-dev.32771",
+		.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_INCALL_RECORD_RX,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.ignore_suspend = 1,
+	},
+	/* Incall Music BACK END DAI Link */
+	{
+		.name = LPASS_BE_VOICE_PLAYBACK_TX,
+		.stream_name = "Voice Farend Playback",
+		.cpu_dai_name = "msm-dai-q6-dev.32773",
+		.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_VOICE_PLAYBACK_TX,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.ignore_suspend = 1,
+	},
+	/* Incall Music 2 BACK END DAI Link */
+	{
+		.name = LPASS_BE_VOICE2_PLAYBACK_TX,
+		.stream_name = "Voice2 Farend Playback",
+		.cpu_dai_name = "msm-dai-q6-dev.32770",
+		.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_VOICE2_PLAYBACK_TX,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.ignore_suspend = 1,
+	},
+};
+
+static struct snd_soc_dai_link msm8226_9306_dai[] = {
 	/* Backend DAI Links */
 	{
 		.name = LPASS_BE_SLIMBUS_0_RX,
@@ -1402,64 +1457,142 @@
 		.ops = &msm8226_be_ops,
 		.ignore_suspend = 1,
 	},
-	/* Incall Record Uplink BACK END DAI Link */
+};
+
+static struct snd_soc_dai_link msm8226_9302_dai[] = {
+	/* Backend DAI Links */
 	{
-		.name = LPASS_BE_INCALL_RECORD_TX,
-		.stream_name = "Voice Uplink Capture",
-		.cpu_dai_name = "msm-dai-q6-dev.32772",
+		.name = LPASS_BE_SLIMBUS_0_RX,
+		.stream_name = "Slimbus Playback",
+		.cpu_dai_name = "msm-dai-q6-dev.16384",
 		.platform_name = "msm-pcm-routing",
-		.codec_name     = "msm-stub-codec.1",
-		.codec_dai_name = "msm-stub-tx",
+		.codec_name = "tapan_codec",
+		.codec_dai_name	= "tapan9302_rx1",
 		.no_pcm = 1,
-		.be_id = MSM_BACKEND_DAI_INCALL_RECORD_TX,
-		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.be_id = MSM_BACKEND_DAI_SLIMBUS_0_RX,
+		.init = &msm_audrx_init,
+		.be_hw_params_fixup = msm_slim_0_rx_be_hw_params_fixup,
+		.ops = &msm8226_be_ops,
+		.ignore_pmdown_time = 1, /* dai link has playback support */
 		.ignore_suspend = 1,
 	},
-	/* Incall Record Downlink BACK END DAI Link */
 	{
-		.name = LPASS_BE_INCALL_RECORD_RX,
-		.stream_name = "Voice Downlink Capture",
-		.cpu_dai_name = "msm-dai-q6-dev.32771",
+		.name = LPASS_BE_SLIMBUS_0_TX,
+		.stream_name = "Slimbus Capture",
+		.cpu_dai_name = "msm-dai-q6-dev.16385",
 		.platform_name = "msm-pcm-routing",
-		.codec_name     = "msm-stub-codec.1",
-		.codec_dai_name = "msm-stub-tx",
+		.codec_name = "tapan_codec",
+		.codec_dai_name	= "tapan9302_tx1",
 		.no_pcm = 1,
-		.be_id = MSM_BACKEND_DAI_INCALL_RECORD_RX,
-		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.be_id = MSM_BACKEND_DAI_SLIMBUS_0_TX,
+		.be_hw_params_fixup = msm_slim_0_tx_be_hw_params_fixup,
+		.ops = &msm8226_be_ops,
 		.ignore_suspend = 1,
 	},
-	/* Incall Music BACK END DAI Link */
 	{
-		.name = LPASS_BE_VOICE_PLAYBACK_TX,
-		.stream_name = "Voice Farend Playback",
-		.cpu_dai_name = "msm-dai-q6-dev.32773",
+		.name = LPASS_BE_SLIMBUS_1_RX,
+		.stream_name = "Slimbus1 Playback",
+		.cpu_dai_name = "msm-dai-q6-dev.16386",
 		.platform_name = "msm-pcm-routing",
-		.codec_name     = "msm-stub-codec.1",
-		.codec_dai_name = "msm-stub-rx",
+		.codec_name = "tapan_codec",
+		.codec_dai_name	= "tapan9302_rx1",
 		.no_pcm = 1,
-		.be_id = MSM_BACKEND_DAI_VOICE_PLAYBACK_TX,
-		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.be_id = MSM_BACKEND_DAI_SLIMBUS_1_RX,
+		.be_hw_params_fixup = msm_slim_0_rx_be_hw_params_fixup,
+		.ops = &msm8226_be_ops,
+		/* dai link has playback support */
+		.ignore_pmdown_time = 1,
 		.ignore_suspend = 1,
 	},
-	/* Incall Music 2 BACK END DAI Link */
 	{
-		.name = LPASS_BE_VOICE2_PLAYBACK_TX,
-		.stream_name = "Voice2 Farend Playback",
-		.cpu_dai_name = "msm-dai-q6-dev.32770",
+		.name = LPASS_BE_SLIMBUS_1_TX,
+		.stream_name = "Slimbus1 Capture",
+		.cpu_dai_name = "msm-dai-q6-dev.16387",
 		.platform_name = "msm-pcm-routing",
-		.codec_name     = "msm-stub-codec.1",
-		.codec_dai_name = "msm-stub-rx",
+		.codec_name = "tapan_codec",
+		.codec_dai_name	= "tapan9302_tx1",
 		.no_pcm = 1,
-		.be_id = MSM_BACKEND_DAI_VOICE2_PLAYBACK_TX,
-		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.be_id = MSM_BACKEND_DAI_SLIMBUS_1_TX,
+		.be_hw_params_fixup = msm_slim_0_tx_be_hw_params_fixup,
+		.ops = &msm8226_be_ops,
+		.ignore_suspend = 1,
+	},
+	{
+		.name = LPASS_BE_SLIMBUS_3_RX,
+		.stream_name = "Slimbus3 Playback",
+		.cpu_dai_name = "msm-dai-q6-dev.16390",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "tapan_codec",
+		.codec_dai_name	= "tapan9302_rx1",
+		.no_pcm = 1,
+		.be_id = MSM_BACKEND_DAI_SLIMBUS_3_RX,
+		.be_hw_params_fixup = msm_slim_0_rx_be_hw_params_fixup,
+		.ops = &msm8226_be_ops,
+		/* dai link has playback support */
+		.ignore_pmdown_time = 1,
+		.ignore_suspend = 1,
+	},
+	{
+		.name = LPASS_BE_SLIMBUS_3_TX,
+		.stream_name = "Slimbus3 Capture",
+		.cpu_dai_name = "msm-dai-q6-dev.16391",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "tapan_codec",
+		.codec_dai_name	= "tapan9302_tx1",
+		.no_pcm = 1,
+		.be_id = MSM_BACKEND_DAI_SLIMBUS_3_TX,
+		.be_hw_params_fixup = msm_slim_0_tx_be_hw_params_fixup,
+		.ops = &msm8226_be_ops,
+		.ignore_suspend = 1,
+	},
+	{
+		.name = LPASS_BE_SLIMBUS_4_RX,
+		.stream_name = "Slimbus4 Playback",
+		.cpu_dai_name = "msm-dai-q6-dev.16392",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "tapan_codec",
+		.codec_dai_name	= "tapan9302_rx1",
+		.no_pcm = 1,
+		.be_id = MSM_BACKEND_DAI_SLIMBUS_4_RX,
+		.be_hw_params_fixup = msm_slim_0_rx_be_hw_params_fixup,
+		.ops = &msm8226_be_ops,
+		/* dai link has playback support */
+		.ignore_pmdown_time = 1,
+		.ignore_suspend = 1,
+	},
+	{
+		.name = LPASS_BE_SLIMBUS_4_TX,
+		.stream_name = "Slimbus4 Capture",
+		.cpu_dai_name = "msm-dai-q6-dev.16393",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "tapan_codec",
+		.codec_dai_name	= "tapan9302_tx1",
+		.no_pcm = 1,
+		.be_id = MSM_BACKEND_DAI_SLIMBUS_4_TX,
+		.be_hw_params_fixup = msm_slim_0_tx_be_hw_params_fixup,
+		.ops = &msm8226_be_ops,
 		.ignore_suspend = 1,
 	},
 };
 
+static struct snd_soc_dai_link msm8226_9306_dai_links[
+				ARRAY_SIZE(msm8226_common_dai) +
+				ARRAY_SIZE(msm8226_9306_dai)];
+
+static struct snd_soc_dai_link msm8226_9302_dai_links[
+				ARRAY_SIZE(msm8226_common_dai) +
+				ARRAY_SIZE(msm8226_9302_dai)];
+
 struct snd_soc_card snd_soc_card_msm8226 = {
 	.name		= "msm8226-tapan-snd-card",
-	.dai_link	= msm8226_dai,
-	.num_links	= ARRAY_SIZE(msm8226_dai),
+	.dai_link	= msm8226_9306_dai_links,
+	.num_links	= ARRAY_SIZE(msm8226_9306_dai_links),
+};
+
+struct snd_soc_card snd_soc_card_9302_msm8226 = {
+	.name		= "msm8226-tapan9302-snd-card",
+	.dai_link	= msm8226_9302_dai_links,
+	.num_links	= ARRAY_SIZE(msm8226_9302_dai_links),
 };
 
 static int msm8226_dtparse_auxpcm(struct platform_device *pdev,
@@ -1534,7 +1667,7 @@
 		ret = gpio_request(pdata->mclk_gpio, "TAPAN_CODEC_PMIC_MCLK");
 		if (ret) {
 			dev_err(card->dev,
-				"%s: Failed to request taiko mclk gpio %d\n",
+				"%s: Failed to request tapan mclk gpio %d\n",
 				__func__, pdata->mclk_gpio);
 			return ret;
 		}
@@ -1580,9 +1713,36 @@
 	return 0;
 }
 
+static struct snd_soc_card *populate_snd_card_dailinks(struct device *dev)
+{
+
+	struct snd_soc_card *card;
+
+	if (of_property_read_bool(dev->of_node,
+					"qcom,tapan-codec-9302")) {
+		card = &snd_soc_card_9302_msm8226;
+
+		memcpy(msm8226_9302_dai_links, msm8226_common_dai,
+				sizeof(msm8226_common_dai));
+		memcpy(msm8226_9302_dai_links + ARRAY_SIZE(msm8226_common_dai),
+			msm8226_9302_dai, sizeof(msm8226_9302_dai));
+
+	} else {
+
+		card = &snd_soc_card_msm8226;
+
+		memcpy(msm8226_9306_dai_links, msm8226_common_dai,
+				sizeof(msm8226_common_dai));
+		memcpy(msm8226_9306_dai_links + ARRAY_SIZE(msm8226_common_dai),
+			msm8226_9306_dai, sizeof(msm8226_9306_dai));
+	}
+
+	return card;
+}
+
 static __devinit int msm8226_asoc_machine_probe(struct platform_device *pdev)
 {
-	struct snd_soc_card *card = &snd_soc_card_msm8226;
+	struct snd_soc_card *card;
 	struct msm8226_asoc_mach_data *pdata;
 	int ret;
 	const char *auxpcm_pri_gpio_set = NULL;
@@ -1600,14 +1760,7 @@
 		goto err;
 	}
 
-	/* Parse AUXPCM info from DT */
-	ret = msm8226_dtparse_auxpcm(pdev, &pdata->auxpcm_ctrl,
-					msm_auxpcm_gpio_name);
-	if (ret) {
-		dev_err(&pdev->dev,
-		"%s: Auxpcm pin data parse failed\n", __func__);
-		goto err;
-	}
+	card = populate_snd_card_dailinks(&pdev->dev);
 
 	card->dev = &pdev->dev;
 	platform_set_drvdata(pdev, card);
@@ -1649,6 +1802,33 @@
 		goto err;
 	}
 
+	ret = msm8226_prepare_codec_mclk(card);
+	if (ret)
+		goto err1;
+
+	mutex_init(&cdc_mclk_mutex);
+
+	mbhc_cfg.gpio_level_insert = of_property_read_bool(pdev->dev.of_node,
+					"qcom,headset-jack-type-NO");
+
+	ret = snd_soc_register_card(card);
+	if (ret == -EPROBE_DEFER)
+		goto err;
+	else if (ret) {
+		dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n",
+			ret);
+		goto err;
+	}
+
+	/* Parse AUXPCM info from DT */
+	ret = msm8226_dtparse_auxpcm(pdev, &pdata->auxpcm_ctrl,
+					msm_auxpcm_gpio_name);
+	if (ret) {
+		dev_err(&pdev->dev,
+		"%s: Auxpcm pin data parse failed\n", __func__);
+		goto err;
+	}
+
 	vdd_spkr_gpio = of_get_named_gpio(pdev->dev.of_node,
 				"qcom,cdc-vdd-spkr-gpios", 0);
 	if (vdd_spkr_gpio < 0) {
@@ -1686,22 +1866,8 @@
 		}
 	}
 
-	mbhc_cfg.gpio_level_insert = of_property_read_bool(pdev->dev.of_node,
-					"qcom,headset-jack-type-NO");
 	msm8226_setup_hs_jack(pdev, pdata);
 
-	ret = msm8226_prepare_codec_mclk(card);
-	if (ret)
-		goto err_lineout_spkr;
-
-	ret = snd_soc_register_card(card);
-	if (ret) {
-		dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n",
-			ret);
-		goto err_lineout_spkr;
-	}
-	mutex_init(&cdc_mclk_mutex);
-
 	ret = of_property_read_string(pdev->dev.of_node,
 			"qcom,prim-auxpcm-gpio-set", &auxpcm_pri_gpio_set);
 	if (ret) {
@@ -1747,6 +1913,7 @@
 		gpio_free(pdata->mclk_gpio);
 		pdata->mclk_gpio = 0;
 	}
+err1:
 	devm_kfree(&pdev->dev, pdata);
 	return ret;
 }
diff --git a/sound/soc/msm/msm8974.c b/sound/soc/msm/msm8974.c
index 769b8eb..25bc86b 100644
--- a/sound/soc/msm/msm8974.c
+++ b/sound/soc/msm/msm8974.c
@@ -269,7 +269,9 @@
 static void msm8974_liquid_ext_ult_spk_power_amp_enable(u32 on)
 {
 	if (on) {
-		regulator_enable(ext_spk_amp_regulator);
+		if (regulator_enable(ext_spk_amp_regulator))
+			pr_err("%s: enable failed ext_spk_amp_reg\n",
+				__func__);
 		gpio_direction_output(ext_ult_spk_amp_gpio, 1);
 		/* time takes enable the external power class AB amplifier */
 		usleep_range(EXT_CLASS_AB_EN_DELAY,
@@ -289,7 +291,9 @@
 static void msm8974_liquid_ext_spk_power_amp_enable(u32 on)
 {
 	if (on) {
-		regulator_enable(ext_spk_amp_regulator);
+		if (regulator_enable(ext_spk_amp_regulator))
+			pr_err("%s: enable failed ext_spk_amp_reg\n",
+				__func__);
 		gpio_direction_output(ext_spk_amp_gpio, on);
 		/*time takes enable the external power amplifier*/
 		usleep_range(EXT_CLASS_D_EN_DELAY,
@@ -2659,24 +2663,6 @@
 		return -ENOMEM;
 	}
 
-	/* Parse Primary AUXPCM info from DT */
-	ret = msm8974_dtparse_auxpcm(pdev, &pdata->pri_auxpcm_ctrl,
-					msm_prim_auxpcm_gpio_name);
-	if (ret) {
-		dev_err(&pdev->dev,
-		"%s: Primary Auxpcm pin data parse failed\n", __func__);
-		goto err;
-	}
-
-	/* Parse Secondary AUXPCM info from DT */
-	ret = msm8974_dtparse_auxpcm(pdev, &pdata->sec_auxpcm_ctrl,
-					msm_sec_auxpcm_gpio_name);
-	if (ret) {
-		dev_err(&pdev->dev,
-		"%s: Secondary Auxpcm pin data parse failed\n", __func__);
-		goto err;
-	}
-
 	card->dev = &pdev->dev;
 	platform_set_drvdata(pdev, card);
 	snd_soc_card_set_drvdata(card, pdata);
@@ -2717,26 +2703,9 @@
 		goto err;
 	}
 
-	ext_ult_lo_amp_gpio = of_get_named_gpio(pdev->dev.of_node,
-						prop_name_ult_lo_gpio, 0);
-	if (!gpio_is_valid(ext_ult_lo_amp_gpio)) {
-		dev_dbg(&pdev->dev,
-			"Couldn't find %s property in node %s, %d\n",
-			prop_name_ult_lo_gpio, pdev->dev.of_node->full_name,
-			ext_ult_lo_amp_gpio);
-	} else {
-		ret = gpio_request(ext_ult_lo_amp_gpio, "US_AMP_GPIO");
-		if (ret) {
-			dev_err(card->dev,
-				"%s: Failed to request US amp gpio %d\n",
-				__func__, ext_ult_lo_amp_gpio);
-			goto err;
-		}
-	}
-
 	ret = msm8974_prepare_codec_mclk(card);
 	if (ret)
-		goto err1;
+		goto err;
 
 	if (of_property_read_bool(pdev->dev.of_node, "qcom,hdmi-audio-rx")) {
 		dev_info(&pdev->dev, "%s(): hdmi audio support present\n",
@@ -2756,6 +2725,58 @@
 		card->dai_link	= msm8974_common_dai_links;
 		card->num_links	= ARRAY_SIZE(msm8974_common_dai_links);
 	}
+	mutex_init(&cdc_mclk_mutex);
+	atomic_set(&prim_auxpcm_rsc_ref, 0);
+	atomic_set(&sec_auxpcm_rsc_ref, 0);
+	spdev = pdev;
+	ext_spk_amp_regulator = NULL;
+	msm8974_liquid_dock_dev = NULL;
+
+	ret = snd_soc_register_card(card);
+	if (ret == -EPROBE_DEFER)
+		goto err;
+	else if (ret) {
+		dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n",
+			ret);
+		goto err;
+	}
+
+	/* Parse Primary AUXPCM info from DT */
+	ret = msm8974_dtparse_auxpcm(pdev, &pdata->pri_auxpcm_ctrl,
+					msm_prim_auxpcm_gpio_name);
+	if (ret) {
+		dev_err(&pdev->dev,
+		"%s: Primary Auxpcm pin data parse failed\n", __func__);
+		goto err;
+	}
+
+	/* Parse Secondary AUXPCM info from DT */
+	ret = msm8974_dtparse_auxpcm(pdev, &pdata->sec_auxpcm_ctrl,
+					msm_sec_auxpcm_gpio_name);
+	if (ret) {
+		dev_err(&pdev->dev,
+		"%s: Secondary Auxpcm pin data parse failed\n", __func__);
+		goto err;
+	}
+
+
+	ext_ult_lo_amp_gpio = of_get_named_gpio(pdev->dev.of_node,
+						prop_name_ult_lo_gpio, 0);
+	if (!gpio_is_valid(ext_ult_lo_amp_gpio)) {
+		dev_dbg(&pdev->dev,
+			"Couldn't find %s property in node %s, %d\n",
+			prop_name_ult_lo_gpio, pdev->dev.of_node->full_name,
+			ext_ult_lo_amp_gpio);
+	} else {
+		ret = gpio_request(ext_ult_lo_amp_gpio, "US_AMP_GPIO");
+		if (ret) {
+			dev_err(card->dev,
+				"%s: Failed to request US amp gpio %d\n",
+				__func__, ext_ult_lo_amp_gpio);
+			goto err;
+		}
+	}
+
 
 	pdata->us_euro_gpio = of_get_named_gpio(pdev->dev.of_node,
 				"qcom,us-euro-gpios", 0);
@@ -2774,20 +2795,6 @@
 		dev_err(&pdev->dev, "msm8974_prepare_us_euro failed (%d)\n",
 			ret);
 
-	mutex_init(&cdc_mclk_mutex);
-	atomic_set(&prim_auxpcm_rsc_ref, 0);
-	atomic_set(&sec_auxpcm_rsc_ref, 0);
-	spdev = pdev;
-	ext_spk_amp_regulator = NULL;
-	msm8974_liquid_dock_dev = NULL;
-
-	ret = snd_soc_register_card(card);
-	if (ret) {
-		dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n",
-			ret);
-		goto err1;
-	}
-
 	ret = of_property_read_string(pdev->dev.of_node,
 			"qcom,prim-auxpcm-gpio-set", &auxpcm_pri_gpio_set);
 	if (ret) {
@@ -2820,7 +2827,8 @@
 	return 0;
 
 err1:
-	gpio_free(ext_ult_lo_amp_gpio);
+	if (ext_ult_lo_amp_gpio >= 0)
+		gpio_free(ext_ult_lo_amp_gpio);
 	ext_ult_lo_amp_gpio = -1;
 err:
 	if (pdata->mclk_gpio > 0) {
@@ -2835,6 +2843,7 @@
 		gpio_free(pdata->us_euro_gpio);
 		pdata->us_euro_gpio = 0;
 	}
+	mutex_destroy(&cdc_mclk_mutex);
 	devm_kfree(&pdev->dev, pdata);
 	return ret;
 }
diff --git a/sound/soc/msm/msm8x10.c b/sound/soc/msm/msm8x10.c
index e1f1efc..bc36aae 100644
--- a/sound/soc/msm/msm8x10.c
+++ b/sound/soc/msm/msm8x10.c
@@ -39,12 +39,14 @@
 #define EXT_CLASS_D_DIS_DELAY 3000
 #define EXT_CLASS_D_DELAY_DELTA 2000
 
+#define CDC_EXT_CLK_RATE 9600000
+#define WCD9XXX_MBHC_DEF_BUTTONS 8
+#define WCD9XXX_MBHC_DEF_RLOADS 5
 
 static int msm_btsco_rate = BTSCO_RATE_8KHZ;
 static int msm_btsco_ch = 1;
 
 static int msm_proxy_rx_ch = 2;
-static struct snd_soc_jack hs_jack;
 static struct platform_device *spdev;
 static int ext_spk_amp_gpio = -1;
 
@@ -55,6 +57,23 @@
 static int msm_sec_mi2s_rx_ch = 1;
 static int msm_pri_mi2s_tx_ch = 1;
 
+static void *def_msm8x10_wcd_mbhc_cal(void);
+static int msm8x10_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_MICBIAS1,
+	.mclk_cb_fn = msm8x10_enable_codec_ext_clk,
+	.mclk_rate = CDC_EXT_CLK_RATE,
+	.gpio = 0,
+	.gpio_irq = 0,
+	.gpio_level_insert = 0,
+	.detect_extn_cable = false,
+	.insert_detect = true,
+	.swap_gnd_mic = NULL,
+};
+
 /*
  * There is limitation for the clock root selection from
  * either MI2S or DIG_CODEC.
@@ -450,6 +469,19 @@
 
 	pr_debug("%s(),dev_name%s\n", __func__, dev_name(cpu_dai->dev));
 	msm8x10_ext_spk_power_amp_init();
+
+	mbhc_cfg.calibration = def_msm8x10_wcd_mbhc_cal();
+	if (mbhc_cfg.calibration) {
+		ret = msm8x10_wcd_hs_detect(codec, &mbhc_cfg);
+		if (ret) {
+			pr_err("%s: msm8x10_wcd_hs_detect failed\n", __func__);
+			goto exit;
+		}
+	} else {
+		ret = -ENOMEM;
+		goto exit;
+	}
+
 	snd_soc_dapm_new_controls(dapm, msm8x10_dapm_widgets,
 				ARRAY_SIZE(msm8x10_dapm_widgets));
 
@@ -461,15 +493,92 @@
 	if (ret < 0)
 		return ret;
 
-	ret = snd_soc_jack_new(codec, "Headset Jack",
-			SND_JACK_HEADSET, &hs_jack);
-	if (ret) {
-		pr_err("%s: Failed to create headset jack\n", __func__);
-	}
+exit:
+	if (gpio_is_valid(ext_spk_amp_gpio))
+		gpio_free(ext_spk_amp_gpio);
 
 	return ret;
 }
 
+static void *def_msm8x10_wcd_mbhc_cal(void)
+{
+	void *msm8x10_wcd_cal;
+	struct wcd9xxx_mbhc_btn_detect_cfg *btn_cfg;
+	u16 *btn_low, *btn_high;
+	u8 *n_ready, *n_cic, *gain;
+
+	msm8x10_wcd_cal = kzalloc(WCD9XXX_MBHC_CAL_SIZE(
+						WCD9XXX_MBHC_DEF_BUTTONS,
+						WCD9XXX_MBHC_DEF_RLOADS),
+					GFP_KERNEL);
+	if (!msm8x10_wcd_cal) {
+		pr_err("%s: out of memory\n", __func__);
+		return NULL;
+	}
+
+#define S(X, Y) ((WCD9XXX_MBHC_CAL_GENERAL_PTR(msm8x10_wcd_cal)->X) = (Y))
+	S(t_ldoh, 100);
+	S(t_bg_fast_settle, 100);
+	S(t_shutdown_plug_rem, 255);
+	S(mbhc_nsa, 2);
+	S(mbhc_navg, 128);
+#undef S
+#define S(X, Y) ((WCD9XXX_MBHC_CAL_PLUG_DET_PTR(msm8x10_wcd_cal)->X) = (Y))
+	S(mic_current, MSM8X10_WCD_PID_MIC_5_UA);
+	S(hph_current, MSM8X10_WCD_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(msm8x10_wcd_cal)->X) = (Y))
+	S(v_no_mic, 30);
+	S(v_hs_max, 1650);
+#undef S
+#define S(X, Y) ((WCD9XXX_MBHC_CAL_BTN_DET_PTR(msm8x10_wcd_cal)->X) = (Y))
+	S(c[0], 62);
+	S(c[1], 124);
+	S(nc, 1);
+	S(n_meas, 5);
+	S(mbhc_nsc, 10);
+	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(msm8x10_wcd_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] = 10;
+	btn_low[1] = 11;
+	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] = 14;
+
+	return msm8x10_wcd_cal;
+}
+
 static int msm_proxy_rx_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
 					struct snd_pcm_hw_params *params)
 {
@@ -885,19 +994,38 @@
 	if (ret)
 		goto err;
 
+	mutex_init(&cdc_mclk_mutex);
 	pcbcr = ioremap(MSM8X10_DINO_LPASS_DIGCODEC_CBCR, 4);
+	if (!pcbcr) {
+		ret = -ENOMEM;
+		goto err1;
+	}
 	prcgr = ioremap(MSM8X10_DINO_LPASS_DIGCODEC_CMD_RCGR, 4);
+	if (!prcgr) {
+		ret = -ENOMEM;
+		goto err1;
+	}
+	atomic_set(&mclk_rsc_ref, 0);
+	mbhc_cfg.gpio_level_insert = of_property_read_bool(pdev->dev.of_node,
+						"qcom,headset-jack-type-NC");
 
 	spdev = pdev;
+
 	ret = snd_soc_register_card(card);
-	if (ret) {
+	if (ret == -EPROBE_DEFER)
+		goto err1;
+	else if (ret) {
 		dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n",
 			ret);
-		goto err;
+		goto err1;
 	}
-	mutex_init(&cdc_mclk_mutex);
-	atomic_set(&mclk_rsc_ref, 0);
 	return 0;
+err1:
+	mutex_destroy(&cdc_mclk_mutex);
+	if (pcbcr)
+		iounmap(pcbcr);
+	if (prcgr)
+		iounmap(prcgr);
 err:
 	return ret;
 }
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
index 79016b5..de98feb 100644
--- a/sound/soc/soc-core.c
+++ b/sound/soc/soc-core.c
@@ -55,7 +55,6 @@
 #endif
 
 static DEFINE_MUTEX(client_mutex);
-static LIST_HEAD(card_list);
 static LIST_HEAD(dai_list);
 static LIST_HEAD(platform_list);
 static LIST_HEAD(codec_list);
@@ -850,15 +849,9 @@
 	struct snd_soc_dai *codec_dai, *cpu_dai;
 	const char *platform_name;
 
-	if (rtd->complete)
-		return 1;
 	dev_dbg(card->dev, "binding %s at idx %d\n", dai_link->name, num);
 
-	/* do we already have the CPU DAI for this link ? */
-	if (rtd->cpu_dai) {
-		goto find_codec;
-	}
-	/* no, then find CPU DAI from registered DAIs*/
+	/* Find CPU DAI from registered DAIs*/
 	list_for_each_entry(cpu_dai, &dai_list, list) {
 		if (dai_link->cpu_dai_of_node) {
 			if (cpu_dai->dev->of_node != dai_link->cpu_dai_of_node)
@@ -869,15 +862,13 @@
 		}
 
 		rtd->cpu_dai = cpu_dai;
-		goto find_codec;
 	}
-	dev_dbg(card->dev, "CPU DAI %s not registered\n",
-			dai_link->cpu_dai_name);
 
-find_codec:
-	/* do we already have the CODEC for this link ? */
-	if (rtd->codec) {
-		goto find_platform;
+	if (!rtd->cpu_dai) {
+		dev_dbg(card->dev, "CPU DAI %s not registered\n",
+			dai_link->cpu_dai_name);
+		return -EPROBE_DEFER;
+
 	}
 
 	/* no, then find CODEC from registered CODECs*/
@@ -902,21 +893,21 @@
 					dai_link->codec_dai_name)) {
 
 				rtd->codec_dai = codec_dai;
-				goto find_platform;
 			}
 		}
-		dev_dbg(card->dev, "CODEC DAI %s not registered\n",
+		if (!rtd->codec_dai) {
+			dev_dbg(card->dev, "CODEC DAI %s not registered\n",
 				dai_link->codec_dai_name);
+			return -EPROBE_DEFER;
+		}
 
-		goto find_platform;
 	}
-	dev_dbg(card->dev, "CODEC %s not registered\n",
-			dai_link->codec_name);
 
-find_platform:
-	/* do we need a platform? */
-	if (rtd->platform)
-		goto out;
+	if (!rtd->codec) {
+		dev_dbg(card->dev, "CODEC %s not registered\n",
+			dai_link->codec_name);
+		return -EPROBE_DEFER;
+	}
 
 	/* if there's no platform we match on the empty platform */
 	platform_name = dai_link->platform_name;
@@ -935,20 +926,17 @@
 		}
 
 		rtd->platform = platform;
-		goto out;
 	}
-
-	dev_dbg(card->dev, "platform %s not registered\n",
+	if (!rtd->platform) {
+		dev_dbg(card->dev, "platform %s not registered\n",
 			dai_link->platform_name);
+
+		return -EPROBE_DEFER;
+	}
+	card->num_rtd++;
+
 	return 0;
 
-out:
-	/* mark rtd as complete if we found all 4 of our client devices */
-	if (rtd->codec && rtd->codec_dai && rtd->platform && rtd->cpu_dai) {
-		rtd->complete = 1;
-		card->num_rtd++;
-	}
-	return 1;
 }
 
 static void soc_remove_codec(struct snd_soc_codec *codec)
@@ -1399,6 +1387,20 @@
 }
 #endif
 
+static int soc_check_aux_dev(struct snd_soc_card *card, int num)
+{
+	struct snd_soc_aux_dev *aux_dev = &card->aux_dev[num];
+	struct snd_soc_codec *codec;
+
+	/* find CODEC from registered CODECs*/
+	list_for_each_entry(codec, &codec_list, list) {
+		if (!strcmp(codec->name, aux_dev->codec_name))
+			return 0;
+	}
+
+	return -EPROBE_DEFER;
+}
+
 static int soc_probe_aux_dev(struct snd_soc_card *card, int num)
 {
 	struct snd_soc_aux_dev *aux_dev = &card->aux_dev[num];
@@ -1419,7 +1421,7 @@
 	}
 	/* codec not found */
 	dev_err(card->dev, "asoc: codec %s not found", aux_dev->codec_name);
-	goto out;
+	return -EPROBE_DEFER;
 
 found:
 	ret = soc_probe_codec(card, codec);
@@ -1559,7 +1561,7 @@
 }
 
 
-static void snd_soc_instantiate_card(struct snd_soc_card *card)
+static int snd_soc_instantiate_card(struct snd_soc_card *card)
 {
 	struct snd_soc_codec *codec;
 	struct snd_soc_codec_conf *codec_conf;
@@ -1569,19 +1571,19 @@
 
 	mutex_lock(&card->mutex);
 
-	if (card->instantiated) {
-		mutex_unlock(&card->mutex);
-		return;
-	}
 
 	/* bind DAIs */
-	for (i = 0; i < card->num_links; i++)
-		soc_bind_dai_link(card, i);
+	for (i = 0; i < card->num_links; i++) {
+		ret = soc_bind_dai_link(card, i);
+		if (ret != 0)
+			goto base_error;
+	}
 
-	/* bind completed ? */
-	if (card->num_rtd != card->num_links) {
-		mutex_unlock(&card->mutex);
-		return;
+	/* check aux_devs too */
+	for (i = 0; i < card->num_aux_devs; i++) {
+		ret = soc_check_aux_dev(card, i);
+		if (ret != 0)
+			goto base_error;
 	}
 
 	/* initialize the register cache for each available codec */
@@ -1601,10 +1603,8 @@
 			}
 		}
 		ret = snd_soc_init_codec_cache(codec, compress_type);
-		if (ret < 0) {
-			mutex_unlock(&card->mutex);
-			return;
-		}
+		if (ret < 0)
+			goto base_error;
 	}
 
 	/* card bind complete so register a sound card */
@@ -1613,8 +1613,7 @@
 	if (ret < 0) {
 		printk(KERN_ERR "asoc: can't create sound card for card %s\n",
 			card->name);
-		mutex_unlock(&card->mutex);
-		return;
+		goto base_error;
 	}
 	card->snd_card->dev = card->dev;
 
@@ -1751,7 +1750,7 @@
 	card->instantiated = 1;
 	snd_soc_dapm_sync(&card->dapm);
 	mutex_unlock(&card->mutex);
-	return;
+	return 0;
 
 probe_aux_dev_err:
 	for (i = 0; i < card->num_aux_devs; i++)
@@ -1765,19 +1764,9 @@
 		card->remove(card);
 
 	snd_card_free(card->snd_card);
-
+base_error:
 	mutex_unlock(&card->mutex);
-}
-
-/*
- * Attempt to initialise any uninitialised cards.  Must be called with
- * client_mutex.
- */
-static void snd_soc_instantiate_cards(void)
-{
-	struct snd_soc_card *card;
-	list_for_each_entry(card, &card_list, list)
-		snd_soc_instantiate_card(card);
+	return ret;
 }
 
 /* probes a new socdev */
@@ -2035,6 +2024,10 @@
 {
 	unsigned int ret;
 
+	if (unlikely(!snd_card_is_online_state(codec->card->snd_card))) {
+		dev_err(codec->dev, "read 0x%02x while offline\n", reg);
+		return -ENODEV;
+	}
 	ret = codec->read(codec, reg);
 	dev_dbg(codec->dev, "read %x => %x\n", reg, ret);
 	trace_snd_soc_reg_read(codec, reg, ret);
@@ -2046,6 +2039,10 @@
 unsigned int snd_soc_write(struct snd_soc_codec *codec,
 			   unsigned int reg, unsigned int val)
 {
+	if (unlikely(!snd_card_is_online_state(codec->card->snd_card))) {
+		dev_err(codec->dev, "write 0x%02x while offline\n", reg);
+		return -ENODEV;
+	}
 	dev_dbg(codec->dev, "write %x = %x\n", reg, val);
 	trace_snd_soc_reg_write(codec, reg, val);
 	return codec->write(codec, reg, val);
@@ -3233,12 +3230,10 @@
 	mutex_init(&card->dpcm_mutex);
 	mutex_init(&card->dapm_power_mutex);
 
-	mutex_lock(&client_mutex);
-	list_add(&card->list, &card_list);
-	snd_soc_instantiate_cards();
-	mutex_unlock(&client_mutex);
+	ret = snd_soc_instantiate_card(card);
+	if (ret != 0)
+		soc_cleanup_card_debugfs(card);
 
-	dev_dbg(card->dev, "Registered card '%s'\n", card->name);
 
 	return ret;
 }
@@ -3254,9 +3249,6 @@
 {
 	if (card->instantiated)
 		soc_cleanup_card_resources(card);
-	mutex_lock(&client_mutex);
-	list_del(&card->list);
-	mutex_unlock(&client_mutex);
 	dev_dbg(card->dev, "Unregistered card '%s'\n", card->name);
 
 	return 0;
@@ -3352,7 +3344,6 @@
 
 	mutex_lock(&client_mutex);
 	list_add(&dai->list, &dai_list);
-	snd_soc_instantiate_cards();
 	mutex_unlock(&client_mutex);
 
 	pr_debug("Registered DAI '%s'\n", dai->name);
@@ -3434,9 +3425,6 @@
 		pr_debug("Registered DAI '%s'\n", dai->name);
 	}
 
-	mutex_lock(&client_mutex);
-	snd_soc_instantiate_cards();
-	mutex_unlock(&client_mutex);
 	return 0;
 
 err:
@@ -3493,7 +3481,6 @@
 
 	mutex_lock(&client_mutex);
 	list_add(&platform->list, &platform_list);
-	snd_soc_instantiate_cards();
 	mutex_unlock(&client_mutex);
 
 	pr_debug("Registered platform '%s'\n", platform->name);
@@ -3562,6 +3549,17 @@
 }
 
 /**
+ * snd_soc_card_change_online_state - Mark if soc card is online/offline
+ *
+ * @soc_card : soc_card to mark
+ */
+void snd_soc_card_change_online_state(struct snd_soc_card *soc_card, int online)
+{
+	snd_card_change_online_state(soc_card->snd_card, online);
+}
+EXPORT_SYMBOL(snd_soc_card_change_online_state);
+
+/**
  * snd_soc_register_codec - Register a codec with the ASoC core
  *
  * @codec: codec to register
@@ -3651,7 +3649,6 @@
 
 	mutex_lock(&client_mutex);
 	list_add(&codec->list, &codec_list);
-	snd_soc_instantiate_cards();
 	mutex_unlock(&client_mutex);
 
 	pr_debug("Registered codec '%s'\n", codec->name);