Merge "defconfig: sdm845: Enable llcc for sdm670"
diff --git a/Documentation/devicetree/bindings/scheduler/energy.txt b/Documentation/devicetree/bindings/scheduler/energy.txt
new file mode 100644
index 0000000..3c7121c
--- /dev/null
+++ b/Documentation/devicetree/bindings/scheduler/energy.txt
@@ -0,0 +1,13 @@
+* Scheduler Energy Driver
+
+Scheduler Energy Driver updates capacities in the scheduler group energy array.
+The array contains power cost at each CPU operating points so energy aware
+scheduler (EAS) can utilize it for task placement.
+
+Required properties:
+- compatible:		Must be "sched-energy"
+
+Example:
+	energy-costs {
+		compatible = "sched-energy";
+	}
diff --git a/arch/arm64/boot/dts/qcom/pm660.dtsi b/arch/arm64/boot/dts/qcom/pm660.dtsi
new file mode 100644
index 0000000..ef8fe1b
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/pm660.dtsi
@@ -0,0 +1,631 @@
+/* Copyright (c) 2016-2017, 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 <dt-bindings/spmi/spmi.h>
+#include <dt-bindings/interrupt-controller/irq.h>
+
+&spmi_bus {
+	qcom,pm660@0 {
+		compatible ="qcom,spmi-pmic";
+		reg = <0x0 SPMI_USID>;
+		#address-cells = <2>;
+		#size-cells = <0>;
+
+		pm660_revid: qcom,revid@100 {
+			compatible = "qcom,qpnp-revid";
+			reg = <0x100 0x100>;
+			qcom,fab-id-valid;
+		};
+
+		pm660_misc: qcom,misc@900 {
+			compatible = "qcom,qpnp-misc";
+			reg = <0x900 0x100>;
+		};
+
+		qcom,power-on@800 {
+			compatible = "qcom,qpnp-power-on";
+			reg = <0x800 0x100>;
+			interrupts = <0x0 0x8 0x0 IRQ_TYPE_NONE>,
+				     <0x0 0x8 0x1 IRQ_TYPE_NONE>,
+				     <0x0 0x8 0x4 IRQ_TYPE_NONE>,
+				     <0x0 0x8 0x5 IRQ_TYPE_NONE>;
+			interrupt-names = "kpdpwr", "resin",
+					"resin-bark", "kpdpwr-resin-bark";
+			qcom,pon-dbc-delay = <15625>;
+			qcom,kpdpwr-sw-debounce;
+			qcom,system-reset;
+			qcom,store-hard-reset-reason;
+
+			qcom,pon_1 {
+				qcom,pon-type = <0>;
+				qcom,pull-up = <1>;
+				linux,code = <116>;
+			};
+
+			qcom,pon_2 {
+				qcom,pon-type = <1>;
+				qcom,pull-up = <1>;
+				linux,code = <114>;
+			};
+		};
+
+		qcom,temp-alarm@2400 {
+			compatible = "qcom,qpnp-temp-alarm";
+			reg = <0x2400 0x100>;
+			interrupts = <0x0 0x24 0x0 IRQ_TYPE_EDGE_RISING>;
+			label = "pm660_tz";
+			qcom,channel-num = <6>;
+			qcom,temp_alarm-vadc = <&pm660_vadc>;
+		};
+
+		pm660_gpios: gpios {
+			compatible = "qcom,qpnp-pin";
+			gpio-controller;
+			#gpio-cells = <2>;
+			#address-cells = <1>;
+			#size-cells = <1>;
+			label = "pm660-gpio";
+
+			gpio@c000 {
+				reg = <0xc000 0x100>;
+				qcom,pin-num = <1>;
+				status = "disabled";
+			};
+
+			gpio@c100 {
+				reg = <0xc100 0x100>;
+				qcom,pin-num = <2>;
+				status = "disabled";
+			};
+
+			gpio@c200 {
+				reg = <0xc200 0x100>;
+				qcom,pin-num = <3>;
+				status = "disabled";
+			};
+
+			gpio@c300 {
+				reg = <0xc300 0x100>;
+				qcom,pin-num = <4>;
+				status = "disabled";
+			};
+
+			gpio@c400 {
+				reg = <0xc400 0x100>;
+				qcom,pin-num = <5>;
+				status = "disabled";
+			};
+
+			gpio@c500 {
+				reg = <0xc500 0x100>;
+				qcom,pin-num = <6>;
+				status = "disabled";
+			};
+
+			gpio@c600 {
+				reg = <0xc600 0x100>;
+				qcom,pin-num = <7>;
+				status = "disabled";
+			};
+
+			gpio@c700 {
+				reg = <0xc700 0x100>;
+				qcom,pin-num = <8>;
+				status = "disabled";
+			};
+
+			gpio@c800 {
+				reg = <0xc800 0x100>;
+				qcom,pin-num = <9>;
+				status = "disabled";
+			};
+
+			gpio@c900 {
+				reg = <0xc900 0x100>;
+				qcom,pin-num = <10>;
+				status = "disabled";
+			};
+
+			gpio@ca00 {
+				reg = <0xca00 0x100>;
+				qcom,pin-num = <11>;
+				status = "disabled";
+			};
+
+			gpio@cb00 {
+				reg = <0xcb00 0x100>;
+				qcom,pin-num = <12>;
+				status = "disabled";
+			};
+
+			gpio@cc00 {
+				reg = <0xcc00 0x100>;
+				qcom,pin-num = <13>;
+				status = "disabled";
+			};
+		};
+
+		pm660_coincell: qcom,coincell@2800 {
+			compatible = "qcom,qpnp-coincell";
+			reg = <0x2800 0x100>;
+		};
+
+		pm660_rtc: qcom,pm660_rtc {
+			compatible = "qcom,qpnp-rtc";
+			#address-cells = <1>;
+			#size-cells = <1>;
+			qcom,qpnp-rtc-write = <0>;
+			qcom,qpnp-rtc-alarm-pwrup = <0>;
+
+			qcom,pm660_rtc_rw@6000 {
+				reg = <0x6000 0x100>;
+			};
+			qcom,pm660_rtc_alarm@6100 {
+				reg = <0x6100 0x100>;
+				interrupts = <0x0 0x61 0x1 IRQ_TYPE_NONE>;
+			};
+		};
+
+		pm660_vadc: vadc@3100 {
+			compatible = "qcom,qpnp-vadc-hc";
+			reg = <0x3100 0x100>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+			interrupts = <0x0 0x31 0x0 IRQ_TYPE_EDGE_RISING>;
+			interrupt-names = "eoc-int-en-set";
+			qcom,adc-bit-resolution = <15>;
+			qcom,adc-vdd-reference = <1875>;
+
+			chan@6 {
+				label = "die_temp";
+				reg = <6>;
+				qcom,decimation = <2>;
+				qcom,pre-div-channel-scaling = <0>;
+				qcom,calibration-type = "absolute";
+				qcom,scale-function = <3>;
+				qcom,hw-settle-time = <0>;
+				qcom,fast-avg-setup = <0>;
+				qcom,cal-val = <0>;
+			};
+
+			chan@0 {
+				label = "ref_gnd";
+				reg = <0>;
+				qcom,decimation = <2>;
+				qcom,pre-div-channel-scaling = <0>;
+				qcom,calibration-type = "absolute";
+				qcom,scale-function = <0>;
+				qcom,hw-settle-time = <0>;
+				qcom,fast-avg-setup = <0>;
+				qcom,cal-val = <0>;
+			};
+
+			chan@1 {
+				label = "ref_1250v";
+				reg = <1>;
+				qcom,decimation = <2>;
+				qcom,pre-div-channel-scaling = <0>;
+				qcom,calibration-type = "absolute";
+				qcom,scale-function = <0>;
+				qcom,hw-settle-time = <0>;
+				qcom,fast-avg-setup = <0>;
+				qcom,cal-val = <0>;
+			};
+
+			chan@83 {
+				label = "vph_pwr";
+				reg = <0x83>;
+				qcom,decimation = <2>;
+				qcom,pre-div-channel-scaling = <1>;
+				qcom,calibration-type = "absolute";
+				qcom,scale-function = <0>;
+				qcom,hw-settle-time = <0>;
+				qcom,fast-avg-setup = <0>;
+			};
+
+			chan@85 {
+				label = "vcoin";
+				reg = <0x85>;
+				qcom,decimation = <2>;
+				qcom,pre-div-channel-scaling = <1>;
+				qcom,calibration-type = "absolute";
+				qcom,scale-function = <0>;
+				qcom,hw-settle-time = <0>;
+				qcom,fast-avg-setup = <0>;
+			};
+
+			chan@4c {
+				label = "xo_therm";
+				reg = <0x4c>;
+				qcom,decimation = <2>;
+				qcom,pre-div-channel-scaling = <0>;
+				qcom,calibration-type = "ratiometric";
+				qcom,scale-function = <4>;
+				qcom,hw-settle-time = <2>;
+				qcom,fast-avg-setup = <0>;
+			};
+
+			chan@4d {
+				label = "msm_therm";
+				reg = <0x4d>;
+				qcom,decimation = <2>;
+				qcom,pre-div-channel-scaling = <0>;
+				qcom,calibration-type = "ratiometric";
+				qcom,scale-function = <2>;
+				qcom,hw-settle-time = <2>;
+				qcom,fast-avg-setup = <0>;
+			};
+
+			chan@51 {
+				label = "quiet_therm";
+				reg = <0x51>;
+				qcom,decimation = <2>;
+				qcom,pre-div-channel-scaling = <0>;
+				qcom,calibration-type = "ratiometric";
+				qcom,scale-function = <2>;
+				qcom,hw-settle-time = <2>;
+				qcom,fast-avg-setup = <0>;
+			};
+
+			chan@4e {
+				label = "emmc_therm";
+				reg = <0x4e>;
+				qcom,decimation = <2>;
+				qcom,pre-div-channel-scaling = <0>;
+				qcom,calibration-type = "ratiometric";
+				qcom,scale-function = <2>;
+				qcom,hw-settle-time = <2>;
+				qcom,fast-avg-setup = <0>;
+				qcom,vadc-thermal-node;
+			};
+
+			chan@4f {
+				label = "pa_therm0";
+				reg = <0x4f>;
+				qcom,decimation = <2>;
+				qcom,pre-div-channel-scaling = <0>;
+				qcom,calibration-type = "ratiometric";
+				qcom,scale-function = <2>;
+				qcom,hw-settle-time = <2>;
+				qcom,fast-avg-setup = <0>;
+				qcom,vadc-thermal-node;
+			};
+
+			chan@1d {
+				label = "drax_temp";
+				reg = <0x1d>;
+				qcom,decimation = <2>;
+				qcom,pre-div-channel-scaling = <0>;
+				qcom,calibration-type = "absolute";
+				qcom,scale-function = <3>;
+				qcom,hw-settle-time = <0>;
+				qcom,fast-avg-setup = <0>;
+				qcom,cal-val = <0>;
+			};
+		};
+
+		pm660_charger: qcom,qpnp-smb2 {
+			compatible = "qcom,qpnp-smb2";
+			#address-cells = <1>;
+			#size-cells = <1>;
+
+			qcom,pmic-revid = <&pm660_revid>;
+
+			io-channels = <&pm660_rradc 8>,
+				      <&pm660_rradc 10>,
+				      <&pm660_rradc 3>,
+				      <&pm660_rradc 4>;
+			io-channel-names = "charger_temp",
+					   "charger_temp_max",
+					   "usbin_i",
+					   "usbin_v";
+
+			qcom,wipower-max-uw = <5000000>;
+			dpdm-supply = <&qusb_phy0>;
+
+			qcom,thermal-mitigation
+					= <3000000 2500000 2000000 1500000
+						1000000 500000>;
+
+			qcom,chgr@1000 {
+				reg = <0x1000 0x100>;
+				interrupts =
+					<0x0 0x10 0x0 IRQ_TYPE_EDGE_RISING>,
+					<0x0 0x10 0x1 IRQ_TYPE_EDGE_RISING>,
+					<0x0 0x10 0x2 IRQ_TYPE_EDGE_RISING>,
+					<0x0 0x10 0x3 IRQ_TYPE_EDGE_RISING>,
+					<0x0 0x10 0x4 IRQ_TYPE_EDGE_RISING>;
+
+				interrupt-names = "chg-error",
+						  "chg-state-change",
+						  "step-chg-state-change",
+						  "step-chg-soc-update-fail",
+						  "step-chg-soc-update-request";
+			};
+
+			qcom,otg@1100 {
+				reg = <0x1100 0x100>;
+				interrupts = <0x0 0x11 0x0 IRQ_TYPE_EDGE_BOTH>,
+					     <0x0 0x11 0x1 IRQ_TYPE_EDGE_BOTH>,
+					     <0x0 0x11 0x2 IRQ_TYPE_EDGE_BOTH>,
+					     <0x0 0x11 0x3 IRQ_TYPE_EDGE_BOTH>;
+
+				interrupt-names = "otg-fail",
+						  "otg-overcurrent",
+						  "otg-oc-dis-sw-sts",
+						  "testmode-change-detect";
+			};
+
+			qcom,bat-if@1200 {
+				reg = <0x1200 0x100>;
+				interrupts =
+					<0x0 0x12 0x0 IRQ_TYPE_EDGE_RISING>,
+					<0x0 0x12 0x1 IRQ_TYPE_EDGE_BOTH>,
+					<0x0 0x12 0x2 IRQ_TYPE_EDGE_BOTH>,
+					<0x0 0x12 0x3 IRQ_TYPE_EDGE_BOTH>,
+					<0x0 0x12 0x4 IRQ_TYPE_EDGE_BOTH>,
+					<0x0 0x12 0x5 IRQ_TYPE_EDGE_BOTH>;
+
+				interrupt-names = "bat-temp",
+						  "bat-ocp",
+						  "bat-ov",
+						  "bat-low",
+						  "bat-therm-or-id-missing",
+						  "bat-terminal-missing";
+			};
+
+			qcom,usb-chgpth@1300 {
+				reg = <0x1300 0x100>;
+				interrupts =
+					<0x0 0x13 0x0 IRQ_TYPE_EDGE_BOTH>,
+					<0x0 0x13 0x1 IRQ_TYPE_EDGE_BOTH>,
+					<0x0 0x13 0x2 IRQ_TYPE_EDGE_BOTH>,
+					<0x0 0x13 0x3 IRQ_TYPE_EDGE_BOTH>,
+					<0x0 0x13 0x4 IRQ_TYPE_EDGE_BOTH>,
+					<0x0 0x13 0x5 IRQ_TYPE_EDGE_RISING>,
+					<0x0 0x13 0x6 IRQ_TYPE_EDGE_RISING>,
+					<0x0 0x13 0x7 IRQ_TYPE_EDGE_RISING>;
+
+				interrupt-names = "usbin-collapse",
+						  "usbin-lt-3p6v",
+						  "usbin-uv",
+						  "usbin-ov",
+						  "usbin-plugin",
+						  "usbin-src-change",
+						  "usbin-icl-change",
+						  "type-c-change";
+			};
+
+			qcom,dc-chgpth@1400 {
+				reg = <0x1400 0x100>;
+				interrupts =
+					<0x0 0x14 0x0 IRQ_TYPE_EDGE_BOTH>,
+					<0x0 0x14 0x1 IRQ_TYPE_EDGE_BOTH>,
+					<0x0 0x14 0x2 IRQ_TYPE_EDGE_BOTH>,
+					<0x0 0x14 0x3 IRQ_TYPE_EDGE_BOTH>,
+					<0x0 0x14 0x4 IRQ_TYPE_EDGE_BOTH>,
+					<0x0 0x14 0x5 IRQ_TYPE_EDGE_BOTH>,
+					<0x0 0x14 0x6 IRQ_TYPE_EDGE_RISING>;
+
+				interrupt-names = "dcin-collapse",
+						  "dcin-lt-3p6v",
+						  "dcin-uv",
+						  "dcin-ov",
+						  "dcin-plugin",
+						  "div2-en-dg",
+						  "dcin-icl-change";
+			};
+
+			qcom,chgr-misc@1600 {
+				reg = <0x1600 0x100>;
+				interrupts =
+					<0x0 0x16 0x0 IRQ_TYPE_EDGE_RISING>,
+					<0x0 0x16 0x1 IRQ_TYPE_EDGE_RISING>,
+					<0x0 0x16 0x2 IRQ_TYPE_EDGE_BOTH>,
+					<0x0 0x16 0x3 IRQ_TYPE_EDGE_BOTH>,
+					<0x0 0x16 0x4 IRQ_TYPE_EDGE_BOTH>,
+					<0x0 0x16 0x5 IRQ_TYPE_EDGE_BOTH>,
+					<0x0 0x16 0x6 IRQ_TYPE_EDGE_FALLING>,
+					<0x0 0x16 0x7 IRQ_TYPE_EDGE_BOTH>;
+
+				interrupt-names = "wdog-snarl",
+						  "wdog-bark",
+						  "aicl-fail",
+						  "aicl-done",
+						  "high-duty-cycle",
+						  "input-current-limiting",
+						  "temperature-change",
+						  "switcher-power-ok";
+			};
+		};
+
+		pm660_pdphy: qcom,usb-pdphy@1700 {
+			compatible = "qcom,qpnp-pdphy";
+			reg = <0x1700 0x100>;
+			vdd-pdphy-supply = <&pm660l_l7>;
+			vbus-supply = <&smb2_vbus>;
+			vconn-supply = <&smb2_vconn>;
+			interrupts = <0x0 0x17 0x0 IRQ_TYPE_EDGE_RISING>,
+				     <0x0 0x17 0x1 IRQ_TYPE_EDGE_RISING>,
+				     <0x0 0x17 0x2 IRQ_TYPE_EDGE_RISING>,
+				     <0x0 0x17 0x3 IRQ_TYPE_EDGE_RISING>,
+				     <0x0 0x17 0x4 IRQ_TYPE_EDGE_RISING>,
+				     <0x0 0x17 0x5 IRQ_TYPE_EDGE_RISING>,
+				     <0x0 0x17 0x6 IRQ_TYPE_EDGE_RISING>;
+
+			interrupt-names = "sig-tx",
+					  "sig-rx",
+					  "msg-tx",
+					  "msg-rx",
+					  "msg-tx-failed",
+					  "msg-tx-discarded",
+					  "msg-rx-discarded";
+
+			qcom,default-sink-caps = <5000 3000>, /* 5V @ 3A */
+						 <9000 3000>; /* 9V @ 3A */
+		};
+
+		pm660_adc_tm: vadc@3400 {
+			compatible = "qcom,qpnp-adc-tm-hc";
+			reg = <0x3400 0x100>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+			interrupts = <0x0 0x34 0x0 IRQ_TYPE_EDGE_RISING>;
+			interrupt-names = "eoc-int-en-set";
+			qcom,adc-bit-resolution = <15>;
+			qcom,adc-vdd-reference = <1875>;
+			qcom,adc_tm-vadc = <&pm660_vadc>;
+			qcom,decimation = <0>;
+			qcom,fast-avg-setup = <0>;
+
+			chan@83 {
+				label = "vph_pwr";
+				reg = <0x83>;
+				qcom,pre-div-channel-scaling = <1>;
+				qcom,calibration-type = "absolute";
+				qcom,scale-function = <0>;
+				qcom,hw-settle-time = <0>;
+				qcom,btm-channel-number = <0x60>;
+			};
+
+			chan@4d {
+				label = "msm_therm";
+				reg = <0x4d>;
+				qcom,pre-div-channel-scaling = <0>;
+				qcom,calibration-type = "ratiometric";
+				qcom,scale-function = <2>;
+				qcom,hw-settle-time = <2>;
+				qcom,btm-channel-number = <0x68>;
+				qcom,thermal-node;
+			};
+
+			chan@51 {
+				label = "quiet_therm";
+				reg = <0x51>;
+				qcom,pre-div-channel-scaling = <0>;
+				qcom,calibration-type = "ratiometric";
+				qcom,scale-function = <2>;
+				qcom,hw-settle-time = <2>;
+				qcom,btm-channel-number = <0x70>;
+				qcom,thermal-node;
+			};
+
+			chan@4c {
+				label = "xo_therm";
+				reg = <0x4c>;
+				qcom,pre-div-channel-scaling = <0>;
+				qcom,calibration-type = "ratiometric";
+				qcom,scale-function = <4>;
+				qcom,hw-settle-time = <2>;
+				qcom,btm-channel-number = <0x78>;
+				qcom,thermal-node;
+			};
+		};
+
+		pm660_rradc: rradc@4500 {
+			compatible = "qcom,rradc";
+			reg = <0x4500 0x100>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+			#io-channel-cells = <1>;
+			qcom,pmic-revid = <&pm660_revid>;
+		};
+
+		pm660_fg: qpnp,fg {
+			compatible = "qcom,fg-gen3";
+			#address-cells = <1>;
+			#size-cells = <1>;
+			qcom,pmic-revid = <&pm660_revid>;
+			io-channels = <&pm660_rradc 0>,
+				      <&pm660_rradc 7>;
+			io-channel-names = "rradc_batt_id",
+					   "rradc_die_temp";
+			qcom,rradc-base = <0x4500>;
+			qcom,fg-esr-timer-awake = <96 96>;
+			qcom,fg-esr-timer-asleep = <256 256>;
+			qcom,fg-esr-timer-charging = <0 96>;
+			qcom,cycle-counter-en;
+			status = "okay";
+
+			qcom,fg-batt-soc@4000 {
+				status = "okay";
+				reg = <0x4000 0x100>;
+				interrupts = <0x0 0x40 0x0 IRQ_TYPE_EDGE_BOTH>,
+					     <0x0 0x40 0x1 IRQ_TYPE_EDGE_BOTH>,
+					     <0x0 0x40 0x2
+							IRQ_TYPE_EDGE_RISING>,
+					     <0x0 0x40 0x3
+							IRQ_TYPE_EDGE_RISING>,
+					     <0x0 0x40 0x4 IRQ_TYPE_EDGE_BOTH>,
+					     <0x0 0x40 0x5
+							IRQ_TYPE_EDGE_RISING>,
+					     <0x0 0x40 0x6 IRQ_TYPE_EDGE_BOTH>,
+					     <0x0 0x40 0x7 IRQ_TYPE_EDGE_BOTH>;
+				interrupt-names = "soc-update",
+						  "soc-ready",
+						  "bsoc-delta",
+						  "msoc-delta",
+						  "msoc-low",
+						  "msoc-empty",
+						  "msoc-high",
+						  "msoc-full";
+			};
+
+			qcom,fg-batt-info@4100 {
+				status = "okay";
+				reg = <0x4100 0x100>;
+				interrupts = <0x0 0x41 0x0 IRQ_TYPE_EDGE_BOTH>,
+					     <0x0 0x41 0x1 IRQ_TYPE_EDGE_BOTH>,
+					     <0x0 0x41 0x2 IRQ_TYPE_EDGE_BOTH>,
+					     <0x0 0x41 0x3 IRQ_TYPE_EDGE_BOTH>,
+					     <0x0 0x41 0x6 IRQ_TYPE_EDGE_BOTH>;
+				interrupt-names = "vbatt-pred-delta",
+						  "vbatt-low",
+						  "esr-delta",
+						  "batt-missing",
+						  "batt-temp-delta";
+			};
+
+			qcom,fg-memif@4400 {
+				status = "okay";
+				reg = <0x4400 0x100>;
+				interrupts = <0x0 0x44 0x0 IRQ_TYPE_EDGE_BOTH>,
+					     <0x0 0x44 0x1 IRQ_TYPE_EDGE_BOTH>,
+					     <0x0 0x44 0x2 IRQ_TYPE_EDGE_BOTH>;
+				interrupt-names = "ima-rdy",
+						  "mem-xcp",
+						  "dma-grant";
+			};
+		};
+
+		bcl@4200 {
+			compatible = "qcom,msm-bcl-lmh";
+			reg = <0x4200 0xff>,
+				<0x4300 0xff>;
+			reg-names = "fg_user_adc",
+					"fg_lmh";
+			interrupts = <0x0 0x42 0x0 IRQ_TYPE_NONE>,
+					<0x0 0x42 0x2 IRQ_TYPE_NONE>;
+			interrupt-names = "bcl-high-ibat-int",
+					"bcl-low-vbat-int";
+			qcom,vbat-polling-delay-ms = <100>;
+			qcom,ibat-polling-delay-ms = <100>;
+		};
+	};
+
+	qcom,pm660@1 {
+		compatible ="qcom,spmi-pmic";
+		reg = <0x1 SPMI_USID>;
+		#address-cells = <2>;
+		#size-cells = <0>;
+	};
+};
diff --git a/arch/arm64/boot/dts/qcom/pm660a.dtsi b/arch/arm64/boot/dts/qcom/pm660a.dtsi
new file mode 100644
index 0000000..bfe1b5a
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/pm660a.dtsi
@@ -0,0 +1,29 @@
+/* Copyright (c) 2016-2017, 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.
+ */
+
+/* Disable WLED */
+&pm660l_wled {
+	status = "disabled";
+};
+
+/* disable LCDB */
+&pm660l_lcdb {
+	status = "disabled";
+};
+
+&pm660a_oledb {
+	status = "okay";
+};
+
+&pm660a_labibb {
+	status = "okay";
+};
diff --git a/arch/arm64/boot/dts/qcom/pm660l.dtsi b/arch/arm64/boot/dts/qcom/pm660l.dtsi
new file mode 100644
index 0000000..0f18ba5
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/pm660l.dtsi
@@ -0,0 +1,470 @@
+/* Copyright (c) 2016-2017, 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 <dt-bindings/interrupt-controller/irq.h>
+#include <dt-bindings/spmi/spmi.h>
+#include <dt-bindings/msm/power-on.h>
+
+&spmi_bus {
+	qcom,pm660l@2 {
+		compatible = "qcom,spmi-pmic";
+		reg = <0x2 SPMI_USID>;
+		#address-cells = <2>;
+		#size-cells = <0>;
+
+		pm660l_revid: qcom,revid@100 {
+			compatible = "qcom,qpnp-revid";
+			reg = <0x100 0x100>;
+		};
+
+		pm660l_pbs: qcom,pbs@7300 {
+			compatible = "qcom,qpnp-pbs";
+			reg = <0x7300 0x100>;
+		};
+
+		qcom,power-on@800 {
+			compatible = "qcom,qpnp-power-on";
+			reg = <0x800 0x100>;
+			qcom,secondary-pon-reset;
+			qcom,hard-reset-poweroff-type =
+				<PON_POWER_OFF_SHUTDOWN>;
+		};
+
+		qcom,temp-alarm@2400 {
+			compatible = "qcom,qpnp-temp-alarm";
+			reg = <0x2400 0x100>;
+			interrupts = <0x2 0x24 0x0 IRQ_TYPE_EDGE_RISING>;
+			label = "pm660l_tz";
+		};
+
+		pm660l_gpios: gpios {
+			compatible = "qcom,qpnp-pin";
+			gpio-controller;
+			#gpio-cells = <2>;
+			#address-cells = <1>;
+			#size-cells = <1>;
+			label = "pm660l-gpio";
+
+			gpio@c000 {
+				reg = <0xc000 0x100>;
+				qcom,pin-num = <1>;
+				status = "disabled";
+			};
+
+			gpio@c100 {
+				reg = <0xc100 0x100>;
+				qcom,pin-num = <2>;
+				status = "disabled";
+			};
+
+			gpio@c200 {
+				reg = <0xc200 0x100>;
+				qcom,pin-num = <3>;
+				status = "disabled";
+			};
+
+			gpio@c300 {
+				reg = <0xc300 0x100>;
+				qcom,pin-num = <4>;
+				status = "disabled";
+			};
+
+			gpio@c400 {
+				reg = <0xc400 0x100>;
+				qcom,pin-num = <5>;
+				status = "disabled";
+			};
+
+			gpio@c500 {
+				reg = <0xc500 0x100>;
+				qcom,pin-num = <6>;
+				status = "disabled";
+			};
+
+			gpio@c600 {
+				reg = <0xc600 0x100>;
+				qcom,pin-num = <7>;
+				status = "disabled";
+			};
+
+			gpio@c700 {
+				reg = <0xc700 0x100>;
+				qcom,pin-num = <8>;
+				status = "disabled";
+			};
+
+			gpio@c800 {
+				reg = <0xc800 0x100>;
+				qcom,pin-num = <9>;
+				status = "disabled";
+			};
+
+			gpio@c900 {
+				reg = <0xc900 0x100>;
+				qcom,pin-num = <10>;
+				status = "disabled";
+			};
+
+			gpio@ca00 {
+				reg = <0xca00 0x100>;
+				qcom,pin-num = <11>;
+				status = "disabled";
+			};
+
+			gpio@cb00 {
+				reg = <0xcb00 0x100>;
+				qcom,pin-num = <12>;
+				status = "disabled";
+			};
+
+		};
+	};
+
+	pm660l_3: qcom,pm660l@3 {
+		compatible ="qcom,spmi-pmic";
+		reg = <0x3 SPMI_USID>;
+		#address-cells = <2>;
+		#size-cells = <0>;
+
+		pm660l_pwm_1: pwm@b100 {
+			compatible = "qcom,qpnp-pwm";
+			reg = <0xb100 0x100>,
+			      <0xb042 0x7e>;
+			reg-names = "qpnp-lpg-channel-base",
+					"qpnp-lpg-lut-base";
+			qcom,channel-id = <1>;
+			qcom,lpg-lut-size = <0x7e>;
+			qcom,supported-sizes = <6>, <9>;
+			qcom,ramp-index = <0>;
+			#pwm-cells = <2>;
+		};
+
+		pm660l_pwm_2: pwm@b200 {
+			compatible = "qcom,qpnp-pwm";
+			reg = <0xb200 0x100>,
+			      <0xb042 0x7e>;
+			reg-names = "qpnp-lpg-channel-base",
+					 "qpnp-lpg-lut-base";
+			qcom,channel-id = <2>;
+			qcom,lpg-lut-size = <0x7e>;
+			qcom,supported-sizes = <6>, <9>;
+			qcom,ramp-index = <1>;
+			#pwm-cells = <2>;
+		};
+
+		pm660l_pwm_3: pwm@b300 {
+			compatible = "qcom,qpnp-pwm";
+			reg = <0xb300 0x100>,
+			      <0xb042 0x7e>;
+			reg-names = "qpnp-lpg-channel-base",
+					 "qpnp-lpg-lut-base";
+			qcom,channel-id = <3>;
+			qcom,lpg-lut-size = <0x7e>;
+			qcom,supported-sizes = <6>, <9>;
+			qcom,ramp-index = <2>;
+			#pwm-cells = <2>;
+			qcom,period = <6000000>;
+
+			qcom,lpg {
+				label = "lpg";
+				cell-index = <0>;
+				qcom,duty-percents =
+					<0x01 0x0a 0x14 0x1e 0x28 0x32 0x3c
+					0x46 0x50 0x5a 0x64
+					0x64 0x5a 0x50 0x46 0x3c 0x32 0x28 0x1e
+					0x14 0x0a 0x01>;
+			};
+		};
+
+		pm660l_pwm_4: pwm@b400 {
+			compatible = "qcom,qpnp-pwm";
+			reg = <0xb400 0x100>,
+			      <0xb042 0x7e>;
+			reg-names = "qpnp-lpg-channel-base",
+					 "qpnp-lpg-lut-base";
+			qcom,channel-id = <4>;
+			qcom,lpg-lut-size = <0x7e>;
+			qcom,supported-sizes = <6>, <9>;
+			qcom,ramp-index = <3>;
+			#pwm-cells = <2>;
+			status = "disabled";
+		};
+
+		qcom,leds@d000 {
+			compatible = "qcom,leds-qpnp";
+			reg = <0xd000 0x100>;
+			label = "rgb";
+
+			red_led: qcom,rgb_0 {
+				label = "rgb";
+				qcom,id = <3>;
+				qcom,mode = "pwm";
+				pwms = <&pm660l_pwm_3 0 0>;
+				qcom,pwm-us = <1000>;
+				qcom,max-current = <12>;
+				qcom,default-state = "off";
+				linux,name = "red";
+				qcom,start-idx = <0>;
+				qcom,idx-len = <22>;
+				qcom,duty-pcts =
+					[01 0a 14 1e 28 32 3c 46 50 5a 64
+					64 5a 50 46 3c 32 28 1e 14 0a 01];
+				qcom,use-blink;
+			};
+
+			green_led: qcom,rgb_1 {
+				label = "rgb";
+				qcom,id = <4>;
+				qcom,mode = "pwm";
+				pwms = <&pm660l_pwm_2 0 0>;
+				qcom,pwm-us = <1000>;
+				qcom,max-current = <12>;
+				qcom,default-state = "off";
+				linux,name = "green";
+			};
+
+			blue_led: qcom,rgb_2 {
+				label = "rgb";
+				qcom,id = <5>;
+				qcom,mode = "pwm";
+				pwms = <&pm660l_pwm_1 0 0>;
+				qcom,pwm-us = <1000>;
+				qcom,max-current = <12>;
+				qcom,default-state = "off";
+				linux,name = "blue";
+			};
+		};
+
+		pm660l_wled: qcom,leds@d800 {
+			compatible = "qcom,qpnp-wled";
+			reg = <0xd800 0x100>,
+				<0xd900 0x100>;
+			reg-names = "qpnp-wled-ctrl-base",
+					"qpnp-wled-sink-base";
+			interrupts = <0x3 0xd8 0x1 IRQ_TYPE_EDGE_RISING>,
+					<0x3 0xd8 0x2 IRQ_TYPE_EDGE_RISING>;
+			interrupt-names = "ovp-irq", "sc-irq";
+			linux,name = "wled";
+			linux,default-trigger = "bkl-trigger";
+			qcom,fdbk-output = "auto";
+			qcom,vref-uv = <127500>;
+			qcom,switch-freq-khz = <800>;
+			qcom,ovp-mv = <29600>;
+			qcom,ilim-ma = <970>;
+			qcom,boost-duty-ns = <26>;
+			qcom,mod-freq-khz = <9600>;
+			qcom,dim-mode = "hybrid";
+			qcom,hyb-thres = <625>;
+			qcom,sync-dly-us = <800>;
+			qcom,fs-curr-ua = <25000>;
+			qcom,cons-sync-write-delay-us = <1000>;
+			qcom,led-strings-list = [00 01 02];
+			qcom,en-ext-pfet-sc-pro;
+			qcom,loop-auto-gm-en;
+			qcom,pmic-revid = <&pm660l_revid>;
+			status = "ok";
+		};
+
+		flash_led: qcom,leds@d300 {
+			compatible = "qcom,qpnp-flash-led-v2";
+			reg = <0xd300 0x100>;
+			label = "flash";
+			interrupts = <0x3 0xd3 0x0 IRQ_TYPE_EDGE_RISING>,
+					<0x3 0xd3 0x3 IRQ_TYPE_EDGE_RISING>,
+					<0x3 0xd3 0x4 IRQ_TYPE_EDGE_RISING>;
+			interrupt-names = "led-fault-irq",
+					"all-ramp-down-done-irq",
+					"all-ramp-up-done-irq";
+			qcom,hdrm-auto-mode;
+			qcom,short-circuit-det;
+			qcom,open-circuit-det;
+			qcom,vph-droop-det;
+			qcom,thermal-derate-en;
+			qcom,thermal-derate-current = <200 500 1000>;
+			qcom,isc-delay = <192>;
+			qcom,pmic-revid = <&pm660l_revid>;
+
+			pm660l_flash0: qcom,flash_0 {
+				label = "flash";
+				qcom,led-name = "led:flash_0";
+				qcom,max-current = <1500>;
+				qcom,default-led-trigger = "flash0_trigger";
+				qcom,id = <0>;
+				qcom,current-ma = <1000>;
+				qcom,duration-ms = <1280>;
+				qcom,ires-ua = <12500>;
+				qcom,hdrm-voltage-mv = <325>;
+				qcom,hdrm-vol-hi-lo-win-mv = <100>;
+			};
+
+			pm660l_flash1: qcom,flash_1 {
+				label = "flash";
+				qcom,led-name = "led:flash_1";
+				qcom,max-current = <1500>;
+				qcom,default-led-trigger = "flash1_trigger";
+				qcom,id = <1>;
+				qcom,current-ma = <1000>;
+				qcom,duration-ms = <1280>;
+				qcom,ires-ua = <12500>;
+				qcom,hdrm-voltage-mv = <325>;
+				qcom,hdrm-vol-hi-lo-win-mv = <100>;
+			};
+
+			pm660l_flash2: qcom,flash_2 {
+				label = "flash";
+				qcom,led-name = "led:flash_2";
+				qcom,max-current = <750>;
+				qcom,default-led-trigger = "flash2_trigger";
+				qcom,id = <2>;
+				qcom,current-ma = <500>;
+				qcom,duration-ms = <1280>;
+				qcom,ires-ua = <12500>;
+				qcom,hdrm-voltage-mv = <325>;
+				qcom,hdrm-vol-hi-lo-win-mv = <100>;
+			};
+
+			pm660l_torch0: qcom,torch_0 {
+				label = "torch";
+				qcom,led-name = "led:torch_0";
+				qcom,max-current = <500>;
+				qcom,default-led-trigger = "torch0_trigger";
+				qcom,id = <0>;
+				qcom,current-ma = <300>;
+				qcom,ires-ua = <12500>;
+				qcom,hdrm-voltage-mv = <325>;
+				qcom,hdrm-vol-hi-lo-win-mv = <100>;
+			};
+
+			pm660l_torch1: qcom,torch_1 {
+				label = "torch";
+				qcom,led-name = "led:torch_1";
+				qcom,max-current = <500>;
+				qcom,default-led-trigger = "torch1_trigger";
+				qcom,id = <1>;
+				qcom,current-ma = <300>;
+				qcom,ires-ua = <12500>;
+				qcom,hdrm-voltage-mv = <325>;
+				qcom,hdrm-vol-hi-lo-win-mv = <100>;
+			};
+
+			pm660l_torch2: qcom,torch_2 {
+				label = "torch";
+				qcom,led-name = "led:torch_2";
+				qcom,max-current = <500>;
+				qcom,default-led-trigger = "torch2_trigger";
+				qcom,id = <2>;
+				qcom,current-ma = <300>;
+				qcom,ires-ua = <12500>;
+				qcom,hdrm-voltage-mv = <325>;
+				qcom,hdrm-vol-hi-lo-win-mv = <100>;
+			};
+
+			pm660l_switch0: qcom,led_switch_0 {
+				label = "switch";
+				qcom,led-name = "led:switch_0";
+				qcom,led-mask = <3>;
+				qcom,default-led-trigger = "switch0_trigger";
+			};
+
+			pm660l_switch1: qcom,led_switch_1 {
+				label = "switch";
+				qcom,led-name = "led:switch_1";
+				qcom,led-mask = <4>;
+				qcom,default-led-trigger = "switch1_trigger";
+			};
+		};
+
+		pm660l_lcdb: qpnp-lcdb@ec00 {
+			compatible = "qcom,qpnp-lcdb-regulator";
+			#address-cells = <1>;
+			#size-cells = <1>;
+			reg = <0xec00 0x100>;
+			interrupts = <0x3 0xec 0x1 IRQ_TYPE_EDGE_RISING>;
+			interrupt-names = "sc-irq";
+
+			qcom,pmic-revid = <&pm660l_revid>;
+
+			lcdb_ldo_vreg: ldo {
+				label = "ldo";
+				regulator-name = "lcdb_ldo";
+				regulator-min-microvolt = <4000000>;
+				regulator-max-microvolt = <6000000>;
+			};
+
+			lcdb_ncp_vreg: ncp {
+				label = "ncp";
+				regulator-name = "lcdb_ncp";
+				regulator-min-microvolt = <4000000>;
+				regulator-max-microvolt = <6000000>;
+			};
+		};
+
+		pm660a_oledb: qpnp-oledb@e000 {
+		       compatible = "qcom,qpnp-oledb-regulator";
+		       #address-cells = <1>;
+		       #size-cells = <1>;
+		       qcom,pmic-revid = <&pm660l_revid>;
+		       reg = <0xe000 0x100>;
+		       qcom,pbs-client = <&pm660l_pbs>;
+
+		       label = "oledb";
+		       regulator-name = "regulator-oledb";
+		       regulator-min-microvolt = <5000000>;
+		       regulator-max-microvolt = <8100000>;
+
+		       qcom,swire-control;
+		       qcom,ext-pin-control;
+		       status = "disabled";
+		};
+
+		pm660a_labibb: qpnp-labibb-regulator {
+			compatible = "qcom,qpnp-labibb-regulator";
+			#address-cells = <1>;
+			#size-cells = <1>;
+			qcom,pmic-revid = <&pm660l_revid>;
+			qcom,swire-control;
+			status = "disabled";
+
+			ibb_regulator: qcom,ibb@dc00 {
+				reg = <0xdc00 0x100>;
+				reg-names = "ibb_reg";
+				regulator-name = "ibb_reg";
+
+				regulator-min-microvolt = <4000000>;
+				regulator-max-microvolt = <6300000>;
+
+				qcom,qpnp-ibb-min-voltage = <1400000>;
+				qcom,qpnp-ibb-step-size = <100000>;
+				qcom,qpnp-ibb-slew-rate = <2000000>;
+				qcom,qpnp-ibb-init-voltage = <4000000>;
+				qcom,qpnp-ibb-init-amoled-voltage = <4000000>;
+			};
+
+			lab_regulator: qcom,lab@de00 {
+				reg = <0xde00 0x100>;
+				reg-names = "lab";
+				regulator-name = "lab_reg";
+
+				regulator-min-microvolt = <4600000>;
+				regulator-max-microvolt = <6100000>;
+
+				qcom,qpnp-lab-min-voltage = <4600000>;
+				qcom,qpnp-lab-step-size = <100000>;
+				qcom,qpnp-lab-slew-rate = <5000>;
+				qcom,qpnp-lab-init-voltage = <4600000>;
+				qcom,qpnp-lab-init-amoled-voltage = <4600000>;
+
+				qcom,notify-lab-vreg-ok-sts;
+			};
+		};
+	};
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm670-regulator.dtsi b/arch/arm64/boot/dts/qcom/sdm670-regulator.dtsi
new file mode 100644
index 0000000..a1dc261
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sdm670-regulator.dtsi
@@ -0,0 +1,363 @@
+/* Copyright (c) 2017, 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  PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <dt-bindings/regulator/qcom,rpmh-regulator.h>
+
+/* Stub regulators */
+
+/ {
+	pm660_s4: regulator-pm660-s4 {
+		compatible = "qcom,stub-regulator";
+		regulator-name = "pm660_s4";
+		qcom,hpm-min-load = <100000>;
+		regulator-min-microvolt = <2040000>;
+		regulator-max-microvolt = <2040000>;
+	};
+
+	/* pm660 S5 - VDD_MODEM supply */
+	pm660_s5_level: regulator-pm660-s5 {
+		compatible = "qcom,stub-regulator";
+		regulator-name = "pm660_s5_level";
+		qcom,hpm-min-load = <100000>;
+		regulator-min-microvolt = <RPMH_REGULATOR_LEVEL_OFF>;
+		regulator-max-microvolt = <RPMH_REGULATOR_LEVEL_MAX>;
+	};
+
+	pm660_s6: regulator-pm660-s6 {
+		compatible = "qcom,stub-regulator";
+		regulator-name = "pm660_s6";
+		qcom,hpm-min-load = <100000>;
+		regulator-min-microvolt = <1352000>;
+		regulator-max-microvolt = <1352000>;
+	};
+
+	/* pm660l S1 - VDD_MX supply */
+	pm660l_s1_level: regulator-pm660l-s1 {
+		compatible = "qcom,stub-regulator";
+		regulator-name = "pm660l_s1_level";
+		qcom,hpm-min-load = <100000>;
+		regulator-min-microvolt = <RPMH_REGULATOR_LEVEL_OFF>;
+		regulator-max-microvolt = <RPMH_REGULATOR_LEVEL_MAX>;
+	};
+
+	pm660l_s1_floor_level: regulator-pm660l-s1-floor-level {
+		compatible = "qcom,stub-regulator";
+		regulator-name = "pm660l_s1_floor_level";
+		qcom,hpm-min-load = <100000>;
+		regulator-min-microvolt = <RPMH_REGULATOR_LEVEL_OFF>;
+		regulator-max-microvolt = <RPMH_REGULATOR_LEVEL_MAX>;
+	};
+
+	pm660l_s1_level_ao: regulator-pm660l-s1-level-ao {
+		compatible = "qcom,stub-regulator";
+		regulator-name = "pm660l_s1_level_ao";
+		qcom,hpm-min-load = <100000>;
+		regulator-min-microvolt = <RPMH_REGULATOR_LEVEL_OFF>;
+		regulator-max-microvolt = <RPMH_REGULATOR_LEVEL_MAX>;
+	};
+
+	/* pm660l S2 - VDD_GFX supply */
+	pm660l_s2_level: regulator-pm660l-s2 {
+		compatible = "qcom,stub-regulator";
+		regulator-name = "pm660l_s2_level";
+		qcom,hpm-min-load = <100000>;
+		regulator-min-microvolt = <RPMH_REGULATOR_LEVEL_OFF>;
+		regulator-max-microvolt = <RPMH_REGULATOR_LEVEL_MAX>;
+	};
+
+	/* pm660l S3 + S4 - VDD_CX supply */
+	pm660l_s3_level: regulator-pm660l-s3-level {
+		compatible = "qcom,stub-regulator";
+		regulator-name = "pm660l_s3_level";
+		qcom,hpm-min-load = <100000>;
+		regulator-min-microvolt = <RPMH_REGULATOR_LEVEL_OFF>;
+		regulator-max-microvolt = <RPMH_REGULATOR_LEVEL_MAX>;
+	};
+
+	pm660l_s3_floor_level: regulator-pm660l-s3-floor-level {
+		compatible = "qcom,stub-regulator";
+		regulator-name = "pm660l_s3_floor_level";
+		qcom,hpm-min-load = <100000>;
+		regulator-min-microvolt = <RPMH_REGULATOR_LEVEL_OFF>;
+		regulator-max-microvolt = <RPMH_REGULATOR_LEVEL_MAX>;
+	};
+
+	pm660l_s3_level_ao: regulator-pm660l-s3-level-ao {
+		compatible = "qcom,stub-regulator";
+		regulator-name = "pm660_s3_level_ao";
+		qcom,hpm-min-load = <100000>;
+		regulator-min-microvolt = <RPMH_REGULATOR_LEVEL_OFF>;
+		regulator-max-microvolt = <RPMH_REGULATOR_LEVEL_MAX>;
+	};
+
+	pm660_l1: regulator-pm660-l1 {
+		compatible = "qcom,stub-regulator";
+		regulator-name = "pm660_l1";
+		qcom,hpm-min-load = <10000>;
+		regulator-min-microvolt = <1200000>;
+		regulator-max-microvolt = <1250000>;
+	};
+
+	pm660_l2: regulator-pm660-l2 {
+		compatible = "qcom,stub-regulator";
+		regulator-name = "pm660_l2";
+		qcom,hpm-min-load = <10000>;
+		regulator-min-microvolt = <1000000>;
+		regulator-max-microvolt = <1000000>;
+	};
+
+	pm660_l3: regulator-pm660-l3 {
+		compatible = "qcom,stub-regulator";
+		regulator-name = "pm660_l3";
+		qcom,hpm-min-load = <10000>;
+		regulator-min-microvolt = <1000000>;
+		regulator-max-microvolt = <1000000>;
+	};
+
+	pm660_l5: regulator-pm660-l5 {
+		compatible = "qcom,stub-regulator";
+		regulator-name = "pm660_l5";
+		qcom,hpm-min-load = <10000>;
+		regulator-min-microvolt = <800000>;
+		regulator-max-microvolt = <800000>;
+	};
+
+	pm660_l6: regulator-pm660-l6 {
+		compatible = "qcom,stub-regulator";
+		regulator-name = "pm660_l6";
+		qcom,hpm-min-load = <10000>;
+		regulator-min-microvolt = <1304000>;
+		regulator-max-microvolt = <1304000>;
+	};
+
+	pm660_l7: regulator-pm660-l7 {
+		compatible = "qcom,stub-regulator";
+		regulator-name = "pm660_l7";
+		qcom,hpm-min-load = <10000>;
+		regulator-min-microvolt = <1200000>;
+		regulator-max-microvolt = <1200000>;
+	};
+
+	pm660_l8: regulator-pm660-l8 {
+		compatible = "qcom,stub-regulator";
+		regulator-name = "pm660_l8";
+		qcom,hpm-min-load = <10000>;
+		regulator-min-microvolt = <1800000>;
+		regulator-max-microvolt = <1800000>;
+	};
+
+	pm660_l9: regulator-pm660-l9 {
+		compatible = "qcom,stub-regulator";
+		regulator-name = "pm660_l9";
+		qcom,hpm-min-load = <10000>;
+		regulator-min-microvolt = <1800000>;
+		regulator-max-microvolt = <1800000>;
+	};
+
+	pm660_l10: regulator-pm660-l10 {
+		compatible = "qcom,stub-regulator";
+		regulator-name = "pm660_l10";
+		qcom,hpm-min-load = <10000>;
+		regulator-min-microvolt = <1800000>;
+		regulator-max-microvolt = <1800000>;
+	};
+
+	pm660_l11: regulator-pm660-l11 {
+		compatible = "qcom,stub-regulator";
+		regulator-name = "pm660_l11";
+		qcom,hpm-min-load = <10000>;
+		regulator-min-microvolt = <1800000>;
+		regulator-max-microvolt = <1800000>;
+	};
+
+	pm660_l12: regulator-pm660-l12 {
+		compatible = "qcom,stub-regulator";
+		regulator-name = "pm660_l12";
+		qcom,hpm-min-load = <10000>;
+		regulator-min-microvolt = <1800000>;
+		regulator-max-microvolt = <1800000>;
+	};
+
+	pm660_l13: regulator-pm660-l13 {
+		compatible = "qcom,stub-regulator";
+		regulator-name = "pm660_l13";
+		qcom,hpm-min-load = <10000>;
+		regulator-min-microvolt = <1800000>;
+		regulator-max-microvolt = <1800000>;
+	};
+
+	pm660_l14: regulator-pm660-l14 {
+		compatible = "qcom,stub-regulator";
+		regulator-name = "pm660_l14";
+		qcom,hpm-min-load = <10000>;
+		regulator-min-microvolt = <1800000>;
+		regulator-max-microvolt = <1800000>;
+	};
+
+	pm660_l15: regulator-pm660-l15 {
+		compatible = "qcom,stub-regulator";
+		regulator-name = "pm660_l15";
+		qcom,hpm-min-load = <10000>;
+		regulator-min-microvolt = <1800000>;
+		regulator-max-microvolt = <2950000>;
+	};
+
+	pm660_l16: regulator-pm660-l16 {
+		compatible = "qcom,stub-regulator";
+		regulator-name = "pm660_l16";
+		qcom,hpm-min-load = <10000>;
+		regulator-min-microvolt = <2700000>;
+		regulator-max-microvolt = <2700000>;
+	};
+
+	pm660_l17: regulator-pm660-l17 {
+		compatible = "qcom,stub-regulator";
+		regulator-name = "pm660_l17";
+		qcom,hpm-min-load = <10000>;
+		regulator-min-microvolt = <1800000>;
+		regulator-max-microvolt = <2950000>;
+	};
+
+	pm660_l19: regulator-pm660-l19 {
+		compatible = "qcom,stub-regulator";
+		regulator-name = "pm660_l19";
+		qcom,hpm-min-load = <10000>;
+		regulator-min-microvolt = <3312000>;
+		regulator-max-microvolt = <3312000>;
+	};
+
+	pm660l_l1: regulator-pm660l-l1 {
+		compatible = "qcom,stub-regulator";
+		regulator-name = "pm660l_l1";
+		qcom,hpm-min-load = <10000>;
+		regulator-min-microvolt = <880000>;
+		regulator-max-microvolt = <900000>;
+	};
+
+	pm660l_l2: regulator-pm660l-l2 {
+		compatible = "qcom,stub-regulator";
+		regulator-name = "pm660l_l2";
+		qcom,hpm-min-load = <5000>;
+		regulator-min-microvolt = <1800000>;
+		regulator-max-microvolt = <2960000>;
+	};
+
+	pm660l_l3: regulator-pm660l-l3 {
+		compatible = "qcom,stub-regulator";
+		regulator-name = "pm660l_l3";
+		qcom,hpm-min-load = <10000>;
+		regulator-min-microvolt = <2850000>;
+		regulator-max-microvolt = <3008000>;
+	};
+
+	pm660l_l4: regulator-pm660l-l4 {
+		compatible = "qcom,stub-regulator";
+		regulator-name = "pm660l_l4";
+		qcom,hpm-min-load = <10000>;
+		regulator-min-microvolt = <2960000>;
+		regulator-max-microvolt = <2960000>;
+	};
+
+	pm660l_l5: regulator-pm660l-l5 {
+		compatible = "qcom,stub-regulator";
+		regulator-name = "pm660l_l5";
+		qcom,hpm-min-load = <10000>;
+		regulator-min-microvolt = <2960000>;
+		regulator-max-microvolt = <2960000>;
+	};
+
+	pm660l_l6: regulator-pm660l-l6 {
+		compatible = "qcom,stub-regulator";
+		regulator-name = "pm660l_l6";
+		qcom,hpm-min-load = <5000>;
+		regulator-min-microvolt = <3008000>;
+		regulator-max-microvolt = <3300000>;
+	};
+
+	pm660l_l7: regulator-pm660l-l7 {
+		compatible = "qcom,stub-regulator";
+		regulator-name = "pm660l_l7";
+		qcom,hpm-min-load = <10000>;
+		regulator-min-microvolt = <3088000>;
+		regulator-max-microvolt = <3100000>;
+	};
+
+	pm660l_l8: regulator-pm660l-l8 {
+		compatible = "qcom,stub-regulator";
+		regulator-name = "pm660l_l8";
+		qcom,hpm-min-load = <10000>;
+		regulator-min-microvolt = <3300000>;
+		regulator-max-microvolt = <3312000>;
+	};
+
+	/* pm660l L9 = VDD_LPI_CX supply */
+	pm660l_l9_level: regulator-pm660l-l9-level {
+		compatible = "qcom,stub-regulator";
+		regulator-name = "pm660l_l9_level";
+		qcom,hpm-min-load = <10000>;
+		regulator-min-microvolt = <RPMH_REGULATOR_LEVEL_OFF>;
+		regulator-max-microvolt = <RPMH_REGULATOR_LEVEL_MAX>;
+	};
+
+	pm660l_l9_floor_level: regulator-pm660l-l9-floor-level {
+		compatible = "qcom,stub-regulator";
+		regulator-name = "pm660l_l9_floor_level";
+		qcom,hpm-min-load = <10000>;
+		regulator-min-microvolt = <RPMH_REGULATOR_LEVEL_OFF>;
+		regulator-max-microvolt = <RPMH_REGULATOR_LEVEL_MAX>;
+	};
+
+	/* pm660l L10 = VDD_LPI_MX supply */
+	pm660l_l10_level: regulator-pm660l-l10-level {
+		compatible = "qcom,stub-regulator";
+		regulator-name = "pm660l_l10_level";
+		qcom,hpm-min-load = <10000>;
+		regulator-min-microvolt = <RPMH_REGULATOR_LEVEL_OFF>;
+		regulator-max-microvolt = <RPMH_REGULATOR_LEVEL_MAX>;
+	};
+
+	pm660l_l10_floor_level: regulator-pm660l-l10-floor-level {
+		compatible = "qcom,stub-regulator";
+		regulator-name = "pm660l_l10_floor_level";
+		qcom,hpm-min-load = <10000>;
+		regulator-min-microvolt = <RPMH_REGULATOR_LEVEL_OFF>;
+		regulator-max-microvolt = <RPMH_REGULATOR_LEVEL_MAX>;
+	};
+
+	pm660l_bob: regulator-pm660l-bob {
+		compatible = "qcom,stub-regulator";
+		regulator-name = "pm660l_bob";
+		regulator-min-microvolt = <3312000>;
+		regulator-max-microvolt = <3312000>;
+	};
+
+	apc0_pwrcl_vreg: regulator-pwrcl {
+		compatible = "qcom,stub-regulator";
+		regulator-name = "apc0_pwrcl_corner";
+		regulator-min-microvolt = <1>;
+		regulator-max-microvolt = <7>;
+	};
+
+	apc0_l3_vreg: regulator-l3 {
+		compatible = "qcom,stub-regulator";
+		regulator-name = "apc0_l3_corner";
+		regulator-min-microvolt = <1>;
+		regulator-max-microvolt = <7>;
+	};
+
+	apc1_perfcl_vreg: regulator-perfcl {
+		compatible = "qcom,stub-regulator";
+		regulator-name = "apc1_perfcl_corner";
+		regulator-min-microvolt = <1>;
+		regulator-max-microvolt = <7>;
+	};
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm670.dtsi b/arch/arm64/boot/dts/qcom/sdm670.dtsi
index 3c1aeee..927e0b2 100644
--- a/arch/arm64/boot/dts/qcom/sdm670.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670.dtsi
@@ -19,6 +19,7 @@
 #include <dt-bindings/clock/qcom,videocc-sdm845.h>
 #include <dt-bindings/clock/qcom,cpucc-sdm845.h>
 #include <dt-bindings/clock/qcom,rpmh.h>
+#include <dt-bindings/regulator/qcom,rpmh-regulator.h>
 
 / {
 	model = "Qualcomm Technologies, Inc. SDM670";
@@ -825,3 +826,5 @@
 &venus_gdsc {
 	status = "ok";
 };
+
+#include "sdm670-regulator.dtsi"
diff --git a/arch/arm64/boot/dts/qcom/sdm845.dtsi b/arch/arm64/boot/dts/qcom/sdm845.dtsi
index 8d9ef1f..7ea200e 100644
--- a/arch/arm64/boot/dts/qcom/sdm845.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845.dtsi
@@ -345,25 +345,31 @@
 	};
 
 	energy-costs {
+		compatible = "sched-energy";
+
 		CPU_COST_0: core-cost0 {
 			busy-cost-data = <
-				 92   34 /*  300000 */
-				129   40 /*  422400 */
-				153   43 /*  499200 */
-				177   48 /*  576000 */
-				200   52 /*  652800 */
-				230   58 /*  748800 */
-				253   64 /*  825600 */
-				277   70 /*  902400 */
-				301   76 /*  979200 */
-				324   83 /* 1056000 */
-				348   90 /* 1132800 */
-				371   98 /* 1209600 */
-				395  105 /* 1286400 */
-				419  114 /* 1363200 */
-				442  123 /* 1440000 */
-				466  135 /* 1516800 */
-				490  152 /* 1593600 */
+				 300000   31
+				 422400   38
+				 499200   42
+				 576000   46
+				 652800   51
+				 748800   58
+				 825600   64
+				 902400   70
+				 979200   76
+				1056000   83
+				1132800   90
+				1209600   97
+				1286400  105
+				1363200  114
+				1440000  124
+				1516800  136
+				1593600  152
+				1651200  167 /* speedbin 0,1 */
+				1670400  173 /* speedbin 2 */
+				1708800  186 /* speedbin 0,1 */
+				1747200  201 /* speedbin 2 */
 			>;
 			idle-cost-data = <
 				22 18 14 12
@@ -371,28 +377,32 @@
 		};
 		CPU_COST_1: core-cost1 {
 			busy-cost-data = <
-				156  240 /*  300000 */
-				220  247 /*  422400 */
-				261  252 /*  499200 */
-				301  257 /*  576000 */
-				341  264 /*  652800 */
-				381  272 /*  729600 */
-				421  281 /*  806400 */
-				461  292 /*  883200 */
-				501  306 /*  960000 */
-				542  324 /* 1036800 */
-				582  346 /* 1113600 */
-				622  373 /* 1190400 */
-				662  407 /* 1267200 */
-				702  450 /* 1344000 */
-				742  504 /* 1420800 */
-				783  570 /* 1497600 */
-				823  649 /* 1574400 */
-				863  743 /* 1651200 */
-				903  849 /* 1728000 */
-				943  960 /* 1804800 */
-				983 1062 /* 1881600 */
-			       1024 1131 /* 1958400 */
+				300000   258
+				422400   260
+				499200   261
+				576000   263
+				652800   267
+				729600   272
+				806400   280
+				883200   291
+				960000   305
+			       1036800   324
+			       1113600   348
+			       1190400   378
+			       1267200   415
+			       1344000   460
+			       1420800   513
+			       1497600   576
+			       1574400   649
+			       1651200   732
+			       1728000   824
+			       1804800   923
+			       1881600  1027
+			       1958400  1131
+			       2035000  1228 /* speedbin 1,2 */
+			       2092000  1290 /* speedbin 1 */
+			       2112000  1308 /* speedbin 2 */
+			       2208000  1363 /* speedbin 2 */
 			>;
 			idle-cost-data = <
 				100 80 60 40
@@ -400,23 +410,27 @@
 		};
 		CLUSTER_COST_0: cluster-cost0 {
 			busy-cost-data = <
-				 92   3 /*  300000 */
-				129   4 /*  422400 */
-				153   4 /*  499200 */
-				177   4 /*  576000 */
-				200   5 /*  652800 */
-				230   5 /*  748800 */
-				253   6 /*  825600 */
-				277   7 /*  902400 */
-				301   7 /*  979200 */
-				324   8 /* 1056000 */
-				348   9 /* 1132800 */
-				371   9 /* 1209600 */
-				395  10 /* 1286400 */
-				419  11 /* 1363200 */
-				442  12 /* 1440000 */
-				466  13 /* 1516800 */
-				490  15 /* 1593600 */
+				 300000   3
+				 422400   4
+				 499200   4
+				 576000   4
+				 652800   5
+				 748800   5
+				 825600   6
+				 902400   7
+				 979200   7
+				1056000   8
+				1132800   9
+				1209600   9
+				1286400  10
+				1363200  11
+				1440000  12
+				1516800  13
+				1593600  15
+				1651200  17 /* speedbin 0,1 */
+				1670400  19 /* speedbin 2 */
+				1708800  21 /* speedbin 0,1 */
+				1747200  23 /* speedbin 2 */
 			>;
 			idle-cost-data = <
 				4 3 2 1
@@ -424,28 +438,32 @@
 		};
 		CLUSTER_COST_1: cluster-cost1 {
 			busy-cost-data = <
-				156  24 /*  300000 */
-				220  24 /*  422400 */
-				261  25 /*  499200 */
-				301  25 /*  576000 */
-				341  26 /*  652800 */
-				381  27 /*  729600 */
-				421  28 /*  806400 */
-				461  29 /*  883200 */
-				501  30 /*  960000 */
-				542  32 /* 1036800 */
-				582  34 /* 1113600 */
-				622  37 /* 1190400 */
-				662  40 /* 1267200 */
-				702  45 /* 1344000 */
-				742  50 /* 1420800 */
-				783  57 /* 1497600 */
-				823  64 /* 1574400 */
-				863  74 /* 1651200 */
-				903  84 /* 1728000 */
-				943  96 /* 1804800 */
-				983 106 /* 1881600 */
-			       1024 113 /* 1958400 */
+				300000  24
+				422400  24
+				499200  25
+				576000  25
+				652800  26
+				729600  27
+				806400  28
+				883200  29
+				960000  30
+			       1036800  32
+			       1113600  34
+			       1190400  37
+			       1267200  40
+			       1344000  45
+			       1420800  50
+			       1497600  57
+			       1574400  64
+			       1651200  74
+			       1728000  84
+			       1804800  96
+			       1881600 106
+			       1958400 113
+			       2035000 120 /* speedbin 1,2 */
+			       2092000 125 /* speedbin 1 */
+			       2112000 127 /* speedbin 2 */
+			       2208000 130 /* speedbin 2 */
 			>;
 			idle-cost-data = <
 				4 3 2 1
diff --git a/arch/arm64/configs/sdm845-perf_defconfig b/arch/arm64/configs/sdm845-perf_defconfig
index 36672cc..c69e015 100644
--- a/arch/arm64/configs/sdm845-perf_defconfig
+++ b/arch/arm64/configs/sdm845-perf_defconfig
@@ -83,7 +83,6 @@
 CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y
 CONFIG_CPU_BOOST=y
 CONFIG_CPU_FREQ_GOV_SCHEDUTIL=y
-CONFIG_CPU_FREQ_MSM=y
 CONFIG_NET=y
 CONFIG_PACKET=y
 CONFIG_UNIX=y
diff --git a/arch/arm64/configs/sdm845_defconfig b/arch/arm64/configs/sdm845_defconfig
index bd1a031..2da9d07 100644
--- a/arch/arm64/configs/sdm845_defconfig
+++ b/arch/arm64/configs/sdm845_defconfig
@@ -89,7 +89,6 @@
 CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y
 CONFIG_CPU_BOOST=y
 CONFIG_CPU_FREQ_GOV_SCHEDUTIL=y
-CONFIG_CPU_FREQ_MSM=y
 CONFIG_NET=y
 CONFIG_PACKET=y
 CONFIG_UNIX=y
diff --git a/arch/arm64/include/asm/topology.h b/arch/arm64/include/asm/topology.h
index 61e032f2..b58f429 100644
--- a/arch/arm64/include/asm/topology.h
+++ b/arch/arm64/include/asm/topology.h
@@ -41,6 +41,9 @@ extern unsigned long cpufreq_scale_max_freq_capacity(int cpu);
 #define arch_scale_cpu_capacity scale_cpu_capacity
 extern unsigned long scale_cpu_capacity(struct sched_domain *sd, int cpu);
 
+#define arch_update_cpu_capacity update_cpu_power_capacity
+extern void update_cpu_power_capacity(int cpu);
+
 #include <asm-generic/topology.h>
 
 #endif /* _ASM_ARM_TOPOLOGY_H */
diff --git a/arch/arm64/kernel/topology.c b/arch/arm64/kernel/topology.c
index aaf4bd7..7b670f1 100644
--- a/arch/arm64/kernel/topology.c
+++ b/arch/arm64/kernel/topology.c
@@ -449,6 +449,12 @@ static void update_cpu_capacity(unsigned int cpu)
 		cpu, arch_scale_cpu_capacity(NULL, cpu));
 }
 
+void update_cpu_power_capacity(int cpu)
+{
+	update_cpu_power(cpu);
+	update_cpu_capacity(cpu);
+}
+
 static void update_siblings_masks(unsigned int cpuid)
 {
 	struct cpu_topology *cpu_topo, *cpuid_topo = &cpu_topology[cpuid];
@@ -510,8 +516,6 @@ void store_cpu_topology(unsigned int cpuid)
 
 topology_populated:
 	update_siblings_masks(cpuid);
-	update_cpu_power(cpuid);
-	update_cpu_capacity(cpuid);
 }
 
 static void __init reset_cpu_topology(void)
diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h
index dff4f46..77dde55 100644
--- a/drivers/gpu/drm/msm/msm_drv.h
+++ b/drivers/gpu/drm/msm/msm_drv.h
@@ -206,6 +206,16 @@ enum msm_display_caps {
 };
 
 /**
+ * enum msm_event_wait - type of HW events to wait for
+ * @MSM_ENC_COMMIT_DONE - wait for the driver to flush the registers to HW
+ * @MSM_ENC_TX_COMPLETE - wait for the HW to transfer the frame to panel
+ */
+enum msm_event_wait {
+	MSM_ENC_COMMIT_DONE = 0,
+	MSM_ENC_TX_COMPLETE,
+};
+
+/**
  * struct msm_roi_alignment - region of interest alignment restrictions
  * @xstart_pix_align: left x offset alignment restriction
  * @width_pix_align: width alignment restriction
diff --git a/drivers/gpu/drm/msm/msm_kms.h b/drivers/gpu/drm/msm/msm_kms.h
index aa1b090..d8ac407 100644
--- a/drivers/gpu/drm/msm/msm_kms.h
+++ b/drivers/gpu/drm/msm/msm_kms.h
@@ -62,6 +62,9 @@ struct msm_kms_funcs {
 	/* functions to wait for atomic commit completed on each CRTC */
 	void (*wait_for_crtc_commit_done)(struct msm_kms *kms,
 					struct drm_crtc *crtc);
+	/* function pointer to wait for pixel transfer to panel to complete*/
+	void (*wait_for_tx_complete)(struct msm_kms *kms,
+					struct drm_crtc *crtc);
 	/* get msm_format w/ optional format modifiers from drm_mode_fb_cmd2 */
 	const struct msm_format *(*get_format)(struct msm_kms *kms,
 					const uint32_t format,
diff --git a/drivers/gpu/drm/msm/sde/sde_core_irq.c b/drivers/gpu/drm/msm/sde/sde_core_irq.c
index 7875d95..cec2b5f 100644
--- a/drivers/gpu/drm/msm/sde/sde_core_irq.c
+++ b/drivers/gpu/drm/msm/sde/sde_core_irq.c
@@ -195,6 +195,12 @@ u32 sde_core_irq_read(struct sde_kms *sde_kms, int irq_idx, bool clear)
 			!sde_kms->hw_intr->ops.get_interrupt_status)
 		return 0;
 
+	if (irq_idx < 0) {
+		SDE_ERROR("[%pS] invalid irq_idx=%d\n",
+				__builtin_return_address(0), irq_idx);
+		return 0;
+	}
+
 	return sde_kms->hw_intr->ops.get_interrupt_status(sde_kms->hw_intr,
 			irq_idx, clear);
 }
