Merge "Merge android-4.9.95 (b4c14c2) into msm-4.9"
diff --git a/Documentation/devicetree/bindings/iio/imu/invn-icm20602.txt b/Documentation/devicetree/bindings/iio/imu/invn-icm20602.txt
new file mode 100644
index 0000000..27c304e
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/imu/invn-icm20602.txt
@@ -0,0 +1,28 @@
+The ICM20602 sensor is 6-axis gyroscope+accelerometer combo
+device which is made by InvenSense Inc.
+
+Required properties:
+
+ - compatible	: Should be "invensense,icm20602".
+ - reg	: the I2C address which depends on the AD0 pin.
+ - interrupt-parent	: should be the phandle for the interrupt controller
+ - interrupts	: interrupt mapping for GPIO IRQ
+
+Optional properties:
+ - invensense,icm20602-gpio:  the irq gpio. This is not used if
+ using SMD to trigger. this is needed only if using the
+ device IRQ to trigger. Only using SMD to trigger can
+ support 8K sample rate.
+
+Example:
+	icm20602-i2c@068 {
+		compatible = "invensense,icm20602";
+		reg = <0x68>;
+		interrupt-parent = <&msm_gpio>;
+		interrupts = <13 0>;
+		invensense,icm20602-gpio = <&msm_gpio 13 0x0>;
+		pinctrl-names = "imu_active","imu_suspend";
+		pinctrl-0 = <&imu_int_active>;
+		pinctrl-1 = <&imu_int_suspend>;
+		status = "okay";
+	};
diff --git a/Documentation/devicetree/bindings/power/supply/qcom/qpnp-qg.txt b/Documentation/devicetree/bindings/power/supply/qcom/qpnp-qg.txt
index efa67f5..afeb65d 100644
--- a/Documentation/devicetree/bindings/power/supply/qcom/qpnp-qg.txt
+++ b/Documentation/devicetree/bindings/power/supply/qcom/qpnp-qg.txt
@@ -191,6 +191,77 @@
 		    temperature specific configuration as applied. If not
 		    specified, the default value is 0 degree centigrade.
 
+- qcom,cl-disable
+	Usage:      optional
+	Value type: <empty>
+	Definition: A boolean property to disable the battery capacity
+		    learning when charging.
+
+- qcom,cl-feedback-on
+	Usage:      optional
+	Value type: <empty>
+	Definition: A boolean property to feedback the learned capacity into
+		    the capacity lerning algorithm. This has to be used only if the
+		    property "qcom,cl-disable" is not specified.
+
+- qcom,cl-max-start-soc
+	Usage:      optional
+	Value type: <u32>
+	Definition: Battery SOC has to be below or equal to this value at the
+		    start of a charge cycle to start the capacity learning.
+		    If this is not specified, then the default value used
+		    will be 15. Unit is in percentage.
+
+- qcom,cl-min-start-soc
+	Usage:      optional
+	Value type: <u32>
+	Definition: Battery SOC has to be above or equal to this value at the
+		    start of a charge cycle to start the capacity learning.
+		    If this is not specified, then the default value used
+		    will be 10. Unit is in percentage.
+
+- qcom,cl-min-temp
+	Usage:      optional
+	Value type: <u32>
+	Definition: Lower limit of battery temperature to start the capacity
+		    learning. If this is not specified, then the default value
+		    used will be 150 (15 C). Unit is in decidegC.
+
+- qcom,cl-max-temp
+	Usage:      optional
+	Value type: <u32>
+	Definition: Upper limit of battery temperature to start the capacity
+		    learning. If this is not specified, then the default value
+		    used will be 500 (50 C). Unit is in decidegC.
+
+- qcom,cl-max-increment
+	Usage:      optional
+	Value type: <u32>
+	Definition: Maximum capacity increment allowed per capacity learning
+		    cycle. If this is not specified, then the default value
+		    used will be 5 (0.5%). Unit is in decipercentage.
+
+- qcom,cl-max-decrement
+	Usage:      optional
+	Value type: <u32>
+	Definition: Maximum capacity decrement allowed per capacity learning
+		    cycle. If this is not specified, then the default value
+		    used will be 100 (10%). Unit is in decipercentage.
+
+- qcom,cl-min-limit
+	Usage:      optional
+	Value type: <u32>
+	Definition: Minimum limit that the capacity cannot go below in a
+		    capacity learning cycle. If this is not specified, then
+		    the default value is 0. Unit is in decipercentage.
+
+- qcom,cl-max-limit
+	Usage:      optional
+	Value type: <u32>
+	Definition: Maximum limit that the capacity cannot go above in a
+		    capacity learning cycle. If this is not specified, then
+		    the default value is 0. Unit is in decipercentage.
+
 ==========================================================
 Second Level Nodes - Peripherals managed by QGAUGE driver
 ==========================================================
diff --git a/arch/arm/boot/dts/qcom/sdxpoorwills.dtsi b/arch/arm/boot/dts/qcom/sdxpoorwills.dtsi
index c6608a8..68a13ee 100644
--- a/arch/arm/boot/dts/qcom/sdxpoorwills.dtsi
+++ b/arch/arm/boot/dts/qcom/sdxpoorwills.dtsi
@@ -1075,6 +1075,12 @@
 		#mbox-cells = <1>;
 	};
 
+	aop-msg-client {
+		compatible = "qcom,debugfs-qmp-client";
+		mboxes = <&qmp_aop 0>;
+		mbox-names = "aop";
+	};
+
 	vbus_detect: qcom,pmd-vbus-det {
 		compatible = "qcom,pmd-vbus-det";
 		interrupt-parent = <&spmi_bus>;
diff --git a/arch/arm/configs/msm8909-perf_defconfig b/arch/arm/configs/msm8909-perf_defconfig
index aee8104..9f5001f 100644
--- a/arch/arm/configs/msm8909-perf_defconfig
+++ b/arch/arm/configs/msm8909-perf_defconfig
@@ -259,7 +259,9 @@
 CONFIG_PPP_ASYNC=y
 CONFIG_PPP_SYNC_TTY=y
 CONFIG_WCNSS_MEM_PRE_ALLOC=y
-CONFIG_CLD_LL_CORE=y
+CONFIG_CNSS=y
+CONFIG_CNSS_SDIO=y
+CONFIG_CLD_HL_SDIO_CORE=y
 CONFIG_INPUT_EVDEV=y
 CONFIG_KEYBOARD_GPIO=y
 CONFIG_INPUT_JOYSTICK=y
@@ -423,6 +425,7 @@
 CONFIG_MSM_EVENT_TIMER=y
 CONFIG_QTI_RPM_STATS_LOG=y
 CONFIG_QCOM_FORCE_WDOG_BITE_ON_PANIC=y
+CONFIG_CNSS_CRYPTO=y
 CONFIG_PWM=y
 CONFIG_PWM_QPNP=y
 CONFIG_QTI_MPM=y
diff --git a/arch/arm/configs/msm8909_defconfig b/arch/arm/configs/msm8909_defconfig
index b82c3fd..c8087ad 100644
--- a/arch/arm/configs/msm8909_defconfig
+++ b/arch/arm/configs/msm8909_defconfig
@@ -253,7 +253,9 @@
 CONFIG_PPP_ASYNC=y
 CONFIG_PPP_SYNC_TTY=y
 CONFIG_WCNSS_MEM_PRE_ALLOC=y
-CONFIG_CLD_LL_CORE=y
+CONFIG_CNSS=y
+CONFIG_CNSS_SDIO=y
+CONFIG_CLD_HL_SDIO_CORE=y
 CONFIG_INPUT_EVDEV=y
 CONFIG_KEYBOARD_GPIO=y
 CONFIG_INPUT_JOYSTICK=y
@@ -418,6 +420,7 @@
 CONFIG_MSM_EVENT_TIMER=y
 CONFIG_QTI_RPM_STATS_LOG=y
 CONFIG_QCOM_FORCE_WDOG_BITE_ON_PANIC=y
+CONFIG_CNSS_CRYPTO=y
 CONFIG_PWM=y
 CONFIG_PWM_QPNP=y
 CONFIG_QTI_MPM=y
diff --git a/arch/arm/configs/msm8909w-perf_defconfig b/arch/arm/configs/msm8909w-perf_defconfig
index 9b55740..fb711f6 100644
--- a/arch/arm/configs/msm8909w-perf_defconfig
+++ b/arch/arm/configs/msm8909w-perf_defconfig
@@ -30,7 +30,7 @@
 CONFIG_EMBEDDED=y
 CONFIG_PROFILING=y
 CONFIG_OPROFILE=y
-CONFIG_CC_STACKPROTECTOR_STRONG=y
+CONFIG_CC_STACKPROTECTOR_REGULAR=y
 CONFIG_MODULES=y
 CONFIG_MODULE_UNLOAD=y
 CONFIG_MODULE_FORCE_UNLOAD=y
@@ -132,6 +132,7 @@
 CONFIG_NETFILTER_XT_MATCH_QTAGUID=y
 CONFIG_NETFILTER_XT_MATCH_QUOTA=y
 CONFIG_NETFILTER_XT_MATCH_QUOTA2=y
+CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG=y
 CONFIG_NETFILTER_XT_MATCH_SOCKET=y
 CONFIG_NETFILTER_XT_MATCH_STATE=y
 CONFIG_NETFILTER_XT_MATCH_STATISTIC=y
@@ -498,6 +499,7 @@
 CONFIG_CRYPTO_ANSI_CPRNG=y
 CONFIG_CRYPTO_DEV_QCE=y
 CONFIG_CRYPTO_DEV_QCOM_MSM_QCE=y
+CONFIG_CRYPTO_DEV_QCRYPTO=y
 CONFIG_CRYPTO_DEV_QCEDEV=y
 CONFIG_CRYPTO_DEV_OTA_CRYPTO=y
 CONFIG_CRYPTO_DEV_QCOM_ICE=y
diff --git a/arch/arm/configs/msm8909w_defconfig b/arch/arm/configs/msm8909w_defconfig
index bb6bc88..de6fb6b 100644
--- a/arch/arm/configs/msm8909w_defconfig
+++ b/arch/arm/configs/msm8909w_defconfig
@@ -34,7 +34,7 @@
 CONFIG_EMBEDDED=y
 CONFIG_PROFILING=y
 CONFIG_OPROFILE=y
-CONFIG_CC_STACKPROTECTOR_STRONG=y
+CONFIG_CC_STACKPROTECTOR_REGULAR=y
 CONFIG_MODULES=y
 CONFIG_MODULE_UNLOAD=y
 CONFIG_MODULE_FORCE_UNLOAD=y
@@ -141,6 +141,7 @@
 CONFIG_NETFILTER_XT_MATCH_QTAGUID=y
 CONFIG_NETFILTER_XT_MATCH_QUOTA=y
 CONFIG_NETFILTER_XT_MATCH_QUOTA2=y
+CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG=y
 CONFIG_NETFILTER_XT_MATCH_SOCKET=y
 CONFIG_NETFILTER_XT_MATCH_STATE=y
 CONFIG_NETFILTER_XT_MATCH_STATISTIC=y
diff --git a/arch/arm/configs/sdxpoorwills-perf_defconfig b/arch/arm/configs/sdxpoorwills-perf_defconfig
index 0e971d2..52b3158 100644
--- a/arch/arm/configs/sdxpoorwills-perf_defconfig
+++ b/arch/arm/configs/sdxpoorwills-perf_defconfig
@@ -222,6 +222,7 @@
 CONFIG_INPUT_GPIO=m
 CONFIG_SERIO_LIBPS2=y
 # CONFIG_LEGACY_PTYS is not set
+CONFIG_SERIAL_MSM=y
 CONFIG_SERIAL_MSM_HS=y
 CONFIG_DIAG_CHAR=y
 CONFIG_HW_RANDOM=y
diff --git a/arch/arm/mach-qcom/board-sdm429.c b/arch/arm/mach-qcom/board-sdm429.c
index c648eaf..6bdf0f4 100644
--- a/arch/arm/mach-qcom/board-sdm429.c
+++ b/arch/arm/mach-qcom/board-sdm429.c
@@ -17,6 +17,7 @@
 
 static const char *sdm429_dt_match[] __initconst = {
 	"qcom,sdm429",
+	"qcom,sda429",
 	NULL
 };
 
diff --git a/arch/arm/mach-qcom/board-sdm439.c b/arch/arm/mach-qcom/board-sdm439.c
index 312f3a5..7b9aa0f 100644
--- a/arch/arm/mach-qcom/board-sdm439.c
+++ b/arch/arm/mach-qcom/board-sdm439.c
@@ -17,6 +17,7 @@
 
 static const char *sdm439_dt_match[] __initconst = {
 	"qcom,sdm439",
+	"qcom,sda439",
 	NULL
 };
 
diff --git a/arch/arm64/boot/dts/qcom/8909w-pm660.dtsi b/arch/arm64/boot/dts/qcom/8909w-pm660.dtsi
index 3947406..c8330bd 100644
--- a/arch/arm64/boot/dts/qcom/8909w-pm660.dtsi
+++ b/arch/arm64/boot/dts/qcom/8909w-pm660.dtsi
@@ -79,6 +79,31 @@
 	};
 };
 
+&msm_gpio {
+	/delete-node/ mpu6050_int_pin;
+	/delete-node/ apds99xx_int_pin;
+	/delete-node/ ak8963_int_pin;
+
+	pmx_mdss {
+		mdss_dsi_active: mdss_dsi_active {
+			mux {
+				pins = "gpio25", "gpio37", "gpio59";
+			};
+			config {
+				pins = "gpio25", "gpio37", "gpio59";
+			};
+		};
+		mdss_dsi_suspend: mdss_dsi_suspend {
+			mux {
+				pins = "gpio25", "gpio37", "gpio59";
+			};
+			config {
+				pins = "gpio25", "gpio37", "gpio59";
+			};
+		};
+	};
+};
+
 &i2c_3 {
 	qcom,actuator@0 {
 		/delete-property/ cam_vaf-supply;
diff --git a/arch/arm64/boot/dts/qcom/Makefile b/arch/arm64/boot/dts/qcom/Makefile
index 31d4b0d..3c2ae63 100644
--- a/arch/arm64/boot/dts/qcom/Makefile
+++ b/arch/arm64/boot/dts/qcom/Makefile
@@ -398,6 +398,10 @@
 	apq8053-iot-mtp.dtb \
 	apq8053-lite-dragon-v1.0.dtb \
 	apq8053-lite-dragon-v2.0.dtb \
+	apq8053-lite-lenovo-v1.0.dtb \
+	apq8053-lite-lenovo-v1.1.dtb \
+	apq8053-lite-harman-v1.0.dtb \
+	apq8053-lite-lge-v1.0.dtb \
 	msm8953-pmi8940-cdp.dtb \
 	msm8953-pmi8940-mtp.dtb \
 	msm8953-pmi8937-cdp.dtb \
@@ -422,6 +426,7 @@
 	apq8009w-bg-wtp-v2.dtb \
 	apq8009w-bg-alpha.dtb \
 	apq8009-mtp-wcd9326-refboard.dtb \
+	apq8009-robot-som-refboard.dtb \
 	apq8009-dragon.dtb
 
 dtb-$(CONFIG_ARCH_SDM450) += sdm450-rcm.dtb \
diff --git a/arch/arm64/boot/dts/qcom/apq8009-dragon.dts b/arch/arm64/boot/dts/qcom/apq8009-dragon.dts
index 6b3aea0..12a4363 100644
--- a/arch/arm64/boot/dts/qcom/apq8009-dragon.dts
+++ b/arch/arm64/boot/dts/qcom/apq8009-dragon.dts
@@ -66,6 +66,11 @@
 	mdss_mdp: qcom,mdss_mdp@1a00000 {
 		status = "disabled";
 	};
+
+	bluetooth: bt_qca9379 {
+		compatible = "qca,qca9379";
+		qca,bt-reset-gpio = <&msm_gpio 47 0>; /* BT_EN */
+	};
 };
 
 &sdhc_2 {
@@ -133,3 +138,7 @@
 &pm8916_bms {
 	status = "ok";
 };
+
+&blsp1_uart2_hs {
+	status = "ok";
+};
diff --git a/arch/arm64/boot/dts/qcom/apq8009-robot-som-refboard.dts b/arch/arm64/boot/dts/qcom/apq8009-robot-som-refboard.dts
new file mode 100644
index 0000000..72486b2
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/apq8009-robot-som-refboard.dts
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2018, 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.
+ */
+
+/dts-v1/;
+
+#include "msm8909-mtp.dtsi"
+#include "8909-pm8916.dtsi"
+#include "msm8909-pm8916-mtp.dtsi"
+#include "apq8009-audio-external_codec.dtsi"
+
+/ {
+	model = "Qualcomm Technologies, Inc. APQ8009 Robot SOM refboard";
+	compatible = "qcom,apq8009-mtp", "qcom,apq8009", "qcom,mtp";
+	qcom,msm-id = <265 2>;
+	qcom,board-id = <8 0x15>;
+};
+
+&soc {
+	ext-codec {
+		qcom,msm-mbhc-hphl-swh = <0>;
+		qcom,audio-routing =
+			"AIF4 VI", "MCLK",
+			"RX_BIAS", "MCLK",
+			"MADINPUT", "MCLK",
+			"AMIC2", "MIC BIAS2",
+			"MIC BIAS2", "Headset Mic",
+			"DMIC0", "MIC BIAS1",
+			"MIC BIAS1", "Digital Mic0",
+			"DMIC1", "MIC BIAS1",
+			"MIC BIAS1", "Digital Mic1",
+			"DMIC2", "MIC BIAS3",
+			"MIC BIAS3", "Digital Mic2",
+			"DMIC3", "MIC BIAS3",
+			"MIC BIAS3", "Digital Mic3",
+			"SpkrLeft IN", "SPK1 OUT",
+			"SpkrRight IN", "SPK2 OUT";
+	};
+
+	sound-9335 {
+		status = "disabled";
+	};
+
+	i2c@78b8000 {
+		wcd9xxx_codec@d {
+		  status = "disabled";
+		};
+	};
+
+	vph_pwr_vreg: vph_pwr_vreg {
+		compatible = "regulator-fixed";
+		status = "ok";
+		regulator-name = "vph_pwr";
+		regulator-always-on;
+	};
+
+	mdss_mdp: qcom,mdss_mdp@1a00000 {
+		status = "disabled";
+	};
+
+	bluetooth: bt_qca9379 {
+		compatible = "qca,qca9379";
+		qca,bt-reset-gpio = <&msm_gpio 47 0>; /* BT_EN */
+	};
+};
+
+&sdhc_2 {
+	status = "disabled";
+};
+
+&usb_otg {
+	interrupts = <0 134 0>,<0 140 0>,<0 136 0>;
+	interrupt-names = "core_irq", "async_irq", "phy_irq";
+	qcom,hsusb-otg-mode = <3>;
+	qcom,switch-vbus-w-id;
+	vbus_otg-supply = <&vph_pwr_vreg>;
+	extcon = <&pm8916_chg>;
+};
+
+&external_image_mem {
+	reg = <0x0 0x87a00000 0x0 0x0600000>;
+};
+
+&modem_adsp_mem {
+	reg = <0x0 0x88000000 0x0 0x01e00000>;
+};
+
+&peripheral_mem {
+	reg = <0x0 0x89e00000 0x0 0x0700000>;
+};
+
+&pm8916_chg {
+	status = "ok";
+};
+
+&pm8916_bms {
+	status = "ok";
+};
+
+&blsp1_uart2_hs {
+	status = "ok";
+};
diff --git a/arch/arm64/boot/dts/qcom/apq8009w-bg-alpha.dts b/arch/arm64/boot/dts/qcom/apq8009w-bg-alpha.dts
index 89e1b76..1fe7b15 100644
--- a/arch/arm64/boot/dts/qcom/apq8009w-bg-alpha.dts
+++ b/arch/arm64/boot/dts/qcom/apq8009w-bg-alpha.dts
@@ -300,4 +300,5 @@
 &mdss_dsi0 {
 	qcom,dsi-pref-prim-pan = <&dsi_auo_390p_cmd>;
 	qcom,platform-bklight-en-gpio = <&msm_gpio 52 0>;
+	qcom,platform-enable-gpio = <&msm_gpio 59 0>;
 };
diff --git a/arch/arm64/boot/dts/qcom/apq8009w-bg-wtp-v2.dts b/arch/arm64/boot/dts/qcom/apq8009w-bg-wtp-v2.dts
index 9fb6626..8113670 100644
--- a/arch/arm64/boot/dts/qcom/apq8009w-bg-wtp-v2.dts
+++ b/arch/arm64/boot/dts/qcom/apq8009w-bg-wtp-v2.dts
@@ -317,4 +317,5 @@
 &mdss_dsi0 {
 	qcom,dsi-pref-prim-pan = <&dsi_auo_390p_cmd>;
 	qcom,platform-bklight-en-gpio = <&msm_gpio 52 0>;
+	qcom,platform-enable-gpio = <&msm_gpio 59 0>;
 };
diff --git a/arch/arm64/boot/dts/qcom/apq8053-lite-dragon-v2.0.dts b/arch/arm64/boot/dts/qcom/apq8053-lite-dragon-v2.0.dts
index d7836e5..55d8b7b 100644
--- a/arch/arm64/boot/dts/qcom/apq8053-lite-dragon-v2.0.dts
+++ b/arch/arm64/boot/dts/qcom/apq8053-lite-dragon-v2.0.dts
@@ -13,7 +13,6 @@
 
 /dts-v1/;
 
-#include "apq8053-lite.dtsi"
 #include "apq8053-lite-dragon-v2.0.dtsi"
 
 / {
diff --git a/arch/arm64/boot/dts/qcom/apq8053-lite-dragon-v2.0.dtsi b/arch/arm64/boot/dts/qcom/apq8053-lite-dragon-v2.0.dtsi
index 947af4d..bb40018 100644
--- a/arch/arm64/boot/dts/qcom/apq8053-lite-dragon-v2.0.dtsi
+++ b/arch/arm64/boot/dts/qcom/apq8053-lite-dragon-v2.0.dtsi
@@ -12,68 +12,25 @@
  */
 
 #include "apq8053-lite-dragon.dtsi"
-#include "msm8953-mdss-panels.dtsi"
-
-&i2c_3 {
-	status = "okay";
-	himax_ts@48 {
-		compatible = "himax,hxcommon";
-		reg = <0x48>;
-		interrupt-parent = <&tlmm>;
-		interrupts = <65 0x2>;
-		vdd-supply = <&pm8953_l10>;
-		avdd-supply = <&pm8953_l5>;
-		pinctrl-names = "pmx_ts_active","pmx_ts_suspend",
-					"pmx_ts_release";
-		pinctrl-0 = <&ts_int_active &ts_reset_active>;
-		pinctrl-1 = <&ts_int_suspend &ts_reset_suspend>;
-		pinctrl-2 = <&ts_release>;
-		himax,panel-coords = <0 800 0 1280>;
-		himax,display-coords = <0 800 0 1280>;
-		himax,irq-gpio = <&tlmm 65 0x2008>;
-		report_type = <1>;
-	};
-};
-
-&mdss_mdp {
-	qcom,mdss-pref-prim-intf = "dsi";
-};
-
-&mdss_dsi {
-	hw-config = "single_dsi";
-};
 
 &mdss_dsi0 {
-	qcom,dsi-pref-prim-pan = <&dsi_boyi_hx83100a_800p_video>;
-	pinctrl-names = "mdss_default", "mdss_sleep";
 	pinctrl-0 = <&mdss_dsi_active &mdss_te_active &mdss_dsi_gpio>;
 	pinctrl-1 = <&mdss_dsi_suspend &mdss_te_suspend &mdss_dsi_gpio>;
-
-	vdd-supply = <&pm8953_l10>;
-	vddio-supply = <&pm8953_l6>;
-	lab-supply = <&lab_regulator>;
-	ibb-supply = <&ibb_regulator>;
-
-	qcom,platform-te-gpio = <&tlmm 24 0>;
-	qcom,platform-reset-gpio = <&tlmm 61 0>;
-	qcom,platform-bklight-en-gpio = <&tlmm 100 0>;
-};
-
-&mdss_dsi1 {
-	status = "disabled";
-};
-
-&labibb {
-	status = "okay";
-	qpnp,qpnp-labibb-mode = "lcd";
-};
-
-&wled {
-	qcom,cons-sync-write-delay-us = <1000>;
-	qcom,led-strings-list = [00 01 02 03];
 };
 
 &pm8953_l4 {
 	status = "okay";
 	regulator-always-on;
 };
+
+&camera0 {
+	qcom,mount-angle = <90>;
+};
+
+&camera1 {
+	qcom,mount-angle = <90>;
+};
+
+&camera2{
+	qcom,mount-angle = <90>;
+};
diff --git a/arch/arm64/boot/dts/qcom/apq8053-lite-dragon.dtsi b/arch/arm64/boot/dts/qcom/apq8053-lite-dragon.dtsi
index 5e3ddce..e5b4c84 100644
--- a/arch/arm64/boot/dts/qcom/apq8053-lite-dragon.dtsi
+++ b/arch/arm64/boot/dts/qcom/apq8053-lite-dragon.dtsi
@@ -11,6 +11,7 @@
  * GNU General Public License for more details.
  */
 
+#include "apq8053-lite.dtsi"
 #include "msm8953-pinctrl.dtsi"
 #include "apq8053-camera-sensor-dragon.dtsi"
 #include "pmi8950.dtsi"
@@ -185,39 +186,22 @@
 
 &i2c_3 {
 	status = "okay";
-	focaltech@38 {
-		compatible = "focaltech,5x06";
-		reg = <0x38>;
+	himax_ts@48 {
+		compatible = "himax,hxcommon";
+		reg = <0x48>;
 		interrupt-parent = <&tlmm>;
 		interrupts = <65 0x2>;
 		vdd-supply = <&pm8953_l10>;
-		vcc_i2c-supply = <&pm8953_l5>;
-		/* pins used by touchscreen */
+		avdd-supply = <&pm8953_l5>;
 		pinctrl-names = "pmx_ts_active","pmx_ts_suspend",
-			"pmx_ts_release";
+					"pmx_ts_release";
 		pinctrl-0 = <&ts_int_active &ts_reset_active>;
 		pinctrl-1 = <&ts_int_suspend &ts_reset_suspend>;
 		pinctrl-2 = <&ts_release>;
-		focaltech,name = "ft5606";
-		focaltech,family-id = <0x08>;
-		focaltech,reset-gpio = <&tlmm 64 0x0>;
-		focaltech,irq-gpio = <&tlmm 65 0x2008>;
-		focaltech,display-coords = <0 0 1919 1199>;
-		focaltech,panel-coords = <0 0 1919 1199>;
-		focaltech,no-force-update;
-		focaltech,i2c-pull-up;
-		focaltech,group-id = <1>;
-		focaltech,hard-reset-delay-ms = <20>;
-		focaltech,soft-reset-delay-ms = <200>;
-		focaltech,num-max-touches = <5>;
-		focaltech,fw-delay-aa-ms = <30>;
-		focaltech,fw-delay-55-ms = <30>;
-		focaltech,fw-upgrade-id1 = <0x79>;
-		focaltech,fw-upgrade-id2 = <0x08>;
-		focaltech,fw-delay-readid-ms = <10>;
-		focaltech,fw-delay-era-flsh-ms = <2000>;
-		focaltech,fw-auto-cal;
-		focaltech,resume-in-workqueue;
+		himax,panel-coords = <0 800 0 1280>;
+		himax,display-coords = <0 800 0 1280>;
+		himax,irq-gpio = <&tlmm 65 0x2008>;
+		report_type = <1>;
 	};
 };
 
@@ -229,6 +213,71 @@
 	status = "disabled";
 };
 
+#include "msm8953-mdss-panels.dtsi"
+&mdss_mdp {
+	qcom,mdss-pref-prim-intf = "dsi";
+};
+
+&mdss_dsi {
+	hw-config = "single_dsi";
+};
+
+&mdss_dsi_active {
+	mux {
+		pins = "gpio61", "gpio100";
+		function = "gpio";
+	};
+
+	config {
+		pins = "gpio61", "gpio100";
+		drive-strength = <8>;
+		bias-disable = <0>;
+		output-high;
+	};
+};
+
+&mdss_dsi_suspend {
+	mux {
+		pins = "gpio61", "gpio100";
+		function = "gpio";
+	};
+
+	config {
+		pins = "gpio61", "gpio100";
+		drive-strength = <2>;
+		bias-pull-down;
+	};
+};
+
+&mdss_dsi0 {
+	qcom,dsi-pref-prim-pan = <&dsi_boyi_hx83100a_800p_video>;
+	pinctrl-names = "mdss_default", "mdss_sleep";
+	pinctrl-0 = <&mdss_dsi_active &mdss_te_active>;
+	pinctrl-1 = <&mdss_dsi_suspend &mdss_te_suspend>;
+
+	vdd-supply = <&pm8953_l10>;
+	vddio-supply = <&pm8953_l6>;
+	lab-supply = <&lab_regulator>;
+	ibb-supply = <&ibb_regulator>;
+
+	qcom,platform-te-gpio = <&tlmm 24 0>;
+	qcom,platform-reset-gpio = <&tlmm 61 0>;
+	qcom,platform-bklight-en-gpio = <&tlmm 100 0>;
+};
+
+&mdss_dsi1 {
+	status = "disabled";
+};
+
+&labibb {
+	status = "okay";
+	qpnp,qpnp-labibb-mode = "lcd";
+};
+
+&wled {
+	qcom,cons-sync-write-delay-us = <1000>;
+	qcom,led-strings-list = [00 01 02 03];
+};
 &blsp1_uart0 {
 	status = "ok";
 	pinctrl-names = "default";
@@ -301,7 +350,7 @@
 	pinctrl-1 = <&sdc2_clk_off &sdc2_cmd_off &sdc2_data_off
 		 &sdc2_wlan_gpio_off>;
 
-	qcom,clk-rates = <400000 20000000 25000000 50000000>;
+	qcom,clk-rates = <400000 20000000 25000000 50000000 100000000>;
 	qcom,bus-speed-mode = "SDR12", "SDR25", "SDR50", "DDR50", "SDR104";
 
 	status = "ok";
diff --git a/arch/arm64/boot/dts/qcom/apq8053-lite-harman-v1.0.dts b/arch/arm64/boot/dts/qcom/apq8053-lite-harman-v1.0.dts
new file mode 100644
index 0000000..203b6b8
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/apq8053-lite-harman-v1.0.dts
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2017-2018, 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.
+ */
+
+/dts-v1/;
+
+#include "apq8053-lite-harman-v1.0.dtsi"
+
+/ {
+	model = "Qualcomm Technologies, Inc. APQ8053 Lite Harman v1.0 Board";
+	compatible = "qcom,apq8053-lite-dragonboard", "qcom,apq8053",
+			"qcom,dragonboard";
+	qcom,board-id= <0x01020020 0>;
+};
+
+&blsp2_uart0 {
+	status = "okay";
+};
diff --git a/arch/arm64/boot/dts/qcom/apq8053-lite-harman-v1.0.dtsi b/arch/arm64/boot/dts/qcom/apq8053-lite-harman-v1.0.dtsi
new file mode 100644
index 0000000..5cf8ac0
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/apq8053-lite-harman-v1.0.dtsi
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2017-2018, 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 "apq8053-lite-dragon.dtsi"
+
+&pm8953_l4 {
+	status = "okay";
+	regulator-always-on;
+};
+
+&pm8953_l10 {
+	status = "okay";
+	regulator-min-microvolt = <3000000>;
+	regulator-max-microvolt = <3300000>;
+	regulator-always-on;
+};
+
+&pm8953_l2 {
+	status = "okay";
+	regulator-always-on;
+};
+
+&pm8953_l17 {
+	status = "okay";
+	regulator-always-on;
+};
+
+&pm8953_l22 {
+	status = "okay";
+	regulator-always-on;
+};
+
+&i2c_3 {
+	status = "okay";
+	/delete-node/ himax_ts@48;
+	focaltech_ts@38 {
+		compatible = "focaltech,fts";
+		reg = <0x38>;
+		interrupt-parent = <&tlmm>;
+		interrupts = <65 0x2>;
+		vdd-supply = <&pm8953_l10>;
+		avdd-supply = <&pm8953_l6>;
+		pinctrl-names = "pmx_ts_active","pmx_ts_suspend",
+					"pmx_ts_release";
+		pinctrl-0 = <&ts_int_active &ts_reset_active>;
+		pinctrl-1 = <&ts_int_suspend &ts_reset_suspend>;
+		pinctrl-2 = <&ts_release>;
+		focaltech,display-coords = <0 0 800 1280>;
+		focaltech,reset-gpio = <&tlmm 64 0x0>;
+		focaltech,irq-gpio = <&tlmm 65 0x2>;
+		focaltech,max-touch-number = <5>;
+		report_type = <1>;
+	};
+};
+
+&wled {
+	qcom,led-strings-list = [00 01 02];
+};
+
+&camera0 {
+	qcom,mount-angle = <90>;
+};
+
+&camera1 {
+	qcom,mount-angle = <90>;
+};
+
+&camera2{
+	qcom,mount-angle = <90>;
+};
diff --git a/arch/arm64/boot/dts/qcom/apq8053-lite-lenovo-v1.0.dts b/arch/arm64/boot/dts/qcom/apq8053-lite-lenovo-v1.0.dts
new file mode 100644
index 0000000..325accf
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/apq8053-lite-lenovo-v1.0.dts
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2017-2018, 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.
+ */
+
+/dts-v1/;
+
+#include "apq8053-lite-lenovo-v1.0.dtsi"
+
+/ {
+	model = "Qualcomm Technologies, Inc. APQ8053 Lite Lenovo v1.0 Board";
+	compatible = "qcom,apq8053-lite-dragonboard", "qcom,apq8053",
+			"qcom,dragonboard";
+	qcom,board-id= <0x01010020 0>;
+};
+
+&blsp2_uart0 {
+	status = "okay";
+};
diff --git a/arch/arm64/boot/dts/qcom/apq8053-lite-lenovo-v1.0.dtsi b/arch/arm64/boot/dts/qcom/apq8053-lite-lenovo-v1.0.dtsi
new file mode 100644
index 0000000..4d9c40c
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/apq8053-lite-lenovo-v1.0.dtsi
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2017-2018, 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 "apq8053-lite-dragon.dtsi"
+
+&mdss_dsi0 {
+	qcom,ext_vdd-gpio = <&tlmm 100 0>;
+	qcom,platform-bklight-en-gpio = <&tlmm 95 0>;
+
+	qcom,platform-lane-config = [00 00 ff 0f
+				00 00 ff 0f
+				00 00 ff 0f
+				00 00 ff 0f
+				00 00 ff 8f];
+};
+
+&eeprom0 {
+	gpios = <&tlmm 26 0>,
+		<&tlmm 40 0>,
+		<&tlmm 118 0>,
+		<&tlmm 119 0>,
+		<&tlmm 39 0>;
+	qcom,gpio-vdig = <3>;
+	qcom,gpio-vana = <4>;
+	qcom,gpio-req-tbl-num = <0 1 2 3 4>;
+	qcom,gpio-req-tbl-flags = <1 0 0 0 0>;
+	qcom,gpio-req-tbl-label = "CAMIF_MCLK0",
+			"CAM_RESET0",
+			"CAM_VDIG",
+			"CAM_VANA",
+			"CAM_STANDBY0";
+};
+
+&camera0 {
+	qcom,mount-angle = <270>;
+	gpios = <&tlmm 26 0>,
+		<&tlmm 40 0>,
+		<&tlmm 39 0>,
+		<&tlmm 118 0>,
+		<&tlmm 119 0>;
+	qcom,gpio-vdig = <3>;
+	qcom,gpio-vana = <4>;
+	qcom,gpio-req-tbl-num = <0 1 2 3 4>;
+	qcom,gpio-req-tbl-flags = <1 0 0 0 0>;
+	qcom,gpio-req-tbl-label = "CAMIF_MCLK0",
+			"CAM_RESET0",
+			"CAM_STANDBY0",
+			"CAM_VDIG",
+			"CAM_VANA";
+};
+
+&camera1 {
+	qcom,mount-angle = <270>;
+};
+
+&camera2{
+	qcom,mount-angle = <270>;
+};
diff --git a/arch/arm64/boot/dts/qcom/apq8053-lite-lenovo-v1.1.dts b/arch/arm64/boot/dts/qcom/apq8053-lite-lenovo-v1.1.dts
new file mode 100644
index 0000000..0c7b557
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/apq8053-lite-lenovo-v1.1.dts
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2017-2018, 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.
+ */
+
+/dts-v1/;
+
+#include "apq8053-lite-lenovo-v1.1.dtsi"
+
+/ {
+	model = "Qualcomm Technologies, Inc. APQ8053 Lite Lenovo v1.1 Board";
+	compatible = "qcom,apq8053-lite-dragonboard", "qcom,apq8053",
+			"qcom,dragonboard";
+	qcom,board-id= <0x01010120 0>;
+};
+
+&blsp2_uart0 {
+	status = "okay";
+};
diff --git a/arch/arm64/boot/dts/qcom/apq8053-lite-lenovo-v1.1.dtsi b/arch/arm64/boot/dts/qcom/apq8053-lite-lenovo-v1.1.dtsi
new file mode 100644
index 0000000..396fd55
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/apq8053-lite-lenovo-v1.1.dtsi
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2017-2018, 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 "apq8053-lite-dragon.dtsi"
+
+&i2c_3 {
+	status = "okay";
+	/delete-node/ himax_ts@48;
+};
+
+&eeprom0 {
+	gpios = <&tlmm 26 0>,
+		<&tlmm 40 0>,
+		<&tlmm 118 0>,
+		<&tlmm 119 0>,
+		<&tlmm 39 0>;
+	qcom,gpio-vdig = <3>;
+	qcom,gpio-vana = <4>;
+	qcom,gpio-req-tbl-num = <0 1 2 3 4>;
+	qcom,gpio-req-tbl-flags = <1 0 0 0 0>;
+	qcom,gpio-req-tbl-label = "CAMIF_MCLK0",
+			"CAM_RESET0",
+			"CAM_VDIG",
+			"CAM_VANA",
+			"CAM_STANDBY0";
+};
+
+&camera0 {
+	qcom,mount-angle = <270>;
+	gpios = <&tlmm 26 0>,
+		<&tlmm 40 0>,
+		<&tlmm 39 0>,
+		<&tlmm 118 0>,
+		<&tlmm 119 0>;
+	qcom,gpio-vdig = <3>;
+	qcom,gpio-vana = <4>;
+	qcom,gpio-req-tbl-num = <0 1 2 3 4>;
+	qcom,gpio-req-tbl-flags = <1 0 0 0 0>;
+	qcom,gpio-req-tbl-label = "CAMIF_MCLK0",
+			"CAM_RESET0",
+			"CAM_STANDBY0",
+			"CAM_VDIG",
+			"CAM_VANA";
+};
+
+&camera1 {
+	qcom,mount-angle = <270>;
+};
+
+&camera2{
+	qcom,mount-angle = <270>;
+};
diff --git a/arch/arm64/boot/dts/qcom/apq8053-lite-lge-v1.0.dts b/arch/arm64/boot/dts/qcom/apq8053-lite-lge-v1.0.dts
new file mode 100644
index 0000000..70952dc
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/apq8053-lite-lge-v1.0.dts
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2017-2018, 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.
+ */
+
+/dts-v1/;
+
+#include "apq8053-lite-lge-v1.0.dtsi"
+
+/ {
+	model = "Qualcomm Technologies, Inc. APQ8053 Lite LGE v1.0 Board";
+	compatible = "qcom,apq8053-lite-dragonboard", "qcom,apq8053",
+			"qcom,dragonboard";
+	qcom,board-id= <0x01030020 0>;
+};
+
+&blsp2_uart0 {
+	status = "okay";
+};
diff --git a/arch/arm64/boot/dts/qcom/apq8053-lite-lge-v1.0.dtsi b/arch/arm64/boot/dts/qcom/apq8053-lite-lge-v1.0.dtsi
new file mode 100644
index 0000000..db0331e
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/apq8053-lite-lge-v1.0.dtsi
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2017-2018, 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 "apq8053-lite-dragon.dtsi"
+
+&wled {
+	qcom,fs-curr-ua = <17500>;
+};
+
+&camera0 {
+	qcom,mount-angle = <180>;
+};
+
+&camera1 {
+	qcom,mount-angle = <180>;
+};
+
+&camera2 {
+	qcom,mount-angle = <180>;
+};
diff --git a/arch/arm64/boot/dts/qcom/msm8909w-bg-wtp-v2.dts b/arch/arm64/boot/dts/qcom/msm8909w-bg-wtp-v2.dts
index 8149eb2..9dd80f0 100644
--- a/arch/arm64/boot/dts/qcom/msm8909w-bg-wtp-v2.dts
+++ b/arch/arm64/boot/dts/qcom/msm8909w-bg-wtp-v2.dts
@@ -320,4 +320,5 @@
 &mdss_dsi0 {
 	qcom,dsi-pref-prim-pan = <&dsi_auo_390p_cmd>;
 	qcom,platform-bklight-en-gpio = <&msm_gpio 52 0>;
+	qcom,platform-enable-gpio = <&msm_gpio 59 0>;
 };
diff --git a/arch/arm64/boot/dts/qcom/msm8917-qrd.dtsi b/arch/arm64/boot/dts/qcom/msm8917-qrd.dtsi
index f897b59..fae258d0 100644
--- a/arch/arm64/boot/dts/qcom/msm8917-qrd.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8917-qrd.dtsi
@@ -78,6 +78,12 @@
 };
 
 &soc {
+	int_codec: sound {
+		status = "okay";
+		qcom,msm-mbhc-hphl-swh = <1>;
+		qcom,msm-hs-micbias-type = "internal";
+	};
+
 	gpio_keys {
 		compatible = "gpio-keys";
 		input-name = "gpio-keys";
diff --git a/arch/arm64/boot/dts/qcom/msm8953-iot-mtp.dts b/arch/arm64/boot/dts/qcom/msm8953-iot-mtp.dts
index 39c76cc..c3d4cc5 100644
--- a/arch/arm64/boot/dts/qcom/msm8953-iot-mtp.dts
+++ b/arch/arm64/boot/dts/qcom/msm8953-iot-mtp.dts
@@ -25,3 +25,18 @@
 	qcom,pmic-id = <0x010016 0x010011 0x0 0x0>;
 };
 
+&kgsl_smmu {
+	qcom,hibernation-support;
+	qcom,static-ns-cbs = <0>;
+	/delete-property/ qcom,skip-init;
+};
+
+&apps_iommu {
+	qcom,hibernation-support;
+	qcom,static-ns-cbs =
+			<15 16 17 18 19>,
+			<20 21 22 23 24 25 26 27 28 29 30>,
+			<31>;
+
+	/delete-property/ qcom,skip-init;
+};
diff --git a/arch/arm64/boot/dts/qcom/pmi632.dtsi b/arch/arm64/boot/dts/qcom/pmi632.dtsi
index 80480f1..8a640a5 100644
--- a/arch/arm64/boot/dts/qcom/pmi632.dtsi
+++ b/arch/arm64/boot/dts/qcom/pmi632.dtsi
@@ -44,8 +44,6 @@
 			interrupt-names = "eoc-int-en-set";
 			qcom,adc-vdd-reference = <1875>;
 			qcom,adc-full-scale-code = <0x70e4>;
-			pinctrl-names = "default";
-			pinctrl-0 = <&quiet_therm_default &smb_therm_default>;
 
 			chan@0 {
 				label = "ref_gnd";
@@ -305,21 +303,6 @@
 			gpio-controller;
 			#gpio-cells = <2>;
 			qcom,gpios-disallowed = <1>;
-
-			quiet_therm {
-				quiet_therm_default: quiet_therm_default {
-					pins = "gpio3";
-					bias-high-impedance;
-				};
-			};
-
-			smb_therm {
-				smb_therm_default: smb_therm_default {
-					pins = "gpio4";
-					bias-high-impedance;
-				};
-			};
-
 		};
 
 		pmi632_charger: qcom,qpnp-smb5 {
@@ -331,6 +314,7 @@
 			qcom,pmic-revid = <&pmi632_revid>;
 			dpdm-supply = <&qusb_phy>;
 			qcom,auto-recharge-soc = <98>;
+			qcom,chg-vadc = <&pmi632_vadc>;
 
 			qcom,thermal-mitigation
 				= <3000000 2500000 2000000 1500000
diff --git a/arch/arm64/boot/dts/qcom/qcs605.dtsi b/arch/arm64/boot/dts/qcom/qcs605.dtsi
index be88a5d..747593f 100644
--- a/arch/arm64/boot/dts/qcom/qcs605.dtsi
+++ b/arch/arm64/boot/dts/qcom/qcs605.dtsi
@@ -19,39 +19,39 @@
 };
 
 &pil_modem_mem {
-	reg = <0 0x8b000000 0 0x3e00000>;
+	reg = <0 0x8b000000 0 0x3100000>;
 };
 
 &pil_video_mem {
-	reg = <0 0x8ee00000 0 0x500000>;
+	reg = <0 0x8e100000 0 0x500000>;
 };
 
 &wlan_msa_mem {
-	reg = <0 0x8f300000 0 0x100000>;
+	reg = <0 0x8e600000 0 0x100000>;
 };
 
 &pil_cdsp_mem {
-	reg = <0 0x8f400000 0 0x800000>;
+	reg = <0 0x8e700000 0 0x800000>;
 };
 
 &pil_mba_mem {
-	reg = <0 0x8fc00000 0 0x200000>;
+	reg = <0 0x8ef00000 0 0x200000>;
 };
 
 &pil_adsp_mem {
-	reg = <0 0x8fe00000 0 0x1e00000>;
+	reg = <0 0x8f100000 0 0x1e00000>;
 };
 
 &pil_ipa_fw_mem {
-	reg = <0 0x91c00000 0 0x10000>;
+	reg = <0 0x90f00000 0 0x10000>;
 };
 
 &pil_ipa_gsi_mem {
-	reg = <0 0x91c10000 0 0x5000>;
+	reg = <0 0x90f10000 0 0x5000>;
 };
 
 &pil_gpu_mem {
-	reg = <0 0x91c15000 0 0x2000>;
+	reg = <0 0x90f15000 0 0x2000>;
 };
 
 &adsp_mem {
diff --git a/arch/arm64/boot/dts/qcom/sdm429-qrd.dtsi b/arch/arm64/boot/dts/qcom/sdm429-qrd.dtsi
index 7116662..dde9c56 100644
--- a/arch/arm64/boot/dts/qcom/sdm429-qrd.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm429-qrd.dtsi
@@ -12,3 +12,7 @@
  */
 
 #include "sdm439-qrd.dtsi"
+
+&mdss_dsi0 {
+	qcom,dsi-pref-prim-pan = <&dsi_hx8399c_hd_vid>;
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm429.dtsi b/arch/arm64/boot/dts/qcom/sdm429.dtsi
index 24e9905..19df054 100644
--- a/arch/arm64/boot/dts/qcom/sdm429.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm429.dtsi
@@ -207,3 +207,9 @@
 		"byte1_src";
 	#clock-cells = <1>;
 };
+
+/* GPU overrides */
+&msm_gpu {
+	/* Update GPU chip ID*/
+	qcom,chipid = <0x05000400>;
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm439-audio.dtsi b/arch/arm64/boot/dts/qcom/sdm439-audio.dtsi
index f6751d2..dba56a4 100644
--- a/arch/arm64/boot/dts/qcom/sdm439-audio.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm439-audio.dtsi
@@ -15,6 +15,7 @@
 	int_codec: sound {
 		qcom,model = "sdm439-snd-card-mtp";
 		qcom,msm-hs-micbias-type = "internal";
+		qcom,msm-micbias2-ext-cap;
 
 		asoc-codec = <&stub_codec>, <&msm_digital_codec>,
 				<&pmic_analog_codec>;
diff --git a/arch/arm64/boot/dts/qcom/sdm439-cdp.dtsi b/arch/arm64/boot/dts/qcom/sdm439-cdp.dtsi
index 08745cd..fd66f4b 100644
--- a/arch/arm64/boot/dts/qcom/sdm439-cdp.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm439-cdp.dtsi
@@ -150,6 +150,30 @@
 	};
 };
 
+&cdc_pdm_lines_2_act {
+	mux {
+		pins = "gpio70", "gpio71", "gpio72";
+		function = "cdc_pdm0";
+	};
+
+	config {
+		pins = "gpio70", "gpio71", "gpio72";
+		drive-strength = <16>;
+	};
+};
+
+&cdc_pdm_lines_act {
+	mux {
+		pins = "gpio69", "gpio73", "gpio74";
+		function = "cdc_pdm0";
+	};
+
+	config {
+		pins = "gpio69", "gpio73", "gpio74";
+		drive-strength = <16>;
+	};
+};
+
 &pm8953_pwm {
 	status = "ok";
 };
diff --git a/arch/arm64/boot/dts/qcom/sdm439-qrd.dtsi b/arch/arm64/boot/dts/qcom/sdm439-qrd.dtsi
index 18714ce..bfe7bfa 100644
--- a/arch/arm64/boot/dts/qcom/sdm439-qrd.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm439-qrd.dtsi
@@ -297,3 +297,13 @@
 	qcom,mdss-dsi-pan-fps-update =
 		"dfps_immediate_porch_mode_vfp";
 };
+
+&dsi_hx8399c_hd_vid {
+	/delete-property/ qcom,mdss-dsi-panel-timings;
+	qcom,mdss-dsi-panel-timings-phy-12nm = [08 06 0a 02 00 04 02 08];
+	qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+	qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_pwm";
+	qcom,mdss-dsi-bl-pmic-pwm-frequency = <100>;
+	qcom,mdss-dsi-bl-pmic-bank-select = <0>;
+	qcom,mdss-dsi-pwm-gpio = <&pm8953_gpios 8 0>;
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm439-regulator.dtsi b/arch/arm64/boot/dts/qcom/sdm439-regulator.dtsi
index f325925..a8b8291 100644
--- a/arch/arm64/boot/dts/qcom/sdm439-regulator.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm439-regulator.dtsi
@@ -468,5 +468,19 @@
 		qcom,cpr-quot-adjust-scaling-factor-max = <0 1400 1400>;
 		qcom,cpr-voltage-scaling-factor-max = <0 2000 2000>;
 		qcom,cpr-scaled-init-voltage-as-ceiling;
+
+		qcom,cpr-fuse-version-map =
+			/* <Speed-bin pvs-version cpr-rev ... ... ...> */
+			<(-1)    (-1)   ( 0)   (-1)    (-1)    (-1)>,
+			<(-1)    (-1)   (-1)   (-1)    (-1)    (-1)>;
+
+		qcom,cpr-quotient-adjustment =
+			<66    77      66>, /* SVSP_30mV, NOM_35mV, TUR_30mV */
+			<0      0       0>;
+
+		qcom,cpr-voltage-ceiling-override =
+			<(-1) (-1) 795000 795000 835000 910000 910000>;
+
+		qcom,cpr-enable;
 	};
 };
diff --git a/arch/arm64/boot/dts/qcom/sdm450-pmi632.dtsi b/arch/arm64/boot/dts/qcom/sdm450-pmi632.dtsi
index 5c127bc..e09d637 100644
--- a/arch/arm64/boot/dts/qcom/sdm450-pmi632.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm450-pmi632.dtsi
@@ -41,6 +41,27 @@
 	};
 };
 
+&pmi632_vadc {
+	pinctrl-names = "default";
+	pinctrl-0 = <&quiet_therm_default &smb_therm_default>;
+};
+
+&pmi632_gpios {
+	quiet_therm {
+		quiet_therm_default: quiet_therm_default {
+			pins = "gpio3";
+			bias-high-impedance;
+		};
+	};
+
+	smb_therm {
+		smb_therm_default: smb_therm_default {
+			pins = "gpio4";
+			bias-high-impedance;
+		};
+	};
+};
+
 &pmi632_qg {
 	qcom,battery-data = <&mtp_batterydata>;
 };
diff --git a/arch/arm64/boot/dts/qcom/sdm670-cdp.dtsi b/arch/arm64/boot/dts/qcom/sdm670-cdp.dtsi
index bd88087..da4d27d 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-cdp.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670-cdp.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2017-2018, 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
@@ -143,7 +143,7 @@
 
 		cam_snapshot {
 			label = "cam_snapshot";
-			gpios = <&tlmm 91 0>;
+			gpios = <&tlmm 91 GPIO_ACTIVE_LOW>;
 			linux,input-type = <1>;
 			linux,code = <766>;
 			gpio-key,wakeup;
@@ -153,7 +153,7 @@
 
 		cam_focus {
 			label = "cam_focus";
-			gpios = <&tlmm 92 0>;
+			gpios = <&tlmm 92 GPIO_ACTIVE_HIGH>;
 			linux,input-type = <1>;
 			linux,code = <528>;
 			gpio-key,wakeup;
diff --git a/arch/arm64/boot/dts/qcom/sdm670-mtp.dtsi b/arch/arm64/boot/dts/qcom/sdm670-mtp.dtsi
index cc55127..7764837 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-mtp.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670-mtp.dtsi
@@ -202,7 +202,7 @@
 
 		cam_snapshot {
 			label = "cam_snapshot";
-			gpios = <&tlmm 91 0>;
+			gpios = <&tlmm 91 GPIO_ACTIVE_LOW>;
 			linux,input-type = <1>;
 			linux,code = <766>;
 			gpio-key,wakeup;
@@ -212,7 +212,7 @@
 
 		cam_focus {
 			label = "cam_focus";
-			gpios = <&tlmm 92 0>;
+			gpios = <&tlmm 92 GPIO_ACTIVE_HIGH>;
 			linux,input-type = <1>;
 			linux,code = <528>;
 			gpio-key,wakeup;
diff --git a/arch/arm64/configs/msm8937-perf_defconfig b/arch/arm64/configs/msm8937-perf_defconfig
index 4c6b6a1..72a8fb4 100644
--- a/arch/arm64/configs/msm8937-perf_defconfig
+++ b/arch/arm64/configs/msm8937-perf_defconfig
@@ -68,7 +68,6 @@
 CONFIG_ZSMALLOC=y
 CONFIG_PROCESS_RECLAIM=y
 CONFIG_SECCOMP=y
-CONFIG_HARDEN_BRANCH_PREDICTOR=y
 CONFIG_ARMV8_DEPRECATED=y
 CONFIG_SWP_EMULATION=y
 CONFIG_CP15_BARRIER_EMULATION=y
@@ -148,6 +147,7 @@
 CONFIG_NETFILTER_XT_TARGET_TRACE=y
 CONFIG_NETFILTER_XT_TARGET_SECMARK=y
 CONFIG_NETFILTER_XT_TARGET_TCPMSS=y
+CONFIG_NETFILTER_XT_MATCH_BPF=y
 CONFIG_NETFILTER_XT_MATCH_COMMENT=y
 CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y
 CONFIG_NETFILTER_XT_MATCH_CONNMARK=y
diff --git a/arch/arm64/configs/msm8937_defconfig b/arch/arm64/configs/msm8937_defconfig
index 11bd200..fc7dc6c 100644
--- a/arch/arm64/configs/msm8937_defconfig
+++ b/arch/arm64/configs/msm8937_defconfig
@@ -71,7 +71,6 @@
 CONFIG_ZSMALLOC=y
 CONFIG_PROCESS_RECLAIM=y
 CONFIG_SECCOMP=y
-CONFIG_HARDEN_BRANCH_PREDICTOR=y
 CONFIG_ARMV8_DEPRECATED=y
 CONFIG_SWP_EMULATION=y
 CONFIG_CP15_BARRIER_EMULATION=y
@@ -152,6 +151,7 @@
 CONFIG_NETFILTER_XT_TARGET_TRACE=y
 CONFIG_NETFILTER_XT_TARGET_SECMARK=y
 CONFIG_NETFILTER_XT_TARGET_TCPMSS=y
+CONFIG_NETFILTER_XT_MATCH_BPF=y
 CONFIG_NETFILTER_XT_MATCH_COMMENT=y
 CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y
 CONFIG_NETFILTER_XT_MATCH_CONNMARK=y
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_display.c b/drivers/gpu/drm/msm/dsi-staging/dsi_display.c
index adcec15..5c34b8e 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_display.c
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_display.c
@@ -559,9 +559,6 @@
 	if (dsi_ctrl_validate_host_state(ctrl->ctrl))
 		return 1;
 
-	/* acquire panel_lock to make sure no commands are in progress */
-	dsi_panel_acquire_panel_lock(panel);
-
 	config = &(panel->esd_config);
 	lenp = config->status_valid_params ?: config->status_cmds_rlen;
 	count = config->status_cmd.count;
@@ -580,7 +577,7 @@
 		rc = dsi_ctrl_cmd_transfer(ctrl->ctrl, &cmds[i].msg, flags);
 		if (rc <= 0) {
 			pr_err("rx cmd transfer failed rc=%d\n", rc);
-			goto error;
+			return rc;
 		}
 
 		memcpy(config->return_buf + start,
@@ -588,9 +585,6 @@
 		start += lenp[i];
 	}
 
-error:
-	/* release panel_lock */
-	dsi_panel_release_panel_lock(panel);
 	return rc;
 }
 
@@ -709,15 +703,16 @@
 	u32 status_mode;
 	int rc = 0x1;
 
-	if (dsi_display == NULL)
+	if (!dsi_display || !dsi_display->panel)
 		return -EINVAL;
 
-	mutex_lock(&dsi_display->display_lock);
-
 	panel = dsi_display->panel;
+
+	dsi_panel_acquire_panel_lock(panel);
+
 	if (!panel->panel_initialized) {
 		pr_debug("Panel not initialized\n");
-		mutex_unlock(&dsi_display->display_lock);
+		dsi_panel_release_panel_lock(panel);
 		return rc;
 	}
 
@@ -742,7 +737,7 @@
 
 	dsi_display_clk_ctrl(dsi_display->dsi_clk_handle,
 		DSI_ALL_CLKS, DSI_CLK_OFF);
-	mutex_unlock(&dsi_display->display_lock);
+	dsi_panel_release_panel_lock(panel);
 
 	return rc;
 }
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_drm.c b/drivers/gpu/drm/msm/dsi-staging/dsi_drm.c
index 5b47865..6b5bfb4 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_drm.c
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_drm.c
@@ -186,6 +186,7 @@
 {
 	int rc = 0;
 	struct dsi_bridge *c_bridge = to_dsi_bridge(bridge);
+	struct dsi_display *display;
 
 	if (!bridge) {
 		pr_err("Invalid params\n");
@@ -197,11 +198,15 @@
 		pr_debug("[%d] seamless enable\n", c_bridge->id);
 		return;
 	}
+	display = c_bridge->display;
 
-	rc = dsi_display_post_enable(c_bridge->display);
+	rc = dsi_display_post_enable(display);
 	if (rc)
 		pr_err("[%d] DSI display post enabled failed, rc=%d\n",
 		       c_bridge->id, rc);
+
+	if (display && display->drm_conn)
+		sde_connector_helper_bridge_enable(display->drm_conn);
 }
 
 static void dsi_bridge_disable(struct drm_bridge *bridge)
diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c
index 19df8be..0697db8 100644
--- a/drivers/gpu/drm/msm/msm_drv.c
+++ b/drivers/gpu/drm/msm/msm_drv.c
@@ -947,6 +947,14 @@
 	struct msm_kms *kms = priv->kms;
 	int i;
 
+	/* check for splash status before triggering cleanup
+	 * if we end up here with splash status ON i.e before first
+	 * commit then ignore the last close call
+	 */
+	if (kms && kms->funcs && kms->funcs->check_for_splash
+		&& kms->funcs->check_for_splash(kms))
+		return;
+
 	/*
 	 * clean up vblank disable immediately as this is the last close.
 	 */
diff --git a/drivers/gpu/drm/msm/msm_kms.h b/drivers/gpu/drm/msm/msm_kms.h
index db9e7ee..e99ff9c 100644
--- a/drivers/gpu/drm/msm/msm_kms.h
+++ b/drivers/gpu/drm/msm/msm_kms.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
  * Copyright (C) 2013 Red Hat
  * Author: Rob Clark <robdclark@gmail.com>
  *
@@ -106,6 +106,8 @@
 			unsigned int domain);
 	/* handle continuous splash  */
 	int (*cont_splash_config)(struct msm_kms *kms);
+	/* check for continuous splash status */
+	bool (*check_for_splash)(struct msm_kms *kms);
 };
 
 struct msm_kms {
diff --git a/drivers/gpu/drm/msm/sde/sde_ad4.h b/drivers/gpu/drm/msm/sde/sde_ad4.h
index bf08360..b254d7d 100644
--- a/drivers/gpu/drm/msm/sde/sde_ad4.h
+++ b/drivers/gpu/drm/msm/sde/sde_ad4.h
@@ -52,6 +52,7 @@
 	AD_IPC_SUSPEND,
 	AD_IPC_RESUME,
 	AD_IPC_RESET,
+	AD_VSYNC_UPDATE,
 	AD_PROPMAX,
 };
 
diff --git a/drivers/gpu/drm/msm/sde/sde_color_processing.c b/drivers/gpu/drm/msm/sde/sde_color_processing.c
index 0f55b19..47ff024 100644
--- a/drivers/gpu/drm/msm/sde/sde_color_processing.c
+++ b/drivers/gpu/drm/msm/sde/sde_color_processing.c
@@ -88,6 +88,7 @@
 		enum ad_property ad_prop);
 
 static void sde_cp_notify_hist_event(struct drm_crtc *crtc_drm, void *arg);
+static void sde_cp_update_ad_vsync_prop(struct sde_crtc *sde_crtc, u32 val);
 
 #define setup_dspp_prop_install_funcs(func) \
 do { \
@@ -138,6 +139,7 @@
 	SDE_CP_CRTC_DSPP_AD_ASSERTIVENESS,
 	SDE_CP_CRTC_DSPP_AD_BACKLIGHT,
 	SDE_CP_CRTC_DSPP_AD_STRENGTH,
+	SDE_CP_CRTC_DSPP_AD_VSYNC_COUNT,
 	SDE_CP_CRTC_DSPP_MAX,
 	/* DSPP features end */
 
@@ -407,6 +409,7 @@
 	if (IS_ERR(sde_crtc->hist_blob))
 		sde_crtc->hist_blob = NULL;
 
+	sde_crtc->ad_vsync_count = 0;
 	mutex_init(&sde_crtc->crtc_cp_lock);
 	INIT_LIST_HEAD(&sde_crtc->active_list);
 	INIT_LIST_HEAD(&sde_crtc->dirty_list);
@@ -789,6 +792,9 @@
 			ad_cfg.prop = AD_MODE;
 			ad_cfg.hw_cfg = &hw_cfg;
 			hw_dspp->ops.setup_ad(hw_dspp, &ad_cfg);
+			sde_crtc->ad_vsync_count = 0;
+			sde_cp_update_ad_vsync_prop(sde_crtc,
+					sde_crtc->ad_vsync_count);
 			break;
 		case SDE_CP_CRTC_DSPP_AD_INIT:
 			if (!hw_dspp || !hw_dspp->ops.setup_ad) {
@@ -798,6 +804,9 @@
 			ad_cfg.prop = AD_INIT;
 			ad_cfg.hw_cfg = &hw_cfg;
 			hw_dspp->ops.setup_ad(hw_dspp, &ad_cfg);
+			sde_crtc->ad_vsync_count = 0;
+			sde_cp_update_ad_vsync_prop(sde_crtc,
+					sde_crtc->ad_vsync_count);
 			break;
 		case SDE_CP_CRTC_DSPP_AD_CFG:
 			if (!hw_dspp || !hw_dspp->ops.setup_ad) {
@@ -807,6 +816,9 @@
 			ad_cfg.prop = AD_CFG;
 			ad_cfg.hw_cfg = &hw_cfg;
 			hw_dspp->ops.setup_ad(hw_dspp, &ad_cfg);
+			sde_crtc->ad_vsync_count = 0;
+			sde_cp_update_ad_vsync_prop(sde_crtc,
+					sde_crtc->ad_vsync_count);
 			break;
 		case SDE_CP_CRTC_DSPP_AD_INPUT:
 			if (!hw_dspp || !hw_dspp->ops.setup_ad) {
@@ -816,6 +828,9 @@
 			ad_cfg.prop = AD_INPUT;
 			ad_cfg.hw_cfg = &hw_cfg;
 			hw_dspp->ops.setup_ad(hw_dspp, &ad_cfg);
+			sde_crtc->ad_vsync_count = 0;
+			sde_cp_update_ad_vsync_prop(sde_crtc,
+					sde_crtc->ad_vsync_count);
 			break;
 		case SDE_CP_CRTC_DSPP_AD_ASSERTIVENESS:
 			if (!hw_dspp || !hw_dspp->ops.setup_ad) {
@@ -825,6 +840,9 @@
 			ad_cfg.prop = AD_ASSERTIVE;
 			ad_cfg.hw_cfg = &hw_cfg;
 			hw_dspp->ops.setup_ad(hw_dspp, &ad_cfg);
+			sde_crtc->ad_vsync_count = 0;
+			sde_cp_update_ad_vsync_prop(sde_crtc,
+					sde_crtc->ad_vsync_count);
 			break;
 		case SDE_CP_CRTC_DSPP_AD_BACKLIGHT:
 			if (!hw_dspp || !hw_dspp->ops.setup_ad) {
@@ -834,6 +852,9 @@
 			ad_cfg.prop = AD_BACKLIGHT;
 			ad_cfg.hw_cfg = &hw_cfg;
 			hw_dspp->ops.setup_ad(hw_dspp, &ad_cfg);
+			sde_crtc->ad_vsync_count = 0;
+			sde_cp_update_ad_vsync_prop(sde_crtc,
+					sde_crtc->ad_vsync_count);
 			break;
 		case SDE_CP_CRTC_DSPP_AD_STRENGTH:
 			if (!hw_dspp || !hw_dspp->ops.setup_ad) {
@@ -843,6 +864,9 @@
 			ad_cfg.prop = AD_STRENGTH;
 			ad_cfg.hw_cfg = &hw_cfg;
 			hw_dspp->ops.setup_ad(hw_dspp, &ad_cfg);
+			sde_crtc->ad_vsync_count = 0;
+			sde_cp_update_ad_vsync_prop(sde_crtc,
+					sde_crtc->ad_vsync_count);
 			break;
 		default:
 			ret = -EINVAL;
@@ -924,10 +948,15 @@
 			DRM_DEBUG_DRIVER("Dirty list is empty\n");
 			goto exit;
 		}
-		sde_cp_ad_set_prop(sde_crtc, AD_IPC_RESET);
 		set_dspp_flush = true;
 	}
 
+	if (!list_empty(&sde_crtc->ad_active)) {
+		sde_cp_ad_set_prop(sde_crtc, AD_IPC_RESET);
+		sde_cp_ad_set_prop(sde_crtc, AD_VSYNC_UPDATE);
+		sde_cp_update_ad_vsync_prop(sde_crtc, sde_crtc->ad_vsync_count);
+	}
+
 	list_for_each_entry_safe(prop_node, n, &sde_crtc->dirty_list,
 				dirty_list) {
 		sde_dspp_feature = crtc_feature_map[prop_node->feature];
@@ -1449,6 +1478,9 @@
 				"SDE_DSPP_AD_V4_BACKLIGHT",
 			SDE_CP_CRTC_DSPP_AD_BACKLIGHT, 0, (BIT(16) - 1),
 			0);
+		sde_cp_crtc_install_range_property(crtc,
+			"SDE_DSPP_AD_V4_VSYNC_COUNT",
+			SDE_CP_CRTC_DSPP_AD_VSYNC_COUNT, 0, U32_MAX, 0);
 		break;
 	default:
 		DRM_ERROR("version %d not supported\n", version);
@@ -1867,6 +1899,11 @@
 		hw_cfg.displayh = num_mixers * hw_lm->cfg.out_width;
 		hw_cfg.displayv = hw_lm->cfg.out_height;
 		hw_cfg.mixer_info = hw_lm;
+
+		if (ad_prop == AD_VSYNC_UPDATE) {
+			hw_cfg.payload = &sde_crtc->ad_vsync_count;
+			hw_cfg.len = sizeof(sde_crtc->ad_vsync_count);
+		}
 		ad_cfg.prop = ad_prop;
 		ad_cfg.hw_cfg = &hw_cfg;
 		ret = hw_dspp->ops.validate_ad(hw_dspp, (u32 *)&ad_prop);
@@ -2118,3 +2155,35 @@
 exit:
 	return ret;
 }
+
+void sde_cp_update_ad_vsync_count(struct drm_crtc *crtc, u32 val)
+{
+	struct sde_crtc *sde_crtc;
+
+	if (!crtc) {
+		DRM_ERROR("invalid crtc %pK\n", crtc);
+		return;
+	}
+
+	sde_crtc = to_sde_crtc(crtc);
+	if (!sde_crtc) {
+		DRM_ERROR("invalid sde_crtc %pK\n", sde_crtc);
+		return;
+	}
+
+	sde_crtc->ad_vsync_count = val;
+	sde_cp_update_ad_vsync_prop(sde_crtc, val);
+}
+
+static void sde_cp_update_ad_vsync_prop(struct sde_crtc *sde_crtc, u32 val)
+{
+	struct sde_cp_node *prop_node = NULL;
+
+	list_for_each_entry(prop_node, &sde_crtc->feature_list, feature_list) {
+		if (prop_node->feature == SDE_CP_CRTC_DSPP_AD_VSYNC_COUNT) {
+			prop_node->prop_val = val;
+			pr_debug("AD vsync count updated to %d\n", val);
+			return;
+		}
+	}
+}
diff --git a/drivers/gpu/drm/msm/sde/sde_color_processing.h b/drivers/gpu/drm/msm/sde/sde_color_processing.h
index 7eb1738..620db26 100644
--- a/drivers/gpu/drm/msm/sde/sde_color_processing.h
+++ b/drivers/gpu/drm/msm/sde/sde_color_processing.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2018, 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
@@ -146,4 +146,11 @@
  */
 int sde_cp_hist_interrupt(struct drm_crtc *crtc_drm, bool en,
 	struct sde_irq_callback *hist_irq);
+
+/**
+ * sde_cp_update_ad_vsync_count: Api to update AD vsync count
+ * @crtc: Pointer to crtc.
+ * @val: vsync count value
+ */
+void sde_cp_update_ad_vsync_count(struct drm_crtc *crtc, u32 val);
 #endif /*_SDE_COLOR_PROCESSING_H */
diff --git a/drivers/gpu/drm/msm/sde/sde_connector.c b/drivers/gpu/drm/msm/sde/sde_connector.c
index d2f8d12..15b5465 100644
--- a/drivers/gpu/drm/msm/sde/sde_connector.c
+++ b/drivers/gpu/drm/msm/sde/sde_connector.c
@@ -623,6 +623,7 @@
 void sde_connector_helper_bridge_disable(struct drm_connector *connector)
 {
 	int rc;
+	struct sde_connector *c_conn = NULL;
 
 	if (!connector)
 		return;
@@ -633,6 +634,34 @@
 				connector->base.id, rc);
 		SDE_EVT32(connector->base.id, SDE_EVTLOG_ERROR);
 	}
+
+	/* Disable ESD thread */
+	sde_connector_schedule_status_work(connector, false);
+
+	c_conn = to_sde_connector(connector);
+	if (c_conn->panel_dead) {
+		c_conn->bl_device->props.power = FB_BLANK_POWERDOWN;
+		c_conn->bl_device->props.state |= BL_CORE_FBBLANK;
+		backlight_update_status(c_conn->bl_device);
+	}
+}
+
+void sde_connector_helper_bridge_enable(struct drm_connector *connector)
+{
+	struct sde_connector *c_conn = NULL;
+
+	if (!connector)
+		return;
+
+	c_conn = to_sde_connector(connector);
+
+	/* Special handling for ESD recovery case */
+	if (c_conn->panel_dead) {
+		c_conn->bl_device->props.power = FB_BLANK_UNBLANK;
+		c_conn->bl_device->props.state &= ~BL_CORE_FBBLANK;
+		backlight_update_status(c_conn->bl_device);
+		c_conn->panel_dead = false;
+	}
 }
 
 int sde_connector_clk_ctrl(struct drm_connector *connector, bool enable)
@@ -1734,15 +1763,15 @@
 static void _sde_connector_report_panel_dead(struct sde_connector *conn)
 {
 	struct drm_event event;
-	bool panel_dead = true;
 
 	if (!conn)
 		return;
 
+	conn->panel_dead = true;
 	event.type = DRM_EVENT_PANEL_DEAD;
 	event.length = sizeof(bool);
 	msm_mode_object_event_notify(&conn->base.base,
-		conn->base.dev, &event, (u8 *)&panel_dead);
+		conn->base.dev, &event, (u8 *)&conn->panel_dead);
 	sde_encoder_display_failure_notification(conn->encoder);
 	SDE_EVT32(SDE_EVTLOG_ERROR);
 	SDE_ERROR("esd check failed report PANEL_DEAD conn_id: %d enc_id: %d\n",
diff --git a/drivers/gpu/drm/msm/sde/sde_connector.h b/drivers/gpu/drm/msm/sde/sde_connector.h
index c6f348e..0c24454 100644
--- a/drivers/gpu/drm/msm/sde/sde_connector.h
+++ b/drivers/gpu/drm/msm/sde/sde_connector.h
@@ -324,6 +324,8 @@
  * @status_work: work object to perform status checks
  * @force_panel_dead: variable to trigger forced ESD recovery
  * @esd_status_interval: variable to change ESD check interval in millisec
+ * @panel_dead: Flag to indicate if panel has gone bad
+ * @esd_status_check: Flag to indicate if ESD thread is scheduled or not
  * @bl_scale_dirty: Flag to indicate PP BL scale value(s) is changed
  * @bl_scale: BL scale value for ABA feature
  * @bl_scale_ad: BL scale value for AD feature
@@ -365,7 +367,7 @@
 	struct delayed_work status_work;
 	u32 force_panel_dead;
 	u32 esd_status_interval;
-
+	bool panel_dead;
 	bool esd_status_check;
 
 	bool bl_scale_dirty;
@@ -763,6 +765,12 @@
 void sde_connector_helper_bridge_disable(struct drm_connector *connector);
 
 /**
+ * sde_connector_helper_bridge_enable - helper function for drm bridge enable
+ * @connector: Pointer to DRM connector object
+ */
+void sde_connector_helper_bridge_enable(struct drm_connector *connector);
+
+/**
  * sde_connector_get_panel_vfp - helper to get panel vfp
  * @connector: pointer to drm connector
  * @h_active: panel width
diff --git a/drivers/gpu/drm/msm/sde/sde_crtc.c b/drivers/gpu/drm/msm/sde/sde_crtc.c
index 7daab2a..7df1cc9 100644
--- a/drivers/gpu/drm/msm/sde/sde_crtc.c
+++ b/drivers/gpu/drm/msm/sde/sde_crtc.c
@@ -2440,6 +2440,23 @@
 }
 
 /**
+ * _sde_crtc_clear_dim_layers_v1 - clear all dim layer settings
+ * @cstate:      Pointer to sde crtc state
+ */
+static void _sde_crtc_clear_dim_layers_v1(struct sde_crtc_state *cstate)
+{
+	u32 i;
+
+	if (!cstate)
+		return;
+
+	for (i = 0; i < cstate->num_dim_layers; i++)
+		memset(&cstate->dim_layer[i], 0, sizeof(cstate->dim_layer[i]));
+
+	cstate->num_dim_layers = 0;
+}
+
+/**
  * _sde_crtc_set_dim_layer_v1 - copy dim layer settings from userspace
  * @cstate:      Pointer to sde crtc state
  * @user_ptr:    User ptr for sde_drm_dim_layer_v1 struct
@@ -2459,6 +2476,8 @@
 	dim_layer = cstate->dim_layer;
 
 	if (!usr_ptr) {
+		/* usr_ptr is null when setting the default property value */
+		_sde_crtc_clear_dim_layers_v1(cstate);
 		SDE_DEBUG("dim_layer data removed\n");
 		return;
 	}
@@ -4125,6 +4144,7 @@
 	event.type = DRM_EVENT_CRTC_POWER;
 	event.length = sizeof(u32);
 	sde_cp_crtc_suspend(crtc);
+	sde_cp_update_ad_vsync_count(crtc, 0);
 	power_on = 0;
 	msm_mode_object_event_notify(&crtc->base, crtc->dev, &event,
 			(u8 *)&power_on);
diff --git a/drivers/gpu/drm/msm/sde/sde_crtc.h b/drivers/gpu/drm/msm/sde/sde_crtc.h
index 64f3821..116af84 100644
--- a/drivers/gpu/drm/msm/sde/sde_crtc.h
+++ b/drivers/gpu/drm/msm/sde/sde_crtc.h
@@ -155,6 +155,7 @@
  * @dirty_list    : list of color processing features are dirty
  * @ad_dirty: list containing ad properties that are dirty
  * @ad_active: list containing ad properties that are active
+ * @ad_vsync_count : count of vblank since last reset for AD
  * @crtc_lock     : crtc lock around create, destroy and access.
  * @frame_pending : Whether or not an update is pending
  * @frame_events  : static allocation of in-flight frame events
@@ -224,6 +225,7 @@
 	struct list_head ad_dirty;
 	struct list_head ad_active;
 	struct list_head user_event_list;
+	u32 ad_vsync_count;
 
 	struct mutex crtc_lock;
 	struct mutex crtc_cp_lock;
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder.c b/drivers/gpu/drm/msm/sde/sde_encoder.c
index bea6693..d287b71 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder.c
+++ b/drivers/gpu/drm/msm/sde/sde_encoder.c
@@ -80,6 +80,11 @@
 /* Maximum number of VSYNC wait attempts for RSC state transition */
 #define MAX_RSC_WAIT	5
 
+#define TOPOLOGY_DUALPIPE_MERGE_MODE(x) \
+		(((x) == SDE_RM_TOPOLOGY_DUALPIPE_DSCMERGE) || \
+		((x) == SDE_RM_TOPOLOGY_DUALPIPE_3DMERGE) || \
+		((x) == SDE_RM_TOPOLOGY_DUALPIPE_3DMERGE_DSC))
+
 /**
  * enum sde_enc_rc_events - events for resource control state machine
  * @SDE_ENC_RC_EVENT_KICKOFF:
@@ -215,6 +220,7 @@
 struct sde_encoder_virt {
 	struct drm_encoder base;
 	spinlock_t enc_spinlock;
+	struct mutex vblank_ctl_lock;
 	uint32_t bus_scaling_client;
 
 	uint32_t display_num_of_h_tiles;
@@ -2648,7 +2654,6 @@
 	struct sde_encoder_virt *sde_enc = NULL;
 	struct msm_drm_private *priv;
 	struct sde_kms *sde_kms;
-	struct drm_connector *drm_conn = NULL;
 	enum sde_intf_mode intf_mode;
 	int i = 0;
 
@@ -2677,10 +2682,6 @@
 
 	SDE_EVT32(DRMID(drm_enc));
 
-	/* Disable ESD thread */
-	drm_conn = sde_enc->cur_master->connector;
-	sde_connector_schedule_status_work(drm_conn, false);
-
 	/* wait for idle */
 	sde_encoder_wait_for_event(drm_enc, MSM_ENC_TX_COMPLETE);
 
@@ -3402,13 +3403,14 @@
 static void _sde_encoder_setup_dither(struct sde_encoder_phys *phys)
 {
 	void *dither_cfg;
-	int ret = 0, rc;
+	int ret = 0, rc, i = 0;
 	size_t len = 0;
 	enum sde_rm_topology_name topology;
 	struct drm_encoder *drm_enc;
 	struct msm_mode_info mode_info;
 	struct msm_display_dsc_info *dsc = NULL;
 	struct sde_encoder_virt *sde_enc;
+	struct sde_hw_pingpong *hw_pp;
 
 	if (!phys || !phys->connector || !phys->hw_pp ||
 			!phys->hw_pp->ops.setup_dither || !phys->parent)
@@ -3431,12 +3433,24 @@
 	/* disable dither for 10 bpp or 10bpc dsc config */
 	if (dsc->bpp == 10 || dsc->bpc == 10) {
 		phys->hw_pp->ops.setup_dither(phys->hw_pp, NULL, 0);
+		return;
+	}
+
+	ret = sde_connector_get_dither_cfg(phys->connector,
+			phys->connector->state, &dither_cfg, &len);
+	if (ret)
+		return;
+
+	if (TOPOLOGY_DUALPIPE_MERGE_MODE(topology)) {
+		for (i = 0; i < MAX_CHANNELS_PER_ENC; i++) {
+			hw_pp = sde_enc->hw_pp[i];
+			if (hw_pp) {
+				phys->hw_pp->ops.setup_dither(hw_pp, dither_cfg,
+								len);
+			}
+		}
 	} else {
-		ret = sde_connector_get_dither_cfg(phys->connector,
-				phys->connector->state, &dither_cfg, &len);
-		if (!ret)
-			phys->hw_pp->ops.setup_dither(phys->hw_pp,
-				dither_cfg, len);
+		phys->hw_pp->ops.setup_dither(phys->hw_pp, dither_cfg, len);
 	}
 }
 
@@ -4388,6 +4402,7 @@
 	phys_params.parent = &sde_enc->base;
 	phys_params.parent_ops = parent_ops;
 	phys_params.enc_spinlock = &sde_enc->enc_spinlock;
+	phys_params.vblank_ctl_lock = &sde_enc->vblank_ctl_lock;
 
 	SDE_DEBUG("\n");
 
@@ -4530,6 +4545,7 @@
 
 	sde_enc->cur_master = NULL;
 	spin_lock_init(&sde_enc->enc_spinlock);
+	mutex_init(&sde_enc->vblank_ctl_lock);
 	drm_enc = &sde_enc->base;
 	drm_encoder_init(dev, drm_enc, &sde_encoder_funcs, drm_enc_mode, NULL);
 	drm_encoder_helper_add(drm_enc, &sde_encoder_helper_funcs);
@@ -4858,10 +4874,14 @@
 	SDE_EVT32_VERBOSE(DRMID(enc));
 
 	disp_thread = &priv->disp_thread[sde_enc->crtc->index];
-
-	kthread_queue_work(&disp_thread->worker,
-				&sde_enc->esd_trigger_work);
-	kthread_flush_work(&sde_enc->esd_trigger_work);
+	if (current->tgid == disp_thread->thread->tgid) {
+		sde_encoder_resource_control(&sde_enc->base,
+					     SDE_ENC_RC_EVENT_KICKOFF);
+	} else {
+		kthread_queue_work(&disp_thread->worker,
+				   &sde_enc->esd_trigger_work);
+		kthread_flush_work(&sde_enc->esd_trigger_work);
+	}
 	/**
 	 * panel may stop generating te signal (vsync) during esd failure. rsc
 	 * hardware may hang without vsync. Avoid rsc hang by generating the
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder.h b/drivers/gpu/drm/msm/sde/sde_encoder.h
index 0e8e9dd..8225dcd 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder.h
+++ b/drivers/gpu/drm/msm/sde/sde_encoder.h
@@ -242,10 +242,6 @@
  * esd timeout or other display failure notification. This event flows from
  * dsi, sde_connector to sde_encoder.
  *
- * This api must not be called from crtc_commit (display) thread because it
- * requests the flush work on same thread. It is called from esd check thread
- * based on current design.
- *
  *      TODO: manage the event at sde_kms level for forward processing.
  * @drm_enc:    Pointer to drm encoder structure
  * @Return:     true if successful in updating the encoder structure
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder_phys.h b/drivers/gpu/drm/msm/sde/sde_encoder_phys.h
index 9557d41..ea53af9 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder_phys.h
+++ b/drivers/gpu/drm/msm/sde/sde_encoder_phys.h
@@ -284,6 +284,7 @@
 	enum msm_display_compression_type comp_type;
 	spinlock_t *enc_spinlock;
 	enum sde_enc_enable_state enable_state;
+	struct mutex *vblank_ctl_lock;
 	atomic_t vblank_refcount;
 	atomic_t vsync_cnt;
 	atomic_t underrun_cnt;
@@ -434,6 +435,7 @@
 	enum sde_wb wb_idx;
 	enum msm_display_compression_type comp_type;
 	spinlock_t *enc_spinlock;
+	struct mutex *vblank_ctl_lock;
 };
 
 /**
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder_phys_cmd.c b/drivers/gpu/drm/msm/sde/sde_encoder_phys_cmd.c
index 828d771..f06ceb7 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder_phys_cmd.c
+++ b/drivers/gpu/drm/msm/sde/sde_encoder_phys_cmd.c
@@ -680,6 +680,7 @@
 		return -EINVAL;
 	}
 
+	mutex_lock(phys_enc->vblank_ctl_lock);
 	refcount = atomic_read(&phys_enc->vblank_refcount);
 
 	/* Slave encoders don't report vblank */
@@ -713,6 +714,7 @@
 				enable, refcount, SDE_EVTLOG_ERROR);
 	}
 
+	mutex_unlock(phys_enc->vblank_ctl_lock);
 	return ret;
 }
 
@@ -1398,6 +1400,7 @@
 	phys_enc->split_role = p->split_role;
 	phys_enc->intf_mode = INTF_MODE_CMD;
 	phys_enc->enc_spinlock = p->enc_spinlock;
+	phys_enc->vblank_ctl_lock = p->vblank_ctl_lock;
 	cmd_enc->stream_sel = 0;
 	phys_enc->enable_state = SDE_ENC_DISABLED;
 	phys_enc->comp_type = p->comp_type;
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c b/drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c
index 3fd45cb..d363d62 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c
+++ b/drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c
@@ -625,6 +625,7 @@
 		return -EINVAL;
 	}
 
+	mutex_lock(phys_enc->vblank_ctl_lock);
 	refcount = atomic_read(&phys_enc->vblank_refcount);
 	vid_enc = to_sde_encoder_phys_vid(phys_enc);
 
@@ -666,6 +667,7 @@
 				vid_enc->hw_intf->idx - INTF_0,
 				enable, refcount, SDE_EVTLOG_ERROR);
 	}
+	mutex_unlock(phys_enc->vblank_ctl_lock);
 	return ret;
 }
 
@@ -1239,6 +1241,7 @@
 	phys_enc->split_role = p->split_role;
 	phys_enc->intf_mode = INTF_MODE_VIDEO;
 	phys_enc->enc_spinlock = p->enc_spinlock;
+	phys_enc->vblank_ctl_lock = p->vblank_ctl_lock;
 	phys_enc->comp_type = p->comp_type;
 	for (i = 0; i < INTR_IDX_MAX; i++) {
 		irq = &phys_enc->irq[i];
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder_phys_wb.c b/drivers/gpu/drm/msm/sde/sde_encoder_phys_wb.c
index 9a90075..a3f2c73 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder_phys_wb.c
+++ b/drivers/gpu/drm/msm/sde/sde_encoder_phys_wb.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015-2017 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2015-2018 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
@@ -1410,6 +1410,7 @@
 	phys_enc->intf_mode = INTF_MODE_WB_LINE;
 	phys_enc->intf_idx = p->intf_idx;
 	phys_enc->enc_spinlock = p->enc_spinlock;
+	phys_enc->vblank_ctl_lock = p->vblank_ctl_lock;
 	atomic_set(&phys_enc->pending_retire_fence_cnt, 0);
 	INIT_LIST_HEAD(&wb_enc->irq_cb.list);
 
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_ad4.c b/drivers/gpu/drm/msm/sde/sde_hw_ad4.c
index 66445da..e60defd 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_ad4.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_ad4.c
@@ -109,6 +109,9 @@
 static int ad4_cfg_ipc_reset(struct sde_hw_dspp *dspp,
 		struct sde_ad_hw_cfg *cfg);
 
+static int ad4_vsync_update(struct sde_hw_dspp *dspp,
+		struct sde_ad_hw_cfg *cfg);
+
 static ad4_prop_setup prop_set_func[ad4_state_max][AD_PROPMAX] = {
 	[ad4_state_idle][AD_MODE] = ad4_mode_setup_common,
 	[ad4_state_idle][AD_INIT] = ad4_init_setup_idle,
@@ -121,6 +124,7 @@
 	[ad4_state_idle][AD_IPC_SUSPEND] = ad4_no_op_setup,
 	[ad4_state_idle][AD_IPC_RESUME] = ad4_no_op_setup,
 	[ad4_state_idle][AD_IPC_RESET] = ad4_no_op_setup,
+	[ad4_state_idle][AD_VSYNC_UPDATE] = ad4_no_op_setup,
 
 	[ad4_state_startup][AD_MODE] = ad4_mode_setup_common,
 	[ad4_state_startup][AD_INIT] = ad4_init_setup,
@@ -133,6 +137,7 @@
 	[ad4_state_startup][AD_STRENGTH] = ad4_no_op_setup,
 	[ad4_state_startup][AD_IPC_RESUME] = ad4_no_op_setup,
 	[ad4_state_startup][AD_IPC_RESET] = ad4_ipc_reset_setup_startup,
+	[ad4_state_startup][AD_VSYNC_UPDATE] = ad4_vsync_update,
 
 	[ad4_state_run][AD_MODE] = ad4_mode_setup_common,
 	[ad4_state_run][AD_INIT] = ad4_init_setup_run,
@@ -145,6 +150,7 @@
 	[ad4_state_run][AD_IPC_SUSPEND] = ad4_ipc_suspend_setup_run,
 	[ad4_state_run][AD_IPC_RESUME] = ad4_no_op_setup,
 	[ad4_state_run][AD_IPC_RESET] = ad4_setup_debug,
+	[ad4_state_run][AD_VSYNC_UPDATE] = ad4_vsync_update,
 
 	[ad4_state_ipcs][AD_MODE] = ad4_no_op_setup,
 	[ad4_state_ipcs][AD_INIT] = ad4_no_op_setup,
@@ -157,6 +163,7 @@
 	[ad4_state_ipcs][AD_IPC_SUSPEND] = ad4_no_op_setup,
 	[ad4_state_ipcs][AD_IPC_RESUME] = ad4_ipc_resume_setup_ipcs,
 	[ad4_state_ipcs][AD_IPC_RESET] = ad4_no_op_setup,
+	[ad4_state_ipcs][AD_VSYNC_UPDATE] = ad4_no_op_setup,
 
 	[ad4_state_ipcr][AD_MODE] = ad4_mode_setup_common,
 	[ad4_state_ipcr][AD_INIT] = ad4_init_setup_ipcr,
@@ -169,6 +176,7 @@
 	[ad4_state_ipcr][AD_IPC_SUSPEND] = ad4_ipc_suspend_setup_ipcr,
 	[ad4_state_ipcr][AD_IPC_RESUME] = ad4_no_op_setup,
 	[ad4_state_ipcr][AD_IPC_RESET] = ad4_ipc_reset_setup_ipcr,
+	[ad4_state_ipcr][AD_VSYNC_UPDATE] = ad4_no_op_setup,
 
 	[ad4_state_manual][AD_MODE] = ad4_mode_setup_common,
 	[ad4_state_manual][AD_INIT] = ad4_init_setup,
@@ -181,6 +189,7 @@
 	[ad4_state_manual][AD_IPC_SUSPEND] = ad4_no_op_setup,
 	[ad4_state_manual][AD_IPC_RESUME] = ad4_no_op_setup,
 	[ad4_state_manual][AD_IPC_RESET] = ad4_setup_debug_manual,
+	[ad4_state_manual][AD_VSYNC_UPDATE] = ad4_no_op_setup,
 };
 
 struct ad4_info {
@@ -201,6 +210,7 @@
 	u32 irdx_control_0;
 	u32 tf_ctrl;
 	u32 vc_control_0;
+	u32 frame_pushes;
 };
 
 static struct ad4_info info[DSPP_MAX] = {
@@ -905,6 +915,8 @@
 	val = (ad_cfg->cfg_param_046 & (BIT(16) - 1));
 	SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset, val);
 
+	info[dspp->idx].frame_pushes = ad_cfg->cfg_param_047;
+
 	return 0;
 }
 
@@ -1546,3 +1558,25 @@
 		ad4_mode_setup(dspp, info[dspp->idx].mode);
 	return 0;
 }
+
+static int ad4_vsync_update(struct sde_hw_dspp *dspp,
+		struct sde_ad_hw_cfg *cfg)
+{
+	u32 *count;
+	struct sde_hw_mixer *hw_lm;
+
+	if (cfg->hw_cfg->len != sizeof(u32) || !cfg->hw_cfg->payload) {
+		DRM_ERROR("invalid sz param exp %zd given %d cfg %pK\n",
+			sizeof(u32), cfg->hw_cfg->len, cfg->hw_cfg->payload);
+		return -EINVAL;
+	}
+
+	count = (u32 *)(cfg->hw_cfg->payload);
+	hw_lm = cfg->hw_cfg->mixer_info;
+
+	if (hw_lm && !hw_lm->cfg.right_mixer &&
+		(*count < info[dspp->idx].frame_pushes))
+		(*count)++;
+
+	return 0;
+}
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_mdss.h b/drivers/gpu/drm/msm/sde/sde_hw_mdss.h
index 8022f23..9b4e03d 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_mdss.h
+++ b/drivers/gpu/drm/msm/sde/sde_hw_mdss.h
@@ -570,7 +570,8 @@
 /**
  * struct sde_splash_data - Struct contains details of continuous splash
  *	memory region and initial pipeline configuration.
- * @smmu_handoff_pending:boolean to notify handoff from splash memory to smmu
+ * @resource_handoff_pending: boolean to notify boot up resource handoff
+ *			is pending.
  * @splash_base:	Base address of continuous splash region reserved
  *                      by bootloader
  * @splash_size:	Size of continuous splash region
@@ -585,7 +586,7 @@
  * @single_flush_en: Stores if the single flush is enabled.
  */
 struct sde_splash_data {
-	bool smmu_handoff_pending;
+	bool resource_handoff_pending;
 	unsigned long splash_base;
 	u32 splash_size;
 	struct ctl_top top[CTL_MAX - CTL_0];
diff --git a/drivers/gpu/drm/msm/sde/sde_kms.c b/drivers/gpu/drm/msm/sde/sde_kms.c
index 33bdec9..b0a52a7 100644
--- a/drivers/gpu/drm/msm/sde/sde_kms.c
+++ b/drivers/gpu/drm/msm/sde/sde_kms.c
@@ -936,9 +936,6 @@
 		}
 	}
 
-	if (sde_kms->splash_data.smmu_handoff_pending)
-		sde_kms->splash_data.smmu_handoff_pending = false;
-
 	/*
 	 * NOTE: for secure use cases we want to apply the new HW
 	 * configuration only after completing preparation for secure
@@ -972,6 +969,62 @@
 	}
 }
 
+static void _sde_kms_release_splash_resource(struct sde_kms *sde_kms,
+		struct drm_atomic_state *old_state)
+{
+	struct drm_crtc *crtc;
+	struct drm_crtc_state *crtc_state;
+	bool primary_crtc_active = false;
+	struct msm_drm_private *priv;
+	int i, rc = 0;
+
+	priv = sde_kms->dev->dev_private;
+
+	if (!sde_kms->splash_data.resource_handoff_pending)
+		return;
+
+	SDE_EVT32(SDE_EVTLOG_FUNC_CASE1);
+	for_each_crtc_in_state(old_state, crtc, crtc_state, i) {
+		if (crtc->state->active)
+			primary_crtc_active = true;
+		SDE_EVT32(crtc->base.id, crtc->state->active);
+	}
+
+	if (!primary_crtc_active) {
+		SDE_EVT32(SDE_EVTLOG_FUNC_CASE2);
+		return;
+	}
+
+	sde_kms->splash_data.resource_handoff_pending = false;
+
+	if (sde_kms->splash_data.cont_splash_en) {
+		SDE_DEBUG("disabling cont_splash feature\n");
+		sde_kms->splash_data.cont_splash_en = false;
+
+		for (i = 0; i < SDE_POWER_HANDLE_DBUS_ID_MAX; i++)
+			sde_power_data_bus_set_quota(&priv->phandle,
+				sde_kms->core_client,
+				SDE_POWER_HANDLE_DATA_BUS_CLIENT_RT, i,
+				SDE_POWER_HANDLE_ENABLE_BUS_AB_QUOTA,
+				SDE_POWER_HANDLE_ENABLE_BUS_IB_QUOTA);
+
+		sde_power_resource_enable(&priv->phandle, sde_kms->core_client,
+			false);
+	}
+
+	if (sde_kms->splash_data.splash_base) {
+		_sde_kms_splash_smmu_unmap(sde_kms);
+
+		rc = _sde_kms_release_splash_buffer(
+			sde_kms->splash_data.splash_base,
+			sde_kms->splash_data.splash_size);
+		if (rc)
+			pr_err("failed to release splash memory\n");
+		sde_kms->splash_data.splash_base = 0;
+		sde_kms->splash_data.splash_size = 0;
+	}
+}
+
 static void sde_kms_complete_commit(struct msm_kms *kms,
 		struct drm_atomic_state *old_state)
 {
@@ -1019,39 +1072,9 @@
 
 	sde_power_resource_enable(&priv->phandle, sde_kms->core_client, false);
 
+	_sde_kms_release_splash_resource(sde_kms, old_state);
+
 	SDE_EVT32_VERBOSE(SDE_EVTLOG_FUNC_EXIT);
-
-	if (sde_kms->splash_data.cont_splash_en) {
-		/* Releasing splash resources as we have first frame update */
-		rc = _sde_kms_splash_smmu_unmap(sde_kms);
-		SDE_DEBUG("Disabling cont_splash feature\n");
-		sde_kms->splash_data.cont_splash_en = false;
-
-		for (i = 0; i < SDE_POWER_HANDLE_DBUS_ID_MAX; i++)
-			sde_power_data_bus_set_quota(&priv->phandle,
-				sde_kms->core_client,
-				SDE_POWER_HANDLE_DATA_BUS_CLIENT_RT, i,
-				SDE_POWER_HANDLE_ENABLE_BUS_AB_QUOTA,
-				SDE_POWER_HANDLE_ENABLE_BUS_IB_QUOTA);
-
-		sde_power_resource_enable(&priv->phandle,
-				sde_kms->core_client, false);
-		SDE_DEBUG("removing Vote for MDP Resources\n");
-	}
-
-	/*
-	 * Even for continuous splash disabled cases we have to release
-	 * splash memory reservation back to system after first frame update.
-	 */
-	if (sde_kms->splash_data.splash_base) {
-		rc = _sde_kms_release_splash_buffer(
-				sde_kms->splash_data.splash_base,
-				sde_kms->splash_data.splash_size);
-		if (rc)
-			pr_err("Failed to release splash memory\n");
-		sde_kms->splash_data.splash_base = 0;
-		sde_kms->splash_data.splash_size = 0;
-	}
 }
 
 static void sde_kms_wait_for_commit_done(struct msm_kms *kms,
@@ -2704,6 +2727,19 @@
 	return rc;
 }
 
+static bool sde_kms_check_for_splash(struct msm_kms *kms)
+{
+	struct sde_kms *sde_kms;
+
+	if (!kms) {
+		SDE_ERROR("invalid kms\n");
+		return false;
+	}
+
+	sde_kms = to_sde_kms(kms);
+	return sde_kms->splash_data.cont_splash_en;
+}
+
 static int sde_kms_pm_suspend(struct device *dev)
 {
 	struct drm_device *ddev;
@@ -2904,6 +2940,7 @@
 	.register_events = _sde_kms_register_events,
 	.get_address_space = _sde_kms_get_address_space,
 	.postopen = _sde_kms_post_open,
+	.check_for_splash = sde_kms_check_for_splash,
 };
 
 /* the caller api needs to turn on clock before calling it */
@@ -2956,8 +2993,7 @@
 		 * address. To facilitate this requirement we need to have a
 		 * one to one mapping on SMMU until we have our first frame.
 		 */
-		if ((i == MSM_SMMU_DOMAIN_UNSECURE) &&
-			sde_kms->splash_data.smmu_handoff_pending) {
+		if (i == MSM_SMMU_DOMAIN_UNSECURE) {
 			ret = mmu->funcs->set_attribute(mmu,
 				DOMAIN_ATTR_EARLY_MAP,
 				&early_map);
@@ -2986,27 +3022,27 @@
 		}
 		aspace->domain_attached = true;
 		early_map = 0;
+
 		/* Mapping splash memory block */
 		if ((i == MSM_SMMU_DOMAIN_UNSECURE) &&
-			sde_kms->splash_data.smmu_handoff_pending) {
+				sde_kms->splash_data.splash_base) {
 			ret = _sde_kms_splash_smmu_map(sde_kms->dev, mmu,
 					&sde_kms->splash_data);
 			if (ret) {
 				SDE_ERROR("failed to map ret:%d\n", ret);
 				goto fail;
 			}
-			/*
-			 * Turning off early map after generating one to one
-			 * mapping for splash address space.
-			 */
-			ret = mmu->funcs->set_attribute(mmu,
-				DOMAIN_ATTR_EARLY_MAP,
-				&early_map);
-			if (ret) {
-				SDE_ERROR("failed to set map att ret:%d\n",
-									ret);
-				goto early_map_fail;
-			}
+		}
+
+		/*
+		 * Turning off early map after generating one to one
+		 * mapping for splash address space.
+		 */
+		ret = mmu->funcs->set_attribute(mmu, DOMAIN_ATTR_EARLY_MAP,
+			&early_map);
+		if (ret) {
+			SDE_ERROR("failed to set map att ret:%d\n", ret);
+			goto early_map_fail;
 		}
 	}
 
@@ -3285,12 +3321,7 @@
 					&sde_kms->splash_data,
 					sde_kms->catalog);
 
-	/*
-	 * SMMU handoff is necessary for continuous splash enabled
-	 * scenario.
-	 */
-	if (sde_kms->splash_data.cont_splash_en)
-		sde_kms->splash_data.smmu_handoff_pending = true;
+	sde_kms->splash_data.resource_handoff_pending = true;
 
 	/* Initialize reg dma block which is a singleton */
 	rc = sde_reg_dma_init(sde_kms->reg_dma, sde_kms->catalog,
diff --git a/drivers/iio/imu/Kconfig b/drivers/iio/imu/Kconfig
index 1f1ad41..0a8b763 100644
--- a/drivers/iio/imu/Kconfig
+++ b/drivers/iio/imu/Kconfig
@@ -39,6 +39,7 @@
 	  be called kmx61.
 
 source "drivers/iio/imu/inv_mpu6050/Kconfig"
+source "drivers/iio/imu/inv_icm20602/Kconfig"
 
 endmenu
 
diff --git a/drivers/iio/imu/Makefile b/drivers/iio/imu/Makefile
index c71bcd3..fab6a5e 100644
--- a/drivers/iio/imu/Makefile
+++ b/drivers/iio/imu/Makefile
@@ -15,5 +15,6 @@
 
 obj-y += bmi160/
 obj-y += inv_mpu6050/
+obj-y += inv_icm20602/
 
 obj-$(CONFIG_KMX61) += kmx61.o
diff --git a/drivers/iio/imu/inv_icm20602/Kconfig b/drivers/iio/imu/inv_icm20602/Kconfig
new file mode 100644
index 0000000..fa88689
--- /dev/null
+++ b/drivers/iio/imu/inv_icm20602/Kconfig
@@ -0,0 +1,14 @@
+#
+# inv-icm20602 drivers for Invensense MPU devices and combos
+#
+config INV_ICM20602_IIO
+	tristate "Invensense ICM20602 devices"
+	depends on I2C && SYSFS
+	select IIO_BUFFER
+	select IIO_BUFFER_CB
+	select IIO_TRIGGERED_BUFFER
+	help
+	  This driver supports the Invensense ICM20602 devices.
+	  It is a gyroscope/accelerometer combo device.
+	  This driver can be built as a module. The module will be called
+	  inv-icm20602.
diff --git a/drivers/iio/imu/inv_icm20602/Makefile b/drivers/iio/imu/inv_icm20602/Makefile
new file mode 100644
index 0000000..d60c10a
--- /dev/null
+++ b/drivers/iio/imu/inv_icm20602/Makefile
@@ -0,0 +1,6 @@
+#
+# Makefile for Invensense icm20602 device.
+#
+
+obj-$(CONFIG_INV_ICM20602_IIO) += inv-icm20602.o
+inv-icm20602-objs := inv_icm20602_core.o inv_icm20602_ring.o inv_icm20602_trigger.o inv_icm20602_bsp.o
diff --git a/drivers/iio/imu/inv_icm20602/inv_icm20602_bsp.c b/drivers/iio/imu/inv_icm20602/inv_icm20602_bsp.c
new file mode 100644
index 0000000..bfff285
--- /dev/null
+++ b/drivers/iio/imu/inv_icm20602/inv_icm20602_bsp.c
@@ -0,0 +1,932 @@
+/*
+ * Copyright (c) 2018 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/sysfs.h>
+#include <linux/jiffies.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/kfifo.h>
+#include <linux/poll.h>
+#include <linux/i2c.h>
+#include <linux/spi/spi.h>
+#include "inv_icm20602_bsp.h"
+#include "inv_icm20602_iio.h"
+
+#define icm20602_init_reg_addr(head, tail, reg_map) { \
+			enum inv_icm20602_reg_addr i;\
+			for (i = head; i <= tail; i++) {\
+				reg_map->address = i;\
+				reg_map->reg_u.reg = 0x0;\
+				reg_map++;\
+			} \
+}
+
+#define icm20602_write_reg_simple(st, register) \
+		icm20602_write_reg(st, \
+		register.address, \
+		register.reg_u.reg)
+
+static struct inv_icm20602_reg_map reg_set_20602;
+
+int icm20602_init_reg_map(void)
+{
+	struct struct_XG_OFFS_TC_H *reg_map = &(reg_set_20602.XG_OFFS_TC_H);
+
+	icm20602_init_reg_addr(ADDR_XG_OFFS_TC_H,
+			ADDR_XG_OFFS_TC_L, reg_map);
+
+	icm20602_init_reg_addr(ADDR_YG_OFFS_TC_H,
+			ADDR_YG_OFFS_TC_L, reg_map);
+
+	icm20602_init_reg_addr(ADDR_ZG_OFFS_TC_H,
+			ADDR_ZG_OFFS_TC_L, reg_map);
+
+	icm20602_init_reg_addr(ADDR_SELF_TEST_X_ACCEL,
+			ADDR_SELF_TEST_Z_ACCEL, reg_map);
+
+	icm20602_init_reg_addr(ADDR_XG_OFFS_USRH,
+			ADDR_LP_MODE_CFG, reg_map);
+
+	icm20602_init_reg_addr(ADDR_ACCEL_WOM_X_THR,
+			ADDR_FIFO_EN, reg_map);
+
+	icm20602_init_reg_addr(ADDR_FSYNC_INT,
+			ADDR_GYRO_ZOUT_L, reg_map);
+
+	icm20602_init_reg_addr(ADDR_SELF_TEST_X_GYRO,
+			ADDR_SELF_TEST_Z_GYRO, reg_map);
+
+	icm20602_init_reg_addr(ADDR_FIFO_WM_TH1,
+			ADDR_FIFO_WM_TH2, reg_map);
+
+	icm20602_init_reg_addr(ADDR_SIGNAL_PATH_RESET,
+			ADDR_PWR_MGMT_2, reg_map);
+
+	icm20602_init_reg_addr(ADDR_I2C_IF,
+			ADDR_I2C_IF, reg_map);
+
+	icm20602_init_reg_addr(ADDR_FIFO_COUNTH,
+			ADDR_XA_OFFSET_L, reg_map);
+
+	icm20602_init_reg_addr(ADDR_YA_OFFSET_H,
+			ADDR_YA_OFFSET_L, reg_map);
+
+	icm20602_init_reg_addr(ADDR_ZA_OFFSET_H,
+			ADDR_ZA_OFFSET_L, reg_map);
+
+	return MPU_SUCCESS;
+}
+
+#define W_FLG	0
+#define R_FLG	1
+int icm20602_bulk_read(struct inv_icm20602_state *st,
+			int reg, char *buf, int size)
+{
+	int result = MPU_SUCCESS;
+	char tx_buf[2] = {0x0, 0x0};
+	int tmp_size = size;
+	int tmp_buf = buf;
+	struct i2c_msg msg[2];
+
+	if (!st || !buf)
+		return -MPU_FAIL;
+
+	if (st->interface == ICM20602_SPI) {
+		tx_buf[0] = ICM20602_READ_REG(reg);
+		result = spi_write_then_read(st->spi, &tx_buf[0],
+			1, tmp_buf, size);
+		if (result) {
+			pr_err("mpu read reg %u failed, rc %d\n",
+				reg, result);
+			result = -MPU_READ_FAIL;
+		}
+	} else {
+		result = size;
+		while (tmp_size > 0) {
+#ifdef ICM20602_I2C_SMBUS
+			result += i2c_smbus_read_i2c_block_data(st->client,
+				reg, (tmp_size < 32)?tmp_size:32, tmp_buf);
+			tmp_size -= 32;
+			tmp_buf += tmp_size;
+#else
+			tx_buf[0] = reg;
+			msg[0].addr = st->client->addr;
+			msg[0].flags = W_FLG;
+			msg[0].len = 1;
+			msg[0].buf = tx_buf;
+
+			msg[1].addr = st->client->addr;
+			msg[1].flags = I2C_M_RD;
+			msg[1].len = (tmp_size < 32)?tmp_size:32;
+			msg[1].buf = tmp_buf;
+			i2c_transfer(st->client->adapter, msg, ARRAY_SIZE(msg));
+			tmp_size -= 32;
+			tmp_buf += tmp_size;
+#endif
+		}
+	}
+
+	return result;
+}
+
+static int icm20602_write_reg(struct inv_icm20602_state *st,
+			 uint8_t reg, uint8_t val)
+{
+	int result = MPU_SUCCESS;
+	char txbuf[2] = {0x0, 0x0};
+	struct i2c_msg msg[1];
+
+	if (st->interface == ICM20602_SPI) {
+		txbuf[0] = ICM20602_WRITE_REG(reg);
+		txbuf[1] = val;
+		result = spi_write_then_read(st->spi, &txbuf[0], 2, NULL, 0);
+		if (result) {
+			pr_err("mpu write reg %u failed, rc %d\n",
+						reg, val);
+			result = -MPU_READ_FAIL;
+		}
+	} else if (st->interface == ICM20602_I2C) {
+#ifdef ICM20602_I2C_SMBUS
+		result = i2c_smbus_write_i2c_block_data(st->client,
+						reg, 1, &val);
+#else
+		txbuf[0] = reg;
+		txbuf[1] = val;
+		msg[0].addr = st->client->addr;
+		msg[0].flags = I2C_M_IGNORE_NAK;
+		msg[0].len = 2;
+		msg[0].buf = txbuf;
+
+		i2c_transfer(st->client->adapter, msg, ARRAY_SIZE(msg));
+#endif
+	}
+
+	return result;
+}
+
+static int icm20602_read_reg(struct inv_icm20602_state *st,
+				uint8_t reg, uint8_t *val)
+{
+	int result = MPU_SUCCESS;
+	char txbuf[1] = {0x0};
+	char rxbuf[1] = {0x0};
+	struct i2c_msg msg[2];
+
+	if (st->interface == ICM20602_SPI) {
+		txbuf[0] = ICM20602_READ_REG(reg);
+		result = spi_write_then_read(st->spi,
+						&txbuf[0], 1, rxbuf, 1);
+		if (result) {
+			pr_err("mpu read reg %u failed, rc %d\n",
+						reg, result);
+			result = -MPU_READ_FAIL;
+		}
+	} else if (st->interface == ICM20602_I2C) {
+#ifdef ICM20602_I2C_SMBUS
+		result = i2c_smbus_read_i2c_block_data(st->client,
+						reg, 1, rxbuf);
+		if (result != 1) {
+			pr_err("mpu read reg %u failed, rc %d\n",
+						reg, result);
+			result = -MPU_READ_FAIL;
+		}
+#else
+		txbuf[0] = reg;
+		msg[0].addr = st->client->addr;
+		msg[0].flags = W_FLG;
+		msg[0].len = 1;
+		msg[0].buf = txbuf;
+
+		msg[1].addr = st->client->addr;
+		msg[1].flags = I2C_M_RD;
+		msg[1].len = 1;
+		msg[1].buf = rxbuf;
+
+		i2c_transfer(st->client->adapter, msg, ARRAY_SIZE(msg));
+#endif
+	}
+	*val = rxbuf[0];
+
+	return result;
+}
+
+#define combine_8_to_16(upper, lower) ((upper << 8) | lower)
+
+int icm20602_read_raw(struct inv_icm20602_state *st,
+		struct struct_icm20602_real_data *real_data, uint32_t type)
+{
+	struct struct_icm20602_raw_data raw_data;
+
+	if (type & ACCEL != 0) {
+		icm20602_read_reg(st,
+			reg_set_20602.ACCEL_XOUT_H.address,
+			&raw_data.ACCEL_XOUT_H);
+		icm20602_read_reg(st,
+			reg_set_20602.ACCEL_XOUT_L.address,
+			&raw_data.ACCEL_XOUT_L);
+		real_data->ACCEL_XOUT =
+			combine_8_to_16(raw_data.ACCEL_XOUT_H,
+			raw_data.ACCEL_XOUT_L);
+
+		icm20602_read_reg(st,
+			reg_set_20602.ACCEL_YOUT_H.address,
+			&raw_data.ACCEL_YOUT_H);
+		icm20602_read_reg(st,
+			reg_set_20602.ACCEL_YOUT_L.address,
+			&raw_data.ACCEL_YOUT_L);
+		real_data->ACCEL_YOUT =
+			combine_8_to_16(raw_data.ACCEL_YOUT_H,
+			raw_data.ACCEL_YOUT_L);
+
+		icm20602_read_reg(st,
+			reg_set_20602.ACCEL_ZOUT_H.address,
+			&raw_data.ACCEL_ZOUT_H);
+		icm20602_read_reg(st,
+			reg_set_20602.ACCEL_ZOUT_L.address,
+			&raw_data.ACCEL_ZOUT_L);
+		real_data->ACCEL_ZOUT =
+			combine_8_to_16(raw_data.ACCEL_ZOUT_H,
+			raw_data.ACCEL_ZOUT_L);
+	}
+
+	if (type & GYRO != 0) {
+		icm20602_read_reg(st,
+			reg_set_20602.GYRO_XOUT_H.address,
+			&raw_data.GYRO_XOUT_H);
+		icm20602_read_reg(st,
+			reg_set_20602.GYRO_XOUT_L.address,
+			&raw_data.GYRO_XOUT_L);
+		real_data->GYRO_XOUT =
+			combine_8_to_16(raw_data.GYRO_XOUT_H,
+			raw_data.GYRO_XOUT_L);
+
+		icm20602_read_reg(st,
+			reg_set_20602.GYRO_YOUT_H.address,
+			&raw_data.GYRO_YOUT_H);
+		icm20602_read_reg(st,
+			reg_set_20602.GYRO_YOUT_L.address,
+			&raw_data.GYRO_YOUT_L);
+		real_data->GYRO_YOUT =
+			combine_8_to_16(raw_data.GYRO_YOUT_H,
+			raw_data.GYRO_YOUT_L);
+
+		icm20602_read_reg(st,
+			reg_set_20602.GYRO_ZOUT_H.address,
+			&raw_data.GYRO_ZOUT_H);
+		icm20602_read_reg(st,
+			reg_set_20602.GYRO_ZOUT_L.address,
+			&raw_data.GYRO_ZOUT_L);
+		real_data->GYRO_ZOUT =
+			combine_8_to_16(raw_data.GYRO_ZOUT_H,
+			raw_data.GYRO_ZOUT_L);
+	}
+
+	return MPU_SUCCESS;
+}
+
+int icm20602_read_fifo(struct inv_icm20602_state *st,
+					void *buf, const int size)
+{
+	return icm20602_bulk_read(st,
+		reg_set_20602.FIFO_R_W.address, buf, size);
+}
+
+int icm20602_start_fifo(struct inv_icm20602_state *st)
+{
+	struct icm20602_user_config *config = NULL;
+
+	config = st->config;
+
+	/* enable fifo */
+	if (config->fifo_enabled) {
+		reg_set_20602.USER_CTRL.reg_u.REG.FIFO_EN = 0x1;
+		if (icm20602_write_reg_simple(st,
+						reg_set_20602.USER_CTRL)) {
+			pr_err("icm20602 start fifo failed\n");
+			return -MPU_FAIL;
+		}
+
+		/* enable interrupt, need to test */
+		reg_set_20602.INT_ENABLE.reg_u.REG.FIFO_OFLOW_EN = 0x1;
+		reg_set_20602.INT_ENABLE.reg_u.REG.DATA_RDY_INT_EN = 0x0;
+		if (icm20602_write_reg_simple(st,
+						reg_set_20602.INT_ENABLE)) {
+			pr_err("icm20602 set FIFO_OFLOW_EN failed\n");
+			return -MPU_FAIL;
+		}
+	}
+
+	return MPU_SUCCESS;
+}
+
+static int icm20602_stop_fifo(struct inv_icm20602_state *st)
+{
+	struct icm20602_user_config *config = NULL;
+
+	config = st->config;
+	/* disable fifo */
+	if (config->fifo_enabled) {
+		reg_set_20602.USER_CTRL.reg_u.REG.FIFO_EN = 0x0;
+		reg_set_20602.USER_CTRL.reg_u.REG.FIFO_RST = 0x1;
+		if (icm20602_write_reg_simple(st,
+				reg_set_20602.USER_CTRL)) {
+			reg_set_20602.USER_CTRL.reg_u.REG.FIFO_RST = 0x0;
+			pr_err("icm20602 stop fifo failed\n");
+			return -MPU_FAIL;
+		}
+		reg_set_20602.USER_CTRL.reg_u.REG.FIFO_RST = 0x0;
+	}
+
+	return MPU_SUCCESS;
+}
+
+static int icm20602_config_waterlevel(struct inv_icm20602_state *st)
+{
+	struct icm20602_user_config *config = NULL;
+	uint8_t val = 0;
+
+	config = st->config;
+	if (config->fifo_enabled != true)
+		return MPU_SUCCESS;
+	/* config waterlevel as the fps need */
+	config->fifo_waterlevel = (config->user_fps_in_ms /
+				(1000 / config->gyro_accel_sample_rate))
+				*ICM20602_PACKAGE_SIZE;
+
+	if (config->fifo_waterlevel > 1023 ||
+		config->fifo_waterlevel/50 >
+		(1023-config->fifo_waterlevel)/ICM20602_PACKAGE_SIZE) {
+		pr_err("set fifo_waterlevel failed %d\n",
+					config->fifo_waterlevel);
+		return MPU_FAIL;
+	}
+	reg_set_20602.FIFO_WM_TH1.reg_u.reg =
+				(config->fifo_waterlevel & 0xff00) >> 8;
+	reg_set_20602.FIFO_WM_TH2.reg_u.reg =
+				(config->fifo_waterlevel & 0x00ff);
+
+	icm20602_write_reg_simple(st, reg_set_20602.FIFO_WM_TH1);
+	icm20602_write_reg_simple(st, reg_set_20602.FIFO_WM_TH2);
+	icm20602_read_reg(st, reg_set_20602.FIFO_WM_TH1.address, &val);
+	icm20602_read_reg(st, reg_set_20602.FIFO_WM_TH2.address, &val);
+
+	return MPU_SUCCESS;
+}
+
+static int icm20602_read_ST_code(struct inv_icm20602_state *st)
+{
+	struct icm20602_user_config *config = NULL;
+	int result = 0;
+
+	config = st->config;
+	result |= icm20602_read_reg(st, reg_set_20602.SELF_TEST_X_ACCEL.address,
+		&(config->acc_self_test.X));
+	result |= icm20602_read_reg(st, reg_set_20602.SELF_TEST_Y_ACCEL.address,
+		&(config->acc_self_test.Y));
+	result |= icm20602_read_reg(st, reg_set_20602.SELF_TEST_Z_ACCEL.address,
+		&(config->acc_self_test.Z));
+
+	result |= icm20602_read_reg(st, reg_set_20602.SELF_TEST_X_GYRO.address,
+		&(config->gyro_self_test.X));
+	result |= icm20602_read_reg(st, reg_set_20602.SELF_TEST_Y_GYRO.address,
+		&(config->gyro_self_test.Y));
+	result |= icm20602_read_reg(st, reg_set_20602.SELF_TEST_Z_GYRO.address,
+		&(config->gyro_self_test.Z));
+
+	return result;
+}
+
+static int icm20602_set_self_test(struct inv_icm20602_state *st)
+{
+	uint8_t raw_data[6] = {0, 0, 0, 0, 0, 0};
+	uint8_t selfTest[6];
+	float factory_trim[6];
+	int result = 0;
+	int ii;
+
+	reg_set_20602.SMPLRT_DIV.reg_u.REG.SMPLRT_DIV = 0;
+	result |= icm20602_write_reg_simple(st, reg_set_20602.SMPLRT_DIV);
+
+	reg_set_20602.CONFIG.reg_u.REG.DLFP_CFG = INV_ICM20602_GYRO_LFP_92HZ;
+	result |= icm20602_write_reg_simple(st, reg_set_20602.CONFIG);
+
+	reg_set_20602.GYRO_CONFIG.reg_u.REG.FCHOICE_B = 0x0;
+	reg_set_20602.GYRO_CONFIG.reg_u.REG.FS_SEL = ICM20602_GYRO_FSR_250DPS;
+	result |= icm20602_write_reg_simple(st, reg_set_20602.GYRO_CONFIG);
+
+	reg_set_20602.ACCEL_CONFIG2.reg_u.REG.A_DLPF_CFG = ICM20602_ACCLFP_99;
+	reg_set_20602.ACCEL_CONFIG2.reg_u.REG.ACCEL_FCHOICE_B = 0X0;
+	result |= icm20602_write_reg_simple(st, reg_set_20602.ACCEL_CONFIG2);
+
+	reg_set_20602.ACCEL_CONFIG.reg_u.REG.ACCEL_FS_SEL = ICM20602_ACC_FSR_2G;
+	result |= icm20602_write_reg_simple(st, reg_set_20602.ACCEL_CONFIG);
+
+	//icm20602_read_ST_code(st);
+
+	return 0;
+}
+
+static int icm20602_do_test_acc(struct inv_icm20602_state *st,
+	struct X_Y_Z *acc, struct X_Y_Z *acc_st)
+{
+	struct struct_icm20602_real_data *real_data =
+		kmalloc(sizeof(struct inv_icm20602_state), GFP_ATOMIC);
+	struct icm20602_user_config *config = st->config;
+	int i, j;
+
+	for (i = 0; i < SELFTEST_COUNT; i++) {
+		icm20602_read_raw(st, real_data, ACCEL);
+		acc->X += real_data->ACCEL_XOUT;
+		acc->Y += real_data->ACCEL_YOUT;
+		acc->Z += real_data->ACCEL_ZOUT;
+		usleep_range(1000, 1001);
+	}
+	acc->X /= SELFTEST_COUNT;
+	acc->X *= ST_PRECISION;
+
+	acc->Y /= SELFTEST_COUNT;
+	acc->Y *= ST_PRECISION;
+
+	acc->Z /= SELFTEST_COUNT;
+	acc->Z *= ST_PRECISION;
+
+	reg_set_20602.ACCEL_CONFIG.reg_u.REG.XG_ST = 0x1;
+	reg_set_20602.ACCEL_CONFIG.reg_u.REG.YG_ST = 0x1;
+	reg_set_20602.ACCEL_CONFIG.reg_u.REG.ZG_ST = 0x1;
+	icm20602_write_reg_simple(st, reg_set_20602.ACCEL_CONFIG);
+
+	for (i = 0; i < SELFTEST_COUNT; i++) {
+		icm20602_read_raw(st, real_data, ACCEL);
+		acc_st->X += real_data->ACCEL_XOUT;
+		acc_st->Y += real_data->ACCEL_YOUT;
+		acc_st->Z += real_data->ACCEL_ZOUT;
+		usleep_range(1000, 1001);
+	}
+	acc_st->X /= SELFTEST_COUNT;
+	acc_st->X *= ST_PRECISION;
+
+	acc_st->Y /= SELFTEST_COUNT;
+	acc_st->Y *= ST_PRECISION;
+
+	acc_st->Z /= SELFTEST_COUNT;
+	acc_st->Z *= ST_PRECISION;
+
+	return MPU_SUCCESS;
+}
+
+static int icm20602_do_test_gyro(struct inv_icm20602_state *st,
+	struct X_Y_Z *gyro, struct X_Y_Z *gyro_st)
+{
+	struct struct_icm20602_real_data *real_data =
+		kmalloc(sizeof(struct inv_icm20602_state), GFP_ATOMIC);
+	int i, j;
+
+	for (i = 0; i < SELFTEST_COUNT; i++) {
+		icm20602_read_raw(st, real_data, GYRO);
+		gyro->X += real_data->GYRO_XOUT;
+		gyro->Y += real_data->GYRO_YOUT;
+		gyro->Z += real_data->GYRO_ZOUT;
+		usleep_range(1000, 1001);
+	}
+	gyro->X /= SELFTEST_COUNT;
+	gyro->X *= ST_PRECISION;
+
+	gyro->Y /= SELFTEST_COUNT;
+	gyro->Y *= ST_PRECISION;
+
+	gyro->Z /= SELFTEST_COUNT;
+	gyro->Z *= ST_PRECISION;
+
+	reg_set_20602.GYRO_CONFIG.reg_u.REG.XG_ST = 0x1;
+	reg_set_20602.GYRO_CONFIG.reg_u.REG.YG_ST = 0x1;
+	reg_set_20602.GYRO_CONFIG.reg_u.REG.ZG_ST = 0x1;
+	icm20602_write_reg_simple(st, reg_set_20602.GYRO_CONFIG);
+
+	for (i = 0; i < SELFTEST_COUNT; i++) {
+		icm20602_read_raw(st, real_data, ACCEL);
+		gyro_st->X += real_data->GYRO_XOUT;
+		gyro_st->Y += real_data->GYRO_YOUT;
+		gyro_st->Z += real_data->GYRO_ZOUT;
+		usleep_range(1000, 1001);
+	}
+	gyro_st->X /= SELFTEST_COUNT;
+	gyro_st->X *= ST_PRECISION;
+
+	gyro_st->Y /= SELFTEST_COUNT;
+	gyro_st->Y *= ST_PRECISION;
+
+	gyro_st->Z /= SELFTEST_COUNT;
+	gyro_st->Z *= ST_PRECISION;
+
+	return MPU_SUCCESS;
+}
+
+static bool icm20602_check_acc_selftest(struct inv_icm20602_state *st,
+	struct X_Y_Z *acc, struct X_Y_Z *acc_st)
+{
+	struct X_Y_Z acc_ST_code, st_otp, st_shift_cust;
+	bool otp_value_zero = false, test_result = true;
+
+	acc_ST_code.X = st->config->acc_self_test.X;
+	acc_ST_code.Y = st->config->acc_self_test.Y;
+	acc_ST_code.Z = st->config->acc_self_test.Z;
+
+	st_otp.X = (st_otp.X != 0) ? mpu_st_tb[acc_ST_code.X - 1] : 0;
+	st_otp.Y = (st_otp.Y != 0) ? mpu_st_tb[acc_ST_code.Y - 1] : 0;
+	st_otp.Z = (st_otp.Z != 0) ? mpu_st_tb[acc_ST_code.Z - 1] : 0;
+
+	if (st_otp.X & st_otp.Y & st_otp.Z == 0)
+		otp_value_zero = true;
+
+	st_shift_cust.X = acc_st->X - acc->X;
+	st_shift_cust.Y = acc_st->X - acc->Y;
+	st_shift_cust.Z = acc_st->X - acc->Z;
+	if (!otp_value_zero) {
+		if (
+		st_shift_cust.X <
+		(st_otp.X * ST_PRECISION * ACC_ST_SHIFT_MIN / 100) ||
+		st_shift_cust.Y <
+		(st_otp.Y * ST_PRECISION * ACC_ST_SHIFT_MIN / 100) ||
+		st_shift_cust.Z <
+		(st_otp.Z * ST_PRECISION * ACC_ST_SHIFT_MIN / 100) ||
+
+		st_shift_cust.X >
+		(st_otp.X * ST_PRECISION * ACC_ST_SHIFT_MAX / 100) ||
+		st_shift_cust.Y >
+		(st_otp.Y * ST_PRECISION * ACC_ST_SHIFT_MAX / 100) ||
+		st_shift_cust.Z >
+		(st_otp.Z * ST_PRECISION * ACC_ST_SHIFT_MAX / 100)
+		) {
+			test_result = false;
+		}
+	} else {
+		if (
+		abs(st_shift_cust.X) <
+		(ACC_ST_AL_MIN * 16384 / 1000 * ST_PRECISION) ||
+		abs(st_shift_cust.Y) <
+		(ACC_ST_AL_MIN * 16384 / 1000 * ST_PRECISION) ||
+		abs(st_shift_cust.Z) <
+		(ACC_ST_AL_MIN * 16384 / 1000 * ST_PRECISION)  ||
+
+		abs(st_shift_cust.X) >
+		(ACC_ST_AL_MAX * 16384 / 1000 * ST_PRECISION) ||
+		abs(st_shift_cust.Y) >
+		(ACC_ST_AL_MAX * 16384 / 1000 * ST_PRECISION) ||
+		abs(st_shift_cust.Z) >
+		(ACC_ST_AL_MAX * 16384 / 1000 * ST_PRECISION)
+		) {
+			test_result = false;
+		}
+	}
+
+	return test_result;
+}
+
+static int icm20602_check_gyro_selftest(struct inv_icm20602_state *st,
+	struct X_Y_Z *gyro, struct X_Y_Z *gyro_st)
+{
+	struct X_Y_Z gyro_ST_code, st_otp, st_shift_cust;
+	bool otp_value_zero = false, test_result = true;
+
+	gyro_ST_code.X = st->config->gyro_self_test.X;
+	gyro_ST_code.Y = st->config->gyro_self_test.Y;
+	gyro_ST_code.Z = st->config->gyro_self_test.Z;
+
+	st_otp.X = (st_otp.X != 0) ? mpu_st_tb[gyro_ST_code.X - 1] : 0;
+	st_otp.Y = (st_otp.Y != 0) ? mpu_st_tb[gyro_ST_code.Y - 1] : 0;
+	st_otp.Z = (st_otp.Z != 0) ? mpu_st_tb[gyro_ST_code.Z - 1] : 0;
+
+	if (st_otp.X & st_otp.Y & st_otp.Z == 0)
+		otp_value_zero = true;
+
+	st_shift_cust.X = gyro_st->X - gyro->X;
+	st_shift_cust.Y = gyro_st->X - gyro->Y;
+	st_shift_cust.Z = gyro_st->X - gyro->Z;
+	if (!otp_value_zero) {
+		if (
+		st_shift_cust.X <
+		(st_otp.X * ST_PRECISION * GYRO_ST_SHIFT / 100) ||
+		st_shift_cust.Y <
+		(st_otp.Y * ST_PRECISION * GYRO_ST_SHIFT / 100) ||
+		st_shift_cust.Z <
+		(st_otp.Z * ST_PRECISION * GYRO_ST_SHIFT / 100)
+		) {
+			test_result = false;
+		}
+	} else {
+		if (
+		abs(st_shift_cust.X) <
+		(GYRO_ST_AL * 32768 / 250 * ST_PRECISION) ||
+		abs(st_shift_cust.Y) <
+		(GYRO_ST_AL * 32768 / 250 * ST_PRECISION) ||
+		abs(st_shift_cust.Z) <
+		(GYRO_ST_AL * 32768 / 250 * ST_PRECISION)
+		) {
+			test_result = false;
+		}
+	}
+
+	if (test_result == true) {
+		/* Self Test Pass/Fail Criteria C */
+		if (
+		abs(st_shift_cust.X) >
+		GYRO_OFFSET_MAX * 32768 / 250 * ST_PRECISION ||
+		abs(st_shift_cust.Y) >
+		GYRO_OFFSET_MAX * 32768 / 250 * ST_PRECISION ||
+		abs(st_shift_cust.Z) >
+		GYRO_OFFSET_MAX * 32768 / 250 * ST_PRECISION
+		) {
+			test_result = false;
+		}
+	}
+
+	return test_result;
+}
+
+bool icm20602_self_test(struct inv_icm20602_state *st)
+{
+	struct X_Y_Z acc, acc_st;
+	struct X_Y_Z gyro, gyro_st;
+	bool test_result = true;
+
+	icm20602_set_self_test(st);
+	icm20602_do_test_acc(st, &acc, &acc_st);
+	icm20602_do_test_gyro(st, &gyro, &gyro_st);
+	test_result = icm20602_check_acc_selftest(st, &acc, &acc_st);
+	test_result = icm20602_check_gyro_selftest(st, &gyro, &gyro_st);
+
+	return test_result;
+}
+
+static int icm20602_config_fifo(struct inv_icm20602_state *st)
+{
+	struct icm20602_user_config *config = NULL;
+
+	config = st->config;
+	if (config->fifo_enabled != true)
+		return MPU_SUCCESS;
+
+	/*
+	 * Set CONFIG.USER_SET_BIT = 0, No reason as datasheet said
+	 */
+	reg_set_20602.CONFIG.reg_u.REG.USER_SET_BIT = 0x0;
+	/*
+	 * Set CONFIG.FIFO_MODE = 1,
+	 * i.e. when FIFO is full, additional writes will
+	 * not be written to FIFO
+	 */
+	reg_set_20602.CONFIG.reg_u.REG.FIFO_MODE = 0x1;
+	if (icm20602_write_reg_simple(st, reg_set_20602.CONFIG))
+		return -MPU_FAIL;
+
+	/* reset fifo */
+	reg_set_20602.USER_CTRL.reg_u.REG.FIFO_RST = 0x1;
+	if (icm20602_write_reg_simple(st, reg_set_20602.USER_CTRL)) {
+		reg_set_20602.USER_CTRL.reg_u.REG.FIFO_RST = 0x0;
+		return -MPU_FAIL;
+	}
+	reg_set_20602.USER_CTRL.reg_u.REG.FIFO_RST = 0x0;
+
+	/* Enable FIFO on specified sensors */
+	reg_set_20602.FIFO_EN.reg_u.REG.GYRO_FIFO_EN = 0x1;
+	reg_set_20602.FIFO_EN.reg_u.REG.ACCEL_FIFO_EN = 0x1;
+	if (icm20602_write_reg_simple(st, reg_set_20602.FIFO_EN))
+		return -MPU_FAIL;
+
+	if (icm20602_config_waterlevel(st))
+		return -MPU_FAIL;
+
+	if (icm20602_start_fifo(st))
+		return -MPU_FAIL;
+
+	return MPU_SUCCESS;
+}
+
+static int icm20602_initialize_gyro(struct inv_icm20602_state *st)
+{
+	struct icm20602_user_config *config = NULL;
+	int result = MPU_SUCCESS;
+	int sample_rate;
+	uint8_t fchoice_b;
+
+	if (st == NULL)
+		return -MPU_FAIL;
+
+	/*
+	 * ICM20602 supports gyro sampling rate up to 32KHz
+	 * when fchoice_b != 0x00
+	 * In our driver, we supports up to 8KHz
+	 * thus always set fchoice_b to 0x00;
+	 */
+	config = st->config;
+	/*
+	 * SAPLRT_DIV in ICM20602_REG_SMPLRT_DIV is only used for 1kHz internal
+	 * sampling, i.e. fchoice_b in ICM20602_REG_GYRO_CONFIG is 00
+	 * and 0 < dlpf_cfg in ICM20602_REG_CONFIG < 7
+	 * SAMPLE_RATE=Internal_Sample_Rate / (1 + SMPLRT_DIV)
+	 */
+	if (config->gyro_accel_sample_rate <= ICM20602_SAMPLE_RATE_1000HZ)
+		reg_set_20602.SMPLRT_DIV.reg_u.reg =
+		ICM20602_INTERNAL_SAMPLE_RATE_HZ /
+		config->gyro_accel_sample_rate - 1;
+
+	result = icm20602_write_reg_simple(st, reg_set_20602.SMPLRT_DIV);
+
+	/* Set gyro&temperature(combine) LPF */
+	reg_set_20602.CONFIG.reg_u.REG.DLFP_CFG = config->gyro_lpf;
+	result |= icm20602_write_reg_simple(st, reg_set_20602.CONFIG);
+
+	/* Set gyro full scale range */
+	reg_set_20602.GYRO_CONFIG.reg_u.REG.FCHOICE_B = 0x0;
+	reg_set_20602.GYRO_CONFIG.reg_u.REG.FS_SEL = config->gyro_fsr;
+	result |= icm20602_write_reg_simple(st, reg_set_20602.GYRO_CONFIG);
+
+	/* Set Accel full scale range */
+	reg_set_20602.ACCEL_CONFIG.reg_u.REG.ACCEL_FS_SEL = config->acc_fsr;
+	result |= icm20602_write_reg_simple(st, reg_set_20602.ACCEL_CONFIG);
+
+	/*
+	 * Set accel LPF
+	 * Support accel sample rate up to 1KHz
+	 * thus set accel_fchoice_b to 0x00
+	 * The actual accel sample rate is 1KHz/(1+SMPLRT_DIV)
+	 */
+	reg_set_20602.ACCEL_CONFIG2.reg_u.REG.ACCEL_FCHOICE_B = 0x0;
+	reg_set_20602.ACCEL_CONFIG2.reg_u.REG.A_DLPF_CFG = config->acc_lpf;
+	result |= icm20602_write_reg_simple(st,
+				reg_set_20602.ACCEL_CONFIG2);
+
+	if (result) {
+		pr_err("icm20602 init gyro and accel failed\n");
+		return -MPU_FAIL;
+	}
+
+	return result;
+}
+
+int icm20602_set_power_itg(struct inv_icm20602_state *st, bool power_on)
+{
+	int result = MPU_SUCCESS;
+
+	if (power_on) {
+		reg_set_20602.PWR_MGMT_1.reg_u.reg = 0;
+		result = icm20602_write_reg_simple(st,
+					reg_set_20602.PWR_MGMT_1);
+	} else {
+		reg_set_20602.PWR_MGMT_1.reg_u.REG.SLEEP = 0x1;
+		result = icm20602_write_reg_simple(st,
+					reg_set_20602.PWR_MGMT_1);
+	}
+	if (result) {
+		pr_err("set power failed power %d  err %d\n",
+					power_on, result);
+		return result;
+	}
+
+	if (power_on)
+		msleep(30);
+
+	return result;
+}
+
+int icm20602_init_device(struct inv_icm20602_state *st)
+{
+	int result = MPU_SUCCESS;
+	struct icm20602_user_config *config = NULL;
+	int package_count;
+
+	config = st->config;
+	if (st == NULL || st->config == NULL) {
+		pr_err("icm20602 validate config failed\n");
+		return -MPU_FAIL;
+	}
+
+	/* turn on gyro and accel */
+	reg_set_20602.PWR_MGMT_2.reg_u.reg = 0x0;
+	result |= icm20602_write_reg_simple(st, reg_set_20602.PWR_MGMT_2);
+	msleep(30);
+
+	/* disable INT */
+	reg_set_20602.INT_ENABLE.reg_u.reg = 0x0;
+	result |= icm20602_write_reg_simple(st, reg_set_20602.INT_ENABLE);
+
+	/* disbale FIFO */
+	reg_set_20602.FIFO_EN.reg_u.reg = 0x0;
+	result |= icm20602_write_reg_simple(st, reg_set_20602.FIFO_EN);
+
+	/* reset FIFO */
+	reg_set_20602.USER_CTRL.reg_u.REG.FIFO_RST = 0x1;
+	result |= icm20602_write_reg_simple(st, reg_set_20602.USER_CTRL);
+	reg_set_20602.USER_CTRL.reg_u.REG.FIFO_RST = 0x0;
+	msleep(30);
+
+	/* init gyro and accel */
+	if (icm20602_initialize_gyro(st)) {
+		pr_err("icm20602 init device failed\n");
+		return -MPU_FAIL;
+	}
+
+	/* if FIFO enable, config FIFO */
+	if (config->fifo_enabled) {
+		if (icm20602_config_fifo(st)) {
+			pr_err("icm20602 init config fifo failed\n");
+			return -MPU_FAIL;
+		}
+	} else {
+		/* enable interrupt */
+		reg_set_20602.INT_ENABLE.reg_u.REG.DATA_RDY_INT_EN = 0x0;
+		if (icm20602_write_reg_simple(st,
+				reg_set_20602.INT_ENABLE)) {
+			pr_err("icm20602 set raw rdy failed\n");
+			return -MPU_FAIL;
+		}
+	}
+
+	/* buffer malloc */
+	package_count = config->fifo_waterlevel / ICM20602_PACKAGE_SIZE;
+
+	st->buf = kzalloc(sizeof(config->fifo_waterlevel * 2), GFP_ATOMIC);
+	if (!st->buf)
+		return -ENOMEM;
+
+	st->data_push = kcalloc(package_count,
+		sizeof(struct struct_icm20602_data), GFP_ATOMIC);
+	if (!st->data_push)
+		return -ENOMEM;
+
+	return result;
+}
+
+void icm20602_rw_test(struct inv_icm20602_state *st)
+{
+	uint8_t val = 0;
+
+	reg_set_20602.PWR_MGMT_2.reg_u.REG.STBY_ZG = 0x1;
+	icm20602_write_reg_simple(st, reg_set_20602.PWR_MGMT_2);
+	reg_set_20602.CONFIG.reg_u.REG.FIFO_MODE = 0x1;
+	icm20602_write_reg_simple(st, reg_set_20602.CONFIG);
+
+	icm20602_read_reg(st, reg_set_20602.PWR_MGMT_2.address, &val);
+	icm20602_read_reg(st, reg_set_20602.CONFIG.address, &val);
+
+}
+
+int icm20602_detect(struct inv_icm20602_state *st)
+{
+	int result = MPU_SUCCESS;
+	uint8_t retry = 0, val = 0;
+	uint8_t usr_ctrl = 0;
+
+	pr_debug("icm20602_detect\n");
+	/* reset to make sure previous state are not there */
+	reg_set_20602.PWR_MGMT_1.reg_u.REG.DEVICE_RESET = 0x1;
+	result = icm20602_write_reg_simple(st, reg_set_20602.PWR_MGMT_1);
+	if (result) {
+		pr_err("mpu write reg 0x%x value 0x%x failed\n",
+		reg_set_20602.PWR_MGMT_1.reg_u.reg,
+		reg_set_20602.PWR_MGMT_1.reg_u.REG.DEVICE_RESET);
+		return result;
+	}
+	reg_set_20602.PWR_MGMT_1.reg_u.REG.DEVICE_RESET = 0x0;
+
+	/* the power up delay */
+	msleep(30);
+
+	/* out of sleep */
+	result = icm20602_set_power_itg(st, true);
+	if (result)
+		return result;
+	/* get who am i register */
+	while (retry < 10) {
+		/* get version (expecting 0x12 for the icm20602) */
+		icm20602_read_reg(st, reg_set_20602.WHO_AM_I.address, &val);
+		if (val == ICM20602_WHO_AM_I)
+			break;
+		retry++;
+	}
+
+	if (val != ICM20602_WHO_AM_I) {
+		pr_err("detect mpu failed,whoami reg 0x%x\n", val);
+		result = -MPU_FAIL;
+	} else {
+		pr_debug("detect mpu ok,whoami reg 0x%x\n", val);
+	}
+	icm20602_rw_test(st);
+
+	return result;
+}
diff --git a/drivers/iio/imu/inv_icm20602/inv_icm20602_bsp.h b/drivers/iio/imu/inv_icm20602/inv_icm20602_bsp.h
new file mode 100644
index 0000000..9c4dbd0
--- /dev/null
+++ b/drivers/iio/imu/inv_icm20602/inv_icm20602_bsp.h
@@ -0,0 +1,978 @@
+/*
+ * Copyright (c) 2018 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.
+ */
+
+/* register high bit=1 for read */
+#define ICM20602_READ_REG(reg) (reg | 0x80)
+
+/* register high bit=0 for write */
+#define ICM20602_WRITE_REG(reg) (reg & (~0x80))
+#define ICM20602_WHO_AM_I 0x12
+#define ICM20602_INTERNAL_SAMPLE_RATE_HZ 1000
+
+#define SELFTEST_COUNT 200
+#define ST_PRECISION 1000
+#define ACC_ST_SHIFT_MAX	150
+#define ACC_ST_SHIFT_MIN	50
+#define ACC_ST_AL_MIN		225
+#define ACC_ST_AL_MAX		675
+#define GYRO_ST_SHIFT	50
+#define GYRO_ST_AL		60
+#define GYRO_OFFSET_MAX	20
+
+static const uint16_t mpu_st_tb[256] = {
+	2620, 2646, 2672, 2699, 2726, 2753, 2781, 2808,
+	2837, 2865, 2894, 2923, 2952, 2981, 3011, 3041,
+	3072, 3102, 3133, 3165, 3196, 3228, 3261, 3293,
+	3326, 3359, 3393, 3427, 3461, 3496, 3531, 3566,
+	3602, 3638, 3674, 3711, 3748, 3786, 3823, 3862,
+	3900, 3939, 3979, 4019, 4059, 4099, 4140, 4182,
+	4224, 4266, 4308, 4352, 4395, 4439, 4483, 4528,
+	4574, 4619, 4665, 4712, 4759, 4807, 4855, 4903,
+	4953, 5002, 5052, 5103, 5154, 5205, 5257, 5310,
+	5363, 5417, 5471, 5525, 5581, 5636, 5693, 5750,
+	5807, 5865, 5924, 5983, 6043, 6104, 6165, 6226,
+	6289, 6351, 6415, 6479, 6544, 6609, 6675, 6742,
+	6810, 6878, 6946, 7016, 7086, 7157, 7229, 7301,
+	7374, 7448, 7522, 7597, 7673, 7750, 7828, 7906,
+	7985, 8065, 8145, 8227, 8309, 8392, 8476, 8561,
+	8647, 8733, 8820, 8909, 8998, 9088, 9178, 9270,
+	9363, 9457, 9551, 9647, 9743, 9841, 9939, 10038,
+	10139, 10240, 10343, 10446, 10550, 10656, 10763, 10870,
+	10979, 11089, 11200, 11312, 11425, 11539, 11654, 11771,
+	11889, 12008, 12128, 12249, 12371, 12495, 12620, 12746,
+	12874, 13002, 13132, 13264, 13396, 13530, 13666, 13802,
+	13940, 14080, 14221, 14363, 14506, 14652, 14798, 14946,
+	15096, 15247, 15399, 15553, 15709, 15866, 16024, 16184,
+	16346, 16510, 16675, 16842, 17010, 17180, 17352, 17526,
+	17701, 17878, 18057, 18237, 18420, 18604, 18790, 18978,
+	19167, 19359, 19553, 19748, 19946, 20145, 20347, 20550,
+	20756, 20963, 21173, 21385, 21598, 21814, 22033, 22253,
+	22475, 22700, 22927, 23156, 23388, 23622, 23858, 24097,
+	24338, 24581, 24827, 25075, 25326, 25579, 25835, 26093,
+	26354, 26618, 26884, 27153, 27424, 27699, 27976, 28255,
+	28538, 28823, 29112, 29403, 29697, 29994, 30294, 30597,
+	30903, 31212, 31524, 31839, 32157, 32479, 32804
+};
+
+enum inv_icm20602_reg_addr {
+	ADDR_XG_OFFS_TC_H = 0x04,
+	ADDR_XG_OFFS_TC_L,
+	ADDR_YG_OFFS_TC_H = 0x07,
+	ADDR_YG_OFFS_TC_L,
+	ADDR_ZG_OFFS_TC_H = 0x0A,
+	ADDR_ZG_OFFS_TC_L,
+
+	ADDR_SELF_TEST_X_ACCEL = 0x0D,
+	ADDR_SELF_TEST_Y_ACCEL,
+	ADDR_SELF_TEST_Z_ACCEL,
+
+	ADDR_XG_OFFS_USRH = 0x13,
+	ADDR_XG_OFFS_USRL,
+	ADDR_YG_OFFS_USRH,
+	ADDR_YG_OFFS_USRL,
+	ADDR_ZG_OFFS_USRH,
+	ADDR_ZG_OFFS_USRL,
+
+	ADDR_SMPLRT_DIV,
+	ADDR_CONFIG,
+
+	ADDR_GYRO_CONFIG,
+
+	ADDR_ACCEL_CONFIG,
+	ADDR_ACCEL_CONFIG2,
+
+	ADDR_LP_MODE_CFG,
+
+	ADDR_ACCEL_WOM_X_THR = 0x20,
+	ADDR_ACCEL_WOM_Y_THR,
+	ADDR_ACCEL_WOM_Z_THR,
+	ADDR_FIFO_EN,
+
+	ADDR_FSYNC_INT = 0x36,
+	ADDR_INT_PIN_CFG,
+	ADDR_INT_ENABLE,
+	ADDR_FIFO_WM_INT_STATUS,
+	ADDR_INT_STATUS,
+
+	ADDR_ACCEL_XOUT_H,
+	ADDR_ACCEL_XOUT_L,
+	ADDR_ACCEL_YOUT_H,
+	ADDR_ACCEL_YOUT_L,
+	ADDR_ACCEL_ZOUT_H,
+	ADDR_ACCEL_ZOUT_L,
+
+	ADDR_TEMP_OUT_H,
+	ADDR_TEMP_OUT_L,
+
+	ADDR_GYRO_XOUT_H,
+	ADDR_GYRO_XOUT_L,
+	ADDR_GYRO_YOUT_H,
+	ADDR_GYRO_YOUT_L,
+	ADDR_GYRO_ZOUT_H,
+	ADDR_GYRO_ZOUT_L,
+
+	ADDR_SELF_TEST_X_GYRO = 0x50,
+	ADDR_SELF_TEST_Y_GYRO,
+	ADDR_SELF_TEST_Z_GYRO,
+
+	ADDR_FIFO_WM_TH1 = 0x60,
+	ADDR_FIFO_WM_TH2,
+
+	ADDR_SIGNAL_PATH_RESET = 0x68,
+	ADDR_ACCEL_INTEL_CTRL,
+	ADDR_USER_CTRL,
+
+	ADDR_PWR_MGMT_1,
+	ADDR_PWR_MGMT_2,
+
+	ADDR_I2C_IF = 0x70,
+
+	ADDR_FIFO_COUNTH = 0x72,
+	ADDR_FIFO_COUNTL,
+	ADDR_FIFO_R_W,
+
+	ADDR_WHO_AM_I,
+
+	ADDR_XA_OFFSET_H,
+	ADDR_XA_OFFSET_L,
+	ADDR_YA_OFFSET_H = 0x7A,
+	ADDR_YA_OFFSET_L,
+	ADDR_ZA_OFFSET_H = 0x7D,
+	ADDR_ZA_OFFSET_L
+};
+
+struct struct_XG_OFFS_TC_H {
+	enum inv_icm20602_reg_addr address;
+	union {
+		struct {
+			u8 REG_XG_OFFS_TC_H :2;
+			u8 REG_XG_OFFS_LP	:6;
+		} REG;
+		u8 reg;
+	} reg_u;
+};
+
+struct struct_XG_OFFS_TC_L {
+	enum inv_icm20602_reg_addr address;
+	union {
+		struct {
+			u8 REG_XG_OFFS_TC_L	:8;
+		} REG;
+		u8 reg;
+	} reg_u;
+};
+
+struct struct_YG_OFFS_TC_H {
+	enum inv_icm20602_reg_addr address;
+	union {
+		struct {
+			u8 REG_YG_OFFS_TC_H	:2;
+			u8 REG_YG_OFFS_LP	:6;
+		} REG;
+		u8 reg;
+	} reg_u;
+};
+
+struct struct_YG_OFFS_TC_L {
+	enum inv_icm20602_reg_addr address;
+	union {
+		struct {
+			u8 REG_YG_OFFS_TC_L :8;
+		} REG;
+		u8 reg;
+	} reg_u;
+};
+
+struct struct_ZG_OFFS_TC_H {
+	enum inv_icm20602_reg_addr address;
+	union {
+		struct {
+			u8 REG_ZG_OFFS_TC_H :2;
+			u8 REG_ZG_OFFS_LP	:6;
+		} REG;
+		u8 reg;
+	} reg_u;
+};
+
+struct struct_ZG_OFFS_TC_L {
+	enum inv_icm20602_reg_addr address;
+	union {
+		struct {
+			u8 REG_ZG_OFFS_TC_L :8;
+		} REG;
+		u8 reg;
+	} reg_u;
+};
+
+struct struct_SELF_TEST_X_ACCEL {
+	enum inv_icm20602_reg_addr address;
+	union {
+		struct {
+			u8 XA_ST_DATA	:8;
+		} REG;
+		u8 reg;
+	} reg_u;
+};
+
+struct struct_SELF_TEST_Y_ACCEL {
+	enum inv_icm20602_reg_addr address;
+	union {
+		struct {
+			u8 YA_ST_DATA	:8;
+		} REG;
+		u8 reg;
+	} reg_u;
+};
+
+struct struct_SELF_TEST_Z_ACCEL {
+	enum inv_icm20602_reg_addr address;
+	union {
+		struct {
+			u8 ZA_ST_DATA	:8;
+		} REG;
+		u8 reg;
+	} reg_u;
+};
+
+struct struct_XG_OFFS_USRH {
+	enum inv_icm20602_reg_addr address;
+	union {
+		struct {
+			u8 X_OFFS_USR	:8;
+		} REG;
+		u8 reg;
+	} reg_u;
+};
+
+struct struct_XG_OFFS_USRL {
+	enum inv_icm20602_reg_addr address;
+	union {
+		struct {
+			u8 X_OFFS_USR	:8;
+		} REG;
+		u8 reg;
+	} reg_u;
+};
+
+struct struct_YG_OFFS_USRH {
+	enum inv_icm20602_reg_addr address;
+	union {
+		struct {
+			u8 Y_OFFS_USR	:8;
+		} REG;
+		u8 reg;
+	} reg_u;
+};
+
+struct struct_YG_OFFS_USRL {
+	enum inv_icm20602_reg_addr address;
+	union {
+		struct {
+			u8 Y_OFFS_USR	:8;
+		} REG;
+		u8 reg;
+	} reg_u;
+};
+
+struct struct_ZG_OFFS_USRH {
+	enum inv_icm20602_reg_addr address;
+	union {
+		struct {
+			u8 Z_OFFS_USR	:8;
+		} REG;
+		u8 reg;
+	} reg_u;
+};
+
+struct struct_ZG_OFFS_USRL {
+	enum inv_icm20602_reg_addr address;
+	union {
+		struct {
+			u8 Z_OFFS_USR	:8;
+		} REG;
+		u8 reg;
+	} reg_u;
+};
+
+struct struct_SMPLRT_DIV {
+	enum inv_icm20602_reg_addr address;
+	union {
+		struct {
+			u8 SMPLRT_DIV	:8;
+		} REG;
+		u8 reg;
+	} reg_u;
+};
+
+struct struct_CONFIG {
+	enum inv_icm20602_reg_addr address;
+	union {
+		struct {
+			u8 DLFP_CFG		:3;
+			u8 EXT_SYNC_SET	:3;
+			u8 FIFO_MODE	:1;
+			u8 USER_SET_BIT	:1;
+		} REG;
+		u8 reg;
+	} reg_u;
+};
+
+struct struct_GYRO_CONFIG {
+	enum inv_icm20602_reg_addr address;
+	union {
+		struct {
+			u8 FCHOICE_B	:2;
+			u8 RESERVE0		:1;
+			u8 FS_SEL		:2;
+			u8 ZG_ST		:1;
+			u8 YG_ST		:1;
+			u8 XG_ST		:1;
+		} REG;
+		u8 reg;
+	} reg_u;
+};
+
+struct struct_ACCEL_CONFIG {
+	enum inv_icm20602_reg_addr address;
+	union {
+		struct {
+			u8 RESERVE0			:3;
+			u8 ACCEL_FS_SEL		:2;
+			u8 ZG_ST			:1;
+			u8 YG_ST			:1;
+			u8 XG_ST			:1;
+		} REG;
+		u8 reg;
+	} reg_u;
+};
+
+struct struct_ACCEL_CONFIG2 {
+	enum inv_icm20602_reg_addr address;
+	union {
+		struct {
+			u8 A_DLPF_CFG		:3;
+			u8 ACCEL_FCHOICE_B	:1;
+			u8 DEC2_CFG			:2;
+			u8 RESERVE0			:2;
+		} REG;
+		u8 reg;
+	} reg_u;
+};
+
+struct struct_LP_MODE_CFG {
+	enum inv_icm20602_reg_addr address;
+	union {
+		struct {
+			u8 RESERVE0			:4;
+			u8 G_AVGCFG			:3;
+			u8 GYRO_CYCLE		:1;
+		} REG;
+		u8 reg;
+	} reg_u;
+};
+
+struct struct_ACCEL_WOM_X_THR {
+	enum inv_icm20602_reg_addr address;
+	union {
+		struct {
+			u8 WOM_X_TH			:8;
+		} REG;
+		u8 reg;
+	} reg_u;
+};
+
+
+struct struct_ACCEL_WOM_Y_THR {
+	enum inv_icm20602_reg_addr address;
+	union {
+		struct {
+			u8 WOM_Y_TH			:8;
+		} REG;
+		u8 reg;
+	} reg_u;
+};
+
+struct struct_ACCEL_WOM_Z_THR {
+	enum inv_icm20602_reg_addr address;
+	union {
+		struct {
+			u8 WOM_Z_TH			:8;
+		} REG;
+		u8 reg;
+	} reg_u;
+};
+
+struct struct_FIFO_EN {
+	enum inv_icm20602_reg_addr address;
+	union {
+		struct {
+			u8 RESERVE1			:3;
+			u8 ACCEL_FIFO_EN	:1;
+			u8 GYRO_FIFO_EN		:1;
+			u8 RESERVE0			:3;
+		} REG;
+		u8 reg;
+	} reg_u;
+};
+
+struct struct_FSYNC_INT {
+	enum inv_icm20602_reg_addr address;
+	union {
+		struct {
+			u8 RESERVE0			:7;
+			u8 FSYNC_INT		:1;
+		} REG;
+		u8 reg;
+	} reg_u;
+};
+
+struct struct_INT_PIN_CFG {
+	enum inv_icm20602_reg_addr address;
+	union {
+		struct {
+			u8 RESERVE0			:2;
+			u8 FSYNC_INT_MODE_EN:1;
+			u8 FSYNC_INT_LEVEL	:1;
+			u8 INT_RD_CLEAR		:1;
+			u8 LATCH_INT_EN		:1;
+			u8 INT_OPEN			:1;
+			u8 INT_LEVEL		:1;
+		} REG;
+		u8 reg;
+	} reg_u;
+};
+
+struct struct_INT_ENABLE {
+	enum inv_icm20602_reg_addr address;
+	union {
+		struct {
+			u8 DATA_RDY_INT_EN	:1;
+			u8 RESERVE0			:1;
+			u8 GDRIVE_INT_EN	:1;
+			u8 FSYNC_INT_EN		:1;
+			u8 FIFO_OFLOW_EN	:1;
+			u8 WOM_Z_INT_EN		:1;
+			u8 WOM_Y_INT_EN		:1;
+			u8 WOM_X_INT_EN		:1;
+		} REG;
+		u8 reg;
+	} reg_u;
+};
+
+struct struct_FIFO_WM_INT_STATUS {
+	enum inv_icm20602_reg_addr address;
+	union {
+		struct {
+			u8 RESERVE1			:6;
+			u8 FIFO_WM_INT		:1;
+			u8 RESERVE0			:1;
+		} REG;
+		u8 reg;
+	} reg_u;
+};
+
+struct struct_INT_STATUS {
+	enum inv_icm20602_reg_addr address;
+	union {
+		struct {
+			u8 DATA_RDY_INT		:1;
+			u8 RESERVE1			:1;
+			u8 GDRIVE_INT		:1;
+			u8 RESERVE0			:1;
+			u8 FIFO_OFLOW_INT	:1;
+			u8 WOM_Z_INT		:1;
+			u8 WOM_Y_INT		:1;
+			u8 WOM_X_INT		:1;
+		} REG;
+		u8 reg;
+	} reg_u;
+};
+
+struct struct_ACCEL_XOUT_H {
+	enum inv_icm20602_reg_addr address;
+	union {
+		struct {
+			u8 ACCEL_XOUT		:8;
+		} REG;
+		u8 reg;
+	} reg_u;
+};
+
+struct struct_ACCEL_XOUT_L {
+	enum inv_icm20602_reg_addr address;
+	union {
+		struct {
+			u8 ACCEL_XOUT		:8;
+		} REG;
+		u8 reg;
+	} reg_u;
+};
+
+struct struct_ACCEL_YOUT_H {
+	enum inv_icm20602_reg_addr address;
+	union {
+		struct {
+			u8 ACCEL_YOUT		:8;
+		} REG;
+		u8 reg;
+	} reg_u;
+};
+
+struct struct_ACCEL_YOUT_L {
+	enum inv_icm20602_reg_addr address;
+	union {
+		struct {
+			u8 ACCEL_YOUT		:8;
+		} REG;
+		u8 reg;
+	} reg_u;
+};
+
+struct struct_ACCEL_ZOUT_H {
+	enum inv_icm20602_reg_addr address;
+	union {
+		struct {
+			u8 ACCEL_ZOUT		:8;
+		} REG;
+		u8 reg;
+	} reg_u;
+};
+
+struct struct_ACCEL_ZOUT_L {
+	enum inv_icm20602_reg_addr address;
+	union {
+		struct {
+			u8 ACCEL_ZOUT		:8;
+		} REG;
+		u8 reg;
+	} reg_u;
+};
+
+struct struct_TEMP_OUT_H {
+	enum inv_icm20602_reg_addr address;
+	union {
+		struct {
+			u8 TEMP_OUT			:8;
+		} REG;
+		u8 reg;
+	} reg_u;
+};
+
+struct struct_TEMP_OUT_L {
+	enum inv_icm20602_reg_addr address;
+	union {
+		struct {
+			u8 TEMP_OUT			:8;
+		} REG;
+		u8 reg;
+	} reg_u;
+};
+
+struct struct_GYRO_XOUT_H {
+	enum inv_icm20602_reg_addr address;
+	union {
+		struct {
+			u8 GYRO_XOUT		:8;
+		} REG;
+		u8 reg;
+	} reg_u;
+};
+
+struct struct_GYRO_XOUT_L {
+	enum inv_icm20602_reg_addr address;
+	union {
+		struct {
+			u8 GYRO_XOUT		:8;
+		} REG;
+		u8 reg;
+	} reg_u;
+};
+
+struct struct_GYRO_YOUT_H {
+	enum inv_icm20602_reg_addr address;
+	union {
+		struct {
+			u8 GYRO_YOUT		:8;
+		} REG;
+		u8 reg;
+	} reg_u;
+};
+
+struct struct_GYRO_YOUT_L {
+	enum inv_icm20602_reg_addr address;
+	union {
+		struct {
+			u8 GYRO_YOUT		:8;
+		} REG;
+		u8 reg;
+	} reg_u;
+};
+
+struct struct_GYRO_ZOUT_H {
+	enum inv_icm20602_reg_addr address;
+	union {
+		struct {
+			u8 GYRO_ZOUT		:8;
+		} REG;
+		u8 reg;
+	} reg_u;
+};
+
+struct struct_GYRO_ZOUT_L {
+	enum inv_icm20602_reg_addr address;
+	union {
+		struct {
+			u8 GYRO_ZOUT		:8;
+		} REG;
+		u8 reg;
+	} reg_u;
+};
+
+struct struct_SELF_TEST_X_GYRO {
+	enum inv_icm20602_reg_addr address;
+	union {
+		struct {
+			u8 XG_ST_DATA		:8;
+		} REG;
+		u8 reg;
+	} reg_u;
+};
+
+struct struct_SELF_TEST_Y_GYRO {
+	enum inv_icm20602_reg_addr address;
+	union {
+		struct {
+			u8 YG_ST_DATA		:8;
+		} REG;
+		u8 reg;
+	} reg_u;
+};
+
+struct struct_SELF_TEST_Z_GYRO {
+	enum inv_icm20602_reg_addr address;
+	union {
+		struct {
+			u8 ZG_ST_DATA		:8;
+		} REG;
+		u8 reg;
+	} reg_u;
+};
+
+struct struct_FIFO_WM_TH1 {
+	enum inv_icm20602_reg_addr address;
+	union {
+		struct {
+			u8 RESERVE0			:6;
+			u8 FIFO_WM_TH		:2;
+		} REG;
+		u8 reg;
+	} reg_u;
+};
+
+struct struct_FIFO_WM_TH2 {
+	enum inv_icm20602_reg_addr address;
+	union {
+		struct {
+			u8 FIFO_WM_TH		:8;
+		} REG;
+		u8 reg;
+	} reg_u;
+};
+
+struct struct_SIGNAL_PATH_RESET {
+	enum inv_icm20602_reg_addr address;
+	union {
+		struct {
+			u8 TEMP_RST			:1;
+			u8 ACCEL_RST		:1;
+			u8 FIFO_WM_TH		:6;
+		} REG;
+		u8 reg;
+	} reg_u;
+};
+
+struct struct_ACCEL_INTEL_CTRL {
+	enum inv_icm20602_reg_addr address;
+	union {
+		struct {
+			u8 WOM_TH_MODE		:1;
+			u8 OUTPUT_LIMIT		:1;
+			u8 RESERVE0			:4;
+			u8 ACCEL_INTEL_MODE	:1;
+			u8 ACCEL_INTEL_EN	:1;
+		} REG;
+		u8 reg;
+	} reg_u;
+};
+
+struct struct_USER_CTRL {
+	enum inv_icm20602_reg_addr address;
+	union {
+		struct {
+			u8 SIG_COND_RST		:1;
+			u8 RESERVE2			:1;
+			u8 FIFO_RST			:1;
+			u8 RESERVE1			:3;
+			u8 FIFO_EN			:1;
+			u8 RESERVE0			:1;
+		} REG;
+		u8 reg;
+	} reg_u;
+};
+
+struct struct_PWR_MGMT_1 {
+	enum inv_icm20602_reg_addr address;
+	union {
+		struct {
+			u8 CLKSEL			:3;
+			u8 TEMP_DIS			:1;
+			u8 GYRO_STANDBY		:1;
+			u8 CYCLE			:1;
+			u8 SLEEP			:1;
+			u8 DEVICE_RESET		:1;
+		} REG;
+		u8 reg;
+	} reg_u;
+};
+
+struct struct_PWR_MGMT_2 {
+	enum inv_icm20602_reg_addr address;
+	union {
+		struct {
+			u8 STBY_ZG			:1;
+			u8 STBY_YG			:1;
+			u8 STBY_XG			:1;
+			u8 STBY_ZA			:1;
+			u8 STBY_YA			:1;
+			u8 STBY_XA			:1;
+			u8 RESERVE0			:2;
+		} REG;
+		u8 reg;
+	} reg_u;
+};
+
+struct struct_I2C_IF {
+	enum inv_icm20602_reg_addr address;
+	union {
+		struct {
+			u8 RESERVE1			:6;
+			u8 I2C_IF_DIS		:1;
+			u8 RESERVE0			:1;
+		} REG;
+		u8 reg;
+	} reg_u;
+};
+
+struct struct_FIFO_COUNTH {
+	enum inv_icm20602_reg_addr address;
+	union {
+		struct {
+			u8 FIFO_COUNT		:8;
+		} REG;
+		u8 reg;
+	} reg_u;
+};
+
+struct struct_FIFO_COUNTL {
+	enum inv_icm20602_reg_addr address;
+	union {
+		struct {
+			u8 FIFO_COUNT		:8;
+		} REG;
+		u8 reg;
+	} reg_u;
+};
+
+struct struct_FIFO_R_W {
+	enum inv_icm20602_reg_addr address;
+	union {
+		struct {
+			u8 FIFO_DATA		:8;
+		} REG;
+		u8 reg;
+	} reg_u;
+};
+
+struct struct_WHO_AM_I {
+	enum inv_icm20602_reg_addr address;
+	union {
+		struct {
+			u8 WHOAMI			:8;
+		} REG;
+		u8 reg;
+	} reg_u;
+};
+
+struct struct_XA_OFFSET_H {
+	enum inv_icm20602_reg_addr address;
+	union {
+		struct {
+			u8 XA_OFFS			:8;
+		} REG;
+		u8 reg;
+	} reg_u;
+};
+
+struct struct_XA_OFFSET_L {
+	enum inv_icm20602_reg_addr address;
+	union {
+		struct {
+			u8 XA_OFFS			:8;
+		} REG;
+		u8 reg;
+	} reg_u;
+};
+
+struct struct_YA_OFFSET_H {
+	enum inv_icm20602_reg_addr address;
+	union {
+		struct {
+			u8 YA_OFFS			:8;
+		} REG;
+		u8 reg;
+	} reg_u;
+};
+
+struct struct_YA_OFFSET_L {
+	enum inv_icm20602_reg_addr address;
+	union {
+		struct {
+			u8 YA_OFFS			:8;
+		} REG;
+		u8 reg;
+	} reg_u;
+};
+
+struct struct_ZA_OFFSET_H {
+	enum inv_icm20602_reg_addr address;
+	union {
+		struct {
+			u8 ZA_OFFS			:8;
+		} REG;
+		u8 reg;
+	} reg_u;
+};
+
+struct struct_ZA_OFFSET_L {
+	enum inv_icm20602_reg_addr address;
+	union {
+		struct {
+			u8 ZA_OFFS			:8;
+		} REG;
+		u8 reg;
+	} reg_u;
+};
+
+/*
+ *  struct inv_icm20602_reg_map - Notable registers.
+ *  @sample_rate_div:	Divider applied to gyro output rate.
+ *  @lpf:		Configures internal low pass filter.
+ *  @user_ctrl:		Enables/resets the FIFO.
+ *  @fifo_en:		Determines which data will appear in FIFO.
+ *  @gyro_config:	gyro config register.
+ *  @accl_config:	accel config register
+ *  @fifo_count_h:	Upper byte of FIFO count.
+ *  @fifo_r_w:		FIFO register.
+ *  @raw_gyro:		Address of first gyro register.
+ *  @raw_accl:		Address of first accel register.
+ *  @temperature:	temperature register
+ *  @int_enable:	Interrupt enable register.
+ *  @pwr_mgmt_1:	Controls chip's power state and clock source.
+ *  @pwr_mgmt_2:	Controls power state of individual sensors.
+ */
+struct inv_icm20602_reg_map {
+	struct struct_XG_OFFS_TC_H XG_OFFS_TC_H;
+	struct struct_XG_OFFS_TC_L XG_OFFS_TC_L;
+	struct struct_YG_OFFS_TC_H YG_OFFS_TC_H;
+	struct struct_YG_OFFS_TC_L YG_OFFS_TC_L;
+	struct struct_ZG_OFFS_TC_H ZG_OFFS_TC_H;
+	struct struct_ZG_OFFS_TC_L ZG_OFFS_TC_L;
+
+	struct struct_SELF_TEST_X_ACCEL SELF_TEST_X_ACCEL;
+	struct struct_SELF_TEST_Y_ACCEL SELF_TEST_Y_ACCEL;
+	struct struct_SELF_TEST_Z_ACCEL SELF_TEST_Z_ACCEL;
+
+	struct struct_XG_OFFS_USRH XG_OFFS_USRH;
+	struct struct_XG_OFFS_USRL XG_OFFS_USRL;
+	struct struct_YG_OFFS_USRH YG_OFFS_USRH;
+	struct struct_YG_OFFS_USRL YG_OFFS_USRL;
+	struct struct_ZG_OFFS_USRH ZG_OFFS_USRH;
+	struct struct_ZG_OFFS_USRL ZG_OFFS_USRL;
+
+	struct struct_SMPLRT_DIV SMPLRT_DIV;
+	struct struct_CONFIG CONFIG;
+
+	struct struct_GYRO_CONFIG GYRO_CONFIG;
+
+	struct struct_ACCEL_CONFIG ACCEL_CONFIG;
+	struct struct_ACCEL_CONFIG2 ACCEL_CONFIG2;
+
+	struct struct_LP_MODE_CFG LP_MODE_CFG;
+
+	struct struct_ACCEL_WOM_X_THR ACCEL_WOM_X_THR;
+	struct struct_ACCEL_WOM_Y_THR ACCEL_WOM_Y_THR;
+	struct struct_ACCEL_WOM_Z_THR ACCEL_WOM_Z_THR;
+
+	struct struct_FIFO_EN FIFO_EN;
+	struct struct_FSYNC_INT FSYNC_INT;
+	struct struct_INT_PIN_CFG INT_PIN_CFG;
+	struct struct_INT_ENABLE INT_ENABLE;
+	struct struct_FIFO_WM_INT_STATUS FIFO_WM_INT_STATUS;
+	struct struct_INT_STATUS INT_STATUS;
+
+	struct struct_ACCEL_XOUT_H ACCEL_XOUT_H;
+	struct struct_ACCEL_XOUT_L ACCEL_XOUT_L;
+	struct struct_ACCEL_YOUT_H ACCEL_YOUT_H;
+	struct struct_ACCEL_YOUT_L ACCEL_YOUT_L;
+	struct struct_ACCEL_ZOUT_H ACCEL_ZOUT_H;
+	struct struct_ACCEL_ZOUT_L ACCEL_ZOUT_L;
+
+	struct struct_TEMP_OUT_H TEMP_OUT_H;
+	struct struct_TEMP_OUT_L TEMP_OUT_L;
+
+	struct struct_GYRO_XOUT_H GYRO_XOUT_H;
+	struct struct_GYRO_XOUT_L GYRO_XOUT_L;
+	struct struct_GYRO_YOUT_H GYRO_YOUT_H;
+	struct struct_GYRO_YOUT_L GYRO_YOUT_L;
+	struct struct_GYRO_ZOUT_H GYRO_ZOUT_H;
+	struct struct_GYRO_ZOUT_L GYRO_ZOUT_L;
+
+	struct struct_SELF_TEST_X_GYRO SELF_TEST_X_GYRO;
+	struct struct_SELF_TEST_Y_GYRO SELF_TEST_Y_GYRO;
+	struct struct_SELF_TEST_Z_GYRO SELF_TEST_Z_GYRO;
+	struct struct_FIFO_WM_TH1 FIFO_WM_TH1;
+	struct struct_FIFO_WM_TH2 FIFO_WM_TH2;
+	struct struct_SIGNAL_PATH_RESET SIGNAL_PATH_RESET;
+	struct struct_ACCEL_INTEL_CTRL ACCEL_INTEL_CTRL;
+	struct struct_USER_CTRL USER_CTRL;
+
+	struct struct_PWR_MGMT_1 PWR_MGMT_1;
+	struct struct_PWR_MGMT_2 PWR_MGMT_2;
+
+	struct struct_I2C_IF I2C_IF;
+
+	struct struct_FIFO_COUNTH FIFO_COUNTH;
+	struct struct_FIFO_COUNTL FIFO_COUNTL;
+	struct struct_FIFO_R_W FIFO_R_W;
+
+	struct struct_WHO_AM_I WHO_AM_I;
+
+	struct struct_XA_OFFSET_H XA_OFFSET_H;
+	struct struct_XA_OFFSET_L XA_OFFSET_L;
+	struct struct_YA_OFFSET_H YA_OFFSET_H;
+	struct struct_YA_OFFSET_L YA_OFFSET_L;
+	struct struct_ZA_OFFSET_H ZA_OFFSET_H;
+	struct struct_ZA_OFFSET_L ZA_OFFSET_L;
+};
+
+
diff --git a/drivers/iio/imu/inv_icm20602/inv_icm20602_core.c b/drivers/iio/imu/inv_icm20602/inv_icm20602_core.c
new file mode 100644
index 0000000..0f7fc92
--- /dev/null
+++ b/drivers/iio/imu/inv_icm20602/inv_icm20602_core.c
@@ -0,0 +1,547 @@
+/*
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * Copyright (C) 2012 Invensense, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/sysfs.h>
+#include <linux/jiffies.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/kfifo.h>
+#include <linux/spinlock.h>
+#include <linux/spi/spi.h>
+#include <linux/i2c.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include "inv_icm20602_iio.h"
+
+/* Attribute of icm20602 device init show */
+static ssize_t inv_icm20602_init_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	return 0;
+}
+
+static void inv_icm20602_def_config(struct inv_icm20602_state *st)
+{
+	struct icm20602_user_config *config = st->config;
+
+	config->user_fps_in_ms = 10;
+	config->gyro_lpf = INV_ICM20602_GYRO_LFP_92HZ;
+	config->gyro_fsr = ICM20602_GYRO_FSR_1000DPS;
+	config->acc_lpf = ICM20602_ACCLFP_99;
+	config->acc_fsr = ICM20602_ACC_FSR_4G;
+	config->gyro_accel_sample_rate = ICM20602_SAMPLE_RATE_100HZ;
+	config->fifo_enabled = true;
+
+}
+
+static void inv_icm20602_load_config(struct inv_icm20602_state *st)
+{
+	struct icm20602_user_config *config = st->config;
+
+	if (config->user_fps_in_ms == 0)
+		inv_icm20602_def_config(st);
+	config->fifo_enabled = true;
+
+}
+
+static ssize_t inv_icm20602_init_store(struct device *dev,
+	struct device_attribute *attr, const char *buf, size_t count)
+{
+	int result = MPU_SUCCESS;
+	struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+	struct inv_icm20602_state *st = iio_priv(indio_dev);
+
+	inv_icm20602_load_config(st);
+	result |= icm20602_detect(st);
+	result |= icm20602_init_device(st);
+	icm20602_start_fifo(st);
+	if (result)
+		return result;
+
+	return count;
+}
+
+static IIO_DEVICE_ATTR(
+						inv_icm20602_init,
+						0644,
+						inv_icm20602_init_show,
+						inv_icm20602_init_store,
+						0);
+
+/* Attribute of gyro lpf base on the enum inv_icm20602_gyro_temp_lpf_e */
+static ssize_t inv_gyro_lpf_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	return 0;
+}
+
+static ssize_t inv_gyro_lpf_store(struct device *dev,
+	struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+	struct inv_icm20602_state *st = iio_priv(indio_dev);
+	struct icm20602_user_config *config = st->config;
+	int gyro_lpf;
+
+	if (kstrtoint(buf, 10, &gyro_lpf))
+		return -EINVAL;
+	if (gyro_lpf > INV_ICM20602_GYRO_LFP_NUM)
+		return -EINVAL;
+	config->gyro_lpf = gyro_lpf;
+	return count;
+}
+static IIO_DEVICE_ATTR(
+						inv_icm20602_gyro_lpf,
+						0644,
+						inv_gyro_lpf_show,
+						inv_gyro_lpf_store,
+						0);
+
+/* Attribute of gyro fsr base on enum inv_icm20602_gyro_temp_lpf_e */
+static ssize_t inv_gyro_fsr_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	return 0;
+}
+
+static ssize_t inv_gyro_fsr_store(struct device *dev,
+	struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+	struct inv_icm20602_state *st = iio_priv(indio_dev);
+	struct icm20602_user_config *config = st->config;
+	int gyro_fsr;
+
+	if (kstrtoint(buf, 10, &gyro_fsr))
+		return -EINVAL;
+	if (gyro_fsr > ICM20602_GYRO_FSR_NUM)
+		return -EINVAL;
+
+	config->gyro_fsr = gyro_fsr;
+	return count;
+}
+
+static IIO_DEVICE_ATTR(
+						inv_icm20602_gyro_fsr,
+						0644,
+						inv_gyro_fsr_show,
+						inv_gyro_fsr_store,
+						0);
+
+/* Attribute of gyro_self_test */
+static ssize_t inv_gyro_self_test_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	return 0;
+}
+
+static ssize_t inv_gyro_self_test_store(struct device *dev,
+	struct device_attribute *attr, const char *buf, size_t count)
+{
+	return 0;
+}
+static IIO_DEVICE_ATTR(
+						inv_icm20602_gyro_self_test,
+						0644,
+						inv_gyro_self_test_show,
+						inv_gyro_self_test_store,
+						0);
+
+/* Attribute of gyro fsr base on enum inv_icm20602_acc_fsr_e */
+static ssize_t inv_gyro_acc_fsr_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	return 0;
+}
+
+static ssize_t inv_gyro_acc_fsr_store(struct device *dev,
+	struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+	struct inv_icm20602_state *st = iio_priv(indio_dev);
+	struct icm20602_user_config *config = st->config;
+	int acc_fsr;
+
+	if (kstrtoint(buf, 10, &acc_fsr))
+		return -EINVAL;
+	if (acc_fsr > ICM20602_ACC_FSR_NUM)
+		return -EINVAL;
+
+	config->acc_fsr = acc_fsr;
+	return count;
+}
+static IIO_DEVICE_ATTR(
+						inv_icm20602_acc_fsr,
+						0644,
+						inv_gyro_acc_fsr_show,
+						inv_gyro_acc_fsr_store,
+						0);
+
+/* Attribute of gyro fsr base on enum inv_icm20602_acc_lpf_e */
+static ssize_t inv_gyro_acc_lpf_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	return 0;
+}
+
+static ssize_t inv_gyro_acc_lpf_store(struct device *dev,
+	struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+	struct inv_icm20602_state *st = iio_priv(indio_dev);
+	struct icm20602_user_config *config = st->config;
+	int acc_lpf;
+
+	if (kstrtoint(buf, 10, &acc_lpf))
+		return -EINVAL;
+	if (acc_lpf > ICM20602_ACCLPF_NUM)
+		return -EINVAL;
+
+	config->acc_fsr = acc_lpf;
+	return count;
+}
+static IIO_DEVICE_ATTR(
+						inv_icm20602_acc_lpf,
+						0644,
+						inv_gyro_acc_lpf_show,
+						inv_gyro_acc_lpf_store,
+						0);
+
+/* Attribute of acc_self_test */
+static ssize_t inv_acc_self_test_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	return 0;
+}
+
+static ssize_t inv_acc_self_test_store(struct device *dev,
+	struct device_attribute *attr, const char *buf, size_t count)
+{
+	return 0;
+}
+static IIO_DEVICE_ATTR(
+						inv_icm20602_acc_self_test,
+						0644,
+						inv_acc_self_test_show,
+						inv_acc_self_test_store,
+						0);
+
+/* Attribute of user_fps_in_ms */
+static ssize_t inv_user_fps_in_ms_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	return 0;
+}
+
+static ssize_t inv_user_fps_in_ms_store(struct device *dev,
+	struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+	struct inv_icm20602_state *st = iio_priv(indio_dev);
+	struct icm20602_user_config *config = st->config;
+	int user_fps_in_ms;
+
+	if (kstrtoint(buf, 10, &user_fps_in_ms))
+		return -EINVAL;
+	if (user_fps_in_ms < 10)
+		return -EINVAL;
+
+	config->user_fps_in_ms = user_fps_in_ms;
+	return count;
+}
+static IIO_DEVICE_ATTR(
+						inv_user_fps_in_ms,
+						0644,
+						inv_user_fps_in_ms_show,
+						inv_user_fps_in_ms_store,
+						0);
+
+/* Attribute of gyro_accel_sample_rate */
+static ssize_t inv_sampling_frequency_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	return 0;
+}
+
+static ssize_t inv_sampling_frequency_store(struct device *dev,
+	struct device_attribute *attr, const char *buf, size_t count)
+{
+	return 0;
+}
+static IIO_DEV_ATTR_SAMP_FREQ(
+						0644,
+						inv_sampling_frequency_show,
+						inv_sampling_frequency_store);
+
+static struct attribute *inv_icm20602_attributes[] = {
+	&iio_dev_attr_inv_icm20602_init.dev_attr.attr,
+
+	&iio_dev_attr_inv_icm20602_gyro_self_test.dev_attr.attr,
+	&iio_dev_attr_inv_icm20602_gyro_fsr.dev_attr.attr,
+	&iio_dev_attr_inv_icm20602_gyro_lpf.dev_attr.attr,
+
+	&iio_dev_attr_inv_icm20602_acc_self_test.dev_attr.attr,
+	&iio_dev_attr_inv_icm20602_acc_fsr.dev_attr.attr,
+	&iio_dev_attr_inv_icm20602_acc_lpf.dev_attr.attr,
+
+	&iio_dev_attr_sampling_frequency.dev_attr.attr,
+
+	&iio_dev_attr_inv_user_fps_in_ms.dev_attr.attr,
+	NULL,
+};
+
+#define INV_ICM20602_CHAN(_type, _channel2, _index)                    \
+{                                                             \
+	.type = _type,                                        \
+	.modified = 1,                                        \
+	.channel2 = _channel2,                                \
+	.info_mask_shared_by_type =  BIT(IIO_CHAN_INFO_SCALE), \
+	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),         \
+	.scan_index = _index,                                 \
+	.scan_type = {                                        \
+			.sign = 's',                          \
+			.realbits = 16,                       \
+			.storagebits = 16,                    \
+			.shift = 0,                          \
+			.endianness = IIO_BE,                 \
+	},                                       \
+}
+
+static const struct iio_chan_spec inv_icm20602_channels[] = {
+	IIO_CHAN_SOFT_TIMESTAMP(INV_ICM20602_SCAN_TIMESTAMP),
+
+	{
+		.type = IIO_TEMP,
+		.info_mask_separate =  BIT(IIO_CHAN_INFO_RAW)
+				| BIT(IIO_CHAN_INFO_OFFSET)
+				| BIT(IIO_CHAN_INFO_SCALE),
+		.scan_index = INV_ICM20602_SCAN_TEMP,
+		.channel2 = IIO_MOD_X,
+		.scan_type = {
+				.sign = 's',
+				.realbits = 16,
+				.storagebits = 16,
+				.shift = 0,
+				.endianness = IIO_BE,
+			     },
+	},
+	INV_ICM20602_CHAN(IIO_ANGL_VEL, IIO_MOD_X, INV_ICM20602_SCAN_GYRO_X),
+	INV_ICM20602_CHAN(IIO_ANGL_VEL, IIO_MOD_Y, INV_ICM20602_SCAN_GYRO_Y),
+	INV_ICM20602_CHAN(IIO_ANGL_VEL, IIO_MOD_Z, INV_ICM20602_SCAN_GYRO_Z),
+
+	INV_ICM20602_CHAN(IIO_ACCEL, IIO_MOD_X, INV_ICM20602_SCAN_ACCL_X),
+	INV_ICM20602_CHAN(IIO_ACCEL, IIO_MOD_Y, INV_ICM20602_SCAN_ACCL_Y),
+	INV_ICM20602_CHAN(IIO_ACCEL, IIO_MOD_Z, INV_ICM20602_SCAN_ACCL_Z),
+};
+
+static const struct attribute_group inv_icm20602_attribute_group = {
+	.attrs = inv_icm20602_attributes,
+};
+
+static const struct iio_info icm20602_info = {
+	.driver_module = THIS_MODULE,
+	.read_raw = NULL,
+	.write_raw = NULL,
+	.attrs = &inv_icm20602_attribute_group,
+	.validate_trigger = inv_icm20602_validate_trigger,
+};
+
+static int of_populate_icm20602_dt(struct inv_icm20602_state *st)
+{
+	int result = MPU_SUCCESS;
+
+	/* use client device irq */
+	st->gpio = of_get_named_gpio(st->client->dev.of_node,
+			"invensense,icm20602-gpio", 0);
+	result = gpio_is_valid(st->gpio);
+	if (!result) {
+		pr_err("gpio_is_valid %d failed\n", st->gpio);
+		return -MPU_FAIL;
+	}
+
+	result = gpio_request(st->gpio, "icm20602-irq");
+	if (result) {
+		pr_err("gpio_request failed\n");
+		return -MPU_FAIL;
+	}
+
+	result = gpio_direction_input(st->gpio);
+	if (result) {
+		pr_err("gpio_direction_input failed\n");
+		return -MPU_FAIL;
+	}
+
+	st->client->irq = gpio_to_irq(st->gpio);
+	if (st->client->irq < 0) {
+		pr_err("gpio_to_irq failed\n");
+		return -MPU_FAIL;
+	}
+
+	return MPU_SUCCESS;
+}
+
+/*
+ *  inv_icm20602_probe() - probe function.
+ *  @client:          i2c client.
+ *  @id:              i2c device id.
+ *
+ *  Returns 0 on success, a negative error code otherwise.
+ *  The I2C address of the ICM-20602 is 0x68 or 0x69
+ *  depending upon the value driven on AD0 pin.
+ */
+static int inv_icm20602_probe(struct i2c_client *client,
+	const struct i2c_device_id *id)
+{
+	struct inv_icm20602_state *st;
+	struct iio_dev *indio_dev;
+	int result = MPU_SUCCESS;
+
+	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*st));
+	if (indio_dev == NULL) {
+		result =  -ENOMEM;
+		goto out_remove_trigger;
+	}
+	st = iio_priv(indio_dev);
+	st->client = client;
+	st->interface = ICM20602_I2C;
+
+	pr_debug("i2c address is %x\n", client->addr);
+	result = of_populate_icm20602_dt(st);
+	if (result)
+		return result;
+
+	st->config = kzalloc(sizeof(struct icm20602_user_config), GFP_ATOMIC);
+	if (st->config == NULL)
+		return -ENOMEM;
+
+	icm20602_init_reg_map();
+
+	i2c_set_clientdata(client, indio_dev);
+
+	dev_set_drvdata(&client->dev, indio_dev);
+	indio_dev->dev.parent = &client->dev;
+	indio_dev->name = ICM20602_DEV_NAME;
+	indio_dev->channels = inv_icm20602_channels;
+	indio_dev->num_channels = ARRAY_SIZE(inv_icm20602_channels);
+
+	indio_dev->info = &icm20602_info;
+	indio_dev->modes = INDIO_BUFFER_TRIGGERED;
+	result = iio_triggered_buffer_setup(indio_dev,
+		inv_icm20602_irq_handler, inv_icm20602_read_fifo_fn, NULL);
+	if (result) {
+		dev_err(&st->client->dev, " configure buffer fail %d\n",
+				result);
+		goto out_remove_trigger;
+	}
+
+	result = inv_icm20602_probe_trigger(indio_dev);
+	if (result) {
+		dev_err(&st->client->dev, "trigger probe fail %d\n", result);
+		goto out_unreg_ring;
+	}
+
+	INIT_KFIFO(st->timestamps);
+	spin_lock_init(&st->time_stamp_lock);
+	result = iio_device_register(indio_dev);
+	if (result) {
+		dev_err(&st->client->dev, "IIO register fail %d\n", result);
+		goto out_remove_trigger;
+	}
+
+	return 0;
+
+out_remove_trigger:
+	inv_icm20602_remove_trigger(st);
+out_unreg_ring:
+	iio_triggered_buffer_cleanup(indio_dev);
+out_free:
+	iio_device_free(indio_dev);
+	gpio_free(st->gpio);
+
+	return 0;
+}
+
+static int inv_icm20602_remove(struct i2c_client *client)
+{
+	struct iio_dev *indio_dev = i2c_get_clientdata(client);
+	struct inv_icm20602_state *st = iio_priv(indio_dev);
+
+	gpio_free(st->gpio);
+	iio_device_unregister(indio_dev);
+	inv_icm20602_remove_trigger(st);
+	iio_triggered_buffer_cleanup(indio_dev);
+	iio_device_free(indio_dev);
+
+	return 0;
+}
+
+static int inv_icm20602_suspend(struct device *dev)
+{
+	return 0;
+}
+
+static int inv_icm20602_resume(struct device *dev)
+{
+	return 0;
+}
+
+static const struct dev_pm_ops icm20602_pm_ops = {
+	.resume		=	inv_icm20602_resume,
+	.suspend	=	inv_icm20602_suspend,
+};
+
+static const struct of_device_id icm20602_match_table[] = {
+	{.compatible = "invensense,icm20602"},
+	{}
+};
+MODULE_DEVICE_TABLE(of, icm20602_match_table);
+
+static const struct i2c_device_id inv_icm20602_id[] = {
+	{"icm20602", 0},
+	{}
+};
+
+static struct i2c_driver icm20602_i2c_driver = {
+	.probe		=	inv_icm20602_probe,
+	.remove		=	inv_icm20602_remove,
+	.id_table	=	inv_icm20602_id,
+	.driver = {
+		.owner	=	THIS_MODULE,
+		.name	=	"inv-icm20602",
+		.of_match_table = icm20602_match_table,
+		.probe_type = PROBE_PREFER_ASYNCHRONOUS,
+		.pm = &icm20602_pm_ops,
+	},
+};
+
+static int __init inv_icm20602_init(void)
+{
+	return i2c_add_driver(&icm20602_i2c_driver);
+}
+module_init(inv_icm20602_init);
+
+static void __exit inv_icm20602_exit(void)
+{
+	i2c_del_driver(&icm20602_i2c_driver);
+}
+module_exit(inv_icm20602_exit);
+
+MODULE_DESCRIPTION("icm20602 IMU driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/imu/inv_icm20602/inv_icm20602_iio.h b/drivers/iio/imu/inv_icm20602/inv_icm20602_iio.h
new file mode 100644
index 0000000..943fc1e
--- /dev/null
+++ b/drivers/iio/imu/inv_icm20602/inv_icm20602_iio.h
@@ -0,0 +1,282 @@
+/*
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * Copyright (C) 2012 Invensense, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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 _INV_ICM20602_IIO_H_
+#define _INV_ICM20602_IIO_H_
+
+#include <linux/i2c.h>
+#include <linux/kfifo.h>
+#include <linux/spinlock.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/kfifo_buf.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/platform_data/invensense_mpu6050.h>
+
+/*
+ * it uses sensor irq to trigger
+ * if set INV20602_DEVICE_IRQ_TRIGGER as 1,
+ * otherwise use SMD IRQ to trigger
+ */
+#define INV20602_DEVICE_IRQ_TRIGGER    0
+
+#if INV20602_DEVICE_IRQ_TRIGGER
+#define INV20602_SMD_IRQ_TRIGGER    0
+#else
+#define INV20602_SMD_IRQ_TRIGGER    1
+#endif
+
+#define INV_ICM20602_TIME_STAMP_TOR           5
+#define ICM20602_PACKAGE_SIZE 14
+
+/* device enum */
+enum inv_devices {
+	INV_ICM20602,
+	INV_NUM_PARTS,
+};
+
+enum _mpu_err {
+	MPU_SUCCESS = 0,
+	MPU_FAIL = 1,
+	MPU_READ_FAIL = 2,
+	MPU_WRITE_FAIL = 3,
+};
+
+/* Gyro Full Scale Range Enum */
+enum inv_icm20602_gyro_fsr_e {
+	ICM20602_GYRO_FSR_250DPS = 0,
+	ICM20602_GYRO_FSR_500DPS,
+	ICM20602_GYRO_FSR_1000DPS,
+	ICM20602_GYRO_FSR_2000DPS,
+	ICM20602_GYRO_FSR_NUM,
+};
+
+/* Accelerometor Full Scale Range Enum */
+enum inv_icm20602_acc_fsr_e {
+	ICM20602_ACC_FSR_2G = 0,
+	ICM20602_ACC_FSR_4G,
+	ICM20602_ACC_FSR_8G,
+	ICM20602_ACC_FSR_16G,
+	ICM20602_ACC_FSR_NUM,
+};
+
+/* scan element definition */
+enum inv_icm20602_scan {
+	INV_ICM20602_SCAN_ACCL_X,
+	INV_ICM20602_SCAN_ACCL_Y,
+	INV_ICM20602_SCAN_ACCL_Z,
+	INV_ICM20602_SCAN_GYRO_X,
+	INV_ICM20602_SCAN_GYRO_Y,
+	INV_ICM20602_SCAN_GYRO_Z,
+	INV_ICM20602_SCAN_TEMP,
+	INV_ICM20602_SCAN_TIMESTAMP,
+};
+
+/* this is for CONFIGURATION reg bit[2:0] DLFP_CFG */
+enum inv_icm20602_gyro_temp_lpf_e {
+	INV_ICM20602_GLFP_250HZ = 0,  /* 8KHz */
+	INV_ICM20602_GYRO_LFP_176HZ,      /* 1KHz */
+	INV_ICM20602_GYRO_LFP_92HZ,
+	INV_ICM20602_GYRO_LFP_41HZ,
+	INV_ICM20602_GYRO_LFP_20HZ,
+	INV_ICM20602_GYRO_LFP_10HZ,
+	INV_ICM20602_GYRO_LFP_5HZ,
+	INV_ICM20602_GYRO_LFP_NUM,
+};
+
+enum inv_icm20602_gyro_sample_rate_e {
+	ICM20602_SAMPLE_RATE_100HZ = 100,
+	ICM20602_SAMPLE_RATE_200HZ = 200,
+	ICM20602_SAMPLE_RATE_500HZ = 500,
+	ICM20602_SAMPLE_RATE_1000HZ = 1000,
+};
+
+/* this is for ACCEL CONFIGURATION 2 reg */
+enum inv_icm20602_acc_lpf_e {
+	ICM20602_ACCLFP_218 = 1,
+	ICM20602_ACCLFP_99,
+	ICM20602_ACCLFP_44,
+	ICM20602_ACCLFP_21,
+	ICM20602_ACCLFP_10,
+	ICM20602_ACCLFP_5,
+	ICM20602_ACCLFP_420_NOLPF,
+	ICM20602_ACCLPF_NUM = 7,
+};
+
+/* IIO attribute address */
+enum INV_ICM20602_IIO_ATTR_ADDR {
+	ATTR_ICM20602_GYRO_MATRIX,
+	ATTR_ICM20602_ACCL_MATRIX,
+};
+
+/* this is for GYRO CONFIGURATION reg */
+enum inv_icm20602_fs_e {
+	INV_ICM20602_FS_250DPS = 0,
+	INV_ICM20602_FS_500DPS,
+	INV_ICM20602_FS_1000DPS,
+	INV_ICM20602_FS_2000DPS,
+	NUM_ICM20602_FS,
+};
+
+enum inv_icm20602_clock_sel_e {
+	INV_ICM20602_CLK_INTERNAL = 0,
+	INV_ICM20602_CLK_PLL,
+	INV_NUM_CLK,
+};
+
+enum inv_icm20602_spi_freq {
+	MPU_SPI_FREQUENCY_1MHZ = 960000UL,
+	MPU_SPI_FREQUENCY_5MHZ = 4800000UL,
+	MPU_SPI_FREQUENCY_8MHZ = 8000000UL,
+	MPU_SPI_FREQUENCY_10MHZ = 10000000UL,
+	MPU_SPI_FREQUENCY_15MHZ = 15000000UL,
+	MPU_SPI_FREQUENCY_20MHZ = 20000000UL,
+};
+
+#define MPU_SPI_BUF_LEN   512
+#define	ICM20602_DEV_NAME	"icm20602_iio"
+
+struct inv_icm20602_platform_data {
+	__s8 orientation[9];
+};
+
+struct X_Y_Z {
+	u8 X;
+	u8 Y;
+	u8 Z;
+};
+
+enum RAW_TYPE {
+	ACCEL = 1,
+	GYRO  = 2,
+	TEMP  = 4,
+};
+
+struct icm20602_user_config {
+	enum inv_icm20602_gyro_temp_lpf_e gyro_lpf;
+	enum inv_icm20602_gyro_fsr_e gyro_fsr;
+	struct X_Y_Z gyro_self_test;
+
+	enum inv_icm20602_acc_lpf_e	acc_lpf;
+	enum inv_icm20602_acc_fsr_e	acc_fsr;
+	struct X_Y_Z acc_self_test;
+
+	uint32_t gyro_accel_sample_rate;
+	uint32_t user_fps_in_ms;
+
+	bool fifo_enabled;
+	uint32_t fifo_waterlevel;
+	struct X_Y_Z wake_on_motion;
+};
+
+enum inv_icm20602_interface {
+	ICM20602_I2C = 0,
+	ICM20602_SPI
+};
+
+/*
+ *  struct inv_icm20602_state - Driver state variables.
+ *  @TIMESTAMP_FIFO_SIZE: fifo size for timestamp.
+ *  @trig:              IIO trigger.
+ *  @chip_config:	Cached attribute information.
+ *  @reg:		Map of important registers.
+ *  @hw:		Other hardware-specific information.
+ *  @chip_type:		chip type.
+ *  @time_stamp_lock:	spin lock to time stamp.
+ *  @spi: spi devices
+ *  @plat_data:		platform data.
+ *  @timestamps:        kfifo queue to store time stamp.
+ */
+struct inv_icm20602_state {
+#define TIMESTAMP_FIFO_SIZE 32
+	enum inv_icm20602_interface interface;
+	struct iio_trigger  *trig;
+	const struct inv_icm20602_reg_map *reg;
+	struct icm20602_user_config *config;
+	spinlock_t time_stamp_lock;
+	struct spi_device *spi;
+	struct i2c_client *client;
+	u8 fifo_packet_size;
+	int fifo_cnt_threshold;
+	char *buf;
+	struct struct_icm20602_data *data_push;
+	enum inv_devices chip_type;
+	int gpio;
+	DECLARE_KFIFO(timestamps, long long, TIMESTAMP_FIFO_SIZE);
+};
+
+struct struct_icm20602_raw_data {
+	u8 ACCEL_XOUT_H;
+	u8 ACCEL_XOUT_L;
+
+	u8 ACCEL_YOUT_H;
+	u8 ACCEL_YOUT_L;
+
+	u8 ACCEL_ZOUT_H;
+	u8 ACCEL_ZOUT_L;
+
+	u8 TEMP_OUT_H;
+	u8 TEMP_OUT_L;
+
+	u8 GYRO_XOUT_H;
+	u8 GYRO_XOUT_L;
+
+	u8 GYRO_YOUT_H;
+	u8 GYRO_YOUT_L;
+
+	u8 GYRO_ZOUT_H;
+	u8 GYRO_ZOUT_L;
+};
+
+struct struct_icm20602_real_data {
+	u16 ACCEL_XOUT;
+	u16 ACCEL_YOUT;
+	u16 ACCEL_ZOUT;
+
+	u16 GYRO_XOUT;
+	u16 GYRO_YOUT;
+	u16 GYRO_ZOUT;
+
+	u16 TEMP_OUT;
+};
+
+struct struct_icm20602_data {
+	s64 timestamps;
+	struct struct_icm20602_raw_data raw_data;
+	struct struct_icm20602_real_data real_data;
+};
+
+extern struct iio_trigger *inv_trig;
+irqreturn_t inv_icm20602_irq_handler(int irq, void *p);
+irqreturn_t inv_icm20602_read_fifo_fn(int irq, void *p);
+int inv_icm20602_reset_fifo(struct iio_dev *indio_dev);
+
+int inv_icm20602_probe_trigger(struct iio_dev *indio_dev);
+void inv_icm20602_remove_trigger(struct inv_icm20602_state *st);
+int inv_icm20602_validate_trigger(struct iio_dev *indio_dev,
+				struct iio_trigger *trig);
+
+int icm20602_read_raw(struct inv_icm20602_state *st,
+		struct struct_icm20602_real_data *real_data, uint32_t type);
+int icm20602_init_reg_map(void);
+int icm20602_init_device(struct inv_icm20602_state *st);
+int icm20602_detect(struct inv_icm20602_state *st);
+int icm20602_read_fifo(struct inv_icm20602_state *st,
+	void *buf, const int size);
+int icm20602_start_fifo(struct inv_icm20602_state *st);
+#endif
diff --git a/drivers/iio/imu/inv_icm20602/inv_icm20602_ring.c b/drivers/iio/imu/inv_icm20602/inv_icm20602_ring.c
new file mode 100644
index 0000000..b0f93be
--- /dev/null
+++ b/drivers/iio/imu/inv_icm20602/inv_icm20602_ring.c
@@ -0,0 +1,131 @@
+/*
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * Copyright (C) 2012 Invensense, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/sysfs.h>
+#include <linux/jiffies.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/kfifo.h>
+#include <linux/poll.h>
+#include <linux/spi/spi.h>
+#include "inv_icm20602_iio.h"
+#include <linux/i2c.h>
+
+#define combine_8_to_16(upper, lower)  ((_upper << 8) | _lower)
+
+static void inv_clear_kfifo(struct inv_icm20602_state *st)
+{
+	unsigned long flags;
+
+	/* Take the spin lock sem to avoid interrupt kick in */
+	spin_lock_irqsave(&st->time_stamp_lock, flags);
+	kfifo_reset(&st->timestamps);
+	spin_unlock_irqrestore(&st->time_stamp_lock, flags);
+}
+
+int inv_icm20602_reset_fifo(struct iio_dev *indio_dev)
+{
+	int result;
+	//u8 d;
+	//struct inv_icm20602_state  *st = iio_priv(indio_dev);
+
+	return result;
+}
+
+/*
+ * inv_icm20602_irq_handler() - Cache a timestamp at each data ready interrupt.
+ */
+irqreturn_t inv_icm20602_irq_handler(int irq, void *p)
+{
+	struct iio_poll_func *pf = p;
+	struct iio_dev *indio_dev = pf->indio_dev;
+	struct inv_icm20602_state *st = iio_priv(indio_dev);
+	s64 timestamp;
+
+	timestamp = iio_get_time_ns(indio_dev);
+
+	kfifo_in_spinlocked(&st->timestamps, &timestamp, 1,
+				&st->time_stamp_lock);
+
+	return IRQ_WAKE_THREAD;
+}
+
+static int inv_icm20602_read_data(struct iio_dev *indio_dev)
+{
+	int result = MPU_SUCCESS;
+	struct inv_icm20602_state *st = iio_priv(indio_dev);
+	struct icm20602_user_config *config = st->config;
+	int package_count;
+	//char *buf = st->buf;
+	//struct struct_icm20602_data *data_push = st->data_push;
+	s64 timestamp;
+	int i;
+
+	if (!st)
+		return -MPU_FAIL;
+	package_count = config->fifo_waterlevel / ICM20602_PACKAGE_SIZE;
+	mutex_lock(&indio_dev->mlock);
+	if (config->fifo_enabled) {
+		result = icm20602_read_fifo(st,
+				st->buf, config->fifo_waterlevel);
+		if (result != config->fifo_waterlevel) {
+			pr_err("icm20602 read fifo failed, result = %d\n",
+				result);
+			goto flush_fifo;
+		}
+
+		for (i = 0; i < package_count; i++) {
+			memcpy((char *)(&st->data_push[i].raw_data),
+				st->buf, ICM20602_PACKAGE_SIZE);
+			result = kfifo_out(&st->timestamps,
+				&timestamp, 1);
+			/* when there is no timestamp, put it as 0 */
+			if (result == 0)
+				timestamp = 0;
+			st->data_push[i].timestamps = timestamp;
+			iio_push_to_buffers(indio_dev, st->data_push+i);
+			st->buf += ICM20602_PACKAGE_SIZE;
+		}
+	}
+//end_session:
+	mutex_unlock(&indio_dev->mlock);
+	iio_trigger_notify_done(indio_dev->trig);
+	return MPU_SUCCESS;
+
+flush_fifo:
+	/* Flush HW and SW FIFOs. */
+	inv_clear_kfifo(st);
+	inv_icm20602_reset_fifo(indio_dev);
+	mutex_unlock(&indio_dev->mlock);
+	iio_trigger_notify_done(indio_dev->trig);
+	return MPU_SUCCESS;
+}
+
+/*
+ * inv_icm20602_read_fifo() - Transfer data from hardware FIFO to KFIFO.
+ */
+irqreturn_t inv_icm20602_read_fifo_fn(int irq, void *p)
+{
+	struct iio_poll_func *pf = p;
+	struct iio_dev *indio_dev = pf->indio_dev;
+
+	inv_icm20602_read_data(indio_dev);
+	return IRQ_HANDLED;
+}
diff --git a/drivers/iio/imu/inv_icm20602/inv_icm20602_trigger.c b/drivers/iio/imu/inv_icm20602/inv_icm20602_trigger.c
new file mode 100644
index 0000000..f05a631
--- /dev/null
+++ b/drivers/iio/imu/inv_icm20602/inv_icm20602_trigger.c
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * Copyright (C) 2012 Invensense, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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/spi/spi.h>
+#include <linux/sched/rt.h>
+#include <linux/delay.h>
+#include "inv_icm20602_iio.h"
+#include <linux/i2c.h>
+
+static const struct iio_trigger_ops inv_icm20602_trigger_ops = {
+	.owner = THIS_MODULE,
+};
+
+int inv_icm20602_validate_trigger(struct iio_dev *indio_dev,
+					struct iio_trigger *trig)
+{
+	struct inv_icm20602_state *st = iio_priv(indio_dev);
+
+	if (st->trig != trig)
+		return -EINVAL;
+
+	return 0;
+}
+
+int inv_icm20602_probe_trigger(struct iio_dev *indio_dev)
+{
+	int ret;
+	struct inv_icm20602_state *st = iio_priv(indio_dev);
+
+	st->trig = iio_trigger_alloc("%s-dev%d",
+					indio_dev->name,
+					indio_dev->id);
+	if (st->trig == NULL) {
+		ret = -ENOMEM;
+		goto error_ret;
+	}
+
+	ret = request_irq(st->client->irq, &iio_trigger_generic_data_rdy_poll,
+				IRQF_TRIGGER_RISING,
+				"inv_icm20602",
+				st->trig);
+	if (ret) {
+		pr_err("request_irq failed\n");
+		goto error_free_trig;
+	}
+	st->trig->dev.parent = &st->client->dev;
+	st->trig->ops = &inv_icm20602_trigger_ops;
+	iio_trigger_set_drvdata(st->trig, indio_dev);
+	ret = iio_trigger_register(st->trig);
+	if (ret) {
+		pr_err("iio_trigger_register failed\n");
+		goto error_free_irq;
+	}
+	indio_dev->trig = st->trig;
+
+	return 0;
+
+error_free_irq:
+	free_irq(st->client->irq, st->trig);
+error_free_trig:
+	iio_trigger_free(st->trig);
+error_ret:
+	return ret;
+}
+
+void inv_icm20602_remove_trigger(struct inv_icm20602_state *st)
+{
+	iio_trigger_unregister(st->trig);
+	free_irq(st->client->irq, st->trig);
+	iio_trigger_free(st->trig);
+}
diff --git a/drivers/media/platform/msm/camera_v2/sensor/csiphy/msm_csiphy.c b/drivers/media/platform/msm/camera_v2/sensor/csiphy/msm_csiphy.c
index 72586fa..6fc8e1e 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/csiphy/msm_csiphy.c
+++ b/drivers/media/platform/msm/camera_v2/sensor/csiphy/msm_csiphy.c
@@ -293,10 +293,18 @@
 	void __iomem *csiphybase;
 	enum snps_csiphy_mode mode = INVALID_MODE;
 	uint32_t value, num_tries, num_lanes, offset;
+	uint32_t clk_mux_reg = 0;
 
 	csiphybase = csiphy_dev->base;
+	if (csiphy_dev->clk_mux_base != NULL)
+		clk_mux_reg = msm_camera_io_r(csiphy_dev->clk_mux_base);
+	else {
+		pr_err("%s: invalid clk_mux_base\n", __func__);
+		return -EINVAL;
+	}
+
 	/* lane mask usage
-	 * BIT	  LANE
+	 * BIT     LANE
 	 * 0(LSB)  PHY A data 0
 	 * 1       PHY A data 1
 	 * 2       PHY B data 0
@@ -318,6 +326,9 @@
 			return -EINVAL;
 		}
 		csiphy_dev->snps_state = CONFIGURED_AGGREGATE_MODE;
+		clk_mux_reg &= ~0xff;
+		clk_mux_reg |= csiphy_params->csid_core << 4;
+		clk_mux_reg |= (uint32_t)csiphy_params->csid_core;
 	} else if (lane_mask == LANE_MASK_PHY_A) { /* PHY A */
 		/* 2 lane config */
 		num_lanes = 2;
@@ -333,6 +344,8 @@
 			pr_err("%s: invalid request\n", __func__);
 			return -EINVAL;
 		}
+		clk_mux_reg &= ~0xf;
+		clk_mux_reg |= (uint32_t)csiphy_params->csid_core;
 	} else if (lane_mask == LANE_MASK_PHY_B) { /* PHY B */
 		/* 2 lane config */
 		num_lanes = 2;
@@ -348,11 +361,17 @@
 			pr_err("%s: invalid request\n", __func__);
 			return -EINVAL;
 		}
+		clk_mux_reg &= ~0xf0;
+		clk_mux_reg |= csiphy_params->csid_core << 4;
 	} else { /* None of available configurations */
 		pr_err("%s: invalid configuration requested\n", __func__);
 		return -EINVAL;
 	}
 
+	msm_camera_io_w(clk_mux_reg, csiphy_dev->clk_mux_base);
+	/* ensure write is done */
+	mb();
+
 	if (mode == AGGREGATE_MODE || mode == TWO_LANE_PHY_A) {
 		ret = msm_csiphy_snps_2_lane_config(csiphy_dev,
 			csiphy_params, TWO_LANE_PHY_A, num_lanes);
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_qmi_service.h b/drivers/platform/msm/ipa/ipa_v2/ipa_qmi_service.h
index 1f5d619..98f5574 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_qmi_service.h
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_qmi_service.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2018, 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
@@ -281,7 +281,7 @@
 {
 }
 
-static int rmnet_ipa_reset_tethering_stats
+static inline int rmnet_ipa_reset_tethering_stats
 (
 	struct wan_ioctl_reset_tether_stats *data
 )
diff --git a/drivers/power/supply/qcom/Makefile b/drivers/power/supply/qcom/Makefile
index da20d93..ffc1ce3 100644
--- a/drivers/power/supply/qcom/Makefile
+++ b/drivers/power/supply/qcom/Makefile
@@ -7,7 +7,7 @@
 obj-$(CONFIG_SMB1351_USB_CHARGER) += smb1351-charger.o pmic-voter.o battery.o
 obj-$(CONFIG_QPNP_SMB2)		+= step-chg-jeita.o battery.o qpnp-smb2.o smb-lib.o pmic-voter.o storm-watch.o
 obj-$(CONFIG_SMB138X_CHARGER)	+= step-chg-jeita.o smb138x-charger.o smb-lib.o pmic-voter.o storm-watch.o battery.o
-obj-$(CONFIG_QPNP_QG)		+= qpnp-qg.o pmic-voter.o qg-util.o qg-soc.o qg-sdam.o qg-battery-profile.o qg-profile-lib.o
+obj-$(CONFIG_QPNP_QG)		+= qpnp-qg.o pmic-voter.o qg-util.o qg-soc.o qg-sdam.o qg-battery-profile.o qg-profile-lib.o fg-alg.o
 obj-$(CONFIG_QPNP_QNOVO)	+= qpnp-qnovo.o battery.o
 obj-$(CONFIG_QPNP_TYPEC)	+= qpnp-typec.o
 obj-$(CONFIG_QPNP_SMB5)		+= step-chg-jeita.o battery.o qpnp-smb5.o smb5-lib.o pmic-voter.o storm-watch.o schgm-flash.o
diff --git a/drivers/power/supply/qcom/qg-battery-profile.c b/drivers/power/supply/qcom/qg-battery-profile.c
index 441c759..36edd76 100644
--- a/drivers/power/supply/qcom/qg-battery-profile.c
+++ b/drivers/power/supply/qcom/qg-battery-profile.c
@@ -22,6 +22,7 @@
 #include <linux/slab.h>
 #include <linux/uaccess.h>
 #include <uapi/linux/qg-profile.h>
+#include "qg-battery-profile.h"
 #include "qg-profile-lib.h"
 #include "qg-defs.h"
 
@@ -410,6 +411,26 @@
 	return 0;
 }
 
+int qg_get_nominal_capacity(u32 *nom_cap_uah, int batt_temp, bool charging)
+{
+	u8 table_index = charging ? TABLE_FCC1 : TABLE_FCC2;
+	u32 fcc_mah;
+
+	if (!the_battery || !the_battery->profile) {
+		pr_err("Battery profile not loaded\n");
+		return -ENODEV;
+	}
+
+	fcc_mah = interpolate_single_row_lut(
+				&the_battery->profile[table_index],
+					batt_temp, DEGC_SCALE);
+	fcc_mah = CAP(QG_MIN_FCC_MAH, QG_MAX_FCC_MAH, fcc_mah);
+
+	*nom_cap_uah = fcc_mah * 1000;
+
+	return 0;
+}
+
 int qg_batterydata_init(struct device_node *profile_node)
 {
 	int rc = 0;
diff --git a/drivers/power/supply/qcom/qg-battery-profile.h b/drivers/power/supply/qcom/qg-battery-profile.h
index 143a4f2..1b06277 100644
--- a/drivers/power/supply/qcom/qg-battery-profile.h
+++ b/drivers/power/supply/qcom/qg-battery-profile.h
@@ -15,5 +15,6 @@
 int qg_batterydata_init(struct device_node *node);
 void qg_batterydata_exit(void);
 int lookup_soc_ocv(u32 *soc, u32 ocv_uv, int batt_temp, bool charging);
+int qg_get_nominal_capacity(u32 *nom_cap_uah, int batt_temp, bool charging);
 
 #endif /* __QG_BATTERY_PROFILE_H__ */
diff --git a/drivers/power/supply/qcom/qg-core.h b/drivers/power/supply/qcom/qg-core.h
index 6e44f33..05ca452 100644
--- a/drivers/power/supply/qcom/qg-core.h
+++ b/drivers/power/supply/qcom/qg-core.h
@@ -12,6 +12,9 @@
 #ifndef __QG_CORE_H__
 #define __QG_CORE_H__
 
+#include <linux/kernel.h>
+#include "fg-alg.h"
+
 struct qg_batt_props {
 	const char		*batt_type_str;
 	int			float_volt_uv;
@@ -49,6 +52,8 @@
 	int			cold_temp_threshold;
 	bool			hold_soc_while_full;
 	bool			linearize_soc;
+	bool			cl_disable;
+	bool			cl_feedback_on;
 };
 
 struct qpnp_qg {
@@ -109,12 +114,19 @@
 	int			maint_soc;
 	int			msoc;
 	int			pon_soc;
+	int			batt_soc;
+	int			cc_soc;
 	struct alarm		alarm_timer;
 	u32			sdam_data[SDAM_MAX];
 
 	/* DT */
 	struct qg_dt		dt;
 	struct qg_batt_props	bp;
+	/* capacity learning */
+	struct cap_learning	*cl;
+	/* charge counter */
+	struct cycle_counter	*counter;
+	char			counter_buf[BUCKET_COUNT * 8];
 };
 
 enum ocv_type {
@@ -135,6 +147,7 @@
 	QG_DEBUG_PM		= BIT(7),
 	QG_DEBUG_BUS_READ	= BIT(8),
 	QG_DEBUG_BUS_WRITE	= BIT(9),
+	QG_DEBUG_ALG_CL		= BIT(10),
 };
 
 enum qg_irq {
diff --git a/drivers/power/supply/qcom/qg-defs.h b/drivers/power/supply/qcom/qg-defs.h
index 6ae9aa2..2061208 100644
--- a/drivers/power/supply/qcom/qg-defs.h
+++ b/drivers/power/supply/qcom/qg-defs.h
@@ -47,4 +47,7 @@
 #define CAP(min, max, value)			\
 		((min > value) ? min : ((value > max) ? max : value))
 
+#define QG_SOC_FULL	10000
+#define BATT_SOC_32BIT	GENMASK(31, 0)
+
 #endif /* __QG_DEFS_H__ */
diff --git a/drivers/power/supply/qcom/qg-reg.h b/drivers/power/supply/qcom/qg-reg.h
index 96533d4..66f9be1 100644
--- a/drivers/power/supply/qcom/qg-reg.h
+++ b/drivers/power/supply/qcom/qg-reg.h
@@ -87,6 +87,8 @@
 #define QG_SDAM_OCV_OFFSET			0x4C
 #define QG_SDAM_IBAT_OFFSET			0x50
 #define QG_SDAM_TIME_OFFSET			0x54
+#define QG_SDAM_CYCLE_COUNT_OFFSET		0x58
+#define QG_SDAM_LEARNED_CAPACITY_OFFSET		0x68
 #define QG_SDAM_PON_OCV_OFFSET			0x7C
 
 #endif
diff --git a/drivers/power/supply/qcom/qg-sdam.c b/drivers/power/supply/qcom/qg-sdam.c
index 54eed16..7bc4afa 100644
--- a/drivers/power/supply/qcom/qg-sdam.c
+++ b/drivers/power/supply/qcom/qg-sdam.c
@@ -130,6 +130,53 @@
 	return rc;
 }
 
+int qg_sdam_multibyte_write(u32 offset, u8 *data, u32 length)
+{
+	int rc, i;
+	struct qg_sdam *chip = the_chip;
+
+	if (!chip) {
+		pr_err("Invalid sdam-chip pointer\n");
+		return -EINVAL;
+	}
+
+	offset = chip->sdam_base + offset;
+	rc = regmap_bulk_write(chip->regmap, offset, data, (size_t)length);
+	if (rc < 0) {
+		pr_err("Failed to write offset=%0x4x value=%d\n",
+					offset, *data);
+	} else {
+		for (i = 0; i < length; i++)
+			pr_debug("QG SDAM write offset=%0x4x value=%d\n",
+					offset++, data[i]);
+	}
+
+	return rc;
+}
+
+int qg_sdam_multibyte_read(u32 offset, u8 *data, u32 length)
+{
+	int rc, i;
+	struct qg_sdam *chip = the_chip;
+
+	if (!chip) {
+		pr_err("Invalid sdam-chip pointer\n");
+		return -EINVAL;
+	}
+
+	offset = chip->sdam_base + offset;
+	rc = regmap_raw_read(chip->regmap, offset, (u8 *)data, (size_t)length);
+	if (rc < 0) {
+		pr_err("Failed to read offset=%0x4x\n", offset);
+	} else {
+		for (i = 0; i < length; i++)
+			pr_debug("QG SDAM read offset=%0x4x value=%d\n",
+					offset++, data[i]);
+	}
+
+	return rc;
+}
+
 int qg_sdam_read_all(u32 *sdam_data)
 {
 	int i, rc;
diff --git a/drivers/power/supply/qcom/qg-sdam.h b/drivers/power/supply/qcom/qg-sdam.h
index 51af04c..10e684f 100644
--- a/drivers/power/supply/qcom/qg-sdam.h
+++ b/drivers/power/supply/qcom/qg-sdam.h
@@ -37,5 +37,7 @@
 int qg_sdam_read(u8 param, u32 *data);
 int qg_sdam_write_all(u32 *sdam_data);
 int qg_sdam_read_all(u32 *sdam_data);
+int qg_sdam_multibyte_write(u32 offset, u8 *sdam_data, u32 length);
+int qg_sdam_multibyte_read(u32 offset, u8 *sdam_data, u32 length);
 
 #endif
diff --git a/drivers/power/supply/qcom/qg-soc.c b/drivers/power/supply/qcom/qg-soc.c
index c4d5043..af8b158 100644
--- a/drivers/power/supply/qcom/qg-soc.c
+++ b/drivers/power/supply/qcom/qg-soc.c
@@ -13,9 +13,11 @@
 #define pr_fmt(fmt)	"QG-K: %s: " fmt, __func__
 
 #include <linux/alarmtimer.h>
+#include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/power_supply.h>
 #include <uapi/linux/qg.h>
+#include "fg-alg.h"
 #include "qg-sdam.h"
 #include "qg-core.h"
 #include "qg-reg.h"
@@ -98,11 +100,12 @@
 
 static void update_msoc(struct qpnp_qg *chip)
 {
-	int rc;
+	int rc = 0, batt_temp = 0,  batt_soc_32bit = 0;
+	bool usb_present = is_usb_present(chip);
 
 	if (chip->catch_up_soc > chip->msoc) {
 		/* SOC increased */
-		if (is_usb_present(chip)) /* Increment if USB is present */
+		if (usb_present) /* Increment if USB is present */
 			chip->msoc += chip->dt.delta_soc;
 	} else if (chip->catch_up_soc < chip->msoc) {
 		/* SOC dropped */
@@ -130,6 +133,25 @@
 	if (rc < 0)
 		pr_err("Failed to update SDAM with MSOC rc=%d\n", rc);
 
+	if (!chip->dt.cl_disable && chip->cl->active) {
+		rc = qg_get_battery_temp(chip, &batt_temp);
+		if (rc < 0) {
+			pr_err("Failed to read BATT_TEMP rc=%d\n", rc);
+		} else {
+			batt_soc_32bit = div64_u64(
+						chip->batt_soc * BATT_SOC_32BIT,
+						QG_SOC_FULL);
+			cap_learning_update(chip->cl, batt_temp, batt_soc_32bit,
+					chip->charge_status, chip->charge_done,
+					usb_present, false);
+		}
+	}
+
+	cycle_count_update(chip->counter,
+			DIV_ROUND_CLOSEST(chip->msoc * 255, 100),
+			chip->charge_status, chip->charge_done,
+			usb_present);
+
 	qg_dbg(chip, QG_DEBUG_SOC,
 		"SOC scale: Update maint_soc=%d msoc=%d catch_up_soc=%d delta_soc=%d\n",
 				chip->maint_soc, chip->msoc,
diff --git a/drivers/power/supply/qcom/qpnp-qg.c b/drivers/power/supply/qcom/qpnp-qg.c
index f7a6b7a..09705ad 100644
--- a/drivers/power/supply/qcom/qpnp-qg.c
+++ b/drivers/power/supply/qcom/qpnp-qg.c
@@ -16,6 +16,7 @@
 #include <linux/cdev.h>
 #include <linux/device.h>
 #include <linux/interrupt.h>
+#include <linux/kernel.h>
 #include <linux/ktime.h>
 #include <linux/module.h>
 #include <linux/of.h>
@@ -28,6 +29,7 @@
 #include <linux/pmic-voter.h>
 #include <linux/qpnp/qpnp-adc.h>
 #include <uapi/linux/qg.h>
+#include "fg-alg.h"
 #include "qg-sdam.h"
 #include "qg-core.h"
 #include "qg-reg.h"
@@ -641,6 +643,12 @@
 			struct qpnp_qg, udata_work);
 	int rc;
 
+	if (chip->udata.param[QG_CC_SOC].valid)
+		chip->cc_soc = chip->udata.param[QG_CC_SOC].data;
+
+	if (chip->udata.param[QG_BATT_SOC].valid)
+		chip->batt_soc = chip->udata.param[QG_BATT_SOC].data;
+
 	if (chip->udata.param[QG_SOC].valid) {
 		qg_dbg(chip, QG_DEBUG_SOC, "udata SOC=%d last SOC=%d\n",
 			chip->udata.param[QG_SOC].data, chip->catch_up_soc);
@@ -946,6 +954,127 @@
 	return 0;
 }
 
+/* ALG callback functions below */
+
+static int qg_get_learned_capacity(void *data, int64_t *learned_cap_uah)
+{
+	struct qpnp_qg *chip = data;
+	int16_t cc_mah;
+	int rc;
+
+	if (!chip)
+		return -ENODEV;
+
+	if (chip->battery_missing || !chip->profile_loaded)
+		return -EPERM;
+
+	rc = qg_sdam_multibyte_read(QG_SDAM_LEARNED_CAPACITY_OFFSET,
+					(u8 *)&cc_mah, 2);
+	if (rc < 0) {
+		pr_err("Error in reading learned_capacity, rc=%d\n", rc);
+		return rc;
+	}
+	*learned_cap_uah = cc_mah * 1000;
+
+	qg_dbg(chip, QG_DEBUG_ALG_CL, "Retrieved learned capacity %llduah\n",
+					*learned_cap_uah);
+	return 0;
+}
+
+static int qg_store_learned_capacity(void *data, int64_t learned_cap_uah)
+{
+	struct qpnp_qg *chip = data;
+	int16_t cc_mah;
+	int rc;
+
+	if (!chip)
+		return -ENODEV;
+
+	if (chip->battery_missing || !learned_cap_uah)
+		return -EPERM;
+
+	cc_mah = div64_s64(learned_cap_uah, 1000);
+	rc = qg_sdam_multibyte_write(QG_SDAM_LEARNED_CAPACITY_OFFSET,
+					 (u8 *)&cc_mah, 2);
+	if (rc < 0) {
+		pr_err("Error in writing learned_capacity, rc=%d\n", rc);
+		return rc;
+	}
+
+	qg_dbg(chip, QG_DEBUG_ALG_CL, "Stored learned capacity %llduah\n",
+					learned_cap_uah);
+	return 0;
+}
+
+static int qg_get_cc_soc(void *data, int *cc_soc)
+{
+	struct qpnp_qg *chip = data;
+
+	if (!chip)
+		return -ENODEV;
+
+	if (chip->cc_soc == INT_MIN)
+		return -EINVAL;
+
+	*cc_soc = chip->cc_soc;
+
+	return 0;
+}
+
+static int qg_restore_cycle_count(void *data, u16 *buf, int length)
+{
+	struct qpnp_qg *chip = data;
+	int id, rc = 0;
+	u8 tmp[2];
+
+	if (!chip)
+		return -ENODEV;
+
+	if (chip->battery_missing || !chip->profile_loaded)
+		return -EPERM;
+
+	if (!buf || length > BUCKET_COUNT)
+		return -EINVAL;
+
+	for (id = 0; id < length; id++) {
+		rc = qg_sdam_multibyte_read(
+				QG_SDAM_CYCLE_COUNT_OFFSET + (id * 2),
+				(u8 *)tmp, 2);
+		if (rc < 0) {
+			pr_err("failed to read bucket %d rc=%d\n", id, rc);
+			return rc;
+		}
+		*buf++ = tmp[0] | tmp[1] << 8;
+	}
+
+	return rc;
+}
+
+static int qg_store_cycle_count(void *data, u16 *buf, int id, int length)
+{
+	struct qpnp_qg *chip = data;
+	int rc = 0;
+
+	if (!chip)
+		return -ENODEV;
+
+	if (chip->battery_missing || !chip->profile_loaded)
+		return -EPERM;
+
+	if (!buf || length > BUCKET_COUNT * 2 || id < 0 ||
+		id > BUCKET_COUNT - 1 ||
+		(((id * 2) + length) > BUCKET_COUNT * 2))
+		return -EINVAL;
+
+	rc = qg_sdam_multibyte_write(
+			QG_SDAM_CYCLE_COUNT_OFFSET + (id * 2),
+			(u8 *)buf, length);
+	if (rc < 0)
+		pr_err("failed to write bucket %d rc=%d\n", id, rc);
+
+	return rc;
+}
+
 #define DEFAULT_BATT_TYPE	"Unknown Battery"
 #define MISSING_BATT_TYPE	"Missing Battery"
 #define DEBUG_BATT_TYPE		"Debug Board"
@@ -1042,10 +1171,62 @@
 	return 0;
 }
 
+static const char *qg_get_cycle_counts(struct qpnp_qg *chip)
+{
+	int i, rc, len = 0;
+	char *buf;
+
+	buf = chip->counter_buf;
+	for (i = 1; i <= BUCKET_COUNT; i++) {
+		chip->counter->id = i;
+		rc = get_cycle_count(chip->counter);
+		if (rc < 0) {
+			pr_err("Couldn't get cycle count rc=%d\n", rc);
+			return NULL;
+		}
+
+		if (sizeof(chip->counter_buf) - len < 8) {
+			pr_err("Invalid length %d\n", len);
+			return NULL;
+		}
+
+		len += snprintf(buf+len, 8, "%d ", rc);
+	}
+
+	buf[len] = '\0';
+	return buf;
+}
+
 static int qg_psy_set_property(struct power_supply *psy,
 			       enum power_supply_property psp,
 			       const union power_supply_propval *pval)
 {
+	struct qpnp_qg *chip = power_supply_get_drvdata(psy);
+	int rc = 0;
+
+	switch (psp) {
+	case POWER_SUPPLY_PROP_CHARGE_FULL:
+		if (chip->dt.cl_disable) {
+			pr_warn("Capacity learning disabled!\n");
+			return 0;
+		}
+		if (chip->cl->active) {
+			pr_warn("Capacity learning active!\n");
+			return 0;
+		}
+		if (pval->intval <= 0 || pval->intval > chip->cl->nom_cap_uah) {
+			pr_err("charge_full is out of bounds\n");
+			return -EINVAL;
+		}
+		mutex_lock(&chip->cl->lock);
+		rc = qg_store_learned_capacity(chip, pval->intval);
+		if (!rc)
+			chip->cl->learned_cap_uah = pval->intval;
+		mutex_unlock(&chip->cl->lock);
+		break;
+	default:
+		break;
+	}
 	return 0;
 }
 
@@ -1055,6 +1236,7 @@
 {
 	struct qpnp_qg *chip = power_supply_get_drvdata(psy);
 	int rc = 0;
+	int64_t temp = 0;
 
 	pval->intval = 0;
 
@@ -1106,6 +1288,22 @@
 	case POWER_SUPPLY_PROP_CHARGE_COUNTER:
 		pval->intval = chip->charge_counter_uah;
 		break;
+	case POWER_SUPPLY_PROP_CHARGE_FULL:
+		if (!chip->dt.cl_disable && chip->dt.cl_feedback_on)
+			rc = qg_get_learned_capacity(chip, &temp);
+		else
+			rc = qg_get_nominal_capacity((int *)&temp, 250, true);
+		if (!rc)
+			pval->intval = (int)temp;
+		break;
+	case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
+		rc = qg_get_nominal_capacity((int *)&temp, 250, true);
+		if (!rc)
+			pval->intval = (int)temp;
+		break;
+	case POWER_SUPPLY_PROP_CYCLE_COUNTS:
+		pval->strval = qg_get_cycle_counts(chip);
+		break;
 	default:
 		pr_debug("Unsupported property %d\n", psp);
 		break;
@@ -1117,6 +1315,12 @@
 static int qg_property_is_writeable(struct power_supply *psy,
 				enum power_supply_property psp)
 {
+	switch (psp) {
+	case POWER_SUPPLY_PROP_CHARGE_FULL:
+		return 1;
+	default:
+		break;
+	}
 	return 0;
 }
 
@@ -1136,6 +1340,9 @@
 	POWER_SUPPLY_PROP_VOLTAGE_MAX,
 	POWER_SUPPLY_PROP_BATT_FULL_CURRENT,
 	POWER_SUPPLY_PROP_BATT_PROFILE_VERSION,
+	POWER_SUPPLY_PROP_CYCLE_COUNTS,
+	POWER_SUPPLY_PROP_CHARGE_FULL,
+	POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
 };
 
 static const struct power_supply_desc qg_psy_desc = {
@@ -1262,7 +1469,7 @@
 	struct qpnp_qg *chip = container_of(work,
 			struct qpnp_qg, qg_status_change_work);
 	union power_supply_propval prop = {0, };
-	int rc = 0;
+	int rc = 0, batt_temp = 0, batt_soc_32b = 0;
 
 	if (!is_batt_available(chip)) {
 		pr_debug("batt-psy not available\n");
@@ -1294,6 +1501,24 @@
 	if (rc < 0)
 		pr_err("Failed to update usb status, rc=%d\n", rc);
 
+	cycle_count_update(chip->counter,
+			DIV_ROUND_CLOSEST(chip->msoc * 255, 100),
+			chip->charge_status, chip->charge_done,
+			chip->usb_present);
+
+	if (!chip->dt.cl_disable) {
+		rc = qg_get_battery_temp(chip, &batt_temp);
+		if (rc < 0) {
+			pr_err("Failed to read BATT_TEMP at PON rc=%d\n", rc);
+		} else {
+			batt_soc_32b = div64_u64(
+					chip->batt_soc * BATT_SOC_32BIT,
+					QG_SOC_FULL);
+			cap_learning_update(chip->cl, batt_temp, batt_soc_32b,
+				chip->charge_status, chip->charge_done,
+				chip->usb_present, false);
+		}
+	}
 	rc = qg_charge_full_update(chip);
 	if (rc < 0)
 		pr_err("Failed in charge_full_update, rc=%d\n", rc);
@@ -2069,6 +2294,64 @@
 	return 0;
 }
 
+static int qg_alg_init(struct qpnp_qg *chip)
+{
+	struct cycle_counter *counter;
+	struct cap_learning *cl;
+	struct device_node *node = chip->dev->of_node;
+	int rc;
+
+	counter = devm_kzalloc(chip->dev, sizeof(*counter), GFP_KERNEL);
+	if (!counter)
+		return -ENOMEM;
+
+	counter->restore_count = qg_restore_cycle_count;
+	counter->store_count = qg_store_cycle_count;
+	counter->data = chip;
+
+	rc = cycle_count_init(counter);
+	if (rc < 0) {
+		dev_err(chip->dev, "Error in initializing cycle counter, rc:%d\n",
+			rc);
+		counter->data = NULL;
+		devm_kfree(chip->dev, counter);
+		return rc;
+	}
+
+	chip->counter = counter;
+
+	chip->dt.cl_disable = of_property_read_bool(node,
+					"qcom,cl-disable");
+
+	/*Return if capacity learning is disabled*/
+	if (chip->dt.cl_disable)
+		return 0;
+
+	cl = devm_kzalloc(chip->dev, sizeof(*cl), GFP_KERNEL);
+	if (!cl)
+		return -ENOMEM;
+
+	cl->cc_soc_max = QG_SOC_FULL;
+	cl->get_cc_soc = qg_get_cc_soc;
+	cl->get_learned_capacity = qg_get_learned_capacity;
+	cl->store_learned_capacity = qg_store_learned_capacity;
+	cl->data = chip;
+
+	rc = cap_learning_init(cl);
+	if (rc < 0) {
+		dev_err(chip->dev, "Error in initializing capacity learning, rc:%d\n",
+			rc);
+		counter->data = NULL;
+		cl->data = NULL;
+		devm_kfree(chip->dev, counter);
+		devm_kfree(chip->dev, cl);
+		return rc;
+	}
+
+	chip->cl = cl;
+	return 0;
+}
+
 #define DEFAULT_VBATT_EMPTY_MV		3200
 #define DEFAULT_VBATT_EMPTY_COLD_MV	3000
 #define DEFAULT_VBATT_CUTOFF_MV		3400
@@ -2082,6 +2365,14 @@
 #define DEFAULT_DELTA_SOC		1
 #define DEFAULT_SHUTDOWN_SOC_SECS	360
 #define DEFAULT_COLD_TEMP_THRESHOLD	0
+#define DEFAULT_CL_MIN_START_SOC	10
+#define DEFAULT_CL_MAX_START_SOC	15
+#define DEFAULT_CL_MIN_TEMP_DECIDEGC	150
+#define DEFAULT_CL_MAX_TEMP_DECIDEGC	500
+#define DEFAULT_CL_MAX_INC_DECIPERC	5
+#define DEFAULT_CL_MAX_DEC_DECIPERC	100
+#define DEFAULT_CL_MIN_LIM_DECIPERC	0
+#define DEFAULT_CL_MAX_LIM_DECIPERC	0
 static int qg_parse_dt(struct qpnp_qg *chip)
 {
 	int rc = 0;
@@ -2275,6 +2566,65 @@
 	else
 		chip->dt.rbat_conn_mohm = temp;
 
+	/* Capacity learning params*/
+	if (!chip->dt.cl_disable) {
+		chip->dt.cl_feedback_on = of_property_read_bool(node,
+						"qcom,cl-feedback-on");
+
+		rc = of_property_read_u32(node, "qcom,cl-min-start-soc", &temp);
+		if (rc < 0)
+			chip->cl->dt.min_start_soc = DEFAULT_CL_MIN_START_SOC;
+		else
+			chip->cl->dt.min_start_soc = temp;
+
+		rc = of_property_read_u32(node, "qcom,cl-max-start-soc", &temp);
+		if (rc < 0)
+			chip->cl->dt.max_start_soc = DEFAULT_CL_MAX_START_SOC;
+		else
+			chip->cl->dt.max_start_soc = temp;
+
+		rc = of_property_read_u32(node, "qcom,cl-min-temp", &temp);
+		if (rc < 0)
+			chip->cl->dt.min_temp = DEFAULT_CL_MIN_TEMP_DECIDEGC;
+		else
+			chip->cl->dt.min_temp = temp;
+
+		rc = of_property_read_u32(node, "qcom,cl-max-temp", &temp);
+		if (rc < 0)
+			chip->cl->dt.max_temp = DEFAULT_CL_MAX_TEMP_DECIDEGC;
+		else
+			chip->cl->dt.max_temp = temp;
+
+		rc = of_property_read_u32(node, "qcom,cl-max-increment", &temp);
+		if (rc < 0)
+			chip->cl->dt.max_cap_inc = DEFAULT_CL_MAX_INC_DECIPERC;
+		else
+			chip->cl->dt.max_cap_inc = temp;
+
+		rc = of_property_read_u32(node, "qcom,cl-max-decrement", &temp);
+		if (rc < 0)
+			chip->cl->dt.max_cap_dec = DEFAULT_CL_MAX_DEC_DECIPERC;
+		else
+			chip->cl->dt.max_cap_dec = temp;
+
+		rc = of_property_read_u32(node, "qcom,cl-min-limit", &temp);
+		if (rc < 0)
+			chip->cl->dt.min_cap_limit =
+						DEFAULT_CL_MIN_LIM_DECIPERC;
+		else
+			chip->cl->dt.min_cap_limit = temp;
+
+		rc = of_property_read_u32(node, "qcom,cl-max-limit", &temp);
+		if (rc < 0)
+			chip->cl->dt.max_cap_limit =
+						DEFAULT_CL_MAX_LIM_DECIPERC;
+		else
+			chip->cl->dt.max_cap_limit = temp;
+
+		qg_dbg(chip, QG_DEBUG_PON, "DT: cl_min_start_soc=%d cl_max_start_soc=%d cl_min_temp=%d cl_max_temp=%d\n",
+			chip->cl->dt.min_start_soc, chip->cl->dt.max_start_soc,
+			chip->cl->dt.min_temp, chip->cl->dt.max_temp);
+	}
 	qg_dbg(chip, QG_DEBUG_PON, "DT: vbatt_empty_mv=%dmV vbatt_low_mv=%dmV delta_soc=%d\n",
 			chip->dt.vbatt_empty_mv, chip->dt.vbatt_low_mv,
 			chip->dt.delta_soc);
@@ -2464,7 +2814,7 @@
 
 static int qpnp_qg_probe(struct platform_device *pdev)
 {
-	int rc = 0, soc = 0;
+	int rc = 0, soc = 0, nom_cap_uah;
 	struct qpnp_qg *chip;
 
 	chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL);
@@ -2497,6 +2847,14 @@
 	mutex_init(&chip->data_lock);
 	init_waitqueue_head(&chip->qg_wait_q);
 	chip->maint_soc = -EINVAL;
+	chip->batt_soc = INT_MIN;
+	chip->cc_soc = INT_MIN;
+
+	rc = qg_alg_init(chip);
+	if (rc < 0) {
+		pr_err("Error in alg_init, rc:%d\n", rc);
+		return rc;
+	}
 
 	rc = qg_parse_dt(chip);
 	if (rc < 0) {
@@ -2534,6 +2892,30 @@
 		return rc;
 	}
 
+	if (chip->profile_loaded) {
+		if (!chip->dt.cl_disable) {
+			/*
+			 * Use FCC @ 25 C and charge-profile for
+			 * Nominal Capacity
+			 */
+			rc = qg_get_nominal_capacity(&nom_cap_uah, 250, true);
+			if (!rc) {
+				rc = cap_learning_post_profile_init(chip->cl,
+						nom_cap_uah);
+				if (rc < 0) {
+					pr_err("Error in cap_learning_post_profile_init rc=%d\n",
+						rc);
+					return rc;
+				}
+			}
+		}
+		rc = restore_cycle_count(chip->counter);
+		if (rc < 0) {
+			pr_err("Error in restoring cycle_count, rc=%d\n", rc);
+			return rc;
+		}
+	}
+
 	rc = qg_determine_pon_soc(chip);
 	if (rc < 0) {
 		pr_err("Failed to determine initial state, rc=%d\n", rc);
@@ -2627,6 +3009,22 @@
 	return 0;
 }
 
+static void qpnp_qg_shutdown(struct platform_device *pdev)
+{
+	struct qpnp_qg *chip = platform_get_drvdata(pdev);
+
+	if (!is_usb_present(chip) || !chip->profile_loaded)
+		return;
+	/*
+	 * Charging status doesn't matter when the device shuts down and we
+	 * have to treat this as charge done. Hence pass charge_done as true.
+	 */
+	cycle_count_update(chip->counter,
+			DIV_ROUND_CLOSEST(chip->msoc * 255, 100),
+			POWER_SUPPLY_STATUS_NOT_CHARGING,
+			true, chip->usb_present);
+}
+
 static const struct of_device_id match_table[] = {
 	{ .compatible = "qcom,qpnp-qg", },
 	{ },
@@ -2641,6 +3039,7 @@
 	},
 	.probe		= qpnp_qg_probe,
 	.remove		= qpnp_qg_remove,
+	.shutdown	= qpnp_qg_shutdown,
 };
 module_platform_driver(qpnp_qg_driver);
 
diff --git a/drivers/power/supply/qcom/qpnp-smb5.c b/drivers/power/supply/qcom/qpnp-smb5.c
index d3aaf26..462e55f 100644
--- a/drivers/power/supply/qcom/qpnp-smb5.c
+++ b/drivers/power/supply/qcom/qpnp-smb5.c
@@ -52,6 +52,13 @@
 		.max_u  = 3000000,
 		.step_u = 50000,
 	},
+	.icl_max_stat		= {
+		.name   = "dcdc icl max status",
+		.reg    = ICL_MAX_STATUS_REG,
+		.min_u  = 0,
+		.max_u  = 3000000,
+		.step_u = 50000,
+	},
 	.icl_stat		= {
 		.name   = "input current limit status",
 		.reg    = AICL_ICL_STATUS_REG,
@@ -90,7 +97,7 @@
 	},
 };
 
-static struct smb_params smb5_pmi855_params = {
+static struct smb_params smb5_pm855b_params = {
 	.fcc			= {
 		.name   = "fast charge current",
 		.reg    = CHGR_FAST_CHARGE_CURRENT_CFG_REG,
@@ -112,8 +119,15 @@
 		.max_u  = 5000000,
 		.step_u = 50000,
 	},
+	.icl_max_stat		= {
+		.name   = "dcdc icl max status",
+		.reg    = ICL_MAX_STATUS_REG,
+		.min_u  = 0,
+		.max_u  = 5000000,
+		.step_u = 50000,
+	},
 	.icl_stat		= {
-		.name   = "input current limit status",
+		.name   = "aicl icl status",
 		.reg    = AICL_ICL_STATUS_REG,
 		.min_u  = 0,
 		.max_u  = 5000000,
@@ -177,6 +191,11 @@
 	debug_mask, __debug_mask, int, 0600
 );
 
+static int __pd_disabled;
+module_param_named(
+	pd_disabled, __pd_disabled, int, 0600
+);
+
 static int __weak_chg_icl_ua = 500000;
 module_param_named(
 	weak_chg_icl_ua, __weak_chg_icl_ua, int, 0600
@@ -216,7 +235,7 @@
 	switch (pmic_rev_id->pmic_subtype) {
 	case PM855B_SUBTYPE:
 		chip->chg.smb_version = PM855B_SUBTYPE;
-		chg->param = smb5_pmi855_params;
+		chg->param = smb5_pm855b_params;
 		chg->name = "pm855b_charger";
 		break;
 	case PMI632_SUBTYPE:
@@ -434,7 +453,6 @@
 	POWER_SUPPLY_PROP_TYPEC_MODE,
 	POWER_SUPPLY_PROP_TYPEC_POWER_ROLE,
 	POWER_SUPPLY_PROP_TYPEC_CC_ORIENTATION,
-	POWER_SUPPLY_PROP_PD_ALLOWED,
 	POWER_SUPPLY_PROP_PD_ACTIVE,
 	POWER_SUPPLY_PROP_INPUT_CURRENT_SETTLED,
 	POWER_SUPPLY_PROP_INPUT_CURRENT_NOW,
@@ -515,9 +533,6 @@
 		else
 			rc = smblib_get_prop_typec_cc_orientation(chg, val);
 		break;
-	case POWER_SUPPLY_PROP_PD_ALLOWED:
-		rc = smblib_get_prop_pd_allowed(chg, val);
-		break;
 	case POWER_SUPPLY_PROP_PD_ACTIVE:
 		val->intval = chg->pd_active;
 		break;
@@ -605,12 +620,6 @@
 	struct smb_charger *chg = &chip->chg;
 	int rc = 0;
 
-	mutex_lock(&chg->lock);
-	if (!chg->typec_present) {
-		rc = -EINVAL;
-		goto unlock;
-	}
-
 	switch (psp) {
 	case POWER_SUPPLY_PROP_PD_CURRENT_MAX:
 		rc = smblib_set_prop_pd_current_max(chg, val);
@@ -652,8 +661,6 @@
 		break;
 	}
 
-unlock:
-	mutex_unlock(&chg->lock);
 	return rc;
 }
 
@@ -1425,6 +1432,15 @@
 {
 	int rc;
 
+	/* disable apsd */
+	rc = smblib_masked_write(chg, USBIN_OPTIONS_1_CFG_REG,
+				HVDCP_EN_BIT | BC1P2_SRC_DETECT_BIT,
+				0);
+	if (rc < 0) {
+		dev_err(chg->dev, "Couldn't disable APSD rc=%d\n", rc);
+		return rc;
+	}
+
 	rc = smblib_write(chg, TYPE_C_INTERRUPT_EN_CFG_1_REG,
 				TYPEC_CCOUT_DETACH_INT_EN_BIT |
 				TYPEC_CCOUT_ATTACH_INT_EN_BIT);
@@ -1459,14 +1475,6 @@
 {
 	int rc;
 
-	/* configure micro USB mode */
-	rc = smblib_masked_write(chg, TYPEC_U_USB_CFG_REG,
-			EN_MICRO_USB_MODE_BIT, EN_MICRO_USB_MODE_BIT);
-	if (rc < 0) {
-		dev_err(chg->dev, "Couldn't enable micro USB mode rc=%d\n", rc);
-		return rc;
-	}
-
 	rc = smblib_masked_write(chg, TYPE_C_INTERRUPT_EN_CFG_2_REG,
 					MICRO_USB_STATE_CHANGE_INT_EN_BIT,
 					MICRO_USB_STATE_CHANGE_INT_EN_BIT);
@@ -1500,7 +1508,6 @@
 				&chg->default_icl_ua);
 
 	/* Use SW based VBUS control, disable HW autonomous mode */
-	/* TODO: auth can be enabled through vote based on APSD flow */
 	rc = smblib_masked_write(chg, USBIN_OPTIONS_1_CFG_REG,
 		HVDCP_AUTH_ALG_EN_CFG_BIT | HVDCP_AUTONOMOUS_MODE_EN_CFG_BIT,
 		HVDCP_AUTH_ALG_EN_CFG_BIT);
@@ -1537,10 +1544,22 @@
 		type = !!(val & EN_MICRO_USB_MODE_BIT);
 	}
 
-	chg->connector_type = type ? POWER_SUPPLY_CONNECTOR_MICRO_USB
-					: POWER_SUPPLY_CONNECTOR_TYPEC;
 	pr_debug("Connector type=%s\n", type ? "Micro USB" : "TypeC");
 
+	if (type) {
+		chg->connector_type = POWER_SUPPLY_CONNECTOR_MICRO_USB;
+		smblib_rerun_apsd_if_required(chg);
+		rc = smb5_configure_micro_usb(chg);
+	} else {
+		chg->connector_type = POWER_SUPPLY_CONNECTOR_TYPEC;
+		rc = smb5_configure_typec(chg);
+	}
+	if (rc < 0) {
+		dev_err(chg->dev,
+			"Couldn't configure TypeC/micro-USB mode rc=%d\n", rc);
+		return rc;
+	}
+
 	/*
 	 * PMI632 based hw init:
 	 * - Initialize flash module for PMI632
@@ -1548,8 +1567,6 @@
 	if (chg->smb_version == PMI632_SUBTYPE)
 		schgm_flash_init(chg);
 
-	smblib_rerun_apsd_if_required(chg);
-
 	/* vote 0mA on usb_icl for non battery platforms */
 	vote(chg->usb_icl_votable,
 		DEFAULT_VOTER, chip->dt.no_battery, 0);
@@ -1565,12 +1582,6 @@
 	vote(chg->fv_votable,
 		BATT_PROFILE_VOTER, chg->batt_profile_fv_uv > 0,
 		chg->batt_profile_fv_uv);
-	vote(chg->pd_disallowed_votable_indirect, CC_DETACHED_VOTER,
-			true, 0);
-	vote(chg->pd_disallowed_votable_indirect, APSD_VOTER,
-			true, 0);
-	vote(chg->pd_disallowed_votable_indirect, MICRO_USB_VOTER,
-		chg->connector_type == POWER_SUPPLY_CONNECTOR_MICRO_USB, 0);
 
 	/* Some h/w limit maximum supported ICL */
 	vote(chg->usb_icl_votable, HW_LIMIT_VOTER,
@@ -1594,16 +1605,6 @@
 		return rc;
 	}
 
-	if (chg->connector_type == POWER_SUPPLY_CONNECTOR_MICRO_USB)
-		rc = smb5_configure_micro_usb(chg);
-	else
-		rc = smb5_configure_typec(chg);
-	if (rc < 0) {
-		dev_err(chg->dev,
-			"Couldn't configure TypeC/micro-USB mode rc=%d\n", rc);
-		return rc;
-	}
-
 	/* configure VBUS for software control */
 	rc = smblib_masked_write(chg, DCDC_OTG_CFG_REG, OTG_EN_SRC_CFG_BIT, 0);
 	if (rc < 0) {
@@ -1812,11 +1813,21 @@
 {
 	struct smb_irq_data irq_data = {chip, "determine-initial-status"};
 	struct smb_charger *chg = &chip->chg;
+	union power_supply_propval val;
+	int rc;
+
+	rc = smblib_get_prop_usb_present(chg, &val);
+	if (rc < 0) {
+		pr_err("Couldn't get usb present rc=%d\n", rc);
+		return rc;
+	}
+	chg->early_usb_attach = val.intval;
 
 	if (chg->bms_psy)
 		smblib_suspend_on_debug_battery(chg);
 
 	usb_plugin_irq_handler(0, &irq_data);
+	typec_attach_detach_irq_handler(0, &irq_data);
 	typec_state_change_irq_handler(0, &irq_data);
 	usb_source_change_irq_handler(0, &irq_data);
 	chg_state_change_irq_handler(0, &irq_data);
@@ -2008,6 +2019,7 @@
 	},
 	[TYPEC_ATTACH_DETACH_IRQ] = {
 		.name		= "typec-attach-detach",
+		.handler	= typec_attach_detach_irq_handler,
 	},
 	[TYPEC_LEGACY_CABLE_DETECT_IRQ] = {
 		.name		= "typec-legacy-cable-detect",
@@ -2303,6 +2315,7 @@
 	chg = &chip->chg;
 	chg->dev = &pdev->dev;
 	chg->debug_mask = &__debug_mask;
+	chg->pd_disabled = &__pd_disabled;
 	chg->weak_chg_icl_ua = &__weak_chg_icl_ua;
 	chg->mode = PARALLEL_MASTER;
 	chg->irq_info = smb5_irqs;
@@ -2452,6 +2465,10 @@
 	struct smb5 *chip = platform_get_drvdata(pdev);
 	struct smb_charger *chg = &chip->chg;
 
+	/* force enable APSD */
+	smblib_masked_write(chg, USBIN_OPTIONS_1_CFG_REG,
+				BC1P2_SRC_DETECT_BIT, BC1P2_SRC_DETECT_BIT);
+
 	smb5_free_interrupts(chg);
 	smblib_deinit(chg);
 	platform_set_drvdata(pdev, NULL);
diff --git a/drivers/power/supply/qcom/qpnp-smbcharger.c b/drivers/power/supply/qcom/qpnp-smbcharger.c
index 9b5c25f..3bb4810 100644
--- a/drivers/power/supply/qcom/qpnp-smbcharger.c
+++ b/drivers/power/supply/qcom/qpnp-smbcharger.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2014-2016 The Linux Foundation. All rights reserved.
+/* Copyright (c) 2014-2016, 2018 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
@@ -8488,6 +8488,8 @@
 out:
 	handle_usb_removal(chip);
 votables_cleanup:
+	if (chip->hvdcp_enable_votable)
+		destroy_votable(chip->hvdcp_enable_votable);
 	if (chip->aicl_deglitch_short_votable)
 		destroy_votable(chip->aicl_deglitch_short_votable);
 	if (chip->hw_aicl_rerun_enable_indirect_votable)
diff --git a/drivers/power/supply/qcom/smb5-lib.c b/drivers/power/supply/qcom/smb5-lib.c
index 8756186..9d7bb83 100644
--- a/drivers/power/supply/qcom/smb5-lib.c
+++ b/drivers/power/supply/qcom/smb5-lib.c
@@ -39,6 +39,10 @@
 				__func__, ##__VA_ARGS__);	\
 	} while (0)
 
+#define typec_rp_med_high(chg, typec_mode)			\
+	((typec_mode == POWER_SUPPLY_TYPEC_SOURCE_MEDIUM	\
+	|| typec_mode == POWER_SUPPLY_TYPEC_SOURCE_HIGH)	\
+	&& !chg->typec_legacy)
 
 int smblib_read(struct smb_charger *chg, u16 addr, u8 *val)
 {
@@ -654,7 +658,7 @@
 	return 0;
 }
 
-#define USBIN_100MA	100000
+#define SDP_100_MA			100000
 static void smblib_uusb_removal(struct smb_charger *chg)
 {
 	int rc;
@@ -663,10 +667,6 @@
 
 	cancel_delayed_work_sync(&chg->pl_enable_work);
 
-	rc = smblib_request_dpdm(chg, false);
-	if (rc < 0)
-		smblib_err(chg, "Couldn't to disable DPDM rc=%d\n", rc);
-
 	if (chg->wa_flags & BOOST_BACK_WA) {
 		data = chg->irq_info[SWITCHER_POWER_OK_IRQ].irq_data;
 		if (data) {
@@ -683,6 +683,7 @@
 	/* reset both usbin current and voltage votes */
 	vote(chg->pl_enable_votable_indirect, USBIN_I_VOTER, false, 0);
 	vote(chg->pl_enable_votable_indirect, USBIN_V_VOTER, false, 0);
+	vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, true, SDP_100_MA);
 	vote(chg->usb_icl_votable, SW_QC3_VOTER, false, 0);
 
 	/* reconfigure allowed voltage for HVDCP */
@@ -705,8 +706,6 @@
 		smblib_err(chg, "Couldn't write float charger options rc=%d\n",
 			rc);
 
-	/* leave the ICL configured to 100mA for next insertion */
-	vote(chg->usb_icl_votable, DEFAULT_100MA_VOTER, true, USBIN_100MA);
 	/* clear USB ICL vote for USB_PSY_VOTER */
 	rc = vote(chg->usb_icl_votable, USB_PSY_VOTER, false, 0);
 	if (rc < 0)
@@ -772,6 +771,7 @@
 }
 
 #define USBIN_25MA	25000
+#define USBIN_100MA	100000
 #define USBIN_150MA	150000
 #define USBIN_500MA	500000
 #define USBIN_900MA	900000
@@ -885,6 +885,10 @@
 set_mode:
 	rc = smblib_masked_write(chg, USBIN_ICL_OPTIONS_REG,
 		USBIN_MODE_CHG_BIT, hc_mode ? USBIN_MODE_CHG_BIT : 0);
+	if (rc < 0) {
+		smblib_err(chg, "Couldn't set USBIN_ICL_OPTIONS rc=%d\n", rc);
+		goto out;
+	}
 
 	/* unsuspend after configuring current and override */
 	rc = smblib_set_usb_suspend(chg, false);
@@ -925,7 +929,8 @@
 			return INT_MAX;
 
 		/* override is set */
-		rc = smblib_get_charge_param(chg, &chg->param.usb_icl, icl_ua);
+		rc = smblib_get_charge_param(chg, &chg->param.icl_max_stat,
+					icl_ua);
 		if (rc < 0) {
 			smblib_err(chg, "Couldn't get HC ICL rc=%d\n", rc);
 			return rc;
@@ -954,18 +959,6 @@
 	return smblib_set_dc_suspend(chg, (bool)suspend);
 }
 
-static int smblib_pd_disallowed_votable_indirect_callback(
-	struct votable *votable, void *data, int disallowed, const char *client)
-{
-	struct smb_charger *chg = data;
-	int rc;
-
-	rc = vote(chg->pd_allowed_votable, PD_DISALLOWED_INDIRECT_VOTER,
-		!disallowed, 0);
-
-	return rc;
-}
-
 static int smblib_awake_vote_callback(struct votable *votable, void *data,
 			int awake, const char *client)
 {
@@ -1989,13 +1982,6 @@
 	return rc;
 }
 
-int smblib_get_prop_pd_allowed(struct smb_charger *chg,
-			       union power_supply_propval *val)
-{
-	val->intval = get_effective_result(chg->pd_allowed_votable);
-	return 0;
-}
-
 int smblib_get_prop_input_current_settled(struct smb_charger *chg,
 					  union power_supply_propval *val)
 {
@@ -2039,13 +2025,7 @@
 int smblib_get_pe_start(struct smb_charger *chg,
 			       union power_supply_propval *val)
 {
-	/*
-	 * hvdcp timeout voter is the last one to allow pd. Use its vote
-	 * to indicate start of pe engine
-	 */
-	val->intval
-		= !get_client_vote_locked(chg->pd_disallowed_votable_indirect,
-			APSD_VOTER);
+	val->intval = chg->ok_to_pd;
 	return 0;
 }
 
@@ -2132,24 +2112,40 @@
 
 	if (chg->real_charger_type == POWER_SUPPLY_TYPE_USB_FLOAT) {
 		if (usb_current == -ETIMEDOUT) {
-			/*
-			 * Valid FLOAT charger, report the current based
-			 * of Rp
-			 */
+			if ((chg->float_cfg & FLOAT_OPTIONS_MASK)
+						== FORCE_FLOAT_SDP_CFG_BIT) {
+				/*
+				 * Confiugure USB500 mode if Float charger is
+				 * configured for SDP.
+				 */
+				rc = set_sdp_current(chg, USBIN_500MA);
+				if (rc < 0)
+					smblib_err(chg,
+						"Couldn't set SDP ICL rc=%d\n",
+						rc);
+
+				return rc;
+			}
+
 			if (chg->connector_type ==
 					POWER_SUPPLY_CONNECTOR_TYPEC) {
+				/*
+				 * Valid FLOAT charger, report the current
+				 * based of Rp.
+				 */
 				typec_mode = smblib_get_prop_typec_mode(chg);
 				rp_ua = get_rp_based_dcp_current(chg,
 								typec_mode);
 				rc = vote(chg->usb_icl_votable,
-						DYNAMIC_RP_VOTER, true, rp_ua);
-				if (rc < 0) {
-					pr_err("Couldn't vote ICL DYNAMIC_RP_VOTER rc=%d\n",
-							rc);
+					SW_ICL_MAX_VOTER, true, rp_ua);
+				if (rc < 0)
 					return rc;
-				}
+			} else {
+				rc = vote(chg->usb_icl_votable,
+					SW_ICL_MAX_VOTER, true, DCP_CURRENT_UA);
+				if (rc < 0)
+					return rc;
 			}
-			/* No specific handling required for micro-USB */
 		} else {
 			/*
 			 * FLOAT charger detected as SDP by USB driver,
@@ -2158,12 +2154,13 @@
 			 */
 			chg->real_charger_type = POWER_SUPPLY_TYPE_USB;
 			rc = vote(chg->usb_icl_votable, USB_PSY_VOTER,
-							true, usb_current);
-			if (rc < 0) {
-				pr_err("Couldn't vote ICL USB_PSY_VOTER rc=%d\n",
-						rc);
+						true, usb_current);
+			if (rc < 0)
 				return rc;
-			}
+			rc = vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER,
+							false, 0);
+			if (rc < 0)
+				return rc;
 		}
 	} else {
 		rc = vote(chg->usb_icl_votable, USB_PSY_VOTER,
@@ -2173,12 +2170,12 @@
 			return rc;
 		}
 
-	}
+		rc = vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, false, 0);
+		if (rc < 0) {
+			pr_err("Couldn't remove SW_ICL_MAX vote rc=%d\n", rc);
+			return rc;
+		}
 
-	rc = vote(chg->usb_icl_votable, DEFAULT_100MA_VOTER, false, 0);
-	if (rc < 0) {
-		pr_err("Couldn't unvote ICL DEFAULT_100MA_VOTER rc=%d\n", rc);
-		return rc;
 	}
 
 	return 0;
@@ -2298,14 +2295,14 @@
 	return rc;
 }
 
-static int __smblib_set_prop_pd_active(struct smb_charger *chg, bool pd_active)
+int smblib_set_prop_pd_active(struct smb_charger *chg,
+				const union power_supply_propval *val)
 {
 	int rc = 0;
 
-	chg->pd_active = pd_active;
+	chg->pd_active = val->intval;
 
 	if (chg->pd_active) {
-		vote(chg->pd_allowed_votable, PD_VOTER, true, 0);
 		vote(chg->usb_irq_enable_votable, PD_VOTER, true, 0);
 
 		/*
@@ -2313,24 +2310,26 @@
 		 * It is guaranteed that pd_active is set prior to
 		 * pd_current_max
 		 */
-		rc = vote(chg->usb_icl_votable, PD_VOTER, true, USBIN_500MA);
-		if (rc < 0)
-			smblib_err(chg, "Couldn't vote for USB ICL rc=%d\n",
-									rc);
-
-		/* clear USB ICL vote for DCP_VOTER */
-		rc = vote(chg->usb_icl_votable, DCP_VOTER, false, 0);
-		if (rc < 0)
-			smblib_err(chg, "Couldn't un-vote DCP from USB ICL rc=%d\n",
-									rc);
-
-		/* remove USB_PSY_VOTER */
-		rc = vote(chg->usb_icl_votable, USB_PSY_VOTER, false, 0);
-		if (rc < 0)
-			smblib_err(chg, "Couldn't unvote USB_PSY rc=%d\n", rc);
+		vote(chg->usb_icl_votable, PD_VOTER, true, USBIN_500MA);
+		vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, false, 0);
 	} else {
-		vote(chg->pd_allowed_votable, PD_VOTER, true, 0);
+		vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, true, SDP_100_MA);
+		vote(chg->usb_icl_votable, PD_VOTER, false, 0);
 		vote(chg->usb_irq_enable_votable, PD_VOTER, false, 0);
+
+		/* PD hard resets failed, rerun apsd */
+		if (chg->ok_to_pd) {
+			chg->ok_to_pd = false;
+			rc = smblib_masked_write(chg, USBIN_OPTIONS_1_CFG_REG,
+					HVDCP_EN_BIT | BC1P2_SRC_DETECT_BIT,
+					HVDCP_EN_BIT | BC1P2_SRC_DETECT_BIT);
+			if (rc < 0) {
+				dev_err(chg->dev,
+					"Couldn't disable APSD rc=%d\n", rc);
+				return rc;
+			}
+			smblib_rerun_apsd(chg);
+		}
 	}
 
 	smblib_update_usb_type(chg);
@@ -2338,15 +2337,6 @@
 	return rc;
 }
 
-int smblib_set_prop_pd_active(struct smb_charger *chg,
-			      const union power_supply_propval *val)
-{
-	if (!get_effective_result(chg->pd_allowed_votable))
-		return -EINVAL;
-
-	return __smblib_set_prop_pd_active(chg, val->intval);
-}
-
 int smblib_set_prop_ship_mode(struct smb_charger *chg,
 				const union power_supply_propval *val)
 {
@@ -2657,11 +2647,7 @@
 
 static void smblib_micro_usb_plugin(struct smb_charger *chg, bool vbus_rising)
 {
-	if (vbus_rising) {
-		/* use the typec flag even though its not typec */
-		chg->typec_present = 1;
-	} else {
-		chg->typec_present = 0;
+	if (!vbus_rising) {
 		smblib_update_usb_type(chg);
 		smblib_notify_device_mode(chg, false);
 		smblib_uusb_removal(chg);
@@ -2764,12 +2750,10 @@
 	struct smb_irq_data *irq_data = data;
 	struct smb_charger *chg = irq_data->parent_data;
 
-	mutex_lock(&chg->lock);
 	if (chg->pd_hard_reset)
 		smblib_usb_plugin_hard_reset_locked(chg);
 	else
 		smblib_usb_plugin_locked(chg);
-	mutex_unlock(&chg->lock);
 
 	return IRQ_HANDLED;
 }
@@ -2858,8 +2842,6 @@
 	if (!rising)
 		return;
 
-	vote(chg->pd_disallowed_votable_indirect, APSD_VOTER, false, 0);
-
 	if (chg->mode == PARALLEL_MASTER)
 		vote(chg->pl_enable_votable_indirect, USBIN_V_VOTER, true, 0);
 
@@ -2872,33 +2854,18 @@
 static void smblib_handle_hvdcp_check_timeout(struct smb_charger *chg,
 					      bool rising, bool qc_charger)
 {
-	const struct apsd_result *apsd_result = smblib_get_apsd_result(chg);
-
-	/* Hold off PD only until hvdcp 2.0 detection timeout */
 	if (rising) {
 
-		/* enable HDC and ICL irq for QC2/3 charger */
-		if (qc_charger)
+		if (qc_charger) {
+			/* enable HDC and ICL irq for QC2/3 charger */
 			vote(chg->usb_irq_enable_votable, QC_VOTER, true, 0);
-		else
-			vote(chg->pd_disallowed_votable_indirect, APSD_VOTER,
-							false, 0);
-
-		/*
-		 * HVDCP detection timeout done
-		 * If adapter is not QC2.0/QC3.0 - it is a plain old DCP.
-		 */
-		if (!qc_charger && (apsd_result->bit & DCP_CHARGER_BIT))
-			/* enforce DCP ICL if specified */
+			vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, true,
+				HVDCP_CURRENT_UA);
+		} else {
+			/* A plain DCP, enforce DCP ICL if specified */
 			vote(chg->usb_icl_votable, DCP_VOTER,
 				chg->dcp_icl_ua != -EINVAL, chg->dcp_icl_ua);
-
-		/*
-		 * if pd is not allowed, then set pd_active = false right here,
-		 * so that it starts the hvdcp engine
-		 */
-		if (!get_effective_result(chg->pd_allowed_votable))
-			__smblib_set_prop_pd_active(chg, 0);
+		}
 	}
 
 	smblib_dbg(chg, PR_INTERRUPT, "IRQ: %s %s\n", __func__,
@@ -2913,6 +2880,69 @@
 		   rising ? "rising" : "falling");
 }
 
+static void update_sw_icl_max(struct smb_charger *chg, int pst)
+{
+	int typec_mode;
+	int rp_ua;
+
+	/* while PD is active it should have complete ICL control */
+	if (chg->pd_active)
+		return;
+
+	/*
+	 * HVDCP 2/3, handled separately
+	 * For UNKNOWN(input not present) return without updating ICL
+	 */
+	if (pst == POWER_SUPPLY_TYPE_USB_HVDCP
+			|| pst == POWER_SUPPLY_TYPE_USB_HVDCP_3
+			|| pst == POWER_SUPPLY_TYPE_UNKNOWN)
+		return;
+
+	/* TypeC rp med or high, use rp value */
+	typec_mode = smblib_get_prop_typec_mode(chg);
+	if (typec_rp_med_high(chg, typec_mode)) {
+		rp_ua = get_rp_based_dcp_current(chg, typec_mode);
+		vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, true, rp_ua);
+		return;
+	}
+
+	/* rp-std or legacy, USB BC 1.2 */
+	switch (pst) {
+	case POWER_SUPPLY_TYPE_USB:
+		/*
+		 * USB_PSY will vote to increase the current to 500/900mA once
+		 * enumeration is done.
+		 */
+		if (!is_client_vote_enabled(chg->usb_icl_votable,
+								USB_PSY_VOTER))
+			vote(chg->usb_icl_votable, USB_PSY_VOTER, true,
+					SDP_100_MA);
+		vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, false, 0);
+		break;
+	case POWER_SUPPLY_TYPE_USB_CDP:
+		vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, true,
+					CDP_CURRENT_UA);
+		break;
+	case POWER_SUPPLY_TYPE_USB_DCP:
+		vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, true,
+					DCP_CURRENT_UA);
+		break;
+	case POWER_SUPPLY_TYPE_USB_FLOAT:
+		/*
+		 * limit ICL to 100mA, the USB driver will enumerate to check
+		 * if this is a SDP and appropriately set the current
+		 */
+		vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, true,
+					SDP_100_MA);
+		break;
+	default:
+		smblib_err(chg, "Unknown APSD %d; forcing 500mA\n", pst);
+		vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, true,
+					SDP_CURRENT_UA);
+		break;
+	}
+}
+
 static void smblib_handle_apsd_done(struct smb_charger *chg, bool rising)
 {
 	const struct apsd_result *apsd_result;
@@ -2922,25 +2952,18 @@
 
 	apsd_result = smblib_update_usb_type(chg);
 
+	update_sw_icl_max(chg, apsd_result->pst);
+
 	switch (apsd_result->bit) {
 	case SDP_CHARGER_BIT:
 	case CDP_CHARGER_BIT:
 	case FLOAT_CHARGER_BIT:
-		/* if not DCP then no hvdcp timeout happens. Enable pd here */
-		vote(chg->pd_disallowed_votable_indirect, APSD_VOTER,
-				false, 0);
 		if ((chg->connector_type == POWER_SUPPLY_CONNECTOR_MICRO_USB)
 				|| chg->use_extcon)
 			smblib_notify_device_mode(chg, true);
 		break;
 	case OCP_CHARGER_BIT:
-		vote(chg->usb_icl_votable, DEFAULT_100MA_VOTER, false, 0);
-		/* if not DCP then no hvdcp timeout happens, Enable pd here. */
-		vote(chg->pd_disallowed_votable_indirect, APSD_VOTER,
-				false, 0);
-		break;
 	case DCP_CHARGER_BIT:
-		vote(chg->usb_icl_votable, DEFAULT_100MA_VOTER, false, 0);
 		break;
 	default:
 		break;
@@ -2957,6 +2980,14 @@
 	int rc = 0;
 	u8 stat;
 
+	/*
+	 * Prepared to run PD or PD is active. At this moment, APSD is disabled,
+	 * but there still can be irq on apsd_done from previously unfinished
+	 * APSD run, skip it.
+	 */
+	if (chg->ok_to_pd)
+		return IRQ_HANDLED;
+
 	rc = smblib_read(chg, APSD_STATUS_REG, &stat);
 	if (rc < 0) {
 		smblib_err(chg, "Couldn't read APSD_STATUS rc=%d\n", rc);
@@ -3011,32 +3042,68 @@
 
 static void typec_sink_insertion(struct smb_charger *chg)
 {
-	/* when a sink is inserted we should not wait on hvdcp timeout to
-	 * enable pd
-	 */
-	vote(chg->pd_disallowed_votable_indirect, APSD_VOTER, false, 0);
+	vote(chg->usb_icl_votable, OTG_VOTER, true, 0);
+
 	if (chg->use_extcon) {
 		smblib_notify_usb_host(chg, true);
 		chg->otg_present = true;
 	}
+
+	if (!chg->pr_swap_in_progress)
+		chg->ok_to_pd = !(*chg->pd_disabled) || chg->early_usb_attach;
+}
+
+static void typec_src_insertion(struct smb_charger *chg)
+{
+	int rc = 0;
+	u8 stat;
+
+	if (chg->pr_swap_in_progress)
+		return;
+
+	rc = smblib_read(chg, LEGACY_CABLE_STATUS_REG, &stat);
+	if (rc < 0) {
+		smblib_err(chg, "Couldn't read TYPE_C_STATE_MACHINE_STATUS_REG rc=%d\n",
+			rc);
+		return;
+	}
+
+	chg->typec_legacy = stat & TYPEC_LEGACY_CABLE_STATUS_BIT;
+	chg->ok_to_pd = !(chg->typec_legacy || *chg->pd_disabled)
+						|| chg->early_usb_attach;
+	if (!chg->ok_to_pd) {
+		rc = smblib_masked_write(chg, USBIN_OPTIONS_1_CFG_REG,
+				HVDCP_EN_BIT | BC1P2_SRC_DETECT_BIT,
+				HVDCP_EN_BIT | BC1P2_SRC_DETECT_BIT);
+		if (rc < 0) {
+			dev_err(chg->dev,
+				"Couldn't disable APSD rc=%d\n", rc);
+			return;
+		}
+		smblib_rerun_apsd(chg);
+	}
 }
 
 static void typec_sink_removal(struct smb_charger *chg)
 {
-	smblib_set_charge_param(chg, &chg->param.freq_switcher,
-			chg->chg_freq.freq_above_otg_threshold);
-	chg->boost_current_ua = 0;
+	vote(chg->usb_icl_votable, OTG_VOTER, false, 0);
 }
 
-static void smblib_handle_typec_removal(struct smb_charger *chg)
+static void typec_src_removal(struct smb_charger *chg)
 {
 	int rc;
 	struct smb_irq_data *data;
 	struct storm_watch *wdata;
 
-	rc = smblib_request_dpdm(chg, false);
+	/* disable apsd */
+	rc = smblib_masked_write(chg, USBIN_OPTIONS_1_CFG_REG,
+				HVDCP_EN_BIT | BC1P2_SRC_DETECT_BIT,
+				0);
 	if (rc < 0)
-		smblib_err(chg, "Couldn't disable DPDM rc=%d\n", rc);
+		smblib_err(chg,
+			"Couldn't disable APSD rc=%d\n", rc);
+
+	smblib_update_usb_type(chg);
 
 	if (chg->wa_flags & BOOST_BACK_WA) {
 		data = chg->irq_info[SWITCHER_POWER_OK_IRQ].irq_data;
@@ -3052,7 +3119,7 @@
 	cancel_delayed_work_sync(&chg->pl_enable_work);
 
 	/* reset input current limit voters */
-	vote(chg->usb_icl_votable, DEFAULT_100MA_VOTER, true, USBIN_100MA);
+	vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, true, SDP_100_MA);
 	vote(chg->usb_icl_votable, PD_VOTER, false, 0);
 	vote(chg->usb_icl_votable, USB_PSY_VOTER, false, 0);
 	vote(chg->usb_icl_votable, DCP_VOTER, false, 0);
@@ -3060,12 +3127,6 @@
 	vote(chg->usb_icl_votable, SW_QC3_VOTER, false, 0);
 	vote(chg->usb_icl_votable, OTG_VOTER, false, 0);
 	vote(chg->usb_icl_votable, CTM_VOTER, false, 0);
-	vote(chg->usb_icl_votable, DYNAMIC_RP_VOTER, false, 0);
-
-	/* reset power delivery voters */
-	vote(chg->pd_allowed_votable, PD_VOTER, false, 0);
-	vote(chg->pd_disallowed_votable_indirect, CC_DETACHED_VOTER, true, 0);
-	vote(chg->pd_disallowed_votable_indirect, APSD_VOTER, true, 0);
 
 	/* reset usb irq voters */
 	vote(chg->usb_irq_enable_votable, PD_VOTER, false, 0);
@@ -3078,14 +3139,10 @@
 	vote(chg->pl_enable_votable_indirect, USBIN_V_VOTER, false, 0);
 	vote(chg->awake_votable, PL_DELAY_VOTER, false, 0);
 
-	chg->vconn_attempts = 0;
-	chg->otg_attempts = 0;
 	chg->pulse_cnt = 0;
 	chg->usb_icl_delta_ua = 0;
 	chg->voltage_min_uv = MICRO_5V;
 	chg->voltage_max_uv = MICRO_5V;
-	chg->pd_active = 0;
-	chg->pd_hard_reset = 0;
 
 	/* write back the default FLOAT charger configuration */
 	rc = smblib_masked_write(chg, USBIN_OPTIONS_2_CFG_REG,
@@ -3094,12 +3151,6 @@
 		smblib_err(chg, "Couldn't write float charger options rc=%d\n",
 			rc);
 
-	/* reset back to 103mS tCC debounce */
-	rc = smblib_masked_write(chg, TYPE_C_DEBOUNCE_OPTION_REG,
-					REDUCE_TCCDEBOUNCE_TO_2MS_BIT, 0);
-	if (rc < 0)
-		smblib_err(chg, "Couldn't set 120mS tCC debounce rc=%d\n", rc);
-
 	/* reconfigure allowed voltage for HVDCP */
 	rc = smblib_set_adapter_allowance(chg,
 			USBIN_ADAPTER_ALLOW_5V_OR_9V_TO_12V);
@@ -3107,157 +3158,40 @@
 		smblib_err(chg, "Couldn't set USBIN_ADAPTER_ALLOW_5V_OR_9V_TO_12V rc=%d\n",
 			rc);
 
-	/* enable DRP */
-	rc = smblib_masked_write(chg, TYPE_C_MODE_CFG_REG,
-				 TYPEC_POWER_ROLE_CMD_MASK, 0);
-	if (rc < 0)
-		smblib_err(chg, "Couldn't enable DRP rc=%d\n", rc);
-
-	/* HW controlled CC_OUT */
-	rc = smblib_masked_write(chg, TYPE_C_CCOUT_CONTROL_REG,
-				TYPEC_CCOUT_SRC_BIT, 0);
-	if (rc < 0)
-		smblib_err(chg, "Couldn't enable HW cc_out rc=%d\n", rc);
-
-	/* clear exit sink based on cc */
-	rc = smblib_masked_write(chg, TYPE_C_EXIT_STATE_CFG_REG,
-						EXIT_SNK_BASED_ON_CC_BIT, 0);
-	if (rc < 0)
-		smblib_err(chg, "Couldn't clear exit_sink_based_on_cc rc=%d\n",
-				rc);
-
-	typec_sink_removal(chg);
-	smblib_update_usb_type(chg);
-
 	if (chg->use_extcon) {
 		if (chg->otg_present)
 			smblib_notify_usb_host(chg, false);
 		else
 			smblib_notify_device_mode(chg, false);
 	}
+
 	chg->otg_present = false;
-}
-
-static void smblib_handle_typec_insertion(struct smb_charger *chg)
-{
-	int rc;
-	u8 stat;
-
-	vote(chg->pd_disallowed_votable_indirect, CC_DETACHED_VOTER, false, 0);
-
-	rc = smblib_read(chg, TYPE_C_MISC_STATUS_REG, &stat);
-	if (rc < 0) {
-		smblib_err(chg, "Couldn't read TYPE_C_STATUS_4 rc=%d\n", rc);
-		return;
-	}
-
-	if (stat & SNK_SRC_MODE_BIT) {
-		typec_sink_insertion(chg);
-	} else {
-		rc = smblib_request_dpdm(chg, true);
-		if (rc < 0)
-			smblib_err(chg, "Couldn't to enable DPDM rc=%d\n", rc);
-		typec_sink_removal(chg);
-	}
+	chg->typec_legacy = false;
 }
 
 static void smblib_handle_rp_change(struct smb_charger *chg, int typec_mode)
 {
-	int rp_ua;
 	const struct apsd_result *apsd = smblib_get_apsd_result(chg);
 
-	if ((apsd->pst != POWER_SUPPLY_TYPE_USB_DCP)
-		&& (apsd->pst != POWER_SUPPLY_TYPE_USB_FLOAT))
-		return;
-
-	/*
-	 * if APSD indicates FLOAT and the USB stack had detected SDP,
-	 * do not respond to Rp changes as we do not confirm that its
-	 * a legacy cable
-	 */
-	if (chg->real_charger_type == POWER_SUPPLY_TYPE_USB)
-		return;
 	/*
 	 * We want the ICL vote @ 100mA for a FLOAT charger
 	 * until the detection by the USB stack is complete.
 	 * Ignore the Rp changes unless there is a
-	 * pre-existing valid vote.
+	 * pre-existing valid vote or FLOAT is configured for
+	 * SDP current.
 	 */
-	if (apsd->pst == POWER_SUPPLY_TYPE_USB_FLOAT &&
-		(get_client_vote(chg->usb_icl_votable, DEFAULT_100MA_VOTER)
-					<= USBIN_100MA))
+	if (apsd->pst == POWER_SUPPLY_TYPE_USB_FLOAT) {
+		if (get_client_vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER)
+					<= USBIN_100MA
+			|| (chg->float_cfg & FLOAT_OPTIONS_MASK)
+					== FORCE_FLOAT_SDP_CFG_BIT)
 		return;
+	}
 
-	/*
-	 * handle Rp change for DCP/FLOAT/OCP.
-	 * Update the current only if the Rp is different from
-	 * the last Rp value.
-	 */
+	update_sw_icl_max(chg, apsd->pst);
+
 	smblib_dbg(chg, PR_MISC, "CC change old_mode=%d new_mode=%d\n",
 						chg->typec_mode, typec_mode);
-
-	rp_ua = get_rp_based_dcp_current(chg, typec_mode);
-	vote(chg->usb_icl_votable, DYNAMIC_RP_VOTER, true, rp_ua);
-}
-
-static void smblib_handle_typec_cc_state_change(struct smb_charger *chg)
-{
-	u8 stat;
-	int typec_mode, rc;
-
-	if (chg->pr_swap_in_progress)
-		return;
-
-	typec_mode = smblib_get_prop_typec_mode(chg);
-	if (chg->typec_present && (typec_mode != chg->typec_mode))
-		smblib_handle_rp_change(chg, typec_mode);
-
-	chg->typec_mode = typec_mode;
-
-	if (!chg->typec_present && chg->typec_mode != POWER_SUPPLY_TYPEC_NONE) {
-		chg->typec_present = true;
-		smblib_dbg(chg, PR_MISC, "TypeC %s insertion\n",
-			smblib_typec_mode_name[chg->typec_mode]);
-		smblib_handle_typec_insertion(chg);
-	} else if (chg->typec_present &&
-				chg->typec_mode == POWER_SUPPLY_TYPEC_NONE) {
-		chg->typec_present = false;
-		smblib_dbg(chg, PR_MISC, "TypeC removal\n");
-		smblib_handle_typec_removal(chg);
-	}
-
-	rc = smblib_read(chg, TYPE_C_MISC_STATUS_REG, &stat);
-	if (rc < 0) {
-		smblib_err(chg, "Couldn't read TYPE_C_STATUS_4 rc=%d\n", rc);
-		return;
-	}
-	/* suspend usb if sink */
-	if ((stat & SNK_SRC_MODE_BIT) && chg->typec_present)
-		vote(chg->usb_icl_votable, OTG_VOTER, true, 0);
-	else
-		vote(chg->usb_icl_votable, OTG_VOTER, false, 0);
-
-	smblib_dbg(chg, PR_INTERRUPT, "IRQ: cc-state-change; Type-C %s detected\n",
-				smblib_typec_mode_name[chg->typec_mode]);
-}
-
-static void smblib_usb_typec_change(struct smb_charger *chg)
-{
-	int rc;
-	u8 stat;
-
-	smblib_handle_typec_cc_state_change(chg);
-
-	rc = smblib_read(chg, TYPE_C_MISC_STATUS_REG, &stat);
-	if (rc < 0) {
-		smblib_err(chg, "Couldn't read TYPE_C_STATUS_4 rc=%d\n", rc);
-		return;
-	}
-
-	if (stat & TYPEC_VBUS_ERROR_STATUS_BIT)
-		smblib_dbg(chg, PR_INTERRUPT, "IRQ: vbus-error\n");
-
-	power_supply_changed(chg->usb_psy);
 }
 
 irqreturn_t typec_or_rid_detection_change_irq_handler(int irq, void *data)
@@ -3281,17 +3215,81 @@
 {
 	struct smb_irq_data *irq_data = data;
 	struct smb_charger *chg = irq_data->parent_data;
+	int typec_mode;
 
-	if ((chg->connector_type == POWER_SUPPLY_CONNECTOR_MICRO_USB)
-			|| chg->pr_swap_in_progress) {
+	if (chg->connector_type == POWER_SUPPLY_CONNECTOR_MICRO_USB) {
 		smblib_dbg(chg, PR_INTERRUPT,
-				"Ignoring since pr_swap_in_progress\n");
+				"Ignoring for micro USB\n");
 		return IRQ_HANDLED;
 	}
 
-	mutex_lock(&chg->lock);
-	smblib_usb_typec_change(chg);
-	mutex_unlock(&chg->lock);
+	typec_mode = smblib_get_prop_typec_mode(chg);
+	if (chg->sink_src_mode != UNATTACHED_MODE
+			&& (typec_mode != chg->typec_mode))
+		smblib_handle_rp_change(chg, typec_mode);
+	chg->typec_mode = typec_mode;
+
+	smblib_dbg(chg, PR_INTERRUPT, "IRQ: cc-state-change; Type-C %s detected\n",
+				smblib_typec_mode_name[chg->typec_mode]);
+
+	power_supply_changed(chg->usb_psy);
+
+	return IRQ_HANDLED;
+}
+
+irqreturn_t typec_attach_detach_irq_handler(int irq, void *data)
+{
+	struct smb_irq_data *irq_data = data;
+	struct smb_charger *chg = irq_data->parent_data;
+	u8 stat;
+	int rc;
+
+	smblib_dbg(chg, PR_INTERRUPT, "IRQ: %s\n", irq_data->name);
+
+	rc = smblib_read(chg, TYPE_C_STATE_MACHINE_STATUS_REG, &stat);
+	if (rc < 0) {
+		smblib_err(chg, "Couldn't read TYPE_C_STATE_MACHINE_STATUS_REG rc=%d\n",
+			rc);
+		return IRQ_HANDLED;
+	}
+
+	if (stat & TYPEC_ATTACH_DETACH_STATE_BIT) {
+		rc = smblib_read(chg, TYPE_C_MISC_STATUS_REG, &stat);
+		if (rc < 0) {
+			smblib_err(chg, "Couldn't read TYPE_C_MISC_STATUS_REG rc=%d\n",
+				rc);
+			return IRQ_HANDLED;
+		}
+
+		if (stat & SNK_SRC_MODE_BIT) {
+			chg->sink_src_mode = SRC_MODE;
+			typec_sink_insertion(chg);
+		} else {
+			chg->sink_src_mode = SINK_MODE;
+			typec_src_insertion(chg);
+		}
+
+	} else {
+		switch (chg->sink_src_mode) {
+		case SRC_MODE:
+			typec_sink_removal(chg);
+			break;
+		case SINK_MODE:
+			typec_src_removal(chg);
+			break;
+		default:
+			break;
+		}
+
+		if (!chg->pr_swap_in_progress) {
+			chg->ok_to_pd = false;
+			chg->sink_src_mode = UNATTACHED_MODE;
+			chg->early_usb_attach = false;
+		}
+	}
+
+	power_supply_changed(chg->usb_psy);
+
 	return IRQ_HANDLED;
 }
 
@@ -3426,19 +3424,55 @@
 				const union power_supply_propval *val)
 {
 	int rc;
+	u8 stat = 0, orientation;
 
 	chg->pr_swap_in_progress = val->intval;
 
-	/*
-	 * call the cc changed irq to handle real removals while
-	 * PR_SWAP was in progress
-	 */
-	smblib_usb_typec_change(chg);
 	rc = smblib_masked_write(chg, TYPE_C_DEBOUNCE_OPTION_REG,
 			REDUCE_TCCDEBOUNCE_TO_2MS_BIT,
 			val->intval ? REDUCE_TCCDEBOUNCE_TO_2MS_BIT : 0);
 	if (rc < 0)
 		smblib_err(chg, "Couldn't set tCC debounce rc=%d\n", rc);
+
+	rc = smblib_masked_write(chg, TYPE_C_EXIT_STATE_CFG_REG,
+			BYPASS_VSAFE0V_DURING_ROLE_SWAP_BIT,
+			val->intval ? BYPASS_VSAFE0V_DURING_ROLE_SWAP_BIT : 0);
+	if (rc < 0)
+		smblib_err(chg, "Couldn't set exit state cfg rc=%d\n", rc);
+
+	if (chg->pr_swap_in_progress) {
+		rc = smblib_read(chg, TYPE_C_MISC_STATUS_REG, &stat);
+		if (rc < 0) {
+			smblib_err(chg, "Couldn't read TYPE_C_STATUS_4 rc=%d\n",
+				rc);
+		}
+
+		orientation =
+			stat & CC_ORIENTATION_BIT ? TYPEC_CCOUT_VALUE_BIT : 0;
+		rc = smblib_masked_write(chg, TYPE_C_CCOUT_CONTROL_REG,
+			TYPEC_CCOUT_SRC_BIT | TYPEC_CCOUT_BUFFER_EN_BIT
+					| TYPEC_CCOUT_VALUE_BIT,
+			TYPEC_CCOUT_SRC_BIT | TYPEC_CCOUT_BUFFER_EN_BIT
+					| orientation);
+		if (rc < 0) {
+			smblib_err(chg, "Couldn't read TYPE_C_CCOUT_CONTROL_REG rc=%d\n",
+				rc);
+		}
+	} else {
+		rc = smblib_masked_write(chg, TYPE_C_CCOUT_CONTROL_REG,
+			TYPEC_CCOUT_SRC_BIT, 0);
+		if (rc < 0) {
+			smblib_err(chg, "Couldn't read TYPE_C_CCOUT_CONTROL_REG rc=%d\n",
+				rc);
+		}
+
+		/* enable DRP */
+		rc = smblib_masked_write(chg, TYPE_C_MODE_CFG_REG,
+				 TYPEC_POWER_ROLE_CMD_MASK, 0);
+		if (rc < 0)
+			smblib_err(chg, "Couldn't enable DRP rc=%d\n", rc);
+	}
+
 	return 0;
 }
 
@@ -3681,23 +3715,6 @@
 		return rc;
 	}
 
-	chg->pd_disallowed_votable_indirect
-		= create_votable("PD_DISALLOWED_INDIRECT", VOTE_SET_ANY,
-			smblib_pd_disallowed_votable_indirect_callback, chg);
-	if (IS_ERR(chg->pd_disallowed_votable_indirect)) {
-		rc = PTR_ERR(chg->pd_disallowed_votable_indirect);
-		chg->pd_disallowed_votable_indirect = NULL;
-		return rc;
-	}
-
-	chg->pd_allowed_votable = create_votable("PD_ALLOWED",
-					VOTE_SET_ANY, NULL, NULL);
-	if (IS_ERR(chg->pd_allowed_votable)) {
-		rc = PTR_ERR(chg->pd_allowed_votable);
-		chg->pd_allowed_votable = NULL;
-		return rc;
-	}
-
 	chg->awake_votable = create_votable("AWAKE", VOTE_SET_ANY,
 					smblib_awake_vote_callback,
 					chg);
@@ -3735,10 +3752,6 @@
 		destroy_votable(chg->dc_suspend_votable);
 	if (chg->usb_icl_votable)
 		destroy_votable(chg->usb_icl_votable);
-	if (chg->pd_disallowed_votable_indirect)
-		destroy_votable(chg->pd_disallowed_votable_indirect);
-	if (chg->pd_allowed_votable)
-		destroy_votable(chg->pd_allowed_votable);
 	if (chg->awake_votable)
 		destroy_votable(chg->awake_votable);
 	if (chg->chg_disable_votable)
@@ -3750,7 +3763,6 @@
 	int rc = 0;
 
 	mutex_init(&chg->lock);
-	mutex_init(&chg->otg_oc_lock);
 	INIT_WORK(&chg->bms_update_work, bms_update_work);
 	INIT_WORK(&chg->pl_update_work, pl_update_work);
 	INIT_WORK(&chg->jeita_update_work, jeita_update_work);
@@ -3763,6 +3775,7 @@
 	chg->fake_input_current_limited = -EINVAL;
 	chg->fake_batt_status = -EINVAL;
 	chg->jeita_configured = false;
+	chg->sink_src_mode = UNATTACHED_MODE;
 
 	switch (chg->mode) {
 	case PARALLEL_MASTER:
diff --git a/drivers/power/supply/qcom/smb5-lib.h b/drivers/power/supply/qcom/smb5-lib.h
index 5d6d3a6..7e2558c 100644
--- a/drivers/power/supply/qcom/smb5-lib.h
+++ b/drivers/power/supply/qcom/smb5-lib.h
@@ -56,6 +56,7 @@
 #define CTM_VOTER			"CTM_VOTER"
 #define SW_QC3_VOTER			"SW_QC3_VOTER"
 #define AICL_RERUN_VOTER		"AICL_RERUN_VOTER"
+#define SW_ICL_MAX_VOTER		"SW_ICL_MAX_VOTER"
 #define QNOVO_VOTER			"QNOVO_VOTER"
 #define BATT_PROFILE_VOTER		"BATT_PROFILE_VOTER"
 #define OTG_DELAY_VOTER			"OTG_DELAY_VOTER"
@@ -65,8 +66,6 @@
 #define PL_FCC_LOW_VOTER		"PL_FCC_LOW_VOTER"
 #define WBC_VOTER			"WBC_VOTER"
 #define HW_LIMIT_VOTER			"HW_LIMIT_VOTER"
-#define DYNAMIC_RP_VOTER		"DYNAMIC_RP_VOTER"
-#define DEFAULT_100MA_VOTER		"DEFAULT_100MA_VOTER"
 #define FORCE_RECHARGE_VOTER		"FORCE_RECHARGE_VOTER"
 
 #define BOOST_BACK_STORM_COUNT	3
@@ -80,6 +79,12 @@
 	NUM_MODES,
 };
 
+enum sink_src_mode {
+	SINK_MODE,
+	SRC_MODE,
+	UNATTACHED_MODE,
+};
+
 enum {
 	BOOST_BACK_WA			= BIT(0),
 };
@@ -228,6 +233,7 @@
 	struct smb_chg_param	fcc;
 	struct smb_chg_param	fv;
 	struct smb_chg_param	usb_icl;
+	struct smb_chg_param	icl_max_stat;
 	struct smb_chg_param	icl_stat;
 	struct smb_chg_param	otg_cl;
 	struct smb_chg_param	jeita_cc_comp_hot;
@@ -259,6 +265,7 @@
 	struct smb_params	param;
 	struct smb_iio		iio;
 	int			*debug_mask;
+	int			*pd_disabled;
 	enum smb_mode		mode;
 	struct smb_chg_freq	chg_freq;
 	int			smb_version;
@@ -269,7 +276,6 @@
 	/* locks */
 	struct mutex		lock;
 	struct mutex		ps_change_lock;
-	struct mutex		otg_oc_lock;
 
 	/* power supplies */
 	struct power_supply		*batt_psy;
@@ -296,8 +302,6 @@
 	struct votable		*fcc_votable;
 	struct votable		*fv_votable;
 	struct votable		*usb_icl_votable;
-	struct votable		*pd_disallowed_votable_indirect;
-	struct votable		*pd_allowed_votable;
 	struct votable		*awake_votable;
 	struct votable		*pl_disable_votable;
 	struct votable		*chg_disable_votable;
@@ -315,10 +319,17 @@
 	struct delayed_work	uusb_otg_work;
 	struct delayed_work	bb_removal_work;
 
-	/* cached status */
+	/* pd */
 	int			voltage_min_uv;
 	int			voltage_max_uv;
 	int			pd_active;
+	bool			pd_hard_reset;
+	bool			pr_swap_in_progress;
+	bool			early_usb_attach;
+	bool			ok_to_pd;
+	bool			typec_legacy;
+
+	/* cached status */
 	bool			system_suspend_supported;
 	int			boost_threshold_ua;
 	int			system_temp_level;
@@ -334,15 +345,10 @@
 	int			connector_type;
 	bool			otg_en;
 	bool			suspend_input_on_debug_batt;
-	int			otg_attempts;
-	int			vconn_attempts;
 	int			default_icl_ua;
 	int			otg_cl_ua;
 	bool			uusb_apsd_rerun_done;
-	bool			pd_hard_reset;
-	bool			typec_present;
 	int			fake_input_current_limited;
-	bool			pr_swap_in_progress;
 	int			typec_mode;
 	int			usb_icl_change_irq_enabled;
 	u32			jeita_status;
@@ -352,6 +358,7 @@
 	int			hw_max_icl_ua;
 	int			auto_recharge_soc;
 	bool			jeita_configured;
+	enum sink_src_mode	sink_src_mode;
 
 	/* workaround flag */
 	u32			wa_flags;
@@ -420,6 +427,7 @@
 irqreturn_t usb_source_change_irq_handler(int irq, void *data);
 irqreturn_t icl_change_irq_handler(int irq, void *data);
 irqreturn_t typec_state_change_irq_handler(int irq, void *data);
+irqreturn_t typec_attach_detach_irq_handler(int irq, void *data);
 irqreturn_t dc_plugin_irq_handler(int irq, void *data);
 irqreturn_t high_duty_cycle_irq_handler(int irq, void *data);
 irqreturn_t switcher_power_ok_irq_handler(int irq, void *data);
@@ -485,8 +493,6 @@
 				union power_supply_propval *val);
 int smblib_get_prop_typec_power_role(struct smb_charger *chg,
 				union power_supply_propval *val);
-int smblib_get_prop_pd_allowed(struct smb_charger *chg,
-				union power_supply_propval *val);
 int smblib_get_prop_input_current_settled(struct smb_charger *chg,
 				union power_supply_propval *val);
 int smblib_get_prop_input_voltage_settled(struct smb_charger *chg,
diff --git a/drivers/power/supply/qcom/smb5-reg.h b/drivers/power/supply/qcom/smb5-reg.h
index e199087..4fdf866 100644
--- a/drivers/power/supply/qcom/smb5-reg.h
+++ b/drivers/power/supply/qcom/smb5-reg.h
@@ -105,6 +105,8 @@
 /********************************
  *  DCDC Peripheral Registers  *
  ********************************/
+#define ICL_MAX_STATUS_REG			(DCDC_BASE + 0x06)
+
 #define AICL_ICL_STATUS_REG			(DCDC_BASE + 0x08)
 
 #define AICL_STATUS_REG				(DCDC_BASE + 0x0A)
@@ -215,6 +217,7 @@
 #define HVDCP_AUTH_ALG_EN_CFG_BIT		BIT(6)
 #define HVDCP_AUTONOMOUS_MODE_EN_CFG_BIT	BIT(5)
 #define BC1P2_SRC_DETECT_BIT			BIT(3)
+#define HVDCP_EN_BIT				BIT(2)
 
 #define USBIN_OPTIONS_2_CFG_REG			(USBIN_BASE + 0x63)
 #define FLOAT_OPTIONS_MASK			GENMASK(2, 0)
@@ -262,6 +265,9 @@
 #define SRC_RA_OPEN_BIT				BIT(1)
 #define AUDIO_ACCESS_RA_RA_BIT			BIT(0)
 
+#define TYPE_C_STATE_MACHINE_STATUS_REG		(TYPEC_BASE + 0x09)
+#define TYPEC_ATTACH_DETACH_STATE_BIT		BIT(5)
+
 #define TYPE_C_MISC_STATUS_REG			(TYPEC_BASE + 0x0B)
 #define SNK_SRC_MODE_BIT			BIT(6)
 #define TYPEC_VBUS_ERROR_STATUS_BIT		BIT(4)
@@ -269,6 +275,7 @@
 #define CC_ATTACHED_BIT				BIT(0)
 
 #define LEGACY_CABLE_STATUS_REG			(TYPEC_BASE + 0x0D)
+#define TYPEC_LEGACY_CABLE_STATUS_BIT		BIT(1)
 #define TYPEC_NONCOMP_LEGACY_CABLE_STATUS_BIT	BIT(0)
 
 #define TYPEC_U_USB_STATUS_REG			(TYPEC_BASE + 0x0F)
@@ -287,10 +294,12 @@
 #define VCONN_EN_SRC_BIT			BIT(0)
 
 #define TYPE_C_CCOUT_CONTROL_REG		(TYPEC_BASE + 0x48)
+#define TYPEC_CCOUT_BUFFER_EN_BIT		BIT(2)
 #define TYPEC_CCOUT_VALUE_BIT			BIT(1)
 #define TYPEC_CCOUT_SRC_BIT			BIT(0)
 
 #define TYPE_C_EXIT_STATE_CFG_REG		(TYPEC_BASE + 0x50)
+#define BYPASS_VSAFE0V_DURING_ROLE_SWAP_BIT	BIT(3)
 #define EXIT_SNK_BASED_ON_CC_BIT		BIT(0)
 
 #define TYPE_C_INTERRUPT_EN_CFG_1_REG			(TYPEC_BASE + 0x5E)
diff --git a/drivers/thermal/tsens1xxx.c b/drivers/thermal/tsens1xxx.c
index d63fcd1..19e2b5a 100644
--- a/drivers/thermal/tsens1xxx.c
+++ b/drivers/thermal/tsens1xxx.c
@@ -517,8 +517,8 @@
 			th_temp = code_to_degc((threshold &
 					TSENS_UPPER_THRESHOLD_MASK) >>
 					TSENS_UPPER_THRESHOLD_SHIFT,
-					tm->sensor);
-			if (th_temp > temp) {
+					(tm->sensor + i));
+			if (th_temp > (temp/TSENS_SCALE_MILLIDEG)) {
 				pr_debug("Re-arm high threshold\n");
 				rc = tsens_tz_activate_trip_type(
 						&tm->sensor[i],
@@ -539,8 +539,8 @@
 					tm->tsens_tm_addr + addr_offset));
 			th_temp = code_to_degc((threshold &
 					TSENS_LOWER_THRESHOLD_MASK),
-					tm->sensor);
-			if (th_temp < temp) {
+					(tm->sensor + i));
+			if (th_temp < (temp/TSENS_SCALE_MILLIDEG)) {
 				pr_debug("Re-arm Low threshold\n");
 				rc = tsens_tz_activate_trip_type(
 						&tm->sensor[i],
diff --git a/drivers/usb/pd/policy_engine.c b/drivers/usb/pd/policy_engine.c
index 6111d39..d21823b 100644
--- a/drivers/usb/pd/policy_engine.c
+++ b/drivers/usb/pd/policy_engine.c
@@ -942,7 +942,7 @@
 
 	/* check against received length to avoid overrun */
 	if (bytes_to_copy > len - sizeof(ext_hdr)) {
-		usbpd_warn(&pd->dev, "not enough bytes in chunk, expected:%u received:%lu\n",
+		usbpd_warn(&pd->dev, "not enough bytes in chunk, expected:%u received:%zu\n",
 			bytes_to_copy, len - sizeof(ext_hdr));
 		bytes_to_copy = len - sizeof(ext_hdr);
 	}
diff --git a/drivers/video/fbdev/msm/mdss_dsi_host.c b/drivers/video/fbdev/msm/mdss_dsi_host.c
index ca0af2b..56ed15d 100644
--- a/drivers/video/fbdev/msm/mdss_dsi_host.c
+++ b/drivers/video/fbdev/msm/mdss_dsi_host.c
@@ -161,7 +161,14 @@
 
 	MDSS_XLOG(ctrl->ndx, enable, ctrl->mdp_busy, current->pid,
 		client);
-	if (enable == 0) {
+	/*
+	 * ensure that before going into ecg or turning
+	 * off the clocks, cmd_mdp_busy is not true. During a
+	 * race condition, clocks are turned off and so the
+	 * isr for cmd_mdp_busy does not get cleared in hw.
+	 */
+	if (enable == MDSS_DSI_CLK_OFF ||
+		enable == MDSS_DSI_CLK_EARLY_GATE) {
 		/* need wait before disable */
 		mutex_lock(&ctrl->cmd_mutex);
 		mdss_dsi_cmd_mdp_busy(ctrl);
diff --git a/drivers/video/fbdev/msm/mdss_fb.c b/drivers/video/fbdev/msm/mdss_fb.c
index 27299d10..7ca94df 100644
--- a/drivers/video/fbdev/msm/mdss_fb.c
+++ b/drivers/video/fbdev/msm/mdss_fb.c
@@ -1161,11 +1161,24 @@
 		if (pdata->next) {
 			spt = mdss_panel_get_timing_by_name(pdata->next,
 					modedb[i].name);
-			if (!IS_ERR_OR_NULL(spt))
+			/* for split config, recalculate xres and pixel clock */
+			if (!IS_ERR_OR_NULL(spt)) {
+				unsigned long pclk, h_total, v_total;
 				modedb[i].xres += spt->xres;
-			else
+				h_total = modedb[i].xres +
+					modedb[i].left_margin +
+					modedb[i].right_margin +
+					modedb[i].hsync_len;
+				v_total = modedb[i].yres +
+					modedb[i].lower_margin +
+					modedb[i].upper_margin +
+					modedb[i].vsync_len;
+				pclk = h_total * v_total * modedb[i].refresh;
+				modedb[i].pixclock = KHZ2PICOS(pclk / 1000);
+			} else {
 				pr_debug("no matching split config for %s\n",
 						modedb[i].name);
+			}
 
 			/*
 			 * if no panel timing found for current, need to
diff --git a/include/trace/events/sched.h b/include/trace/events/sched.h
index e06df4d..b355ebf 100644
--- a/include/trace/events/sched.h
+++ b/include/trace/events/sched.h
@@ -713,10 +713,10 @@
 TRACE_EVENT(sched_task_util,
 
 	TP_PROTO(struct task_struct *p, int next_cpu, int backup_cpu,
-		 int target_cpu, bool sync, bool need_idle,
+		 int target_cpu, bool sync, bool need_idle, int fastpath,
 		 bool placement_boost, int rtg_cpu, u64 start_t),
 
-	TP_ARGS(p, next_cpu, backup_cpu, target_cpu, sync, need_idle,
+	TP_ARGS(p, next_cpu, backup_cpu, target_cpu, sync, need_idle, fastpath,
 		placement_boost, rtg_cpu, start_t),
 
 	TP_STRUCT__entry(
@@ -729,6 +729,7 @@
 		__field(int, target_cpu			)
 		__field(bool, sync			)
 		__field(bool, need_idle			)
+		__field(int, fastpath			)
 		__field(bool, placement_boost		)
 		__field(int, rtg_cpu			)
 		__field(u64, latency			)
@@ -744,13 +745,14 @@
 		__entry->target_cpu		= target_cpu;
 		__entry->sync			= sync;
 		__entry->need_idle		= need_idle;
+		__entry->fastpath		= fastpath;
 		__entry->placement_boost	= placement_boost;
 		__entry->rtg_cpu		= rtg_cpu;
 		__entry->latency		= (sched_clock() - start_t);
 	),
 
-	TP_printk("pid=%d comm=%s util=%lu prev_cpu=%d next_cpu=%d backup_cpu=%d target_cpu=%d sync=%d need_idle=%d placement_boost=%d rtg_cpu=%d latency=%llu",
-		__entry->pid, __entry->comm, __entry->util, __entry->prev_cpu, __entry->next_cpu, __entry->backup_cpu, __entry->target_cpu, __entry->sync, __entry->need_idle, __entry->placement_boost, __entry->rtg_cpu, __entry->latency)
+	TP_printk("pid=%d comm=%s util=%lu prev_cpu=%d next_cpu=%d backup_cpu=%d target_cpu=%d sync=%d need_idle=%d fastpath=%d placement_boost=%d rtg_cpu=%d latency=%llu",
+		__entry->pid, __entry->comm, __entry->util, __entry->prev_cpu, __entry->next_cpu, __entry->backup_cpu, __entry->target_cpu, __entry->sync, __entry->need_idle,  __entry->fastpath, __entry->placement_boost, __entry->rtg_cpu, __entry->latency)
 );
 
 #endif
diff --git a/include/uapi/linux/qg.h b/include/uapi/linux/qg.h
index 54488ff..2c7b49a 100644
--- a/include/uapi/linux/qg.h
+++ b/include/uapi/linux/qg.h
@@ -12,8 +12,8 @@
 	QG_ESR,
 	QG_CHARGE_COUNTER,
 	QG_FIFO_TIME_DELTA,
-	QG_RESERVED_1,
-	QG_RESERVED_2,
+	QG_BATT_SOC,
+	QG_CC_SOC,
 	QG_RESERVED_3,
 	QG_RESERVED_4,
 	QG_RESERVED_5,
@@ -25,6 +25,9 @@
 	QG_MAX,
 };
 
+#define QG_BATT_SOC QG_BATT_SOC
+#define QG_CC_SOC QG_CC_SOC
+
 struct fifo_data {
 	unsigned int			v;
 	unsigned int			i;
diff --git a/include/uapi/media/msm_media_info.h b/include/uapi/media/msm_media_info.h
index 4f12e5c..3fd0c88 100644
--- a/include/uapi/media/msm_media_info.h
+++ b/include/uapi/media/msm_media_info.h
@@ -1229,6 +1229,7 @@
 {
 	const unsigned int extra_size = VENUS_EXTRADATA_SIZE(width, height);
 	unsigned int uv_alignment = 0, size = 0;
+	unsigned int w_alignment = 512;
 	unsigned int y_plane, uv_plane, y_stride,
 		uv_stride, y_sclines, uv_sclines;
 	unsigned int y_ubwc_plane = 0, uv_ubwc_plane = 0;
@@ -1252,6 +1253,17 @@
 	switch (color_fmt) {
 	case COLOR_FMT_NV21:
 	case COLOR_FMT_NV12:
+		uv_alignment = 4096;
+		y_plane = y_stride * y_sclines;
+		uv_plane = uv_stride * uv_sclines + uv_alignment;
+		size = y_plane + uv_plane +
+				MSM_MEDIA_MAX(extra_size, 8 * y_stride);
+		size = MSM_MEDIA_ALIGN(size, 4096);
+
+		/* Additional size to cover last row of non-aligned frame */
+		size += MSM_MEDIA_ALIGN(width, w_alignment) * w_alignment;
+		size = MSM_MEDIA_ALIGN(size, 4096);
+		break;
 	case COLOR_FMT_P010:
 		uv_alignment = 4096;
 		y_plane = y_stride * y_sclines;
@@ -1288,6 +1300,10 @@
 			uv_meta_plane)*2 +
 			MSM_MEDIA_MAX(extra_size + 8192, 48 * y_stride);
 		size = MSM_MEDIA_ALIGN(size, 4096);
+
+		/* Additional size to cover last row of non-aligned frame */
+		size += MSM_MEDIA_ALIGN(width, w_alignment) * w_alignment;
+		size = MSM_MEDIA_ALIGN(size, 4096);
 		break;
 	case COLOR_FMT_NV12_BPP10_UBWC:
 		y_ubwc_plane = MSM_MEDIA_ALIGN(y_stride * y_sclines, 4096);
diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
index 66a4658..7e55c81 100644
--- a/kernel/sched/fair.c
+++ b/kernel/sched/fair.c
@@ -7358,6 +7358,12 @@
 }
 #endif
 
+enum fastpaths {
+	NONE = 0,
+	SYNC_WAKEUP,
+	PREV_CPU_BIAS,
+};
+
 static int select_energy_cpu_brute(struct task_struct *p, int prev_cpu, int sync)
 {
 	bool boosted, prefer_idle;
@@ -7368,6 +7374,7 @@
 	struct cpumask *rtg_target = find_rtg_target(p);
 	struct find_best_target_env fbt_env;
 	u64 start_t = 0;
+	int fastpath = 0;
 
 	if (trace_sched_task_util_enabled())
 		start_t = sched_clock();
@@ -7404,12 +7411,17 @@
 		if (bias_to_waker_cpu(p, cpu, rtg_target)) {
 			schedstat_inc(p->se.statistics.nr_wakeups_secb_sync);
 			schedstat_inc(this_rq()->eas_stats.secb_sync);
-			return cpu;
+			target_cpu = cpu;
+			fastpath = SYNC_WAKEUP;
+			goto out;
 		}
 	}
 
-	if (bias_to_prev_cpu(p, rtg_target))
-		return prev_cpu;
+	if (bias_to_prev_cpu(p, rtg_target)) {
+		target_cpu = prev_cpu;
+		fastpath = PREV_CPU_BIAS;
+		goto out;
+	}
 
 	rcu_read_lock();
 
@@ -7496,11 +7508,12 @@
 	schedstat_inc(this_rq()->eas_stats.secb_count);
 
 unlock:
-	trace_sched_task_util(p, next_cpu, backup_cpu, target_cpu, sync,
-			      fbt_env.need_idle, fbt_env.placement_boost,
-			      rtg_target ? cpumask_first(rtg_target) : -1,
-			      start_t);
 	rcu_read_unlock();
+out:
+	trace_sched_task_util(p, next_cpu, backup_cpu, target_cpu, sync,
+			      fbt_env.need_idle, fastpath,
+			      fbt_env.placement_boost, rtg_target ?
+			      cpumask_first(rtg_target) : -1, start_t);
 	return target_cpu;
 }
 
diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
index 22c73c3..de0188e 100644
--- a/net/ipv6/ip6_output.c
+++ b/net/ipv6/ip6_output.c
@@ -1279,7 +1279,7 @@
 		if (np->frag_size)
 			mtu = np->frag_size;
 	}
-	if (mtu < IPV6_MIN_MTU)
+	if (!(rt->dst.flags & DST_XFRM_TUNNEL) && mtu < IPV6_MIN_MTU)
 		return -EINVAL;
 	cork->base.fragsize = mtu;
 	if (dst_allfrag(rt->dst.path))