diff --git a/drivers/gpu/drm/msm/sde/sde_crtc.c b/drivers/gpu/drm/msm/sde/sde_crtc.c
index d5c23ea..e708290 100644
--- a/drivers/gpu/drm/msm/sde/sde_crtc.c
+++ b/drivers/gpu/drm/msm/sde/sde_crtc.c
@@ -2143,6 +2143,7 @@ void sde_crtc_commit_kickoff(struct drm_crtc *crtc)
 		SDE_DEBUG("crtc%d commit\n", crtc->base.id);
 		SDE_EVT32(DRMID(crtc), 2);
 	}
+	sde_crtc->play_count++;
 
 	list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
 		if (encoder->crtc != crtc)
@@ -2828,43 +2829,67 @@ static int sde_crtc_atomic_check(struct drm_crtc *crtc,
 		goto end;
 	}
 
-	/*
-	 * enforce pipe priority restrictions
+	/* validate source split:
 	 * use pstates sorted by stage to check planes on same stage
 	 * we assume that all pipes are in source split so its valid to compare
 	 * without taking into account left/right mixer placement
 	 */
 	for (i = 1; i < cnt; i++) {
 		struct plane_state *prv_pstate, *cur_pstate;
-		int32_t prv_x, cur_x, prv_id, cur_id;
+		struct sde_rect left_rect, right_rect;
+		int32_t left_pid, right_pid;
+		int32_t stage;
 
 		prv_pstate = &pstates[i - 1];
 		cur_pstate = &pstates[i];
 		if (prv_pstate->stage != cur_pstate->stage)
 			continue;
 
-		prv_x = prv_pstate->drm_pstate->crtc_x;
-		cur_x = cur_pstate->drm_pstate->crtc_x;
-		prv_id = prv_pstate->sde_pstate->base.plane->base.id;
-		cur_id = cur_pstate->sde_pstate->base.plane->base.id;
+		stage = cur_pstate->stage;
 
-		/*
-		 * Planes are enumerated in pipe-priority order such that planes
-		 * with lower drm_id must be left-most in a shared blend-stage
-		 * when using source split.
+		left_pid = prv_pstate->sde_pstate->base.plane->base.id;
+		POPULATE_RECT(&left_rect, prv_pstate->drm_pstate->crtc_x,
+			prv_pstate->drm_pstate->crtc_y,
+			prv_pstate->drm_pstate->crtc_w,
+			prv_pstate->drm_pstate->crtc_h, false);
+
+		right_pid = cur_pstate->sde_pstate->base.plane->base.id;
+		POPULATE_RECT(&right_rect, cur_pstate->drm_pstate->crtc_x,
+			cur_pstate->drm_pstate->crtc_y,
+			cur_pstate->drm_pstate->crtc_w,
+			cur_pstate->drm_pstate->crtc_h, false);
+
+		if (right_rect.x < left_rect.x) {
+			swap(left_pid, right_pid);
+			swap(left_rect, right_rect);
+		}
+
+		/**
+		 * - planes are enumerated in pipe-priority order such that
+		 *   planes with lower drm_id must be left-most in a shared
+		 *   blend-stage when using source split.
+		 * - planes in source split must be contiguous in width
+		 * - planes in source split must have same dest yoff and height
 		 */
-		if (cur_x > prv_x && cur_id < prv_id) {
+		if (right_pid < left_pid) {
 			SDE_ERROR(
-				"shared z_pos %d lower id plane%d @ x%d should be left of plane%d @ x %d\n",
-				cur_pstate->stage, cur_id, cur_x,
-				prv_id, prv_x);
+				"invalid src split cfg. priority mismatch. stage: %d left: %d right: %d\n",
+				stage, left_pid, right_pid);
 			rc = -EINVAL;
 			goto end;
-		} else if (cur_x < prv_x && cur_id > prv_id) {
+		} else if (right_rect.x != (left_rect.x + left_rect.w)) {
 			SDE_ERROR(
-				"shared z_pos %d lower id plane%d @ x%d should be left of plane%d @ x %d\n",
-				cur_pstate->stage, prv_id, prv_x,
-				cur_id, cur_x);
+				"non-contiguous coordinates for src split. stage: %d left: %d - %d right: %d - %d\n",
+				stage, left_rect.x, left_rect.w,
+				right_rect.x, right_rect.w);
+			rc = -EINVAL;
+			goto end;
+		} else if ((left_rect.y != right_rect.y) ||
+				(left_rect.h != right_rect.h)) {
+			SDE_ERROR(
+				"source split at stage: %d. invalid yoff/height: l_y: %d r_y: %d l_h: %d r_h: %d\n",
+				stage, left_rect.y, right_rect.y,
+				left_rect.h, right_rect.h);
 			rc = -EINVAL;
 			goto end;
 		}
@@ -3308,10 +3333,9 @@ static int _sde_debugfs_status_show(struct seq_file *s, void *data)
 				sde_crtc->vblank_cb_count * 1000, diff_ms) : 0;
 
 		seq_printf(s,
-			"vblank fps:%lld count:%u total:%llums\n",
-				fps,
-				sde_crtc->vblank_cb_count,
-				ktime_to_ms(diff));
+			"vblank fps:%lld count:%u total:%llums total_framecount:%llu\n",
+				fps, sde_crtc->vblank_cb_count,
+				ktime_to_ms(diff), sde_crtc->play_count);
 
 		/* reset time & count for next measurement */
 		sde_crtc->vblank_cb_count = 0;
diff --git a/drivers/gpu/drm/msm/sde/sde_crtc.h b/drivers/gpu/drm/msm/sde/sde_crtc.h
index a622d9c..38311c1 100644
--- a/drivers/gpu/drm/msm/sde/sde_crtc.h
+++ b/drivers/gpu/drm/msm/sde/sde_crtc.h
@@ -121,6 +121,7 @@ struct sde_crtc_event {
  * @stage_cfg     : H/w mixer stage configuration
  * @debugfs_root  : Parent of debugfs node
  * @vblank_cb_count : count of vblank callback since last reset
+ * @play_count    : frame count between crtc enable and disable
  * @vblank_cb_time  : ktime at vblank count reset
  * @vblank_refcount : reference count for vblank enable request
  * @suspend         : whether or not a suspend operation is in progress
@@ -167,6 +168,7 @@ struct sde_crtc {
 	struct dentry *debugfs_root;
 
 	u32 vblank_cb_count;
+	u64 play_count;
 	ktime_t vblank_cb_time;
 	atomic_t vblank_refcount;
 	bool suspend;
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder.c b/drivers/gpu/drm/msm/sde/sde_encoder.c
index c7ac00c..e1caeaf 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder.c
+++ b/drivers/gpu/drm/msm/sde/sde_encoder.c
@@ -36,6 +36,7 @@
 #include "sde_hw_dsc.h"
 #include "sde_crtc.h"
 #include "sde_trace.h"
+#include "sde_core_irq.h"
 
 #define SDE_DEBUG_ENC(e, fmt, ...) SDE_DEBUG("enc%d " fmt,\
 		(e) ? (e)->base.base.id : -1, ##__VA_ARGS__)
@@ -43,6 +44,18 @@
 #define SDE_ERROR_ENC(e, fmt, ...) SDE_ERROR("enc%d " fmt,\
 		(e) ? (e)->base.base.id : -1, ##__VA_ARGS__)
 
+#define SDE_DEBUG_PHYS(p, fmt, ...) SDE_DEBUG("enc%d intf%d pp%d " fmt,\
+		(p) ? (p)->parent->base.id : -1, \
+		(p) ? (p)->intf_idx - INTF_0 : -1, \
+		(p) ? ((p)->hw_pp ? (p)->hw_pp->idx - PINGPONG_0 : -1) : -1, \
+		##__VA_ARGS__)
+
+#define SDE_ERROR_PHYS(p, fmt, ...) SDE_ERROR("enc%d intf%d pp%d " fmt,\
+		(p) ? (p)->parent->base.id : -1, \
+		(p) ? (p)->intf_idx - INTF_0 : -1, \
+		(p) ? ((p)->hw_pp ? (p)->hw_pp->idx - PINGPONG_0 : -1) : -1, \
+		##__VA_ARGS__)
+
 /* timeout in frames waiting for frame done */
 #define SDE_ENCODER_FRAME_DONE_TIMEOUT	60
 
@@ -278,6 +291,174 @@ static inline int _sde_encoder_power_enable(struct sde_encoder_virt *sde_enc,
 									enable);
 }
 
+void sde_encoder_helper_report_irq_timeout(struct sde_encoder_phys *phys_enc,
+		enum sde_intr_idx intr_idx)
+{
+	SDE_EVT32(DRMID(phys_enc->parent),
+			phys_enc->intf_idx - INTF_0,
+			phys_enc->hw_pp->idx - PINGPONG_0,
+			intr_idx);
+	SDE_ERROR_PHYS(phys_enc, "irq %d timeout\n", intr_idx);
+
+	if (phys_enc->parent_ops.handle_frame_done)
+		phys_enc->parent_ops.handle_frame_done(
+				phys_enc->parent, phys_enc,
+				SDE_ENCODER_FRAME_EVENT_ERROR);
+}
+
+int sde_encoder_helper_wait_for_irq(struct sde_encoder_phys *phys_enc,
+		enum sde_intr_idx intr_idx,
+		struct sde_encoder_wait_info *wait_info)
+{
+	struct sde_encoder_irq *irq;
+	u32 irq_status;
+	int ret;
+
+	if (!phys_enc || !wait_info || intr_idx >= INTR_IDX_MAX) {
+		SDE_ERROR("invalid params\n");
+		return -EINVAL;
+	}
+	irq = &phys_enc->irq[intr_idx];
+
+	/* note: do master / slave checking outside */
+
+	/* return EWOULDBLOCK since we know the wait isn't necessary */
+	if (phys_enc->enable_state == SDE_ENC_DISABLED) {
+		SDE_ERROR_PHYS(phys_enc, "encoder is disabled\n");
+		return -EWOULDBLOCK;
+	}
+
+	if (irq->irq_idx < 0) {
+		SDE_DEBUG_PHYS(phys_enc, "irq %s hw %d disabled, skip wait\n",
+				irq->name, irq->hw_idx);
+		SDE_EVT32(DRMID(phys_enc->parent), intr_idx, irq->hw_idx,
+				irq->irq_idx);
+		return 0;
+	}
+
+	SDE_DEBUG_PHYS(phys_enc, "pending_cnt %d\n",
+			atomic_read(wait_info->atomic_cnt));
+	SDE_EVT32(DRMID(phys_enc->parent), irq->hw_idx,
+			atomic_read(wait_info->atomic_cnt),
+			SDE_EVTLOG_FUNC_ENTRY);
+
+	ret = sde_encoder_helper_wait_event_timeout(
+			DRMID(phys_enc->parent),
+			irq->hw_idx,
+			wait_info);
+
+	if (ret <= 0) {
+		irq_status = sde_core_irq_read(phys_enc->sde_kms,
+				irq->irq_idx, true);
+		if (irq_status) {
+			unsigned long flags;
+
+			SDE_EVT32(DRMID(phys_enc->parent),
+					irq->hw_idx,
+					atomic_read(wait_info->atomic_cnt));
+			SDE_DEBUG_PHYS(phys_enc,
+					"done but irq %d not triggered\n",
+					irq->irq_idx);
+			local_irq_save(flags);
+			irq->cb.func(phys_enc, irq->irq_idx);
+			local_irq_restore(flags);
+			ret = 0;
+		} else {
+			ret = -ETIMEDOUT;
+		}
+	} else {
+		ret = 0;
+	}
+
+	SDE_EVT32(DRMID(phys_enc->parent), irq->hw_idx, ret,
+			SDE_EVTLOG_FUNC_EXIT);
+
+	return ret;
+}
+
+int sde_encoder_helper_register_irq(struct sde_encoder_phys *phys_enc,
+		enum sde_intr_idx intr_idx)
+{
+	struct sde_encoder_irq *irq;
+	int ret = 0;
+
+	if (!phys_enc || intr_idx >= INTR_IDX_MAX) {
+		SDE_ERROR("invalid params\n");
+		return -EINVAL;
+	}
+	irq = &phys_enc->irq[intr_idx];
+
+	if (irq->irq_idx >= 0) {
+		SDE_ERROR_PHYS(phys_enc,
+				"skipping already registered irq %s type %d\n",
+				irq->name, irq->intr_type);
+		return 0;
+	}
+
+	irq->irq_idx = sde_core_irq_idx_lookup(phys_enc->sde_kms,
+			irq->intr_type, irq->hw_idx);
+	if (irq->irq_idx < 0) {
+		SDE_ERROR_PHYS(phys_enc,
+			"failed to lookup IRQ index for %s type:%d\n",
+			irq->name, irq->intr_type);
+		return -EINVAL;
+	}
+
+	ret = sde_core_irq_register_callback(phys_enc->sde_kms, irq->irq_idx,
+			&irq->cb);
+	if (ret) {
+		SDE_ERROR_PHYS(phys_enc,
+			"failed to register IRQ callback for %s\n",
+			irq->name);
+		irq->irq_idx = -EINVAL;
+		return ret;
+	}
+
+	ret = sde_core_irq_enable(phys_enc->sde_kms, &irq->irq_idx, 1);
+	if (ret) {
+		SDE_ERROR_PHYS(phys_enc,
+			"enable IRQ for intr:%s failed, irq_idx %d\n",
+			irq->name, irq->irq_idx);
+
+		sde_core_irq_unregister_callback(phys_enc->sde_kms,
+				irq->irq_idx, &irq->cb);
+		irq->irq_idx = -EINVAL;
+		return ret;
+	}
+
+	SDE_EVT32(DRMID(phys_enc->parent), intr_idx, irq->hw_idx, irq->irq_idx);
+	SDE_DEBUG_PHYS(phys_enc, "registered irq %s idx: %d\n",
+			irq->name, irq->irq_idx);
+
+	return ret;
+}
+
+int sde_encoder_helper_unregister_irq(struct sde_encoder_phys *phys_enc,
+		enum sde_intr_idx intr_idx)
+{
+	struct sde_encoder_irq *irq;
+
+	if (!phys_enc) {
+		SDE_ERROR("invalid encoder\n");
+		return -EINVAL;
+	}
+	irq = &phys_enc->irq[intr_idx];
+
+	/* silently skip irqs that weren't registered */
+	if (irq->irq_idx < 0)
+		return 0;
+
+	sde_core_irq_disable(phys_enc->sde_kms, &irq->irq_idx, 1);
+	sde_core_irq_unregister_callback(phys_enc->sde_kms, irq->irq_idx,
+			&irq->cb);
+	irq->irq_idx = -EINVAL;
+
+	SDE_EVT32(DRMID(phys_enc->parent), intr_idx, irq->hw_idx, irq->irq_idx);
+	SDE_DEBUG_PHYS(phys_enc, "unregistered %d\n", irq->irq_idx);
+
+	return 0;
+}
+
 void sde_encoder_get_hw_resources(struct drm_encoder *drm_enc,
 		struct sde_encoder_hw_resources *hw_res,
 		struct drm_connector_state *conn_state)
@@ -1787,23 +1968,23 @@ void sde_encoder_helper_trigger_start(struct sde_encoder_phys *phys_enc)
 int sde_encoder_helper_wait_event_timeout(
 		int32_t drm_id,
 		int32_t hw_id,
-		wait_queue_head_t *wq,
-		atomic_t *cnt,
-		s64 timeout_ms)
+		struct sde_encoder_wait_info *info)
 {
 	int rc = 0;
-	s64 expected_time = ktime_to_ms(ktime_get()) + timeout_ms;
-	s64 jiffies = msecs_to_jiffies(timeout_ms);
+	s64 expected_time = ktime_to_ms(ktime_get()) + info->timeout_ms;
+	s64 jiffies = msecs_to_jiffies(info->timeout_ms);
 	s64 time;
 
 	do {
-		rc = wait_event_timeout(*wq, atomic_read(cnt) == 0, jiffies);
+		rc = wait_event_timeout(*(info->wq),
+				atomic_read(info->atomic_cnt) == 0, jiffies);
 		time = ktime_to_ms(ktime_get());
 
 		SDE_EVT32(drm_id, hw_id, rc, time, expected_time,
-				atomic_read(cnt));
+				atomic_read(info->atomic_cnt));
 	/* If we timed out, counter is valid and time is less, wait again */
-	} while (atomic_read(cnt) && (rc == 0) && (time < expected_time));
+	} while (atomic_read(info->atomic_cnt) && (rc == 0) &&
+			(time < expected_time));
 
 	return rc;
 }
@@ -2812,8 +2993,10 @@ struct drm_encoder *sde_encoder_init(
 	return ERR_PTR(ret);
 }
 
-int sde_encoder_wait_for_commit_done(struct drm_encoder *drm_enc)
+int sde_encoder_wait_for_event(struct drm_encoder *drm_enc,
+	enum msm_event_wait event)
 {
+	int (*fn_wait)(struct sde_encoder_phys *phys_enc) = NULL;
 	struct sde_encoder_virt *sde_enc = NULL;
 	int i, ret = 0;
 
@@ -2827,8 +3010,17 @@ int sde_encoder_wait_for_commit_done(struct drm_encoder *drm_enc)
 	for (i = 0; i < sde_enc->num_phys_encs; i++) {
 		struct sde_encoder_phys *phys = sde_enc->phys_encs[i];
 
-		if (phys && phys->ops.wait_for_commit_done) {
-			ret = phys->ops.wait_for_commit_done(phys);
+		switch (event) {
+		case MSM_ENC_COMMIT_DONE:
+			fn_wait = phys->ops.wait_for_commit_done;
+			break;
+		case MSM_ENC_TX_COMPLETE:
+			fn_wait = phys->ops.wait_for_tx_complete;
+			break;
+		};
+
+		if (phys && fn_wait) {
+			ret = fn_wait(phys);
 			if (ret)
 				return ret;
 		}
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder.h b/drivers/gpu/drm/msm/sde/sde_encoder.h
index 28b6e1c..0b14a58 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder.h
+++ b/drivers/gpu/drm/msm/sde/sde_encoder.h
@@ -128,14 +128,24 @@ void sde_encoder_trigger_kickoff_pending(struct drm_encoder *encoder);
 void sde_encoder_kickoff(struct drm_encoder *encoder);
 
 /**
- * sde_encoder_wait_nxt_committed - Wait for hardware to have flushed the
- *	current pending frames to hardware at a vblank or ctl_start
- *	Encoders will map this differently depending on irqs
- *	vid mode -> vsync_irq
+ * sde_encoder_wait_for_event - Waits for encoder events
  * @encoder:	encoder pointer
+ * @event:      event to wait for
+ * MSM_ENC_COMMIT_DONE -  Wait for hardware to have flushed the current pending
+ *                        frames to hardware at a vblank or ctl_start
+ *                        Encoders will map this differently depending on the
+ *                        panel type.
+ *	                  vid mode -> vsync_irq
+ *                        cmd mode -> ctl_start
+ * MSM_ENC_TX_COMPLETE -  Wait for the hardware to transfer all the pixels to
+ *                        the panel. Encoders will map this differently
+ *                        depending on the panel type.
+ *                        vid mode -> vsync_irq
+ *                        cmd mode -> pp_done
  * Returns: 0 on success, -EWOULDBLOCK if already signaled, error otherwise
  */
-int sde_encoder_wait_for_commit_done(struct drm_encoder *drm_encoder);
+int sde_encoder_wait_for_event(struct drm_encoder *drm_encoder,
+						enum msm_event_wait event);
 
 /*
  * sde_encoder_get_intf_mode - get interface mode of the given encoder
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder_phys.h b/drivers/gpu/drm/msm/sde/sde_encoder_phys.h
index 9911666..6e6960a 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder_phys.h
+++ b/drivers/gpu/drm/msm/sde/sde_encoder_phys.h
@@ -111,6 +111,8 @@ struct sde_encoder_virt_ops {
  * @control_vblank_irq		Register/Deregister for VBLANK IRQ
  * @wait_for_commit_done:	Wait for hardware to have flushed the
  *				current pending frames to hardware
+ * @wait_for_tx_complete:	Wait for hardware to transfer the pixels
+ *				to the panel
  * @prepare_for_kickoff:	Do any work necessary prior to a kickoff
  *				For CMD encoder, may wait for previous tx done
  * @handle_post_kickoff:	Do any work necessary post-kickoff work
@@ -147,6 +149,7 @@ struct sde_encoder_phys_ops {
 			struct drm_connector_state *conn_state);
 	int (*control_vblank_irq)(struct sde_encoder_phys *enc, bool enable);
 	int (*wait_for_commit_done)(struct sde_encoder_phys *phys_enc);
+	int (*wait_for_tx_complete)(struct sde_encoder_phys *phys_enc);
 	void (*prepare_for_kickoff)(struct sde_encoder_phys *phys_enc,
 			struct sde_encoder_kickoff_params *params);
 	void (*handle_post_kickoff)(struct sde_encoder_phys *phys_enc);
@@ -180,6 +183,25 @@ enum sde_intr_idx {
 };
 
 /**
+ * sde_encoder_irq - tracking structure for interrupts
+ * @name:		string name of interrupt
+ * @intr_type:		Encoder interrupt type
+ * @intr_idx:		Encoder interrupt enumeration
+ * @hw_idx:		HW Block ID
+ * @irq_idx:		IRQ interface lookup index from SDE IRQ framework
+ *			will be -EINVAL if IRQ is not registered
+ * @irq_cb:		interrupt callback
+ */
+struct sde_encoder_irq {
+	const char *name;
+	enum sde_intr_type intr_type;
+	enum sde_intr_idx intr_idx;
+	int hw_idx;
+	int irq_idx;
+	struct sde_irq_callback cb;
+};
+
+/**
  * struct sde_encoder_phys - physical encoder that drives a single INTF block
  *	tied to a specific panel / sub-panel. Abstract type, sub-classed by
  *	phys_vid or phys_cmd for video mode or command mode encs respectively.
@@ -211,6 +233,7 @@ enum sde_intr_idx {
  * @pending_ctlstart_cnt:	Atomic counter tracking the number of ctl start
  *                              pending.
  * @pending_kickoff_wq:		Wait queue for blocking until kickoff completes
+ * @irq:			IRQ tracking structures
  */
 struct sde_encoder_phys {
 	struct drm_encoder *parent;
@@ -236,6 +259,7 @@ struct sde_encoder_phys {
 	atomic_t pending_ctlstart_cnt;
 	atomic_t pending_kickoff_cnt;
 	wait_queue_head_t pending_kickoff_wq;
+	struct sde_encoder_irq irq[INTR_IDX_MAX];
 };
 
 static inline int sde_encoder_phys_inc_pending(struct sde_encoder_phys *phys)
@@ -248,16 +272,12 @@ static inline int sde_encoder_phys_inc_pending(struct sde_encoder_phys *phys)
  * struct sde_encoder_phys_vid - sub-class of sde_encoder_phys to handle video
  *	mode specific operations
  * @base:	Baseclass physical encoder structure
- * @irq_idx:	IRQ interface lookup index
- * @irq_cb:	interrupt callback
  * @hw_intf:	Hardware interface to the intf registers
  * @timing_params: Current timing parameter
  * @rot_prefill_line: number of line to prefill for inline rotation; 0 disable
  */
 struct sde_encoder_phys_vid {
 	struct sde_encoder_phys base;
-	int irq_idx[INTR_IDX_MAX];
-	struct sde_irq_callback irq_cb[INTR_IDX_MAX];
 	struct sde_hw_intf *hw_intf;
 	struct intf_timing_params timing_params;
 	u64 rot_prefill_line;
@@ -269,10 +289,6 @@ struct sde_encoder_phys_vid {
  * @base:	Baseclass physical encoder structure
  * @intf_idx:	Intf Block index used by this phys encoder
  * @stream_sel:	Stream selection for multi-stream interfaces
- * @pp_rd_ptr_irq_idx:	IRQ signifying panel's frame read pointer
- *			For CMD encoders, VBLANK is driven by the PP RD Done IRQ
- * @pp_tx_done_irq_idx:	IRQ signifying frame transmission to panel complete
- * @irq_cb:		interrupt callback
  * @serialize_wait4pp:	serialize wait4pp feature waits for pp_done interrupt
  *			after ctl_start instead of before next frame kickoff
  * @pp_timeout_report_cnt: number of pingpong done irq timeout errors
@@ -280,8 +296,6 @@ struct sde_encoder_phys_vid {
 struct sde_encoder_phys_cmd {
 	struct sde_encoder_phys base;
 	int stream_sel;
-	int irq_idx[INTR_IDX_MAX];
-	struct sde_irq_callback irq_cb[INTR_IDX_MAX];
 	bool serialize_wait4pp;
 	int pp_timeout_report_cnt;
 };
@@ -355,6 +369,18 @@ struct sde_enc_phys_init_params {
 };
 
 /**
+ * sde_encoder_wait_info - container for passing arguments to irq wait functions
+ * @wq: wait queue structure
+ * @atomic_cnt: wait until atomic_cnt equals zero
+ * @timeout_ms: timeout value in milliseconds
+ */
+struct sde_encoder_wait_info {
+	wait_queue_head_t *wq;
+	atomic_t *atomic_cnt;
+	s64 timeout_ms;
+};
+
+/**
  * sde_encoder_phys_vid_init - Construct a new video mode physical encoder
  * @p:	Pointer to init params structure
  * Return: Error code or newly allocated encoder
@@ -406,16 +432,12 @@ void sde_encoder_helper_trigger_start(struct sde_encoder_phys *phys_enc);
  *	making sure that elapsed time during wait is valid.
  * @drm_id: drm object id for logging
  * @hw_id: hw instance id for logging
- * @wq: wait queue structure
- * @cnt: atomic counter to wait on
- * @timeout_ms: timeout value in milliseconds
+ * @info: wait info structure
  */
 int sde_encoder_helper_wait_event_timeout(
 		int32_t drm_id,
 		int32_t hw_id,
-		wait_queue_head_t *wq,
-		atomic_t *cnt,
-		s64 timeout_ms);
+		struct sde_encoder_wait_info *info);
 
 /**
  * sde_encoder_helper_hw_reset - issue ctl hw reset
@@ -463,4 +485,43 @@ void sde_encoder_helper_split_config(
 int sde_encoder_helper_hw_release(struct sde_encoder_phys *phys_enc,
 		struct drm_framebuffer *fb);
 
+/**
+ * sde_encoder_helper_report_irq_timeout - utility to report error that irq has
+ *	timed out, including reporting frame error event to crtc and debug dump
+ * @phys_enc: Pointer to physical encoder structure
+ * @intr_idx: Failing interrupt index
+ */
+void sde_encoder_helper_report_irq_timeout(struct sde_encoder_phys *phys_enc,
+		enum sde_intr_idx intr_idx);
+
+/**
+ * sde_encoder_helper_wait_for_irq - utility to wait on an irq.
+ *	note: will call sde_encoder_helper_wait_for_irq on timeout
+ * @phys_enc: Pointer to physical encoder structure
+ * @intr_idx: encoder interrupt index
+ * @wait_info: wait info struct
+ * @Return: 0 or -ERROR
+ */
+int sde_encoder_helper_wait_for_irq(struct sde_encoder_phys *phys_enc,
+		enum sde_intr_idx intr_idx,
+		struct sde_encoder_wait_info *wait_info);
+
+/**
+ * sde_encoder_helper_register_irq - register and enable an irq
+ * @phys_enc: Pointer to physical encoder structure
+ * @intr_idx: encoder interrupt index
+ * @Return: 0 or -ERROR
+ */
+int sde_encoder_helper_register_irq(struct sde_encoder_phys *phys_enc,
+		enum sde_intr_idx intr_idx);
+
+/**
+ * sde_encoder_helper_unregister_irq - unregister and disable an irq
+ * @phys_enc: Pointer to physical encoder structure
+ * @intr_idx: encoder interrupt index
+ * @Return: 0 or -ERROR
+ */
+int sde_encoder_helper_unregister_irq(struct sde_encoder_phys *phys_enc,
+		enum sde_intr_idx intr_idx);
+
 #endif /* __sde_encoder_phys_H__ */
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 dbfdc89..447fdcc 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder_phys_cmd.c
+++ b/drivers/gpu/drm/msm/sde/sde_encoder_phys_cmd.c
@@ -99,6 +99,98 @@ static void _sde_encoder_phys_cmd_update_intf_cfg(
 	ctl->ops.setup_intf_cfg(ctl, &intf_cfg);
 }
 
+static void sde_encoder_phys_cmd_pp_tx_done_irq(void *arg, int irq_idx)
+{
+	struct sde_encoder_phys *phys_enc = arg;
+	unsigned long lock_flags;
+	int new_cnt;
+
+	if (!phys_enc)
+		return;
+
+	/* notify all synchronous clients first, then asynchronous clients */
+	if (phys_enc->parent_ops.handle_frame_done)
+		phys_enc->parent_ops.handle_frame_done(phys_enc->parent,
+				phys_enc, SDE_ENCODER_FRAME_EVENT_DONE);
+
+	spin_lock_irqsave(phys_enc->enc_spinlock, lock_flags);
+	new_cnt = atomic_add_unless(&phys_enc->pending_kickoff_cnt, -1, 0);
+	spin_unlock_irqrestore(phys_enc->enc_spinlock, lock_flags);
+
+	SDE_EVT32_IRQ(DRMID(phys_enc->parent),
+			phys_enc->hw_pp->idx - PINGPONG_0, new_cnt);
+
+	/* Signal any waiting atomic commit thread */
+	wake_up_all(&phys_enc->pending_kickoff_wq);
+}
+
+static void sde_encoder_phys_cmd_pp_rd_ptr_irq(void *arg, int irq_idx)
+{
+	struct sde_encoder_phys *phys_enc = arg;
+
+	if (!phys_enc)
+		return;
+
+	SDE_EVT32_IRQ(DRMID(phys_enc->parent),
+			phys_enc->hw_pp->idx - PINGPONG_0, 0xfff);
+
+	if (phys_enc->parent_ops.handle_vblank_virt)
+		phys_enc->parent_ops.handle_vblank_virt(phys_enc->parent,
+			phys_enc);
+}
+
+static void sde_encoder_phys_cmd_ctl_start_irq(void *arg, int irq_idx)
+{
+	struct sde_encoder_phys *phys_enc = arg;
+	struct sde_hw_ctl *ctl;
+
+	if (!phys_enc)
+		return;
+
+	if (!phys_enc->hw_ctl)
+		return;
+
+	ctl = phys_enc->hw_ctl;
+	SDE_EVT32_IRQ(DRMID(phys_enc->parent), ctl->idx - CTL_0, 0xfff);
+	atomic_add_unless(&phys_enc->pending_ctlstart_cnt, -1, 0);
+
+	/* Signal any waiting ctl start interrupt */
+	wake_up_all(&phys_enc->pending_kickoff_wq);
+}
+
+static void sde_encoder_phys_cmd_underrun_irq(void *arg, int irq_idx)
+{
+	struct sde_encoder_phys *phys_enc = arg;
+
+	if (!phys_enc)
+		return;
+
+	if (phys_enc->parent_ops.handle_underrun_virt)
+		phys_enc->parent_ops.handle_underrun_virt(phys_enc->parent,
+			phys_enc);
+}
+
+static void _sde_encoder_phys_cmd_setup_irq_hw_idx(
+		struct sde_encoder_phys *phys_enc)
+{
+	struct sde_encoder_irq *irq;
+
+	irq = &phys_enc->irq[INTR_IDX_CTL_START];
+	irq->hw_idx = phys_enc->hw_ctl->idx;
+	irq->irq_idx = -EINVAL;
+
+	irq = &phys_enc->irq[INTR_IDX_PINGPONG];
+	irq->hw_idx = phys_enc->hw_pp->idx;
+	irq->irq_idx = -EINVAL;
+
+	irq = &phys_enc->irq[INTR_IDX_RDPTR];
+	irq->hw_idx = phys_enc->hw_pp->idx;
+	irq->irq_idx = -EINVAL;
+
+	irq = &phys_enc->irq[INTR_IDX_UNDERRUN];
+	irq->hw_idx = phys_enc->intf_idx;
+	irq->irq_idx = -EINVAL;
+}
 
 static void sde_encoder_phys_cmd_mode_set(
 		struct sde_encoder_phys *phys_enc,
@@ -112,8 +204,7 @@ static void sde_encoder_phys_cmd_mode_set(
 	int i, instance;
 
 	if (!phys_enc || !mode || !adj_mode) {
-		SDE_ERROR("invalid arg(s), enc %d mode %d adj_mode %d\n",
-				phys_enc != 0, mode != 0, adj_mode != 0);
+		SDE_ERROR("invalid args\n");
 		return;
 	}
 	phys_enc->cached_mode = *adj_mode;
@@ -135,71 +226,8 @@ static void sde_encoder_phys_cmd_mode_set(
 		phys_enc->hw_ctl = NULL;
 		return;
 	}
-}
 
-static void sde_encoder_phys_cmd_pp_tx_done_irq(void *arg, int irq_idx)
-{
-	struct sde_encoder_phys_cmd *cmd_enc = arg;
-	struct sde_encoder_phys *phys_enc;
-	unsigned long lock_flags;
-	int new_cnt;
-
-	if (!cmd_enc)
-		return;
-
-	phys_enc = &cmd_enc->base;
-
-	/* notify all synchronous clients first, then asynchronous clients */
-	if (phys_enc->parent_ops.handle_frame_done)
-		phys_enc->parent_ops.handle_frame_done(phys_enc->parent,
-				phys_enc, SDE_ENCODER_FRAME_EVENT_DONE);
-
-	spin_lock_irqsave(phys_enc->enc_spinlock, lock_flags);
-	new_cnt = atomic_add_unless(&phys_enc->pending_kickoff_cnt, -1, 0);
-	spin_unlock_irqrestore(phys_enc->enc_spinlock, lock_flags);
-
-	SDE_EVT32_IRQ(DRMID(phys_enc->parent),
-			phys_enc->hw_pp->idx - PINGPONG_0, new_cnt);
-
-	/* Signal any waiting atomic commit thread */
-	wake_up_all(&phys_enc->pending_kickoff_wq);
-}
-
-static void sde_encoder_phys_cmd_pp_rd_ptr_irq(void *arg, int irq_idx)
-{
-	struct sde_encoder_phys_cmd *cmd_enc = arg;
-	struct sde_encoder_phys *phys_enc = &cmd_enc->base;
-
-	if (!cmd_enc)
-		return;
-
-	SDE_EVT32_IRQ(DRMID(phys_enc->parent),
-			phys_enc->hw_pp->idx - PINGPONG_0, 0xfff);
-
-	if (phys_enc->parent_ops.handle_vblank_virt)
-		phys_enc->parent_ops.handle_vblank_virt(phys_enc->parent,
-			phys_enc);
-}
-
-static void sde_encoder_phys_cmd_ctl_start_irq(void *arg, int irq_idx)
-{
-	struct sde_encoder_phys_cmd *cmd_enc = arg;
-	struct sde_encoder_phys *phys_enc;
-	struct sde_hw_ctl *ctl;
-
-	if (!cmd_enc)
-		return;
-
-	phys_enc = &cmd_enc->base;
-	if (!phys_enc->hw_ctl)
-		return;
-
-	ctl = phys_enc->hw_ctl;
-	SDE_EVT32_IRQ(DRMID(phys_enc->parent), ctl->idx - CTL_0, 0xfff);
-	atomic_add_unless(&phys_enc->pending_ctlstart_cnt, -1, 0);
-
-	/* Signal any waiting ctl start interrupt */
-	wake_up_all(&phys_enc->pending_kickoff_wq);
+	_sde_encoder_phys_cmd_setup_irq_hw_idx(phys_enc);
 }
 
 static bool _sde_encoder_phys_is_ppsplit(struct sde_encoder_phys *phys_enc)
@@ -279,7 +307,7 @@ static int _sde_encoder_phys_cmd_wait_for_idle(
 {
 	struct sde_encoder_phys_cmd *cmd_enc =
 			to_sde_encoder_phys_cmd(phys_enc);
-	u32 irq_status;
+	struct sde_encoder_wait_info wait_info;
 	int ret;
 
 	if (!phys_enc) {
@@ -287,154 +315,24 @@ static int _sde_encoder_phys_cmd_wait_for_idle(
 		return -EINVAL;
 	}
 
+	wait_info.wq = &phys_enc->pending_kickoff_wq;
+	wait_info.atomic_cnt = &phys_enc->pending_kickoff_cnt;
+	wait_info.timeout_ms = KICKOFF_TIMEOUT_MS;
+
 	/* slave encoder doesn't enable for ppsplit */
 	if (_sde_encoder_phys_is_ppsplit_slave(phys_enc))
 		return 0;
 
-	/* return EWOULDBLOCK since we know the wait isn't necessary */
-	if (phys_enc->enable_state == SDE_ENC_DISABLED) {
-		SDE_ERROR_CMDENC(cmd_enc, "encoder is disabled\n");
-		return -EWOULDBLOCK;
-	}
-
-	/* wait for previous kickoff to complete */
-	ret = sde_encoder_helper_wait_event_timeout(
-			DRMID(phys_enc->parent),
-			phys_enc->hw_pp->idx - PINGPONG_0,
-			&phys_enc->pending_kickoff_wq,
-			&phys_enc->pending_kickoff_cnt,
-			KICKOFF_TIMEOUT_MS);
-	if (ret <= 0) {
-		/* read and clear interrupt */
-		irq_status = sde_core_irq_read(phys_enc->sde_kms,
-				cmd_enc->irq_idx[INTR_IDX_PINGPONG], true);
-		if (irq_status) {
-			unsigned long flags;
-			SDE_EVT32(DRMID(phys_enc->parent),
-					phys_enc->hw_pp->idx - PINGPONG_0);
-			SDE_DEBUG_CMDENC(cmd_enc,
-					"pp:%d done but irq not triggered\n",
-					phys_enc->hw_pp->idx - PINGPONG_0);
-			local_irq_save(flags);
-			sde_encoder_phys_cmd_pp_tx_done_irq(cmd_enc,
-					INTR_IDX_PINGPONG);
-			local_irq_restore(flags);
-			ret = 0;
-		} else {
-			ret = _sde_encoder_phys_cmd_handle_ppdone_timeout(
-					phys_enc);
-		}
-	} else {
-		ret = 0;
-	}
-
-	if (!ret)
+	ret = sde_encoder_helper_wait_for_irq(phys_enc, INTR_IDX_PINGPONG,
+			&wait_info);
+	if (ret == -ETIMEDOUT)
+		_sde_encoder_phys_cmd_handle_ppdone_timeout(phys_enc);
+	else if (!ret)
 		cmd_enc->pp_timeout_report_cnt = 0;
 
 	return ret;
 }
 
-static void sde_encoder_phys_cmd_underrun_irq(void *arg, int irq_idx)
-{
-	struct sde_encoder_phys_cmd *cmd_enc = arg;
-	struct sde_encoder_phys *phys_enc;
-
-	if (!cmd_enc)
-		return;
-
-	phys_enc = &cmd_enc->base;
-	if (phys_enc->parent_ops.handle_underrun_virt)
-		phys_enc->parent_ops.handle_underrun_virt(phys_enc->parent,
-			phys_enc);
-}
-
-static int sde_encoder_phys_cmd_register_irq(struct sde_encoder_phys *phys_enc,
-	enum sde_intr_type intr_type, int idx,
-	void (*irq_func)(void *, int), const char *irq_name)
-{
-	struct sde_encoder_phys_cmd *cmd_enc =
-			to_sde_encoder_phys_cmd(phys_enc);
-	int idx_lookup = 0;
-	int ret = 0;
-
-	if (!phys_enc) {
-		SDE_ERROR("invalid encoder\n");
-		return -EINVAL;
-	}
-
-	if (intr_type == SDE_IRQ_TYPE_INTF_UNDER_RUN)
-		idx_lookup = phys_enc->intf_idx;
-	else if (intr_type == SDE_IRQ_TYPE_CTL_START)
-		idx_lookup = phys_enc->hw_ctl ? phys_enc->hw_ctl->idx : -1;
-	else
-		idx_lookup = phys_enc->hw_pp->idx;
-
-	cmd_enc->irq_idx[idx] = sde_core_irq_idx_lookup(phys_enc->sde_kms,
-			intr_type, idx_lookup);
-	if (cmd_enc->irq_idx[idx] < 0) {
-		SDE_ERROR_CMDENC(cmd_enc,
-			"failed to lookup IRQ index for %s with pp=%d\n",
-			irq_name,
-			phys_enc->hw_pp->idx - PINGPONG_0);
-		return -EINVAL;
-	}
-
-	cmd_enc->irq_cb[idx].func = irq_func;
-	cmd_enc->irq_cb[idx].arg = cmd_enc;
-	ret = sde_core_irq_register_callback(phys_enc->sde_kms,
-			cmd_enc->irq_idx[idx], &cmd_enc->irq_cb[idx]);
-	if (ret) {
-		SDE_ERROR_CMDENC(cmd_enc,
-				"failed to register IRQ callback %s\n",
-				irq_name);
-		return ret;
-	}
-
-	ret = sde_core_irq_enable(phys_enc->sde_kms, &cmd_enc->irq_idx[idx], 1);
-	if (ret) {
-		SDE_ERROR_CMDENC(cmd_enc,
-			"failed to enable IRQ for %s, pp %d, irq_idx %d\n",
-			irq_name,
-			phys_enc->hw_pp->idx - PINGPONG_0,
-			cmd_enc->irq_idx[idx]);
-		cmd_enc->irq_idx[idx] = -EINVAL;
-
-		/* Unregister callback on IRQ enable failure */
-		sde_core_irq_unregister_callback(phys_enc->sde_kms,
-				cmd_enc->irq_idx[idx], &cmd_enc->irq_cb[idx]);
-		return ret;
-	}
-
-	SDE_DEBUG_CMDENC(cmd_enc, "registered IRQ %s for pp %d, irq_idx %d\n",
-			irq_name,
-			phys_enc->hw_pp->idx - PINGPONG_0,
-			cmd_enc->irq_idx[idx]);
-
-	return ret;
-}
-
-static int sde_encoder_phys_cmd_unregister_irq(
-		struct sde_encoder_phys *phys_enc, int idx)
-{
-	struct sde_encoder_phys_cmd *cmd_enc =
-			to_sde_encoder_phys_cmd(phys_enc);
-
-	if (!phys_enc) {
-		SDE_ERROR("invalid encoder\n");
-		return -EINVAL;
-	}
-
-	sde_core_irq_disable(phys_enc->sde_kms, &cmd_enc->irq_idx[idx], 1);
-	sde_core_irq_unregister_callback(phys_enc->sde_kms,
-			cmd_enc->irq_idx[idx], &cmd_enc->irq_cb[idx]);
-
-	SDE_DEBUG_CMDENC(cmd_enc, "unregistered IRQ for pp %d, irq_idx %d\n",
-			phys_enc->hw_pp->idx - PINGPONG_0,
-			cmd_enc->irq_idx[idx]);
-
-	return 0;
-}
-
 static int sde_encoder_phys_cmd_control_vblank_irq(
 		struct sde_encoder_phys *phys_enc,
 		bool enable)
@@ -460,13 +358,9 @@ static int sde_encoder_phys_cmd_control_vblank_irq(
 			enable, atomic_read(&phys_enc->vblank_refcount));
 
 	if (enable && atomic_inc_return(&phys_enc->vblank_refcount) == 1)
-		ret = sde_encoder_phys_cmd_register_irq(phys_enc,
-				SDE_IRQ_TYPE_PING_PONG_RD_PTR,
-				INTR_IDX_RDPTR,
-				sde_encoder_phys_cmd_pp_rd_ptr_irq,
-				"pp_rd_ptr");
+		ret = sde_encoder_helper_register_irq(phys_enc, INTR_IDX_RDPTR);
 	else if (!enable && atomic_dec_return(&phys_enc->vblank_refcount) == 0)
-		ret = sde_encoder_phys_cmd_unregister_irq(phys_enc,
+		ret = sde_encoder_helper_unregister_irq(phys_enc,
 				INTR_IDX_RDPTR);
 
 end:
@@ -489,35 +383,22 @@ void sde_encoder_phys_cmd_irq_control(struct sde_encoder_phys *phys_enc,
 	cmd_enc = to_sde_encoder_phys_cmd(phys_enc);
 
 	if (enable) {
-		sde_encoder_phys_cmd_register_irq(phys_enc,
-				SDE_IRQ_TYPE_PING_PONG_COMP,
-				INTR_IDX_PINGPONG,
-				sde_encoder_phys_cmd_pp_tx_done_irq,
-				"pp_tx_done");
-
+		sde_encoder_helper_register_irq(phys_enc, INTR_IDX_PINGPONG);
+		sde_encoder_helper_register_irq(phys_enc, INTR_IDX_UNDERRUN);
 		sde_encoder_phys_cmd_control_vblank_irq(phys_enc, true);
 
-		sde_encoder_phys_cmd_register_irq(phys_enc,
-				SDE_IRQ_TYPE_INTF_UNDER_RUN,
-				INTR_IDX_UNDERRUN,
-				sde_encoder_phys_cmd_underrun_irq,
-				"underrun");
+		if (sde_encoder_phys_cmd_is_master(phys_enc))
+			sde_encoder_helper_register_irq(phys_enc,
+					INTR_IDX_CTL_START);
+	} else {
 
 		if (sde_encoder_phys_cmd_is_master(phys_enc))
-			sde_encoder_phys_cmd_register_irq(phys_enc,
-				SDE_IRQ_TYPE_CTL_START,
-				INTR_IDX_CTL_START,
-				sde_encoder_phys_cmd_ctl_start_irq,
-				"ctl_start");
-	} else {
-		if (sde_encoder_phys_cmd_is_master(phys_enc))
-			sde_encoder_phys_cmd_unregister_irq(
-				phys_enc, INTR_IDX_CTL_START);
-		sde_encoder_phys_cmd_unregister_irq(
-				phys_enc, INTR_IDX_UNDERRUN);
+			sde_encoder_helper_unregister_irq(phys_enc,
+					INTR_IDX_CTL_START);
+
+		sde_encoder_helper_unregister_irq(phys_enc, INTR_IDX_UNDERRUN);
 		sde_encoder_phys_cmd_control_vblank_irq(phys_enc, false);
-		sde_encoder_phys_cmd_unregister_irq(
-				phys_enc, INTR_IDX_PINGPONG);
+		sde_encoder_helper_unregister_irq(phys_enc, INTR_IDX_PINGPONG);
 	}
 }
 
@@ -776,45 +657,51 @@ static void sde_encoder_phys_cmd_prepare_for_kickoff(
 static int _sde_encoder_phys_cmd_wait_for_ctl_start(
 		struct sde_encoder_phys *phys_enc)
 {
-	int rc = 0;
-	struct sde_hw_ctl *ctl;
-	u32 irq_status;
-	struct sde_encoder_phys_cmd *cmd_enc;
+	struct sde_encoder_phys_cmd *cmd_enc =
+			to_sde_encoder_phys_cmd(phys_enc);
+	struct sde_encoder_wait_info wait_info;
+	int ret;
 
-	if (!phys_enc->hw_ctl) {
-		SDE_ERROR("invalid ctl\n");
+	if (!phys_enc) {
+		SDE_ERROR("invalid encoder\n");
 		return -EINVAL;
 	}
 
-	ctl = phys_enc->hw_ctl;
-	cmd_enc = to_sde_encoder_phys_cmd(phys_enc);
-	rc = sde_encoder_helper_wait_event_timeout(DRMID(phys_enc->parent),
-			ctl->idx - CTL_0,
-			&phys_enc->pending_kickoff_wq,
-			&phys_enc->pending_ctlstart_cnt,
-			CTL_START_TIMEOUT_MS);
-	if (rc <= 0) {
-		/* read and clear interrupt */
-		irq_status = sde_core_irq_read(phys_enc->sde_kms,
-				cmd_enc->irq_idx[INTR_IDX_CTL_START], true);
-		if (irq_status) {
-			unsigned long flags;
+	wait_info.wq = &phys_enc->pending_kickoff_wq;
+	wait_info.atomic_cnt = &phys_enc->pending_ctlstart_cnt;
+	wait_info.timeout_ms = CTL_START_TIMEOUT_MS;
 
-			SDE_EVT32(DRMID(phys_enc->parent), ctl->idx - CTL_0);
-			SDE_DEBUG_CMDENC(cmd_enc,
-					"ctl:%d start done but irq not triggered\n",
-					ctl->idx - CTL_0);
-			local_irq_save(flags);
-			sde_encoder_phys_cmd_ctl_start_irq(cmd_enc,
-					INTR_IDX_CTL_START);
-			local_irq_restore(flags);
-			rc = 0;
-		} else {
-			SDE_ERROR("ctl start interrupt wait failed\n");
-			rc = -EINVAL;
-		}
-	} else {
-		rc = 0;
+	/* slave encoder doesn't enable for ppsplit */
+	if (_sde_encoder_phys_is_ppsplit_slave(phys_enc))
+		return 0;
+
+	ret = sde_encoder_helper_wait_for_irq(phys_enc, INTR_IDX_CTL_START,
+			&wait_info);
+	if (ret == -ETIMEDOUT) {
+		SDE_ERROR_CMDENC(cmd_enc, "ctl start interrupt wait failed\n");
+		ret = -EINVAL;
+	} else if (!ret)
+		ret = 0;
+
+	return ret;
+}
+
+static int sde_encoder_phys_cmd_wait_for_tx_complete(
+		struct sde_encoder_phys *phys_enc)
+{
+	int rc;
+	struct sde_encoder_phys_cmd *cmd_enc;
+
+	if (!phys_enc)
+		return -EINVAL;
+
+	cmd_enc = to_sde_encoder_phys_cmd(phys_enc);
+
+	rc = _sde_encoder_phys_cmd_wait_for_idle(phys_enc);
+	if (rc) {
+		SDE_EVT32(DRMID(phys_enc->parent),
+				phys_enc->intf_idx - INTF_0);
+		SDE_ERROR("failed wait_for_idle: %d\n", rc);
 	}
 
 	return rc;
@@ -894,6 +781,7 @@ static void sde_encoder_phys_cmd_init_ops(
 	ops->control_vblank_irq = sde_encoder_phys_cmd_control_vblank_irq;
 	ops->wait_for_commit_done = sde_encoder_phys_cmd_wait_for_commit_done;
 	ops->prepare_for_kickoff = sde_encoder_phys_cmd_prepare_for_kickoff;
+	ops->wait_for_tx_complete = sde_encoder_phys_cmd_wait_for_tx_complete;
 	ops->trigger_start = sde_encoder_helper_trigger_start;
 	ops->needs_single_flush = sde_encoder_phys_cmd_needs_single_flush;
 	ops->hw_reset = sde_encoder_helper_hw_reset;
@@ -908,6 +796,7 @@ struct sde_encoder_phys *sde_encoder_phys_cmd_init(
 	struct sde_encoder_phys *phys_enc = NULL;
 	struct sde_encoder_phys_cmd *cmd_enc = NULL;
 	struct sde_hw_mdp *hw_mdp;
+	struct sde_encoder_irq *irq;
 	int i, ret = 0;
 
 	SDE_DEBUG("intf %d\n", p->intf_idx - INTF_0);
@@ -939,8 +828,38 @@ struct sde_encoder_phys *sde_encoder_phys_cmd_init(
 	cmd_enc->stream_sel = 0;
 	phys_enc->enable_state = SDE_ENC_DISABLED;
 	phys_enc->comp_type = p->comp_type;
-	for (i = 0; i < INTR_IDX_MAX; i++)
-		INIT_LIST_HEAD(&cmd_enc->irq_cb[i].list);
+	for (i = 0; i < INTR_IDX_MAX; i++) {
+		irq = &phys_enc->irq[i];
+		INIT_LIST_HEAD(&irq->cb.list);
+		irq->irq_idx = -EINVAL;
+		irq->hw_idx = -EINVAL;
+		irq->cb.arg = phys_enc;
+	}
+
+	irq = &phys_enc->irq[INTR_IDX_CTL_START];
+	irq->name = "ctl_start";
+	irq->intr_type = SDE_IRQ_TYPE_CTL_START;
+	irq->intr_idx = INTR_IDX_CTL_START;
+	irq->cb.func = sde_encoder_phys_cmd_ctl_start_irq;
+
+	irq = &phys_enc->irq[INTR_IDX_PINGPONG];
+	irq->name = "pp_done";
+	irq->intr_type = SDE_IRQ_TYPE_PING_PONG_COMP;
+	irq->intr_idx = INTR_IDX_PINGPONG;
+	irq->cb.func = sde_encoder_phys_cmd_pp_tx_done_irq;
+
+	irq = &phys_enc->irq[INTR_IDX_RDPTR];
+	irq->name = "pp_rd_ptr";
+	irq->intr_type = SDE_IRQ_TYPE_PING_PONG_RD_PTR;
+	irq->intr_idx = INTR_IDX_RDPTR;
+	irq->cb.func = sde_encoder_phys_cmd_pp_rd_ptr_irq;
+
+	irq = &phys_enc->irq[INTR_IDX_UNDERRUN];
+	irq->name = "underrun";
+	irq->intr_type = SDE_IRQ_TYPE_INTF_UNDER_RUN;
+	irq->intr_idx = INTR_IDX_UNDERRUN;
+	irq->cb.func = sde_encoder_phys_cmd_underrun_irq;
+
 	atomic_set(&phys_enc->vblank_refcount, 0);
 	atomic_set(&phys_enc->pending_kickoff_cnt, 0);
 	atomic_set(&phys_enc->pending_ctlstart_cnt, 0);
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 4f28a5f..007738a6 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c
+++ b/drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c
@@ -345,17 +345,17 @@ static void sde_encoder_phys_vid_setup_timing_engine(
 
 static void sde_encoder_phys_vid_vblank_irq(void *arg, int irq_idx)
 {
-	struct sde_encoder_phys_vid *vid_enc = arg;
-	struct sde_encoder_phys *phys_enc;
+	struct sde_encoder_phys *phys_enc = arg;
+	struct sde_encoder_phys_vid *vid_enc =
+			to_sde_encoder_phys_vid(phys_enc);
 	struct sde_hw_ctl *hw_ctl;
 	unsigned long lock_flags;
 	u32 flush_register = 0;
 	int new_cnt = -1, old_cnt = -1;
 
-	if (!vid_enc)
+	if (!phys_enc)
 		return;
 
-	phys_enc = &vid_enc->base;
 	hw_ctl = phys_enc->hw_ctl;
 
 	if (phys_enc->parent_ops.handle_vblank_virt)
@@ -387,13 +387,11 @@ static void sde_encoder_phys_vid_vblank_irq(void *arg, int irq_idx)
 
 static void sde_encoder_phys_vid_underrun_irq(void *arg, int irq_idx)
 {
-	struct sde_encoder_phys_vid *vid_enc = arg;
-	struct sde_encoder_phys *phys_enc;
+	struct sde_encoder_phys *phys_enc = arg;
 
-	if (!vid_enc)
+	if (!phys_enc)
 		return;
 
-	phys_enc = &vid_enc->base;
 	if (phys_enc->parent_ops.handle_underrun_virt)
 		phys_enc->parent_ops.handle_underrun_virt(phys_enc->parent,
 			phys_enc);
@@ -419,77 +417,18 @@ static bool sde_encoder_phys_vid_needs_single_flush(
 	return phys_enc && _sde_encoder_phys_is_ppsplit(phys_enc);
 }
 
-static int sde_encoder_phys_vid_register_irq(struct sde_encoder_phys *phys_enc,
-	enum sde_intr_type intr_type, int idx,
-	void (*irq_func)(void *, int), const char *irq_name)
+static void _sde_encoder_phys_vid_setup_irq_hw_idx(
+		struct sde_encoder_phys *phys_enc)
 {
-	struct sde_encoder_phys_vid *vid_enc;
-	int ret = 0;
+	struct sde_encoder_irq *irq;
 
-	if (!phys_enc) {
-		SDE_ERROR("invalid encoder\n");
-		return -EINVAL;
-	}
+	irq = &phys_enc->irq[INTR_IDX_VSYNC];
+	irq->hw_idx = phys_enc->intf_idx;
+	irq->irq_idx = -EINVAL;
 
-	vid_enc = to_sde_encoder_phys_vid(phys_enc);
-	vid_enc->irq_idx[idx] = sde_core_irq_idx_lookup(phys_enc->sde_kms,
-			intr_type, vid_enc->hw_intf->idx);
-	if (vid_enc->irq_idx[idx] < 0) {
-		SDE_ERROR_VIDENC(vid_enc,
-			"failed to lookup IRQ index for %s type:%d\n", irq_name,
-			intr_type);
-		return -EINVAL;
-	}
-
-	vid_enc->irq_cb[idx].func = irq_func;
-	vid_enc->irq_cb[idx].arg = vid_enc;
-	ret = sde_core_irq_register_callback(phys_enc->sde_kms,
-			vid_enc->irq_idx[idx], &vid_enc->irq_cb[idx]);
-	if (ret) {
-		SDE_ERROR_VIDENC(vid_enc,
-			"failed to register IRQ callback for %s\n", irq_name);
-		return ret;
-	}
-
-	ret = sde_core_irq_enable(phys_enc->sde_kms, &vid_enc->irq_idx[idx], 1);
-	if (ret) {
-		SDE_ERROR_VIDENC(vid_enc,
-			"enable IRQ for intr:%s failed, irq_idx %d\n",
-			irq_name, vid_enc->irq_idx[idx]);
-		vid_enc->irq_idx[idx] = -EINVAL;
-
-		/* unregister callback on IRQ enable failure */
-		sde_core_irq_unregister_callback(phys_enc->sde_kms,
-				vid_enc->irq_idx[idx], &vid_enc->irq_cb[idx]);
-		return ret;
-	}
-
-	SDE_DEBUG_VIDENC(vid_enc, "registered irq %s idx: %d\n",
-			irq_name, vid_enc->irq_idx[idx]);
-
-	return ret;
-}
-
-static int sde_encoder_phys_vid_unregister_irq(
-	struct sde_encoder_phys *phys_enc, int idx)
-{
-	struct sde_encoder_phys_vid *vid_enc;
-
-	if (!phys_enc) {
-		SDE_ERROR("invalid encoder\n");
-		goto end;
-	}
-
-	vid_enc = to_sde_encoder_phys_vid(phys_enc);
-	sde_core_irq_disable(phys_enc->sde_kms, &vid_enc->irq_idx[idx], 1);
-
-	sde_core_irq_unregister_callback(phys_enc->sde_kms,
-			vid_enc->irq_idx[idx], &vid_enc->irq_cb[idx]);
-
-	SDE_DEBUG_VIDENC(vid_enc, "unregistered %d\n", vid_enc->irq_idx[idx]);
-
-end:
-	return 0;
+	irq = &phys_enc->irq[INTR_IDX_UNDERRUN];
+	irq->hw_idx = phys_enc->intf_idx;
+	irq->irq_idx = -EINVAL;
 }
 
 static void sde_encoder_phys_vid_mode_set(
@@ -527,6 +466,8 @@ static void sde_encoder_phys_vid_mode_set(
 		phys_enc->hw_ctl = NULL;
 		return;
 	}
+
+	_sde_encoder_phys_vid_setup_irq_hw_idx(phys_enc);
 }
 
 static int sde_encoder_phys_vid_control_vblank_irq(
@@ -555,13 +496,10 @@ static int sde_encoder_phys_vid_control_vblank_irq(
 			atomic_read(&phys_enc->vblank_refcount));
 
 	if (enable && atomic_inc_return(&phys_enc->vblank_refcount) == 1)
-		ret = sde_encoder_phys_vid_register_irq(phys_enc,
-			SDE_IRQ_TYPE_INTF_VSYNC,
-			INTR_IDX_VSYNC,
-			sde_encoder_phys_vid_vblank_irq, "vsync_irq");
+		ret = sde_encoder_helper_register_irq(phys_enc, INTR_IDX_VSYNC);
 	else if (!enable && atomic_dec_return(&phys_enc->vblank_refcount) == 0)
-		ret = sde_encoder_phys_vid_unregister_irq(phys_enc,
-			INTR_IDX_VSYNC);
+		ret = sde_encoder_helper_unregister_irq(phys_enc,
+				INTR_IDX_VSYNC);
 
 	if (ret)
 		SDE_ERROR_VIDENC(vid_enc,
@@ -608,10 +546,7 @@ static void sde_encoder_phys_vid_enable(struct sde_encoder_phys *phys_enc)
 	if (ret)
 		goto end;
 
-	ret = sde_encoder_phys_vid_register_irq(phys_enc,
-		SDE_IRQ_TYPE_INTF_UNDER_RUN,
-		INTR_IDX_UNDERRUN,
-		sde_encoder_phys_vid_underrun_irq, "underrun");
+	ret = sde_encoder_helper_register_irq(phys_enc, INTR_IDX_UNDERRUN);
 	if (ret) {
 		sde_encoder_phys_vid_control_vblank_irq(phys_enc, false);
 		goto end;
@@ -680,9 +615,11 @@ static void sde_encoder_phys_vid_get_hw_resources(
 static int sde_encoder_phys_vid_wait_for_vblank(
 		struct sde_encoder_phys *phys_enc, bool notify)
 {
-	struct sde_encoder_phys_vid *vid_enc =
-			to_sde_encoder_phys_vid(phys_enc);
-	u32 irq_status;
+	struct sde_encoder_wait_info wait_info = {
+		.wq = &phys_enc->pending_kickoff_wq,
+		.atomic_cnt = &phys_enc->pending_kickoff_cnt,
+		.timeout_ms = KICKOFF_TIMEOUT_MS,
+	};
 	int ret;
 
 	if (!sde_encoder_phys_vid_is_master(phys_enc)) {
@@ -695,54 +632,18 @@ static int sde_encoder_phys_vid_wait_for_vblank(
 		return 0;
 	}
 
-	if (phys_enc->enable_state != SDE_ENC_ENABLED) {
-		SDE_ERROR("encoder not enabled\n");
-		return -EWOULDBLOCK;
-	}
-
-	SDE_EVT32(DRMID(phys_enc->parent), vid_enc->hw_intf->idx - INTF_0,
-			SDE_EVTLOG_FUNC_ENTRY);
-
 	/* Wait for kickoff to complete */
-	ret = sde_encoder_helper_wait_event_timeout(
-			DRMID(phys_enc->parent),
-			vid_enc->hw_intf->idx - INTF_0,
-			&phys_enc->pending_kickoff_wq,
-			&phys_enc->pending_kickoff_cnt,
-			KICKOFF_TIMEOUT_MS);
-	if (ret <= 0) {
-		irq_status = sde_core_irq_read(phys_enc->sde_kms,
-				vid_enc->irq_idx[INTR_IDX_VSYNC], true);
-		if (irq_status) {
-			SDE_EVT32(DRMID(phys_enc->parent),
-					vid_enc->hw_intf->idx - INTF_0);
-			SDE_DEBUG_VIDENC(vid_enc, "done, irq not triggered\n");
-			if (notify && phys_enc->parent_ops.handle_frame_done)
-				phys_enc->parent_ops.handle_frame_done(
-						phys_enc->parent, phys_enc,
-						SDE_ENCODER_FRAME_EVENT_DONE);
-			sde_encoder_phys_vid_vblank_irq(vid_enc,
-					INTR_IDX_VSYNC);
-			ret = 0;
-		} else {
-			SDE_EVT32(DRMID(phys_enc->parent),
-					vid_enc->hw_intf->idx - INTF_0);
-			SDE_ERROR_VIDENC(vid_enc, "kickoff timed out\n");
-			if (notify && phys_enc->parent_ops.handle_frame_done)
-				phys_enc->parent_ops.handle_frame_done(
-						phys_enc->parent, phys_enc,
-						SDE_ENCODER_FRAME_EVENT_ERROR);
-			ret = -ETIMEDOUT;
-		}
-	} else {
-		if (notify && phys_enc->parent_ops.handle_frame_done)
-			phys_enc->parent_ops.handle_frame_done(
-					phys_enc->parent, phys_enc,
-					SDE_ENCODER_FRAME_EVENT_DONE);
-		ret = 0;
-	}
+	ret = sde_encoder_helper_wait_for_irq(phys_enc, INTR_IDX_VSYNC,
+			&wait_info);
 
-	return 0;
+	if (ret == -ETIMEDOUT) {
+		sde_encoder_helper_report_irq_timeout(phys_enc, INTR_IDX_VSYNC);
+	} else if (!ret && notify && phys_enc->parent_ops.handle_frame_done)
+		phys_enc->parent_ops.handle_frame_done(
+				phys_enc->parent, phys_enc,
+				SDE_ENCODER_FRAME_EVENT_DONE);
+
+	return ret;
 }
 
 static int sde_encoder_phys_vid_wait_for_commit_done(
@@ -845,6 +746,8 @@ static void sde_encoder_phys_vid_disable(struct sde_encoder_phys *phys_enc)
 		sde_encoder_phys_vid_control_vblank_irq(phys_enc, false);
 	}
 
+	sde_encoder_helper_unregister_irq(phys_enc, INTR_IDX_UNDERRUN);
+
 	if (atomic_read(&phys_enc->vblank_refcount))
 		SDE_ERROR_VIDENC(vid_enc, "invalid vblank refcount %d\n",
 				atomic_read(&phys_enc->vblank_refcount));
@@ -932,6 +835,7 @@ struct sde_encoder_phys *sde_encoder_phys_vid_init(
 	struct sde_encoder_phys_vid *vid_enc = NULL;
 	struct sde_rm_hw_iter iter;
 	struct sde_hw_mdp *hw_mdp;
+	struct sde_encoder_irq *irq;
 	int i, ret = 0;
 
 	if (!p) {
@@ -986,8 +890,26 @@ struct sde_encoder_phys *sde_encoder_phys_vid_init(
 	phys_enc->intf_mode = INTF_MODE_VIDEO;
 	phys_enc->enc_spinlock = p->enc_spinlock;
 	phys_enc->comp_type = p->comp_type;
-	for (i = 0; i < INTR_IDX_MAX; i++)
-		INIT_LIST_HEAD(&vid_enc->irq_cb[i].list);
+	for (i = 0; i < INTR_IDX_MAX; i++) {
+		irq = &phys_enc->irq[i];
+		INIT_LIST_HEAD(&irq->cb.list);
+		irq->irq_idx = -EINVAL;
+		irq->hw_idx = -EINVAL;
+		irq->cb.arg = phys_enc;
+	}
+
+	irq = &phys_enc->irq[INTR_IDX_VSYNC];
+	irq->name = "vsync_irq";
+	irq->intr_type = SDE_IRQ_TYPE_INTF_VSYNC;
+	irq->intr_idx = INTR_IDX_VSYNC;
+	irq->cb.func = sde_encoder_phys_vid_vblank_irq;
+
+	irq = &phys_enc->irq[INTR_IDX_UNDERRUN];
+	irq->name = "underrun";
+	irq->intr_type = SDE_IRQ_TYPE_INTF_UNDER_RUN;
+	irq->intr_idx = INTR_IDX_UNDERRUN;
+	irq->cb.func = sde_encoder_phys_vid_underrun_irq;
+
 	atomic_set(&phys_enc->vblank_refcount, 0);
 	atomic_set(&phys_enc->pending_kickoff_cnt, 0);
 	init_waitqueue_head(&phys_enc->pending_kickoff_wq);
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_interrupts.c b/drivers/gpu/drm/msm/sde/sde_hw_interrupts.c
index 53a48c8..8c3d4fc 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_interrupts.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_interrupts.c
@@ -974,6 +974,11 @@ static u32 sde_hw_intr_get_interrupt_status(struct sde_hw_intr *intr,
 	if (!intr)
 		return 0;
 
+	if (irq_idx >= ARRAY_SIZE(sde_irq_map) || irq_idx < 0) {
+		pr_err("invalid IRQ index: [%d]\n", irq_idx);
+		return 0;
+	}
+
 	spin_lock_irqsave(&intr->mask_lock, irq_flags);
 
 	reg_idx = sde_irq_map[irq_idx].reg_idx;
diff --git a/drivers/gpu/drm/msm/sde/sde_kms.c b/drivers/gpu/drm/msm/sde/sde_kms.c
index 85e9c6c..bda89cf 100644
--- a/drivers/gpu/drm/msm/sde/sde_kms.c
+++ b/drivers/gpu/drm/msm/sde/sde_kms.c
@@ -412,6 +412,49 @@ static void sde_kms_complete_commit(struct msm_kms *kms,
 	SDE_EVT32(SDE_EVTLOG_FUNC_EXIT);
 }
 
+static void sde_kms_wait_for_tx_complete(struct msm_kms *kms,
+		struct drm_crtc *crtc)
+{
+	struct drm_encoder *encoder;
+	struct drm_device *dev;
+	int ret;
+
+	if (!kms || !crtc || !crtc->state || !crtc->dev) {
+		SDE_ERROR("invalid params\n");
+		return;
+	}
+
+	if (!crtc->state->enable) {
+		SDE_DEBUG("[crtc:%d] not enable\n", crtc->base.id);
+		return;
+	}
+
+	if (!crtc->state->active) {
+		SDE_DEBUG("[crtc:%d] not active\n", crtc->base.id);
+		return;
+	}
+
+	dev = crtc->dev;
+
+	list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
+		if (encoder->crtc != crtc)
+			continue;
+		/*
+		 * Video Mode - Wait for VSYNC
+		 * Cmd Mode   - Wait for PP_DONE. Will be no-op if transfer is
+		 *              complete
+		 */
+		SDE_EVT32_VERBOSE(DRMID(crtc));
+		ret = sde_encoder_wait_for_event(encoder, MSM_ENC_TX_COMPLETE);
+		if (ret && ret != -EWOULDBLOCK) {
+			SDE_ERROR(
+			"[crtc: %d][enc: %d] wait for commit done returned %d\n",
+			crtc->base.id, encoder->base.id, ret);
+			break;
+		}
+	}
+}
+
 static void sde_kms_wait_for_commit_done(struct msm_kms *kms,
 		struct drm_crtc *crtc)
 {
@@ -443,7 +486,7 @@ static void sde_kms_wait_for_commit_done(struct msm_kms *kms,
 		 * mode panels. This may be a no-op for command mode panels.
 		 */
 		SDE_EVT32_VERBOSE(DRMID(crtc));
-		ret = sde_encoder_wait_for_commit_done(encoder);
+		ret = sde_encoder_wait_for_event(encoder, MSM_ENC_COMMIT_DONE);
 		if (ret && ret != -EWOULDBLOCK) {
 			SDE_ERROR("wait for commit done returned %d\n", ret);
 			break;
@@ -1340,6 +1383,7 @@ static const struct msm_kms_funcs kms_funcs = {
 	.commit          = sde_kms_commit,
 	.complete_commit = sde_kms_complete_commit,
 	.wait_for_crtc_commit_done = sde_kms_wait_for_commit_done,
+	.wait_for_tx_complete = sde_kms_wait_for_tx_complete,
 	.enable_vblank   = sde_kms_enable_vblank,
 	.disable_vblank  = sde_kms_disable_vblank,
 	.check_modified_format = sde_format_check_modified_format,
diff --git a/drivers/media/platform/msm/camera/cam_core/cam_context_utils.c b/drivers/media/platform/msm/camera/cam_core/cam_context_utils.c
index 21a61ff..2a0c4a7 100644
--- a/drivers/media/platform/msm/camera/cam_core/cam_context_utils.c
+++ b/drivers/media/platform/msm/camera/cam_core/cam_context_utils.c
@@ -60,10 +60,6 @@ int cam_context_buf_done_from_hw(struct cam_context *ctx,
 		cam_sync_signal(req->out_map_entries[j].sync_id,
 			CAM_SYNC_STATE_SIGNALED_SUCCESS);
 		req->num_out_acked++;
-		trace_printk("Sync success req %lld, reset sync id 0x%x\n",
-			req->request_id,
-			req->out_map_entries[j].sync_id);
-
 		req->out_map_entries[j].sync_id = -1;
 	}
 
@@ -195,8 +191,6 @@ void cam_context_sync_callback(int32_t sync_obj, int status, void *data)
 	req->num_in_acked++;
 	if (req->num_in_acked == req->num_in_map_entries) {
 		apply.request_id = req->request_id;
-		trace_printk("async cb for request :%llu",
-			req->request_id);
 		cam_context_apply_req_to_hw(ctx, &apply);
 	}
 }
@@ -287,8 +281,6 @@ int32_t cam_context_prepare_dev_to_hw(struct cam_context *ctx,
 		list_add_tail(&req->list, &ctx->pending_req_list);
 		spin_unlock(&ctx->lock);
 		for (i = 0; i < req->num_in_map_entries; i++) {
-			trace_printk("register in fence callback: %d\n",
-				req->in_map_entries[i].sync_id);
 			rc = cam_sync_register_callback(
 					cam_context_sync_callback,
 					(void *)ctx,
diff --git a/drivers/media/platform/msm/camera/cam_core/cam_node.c b/drivers/media/platform/msm/camera/cam_core/cam_node.c
index 17f6973..74a94b2 100644
--- a/drivers/media/platform/msm/camera/cam_core/cam_node.c
+++ b/drivers/media/platform/msm/camera/cam_core/cam_node.c
@@ -16,6 +16,13 @@
 
 #include "cam_node.h"
 
+static void  __cam_node_handle_shutdown(struct cam_node *node)
+{
+	if (node->hw_mgr_intf.hw_close)
+		node->hw_mgr_intf.hw_close(node->hw_mgr_intf.hw_mgr_priv,
+			NULL);
+}
+
 static int __cam_node_handle_query_cap(struct cam_node *node,
 	struct cam_query_cap_cmd *query)
 {
@@ -408,6 +415,9 @@ int cam_node_handle_ioctl(struct cam_node *node, struct cam_control *cmd)
 		}
 		break;
 	}
+	case CAM_SD_SHUTDOWN:
+		__cam_node_handle_shutdown(node);
+		break;
 	default:
 		pr_err("Unknown op code %d\n", cmd->op_code);
 		rc = -EINVAL;
diff --git a/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_intf.c b/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_intf.c
index b774625..aba0caa 100644
--- a/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_intf.c
+++ b/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_intf.c
@@ -327,7 +327,7 @@ EXPORT_SYMBOL(cam_cpas_register_client);
 int cam_cpas_subdev_cmd(struct cam_cpas_intf *cpas_intf,
 	struct cam_control *cmd)
 {
-	int rc;
+	int rc = 0;
 
 	if (!cmd) {
 		pr_err("Invalid input cmd\n");
@@ -357,6 +357,8 @@ int cam_cpas_subdev_cmd(struct cam_cpas_intf *cpas_intf,
 
 		break;
 	}
+	case CAM_SD_SHUTDOWN:
+		break;
 	default:
 		pr_err("Unknown op code %d for CPAS\n", cmd->op_code);
 		rc = -EINVAL;
diff --git a/drivers/media/platform/msm/camera/cam_isp/cam_isp_dev.c b/drivers/media/platform/msm/camera/cam_isp/cam_isp_dev.c
index 4cebb58..4c819cf 100644
--- a/drivers/media/platform/msm/camera/cam_isp/cam_isp_dev.c
+++ b/drivers/media/platform/msm/camera/cam_isp/cam_isp_dev.c
@@ -72,6 +72,7 @@ static int cam_isp_dev_probe(struct platform_device *pdev)
 	}
 	node = (struct cam_node *) g_isp_dev.sd.token;
 
+	memset(&hw_mgr_intf, 0, sizeof(hw_mgr_intf));
 	rc = cam_isp_hw_mgr_init(pdev->dev.of_node, &hw_mgr_intf);
 	if (rc != 0) {
 		pr_err("%s: Can not initialized ISP HW manager!\n", __func__);
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_bus/cam_vfe_bus_ver2.c b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_bus/cam_vfe_bus_ver2.c
index 5e629b6..92a17d8 100644
--- a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_bus/cam_vfe_bus_ver2.c
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_bus/cam_vfe_bus_ver2.c
@@ -1581,6 +1581,7 @@ static int cam_vfe_bus_ver2_handle_irq(uint32_t    evt_id,
 	struct cam_vfe_bus_ver2_priv          *bus_priv;
 	struct cam_irq_controller_reg_info    *reg_info;
 	uint32_t                               irq_mask;
+	int                                    found = 0;
 
 	handler_priv = th_payload->handler_priv;
 	core_info    = handler_priv->core_info;
@@ -1613,6 +1614,8 @@ static int cam_vfe_bus_ver2_handle_irq(uint32_t    evt_id,
 			irq_reg_offset[i] - (0xC * 2));
 		evt_payload->irq_reg_val[i] = irq_mask &
 			cam_io_r(handler_priv->mem_base + irq_reg_offset[i]);
+		if (evt_payload->irq_reg_val[i])
+			found = 1;
 		CDBG("irq_status%d = 0x%x\n", i, evt_payload->irq_reg_val[i]);
 	}
 	for (i = 0; i <= CAM_IFE_IRQ_BUS_REG_STATUS2; i++) {
@@ -1628,7 +1631,13 @@ static int cam_vfe_bus_ver2_handle_irq(uint32_t    evt_id,
 			reg_info->global_clear_bitmask,
 			reg_info->global_clear_offset);
 
-	th_payload->evt_payload_priv = evt_payload;
+	if (found)
+		th_payload->evt_payload_priv = evt_payload;
+	else {
+		cam_vfe_bus_put_evt_payload(evt_payload->core_info,
+			&evt_payload);
+		rc = -ENOMSG;
+	}
 
 	return rc;
 }
diff --git a/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_dev.c b/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_dev.c
index 1a8356a..c495088 100644
--- a/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_dev.c
+++ b/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_dev.c
@@ -20,6 +20,7 @@
 #include <media/v4l2-event.h>
 #include <media/v4l2-ioctl.h>
 #include <media/cam_req_mgr.h>
+#include <media/cam_defs.h>
 #include "cam_req_mgr_dev.h"
 #include "cam_req_mgr_util.h"
 #include "cam_req_mgr_core.h"
@@ -151,6 +152,9 @@ static unsigned int cam_req_mgr_poll(struct file *f,
 
 static int cam_req_mgr_close(struct file *filep)
 {
+	struct v4l2_subdev *sd;
+	struct cam_control cam_ctrl;
+
 	mutex_lock(&g_dev.cam_lock);
 
 	if (g_dev.open_cnt <= 0) {
@@ -158,6 +162,14 @@ static int cam_req_mgr_close(struct file *filep)
 		return -EINVAL;
 	}
 
+	cam_ctrl.op_code = CAM_SD_SHUTDOWN;
+	list_for_each_entry(sd, &g_dev.v4l2_dev->subdevs, list) {
+		if (!(sd->flags & V4L2_SUBDEV_FL_HAS_DEVNODE))
+			continue;
+		v4l2_subdev_call(sd, core, ioctl, VIDIOC_CAM_CONTROL,
+			&cam_ctrl);
+	}
+
 	g_dev.open_cnt--;
 	v4l2_fh_release(filep);
 
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_actuator/cam_actuator_core.c b/drivers/media/platform/msm/camera/cam_sensor_module/cam_actuator/cam_actuator_core.c
index 648617e..91b68cf 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_actuator/cam_actuator_core.c
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_actuator/cam_actuator_core.c
@@ -616,6 +616,8 @@ int32_t cam_actuator_driver_cmd(struct cam_actuator_ctrl_t *a_ctrl,
 		}
 	}
 		break;
+	case CAM_SD_SHUTDOWN:
+		break;
 	default:
 		pr_err("%s:%d Invalid Opcode %d\n",
 			__func__, __LINE__, cmd->op_code);
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_cci/cam_cci_dev.c b/drivers/media/platform/msm/camera/cam_sensor_module/cam_cci/cam_cci_dev.c
index 789522d..6764b8a 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_cci/cam_cci_dev.c
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_cci/cam_cci_dev.c
@@ -34,6 +34,8 @@ static long cam_cci_subdev_ioctl(struct v4l2_subdev *sd,
 	case VIDIOC_MSM_CCI_CFG:
 		rc = cam_cci_core_cfg(sd, arg);
 		break;
+	case VIDIOC_CAM_CONTROL:
+		break;
 	default:
 		pr_err("%s:%d Invalid ioctl cmd: %d\n",
 			__func__, __LINE__, cmd);
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_csiphy/cam_csiphy_core.c b/drivers/media/platform/msm/camera/cam_sensor_module/cam_csiphy/cam_csiphy_core.c
index 8dc65f5..6751fdd 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_csiphy/cam_csiphy_core.c
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_csiphy/cam_csiphy_core.c
@@ -486,6 +486,8 @@ int32_t cam_csiphy_core_cfg(void *phy_dev,
 		}
 	}
 		break;
+	case CAM_SD_SHUTDOWN:
+		break;
 	default:
 		pr_err("%s:%d :Error: Invalid Opcode: %d\n",
 			__func__, __LINE__, cmd->op_code);
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor/cam_sensor_core.c b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor/cam_sensor_core.c
index 4888e5b..4fc3aa1 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor/cam_sensor_core.c
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor/cam_sensor_core.c
@@ -773,6 +773,8 @@ int32_t cam_sensor_driver_cmd(struct cam_sensor_ctrl_t *s_ctrl,
 		}
 	}
 		break;
+	case CAM_SD_SHUTDOWN:
+		break;
 	default:
 		pr_err("%s:%d :Error: Invalid Opcode: %d\n",
 			__func__, __LINE__, cmd->op_code);
diff --git a/drivers/media/platform/msm/camera/icp/fw_inc/hfi_reg.h b/drivers/media/platform/msm/camera/icp/fw_inc/hfi_reg.h
index d1bbe01..ff6b72a 100644
--- a/drivers/media/platform/msm/camera/icp/fw_inc/hfi_reg.h
+++ b/drivers/media/platform/msm/camera/icp/fw_inc/hfi_reg.h
@@ -92,17 +92,14 @@
 
 /**
  * @INVALID: Invalid state
- * @FW_LOAD_DONE: Firmware load is completed
- * @FW_RESP_DONE: Firmware response is received
- * @FW_START_SENT: firmware start is send
- * @FW_READY: firmware is ready to accept commands
+ * @HFI_DEINIT: HFI is not initialized yet
+ * @HFI_INIT: HFI is initialized
+ * @HFI_READY: HFI is ready to send/receive commands/messages
  */
 enum hfi_state {
-	INVALID,
-	FW_LOAD_DONE,
-	FW_RESP_DONE,
-	FW_START_SENT,
-	FW_READY
+	HFI_DEINIT,
+	HFI_INIT,
+	HFI_READY
 };
 
 /**
@@ -292,6 +289,9 @@ struct hfi_qtbl {
  * @msgpacket_buf: message buffer
  * @hfi_state: State machine for hfi
  * @cmd_q_lock: Lock for command queue
+ * @cmd_q_state: State of command queue
+ * @mutex msg_q_lock: Lock for message queue
+ * @msg_q_state: State of message queue
  * @csr_base: CSR base address
  */
 struct hfi_info {
@@ -301,7 +301,9 @@ struct hfi_info {
 	uint32_t msgpacket_buf[ICP_HFI_MAX_MSG_SIZE_IN_WORDS];
 	uint8_t hfi_state;
 	struct mutex cmd_q_lock;
+	bool cmd_q_state;
 	struct mutex msg_q_lock;
+	bool msg_q_state;
 	void __iomem *csr_base;
 };
 
diff --git a/drivers/media/platform/msm/camera/icp/hfi.c b/drivers/media/platform/msm/camera/icp/hfi.c
index 15e0315..170c8cf 100644
--- a/drivers/media/platform/msm/camera/icp/hfi.c
+++ b/drivers/media/platform/msm/camera/icp/hfi.c
@@ -42,7 +42,7 @@
 #undef  HFI_DBG
 #define HFI_DBG(fmt, args...) pr_debug(fmt, ##args)
 
-struct hfi_info *g_hfi;
+static struct hfi_info *g_hfi;
 unsigned int g_icp_mmu_hdl;
 
 int hfi_write_cmd(void *cmd_ptr)
@@ -59,12 +59,17 @@ int hfi_write_cmd(void *cmd_ptr)
 		return -EINVAL;
 	}
 
-	if (!g_hfi || g_hfi->hfi_state < FW_START_SENT) {
-		pr_err("FW not ready yet\n");
+	if (!g_hfi || (g_hfi->hfi_state != HFI_READY)) {
+		pr_err("HFI interface not ready yet\n");
 		return -EIO;
 	}
 
 	mutex_lock(&g_hfi->cmd_q_lock);
+	if (!g_hfi->cmd_q_state) {
+		pr_err("HFI command interface not ready yet\n");
+		mutex_unlock(&g_hfi->cmd_q_lock);
+		return -EIO;
+	}
 
 	q_tbl = (struct hfi_qtbl *)g_hfi->map.qtbl.kva;
 	q = &q_tbl->q_hdr[Q_CMD];
@@ -78,11 +83,10 @@ int hfi_write_cmd(void *cmd_ptr)
 		goto err;
 	}
 
-	HFI_DBG("size_in_words : %u\n", size_in_words);
-	HFI_DBG("q->qhdr_write_idx %x\n", q->qhdr_write_idx);
+	HFI_DBG("size_in_words : %u, q->qhdr_write_idx %x\n", size_in_words,
+		q->qhdr_write_idx);
 
 	read_idx = q->qhdr_read_idx;
-
 	empty_space = (q->qhdr_write_idx >= read_idx) ?
 		(q->qhdr_q_size - (q->qhdr_write_idx - read_idx)) :
 		(read_idx - q->qhdr_write_idx);
@@ -115,7 +119,7 @@ int hfi_write_cmd(void *cmd_ptr)
 		g_hfi->csr_base + HFI_REG_A5_CSR_HOST2ICPINT);
 err:
 	mutex_unlock(&g_hfi->cmd_q_lock);
-	return 0;
+	return rc;
 }
 
 int hfi_read_message(uint32_t *pmsg, uint8_t q_id)
@@ -132,21 +136,28 @@ int hfi_read_message(uint32_t *pmsg, uint8_t q_id)
 		return -EINVAL;
 	}
 
+	if (!g_hfi || (g_hfi->hfi_state != HFI_READY)) {
+		pr_err("HFI interface not ready yet\n");
+		return -EIO;
+	}
+
 	q_tbl_ptr = (struct hfi_qtbl *)g_hfi->map.qtbl.kva;
 	q = &q_tbl_ptr->q_hdr[q_id];
 
-	if ((g_hfi->hfi_state < FW_START_SENT) ||
-		(q->qhdr_read_idx == q->qhdr_write_idx)) {
+	if (q->qhdr_read_idx == q->qhdr_write_idx) {
 		pr_debug("FW or Q not ready, hfi state : %u, r idx : %u, w idx : %u\n",
 			g_hfi->hfi_state, q->qhdr_read_idx, q->qhdr_write_idx);
 		return -EIO;
 	}
 
 	mutex_lock(&g_hfi->msg_q_lock);
+	if (!g_hfi->msg_q_state) {
+		pr_err("HFI message interface not ready yet\n");
+		mutex_unlock(&g_hfi->msg_q_lock);
+		return -EIO;
+	}
 
-	if (q_id == Q_CMD)
-		read_q = (uint32_t *)g_hfi->map.cmd_q.kva;
-	else if (q_id == Q_MSG)
+	if (q_id == Q_MSG)
 		read_q = (uint32_t *)g_hfi->map.msg_q.kva;
 	else
 		read_q = (uint32_t *)g_hfi->map.dbg_q.kva;
@@ -154,8 +165,8 @@ int hfi_read_message(uint32_t *pmsg, uint8_t q_id)
 	read_ptr = (uint32_t *)(read_q + q->qhdr_read_idx);
 	size_in_words = (*read_ptr) >> BYTE_WORD_SHIFT;
 
-	HFI_DBG("size_in_words : %u\n", size_in_words);
-	HFI_DBG("read_ptr : %pK\n", (void *)read_ptr);
+	HFI_DBG("size_in_words : %u, read_ptr : %pK\n", size_in_words,
+		(void *)read_ptr);
 
 	if ((size_in_words == 0) ||
 		(size_in_words > ICP_HFI_MAX_MSG_SIZE_IN_WORDS)) {
@@ -180,13 +191,12 @@ int hfi_read_message(uint32_t *pmsg, uint8_t q_id)
 	}
 
 	for (i = 0; i < size_in_words; i++)
-		pr_debug("%x\n", read_ptr[i]);
+		HFI_DBG("%x\n", read_ptr[i]);
 
 	q->qhdr_read_idx = new_read_idx;
 err:
 	mutex_unlock(&g_hfi->msg_q_lock);
-	HFI_DBG("Exit\n");
-	return 0;
+	return rc;
 }
 
 void hfi_send_system_cmd(uint32_t type, uint64_t data, uint32_t size)
@@ -307,8 +317,7 @@ int cam_hfi_init(uint8_t event_driven_mode, struct hfi_mem_info *hfi_mem,
 	struct hfi_qtbl *qtbl;
 	struct hfi_qtbl_hdr *qtbl_hdr;
 	struct hfi_q_hdr *cmd_q_hdr, *msg_q_hdr, *dbg_q_hdr;
-	uint32_t hw_version, fw_version;
-	uint32_t status;
+	uint32_t hw_version, fw_version, status = 0;
 
 	if (!g_hfi) {
 		g_hfi = kzalloc(sizeof(struct hfi_info), GFP_KERNEL);
@@ -318,13 +327,12 @@ int cam_hfi_init(uint8_t event_driven_mode, struct hfi_mem_info *hfi_mem,
 		}
 	}
 
-	pr_debug("g_hfi: %pK\n", (void *)g_hfi);
-	if (g_hfi->hfi_state != INVALID) {
+	HFI_DBG("g_hfi: %pK\n", (void *)g_hfi);
+	if (g_hfi->hfi_state != HFI_DEINIT) {
 		pr_err("hfi_init: invalid state\n");
 		return -EINVAL;
 	}
 
-	g_hfi->hfi_state = FW_LOAD_DONE;
 	memcpy(&g_hfi->map, hfi_mem, sizeof(g_hfi->map));
 
 	if (debug) {
@@ -342,11 +350,6 @@ int cam_hfi_init(uint8_t event_driven_mode, struct hfi_mem_info *hfi_mem,
 			icp_base + HFI_REG_A5_CSR_A5_CONTROL);
 	}
 
-	mutex_init(&g_hfi->cmd_q_lock);
-	mutex_init(&g_hfi->msg_q_lock);
-
-	g_hfi->csr_base = icp_base;
-
 	qtbl = (struct hfi_qtbl *)hfi_mem->qtbl.kva;
 	qtbl_hdr = &qtbl->q_tbl_hdr;
 	qtbl_hdr->qtbl_version = 0xFFFFFFFF;
@@ -474,7 +477,7 @@ int cam_hfi_init(uint8_t event_driven_mode, struct hfi_mem_info *hfi_mem,
 		icp_base + HFI_REG_HOST_ICP_INIT_REQUEST);
 
 	hw_version = cam_io_r(icp_base + HFI_REG_A5_HW_VERSION);
-	pr_debug("hw version : %u[%x]\n", hw_version, hw_version);
+	HFI_DBG("hw version : [%x]\n", hw_version);
 
 	rc = readw_poll_timeout((icp_base + HFI_REG_ICP_HOST_INIT_RESPONSE),
 		status, status != ICP_INIT_RESP_SUCCESS, 15, 200);
@@ -484,14 +487,19 @@ int cam_hfi_init(uint8_t event_driven_mode, struct hfi_mem_info *hfi_mem,
 	}
 
 	fw_version = cam_io_r(icp_base + HFI_REG_FW_VERSION);
-	g_hfi->hfi_state = FW_START_SENT;
-
 	HFI_DBG("fw version : %u[%x]\n", fw_version, fw_version);
+
+	g_hfi->csr_base = icp_base;
+	g_hfi->hfi_state = HFI_READY;
+	g_hfi->cmd_q_state = true;
+	g_hfi->msg_q_state = true;
+	mutex_init(&g_hfi->cmd_q_lock);
+	mutex_init(&g_hfi->msg_q_lock);
 	cam_io_w((uint32_t)INTR_ENABLE, icp_base + HFI_REG_A5_CSR_A2HOSTINTEN);
 
 	return rc;
 regions_fail:
-	kzfree(g_hfi);
+	kfree(g_hfi);
 alloc_fail:
 	return rc;
 }
@@ -499,6 +507,24 @@ int cam_hfi_init(uint8_t event_driven_mode, struct hfi_mem_info *hfi_mem,
 
 void cam_hfi_deinit(void)
 {
+	if (!g_hfi) {
+		pr_err("hfi path not established yet\n");
+		return;
+	}
+	cam_io_w((uint32_t)INTR_DISABLE,
+		g_hfi->csr_base + HFI_REG_A5_CSR_A2HOSTINTEN);
+
+	mutex_lock(&g_hfi->cmd_q_lock);
+	g_hfi->cmd_q_state = false;
+	mutex_unlock(&g_hfi->cmd_q_lock);
+
+	mutex_lock(&g_hfi->msg_q_lock);
+	g_hfi->msg_q_state = false;
+	mutex_unlock(&g_hfi->msg_q_lock);
+
+	mutex_destroy(&g_hfi->cmd_q_lock);
+	mutex_destroy(&g_hfi->msg_q_lock);
+
 	kfree(g_hfi);
 	g_hfi = NULL;
 }
diff --git a/drivers/media/platform/msm/camera/icp/icp_hw/a5_hw/a5_core.c b/drivers/media/platform/msm/camera/icp/icp_hw/a5_hw/a5_core.c
index f562bb9..39eacd8 100644
--- a/drivers/media/platform/msm/camera/icp/icp_hw/a5_hw/a5_core.c
+++ b/drivers/media/platform/msm/camera/icp/icp_hw/a5_hw/a5_core.c
@@ -278,19 +278,22 @@ int cam_a5_init_hw(void *device_priv,
 
 	rc = cam_cpas_start(core_info->cpas_handle,
 		&cpas_vote.ahb_vote, &cpas_vote.axi_vote);
-	if (rc < 0) {
+	if (rc) {
 		pr_err("cpass start failed: %d\n", rc);
 		return rc;
 	}
+	core_info->cpas_start = true;
 
 	rc = cam_a5_enable_soc_resources(soc_info);
-	if (rc < 0) {
-		pr_err("soc enable is failed\n");
-		rc = cam_cpas_stop(core_info->cpas_handle);
-		return rc;
+	if (rc) {
+		pr_err("soc enable is failed: %d\n", rc);
+		if (cam_cpas_stop(core_info->cpas_handle))
+			pr_err("cpas stop is failed\n");
+		else
+			core_info->cpas_start = false;
 	}
 
-	return 0;
+	return rc;
 }
 
 int cam_a5_deinit_hw(void *device_priv,
@@ -314,14 +317,17 @@ int cam_a5_deinit_hw(void *device_priv,
 	}
 
 	rc = cam_a5_disable_soc_resources(soc_info);
-	if (rc < 0)
-		pr_err("soc enable is failed\n");
+	if (rc)
+		pr_err("soc disable is failed: %d\n", rc);
 
-	rc = cam_cpas_stop(core_info->cpas_handle);
-	if (rc < 0)
-		pr_err("cpas stop is failed: %d\n", rc);
+	if (core_info->cpas_start) {
+		if (cam_cpas_stop(core_info->cpas_handle))
+			pr_err("cpas stop is failed\n");
+		else
+			core_info->cpas_start = false;
+	}
 
-	return 0;
+	return rc;
 }
 
 irqreturn_t cam_a5_irq(int irq_num, void *data)
@@ -443,13 +449,20 @@ int cam_a5_process_cmd(void *device_priv, uint32_t cmd_type,
 			return -EINVAL;
 		}
 
-		rc = cam_cpas_start(core_info->cpas_handle,
-				&cpas_vote->ahb_vote, &cpas_vote->axi_vote);
+		if (!core_info->cpas_start) {
+			rc = cam_cpas_start(core_info->cpas_handle,
+					&cpas_vote->ahb_vote,
+					&cpas_vote->axi_vote);
+			core_info->cpas_start = true;
+		}
 		break;
 	}
 
 	case CAM_ICP_A5_CMD_CPAS_STOP:
-		cam_cpas_stop(core_info->cpas_handle);
+		if (core_info->cpas_start) {
+			cam_cpas_stop(core_info->cpas_handle);
+			core_info->cpas_start = false;
+		}
 		break;
 	default:
 		break;
diff --git a/drivers/media/platform/msm/camera/icp/icp_hw/a5_hw/a5_core.h b/drivers/media/platform/msm/camera/icp/icp_hw/a5_hw/a5_core.h
index 8b84270..4aa6b4b 100644
--- a/drivers/media/platform/msm/camera/icp/icp_hw/a5_hw/a5_core.h
+++ b/drivers/media/platform/msm/camera/icp/icp_hw/a5_hw/a5_core.h
@@ -62,6 +62,7 @@ struct cam_a5_device_hw_info {
  * @a5_acquire: Acquire information of A5
  * @irq_cb: IRQ callback
  * @cpas_handle: CPAS handle for A5
+ * @cpast_start: state variable for cpas
  */
 struct cam_a5_device_core_info {
 	struct cam_a5_device_hw_info *a5_hw_info;
@@ -74,6 +75,7 @@ struct cam_a5_device_core_info {
 	struct cam_icp_a5_acquire_dev a5_acquire[8];
 	struct cam_icp_a5_set_irq_cb irq_cb;
 	uint32_t cpas_handle;
+	bool cpas_start;
 };
 
 int cam_a5_init_hw(void *device_priv,
diff --git a/drivers/media/platform/msm/camera/icp/icp_hw/a5_hw/a5_soc.c b/drivers/media/platform/msm/camera/icp/icp_hw/a5_hw/a5_soc.c
index 641c154..d12b3b6 100644
--- a/drivers/media/platform/msm/camera/icp/icp_hw/a5_hw/a5_soc.c
+++ b/drivers/media/platform/msm/camera/icp/icp_hw/a5_hw/a5_soc.c
@@ -95,7 +95,7 @@ int cam_a5_disable_soc_resources(struct cam_hw_soc_info *soc_info)
 
 	rc = cam_soc_util_disable_platform_resource(soc_info, true, true);
 	if (rc)
-		pr_err("%s: enable platform failed\n", __func__);
+		pr_err("%s: disable platform failed\n", __func__);
 
 	return rc;
 }
diff --git a/drivers/media/platform/msm/camera/icp/icp_hw/bps_hw/bps_core.c b/drivers/media/platform/msm/camera/icp/icp_hw/bps_hw/bps_core.c
index 50863a5..91652d7 100644
--- a/drivers/media/platform/msm/camera/icp/icp_hw/bps_hw/bps_core.c
+++ b/drivers/media/platform/msm/camera/icp/icp_hw/bps_hw/bps_core.c
@@ -80,15 +80,19 @@ int cam_bps_init_hw(void *device_priv,
 
 	rc = cam_cpas_start(core_info->cpas_handle,
 			&cpas_vote.ahb_vote, &cpas_vote.axi_vote);
-	if (rc < 0) {
+	if (rc) {
 		pr_err("cpass start failed: %d\n", rc);
 		return rc;
 	}
+	core_info->cpas_start = true;
 
 	rc = cam_bps_enable_soc_resources(soc_info);
-	if (rc < 0) {
-		pr_err("soc enable is failed\n");
-		rc = cam_cpas_stop(core_info->cpas_handle);
+	if (rc) {
+		pr_err("soc enable is failed: %d\n", rc);
+		if (cam_cpas_stop(core_info->cpas_handle))
+			pr_err("cpas stop is failed\n");
+		else
+			core_info->cpas_start = false;
 	}
 
 	return rc;
@@ -115,12 +119,15 @@ int cam_bps_deinit_hw(void *device_priv,
 	}
 
 	rc = cam_bps_disable_soc_resources(soc_info);
-	if (rc < 0)
-		pr_err("soc enable is failed\n");
+	if (rc)
+		pr_err("soc disable is failed: %d\n", rc);
 
-	rc = cam_cpas_stop(core_info->cpas_handle);
-	if (rc < 0)
-		pr_err("cpas stop is failed: %d\n", rc);
+	if (core_info->cpas_start) {
+		if (cam_cpas_stop(core_info->cpas_handle))
+			pr_err("cpas stop is failed\n");
+		else
+			core_info->cpas_start = false;
+	}
 
 	return rc;
 }
@@ -169,13 +176,20 @@ int cam_bps_process_cmd(void *device_priv, uint32_t cmd_type,
 			return -EINVAL;
 		}
 
-		rc = cam_cpas_start(core_info->cpas_handle,
-				&cpas_vote->ahb_vote, &cpas_vote->axi_vote);
+		if (!core_info->cpas_start) {
+			rc = cam_cpas_start(core_info->cpas_handle,
+					&cpas_vote->ahb_vote,
+					&cpas_vote->axi_vote);
+			core_info->cpas_start = true;
+		}
 		break;
 	}
 
 	case CAM_ICP_BPS_CMD_CPAS_STOP:
-		cam_cpas_stop(core_info->cpas_handle);
+		if (core_info->cpas_start) {
+			cam_cpas_stop(core_info->cpas_handle);
+			core_info->cpas_start = false;
+		}
 		break;
 	default:
 		break;
diff --git a/drivers/media/platform/msm/camera/icp/icp_hw/bps_hw/bps_core.h b/drivers/media/platform/msm/camera/icp/icp_hw/bps_hw/bps_core.h
index 67e1c03..8a15a7b 100644
--- a/drivers/media/platform/msm/camera/icp/icp_hw/bps_hw/bps_core.h
+++ b/drivers/media/platform/msm/camera/icp/icp_hw/bps_hw/bps_core.h
@@ -26,6 +26,7 @@ struct cam_bps_device_hw_info {
 struct cam_bps_device_core_info {
 	struct cam_bps_device_hw_info *bps_hw_info;
 	uint32_t cpas_handle;
+	bool cpas_start;
 };
 
 int cam_bps_init_hw(void *device_priv,
diff --git a/drivers/media/platform/msm/camera/icp/icp_hw/icp_hw_mgr/cam_icp_hw_mgr.c b/drivers/media/platform/msm/camera/icp/icp_hw/icp_hw_mgr/cam_icp_hw_mgr.c
index 43491a9..677c24e 100644
--- a/drivers/media/platform/msm/camera/icp/icp_hw/icp_hw_mgr/cam_icp_hw_mgr.c
+++ b/drivers/media/platform/msm/camera/icp/icp_hw/icp_hw_mgr/cam_icp_hw_mgr.c
@@ -74,166 +74,6 @@ static int cam_icp_hw_mgr_create_debugfs_entry(void)
 	return 0;
 }
 
-static int cam_icp_stop_cpas(struct cam_icp_hw_mgr *hw_mgr_priv)
-{
-	struct cam_hw_intf *a5_dev_intf = NULL;
-	struct cam_hw_intf *ipe0_dev_intf = NULL;
-	struct cam_hw_intf *ipe1_dev_intf = NULL;
-	struct cam_hw_intf *bps_dev_intf = NULL;
-	struct cam_icp_hw_mgr *hw_mgr = hw_mgr_priv;
-	struct cam_icp_cpas_vote cpas_vote;
-	int rc = 0;
-
-	if (!hw_mgr) {
-		pr_err("Invalid params\n");
-		return -EINVAL;
-	}
-
-	a5_dev_intf = hw_mgr->devices[CAM_ICP_DEV_A5][0];
-	bps_dev_intf = hw_mgr->devices[CAM_ICP_DEV_BPS][0];
-	ipe0_dev_intf = hw_mgr->devices[CAM_ICP_DEV_IPE][0];
-
-	if ((!a5_dev_intf) || (!bps_dev_intf) || (!ipe0_dev_intf)) {
-		pr_err("dev intfs are NULL\n");
-		return -EINVAL;
-	}
-
-	rc = a5_dev_intf->hw_ops.process_cmd(
-		a5_dev_intf->hw_priv,
-		CAM_ICP_A5_CMD_CPAS_STOP,
-		&cpas_vote,
-		sizeof(struct cam_icp_cpas_vote));
-	if (rc < 0)
-		pr_err("CAM_ICP_A5_CMD_CPAS_STOP is failed: %d\n", rc);
-
-	rc = bps_dev_intf->hw_ops.process_cmd(
-		bps_dev_intf->hw_priv,
-		CAM_ICP_BPS_CMD_CPAS_STOP,
-		&cpas_vote,
-		sizeof(struct cam_icp_cpas_vote));
-	if (rc < 0)
-		pr_err("CAM_ICP_BPS_CMD_CPAS_STOP is failed: %d\n", rc);
-
-	rc = ipe0_dev_intf->hw_ops.process_cmd(
-		ipe0_dev_intf->hw_priv,
-		CAM_ICP_IPE_CMD_CPAS_STOP,
-		&cpas_vote,
-		sizeof(struct cam_icp_cpas_vote));
-	if (rc < 0)
-		pr_err("CAM_ICP_IPE_CMD_CPAS_STOP is failed: %d\n", rc);
-
-	ipe1_dev_intf = hw_mgr->devices[CAM_ICP_DEV_IPE][1];
-	if (!ipe1_dev_intf)
-		return rc;
-
-	rc = ipe1_dev_intf->hw_ops.process_cmd(
-		ipe1_dev_intf->hw_priv,
-		CAM_ICP_IPE_CMD_CPAS_STOP,
-		&cpas_vote,
-		sizeof(struct cam_icp_cpas_vote));
-	if (rc < 0)
-		pr_err("CAM_ICP_IPE_CMD_CPAS_STOP is failed: %d\n", rc);
-
-	return rc;
-}
-
-static int cam_icp_start_cpas(struct cam_icp_hw_mgr *hw_mgr_priv)
-{
-	struct cam_hw_intf *a5_dev_intf = NULL;
-	struct cam_hw_intf *ipe0_dev_intf = NULL;
-	struct cam_hw_intf *ipe1_dev_intf = NULL;
-	struct cam_hw_intf *bps_dev_intf = NULL;
-	struct cam_icp_hw_mgr *hw_mgr = hw_mgr_priv;
-	struct cam_icp_cpas_vote cpas_vote;
-	int rc = 0;
-
-	if (!hw_mgr) {
-		pr_err("Invalid params\n");
-		return -EINVAL;
-	}
-
-	a5_dev_intf = hw_mgr->devices[CAM_ICP_DEV_A5][0];
-	bps_dev_intf = hw_mgr->devices[CAM_ICP_DEV_BPS][0];
-	ipe0_dev_intf = hw_mgr->devices[CAM_ICP_DEV_IPE][0];
-
-	if ((!a5_dev_intf) || (!bps_dev_intf) || (!ipe0_dev_intf)) {
-		pr_err("dev intfs are null\n");
-		return -EINVAL;
-	}
-
-	cpas_vote.ahb_vote.type = CAM_VOTE_ABSOLUTE;
-	cpas_vote.ahb_vote.vote.level = CAM_TURBO_VOTE;
-	cpas_vote.axi_vote.compressed_bw = 640000000;
-	cpas_vote.axi_vote.uncompressed_bw = 640000000;
-
-	rc = a5_dev_intf->hw_ops.process_cmd(
-		a5_dev_intf->hw_priv,
-		CAM_ICP_A5_CMD_CPAS_START,
-		&cpas_vote,
-		sizeof(struct cam_icp_cpas_vote));
-	if (rc) {
-		pr_err("CAM_ICP_A5_CMD_CPAS_START is failed: %d\n", rc);
-		goto a5_cpas_start_failed;
-	}
-
-	rc = bps_dev_intf->hw_ops.process_cmd(
-		bps_dev_intf->hw_priv,
-		CAM_ICP_BPS_CMD_CPAS_START,
-		&cpas_vote,
-		sizeof(struct cam_icp_cpas_vote));
-	if (rc < 0) {
-		pr_err("CAM_ICP_BPS_CMD_CPAS_START is failed: %d\n", rc);
-		goto bps_cpas_start_failed;
-	}
-
-	rc = ipe0_dev_intf->hw_ops.process_cmd(
-		ipe0_dev_intf->hw_priv,
-		CAM_ICP_IPE_CMD_CPAS_START,
-		&cpas_vote,
-		sizeof(struct cam_icp_cpas_vote));
-	if (rc < 0) {
-		pr_err("CAM_ICP_IPE_CMD_CPAS_START is failed: %d\n", rc);
-		goto ipe0_cpas_start_failed;
-	}
-
-	ipe1_dev_intf = hw_mgr->devices[CAM_ICP_DEV_IPE][1];
-	if (!ipe1_dev_intf)
-		return rc;
-
-	rc = ipe1_dev_intf->hw_ops.process_cmd(
-		ipe1_dev_intf->hw_priv,
-		CAM_ICP_IPE_CMD_CPAS_START,
-		&cpas_vote,
-		sizeof(struct cam_icp_cpas_vote));
-	if (rc < 0) {
-		pr_err("CAM_ICP_IPE_CMD_CPAS_START is failed: %d\n", rc);
-		goto ipe1_cpas_start_failed;
-	}
-
-	return rc;
-
-ipe1_cpas_start_failed:
-	rc = ipe0_dev_intf->hw_ops.process_cmd(
-		ipe0_dev_intf->hw_priv,
-		CAM_ICP_IPE_CMD_CPAS_STOP,
-		&cpas_vote,
-		sizeof(struct cam_icp_cpas_vote));
-ipe0_cpas_start_failed:
-	rc = bps_dev_intf->hw_ops.process_cmd(
-		bps_dev_intf->hw_priv,
-		CAM_ICP_BPS_CMD_CPAS_STOP,
-		&cpas_vote,
-		sizeof(struct cam_icp_cpas_vote));
-bps_cpas_start_failed:
-	rc = a5_dev_intf->hw_ops.process_cmd(
-		a5_dev_intf->hw_priv,
-		CAM_ICP_A5_CMD_CPAS_STOP,
-		&cpas_vote,
-		sizeof(struct cam_icp_cpas_vote));
-a5_cpas_start_failed:
-	return rc;
-}
-
 static int cam_icp_mgr_process_cmd(void *priv, void *data)
 {
 	int rc;
@@ -566,7 +406,7 @@ int32_t cam_icp_hw_mgr_cb(uint32_t irq_status, void *data)
 	return rc;
 }
 
-static int cam_icp_free_hfi_mem(void)
+static void cam_icp_free_hfi_mem(void)
 {
 	cam_smmu_dealloc_firmware(icp_hw_mgr.iommu_hdl);
 	cam_mem_mgr_release_mem(&icp_hw_mgr.hfi_mem.qtbl);
@@ -574,8 +414,6 @@ static int cam_icp_free_hfi_mem(void)
 	cam_mem_mgr_release_mem(&icp_hw_mgr.hfi_mem.msg_q);
 	cam_mem_mgr_release_mem(&icp_hw_mgr.hfi_mem.dbg_q);
 	cam_mem_mgr_release_mem(&icp_hw_mgr.hfi_mem.sec_heap);
-
-	return 0;
 }
 
 static int cam_icp_allocate_hfi_mem(void)
@@ -806,18 +644,17 @@ static int cam_icp_mgr_release_ctx(struct cam_icp_hw_mgr *hw_mgr, int ctx_id)
 
 	mutex_lock(&hw_mgr->ctx_data[ctx_id].ctx_mutex);
 	if (!hw_mgr->ctx_data[ctx_id].in_use) {
-		pr_err("ctx is already in use: %d\n", ctx_id);
+		ICP_DBG("ctx is not in use: %d\n", ctx_id);
 		mutex_unlock(&hw_mgr->ctx_data[ctx_id].ctx_mutex);
-		return -EINVAL;
+		return 0;
 	}
 	mutex_unlock(&hw_mgr->ctx_data[ctx_id].ctx_mutex);
 
-	mutex_lock(&hw_mgr->hw_mgr_mutex);
 	task = cam_req_mgr_workq_get_task(icp_hw_mgr.cmd_work);
-	mutex_unlock(&hw_mgr->hw_mgr_mutex);
 	if (task)
 		cam_icp_mgr_destroy_handle(&hw_mgr->ctx_data[ctx_id], task);
 
+	mutex_lock(&hw_mgr->hw_mgr_mutex);
 	mutex_lock(&hw_mgr->ctx_data[ctx_id].ctx_mutex);
 	hw_mgr->ctx_data[ctx_id].in_use = 0;
 	hw_mgr->ctx_data[ctx_id].fw_handle = 0;
@@ -829,6 +666,7 @@ static int cam_icp_mgr_release_ctx(struct cam_icp_hw_mgr *hw_mgr, int ctx_id)
 	mutex_destroy(&hw_mgr->ctx_data[ctx_id].hfi_frame_process.lock);
 	mutex_unlock(&hw_mgr->ctx_data[ctx_id].ctx_mutex);
 	kfree(hw_mgr->ctx_data[ctx_id].hfi_frame_process.bitmap);
+	mutex_unlock(&hw_mgr->hw_mgr_mutex);
 
 	return 0;
 }
@@ -861,40 +699,64 @@ static int cam_icp_mgr_hw_close(void *hw_priv, void *hw_close_args)
 	struct cam_hw_intf *ipe0_dev_intf = NULL;
 	struct cam_hw_intf *ipe1_dev_intf = NULL;
 	struct cam_hw_intf *bps_dev_intf = NULL;
-	int rc = 0;
+	struct cam_icp_a5_set_irq_cb irq_cb;
+	struct cam_icp_a5_set_fw_buf_info fw_buf_info;
+	struct cam_icp_hw_ctx_data *ctx_data = NULL;
+	int i;
+
+	mutex_lock(&hw_mgr->hw_mgr_mutex);
+	if (hw_mgr->fw_download ==  false) {
+		ICP_DBG("hw mgr is already closed\n");
+		mutex_unlock(&hw_mgr->hw_mgr_mutex);
+		return 0;
+	}
 
 	a5_dev_intf = hw_mgr->devices[CAM_ICP_DEV_A5][0];
 	ipe0_dev_intf = hw_mgr->devices[CAM_ICP_DEV_IPE][0];
+	ipe1_dev_intf = hw_mgr->devices[CAM_ICP_DEV_IPE][1];
 	bps_dev_intf = hw_mgr->devices[CAM_ICP_DEV_BPS][0];
 
 	if ((!a5_dev_intf) || (!ipe0_dev_intf) || (!bps_dev_intf)) {
-		pr_err("dev intfs are wrong\n");
-		return rc;
+		pr_err("dev intfs are wrong, failed to close\n");
+		mutex_unlock(&hw_mgr->hw_mgr_mutex);
+		return -EINVAL;
 	}
+
+	irq_cb.icp_hw_mgr_cb = NULL;
+	irq_cb.data = NULL;
+	a5_dev_intf->hw_ops.process_cmd(
+		a5_dev_intf->hw_priv,
+		CAM_ICP_A5_SET_IRQ_CB,
+		&irq_cb, sizeof(irq_cb));
+
+	fw_buf_info.kva = 0;
+	fw_buf_info.iova = 0;
+	fw_buf_info.len = 0;
+	a5_dev_intf->hw_ops.process_cmd(
+		a5_dev_intf->hw_priv,
+		CAM_ICP_A5_CMD_SET_FW_BUF,
+		&fw_buf_info,
+		sizeof(fw_buf_info));
+
+	mutex_unlock(&hw_mgr->hw_mgr_mutex);
+
+	for (i = 0; i < CAM_ICP_CTX_MAX; i++) {
+		ctx_data = &hw_mgr->ctx_data[i];
+		cam_icp_mgr_release_ctx(hw_mgr, i);
+	}
+
 	mutex_lock(&hw_mgr->hw_mgr_mutex);
-	rc = a5_dev_intf->hw_ops.deinit(a5_dev_intf->hw_priv, NULL, 0);
-	if (rc < 0)
-		pr_err("a5 dev de-init failed\n");
-
-	rc = bps_dev_intf->hw_ops.deinit(bps_dev_intf->hw_priv, NULL, 0);
-	if (rc < 0)
-		pr_err("bps dev de-init failed\n");
-
-	rc = ipe0_dev_intf->hw_ops.deinit(ipe0_dev_intf->hw_priv, NULL, 0);
-	if (rc < 0)
-		pr_err("ipe0 dev de-init failed\n");
-
 	ipe1_dev_intf = hw_mgr->devices[CAM_ICP_DEV_IPE][1];
-	if (ipe1_dev_intf) {
-		rc = ipe1_dev_intf->hw_ops.deinit(ipe1_dev_intf->hw_priv,
-						NULL, 0);
-		if (rc < 0)
-			pr_err("ipe1 dev de-init failed\n");
-	}
+	if (ipe1_dev_intf)
+		ipe1_dev_intf->hw_ops.deinit(ipe1_dev_intf->hw_priv,
+			NULL, 0);
 
+	ipe0_dev_intf->hw_ops.deinit(ipe0_dev_intf->hw_priv, NULL, 0);
+	bps_dev_intf->hw_ops.deinit(bps_dev_intf->hw_priv, NULL, 0);
+	a5_dev_intf->hw_ops.deinit(a5_dev_intf->hw_priv, NULL, 0);
+	cam_hfi_deinit();
 	cam_icp_free_hfi_mem();
 	hw_mgr->fw_download = false;
-	debugfs_remove_recursive(icp_hw_mgr.dentry);
 	mutex_unlock(&hw_mgr->hw_mgr_mutex);
 
 	return 0;
@@ -975,9 +837,9 @@ static int cam_icp_mgr_download_fw(void *hw_mgr_priv, void *download_fw_args)
 	irq_cb.icp_hw_mgr_cb = cam_icp_hw_mgr_cb;
 	irq_cb.data = hw_mgr_priv;
 	rc = a5_dev_intf->hw_ops.process_cmd(
-				a5_dev_intf->hw_priv,
-				CAM_ICP_A5_SET_IRQ_CB,
-				&irq_cb, sizeof(irq_cb));
+		a5_dev_intf->hw_priv,
+		CAM_ICP_A5_SET_IRQ_CB,
+		&irq_cb, sizeof(irq_cb));
 	if (rc < 0) {
 		pr_err("CAM_ICP_A5_SET_IRQ_CB failed\n");
 		rc = -EINVAL;
@@ -989,10 +851,10 @@ static int cam_icp_mgr_download_fw(void *hw_mgr_priv, void *download_fw_args)
 	fw_buf_info.len = icp_hw_mgr.hfi_mem.fw_buf.len;
 
 	rc = a5_dev_intf->hw_ops.process_cmd(
-			a5_dev_intf->hw_priv,
-			CAM_ICP_A5_CMD_SET_FW_BUF,
-			&fw_buf_info,
-			sizeof(fw_buf_info));
+		a5_dev_intf->hw_priv,
+		CAM_ICP_A5_CMD_SET_FW_BUF,
+		&fw_buf_info,
+		sizeof(fw_buf_info));
 	if (rc < 0) {
 		pr_err("CAM_ICP_A5_CMD_SET_FW_BUF failed\n");
 		goto set_irq_failed;
@@ -1001,9 +863,9 @@ static int cam_icp_mgr_download_fw(void *hw_mgr_priv, void *download_fw_args)
 	cam_hfi_enable_cpu(a5_dev->soc_info.reg_map[A5_SIERRA_BASE].mem_base);
 
 	rc = a5_dev_intf->hw_ops.process_cmd(
-			a5_dev_intf->hw_priv,
-			CAM_ICP_A5_CMD_FW_DOWNLOAD,
-			NULL, 0);
+		a5_dev_intf->hw_priv,
+		CAM_ICP_A5_CMD_FW_DOWNLOAD,
+		NULL, 0);
 	if (rc < 0) {
 		pr_err("FW download is failed\n");
 		goto set_irq_failed;
@@ -1083,14 +945,8 @@ static int cam_icp_mgr_download_fw(void *hw_mgr_priv, void *download_fw_args)
 	}
 
 	hw_mgr->fw_download = true;
-
-	rc = cam_icp_stop_cpas(hw_mgr);
-	if (rc) {
-		pr_err("cpas stop failed\n");
-		goto set_irq_failed;
-	}
-
 	hw_mgr->ctxt_cnt = 0;
+	ICP_DBG("FW download done successfully\n");
 
 	return rc;
 
@@ -1443,20 +1299,11 @@ static int cam_icp_mgr_release_hw(void *hw_mgr_priv, void *release_hw_args)
 		mutex_unlock(&hw_mgr->hw_mgr_mutex);
 		return -EINVAL;
 	}
+	mutex_unlock(&hw_mgr->hw_mgr_mutex);
 
 	rc = cam_icp_mgr_release_ctx(hw_mgr, ctx_id);
-	if (rc) {
-		mutex_unlock(&hw_mgr->hw_mgr_mutex);
+	if (rc)
 		return -EINVAL;
-	}
-
-	--hw_mgr->ctxt_cnt;
-	if (!hw_mgr->ctxt_cnt) {
-		ICP_DBG("stop cpas for last context\n");
-		cam_icp_stop_cpas(hw_mgr);
-	}
-	ICP_DBG("context count : %u\n", hw_mgr->ctxt_cnt);
-	mutex_unlock(&hw_mgr->hw_mgr_mutex);
 
 	ICP_DBG("fw handle %d\n", fw_handle);
 	return rc;
@@ -1662,13 +1509,6 @@ static int cam_icp_mgr_acquire_hw(void *hw_mgr_priv, void *acquire_hw_args)
 
 	/* Fill ctx with acquire info */
 	ctx_data = &hw_mgr->ctx_data[ctx_id];
-
-	if (!hw_mgr->ctxt_cnt++) {
-		ICP_DBG("starting cpas\n");
-		cam_icp_start_cpas(hw_mgr);
-	}
-	ICP_DBG("context count : %u\n", hw_mgr->ctxt_cnt);
-
 	mutex_unlock(&hw_mgr->hw_mgr_mutex);
 
 	/* Fill ctx with acquire info */
@@ -1782,9 +1622,6 @@ static int cam_icp_mgr_acquire_hw(void *hw_mgr_priv, void *acquire_hw_args)
 create_handle_failed:
 get_create_task_failed:
 cmd_cpu_buf_failed:
-	--hw_mgr->ctxt_cnt;
-	if (!hw_mgr->ctxt_cnt)
-		cam_icp_stop_cpas(hw_mgr);
 	cam_icp_mgr_release_ctx(hw_mgr, ctx_id);
 	kfree(tmp_acquire);
 	return rc;
diff --git a/drivers/media/platform/msm/camera/icp/icp_hw/ipe_hw/ipe_core.c b/drivers/media/platform/msm/camera/icp/icp_hw/ipe_hw/ipe_core.c
index 15cb943..07f63d2 100644
--- a/drivers/media/platform/msm/camera/icp/icp_hw/ipe_hw/ipe_core.c
+++ b/drivers/media/platform/msm/camera/icp/icp_hw/ipe_hw/ipe_core.c
@@ -43,7 +43,7 @@ static int cam_ipe_caps_vote(struct cam_ipe_device_core_info *core_info,
 		rc = cam_cpas_update_axi_vote(core_info->cpas_handle,
 			&cpas_vote->axi_vote);
 
-	if (rc < 0)
+	if (rc)
 		pr_err("cpas vote is failed: %d\n", rc);
 
 	return rc;
@@ -78,15 +78,19 @@ int cam_ipe_init_hw(void *device_priv,
 
 	rc = cam_cpas_start(core_info->cpas_handle,
 		&cpas_vote.ahb_vote, &cpas_vote.axi_vote);
-	if (rc < 0) {
+	if (rc) {
 		pr_err("cpass start failed: %d\n", rc);
 		return rc;
 	}
+	core_info->cpas_start = true;
 
 	rc = cam_ipe_enable_soc_resources(soc_info);
-	if (rc < 0) {
-		pr_err("soc enable is failed\n");
-		rc = cam_cpas_stop(core_info->cpas_handle);
+	if (rc) {
+		pr_err("soc enable is failed : %d\n", rc);
+		if (cam_cpas_stop(core_info->cpas_handle))
+			pr_err("cpas stop is failed\n");
+		else
+			core_info->cpas_start = false;
 	}
 
 	return rc;
@@ -113,12 +117,15 @@ int cam_ipe_deinit_hw(void *device_priv,
 	}
 
 	rc = cam_ipe_disable_soc_resources(soc_info);
-	if (rc < 0)
-		pr_err("soc enable is failed\n");
+	if (rc)
+		pr_err("soc disable is failed : %d\n", rc);
 
-	rc = cam_cpas_stop(core_info->cpas_handle);
-	if (rc < 0)
-		pr_err("cpas stop is failed: %d\n", rc);
+	if (core_info->cpas_start) {
+		if (cam_cpas_stop(core_info->cpas_handle))
+			pr_err("cpas stop is failed\n");
+		else
+			core_info->cpas_start = false;
+	}
 
 	return rc;
 }
@@ -163,13 +170,19 @@ int cam_ipe_process_cmd(void *device_priv, uint32_t cmd_type,
 		if (!cmd_args)
 			return -EINVAL;
 
-		rc = cam_cpas_start(core_info->cpas_handle,
-			&cpas_vote->ahb_vote, &cpas_vote->axi_vote);
+		if (!core_info->cpas_start) {
+			rc = cam_cpas_start(core_info->cpas_handle,
+				&cpas_vote->ahb_vote, &cpas_vote->axi_vote);
+			core_info->cpas_start = true;
+		}
 		break;
 	}
 
 	case CAM_ICP_IPE_CMD_CPAS_STOP:
-		cam_cpas_stop(core_info->cpas_handle);
+		if (core_info->cpas_start) {
+			cam_cpas_stop(core_info->cpas_handle);
+			core_info->cpas_start = false;
+		}
 		break;
 	default:
 		break;
diff --git a/drivers/media/platform/msm/camera/icp/icp_hw/ipe_hw/ipe_core.h b/drivers/media/platform/msm/camera/icp/icp_hw/ipe_hw/ipe_core.h
index 4818846..8f0e882 100644
--- a/drivers/media/platform/msm/camera/icp/icp_hw/ipe_hw/ipe_core.h
+++ b/drivers/media/platform/msm/camera/icp/icp_hw/ipe_hw/ipe_core.h
@@ -26,6 +26,7 @@ struct cam_ipe_device_hw_info {
 struct cam_ipe_device_core_info {
 	struct cam_ipe_device_hw_info *ipe_hw_info;
 	uint32_t cpas_handle;
+	bool cpas_start;
 };
 
 int cam_ipe_init_hw(void *device_priv,
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_core.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_core.c
index 44a29aa..a850bc0 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_core.c
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_core.c
@@ -1021,6 +1021,7 @@ static int sde_rotator_init_queue(struct sde_rot_mgr *mgr)
 {
 	int i, size, ret = 0;
 	char name[32];
+	struct sched_param param = { .sched_priority = 5 };
 
 	size = sizeof(struct sde_rot_queue) * mgr->queue_count;
 	mgr->commitq = devm_kzalloc(mgr->device, size, GFP_KERNEL);
@@ -1031,11 +1032,21 @@ static int sde_rotator_init_queue(struct sde_rot_mgr *mgr)
 		snprintf(name, sizeof(name), "rot_commitq_%d_%d",
 				mgr->device->id, i);
 		SDEROT_DBG("work queue name=%s\n", name);
-		mgr->commitq[i].rot_work_queue =
-			alloc_ordered_workqueue("%s",
-				WQ_MEM_RECLAIM | WQ_HIGHPRI, name);
-		if (!mgr->commitq[i].rot_work_queue) {
+		kthread_init_worker(&mgr->commitq[i].rot_kw);
+		mgr->commitq[i].rot_thread = kthread_run(kthread_worker_fn,
+				&mgr->commitq[i].rot_kw, name);
+		if (IS_ERR(mgr->commitq[i].rot_thread)) {
 			ret = -EPERM;
+			mgr->commitq[i].rot_thread = NULL;
+			break;
+		}
+
+		ret = sched_setscheduler(mgr->commitq[i].rot_thread,
+			SCHED_FIFO, &param);
+		if (ret) {
+			SDEROT_ERR(
+				"failed to set kthread priority for commitq %d\n",
+				ret);
 			break;
 		}
 
@@ -1052,10 +1063,21 @@ static int sde_rotator_init_queue(struct sde_rot_mgr *mgr)
 		snprintf(name, sizeof(name), "rot_doneq_%d_%d",
 				mgr->device->id, i);
 		SDEROT_DBG("work queue name=%s\n", name);
-		mgr->doneq[i].rot_work_queue = alloc_ordered_workqueue("%s",
-				WQ_MEM_RECLAIM | WQ_HIGHPRI, name);
-		if (!mgr->doneq[i].rot_work_queue) {
+		kthread_init_worker(&mgr->doneq[i].rot_kw);
+		mgr->doneq[i].rot_thread = kthread_run(kthread_worker_fn,
+				&mgr->doneq[i].rot_kw, name);
+		if (IS_ERR(mgr->doneq[i].rot_thread)) {
 			ret = -EPERM;
+			mgr->doneq[i].rot_thread = NULL;
+			break;
+		}
+
+		ret = sched_setscheduler(mgr->doneq[i].rot_thread,
+			SCHED_FIFO, &param);
+		if (ret) {
+			SDEROT_ERR(
+				"failed to set kthread priority for doneq %d\n",
+				ret);
 			break;
 		}
 
@@ -1071,18 +1093,20 @@ static void sde_rotator_deinit_queue(struct sde_rot_mgr *mgr)
 
 	if (mgr->commitq) {
 		for (i = 0; i < mgr->queue_count; i++) {
-			if (mgr->commitq[i].rot_work_queue)
-				destroy_workqueue(
-					mgr->commitq[i].rot_work_queue);
+			if (mgr->commitq[i].rot_thread) {
+				kthread_flush_worker(&mgr->commitq[i].rot_kw);
+				kthread_stop(mgr->commitq[i].rot_thread);
+			}
 		}
 		devm_kfree(mgr->device, mgr->commitq);
 		mgr->commitq = NULL;
 	}
 	if (mgr->doneq) {
 		for (i = 0; i < mgr->queue_count; i++) {
-			if (mgr->doneq[i].rot_work_queue)
-				destroy_workqueue(
-					mgr->doneq[i].rot_work_queue);
+			if (mgr->doneq[i].rot_thread) {
+				kthread_flush_worker(&mgr->doneq[i].rot_kw);
+				kthread_stop(mgr->doneq[i].rot_thread);
+			}
 		}
 		devm_kfree(mgr->device, mgr->doneq);
 		mgr->doneq = NULL;
@@ -1203,7 +1227,7 @@ void sde_rotator_queue_request(struct sde_rot_mgr *mgr,
 
 		if (entry->item.ts)
 			entry->item.ts[SDE_ROTATOR_TS_QUEUE] = ktime_get();
-		queue_work(queue->rot_work_queue, &entry->commit_work);
+		kthread_queue_work(&queue->rot_kw, &entry->commit_work);
 	}
 }
 
@@ -1423,12 +1447,13 @@ static void sde_rotator_release_entry(struct sde_rot_mgr *mgr,
  *
  * Note this asynchronous handler is protected by hal lock.
  */
-static void sde_rotator_commit_handler(struct work_struct *work)
+static void sde_rotator_commit_handler(struct kthread_work *work)
 {
 	struct sde_rot_entry *entry;
 	struct sde_rot_entry_container *request;
 	struct sde_rot_hw_resource *hw;
 	struct sde_rot_mgr *mgr;
+	struct sched_param param = { .sched_priority = 5 };
 	int ret;
 
 	entry = container_of(work, struct sde_rot_entry, commit_work);
@@ -1439,6 +1464,12 @@ static void sde_rotator_commit_handler(struct work_struct *work)
 		return;
 	}
 
+	ret = sched_setscheduler(entry->fenceq->rot_thread, SCHED_FIFO, &param);
+	if (ret) {
+		SDEROT_WARN("Fail to set kthread priority for fenceq: %d\n",
+				ret);
+	}
+
 	mgr = entry->private->mgr;
 
 	SDEROT_EVTLOG(
@@ -1514,7 +1545,7 @@ static void sde_rotator_commit_handler(struct work_struct *work)
 
 	SDEROT_EVTLOG(entry->item.session_id, 1);
 
-	queue_work(entry->doneq->rot_work_queue, &entry->done_work);
+	kthread_queue_work(&entry->doneq->rot_kw, &entry->done_work);
 	sde_rot_mgr_unlock(mgr);
 	return;
 error:
@@ -1526,8 +1557,8 @@ static void sde_rotator_commit_handler(struct work_struct *work)
 	sde_rotator_release_entry(mgr, entry);
 	atomic_dec(&request->pending_count);
 	atomic_inc(&request->failed_count);
-	if (request->retireq && request->retire_work)
-		queue_work(request->retireq, request->retire_work);
+	if (request->retire_kw && request->retire_work)
+		kthread_queue_work(request->retire_kw, request->retire_work);
 	sde_rot_mgr_unlock(mgr);
 }
 
@@ -1541,7 +1572,7 @@ static void sde_rotator_commit_handler(struct work_struct *work)
  *
  * Note this asynchronous handler is protected by hal lock.
  */
-static void sde_rotator_done_handler(struct work_struct *work)
+static void sde_rotator_done_handler(struct kthread_work *work)
 {
 	struct sde_rot_entry *entry;
 	struct sde_rot_entry_container *request;
@@ -1606,8 +1637,8 @@ static void sde_rotator_done_handler(struct work_struct *work)
 	ATRACE_INT("sde_rot_done", 1);
 	sde_rotator_release_entry(mgr, entry);
 	atomic_dec(&request->pending_count);
-	if (request->retireq && request->retire_work)
-		queue_work(request->retireq, request->retire_work);
+	if (request->retire_kw && request->retire_work)
+		kthread_queue_work(request->retire_kw, request->retire_work);
 	if (entry->item.ts)
 		entry->item.ts[SDE_ROTATOR_TS_RETIRE] = ktime_get();
 	sde_rot_mgr_unlock(mgr);
@@ -1966,8 +1997,10 @@ static int sde_rotator_add_request(struct sde_rot_mgr *mgr,
 
 		entry->request = req;
 
-		INIT_WORK(&entry->commit_work, sde_rotator_commit_handler);
-		INIT_WORK(&entry->done_work, sde_rotator_done_handler);
+		kthread_init_work(&entry->commit_work,
+				sde_rotator_commit_handler);
+		kthread_init_work(&entry->done_work,
+				sde_rotator_done_handler);
 		SDEROT_DBG(
 			"Entry added. wbidx=%u, src{%u,%u,%u,%u}f=%x dst{%u,%u,%u,%u}f=%x session_id=%u\n",
 			item->wb_idx,
@@ -2016,8 +2049,8 @@ static void sde_rotator_cancel_request(struct sde_rot_mgr *mgr,
 		sde_rot_mgr_unlock(mgr);
 		for (i = req->count - 1; i >= 0; i--) {
 			entry = req->entries + i;
-			cancel_work_sync(&entry->commit_work);
-			cancel_work_sync(&entry->done_work);
+			kthread_cancel_work_sync(&entry->commit_work);
+			kthread_cancel_work_sync(&entry->done_work);
 		}
 		sde_rot_mgr_lock(mgr);
 		SDEROT_DBG("cancel work done\n");
@@ -2134,7 +2167,7 @@ void sde_rotator_commit_request(struct sde_rot_mgr *mgr,
 
 	sde_rot_mgr_unlock(mgr);
 	for (i = 0; i < req->count; i++)
-		flush_work(&req->entries[i].commit_work);
+		kthread_flush_work(&req->entries[i].commit_work);
 	sde_rot_mgr_lock(mgr);
 }
 
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_core.h b/drivers/media/platform/msm/sde/rotator/sde_rotator_core.h
index 7b8a066..731ff1e 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_core.h
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_core.h
@@ -21,7 +21,7 @@
 #include <linux/types.h>
 #include <linux/cdev.h>
 #include <linux/pm_runtime.h>
-#include <linux/completion.h>
+#include <linux/kthread.h>
 
 #include "sde_rotator_base.h"
 #include "sde_rotator_util.h"
@@ -230,7 +230,8 @@ struct sde_rot_hw_resource {
 };
 
 struct sde_rot_queue {
-	struct workqueue_struct *rot_work_queue;
+	struct kthread_worker rot_kw;
+	struct task_struct *rot_thread;
 	struct sde_rot_timeline *timeline;
 	struct sde_rot_hw_resource *hw;
 };
@@ -253,8 +254,8 @@ struct sde_rot_entry_container {
 	u32 count;
 	atomic_t pending_count;
 	atomic_t failed_count;
-	struct workqueue_struct *retireq;
-	struct work_struct *retire_work;
+	struct kthread_worker *retire_kw;
+	struct kthread_work *retire_work;
 	bool finished;
 	struct sde_rot_entry *entries;
 };
@@ -284,8 +285,8 @@ struct sde_rot_file_private;
  */
 struct sde_rot_entry {
 	struct sde_rotation_item item;
-	struct work_struct commit_work;
-	struct work_struct done_work;
+	struct kthread_work commit_work;
+	struct kthread_work done_work;
 	struct sde_rot_queue *commitq;
 	struct sde_rot_queue *fenceq;
 	struct sde_rot_queue *doneq;
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.c
index 2e91d54..f2778b0 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.c
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.c
@@ -54,8 +54,8 @@
 #define SDE_ROTATOR_DEGREE_180		180
 #define SDE_ROTATOR_DEGREE_90		90
 
-static void sde_rotator_submit_handler(struct work_struct *work);
-static void sde_rotator_retire_handler(struct work_struct *work);
+static void sde_rotator_submit_handler(struct kthread_work *work);
+static void sde_rotator_retire_handler(struct kthread_work *work);
 #ifdef CONFIG_COMPAT
 static long sde_rotator_compat_ioctl32(struct file *file,
 	unsigned int cmd, unsigned long arg);
@@ -467,8 +467,8 @@ static void sde_rotator_stop_streaming(struct vb2_queue *q)
 			SDEDEV_DBG(rot_dev->dev, "cancel request s:%d\n",
 					ctx->session_id);
 			mutex_unlock(q->lock);
-			cancel_work_sync(&request->submit_work);
-			cancel_work_sync(&request->retire_work);
+			kthread_cancel_work_sync(&request->submit_work);
+			kthread_cancel_work_sync(&request->retire_work);
 			mutex_lock(q->lock);
 			spin_lock(&ctx->list_lock);
 			list_del_init(&request->list);
@@ -926,9 +926,9 @@ struct sde_rotator_ctx *sde_rotator_ctx_open(
 	for (i = 0 ; i < ARRAY_SIZE(ctx->requests); i++) {
 		struct sde_rotator_request *request = &ctx->requests[i];
 
-		INIT_WORK(&request->submit_work,
+		kthread_init_work(&request->submit_work,
 				sde_rotator_submit_handler);
-		INIT_WORK(&request->retire_work,
+		kthread_init_work(&request->retire_work,
 				sde_rotator_retire_handler);
 		request->ctx = ctx;
 		INIT_LIST_HEAD(&request->list);
@@ -965,14 +965,16 @@ struct sde_rotator_ctx *sde_rotator_ctx_open(
 
 	snprintf(name, sizeof(name), "rot_fenceq_%d_%d", rot_dev->dev->id,
 			ctx->session_id);
-	ctx->work_queue.rot_work_queue = alloc_ordered_workqueue("%s",
-			WQ_MEM_RECLAIM | WQ_HIGHPRI, name);
-	if (!ctx->work_queue.rot_work_queue) {
-		SDEDEV_ERR(ctx->rot_dev->dev, "fail allocate workqueue\n");
+	kthread_init_worker(&ctx->work_queue.rot_kw);
+	ctx->work_queue.rot_thread = kthread_run(kthread_worker_fn,
+			&ctx->work_queue.rot_kw, name);
+	if (IS_ERR(ctx->work_queue.rot_thread)) {
+		SDEDEV_ERR(ctx->rot_dev->dev, "fail allocate kthread\n");
 		ret = -EPERM;
+		ctx->work_queue.rot_thread = NULL;
 		goto error_alloc_workqueue;
 	}
-	SDEDEV_DBG(ctx->rot_dev->dev, "work queue name=%s\n", name);
+	SDEDEV_DBG(ctx->rot_dev->dev, "kthread name=%s\n", name);
 
 	snprintf(name, sizeof(name), "%d_%d", rot_dev->dev->id,
 			ctx->session_id);
@@ -1022,7 +1024,8 @@ struct sde_rotator_ctx *sde_rotator_ctx_open(
 error_open_session:
 	sde_rot_mgr_unlock(rot_dev->mgr);
 	sde_rotator_destroy_timeline(ctx->work_queue.timeline);
-	destroy_workqueue(ctx->work_queue.rot_work_queue);
+	kthread_flush_worker(&ctx->work_queue.rot_kw);
+	kthread_stop(ctx->work_queue.rot_thread);
 error_alloc_workqueue:
 	sysfs_remove_group(&ctx->kobj, &sde_rotator_fs_attr_group);
 error_create_sysfs:
@@ -1072,7 +1075,7 @@ static int sde_rotator_ctx_release(struct sde_rotator_ctx *ctx,
 
 		SDEDEV_DBG(rot_dev->dev, "release submit work s:%d\n",
 				session_id);
-		cancel_work_sync(&request->submit_work);
+		kthread_cancel_work_sync(&request->submit_work);
 	}
 	SDEDEV_DBG(rot_dev->dev, "release session s:%d\n", session_id);
 	sde_rot_mgr_lock(rot_dev->mgr);
@@ -1085,12 +1088,13 @@ static int sde_rotator_ctx_release(struct sde_rotator_ctx *ctx,
 
 		SDEDEV_DBG(rot_dev->dev, "release retire work s:%d\n",
 				session_id);
-		cancel_work_sync(&request->retire_work);
+		kthread_cancel_work_sync(&request->retire_work);
 	}
 	mutex_lock(&rot_dev->lock);
 	SDEDEV_DBG(rot_dev->dev, "release context s:%d\n", session_id);
 	sde_rotator_destroy_timeline(ctx->work_queue.timeline);
-	destroy_workqueue(ctx->work_queue.rot_work_queue);
+	kthread_flush_worker(&ctx->work_queue.rot_kw);
+	kthread_stop(ctx->work_queue.rot_thread);
 	sysfs_remove_group(&ctx->kobj, &sde_rotator_fs_attr_group);
 	kobject_put(&ctx->kobj);
 	if (ctx->file) {
@@ -1609,7 +1613,7 @@ int sde_rotator_inline_commit(void *handle, struct sde_rotator_inline_cmd *cmd,
 		} else {
 			SDEROT_ERR("invalid stats timestamp\n");
 		}
-		req->retireq = ctx->work_queue.rot_work_queue;
+		req->retire_kw = &ctx->work_queue.rot_kw;
 		req->retire_work = &request->retire_work;
 
 		trace_rot_entry_fence(
@@ -2719,7 +2723,7 @@ static const struct v4l2_ioctl_ops sde_rotator_ioctl_ops = {
  *
  * This function is scheduled in work queue context.
  */
-static void sde_rotator_retire_handler(struct work_struct *work)
+static void sde_rotator_retire_handler(struct kthread_work *work)
 {
 	struct vb2_v4l2_buffer *src_buf;
 	struct vb2_v4l2_buffer *dst_buf;
@@ -2909,7 +2913,7 @@ static int sde_rotator_process_buffers(struct sde_rotator_ctx *ctx,
 		goto error_init_request;
 	}
 
-	req->retireq = ctx->work_queue.rot_work_queue;
+	req->retire_kw = &ctx->work_queue.rot_kw;
 	req->retire_work = &request->retire_work;
 
 	ret = sde_rotator_handle_request_common(
@@ -2938,7 +2942,7 @@ static int sde_rotator_process_buffers(struct sde_rotator_ctx *ctx,
  *
  * This function is scheduled in work queue context.
  */
-static void sde_rotator_submit_handler(struct work_struct *work)
+static void sde_rotator_submit_handler(struct kthread_work *work)
 {
 	struct sde_rotator_ctx *ctx;
 	struct sde_rotator_device *rot_dev;
@@ -3203,7 +3207,7 @@ static int sde_rotator_job_ready(void *priv)
 			list_del_init(&request->list);
 			list_add_tail(&request->list, &ctx->pending_list);
 			spin_unlock(&ctx->list_lock);
-			queue_work(ctx->work_queue.rot_work_queue,
+			kthread_queue_work(&ctx->work_queue.rot_kw,
 					&request->submit_work);
 		}
 	} else if (request && !atomic_read(&request->req->pending_count)) {
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.h b/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.h
index 100ce27..627ea86 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.h
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.h
@@ -23,6 +23,7 @@
 #include <linux/msm-bus.h>
 #include <linux/platform_device.h>
 #include <linux/soc/qcom/llcc-qcom.h>
+#include <linux/kthread.h>
 #include <media/v4l2-device.h>
 #include <media/v4l2-fh.h>
 #include <media/v4l2-ctrls.h>
@@ -95,8 +96,8 @@ struct sde_rotator_vbinfo {
  */
 struct sde_rotator_request {
 	struct list_head list;
-	struct work_struct submit_work;
-	struct work_struct retire_work;
+	struct kthread_work submit_work;
+	struct kthread_work retire_work;
 	struct sde_rot_entry_container *req;
 	struct sde_rotator_ctx *ctx;
 	bool committed;
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
index a36bcbb..564b5c9 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -2696,6 +2696,9 @@ static int mmc_partial_init(struct mmc_host *host)
 	if (mmc_card_hs400(card)) {
 		if (card->ext_csd.strobe_support && host->ops->enhanced_strobe)
 			err = host->ops->enhanced_strobe(host);
+		else if (host->ops->execute_tuning)
+			err = host->ops->execute_tuning(host,
+				MMC_SEND_TUNING_BLOCK_HS200);
 	} else if (mmc_card_hs200(card) && host->ops->execute_tuning) {
 		err = host->ops->execute_tuning(host,
 			MMC_SEND_TUNING_BLOCK_HS200);
diff --git a/drivers/platform/msm/qcom-geni-se.c b/drivers/platform/msm/qcom-geni-se.c
index 1fffa7c..7c77280 100644
--- a/drivers/platform/msm/qcom-geni-se.c
+++ b/drivers/platform/msm/qcom-geni-se.c
@@ -31,7 +31,7 @@
 #define GENI_SE_IOMMU_VA_SIZE	(0xC0000000)
 
 #define NUM_LOG_PAGES 2
-
+#define MAX_CLK_PERF_LEVEL 32
 static unsigned long default_bus_bw_set[] = {0, 19200000, 50000000, 100000000};
 
 /**
@@ -52,6 +52,8 @@ static unsigned long default_bus_bw_set[] = {0, 19200000, 50000000, 100000000};
  * @cur_ib:		Current Bus Instantaneous BW request value.
  * @bus_bw_set:		Clock plan for the bus driver.
  * @cur_bus_bw_idx:	Current index within the bus clock plan.
+ * @num_clk_levels:	Number of valid clock levels in clk_perf_tbl.
+ * @clk_perf_tbl:	Table of clock frequency input to Serial Engine clock.
  * @log_ctx:		Logging context to hold the debug information
  */
 struct geni_se_device {
@@ -72,6 +74,8 @@ struct geni_se_device {
 	int bus_bw_set_size;
 	unsigned long *bus_bw_set;
 	int cur_bus_bw_idx;
+	unsigned int num_clk_levels;
+	unsigned long *clk_perf_tbl;
 	void *log_ctx;
 };
 
@@ -809,6 +813,111 @@ int geni_se_resources_init(struct se_geni_rsc *rsc,
 EXPORT_SYMBOL(geni_se_resources_init);
 
 /**
+ * geni_se_clk_tbl_get() - Get the clock table to program DFS
+ * @rsc:	Resource for which the clock table is requested.
+ * @tbl:	Table in which the output is returned.
+ *
+ * This function is called by the protocol drivers to determine the different
+ * clock frequencies supported by Serail Engine Core Clock. The protocol
+ * drivers use the output to determine the clock frequency index to be
+ * programmed into DFS.
+ *
+ * Return:	number of valid performance levels in the table on success,
+ *		standard Linux error codes on failure.
+ */
+int geni_se_clk_tbl_get(struct se_geni_rsc *rsc, unsigned long **tbl)
+{
+	struct geni_se_device *geni_se_dev;
+	int i;
+	unsigned long prev_freq = 0;
+
+	if (unlikely(!rsc || !rsc->wrapper_dev || !rsc->se_clk || !tbl))
+		return -EINVAL;
+
+	*tbl = NULL;
+	geni_se_dev = dev_get_drvdata(rsc->wrapper_dev);
+	if (unlikely(!geni_se_dev))
+		return -EPROBE_DEFER;
+
+	if (geni_se_dev->clk_perf_tbl) {
+		*tbl = geni_se_dev->clk_perf_tbl;
+		return geni_se_dev->num_clk_levels;
+	}
+
+	geni_se_dev->clk_perf_tbl = kzalloc(sizeof(*geni_se_dev->clk_perf_tbl) *
+						MAX_CLK_PERF_LEVEL, GFP_KERNEL);
+	if (!geni_se_dev->clk_perf_tbl)
+		return -ENOMEM;
+
+	for (i = 0; i < MAX_CLK_PERF_LEVEL; i++) {
+		geni_se_dev->clk_perf_tbl[i] = clk_round_rate(rsc->se_clk,
+								prev_freq + 1);
+		if (geni_se_dev->clk_perf_tbl[i] == prev_freq) {
+			geni_se_dev->clk_perf_tbl[i] = 0;
+			break;
+		}
+		prev_freq = geni_se_dev->clk_perf_tbl[i];
+	}
+	geni_se_dev->num_clk_levels = i;
+	*tbl = geni_se_dev->clk_perf_tbl;
+	return geni_se_dev->num_clk_levels;
+}
+EXPORT_SYMBOL(geni_se_clk_tbl_get);
+
+/**
+ * geni_se_clk_freq_match() - Get the matching or closest SE clock frequency
+ * @rsc:	Resource for which the clock frequency is requested.
+ * @req_freq:	Requested clock frequency.
+ * @index:	Index of the resultant frequency in the table.
+ * @res_freq:	Resultant frequency which matches or is closer to the
+ *		requested frequency.
+ * @exact:	Flag to indicate exact multiple requirement of the requested
+ *		frequency .
+ *
+ * This function is called by the protocol drivers to determine the matching
+ * or closest frequency of the Serial Engine clock to be selected in order
+ * to meet the performance requirements.
+ *
+ * Return:	0 on success, standard Linux error codes on failure.
+ */
+int geni_se_clk_freq_match(struct se_geni_rsc *rsc, unsigned long req_freq,
+			   unsigned int *index, unsigned long *res_freq,
+			   bool exact)
+{
+	unsigned long *tbl;
+	int num_clk_levels;
+	int i;
+
+	num_clk_levels = geni_se_clk_tbl_get(rsc, &tbl);
+	if (num_clk_levels < 0)
+		return num_clk_levels;
+
+	if (num_clk_levels == 0)
+		return -EFAULT;
+
+	*res_freq = 0;
+	for (i = 0; i < num_clk_levels; i++) {
+		if (!(tbl[i] % req_freq)) {
+			*index = i;
+			*res_freq = tbl[i];
+			return 0;
+		}
+
+		if (!(*res_freq) || ((tbl[i] > *res_freq) &&
+				     (tbl[i] < req_freq))) {
+			*index = i;
+			*res_freq = tbl[i];
+		}
+	}
+
+	if (exact || !(*res_freq))
+		return -ENOKEY;
+
+	return 0;
+}
+EXPORT_SYMBOL(geni_se_clk_freq_match);
+
+/**
  * geni_se_tx_dma_prep() - Prepare the Serial Engine for TX DMA transfer
  * @wrapper_dev:	QUPv3 Wrapper Device to which the TX buffer is mapped.
  * @base:		Base address of the SE register block.
diff --git a/drivers/spi/spi-geni-qcom.c b/drivers/spi/spi-geni-qcom.c
index 08eb00a..ad3eb187 100644
--- a/drivers/spi/spi-geni-qcom.c
+++ b/drivers/spi/spi-geni-qcom.c
@@ -110,14 +110,6 @@ static struct spi_master *get_spi_master(struct device *dev)
 	return spi;
 }
 
-static int get_sclk(u32 speed_hz, unsigned long *sclk_freq)
-{
-	u32 root_freq[] = { 19200000 };
-
-	*sclk_freq = root_freq[0];
-	return 0;
-}
-
 static int do_spi_clk_cfg(u32 speed_hz, struct spi_geni_master *mas)
 {
 	unsigned long sclk_freq;
@@ -131,14 +123,20 @@ static int do_spi_clk_cfg(u32 speed_hz, struct spi_geni_master *mas)
 	clk_sel &= ~CLK_SEL_MSK;
 	m_clk_cfg &= ~CLK_DIV_MSK;
 
-	idx = get_sclk(speed_hz, &sclk_freq);
-	if (idx < 0)
-		return -EINVAL;
+	ret = geni_se_clk_freq_match(&mas->spi_rsc, speed_hz, &idx,
+					&sclk_freq, true);
+	if (ret) {
+		dev_err(mas->dev, "%s: Failed(%d) to find src clk for 0x%x\n",
+						__func__, ret, speed_hz);
+		return ret;
+	}
 
 	div = ((sclk_freq / SPI_OVERSAMPLING) / speed_hz);
 	if (!div)
 		return -EINVAL;
 
+	dev_dbg(mas->dev, "%s: req %u sclk %lu, idx %d, div %d\n", __func__,
+						speed_hz, sclk_freq, idx, div);
 	clk_sel |= (idx & CLK_SEL_MSK);
 	m_clk_cfg |= ((div << CLK_DIV_SHFT) | SER_CLK_EN);
 	ret = clk_set_rate(rsc->se_clk, sclk_freq);
@@ -362,13 +360,13 @@ static int spi_geni_transfer_one(struct spi_master *spi,
 	reinit_completion(&mas->xfer_done);
 	/* Speed and bits per word can be overridden per transfer */
 	if (xfer->speed_hz != mas->cur_speed_hz) {
+		mas->cur_speed_hz = xfer->speed_hz;
 		ret = do_spi_clk_cfg(mas->cur_speed_hz, mas);
 		if (ret) {
 			dev_err(mas->dev, "%s:Err setting clks:%d\n",
 								__func__, ret);
 			goto geni_transfer_one_exit;
 		}
-		mas->cur_speed_hz = xfer->speed_hz;
 	}
 
 	setup_fifo_xfer(xfer, mas, slv->mode, spi);
diff --git a/include/linux/qcom-geni-se.h b/include/linux/qcom-geni-se.h
index 5947107..6c9ddcd 100644
--- a/include/linux/qcom-geni-se.h
+++ b/include/linux/qcom-geni-se.h
@@ -565,6 +565,41 @@ int geni_se_resources_init(struct se_geni_rsc *rsc,
 			   unsigned long ab, unsigned long ib);
 
 /**
+ * geni_se_clk_tbl_get() - Get the clock table to program DFS
+ * @rsc:	Resource for which the clock table is requested.
+ * @tbl:	Table in which the output is returned.
+ *
+ * This function is called by the protocol drivers to determine the different
+ * clock frequencies supported by Serail Engine Core Clock. The protocol
+ * drivers use the output to determine the clock frequency index to be
+ * programmed into DFS.
+ *
+ * Return:	number of valid performance levels in the table on success,
+ *		standard Linux error codes on failure.
+ */
+int geni_se_clk_tbl_get(struct se_geni_rsc *rsc, unsigned long **tbl);
+
+/**
+ * geni_se_clk_freq_match() - Get the matching or closest SE clock frequency
+ * @rsc:	Resource for which the clock frequency is requested.
+ * @req_freq:	Requested clock frequency.
+ * @index:	Index of the resultant frequency in the table.
+ * @res_freq:	Resultant frequency which matches or is closer to the
+ *		requested frequency.
+ * @exact:	Flag to indicate exact multiple requirement of the requested
+ *		frequency .
+ *
+ * This function is called by the protocol drivers to determine the matching
+ * or closest frequency of the Serial Engine clock to be selected in order
+ * to meet the performance requirements.
+ *
+ * Return:	0 on success, standard Linux error codes on failure.
+ */
+int geni_se_clk_freq_match(struct se_geni_rsc *rsc, unsigned long req_freq,
+			   unsigned int *index, unsigned long *res_freq,
+			   bool exact);
+
+/**
  * geni_se_tx_dma_prep() - Prepare the Serial Engine for TX DMA transfer
  * @wrapper_dev:	QUPv3 Wrapper Device to which the TX buffer is mapped.
  * @base:		Base address of the SE register block.
@@ -796,6 +831,19 @@ static inline int geni_se_resources_init(struct se_geni_rsc *rsc,
 	return -ENXIO;
 }
 
+static inline int geni_se_clk_tbl_get(struct se_geni_rsc *rsc,
+					unsigned long **tbl)
+{
+	return -ENXIO;
+}
+
+static inline int geni_se_clk_freq_match(struct se_geni_rsc *rsc,
+			unsigned long req_freq, unsigned int *index,
+			unsigned long *res_freq, bool exact)
+{
+	return -ENXIO;
+}
+
 static inline int geni_se_tx_dma_prep(struct device *wrapper_dev,
 	void __iomem *base, void *tx_buf, int tx_len, dma_addr_t *tx_dma)
 {
diff --git a/include/linux/sched.h b/include/linux/sched.h
index 8d4813d..9e7ab05 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -1155,8 +1155,9 @@ struct sched_domain_attr {
 extern int sched_domain_level_max;
 
 struct capacity_state {
-	unsigned long cap;	/* compute capacity */
-	unsigned long power;	/* power consumption at this compute capacity */
+	unsigned long cap;	/* capacity - calculated by energy driver */
+	unsigned long frequency;/* frequency */
+	unsigned long power;	/* power consumption at this frequency */
 };
 
 struct idle_state {
diff --git a/include/uapi/media/cam_defs.h b/include/uapi/media/cam_defs.h
index a4557d1..5f0f070 100644
--- a/include/uapi/media/cam_defs.h
+++ b/include/uapi/media/cam_defs.h
@@ -7,14 +7,15 @@
 
 
 /* camera op codes */
-#define CAM_COMMON_OPCODE_BASE                  0
-#define CAM_QUERY_CAP                           1
-#define CAM_ACQUIRE_DEV                         2
-#define CAM_START_DEV                           3
-#define CAM_STOP_DEV                            4
-#define CAM_CONFIG_DEV                          5
-#define CAM_RELEASE_DEV                         6
-#define CAM_COMMON_OPCODE_MAX                   7
+#define CAM_COMMON_OPCODE_BASE                  0x100
+#define CAM_QUERY_CAP                           (CAM_COMMON_OPCODE_BASE + 0x1)
+#define CAM_ACQUIRE_DEV                         (CAM_COMMON_OPCODE_BASE + 0x2)
+#define CAM_START_DEV                           (CAM_COMMON_OPCODE_BASE + 0x3)
+#define CAM_STOP_DEV                            (CAM_COMMON_OPCODE_BASE + 0x4)
+#define CAM_CONFIG_DEV                          (CAM_COMMON_OPCODE_BASE + 0x5)
+#define CAM_RELEASE_DEV                         (CAM_COMMON_OPCODE_BASE + 0x6)
+#define CAM_SD_SHUTDOWN                         (CAM_COMMON_OPCODE_BASE + 0x7)
+#define CAM_COMMON_OPCODE_MAX                   (CAM_COMMON_OPCODE_BASE + 0x8)
 
 /* camera handle type */
 #define CAM_HANDLE_USER_POINTER                 1
diff --git a/kernel/sched/energy.c b/kernel/sched/energy.c
index 30ab6f4..c32defa 100644
--- a/kernel/sched/energy.c
+++ b/kernel/sched/energy.c
@@ -18,14 +18,15 @@
  */
 #define pr_fmt(fmt) "sched-energy: " fmt
 
-#define DEBUG
-
 #include <linux/gfp.h>
 #include <linux/of.h>
 #include <linux/printk.h>
 #include <linux/sched.h>
 #include <linux/sched_energy.h>
 #include <linux/stddef.h>
+#include <linux/cpu.h>
+#include <linux/pm_opp.h>
+#include <linux/platform_device.h>
 
 struct sched_group_energy *sge_array[NR_CPUS][NR_SD_LEVELS];
 
@@ -97,7 +98,8 @@ void init_sched_energy_costs(void)
 			}
 
 			for (i = 0, val = prop->value; i < nstates; i++) {
-				cap_states[i].cap = be32_to_cpup(val++);
+				cap_states[i].cap = SCHED_CAPACITY_SCALE;
+				cap_states[i].frequency = be32_to_cpup(val++);
 				cap_states[i].power = be32_to_cpup(val++);
 			}
 
@@ -138,3 +140,166 @@ void init_sched_energy_costs(void)
 out:
 	free_resources();
 }
+
+static int sched_energy_probe(struct platform_device *pdev)
+{
+	unsigned long max_freq = 0;
+	int max_efficiency = INT_MIN;
+	int cpu;
+	unsigned long *max_frequencies = NULL;
+	int ret;
+
+	if (!sched_is_energy_aware())
+		return 0;
+
+	max_frequencies = kmalloc_array(nr_cpu_ids, sizeof(unsigned long),
+					GFP_KERNEL);
+	if (!max_frequencies) {
+		ret = -ENOMEM;
+		goto exit;
+	}
+
+	/*
+	 * Find system max possible frequency and max frequencies for each
+	 * CPUs.
+	 */
+	for_each_possible_cpu(cpu) {
+		struct device *cpu_dev;
+		struct dev_pm_opp *opp;
+		int efficiency = arch_get_cpu_efficiency(cpu);
+
+		max_efficiency = max(efficiency, max_efficiency);
+
+		cpu_dev = get_cpu_device(cpu);
+		if (IS_ERR_OR_NULL(cpu_dev)) {
+			if (!cpu_dev)
+				ret = -EINVAL;
+			else
+				ret = PTR_ERR(cpu_dev);
+			goto exit;
+		}
+
+		max_frequencies[cpu] = ULONG_MAX;
+
+		rcu_read_lock();
+		opp = dev_pm_opp_find_freq_floor(cpu_dev,
+						 &max_frequencies[cpu]);
+		if (IS_ERR_OR_NULL(opp)) {
+			if (!opp || PTR_ERR(opp) == -ENODEV)
+				ret = -EPROBE_DEFER;
+			else
+				ret = PTR_ERR(opp);
+			goto exit_rcu_unlock;
+		}
+		rcu_read_unlock();
+
+		/* Convert HZ to KHZ */
+		max_frequencies[cpu] /= 1000;
+		max_freq = max(max_freq, max_frequencies[cpu]);
+	}
+
+	/* update capacity in energy model */
+	for_each_possible_cpu(cpu) {
+		unsigned long cpu_max_cap;
+		struct sched_group_energy *sge_l0, *sge;
+		int efficiency = arch_get_cpu_efficiency(cpu);
+
+		cpu_max_cap = DIV_ROUND_UP(SCHED_CAPACITY_SCALE *
+					   max_frequencies[cpu], max_freq);
+		cpu_max_cap = DIV_ROUND_UP(cpu_max_cap * efficiency,
+					   max_efficiency);
+
+		/*
+		 * All the cap_states have same frequency table so use
+		 * SD_LEVEL0's.
+		 */
+		sge_l0 = sge_array[cpu][SD_LEVEL0];
+		if (sge_l0 && sge_l0->nr_cap_states > 0) {
+			int i;
+			int ncapstates = sge_l0->nr_cap_states;
+
+			for (i = 0; i < ncapstates; i++) {
+				int sd_level;
+				unsigned long freq, cap;
+
+				/*
+				 * Energy model can contain more frequency
+				 * steps than actual for multiple speedbin
+				 * support. Ceil the max capacity with actual
+				 * one.
+				 */
+				freq = min(sge_l0->cap_states[i].frequency,
+					   max_frequencies[cpu]);
+				cap = DIV_ROUND_UP(cpu_max_cap * freq,
+						   max_frequencies[cpu]);
+
+				for_each_possible_sd_level(sd_level) {
+					sge = sge_array[cpu][sd_level];
+					if (!sge)
+						break;
+					sge->cap_states[i].cap = cap;
+				}
+
+				dev_dbg(&pdev->dev,
+					"cpu=%d freq=%ld cap=%ld power_d0=%ld\n",
+					cpu, freq, sge_l0->cap_states[i].cap,
+					sge_l0->cap_states[i].power);
+			}
+
+			dev_info(&pdev->dev,
+				"cpu=%d eff=%d [freq=%ld cap=%ld power_d0=%ld] -> [freq=%ld cap=%ld power_d0=%ld]\n",
+				cpu, efficiency,
+				sge_l0->cap_states[0].frequency,
+				sge_l0->cap_states[0].cap,
+				sge_l0->cap_states[0].power,
+				sge_l0->cap_states[ncapstates - 1].frequency,
+				sge_l0->cap_states[ncapstates - 1].cap,
+				sge_l0->cap_states[ncapstates - 1].power
+				);
+		}
+
+
+		dev_dbg(&pdev->dev,
+			"cpu=%d efficiency=%d max_frequency=%ld max_efficiency=%d cpu_max_capacity=%ld\n",
+			cpu, efficiency, max_frequencies[cpu], max_efficiency,
+			cpu_max_cap);
+
+		arch_update_cpu_capacity(cpu);
+	}
+
+	kfree(max_frequencies);
+
+	dev_info(&pdev->dev, "Sched-energy-costs capacity updated\n");
+	return 0;
+
+exit_rcu_unlock:
+	rcu_read_unlock();
+
+exit:
+	if (ret != -EPROBE_DEFER)
+		dev_err(&pdev->dev, "error=%d\n", ret);
+
+	kfree(max_frequencies);
+	return ret;
+}
+
+static const struct of_device_id of_sched_energy_dt[] = {
+	{
+		.compatible = "sched-energy",
+	},
+	{ }
+};
+
+static struct platform_driver energy_driver = {
+	.driver = {
+		.name = "sched-energy",
+		.of_match_table = of_sched_energy_dt,
+	},
+	.probe = sched_energy_probe,
+};
+
+static int __init sched_energy_init(void)
+{
+	return platform_driver_register(&energy_driver);
+}
+subsys_initcall(sched_energy_init);
diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
index a0b62d4..e4b706d 100755
--- a/kernel/sched/fair.c
+++ b/kernel/sched/fair.c
@@ -5406,28 +5406,12 @@ static inline int task_util(struct task_struct *p)
 	return p->se.avg.util_avg;
 }
 
-#define SCHED_ENABLE_WAKER_WAKEE	0
-
-static unsigned int sched_small_wakee_task_util = 102; /* ~10% of max cap */
-static unsigned int sched_big_waker_task_util = 256;  /* 25% of max cap */
-
-static inline bool
-wake_on_waker_sibling(struct task_struct *p)
-{
-	return SCHED_ENABLE_WAKER_WAKEE &&
-	       task_util(current) > sched_big_waker_task_util &&
-	       task_util(p) < sched_small_wakee_task_util;
-}
-
-#define sysctl_sched_prefer_sync_wakee_to_waker 0
-
 static inline bool
 bias_to_waker_cpu(struct task_struct *p, int cpu)
 {
-	return sysctl_sched_prefer_sync_wakee_to_waker &&
-	       cpu_rq(cpu)->nr_running == 1 &&
-	       cpumask_test_cpu(cpu, tsk_cpus_allowed(p)) &&
-	       cpu_active(cpu) && !cpu_isolated(cpu);
+	return cpumask_test_cpu(cpu, tsk_cpus_allowed(p)) &&
+	       cpu_active(cpu) && !cpu_isolated(cpu) &&
+	       task_fits_max(p, cpu);
 }
 
 static int calc_util_delta(struct energy_env *eenv, int cpu)
@@ -6738,10 +6722,8 @@ static int energy_aware_wake_cpu(struct task_struct *p, int target, int sync)
 	unsigned int target_cpu_util = UINT_MAX;
 	long target_cpu_new_util_cum = LONG_MAX;
 	struct cpumask *rtg_target = NULL;
-	bool wake_on_sibling = false;
 	int isolated_candidate = -1;
 	bool need_idle;
-	bool skip_ediff = false;
 	enum sched_boost_policy placement_boost = task_sched_boost(p) ?
 				sched_boost_policy() : SCHED_BOOST_NONE;
 
@@ -6754,10 +6736,17 @@ static int energy_aware_wake_cpu(struct task_struct *p, int target, int sync)
 	sg_target = sg;
 
 	sync = sync && sysctl_sched_sync_hint_enable;
+
 	curr_util = boosted_task_util(cpu_rq(cpu)->curr);
 
 	need_idle = wake_to_idle(p);
 
+	if (sync && bias_to_waker_cpu(p, cpu)) {
+		trace_sched_task_util_bias_to_waker(p, task_cpu(p),
+					task_util(p), cpu, cpu, 0, need_idle);
+		return cpu;
+	}
+
 	if (sysctl_sched_is_big_little) {
 		struct related_thread_group *grp;
 
@@ -6765,17 +6754,8 @@ static int energy_aware_wake_cpu(struct task_struct *p, int target, int sync)
 		grp = task_related_thread_group(p);
 		rcu_read_unlock();
 
-		if (grp && grp->preferred_cluster) {
+		if (grp && grp->preferred_cluster)
 			rtg_target = &grp->preferred_cluster->cpus;
-		} else if (sync && wake_on_waker_sibling(p)) {
-			if (bias_to_waker_cpu(p, cpu)) {
-				trace_sched_task_util_bias_to_waker(p,
-						task_cpu(p), task_util(p), cpu,
-						cpu, 0, need_idle);
-				return cpu;
-			}
-			wake_on_sibling = true;
-		}
 
 		task_util_boosted = boosted_task_util(p);
 
@@ -6826,21 +6806,6 @@ static int energy_aware_wake_cpu(struct task_struct *p, int target, int sync)
 							     rtg_target))
 						break;
 					continue;
-				} else if (wake_on_sibling) {
-					/* Skip non-sibling CPUs */
-					if (!cpumask_test_cpu(cpu,
-							sched_group_cpus(sg)))
-						continue;
-				} else if (sync && curr_util >=
-						   task_util_boosted) {
-					if (cpumask_test_cpu(cpu,
-							sched_group_cpus(sg))) {
-						if (!cpumask_test_cpu(task_cpu(p),
-								      sched_group_cpus(sg)))
-							skip_ediff = true;
-						break;
-					}
-					continue;
 				}
 
 				target_max_cap = capacity_of(max_cap_cpu);
@@ -6909,8 +6874,6 @@ static int energy_aware_wake_cpu(struct task_struct *p, int target, int sync)
 				       idle_get_state_idx(cpu_rq(i));
 
 			if (!need_idle &&
-			    (!wake_on_sibling ||
-			     (wake_on_sibling && i != cpu)) &&
 			    add_capacity_margin(new_util_cum) <
 			    capacity_curr_of(i)) {
 				if (sysctl_sched_cstate_aware) {
@@ -6944,9 +6907,7 @@ static int energy_aware_wake_cpu(struct task_struct *p, int target, int sync)
 					target_cpu = i;
 					break;
 				}
-			} else if (!need_idle &&
-				   (!wake_on_sibling ||
-				    (wake_on_sibling && i != cpu))) {
+			} else if (!need_idle) {
 				/*
 				 * At least one CPU other than target_cpu is
 				 * going to raise CPU's OPP higher than current
@@ -7017,13 +6978,6 @@ static int energy_aware_wake_cpu(struct task_struct *p, int target, int sync)
 		}
 	}
 
-	if (wake_on_sibling && target_cpu != -1) {
-		trace_sched_task_util_bias_to_waker(p, task_cpu(p),
-						task_util(p), target_cpu,
-						target_cpu, 0, need_idle);
-		return target_cpu;
-	}
-
 	if (target_cpu != task_cpu(p) && !cpu_isolated(task_cpu(p))) {
 		struct energy_env eenv = {
 			.util_delta	= task_util(p),
@@ -7059,8 +7013,7 @@ static int energy_aware_wake_cpu(struct task_struct *p, int target, int sync)
 			return target_cpu;
 		}
 
-		if (!skip_ediff)
-			ediff = energy_diff(&eenv);
+		ediff = energy_diff(&eenv);
 
 		if (!sysctl_sched_cstate_aware) {
 			if (ediff >= 0) {
diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h
index 28d660b..d4a0612 100644
--- a/kernel/sched/sched.h
+++ b/kernel/sched/sched.h
@@ -1700,6 +1700,13 @@ unsigned long arch_scale_cpu_capacity(struct sched_domain *sd, int cpu)
 }
 #endif
 
+#ifndef arch_update_cpu_capacity
+static __always_inline
+void arch_update_cpu_capacity(int cpu)
+{
+}
+#endif
+
 #ifdef CONFIG_SMP
 static inline unsigned long capacity_of(int cpu)
 {