Merge "msm: rpc: invalidate data structures during close"
diff --git a/Documentation/ABI/testing/sysfs-bus-pil b/Documentation/ABI/testing/sysfs-bus-pil
deleted file mode 100644
index 797b2ea..0000000
--- a/Documentation/ABI/testing/sysfs-bus-pil
+++ /dev/null
@@ -1,18 +0,0 @@
-What:		/sys/bus/pil/devices/.../name
-Date:		March 2012
-Contact:	Stephen Boyd <sboyd@codeaurora.org>
-Description:
-		Shows the name of the peripheral used in pil_get().
-
-What:		/sys/bus/pil/devices/.../state
-Date:		March 2012
-Contact:	Stephen Boyd <sboyd@codeaurora.org>
-Description:
-		Shows the state state of a peripheral. Current states
-		supported are:
-
-			OFFLINE - peripheral is offline
-			ONLINE - peripheral is online
-
-		This file supports poll() to detect when a peripheral changes
-		state.
diff --git a/Documentation/arm/msm/tspp.txt b/Documentation/arm/msm/tspp.txt
index a56f014..d770260 100644
--- a/Documentation/arm/msm/tspp.txt
+++ b/Documentation/arm/msm/tspp.txt
@@ -157,13 +157,12 @@
 
 API
 ===
-int tspp_open_stream(tspp_device *dev, void *stream, void *channel, tspp_mode
-	mode);
-int tspp_close_stream(tspp_device *dev, void *stream);
-int tspp_open_channel(tspp_device *dev, int dest, int bufsize, void *channel);
-int tspp_close_channel(tspp_device *dev, void *channel);
-int tspp_register_filter(tspp_device *dev, void *channel, tspp_filter *filter);
-int tspp_unregister_filter(tspp_device *dev, void *channel, int pid);
+int tspp_open_stream(u32 dev, u32 channel, struct tspp_select_source *source);
+int tspp_close_stream(u32 dev, u32 channel);
+int tspp_open_channel(u32 dev, u32 channel);
+int tspp_close_channelu(32 dev, u32 channel);
+int tspp_add_filter(u32 dev, u32 channel, struct tspp_filter *filter);
+int tspp_remove_filter(u32 dev, u32 channel, struct tspp_filter *filter);
 
 Refer to chrdev implementation in kernel/drivers/misc/tspp.c for an example of
 how to use this api.
diff --git a/Documentation/devicetree/bindings/arm/msm/acpuclock/acpuclock-9625.txt b/Documentation/devicetree/bindings/arm/msm/acpuclock/acpuclock-9625.txt
new file mode 100644
index 0000000..ad0a6db
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/msm/acpuclock/acpuclock-9625.txt
@@ -0,0 +1,22 @@
+* Qualcomm Application CPU clock driver
+
+acpuclock-9625 is the application cpu clock driver for MDM9625. It is used for
+cpu frequency scaling, voltage scaling and bus bandwidth scaling.
+
+Required properties:
+- compatible: "qcom,acpuclk-9625"
+- reg: offset and length of the register sets for the acpuclock controller
+- reg-names: name of the bases for the above registers. "rcg_base", "pwr_base"
+	     are expected.
+- a5_cpu-supply: regulator to supply a5 cpu
+- a5_mem-supply: regulator to supply a5 l2 cache
+
+Example:
+        qcom,acpuclk@f9010000 {
+                compatible = "qcom,acpuclk-9625";
+                reg = <0xf9010008 0x10>,
+                      <0xf9008004 0x4>;
+                reg-names = "rcg_base", "pwr_base";
+                a5_cpu-supply = <&pm8019_l10_corner_ao>;
+                a5_mem-supply = <&pm8019_l12_ao>;
+        };
diff --git a/Documentation/devicetree/bindings/arm/msm/bam_dmux.txt b/Documentation/devicetree/bindings/arm/msm/bam_dmux.txt
index d82284d..53a67a4 100644
--- a/Documentation/devicetree/bindings/arm/msm/bam_dmux.txt
+++ b/Documentation/devicetree/bindings/arm/msm/bam_dmux.txt
@@ -5,10 +5,14 @@
 - reg : the location and size of the BAM hardware
 - interrupts : the BAM hardware to apps processor interrupt line
 
+Optional properties:
+-qcom,satellite-mode: the hardware needs to be configured in satellite mode
+
 Example:
 
 	qcom,bam_dmux@fc834000 {
 		compatible = "qcom,bam_dmux";
 		reg = <0xfc834000 0x7000>;
 		interrupts = <0 29 1>;
+		qcom,satellite-mode;
 	};
diff --git a/Documentation/devicetree/bindings/arm/msm/dcvs-core-info.txt b/Documentation/devicetree/bindings/arm/msm/dcvs-core-info.txt
index 5b4d3cf..b7dd427 100644
--- a/Documentation/devicetree/bindings/arm/msm/dcvs-core-info.txt
+++ b/Documentation/devicetree/bindings/arm/msm/dcvs-core-info.txt
@@ -7,10 +7,14 @@
 
 - qcom,core-core-type:	indicates whether this core is a CPU(0) or a GPU(1)
 
+- qcom,num-cores:	The number of cores this entry represents
+- qcom,sensors:		The vector of sensor ids for the cores
+
 - qcom,algo-disable-pc-threshold:	sets highest frequency at which DCVS
 					will allow the CPU to power collapse.
 - qcom,algo-em-win-size-min-us:		sets minimum Energy Minimization(EM)
 					window size.
+
 - qcom,algo-em-win-size-max-us:		sets maximum EM window size.
 - qcom,algo-em-max-util-pct:		sets maximum CPU utilization that will
 					not be exceeded by any core when
@@ -67,7 +71,7 @@
 
 			compatible = "qcom,dcvs-core-info";
 
-			qcom,num_cores = <1>;
+			qcom,num-cores = <1>;
 			qcom,sensors = <0>;
 
 			qcom,core-core-type = <1>;
diff --git a/Documentation/devicetree/bindings/arm/msm/lpm-resources.txt b/Documentation/devicetree/bindings/arm/msm/lpm-resources.txt
index b16d40f..7f2a21b 100644
--- a/Documentation/devicetree/bindings/arm/msm/lpm-resources.txt
+++ b/Documentation/devicetree/bindings/arm/msm/lpm-resources.txt
@@ -8,13 +8,25 @@
 that need to be monitored for usage requirement to check if a given low power
 state can be entered.Each resource is identified by a combination of the name,
 id,type and key which is also used by the RPM to identify a shared resource.
+The name and resource-type are required nodes; the type, id and key are
+optional nodes which are needed if the resource type is RPM shared resource
+(MSM_LPM_RPM_RS_TYPE).
 
-The required nodes for lpm-resources are:
+The nodes for lpm-resources are:
+
+Required Nodes:
 
 - compatible: "qcom,lpm-resources"
 - reg: The numeric level id
 - qcom,name: The name of the low power resource represented
              as a string.
+- qcom,resource-type: The type of the LPM resource.
+   MSM_LPM_RPM_RS_TYPE    = 0
+   MSM_LPM_LOCAL_RS_TYPE  = 1
+
+
+Optional Nodes:
+
 - qcom,type: The type of resource used like smps or pxo
              represented as a hex value.
 - qcom,id: The id representing a device within a resource type.
@@ -25,6 +37,7 @@
             qcom,lpm-resources@0 {
                         reg = <0x0>;
                         qcom,name = "vdd-dig";
+                        qcom,resource-type = <0>;
                         qcom,type = <0x62706d73>;   /* "smpb" */
                         qcom,id = <0x02>;
                         qcom,key = <0x6e726f63>;   /* "corn" */
diff --git a/Documentation/devicetree/bindings/arm/msm/memory-reserve.txt b/Documentation/devicetree/bindings/arm/msm/memory-reserve.txt
index 068e256..c1b79ae 100644
--- a/Documentation/devicetree/bindings/arm/msm/memory-reserve.txt
+++ b/Documentation/devicetree/bindings/arm/msm/memory-reserve.txt
@@ -40,3 +40,32 @@
 EXPORT_COMPAT("qcom,a-driver") to the driver, similar to EXPORT_SYMBOL.
 The EXPORT_COMPAT is to ensure that memory is only carved out if the
 driver is actually enabled, otherwise the memory will not be used.
+
+If a reservation is needed that isn't associated directly with any one
+driver, the compatible string "qcom,msm-contig-mem" can be used. For
+example:
+
+	qcom,msm-contig-mem {
+		compatible = "qcom,msm-contig-mem";
+		qcom,memory-reservation-type = "EBI1";
+		qcom,memory-reservation-size = <0x280000>; /* 2.5M EBI1 buffer */
+	};
+
+
+In order to specify the size and address of the fixed memory which has
+previously been removed the memory-fixed binding can be used. This assumes
+that the region has been removed by a separate memblock-remove property
+present in the device tree.
+
+Required parameters:
+-qcom,memory-fixed: base and size of the fixed memory region
+
+	qcom,a-driver {
+		compatible = "qcom,a-driver";
+		/* Fixed Memory region of 4MB at 0x200000*/
+		qcom,memory-fixed = <0x200000 0x400000>;
+	};
+
+This region is assumed to be a part of a separate hole that has been removed
+and this binding specifies the fixed location and size of the region within
+that hole.
diff --git a/Documentation/devicetree/bindings/arm/msm/msm_bus.txt b/Documentation/devicetree/bindings/arm/msm/msm_bus.txt
index 1ec3081..fb72525 100644
--- a/Documentation/devicetree/bindings/arm/msm/msm_bus.txt
+++ b/Documentation/devicetree/bindings/arm/msm/msm_bus.txt
@@ -12,23 +12,23 @@
 the clients' device nodes. The clients can register with the bus driver
 using the following properties:
 
-- qcom,msm_bus,name:		String representing the client-name
-- qcom,msm_bus,num_cases:	Total number of usecases
-- qcom,msm_bus,active_only:	Context flag for requests in active or
+- qcom,msm-bus,name:		String representing the client-name
+- qcom,msm-bus,num-cases:	Total number of usecases
+- qcom,msm-bus,active-only:	Context flag for requests in active or
 				dual (active & sleep) contex
-- qcom,msm_bus,num_paths:	Total number of master-slave pairs
-- qcom,msm_bus,vectors:		Arrays of unsigned integers representing:
-				master-id, slave-id, arbitrated bandwidth,
-				instantaneous bandwidth
+- qcom,msm-bus,num-paths:	Total number of master-slave pairs
+- qcom,msm-bus,vectors-KBps:	Arrays of unsigned integers representing:
+				master-id, slave-id, arbitrated bandwidth
+				in KBps, instantaneous bandwidth in KBps
 
 Example:
 
-	qcom,msm_bus,name = "client-name";
-	qcom,msm_bus,num_cases = <3>;
-	qcom,msm_bus,active_only = <0>;
-	qcom,msm_bus,num_paths = <2>;
-	qcom,msm_bus,vectors =
+	qcom,msm-bus,name = "client-name";
+	qcom,msm-bus,num-cases = <3>;
+	qcom,msm-bus,active-only = <0>;
+	qcom,msm-bus,num-paths = <2>;
+	qcom,msm-bus,vectors =
 			<22 512 0 0>, <26 512 0 0>,
-			<22 512 320000 320000000>, <26 512 3200000 320000000>,
-			<22 512 160000 160000000>, <26 512 1600000 160000000>;
+			<22 512 320000 3200000>, <26 512 3200000 3200000>,
+			<22 512 160000 1600000>, <26 512 1600000 1600000>;
 
diff --git a/Documentation/devicetree/bindings/arm/msm/pm-8x60.txt b/Documentation/devicetree/bindings/arm/msm/pm-8x60.txt
new file mode 100644
index 0000000..b429072
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/msm/pm-8x60.txt
@@ -0,0 +1,28 @@
+* MSM PM-8x60
+
+PM-8x60 is the low power management device for MSM (Snapdragon class) chipsets.
+This device sets up different components to do low power modes and registers with
+the kernel to be notified of idle and suspend states and when called, follows
+through the set of instructions in putting the application cores to the lowest
+power mode possible.
+
+The required properties for PM-8x60 are:
+
+- compatible: "qcom,pm-8x60"
+
+The optional properties are:
+
+- qcom,use-sync-timer: Indicates whether the target uses the synchronized QTimer.
+- qcom,pc-mode: Indicates the type of power collapse used by the target. The
+           valid values for this are:
+	0  (Power collapse terminates in TZ; integrated L2 cache controller)
+	1, (Power collapse doesn't terminate in TZ; external L2 cache controller)
+	2  (Power collapse terminates in TZ; external L2 cache controller)
+
+Example:
+
+qcom,pm-8x60 {
+		compatible = "qcom,pm-8x60";
+		qcom,pc-mode = <0>;
+		qcom,use-sync-timer;
+	};
diff --git a/Documentation/devicetree/bindings/arm/msm/spm-v2.txt b/Documentation/devicetree/bindings/arm/msm/spm-v2.txt
index 1a19dbb..a2d8359 100644
--- a/Documentation/devicetree/bindings/arm/msm/spm-v2.txt
+++ b/Documentation/devicetree/bindings/arm/msm/spm-v2.txt
@@ -33,8 +33,10 @@
 	between AVS controller requests
 - qcom,saw2-pmic-data0..7: Specify the pmic data value and the associated FTS
 	index to send the PMIC data to
-- qcom,saw2-vctl-port: The FTS port used for changing voltage
-- qcom,saw2-phase-port: The FTS port used for changing the number of phases
+- qcom,saw2-vctl-port: The PVC (PMIC Virtual Channel) port used for changing
+	voltage
+- qcom,saw2-phase-port: The PVC port used for changing the number of phases
+- qcom,saw2-pfm-port: The PVC port used for enabling PWM/PFM modes
 - qcom,saw2-spm-cmd-wfi: The WFI command sequence
 - qcom,saw2-spm-cmd-ret: The Retention command sequence
 - qcom,saw2-spm-cmd-spc: The Standalone PC command sequence
diff --git a/Documentation/devicetree/bindings/fb/mdss-edp.txt b/Documentation/devicetree/bindings/fb/mdss-edp.txt
new file mode 100644
index 0000000..3c4e1d3
--- /dev/null
+++ b/Documentation/devicetree/bindings/fb/mdss-edp.txt
@@ -0,0 +1,35 @@
+Qualcomm MDSS EDP
+
+MDSS EDP is a edp driver which supports panels that are compatable with
+VESA EDP display interface specification.
+
+Required properties
+- compatible :				Must be "qcom,mdss-edp".
+- reg :						Offset and length of the register set for the
+							device.
+- reg-names :				Names to refer to register sets related to this
+							device
+- vdda-supply :				Phandle for vdd regulator device node.
+- gpio-panel-en	:			GPIO for supplying power to panel and backlight
+							driver.
+- qcom,panel-lpg-channel :	LPG channel for backlight.
+- qcom,panel-pwm-period :	PWM period in microseconds.
+- status :					A string that has to be set to "okay/ok" to enable
+							the driver. By default this property will be set to
+							"disable". Will be set to "ok/okay" status for
+							specific platforms.
+
+Example:
+	mdss_edp: qcom,mdss_edp@fd923400 {
+		compatible = "qcom,mdss-edp";
+		reg = <0xfd923400 0x700>,
+			<0xfd8c2000 0x1000>;
+		reg-names = "edp_base", "mmss_cc_base";
+		vdda-supply = <&pm8941_l12>;
+		gpio-panel-en = <&msmgpio 58 0>;
+		qcom,panel-lpg-channel = <7>; /* LPG Channel 8 */
+		qcom,panel-pwm-period = <53>;
+		status = "disable";
+	};
+
+
diff --git a/Documentation/devicetree/bindings/gpio/gpio_keys.txt b/Documentation/devicetree/bindings/gpio/gpio_keys.txt
index 5c2c021..4e810e1 100644
--- a/Documentation/devicetree/bindings/gpio/gpio_keys.txt
+++ b/Documentation/devicetree/bindings/gpio/gpio_keys.txt
@@ -4,6 +4,7 @@
 	- compatible = "gpio-keys";
 
 Optional properties:
+	- input-name: input name of the device
 	- autorepeat: Boolean, Enable auto repeat feature of Linux input
 	  subsystem.
 
@@ -28,6 +29,7 @@
 			#address-cells = <1>;
 			#size-cells = <0>;
 			autorepeat;
+			input-name = "gpio-keys";
 			button@21 {
 				label = "GPIO Key UP";
 				linux,code = <103>;
diff --git a/Documentation/devicetree/bindings/gpio/qpnp-pin.txt b/Documentation/devicetree/bindings/gpio/qpnp-pin.txt
index 31c3bc2..36ac336 100644
--- a/Documentation/devicetree/bindings/gpio/qpnp-pin.txt
+++ b/Documentation/devicetree/bindings/gpio/qpnp-pin.txt
@@ -94,7 +94,7 @@
 			QPNP_PIN_OUT_STRENGTH_MED  = 2, (GPIO)
 			QPNP_PIN_OUT_STRENGTH_HIGH = 3, (GPIO)
 
-  - qcom,src-select:	select a function for the pin. Certain pins
+  - qcom,src-sel:	select a function for the pin. Certain pins
 			can be paired (shorted) with each other. Some gpio pins
 			can act as alternate functions.
 			In the context of gpio, this acts as a source select.
diff --git a/Documentation/devicetree/bindings/gpu/adreno.txt b/Documentation/devicetree/bindings/gpu/adreno.txt
index 16925fb..38b2721 100644
--- a/Documentation/devicetree/bindings/gpu/adreno.txt
+++ b/Documentation/devicetree/bindings/gpu/adreno.txt
@@ -62,6 +62,7 @@
 - qcom,chipid:		   If it exists this property is used to replace
 			   the chip identification read from the GPU hardware.
 			   This is used to override faulty hardware readings.
+- qcom,strtstp-sleepwake:  Boolean. Enables use of GPU SLUMBER instead of SLEEP for power savings
 
 Example of A330 GPU in MSM8974:
 
diff --git a/Documentation/devicetree/bindings/hwmon/epm_adc.txt b/Documentation/devicetree/bindings/hwmon/epm_adc.txt
index 89edc16..a0ca490 100644
--- a/Documentation/devicetree/bindings/hwmon/epm_adc.txt
+++ b/Documentation/devicetree/bindings/hwmon/epm_adc.txt
@@ -9,7 +9,7 @@
 EPM node
 
 Required properties:
-- compatible : should be "qcom,epm-adc" for EPM using PSoC5.
+- compatible : should be "cy,epm-adc-cy8c5568lti-114" for EPM using PSoC5.
 - reg : chip select for the device.
 - interrupt-parent : should be phandle of the interrupt controller
 		     servicing the interrupt for this device.
diff --git a/Documentation/devicetree/bindings/input/touchscreen/atmel-mxt-ts.txt b/Documentation/devicetree/bindings/input/touchscreen/atmel-mxt-ts.txt
index 88fca69..bcea355 100644
--- a/Documentation/devicetree/bindings/input/touchscreen/atmel-mxt-ts.txt
+++ b/Documentation/devicetree/bindings/input/touchscreen/atmel-mxt-ts.txt
@@ -29,6 +29,8 @@
 					needed
  - atmel,need-calibration	: specify to indicate whether calibration is
 					needed during wakeup.
+ - atmel,no-force-update	: flag that signifies whether force configuration
+					update is applicable or not
 
 Example:
 	i2c@f9966000 {
@@ -52,7 +54,8 @@
 			vcc_i2c-supply = <&pm8941_lvs1>;
 			atmel,panel-coords = <0 0 479 799>;
 			atmel,display-coords = <0 0 479 799>;
-			atmel,i2c-pull-up = <1>;
+			atmel,i2c-pull-up;
+			atmel,no-force-update;
 			atmel,dig-reg-support;
 			atmel,key-codes = <
 				102 139 0 0 0 0 0 0
diff --git a/Documentation/devicetree/bindings/iommu/msm_iommu.txt b/Documentation/devicetree/bindings/iommu/msm_iommu.txt
index 7a90cc0..7872280 100644
--- a/Documentation/devicetree/bindings/iommu/msm_iommu.txt
+++ b/Documentation/devicetree/bindings/iommu/msm_iommu.txt
@@ -5,6 +5,9 @@
 	- "qcom,msm-smmu-v2"
 - reg : offset and length of the register set for the device.
 
+Optional properties:
+- qcom,iommu-secure-id : Secure identifier for the IOMMU block
+
 - List of sub nodes, one for each of the translation context banks supported.
   Each sub node has the following required properties:
 
@@ -13,7 +16,6 @@
   - qcom,iommu-ctx-sids : List of stream identifiers associated with this
     translation context.
   - label : Name of the context bank
-  - qcom,iommu-smt-size : Number of SMR entries in the SMT of this HW block
   - vdd-supply : vdd-supply: phandle to GDSC regulator controlling this IOMMU.
 
 Optional properties:
diff --git a/Documentation/devicetree/bindings/leds/leds-qpnp.txt b/Documentation/devicetree/bindings/leds/leds-qpnp.txt
index 51bf9e6..ef77e1e 100644
--- a/Documentation/devicetree/bindings/leds/leds-qpnp.txt
+++ b/Documentation/devicetree/bindings/leds/leds-qpnp.txt
@@ -12,14 +12,14 @@
 - compatible	: should be "qcom,leds-qpnp"
 
 Each LED module is represented as a node of "leds-qpnp". This
-node will furthur contain the type of LED supported and its
+node will further contain the type of LED supported and its
 properties.
 
 Required properties:
 - qcom,id		: must be one of values supported in enum qpnp_led
-- qcom,label		: type of led that will be used, ie "wled"
+- label			: type of led that will be used, ie "wled"
 - qcom,max-current	: maximum current that the LED can sustain
-= qcom,name		: name the led will be called by in sysfs entry
+- linux,name		: name of the led that is used in led framework
 
 WLED is primarily used as display backlight. Display subsystem uses
 LED triggers for WLED to control the brightness as needed.
@@ -37,21 +37,56 @@
 - linux,default-trigger: trigger the led from external modules such as display
 - qcom,default-state:  default state of the led, should be "on" or "off"
 
+Flash is used primarily as a camera or video flash.
+
+Optional properties for flash:
+- qcom,headroom: headroom to use, mV
+- qcom,duration: duration of the flash, ms
+- qcom,clamp-curr: current to clamp at, mA
+- qcom,startup-dly: delay before flashing after flash executed, us
+- qcom,saftey-timer: include for safety timer use, otherwise watchdog timer will be used
+- linux,default-trigger: trigger the led from external modules such as display
+- qcom,default-state:  default state of the led, should be "on" or "off"
+
 Example:
 
 	qcom,leds@d800 {
 			compatible = "qcom,leds-qpnp";
-			reg = <0xD800 0x100>;
-			linux,default-trigger = "bkl-trigger"
-			qcom,label = "wled";
-			qcom,cs-out-en;
-			qcom,op-fdbck;
-			qcom,default-state "off";
-			qcom,max-current = <25>;
-			qcom,ctrl-delay-us = <0>;
-			qcom,boost-curr-lim = <3>;
-			qcom,cp-sel = <0>;
-			qcom,switch-freq = <2>;
-			qcom,ovp-val = <2>;
-			qcom,num-strings = <1>;
-	}
+			status = "okay";
+			qcom,wled_0 {
+				linux,default-trigger = "bkl-trigger"
+				label = "wled";
+				qcom,cs-out-en;
+				qcom,op-fdbck;
+				qcom,default-state "off";
+				qcom,max-current = <25>;
+				qcom,ctrl-delay-us = <0>;
+				qcom,boost-curr-lim = <3>;
+				qcom,cp-sel = <0>;
+				qcom,switch-freq = <2>;
+				qcom,ovp-val = <2>;
+				qcom,num-strings = <1>;
+				qcom,id = <0>;
+				linux,name = "led:wled_backlight";
+			};
+	};
+
+	qcom,leds@d300 {
+			compatible = "qcom,leds-qpnp";
+			status = "okay";
+			qcom,flash_0 {
+				qcom,max-current = <1000>;
+				qcom,default-state = "off";
+				qcom,headroom = <0>;
+				qcom,duration = <200>;
+				qcom,clamp-curr = <200>;
+				qcom,startup-dly = <1>;
+				qcom,safety-timer;
+				label = "flash";
+				linux,default-trigger =
+					"flash0_trigger";
+				linux,name = "led:flash_0";
+				qcom,current = <625>;
+				qcom,id = <1>;
+			};
+	};
diff --git a/Documentation/devicetree/bindings/pil/pil-mba.txt b/Documentation/devicetree/bindings/pil/pil-mba.txt
deleted file mode 100644
index 9692059..0000000
--- a/Documentation/devicetree/bindings/pil/pil-mba.txt
+++ /dev/null
@@ -1,30 +0,0 @@
-Qualcomm Modem Boot Authenticator Peripheral Image Loader
-
-pil-mba is a peripheral image loader (PIL) driver. It is used for loading
-modem images using the self-authenticating hardware and software features
-of the Modem Boot Authenticator.
-
-Required properties:
-- compatible:	      Must be "qcom,pil-mba"
-- reg:		      Two pairs of physical base addresses and sizes. The
-		      first corresponds to the Relay Message Buffer (RMB)
-		      register base. The second specifies the address at which
-		      the primary modem image metadata should be stored.
-- reg-names:	      Names for the above base addresses. "rmb_base" and
-	              "metadata_base" are expected.
-- qcom,firmware-name: Base name of the firmware image. Ex. "modem"
-
-Optional properties:
-- qcom,depends-on:    firmware-name of a prerequisite image that must already
-		      be running.
-
-Example:
-	qcom,mba@fc820000 {
-		compatible = "qcom,pil-mba";
-		reg = <0xfc820000 0x0020>,
-		      <0x0d1f0000 0x4000>;
-		reg-names = "rmb_base", "metadata_base";
-
-		qcom,firmware-name = "modem";
-		qcom,depends-on    = "mba";
-	};
diff --git a/Documentation/devicetree/bindings/pil/pil-pronto.txt b/Documentation/devicetree/bindings/pil/pil-pronto.txt
index e123bdb..e3108ac 100644
--- a/Documentation/devicetree/bindings/pil/pil-pronto.txt
+++ b/Documentation/devicetree/bindings/pil/pil-pronto.txt
@@ -10,6 +10,7 @@
 - reg: offset and length of the register set for the device.
 - reg-names: names of the bases for the above registers. "pmu_base", "clk_base",
              and "halt_base" are expected.
+- interrupts: WCNSS to Apps watchdog bite interrupt
 - vdd_pronto_pll-supply: regulator to supply pronto pll.
 - qcom,firmware-name: Base name of the firmware image. Ex. "wcnss"
 
@@ -21,6 +22,7 @@
 		      <0xfd485300 0xc>;
 		reg-names = "pmu_base", "clk_base", "halt_base";
 		vdd_pronto_pll-supply = <&pm8941_l12>;
+		interrupts = <0 231 1>;
 
 		qcom,firmware-name = "wcnss";
 	};
diff --git a/Documentation/devicetree/bindings/pil/pil-q6v5-lpass.txt b/Documentation/devicetree/bindings/pil/pil-q6v5-lpass.txt
index d39c98c..ac9600d 100644
--- a/Documentation/devicetree/bindings/pil/pil-q6v5-lpass.txt
+++ b/Documentation/devicetree/bindings/pil/pil-q6v5-lpass.txt
@@ -11,6 +11,7 @@
 		      memory mapped registers.
 - reg-names:	      Names of the bases for the above registers. "qdsp6_base"
 		      and "halt_base" are expected.
+- interrupts:         The lpass watchdog interrupt
 - qcom,firmware-name: Base name of the firmware image. Ex. "lpass"
 
 Example:
@@ -19,6 +20,7 @@
 	        reg = <0xfe200000 0x00100>,
 	              <0xfd485100 0x00010>;
 		reg-names = "qdsp6_base", "halt_base";
+		interrupts = <0 194 1>;
 
 	        qcom,firmware-name = "lpass";
 	};
diff --git a/Documentation/devicetree/bindings/pil/pil-q6v5-mss.txt b/Documentation/devicetree/bindings/pil/pil-q6v5-mss.txt
index 41ffd8a..f83de7e 100644
--- a/Documentation/devicetree/bindings/pil/pil-q6v5-mss.txt
+++ b/Documentation/devicetree/bindings/pil/pil-q6v5-mss.txt
@@ -10,8 +10,9 @@
 - reg:		      Pairs of physical base addresses and region sizes of
 		      memory mapped registers.
 - reg-names:	      Names of the bases for the above registers. "qdsp6_base",
-		      "halt_base", "rmb_base", "restart_reg" and "clamp_reg"
-		      are expected.
+		      "halt_base", "rmb_base", "restart_reg", and
+		      "metadata_base" are expected.
+- interrupts:         The modem watchdog interrupt
 - vdd_mss-supply:     Reference to the regulator that supplies the processor.
 - qcom,firmware-name: Base name of the firmware image. Ex. "mdsp"
 - qcom,pil-self-auth: <0> if the hardware does not require self-authenticating
@@ -25,9 +26,10 @@
 		      <0xfd485000 0x400>,
 		      <0xfc820000 0x020>,
 		      <0xfc401680 0x004>,
-		      <0xfc980008 0x004>;
+		      <0x0d1f0000 0x4000>;
 		reg-names = "qdsp6_base", "halt_base", "rmb_base",
-			    "restart_reg", "clamp_reg";
+			    "restart_reg", metadata_base";
+		interrupts = <0 24 1>;
 		vdd_mss-supply = <&pm8841_s3>;
 
 		qcom,firmware-name = "mba";
diff --git a/Documentation/devicetree/bindings/pil/pil-venus.txt b/Documentation/devicetree/bindings/pil/pil-venus.txt
index 4b87f17..232c2cd 100644
--- a/Documentation/devicetree/bindings/pil/pil-venus.txt
+++ b/Documentation/devicetree/bindings/pil/pil-venus.txt
@@ -12,8 +12,6 @@
              "vbif_base" are expected.
 - vdd-supply: regulator to supply venus.
 - qcom,firmware-name: Base name of the firmware image. Ex. "venus"
-- qcom,firmware-min-paddr: The lowest addr boundary for firmware image in DDR
-- qcom,firmware-max-paddr: The highest addr boundary for firmware image in DDR
 
 Example:
         qcom,venus@fdce0000 {
@@ -24,7 +22,4 @@
                 vdd-supply = <&gdsc_venus>;
 
                 qcom,firmware-name = "venus";
-                qcom,firmware-min-paddr = <0xF500000>;
-                qcom,firmware-max-paddr = <0xFA00000>;
-
         };
diff --git a/Documentation/devicetree/bindings/platform/msm/qpnp-power-on.txt b/Documentation/devicetree/bindings/platform/msm/qpnp-power-on.txt
index 21d376a..adb93b8 100644
--- a/Documentation/devicetree/bindings/platform/msm/qpnp-power-on.txt
+++ b/Documentation/devicetree/bindings/platform/msm/qpnp-power-on.txt
@@ -11,18 +11,24 @@
 - compatible:	Must be "qcom,qpnp-power-on"
 - reg:		Specifies the SPMI address and size for this PON (power-on) peripheral
 - interrupts:	Specifies the interrupt associated with PON.
+- interrupt-names:	Specify the interrupt names associated with interrupts. Must be
+			one of "kpdpwr", "kpdpwr-bark", "resin", "resin-bark", "cblpwr".
+			Bark interrupts are associated with system reset configuration
+			to allow default reset configuration to be activated. If system
+			reset configuration is not supported then bark interrupts are
+			nops.
 
 Optional properties:
-- qcom,pon-dbc-delay		The debouce delay for the power-key interrupt
-				specifed in us. The value ranges from 2 seconds
+- qcom,pon-dbc-delay		The debounce delay for the power-key interrupt
+				specified in us. The value ranges from 2 seconds
 				to 1/64 of a second. Possible values are -
 				- 2, 1, 1/2, 1/4, 1/8, 1/16, 1/32, 1/64
 				- Intermediate value is rounded down to the
 				nearest valid value.
 - qcom,pon_1 ...pon_n		These represent the child nodes which describe
 				the properties (reset, key) for each of the pon
-				reset source. All the child nodes are optional,
-				if none of them are specified the driver fails
+				reset source. All the child nodes are optional.
+				If none of them is specified, the driver fails
 				to register.
 - qcom,system-reset		Specifies that this PON peripheral can be used
 				to reset the system. This property can only be
@@ -32,32 +38,32 @@
 All the below properties are in the sub-node section (properties of the child
 node).
 
+Sub-node required properties:
+- qcom,pon-type			The type of PON/RESET source. The driver
+				currently supports KPDPWR(0), RESIN(1) and
+				CBLPWR(2) pon/reset sources.
+
+Sub-node optional properties:
 - qcom,pull-up			The initial state of the reset pin under
 				consideration.
 				0 = No pull-up
 				1 = pull-up enabled
-				This property is optional and is set to '0'
-				if not specified.
-- qcom,pon-type			The type of PON/RESET source. The driver
-				currently supports KPDPWR(0) and RESIN(1)
-				pon/reset sources. This property must be
-				specified.
+				This property is set to '0' if not specified.
 - qcom,support-reset		Indicates if this PON source supports
 				reset functionality.
 				0 = Not supported
 				1 = Supported
-				This property is optional and is set to '0'
-				if not specified.
-- qcom,s1-timer			The debouce timer for the BARK interrupt for
+				This property is set to '0' if not specified.
+- qcom,s1-timer			The debounce timer for the BARK interrupt for
 				that reset source. Value is specified in ms.
 				Supported values are -
 				- 0, 32, 56, 80, 128, 184, 272, 408, 608, 904
 				  1352, 2048, 3072, 4480, 6720, 10256
 				This property must be specified only if
 				'support-reset' is set to 1.
-- qcom,s2-timer			The debouce timer for the S2 reset specified
+- qcom,s2-timer			The debounce timer for the S2 reset specified
 				in ms. On the expiry of this timer, the PMIC
-				executes the reset sequence. Supoprted values -
+				executes the reset sequence. Supported values -
 				- 0, 10, 50, 100, 250, 500, 1000, 2000
 				This property is required only if
 				'support-reset' is set to 1.
@@ -68,7 +74,7 @@
 				'support-reset' is set to 1.
 - linux,code			The input key-code associated with the reset source.
 				The reset source in its default configuration can be
-				used to support standard keys. This property is optional.
+				used to support standard keys.
 
 Example:
 	qcom,power-on@800 {
diff --git a/Documentation/devicetree/bindings/power/bq28400-battery.txt b/Documentation/devicetree/bindings/power/bq28400-battery.txt
new file mode 100644
index 0000000..3879b4d
--- /dev/null
+++ b/Documentation/devicetree/bindings/power/bq28400-battery.txt
@@ -0,0 +1,18 @@
+TI BQ28400 Battery Gas Gauge
+
+The bq28400 monitors the battery temperature, capacity, voltage, current etc.
+The device interface is I2C, its I2C slave 7-bit address is 0xb.
+The device is usually embedded inside the "smart battery" pack.
+
+node required properties:
+- compatible:	Must be "ti,bq28400-battery".
+- reg:		I2C Address must be 0xb.
+
+Example:
+	i2c@f9967000 {
+		battery@b {
+			compatible = "ti,bq28400-battery";
+			reg = <0xb>;
+		};
+	};
+
diff --git a/Documentation/devicetree/bindings/power/qpnp-charger.txt b/Documentation/devicetree/bindings/power/qpnp-charger.txt
index 244e622..2103bbc 100644
--- a/Documentation/devicetree/bindings/power/qpnp-charger.txt
+++ b/Documentation/devicetree/bindings/power/qpnp-charger.txt
@@ -29,6 +29,12 @@
 - qcom,chg-ibatmax-ma:	Maximum battery charge current in mA
 - qcom,chg-ibatterm-ma:	Current at which charging is terminated in mA.
 
+Parent node optional properties:
+- qcom,chg-charging-disabled:	Set this property to disable charging
+				by default. This can then be overriden
+				writing the the module parameter
+				"charging_disabled".
+
 Sub node required structure:
 - A qcom,chg node must be a child of an SPMI node that has specified
 	the spmi-dev-container property. Each subnode reflects
diff --git a/Documentation/devicetree/bindings/power/smb350.txt b/Documentation/devicetree/bindings/power/smb350.txt
new file mode 100644
index 0000000..6f21236
--- /dev/null
+++ b/Documentation/devicetree/bindings/power/smb350.txt
@@ -0,0 +1,43 @@
+Summit smb350 battery charger
+
+The smb350 charger supports stack-cell battery charging.
+
+The smb350 interface is via I2C bus.
+The i2c slave 7-bit address is programmable at manufacture.
+
+Node required properties:
+- compatible:		Must be "summit,smb350-charger".
+- reg:			The device 7-bit I2C address.
+- summit,stat-gpio		gpio which smb350 STAT pin connects to.
+- summit,chg-en-n-gpio		gpio which control charging enable.
+- summit,chg-susp-n-gpio	gpio which control device shutdown
+- summit,chg-current-ma		charging current in milliamps.
+- summit,term-current-ma	charging termination current in milliamps.
+				valid values are 200/300/400/500/600/700.
+				A value of zero means no termination current.
+
+Example:
+	i2c@f9967000 {
+		cell-index = <0>;
+		compatible = "qcom,i2c-qup";
+		reg = <0Xf9967000 0x1000>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+		reg-names = "qup_phys_addr";
+		interrupts = <0 105 0>;
+		interrupt-names = "qup_err_intr";
+		qcom,i2c-bus-freq = <100000>;
+		qcom,i2c-src-freq = <24000000>;
+		label = "blsp_11";
+
+		smb350-charger@2b {
+			compatible = "summit,smb350-charger";
+			reg = <0x2b>; /* 0x56/0x57 */
+			summit,stat-gpio = <&pm8941_gpios 30 0x00>;
+			summit,chg-en-n-gpio = <&pm8941_gpios 10 0x00>;
+			summit,chg-susp-n-gpio = <&pm8941_gpios 13 0x00>;
+			summit,chg-current-ma = <1600>;
+			summit,term-current-ma = <200>;
+		};
+	};
+
diff --git a/Documentation/devicetree/bindings/qseecom/qseecom.txt b/Documentation/devicetree/bindings/qseecom/qseecom.txt
index 8b17ba9..5e7c42a 100644
--- a/Documentation/devicetree/bindings/qseecom/qseecom.txt
+++ b/Documentation/devicetree/bindings/qseecom/qseecom.txt
@@ -2,9 +2,21 @@
 
 Required properties:
 - compatible : Should be "qcom,qseecom"
+- qcom, msm_bus,name: Should be "qseecom-noc"
+- qcom, msm_bus,num_cases: Depends on the use cases for bus scaling
+- qcom, msm_bus,num_paths: The paths for source and destination ports
+- qcom, msm_bus,vectors: Vectors for bus topology.
 
 Example:
-
 	qcom,qseecom@fe806000 {
 		compatible = "qcom,qseecom";
+		qcom,msm_bus,name = "qseecom-noc";
+		qcom,msm_bus,num_cases = <4>;
+		qcom,msm_bus,active_only = <0>;
+		qcom,msm_bus,num_paths = <1>;
+		qcom,msm_bus,vectors =
+			<55 512 0 0>,
+			<55 512 3936000000 393600000>,
+			<55 512 3936000000 393600000>,
+			<55 512 3936000000 393600000>;
 	};
diff --git a/Documentation/devicetree/bindings/regulator/stub-regulator.txt b/Documentation/devicetree/bindings/regulator/stub-regulator.txt
new file mode 100644
index 0000000..1057e17
--- /dev/null
+++ b/Documentation/devicetree/bindings/regulator/stub-regulator.txt
@@ -0,0 +1,48 @@
+Stub Voltage Regulators
+
+stub-regulators are place-holder regulator devices which do not impact any
+hardware state.  They provide a means for consumer devices to utilize all
+regulator features for testing purposes.
+
+Required properties:
+- compatible:      Must be "qcom,stub-regulator".
+- regulator-name:  A string used as a descriptive name for regulator outputs.
+
+Optional properties:
+- parent-supply:     phandle to the parent supply/regulator node if one exists.
+- qcom,hpm-min-load: Load current in uA which corresponds to the minimum load
+			which requires the regulator to be in high power mode.
+- qcom,system-load:  Load in uA present on regulator that is not captured by any
+			consumer request.
+
+All properties specified within the core regulator framework can also be used.
+These bindings can be found in regulator.txt.
+
+Example:
+
+/ {
+	pm8026_s3: regulator-s3 {
+		compatible = "qcom,stub-regulator";
+		regulator-name = "8026_s3";
+		qcom,hpm-min-load = <100000>;
+		regulator-min-microvolt = <1300000>;
+		regulator-max-microvolt = <1300000>;
+	};
+
+	pm8026_l1: regulator-l1 {
+		compatible = "qcom,stub-regulator";
+		regulator-name = "8026_l1";
+		parent-supply = <&pm8026_s3>;
+		qcom,hpm-min-load = <10000>;
+		regulator-min-microvolt = <1225000>;
+		regulator-max-microvolt = <1225000>;
+	};
+
+	pm8026_l20: regulator-l20 {
+		compatible = "qcom,stub-regulator";
+		regulator-name = "8026_l20";
+		qcom,hpm-min-load = <5000>;
+		regulator-min-microvolt = <3075000>;
+		regulator-max-microvolt = <3075000>;
+	};
+};
diff --git a/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt b/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt
index 2864fd1..9743d0d 100644
--- a/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt
+++ b/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt
@@ -54,6 +54,16 @@
 
  - compatible : "qcom,msm-pcm-afe"
 
+* msm-dai-q6-hdmi
+
+Required properties:
+ - compatible : "msm-dai-q6-hdmi"
+ - qcom,msm-dai-q6-dev-id : The hdmi multi channel port ID.
+   It is passed onto the dsp from the apps to form an audio
+   path to the HDMI device. Currently the only supported value
+   is 8, which indicates the rx path used for audio playback
+   on HDMI device.
+
 * msm-dai-q6
 
 [First Level Nodes]
@@ -71,6 +81,7 @@
                             Value is from 16384 to 16393
                             BT SCO port ID value from 12288 to 12289
                             RT Proxy port ID values from 224 to 225 and 240 to 241
+                            FM Rx and TX port ID values from 12292 to 12293
 
 * msm-auxpcm
 
@@ -123,15 +134,36 @@
 
  - compatible :                            "qcom,msm-ocmem-audio"
 
- - qcom,msm-ocmem-audio-src-id:            Master port id
+ - qcom,msm_bus,name:                      Client name
 
- - qcom,msm-ocmem-audio-dst-id:            Slave port id
+ - qcom,msm_bus,num_cases:                 Total number of use cases
 
- - qcom,msm-ocmem-audio-ab:                arbitrated bandwidth
-                                           in Bytes/s
+ - qcom,msm_bus,active_only:               Context flag for requests in active or
+                                           dual (active & sleep) contex
 
- - qcom,msm-ocmem-audio-ib:                instantaneous bandwidth
-                                           in Bytes/s
+ - qcom,msm_bus,num_paths:                 Total number of master-slave pairs
+
+ - qcom,msm_bus,vectors:                   Arrays of unsigned integers representing:
+                                           master-id, slave-id, arbitrated bandwidth,
+                                           instantaneous bandwidth
+* wcd9xxx_intc
+
+Required properties:
+
+ - compatible :                            "qcom,wcd9xxx-irq"
+
+ - interrupt-controller :                  Mark this device node as an interrupt
+                                           controller
+
+ - #interrupt-cells :                      Should be 1
+
+ - interrupt-parent :                      Parent interrupt controller
+
+ - interrupts :                            Interrupt number on the parent
+                                           interrupt controller
+
+ - interrupt-names :                       Name of interrupt on the parent
+                                           interrupt controller
 
 Example:
 
@@ -163,6 +195,11 @@
                 compatible = "qcom,msm-dai-fe";
         };
 
+	qcom,msm-dai-q6-hdmi {
+		compatible = "qcom,msm-dai-q6-hdmi";
+		qcom,msm-dai-q6-dev-id = <8>;
+	};
+
 	qcom,msm-dai-q6 {
 		compatible = "qcom,msm-dai-q6";
 		qcom,msm-dai-q6-sb-0-rx {
@@ -185,6 +222,16 @@
 			qcom,msm-dai-q6-dev-id = <12289>;
 		};
 
+		qcom,msm-dai-q6-int-fm-rx {
+			compatible = "qcom,msm-dai-q6-dev";
+			qcom,msm-dai-q6-dev-id = <12292>;
+		};
+
+		qcom,msm-dai-q6-int-fm-tx {
+			compatible = "qcom,msm-dai-q6-dev";
+			qcom,msm-dai-q6-dev-id = <12293>;
+		};
+
 		qcom,msm-dai-q6-be-afe-pcm-rx {
 			compatible = "qcom,msm-dai-q6-dev";
 			qcom,msm-dai-q6-dev-id = <224>;
@@ -234,10 +281,22 @@
 
 	qcom,msm-ocmem-audio {
 		compatible = "qcom,msm-ocmem-audio";
-		qcom,msm-ocmem-audio-src-id = <11>;
-		qcom,msm-ocmem-audio-dst-id = <604>;
-		qcom,msm-ocmem-audio-ab = <209715200>;
-		qcom,msm-ocmem-audio-ib = <471859200>;
+		qcom,msm_bus,name = "audio-ocmem";
+		qcom,msm_bus,num_cases = <2>;
+		qcom,msm_bus,active_only = <0>;
+		qcom,msm_bus,num_paths = <1>;
+		qcom,msm_bus,vectors =
+			<11 604 0 0>,
+			<11 604 32505856 325058560>;
+	};
+
+	wcd9xxx_intc: wcd9xxx-irq {
+		compatible = "qcom,wcd9xxx-irq";
+		interrupt-controller;
+		#interrupt-cells = <1>;
+		interrupt-parent = <&msmgpio>;
+		interrupts = <72 0>;
+		interrupt-names = "cdc-int";
 	};
 
 * MSM8974 ASoC Machine driver
diff --git a/Documentation/devicetree/bindings/sound/taiko_codec.txt b/Documentation/devicetree/bindings/sound/taiko_codec.txt
index 9f3719b..96e3a61 100644
--- a/Documentation/devicetree/bindings/sound/taiko_codec.txt
+++ b/Documentation/devicetree/bindings/sound/taiko_codec.txt
@@ -28,6 +28,10 @@
  - qcom,cdc-micbias2-cfilt-sel = cfilt to use for micbias2 (should be from 1 to 3).
  - qcom,cdc-micbias3-cfilt-sel = cfilt to use for micbias3 (should be from 1 to 3).
  - qcom,cdc-micbias4-cfilt-sel = cfilt to use for micbias4 (should be from 1 to 3).
+ - qcom,cdc-micbias1-ext-cap: Boolean. Enable micbias 1 external capacitor mode.
+ - qcom,cdc-micbias2-ext-cap: Boolean. Enable micbias 2 external capacitor mode.
+ - qcom,cdc-micbias3-ext-cap: Boolean. Enable micbias 3 external capacitor mode.
+ - qcom,cdc-micbias4-ext-cap: Boolean. Enable micbias 4 external capacitor mode.
 
  - qcom,cdc-slim-ifd-dev - namme of the codec slim interface device.
  - qcom,cdc-slim-ifd-elemental-addr - codec slimbus slave interface device
@@ -76,6 +80,10 @@
 	qcom,cdc-micbias2-cfilt-sel = <0x1>;
 	qcom,cdc-micbias3-cfilt-sel = <0x2>;
 	qcom,cdc-micbias4-cfilt-sel = <0x2>;
+	qcom,cdc-micbias1-ext-cap;
+	qcom,cdc-micbias2-ext-cap;
+	qcom,cdc-micbias3-ext-cap;
+	qcom,cdc-micbias4-ext-cap;
 
 	qcom,cdc-slim-ifd = "taiko-slim-ifd";
 	qcom,cdc-slim-ifd-elemental-addr = [00 00 A0 00 17 02];
diff --git a/Documentation/devicetree/bindings/thermal/qpnp-temp-alarm.txt b/Documentation/devicetree/bindings/thermal/qpnp-temp-alarm.txt
new file mode 100644
index 0000000..19fbd3a
--- /dev/null
+++ b/Documentation/devicetree/bindings/thermal/qpnp-temp-alarm.txt
@@ -0,0 +1,66 @@
+Qualcomm QPNP Temperature Alarm
+
+QPNP temperature alarm peripherals are found inside of Qualcomm PMIC chips that
+utilize the MSM SPMI implementation.  These peripherals provide an interrupt
+signal and status register to identify high PMIC die temperature.
+
+Required properties:
+- compatible:      Must be "qcom,qpnp-temp-alarm".
+- reg:             Specifies the SPMI address and size for this temperature
+		    alarm device.
+- interrupts:      PMIC temperature alarm interrupt
+- label:           A string used as a descriptive name for this thermal device.
+		    This name should be 19 characters or less.
+
+Required structure:
+- A qcom,qpnp-temp-alarm node must be a child of an SPMI node that has specified
+   the spmi-slave-container property
+
+Optional properties:
+- qcom,channel-num:    VADC channel number associated PMIC DIE_TEMP thermistor.
+			If no channel is specified, then the die temperature
+			must be estimated based on the over temperature stage.
+- qcom,threshold-set:  Integer value which specifies which set of threshold
+			temperatures to use for the over temperature stages.
+			Possible values (x = {stage 1 threshold temperature,
+				stage 2 threshold temperature,
+				stage 3 threshold temperature}):
+			 0 = {105 C, 125 C, 145 C}
+			 1 = {110 C, 130 C, 150 C}
+			 2 = {115 C, 135 C, 155 C}
+			 3 = {120 C, 140 C, 160 C}
+- qcom,allow-override: Boolean which controls the ability of software to
+			override shutdowns.  If present, then software is
+			allowed to override automatic PMIC hardware stage 2 and
+			stage 3 over temperature shutdowns.  Otherwise, software
+			is not allowed to override automatic shutdown.
+- qcom,default-temp:   Specifies the default temperature in millicelcius to use
+			if no ADC channel is present to read the real time
+			temperature.
+
+Note, if a given optional qcom,* binding is not present, then the default
+hardware state for that feature will be maintained.
+
+Example:
+&spmi_bus {
+	#address-cells = <1>;
+	#size-cells = <0>;
+	interrupt-controller;
+	#interrupt-cells = <3>;
+
+	qcom,pm8941@0 {
+		spmi-slave-container;
+		reg = <0x0>;
+		#address-cells = <1>;
+		#size-cells = <1>;
+
+		qcom,temp-alarm@2400 {
+			compatible = "qcom,qpnp-temp-alarm";
+			reg = <0x2400 0x100>;
+			interrupts = <0x0 0x24 0x0>;
+			label = "pm8941_tz";
+			qcom,channel-num = <8>;
+			qcom,threshold-set = <0>;
+		};
+	};
+};
diff --git a/Documentation/devicetree/bindings/thermal/tsens.txt b/Documentation/devicetree/bindings/thermal/tsens.txt
index c683f58..0682cd1 100644
--- a/Documentation/devicetree/bindings/thermal/tsens.txt
+++ b/Documentation/devicetree/bindings/thermal/tsens.txt
@@ -17,10 +17,12 @@
 - reg : offset and length of the QFPROM registers used for storing
 	the calibration data for the individual sensors.
 - reg-names : resource names used for the physical address of the TSENS
-	      registers and the QFPROM efuse calibration address.
-	      Should be "tsens_physical" for physical address of the TSENS
-	      and "tsens_eeprom_physical" for physical address where calibration
-	      data is stored.
+	      registers, the QFPROM efuse primary calibration address region,
+	      Should be "tsens_physical" for physical address of the TSENS,
+	      "tsens_eeprom_physical" for physical address where primary
+	      calibration data is stored. This includes the backup
+	      calibration address region if TSENS calibration data is stored
+	      in the region.
 - interrupts : TSENS interrupt for cool/warm temperature threshold.
 - qcom,sensors : Total number of available Temperature sensors for TSENS.
 - qcom,slope : One point calibration characterized slope data for each
@@ -28,14 +30,20 @@
 	       as ADC code/DegC and the value is multipled by a factor
 	       of 1000.
 
+Optional properties:
+- qcom,calibration-less-mode : If present the pre-characterized data for offsets
+		are used else it defaults to use calibration data from QFPROM.
+
 Example:
 
 tsens@fc4a8000 {
 	compatible = "qcom,msm-tsens";
 	reg = <0xfc4a8000 0x2000>,
-	      <0xfc4b80d0 0x5>;
-	reg-names = "tsens_physical", "tsens_eeprom_physical";
+	      <0xfc4b8000 0x1000>;
+	reg-names = "tsens_physical",
+		    "tsens_eeprom_physical";
 	interrupts = <0 184 0>;
+	qcom,calibration-less-mode;
 	qcom,sensors = <11>;
 	qcom,slope = <1134 1122 1142 1123 1176 1176 1176 1186 1176
 			1176>;
diff --git a/Documentation/devicetree/bindings/tty/serial/msm_serial.txt b/Documentation/devicetree/bindings/tty/serial/msm_serial.txt
index 3b0426b..e784bfa 100644
--- a/Documentation/devicetree/bindings/tty/serial/msm_serial.txt
+++ b/Documentation/devicetree/bindings/tty/serial/msm_serial.txt
@@ -34,10 +34,15 @@
 - reg : offset and length of the register set for the device.
 - interrupts : should contain the uart interrupt.
 
+Optional properties:
+- cell-index: An integer specifying the line number of the UART device that
+  represents this HSL hardware instance.
+
 Example:
 
 	serial@19c400000 {
 		compatible = "qcom,msm-lsuart-v14"
 		reg = <0x19c40000 0x1000">;
 		interrupts = <195>;
+		cell-index = <0>;	// this device will be named ttyHSL0
 	};
diff --git a/Documentation/devicetree/bindings/usb/msm-hsusb.txt b/Documentation/devicetree/bindings/usb/msm-hsusb.txt
index 12fbfec..186a58d 100644
--- a/Documentation/devicetree/bindings/usb/msm-hsusb.txt
+++ b/Documentation/devicetree/bindings/usb/msm-hsusb.txt
@@ -22,10 +22,13 @@
             1 - PHY control
 	    2 - PMIC control
 	    3 - User control (via debugfs)
-- qcom,hsusb-otg-disable-reset: It present then core is RESET only during
-	    init, otherwise core is RESET for every cable disconnect as well
 
 Optional properties :
+- qcom,hsusb-otg-disable-reset: If present then core is RESET only during
+	    init, otherwise core is RESET for every cable disconnect as well
+- qcom,hsusb-otg-pnoc-errata-fix: If present then workaround for PNOC
+	    performance issue is applied which requires changing the mem-type
+	    attribute via VMIDMT.
 - qcom,hsusb-otg-default-mode: The default USB mode after boot-up.
   Applicable only when OTG is controlled by user. Can be one of
             0 - None. Low power mode
@@ -56,6 +59,7 @@
 		qcom,hsusb-otg-mode = <1>;
 		qcom,hsusb-otg-otg-control = <1>;
 		qcom,hsusb-otg-disable-reset;
+		qcom,hsusb-otg-pnoc-errata-fix;
 		qcom,hsusb-otg-default-mode = <2>;
 		qcom,hsusb-otg-phy-init-seq = <0x01 0x90 0xffffffff>;
 		qcom,hsusb-otg-power-budget = <500>;
diff --git a/Documentation/devicetree/bindings/usb/msm-ssusb.txt b/Documentation/devicetree/bindings/usb/msm-ssusb.txt
index 9cc9e6e..99274d5 100644
--- a/Documentation/devicetree/bindings/usb/msm-ssusb.txt
+++ b/Documentation/devicetree/bindings/usb/msm-ssusb.txt
@@ -11,7 +11,7 @@
 	"otg_irq" : Interrupt for DWC3 core's OTG Events
 - <supply-name>-supply: phandle to the regulator device tree node
   Required "supply-name" examples are "SSUSB_VDDCX", "SSUSB_1p8",
-  "HSUSB_VDDCX", "HSUSB_1p8", "HSUSB_3p3".
+  "HSUSB_VDDCX", "HSUSB_1p8", "HSUSB_3p3" and "vbus_dwc3".
 - qcom,dwc-usb3-msm-dbm-eps: Number of endpoints avaliable for
   the DBM (Device Bus Manager). The DBM is HW unit which is part of
   the MSM USB3.0 core (which also includes the Synopsys DesignWare
@@ -25,19 +25,25 @@
     - qcom,msm_bus,active_only
     - qcom,msm_bus,num_paths
     - qcom,msm_bus,vectors
+- interrupt-names : Optional interrupt resource entries are:
+    "hs_phy_irq" : Interrupt from HSPHY for asynchronous events in LPM.
+	This is not used if wakeup events are received externally (e.g. PMIC)
+- qcom,dwc-usb3-msm-otg-capability: If present then depends on PMIC
+  for VBUS notifications, otherwise depends on PHY.
 
 Example MSM USB3.0 controller device node :
 	usb@f9200000 {
 		compatible = "qcom,dwc-usb3-msm";
 		reg = <0xF9200000 0xFA000>,
 		      <0xFD4AB000 0x4>;
-		interrupts = <0 131 0 0 179 0>;
-		interrupt-names = "irq", "otg_irq";
+		interrupts = <0 131 0>, <0 179 0>, <0 133 0>;
+		interrupt-names = "irq", "otg_irq", "hs_phy_irq";
 		SSUSB_VDDCX-supply = <&pm8841_s2>;
 		SSUSB_1p8-supply = <&pm8941_l6>;
 		HSUSB_VDDCX-supply = <&pm8841_s2>;
 		HSUSB_1p8-supply = <&pm8941_l6>;
 		HSUSB_3p3-supply = <&pm8941_l24>;
+		vbus_dwc3-supply = <&pm8941_mvs1>;
 		qcom,dwc-usb3-msm-dbm-eps = <4>
 
 		qcom,msm_bus,name = "usb3";
diff --git a/Documentation/mmc/mmc-dev-attrs.txt b/Documentation/mmc/mmc-dev-attrs.txt
index 22ae844..7dde34f 100644
--- a/Documentation/mmc/mmc-dev-attrs.txt
+++ b/Documentation/mmc/mmc-dev-attrs.txt
@@ -8,6 +8,32 @@
 
 	force_ro		Enforce read-only access even if write protect switch is off.
 
+	num_wr_reqs_to_start_packing 	This attribute is used to determine
+	the trigger for activating the write packing, in case the write
+	packing control feature is enabled.
+
+	When the MMC manages to reach a point where num_wr_reqs_to_start_packing
+	write requests could be packed, it enables the write packing feature.
+	This allows us to start the write packing only when it is beneficial
+	and has minimum affect on the read latency.
+
+	The number of potential packed requests that will trigger the packing
+	can be configured via sysfs by writing the required value to:
+	/sys/block/<block_dev_name>/num_wr_reqs_to_start_packing.
+
+	The default value of num_wr_reqs_to_start_packing was determined by
+	running parallel lmdd write and lmdd read operations and calculating
+	the max number of packed writes requests.
+
+	min_sectors_to_check_bkops_status	This attribute is used to
+	determine whether the status bit that indicates the need for BKOPS
+	should be checked. The value is stored in this attribute represents
+	the minimum number of sectors that needs to be changed in the device
+	(written or discarded) in order to require the status-bit of BKOPS
+	to be checked. The value can modified via sysfs by writing the
+	required value to:
+	/sys/block/<block_dev_name>/min_sectors_to_check_bkops_status
+
 SD and MMC Device Attributes
 ============================
 
diff --git a/arch/arm/boot/dts/mpq8092-ion.dtsi b/arch/arm/boot/dts/mpq8092-ion.dtsi
new file mode 100644
index 0000000..2cd2f7b
--- /dev/null
+++ b/arch/arm/boot/dts/mpq8092-ion.dtsi
@@ -0,0 +1,77 @@
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+/ {
+	qcom,ion {
+		compatible = "qcom,msm-ion";
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		qcom,ion-heap@30 { /* SYSTEM HEAP */
+			reg = <30>;
+		};
+
+		qcom,ion-heap@8 { /* CP_MM HEAP */
+			compatible = "qcom,msm-ion-reserve";
+			reg = <8>;
+			qcom,heap-align = <0x1000>;
+			qcom,memory-reservation-type = "EBI1"; /* reserve EBI memory */
+			qcom,memory-reservation-size = <0x7800000>;
+		};
+
+		qcom,ion-heap@29 { /* FIRMWARE HEAP */
+			compatible = "qcom,msm-ion-reserve";
+			reg = <29>;
+			qcom,heap-align = <0x20000>;
+			qcom,heap-adjacent = <8>;
+			qcom,memory-reservation-type = "EBI1"; /* reserve EBI memory */
+			qcom,memory-reservation-size = <0xA00000>;
+		};
+
+		qcom,ion-heap@12 { /* MFC HEAP */
+			compatible = "qcom,msm-ion-reserve";
+			reg = <12>;
+			qcom,heap-align = <0x1000>;
+			qcom,memory-reservation-type = "EBI1"; /* reserve EBI memory */
+			qcom,memory-reservation-size = <0x2000>;
+		};
+
+		qcom,ion-heap@24 { /* SF HEAP */
+			compatible = "qcom,msm-ion-reserve";
+			reg = <24>;
+			qcom,heap-align = <0x1000>;
+			qcom,memory-reservation-type = "EBI1"; /* reserve EBI memory */
+			qcom,memory-reservation-size = <0x2800000>;
+		};
+
+		qcom,ion-heap@25 { /* IOMMU HEAP */
+			reg = <25>;
+		};
+
+		qcom,ion-heap@27 { /* QSECOM HEAP */
+			compatible = "qcom,msm-ion-reserve";
+			reg = <27>;
+			qcom,heap-align = <0x1000>;
+			qcom,memory-reservation-type = "EBI1"; /* reserve EBI memory */
+			qcom,memory-reservation-size = <0x600000>;
+		};
+
+		qcom,ion-heap@28 { /* AUDIO HEAP */
+			compatible = "qcom,msm-ion-reserve";
+			reg = <28>;
+			qcom,heap-align = <0x1000>;
+			qcom,memory-reservation-type = "EBI1"; /* reserve EBI memory */
+			qcom,memory-reservation-size = <0x2B4000>;
+		};
+	};
+};
+
diff --git a/arch/arm/boot/dts/mpq8092-regulator.dtsi b/arch/arm/boot/dts/mpq8092-regulator.dtsi
new file mode 100644
index 0000000..b724a3d
--- /dev/null
+++ b/arch/arm/boot/dts/mpq8092-regulator.dtsi
@@ -0,0 +1,290 @@
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+
+/* QPNP controlled regulators: */
+
+&spmi_bus {
+
+	qcom,pm8644@1 {
+
+		pm8644_s3: regulator@1a00 {
+			regulator-min-microvolt = <1350000>;
+			regulator-max-microvolt = <1350000>;
+			qcom,enable-time = <500>;
+			qcom,pull-down-enable = <1>;
+			regulator-always-on;
+			qcom,system-load = <100000>;
+			status = "okay";
+		};
+
+		pm8644_s4: regulator@1d00 {
+			regulator-min-microvolt = <1800000>;
+			regulator-max-microvolt = <1800000>;
+			qcom,enable-time = <500>;
+			qcom,pull-down-enable = <1>;
+			regulator-always-on;
+			qcom,system-load = <100000>;
+			status = "okay";
+		};
+
+		pm8644_s5: regulator@2000 {
+			regulator-min-microvolt = <2150000>;
+			regulator-max-microvolt = <2150000>;
+			qcom,enable-time = <500>;
+			qcom,pull-down-enable = <1>;
+			status = "okay";
+		};
+
+		pm8644_s6: regulator@2300 {
+			regulator-min-microvolt = <900000>;
+			regulator-max-microvolt = <900000>;
+			qcom,enable-time = <500>;
+			qcom,pull-down-enable = <1>;
+			status = "okay";
+		};
+
+		pm8644_s7: regulator@2600 {
+			regulator-min-microvolt = <900000>;
+			regulator-max-microvolt = <900000>;
+			qcom,enable-time = <500>;
+			qcom,pull-down-enable = <1>;
+			status = "okay";
+		};
+
+		pm8644_s8: regulator@2900 {
+			regulator-min-microvolt = <900000>;
+			regulator-max-microvolt = <900000>;
+			qcom,enable-time = <500>;
+			qcom,pull-down-enable = <1>;
+			status = "okay";
+		};
+
+		pm8644_l1: regulator@4000 {
+			parent-supply = <&pm8644_s3>;
+			regulator-min-microvolt = <1225000>;
+			regulator-max-microvolt = <1225000>;
+			qcom,enable-time = <200>;
+			qcom,pull-down-enable = <1>;
+			regulator-always-on;
+			qcom,system-load = <10000>;
+			status = "okay";
+		};
+
+		pm8644_l2: regulator@4100 {
+			parent-supply = <&pm8644_s3>;
+			regulator-min-microvolt = <900000>;
+			regulator-max-microvolt = <900000>;
+			qcom,enable-time = <200>;
+			qcom,pull-down-enable = <1>;
+			status = "okay";
+		};
+
+		pm8644_l3: regulator@4200 {
+			parent-supply = <&pm8644_s3>;
+			regulator-min-microvolt = <1200000>;
+			regulator-max-microvolt = <1200000>;
+			qcom,enable-time = <200>;
+			qcom,pull-down-enable = <1>;
+			status = "okay";
+		};
+
+		pm8644_l4: regulator@4300 {
+			parent-supply = <&pm8644_s3>;
+			regulator-min-microvolt = <1000000>;
+			regulator-max-microvolt = <1000000>;
+			qcom,enable-time = <200>;
+			qcom,pull-down-enable = <1>;
+			status = "okay";
+		};
+
+		pm8644_l6: regulator@4500 {
+			parent-supply = <&pm8644_s5>;
+			regulator-min-microvolt = <1800000>;
+			regulator-max-microvolt = <1800000>;
+			qcom,enable-time = <200>;
+			qcom,pull-down-enable = <1>;
+			status = "okay";
+		};
+
+		pm8644_l8: regulator@4700 {
+			regulator-min-microvolt = <1800000>;
+			regulator-max-microvolt = <1800000>;
+			qcom,enable-time = <200>;
+			qcom,pull-down-enable = <1>;
+			status = "okay";
+		};
+
+		pm8644_l9: regulator@4800 {
+			regulator-min-microvolt = <1800000>;
+			regulator-max-microvolt = <2950000>;
+			qcom,enable-time = <200>;
+			qcom,pull-down-enable = <1>;
+			status = "okay";
+		};
+
+		pm8644_l10: regulator@4900 {
+			regulator-min-microvolt = <2000000>;
+			regulator-max-microvolt = <2000000>;
+			qcom,enable-time = <200>;
+			qcom,pull-down-enable = <1>;
+			status = "okay";
+		};
+
+		pm8644_l11: regulator@4a00 {
+			parent-supply = <&pm8644_s3>;
+			regulator-min-microvolt = <1300000>;
+			regulator-max-microvolt = <1300000>;
+			qcom,enable-time = <200>;
+			qcom,pull-down-enable = <1>;
+			status = "okay";
+		};
+
+		pm8644_l12: regulator@4b00 {
+			parent-supply = <&pm8644_s5>;
+			regulator-min-microvolt = <1800000>;
+			regulator-max-microvolt = <1800000>;
+			qcom,enable-time = <200>;
+			qcom,pull-down-enable = <1>;
+			status = "okay";
+		};
+
+		pm8644_l13: regulator@4c00 {
+			regulator-min-microvolt = <1800000>;
+			regulator-max-microvolt = <2950000>;
+			qcom,enable-time = <200>;
+			qcom,pull-down-enable = <1>;
+			status = "okay";
+		};
+
+		pm8644_l14: regulator@4d00 {
+			parent-supply = <&pm8644_s5>;
+			regulator-min-microvolt = <1000000>;
+			regulator-max-microvolt = <1000000>;
+			qcom,enable-time = <200>;
+			qcom,pull-down-enable = <1>;
+			status = "okay";
+		};
+
+		pm8644_l15: regulator@4e00 {
+			parent-supply = <&pm8644_s5>;
+			regulator-min-microvolt = <1800000>;
+			regulator-max-microvolt = <1800000>;
+			qcom,enable-time = <200>;
+			qcom,pull-down-enable = <1>;
+			status = "okay";
+		};
+
+		pm8644_l16: regulator@4f00 {
+			parent-supply = <&pm8644_s4>;
+			regulator-min-microvolt = <750000>;
+			regulator-max-microvolt = <750000>;
+			qcom,enable-time = <200>;
+			qcom,pull-down-enable = <1>;
+			status = "okay";
+		};
+
+		pm8644_l17: regulator@5000 {
+			regulator-min-microvolt = <3150000>;
+			regulator-max-microvolt = <3150000>;
+			qcom,enable-time = <200>;
+			qcom,pull-down-enable = <1>;
+			regulator-always-on;
+			qcom,system-load = <100000>;
+			status = "okay";
+		};
+
+		pm8644_l18: regulator@5100 {
+			regulator-min-microvolt = <2850000>;
+			regulator-max-microvolt = <2850000>;
+			qcom,enable-time = <200>;
+			qcom,pull-down-enable = <1>;
+			status = "okay";
+		};
+
+		pm8644_l19: regulator@5200 {
+			parent-supply = <&pm8644_s4>;
+			regulator-min-microvolt = <1500000>;
+			regulator-max-microvolt = <1500000>;
+			qcom,enable-time = <200>;
+			qcom,pull-down-enable = <1>;
+			status = "okay";
+		};
+
+		pm8644_l20: regulator@5300 {
+			regulator-min-microvolt = <2950000>;
+			regulator-max-microvolt = <2950000>;
+			qcom,enable-time = <200>;
+			qcom,pull-down-enable = <1>;
+			status = "okay";
+		};
+
+		pm8644_l21: regulator@5400 {
+			regulator-min-microvolt = <2950000>;
+			regulator-max-microvolt = <2950000>;
+			qcom,enable-time = <200>;
+			qcom,pull-down-enable = <1>;
+			status = "okay";
+		};
+
+		pm8644_l22: regulator@5500 {
+			regulator-min-microvolt = <2500000>;
+			regulator-max-microvolt = <2500000>;
+			qcom,enable-time = <200>;
+			qcom,pull-down-enable = <1>;
+			status = "okay";
+		};
+
+		pm8644_l23: regulator@5600 {
+			regulator-min-microvolt = <3000000>;
+			regulator-max-microvolt = <3000000>;
+			qcom,enable-time = <200>;
+			qcom,pull-down-enable = <1>;
+			status = "okay";
+		};
+
+		pm8644_l24: regulator@5700 {
+			regulator-min-microvolt = <3075000>;
+			regulator-max-microvolt = <3075000>;
+			qcom,enable-time = <200>;
+			qcom,pull-down-enable = <1>;
+			status = "okay";
+		};
+
+		pm8644_lvs1: regulator@8000 {
+			parent-supply = <&pm8644_s4>;
+			qcom,enable-time = <200>;
+			qcom,pull-down-enable = <1>;
+			status = "okay";
+		};
+
+		pm8644_lvs2: regulator@8100 {
+			parent-supply = <&pm8644_s4>;
+			qcom,enable-time = <200>;
+			qcom,pull-down-enable = <1>;
+			status = "okay";
+		};
+
+		pm8644_mvs1: regulator@8200 {
+			qcom,enable-time = <200>;
+			qcom,pull-down-enable = <1>;
+			status = "okay";
+		};
+
+		pm8644_mvs2: regulator@8300 {
+			qcom,enable-time = <200>;
+			qcom,pull-down-enable = <1>;
+			status = "okay";
+		};
+	};
+};
+
diff --git a/arch/arm/boot/dts/mpq8092-sim.dts b/arch/arm/boot/dts/mpq8092-sim.dts
index ac984a1..0cbfa33 100644
--- a/arch/arm/boot/dts/mpq8092-sim.dts
+++ b/arch/arm/boot/dts/mpq8092-sim.dts
@@ -26,6 +26,155 @@
 	serial@f995e000 {
 		status = "ok";
 	};
-
 };
 
+&pm8644_gpios {
+	gpio@c000 { /* GPIO 1 */
+	};
+
+	gpio@c100 { /* GPIO 2 */
+	};
+
+	gpio@c200 { /* GPIO 3 */
+	};
+
+	gpio@c300 { /* GPIO 4 */
+	};
+
+	gpio@c400 { /* GPIO 5 */
+	};
+
+	gpio@c500 { /* GPIO 6 */
+	};
+
+	gpio@c600 { /* GPIO 7 */
+	};
+
+	gpio@c700 { /* GPIO 8 */
+	};
+
+	gpio@c800 { /* GPIO 9 */
+	};
+
+	gpio@c900 { /* GPIO 10 */
+	};
+
+	gpio@ca00 { /* GPIO 11 */
+	};
+
+	gpio@cb00 { /* GPIO 12 */
+	};
+
+	gpio@cc00 { /* GPIO 13 */
+	};
+
+	gpio@cd00 { /* GPIO 14 */
+	};
+
+	gpio@ce00 { /* GPIO 15 */
+	};
+
+	gpio@cf00 { /* GPIO 16 */
+	};
+
+	gpio@d000 { /* GPIO 17 */
+	};
+
+	gpio@d100 { /* GPIO 18 */
+	};
+
+	gpio@d200 { /* GPIO 19 */
+	};
+
+	gpio@d300 { /* GPIO 20 */
+	};
+
+	gpio@d400 { /* GPIO 21 */
+	};
+
+	gpio@d500 { /* GPIO 22 */
+	};
+
+	gpio@d600 { /* GPIO 23 */
+	};
+
+	gpio@d700 { /* GPIO 24 */
+	};
+
+	gpio@d800 { /* GPIO 25 */
+	};
+
+	gpio@d900 { /* GPIO 26 */
+	};
+
+	gpio@da00 { /* GPIO 27 */
+	};
+
+	gpio@db00 { /* GPIO 28 */
+	};
+
+	gpio@dc00 { /* GPIO 29 */
+	};
+
+	gpio@dd00 { /* GPIO 30 */
+	};
+
+	gpio@de00 { /* GPIO 31 */
+	};
+
+	gpio@df00 { /* GPIO 32 */
+	};
+
+	gpio@e000 { /* GPIO 33 */
+	};
+
+	gpio@e100 { /* GPIO 34 */
+	};
+
+	gpio@e200 { /* GPIO 35 */
+	};
+
+	gpio@e300 { /* GPIO 36 */
+	};
+
+	gpio@e400 { /* GPIO 37 */
+	};
+
+	gpio@e500 { /* GPIO 38 */
+	};
+
+	gpio@e600 { /* GPIO 39 */
+	};
+
+	gpio@e700 { /* GPIO 40 */
+	};
+
+	gpio@e800 { /* GPIO 41 */
+	};
+
+	gpio@e900 { /* GPIO 42 */
+	};
+
+	gpio@ea00 { /* GPIO 43 */
+	};
+};
+
+&pm8644_mpps {
+	mpp@a000 { /* MPP 1 */
+	};
+
+	mpp@a100 { /* MPP 2 */
+	};
+
+	mpp@a200 { /* MPP 3 */
+	};
+
+	mpp@a300 { /* MPP 4 */
+	};
+
+	mpp@a400 { /* MPP5 */
+	};
+
+	mpp@a500 { /* MPP 6 */
+	};
+};
diff --git a/arch/arm/boot/dts/mpq8092.dtsi b/arch/arm/boot/dts/mpq8092.dtsi
index 9b51ceb..7398b25 100644
--- a/arch/arm/boot/dts/mpq8092.dtsi
+++ b/arch/arm/boot/dts/mpq8092.dtsi
@@ -13,6 +13,7 @@
 /include/ "skeleton.dtsi"
 /include/ "mpq8092-iommu.dtsi"
 /include/ "msm-gdsc.dtsi"
+/include/ "mpq8092-ion.dtsi"
 
 / {
 	model = "Qualcomm MPQ8092";
@@ -55,4 +56,221 @@
 		interrupts = <0 114 0>;
 		status = "disabled";
 	};
+
+	spmi_bus: qcom,spmi@fc4c0000 {
+		cell-index = <0>;
+		compatible = "qcom,spmi-pmic-arb";
+		reg = <0xfc4cf000 0x1000>,
+		      <0Xfc4cb000 0x1000>;
+		/* 190,ee0_krait_hlos_spmi_periph_irq */
+		/* 187,channel_0_krait_hlos_trans_done_irq */
+		interrupts = <0 190 0 0 187 0>;
+		qcom,not-wakeup;
+		qcom,pmic-arb-ee = <0>;
+		qcom,pmic-arb-channel = <0>;
+		qcom,pmic-arb-ppid-map = <0x00100000>, /* PM8644_0 */
+				 <0x10100001>, /* PM8644_1 */
+				 <0x00500002>, /* INTERRUPT */
+				 <0x00800003>, /* PON0 */
+				 <0x03000004>, /* ADC_1 */
+				 <0x03100005>, /* ADC_2 */
+				 <0x03200006>, /* ADC_3 */
+				 <0x03300007>, /* ADC_4 */
+				 <0x03400008>, /* ADC_5 */
+				 <0x03500009>, /* ADC_6 */
+				 <0x0360000a>, /* ADC_7 */
+				 <0x0370000b>, /* ADC_8 */
+				 <0x0500000c>, /* SHARED_XO */
+				 <0x0510000d>, /* BB_CLK1 */
+				 <0x0520000e>, /* BB_CLK2 */
+				 <0x05a0000f>, /* SLEEP_CLK */
+				 <0x06000010>, /* RTC_RW */
+				 <0x06100011>, /* RTC_ALARM */
+				 <0x07000012>, /* PBS_CORE */
+				 <0x07100013>, /* PBS_CLIENT_1 */
+				 <0x07200014>, /* PBS_CLIENT_2 */
+				 <0x07300015>, /* PBS_CLIENT_3 */
+				 <0x07400016>, /* PBS_CLIENT_4 */
+				 <0x07500017>, /* PBS_CLIENT_5 */
+				 <0x07600018>, /* PBS_CLIENT_6 */
+				 <0x07700019>, /* PBS_CLIENT_7 */
+				 <0x0780001a>, /* PBS_CLIENT_8 */
+				 <0x0790001b>, /* PBS_CLIENT_9 */
+				 <0x07a0001c>, /* PBS_CLIENT_10 */
+				 <0x07b0001d>, /* PBS_CLIENT_11 */
+				 <0x07c0001e>, /* PBS_CLIENT_12 */
+				 <0x07d0001f>, /* PBS_CLIENT_13 */
+				 <0x07e00020>, /* PBS_CLIENT_14 */
+				 <0x07f00021>, /* PBS_CLIENT_15 */
+				 <0x08000022>, /* PBS_CLIENT_16 */
+				 <0x0a000023>, /* MPP_1 */
+				 <0x0a100024>, /* MPP_2 */
+				 <0x0a200025>, /* MPP_3 */
+				 <0x0a300026>, /* MPP_4 */
+				 <0x0a400027>, /* MPP_5 */
+				 <0x0a500028>, /* MPP_6 */
+				 <0x0c000029>, /* PM8644_GPIO_1 */
+				 <0x0c10002a>, /* PM8644_GPIO_2 */
+				 <0x0c20002b>, /* PM8644_GPIO_3 */
+				 <0x0c30002c>, /* PM8644_GPIO_4 */
+				 <0x0c40002d>, /* PM8644_GPIO_5 */
+				 <0x0c50002e>, /* PM8644_GPIO_6 */
+				 <0x0c60002f>, /* PM8644_GPIO_7 */
+				 <0x0c700030>, /* PM8644_GPIO_8 */
+				 <0x0c800031>, /* PM8644_GPIO_9 */
+				 <0x0c900032>, /* PM8644_GPIO_10 */
+				 <0x0ca00033>, /* PM8644_GPIO_11 */
+				 <0x0cb00034>, /* PM8644_GPIO_12 */
+				 <0x0cc00035>, /* PM8644_GPIO_13 */
+				 <0x0cd00036>, /* PM8644_GPIO_14 */
+				 <0x0ce00037>, /* PM8644_GPIO_15 */
+				 <0x0cf00038>, /* PM8644_GPIO_16 */
+				 <0x0d000039>, /* PM8644_GPIO_17 */
+				 <0x0d10003a>, /* PM8644_GPIO_18 */
+				 <0x0d20003b>, /* PM8644_GPIO_19 */
+				 <0x0d30003c>, /* PM8644_GPIO_20 */
+				 <0x0d40003d>, /* PM8644_GPIO_21 */
+				 <0x0d50003e>, /* PM8644_GPIO_22 */
+				 <0x0d60003f>, /* PM8644_GPIO_23 */
+				 <0x0d700040>, /* PM8644_GPIO_24 */
+				 <0x0d800041>, /* PM8644_GPIO_25 */
+				 <0x0d900042>, /* PM8644_GPIO_26 */
+				 <0x0da00043>, /* PM8644_GPIO_27 */
+				 <0x0db00044>, /* PM8644_GPIO_28 */
+				 <0x0dc00045>, /* PM8644_GPIO_29 */
+				 <0x0dd00046>, /* PM8644_GPIO_30 */
+				 <0x0de00047>, /* PM8644_GPIO_31 */
+				 <0x0df00048>, /* PM8644_GPIO_32 */
+				 <0x0e000049>, /* PM8644_GPIO_33 */
+				 <0x0e10004a>, /* PM8644_GPIO_34 */
+				 <0x0e20004b>, /* PM8644_GPIO_35 */
+				 <0x0e30004c>, /* PM8644_GPIO_36 */
+				 <0x0e40004d>, /* PM8644_GPIO_37 */
+				 <0x0e50004e>, /* PM8644_GPIO_38 */
+				 <0x0e60004f>, /* PM8644_GPIO_39 */
+				 <0x0e700050>, /* PM8644_GPIO_40 */
+				 <0x0e800051>, /* PM8644_GPIO_41 */
+				 <0x0e900052>, /* PM8644_GPIO_42 */
+				 <0x0ea00053>, /* PM8644_GPIO_43 */
+				 <0x11000054>, /* BUCK_CMN_1 */
+				 <0x11100055>, /* BUCK_CMN_2 */
+				 <0x11200056>, /* BUCK_CMN_3 */
+				 <0x11400057>, /* PM8644_SMPS1 */
+				 <0x11500058>, /* SMPS_1_PS1 */
+				 <0x11600059>, /* BUCK_FREQ_1 */
+				 <0x1170005a>, /* PM8644_SMPS2 */
+				 <0x1180005b>, /* SMPS_2_PS1 */
+				 <0x1190005c>, /* BUCK_FREQ_2 */
+				 <0x11a0005d>, /* PM8644_SMPS3 */
+				 <0x11b0005e>, /* SMPS_3_PS1 */
+				 <0x11c0005f>, /* BUCK_FREQ_3 */
+				 <0x11d00060>, /* PM8644_SMPS4 */
+				 <0x11e00061>, /* SMPS_4_PS1 */
+				 <0x11f00062>, /* PM8644_BUCK_FREQ_4 */
+				 <0x12000063>, /* PM8644_SMPS5 */
+				 <0x12100064>, /* FTPS1_5 */
+				 <0x12200065>, /* PM8644_BUCK_FREQ_5 */
+				 <0x12300066>, /* PM8644_SMPS6 */
+				 <0x12400067>, /* FTPS1_6 */
+				 <0x12500068>, /* PM8644_BUCK_FREQ_6 */
+				 <0x12600069>, /* PM8644_SMPS7 */
+				 <0x1270006a>, /* FTPS1_7 */
+				 <0x1280006b>, /* PM8644_BUCK_FREQ_7 */
+				 <0x1290006c>, /* PM8644_SMPS8 */
+				 <0x12a0006d>, /* FTPS1_8 */
+				 <0x12b0006e>, /* PM8644_BUCK_FREQ_8 */
+				 <0x12c0006f>, /* PM8644_SMPS9 */
+				 <0x12d00070>, /* FTPS1_9 */
+				 <0x12e00071>, /* PM8644_BUCK_FREQ_9 */
+				 <0x12f00072>, /* PM8644_SMPS10 */
+				 <0x13000073>, /* FTPS1_10 */
+				 <0x13100074>, /* PM8644_BUCK_FREQ_10 */
+				 <0x13200075>, /* PM8644_SMPS11 */
+				 <0x13300076>, /* FTPS1_11 */
+				 <0x13400077>, /* BUCK_FREQ_11 */
+				 <0x14000078>, /* PM8644_LDO_1 */
+				 <0x14100079>, /* PM8644_LDO_2 */
+				 <0x1420007a>, /* PM8644_LDO_3 */
+				 <0x1430007b>, /* PM8644_LDO_4 */
+				 <0x1440007c>, /* PM8644_LDO_5 */
+				 <0x1450007d>, /* PM8644_LDO_6 */
+				 <0x1460007e>, /* PM8644_LDO_7 */
+				 <0x1470007f>, /* PM8644_LDO_8 */
+				 <0x14800080>, /* PM8644_LDO_9 */
+				 <0x14900081>, /* PM8644_LDO_10 */
+				 <0x14a00082>, /* PM8644_LDO_11 */
+				 <0x14b00083>, /* PM8644_LDO_12 */
+				 <0x14c00084>, /* PM8644_LDO_13 */
+				 <0x14d00085>, /* PM8644_LDO_14 */
+				 <0x14e00086>, /* PM8644_LDO_15 */
+				 <0x14f00087>, /* PM8644_LDO_16 */
+				 <0x15000088>, /* PM8644_LDO_17 */
+				 <0x15100089>, /* PM8644_LDO_18 */
+				 <0x1520008a>, /* PM8644_LDO_19 */
+				 <0x1530008b>, /* PM8644_LDO_20 */
+				 <0x1540008c>, /* PM8644_LDO_21 */
+				 <0x1550008d>, /* PM8644_LDO_22 */
+				 <0x1560008e>, /* PM8644_LDO_23 */
+				 <0x1570008f>, /* PM8644_LDO_24 */
+				 <0x15800090>, /* PM8644_LDO_25 */
+				 <0x18000091>, /* PM8644_LVS_1 */
+				 <0x18100092>, /* PM8644_LVS_2 */
+				 <0x18200093>, /* PM8644_OTG */
+				 <0x18300094>, /* PM8644_HDMI */
+				 <0x1a800095>, /* KEYPAD */
+				 <0x1b000096>, /* LPG_LUT */
+				 <0x1b100097>, /* LPG_CHAN_1 */
+				 <0x1b200098>, /* LPG_CHAN_2 */
+				 <0x1b300099>, /* LPG_CHAN_3 */
+				 <0x1b40009a>, /* LPG_CHAN_4 */
+				 <0x1b50009b>, /* LPG_CHAN_5 */
+				 <0x1b60009c>, /* LPG_CHAN_6 */
+				 <0x1b70009d>, /* LPG_CHAN_7 */
+				 <0x1b80009e>, /* LPG_CHAN_8 */
+				 <0x1bc0009f>; /* LPG_PWM */
+	};
+
+	sdcc1: qcom,sdcc@f9824000 {
+		cell-index = <1>; /* SDC1 eMMC slot */
+		compatible = "qcom,msm-sdcc";
+		reg = <0xf9824000 0x800>;
+		reg-names = "core_mem";
+		interrupts = <0 123 0>;
+		interrupt-names = "core_irq";
+
+		qcom,sdcc-pad-pull-on = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
+		qcom,sdcc-pad-pull-off = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
+		qcom,sdcc-pad-drv-on = <0x7 0x4 0x4>; /* 16mA, 10mA, 10mA */
+		qcom,sdcc-pad-drv-off = <0x0 0x0 0x0>; /* 2mA, 2mA, 2mA */
+
+		qcom,sdcc-clk-rates = <400000 25000000 50000000 100000000 200000000>;
+		qcom,sdcc-sup-voltages = <2950 2950>;
+		qcom,sdcc-bus-width = <8>;
+		qcom,sdcc-nonremovable;
+		qcom,sdcc-bus-speed-mode = "HS200_1p8v", "DDR_1p8v";
+	};
+
+	sdcc2: qcom,sdcc@f98a4000 {
+		cell-index = <2>; /* SDC2 SD card slot */
+		compatible = "qcom,msm-sdcc";
+		reg = <0xf98a4000 0x800>;
+		reg-names = "core_mem";
+		interrupts = <0 125 0>;
+		interrupt-names = "core_irq";
+
+		qcom,sdcc-pad-pull-on = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
+		qcom,sdcc-pad-pull-off = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
+		qcom,sdcc-pad-drv-on = <0x7 0x4 0x4>; /* 16mA, 10mA, 10mA */
+		qcom,sdcc-pad-drv-off = <0x0 0x0 0x0>; /* 2mA, 2mA, 2mA */
+
+		qcom,sdcc-clk-rates = <400000 25000000 50000000 100000000 200000000>;
+		qcom,sdcc-sup-voltages = <2950 2950>;
+		qcom,sdcc-bus-width = <4>;
+		qcom,sdcc-xpc;
+		qcom,sdcc-bus-speed-mode = "SDR12", "SDR25", "SDR50", "DDR50", "SDR104";
+		qcom,sdcc-current-limit = <800>;
+	};
 };
+
+/include/ "msm-pm8644.dtsi"
+/include/ "mpq8092-regulator.dtsi"
diff --git a/arch/arm/boot/dts/msm-iommu.dtsi b/arch/arm/boot/dts/msm-iommu.dtsi
index e907de8..839199a 100755
--- a/arch/arm/boot/dts/msm-iommu.dtsi
+++ b/arch/arm/boot/dts/msm-iommu.dtsi
@@ -18,9 +18,40 @@
 		ranges;
 		reg = <0xfda64000 0x10000>;
 		vdd-supply = <&gdsc_jpeg>;
-		qcom,iommu-smt-size = <16>;
 		status = "disabled";
 
+		qcom,iommu-bfb-regs =  <0x204c
+					0x2050
+					0x2514
+					0x2540
+					0x256c
+					0x2314
+					0x2394
+					0x2414
+					0x20ac
+					0x215c
+					0x220c
+					0x2008
+					0x200c
+					0x2010
+					0x2014>;
+
+		qcom,iommu-bfb-data =  <0xffffffff
+					0xffffffff
+					0x4
+					0x4
+					0x0
+					0x0
+					0x10
+					0x50
+					0x0
+					0x10
+					0x20
+					0x0
+					0x0
+					0x0
+					0x0>;
+
 		qcom,iommu-ctx@fda6c000 {
 			reg = <0xfda6c000 0x1000>;
 			interrupts = <0 70 0>;
@@ -50,9 +81,47 @@
 		ranges;
 		reg = <0xfd928000 0x10000>;
 		vdd-supply = <&gdsc_mdss>;
-		qcom,iommu-smt-size = <16>;
+		qcom,iommu-secure-id = <1>;
 		status = "disabled";
 
+		qcom,iommu-bfb-regs =  <0x204c
+					0x2050
+					0x2514
+					0x2540
+					0x256c
+					0x20ac
+					0x215c
+					0x220c
+					0x2314
+					0x2394
+					0x2414
+					0x2008
+					0x200c
+					0x2010
+					0x2014
+					0x2018
+					0x201c
+					0x2020>;
+
+		qcom,iommu-bfb-data =  <0xffffffff
+					0xffffffff
+					0x00000004
+					0x00000010
+					0x00000000
+					0x00000000
+					0x00000034
+					0x00000044
+					0x0
+					0x34
+					0x74
+					0x0
+					0x0
+					0x0
+					0x0
+					0x0
+					0x0
+					0x0>;
+
 		qcom,iommu-ctx@fd930000 {
 			reg = <0xfd930000 0x1000>;
 			interrupts = <0 47 0>;
@@ -75,10 +144,60 @@
 		ranges;
 		reg = <0xfdc84000 0x10000>;
 		vdd-supply = <&gdsc_venus>;
-		qcom,iommu-smt-size = <16>;
+		qcom,iommu-secure-id = <0>;
 		qcom,needs-alt-core-clk;
 		status = "disabled";
 
+		qcom,iommu-bfb-regs =  <0x204c
+					0x2050
+					0x2514
+					0x2540
+					0x256c
+					0x20ac
+					0x215c
+					0x220c
+					0x2314
+					0x2394
+					0x2414
+					0x2008
+					0x200c
+					0x2010
+					0x2014
+					0x2018
+					0x201c
+					0x2020
+					0x2024
+					0x2028
+					0x202c
+					0x2030
+					0x2034
+					0x2038>;
+
+		qcom,iommu-bfb-data =  <0xffffffff
+					0xffffffff
+					0x00000004
+					0x00000008
+					0x00000000
+					0x00000000
+					0x00000094
+					0x000000b4
+					0x0
+					0x94
+					0x114
+					0x0
+					0x0
+					0x0
+					0x0
+					0x0
+					0x0
+					0x0
+					0x0
+					0x0
+					0x0
+					0x0
+					0x0
+					0x0>;
+
 		qcom,iommu-ctx@fdc8c000 {
 			reg = <0xfdc8c000 0x1000>;
 			interrupts = <0 42 0>;
@@ -108,10 +227,35 @@
 		ranges;
 		reg = <0xfdb10000 0x10000>;
 		vdd-supply = <&gdsc_oxili_cx>;
-		qcom,iommu-smt-size = <32>;
 		qcom,needs-alt-core-clk;
 		status = "disabled";
 
+		qcom,iommu-bfb-regs =  <0x204c
+					0x2050
+					0x2514
+					0x2540
+					0x256c
+					0x20ac
+					0x215c
+					0x220c
+					0x2314
+					0x2394
+					0x2414
+					0x2008>;
+
+		qcom,iommu-bfb-data =  <0xffffffff
+					0xffffffff
+					0x00000004
+					0x00000010
+					0x00000000
+					0x00000000
+					0x00000001
+					0x00000021
+					0x0
+					0x1
+					0x81
+					0x0>;
+
 		qcom,iommu-ctx@fdb18000 {
 			reg = <0xfdb18000 0x1000>;
 			interrupts = <0 241 0>;
@@ -134,9 +278,46 @@
 		ranges;
 		reg = <0xfda44000 0x10000>;
 		vdd-supply = <&gdsc_vfe>;
-		qcom,iommu-smt-size = <32>;
 		status = "disabled";
 
+		qcom,iommu-bfb-regs =  <0x204c
+					0x2050
+					0x2514
+					0x2540
+					0x256c
+					0x2314
+					0x2394
+					0x2414
+					0x20ac
+					0x215c
+					0x220c
+					0x2008
+					0x200c
+					0x2010
+					0x2014
+					0x2018
+					0x201c
+					0x2020>;
+
+		qcom,iommu-bfb-data =  <0xffffffff
+					0xffffffff
+					0x4
+					0x8
+					0x0
+					0x0
+					0x20
+					0x78
+					0x0
+					0x20
+					0x36
+					0x0
+					0x0
+					0x0
+					0x0
+					0x0
+					0x0
+					0x0>;
+
 		qcom,iommu-ctx@fda4c000 {
 			reg = <0xfda4c000 0x1000>;
 			interrupts = <0 65 0>;
diff --git a/arch/arm/boot/dts/msm-pm8019.dtsi b/arch/arm/boot/dts/msm-pm8019.dtsi
index 3b06450..2105e8a 100755
--- a/arch/arm/boot/dts/msm-pm8019.dtsi
+++ b/arch/arm/boot/dts/msm-pm8019.dtsi
@@ -22,6 +22,21 @@
 		#address-cells = <1>;
 		#size-cells = <1>;
 
+		qcom,power_on@800 {
+			compatible = "qcom,qpnp-power-on";
+			reg = <0x800 0x100>;
+			interrupts = <0x0 0x8 0x2>;
+			interrupt-names = "cblpwr";
+			qcom,pon-dbc-delay = <15625>;
+			qcom,system-reset;
+
+			qcom,pon_1 {
+				qcom,pon-type = <2>;
+				qcom,pull-up = <1>;
+				linux,code = <116>;
+			};
+		};
+
 		clkdiv@5b00 {
 			reg = <0x5b00 0x100>;
 			compatible = "qcom,qpnp-clkdiv";
@@ -137,6 +152,47 @@
 				qcom,pin-num = <6>;
 			};
 		};
+
+		pm8019_vadc: vadc@3100 {
+			compatible = "qcom,qpnp-vadc";
+			reg = <0x3100 0x100>;
+			interrupts = <0x0 0x31 0x0>;
+			qcom,adc-bit-resolution = <15>;
+			qcom,adc-vdd-reference = <1800>;
+
+			chan@8 {
+				label = "die_temp";
+				qcom,channel-num = <8>;
+				qcom,decimation = <0>;
+				qcom,pre-div-channel-scaling = <0>;
+				qcom,calibration-type = "absolute";
+				qcom,scale-function = <3>;
+				qcom,hw-settle-time = <0>;
+				qcom,fast-avg-setup = <0>;
+			};
+
+			chan@9 {
+				label = "ref_625mv";
+				qcom,channel-num = <9>;
+				qcom,decimation = <0>;
+				qcom,pre-div-channel-scaling = <0>;
+				qcom,calibration-type = "absolute";
+				qcom,scale-function = <0>;
+				qcom,hw-settle-time = <0>;
+				qcom,fast-avg-setup = <0>;
+			};
+
+			chan@10 {
+				label = "ref_1250v";
+				qcom,channel-num = <10>;
+				qcom,decimation = <0>;
+				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,pm8019@1 {
diff --git a/arch/arm/boot/dts/msm-pm8644.dtsi b/arch/arm/boot/dts/msm-pm8644.dtsi
new file mode 100644
index 0000000..17a6b0b
--- /dev/null
+++ b/arch/arm/boot/dts/msm-pm8644.dtsi
@@ -0,0 +1,722 @@
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+&spmi_bus {
+	#address-cells = <1>;
+	#size-cells = <0>;
+	interrupt-controller;
+	#interrupt-cells = <3>;
+
+	qcom,pm8644@0 {
+		spmi-slave-container;
+		reg = <0x0>;
+		#address-cells = <1>;
+		#size-cells = <1>;
+
+		pm8644_gpios: gpios {
+			spmi-dev-container;
+			compatible = "qcom,qpnp-pin";
+			gpio-controller;
+			#gpio-cells = <2>;
+			#address-cells = <1>;
+			#size-cells = <1>;
+			label = "pm8644-gpio";
+
+			gpio@c000 {
+				reg = <0xc000 0x100>;
+				qcom,pin-num = <1>;
+			};
+
+			gpio@c100 {
+				reg = <0xc100 0x100>;
+				qcom,pin-num = <2>;
+			};
+
+			gpio@c200 {
+				reg = <0xc200 0x100>;
+				qcom,pin-num = <3>;
+			};
+
+			gpio@c300 {
+				reg = <0xc300 0x100>;
+				qcom,pin-num = <4>;
+			};
+
+			gpio@c400 {
+				reg = <0xc400 0x100>;
+				qcom,pin-num = <5>;
+			};
+
+			gpio@c500 {
+				reg = <0xc500 0x100>;
+				qcom,pin-num = <6>;
+			};
+
+			gpio@c600 {
+				reg = <0xc600 0x100>;
+				qcom,pin-num = <7>;
+			};
+
+			gpio@c700 {
+				reg = <0xc700 0x100>;
+				qcom,pin-num = <8>;
+			};
+
+			gpio@c800 {
+				reg = <0xc800 0x100>;
+				qcom,pin-num = <9>;
+			};
+
+			gpio@c900 {
+				reg = <0xc900 0x100>;
+				qcom,pin-num = <10>;
+			};
+
+			gpio@ca00 {
+				reg = <0xca00 0x100>;
+				qcom,pin-num = <11>;
+			};
+
+			gpio@cb00 {
+				reg = <0xcb00 0x100>;
+				qcom,pin-num = <12>;
+			};
+
+			gpio@cc00 {
+				reg = <0xcc00 0x100>;
+				qcom,pin-num = <13>;
+			};
+
+			gpio@cd00 {
+				reg = <0xcd00 0x100>;
+				qcom,pin-num = <14>;
+			};
+
+			gpio@ce00 {
+				reg = <0xce00 0x100>;
+				qcom,pin-num = <15>;
+			};
+
+			gpio@cf00 {
+				reg = <0xcf00 0x100>;
+				qcom,pin-num = <16>;
+			};
+
+			gpio@d000 {
+				reg = <0xd000 0x100>;
+				qcom,pin-num = <17>;
+			};
+
+			gpio@d100 {
+				reg = <0xd100 0x100>;
+				qcom,pin-num = <18>;
+			};
+
+			gpio@d200 {
+				reg = <0xd200 0x100>;
+				qcom,pin-num = <19>;
+			};
+
+			gpio@d300 {
+				reg = <0xd300 0x100>;
+				qcom,pin-num = <20>;
+			};
+
+			gpio@d400 {
+				reg = <0xd400 0x100>;
+				qcom,pin-num = <21>;
+			};
+
+			gpio@d500 {
+				reg = <0xd500 0x100>;
+				qcom,pin-num = <22>;
+			};
+
+			gpio@d600 {
+				reg = <0xd600 0x100>;
+				qcom,pin-num = <23>;
+			};
+
+			gpio@d700 {
+				reg = <0xd700 0x100>;
+				qcom,pin-num = <24>;
+			};
+
+			gpio@d800 {
+				reg = <0xd800 0x100>;
+				qcom,pin-num = <25>;
+			};
+
+			gpio@d900 {
+				reg = <0xd900 0x100>;
+				qcom,pin-num = <26>;
+			};
+
+			gpio@da00 {
+				reg = <0xda00 0x100>;
+				qcom,pin-num = <27>;
+			};
+
+			gpio@db00 {
+				reg = <0xdb00 0x100>;
+				qcom,pin-num = <28>;
+			};
+
+			gpio@dc00 {
+				reg = <0xdc00 0x100>;
+				qcom,pin-num = <29>;
+			};
+
+			gpio@dd00 {
+				reg = <0xdd00 0x100>;
+				qcom,pin-num = <30>;
+			};
+
+			gpio@de00 {
+				reg = <0xde00 0x100>;
+				qcom,pin-num = <31>;
+			};
+
+			gpio@df00 {
+				reg = <0xdf00 0x100>;
+				qcom,pin-num = <32>;
+			};
+
+			gpio@e000 {
+				reg = <0xe000 0x100>;
+				qcom,pin-num = <33>;
+			};
+
+			gpio@e100 {
+				reg = <0xe100 0x100>;
+				qcom,pin-num = <34>;
+			};
+
+			gpio@e200 {
+				reg = <0xe200 0x100>;
+				qcom,pin-num = <35>;
+			};
+
+			gpio@e300 {
+				reg = <0xe300 0x100>;
+				qcom,pin-num = <36>;
+			};
+
+			gpio@e400 {
+				reg = <0xe400 0x100>;
+				qcom,pin-num = <37>;
+			};
+
+			gpio@e500 {
+				reg = <0xe500 0x100>;
+				qcom,pin-num = <38>;
+			};
+
+			gpio@e600 {
+				reg = <0xe600 0x100>;
+				qcom,pin-num = <39>;
+			};
+
+			gpio@e700 {
+				reg = <0xe700 0x100>;
+				qcom,pin-num = <40>;
+			};
+
+			gpio@e800 {
+				reg = <0xe800 0x100>;
+				qcom,pin-num = <41>;
+			};
+
+			gpio@e900 {
+				reg = <0xe900 0x100>;
+				qcom,pin-num = <42>;
+			};
+
+			gpio@ea00 {
+				reg = <0xea00 0x100>;
+				qcom,pin-num = <43>;
+			};
+		};
+
+		pm8644_mpps: mpps {
+			spmi-dev-container;
+			compatible = "qcom,qpnp-pin";
+			gpio-controller;
+			#gpio-cells = <2>;
+			#address-cells = <1>;
+			#size-cells = <1>;
+			label = "pm8644-mpp";
+
+			mpp@a000 {
+				reg = <0xa000 0x100>;
+				qcom,pin-num = <1>;
+			};
+
+			mpp@a100 {
+				reg = <0xa100 0x100>;
+				qcom,pin-num = <2>;
+			};
+
+			mpp@a200 {
+				reg = <0xa200 0x100>;
+				qcom,pin-num = <3>;
+			};
+
+			mpp@a300 {
+				reg = <0xa300 0x100>;
+				qcom,pin-num = <4>;
+			};
+
+			mpp@a400 {
+				reg = <0xa400 0x100>;
+				qcom,pin-num = <5>;
+			};
+
+			mpp@a500 {
+				reg = <0xa500 0x100>;
+				qcom,pin-num = <6>;
+			};
+		};
+	};
+
+	qcom,pm8644@1 {
+		spmi-slave-container;
+		reg = <0x1>;
+		#address-cells = <1>;
+		#size-cells = <1>;
+
+		regulator@1400 {
+			regulator-name = "8644_s1";
+			spmi-dev-container;
+			#address-cells = <1>;
+			#size-cells = <1>;
+			compatible = "qcom,qpnp-regulator";
+			reg = <0x1400 0x300>;
+			status = "disabled";
+
+			qcom,ctl@1400 {
+				reg = <0x1400 0x100>;
+			};
+			qcom,ps@1500 {
+				reg = <0x1500 0x100>;
+			};
+			qcom,freq@1600 {
+				reg = <0x1600 0x100>;
+			};
+		};
+
+		regulator@1700 {
+			regulator-name = "8644_s2";
+			spmi-dev-container;
+			#address-cells = <1>;
+			#size-cells = <1>;
+			compatible = "qcom,qpnp-regulator";
+			reg = <0x1700 0x300>;
+			status = "disabled";
+
+			qcom,ctl@1700 {
+				reg = <0x1700 0x100>;
+			};
+			qcom,ps@1800 {
+				reg = <0x1800 0x100>;
+			};
+			qcom,freq@1900 {
+				reg = <0x1900 0x100>;
+			};
+		};
+
+		regulator@1a00 {
+			regulator-name = "8644_s3";
+			spmi-dev-container;
+			#address-cells = <1>;
+			#size-cells = <1>;
+			compatible = "qcom,qpnp-regulator";
+			reg = <0x1a00 0x300>;
+			status = "disabled";
+
+			qcom,ctl@1a00 {
+				reg = <0x1a00 0x100>;
+			};
+			qcom,ps@1b00 {
+				reg = <0x1b00 0x100>;
+			};
+			qcom,freq@1c00 {
+				reg = <0x1c00 0x100>;
+			};
+		};
+
+		regulator@1d00 {
+			regulator-name = "8644_s4";
+			spmi-dev-container;
+			#address-cells = <1>;
+			#size-cells = <1>;
+			compatible = "qcom,qpnp-regulator";
+			reg = <0x1d00 0x300>;
+			status = "disabled";
+
+			qcom,ctl@1d00 {
+				reg = <0x1d00 0x100>;
+			};
+			qcom,ps@1e00 {
+				reg = <0x1e00 0x100>;
+			};
+			qcom,freq@1f00 {
+				reg = <0x1f00 0x100>;
+			};
+		};
+
+		regulator@2000 {
+			regulator-name = "8644_s5";
+			spmi-dev-container;
+			#address-cells = <1>;
+			#size-cells = <1>;
+			compatible = "qcom,qpnp-regulator";
+			reg = <0x2000 0x300>;
+			status = "disabled";
+
+			qcom,ctl@2000 {
+				reg = <0x2000 0x100>;
+			};
+			qcom,ps@2100 {
+				reg = <0x2100 0x100>;
+			};
+			qcom,freq@2200 {
+				reg = <0x2200 0x100>;
+			};
+		};
+
+		regulator@2300 {
+			regulator-name = "8644_s6";
+			spmi-dev-container;
+			#address-cells = <1>;
+			#size-cells = <1>;
+			compatible = "qcom,qpnp-regulator";
+			reg = <0x2300 0x300>;
+			status = "disabled";
+
+			qcom,ctl@2300 {
+				reg = <0x2300 0x100>;
+			};
+			qcom,ps@2400 {
+				reg = <0x2400 0x100>;
+			};
+			qcom,freq@2500 {
+				reg = <0x2500 0x100>;
+			};
+		};
+
+		regulator@2600 {
+			regulator-name = "8644_s7";
+			spmi-dev-container;
+			#address-cells = <1>;
+			#size-cells = <1>;
+			compatible = "qcom,qpnp-regulator";
+			reg = <0x2600 0x300>;
+			status = "disabled";
+
+			qcom,ctl@2600 {
+				reg = <0x2600 0x100>;
+			};
+			qcom,ps@2700 {
+				reg = <0x2700 0x100>;
+			};
+			qcom,freq@2800 {
+				reg = <0x2800 0x100>;
+			};
+		};
+
+		regulator@2900 {
+			regulator-name = "8644_s8";
+			spmi-dev-container;
+			#address-cells = <1>;
+			#size-cells = <1>;
+			compatible = "qcom,qpnp-regulator";
+			reg = <0x2900 0x300>;
+			status = "disabled";
+
+			qcom,ctl@2900 {
+				reg = <0x2900 0x100>;
+			};
+			qcom,ps@2a00 {
+				reg = <0x2a00 0x100>;
+			};
+			qcom,freq@2b00 {
+				reg = <0x2b00 0x100>;
+			};
+		};
+
+		regulator@2c00 {
+			regulator-name = "8644_s9";
+			spmi-dev-container;
+			#address-cells = <1>;
+			#size-cells = <1>;
+			compatible = "qcom,qpnp-regulator";
+			reg = <0x2c00 0x300>;
+			status = "disabled";
+
+			qcom,ctl@2c00 {
+				reg = <0x2c00 0x100>;
+			};
+			qcom,ps@2d00 {
+				reg = <0x2d00 0x100>;
+			};
+			qcom,freq@2e00 {
+				reg = <0x2e00 0x100>;
+			};
+		};
+
+		regulator@2f00 {
+			regulator-name = "8644_s10";
+			spmi-dev-container;
+			#address-cells = <1>;
+			#size-cells = <1>;
+			compatible = "qcom,qpnp-regulator";
+			reg = <0x2f00 0x300>;
+			status = "disabled";
+
+			qcom,ctl@2f00 {
+				reg = <0x2f00 0x100>;
+			};
+			qcom,ps@3000 {
+				reg = <0x3000 0x100>;
+			};
+			qcom,freq@3100 {
+				reg = <0x3100 0x100>;
+			};
+		};
+
+		regulator@3200 {
+			regulator-name = "8644_s11";
+			spmi-dev-container;
+			#address-cells = <1>;
+			#size-cells = <1>;
+			compatible = "qcom,qpnp-regulator";
+			reg = <0x3200 0x300>;
+			status = "disabled";
+
+			qcom,ctl@3200 {
+				reg = <0x3200 0x100>;
+			};
+			qcom,ps@3300 {
+				reg = <0x3300 0x100>;
+			};
+			qcom,freq@3400 {
+				reg = <0x3400 0x100>;
+			};
+		};
+
+		regulator@4000 {
+			regulator-name = "8644_l1";
+			reg = <0x4000 0x100>;
+			compatible = "qcom,qpnp-regulator";
+			status = "disabled";
+		};
+
+		regulator@4100 {
+			regulator-name = "8644_l2";
+			reg = <0x4100 0x100>;
+			compatible = "qcom,qpnp-regulator";
+			status = "disabled";
+		};
+
+		regulator@4200 {
+			regulator-name = "8644_l3";
+			reg = <0x4200 0x100>;
+			compatible = "qcom,qpnp-regulator";
+			status = "disabled";
+		};
+
+		regulator@4300 {
+			regulator-name = "8644_l4";
+			reg = <0x4300 0x100>;
+			compatible = "qcom,qpnp-regulator";
+			status = "disabled";
+		};
+
+		regulator@4400 {
+			regulator-name = "8644_l5";
+			reg = <0x4400 0x100>;
+			compatible = "qcom,qpnp-regulator";
+			qcom,force-type = <0x04 0x10>;
+			status = "disabled";
+		};
+
+		regulator@4500 {
+			regulator-name = "8644_l6";
+			reg = <0x4500 0x100>;
+			compatible = "qcom,qpnp-regulator";
+			status = "disabled";
+		};
+
+		regulator@4600 {
+			regulator-name = "8644_l7";
+			reg = <0x4600 0x100>;
+			compatible = "qcom,qpnp-regulator";
+			qcom,force-type = <0x04 0x10>;
+			status = "disabled";
+		};
+
+		regulator@4700 {
+			regulator-name = "8644_l8";
+			reg = <0x4700 0x100>;
+			compatible = "qcom,qpnp-regulator";
+			status = "disabled";
+		};
+
+		regulator@4800 {
+			regulator-name = "8644_l9";
+			reg = <0x4800 0x100>;
+			compatible = "qcom,qpnp-regulator";
+			status = "disabled";
+		};
+
+		regulator@4900 {
+			regulator-name = "8644_l10";
+			reg = <0x4900 0x100>;
+			compatible = "qcom,qpnp-regulator";
+			status = "disabled";
+		};
+
+		regulator@4a00 {
+			regulator-name = "8644_l11";
+			reg = <0x4a00 0x100>;
+			compatible = "qcom,qpnp-regulator";
+			status = "disabled";
+		};
+
+		regulator@4b00 {
+			regulator-name = "8644_l12";
+			reg = <0x4b00 0x100>;
+			compatible = "qcom,qpnp-regulator";
+			status = "disabled";
+		};
+
+		regulator@4c00 {
+			regulator-name = "8644_l13";
+			reg = <0x4c00 0x100>;
+			compatible = "qcom,qpnp-regulator";
+			status = "disabled";
+		};
+
+		regulator@4d00 {
+			regulator-name = "8644_l14";
+			reg = <0x4d00 0x100>;
+			compatible = "qcom,qpnp-regulator";
+			status = "disabled";
+		};
+
+		regulator@4e00 {
+			regulator-name = "8644_l15";
+			reg = <0x4e00 0x100>;
+			compatible = "qcom,qpnp-regulator";
+			status = "disabled";
+		};
+
+		regulator@4f00 {
+			regulator-name = "8644_l16";
+			reg = <0x4f00 0x100>;
+			compatible = "qcom,qpnp-regulator";
+			status = "disabled";
+		};
+
+		regulator@5000 {
+			regulator-name = "8644_l17";
+			reg = <0x5000 0x100>;
+			compatible = "qcom,qpnp-regulator";
+			status = "disabled";
+		};
+
+		regulator@5100 {
+			regulator-name = "8644_l18";
+			reg = <0x5100 0x100>;
+			compatible = "qcom,qpnp-regulator";
+			status = "disabled";
+		};
+
+		regulator@5200 {
+			regulator-name = "8644_l19";
+			reg = <0x5200 0x100>;
+			compatible = "qcom,qpnp-regulator";
+			status = "disabled";
+		};
+
+		regulator@5300 {
+			regulator-name = "8644_l20";
+			reg = <0x5300 0x100>;
+			compatible = "qcom,qpnp-regulator";
+			status = "disabled";
+		};
+
+		regulator@5400 {
+			regulator-name = "8644_l21";
+			reg = <0x5400 0x100>;
+			compatible = "qcom,qpnp-regulator";
+			status = "disabled";
+		};
+
+		regulator@5500 {
+			regulator-name = "8644_l22";
+			reg = <0x5500 0x100>;
+			compatible = "qcom,qpnp-regulator";
+			status = "disabled";
+		};
+
+		regulator@5600 {
+			regulator-name = "8644_l23";
+			reg = <0x5600 0x100>;
+			compatible = "qcom,qpnp-regulator";
+			status = "disabled";
+		};
+
+		regulator@5700 {
+			regulator-name = "8644_l24";
+			reg = <0x5700 0x100>;
+			compatible = "qcom,qpnp-regulator";
+			status = "disabled";
+		};
+
+		regulator@5800 {
+			regulator-name = "8644_l25";
+			reg = <0x5800 0x100>;
+			compatible = "qcom,qpnp-regulator";
+			status = "disabled";
+		};
+
+		regulator@8000 {
+			regulator-name = "8644_lvs1";
+			reg = <0x8000 0x100>;
+			compatible = "qcom,qpnp-regulator";
+			status = "disabled";
+		};
+
+		regulator@8100 {
+			regulator-name = "8644_lvs2";
+			reg = <0x8100 0x100>;
+			compatible = "qcom,qpnp-regulator";
+			status = "disabled";
+		};
+
+		regulator@8200 {
+			regulator-name = "8644_mvs1";
+			reg = <0x8200 0x100>;
+			compatible = "qcom,qpnp-regulator";
+			status = "disabled";
+		};
+
+		regulator@8300 {
+			regulator-name = "8644_mvs2";
+			reg = <0x8300 0x100>;
+			compatible = "qcom,qpnp-regulator";
+			status = "disabled";
+		};
+	};
+};
diff --git a/arch/arm/boot/dts/msm-pm8841.dtsi b/arch/arm/boot/dts/msm-pm8841.dtsi
index ea83231..1e0e5dfa 100644
--- a/arch/arm/boot/dts/msm-pm8841.dtsi
+++ b/arch/arm/boot/dts/msm-pm8841.dtsi
@@ -22,6 +22,15 @@
 		#address-cells = <1>;
 		#size-cells = <1>;
 
+		qcom,temp-alarm@2400 {
+			compatible = "qcom,qpnp-temp-alarm";
+			reg = <0x2400 0x100>;
+			interrupts = <0x4 0x24 0x0>;
+			label = "pm8841_tz";
+			qcom,threshold-set = <0>;
+			qcom,default-temp = <37000>;
+		};
+
 		pm8841_mpps: mpps {
 			spmi-dev-container;
 			compatible = "qcom,qpnp-pin";
diff --git a/arch/arm/boot/dts/msm-pm8941.dtsi b/arch/arm/boot/dts/msm-pm8941.dtsi
index f1e18cf..341b49b 100644
--- a/arch/arm/boot/dts/msm-pm8941.dtsi
+++ b/arch/arm/boot/dts/msm-pm8941.dtsi
@@ -22,6 +22,15 @@
 		#address-cells = <1>;
 		#size-cells = <1>;
 
+		qcom,temp-alarm@2400 {
+			compatible = "qcom,qpnp-temp-alarm";
+			reg = <0x2400 0x100>;
+			interrupts = <0x0 0x24 0x0>;
+			label = "pm8941_tz";
+			qcom,channel-num = <8>;
+			qcom,threshold-set = <0>;
+		};
+
 		qcom,power-on@800 {
 			compatible = "qcom,qpnp-power-on";
 			reg = <0x800 0x100>;
@@ -102,11 +111,12 @@
 			qcom,cxo-freq = <19200000>;
 		};
 
-		pm8941-chg {
+		pm8941_chg: qcom,charger {
 			spmi-dev-container;
 			compatible = "qcom,qpnp-charger";
 			#address-cells = <1>;
 			#size-cells = <1>;
+			status = "disabled";
 
 			qcom,chg-vddmax-mv = <4200>;
 			qcom,chg-vddsafe-mv = <4200>;
@@ -115,6 +125,7 @@
 			qcom,chg-ibatterm-ma = <200>;
 
 			qcom,chg-chgr@1000 {
+				status = "disabled";
 				reg = <0x1000 0x100>;
 				interrupts =	<0x0 0x10 0x0>,
 						<0x0 0x10 0x1>,
@@ -136,6 +147,7 @@
 			};
 
 			qcom,chg-buck@1100 {
+				status = "disabled";
 				reg = <0x1100 0x100>;
 				interrupts =	<0x0 0x11 0x0>,
 						<0x0 0x11 0x1>,
@@ -155,6 +167,7 @@
 			};
 
 			qcom,chg-bat-if@1200 {
+				status = "disabled";
 				reg = <0x1200 0x100>;
 				interrupts =	<0x0 0x12 0x0>,
 						<0x0 0x12 0x1>,
@@ -170,17 +183,19 @@
 			};
 
 			qcom,chg-usb-chgpth@1300 {
+				status = "disabled";
 				reg = <0x1300 0x100>;
 				interrupts =	<0 0x13 0x0>,
 						<0 0x13 0x1>,
 						<0x0 0x13 0x2>;
 
-				interrupt-names =	"usbin-valid",
-							"coarse-det-usb",
+				interrupt-names =	"coarse-det-usb",
+							"usbin-valid",
 							"chg-gone";
 			};
 
 			qcom,chg-dc-chgpth@1400 {
+				status = "disabled";
 				reg = <0x1400 0x100>;
 				interrupts =	<0x0 0x14 0x0>,
 						<0x0 0x14 0x1>;
@@ -190,6 +205,7 @@
 			};
 
 			qcom,chg-boost@1500 {
+				status = "disabled";
 				reg = <0x1500 0x100>;
 				interrupts =	<0x0 0x15 0x0>,
 						<0x0 0x15 0x1>;
@@ -199,6 +215,7 @@
 			};
 
 			qcom,chg-misc@1600 {
+				status = "disabled";
 				reg = <0x1600 0x100>;
 			};
 		};
@@ -976,65 +993,158 @@
 			status = "disabled";
 		};
 
+		qcom,leds@d300 {
+			compatible = "qcom,leds-qpnp";
+			reg = <0xd300 0x100>;
+			label = "flash";
+		};
+
+		qcom,leds@d400 {
+			compatible = "qcom,leds-qpnp";
+			reg = <0xd400 0x100>;
+			label = "flash";
+		};
+
+		qcom,leds@d500 {
+			compatible = "qcom,leds-qpnp";
+			reg = <0xd500 0x100>;
+			label = "flash";
+		};
+
+		qcom,leds@d600 {
+			compatible = "qcom,leds-qpnp";
+			reg = <0xd600 0x100>;
+			label = "flash";
+		};
+
+		qcom,leds@d700 {
+			compatible = "qcom,leds-qpnp";
+			reg = <0xd700 0x100>;
+			label = "flash";
+		};
+
 		qcom,leds@d800 {
 			compatible = "qcom,leds-qpnp";
 			reg = <0xd800 0x100>;
-			qcom,label = "wled";
+			label = "wled";
 		};
 
 		qcom,leds@d900 {
 			compatible = "qcom,leds-qpnp";
 			reg = <0xd900 0x100>;
-			qcom,label = "wled";
+			label = "wled";
 		};
 
 		qcom,leds@da00 {
 			compatible = "qcom,leds-qpnp";
 			reg = <0xda00 0x100>;
-			qcom,label = "wled";
+			label = "wled";
 		};
 
 		qcom,leds@db00 {
 			compatible = "qcom,leds-qpnp";
 			reg = <0xdb00 0x100>;
-			qcom,label = "wled";
+			label = "wled";
 		};
 
 		qcom,leds@dc00 {
 			compatible = "qcom,leds-qpnp";
 			reg = <0xdc00 0x100>;
-			qcom,label = "wled";
+			label = "wled";
 		};
 
 		qcom,leds@dd00 {
 			compatible = "qcom,leds-qpnp";
 			reg = <0xdd00 0x100>;
-			qcom,label = "wled";
+			label = "wled";
 		};
 
 		qcom,leds@de00 {
 			compatible = "qcom,leds-qpnp";
 			reg = <0xde00 0x100>;
-			qcom,label = "wled";
+			label = "wled";
 		};
 
 		qcom,leds@df00 {
 			compatible = "qcom,leds-qpnp";
 			reg = <0xdf00 0x100>;
-			qcom,label = "wled";
+			label = "wled";
 		};
 
 		qcom,leds@e000 {
 			compatible = "qcom,leds-qpnp";
 			reg = <0xe000 0x100>;
-			qcom,label = "wled";
+			label = "wled";
 		};
 
 		qcom,leds@e100 {
 			compatible = "qcom,leds-qpnp";
 			reg = <0xe100 0x100>;
-			qcom,label = "wled";
+			label = "wled";
 		};
 
+		pwm@b100 {
+			compatible = "qcom,qpnp-pwm";
+			reg = <0xb100 0x100>,
+			      <0xb042 0x7e>;
+			reg-names = "qpnp-lpg-channel-base", "qpnp-lpg-lut-base";
+			qcom,channel-id = <0>;
+		};
+
+		pwm@b200 {
+			compatible = "qcom,qpnp-pwm";
+			reg = <0xb200 0x100>,
+			      <0xb042 0x7e>;
+			reg-names = "qpnp-lpg-channel-base", "qpnp-lpg-lut-base";
+			qcom,channel-id = <1>;
+		};
+
+		pwm@b300 {
+			compatible = "qcom,qpnp-pwm";
+			reg = <0xb300 0x100>,
+			      <0xb042 0x7e>;
+			reg-names = "qpnp-lpg-channel-base", "qpnp-lpg-lut-base";
+			qcom,channel-id = <2>;
+		};
+
+		pwm@b400 {
+			compatible = "qcom,qpnp-pwm";
+			reg = <0xb400 0x100>,
+			      <0xb042 0x7e>;
+			reg-names = "qpnp-lpg-channel-base", "qpnp-lpg-lut-base";
+			qcom,channel-id = <3>;
+		};
+
+		pwm@b500 {
+			compatible = "qcom,qpnp-pwm";
+			reg = <0xb500 0x100>,
+			      <0xb042 0x7e>;
+			reg-names = "qpnp-lpg-channel-base", "qpnp-lpg-lut-base";
+			qcom,channel-id = <4>;
+		};
+
+		pwm@b600 {
+			compatible = "qcom,qpnp-pwm";
+			reg = <0xb600 0x100>,
+			      <0xb042 0x7e>;
+			reg-names = "qpnp-lpg-channel-base", "qpnp-lpg-lut-base";
+			qcom,channel-id = <5>;
+		};
+
+		pwm@b700 {
+			compatible = "qcom,qpnp-pwm";
+			reg = <0xb700 0x100>,
+			      <0xb042 0x7e>;
+			reg-names = "qpnp-lpg-channel-base", "qpnp-lpg-lut-base";
+			qcom,channel-id = <6>;
+		};
+
+		pwm@b800 {
+			compatible = "qcom,qpnp-pwm";
+			reg = <0xb800 0x100>,
+			      <0xb042 0x7e>;
+			reg-names = "qpnp-lpg-channel-base", "qpnp-lpg-lut-base";
+			qcom,channel-id = <7>;
+		};
 	};
 };
diff --git a/arch/arm/boot/dts/msm8226-regulator.dtsi b/arch/arm/boot/dts/msm8226-regulator.dtsi
new file mode 100644
index 0000000..8fe94a5
--- /dev/null
+++ b/arch/arm/boot/dts/msm8226-regulator.dtsi
@@ -0,0 +1,274 @@
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+ /* Stub Regulators */
+
+ / {
+	pm8026_s1: regulator-s1 {
+		compatible = "qcom,stub-regulator";
+		regulator-name = "8026_s1";
+		qcom,hpm-min-load = <100000>;
+		regulator-min-microvolt = <1150000>;
+		regulator-max-microvolt = <1150000>;
+	};
+
+	pm8026_s2: regulator-s2 {
+		compatible = "qcom,stub-regulator";
+		regulator-name = "8026_s2";
+		qcom,hpm-min-load = <100000>;
+		regulator-min-microvolt = <1050000>;
+		regulator-max-microvolt = <1050000>;
+	};
+
+	pm8026_s3: regulator-s3 {
+		compatible = "qcom,stub-regulator";
+		regulator-name = "8026_s3";
+		qcom,hpm-min-load = <100000>;
+		regulator-min-microvolt = <1300000>;
+		regulator-max-microvolt = <1300000>;
+	};
+
+	pm8026_s4: regulator-s4 {
+		compatible = "qcom,stub-regulator";
+		regulator-name = "8026_s4";
+		qcom,hpm-min-load = <100000>;
+		regulator-min-microvolt = <2100000>;
+		regulator-max-microvolt = <2100000>;
+	};
+
+	pm8026_s5: regulator-s5 {
+		compatible = "qcom,stub-regulator";
+		regulator-name = "8026_s5";
+		qcom,hpm-min-load = <100000>;
+		regulator-min-microvolt = <1150000>;
+		regulator-max-microvolt = <1150000>;
+	};
+
+	pm8026_l1: regulator-l1 {
+		compatible = "qcom,stub-regulator";
+		regulator-name = "8026_l1";
+		parent-supply = <&pm8026_s3>;
+		qcom,hpm-min-load = <10000>;
+		regulator-min-microvolt = <1225000>;
+		regulator-max-microvolt = <1225000>;
+	};
+
+	pm8026_l2: regulator-l2 {
+		compatible = "qcom,stub-regulator";
+		regulator-name = "8026_l2";
+		parent-supply = <&pm8026_s3>;
+		qcom,hpm-min-load = <10000>;
+		regulator-min-microvolt = <1200000>;
+		regulator-max-microvolt = <1200000>;
+	};
+
+	pm8026_l3: regulator-l3 {
+		compatible = "qcom,stub-regulator";
+		regulator-name = "8026_l3";
+		parent-supply = <&pm8026_s3>;
+		qcom,hpm-min-load = <10000>;
+		regulator-min-microvolt = <1150000>;
+		regulator-max-microvolt = <1150000>;
+	};
+
+	pm8026_l4: regulator-l4 {
+		compatible = "qcom,stub-regulator";
+		regulator-name = "8026_l4";
+		parent-supply = <&pm8026_s3>;
+		qcom,hpm-min-load = <10000>;
+		regulator-min-microvolt = <1200000>;
+		regulator-max-microvolt = <1200000>;
+	};
+
+	pm8026_l5: regulator-l5 {
+		compatible = "qcom,stub-regulator";
+		regulator-name = "8026_l5";
+		parent-supply = <&pm8026_s3>;
+		qcom,hpm-min-load = <10000>;
+		regulator-min-microvolt = <1200000>;
+		regulator-max-microvolt = <1200000>;
+	};
+
+	pm8026_l6: regulator-l6 {
+		compatible = "qcom,stub-regulator";
+		regulator-name = "8026_l6";
+		parent-supply = <&pm8026_s4>;
+		qcom,hpm-min-load = <10000>;
+		regulator-min-microvolt = <1800000>;
+		regulator-max-microvolt = <1800000>;
+	};
+
+	pm8026_l7: regulator-l7 {
+		compatible = "qcom,stub-regulator";
+		regulator-name = "8026_l7";
+		parent-supply = <&pm8026_s4>;
+		qcom,hpm-min-load = <10000>;
+		regulator-min-microvolt = <1850000>;
+		regulator-max-microvolt = <1850000>;
+	};
+
+	pm8026_l8: regulator-l8 {
+		compatible = "qcom,stub-regulator";
+		regulator-name = "8026_l8";
+		parent-supply = <&pm8026_s4>;
+		qcom,hpm-min-load = <5000>;
+		regulator-min-microvolt = <1800000>;
+		regulator-max-microvolt = <1800000>;
+	};
+
+	pm8026_l9: regulator-l9 {
+		compatible = "qcom,stub-regulator";
+		regulator-name = "8026_l9";
+		parent-supply = <&pm8026_s4>;
+		qcom,hpm-min-load = <10000>;
+		regulator-min-microvolt = <2050000>;
+		regulator-max-microvolt = <2050000>;
+	};
+
+	pm8026_l10: regulator-l10 {
+		compatible = "qcom,stub-regulator";
+		regulator-name = "8026_l10";
+		parent-supply = <&pm8026_s4>;
+		qcom,hpm-min-load = <5000>;
+		regulator-min-microvolt = <1800000>;
+		regulator-max-microvolt = <1800000>;
+	};
+
+	pm8026_l12: regulator-l12 {
+		compatible = "qcom,stub-regulator";
+		regulator-name = "8026_l12";
+		qcom,hpm-min-load = <5000>;
+		regulator-min-microvolt = <1800000>;
+		regulator-max-microvolt = <1800000>;
+	};
+
+	pm8026_l14: regulator-l14 {
+		compatible = "qcom,stub-regulator";
+		regulator-name = "8026_l14";
+		qcom,hpm-min-load = <5000>;
+		regulator-min-microvolt = <2750000>;
+		regulator-max-microvolt = <2750000>;
+	};
+
+	pm8026_l15: regulator-l15 {
+		compatible = "qcom,stub-regulator";
+		regulator-name = "8026_l15";
+		qcom,hpm-min-load = <10000>;
+		regulator-min-microvolt = <2800000>;
+		regulator-max-microvolt = <2800000>;
+	};
+
+	pm8026_l16: regulator-l16 {
+		compatible = "qcom,stub-regulator";
+		regulator-name = "8026_l16";
+		qcom,hpm-min-load = <10000>;
+		regulator-min-microvolt = <3000000>;
+		regulator-max-microvolt = <3000000>;
+	};
+
+	pm8026_l17: regulator-l17 {
+		compatible = "qcom,stub-regulator";
+		regulator-name = "8026_l17";
+		qcom,hpm-min-load = <10000>;
+		regulator-min-microvolt = <2950000>;
+		regulator-max-microvolt = <2950000>;
+	};
+
+	pm8026_l18: regulator-l18 {
+		compatible = "qcom,stub-regulator";
+		regulator-name = "8026_l18";
+		qcom,hpm-min-load = <10000>;
+		regulator-min-microvolt = <2950000>;
+		regulator-max-microvolt = <2950000>;
+	};
+
+	pm8026_l19: regulator-l19 {
+		compatible = "qcom,stub-regulator";
+		regulator-name = "8026_l19";
+		qcom,hpm-min-load = <10000>;
+		regulator-min-microvolt = <2850000>;
+		regulator-max-microvolt = <2850000>;
+	};
+
+	pm8026_l20: regulator-l20 {
+		compatible = "qcom,stub-regulator";
+		regulator-name = "8026_l20";
+		qcom,hpm-min-load = <5000>;
+		regulator-min-microvolt = <3075000>;
+		regulator-max-microvolt = <3075000>;
+	};
+
+	pm8026_l21: regulator-l21 {
+		compatible = "qcom,stub-regulator";
+		regulator-name = "8026_l21";
+		qcom,hpm-min-load = <5000>;
+		regulator-min-microvolt = <1800000>;
+		regulator-max-microvolt = <2950000>;
+	};
+
+	pm8026_l22: regulator-l22 {
+		compatible = "qcom,stub-regulator";
+		regulator-name = "8026_l22";
+		qcom,hpm-min-load = <10000>;
+		regulator-min-microvolt = <1800000>;
+		regulator-max-microvolt = <2950000>;
+	};
+
+	pm8026_l23: regulator-l23 {
+		compatible = "qcom,stub-regulator";
+		regulator-name = "8026_l23";
+		qcom,hpm-min-load = <10000>;
+		regulator-min-microvolt = <1800000>;
+		regulator-max-microvolt = <2950000>;
+	};
+
+	pm8026_l24: regulator-l24 {
+		compatible = "qcom,stub-regulator";
+		regulator-name = "8026_l24";
+		parent-supply = <&pm8026_s3>;
+		qcom,hpm-min-load = <10000>;
+		regulator-min-microvolt = <1300000>;
+		regulator-max-microvolt = <1300000>;
+	};
+
+	pm8026_l26: regulator-l26 {
+		compatible = "qcom,stub-regulator";
+		regulator-name = "8026_l26";
+		parent-supply = <&pm8026_s3>;
+		qcom,hpm-min-load = <10000>;
+		regulator-min-microvolt = <1225000>;
+		regulator-max-microvolt = <1225000>;
+	};
+
+	pm8026_l27: regulator-l27 {
+		compatible = "qcom,stub-regulator";
+		regulator-name = "8026_l27";
+		parent-supply = <&pm8026_s4>;
+		qcom,hpm-min-load = <10000>;
+		regulator-min-microvolt = <2050000>;
+		regulator-max-microvolt = <2050000>;
+	};
+
+	pm8026_l28: regulator-l28 {
+		compatible = "qcom,stub-regulator";
+		regulator-name = "8026_l28";
+		qcom,hpm-min-load = <10000>;
+		regulator-min-microvolt = <1800000>;
+		regulator-max-microvolt = <2950000>;
+	};
+
+	 pm8026_lvs1: regulator-lvs1 {
+		compatible = "qcom,stub-regulator";
+		regulator-name = "8026_lvs1";
+		parent-supply = <&pm8026_l6>;
+	};
+};
diff --git a/arch/arm/boot/dts/msm8226-sim.dts b/arch/arm/boot/dts/msm8226-sim.dts
index 4330849..7c25680 100644
--- a/arch/arm/boot/dts/msm8226-sim.dts
+++ b/arch/arm/boot/dts/msm8226-sim.dts
@@ -11,7 +11,7 @@
  */
 
 /dts-v1/;
-/include/ "skeleton.dtsi"
+/include/ "msm8226.dtsi"
 /include/ "msm8226-ion.dtsi"
 /include/ "msm8226-camera.dtsi"
 
@@ -19,43 +19,8 @@
 	model = "Qualcomm MSM 8226 Simulator";
 	compatible = "qcom,msm8226-sim", "qcom,msm8226";
 	qcom,msm-id = <145 1 0>;
-	interrupt-parent = <&intc>;
-
-	chosen {
-		bootargs ="root=/dev/ram rw init=/init console=ttyHSL0,115200n8 initrd=0x00000000,0x00000000 mem=512M@0x00000000";
-	};
-
-	intc: interrupt-controller@f9000000 {
-		compatible = "qcom,msm-qgic2";
-		interrupt-controller;
-		#interrupt-cells = <3>;
-		reg = <0xF9000000 0x1000>,
-		      <0xF9002000 0x1000>;
-	};
-
-	msmgpio: gpio@fd510000 {
-		compatible = "qcom,msm-gpio";
-		interrupt-controller;
-		#interrupt-cells = <2>;
-		reg = <0xfd510000 0x4000>;
-		#gpio-cells = <2>;
-	};
-
-	timer {
-		compatible = "qcom,msm-qtimer", "arm,armv7-timer";
-		interrupts = <1 2 0 1 3 0>;
-		clock-frequency = <19200000>;
-	};
 
 	serial@f991f000 {
-		compatible = "qcom,msm-lsuart-v14";
-		reg = <0xf991f000 0x1000>;
-		interrupts = <0 109 0>;
-	};
-
-	serial@f995e000 {
-		compatible = "qcom,msm-lsuart-v14";
-		reg = <0xf995e000 0x1000>;
-		interrupts = <0 114 0>;
+		status = "ok";
 	};
 };
diff --git a/arch/arm/boot/dts/msm8226.dtsi b/arch/arm/boot/dts/msm8226.dtsi
new file mode 100644
index 0000000..db2bfb3
--- /dev/null
+++ b/arch/arm/boot/dts/msm8226.dtsi
@@ -0,0 +1,77 @@
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+/include/ "skeleton.dtsi"
+
+/ {
+	model = "Qualcomm MSM 8226";
+	compatible = "qcom,msm8226";
+	interrupt-parent = <&intc>;
+
+	intc: interrupt-controller@f9000000 {
+		compatible = "qcom,msm-qgic2";
+		interrupt-controller;
+		#interrupt-cells = <3>;
+		reg = <0xF9000000 0x1000>,
+		      <0xF9002000 0x1000>;
+	};
+
+	msmgpio: gpio@fd510000 {
+		compatible = "qcom,msm-gpio";
+		interrupt-controller;
+		#interrupt-cells = <2>;
+		reg = <0xfd510000 0x4000>;
+		#gpio-cells = <2>;
+	};
+
+	timer {
+		compatible = "qcom,msm-qtimer", "arm,armv7-timer";
+		interrupts = <1 2 0 1 3 0>;
+		clock-frequency = <19200000>;
+	};
+
+	serial@f991f000 {
+		compatible = "qcom,msm-lsuart-v14";
+		reg = <0xf991f000 0x1000>;
+		interrupts = <0 109 0>;
+		status = "disabled";
+	};
+
+	serial@f995e000 {
+		compatible = "qcom,msm-lsuart-v14";
+		reg = <0xf995e000 0x1000>;
+		interrupts = <0 114 0>;
+		status = "disabled";
+	};
+
+        usb@f9a55000 {
+		compatible = "qcom,hsusb-otg";
+		reg = <0xf9a55000 0x400>;
+		interrupts = <0 134 0>;
+		interrupt-names = "core_irq";
+                HSUSB_VDDCX-supply = <&pm8026_s1>;
+                HSUSB_1p8-supply = <&pm8026_l10>;
+                HSUSB_3p3-supply = <&pm8026_l20>;
+
+		qcom,hsusb-otg-phy-type = <2>;
+		qcom,hsusb-otg-mode = <1>;
+		qcom,hsusb-otg-otg-control = <1>;
+		qcom,hsusb-otg-disable-reset;
+	};
+
+	android_usb {
+		compatible = "qcom,android-usb";
+	};
+
+};
+
+/include/ "msm8226-regulator.dtsi"
diff --git a/arch/arm/boot/dts/msm8910-regulator.dtsi b/arch/arm/boot/dts/msm8910-regulator.dtsi
new file mode 100644
index 0000000..a32d4ab
--- /dev/null
+++ b/arch/arm/boot/dts/msm8910-regulator.dtsi
@@ -0,0 +1,218 @@
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+ /* Stub Regulators */
+
+ / {
+	pm8110_s1: regulator-s1 {
+		compatible = "qcom,stub-regulator";
+		regulator-name = "8110_s1";
+		qcom,hpm-min-load = <100000>;
+		regulator-min-microvolt = <1150000>;
+		regulator-max-microvolt = <1150000>;
+	};
+
+	pm8110_s2: regulator-s2 {
+		compatible = "qcom,stub-regulator";
+		regulator-name = "8110_s2";
+		qcom,hpm-min-load = <100000>;
+		regulator-min-microvolt = <1050000>;
+		regulator-max-microvolt = <1050000>;
+	};
+
+	pm8110_s3: regulator-s3 {
+		compatible = "qcom,stub-regulator";
+		regulator-name = "8110_s3";
+		qcom,hpm-min-load = <100000>;
+		regulator-min-microvolt = <1350000>;
+		regulator-max-microvolt = <1350000>;
+	};
+
+	pm8110_s4: regulator-s4 {
+		compatible = "qcom,stub-regulator";
+		regulator-name = "8110_s4";
+		qcom,hpm-min-load = <100000>;
+		regulator-min-microvolt = <2150000>;
+		regulator-max-microvolt = <2150000>;
+	};
+
+	pm8110_l1: regulator-l1 {
+		compatible = "qcom,stub-regulator";
+		regulator-name = "8110_l1";
+		parent-supply = <&pm8110_s3>;
+		qcom,hpm-min-load = <10000>;
+		regulator-min-microvolt = <1225000>;
+		regulator-max-microvolt = <1225000>;
+	};
+
+	pm8110_l2: regulator-l2 {
+		compatible = "qcom,stub-regulator";
+		regulator-name = "8110_l2";
+		parent-supply = <&pm8110_s3>;
+		qcom,hpm-min-load = <10000>;
+		regulator-min-microvolt = <1200000>;
+		regulator-max-microvolt = <1200000>;
+	};
+
+	pm8110_l3: regulator-l3 {
+		compatible = "qcom,stub-regulator";
+		regulator-name = "8110_l3";
+		parent-supply = <&pm8110_s3>;
+		qcom,hpm-min-load = <10000>;
+		regulator-min-microvolt = <1150000>;
+		regulator-max-microvolt = <1150000>;
+	};
+
+	pm8110_l4: regulator-l4 {
+		compatible = "qcom,stub-regulator";
+		regulator-name = "8110_l4";
+		parent-supply = <&pm8110_s3>;
+		qcom,hpm-min-load = <10000>;
+		regulator-min-microvolt = <1200000>;
+		regulator-max-microvolt = <1200000>;
+	};
+
+	pm8110_l5: regulator-l5 {
+		compatible = "qcom,stub-regulator";
+		regulator-name = "8110_l5";
+		parent-supply = <&pm8110_s3>;
+		qcom,hpm-min-load = <10000>;
+		regulator-min-microvolt = <1300000>;
+		regulator-max-microvolt = <1300000>;
+	};
+
+	pm8110_l6: regulator-l6 {
+		compatible = "qcom,stub-regulator";
+		regulator-name = "8110_l6";
+		parent-supply = <&pm8110_s4>;
+		qcom,hpm-min-load = <10000>;
+		regulator-min-microvolt = <1800000>;
+		regulator-max-microvolt = <1800000>;
+	};
+
+	pm8110_l7: regulator-l7 {
+		compatible = "qcom,stub-regulator";
+		regulator-name = "8110_l7";
+		parent-supply = <&pm8110_s4>;
+		qcom,hpm-min-load = <10000>;
+		regulator-min-microvolt = <2050000>;
+		regulator-max-microvolt = <2050000>;
+	};
+
+	pm8110_l8: regulator-l8 {
+		compatible = "qcom,stub-regulator";
+		regulator-name = "8110_l8";
+		parent-supply = <&pm8110_s4>;
+		qcom,hpm-min-load = <5000>;
+		regulator-min-microvolt = <1800000>;
+		regulator-max-microvolt = <1800000>;
+	};
+
+	pm8110_l9: regulator-l9 {
+		compatible = "qcom,stub-regulator";
+		regulator-name = "8110_l9";
+		parent-supply = <&pm8110_s4>;
+		qcom,hpm-min-load = <10000>;
+		regulator-min-microvolt = <2050000>;
+		regulator-max-microvolt = <2050000>;
+	};
+
+	pm8110_l10: regulator-l10 {
+		compatible = "qcom,stub-regulator";
+		regulator-name = "8110_l10";
+		parent-supply = <&pm8110_s4>;
+		qcom,hpm-min-load = <10000>;
+		regulator-min-microvolt = <1800000>;
+		regulator-max-microvolt = <1800000>;
+	};
+
+	pm8110_l12: regulator-l12 {
+		compatible = "qcom,stub-regulator";
+		regulator-name = "8110_l12";
+		qcom,hpm-min-load = <10000>;
+		regulator-min-microvolt = <1800000>;
+		regulator-max-microvolt = <3300000>;
+	};
+
+	pm8110_l14: regulator-l14 {
+		compatible = "qcom,stub-regulator";
+		regulator-name = "8110_l14";
+		parent-supply = <&pm8110_s4>;
+		qcom,hpm-min-load = <10000>;
+		regulator-min-microvolt = <1800000>;
+		regulator-max-microvolt = <1800000>;
+	};
+
+	pm8110_l15: regulator-l15 {
+		compatible = "qcom,stub-regulator";
+		regulator-name = "8110_l15";
+		qcom,hpm-min-load = <10000>;
+		regulator-min-microvolt = <1800000>;
+		regulator-max-microvolt = <3300000>;
+	};
+
+	pm8110_l16: regulator-l16 {
+		compatible = "qcom,stub-regulator";
+		regulator-name = "8110_l16";
+		qcom,hpm-min-load = <10000>;
+		regulator-min-microvolt = <3000000>;
+		regulator-max-microvolt = <3000000>;
+	};
+
+	pm8110_l17: regulator-l17 {
+		compatible = "qcom,stub-regulator";
+		regulator-name = "8110_l17";
+		qcom,hpm-min-load = <10000>;
+		regulator-min-microvolt = <2900000>;
+		regulator-max-microvolt = <2900000>;
+	};
+
+	pm8110_l18: regulator-l18 {
+		compatible = "qcom,stub-regulator";
+		regulator-name = "8110_l18";
+		qcom,hpm-min-load = <10000>;
+		regulator-min-microvolt = <1800000>;
+		regulator-max-microvolt = <2950000>;
+	};
+
+	pm8110_l19: regulator-l19 {
+		compatible = "qcom,stub-regulator";
+		regulator-name = "8110_l19";
+		qcom,hpm-min-load = <10000>;
+		regulator-min-microvolt = <2850000>;
+		regulator-max-microvolt = <2850000>;
+	};
+
+	pm8110_l20: regulator-l20 {
+		compatible = "qcom,stub-regulator";
+		regulator-name = "8110_l20";
+		qcom,hpm-min-load = <5000>;
+		regulator-min-microvolt = <3075000>;
+		regulator-max-microvolt = <3075000>;
+	};
+
+	pm8110_l21: regulator-l21 {
+		compatible = "qcom,stub-regulator";
+		regulator-name = "8110_l21";
+		qcom,hpm-min-load = <10000>;
+		regulator-min-microvolt = <1800000>;
+		regulator-max-microvolt = <2950000>;
+	};
+
+	pm8110_l22: regulator-l22 {
+		compatible = "qcom,stub-regulator";
+		regulator-name = "8110_l22";
+		qcom,hpm-min-load = <10000>;
+		regulator-min-microvolt = <1800000>;
+		regulator-max-microvolt = <3300000>;
+	};
+};
diff --git a/arch/arm/boot/dts/msm8910-sim.dts b/arch/arm/boot/dts/msm8910-sim.dts
new file mode 100644
index 0000000..aae88b1
--- /dev/null
+++ b/arch/arm/boot/dts/msm8910-sim.dts
@@ -0,0 +1,25 @@
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+/dts-v1/;
+
+/include/ "msm8910.dtsi"
+
+/ {
+	model = "Qualcomm MSM 8910 Simulator";
+	compatible = "qcom,msm8910-sim", "qcom,msm8910";
+	qcom,msm-id = <147 1 0>;
+
+	serial@f991f000 {
+		status = "ok";
+	};
+};
diff --git a/arch/arm/boot/dts/msm8910.dtsi b/arch/arm/boot/dts/msm8910.dtsi
new file mode 100644
index 0000000..a2e1338
--- /dev/null
+++ b/arch/arm/boot/dts/msm8910.dtsi
@@ -0,0 +1,108 @@
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+/include/ "skeleton.dtsi"
+
+/ {
+	model = "Qualcomm MSM 8910";
+	compatible = "qcom,msm8910";
+	interrupt-parent = <&intc>;
+
+	intc: interrupt-controller@f9000000 {
+		compatible = "qcom,msm-qgic2";
+		interrupt-controller;
+		#interrupt-cells = <3>;
+		reg = <0xf9000000 0x1000>,
+		      <0xf9002000 0x1000>;
+	};
+
+	msmgpio: gpio@fd510000 {
+		compatible = "qcom,msm-gpio";
+		interrupt-controller;
+		#interrupt-cells = <2>;
+		reg = <0xfd510000 0x4000>;
+		#gpio-cells = <2>;
+	};
+
+	timer {
+		compatible = "qcom,msm-qtimer", "arm,armv7-timer";
+		interrupts = <1 2 0 1 3 0>;
+		clock-frequency = <19200000>;
+	};
+
+	serial@f991f000 {
+		compatible = "qcom,msm-lsuart-v14";
+		reg = <0xf991f000 0x1000>;
+		interrupts = <0 109 0>;
+		status = "disabled";
+	};
+
+	usb@f9a55000 {
+		compatible = "qcom,hsusb-otg";
+		reg = <0xf9a55000 0x400>;
+		interrupts = <0 134 0>;
+		interrupt-names = "core_irq";
+
+		qcom,hsusb-otg-phy-type = <2>;
+		qcom,hsusb-otg-mode = <1>;
+		qcom,hsusb-otg-otg-control = <1>;
+		qcom,hsusb-otg-disable-reset;
+	};
+
+	android_usb {
+		compatible = "qcom,android-usb";
+	};
+
+	sdcc1: qcom,sdcc@f9824000 {
+		cell-index = <1>; /* SDC1 eMMC slot */
+		compatible = "qcom,msm-sdcc";
+		reg = <0xf9824000 0x800>;
+		reg-names = "core_mem";
+		interrupts = <0 123 0>;
+		interrupt-names = "core_irq";
+
+		qcom,sdcc-pad-pull-on = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
+		qcom,sdcc-pad-pull-off = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
+		qcom,sdcc-pad-drv-on = <0x7 0x4 0x4>; /* 16mA, 10mA, 10mA */
+		qcom,sdcc-pad-drv-off = <0x0 0x0 0x0>; /* 2mA, 2mA, 2mA */
+
+		qcom,sdcc-clk-rates = <400000 25000000 50000000 100000000 200000000>;
+		qcom,sdcc-sup-voltages = <2950 2950>;
+		qcom,sdcc-bus-width = <8>;
+		qcom,sdcc-nonremovable;
+		qcom,sdcc-bus-speed-mode = "HS200_1p8v", "DDR_1p8v";
+	};
+
+	sdcc2: qcom,sdcc@f98a4000 {
+		cell-index = <2>; /* SDC2 SD card slot */
+		compatible = "qcom,msm-sdcc";
+		reg = <0xf98a4000 0x800>;
+		reg-names = "core_mem";
+		interrupts = <0 125 0>;
+		interrupt-names = "core_irq";
+
+		qcom,sdcc-pad-pull-on = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
+		qcom,sdcc-pad-pull-off = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
+		qcom,sdcc-pad-drv-on = <0x7 0x4 0x4>; /* 16mA, 10mA, 10mA */
+		qcom,sdcc-pad-drv-off = <0x0 0x0 0x0>; /* 2mA, 2mA, 2mA */
+
+		qcom,sdcc-clk-rates = <400000 25000000 50000000 100000000 200000000>;
+		qcom,sdcc-sup-voltages = <2950 2950>;
+		qcom,sdcc-bus-width = <4>;
+		qcom,sdcc-xpc;
+		qcom,sdcc-bus-speed-mode = "SDR12", "SDR25", "SDR50", "DDR50", "SDR104";
+		qcom,sdcc-current-limit = <800>;
+	};
+
+};
+
+/include/ "msm8910-regulator.dtsi"
diff --git a/arch/arm/boot/dts/msm8974-cdp.dts b/arch/arm/boot/dts/msm8974-cdp.dts
index aff0adc..b8b3141 100644
--- a/arch/arm/boot/dts/msm8974-cdp.dts
+++ b/arch/arm/boot/dts/msm8974-cdp.dts
@@ -13,351 +13,10 @@
 /dts-v1/;
 
 /include/ "msm8974.dtsi"
-/include/ "dsi-panel-toshiba-720p-video.dtsi"
+/include/ "msm8974-cdp.dtsi"
 
 / {
 	model = "Qualcomm MSM 8974 CDP";
 	compatible = "qcom,msm8974-cdp", "qcom,msm8974";
 	qcom,msm-id = <126 1 0>;
-
-	serial@f991e000 {
-		status = "ok";
-	};
-
-	qcom,mdss_dsi@fd922800 {
-		qcom,mdss_dsi_toshiba_720p_video {
-			status = "ok";
-		};
-	};
-
-	i2c@f9924000 {
-		atmel_mxt_ts@4a {
-			compatible = "atmel,mxt-ts";
-			reg = <0x4a>;
-			interrupt-parent = <&msmgpio>;
-			interrupts = <61 0x2>;
-			vdd_ana-supply = <&pm8941_l18>;
-			vcc_i2c-supply = <&pm8941_lvs1>;
-			atmel,reset-gpio = <&msmgpio 60 0x00>;
-			atmel,irq-gpio = <&msmgpio 61 0x00>;
-			atmel,panel-coords = <0  0 760 1424>;
-			atmel,display-coords = <0 0 720 1280>;
-			atmel,i2c-pull-up = <1>;
-			atmel,cfg_1 {
-				atmel,family-id = <0x82>;
-				atmel,variant-id = <0x19>;
-				atmel,version = <0x10>;
-				atmel,build = <0xaa>;
-				atmel,config = [
-					/* Object 6, Instance = 0 */
-					00 00 00 00 00 00
-					/* Object 38, Instance = 0 */
-					15 00 02 10 08 0C 00 00
-					/* Object 7, Instance = 0 */
-					FF FF 32 03
-					/* Object 8, Instance = 0 */
-					0F 00 0A 0A 00 00 0A 00 00 00
-					/* Object 9, Instance = 0 */
-					83 00 00 18 0E 00 70 32 02 01
-					00 03 01 01 05 0A 0A 0A 90 05
-					F8 02 00 00 0F 0F 00 00 48 2D
-					07 0C 00 00 00 00
-					/* Object 15, Instance = 0 */
-					00 00 00 00 00 00 00 00 00 00
-					00
-					/* Object 18, Instance = 0 */
-					00 00
-					/* Object 19, Instance = 0 */
-					00 00 00 00 00 00
-					/* Object 23, Instance = 0 */
-					00 00 00 00 00 00 00 00 00 00
-					00 00 00 00 00
-					/* Object 25, Instance = 0 */
-					00 00 00 00 00 00 00 00 00 00
-					00 00 00 00 00
-					/* Object 40, Instance = 0 */
-					00 00 00 00 00
-					/* Object 42, Instance = 0 */
-					00 00 00 00 00 00 00 00 00 00
-					/* Object 46, Instance = 0 */
-					00 00 10 10 00 00 03 00 00 01
-					/* Object 47, Instance = 0 */
-					08 0A 28 0A 02 0A 00 8C 00 20
-					00 00 00
-					/* Object 55, Instance = 0 */
-					00 00 00 00 00 00
-					/* Object 56, Instance = 0 */
-					03 00 01 18 05 05 05 05 05 05
-					05 05 05 05 05 05 05 05 05 05
-					05 05 05 05 05 05 05 05 00 00
-					00 00 00 00 00 00 00 00 00 00
-					00 00
-					/* Object 57, Instance = 0 */
-					00 00 00
-					/* Object 61, Instance = 0 */
-					00 00 00 00 00
-					/* Object 61, Instance = 1 */
-					00 00 00 00 00
-					/* Object 62, Instance = 0 */
-					7F 03 00 16 00 00 00 00 00 00
-					04 08 10 18 05 00 0A 05 05 50
-					14 19 34 1A 64 00 00 04 40 00
-					00 00 00 00 30 32 02 00 01 00
-					05 00 00 00 00 00 00 00 00 00
-					00 00 0C 00
-					];
-			};
-		};
-	};
-
-	gpio_keys {
-		compatible = "gpio-keys";
-
-		camera_snapshot {
-			label = "camera_snapshot";
-			gpios = <&pm8941_gpios 3 0x1>;
-			linux,input-type = <1>;
-			linux,code = <0x2fe>;
-			gpio-key,wakeup;
-			debounce-interval = <15>;
-		};
-
-		camera_focus {
-			label = "camera_focus";
-			gpios = <&pm8941_gpios 4 0x1>;
-			linux,input-type = <1>;
-			linux,code = <0x210>;
-			gpio-key,wakeup;
-			debounce-interval = <15>;
-		};
-
-		vol_up {
-			label = "volume_up";
-			gpios = <&pm8941_gpios 5 0x1>;
-			linux,input-type = <1>;
-			linux,code = <115>;
-			gpio-key,wakeup;
-			debounce-interval = <15>;
-		};
-	};
-
-	spi@f9923000 {
-		ethernet-switch@2 {
-			compatible = "micrel,ks8851";
-			reg = <2>;
-			interrupt-parent = <&msmgpio>;
-			interrupts = <94 0>;
-			spi-max-frequency = <4800000>;
-			rst-gpio = <&pm8941_mpps 6 0>;
-			vdd-io-supply = <&spi_eth_vreg>;
-			vdd-phy-supply = <&spi_eth_vreg>;
-		};
-	};
-};
-
-&sdcc2 {
-	#address-cells = <0>;
-	interrupt-parent = <&sdcc2>;
-	interrupts = <0 1 2>;
-	#interrupt-cells = <1>;
-	interrupt-map-mask = <0xffffffff>;
-	interrupt-map = <0 &intc 0 125 0
-			1 &intc 0 220 0
-			2 &msmgpio 62 0x3>;
-	interrupt-names = "core_irq", "bam_irq", "status_irq";
-	cd-gpios = <&msmgpio 62 0x1>;
-	wp-gpios = <&pm8941_gpios 29 0x1>;
-};
-
-&pm8941_gpios {
-	gpio@c000 { /* GPIO 1 */
-	};
-
-	gpio@c100 { /* GPIO 2 */
-	};
-
-	gpio@c200 { /* GPIO 3 */
-		qcom,mode = <0>;
-		qcom,pull = <0>;
-		qcom,vin-sel = <2>;
-		qcom,select = <0>;
-	};
-
-	gpio@c300 { /* GPIO 4 */
-		qcom,mode = <0>;
-		qcom,pull = <0>;
-		qcom,vin-sel = <2>;
-		qcom,select = <0>;
-	};
-
-	gpio@c400 { /* GPIO 5 */
-		qcom,mode = <0>;
-		qcom,pull = <0>;
-		qcom,vin-sel = <2>;
-		qcom,select = <0>;
-	};
-
-	gpio@c500 { /* GPIO 6 */
-	};
-
-	gpio@c600 { /* GPIO 7 */
-	};
-
-	gpio@c700 { /* GPIO 8 */
-	};
-
-	gpio@c800 { /* GPIO 9 */
-	};
-
-	gpio@c900 { /* GPIO 10 */
-	};
-
-	gpio@ca00 { /* GPIO 11 */
-	};
-
-	gpio@cb00 { /* GPIO 12 */
-	};
-
-	gpio@cc00 { /* GPIO 13 */
-	};
-
-	gpio@cd00 { /* GPIO 14 */
-	};
-
-	gpio@ce00 { /* GPIO 15 */
-		qcom,mode = <1>;
-		qcom,output-type = <0>;
-		qcom,pull = <5>;
-		qcom,vin-sel = <2>;
-		qcom,out-strength = <3>;
-		qcom,src-select = <2>;
-		qcom,master-en = <1>;
-	};
-
-	gpio@cf00 { /* GPIO 16 */
-	};
-
-	gpio@d000 { /* GPIO 17 */
-	};
-
-	gpio@d100 { /* GPIO 18 */
-	};
-
-	gpio@d200 { /* GPIO 19 */
-		qcom,mode = <1>;		/* QPNP_PIN_MODE_DIG_OUT */
-		qcom,output-type = <0>;		/* QPNP_PIN_OUT_BUF_CMOS */
-		qcom,pull = <5>;		/* QPNP_PIN_PULL_NO */
-		qcom,vin-sel = <2>;		/* QPNP_PIN_VIN2 */
-		qcom,out-strength = <2>;	/* QPNP_PIN_OUT_STRENGTH_MED */
-		qcom,src-select = <0>;		/* QPNP_PIN_SEL_FUNC_CONSTANT */
-		qcom,master-en = <1>;
-	};
-
-	gpio@d300 { /* GPIO 20 */
-	};
-
-	gpio@d400 { /* GPIO 21 */
-	};
-
-	gpio@d500 { /* GPIO 22 */
-	};
-
-	gpio@d600 { /* GPIO 23 */
-	};
-
-	gpio@d700 { /* GPIO 24 */
-	};
-
-	gpio@d800 { /* GPIO 25 */
-	};
-
-	gpio@d900 { /* GPIO 26 */
-	};
-
-	gpio@da00 { /* GPIO 27 */
-	};
-
-	gpio@db00 { /* GPIO 28 */
-	};
-
-	gpio@dc00 { /* GPIO 29 */
-		qcom,pull = <0>; /* set to default pull */
-		qcom,master-en = <1>;
-		qcom,vin-sel = <2>; /* select 1.8 V source */
-	};
-
-	gpio@dd00 { /* GPIO 30 */
-	};
-
-	gpio@de00 { /* GPIO 31 */
-	};
-
-	gpio@df00 { /* GPIO 32 */
-	};
-
-	gpio@e000 { /* GPIO 33 */
-	};
-
-	gpio@e100 { /* GPIO 34 */
-	};
-
-	gpio@e200 { /* GPIO 35 */
-	};
-
-	gpio@e300 { /* GPIO 36 */
-	};
-};
-
-&pm8941_mpps {
-
-	mpp@a000 { /* MPP 1 */
-	};
-
-	mpp@a100 { /* MPP 2 */
-	};
-
-	mpp@a200 { /* MPP 3 */
-	};
-
-	mpp@a300 { /* MPP 4 */
-	};
-
-	mpp@a400 { /* MPP 5 */
-		/* SPI_ETH config */
-		qcom,mode = <1>; /* DIG_OUT */
-		qcom,output-type = <0>; /* CMOS */
-		qcom,vin-sel = <2>; /* PM8941_S3 1.8V > 1.6V */
-		qcom,src-select = <0>; /* CONSTANT */
-		qcom,master-en = <1>; /* ENABLE MPP */
-	};
-
-	mpp@a500 { /* MPP 6 */
-		/* SPI_ETH_RST config */
-		qcom,mode = <1>; /* DIG_OUT */
-		qcom,output-type = <0>; /* CMOS */
-		qcom,vin-sel = <2>; /* PM8941_S3 1.8V > 1.6V */
-		qcom,src-select = <0>; /* CONSTANT */
-		qcom,master-en = <1>; /* ENABLE MPP */
-	};
-
-	mpp@a600 { /* MPP 7 */
-	};
-
-	mpp@a700 { /* MPP 8 */
-	};
-};
-
-&pm8841_mpps {
-
-	mpp@a000 { /* MPP 1 */
-	};
-
-	mpp@a100 { /* MPP 2 */
-	};
-
-	mpp@a200 { /* MPP 3 */
-	};
-
-	mpp@a300 { /* MPP 4 */
-	};
 };
diff --git a/arch/arm/boot/dts/msm8974-cdp.dtsi b/arch/arm/boot/dts/msm8974-cdp.dtsi
new file mode 100644
index 0000000..6004d15
--- /dev/null
+++ b/arch/arm/boot/dts/msm8974-cdp.dtsi
@@ -0,0 +1,362 @@
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+/include/ "dsi-panel-toshiba-720p-video.dtsi"
+
+/ {
+	serial@f991e000 {
+		status = "ok";
+	};
+
+	qcom,mdss_dsi@fd922800 {
+		qcom,mdss_dsi_toshiba_720p_video {
+			status = "ok";
+		};
+	};
+
+	qcom,hdmi_tx@fd922100 {
+		status = "ok";
+	};
+
+	i2c@f9924000 {
+		atmel_mxt_ts@4a {
+			compatible = "atmel,mxt-ts";
+			reg = <0x4a>;
+			interrupt-parent = <&msmgpio>;
+			interrupts = <61 0x2>;
+			vdd_ana-supply = <&pm8941_l18>;
+			vcc_i2c-supply = <&pm8941_lvs1>;
+			atmel,reset-gpio = <&msmgpio 60 0x00>;
+			atmel,irq-gpio = <&msmgpio 61 0x00>;
+			atmel,panel-coords = <0  0 760 1424>;
+			atmel,display-coords = <0 0 720 1280>;
+			atmel,i2c-pull-up;
+			atmel,no-force-update;
+			atmel,cfg_1 {
+				atmel,family-id = <0x82>;
+				atmel,variant-id = <0x19>;
+				atmel,version = <0x10>;
+				atmel,build = <0xaa>;
+				atmel,config = [
+					/* Object 6, Instance = 0 */
+					00 00 00 00 00 00
+					/* Object 38, Instance = 0 */
+					15 00 02 10 08 0C 00 00
+					/* Object 7, Instance = 0 */
+					FF FF 32 03
+					/* Object 8, Instance = 0 */
+					0F 00 0A 0A 00 00 0A 00 00 00
+					/* Object 9, Instance = 0 */
+					83 00 00 18 0E 00 70 32 02 01
+					00 03 01 01 05 0A 0A 0A 90 05
+					F8 02 00 00 0F 0F 00 00 48 2D
+					07 0C 00 00 00 00
+					/* Object 15, Instance = 0 */
+					00 00 00 00 00 00 00 00 00 00
+					00
+					/* Object 18, Instance = 0 */
+					00 00
+					/* Object 19, Instance = 0 */
+					00 00 00 00 00 00
+					/* Object 23, Instance = 0 */
+					00 00 00 00 00 00 00 00 00 00
+					00 00 00 00 00
+					/* Object 25, Instance = 0 */
+					00 00 00 00 00 00 00 00 00 00
+					00 00 00 00 00
+					/* Object 40, Instance = 0 */
+					00 00 00 00 00
+					/* Object 42, Instance = 0 */
+					00 00 00 00 00 00 00 00 00 00
+					/* Object 46, Instance = 0 */
+					00 00 10 10 00 00 03 00 00 01
+					/* Object 47, Instance = 0 */
+					08 0A 28 0A 02 0A 00 8C 00 20
+					00 00 00
+					/* Object 55, Instance = 0 */
+					00 00 00 00 00 00
+					/* Object 56, Instance = 0 */
+					03 00 01 18 05 05 05 05 05 05
+					05 05 05 05 05 05 05 05 05 05
+					05 05 05 05 05 05 05 05 00 00
+					00 00 00 00 00 00 00 00 00 00
+					00 00
+					/* Object 57, Instance = 0 */
+					00 00 00
+					/* Object 61, Instance = 0 */
+					00 00 00 00 00
+					/* Object 61, Instance = 1 */
+					00 00 00 00 00
+					/* Object 62, Instance = 0 */
+					7F 03 00 16 00 00 00 00 00 00
+					04 08 10 18 05 00 0A 05 05 50
+					14 19 34 1A 64 00 00 04 40 00
+					00 00 00 00 30 32 02 00 01 00
+					05 00 00 00 00 00 00 00 00 00
+					00 00 0C 00
+					];
+			};
+		};
+	};
+
+	gpio_keys {
+		compatible = "gpio-keys";
+		input-name = "gpio-keys";
+
+		camera_snapshot {
+			label = "camera_snapshot";
+			gpios = <&pm8941_gpios 3 0x1>;
+			linux,input-type = <1>;
+			linux,code = <0x2fe>;
+			gpio-key,wakeup;
+			debounce-interval = <15>;
+		};
+
+		camera_focus {
+			label = "camera_focus";
+			gpios = <&pm8941_gpios 4 0x1>;
+			linux,input-type = <1>;
+			linux,code = <0x210>;
+			gpio-key,wakeup;
+			debounce-interval = <15>;
+		};
+
+		vol_up {
+			label = "volume_up";
+			gpios = <&pm8941_gpios 5 0x1>;
+			linux,input-type = <1>;
+			linux,code = <115>;
+			gpio-key,wakeup;
+			debounce-interval = <15>;
+		};
+	};
+
+	spi@f9923000 {
+		ethernet-switch@2 {
+			compatible = "micrel,ks8851";
+			reg = <2>;
+			interrupt-parent = <&msmgpio>;
+			interrupts = <94 0>;
+			spi-max-frequency = <4800000>;
+			rst-gpio = <&pm8941_mpps 6 0>;
+			vdd-io-supply = <&spi_eth_vreg>;
+			vdd-phy-supply = <&spi_eth_vreg>;
+		};
+	};
+};
+
+&sdcc2 {
+	#address-cells = <0>;
+	interrupt-parent = <&sdcc2>;
+	interrupts = <0 1 2>;
+	#interrupt-cells = <1>;
+	interrupt-map-mask = <0xffffffff>;
+	interrupt-map = <0 &intc 0 125 0
+			1 &intc 0 220 0
+			2 &msmgpio 62 0x3>;
+	interrupt-names = "core_irq", "bam_irq", "status_irq";
+	cd-gpios = <&msmgpio 62 0x1>;
+	wp-gpios = <&pm8941_gpios 29 0x1>;
+};
+
+&pm8941_gpios {
+	gpio@c000 { /* GPIO 1 */
+	};
+
+	gpio@c100 { /* GPIO 2 */
+	};
+
+	gpio@c200 { /* GPIO 3 */
+		qcom,mode = <0>;
+		qcom,pull = <0>;
+		qcom,vin-sel = <2>;
+		qcom,src-sel = <0>;
+	};
+
+	gpio@c300 { /* GPIO 4 */
+		qcom,mode = <0>;
+		qcom,pull = <0>;
+		qcom,vin-sel = <2>;
+		qcom,src-sel = <0>;
+	};
+
+	gpio@c400 { /* GPIO 5 */
+		qcom,mode = <0>;
+		qcom,pull = <0>;
+		qcom,vin-sel = <2>;
+		qcom,src-sel = <0>;
+	};
+
+	gpio@c500 { /* GPIO 6 */
+	};
+
+	gpio@c600 { /* GPIO 7 */
+	};
+
+	gpio@c700 { /* GPIO 8 */
+	};
+
+	gpio@c800 { /* GPIO 9 */
+	};
+
+	gpio@c900 { /* GPIO 10 */
+	};
+
+	gpio@ca00 { /* GPIO 11 */
+	};
+
+	gpio@cb00 { /* GPIO 12 */
+	};
+
+	gpio@cc00 { /* GPIO 13 */
+	};
+
+	gpio@cd00 { /* GPIO 14 */
+	};
+
+	gpio@ce00 { /* GPIO 15 */
+		qcom,mode = <1>;
+		qcom,output-type = <0>;
+		qcom,pull = <5>;
+		qcom,vin-sel = <2>;
+		qcom,out-strength = <3>;
+		qcom,src-sel = <2>;
+		qcom,master-en = <1>;
+	};
+
+	gpio@cf00 { /* GPIO 16 */
+	};
+
+	gpio@d000 { /* GPIO 17 */
+	};
+
+	gpio@d100 { /* GPIO 18 */
+	};
+
+	gpio@d200 { /* GPIO 19 */
+		qcom,mode = <1>;		/* QPNP_PIN_MODE_DIG_OUT */
+		qcom,output-type = <0>;		/* QPNP_PIN_OUT_BUF_CMOS */
+		qcom,pull = <5>;		/* QPNP_PIN_PULL_NO */
+		qcom,vin-sel = <2>;		/* QPNP_PIN_VIN2 */
+		qcom,out-strength = <2>;	/* QPNP_PIN_OUT_STRENGTH_MED */
+		qcom,src-sel = <0>;		/* QPNP_PIN_SEL_FUNC_CONSTANT */
+		qcom,master-en = <1>;
+	};
+
+	gpio@d300 { /* GPIO 20 */
+	};
+
+	gpio@d400 { /* GPIO 21 */
+	};
+
+	gpio@d500 { /* GPIO 22 */
+	};
+
+	gpio@d600 { /* GPIO 23 */
+	};
+
+	gpio@d700 { /* GPIO 24 */
+	};
+
+	gpio@d800 { /* GPIO 25 */
+	};
+
+	gpio@d900 { /* GPIO 26 */
+	};
+
+	gpio@da00 { /* GPIO 27 */
+	};
+
+	gpio@db00 { /* GPIO 28 */
+	};
+
+	gpio@dc00 { /* GPIO 29 */
+		qcom,pull = <0>; /* set to default pull */
+		qcom,master-en = <1>;
+		qcom,vin-sel = <2>; /* select 1.8 V source */
+	};
+
+	gpio@dd00 { /* GPIO 30 */
+	};
+
+	gpio@de00 { /* GPIO 31 */
+	};
+
+	gpio@df00 { /* GPIO 32 */
+	};
+
+	gpio@e000 { /* GPIO 33 */
+	};
+
+	gpio@e100 { /* GPIO 34 */
+	};
+
+	gpio@e200 { /* GPIO 35 */
+	};
+
+	gpio@e300 { /* GPIO 36 */
+	};
+};
+
+&pm8941_mpps {
+
+	mpp@a000 { /* MPP 1 */
+	};
+
+	mpp@a100 { /* MPP 2 */
+	};
+
+	mpp@a200 { /* MPP 3 */
+	};
+
+	mpp@a300 { /* MPP 4 */
+	};
+
+	mpp@a400 { /* MPP 5 */
+		/* SPI_ETH config */
+		qcom,mode = <1>; /* DIG_OUT */
+		qcom,output-type = <0>; /* CMOS */
+		qcom,vin-sel = <2>; /* PM8941_S3 1.8V > 1.6V */
+		qcom,src-sel = <0>; /* CONSTANT */
+		qcom,master-en = <1>; /* ENABLE MPP */
+	};
+
+	mpp@a500 { /* MPP 6 */
+		/* SPI_ETH_RST config */
+		qcom,mode = <1>; /* DIG_OUT */
+		qcom,output-type = <0>; /* CMOS */
+		qcom,vin-sel = <2>; /* PM8941_S3 1.8V > 1.6V */
+		qcom,src-sel = <0>; /* CONSTANT */
+		qcom,master-en = <1>; /* ENABLE MPP */
+	};
+
+	mpp@a600 { /* MPP 7 */
+	};
+
+	mpp@a700 { /* MPP 8 */
+	};
+};
+
+&pm8841_mpps {
+
+	mpp@a000 { /* MPP 1 */
+	};
+
+	mpp@a100 { /* MPP 2 */
+	};
+
+	mpp@a200 { /* MPP 3 */
+	};
+
+	mpp@a300 { /* MPP 4 */
+	};
+};
diff --git a/arch/arm/boot/dts/msm8974-fluid.dts b/arch/arm/boot/dts/msm8974-fluid.dts
index b1d467e..b014e14 100644
--- a/arch/arm/boot/dts/msm8974-fluid.dts
+++ b/arch/arm/boot/dts/msm8974-fluid.dts
@@ -13,354 +13,10 @@
 /dts-v1/;
 
 /include/ "msm8974.dtsi"
-/include/ "dsi-panel-toshiba-720p-video.dtsi"
+/include/ "msm8974-fluid.dtsi"
 
 / {
 	model = "Qualcomm MSM 8974 FLUID";
 	compatible = "qcom,msm8974-fluid", "qcom,msm8974";
 	qcom,msm-id = <126 3 0>;
-
-	serial@f991e000 {
-		status = "ok";
-	};
-
-	qcom,mdss_dsi@fd922800 {
-		qcom,mdss_dsi_toshiba_720p_video {
-			status = "ok";
-		};
-	};
-
-	i2c@f9924000 {
-		atmel_mxt_ts@4a {
-			compatible = "atmel,mxt-ts";
-			reg = <0x4a>;
-			interrupt-parent = <&msmgpio>;
-			interrupts = <61 0x2>;
-			vdd_ana-supply = <&pm8941_l18>;
-			vcc_i2c-supply = <&pm8941_lvs1>;
-			atmel,reset-gpio = <&msmgpio 60 0x00>;
-			atmel,irq-gpio = <&msmgpio 61 0x00>;
-			atmel,panel-coords = <0  0 760 1424>;
-			atmel,display-coords = <0 0 720 1280>;
-			atmel,i2c-pull-up = <1>;
-			atmel,cfg_1 {
-				atmel,family-id = <0x82>;
-				atmel,variant-id = <0x19>;
-				atmel,version = <0x10>;
-				atmel,build = <0xaa>;
-				atmel,config = [
-					/* Object 6, Instance = 0 */
-					00 00 00 00 00 00
-					/* Object 38, Instance = 0 */
-					15 00 02 10 08 0C 00 00
-					/* Object 7, Instance = 0 */
-					FF FF 32 03
-					/* Object 8, Instance = 0 */
-					0F 00 0A 0A 00 00 0A 00 00 00
-					/* Object 9, Instance = 0 */
-					83 00 00 18 0E 00 70 32 02 01
-					00 03 01 01 05 0A 0A 0A 90 05
-					F8 02 00 00 0F 0F 00 00 48 2D
-					07 0C 00 00 00 00
-					/* Object 15, Instance = 0 */
-					00 00 00 00 00 00 00 00 00 00
-					00
-					/* Object 18, Instance = 0 */
-					00 00
-					/* Object 19, Instance = 0 */
-					00 00 00 00 00 00
-					/* Object 23, Instance = 0 */
-					00 00 00 00 00 00 00 00 00 00
-					00 00 00 00 00
-					/* Object 25, Instance = 0 */
-					00 00 00 00 00 00 00 00 00 00
-					00 00 00 00 00
-					/* Object 40, Instance = 0 */
-					00 00 00 00 00
-					/* Object 42, Instance = 0 */
-					00 00 00 00 00 00 00 00 00 00
-					/* Object 46, Instance = 0 */
-					00 00 10 10 00 00 03 00 00 01
-					/* Object 47, Instance = 0 */
-					08 0A 28 0A 02 0A 00 8C 00 20
-					00 00 00
-					/* Object 55, Instance = 0 */
-					00 00 00 00 00 00
-					/* Object 56, Instance = 0 */
-					03 00 01 18 05 05 05 05 05 05
-					05 05 05 05 05 05 05 05 05 05
-					05 05 05 05 05 05 05 05 00 00
-					00 00 00 00 00 00 00 00 00 00
-					00 00
-					/* Object 57, Instance = 0 */
-					00 00 00
-					/* Object 61, Instance = 0 */
-					00 00 00 00 00
-					/* Object 61, Instance = 1 */
-					00 00 00 00 00
-					/* Object 62, Instance = 0 */
-					7F 03 00 16 00 00 00 00 00 00
-					04 08 10 18 05 00 0A 05 05 50
-					14 19 34 1A 64 00 00 04 40 00
-					00 00 00 00 30 32 02 00 01 00
-					05 00 00 00 00 00 00 00 00 00
-					00 00 0C 00
-					];
-			};
-		};
-	};
-
-	gpio_keys {
-		compatible = "gpio-keys";
-
-		camera_snapshot {
-			label = "camera_snapshot";
-			gpios = <&pm8941_gpios 3 0x1>;
-			linux,input-type = <1>;
-			linux,code = <0x2fe>;
-			gpio-key,wakeup;
-			debounce-interval = <15>;
-		};
-
-		camera_focus {
-			label = "camera_focus";
-			gpios = <&pm8941_gpios 4 0x1>;
-			linux,input-type = <1>;
-			linux,code = <0x210>;
-			gpio-key,wakeup;
-			debounce-interval = <15>;
-		};
-
-		vol_up {
-			label = "volume_up";
-			gpios = <&pm8941_gpios 5 0x1>;
-			linux,input-type = <1>;
-			linux,code = <115>;
-			gpio-key,wakeup;
-			debounce-interval = <15>;
-		};
-	};
-
-	spi@f9923000 {
-		ethernet-switch@2 {
-			compatible = "micrel,ks8851";
-			reg = <2>;
-			interrupt-parent = <&msmgpio>;
-			interrupts = <94 0>;
-			spi-max-frequency = <4800000>;
-			rst-gpio = <&pm8941_mpps 6 0>;
-			vdd-io-supply = <&spi_eth_vreg>;
-			vdd-phy-supply = <&spi_eth_vreg>;
-		};
-	};
-};
-
-&sdcc1 {
-	qcom,sdcc-bus-width = <4>;
-};
-
-&sdcc2 {
-	#address-cells = <0>;
-	interrupt-parent = <&sdcc2>;
-	interrupts = <0 1 2>;
-	#interrupt-cells = <1>;
-	interrupt-map-mask = <0xffffffff>;
-	interrupt-map = <0 &intc 0 125 0
-			1 &intc 0 220 0
-			2 &msmgpio 62 0x3>;
-	interrupt-names = "core_irq", "bam_irq", "status_irq";
-	cd-gpios = <&msmgpio 62 0x1>;
-};
-
-&pm8941_gpios {
-	gpio@c000 { /* GPIO 1 */
-	};
-
-	gpio@c100 { /* GPIO 2 */
-	};
-
-	gpio@c200 { /* GPIO 3 */
-		qcom,mode = <0>;
-		qcom,pull = <0>;
-		qcom,vin-sel = <2>;
-		qcom,select = <0>;
-	};
-
-	gpio@c300 { /* GPIO 4 */
-		qcom,mode = <0>;
-		qcom,pull = <0>;
-		qcom,vin-sel = <2>;
-		qcom,select = <0>;
-	};
-
-	gpio@c400 { /* GPIO 5 */
-		qcom,mode = <0>;
-		qcom,pull = <0>;
-		qcom,vin-sel = <2>;
-		qcom,select = <0>;
-	};
-
-	gpio@c500 { /* GPIO 6 */
-	};
-
-	gpio@c600 { /* GPIO 7 */
-	};
-
-	gpio@c700 { /* GPIO 8 */
-	};
-
-	gpio@c800 { /* GPIO 9 */
-	};
-
-	gpio@c900 { /* GPIO 10 */
-	};
-
-	gpio@ca00 { /* GPIO 11 */
-	};
-
-	gpio@cb00 { /* GPIO 12 */
-	};
-
-	gpio@cc00 { /* GPIO 13 */
-	};
-
-	gpio@cd00 { /* GPIO 14 */
-	};
-
-	gpio@ce00 { /* GPIO 15 */
-		qcom,mode = <1>;
-		qcom,output-type = <0>;
-		qcom,pull = <5>;
-		qcom,vin-sel = <2>;
-		qcom,out-strength = <3>;
-		qcom,src-select = <2>;
-		qcom,master-en = <1>;
-	};
-
-	gpio@cf00 { /* GPIO 16 */
-	};
-
-	gpio@d000 { /* GPIO 17 */
-	};
-
-	gpio@d100 { /* GPIO 18 */
-	};
-
-	gpio@d200 { /* GPIO 19 */
-		qcom,mode = <1>;		/* QPNP_PIN_MODE_DIG_OUT */
-		qcom,output-type = <0>;		/* QPNP_PIN_OUT_BUF_CMOS */
-		qcom,pull = <5>;		/* QPNP_PIN_PULL_NO */
-		qcom,vin-sel = <2>;		/* QPNP_PIN_VIN2 */
-		qcom,out-strength = <2>;	/* QPNP_PIN_OUT_STRENGTH_MED */
-		qcom,src-select = <0>;		/* QPNP_PIN_SEL_FUNC_CONSTANT */
-		qcom,master-en = <1>;
-	};
-
-	gpio@d300 { /* GPIO 20 */
-	};
-
-	gpio@d400 { /* GPIO 21 */
-	};
-
-	gpio@d500 { /* GPIO 22 */
-	};
-
-	gpio@d600 { /* GPIO 23 */
-	};
-
-	gpio@d700 { /* GPIO 24 */
-	};
-
-	gpio@d800 { /* GPIO 25 */
-	};
-
-	gpio@d900 { /* GPIO 26 */
-	};
-
-	gpio@da00 { /* GPIO 27 */
-	};
-
-	gpio@db00 { /* GPIO 28 */
-	};
-
-	gpio@dc00 { /* GPIO 29 */
-		qcom,pull = <0>; /* set to default pull */
-		qcom,master-en = <1>;
-		qcom,vin-sel = <2>; /* select 1.8 V source */
-	};
-
-	gpio@dd00 { /* GPIO 30 */
-	};
-
-	gpio@de00 { /* GPIO 31 */
-	};
-
-	gpio@df00 { /* GPIO 32 */
-	};
-
-	gpio@e000 { /* GPIO 33 */
-	};
-
-	gpio@e100 { /* GPIO 34 */
-	};
-
-	gpio@e200 { /* GPIO 35 */
-	};
-
-	gpio@e300 { /* GPIO 36 */
-	};
-};
-
-&pm8941_mpps {
-
-	mpp@a000 { /* MPP 1 */
-	};
-
-	mpp@a100 { /* MPP 2 */
-	};
-
-	mpp@a200 { /* MPP 3 */
-	};
-
-	mpp@a300 { /* MPP 4 */
-	};
-
-	mpp@a400 { /* MPP 5 */
-		/* SPI_ETH config */
-		qcom,mode = <1>; /* DIG_OUT */
-		qcom,output-type = <0>; /* CMOS */
-		qcom,vin-sel = <2>; /* PM8941_S3 1.8V > 1.6V */
-		qcom,src-select = <0>; /* CONSTANT */
-		qcom,master-en = <1>; /* ENABLE MPP */
-	};
-
-	mpp@a500 { /* MPP 6 */
-		/* SPI_ETH_RST config */
-		qcom,mode = <1>; /* DIG_OUT */
-		qcom,output-type = <0>; /* CMOS */
-		qcom,vin-sel = <2>; /* PM8941_S3 1.8V > 1.6V */
-		qcom,src-select = <0>; /* CONSTANT */
-		qcom,master-en = <1>; /* ENABLE MPP */
-	};
-
-	mpp@a600 { /* MPP 7 */
-	};
-
-	mpp@a700 { /* MPP 8 */
-	};
-};
-
-&pm8841_mpps {
-
-	mpp@a000 { /* MPP 1 */
-	};
-
-	mpp@a100 { /* MPP 2 */
-	};
-
-	mpp@a200 { /* MPP 3 */
-	};
-
-	mpp@a300 { /* MPP 4 */
-	};
 };
diff --git a/arch/arm/boot/dts/msm8974-fluid.dtsi b/arch/arm/boot/dts/msm8974-fluid.dtsi
new file mode 100644
index 0000000..93f92c7
--- /dev/null
+++ b/arch/arm/boot/dts/msm8974-fluid.dtsi
@@ -0,0 +1,365 @@
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+/include/ "dsi-panel-toshiba-720p-video.dtsi"
+
+/ {
+	serial@f991e000 {
+		status = "ok";
+	};
+
+	qcom,mdss_dsi@fd922800 {
+		qcom,mdss_dsi_toshiba_720p_video {
+			status = "ok";
+		};
+	};
+
+	qcom,hdmi_tx@fd922100 {
+		status = "ok";
+	};
+
+	i2c@f9924000 {
+		atmel_mxt_ts@4a {
+			compatible = "atmel,mxt-ts";
+			reg = <0x4a>;
+			interrupt-parent = <&msmgpio>;
+			interrupts = <61 0x2>;
+			vdd_ana-supply = <&pm8941_l18>;
+			vcc_i2c-supply = <&pm8941_lvs1>;
+			atmel,reset-gpio = <&msmgpio 60 0x00>;
+			atmel,irq-gpio = <&msmgpio 61 0x00>;
+			atmel,panel-coords = <0  0 760 1424>;
+			atmel,display-coords = <0 0 720 1280>;
+			atmel,i2c-pull-up;
+			atmel,no-force-update;
+			atmel,cfg_1 {
+				atmel,family-id = <0x82>;
+				atmel,variant-id = <0x19>;
+				atmel,version = <0x10>;
+				atmel,build = <0xaa>;
+				atmel,config = [
+					/* Object 6, Instance = 0 */
+					00 00 00 00 00 00
+					/* Object 38, Instance = 0 */
+					15 00 02 10 08 0C 00 00
+					/* Object 7, Instance = 0 */
+					FF FF 32 03
+					/* Object 8, Instance = 0 */
+					0F 00 0A 0A 00 00 0A 00 00 00
+					/* Object 9, Instance = 0 */
+					83 00 00 18 0E 00 70 32 02 01
+					00 03 01 01 05 0A 0A 0A 90 05
+					F8 02 00 00 0F 0F 00 00 48 2D
+					07 0C 00 00 00 00
+					/* Object 15, Instance = 0 */
+					00 00 00 00 00 00 00 00 00 00
+					00
+					/* Object 18, Instance = 0 */
+					00 00
+					/* Object 19, Instance = 0 */
+					00 00 00 00 00 00
+					/* Object 23, Instance = 0 */
+					00 00 00 00 00 00 00 00 00 00
+					00 00 00 00 00
+					/* Object 25, Instance = 0 */
+					00 00 00 00 00 00 00 00 00 00
+					00 00 00 00 00
+					/* Object 40, Instance = 0 */
+					00 00 00 00 00
+					/* Object 42, Instance = 0 */
+					00 00 00 00 00 00 00 00 00 00
+					/* Object 46, Instance = 0 */
+					00 00 10 10 00 00 03 00 00 01
+					/* Object 47, Instance = 0 */
+					08 0A 28 0A 02 0A 00 8C 00 20
+					00 00 00
+					/* Object 55, Instance = 0 */
+					00 00 00 00 00 00
+					/* Object 56, Instance = 0 */
+					03 00 01 18 05 05 05 05 05 05
+					05 05 05 05 05 05 05 05 05 05
+					05 05 05 05 05 05 05 05 00 00
+					00 00 00 00 00 00 00 00 00 00
+					00 00
+					/* Object 57, Instance = 0 */
+					00 00 00
+					/* Object 61, Instance = 0 */
+					00 00 00 00 00
+					/* Object 61, Instance = 1 */
+					00 00 00 00 00
+					/* Object 62, Instance = 0 */
+					7F 03 00 16 00 00 00 00 00 00
+					04 08 10 18 05 00 0A 05 05 50
+					14 19 34 1A 64 00 00 04 40 00
+					00 00 00 00 30 32 02 00 01 00
+					05 00 00 00 00 00 00 00 00 00
+					00 00 0C 00
+					];
+			};
+		};
+	};
+
+	gpio_keys {
+		compatible = "gpio-keys";
+		input-name = "gpio-keys";
+
+		camera_snapshot {
+			label = "camera_snapshot";
+			gpios = <&pm8941_gpios 3 0x1>;
+			linux,input-type = <1>;
+			linux,code = <0x2fe>;
+			gpio-key,wakeup;
+			debounce-interval = <15>;
+		};
+
+		camera_focus {
+			label = "camera_focus";
+			gpios = <&pm8941_gpios 4 0x1>;
+			linux,input-type = <1>;
+			linux,code = <0x210>;
+			gpio-key,wakeup;
+			debounce-interval = <15>;
+		};
+
+		vol_up {
+			label = "volume_up";
+			gpios = <&pm8941_gpios 5 0x1>;
+			linux,input-type = <1>;
+			linux,code = <115>;
+			gpio-key,wakeup;
+			debounce-interval = <15>;
+		};
+	};
+
+	spi@f9923000 {
+		ethernet-switch@2 {
+			compatible = "micrel,ks8851";
+			reg = <2>;
+			interrupt-parent = <&msmgpio>;
+			interrupts = <94 0>;
+			spi-max-frequency = <4800000>;
+			rst-gpio = <&pm8941_mpps 6 0>;
+			vdd-io-supply = <&spi_eth_vreg>;
+			vdd-phy-supply = <&spi_eth_vreg>;
+		};
+	};
+};
+
+&sdcc1 {
+	qcom,sdcc-bus-width = <4>;
+};
+
+&sdcc2 {
+	#address-cells = <0>;
+	interrupt-parent = <&sdcc2>;
+	interrupts = <0 1 2>;
+	#interrupt-cells = <1>;
+	interrupt-map-mask = <0xffffffff>;
+	interrupt-map = <0 &intc 0 125 0
+			1 &intc 0 220 0
+			2 &msmgpio 62 0x3>;
+	interrupt-names = "core_irq", "bam_irq", "status_irq";
+	cd-gpios = <&msmgpio 62 0x1>;
+};
+
+&pm8941_gpios {
+	gpio@c000 { /* GPIO 1 */
+	};
+
+	gpio@c100 { /* GPIO 2 */
+	};
+
+	gpio@c200 { /* GPIO 3 */
+		qcom,mode = <0>;
+		qcom,pull = <0>;
+		qcom,vin-sel = <2>;
+		qcom,src-sel = <0>;
+	};
+
+	gpio@c300 { /* GPIO 4 */
+		qcom,mode = <0>;
+		qcom,pull = <0>;
+		qcom,vin-sel = <2>;
+		qcom,src-sel = <0>;
+	};
+
+	gpio@c400 { /* GPIO 5 */
+		qcom,mode = <0>;
+		qcom,pull = <0>;
+		qcom,vin-sel = <2>;
+		qcom,src-sel = <0>;
+	};
+
+	gpio@c500 { /* GPIO 6 */
+	};
+
+	gpio@c600 { /* GPIO 7 */
+	};
+
+	gpio@c700 { /* GPIO 8 */
+	};
+
+	gpio@c800 { /* GPIO 9 */
+	};
+
+	gpio@c900 { /* GPIO 10 */
+	};
+
+	gpio@ca00 { /* GPIO 11 */
+	};
+
+	gpio@cb00 { /* GPIO 12 */
+	};
+
+	gpio@cc00 { /* GPIO 13 */
+	};
+
+	gpio@cd00 { /* GPIO 14 */
+	};
+
+	gpio@ce00 { /* GPIO 15 */
+		qcom,mode = <1>;
+		qcom,output-type = <0>;
+		qcom,pull = <5>;
+		qcom,vin-sel = <2>;
+		qcom,out-strength = <3>;
+		qcom,src-sel = <2>;
+		qcom,master-en = <1>;
+	};
+
+	gpio@cf00 { /* GPIO 16 */
+	};
+
+	gpio@d000 { /* GPIO 17 */
+	};
+
+	gpio@d100 { /* GPIO 18 */
+	};
+
+	gpio@d200 { /* GPIO 19 */
+		qcom,mode = <1>;		/* QPNP_PIN_MODE_DIG_OUT */
+		qcom,output-type = <0>;		/* QPNP_PIN_OUT_BUF_CMOS */
+		qcom,pull = <5>;		/* QPNP_PIN_PULL_NO */
+		qcom,vin-sel = <2>;		/* QPNP_PIN_VIN2 */
+		qcom,out-strength = <2>;	/* QPNP_PIN_OUT_STRENGTH_MED */
+		qcom,src-sel = <0>;		/* QPNP_PIN_SEL_FUNC_CONSTANT */
+		qcom,master-en = <1>;
+	};
+
+	gpio@d300 { /* GPIO 20 */
+	};
+
+	gpio@d400 { /* GPIO 21 */
+	};
+
+	gpio@d500 { /* GPIO 22 */
+	};
+
+	gpio@d600 { /* GPIO 23 */
+	};
+
+	gpio@d700 { /* GPIO 24 */
+	};
+
+	gpio@d800 { /* GPIO 25 */
+	};
+
+	gpio@d900 { /* GPIO 26 */
+	};
+
+	gpio@da00 { /* GPIO 27 */
+	};
+
+	gpio@db00 { /* GPIO 28 */
+	};
+
+	gpio@dc00 { /* GPIO 29 */
+		qcom,pull = <0>; /* set to default pull */
+		qcom,master-en = <1>;
+		qcom,vin-sel = <2>; /* select 1.8 V source */
+	};
+
+	gpio@dd00 { /* GPIO 30 */
+	};
+
+	gpio@de00 { /* GPIO 31 */
+	};
+
+	gpio@df00 { /* GPIO 32 */
+	};
+
+	gpio@e000 { /* GPIO 33 */
+	};
+
+	gpio@e100 { /* GPIO 34 */
+	};
+
+	gpio@e200 { /* GPIO 35 */
+	};
+
+	gpio@e300 { /* GPIO 36 */
+	};
+};
+
+&pm8941_mpps {
+
+	mpp@a000 { /* MPP 1 */
+	};
+
+	mpp@a100 { /* MPP 2 */
+	};
+
+	mpp@a200 { /* MPP 3 */
+	};
+
+	mpp@a300 { /* MPP 4 */
+	};
+
+	mpp@a400 { /* MPP 5 */
+		/* SPI_ETH config */
+		qcom,mode = <1>; /* DIG_OUT */
+		qcom,output-type = <0>; /* CMOS */
+		qcom,vin-sel = <2>; /* PM8941_S3 1.8V > 1.6V */
+		qcom,src-sel = <0>; /* CONSTANT */
+		qcom,master-en = <1>; /* ENABLE MPP */
+	};
+
+	mpp@a500 { /* MPP 6 */
+		/* SPI_ETH_RST config */
+		qcom,mode = <1>; /* DIG_OUT */
+		qcom,output-type = <0>; /* CMOS */
+		qcom,vin-sel = <2>; /* PM8941_S3 1.8V > 1.6V */
+		qcom,src-sel = <0>; /* CONSTANT */
+		qcom,master-en = <1>; /* ENABLE MPP */
+	};
+
+	mpp@a600 { /* MPP 7 */
+	};
+
+	mpp@a700 { /* MPP 8 */
+	};
+};
+
+&pm8841_mpps {
+
+	mpp@a000 { /* MPP 1 */
+	};
+
+	mpp@a100 { /* MPP 2 */
+	};
+
+	mpp@a200 { /* MPP 3 */
+	};
+
+	mpp@a300 { /* MPP 4 */
+	};
+};
diff --git a/arch/arm/boot/dts/msm8974-gpu.dtsi b/arch/arm/boot/dts/msm8974-gpu.dtsi
index 017aea9..403a5cc 100644
--- a/arch/arm/boot/dts/msm8974-gpu.dtsi
+++ b/arch/arm/boot/dts/msm8974-gpu.dtsi
@@ -25,15 +25,19 @@
 
 		qcom,idle-timeout = <83>; //<HZ/12>
 		qcom,nap-allowed = <1>;
-		qcom,clk-map = <0x00000016>; //KGSL_CLK_CORE | KGSL_CLK_IFACE | KGSL_CLK_MEM_IFACE
+		qcom,strtstp-sleepwake;
+		qcom,clk-map = <0x0000006>; //KGSL_CLK_CORE | KGSL_CLK_IFACE
 
 		/* Bus Scale Settings */
-		qcom,grp3d-vectors = <0 0 0 0>, <2 1 0 0>,
-				<0 0 0 2000>, <2 1 0 3000>,
-				<0 0 0 4000>, <2 1 0 5000>,
-				<0 0 0 6400>, <2 1 0 7600>;
-		qcom,grp3d-num-vectors-per-usecase = <2>;
-		qcom,grp3d-num-bus-scale-usecases = <4>;
+		qcom,msm-bus,name = "grp3d";
+		qcom,msm-bus,num-cases = <4>;
+		qcom,msm-bus,active-only = <0>;
+		qcom,msm-bus,num-paths = <2>;
+		qcom,msm-bus,vectors-KBps =
+				<26 512 0 0>, <89 604 0 0>,
+				<26 512 0 2000000>, <89 604 0 3000000>,
+				<26 512 0 4000000>, <89 604 0 5000000>,
+				<26 512 0 6400000>, <89 604 0 7600000>;
 
 		/* GDSC oxili regulators */
 		vddcx-supply = <&gdsc_oxili_cx>;
@@ -85,6 +89,9 @@
 
 			compatible = "qcom,dcvs-core-info";
 
+			qcom,num-cores = <1>;
+			qcom,sensors = <0>;
+
 			qcom,core-core-type = <1>;
 
 			qcom,algo-disable-pc-threshold = <0>;
diff --git a/arch/arm/boot/dts/msm8974-ion.dtsi b/arch/arm/boot/dts/msm8974-ion.dtsi
index 1893ae4..01e200a 100644
--- a/arch/arm/boot/dts/msm8974-ion.dtsi
+++ b/arch/arm/boot/dts/msm8974-ion.dtsi
@@ -28,41 +28,30 @@
 			qcom,memory-reservation-size = <0x7800000>;
 		};
 
-		qcom,ion-heap@29 { /* FIRMWARE HEAP */
+		qcom,ion-heap@23 { /* PIL1 HEAP */
 			compatible = "qcom,msm-ion-reserve";
-			reg = <29>;
-			qcom,heap-align = <0x20000>;
-			qcom,heap-adjacent = <8>;
-			qcom,memory-reservation-type = "EBI1"; /* reserve EBI memory */
-			qcom,memory-reservation-size = <0xA00000>;
-		};
-
-		qcom,ion-heap@12 { /* MFC HEAP */
-			compatible = "qcom,msm-ion-reserve";
-			reg = <12>;
-			qcom,heap-align = <0x1000>;
-			qcom,memory-reservation-type = "EBI1"; /* reserve EBI memory */
-			qcom,memory-reservation-size = <0x2000>;
-		};
-
-		qcom,ion-heap@24 { /* SF HEAP */
-			compatible = "qcom,msm-ion-reserve";
-			reg = <24>;
-			qcom,heap-align = <0x1000>;
-			qcom,memory-reservation-type = "EBI1"; /* reserve EBI memory */
-			qcom,memory-reservation-size = <0x2800000>;
+			reg = <23>;
+			qcom,heap-align = <0x10000>;
+			qcom,memory-fixed = <0xd200000 0x2800000>;
 		};
 
 		qcom,ion-heap@25 { /* IOMMU HEAP */
 			reg = <25>;
 		};
 
+		qcom,ion-heap@26 { /* PIL2 HEAP */
+			compatible = "qcom,msm-ion-reserve";
+			reg = <26>;
+			qcom,heap-align = <0x10000>;
+			qcom,memory-fixed = <0x8400000 0x4e00000>;
+		};
+
 		qcom,ion-heap@27 { /* QSECOM HEAP */
 			compatible = "qcom,msm-ion-reserve";
 			reg = <27>;
 			qcom,heap-align = <0x1000>;
 			qcom,memory-reservation-type = "EBI1"; /* reserve EBI memory */
-			qcom,memory-reservation-size = <0x600000>;
+			qcom,memory-reservation-size = <0x780000>;
 		};
 
 		qcom,ion-heap@28 { /* AUDIO HEAP */
@@ -70,7 +59,7 @@
 			reg = <28>;
 			qcom,heap-align = <0x1000>;
 			qcom,memory-reservation-type = "EBI1"; /* reserve EBI memory */
-			qcom,memory-reservation-size = <0x2B4000>;
+			qcom,memory-reservation-size = <0x314000>;
 		};
 	};
 };
diff --git a/arch/arm/boot/dts/msm8974-liquid.dts b/arch/arm/boot/dts/msm8974-liquid.dts
index 2abc1d5..ef38036 100644
--- a/arch/arm/boot/dts/msm8974-liquid.dts
+++ b/arch/arm/boot/dts/msm8974-liquid.dts
@@ -13,206 +13,10 @@
 /dts-v1/;
 
 /include/ "msm8974.dtsi"
+/include/ "msm8974-liquid.dtsi"
 
 / {
 	model = "Qualcomm MSM 8974 LIQUID";
 	compatible = "qcom,msm8974-liquid", "qcom,msm8974";
 	qcom,msm-id = <126 9 0>;
-
-	serial@f991e000 {
-		status = "ok";
-	};
-};
-
-&pm8941_gpios {
-	gpio@c000 { /* GPIO 1 */
-	};
-
-	gpio@c100 { /* GPIO 2 */
-	};
-
-	gpio@c200 { /* GPIO 3 */
-		qcom,mode = <0>;
-		qcom,pull = <0>;
-		qcom,vin-sel = <2>;
-		qcom,select = <0>;
-	};
-
-	gpio@c300 { /* GPIO 4 */
-		qcom,mode = <0>;
-		qcom,pull = <0>;
-		qcom,vin-sel = <2>;
-		qcom,select = <0>;
-	};
-
-	gpio@c400 { /* GPIO 5 */
-		qcom,mode = <0>;
-		qcom,pull = <0>;
-		qcom,vin-sel = <2>;
-		qcom,select = <0>;
-	};
-
-	gpio@c500 { /* GPIO 6 */
-	};
-
-	gpio@c600 { /* GPIO 7 */
-	};
-
-	gpio@c700 { /* GPIO 8 */
-	};
-
-	gpio@c800 { /* GPIO 9 */
-	};
-
-	gpio@c900 { /* GPIO 10 */
-	};
-
-	gpio@ca00 { /* GPIO 11 */
-	};
-
-	gpio@cb00 { /* GPIO 12 */
-	};
-
-	gpio@cc00 { /* GPIO 13 */
-	};
-
-	gpio@cd00 { /* GPIO 14 */
-	};
-
-	gpio@ce00 { /* GPIO 15 */
-		qcom,mode = <1>;
-		qcom,output-type = <0>;
-		qcom,pull = <5>;
-		qcom,vin-sel = <2>;
-		qcom,out-strength = <3>;
-		qcom,src-select = <2>;
-		qcom,master-en = <1>;
-	};
-
-	gpio@cf00 { /* GPIO 16 */
-	};
-
-	gpio@d000 { /* GPIO 17 */
-	};
-
-	gpio@d100 { /* GPIO 18 */
-	};
-
-	gpio@d200 { /* GPIO 19 */
-		qcom,mode = <1>;		/* QPNP_PIN_MODE_DIG_OUT */
-		qcom,output-type = <0>;		/* QPNP_PIN_OUT_BUF_CMOS */
-		qcom,pull = <5>;		/* QPNP_PIN_PULL_NO */
-		qcom,vin-sel = <2>;		/* QPNP_PIN_VIN2 */
-		qcom,out-strength = <2>;	/* QPNP_PIN_OUT_STRENGTH_MED */
-		qcom,src-select = <0>;		/* QPNP_PIN_SEL_FUNC_CONSTANT */
-		qcom,master-en = <1>;
-	};
-
-	gpio@d300 { /* GPIO 20 */
-	};
-
-	gpio@d400 { /* GPIO 21 */
-	};
-
-	gpio@d500 { /* GPIO 22 */
-	};
-
-	gpio@d600 { /* GPIO 23 */
-	};
-
-	gpio@d700 { /* GPIO 24 */
-	};
-
-	gpio@d800 { /* GPIO 25 */
-	};
-
-	gpio@d900 { /* GPIO 26 */
-	};
-
-	gpio@da00 { /* GPIO 27 */
-	};
-
-	gpio@db00 { /* GPIO 28 */
-	};
-
-	gpio@dc00 { /* GPIO 29 */
-		qcom,pull = <0>; /* set to default pull */
-		qcom,master-en = <1>;
-		qcom,vin-sel = <2>; /* select 1.8 V source */
-	};
-
-	gpio@dd00 { /* GPIO 30 */
-	};
-
-	gpio@de00 { /* GPIO 31 */
-	};
-
-	gpio@df00 { /* GPIO 32 */
-	};
-
-	gpio@e000 { /* GPIO 33 */
-	};
-
-	gpio@e100 { /* GPIO 34 */
-	};
-
-	gpio@e200 { /* GPIO 35 */
-	};
-
-	gpio@e300 { /* GPIO 36 */
-	};
-};
-
-&pm8941_mpps {
-
-	mpp@a000 { /* MPP 1 */
-	};
-
-	mpp@a100 { /* MPP 2 */
-	};
-
-	mpp@a200 { /* MPP 3 */
-	};
-
-	mpp@a300 { /* MPP 4 */
-	};
-
-	mpp@a400 { /* MPP 5 */
-		/* SPI_ETH config */
-		qcom,mode = <1>; /* DIG_OUT */
-		qcom,output-type = <0>; /* CMOS */
-		qcom,vin-sel = <2>; /* PM8941_S3 1.8V > 1.6V */
-		qcom,src-select = <0>; /* CONSTANT */
-		qcom,master-en = <1>; /* ENABLE MPP */
-	};
-
-	mpp@a500 { /* MPP 6 */
-		/* SPI_ETH_RST config */
-		qcom,mode = <1>; /* DIG_OUT */
-		qcom,output-type = <0>; /* CMOS */
-		qcom,vin-sel = <2>; /* PM8941_S3 1.8V > 1.6V */
-		qcom,src-select = <0>; /* CONSTANT */
-		qcom,master-en = <1>; /* ENABLE MPP */
-	};
-
-	mpp@a600 { /* MPP 7 */
-	};
-
-	mpp@a700 { /* MPP 8 */
-	};
-};
-
-&pm8841_mpps {
-
-	mpp@a000 { /* MPP 1 */
-	};
-
-	mpp@a100 { /* MPP 2 */
-	};
-
-	mpp@a200 { /* MPP 3 */
-	};
-
-	mpp@a300 { /* MPP 4 */
-	};
 };
diff --git a/arch/arm/boot/dts/msm8974-liquid.dtsi b/arch/arm/boot/dts/msm8974-liquid.dtsi
new file mode 100644
index 0000000..002332b
--- /dev/null
+++ b/arch/arm/boot/dts/msm8974-liquid.dtsi
@@ -0,0 +1,429 @@
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+/ {
+	serial@f991e000 {
+		status = "ok";
+	};
+
+	qcom,mdss_edp@fd923400 {
+		status = "ok";
+	};
+
+	i2c@f9967000 {
+		battery@b {
+			compatible = "ti,bq28400-battery";
+			reg = <0xb>;
+		};
+
+		charger@2b {
+			compatible = "summit,smb350-charger";
+			reg = <0x2b>; /* 0x56/0x57 */
+			summit,stat-gpio = <&pm8941_gpios 30 0x00>;
+			summit,chg-en-n-gpio = <&pm8941_gpios 10 0x00>;
+			summit,chg-susp-n-gpio = <&pm8941_gpios 13 0x00>;
+			summit,chg-current-ma = <1600>;
+			summit,term-current-ma = <300>;
+		};
+	};
+
+	gpio_keys {
+		compatible = "gpio-keys";
+		input-name = "gpio-keys";
+
+		home {
+			label = "home";
+			gpios = <&pm8941_gpios 1 0x1>;
+			linux,input-type = <1>;
+			linux,code = <102>;
+			gpio-key,wakeup;
+			debounce-interval = <15>;
+		};
+
+		vol_down {
+			label = "volume_down";
+			gpios = <&pm8941_gpios 2 0x1>;
+			linux,input-type = <1>;
+			linux,code = <114>;
+			gpio-key,wakeup;
+			debounce-interval = <15>;
+		};
+
+		vol_up {
+			label = "volume_up";
+			gpios = <&pm8941_gpios 5 0x1>;
+			linux,input-type = <1>;
+			linux,code = <115>;
+			gpio-key,wakeup;
+			debounce-interval = <15>;
+		};
+	};
+
+	qcom,mdss_mdp@fd900000 {
+		qcom,memory-reservation-size = <0x1000000>; /* size 16MB */
+	};
+
+	qcom,hdmi_tx@fd922100 {
+		status = "ok";
+	};
+
+	i2c@f9924000 {
+		atmel_mxt_ts@4a {
+			compatible = "atmel,mxt-ts";
+			reg = <0x4a>;
+			interrupt-parent = <&msmgpio>;
+			interrupts = <61 0x2>;
+			vdd_ana-supply = <&pm8941_l22>;
+			vcc_i2c-supply = <&pm8941_s3>;
+			atmel,reset-gpio = <&msmgpio 60 0x00>;
+			atmel,irq-gpio = <&msmgpio 61 0x00>;
+			atmel,panel-coords = <0 0 1080 1920>;
+			atmel,display-coords = <0 0 1080 1920>;
+			atmel,i2c-pull-up;
+			atmel,no-force-update;
+			atmel,cfg_1 {
+				atmel,family-id = <0xa2>;
+				atmel,variant-id = <0x00>;
+				atmel,version = <0x11>;
+				atmel,build = <0xaa>;
+				atmel,config = [
+					/* Object 6, Instance = 0 */
+					00 00 00 00 00 00
+					/* Object 38, Instance = 0 */
+					16 00 00 14 09 0C 00 00 00 00
+					00 00 00 00 00 00 00 00 00 00
+					00 00 00 00 00 00 00 00 00 00
+					00 00 00 00 00 00 00 00 00 00
+					00 00 00 00 00 00 00 00 00 00
+					00 00 00 00 00 00 00 00 00 00
+					00 00 00 00
+					/* Object 7, Instance = 0 */
+					FF FF 0A 03
+					/* Object 8, Instance = 0 */
+					5F 00 14 14 00 00 00 01 00 00
+					/* Object 9, Instance = 0 */
+					8F 00 00 20 34 00 87 3C 08 03
+					00 05 03 80 0A 14 14 0A 80 07
+					38 04 00 00 00 00 00 00 00 00
+					0F 0F 2E 33 02 00
+					/* Object 15, Instance = 0 */
+					00 00 00 00 00 00 00 00 00 00
+					00
+					/* Object 18, Instance = 0 */
+					04 00
+					/* Object 24, Instance = 0 */
+					00 00 00 00 00 00 00 00 00 00
+					00 00 00 00 00 00 00 00 00
+					/* Object 25, Instance = 0 */
+					00 00 54 6F F0 55 00 00 00 00
+					00 00 00 00 00
+					/* Object 27, Instance = 0 */
+					00 00 00 00 00 00 00
+					/* Object 40, Instance = 0 */
+					00 14 14 14 14
+					/* Object 42, Instance = 0 */
+					20 14 00 00 00 14 11 00 03 00
+					/* Object 43, Instance = 0 */
+					09 00 01 01 91 00 80 00 00 00
+					00 00
+					/* Object 46, Instance = 0 */
+					00 00 10 10 00 00 01 00 00 0F
+					0A
+					/* Object 47, Instance = 0 */
+					00 14 23 02 05 1E 01 78 03 10
+					00 00 0C 00 00 00 00 00 00 00
+					00 00
+					/* Object 55, Instance = 0 */
+					00 00 00 00 00 00 00
+					/* Object 56, Instance = 0 */
+					02 00 01 30 13 14 14 14 15 15
+					15 15 15 15 15 16 16 16 16 16
+					16 16 16 16 16 15 14 14 14 14
+					15 14 14 14 14 13 00 00 01 02
+					05 05 00 00 00 00 00 00 00 00
+					00
+					/* Object 57, Instance = 0 */
+					00 00 00
+					/* Object 61, Instance = 0 */
+					00 00 00 00 00
+					/* Object 62, Instance = 0 */
+					00 01 03 01 00 00 00 00 00 0A
+					0F 14 19 23 05 00 0A 05 05 69
+					23 23 34 11 64 06 06 04 40 00
+					00 00 00 00 69 4B 02 00 00 80
+					0A 14 14 18 18 10 10 80 00 80
+					00 00 0F 02 00 00 00 00 00 00
+					00 00 00 00 00 00 00 00 00 00
+					00 00 00 00
+					/* Object 63, Instance = 0 */
+					00 00 00 00 00 00 00 00 00 00
+					00 00
+					];
+			};
+		};
+	};
+
+	ext_5v: regulator-smb210 {
+		compatible = "regulator-fixed";
+		regulator-name = "ext_5v";
+		gpio = <&pm8941_mpps 2 0>;
+		enable-active-high;
+	};
+};
+
+&pm8941_mvs1 {
+	parent-supply = <&ext_5v>;
+};
+
+&pm8941_mvs2 {
+	parent-supply = <&ext_5v>;
+};
+
+&pm8941_gpios {
+	gpio@c000 { /* GPIO 1 */
+		qcom,mode = <0>;
+		qcom,pull = <0>;
+		qcom,vin-sel = <2>;
+		qcom,src-sel = <0>;
+	};
+
+	gpio@c100 { /* GPIO 2 */
+		qcom,mode = <0>;
+		qcom,pull = <0>;
+		qcom,vin-sel = <2>;
+		qcom,src-sel = <0>;
+	};
+
+	gpio@c200 { /* GPIO 3 */
+	};
+
+	gpio@c300 { /* GPIO 4 */
+	};
+
+	gpio@c400 { /* GPIO 5 */
+		qcom,mode = <0>;
+		qcom,pull = <0>;
+		qcom,vin-sel = <2>;
+		qcom,src-sel = <0>;
+	};
+
+	gpio@c500 { /* GPIO 6 */
+	};
+
+	gpio@c600 { /* GPIO 7 */
+	};
+
+	gpio@c700 { /* GPIO 8 */
+	};
+
+	gpio@c800 { /* GPIO 9 */
+	};
+
+	gpio@c900 { /* GPIO 10 */
+		/* SMB350-CHG-EN-N */
+		qcom,mode = <1>;		/* DIG_OUT */
+		qcom,output-type = <0>;		/* CMOS */
+		qcom,pull = <5>;		/* PULL_NO */
+		qcom,vin-sel = <0>;		/* VPH */
+		qcom,out-strength = <2>;	/* STRENGTH_MED */
+		qcom,src-sel = <0>;		/* CONSTANT */
+		qcom,master-en = <1>;
+	};
+
+	gpio@ca00 { /* GPIO 11 */
+	};
+
+	gpio@cb00 { /* GPIO 12 */
+	};
+
+	gpio@cc00 { /* GPIO 13 */
+		/* SMB350-CHG-SUSP-N */
+		qcom,mode = <1>;		/* DIG_OUT */
+		qcom,output-type = <0>;		/* CMOS */
+		qcom,pull = <5>;		/* PULL_NO */
+		qcom,vin-sel = <0>;		/* VPH */
+		qcom,out-strength = <2>;	/* STRENGTH_MED */
+		qcom,src-sel = <0>;		/* CONSTANT */
+		qcom,master-en = <1>;
+	};
+
+	gpio@cd00 { /* GPIO 14 */
+	};
+
+	gpio@ce00 { /* GPIO 15 */
+		qcom,mode = <1>;
+		qcom,output-type = <0>;
+		qcom,pull = <5>;
+		qcom,vin-sel = <2>;
+		qcom,out-strength = <3>;
+		qcom,src-sel = <2>;
+		qcom,master-en = <1>;
+	};
+
+	gpio@cf00 { /* GPIO 16 */
+	};
+
+	gpio@d000 { /* GPIO 17 */
+	};
+
+	gpio@d100 { /* GPIO 18 */
+	};
+
+	gpio@d200 { /* GPIO 19 */
+		qcom,mode = <1>;		/* QPNP_PIN_MODE_DIG_OUT */
+		qcom,output-type = <0>;		/* QPNP_PIN_OUT_BUF_CMOS */
+		qcom,pull = <5>;		/* QPNP_PIN_PULL_NO */
+		qcom,vin-sel = <2>;		/* QPNP_PIN_VIN2 */
+		qcom,out-strength = <2>;	/* QPNP_PIN_OUT_STRENGTH_MED */
+		qcom,src-sel = <0>;		/* QPNP_PIN_SEL_FUNC_CONSTANT */
+		qcom,master-en = <1>;
+	};
+
+	gpio@d300 { /* GPIO 20 */
+	};
+
+	gpio@d400 { /* GPIO 21 */
+	};
+
+	gpio@d500 { /* GPIO 22 */
+	};
+
+	gpio@d600 { /* GPIO 23 */
+	};
+
+	gpio@d700 { /* GPIO 24 */
+	};
+
+	gpio@d800 { /* GPIO 25 */
+	};
+
+	gpio@d900 { /* GPIO 26 */
+	};
+
+	gpio@da00 { /* GPIO 27 */
+	};
+
+	gpio@db00 { /* GPIO 28 */
+	};
+
+	gpio@dc00 { /* GPIO 29 */
+		qcom,pull = <0>; /* set to default pull */
+		qcom,master-en = <1>;
+		qcom,vin-sel = <2>; /* select 1.8 V source */
+	};
+
+	gpio@dd00 { /* GPIO 30 */
+		/* SMB350-STAT */
+		qcom,mode = <0>;		/* DIG_IN */
+		qcom,pull = <5>;		/* PULL_NO */
+		qcom,vin-sel = <2>;		/* S3 1.8V */
+		qcom,src-sel = <0>;		/* CONSTANT */
+		qcom,master-en = <1>;
+	};
+
+	gpio@de00 { /* GPIO 31 */
+	};
+
+	gpio@df00 { /* GPIO 32 */
+	};
+
+	gpio@e000 { /* GPIO 33 */
+	};
+
+	gpio@e100 { /* GPIO 34 */
+	};
+
+	gpio@e200 { /* GPIO 35 */
+	};
+
+	gpio@e300 { /* GPIO 36 */
+		qcom,mode = <1>;  /* QPNP_PIN_MODE_DIG_OUT */
+		qcom,output-type = <0>; /* QPNP_PIN_OUT_BUF_CMOS */
+		qcom,pull = <5>; /* QPNP_PIN_PULL_NO */
+		qcom,vin-sel = <2>; /* QPNP_PIN_VIN2 */
+		qcom,out-strength = <3>; /* QPNP_PIN_OUT_STRENGTH_HIGH */
+		qcom,src-sel = <3>; /* QPNP_PIN_SEL_FUNC_2 */
+		qcom,master-en = <1>;
+	};
+};
+
+&pm8941_mpps {
+
+	mpp@a000 { /* MPP 1 */
+	};
+
+	mpp@a100 { /* MPP 2 */
+		/* ext_5v regulator enable */
+		qcom,mode = <1>; /* Digital output */
+		qcom,invert = <0>; /* Output low initially */
+		qcom,vin-sel = <2>; /* PM8941 S3 = 1.8 V */
+		qcom,src-sel = <0>; /* Constant */
+		qcom,master-en = <1>; /* Enable MPP */
+	};
+
+	mpp@a200 { /* MPP 3 */
+	};
+
+	mpp@a300 { /* MPP 4 */
+	};
+
+	mpp@a400 { /* MPP 5 */
+		/* SPI_ETH config */
+		qcom,mode = <1>; /* DIG_OUT */
+		qcom,output-type = <0>; /* CMOS */
+		qcom,vin-sel = <2>; /* PM8941_S3 1.8V > 1.6V */
+		qcom,src-sel = <0>; /* CONSTANT */
+		qcom,master-en = <1>; /* ENABLE MPP */
+	};
+
+	mpp@a500 { /* MPP 6 */
+		/* SPI_ETH_RST config */
+		qcom,mode = <1>; /* DIG_OUT */
+		qcom,output-type = <0>; /* CMOS */
+		qcom,vin-sel = <2>; /* PM8941_S3 1.8V > 1.6V */
+		qcom,src-sel = <0>; /* CONSTANT */
+		qcom,master-en = <1>; /* ENABLE MPP */
+	};
+
+	mpp@a600 { /* MPP 7 */
+	};
+
+	mpp@a700 { /* MPP 8 */
+	};
+};
+
+&pm8841_mpps {
+
+	mpp@a000 { /* MPP 1 */
+	};
+
+	mpp@a100 { /* MPP 2 */
+	};
+
+	mpp@a200 { /* HDMI_MUX_SEL MPP 3*/
+		status = "ok";
+		qcom,mode = <1>; /* DIG_OUT */
+		qcom,output-type = <0>; /* CMOS */
+		qcom,vin-sel = <2>; /* PM8841_S3A 1.8V */
+		qcom,src-sel = <0>; /* CONSTANT */
+		qcom,master-en = <1>; /* ENABLE MPP */
+	};
+
+	mpp@a300 { /* HDMI_MUX_EN MPP 4*/
+		status = "ok";
+		qcom,mode = <1>; /* DIG_OUT */
+		qcom,output-type = <0>; /* CMOS */
+		qcom,vin-sel = <0>; /* PM8841_VPH 3.4V */
+		qcom,src-sel = <0>; /* CONSTANT */
+		qcom,master-en = <1>; /* ENABLE MPP */
+	};
+};
diff --git a/arch/arm/boot/dts/msm8974-mdss.dtsi b/arch/arm/boot/dts/msm8974-mdss.dtsi
index ca98706..a51a38d 100644
--- a/arch/arm/boot/dts/msm8974-mdss.dtsi
+++ b/arch/arm/boot/dts/msm8974-mdss.dtsi
@@ -60,4 +60,17 @@
 		qcom,mdss_pan_res = <1920 1080>;
 		qcom,mdss_pan_bpp = <24>;
 	};
+
+	mdss_edp: qcom,mdss_edp@fd923400 {
+		compatible = "qcom,mdss-edp";
+		reg = <0xfd923400 0x700>,
+			<0xfd8c2000 0x1000>;
+		reg-names = "edp_base", "mmss_cc_base";
+		vdda-supply = <&pm8941_l12>;
+		gpio-panel-en = <&msmgpio 58 0>;
+		gpio-panel-pwm = <&pm8941_gpios 36 0>;
+		qcom,panel-lpg-channel = <7>; /* LPG Channel 8 */
+		qcom,panel-pwm-period = <53>;
+		status = "disable";
+	};
 };
diff --git a/arch/arm/boot/dts/msm8974-mtp.dts b/arch/arm/boot/dts/msm8974-mtp.dts
index 00aec9f..9946cf0 100644
--- a/arch/arm/boot/dts/msm8974-mtp.dts
+++ b/arch/arm/boot/dts/msm8974-mtp.dts
@@ -13,350 +13,10 @@
 /dts-v1/;
 
 /include/ "msm8974.dtsi"
-/include/ "dsi-panel-toshiba-720p-video.dtsi"
+/include/ "msm8974-mtp.dtsi"
 
 / {
 	model = "Qualcomm MSM 8974 MTP";
 	compatible = "qcom,msm8974-mtp", "qcom,msm8974";
 	qcom,msm-id = <126 8 0>;
-
-	serial@f991e000 {
-		status = "ok";
-	};
-
-	qcom,mdss_dsi@fd922800 {
-		qcom,mdss_dsi_toshiba_720p_video {
-			status = "ok";
-		};
-	};
-
-	i2c@f9924000 {
-		atmel_mxt_ts@4a {
-			compatible = "atmel,mxt-ts";
-			reg = <0x4a>;
-			interrupt-parent = <&msmgpio>;
-			interrupts = <61 0x2>;
-			vdd_ana-supply = <&pm8941_l18>;
-			vcc_i2c-supply = <&pm8941_lvs1>;
-			atmel,reset-gpio = <&msmgpio 60 0x00>;
-			atmel,irq-gpio = <&msmgpio 61 0x00>;
-			atmel,panel-coords = <0  0 760 1424>;
-			atmel,display-coords = <0 0 720 1280>;
-			atmel,i2c-pull-up = <1>;
-			atmel,cfg_1 {
-				atmel,family-id = <0x82>;
-				atmel,variant-id = <0x19>;
-				atmel,version = <0x10>;
-				atmel,build = <0xaa>;
-				atmel,config = [
-					/* Object 6, Instance = 0 */
-					00 00 00 00 00 00
-					/* Object 38, Instance = 0 */
-					15 00 02 10 08 0C 00 00
-					/* Object 7, Instance = 0 */
-					FF FF 32 03
-					/* Object 8, Instance = 0 */
-					0F 00 0A 0A 00 00 0A 00 00 00
-					/* Object 9, Instance = 0 */
-					83 00 00 18 0E 00 70 32 02 01
-					00 03 01 01 05 0A 0A 0A 90 05
-					F8 02 00 00 0F 0F 00 00 48 2D
-					07 0C 00 00 00 00
-					/* Object 15, Instance = 0 */
-					00 00 00 00 00 00 00 00 00 00
-					00
-					/* Object 18, Instance = 0 */
-					00 00
-					/* Object 19, Instance = 0 */
-					00 00 00 00 00 00
-					/* Object 23, Instance = 0 */
-					00 00 00 00 00 00 00 00 00 00
-					00 00 00 00 00
-					/* Object 25, Instance = 0 */
-					00 00 00 00 00 00 00 00 00 00
-					00 00 00 00 00
-					/* Object 40, Instance = 0 */
-					00 00 00 00 00
-					/* Object 42, Instance = 0 */
-					00 00 00 00 00 00 00 00 00 00
-					/* Object 46, Instance = 0 */
-					00 00 10 10 00 00 03 00 00 01
-					/* Object 47, Instance = 0 */
-					08 0A 28 0A 02 0A 00 8C 00 20
-					00 00 00
-					/* Object 55, Instance = 0 */
-					00 00 00 00 00 00
-					/* Object 56, Instance = 0 */
-					03 00 01 18 05 05 05 05 05 05
-					05 05 05 05 05 05 05 05 05 05
-					05 05 05 05 05 05 05 05 00 00
-					00 00 00 00 00 00 00 00 00 00
-					00 00
-					/* Object 57, Instance = 0 */
-					00 00 00
-					/* Object 61, Instance = 0 */
-					00 00 00 00 00
-					/* Object 61, Instance = 1 */
-					00 00 00 00 00
-					/* Object 62, Instance = 0 */
-					7F 03 00 16 00 00 00 00 00 00
-					04 08 10 18 05 00 0A 05 05 50
-					14 19 34 1A 64 00 00 04 40 00
-					00 00 00 00 30 32 02 00 01 00
-					05 00 00 00 00 00 00 00 00 00
-					00 00 0C 00
-					];
-			};
-		};
-	};
-
-	gpio_keys {
-		compatible = "gpio-keys";
-
-		camera_snapshot {
-			label = "camera_snapshot";
-			gpios = <&pm8941_gpios 3 0x1>;
-			linux,input-type = <1>;
-			linux,code = <0x2fe>;
-			gpio-key,wakeup;
-			debounce-interval = <15>;
-		};
-
-		camera_focus {
-			label = "camera_focus";
-			gpios = <&pm8941_gpios 4 0x1>;
-			linux,input-type = <1>;
-			linux,code = <0x210>;
-			gpio-key,wakeup;
-			debounce-interval = <15>;
-		};
-
-		vol_up {
-			label = "volume_up";
-			gpios = <&pm8941_gpios 5 0x1>;
-			linux,input-type = <1>;
-			linux,code = <115>;
-			gpio-key,wakeup;
-			debounce-interval = <15>;
-		};
-	};
-
-	spi@f9923000 {
-		ethernet-switch@2 {
-			compatible = "micrel,ks8851";
-			reg = <2>;
-			interrupt-parent = <&msmgpio>;
-			interrupts = <94 0>;
-			spi-max-frequency = <4800000>;
-			rst-gpio = <&pm8941_mpps 6 0>;
-			vdd-io-supply = <&spi_eth_vreg>;
-			vdd-phy-supply = <&spi_eth_vreg>;
-		};
-	};
-};
-
-&sdcc2 {
-	#address-cells = <0>;
-	interrupt-parent = <&sdcc2>;
-	interrupts = <0 1 2>;
-	#interrupt-cells = <1>;
-	interrupt-map-mask = <0xffffffff>;
-	interrupt-map = <0 &intc 0 125 0
-			1 &intc 0 220 0
-			2 &msmgpio 62 0x3>;
-	interrupt-names = "core_irq", "bam_irq", "status_irq";
-	cd-gpios = <&msmgpio 62 0x1>;
-};
-
-&pm8941_gpios {
-	gpio@c000 { /* GPIO 1 */
-	};
-
-	gpio@c100 { /* GPIO 2 */
-	};
-
-	gpio@c200 { /* GPIO 3 */
-		qcom,mode = <0>;
-		qcom,pull = <0>;
-		qcom,vin-sel = <2>;
-		qcom,select = <0>;
-	};
-
-	gpio@c300 { /* GPIO 4 */
-		qcom,mode = <0>;
-		qcom,pull = <0>;
-		qcom,vin-sel = <2>;
-		qcom,select = <0>;
-	};
-
-	gpio@c400 { /* GPIO 5 */
-		qcom,mode = <0>;
-		qcom,pull = <0>;
-		qcom,vin-sel = <2>;
-		qcom,select = <0>;
-	};
-
-	gpio@c500 { /* GPIO 6 */
-	};
-
-	gpio@c600 { /* GPIO 7 */
-	};
-
-	gpio@c700 { /* GPIO 8 */
-	};
-
-	gpio@c800 { /* GPIO 9 */
-	};
-
-	gpio@c900 { /* GPIO 10 */
-	};
-
-	gpio@ca00 { /* GPIO 11 */
-	};
-
-	gpio@cb00 { /* GPIO 12 */
-	};
-
-	gpio@cc00 { /* GPIO 13 */
-	};
-
-	gpio@cd00 { /* GPIO 14 */
-	};
-
-	gpio@ce00 { /* GPIO 15 */
-		qcom,mode = <1>;
-		qcom,output-type = <0>;
-		qcom,pull = <5>;
-		qcom,vin-sel = <2>;
-		qcom,out-strength = <3>;
-		qcom,src-select = <2>;
-		qcom,master-en = <1>;
-	};
-
-	gpio@cf00 { /* GPIO 16 */
-	};
-
-	gpio@d000 { /* GPIO 17 */
-	};
-
-	gpio@d100 { /* GPIO 18 */
-	};
-
-	gpio@d200 { /* GPIO 19 */
-		qcom,mode = <1>;		/* QPNP_PIN_MODE_DIG_OUT */
-		qcom,output-type = <0>;		/* QPNP_PIN_OUT_BUF_CMOS */
-		qcom,pull = <5>;		/* QPNP_PIN_PULL_NO */
-		qcom,vin-sel = <2>;		/* QPNP_PIN_VIN2 */
-		qcom,out-strength = <2>;	/* QPNP_PIN_OUT_STRENGTH_MED */
-		qcom,src-select = <0>;		/* QPNP_PIN_SEL_FUNC_CONSTANT */
-		qcom,master-en = <1>;
-	};
-
-	gpio@d300 { /* GPIO 20 */
-	};
-
-	gpio@d400 { /* GPIO 21 */
-	};
-
-	gpio@d500 { /* GPIO 22 */
-	};
-
-	gpio@d600 { /* GPIO 23 */
-	};
-
-	gpio@d700 { /* GPIO 24 */
-	};
-
-	gpio@d800 { /* GPIO 25 */
-	};
-
-	gpio@d900 { /* GPIO 26 */
-	};
-
-	gpio@da00 { /* GPIO 27 */
-	};
-
-	gpio@db00 { /* GPIO 28 */
-	};
-
-	gpio@dc00 { /* GPIO 29 */
-		qcom,pull = <0>; /* set to default pull */
-		qcom,master-en = <1>;
-		qcom,vin-sel = <2>; /* select 1.8 V source */
-	};
-
-	gpio@dd00 { /* GPIO 30 */
-	};
-
-	gpio@de00 { /* GPIO 31 */
-	};
-
-	gpio@df00 { /* GPIO 32 */
-	};
-
-	gpio@e000 { /* GPIO 33 */
-	};
-
-	gpio@e100 { /* GPIO 34 */
-	};
-
-	gpio@e200 { /* GPIO 35 */
-	};
-
-	gpio@e300 { /* GPIO 36 */
-	};
-};
-
-&pm8941_mpps {
-
-	mpp@a000 { /* MPP 1 */
-	};
-
-	mpp@a100 { /* MPP 2 */
-	};
-
-	mpp@a200 { /* MPP 3 */
-	};
-
-	mpp@a300 { /* MPP 4 */
-	};
-
-	mpp@a400 { /* MPP 5 */
-		/* SPI_ETH config */
-		qcom,mode = <1>; /* DIG_OUT */
-		qcom,output-type = <0>; /* CMOS */
-		qcom,vin-sel = <2>; /* PM8941_S3 1.8V > 1.6V */
-		qcom,src-select = <0>; /* CONSTANT */
-		qcom,master-en = <1>; /* ENABLE MPP */
-	};
-
-	mpp@a500 { /* MPP 6 */
-		/* SPI_ETH_RST config */
-		qcom,mode = <1>; /* DIG_OUT */
-		qcom,output-type = <0>; /* CMOS */
-		qcom,vin-sel = <2>; /* PM8941_S3 1.8V > 1.6V */
-		qcom,src-select = <0>; /* CONSTANT */
-		qcom,master-en = <1>; /* ENABLE MPP */
-	};
-
-	mpp@a600 { /* MPP 7 */
-	};
-
-	mpp@a700 { /* MPP 8 */
-	};
-};
-
-&pm8841_mpps {
-
-	mpp@a000 { /* MPP 1 */
-	};
-
-	mpp@a100 { /* MPP 2 */
-	};
-
-	mpp@a200 { /* MPP 3 */
-	};
-
-	mpp@a300 { /* MPP 4 */
-	};
 };
diff --git a/arch/arm/boot/dts/msm8974-mtp.dtsi b/arch/arm/boot/dts/msm8974-mtp.dtsi
new file mode 100644
index 0000000..f4be0dc
--- /dev/null
+++ b/arch/arm/boot/dts/msm8974-mtp.dtsi
@@ -0,0 +1,409 @@
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+/include/ "dsi-panel-toshiba-720p-video.dtsi"
+
+/ {
+	serial@f991e000 {
+		status = "ok";
+	};
+
+	qcom,mdss_dsi@fd922800 {
+		qcom,mdss_dsi_toshiba_720p_video {
+			status = "ok";
+		};
+	};
+
+	qcom,hdmi_tx@fd922100 {
+		status = "disabled";
+	};
+
+	i2c@f9924000 {
+		atmel_mxt_ts@4a {
+			compatible = "atmel,mxt-ts";
+			reg = <0x4a>;
+			interrupt-parent = <&msmgpio>;
+			interrupts = <61 0x2>;
+			vdd_ana-supply = <&pm8941_l18>;
+			vcc_i2c-supply = <&pm8941_lvs1>;
+			atmel,reset-gpio = <&msmgpio 60 0x00>;
+			atmel,irq-gpio = <&msmgpio 61 0x00>;
+			atmel,panel-coords = <0  0 760 1424>;
+			atmel,display-coords = <0 0 720 1280>;
+			atmel,i2c-pull-up;
+			atmel,no-force-update;
+			atmel,cfg_1 {
+				atmel,family-id = <0x82>;
+				atmel,variant-id = <0x19>;
+				atmel,version = <0x10>;
+				atmel,build = <0xaa>;
+				atmel,config = [
+					/* Object 6, Instance = 0 */
+					00 00 00 00 00 00
+					/* Object 38, Instance = 0 */
+					15 00 02 10 08 0C 00 00
+					/* Object 7, Instance = 0 */
+					FF FF 32 03
+					/* Object 8, Instance = 0 */
+					0F 00 0A 0A 00 00 0A 00 00 00
+					/* Object 9, Instance = 0 */
+					83 00 00 18 0E 00 70 32 02 01
+					00 03 01 01 05 0A 0A 0A 90 05
+					F8 02 00 00 0F 0F 00 00 48 2D
+					07 0C 00 00 00 00
+					/* Object 15, Instance = 0 */
+					00 00 00 00 00 00 00 00 00 00
+					00
+					/* Object 18, Instance = 0 */
+					00 00
+					/* Object 19, Instance = 0 */
+					00 00 00 00 00 00
+					/* Object 23, Instance = 0 */
+					00 00 00 00 00 00 00 00 00 00
+					00 00 00 00 00
+					/* Object 25, Instance = 0 */
+					00 00 00 00 00 00 00 00 00 00
+					00 00 00 00 00
+					/* Object 40, Instance = 0 */
+					00 00 00 00 00
+					/* Object 42, Instance = 0 */
+					00 00 00 00 00 00 00 00 00 00
+					/* Object 46, Instance = 0 */
+					00 00 10 10 00 00 03 00 00 01
+					/* Object 47, Instance = 0 */
+					08 0A 28 0A 02 0A 00 8C 00 20
+					00 00 00
+					/* Object 55, Instance = 0 */
+					00 00 00 00 00 00
+					/* Object 56, Instance = 0 */
+					03 00 01 18 05 05 05 05 05 05
+					05 05 05 05 05 05 05 05 05 05
+					05 05 05 05 05 05 05 05 00 00
+					00 00 00 00 00 00 00 00 00 00
+					00 00
+					/* Object 57, Instance = 0 */
+					00 00 00
+					/* Object 61, Instance = 0 */
+					00 00 00 00 00
+					/* Object 61, Instance = 1 */
+					00 00 00 00 00
+					/* Object 62, Instance = 0 */
+					7F 03 00 16 00 00 00 00 00 00
+					04 08 10 18 05 00 0A 05 05 50
+					14 19 34 1A 64 00 00 04 40 00
+					00 00 00 00 30 32 02 00 01 00
+					05 00 00 00 00 00 00 00 00 00
+					00 00 0C 00
+					];
+			};
+		};
+	};
+
+	gpio_keys {
+		compatible = "gpio-keys";
+		input-name = "gpio-keys";
+
+		camera_snapshot {
+			label = "camera_snapshot";
+			gpios = <&pm8941_gpios 3 0x1>;
+			linux,input-type = <1>;
+			linux,code = <0x2fe>;
+			gpio-key,wakeup;
+			debounce-interval = <15>;
+		};
+
+		camera_focus {
+			label = "camera_focus";
+			gpios = <&pm8941_gpios 4 0x1>;
+			linux,input-type = <1>;
+			linux,code = <0x210>;
+			gpio-key,wakeup;
+			debounce-interval = <15>;
+		};
+
+		vol_up {
+			label = "volume_up";
+			gpios = <&pm8941_gpios 5 0x1>;
+			linux,input-type = <1>;
+			linux,code = <115>;
+			gpio-key,wakeup;
+			debounce-interval = <15>;
+		};
+	};
+
+	spi@f9923000 {
+		ethernet-switch@2 {
+			compatible = "micrel,ks8851";
+			reg = <2>;
+			interrupt-parent = <&msmgpio>;
+			interrupts = <94 0>;
+			spi-max-frequency = <4800000>;
+			rst-gpio = <&pm8941_mpps 6 0>;
+			vdd-io-supply = <&spi_eth_vreg>;
+			vdd-phy-supply = <&spi_eth_vreg>;
+		};
+	};
+};
+
+&sdcc2 {
+	#address-cells = <0>;
+	interrupt-parent = <&sdcc2>;
+	interrupts = <0 1 2>;
+	#interrupt-cells = <1>;
+	interrupt-map-mask = <0xffffffff>;
+	interrupt-map = <0 &intc 0 125 0
+			1 &intc 0 220 0
+			2 &msmgpio 62 0x3>;
+	interrupt-names = "core_irq", "bam_irq", "status_irq";
+	cd-gpios = <&msmgpio 62 0x1>;
+};
+
+&usb_otg {
+	qcom,hsusb-otg-otg-control = <2>;
+};
+
+&usb3 {
+	qcom,dwc-usb3-msm-otg-capability;
+};
+
+&pm8941_chg {
+	status = "ok";
+
+	qcom,chg-charging-disabled;
+
+	qcom,chg-chgr@1000 {
+		status = "ok";
+	};
+
+	qcom,chg-buck@1100 {
+		status = "ok";
+	};
+
+	qcom,chg-bat-if@1200 {
+		status = "ok";
+	};
+
+	qcom,chg-usb-chgpth@1300 {
+		status = "ok";
+	};
+
+	qcom,chg-dc-chgpth@1400 {
+		status = "ok";
+	};
+
+	qcom,chg-boost@1500 {
+		status = "ok";
+	};
+
+	qcom,chg-misc@1600 {
+		status = "ok";
+	};
+};
+
+&pm8941_gpios {
+	gpio@c000 { /* GPIO 1 */
+	};
+
+	gpio@c100 { /* GPIO 2 */
+	};
+
+	gpio@c200 { /* GPIO 3 */
+		qcom,mode = <0>;
+		qcom,pull = <0>;
+		qcom,vin-sel = <2>;
+		qcom,src-sel = <0>;
+	};
+
+	gpio@c300 { /* GPIO 4 */
+		qcom,mode = <0>;
+		qcom,pull = <0>;
+		qcom,vin-sel = <2>;
+		qcom,src-sel = <0>;
+	};
+
+	gpio@c400 { /* GPIO 5 */
+		qcom,mode = <0>;
+		qcom,pull = <0>;
+		qcom,vin-sel = <2>;
+		qcom,src-sel = <0>;
+	};
+
+	gpio@c500 { /* GPIO 6 */
+	};
+
+	gpio@c600 { /* GPIO 7 */
+	};
+
+	gpio@c700 { /* GPIO 8 */
+	};
+
+	gpio@c800 { /* GPIO 9 */
+	};
+
+	gpio@c900 { /* GPIO 10 */
+	};
+
+	gpio@ca00 { /* GPIO 11 */
+	};
+
+	gpio@cb00 { /* GPIO 12 */
+	};
+
+	gpio@cc00 { /* GPIO 13 */
+	};
+
+	gpio@cd00 { /* GPIO 14 */
+	};
+
+	gpio@ce00 { /* GPIO 15 */
+		qcom,mode = <1>;
+		qcom,output-type = <0>;
+		qcom,pull = <5>;
+		qcom,vin-sel = <2>;
+		qcom,out-strength = <3>;
+		qcom,src-sel = <2>;
+		qcom,master-en = <1>;
+	};
+
+	gpio@cf00 { /* GPIO 16 */
+	};
+
+	gpio@d000 { /* GPIO 17 */
+	};
+
+	gpio@d100 { /* GPIO 18 */
+	};
+
+	gpio@d200 { /* GPIO 19 */
+		qcom,mode = <1>;		/* QPNP_PIN_MODE_DIG_OUT */
+		qcom,output-type = <0>;		/* QPNP_PIN_OUT_BUF_CMOS */
+		qcom,pull = <5>;		/* QPNP_PIN_PULL_NO */
+		qcom,vin-sel = <2>;		/* QPNP_PIN_VIN2 */
+		qcom,out-strength = <2>;	/* QPNP_PIN_OUT_STRENGTH_MED */
+		qcom,src-sel = <0>;		/* QPNP_PIN_SEL_FUNC_CONSTANT */
+		qcom,master-en = <1>;
+	};
+
+	gpio@d300 { /* GPIO 20 */
+	};
+
+	gpio@d400 { /* GPIO 21 */
+	};
+
+	gpio@d500 { /* GPIO 22 */
+	};
+
+	gpio@d600 { /* GPIO 23 */
+	};
+
+	gpio@d700 { /* GPIO 24 */
+	};
+
+	gpio@d800 { /* GPIO 25 */
+	};
+
+	gpio@d900 { /* GPIO 26 */
+	};
+
+	gpio@da00 { /* GPIO 27 */
+	};
+
+	gpio@db00 { /* GPIO 28 */
+	};
+
+	gpio@dc00 { /* GPIO 29 */
+		qcom,pull = <0>; /* set to default pull */
+		qcom,master-en = <1>;
+		qcom,vin-sel = <2>; /* select 1.8 V source */
+	};
+
+	gpio@dd00 { /* GPIO 30 */
+	};
+
+	gpio@de00 { /* GPIO 31 */
+	};
+
+	gpio@df00 { /* GPIO 32 */
+	};
+
+	gpio@e000 { /* GPIO 33 */
+	};
+
+	gpio@e100 { /* GPIO 34 */
+	};
+
+	gpio@e200 { /* GPIO 35 */
+	};
+
+	gpio@e300 { /* GPIO 36 */
+	};
+};
+
+&pm8941_mpps {
+
+	mpp@a000 { /* MPP 1 */
+	};
+
+	mpp@a100 { /* MPP 2 */
+	};
+
+	mpp@a200 { /* MPP 3 */
+	};
+
+	mpp@a300 { /* MPP 4 */
+	};
+
+	mpp@a400 { /* MPP 5 */
+		/* SPI_ETH config */
+		qcom,mode = <1>; /* DIG_OUT */
+		qcom,output-type = <0>; /* CMOS */
+		qcom,vin-sel = <2>; /* PM8941_S3 1.8V > 1.6V */
+		qcom,src-sel = <0>; /* CONSTANT */
+		qcom,master-en = <1>; /* ENABLE MPP */
+	};
+
+	mpp@a500 { /* MPP 6 */
+		/* SPI_ETH_RST config */
+		qcom,mode = <1>; /* DIG_OUT */
+		qcom,output-type = <0>; /* CMOS */
+		qcom,vin-sel = <2>; /* PM8941_S3 1.8V > 1.6V */
+		qcom,src-sel = <0>; /* CONSTANT */
+		qcom,master-en = <1>; /* ENABLE MPP */
+	};
+
+	mpp@a600 { /* MPP 7 */
+	};
+
+	mpp@a700 { /* MPP 8 */
+	};
+};
+
+&pm8841_mpps {
+
+	mpp@a000 { /* MPP 1 */
+	};
+
+	mpp@a100 { /* MPP 2 */
+	};
+
+	mpp@a200 { /* MPP 3 */
+	};
+
+	mpp@a300 { /* MPP 4 */
+	};
+};
+
+&slim_msm {
+	taiko_codec {
+		qcom,cdc-micbias2-ext-cap;
+	};
+};
diff --git a/arch/arm/boot/dts/msm8974_pm.dtsi b/arch/arm/boot/dts/msm8974-pm.dtsi
similarity index 90%
rename from arch/arm/boot/dts/msm8974_pm.dtsi
rename to arch/arm/boot/dts/msm8974-pm.dtsi
index e39a72a..30483bc 100644
--- a/arch/arm/boot/dts/msm8974_pm.dtsi
+++ b/arch/arm/boot/dts/msm8974-pm.dtsi
@@ -28,6 +28,8 @@
 		qcom,saw2-spm-dly= <0x20000400>;
 		qcom,saw2-spm-ctl = <0x1>;
 		qcom,saw2-spm-cmd-wfi = [03 0b 0f];
+		qcom,saw2-spm-cmd-ret = [42 1b 00 d0 c0 a0 90 03 d0 98 a2 c0
+				0b 00 42 1b 0f];
 		qcom,saw2-spm-cmd-spc = [00 20 80 10 90 a0 b0 03 3b 98 a2 b0 82
 				10 0b 30 06 26 30 0f];
 		qcom,saw2-spm-cmd-pc = [00 20 80 10 90 a0 b0 07 3b 98 a2 b0 82
@@ -49,6 +51,8 @@
 		qcom,saw2-spm-dly= <0x20000400>;
 		qcom,saw2-spm-ctl = <0x1>;
 		qcom,saw2-spm-cmd-wfi = [03 0b 0f];
+		qcom,saw2-spm-cmd-ret = [42 1b 00 d0 c0 a0 90 03 d0 98 a2 c0
+				0b 00 42 1b 0f];
 		qcom,saw2-spm-cmd-spc = [00 20 80 10 90 a0 b0 03 3b 98 a2 b0 82
 				10 0b 30 06 26 30 0f];
 		qcom,saw2-spm-cmd-pc = [00 20 80 10 90 a0 b0 07 3b 98 a2 b0 82
@@ -70,6 +74,8 @@
 		qcom,saw2-spm-dly= <0x20000400>;
 		qcom,saw2-spm-ctl = <0x1>;
 		qcom,saw2-spm-cmd-wfi = [03 0b 0f];
+		qcom,saw2-spm-cmd-ret = [42 1b 00 d0 c0 a0 90 03 d0 98 a2 c0
+				0b 00 42 1b 0f];
 		qcom,saw2-spm-cmd-spc = [00 20 80 10 90 a0 b0 03 3b 98 a2 b0 82
 				10 0b 30 06 26 30 0f];
 		qcom,saw2-spm-cmd-pc = [00 20 80 10 90 a0 b0 07 3b 98 a2 b0 82
@@ -91,6 +97,8 @@
 		qcom,saw2-spm-dly= <0x20000400>;
 		qcom,saw2-spm-ctl = <0x1>;
 		qcom,saw2-spm-cmd-wfi = [03 0b 0f];
+		qcom,saw2-spm-cmd-ret = [42 1b 00 d0 c0 a0 90 03 d0 98 a2 c0
+				0b 00 42 1b 0f];
 		qcom,saw2-spm-cmd-spc = [00 20 80 10 90 a0 b0 03 3b 98 a2 b0 82
 				10 0b 30 06 26 30 0f];
 		qcom,saw2-spm-cmd-pc = [00 20 80 10 90 a0 b0 07 3b 98 a2 b0 82
@@ -116,7 +124,8 @@
 		qcom,vctl-timeout-us = <50>;
 		qcom,vctl-port = <0x0>;
 		qcom,phase-port = <0x1>;
-		qcom,saw2-spm-cmd-ret = [00 20 03 22 00 0f];
+		qcom,pfm-port = <0x2>;
+		qcom,saw2-spm-cmd-ret = [1f 00 20 03 22 00 0f];
 		qcom,saw2-spm-cmd-gdhs = [00 20 32 42 07 44 22 50 02 32 50 0f];
 		qcom,saw2-spm-cmd-pc = [00 10 32 b0 11 42 07 01 b0 12 44
 				50 02 32 50 0f];
@@ -130,6 +139,7 @@
 		qcom,lpm-resources@0 {
 			reg = <0x0>;
 			qcom,name = "vdd-dig";
+			qcom,resource-type = <0>;
 			qcom,type = <0x62706d73>;	/* "smpb" */
 			qcom,id = <0x02>;
 			qcom,key = <0x6e726f63>;	/* "corn" */
@@ -138,6 +148,7 @@
 		qcom,lpm-resources@1 {
 			reg = <0x1>;
 			qcom,name = "vdd-mem";
+			qcom,resource-type = <0>;
 			qcom,type = <0x62706d73>;	/* "smpb" */
 			qcom,id = <0x01>;
 			qcom,key = <0x7675>;		/* "uv" */
@@ -146,10 +157,17 @@
 		qcom,lpm-resources@2 {
 			reg = <0x2>;
 			qcom,name = "pxo";
+			qcom,resource-type = <0>;
 			qcom,type = <0x306b6c63>;	/* "clk0" */
 			qcom,id = <0x00>;
 			qcom,key = <0x62616e45>;	/* "Enab" */
 		};
+
+		qcom,lpm-resources@3 {
+			reg = <0x3>;
+			qcom,name = "l2";
+			qcom,resource-type = <1>;
+		};
 	};
 
 	qcom,lpm-levels {
@@ -174,6 +192,22 @@
 
 		qcom,lpm-level@1 {
 			reg = <0x1>;
+			qcom,mode = <4>;        /* MSM_PM_SLEEP_MODE_RETENTION*/
+			qcom,xo = <1>;          /* ON */
+			qcom,l2 = <3>;          /* ACTIVE */
+			qcom,vdd-mem-upper-bound = <1150000>; /* MAX */
+			qcom,vdd-mem-lower-bound = <1050000>; /* ACTIVE */
+			qcom,vdd-dig-upper-bound = <5>; /* MAX */
+			qcom,vdd-dig-lower-bound = <3>;  /* ACTIVE */
+			qcom,latency-us = <1500>;
+			qcom,ss-power = <200>;
+			qcom,energy-overhead = <576000>;
+			qcom,time-overhead = <2000>;
+		};
+
+
+		qcom,lpm-level@2 {
+			reg = <0x2>;
 			qcom,mode = <2>;        /* MSM_PM_SLEEP_MODE_STANDALONE_POWER_COLLAPSE */
 			qcom,xo = <1>;          /* ON */
 			qcom,l2 = <3>;          /* ACTIVE */
@@ -187,8 +221,8 @@
 			qcom,time-overhead = <2000>;
 		};
 
-		qcom,lpm-level@2 {
-			reg = <0x2>;
+		qcom,lpm-level@3 {
+			reg = <0x3>;
 			qcom,mode = <3>;        /* MSM_PM_SLEEP_MODE_POWER_COLLAPSE */
 			qcom,xo = <1>;          /* ON */
 			qcom,l2 = <1>;          /* GDHS */
@@ -202,8 +236,8 @@
 			qcom,time-overhead = <8500>;
 		};
 
-		qcom,lpm-level@3 {
-			reg = <0x3>;
+		qcom,lpm-level@4 {
+			reg = <0x4>;
 			qcom,mode = <3>;        /* MSM_PM_SLEEP_MODE_POWER_COLLAPSE */
 			qcom,xo = <1>;          /* ON */
 			qcom,l2 = <0>;          /* OFF */
@@ -217,8 +251,8 @@
 			qcom,time-overhead = <9000>;
 		};
 
-		qcom,lpm-level@4 {
-			reg = <0x4>;
+		qcom,lpm-level@5 {
+			reg = <0x5>;
 			qcom,mode = <3>;        /* MSM_PM_SLEEP_MODE_POWER_COLLAPSE */
 			qcom,xo = <1>;          /* ON */
 			qcom,l2 = <0>;          /* OFF */
@@ -232,8 +266,8 @@
 			qcom,time-overhead = <10000>;
 		};
 
-		qcom,lpm-level@5 {
-			reg = <0x5>;
+		qcom,lpm-level@6 {
+			reg = <0x6>;
 			qcom,mode = <3>;        /* MSM_PM_SLEEP_MODE_POWER_COLLAPSE */
 			qcom,xo = <0>;          /* OFF */
 			qcom,l2 = <1>;          /* GDHS */
@@ -247,8 +281,8 @@
 			qcom,time-overhead = <12000>;
 		};
 
-		qcom,lpm-level@6 {
-			reg = <0x6>;
+		qcom,lpm-level@7 {
+			reg = <0x7>;
 			qcom,mode = <3>;        /* MSM_PM_SLEEP_MODE_POWER_COLLAPSE */
 			qcom,xo = <0>;          /* OFF */
 			qcom,l2 = <0>;          /* OFF */
@@ -262,8 +296,8 @@
 			qcom,time-overhead = <18000>;
 		};
 
-		qcom,lpm-level@7 {
-			reg = <0x7>;
+		qcom,lpm-level@8 {
+			reg = <0x8>;
 			qcom,mode= <3>;         /* MSM_PM_SLEEP_MODE_POWER_COLLAPSE */
 			qcom,xo = <0>;          /* OFF */
 			qcom,l2 = <0>;          /* OFF */
@@ -277,8 +311,8 @@
 			qcom,time-overhead = <23500>;
 		};
 
-		qcom,lpm-level@8 {
-			reg = <0x8>;
+		qcom,lpm-level@9 {
+			reg = <0x9>;
 			qcom,mode= <3>;         /* MSM_PM_SLEEP_MODE_POWER_COLLAPSE */
 			qcom,xo = <0>;          /* OFF */
 			qcom,l2 = <0>;          /* OFF */
@@ -388,4 +422,10 @@
 		compatible = "qcom,pc-cntr";
 		reg = <0xfe805664 0x40>;
 	};
+
+	qcom,pm-8x60 {
+		compatible = "qcom,pm-8x60";
+		qcom,pc-mode = <0>; /*MSM_PC_TZ_L2_INT */
+		qcom,use-sync-timer;
+	};
 };
diff --git a/arch/arm/boot/dts/msm8974-rumi.dts b/arch/arm/boot/dts/msm8974-rumi.dts
index 3533151..738ff86 100644
--- a/arch/arm/boot/dts/msm8974-rumi.dts
+++ b/arch/arm/boot/dts/msm8974-rumi.dts
@@ -13,103 +13,10 @@
 /dts-v1/;
 
 /include/ "msm8974.dtsi"
+/include/ "msm8974-rumi.dtsi"
 
 / {
 	model = "Qualcomm MSM 8974 RUMI";
 	compatible = "qcom,msm8974-rumi", "qcom,msm8974";
 	qcom,msm-id = <126 15 0>;
-
-	timer {
-		clock-frequency = <5000000>;
-	};
-
-	serial@f995e000 {
-		status = "ok";
-	};
-
-	usb@f9a55000 {
-		status = "disable";
-	};
-
-	qcom,sdcc@f9824000 {
-                qcom,sdcc-clk-rates = <400000 19200000>;
-        };
-
-        qcom,sdcc@f98a4000 {
-                qcom,sdcc-clk-rates = <400000 19200000>;
-        };
-
-	qcom,sps@f998000 {
-		status = "disable";
-	};
-
-	spi@f9924000 {
-		status = "disable";
-	};
-
-	spi@f9923000 {
-		compatible = "qcom,spi-qup-v2";
-		reg = <0xf9923000 0x1000>;
-		interrupts = <0 95 0>;
-		spi-max-frequency = <24000000>;
-		#address-cells = <1>;
-		#size-cells = <0>;
-		gpios = <&msmgpio 3 0>, /* CLK  */
-			<&msmgpio 1 0>, /* MISO */
-			<&msmgpio 0 0>; /* MOSI */
-		cs-gpios = <&msmgpio 9 0>;
-
-		ethernet-switch@2 {
-			compatible = "simtec,ks8851";
-			reg = <2>;
-			interrupt-parent = <&msmgpio>;
-			interrupts = <90 0>;
-			spi-max-frequency = <5000000>;
-		};
-	};
-
-	i2c@f9966000 {
-		status = "disable";
-	};
-
-	i2c@f9967000 {
-		cell-index = <0>;
-		compatible = "qcom,i2c-qup";
-		reg = <0Xf9967000 0x1000>;
-		reg-names = "qup_phys_addr";
-		interrupts = <0 105 0>;
-		interrupt-names = "qup_err_intr";
-		qcom,i2c-bus-freq = <100000>;
-		qcom,i2c-src-freq = <24000000>;
-		gpios = <&msmgpio 83 0>, /* DAT  */
-			<&msmgpio 84 0>; /* CLK */
-	};
-
-	slim@fe12f000 {
-		status = "disable";
-	};
-
-	qcom,mdss_dsi@fd922800 {
-		status = "disable";
-	};
-
-	qcom,spmi@fc4c0000 {
-		status = "disable";
-	};
-
-	qcom,ssusb@F9200000 {
-		status = "disable";
-	};
-
-	qcom,lpass@fe200000 {
-		status = "disable";
-	};
-
-	qcom,pronto@fb21b000 {
-		status = "disable";
-	};
-
-	qcom,mss@fc880000 {
-		status = "disable";
-	};
 };
diff --git a/arch/arm/boot/dts/msm8974-rumi.dtsi b/arch/arm/boot/dts/msm8974-rumi.dtsi
new file mode 100644
index 0000000..d4b7793
--- /dev/null
+++ b/arch/arm/boot/dts/msm8974-rumi.dtsi
@@ -0,0 +1,107 @@
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+/ {
+	timer {
+		clock-frequency = <5000000>;
+	};
+
+	serial@f995e000 {
+		status = "ok";
+	};
+
+	usb@f9a55000 {
+		status = "disable";
+	};
+
+	qcom,sdcc@f9824000 {
+                qcom,sdcc-clk-rates = <400000 19200000>;
+        };
+
+        qcom,sdcc@f98a4000 {
+                qcom,sdcc-clk-rates = <400000 19200000>;
+        };
+
+	qcom,sps@f998000 {
+		status = "disable";
+	};
+
+	spi@f9924000 {
+		status = "disable";
+	};
+
+	spi@f9923000 {
+		compatible = "qcom,spi-qup-v2";
+		reg = <0xf9923000 0x1000>;
+		interrupts = <0 95 0>;
+		spi-max-frequency = <24000000>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+		gpios = <&msmgpio 3 0>, /* CLK  */
+			<&msmgpio 1 0>, /* MISO */
+			<&msmgpio 0 0>; /* MOSI */
+		cs-gpios = <&msmgpio 9 0>;
+
+		ethernet-switch@2 {
+			compatible = "simtec,ks8851";
+			reg = <2>;
+			interrupt-parent = <&msmgpio>;
+			interrupts = <90 0>;
+			spi-max-frequency = <5000000>;
+		};
+	};
+
+	i2c@f9966000 {
+		status = "disable";
+	};
+
+	i2c@f9967000 {
+		cell-index = <0>;
+		compatible = "qcom,i2c-qup";
+		reg = <0Xf9967000 0x1000>;
+		reg-names = "qup_phys_addr";
+		interrupts = <0 105 0>;
+		interrupt-names = "qup_err_intr";
+		qcom,i2c-bus-freq = <100000>;
+		qcom,i2c-src-freq = <24000000>;
+		gpios = <&msmgpio 83 0>, /* DAT  */
+			<&msmgpio 84 0>; /* CLK */
+	};
+
+	slim@fe12f000 {
+		status = "disable";
+	};
+
+	qcom,mdss_dsi@fd922800 {
+		status = "disable";
+	};
+
+	qcom,spmi@fc4c0000 {
+		status = "disable";
+	};
+
+	qcom,ssusb@F9200000 {
+		status = "disable";
+	};
+
+	qcom,lpass@fe200000 {
+		status = "disable";
+	};
+
+	qcom,pronto@fb21b000 {
+		status = "disable";
+	};
+
+	qcom,mss@fc880000 {
+		status = "disable";
+	};
+};
diff --git a/arch/arm/boot/dts/msm8974-sim.dts b/arch/arm/boot/dts/msm8974-sim.dts
index d5368fa..09ea419 100644
--- a/arch/arm/boot/dts/msm8974-sim.dts
+++ b/arch/arm/boot/dts/msm8974-sim.dts
@@ -13,86 +13,10 @@
 /dts-v1/;
 
 /include/ "msm8974.dtsi"
-/include/ "dsi-panel-sim-video.dtsi"
+/include/ "msm8974-sim.dtsi"
 
 / {
 	model = "Qualcomm MSM 8974 Simulator";
 	compatible = "qcom,msm8974-sim", "qcom,msm8974";
 	qcom,msm-id = <126 16 0>;
-
-	qcom,mdss_dsi@fd922800 {
-		qcom,mdss_dsi_sim_video {
-			status = "ok";
-		};
-	};
-
-	serial@f991f000 {
-		status = "ok";
-	};
-
-	serial@f995e000 {
-		status = "ok";
-	};
-};
-
-&jpeg_iommu {
-		qcom,iommu-ctx@fda6c000 {
-			interrupts = <0 69 0>;
-		};
-
-		qcom,iommu-ctx@fda6d000 {
-			interrupts = <0 70 0>;
-		};
-
-		qcom,iommu-ctx@fda6e000 {
-			interrupts = <0 71 0>;
-		};
-};
-
-&mdp_iommu {
-		qcom,iommu-ctx@fd930000 {
-			interrupts = <0 46 0>;
-		};
-
-		qcom,iommu-ctx@fd931000 {
-			interrupts = <0 47 0>;
-		};
-};
-
-&venus_iommu {
-		qcom,iommu-ctx@fdc8c000 {
-			interrupts = <0 43 0>;
-		};
-
-		qcom,iommu-ctx@fdc8d000 {
-			interrupts = <0 42 0>;
-		};
-
-		qcom,iommu-ctx@fdc8e000 {
-			interrupts = <0 41 0>;
-		};
-};
-
-&kgsl_iommu {
-		qcom,iommu-ctx@fdb18000 {
-			interrupts = <0 240 0>;
-		};
-
-		qcom,iommu-ctx@fdb19000 {
-			interrupts = <0 241 0>;
-		};
-};
-
-&vfe_iommu {
-		qcom,iommu-ctx@fda4c000 {
-			interrupts = <0 64 0>;
-		};
-
-		qcom,iommu-ctx@fda4d000 {
-			interrupts = <0 65 0>;
-		};
-
-		qcom,iommu-ctx@fda4e000 {
-			interrupts = <0 66 0>;
-		};
 };
diff --git a/arch/arm/boot/dts/msm8974-sim.dtsi b/arch/arm/boot/dts/msm8974-sim.dtsi
new file mode 100644
index 0000000..41e37de
--- /dev/null
+++ b/arch/arm/boot/dts/msm8974-sim.dtsi
@@ -0,0 +1,91 @@
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+/include/ "dsi-panel-sim-video.dtsi"
+
+/ {
+	qcom,mdss_dsi@fd922800 {
+		qcom,mdss_dsi_sim_video {
+			status = "ok";
+		};
+	};
+
+	serial@f991f000 {
+		status = "ok";
+	};
+
+	serial@f995e000 {
+		status = "ok";
+	};
+};
+
+&jpeg_iommu {
+		qcom,iommu-ctx@fda6c000 {
+			interrupts = <0 69 0>;
+		};
+
+		qcom,iommu-ctx@fda6d000 {
+			interrupts = <0 70 0>;
+		};
+
+		qcom,iommu-ctx@fda6e000 {
+			interrupts = <0 71 0>;
+		};
+};
+
+&mdp_iommu {
+		qcom,iommu-ctx@fd930000 {
+			interrupts = <0 46 0>;
+		};
+
+		qcom,iommu-ctx@fd931000 {
+			interrupts = <0 47 0>;
+		};
+};
+
+&venus_iommu {
+		qcom,iommu-ctx@fdc8c000 {
+			interrupts = <0 43 0>;
+		};
+
+		qcom,iommu-ctx@fdc8d000 {
+			interrupts = <0 42 0>;
+		};
+
+		qcom,iommu-ctx@fdc8e000 {
+			interrupts = <0 41 0>;
+		};
+};
+
+&kgsl_iommu {
+		qcom,iommu-ctx@fdb18000 {
+			interrupts = <0 240 0>;
+		};
+
+		qcom,iommu-ctx@fdb19000 {
+			interrupts = <0 241 0>;
+		};
+};
+
+&vfe_iommu {
+		qcom,iommu-ctx@fda4c000 {
+			interrupts = <0 64 0>;
+		};
+
+		qcom,iommu-ctx@fda4d000 {
+			interrupts = <0 65 0>;
+		};
+
+		qcom,iommu-ctx@fda4e000 {
+			interrupts = <0 66 0>;
+		};
+};
diff --git a/arch/arm/boot/dts/msm8974.dtsi b/arch/arm/boot/dts/msm8974.dtsi
index b992e86..c554a5a 100644
--- a/arch/arm/boot/dts/msm8974.dtsi
+++ b/arch/arm/boot/dts/msm8974.dtsi
@@ -11,7 +11,7 @@
  */
 
 /include/ "skeleton.dtsi"
-/include/ "msm8974_pm.dtsi"
+/include/ "msm8974-pm.dtsi"
 /include/ "msm8974-iommu.dtsi"
 /include/ "msm8974-camera.dtsi"
 /include/ "msm8974-coresight.dtsi"
@@ -42,6 +42,15 @@
 		reg = <0xfd510000 0x4000>;
 	};
 
+	wcd9xxx_intc: wcd9xxx-irq {
+		compatible = "qcom,wcd9xxx-irq";
+		interrupt-controller;
+		#interrupt-cells = <1>;
+		interrupt-parent = <&msmgpio>;
+		interrupts = <72 0>;
+		interrupt-names = "cdc-int";
+	};
+
 	timer {
 		compatible = "qcom,msm-qtimer", "arm,armv7-timer";
 		interrupts = <1 2 0 1 3 0>;
@@ -52,7 +61,7 @@
 		compatible = "qcom,msm-vidc";
 		reg = <0xfdc00000 0xff000>;
 		interrupts = <0 44 0>;
-		vidc-cp-map = <0x1000000 0x40000000>;
+		vidc-cp-map = <0x1000000 0x3f000000>;
 		vidc-ns-map = <0x40000000 0x40000000>;
 		load-freq-tbl = <979200 410000000>,
 			<783360 410000000>,
@@ -85,7 +94,7 @@
 		status = "disabled";
 	};
 
-	usb@f9a55000 {
+	usb_otg: usb@f9a55000 {
 		compatible = "qcom,hsusb-otg";
 		reg = <0xf9a55000 0x400>;
 		interrupts = <0 134 0 0 140 0>;
@@ -99,14 +108,15 @@
 		qcom,hsusb-otg-mode = <1>;
 		qcom,hsusb-otg-otg-control = <1>;
 		qcom,hsusb-otg-disable-reset;
+		qcom,hsusb-otg-pnoc-errata-fix;
 
-		qcom,msm_bus,name = "usb2";
-		qcom,msm_bus,num_cases = <2>;
-		qcom,msm_bus,active_only = <0>;
-		qcom,msm_bus,num_paths = <1>;
-		qcom,msm_bus,vectors =
+		qcom,msm-bus,name = "usb2";
+		qcom,msm-bus,num-cases = <2>;
+		qcom,msm-bus,active-only = <0>;
+		qcom,msm-bus,num-paths = <1>;
+		qcom,msm-bus,vectors-KBps =
 				<87 512 0 0>,
-				<87 512 60000000 960000000>;
+				<87 512 60000 960000>;
 	};
 
 	android_usb@fc42b0c8 {
@@ -251,9 +261,26 @@
 			<&msmgpio 54 0>, /* MISO */
 			<&msmgpio 53 0>; /* MOSI */
 		cs-gpios = <&msmgpio 55 0>;
+
+		epm-adc@0 {
+			compatible = "cy,epm-adc-cy8c5568lti-114";
+			reg = <0>;
+			interrupt-parent = <&msmgpio>;
+			spi-max-frequency = <960000>;
+			qcom,channels = <31>;
+			qcom,gain = <100 100 100 50 100 100 1 100 1 50
+					1 100 1 100 50 50 50 50 50 50
+					100 50 100 50 50 50 50 50 50 50
+					50>;
+			qcom,rsense = <2 2 2 200 20 2 1 2 1 30
+					1 10 1 30 50 30 500 30 100 30
+					100 500 20 200 1000 20 1000 1000 70 200
+					50>;
+			qcom,channel-type = <0x2a40>;
+		};
 	};
 
-	slim@fe12f000 {
+	slim_msm: slim@fe12f000 {
 		cell-index = <1>;
 		compatible = "qcom,slim-msm";
 		reg = <0xfe12f000 0x35000>,
@@ -268,6 +295,9 @@
 			compatible = "qcom,taiko-slim-pgd";
 			elemental-addr = [00 01 A0 00 17 02];
 
+			interrupt-parent = <&wcd9xxx_intc>;
+			interrupts = <0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28>;
+
 			qcom,cdc-reset-gpio = <&msmgpio 63 0>;
 
 			cdc-vdd-buck-supply = <&pm8941_s2>;
@@ -327,10 +357,10 @@
 			"MIC BIAS1 Internal1", "Handset Mic",
 			"AMIC2", "MIC BIAS2 External",
 			"MIC BIAS2 External", "Headset Mic",
-			"AMIC3", "MIC BIAS3 Internal1",
-			"MIC BIAS3 Internal1", "ANCRight Headset Mic",
-			"AMIC4", "MIC BIAS1 Internal2",
-			"MIC BIAS1 Internal2", "ANCLeft Headset Mic",
+			"AMIC3", "MIC BIAS2 External",
+			"MIC BIAS2 External", "ANCRight Headset Mic",
+			"AMIC4", "MIC BIAS2 External",
+			"MIC BIAS2 External", "ANCLeft Headset Mic",
 			"DMIC1", "MIC BIAS1 External",
 			"MIC BIAS1 External", "Digital Mic1",
 			"DMIC2", "MIC BIAS1 External",
@@ -497,20 +527,75 @@
 					 <0x1e70008c>; /* LPG_CHAN12 */
 
 		qcom,pm8941@1 {
-			qcom,leds@d800 {
-				qcom,name = "wled:backlight";
-				linux,default-trigger = "bkl-trigger";
-				qcom,cs-out-en;
-				qcom,op-fdbck;
-				qcom,default-state = "on";
-				qcom,max-current = <25>;
-				qcom,ctrl-delay-us = <0>;
-				qcom,boost-curr-lim = <3>;
-				qcom,cp-sel = <0>;
-				qcom,switch-freq = <2>;
-				qcom,ovp-val = <2>;
-				qcom,num-strings = <1>;
+			qcom,leds@d300 {
 				status = "okay";
+				qcom,flash_0 {
+					qcom,max-current = <1000>;
+					qcom,default-state = "off";
+					qcom,headroom = <0>;
+					qcom,duration = <1280>;
+					qcom,clamp-curr = <200>;
+					qcom,startup-dly = <1>;
+					qcom,safety-timer;
+					label = "flash";
+					linux,default-trigger =
+						"flash0_trigger";
+					qcom,id = <1>;
+					linux,name = "led:flash_0";
+					qcom,current = <625>;
+				};
+
+				qcom,flash_1 {
+					qcom,max-current = <1000>;
+					qcom,default-state = "off";
+					qcom,headroom = <0>;
+					qcom,duration = <1280>;
+					qcom,clamp-curr = <200>;
+					qcom,startup-dly = <1>;
+					qcom,safety-timer;
+					linux,default-trigger =
+						"flash1_trigger";
+					label = "flash";
+					qcom,id = <2>;
+					linux,name = "led:flash_1";
+					qcom,current = <625>;
+				};
+			};
+
+			qcom,leds@d400 {
+				status = "disabled";
+			};
+
+			qcom,leds@d500 {
+				status = "disabled";
+			};
+
+			qcom,leds@d600 {
+				status = "disabled";
+			};
+
+			qcom,leds@d700 {
+				status = "disabled";
+			};
+
+			qcom,leds@d800 {
+				status = "okay";
+				qcom,wled_0 {
+					label = "wled";
+					linux,name = "wled:backlight";
+					linux,default-trigger = "bkl-trigger";
+					qcom,cs-out-en;
+					qcom,op-fdbck;
+					qcom,default-state = "off";
+					qcom,max-current = <25>;
+					qcom,ctrl-delay-us = <0>;
+					qcom,boost-curr-lim = <3>;
+					qcom,cp-sel = <0>;
+					qcom,switch-freq = <2>;
+					qcom,ovp-val = <2>;
+					qcom,num-strings = <1>;
+					qcom,id = <0>;
+				};
 			};
 
 			qcom,leds@d900 {
@@ -548,13 +633,16 @@
 			qcom,leds@e100 {
 				status = "disabled";
 			};
+
 		};
 	};
 
-	i2c@f9967000 {
+	i2c@f9967000 { /* BLSP#11 */
 		cell-index = <0>;
 		compatible = "qcom,i2c-qup";
 		reg = <0Xf9967000 0x1000>;
+		#address-cells = <1>;
+		#size-cells = <0>;
 		reg-names = "qup_phys_addr";
 		interrupts = <0 105 0>;
 		interrupt-names = "qup_err_intr";
@@ -615,26 +703,27 @@
 		l2_hfpll_b-supply = <&pm8941_l12_ao>;
 	};
 
-	qcom,ssusb@f9200000 {
+	usb3: qcom,ssusb@f9200000 {
 		compatible = "qcom,dwc-usb3-msm";
 		reg = <0xf9200000 0xfc000>,
 			  <0xfd4ab000 0x4>;
-		interrupts = <0 131 0 0 179 0>;
-		interrupt-names = "irq", "otg_irq";
+		interrupts = <0 131 0>, <0 179 0>, <0 133 0>;
+		interrupt-names = "irq", "otg_irq", "hs_phy_irq";
 		SSUSB_VDDCX-supply = <&pm8841_s2>;
 		SSUSB_1p8-supply = <&pm8941_l6>;
 		HSUSB_VDDCX-supply = <&pm8841_s2>;
 		HSUSB_1p8-supply = <&pm8941_l6>;
 		HSUSB_3p3-supply = <&pm8941_l24>;
+		vbus_dwc3-supply = <&pm8941_mvs1>;
 		qcom,dwc-usb3-msm-dbm-eps = <4>;
 
-		qcom,msm_bus,name = "usb3";
-		qcom,msm_bus,num_cases = <2>;
-		qcom,msm_bus,active_only = <0>;
-		qcom,msm_bus,num_paths = <1>;
-		qcom,msm_bus,vectors =
+		qcom,msm-bus,name = "usb3";
+		qcom,msm-bus,num-cases = <2>;
+		qcom,msm-bus,active-only = <0>;
+		qcom,msm-bus,num-paths = <1>;
+		qcom,msm-bus,vectors-KBps =
 				<61 512 0 0>,
-				<61 512 240000000 960000000>;
+				<61 512 240000 960000>;
 	};
 
 	gdsc_oxili_gx: qcom,gdsc@fd8c4024 {
@@ -646,6 +735,7 @@
 		reg = <0xfe200000 0x00100>,
 		      <0xfd485100 0x00010>;
 		reg-names = "qdsp6_base", "halt_base";
+		interrupts = <0 162 1>;
 
 		qcom,firmware-name = "adsp";
 	};
@@ -690,6 +780,11 @@
 		compatible = "qcom,msm-pcm-afe";
 	};
 
+	qcom,msm-dai-q6-hdmi {
+		compatible = "qcom,msm-dai-q6-hdmi";
+		qcom,msm-dai-q6-dev-id = <8>;
+	};
+
 	qcom,msm-dai-q6 {
 		compatible = "qcom,msm-dai-q6";
 		qcom,msm-dai-q6-sb-0-rx {
@@ -712,6 +807,16 @@
 			qcom,msm-dai-q6-dev-id = <12289>;
 		};
 
+		qcom,msm-dai-q6-int-fm-rx {
+			compatible = "qcom,msm-dai-q6-dev";
+			qcom,msm-dai-q6-dev-id = <12292>;
+		};
+
+		qcom,msm-dai-q6-int-fm-tx {
+			compatible = "qcom,msm-dai-q6-dev";
+			qcom,msm-dai-q6-dev-id = <12293>;
+		};
+
 		qcom,msm-dai-q6-be-afe-pcm-rx {
 			compatible = "qcom,msm-dai-q6-dev";
 			qcom,msm-dai-q6-dev-id = <224>;
@@ -761,10 +866,13 @@
 
 	qcom,msm-ocmem-audio {
 		compatible = "qcom,msm-ocmem-audio";
-		qcom,msm-ocmem-audio-src-id = <11>;
-		qcom,msm-ocmem-audio-dst-id = <604>;
-		qcom,msm-ocmem-audio-ab = <32505856>;
-		qcom,msm-ocmem-audio-ib = <32505856>;
+		qcom,msm-bus,name = "audio-ocmem";
+		qcom,msm-bus,num-cases = <2>;
+		qcom,msm-bus,active-only = <0>;
+		qcom,msm-bus,num-paths = <1>;
+		qcom,msm-bus,vectors-KBps =
+			<11 604 0 0>,
+			<11 604 32506 32506>;
 	};
 
 	qcom,mss@fc880000 {
@@ -773,32 +881,24 @@
 		      <0xfd485000 0x400>,
 		      <0xfc820000 0x020>,
 		      <0xfc401680 0x004>,
-		      <0xfc980008 0x004>;
+		      <0x0d1fc000 0x4000>;
 		reg-names = "qdsp6_base", "halt_base", "rmb_base",
-			    "restart_reg", "clamp_reg";
+			    "restart_reg", "metadata_base";
 
+		interrupts = <0 24 1>;
 		vdd_mss-supply = <&pm8841_s3>;
 
 		qcom,firmware-name = "mba";
 		qcom,pil-self-auth = <1>;
 	};
 
-	qcom,mba@fc820000 {
-		compatible = "qcom,pil-mba";
-		reg = <0xfc820000 0x0020>,
-		      <0x0d1fc000 0x4000>;
-		reg-names = "rmb_base", "metadata_base";
-
-		qcom,firmware-name = "modem";
-		qcom,depends-on    = "mba";
-	};
-
 	qcom,pronto@fb21b000 {
 		compatible = "qcom,pil-pronto";
 		reg = <0xfb21b000 0x3000>,
 		      <0xfc401700 0x4>,
 		      <0xfd485300 0xc>;
 		reg-names = "pmu_base", "clk_base", "halt_base";
+		interrupts = <0 149 1>;
 		vdd_pronto_pll-supply = <&pm8941_l12>;
 
 		qcom,firmware-name = "wcnss";
@@ -883,6 +983,15 @@
 
 	qcom,qseecom@fe806000 {
 		compatible = "qcom,qseecom";
+		qcom,msm-bus,name = "qseecom-noc";
+		qcom,msm-bus,num-cases = <4>;
+		qcom,msm-bus,active-only = <0>;
+		qcom,msm-bus,num-paths = <1>;
+		qcom,msm-bus,vectors-KBps =
+				<55 512 0 0>,
+				<55 512 3936000 393600>,
+				<55 512 3936000 393600>,
+				<55 512 3936000 393600>;
 	};
 
 	qcom,wdt@f9017000 {
@@ -894,9 +1003,9 @@
 		qcom,ipi-ping = <1>;
 	};
 
-	qcom,tz-log@fe805720 {
+	qcom,tz-log@fc03000 {
 		compatible = "qcom,tz-log";
-		reg = <0xfe805720 0x1000>;
+		reg = <0x0fc03000 0x1000>;
 	};
 
 	qcom,venus@fdce0000 {
@@ -907,8 +1016,6 @@
 		vdd-supply = <&gdsc_venus>;
 
 		qcom,firmware-name = "venus";
-		qcom,firmware-min-paddr = <0xF500000>;
-		qcom,firmware-max-paddr = <0xFA00000>;
 	};
 
 	qcom,cache_erp {
@@ -928,9 +1035,10 @@
 	tsens@fc4a8000 {
 		compatible = "qcom,msm-tsens";
 		reg = <0xfc4a8000 0x2000>,
-		      <0xfc4b80d0 0x5>;
+		      <0xfc4b8000 0x1000>;
 		reg-names = "tsens_physical", "tsens_eeprom_physical";
 		interrupts = <0 184 0>;
+		qcom,calibration-less-mode;
 		qcom,sensors = <11>;
 		qcom,slope = <3200 3200 3200 3200 3200 3200 3200 3200 3200
 				3200 3200>;
@@ -942,6 +1050,12 @@
 		qcom,memory-reservation-size = <0x100000>; /* 1M EBI1 buffer */
 	};
 
+	qcom,msm-contig-mem {
+		compatible = "qcom,msm-contig-mem";
+		qcom,memory-reservation-type = "EBI1";
+		qcom,memory-reservation-size = <0x280000>; /* 2.5M EBI1 buffer */
+	};
+
         qcom,qcedev@fd440000 {
 		compatible = "qcom,qcedev";
 		reg = <0xfd440000 0x20000>,
@@ -997,7 +1111,7 @@
 			qcom,dst-bam-physical-address = <0xf9304000>;
 			qcom,dst-bam-pipe-index = <2>;
 			qcom,data-fifo-offset = <0xf0000>;
-			qcom,data-fifo-size = <0x4000>;
+			qcom,data-fifo-size = <0x1800>;
 			qcom,descriptor-fifo-offset = <0xf4000>;
 			qcom,descriptor-fifo-size = <0x1400>;
 		};
@@ -1050,7 +1164,10 @@
                compatible = "qcom,msm-wdog-debug";
                reg = <0xfc401000 0x1000>;
         };
-
+        qcom,msm-mem-hole {
+                compatible = "qcom,msm-mem-hole";
+                qcom,memblock-remove = <0x8400000 0x7b00000>; /* Address and Size of Hole */
+        };
 };
 
 /include/ "msm-pm8x41-rpm-regulator.dtsi"
diff --git a/arch/arm/boot/dts/msm9625-cdp.dts b/arch/arm/boot/dts/msm9625-cdp.dts
index aa1ec92..e48572c 100644
--- a/arch/arm/boot/dts/msm9625-cdp.dts
+++ b/arch/arm/boot/dts/msm9625-cdp.dts
@@ -17,7 +17,7 @@
 / {
 	model = "Qualcomm MSM 9625 CDP";
 	compatible = "qcom,msm9625-cdp", "qcom,msm9625";
-	qcom,msm-id = <134 1 0>;
+	qcom,msm-id = <134 1 0>, <152 1 0>;
 };
 
 /* PM8019 GPIO and MPP configuration */
@@ -32,6 +32,14 @@
 	};
 
 	gpio@c300 { /* GPIO 4 */
+		/* ext_2p95v regulator enable config */
+		qcom,mode = <1>; /* Digital output */
+		qcom,output-type = <0>; /* CMOS */
+		qcom,invert = <0>; /* Output low */
+		qcom,out-strength = <1>; /* Low */
+		qcom,vin-sel = <2>; /* PM8019 L11 - 1.8V */
+		qcom,src-sel = <0>; /* Constant */
+		qcom,master-en = <1>; /* Enable GPIO */
 	};
 
 	gpio@c400 { /* GPIO 5 */
diff --git a/arch/arm/boot/dts/msm9625-ion.dtsi b/arch/arm/boot/dts/msm9625-ion.dtsi
new file mode 100644
index 0000000..8183264
--- /dev/null
+++ b/arch/arm/boot/dts/msm9625-ion.dtsi
@@ -0,0 +1,35 @@
+/* Copyright (c) 2012, Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+/ {
+	qcom,ion {
+		compatible = "qcom,msm-ion";
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		qcom,ion-heap@30 { /* SYSTEM HEAP */
+			reg = <30>;
+		};
+
+		qcom,ion-heap@25 { /* IOMMU HEAP */
+			reg = <25>;
+		};
+
+		qcom,ion-heap@28 { /* AUDIO HEAP */
+			compatible = "qcom,msm-ion-reserve";
+			reg = <28>;
+			qcom,heap-align = <0x1000>;
+			qcom,memory-reservation-type = "EBI1"; /* reserve EBI memory */
+			qcom,memory-reservation-size = <0xAF000>;
+		};
+	};
+};
diff --git a/arch/arm/boot/dts/msm9625-mtp.dts b/arch/arm/boot/dts/msm9625-mtp.dts
index 3ec949f..606a4bc 100644
--- a/arch/arm/boot/dts/msm9625-mtp.dts
+++ b/arch/arm/boot/dts/msm9625-mtp.dts
@@ -17,7 +17,7 @@
 / {
 	model = "Qualcomm MSM 9625 MTP";
 	compatible = "qcom,msm9625-mtp", "qcom,msm9625";
-	qcom,msm-id = <134 8 0>;
+	qcom,msm-id = <134 7 0>, <152 7 0>;
 };
 
 /* PM8019 GPIO and MPP configuration */
@@ -32,6 +32,14 @@
 	};
 
 	gpio@c300 { /* GPIO 4 */
+		/* ext_2p95v regulator enable config */
+		qcom,mode = <1>; /* Digital output */
+		qcom,output-type = <0>; /* CMOS */
+		qcom,invert = <0>; /* Output low */
+		qcom,out-strength = <1>; /* Low */
+		qcom,vin-sel = <2>; /* PM8019 L11 - 1.8V */
+		qcom,src-sel = <0>; /* Constant */
+		qcom,master-en = <1>; /* Enable GPIO */
 	};
 
 	gpio@c400 { /* GPIO 5 */
diff --git a/arch/arm/boot/dts/msm9625-pm.dtsi b/arch/arm/boot/dts/msm9625-pm.dtsi
new file mode 100644
index 0000000..86e0cf7
--- /dev/null
+++ b/arch/arm/boot/dts/msm9625-pm.dtsi
@@ -0,0 +1,207 @@
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+/include/ "skeleton.dtsi"
+
+/ {
+	qcom,spm@f9009000 {
+		compatible = "qcom,spm-v2";
+		#address-cells = <1>;
+		#size-cells = <1>;
+		reg = <0xf9009000 0x1000>;
+		qcom,core-id = <0>;
+		qcom,saw2-ver-reg = <0xfd0>;
+		qcom,saw2-cfg = <0x101>;
+		qcom,saw2-spm-dly= <0>;
+		qcom,saw2-spm-ctl = <0x1>;
+		qcom,saw2-spm-cmd-wfi = [04 03 04 0f];
+		qcom,saw2-spm-cmd-spc = [34 04 44 14 24 54 03 54 44 14 04 24
+		3e 0f];
+		qcom,saw2-spm-cmd-pc = [34 04 44 14 24 54 07 54 44 14 04 24
+		3e 0f];
+	};
+
+	qcom,lpm-resources {
+		compatible = "qcom,lpm-resources";
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		qcom,lpm-resources@0 {
+			reg = <0x0>;
+			qcom,name = "vdd-dig";
+			qcom,resource-type = <0>;
+			qcom,type = <0x616F646C>;       /* "ldoa" */
+			qcom,id = <0x0A>;
+			qcom,key = <0x6e726f63>;	/* "corn" */
+		};
+
+		qcom,lpm-resources@1 {
+			reg = <0x1>;
+			qcom,name = "vdd-mem";
+			qcom,resource-type = <0>;
+			qcom,type = <0x616F646C>;       /* "ldoa" */
+			qcom,id = <0x0C>;
+			qcom,key =  <0x7675>;		/* "uv" */
+		};
+
+		qcom,lpm-resources@2 {
+			reg = <0x2>;
+			qcom,name = "pxo";
+			qcom,resource-type = <0>;
+			qcom,type = <0x306b6c63>;	/* "clk0" */
+			qcom,id = <0x00>;
+			qcom,key = <0x62616e45>;	/* "Enab" */
+		};
+	};
+
+	qcom,lpm-levels {
+		compatible = "qcom,lpm-levels";
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		qcom,lpm-level@0 {
+			reg = <0x0>;
+			qcom,mode = <0>;        /* MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT */
+			qcom,xo = <1>;          /* ON */
+			qcom,l2 = <3>;          /* ACTIVE */
+			qcom,vdd-mem-upper-bound = <1050000>; /* SUPER TURBO */
+			qcom,vdd-mem-lower-bound = <950000>; /* NORMAL */
+			qcom,vdd-dig-upper-bound = <6>; /* SUPER TURBO */
+			qcom,vdd-dig-lower-bound = <4>;  /* NORMAL */
+			qcom,latency-us = <100>;
+			qcom,ss-power = <8000>;
+			qcom,energy-overhead = <100000>;
+			qcom,time-overhead = <1>;
+		};
+
+		qcom,lpm-level@1 {
+			reg = <0x1>;
+			qcom,mode = <2>;        /* MSM_PM_SLEEP_MODE_STANDALONE_POWER_COLLAPSE */
+			qcom,xo = <1>;          /* ON */
+			qcom,l2 = <3>;          /* ACTIVE */
+			qcom,vdd-mem-upper-bound = <1050000>; /* SUPER TURBO */
+			qcom,vdd-mem-lower-bound = <950000>; /* NORMAL */
+			qcom,vdd-dig-upper-bound = <6>; /* SUPER TURBO */
+			qcom,vdd-dig-lower-bound = <4>;  /* NORMAL */
+			qcom,latency-us = <2000>;
+			qcom,ss-power = <5000>;
+			qcom,energy-overhead = <60100000>;
+			qcom,time-overhead = <3000>;
+		};
+
+		qcom,lpm-level@2 {
+			reg = <0x2>;
+			qcom,mode = <3>;        /* MSM_PM_SLEEP_MODE_POWER_COLLAPSE */
+			qcom,xo = <1>;          /* ON */
+			qcom,l2 = <1>;          /* GDHS */
+			qcom,vdd-mem-upper-bound = <1050000>; /* SUPER TURBO */
+			qcom,vdd-mem-lower-bound = <950000>; /* NORMAL */
+			qcom,vdd-dig-upper-bound = <6>; /* SUPER TURBO */
+			qcom,vdd-dig-lower-bound = <4>;  /* NORMAL */
+			qcom,latency-us = <3500>;
+			qcom,ss-power = <5000>;
+			qcom,energy-overhead = <60350000>;
+			qcom,time-overhead = <6300>;
+		};
+
+		qcom,lpm-level@3 {
+			reg = <0x3>;
+			qcom,mode= <3>;         /* MSM_PM_SLEEP_MODE_POWER_COLLAPSE */
+			qcom,xo = <0>;          /* OFF */
+			qcom,l2 = <0>;          /* OFF */
+			qcom,vdd-mem-upper-bound = <1050000>; /* SUPER TURBO */
+			qcom,vdd-mem-lower-bound = <950000>; /* NORMAL */
+			qcom,vdd-dig-upper-bound = <6>; /* SUPER TURBO */
+			qcom,vdd-dig-lower-bound = <4>;  /* NORMAL */
+			qcom,latency-us = <6800>;
+			qcom,ss-power = <2000>;
+			qcom,energy-overhead = <71850000>;
+			qcom,time-overhead = <13300>;
+		};
+
+		qcom,lpm-level@4 {
+			reg = <0x4>;
+			qcom,mode= <3>;         /* MSM_PM_SLEEP_MODE_POWER_COLLAPSE */
+			qcom,xo = <0>;          /* OFF */
+			qcom,l2 = <0>;          /* OFF */
+			qcom,vdd-mem-upper-bound = <950000>; /* SVS SOC */
+			qcom,vdd-mem-lower-bound = <675000>; /* RETENTION */
+			qcom,vdd-dig-upper-bound = <3>; /* SVS SOC */
+			qcom,vdd-dig-lower-bound = <1>; /* RETENTION */
+			qcom,latency-us = <9800>;
+			qcom,ss-power = <0>;
+			qcom,energy-overhead = <76350000>;
+			qcom,time-overhead = <28300>;
+		};
+	};
+
+	qcom,pm-boot {
+		compatible = "qcom,pm-boot";
+		qcom,mode = <0>; /* MSM_PM_BOOT_CONFIG_TZ */
+	};
+
+	qcom,mpm@fc4281d0 {
+		compatible = "qcom,mpm-v2";
+		reg = <0xfc4281d0 0x1000>, /* MSM_RPM_MPM_BASE 4K */
+		    <0xf9011008 0x4>;   /* MSM_APCS_GCC_BASE 4K */
+		reg-names = "vmpm", "ipc";
+		interrupts = <0 171 1>;
+
+		qcom,ipc-bit-offset = <1>;
+
+		qcom,gic-parent = <&intc>;
+		qcom,gic-map = <41 172>, /* usb2_hsic_async_wakeup_irq */
+			<0xff 208>; /* summary_irq_kpss */
+
+		qcom,gpio-parent = <&msmgpio>;
+		qcom,gpio-map = <4  1>,
+			<5  5>,
+			<6  9>,
+			<7  18>,
+			<8  20>,
+			<9  24>,
+			<10  27>,
+			<11  28>,
+			<12  34>,
+			<13  35>,
+			<14  37>,
+			<15  42>,
+			<16  44>,
+			<17  46>,
+			<18  50>,
+			<19  54>,
+			<20  59>,
+			<21  61>,
+			<22  62>,
+			<23  64>,
+			<24  65>,
+			<25  66>,
+			<26  67>,
+			<27  68>,
+			<28  71>,
+			<29  72>,
+			<30  73>,
+			<31  74>,
+			<32  75>,
+			<33  77>,
+			<34  79>,
+			<35  80>,
+			<36  82>,
+			<37  86>;
+	};
+
+	qcom,pm-8x60 {
+		compatible = "qcom,pm-8x60";
+		qcom,pc-mode = <2>; /*MSM_PC_TZ_L2_EXT */
+		qcom,use-sync-timer;
+	};
+};
diff --git a/arch/arm/boot/dts/msm9625-regulator.dtsi b/arch/arm/boot/dts/msm9625-regulator.dtsi
index c42af2c..dccc723 100644
--- a/arch/arm/boot/dts/msm9625-regulator.dtsi
+++ b/arch/arm/boot/dts/msm9625-regulator.dtsi
@@ -10,155 +10,252 @@
  * GNU General Public License for more details.
  */
 
-&spmi_bus {
-	qcom,pm8019@1 {
-		pm8019_s1: regulator@1400 {
+&rpm_bus {
+	rpm-regulator-smpa1 {
+		status = "okay";
+		pm8019_s1: regulator-s1 {
 			regulator-min-microvolt = <800000>;
 			regulator-max-microvolt = <1050000>;
-			qcom,enable-time = <500>;
+			qcom,init-voltage = <1050000>;
 			status = "okay";
 		};
+	};
 
-		pm8019_s2: regulator@1700 {
+	rpm-regulator-smpa2 {
+		status = "okay";
+		qcom,allow-atomic = <1>;
+		pm8019_s2: regulator-s2 {
 			regulator-min-microvolt = <1250000>;
 			regulator-max-microvolt = <1250000>;
+			qcom,init-voltage = <1250000>;
+			qcom,init-current = <100>;
 			qcom,system-load = <100000>;
-			qcom,enable-time = <500>;
 			regulator-always-on;
 			status = "okay";
 		};
+	};
 
-		pm8019_s3: regulator@1a00 {
-			regulator-min-microvolt = <1100000>;
+	rpm-regulator-smpa3 {
+		status = "okay";
+		pm8019_s3: regulator-s3 {
+			regulator-min-microvolt = <1000000>;
 			regulator-max-microvolt = <1100000>;
+			qcom,init-voltage = <1100000>;
+			qcom,init-current = <100>;
 			qcom,system-load = <100000>;
-			qcom,enable-time = <500>;
 			regulator-always-on;
 			status = "okay";
 		};
+		pm8019_s3_ao: regulator-s3-ao {
+			compatible = "qcom,rpm-regulator-smd";
+			regulator-name = "8019_s3_ao";
+			qcom,set = <1>;
+			regulator-min-microvolt = <1000000>;
+			regulator-max-microvolt = <1100000>;
+			status = "okay";
+		};
+	};
 
-		pm8019_s4: regulator@1d00 {
+	rpm-regulator-smpa4 {
+		status = "okay";
+		pm8019_s4: regulator-s4 {
 			regulator-min-microvolt = <1800000>;
 			regulator-max-microvolt = <2075000>;
+			qcom,init-voltage = <2075000>;
+			qcom,init-current = <100>;
 			qcom,system-load = <100000>;
-			qcom,enable-time = <500>;
 			regulator-always-on;
 			status = "okay";
 		};
+	};
 
-		pm8019_l1: regulator@4000 {
+	rpm-regulator-ldoa1 {
+		status = "okay";
+		pm8019_l1: regulator-l1 {
 			parent-supply = <&pm8019_s2>;
 			regulator-min-microvolt = <1225000>;
 			regulator-max-microvolt = <1225000>;
-			qcom,enable-time = <200>;
+			qcom,init-voltage = <1225000>;
 			status = "okay";
 		};
+	};
 
-		pm8019_l2: regulator@4100 {
+	rpm-regulator-ldoa2 {
+		status = "okay";
+		pm8019_l2: regulator-l2 {
 			parent-supply = <&pm8019_s4>;
 			regulator-min-microvolt = <1800000>;
 			regulator-max-microvolt = <1800000>;
-			qcom,enable-time = <200>;
+			qcom,init-voltage = <1800000>;
 			status = "okay";
 		};
+	};
 
-		pm8019_l3: regulator@4200 {
+	rpm-regulator-ldoa3 {
+		status = "okay";
+		pm8019_l3: regulator-l3 {
 			parent-supply = <&pm8019_s4>;
 			regulator-min-microvolt = <1800000>;
 			regulator-max-microvolt = <1800000>;
-			qcom,enable-time = <200>;
+			qcom,init-voltage = <1800000>;
 			status = "okay";
 		};
+	};
 
-		pm8019_l4: regulator@4300 {
+	rpm-regulator-ldoa4 {
+		status = "okay";
+		pm8019_l4: regulator-l4 {
 			regulator-min-microvolt = <3075000>;
 			regulator-max-microvolt = <3075000>;
-			qcom,enable-time = <200>;
+			qcom,init-voltage = <3075000>;
 			status = "okay";
 		};
+	};
 
-		pm8019_l5: regulator@4400 {
+	rpm-regulator-ldoa5 {
+		status = "okay";
+		pm8019_l5: regulator-l5 {
 			regulator-min-microvolt = <1800000>;
 			regulator-max-microvolt = <2850000>;
-			qcom,enable-time = <200>;
+			qcom,init-voltage = <1800000>;
 			status = "okay";
 		};
+	};
 
-		pm8019_l6: regulator@4500 {
+	rpm-regulator-ldoa6 {
+		status = "okay";
+		pm8019_l6: regulator-l6 {
 			regulator-min-microvolt = <1800000>;
 			regulator-max-microvolt = <2850000>;
-			qcom,enable-time = <200>;
+			qcom,init-voltage = <1800000>;
 			status = "okay";
 		};
+	};
 
-		pm8019_l7: regulator@4600 {
+	rpm-regulator-ldoa7 {
+		status = "okay";
+		pm8019_l7: regulator-l7 {
 			parent-supply = <&pm8019_s4>;
 			regulator-min-microvolt = <1800000>;
 			regulator-max-microvolt = <1800000>;
-			qcom,enable-time = <200>;
+			qcom,init-voltage = <1800000>;
 			status = "okay";
 		};
+	};
 
-		pm8019_l8: regulator@4700 {
+	rpm-regulator-ldoa8 {
+		status = "okay";
+		pm8019_l8: regulator-l8 {
 			parent-supply = <&pm8019_s4>;
 			regulator-min-microvolt = <2050000>;
 			regulator-max-microvolt = <2050000>;
-			qcom,enable-time = <200>;
+			qcom,init-voltage = <2050000>;
 			status = "okay";
 		};
+	};
 
-		pm8019_l9: regulator@4800 {
+	rpm-regulator-ldoa9 {
+		status = "okay";
+		pm8019_l9: regulator-l9 {
 			parent-supply = <&pm8019_s2>;
 			regulator-min-microvolt = <1200000>;
 			regulator-max-microvolt = <1200000>;
+			qcom,init-voltage = <1200000>;
+			qcom,init-current = <10>;
 			qcom,system-load = <10000>;
-			qcom,enable-time = <200>;
 			regulator-always-on;
 			status = "okay";
 		};
+	};
 
-		pm8019_l10: regulator@4900 {
+	rpm-regulator-ldoa10 {
+		status = "okay";
+		pm8019_l10: regulator-l10 {
 			parent-supply = <&pm8019_s3>;
-			regulator-min-microvolt = <1050000>;
+			regulator-min-microvolt = <500000>;
 			regulator-max-microvolt = <1050000>;
-			qcom,system-load = <10000>;
-			qcom,enable-time = <200>;
-			regulator-always-on;
 			status = "okay";
 		};
+		pm8019_l10_corner: regulator-l10-corner {
+			compatible = "qcom,rpm-regulator-smd";
+			regulator-name = "8019_l10_corner";
+			qcom,set = <3>;
+			regulator-min-microvolt = <1>;
+			regulator-max-microvolt = <7>;
+			qcom,use-voltage-corner;
+			status = "okay";
+			qcom,consumer-supplies = "vdd_dig", "";
+		};
+		pm8019_l10_corner_ao: regulator-l10-corner-ao {
+			compatible = "qcom,rpm-regulator-smd";
+			regulator-name = "8019_l10_corner_ao";
+			qcom,set = <1>;
+			regulator-min-microvolt = <1>;
+			regulator-max-microvolt = <7>;
+			qcom,use-voltage-corner;
+			status = "okay";
+		};
+	};
 
-		pm8019_l11: regulator@4a00 {
+	rpm-regulator-ldoa11 {
+		status = "okay";
+		pm8019_l11: regulator-l11 {
 			parent-supply = <&pm8019_s4>;
 			regulator-min-microvolt = <1800000>;
 			regulator-max-microvolt = <1800000>;
+			qcom,init-voltage = <1800000>;
+			qcom,init-current = <10>;
 			qcom,system-load = <10000>;
-			qcom,enable-time = <200>;
 			regulator-always-on;
 			status = "okay";
 		};
+	};
 
-		pm8019_l12: regulator@4b00 {
+	rpm-regulator-ldoa12 {
+		status = "okay";
+		pm8019_l12: regulator-l12 {
 			parent-supply = <&pm8019_s3>;
-			regulator-min-microvolt = <1050000>;
+			regulator-min-microvolt = <750000>;
 			regulator-max-microvolt = <1050000>;
-			qcom,system-load = <10000>;
-			qcom,enable-time = <200>;
-			regulator-always-on;
 			status = "okay";
 		};
+		pm8019_l12_ao: regulator-l12-ao {
+			compatible = "qcom,rpm-regulator-smd";
+			regulator-name = "8019_l12_ao";
+			qcom,set = <1>;
+			parent-supply = <&pm8019_s3_ao>;
+			regulator-min-microvolt = <750000>;
+			regulator-max-microvolt = <1050000>;
+			status = "okay";
+		};
+	};
 
-		pm8019_l13: regulator@4c00 {
+	rpm-regulator-ldoa13 {
+		status = "okay";
+		pm8019_l13: regulator-l13 {
 			regulator-min-microvolt = <1800000>;
 			regulator-max-microvolt = <2950000>;
-			qcom,enable-time = <200>;
+			qcom,init-voltage = <2950000>;
 			status = "okay";
 		};
+	};
 
-		pm8019_l14: regulator@4d00 {
+	rpm-regulator-ldoa14 {
+		status = "okay";
+		pm8019_l14: regulator-l14 {
 			regulator-min-microvolt = <2700000>;
 			regulator-max-microvolt = <2700000>;
-			qcom,enable-time = <200>;
+			qcom,init-voltage = <2700000>;
 			status = "okay";
 		};
 	};
 };
+
+/ {
+	ext_2p95v: regulator-isl80101 {
+		compatible = "regulator-fixed";
+		regulator-name = "ext_2p95v";
+		gpio = <&pm8019_gpios 4 0>;
+		enable-active-high;
+	};
+};
diff --git a/arch/arm/boot/dts/msm9625.dtsi b/arch/arm/boot/dts/msm9625.dtsi
index f50d14f..7b01020 100644
--- a/arch/arm/boot/dts/msm9625.dtsi
+++ b/arch/arm/boot/dts/msm9625.dtsi
@@ -11,6 +11,8 @@
  */
 
 /include/ "skeleton.dtsi"
+/include/ "msm9625-ion.dtsi"
+/include/ "msm9625-pm.dtsi"
 
 / {
 	model = "Qualcomm MSM 9625";
@@ -28,8 +30,6 @@
 	l2: cache-controller@f9040000 {
 		compatible = "arm,pl310-cache";
 		reg = <0xf9040000 0x1000>;
-		arm,data-latency = <1 1 1>;
-		arm,tag-latency = <1 1 1>;
 		cache-unified;
 		cache-level = <2>;
 	};
@@ -159,8 +159,164 @@
 					 <0x0c50000c>, /* GPIO6 */
 					 <0x0080000d>; /* PON */
 	};
+
+	i2c@f9925000 {
+		cell-index = <3>;
+		compatible = "qcom,i2c-qup";
+		reg = <0xf9925000 0x1000>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+		reg-names = "qup_phys_addr";
+		interrupts = <0 97 0>;
+		interrupt-names = "qup_err_intr";
+		qcom,i2c-bus-freq = <100000>;
+		qcom,i2c-src-freq = <24000000>;
+	};
+
+	sdcc2: qcom,sdcc@f98a4000 {
+		cell-index = <2>; /* SDC2 SD card slot */
+		compatible = "qcom,msm-sdcc";
+		reg = <0xf98a4000 0x800>,
+		      <0xf98a4800 0x100>,
+		      <0xf9884000 0x7000>;
+		reg-names = "core_mem", "dml_mem", "bam_mem";
+
+		vdd-supply = <&ext_2p95v>;
+
+		vdd-io-supply = <&pm8019_l13>;
+		qcom,sdcc-vdd-io-always_on;
+		qcom,sdcc-vdd-io-lpm_sup;
+		qcom,sdcc-vdd-io-voltage_level = <1800000 2950000>;
+		qcom,sdcc-vdd-io-current_level = <6 22000>;
+
+		qcom,sdcc-pad-pull-on = <0x0 0x3 0x3>;
+		qcom,sdcc-pad-pull-off = <0x0 0x3 0x3>;
+		qcom,sdcc-pad-drv-on = <0x7 0x4 0x4>;
+		qcom,sdcc-pad-drv-off = <0x0 0x0 0x0>;
+
+		qcom,sdcc-clk-rates = <400000 25000000 50000000 100000000 200000000>;
+		qcom,sdcc-sup-voltages = <2950 2950>;
+		qcom,sdcc-bus-width = <4>;
+		qcom,sdcc-xpc;
+		qcom,sdcc-bus-speed-mode = "SDR12", "SDR25", "SDR50", "DDR50", "SDR104";
+		qcom,sdcc-current-limit = <800>;
+
+		interrupt-parent = <&sdcc2>;
+		#address-cells = <0>;
+		interrupts = <0 1 2>;
+		#interrupt-cells = <1>;
+		interrupt-map-mask = <0xffffffff>;
+		interrupt-map = <0 &intc 0 125 0
+				 1 &intc 0 220 0
+				 2 &msmgpio 66 0x3>;
+		interrupt-names = "core_irq", "bam_irq", "status_irq";
+		cd-gpios = <&msmgpio 66 0>;
+	};
+
+	sdcc3: qcom,sdcc@f9864000 {
+		cell-index = <3>; /* SDC3 SDIO slot */
+		compatible = "qcom,msm-sdcc";
+		reg = <0xf9864000 0x800>,
+		      <0xf9864800 0x100>,
+		      <0xf9844000 0x7000>;
+		reg-names = "core_mem", "dml_mem", "bam_mem";
+		interrupts = <0 127 0>, <0 223 0>;
+		interrupt-names = "core_irq", "bam_irq";
+
+		gpios = <&msmgpio 25 0>,
+			<&msmgpio 24 0>,
+			<&msmgpio 16 0>,
+			<&msmgpio 17 0>,
+			<&msmgpio 18 0>,
+			<&msmgpio 19 0>;
+		qcom,sdcc-gpio-names = "CLK", "CMD", "DAT0", "DAT1", "DAT2", "DAT3";
+
+		qcom,sdcc-clk-rates = <400000 25000000 50000000 100000000>;
+		qcom,sdcc-sup-voltages = <2950 2950>;
+		qcom,sdcc-bus-width = <4>;
+		qcom,sdcc-bus-speed-mode = "SDR12", "SDR25", "SDR50", "DDR50";
+	};
+
+	qcom,bam_dmux@fc834000 {
+		compatible = "qcom,bam_dmux";
+		reg = <0xfc834000 0x7000>;
+		interrupts = <0 29 1>;
+		qcom,satellite-mode;
+	};
+
+	qcom,acpuclk@f9010000 {
+		compatible = "qcom,acpuclk-9625";
+		reg = <0xf9010008 0x10>,
+		      <0xf9008004 0x4>;
+		reg-names = "rcg_base", "pwr_base";
+		a5_cpu-supply = <&pm8019_l10_corner_ao>;
+		a5_mem-supply = <&pm8019_l12_ao>;
+	};
+
+	gdsc_usb_hsic: qcom,gdsc@fc400404 {
+		compatible = "qcom,gdsc";
+		reg = <0xfc400404 0x4>;
+		regulator-name = "gdsc_usb_hsic";
+	};
 };
 
 /include/ "msm-pm8019-rpm-regulator.dtsi"
 /include/ "msm-pm8019.dtsi"
 /include/ "msm9625-regulator.dtsi"
+
+&pm8019_vadc {
+	chan@49 {
+		label = "batt_id_therm";
+		qcom,channel-num = <49>;
+		qcom,decimation = <0>;
+		qcom,pre-div-channel-scaling = <0>;
+		qcom,calibration-type = "ratiometric";
+		qcom,scale-function = <0>;
+		qcom,hw-settle-time = <0>;
+		qcom,fast-avg-setup = <0>;
+	};
+
+	chan@51 {
+		label = "pa_therm1";
+		qcom,channel-num = <51>;
+		qcom,decimation = <0>;
+		qcom,pre-div-channel-scaling = <0>;
+		qcom,calibration-type = "ratiometric";
+		qcom,scale-function = <2>;
+		qcom,hw-settle-time = <0>;
+		qcom,fast-avg-setup = <0>;
+	};
+
+	chan@52 {
+		label = "pa_therm2";
+		qcom,channel-num = <52>;
+		qcom,decimation = <0>;
+		qcom,pre-div-channel-scaling = <0>;
+		qcom,calibration-type = "ratiometric";
+		qcom,scale-function = <2>;
+		qcom,hw-settle-time = <0>;
+		qcom,fast-avg-setup = <0>;
+	};
+
+	chan@50 {
+		label = "xo_therm";
+		qcom,channel-num = <50>;
+		qcom,decimation = <0>;
+		qcom,pre-div-channel-scaling = <0>;
+		qcom,calibration-type = "ratiometric";
+		qcom,scale-function = <4>;
+		qcom,hw-settle-time = <0>;
+		qcom,fast-avg-setup = <0>;
+	};
+
+	chan@60 {
+		label = "xo_therm_amux";
+		qcom,channel-num = <60>;
+		qcom,decimation = <0>;
+		qcom,pre-div-channel-scaling = <0>;
+		qcom,calibration-type = "ratiometric";
+		qcom,scale-function = <4>;
+		qcom,hw-settle-time = <0>;
+		qcom,fast-avg-setup = <0>;
+	};
+};
diff --git a/arch/arm/common/gic.c b/arch/arm/common/gic.c
index 9dd4347..b4574aa 100644
--- a/arch/arm/common/gic.c
+++ b/arch/arm/common/gic.c
@@ -67,6 +67,7 @@
 	u32 __percpu *saved_ppi_enable;
 	u32 __percpu *saved_ppi_conf;
 #endif
+	u32 saved_dist_isr[DIV_ROUND_UP(1020, 32)];
 	struct irq_domain *domain;
 	unsigned int gic_irqs;
 #ifdef CONFIG_GIC_NON_BANKED
@@ -640,6 +641,11 @@
 	for (i = 0; i < DIV_ROUND_UP(gic_irqs, 32); i++)
 		gic_data[gic_nr].saved_spi_enable[i] =
 			readl_relaxed(dist_base + GIC_DIST_ENABLE_SET + i * 4);
+	if (is_cpu_secure()) {
+		for (i = 0; i < DIV_ROUND_UP(gic_irqs, 32); i++)
+			gic_data[gic_nr].saved_dist_isr[i] =
+				readl_relaxed(dist_base + GIC_DIST_ISR + i * 4);
+	}
 }
 
 /*
@@ -682,6 +688,12 @@
 		writel_relaxed(gic_data[gic_nr].saved_spi_enable[i],
 			dist_base + GIC_DIST_ENABLE_SET + i * 4);
 
+	if (is_cpu_secure()) {
+		for (i = 0; i < DIV_ROUND_UP(gic_irqs, 32); i++)
+			writel_relaxed(gic_data[gic_nr].saved_dist_isr[i],
+					dist_base + GIC_DIST_ISR + i * 4);
+	}
+
 	writel_relaxed(saved_dist_ctrl, dist_base + GIC_DIST_CTRL);
 }
 
@@ -1167,41 +1179,35 @@
 /*
  * Configure the GIC after we come out of power collapse.
  * This function will configure some of the GIC registers so as to prepare the
- * core1 to receive an SPI(ACSR_MP_CORE_IPC1, (32 + 8)), which will bring
- * core1 out of GDFS.
+ * secondary cores to receive an SPI(ACSR_MP_CORE_IPC1/IPC2/IPC3, 40/92/93),
+ * which will bring cores out of GDFS.
  */
-void core1_gic_configure_and_raise(void)
+void gic_configure_and_raise(unsigned int irq, unsigned int cpu)
 {
 	struct gic_chip_data *gic = &gic_data[0];
+	struct irq_data *d = irq_get_irq_data(irq);
 	void __iomem *base = gic_data_dist_base(gic);
-	unsigned int value = 0;
+	unsigned int value = 0, byte_offset, offset, bit;
 	unsigned long flags;
 
+	offset = ((gic_irq(d) / 32) * 4);
+	bit = BIT(gic_irq(d) % 32);
+
 	raw_spin_lock_irqsave(&irq_controller_lock, flags);
 
-	value = __raw_readl(base + GIC_DIST_ACTIVE_BIT + 0x4);
-	value |= BIT(8);
-	__raw_writel(value, base + GIC_DIST_ACTIVE_BIT + 0x4);
+	value = __raw_readl(base + GIC_DIST_ACTIVE_BIT + offset);
+	__raw_writel(value | bit, base + GIC_DIST_ACTIVE_BIT + offset);
 	mb();
 
-	value = __raw_readl(base + GIC_DIST_TARGET + 0x24);
-	value |= BIT(13);
-	__raw_writel(value, base + GIC_DIST_TARGET + 0x24);
+	value = __raw_readl(base + GIC_DIST_TARGET + (gic_irq(d) / 4) * 4);
+	byte_offset = (gic_irq(d) % 4) * 8;
+	value |= 1 << (cpu + byte_offset);
+	__raw_writel(value, base + GIC_DIST_TARGET + (gic_irq(d) / 4) * 4);
 	mb();
 
-	value = __raw_readl(base + GIC_DIST_TARGET + 0x28);
-	value |= BIT(1);
-	__raw_writel(value, base + GIC_DIST_TARGET + 0x28);
+	value =  __raw_readl(base + GIC_DIST_ENABLE_SET + offset);
+	__raw_writel(value | bit, base + GIC_DIST_ENABLE_SET + offset);
 	mb();
 
-	value =  __raw_readl(base + GIC_DIST_ENABLE_SET + 0x4);
-	value |= BIT(8);
-	__raw_writel(value, base + GIC_DIST_ENABLE_SET + 0x4);
-	mb();
-
-	value =  __raw_readl(base + GIC_DIST_PENDING_SET + 0x4);
-	value |= BIT(8);
-	__raw_writel(value, base + GIC_DIST_PENDING_SET + 0x4);
-	mb();
 	raw_spin_unlock_irqrestore(&irq_controller_lock, flags);
 }
diff --git a/arch/arm/configs/fsm9xxx-perf_defconfig b/arch/arm/configs/fsm9xxx-perf_defconfig
index 93e84e9..1dc853b 100644
--- a/arch/arm/configs/fsm9xxx-perf_defconfig
+++ b/arch/arm/configs/fsm9xxx-perf_defconfig
@@ -11,7 +11,6 @@
 CONFIG_PANIC_TIMEOUT=5
 CONFIG_ASHMEM=y
 CONFIG_EMBEDDED=y
-# CONFIG_PERF_EVENTS is not set
 CONFIG_SLAB=y
 CONFIG_PROFILING=y
 CONFIG_OPROFILE=m
@@ -84,6 +83,7 @@
 CONFIG_IPV6_MROUTE=y
 CONFIG_IPV6_PIMSM_V2=y
 # CONFIG_NET_ACTIVITY_STATS is not set
+CONFIG_IP_SCTP=y
 CONFIG_RFKILL=y
 CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
 CONFIG_MTD=y
diff --git a/arch/arm/configs/fsm9xxx_defconfig b/arch/arm/configs/fsm9xxx_defconfig
index c45063f..203d3b7 100644
--- a/arch/arm/configs/fsm9xxx_defconfig
+++ b/arch/arm/configs/fsm9xxx_defconfig
@@ -12,7 +12,6 @@
 CONFIG_KALLSYMS_ALL=y
 CONFIG_ASHMEM=y
 CONFIG_EMBEDDED=y
-# CONFIG_PERF_EVENTS is not set
 CONFIG_SLAB=y
 CONFIG_PROFILING=y
 CONFIG_OPROFILE=m
@@ -83,6 +82,7 @@
 CONFIG_IPV6_MROUTE=y
 CONFIG_IPV6_PIMSM_V2=y
 # CONFIG_NET_ACTIVITY_STATS is not set
+CONFIG_IP_SCTP=y
 CONFIG_RFKILL=y
 CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
 CONFIG_MTD=y
diff --git a/arch/arm/configs/msm7627a-perf_defconfig b/arch/arm/configs/msm7627a-perf_defconfig
index 9a0bfba..76650e0 100644
--- a/arch/arm/configs/msm7627a-perf_defconfig
+++ b/arch/arm/configs/msm7627a-perf_defconfig
@@ -58,6 +58,7 @@
 CONFIG_MSM_RPC_PMIC=y
 CONFIG_MSM_RPC_USB=y
 CONFIG_MSM_RPC_PMAPP=y
+CONFIG_MSM_FIQ=y
 CONFIG_ARM_THUMBEE=y
 CONFIG_NO_HZ=y
 CONFIG_HIGH_RES_TIMERS=y
@@ -167,10 +168,10 @@
 CONFIG_IP_NF_ARPTABLES=y
 CONFIG_IP_NF_ARPFILTER=y
 CONFIG_IP_NF_ARP_MANGLE=y
-CONFIG_IP6_NF_MANGLE=y
-CONFIG_IP6_NF_RAW=y
 CONFIG_IP6_NF_IPTABLES=y
 CONFIG_IP6_NF_FILTER=y
+CONFIG_IP6_NF_MANGLE=y
+CONFIG_IP6_NF_RAW=y
 CONFIG_ATM=y
 CONFIG_L2TP=y
 CONFIG_L2TP_DEBUGFS=y
@@ -252,6 +253,7 @@
 # CONFIG_HW_RANDOM is not set
 CONFIG_I2C=y
 CONFIG_I2C_CHARDEV=y
+CONFIG_I2C_GPIO=y
 # CONFIG_I2C_MSM is not set
 CONFIG_I2C_QUP=y
 CONFIG_DEBUG_GPIO=y
@@ -261,6 +263,7 @@
 CONFIG_BATTERY_MSM=y
 CONFIG_SENSORS_MSM_ADC=y
 CONFIG_MARIMBA_CORE=y
+CONFIG_REGULATOR_ONSEMI_NCP6335D=y
 CONFIG_REGULATOR_MSM_GPIO=y
 CONFIG_MEDIA_SUPPORT=y
 CONFIG_MEDIA_CONTROLLER=y
@@ -378,4 +381,3 @@
 CONFIG_DYNAMIC_DEBUG=y
 CONFIG_DEBUG_USER=y
 CONFIG_CRYPTO_TWOFISH=y
-CONFIG_CRC_CCITT=y
diff --git a/arch/arm/configs/msm7627a_defconfig b/arch/arm/configs/msm7627a_defconfig
index 60a2d72..8ab57de 100644
--- a/arch/arm/configs/msm7627a_defconfig
+++ b/arch/arm/configs/msm7627a_defconfig
@@ -60,6 +60,7 @@
 CONFIG_MSM_RPC_PMIC=y
 CONFIG_MSM_RPC_USB=y
 CONFIG_MSM_RPC_PMAPP=y
+CONFIG_MSM_FIQ=y
 CONFIG_ARM_THUMBEE=y
 CONFIG_NO_HZ=y
 CONFIG_HIGH_RES_TIMERS=y
@@ -169,10 +170,10 @@
 CONFIG_IP_NF_ARPTABLES=y
 CONFIG_IP_NF_ARPFILTER=y
 CONFIG_IP_NF_ARP_MANGLE=y
-CONFIG_IP6_NF_MANGLE=y
-CONFIG_IP6_NF_RAW=y
 CONFIG_IP6_NF_IPTABLES=y
 CONFIG_IP6_NF_FILTER=y
+CONFIG_IP6_NF_MANGLE=y
+CONFIG_IP6_NF_RAW=y
 CONFIG_ATM=y
 CONFIG_L2TP=y
 CONFIG_L2TP_DEBUGFS=y
@@ -254,6 +255,7 @@
 # CONFIG_HW_RANDOM is not set
 CONFIG_I2C=y
 CONFIG_I2C_CHARDEV=y
+CONFIG_I2C_GPIO=y
 # CONFIG_I2C_MSM is not set
 CONFIG_I2C_QUP=y
 CONFIG_DEBUG_GPIO=y
@@ -263,6 +265,7 @@
 CONFIG_BATTERY_MSM=y
 CONFIG_SENSORS_MSM_ADC=y
 CONFIG_MARIMBA_CORE=y
+CONFIG_REGULATOR_ONSEMI_NCP6335D=y
 CONFIG_REGULATOR_MSM_GPIO=y
 CONFIG_MEDIA_SUPPORT=y
 CONFIG_MEDIA_CONTROLLER=y
diff --git a/arch/arm/configs/msm7630-perf_defconfig b/arch/arm/configs/msm7630-perf_defconfig
index 401654d..f2d25ac 100644
--- a/arch/arm/configs/msm7630-perf_defconfig
+++ b/arch/arm/configs/msm7630-perf_defconfig
@@ -280,8 +280,8 @@
 CONFIG_FB_MSM_TRIPLE_BUFFER=y
 CONFIG_FB_MSM_MDP40=y
 CONFIG_FB_MSM_OVERLAY=y
-CONFIG_FB_MSM_OVERLAY0_WRITEBACK=y
 CONFIG_FB_MSM_NO_MDP_PIPE_CTRL=y
+CONFIG_FB_MSM_OVERLAY0_WRITEBACK=y
 CONFIG_FB_MSM_TRY_MDDI_CATCH_LCDC_PRISM=y
 CONFIG_FB_MSM_HDMI_ADV7520_PANEL=y
 CONFIG_BACKLIGHT_LCD_SUPPORT=y
diff --git a/arch/arm/configs/msm8660-perf_defconfig b/arch/arm/configs/msm8660-perf_defconfig
index 957dbcf..2ee3f3b 100644
--- a/arch/arm/configs/msm8660-perf_defconfig
+++ b/arch/arm/configs/msm8660-perf_defconfig
@@ -35,8 +35,6 @@
 CONFIG_DEFAULT_DEADLINE=y
 CONFIG_ARCH_MSM=y
 CONFIG_ARCH_MSM8X60=y
-CONFIG_MACH_MSM8X60_RUMI3=y
-CONFIG_MACH_MSM8X60_SIM=y
 CONFIG_MACH_MSM8X60_SURF=y
 CONFIG_MACH_MSM8X60_FFA=y
 CONFIG_MACH_MSM8X60_FLUID=y
@@ -64,13 +62,13 @@
 CONFIG_MSM_RMT_STORAGE_CLIENT=y
 CONFIG_MSM_SDIO_SMEM=y
 # CONFIG_MSM_HW3D is not set
+CONFIG_MSM_SUBSYSTEM_RESTART=y
+CONFIG_MSM_SYSMON_COMM=y
 CONFIG_MSM_PIL_MODEM=y
 CONFIG_MSM_PIL_QDSP6V3=y
 CONFIG_MSM_PIL_TZAPPS=y
 CONFIG_MSM_PIL_DSPS=y
 CONFIG_MSM_PIL_VIDC=y
-CONFIG_MSM_SUBSYSTEM_RESTART=y
-CONFIG_MSM_SYSMON_COMM=y
 CONFIG_MSM_TZ_LOG=y
 CONFIG_MSM_RPM_LOG=y
 CONFIG_MSM_RPM_STATS_LOG=y
diff --git a/arch/arm/configs/msm8660_defconfig b/arch/arm/configs/msm8660_defconfig
index 4e5479a..25c5207 100644
--- a/arch/arm/configs/msm8660_defconfig
+++ b/arch/arm/configs/msm8660_defconfig
@@ -34,8 +34,6 @@
 CONFIG_DEFAULT_DEADLINE=y
 CONFIG_ARCH_MSM=y
 CONFIG_ARCH_MSM8X60=y
-CONFIG_MACH_MSM8X60_RUMI3=y
-CONFIG_MACH_MSM8X60_SIM=y
 CONFIG_MACH_MSM8X60_SURF=y
 CONFIG_MACH_MSM8X60_FFA=y
 CONFIG_MACH_MSM8X60_FLUID=y
@@ -63,20 +61,19 @@
 CONFIG_MSM_RMT_STORAGE_CLIENT=y
 CONFIG_MSM_SDIO_SMEM=y
 # CONFIG_MSM_HW3D is not set
+CONFIG_MSM_SUBSYSTEM_RESTART=y
+CONFIG_MSM_SYSMON_COMM=y
 CONFIG_MSM_PIL_MODEM=y
 CONFIG_MSM_PIL_QDSP6V3=y
 CONFIG_MSM_PIL_TZAPPS=y
 CONFIG_MSM_PIL_DSPS=y
 CONFIG_MSM_PIL_VIDC=y
-CONFIG_MSM_SUBSYSTEM_RESTART=y
-CONFIG_MSM_SYSMON_COMM=y
 CONFIG_MSM_TZ_LOG=y
 CONFIG_MSM_RPM_LOG=y
 CONFIG_MSM_RPM_STATS_LOG=y
 CONFIG_MSM_WATCHDOG=y
 CONFIG_MSM_DLOAD_MODE=y
 CONFIG_MSM_ETM=y
-CONFIG_MSM_SLEEP_STATS=y
 CONFIG_MSM_GSBI9_UART=y
 CONFIG_STRICT_MEMORY_RWX=y
 CONFIG_NO_HZ=y
diff --git a/arch/arm/configs/msm8910_defconfig b/arch/arm/configs/msm8910_defconfig
new file mode 100644
index 0000000..7e4e7705
--- /dev/null
+++ b/arch/arm/configs/msm8910_defconfig
@@ -0,0 +1,170 @@
+# CONFIG_ARM_PATCH_PHYS_VIRT is not set
+CONFIG_EXPERIMENTAL=y
+# CONFIG_LOCALVERSION_AUTO is not set
+CONFIG_SYSVIPC=y
+CONFIG_IKCONFIG=y
+CONFIG_IKCONFIG_PROC=y
+CONFIG_CGROUPS=y
+CONFIG_CGROUP_DEBUG=y
+CONFIG_CGROUP_FREEZER=y
+CONFIG_CGROUP_CPUACCT=y
+CONFIG_RESOURCE_COUNTERS=y
+CONFIG_CGROUP_SCHED=y
+CONFIG_RT_GROUP_SCHED=y
+CONFIG_NAMESPACES=y
+# CONFIG_UTS_NS is not set
+# CONFIG_IPC_NS is not set
+# CONFIG_USER_NS is not set
+# CONFIG_PID_NS is not set
+CONFIG_RELAY=y
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_RD_BZIP2=y
+CONFIG_RD_LZMA=y
+CONFIG_PANIC_TIMEOUT=5
+CONFIG_KALLSYMS_ALL=y
+CONFIG_ASHMEM=y
+CONFIG_EMBEDDED=y
+CONFIG_PROFILING=y
+CONFIG_OPROFILE=m
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+CONFIG_MODULE_FORCE_UNLOAD=y
+CONFIG_MODVERSIONS=y
+CONFIG_PARTITION_ADVANCED=y
+CONFIG_EFI_PARTITION=y
+CONFIG_ARCH_MSM=y
+CONFIG_ARCH_MSM8910=y
+# CONFIG_MSM_STACKED_MEMORY is not set
+CONFIG_CPU_HAS_L2_PMU=y
+# CONFIG_MSM_FIQ_SUPPORT is not set
+# CONFIG_MSM_PROC_COMM is not set
+CONFIG_MSM_SMD=y
+CONFIG_MSM_DIRECT_SCLK_ACCESS=y
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_SMP=y
+# CONFIG_SMP_ON_UP is not set
+CONFIG_ARM_ARCH_TIMER=y
+CONFIG_HOTPLUG_CPU=y
+CONFIG_PREEMPT=y
+CONFIG_AEABI=y
+CONFIG_HIGHMEM=y
+CONFIG_VMALLOC_RESERVE=0x19000000
+CONFIG_USE_OF=y
+CONFIG_VFP=y
+CONFIG_NEON=y
+# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
+# CONFIG_SUSPEND is not set
+CONFIG_NET=y
+CONFIG_PACKET=y
+CONFIG_UNIX=y
+CONFIG_INET=y
+CONFIG_IP_ADVANCED_ROUTER=y
+CONFIG_IP_MULTIPLE_TABLES=y
+CONFIG_IP_ROUTE_VERBOSE=y
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+# CONFIG_INET_XFRM_MODE_TRANSPORT is not set
+# CONFIG_INET_XFRM_MODE_TUNNEL is not set
+# CONFIG_INET_XFRM_MODE_BEET is not set
+# CONFIG_INET_LRO is not set
+CONFIG_IPV6=y
+CONFIG_IPV6_PRIVACY=y
+CONFIG_IPV6_ROUTER_PREF=y
+CONFIG_IPV6_ROUTE_INFO=y
+CONFIG_IPV6_OPTIMISTIC_DAD=y
+CONFIG_INET6_AH=y
+CONFIG_INET6_ESP=y
+CONFIG_INET6_IPCOMP=y
+CONFIG_IPV6_MIP6=y
+CONFIG_IPV6_MULTIPLE_TABLES=y
+CONFIG_IPV6_SUBTREES=y
+CONFIG_NETFILTER=y
+CONFIG_BLK_DEV_LOOP=y
+CONFIG_BLK_DEV_RAM=y
+CONFIG_MD=y
+CONFIG_BLK_DEV_DM=y
+CONFIG_DM_CRYPT=y
+CONFIG_NETDEVICES=y
+CONFIG_DUMMY=y
+CONFIG_INPUT_EVDEV=y
+CONFIG_INPUT_EVBUG=m
+CONFIG_INPUT_MISC=y
+CONFIG_INPUT_UINPUT=y
+CONFIG_INPUT_GPIO=m
+CONFIG_SERIO_LIBPS2=y
+CONFIG_SERIAL_MSM_HSL=y
+CONFIG_SERIAL_MSM_HSL_CONSOLE=y
+CONFIG_DIAG_CHAR=y
+CONFIG_HW_RANDOM=y
+CONFIG_DEBUG_GPIO=y
+CONFIG_GPIO_SYSFS=y
+# CONFIG_HWMON is not set
+CONFIG_REGULATOR=y
+CONFIG_REGULATOR_STUB=y
+CONFIG_FB=y
+CONFIG_FB_VIRTUAL=y
+# CONFIG_MSM_HW3D is not set
+CONFIG_SOUND=y
+CONFIG_SND=y
+CONFIG_SND_SOC=y
+CONFIG_USB_GADGET=y
+CONFIG_USB_GADGET_DEBUG_FILES=y
+CONFIG_USB_GADGET_DEBUG_FS=y
+CONFIG_USB_CI13XXX_MSM=y
+CONFIG_USB_G_ANDROID=y
+CONFIG_MMC=y
+CONFIG_MMC_PERF_PROFILING=y
+CONFIG_MMC_UNSAFE_RESUME=y
+CONFIG_MMC_CLKGATE=y
+CONFIG_MMC_EMBEDDED_SDIO=y
+CONFIG_MMC_PARANOID_SD_INIT=y
+CONFIG_MMC_BLOCK_MINORS=32
+CONFIG_MMC_TEST=m
+CONFIG_MMC_MSM=y
+CONFIG_MMC_MSM_SPS_SUPPORT=y
+CONFIG_STAGING=y
+CONFIG_ANDROID=y
+CONFIG_ANDROID_BINDER_IPC=y
+CONFIG_ANDROID_LOGGER=y
+CONFIG_ANDROID_RAM_CONSOLE=y
+CONFIG_ANDROID_TIMED_GPIO=y
+CONFIG_ANDROID_LOW_MEMORY_KILLER=y
+CONFIG_EXT2_FS=y
+CONFIG_EXT2_FS_XATTR=y
+CONFIG_EXT3_FS=y
+# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set
+CONFIG_EXT4_FS=y
+CONFIG_FUSE_FS=y
+CONFIG_VFAT_FS=y
+CONFIG_TMPFS=y
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_ASCII=y
+CONFIG_NLS_ISO8859_1=y
+CONFIG_PRINTK_TIME=y
+CONFIG_MAGIC_SYSRQ=y
+CONFIG_DEBUG_FS=y
+# CONFIG_SCHED_DEBUG is not set
+CONFIG_TIMER_STATS=y
+# CONFIG_DEBUG_PREEMPT is not set
+CONFIG_DEBUG_INFO=y
+CONFIG_DEBUG_MEMORY_INIT=y
+CONFIG_DYNAMIC_DEBUG=y
+CONFIG_DEBUG_USER=y
+CONFIG_KEYS=y
+CONFIG_CRYPTO_AUTHENC=y
+CONFIG_CRYPTO_CBC=y
+CONFIG_CRYPTO_HMAC=y
+CONFIG_CRYPTO_MD4=y
+CONFIG_CRYPTO_MD5=y
+CONFIG_CRYPTO_SHA1=y
+CONFIG_CRYPTO_SHA256=y
+CONFIG_CRYPTO_AES=y
+CONFIG_CRYPTO_ARC4=y
+CONFIG_CRYPTO_DES=y
+CONFIG_CRYPTO_TWOFISH=y
+CONFIG_CRYPTO_DEFLATE=y
+# CONFIG_CRYPTO_HW is not set
+CONFIG_CRC_CCITT=y
+CONFIG_CRC16=y
+CONFIG_LIBCRC32C=y
diff --git a/arch/arm/configs/msm8960-perf_defconfig b/arch/arm/configs/msm8960-perf_defconfig
index 45d52e4..ec00b68 100644
--- a/arch/arm/configs/msm8960-perf_defconfig
+++ b/arch/arm/configs/msm8960-perf_defconfig
@@ -39,8 +39,6 @@
 CONFIG_ARCH_MSM8930=y
 CONFIG_ARCH_APQ8064=y
 CONFIG_MSM_KRAIT_TBB_ABORT_HANDLER=y
-CONFIG_MACH_MSM8960_SIM=y
-CONFIG_MACH_MSM8960_RUMI3=y
 CONFIG_MACH_MSM8960_CDP=y
 CONFIG_MACH_MSM8960_MTP=y
 CONFIG_MACH_MSM8960_FLUID=y
@@ -50,8 +48,6 @@
 CONFIG_MACH_MSM8930_FLUID=y
 CONFIG_MACH_MSM8627_CDP=y
 CONFIG_MACH_MSM8627_MTP=y
-CONFIG_MACH_APQ8064_SIM=y
-CONFIG_MACH_APQ8064_RUMI3=y
 CONFIG_MACH_APQ8064_CDP=y
 CONFIG_MACH_APQ8064_MTP=y
 CONFIG_MACH_APQ8064_LIQUID=y
@@ -66,38 +62,35 @@
 CONFIG_MSM_SMD_PKG4=y
 CONFIG_MSM_PCIE=y
 CONFIG_MSM_BAM_DMUX=y
+CONFIG_MSM_IPC_LOGGING=y
 CONFIG_MSM_DSPS=y
 CONFIG_MSM_IPC_ROUTER=y
 CONFIG_MSM_IPC_ROUTER_SMD_XPRT=y
 CONFIG_MSM_AVS_HW=y
 # CONFIG_MSM_HW3D is not set
-CONFIG_MSM_PIL_QDSP6V4=y
+CONFIG_MSM_SUBSYSTEM_RESTART=y
+CONFIG_MSM_SYSMON_COMM=y
+CONFIG_MSM_PIL_LPASS_QDSP6V4=y
+CONFIG_MSM_PIL_MODEM_QDSP6V4=y
 CONFIG_MSM_PIL_RIVA=y
 CONFIG_MSM_PIL_TZAPPS=y
 CONFIG_MSM_PIL_DSPS=y
 CONFIG_MSM_PIL_VIDC=y
 CONFIG_MSM_PIL_GSS=y
-CONFIG_MSM_SUBSYSTEM_RESTART=y
-CONFIG_MSM_SYSMON_COMM=y
-CONFIG_MSM_MODEM_8960=y
-CONFIG_MSM_LPASS_8960=y
-CONFIG_MSM_WCNSS_SSR_8960=y
-CONFIG_MSM_GSS_SSR_8064=y
 CONFIG_MSM_TZ_LOG=y
 CONFIG_MSM_RPM_LOG=y
-CONFIG_MSM_RPM_RBCPR_STATS_LOG=y
 CONFIG_MSM_RPM_STATS_LOG=y
+CONFIG_MSM_RPM_RBCPR_STATS_LOG=y
+CONFIG_MSM_EVENT_TIMER=y
 CONFIG_MSM_BUS_SCALING=y
 CONFIG_MSM_BUS_RPM_MULTI_TIER_ENABLED=y
 CONFIG_MSM_WATCHDOG=y
 CONFIG_MSM_DLOAD_MODE=y
-CONFIG_MSM_SLEEP_STATS=y
 CONFIG_MSM_EBI_ERP=y
 CONFIG_MSM_CACHE_ERP=y
 CONFIG_MSM_L1_ERR_PANIC=y
 CONFIG_MSM_L1_ERR_LOG=y
 CONFIG_MSM_L2_ERP_2BIT_PANIC=y
-CONFIG_MSM_EVENT_TIMER=y
 CONFIG_MSM_DCVS=y
 CONFIG_MSM_HSIC_SYSMON=y
 CONFIG_STRICT_MEMORY_RWX=y
@@ -231,7 +224,6 @@
 CONFIG_NET_EMATCH_META=y
 CONFIG_NET_EMATCH_TEXT=y
 CONFIG_NET_CLS_ACT=y
-CONFIG_MARIMBA_CORE=y
 CONFIG_BT=y
 CONFIG_BT_RFCOMM=y
 CONFIG_BT_RFCOMM_TTY=y
@@ -242,8 +234,8 @@
 CONFIG_BT_HCISMD=y
 CONFIG_BT_HCIUART=y
 CONFIG_BT_HCIUART_H4=y
-CONFIG_BT_HCIUART_IBS=y
 CONFIG_BT_HCIUART_ATH3K=y
+CONFIG_BT_HCIUART_IBS=y
 CONFIG_MSM_BT_POWER=y
 CONFIG_CFG80211=m
 # CONFIG_CFG80211_WEXT is not set
@@ -252,6 +244,7 @@
 CONFIG_GENLOCK_MISCDEVICE=y
 CONFIG_BLK_DEV_LOOP=y
 CONFIG_BLK_DEV_RAM=y
+CONFIG_TSPP=m
 CONFIG_HAPTIC_ISA1200=y
 CONFIG_PMIC8XXX_VIBRATOR=y
 CONFIG_QSEECOM=y
@@ -284,6 +277,7 @@
 CONFIG_USB_USBNET=y
 CONFIG_MSM_RMNET_USB=y
 CONFIG_WCNSS_CORE=y
+CONFIG_WCNSS_MEM_PRE_ALLOC=y
 CONFIG_INPUT_EVDEV=y
 CONFIG_INPUT_EVBUG=m
 CONFIG_KEYBOARD_GPIO=y
@@ -296,11 +290,10 @@
 CONFIG_TOUCHSCREEN_CYTTSP_I2C_QC=y
 CONFIG_INPUT_MISC=y
 CONFIG_INPUT_PMIC8XXX_PWRKEY=y
+CONFIG_INPUT_MPU3050=y
 CONFIG_INPUT_UINPUT=y
 CONFIG_STM_LIS3DH=y
-CONFIG_INPUT_MPU3050=y
 # CONFIG_LEGACY_PTYS is not set
-CONFIG_MSM_IPC_LOGGING=y
 CONFIG_N_SMUX=y
 CONFIG_N_SMUX_LOOPBACK=y
 CONFIG_SMUX_CTL=y
@@ -334,6 +327,7 @@
 CONFIG_THERMAL_TSENS8960=y
 CONFIG_THERMAL_PM8XXX=y
 CONFIG_THERMAL_MONITOR=y
+CONFIG_MARIMBA_CORE=y
 CONFIG_MFD_PM8921_CORE=y
 CONFIG_MFD_PM8821_CORE=y
 CONFIG_MFD_PM8038_CORE=y
@@ -347,7 +341,7 @@
 CONFIG_MEDIA_CONTROLLER=y
 CONFIG_VIDEO_DEV=y
 CONFIG_VIDEO_V4L2_SUBDEV_API=y
-CONFIG_MSM_WFD=y
+CONFIG_DVB_CORE=m
 CONFIG_USER_RC_INPUT=y
 CONFIG_IR_GPIO_CIR=y
 # CONFIG_MEDIA_TUNER_CUSTOMISE is not set
@@ -372,8 +366,15 @@
 CONFIG_MSM_CSI20_HEADER=y
 CONFIG_S5K3L1YX=y
 CONFIG_IMX091=y
+CONFIG_MSM_WFD=y
+CONFIG_IMX135=y
 CONFIG_RADIO_IRIS=y
 CONFIG_RADIO_IRIS_TRANSPORT=m
+# CONFIG_DVB_FE_CUSTOMISE is not set
+CONFIG_DVB_MPQ=m
+CONFIG_DVB_MPQ_DEMUX=m
+CONFIG_DVB_MPQ_VIDEO=m
+CONFIG_DVB_MPQ_TSPP1=y
 CONFIG_ION=y
 CONFIG_ION_MSM=y
 CONFIG_MSM_KGSL=y
@@ -437,7 +438,6 @@
 CONFIG_USB_GADGET_DEBUG_FILES=y
 CONFIG_USB_CI13XXX_MSM=y
 CONFIG_USB_G_ANDROID=y
-CONFIG_USB_ANDROID_RMNET_CTRL_SMD=y
 CONFIG_MMC=y
 CONFIG_MMC_PERF_PROFILING=y
 CONFIG_MMC_UNSAFE_RESUME=y
@@ -473,6 +473,7 @@
 CONFIG_MOBICORE_SUPPORT=m
 CONFIG_MOBICORE_API=m
 CONFIG_MSM_QDSS=y
+CONFIG_CONTROL_TRACE=m
 CONFIG_EXT2_FS=y
 CONFIG_EXT2_FS_XATTR=y
 CONFIG_EXT3_FS=y
@@ -505,5 +506,3 @@
 CONFIG_CRYPTO_DEV_QCE=m
 CONFIG_CRYPTO_DEV_QCEDEV=m
 CONFIG_CRC_CCITT=y
-CONFIG_WCNSS_MEM_PRE_ALLOC=y
-CONFIG_CONTROL_TRACE=m
diff --git a/arch/arm/configs/msm8960_defconfig b/arch/arm/configs/msm8960_defconfig
index 465598f..33f7987 100644
--- a/arch/arm/configs/msm8960_defconfig
+++ b/arch/arm/configs/msm8960_defconfig
@@ -38,8 +38,6 @@
 CONFIG_ARCH_MSM8930=y
 CONFIG_ARCH_APQ8064=y
 CONFIG_MSM_KRAIT_TBB_ABORT_HANDLER=y
-CONFIG_MACH_MSM8960_SIM=y
-CONFIG_MACH_MSM8960_RUMI3=y
 CONFIG_MACH_MSM8960_CDP=y
 CONFIG_MACH_MSM8960_MTP=y
 CONFIG_MACH_MSM8960_FLUID=y
@@ -49,8 +47,6 @@
 CONFIG_MACH_MSM8930_FLUID=y
 CONFIG_MACH_MSM8627_CDP=y
 CONFIG_MACH_MSM8627_MTP=y
-CONFIG_MACH_APQ8064_SIM=y
-CONFIG_MACH_APQ8064_RUMI3=y
 CONFIG_MACH_APQ8064_CDP=y
 CONFIG_MACH_APQ8064_MTP=y
 CONFIG_MACH_APQ8064_LIQUID=y
@@ -65,30 +61,28 @@
 CONFIG_MSM_SMD_PKG4=y
 CONFIG_MSM_PCIE=y
 CONFIG_MSM_BAM_DMUX=y
+CONFIG_MSM_IPC_LOGGING=y
 CONFIG_MSM_DSPS=y
 CONFIG_MSM_IPC_ROUTER=y
 CONFIG_MSM_IPC_ROUTER_SMD_XPRT=y
 CONFIG_MSM_AVS_HW=y
 # CONFIG_MSM_HW3D is not set
-CONFIG_MSM_PIL_QDSP6V4=y
+CONFIG_MSM_SUBSYSTEM_RESTART=y
+CONFIG_MSM_SYSMON_COMM=y
+CONFIG_MSM_PIL_LPASS_QDSP6V4=y
+CONFIG_MSM_PIL_MODEM_QDSP6V4=y
 CONFIG_MSM_PIL_RIVA=y
 CONFIG_MSM_PIL_TZAPPS=y
 CONFIG_MSM_PIL_DSPS=y
 CONFIG_MSM_PIL_VIDC=y
 CONFIG_MSM_PIL_GSS=y
-CONFIG_MSM_SUBSYSTEM_RESTART=y
-CONFIG_MSM_SYSMON_COMM=y
-CONFIG_MSM_MODEM_8960=y
-CONFIG_MSM_LPASS_8960=y
-CONFIG_MSM_WCNSS_SSR_8960=y
-CONFIG_MSM_GSS_SSR_8064=y
 CONFIG_MSM_TZ_LOG=y
 CONFIG_MSM_RPM_LOG=y
 CONFIG_MSM_RPM_STATS_LOG=y
 CONFIG_MSM_RPM_RBCPR_STATS_LOG=y
+CONFIG_MSM_EVENT_TIMER=y
 CONFIG_MSM_BUS_SCALING=y
 CONFIG_MSM_BUS_RPM_MULTI_TIER_ENABLED=y
-CONFIG_MSM_EVENT_TIMER=y
 CONFIG_MSM_WATCHDOG=y
 CONFIG_MSM_DLOAD_MODE=y
 CONFIG_MSM_RTB=y
@@ -235,7 +229,6 @@
 CONFIG_NET_EMATCH_META=y
 CONFIG_NET_EMATCH_TEXT=y
 CONFIG_NET_CLS_ACT=y
-CONFIG_MARIMBA_CORE=y
 CONFIG_BT=y
 CONFIG_BT_RFCOMM=y
 CONFIG_BT_RFCOMM_TTY=y
@@ -245,8 +238,8 @@
 CONFIG_BT_HIDP=y
 CONFIG_BT_HCISMD=y
 CONFIG_BT_HCIUART=y
-CONFIG_BT_HCIUART_ATH3K=y
 CONFIG_BT_HCIUART_H4=y
+CONFIG_BT_HCIUART_ATH3K=y
 CONFIG_BT_HCIUART_IBS=y
 CONFIG_MSM_BT_POWER=y
 CONFIG_CFG80211=m
@@ -256,6 +249,7 @@
 CONFIG_GENLOCK_MISCDEVICE=y
 CONFIG_BLK_DEV_LOOP=y
 CONFIG_BLK_DEV_RAM=y
+CONFIG_TSPP=m
 CONFIG_HAPTIC_ISA1200=y
 CONFIG_PMIC8XXX_VIBRATOR=y
 CONFIG_QSEECOM=y
@@ -288,6 +282,7 @@
 CONFIG_USB_USBNET=y
 CONFIG_MSM_RMNET_USB=y
 CONFIG_WCNSS_CORE=y
+CONFIG_WCNSS_MEM_PRE_ALLOC=y
 CONFIG_INPUT_EVDEV=y
 CONFIG_INPUT_EVBUG=m
 CONFIG_KEYBOARD_GPIO=y
@@ -300,11 +295,10 @@
 CONFIG_TOUCHSCREEN_CYTTSP_I2C_QC=y
 CONFIG_INPUT_MISC=y
 CONFIG_INPUT_PMIC8XXX_PWRKEY=y
+CONFIG_INPUT_MPU3050=y
 CONFIG_INPUT_UINPUT=y
 CONFIG_STM_LIS3DH=y
-CONFIG_INPUT_MPU3050=y
 # CONFIG_LEGACY_PTYS is not set
-CONFIG_MSM_IPC_LOGGING=y
 CONFIG_N_SMUX=y
 CONFIG_N_SMUX_LOOPBACK=y
 CONFIG_SMUX_CTL=y
@@ -338,6 +332,7 @@
 CONFIG_THERMAL_TSENS8960=y
 CONFIG_THERMAL_PM8XXX=y
 CONFIG_THERMAL_MONITOR=y
+CONFIG_MARIMBA_CORE=y
 CONFIG_MFD_PM8921_CORE=y
 CONFIG_MFD_PM8821_CORE=y
 CONFIG_MFD_PM8038_CORE=y
@@ -351,7 +346,7 @@
 CONFIG_MEDIA_CONTROLLER=y
 CONFIG_VIDEO_DEV=y
 CONFIG_VIDEO_V4L2_SUBDEV_API=y
-CONFIG_MSM_WFD=y
+CONFIG_DVB_CORE=m
 CONFIG_USER_RC_INPUT=y
 CONFIG_IR_GPIO_CIR=y
 # CONFIG_MEDIA_TUNER_CUSTOMISE is not set
@@ -375,8 +370,15 @@
 CONFIG_MSM_CSI20_HEADER=y
 CONFIG_S5K3L1YX=y
 CONFIG_IMX091=y
+CONFIG_MSM_WFD=y
+CONFIG_IMX135=y
 CONFIG_RADIO_IRIS=y
 CONFIG_RADIO_IRIS_TRANSPORT=m
+# CONFIG_DVB_FE_CUSTOMISE is not set
+CONFIG_DVB_MPQ=m
+CONFIG_DVB_MPQ_DEMUX=m
+CONFIG_DVB_MPQ_VIDEO=m
+CONFIG_DVB_MPQ_TSPP1=y
 CONFIG_ION=y
 CONFIG_ION_MSM=y
 CONFIG_MSM_KGSL=y
@@ -439,7 +441,6 @@
 CONFIG_USB_GADGET_DEBUG_FILES=y
 CONFIG_USB_CI13XXX_MSM=y
 CONFIG_USB_G_ANDROID=y
-CONFIG_USB_ANDROID_RMNET_CTRL_SMD=y
 CONFIG_MMC=y
 CONFIG_MMC_PERF_PROFILING=y
 CONFIG_MMC_UNSAFE_RESUME=y
@@ -476,6 +477,7 @@
 CONFIG_MOBICORE_API=m
 CONFIG_MSM_QDSS=y
 CONFIG_MSM_QDSS_ETM_DEFAULT_ENABLE=y
+CONFIG_CONTROL_TRACE=m
 CONFIG_EXT2_FS=y
 CONFIG_EXT2_FS_XATTR=y
 CONFIG_EXT3_FS=y
@@ -513,7 +515,6 @@
 CONFIG_FAULT_INJECTION_DEBUG_FS=y
 CONFIG_FAULT_INJECTION_STACKTRACE_FILTER=y
 CONFIG_DEBUG_PAGEALLOC=y
-CONFIG_ENABLE_DEFAULT_TRACERS=y
 CONFIG_CPU_FREQ_SWITCH_PROFILER=y
 CONFIG_DYNAMIC_DEBUG=y
 CONFIG_DEBUG_USER=y
@@ -523,5 +524,3 @@
 CONFIG_CRYPTO_DEV_QCE=m
 CONFIG_CRYPTO_DEV_QCEDEV=m
 CONFIG_CRC_CCITT=y
-CONFIG_WCNSS_MEM_PRE_ALLOC=y
-CONFIG_CONTROL_TRACE=m
diff --git a/arch/arm/configs/msm8974-perf_defconfig b/arch/arm/configs/msm8974-perf_defconfig
index 2f1833e..8bf3429 100644
--- a/arch/arm/configs/msm8974-perf_defconfig
+++ b/arch/arm/configs/msm8974-perf_defconfig
@@ -50,14 +50,12 @@
 CONFIG_MSM_IPC_ROUTER_SMD_XPRT=y
 # CONFIG_MSM_HW3D is not set
 CONFIG_MSM_SUBSYSTEM_RESTART=y
+CONFIG_MSM_SYSMON_COMM=y
 CONFIG_MSM_PIL_LPASS_QDSP6V5=y
 CONFIG_MSM_PIL_MSS_QDSP6V5=y
 CONFIG_MSM_PIL_MBA=y
 CONFIG_MSM_PIL_VENUS=y
 CONFIG_MSM_PIL_PRONTO=y
-CONFIG_MSM_MODEM_SSR_8974=y
-CONFIG_MSM_ADSP_SSR_8974=y
-CONFIG_MSM_WCNSS_SSR_8974=y
 CONFIG_MSM_TZ_LOG=y
 CONFIG_MSM_DIRECT_SCLK_ACCESS=y
 CONFIG_MSM_BUS_SCALING=y
@@ -73,6 +71,7 @@
 CONFIG_MSM_L1_ERR_PANIC=y
 CONFIG_MSM_L1_ERR_LOG=y
 CONFIG_MSM_L2_ERP_2BIT_PANIC=y
+CONFIG_MSM_ENABLE_WDOG_DEBUG_CONTROL=y
 CONFIG_STRICT_MEMORY_RWX=y
 CONFIG_NO_HZ=y
 CONFIG_HIGH_RES_TIMERS=y
@@ -240,6 +239,7 @@
 CONFIG_MSM_RMNET_BAM=y
 CONFIG_SLIP=y
 CONFIG_SLIP_COMPRESSED=y
+CONFIG_USB_USBNET=y
 CONFIG_SLIP_MODE_SLIP6=y
 CONFIG_WCNSS_CORE=y
 CONFIG_WCNSS_CORE_PRONTO=y
@@ -271,11 +271,15 @@
 CONFIG_GPIO_QPNP_PIN=y
 CONFIG_GPIO_QPNP_PIN_DEBUG=y
 CONFIG_POWER_SUPPLY=y
-# CONFIG_BATTERY_MSM is not set
+CONFIG_BATTERY_BQ28400=y
+CONFIG_QPNP_CHARGER=y
+CONFIG_QPNP_BMS=y
 CONFIG_SENSORS_QPNP_ADC_VOLTAGE=y
 CONFIG_SENSORS_QPNP_ADC_CURRENT=y
+CONFIG_SENSORS_EPM_ADC=y
 CONFIG_THERMAL=y
 CONFIG_THERMAL_TSENS8974=y
+CONFIG_THERMAL_QPNP=y
 CONFIG_WCD9320_CODEC=y
 CONFIG_REGULATOR_FIXED_VOLTAGE=y
 CONFIG_REGULATOR_STUB=y
@@ -285,8 +289,10 @@
 CONFIG_VIDEO_DEV=y
 CONFIG_VIDEO_V4L2_SUBDEV_API=y
 CONFIG_VIDEOBUF2_MSM_MEM=y
+CONFIG_USB_VIDEO_CLASS=y
 CONFIG_V4L_PLATFORM_DRIVERS=y
 CONFIG_MSM_CAMERA_V4L2=y
+CONFIG_MT9M114=y
 CONFIG_OV2720=y
 CONFIG_MSM_CAMERA_SENSOR=y
 CONFIG_MSM_ACTUATOR=y
@@ -298,7 +304,7 @@
 CONFIG_MSM_CSI2_REGISTER=y
 CONFIG_MSM_ISPIF=y
 CONFIG_S5K3L1YX=y
-CONFIG_MT9M114=y
+CONFIG_MSM_WFD=y
 CONFIG_RADIO_IRIS=y
 CONFIG_RADIO_IRIS_TRANSPORT=m
 CONFIG_ION=y
@@ -316,10 +322,12 @@
 # CONFIG_BACKLIGHT_GENERIC is not set
 CONFIG_SOUND=y
 CONFIG_SND=y
+CONFIG_SND_USB_AUDIO=y
 CONFIG_SND_SOC=y
 CONFIG_SND_SOC_MSM8974=y
 CONFIG_USB=y
 CONFIG_USB_XHCI_HCD=y
+CONFIG_USB_ACM=y
 CONFIG_USB_STORAGE=y
 CONFIG_USB_STORAGE_DATAFAB=y
 CONFIG_USB_STORAGE_FREECOM=y
@@ -335,7 +343,7 @@
 CONFIG_USB_STORAGE_ENE_UB6250=y
 CONFIG_USB_GADGET=y
 CONFIG_USB_GADGET_DEBUG_FILES=y
-CONFIG_USB_CI13XXX_MSM=y
+CONFIG_USB_DWC3_MSM=y
 CONFIG_USB_G_ANDROID=y
 CONFIG_MMC=y
 CONFIG_MMC_PERF_PROFILING=y
@@ -367,6 +375,7 @@
 CONFIG_USB_BAM=y
 CONFIG_SPS_SUPPORT_BAMDMA=y
 CONFIG_SPS_SUPPORT_NDP_BAM=y
+CONFIG_QPNP_PWM=y
 CONFIG_QPNP_POWER_ON=y
 CONFIG_QPNP_CLKDIV=y
 CONFIG_MSM_IOMMU=y
@@ -402,4 +411,3 @@
 CONFIG_CRYPTO_DEV_QCE=m
 CONFIG_CRYPTO_DEV_QCEDEV=m
 CONFIG_CRC_CCITT=y
-CONFIG_MSM_ENABLE_WDOG_DEBUG_CONTROL=y
diff --git a/arch/arm/configs/msm8974_defconfig b/arch/arm/configs/msm8974_defconfig
index 1230fbe..e2524ba 100644
--- a/arch/arm/configs/msm8974_defconfig
+++ b/arch/arm/configs/msm8974_defconfig
@@ -36,7 +36,6 @@
 CONFIG_EFI_PARTITION=y
 CONFIG_ARCH_MSM=y
 CONFIG_ARCH_MSM8974=y
-CONFIG_ARCH_MSM8226=y
 CONFIG_MSM_KRAIT_TBB_ABORT_HANDLER=y
 # CONFIG_MSM_STACKED_MEMORY is not set
 CONFIG_CPU_HAS_L2_PMU=y
@@ -55,9 +54,6 @@
 CONFIG_MSM_PIL_MBA=y
 CONFIG_MSM_PIL_VENUS=y
 CONFIG_MSM_PIL_PRONTO=y
-CONFIG_MSM_MODEM_SSR_8974=y
-CONFIG_MSM_ADSP_SSR_8974=y
-CONFIG_MSM_WCNSS_SSR_8974=y
 CONFIG_MSM_TZ_LOG=y
 CONFIG_MSM_DIRECT_SCLK_ACCESS=y
 CONFIG_MSM_BUS_SCALING=y
@@ -76,6 +72,7 @@
 CONFIG_MSM_L2_ERP_2BIT_PANIC=y
 CONFIG_MSM_CACHE_DUMP=y
 CONFIG_MSM_CACHE_DUMP_ON_PANIC=y
+CONFIG_MSM_ENABLE_WDOG_DEBUG_CONTROL=y
 CONFIG_STRICT_MEMORY_RWX=y
 CONFIG_NO_HZ=y
 CONFIG_HIGH_RES_TIMERS=y
@@ -243,6 +240,7 @@
 CONFIG_MSM_RMNET_BAM=y
 CONFIG_SLIP=y
 CONFIG_SLIP_COMPRESSED=y
+CONFIG_USB_USBNET=y
 CONFIG_SLIP_MODE_SLIP6=y
 CONFIG_WCNSS_CORE=y
 CONFIG_WCNSS_CORE_PRONTO=y
@@ -274,12 +272,16 @@
 CONFIG_GPIO_QPNP_PIN=y
 CONFIG_GPIO_QPNP_PIN_DEBUG=y
 CONFIG_POWER_SUPPLY=y
-# CONFIG_BATTERY_MSM is not set
+CONFIG_BATTERY_BQ28400=y
+CONFIG_SMB350_CHARGER=y
+CONFIG_QPNP_CHARGER=y
 CONFIG_QPNP_BMS=y
 CONFIG_SENSORS_QPNP_ADC_VOLTAGE=y
 CONFIG_SENSORS_QPNP_ADC_CURRENT=y
+CONFIG_SENSORS_EPM_ADC=y
 CONFIG_THERMAL=y
 CONFIG_THERMAL_TSENS8974=y
+CONFIG_THERMAL_QPNP=y
 CONFIG_WCD9320_CODEC=y
 CONFIG_REGULATOR_FIXED_VOLTAGE=y
 CONFIG_REGULATOR_STUB=y
@@ -289,6 +291,7 @@
 CONFIG_VIDEO_DEV=y
 CONFIG_VIDEO_V4L2_SUBDEV_API=y
 CONFIG_VIDEOBUF2_MSM_MEM=y
+CONFIG_USB_VIDEO_CLASS=y
 CONFIG_V4L_PLATFORM_DRIVERS=y
 CONFIG_MSM_CAMERA_V4L2=y
 CONFIG_MT9M114=y
@@ -321,10 +324,12 @@
 # CONFIG_BACKLIGHT_GENERIC is not set
 CONFIG_SOUND=y
 CONFIG_SND=y
+CONFIG_SND_USB_AUDIO=y
 CONFIG_SND_SOC=y
 CONFIG_SND_SOC_MSM8974=y
 CONFIG_USB=y
 CONFIG_USB_XHCI_HCD=y
+CONFIG_USB_ACM=y
 CONFIG_USB_STORAGE=y
 CONFIG_USB_STORAGE_DATAFAB=y
 CONFIG_USB_STORAGE_FREECOM=y
@@ -340,7 +345,7 @@
 CONFIG_USB_STORAGE_ENE_UB6250=y
 CONFIG_USB_GADGET=y
 CONFIG_USB_GADGET_DEBUG_FILES=y
-CONFIG_USB_CI13XXX_MSM=y
+CONFIG_USB_DWC3_MSM=y
 CONFIG_USB_G_ANDROID=y
 CONFIG_MMC=y
 CONFIG_MMC_PERF_PROFILING=y
@@ -372,6 +377,7 @@
 CONFIG_USB_BAM=y
 CONFIG_SPS_SUPPORT_BAMDMA=y
 CONFIG_SPS_SUPPORT_NDP_BAM=y
+CONFIG_QPNP_PWM=y
 CONFIG_QPNP_POWER_ON=y
 CONFIG_QPNP_CLKDIV=y
 CONFIG_MSM_IOMMU=y
@@ -398,6 +404,7 @@
 CONFIG_DEBUG_KMEMLEAK_DEFAULT_OFF=y
 # CONFIG_DEBUG_PREEMPT is not set
 CONFIG_DEBUG_SPINLOCK=y
+CONFIG_DEBUG_MUTEXES=y
 CONFIG_DEBUG_ATOMIC_SLEEP=y
 CONFIG_DEBUG_STACK_USAGE=y
 CONFIG_DEBUG_INFO=y
@@ -408,7 +415,6 @@
 CONFIG_FAULT_INJECTION_DEBUG_FS=y
 CONFIG_FAULT_INJECTION_STACKTRACE_FILTER=y
 CONFIG_DEBUG_PAGEALLOC=y
-CONFIG_ENABLE_DEFAULT_TRACERS=y
 CONFIG_CPU_FREQ_SWITCH_PROFILER=y
 CONFIG_DYNAMIC_DEBUG=y
 CONFIG_DEBUG_USER=y
@@ -421,4 +427,3 @@
 CONFIG_CRYPTO_DEV_QCE=m
 CONFIG_CRYPTO_DEV_QCEDEV=m
 CONFIG_CRC_CCITT=y
-CONFIG_MSM_ENABLE_WDOG_DEBUG_CONTROL=y
diff --git a/arch/arm/configs/msm9615_defconfig b/arch/arm/configs/msm9615_defconfig
index 2cc801e..81b853d 100644
--- a/arch/arm/configs/msm9615_defconfig
+++ b/arch/arm/configs/msm9615_defconfig
@@ -47,9 +47,8 @@
 CONFIG_MSM_IPC_ROUTER=y
 CONFIG_MSM_IPC_ROUTER_SMD_XPRT=y
 CONFIG_MSM_SUBSYSTEM_RESTART=y
-# CONFIG_MSM_SYSMON_COMM is not set
-CONFIG_MSM_MODEM_8960=y
-CONFIG_MSM_LPASS_8960=y
+CONFIG_MSM_PIL_LPASS_QDSP6V4=y
+CONFIG_MSM_PIL_MODEM_QDSP6V4=y
 CONFIG_MSM_RPM_LOG=y
 CONFIG_MSM_RPM_STATS_LOG=y
 CONFIG_MSM_DIRECT_SCLK_ACCESS=y
diff --git a/arch/arm/configs/msm9625_defconfig b/arch/arm/configs/msm9625_defconfig
index 364a1bf..e1d4ca0 100644
--- a/arch/arm/configs/msm9625_defconfig
+++ b/arch/arm/configs/msm9625_defconfig
@@ -35,6 +35,12 @@
 CONFIG_CPU_HAS_L2_PMU=y
 # CONFIG_MSM_FIQ_SUPPORT is not set
 # CONFIG_MSM_PROC_COMM is not set
+CONFIG_MSM_SMD=y
+CONFIG_MSM_SMD_PKG4=y
+CONFIG_MSM_BAM_DMUX=y
+CONFIG_MSM_IPC_ROUTER=y
+CONFIG_MSM_IPC_ROUTER_SMD_XPRT=y
+CONFIG_MSM_RPM_REGULATOR_SMD=y
 CONFIG_MSM_DIRECT_SCLK_ACCESS=y
 CONFIG_MSM_WATCHDOG_V2=y
 CONFIG_NO_HZ=y
@@ -45,15 +51,25 @@
 CONFIG_HIGHMEM=y
 CONFIG_VMALLOC_RESERVE=0x19000000
 CONFIG_USE_OF=y
+CONFIG_CPU_IDLE=y
+CONFIG_CPU_FREQ=y
+CONFIG_CPU_FREQ_GOV_POWERSAVE=y
+CONFIG_CPU_FREQ_GOV_USERSPACE=y
+CONFIG_CPU_FREQ_GOV_ONDEMAND=y
+CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y
 CONFIG_VFP=y
 CONFIG_NEON=y
 # CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
-# CONFIG_SUSPEND is not set
+CONFIG_PM_RUNTIME=y
 CONFIG_NET=y
 CONFIG_PACKET=y
 CONFIG_UNIX=y
 CONFIG_INET=y
 CONFIG_IPV6=y
+CONFIG_NET_SCHED=y
+CONFIG_NET_SCH_HTB=y
+CONFIG_NET_SCH_PRIO=y
+CONFIG_NET_CLS_FW=y
 # CONFIG_WIRELESS is not set
 CONFIG_MTD=y
 CONFIG_MTD_CMDLINE_PARTS=y
@@ -69,9 +85,17 @@
 # CONFIG_NET_VENDOR_CIRRUS is not set
 # CONFIG_NET_VENDOR_FARADAY is not set
 # CONFIG_NET_VENDOR_INTEL is not set
+CONFIG_WIRELESS=y
+CONFIG_CFG80211=m
+CONFIG_NL80211_TESTMODE=y
+CONFIG_ATH_COMMON=m
+CONFIG_ATH6KL=m
+CONFIG_ATH6KL_SDIO=m
+CONFIG_ATH6KL_DEBUG=y
 CONFIG_KS8851=y
 # CONFIG_NET_VENDOR_MICROCHIP is not set
 # CONFIG_MSM_RMNET is not set
+CONFIG_MSM_RMNET_BAM=y
 # CONFIG_NET_VENDOR_NATSEMI is not set
 # CONFIG_NET_VENDOR_SEEQ is not set
 # CONFIG_NET_VENDOR_SMSC is not set
@@ -89,23 +113,43 @@
 CONFIG_SERIAL_MSM_HSL_CONSOLE=y
 CONFIG_DIAG_CHAR=y
 CONFIG_HW_RANDOM=y
+CONFIG_I2C=y
+CONFIG_I2C_CHARDEV=y
+CONFIG_I2C_QUP=y
 CONFIG_SPI=y
 CONFIG_SPI_QUP=y
 CONFIG_SPI_SPIDEV=m
 CONFIG_SPMI=y
 CONFIG_SPMI_MSM_PMIC_ARB=y
 CONFIG_MSM_QPNP_INT=y
+CONFIG_REGULATOR_FIXED_VOLTAGE=y
 CONFIG_DEBUG_GPIO=y
 CONFIG_GPIO_SYSFS=y
 CONFIG_GPIO_QPNP_PIN=y
 CONFIG_GPIO_QPNP_PIN_DEBUG=y
-# CONFIG_HWMON is not set
+CONFIG_POWER_SUPPLY=y
+CONFIG_HWMON=y
+CONFIG_SENSORS_QPNP_ADC_VOLTAGE=y
 CONFIG_REGULATOR=y
 CONFIG_REGULATOR_QPNP=y
-# CONFIG_HID_SUPPORT is not set
+CONFIG_ION=y
+CONFIG_ION_MSM=y
+CONFIG_SOUND=y
+CONFIG_SND=y
+CONFIG_SND_SOC=y
 CONFIG_USB_GADGET=y
 CONFIG_USB_CI13XXX_MSM=y
 CONFIG_USB_G_ANDROID=y
+CONFIG_MMC=y
+CONFIG_MMC_PERF_PROFILING=y
+CONFIG_MMC_UNSAFE_RESUME=y
+CONFIG_MMC_CLKGATE=y
+CONFIG_MMC_EMBEDDED_SDIO=y
+CONFIG_MMC_PARANOID_SD_INIT=y
+CONFIG_MMC_BLOCK_MINORS=32
+CONFIG_MMC_TEST=m
+CONFIG_MMC_MSM=y
+CONFIG_MMC_MSM_SPS_SUPPORT=y
 CONFIG_RTC_CLASS=y
 # CONFIG_RTC_DRV_MSM is not set
 CONFIG_RTC_DRV_QPNP=y
@@ -113,6 +157,7 @@
 CONFIG_USB_BAM=y
 CONFIG_SPS_SUPPORT_BAMDMA=y
 CONFIG_SPS_SUPPORT_NDP_BAM=y
+CONFIG_QPNP_POWER_ON=y
 CONFIG_VFAT_FS=y
 CONFIG_TMPFS=y
 CONFIG_YAFFS_FS=y
@@ -146,3 +191,5 @@
 # CONFIG_CRYPTO_HW is not set
 CONFIG_CRC_CCITT=y
 CONFIG_LIBCRC32C=y
+CONFIG_PANIC_TIMEOUT=5
+CONFIG_MSM_DLOAD_MODE=y
diff --git a/arch/arm/include/asm/fiq.h b/arch/arm/include/asm/fiq.h
index ec4b8b8..006f577 100644
--- a/arch/arm/include/asm/fiq.h
+++ b/arch/arm/include/asm/fiq.h
@@ -39,6 +39,7 @@
 extern void set_fiq_handler(void *start, unsigned int length);
 extern void enable_fiq(int fiq);
 extern void disable_fiq(int fiq);
+extern void fiq_set_type(int fiq, unsigned int type);
 #else
 static inline int claim_fiq(struct fiq_handler *f)
 {
@@ -48,6 +49,7 @@
 static inline void set_fiq_handler(void *start, unsigned int length) { }
 static inline void enable_fiq(int fiq) { }
 static inline void disable_fiq(int fiq) { }
+static inline void fiq_set_type(int fiq, unsigned int type) { }
 #endif
 
 /* helpers defined in fiqasm.S: */
diff --git a/arch/arm/include/asm/hardware/gic.h b/arch/arm/include/asm/hardware/gic.h
index ad12bcd..72c3c27 100644
--- a/arch/arm/include/asm/hardware/gic.h
+++ b/arch/arm/include/asm/hardware/gic.h
@@ -62,7 +62,7 @@
 
 void msm_gic_save(void);
 void msm_gic_restore(void);
-void core1_gic_configure_and_raise(void);
+void gic_configure_and_raise(unsigned int irq, unsigned int cpu);
 #endif
 
 #endif
diff --git a/arch/arm/include/asm/io.h b/arch/arm/include/asm/io.h
index 42fef7c..938be62 100644
--- a/arch/arm/include/asm/io.h
+++ b/arch/arm/include/asm/io.h
@@ -268,12 +268,17 @@
 					__raw_readw(c)); __r; })
 #define readl_relaxed(c) ({ u32 __r = le32_to_cpu((__force __le32) \
 					__raw_readl(c)); __r; })
+#define readl_relaxed_no_log(c) ({ u32 __r = le32_to_cpu((__force __le32) \
+					__raw_readl_no_log(c)); __r; })
+
 
 #define writeb_relaxed(v,c)	((void)__raw_writeb(v,c))
 #define writew_relaxed(v,c)	((void)__raw_writew((__force u16) \
 					cpu_to_le16(v),c))
 #define writel_relaxed(v,c)	((void)__raw_writel((__force u32) \
 					cpu_to_le32(v),c))
+#define writel_relaxed_no_log(v, c)  ((void)__raw_writel_no_log((__force u32) \
+					cpu_to_le32(v), c))
 
 #define readb(c)		({ u8  __v = readb_relaxed(c); __iormb(); __v; })
 #define readw(c)		({ u16 __v = readw_relaxed(c); __iormb(); __v; })
diff --git a/arch/arm/include/asm/outercache.h b/arch/arm/include/asm/outercache.h
index 53426c6..12f71a1 100644
--- a/arch/arm/include/asm/outercache.h
+++ b/arch/arm/include/asm/outercache.h
@@ -92,6 +92,7 @@
 static inline void outer_flush_all(void) { }
 static inline void outer_inv_all(void) { }
 static inline void outer_disable(void) { }
+static inline void outer_resume(void) { }
 
 #endif
 
diff --git a/arch/arm/kernel/fiq.c b/arch/arm/kernel/fiq.c
index c32f845..ca852c5 100644
--- a/arch/arm/kernel/fiq.c
+++ b/arch/arm/kernel/fiq.c
@@ -39,6 +39,7 @@
 #include <linux/kernel.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
+#include <linux/irq.h>
 #include <linux/seq_file.h>
 
 #include <asm/cacheflush.h>
@@ -132,6 +133,11 @@
 	disable_irq(fiq + FIQ_START);
 }
 
+void fiq_set_type(int fiq, unsigned int type)
+{
+	irq_set_irq_type(fiq + FIQ_START, type);
+}
+
 EXPORT_SYMBOL(set_fiq_handler);
 EXPORT_SYMBOL(__set_fiq_regs);	/* defined in fiqasm.S */
 EXPORT_SYMBOL(__get_fiq_regs);	/* defined in fiqasm.S */
@@ -139,6 +145,7 @@
 EXPORT_SYMBOL(release_fiq);
 EXPORT_SYMBOL(enable_fiq);
 EXPORT_SYMBOL(disable_fiq);
+EXPORT_SYMBOL(fiq_set_type);
 
 void __init init_FIQ(void)
 {
diff --git a/arch/arm/kernel/perf_event_msm.c b/arch/arm/kernel/perf_event_msm.c
index 90c9c9e..8f58adf 100644
--- a/arch/arm/kernel/perf_event_msm.c
+++ b/arch/arm/kernel/perf_event_msm.c
@@ -709,8 +709,6 @@
 
 static struct arm_pmu scorpion_pmu = {
 	.handle_irq		= armv7pmu_handle_irq,
-	.request_pmu_irq	= msm_request_irq,
-	.free_pmu_irq		= msm_free_irq,
 	.enable			= scorpion_pmu_enable_event,
 	.disable		= scorpion_pmu_disable_event,
 	.read_counter		= armv7pmu_read_counter,
@@ -731,6 +729,9 @@
 	scorpion_pmu.name	= "ARMv7 Scorpion";
 	scorpion_pmu.num_events	= armv7_read_num_pmnc_events();
 	scorpion_pmu.pmu.attr_groups	= msm_l1_pmu_attr_grps;
+	/* Unicore can't use the percpu IRQ API. */
+	scorpion_pmu.request_pmu_irq	= armpmu_generic_request_irq;
+	scorpion_pmu.free_pmu_irq	= armpmu_generic_free_irq;
 	scorpion_clear_pmuregs();
 	return &scorpion_pmu;
 }
@@ -741,6 +742,8 @@
 	scorpion_pmu.name	= "ARMv7 Scorpion-MP";
 	scorpion_pmu.num_events	= armv7_read_num_pmnc_events();
 	scorpion_pmu.pmu.attr_groups	= msm_l1_pmu_attr_grps;
+	scorpion_pmu.request_pmu_irq	= msm_request_irq;
+	scorpion_pmu.free_pmu_irq	= msm_free_irq;
 	scorpion_clear_pmuregs();
 	return &scorpion_pmu;
 }
diff --git a/arch/arm/kernel/perf_event_msm_krait.c b/arch/arm/kernel/perf_event_msm_krait.c
index 8d8f47a..eec614b 100644
--- a/arch/arm/kernel/perf_event_msm_krait.c
+++ b/arch/arm/kernel/perf_event_msm_krait.c
@@ -540,8 +540,8 @@
         int err = 0;
         int cpu;
 
-        err = request_percpu_irq(irq, *handle_irq, "krait-l1-armpmu",
-                        &cpu_hw_events);
+	err = request_percpu_irq(irq, *handle_irq, "l1-armpmu",
+			&cpu_hw_events);
 
         if (!err) {
                 for_each_cpu(cpu, cpu_online_mask) {
diff --git a/arch/arm/kernel/perf_event_v7.c b/arch/arm/kernel/perf_event_v7.c
index 678c55d..3163b2a 100644
--- a/arch/arm/kernel/perf_event_v7.c
+++ b/arch/arm/kernel/perf_event_v7.c
@@ -1235,6 +1235,8 @@
 }
 
 static struct arm_pmu armv7pmu = {
+	.request_pmu_irq	= armpmu_generic_request_irq,
+	.free_pmu_irq		= armpmu_generic_free_irq,
 	.handle_irq		= armv7pmu_handle_irq,
 	.enable			= armv7pmu_enable_event,
 	.disable		= armv7pmu_disable_event,
diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig
index 2020422..fd642ee 100644
--- a/arch/arm/mach-msm/Kconfig
+++ b/arch/arm/mach-msm/Kconfig
@@ -22,7 +22,7 @@
 config ARCH_MSM7X27
 	bool "MSM7x27"
 	select ARCH_MSM_ARM11 if MSM_SOC_REV_NONE
-	select ARCH_HAS_BARRIERS if MSM_SOC_REV_NONE
+	select ARCH_HAS_BARRIERS
 	select ARCH_MSM_CORTEX_A5 if MSM_SOC_REV_A
 	select MSM_VIC
 	select CPU_V6 if MSM_SOC_REV_NONE
@@ -176,6 +176,7 @@
 	select MSM_RUN_QUEUE_STATS
 	select ARM_HAS_SG_CHAIN
 	select MSM_KRAIT_WFE_FIXUP
+	select MSM_ULTRASOUND_A
 
 config ARCH_MSM8930
 	bool "MSM8930"
@@ -202,7 +203,7 @@
 	select MSM_REMOTE_SPINLOCK_SFPB
 	select ARCH_SPARSEMEM_ENABLE
 	select ARCH_HAS_HOLES_MEMORYMODEL
-	select MSM_ULTRASOUND
+	select MSM_ULTRASOUND_A
 	select MULTI_IRQ_HANDLER
 	select MSM_PM8X60 if PM
 	select HOLES_IN_ZONE if SPARSEMEM
@@ -235,6 +236,7 @@
 	select ARCH_SUPPORTS_MSI
 	select ARM_HAS_SG_CHAIN
 	select MSM_KRAIT_WFE_FIXUP
+	select MSM_ULTRASOUND_A
 
 config ARCH_MSM8974
 	bool "MSM8974"
@@ -262,6 +264,7 @@
 	select MSM_RPM_REGULATOR_SMD
 	select ARM_HAS_SG_CHAIN
 	select MSM_RUN_QUEUE_STATS
+	select MEMORY_HOLE_CARVEOUT
 
 config ARCH_MPQ8092
 	bool "MPQ8092"
@@ -315,6 +318,7 @@
 	select GIC_SECURE
 	select ARCH_MSM_CORTEX_A5
 	select CPU_V7
+	select MIGHT_HAVE_CACHE_L2X0
 	select GPIO_MSM_V2
 	select MSM_GPIOMUX
 	select MSM_RPM
@@ -348,17 +352,40 @@
 config ARCH_MSM9625
 	bool "MSM9625"
 	select ARM_GIC
-	select GIC_SECURE
 	select MIGHT_HAVE_CACHE_L2X0
 	select ARCH_MSM_CORTEX_A5
 	select SMP
 	select MSM_SMP
 	select CPU_V7
+	select MSM_SCM if SMP
 	select MSM_GPIOMUX
+	select MSM_RPM_SMD
+	select MSM_NATIVE_RESTART
+	select MSM_RESTART_V2
+	select MSM_SPM_V2
+	select MSM_PM8X60 if PM
+	select MSM_SCM if SMP
 	select MULTI_IRQ_HANDLER
 	select GPIO_MSM_V3
 	select MAY_HAVE_SPARSE_IRQ
 	select SPARSE_IRQ
+	select MSM_MULTIMEDIA_USE_ION
+
+config ARCH_MSM8910
+	bool "MSM8910"
+	select ARM_GIC
+	select GIC_SECURE
+	select SMP
+	select ARCH_MSM_CORTEXMP
+	select CPU_V7
+	select MSM_SCM if SMP
+	select MAY_HAVE_SPARSE_IRQ
+	select SPARSE_IRQ
+	select MULTI_IRQ_HANDLER
+	select GPIO_MSM_V3
+	select MSM_GPIOMUX
+	select MSM_NATIVE_RESTART
+	select MSM_RESTART_V2
 endmenu
 
 choice
@@ -647,6 +674,18 @@
 	help
 	  Support for the Qualcomm MSM8625 Reference Design.
 
+config MACH_QRD_SKUD_PRIME
+	depends on ARCH_MSM8625
+	depends on !MSM_STACKED_MEMORY
+	default y
+	bool "MSM8625 SKUD PRIME"
+	help
+	  Support for the Qualcomm MSM8625 SKUD prime Reference Design.
+	  Add support for a SKUD prime reference design based on MSM8x25
+	  chipset. This device is much closer to a phone than regular form
+	  factor devices, with new touch, display panel and other hardware
+	  configurations.
+
 config MACH_MSM7X30_SURF
        depends on ARCH_MSM7X30
        depends on !MSM_STACKED_MEMORY
@@ -926,6 +965,7 @@
 	default "0x00000000" if ARCH_MSM8974
 	default "0x00000000" if ARCH_MPQ8092
 	default "0x00000000" if ARCH_MSM8226
+	default "0x00000000" if ARCH_MSM8910
 	default "0x10000000" if ARCH_FSM9XXX
 	default "0x00200000" if ARCH_MSM9625
 	default "0x00200000" if !MSM_STACKED_MEMORY
@@ -1914,34 +1954,44 @@
 	  Support for booting and shutting down QDSP6v3 processors (hexagon).
 	  The QDSP6 is a low power DSP used in audio software applications.
 
-config MSM_PIL_QDSP6V4
-	tristate "QDSP6v4 (Hexagon) Boot Support"
-	depends on MSM_PIL
+config MSM_PIL_LPASS_QDSP6V4
+	tristate "LPASS QDSP6v4 (Hexagon) Boot Support"
+	depends on MSM_SUBSYSTEM_RESTART
 	help
-	  Support for booting and shutting down QDSP6v4 processors (hexagon).
-	  The QDSP6 is a low power DSP used in audio, modem firmware, and modem
-	  software applications.
+	  Support for booting and shutting down QDSP6v4 processors (hexagon)
+	  in low power audio subsystems. If you would like to record or
+	  play audio then say Y here.
+
+	  If unsure, say N.
+
+config MSM_PIL_MODEM_QDSP6V4
+	tristate "Modem QDSP6v4 (Hexagon) Boot Support"
+	depends on MSM_SUBSYSTEM_RESTART
+	help
+	  Support for booting and shutting down QDSP6v4 processors (hexagon)
+	  in modem subsystems. If you would like to make or receive phone
+	  calls then say Y here.
+
+	  If unsure, say N.
 
 config MSM_PIL_LPASS_QDSP6V5
-       tristate "LPASS QDSP6v5 (Hexagon) Boot Support"
-       depends on MSM_PIL
-       help
-         Support for booting and shutting down QDSP6v5 (Hexagon) processors
-	 in low power audio subsystems.
+	tristate "LPASS QDSP6v5 (Hexagon) Boot Support"
+	depends on MSM_PIL && MSM_SUBSYSTEM_RESTART
+	help
+	  Support for booting and shutting down QDSP6v5 (Hexagon) processors
+	  in low power audio subsystems. This driver also monitors the ADSP
+	  SMSM status bits and the ADSP's watchdog interrupt and restarts the
+	  ADSP if the processor encounters a fatal error.
 
 config MSM_PIL_MSS_QDSP6V5
-       tristate "MSS QDSP6v5 (Hexagon) Boot Support"
-       depends on MSM_PIL
-       help
-         Support for booting and shutting down QDSP6v5 (Hexagon) processors
-	 in modem subsystems.
-
-config MSM_PIL_MBA
-	tristate "Support for modem self-authentication"
-	depends on MSM_PIL_MSS_QDSP6V5
+	tristate "MSS QDSP6v5 (Hexagon) Boot Support"
+	depends on MSM_PIL && MSM_SUBSYSTEM_RESTART
 	help
-	  Support for booting self-authenticating modems using the Modem Boot
-	  Authenticator.
+	  Support for booting and shutting down QDSP6v5 (Hexagon) processors
+	  in modem subsystems. If you would like to make or receive phone
+	  calls then say Y here.
+
+	  If unsure, say N.
 
 config MSM_PIL_RIVA
 	tristate "RIVA (WCNSS) Boot Support"
@@ -1953,7 +2003,7 @@
 
 config MSM_PIL_TZAPPS
 	tristate "TZApps Boot Support"
-	depends on MSM_PIL
+	depends on MSM_PIL && MSM_SUBSYSTEM_RESTART
 	help
 	  Support for booting and shutting down TZApps.
 
@@ -1963,7 +2013,7 @@
 
 config MSM_PIL_DSPS
 	tristate "DSPS Boot Support"
-	depends on MSM_PIL
+	depends on MSM_PIL && MSM_SUBSYSTEM_RESTART
 	help
 	  Support for booting and shutting down ARM7 DSPS processors.
 
@@ -1972,13 +2022,13 @@
 
 config MSM_PIL_VIDC
 	tristate "Video Core Secure Boot Support"
-	depends on MSM_PIL
+	depends on MSM_PIL && MSM_SUBSYSTEM_RESTART
 	help
 	  Support for authenticating the video core image.
 
 config MSM_PIL_VENUS
 	tristate "VENUS (Video) Boot Support"
-	depends on MSM_PIL
+	depends on MSM_PIL && MSM_SUBSYSTEM_RESTART
 	help
 	  Support for booting and shutting down the VENUS processor (Video).
 	  Venus is the Video subsystem processor used for video codecs.
@@ -1992,7 +2042,7 @@
 
 config MSM_PIL_PRONTO
 	tristate "PRONTO (WCNSS) Boot Support"
-	depends on MSM_PIL
+	depends on MSM_PIL && MSM_SUBSYSTEM_RESTART
 	help
 	  Support for booting and shutting down the PRONTO processor (WCNSS).
 	  PRONTO is the wireless subsystem processor used in bluetooth, wireless
@@ -2002,49 +2052,6 @@
 	bool "Secure Channel Manager (SCM) support"
 	default n
 
-config MSM_MODEM_8960
-	bool "MSM 8960 Modem driver"
-	depends on (ARCH_MSM8960 || ARCH_MSM9615)
-	help
-	 This option enables the modem driver for the MSM8960 and MSM9615, which monitors
-	 modem hardware watchdog interrupt lines and plugs into the subsystem
-	 restart and PIL drivers. For MSM9615, it only supports a full chip reset.
-
-config MSM_LPASS_8960
-	tristate "MSM 8960 Lpass driver"
-	depends on (ARCH_MSM8960 || ARCH_MSM9615)
-	help
-	 This option enables the lpass driver for the MSM8960 and MSM9615. This monitors
-	 lpass hardware watchdog interrupt lines and plugs into the subsystem
-	 restart and PIL drivers. For MSM9615, it only supports a full chip reset.
-
-config MSM_MODEM_SSR_8974
-	bool "MSM 8974 Modem restart driver"
-	depends on (ARCH_MSM8974)
-	help
-	 This option enables the modem subsystem restart driver for the MSM8974.
-	 It monitors the modem SMSM status bits and the modem watchdog line and
-	 restarts the modem or the 8974 when the modem encounters a fatal error,
-	 depending on the restart level selected in the subsystem restart driver.
-
-config MSM_ADSP_SSR_8974
-	bool "MSM 8974 adsp restart driver"
-	depends on (ARCH_MSM8974)
-	help
-	 This option enables the adsp restart driver for the MSM8974.
-	 It monitors the adsp SMSM status bits and the adsp watchdog line and
-	 restarts the adsp or the 8974 when the adsp encounters a fatal error,
-	 depending on the restart level selected in the subsystem restart driver.
-
-config MSM_WCNSS_SSR_8974
-	tristate "MSM 8974 WCNSS restart module"
-	depends on (ARCH_MSM8974)
-	help
-	 This option enables the WCNSS restart module for MSM8974. It monitors
-	 WCNSS SMSM status bits and WCNSS hardware watchdog interrupt line; and
-	 depending on the restart level, it will restart WCNSS when a fatal error
-	 occurs at WCNSS.
-
 config SCORPION_Uni_45nm_BUG
 	bool "Scorpion Uni 45nm(SC45U): Workaround for ICIMVAU and BPIMVA"
 	depends on ARCH_MSM7X30 || (ARCH_QSD8X50 && MSM_SOC_REV_A)
@@ -2182,7 +2189,7 @@
 
 config MSM_DLOAD_MODE
 	bool "Enable download mode on crashes"
-	depends on ARCH_MSM8X60 || ARCH_MSM8960 || ARCH_MSM9615 || ARCH_MSM8974
+	depends on ARCH_MSM8X60 || ARCH_MSM8960 || ARCH_MSM9615 || ARCH_MSM8974 || ARCH_MSM9625
 	default n
 	help
 		This makes the SoC enter download mode when it resets
@@ -2331,11 +2338,15 @@
 	  for the platforms that use APRv2.
 	  Say M if you want to enable this module.
 
-config MSM_ULTRASOUND
-	bool "MSM ultrasound support"
-	depends on MSM_AUDIO_QDSP6
+config MSM_ULTRASOUND_A
+	bool "QDSP6 HW Ultrasound support"
 	help
-	  Enable support for qdsp6/ultrasound.
+          Enable HW ultrasound support in QDSP6.
+          QDSP6 can support HW encoder & decoder and
+          ultrasound processing. It will enable
+          ultrasound data paths between
+          HW and services, calculating input events
+          upon the  ultrasound data.
 
 config MSM_RPC_VIBRATOR
 	bool "RPC based MSM Vibrator Support"
@@ -2635,4 +2646,15 @@
 	 used to control debug image.
 	 This support is currently required for MSM8974 to disable debug image
 	 on PS HOLD reset
+
+config MSM_FIQ
+	bool "Enable FIQ for debugging"
+	depends on ARCH_MSM8625
+	select FIQ
+	select GIC_SECURE
+	help
+	  Enable any line to be used as an FIQ. This will help debugging
+	  if apps is not responding and holding lock with irqs disabled.
+	  Modem will then generate an raise a FIQ on this line before sending
+	  SMSM reset.
 endif
diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile
index 7dece76..b9a16fa 100644
--- a/arch/arm/mach-msm/Makefile
+++ b/arch/arm/mach-msm/Makefile
@@ -6,6 +6,7 @@
 endif
 obj-y += clock.o clock-voter.o clock-dummy.o
 obj-y += modem_notifier.o subsystem_map.o
+obj-$(CONFIG_USE_OF) += board-dt.o
 obj-$(CONFIG_CPU_FREQ_MSM) += cpufreq.o
 obj-$(CONFIG_DEBUG_FS) += nohlt.o clock-debug.o
 obj-$(CONFIG_KEXEC) += msm_kexec.o
@@ -24,6 +25,9 @@
 
 obj-y += acpuclock.o
 obj-$(CONFIG_ARCH_MSM_KRAIT) += acpuclock-krait.o
+ifdef CONFIG_ARCH_MSM_KRAIT
+obj-$(CONFIG_DEBUG_FS) += acpuclock-krait-debug.o
+endif
 obj-$(CONFIG_ARCH_MSM7X27) += acpuclock-7627.o clock-pll.o
 obj-$(CONFIG_ARCH_MSM_SCORPION) += pmu.o
 obj-$(CONFIG_ARCH_MSM_SCORPIONMP) += perf_event_msm_l2.o
@@ -45,8 +49,12 @@
 ifdef CONFIG_ARCH_MSM8625
 	obj-$(CONFIG_SMP) += platsmp-8625.o
 else
+ifdef CONFIG_ARCH_MSM8910
+	obj-$(CONFIG_SMP) += platsmp-8910.o
+else
 	obj-$(CONFIG_SMP) += platsmp.o
 endif
+endif
 obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o
 
 obj-$(CONFIG_MSM_CPU_AVS) += avs.o
@@ -72,10 +80,10 @@
 obj-$(CONFIG_MSM_PIL) += peripheral-loader.o
 obj-$(CONFIG_MSM_PIL) += scm-pas.o
 obj-$(CONFIG_MSM_PIL_QDSP6V3) += pil-q6v3.o
-obj-$(CONFIG_MSM_PIL_QDSP6V4) += pil-q6v4.o
+obj-$(CONFIG_MSM_PIL_LPASS_QDSP6V4) += pil-q6v4.o pil-q6v4-lpass.o
+obj-$(CONFIG_MSM_PIL_MODEM_QDSP6V4) += pil-q6v4.o pil-q6v4-mss.o
 obj-$(CONFIG_MSM_PIL_LPASS_QDSP6V5) += pil-q6v5.o pil-q6v5-lpass.o
 obj-$(CONFIG_MSM_PIL_MSS_QDSP6V5) += pil-q6v5.o pil-q6v5-mss.o
-obj-$(CONFIG_MSM_PIL_MBA) += pil-mba.o
 obj-$(CONFIG_MSM_PIL_RIVA) += pil-riva.o
 obj-$(CONFIG_MSM_PIL_TZAPPS) += pil-tzapps.o
 obj-$(CONFIG_MSM_PIL_VIDC) += pil-vidc.o
@@ -106,6 +114,7 @@
 ifndef CONFIG_ARCH_MSM8226
 ifndef CONFIG_ARCH_MSM9625
 ifndef CONFIG_ARCH_MPQ8092
+ifndef CONFIG_ARCH_MSM8910
 	obj-y += nand_partitions.o
 endif
 endif
@@ -114,6 +123,7 @@
 endif
 endif
 endif
+endif
 obj-$(CONFIG_MSM_SDIO_TTY) += sdio_tty.o
 obj-$(CONFIG_MSM_SMD_TTY) += smd_tty.o
 obj-$(CONFIG_MSM_SMD_QMI) += smd_qmi.o
@@ -197,16 +207,13 @@
 	obj-y += ramdump.o
 endif
 obj-$(CONFIG_MSM_SYSMON_COMM) += sysmon.o
-obj-$(CONFIG_MSM_MODEM_8960) += modem-8960.o
-obj-$(CONFIG_MSM_MODEM_SSR_8974) += modem-ssr-8974.o
-obj-$(CONFIG_MSM_LPASS_8960) += lpass-8960.o
-obj-$(CONFIG_MSM_ADSP_SSR_8974) += adsp-8974.o
 
 ifdef CONFIG_CPU_IDLE
 	obj-$(CONFIG_ARCH_APQ8064) += cpuidle.o
 	obj-$(CONFIG_ARCH_MSM8960) += cpuidle.o
 	obj-$(CONFIG_ARCH_MSM8X60) += cpuidle.o
 	obj-$(CONFIG_ARCH_MSM9615) += cpuidle.o
+	obj-$(CONFIG_ARCH_MSM9625) += cpuidle.o
 	obj-$(CONFIG_ARCH_MSM8974) += cpuidle.o
 endif
 
@@ -249,6 +256,7 @@
 obj-$(CONFIG_MACH_MSM8625_SURF) +=  board-msm7x27a.o board-7627a-all.o
 obj-$(CONFIG_MACH_MSM8625_EVB) +=  board-qrd7627a.o board-7627a-all.o
 obj-$(CONFIG_MACH_MSM8625_QRD7) +=  board-qrd7627a.o board-7627a-all.o
+obj-$(CONFIG_MACH_QRD_SKUD_PRIME) +=  board-qrd7627a.o board-7627a-all.o
 obj-$(CONFIG_MACH_MSM8625_FFA) += board-msm7x27a.o board-7627a-all.o
 obj-$(CONFIG_MACH_MSM8625_EVT) += board-msm7x27a.o board-7627a-all.o
 obj-$(CONFIG_ARCH_MSM7X30) += board-msm7x30.o devices-msm7x30.o memory_topology.o
@@ -280,15 +288,18 @@
 obj-$(CONFIG_MACH_MPQ8064_DTV) += board-8064-all.o board-8064-regulator.o
 obj-$(CONFIG_ARCH_MSM9615) += board-9615.o devices-9615.o board-9615-regulator.o board-9615-gpiomux.o board-9615-storage.o board-9615-display.o
 obj-$(CONFIG_ARCH_MSM9615) += clock-local.o clock-9615.o acpuclock-9615.o clock-rpm.o clock-pll.o
-obj-$(CONFIG_ARCH_MSM8974) += board-8974.o board-dt.o board-8974-gpiomux.o
+obj-$(CONFIG_ARCH_MSM8974) += board-8974.o board-8974-gpiomux.o
 obj-$(CONFIG_ARCH_MSM8974) += acpuclock-8974.o
 obj-$(CONFIG_ARCH_MSM8974) += clock-local2.o clock-pll.o clock-8974.o clock-rpm.o clock-voter.o clock-mdss-8974.o
 obj-$(CONFIG_ARCH_MSM8974) += gdsc.o
+obj-$(CONFIG_ARCH_MSM9625) += gdsc.o
 obj-$(CONFIG_ARCH_MSM8974) += krait-regulator.o
 obj-$(CONFIG_ARCH_MSM9625) += board-9625.o board-9625-gpiomux.o
+obj-$(CONFIG_ARCH_MSM9625) += clock-local2.o clock-pll.o clock-9625.o clock-rpm.o clock-voter.o acpuclock-9625.o
 obj-$(CONFIG_ARCH_MSM8930) += acpuclock-8930.o acpuclock-8627.o acpuclock-8930aa.o
 obj-$(CONFIG_ARCH_MPQ8092) += board-8092.o board-8092-gpiomux.o
 obj-$(CONFIG_ARCH_MSM8226) += board-8226.o board-8226-gpiomux.o
+obj-$(CONFIG_ARCH_MSM8910) += board-8910.o
 
 obj-$(CONFIG_MACH_SAPPHIRE) += board-sapphire.o board-sapphire-gpio.o
 obj-$(CONFIG_MACH_SAPPHIRE) += board-sapphire-keypad.o board-sapphire-panel.o
@@ -315,10 +326,11 @@
 endif
 ifdef CONFIG_MSM_RPM_SMD
 	obj-$(CONFIG_ARCH_MSM8974) += lpm_levels.o lpm_resources.o
+	obj-$(CONFIG_ARCH_MSM9625) += lpm_levels.o lpm_resources.o
 endif
 obj-$(CONFIG_MSM_MPM_OF) += mpm-of.o
 obj-$(CONFIG_MSM_MPM) += mpm.o
-obj-$(CONFIG_MSM_RPM_STATS_LOG) += rpm_stats.o
+obj-$(CONFIG_MSM_RPM_STATS_LOG) += rpm_stats.o rpm_master_stat.o
 obj-$(CONFIG_MSM_RPM_RBCPR_STATS_LOG) += rpm_rbcpr_stats.o
 obj-$(CONFIG_MSM_RPM_LOG) += rpm_log.o
 obj-$(CONFIG_MSM_TZ_LOG) += tz_log.o
@@ -346,9 +358,10 @@
 obj-$(CONFIG_ARCH_MSM9625) += gpiomux-v2.o gpiomux.o
 obj-$(CONFIG_ARCH_MPQ8092) += gpiomux-v2.o gpiomux.o
 obj-$(CONFIG_ARCH_MSM8226) += gpiomux-v2.o gpiomux.o
+obj-$(CONFIG_ARCH_MSM8910) += gpiomux-v2.o gpiomux.o
 
 obj-$(CONFIG_MSM_SLEEP_STATS_DEVICE) += idle_stats_device.o
-obj-$(CONFIG_MSM_DCVS) += msm_dcvs_scm.o msm_dcvs.o msm_dcvs_idle.o msm_mpdecision.o
+obj-$(CONFIG_MSM_DCVS) += msm_dcvs_scm.o msm_dcvs.o msm_mpdecision.o
 obj-$(CONFIG_MSM_RUN_QUEUE_STATS) += msm_rq_stats.o
 obj-$(CONFIG_MSM_SHOW_RESUME_IRQ) += msm_show_resume_irq.o
 obj-$(CONFIG_BT_MSM_PINTEST)  += btpintest.o
@@ -386,3 +399,7 @@
 ifdef CONFIG_MSM_CPR
 obj-$(CONFIG_DEBUG_FS) += msm_cpr-debug.o
 endif
+obj-$(CONFIG_MSM_FIQ) += msm7k_fiq.o
+obj-$(CONFIG_MSM_FIQ) += msm7k_fiq_handler.o
+
+obj-$(CONFIG_MEMORY_HOLE_CARVEOUT) +=  msm_mem_hole.o
diff --git a/arch/arm/mach-msm/Makefile.boot b/arch/arm/mach-msm/Makefile.boot
index 9234b2c..cf1f401 100644
--- a/arch/arm/mach-msm/Makefile.boot
+++ b/arch/arm/mach-msm/Makefile.boot
@@ -65,3 +65,5 @@
 # MPQ8092
    zreladdr-$(CONFIG_ARCH_MPQ8092)	:= 0x00008000
 
+# MSM8910
+   zreladdr-$(CONFIG_ARCH_MSM8910)	:= 0x00008000
diff --git a/arch/arm/mach-msm/acpuclock-7627.c b/arch/arm/mach-msm/acpuclock-7627.c
index 5c4a923..dd27123 100644
--- a/arch/arm/mach-msm/acpuclock-7627.c
+++ b/arch/arm/mach-msm/acpuclock-7627.c
@@ -85,10 +85,12 @@
 };
 
 static struct pll_config pll4_cfg_tbl[] = {
-	{  36, 1, 2 }, /*  700.8 MHz */
-	{  52, 1, 2 }, /* 1008 MHz */
-	{  63, 0, 1 }, /* 1209.6 MHz */
-	{  73, 0, 1 }, /* 1401.6 MHz */
+	[0] = {  36, 1, 2 }, /*  700.8 MHz */
+	[1] = {  52, 1, 2 }, /* 1008 MHz */
+	[2] = {  63, 0, 1 }, /* 1209.6 MHz */
+	[3] = {  73, 0, 1 }, /* 1401.6 MHz */
+	[4] = {  60, 0, 1 }, /* 1152 MHz */
+	[5] = {  57, 1, 2 }, /* 1104 MHz */
 };
 
 struct clock_state {
@@ -266,8 +268,8 @@
 static struct clkctl_acpu_speed pll0_960_pll1_196_pll2_1200_pll4_1209[] = {
 	{ 0, 19200, ACPU_PLL_TCXO, 0, 0, 2400, 3, 0, 24576 },
 	{ 0, 65536, ACPU_PLL_1, 1, 3,  8192, 3, 1, 49152 },
-	{ 0, 98304, ACPU_PLL_1, 1, 1,  12288, 3, 2, 49152 },
-	{ 1, 196608, ACPU_PLL_1, 1, 0, 24576, 3, 3, 98304 },
+	{ 0, 98304, ACPU_PLL_1, 1, 1,  12288, 3, 1, 49152 },
+	{ 1, 196608, ACPU_PLL_1, 1, 0, 24576, 3, 1, 98304 },
 	{ 1, 320000, ACPU_PLL_0, 4, 2, 40000, 3, 2, 122880 },
 	{ 1, 480000, ACPU_PLL_0, 4, 1, 60000, 3, 3, 122880 },
 	{ 0, 600000, ACPU_PLL_2, 2, 1, 75000, 3, 4, 160000 },
@@ -298,8 +300,8 @@
 static struct clkctl_acpu_speed pll0_960_pll1_196_pll2_1200_pll4_1401[] = {
 	{ 0, 19200, ACPU_PLL_TCXO, 0, 0, 2400, 3, 0, 24576 },
 	{ 0, 65536, ACPU_PLL_1, 1, 3,  8192, 3, 1, 49152 },
-	{ 0, 98304, ACPU_PLL_1, 1, 1,  12288, 3, 2, 49152 },
-	{ 1, 196608, ACPU_PLL_1, 1, 0, 24576, 3, 3, 98304 },
+	{ 0, 98304, ACPU_PLL_1, 1, 1,  12288, 3, 1, 49152 },
+	{ 1, 196608, ACPU_PLL_1, 1, 0, 24576, 3, 1, 98304 },
 	{ 1, 320000, ACPU_PLL_0, 4, 2, 40000, 3, 2, 122880 },
 	{ 1, 480000, ACPU_PLL_0, 4, 1, 60000, 3, 3, 122880 },
 	{ 0, 600000, ACPU_PLL_2, 2, 1, 75000, 3, 4, 160000 },
@@ -310,7 +312,7 @@
 	{ 0 }
 };
 
-/* 8625v2.0 PLL4 @ 1008MHz with GSM capable modem */
+/* 8625 PLL4 @ 1008MHz with GSM capable modem */
 static struct clkctl_acpu_speed pll0_960_pll1_245_pll2_1200_pll4_1008_2p0[] = {
 	{ 0, 19200, ACPU_PLL_TCXO, 0, 0, 2400, 3, 0, 30720 },
 	{ 0, 61440, ACPU_PLL_1, 1, 3,  7680, 3, 0, 61440 },
@@ -325,12 +327,12 @@
 	{ 0 }
 };
 
-/* 8625v2.0 PLL4 @ 1008MHz with CDMA capable modem */
+/* 8625 PLL4 @ 1008MHz with CDMA capable modem */
 static struct clkctl_acpu_speed pll0_960_pll1_196_pll2_1200_pll4_1008_2p0[] = {
 	{ 0, 19200, ACPU_PLL_TCXO, 0, 0, 2400, 3, 0, 24576 },
 	{ 0, 65536, ACPU_PLL_1, 1, 3,  8192, 3, 1, 49152 },
-	{ 0, 98304, ACPU_PLL_1, 1, 1,  12288, 3, 2, 49152 },
-	{ 1, 196608, ACPU_PLL_1, 1, 0, 24576, 3, 3, 98304 },
+	{ 0, 98304, ACPU_PLL_1, 1, 1,  12288, 3, 1, 49152 },
+	{ 1, 196608, ACPU_PLL_1, 1, 0, 24576, 3, 1, 98304 },
 	{ 1, 320000, ACPU_PLL_0, 4, 2, 40000, 3, 2, 122880 },
 	{ 1, 480000, ACPU_PLL_0, 4, 1, 60000, 3, 3, 122880 },
 	{ 0, 600000, ACPU_PLL_2, 2, 1, 75000, 3, 4, 160000 },
@@ -339,35 +341,58 @@
 	{ 0 }
 };
 
-/* 8625 PLL4 @ 1152MHz with GSM capable modem */
+/* 8625 PLL4 @ 1104MHz with GSM capable modem with v2.0 plan */
+static struct clkctl_acpu_speed pll0_960_pll1_245_pll2_1200_pll4_1104[] = {
+	{ 0, 19200, ACPU_PLL_TCXO, 0, 0, 2400, 3, 0, 30720 },
+	{ 1, 245760, ACPU_PLL_1, 1, 0, 30720, 3, 1, 61440 },
+	{ 1, 320000, ACPU_PLL_0, 4, 2, 40000, 3, 2, 122880 },
+	{ 1, 480000, ACPU_PLL_0, 4, 1, 60000, 3, 3, 122880 },
+	{ 0, 600000, ACPU_PLL_2, 2, 1, 75000, 3, 4, 160000 },
+	{ 1, 700800, ACPU_PLL_4, 6, 0, 87500, 3, 4, 160000, &pll4_cfg_tbl[0]},
+	{ 1, 1008000, ACPU_PLL_4, 6, 0, 126000, 3, 5, 200000, &pll4_cfg_tbl[1]},
+	{ 1, 1104000, ACPU_PLL_4, 6, 0, 151200, 3, 6, 200000, &pll4_cfg_tbl[5]},
+	{ 0 }
+};
+
+/* 8625 PLL4 @ 1104MHz with CDMA capable modem with v2.0 plan */
+static struct clkctl_acpu_speed pll0_960_pll1_196_pll2_1200_pll4_1104[] = {
+	{ 0, 19200, ACPU_PLL_TCXO, 0, 0, 2400, 3, 0, 24576 },
+	{ 1, 196608, ACPU_PLL_1, 1, 0, 24576, 3, 1, 98304 },
+	{ 1, 320000, ACPU_PLL_0, 4, 2, 40000, 3, 2, 122880 },
+	{ 1, 480000, ACPU_PLL_0, 4, 1, 60000, 3, 3, 122880 },
+	{ 0, 600000, ACPU_PLL_2, 2, 1, 75000, 3, 4, 160000 },
+	{ 1, 700800, ACPU_PLL_4, 6, 0, 87500, 3, 4, 160000, &pll4_cfg_tbl[0]},
+	{ 1, 1008000, ACPU_PLL_4, 6, 0, 126000, 3, 5, 200000, &pll4_cfg_tbl[1]},
+	{ 1, 1104000, ACPU_PLL_4, 6, 0, 151200, 3, 6, 200000, &pll4_cfg_tbl[5]},
+	{ 0 }
+};
+
+/* 8625 PLL4 @ 1152MHz with GSM capable modem with v2.0 plan */
 static struct clkctl_acpu_speed pll0_960_pll1_245_pll2_1200_pll4_1152[] = {
 	{ 0, 19200, ACPU_PLL_TCXO, 0, 0, 2400, 3, 0, 30720 },
-	{ 0, 61440, ACPU_PLL_1, 1, 3,  7680, 3, 1, 61440 },
-	{ 1, 122880, ACPU_PLL_1, 1, 1,  15360, 3, 2, 61440 },
-	{ 1, 245760, ACPU_PLL_1, 1, 0, 30720, 3, 3, 61440 },
-	{ 1, 320000, ACPU_PLL_0, 4, 2, 40000, 3, 4, 122880 },
-	{ 1, 480000, ACPU_PLL_0, 4, 1, 60000, 3, 5, 122880 },
-	{ 0, 576000, ACPU_PLL_4, 6, 1, 72000, 3, 6, 160000 },
-	{ 1, 600000, ACPU_PLL_2, 2, 1, 75000, 3, 6, 160000 },
-	{ 1, 1152000, ACPU_PLL_4, 6, 0, 144000, 3, 7, 200000},
+	{ 1, 245760, ACPU_PLL_1, 1, 0, 30720, 3, 1, 61440 },
+	{ 1, 320000, ACPU_PLL_0, 4, 2, 40000, 3, 2, 122880 },
+	{ 1, 480000, ACPU_PLL_0, 4, 1, 60000, 3, 3, 122880 },
+	{ 0, 600000, ACPU_PLL_2, 2, 1, 75000, 3, 4, 160000 },
+	{ 1, 700800, ACPU_PLL_4, 6, 0, 87500, 3, 4, 160000, &pll4_cfg_tbl[0]},
+	{ 1, 1008000, ACPU_PLL_4, 6, 0, 126000, 3, 5, 200000, &pll4_cfg_tbl[1]},
+	{ 1, 1152000, ACPU_PLL_4, 6, 0, 151200, 3, 6, 200000, &pll4_cfg_tbl[4]},
 	{ 0 }
 };
 
-/* 8625 PLL4 @ 1115MHz with CDMA capable modem */
+/* 8625 PLL4 @ 1115MHz with CDMA capable modem with v2.0 plan */
 static struct clkctl_acpu_speed pll0_960_pll1_196_pll2_1200_pll4_1152[] = {
 	{ 0, 19200, ACPU_PLL_TCXO, 0, 0, 2400, 3, 0, 24576 },
-	{ 0, 65536, ACPU_PLL_1, 1, 3,  8192, 3, 1, 49152 },
-	{ 1, 98304, ACPU_PLL_1, 1, 1,  12288, 3, 2, 49152 },
-	{ 1, 196608, ACPU_PLL_1, 1, 0, 24576, 3, 3, 98304 },
-	{ 1, 320000, ACPU_PLL_0, 4, 2, 40000, 3, 4, 122880 },
-	{ 1, 480000, ACPU_PLL_0, 4, 1, 60000, 3, 5, 122880 },
-	{ 0, 576000, ACPU_PLL_4, 6, 1, 72000, 3, 6, 160000 },
-	{ 1, 600000, ACPU_PLL_2, 2, 1, 75000, 3, 6, 160000 },
-	{ 1, 1152000, ACPU_PLL_4, 6, 0, 144000, 3, 7, 200000},
+	{ 1, 196608, ACPU_PLL_1, 1, 0, 24576, 3, 1, 98304 },
+	{ 1, 320000, ACPU_PLL_0, 4, 2, 40000, 3, 2, 122880 },
+	{ 1, 480000, ACPU_PLL_0, 4, 1, 60000, 3, 3, 122880 },
+	{ 0, 600000, ACPU_PLL_2, 2, 1, 75000, 3, 4, 160000 },
+	{ 1, 700800, ACPU_PLL_4, 6, 0, 87500, 3, 4, 160000, &pll4_cfg_tbl[0]},
+	{ 1, 1008000, ACPU_PLL_4, 6, 0, 126000, 3, 5, 200000, &pll4_cfg_tbl[1]},
+	{ 1, 1152000, ACPU_PLL_4, 6, 0, 151200, 3, 6, 200000, &pll4_cfg_tbl[4]},
 	{ 0 }
 };
 
-
 /* 7625a PLL2 @ 1200MHz with GSM capable modem */
 static struct clkctl_acpu_speed pll0_960_pll1_245_pll2_1200_25a[] = {
 	{ 0, 19200, ACPU_PLL_TCXO, 0, 0, 2400, 3, 0, 30720 },
@@ -486,6 +511,8 @@
 	PLL_CONFIG(960, 196, 1200, 1209),
 	PLL_CONFIG(960, 245, 1200, 1152),
 	PLL_CONFIG(960, 196, 1200, 1152),
+	PLL_CONFIG(960, 245, 1200, 1104),
+	PLL_CONFIG(960, 196, 1200, 1104),
 	PLL_CONFIG(960, 245, 1200, 1401),
 	PLL_CONFIG(960, 196, 1200, 1401),
 	{ 0, 0, 0, 0, 0 }
@@ -754,7 +781,7 @@
 		acpuclk_set_div(tgt_s);
 		drv_state.current_speed = tgt_s;
 		/* Re-adjust lpj for the new clock speed. */
-		update_jiffies(cpu, cur_s->lpj);
+		update_jiffies(cpu, tgt_s->lpj);
 
 		/* Disable the backup PLL */
 		if ((delta > drv_state.max_speed_delta_khz)
@@ -987,14 +1014,10 @@
 
 	/*
 	 * 1008Mhz table selection based on the Lvalue of the PLL
-	 * is conflicting with the 7627AA and 8625 v1.0 1GHz parts
-	 * since v2.0 8625 chips are using different clock plan based
-	 * reprogramming method.
+	 * is conflicting with the 7627AA 1GHz parts since 8625 chips
+	 * are using different clock plan based reprogramming method.
 	 */
-	if (cpu_is_msm8625() &&
-		(SOCINFO_VERSION_MAJOR(socinfo_get_version()) >= 2) &&
-		pll_mhz[ACPU_PLL_4] == 1008) {
-
+	if (cpu_is_msm8625() &&	pll_mhz[ACPU_PLL_4] == 1008) {
 		if (pll_mhz[ACPU_PLL_2] == 245)
 			acpu_freq_tbl =
 				pll0_960_pll1_245_pll2_1200_pll4_1008_2p0;
diff --git a/arch/arm/mach-msm/acpuclock-8064.c b/arch/arm/mach-msm/acpuclock-8064.c
index d10211bc..cda952f 100644
--- a/arch/arm/mach-msm/acpuclock-8064.c
+++ b/arch/arm/mach-msm/acpuclock-8064.c
@@ -116,7 +116,7 @@
 };
 
 static struct l2_level l2_freq_tbl[] __initdata = {
-	[0]  = { {  384000, PLL_8, 0, 0x00 }, 1050000, 1050000, 1 },
+	[0]  = { {  384000, PLL_8, 0, 0x00 },  950000, 1050000, 1 },
 	[1]  = { {  432000, HFPLL, 2, 0x20 }, 1050000, 1050000, 2 },
 	[2]  = { {  486000, HFPLL, 2, 0x24 }, 1050000, 1050000, 2 },
 	[3]  = { {  540000, HFPLL, 2, 0x28 }, 1050000, 1050000, 2 },
@@ -132,8 +132,6 @@
 	[13] = { { 1080000, HFPLL, 1, 0x28 }, 1150000, 1150000, 5 },
 	[14] = { { 1134000, HFPLL, 1, 0x2A }, 1150000, 1150000, 5 },
 	[15] = { { 1188000, HFPLL, 1, 0x2C }, 1150000, 1150000, 5 },
-	/* L2 Level 16 is for 8064ab only */
-	[16] = { { 1242000, HFPLL, 1, 0x2E }, 1150000, 1150000, 5 },
 	{ }
 };
 
@@ -215,27 +213,17 @@
 	{ 0, { 0 } }
 };
 
-static struct acpu_level tbl_slow_1p7[] __initdata = {
+static struct acpu_level tbl_PVS0_1700MHz[] __initdata = {
 	{ 1, {   384000, PLL_8, 0, 0x00 }, L2(0),   950000 },
-	{ 0, {   432000, HFPLL, 2, 0x20 }, L2(6),   975000 },
 	{ 1, {   486000, HFPLL, 2, 0x24 }, L2(6),   975000 },
-	{ 0, {   540000, HFPLL, 2, 0x28 }, L2(6),  1000000 },
 	{ 1, {   594000, HFPLL, 1, 0x16 }, L2(6),  1000000 },
-	{ 0, {   648000, HFPLL, 1, 0x18 }, L2(6),  1025000 },
 	{ 1, {   702000, HFPLL, 1, 0x1A }, L2(6),  1025000 },
-	{ 0, {   756000, HFPLL, 1, 0x1C }, L2(6),  1075000 },
 	{ 1, {   810000, HFPLL, 1, 0x1E }, L2(6),  1075000 },
-	{ 0, {   864000, HFPLL, 1, 0x20 }, L2(6),  1100000 },
 	{ 1, {   918000, HFPLL, 1, 0x22 }, L2(6),  1100000 },
-	{ 0, {   972000, HFPLL, 1, 0x24 }, L2(6),  1125000 },
 	{ 1, {  1026000, HFPLL, 1, 0x26 }, L2(6),  1125000 },
-	{ 0, {  1080000, HFPLL, 1, 0x28 }, L2(15), 1175000 },
 	{ 1, {  1134000, HFPLL, 1, 0x2A }, L2(15), 1175000 },
-	{ 0, {  1188000, HFPLL, 1, 0x2C }, L2(15), 1200000 },
 	{ 1, {  1242000, HFPLL, 1, 0x2E }, L2(15), 1200000 },
-	{ 0, {  1296000, HFPLL, 1, 0x30 }, L2(15), 1225000 },
 	{ 1, {  1350000, HFPLL, 1, 0x32 }, L2(15), 1225000 },
-	{ 0, {  1404000, HFPLL, 1, 0x34 }, L2(15), 1237500 },
 	{ 1, {  1458000, HFPLL, 1, 0x36 }, L2(15), 1237500 },
 	{ 1, {  1512000, HFPLL, 1, 0x38 }, L2(15), 1250000 },
 	{ 1, {  1566000, HFPLL, 1, 0x3A }, L2(15), 1250000 },
@@ -244,38 +232,136 @@
 	{ 0, { 0 } }
 };
 
-static struct acpu_level tbl_slow_2p0[] __initdata = {
-	{ 1, {   384000, PLL_8, 0, 0x00 }, L2(0),   950000 },
-	{ 0, {   432000, HFPLL, 2, 0x20 }, L2(6),   975000 },
-	{ 1, {   486000, HFPLL, 2, 0x24 }, L2(6),   975000 },
-	{ 0, {   540000, HFPLL, 2, 0x28 }, L2(6),  1000000 },
-	{ 1, {   594000, HFPLL, 1, 0x16 }, L2(6),  1000000 },
-	{ 0, {   648000, HFPLL, 1, 0x18 }, L2(6),  1025000 },
-	{ 1, {   702000, HFPLL, 1, 0x1A }, L2(6),  1025000 },
-	{ 0, {   756000, HFPLL, 1, 0x1C }, L2(6),  1075000 },
-	{ 1, {   810000, HFPLL, 1, 0x1E }, L2(6),  1075000 },
-	{ 0, {   864000, HFPLL, 1, 0x20 }, L2(6),  1100000 },
-	{ 1, {   918000, HFPLL, 1, 0x22 }, L2(6),  1100000 },
-	{ 0, {   972000, HFPLL, 1, 0x24 }, L2(6),  1125000 },
-	{ 1, {  1026000, HFPLL, 1, 0x26 }, L2(6),  1125000 },
-	{ 0, {  1080000, HFPLL, 1, 0x28 }, L2(15), 1175000 },
-	{ 1, {  1134000, HFPLL, 1, 0x2A }, L2(15), 1175000 },
-	{ 0, {  1188000, HFPLL, 1, 0x2C }, L2(15), 1200000 },
-	{ 1, {  1242000, HFPLL, 1, 0x2E }, L2(15), 1200000 },
-	{ 0, {  1296000, HFPLL, 1, 0x30 }, L2(15), 1225000 },
-	{ 1, {  1350000, HFPLL, 1, 0x32 }, L2(15), 1225000 },
-	{ 0, {  1404000, HFPLL, 1, 0x34 }, L2(15), 1237500 },
-	{ 1, {  1458000, HFPLL, 1, 0x36 }, L2(15), 1237500 },
-	{ 1, {  1512000, HFPLL, 1, 0x38 }, L2(15), 1250000 },
-	{ 1, {  1566000, HFPLL, 1, 0x3A }, L2(15), 1250000 },
-	{ 1, {  1620000, HFPLL, 1, 0x3C }, L2(15), 1250000 },
-	{ 1, {  1674000, HFPLL, 1, 0x3E }, L2(15), 1250000 },
-	{ 1, {  1728000, HFPLL, 1, 0x40 }, L2(15), 1250000 },
-	{ 1, {  1782000, HFPLL, 1, 0x42 }, L2(15), 1250000 },
-	{ 1, {  1836000, HFPLL, 1, 0x44 }, L2(15), 1250000 },
+static struct acpu_level tbl_PVS0_2000MHz[] __initdata = {
+	{ 1, {   384000, PLL_8, 0, 0x00 }, L2(0),   900000 },
+	{ 1, {   486000, HFPLL, 2, 0x24 }, L2(6),   900000 },
+	{ 1, {   594000, HFPLL, 1, 0x16 }, L2(6),   900000 },
+	{ 1, {   702000, HFPLL, 1, 0x1A }, L2(6),   900000 },
+	{ 1, {   810000, HFPLL, 1, 0x1E }, L2(6),   912500 },
+	{ 1, {   918000, HFPLL, 1, 0x22 }, L2(6),   962500 },
+	{ 1, {  1026000, HFPLL, 1, 0x26 }, L2(6),   987500 },
+	{ 1, {  1134000, HFPLL, 1, 0x2A }, L2(15), 1012500 },
+	{ 1, {  1242000, HFPLL, 1, 0x2E }, L2(15), 1025000 },
+	{ 1, {  1350000, HFPLL, 1, 0x32 }, L2(15), 1075000 },
+	{ 1, {  1458000, HFPLL, 1, 0x36 }, L2(15), 1112500 },
+	{ 1, {  1566000, HFPLL, 1, 0x3A }, L2(15), 1150000 },
+	{ 1, {  1674000, HFPLL, 1, 0x3E }, L2(15), 1200000 },
+	{ 1, {  1782000, HFPLL, 1, 0x42 }, L2(15), 1262500 },
+	{ 1, {  1890000, HFPLL, 1, 0x46 }, L2(15), 1300000 },
+	{ 0, { 0 } }
+};
+
+static struct acpu_level tbl_PVS1_2000MHz[] __initdata = {
+	{ 1, {   384000, PLL_8, 0, 0x00 }, L2(0),   900000 },
+	{ 1, {   486000, HFPLL, 2, 0x24 }, L2(6),   900000 },
+	{ 1, {   594000, HFPLL, 1, 0x16 }, L2(6),   900000 },
+	{ 1, {   702000, HFPLL, 1, 0x1A }, L2(6),   900000 },
+	{ 1, {   810000, HFPLL, 1, 0x1E }, L2(6),   900000 },
+	{ 1, {   918000, HFPLL, 1, 0x22 }, L2(6),   962500 },
+	{ 1, {  1026000, HFPLL, 1, 0x26 }, L2(6),   987500 },
+	{ 1, {  1134000, HFPLL, 1, 0x2A }, L2(15), 1000000 },
+	{ 1, {  1242000, HFPLL, 1, 0x2E }, L2(15), 1012500 },
+	{ 1, {  1350000, HFPLL, 1, 0x32 }, L2(15), 1062500 },
+	{ 1, {  1458000, HFPLL, 1, 0x36 }, L2(15), 1087500 },
+	{ 1, {  1566000, HFPLL, 1, 0x3A }, L2(15), 1125000 },
+	{ 1, {  1674000, HFPLL, 1, 0x3E }, L2(15), 1187500 },
+	{ 1, {  1782000, HFPLL, 1, 0x42 }, L2(15), 1237500 },
+	{ 1, {  1890000, HFPLL, 1, 0x46 }, L2(15), 1275000 },
+	{ 0, { 0 } }
+};
+
+static struct acpu_level tbl_PVS2_2000MHz[] __initdata = {
+	{ 1, {   384000, PLL_8, 0, 0x00 }, L2(0),   900000 },
+	{ 1, {   486000, HFPLL, 2, 0x24 }, L2(6),   900000 },
+	{ 1, {   594000, HFPLL, 1, 0x16 }, L2(6),   900000 },
+	{ 1, {   702000, HFPLL, 1, 0x1A }, L2(6),   900000 },
+	{ 1, {   810000, HFPLL, 1, 0x1E }, L2(6),   900000 },
+	{ 1, {   918000, HFPLL, 1, 0x22 }, L2(6),   950000 },
+	{ 1, {  1026000, HFPLL, 1, 0x26 }, L2(6),   975000 },
+	{ 1, {  1134000, HFPLL, 1, 0x2A }, L2(15),  987500 },
+	{ 1, {  1242000, HFPLL, 1, 0x2E }, L2(15), 1000000 },
+	{ 1, {  1350000, HFPLL, 1, 0x32 }, L2(15), 1050000 },
+	{ 1, {  1458000, HFPLL, 1, 0x36 }, L2(15), 1075000 },
+	{ 1, {  1566000, HFPLL, 1, 0x3A }, L2(15), 1112500 },
+	{ 1, {  1674000, HFPLL, 1, 0x3E }, L2(15), 1162500 },
+	{ 1, {  1782000, HFPLL, 1, 0x42 }, L2(15), 1212500 },
 	{ 1, {  1890000, HFPLL, 1, 0x46 }, L2(15), 1250000 },
-	{ 1, {  1944000, HFPLL, 1, 0x48 }, L2(15), 1250000 },
-	{ 1, {  1998000, HFPLL, 1, 0x4A }, L2(15), 1250000 },
+	{ 0, { 0 } }
+};
+
+static struct acpu_level tbl_PVS3_2000MHz[] __initdata = {
+	{ 1, {   384000, PLL_8, 0, 0x00 }, L2(0),   900000 },
+	{ 1, {   486000, HFPLL, 2, 0x24 }, L2(6),   900000 },
+	{ 1, {   594000, HFPLL, 1, 0x16 }, L2(6),   900000 },
+	{ 1, {   702000, HFPLL, 1, 0x1A }, L2(6),   900000 },
+	{ 1, {   810000, HFPLL, 1, 0x1E }, L2(6),   900000 },
+	{ 1, {   918000, HFPLL, 1, 0x22 }, L2(6),   925000 },
+	{ 1, {  1026000, HFPLL, 1, 0x26 }, L2(6),   950000 },
+	{ 1, {  1134000, HFPLL, 1, 0x2A }, L2(15),  962500 },
+	{ 1, {  1242000, HFPLL, 1, 0x2E }, L2(15),  975000 },
+	{ 1, {  1350000, HFPLL, 1, 0x32 }, L2(15), 1012500 },
+	{ 1, {  1458000, HFPLL, 1, 0x36 }, L2(15), 1037500 },
+	{ 1, {  1566000, HFPLL, 1, 0x3A }, L2(15), 1075000 },
+	{ 1, {  1674000, HFPLL, 1, 0x3E }, L2(15), 1112500 },
+	{ 1, {  1782000, HFPLL, 1, 0x42 }, L2(15), 1162500 },
+	{ 1, {  1890000, HFPLL, 1, 0x46 }, L2(15), 1200000 },
+	{ 0, { 0 } }
+};
+
+static struct acpu_level tbl_PVS4_2000MHz[] __initdata = {
+	{ 1, {   384000, PLL_8, 0, 0x00 }, L2(0),   900000 },
+	{ 1, {   486000, HFPLL, 2, 0x24 }, L2(6),   900000 },
+	{ 1, {   594000, HFPLL, 1, 0x16 }, L2(6),   900000 },
+	{ 1, {   702000, HFPLL, 1, 0x1A }, L2(6),   900000 },
+	{ 1, {   810000, HFPLL, 1, 0x1E }, L2(6),   900000 },
+	{ 1, {   918000, HFPLL, 1, 0x22 }, L2(6),   900000 },
+	{ 1, {  1026000, HFPLL, 1, 0x26 }, L2(6),   925000 },
+	{ 1, {  1134000, HFPLL, 1, 0x2A }, L2(15),  937500 },
+	{ 1, {  1242000, HFPLL, 1, 0x2E }, L2(15),  950000 },
+	{ 1, {  1350000, HFPLL, 1, 0x32 }, L2(15),  975000 },
+	{ 1, {  1458000, HFPLL, 1, 0x36 }, L2(15), 1000000 },
+	{ 1, {  1566000, HFPLL, 1, 0x3A }, L2(15), 1037500 },
+	{ 1, {  1674000, HFPLL, 1, 0x3E }, L2(15), 1062500 },
+	{ 1, {  1782000, HFPLL, 1, 0x42 }, L2(15), 1112500 },
+	{ 1, {  1890000, HFPLL, 1, 0x46 }, L2(15), 1150000 },
+	{ 0, { 0 } }
+};
+
+static struct acpu_level tbl_PVS5_2000MHz[] __initdata = {
+	{ 1, {   384000, PLL_8, 0, 0x00 }, L2(0),   900000 },
+	{ 1, {   486000, HFPLL, 2, 0x24 }, L2(6),   900000 },
+	{ 1, {   594000, HFPLL, 1, 0x16 }, L2(6),   900000 },
+	{ 1, {   702000, HFPLL, 1, 0x1A }, L2(6),   900000 },
+	{ 1, {   810000, HFPLL, 1, 0x1E }, L2(6),   900000 },
+	{ 1, {   918000, HFPLL, 1, 0x22 }, L2(6),   900000 },
+	{ 1, {  1026000, HFPLL, 1, 0x26 }, L2(6),   925000 },
+	{ 1, {  1134000, HFPLL, 1, 0x2A }, L2(15),  937500 },
+	{ 1, {  1242000, HFPLL, 1, 0x2E }, L2(15),  950000 },
+	{ 1, {  1350000, HFPLL, 1, 0x32 }, L2(15),  962500 },
+	{ 1, {  1458000, HFPLL, 1, 0x36 }, L2(15),  987500 },
+	{ 1, {  1566000, HFPLL, 1, 0x3A }, L2(15), 1012500 },
+	{ 1, {  1674000, HFPLL, 1, 0x3E }, L2(15), 1037500 },
+	{ 1, {  1782000, HFPLL, 1, 0x42 }, L2(15), 1087500 },
+	{ 1, {  1890000, HFPLL, 1, 0x46 }, L2(15), 1125000 },
+	{ 0, { 0 } }
+};
+
+static struct acpu_level tbl_PVS6_2000MHz[] __initdata = {
+	{ 1, {   384000, PLL_8, 0, 0x00 }, L2(0),   900000 },
+	{ 1, {   486000, HFPLL, 2, 0x24 }, L2(6),   900000 },
+	{ 1, {   594000, HFPLL, 1, 0x16 }, L2(6),   900000 },
+	{ 1, {   702000, HFPLL, 1, 0x1A }, L2(6),   900000 },
+	{ 1, {   810000, HFPLL, 1, 0x1E }, L2(6),   900000 },
+	{ 1, {   918000, HFPLL, 1, 0x22 }, L2(6),   900000 },
+	{ 1, {  1026000, HFPLL, 1, 0x26 }, L2(6),   925000 },
+	{ 1, {  1134000, HFPLL, 1, 0x2A }, L2(15),  937500 },
+	{ 1, {  1242000, HFPLL, 1, 0x2E }, L2(15),  950000 },
+	{ 1, {  1350000, HFPLL, 1, 0x32 }, L2(15),  962500 },
+	{ 1, {  1458000, HFPLL, 1, 0x36 }, L2(15),  975000 },
+	{ 1, {  1566000, HFPLL, 1, 0x3A }, L2(15), 1000000 },
+	{ 1, {  1674000, HFPLL, 1, 0x3E }, L2(15), 1025000 },
+	{ 1, {  1782000, HFPLL, 1, 0x42 }, L2(15), 1062500 },
+	{ 1, {  1890000, HFPLL, 1, 0x46 }, L2(15), 1100000 },
 	{ 0, { 0 } }
 };
 
@@ -285,21 +371,21 @@
 	[0][PVS_FAST]    = {tbl_fast, sizeof(tbl_fast), 25000 },
 	[0][PVS_FASTER]  = {tbl_fast, sizeof(tbl_fast), 25000 },
 
-	[1][0] = { tbl_slow_1p7, sizeof(tbl_slow_1p7),     0 },
-	[1][1] = { tbl_slow_1p7, sizeof(tbl_slow_1p7),     0 },
-	[1][2] = { tbl_slow_1p7, sizeof(tbl_slow_1p7),     0 },
-	[1][3] = { tbl_slow_1p7, sizeof(tbl_slow_1p7),     0 },
-	[1][4] = { tbl_slow_1p7, sizeof(tbl_slow_1p7),     0 },
-	[1][5] = { tbl_slow_1p7, sizeof(tbl_slow_1p7),     0 },
-	[1][6] = { tbl_slow_1p7, sizeof(tbl_slow_1p7),     0 },
+	[1][0] = { tbl_PVS0_1700MHz, sizeof(tbl_PVS0_1700MHz),     0 },
+	[1][1] = { tbl_PVS0_1700MHz, sizeof(tbl_PVS0_1700MHz),     0 },
+	[1][2] = { tbl_PVS0_1700MHz, sizeof(tbl_PVS0_1700MHz),     0 },
+	[1][3] = { tbl_PVS0_1700MHz, sizeof(tbl_PVS0_1700MHz),     0 },
+	[1][4] = { tbl_PVS0_1700MHz, sizeof(tbl_PVS0_1700MHz),     0 },
+	[1][5] = { tbl_PVS0_1700MHz, sizeof(tbl_PVS0_1700MHz),     0 },
+	[1][6] = { tbl_PVS0_1700MHz, sizeof(tbl_PVS0_1700MHz),     0 },
 
-	[2][0] = { tbl_slow_2p0, sizeof(tbl_slow_2p0),     0 },
-	[2][1] = { tbl_slow_2p0, sizeof(tbl_slow_2p0),     0 },
-	[2][2] = { tbl_slow_2p0, sizeof(tbl_slow_2p0),     0 },
-	[2][3] = { tbl_slow_2p0, sizeof(tbl_slow_2p0),     0 },
-	[2][4] = { tbl_slow_2p0, sizeof(tbl_slow_2p0),     0 },
-	[2][5] = { tbl_slow_2p0, sizeof(tbl_slow_2p0),     0 },
-	[2][6] = { tbl_slow_2p0, sizeof(tbl_slow_2p0),     0 },
+	[2][0] = { tbl_PVS0_2000MHz, sizeof(tbl_PVS0_2000MHz),     0 },
+	[2][1] = { tbl_PVS1_2000MHz, sizeof(tbl_PVS1_2000MHz),     0 },
+	[2][2] = { tbl_PVS2_2000MHz, sizeof(tbl_PVS2_2000MHz),     0 },
+	[2][3] = { tbl_PVS3_2000MHz, sizeof(tbl_PVS3_2000MHz),     0 },
+	[2][4] = { tbl_PVS4_2000MHz, sizeof(tbl_PVS4_2000MHz),     0 },
+	[2][5] = { tbl_PVS5_2000MHz, sizeof(tbl_PVS5_2000MHz),     0 },
+	[2][6] = { tbl_PVS6_2000MHz, sizeof(tbl_PVS6_2000MHz),     0 },
 };
 
 static struct acpuclk_krait_params acpuclk_8064_params __initdata = {
diff --git a/arch/arm/mach-msm/acpuclock-8974.c b/arch/arm/mach-msm/acpuclock-8974.c
index 098f854..61213cf 100644
--- a/arch/arm/mach-msm/acpuclock-8974.c
+++ b/arch/arm/mach-msm/acpuclock-8974.c
@@ -143,7 +143,7 @@
 };
 
 static struct acpu_level acpu_freq_tbl[] __initdata = {
-	{ 1, {  300000, PLL_0, 0,   0 }, L2(0),   950000, 3200000 },
+	{ 1, {  300000, PLL_0, 0,   0 }, L2(0),   950000,  100000 },
 	{ 1, {  384000, HFPLL, 2,  40 }, L2(3),   950000, 3200000 },
 	{ 1, {  460800, HFPLL, 2,  48 }, L2(3),   950000, 3200000 },
 	{ 1, {  537600, HFPLL, 1,  28 }, L2(5),   950000, 3200000 },
diff --git a/arch/arm/mach-msm/acpuclock-9625.c b/arch/arm/mach-msm/acpuclock-9625.c
new file mode 100644
index 0000000..7fd00e6
--- /dev/null
+++ b/arch/arm/mach-msm/acpuclock-9625.c
@@ -0,0 +1,475 @@
+/*
+ * Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <linux/spinlock.h>
+#include <linux/errno.h>
+#include <linux/cpufreq.h>
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <linux/iopoll.h>
+
+#include <mach/board.h>
+#include <mach/msm_iomap.h>
+#include <mach/msm_bus.h>
+#include <mach/msm_bus_board.h>
+#include <mach/rpm-regulator.h>
+#include <mach/clk-provider.h>
+#include <mach/rpm-regulator-smd.h>
+
+#include "acpuclock.h"
+
+#define RCG_SRC_DIV_MASK		BM(7, 0)
+#define RCG_CONFIG_PGM_DATA_BIT		BIT(11)
+#define RCG_CONFIG_PGM_ENA_BIT		BIT(10)
+#define POLL_INTERVAL_US		1
+#define APCS_RCG_UPDATE_TIMEOUT_US	20
+#define GPLL0_TO_A5_ALWAYS_ENABLE	BIT(18)
+
+#define MAX_VDD_MEM			1050000
+#define MAX_VDD_CPU			1050000
+
+/* Corner type vreg VDD values */
+#define LVL_NONE        RPM_REGULATOR_CORNER_NONE
+#define LVL_LOW         RPM_REGULATOR_CORNER_SVS_SOC
+#define LVL_NOM         RPM_REGULATOR_CORNER_NORMAL
+#define LVL_HIGH        RPM_REGULATOR_CORNER_SUPER_TURBO
+
+enum clk_src {
+	CXO,
+	PLL0,
+	ACPUPLL,
+	NUM_SRC,
+};
+
+struct src_clock {
+	struct clk *clk;
+	const char *name;
+};
+
+static struct src_clock src_clocks[NUM_SRC] = {
+	[PLL0].name = "pll0",
+	[ACPUPLL].name = "pll14",
+};
+
+struct clkctl_acpu_speed {
+	bool use_for_scaling;
+	unsigned int khz;
+	int src;
+	unsigned int src_sel;
+	unsigned int src_div;
+	unsigned int vdd_cpu;
+	unsigned int vdd_mem;
+	unsigned int bw_level;
+};
+
+struct acpuclk_drv_data {
+	struct mutex			lock;
+	struct clkctl_acpu_speed	*current_speed;
+	void __iomem			*apcs_rcg_config;
+	void __iomem			*apcs_cpu_pwr_ctl;
+	struct regulator		*vdd_cpu;
+	struct regulator		*vdd_mem;
+};
+
+static struct acpuclk_drv_data drv_data = {
+	.current_speed = &(struct clkctl_acpu_speed){ 0 },
+};
+
+/* Instantaneous bandwidth requests in MB/s. */
+#define BW_MBPS(_bw) \
+	{ \
+		.vectors = &(struct msm_bus_vectors){ \
+			.src = MSM_BUS_MASTER_AMPSS_M0, \
+			.dst = MSM_BUS_SLAVE_EBI_CH0, \
+			.ib = (_bw) * 1000000UL, \
+			.ab = 0, \
+		}, \
+		.num_paths = 1, \
+	}
+
+static struct msm_bus_paths bw_level_tbl[] = {
+	[0] =  BW_MBPS(152), /* At least 19 MHz on bus. */
+	[1] =  BW_MBPS(264), /* At least 33 MHz on bus. */
+	[2] =  BW_MBPS(528), /* At least 66 MHz on bus. */
+	[3] =  BW_MBPS(664), /* At least 83 MHz on bus. */
+	[4] = BW_MBPS(1064), /* At least 133 MHz on bus. */
+	[5] = BW_MBPS(1328), /* At least 166 MHz on bus. */
+	[6] = BW_MBPS(2128), /* At least 266 MHz on bus. */
+	[7] = BW_MBPS(2664), /* At least 333 MHz on bus. */
+};
+
+static struct msm_bus_scale_pdata bus_client_pdata = {
+	.usecase = bw_level_tbl,
+	.num_usecases = ARRAY_SIZE(bw_level_tbl),
+	.active_only = 1,
+	.name = "acpuclock",
+};
+
+static uint32_t bus_perf_client;
+
+/* TODO:
+ * 1) Update MX voltage when they are avaiable
+ * 2) Update bus bandwidth
+ */
+static struct clkctl_acpu_speed acpu_freq_tbl[] = {
+	{ 0,  19200, CXO,     0, 0,   LVL_LOW,    950000, 0 },
+	{ 1, 300000, PLL0,    1, 2,   LVL_LOW,    950000, 4 },
+	{ 1, 600000, PLL0,    1, 0,   LVL_NOM,    950000, 4 },
+	{ 1, 748800, ACPUPLL, 5, 0,   LVL_HIGH,  1050000, 7 },
+	{ 1, 998400, ACPUPLL, 5, 0,   LVL_HIGH,  1050000, 7 },
+	{ 0 }
+};
+
+/* Update the bus bandwidth request. */
+static void set_bus_bw(unsigned int bw)
+{
+	int ret;
+
+	if (bw >= ARRAY_SIZE(bw_level_tbl)) {
+		pr_err("invalid bandwidth request (%d)\n", bw);
+		return;
+	}
+
+	/* Update bandwidth if request has changed. This may sleep. */
+	ret = msm_bus_scale_client_update_request(bus_perf_client, bw);
+	if (ret)
+		pr_err("bandwidth request failed (%d)\n", ret);
+
+	return;
+}
+
+/* Apply any per-cpu voltage increases. */
+static int increase_vdd(unsigned int vdd_cpu, unsigned int vdd_mem)
+{
+	int rc = 0;
+
+	/* Increase vdd_mem before vdd_cpu. vdd_mem should be >= vdd_cpu. */
+	rc = regulator_set_voltage(drv_data.vdd_mem, vdd_mem, MAX_VDD_MEM);
+	if (rc) {
+		pr_err("vdd_mem increase failed (%d)\n", rc);
+		return rc;
+	}
+
+	rc = regulator_set_voltage(drv_data.vdd_cpu, vdd_cpu, MAX_VDD_CPU);
+	if (rc)
+		pr_err("vdd_cpu increase failed (%d)\n", rc);
+
+	return rc;
+}
+
+/* Apply any per-cpu voltage decreases. */
+static void decrease_vdd(unsigned int vdd_cpu, unsigned int vdd_mem)
+{
+	int ret;
+
+	/* Update CPU voltage. */
+	ret = regulator_set_voltage(drv_data.vdd_cpu, vdd_cpu, MAX_VDD_CPU);
+	if (ret) {
+		pr_err("vdd_cpu decrease failed (%d)\n", ret);
+		return;
+	}
+
+	/* Decrease vdd_mem after vdd_cpu. vdd_mem should be >= vdd_cpu. */
+	ret = regulator_set_voltage(drv_data.vdd_mem, vdd_mem, MAX_VDD_MEM);
+	if (ret)
+		pr_err("vdd_mem decrease failed (%d)\n", ret);
+}
+
+static void select_clk_source_div(struct clkctl_acpu_speed *s)
+{
+	u32 regval, rc, src_div;
+	void __iomem *apcs_rcg_config = drv_data.apcs_rcg_config;
+
+	src_div = s->src_div ? ((2 * s->src_div) - 1) : s->src_div;
+
+	regval = readl_relaxed(apcs_rcg_config);
+	regval &= ~RCG_SRC_DIV_MASK;
+	regval |= BVAL(2, 0, s->src_sel) | BVAL(7, 3, src_div);
+	writel_relaxed(regval, apcs_rcg_config);
+
+	/*
+	 * Make sure writing of src and div finishes before update
+	 * the configuration
+	 */
+	mb();
+
+	/* Update the configruation */
+	regval = readl_relaxed(apcs_rcg_config);
+	regval |= RCG_CONFIG_PGM_DATA_BIT | RCG_CONFIG_PGM_ENA_BIT;
+	writel_relaxed(regval, apcs_rcg_config);
+
+	/* Wait for update to take effect */
+	rc = readl_poll_timeout(apcs_rcg_config, regval,
+		   !(regval & RCG_CONFIG_PGM_DATA_BIT),
+		   POLL_INTERVAL_US,
+		   APCS_RCG_UPDATE_TIMEOUT_US);
+	if (rc)
+		pr_warn("acpu rcg didn't update its configuration\n");
+}
+
+static int set_speed(struct clkctl_acpu_speed *tgt_s)
+{
+	int rc = 0;
+	unsigned int tgt_freq_hz = tgt_s->khz * 1000;
+	struct clkctl_acpu_speed *strt_s = drv_data.current_speed;
+	struct clkctl_acpu_speed *cxo_s = &acpu_freq_tbl[0];
+	struct clk *strt = src_clocks[strt_s->src].clk;
+	struct clk *tgt = src_clocks[tgt_s->src].clk;
+
+	if (strt_s->src == ACPUPLL && tgt_s->src == ACPUPLL) {
+		/* Switch to another always on src */
+		select_clk_source_div(cxo_s);
+
+		/* Re-program acpu pll */
+		clk_disable(tgt);
+		rc = clk_set_rate(tgt, tgt_freq_hz);
+		if (rc)
+			pr_err("Failed to set ACPU PLL to %u\n", tgt_freq_hz);
+		BUG_ON(clk_enable(tgt));
+
+		/* Switch back to acpu pll */
+		select_clk_source_div(tgt_s);
+	} else if (strt_s->src != ACPUPLL && tgt_s->src == ACPUPLL) {
+		rc = clk_set_rate(tgt, tgt_freq_hz);
+		if (rc) {
+			pr_err("Failed to set ACPU PLL to %u\n", tgt_freq_hz);
+			return rc;
+		}
+
+		rc = clk_enable(tgt);
+		if (rc) {
+			pr_err("ACPU PLL enable failed\n");
+			return rc;
+		}
+
+		select_clk_source_div(tgt_s);
+
+		clk_disable(strt);
+	} else {
+		rc = clk_enable(tgt);
+		if (rc) {
+			pr_err("%s enable failed\n",
+					src_clocks[tgt_s->src].name);
+			return rc;
+		}
+
+		select_clk_source_div(tgt_s);
+
+		clk_disable(strt);
+	}
+
+	return rc;
+}
+
+static int acpuclk_9625_set_rate(int cpu, unsigned long rate,
+				 enum setrate_reason reason)
+{
+	struct clkctl_acpu_speed *tgt_s, *strt_s;
+	int rc = 0;
+
+	if (reason == SETRATE_CPUFREQ)
+		mutex_lock(&drv_data.lock);
+
+	strt_s = drv_data.current_speed;
+
+	/* Return early if rate didn't change */
+	if (rate == strt_s->khz)
+		goto out;
+
+	/* Find target frequency */
+	for (tgt_s = acpu_freq_tbl; tgt_s->khz != 0; tgt_s++)
+		if (tgt_s->khz == rate)
+			break;
+	if (tgt_s->khz == 0) {
+		rc = -EINVAL;
+		goto out;
+	}
+
+	/* Increase VDD levels if needed */
+	if ((reason == SETRATE_CPUFREQ || reason == SETRATE_INIT)
+			&& (tgt_s->khz > strt_s->khz)) {
+		rc = increase_vdd(tgt_s->vdd_cpu, tgt_s->vdd_mem);
+		if (rc)
+			goto out;
+	}
+
+	pr_debug("Switching from CPU rate %u KHz -> %u KHz\n",
+		strt_s->khz, tgt_s->khz);
+
+	/* Switch CPU speed. */
+	rc = set_speed(tgt_s);
+	if (rc)
+		goto out;
+
+	drv_data.current_speed = tgt_s;
+	pr_debug("CPU speed change complete\n");
+
+	/* Nothing else to do for SWFI or power-collapse. */
+	if (reason == SETRATE_SWFI || reason == SETRATE_PC)
+		goto out;
+
+	/* Update bus bandwith request */
+	set_bus_bw(tgt_s->bw_level);
+
+	/* Drop VDD levels if we can. */
+	if (tgt_s->khz < strt_s->khz)
+		decrease_vdd(tgt_s->vdd_cpu, tgt_s->vdd_mem);
+
+out:
+	if (reason == SETRATE_CPUFREQ)
+		mutex_unlock(&drv_data.lock);
+	return rc;
+}
+
+static unsigned long acpuclk_9625_get_rate(int cpu)
+{
+	return drv_data.current_speed->khz;
+}
+
+#ifdef CONFIG_CPU_FREQ_MSM
+static struct cpufreq_frequency_table freq_table[30];
+
+static void __init cpufreq_table_init(void)
+{
+	int i, freq_cnt = 0;
+
+	/* Construct the freq_table tables from acpu_freq_tbl. */
+	for (i = 0; acpu_freq_tbl[i].khz != 0
+			&& freq_cnt < ARRAY_SIZE(freq_table); i++) {
+		if (!acpu_freq_tbl[i].use_for_scaling)
+			continue;
+		freq_table[freq_cnt].index = freq_cnt;
+		freq_table[freq_cnt].frequency = acpu_freq_tbl[i].khz;
+		freq_cnt++;
+	}
+	/* freq_table not big enough to store all usable freqs. */
+	BUG_ON(acpu_freq_tbl[i].khz != 0);
+
+	freq_table[freq_cnt].index = freq_cnt;
+	freq_table[freq_cnt].frequency = CPUFREQ_TABLE_END;
+
+	pr_info("CPU: %d scaling frequencies supported.\n", freq_cnt);
+
+	/* Register table with CPUFreq. */
+	cpufreq_frequency_table_get_attr(freq_table, smp_processor_id());
+}
+#else
+static void __init cpufreq_table_init(void) {}
+#endif
+
+static struct acpuclk_data acpuclk_9625_data = {
+	.set_rate = acpuclk_9625_set_rate,
+	.get_rate = acpuclk_9625_get_rate,
+	.power_collapse_khz = 19200,
+	.wait_for_irq_khz = 19200,
+};
+
+static int __init acpuclk_9625_probe(struct platform_device *pdev)
+{
+	unsigned long max_cpu_khz = 0;
+	struct resource *res;
+	int i;
+	u32 regval;
+
+	mutex_init(&drv_data.lock);
+
+	bus_perf_client = msm_bus_scale_register_client(&bus_client_pdata);
+	if (!bus_perf_client) {
+		pr_err("Unable to register bus client\n");
+		BUG();
+	}
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "rcg_base");
+	if (!res)
+		return -EINVAL;
+
+	drv_data.apcs_rcg_config = ioremap(res->start, resource_size(res));
+	if (!drv_data.apcs_rcg_config)
+		return -ENOMEM;
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pwr_base");
+	if (!res)
+		return -EINVAL;
+
+	drv_data.apcs_cpu_pwr_ctl = ioremap(res->start, resource_size(res));
+	if (!drv_data.apcs_cpu_pwr_ctl)
+		return -ENOMEM;
+
+	drv_data.vdd_cpu = regulator_get(&pdev->dev, "a5_cpu");
+	if (IS_ERR(drv_data.vdd_cpu)) {
+		dev_err(&pdev->dev, "regulator for %s get failed\n", "a5_cpu");
+		return PTR_ERR(drv_data.vdd_cpu);
+	}
+
+	drv_data.vdd_mem = regulator_get(&pdev->dev, "a5_mem");
+	if (IS_ERR(drv_data.vdd_mem)) {
+		dev_err(&pdev->dev, "regulator for %s get failed\n", "a5_mem");
+		return PTR_ERR(drv_data.vdd_mem);
+	}
+
+	/* Disable hardware gating of gpll0 to A5SS */
+	regval = readl_relaxed(drv_data.apcs_cpu_pwr_ctl);
+	regval |= GPLL0_TO_A5_ALWAYS_ENABLE;
+	writel_relaxed(regval, drv_data.apcs_cpu_pwr_ctl);
+
+	for (i = 0; i < NUM_SRC; i++) {
+		if (!src_clocks[i].name)
+			continue;
+		src_clocks[i].clk = clk_get(&pdev->dev, src_clocks[i].name);
+		BUG_ON(IS_ERR(src_clocks[i].clk));
+		/*
+		 * Prepare the PLLs because we enable/disable them
+		 * in atomic context during power collapse/restore.
+		 */
+		BUG_ON(clk_prepare(src_clocks[i].clk));
+	}
+
+	/* Improve boot time by ramping up CPU immediately */
+	for (i = 0; acpu_freq_tbl[i].khz != 0 &&
+				acpu_freq_tbl[i].use_for_scaling; i++)
+		max_cpu_khz = acpu_freq_tbl[i].khz;
+
+	acpuclk_9625_set_rate(smp_processor_id(), max_cpu_khz, SETRATE_INIT);
+
+	acpuclk_register(&acpuclk_9625_data);
+	cpufreq_table_init();
+
+	return 0;
+}
+
+static struct of_device_id acpuclk_9625_match_table[] = {
+	{.compatible = "qcom,acpuclk-9625"},
+	{}
+};
+
+static struct platform_driver acpuclk_9625_driver = {
+	.driver = {
+		.name = "acpuclk-9625",
+		.of_match_table = acpuclk_9625_match_table,
+		.owner = THIS_MODULE,
+	},
+};
+
+static int __init acpuclk_9625_init(void)
+{
+	return platform_driver_probe(&acpuclk_9625_driver, acpuclk_9625_probe);
+}
+device_initcall(acpuclk_9625_init);
diff --git a/arch/arm/mach-msm/acpuclock-krait-debug.c b/arch/arm/mach-msm/acpuclock-krait-debug.c
new file mode 100644
index 0000000..a29735e
--- /dev/null
+++ b/arch/arm/mach-msm/acpuclock-krait-debug.c
@@ -0,0 +1,335 @@
+/*
+ * Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <linux/debugfs.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/cpu.h>
+#include <linux/smp.h>
+
+#include <mach/msm_bus.h>
+#include <mach/msm-krait-l2-accessors.h>
+
+#include "acpuclock-krait.h"
+
+static struct drv_data *drv;
+static DEFINE_MUTEX(debug_lock);
+
+struct acg_action {
+	bool set;
+	bool enable;
+};
+static int l2_acg_en_val;
+static struct dentry *base_dir;
+static struct dentry *sc_dir[MAX_SCALABLES];
+
+static void cpu_action(void *info)
+{
+	struct acg_action *action = info;
+
+	u32 val;
+	asm volatile ("mrc p15, 7, %[cpmr0], c15, c0, 5\n\t"
+			: [cpmr0]"=r" (val));
+	if (action->set) {
+		if (action->enable)
+			val &= ~BIT(0);
+		else
+			val |= BIT(0);
+		asm volatile ("mcr p15, 7, %[cpmr0], c15, c0, 5\n\t"
+				: : [cpmr0]"r" (val));
+	} else {
+		action->enable = !(val & BIT(0));
+	}
+}
+
+/* Disable auto clock-gating for a scalable. */
+static void disable_acg(int sc_id)
+{
+	u32 regval;
+
+	if (sc_id == L2) {
+		regval = get_l2_indirect_reg(drv->scalable[sc_id].l2cpmr_iaddr);
+		l2_acg_en_val = regval & (0x3 << 10);
+		regval |= (0x3 << 10);
+		set_l2_indirect_reg(drv->scalable[sc_id].l2cpmr_iaddr, regval);
+	} else {
+		struct acg_action action = { .set = true, .enable = false };
+		smp_call_function_single(sc_id, cpu_action, &action, 1);
+	}
+}
+
+/* Enable auto clock-gating for a scalable. */
+static void enable_acg(int sc_id)
+{
+	u32 regval;
+
+	if (sc_id == L2) {
+		regval = get_l2_indirect_reg(drv->scalable[sc_id].l2cpmr_iaddr);
+		regval &= ~(0x3 << 10);
+		regval |= l2_acg_en_val;
+		set_l2_indirect_reg(drv->scalable[sc_id].l2cpmr_iaddr, regval);
+	} else {
+		struct acg_action action = { .set = true, .enable = true };
+		smp_call_function_single(sc_id, cpu_action, &action, 1);
+	}
+}
+
+/* Check if auto clock-gating for a scalable. */
+static bool acg_is_enabled(int sc_id)
+{
+	u32 regval;
+
+	if (sc_id == L2) {
+		regval = get_l2_indirect_reg(drv->scalable[sc_id].l2cpmr_iaddr);
+		return ((regval >> 10) & 0x3) != 0x3;
+	} else {
+		struct acg_action action = { .set = false };
+		smp_call_function_single(sc_id, cpu_action, &action, 1);
+		return action.enable;
+	}
+}
+
+/* Enable/Disable auto clock gating. */
+static int acg_set(void *data, u64 val)
+{
+	int ret = 0;
+	int sc_id = (int)data;
+
+	mutex_lock(&debug_lock);
+	get_online_cpus();
+	if (!sc_dir[sc_id]) {
+		ret = -ENODEV;
+		goto out;
+	}
+
+	if (val == 0 && acg_is_enabled(sc_id))
+		disable_acg(sc_id);
+	else if (val == 1)
+		enable_acg(sc_id);
+out:
+	put_online_cpus();
+	mutex_unlock(&debug_lock);
+
+	return ret;
+}
+
+/* Get auto clock-gating state. */
+static int acg_get(void *data, u64 *val)
+{
+	int ret = 0;
+	int sc_id = (int)data;
+
+	mutex_lock(&debug_lock);
+	get_online_cpus();
+	if (!sc_dir[sc_id]) {
+		ret = -ENODEV;
+		goto out;
+	}
+
+	*val = acg_is_enabled(sc_id);
+out:
+	put_online_cpus();
+	mutex_unlock(&debug_lock);
+
+	return ret;
+}
+DEFINE_SIMPLE_ATTRIBUTE(acgd_fops, acg_get, acg_set, "%lld\n");
+
+/* Get the rate */
+static int rate_get(void *data, u64 *val)
+{
+	int sc_id = (int)data;
+	*val = drv->scalable[sc_id].cur_speed->khz;
+	return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(rate_fops, rate_get, NULL, "%lld\n");
+
+/* Get the HFPLL's L-value. */
+static int hfpll_l_get(void *data, u64 *val)
+{
+	int sc_id = (int)data;
+	*val = drv->scalable[sc_id].cur_speed->pll_l_val;
+	return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(hfpll_l_fops, hfpll_l_get, NULL, "%lld\n");
+
+/* Get the L2 rate vote. */
+static int l2_vote_get(void *data, u64 *val)
+{
+	int level, sc_id = (int)data;
+
+	level = drv->scalable[sc_id].l2_vote;
+	*val = drv->l2_freq_tbl[level].speed.khz;
+
+	return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(l2_vote_fops, l2_vote_get, NULL, "%lld\n");
+
+/* Get the bandwidth vote. */
+static int bw_vote_get(void *data, u64 *val)
+{
+	struct l2_level *l;
+
+	l = container_of(drv->scalable[L2].cur_speed,
+			 struct l2_level, speed);
+	*val = drv->bus_scale->usecase[l->bw_level].vectors->ib;
+
+	return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(bw_vote_fops, bw_vote_get, NULL, "%lld\n");
+
+/* Get the name of the currently-selected clock source. */
+static int src_name_show(struct seq_file *m, void *unused)
+{
+	const char *const src_names[NUM_SRC_ID] = {
+		[PLL_0] = "PLL0",
+		[HFPLL] = "HFPLL",
+		[PLL_8] = "PLL8",
+	};
+	int src, sc_id = (int)m->private;
+
+	src = drv->scalable[sc_id].cur_speed->src;
+	if (src > ARRAY_SIZE(src_names))
+		return -EINVAL;
+
+	seq_printf(m, "%s\n", src_names[src]);
+
+	return 0;
+}
+
+static int src_name_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, src_name_show, inode->i_private);
+}
+
+static const struct file_operations src_name_fops = {
+	.open		= src_name_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= seq_release,
+};
+
+/* Get speed_bin ID */
+static int speed_bin_get(void *data, u64 *val)
+{
+	*val = drv->speed_bin;
+	return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(speed_bin_fops, speed_bin_get, NULL, "%lld\n");
+
+/* Get pvs_bin ID */
+static int pvs_bin_get(void *data, u64 *val)
+{
+	*val = drv->pvs_bin;
+	return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(pvs_bin_fops, pvs_bin_get, NULL, "%lld\n");
+
+/* Get boost_uv */
+static int boost_get(void *data, u64 *val)
+{
+	*val = drv->boost_uv;
+	return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(boost_fops, boost_get, NULL, "%lld\n");
+
+static void __cpuinit add_scalable_dir(int sc_id)
+{
+	char sc_name[8];
+
+	if (sc_id == L2)
+		snprintf(sc_name, sizeof(sc_name), "l2");
+	else
+		snprintf(sc_name, sizeof(sc_name), "cpu%d", sc_id);
+
+	sc_dir[sc_id] = debugfs_create_dir(sc_name, base_dir);
+	if (!sc_dir[sc_id])
+		return;
+
+	debugfs_create_file("auto_gating", S_IRUGO | S_IWUSR,
+			sc_dir[sc_id], (void *)sc_id, &acgd_fops);
+
+	debugfs_create_file("rate", S_IRUGO,
+			sc_dir[sc_id], (void *)sc_id, &rate_fops);
+
+	debugfs_create_file("hfpll_l", S_IRUGO,
+			sc_dir[sc_id], (void *)sc_id, &hfpll_l_fops);
+
+	debugfs_create_file("src", S_IRUGO,
+			sc_dir[sc_id], (void *)sc_id, &src_name_fops);
+
+	if (sc_id == L2)
+		debugfs_create_file("bw_ib_vote", S_IRUGO,
+			sc_dir[sc_id], (void *)sc_id, &bw_vote_fops);
+	else
+		debugfs_create_file("l2_vote", S_IRUGO,
+			sc_dir[sc_id], (void *)sc_id, &l2_vote_fops);
+}
+
+static void __cpuinit remove_scalable_dir(int sc_id)
+{
+	debugfs_remove_recursive(sc_dir[sc_id]);
+	sc_dir[sc_id] = NULL;
+}
+
+static int __cpuinit debug_cpu_callback(struct notifier_block *nfb,
+			unsigned long action, void *hcpu)
+{
+	int cpu = (int)hcpu;
+
+	switch (action & ~CPU_TASKS_FROZEN) {
+	case CPU_DOWN_FAILED:
+	case CPU_UP_PREPARE:
+		add_scalable_dir(cpu);
+		break;
+	case CPU_UP_CANCELED:
+	case CPU_DOWN_PREPARE:
+		remove_scalable_dir(cpu);
+		break;
+	default:
+		break;
+	}
+
+	return NOTIFY_OK;
+}
+
+static struct notifier_block __cpuinitdata debug_cpu_notifier = {
+	.notifier_call = debug_cpu_callback,
+};
+
+void __init acpuclk_krait_debug_init(struct drv_data *drv_data)
+{
+	int cpu;
+	drv = drv_data;
+
+	base_dir = debugfs_create_dir("acpuclk", NULL);
+	if (!base_dir)
+		return;
+
+	debugfs_create_file("speed_bin", S_IRUGO, base_dir, NULL,
+							&speed_bin_fops);
+	debugfs_create_file("pvs_bin", S_IRUGO, base_dir, NULL, &pvs_bin_fops);
+	debugfs_create_file("boost_uv", S_IRUGO, base_dir, NULL, &boost_fops);
+
+	for_each_online_cpu(cpu)
+		add_scalable_dir(cpu);
+	add_scalable_dir(L2);
+
+	register_hotcpu_notifier(&debug_cpu_notifier);
+}
diff --git a/arch/arm/mach-msm/acpuclock-krait.c b/arch/arm/mach-msm/acpuclock-krait.c
index 57c4411..bf57eab 100644
--- a/arch/arm/mach-msm/acpuclock-krait.c
+++ b/arch/arm/mach-msm/acpuclock-krait.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -48,16 +48,7 @@
 static DEFINE_MUTEX(driver_lock);
 static DEFINE_SPINLOCK(l2_lock);
 
-static struct drv_data {
-	struct acpu_level *acpu_freq_tbl;
-	const struct l2_level *l2_freq_tbl;
-	struct scalable *scalable;
-	struct hfpll_data *hfpll_data;
-	u32 bus_perf_client;
-	struct msm_bus_scale_pdata *bus_scale;
-	int boost_uv;
-	struct device *dev;
-} drv;
+static struct drv_data drv;
 
 static unsigned long acpuclk_krait_get_rate(int cpu)
 {
@@ -218,7 +209,8 @@
 }
 
 /* Set the CPU or L2 clock speed. */
-static void set_speed(struct scalable *sc, const struct core_speed *tgt_s)
+static void set_speed(struct scalable *sc, const struct core_speed *tgt_s,
+	bool skip_regulators)
 {
 	const struct core_speed *strt_s = sc->cur_speed;
 
@@ -241,10 +233,10 @@
 		set_pri_clk_src(sc, tgt_s->pri_src_sel);
 	} else if (strt_s->src == HFPLL && tgt_s->src != HFPLL) {
 		set_pri_clk_src(sc, tgt_s->pri_src_sel);
-		hfpll_disable(sc, false);
+		hfpll_disable(sc, skip_regulators);
 	} else if (strt_s->src != HFPLL && tgt_s->src == HFPLL) {
 		hfpll_set_rate(sc, tgt_s);
-		hfpll_enable(sc, false);
+		hfpll_enable(sc, skip_regulators);
 		set_pri_clk_src(sc, tgt_s->pri_src_sel);
 	}
 
@@ -435,6 +427,47 @@
 	return tgt->vdd_core + (enable_boost ? drv.boost_uv : 0);
 }
 
+static DEFINE_MUTEX(l2_regulator_lock);
+static int l2_vreg_count;
+
+static int enable_l2_regulators(void)
+{
+	int ret = 0;
+
+	mutex_lock(&l2_regulator_lock);
+	if (l2_vreg_count == 0) {
+		ret = enable_rpm_vreg(&drv.scalable[L2].vreg[VREG_HFPLL_A]);
+		if (ret)
+			goto out;
+		ret = enable_rpm_vreg(&drv.scalable[L2].vreg[VREG_HFPLL_B]);
+		if (ret) {
+			disable_rpm_vreg(&drv.scalable[L2].vreg[VREG_HFPLL_A]);
+			goto out;
+		}
+	}
+	l2_vreg_count++;
+out:
+	mutex_unlock(&l2_regulator_lock);
+
+	return ret;
+}
+
+static void disable_l2_regulators(void)
+{
+	mutex_lock(&l2_regulator_lock);
+
+	if (WARN(!l2_vreg_count, "L2 regulator votes are unbalanced!"))
+		goto out;
+
+	if (l2_vreg_count == 1) {
+		disable_rpm_vreg(&drv.scalable[L2].vreg[VREG_HFPLL_B]);
+		disable_rpm_vreg(&drv.scalable[L2].vreg[VREG_HFPLL_A]);
+	}
+	l2_vreg_count--;
+out:
+	mutex_unlock(&l2_regulator_lock);
+}
+
 /* Set the CPU's clock rate and adjust the L2 rate, voltage and BW requests. */
 static int acpuclk_krait_set_rate(int cpu, unsigned long rate,
 				  enum setrate_reason reason)
@@ -442,8 +475,9 @@
 	const struct core_speed *strt_acpu_s, *tgt_acpu_s;
 	const struct acpu_level *tgt;
 	int tgt_l2_l;
+	enum src_id prev_l2_src = NUM_SRC_ID;
 	struct vdd_data vdd_data;
-	unsigned long flags;
+	bool skip_regulators;
 	int rc = 0;
 
 	if (cpu > num_possible_cpus())
@@ -487,13 +521,31 @@
 		rc = increase_vdd(cpu, &vdd_data, reason);
 		if (rc)
 			goto out;
+
+		prev_l2_src =
+			drv.l2_freq_tbl[drv.scalable[cpu].l2_vote].speed.src;
+		/* Vote for the L2 regulators here if necessary. */
+		if (drv.l2_freq_tbl[tgt->l2_level].speed.src == HFPLL) {
+			rc = enable_l2_regulators();
+			if (rc)
+				goto out;
+		}
 	}
 
 	dev_dbg(drv.dev, "Switching from ACPU%d rate %lu KHz -> %lu KHz\n",
 		cpu, strt_acpu_s->khz, tgt_acpu_s->khz);
 
+	/*
+	 * If we are setting the rate as part of power collapse or in the resume
+	 * path after power collapse, skip the vote for the HFPLL regulators,
+	 * which are active-set-only votes that will be removed when apps enters
+	 * its sleep set. This is needed to avoid voting for regulators with
+	 * sleeping APIs from an atomic context.
+	 */
+	skip_regulators = (reason == SETRATE_PC);
+
 	/* Set the new CPU speed. */
-	set_speed(&drv.scalable[cpu], tgt_acpu_s);
+	set_speed(&drv.scalable[cpu], tgt_acpu_s, skip_regulators);
 
 	/*
 	 * Update the L2 vote and apply the rate change. A spinlock is
@@ -502,15 +554,23 @@
 	 * called from an atomic context and the driver_lock mutex is not
 	 * acquired.
 	 */
-	spin_lock_irqsave(&l2_lock, flags);
+	spin_lock(&l2_lock);
 	tgt_l2_l = compute_l2_level(&drv.scalable[cpu], tgt->l2_level);
-	set_speed(&drv.scalable[L2], &drv.l2_freq_tbl[tgt_l2_l].speed);
-	spin_unlock_irqrestore(&l2_lock, flags);
+	set_speed(&drv.scalable[L2],
+			&drv.l2_freq_tbl[tgt_l2_l].speed, true);
+	spin_unlock(&l2_lock);
 
 	/* Nothing else to do for power collapse or SWFI. */
 	if (reason == SETRATE_PC || reason == SETRATE_SWFI)
 		goto out;
 
+	/*
+	 * Remove the vote for the L2 HFPLL regulators only if the L2
+	 * was already on an HFPLL source.
+	 */
+	if (prev_l2_src == HFPLL)
+		disable_l2_regulators();
+
 	/* Update bus bandwith request. */
 	set_bus_bw(drv.l2_freq_tbl[tgt_l2_l].bw_level);
 
@@ -537,7 +597,7 @@
 };
 
 /* Initialize a HFPLL at a given rate and enable it. */
-static void __init hfpll_init(struct scalable *sc,
+static void __cpuinit hfpll_init(struct scalable *sc,
 			      const struct core_speed *tgt_s)
 {
 	dev_dbg(drv.dev, "Initializing HFPLL%d\n", sc - drv.scalable);
@@ -672,6 +732,14 @@
 		goto err_core_conf;
 	}
 
+	/*
+	 * Increment the L2 HFPLL regulator refcount if _this_ CPU's frequency
+	 * requires a corresponding target L2 frequency that needs the L2 to
+	 * run off of an HFPLL.
+	 */
+	if (drv.l2_freq_tbl[acpu_level->l2_level].speed.src == HFPLL)
+		l2_vreg_count++;
+
 	return 0;
 
 err_core_conf:
@@ -986,7 +1054,7 @@
 			struct pvs_table (*pvs_tables)[NUM_PVS])
 {
 	void __iomem *pte_efuse;
-	u32 pte_efuse_val, tbl_idx, bin_idx;
+	u32 pte_efuse_val;
 
 	pte_efuse = ioremap(pte_efuse_phys, 4);
 	if (!pte_efuse) {
@@ -998,10 +1066,10 @@
 	iounmap(pte_efuse);
 
 	/* Select frequency tables. */
-	bin_idx = get_speed_bin(pte_efuse_val);
-	tbl_idx = get_pvs_bin(pte_efuse_val);
+	drv.speed_bin = get_speed_bin(pte_efuse_val);
+	drv.pvs_bin = get_pvs_bin(pte_efuse_val);
 
-	return &pvs_tables[bin_idx][tbl_idx];
+	return &pvs_tables[drv.speed_bin][drv.pvs_bin];
 }
 
 static void __init drv_data_init(struct device *dev,
@@ -1091,5 +1159,7 @@
 	acpuclk_register(&acpuclk_krait_data);
 	register_hotcpu_notifier(&acpuclk_cpu_notifier);
 
+	acpuclk_krait_debug_init(&drv);
+
 	return 0;
 }
diff --git a/arch/arm/mach-msm/acpuclock-krait.h b/arch/arm/mach-msm/acpuclock-krait.h
index 3fa10e3..ca8013e 100644
--- a/arch/arm/mach-msm/acpuclock-krait.h
+++ b/arch/arm/mach-msm/acpuclock-krait.h
@@ -21,12 +21,12 @@
 			{\
 				.src = MSM_BUS_MASTER_AMPSS_M0, \
 				.dst = MSM_BUS_SLAVE_EBI_CH0, \
-				.ib = (_bw) * 1000000UL, \
+				.ib = (_bw) * 1000000ULL, \
 			}, \
 			{ \
 				.src = MSM_BUS_MASTER_AMPSS_M1, \
 				.dst = MSM_BUS_SLAVE_EBI_CH0, \
-				.ib = (_bw) * 1000000UL, \
+				.ib = (_bw) * 1000000ULL, \
 			}, \
 		}, \
 		.num_paths = 2, \
@@ -39,6 +39,7 @@
 	PLL_0 = 0,
 	HFPLL,
 	PLL_8,
+	NUM_SRC_ID
 };
 
 /**
@@ -66,6 +67,7 @@
 	CPU2,
 	CPU3,
 	L2,
+	MAX_SCALABLES
 };
 
 
@@ -261,6 +263,32 @@
 };
 
 /**
+ * struct drv_data - Driver state
+ * @acpu_freq_tbl: CPU frequency table.
+ * @l2_freq_tbl: L2 frequency table.
+ * @scalable: Array of scalables (CPUs and L2).
+ * @hfpll_data: High-frequency PLL data.
+ * @bus_perf_client: Bus driver client handle.
+ * @bus_scale: Bus driver scaling data.
+ * @boost_uv: Voltage boost amount
+ * @speed_bin: Speed bin ID.
+ * @pvs_bin: PVS bin ID.
+ * @dev: Device.
+ */
+struct drv_data {
+	struct acpu_level *acpu_freq_tbl;
+	const struct l2_level *l2_freq_tbl;
+	struct scalable *scalable;
+	struct hfpll_data *hfpll_data;
+	u32 bus_perf_client;
+	struct msm_bus_scale_pdata *bus_scale;
+	int boost_uv;
+	int speed_bin;
+	int pvs_bin;
+	struct device *dev;
+};
+
+/**
  * struct acpuclk_platform_data - PMIC configuration data.
  * @uses_pm8917: Boolean indicates presence of pm8917.
  */
@@ -273,4 +301,14 @@
  */
 extern int acpuclk_krait_init(struct device *dev,
 			      const struct acpuclk_krait_params *params);
+
+#ifdef CONFIG_DEBUG_FS
+/**
+ * acpuclk_krait_debug_init - Initialize debugfs interface.
+ */
+extern void __init acpuclk_krait_debug_init(struct drv_data *drv);
+#else
+static inline void acpuclk_krait_debug_init(void) { }
+#endif
+
 #endif
diff --git a/arch/arm/mach-msm/adsp-8974.c b/arch/arm/mach-msm/adsp-8974.c
deleted file mode 100644
index fa7d9d4..0000000
--- a/arch/arm/mach-msm/adsp-8974.c
+++ /dev/null
@@ -1,301 +0,0 @@
-/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- */
-
-#include <linux/kernel.h>
-#include <linux/interrupt.h>
-#include <linux/reboot.h>
-#include <linux/workqueue.h>
-#include <linux/io.h>
-#include <linux/delay.h>
-#include <linux/module.h>
-#include <linux/err.h>
-
-#include <mach/irqs.h>
-#include <mach/scm.h>
-#include <mach/peripheral-loader.h>
-#include <mach/subsystem_restart.h>
-#include <mach/subsystem_notif.h>
-
-#include "smd_private.h"
-#include "ramdump.h"
-#include "sysmon.h"
-
-#define SCM_Q6_NMI_CMD			0x1
-#define MODULE_NAME			"adsp_8974"
-#define MAX_BUF_SIZE			0x51
-
-/* Interrupt line for WDOG bite*/
-#define ADSP_Q6SS_WDOG_EXPIRED		194
-
-/* Subsystem restart: QDSP6 data, functions */
-static void adsp_fatal_fn(struct work_struct *);
-static DECLARE_WORK(adsp_fatal_work, adsp_fatal_fn);
-
-struct adsp_ssr {
-	void *adsp_ramdump_dev;
-} adsp_ssr;
-
-static struct adsp_ssr adsp_ssr_8974;
-static int q6_crash_shutdown;
-
-static int riva_notifier_cb(struct notifier_block *this, unsigned long code,
-								void *ss_handle)
-{
-	int ret;
-	switch (code) {
-	case SUBSYS_BEFORE_SHUTDOWN:
-		pr_debug("%s: R-Notify: Shutdown started\n", __func__);
-		ret = sysmon_send_event(SYSMON_SS_LPASS, "wcnss",
-				SUBSYS_BEFORE_SHUTDOWN);
-		if (ret < 0)
-			pr_err("%s: sysmon_send_event error %d", __func__,
-				ret);
-		break;
-	}
-	return NOTIFY_DONE;
-}
-
-static void *ssr_notif_hdle;
-static struct notifier_block rnb = {
-	.notifier_call = riva_notifier_cb,
-};
-
-static int modem_notifier_cb(struct notifier_block *this, unsigned long code,
-								void *ss_handle)
-{
-	int ret;
-	switch (code) {
-	case SUBSYS_BEFORE_SHUTDOWN:
-		pr_debug("%s: M-Notify: Shutdown started\n", __func__);
-		ret = sysmon_send_event(SYSMON_SS_LPASS, "modem",
-				SUBSYS_BEFORE_SHUTDOWN);
-		if (ret < 0)
-			pr_err("%s: sysmon_send_event error %d", __func__,
-				ret);
-		break;
-	}
-	return NOTIFY_DONE;
-}
-
-static void *ssr_modem_notif_hdle;
-static struct notifier_block mnb = {
-	.notifier_call = modem_notifier_cb,
-};
-
-static void adsp_log_failure_reason(void)
-{
-	char *reason;
-	char buffer[MAX_BUF_SIZE];
-	unsigned size;
-
-	reason = smem_get_entry(SMEM_SSR_REASON_LPASS0, &size);
-
-	if (!reason) {
-		pr_err("%s: subsystem failure reason: (unknown, smem_get_entry failed).",
-			 MODULE_NAME);
-		return;
-	}
-
-	if (reason[0] == '\0') {
-		pr_err("%s: subsystem failure reason: (unknown, init value found)",
-			 MODULE_NAME);
-		return;
-	}
-
-	size = size < MAX_BUF_SIZE ? size : (MAX_BUF_SIZE-1);
-	memcpy(buffer, reason, size);
-	buffer[size] = '\0';
-	pr_err("%s: subsystem failure reason: %s", MODULE_NAME, buffer);
-	memset((void *)reason, 0x0, size);
-	wmb();
-}
-
-static void restart_adsp(void)
-{
-	adsp_log_failure_reason();
-	subsystem_restart("adsp");
-}
-
-static void adsp_fatal_fn(struct work_struct *work)
-{
-	pr_err("%s %s: Watchdog bite received from Q6!\n", MODULE_NAME,
-		__func__);
-	restart_adsp();
-}
-
-static void adsp_smsm_state_cb(void *data, uint32_t old_state,
-				uint32_t new_state)
-{
-	/* Ignore if we're the one that set SMSM_RESET */
-	if (q6_crash_shutdown)
-		return;
-
-	if (new_state & SMSM_RESET) {
-		pr_debug("%s: ADSP SMSM state changed to SMSM_RESET, new_state= 0x%x, old_state = 0x%x\n",
-			 __func__, new_state, old_state);
-		restart_adsp();
-	}
-}
-
-static void send_q6_nmi(void)
-{
-	/* Send NMI to QDSP6 via an SCM call. */
-	scm_call_atomic1(SCM_SVC_UTIL, SCM_Q6_NMI_CMD, 0x1);
-	pr_debug("%s: Q6 NMI was sent.\n", __func__);
-}
-
-static int adsp_shutdown(const struct subsys_desc *subsys)
-{
-	send_q6_nmi();
-
-	/* The write needs to go through before the q6 is shutdown. */
-	mb();
-
-	pil_force_shutdown("adsp");
-	disable_irq_nosync(ADSP_Q6SS_WDOG_EXPIRED);
-
-	return 0;
-}
-
-static int adsp_powerup(const struct subsys_desc *subsys)
-{
-	int ret;
-
-	if (get_restart_level() == RESET_SUBSYS_INDEPENDENT) {
-		pr_debug("%s: Wait for ADSP power up!", __func__);
-		msleep(10000);
-	}
-
-	ret = pil_force_boot("adsp");
-	enable_irq(ADSP_Q6SS_WDOG_EXPIRED);
-	return ret;
-}
-/* RAM segments - address and size for 8974 */
-static struct ramdump_segment q6_segment = {0xdc00000, 0x1800000};
-
-static int adsp_ramdump(int enable, const struct subsys_desc *subsys)
-{
-	pr_debug("%s: enable[%d]\n", __func__, enable);
-	if (enable)
-		return do_ramdump(adsp_ssr_8974.adsp_ramdump_dev,
-				&q6_segment, 1);
-	else
-		return 0;
-}
-
-static void adsp_crash_shutdown(const struct subsys_desc *subsys)
-{
-	q6_crash_shutdown = 1;
-	send_q6_nmi();
-}
-
-static irqreturn_t adsp_wdog_bite_irq(int irq, void *dev_id)
-{
-	int ret;
-
-	pr_debug("%s: rxed irq[0x%x]", __func__, irq);
-	disable_irq_nosync(ADSP_Q6SS_WDOG_EXPIRED);
-	ret = schedule_work(&adsp_fatal_work);
-
-	return IRQ_HANDLED;
-}
-
-static struct subsys_device *adsp_8974_dev;
-
-static struct subsys_desc adsp_8974 = {
-	.name = "adsp",
-	.shutdown = adsp_shutdown,
-	.powerup = adsp_powerup,
-	.ramdump = adsp_ramdump,
-	.crash_shutdown = adsp_crash_shutdown
-};
-
-static int __init adsp_restart_init(void)
-{
-	adsp_8974_dev = subsys_register(&adsp_8974);
-	if (IS_ERR(adsp_8974_dev))
-		return PTR_ERR(adsp_8974_dev);
-	return 0;
-}
-
-static int __init adsp_fatal_init(void)
-{
-	int ret;
-
-	ret = smsm_state_cb_register(SMSM_Q6_STATE, SMSM_RESET,
-		adsp_smsm_state_cb, 0);
-
-	if (ret < 0)
-		pr_err("%s: Unable to register SMSM callback! (%d)\n",
-				__func__, ret);
-
-	ret = request_irq(ADSP_Q6SS_WDOG_EXPIRED, adsp_wdog_bite_irq,
-			IRQF_TRIGGER_RISING, "q6_wdog", NULL);
-
-	if (ret < 0) {
-		pr_err("%s: Unable to request ADSP_Q6SS_WDOG_EXPIRED irq.",
-			__func__);
-		goto out;
-	}
-	ret = adsp_restart_init();
-	if (ret < 0) {
-		pr_err("%s: Unable to reg with adsp ssr. (%d)\n",
-				__func__, ret);
-		goto out;
-	}
-
-	adsp_ssr_8974.adsp_ramdump_dev = create_ramdump_device("adsp");
-
-	if (!adsp_ssr_8974.adsp_ramdump_dev) {
-		pr_err("%s: Unable to create ramdump device.\n",
-				__func__);
-		ret = -ENOMEM;
-		goto out;
-	}
-	ssr_notif_hdle = subsys_notif_register_notifier("riva",
-							&rnb);
-	if (IS_ERR(ssr_notif_hdle) < 0) {
-		ret = PTR_ERR(ssr_notif_hdle);
-		pr_err("%s: subsys_register_notifier for Riva: err = %d\n",
-			__func__, ret);
-		free_irq(ADSP_Q6SS_WDOG_EXPIRED, NULL);
-		goto out;
-	}
-
-	ssr_modem_notif_hdle = subsys_notif_register_notifier("modem",
-							&mnb);
-	if (IS_ERR(ssr_modem_notif_hdle) < 0) {
-		ret = PTR_ERR(ssr_modem_notif_hdle);
-		pr_err("%s: subsys_register_notifier for Modem: err = %d\n",
-			__func__, ret);
-		subsys_notif_unregister_notifier(ssr_notif_hdle, &rnb);
-		free_irq(ADSP_Q6SS_WDOG_EXPIRED, NULL);
-		goto out;
-	}
-
-	pr_info("%s: adsp ssr driver init'ed.\n", __func__);
-out:
-	return ret;
-}
-
-static void __exit adsp_fatal_exit(void)
-{
-	subsys_notif_unregister_notifier(ssr_notif_hdle, &rnb);
-	subsys_notif_unregister_notifier(ssr_modem_notif_hdle, &mnb);
-	subsys_unregister(adsp_8974_dev);
-	free_irq(ADSP_Q6SS_WDOG_EXPIRED, NULL);
-}
-
-module_init(adsp_fatal_init);
-module_exit(adsp_fatal_exit);
-
-MODULE_LICENSE("GPL v2");
diff --git a/arch/arm/mach-msm/avs.h b/arch/arm/mach-msm/avs.h
index a549e9d..e87bded 100644
--- a/arch/arm/mach-msm/avs.h
+++ b/arch/arm/mach-msm/avs.h
@@ -37,7 +37,8 @@
 u32 avs_get_avsdscr(void);
 u32 avs_get_tscsr(void);
 void avs_set_tscsr(u32 to_tscsr);
-void avs_disable(void);
+u32 avs_disable(void);
+void avs_enable(u32 avscsr);
 #else
 static inline u32 avs_reset_delays(u32 avsdscr)
 { return 0; }
@@ -48,7 +49,9 @@
 static inline u32 avs_get_tscsr(void)
 { return 0; }
 static inline void avs_set_tscsr(u32 to_tscsr) {}
-static inline void avs_disable(void) {}
+static inline u32 avs_disable(void)
+{return 0; }
+static inline void avs_enable(u32 avscsr) {}
 #endif
 
 /*#define AVSDEBUG(x...) pr_info("AVS: " x);*/
@@ -60,9 +63,13 @@
 		put_cpu();			\
 	} while (0);
 
+/* AVSCSR(0x61) to enable CPU, V and L2 AVS module */
+
 #define AVS_ENABLE(cpu, x) do {			\
-		if (get_cpu() == (cpu))		\
+		if (get_cpu() == (cpu)) {       \
 			avs_reset_delays((x));	\
+			avs_enable(0x61);	\
+		}				\
 		put_cpu();			\
 	} while (0);
 
diff --git a/arch/arm/mach-msm/avs_hw.S b/arch/arm/mach-msm/avs_hw.S
index 1cc3ce0..efb9c47 100644
--- a/arch/arm/mach-msm/avs_hw.S
+++ b/arch/arm/mach-msm/avs_hw.S
@@ -102,23 +102,23 @@
 
 /*      Read r0=AVSDSCR */
 		mrc p15, 7, r0, c15, c0, 6
-
-/*      AVSCSR(0x61) to enable CPU, V and L2 AVS module  */
-		mov r3, #0x61
-		mcr p15, 7, r3, c15, c1, 7
-
 		bx lr
 
-
+	.global avs_enable
+avs_enable:
+/*	Restore the avs_scr register */
+		mcr p15, 7, r0, c15, c1, 7
+		bx lr
 
         .global avs_disable
 avs_disable:
 
+/*	Get the AVSCSR value */
+		mrc p15, 7, r0, c15, c1, 7
 /*      Clear AVSCSR */
-		mov r0, #0
-
+		mov r1, #0
 /*      Write AVSCSR */
-		mcr p15, 7, r0, c15, c1, 7
+		mcr p15, 7, r1, c15, c1, 7
 
 		bx lr
 
diff --git a/arch/arm/mach-msm/bam_dmux.c b/arch/arm/mach-msm/bam_dmux.c
index 7ba22f4..c475e2d 100644
--- a/arch/arm/mach-msm/bam_dmux.c
+++ b/arch/arm/mach-msm/bam_dmux.c
@@ -193,6 +193,7 @@
 static struct sps_mem_buffer rx_desc_mem_buf;
 static struct sps_register_event tx_register_event;
 static struct sps_register_event rx_register_event;
+static bool satellite_mode;
 
 static struct bam_ch_info bam_ch[BAM_DMUX_NUM_CHANNELS];
 static int bam_mux_initialized;
@@ -2074,7 +2075,7 @@
 	a2_props.options = SPS_BAM_OPT_IRQ_WAKEUP;
 	a2_props.num_pipes = A2_NUM_PIPES;
 	a2_props.summing_threshold = A2_SUMMING_THRESHOLD;
-	if (cpu_is_msm9615())
+	if (cpu_is_msm9615() || satellite_mode)
 		a2_props.manage = SPS_BAM_MGR_DEVICE_REMOTE;
 	/* need to free on tear down */
 	ret = sps_register_bam_device(&a2_props, &h);
@@ -2246,7 +2247,7 @@
 	a2_props.options = SPS_BAM_OPT_IRQ_WAKEUP;
 	a2_props.num_pipes = A2_NUM_PIPES;
 	a2_props.summing_threshold = A2_SUMMING_THRESHOLD;
-	if (cpu_is_msm9615())
+	if (cpu_is_msm9615() || satellite_mode)
 		a2_props.manage = SPS_BAM_MGR_DEVICE_REMOTE;
 	ret = sps_register_bam_device(&a2_props, &h);
 	if (ret < 0) {
@@ -2374,10 +2375,14 @@
 			pr_err("%s: irq field missing\n", __func__);
 			return -ENODEV;
 		}
-		DBG("%s: base:%p size:%x irq:%d\n", __func__,
+		satellite_mode = of_property_read_bool(pdev->dev.of_node,
+						"qcom,satellite-mode");
+
+		DBG("%s: base:%p size:%x irq:%d satellite:%d\n", __func__,
 							a2_phys_base,
 							a2_phys_size,
-							a2_bam_irq);
+							a2_bam_irq,
+							satellite_mode);
 	} else { /* fallback to default init data */
 		a2_phys_base = (void *)(A2_PHYS_BASE);
 		a2_phys_size = A2_PHYS_SIZE;
diff --git a/arch/arm/mach-msm/board-8064-camera.c b/arch/arm/mach-msm/board-8064-camera.c
index c79f82f..0a95e51 100644
--- a/arch/arm/mach-msm/board-8064-camera.c
+++ b/arch/arm/mach-msm/board-8064-camera.c
@@ -503,6 +503,34 @@
 	.i2c_mux_mode = MODE_L,
 };
 
+static struct msm_camera_sensor_flash_data flash_imx135 = {
+	.flash_type = MSM_CAMERA_FLASH_NONE,
+};
+
+static struct msm_camera_csi_lane_params imx135_csi_lane_params = {
+	.csi_lane_assign = 0xE4,
+	.csi_lane_mask = 0xF,
+};
+
+static struct msm_camera_sensor_platform_info sensor_board_info_imx135 = {
+	.mount_angle    = 90,
+	.cam_vreg = apq_8064_cam_vreg,
+	.num_vreg = ARRAY_SIZE(apq_8064_cam_vreg),
+	.gpio_conf = &apq8064_back_cam_gpio_conf,
+	.i2c_conf = &apq8064_back_cam_i2c_conf,
+	.csi_lane_params = &imx135_csi_lane_params,
+};
+
+static struct msm_camera_sensor_info msm_camera_sensor_imx135_data = {
+	.sensor_name    = "imx135",
+	.pdata  = &msm_camera_csi_device_data[0],
+	.flash_data = &flash_imx135,
+	.sensor_platform_info = &sensor_board_info_imx135,
+	.csi_if = 1,
+	.camera_type = BACK_CAMERA_2D,
+	.sensor_type = BAYER_SENSOR,
+};
+
 static struct msm_camera_sensor_flash_data flash_imx074 = {
 	.flash_type	= MSM_CAMERA_FLASH_LED,
 	.flash_src	= &msm_flash_src
@@ -700,6 +728,10 @@
 	.platform_data = &msm_camera_sensor_imx074_data,
 	},
 	{
+	I2C_BOARD_INFO("imx135", 0x10),
+	.platform_data = &msm_camera_sensor_imx135_data,
+	},
+	{
 	I2C_BOARD_INFO("mt9m114", 0x48),
 	.platform_data = &msm_camera_sensor_mt9m114_data,
 	},
diff --git a/arch/arm/mach-msm/board-8064-display.c b/arch/arm/mach-msm/board-8064-display.c
index 56c3241..b717973 100644
--- a/arch/arm/mach-msm/board-8064-display.c
+++ b/arch/arm/mach-msm/board-8064-display.c
@@ -50,6 +50,7 @@
 #define MSM_FB_OVERLAY1_WRITEBACK_SIZE (0)
 #endif  /* CONFIG_FB_MSM_OVERLAY1_WRITEBACK */
 
+#define AVTIMER_PHYSICAL_ADDRESS 0x28009008
 
 static struct resource msm_fb_resources[] = {
 	{
@@ -246,7 +247,7 @@
 
 static struct msm_panel_common_pdata mdp_pdata = {
 	.gpio = MDP_VSYNC_GPIO,
-	.mdp_max_clk = 200000000,
+	.mdp_max_clk = 266667000,
 	.mdp_bus_scale_table = &mdp_bus_scale_pdata,
 	.mdp_rev = MDP_REV_44,
 #ifdef CONFIG_MSM_MULTIMEDIA_USE_ION
@@ -255,6 +256,7 @@
 	.mem_hid = MEMTYPE_EBI1,
 #endif
 	.mdp_iommu_split_domain = 1,
+	.avtimer_phy = AVTIMER_PHYSICAL_ADDRESS,
 };
 
 void __init apq8064_mdp_writeback(struct memtype_reserve* reserve_table)
diff --git a/arch/arm/mach-msm/board-8064-gpiomux.c b/arch/arm/mach-msm/board-8064-gpiomux.c
index cb03d4b..fc44b18 100644
--- a/arch/arm/mach-msm/board-8064-gpiomux.c
+++ b/arch/arm/mach-msm/board-8064-gpiomux.c
@@ -479,6 +479,12 @@
 	.pull = GPIOMUX_PULL_NONE,
 };
 
+static struct gpiomux_setting gsbi6_spi_cfg = {
+	.func = GPIOMUX_FUNC_2,
+	.drv = GPIOMUX_DRV_16MA,
+	.pull = GPIOMUX_PULL_NONE,
+};
+
 static struct gpiomux_setting sx150x_suspended_cfg = {
 	.func = GPIOMUX_FUNC_GPIO,
 	.drv = GPIOMUX_DRV_8MA,
@@ -1514,6 +1520,33 @@
 	},
 };
 
+static struct msm_gpiomux_config mpq8064_gsbi6_spi_configs[] __initdata = {
+	{
+		.gpio      = 17,        /* GSBI6_0 SPI CLK */
+		.settings = {
+			[GPIOMUX_SUSPENDED] = &gsbi6_spi_cfg,
+		},
+	},
+	{
+		.gpio      = 16,        /* GSBI6_1 SPI CS */
+		.settings = {
+			[GPIOMUX_SUSPENDED] = &gsbi6_spi_cfg,
+		},
+	},
+	{
+		.gpio      = 15,        /* GSBI6_2 SPI MISO */
+		.settings = {
+			[GPIOMUX_SUSPENDED] = &gsbi6_spi_cfg,
+		},
+	},
+	{
+		.gpio      = 14,        /* GSBI6_3 SPI_MOSI */
+		.settings = {
+			[GPIOMUX_SUSPENDED] = &gsbi6_spi_cfg,
+		},
+	},
+};
+
 void __init apq8064_init_gpiomux(void)
 {
 	int rc;
@@ -1630,10 +1663,14 @@
 		msm_gpiomux_install(apq8064_mhl_configs,
 				ARRAY_SIZE(apq8064_mhl_configs));
 
-	 if (machine_is_mpq8064_cdp())
+	if (machine_is_mpq8064_cdp()) {
 		msm_gpiomux_install(mpq8064_ir_configs,
 				ARRAY_SIZE(mpq8064_ir_configs));
 
+		msm_gpiomux_install(mpq8064_gsbi6_spi_configs,
+				ARRAY_SIZE(mpq8064_gsbi6_spi_configs));
+	}
+
 #ifdef CONFIG_MMC_MSM_SDC2_SUPPORT
 	 msm_gpiomux_install(apq8064_sdc2_configs,
 			     ARRAY_SIZE(apq8064_sdc2_configs));
diff --git a/arch/arm/mach-msm/board-8064-gpu.c b/arch/arm/mach-msm/board-8064-gpu.c
index 3be7fc6..5ebb010 100644
--- a/arch/arm/mach-msm/board-8064-gpu.c
+++ b/arch/arm/mach-msm/board-8064-gpu.c
@@ -31,11 +31,14 @@
 };
 
 static struct msm_dcvs_core_info grp3d_core_info = {
-	.freq_tbl	= &grp3d_freq[0],
-	.core_param	= {
+	.freq_tbl		= &grp3d_freq[0],
+	.num_cores		= 1,
+	.sensors		= (int[]){0},
+	.thermal_poll_ms	= 60000,
+	.core_param		= {
 		.core_type	= MSM_DCVS_CORE_TYPE_GPU,
 	},
-	.algo_param	= {
+	.algo_param		= {
 		.disable_pc_threshold	= 0,
 		.em_win_size_min_us	= 100000,
 		.em_win_size_max_us	= 300000,
@@ -51,8 +54,7 @@
 		.ss_iobusy_conv		= 100,
 	},
 
-
-	.energy_coeffs	= {
+	.energy_coeffs		= {
 		.leakage_coeff_a	= -17720,
 		.leakage_coeff_b	= 37,
 		.leakage_coeff_c	= 3329,
@@ -63,7 +65,7 @@
 		.active_coeff_c		= 0
 	},
 
-	.power_param	= {
+	.power_param		= {
 		.current_temp	= 25,
 		.num_freq	= ARRAY_SIZE(grp3d_freq),
 	}
@@ -224,7 +226,7 @@
 			.io_fraction = 0,
 		},
 		{
-			.gpu_freq = 325000000,
+			.gpu_freq = 320000000,
 			.bus_freq = 3,
 			.io_fraction = 33,
 		},
@@ -279,8 +281,11 @@
 	if (SOCINFO_VERSION_MAJOR(version) == 2) {
 		kgsl_3d0_pdata.chipid = ADRENO_CHIPID(3, 2, 0, 2);
 	} else {
+		/* The bootloader has started returning 1.2 for chips that
+		   are either 1.1 or 1.2. To handle that and default any
+		   future revisions to this path, check for minor version >=1 */
 		if ((SOCINFO_VERSION_MAJOR(version) == 1) &&
-				(SOCINFO_VERSION_MINOR(version) == 1))
+				(SOCINFO_VERSION_MINOR(version) >= 1))
 			kgsl_3d0_pdata.chipid = ADRENO_CHIPID(3, 2, 0, 1);
 		else
 			kgsl_3d0_pdata.chipid = ADRENO_CHIPID(3, 2, 0, 0);
diff --git a/arch/arm/mach-msm/board-8064-pmic.c b/arch/arm/mach-msm/board-8064-pmic.c
index f6423c8..c973bd5 100644
--- a/arch/arm/mach-msm/board-8064-pmic.c
+++ b/arch/arm/mach-msm/board-8064-pmic.c
@@ -143,6 +143,7 @@
 
 static struct pm8xxx_gpio_init pm8921_mpq8064_hrd_gpios[] __initdata = {
 	PM8921_GPIO_OUTPUT(37, 0, LOW),	/* MUX1_SEL */
+	PM8921_GPIO_INPUT(40, PM_GPIO_PULL_UP_30), /* irq for sx150 exp2 */
 };
 
 static struct pm8xxx_gpio_init touchscreen_gpios[] __initdata = {
@@ -401,7 +402,8 @@
 	.uvd_thresh_voltage	= 4050,
 	.alarm_low_mv		= 3400,
 	.alarm_high_mv		= 4000,
-	.resume_voltage_delta	= 100,
+	.resume_voltage_delta	= 60,
+	.resume_charge_percent	= 99,
 	.term_current		= CHG_TERM_MA,
 	.cool_temp		= 10,
 	.warm_temp		= 40,
@@ -511,4 +513,7 @@
 	} else if (machine_is_apq8064_cdp()) {
 		apq8064_pm8921_chg_pdata.has_dc_supply = true;
 	}
+
+	if (!machine_is_apq8064_mtp() && !machine_is_apq8064_liquid())
+		apq8064_pm8921_chg_pdata.battery_less_hardware = 1;
 }
diff --git a/arch/arm/mach-msm/board-8064-regulator.c b/arch/arm/mach-msm/board-8064-regulator.c
index ef3c81d..851f7d9 100644
--- a/arch/arm/mach-msm/board-8064-regulator.c
+++ b/arch/arm/mach-msm/board-8064-regulator.c
@@ -64,6 +64,7 @@
 VREG_CONSUMERS(L8) = {
 	REGULATOR_SUPPLY("8921_l8",		NULL),
 	REGULATOR_SUPPLY("cam_vana",		"4-001a"),
+	REGULATOR_SUPPLY("cam_vana",		"4-0010"),
 	REGULATOR_SUPPLY("cam_vana",		"4-0048"),
 	REGULATOR_SUPPLY("cam_vana",		"4-006c"),
 	REGULATOR_SUPPLY("cam_vana",		"4-0034"),
@@ -84,6 +85,7 @@
 };
 VREG_CONSUMERS(L12) = {
 	REGULATOR_SUPPLY("cam_vdig",		"4-001a"),
+	REGULATOR_SUPPLY("cam_vdig",		"4-0010"),
 	REGULATOR_SUPPLY("cam_vdig",		"4-0048"),
 	REGULATOR_SUPPLY("cam_vdig",		"4-006c"),
 	REGULATOR_SUPPLY("cam_vdig",		"4-0034"),
@@ -102,6 +104,7 @@
 VREG_CONSUMERS(L16) = {
 	REGULATOR_SUPPLY("8921_l16",		NULL),
 	REGULATOR_SUPPLY("cam_vaf",		"4-001a"),
+	REGULATOR_SUPPLY("cam_vaf",		"4-0010"),
 	REGULATOR_SUPPLY("cam_vaf",		"4-0048"),
 	REGULATOR_SUPPLY("cam_vaf",		"4-006c"),
 	REGULATOR_SUPPLY("cam_vaf",		"4-0034"),
@@ -121,8 +124,6 @@
 };
 VREG_CONSUMERS(L23) = {
 	REGULATOR_SUPPLY("8921_l23",		NULL),
-	REGULATOR_SUPPLY("pll_vdd",		"pil_qdsp6v4.1"),
-	REGULATOR_SUPPLY("pll_vdd",		"pil_qdsp6v4.2"),
 	REGULATOR_SUPPLY("HSUSB_1p8",		"msm_ehci_host.0"),
 	REGULATOR_SUPPLY("HSUSB_1p8",		"msm_ehci_host.1"),
 };
@@ -141,15 +142,13 @@
 };
 VREG_CONSUMERS(L26) = {
 	REGULATOR_SUPPLY("8921_l26",		NULL),
-	REGULATOR_SUPPLY("core_vdd",		"pil_qdsp6v4.0"),
+	REGULATOR_SUPPLY("core_vdd",		"pil-q6v4-lpass"),
 };
 VREG_CONSUMERS(L27) = {
 	REGULATOR_SUPPLY("8921_l27",		NULL),
-	REGULATOR_SUPPLY("core_vdd",		"pil_qdsp6v4.2"),
 };
 VREG_CONSUMERS(L28) = {
 	REGULATOR_SUPPLY("8921_l28",		NULL),
-	REGULATOR_SUPPLY("core_vdd",		"pil_qdsp6v4.1"),
 };
 VREG_CONSUMERS(L29) = {
 	REGULATOR_SUPPLY("8921_l29",		NULL),
@@ -218,6 +217,7 @@
 VREG_CONSUMERS(LVS5) = {
 	REGULATOR_SUPPLY("8921_lvs5",		NULL),
 	REGULATOR_SUPPLY("cam_vio",		"4-001a"),
+	REGULATOR_SUPPLY("cam_vio",		"4-0010"),
 	REGULATOR_SUPPLY("cam_vio",		"4-0048"),
 	REGULATOR_SUPPLY("cam_vio",		"4-006c"),
 	REGULATOR_SUPPLY("cam_vio",		"4-0034"),
diff --git a/arch/arm/mach-msm/board-8064-storage.c b/arch/arm/mach-msm/board-8064-storage.c
index 379d7ae..28f7f63 100644
--- a/arch/arm/mach-msm/board-8064-storage.c
+++ b/arch/arm/mach-msm/board-8064-storage.c
@@ -383,6 +383,14 @@
 				apq8064_sdc3_pdata->pin_data->pad_data->\
 					drv->on[i].val = GPIO_CFG_10MA;
 		}
+		if (machine_is_mpq8064_hrd() || machine_is_mpq8064_dtv()) {
+			apq8064_sdc3_pdata->pin_data->pad_data->\
+				drv->on[0].val = GPIO_CFG_16MA;
+			apq8064_sdc3_pdata->pin_data->pad_data->\
+				drv->on[1].val = GPIO_CFG_10MA;
+			apq8064_sdc3_pdata->pin_data->pad_data->\
+				drv->on[2].val = GPIO_CFG_10MA;
+		}
 		apq8064_add_sdcc(3, apq8064_sdc3_pdata);
 	}
 
diff --git a/arch/arm/mach-msm/board-8064.c b/arch/arm/mach-msm/board-8064.c
index cc9dcbb..c6bcb6b 100644
--- a/arch/arm/mach-msm/board-8064.c
+++ b/arch/arm/mach-msm/board-8064.c
@@ -42,6 +42,7 @@
 #include <asm/hardware/gic.h>
 #include <asm/mach/mmc.h>
 #include <linux/platform_data/qcom_wcnss_device.h>
+#include <linux/ci-bridge-spi.h>
 
 #include <mach/board.h>
 #include <mach/msm_iomap.h>
@@ -450,59 +451,27 @@
 {
 #if defined(CONFIG_ION_MSM) && defined(CONFIG_MSM_MULTIMEDIA_USE_ION)
 	unsigned int i;
-	unsigned int reusable_count = 0;
 	unsigned int fixed_size = 0;
 	unsigned int fixed_low_size, fixed_middle_size, fixed_high_size;
 	unsigned long fixed_low_start, fixed_middle_start, fixed_high_start;
 
-	apq8064_fmem_pdata.size = 0;
-	apq8064_fmem_pdata.reserved_size_low = 0;
-	apq8064_fmem_pdata.reserved_size_high = 0;
-	apq8064_fmem_pdata.align = PAGE_SIZE;
 	fixed_low_size = 0;
 	fixed_middle_size = 0;
 	fixed_high_size = 0;
 
-	/* We only support 1 reusable heap. Check if more than one heap
-	 * is specified as reusable and set as non-reusable if found.
-	 */
-	for (i = 0; i < apq8064_ion_pdata.nr; ++i) {
-		const struct ion_platform_heap *heap =
-			&(apq8064_ion_pdata.heaps[i]);
-
-		if (heap->type == (enum ion_heap_type) ION_HEAP_TYPE_CP
-			&& heap->extra_data) {
-			struct ion_cp_heap_pdata *data = heap->extra_data;
-
-			reusable_count += (data->reusable) ? 1 : 0;
-
-			if (data->reusable && reusable_count > 1) {
-				pr_err("%s: Too many heaps specified as "
-					"reusable. Heap %s was not configured "
-					"as reusable.\n", __func__, heap->name);
-				data->reusable = 0;
-			}
-		}
-	}
-
 	for (i = 0; i < apq8064_ion_pdata.nr; ++i) {
 		const struct ion_platform_heap *heap =
 			&(apq8064_ion_pdata.heaps[i]);
 
 		if (heap->extra_data) {
 			int fixed_position = NOT_FIXED;
-			int mem_is_fmem = 0;
 
 			switch ((int)heap->type) {
 			case ION_HEAP_TYPE_CP:
-				mem_is_fmem = ((struct ion_cp_heap_pdata *)
-					heap->extra_data)->mem_is_fmem;
 				fixed_position = ((struct ion_cp_heap_pdata *)
 					heap->extra_data)->fixed_position;
 				break;
 			case ION_HEAP_TYPE_CARVEOUT:
-				mem_is_fmem = ((struct ion_co_heap_pdata *)
-					heap->extra_data)->mem_is_fmem;
 				fixed_position = ((struct ion_co_heap_pdata *)
 					heap->extra_data)->fixed_position;
 				break;
@@ -521,21 +490,12 @@
 				fixed_middle_size += heap->size;
 			else if (fixed_position == FIXED_HIGH)
 				fixed_high_size += heap->size;
-
-			if (mem_is_fmem)
-				apq8064_fmem_pdata.size += heap->size;
 		}
 	}
 
 	if (!fixed_size)
 		return;
 
-	if (apq8064_fmem_pdata.size) {
-		apq8064_fmem_pdata.reserved_size_low = fixed_low_size +
-								HOLE_SIZE;
-		apq8064_fmem_pdata.reserved_size_high = fixed_high_size;
-	}
-
 	/* Since the fixed area may be carved out of lowmem,
 	 * make sure the length is a multiple of 1M.
 	 */
@@ -605,6 +565,11 @@
 #endif
 }
 
+static void __init reserve_mpdcvs_memory(void)
+{
+	apq8064_reserve_table[MEMTYPE_EBI1].size += SZ_32K;
+}
+
 static void __init apq8064_calculate_reserve_sizes(void)
 {
 	size_pmem_devices();
@@ -613,6 +578,7 @@
 	reserve_mdp_memory();
 	reserve_rtb_memory();
 	reserve_cache_dump_memory();
+	reserve_mpdcvs_memory();
 }
 
 static struct reserve_info apq8064_reserve_info __initdata = {
@@ -703,19 +669,6 @@
 	apq8064_set_display_params(prim_panel_name, ext_panel_name,
 		ext_resolution);
 	msm_reserve();
-	if (apq8064_fmem_pdata.size) {
-#if defined(CONFIG_ION_MSM) && defined(CONFIG_MSM_MULTIMEDIA_USE_ION)
-		if (reserve_info->fixed_area_size) {
-			apq8064_fmem_pdata.phys =
-				reserve_info->fixed_area_start + MSM_MM_FW_SIZE;
-			pr_info("mm fw at %lx (fixed) size %x\n",
-				reserve_info->fixed_area_start, MSM_MM_FW_SIZE);
-			pr_info("fmem start %lx (fixed) size %lx\n",
-				apq8064_fmem_pdata.phys,
-				apq8064_fmem_pdata.size);
-		}
-#endif
-	}
 }
 
 static void __init place_movable_zone(void)
@@ -2430,6 +2383,7 @@
 static struct gpio_ir_recv_platform_data gpio_ir_recv_pdata = {
 	.gpio_nr = 88,
 	.active_low = 1,
+	.can_wakeup = true,
 };
 
 static struct platform_device gpio_ir_recv_pdev = {
@@ -2448,6 +2402,7 @@
 static struct platform_device *common_mpq_devices[] __initdata = {
 	&mpq_cpudai_sec_i2s_rx,
 	&mpq_cpudai_mi2s_tx,
+	&mpq_cpudai_pseudo,
 };
 
 static struct platform_device *common_i2s_devices[] __initdata = {
@@ -2556,6 +2511,7 @@
 	&apq8064_rpm_device,
 	&apq8064_rpm_log_device,
 	&apq8064_rpm_stat_device,
+	&apq8064_rpm_master_stat_device,
 	&apq_device_tz_log,
 	&msm_bus_8064_apps_fabric,
 	&msm_bus_8064_sys_fabric,
@@ -2568,7 +2524,6 @@
 	&msm_pil_vidc,
 	&msm_gss,
 	&apq8064_rtb_device,
-	&apq8064_cpu_idle_device,
 	&apq8064_msm_gov_device,
 	&apq8064_device_cache_erp,
 	&msm8960_device_ebi1_ch0_erp,
@@ -2740,12 +2695,22 @@
 	&msm8064_device_vcap,
 #endif
 	&rc_input_loopback_pdev,
+	&mpq8064_device_qup_spi_gsbi6,
 };
 
 static struct msm_spi_platform_data apq8064_qup_spi_gsbi5_pdata = {
 	.max_clock_speed = 1100000,
 };
 
+static struct msm_spi_platform_data mpq8064_qup_spi_gsbi6_pdata = {
+	.max_clock_speed = 1100000,
+};
+
+static struct ci_bridge_platform_data mpq8064_ci_bridge_pdata = {
+	.reset_pin = 260,
+	.interrupt_pin = 261,
+};
+
 #define KS8851_IRQ_GPIO		43
 
 static struct spi_board_info spi_board_info[] __initdata = {
@@ -2763,6 +2728,17 @@
 		.bus_num		= 0,
 		.chip_select		= 3,
 		.mode			= SPI_MODE_0,
+	}
+};
+
+static struct spi_board_info mpq8064_spi_board_info[] __initdata = {
+	{
+		.modalias		= "ci_bridge_spi",
+		.max_speed_hz		= 1000000,
+		.bus_num		= 1,
+		.chip_select		= 0,
+		.mode			= SPI_MODE_0,
+		.platform_data		= &mpq8064_ci_bridge_pdata,
 	},
 };
 
@@ -2992,6 +2968,83 @@
 	},
 };
 
+#define MPQ_HRD_HOME_GPIO	SX150X_EXP2_GPIO_BASE
+#define MPQ_HRD_VOL_UP_GPIO	(SX150X_EXP2_GPIO_BASE + 1)
+#define MPQ_HRD_VOL_DOWN_GPIO	(SX150X_EXP2_GPIO_BASE + 2)
+#define MPQ_HRD_RIGHT_GPIO	(SX150X_EXP2_GPIO_BASE + 3)
+#define MPQ_HRD_LEFT_GPIO	(SX150X_EXP2_GPIO_BASE + 4)
+#define MPQ_HRD_ENTER_GPIO	(SX150X_EXP2_GPIO_BASE + 5)
+
+static struct gpio_keys_button mpq_hrd_keys[] = {
+	{
+		.code		= KEY_HOME,
+		.gpio		= MPQ_HRD_HOME_GPIO,
+		.desc		= "home_key",
+		.active_low	= 1,
+		.type		= EV_KEY,
+		.wakeup		= 1,
+		.debounce_interval = 15,
+	},
+	{
+		.code		= KEY_VOLUMEUP,
+		.gpio		= MPQ_HRD_VOL_UP_GPIO,
+		.desc		= "volume_up_key",
+		.active_low	= 1,
+		.type		= EV_KEY,
+		.wakeup		= 1,
+		.debounce_interval = 15,
+	},
+	{
+		.code		= KEY_VOLUMEDOWN,
+		.gpio		= MPQ_HRD_VOL_DOWN_GPIO,
+		.desc		= "volume_down_key",
+		.active_low	= 1,
+		.type		= EV_KEY,
+		.wakeup		= 1,
+		.debounce_interval = 15,
+	},
+	{
+		.code		= KEY_RIGHT,
+		.gpio		= MPQ_HRD_RIGHT_GPIO,
+		.desc		= "right_key",
+		.active_low	= 1,
+		.type		= EV_KEY,
+		.wakeup		= 1,
+		.debounce_interval = 15,
+	},
+	{
+		.code		= KEY_LEFT,
+		.gpio		= MPQ_HRD_LEFT_GPIO,
+		.desc		= "left_key",
+		.active_low	= 1,
+		.type		= EV_KEY,
+		.wakeup		= 1,
+		.debounce_interval = 15,
+	},
+	{
+		.code		= KEY_ENTER,
+		.gpio		= MPQ_HRD_ENTER_GPIO,
+		.desc		= "enter_key",
+		.active_low	= 1,
+		.type		= EV_KEY,
+		.wakeup		= 1,
+		.debounce_interval = 15,
+	},
+};
+
+static struct gpio_keys_platform_data mpq_hrd_keys_pdata = {
+		.buttons	= mpq_hrd_keys,
+		.nbuttons	= ARRAY_SIZE(mpq_hrd_keys),
+};
+
+static struct platform_device mpq_hrd_keys_pdev = {
+		.name	= "gpio-keys",
+		.id	= -1,
+		.dev	= {
+			.platform_data = &mpq_hrd_keys_pdata,
+		},
+};
+
 static struct gpio_keys_button mpq_keys[] = {
 	{
 		.code           = KEY_VOLUMEDOWN,
@@ -3347,6 +3400,15 @@
 	msm_clock_init(&apq8064_clock_init_data);
 	apq8064_init_gpiomux();
 	apq8064_i2c_init();
+
+	/* configure sx150x parameters for HRD */
+	if (machine_is_mpq8064_hrd()) {
+		mpq8064_sx150x_pdata[SX150X_EXP2].irq_summary    =
+					PM8921_GPIO_IRQ(PM8921_IRQ_BASE, 40);
+		mpq8064_sx150x_pdata[SX150X_EXP2].io_pullup_ena  = 0xff;
+		mpq8064_sx150x_pdata[SX150X_EXP2].io_pulldn_ena  = 0x00;
+	}
+
 	register_i2c_devices();
 
 	apq8064_device_qup_spi_gsbi5.dev.platform_data =
@@ -3447,10 +3509,18 @@
 	apq8064_common_init();
 	if (machine_is_mpq8064_cdp() || machine_is_mpq8064_hrd() ||
 		machine_is_mpq8064_dtv()) {
+		gpio_ir_recv_pdata.swfi_latency =
+					msm_rpmrs_levels[0].latency_us;
 		enable_avc_i2c_bus();
 		msm_rotator_set_split_iommu_domain();
+
+		mpq8064_device_qup_spi_gsbi6.dev.platform_data =
+						&mpq8064_qup_spi_gsbi6_pdata;
+
 		platform_add_devices(mpq_devices, ARRAY_SIZE(mpq_devices));
 		mpq8064_pcie_init();
+		spi_register_board_info(mpq8064_spi_board_info,
+					ARRAY_SIZE(mpq8064_spi_board_info));
 	} else {
 		ethernet_init();
 		msm_rotator_set_split_iommu_domain();
@@ -3491,7 +3561,8 @@
 	if (machine_is_mpq8064_cdp()) {
 		platform_device_register(&mpq_gpio_keys_pdev);
 		platform_device_register(&mpq_keypad_device);
-	}
+	} else if (machine_is_mpq8064_hrd())
+		platform_device_register(&mpq_hrd_keys_pdev);
 }
 
 MACHINE_START(APQ8064_CDP, "QCT APQ8064 CDP")
diff --git a/arch/arm/mach-msm/board-8092.c b/arch/arm/mach-msm/board-8092.c
index 0471ff4..3e31f68 100644
--- a/arch/arm/mach-msm/board-8092.c
+++ b/arch/arm/mach-msm/board-8092.c
@@ -18,25 +18,20 @@
 #include <linux/of_fdt.h>
 #include <linux/of_irq.h>
 #include <asm/hardware/gic.h>
-#include <asm/arch_timer.h>
 #include <asm/mach/arch.h>
 #include <asm/mach/time.h>
 #include <mach/socinfo.h>
 #include <mach/board.h>
-
+#include <mach/msm_memtypes.h>
+#include <mach/qpnp-int.h>
 #include <linux/io.h>
 #include <linux/gpio.h>
 #include <linux/irq.h>
 #include <linux/irqdomain.h>
 
+#include "board-dt.h"
 #include "clock.h"
 
-static struct of_device_id irq_match[] __initdata  = {
-	{ .compatible = "qcom,msm-qgic2", .data = gic_of_init, },
-	{ .compatible = "qcom,msm-gpio", .data = msm_gpio_of_init, },
-	{}
-};
-
 static struct clk_lookup msm_clocks_dummy[] = {
 	CLK_DUMMY("core_clk",   BLSP1_UART_CLK, "msm_serial_hsl.0", OFF),
 	CLK_DUMMY("iface_clk",  BLSP1_UART_CLK, "msm_serial_hsl.0", OFF),
@@ -47,26 +42,39 @@
 	.size = ARRAY_SIZE(msm_clocks_dummy),
 };
 
-void __init mpq8092_init_irq(void)
-{
-	of_irq_init(irq_match);
-}
-
-static void __init mpq8092_dt_timer_init(void)
-{
-	arch_timer_of_register();
-}
-
-static struct sys_timer mpq8092_dt_timer = {
-	.init = mpq8092_dt_timer_init
+static struct memtype_reserve mpq8092_reserve_table[] __initdata = {
+	[MEMTYPE_SMI] = {
+	},
+	[MEMTYPE_EBI0] = {
+		.flags  =       MEMTYPE_FLAGS_1M_ALIGN,
+		},
+	[MEMTYPE_EBI1] = {
+		.flags  =       MEMTYPE_FLAGS_1M_ALIGN,
+		},
 };
 
-static void __init mpq8092_dt_init_irq(void)
+static int mpq8092_paddr_to_memtype(unsigned int paddr)
 {
-	mpq8092_init_irq();
+	return MEMTYPE_EBI1;
 }
 
-static void __init mpq8092_dt_map_io(void)
+static struct reserve_info mpq8092_reserve_info __initdata = {
+	.memtype_reserve_table = mpq8092_reserve_table,
+	.paddr_to_memtype = mpq8092_paddr_to_memtype,
+};
+
+static void __init mpq8092_early_memory(void)
+{
+	reserve_info = &mpq8092_reserve_info;
+	of_scan_flat_dt(dt_scan_for_memory_reserve, mpq8092_reserve_table);
+}
+
+static void __init mpq8092_dt_reserve(void)
+{
+	msm_reserve();
+}
+
+static void __init mpq8092_map_io(void)
 {
 	msm_map_mpq8092_io();
 	if (socinfo_init() < 0)
@@ -77,21 +85,21 @@
 static struct of_dev_auxdata mpq8092_auxdata_lookup[] __initdata = {
 	OF_DEV_AUXDATA("qcom,msm-lsuart-v14", 0xF991F000, \
 			"msm_serial_hsl.0", NULL),
+	OF_DEV_AUXDATA("qcom,spmi-pmic-arb", 0xFC4C0000, \
+			"spmi-pmic-arb.0", NULL),
+	OF_DEV_AUXDATA("qcom,msm-sdcc", 0xF9824000, \
+			"msm_sdcc.1", NULL),
+	OF_DEV_AUXDATA("qcom,msm-sdcc", 0xF98A4000, \
+			"msm_sdcc.2", NULL),
 	{}
 };
 
-static void __init mpq8092_init(struct of_dev_auxdata **adata)
+static void __init mpq8092_init(void)
 {
+	struct of_dev_auxdata *adata = mpq8092_auxdata_lookup;
+
 	mpq8092_init_gpiomux();
-	*adata = mpq8092_auxdata_lookup;
 	msm_clock_init(&mpq8092_clock_init_data);
-}
-
-static void __init mpq8092_dt_init(void)
-{
-	struct of_dev_auxdata *adata = NULL;
-
-	mpq8092_init(&adata);
 	of_platform_populate(NULL, of_default_bus_match_table, adata, NULL);
 }
 
@@ -100,11 +108,13 @@
 	NULL
 };
 
-DT_MACHINE_START(MSM_DT, "Qualcomm MSM (Flattened Device Tree)")
-	.map_io = mpq8092_dt_map_io,
-	.init_irq = mpq8092_dt_init_irq,
-	.init_machine = mpq8092_dt_init,
+DT_MACHINE_START(MSM8092_DT, "Qualcomm MSM 8092 (Flattened Device Tree)")
+	.map_io = mpq8092_map_io,
+	.init_irq = msm_dt_init_irq_nompm,
+	.init_machine = mpq8092_init,
 	.handle_irq = gic_handle_irq,
-	.timer = &mpq8092_dt_timer,
+	.timer = &msm_dt_timer,
 	.dt_compat = mpq8092_dt_match,
+	.reserve = mpq8092_dt_reserve,
+	.init_very_early = mpq8092_early_memory,
 MACHINE_END
diff --git a/arch/arm/mach-msm/board-8226.c b/arch/arm/mach-msm/board-8226.c
index 9545c7a..33f18a2 100644
--- a/arch/arm/mach-msm/board-8226.c
+++ b/arch/arm/mach-msm/board-8226.c
@@ -28,7 +28,6 @@
 #endif
 #include <asm/mach/map.h>
 #include <asm/hardware/gic.h>
-#include <asm/arch_timer.h>
 #include <asm/mach/arch.h>
 #include <asm/mach/time.h>
 #include <mach/board.h>
@@ -41,11 +40,14 @@
 #include <mach/socinfo.h>
 #include <mach/board.h>
 #include <mach/clk-provider.h>
+#include "board-dt.h"
 #include "clock.h"
 
 static struct clk_lookup msm_clocks_dummy[] = {
-	CLK_DUMMY("core_clk",   BLSP1_UART_CLK, "msm_serial_hsl.0", OFF),
-	CLK_DUMMY("iface_clk",  BLSP1_UART_CLK, "msm_serial_hsl.0", OFF),
+	CLK_DUMMY("core_clk",   BLSP1_UART_CLK, "f991f000.serial", OFF),
+	CLK_DUMMY("iface_clk",  BLSP1_UART_CLK, "f991f000.serial", OFF),
+	CLK_DUMMY("iface_clk",  HSUSB_IFACE_CLK, "f9a55000.usb", OFF),
+	CLK_DUMMY("core_clk",	HSUSB_CORE_CLK, "f9a55000.usb", OFF),
 };
 
 struct clock_init_data msm_dummy_clock_init_data __initdata = {
@@ -53,50 +55,15 @@
 	.size = ARRAY_SIZE(msm_clocks_dummy),
 };
 
-static struct of_device_id irq_match[] __initdata  = {
-	{ .compatible = "qcom,msm-qgic2", .data = gic_of_init, },
-	{ .compatible = "qcom,msm-gpio", .data = msm_gpio_of_init, },
-	{}
-};
-
-static struct of_dev_auxdata msm8226_auxdata_lookup[] __initdata = {
-	OF_DEV_AUXDATA("qcom,msm-lsuart-v14", 0xF991F000, \
-			"msm_serial_hsl.0", NULL),
-	{}
-};
-
-static void __init msm8226_dt_timer_init(void)
-{
-	arch_timer_of_register();
-}
-
-static struct sys_timer msm8226_dt_timer = {
-	.init = msm8226_dt_timer_init
-};
-
-void __init msm8226_init_irq(void)
-{
-	of_irq_init(irq_match);
-}
-
-void __init msm8226_init(struct of_dev_auxdata **adata)
+void __init msm8226_init(void)
 {
 	msm8226_init_gpiomux();
-
 	msm_clock_init(&msm_dummy_clock_init_data);
 
-	*adata = msm8226_auxdata_lookup;
-}
-
-void __init msm8226_dt_init(void)
-{
-	struct of_dev_auxdata *adata = NULL;
-	msm8226_init(&adata);
-
 	if (socinfo_init() < 0)
 		pr_err("%s: socinfo_init() failed\n", __func__);
 
-	of_platform_populate(NULL, of_default_bus_match_table, adata, NULL);
+	of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
 }
 
 
@@ -107,9 +74,9 @@
 
 DT_MACHINE_START(MSM8226_DT, "Qualcomm MSM 8226 (Flattened Device Tree)")
 	.map_io = msm_map_msm8226_io,
-	.init_irq = msm8226_init_irq,
-	.init_machine = msm8226_dt_init,
+	.init_irq = msm_dt_init_irq_nompm,
+	.init_machine = msm8226_init,
 	.handle_irq = gic_handle_irq,
-	.timer = &msm8226_dt_timer,
+	.timer = &msm_dt_timer,
 	.dt_compat = msm8226_dt_match,
 MACHINE_END
diff --git a/arch/arm/mach-msm/board-8910.c b/arch/arm/mach-msm/board-8910.c
new file mode 100644
index 0000000..b79ee0b
--- /dev/null
+++ b/arch/arm/mach-msm/board-8910.c
@@ -0,0 +1,95 @@
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/gpio.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+#include <linux/of_fdt.h>
+#include <linux/of_irq.h>
+#include <linux/memory.h>
+#include <asm/mach/map.h>
+#include <asm/arch_timer.h>
+#include <asm/hardware/gic.h>
+#include <asm/mach/arch.h>
+#include <asm/mach/time.h>
+#include <mach/board.h>
+#include <mach/gpiomux.h>
+#include <mach/msm_iomap.h>
+#include <mach/restart.h>
+#ifdef CONFIG_ION_MSM
+#include <mach/ion.h>
+#endif
+#include <mach/socinfo.h>
+#include <mach/board.h>
+#include <mach/clk-provider.h>
+#include "board-dt.h"
+#include "clock.h"
+
+static struct clk_lookup msm_clocks_dummy[] = {
+	CLK_DUMMY("core_clk",   BLSP1_UART_CLK, "f991f000.serial", OFF),
+	CLK_DUMMY("iface_clk",  BLSP1_UART_CLK, "f991f000.serial", OFF),
+	CLK_DUMMY("iface_clk",  HSUSB_IFACE_CLK, "f9a55000.usb", OFF),
+	CLK_DUMMY("core_clk",	HSUSB_CORE_CLK, "f9a55000.usb", OFF),
+	CLK_DUMMY("iface_clk",	NULL,		"msm_sdcc.1", OFF),
+	CLK_DUMMY("core_clk",	NULL,		"msm_sdcc.1", OFF),
+	CLK_DUMMY("bus_clk",	NULL,		"msm_sdcc.1", OFF),
+	CLK_DUMMY("iface_clk",	NULL,		"msm_sdcc.2", OFF),
+	CLK_DUMMY("core_clk",	NULL,		"msm_sdcc.2", OFF),
+	CLK_DUMMY("bus_clk",	NULL,		"msm_sdcc.2", OFF),
+};
+
+struct clock_init_data msm_dummy_clock_init_data __initdata = {
+	.table = msm_clocks_dummy,
+	.size = ARRAY_SIZE(msm_clocks_dummy),
+};
+
+static struct of_dev_auxdata msm8910_auxdata_lookup[] __initdata = {
+	OF_DEV_AUXDATA("qcom,msm-sdcc", 0xF9824000, \
+			"msm_sdcc.1", NULL),
+	OF_DEV_AUXDATA("qcom,msm-sdcc", 0xF98A4000, \
+			"msm_sdcc.2", NULL),
+	{}
+};
+
+void __init msm8910_init(void)
+{
+	struct of_dev_auxdata *adata = msm8910_auxdata_lookup;
+
+	msm_clock_init(&msm_dummy_clock_init_data);
+
+	if (socinfo_init() < 0)
+		pr_err("%s: socinfo_init() failed\n", __func__);
+
+	of_platform_populate(NULL, of_default_bus_match_table, adata, NULL);
+}
+
+static const char *msm8910_dt_match[] __initconst = {
+	"qcom,msm8910",
+	NULL
+};
+
+DT_MACHINE_START(MSM8910_DT, "Qualcomm MSM 8910 (Flattened Device Tree)")
+	.map_io = msm_map_msm8910_io,
+	.init_irq = msm_dt_init_irq_nompm,
+	.init_machine = msm8910_init,
+	.handle_irq = gic_handle_irq,
+	.timer = &msm_dt_timer,
+	.dt_compat = msm8910_dt_match,
+	.restart = msm_restart,
+MACHINE_END
diff --git a/arch/arm/mach-msm/board-8930-gpiomux.c b/arch/arm/mach-msm/board-8930-gpiomux.c
index fcb5abd..cf44e08 100644
--- a/arch/arm/mach-msm/board-8930-gpiomux.c
+++ b/arch/arm/mach-msm/board-8930-gpiomux.c
@@ -69,7 +69,7 @@
 
 static struct gpiomux_setting cdc_mclk = {
 	.func = GPIOMUX_FUNC_1,
-	.drv = GPIOMUX_DRV_8MA,
+	.drv = GPIOMUX_DRV_2MA,
 	.pull = GPIOMUX_PULL_NONE,
 };
 
diff --git a/arch/arm/mach-msm/board-8930-gpu.c b/arch/arm/mach-msm/board-8930-gpu.c
index 99a5a34..578c665 100644
--- a/arch/arm/mach-msm/board-8930-gpu.c
+++ b/arch/arm/mach-msm/board-8930-gpu.c
@@ -163,10 +163,18 @@
 {
 	unsigned int version = socinfo_get_version();
 
+	/* Set the turbo speed for the AA and AB respectively */
+
 	if (cpu_is_msm8930aa())
 		kgsl_3d0_pdata.pwrlevel[0].gpu_freq = 450000000;
+	else if (cpu_is_msm8930ab())
+		kgsl_3d0_pdata.pwrlevel[0].gpu_freq = 500000000;
 
-	if ((SOCINFO_VERSION_MAJOR(version) == 1) &&
+	/* Set up the chip ID based on the SoC version */
+
+	if (cpu_is_msm8930ab())
+		kgsl_3d0_pdata.chipid = ADRENO_CHIPID(3, 0, 5, 3);
+	else if ((SOCINFO_VERSION_MAJOR(version) == 1) &&
 		(SOCINFO_VERSION_MINOR(version) == 2))
 		kgsl_3d0_pdata.chipid = ADRENO_CHIPID(3, 0, 5, 2);
 	else
diff --git a/arch/arm/mach-msm/board-8930-pmic.c b/arch/arm/mach-msm/board-8930-pmic.c
index 402aec4..0c7666b 100644
--- a/arch/arm/mach-msm/board-8930-pmic.c
+++ b/arch/arm/mach-msm/board-8930-pmic.c
@@ -322,7 +322,8 @@
 	.uvd_thresh_voltage	= 4050,
 	.alarm_low_mv		= 3400,
 	.alarm_high_mv		= 4000,
-	.resume_voltage_delta	= 100,
+	.resume_voltage_delta	= 60,
+	.resume_charge_percent	= 99,
 	.term_current		= CHG_TERM_MA,
 	.cool_temp		= 10,
 	.warm_temp		= 40,
@@ -595,4 +596,7 @@
 		else if (machine_is_msm8930_cdp())
 			pm8921_chg_pdata.has_dc_supply = true;
 	}
+
+	if (!machine_is_msm8930_mtp())
+		pm8921_chg_pdata.battery_less_hardware = 1;
 }
diff --git a/arch/arm/mach-msm/board-8930-regulator-pm8038.c b/arch/arm/mach-msm/board-8930-regulator-pm8038.c
index 208f15b..727c4c6 100644
--- a/arch/arm/mach-msm/board-8930-regulator-pm8038.c
+++ b/arch/arm/mach-msm/board-8930-regulator-pm8038.c
@@ -97,6 +97,8 @@
 	REGULATOR_SUPPLY("CDC_VDDA_RX",		"sitar1p1-slim"),
 	REGULATOR_SUPPLY("vddp",		"0-0048"),
 	REGULATOR_SUPPLY("mhl_iovcc18",		"0-0039"),
+	REGULATOR_SUPPLY("vdd-io",		"spi0.0"),
+	REGULATOR_SUPPLY("vdd-phy",		"spi0.0"),
 };
 VREG_CONSUMERS(L12) = {
 	REGULATOR_SUPPLY("8038_l12",		NULL),
@@ -117,7 +119,7 @@
 };
 VREG_CONSUMERS(L16) = {
 	REGULATOR_SUPPLY("8038_l16",		NULL),
-	REGULATOR_SUPPLY("core_vdd",		"pil_qdsp6v4.2"),
+	REGULATOR_SUPPLY("sw_core_vdd",		"pil-q6v4-modem"),
 };
 VREG_CONSUMERS(L17) = {
 	REGULATOR_SUPPLY("8038_l17",		NULL),
@@ -127,7 +129,7 @@
 };
 VREG_CONSUMERS(L19) = {
 	REGULATOR_SUPPLY("8038_l19",		NULL),
-	REGULATOR_SUPPLY("core_vdd",		"pil_qdsp6v4.1"),
+	REGULATOR_SUPPLY("fw_core_vdd",		"pil-q6v4-modem"),
 };
 VREG_CONSUMERS(L20) = {
 	REGULATOR_SUPPLY("8038_l20",		NULL),
@@ -151,8 +153,7 @@
 	REGULATOR_SUPPLY("hdmi_avdd",		"hdmi_msm.0"),
 	REGULATOR_SUPPLY("hdmi_vcc",		"hdmi_msm.0"),
 	REGULATOR_SUPPLY("pll_vdd",		"pil_riva"),
-	REGULATOR_SUPPLY("pll_vdd",		"pil_qdsp6v4.1"),
-	REGULATOR_SUPPLY("pll_vdd",		"pil_qdsp6v4.2"),
+	REGULATOR_SUPPLY("pll_vdd",		"pil-q6v4-modem"),
 };
 VREG_CONSUMERS(L24) = {
 	REGULATOR_SUPPLY("8038_l24",		NULL),
@@ -166,7 +167,7 @@
 };
 VREG_CONSUMERS(L27) = {
 	REGULATOR_SUPPLY("8038_l27",		NULL),
-	REGULATOR_SUPPLY("core_vdd",		"pil_qdsp6v4.0"),
+	REGULATOR_SUPPLY("core_vdd",		"pil-q6v4-lpass"),
 };
 VREG_CONSUMERS(S1) = {
 	REGULATOR_SUPPLY("8038_s1",		NULL),
diff --git a/arch/arm/mach-msm/board-8930-regulator-pm8917.c b/arch/arm/mach-msm/board-8930-regulator-pm8917.c
index db40e5d..33e38ab 100644
--- a/arch/arm/mach-msm/board-8930-regulator-pm8917.c
+++ b/arch/arm/mach-msm/board-8930-regulator-pm8917.c
@@ -196,6 +196,8 @@
 	REGULATOR_SUPPLY("mhl_iovcc18",		"0-0039"),
 	REGULATOR_SUPPLY("CDC_VDD_CP",		"sitar-slim"),
 	REGULATOR_SUPPLY("CDC_VDD_CP",		"sitar1p1-slim"),
+	REGULATOR_SUPPLY("vdd-io",		"spi0.0"),
+	REGULATOR_SUPPLY("vdd-phy",		"spi0.0"),
 };
 VREG_CONSUMERS(S5) = {
 	REGULATOR_SUPPLY("8917_s5",		NULL),
@@ -547,11 +549,11 @@
 	RPM_SMPS(S2, 0, 1, 0, 1300000, 1300000, NULL,      0, 1p60, NONE, NONE),
 	RPM_SMPS(S3, 0, 1, 1,  500000, 1150000, NULL, 100000, 4p80, AUTO, LPM),
 	RPM_SMPS(S4, 1, 1, 0, 1800000, 1800000, NULL, 100000, 1p60, AUTO, LPM),
-	RPM_SMPS(S7, 0, 1, 0, 1150000, 1150000, NULL, 100000, 3p20, NONE, NONE),
+	RPM_SMPS(S7, 0, 1, 0, 1150000, 1150000, NULL, 100000, 3p20, AUTO, AUTO),
 	RPM_SMPS(S8, 1, 1, 1, 2050000, 2050000, NULL, 100000, 1p60, NONE, NONE),
 
 	/*	ID     a_on pd ss min_uV   max_uV  supply  sys_uA init_ip */
-	RPM_LDO(L1,	 1, 1, 0, 1050000, 1050000, "8917_s4", 0, 10000),
+	RPM_LDO(L1,	 0, 1, 0, 1050000, 1050000, "8917_s4", 0, 10000),
 	RPM_LDO(L2,	 0, 1, 0, 1200000, 1200000, "8917_s4", 0, 0),
 	RPM_LDO(L3,	 0, 1, 0, 3075000, 3075000, NULL,      0, 0),
 	RPM_LDO(L4,	 1, 1, 0, 1800000, 1800000, NULL,      10000, 10000),
diff --git a/arch/arm/mach-msm/board-8930.c b/arch/arm/mach-msm/board-8930.c
index a6a90a7..1aa9c10 100644
--- a/arch/arm/mach-msm/board-8930.c
+++ b/arch/arm/mach-msm/board-8930.c
@@ -133,7 +133,7 @@
 #endif
 
 #define MSM_PMEM_ADSP_SIZE         0x7800000
-#define MSM_PMEM_AUDIO_SIZE        0x4CF000
+#define MSM_PMEM_AUDIO_SIZE        0x314000
 #ifdef CONFIG_FB_MSM_HDMI_AS_PRIMARY
 #define MSM_PMEM_SIZE 0x4000000 /* 64 Mbytes */
 #else
@@ -498,59 +498,27 @@
 {
 #if defined(CONFIG_ION_MSM) && defined(CONFIG_MSM_MULTIMEDIA_USE_ION)
 	unsigned int i;
-	unsigned int reusable_count = 0;
 	unsigned int fixed_size = 0;
 	unsigned int fixed_low_size, fixed_middle_size, fixed_high_size;
 	unsigned long fixed_low_start, fixed_middle_start, fixed_high_start;
 
-	msm8930_fmem_pdata.size = 0;
-	msm8930_fmem_pdata.reserved_size_low = 0;
-	msm8930_fmem_pdata.reserved_size_high = 0;
-	msm8930_fmem_pdata.align = PAGE_SIZE;
 	fixed_low_size = 0;
 	fixed_middle_size = 0;
 	fixed_high_size = 0;
 
-	/* We only support 1 reusable heap. Check if more than one heap
-	 * is specified as reusable and set as non-reusable if found.
-	 */
-	for (i = 0; i < msm8930_ion_pdata.nr; ++i) {
-		const struct ion_platform_heap *heap =
-						&(msm8930_ion_pdata.heaps[i]);
-
-		if (heap->type == (enum ion_heap_type) ION_HEAP_TYPE_CP
-			&& heap->extra_data) {
-			struct ion_cp_heap_pdata *data = heap->extra_data;
-
-			reusable_count += (data->reusable) ? 1 : 0;
-
-			if (data->reusable && reusable_count > 1) {
-				pr_err("%s: Too many heaps specified as "
-					"reusable. Heap %s was not configured "
-					"as reusable.\n", __func__, heap->name);
-				data->reusable = 0;
-			}
-		}
-	}
-
 	for (i = 0; i < msm8930_ion_pdata.nr; ++i) {
 		const struct ion_platform_heap *heap =
 						&(msm8930_ion_pdata.heaps[i]);
 
 		if (heap->extra_data) {
 			int fixed_position = NOT_FIXED;
-			int mem_is_fmem = 0;
 
 			switch ((int) heap->type) {
 			case ION_HEAP_TYPE_CP:
-				mem_is_fmem = ((struct ion_cp_heap_pdata *)
-					heap->extra_data)->mem_is_fmem;
 				fixed_position = ((struct ion_cp_heap_pdata *)
 					heap->extra_data)->fixed_position;
 				break;
 			case ION_HEAP_TYPE_CARVEOUT:
-				mem_is_fmem = ((struct ion_co_heap_pdata *)
-					heap->extra_data)->mem_is_fmem;
 				fixed_position = ((struct ion_co_heap_pdata *)
 					heap->extra_data)->fixed_position;
 				break;
@@ -570,20 +538,12 @@
 			else if (fixed_position == FIXED_HIGH)
 				fixed_high_size += heap->size;
 
-			if (mem_is_fmem)
-				msm8930_fmem_pdata.size += heap->size;
 		}
 	}
 
 	if (!fixed_size)
 		return;
 
-	if (msm8930_fmem_pdata.size) {
-		msm8930_fmem_pdata.reserved_size_low = fixed_low_size +
-							HOLE_SIZE;
-		msm8930_fmem_pdata.reserved_size_high = fixed_high_size;
-	}
-
 	/* Since the fixed area may be carved out of lowmem,
 	 * make sure the length is a multiple of 1M.
 	 */
@@ -755,18 +715,6 @@
 {
 	msm8930_set_display_params(prim_panel_name, ext_panel_name);
 	msm_reserve();
-	if (msm8930_fmem_pdata.size) {
-#if defined(CONFIG_ION_MSM) && defined(CONFIG_MSM_MULTIMEDIA_USE_ION)
-		if (reserve_info->fixed_area_size) {
-			msm8930_fmem_pdata.phys =
-				reserve_info->fixed_area_start + MSM_MM_FW_SIZE;
-		pr_info("mm fw at %lx (fixed) size %x\n",
-			reserve_info->fixed_area_start, MSM_MM_FW_SIZE);
-		pr_info("fmem start %lx (fixed) size %lx\n",
-			msm8930_fmem_pdata.phys, msm8930_fmem_pdata.size);
-		}
-#endif
-	}
 }
 
 static int msm8930_change_memory_power(u64 start, u64 size,
@@ -814,7 +762,7 @@
 	.regulator = {
 	{
 		.name = "CDC_VDD_CP",
-		.min_uV = 1800000,
+		.min_uV = 2200000,
 		.max_uV = 2200000,
 		.optimum_uA = WCD9XXX_CDC_VDDA_CP_CUR_MAX,
 	},
@@ -880,7 +828,7 @@
 	.regulator = {
 	{
 		.name = "CDC_VDD_CP",
-		.min_uV = 1800000,
+		.min_uV = 2200000,
 		.max_uV = 2200000,
 		.optimum_uA = WCD9XXX_CDC_VDDA_CP_CUR_MAX,
 	},
@@ -2347,8 +2295,6 @@
 
 static struct platform_device *common_devices[] __initdata = {
 	&msm_8960_q6_lpass,
-	&msm_8960_q6_mss_fw,
-	&msm_8960_q6_mss_sw,
 	&msm_8960_riva,
 	&msm_pil_tzapps,
 	&msm_pil_vidc,
@@ -2398,6 +2344,7 @@
 	&msm8930_rpm_log_device,
 	&msm8930_rpm_rbcpr_device,
 	&msm8930_rpm_stat_device,
+	&msm8930_rpm_master_stat_device,
 #ifdef CONFIG_ION_MSM
 	&msm8930_ion_dev,
 #endif
@@ -2413,7 +2360,6 @@
 	&gpio_keys_8930,
 #endif
 	&msm8930_rtb_device,
-	&msm8930_cpu_idle_device,
 	&msm_bus_8930_apps_fabric,
 	&msm_bus_8930_sys_fabric,
 	&msm_bus_8930_mm_fabric,
@@ -2767,11 +2713,34 @@
 #endif
 }
 
+/*Modify the WCD9xxx platform data to support supplies from PM8917 */
+static void __init msm8930_pm8917_wcd9xxx_pdata_fixup(
+		struct wcd9xxx_pdata *cdc_pdata)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(cdc_pdata->regulator); i++) {
+
+		if (cdc_pdata->regulator[i].name != NULL
+			&& strncmp(cdc_pdata->regulator[i].name,
+				"CDC_VDD_CP", 10) == 0) {
+			cdc_pdata->regulator[i].min_uV =
+				cdc_pdata->regulator[i].max_uV = 1800000;
+			pr_info("%s: CDC_VDD_CP forced to 1.8 volts for PM8917\n",
+				__func__);
+			return;
+		}
+	}
+}
+
 /* Modify platform data values to match requirements for PM8917. */
 static void __init msm8930_pm8917_pdata_fixup(void)
 {
 	struct acpuclk_platform_data *pdata;
 
+	msm8930_pm8917_wcd9xxx_pdata_fixup(&sitar_platform_data);
+	msm8930_pm8917_wcd9xxx_pdata_fixup(&sitar1p1_platform_data);
+
 	mhl_platform_data.gpio_mhl_power = MHL_POWER_GPIO_PM8917;
 
 	gpio_keys_8930_pdata.buttons = keys_8930_pm8917;
@@ -2878,6 +2847,10 @@
 	else
 		msm8930_pm8917_gpio_mpp_init();
 #endif
+	/* Don't add modem devices on APQ targets */
+	if (socinfo_get_id() != 119 && socinfo_get_id() != 157
+	    && socinfo_get_id() != 160)
+		platform_device_register(&msm_8960_q6_mss);
 	platform_add_devices(cdp_devices, ARRAY_SIZE(cdp_devices));
 #ifdef CONFIG_MSM_CAMERA
 	msm8930_init_cam();
diff --git a/arch/arm/mach-msm/board-8960-camera.c b/arch/arm/mach-msm/board-8960-camera.c
index 9bb6e09..88fd527 100644
--- a/arch/arm/mach-msm/board-8960-camera.c
+++ b/arch/arm/mach-msm/board-8960-camera.c
@@ -752,6 +752,33 @@
 	.eeprom_info = &imx091_eeprom_info,
 };
 
+static struct msm_camera_sensor_flash_data flash_imx135 = {
+	.flash_type = MSM_CAMERA_FLASH_NONE,
+};
+
+static struct msm_camera_csi_lane_params imx135_csi_lane_params = {
+	.csi_lane_assign = 0xE4,
+	.csi_lane_mask = 0xF,
+};
+
+static struct msm_camera_sensor_platform_info sensor_board_info_imx135 = {
+	.mount_angle = 90,
+	.cam_vreg = msm_8960_cam_vreg,
+	.num_vreg = ARRAY_SIZE(msm_8960_cam_vreg),
+	.gpio_conf = &msm_8960_back_cam_gpio_conf,
+	.csi_lane_params = &imx135_csi_lane_params,
+};
+
+static struct msm_camera_sensor_info msm_camera_sensor_imx135_data = {
+	.sensor_name = "imx135",
+	.pdata = &msm_camera_csi_device_data[0],
+	.flash_data = &flash_imx135,
+	.sensor_platform_info = &sensor_board_info_imx135,
+	.csi_if = 1,
+	.camera_type = BACK_CAMERA_2D,
+	.sensor_type = BAYER_SENSOR,
+};
+
 static struct pm8xxx_mpp_config_data privacy_light_on_config = {
 	.type		= PM8XXX_MPP_TYPE_SINK,
 	.level		= PM8XXX_MPP_CS_OUT_5MA,
@@ -838,6 +865,10 @@
 	.platform_data = &msm_camera_sensor_ov2720_data,
 	},
 	{
+	I2C_BOARD_INFO("imx135", 0x10),
+	.platform_data = &msm_camera_sensor_imx135_data,
+	},
+	{
 	I2C_BOARD_INFO("mt9m114", 0x48),
 	.platform_data = &msm_camera_sensor_mt9m114_data,
 	},
diff --git a/arch/arm/mach-msm/board-8960-gpiomux.c b/arch/arm/mach-msm/board-8960-gpiomux.c
index 1771bb9..fe37f2a 100644
--- a/arch/arm/mach-msm/board-8960-gpiomux.c
+++ b/arch/arm/mach-msm/board-8960-gpiomux.c
@@ -449,6 +449,13 @@
 		},
 	},
 	{
+		.gpio      = 26,	/* GSBI6 WLAN_PWD_L for AR6004 */
+		.settings = {
+			[GPIOMUX_SUSPENDED] = &gsbi6_suspended_cfg,
+			[GPIOMUX_ACTIVE] = &gsbi6_active_cfg,
+		},
+	},
+	{
 		.gpio      = 27,        /* GSBI6 BT_INT2AP_N for AR3002 */
 		.settings = {
 			[GPIOMUX_SUSPENDED] = &gsbi6_suspended_cfg,
diff --git a/arch/arm/mach-msm/board-8960-pmic.c b/arch/arm/mach-msm/board-8960-pmic.c
index f6c3653..9efedb1 100644
--- a/arch/arm/mach-msm/board-8960-pmic.c
+++ b/arch/arm/mach-msm/board-8960-pmic.c
@@ -403,7 +403,8 @@
 	.uvd_thresh_voltage	= 4050,
 	.alarm_low_mv		= 3400,
 	.alarm_high_mv		= 4000,
-	.resume_voltage_delta	= 100,
+	.resume_voltage_delta	= 60,
+	.resume_charge_percent	= 99,
 	.term_current		= CHG_TERM_MA,
 	.cool_temp		= 10,
 	.warm_temp		= 40,
@@ -611,4 +612,8 @@
 
 	if (machine_is_msm8960_fluid())
 		pm8921_bms_pdata.rconn_mohm = 20;
+
+	if (!machine_is_msm8960_fluid() && !machine_is_msm8960_liquid()
+			&& !machine_is_msm8960_fluid())
+		pm8921_chg_pdata.battery_less_hardware = 1;
 }
diff --git a/arch/arm/mach-msm/board-8960-regulator.c b/arch/arm/mach-msm/board-8960-regulator.c
index 2fa98b6..f9e2c8e 100644
--- a/arch/arm/mach-msm/board-8960-regulator.c
+++ b/arch/arm/mach-msm/board-8960-regulator.c
@@ -78,6 +78,7 @@
 	REGULATOR_SUPPLY("cam_vana",		"4-0048"),
 	REGULATOR_SUPPLY("cam_vana",		"4-0020"),
 	REGULATOR_SUPPLY("cam_vana",		"4-0034"),
+	REGULATOR_SUPPLY("cam_vana",		"4-0010"),
 };
 VREG_CONSUMERS(L12) = {
 	REGULATOR_SUPPLY("8921_l12",		NULL),
@@ -86,6 +87,7 @@
 	REGULATOR_SUPPLY("cam_vdig",		"4-0048"),
 	REGULATOR_SUPPLY("cam_vdig",		"4-0020"),
 	REGULATOR_SUPPLY("cam_vdig",		"4-0034"),
+	REGULATOR_SUPPLY("cam_vdig",		"4-0010"),
 };
 VREG_CONSUMERS(L14) = {
 	REGULATOR_SUPPLY("8921_l14",		NULL),
@@ -101,6 +103,7 @@
 	REGULATOR_SUPPLY("cam_vaf",		"4-0048"),
 	REGULATOR_SUPPLY("cam_vaf",		"4-0020"),
 	REGULATOR_SUPPLY("cam_vaf",		"4-0034"),
+	REGULATOR_SUPPLY("cam_vaf",		"4-0010"),
 };
 VREG_CONSUMERS(L17) = {
 	REGULATOR_SUPPLY("8921_l17",		NULL),
@@ -120,8 +123,7 @@
 	REGULATOR_SUPPLY("dsi_pll_vddio",	"mdp.0"),
 	REGULATOR_SUPPLY("hdmi_avdd",		"hdmi_msm.0"),
 	REGULATOR_SUPPLY("pll_vdd",		"pil_riva"),
-	REGULATOR_SUPPLY("pll_vdd",		"pil_qdsp6v4.1"),
-	REGULATOR_SUPPLY("pll_vdd",		"pil_qdsp6v4.2"),
+	REGULATOR_SUPPLY("pll_vdd",		"pil-q6v4-modem"),
 };
 VREG_CONSUMERS(L24) = {
 	REGULATOR_SUPPLY("8921_l24",		NULL),
@@ -136,15 +138,15 @@
 };
 VREG_CONSUMERS(L26) = {
 	REGULATOR_SUPPLY("8921_l26",		NULL),
-	REGULATOR_SUPPLY("core_vdd",		"pil_qdsp6v4.0"),
+	REGULATOR_SUPPLY("core_vdd",		"pil-q6v4-lpass"),
 };
 VREG_CONSUMERS(L27) = {
 	REGULATOR_SUPPLY("8921_l27",		NULL),
-	REGULATOR_SUPPLY("core_vdd",		"pil_qdsp6v4.2"),
+	REGULATOR_SUPPLY("sw_core_vdd",		"pil-q6v4-modem"),
 };
 VREG_CONSUMERS(L28) = {
 	REGULATOR_SUPPLY("8921_l28",		NULL),
-	REGULATOR_SUPPLY("core_vdd",		"pil_qdsp6v4.1"),
+	REGULATOR_SUPPLY("fw_core_vdd",		"pil-q6v4-modem"),
 };
 VREG_CONSUMERS(L29) = {
 	REGULATOR_SUPPLY("8921_l29",		NULL),
@@ -221,6 +223,7 @@
 	REGULATOR_SUPPLY("cam_vio",		"4-0048"),
 	REGULATOR_SUPPLY("cam_vio",		"4-0020"),
 	REGULATOR_SUPPLY("cam_vio",		"4-0034"),
+	REGULATOR_SUPPLY("cam_vio",		"4-0010"),
 };
 /* This mapping is used for CDP only. */
 VREG_CONSUMERS(CDP_LVS6) = {
diff --git a/arch/arm/mach-msm/board-8960-storage.c b/arch/arm/mach-msm/board-8960-storage.c
index 67f44aa..ded5bad 100644
--- a/arch/arm/mach-msm/board-8960-storage.c
+++ b/arch/arm/mach-msm/board-8960-storage.c
@@ -327,9 +327,11 @@
 #endif
 	.vreg_data	= &mmc_slot_vreg_data[SDCC3],
 	.pin_data	= &mmc_slot_pin_data[SDCC3],
+#ifndef CONFIG_MMC_MSM_SDC3_POLLING
 	.status_gpio	= PM8921_GPIO_PM_TO_SYS(26),
 	.status_irq	= PM8921_GPIO_IRQ(PM8921_IRQ_BASE, 26),
 	.irq_flags	= IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+#endif
 	.is_status_gpio_active_low = true,
 	.xpc_cap	= 1,
 	.uhs_caps	= (MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 |
diff --git a/arch/arm/mach-msm/board-8960.c b/arch/arm/mach-msm/board-8960.c
index 7115e40..167923f 100644
--- a/arch/arm/mach-msm/board-8960.c
+++ b/arch/arm/mach-msm/board-8960.c
@@ -103,6 +103,11 @@
 #include "pm-boot.h"
 #include "msm_watchdog.h"
 
+#if defined(CONFIG_BT) && defined(CONFIG_BT_HCIUART_ATH3K)
+#include <linux/wlan_plat.h>
+#include <linux/mutex.h>
+#endif
+
 static struct platform_device msm_fm_platform_init = {
 	.name = "iris_fm",
 	.id   = -1,
@@ -541,42 +546,15 @@
 {
 #if defined(CONFIG_ION_MSM) && defined(CONFIG_MSM_MULTIMEDIA_USE_ION)
 	unsigned int i;
-	unsigned int reusable_count = 0;
 	unsigned int fixed_size = 0;
 	unsigned int fixed_low_size, fixed_middle_size, fixed_high_size;
 	unsigned long fixed_low_start, fixed_middle_start, fixed_high_start;
 
 	adjust_mem_for_liquid();
-	msm8960_fmem_pdata.size = 0;
-	msm8960_fmem_pdata.reserved_size_low = 0;
-	msm8960_fmem_pdata.reserved_size_high = 0;
-	msm8960_fmem_pdata.align = PAGE_SIZE;
 	fixed_low_size = 0;
 	fixed_middle_size = 0;
 	fixed_high_size = 0;
 
-	/* We only support 1 reusable heap. Check if more than one heap
-	 * is specified as reusable and set as non-reusable if found.
-	 */
-	for (i = 0; i < msm8960_ion_pdata.nr; ++i) {
-		const struct ion_platform_heap *heap =
-						&(msm8960_ion_pdata.heaps[i]);
-
-		if (heap->type == (enum ion_heap_type) ION_HEAP_TYPE_CP
-			&& heap->extra_data) {
-			struct ion_cp_heap_pdata *data = heap->extra_data;
-
-			reusable_count += (data->reusable) ? 1 : 0;
-
-			if (data->reusable && reusable_count > 1) {
-				pr_err("%s: Too many heaps specified as "
-					"reusable. Heap %s was not configured "
-					"as reusable.\n", __func__, heap->name);
-				data->reusable = 0;
-			}
-		}
-	}
-
 	for (i = 0; i < msm8960_ion_pdata.nr; ++i) {
 		struct ion_platform_heap *heap =
 						&(msm8960_ion_pdata.heaps[i]);
@@ -586,12 +564,9 @@
 
 		if (heap->extra_data) {
 			int fixed_position = NOT_FIXED;
-			int mem_is_fmem = 0;
 
 			switch ((int) heap->type) {
 			case ION_HEAP_TYPE_CP:
-				mem_is_fmem = ((struct ion_cp_heap_pdata *)
-					heap->extra_data)->mem_is_fmem;
 				fixed_position = ((struct ion_cp_heap_pdata *)
 					heap->extra_data)->fixed_position;
 				align = ((struct ion_cp_heap_pdata *)
@@ -601,8 +576,6 @@
 					heap->extra_data)->iommu_map_all;
 				break;
 			case ION_HEAP_TYPE_CARVEOUT:
-				mem_is_fmem = ((struct ion_co_heap_pdata *)
-					heap->extra_data)->mem_is_fmem;
 				fixed_position = ((struct ion_co_heap_pdata *)
 					heap->extra_data)->fixed_position;
 				adjacent_mem_id = ((struct ion_co_heap_pdata *)
@@ -620,9 +593,6 @@
 				}
 			}
 
-			if (mem_is_fmem && adjacent_mem_id != INVALID_HEAP_ID)
-				msm8960_fmem_pdata.align = align;
-
 			if (fixed_position != NOT_FIXED)
 				fixed_size += heap->size;
 			else
@@ -634,21 +604,12 @@
 				fixed_middle_size += heap->size;
 			else if (fixed_position == FIXED_HIGH)
 				fixed_high_size += heap->size;
-
-			if (mem_is_fmem)
-				msm8960_fmem_pdata.size += heap->size;
 		}
 	}
 
 	if (!fixed_size)
 		return;
 
-	if (msm8960_fmem_pdata.size) {
-		msm8960_fmem_pdata.reserved_size_low = fixed_low_size +
-							HOLE_SIZE;
-		msm8960_fmem_pdata.reserved_size_high = fixed_high_size;
-	}
-
 	/* Since the fixed area may be carved out of lowmem,
 	 * make sure the length is a multiple of 1M.
 	 */
@@ -818,19 +779,6 @@
 {
 	msm8960_set_display_params(prim_panel_name, ext_panel_name);
 	msm_reserve();
-	if (msm8960_fmem_pdata.size) {
-#if defined(CONFIG_ION_MSM) && defined(CONFIG_MSM_MULTIMEDIA_USE_ION)
-		if (reserve_info->fixed_area_size) {
-			msm8960_fmem_pdata.phys =
-				reserve_info->fixed_area_start + MSM_MM_FW_SIZE;
-			pr_info("mm fw at %lx (fixed) size %x\n",
-				reserve_info->fixed_area_start, MSM_MM_FW_SIZE);
-			pr_info("fmem start %lx (fixed) size %lx\n",
-				msm8960_fmem_pdata.phys,
-				msm8960_fmem_pdata.size);
-		}
-#endif
-	}
 }
 
 static int msm8960_change_memory_power(u64 start, u64 size,
@@ -2602,6 +2550,76 @@
 #endif
 
 #if defined(CONFIG_BT) && defined(CONFIG_BT_HCIUART_ATH3K)
+enum WLANBT_STATUS {
+	WLANOFF_BTOFF = 1,
+	WLANOFF_BTON,
+	WLANON_BTOFF,
+	WLANON_BTON
+};
+
+static DEFINE_MUTEX(ath_wlanbt_mutex);
+static int gpio_wlan_sys_rest_en = 26;
+static int ath_wlanbt_status = WLANOFF_BTOFF;
+
+static int ath6kl_power_control(int on)
+{
+	int rc;
+
+	if (on) {
+		rc = gpio_request(gpio_wlan_sys_rest_en, "wlan sys_rst_n");
+		if (rc) {
+			pr_err("%s: unable to request gpio %d (%d)\n",
+				__func__, gpio_wlan_sys_rest_en, rc);
+			return rc;
+		}
+		rc = gpio_direction_output(gpio_wlan_sys_rest_en, 0);
+		msleep(200);
+		rc = gpio_direction_output(gpio_wlan_sys_rest_en, 1);
+		msleep(100);
+	} else {
+		gpio_set_value(gpio_wlan_sys_rest_en, 0);
+		rc = gpio_direction_input(gpio_wlan_sys_rest_en);
+		msleep(100);
+		gpio_free(gpio_wlan_sys_rest_en);
+	}
+	return 0;
+};
+
+static int ath6kl_wlan_power(int on)
+{
+	int ret = 0;
+
+	mutex_lock(&ath_wlanbt_mutex);
+	if (on) {
+		if (ath_wlanbt_status == WLANOFF_BTOFF) {
+			ret = ath6kl_power_control(1);
+			ath_wlanbt_status = WLANON_BTOFF;
+		} else if (ath_wlanbt_status == WLANOFF_BTON)
+			ath_wlanbt_status = WLANON_BTON;
+	} else {
+		if (ath_wlanbt_status == WLANON_BTOFF) {
+			ret = ath6kl_power_control(0);
+			ath_wlanbt_status = WLANOFF_BTOFF;
+		} else if (ath_wlanbt_status == WLANON_BTON)
+			ath_wlanbt_status = WLANOFF_BTON;
+	}
+	mutex_unlock(&ath_wlanbt_mutex);
+	pr_debug("%s on= %d, wlan_status= %d\n",
+		__func__, on, ath_wlanbt_status);
+	return ret;
+};
+
+static struct wifi_platform_data ath6kl_wifi_control = {
+	.set_power      = ath6kl_wlan_power,
+};
+
+static struct platform_device msm_wlan_power_device = {
+	.name = "ath6kl_power",
+	.dev            = {
+		.platform_data = &ath6kl_wifi_control,
+	},
+};
+
 static struct resource bluesleep_resources[] = {
 	{
 		.name   = "gpio_host_wake",
@@ -2634,50 +2652,54 @@
 	.name = "bt_power",
 };
 
-int gpio_bt_sys_rest_en = 28;
+static int gpio_bt_sys_rest_en = 28;
 
 static int bluetooth_power(int on)
 {
 	int rc;
 
-	pr_debug("%s on= %d\n", __func__, on);
-
+	mutex_lock(&ath_wlanbt_mutex);
 	if (on) {
+		if (ath_wlanbt_status == WLANOFF_BTOFF) {
+			ath6kl_power_control(1);
+			ath_wlanbt_status = WLANOFF_BTON;
+		} else if (ath_wlanbt_status == WLANON_BTOFF)
+			ath_wlanbt_status = WLANON_BTON;
+
 		rc = gpio_request(gpio_bt_sys_rest_en, "bt sys_rst_n");
 		if (rc) {
 			pr_err("%s: unable to request gpio %d (%d)\n",
 				__func__, gpio_bt_sys_rest_en, rc);
-			goto out;
+			mutex_unlock(&ath_wlanbt_mutex);
+			return rc;
 		}
 		rc = gpio_direction_output(gpio_bt_sys_rest_en, 0);
-		if (rc) {
-			pr_err("%s: Unable to set gpio %d direction\n",
-				__func__, gpio_bt_sys_rest_en);
-			goto free_gpio;
-		}
+		msleep(20);
+		rc = gpio_direction_output(gpio_bt_sys_rest_en, 1);
 		msleep(100);
-		gpio_set_value(gpio_bt_sys_rest_en, 1);
-		msleep(100);
-		goto out;
 	} else {
 		gpio_set_value(gpio_bt_sys_rest_en, 0);
 		rc = gpio_direction_input(gpio_bt_sys_rest_en);
 		msleep(100);
-	}
+		gpio_free(gpio_bt_sys_rest_en);
 
-free_gpio:
-	gpio_free(gpio_bt_sys_rest_en);
-out:
-	return rc;
-}
+		if (ath_wlanbt_status == WLANOFF_BTON) {
+			ath6kl_power_control(0);
+			ath_wlanbt_status = WLANOFF_BTOFF;
+		} else if (ath_wlanbt_status == WLANON_BTON)
+			ath_wlanbt_status = WLANON_BTOFF;
+	}
+	mutex_unlock(&ath_wlanbt_mutex);
+	pr_debug("%s on= %d, wlan_status= %d\n",
+		__func__, on, ath_wlanbt_status);
+	return 0;
+};
 
 static void __init bt_power_init(void)
 {
-	pr_debug("%s enter\n", __func__);
 	msm_bt_power_device.dev.platform_data = &bluetooth_power;
-
 	return;
-}
+};
 #else
 #define bt_power_init(x) do {} while (0)
 #endif
@@ -2703,6 +2725,7 @@
 #if defined(CONFIG_BT) && defined(CONFIG_BT_HCIUART_ATH3K)
 	&msm_bluesleep_device,
 	&msm_bt_power_device,
+	&msm_wlan_power_device,
 #endif
 #if defined(CONFIG_QSEECOM)
 	&qseecom_device,
@@ -2753,6 +2776,7 @@
 	&msm8960_rpm_device,
 	&msm8960_rpm_log_device,
 	&msm8960_rpm_stat_device,
+	&msm8960_rpm_master_stat_device,
 	&msm_device_tz_log,
 	&coresight_tpiu_device,
 	&coresight_etb_device,
@@ -2762,7 +2786,6 @@
 	&msm_device_dspcrashd_8960,
 	&msm8960_device_watchdog,
 	&msm8960_rtb_device,
-	&msm8960_cpu_idle_device,
 	&msm8960_device_cache_erp,
 	&msm8960_device_ebi1_ch0_erp,
 	&msm8960_device_ebi1_ch1_erp,
@@ -2852,8 +2875,8 @@
 	/* Fixup data that needs to change based on GPU ID */
 	if (cpu_is_msm8960ab()) {
 		kgsl_3d0_pdata->chipid = ADRENO_CHIPID(3, 2, 1, 0);
-		/* 8960PRO nominal clock rate is 325Mhz instead of 320Mhz */
-		kgsl_3d0_pdata->pwrlevel[1].gpu_freq = 325000000;
+		/* 8960PRO nominal clock rate is 320Mhz */
+		kgsl_3d0_pdata->pwrlevel[1].gpu_freq = 320000000;
 	} else {
 		kgsl_3d0_pdata->iommu_count = 1;
 		if (SOCINFO_VERSION_MAJOR(soc_platform_version) == 1) {
@@ -3193,6 +3216,17 @@
 	msm_tsens_early_init(&msm_tsens_pdata);
 }
 
+static void __init msm8960_reset_spm_avs(void)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(msm_spm_data); i++) {
+		struct msm_spm_platform_data *pdata = &msm_spm_data[i];
+		pdata->reg_init_values[MSM_SPM_REG_SAW2_AVS_CTL] = 0;
+		pdata->reg_init_values[MSM_SPM_REG_SAW2_AVS_HYSTERESIS] = 0;
+	}
+}
+
 static void __init msm8960_cdp_init(void)
 {
 	if (meminfo_init(SYS_MEMORY, SZ_256M) < 0)
@@ -3246,10 +3280,20 @@
 		msm_isa1200_board_info[0].platform_data = &isa1200_1_pdata;
 	msm8960_i2c_init();
 	msm8960_gfx_init();
+
+	if (cpu_is_msm8960ab())
+		msm8960_reset_spm_avs();
 	msm_spm_init(msm_spm_data, ARRAY_SIZE(msm_spm_data));
 	msm_spm_l2_init(msm_spm_l2_data);
+
 	msm8960_init_buses();
-	platform_add_devices(msm8960_footswitch, msm8960_num_footswitch);
+	if (cpu_is_msm8960ab()) {
+		platform_add_devices(msm8960ab_footswitch,
+				     msm8960ab_num_footswitch);
+	} else {
+		platform_add_devices(msm8960_footswitch,
+				     msm8960_num_footswitch);
+	}
 	if (machine_is_msm8960_liquid())
 		platform_device_register(&msm8960_device_ext_3p3v_vreg);
 	if (machine_is_msm8960_cdp())
@@ -3281,10 +3325,8 @@
 
 	msm8960_pm8921_gpio_mpp_init();
 	/* Don't add modem devices on APQ targets */
-	if (socinfo_get_id() != 124) {
-		platform_device_register(&msm_8960_q6_mss_fw);
-		platform_device_register(&msm_8960_q6_mss_sw);
-	}
+	if (socinfo_get_id() != 124)
+		platform_device_register(&msm_8960_q6_mss);
 	platform_add_devices(cdp_devices, ARRAY_SIZE(cdp_devices));
 	msm8960_init_smsc_hub();
 	msm8960_init_hsic();
diff --git a/arch/arm/mach-msm/board-8974-gpiomux.c b/arch/arm/mach-msm/board-8974-gpiomux.c
index 8568340..50b59e1 100644
--- a/arch/arm/mach-msm/board-8974-gpiomux.c
+++ b/arch/arm/mach-msm/board-8974-gpiomux.c
@@ -128,6 +128,12 @@
 	.dir = GPIOMUX_OUT_LOW,
 };
 
+static struct gpiomux_setting taiko_int = {
+	.func = GPIOMUX_FUNC_GPIO,
+	.drv = GPIOMUX_DRV_2MA,
+	.pull = GPIOMUX_PULL_NONE,
+};
+
 static struct msm_gpiomux_config msm_touch_configs[] __initdata = {
 	{
 		.gpio      = 60,		/* TOUCH RESET */
@@ -344,6 +350,21 @@
 	},
 };
 
+static struct gpiomux_setting sd_card_det_config = {
+	.func = GPIOMUX_FUNC_GPIO,
+	.drv = GPIOMUX_DRV_2MA,
+	.pull = GPIOMUX_PULL_NONE,
+	.dir = GPIOMUX_IN,
+};
+
+static struct msm_gpiomux_config sd_card_det __initdata = {
+	.gpio = 62,
+	.settings = {
+		[GPIOMUX_ACTIVE]    = &sd_card_det_config,
+		[GPIOMUX_SUSPENDED] = &sd_card_det_config,
+	},
+};
+
 static struct msm_gpiomux_config msm_sensor_configs[] __initdata = {
 	{
 		.gpio = 15, /* CAM_MCLK0 */
@@ -516,7 +537,13 @@
 		.settings = {
 			[GPIOMUX_SUSPENDED] = &taiko_reset,
 		},
-	}
+	},
+	{
+		.gpio	= 72,		/* CDC_INT */
+		.settings = {
+			[GPIOMUX_SUSPENDED] = &taiko_int,
+		},
+	},
 };
 
 void __init msm_8974_init_gpiomux(void)
@@ -543,6 +570,8 @@
 
 	msm_gpiomux_install(msm_sensor_configs, ARRAY_SIZE(msm_sensor_configs));
 
+	msm_gpiomux_install(&sd_card_det, 1);
+
 	msm_gpiomux_install(msm_taiko_config, ARRAY_SIZE(msm_taiko_config));
 
 	msm_gpiomux_install(msm_hdmi_configs, ARRAY_SIZE(msm_hdmi_configs));
diff --git a/arch/arm/mach-msm/board-8974.c b/arch/arm/mach-msm/board-8974.c
index dcc0d01..98a82b1 100644
--- a/arch/arm/mach-msm/board-8974.c
+++ b/arch/arm/mach-msm/board-8974.c
@@ -13,13 +13,11 @@
 #include <linux/kernel.h>
 #include <linux/platform_device.h>
 #include <linux/io.h>
-#include <linux/gpio.h>
 #include <linux/irq.h>
 #include <linux/irqdomain.h>
 #include <linux/of.h>
 #include <linux/of_address.h>
 #include <linux/of_platform.h>
-#include <linux/of_irq.h>
 #include <linux/memory.h>
 #ifdef CONFIG_ANDROID_PMEM
 #include <linux/android_pmem.h>
@@ -29,6 +27,8 @@
 #include <linux/msm_thermal.h>
 #include <asm/mach/map.h>
 #include <asm/hardware/gic.h>
+#include <asm/mach/map.h>
+#include <asm/mach/arch.h>
 #include <mach/board.h>
 #include <mach/gpiomux.h>
 #include <mach/msm_iomap.h>
@@ -37,31 +37,20 @@
 #endif
 #include <mach/msm_memtypes.h>
 #include <mach/msm_smd.h>
+#include <mach/restart.h>
 #include <mach/rpm-smd.h>
 #include <mach/rpm-regulator-smd.h>
-#include <mach/qpnp-int.h>
 #include <mach/socinfo.h>
 #include <mach/msm_bus_board.h>
-#include <mach/mpm.h>
+#include "board-dt.h"
 #include "clock.h"
 #include "devices.h"
 #include "spm.h"
 #include "modem_notifier.h"
 #include "lpm_resources.h"
 
-#define MSM_KERNEL_EBI1_MEM_SIZE	0x280000
 
-#ifdef CONFIG_KERNEL_PMEM_EBI_REGION
-static unsigned kernel_ebi1_mem_size = MSM_KERNEL_EBI1_MEM_SIZE;
-static int __init kernel_ebi1_mem_size_setup(char *p)
-{
-	kernel_ebi1_mem_size = memparse(p, NULL);
-	return 0;
-}
-early_param("kernel_ebi1_mem_size", kernel_ebi1_mem_size_setup);
-#endif
-
-static struct memtype_reserve msm_8974_reserve_table[] __initdata = {
+static struct memtype_reserve msm8974_reserve_table[] __initdata = {
 	[MEMTYPE_SMI] = {
 	},
 	[MEMTYPE_EBI0] = {
@@ -72,18 +61,11 @@
 	},
 };
 
-static int msm_8974_paddr_to_memtype(unsigned int paddr)
+static int msm8974_paddr_to_memtype(unsigned int paddr)
 {
 	return MEMTYPE_EBI1;
 }
 
-static void __init reserve_ebi_memory(void)
-{
-#ifdef CONFIG_KERNEL_PMEM_EBI_REGION
-	msm_8974_reserve_table[MEMTYPE_EBI1].size += kernel_ebi1_mem_size;
-#endif
-}
-
 static struct resource smd_resource[] = {
 	{
 		.name	= "modem_smd_in",
@@ -198,7 +180,7 @@
 		.edge = SMD_APPS_RPM,
 
 		.smd_int.irq_name = "rpm_smd_in",
-		.smd_int.flags = IRQF_TRIGGER_RISING,
+		.smd_int.flags = IRQF_TRIGGER_RISING | IRQF_NO_SUSPEND,
 		.smd_int.irq_id = -1,
 		.smd_int.device_name = "smd_dev",
 		.smd_int.dev_id = 0,
@@ -246,26 +228,15 @@
 	}
 };
 
-static void __init msm_8974_calculate_reserve_sizes(void)
-{
-	reserve_ebi_memory();
-}
-
-static struct reserve_info msm_8974_reserve_info __initdata = {
-	.memtype_reserve_table = msm_8974_reserve_table,
-	.calculate_reserve_sizes = msm_8974_calculate_reserve_sizes,
-	.paddr_to_memtype = msm_8974_paddr_to_memtype,
+static struct reserve_info msm8974_reserve_info __initdata = {
+	.memtype_reserve_table = msm8974_reserve_table,
+	.paddr_to_memtype = msm8974_paddr_to_memtype,
 };
 
-static void __init msm_8974_early_memory(void)
+static void __init msm8974_early_memory(void)
 {
-	reserve_info = &msm_8974_reserve_info;
-	of_scan_flat_dt(dt_scan_for_memory_reserve, msm_8974_reserve_table);
-}
-
-void __init msm_8974_reserve(void)
-{
-	msm_reserve();
+	reserve_info = &msm8974_reserve_info;
+	of_scan_flat_dt(dt_scan_for_memory_reserve, msm8974_reserve_table);
 }
 
 #define BIMC_BASE	0xfc380000
@@ -455,7 +426,7 @@
 				ARRAY_SIZE(msm_bus_8974_devices));
 };
 
-void __init msm_8974_add_devices(void)
+void __init msm8974_add_devices(void)
 {
 	platform_device_register(&msm_device_smd_8974);
 }
@@ -466,7 +437,7 @@
  * into this category, and thus the driver should not be added here. The
  * EPROBE_DEFER can satisfy most dependency problems.
  */
-void __init msm_8974_add_drivers(void)
+void __init msm8974_add_drivers(void)
 {
 	msm_init_modem_notifier_list();
 	msm_smd_init();
@@ -484,31 +455,7 @@
 	mxt_init_vkeys_8974();
 }
 
-static struct of_device_id irq_match[] __initdata  = {
-	{ .compatible = "qcom,msm-qgic2", .data = gic_of_init, },
-	{ .compatible = "qcom,msm-gpio", .data = msm_gpio_of_init, },
-	{ .compatible = "qcom,spmi-pmic-arb", .data = qpnpint_of_init, },
-	{}
-};
-static struct of_device_id mpm_match[] __initdata = {
-	{.compatible = "qcom,mpm-v2", },
-	{},
-};
-
-void __init msm_8974_init_irq(void)
-{
-	struct device_node *node;
-
-	of_irq_init(irq_match);
-	node = of_find_matching_node(NULL, mpm_match);
-
-	WARN_ON(!node);
-
-	if (node)
-		of_mpm_init(node);
-}
-
-static struct of_dev_auxdata msm_8974_auxdata_lookup[] __initdata = {
+static struct of_dev_auxdata msm8974_auxdata_lookup[] __initdata = {
 	OF_DEV_AUXDATA("qcom,hsusb-otg", 0xF9A55000, \
 			"msm_otg", NULL),
 	OF_DEV_AUXDATA("qcom,dwc-usb3-msm", 0xF9200000, \
@@ -530,7 +477,6 @@
 	OF_DEV_AUXDATA("qcom,pil-q6v5-lpass",   0xFE200000, \
 			"pil-q6v5-lpass", NULL),
 	OF_DEV_AUXDATA("qcom,pil-q6v5-mss", 0xFC880000, "pil-q6v5-mss", NULL),
-	OF_DEV_AUXDATA("qcom,pil-mba",     0xFC820000, "pil-mba", NULL),
 	OF_DEV_AUXDATA("qcom,pil-pronto", 0xFB21B000, \
 			"pil_pronto", NULL),
 	OF_DEV_AUXDATA("arm,coresight-tmc", 0xFC322000, \
@@ -575,16 +521,43 @@
 	{}
 };
 
-void __init msm_8974_init(struct of_dev_auxdata **adata)
+static void __init msm8974_map_io(void)
 {
+	msm_map_8974_io();
+	if (socinfo_init() < 0)
+		pr_err("%s: socinfo_init() failed\n", __func__);
+}
+
+void __init msm8974_init(void)
+{
+	struct of_dev_auxdata *adata = msm8974_auxdata_lookup;
+
 	msm_8974_init_gpiomux();
-
-	*adata = msm_8974_auxdata_lookup;
-
 	regulator_has_full_constraints();
+	of_platform_populate(NULL, of_default_bus_match_table, adata, NULL);
+
+	msm8974_add_devices();
+	msm8974_add_drivers();
 }
 
-void __init msm_8974_very_early(void)
+void __init msm8974_init_very_early(void)
 {
-	msm_8974_early_memory();
+	msm8974_early_memory();
 }
+
+static const char *msm8974_dt_match[] __initconst = {
+	"qcom,msm8974",
+	NULL
+};
+
+DT_MACHINE_START(MSM8974_DT, "Qualcomm MSM 8974 (Flattened Device Tree)")
+	.map_io = msm8974_map_io,
+	.init_irq = msm_dt_init_irq,
+	.init_machine = msm8974_init,
+	.handle_irq = gic_handle_irq,
+	.timer = &msm_dt_timer,
+	.dt_compat = msm8974_dt_match,
+	.reserve = msm_reserve,
+	.init_very_early = msm8974_init_very_early,
+	.restart = msm_restart,
+MACHINE_END
diff --git a/arch/arm/mach-msm/board-9615-gpiomux.c b/arch/arm/mach-msm/board-9615-gpiomux.c
index 9339638..cef967c 100644
--- a/arch/arm/mach-msm/board-9615-gpiomux.c
+++ b/arch/arm/mach-msm/board-9615-gpiomux.c
@@ -201,6 +201,23 @@
 	},
 };
 
+static struct gpiomux_setting sd_card_det = {
+	.func = GPIOMUX_FUNC_GPIO,
+	.drv = GPIOMUX_DRV_2MA,
+	.pull = GPIOMUX_PULL_NONE,
+	.dir = GPIOMUX_IN,
+};
+
+struct msm_gpiomux_config sd_card_det_config[] __initdata = {
+	{
+		.gpio = 80,
+		.settings = {
+			[GPIOMUX_ACTIVE]    = &sd_card_det,
+			[GPIOMUX_SUSPENDED] = &sd_card_det,
+		},
+	},
+};
+
 #ifdef CONFIG_LTC4088_CHARGER
 static struct msm_gpiomux_config
 	msm9615_ltc4088_charger_config[] __initdata = {
@@ -362,6 +379,8 @@
 
 	msm_gpiomux_install(msm9615_ps_hold_config,
 			ARRAY_SIZE(msm9615_ps_hold_config));
+	msm_gpiomux_install(sd_card_det_config,
+			ARRAY_SIZE(sd_card_det_config));
 	msm_gpiomux_install(msm9615_sdcc2_configs,
 			ARRAY_SIZE(msm9615_sdcc2_configs));
 #ifdef CONFIG_LTC4088_CHARGER
diff --git a/arch/arm/mach-msm/board-9615.c b/arch/arm/mach-msm/board-9615.c
index ca95b62..2f3ab7f 100644
--- a/arch/arm/mach-msm/board-9615.c
+++ b/arch/arm/mach-msm/board-9615.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -870,6 +870,8 @@
 #ifdef CONFIG_LTC4088_CHARGER
 	&msm_device_charger,
 #endif
+	&msm_9615_q6_lpass,
+	&msm_9615_q6_mss,
 	&msm_device_otg,
 	&msm_device_hsic_peripheral,
 	&msm_device_gadget_peripheral,
@@ -908,6 +910,8 @@
 	&msm_voip,
 	&msm_i2s_cpudai0,
 	&msm_i2s_cpudai1,
+	&msm_i2s_cpudai4,
+	&msm_i2s_cpudai5,
 	&msm_pcm_hostless,
 	&msm_cpudai_afe_01_rx,
 	&msm_cpudai_afe_01_tx,
@@ -933,7 +937,9 @@
 	&msm_bus_def_fab,
 	&msm9615_rpm_log_device,
 	&msm9615_rpm_stat_device,
+	&msm9615_rpm_master_stat_device,
 	&msm_tsens_device,
+	&msm9615_pm_8x60,
 };
 
 static void __init msm9615_i2c_init(void)
diff --git a/arch/arm/mach-msm/board-9625-gpiomux.c b/arch/arm/mach-msm/board-9625-gpiomux.c
index fe7670b..c4e174b 100644
--- a/arch/arm/mach-msm/board-9625-gpiomux.c
+++ b/arch/arm/mach-msm/board-9625-gpiomux.c
@@ -76,6 +76,82 @@
 
 };
 
+static struct gpiomux_setting sdc3_clk_active_cfg = {
+	.func = GPIOMUX_FUNC_1,
+	.drv = GPIOMUX_DRV_8MA,
+	.pull = GPIOMUX_PULL_NONE,
+};
+
+static struct gpiomux_setting sdc3_cmd_active_cfg = {
+	.func = GPIOMUX_FUNC_1,
+	.drv = GPIOMUX_DRV_8MA,
+	.pull = GPIOMUX_PULL_UP,
+};
+
+static struct gpiomux_setting sdc3_data_0_3_active_cfg = {
+	.func = GPIOMUX_FUNC_6,
+	.drv = GPIOMUX_DRV_8MA,
+	.pull = GPIOMUX_PULL_UP,
+};
+
+static struct gpiomux_setting sdc3_suspended_cfg = {
+	.func = GPIOMUX_FUNC_GPIO,
+	.drv = GPIOMUX_DRV_2MA,
+	.pull = GPIOMUX_PULL_DOWN,
+};
+
+static struct gpiomux_setting sdc3_data_1_suspended_cfg = {
+	.func = GPIOMUX_FUNC_GPIO,
+	.drv = GPIOMUX_DRV_2MA,
+	.pull = GPIOMUX_PULL_UP,
+};
+
+static struct msm_gpiomux_config sdc3_configs[] __initdata = {
+	{
+		.gpio      = 25,
+		.settings = {
+			[GPIOMUX_ACTIVE] = &sdc3_clk_active_cfg,
+			[GPIOMUX_SUSPENDED] = &sdc3_suspended_cfg,
+		},
+	},
+	{
+		.gpio      = 24,
+		.settings = {
+			[GPIOMUX_ACTIVE] = &sdc3_cmd_active_cfg,
+			[GPIOMUX_SUSPENDED] = &sdc3_suspended_cfg,
+		},
+
+	},
+	{
+		.gpio      = 16,
+		.settings = {
+			[GPIOMUX_ACTIVE] = &sdc3_data_0_3_active_cfg,
+			[GPIOMUX_SUSPENDED] = &sdc3_suspended_cfg,
+		},
+	},
+	{
+		.gpio      = 17,
+		.settings = {
+			[GPIOMUX_ACTIVE] = &sdc3_data_0_3_active_cfg,
+			[GPIOMUX_SUSPENDED] = &sdc3_data_1_suspended_cfg,
+		},
+	},
+	{
+		.gpio      = 18,
+		.settings = {
+			[GPIOMUX_ACTIVE] = &sdc3_data_0_3_active_cfg,
+			[GPIOMUX_SUSPENDED] = &sdc3_suspended_cfg,
+		},
+	},
+	{
+		.gpio      = 19,
+		.settings = {
+			[GPIOMUX_ACTIVE] = &sdc3_data_0_3_active_cfg,
+			[GPIOMUX_SUSPENDED] = &sdc3_suspended_cfg,
+		},
+	},
+};
+
 void __init msm9625_init_gpiomux(void)
 {
 	int rc;
@@ -87,4 +163,5 @@
 	}
 
 	msm_gpiomux_install(msm_blsp_configs, ARRAY_SIZE(msm_blsp_configs));
+	msm_gpiomux_install(sdc3_configs, ARRAY_SIZE(sdc3_configs));
 }
diff --git a/arch/arm/mach-msm/board-9625.c b/arch/arm/mach-msm/board-9625.c
index 37e93b6..5c7eebe 100644
--- a/arch/arm/mach-msm/board-9625.c
+++ b/arch/arm/mach-msm/board-9625.c
@@ -21,18 +21,24 @@
 #include <linux/of_irq.h>
 #include <linux/memory.h>
 #include <asm/mach/map.h>
-#include <asm/hardware/cache-l2x0.h>
 #include <asm/hardware/gic.h>
-#include <asm/arch_timer.h>
 #include <asm/mach/arch.h>
-#include <asm/mach/time.h>
 #include <mach/socinfo.h>
 #include <mach/board.h>
+#include <mach/restart.h>
 #include <mach/gpio.h>
 #include <mach/clk-provider.h>
 #include <mach/qpnp-int.h>
 #include <mach/msm_memtypes.h>
+#include <mach/msm_iomap.h>
+#include <mach/msm_smd.h>
+#include <mach/rpm-smd.h>
+#include <mach/rpm-regulator-smd.h>
+#include "board-dt.h"
 #include "clock.h"
+#include "modem_notifier.h"
+#include "lpm_resources.h"
+#include "spm.h"
 
 #define MSM_KERNEL_EBI_SIZE	0x51000
 
@@ -63,11 +69,6 @@
 	.paddr_to_memtype = msm9625_paddr_to_memtype,
 };
 
-
-#define L2CC_AUX_CTRL	((0x1 << L2X0_AUX_CTRL_SHARE_OVERRIDE_SHIFT) | \
-			(0x2 << L2X0_AUX_CTRL_WAY_SIZE_SHIFT) | \
-			(0x1 << L2X0_AUX_CTRL_EVNT_MON_BUS_EN_SHIFT))
-
 static struct clk_lookup msm_clocks_dummy[] = {
 	CLK_DUMMY("core_clk",   BLSP1_UART_CLK, "msm_serial_hsl.0", OFF),
 	CLK_DUMMY("iface_clk",  BLSP1_UART_CLK, "msm_serial_hsl.0", OFF),
@@ -91,13 +92,6 @@
 	.size = ARRAY_SIZE(msm_clocks_dummy),
 };
 
-static struct of_device_id irq_match[] __initdata  = {
-	{ .compatible = "qcom,msm-qgic2", .data = gic_of_init, },
-	{ .compatible = "qcom,msm-gpio", .data = msm_gpio_of_init, },
-	{ .compatible = "qcom,spmi-pmic-arb", .data = qpnpint_of_init, },
-	{}
-};
-
 static const char *msm9625_dt_match[] __initconst = {
 	"qcom,msm9625",
 	NULL
@@ -110,30 +104,174 @@
 			"spi_qsd.1", NULL),
 	OF_DEV_AUXDATA("qcom,spmi-pmic-arb", 0xFC4C0000, \
 			"spmi-pmic-arb.0", NULL),
+	OF_DEV_AUXDATA("qcom,msm-sdcc", 0xF98A4000, \
+			"msm_sdcc.2", NULL),
+	OF_DEV_AUXDATA("qcom,msm-sdcc", 0xF9864000, \
+			"msm_sdcc.3", NULL),
 	{}
 };
 
-void __init msm9625_init_irq(void)
+static void __init msm9625_early_memory(void)
 {
-	l2x0_of_init(L2CC_AUX_CTRL, L2X0_AUX_CTRL_MASK);
-	of_irq_init(irq_match);
+	reserve_info = &msm9625_reserve_info;
+	of_scan_flat_dt(dt_scan_for_memory_reserve, msm9625_reserve_table);
 }
 
-static void __init msm_dt_timer_init(void)
-{
-	arch_timer_of_register();
-}
-
-static struct sys_timer msm_dt_timer = {
-	.init = msm_dt_timer_init
-};
-
 static void __init msm9625_reserve(void)
 {
-	reserve_info = &msm9625_reserve_info;
 	msm_reserve();
 }
 
+static struct resource smd_resource[] = {
+	{
+		.name   = "modem_smd_in",
+		.start  = 32 + 25,              /* mss_sw_to_kpss_ipc_irq0  */
+		.flags  = IORESOURCE_IRQ,
+	},
+	{
+		.name   = "modem_smsm_in",
+		.start  = 32 + 26,              /* mss_sw_to_kpss_ipc_irq1  */
+		.flags  = IORESOURCE_IRQ,
+	},
+	{
+		.name   = "adsp_smd_in",
+		.start  = 32 + 156,             /* lpass_to_kpss_ipc_irq0  */
+		.flags  = IORESOURCE_IRQ,
+	},
+	{
+		.name   = "adsp_smsm_in",
+		.start  = 32 + 157,             /* lpass_to_kpss_ipc_irq1  */
+		.flags  = IORESOURCE_IRQ,
+	},
+	{
+		.name   = "rpm_smd_in",
+		.start  = 32 + 168,             /* rpm_to_kpss_ipc_irq4  */
+		.flags  = IORESOURCE_IRQ,
+	},
+};
+
+static struct smd_subsystem_config smd_config_list[] = {
+	{
+		.irq_config_id = SMD_MODEM,
+		.subsys_name = "modem",
+		.edge = SMD_APPS_MODEM,
+
+		.smd_int.irq_name = "modem_smd_in",
+		.smd_int.flags = IRQF_TRIGGER_RISING,
+		.smd_int.irq_id = -1,
+		.smd_int.device_name = "smd_dev",
+		.smd_int.dev_id = 0,
+		.smd_int.out_bit_pos = 1 << 12,
+		.smd_int.out_base = (void __iomem *)MSM_APCS_GCC_BASE,
+		.smd_int.out_offset = 0x8,
+
+		.smsm_int.irq_name = "modem_smsm_in",
+		.smsm_int.flags = IRQF_TRIGGER_RISING,
+		.smsm_int.irq_id = -1,
+		.smsm_int.device_name = "smsm_dev",
+		.smsm_int.dev_id = 0,
+		.smsm_int.out_bit_pos = 1 << 13,
+		.smsm_int.out_base = (void __iomem *)MSM_APCS_GCC_BASE,
+		.smsm_int.out_offset = 0x8,
+	},
+	{
+		.irq_config_id = SMD_Q6,
+		.subsys_name = "adsp",
+		.edge = SMD_APPS_QDSP,
+
+		.smd_int.irq_name = "adsp_smd_in",
+		.smd_int.flags = IRQF_TRIGGER_RISING,
+		.smd_int.irq_id = -1,
+		.smd_int.device_name = "smd_dev",
+		.smd_int.dev_id = 0,
+		.smd_int.out_bit_pos = 1 << 8,
+		.smd_int.out_base = (void __iomem *)MSM_APCS_GCC_BASE,
+		.smd_int.out_offset = 0x8,
+
+		.smsm_int.irq_name = "adsp_smsm_in",
+		.smsm_int.flags = IRQF_TRIGGER_RISING,
+		.smsm_int.irq_id = -1,
+		.smsm_int.device_name = "smsm_dev",
+		.smsm_int.dev_id = 0,
+		.smsm_int.out_bit_pos = 1 << 9,
+		.smsm_int.out_base = (void __iomem *)MSM_APCS_GCC_BASE,
+		.smsm_int.out_offset = 0x8,
+	},
+	{
+		.irq_config_id = SMD_RPM,
+		.subsys_name = NULL, /* do not use PIL to load RPM */
+		.edge = SMD_APPS_RPM,
+
+		.smd_int.irq_name = "rpm_smd_in",
+		.smd_int.flags = IRQF_TRIGGER_RISING,
+		.smd_int.irq_id = -1,
+		.smd_int.device_name = "smd_dev",
+		.smd_int.dev_id = 0,
+		.smd_int.out_bit_pos = 1 << 0,
+		.smd_int.out_base = (void __iomem *)MSM_APCS_GCC_BASE,
+		.smd_int.out_offset = 0x8,
+
+		.smsm_int.irq_name = NULL, /* RPM does not support SMSM */
+		.smsm_int.flags = 0,
+		.smsm_int.irq_id = 0,
+		.smsm_int.device_name = NULL,
+		.smsm_int.dev_id = 0,
+		.smsm_int.out_bit_pos = 0,
+		.smsm_int.out_base = NULL,
+		.smsm_int.out_offset = 0,
+	},
+};
+
+static struct smd_smem_regions aux_smem_areas[] = {
+	{
+		.phys_addr = (void *)(0xfc428000),
+		.size = 0x4000,
+	},
+};
+
+static struct smd_subsystem_restart_config smd_ssr_cfg = {
+	.disable_smsm_reset_handshake = 1,
+};
+
+static struct smd_platform smd_platform_data = {
+	.num_ss_configs = ARRAY_SIZE(smd_config_list),
+	.smd_ss_configs = smd_config_list,
+	.smd_ssr_config = &smd_ssr_cfg,
+	.num_smem_areas = ARRAY_SIZE(aux_smem_areas),
+	.smd_smem_areas = aux_smem_areas,
+};
+
+struct platform_device msm_device_smd_9625 = {
+	.name   = "msm_smd",
+	.id     = -1,
+	.resource = smd_resource,
+	.num_resources = ARRAY_SIZE(smd_resource),
+	.dev = {
+		.platform_data = &smd_platform_data,
+	}
+};
+
+void __init msm9625_add_devices(void)
+{
+	platform_device_register(&msm_device_smd_9625);
+}
+
+/*
+ * Used to satisfy dependencies for devices that need to be
+ * run early or in a particular order. Most likely your device doesn't fall
+ * into this category, and thus the driver should not be added here.
+ * EPROBE_DEFER can satisfy most dependency problems.
+ */
+void __init msm9625_add_drivers(void)
+{
+	msm_init_modem_notifier_list();
+	msm_smd_init();
+	msm_rpm_driver_init();
+	msm_lpmrs_module_init();
+	rpm_regulator_smd_driver_init();
+	msm_spm_device_init();
+	msm_clock_init(&msm9625_clock_init_data);
+}
 
 void __init msm9625_init(void)
 {
@@ -141,17 +279,20 @@
 		pr_err("%s: socinfo_init() failed\n", __func__);
 
 	msm9625_init_gpiomux();
-	msm_clock_init(&msm_dummy_clock_init_data);
 	of_platform_populate(NULL, of_default_bus_match_table,
 			msm9625_auxdata_lookup, NULL);
+	msm9625_add_devices();
+	msm9625_add_drivers();
 }
 
-DT_MACHINE_START(MSM_DT, "Qualcomm MSM (Flattened Device Tree)")
+DT_MACHINE_START(MSM9625_DT, "Qualcomm MSM 9625 (Flattened Device Tree)")
 	.map_io = msm_map_msm9625_io,
-	.init_irq = msm9625_init_irq,
+	.init_irq = msm_dt_init_irq_l2x0,
 	.init_machine = msm9625_init,
 	.handle_irq = gic_handle_irq,
 	.timer = &msm_dt_timer,
 	.dt_compat = msm9625_dt_match,
 	.reserve = msm9625_reserve,
+	.init_very_early = msm9625_early_memory,
+	.restart = msm_restart,
 MACHINE_END
diff --git a/arch/arm/mach-msm/board-dt.c b/arch/arm/mach-msm/board-dt.c
index 8f9a0ef..74b0d0d 100644
--- a/arch/arm/mach-msm/board-dt.c
+++ b/arch/arm/mach-msm/board-dt.c
@@ -10,83 +10,68 @@
  * GNU General Public License for more details.
  */
 
+#include <linux/gpio.h>
 #include <linux/kernel.h>
-#include <linux/errno.h>
 #include <linux/of.h>
-#include <linux/of_address.h>
-#include <linux/of_platform.h>
-#include <linux/of_fdt.h>
 #include <linux/of_irq.h>
-#include <asm/hardware/gic.h>
+#include <linux/mfd/wcd9xxx/core.h>
 #include <asm/arch_timer.h>
-#include <asm/mach/arch.h>
 #include <asm/mach/time.h>
-#include <mach/socinfo.h>
-#include <mach/board.h>
-#include <mach/restart.h>
+#include <asm/hardware/cache-l2x0.h>
+#include <asm/hardware/gic.h>
+#include <mach/mpm.h>
+#include <mach/qpnp-int.h>
+#include <mach/scm.h>
+
+#include "board-dt.h"
+
+#define SCM_SVC_L2CC_PL310	16
+#define L2CC_PL310_CTRL_ID	1
+#define L2CC_PL310_ON		1
 
 static void __init msm_dt_timer_init(void)
 {
 	arch_timer_of_register();
 }
 
-static struct sys_timer msm_dt_timer = {
+struct sys_timer msm_dt_timer = {
 	.init = msm_dt_timer_init
 };
 
-static void __init msm_dt_init_irq(void)
-{
-	if (machine_is_msm8974())
-		msm_8974_init_irq();
-}
-
-static void __init msm_dt_map_io(void)
-{
-	if (early_machine_is_msm8974())
-		msm_map_8974_io();
-	if (socinfo_init() < 0)
-		pr_err("%s: socinfo_init() failed\n", __func__);
-}
-
-static void __init msm_dt_init(void)
-{
-	struct of_dev_auxdata *adata = NULL;
-
-	if (machine_is_msm8974())
-		msm_8974_init(&adata);
-
-	of_platform_populate(NULL, of_default_bus_match_table, adata, NULL);
-	if (machine_is_msm8974()) {
-		msm_8974_add_devices();
-		msm_8974_add_drivers();
-	}
-}
-
-static const char *msm_dt_match[] __initconst = {
-	"qcom,msm8974",
-	NULL
+static struct of_device_id irq_match[] __initdata  = {
+	{ .compatible = "qcom,msm-qgic2", .data = gic_of_init, },
+	{ .compatible = "qcom,msm-gpio", .data = msm_gpio_of_init, },
+	{ .compatible = "qcom,spmi-pmic-arb", .data = qpnpint_of_init, },
+	{ .compatible = "qcom,wcd9xxx-irq", .data = wcd9xxx_irq_of_init, },
+	{}
 };
 
-static void __init msm_dt_reserve(void)
+static struct of_device_id mpm_match[] __initdata = {
+	{.compatible = "qcom,mpm-v2", },
+	{}
+};
+
+void __init msm_dt_init_irq(void)
 {
-	if (early_machine_is_msm8974())
-		msm_8974_reserve();
+	struct device_node *node;
+
+	of_irq_init(irq_match);
+	node = of_find_matching_node(NULL, mpm_match);
+
+	WARN_ON(!node);
+
+	if (node)
+		of_mpm_init(node);
 }
 
-static void __init msm_dt_init_very_early(void)
+void __init msm_dt_init_irq_nompm(void)
 {
-	if (early_machine_is_msm8974())
-		msm_8974_very_early();
+	of_irq_init(irq_match);
 }
 
-DT_MACHINE_START(MSM_DT, "Qualcomm MSM (Flattened Device Tree)")
-	.map_io = msm_dt_map_io,
-	.init_irq = msm_dt_init_irq,
-	.init_machine = msm_dt_init,
-	.handle_irq = gic_handle_irq,
-	.timer = &msm_dt_timer,
-	.dt_compat = msm_dt_match,
-	.reserve = msm_dt_reserve,
-	.init_very_early = msm_dt_init_very_early,
-	.restart = msm_restart,
-MACHINE_END
+void __init msm_dt_init_irq_l2x0(void)
+{
+	scm_call_atomic1(SCM_SVC_L2CC_PL310, L2CC_PL310_CTRL_ID, L2CC_PL310_ON);
+	l2x0_of_init(0, ~0UL);
+	msm_dt_init_irq();
+}
diff --git a/arch/arm/mach-msm/board-dt.h b/arch/arm/mach-msm/board-dt.h
new file mode 100644
index 0000000..cc3e92c
--- /dev/null
+++ b/arch/arm/mach-msm/board-dt.h
@@ -0,0 +1,16 @@
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+extern struct sys_timer msm_dt_timer;
+void __init msm_dt_init_irq(void);
+void __init msm_dt_init_irq_nompm(void);
+void __init msm_dt_init_irq_l2x0(void);
diff --git a/arch/arm/mach-msm/board-msm7627a-bt.c b/arch/arm/mach-msm/board-msm7627a-bt.c
index e4edf9b..1c2d8a2 100644
--- a/arch/arm/mach-msm/board-msm7627a-bt.c
+++ b/arch/arm/mach-msm/board-msm7627a-bt.c
@@ -107,6 +107,8 @@
 		gpio_bt_sys_rest_en = 16;
 	if (machine_is_msm8625_qrd7())
 		gpio_bt_sys_rest_en = 88;
+	if (machine_is_qrd_skud_prime())
+		gpio_bt_sys_rest_en = 35;
 	if (machine_is_msm7627a_qrd3()) {
 		if (socinfo == 0x70002)
 			gpio_bt_sys_rest_en = 88;
@@ -975,7 +977,6 @@
 	int i, rc = 0;
 	struct device *dev;
 
-
 	gpio_bt_config();
 
 	rc = i2c_register_board_info(MSM_GSBI1_QUP_I2C_BUS_ID,
diff --git a/arch/arm/mach-msm/board-msm7627a-camera.c b/arch/arm/mach-msm/board-msm7627a-camera.c
index b5f214b..79ad996 100644
--- a/arch/arm/mach-msm/board-msm7627a-camera.c
+++ b/arch/arm/mach-msm/board-msm7627a-camera.c
@@ -403,7 +403,8 @@
 	if (machine_is_msm8625_evb() || machine_is_msm7627a_evb()
 				||  machine_is_msm8625_evt()
 				|| machine_is_msm7627a_qrd3()
-				|| machine_is_msm8625_qrd7()) {
+				|| machine_is_msm8625_qrd7()
+				|| machine_is_qrd_skud_prime()) {
 		sensor_board_info_ov7692.cam_vreg =
 			ov7692_gpio_vreg;
 		sensor_board_info_ov7692.num_vreg =
@@ -420,7 +421,8 @@
 	platform_device_register(&msm_camera_server);
 	if (machine_is_msm8625_surf() || machine_is_msm8625_evb()
 			|| machine_is_msm8625_evt()
-			|| machine_is_msm8625_qrd7()) {
+			|| machine_is_msm8625_qrd7()
+			|| machine_is_qrd_skud_prime()) {
 		platform_device_register(&msm8625_device_csic0);
 		platform_device_register(&msm8625_device_csic1);
 	} else {
@@ -429,7 +431,8 @@
 	}
 	if (machine_is_msm8625_evb()
 			|| machine_is_msm8625_evt()
-			|| machine_is_msm8625_qrd7())
+			|| machine_is_msm8625_qrd7()
+			|| machine_is_qrd_skud_prime())
 		*(int *) msm7x27a_device_clkctl.dev.platform_data = 1;
 	platform_device_register(&msm7x27a_device_clkctl);
 	platform_device_register(&msm7x27a_device_vfe);
@@ -1175,7 +1178,6 @@
 #ifndef CONFIG_MSM_CAMERA_V4L2
 	int rc;
 #endif
-
 	pr_debug("msm7627a_camera_init Entered\n");
 
 	if (machine_is_msm7627a_qrd3() || machine_is_msm8625_qrd7()) {
@@ -1194,7 +1196,8 @@
 	if (machine_is_msm7627a_evb() || machine_is_msm8625_evb()
 			|| machine_is_msm8625_evt()
 			|| machine_is_msm7627a_qrd3()
-			|| machine_is_msm8625_qrd7()) {
+			|| machine_is_msm8625_qrd7()
+			|| machine_is_qrd_skud_prime()) {
 #ifndef CONFIG_MSM_CAMERA_V4L2
 		lcd_camera_power_init();
 #endif
@@ -1210,7 +1213,8 @@
 			|| machine_is_msm8625_evb()
 			|| machine_is_msm8625_evt()
 			|| machine_is_msm7627a_qrd3()
-			|| machine_is_msm8625_qrd7()) {
+			|| machine_is_msm8625_qrd7()
+			|| machine_is_qrd_skud_prime()) {
 		platform_add_devices(camera_devices_evb,
 				ARRAY_SIZE(camera_devices_evb));
 	} else if (machine_is_msm7627a_qrd3())
@@ -1223,7 +1227,8 @@
 					|| !machine_is_msm8625_evb()
 					|| !machine_is_msm8625_evt()
 					|| !machine_is_msm7627a_qrd3()
-					|| !machine_is_msm8625_qrd7())
+					|| !machine_is_msm8625_qrd7()
+					|| !machine_is_qrd_skud_prime())
 		register_i2c_devices();
 #ifndef CONFIG_MSM_CAMERA_V4L2
 	rc = regulator_bulk_get(NULL, ARRAY_SIZE(regs_camera), regs_camera);
@@ -1253,7 +1258,8 @@
 			|| machine_is_msm8625_evb()
 			|| machine_is_msm8625_evt()
 			|| machine_is_msm7627a_qrd3()
-			|| machine_is_msm8625_qrd7()) {
+			|| machine_is_msm8625_qrd7()
+			|| machine_is_qrd_skud_prime()) {
 		pr_debug("machine_is_msm7627a_evb i2c_register_board_info\n");
 		i2c_register_board_info(MSM_GSBI0_QUP_I2C_BUS_ID,
 				i2c_camera_devices_evb,
diff --git a/arch/arm/mach-msm/board-msm7627a-display.c b/arch/arm/mach-msm/board-msm7627a-display.c
index d62254a..1249c7b 100644
--- a/arch/arm/mach-msm/board-msm7627a-display.c
+++ b/arch/arm/mach-msm/board-msm7627a-display.c
@@ -542,7 +542,8 @@
 		if (!strncmp(name, "lcdc_truly_hvga_ips3p2335_pt", 28))
 			ret = 0;
 	} else if (machine_is_msm7627a_evb() || machine_is_msm8625_evb() ||
-			machine_is_msm8625_evt()) {
+			machine_is_msm8625_evt() ||
+			machine_is_qrd_skud_prime()) {
 		if (!strncmp(name, "mipi_cmd_nt35510_wvga", 21))
 			ret = 0;
 	}
@@ -796,7 +797,8 @@
 	if (machine_is_msm7625a_surf() || machine_is_msm7625a_ffa())
 		fb_size = MSM7x25A_MSM_FB_SIZE;
 	else if (machine_is_msm7627a_evb() || machine_is_msm8625_evb()
-						|| machine_is_msm8625_evt())
+						|| machine_is_msm8625_evt()
+						|| machine_is_qrd_skud_prime())
 		fb_size = MSM8x25_MSM_FB_SIZE;
 	else
 		fb_size = MSM_FB_SIZE;
@@ -1017,7 +1019,8 @@
 	if (machine_is_msm7627a_qrd1())
 		rc = msm_fb_dsi_client_qrd1_reset();
 	else if (machine_is_msm7627a_evb() || machine_is_msm8625_evb()
-						|| machine_is_msm8625_evt())
+						|| machine_is_msm8625_evt()
+						|| machine_is_qrd_skud_prime())
 		rc = msm_fb_dsi_client_qrd3_reset();
 	else
 		rc = msm_fb_dsi_client_msm_reset();
@@ -1124,10 +1127,12 @@
 			wmb();
 			lcdc_reset_cfg |= 1;
 			writel_relaxed(lcdc_reset_cfg, lcdc_reset_ptr);
+			msleep(20);
 		} else {
 			gpio_set_value_cansleep(GPIO_LCDC_BRDG_RESET_N, 0);
 			msleep(20);
 			gpio_set_value_cansleep(GPIO_LCDC_BRDG_RESET_N, 1);
+			msleep(20);
 		}
 	} else {
 		gpio_set_value_cansleep(GPIO_LCDC_BRDG_PD, 1);
@@ -1325,7 +1330,8 @@
 	if (machine_is_msm7627a_qrd1())
 		rc = mipi_dsi_panel_qrd1_power(on);
 	else if (machine_is_msm7627a_evb() || machine_is_msm8625_evb()
-						|| machine_is_msm8625_evt())
+						|| machine_is_msm8625_evt()
+						|| machine_is_qrd_skud_prime())
 		rc = mipi_dsi_panel_qrd3_power(on);
 	else
 		rc = mipi_dsi_panel_msm_power(on);
@@ -1389,7 +1395,8 @@
 		platform_add_devices(qrd_fb_devices,
 				ARRAY_SIZE(qrd_fb_devices));
 	} else if (machine_is_msm7627a_evb() || machine_is_msm8625_evb()
-						|| machine_is_msm8625_evt()) {
+					|| machine_is_msm8625_evt()
+					|| machine_is_qrd_skud_prime()) {
 		mipi_NT35510_pdata.bl_lock = 1;
 		mipi_NT35516_pdata.bl_lock = 1;
 		if (disable_splash)
@@ -1398,7 +1405,10 @@
 		platform_add_devices(evb_fb_devices,
 				ARRAY_SIZE(evb_fb_devices));
 	} else if (machine_is_msm7627a_qrd3() || machine_is_msm8625_qrd7()) {
-		mdp_pdata.cont_splash_enabled = 0x1;
+		if (machine_is_msm7627a_qrd3())
+			mdp_pdata.cont_splash_enabled = 0x0;
+		else
+			mdp_pdata.cont_splash_enabled = 0x1;
 		platform_add_devices(qrd3_fb_devices,
 						ARRAY_SIZE(qrd3_fb_devices));
 	} else {
@@ -1418,7 +1428,8 @@
 	msm_fb_register_device("mipi_dsi", &mipi_dsi_pdata);
 #endif
 	if (machine_is_msm7627a_evb() || machine_is_msm8625_evb()
-					|| machine_is_msm8625_evt()) {
+					|| machine_is_msm8625_evt()
+					|| machine_is_qrd_skud_prime()) {
 		gpio_reg_2p85v = regulator_get(&mipi_dsi_device.dev,
 								"lcd_vdd");
 		if (IS_ERR(gpio_reg_2p85v))
diff --git a/arch/arm/mach-msm/board-msm7627a-io.c b/arch/arm/mach-msm/board-msm7627a-io.c
index 6e3d10a..2983dc0 100644
--- a/arch/arm/mach-msm/board-msm7627a-io.c
+++ b/arch/arm/mach-msm/board-msm7627a-io.c
@@ -607,6 +607,8 @@
 #define FT5X06_IRQ_GPIO		48
 #define FT5X06_RESET_GPIO	26
 
+#define FT5X16_IRQ_GPIO		122
+
 static ssize_t
 ft5x06_virtual_keys_register(struct kobject *kobj,
 			     struct kobj_attribute *attr,
@@ -620,6 +622,17 @@
 	"\n");
 }
 
+static ssize_t ft5x16_virtual_keys_register(struct kobject *kobj,
+		struct kobj_attribute *attr, char *buf)
+{
+	return snprintf(buf, 200, \
+	__stringify(EV_KEY) ":" __stringify(KEY_HOME) ":68:984:135:50" \
+	":" __stringify(EV_KEY) ":" __stringify(KEY_MENU) ":203:984:135:50" \
+	":" __stringify(EV_KEY) ":" __stringify(KEY_BACK) ":338:984:135:50" \
+	":" __stringify(EV_KEY) ":" __stringify(KEY_SEARCH) ":473:984:135:50" \
+	"\n");
+}
+
 static struct kobj_attribute ft5x06_virtual_keys_attr = {
 	.attr = {
 		.name = "virtualkeys.ft5x06_ts",
@@ -658,13 +671,28 @@
 static void __init ft5x06_touchpad_setup(void)
 {
 	int rc;
+	int irq_gpio;
 
-	rc = gpio_tlmm_config(GPIO_CFG(FT5X06_IRQ_GPIO, 0,
+	if (machine_is_qrd_skud_prime()) {
+		irq_gpio = FT5X16_IRQ_GPIO;
+
+		ft5x06_platformdata.x_max = 540;
+		ft5x06_platformdata.y_max = 960;
+		ft5x06_platformdata.irq_gpio = FT5X16_IRQ_GPIO;
+
+		ft5x06_device_info[0].irq = MSM_GPIO_TO_INT(FT5X16_IRQ_GPIO);
+
+		ft5x06_virtual_keys_attr.show = &ft5x16_virtual_keys_register;
+	} else {
+		irq_gpio = FT5X06_IRQ_GPIO;
+	}
+
+	rc = gpio_tlmm_config(GPIO_CFG(irq_gpio, 0,
 			GPIO_CFG_INPUT, GPIO_CFG_PULL_UP,
 			GPIO_CFG_8MA), GPIO_CFG_ENABLE);
 	if (rc)
 		pr_err("%s: gpio_tlmm_config for %d failed\n",
-			__func__, FT5X06_IRQ_GPIO);
+			__func__, irq_gpio);
 
 	rc = gpio_tlmm_config(GPIO_CFG(FT5X06_RESET_GPIO, 0,
 			GPIO_CFG_OUTPUT, GPIO_CFG_PULL_DOWN,
@@ -845,7 +873,8 @@
 		i2c_register_board_info(MSM_GSBI1_QUP_I2C_BUS_ID,
 					mxt_device_info,
 					ARRAY_SIZE(mxt_device_info));
-	} else if (machine_is_msm7627a_qrd3() || machine_is_msm8625_qrd7()) {
+	} else if (machine_is_msm7627a_qrd3() || machine_is_msm8625_qrd7()
+				|| machine_is_qrd_skud_prime()) {
 		ft5x06_touchpad_setup();
 	}
 
diff --git a/arch/arm/mach-msm/board-msm7627a-storage.c b/arch/arm/mach-msm/board-msm7627a-storage.c
index 49ff393..07ff389 100644
--- a/arch/arm/mach-msm/board-msm7627a-storage.c
+++ b/arch/arm/mach-msm/board-msm7627a-storage.c
@@ -378,9 +378,9 @@
 	if (mmc_regulator_init(1, "mmc", 2850000))
 		return;
 	/* 8x25 EVT do not use hw detector */
-	if (!(machine_is_msm8625_evt()))
+	if (!((machine_is_msm8625_evt() || machine_is_qrd_skud_prime())))
 		sdc1_plat_data.status_irq = MSM_GPIO_TO_INT(gpio_sdc1_hw_det);
-	if (machine_is_msm8625_evt())
+	if (machine_is_msm8625_evt() || machine_is_qrd_skud_prime())
 		sdc1_plat_data.status = NULL;
 
 	msm_add_sdcc(1, &sdc1_plat_data);
diff --git a/arch/arm/mach-msm/board-msm7627a-wlan.c b/arch/arm/mach-msm/board-msm7627a-wlan.c
index 79f213e..ab29fc5 100644
--- a/arch/arm/mach-msm/board-msm7627a-wlan.c
+++ b/arch/arm/mach-msm/board-msm7627a-wlan.c
@@ -23,6 +23,7 @@
 
 #define GPIO_WLAN_3V3_EN 119
 static const char *id = "WLAN";
+static bool wlan_powered_up;
 
 enum {
 	WLAN_VREG_S3 = 0,
@@ -52,7 +53,8 @@
 					|| machine_is_msm8625_evb()
 					|| machine_is_msm8625_evt()
 					|| machine_is_msm7627a_qrd3()
-					|| machine_is_msm8625_qrd7())
+					|| machine_is_msm8625_qrd7()
+					|| machine_is_qrd_skud_prime())
 		gpio_wlan_sys_rest_en = 124;
 }
 
@@ -199,6 +201,11 @@
 	int rc = 0;
 	static bool init_done;
 
+	if (wlan_powered_up) {
+		pr_info("WLAN already powered up\n");
+		return 0;
+	}
+
 	if (unlikely(!init_done)) {
 		gpio_wlan_config();
 		rc = qrf6285_init_regs();
@@ -237,7 +244,8 @@
 					|| machine_is_msm8625_evb()
 					|| machine_is_msm8625_evt()
 					|| machine_is_msm7627a_qrd3()
-					|| machine_is_msm8625_qrd7()) {
+					|| machine_is_msm8625_qrd7()
+					|| machine_is_qrd_skud_prime()) {
 		rc = gpio_tlmm_config(GPIO_CFG(gpio_wlan_sys_rest_en, 0,
 					GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL,
 					GPIO_CFG_2MA), GPIO_CFG_ENABLE);
@@ -279,13 +287,17 @@
 	}
 
 	pr_info("WLAN power-up success\n");
+	wlan_powered_up = true;
 	return 0;
 set_clock_fail:
 	setup_wlan_clock(0);
 set_gpio_fail:
 	setup_wlan_gpio(0);
 gpio_fail:
-	gpio_free(gpio_wlan_sys_rest_en);
+	if (!(machine_is_msm7627a_qrd1() || machine_is_msm7627a_evb() ||
+	    machine_is_msm8625_evb() || machine_is_msm8625_evt() ||
+	    machine_is_msm7627a_qrd3() || machine_is_msm8625_qrd7()))
+			gpio_free(gpio_wlan_sys_rest_en);
 qrd_gpio_fail:
 	/* GPIO_WLAN_3V3_EN is only required for the QRD7627a */
 	if (machine_is_msm7627a_qrd1())
@@ -294,6 +306,7 @@
 	wlan_switch_regulators(0);
 out:
 	pr_info("WLAN power-up failed\n");
+	wlan_powered_up = false;
 	return rc;
 }
 
@@ -301,6 +314,11 @@
 {
 	int rc = 0;
 
+	if (!wlan_powered_up) {
+		pr_info("WLAN is not powered up, returning success\n");
+		return 0;
+	}
+
 	/* Disable the A0 clock */
 	rc = setup_wlan_clock(on);
 	if (rc) {
@@ -316,7 +334,8 @@
 					|| machine_is_msm8625_evb()
 					|| machine_is_msm8625_evt()
 					|| machine_is_msm7627a_qrd3()
-					|| machine_is_msm8625_qrd7()) {
+					|| machine_is_msm8625_qrd7()
+					|| machine_is_qrd_skud_prime()) {
 		rc = gpio_tlmm_config(GPIO_CFG(gpio_wlan_sys_rest_en, 0,
 					GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL,
 					GPIO_CFG_2MA), GPIO_CFG_ENABLE);
@@ -327,20 +346,12 @@
 		}
 		gpio_set_value(gpio_wlan_sys_rest_en, 0);
 	} else {
-		rc = gpio_request(gpio_wlan_sys_rest_en, "WLAN_DEEP_SLEEP_N");
-		if (!rc) {
-			rc = setup_wlan_gpio(on);
-			if (rc) {
-				pr_err("%s: setup_wlan_gpio = %d\n",
-					__func__, rc);
-				goto set_gpio_fail;
-			}
-			gpio_free(gpio_wlan_sys_rest_en);
-		} else {
-			pr_err("%s: WLAN sys_rest_en GPIO %d request failed %d\n",
-				__func__, gpio_wlan_sys_rest_en, rc);
-			goto out;
+		rc = setup_wlan_gpio(on);
+		if (rc) {
+			pr_err("%s: setup_wlan_gpio = %d\n", __func__, rc);
+			goto set_gpio_fail;
 		}
+		gpio_free(gpio_wlan_sys_rest_en);
 	}
 
 	/* GPIO_WLAN_3V3_EN is only required for the QRD7627a */
@@ -362,7 +373,7 @@
 			__func__, rc);
 		goto reg_disable;
 	}
-
+	wlan_powered_up = false;
 	pr_info("WLAN power-down success\n");
 	return 0;
 set_clock_fail:
@@ -370,14 +381,16 @@
 set_gpio_fail:
 	setup_wlan_gpio(0);
 gpio_fail:
-	gpio_free(gpio_wlan_sys_rest_en);
+	if (!(machine_is_msm7627a_qrd1() || machine_is_msm7627a_evb() ||
+	    machine_is_msm8625_evb() || machine_is_msm8625_evt() ||
+	    machine_is_msm7627a_qrd3() || machine_is_msm8625_qrd7()))
+			gpio_free(gpio_wlan_sys_rest_en);
 qrd_gpio_fail:
 	/* GPIO_WLAN_3V3_EN is only required for the QRD7627a */
 	if (machine_is_msm7627a_qrd1())
 		gpio_free(GPIO_WLAN_3V3_EN);
 reg_disable:
 	wlan_switch_regulators(0);
-out:
 	pr_info("WLAN power-down failed\n");
 	return rc;
 }
diff --git a/arch/arm/mach-msm/board-msm7x27a.c b/arch/arm/mach-msm/board-msm7x27a.c
index 13c4be2..4e14ff3 100644
--- a/arch/arm/mach-msm/board-msm7x27a.c
+++ b/arch/arm/mach-msm/board-msm7x27a.c
@@ -375,7 +375,8 @@
 };
 
 /* 8625 PM platform data */
-static struct msm_pm_platform_data msm8625_pm_data[MSM_PM_SLEEP_MODE_NR * 2] = {
+static struct msm_pm_platform_data
+		msm8625_pm_data[MSM_PM_SLEEP_MODE_NR * CONFIG_NR_CPUS] = {
 	/* CORE0 entries */
 	[MSM_PM_MODE(0, MSM_PM_SLEEP_MODE_POWER_COLLAPSE)] = {
 					.idle_supported = 1,
@@ -433,6 +434,44 @@
 					.residency = 10,
 	},
 
+	/* picked latency & redisdency values from 7x30 */
+	[MSM_PM_MODE(2, MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE)] = {
+					.idle_supported = 1,
+					.suspend_supported = 1,
+					.idle_enabled = 0,
+					.suspend_enabled = 0,
+					.latency = 500,
+					.residency = 6000,
+	},
+
+	[MSM_PM_MODE(2, MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT)] = {
+					.idle_supported = 1,
+					.suspend_supported = 1,
+					.idle_enabled = 1,
+					.suspend_enabled = 1,
+					.latency = 2,
+					.residency = 10,
+	},
+
+	/* picked latency & redisdency values from 7x30 */
+	[MSM_PM_MODE(3, MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE)] = {
+					.idle_supported = 1,
+					.suspend_supported = 1,
+					.idle_enabled = 0,
+					.suspend_enabled = 0,
+					.latency = 500,
+					.residency = 6000,
+	},
+
+	[MSM_PM_MODE(3, MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT)] = {
+					.idle_supported = 1,
+					.suspend_supported = 1,
+					.idle_enabled = 1,
+					.suspend_enabled = 1,
+					.latency = 2,
+					.residency = 10,
+	},
+
 };
 
 static struct msm_pm_boot_platform_data msm_pm_8625_boot_pdata __initdata = {
diff --git a/arch/arm/mach-msm/board-qrd7627a.c b/arch/arm/mach-msm/board-qrd7627a.c
index ec8e438..023ce86 100644
--- a/arch/arm/mach-msm/board-qrd7627a.c
+++ b/arch/arm/mach-msm/board-qrd7627a.c
@@ -32,6 +32,8 @@
 #include <linux/msm_adc.h>
 #include <linux/regulator/msm-gpio-regulator.h>
 #include <linux/msm_ion.h>
+#include <linux/i2c-gpio.h>
+#include <linux/regulator/onsemi-ncp6335d.h>
 #include <asm/mach/mmc.h>
 #include <asm/mach-types.h>
 #include <asm/mach/arch.h>
@@ -128,6 +130,27 @@
 	.msm_i2c_config_gpio	= gsbi_qup_i2c_gpio_config,
 };
 
+static struct msm_gpio i2c_gpio_config[] = {
+	{ GPIO_CFG(39, 0, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_8MA),
+		"qup_scl" },
+	{ GPIO_CFG(36, 0, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_8MA),
+		"qup_sda" },
+};
+
+static struct i2c_gpio_platform_data i2c_gpio_pdata = {
+	.scl_pin = 39,
+	.sda_pin = 36,
+	.udelay = 5, /* 100 Khz */
+};
+
+static struct platform_device msm_i2c_gpio = {
+	.name	= "i2c-gpio",
+	.id	= 2,
+	.dev	= {
+		.platform_data = &i2c_gpio_pdata,
+	}
+};
+
 #ifdef CONFIG_ARCH_MSM7X27A
 
 #define MSM_RESERVE_MDP_SIZE       0x1B00000
@@ -326,7 +349,8 @@
 };
 
 /* 8625 PM platform data */
-static struct msm_pm_platform_data msm8625_pm_data[MSM_PM_SLEEP_MODE_NR * 2] = {
+static struct msm_pm_platform_data
+		msm8625_pm_data[MSM_PM_SLEEP_MODE_NR * CONFIG_NR_CPUS] = {
 	/* CORE0 entries */
 	[MSM_PM_MODE(0, MSM_PM_SLEEP_MODE_POWER_COLLAPSE)] = {
 					.idle_supported = 1,
@@ -384,6 +408,44 @@
 					.residency = 10,
 	},
 
+	/* picked latency & redisdency values from 7x30 */
+	[MSM_PM_MODE(2, MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE)] = {
+					.idle_supported = 1,
+					.suspend_supported = 1,
+					.idle_enabled = 0,
+					.suspend_enabled = 0,
+					.latency = 500,
+					.residency = 6000,
+	},
+
+	[MSM_PM_MODE(2, MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT)] = {
+					.idle_supported = 1,
+					.suspend_supported = 1,
+					.idle_enabled = 1,
+					.suspend_enabled = 1,
+					.latency = 2,
+					.residency = 10,
+	},
+
+	/* picked latency & redisdency values from 7x30 */
+	[MSM_PM_MODE(3, MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE)] = {
+					.idle_supported = 1,
+					.suspend_supported = 1,
+					.idle_enabled = 0,
+					.suspend_enabled = 0,
+					.latency = 500,
+					.residency = 6000,
+	},
+
+	[MSM_PM_MODE(3, MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT)] = {
+					.idle_supported = 1,
+					.suspend_supported = 1,
+					.idle_enabled = 1,
+					.suspend_enabled = 1,
+					.latency = 2,
+					.residency = 10,
+	},
+
 };
 
 static struct msm_pm_boot_platform_data msm_pm_8625_boot_pdata __initdata = {
@@ -597,6 +659,41 @@
 	},
 };
 
+/* Regulator configuration for the NCP6335D buck */
+struct regulator_consumer_supply ncp6335d_consumer_supplies[] = {
+	REGULATOR_SUPPLY("ncp6335d", NULL),
+};
+
+static struct regulator_init_data ncp6335d_init_data = {
+	.constraints	= {
+		.name		= "ncp6335d_sw",
+		.min_uV		= 600000,
+		.max_uV		= 1400000,
+		.valid_ops_mask	= REGULATOR_CHANGE_VOLTAGE |
+				REGULATOR_CHANGE_STATUS |
+				REGULATOR_CHANGE_MODE,
+		.valid_modes_mask = REGULATOR_MODE_NORMAL |
+				REGULATOR_MODE_FAST,
+		.initial_mode	= REGULATOR_MODE_NORMAL,
+		.always_on	= 1,
+	},
+	.num_consumer_supplies = ARRAY_SIZE(ncp6335d_consumer_supplies),
+	.consumer_supplies = ncp6335d_consumer_supplies,
+};
+
+static struct ncp6335d_platform_data ncp6335d_pdata = {
+	.init_data = &ncp6335d_init_data,
+	.default_vsel = NCP6335D_VSEL0,
+	.slew_rate_ns = 166,
+};
+
+static struct i2c_board_info i2c2_info[] __initdata = {
+	{
+		I2C_BOARD_INFO("ncp6335d", 0x38 >> 1),
+		.platform_data = &ncp6335d_pdata,
+	},
+};
+
 static struct platform_device *common_devices[] __initdata = {
 	&android_usb_device,
 	&msm_batt_device,
@@ -651,6 +748,7 @@
 	&msm8625_device_smd,
 	&msm8625_gsbi0_qup_i2c_device,
 	&msm8625_gsbi1_qup_i2c_device,
+	&msm_i2c_gpio,  /* TODO: Make this specific to 8625q */
 	&msm8625_device_uart1,
 	&msm8625_device_uart_dm1,
 	&msm8625_device_otg,
@@ -847,10 +945,20 @@
 
 static void __init msm8625_device_i2c_init(void)
 {
+	int i, rc;
+
 	msm8625_gsbi0_qup_i2c_device.dev.platform_data
 					= &msm_gsbi0_qup_i2c_pdata;
 	msm8625_gsbi1_qup_i2c_device.dev.platform_data
 					= &msm_gsbi1_qup_i2c_pdata;
+	if (machine_is_qrd_skud_prime()) {
+		for (i = 0 ; i < ARRAY_SIZE(i2c_gpio_config); i++) {
+			rc = gpio_tlmm_config(i2c_gpio_config[i].gpio_cfg,
+					GPIO_CFG_ENABLE);
+			if (rc)
+				pr_err("I2C-gpio tlmm config failed\n");
+		}
+	}
 }
 
 static struct platform_device msm_proccomm_regulator_dev = {
@@ -884,7 +992,8 @@
 static void __init add_platform_devices(void)
 {
 	if (machine_is_msm8625_evb() || machine_is_msm8625_qrd7()
-				|| machine_is_msm8625_evt()) {
+				|| machine_is_msm8625_evt()
+				|| machine_is_qrd_skud_prime()) {
 		platform_add_devices(msm8625_evb_devices,
 				ARRAY_SIZE(msm8625_evb_devices));
 		platform_add_devices(qrd3_devices,
@@ -899,7 +1008,8 @@
 				ARRAY_SIZE(qrd3_devices));
 
 	if (machine_is_msm7627a_evb() || machine_is_msm8625_evb()
-				|| machine_is_msm8625_evt())
+				|| machine_is_msm8625_evt()
+				|| machine_is_qrd_skud_prime())
 		platform_add_devices(msm8625_lcd_camera_devices,
 				ARRAY_SIZE(msm8625_lcd_camera_devices));
 	else if (machine_is_msm8625_qrd7())
@@ -995,6 +1105,11 @@
 	msm_pm_register_irqs();
 	msm_fb_add_devices();
 
+	if (machine_is_qrd_skud_prime())
+		i2c_register_board_info(2, i2c2_info,
+				ARRAY_SIZE(i2c2_info));
+
+
 #if defined(CONFIG_BT) && defined(CONFIG_MARIMBA_CORE)
 	msm7627a_bt_power_init();
 #endif
@@ -1070,3 +1185,13 @@
 	.init_early	= qrd7627a_init_early,
 	.handle_irq	= gic_handle_irq,
 MACHINE_END
+MACHINE_START(QRD_SKUD_PRIME, "QRD MSM8625 SKUD PRIME")
+	.atag_offset	= 0x100,
+	.map_io		= msm8625_map_io,
+	.reserve	= msm8625_reserve,
+	.init_irq	= msm8625_init_irq,
+	.init_machine	= msm_qrd_init,
+	.timer		= &msm_timer,
+	.init_early	= qrd7627a_init_early,
+	.handle_irq	= gic_handle_irq,
+MACHINE_END
diff --git a/arch/arm/mach-msm/clock-7x30.c b/arch/arm/mach-msm/clock-7x30.c
index e42fe65..e1390db 100644
--- a/arch/arm/mach-msm/clock-7x30.c
+++ b/arch/arm/mach-msm/clock-7x30.c
@@ -160,7 +160,8 @@
 	VDD_DIG_NONE,
 	VDD_DIG_LOW,
 	VDD_DIG_NOMINAL,
-	VDD_DIG_HIGH
+	VDD_DIG_HIGH,
+	VDD_DIG_NUM
 };
 
 static int set_vdd_dig(struct clk_vdd_class *vdd_class, int level)
@@ -184,15 +185,21 @@
 	return rc;
 }
 
-static DEFINE_VDD_CLASS(vdd_dig, set_vdd_dig);
+static DEFINE_VDD_CLASS(vdd_dig, set_vdd_dig, VDD_DIG_NUM);
 
 #define VDD_DIG_FMAX_MAP1(l1, f1) \
-	.vdd_class = &vdd_dig, \
-	.fmax[VDD_DIG_##l1] = (f1)
+	.vdd_class = &vdd_dig,			\
+	.fmax = (unsigned long[VDD_DIG_NUM]) {	\
+		[VDD_DIG_##l1] = (f1),		\
+	},					\
+	.num_fmax = VDD_DIG_NUM
 #define VDD_DIG_FMAX_MAP2(l1, f1, l2, f2) \
-	.vdd_class = &vdd_dig, \
-	.fmax[VDD_DIG_##l1] = (f1), \
-	.fmax[VDD_DIG_##l2] = (f2)
+	.vdd_class = &vdd_dig,			\
+	.fmax = (unsigned long[VDD_DIG_NUM]) {	\
+		[VDD_DIG_##l1] = (f1),		\
+		[VDD_DIG_##l2] = (f2),		\
+	},					\
+	.num_fmax = VDD_DIG_NUM
 
 #define PCOM_XO_DISABLE	0
 #define PCOM_XO_ENABLE	1
diff --git a/arch/arm/mach-msm/clock-8960.c b/arch/arm/mach-msm/clock-8960.c
index 2cd2cd4..3c417c3 100644
--- a/arch/arm/mach-msm/clock-8960.c
+++ b/arch/arm/mach-msm/clock-8960.c
@@ -376,7 +376,8 @@
 	VDD_DIG_NONE,
 	VDD_DIG_LOW,
 	VDD_DIG_NOMINAL,
-	VDD_DIG_HIGH
+	VDD_DIG_HIGH,
+	VDD_DIG_NUM
 };
 
 static int set_vdd_dig_8960(struct clk_vdd_class *vdd_class, int level)
@@ -391,7 +392,7 @@
 				    vdd_uv[level], 1150000, 1);
 }
 
-static DEFINE_VDD_CLASS(vdd_dig, set_vdd_dig_8960);
+static DEFINE_VDD_CLASS(vdd_dig, set_vdd_dig_8960, VDD_DIG_NUM);
 
 static int rpm_vreg_dig_8930 = RPM_VREG_ID_PM8038_VDD_DIG_CORNER;
 static int set_vdd_dig_8930(struct clk_vdd_class *vdd_class, int level)
@@ -409,21 +410,31 @@
 }
 
 #define VDD_DIG_FMAX_MAP1(l1, f1) \
-	.vdd_class = &vdd_dig, \
-	.fmax[VDD_DIG_##l1] = (f1)
+	.vdd_class = &vdd_dig,			\
+	.fmax = (unsigned long[VDD_DIG_NUM]) {	\
+		[VDD_DIG_##l1] = (f1),		\
+	},					\
+	.num_fmax = VDD_DIG_NUM
 #define VDD_DIG_FMAX_MAP2(l1, f1, l2, f2) \
-	.vdd_class = &vdd_dig, \
-	.fmax[VDD_DIG_##l1] = (f1), \
-	.fmax[VDD_DIG_##l2] = (f2)
+	.vdd_class = &vdd_dig,			\
+	.fmax = (unsigned long[VDD_DIG_NUM]) {	\
+		[VDD_DIG_##l1] = (f1),		\
+		[VDD_DIG_##l2] = (f2),		\
+	},					\
+	.num_fmax = VDD_DIG_NUM
 #define VDD_DIG_FMAX_MAP3(l1, f1, l2, f2, l3, f3) \
-	.vdd_class = &vdd_dig, \
-	.fmax[VDD_DIG_##l1] = (f1), \
-	.fmax[VDD_DIG_##l2] = (f2), \
-	.fmax[VDD_DIG_##l3] = (f3)
+	.vdd_class = &vdd_dig,			\
+	.fmax = (unsigned long[VDD_DIG_NUM]) {	\
+		[VDD_DIG_##l1] = (f1),		\
+		[VDD_DIG_##l2] = (f2),		\
+		[VDD_DIG_##l3] = (f3),		\
+	},					\
+	.num_fmax = VDD_DIG_NUM
 
 enum vdd_sr2_hdmi_pll_levels {
 	VDD_SR2_HDMI_PLL_OFF,
-	VDD_SR2_HDMI_PLL_ON
+	VDD_SR2_HDMI_PLL_ON,
+	VDD_SR2_HDMI_PLL_NUM
 };
 
 static int set_vdd_sr2_hdmi_pll_8960(struct clk_vdd_class *vdd_class, int level)
@@ -455,7 +466,8 @@
 	return rc;
 }
 
-static DEFINE_VDD_CLASS(vdd_sr2_hdmi_pll, set_vdd_sr2_hdmi_pll_8960);
+static DEFINE_VDD_CLASS(vdd_sr2_hdmi_pll, set_vdd_sr2_hdmi_pll_8960,
+			VDD_SR2_HDMI_PLL_NUM);
 
 static int sr2_lreg_uv[] = {
 	[VDD_SR2_HDMI_PLL_OFF] = 0,
@@ -530,7 +542,10 @@
 		.rate = 1200000000,
 		.ops = &clk_ops_local_pll,
 		.vdd_class = &vdd_sr2_hdmi_pll,
-		.fmax[VDD_SR2_HDMI_PLL_ON] = ULONG_MAX,
+		.fmax = (unsigned long[VDD_SR2_HDMI_PLL_NUM]) {
+			[VDD_SR2_HDMI_PLL_ON] = ULONG_MAX
+		},
+		.num_fmax = VDD_SR2_HDMI_PLL_NUM,
 		CLK_INIT(pll3_clk.c),
 	},
 };
@@ -1208,8 +1223,6 @@
 	.b = {
 		.ctl_reg = AHB_EN3_REG,
 		.en_mask = BIT(1),
-		.hwcg_reg = AHB_EN3_REG,
-		.hwcg_mask = BIT(0),
 		.reset_reg = SW_RESET_AHB2_REG,
 		.reset_mask = BIT(2),
 		.halt_reg = DBG_BUS_VEC_J_REG,
@@ -1534,7 +1547,7 @@
 static CLK_SDC(sdc4_clk, 4, 3,  33000000,  67000000);
 static CLK_SDC(sdc5_clk, 5, 2,  33000000,  67000000);
 
-static unsigned long fmax_sdc1_8064v2[MAX_VDD_LEVELS] __initdata = {
+static unsigned long fmax_sdc1_8064v2[VDD_DIG_NUM] = {
 	[VDD_DIG_LOW]     = 100000000,
 	[VDD_DIG_NOMINAL] = 200000000,
 };
@@ -1935,7 +1948,7 @@
 	},
 };
 
-static unsigned long fmax_ce3_8064v2[MAX_VDD_LEVELS] __initdata = {
+static unsigned long fmax_ce3_8064v2[VDD_DIG_NUM] = {
 	[VDD_DIG_LOW]     =  57000000,
 	[VDD_DIG_NOMINAL] = 120000000,
 };
@@ -3582,25 +3595,25 @@
 	F_END
 };
 
-static unsigned long fmax_gfx3d_8064ab[MAX_VDD_LEVELS] __initdata = {
+static unsigned long fmax_gfx3d_8064ab[VDD_DIG_NUM] = {
 	[VDD_DIG_LOW]     = 128000000,
 	[VDD_DIG_NOMINAL] = 325000000,
 	[VDD_DIG_HIGH]    = 450000000
 };
 
-static unsigned long fmax_gfx3d_8064[MAX_VDD_LEVELS] __initdata = {
+static unsigned long fmax_gfx3d_8064[VDD_DIG_NUM] = {
 	[VDD_DIG_LOW]     = 128000000,
 	[VDD_DIG_NOMINAL] = 325000000,
 	[VDD_DIG_HIGH]    = 400000000
 };
 
-static unsigned long fmax_gfx3d_8930[MAX_VDD_LEVELS] __initdata = {
+static unsigned long fmax_gfx3d_8930[VDD_DIG_NUM] = {
 	[VDD_DIG_LOW]     = 192000000,
 	[VDD_DIG_NOMINAL] = 320000000,
 	[VDD_DIG_HIGH]    = 400000000
 };
 
-static unsigned long fmax_gfx3d_8930aa[MAX_VDD_LEVELS] __initdata = {
+static unsigned long fmax_gfx3d_8930aa[VDD_DIG_NUM] = {
 	[VDD_DIG_LOW]     = 192000000,
 	[VDD_DIG_NOMINAL] = 320000000,
 	[VDD_DIG_HIGH]    = 450000000
@@ -3752,7 +3765,7 @@
 	F_END
 };
 
-static unsigned long fmax_ijpeg_8064[MAX_VDD_LEVELS] __initdata = {
+static unsigned long fmax_ijpeg_8064[VDD_DIG_NUM] = {
 	[VDD_DIG_LOW]     = 128000000,
 	[VDD_DIG_NOMINAL] = 266667000,
 	[VDD_DIG_HIGH]    = 320000000
@@ -3879,7 +3892,7 @@
 	F_END
 };
 
-static unsigned long fmax_mdp_8064[MAX_VDD_LEVELS] __initdata = {
+static unsigned long fmax_mdp_8064[VDD_DIG_NUM] = {
 	[VDD_DIG_LOW]     = 128000000,
 	[VDD_DIG_NOMINAL] = 266667000
 };
@@ -4073,7 +4086,10 @@
 	.dbg_name = "hdmi_pll_clk",
 	.ops = &clk_ops_hdmi_pll,
 	.vdd_class = &vdd_sr2_hdmi_pll,
-	.fmax[VDD_SR2_HDMI_PLL_ON] = ULONG_MAX,
+	.fmax = (unsigned long [VDD_SR2_HDMI_PLL_NUM]) {
+		[VDD_SR2_HDMI_PLL_ON] = ULONG_MAX,
+	},
+	.num_fmax = VDD_SR2_HDMI_PLL_NUM,
 	CLK_INIT(hdmi_pll_clk),
 };
 
@@ -4105,7 +4121,7 @@
 	F_END
 };
 
-static unsigned long fmax_tv_src_8064[MAX_VDD_LEVELS] __initdata = {
+static unsigned long fmax_tv_src_8064[VDD_DIG_NUM] = {
 	[VDD_DIG_LOW]     =  74250000,
 	[VDD_DIG_NOMINAL] = 149000000
 };
@@ -4344,7 +4360,7 @@
 	},
 };
 
-static unsigned long fmax_vcodec_8064v2[MAX_VDD_LEVELS] __initdata = {
+static unsigned long fmax_vcodec_8064v2[VDD_DIG_NUM] = {
 	[VDD_DIG_LOW]     = 100000000,
 	[VDD_DIG_NOMINAL] = 200000000,
 	[VDD_DIG_HIGH]    = 266670000,
@@ -4426,7 +4442,7 @@
 	F_END
 };
 
-static unsigned long fmax_vfe_8064[MAX_VDD_LEVELS] __initdata = {
+static unsigned long fmax_vfe_8064[VDD_DIG_NUM] = {
 	[VDD_DIG_LOW]     = 128000000,
 	[VDD_DIG_NOMINAL] = 266667000,
 	[VDD_DIG_HIGH]    = 320000000
@@ -4502,6 +4518,7 @@
 	F_AIF_OSR( 8192000, pll4, 4, 1,  15),
 	F_AIF_OSR(12288000, pll4, 4, 1,  10),
 	F_AIF_OSR(24576000, pll4, 4, 1,   5),
+	F_AIF_OSR(27000000, pxo,  1, 0,   0),
 	F_END
 };
 
@@ -4518,6 +4535,7 @@
 	F_AIF_OSR( 8192000, pll4, 4, 1,  12),
 	F_AIF_OSR(12288000, pll4, 4, 1,   8),
 	F_AIF_OSR(24576000, pll4, 4, 1,   4),
+	F_AIF_OSR(27000000, pxo,  1, 0,   0),
 	F_END
 };
 
@@ -4543,7 +4561,7 @@
 		.c = { \
 			.dbg_name = #i "_clk", \
 			.ops = &clk_ops_rcg, \
-			VDD_DIG_FMAX_MAP1(LOW, 24576000), \
+			VDD_DIG_FMAX_MAP1(LOW, 27000000), \
 			CLK_INIT(i##_clk.c), \
 		}, \
 	}
@@ -4569,7 +4587,7 @@
 		.c = { \
 			.dbg_name = #i "_clk", \
 			.ops = &clk_ops_rcg, \
-			VDD_DIG_FMAX_MAP1(LOW, 24576000), \
+			VDD_DIG_FMAX_MAP1(LOW, 27000000), \
 			CLK_INIT(i##_clk.c), \
 		}, \
 	}
@@ -4659,6 +4677,7 @@
 	F_PCM( 8192000, pll4, 4, 1,  15),
 	F_PCM(12288000, pll4, 4, 1,  10),
 	F_PCM(24576000, pll4, 4, 1,   5),
+	F_PCM(27000000, pxo,  1, 0,   0),
 	F_END
 };
 
@@ -4676,6 +4695,7 @@
 	F_PCM( 8192000, pll4, 4, 1,  12),
 	F_PCM(12288000, pll4, 4, 1,   8),
 	F_PCM(24576000, pll4, 4, 1,   4),
+	F_PCM(27000000, pxo,  1, 0,   0),
 	F_END
 };
 
@@ -4700,7 +4720,7 @@
 	.c = {
 		.dbg_name = "pcm_clk",
 		.ops = &clk_ops_rcg,
-		VDD_DIG_FMAX_MAP1(LOW, 24576000),
+		VDD_DIG_FMAX_MAP1(LOW, 27000000),
 		CLK_INIT(pcm_clk.c),
 		.rate = ULONG_MAX,
 	},
@@ -4727,7 +4747,7 @@
 	.c = {
 		.dbg_name = "audio_slimbus_clk",
 		.ops = &clk_ops_rcg,
-		VDD_DIG_FMAX_MAP1(LOW, 24576000),
+		VDD_DIG_FMAX_MAP1(LOW, 27000000),
 		CLK_INIT(audio_slimbus_clk.c),
 	},
 };
@@ -5216,9 +5236,7 @@
 	CLK_LOOKUP("pwm_clk",		cxo_clk.c,	"0-0048"),
 	CLK_LOOKUP("cxo",		cxo_clk.c,	"wcnss_wlan.0"),
 	CLK_LOOKUP("cxo",		cxo_clk.c,	"pil_riva"),
-	CLK_LOOKUP("xo",		pxo_clk.c,	"pil_qdsp6v4.0"),
-	CLK_LOOKUP("xo",		cxo_clk.c,	"pil_qdsp6v4.1"),
-	CLK_LOOKUP("xo",		cxo_clk.c,	"pil_qdsp6v4.2"),
+	CLK_LOOKUP("xo",		pxo_clk.c,	"pil-q6v4-lpass"),
 	CLK_LOOKUP("xo",		cxo_clk.c,	"pil_gss"),
 	CLK_LOOKUP("xo",		cxo_clk.c,	"BAM_RMNT"),
 	CLK_LOOKUP("xo",		cxo_clk.c,	"msm_xo"),
@@ -5289,7 +5307,7 @@
 	CLK_LOOKUP("core_clk",		gsbi4_qup_clk.c,	"qup_i2c.4"),
 	CLK_LOOKUP("core_clk",		gsbi5_qup_clk.c,	"spi_qsd.0"),
 	CLK_LOOKUP("core_clk",		gsbi5_qup_clk.c,	"qup_i2c.5"),
-	CLK_LOOKUP("core_clk",		gsbi6_qup_clk.c,	""),
+	CLK_LOOKUP("core_clk",		gsbi6_qup_clk.c,	"spi_qsd.1"),
 	CLK_LOOKUP("core_clk",		gsbi7_qup_clk.c,	""),
 	CLK_LOOKUP("core_clk",		pdm_clk.c,		""),
 	CLK_LOOKUP("mem_clk",		pmem_clk.c,		"msm_sps"),
@@ -5332,6 +5350,7 @@
 	CLK_LOOKUP("iface_clk",		gsbi5_p_clk.c,		"spi_qsd.0"),
 	CLK_LOOKUP("iface_clk",		gsbi5_p_clk.c,		"qup_i2c.5"),
 	CLK_LOOKUP("iface_clk",		gsbi6_p_clk.c,	"msm_serial_hs.0"),
+	CLK_LOOKUP("iface_clk",		gsbi6_p_clk.c,		"spi_qsd.1"),
 	CLK_LOOKUP("iface_clk",		gsbi7_p_clk.c,	"msm_serial_hsl.0"),
 	CLK_LOOKUP("ref_clk",	tsif_ref_clk.c,	"msm_tspp.0"),
 	CLK_LOOKUP("iface_clk",		tsif_p_clk.c,		"msm_tspp.0"),
@@ -5353,6 +5372,7 @@
 	CLK_LOOKUP("core_clk",		pmic_ssbi2_clk.c,	""),
 	CLK_LOOKUP("mem_clk",		rpm_msg_ram_p_clk.c,	""),
 	CLK_LOOKUP("cam_clk",		cam0_clk.c,	"4-001a"),
+	CLK_LOOKUP("cam_clk",		cam0_clk.c,	"4-0010"),
 	CLK_LOOKUP("cam_clk",		cam0_clk.c,	"4-0034"),
 	CLK_LOOKUP("cam_clk",		cam0_clk.c,	"4-0020"),
 	CLK_LOOKUP("cam_clk",		cam1_clk.c,	"4-0048"),
@@ -5562,9 +5582,8 @@
 	CLK_LOOKUP("xo",		pxo_a_clk.c,	""),
 	CLK_LOOKUP("cxo",		cxo_clk.c,	"wcnss_wlan.0"),
 	CLK_LOOKUP("cxo",		cxo_clk.c,	"pil_riva"),
-	CLK_LOOKUP("xo",		pxo_clk.c,	"pil_qdsp6v4.0"),
-	CLK_LOOKUP("xo",		cxo_clk.c,	"pil_qdsp6v4.1"),
-	CLK_LOOKUP("xo",		cxo_clk.c,	"pil_qdsp6v4.2"),
+	CLK_LOOKUP("xo",		pxo_clk.c,	"pil-q6v4-lpass"),
+	CLK_LOOKUP("xo",		cxo_clk.c,	"pil-q6v4-modem"),
 	CLK_LOOKUP("xo",		cxo_clk.c,	"BAM_RMNT"),
 	CLK_LOOKUP("xo",		cxo_clk.c,	"msm_xo"),
 	CLK_LOOKUP("vref_buff",		cxo_clk.c,	"rpm-regulator"),
@@ -5714,6 +5733,7 @@
 	CLK_LOOKUP("cam_clk",		cam2_clk.c,		NULL),
 	CLK_LOOKUP("cam_clk",		cam0_clk.c,	"4-0020"),
 	CLK_LOOKUP("cam_clk",		cam0_clk.c,	"4-0034"),
+	CLK_LOOKUP("cam_clk",		cam0_clk.c,	"4-0010"),
 	CLK_LOOKUP("csi_src_clk",	csi0_src_clk.c,		"msm_csid.0"),
 	CLK_LOOKUP("csi_src_clk",	csi1_src_clk.c,		"msm_csid.1"),
 	CLK_LOOKUP("csi_src_clk",	csi2_src_clk.c,		"msm_csid.2"),
@@ -5915,9 +5935,8 @@
 	CLK_LOOKUP("xo",		cxo_clk.c,	"msm_xo"),
 	CLK_LOOKUP("cxo",		cxo_clk.c,	"wcnss_wlan.0"),
 	CLK_LOOKUP("cxo",		cxo_clk.c,	"pil_riva"),
-	CLK_LOOKUP("xo",		pxo_clk.c,	"pil_qdsp6v4.0"),
-	CLK_LOOKUP("xo",		cxo_clk.c,	"pil_qdsp6v4.1"),
-	CLK_LOOKUP("xo",		cxo_clk.c,	"pil_qdsp6v4.2"),
+	CLK_LOOKUP("xo",		pxo_clk.c,	"pil-q6v4-lpass"),
+	CLK_LOOKUP("xo",		cxo_clk.c,	"pil-q6v4-modem"),
 	CLK_LOOKUP("xo",		cxo_clk.c,	"BAM_RMNT"),
 	CLK_LOOKUP("vref_buff",		cxo_clk.c,	"rpm-regulator"),
 	CLK_LOOKUP("pll2",		pll2_clk.c,	NULL),
@@ -6330,7 +6349,7 @@
 	}
 
 	if (cpu_is_apq8064() || cpu_is_apq8064ab())
-		rmwreg(0x00000001, AHB_EN3_REG, 0x00000001);
+		rmwreg(0x00000000, AHB_EN3_REG, 0x00000001);
 
 	/* Deassert all locally-owned MM AHB resets. */
 	rmwreg(0, SW_RESET_AHB_REG, 0xFFF7DFFF);
@@ -6565,37 +6584,25 @@
 	 */
 	if (cpu_is_apq8064()) {
 		gfx3d_clk.freq_tbl = clk_tbl_gfx3d_8064;
-
-		memcpy(gfx3d_clk.c.fmax, fmax_gfx3d_8064,
-		       sizeof(gfx3d_clk.c.fmax));
+		gfx3d_clk.c.fmax = fmax_gfx3d_8064;
 	}
 	if (cpu_is_apq8064ab()) {
 		gfx3d_clk.freq_tbl = clk_tbl_gfx3d_8064;
-
-		memcpy(gfx3d_clk.c.fmax, fmax_gfx3d_8064ab,
-		       sizeof(gfx3d_clk.c.fmax));
+		gfx3d_clk.c.fmax = fmax_gfx3d_8064ab;
 	}
 	if ((cpu_is_apq8064() &&
 		SOCINFO_VERSION_MAJOR(socinfo_get_version()) == 2) ||
 		cpu_is_apq8064ab()) {
 
-		memcpy(vcodec_clk.c.fmax, fmax_vcodec_8064v2,
-			sizeof(vcodec_clk.c.fmax));
-		memcpy(ce3_src_clk.c.fmax, fmax_ce3_8064v2,
-			sizeof(ce3_src_clk.c.fmax));
-		memcpy(sdc1_clk.c.fmax, fmax_sdc1_8064v2,
-			sizeof(sdc1_clk.c.fmax));
+		vcodec_clk.c.fmax = fmax_vcodec_8064v2;
+		ce3_src_clk.c.fmax = fmax_ce3_8064v2;
+		sdc1_clk.c.fmax = fmax_sdc1_8064v2;
 	}
 	if (cpu_is_apq8064() || cpu_is_apq8064ab()) {
-		memcpy(ijpeg_clk.c.fmax, fmax_ijpeg_8064,
-		       sizeof(ijpeg_clk.c.fmax));
-		memcpy(mdp_clk.c.fmax, fmax_mdp_8064,
-		       sizeof(ijpeg_clk.c.fmax));
-		memcpy(tv_src_clk.c.fmax, fmax_tv_src_8064,
-		       sizeof(tv_src_clk.c.fmax));
-		memcpy(vfe_clk.c.fmax, fmax_vfe_8064,
-		       sizeof(vfe_clk.c.fmax));
-
+		ijpeg_clk.c.fmax = fmax_ijpeg_8064;
+		mdp_clk.c.fmax = fmax_mdp_8064;
+		tv_src_clk.c.fmax = fmax_tv_src_8064;
+		vfe_clk.c.fmax = fmax_vfe_8064;
 		gmem_axi_clk.c.depends = &gfx3d_axi_clk.c;
 	}
 
@@ -6604,11 +6611,9 @@
 	 * clocks which differ between 8960 and 8930.
 	 */
 	if (cpu_is_msm8930() || cpu_is_msm8627()) {
-		memcpy(gfx3d_clk.c.fmax, fmax_gfx3d_8930,
-		       sizeof(gfx3d_clk.c.fmax));
+		gfx3d_clk.c.fmax = fmax_gfx3d_8930;
 	} else if (cpu_is_msm8930aa()) {
-		memcpy(gfx3d_clk.c.fmax, fmax_gfx3d_8930aa,
-		       sizeof(gfx3d_clk.c.fmax));
+		gfx3d_clk.c.fmax = fmax_gfx3d_8930aa;
 	}
 	if (cpu_is_msm8930() || cpu_is_msm8930aa() || cpu_is_msm8627()) {
 		gfx3d_clk.freq_tbl = clk_tbl_gfx3d_8930;
@@ -6708,22 +6713,22 @@
 	struct clk *mmfpb_a_clk = clk_get_sys("clock-8960", "mmfpb_a_clk");
 	struct clk *cfpb_a_clk = clk_get_sys("clock-8960", "cfpb_a_clk");
 
-	/* Vote for MMFPB to be at least 76.8MHz when an Apps CPU is active. */
+	/* Vote for MMFPB to be on when Apps is active. */
 	if (WARN(IS_ERR(mmfpb_a_clk), "mmfpb_a_clk not found (%ld)\n",
 			PTR_ERR(mmfpb_a_clk)))
 		return PTR_ERR(mmfpb_a_clk);
-	rc = clk_set_rate(mmfpb_a_clk, 76800000);
+	rc = clk_set_rate(mmfpb_a_clk, 38400000);
 	if (WARN(rc, "mmfpb_a_clk rate was not set (%d)\n", rc))
 		return rc;
 	rc = clk_prepare_enable(mmfpb_a_clk);
 	if (WARN(rc, "mmfpb_a_clk not enabled (%d)\n", rc))
 		return rc;
 
-	/* Vote for CFPB to be at least 64MHz when an Apps CPU is active. */
+	/* Vote for CFPB to be on when Apps is active. */
 	if (WARN(IS_ERR(cfpb_a_clk), "cfpb_a_clk not found (%ld)\n",
 			PTR_ERR(cfpb_a_clk)))
 		return PTR_ERR(cfpb_a_clk);
-	rc = clk_set_rate(cfpb_a_clk, 64000000);
+	rc = clk_set_rate(cfpb_a_clk, 32000000);
 	if (WARN(rc, "cfpb_a_clk rate was not set (%d)\n", rc))
 		return rc;
 	rc = clk_prepare_enable(cfpb_a_clk);
diff --git a/arch/arm/mach-msm/clock-8974.c b/arch/arm/mach-msm/clock-8974.c
index 10de231..777e0bf 100644
--- a/arch/arm/mach-msm/clock-8974.c
+++ b/arch/arm/mach-msm/clock-8974.c
@@ -588,23 +588,33 @@
 	}
 
 #define VDD_DIG_FMAX_MAP1(l1, f1) \
-	.vdd_class = &vdd_dig, \
-	.fmax[VDD_DIG_##l1] = (f1)
+	.vdd_class = &vdd_dig,			\
+	.fmax = (unsigned long[VDD_DIG_NUM]) {	\
+		[VDD_DIG_##l1] = (f1),		\
+	},					\
+	.num_fmax = VDD_DIG_NUM
 #define VDD_DIG_FMAX_MAP2(l1, f1, l2, f2) \
-	.vdd_class = &vdd_dig, \
-	.fmax[VDD_DIG_##l1] = (f1), \
-	.fmax[VDD_DIG_##l2] = (f2)
+	.vdd_class = &vdd_dig,			\
+	.fmax = (unsigned long[VDD_DIG_NUM]) {	\
+		[VDD_DIG_##l1] = (f1),		\
+		[VDD_DIG_##l2] = (f2),		\
+	},					\
+	.num_fmax = VDD_DIG_NUM
 #define VDD_DIG_FMAX_MAP3(l1, f1, l2, f2, l3, f3) \
-	.vdd_class = &vdd_dig, \
-	.fmax[VDD_DIG_##l1] = (f1), \
-	.fmax[VDD_DIG_##l2] = (f2), \
-	.fmax[VDD_DIG_##l3] = (f3)
+	.vdd_class = &vdd_dig,			\
+	.fmax = (unsigned long[VDD_DIG_NUM]) {	\
+		[VDD_DIG_##l1] = (f1),		\
+		[VDD_DIG_##l2] = (f2),		\
+		[VDD_DIG_##l3] = (f3),		\
+	},					\
+	.num_fmax = VDD_DIG_NUM
 
 enum vdd_dig_levels {
 	VDD_DIG_NONE,
 	VDD_DIG_LOW,
 	VDD_DIG_NOMINAL,
-	VDD_DIG_HIGH
+	VDD_DIG_HIGH,
+	VDD_DIG_NUM
 };
 
 static const int vdd_corner[] = {
@@ -622,7 +632,7 @@
 					RPM_REGULATOR_CORNER_SUPER_TURBO);
 }
 
-static DEFINE_VDD_CLASS(vdd_dig, set_vdd_dig);
+static DEFINE_VDD_CLASS(vdd_dig, set_vdd_dig, VDD_DIG_NUM);
 
 #define RPM_MISC_CLK_TYPE	0x306b6c63
 #define RPM_BUS_CLK_TYPE	0x316b6c63
@@ -632,7 +642,6 @@
 
 #define CXO_ID			0x0
 #define QDSS_ID			0x1
-#define RPM_SCALING_ENABLE_ID	0x2
 
 #define PNOC_ID		0x0
 #define SNOC_ID		0x1
@@ -789,7 +798,6 @@
 static DEFINE_CLK_VOTER(pnoc_sdcc4_clk, &pnoc_clk.c, 0);
 
 static DEFINE_CLK_VOTER(pnoc_sps_clk, &pnoc_clk.c, 0);
-static DEFINE_CLK_VOTER(pnoc_qseecom_clk, &pnoc_clk.c, 0);
 
 static struct clk_freq_tbl ftbl_gcc_usb30_master_clk[] = {
 	F(125000000,  gpll0,   1,   5,  24),
@@ -2821,8 +2829,8 @@
 	.div_src_val = BVAL(10, 8, dsipll0_byte_mm_source_val),
 };
 static struct clk_freq_tbl pixel_freq = {
-	.src_clk = &dsipll0_byte_clk_src,
-	.div_src_val = BVAL(10, 8, dsipll0_byte_mm_source_val),
+	.src_clk = &dsipll0_pixel_clk_src,
+	.div_src_val = BVAL(10, 8, dsipll0_pixel_mm_source_val),
 };
 static struct clk_ops clk_ops_byte;
 static struct clk_ops clk_ops_pixel;
@@ -2933,7 +2941,7 @@
 };
 
 static struct clk_freq_tbl ftbl_mdss_edplink_clk[] = {
-	F_MDSS(135000000, edppll_270,   2,   0,   0),
+	F_MDSS(162000000, edppll_270,   2,   0,   0),
 	F_MDSS(270000000, edppll_270,  11,   0,   0),
 	F_END
 };
@@ -2953,7 +2961,7 @@
 };
 
 static struct clk_freq_tbl ftbl_mdss_edppixel_clk[] = {
-	F_MDSS(175000000, edppll_350,   2,   0,   0),
+	F_MDSS(148500000, edppll_350,   2,   0,   0),
 	F_MDSS(350000000, edppll_350,  11,   0,   0),
 	F_END
 };
@@ -4679,6 +4687,10 @@
 	{&gcc_ce1_clk.c,			GCC_BASE, 0x0138},
 	{&gcc_lpass_q6_axi_clk.c,		GCC_BASE, 0x0160},
 	{&gcc_mss_q6_bimc_axi_clk.c,		GCC_BASE, 0x0031},
+	{&cnoc_clk.c,                           GCC_BASE, 0x0008},
+	{&pnoc_clk.c,                           GCC_BASE, 0x0010},
+	{&snoc_clk.c,                           GCC_BASE, 0x0000},
+	{&bimc_clk.c,                           GCC_BASE, 0x0155},
 	{&mmss_mmssnoc_axi_clk.c,		MMSS_BASE, 0x0004},
 	{&ocmemnoc_clk.c,			MMSS_BASE, 0x0007},
 	{&ocmemcx_ocmemnoc_clk.c,		MMSS_BASE, 0x0009},
@@ -5010,7 +5022,6 @@
 	CLK_LOOKUP("xo",	cxo_clk_src.c,	"msm_otg"),
 	CLK_LOOKUP("xo",	cxo_clk_src.c,	"pil-q6v5-lpass"),
 	CLK_LOOKUP("xo",	cxo_clk_src.c,	"pil-q6v5-mss"),
-	CLK_LOOKUP("xo",	cxo_clk_src.c,	"pil-mba"),
 	CLK_LOOKUP("xo",	cxo_clk_src.c,	"fb000000.qcom,wcnss-wlan"),
 	CLK_LOOKUP("xo",	cxo_clk_src.c,	"pil_pronto"),
 	CLK_LOOKUP("measure",	measure_clk.c,	"debug"),
@@ -5079,6 +5090,11 @@
 	CLK_LOOKUP("bus_clk",      gcc_ce2_axi_clk.c, "qcrypto.0"),
 	CLK_LOOKUP("core_clk_src", ce2_clk_src.c,     "qcrypto.0"),
 
+	CLK_LOOKUP("core_clk",     gcc_ce1_clk.c,         "qseecom"),
+	CLK_LOOKUP("iface_clk",    gcc_ce1_ahb_clk.c,     "qseecom"),
+	CLK_LOOKUP("bus_clk",      gcc_ce1_axi_clk.c,     "qseecom"),
+	CLK_LOOKUP("core_clk_src", ce1_clk_src.c,         "qseecom"),
+
 	CLK_LOOKUP("core_clk", gcc_gp1_clk.c, ""),
 	CLK_LOOKUP("core_clk", gcc_gp2_clk.c, ""),
 	CLK_LOOKUP("core_clk", gcc_gp3_clk.c, ""),
@@ -5122,8 +5138,9 @@
 	/* Multimedia clocks */
 	CLK_LOOKUP("bus_clk_src", axi_clk_src.c, ""),
 	CLK_LOOKUP("bus_clk", mmss_mmssnoc_axi_clk.c, ""),
-	CLK_LOOKUP("core_clk", mdss_edpaux_clk.c, ""),
-	CLK_LOOKUP("core_clk", mdss_edppixel_clk.c, ""),
+	CLK_LOOKUP("core_clk", mdss_edpaux_clk.c, "fd923400.qcom,mdss_edp"),
+	CLK_LOOKUP("pixel_clk", mdss_edppixel_clk.c, "fd923400.qcom,mdss_edp"),
+	CLK_LOOKUP("link_clk", mdss_edplink_clk.c, "fd923400.qcom,mdss_edp"),
 	CLK_LOOKUP("byte_clk", mdss_byte0_clk.c, "fd922800.qcom,mdss_dsi"),
 	CLK_LOOKUP("byte_clk", mdss_byte1_clk.c, ""),
 	CLK_LOOKUP("core_clk", mdss_esc0_clk.c, "fd922800.qcom,mdss_dsi"),
@@ -5283,6 +5300,7 @@
 	CLK_LOOKUP("core_clk", camss_vfe_cpp_clk.c, "fda44000.qcom,iommu"),
 	CLK_LOOKUP("iface_clk", mdss_ahb_clk.c, "mdp.0"),
 	CLK_LOOKUP("iface_clk", mdss_ahb_clk.c, "mdss_dsi_clk_ctrl"),
+	CLK_LOOKUP("iface_clk", mdss_ahb_clk.c, "fd923400.qcom,mdss_edp"),
 	CLK_LOOKUP("iface_clk", mdss_ahb_clk.c, "fd928000.qcom,iommu"),
 	CLK_LOOKUP("core_clk", mdss_axi_clk.c, "fd928000.qcom,iommu"),
 	CLK_LOOKUP("bus_clk", mdss_axi_clk.c, "mdp.0"),
@@ -5361,7 +5379,6 @@
 	CLK_LOOKUP("core_clk", gcc_prng_ahb_clk.c, "msm_rng"),
 
 	CLK_LOOKUP("dfab_clk", pnoc_sps_clk.c, "msm_sps"),
-	CLK_LOOKUP("bus_clk",  pnoc_qseecom_clk.c, "qseecom"),
 
 	CLK_LOOKUP("bus_clk", snoc_clk.c, ""),
 	CLK_LOOKUP("bus_clk", pnoc_clk.c, ""),
@@ -5746,24 +5763,6 @@
 #define APCS_GCC_CC_PHYS	0xF9011000
 #define APCS_GCC_CC_SIZE	SZ_4K
 
-static void __init enable_rpm_scaling(void)
-{
-	int rc, value = 0x1;
-	struct msm_rpm_kvp kvp = {
-		.key = RPM_SMD_KEY_ENABLE,
-		.data = (void *)&value,
-		.length = sizeof(value),
-	};
-
-	rc = msm_rpm_send_message_noirq(MSM_RPM_CTX_SLEEP_SET,
-			RPM_MISC_CLK_TYPE, RPM_SCALING_ENABLE_ID, &kvp, 1);
-	WARN(rc < 0, "RPM clock scaling (sleep set) did not enable!\n");
-
-	rc = msm_rpm_send_message_noirq(MSM_RPM_CTX_ACTIVE_SET,
-			RPM_MISC_CLK_TYPE, RPM_SCALING_ENABLE_ID, &kvp, 1);
-	WARN(rc < 0, "RPM clock scaling (active set) did not enable!\n");
-}
-
 static void __init msm8974_clock_pre_init(void)
 {
 	virt_bases[GCC_BASE] = ioremap(GCC_CC_PHYS, GCC_CC_SIZE);
diff --git a/arch/arm/mach-msm/clock-8x60.c b/arch/arm/mach-msm/clock-8x60.c
index 6ae66fe..5a9799a 100644
--- a/arch/arm/mach-msm/clock-8x60.c
+++ b/arch/arm/mach-msm/clock-8x60.c
@@ -268,7 +268,8 @@
 	VDD_DIG_NONE,
 	VDD_DIG_LOW,
 	VDD_DIG_NOMINAL,
-	VDD_DIG_HIGH
+	VDD_DIG_HIGH,
+	VDD_DIG_NUM
 };
 
 static int set_vdd_dig(struct clk_vdd_class *vdd_class, int level)
@@ -284,20 +285,29 @@
 				    vdd_uv[level], 1200000, 1);
 }
 
-static DEFINE_VDD_CLASS(vdd_dig, set_vdd_dig);
+static DEFINE_VDD_CLASS(vdd_dig, set_vdd_dig, VDD_DIG_NUM);
 
 #define VDD_DIG_FMAX_MAP1(l1, f1) \
-	.vdd_class = &vdd_dig, \
-	.fmax[VDD_DIG_##l1] = (f1)
+	.vdd_class = &vdd_dig,			\
+	.fmax = (unsigned long[VDD_DIG_NUM]) {	\
+		[VDD_DIG_##l1] = (f1),		\
+	},					\
+	.num_fmax = VDD_DIG_NUM
 #define VDD_DIG_FMAX_MAP2(l1, f1, l2, f2) \
-	.vdd_class = &vdd_dig, \
-	.fmax[VDD_DIG_##l1] = (f1), \
-	.fmax[VDD_DIG_##l2] = (f2)
+	.vdd_class = &vdd_dig,			\
+	.fmax = (unsigned long[VDD_DIG_NUM]) {	\
+		[VDD_DIG_##l1] = (f1),		\
+		[VDD_DIG_##l2] = (f2),		\
+	},					\
+	.num_fmax = VDD_DIG_NUM
 #define VDD_DIG_FMAX_MAP3(l1, f1, l2, f2, l3, f3) \
-	.vdd_class = &vdd_dig, \
-	.fmax[VDD_DIG_##l1] = (f1), \
-	.fmax[VDD_DIG_##l2] = (f2), \
-	.fmax[VDD_DIG_##l3] = (f3)
+	.vdd_class = &vdd_dig,			\
+	.fmax = (unsigned long[VDD_DIG_NUM]) {	\
+		[VDD_DIG_##l1] = (f1),		\
+		[VDD_DIG_##l2] = (f2),		\
+		[VDD_DIG_##l3] = (f3),		\
+	},					\
+	.num_fmax = VDD_DIG_NUM
 
 DEFINE_CLK_RPM_BRANCH(pxo_clk, pxo_a_clk, PXO, 27000000);
 DEFINE_CLK_RPM_BRANCH(cxo_clk, cxo_a_clk, CXO, 19200000);
@@ -597,7 +607,7 @@
 	},
 	.c = {
 		.dbg_name = "smi_2x_axi_clk",
-		.ops = &clk_ops_branch,
+		.ops = &clk_ops_smi_2x,
 		CLK_INIT(smi_2x_axi_clk.c),
 	},
 };
@@ -2971,6 +2981,7 @@
 	F_AIF_OSR( 8192000, pll4, 2, 1,  33),
 	F_AIF_OSR(12288000, pll4, 4, 1,  11),
 	F_AIF_OSR(24576000, pll4, 2, 1,  11),
+	F_AIF_OSR(27000000, pxo,  1, 0,   0),
 	F_END
 };
 
@@ -2996,7 +3007,7 @@
 		.c = { \
 			.dbg_name = #i "_clk", \
 			.ops = &clk_ops_rcg, \
-			VDD_DIG_FMAX_MAP1(LOW, 24576000), \
+			VDD_DIG_FMAX_MAP1(LOW, 27000000), \
 			CLK_INIT(i##_clk.c), \
 		}, \
 	}
@@ -3065,6 +3076,7 @@
 	F_PCM( 8192000, pll4, 2, 1,  33),
 	F_PCM(12288000, pll4, 4, 1,  11),
 	F_PCM(24580000, pll4, 2, 1,  11),
+	F_PCM(27000000, pxo,  1, 0,   0),
 	F_END
 };
 
@@ -3089,7 +3101,7 @@
 	.c = {
 		.dbg_name = "pcm_clk",
 		.ops = &clk_ops_rcg,
-		VDD_DIG_FMAX_MAP1(LOW, 24580000),
+		VDD_DIG_FMAX_MAP1(LOW, 27000000),
 		CLK_INIT(pcm_clk.c),
 		.rate = ULONG_MAX,
 	},
diff --git a/arch/arm/mach-msm/clock-9615.c b/arch/arm/mach-msm/clock-9615.c
index fee8445..338361b 100644
--- a/arch/arm/mach-msm/clock-9615.c
+++ b/arch/arm/mach-msm/clock-9615.c
@@ -182,7 +182,8 @@
 	VDD_DIG_NONE,
 	VDD_DIG_LOW,
 	VDD_DIG_NOMINAL,
-	VDD_DIG_HIGH
+	VDD_DIG_HIGH,
+	VDD_DIG_NUM
 };
 
 static int set_vdd_dig(struct clk_vdd_class *vdd_class, int level)
@@ -198,15 +199,21 @@
 		RPM_VREG_VOTER3, vdd_corner[level], RPM_VREG_CORNER_HIGH, 1);
 }
 
-static DEFINE_VDD_CLASS(vdd_dig, set_vdd_dig);
+static DEFINE_VDD_CLASS(vdd_dig, set_vdd_dig, VDD_DIG_NUM);
 
 #define VDD_DIG_FMAX_MAP1(l1, f1) \
-	.vdd_class = &vdd_dig, \
-	.fmax[VDD_DIG_##l1] = (f1)
+	.vdd_class = &vdd_dig,			\
+	.fmax = (unsigned long[VDD_DIG_NUM]) {	\
+		[VDD_DIG_##l1] = (f1),		\
+	},					\
+	.num_fmax = VDD_DIG_NUM
 #define VDD_DIG_FMAX_MAP2(l1, f1, l2, f2) \
-	.vdd_class = &vdd_dig, \
-	.fmax[VDD_DIG_##l1] = (f1), \
-	.fmax[VDD_DIG_##l2] = (f2)
+	.vdd_class = &vdd_dig,			\
+	.fmax = (unsigned long[VDD_DIG_NUM]) {	\
+		[VDD_DIG_##l1] = (f1),		\
+		[VDD_DIG_##l2] = (f2),		\
+	},					\
+	.num_fmax = VDD_DIG_NUM
 
 /*
  * Clock Descriptions
@@ -214,49 +221,6 @@
 
 DEFINE_CLK_RPM_BRANCH(cxo_clk, cxo_a_clk, CXO, 19200000);
 
-static DEFINE_SPINLOCK(soft_vote_lock);
-
-static int pll_acpu_vote_clk_enable(struct clk *c)
-{
-	int ret = 0;
-	unsigned long flags;
-	struct pll_vote_clk *pllv = to_pll_vote_clk(c);
-
-	spin_lock_irqsave(&soft_vote_lock, flags);
-
-	if (!*pllv->soft_vote)
-		ret = pll_vote_clk_enable(c);
-	if (ret == 0)
-		*pllv->soft_vote |= (pllv->soft_vote_mask);
-
-	spin_unlock_irqrestore(&soft_vote_lock, flags);
-	return ret;
-}
-
-static void pll_acpu_vote_clk_disable(struct clk *c)
-{
-	unsigned long flags;
-	struct pll_vote_clk *pllv = to_pll_vote_clk(c);
-
-	spin_lock_irqsave(&soft_vote_lock, flags);
-
-	*pllv->soft_vote &= ~(pllv->soft_vote_mask);
-	if (!*pllv->soft_vote)
-		pll_vote_clk_disable(c);
-
-	spin_unlock_irqrestore(&soft_vote_lock, flags);
-}
-
-static struct clk_ops clk_ops_pll_acpu_vote = {
-	.enable = pll_acpu_vote_clk_enable,
-	.disable = pll_acpu_vote_clk_disable,
-	.is_enabled = pll_vote_clk_is_enabled,
-	.get_parent = pll_vote_clk_get_parent,
-};
-
-#define PLL_SOFT_VOTE_PRIMARY	BIT(0)
-#define PLL_SOFT_VOTE_ACPU	BIT(1)
-
 static unsigned int soft_vote_pll0;
 
 static struct pll_vote_clk pll0_clk = {
diff --git a/arch/arm/mach-msm/clock-9625.c b/arch/arm/mach-msm/clock-9625.c
new file mode 100644
index 0000000..b9362cf
--- /dev/null
+++ b/arch/arm/mach-msm/clock-9625.c
@@ -0,0 +1,2427 @@
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/ctype.h>
+#include <linux/io.h>
+#include <linux/spinlock.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/regulator/consumer.h>
+#include <linux/iopoll.h>
+
+#include <mach/clk.h>
+#include <mach/rpm-regulator-smd.h>
+#include <mach/socinfo.h>
+
+#include "clock-local2.h"
+#include "clock-pll.h"
+#include "clock-rpm.h"
+#include "clock-voter.h"
+#include "clock.h"
+
+enum {
+	GCC_BASE,
+	LPASS_BASE,
+	APCS_BASE,
+	APCS_PLL_BASE,
+	N_BASES,
+};
+
+static void __iomem *virt_bases[N_BASES];
+
+#define GCC_REG_BASE(x) (void __iomem *)(virt_bases[GCC_BASE] + (x))
+#define LPASS_REG_BASE(x) (void __iomem *)(virt_bases[LPASS_BASE] + (x))
+#define APCS_REG_BASE(x) (void __iomem *)(virt_bases[APCS_BASE] + (x))
+#define APCS_PLL_REG_BASE(x) (void __iomem *)(virt_bases[APCS_PLL_BASE] + (x))
+
+/* GCC registers */
+#define GPLL0_MODE_REG                 0x0000
+#define GPLL0_L_REG                    0x0004
+#define GPLL0_M_REG                    0x0008
+#define GPLL0_N_REG                    0x000C
+#define GPLL0_USER_CTL_REG             0x0010
+#define GPLL0_CONFIG_CTL_REG           0x0014
+#define GPLL0_TEST_CTL_REG             0x0018
+#define GPLL0_STATUS_REG               0x001C
+
+#define GPLL1_MODE_REG                 0x0040
+#define GPLL1_L_REG                    0x0044
+#define GPLL1_M_REG                    0x0048
+#define GPLL1_N_REG                    0x004C
+#define GPLL1_USER_CTL_REG             0x0050
+#define GPLL1_CONFIG_CTL_REG           0x0054
+#define GPLL1_TEST_CTL_REG             0x0058
+#define GPLL1_STATUS_REG               0x005C
+
+#define GCC_DEBUG_CLK_CTL_REG          0x1880
+#define CLOCK_FRQ_MEASURE_CTL_REG      0x1884
+#define CLOCK_FRQ_MEASURE_STATUS_REG   0x1888
+#define GCC_PLLTEST_PAD_CFG_REG        0x188C
+#define GCC_XO_DIV4_CBCR_REG           0x10C8
+#define APCS_GPLL_ENA_VOTE_REG         0x1480
+#define APCS_CLOCK_BRANCH_ENA_VOTE     0x1484
+#define APCS_CLOCK_SLEEP_ENA_VOTE      0x1488
+
+#define APCS_CLK_DIAG_REG              0x001C
+
+#define APCS_CPU_PLL_MODE_REG          0x0000
+#define APCS_CPU_PLL_L_REG             0x0004
+#define APCS_CPU_PLL_M_REG             0x0008
+#define APCS_CPU_PLL_N_REG             0x000C
+#define APCS_CPU_PLL_USER_CTL_REG      0x0010
+#define APCS_CPU_PLL_CONFIG_CTL_REG    0x0014
+#define APCS_CPU_PLL_TEST_CTL_REG      0x0018
+#define APCS_CPU_PLL_STATUS_REG        0x001C
+
+#define USB_HSIC_SYSTEM_CMD_RCGR       0x041C
+#define USB_HSIC_XCVR_FS_CMD_RCGR      0x0424
+#define USB_HSIC_CMD_RCGR              0x0440
+#define USB_HSIC_IO_CAL_CMD_RCGR       0x0458
+#define USB_HS_SYSTEM_CMD_RCGR         0x0490
+#define SDCC2_APPS_CMD_RCGR            0x0510
+#define SDCC3_APPS_CMD_RCGR            0x0550
+#define BLSP1_QUP1_SPI_APPS_CMD_RCGR   0x064C
+#define BLSP1_UART1_APPS_CMD_RCGR      0x068C
+#define BLSP1_QUP2_SPI_APPS_CMD_RCGR   0x06CC
+#define BLSP1_UART2_APPS_CMD_RCGR      0x070C
+#define BLSP1_QUP3_SPI_APPS_CMD_RCGR   0x074C
+#define BLSP1_UART3_APPS_CMD_RCGR      0x078C
+#define BLSP1_QUP4_SPI_APPS_CMD_RCGR   0x07CC
+#define BLSP1_UART4_APPS_CMD_RCGR      0x080C
+#define BLSP1_QUP5_SPI_APPS_CMD_RCGR   0x084C
+#define BLSP1_UART5_APPS_CMD_RCGR      0x088C
+#define BLSP1_QUP6_SPI_APPS_CMD_RCGR   0x08CC
+#define BLSP1_UART6_APPS_CMD_RCGR      0x090C
+#define PDM2_CMD_RCGR                  0x0CD0
+#define CE1_CMD_RCGR                   0x1050
+#define GP1_CMD_RCGR                   0x1904
+#define GP2_CMD_RCGR                   0x1944
+#define GP3_CMD_RCGR                   0x1984
+#define QPIC_CMD_RCGR                  0x1A50
+#define IPA_CMD_RCGR                   0x1A90
+
+#define USB_HS_HSIC_BCR           0x0400
+#define USB_HS_BCR                0x0480
+#define SDCC2_BCR                 0x0500
+#define SDCC3_BCR                 0x0540
+#define BLSP1_BCR                 0x05C0
+#define BLSP1_QUP1_BCR            0x0640
+#define BLSP1_UART1_BCR           0x0680
+#define BLSP1_QUP2_BCR            0x06C0
+#define BLSP1_UART2_BCR           0x0700
+#define BLSP1_QUP3_BCR            0x0740
+#define BLSP1_UART3_BCR           0x0780
+#define BLSP1_QUP4_BCR            0x07C0
+#define BLSP1_UART4_BCR           0x0800
+#define BLSP1_QUP5_BCR            0x0840
+#define BLSP1_UART5_BCR           0x0880
+#define BLSP1_QUP6_BCR            0x08C0
+#define BLSP1_UART6_BCR           0x0900
+#define PDM_BCR                   0x0CC0
+#define PRNG_BCR                  0x0D00
+#define BAM_DMA_BCR               0x0D40
+#define BOOT_ROM_BCR              0x0E00
+#define CE1_BCR                   0x1040
+#define QPIC_BCR                  0x1040
+#define IPA_BCR                   0x1A80
+
+
+#define SYS_NOC_IPA_AXI_CBCR                     0x0128
+#define USB_HSIC_AHB_CBCR                        0x0408
+#define USB_HSIC_SYSTEM_CBCR                     0x040C
+#define USB_HSIC_CBCR                            0x0410
+#define USB_HSIC_IO_CAL_CBCR                     0x0414
+#define USB_HSIC_XCVR_FS_CBCR                    0x042C
+#define USB_HS_SYSTEM_CBCR                       0x0484
+#define USB_HS_AHB_CBCR                          0x0488
+#define SDCC2_APPS_CBCR                          0x0504
+#define SDCC2_AHB_CBCR                           0x0508
+#define SDCC3_APPS_CBCR                          0x0544
+#define SDCC3_AHB_CBCR                           0x0548
+#define BLSP1_AHB_CBCR                           0x05C4
+#define BLSP1_QUP1_SPI_APPS_CBCR                 0x0644
+#define BLSP1_QUP1_I2C_APPS_CBCR                 0x0648
+#define BLSP1_UART1_APPS_CBCR                    0x0684
+#define BLSP1_UART1_SIM_CBCR                     0x0688
+#define BLSP1_QUP2_SPI_APPS_CBCR                 0x06C4
+#define BLSP1_QUP2_I2C_APPS_CBCR                 0x06C8
+#define BLSP1_UART2_APPS_CBCR                    0x0704
+#define BLSP1_UART2_SIM_CBCR                     0x0708
+#define BLSP1_QUP3_SPI_APPS_CBCR                 0x0744
+#define BLSP1_QUP3_I2C_APPS_CBCR                 0x0748
+#define BLSP1_UART3_APPS_CBCR                    0x0784
+#define BLSP1_UART3_SIM_CBCR                     0x0788
+#define BLSP1_QUP4_SPI_APPS_CBCR                 0x07C4
+#define BLSP1_QUP4_I2C_APPS_CBCR                 0x07C8
+#define BLSP1_UART4_APPS_CBCR                    0x0804
+#define BLSP1_UART4_SIM_CBCR                     0x0808
+#define BLSP1_QUP5_SPI_APPS_CBCR                 0x0844
+#define BLSP1_QUP5_I2C_APPS_CBCR                 0x0848
+#define BLSP1_UART5_APPS_CBCR                    0x0884
+#define BLSP1_UART5_SIM_CBCR                     0x0888
+#define BLSP1_QUP6_SPI_APPS_CBCR                 0x08C4
+#define BLSP1_QUP6_I2C_APPS_CBCR                 0x08C8
+#define BLSP1_UART6_APPS_CBCR                    0x0904
+#define BLSP1_UART6_SIM_CBCR                     0x0908
+#define BOOT_ROM_AHB_CBCR                        0x0E04
+#define PDM_AHB_CBCR                             0x0CC4
+#define PDM_XO4_CBCR                             0x0CC8
+#define PDM_AHB_CBCR                             0x0CC4
+#define PDM_XO4_CBCR                             0x0CC8
+#define PDM2_CBCR                                0x0CCC
+#define PRNG_AHB_CBCR                            0x0D04
+#define BAM_DMA_AHB_CBCR                         0x0D44
+#define MSG_RAM_AHB_CBCR                         0x0E44
+#define CE1_CBCR                                 0x1044
+#define CE1_AXI_CBCR                             0x1048
+#define CE1_AHB_CBCR                             0x104C
+#define GCC_AHB_CBCR                             0x10C0
+#define GP1_CBCR                                 0x1900
+#define GP2_CBCR                                 0x1940
+#define GP3_CBCR                                 0x1980
+#define QPIC_CBCR				 0x1A44
+#define QPIC_AHB_CBCR                            0x1A48
+#define IPA_CBCR                                 0x1A84
+#define IPA_CNOC_CBCR                            0x1A88
+#define IPA_SLEEP_CBCR                           0x1A8C
+
+/* LPASS registers */
+/* TODO: Needs to double check lpass regiserts after get the SWI for hw */
+#define LPAPLL_MODE_REG				0x0000
+#define LPAPLL_L_REG				0x0004
+#define LPAPLL_M_REG				0x0008
+#define LPAPLL_N_REG				0x000C
+#define LPAPLL_USER_CTL_REG			0x0010
+#define LPAPLL_CONFIG_CTL_REG			0x0014
+#define LPAPLL_TEST_CTL_REG			0x0018
+#define LPAPLL_STATUS_REG			0x001C
+
+#define LPASS_DEBUG_CLK_CTL_REG			0x29000
+#define LPASS_LPA_PLL_VOTE_APPS_REG		0x2000
+
+#define LPAIF_PRI_CMD_RCGR			0xB000
+#define LPAIF_SEC_CMD_RCGR			0xC000
+#define LPAIF_PCM0_CMD_RCGR			0xF000
+#define LPAIF_PCM1_CMD_RCGR			0x10000
+#define SLIMBUS_CMD_RCGR			0x12000
+#define LPAIF_PCMOE_CMD_RCGR			0x13000
+
+#define AUDIO_CORE_BCR				0x4000
+
+#define AUDIO_CORE_GDSCR			0x7000
+#define AUDIO_CORE_LPAIF_PRI_OSR_CBCR		0xB014
+#define AUDIO_CORE_LPAIF_PRI_IBIT_CBCR		0xB018
+#define AUDIO_CORE_LPAIF_PRI_EBIT_CBCR		0xB01C
+#define AUDIO_CORE_LPAIF_SEC_OSR_CBCR		0xC014
+#define AUDIO_CORE_LPAIF_SEC_IBIT_CBCR		0xC018
+#define AUDIO_CORE_LPAIF_SEC_EBIT_CBCR		0xC01C
+#define AUDIO_CORE_LPAIF_PCM0_IBIT_CBCR		0xF014
+#define AUDIO_CORE_LPAIF_PCM0_EBIT_CBCR		0xF018
+#define AUDIO_CORE_LPAIF_PCM1_IBIT_CBCR		0x10014
+#define AUDIO_CORE_LPAIF_PCM1_EBIT_CBCR		0x10018
+#define AUDIO_CORE_RESAMPLER_CORE_CBCR		0x11014
+#define AUDIO_CORE_RESAMPLER_LFABIF_CBCR	0x11018
+#define AUDIO_CORE_SLIMBUS_CORE_CBCR		0x12014
+#define AUDIO_CORE_SLIMBUS_LFABIF_CBCR		0x12018
+#define AUDIO_CORE_LPAIF_PCM_DATA_OE_CBCR	0x13014
+
+/* Mux source select values */
+#define cxo_source_val	0
+#define gpll0_source_val 1
+#define gpll1_hsic_source_val 4
+#define gnd_source_val	5
+#define cxo_lpass_source_val 0
+#define lpapll0_lpass_source_val 1
+#define gpll0_lpass_source_val 5
+
+#define F(f, s, div, m, n) \
+	{ \
+		.freq_hz = (f), \
+		.src_clk = &s##_clk_src.c, \
+		.m_val = (m), \
+		.n_val = ~((n)-(m)) * !!(n), \
+		.d_val = ~(n),\
+		.div_src_val = BVAL(4, 0, (int)(2*(div) - 1)) \
+			| BVAL(10, 8, s##_source_val), \
+	}
+
+#define F_HSIC(f, s, div, m, n) \
+	{ \
+		.freq_hz = (f), \
+		.src_clk = &s##_clk_src.c, \
+		.m_val = (m), \
+		.n_val = ~((n)-(m)) * !!(n), \
+		.d_val = ~(n),\
+		.div_src_val = BVAL(4, 0, (int)(2*(div) - 1)) \
+			| BVAL(10, 8, s##_hsic_source_val), \
+	}
+
+#define F_LPASS(f, s, div, m, n) \
+	{ \
+		.freq_hz = (f), \
+		.src_clk = &s##_clk_src.c, \
+		.m_val = (m), \
+		.n_val = ~((n)-(m)) * !!(n), \
+		.d_val = ~(n),\
+		.div_src_val = BVAL(4, 0, (int)(2*(div) - 1)) \
+			| BVAL(10, 8, s##_lpass_source_val), \
+	}
+
+#define F_APCS_PLL(f, l, m, n, pre_div, post_div, vco) \
+	{ \
+		.freq_hz = (f), \
+		.l_val = (l), \
+		.m_val = (m), \
+		.n_val = (n), \
+		.pre_div_val = BVAL(14, 12, (pre_div)), \
+		.post_div_val = BVAL(9, 8, (post_div)), \
+		.vco_val = BVAL(21, 20, (vco)), \
+	}
+
+#define VDD_DIG_FMAX_MAP1(l1, f1) \
+	.vdd_class = &vdd_dig,			\
+	.fmax = (unsigned long[VDD_DIG_NUM]) {	\
+		[VDD_DIG_##l1] = (f1),		\
+	},					\
+	.num_fmax = VDD_DIG_NUM
+#define VDD_DIG_FMAX_MAP2(l1, f1, l2, f2) \
+	.vdd_class = &vdd_dig,			\
+	.fmax = (unsigned long[VDD_DIG_NUM]) {	\
+		[VDD_DIG_##l1] = (f1),		\
+		[VDD_DIG_##l2] = (f2),		\
+	},					\
+	.num_fmax = VDD_DIG_NUM
+#define VDD_DIG_FMAX_MAP3(l1, f1, l2, f2, l3, f3) \
+	.vdd_class = &vdd_dig,			\
+	.fmax = (unsigned long[VDD_DIG_NUM]) {	\
+		[VDD_DIG_##l1] = (f1),		\
+		[VDD_DIG_##l2] = (f2),		\
+		[VDD_DIG_##l3] = (f3),		\
+	},					\
+	.num_fmax = VDD_DIG_NUM
+
+enum vdd_dig_levels {
+	VDD_DIG_NONE,
+	VDD_DIG_LOW,
+	VDD_DIG_NOMINAL,
+	VDD_DIG_HIGH,
+	VDD_DIG_NUM
+};
+
+static const int vdd_corner[] = {
+	[VDD_DIG_NONE]	  = RPM_REGULATOR_CORNER_NONE,
+	[VDD_DIG_LOW]	  = RPM_REGULATOR_CORNER_SVS_SOC,
+	[VDD_DIG_NOMINAL] = RPM_REGULATOR_CORNER_NORMAL,
+	[VDD_DIG_HIGH]	  = RPM_REGULATOR_CORNER_SUPER_TURBO,
+};
+
+static struct regulator *vdd_dig_reg;
+
+int set_vdd_dig(struct clk_vdd_class *vdd_class, int level)
+{
+	return regulator_set_voltage(vdd_dig_reg, vdd_corner[level],
+					RPM_REGULATOR_CORNER_SUPER_TURBO);
+}
+
+static DEFINE_VDD_CLASS(vdd_dig, set_vdd_dig, VDD_DIG_NUM);
+
+/* TODO: Needs to confirm the below values */
+#define RPM_MISC_CLK_TYPE	0x306b6c63
+#define RPM_BUS_CLK_TYPE	0x316b6c63
+#define RPM_MEM_CLK_TYPE	0x326b6c63
+
+#define RPM_SMD_KEY_ENABLE	0x62616E45
+
+#define CXO_ID			0x0
+#define QDSS_ID			0x1
+
+#define PNOC_ID		0x0
+#define SNOC_ID		0x1
+#define CNOC_ID		0x2
+
+#define BIMC_ID		0x0
+
+#define D0_ID		 1
+#define D1_ID		 2
+#define A0_ID		 3
+#define A1_ID		 4
+#define A2_ID		 5
+
+DEFINE_CLK_RPM_SMD_BRANCH(cxo_clk_src, cxo_a_clk_src,
+				RPM_MISC_CLK_TYPE, CXO_ID, 19200000);
+
+DEFINE_CLK_RPM_SMD(cnoc_clk, cnoc_a_clk, RPM_BUS_CLK_TYPE, CNOC_ID, NULL);
+DEFINE_CLK_RPM_SMD(pnoc_clk, pnoc_a_clk, RPM_BUS_CLK_TYPE, PNOC_ID, NULL);
+DEFINE_CLK_RPM_SMD(snoc_clk, snoc_a_clk, RPM_BUS_CLK_TYPE, SNOC_ID, NULL);
+
+DEFINE_CLK_RPM_SMD(bimc_clk, bimc_a_clk, RPM_MEM_CLK_TYPE, BIMC_ID, NULL);
+
+DEFINE_CLK_RPM_SMD_QDSS(qdss_clk, qdss_a_clk, RPM_MISC_CLK_TYPE, QDSS_ID);
+
+DEFINE_CLK_RPM_SMD_XO_BUFFER(cxo_d0, cxo_d0_a, D0_ID);
+DEFINE_CLK_RPM_SMD_XO_BUFFER(cxo_d1, cxo_d1_a, D1_ID);
+DEFINE_CLK_RPM_SMD_XO_BUFFER(cxo_a0, cxo_a0_a, A0_ID);
+DEFINE_CLK_RPM_SMD_XO_BUFFER(cxo_a1, cxo_a1_a, A1_ID);
+DEFINE_CLK_RPM_SMD_XO_BUFFER(cxo_a2, cxo_a2_a, A2_ID);
+
+DEFINE_CLK_RPM_SMD_XO_BUFFER_PINCTRL(cxo_d0_pin, cxo_d0_a_pin, D0_ID);
+DEFINE_CLK_RPM_SMD_XO_BUFFER_PINCTRL(cxo_d1_pin, cxo_d1_a_pin, D1_ID);
+DEFINE_CLK_RPM_SMD_XO_BUFFER_PINCTRL(cxo_a0_pin, cxo_a0_a_pin, A0_ID);
+DEFINE_CLK_RPM_SMD_XO_BUFFER_PINCTRL(cxo_a1_pin, cxo_a1_a_pin, A1_ID);
+DEFINE_CLK_RPM_SMD_XO_BUFFER_PINCTRL(cxo_a2_pin, cxo_a2_a_pin, A2_ID);
+
+static unsigned int soft_vote_gpll0;
+
+static struct pll_vote_clk gpll0_clk_src = {
+	.en_reg = (void __iomem *)APCS_GPLL_ENA_VOTE_REG,
+	.status_reg = (void __iomem *)GPLL0_STATUS_REG,
+	.status_mask = BIT(17),
+	.parent = &cxo_clk_src.c,
+	.soft_vote = &soft_vote_gpll0,
+	.soft_vote_mask = PLL_SOFT_VOTE_PRIMARY,
+	.base = &virt_bases[GCC_BASE],
+	.c = {
+		.rate = 600000000,
+		.dbg_name = "gpll0_clk_src",
+		.ops = &clk_ops_pll_acpu_vote,
+		CLK_INIT(gpll0_clk_src.c),
+	},
+};
+
+static struct pll_vote_clk gpll0_activeonly_clk_src = {
+	.en_reg = (void __iomem *)APCS_GPLL_ENA_VOTE_REG,
+	.status_reg = (void __iomem *)GPLL0_STATUS_REG,
+	.status_mask = BIT(17),
+	.soft_vote = &soft_vote_gpll0,
+	.soft_vote_mask = PLL_SOFT_VOTE_ACPU,
+	.base = &virt_bases[GCC_BASE],
+	.c = {
+		.rate = 600000000,
+		.dbg_name = "gpll0_activeonly_clk_src",
+		.ops = &clk_ops_pll_acpu_vote,
+		CLK_INIT(gpll0_activeonly_clk_src.c),
+	},
+};
+
+static struct pll_vote_clk lpapll0_clk_src = {
+	.en_reg = (void __iomem *)LPASS_LPA_PLL_VOTE_APPS_REG,
+	.en_mask = BIT(0),
+	.status_reg = (void __iomem *)LPAPLL_STATUS_REG,
+	.status_mask = BIT(17),
+	.parent = &cxo_clk_src.c,
+	.base = &virt_bases[LPASS_BASE],
+	.c = {
+		.rate = 393216000,
+		.dbg_name = "lpapll0_clk_src",
+		.ops = &clk_ops_pll_vote,
+		CLK_INIT(lpapll0_clk_src.c),
+	},
+};
+
+static struct pll_vote_clk gpll1_clk_src = {
+	.en_reg = (void __iomem *)APCS_GPLL_ENA_VOTE_REG,
+	.en_mask = BIT(1),
+	.status_reg = (void __iomem *)GPLL1_STATUS_REG,
+	.status_mask = BIT(17),
+	.parent = &cxo_clk_src.c,
+	.base = &virt_bases[GCC_BASE],
+	.c = {
+		.rate = 480000000,
+		.dbg_name = "gpll1_clk_src",
+		.ops = &clk_ops_pll_vote,
+		CLK_INIT(gpll1_clk_src.c),
+	},
+};
+
+static struct pll_freq_tbl apcs_pll_freq[] = {
+	F_APCS_PLL(748800000, 0x27, 0x0, 0x1, 0x0, 0x0, 0x0),
+	F_APCS_PLL(998400000, 0x34, 0x0, 0x1, 0x0, 0x0, 0x0),
+	PLL_F_END
+};
+
+/*
+ * Need to skip handoff of the acpu pll to avoid handoff code
+ * to turn off the pll when the acpu is running off this pll.
+ */
+static struct pll_clk apcspll_clk_src = {
+	.mode_reg = (void __iomem *)APCS_CPU_PLL_MODE_REG,
+	.l_reg = (void __iomem *)APCS_CPU_PLL_L_REG,
+	.m_reg = (void __iomem *)APCS_CPU_PLL_M_REG,
+	.n_reg = (void __iomem *)APCS_CPU_PLL_N_REG,
+	.config_reg = (void __iomem *)APCS_CPU_PLL_USER_CTL_REG,
+	.status_reg = (void __iomem *)APCS_CPU_PLL_STATUS_REG,
+	.freq_tbl = apcs_pll_freq,
+	.masks = {
+		.vco_mask = BM(21, 20),
+		.pre_div_mask = BM(14, 12),
+		.post_div_mask = BM(9, 8),
+		.mn_en_mask = BIT(24),
+		.main_output_mask = BIT(0),
+	},
+	.base = &virt_bases[APCS_PLL_BASE],
+	.c = {
+		.dbg_name = "apcspll_clk_src",
+		.ops = &clk_ops_local_pll,
+		CLK_INIT(apcspll_clk_src.c),
+		.flags = CLKFLAG_SKIP_HANDOFF,
+	},
+};
+
+static DEFINE_CLK_VOTER(pnoc_msmbus_clk, &pnoc_clk.c, LONG_MAX);
+static DEFINE_CLK_VOTER(snoc_msmbus_clk, &snoc_clk.c, LONG_MAX);
+static DEFINE_CLK_VOTER(cnoc_msmbus_clk, &cnoc_clk.c, LONG_MAX);
+static DEFINE_CLK_VOTER(pnoc_msmbus_a_clk, &pnoc_a_clk.c, LONG_MAX);
+static DEFINE_CLK_VOTER(snoc_msmbus_a_clk, &snoc_a_clk.c, LONG_MAX);
+static DEFINE_CLK_VOTER(cnoc_msmbus_a_clk, &cnoc_a_clk.c, LONG_MAX);
+
+static DEFINE_CLK_VOTER(bimc_msmbus_clk, &bimc_clk.c, LONG_MAX);
+static DEFINE_CLK_VOTER(bimc_msmbus_a_clk, &bimc_a_clk.c, LONG_MAX);
+
+static DEFINE_CLK_VOTER(pnoc_sdcc2_clk, &pnoc_clk.c, LONG_MAX);
+static DEFINE_CLK_VOTER(pnoc_sdcc3_clk, &pnoc_clk.c, LONG_MAX);
+
+static DEFINE_CLK_VOTER(pnoc_sps_clk, &pnoc_clk.c, LONG_MAX);
+
+static struct clk_freq_tbl ftbl_gcc_ipa_clk[] = {
+	F( 50000000,    gpll0,   12,   0,   0),
+	F( 92310000,    gpll0,  6.5,   0,   0),
+	F(100000000,    gpll0,    6,   0,   0),
+	F_END
+};
+
+static struct rcg_clk ipa_clk_src = {
+	.cmd_rcgr_reg =  IPA_CMD_RCGR,
+	.set_rate = set_rate_mnd,
+	.freq_tbl = ftbl_gcc_ipa_clk,
+	.current_freq = &rcg_dummy_freq,
+	.base = &virt_bases[GCC_BASE],
+	.c = {
+		.dbg_name = "ipa_clk_src",
+		.ops = &clk_ops_rcg_mnd,
+		VDD_DIG_FMAX_MAP2(LOW, 50000000, NOMINAL, 100000000),
+		CLK_INIT(ipa_clk_src.c)
+	},
+};
+
+static struct clk_freq_tbl ftbl_gcc_blsp1_qup1_6_spi_apps_clk[] = {
+	F(  960000,     cxo,   10,   1,   2),
+	F( 4800000,     cxo,    4,   0,   0),
+	F( 9600000,     cxo,    2,   0,   0),
+	F(15000000,   gpll0,   10,   1,   4),
+	F(19200000,     cxo,    1,   0,   0),
+	F(25000000,   gpll0,   12,   1,   2),
+	F(50000000,   gpll0,   12,   0,   0),
+	F_END
+};
+
+static struct rcg_clk blsp1_qup1_spi_apps_clk_src = {
+	.cmd_rcgr_reg =  BLSP1_QUP1_SPI_APPS_CMD_RCGR,
+	.set_rate = set_rate_mnd,
+	.freq_tbl = ftbl_gcc_blsp1_qup1_6_spi_apps_clk,
+	.current_freq = &rcg_dummy_freq,
+	.base = &virt_bases[GCC_BASE],
+	.c = {
+		.dbg_name = "blsp1_qup1_spi_apps_clk_src",
+		.ops = &clk_ops_rcg_mnd,
+		VDD_DIG_FMAX_MAP2(LOW, 25000000, NOMINAL, 50000000),
+		CLK_INIT(blsp1_qup1_spi_apps_clk_src.c)
+	},
+};
+
+static struct rcg_clk blsp1_qup2_spi_apps_clk_src = {
+	.cmd_rcgr_reg =  BLSP1_QUP2_SPI_APPS_CMD_RCGR,
+	.set_rate = set_rate_mnd,
+	.freq_tbl = ftbl_gcc_blsp1_qup1_6_spi_apps_clk,
+	.current_freq = &rcg_dummy_freq,
+	.base = &virt_bases[GCC_BASE],
+	.c = {
+		.dbg_name = "blsp1_qup2_spi_apps_clk_src",
+		.ops = &clk_ops_rcg_mnd,
+		VDD_DIG_FMAX_MAP2(LOW, 25000000, NOMINAL, 50000000),
+		CLK_INIT(blsp1_qup2_spi_apps_clk_src.c)
+	},
+};
+
+static struct rcg_clk blsp1_qup3_spi_apps_clk_src = {
+	.cmd_rcgr_reg =  BLSP1_QUP3_SPI_APPS_CMD_RCGR,
+	.set_rate = set_rate_mnd,
+	.freq_tbl = ftbl_gcc_blsp1_qup1_6_spi_apps_clk,
+	.current_freq = &rcg_dummy_freq,
+	.base = &virt_bases[GCC_BASE],
+	.c = {
+		.dbg_name = "blsp1_qup3_spi_apps_clk_src",
+		.ops = &clk_ops_rcg_mnd,
+		VDD_DIG_FMAX_MAP2(LOW, 25000000, NOMINAL, 50000000),
+		CLK_INIT(blsp1_qup3_spi_apps_clk_src.c)
+	},
+};
+
+static struct rcg_clk blsp1_qup4_spi_apps_clk_src = {
+	.cmd_rcgr_reg =  BLSP1_QUP4_SPI_APPS_CMD_RCGR,
+	.set_rate = set_rate_mnd,
+	.freq_tbl = ftbl_gcc_blsp1_qup1_6_spi_apps_clk,
+	.current_freq = &rcg_dummy_freq,
+	.base = &virt_bases[GCC_BASE],
+	.c = {
+		.dbg_name = "blsp1_qup4_spi_apps_clk_src",
+		.ops = &clk_ops_rcg_mnd,
+		VDD_DIG_FMAX_MAP2(LOW, 25000000, NOMINAL, 50000000),
+		CLK_INIT(blsp1_qup4_spi_apps_clk_src.c)
+	},
+};
+
+static struct rcg_clk blsp1_qup5_spi_apps_clk_src = {
+	.cmd_rcgr_reg =  BLSP1_QUP5_SPI_APPS_CMD_RCGR,
+	.set_rate = set_rate_mnd,
+	.freq_tbl = ftbl_gcc_blsp1_qup1_6_spi_apps_clk,
+	.current_freq = &rcg_dummy_freq,
+	.base = &virt_bases[GCC_BASE],
+	.c = {
+		.dbg_name = "blsp1_qup5_spi_apps_clk_src",
+		.ops = &clk_ops_rcg_mnd,
+		VDD_DIG_FMAX_MAP2(LOW, 25000000, NOMINAL, 50000000),
+		CLK_INIT(blsp1_qup5_spi_apps_clk_src.c)
+	},
+};
+
+static struct rcg_clk blsp1_qup6_spi_apps_clk_src = {
+	.cmd_rcgr_reg =  BLSP1_QUP6_SPI_APPS_CMD_RCGR,
+	.set_rate = set_rate_mnd,
+	.freq_tbl = ftbl_gcc_blsp1_qup1_6_spi_apps_clk,
+	.current_freq = &rcg_dummy_freq,
+	.base = &virt_bases[GCC_BASE],
+	.c = {
+		.dbg_name = "blsp1_qup6_spi_apps_clk_src",
+		.ops = &clk_ops_rcg_mnd,
+		VDD_DIG_FMAX_MAP2(LOW, 25000000, NOMINAL, 50000000),
+		CLK_INIT(blsp1_qup6_spi_apps_clk_src.c)
+	},
+};
+
+static struct clk_freq_tbl ftbl_gcc_blsp1_uart1_6_apps_clk[] = {
+	F( 3686400,    gpll0,    1,    96,   15625),
+	F( 7372800,    gpll0,    1,   192,   15625),
+	F(14745600,    gpll0,    1,   384,   15625),
+	F(16000000,    gpll0,    5,     2,      15),
+	F(19200000,      cxo,    1,     0,       0),
+	F(24000000,    gpll0,    5,     1,       5),
+	F(32000000,    gpll0,    1,     4,      75),
+	F(40000000,    gpll0,   15,     0,       0),
+	F(46400000,    gpll0,    1,    29,     375),
+	F(48000000,    gpll0,  12.5,    0,       0),
+	F(51200000,    gpll0,    1,    32,     375),
+	F(56000000,    gpll0,    1,     7,      75),
+	F(58982400,    gpll0,    1,  1536,   15625),
+	F(60000000,    gpll0,   10,     0,       0),
+	F_END
+};
+
+static struct rcg_clk blsp1_uart1_apps_clk_src = {
+	.cmd_rcgr_reg =  BLSP1_UART1_APPS_CMD_RCGR,
+	.set_rate = set_rate_mnd,
+	.freq_tbl = ftbl_gcc_blsp1_uart1_6_apps_clk,
+	.current_freq = &rcg_dummy_freq,
+	.base = &virt_bases[GCC_BASE],
+	.c = {
+		.dbg_name = "blsp1_uart1_apps_clk_src",
+		.ops = &clk_ops_rcg_mnd,
+		VDD_DIG_FMAX_MAP2(LOW, 31580000, NOMINAL, 63160000),
+		CLK_INIT(blsp1_uart1_apps_clk_src.c)
+	},
+};
+
+static struct rcg_clk blsp1_uart2_apps_clk_src = {
+	.cmd_rcgr_reg =  BLSP1_UART2_APPS_CMD_RCGR,
+	.set_rate = set_rate_mnd,
+	.freq_tbl = ftbl_gcc_blsp1_uart1_6_apps_clk,
+	.current_freq = &rcg_dummy_freq,
+	.base = &virt_bases[GCC_BASE],
+	.c = {
+		.dbg_name = "blsp1_uart2_apps_clk_src",
+		.ops = &clk_ops_rcg_mnd,
+		VDD_DIG_FMAX_MAP2(LOW, 31580000, NOMINAL, 63160000),
+		CLK_INIT(blsp1_uart2_apps_clk_src.c)
+	},
+};
+
+static struct rcg_clk blsp1_uart3_apps_clk_src = {
+	.cmd_rcgr_reg =  BLSP1_UART3_APPS_CMD_RCGR,
+	.set_rate = set_rate_mnd,
+	.freq_tbl = ftbl_gcc_blsp1_uart1_6_apps_clk,
+	.current_freq = &rcg_dummy_freq,
+	.base = &virt_bases[GCC_BASE],
+	.c = {
+		.dbg_name = "blsp1_uart3_apps_clk_src",
+		.ops = &clk_ops_rcg_mnd,
+		VDD_DIG_FMAX_MAP2(LOW, 31580000, NOMINAL, 63160000),
+		CLK_INIT(blsp1_uart3_apps_clk_src.c)
+	},
+};
+
+static struct rcg_clk blsp1_uart4_apps_clk_src = {
+	.cmd_rcgr_reg =  BLSP1_UART4_APPS_CMD_RCGR,
+	.set_rate = set_rate_mnd,
+	.freq_tbl = ftbl_gcc_blsp1_uart1_6_apps_clk,
+	.current_freq = &rcg_dummy_freq,
+	.base = &virt_bases[GCC_BASE],
+	.c = {
+		.dbg_name = "blsp1_uart4_apps_clk_src",
+		.ops = &clk_ops_rcg_mnd,
+		VDD_DIG_FMAX_MAP2(LOW, 31580000, NOMINAL, 63160000),
+		CLK_INIT(blsp1_uart4_apps_clk_src.c)
+	},
+};
+
+static struct rcg_clk blsp1_uart5_apps_clk_src = {
+	.cmd_rcgr_reg =  BLSP1_UART5_APPS_CMD_RCGR,
+	.set_rate = set_rate_mnd,
+	.freq_tbl = ftbl_gcc_blsp1_uart1_6_apps_clk,
+	.current_freq = &rcg_dummy_freq,
+	.base = &virt_bases[GCC_BASE],
+	.c = {
+		.dbg_name = "blsp1_uart5_apps_clk_src",
+		.ops = &clk_ops_rcg_mnd,
+		VDD_DIG_FMAX_MAP2(LOW, 31580000, NOMINAL, 63160000),
+		CLK_INIT(blsp1_uart5_apps_clk_src.c)
+	},
+};
+
+static struct rcg_clk blsp1_uart6_apps_clk_src = {
+	.cmd_rcgr_reg =  BLSP1_UART6_APPS_CMD_RCGR,
+	.set_rate = set_rate_mnd,
+	.freq_tbl = ftbl_gcc_blsp1_uart1_6_apps_clk,
+	.current_freq = &rcg_dummy_freq,
+	.base = &virt_bases[GCC_BASE],
+	.c = {
+		.dbg_name = "blsp1_uart6_apps_clk_src",
+		.ops = &clk_ops_rcg_mnd,
+		VDD_DIG_FMAX_MAP2(LOW, 31580000, NOMINAL, 63160000),
+		CLK_INIT(blsp1_uart6_apps_clk_src.c)
+	},
+};
+
+static struct clk_freq_tbl ftbl_gcc_ce1_clk[] = {
+	F( 50000000,    gpll0,   12,   0,   0),
+	F(100000000,    gpll0,    6,   0,   0),
+	F_END
+};
+
+static struct rcg_clk ce1_clk_src = {
+	.cmd_rcgr_reg = CE1_CMD_RCGR,
+	.set_rate = set_rate_hid,
+	.freq_tbl = ftbl_gcc_ce1_clk,
+	.current_freq = &rcg_dummy_freq,
+	.base = &virt_bases[GCC_BASE],
+	.c = {
+		.dbg_name = "ce1_clk_src",
+		.ops = &clk_ops_rcg,
+		VDD_DIG_FMAX_MAP2(LOW, 50000000, NOMINAL, 100000000),
+		CLK_INIT(ce1_clk_src.c),
+	},
+};
+
+static struct clk_freq_tbl ftbl_gcc_gp_clk[] = {
+	F(19200000,   cxo,   1,   0,   0),
+	F_END
+};
+
+static struct rcg_clk gp1_clk_src = {
+	.cmd_rcgr_reg =  GP1_CMD_RCGR,
+	.set_rate = set_rate_mnd,
+	.freq_tbl = ftbl_gcc_gp_clk,
+	.current_freq = &rcg_dummy_freq,
+	.base = &virt_bases[GCC_BASE],
+	.c = {
+		.dbg_name = "gp1_clk_src",
+		.ops = &clk_ops_rcg_mnd,
+		VDD_DIG_FMAX_MAP2(LOW, 100000000, NOMINAL, 200000000),
+		CLK_INIT(gp1_clk_src.c)
+	},
+};
+
+static struct rcg_clk gp2_clk_src = {
+	.cmd_rcgr_reg =  GP2_CMD_RCGR,
+	.set_rate = set_rate_mnd,
+	.freq_tbl = ftbl_gcc_gp_clk,
+	.current_freq = &rcg_dummy_freq,
+	.base = &virt_bases[GCC_BASE],
+	.c = {
+		.dbg_name = "gp2_clk_src",
+		.ops = &clk_ops_rcg_mnd,
+		VDD_DIG_FMAX_MAP2(LOW, 100000000, NOMINAL, 200000000),
+		CLK_INIT(gp2_clk_src.c)
+	},
+};
+
+static struct rcg_clk gp3_clk_src = {
+	.cmd_rcgr_reg =  GP3_CMD_RCGR,
+	.set_rate = set_rate_mnd,
+	.freq_tbl = ftbl_gcc_gp_clk,
+	.current_freq = &rcg_dummy_freq,
+	.base = &virt_bases[GCC_BASE],
+	.c = {
+		.dbg_name = "gp3_clk_src",
+		.ops = &clk_ops_rcg_mnd,
+		VDD_DIG_FMAX_MAP2(LOW, 100000000, NOMINAL, 200000000),
+		CLK_INIT(gp3_clk_src.c)
+	},
+};
+
+static struct clk_freq_tbl ftbl_gcc_pdm2_clk[] = {
+	F(60000000,   gpll0,  10,   0,   0),
+	F_END
+};
+
+static struct rcg_clk pdm2_clk_src = {
+	.cmd_rcgr_reg = PDM2_CMD_RCGR,
+	.set_rate = set_rate_hid,
+	.freq_tbl = ftbl_gcc_pdm2_clk,
+	.current_freq = &rcg_dummy_freq,
+	.base = &virt_bases[GCC_BASE],
+	.c = {
+		.dbg_name = "pdm2_clk_src",
+		.ops = &clk_ops_rcg,
+		VDD_DIG_FMAX_MAP1(LOW, 60000000),
+		CLK_INIT(pdm2_clk_src.c),
+	},
+};
+
+static struct clk_freq_tbl ftbl_gcc_qpic_clk[] = {
+	F( 50000000,    gpll0,   12,   0,   0),
+	F(100000000,    gpll0,    6,   0,   0),
+	F_END
+};
+
+static struct rcg_clk qpic_clk_src = {
+	.cmd_rcgr_reg =  QPIC_CMD_RCGR,
+	.set_rate = set_rate_mnd,
+	.freq_tbl = ftbl_gcc_qpic_clk,
+	.current_freq = &rcg_dummy_freq,
+	.base = &virt_bases[GCC_BASE],
+	.c = {
+		.dbg_name = "qpic_clk_src",
+		.ops = &clk_ops_rcg_mnd,
+		VDD_DIG_FMAX_MAP2(LOW, 50000000, NOMINAL, 100000000),
+		CLK_INIT(qpic_clk_src.c)
+	},
+};
+
+static struct clk_freq_tbl ftbl_gcc_sdcc2_apps_clk[] = {
+	F(   144000,      cxo,   16,   3,   25),
+	F(   400000,      cxo,   12,   1,    4),
+	F( 20000000,    gpll0,   15,   1,    2),
+	F( 25000000,    gpll0,   12,   1,    2),
+	F( 50000000,    gpll0,   12,   0,    0),
+	F(100000000,    gpll0,    6,   0,    0),
+	F(200000000,    gpll0,    3,   0,    0),
+	F_END
+};
+
+static struct clk_freq_tbl ftbl_gcc_sdcc3_apps_clk[] = {
+	F(   144000,      cxo,   16,   3,   25),
+	F(   400000,      cxo,   12,   1,    4),
+	F( 20000000,    gpll0,   15,   1,    2),
+	F( 25000000,    gpll0,   12,   1,    2),
+	F( 50000000,    gpll0,   12,   0,    0),
+	F(100000000,    gpll0,    6,   0,    0),
+	F_END
+};
+
+static struct rcg_clk sdcc2_apps_clk_src = {
+	.cmd_rcgr_reg =  SDCC2_APPS_CMD_RCGR,
+	.set_rate = set_rate_mnd,
+	.freq_tbl = ftbl_gcc_sdcc2_apps_clk,
+	.current_freq = &rcg_dummy_freq,
+	.base = &virt_bases[GCC_BASE],
+	.c = {
+		.dbg_name = "sdcc2_apps_clk_src",
+		.ops = &clk_ops_rcg_mnd,
+		VDD_DIG_FMAX_MAP2(LOW, 100000000, NOMINAL, 200000000),
+		CLK_INIT(sdcc2_apps_clk_src.c)
+	},
+};
+
+static struct rcg_clk sdcc3_apps_clk_src = {
+	.cmd_rcgr_reg =  SDCC3_APPS_CMD_RCGR,
+	.set_rate = set_rate_mnd,
+	.freq_tbl = ftbl_gcc_sdcc3_apps_clk,
+	.current_freq = &rcg_dummy_freq,
+	.base = &virt_bases[GCC_BASE],
+	.c = {
+		.dbg_name = "sdcc3_apps_clk_src",
+		.ops = &clk_ops_rcg_mnd,
+		VDD_DIG_FMAX_MAP2(LOW, 50000000, NOMINAL, 100000000),
+		CLK_INIT(sdcc3_apps_clk_src.c)
+	},
+};
+
+static struct clk_freq_tbl ftbl_gcc_usb_hs_system_clk[] = {
+	F(75000000,   gpll0,   8,   0,   0),
+	F_END
+};
+
+static struct rcg_clk usb_hs_system_clk_src = {
+	.cmd_rcgr_reg = USB_HS_SYSTEM_CMD_RCGR,
+	.set_rate = set_rate_hid,
+	.freq_tbl = ftbl_gcc_usb_hs_system_clk,
+	.current_freq = &rcg_dummy_freq,
+	.base = &virt_bases[GCC_BASE],
+	.c = {
+		.dbg_name = "usb_hs_system_clk_src",
+		.ops = &clk_ops_rcg,
+		VDD_DIG_FMAX_MAP2(LOW, 37500000, NOMINAL, 75000000),
+		CLK_INIT(usb_hs_system_clk_src.c),
+	},
+};
+
+static struct clk_freq_tbl ftbl_gcc_usb_hsic_clk[] = {
+	F_HSIC(480000000,   gpll1,   1,   0,   0),
+	F_END
+};
+
+static struct rcg_clk usb_hsic_clk_src = {
+	.cmd_rcgr_reg = USB_HSIC_CMD_RCGR,
+	.set_rate = set_rate_hid,
+	.freq_tbl = ftbl_gcc_usb_hsic_clk,
+	.current_freq = &rcg_dummy_freq,
+	.base = &virt_bases[GCC_BASE],
+	.c = {
+		.dbg_name = "usb_hsic_clk_src",
+		.ops = &clk_ops_rcg,
+		VDD_DIG_FMAX_MAP1(LOW, 480000000),
+		CLK_INIT(usb_hsic_clk_src.c),
+	},
+};
+
+static struct clk_freq_tbl ftbl_gcc_usb_hsic_io_cal_clk[] = {
+	F(9600000,   cxo,   2,   0,   0),
+	F_END
+};
+
+static struct rcg_clk usb_hsic_io_cal_clk_src = {
+	.cmd_rcgr_reg = USB_HSIC_IO_CAL_CMD_RCGR,
+	.set_rate = set_rate_hid,
+	.freq_tbl = ftbl_gcc_usb_hsic_io_cal_clk,
+	.current_freq = &rcg_dummy_freq,
+	.base = &virt_bases[GCC_BASE],
+	.c = {
+		.dbg_name = "usb_hsic_io_cal_clk_src",
+		.ops = &clk_ops_rcg,
+		VDD_DIG_FMAX_MAP1(LOW, 9600000),
+		CLK_INIT(usb_hsic_io_cal_clk_src.c),
+	},
+};
+
+static struct clk_freq_tbl ftbl_gcc_usb_hsic_system_clk[] = {
+	F(75000000,   gpll0,   8,   0,   0),
+	F_END
+};
+
+static struct rcg_clk usb_hsic_system_clk_src = {
+	.cmd_rcgr_reg = USB_HSIC_SYSTEM_CMD_RCGR,
+	.set_rate = set_rate_hid,
+	.freq_tbl = ftbl_gcc_usb_hsic_system_clk,
+	.current_freq = &rcg_dummy_freq,
+	.base = &virt_bases[GCC_BASE],
+	.c = {
+		.dbg_name = "usb_hsic_system_clk_src",
+		.ops = &clk_ops_rcg,
+		VDD_DIG_FMAX_MAP2(LOW, 60000000, NOMINAL, 75000000),
+		CLK_INIT(usb_hsic_system_clk_src.c),
+	},
+};
+
+static struct clk_freq_tbl ftbl_gcc_usb_hsic_xcvr_fs_clk[] = {
+	F(60000000,   gpll0,   10,   0,   0),
+	F_END
+};
+
+static struct rcg_clk usb_hsic_xcvr_fs_clk_src = {
+	.cmd_rcgr_reg = USB_HSIC_XCVR_FS_CMD_RCGR,
+	.set_rate = set_rate_hid,
+	.freq_tbl = ftbl_gcc_usb_hsic_xcvr_fs_clk,
+	.current_freq = &rcg_dummy_freq,
+	.base = &virt_bases[GCC_BASE],
+	.c = {
+		.dbg_name = "usb_hsic_xcvr_fs_clk_src",
+		.ops = &clk_ops_rcg,
+		VDD_DIG_FMAX_MAP1(LOW, 60000000),
+		CLK_INIT(usb_hsic_xcvr_fs_clk_src.c),
+	},
+};
+
+static struct local_vote_clk gcc_bam_dma_ahb_clk = {
+	.cbcr_reg = BAM_DMA_AHB_CBCR,
+	.vote_reg = APCS_CLOCK_BRANCH_ENA_VOTE,
+	.en_mask = BIT(12),
+	.base = &virt_bases[GCC_BASE],
+	.c = {
+		.dbg_name = "gcc_bam_dma_ahb_clk",
+		.ops = &clk_ops_vote,
+		CLK_INIT(gcc_bam_dma_ahb_clk.c),
+	},
+};
+
+static struct local_vote_clk gcc_blsp1_ahb_clk = {
+	.cbcr_reg = BLSP1_AHB_CBCR,
+	.vote_reg = APCS_CLOCK_BRANCH_ENA_VOTE,
+	.en_mask = BIT(17),
+	.base = &virt_bases[GCC_BASE],
+	.c = {
+		.dbg_name = "gcc_blsp1_ahb_clk",
+		.ops = &clk_ops_vote,
+		CLK_INIT(gcc_blsp1_ahb_clk.c),
+	},
+};
+
+static struct branch_clk gcc_blsp1_qup1_i2c_apps_clk = {
+	.cbcr_reg = BLSP1_QUP1_I2C_APPS_CBCR,
+	.parent = &cxo_clk_src.c,
+	.has_sibling = 1,
+	.base = &virt_bases[GCC_BASE],
+	.c = {
+		.dbg_name = "gcc_blsp1_qup1_i2c_apps_clk",
+		.ops = &clk_ops_branch,
+		CLK_INIT(gcc_blsp1_qup1_i2c_apps_clk.c),
+	},
+};
+
+static struct branch_clk gcc_blsp1_qup1_spi_apps_clk = {
+	.cbcr_reg = BLSP1_QUP1_SPI_APPS_CBCR,
+	.parent = &blsp1_qup1_spi_apps_clk_src.c,
+	.has_sibling = 0,
+	.base = &virt_bases[GCC_BASE],
+	.c = {
+		.dbg_name = "gcc_blsp1_qup1_spi_apps_clk",
+		.ops = &clk_ops_branch,
+		CLK_INIT(gcc_blsp1_qup1_spi_apps_clk.c),
+	},
+};
+
+static struct branch_clk gcc_blsp1_qup2_i2c_apps_clk = {
+	.cbcr_reg = BLSP1_QUP2_I2C_APPS_CBCR,
+	.parent = &cxo_clk_src.c,
+	.has_sibling = 1,
+	.base = &virt_bases[GCC_BASE],
+	.c = {
+		.dbg_name = "gcc_blsp1_qup2_i2c_apps_clk",
+		.ops = &clk_ops_branch,
+		CLK_INIT(gcc_blsp1_qup2_i2c_apps_clk.c),
+	},
+};
+
+static struct branch_clk gcc_blsp1_qup2_spi_apps_clk = {
+	.cbcr_reg = BLSP1_QUP2_SPI_APPS_CBCR,
+	.parent = &blsp1_qup2_spi_apps_clk_src.c,
+	.has_sibling = 0,
+	.base = &virt_bases[GCC_BASE],
+	.c = {
+		.dbg_name = "gcc_blsp1_qup2_spi_apps_clk",
+		.ops = &clk_ops_branch,
+		CLK_INIT(gcc_blsp1_qup2_spi_apps_clk.c),
+	},
+};
+
+static struct branch_clk gcc_blsp1_qup3_i2c_apps_clk = {
+	.cbcr_reg = BLSP1_QUP3_I2C_APPS_CBCR,
+	.parent = &cxo_clk_src.c,
+	.has_sibling = 1,
+	.base = &virt_bases[GCC_BASE],
+	.c = {
+		.dbg_name = "gcc_blsp1_qup3_i2c_apps_clk",
+		.ops = &clk_ops_branch,
+		CLK_INIT(gcc_blsp1_qup3_i2c_apps_clk.c),
+	},
+};
+
+static struct branch_clk gcc_blsp1_qup3_spi_apps_clk = {
+	.cbcr_reg = BLSP1_QUP3_SPI_APPS_CBCR,
+	.parent = &blsp1_qup3_spi_apps_clk_src.c,
+	.has_sibling = 0,
+	.base = &virt_bases[GCC_BASE],
+	.c = {
+		.dbg_name = "gcc_blsp1_qup3_spi_apps_clk",
+		.ops = &clk_ops_branch,
+		CLK_INIT(gcc_blsp1_qup3_spi_apps_clk.c),
+	},
+};
+
+static struct branch_clk gcc_blsp1_qup4_i2c_apps_clk = {
+	.cbcr_reg = BLSP1_QUP4_I2C_APPS_CBCR,
+	.parent = &cxo_clk_src.c,
+	.has_sibling = 1,
+	.base = &virt_bases[GCC_BASE],
+	.c = {
+		.dbg_name = "gcc_blsp1_qup4_i2c_apps_clk",
+		.ops = &clk_ops_branch,
+		CLK_INIT(gcc_blsp1_qup4_i2c_apps_clk.c),
+	},
+};
+
+static struct branch_clk gcc_blsp1_qup4_spi_apps_clk = {
+	.cbcr_reg = BLSP1_QUP4_SPI_APPS_CBCR,
+	.parent = &blsp1_qup4_spi_apps_clk_src.c,
+	.has_sibling = 0,
+	.base = &virt_bases[GCC_BASE],
+	.c = {
+		.dbg_name = "gcc_blsp1_qup4_spi_apps_clk",
+		.ops = &clk_ops_branch,
+		CLK_INIT(gcc_blsp1_qup4_spi_apps_clk.c),
+	},
+};
+
+static struct branch_clk gcc_blsp1_qup5_i2c_apps_clk = {
+	.cbcr_reg = BLSP1_QUP5_I2C_APPS_CBCR,
+	.parent = &cxo_clk_src.c,
+	.has_sibling = 1,
+	.base = &virt_bases[GCC_BASE],
+	.c = {
+		.dbg_name = "gcc_blsp1_qup5_i2c_apps_clk",
+		.ops = &clk_ops_branch,
+		CLK_INIT(gcc_blsp1_qup5_i2c_apps_clk.c),
+	},
+};
+
+static struct branch_clk gcc_blsp1_qup5_spi_apps_clk = {
+	.cbcr_reg = BLSP1_QUP5_SPI_APPS_CBCR,
+	.parent = &blsp1_qup5_spi_apps_clk_src.c,
+	.has_sibling = 0,
+	.base = &virt_bases[GCC_BASE],
+	.c = {
+		.dbg_name = "gcc_blsp1_qup5_spi_apps_clk",
+		.ops = &clk_ops_branch,
+		CLK_INIT(gcc_blsp1_qup5_spi_apps_clk.c),
+	},
+};
+
+static struct branch_clk gcc_blsp1_qup6_i2c_apps_clk = {
+	.cbcr_reg = BLSP1_QUP6_I2C_APPS_CBCR,
+	.parent = &cxo_clk_src.c,
+	.has_sibling = 1,
+	.base = &virt_bases[GCC_BASE],
+	.c = {
+		.dbg_name = "gcc_blsp1_qup6_i2c_apps_clk",
+		.ops = &clk_ops_branch,
+		CLK_INIT(gcc_blsp1_qup6_i2c_apps_clk.c),
+	},
+};
+
+static struct branch_clk gcc_blsp1_qup6_spi_apps_clk = {
+	.cbcr_reg = BLSP1_QUP6_SPI_APPS_CBCR,
+	.parent = &blsp1_qup6_spi_apps_clk_src.c,
+	.has_sibling = 0,
+	.base = &virt_bases[GCC_BASE],
+	.c = {
+		.dbg_name = "gcc_blsp1_qup6_spi_apps_clk",
+		.ops = &clk_ops_branch,
+		CLK_INIT(gcc_blsp1_qup6_spi_apps_clk.c),
+	},
+};
+
+static struct branch_clk gcc_blsp1_uart1_apps_clk = {
+	.cbcr_reg = BLSP1_UART1_APPS_CBCR,
+	.parent = &blsp1_uart1_apps_clk_src.c,
+	.has_sibling = 0,
+	.base = &virt_bases[GCC_BASE],
+	.c = {
+		.dbg_name = "gcc_blsp1_uart1_apps_clk",
+		.ops = &clk_ops_branch,
+		CLK_INIT(gcc_blsp1_uart1_apps_clk.c),
+	},
+};
+
+static struct branch_clk gcc_blsp1_uart2_apps_clk = {
+	.cbcr_reg = BLSP1_UART2_APPS_CBCR,
+	.parent = &blsp1_uart2_apps_clk_src.c,
+	.has_sibling = 0,
+	.base = &virt_bases[GCC_BASE],
+	.c = {
+		.dbg_name = "gcc_blsp1_uart2_apps_clk",
+		.ops = &clk_ops_branch,
+		CLK_INIT(gcc_blsp1_uart2_apps_clk.c),
+	},
+};
+
+static struct branch_clk gcc_blsp1_uart3_apps_clk = {
+	.cbcr_reg = BLSP1_UART3_APPS_CBCR,
+	.parent = &blsp1_uart3_apps_clk_src.c,
+	.has_sibling = 0,
+	.base = &virt_bases[GCC_BASE],
+	.c = {
+		.dbg_name = "gcc_blsp1_uart3_apps_clk",
+		.ops = &clk_ops_branch,
+		CLK_INIT(gcc_blsp1_uart3_apps_clk.c),
+	},
+};
+
+static struct branch_clk gcc_blsp1_uart4_apps_clk = {
+	.cbcr_reg = BLSP1_UART4_APPS_CBCR,
+	.parent = &blsp1_uart4_apps_clk_src.c,
+	.has_sibling = 0,
+	.base = &virt_bases[GCC_BASE],
+	.c = {
+		.dbg_name = "gcc_blsp1_uart4_apps_clk",
+		.ops = &clk_ops_branch,
+		CLK_INIT(gcc_blsp1_uart4_apps_clk.c),
+	},
+};
+
+static struct branch_clk gcc_blsp1_uart5_apps_clk = {
+	.cbcr_reg = BLSP1_UART5_APPS_CBCR,
+	.parent = &blsp1_uart5_apps_clk_src.c,
+	.has_sibling = 0,
+	.base = &virt_bases[GCC_BASE],
+	.c = {
+		.dbg_name = "gcc_blsp1_uart5_apps_clk",
+		.ops = &clk_ops_branch,
+		CLK_INIT(gcc_blsp1_uart5_apps_clk.c),
+	},
+};
+
+static struct branch_clk gcc_blsp1_uart6_apps_clk = {
+	.cbcr_reg = BLSP1_UART6_APPS_CBCR,
+	.parent = &blsp1_uart6_apps_clk_src.c,
+	.has_sibling = 0,
+	.base = &virt_bases[GCC_BASE],
+	.c = {
+		.dbg_name = "gcc_blsp1_uart6_apps_clk",
+		.ops = &clk_ops_branch,
+		CLK_INIT(gcc_blsp1_uart6_apps_clk.c),
+	},
+};
+
+static struct local_vote_clk gcc_boot_rom_ahb_clk = {
+	.cbcr_reg = BOOT_ROM_AHB_CBCR,
+	.vote_reg = APCS_CLOCK_BRANCH_ENA_VOTE,
+	.en_mask = BIT(10),
+	.base = &virt_bases[GCC_BASE],
+	.c = {
+		.dbg_name = "gcc_boot_rom_ahb_clk",
+		.ops = &clk_ops_vote,
+		CLK_INIT(gcc_boot_rom_ahb_clk.c),
+	},
+};
+
+static struct local_vote_clk gcc_ce1_ahb_clk = {
+	.cbcr_reg = CE1_AHB_CBCR,
+	.vote_reg = APCS_CLOCK_BRANCH_ENA_VOTE,
+	.en_mask = BIT(3),
+	.base = &virt_bases[GCC_BASE],
+	.c = {
+		.dbg_name = "gcc_ce1_ahb_clk",
+		.ops = &clk_ops_vote,
+		CLK_INIT(gcc_ce1_ahb_clk.c),
+	},
+};
+
+static struct local_vote_clk gcc_ce1_axi_clk = {
+	.cbcr_reg = CE1_AXI_CBCR,
+	.vote_reg = APCS_CLOCK_BRANCH_ENA_VOTE,
+	.en_mask = BIT(4),
+	.base = &virt_bases[GCC_BASE],
+	.c = {
+		.dbg_name = "gcc_ce1_axi_clk",
+		.ops = &clk_ops_vote,
+		CLK_INIT(gcc_ce1_axi_clk.c),
+	},
+};
+
+static struct local_vote_clk gcc_ce1_clk = {
+	.cbcr_reg = CE1_CBCR,
+	.vote_reg = APCS_CLOCK_BRANCH_ENA_VOTE,
+	.en_mask = BIT(5),
+	.base = &virt_bases[GCC_BASE],
+	.c = {
+		.dbg_name = "gcc_ce1_clk",
+		.ops = &clk_ops_vote,
+		CLK_INIT(gcc_ce1_clk.c),
+	},
+};
+
+static struct branch_clk gcc_gp1_clk = {
+	.cbcr_reg = GP1_CBCR,
+	.parent = &gp1_clk_src.c,
+	.has_sibling = 0,
+	.base = &virt_bases[GCC_BASE],
+	.c = {
+		.dbg_name = "gcc_gp1_clk",
+		.ops = &clk_ops_branch,
+		CLK_INIT(gcc_gp1_clk.c),
+	},
+};
+
+static struct branch_clk gcc_gp2_clk = {
+	.cbcr_reg = GP2_CBCR,
+	.parent = &gp2_clk_src.c,
+	.has_sibling = 0,
+	.base = &virt_bases[GCC_BASE],
+	.c = {
+		.dbg_name = "gcc_gp2_clk",
+		.ops = &clk_ops_branch,
+		CLK_INIT(gcc_gp2_clk.c),
+	},
+};
+
+static struct branch_clk gcc_gp3_clk = {
+	.cbcr_reg = GP3_CBCR,
+	.parent = &gp3_clk_src.c,
+	.has_sibling = 0,
+	.base = &virt_bases[GCC_BASE],
+	.c = {
+		.dbg_name = "gcc_gp3_clk",
+		.ops = &clk_ops_branch,
+		CLK_INIT(gcc_gp3_clk.c),
+	},
+};
+
+static struct branch_clk gcc_ipa_clk = {
+	.cbcr_reg = IPA_CBCR,
+	.parent = &ipa_clk_src.c,
+	.has_sibling = 1,
+	.base = &virt_bases[GCC_BASE],
+	.c = {
+		.dbg_name = "gcc_ipa_clk",
+		.ops = &clk_ops_branch,
+		CLK_INIT(gcc_ipa_clk.c),
+	},
+};
+
+static struct branch_clk gcc_ipa_cnoc_clk = {
+	.cbcr_reg = IPA_CNOC_CBCR,
+	.has_sibling = 1,
+	.base = &virt_bases[GCC_BASE],
+	.c = {
+		.dbg_name = "gcc_ipa_cnoc_clk",
+		.ops = &clk_ops_branch,
+		CLK_INIT(gcc_ipa_cnoc_clk.c),
+	},
+};
+
+static struct branch_clk gcc_pdm2_clk = {
+	.cbcr_reg = PDM2_CBCR,
+	.parent = &pdm2_clk_src.c,
+	.has_sibling = 0,
+	.base = &virt_bases[GCC_BASE],
+	.c = {
+		.dbg_name = "gcc_pdm2_clk",
+		.ops = &clk_ops_branch,
+		CLK_INIT(gcc_pdm2_clk.c),
+	},
+};
+
+static struct branch_clk gcc_pdm_ahb_clk = {
+	.cbcr_reg = PDM_AHB_CBCR,
+	.has_sibling = 1,
+	.base = &virt_bases[GCC_BASE],
+	.c = {
+		.dbg_name = "gcc_pdm_ahb_clk",
+		.ops = &clk_ops_branch,
+		CLK_INIT(gcc_pdm_ahb_clk.c),
+	},
+};
+
+static struct local_vote_clk gcc_prng_ahb_clk = {
+	.cbcr_reg = PRNG_AHB_CBCR,
+	.vote_reg = APCS_CLOCK_BRANCH_ENA_VOTE,
+	.en_mask = BIT(13),
+	.base = &virt_bases[GCC_BASE],
+	.c = {
+		.dbg_name = "gcc_prng_ahb_clk",
+		.ops = &clk_ops_vote,
+		CLK_INIT(gcc_prng_ahb_clk.c),
+	},
+};
+
+static struct branch_clk gcc_qpic_ahb_clk = {
+	.cbcr_reg = QPIC_AHB_CBCR,
+	.has_sibling = 1,
+	.base = &virt_bases[GCC_BASE],
+	.c = {
+		.dbg_name = "gcc_qpic_ahb_clk",
+		.ops = &clk_ops_branch,
+		CLK_INIT(gcc_qpic_ahb_clk.c),
+	},
+};
+
+static struct branch_clk gcc_qpic_clk = {
+	.cbcr_reg = QPIC_CBCR,
+	.parent = &qpic_clk_src.c,
+	.has_sibling = 0,
+	.base = &virt_bases[GCC_BASE],
+	.c = {
+		.dbg_name = "gcc_qpic_clk",
+		.ops = &clk_ops_branch,
+		CLK_INIT(gcc_qpic_clk.c),
+	},
+};
+
+static struct branch_clk gcc_sdcc2_ahb_clk = {
+	.cbcr_reg = SDCC2_AHB_CBCR,
+	.has_sibling = 1,
+	.base = &virt_bases[GCC_BASE],
+	.c = {
+		.dbg_name = "gcc_sdcc2_ahb_clk",
+		.ops = &clk_ops_branch,
+		CLK_INIT(gcc_sdcc2_ahb_clk.c),
+	},
+};
+
+static struct branch_clk gcc_sdcc2_apps_clk = {
+	.cbcr_reg = SDCC2_APPS_CBCR,
+	.parent = &sdcc2_apps_clk_src.c,
+	.has_sibling = 0,
+	.base = &virt_bases[GCC_BASE],
+	.c = {
+		.dbg_name = "gcc_sdcc2_apps_clk",
+		.ops = &clk_ops_branch,
+		CLK_INIT(gcc_sdcc2_apps_clk.c),
+	},
+};
+
+static struct branch_clk gcc_sdcc3_ahb_clk = {
+	.cbcr_reg = SDCC3_AHB_CBCR,
+	.has_sibling = 1,
+	.base = &virt_bases[GCC_BASE],
+	.c = {
+		.dbg_name = "gcc_sdcc3_ahb_clk",
+		.ops = &clk_ops_branch,
+		CLK_INIT(gcc_sdcc3_ahb_clk.c),
+	},
+};
+
+static struct branch_clk gcc_sdcc3_apps_clk = {
+	.cbcr_reg = SDCC3_APPS_CBCR,
+	.parent = &sdcc3_apps_clk_src.c,
+	.has_sibling = 0,
+	.base = &virt_bases[GCC_BASE],
+	.c = {
+		.dbg_name = "gcc_sdcc3_apps_clk",
+		.ops = &clk_ops_branch,
+		CLK_INIT(gcc_sdcc3_apps_clk.c),
+	},
+};
+
+static struct branch_clk gcc_sys_noc_ipa_axi_clk = {
+	.cbcr_reg = SYS_NOC_IPA_AXI_CBCR,
+	.parent = &ipa_clk_src.c,
+	.has_sibling = 1,
+	.base = &virt_bases[GCC_BASE],
+	.c = {
+		.dbg_name = "gcc_sys_noc_ipa_axi_clk",
+		.ops = &clk_ops_branch,
+		CLK_INIT(gcc_sys_noc_ipa_axi_clk.c),
+	},
+};
+
+static struct branch_clk gcc_usb_hs_ahb_clk = {
+	.cbcr_reg = USB_HS_AHB_CBCR,
+	.has_sibling = 1,
+	.base = &virt_bases[GCC_BASE],
+	.c = {
+		.dbg_name = "gcc_usb_hs_ahb_clk",
+		.ops = &clk_ops_branch,
+		CLK_INIT(gcc_usb_hs_ahb_clk.c),
+	},
+};
+
+static struct branch_clk gcc_usb_hs_system_clk = {
+	.cbcr_reg = USB_HS_SYSTEM_CBCR,
+	.bcr_reg = USB_HS_BCR,
+	.parent = &usb_hs_system_clk_src.c,
+	.has_sibling = 0,
+	.base = &virt_bases[GCC_BASE],
+	.c = {
+		.dbg_name = "gcc_usb_hs_system_clk",
+		.ops = &clk_ops_branch,
+		CLK_INIT(gcc_usb_hs_system_clk.c),
+	},
+};
+
+static struct branch_clk gcc_usb_hsic_ahb_clk = {
+	.cbcr_reg = USB_HSIC_AHB_CBCR,
+	.has_sibling = 1,
+	.base = &virt_bases[GCC_BASE],
+	.c = {
+		.dbg_name = "gcc_usb_hsic_ahb_clk",
+		.ops = &clk_ops_branch,
+		CLK_INIT(gcc_usb_hsic_ahb_clk.c),
+	},
+};
+
+static struct branch_clk gcc_usb_hsic_clk = {
+	.cbcr_reg = USB_HSIC_CBCR,
+	.parent = &usb_hsic_clk_src.c,
+	.has_sibling = 0,
+	.base = &virt_bases[GCC_BASE],
+	.c = {
+		.dbg_name = "gcc_usb_hsic_clk",
+		.ops = &clk_ops_branch,
+		CLK_INIT(gcc_usb_hsic_clk.c),
+	},
+};
+
+static struct branch_clk gcc_usb_hsic_io_cal_clk = {
+	.cbcr_reg = USB_HSIC_IO_CAL_CBCR,
+	.parent = &usb_hsic_io_cal_clk_src.c,
+	.has_sibling = 0,
+	.base = &virt_bases[GCC_BASE],
+	.c = {
+		.dbg_name = "gcc_usb_hsic_io_cal_clk",
+		.ops = &clk_ops_branch,
+		CLK_INIT(gcc_usb_hsic_io_cal_clk.c),
+	},
+};
+
+static struct branch_clk gcc_usb_hsic_system_clk = {
+	.cbcr_reg = USB_HSIC_SYSTEM_CBCR,
+	.bcr_reg = USB_HS_HSIC_BCR,
+	.parent = &usb_hsic_system_clk_src.c,
+	.has_sibling = 0,
+	.base = &virt_bases[GCC_BASE],
+	.c = {
+		.dbg_name = "gcc_usb_hsic_system_clk",
+		.ops = &clk_ops_branch,
+		CLK_INIT(gcc_usb_hsic_system_clk.c),
+	},
+};
+
+static struct branch_clk gcc_usb_hsic_xcvr_fs_clk = {
+	.cbcr_reg = USB_HSIC_XCVR_FS_CBCR,
+	.parent = &usb_hsic_xcvr_fs_clk_src.c,
+	.has_sibling = 0,
+	.base = &virt_bases[GCC_BASE],
+	.c = {
+		.dbg_name = "gcc_usb_hsic_xcvr_fs_clk",
+		.ops = &clk_ops_branch,
+		CLK_INIT(gcc_usb_hsic_xcvr_fs_clk.c),
+	},
+};
+
+/* LPASS clock data */
+static struct clk_freq_tbl ftbl_audio_core_lpaif_clock[] = {
+	F_LPASS(  512000,   lpapll0,   16,   1,   48),
+	F_LPASS(  768000,   lpapll0,   16,   1,   32),
+	F_LPASS( 1024000,   lpapll0,   16,   1,   24),
+	F_LPASS( 1536000,   lpapll0,   16,   1,   16),
+	F_LPASS( 2048000,   lpapll0,   16,   1,   12),
+	F_LPASS( 3072000,   lpapll0,   16,   1,    8),
+	F_LPASS( 4096000,   lpapll0,   16,   1,    6),
+	F_LPASS( 6144000,   lpapll0,   16,   1,    4),
+	F_LPASS( 8192000,   lpapll0,   16,   1,    3),
+	F_LPASS(12288000,   lpapll0,   16,   1,    2),
+	F_END
+};
+
+static struct clk_freq_tbl ftbl_audio_core_lpaif_pcm_clock[] = {
+	F_LPASS(  512000,   lpapll0,   16,   1,   48),
+	F_LPASS(  768000,   lpapll0,   16,   1,   32),
+	F_LPASS( 1024000,   lpapll0,   16,   1,   24),
+	F_LPASS( 1536000,   lpapll0,   16,   1,   16),
+	F_LPASS( 2048000,   lpapll0,   16,   1,   12),
+	F_LPASS( 3072000,   lpapll0,   16,   1,    8),
+	F_LPASS( 4096000,   lpapll0,   16,   1,    6),
+	F_LPASS( 6144000,   lpapll0,   16,   1,    4),
+	F_LPASS( 8192000,   lpapll0,   16,   1,    3),
+	F_END
+};
+
+static struct rcg_clk audio_core_lpaif_pcmoe_clk_src = {
+	.cmd_rcgr_reg =  LPAIF_PCMOE_CMD_RCGR,
+	.set_rate = set_rate_mnd,
+	.freq_tbl = ftbl_audio_core_lpaif_clock,
+	.current_freq = &rcg_dummy_freq,
+	.base = &virt_bases[LPASS_BASE],
+	.c = {
+		.dbg_name = "audio_core_lpaif_pcmoe_clk_src",
+		.ops = &clk_ops_rcg_mnd,
+		VDD_DIG_FMAX_MAP1(LOW, 12288000),
+		CLK_INIT(audio_core_lpaif_pcmoe_clk_src.c)
+	},
+};
+
+static struct rcg_clk audio_core_lpaif_pri_clk_src = {
+	.cmd_rcgr_reg =  LPAIF_PRI_CMD_RCGR,
+	.set_rate = set_rate_mnd,
+	.freq_tbl = ftbl_audio_core_lpaif_clock,
+	.current_freq = &rcg_dummy_freq,
+	.base = &virt_bases[LPASS_BASE],
+	.c = {
+		.dbg_name = "audio_core_lpaif_pri_clk_src",
+		.ops = &clk_ops_rcg_mnd,
+		VDD_DIG_FMAX_MAP2(LOW, 12288000, NOMINAL, 24576000),
+		CLK_INIT(audio_core_lpaif_pri_clk_src.c)
+	},
+};
+
+static struct rcg_clk audio_core_lpaif_sec_clk_src = {
+	.cmd_rcgr_reg =  LPAIF_SEC_CMD_RCGR,
+	.set_rate = set_rate_mnd,
+	.freq_tbl = ftbl_audio_core_lpaif_clock,
+	.current_freq = &rcg_dummy_freq,
+	.base = &virt_bases[LPASS_BASE],
+	.c = {
+		.dbg_name = "audio_core_lpaif_sec_clk_src",
+		.ops = &clk_ops_rcg_mnd,
+		VDD_DIG_FMAX_MAP2(LOW, 12288000, NOMINAL, 24576000),
+		CLK_INIT(audio_core_lpaif_sec_clk_src.c)
+	},
+};
+
+static struct clk_freq_tbl ftbl_audio_core_slimbus_core_clock[] = {
+	F_LPASS(26041000,   lpapll0,   1,   10,   151),
+	F_END
+};
+
+static struct rcg_clk audio_core_slimbus_core_clk_src = {
+	.cmd_rcgr_reg =  SLIMBUS_CMD_RCGR,
+	.set_rate = set_rate_mnd,
+	.freq_tbl = ftbl_audio_core_slimbus_core_clock,
+	.current_freq = &rcg_dummy_freq,
+	.base = &virt_bases[LPASS_BASE],
+	.c = {
+		.dbg_name = "audio_core_slimbus_core_clk_src",
+		.ops = &clk_ops_rcg_mnd,
+		VDD_DIG_FMAX_MAP2(LOW, 13107000, NOMINAL, 26214000),
+		CLK_INIT(audio_core_slimbus_core_clk_src.c)
+	},
+};
+
+static struct rcg_clk audio_core_lpaif_pcm0_clk_src = {
+	.cmd_rcgr_reg =  LPAIF_PCM0_CMD_RCGR,
+	.set_rate = set_rate_mnd,
+	.freq_tbl = ftbl_audio_core_lpaif_pcm_clock,
+	.current_freq = &rcg_dummy_freq,
+	.base = &virt_bases[LPASS_BASE],
+	.c = {
+		.dbg_name = "audio_core_lpaif_pcm0_clk_src",
+		.ops = &clk_ops_rcg_mnd,
+		VDD_DIG_FMAX_MAP2(LOW, 4096000, NOMINAL, 8192000),
+		CLK_INIT(audio_core_lpaif_pcm0_clk_src.c)
+	},
+};
+
+static struct rcg_clk audio_core_lpaif_pcm1_clk_src = {
+	.cmd_rcgr_reg =  LPAIF_PCM1_CMD_RCGR,
+	.set_rate = set_rate_mnd,
+	.freq_tbl = ftbl_audio_core_lpaif_pcm_clock,
+	.current_freq = &rcg_dummy_freq,
+	.base = &virt_bases[LPASS_BASE],
+	.c = {
+		.dbg_name = "audio_core_lpaif_pcm1_clk_src",
+		.ops = &clk_ops_rcg_mnd,
+		VDD_DIG_FMAX_MAP2(LOW, 4096000, NOMINAL, 8192000),
+		CLK_INIT(audio_core_lpaif_pcm1_clk_src.c)
+	},
+};
+
+static struct branch_clk audio_core_slimbus_lfabif_clk = {
+	.cbcr_reg = AUDIO_CORE_SLIMBUS_LFABIF_CBCR,
+	.has_sibling = 1,
+	.base = &virt_bases[LPASS_BASE],
+	.c = {
+		.dbg_name = "audio_core_slimbus_lfabif_clk",
+		.ops = &clk_ops_branch,
+		CLK_INIT(audio_core_slimbus_lfabif_clk.c),
+	},
+};
+
+static struct branch_clk audio_core_lpaif_pcm_data_oe_clk = {
+	.cbcr_reg = AUDIO_CORE_LPAIF_PCM_DATA_OE_CBCR,
+	.parent = &audio_core_lpaif_pcmoe_clk_src.c,
+	.base = &virt_bases[LPASS_BASE],
+	.c = {
+		.dbg_name = "audio_core_lpaif_pcm_data_oe_clk",
+		.ops = &clk_ops_branch,
+		CLK_INIT(audio_core_lpaif_pcm_data_oe_clk.c),
+	},
+};
+
+static struct branch_clk audio_core_slimbus_core_clk = {
+	.cbcr_reg = AUDIO_CORE_SLIMBUS_CORE_CBCR,
+	.parent = &audio_core_slimbus_core_clk_src.c,
+	.base = &virt_bases[LPASS_BASE],
+	.c = {
+		.dbg_name = "audio_core_slimbus_core_clk",
+		.ops = &clk_ops_branch,
+		CLK_INIT(audio_core_slimbus_core_clk.c),
+	},
+};
+
+static struct branch_clk audio_core_lpaif_pri_ebit_clk = {
+	.cbcr_reg = AUDIO_CORE_LPAIF_PRI_EBIT_CBCR,
+	.has_sibling = 0,
+	.base = &virt_bases[LPASS_BASE],
+	.c = {
+		.dbg_name = "audio_core_lpaif_pri_ebit_clk",
+		.ops = &clk_ops_branch,
+		CLK_INIT(audio_core_lpaif_pri_ebit_clk.c),
+	},
+};
+
+static struct branch_clk audio_core_lpaif_pri_ibit_clk = {
+	.cbcr_reg = AUDIO_CORE_LPAIF_PRI_IBIT_CBCR,
+	.parent = &audio_core_lpaif_pri_clk_src.c,
+	.has_sibling = 1,
+	.max_div = 15,
+	.base = &virt_bases[LPASS_BASE],
+	.c = {
+		.dbg_name = "audio_core_lpaif_pri_ibit_clk",
+		.ops = &clk_ops_branch,
+		CLK_INIT(audio_core_lpaif_pri_ibit_clk.c),
+	},
+};
+
+static struct branch_clk audio_core_lpaif_pri_osr_clk = {
+	.cbcr_reg = AUDIO_CORE_LPAIF_PRI_OSR_CBCR,
+	.parent = &audio_core_lpaif_pri_clk_src.c,
+	.has_sibling = 1,
+	.base = &virt_bases[LPASS_BASE],
+	.c = {
+		.dbg_name = "audio_core_lpaif_pri_osr_clk",
+		.ops = &clk_ops_branch,
+		CLK_INIT(audio_core_lpaif_pri_osr_clk.c),
+	},
+};
+
+static struct branch_clk audio_core_lpaif_pcm0_ebit_clk = {
+	.cbcr_reg = AUDIO_CORE_LPAIF_PCM0_EBIT_CBCR,
+	.has_sibling = 0,
+	.base = &virt_bases[LPASS_BASE],
+	.c = {
+		.dbg_name = "audio_core_lpaif_pcm0_ebit_clk",
+		.ops = &clk_ops_branch,
+		CLK_INIT(audio_core_lpaif_pcm0_ebit_clk.c),
+	},
+};
+
+static struct branch_clk audio_core_lpaif_pcm0_ibit_clk = {
+	.cbcr_reg = AUDIO_CORE_LPAIF_PCM0_IBIT_CBCR,
+	.parent = &audio_core_lpaif_pcm0_clk_src.c,
+	.has_sibling = 0,
+	.base = &virt_bases[LPASS_BASE],
+	.c = {
+		.dbg_name = "audio_core_lpaif_pcm0_ibit_clk",
+		.ops = &clk_ops_branch,
+		CLK_INIT(audio_core_lpaif_pcm0_ibit_clk.c),
+	},
+};
+
+static struct branch_clk audio_core_lpaif_sec_ebit_clk = {
+	.cbcr_reg = AUDIO_CORE_LPAIF_SEC_EBIT_CBCR,
+	.has_sibling = 0,
+	.base = &virt_bases[LPASS_BASE],
+	.c = {
+		.dbg_name = "audio_core_lpaif_sec_ebit_clk",
+		.ops = &clk_ops_branch,
+		CLK_INIT(audio_core_lpaif_sec_ebit_clk.c),
+	},
+};
+
+static struct branch_clk audio_core_lpaif_sec_ibit_clk = {
+	.cbcr_reg = AUDIO_CORE_LPAIF_SEC_IBIT_CBCR,
+	.parent = &audio_core_lpaif_sec_clk_src.c,
+	.has_sibling = 1,
+	.max_div = 15,
+	.base = &virt_bases[LPASS_BASE],
+	.c = {
+		.dbg_name = "audio_core_lpaif_sec_ibit_clk",
+		.ops = &clk_ops_branch,
+		CLK_INIT(audio_core_lpaif_sec_ibit_clk.c),
+	},
+};
+
+static struct branch_clk audio_core_lpaif_sec_osr_clk = {
+	.cbcr_reg = AUDIO_CORE_LPAIF_SEC_OSR_CBCR,
+	.parent = &audio_core_lpaif_sec_clk_src.c,
+	.has_sibling = 1,
+	.base = &virt_bases[LPASS_BASE],
+	.c = {
+		.dbg_name = "audio_core_lpaif_sec_osr_clk",
+		.ops = &clk_ops_branch,
+		CLK_INIT(audio_core_lpaif_sec_osr_clk.c),
+	},
+};
+
+static struct branch_clk audio_core_lpaif_pcm1_ebit_clk = {
+	.cbcr_reg = AUDIO_CORE_LPAIF_PCM1_EBIT_CBCR,
+	.has_sibling = 0,
+	.base = &virt_bases[LPASS_BASE],
+	.c = {
+		.dbg_name = "audio_core_lpaif_pcm1_ebit_clk",
+		.ops = &clk_ops_branch,
+		CLK_INIT(audio_core_lpaif_pcm1_ebit_clk.c),
+	},
+};
+
+static struct branch_clk audio_core_lpaif_pcm1_ibit_clk = {
+	.cbcr_reg = AUDIO_CORE_LPAIF_PCM1_IBIT_CBCR,
+	.parent = &audio_core_lpaif_pcm1_clk_src.c,
+	.has_sibling = 0,
+	.base = &virt_bases[LPASS_BASE],
+	.c = {
+		.dbg_name = "audio_core_lpaif_pcm1_ibit_clk",
+		.ops = &clk_ops_branch,
+		CLK_INIT(audio_core_lpaif_pcm1_ibit_clk.c),
+	},
+};
+
+static DEFINE_CLK_MEASURE(a5_m_clk);
+
+#ifdef CONFIG_DEBUG_FS
+
+struct measure_mux_entry {
+	struct clk *c;
+	int base;
+	u32 debug_mux;
+};
+
+struct measure_mux_entry measure_mux[] = {
+	{&gcc_pdm_ahb_clk.c,			GCC_BASE, 0x00d0},
+	{&gcc_usb_hsic_xcvr_fs_clk.c,		GCC_BASE, 0x005d},
+	{&gcc_usb_hsic_system_clk.c,		GCC_BASE, 0x0059},
+	{&gcc_usb_hsic_io_cal_clk.c,		GCC_BASE, 0x005b},
+	{&gcc_sdcc3_ahb_clk.c,			GCC_BASE, 0x0079},
+	{&gcc_blsp1_qup5_i2c_apps_clk.c,	GCC_BASE, 0x009d},
+	{&gcc_blsp1_qup1_spi_apps_clk.c,	GCC_BASE, 0x008a},
+	{&gcc_blsp1_uart2_apps_clk.c,		GCC_BASE, 0x0091},
+	{&gcc_blsp1_qup4_spi_apps_clk.c,	GCC_BASE, 0x0098},
+	{&gcc_blsp1_qup3_spi_apps_clk.c,	GCC_BASE, 0x0093},
+	{&gcc_blsp1_qup6_i2c_apps_clk.c,	GCC_BASE, 0x00a2},
+	{&gcc_bam_dma_ahb_clk.c,		GCC_BASE, 0x00e0},
+	{&gcc_sdcc3_apps_clk.c,			GCC_BASE, 0x0078},
+	{&gcc_usb_hs_system_clk.c,		GCC_BASE, 0x0060},
+	{&gcc_blsp1_ahb_clk.c,			GCC_BASE, 0x0088},
+	{&gcc_blsp1_uart4_apps_clk.c,		GCC_BASE, 0x009a},
+	{&gcc_blsp1_qup2_spi_apps_clk.c,	GCC_BASE, 0x008e},
+	{&gcc_usb_hsic_ahb_clk.c,		GCC_BASE, 0x0058},
+	{&gcc_blsp1_uart3_apps_clk.c,		GCC_BASE, 0x0095},
+	{&gcc_ce1_axi_clk.c,			GCC_BASE, 0x0139},
+	{&gcc_blsp1_qup5_spi_apps_clk.c,	GCC_BASE, 0x009c},
+	{&gcc_usb_hs_ahb_clk.c,			GCC_BASE, 0x0061},
+	{&gcc_blsp1_qup6_spi_apps_clk.c,	GCC_BASE, 0x00a1},
+	{&gcc_prng_ahb_clk.c,			GCC_BASE, 0x00d8},
+	{&gcc_blsp1_qup3_i2c_apps_clk.c,	GCC_BASE, 0x0094},
+	{&gcc_usb_hsic_clk.c,			GCC_BASE, 0x005a},
+	{&gcc_blsp1_uart6_apps_clk.c,		GCC_BASE, 0x00a3},
+	{&gcc_sdcc2_apps_clk.c,			GCC_BASE, 0x0070},
+	{&gcc_blsp1_uart1_apps_clk.c,		GCC_BASE, 0x008c},
+	{&gcc_blsp1_qup4_i2c_apps_clk.c,	GCC_BASE, 0x0099},
+	{&gcc_boot_rom_ahb_clk.c,		GCC_BASE, 0x00f8},
+	{&gcc_ce1_ahb_clk.c,			GCC_BASE, 0x013a},
+	{&gcc_pdm2_clk.c,			GCC_BASE, 0x00d2},
+	{&gcc_blsp1_uart5_apps_clk.c,		GCC_BASE, 0x009e},
+	{&gcc_blsp1_qup2_i2c_apps_clk.c,	GCC_BASE, 0x0090},
+	{&gcc_blsp1_qup1_i2c_apps_clk.c,	GCC_BASE, 0x008b},
+	{&gcc_sdcc2_ahb_clk.c,			GCC_BASE, 0x0071},
+	{&gcc_ce1_clk.c,			GCC_BASE, 0x0138},
+	{&gcc_sys_noc_ipa_axi_clk.c,		GCC_BASE, 0x0007},
+
+	{&audio_core_lpaif_pcm_data_oe_clk.c,	LPASS_BASE, 0x0030},
+	{&audio_core_slimbus_core_clk.c,	LPASS_BASE, 0x003d},
+	{&audio_core_lpaif_pri_clk_src.c,	LPASS_BASE, 0x0017},
+	{&audio_core_lpaif_sec_clk_src.c,	LPASS_BASE, 0x0016},
+	{&audio_core_slimbus_core_clk_src.c,	LPASS_BASE, 0x0011},
+	{&audio_core_lpaif_pcm1_clk_src.c,	LPASS_BASE, 0x0012},
+	{&audio_core_lpaif_pcm0_clk_src.c,	LPASS_BASE, 0x0013},
+	{&audio_core_lpaif_pcmoe_clk_src.c,	LPASS_BASE, 0x000f},
+	{&audio_core_slimbus_lfabif_clk.c,	LPASS_BASE, 0x003e},
+
+	{&a5_m_clk,				APCS_BASE, 0x3},
+
+	{&dummy_clk,				N_BASES,    0x0000},
+};
+
+static int measure_clk_set_parent(struct clk *c, struct clk *parent)
+{
+	struct measure_clk *clk = to_measure_clk(c);
+	unsigned long flags;
+	u32 regval, clk_sel, i;
+
+	if (!parent)
+		return -EINVAL;
+
+	for (i = 0; i < (ARRAY_SIZE(measure_mux) - 1); i++)
+		if (measure_mux[i].c == parent)
+			break;
+
+	if (measure_mux[i].c == &dummy_clk)
+		return -EINVAL;
+
+	spin_lock_irqsave(&local_clock_reg_lock, flags);
+	/*
+	 * Program the test vector, measurement period (sample_ticks)
+	 * and scaling multiplier.
+	 */
+	clk->sample_ticks = 0x10000;
+	clk->multiplier = 1;
+
+	writel_relaxed(0, LPASS_REG_BASE(LPASS_DEBUG_CLK_CTL_REG));
+	writel_relaxed(0, GCC_REG_BASE(GCC_DEBUG_CLK_CTL_REG));
+
+	switch (measure_mux[i].base) {
+
+	case GCC_BASE:
+		clk_sel = measure_mux[i].debug_mux;
+		break;
+
+	case LPASS_BASE:
+		clk_sel = 0x161;
+		regval = BVAL(15, 0, measure_mux[i].debug_mux);
+		writel_relaxed(regval, LPASS_REG_BASE(LPASS_DEBUG_CLK_CTL_REG));
+
+		/* Activate debug clock output */
+		regval |= BIT(20);
+		writel_relaxed(regval, LPASS_REG_BASE(LPASS_DEBUG_CLK_CTL_REG));
+		break;
+
+	case APCS_BASE:
+		clk_sel = 0x16A;
+		regval = BVAL(5, 3, measure_mux[i].debug_mux);
+		writel_relaxed(regval, APCS_REG_BASE(APCS_CLK_DIAG_REG));
+
+		/* Activate debug clock output */
+		regval |= BIT(7);
+		writel_relaxed(regval, APCS_REG_BASE(APCS_CLK_DIAG_REG));
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	/* Set debug mux clock index */
+	regval = BVAL(8, 0, clk_sel);
+	writel_relaxed(regval, GCC_REG_BASE(GCC_DEBUG_CLK_CTL_REG));
+
+	/* Activate debug clock output */
+	regval |= BIT(16);
+	writel_relaxed(regval, GCC_REG_BASE(GCC_DEBUG_CLK_CTL_REG));
+
+	/* Make sure test vector is set before starting measurements. */
+	mb();
+	spin_unlock_irqrestore(&local_clock_reg_lock, flags);
+
+	return 0;
+}
+
+/* Sample clock for 'ticks' reference clock ticks. */
+static u32 run_measurement(unsigned ticks)
+{
+	/* Stop counters and set the XO4 counter start value. */
+	writel_relaxed(ticks, GCC_REG_BASE(CLOCK_FRQ_MEASURE_CTL_REG));
+
+	/* Wait for timer to become ready. */
+	while ((readl_relaxed(GCC_REG_BASE(CLOCK_FRQ_MEASURE_STATUS_REG)) &
+			BIT(25)) != 0)
+		cpu_relax();
+
+	/* Run measurement and wait for completion. */
+	writel_relaxed(BIT(20)|ticks, GCC_REG_BASE(CLOCK_FRQ_MEASURE_CTL_REG));
+	while ((readl_relaxed(GCC_REG_BASE(CLOCK_FRQ_MEASURE_STATUS_REG)) &
+			BIT(25)) == 0)
+		cpu_relax();
+
+	/* Return measured ticks. */
+	return readl_relaxed(GCC_REG_BASE(CLOCK_FRQ_MEASURE_STATUS_REG)) &
+				BM(24, 0);
+}
+
+/*
+ * Perform a hardware rate measurement for a given clock.
+ * FOR DEBUG USE ONLY: Measurements take ~15 ms!
+ */
+static unsigned long measure_clk_get_rate(struct clk *c)
+{
+	unsigned long flags;
+	u32 gcc_xo4_reg_backup;
+	u64 raw_count_short, raw_count_full;
+	struct measure_clk *clk = to_measure_clk(c);
+	unsigned ret;
+
+	ret = clk_prepare_enable(&cxo_clk_src.c);
+	if (ret) {
+		pr_warning("CXO clock failed to enable. Can't measure\n");
+		return 0;
+	}
+
+	spin_lock_irqsave(&local_clock_reg_lock, flags);
+
+	/* Enable CXO/4 and RINGOSC branch. */
+	gcc_xo4_reg_backup = readl_relaxed(GCC_REG_BASE(GCC_XO_DIV4_CBCR_REG));
+	writel_relaxed(0x1, GCC_REG_BASE(GCC_XO_DIV4_CBCR_REG));
+
+	/*
+	 * The ring oscillator counter will not reset if the measured clock
+	 * is not running.  To detect this, run a short measurement before
+	 * the full measurement.  If the raw results of the two are the same
+	 * then the clock must be off.
+	 */
+
+	/* Run a short measurement. (~1 ms) */
+	raw_count_short = run_measurement(0x1000);
+	/* Run a full measurement. (~14 ms) */
+	raw_count_full = run_measurement(clk->sample_ticks);
+
+	writel_relaxed(gcc_xo4_reg_backup, GCC_REG_BASE(GCC_XO_DIV4_CBCR_REG));
+
+	/* Return 0 if the clock is off. */
+	if (raw_count_full == raw_count_short) {
+		ret = 0;
+	} else {
+		/* Compute rate in Hz. */
+		raw_count_full = ((raw_count_full * 10) + 15) * 4800000;
+		do_div(raw_count_full, ((clk->sample_ticks * 10) + 35));
+		ret = (raw_count_full * clk->multiplier);
+	}
+
+	writel_relaxed(0x51A00, GCC_REG_BASE(GCC_PLLTEST_PAD_CFG_REG));
+	spin_unlock_irqrestore(&local_clock_reg_lock, flags);
+
+	clk_disable_unprepare(&cxo_clk_src.c);
+
+	return ret;
+}
+#else /* !CONFIG_DEBUG_FS */
+static int measure_clk_set_parent(struct clk *clk, struct clk *parent)
+{
+	return -EINVAL;
+}
+
+static unsigned long measure_clk_get_rate(struct clk *clk)
+{
+	return 0;
+}
+#endif /* CONFIG_DEBUG_FS */
+
+static struct clk_ops clk_ops_measure = {
+	.set_parent = measure_clk_set_parent,
+	.get_rate = measure_clk_get_rate,
+};
+
+static struct measure_clk measure_clk = {
+	.c = {
+		.dbg_name = "measure_clk",
+		.ops = &clk_ops_measure,
+		CLK_INIT(measure_clk.c),
+	},
+	.multiplier = 1,
+};
+
+static struct clk_lookup msm_clocks_9625[] = {
+	CLK_LOOKUP("xo",	cxo_clk_src.c,	""),
+	CLK_LOOKUP("measure",	measure_clk.c,	"debug"),
+
+	CLK_LOOKUP("pll0", gpll0_activeonly_clk_src.c, "f9010008.qcom,acpuclk"),
+	CLK_LOOKUP("pll14", apcspll_clk_src.c, "f9010008.qcom,acpuclk"),
+
+	CLK_LOOKUP("dma_bam_pclk", gcc_bam_dma_ahb_clk.c, "msm_sps"),
+	CLK_LOOKUP("iface_clk", gcc_blsp1_ahb_clk.c, "msm_serial_hsl.0"),
+	CLK_LOOKUP("iface_clk", gcc_blsp1_ahb_clk.c, "spi_qsd.1"),
+	CLK_LOOKUP("iface_clk", gcc_blsp1_ahb_clk.c, "f9925000.i2c"),
+	CLK_LOOKUP("iface_clk", gcc_blsp1_ahb_clk.c, ""),
+	CLK_LOOKUP("core_clk", gcc_blsp1_qup1_i2c_apps_clk.c, ""),
+	CLK_LOOKUP("core_clk", gcc_blsp1_qup1_spi_apps_clk.c, "spi_qsd.1"),
+	CLK_LOOKUP("core_clk", gcc_blsp1_qup2_i2c_apps_clk.c, ""),
+	CLK_LOOKUP("core_clk", gcc_blsp1_qup2_spi_apps_clk.c, ""),
+	CLK_LOOKUP("core_clk", gcc_blsp1_qup3_i2c_apps_clk.c, "f9925000.i2c"),
+	CLK_LOOKUP("core_clk", gcc_blsp1_qup3_spi_apps_clk.c, ""),
+	CLK_LOOKUP("core_clk", gcc_blsp1_qup4_i2c_apps_clk.c, ""),
+	CLK_LOOKUP("core_clk", gcc_blsp1_qup4_spi_apps_clk.c, ""),
+	CLK_LOOKUP("core_clk", gcc_blsp1_qup5_i2c_apps_clk.c, ""),
+	CLK_LOOKUP("core_clk", gcc_blsp1_qup5_spi_apps_clk.c, ""),
+	CLK_LOOKUP("core_clk", gcc_blsp1_qup6_i2c_apps_clk.c, ""),
+	CLK_LOOKUP("core_clk", gcc_blsp1_qup6_spi_apps_clk.c, ""),
+	CLK_LOOKUP("core_clk", gcc_blsp1_uart1_apps_clk.c, ""),
+	CLK_LOOKUP("core_clk", gcc_blsp1_uart2_apps_clk.c, ""),
+	CLK_LOOKUP("core_clk", gcc_blsp1_uart3_apps_clk.c, "msm_serial_hsl.0"),
+	CLK_LOOKUP("core_clk", gcc_blsp1_uart4_apps_clk.c, ""),
+	CLK_LOOKUP("core_clk", gcc_blsp1_uart5_apps_clk.c, ""),
+	CLK_LOOKUP("core_clk", gcc_blsp1_uart6_apps_clk.c, ""),
+
+	CLK_LOOKUP("core_clk_src", ce1_clk_src.c, ""),
+	CLK_LOOKUP("core_clk", gcc_ce1_clk.c, ""),
+	CLK_LOOKUP("iface_clk", gcc_ce1_ahb_clk.c, ""),
+	CLK_LOOKUP("bus_clk", gcc_ce1_axi_clk.c, ""),
+
+	CLK_LOOKUP("core_clk", gcc_gp1_clk.c, ""),
+	CLK_LOOKUP("core_clk", gcc_gp2_clk.c, ""),
+	CLK_LOOKUP("core_clk", gcc_gp3_clk.c, ""),
+
+	CLK_LOOKUP("core_src_clk", ipa_clk_src.c, "fd4c0000.qcom,ipa"),
+	CLK_LOOKUP("core_clk", gcc_ipa_clk.c, "fd4c0000.qcom,ipa"),
+	CLK_LOOKUP("bus_clk",  gcc_sys_noc_ipa_axi_clk.c, "fd4c0000.qcom,ipa"),
+	CLK_LOOKUP("iface_clk",  gcc_ipa_cnoc_clk.c, "fd4c0000.qcom,ipa"),
+
+	CLK_LOOKUP("core_clk", gcc_pdm2_clk.c, ""),
+	CLK_LOOKUP("iface_clk", gcc_pdm_ahb_clk.c, ""),
+
+	CLK_LOOKUP("iface_clk", gcc_sdcc2_ahb_clk.c, "msm_sdcc.2"),
+	CLK_LOOKUP("core_clk", gcc_sdcc2_apps_clk.c, "msm_sdcc.2"),
+	CLK_LOOKUP("bus_clk",  pnoc_sdcc2_clk.c, "msm_sdcc.2"),
+	CLK_LOOKUP("iface_clk", gcc_sdcc3_ahb_clk.c, "msm_sdcc.3"),
+	CLK_LOOKUP("core_clk", gcc_sdcc3_apps_clk.c, "msm_sdcc.3"),
+	CLK_LOOKUP("bus_clk", pnoc_sdcc3_clk.c, "msm_sdcc.3"),
+
+	CLK_LOOKUP("iface_clk", gcc_usb_hs_ahb_clk.c,     "f9a55000.usb"),
+	CLK_LOOKUP("core_clk", gcc_usb_hs_system_clk.c,   "f9a55000.usb"),
+	CLK_LOOKUP("iface_clk", gcc_usb_hsic_ahb_clk.c,	  "f9a15000.hsic"),
+	CLK_LOOKUP("phy_clk", gcc_usb_hsic_clk.c,	  "f9a15000.hsic"),
+	CLK_LOOKUP("cal_clk", gcc_usb_hsic_io_cal_clk.c,  "f9a15000.hsic"),
+	CLK_LOOKUP("core_clk", gcc_usb_hsic_system_clk.c, "f9a15000.hsic"),
+	CLK_LOOKUP("alt_core_clk", gcc_usb_hsic_xcvr_fs_clk.c,
+							  "f9a15000.hsic"),
+
+	/* LPASS clocks */
+	CLK_LOOKUP("core_clk", audio_core_slimbus_core_clk.c, "fe12f000.slim"),
+	CLK_LOOKUP("iface_clk", audio_core_slimbus_lfabif_clk.c, ""),
+	CLK_LOOKUP("core_clk", audio_core_lpaif_pri_clk_src.c, ""),
+	CLK_LOOKUP("osr_clk", audio_core_lpaif_pri_osr_clk.c, ""),
+	CLK_LOOKUP("ebit_clk", audio_core_lpaif_pri_ebit_clk.c, ""),
+	CLK_LOOKUP("ibit_clk", audio_core_lpaif_pri_ibit_clk.c, ""),
+	CLK_LOOKUP("core_clk", audio_core_lpaif_sec_clk_src.c, ""),
+	CLK_LOOKUP("osr_clk", audio_core_lpaif_sec_osr_clk.c, ""),
+	CLK_LOOKUP("ebit_clk", audio_core_lpaif_sec_ebit_clk.c, ""),
+	CLK_LOOKUP("ibit_clk", audio_core_lpaif_sec_ibit_clk.c, ""),
+	CLK_LOOKUP("core_clk", audio_core_lpaif_pcm0_clk_src.c, ""),
+	CLK_LOOKUP("ebit_clk", audio_core_lpaif_pcm0_ebit_clk.c, ""),
+	CLK_LOOKUP("ibit_clk", audio_core_lpaif_pcm0_ibit_clk.c, ""),
+	CLK_LOOKUP("core_clk", audio_core_lpaif_pcm1_clk_src.c, ""),
+	CLK_LOOKUP("ebit_clk", audio_core_lpaif_pcm1_ebit_clk.c, ""),
+	CLK_LOOKUP("ibit_clk", audio_core_lpaif_pcm1_ibit_clk.c, ""),
+	CLK_LOOKUP("core_oe_src_clk", audio_core_lpaif_pcmoe_clk_src.c, ""),
+	CLK_LOOKUP("core_oe_clk", audio_core_lpaif_pcm_data_oe_clk.c, ""),
+
+	/* RPM and voter clocks */
+	CLK_LOOKUP("bus_clk", snoc_clk.c, ""),
+	CLK_LOOKUP("bus_clk", pnoc_clk.c, ""),
+	CLK_LOOKUP("bus_clk", cnoc_clk.c, ""),
+	CLK_LOOKUP("mem_clk", bimc_clk.c, ""),
+	CLK_LOOKUP("bus_clk", snoc_a_clk.c, ""),
+	CLK_LOOKUP("bus_clk", pnoc_a_clk.c, ""),
+	CLK_LOOKUP("bus_clk", cnoc_a_clk.c, ""),
+	CLK_LOOKUP("mem_clk", bimc_a_clk.c, ""),
+
+	CLK_LOOKUP("bus_clk",	cnoc_msmbus_clk.c,	"msm_config_noc"),
+	CLK_LOOKUP("bus_a_clk",	cnoc_msmbus_a_clk.c,	"msm_config_noc"),
+	CLK_LOOKUP("bus_clk",	snoc_msmbus_clk.c,	"msm_sys_noc"),
+	CLK_LOOKUP("bus_a_clk",	snoc_msmbus_a_clk.c,	"msm_sys_noc"),
+	CLK_LOOKUP("bus_clk",	pnoc_msmbus_clk.c,	"msm_periph_noc"),
+	CLK_LOOKUP("bus_a_clk",	pnoc_msmbus_a_clk.c,	"msm_periph_noc"),
+	CLK_LOOKUP("mem_clk",	bimc_msmbus_clk.c,	"msm_bimc"),
+	CLK_LOOKUP("mem_a_clk",	bimc_msmbus_a_clk.c,	"msm_bimc"),
+
+	CLK_LOOKUP("dfab_clk", pnoc_sps_clk.c, "msm_sps"),
+
+	CLK_LOOKUP("a5_m_clk", a5_m_clk, ""),
+};
+
+static struct pll_config_regs gpll0_regs __initdata = {
+	.l_reg = (void __iomem *)GPLL0_L_REG,
+	.m_reg = (void __iomem *)GPLL0_M_REG,
+	.n_reg = (void __iomem *)GPLL0_N_REG,
+	.config_reg = (void __iomem *)GPLL0_USER_CTL_REG,
+	.mode_reg = (void __iomem *)GPLL0_MODE_REG,
+	.base = &virt_bases[GCC_BASE],
+};
+
+/* GPLL0 at 600 MHz, main output enabled. */
+static struct pll_config gpll0_config __initdata = {
+	.l = 0x1f,
+	.m = 0x1,
+	.n = 0x4,
+	.vco_val = 0x0,
+	.vco_mask = BM(21, 20),
+	.pre_div_val = 0x0,
+	.pre_div_mask = BM(14, 12),
+	.post_div_val = 0x0,
+	.post_div_mask = BM(9, 8),
+	.mn_ena_val = BIT(24),
+	.mn_ena_mask = BIT(24),
+	.main_output_val = BIT(0),
+	.main_output_mask = BIT(0),
+};
+
+static struct pll_config_regs gpll1_regs __initdata = {
+	.l_reg = (void __iomem *)GPLL1_L_REG,
+	.m_reg = (void __iomem *)GPLL1_M_REG,
+	.n_reg = (void __iomem *)GPLL1_N_REG,
+	.config_reg = (void __iomem *)GPLL1_USER_CTL_REG,
+	.mode_reg = (void __iomem *)GPLL1_MODE_REG,
+	.base = &virt_bases[GCC_BASE],
+};
+
+/* GPLL1 at 480 MHz, main output enabled. */
+static struct pll_config gpll1_config __initdata = {
+	.l = 0x19,
+	.m = 0x0,
+	.n = 0x1,
+	.vco_val = 0x0,
+	.vco_mask = BM(21, 20),
+	.pre_div_val = 0x0,
+	.pre_div_mask = BM(14, 12),
+	.post_div_val = 0x0,
+	.post_div_mask = BM(9, 8),
+	.main_output_val = BIT(0),
+	.main_output_mask = BIT(0),
+};
+
+static struct pll_config_regs lpapll0_regs __initdata = {
+	.l_reg = (void __iomem *)LPAPLL_L_REG,
+	.m_reg = (void __iomem *)LPAPLL_M_REG,
+	.n_reg = (void __iomem *)LPAPLL_N_REG,
+	.config_reg = (void __iomem *)LPAPLL_USER_CTL_REG,
+	.mode_reg = (void __iomem *)LPAPLL_MODE_REG,
+	.base = &virt_bases[LPASS_BASE],
+};
+
+/* LPAPLL0 at 393.216 MHz, main output enabled. */
+static struct pll_config lpapll0_config __initdata = {
+	.l = 0x28,
+	.m = 0x18,
+	.n = 0x19,
+	.vco_val = 0x0,
+	.vco_mask = BM(21, 20),
+	.pre_div_val = 0x0,
+	.pre_div_mask = BM(14, 12),
+	.post_div_val = BVAL(9, 8, 0x1),
+	.post_div_mask = BM(9, 8),
+	.mn_ena_val = BIT(24),
+	.mn_ena_mask = BIT(24),
+	.main_output_val = BIT(0),
+	.main_output_mask = BIT(0),
+};
+
+#define PLL_AUX_OUTPUT_BIT 1
+#define PLL_AUX2_OUTPUT_BIT 2
+
+/*
+ * TODO: Need to remove this function when the v2 hardware
+ * fix the broken lock status bit.
+ */
+#define PLL_OUTCTRL BIT(0)
+#define PLL_BYPASSNL BIT(1)
+#define PLL_RESET_N BIT(2)
+
+static DEFINE_SPINLOCK(sr_pll_reg_lock);
+
+static int sr_pll_clk_enable_9625(struct clk *c)
+{
+	unsigned long flags;
+	struct pll_clk *pll = to_pll_clk(c);
+	u32 mode;
+	void __iomem *mode_reg = *pll->base + (u32)pll->mode_reg;
+
+	spin_lock_irqsave(&sr_pll_reg_lock, flags);
+
+	/* Disable PLL bypass mode and de-assert reset. */
+	mode = readl_relaxed(mode_reg);
+	mode |= PLL_BYPASSNL | PLL_RESET_N;
+	writel_relaxed(mode, mode_reg);
+
+	/* Wait for pll to lock. */
+	udelay(100);
+
+	/* Enable PLL output. */
+	mode |= PLL_OUTCTRL;
+	writel_relaxed(mode, mode_reg);
+
+	/* Ensure the write above goes through before returning. */
+	mb();
+
+	spin_unlock_irqrestore(&sr_pll_reg_lock, flags);
+	return 0;
+}
+
+static void __init configure_apcs_pll(void)
+{
+	u32 regval;
+
+	clk_set_rate(&apcspll_clk_src.c, 998400000);
+
+	writel_relaxed(0x00141200,
+			APCS_PLL_REG_BASE(APCS_CPU_PLL_CONFIG_CTL_REG));
+
+	/* Enable AUX and AUX2 output */
+	regval = readl_relaxed(APCS_PLL_REG_BASE(APCS_CPU_PLL_USER_CTL_REG));
+	regval |= BIT(PLL_AUX_OUTPUT_BIT) | BIT(PLL_AUX2_OUTPUT_BIT);
+	writel_relaxed(regval, APCS_PLL_REG_BASE(APCS_CPU_PLL_USER_CTL_REG));
+}
+
+#define PWR_ON_MASK		BIT(31)
+#define EN_REST_WAIT_MASK	(0xF << 20)
+#define EN_FEW_WAIT_MASK	(0xF << 16)
+#define CLK_DIS_WAIT_MASK	(0xF << 12)
+#define SW_OVERRIDE_MASK	BIT(2)
+#define HW_CONTROL_MASK		BIT(1)
+#define SW_COLLAPSE_MASK	BIT(0)
+
+/* Wait 2^n CXO cycles between all states. Here, n=2 (4 cycles). */
+#define EN_REST_WAIT_VAL	(0x2 << 20)
+#define EN_FEW_WAIT_VAL		(0x2 << 16)
+#define CLK_DIS_WAIT_VAL	(0x2 << 12)
+#define GDSC_TIMEOUT_US		50000
+
+static void __init reg_init(void)
+{
+	u32 regval, status;
+	int ret;
+
+	if (!(readl_relaxed(GCC_REG_BASE(GPLL0_STATUS_REG))
+			& gpll0_clk_src.status_mask))
+		configure_sr_hpm_lp_pll(&gpll0_config, &gpll0_regs, 1);
+
+	if (!(readl_relaxed(GCC_REG_BASE(GPLL1_STATUS_REG))
+			& gpll1_clk_src.status_mask))
+		configure_sr_hpm_lp_pll(&gpll1_config, &gpll1_regs, 1);
+
+	configure_sr_hpm_lp_pll(&lpapll0_config, &lpapll0_regs, 1);
+
+	/* TODO: Remove A5 pll configuration once the bootloader is avaiable */
+	regval = readl_relaxed(APCS_PLL_REG_BASE(APCS_CPU_PLL_MODE_REG));
+	if ((regval & BM(2, 0)) != 0x7)
+		configure_apcs_pll();
+
+	/* TODO:
+	 * 1) do we need to turn on AUX2 output too?
+	 * 2) if need to vote off all sleep clocks
+	 */
+
+	/* Enable GPLL0's aux outputs. */
+	regval = readl_relaxed(GCC_REG_BASE(GPLL0_USER_CTL_REG));
+	regval |= BIT(PLL_AUX_OUTPUT_BIT) | BIT(PLL_AUX2_OUTPUT_BIT);
+	writel_relaxed(regval, GCC_REG_BASE(GPLL0_USER_CTL_REG));
+
+	/* Vote for GPLL0 to turn on. Needed by acpuclock. */
+	regval = readl_relaxed(GCC_REG_BASE(APCS_GPLL_ENA_VOTE_REG));
+	regval |= BIT(0);
+	writel_relaxed(regval, GCC_REG_BASE(APCS_GPLL_ENA_VOTE_REG));
+
+	/*
+	 * TODO: Confirm that no clocks need to be voted on in this sleep vote
+	 * register.
+	 */
+	writel_relaxed(0x0, GCC_REG_BASE(APCS_CLOCK_SLEEP_ENA_VOTE));
+
+	/*
+	 * TODO: The following sequence enables the LPASS audio core GDSC.
+	 * Remove when this becomes unnecessary.
+	 */
+
+	/*
+	 * Disable HW trigger: collapse/restore occur based on registers writes.
+	 * Disable SW override: Use hardware state-machine for sequencing.
+	 */
+	regval = readl_relaxed(LPASS_REG_BASE(AUDIO_CORE_GDSCR));
+	regval &= ~(HW_CONTROL_MASK | SW_OVERRIDE_MASK);
+
+	/* Configure wait time between states. */
+	regval &= ~(EN_REST_WAIT_MASK | EN_FEW_WAIT_MASK | CLK_DIS_WAIT_MASK);
+	regval |= EN_REST_WAIT_VAL | EN_FEW_WAIT_VAL | CLK_DIS_WAIT_VAL;
+	writel_relaxed(regval, LPASS_REG_BASE(AUDIO_CORE_GDSCR));
+
+	regval = readl_relaxed(LPASS_REG_BASE(AUDIO_CORE_GDSCR));
+	regval &= ~BIT(0);
+	writel_relaxed(regval, LPASS_REG_BASE(AUDIO_CORE_GDSCR));
+
+	ret = readl_poll_timeout(LPASS_REG_BASE(AUDIO_CORE_GDSCR), status,
+				status & PWR_ON_MASK, 50, GDSC_TIMEOUT_US);
+	WARN(ret, "LPASS Audio Core GDSC did not power on.\n");
+}
+
+static void __init msm9625_clock_post_init(void)
+{
+	/*
+	 * Hold an active set vote for CXO; this is because CXO is expected
+	 * to remain on whenever CPUs aren't power collapsed.
+	 */
+	clk_prepare_enable(&cxo_a_clk_src.c);
+
+	/*
+	 * TODO: This call is to prevent sending 0Hz to rpm to turn off pnoc.
+	 * Needs to remove this after vote of pnoc from sdcc driver is ready.
+	 */
+	clk_prepare_enable(&pnoc_msmbus_a_clk.c);
+
+	/* Set rates for single-rate clocks. */
+	clk_set_rate(&usb_hs_system_clk_src.c,
+			usb_hs_system_clk_src.freq_tbl[0].freq_hz);
+	clk_set_rate(&usb_hsic_clk_src.c,
+			usb_hsic_clk_src.freq_tbl[0].freq_hz);
+	clk_set_rate(&usb_hsic_io_cal_clk_src.c,
+			usb_hsic_io_cal_clk_src.freq_tbl[0].freq_hz);
+	clk_set_rate(&usb_hsic_system_clk_src.c,
+			usb_hsic_system_clk_src.freq_tbl[0].freq_hz);
+	clk_set_rate(&usb_hsic_xcvr_fs_clk_src.c,
+			usb_hsic_xcvr_fs_clk_src.freq_tbl[0].freq_hz);
+	clk_set_rate(&pdm2_clk_src.c, pdm2_clk_src.freq_tbl[0].freq_hz);
+	clk_set_rate(&audio_core_slimbus_core_clk_src.c,
+			audio_core_slimbus_core_clk_src.freq_tbl[0].freq_hz);
+}
+
+#define GCC_CC_PHYS		0xFC400000
+#define GCC_CC_SIZE		SZ_16K
+
+#define LPASS_CC_PHYS		0xFE000000
+#define LPASS_CC_SIZE		SZ_256K
+
+#define APCS_GCC_CC_PHYS	0xF9011000
+#define APCS_GCC_CC_SIZE	SZ_4K
+
+#define APCS_PLL_PHYS		0xF9008018
+#define APCS_PLL_SIZE		0x18
+
+static void __init msm9625_clock_pre_init(void)
+{
+	virt_bases[GCC_BASE] = ioremap(GCC_CC_PHYS, GCC_CC_SIZE);
+	if (!virt_bases[GCC_BASE])
+		panic("clock-9625: Unable to ioremap GCC memory!");
+
+	virt_bases[LPASS_BASE] = ioremap(LPASS_CC_PHYS, LPASS_CC_SIZE);
+	if (!virt_bases[LPASS_BASE])
+		panic("clock-9625: Unable to ioremap LPASS_CC memory!");
+
+	virt_bases[APCS_BASE] = ioremap(APCS_GCC_CC_PHYS, APCS_GCC_CC_SIZE);
+	if (!virt_bases[APCS_BASE])
+		panic("clock-9625: Unable to ioremap APCS_GCC_CC memory!");
+
+	virt_bases[APCS_PLL_BASE] = ioremap(APCS_PLL_PHYS, APCS_PLL_SIZE);
+	if (!virt_bases[APCS_PLL_BASE])
+		panic("clock-9625: Unable to ioremap APCS_PLL memory!");
+
+	clk_ops_local_pll.enable = sr_pll_clk_enable_9625;
+
+	vdd_dig_reg = regulator_get(NULL, "vdd_dig");
+	if (IS_ERR(vdd_dig_reg))
+		panic("clock-9625: Unable to get the vdd_dig regulator!");
+
+	vote_vdd_level(&vdd_dig, VDD_DIG_HIGH);
+	regulator_enable(vdd_dig_reg);
+
+	enable_rpm_scaling();
+
+	reg_init();
+}
+
+static int __init msm9625_clock_late_init(void)
+{
+	return unvote_vdd_level(&vdd_dig, VDD_DIG_HIGH);
+}
+
+struct clock_init_data msm9625_clock_init_data __initdata = {
+	.table = msm_clocks_9625,
+	.size = ARRAY_SIZE(msm_clocks_9625),
+	.pre_init = msm9625_clock_pre_init,
+	.post_init = msm9625_clock_post_init,
+	.late_init = msm9625_clock_late_init,
+};
diff --git a/arch/arm/mach-msm/clock-debug.c b/arch/arm/mach-msm/clock-debug.c
index 8bd4433..489d623 100644
--- a/arch/arm/mach-msm/clock-debug.c
+++ b/arch/arm/mach-msm/clock-debug.c
@@ -157,7 +157,7 @@
 			   clock->dbg_name, clock->rate);
 		return 0;
 	}
-	for (level = 0; level < ARRAY_SIZE(clock->fmax); level++) {
+	for (level = 0; level < clock->num_fmax; level++) {
 		if (vdd_level == level)
 			seq_printf(m, "[%lu] ", clock->fmax[level]);
 		else
@@ -189,7 +189,7 @@
 	if (!clock->vdd_class) {
 		fmax = INT_MAX;
 	} else {
-		for (level = 0; level < ARRAY_SIZE(clock->fmax); level++)
+		for (level = 0; level < clock->num_fmax; level++)
 			if (clock->fmax[level])
 				fmax = clock->fmax[level];
 	}
diff --git a/arch/arm/mach-msm/clock-local.c b/arch/arm/mach-msm/clock-local.c
index ca031ad..c43ca46 100644
--- a/arch/arm/mach-msm/clock-local.c
+++ b/arch/arm/mach-msm/clock-local.c
@@ -352,7 +352,7 @@
 	u32 reg_val;
 
 	reg_val = b->ctl_reg ? readl_relaxed(b->ctl_reg) : 0;
-	if (b->en_mask) {
+	if (b->ctl_reg && b->en_mask) {
 		reg_val &= ~(b->en_mask);
 		writel_relaxed(reg_val, b->ctl_reg);
 	}
@@ -839,6 +839,14 @@
 	.set_flags = branch_clk_set_flags,
 };
 
+struct clk_ops clk_ops_smi_2x = {
+	.prepare = branch_clk_enable,
+	.unprepare = branch_clk_disable,
+	.is_enabled = branch_clk_is_enabled,
+	.get_parent = branch_clk_get_parent,
+	.handoff = branch_clk_handoff,
+};
+
 struct clk_ops clk_ops_reset = {
 	.reset = branch_clk_reset,
 };
diff --git a/arch/arm/mach-msm/clock-local.h b/arch/arm/mach-msm/clock-local.h
index 1873343..fca6486 100644
--- a/arch/arm/mach-msm/clock-local.h
+++ b/arch/arm/mach-msm/clock-local.h
@@ -153,6 +153,7 @@
 };
 
 extern struct clk_ops clk_ops_branch;
+extern struct clk_ops clk_ops_smi_2x;
 extern struct clk_ops clk_ops_reset;
 
 int branch_reset(struct branch *b, enum clk_reset_action action);
diff --git a/arch/arm/mach-msm/clock-mdss-8974.c b/arch/arm/mach-msm/clock-mdss-8974.c
index 1603c93..e7a596d 100644
--- a/arch/arm/mach-msm/clock-mdss-8974.c
+++ b/arch/arm/mach-msm/clock-mdss-8974.c
@@ -67,7 +67,6 @@
 static int pll_byte_clk_rate;
 static int pll_pclk_rate;
 static int pll_initialized;
-static int pll_enabled;
 static struct clk *mdss_dsi_ahb_clk;
 static unsigned long dsi_pll_rate;
 
@@ -208,15 +207,12 @@
 	return 0;
 }
 
-static int mdss_dsi_pll_enable(struct clk *c)
+static int __mdss_dsi_pll_enable(struct clk *c)
 {
 	u32 status;
 	u32 max_reads, timeout_us;
 	int i;
 
-	if (pll_enabled)
-		return 0;
-
 	if (!pll_initialized) {
 		if (dsi_pll_rate)
 			mdss_dsi_pll_byte_set_rate(c, dsi_pll_rate);
@@ -266,12 +262,11 @@
 
 	pr_debug("%s: **** PLL Lock success\n", __func__);
 	clk_disable(mdss_dsi_ahb_clk);
-	pll_enabled = 1;
 
 	return 0;
 }
 
-static void mdss_dsi_pll_disable(struct clk *c)
+static void __mdss_dsi_pll_disable(void)
 {
 	if (!mdss_dsi_ahb_clk)
 		pr_err("%s: mdss_dsi_ahb_clk not initialized\n",
@@ -282,7 +277,40 @@
 	clk_disable(mdss_dsi_ahb_clk);
 	pr_debug("%s: **** disable pll Initialize\n", __func__);
 	pll_initialized = 0;
-	pll_enabled = 0;
+}
+
+static DEFINE_SPINLOCK(dsipll_lock);
+static int dsipll_refcount;
+
+static void mdss_dsi_pll_disable(struct clk *c)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&dsipll_lock, flags);
+	if (WARN(dsipll_refcount == 0, "DSI PLL clock is unbalanced"))
+		goto out;
+	if (dsipll_refcount == 1)
+		__mdss_dsi_pll_disable();
+	dsipll_refcount--;
+out:
+	spin_unlock_irqrestore(&dsipll_lock, flags);
+}
+
+static int mdss_dsi_pll_enable(struct clk *c)
+{
+	unsigned long flags;
+	int ret = 0;
+
+	spin_lock_irqsave(&dsipll_lock, flags);
+	if (dsipll_refcount == 0) {
+		ret = __mdss_dsi_pll_enable(c);
+		if (ret < 0)
+			goto out;
+	}
+	dsipll_refcount++;
+out:
+	spin_unlock_irqrestore(&dsipll_lock, flags);
+	return ret;
 }
 
 void hdmi_pll_disable(void)
diff --git a/arch/arm/mach-msm/clock-pll.c b/arch/arm/mach-msm/clock-pll.c
index 23941d7..240f4e4 100644
--- a/arch/arm/mach-msm/clock-pll.c
+++ b/arch/arm/mach-msm/clock-pll.c
@@ -55,7 +55,7 @@
 
 #define ENABLE_WAIT_MAX_LOOPS 200
 
-int pll_vote_clk_enable(struct clk *c)
+static int pll_vote_clk_enable(struct clk *c)
 {
 	u32 ena, count;
 	unsigned long flags;
@@ -85,7 +85,7 @@
 	return -ETIMEDOUT;
 }
 
-void pll_vote_clk_disable(struct clk *c)
+static void pll_vote_clk_disable(struct clk *c)
 {
 	u32 ena;
 	unsigned long flags;
@@ -98,12 +98,12 @@
 	spin_unlock_irqrestore(&pll_reg_lock, flags);
 }
 
-struct clk *pll_vote_clk_get_parent(struct clk *c)
+static struct clk *pll_vote_clk_get_parent(struct clk *c)
 {
 	return to_pll_vote_clk(c)->parent;
 }
 
-int pll_vote_clk_is_enabled(struct clk *c)
+static int pll_vote_clk_is_enabled(struct clk *c)
 {
 	struct pll_vote_clk *pllv = to_pll_vote_clk(c);
 	return !!(readl_relaxed(PLL_STATUS_REG(pllv)) & pllv->status_mask);
@@ -126,6 +126,34 @@
 	.handoff = pll_vote_clk_handoff,
 };
 
+static void __pll_config_reg(void __iomem *pll_config, struct pll_freq_tbl *f,
+			struct pll_config_masks *masks)
+{
+	u32 regval;
+
+	regval = readl_relaxed(pll_config);
+
+	/* Enable the MN counter if used */
+	if (f->m_val)
+		regval |= masks->mn_en_mask;
+
+	/* Set pre-divider and post-divider values */
+	regval &= ~masks->pre_div_mask;
+	regval |= f->pre_div_val;
+	regval &= ~masks->post_div_mask;
+	regval |= f->post_div_val;
+
+	/* Select VCO setting */
+	regval &= ~masks->vco_mask;
+	regval |= f->vco_val;
+
+	/* Enable main output if it has not been enabled */
+	if (masks->main_output_mask && !(regval & masks->main_output_mask))
+		regval |= masks->main_output_mask;
+
+	writel_relaxed(regval, pll_config);
+}
+
 static void __pll_clk_enable_reg(void __iomem *mode_reg)
 {
 	u32 mode = readl_relaxed(mode_reg);
@@ -206,6 +234,34 @@
 	return to_pll_clk(c)->parent;
 }
 
+static int local_pll_clk_set_rate(struct clk *c, unsigned long rate)
+{
+	struct pll_freq_tbl *nf;
+	struct pll_clk *pll = to_pll_clk(c);
+	u32 mode;
+
+	mode = readl_relaxed(PLL_MODE_REG(pll));
+
+	/* Don't change PLL's rate if it is enabled */
+	if ((mode & PLL_MODE_MASK) == PLL_MODE_MASK)
+		return -EBUSY;
+
+	for (nf = pll->freq_tbl; nf->freq_hz != PLL_FREQ_END
+			&& nf->freq_hz != rate; nf++)
+		;
+
+	if (nf->freq_hz == PLL_FREQ_END)
+		return -EINVAL;
+
+	writel_relaxed(nf->l_val, PLL_L_REG(pll));
+	writel_relaxed(nf->m_val, PLL_M_REG(pll));
+	writel_relaxed(nf->n_val, PLL_N_REG(pll));
+
+	__pll_config_reg(PLL_CONFIG_REG(pll), nf, &pll->masks);
+
+	return 0;
+}
+
 int sr_pll_clk_enable(struct clk *c)
 {
 	u32 mode;
@@ -288,6 +344,7 @@
 struct clk_ops clk_ops_local_pll = {
 	.enable = local_pll_clk_enable,
 	.disable = local_pll_clk_disable,
+	.set_rate = local_pll_clk_set_rate,
 	.handoff = local_pll_clk_handoff,
 	.get_parent = local_pll_clk_get_parent,
 };
@@ -305,6 +362,7 @@
 	{41, 800000000},
 	{50, 960000000},
 	{52, 1008000000},
+	{57, 1104000000},
 	{60, 1152000000},
 	{62, 1200000000},
 	{63, 1209600000},
@@ -442,6 +500,46 @@
 	.is_enabled = pll_clk_is_enabled,
 };
 
+static DEFINE_SPINLOCK(soft_vote_lock);
+
+static int pll_acpu_vote_clk_enable(struct clk *c)
+{
+	int ret = 0;
+	unsigned long flags;
+	struct pll_vote_clk *pllv = to_pll_vote_clk(c);
+
+	spin_lock_irqsave(&soft_vote_lock, flags);
+
+	if (!*pllv->soft_vote)
+		ret = pll_vote_clk_enable(c);
+	if (ret == 0)
+		*pllv->soft_vote |= (pllv->soft_vote_mask);
+
+	spin_unlock_irqrestore(&soft_vote_lock, flags);
+	return ret;
+}
+
+static void pll_acpu_vote_clk_disable(struct clk *c)
+{
+	unsigned long flags;
+	struct pll_vote_clk *pllv = to_pll_vote_clk(c);
+
+	spin_lock_irqsave(&soft_vote_lock, flags);
+
+	*pllv->soft_vote &= ~(pllv->soft_vote_mask);
+	if (!*pllv->soft_vote)
+		pll_vote_clk_disable(c);
+
+	spin_unlock_irqrestore(&soft_vote_lock, flags);
+}
+
+struct clk_ops clk_ops_pll_acpu_vote = {
+	.enable = pll_acpu_vote_clk_enable,
+	.disable = pll_acpu_vote_clk_disable,
+	.is_enabled = pll_vote_clk_is_enabled,
+	.get_parent = pll_vote_clk_get_parent,
+};
+
 static void __init __set_fsm_mode(void __iomem *mode_reg,
 					u32 bias_count, u32 lock_count)
 {
diff --git a/arch/arm/mach-msm/clock-pll.h b/arch/arm/mach-msm/clock-pll.h
index 5c7c304..33b35a8 100644
--- a/arch/arm/mach-msm/clock-pll.h
+++ b/arch/arm/mach-msm/clock-pll.h
@@ -58,6 +58,45 @@
 void msm_shared_pll_control_init(void);
 
 /**
+ * struct pll_freq_tbl - generic PLL frequency definition
+ * @freq_hz: pll frequency in hz
+ * @l_val: pll l value
+ * @m_val: pll m value
+ * @n_val: pll n value
+ * @post_div_val: pll post divider value
+ * @pre_div_val: pll pre-divider value
+ * @vco_val: pll vco value
+ */
+struct pll_freq_tbl {
+	const u32 freq_hz;
+	const u32 l_val;
+	const u32 m_val;
+	const u32 n_val;
+	const u32 post_div_val;
+	const u32 pre_div_val;
+	const u32 vco_val;
+};
+
+/**
+ * struct pll_config_masks - PLL config masks struct
+ * @post_div_mask: mask for post divider bits location
+ * @pre_div_mask: mask for pre-divider bits location
+ * @vco_mask: mask for vco bits location
+ * @mn_en_mask: ORed with pll config register to enable the mn counter
+ * @main_output_mask: ORed with pll config register to enable the main output
+ */
+struct pll_config_masks {
+	u32 post_div_mask;
+	u32 pre_div_mask;
+	u32 vco_mask;
+	u32 mn_en_mask;
+	u32 main_output_mask;
+};
+
+#define PLL_FREQ_END	(UINT_MAX-1)
+#define PLL_F_END { .freq_hz = PLL_FREQ_END }
+
+/**
  * struct pll_vote_clk - phase locked loop (HW voteable)
  * @soft_vote: soft voting variable for multiple PLL software instances
  * @soft_vote_mask: soft voting mask for multiple PLL software instances
@@ -82,6 +121,11 @@
 };
 
 extern struct clk_ops clk_ops_pll_vote;
+extern struct clk_ops clk_ops_pll_acpu_vote;
+
+/* Soft voting values */
+#define PLL_SOFT_VOTE_PRIMARY   BIT(0)
+#define PLL_SOFT_VOTE_ACPU      BIT(1)
 
 static inline struct pll_vote_clk *to_pll_vote_clk(struct clk *c)
 {
@@ -91,15 +135,30 @@
 /**
  * struct pll_clk - phase locked loop
  * @mode_reg: enable register
+ * @l_reg: l value register
+ * @m_reg: m value register
+ * @n_reg: n value register
+ * @config_reg: configuration register, contains mn divider enable, pre divider,
+ *   post divider and vco configuration. register name can be configure register
+ *   or user_ctl register depending on targets
  * @status_reg: status register, contains the lock detection bit
+ * @masks: masks used for settings in config_reg
+ * @freq_tbl: pll freq table
  * @parent: clock source
  * @c: clk
  * @base: pointer to base address of ioremapped registers.
  */
 struct pll_clk {
 	void __iomem *const mode_reg;
+	void __iomem *const l_reg;
+	void __iomem *const m_reg;
+	void __iomem *const n_reg;
+	void __iomem *const config_reg;
 	void __iomem *const status_reg;
 
+	struct pll_config_masks masks;
+	struct pll_freq_tbl *freq_tbl;
+
 	struct clk *parent;
 	struct clk c;
 	void *const __iomem *base;
@@ -115,14 +174,6 @@
 int sr_pll_clk_enable(struct clk *c);
 int sr_hpm_lp_pll_clk_enable(struct clk *c);
 
-/*
- * PLL vote clock APIs
- */
-int pll_vote_clk_enable(struct clk *c);
-void pll_vote_clk_disable(struct clk *c);
-struct clk *pll_vote_clk_get_parent(struct clk *c);
-int pll_vote_clk_is_enabled(struct clk *c);
-
 struct pll_config {
 	u32 l;
 	u32 m;
diff --git a/arch/arm/mach-msm/clock-rpm.c b/arch/arm/mach-msm/clock-rpm.c
index e06eb4b..63e67b3 100644
--- a/arch/arm/mach-msm/clock-rpm.c
+++ b/arch/arm/mach-msm/clock-rpm.c
@@ -12,37 +12,29 @@
  */
 
 #include <linux/err.h>
+#include <linux/mutex.h>
 #include <mach/clk-provider.h>
 
 #include "rpm_resources.h"
 #include "clock-rpm.h"
 
-#define __clk_rpmrs_set_rate(r, value, ctx, noirq) \
-	((r)->rpmrs_data->set_rate_fn((r), (value), (ctx), (noirq)))
+#define __clk_rpmrs_set_rate(r, value, ctx) \
+	((r)->rpmrs_data->set_rate_fn((r), (value), (ctx)))
 
 #define clk_rpmrs_set_rate_sleep(r, value) \
-	    __clk_rpmrs_set_rate((r), (value), (r)->rpmrs_data->ctx_sleep_id, 0)
-
-#define clk_rpmrs_set_rate_sleep_noirq(r, value) \
-	    __clk_rpmrs_set_rate((r), (value), (r)->rpmrs_data->ctx_sleep_id, 1)
+	    __clk_rpmrs_set_rate((r), (value), (r)->rpmrs_data->ctx_sleep_id)
 
 #define clk_rpmrs_set_rate_active(r, value) \
-	   __clk_rpmrs_set_rate((r), (value), (r)->rpmrs_data->ctx_active_id, 0)
-
-#define clk_rpmrs_set_rate_active_noirq(r, value) \
-	   __clk_rpmrs_set_rate((r), (value), (r)->rpmrs_data->ctx_active_id, 1)
+	   __clk_rpmrs_set_rate((r), (value), (r)->rpmrs_data->ctx_active_id)
 
 static int clk_rpmrs_set_rate(struct rpm_clk *r, uint32_t value,
-			   uint32_t context, int noirq)
+			   uint32_t context)
 {
 	struct msm_rpm_iv_pair iv = {
 		.id = r->rpm_clk_id,
 		.value = value,
 	};
-	if (noirq)
-		return msm_rpmrs_set_noirq(context, &iv, 1);
-	else
-		return msm_rpmrs_set(context, &iv, 1);
+	return msm_rpmrs_set(context, &iv, 1);
 }
 
 static int clk_rpmrs_get_rate(struct rpm_clk *r)
@@ -72,7 +64,7 @@
 }
 
 static int clk_rpmrs_set_rate_smd(struct rpm_clk *r, uint32_t value,
-				uint32_t context, int noirq)
+				uint32_t context)
 {
 	struct msm_rpm_kvp kvp = {
 		.key = r->rpm_key,
@@ -80,12 +72,8 @@
 		.length = sizeof(value),
 	};
 
-	if (noirq)
-		return msm_rpm_send_message_noirq(context,
-				r->rpm_res_type, r->rpm_clk_id, &kvp, 1);
-	else
-		return msm_rpm_send_message(context, r->rpm_res_type,
-						r->rpm_clk_id, &kvp, 1);
+	return msm_rpm_send_message(context, r->rpm_res_type, r->rpm_clk_id,
+			&kvp, 1);
 }
 
 static int clk_rpmrs_handoff_smd(struct rpm_clk *r)
@@ -94,8 +82,7 @@
 }
 
 struct clk_rpmrs_data {
-	int (*set_rate_fn)(struct rpm_clk *r, uint32_t value,
-				uint32_t context, int noirq);
+	int (*set_rate_fn)(struct rpm_clk *r, uint32_t value, uint32_t context);
 	int (*get_rate_fn)(struct rpm_clk *r);
 	int (*handoff_fn)(struct rpm_clk *r);
 	int ctx_active_id;
@@ -117,11 +104,10 @@
 	.ctx_sleep_id = MSM_RPM_CTX_SLEEP_SET,
 };
 
-static DEFINE_SPINLOCK(rpm_clock_lock);
+static DEFINE_MUTEX(rpm_clock_lock);
 
-static int rpm_clk_enable(struct clk *clk)
+static int rpm_clk_prepare(struct clk *clk)
 {
-	unsigned long flags;
 	struct rpm_clk *r = to_rpm_clk(clk);
 	uint32_t value;
 	int rc = 0;
@@ -129,7 +115,7 @@
 	unsigned long peer_khz = 0, peer_sleep_khz = 0;
 	struct rpm_clk *peer = r->peer;
 
-	spin_lock_irqsave(&rpm_clock_lock, flags);
+	mutex_lock(&rpm_clock_lock);
 
 	this_khz = r->last_set_khz;
 	/* Don't send requests to the RPM if the rate has not been set. */
@@ -148,7 +134,7 @@
 	if (r->branch)
 		value = !!value;
 
-	rc = clk_rpmrs_set_rate_active_noirq(r, value);
+	rc = clk_rpmrs_set_rate_active(r, value);
 	if (rc)
 		goto out;
 
@@ -156,28 +142,27 @@
 	if (r->branch)
 		value = !!value;
 
-	rc = clk_rpmrs_set_rate_sleep_noirq(r, value);
+	rc = clk_rpmrs_set_rate_sleep(r, value);
 	if (rc) {
 		/* Undo the active set vote and restore it to peer_khz */
 		value = peer_khz;
-		rc = clk_rpmrs_set_rate_active_noirq(r, value);
+		rc = clk_rpmrs_set_rate_active(r, value);
 	}
 
 out:
 	if (!rc)
 		r->enabled = true;
 
-	spin_unlock_irqrestore(&rpm_clock_lock, flags);
+	mutex_unlock(&rpm_clock_lock);
 
 	return rc;
 }
 
-static void rpm_clk_disable(struct clk *clk)
+static void rpm_clk_unprepare(struct clk *clk)
 {
-	unsigned long flags;
 	struct rpm_clk *r = to_rpm_clk(clk);
 
-	spin_lock_irqsave(&rpm_clock_lock, flags);
+	mutex_lock(&rpm_clock_lock);
 
 	if (r->last_set_khz) {
 		uint32_t value;
@@ -192,30 +177,29 @@
 		}
 
 		value = r->branch ? !!peer_khz : peer_khz;
-		rc = clk_rpmrs_set_rate_active_noirq(r, value);
+		rc = clk_rpmrs_set_rate_active(r, value);
 		if (rc)
 			goto out;
 
 		value = r->branch ? !!peer_sleep_khz : peer_sleep_khz;
-		rc = clk_rpmrs_set_rate_sleep_noirq(r, value);
+		rc = clk_rpmrs_set_rate_sleep(r, value);
 	}
 	r->enabled = false;
 out:
-	spin_unlock_irqrestore(&rpm_clock_lock, flags);
+	mutex_unlock(&rpm_clock_lock);
 
 	return;
 }
 
 static int rpm_clk_set_rate(struct clk *clk, unsigned long rate)
 {
-	unsigned long flags;
 	struct rpm_clk *r = to_rpm_clk(clk);
 	unsigned long this_khz, this_sleep_khz;
 	int rc = 0;
 
 	this_khz = DIV_ROUND_UP(rate, r->factor);
 
-	spin_lock_irqsave(&rpm_clock_lock, flags);
+	mutex_lock(&rpm_clock_lock);
 
 	/* Active-only clocks don't care what the rate is during sleep. So,
 	 * they vote for zero. */
@@ -236,12 +220,12 @@
 		}
 
 		value = max(this_khz, peer_khz);
-		rc = clk_rpmrs_set_rate_active_noirq(r, value);
+		rc = clk_rpmrs_set_rate_active(r, value);
 		if (rc)
 			goto out;
 
 		value = max(this_sleep_khz, peer_sleep_khz);
-		rc = clk_rpmrs_set_rate_sleep_noirq(r, value);
+		rc = clk_rpmrs_set_rate_sleep(r, value);
 	}
 	if (!rc) {
 		r->last_set_khz = this_khz;
@@ -249,7 +233,7 @@
 	}
 
 out:
-	spin_unlock_irqrestore(&rpm_clock_lock, flags);
+	mutex_unlock(&rpm_clock_lock);
 
 	return rc;
 }
@@ -297,9 +281,30 @@
 	return HANDOFF_ENABLED_CLK;
 }
 
+#define RPM_MISC_CLK_TYPE	0x306b6c63
+#define RPM_SCALING_ENABLE_ID	0x2
+
+void enable_rpm_scaling(void)
+{
+	int rc, value = 0x1;
+	struct msm_rpm_kvp kvp = {
+		.key = RPM_SMD_KEY_ENABLE,
+		.data = (void *)&value,
+		.length = sizeof(value),
+	};
+
+	rc = msm_rpm_send_message_noirq(MSM_RPM_CTX_SLEEP_SET,
+			RPM_MISC_CLK_TYPE, RPM_SCALING_ENABLE_ID, &kvp, 1);
+	WARN(rc < 0, "RPM clock scaling (sleep set) did not enable!\n");
+
+	rc = msm_rpm_send_message_noirq(MSM_RPM_CTX_ACTIVE_SET,
+			RPM_MISC_CLK_TYPE, RPM_SCALING_ENABLE_ID, &kvp, 1);
+	WARN(rc < 0, "RPM clock scaling (active set) did not enable!\n");
+}
+
 struct clk_ops clk_ops_rpm = {
-	.enable = rpm_clk_enable,
-	.disable = rpm_clk_disable,
+	.prepare = rpm_clk_prepare,
+	.unprepare = rpm_clk_unprepare,
 	.set_rate = rpm_clk_set_rate,
 	.get_rate = rpm_clk_get_rate,
 	.is_enabled = rpm_clk_is_enabled,
@@ -309,8 +314,8 @@
 };
 
 struct clk_ops clk_ops_rpm_branch = {
-	.enable = rpm_clk_enable,
-	.disable = rpm_clk_disable,
+	.prepare = rpm_clk_prepare,
+	.unprepare = rpm_clk_unprepare,
 	.is_local = rpm_clk_is_local,
 	.handoff = rpm_clk_handoff,
 };
diff --git a/arch/arm/mach-msm/clock-rpm.h b/arch/arm/mach-msm/clock-rpm.h
index 2f0b729..252e8cb 100644
--- a/arch/arm/mach-msm/clock-rpm.h
+++ b/arch/arm/mach-msm/clock-rpm.h
@@ -54,6 +54,12 @@
 	return container_of(clk, struct rpm_clk, c);
 }
 
+/*
+ * RPM scaling enable function used for target that has an RPM resource for
+ * rpm clock scaling enable.
+ */
+void enable_rpm_scaling(void);
+
 extern struct clk_rpmrs_data clk_rpmrs_data;
 extern struct clk_rpmrs_data clk_rpmrs_data_smd;
 
diff --git a/arch/arm/mach-msm/clock.c b/arch/arm/mach-msm/clock.c
index c30bd79..e9dd974 100644
--- a/arch/arm/mach-msm/clock.c
+++ b/arch/arm/mach-msm/clock.c
@@ -37,11 +37,11 @@
 {
 	int level;
 
-	for (level = 0; level < ARRAY_SIZE(clk->fmax); level++)
+	for (level = 0; level < clk->num_fmax; level++)
 		if (rate <= clk->fmax[level])
 			break;
 
-	if (level == ARRAY_SIZE(clk->fmax)) {
+	if (level == clk->num_fmax) {
 		pr_err("Rate %lu for %s is greater than highest Fmax\n", rate,
 			clk->dbg_name);
 		return -EINVAL;
@@ -55,7 +55,7 @@
 {
 	int level, rc;
 
-	for (level = ARRAY_SIZE(vdd_class->level_votes)-1; level > 0; level--)
+	for (level = vdd_class->num_levels-1; level > 0; level--)
 		if (vdd_class->level_votes[level])
 			break;
 
@@ -74,6 +74,9 @@
 {
 	int rc;
 
+	if (level >= vdd_class->num_levels)
+		return -EINVAL;
+
 	mutex_lock(&vdd_class->lock);
 	vdd_class->level_votes[level]++;
 	rc = update_vdd(vdd_class);
@@ -89,6 +92,9 @@
 {
 	int rc = 0;
 
+	if (level >= vdd_class->num_levels)
+		return -EINVAL;
+
 	mutex_lock(&vdd_class->lock);
 	if (WARN(!vdd_class->level_votes[level],
 			"Reference counts are incorrect for %s level %d\n",
@@ -425,6 +431,19 @@
 
 static struct clock_init_data *clk_init_data;
 
+static void init_sibling_lists(struct clk_lookup *clock_tbl, size_t num_clocks)
+{
+	struct clk *clk, *parent;
+	unsigned n;
+
+	for (n = 0; n < num_clocks; n++) {
+		clk = clock_tbl[n].clk;
+		parent = clk_get_parent(clk);
+		if (parent && list_empty(&clk->siblings))
+			list_add(&clk->siblings, &parent->children);
+	}
+}
+
 /**
  * msm_clock_register() - Register additional clock tables
  * @table: Table of clocks
@@ -443,6 +462,7 @@
 	if (!table)
 		return -EINVAL;
 
+	init_sibling_lists(table, size);
 	clkdev_add_table(table, size);
 	clock_debug_register(table, size);
 
@@ -514,7 +534,6 @@
 	unsigned n;
 	struct clk_lookup *clock_tbl;
 	size_t num_clocks;
-	struct clk *clk;
 
 	if (!data)
 		return -EINVAL;
@@ -526,13 +545,7 @@
 	clock_tbl = data->table;
 	num_clocks = data->size;
 
-	for (n = 0; n < num_clocks; n++) {
-		struct clk *parent;
-		clk = clock_tbl[n].clk;
-		parent = clk_get_parent(clk);
-		if (parent && list_empty(&clk->siblings))
-			list_add(&clk->siblings, &parent->children);
-	}
+	init_sibling_lists(clock_tbl, num_clocks);
 
 	/*
 	 * Detect and preserve initial clock state until clock_late_init() or
diff --git a/arch/arm/mach-msm/clock.h b/arch/arm/mach-msm/clock.h
index 48f897b..8a75d390 100644
--- a/arch/arm/mach-msm/clock.h
+++ b/arch/arm/mach-msm/clock.h
@@ -34,6 +34,7 @@
 };
 
 extern struct clock_init_data msm9615_clock_init_data;
+extern struct clock_init_data msm9625_clock_init_data;
 extern struct clock_init_data apq8064_clock_init_data;
 extern struct clock_init_data fsm9xxx_clock_init_data;
 extern struct clock_init_data msm7x01a_clock_init_data;
diff --git a/arch/arm/mach-msm/devices-8064.c b/arch/arm/mach-msm/devices-8064.c
index e8baf6a..3b3425f 100644
--- a/arch/arm/mach-msm/devices-8064.c
+++ b/arch/arm/mach-msm/devices-8064.c
@@ -101,6 +101,7 @@
 #define PCIE20_SIZE   SZ_4K
 #define MSM8064_PC_CNTR_PHYS	(APQ8064_IMEM_PHYS + 0x664)
 #define MSM8064_PC_CNTR_SIZE		0x40
+#define MSM8064_RPM_MASTER_STATS_BASE	0x10BB00
 
 static struct resource msm8064_resources_pccntr[] = {
 	{
@@ -372,6 +373,58 @@
 	.resource	= resources_qup_spi_gsbi5,
 };
 
+static struct resource resources_qup_spi_gsbi6[] = {
+	{
+		.name   = "spi_base",
+		.start  = MSM_GSBI6_QUP_PHYS,
+		.end    = MSM_GSBI6_QUP_PHYS + SZ_4K - 1,
+		.flags  = IORESOURCE_MEM,
+	},
+	{
+		.name   = "gsbi_base",
+		.start  = MSM_GSBI6_PHYS,
+		.end    = MSM_GSBI6_PHYS + 4 - 1,
+		.flags  = IORESOURCE_MEM,
+	},
+	{
+		.name   = "spi_irq_in",
+		.start  = GSBI6_QUP_IRQ,
+		.end    = GSBI6_QUP_IRQ,
+		.flags  = IORESOURCE_IRQ,
+	},
+	{
+		.name   = "spi_clk",
+		.start  = 17,
+		.end    = 17,
+		.flags  = IORESOURCE_IO,
+	},
+	{
+		.name   = "spi_miso",
+		.start  = 15,
+		.end    = 15,
+		.flags  = IORESOURCE_IO,
+	},
+	{
+		.name   = "spi_mosi",
+		.start  = 14,
+		.end    = 14,
+		.flags  = IORESOURCE_IO,
+	},
+	{
+		.name   = "spi_cs",
+		.start  = 16,
+		.end    = 16,
+		.flags  = IORESOURCE_IO,
+	}
+};
+
+struct platform_device mpq8064_device_qup_spi_gsbi6 = {
+	.name		= "spi_qsd",
+	.id		= 1,
+	.num_resources	= ARRAY_SIZE(resources_qup_spi_gsbi6),
+	.resource	= resources_qup_spi_gsbi6,
+};
+
 static struct resource resources_qup_i2c_gsbi5[] = {
 	{
 		.name	= "gsbi_qup_i2c_addr",
@@ -541,6 +594,10 @@
 	.id     = 0x4009,
 };
 
+struct platform_device mpq_cpudai_pseudo = {
+	.name   = "msm-dai-q6",
+	.id     = 0x8001,
+};
 #define MSM_TSIF0_PHYS       (0x18200000)
 #define MSM_TSIF1_PHYS       (0x18201000)
 #define MSM_TSIF_SIZE        (0x200)
@@ -1817,7 +1874,7 @@
 	},
 	{
 		.irq_config_id = SMD_Q6,
-		.subsys_name = "q6",
+		.subsys_name = "adsp",
 		.edge = SMD_APPS_QDSP,
 
 		.smd_int.irq_name = "adsp_a11",
@@ -1963,6 +2020,11 @@
 		.flags  = IORESOURCE_MEM,
 	},
 	{
+		.start  = 0x00900000,
+		.end    = 0x00900000 + SZ_16K - 1,
+		.flags  = IORESOURCE_MEM,
+	},
+	{
 		.start	= GSS_A5_WDOG_EXPIRED,
 		.end	= GSS_A5_WDOG_EXPIRED,
 		.flags	= IORESOURCE_IRQ,
@@ -2339,15 +2401,58 @@
 };
 
 static struct msm_rpmstats_platform_data msm_rpm_stat_pdata = {
-	.phys_addr_base = 0x0010DD04,
-	.phys_size = SZ_256,
+	.version = 1,
 };
 
+
+static struct resource msm_rpm_stat_resource[] = {
+	{
+		.start	= 0x0010D204,
+		.end	= 0x0010D204 + SZ_8K,
+		.flags	= IORESOURCE_MEM,
+		.name	= "phys_addr_base"
+	},
+};
+
+
 struct platform_device apq8064_rpm_stat_device = {
 	.name = "msm_rpm_stat",
 	.id = -1,
-	.dev = {
+	.resource = msm_rpm_stat_resource,
+	.num_resources	= ARRAY_SIZE(msm_rpm_stat_resource),
+	.dev	= {
 		.platform_data = &msm_rpm_stat_pdata,
+	}
+};
+
+static struct resource resources_rpm_master_stats[] = {
+	{
+		.start	= MSM8064_RPM_MASTER_STATS_BASE,
+		.end	= MSM8064_RPM_MASTER_STATS_BASE + SZ_256,
+		.flags	= IORESOURCE_MEM,
+	},
+};
+
+static char *master_names[] = {
+	"KPSS",
+	"MPSS",
+	"LPASS",
+	"RIVA",
+	"DSPS",
+};
+
+static struct msm_rpm_master_stats_platform_data msm_rpm_master_stat_pdata = {
+	.masters = master_names,
+	.nomasters = ARRAY_SIZE(master_names),
+};
+
+struct platform_device apq8064_rpm_master_stat_device = {
+	.name = "msm_rpm_master_stat",
+	.id = -1,
+	.num_resources	= ARRAY_SIZE(resources_rpm_master_stats),
+	.resource	= resources_rpm_master_stats,
+	.dev = {
+		.platform_data = &msm_rpm_master_stat_pdata,
 	},
 };
 
@@ -2372,19 +2477,6 @@
 
 /* Sensors DSPS platform data */
 
-#define PPSS_DSPS_TCM_CODE_BASE   0x12000000
-#define PPSS_DSPS_TCM_CODE_SIZE   0x28000
-#define PPSS_DSPS_TCM_BUF_BASE    0x12040000
-#define PPSS_DSPS_TCM_BUF_SIZE    0x4000
-#define PPSS_DSPS_PIPE_BASE       0x12800000
-#define PPSS_DSPS_PIPE_SIZE       0x4000
-#define PPSS_DSPS_DDR_BASE        0x8fe00000
-#define PPSS_DSPS_DDR_SIZE        0x100000
-#define PPSS_SMEM_BASE            0x80000000
-#define PPSS_SMEM_SIZE            0x200000
-#define PPSS_REG_PHYS_BASE        0x12080000
-#define PPSS_WDOG_UNMASKED_INT_EN 0x1808
-
 static struct dsps_clk_info dsps_clks[] = {};
 static struct dsps_regulator_info dsps_regs[] = {};
 
@@ -2393,6 +2485,8 @@
  * apq8064_init_dsps().
  */
 
+#define PPSS_REG_PHYS_BASE	0x12080000
+
 struct msm_dsps_platform_data msm_dsps_pdata_8064 = {
 	.clks = dsps_clks,
 	.clks_num = ARRAY_SIZE(dsps_clks),
@@ -2401,17 +2495,6 @@
 	.regs = dsps_regs,
 	.regs_num = ARRAY_SIZE(dsps_regs),
 	.dsps_pwr_ctl_en = 1,
-	.tcm_code_start = PPSS_DSPS_TCM_CODE_BASE,
-	.tcm_code_size = PPSS_DSPS_TCM_CODE_SIZE,
-	.tcm_buf_start = PPSS_DSPS_TCM_BUF_BASE,
-	.tcm_buf_size = PPSS_DSPS_TCM_BUF_SIZE,
-	.pipe_start = PPSS_DSPS_PIPE_BASE,
-	.pipe_size = PPSS_DSPS_PIPE_SIZE,
-	.ddr_start = PPSS_DSPS_DDR_BASE,
-	.ddr_size = PPSS_DSPS_DDR_SIZE,
-	.smem_start = PPSS_SMEM_BASE,
-	.smem_size  = PPSS_SMEM_SIZE,
-	.ppss_wdog_unmasked_int_en_reg = PPSS_WDOG_UNMASKED_INT_EN,
 	.signature = DSPS_SIGNATURE,
 };
 
@@ -2422,13 +2505,6 @@
 		.name  = "ppss_reg",
 		.flags = IORESOURCE_MEM,
 	},
-
-	{
-		.start = PPSS_WDOG_TIMER_IRQ,
-		.end   = PPSS_WDOG_TIMER_IRQ,
-		.name  = "ppss_wdog",
-		.flags = IORESOURCE_IRQ,
-	},
 };
 
 struct platform_device msm_dsps_device_8064 = {
@@ -2649,15 +2725,6 @@
 	.num_resources	= ARRAY_SIZE(i2s_mdm_resources),
 	.resource	= i2s_mdm_resources,
 };
-static int apq8064_LPM_latency = 1000; /* >100 usec for WFI */
-
-struct platform_device apq8064_cpu_idle_device = {
-	.name   = "msm_cpu_idle",
-	.id     = -1,
-	.dev = {
-		.platform_data = &apq8064_LPM_latency,
-	},
-};
 
 static struct msm_dcvs_freq_entry apq8064_freq[] = {
 	{ 384000, 900,  0, 0, 0},
@@ -2670,11 +2737,14 @@
 };
 
 static struct msm_dcvs_core_info apq8064_core_info = {
-	.freq_tbl	= &apq8064_freq[0],
-	.core_param	= {
+	.freq_tbl		= &apq8064_freq[0],
+	.num_cores		= 4,
+	.sensors		= (int[]){7, 8, 9, 10},
+	.thermal_poll_ms	= 60000,
+	.core_param		= {
 		.core_type	= MSM_DCVS_CORE_TYPE_CPU,
 	},
-	.algo_param	= {
+	.algo_param		= {
 		.disable_pc_threshold		= 1458000,
 		.em_win_size_min_us		= 100000,
 		.em_win_size_max_us		= 300000,
@@ -2690,7 +2760,7 @@
 		.ss_win_size_max_us		= 1000000,
 		.ss_util_pct			= 95,
 	},
-	.energy_coeffs	= {
+	.energy_coeffs		= {
 		.active_coeff_a		= 336,
 		.active_coeff_b		= 0,
 		.active_coeff_c		= 0,
@@ -2700,17 +2770,24 @@
 		.leakage_coeff_c	= 3329,
 		.leakage_coeff_d	= -277,
 	},
-	.power_param	= {
+	.power_param		= {
 		.current_temp	= 25,
 		.num_freq	= ARRAY_SIZE(apq8064_freq),
 	}
 };
 
+#define APQ8064_LPM_LATENCY  1000 /* >100 usec for WFI */
+
+static struct msm_gov_platform_data gov_platform_data = {
+	.info = &apq8064_core_info,
+	.latency = APQ8064_LPM_LATENCY,
+};
+
 struct platform_device apq8064_msm_gov_device = {
 	.name = "msm_dcvs_gov",
 	.id = -1,
 	.dev = {
-		.platform_data = &apq8064_core_info,
+		.platform_data = &gov_platform_data,
 	},
 };
 
diff --git a/arch/arm/mach-msm/devices-8930.c b/arch/arm/mach-msm/devices-8930.c
index d062ff4..6e305e0 100644
--- a/arch/arm/mach-msm/devices-8930.c
+++ b/arch/arm/mach-msm/devices-8930.c
@@ -38,6 +38,7 @@
 #endif
 #define MSM8930_PC_CNTR_PHYS	(MSM8930_IMEM_PHYS + 0x664)
 #define MSM8930_PC_CNTR_SIZE		0x40
+#define MSM8930_RPM_MASTER_STATS_BASE	0x10B100
 
 static struct resource msm8930_resources_pccntr[] = {
 	{
@@ -546,15 +547,57 @@
 };
 
 static struct msm_rpmstats_platform_data msm_rpm_stat_pdata = {
-	.phys_addr_base = 0x0010DD04,
-	.phys_size = SZ_256,
+	.version = 1,
 };
 
+static struct resource msm_rpm_stat_resource[] = {
+	{
+		.start	= 0x0010D204,
+		.end	= 0x0010D204 + SZ_8K,
+		.flags	= IORESOURCE_MEM,
+		.name	= "phys_addr_base"
+
+	},
+};
+
+
 struct platform_device msm8930_rpm_stat_device = {
 	.name = "msm_rpm_stat",
 	.id = -1,
-	.dev = {
+	.resource = msm_rpm_stat_resource,
+	.num_resources	= ARRAY_SIZE(msm_rpm_stat_resource),
+	.dev	= {
 		.platform_data = &msm_rpm_stat_pdata,
+	}
+};
+
+static struct resource resources_rpm_master_stats[] = {
+	{
+		.start	= MSM8930_RPM_MASTER_STATS_BASE,
+		.end	= MSM8930_RPM_MASTER_STATS_BASE + SZ_256,
+		.flags	= IORESOURCE_MEM,
+	},
+};
+
+static char *master_names[] = {
+	"KPSS",
+	"MPSS",
+	"LPASS",
+	"RIVA",
+};
+
+static struct msm_rpm_master_stats_platform_data msm_rpm_master_stat_pdata = {
+	.masters = master_names,
+	.nomasters = ARRAY_SIZE(master_names),
+};
+
+struct platform_device msm8930_rpm_master_stat_device = {
+	.name = "msm_rpm_master_stat",
+	.id = -1,
+	.num_resources	= ARRAY_SIZE(resources_rpm_master_stats),
+	.resource	= resources_rpm_master_stats,
+	.dev = {
+		.platform_data = &msm_rpm_master_stat_pdata,
 	},
 };
 
@@ -583,16 +626,6 @@
 	.resource = &msm_rpm_rbcpr_resource,
 };
 
-static int msm8930_LPM_latency = 1000; /* >100 usec for WFI */
-
-struct platform_device msm8930_cpu_idle_device = {
-	.name   = "msm_cpu_idle",
-	.id     = -1,
-	.dev = {
-		.platform_data = &msm8930_LPM_latency,
-	},
-};
-
 struct platform_device msm_bus_8930_sys_fabric = {
 	.name  = "msm_bus_fabric",
 	.id    =  MSM_BUS_FAB_SYSTEM,
@@ -1062,6 +1095,7 @@
 #endif
 	.disable_dmx = 1,
 	.disable_fullhd = 0,
+	.cont_mode_dpb_count = 18,
 	.fw_addr = 0x9fe00000,
 };
 
diff --git a/arch/arm/mach-msm/devices-8960.c b/arch/arm/mach-msm/devices-8960.c
index 72da3d8..50040a8 100644
--- a/arch/arm/mach-msm/devices-8960.c
+++ b/arch/arm/mach-msm/devices-8960.c
@@ -105,6 +105,7 @@
 
 #define MSM8960_PC_CNTR_PHYS	(MSM8960_IMEM_PHYS + 0x664)
 #define MSM8960_PC_CNTR_SIZE		0x40
+#define MSM8960_RPM_MASTER_STATS_BASE	0x10BB00
 
 static struct resource msm8960_resources_pccntr[] = {
 	{
@@ -1334,108 +1335,109 @@
 	},
 };
 
-#define MSM_LPASS_QDSP6SS_PHYS	0x28800000
-#define SFAB_LPASS_Q6_ACLK_CTL	(MSM_CLK_CTL_BASE + 0x23A0)
-
 static struct resource msm_8960_q6_lpass_resources[] = {
 	{
-		.start  = MSM_LPASS_QDSP6SS_PHYS,
-		.end    = MSM_LPASS_QDSP6SS_PHYS + SZ_256 - 1,
+		.start  = 0x28800000,
+		.end    = 0x28800000 + SZ_256 - 1,
 		.flags  = IORESOURCE_MEM,
 	},
+	{
+		.start  = LPASS_Q6SS_WDOG_EXPIRED,
+		.end    = LPASS_Q6SS_WDOG_EXPIRED,
+		.flags  = IORESOURCE_IRQ,
+	},
 };
 
 static struct pil_q6v4_pdata msm_8960_q6_lpass_data = {
 	.strap_tcm_base  = 0x01460000,
 	.strap_ahb_upper = 0x00290000,
 	.strap_ahb_lower = 0x00000280,
-	.aclk_reg = SFAB_LPASS_Q6_ACLK_CTL,
+	.aclk_reg = MSM_CLK_CTL_BASE + 0x23A0,
 	.name = "q6",
 	.pas_id = PAS_Q6,
 	.bus_port = MSM_BUS_MASTER_LPASS_PROC,
 };
 
 struct platform_device msm_8960_q6_lpass = {
-	.name = "pil_qdsp6v4",
-	.id = 0,
+	.name = "pil-q6v4-lpass",
+	.id = -1,
 	.num_resources  = ARRAY_SIZE(msm_8960_q6_lpass_resources),
 	.resource       = msm_8960_q6_lpass_resources,
 	.dev.platform_data = &msm_8960_q6_lpass_data,
 };
 
-#define MSM_MSS_ENABLE_PHYS	0x08B00000
-#define MSM_FW_QDSP6SS_PHYS	0x08800000
-#define MSS_Q6FW_JTAG_CLK_CTL	(MSM_CLK_CTL_BASE + 0x2C6C)
-#define SFAB_MSS_Q6_FW_ACLK_CTL (MSM_CLK_CTL_BASE + 0x2044)
-
-static struct resource msm_8960_q6_mss_fw_resources[] = {
+static struct resource msm_8960_q6_mss_resources[] = {
 	{
-		.start  = MSM_FW_QDSP6SS_PHYS,
-		.end    = MSM_FW_QDSP6SS_PHYS + SZ_256 - 1,
+		.start  = 0x08800000,
+		.end    = 0x08800000 + SZ_256 - 1,
 		.flags  = IORESOURCE_MEM,
 	},
 	{
-		.start  = MSM_MSS_ENABLE_PHYS,
-		.end    = MSM_MSS_ENABLE_PHYS + 4 - 1,
-		.flags  = IORESOURCE_MEM,
-	},
-};
-
-static struct pil_q6v4_pdata msm_8960_q6_mss_fw_data = {
-	.strap_tcm_base  = 0x00400000,
-	.strap_ahb_upper = 0x00090000,
-	.strap_ahb_lower = 0x00000080,
-	.aclk_reg = SFAB_MSS_Q6_FW_ACLK_CTL,
-	.jtag_clk_reg = MSS_Q6FW_JTAG_CLK_CTL,
-	.name = "modem_fw",
-	.depends = "q6",
-	.pas_id = PAS_MODEM_FW,
-	.bus_port = MSM_BUS_MASTER_MSS_FW_PROC,
-};
-
-struct platform_device msm_8960_q6_mss_fw = {
-	.name = "pil_qdsp6v4",
-	.id = 1,
-	.num_resources  = ARRAY_SIZE(msm_8960_q6_mss_fw_resources),
-	.resource       = msm_8960_q6_mss_fw_resources,
-	.dev.platform_data = &msm_8960_q6_mss_fw_data,
-};
-
-#define MSM_SW_QDSP6SS_PHYS	0x08900000
-#define SFAB_MSS_Q6_SW_ACLK_CTL	(MSM_CLK_CTL_BASE + 0x2040)
-#define MSS_Q6SW_JTAG_CLK_CTL	(MSM_CLK_CTL_BASE + 0x2C68)
-
-static struct resource msm_8960_q6_mss_sw_resources[] = {
-	{
-		.start  = MSM_SW_QDSP6SS_PHYS,
-		.end    = MSM_SW_QDSP6SS_PHYS + SZ_256 - 1,
+		.start  = 0x00900000,
+		.end    = 0x00900000 + SZ_16K - 1,
 		.flags  = IORESOURCE_MEM,
 	},
 	{
-		.start  = MSM_MSS_ENABLE_PHYS,
-		.end    = MSM_MSS_ENABLE_PHYS + 4 - 1,
+		.start  = 0x08B00000,
+		.end    = 0x08B00000 + SZ_256 - 1,
 		.flags  = IORESOURCE_MEM,
 	},
+	{
+		.start  = 0x08882000,
+		.end    = 0x08882000 + SZ_256 - 1,
+		.flags  = IORESOURCE_MEM,
+	},
+	{
+		.start  = 0x08900000,
+		.end    = 0x08900000 + SZ_256 - 1,
+		.flags  = IORESOURCE_MEM,
+	},
+	{
+		.start  = 0x08982000,
+		.end    = 0x08982000 + SZ_256 - 1,
+		.flags  = IORESOURCE_MEM,
+	},
+	{
+		.start  = Q6FW_WDOG_EXPIRED_IRQ,
+		.end    = Q6FW_WDOG_EXPIRED_IRQ,
+		.flags  = IORESOURCE_IRQ,
+	},
+	{
+		.start  = Q6SW_WDOG_EXPIRED_IRQ,
+		.end    = Q6SW_WDOG_EXPIRED_IRQ,
+		.flags  = IORESOURCE_IRQ,
+	},
 };
 
-static struct pil_q6v4_pdata msm_8960_q6_mss_sw_data = {
-	.strap_tcm_base  = 0x00420000,
-	.strap_ahb_upper = 0x00090000,
-	.strap_ahb_lower = 0x00000080,
-	.aclk_reg = SFAB_MSS_Q6_SW_ACLK_CTL,
-	.jtag_clk_reg = MSS_Q6SW_JTAG_CLK_CTL,
-	.name = "modem",
-	.depends = "modem_fw",
-	.pas_id = PAS_MODEM_SW,
-	.bus_port = MSM_BUS_MASTER_MSS_SW_PROC,
+static struct pil_q6v4_pdata msm_8960_q6_mss_data[2] = {
+	{
+		.strap_tcm_base  = 0x00400000,
+		.strap_ahb_upper = 0x00090000,
+		.strap_ahb_lower = 0x00000080,
+		.aclk_reg = MSM_CLK_CTL_BASE + 0x2C6C,
+		.jtag_clk_reg = MSM_CLK_CTL_BASE + 0x2044,
+		.name = "modem_fw",
+		.pas_id = PAS_MODEM_FW,
+		.bus_port = MSM_BUS_MASTER_MSS_FW_PROC,
+	},
+	{
+		.strap_tcm_base  = 0x00420000,
+		.strap_ahb_upper = 0x00090000,
+		.strap_ahb_lower = 0x00000080,
+		.aclk_reg = MSM_CLK_CTL_BASE + 0x2040,
+		.jtag_clk_reg = MSM_CLK_CTL_BASE + 0x2C68,
+		.name = "modem",
+		.pas_id = PAS_MODEM_SW,
+		.bus_port = MSM_BUS_MASTER_MSS_SW_PROC,
+	}
 };
 
-struct platform_device msm_8960_q6_mss_sw = {
-	.name = "pil_qdsp6v4",
-	.id = 2,
-	.num_resources  = ARRAY_SIZE(msm_8960_q6_mss_sw_resources),
-	.resource       = msm_8960_q6_mss_sw_resources,
-	.dev.platform_data = &msm_8960_q6_mss_sw_data,
+struct platform_device msm_8960_q6_mss = {
+	.name = "pil-q6v4-modem",
+	.id = -1,
+	.num_resources  = ARRAY_SIZE(msm_8960_q6_mss_resources),
+	.resource       = msm_8960_q6_mss_resources,
+	.dev.platform_data = msm_8960_q6_mss_data,
 };
 
 static struct resource msm_8960_riva_resources[] = {
@@ -1445,6 +1447,11 @@
 		.flags  = IORESOURCE_MEM,
 	},
 	{
+		.start  = 0x00900000,
+		.end    = 0x00900000 + SZ_16K - 1,
+		.flags  = IORESOURCE_MEM,
+	},
+	{
 		.start  = RIVA_APSS_WDOG_BITE_RESET_RDY_IRQ,
 		.end    = RIVA_APSS_WDOG_BITE_RESET_RDY_IRQ,
 		.flags  = IORESOURCE_IRQ,
@@ -1463,9 +1470,29 @@
 	.id = -1,
 };
 
+static struct resource msm_pil_dsps_resources[] = {
+	{
+		.start  = 0x00900000,
+		.end    = 0x00900000 + SZ_16K - 1,
+		.flags  = IORESOURCE_MEM,
+	},
+	{
+		.start = PPSS_WDOG_TIMER_IRQ,
+		.end   = PPSS_WDOG_TIMER_IRQ,
+		.flags = IORESOURCE_IRQ,
+	},
+	{
+		.start = 0x12080000,
+		.end   = 0x12080000 + SZ_8K - 1,
+		.flags = IORESOURCE_MEM,
+	},
+};
+
 struct platform_device msm_pil_dsps = {
-	.name          = "pil_dsps",
-	.id            = -1,
+	.name		= "pil_dsps",
+	.id		= -1,
+	.resource	= msm_pil_dsps_resources,
+	.num_resources	= ARRAY_SIZE(msm_pil_dsps_resources),
 	.dev.platform_data = "dsps",
 };
 
@@ -1543,7 +1570,7 @@
 	},
 	{
 		.irq_config_id = SMD_Q6,
-		.subsys_name = "q6",
+		.subsys_name = "adsp",
 		.edge = SMD_APPS_QDSP,
 
 		.smd_int.irq_name = "adsp_a11",
@@ -2539,6 +2566,17 @@
 };
 unsigned msm8960_num_footswitch __initdata = ARRAY_SIZE(msm8960_footswitch);
 
+struct platform_device *msm8960ab_footswitch[] __initdata = {
+	FS_8X60(FS_MDP,    "vdd",	"mdp.0",	&mdp_fs_data),
+	FS_8X60(FS_ROT,    "vdd",	"msm_rotator.0", &rot_fs_data),
+	FS_8X60(FS_IJPEG,  "vdd",	"msm_gemini.0",	&ijpeg_fs_data),
+	FS_8X60(FS_VFE,    "vdd",	"msm_vfe.0",	&vfe_fs_data),
+	FS_8X60(FS_VPE,    "vdd",	"msm_vpe.0",	&vpe_fs_data),
+	FS_8X60(FS_GFX3D,  "vdd",	"kgsl-3d0.0",	&gfx3d_fs_data),
+	FS_8X60(FS_VED,    "vdd",	"msm_vidc.0",	&ved_fs_data),
+};
+unsigned msm8960ab_num_footswitch __initdata = ARRAY_SIZE(msm8960ab_footswitch);
+
 #ifdef CONFIG_MSM_ROTATOR
 static struct msm_bus_vectors rotator_init_vectors[] = {
 	{
@@ -3735,15 +3773,58 @@
 };
 
 static struct msm_rpmstats_platform_data msm_rpm_stat_pdata = {
-	.phys_addr_base = 0x0010DD04,
-	.phys_size = SZ_256,
+	.version = 1,
 };
 
+static struct resource msm_rpm_stat_resource[] = {
+	{
+		.start	= 0x0010D204,
+		.end	= 0x0010D204 + SZ_8K,
+		.flags	= IORESOURCE_MEM,
+		.name	= "phys_addr_base"
+	},
+};
+
+
+
 struct platform_device msm8960_rpm_stat_device = {
 	.name = "msm_rpm_stat",
 	.id = -1,
-	.dev = {
+	.resource = msm_rpm_stat_resource,
+	.num_resources	= ARRAY_SIZE(msm_rpm_stat_resource),
+	.dev	= {
 		.platform_data = &msm_rpm_stat_pdata,
+	}
+};
+
+static struct resource resources_rpm_master_stats[] = {
+	{
+		.start	= MSM8960_RPM_MASTER_STATS_BASE,
+		.end	= MSM8960_RPM_MASTER_STATS_BASE + SZ_256,
+		.flags	= IORESOURCE_MEM,
+	},
+};
+
+static char *master_names[] = {
+	"KPSS",
+	"GPSS",
+	"LPASS",
+	"RIVA",
+	"DSPS",
+};
+
+static struct msm_rpm_master_stats_platform_data msm_rpm_master_stat_pdata = {
+	.masters = master_names,
+	.nomasters = ARRAY_SIZE(master_names),
+};
+
+struct platform_device msm8960_rpm_master_stat_device = {
+	.name = "msm_rpm_master_stat",
+	.id = -1,
+	.num_resources	= ARRAY_SIZE(resources_rpm_master_stats),
+	.resource	= resources_rpm_master_stats,
+	.dev = {
+		.platform_data = &msm_rpm_master_stat_pdata,
 	},
 };
 
@@ -3771,18 +3852,7 @@
 /* Sensors DSPS platform data */
 #ifdef CONFIG_MSM_DSPS
 
-#define PPSS_DSPS_TCM_CODE_BASE   0x12000000
-#define PPSS_DSPS_TCM_CODE_SIZE   0x28000
-#define PPSS_DSPS_TCM_BUF_BASE    0x12040000
-#define PPSS_DSPS_TCM_BUF_SIZE    0x4000
-#define PPSS_DSPS_PIPE_BASE       0x12800000
-#define PPSS_DSPS_PIPE_SIZE       0x4000
-#define PPSS_DSPS_DDR_BASE        0x8fe00000
-#define PPSS_DSPS_DDR_SIZE        0x100000
-#define PPSS_SMEM_BASE            0x80000000
-#define PPSS_SMEM_SIZE            0x200000
-#define PPSS_REG_PHYS_BASE        0x12080000
-#define PPSS_WDOG_UNMASKED_INT_EN 0x1808
+#define PPSS_REG_PHYS_BASE	0x12080000
 
 static struct dsps_clk_info dsps_clks[] = {};
 static struct dsps_regulator_info dsps_regs[] = {};
@@ -3800,17 +3870,6 @@
 	.regs = dsps_regs,
 	.regs_num = ARRAY_SIZE(dsps_regs),
 	.dsps_pwr_ctl_en = 1,
-	.tcm_code_start = PPSS_DSPS_TCM_CODE_BASE,
-	.tcm_code_size = PPSS_DSPS_TCM_CODE_SIZE,
-	.tcm_buf_start = PPSS_DSPS_TCM_BUF_BASE,
-	.tcm_buf_size = PPSS_DSPS_TCM_BUF_SIZE,
-	.pipe_start = PPSS_DSPS_PIPE_BASE,
-	.pipe_size = PPSS_DSPS_PIPE_SIZE,
-	.ddr_start = PPSS_DSPS_DDR_BASE,
-	.ddr_size = PPSS_DSPS_DDR_SIZE,
-	.smem_start = PPSS_SMEM_BASE,
-	.smem_size  = PPSS_SMEM_SIZE,
-	.ppss_wdog_unmasked_int_en_reg = PPSS_WDOG_UNMASKED_INT_EN,
 	.signature = DSPS_SIGNATURE,
 };
 
@@ -3821,12 +3880,6 @@
 		.name  = "ppss_reg",
 		.flags = IORESOURCE_MEM,
 	},
-	{
-		.start = PPSS_WDOG_TIMER_IRQ,
-		.end   = PPSS_WDOG_TIMER_IRQ,
-		.name  = "ppss_wdog",
-		.flags = IORESOURCE_IRQ,
-	},
 };
 
 struct platform_device msm_dsps_device = {
@@ -4071,16 +4124,6 @@
 	.resource	= msm_ebi1_ch1_erp_resources,
 };
 
-static int msm8960_LPM_latency = 1000; /* >100 usec for WFI */
-
-struct platform_device msm8960_cpu_idle_device = {
-	.name   = "msm_cpu_idle",
-	.id     = -1,
-	.dev = {
-		.platform_data = &msm8960_LPM_latency,
-	},
-};
-
 static struct resource msm_cache_erp_resources[] = {
 	{
 		.name = "l1_irq",
diff --git a/arch/arm/mach-msm/devices-9615.c b/arch/arm/mach-msm/devices-9615.c
index 46853ac..af8687e 100644
--- a/arch/arm/mach-msm/devices-9615.c
+++ b/arch/arm/mach-msm/devices-9615.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -64,6 +64,7 @@
 
 #define MSM_GPIO_I2C_CLK 16
 #define MSM_GPIO_I2C_SDA 17
+#define MSM9615_RPM_MASTER_STATS_BASE	0x10A700
 
 static struct msm_watchdog_pdata msm_watchdog_pdata = {
 	.pet_time = 10000,
@@ -570,6 +571,15 @@
 	.name   = "msm-dai-q6",
 	.id     = PRIMARY_I2S_TX,
 };
+struct platform_device msm_i2s_cpudai4 = {
+	.name   = "msm-dai-q6",
+	.id     = SECONDARY_I2S_RX,
+};
+
+struct platform_device msm_i2s_cpudai5 = {
+	.name   = "msm-dai-q6",
+	.id     = SECONDARY_I2S_TX,
+};
 struct platform_device msm_voip = {
 	.name	= "msm-voip-dsp",
 	.id	= -1,
@@ -704,6 +714,41 @@
 	.id		= -1,
 };
 
+static struct resource msm_9615_q6_lpass_resources[] = {
+	{
+		.start  = LPASS_Q6SS_WDOG_EXPIRED,
+		.end    = LPASS_Q6SS_WDOG_EXPIRED,
+		.flags  = IORESOURCE_IRQ,
+	},
+};
+
+struct platform_device msm_9615_q6_lpass = {
+	.name = "pil-q6v4-lpass",
+	.id = -1,
+	.num_resources  = ARRAY_SIZE(msm_9615_q6_lpass_resources),
+	.resource       = msm_9615_q6_lpass_resources,
+};
+
+static struct resource msm_9615_q6_mss_resources[] = {
+	{
+		.start  = Q6FW_WDOG_EXPIRED_IRQ,
+		.end    = Q6FW_WDOG_EXPIRED_IRQ,
+		.flags  = IORESOURCE_IRQ,
+	},
+	{
+		.start  = Q6SW_WDOG_EXPIRED_IRQ,
+		.end    = Q6SW_WDOG_EXPIRED_IRQ,
+		.flags  = IORESOURCE_IRQ,
+	},
+};
+
+struct platform_device msm_9615_q6_mss = {
+	.name = "pil-q6v4-modem",
+	.id = -1,
+	.num_resources  = ARRAY_SIZE(msm_9615_q6_mss_resources),
+	.resource       = msm_9615_q6_mss_resources,
+};
+
 #ifdef CONFIG_HW_RANDOM_MSM
 /* PRNG device */
 #define MSM_PRNG_PHYS		0x1A500000
@@ -1316,15 +1361,57 @@
 };
 
 static struct msm_rpmstats_platform_data msm_rpm_stat_pdata = {
-	.phys_addr_base = 0x0010DD04,
-	.phys_size = SZ_256,
+	.version = 1,
 };
 
+
+static struct resource msm_rpm_stat_resource[] = {
+	{
+		.start	= 0x0010D204,
+		.end	= 0x0010D204 + SZ_8K,
+		.flags	= IORESOURCE_MEM,
+		.name	= "phys_addr_base"
+	},
+};
+
+
+
 struct platform_device msm9615_rpm_stat_device = {
 	.name = "msm_rpm_stat",
 	.id = -1,
-	.dev = {
+	.resource = msm_rpm_stat_resource,
+	.num_resources	= ARRAY_SIZE(msm_rpm_stat_resource),
+	.dev	= {
 		.platform_data = &msm_rpm_stat_pdata,
+	}
+};
+
+static struct resource resources_rpm_master_stats[] = {
+	{
+		.start	= MSM9615_RPM_MASTER_STATS_BASE,
+		.end	= MSM9615_RPM_MASTER_STATS_BASE + SZ_256,
+		.flags	= IORESOURCE_MEM,
+	},
+};
+
+static char *master_names[] = {
+	"KPSS",
+	"MPSS",
+	"LPASS",
+};
+
+static struct msm_rpm_master_stats_platform_data msm_rpm_master_stat_pdata = {
+	.masters = master_names,
+	.nomasters = ARRAY_SIZE(master_names),
+};
+
+struct platform_device msm9615_rpm_master_stat_device = {
+	.name = "msm_rpm_master_stat",
+	.id = -1,
+	.num_resources	= ARRAY_SIZE(resources_rpm_master_stats),
+	.resource	= resources_rpm_master_stats,
+	.dev = {
+		.platform_data = &msm_rpm_master_stat_pdata,
 	},
 };
 
@@ -1347,6 +1434,19 @@
 	},
 };
 
+static struct msm_pm_init_data_type msm_pm_data = {
+	.use_sync_timer = false,
+	.pc_mode = MSM_PM_PC_NOTZ_L2_EXT,
+};
+
+struct platform_device msm9615_pm_8x60 = {
+	.name	= "pm-8x60",
+	.id	= -1,
+	.dev	= {
+		.platform_data = &msm_pm_data,
+	},
+};
+
 uint32_t __init msm9615_rpm_get_swfi_latency(void)
 {
 	int i;
diff --git a/arch/arm/mach-msm/devices-fsm9xxx.c b/arch/arm/mach-msm/devices-fsm9xxx.c
index 639eeae..9043223 100644
--- a/arch/arm/mach-msm/devices-fsm9xxx.c
+++ b/arch/arm/mach-msm/devices-fsm9xxx.c
@@ -228,6 +228,7 @@
 	.parts		= NULL,
 	.nr_parts	= 0,
 	.interleave     = 0,
+	.version	= VERSION_2,
 };
 
 struct platform_device msm_device_nand = {
diff --git a/arch/arm/mach-msm/devices-iommu.c b/arch/arm/mach-msm/devices-iommu.c
index 6434a63..091a8e8 100644
--- a/arch/arm/mach-msm/devices-iommu.c
+++ b/arch/arm/mach-msm/devices-iommu.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2010-2012, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -312,11 +312,6 @@
 	},
 };
 
-static struct platform_device msm_root_iommu_dev = {
-	.name = "msm_iommu",
-	.id = -1,
-};
-
 static struct msm_iommu_dev jpegd_iommu = {
 	.name = "jpegd",
 	.ncb = 2,
@@ -395,7 +390,6 @@
 	.name = "msm_iommu",
 	.id = 0,
 	.dev = {
-		.parent = &msm_root_iommu_dev.dev,
 		.platform_data = &jpegd_iommu,
 	},
 	.num_resources = ARRAY_SIZE(msm_iommu_jpegd_resources),
@@ -406,7 +400,6 @@
 	.name = "msm_iommu",
 	.id = 1,
 	.dev = {
-		.parent = &msm_root_iommu_dev.dev,
 		.platform_data = &vpe_iommu,
 	},
 	.num_resources = ARRAY_SIZE(msm_iommu_vpe_resources),
@@ -417,7 +410,6 @@
 	.name = "msm_iommu",
 	.id = 2,
 	.dev = {
-		.parent = &msm_root_iommu_dev.dev,
 		.platform_data = &mdp0_iommu,
 	},
 	.num_resources = ARRAY_SIZE(msm_iommu_mdp0_resources),
@@ -428,7 +420,6 @@
 	.name = "msm_iommu",
 	.id = 3,
 	.dev = {
-		.parent = &msm_root_iommu_dev.dev,
 		.platform_data = &mdp1_iommu,
 	},
 	.num_resources = ARRAY_SIZE(msm_iommu_mdp1_resources),
@@ -439,7 +430,6 @@
 	.name = "msm_iommu",
 	.id = 4,
 	.dev = {
-		.parent = &msm_root_iommu_dev.dev,
 		.platform_data = &rot_iommu,
 	},
 	.num_resources = ARRAY_SIZE(msm_iommu_rot_resources),
@@ -450,7 +440,6 @@
 	.name = "msm_iommu",
 	.id = 5,
 	.dev = {
-		.parent = &msm_root_iommu_dev.dev,
 		.platform_data = &ijpeg_iommu,
 	},
 	.num_resources = ARRAY_SIZE(msm_iommu_ijpeg_resources),
@@ -461,7 +450,6 @@
 	.name = "msm_iommu",
 	.id = 6,
 	.dev = {
-		.parent = &msm_root_iommu_dev.dev,
 		.platform_data = &vfe_iommu,
 	},
 	.num_resources = ARRAY_SIZE(msm_iommu_vfe_resources),
@@ -472,7 +460,6 @@
 	.name = "msm_iommu",
 	.id = 7,
 	.dev = {
-		.parent = &msm_root_iommu_dev.dev,
 		.platform_data = &vcodec_a_iommu,
 	},
 	.num_resources = ARRAY_SIZE(msm_iommu_vcodec_a_resources),
@@ -483,7 +470,6 @@
 	.name = "msm_iommu",
 	.id = 8,
 	.dev = {
-		.parent = &msm_root_iommu_dev.dev,
 		.platform_data = &vcodec_b_iommu,
 	},
 	.num_resources = ARRAY_SIZE(msm_iommu_vcodec_b_resources),
@@ -494,7 +480,6 @@
 	.name = "msm_iommu",
 	.id = 9,
 	.dev = {
-		.parent = &msm_root_iommu_dev.dev,
 		.platform_data = &gfx3d_iommu,
 	},
 	.num_resources = ARRAY_SIZE(msm_iommu_gfx3d_resources),
@@ -505,7 +490,6 @@
 	.name = "msm_iommu",
 	.id = 10,
 	.dev = {
-		.parent = &msm_root_iommu_dev.dev,
 		.platform_data = &gfx3d1_iommu,
 	},
 	.num_resources = ARRAY_SIZE(msm_iommu_gfx3d1_resources),
@@ -516,7 +500,6 @@
 	.name = "msm_iommu",
 	.id = 10,
 	.dev = {
-		.parent = &msm_root_iommu_dev.dev,
 		.platform_data = &gfx2d0_iommu,
 	},
 	.num_resources = ARRAY_SIZE(msm_iommu_gfx2d0_resources),
@@ -527,7 +510,6 @@
 	.name = "msm_iommu",
 	.id = 11,
 	.dev = {
-		.parent = &msm_root_iommu_dev.dev,
 		.platform_data = &gfx2d1_iommu,
 	},
 	.num_resources = ARRAY_SIZE(msm_iommu_gfx2d1_resources),
@@ -538,7 +520,6 @@
 	.name = "msm_iommu",
 	.id = 11,
 	.dev = {
-		.parent = &msm_root_iommu_dev.dev,
 		.platform_data = &vcap_iommu,
 	},
 	.num_resources = ARRAY_SIZE(msm_iommu_vcap_resources),
@@ -999,12 +980,6 @@
 		return -ENODEV;
 	}
 
-	ret = platform_device_register(&msm_root_iommu_dev);
-	if (ret != 0) {
-		pr_err("Failed to register root IOMMU device!\n");
-		goto failure;
-	}
-
 	/* Initialize common devs */
 	platform_add_devices(msm_iommu_common_devs,
 				ARRAY_SIZE(msm_iommu_common_devs));
@@ -1017,13 +992,13 @@
 				ARRAY_SIZE(msm_iommu_gfx2d_devs));
 	}
 
-	if (cpu_is_apq8064() || cpu_is_msm8960ab() || cpu_is_apq8064ab()) {
+	if (soc_class_is_apq8064() || cpu_is_msm8960ab()) {
 		platform_add_devices(msm_iommu_jpegd_devs,
 				ARRAY_SIZE(msm_iommu_jpegd_devs));
 		platform_add_devices(msm_iommu_adreno3xx_gfx_devs,
 				ARRAY_SIZE(msm_iommu_adreno3xx_gfx_devs));
 	}
-	if (cpu_is_apq8064() || cpu_is_apq8064ab())
+	if (soc_class_is_apq8064())
 		platform_add_devices(msm_iommu_vcap_devs,
 				ARRAY_SIZE(msm_iommu_vcap_devs));
 
@@ -1039,21 +1014,18 @@
 				ARRAY_SIZE(msm_iommu_gfx2d_ctx_devs));
 	}
 
-	if (cpu_is_apq8064() || cpu_is_msm8960ab() || cpu_is_apq8064ab()) {
+	if (soc_class_is_apq8064() || cpu_is_msm8960ab()) {
 		platform_add_devices(msm_iommu_jpegd_ctx_devs,
 				ARRAY_SIZE(msm_iommu_jpegd_ctx_devs));
 
 		platform_add_devices(msm_iommu_adreno3xx_ctx_devs,
 				ARRAY_SIZE(msm_iommu_adreno3xx_ctx_devs));
 	}
-	if (cpu_is_apq8064() || cpu_is_apq8064ab())
+	if (soc_class_is_apq8064())
 		platform_add_devices(msm_iommu_vcap_ctx_devs,
 			ARRAY_SIZE(msm_iommu_vcap_ctx_devs));
 
 	return 0;
-
-failure:
-	return ret;
 }
 
 static void __exit iommu_exit(void)
@@ -1081,12 +1053,12 @@
 		for (i = 0; i < ARRAY_SIZE(msm_iommu_jpegd_devs); i++)
 			platform_device_unregister(msm_iommu_jpegd_devs[i]);
 	}
-	if (cpu_is_apq8064() || cpu_is_apq8064ab()) {
+	if (soc_class_is_apq8064()) {
 		for (i = 0; i < ARRAY_SIZE(msm_iommu_vcap_ctx_devs); i++)
 			platform_device_unregister(msm_iommu_vcap_ctx_devs[i]);
 	}
 
-	if (cpu_is_apq8064() || cpu_is_msm8960ab() || cpu_is_apq8064ab()) {
+	if (soc_class_is_apq8064() || cpu_is_msm8960ab()) {
 		for (i = 0; i < ARRAY_SIZE(msm_iommu_adreno3xx_ctx_devs);
 			   i++)
 			platform_device_unregister(
@@ -1097,7 +1069,7 @@
 			platform_device_unregister(
 				msm_iommu_jpegd_ctx_devs[i]);
 
-		if (cpu_is_apq8064() || cpu_is_apq8064ab()) {
+		if (soc_class_is_apq8064()) {
 			for (i = 0; i < ARRAY_SIZE(msm_iommu_vcap_devs);
 			   i++)
 				platform_device_unregister(
@@ -1112,8 +1084,6 @@
 		for (i = 0; i < ARRAY_SIZE(msm_iommu_jpegd_devs); i++)
 			platform_device_unregister(msm_iommu_jpegd_devs[i]);
 	}
-
-	platform_device_unregister(&msm_root_iommu_dev);
 }
 
 subsys_initcall(iommu_init);
diff --git a/arch/arm/mach-msm/devices-msm7x27a.c b/arch/arm/mach-msm/devices-msm7x27a.c
index 50ab26f..5296048 100644
--- a/arch/arm/mach-msm/devices-msm7x27a.c
+++ b/arch/arm/mach-msm/devices-msm7x27a.c
@@ -780,6 +780,16 @@
 		.notify_rpm = false,
 		.cmd = spm_pc_without_modem,
 	},
+	[2] = {
+		.mode = MSM_SPM_MODE_POWER_COLLAPSE,
+		.notify_rpm = false,
+		.cmd = spm_pc_without_modem,
+	},
+	[3] = {
+		.mode = MSM_SPM_MODE_POWER_COLLAPSE,
+		.notify_rpm = false,
+		.cmd = spm_pc_without_modem,
+	},
 };
 
 static struct msm_spm_platform_data msm_spm_data[] __initdata = {
@@ -797,6 +807,20 @@
 		.num_modes = ARRAY_SIZE(msm_spm_seq_list),
 		.modes = msm_spm_seq_list,
 	},
+	[2] = {
+		.reg_base_addr = MSM_SAW2_BASE,
+		.reg_init_values[MSM_SPM_REG_SAW2_CFG] = 0x0,
+		.reg_init_values[MSM_SPM_REG_SAW2_SPM_CTL] = 0x01,
+		.num_modes = ARRAY_SIZE(msm_spm_seq_list),
+		.modes = msm_spm_seq_list,
+	},
+	[3] = {
+		.reg_base_addr = MSM_SAW3_BASE,
+		.reg_init_values[MSM_SPM_REG_SAW2_CFG] = 0x0,
+		.reg_init_values[MSM_SPM_REG_SAW2_SPM_CTL] = 0x01,
+		.num_modes = ARRAY_SIZE(msm_spm_seq_list),
+		.modes = msm_spm_seq_list,
+	},
 };
 
 void __init msm8x25_spm_device_init(void)
@@ -1606,6 +1630,26 @@
 	},
 };
 
+static struct resource pl310_resources[] = {
+	{
+		.start = 0xC0400000,
+		.end   = 0xC0400000 + SZ_4K - 1,
+		.flags = IORESOURCE_MEM,
+	},
+	{
+		.name   = "l2_irq",
+		.start  = MSM8625_INT_L2CC_INTR,
+		.flags  = IORESOURCE_IRQ,
+	},
+};
+
+static struct platform_device pl310_erp_device = {
+	.name           = "pl310_erp",
+	.id             = -1,
+	.resource       = pl310_resources,
+	.num_resources  = ARRAY_SIZE(pl310_resources),
+};
+
 enum {
 	MSM8625,
 	MSM8625A,
@@ -1623,6 +1667,7 @@
 	case 0x771:
 	case 0x77C:
 	case 0x780:
+	case 0x785: /* Edge-only MSM8125-0 */
 	case 0x8D0:
 		cpu = MSM8625;
 		break;
@@ -1710,9 +1755,9 @@
 			.step_quot = ~0,
 			.tgt_volt_offset = 0,
 			.turbo_Vmax = 1350000,
-			.turbo_Vmin = 1100000,
+			.turbo_Vmin = 1150000,
 			.nom_Vmax = 1350000,
-			.nom_Vmin = 1100000,
+			.nom_Vmin = 1150000,
 			.calibrated_uV = 1300000,
 	},
 };
@@ -1750,7 +1795,7 @@
 
 static struct msm_cpr_config msm_cpr_pdata = {
 	.ref_clk_khz = 19200,
-	.delay_us = 1000,
+	.delay_us = 25000,
 	.irq_line = 0,
 	.cpr_mode_data = msm_cpr_mode_data,
 	.tgt_count_div_N = 1,
@@ -1758,12 +1803,13 @@
 	.ceiling = 40,
 	.sw_vlevel = 20,
 	.up_threshold = 1,
-	.dn_threshold = 4,
+	.dn_threshold = 3,
 	.up_margin = 0,
 	.dn_margin = 0,
 	.max_nom_freq = 700800,
 	.max_freq = 1401600,
 	.max_quot = 0,
+	.disable_cpr = false,
 	.vp_data = &vp_data,
 	.get_quot = msm_cpr_get_quot,
 	.clk_enable = msm_cpr_clk_enable,
@@ -1796,6 +1842,7 @@
 	}
 
 	msm_smem_get_cpr_info(cpr_info);
+	msm_cpr_pdata.disable_cpr = cpr_info->disable_cpr;
 
 	/**
 	 * Set the ring_osc based on efuse BIT(0)
@@ -1828,11 +1875,11 @@
 	 * Ditto for a 1.0GHz part.
 	 */
 	if (msm8625_cpu_id() == MSM8625A) {
-		msm_cpr_pdata.max_quot += 100;
+		msm_cpr_pdata.max_quot += 30;
 		if (msm_cpr_pdata.max_quot > 1400)
 			msm_cpr_pdata.max_quot = 1400;
 	} else if (msm8625_cpu_id() == MSM8625) {
-		msm_cpr_pdata.max_quot += 120;
+		msm_cpr_pdata.max_quot += 50;
 		if (msm_cpr_pdata.max_quot > 1350)
 			msm_cpr_pdata.max_quot = 1350;
 	}
@@ -1844,10 +1891,25 @@
 	msm_cpr_mode_data[TURBO_MODE].calibrated_uV =
 				msm_c2_pmic_mv[cpr_info->pvs_fuse & 0x1F];
 
+	if ((cpr_info->floor_fuse & 0x3) == 0x0) {
+		msm_cpr_mode_data[TURBO_MODE].nom_Vmin = 1000000;
+		msm_cpr_mode_data[TURBO_MODE].turbo_Vmin = 1100000;
+	} else if ((cpr_info->floor_fuse & 0x3) == 0x1) {
+		msm_cpr_mode_data[TURBO_MODE].nom_Vmin = 1050000;
+		msm_cpr_mode_data[TURBO_MODE].turbo_Vmin = 1100000;
+	} else if ((cpr_info->floor_fuse & 0x3) == 0x2) {
+		msm_cpr_mode_data[TURBO_MODE].nom_Vmin = 1100000;
+		msm_cpr_mode_data[TURBO_MODE].turbo_Vmin = 1100000;
+	}
+
 	pr_debug("%s: cpr: ring_osc: 0x%x\n", __func__,
 		msm_cpr_mode_data[TURBO_MODE].ring_osc);
 	pr_debug("%s: cpr: turbo_quot: 0x%x\n", __func__, cpr_info->turbo_quot);
 	pr_debug("%s: cpr: pvs_fuse: 0x%x\n", __func__, cpr_info->pvs_fuse);
+	pr_debug("%s: cpr: floor_fuse: 0x%x\n", __func__, cpr_info->floor_fuse);
+	pr_debug("%s: cpr: nom_Vmin: %d, turbo_Vmin: %d\n", __func__,
+		msm_cpr_mode_data[TURBO_MODE].nom_Vmin,
+		msm_cpr_mode_data[TURBO_MODE].turbo_Vmin);
 	kfree(cpr_info);
 
 	if (msm8625_cpu_id() == MSM8625A)
@@ -1928,6 +1990,41 @@
 	.size = ARRAY_SIZE(msm_clock_8625_dummy),
 };
 
+
+static int __init msm_gpio_config_gps(void)
+{
+	unsigned int gps_gpio = 7;
+	int ret = 0;
+
+	if (!machine_is_msm8625_evb())
+		return ret;
+
+	ret = gpio_tlmm_config(GPIO_CFG(gps_gpio, 0, GPIO_CFG_OUTPUT,
+			GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), GPIO_CFG_ENABLE);
+	if (ret < 0) {
+		pr_err("gpio tlmm failed for gpio-%d\n", gps_gpio);
+		return ret;
+	}
+
+	ret = gpio_request(gps_gpio, "gnss-gpio");
+	if (ret < 0) {
+		pr_err("failed to request gpio-%d\n", gps_gpio);
+		return ret;
+	}
+
+	ret = gpio_direction_input(gps_gpio);
+	if (ret < 0) {
+		pr_err("failed to change direction for gpio-%d\n", gps_gpio);
+		return ret;
+	}
+
+	ret = gpio_export(gps_gpio, true);
+	if (ret < 0)
+		pr_err("failed to export gpio for user\n");
+
+	return ret;
+}
+
 int __init msm7x2x_misc_init(void)
 {
 	if (machine_is_msm8625_rumi3()) {
@@ -1954,6 +2051,14 @@
 			(SOCINFO_VERSION_MAJOR(socinfo_get_version()) >= 2))
 		msm_cpr_init();
 
+	if (!cpu_is_msm8625())
+		pl310_resources[1].start = INT_L2CC_INTR;
+
+	platform_device_register(&pl310_erp_device);
+
+	if (msm_gpio_config_gps() < 0)
+		pr_err("Error for gpio config for GPS gpio\n");
+
 	return 0;
 }
 
diff --git a/arch/arm/mach-msm/devices-msm7x2xa.h b/arch/arm/mach-msm/devices-msm7x2xa.h
index 8b59b14..614037c 100644
--- a/arch/arm/mach-msm/devices-msm7x2xa.h
+++ b/arch/arm/mach-msm/devices-msm7x2xa.h
@@ -33,6 +33,6 @@
 void __init msm8x25_spm_device_init(void);
 void __init msm_pm_register_cpr_ops(void);
 void __init msm8x25_kgsl_3d0_init(void);
-void __iomem *core1_reset_base(void);
+void __iomem *core_reset_base(unsigned int);
 extern void setup_mm_for_reboot(void);
 #endif
diff --git a/arch/arm/mach-msm/devices-msm8x60.c b/arch/arm/mach-msm/devices-msm8x60.c
index 37cdc98..c6513d9 100644
--- a/arch/arm/mach-msm/devices-msm8x60.c
+++ b/arch/arm/mach-msm/devices-msm8x60.c
@@ -213,6 +213,11 @@
 		.flags  = IORESOURCE_MEM,
 	},
 	{
+		.start  = 0x00900000,
+		.end    = 0x00900000 + SZ_16K - 1,
+		.flags  = IORESOURCE_MEM,
+	},
+	{
 		.start	= LPASS_Q6SS_WDOG_EXPIRED,
 		.end	= LPASS_Q6SS_WDOG_EXPIRED,
 		.flags	= IORESOURCE_IRQ,
@@ -241,6 +246,11 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	{
+		.start  = 0x00900000,
+		.end    = 0x00900000 + SZ_16K - 1,
+		.flags  = IORESOURCE_MEM,
+	},
+	{
 		.start	= MARM_WDOG_EXPIRED,
 		.end	= MARM_WDOG_EXPIRED,
 		.flags	= IORESOURCE_IRQ,
@@ -259,9 +269,19 @@
 	.id = -1,
 };
 
+static struct resource msm_pil_dsps_resources[] = {
+	{
+		.start  = 0x00900000,
+		.end    = 0x00900000 + SZ_16K - 1,
+		.flags  = IORESOURCE_MEM,
+	},
+};
+
 struct platform_device msm_pil_dsps = {
 	.name          = "pil_dsps",
 	.id            = -1,
+	.resource	= msm_pil_dsps_resources,
+	.num_resources	= ARRAY_SIZE(msm_pil_dsps_resources),
 	.dev.platform_data = "dsps",
 };
 
@@ -1622,16 +1642,6 @@
 /* Sensors DSPS platform data */
 #ifdef CONFIG_MSM_DSPS
 
-#define PPSS_DSPS_TCM_CODE_BASE 0x12000000
-#define PPSS_DSPS_TCM_CODE_SIZE 0x28000
-#define PPSS_DSPS_TCM_BUF_BASE  0x12040000
-#define PPSS_DSPS_TCM_BUF_SIZE  0x4000
-#define PPSS_DSPS_PIPE_BASE     0x12800000
-#define PPSS_DSPS_PIPE_SIZE     0x0 /* 8660 V2 does not use PIPE memory */
-#define PPSS_DSPS_DDR_BASE      0x8fe00000
-#define PPSS_DSPS_DDR_SIZE      0x0 /* 8660 V2 does not use DDR memory */
-#define PPSS_SMEM_BASE          0x40000000
-#define PPSS_SMEM_SIZE          0x4000
 #define PPSS_REG_PHYS_BASE	0x12080000
 #define PPSS_PAUSE_REG          0x1804
 
@@ -1696,16 +1706,6 @@
 	.regs = dsps_regs,
 	.regs_num = ARRAY_SIZE(dsps_regs),
 	.init = dsps_init1,
-	.tcm_code_start = PPSS_DSPS_TCM_CODE_BASE,
-	.tcm_code_size = PPSS_DSPS_TCM_CODE_SIZE,
-	.tcm_buf_start = PPSS_DSPS_TCM_BUF_BASE,
-	.tcm_buf_size = PPSS_DSPS_TCM_BUF_SIZE,
-	.pipe_start = PPSS_DSPS_PIPE_BASE,
-	.pipe_size = PPSS_DSPS_PIPE_SIZE,
-	.ddr_start = PPSS_DSPS_DDR_BASE,
-	.ddr_size = PPSS_DSPS_DDR_SIZE,
-	.smem_start = PPSS_SMEM_BASE,
-	.smem_size  = PPSS_SMEM_SIZE,
 	.ppss_pause_reg = PPSS_PAUSE_REG,
 	.signature = DSPS_SIGNATURE,
 };
diff --git a/arch/arm/mach-msm/devices.h b/arch/arm/mach-msm/devices.h
index 6f3dda3..16b4eee 100644
--- a/arch/arm/mach-msm/devices.h
+++ b/arch/arm/mach-msm/devices.h
@@ -109,6 +109,8 @@
 extern struct platform_device msm_device_sdc3;
 extern struct platform_device msm_device_sdc4;
 
+extern struct platform_device msm9615_pm_8x60;
+
 extern struct platform_device msm8960_pc_cntr;
 extern struct platform_device msm8064_pc_cntr;
 extern struct platform_device msm8930_pc_cntr;
@@ -216,6 +218,7 @@
 extern struct platform_device msm_cpudai0;
 extern struct platform_device msm_cpudai1;
 extern struct platform_device mpq_cpudai_sec_i2s_rx;
+extern struct platform_device mpq_cpudai_pseudo;
 extern struct platform_device msm8960_cpudai_slimbus_2_rx;
 extern struct platform_device msm8960_cpudai_slimbus_2_tx;
 extern struct platform_device msm_cpudai_hdmi_rx;
@@ -244,15 +247,17 @@
 extern struct platform_device msm_cpudai_incall_record_tx;
 extern struct platform_device msm_i2s_cpudai0;
 extern struct platform_device msm_i2s_cpudai1;
-
+extern struct platform_device msm_i2s_cpudai4;
+extern struct platform_device msm_i2s_cpudai5;
 extern struct platform_device msm_pil_q6v3;
 extern struct platform_device msm_pil_modem;
 extern struct platform_device msm_pil_tzapps;
 extern struct platform_device msm_pil_dsps;
 extern struct platform_device msm_pil_vidc;
 extern struct platform_device msm_8960_q6_lpass;
-extern struct platform_device msm_8960_q6_mss_fw;
-extern struct platform_device msm_8960_q6_mss_sw;
+extern struct platform_device msm_9615_q6_lpass;
+extern struct platform_device msm_8960_q6_mss;
+extern struct platform_device msm_9615_q6_mss;
 extern struct platform_device msm_8960_riva;
 extern struct platform_device msm_gss;
 
@@ -298,6 +303,8 @@
 extern unsigned msm8660_num_footswitch;
 extern struct platform_device *msm8960_footswitch[];
 extern unsigned msm8960_num_footswitch;
+extern struct platform_device *msm8960ab_footswitch[];
+extern unsigned msm8960ab_num_footswitch;
 extern struct platform_device *apq8064_footswitch[];
 extern unsigned apq8064_num_footswitch;
 extern struct platform_device *msm8930_footswitch[];
@@ -335,10 +342,12 @@
 
 extern struct platform_device msm8960_rpm_device;
 extern struct platform_device msm8960_rpm_stat_device;
+extern struct platform_device msm8960_rpm_master_stat_device;
 extern struct platform_device msm8960_rpm_log_device;
 
 extern struct platform_device msm8930_rpm_device;
 extern struct platform_device msm8930_rpm_stat_device;
+extern struct platform_device msm8930_rpm_master_stat_device;
 extern struct platform_device msm8930_rpm_log_device;
 extern struct platform_device msm8930_rpm_rbcpr_device;
 
@@ -348,10 +357,12 @@
 
 extern struct platform_device msm9615_rpm_device;
 extern struct platform_device msm9615_rpm_stat_device;
+extern struct platform_device msm9615_rpm_master_stat_device;
 extern struct platform_device msm9615_rpm_log_device;
 
 extern struct platform_device apq8064_rpm_device;
 extern struct platform_device apq8064_rpm_stat_device;
+extern struct platform_device apq8064_rpm_master_stat_device;
 extern struct platform_device apq8064_rpm_log_device;
 
 extern struct platform_device msm_device_rng;
@@ -394,10 +405,6 @@
 extern struct platform_device *msm_8974_stub_regulator_devices[];
 extern int msm_8974_stub_regulator_devices_len;
 
-extern struct platform_device msm8960_cpu_idle_device;
-extern struct platform_device msm8930_cpu_idle_device;
-extern struct platform_device apq8064_cpu_idle_device;
-
 extern struct platform_device apq8064_msm_gov_device;
 
 extern struct platform_device msm_bus_8930_apps_fabric;
@@ -411,6 +418,7 @@
 extern struct platform_device msm_device_vfe;
 extern struct platform_device msm_device_vpe;
 extern struct platform_device mpq8064_device_qup_i2c_gsbi5;
+extern struct platform_device mpq8064_device_qup_spi_gsbi6;
 
 extern struct platform_device msm8660_iommu_domain_device;
 extern struct platform_device msm8960_iommu_domain_device;
diff --git a/arch/arm/mach-msm/idle-v7.S b/arch/arm/mach-msm/idle-v7.S
index 8eed48d..ccd0bf7 100644
--- a/arch/arm/mach-msm/idle-v7.S
+++ b/arch/arm/mach-msm/idle-v7.S
@@ -22,7 +22,7 @@
 #include "idle.h"
 #include "idle-macros.S"
 
-#ifdef CONFIG_ARCH_MSM_KRAIT
+#ifdef CONFIG_MSM_SCM
 #define SCM_SVC_BOOT 0x1
 #define SCM_CMD_TERMINATE_PC 0x2
 #endif
@@ -127,7 +127,18 @@
 	cmp	r1, #1
 	bne	skip
 	bl	v7_flush_dcache_all
+	ldr	r1, =msm_pm_flush_l2_fn
+	ldr	r1, [r1]
+	cmp	r1, #0
+	blxne	r1
+
 skip:
+	ldr	r1, =msm_pm_disable_l2_fn
+	ldr	r1, [r1]
+	cmp	r1, #0
+	blxne	r1
+	dmb
+
 	mrc	p15, 0, r0, c0, c0, 5	/* MPIDR */
 	and	r0, r0, #15		/* what CPU am I */
 
@@ -141,7 +152,7 @@
 	str	r2, [r1]
 skip_pc_debug1:
 
-#ifdef CONFIG_ARCH_MSM_KRAIT
+#ifdef CONFIG_MSM_SCM
 	ldr	r0, =SCM_SVC_BOOT
 	ldr	r1, =SCM_CMD_TERMINATE_PC
 	ldr	r2, =msm_pm_flush_l2_flag
@@ -182,6 +193,11 @@
 	str	r2, [r1]
 
 skip_pc_debug2:
+	ldr	r1, =msm_pm_enable_l2_fn
+	ldr	r1, [r1]
+	cmp	r1, #0
+	blxne	r1
+	dmb
 
 #ifdef CONFIG_MSM_JTAG
 	bl	msm_jtag_restore_state
@@ -286,11 +302,16 @@
 	SET_SMP_COHERENCY ON
 #endif
 
-#ifdef CONFIG_MSM_JTAG
+	ldr	r1, =msm_pm_enable_l2_fn
+	ldr	r1, [r1]
+	cmp	r1, #0
 	stmfd   sp!, {lr}
+	blxne	r1
+	dmb
+#ifdef CONFIG_MSM_JTAG
 	bl      msm_jtag_restore_state
-	ldmfd   sp!, {lr}
 #endif
+	ldmfd   sp!, {lr}
 	mov     r0, #1
 	bx      lr
 	nop
@@ -330,9 +351,14 @@
 	ldr     pc, [r1]                 /* jump                           */
 
 ENTRY(msm_pm_set_l2_flush_flag)
-	ldr r1, =msm_pm_flush_l2_flag
-	str r0, [r1]
-	bx lr
+	ldr     r1, =msm_pm_flush_l2_flag
+	str     r0, [r1]
+	bx      lr
+
+ENTRY(msm_pm_get_l2_flush_flag)
+	ldr     r1, =msm_pm_flush_l2_flag
+	ldr     r0, [r1]
+	bx      lr
 
 	.data
 
@@ -372,6 +398,18 @@
 msm_pc_debug_counters:
 	.long 0x0
 
+	.globl msm_pm_enable_l2_fn
+msm_pm_enable_l2_fn:
+	.long 0x0
+
+	.globl msm_pm_disable_l2_fn
+msm_pm_disable_l2_fn:
+	.long 0x0
+
+	.globl msm_pm_flush_l2_fn
+msm_pm_flush_l2_fn:
+	.long 0x0
+
 /*
  * Default the l2 flush flag to 1 so that caches are flushed during power
  * collapse unless the  L2 driver decides to flush them only during L2
diff --git a/arch/arm/mach-msm/idle.h b/arch/arm/mach-msm/idle.h
index 4abdd04..ee3209c 100644
--- a/arch/arm/mach-msm/idle.h
+++ b/arch/arm/mach-msm/idle.h
@@ -33,11 +33,15 @@
 int msm_pm_collapse(void);
 void msm_pm_collapse_exit(void);
 extern void *msm_saved_state;
+extern void (*msm_pm_disable_l2_fn)(void);
+extern void (*msm_pm_enable_l2_fn)(void);
+extern void (*msm_pm_flush_l2_fn)(void);
 extern unsigned long msm_saved_state_phys;
 
 #ifdef CONFIG_CPU_V7
 void msm_pm_boot_entry(void);
 void msm_pm_set_l2_flush_flag(unsigned int flag);
+int msm_pm_get_l2_flush_flag(void);
 extern unsigned long msm_pm_pc_pgd;
 extern unsigned long msm_pm_boot_vector[NR_CPUS];
 extern uint32_t target_type;
diff --git a/arch/arm/mach-msm/include/mach/bam_dmux.h b/arch/arm/mach-msm/include/mach/bam_dmux.h
index f02a882..f11b72c 100644
--- a/arch/arm/mach-msm/include/mach/bam_dmux.h
+++ b/arch/arm/mach-msm/include/mach/bam_dmux.h
@@ -28,6 +28,18 @@
 	BAM_DMUX_DATA_RMNET_6,
 	BAM_DMUX_DATA_RMNET_7,
 	BAM_DMUX_USB_RMNET_0,
+	BAM_DMUX_RESERVED_0, /* 9..11 are reserved*/
+	BAM_DMUX_RESERVED_1,
+	BAM_DMUX_RESERVED_2,
+	BAM_DMUX_DATA_REV_RMNET_0,
+	BAM_DMUX_DATA_REV_RMNET_1,
+	BAM_DMUX_DATA_REV_RMNET_2,
+	BAM_DMUX_DATA_REV_RMNET_3,
+	BAM_DMUX_DATA_REV_RMNET_4,
+	BAM_DMUX_DATA_REV_RMNET_5,
+	BAM_DMUX_DATA_REV_RMNET_6,
+	BAM_DMUX_DATA_REV_RMNET_7,
+	BAM_DMUX_DATA_REV_RMNET_8,
 	BAM_DMUX_NUM_CHANNELS
 };
 
diff --git a/arch/arm/mach-msm/include/mach/board.h b/arch/arm/mach-msm/include/mach/board.h
index 433fee3..ff4776a 100644
--- a/arch/arm/mach-msm/include/mach/board.h
+++ b/arch/arm/mach-msm/include/mach/board.h
@@ -398,6 +398,7 @@
 	u32 splash_screen_addr;
 	u32 splash_screen_size;
 	char mdp_iommu_split_domain;
+	u32 avtimer_phy;
 };
 
 
@@ -586,6 +587,8 @@
 void msm_map_msm8226_io(void);
 void msm8226_init_irq(void);
 void msm8226_init_gpiomux(void);
+void msm_map_msm8910_io(void);
+void msm8910_init_irq(void);
 
 struct mmc_platform_data;
 int msm_add_sdcc(unsigned int controller,
diff --git a/arch/arm/mach-msm/include/mach/clk-provider.h b/arch/arm/mach-msm/include/mach/clk-provider.h
index 770713d..d47e88e 100644
--- a/arch/arm/mach-msm/include/mach/clk-provider.h
+++ b/arch/arm/mach-msm/include/mach/clk-provider.h
@@ -39,8 +39,6 @@
 #define ENABLE_VOTED	4	/* Bit pol: 1 = running; delay on disable */
 #define DELAY		5	/* No bit to check, just delay */
 
-#define MAX_VDD_LEVELS			4
-
 /**
  * struct clk_vdd_class - Voltage scaling class
  * @class_name: name of the class
@@ -52,16 +50,19 @@
 struct clk_vdd_class {
 	const char *class_name;
 	int (*set_vdd)(struct clk_vdd_class *v_class, int level);
-	int level_votes[MAX_VDD_LEVELS];
+	int *level_votes;
+	int num_levels;
 	unsigned long cur_level;
 	struct mutex lock;
 };
 
-#define DEFINE_VDD_CLASS(_name, _set_vdd) \
+#define DEFINE_VDD_CLASS(_name, _set_vdd, _num_levels) \
 	struct clk_vdd_class _name = { \
 		.class_name = #_name, \
 		.set_vdd = _set_vdd, \
-		.cur_level = ARRAY_SIZE(_name.level_votes), \
+		.level_votes = (int [_num_levels]) {}, \
+		.num_levels = _num_levels, \
+		.cur_level = _num_levels, \
 		.lock = __MUTEX_INITIALIZER(_name.lock) \
 	}
 
@@ -109,7 +110,8 @@
 	const char *dbg_name;
 	struct clk *depends;
 	struct clk_vdd_class *vdd_class;
-	unsigned long fmax[MAX_VDD_LEVELS];
+	unsigned long *fmax;
+	int num_fmax;
 	unsigned long rate;
 
 	struct list_head children;
diff --git a/arch/arm/mach-msm/include/mach/iommu.h b/arch/arm/mach-msm/include/mach/iommu.h
index f63af64..5d6ec96 100644
--- a/arch/arm/mach-msm/include/mach/iommu.h
+++ b/arch/arm/mach-msm/include/mach/iommu.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2010-2012, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -15,14 +15,16 @@
 
 #include <linux/interrupt.h>
 #include <linux/clk.h>
+#include <linux/list.h>
 #include <linux/regulator/consumer.h>
 #include <mach/socinfo.h>
 
 extern pgprot_t     pgprot_kernel;
-extern struct platform_device *msm_iommu_root_dev;
+extern struct bus_type msm_iommu_sec_bus_type;
 
 /* Domain attributes */
 #define MSM_IOMMU_DOMAIN_PT_CACHEABLE	0x1
+#define MSM_IOMMU_DOMAIN_PT_SECURE	0x2
 
 /* Mask for the cache policy attribute */
 #define MSM_IOMMU_CP_MASK		0x03
@@ -88,8 +90,9 @@
  * @aclk:	Alternate clock for this IOMMU core, if any
  * @name:	Human-readable name of this IOMMU device
  * @gdsc:	Regulator needed to power this HW block (v2 only)
- * @nsmr:	Size of the SMT on this HW block (v2 only)
  * @bfb_settings: Optional BFB performance tuning parameters
+ * @dev:	Struct device this hardware instance is tied to
+ * @list:	List head to link all iommus together
  *
  * A msm_iommu_drvdata holds the global driver data about a single piece
  * of an IOMMU hardware instance.
@@ -103,10 +106,15 @@
 	struct clk *aclk;
 	const char *name;
 	struct regulator *gdsc;
-	unsigned int nsmr;
 	struct msm_iommu_bfb_settings *bfb_settings;
+	int sec_id;
+	struct device *dev;
+	struct list_head list;
 };
 
+void msm_iommu_add_drv(struct msm_iommu_drvdata *drv);
+void msm_iommu_remove_drv(struct msm_iommu_drvdata *drv);
+
 /**
  * struct msm_iommu_ctx_drvdata - an IOMMU context bank instance
  * @num:		Hardware context number of this context
diff --git a/arch/arm/mach-msm/include/mach/iommu_domains.h b/arch/arm/mach-msm/include/mach/iommu_domains.h
index 1d538f2..01bc479 100644
--- a/arch/arm/mach-msm/include/mach/iommu_domains.h
+++ b/arch/arm/mach-msm/include/mach/iommu_domains.h
@@ -15,6 +15,8 @@
 
 #include <linux/memory_alloc.h>
 
+#define MSM_IOMMU_DOMAIN_SECURE	0x1
+
 enum {
 	VIDEO_DOMAIN,
 	CAMERA_DOMAIN,
@@ -69,6 +71,7 @@
 	int npartitions;
 	const char *client_name;
 	unsigned int domain_flags;
+	unsigned int is_secure;
 };
 
 #if defined(CONFIG_MSM_IOMMU)
diff --git a/arch/arm/mach-msm/include/mach/irqs-8625.h b/arch/arm/mach-msm/include/mach/irqs-8625.h
index 2ec0e21..2a61118 100644
--- a/arch/arm/mach-msm/include/mach/irqs-8625.h
+++ b/arch/arm/mach-msm/include/mach/irqs-8625.h
@@ -16,6 +16,10 @@
 #define GIC_PPI_START		16
 #define GIC_SPI_START		32
 
+#ifdef CONFIG_MSM_FIQ
+#define FIQ_START               0
+#endif
+
 /* As per QGIC2 PPI 16 aka 0 is reserved */
 #define MSM8625_INT_A5_PMU_IRQ		(GIC_PPI_START + 1)
 #define MSM8625_INT_DEBUG_TIMER_EXP	(GIC_PPI_START + 2)
diff --git a/arch/arm/mach-msm/include/mach/irqs-8910.h b/arch/arm/mach-msm/include/mach/irqs-8910.h
new file mode 100644
index 0000000..e883214
--- /dev/null
+++ b/arch/arm/mach-msm/include/mach/irqs-8910.h
@@ -0,0 +1,40 @@
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __ASM_ARCH_MSM_IRQS_8910_H
+#define __ASM_ARCH_MSM_IRQS_8910_H
+
+/* MSM ACPU Interrupt Numbers */
+
+/*
+ * 0-15:  STI/SGI (software triggered/generated interrupts)
+ * 16-31: PPI (private peripheral interrupts)
+ * 32+:   SPI (shared peripheral interrupts)
+ */
+
+#define GIC_PPI_START 16
+#define GIC_SPI_START 32
+
+#define INT_ARMQC_PERFMON			(GIC_PPI_START + 10)
+
+#define APCC_QGICL2PERFMONIRPTREQ	(GIC_SPI_START + 1)
+#define SC_SICL2PERFMONIRPTREQ		APCC_QGICL2PERFMONIRPTREQ
+#define TLMM_MSM_SUMMARY_IRQ		(GIC_SPI_START + 208)
+
+#define NR_MSM_IRQS 256
+#define NR_GPIO_IRQS 102
+#define NR_QPNP_IRQS 32768 /* SPARSE_IRQ is required to support this */
+#define NR_BOARD_IRQS NR_QPNP_IRQS
+#define NR_TLMM_MSM_DIR_CONN_IRQ 8
+#define NR_MSM_GPIOS NR_GPIO_IRQS
+
+#endif
diff --git a/arch/arm/mach-msm/include/mach/irqs.h b/arch/arm/mach-msm/include/mach/irqs.h
index f562c40..7aff770 100644
--- a/arch/arm/mach-msm/include/mach/irqs.h
+++ b/arch/arm/mach-msm/include/mach/irqs.h
@@ -60,6 +60,8 @@
 
 #if defined(CONFIG_ARCH_MSM8974)
 #include "irqs-8974.h"
+#elif defined(CONFIG_ARCH_MSM8910)
+#include "irqs-8910.h"
 #elif defined(CONFIG_ARCH_MPQ8092)
 #include "irqs-8092.h"
 #elif defined(CONFIG_ARCH_MSM9615)
diff --git a/arch/arm/mach-msm/include/mach/msm-krait-l2-accessors.h b/arch/arm/mach-msm/include/mach/msm-krait-l2-accessors.h
index f835e82..a35ff4d 100644
--- a/arch/arm/mach-msm/include/mach/msm-krait-l2-accessors.h
+++ b/arch/arm/mach-msm/include/mach/msm-krait-l2-accessors.h
@@ -2,7 +2,7 @@
 #define __ASM_ARCH_MSM_MSM_KRAIT_L2_ACCESSORS_H
 
 /*
- * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2011,2012, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -13,8 +13,21 @@
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
  */
+
+#ifdef CONFIG_ARCH_MSM_KRAIT
 extern void set_l2_indirect_reg(u32 reg_addr, u32 val);
 extern u32 get_l2_indirect_reg(u32 reg_addr);
 extern u32 set_get_l2_indirect_reg(u32 reg_addr, u32 val);
+#else
+static inline void set_l2_indirect_reg(u32 reg_addr, u32 val) {}
+static inline u32 get_l2_indirect_reg(u32 reg_addr)
+{
+	return 0;
+}
+static inline u32 set_get_l2_indirect_reg(u32 reg_addr, u32 val)
+{
+	return 0;
+}
+#endif
 
 #endif
diff --git a/arch/arm/mach-msm/include/mach/msm72k_otg.h b/arch/arm/mach-msm/include/mach/msm72k_otg.h
index 623de2a..50e2936 100644
--- a/arch/arm/mach-msm/include/mach/msm72k_otg.h
+++ b/arch/arm/mach-msm/include/mach/msm72k_otg.h
@@ -154,6 +154,7 @@
 	struct work_struct otg_resume_work;
 	struct notifier_block usbdev_nb;
 	struct msm_xo_voter *xo_handle; /*handle to vote for TCXO D1 buffer*/
+	unsigned curr_power;
 #ifdef CONFIG_USB_MSM_ACA
 	struct timer_list	id_timer;	/* drives id_status polling */
 	unsigned		b_max_power;	/* ACA: max power of accessory*/
diff --git a/arch/arm/mach-msm/include/mach/msm_bus.h b/arch/arm/mach-msm/include/mach/msm_bus.h
index c94bf80..6b94a43 100644
--- a/arch/arm/mach-msm/include/mach/msm_bus.h
+++ b/arch/arm/mach-msm/include/mach/msm_bus.h
@@ -44,8 +44,8 @@
 struct msm_bus_vectors {
 	int src; /* Master */
 	int dst; /* Slave */
-	unsigned int ab; /* Arbitrated bandwidth */
-	unsigned int ib; /* Instantaneous bandwidth */
+	uint64_t ab; /* Arbitrated bandwidth */
+	uint64_t ib; /* Instantaneous bandwidth */
 };
 
 struct msm_bus_paths {
diff --git a/arch/arm/mach-msm/include/mach/msm_dcvs.h b/arch/arm/mach-msm/include/mach/msm_dcvs.h
index 490a34b..e81cee4 100644
--- a/arch/arm/mach-msm/include/mach/msm_dcvs.h
+++ b/arch/arm/mach-msm/include/mach/msm_dcvs.h
@@ -19,6 +19,10 @@
 #define CORES_MAX (10)
 
 #define CPU_OFFSET	1  /* used to notify TZ the core number */
+#define GPU_OFFSET (CORES_MAX * 2/3)  /* there will be more cpus than gpus,
+				     * let the GPU be assigned fewer core
+				     * elements and start later
+				     */
 
 enum msm_core_idle_state {
 	MSM_DCVS_IDLE_ENTER,
@@ -32,43 +36,14 @@
 	MSM_DCVS_DISABLE_HIGH_LATENCY_MODES,
 };
 
-/**
- * struct msm_dcvs_idle
- *
- * API for idle code to register and send idle enter/exit
- * notifications to msm_dcvs driver.
- */
-struct msm_dcvs_idle {
-	const char *core_name;
-	/* Enable/Disable idle state/notifications */
-	int (*enable)(struct msm_dcvs_idle *self,
-			enum msm_core_control_event event);
+struct msm_gov_platform_data {
+	struct msm_dcvs_core_info *info;
+	int latency;
 };
 
 /**
- * msm_dcvs_idle_source_register
- * @drv: Pointer to the source driver
- * @return: Handle to be used for sending idle state notifications.
- *
- * Register the idle driver with the msm_dcvs driver to send idle
- * state notifications for the core.
- */
-extern int msm_dcvs_idle_source_register(struct msm_dcvs_idle *drv);
-
-/**
- * msm_dcvs_idle_source_unregister
- * @drv: Pointer to the source driver
- * @return:
- *	0 on success
- *	-EINVAL
- *
- * Description: Unregister the idle driver with the msm_dcvs driver
- */
-extern int msm_dcvs_idle_source_unregister(struct msm_dcvs_idle *drv);
-
-/**
  * msm_dcvs_idle
- * @handle: Handle provided back at registration
+ * @dcvs_core_id: The id returned by msm_dcvs_register_core
  * @state: The enter/exit idle state the core is in
  * @iowaited: iowait in us
  * on iMSM_DCVS_IDLE_EXIT.
@@ -80,7 +55,7 @@
  *
  * Send idle state notifications to the msm_dcvs driver
  */
-int msm_dcvs_idle(int handle, enum msm_core_idle_state state,
+int msm_dcvs_idle(int dcvs_core_id, enum msm_core_idle_state state,
 		uint32_t iowaited);
 
 /**
@@ -90,6 +65,9 @@
  * before the sink driver can be registered.
  */
 struct msm_dcvs_core_info {
+	int					num_cores;
+	int					*sensors;
+	int					thermal_poll_ms;
 	struct msm_dcvs_freq_entry		*freq_tbl;
 	struct msm_dcvs_core_param		core_param;
 	struct msm_dcvs_algo_param		algo_param;
@@ -99,8 +77,10 @@
 
 /**
  * msm_dcvs_register_core
- * @core_name: Unique name identifier for the core.
+ * @type: whether this is a CPU or a GPU
+ * @type_core_num: The number of the core for a type
  * @info: The core specific algorithm parameters.
+ * @sensor: The thermal sensor number of the core in question
  * @return :
  *	0 on success,
  *	-ENOSYS,
@@ -110,35 +90,28 @@
  * msm_dcvs_freq_sink_register
  * Cores that need to run synchronously must share the same group id.
  */
-extern int msm_dcvs_register_core(const char *core_name,
-		struct msm_dcvs_core_info *info);
+extern int msm_dcvs_register_core(
+	enum msm_dcvs_core_type type,
+	int type_core_num,
+	struct msm_dcvs_core_info *info,
+	int (*set_frequency)(int type_core_num, unsigned int freq),
+	unsigned int (*get_frequency)(int type_core_num),
+	int (*idle_enable)(int type_core_num,
+				enum msm_core_control_event event),
+	int sensor);
 
 /**
- * struct msm_dcvs_freq
- *
- * API for clock driver code to register and receive frequency change
- * request for the core from the msm_dcvs driver.
- */
-struct msm_dcvs_freq {
-	const char *core_name;
-	/* Callback from msm_dcvs to set the core frequency */
-	int (*set_frequency)(struct msm_dcvs_freq *self,
-			unsigned int freq);
-	unsigned int (*get_frequency)(struct msm_dcvs_freq *self);
-};
-
-/**
- * msm_dcvs_freq_sink_register
+ * msm_dcvs_freq_sink_start
  * @drv: The sink driver
  * @return: Handle unique to the core.
  *
  * Register the clock driver code with the msm_dvs driver to get notified about
  * frequency change requests.
  */
-extern int msm_dcvs_freq_sink_register(struct msm_dcvs_freq *drv);
+extern int msm_dcvs_freq_sink_start(int dcvs_core_id);
 
 /**
- * msm_dcvs_freq_sink_unregister
+ * msm_dcvs_freq_sink_stop
  * @drv: The sink driver
  * @return:
  *	0 on success,
@@ -147,6 +120,13 @@
  * Unregister the sink driver for the core. This will cause the source driver
  * for the core to stop sending idle pulses.
  */
-extern int msm_dcvs_freq_sink_unregister(struct msm_dcvs_freq *drv);
+extern int msm_dcvs_freq_sink_stop(int dcvs_core_id);
 
+/**
+ * msm_dcvs_update_limits
+ * @drv: The sink driver
+ *
+ * Update the frequency known to dcvs when the limits are changed.
+ */
+extern void msm_dcvs_update_limits(int dcvs_core_id);
 #endif
diff --git a/arch/arm/mach-msm/include/mach/msm_dsps.h b/arch/arm/mach-msm/include/mach/msm_dsps.h
index a876798..ac81616 100644
--- a/arch/arm/mach-msm/include/mach/msm_dsps.h
+++ b/arch/arm/mach-msm/include/mach/msm_dsps.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2011-2012, Code Aurora Forum. 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
@@ -75,16 +75,6 @@
  * @regs_num - number of regulators.
  * @dsps_pwr_ctl_en - to enable DSPS to do power control if set 1
  *  otherwise the apps will do power control
- * @tcm_code_start - start of the TCM code region as physical address
- * @tcm_code_size - size of the TCM code region in bytes
- * @tcm_buf_start - start of the TCM buf region as physical address
- * @tcm_buf_size - size of the TCM buf region in bytes
- * @pipe_start - start of the PIPE region as physical address
- * @pipe_size - size of the PIPE region in bytes
- * @ddr_start - start of the DDR region as physical address
- * @ddr_size - size of the DDR region in bytes
- * @smem_start - start of the smem region as physical address
- * @smem_size - size of the smem region in bytes
  * @ppss_pause_reg - Offset to the PPSS_PAUSE register
  * @ppss_wdog_unmasked_int_en_reg - Offset to PPSS_WDOG_UNMASKED_INT_EN register
  * @signature - signature for validity check.
@@ -99,16 +89,6 @@
 	int regs_num;
 	int dsps_pwr_ctl_en;
 	void (*init)(struct msm_dsps_platform_data *data);
-	int tcm_code_start;
-	int tcm_code_size;
-	int tcm_buf_start;
-	int tcm_buf_size;
-	int pipe_start;
-	int pipe_size;
-	int ddr_start;
-	int ddr_size;
-	int smem_start;
-	int smem_size;
 	int ppss_pause_reg;
 	int ppss_wdog_unmasked_int_en_reg;
 	u32 signature;
diff --git a/arch/arm/mach-msm/include/mach/msm_hdmi_audio.h b/arch/arm/mach-msm/include/mach/msm_hdmi_audio.h
index 2455e93..9b04141 100644
--- a/arch/arm/mach-msm/include/mach/msm_hdmi_audio.h
+++ b/arch/arm/mach-msm/include/mach/msm_hdmi_audio.h
@@ -29,7 +29,8 @@
 	HDMI_SAMPLE_RATE_88_2KHZ,
 	HDMI_SAMPLE_RATE_96KHZ,
 	HDMI_SAMPLE_RATE_176_4KHZ,
-	HDMI_SAMPLE_RATE_192KHZ
+	HDMI_SAMPLE_RATE_192KHZ,
+	HDMI_SAMPLE_RATE_MAX
 };
 
 int hdmi_audio_enable(bool on , u32 fifo_water_mark);
diff --git a/arch/arm/mach-msm/include/mach/msm_hsusb_hw.h b/arch/arm/mach-msm/include/mach/msm_hsusb_hw.h
index 82542b2..831b40e 100644
--- a/arch/arm/mach-msm/include/mach/msm_hsusb_hw.h
+++ b/arch/arm/mach-msm/include/mach/msm_hsusb_hw.h
@@ -100,6 +100,8 @@
 #define CONFIG_MAX_PKT(n)     ((n) << 16)
 #define CONFIG_ZLT            (1 << 29)    /* stop on zero-len xfer */
 #define CONFIG_IOS            (1 << 15)    /* IRQ on setup */
+#define CONFIG_MULT           (3 << 30)
+#define CONFIG_MULT_SHIFT     11
 
 struct ept_queue_item {
     unsigned next;
diff --git a/arch/arm/mach-msm/include/mach/msm_iomap-8625.h b/arch/arm/mach-msm/include/mach/msm_iomap-8625.h
index 43250f5..9b6de20 100644
--- a/arch/arm/mach-msm/include/mach/msm_iomap-8625.h
+++ b/arch/arm/mach-msm/include/mach/msm_iomap-8625.h
@@ -54,6 +54,12 @@
 #define MSM8625_SAW1_PHYS		0xC0700000
 #define MSM8625_SAW1_SIZE		SZ_4K
 
+#define MSM8625_SAW2_PHYS		0xC0A00000
+#define MSM8625_SAW2_SIZE		SZ_4K
+
+#define MSM8625_SAW3_PHYS		0xC0B00000
+#define MSM8625_SAW3_SIZE		SZ_4K
+
 #define MSM8625_CFG_CTL_PHYS		0xA9800000
 #define MSM8625_CFG_CTL_SIZE		SZ_4K
 
diff --git a/arch/arm/mach-msm/include/mach/msm_iomap-8910.h b/arch/arm/mach-msm/include/mach/msm_iomap-8910.h
new file mode 100644
index 0000000..08f21b6
--- /dev/null
+++ b/arch/arm/mach-msm/include/mach/msm_iomap-8910.h
@@ -0,0 +1,44 @@
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __ASM_ARCH_MSM_IOMAP_8910_H
+#define __ASM_ARCH_MSM_IOMAP_8910_H
+
+/* Physical base address and size of peripherals.
+ * Ordered by the virtual base addresses they will be mapped at.
+ *
+ * If you add or remove entries here, you'll want to edit the
+ * io desc array in arch/arm/mach-msm/io.c to reflect your
+ * changes.
+ *
+ */
+
+#define MSM8910_MSM_SHARED_RAM_PHYS	0x0FA00000
+
+#define MSM8910_APCS_GCC_PHYS	0xF9011000
+#define MSM8910_APCS_GCC_SIZE	SZ_4K
+
+#define MSM8910_TLMM_PHYS	0xFD510000
+#define MSM8910_TLMM_SIZE	SZ_16K
+
+#define MSM8910_IMEM_PHYS	0xFE805000
+#define MSM8910_IMEM_SIZE	SZ_4K
+
+#define MSM8910_MPM2_PSHOLD_PHYS	0xFC4AB000
+#define MSM8910_MPM2_PSHOLD_SIZE	SZ_4K
+
+#ifdef CONFIG_DEBUG_MSM8910_UART
+#define MSM_DEBUG_UART_BASE	IOMEM(0xFA71E000)
+#define MSM_DEBUG_UART_PHYS	0xF991E000
+#endif
+
+#endif
diff --git a/arch/arm/mach-msm/include/mach/msm_iomap-9625.h b/arch/arm/mach-msm/include/mach/msm_iomap-9625.h
index 765de13..89252a5 100644
--- a/arch/arm/mach-msm/include/mach/msm_iomap-9625.h
+++ b/arch/arm/mach-msm/include/mach/msm_iomap-9625.h
@@ -25,6 +25,12 @@
 
 #define MSM9625_SHARED_RAM_PHYS	0x00000000
 
+#define MSM9625_QGIC_DIST_PHYS	0xF9000000
+#define MSM9625_QGIC_DIST_SIZE	SZ_4K
+
+#define MSM9625_QGIC_CPU_PHYS	0xF9002000
+#define MSM9625_QGIC_CPU_SIZE	SZ_4K
+
 #define MSM9625_APCS_GCC_PHYS	0xF9011000
 #define MSM9625_APCS_GCC_SIZE	SZ_4K
 
@@ -43,6 +49,9 @@
 #define MSM9625_IMEM_PHYS	0xFC42B000
 #define MSM9625_IMEM_SIZE	SZ_2K
 
+#define MSM9625_MPM2_PSHOLD_PHYS	0xFC4AB000
+#define MSM9625_MPM2_PSHOLD_SIZE	SZ_4K
+
 #ifdef CONFIG_DEBUG_MSM9625_UART
 #define MSM_DEBUG_UART_BASE	IOMEM(0xFA71E000)
 #define MSM_DEBUG_UART_PHYS	0xF991E000
diff --git a/arch/arm/mach-msm/include/mach/msm_iomap.h b/arch/arm/mach-msm/include/mach/msm_iomap.h
index 8dbd29c..f372b1e 100644
--- a/arch/arm/mach-msm/include/mach/msm_iomap.h
+++ b/arch/arm/mach-msm/include/mach/msm_iomap.h
@@ -54,7 +54,7 @@
 	defined(CONFIG_ARCH_MSM7X25) || defined(CONFIG_ARCH_MSM7X01A) || \
 	defined(CONFIG_ARCH_MSM8625) || defined(CONFIG_ARCH_MSM7X30) || \
 	defined(CONFIG_ARCH_MSM9625) || defined(CONFIG_ARCH_MPQ8092) || \
-	defined(CONFIG_ARCH_MSM8226)
+	defined(CONFIG_ARCH_MSM8226) || defined(CONFIG_ARCH_MSM8910)
 
 /* Unified iomap */
 
@@ -122,6 +122,7 @@
 #include "msm_iomap-9625.h"
 #include "msm_iomap-8092.h"
 #include "msm_iomap-8226.h"
+#include "msm_iomap-8910.h"
 
 #else
 /* Legacy single-target iomap */
diff --git a/arch/arm/mach-msm/include/mach/msm_tspp.h b/arch/arm/mach-msm/include/mach/msm_tspp.h
index 48be504..a024a99 100644
--- a/arch/arm/mach-msm/include/mach/msm_tspp.h
+++ b/arch/arm/mach-msm/include/mach/msm_tspp.h
@@ -25,31 +25,34 @@
 struct tspp_data_descriptor {
 	void *virt_base;   /* logical address of the actual data */
 	u32 phys_base;     /* physical address of the actual data */
-	int size;			 /* size of buffer in bytes */
+	u32 size;          /* size of buffer in bytes */
 	int id;            /* unique identifier */
 	void *user;        /* user-defined data */
 };
 
-typedef void (tspp_notifier)(int channel, void *user);
-typedef void* (tspp_allocator)(int channel, int size,
+typedef void (tspp_notifier)(int channel_id, void *user);
+typedef void* (tspp_allocator)(int channel_id, u32 size,
 	u32 *phys_base, void *user);
+typedef void (tspp_memfree)(int channel_id, u32 size,
+	void *virt_base, u32 phys_base, void *user);
 
 /* Kernel API functions */
-int tspp_open_stream(u32 dev, u32 channel_id, enum tspp_source src,
-	enum tspp_tsif_mode mode);
+int tspp_open_stream(u32 dev, u32 channel_id,
+			struct tspp_select_source *source);
 int tspp_close_stream(u32 dev, u32 channel_id);
 int tspp_open_channel(u32 dev, u32 channel_id);
 int tspp_close_channel(u32 dev, u32 channel_id);
-int tspp_add_filter(u32 dev, u32 channel_id,	struct tspp_filter *filter);
+int tspp_add_filter(u32 dev, u32 channel_id, struct tspp_filter *filter);
 int tspp_remove_filter(u32 dev, u32 channel_id,	struct tspp_filter *filter);
 int tspp_set_key(u32 dev, u32 channel_id, struct tspp_key *key);
-int tspp_register_notification(u32 dev, u32 channel, tspp_notifier *notify,
+int tspp_register_notification(u32 dev, u32 channel_id, tspp_notifier *notify,
 	void *data, u32 timer_ms);
-int tspp_unregister_notification(u32 dev, u32 channel);
-const struct tspp_data_descriptor *tspp_get_buffer(u32 dev, u32 channel);
-int tspp_release_buffer(u32 dev, u32 channel, u32 descriptor_id);
+int tspp_unregister_notification(u32 dev, u32 channel_id);
+const struct tspp_data_descriptor *tspp_get_buffer(u32 dev, u32 channel_id);
+int tspp_release_buffer(u32 dev, u32 channel_id, u32 descriptor_id);
 int tspp_allocate_buffers(u32 dev, u32 channel_id, u32 count,
-	u32 size, u32 int_freq, tspp_allocator *alloc, void *user);
+	u32 size, u32 int_freq, tspp_allocator *alloc,
+	tspp_memfree *memfree, void *user);
 
 #endif /* _MSM_TSPP_H_ */
 
diff --git a/arch/arm/mach-msm/include/mach/ocmem.h b/arch/arm/mach-msm/include/mach/ocmem.h
index 904de5e..cb8aae0 100644
--- a/arch/arm/mach-msm/include/mach/ocmem.h
+++ b/arch/arm/mach-msm/include/mach/ocmem.h
@@ -134,6 +134,9 @@
 int ocmem_unmap(int client_id, struct ocmem_buf *buffer,
 			struct ocmem_map_list *list);
 
+int ocmem_dump(int client_id, struct ocmem_buf *buffer,
+				unsigned long dst_phys_addr);
+
 /* Priority Enforcement APIs */
 int ocmem_evict(int client_id);
 
diff --git a/arch/arm/mach-msm/include/mach/ocmem_priv.h b/arch/arm/mach-msm/include/mach/ocmem_priv.h
index 09dfac0..380fde1 100644
--- a/arch/arm/mach-msm/include/mach/ocmem_priv.h
+++ b/arch/arm/mach-msm/include/mach/ocmem_priv.h
@@ -56,6 +56,8 @@
 	NR_TRANSFER_FAILS,
 	NR_EVICTIONS,
 	NR_RESTORES,
+	NR_DUMP_REQUESTS,
+	NR_DUMP_COMPLETE,
 	NR_OCMEM_ZSTAT_ITEMS,
 };
 
@@ -125,7 +127,7 @@
 	struct list_head req_list;
 	struct work_struct work;
 	int prio;
-	int pending;
+	atomic_t pending;
 	bool passive;
 };
 
@@ -198,6 +200,7 @@
 int process_evict(int);
 int process_restore(int);
 int process_shrink(int, struct ocmem_handle *, unsigned long);
+int process_dump(int, struct ocmem_handle *, unsigned long);
 int ocmem_rdm_transfer(int, struct ocmem_map_list *,
 				unsigned long, int);
 int ocmem_clear(unsigned long, unsigned long);
diff --git a/arch/arm/mach-msm/include/mach/peripheral-loader.h b/arch/arm/mach-msm/include/mach/peripheral-loader.h
deleted file mode 100644
index 327c82f..0000000
--- a/arch/arm/mach-msm/include/mach/peripheral-loader.h
+++ /dev/null
@@ -1,27 +0,0 @@
-/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- */
-#ifndef __MACH_PERIPHERAL_LOADER_H
-#define __MACH_PERIPHERAL_LOADER_H
-
-#ifdef CONFIG_MSM_PIL
-extern void *pil_get(const char *name);
-extern void pil_put(void *peripheral_handle);
-extern void pil_force_shutdown(const char *name);
-extern int pil_force_boot(const char *name);
-#else
-static inline void *pil_get(const char *name) { return NULL; }
-static inline void pil_put(void *peripheral_handle) { }
-static inline void pil_force_shutdown(const char *name) { }
-static inline int pil_force_boot(const char *name) { return -ENOSYS; }
-#endif
-
-#endif
diff --git a/arch/arm/mach-msm/include/mach/qdsp5v2/codec_utils.h b/arch/arm/mach-msm/include/mach/qdsp5v2/codec_utils.h
index 92dfe12..2cfdabf 100644
--- a/arch/arm/mach-msm/include/mach/qdsp5v2/codec_utils.h
+++ b/arch/arm/mach-msm/include/mach/qdsp5v2/codec_utils.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010, 2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2010, 2012, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -123,7 +123,8 @@
 	uint64_t bytecount_given;
 	uint64_t bytecount_query;
 
-	struct list_head pmem_region_queue; /* protected by lock */
+	struct list_head ion_region_queue; /* protected by lock */
+	struct ion_client *client;
 
 	int eq_enable;
 	int eq_needs_commit;
diff --git a/arch/arm/mach-msm/include/mach/qdsp6v2/apr.h b/arch/arm/mach-msm/include/mach/qdsp6v2/apr.h
index 296f222..4c06af4 100644
--- a/arch/arm/mach-msm/include/mach/qdsp6v2/apr.h
+++ b/arch/arm/mach-msm/include/mach/qdsp6v2/apr.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2010-2012, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -165,4 +165,5 @@
 enum apr_subsys_state apr_get_q6_state(void);
 int apr_set_q6_state(enum apr_subsys_state state);
 void apr_set_subsys_state(void);
+const char *apr_get_lpass_subsys_name(void);
 #endif
diff --git a/arch/arm/mach-msm/include/mach/qdsp6v2/apr_us.h b/arch/arm/mach-msm/include/mach/qdsp6v2/apr_us.h
index 487e814..22f343c 100644
--- a/arch/arm/mach-msm/include/mach/qdsp6v2/apr_us.h
+++ b/arch/arm/mach-msm/include/mach/qdsp6v2/apr_us.h
@@ -17,20 +17,6 @@
 
 /* ======================================================================= */
 /*  Session Level commands */
-#define USM_SESSION_CMD_MEMORY_MAP			0x00012304
-struct usm_stream_cmd_memory_map {
-	struct apr_hdr	hdr;
-	u32		buf_add;
-	u32		buf_size;
-	u16		mempool_id;
-	u16		reserved;
-} __packed;
-
-#define USM_SESSION_CMD_MEMORY_UNMAP			0x00012305
-struct usm_stream_cmd_memory_unmap {
-	struct apr_hdr  hdr;
-	u32             buf_add;
-} __packed;
 
 #define USM_SESSION_CMD_RUN				0x00012306
 struct usm_stream_cmd_run {
@@ -78,7 +64,7 @@
 } __packed;
 
 /* Max number of static located transparent data (bytes) */
-#define USM_MAX_CFG_DATA_SIZE 20
+#define USM_MAX_CFG_DATA_SIZE 100
 struct usm_encode_cfg_blk {
 	u32 frames_per_buf;
 	u32 format_id;
@@ -113,31 +99,6 @@
 	u8  transp_data[USM_MAX_CFG_DATA_SIZE];
 } __packed;
 
-
-#define USM_DATA_CMD_READ				0x0001230E
-struct usm_stream_cmd_read {
-	struct apr_hdr  hdr;
-	u32                 buf_add;
-	u32                 buf_size;
-	u32                 uid;
-	u32                 counter;
-} __packed;
-
-#define USM_DATA_EVENT_READ_DONE			0x0001230F
-
-#define USM_DATA_CMD_WRITE				0x00011273
-struct usm_stream_cmd_write {
-	struct apr_hdr hdr;
-	u32 buf_add;
-	u32 buf_size;
-	u32 uid;
-	u32 msw_ts;
-	u32 lsw_ts;
-	u32 flags;
-} __packed;
-
-#define USM_DATA_EVENT_WRITE_DONE			0x00011274
-
 /* Start/stop US signal detection */
 #define USM_SESSION_CMD_SIGNAL_DETECT_MODE		0x00012719
 
diff --git a/arch/arm/mach-msm/include/mach/qdsp6v2/apr_us_a.h b/arch/arm/mach-msm/include/mach/qdsp6v2/apr_us_a.h
new file mode 100644
index 0000000..4008698
--- /dev/null
+++ b/arch/arm/mach-msm/include/mach/qdsp6v2/apr_us_a.h
@@ -0,0 +1,59 @@
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __APR_US_A_H__
+#define __APR_US_A_H__
+
+#include "apr_us.h"
+
+/* ======================================================================= */
+/*  Session Level commands */
+#define USM_SESSION_CMD_MEMORY_MAP			0x00012304
+struct usm_stream_cmd_memory_map {
+	struct apr_hdr	hdr;
+	u32		buf_add;
+	u32		buf_size;
+	u16		mempool_id;
+	u16		reserved;
+} __packed;
+
+#define USM_SESSION_CMD_MEMORY_UNMAP			0x00012305
+struct usm_stream_cmd_memory_unmap {
+	struct apr_hdr  hdr;
+	u32             buf_add;
+} __packed;
+
+#define USM_DATA_CMD_READ				0x0001230E
+struct usm_stream_cmd_read {
+	struct apr_hdr  hdr;
+	u32                 buf_add;
+	u32                 buf_size;
+	u32                 uid;
+	u32                 counter;
+} __packed;
+
+#define USM_DATA_EVENT_READ_DONE			0x0001230F
+
+#define USM_DATA_CMD_WRITE				0x00011273
+struct usm_stream_cmd_write {
+	struct apr_hdr hdr;
+	u32 buf_add;
+	u32 buf_size;
+	u32 uid;
+	u32 msw_ts;
+	u32 lsw_ts;
+	u32 flags;
+} __packed;
+
+#define USM_DATA_EVENT_WRITE_DONE			0x00011274
+
+#endif /* __APR_US_A_H__ */
diff --git a/arch/arm/mach-msm/include/mach/qdsp6v2/audio_acdb.h b/arch/arm/mach-msm/include/mach/qdsp6v2/audio_acdb.h
index 3d33350..d34536d 100644
--- a/arch/arm/mach-msm/include/mach/qdsp6v2/audio_acdb.h
+++ b/arch/arm/mach-msm/include/mach/qdsp6v2/audio_acdb.h
@@ -25,6 +25,13 @@
 	MAX_AUDPROC_TYPES
 };
 
+enum {
+	VOCPROC_CAL,
+	VOCSTRM_CAL,
+	VOCVOL_CAL,
+	MAX_VOCPROC_TYPES
+};
+
 struct acdb_cal_block {
 	uint32_t		cal_size;
 	uint32_t		cal_kvaddr;
@@ -47,16 +54,20 @@
 uint32_t get_adm_rx_topology(void);
 uint32_t get_adm_tx_topology(void);
 uint32_t get_asm_topology(void);
+void get_voice_cal_allocation(struct acdb_cal_block *cal_block);
 void get_all_voice_cal(struct acdb_cal_block *cal_block);
 void get_all_cvp_cal(struct acdb_cal_block *cal_block);
 void get_all_vocproc_cal(struct acdb_cal_block *cal_block);
 void get_all_vocstrm_cal(struct acdb_cal_block *cal_block);
 void get_all_vocvol_cal(struct acdb_cal_block *cal_block);
+void get_voice_col_data(uint32_t vocproc_type,
+	struct acdb_cal_block *cal_block);
 void get_anc_cal(struct acdb_cal_block *cal_block);
 void get_afe_cal(int32_t path, struct acdb_cal_block *cal_block);
 void get_audproc_cal(int32_t path, struct acdb_cal_block *cal_block);
 void get_audstrm_cal(int32_t path, struct acdb_cal_block *cal_block);
 void get_audvol_cal(int32_t path, struct acdb_cal_block *cal_block);
+void get_vocproc_dev_cfg_cal(struct acdb_cal_block *cal_block);
 void get_vocproc_cal(struct acdb_cal_data *cal_data);
 void get_vocstrm_cal(struct acdb_cal_data *cal_data);
 void get_vocvol_cal(struct acdb_cal_data *cal_data);
diff --git a/arch/arm/mach-msm/include/mach/rpm-regulator.h b/arch/arm/mach-msm/include/mach/rpm-regulator.h
index 075d20f..b063b97 100644
--- a/arch/arm/mach-msm/include/mach/rpm-regulator.h
+++ b/arch/arm/mach-msm/include/mach/rpm-regulator.h
@@ -101,8 +101,7 @@
  * @init_data:		regulator constraints
  * @id:			regulator id; from enum rpm_vreg_id
  * @sleep_selectable:	flag which indicates that regulator should be accessable
- *			by external private API and that spinlocks should be
- *			used instead of mutex locks
+ *			by external private API
  * @system_uA:		current drawn from regulator not accounted for by any
  *			regulator framework consumer
  * @enable_time:	time in us taken to enable a regulator to the maximum
@@ -184,10 +183,8 @@
  * Returns 0 on success or errno.
  *
  * This function is used to vote for the voltage of a regulator without
- * using the regulator framework.  It is needed by consumers which hold spin
- * locks or have interrupts disabled because the regulator framework can sleep.
- * It is also needed by consumers which wish to only vote for active set
- * regulator voltage.
+ * using the regulator framework.  It is needed for consumers which wish to only
+ * vote for active set regulator voltage.
  *
  * If sleep_also == 0, then a sleep-set value of 0V will be voted for.
  *
diff --git a/arch/arm/mach-msm/include/mach/socinfo.h b/arch/arm/mach-msm/include/mach/socinfo.h
index 225440c..34bdc79 100644
--- a/arch/arm/mach-msm/include/mach/socinfo.h
+++ b/arch/arm/mach-msm/include/mach/socinfo.h
@@ -54,6 +54,12 @@
 	of_machine_is_compatible("qcom,msm8226")
 #define machine_is_msm8226_sim()		\
 	of_machine_is_compatible("qcom,msm8226-sim")
+#define early_machine_is_msm8910()	\
+	of_flat_dt_is_compatible(of_get_flat_dt_root(), "qcom,msm8910")
+#define machine_is_msm8910()		\
+	of_machine_is_compatible("qcom,msm8910")
+#define machine_is_msm8910_sim()		\
+	of_machine_is_compatible("qcom,msm8910-sim")
 #else
 #define early_machine_is_msm8974()	0
 #define machine_is_msm8974()		0
@@ -66,6 +72,9 @@
 #define early_machine_is_msm8226()	0
 #define machine_is_msm8226()		0
 #define machine_is_msm8226_sim()	0
+#define early_machine_is_msm8910()	0
+#define machine_is_msm8910()		0
+#define machine_is_msm8910_sim()	0
 
 #endif
 
@@ -92,6 +101,7 @@
 	MSM_CPU_8064AB,
 	MSM_CPU_8930,
 	MSM_CPU_8930AA,
+	MSM_CPU_8930AB,
 	MSM_CPU_7X27AA,
 	MSM_CPU_9615,
 	MSM_CPU_8974,
@@ -99,7 +109,8 @@
 	MSM_CPU_8625,
 	MSM_CPU_9625,
 	MSM_CPU_8092,
-	MSM_CPU_8226
+	MSM_CPU_8226,
+	MSM_CPU_8910,
 };
 
 enum pmic_model {
@@ -332,6 +343,15 @@
 #endif
 }
 
+static inline int cpu_is_msm8930ab(void)
+{
+#ifdef CONFIG_ARCH_MSM8930
+	return read_msm_cpu_type() == MSM_CPU_8930AB;
+#else
+	return 0;
+#endif
+}
+
 static inline int cpu_is_msm8627(void)
 {
 /* 8930 and 8627 will share the same CONFIG_ARCH type unless otherwise needed */
@@ -415,4 +435,32 @@
 #endif
 }
 
+static inline int cpu_is_msm8910(void)
+{
+#ifdef CONFIG_ARCH_MSM8910
+	enum msm_cpu cpu = socinfo_get_msm_cpu();
+
+	BUG_ON(cpu == MSM_CPU_UNKNOWN);
+	return cpu == MSM_CPU_8910;
+#else
+	return 0;
+#endif
+}
+
+static inline int soc_class_is_msm8960(void)
+{
+	return cpu_is_msm8960() || cpu_is_msm8960ab();
+}
+
+static inline int soc_class_is_apq8064(void)
+{
+	return cpu_is_apq8064() || cpu_is_apq8064ab();
+}
+
+static inline int soc_class_is_msm8930(void)
+{
+	return cpu_is_msm8930() || cpu_is_msm8930aa() || cpu_is_msm8930ab() ||
+	       cpu_is_msm8627();
+}
+
 #endif
diff --git a/arch/arm/mach-msm/include/mach/subsystem_restart.h b/arch/arm/mach-msm/include/mach/subsystem_restart.h
index d3c4eb8..64190d2 100644
--- a/arch/arm/mach-msm/include/mach/subsystem_restart.h
+++ b/arch/arm/mach-msm/include/mach/subsystem_restart.h
@@ -36,6 +36,12 @@
  * @depends_on: subsystem this subsystem depends on to operate
  * @dev: parent device
  * @owner: module the descriptor belongs to
+ * @start: Start a subsystem
+ * @stop: Stop a subsystem
+ * @shutdown: Stop a subsystem
+ * @powerup: Start a subsystem
+ * @crash_shutdown: Shutdown a subsystem when the system crashes (can't sleep)
+ * @ramdump: Collect a ramdump of the subsystem
  */
 struct subsys_desc {
 	const char *name;
@@ -43,6 +49,9 @@
 	struct device *dev;
 	struct module *owner;
 
+	int (*start)(const struct subsys_desc *desc);
+	void (*stop)(const struct subsys_desc *desc);
+
 	int (*shutdown)(const struct subsys_desc *desc);
 	int (*powerup)(const struct subsys_desc *desc);
 	void (*crash_shutdown)(const struct subsys_desc *desc);
@@ -55,9 +64,14 @@
 extern int subsystem_restart_dev(struct subsys_device *dev);
 extern int subsystem_restart(const char *name);
 
+extern void *subsystem_get(const char *name);
+extern void subsystem_put(void *subsystem);
+
 extern struct subsys_device *subsys_register(struct subsys_desc *desc);
 extern void subsys_unregister(struct subsys_device *dev);
 
+extern void subsys_default_online(struct subsys_device *dev);
+
 #else
 
 static inline int get_restart_level(void)
@@ -75,6 +89,13 @@
 	return 0;
 }
 
+static inline void *subsystem_get(const char *name)
+{
+	return NULL;
+}
+
+static inline void subsystem_put(void *subsystem) { }
+
 static inline
 struct subsys_device *subsys_register(struct subsys_desc *desc)
 {
@@ -83,6 +104,8 @@
 
 static inline void subsys_unregister(struct subsys_device *dev) { }
 
+static inline void subsys_default_online(struct subsys_device *dev) { }
+
 #endif /* CONFIG_MSM_SUBSYSTEM_RESTART */
 
 #endif
diff --git a/arch/arm/mach-msm/io.c b/arch/arm/mach-msm/io.c
index 39ac253..ae908e1 100644
--- a/arch/arm/mach-msm/io.c
+++ b/arch/arm/mach-msm/io.c
@@ -25,6 +25,7 @@
 #include <mach/hardware.h>
 #include <asm/page.h>
 #include <mach/msm_iomap.h>
+#include <mach/memory.h>
 #include <asm/mach/map.h>
 #include <linux/dma-mapping.h>
 
@@ -99,6 +100,7 @@
 	asm("mcr p15, 0, %0, c15, c2, 4" : : "r" (0));
 #endif
 	msm_map_io(msm_io_desc, ARRAY_SIZE(msm_io_desc));
+	map_page_strongly_ordered();
 }
 #endif
 
@@ -428,6 +430,8 @@
 	MSM_CHIP_DEVICE(CLK_CTL, MSM8625),
 	MSM_CHIP_DEVICE(SAW0, MSM8625),
 	MSM_CHIP_DEVICE(SAW1, MSM8625),
+	MSM_CHIP_DEVICE(SAW2, MSM8625),
+	MSM_CHIP_DEVICE(SAW3, MSM8625),
 	MSM_CHIP_DEVICE(AD5, MSM7XXX),
 	MSM_CHIP_DEVICE(MDC, MSM7XXX),
 #if defined(CONFIG_DEBUG_MSM_UART1) || defined(CONFIG_DEBUG_MSM_UART2) || \
@@ -452,6 +456,7 @@
 void __init msm_map_msm8625_io(void)
 {
 	msm_map_io(msm8625_io_desc, ARRAY_SIZE(msm8625_io_desc));
+	map_page_strongly_ordered();
 }
 #else
 void __init msm_map_msm8625_io(void) { return; }
@@ -460,7 +465,10 @@
 #ifdef CONFIG_ARCH_MSM9625
 static struct map_desc msm9625_io_desc[] __initdata = {
 	MSM_CHIP_DEVICE(APCS_GCC, MSM9625),
+	MSM_CHIP_DEVICE(QGIC_DIST, MSM9625),
+	MSM_CHIP_DEVICE(QGIC_CPU, MSM9625),
 	MSM_CHIP_DEVICE(TLMM, MSM9625),
+	MSM_CHIP_DEVICE(MPM2_PSHOLD, MSM9625),
 	MSM_CHIP_DEVICE(TMR, MSM9625),
 	MSM_CHIP_DEVICE(IMEM, MSM9625),
 	{
@@ -529,3 +537,23 @@
 	msm_map_io(msm_8226_io_desc, ARRAY_SIZE(msm_8226_io_desc));
 }
 #endif /* CONFIG_ARCH_MSM8226 */
+
+#ifdef CONFIG_ARCH_MSM8910
+static struct map_desc msm8910_io_desc[] __initdata = {
+	MSM_CHIP_DEVICE(APCS_GCC, MSM8910),
+	MSM_CHIP_DEVICE(TLMM, MSM8910),
+	MSM_CHIP_DEVICE(MPM2_PSHOLD, MSM8910),
+	MSM_CHIP_DEVICE(IMEM, MSM8910),
+	{
+		.virtual =  (unsigned long) MSM_SHARED_RAM_BASE,
+		.length =   MSM_SHARED_RAM_SIZE,
+		.type =     MT_DEVICE,
+	},
+};
+
+void __init msm_map_msm8910_io(void)
+{
+	msm_shared_ram_phys = MSM8910_MSM_SHARED_RAM_PHYS;
+	msm_map_io(msm8910_io_desc, ARRAY_SIZE(msm8910_io_desc));
+}
+#endif /* CONFIG_ARCH_MSM8910 */
diff --git a/arch/arm/mach-msm/iommu_domains.c b/arch/arm/mach-msm/iommu_domains.c
index 3acb6d8..75e56fe 100644
--- a/arch/arm/mach-msm/iommu_domains.c
+++ b/arch/arm/mach-msm/iommu_domains.c
@@ -267,6 +267,7 @@
 	else
 		return NULL;
 }
+EXPORT_SYMBOL(msm_get_iommu_domain);
 
 int msm_allocate_iova_address(unsigned int iommu_domain,
 					unsigned int partition_no,
@@ -344,6 +345,7 @@
 	int i;
 	struct msm_iova_data *data;
 	struct mem_pool *pools;
+	struct bus_type *bus;
 
 	if (!layout)
 		return -EINVAL;
@@ -389,11 +391,14 @@
 		}
 	}
 
+	bus = layout->is_secure == MSM_IOMMU_DOMAIN_SECURE ?
+					&msm_iommu_sec_bus_type :
+					&platform_bus_type;
+
 	data->pools = pools;
 	data->npools = layout->npartitions;
 	data->domain_num = atomic_inc_return(&domain_nums);
-	data->domain = iommu_domain_alloc(&platform_bus_type,
-					  layout->domain_flags);
+	data->domain = iommu_domain_alloc(bus, layout->domain_flags);
 
 	add_domain(data);
 
diff --git a/arch/arm/mach-msm/ipc_router.c b/arch/arm/mach-msm/ipc_router.c
index c82eac1..ac33836 100644
--- a/arch/arm/mach-msm/ipc_router.c
+++ b/arch/arm/mach-msm/ipc_router.c
@@ -96,6 +96,13 @@
 static struct list_head local_ports[LP_HASH_SIZE];
 static DEFINE_MUTEX(local_ports_lock);
 
+/*
+ * Server info is organized as a hash table. The server's service ID is
+ * used to index into the hash table. The instance ID of most of the servers
+ * are 1 or 2. The service IDs are well distributed compared to the instance
+ * IDs and hence choosing service ID to index into this hash table optimizes
+ * the hash table operations like add, lookup, destroy.
+ */
 #define SRV_HASH_SIZE 32
 static struct list_head server_list[SRV_HASH_SIZE];
 static DEFINE_MUTEX(server_list_lock);
@@ -174,7 +181,6 @@
 static LIST_HEAD(xprt_info_list);
 static DEFINE_MUTEX(xprt_info_list_lock);
 
-DECLARE_COMPLETION(msm_ipc_remote_router_up);
 static DECLARE_COMPLETION(msm_ipc_local_router_up);
 #define IPC_ROUTER_INIT_TIMEOUT (10 * HZ)
 
@@ -582,6 +588,20 @@
 	return;
 }
 
+/**
+ * msm_ipc_router_lookup_server() - Lookup server information
+ * @service: Service ID of the server info to be looked up.
+ * @instance: Instance ID of the server info to be looked up.
+ * @node_id: Node/Processor ID in which the server is hosted.
+ * @port_id: Port ID within the node in which the server is hosted.
+ *
+ * @return: If found Pointer to server structure, else NULL.
+ *
+ * Note1: Lock the server_list_lock before accessing this function.
+ * Note2: If the <node_id:port_id> are <0:0>, then the lookup is restricted
+ *        to <service:instance>. Used only when a client wants to send a
+ *        message to any QMI server.
+ */
 static struct msm_ipc_server *msm_ipc_router_lookup_server(
 				uint32_t service,
 				uint32_t instance,
@@ -590,30 +610,39 @@
 {
 	struct msm_ipc_server *server;
 	struct msm_ipc_server_port *server_port;
-	int key = (instance & (SRV_HASH_SIZE - 1));
+	int key = (service & (SRV_HASH_SIZE - 1));
 
-	mutex_lock(&server_list_lock);
 	list_for_each_entry(server, &server_list[key], list) {
 		if ((server->name.service != service) ||
 		    (server->name.instance != instance))
 			continue;
-		if ((node_id == 0) && (port_id == 0)) {
-			mutex_unlock(&server_list_lock);
+		if ((node_id == 0) && (port_id == 0))
 			return server;
-		}
 		list_for_each_entry(server_port, &server->server_port_list,
 				    list) {
 			if ((server_port->server_addr.node_id == node_id) &&
-			    (server_port->server_addr.port_id == port_id)) {
-				mutex_unlock(&server_list_lock);
+			    (server_port->server_addr.port_id == port_id))
 				return server;
-			}
 		}
 	}
-	mutex_unlock(&server_list_lock);
 	return NULL;
 }
 
+/**
+ * msm_ipc_router_create_server() - Add server info to hash table
+ * @service: Service ID of the server info to be created.
+ * @instance: Instance ID of the server info to be created.
+ * @node_id: Node/Processor ID in which the server is hosted.
+ * @port_id: Port ID within the node in which the server is hosted.
+ * @xprt_info: XPRT through which the node hosting the server is reached.
+ *
+ * @return: Pointer to server structure on success, else NULL.
+ *
+ * This function adds the server info to the hash table. If the same
+ * server(i.e. <service_id:instance_id>) is hosted in different nodes,
+ * they are maintained as list of "server_port" under "server" structure.
+ * Note: Lock the server_list_lock before accessing this function.
+ */
 static struct msm_ipc_server *msm_ipc_router_create_server(
 					uint32_t service,
 					uint32_t instance,
@@ -623,9 +652,8 @@
 {
 	struct msm_ipc_server *server = NULL;
 	struct msm_ipc_server_port *server_port;
-	int key = (instance & (SRV_HASH_SIZE - 1));
+	int key = (service & (SRV_HASH_SIZE - 1));
 
-	mutex_lock(&server_list_lock);
 	list_for_each_entry(server, &server_list[key], list) {
 		if ((server->name.service == service) &&
 		    (server->name.instance == instance))
@@ -634,7 +662,6 @@
 
 	server = kmalloc(sizeof(struct msm_ipc_server), GFP_KERNEL);
 	if (!server) {
-		mutex_unlock(&server_list_lock);
 		pr_err("%s: Server allocation failed\n", __func__);
 		return NULL;
 	}
@@ -650,7 +677,6 @@
 			list_del(&server->list);
 			kfree(server);
 		}
-		mutex_unlock(&server_list_lock);
 		pr_err("%s: Server Port allocation failed\n", __func__);
 		return NULL;
 	}
@@ -658,11 +684,22 @@
 	server_port->server_addr.port_id = port_id;
 	server_port->xprt_info = xprt_info;
 	list_add_tail(&server_port->list, &server->server_port_list);
-	mutex_unlock(&server_list_lock);
 
 	return server;
 }
 
+/**
+ * msm_ipc_router_destroy_server() - Remove server info from hash table
+ * @server: Server info to be removed.
+ * @node_id: Node/Processor ID in which the server is hosted.
+ * @port_id: Port ID within the node in which the server is hosted.
+ *
+ * This function removes the server_port identified using <node_id:port_id>
+ * from the server structure. If the server_port list under server structure
+ * is empty after removal, then remove the server structure from the server
+ * hash table.
+ * Note: Lock the server_list_lock before accessing this function.
+ */
 static void msm_ipc_router_destroy_server(struct msm_ipc_server *server,
 					  uint32_t node_id, uint32_t port_id)
 {
@@ -671,7 +708,6 @@
 	if (!server)
 		return;
 
-	mutex_lock(&server_list_lock);
 	list_for_each_entry(server_port, &server->server_port_list, list) {
 		if ((server_port->server_addr.node_id == node_id) &&
 		    (server_port->server_addr.port_id == port_id))
@@ -685,7 +721,6 @@
 		list_del(&server->list);
 		kfree(server);
 	}
-	mutex_unlock(&server_list_lock);
 	return;
 }
 
@@ -765,7 +800,7 @@
 	return ret;
 }
 
-static int msm_ipc_router_send_server_list(
+static int msm_ipc_router_send_server_list(uint32_t node_id,
 		struct msm_ipc_router_xprt_info *xprt_info)
 {
 	union rr_control_msg ctl;
@@ -780,15 +815,14 @@
 
 	ctl.cmd = IPC_ROUTER_CTRL_CMD_NEW_SERVER;
 
-	mutex_lock(&server_list_lock);
 	for (i = 0; i < SRV_HASH_SIZE; i++) {
 		list_for_each_entry(server, &server_list[i], list) {
 			ctl.srv.service = server->name.service;
 			ctl.srv.instance = server->name.instance;
 			list_for_each_entry(server_port,
 					    &server->server_port_list, list) {
-				if (server_port->server_addr.node_id ==
-				    xprt_info->remote_node_id)
+				if (server_port->server_addr.node_id !=
+				    node_id)
 					continue;
 
 				ctl.srv.node_id =
@@ -800,7 +834,6 @@
 			}
 		}
 	}
-	mutex_unlock(&server_list_lock);
 
 	return 0;
 }
@@ -1166,14 +1199,89 @@
 	msm_ipc_cleanup_routing_table(xprt_info);
 }
 
+static int process_hello_msg(struct msm_ipc_router_xprt_info *xprt_info,
+			     struct rr_header *hdr)
+{
+	int i, rc = 0;
+	union rr_control_msg ctl;
+	struct msm_ipc_routing_table_entry *rt_entry;
+
+	if (!hdr)
+		return -EINVAL;
+
+	RR("o HELLO NID %d\n", hdr->src_node_id);
+
+	xprt_info->remote_node_id = hdr->src_node_id;
+	/*
+	 * Find the entry from Routing Table corresponding to Node ID.
+	 * Under SSR, an entry will be found. When the system boots up
+	 * for the 1st time, an entry will not be found and hence allocate
+	 * an entry. Update the entry with the Node ID that it corresponds
+	 * to and the XPRT through which it can be reached.
+	 */
+	mutex_lock(&routing_table_lock);
+	rt_entry = lookup_routing_table(hdr->src_node_id);
+	if (!rt_entry) {
+		rt_entry = alloc_routing_table_entry(hdr->src_node_id);
+		if (!rt_entry) {
+			mutex_unlock(&routing_table_lock);
+			pr_err("%s: rt_entry allocation failed\n", __func__);
+			return -ENOMEM;
+		}
+		add_routing_table_entry(rt_entry);
+	}
+	mutex_lock(&rt_entry->lock);
+	rt_entry->neighbor_node_id = xprt_info->remote_node_id;
+	rt_entry->xprt_info = xprt_info;
+	mutex_unlock(&rt_entry->lock);
+	mutex_unlock(&routing_table_lock);
+
+	/* Cleanup any remote ports, if the node is coming out of reset */
+	msm_ipc_cleanup_remote_port_info(xprt_info->remote_node_id);
+
+	/* Send a reply HELLO message */
+	memset(&ctl, 0, sizeof(ctl));
+	ctl.cmd = IPC_ROUTER_CTRL_CMD_HELLO;
+	rc = msm_ipc_router_send_control_msg(xprt_info, &ctl);
+	if (rc < 0) {
+		pr_err("%s: Error sending reply HELLO message\n", __func__);
+		return rc;
+	}
+	xprt_info->initialized = 1;
+
+	/*
+	 * Send list of servers from the local node and from nodes
+	 * outside the mesh network in which this XPRT is part of.
+	 */
+	mutex_lock(&server_list_lock);
+	mutex_lock(&routing_table_lock);
+	for (i = 0; i < RT_HASH_SIZE; i++) {
+		list_for_each_entry(rt_entry, &routing_table[i], list) {
+			if ((rt_entry->node_id != IPC_ROUTER_NID_LOCAL) &&
+			    (rt_entry->xprt_info->xprt->link_id ==
+			     xprt_info->xprt->link_id))
+				continue;
+			rc = msm_ipc_router_send_server_list(rt_entry->node_id,
+							     xprt_info);
+			if (rc < 0) {
+				mutex_unlock(&routing_table_lock);
+				mutex_unlock(&server_list_lock);
+				return rc;
+			}
+		}
+	}
+	mutex_unlock(&routing_table_lock);
+	mutex_unlock(&server_list_lock);
+	RR("HELLO message processed\n");
+	return rc;
+}
+
 static int process_control_msg(struct msm_ipc_router_xprt_info *xprt_info,
 			       struct rr_packet *pkt)
 {
-	union rr_control_msg ctl;
 	union rr_control_msg *msg;
 	struct msm_ipc_router_remote_port *rport_ptr;
 	int rc = 0;
-	static uint32_t first = 1;
 	struct sk_buff *temp_ptr;
 	struct rr_header *hdr;
 	struct msm_ipc_server *server;
@@ -1199,43 +1307,9 @@
 
 	switch (msg->cmd) {
 	case IPC_ROUTER_CTRL_CMD_HELLO:
-		RR("o HELLO NID %d\n", hdr->src_node_id);
-		xprt_info->remote_node_id = hdr->src_node_id;
-
-		mutex_lock(&routing_table_lock);
-		rt_entry = lookup_routing_table(hdr->src_node_id);
-		if (!rt_entry) {
-			rt_entry = alloc_routing_table_entry(hdr->src_node_id);
-			if (!rt_entry) {
-				mutex_unlock(&routing_table_lock);
-				pr_err("%s: rt_entry allocation failed\n",
-					__func__);
-				return -ENOMEM;
-			}
-			add_routing_table_entry(rt_entry);
-		}
-		mutex_lock(&rt_entry->lock);
-		rt_entry->neighbor_node_id = xprt_info->remote_node_id;
-		rt_entry->xprt_info = xprt_info;
-		mutex_unlock(&rt_entry->lock);
-		mutex_unlock(&routing_table_lock);
-		msm_ipc_cleanup_remote_port_info(xprt_info->remote_node_id);
-
-		memset(&ctl, 0, sizeof(ctl));
-		ctl.cmd = IPC_ROUTER_CTRL_CMD_HELLO;
-		msm_ipc_router_send_control_msg(xprt_info, &ctl);
-
-		xprt_info->initialized = 1;
-
-		/* Send list of servers one at a time */
-		msm_ipc_router_send_server_list(xprt_info);
-
-		if (first) {
-			first = 0;
-			complete_all(&msm_ipc_remote_router_up);
-		}
-		RR("HELLO message processed\n");
+		rc = process_hello_msg(xprt_info, hdr);
 		break;
+
 	case IPC_ROUTER_CTRL_CMD_RESUME_TX:
 		RR("o RESUME_TX id=%d:%08x\n",
 		   msg->cli.node_id, msg->cli.port_id);
@@ -1282,6 +1356,7 @@
 		}
 		mutex_unlock(&routing_table_lock);
 
+		mutex_lock(&server_list_lock);
 		server = msm_ipc_router_lookup_server(msg->srv.service,
 						      msg->srv.instance,
 						      msg->srv.node_id,
@@ -1291,6 +1366,7 @@
 				msg->srv.service, msg->srv.instance,
 				msg->srv.node_id, msg->srv.port_id, xprt_info);
 			if (!server) {
+				mutex_unlock(&server_list_lock);
 				pr_err("%s: Server Create failed\n", __func__);
 				return -ENOMEM;
 			}
@@ -1305,6 +1381,7 @@
 			}
 			wake_up(&newserver_wait);
 		}
+		mutex_unlock(&server_list_lock);
 
 		relay_msg(xprt_info, pkt);
 		post_control_ports(pkt);
@@ -1312,6 +1389,7 @@
 	case IPC_ROUTER_CTRL_CMD_REMOVE_SERVER:
 		RR("o REMOVE_SERVER service=%08x:%d\n",
 		   msg->srv.service, msg->srv.instance);
+		mutex_lock(&server_list_lock);
 		server = msm_ipc_router_lookup_server(msg->srv.service,
 						      msg->srv.instance,
 						      msg->srv.node_id,
@@ -1323,6 +1401,7 @@
 			relay_msg(xprt_info, pkt);
 			post_control_ports(pkt);
 		}
+		mutex_unlock(&server_list_lock);
 		break;
 	case IPC_ROUTER_CTRL_CMD_REMOVE_CLIENT:
 		RR("o REMOVE_CLIENT id=%d:%08x\n",
@@ -1507,11 +1586,13 @@
 	if (name->addrtype != MSM_IPC_ADDR_NAME)
 		return -EINVAL;
 
+	mutex_lock(&server_list_lock);
 	server = msm_ipc_router_lookup_server(name->addr.port_name.service,
 					      name->addr.port_name.instance,
 					      IPC_ROUTER_NID_LOCAL,
 					      port_ptr->this_port.port_id);
 	if (server) {
+		mutex_unlock(&server_list_lock);
 		pr_err("%s: Server already present\n", __func__);
 		return -EINVAL;
 	}
@@ -1522,6 +1603,7 @@
 					      port_ptr->this_port.port_id,
 					      NULL);
 	if (!server) {
+		mutex_unlock(&server_list_lock);
 		pr_err("%s: Server Creation failed\n", __func__);
 		return -EINVAL;
 	}
@@ -1531,6 +1613,7 @@
 	ctl.srv.instance = server->name.instance;
 	ctl.srv.node_id = IPC_ROUTER_NID_LOCAL;
 	ctl.srv.port_id = port_ptr->this_port.port_id;
+	mutex_unlock(&server_list_lock);
 	broadcast_ctl_msg(&ctl);
 	spin_lock_irqsave(&port_ptr->port_lock, flags);
 	port_ptr->type = SERVER_PORT;
@@ -1561,11 +1644,13 @@
 		return -EINVAL;
 	}
 
+	mutex_lock(&server_list_lock);
 	server = msm_ipc_router_lookup_server(port_ptr->port_name.service,
 					      port_ptr->port_name.instance,
 					      port_ptr->this_port.node_id,
 					      port_ptr->this_port.port_id);
 	if (!server) {
+		mutex_unlock(&server_list_lock);
 		pr_err("%s: Server lookup failed\n", __func__);
 		return -ENODEV;
 	}
@@ -1575,9 +1660,10 @@
 	ctl.srv.instance = server->name.instance;
 	ctl.srv.node_id = IPC_ROUTER_NID_LOCAL;
 	ctl.srv.port_id = port_ptr->this_port.port_id;
-	broadcast_ctl_msg(&ctl);
 	msm_ipc_router_destroy_server(server, port_ptr->this_port.node_id,
 				      port_ptr->this_port.port_id);
+	mutex_unlock(&server_list_lock);
+	broadcast_ctl_msg(&ctl);
 	spin_lock_irqsave(&port_ptr->port_lock, flags);
 	port_ptr->type = CLIENT_PORT;
 	spin_unlock_irqrestore(&port_ptr->port_lock, flags);
@@ -1776,15 +1862,16 @@
 		dst_node_id = dest->addr.port_addr.node_id;
 		dst_port_id = dest->addr.port_addr.port_id;
 	} else if (dest->addrtype == MSM_IPC_ADDR_NAME) {
+		mutex_lock(&server_list_lock);
 		server = msm_ipc_router_lookup_server(
 					dest->addr.port_name.service,
 					dest->addr.port_name.instance,
 					0, 0);
 		if (!server) {
+			mutex_unlock(&server_list_lock);
 			pr_err("%s: Destination not reachable\n", __func__);
 			return -ENODEV;
 		}
-		mutex_lock(&server_list_lock);
 		server_port = list_first_entry(&server->server_port_list,
 					       struct msm_ipc_server_port,
 					       list);
@@ -1968,6 +2055,7 @@
 	mutex_unlock(&port_ptr->port_rx_q_lock);
 
 	if (port_ptr->type == SERVER_PORT) {
+		mutex_lock(&server_list_lock);
 		server = msm_ipc_router_lookup_server(
 				port_ptr->port_name.service,
 				port_ptr->port_name.instance,
@@ -1977,6 +2065,7 @@
 			msm_ipc_router_destroy_server(server,
 				port_ptr->this_port.node_id,
 				port_ptr->this_port.port_id);
+		mutex_unlock(&server_list_lock);
 	}
 
 	wake_lock_destroy(&port_ptr->port_rx_wake_lock);
@@ -2041,27 +2130,24 @@
 	mutex_lock(&server_list_lock);
 	if (!lookup_mask)
 		lookup_mask = 0xFFFFFFFF;
-	for (key = 0; key < SRV_HASH_SIZE; key++) {
-		list_for_each_entry(server, &server_list[key], list) {
-			if ((server->name.service != srv_name->service) ||
-			    ((server->name.instance & lookup_mask) !=
-				srv_name->instance))
-				continue;
+	key = (srv_name->service & (SRV_HASH_SIZE - 1));
+	list_for_each_entry(server, &server_list[key], list) {
+		if ((server->name.service != srv_name->service) ||
+		    ((server->name.instance & lookup_mask) !=
+			srv_name->instance))
+			continue;
 
-			list_for_each_entry(server_port,
-				&server->server_port_list, list) {
-				if (i < num_entries_in_array) {
-					srv_info[i].node_id =
+		list_for_each_entry(server_port,
+			&server->server_port_list, list) {
+			if (i < num_entries_in_array) {
+				srv_info[i].node_id =
 					  server_port->server_addr.node_id;
-					srv_info[i].port_id =
+				srv_info[i].port_id =
 					  server_port->server_addr.port_id;
-					srv_info[i].service =
-					  server->name.service;
-					srv_info[i].instance =
-					  server->name.instance;
-				}
-				i++;
+				srv_info[i].service = server->name.service;
+				srv_info[i].instance = server->name.instance;
 			}
+			i++;
 		}
 	}
 	mutex_unlock(&server_list_lock);
diff --git a/arch/arm/mach-msm/ipc_router_smd_xprt.c b/arch/arm/mach-msm/ipc_router_smd_xprt.c
index b2e6490..8c0bf4b 100644
--- a/arch/arm/mach-msm/ipc_router_smd_xprt.c
+++ b/arch/arm/mach-msm/ipc_router_smd_xprt.c
@@ -20,7 +20,7 @@
 #include <linux/types.h>
 
 #include <mach/msm_smd.h>
-#include <mach/peripheral-loader.h>
+#include <mach/subsystem_restart.h>
 
 #include "ipc_router.h"
 #include "smd_private.h"
@@ -210,7 +210,7 @@
 
 	rc = smd_close(smd_xprtp->channel);
 	if (smd_xprtp->pil) {
-		pil_put(smd_xprtp->pil);
+		subsystem_put(smd_xprtp->pil);
 		smd_xprtp->pil = NULL;
 	}
 	return rc;
@@ -402,7 +402,7 @@
 
 	peripheral = smd_edge_to_subsystem(edge);
 	if (peripheral) {
-		pil = pil_get(peripheral);
+		pil = subsystem_get(peripheral);
 		if (IS_ERR(pil)) {
 			pr_err("%s: Failed to load %s\n",
 				__func__, peripheral);
@@ -460,7 +460,7 @@
 		pr_err("%s: Channel open failed for %s\n",
 			__func__, smd_xprt_cfg[id].ch_name);
 		if (smd_remote_xprt[id].pil) {
-			pil_put(smd_remote_xprt[id].pil);
+			subsystem_put(smd_remote_xprt[id].pil);
 			smd_remote_xprt[id].pil = NULL;
 		}
 		destroy_workqueue(smd_remote_xprt[id].smd_xprt_wq);
@@ -481,7 +481,7 @@
 
 	peripheral = smd_edge_to_subsystem(SMD_APPS_MODEM);
 	if (peripheral && !strncmp(peripheral, "modem", 6)) {
-		pil = pil_get(peripheral);
+		pil = subsystem_get(peripheral);
 		if (IS_ERR(pil)) {
 			pr_err("%s: Failed to load %s\n",
 				__func__, peripheral);
@@ -495,7 +495,7 @@
 void msm_ipc_unload_default_node(void *pil)
 {
 	if (pil)
-		pil_put(pil);
+		subsystem_put(pil);
 }
 EXPORT_SYMBOL(msm_ipc_unload_default_node);
 
diff --git a/arch/arm/mach-msm/krait-regulator.c b/arch/arm/mach-msm/krait-regulator.c
index 96c4809..aa9b344 100644
--- a/arch/arm/mach-msm/krait-regulator.c
+++ b/arch/arm/mach-msm/krait-regulator.c
@@ -27,6 +27,7 @@
 #include <linux/regulator/machine.h>
 #include <linux/regulator/of_regulator.h>
 #include <linux/regulator/krait-regulator.h>
+#include <mach/msm_iomap.h>
 
 #include "spm.h"
 
@@ -87,6 +88,11 @@
 #define CPU_TRGTD_DBG_RST	0x00000010
 #define APC_PWR_GATE_CTL	0x00000014
 #define APC_LDO_VREF_SET	0x00000018
+#define APC_PWR_GATE_MODE	0x0000001C
+#define APC_PWR_GATE_DLY	0x00000020
+
+#define PWR_GATE_CONFIG		0x00000044
+#define VERSION			0x00000FD0
 
 /* bit definitions for APC_PWR_GATE_CTL */
 #define BHS_CNT_BIT_POS		24
@@ -135,6 +141,7 @@
 	int			pmic_phase_count;
 	struct list_head	krait_power_vregs;
 	struct mutex		krait_power_vregs_lock;
+	bool			pfm_mode;
 };
 
 static struct pmic_gang_vreg *the_gang;
@@ -156,6 +163,8 @@
 	void __iomem			*reg_base;
 };
 
+static u32 version;
+
 static void krait_masked_write(struct krait_power_vreg *kvreg,
 					int reg, uint32_t mask, uint32_t val)
 {
@@ -534,6 +543,9 @@
 	return rc;
 }
 
+#define PMIC_FTS_MODE_PFM	0x00
+#define PMIC_FTS_MODE_PWM	0x80
+#define PFM_LOAD_UA		500000
 static unsigned int krait_power_get_optimum_mode(struct regulator_dev *rdev,
 			int input_uV, int output_uV, int load_uA)
 {
@@ -545,10 +557,40 @@
 
 	mutex_lock(&pvreg->krait_power_vregs_lock);
 
+	reg_mode = kvreg->mode;
+
 	kvreg->load_uA = load_uA;
 
 	load_total_uA = get_total_load(kvreg);
 
+	if (load_total_uA < PFM_LOAD_UA) {
+		if (!pvreg->pfm_mode) {
+			rc = msm_spm_enable_fts_lpm(PMIC_FTS_MODE_PFM);
+			if (rc) {
+				dev_err(&rdev->dev,
+					"%s enter PFM failed load %d rc = %d\n",
+					kvreg->name, load_total_uA, rc);
+				goto out;
+			} else {
+				pvreg->pfm_mode = true;
+			}
+		}
+		mutex_unlock(&pvreg->krait_power_vregs_lock);
+		return reg_mode;
+	}
+
+	if (pvreg->pfm_mode) {
+		rc = msm_spm_enable_fts_lpm(PMIC_FTS_MODE_PWM);
+		if (rc) {
+			dev_err(&rdev->dev,
+				"%s exit PFM failed load %d rc = %d\n",
+				kvreg->name, load_total_uA, rc);
+			goto out;
+		} else {
+			pvreg->pfm_mode = false;
+		}
+	}
+
 	rc = pmic_gang_set_phases(kvreg, load_total_uA);
 	if (rc < 0) {
 		dev_err(&rdev->dev, "%s failed set mode %d rc = %d\n",
@@ -556,7 +598,6 @@
 		goto out;
 	}
 
-	reg_mode = kvreg->mode;
 out:
 	mutex_unlock(&pvreg->krait_power_vregs_lock);
 	return reg_mode;
@@ -599,6 +640,15 @@
 		BHS_SEG_EN_MASK, BHS_SEG_EN_DEFAULT << BHS_SEG_EN_BIT_POS);
 }
 
+static void glb_init(struct platform_device *pdev)
+{
+	/* configure bi-modal switch */
+	writel_relaxed(0x0008736E, MSM_APCS_GCC_BASE + PWR_GATE_CONFIG);
+	/* read kpss version */
+	version = readl_relaxed(MSM_APCS_GCC_BASE + VERSION);
+	pr_debug("version= 0x%x\n", version);
+}
+
 static int __devinit krait_power_probe(struct platform_device *pdev)
 {
 	struct krait_power_vreg *kvreg;
@@ -614,6 +664,8 @@
 				"failed to init pmic gang rc = %d\n", rc);
 			return rc;
 		}
+		/* global initializtion */
+		glb_init(pdev);
 	}
 
 	if (pdev->dev.of_node) {
@@ -738,8 +790,10 @@
 {
 	/* 605mV retention and 705mV operational voltage */
 	writel_relaxed(0x1C30, base_ptr + APC_LDO_VREF_SET);
-	writel_relaxed(0x430000, base_ptr + 0x20);
-	writel_relaxed(0x21, base_ptr + 0x1C);
+	/* HS_EN_DLY=3; LDO_BYP_DLY=1; */
+	writel_relaxed(0x430000, base_ptr + APC_PWR_GATE_DLY);
+	/* MODE = BHS; EN=1; */
+	writel_relaxed(0x21, base_ptr + APC_PWR_GATE_MODE);
 
 	/* Turn on the BHS, turn off LDO Bypass and power down LDO */
 	writel_relaxed(0x403F007F, base_ptr + APC_PWR_GATE_CTL);
diff --git a/arch/arm/mach-msm/lpass-8960.c b/arch/arm/mach-msm/lpass-8960.c
deleted file mode 100644
index b714a7f..0000000
--- a/arch/arm/mach-msm/lpass-8960.c
+++ /dev/null
@@ -1,290 +0,0 @@
-/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- */
-
-#include <linux/kernel.h>
-#include <linux/interrupt.h>
-#include <linux/reboot.h>
-#include <linux/workqueue.h>
-#include <linux/io.h>
-#include <linux/delay.h>
-#include <linux/module.h>
-#include <linux/err.h>
-
-#include <mach/irqs.h>
-#include <mach/scm.h>
-#include <mach/peripheral-loader.h>
-#include <mach/subsystem_restart.h>
-#include <mach/subsystem_notif.h>
-
-#include "smd_private.h"
-#include "ramdump.h"
-#include "sysmon.h"
-
-#define SCM_Q6_NMI_CMD                  0x1
-#define MODULE_NAME			"lpass_8960"
-#define MAX_BUF_SIZE			0x51
-
-/* Subsystem restart: QDSP6 data, functions */
-static void lpass_fatal_fn(struct work_struct *);
-static DECLARE_WORK(lpass_fatal_work, lpass_fatal_fn);
-struct lpass_ssr {
-	void *lpass_ramdump_dev;
-} lpass_ssr;
-
-static struct lpass_ssr lpass_ssr_8960;
-static int q6_crash_shutdown;
-
-static int riva_notifier_cb(struct notifier_block *this, unsigned long code,
-								void *ss_handle)
-{
-	int ret;
-	switch (code) {
-	case SUBSYS_BEFORE_SHUTDOWN:
-		pr_debug("%s: R-Notify: Shutdown started\n", __func__);
-		ret = sysmon_send_event(SYSMON_SS_LPASS, "wcnss",
-				SUBSYS_BEFORE_SHUTDOWN);
-		if (ret < 0)
-			pr_err("%s: sysmon_send_event error %d", __func__,
-				ret);
-		break;
-	}
-	return NOTIFY_DONE;
-}
-
-static void *ssr_notif_hdle;
-static struct notifier_block rnb = {
-	.notifier_call = riva_notifier_cb,
-};
-
-static int modem_notifier_cb(struct notifier_block *this, unsigned long code,
-								void *ss_handle)
-{
-	int ret;
-	switch (code) {
-	case SUBSYS_BEFORE_SHUTDOWN:
-		pr_debug("%s: M-Notify: Shutdown started\n", __func__);
-		ret = sysmon_send_event(SYSMON_SS_LPASS, "modem",
-				SUBSYS_BEFORE_SHUTDOWN);
-		if (ret < 0)
-			pr_err("%s: sysmon_send_event error %d", __func__,
-				ret);
-		break;
-	}
-	return NOTIFY_DONE;
-}
-
-static void *ssr_modem_notif_hdle;
-static struct notifier_block mnb = {
-	.notifier_call = modem_notifier_cb,
-};
-
-static void lpass_log_failure_reason(void)
-{
-	char *reason;
-	char buffer[MAX_BUF_SIZE];
-	unsigned size;
-
-	reason = smem_get_entry(SMEM_SSR_REASON_LPASS0, &size);
-
-	if (!reason) {
-		pr_err("%s: subsystem failure reason: (unknown, smem_get_entry failed).",
-			 MODULE_NAME);
-		return;
-	}
-
-	if (reason[0] == '\0') {
-		pr_err("%s: subsystem failure reason: (unknown, init value found)",
-			 MODULE_NAME);
-		return;
-	}
-
-	size = size < MAX_BUF_SIZE ? size : (MAX_BUF_SIZE-1);
-	memcpy(buffer, reason, size);
-	buffer[size] = '\0';
-	pr_err("%s: subsystem failure reason: %s", MODULE_NAME, buffer);
-	memset((void *)reason, 0x0, size);
-	wmb();
-}
-
-static void lpass_fatal_fn(struct work_struct *work)
-{
-	pr_err("%s %s: Watchdog bite received from Q6!\n", MODULE_NAME,
-		__func__);
-	lpass_log_failure_reason();
-	panic(MODULE_NAME ": Resetting the SoC");
-}
-
-static void lpass_smsm_state_cb(void *data, uint32_t old_state,
-				uint32_t new_state)
-{
-	/* Ignore if we're the one that set SMSM_RESET */
-	if (q6_crash_shutdown)
-		return;
-
-	if (new_state & SMSM_RESET) {
-		pr_err("%s: LPASS SMSM state changed to SMSM_RESET,"
-			" new_state = 0x%x, old_state = 0x%x\n", __func__,
-			new_state, old_state);
-		lpass_log_failure_reason();
-		panic(MODULE_NAME ": Resetting the SoC");
-	}
-}
-
-static void send_q6_nmi(void)
-{
-	/* Send NMI to QDSP6 via an SCM call. */
-	uint32_t cmd = 0x1;
-
-	scm_call(SCM_SVC_UTIL, SCM_Q6_NMI_CMD,
-	&cmd, sizeof(cmd), NULL, 0);
-
-	/* Q6 requires worstcase 100ms to dump caches etc.*/
-	mdelay(100);
-	pr_debug("%s: Q6 NMI was sent.\n", __func__);
-}
-
-static int lpass_shutdown(const struct subsys_desc *subsys)
-{
-	send_q6_nmi();
-	pil_force_shutdown("q6");
-	disable_irq_nosync(LPASS_Q6SS_WDOG_EXPIRED);
-
-	return 0;
-}
-
-static int lpass_powerup(const struct subsys_desc *subsys)
-{
-	int ret = pil_force_boot("q6");
-	enable_irq(LPASS_Q6SS_WDOG_EXPIRED);
-	return ret;
-}
-/* RAM segments - address and size for 8960 */
-static struct ramdump_segment q6_segments[] = { {0x8da00000, 0x8f200000 -
-					0x8da00000}, {0x28400000, 0x20000} };
-static int lpass_ramdump(int enable, const struct subsys_desc *subsys)
-{
-	pr_debug("%s: enable[%d]\n", __func__, enable);
-	if (enable)
-		return do_ramdump(lpass_ssr_8960.lpass_ramdump_dev,
-				q6_segments,
-				ARRAY_SIZE(q6_segments));
-	else
-		return 0;
-}
-
-static void lpass_crash_shutdown(const struct subsys_desc *subsys)
-{
-	q6_crash_shutdown = 1;
-	send_q6_nmi();
-}
-
-static irqreturn_t lpass_wdog_bite_irq(int irq, void *dev_id)
-{
-	int ret;
-
-	pr_debug("%s: rxed irq[0x%x]", __func__, irq);
-	disable_irq_nosync(LPASS_Q6SS_WDOG_EXPIRED);
-	ret = schedule_work(&lpass_fatal_work);
-
-	return IRQ_HANDLED;
-}
-
-static struct subsys_device *lpass_8960_dev;
-
-static struct subsys_desc lpass_8960 = {
-	.name = "lpass",
-	.shutdown = lpass_shutdown,
-	.powerup = lpass_powerup,
-	.ramdump = lpass_ramdump,
-	.crash_shutdown = lpass_crash_shutdown
-};
-
-static int __init lpass_restart_init(void)
-{
-	lpass_8960_dev = subsys_register(&lpass_8960);
-	if (IS_ERR(lpass_8960_dev))
-		return PTR_ERR(lpass_8960_dev);
-	return 0;
-}
-
-static int __init lpass_fatal_init(void)
-{
-	int ret;
-
-	ret = smsm_state_cb_register(SMSM_Q6_STATE, SMSM_RESET,
-		lpass_smsm_state_cb, 0);
-
-	if (ret < 0)
-		pr_err("%s: Unable to register SMSM callback! (%d)\n",
-				__func__, ret);
-
-	ret = request_irq(LPASS_Q6SS_WDOG_EXPIRED, lpass_wdog_bite_irq,
-			IRQF_TRIGGER_RISING, "q6_wdog", NULL);
-
-	if (ret < 0) {
-		pr_err("%s: Unable to request LPASS_Q6SS_WDOG_EXPIRED irq.",
-			__func__);
-		goto out;
-	}
-	ret = lpass_restart_init();
-	if (ret < 0) {
-		pr_err("%s: Unable to reg with lpass ssr. (%d)\n",
-				__func__, ret);
-		goto out;
-	}
-
-	lpass_ssr_8960.lpass_ramdump_dev = create_ramdump_device("lpass");
-
-	if (!lpass_ssr_8960.lpass_ramdump_dev) {
-		pr_err("%s: Unable to create ramdump device.\n",
-				__func__);
-		ret = -ENOMEM;
-		goto out;
-	}
-	ssr_notif_hdle = subsys_notif_register_notifier("riva",
-							&rnb);
-	if (IS_ERR(ssr_notif_hdle) < 0) {
-		ret = PTR_ERR(ssr_notif_hdle);
-		pr_err("%s: subsys_register_notifier for Riva: err = %d\n",
-			__func__, ret);
-		free_irq(LPASS_Q6SS_WDOG_EXPIRED, NULL);
-		goto out;
-	}
-
-	ssr_modem_notif_hdle = subsys_notif_register_notifier("modem",
-							&mnb);
-	if (IS_ERR(ssr_modem_notif_hdle) < 0) {
-		ret = PTR_ERR(ssr_modem_notif_hdle);
-		pr_err("%s: subsys_register_notifier for Modem: err = %d\n",
-			__func__, ret);
-		subsys_notif_unregister_notifier(ssr_notif_hdle, &rnb);
-		free_irq(LPASS_Q6SS_WDOG_EXPIRED, NULL);
-		goto out;
-	}
-
-	pr_info("%s: lpass SSR driver init'ed.\n", __func__);
-out:
-	return ret;
-}
-
-static void __exit lpass_fatal_exit(void)
-{
-	subsys_notif_unregister_notifier(ssr_notif_hdle, &rnb);
-	subsys_notif_unregister_notifier(ssr_modem_notif_hdle, &mnb);
-	subsys_unregister(lpass_8960_dev);
-	free_irq(LPASS_Q6SS_WDOG_EXPIRED, NULL);
-}
-
-module_init(lpass_fatal_init);
-module_exit(lpass_fatal_exit);
-
-MODULE_LICENSE("GPL v2");
diff --git a/arch/arm/mach-msm/lpm_levels.c b/arch/arm/mach-msm/lpm_levels.c
index 8218a42..4854fc48 100644
--- a/arch/arm/mach-msm/lpm_levels.c
+++ b/arch/arm/mach-msm/lpm_levels.c
@@ -22,6 +22,18 @@
 #include "pm.h"
 #include "rpm-notifier.h"
 
+
+enum {
+	MSM_LPM_LVL_DBG_SUSPEND_LIMITS = BIT(0),
+	MSM_LPM_LVL_DBG_IDLE_LIMITS = BIT(1),
+};
+
+static int msm_lpm_lvl_dbg_msk;
+
+module_param_named(
+	debug_mask, msm_lpm_lvl_dbg_msk, int, S_IRUGO | S_IWUSR | S_IWGRP
+);
+
 static struct msm_rpmrs_level *msm_lpm_levels;
 static int msm_lpm_level_count;
 
@@ -41,6 +53,7 @@
 		bool from_idle, bool notify_rpm)
 {
 	int ret = 0;
+	int debug_mask;
 	struct msm_rpmrs_limits *l = (struct msm_rpmrs_limits *)limits;
 
 	ret = msm_rpm_enter_sleep();
@@ -49,6 +62,21 @@
 				__func__, ret);
 		goto bail;
 	}
+	if (from_idle)
+		debug_mask = msm_lpm_lvl_dbg_msk &
+				MSM_LPM_LVL_DBG_IDLE_LIMITS;
+	else
+		debug_mask = msm_lpm_lvl_dbg_msk &
+				MSM_LPM_LVL_DBG_SUSPEND_LIMITS;
+
+	if (debug_mask)
+		pr_info("%s(): pxo:%d l2:%d mem:0x%x(0x%x) dig:0x%x(0x%x)\n",
+				__func__, l->pxo, l->l2_cache,
+				l->vdd_mem_lower_bound,
+				l->vdd_mem_upper_bound,
+				l->vdd_dig_lower_bound,
+				l->vdd_dig_upper_bound);
+
 	ret = msm_lpmrs_enter_sleep(sclk_count, l, from_idle, notify_rpm);
 bail:
 	return ret;
diff --git a/arch/arm/mach-msm/lpm_resources.c b/arch/arm/mach-msm/lpm_resources.c
index 48d31f3..255cd46 100644
--- a/arch/arm/mach-msm/lpm_resources.c
+++ b/arch/arm/mach-msm/lpm_resources.c
@@ -104,6 +104,11 @@
 	struct msm_rpm_request *handle;
 };
 
+enum {
+	MSM_LPM_RPM_RS_TYPE = 0,
+	MSM_LPM_LOCAL_RS_TYPE = 1,
+};
+
 struct msm_lpm_resource {
 	struct msm_lpm_rs_data rs_data;
 	uint32_t sleep_value;
@@ -126,7 +131,7 @@
 	.aggregate = msm_lpm_aggregate_l2,
 	.flush = msm_lpm_flush_l2,
 	.notify = NULL,
-	.valid = true,
+	.valid = false,
 	.rs_data = {
 		.value = MSM_LPM_L2_CACHE_ACTIVE,
 		.default_value = MSM_LPM_L2_CACHE_ACTIVE,
@@ -382,7 +387,7 @@
 static bool msm_lpm_beyond_limits_l2(struct msm_rpmrs_limits *limits)
 {
 	uint32_t l2;
-	bool ret = true;
+	bool ret = false;
 	struct msm_lpm_resource *rs = &msm_lpm_l2;
 
 	if (rs->valid) {
@@ -664,7 +669,7 @@
 	msm_lpm_get_rpm_notif = false;
 	for (i = 0; i < ARRAY_SIZE(msm_lpm_resources); i++) {
 		rs = msm_lpm_resources[i];
-		if (rs->flush)
+		if (rs->valid && rs->flush)
 			rs->flush(notify_rpm);
 	}
 	msm_lpm_get_rpm_notif = true;
@@ -690,6 +695,10 @@
 {
 	struct msm_lpm_resource *rs = &msm_lpm_l2;
 	switch (action) {
+	case CPU_UP_PREPARE:
+	case CPU_UP_PREPARE_FROZEN:
+		rs->rs_data.value = MSM_LPM_L2_CACHE_ACTIVE;
+		break;
 	case CPU_ONLINE_FROZEN:
 	case CPU_ONLINE:
 		if (num_online_cpus() > 1)
@@ -787,6 +796,7 @@
 		struct msm_lpm_resource *rs = NULL;
 		const char *val;
 		int i;
+		uint32_t resource_type;
 
 		key = "qcom,name";
 		ret = of_property_read_string(node, key, &val);
@@ -810,49 +820,73 @@
 			continue;
 		}
 
-		key = "qcom,type";
-		ret = of_property_read_u32(node, key, &rs->rs_data.type);
+		key = "qcom,resource-type";
+		ret = of_property_read_u32(node, key, &resource_type);
 		if (ret) {
-			pr_err("Failed to read type\n");
+			pr_err("Failed to read resource-type\n");
 			goto fail;
 		}
 
-		key = "qcom,id";
-		ret = of_property_read_u32(node, key, &rs->rs_data.id);
-		if (ret) {
-			pr_err("Failed to read id\n");
+		switch (resource_type) {
+		case MSM_LPM_RPM_RS_TYPE:
+			key = "qcom,type";
+			ret = of_property_read_u32(node, key,
+						&rs->rs_data.type);
+			if (ret) {
+				pr_err("Failed to read type\n");
+				goto fail;
+			}
+
+			key = "qcom,id";
+			ret = of_property_read_u32(node, key, &rs->rs_data.id);
+			if (ret) {
+				pr_err("Failed to read id\n");
+				goto fail;
+			}
+
+			key = "qcom,key";
+			ret = of_property_read_u32(node, key, &rs->rs_data.key);
+			if (ret) {
+				pr_err("Failed to read key\n");
+				goto fail;
+			}
+
+			rs->rs_data.handle = msm_lpm_create_rpm_request(
+						rs->rs_data.type,
+						rs->rs_data.id);
+
+			if (!rs->rs_data.handle) {
+				pr_err("%s: Failed to allocate handle for %s\n",
+						__func__, rs->name);
+				ret = -1;
+				goto fail;
+			}
+			/* fall through */
+
+		case MSM_LPM_LOCAL_RS_TYPE:
+			rs->valid = true;
+			break;
+		default:
+			pr_err("%s: Invalid resource type %d", __func__,
+					resource_type);
 			goto fail;
 		}
-
-		key = "qcom,key";
-		ret = of_property_read_u32(node, key, &rs->rs_data.key);
-		if (ret) {
-			pr_err("Failed to read key\n");
-			goto fail;
-		}
-
-		rs->rs_data.handle = msm_lpm_create_rpm_request(
-					rs->rs_data.type, rs->rs_data.id);
-
-		if (!rs->rs_data.handle) {
-			pr_err("%s: Failed to allocate handle for %s\n",
-					__func__, rs->name);
-			ret = -1;
-			goto fail;
-		}
-
-		rs->valid = true;
 	}
 	msm_rpm_register_notifier(&msm_lpm_rpm_nblk);
 	msm_lpm_init_rpm_ctl();
-	register_hotcpu_notifier(&msm_lpm_cpu_nblk);
-	/* For UP mode, set the default to HSFS OPEN*/
-	if (num_possible_cpus() == 1) {
-		msm_lpm_l2.rs_data.default_value = MSM_LPM_L2_CACHE_HSFS_OPEN;
-		msm_lpm_l2.rs_data.value = MSM_LPM_L2_CACHE_HSFS_OPEN;
-	}
-	msm_pm_set_l2_flush_flag(0);
-	return 0;
+
+	if (msm_lpm_l2.valid) {
+		register_hotcpu_notifier(&msm_lpm_cpu_nblk);
+		/* For UP mode, set the default to HSFS OPEN*/
+		if (num_possible_cpus() == 1) {
+			msm_lpm_l2.rs_data.default_value =
+					MSM_LPM_L2_CACHE_HSFS_OPEN;
+			msm_lpm_l2.rs_data.value = MSM_LPM_L2_CACHE_HSFS_OPEN;
+		}
+		msm_pm_set_l2_flush_flag(0);
+	} else
+		msm_pm_set_l2_flush_flag(1);
+
 fail:
 	return ret;
 }
diff --git a/arch/arm/mach-msm/mdm.c b/arch/arm/mach-msm/mdm.c
index 02978cf..8dd4bac 100644
--- a/arch/arm/mach-msm/mdm.c
+++ b/arch/arm/mach-msm/mdm.c
@@ -354,6 +354,7 @@
 		ret = PTR_ERR(charm_subsys);
 		goto fatal_err;
 	}
+	subsys_default_online(charm_subsys);
 
 	irq = platform_get_irq(pdev, 0);
 	if (irq < 0) {
diff --git a/arch/arm/mach-msm/mdm_common.c b/arch/arm/mach-msm/mdm_common.c
index 58b26f2..b81832e 100644
--- a/arch/arm/mach-msm/mdm_common.c
+++ b/arch/arm/mach-msm/mdm_common.c
@@ -681,6 +681,7 @@
 		ret = PTR_ERR(mdm_subsys_dev);
 		goto fatal_err;
 	}
+	subsys_default_online(mdm_subsys_dev);
 
 	/* ERR_FATAL irq. */
 	irq = MSM_GPIO_TO_INT(mdm_drv->mdm2ap_errfatal_gpio);
diff --git a/arch/arm/mach-msm/memory.c b/arch/arm/mach-msm/memory.c
index a785389..3fe65b8 100644
--- a/arch/arm/mach-msm/memory.c
+++ b/arch/arm/mach-msm/memory.c
@@ -44,14 +44,14 @@
 #include <../../mm/mm.h>
 #include <linux/fmem.h>
 
-void *strongly_ordered_page;
-char strongly_ordered_mem[PAGE_SIZE*2-4];
+#if defined(CONFIG_ARCH_MSM7X27)
+static void *strongly_ordered_page;
+static char strongly_ordered_mem[PAGE_SIZE*2-4];
 
-void map_page_strongly_ordered(void)
+void __init map_page_strongly_ordered(void)
 {
-#if defined(CONFIG_ARCH_MSM7X27) && !defined(CONFIG_ARCH_MSM7X27A)
 	long unsigned int phys;
-	struct map_desc map;
+	struct map_desc map[1];
 
 	if (strongly_ordered_page)
 		return;
@@ -59,33 +59,26 @@
 	strongly_ordered_page = (void*)PFN_ALIGN((int)&strongly_ordered_mem);
 	phys = __pa(strongly_ordered_page);
 
-	map.pfn = __phys_to_pfn(phys);
-	map.virtual = MSM_STRONGLY_ORDERED_PAGE;
-	map.length = PAGE_SIZE;
-	map.type = MT_DEVICE_STRONGLY_ORDERED;
-	create_mapping(&map);
+	map[0].pfn = __phys_to_pfn(phys);
+	map[0].virtual = MSM_STRONGLY_ORDERED_PAGE;
+	map[0].length = PAGE_SIZE;
+	map[0].type = MT_MEMORY_SO;
+	iotable_init(map, ARRAY_SIZE(map));
 
 	printk(KERN_ALERT "Initialized strongly ordered page successfully\n");
-#endif
 }
-EXPORT_SYMBOL(map_page_strongly_ordered);
+#else
+void map_page_strongly_ordered(void) { }
+#endif
 
+#if defined(CONFIG_ARCH_MSM7X27)
 void write_to_strongly_ordered_memory(void)
 {
-#if defined(CONFIG_ARCH_MSM7X27) && !defined(CONFIG_ARCH_MSM7X27A)
-	if (!strongly_ordered_page) {
-		if (!in_interrupt())
-			map_page_strongly_ordered();
-		else {
-			printk(KERN_ALERT "Cannot map strongly ordered page in "
-				"Interrupt Context\n");
-			/* capture it here before the allocation fails later */
-			BUG();
-		}
-	}
 	*(int *)MSM_STRONGLY_ORDERED_PAGE = 0;
-#endif
 }
+#else
+void write_to_strongly_ordered_memory(void) { }
+#endif
 EXPORT_SYMBOL(write_to_strongly_ordered_memory);
 
 /* These cache related routines make the assumption (if outer cache is
@@ -278,7 +271,8 @@
 	unsigned long msm_fixed_area_start;
 
 	memory_pool_init();
-	reserve_info->calculate_reserve_sizes();
+	if (reserve_info->calculate_reserve_sizes)
+		reserve_info->calculate_reserve_sizes();
 
 	msm_fixed_area_size = reserve_info->fixed_area_size;
 	msm_fixed_area_start = reserve_info->fixed_area_start;
@@ -470,3 +464,8 @@
 
 	return ret;
 }
+
+/* Provide a string that anonymous device tree allocations (those not
+ * directly associated with any driver) can use for their "compatible"
+ * field */
+EXPORT_COMPAT("qcom,msm-contig-mem");
diff --git a/arch/arm/mach-msm/modem-8960.c b/arch/arm/mach-msm/modem-8960.c
deleted file mode 100644
index 83b3bc4..0000000
--- a/arch/arm/mach-msm/modem-8960.c
+++ /dev/null
@@ -1,332 +0,0 @@
-/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- */
-
-#include <linux/kernel.h>
-#include <linux/interrupt.h>
-#include <linux/reboot.h>
-#include <linux/workqueue.h>
-#include <linux/io.h>
-#include <linux/jiffies.h>
-#include <linux/stringify.h>
-#include <linux/delay.h>
-#include <linux/module.h>
-#include <linux/debugfs.h>
-
-#include <mach/irqs.h>
-#include <mach/scm.h>
-#include <mach/peripheral-loader.h>
-#include <mach/subsystem_restart.h>
-#include <mach/subsystem_notif.h>
-#include <mach/socinfo.h>
-#include <mach/msm_smsm.h>
-
-#include "smd_private.h"
-#include "modem_notifier.h"
-#include "ramdump.h"
-
-static int crash_shutdown;
-
-static struct subsys_device *modem_8960_dev;
-
-#define MAX_SSR_REASON_LEN 81U
-
-static void log_modem_sfr(void)
-{
-	u32 size;
-	char *smem_reason, reason[MAX_SSR_REASON_LEN];
-
-	smem_reason = smem_get_entry(SMEM_SSR_REASON_MSS0, &size);
-	if (!smem_reason || !size) {
-		pr_err("modem subsystem failure reason: (unknown, smem_get_entry failed).\n");
-		return;
-	}
-	if (!smem_reason[0]) {
-		pr_err("modem subsystem failure reason: (unknown, init string found).\n");
-		return;
-	}
-
-	size = min(size, MAX_SSR_REASON_LEN-1);
-	memcpy(reason, smem_reason, size);
-	reason[size] = '\0';
-	pr_err("modem subsystem failure reason: %s.\n", reason);
-
-	smem_reason[0] = '\0';
-	wmb();
-}
-
-static void restart_modem(void)
-{
-	log_modem_sfr();
-	subsystem_restart_dev(modem_8960_dev);
-}
-
-static void smsm_state_cb(void *data, uint32_t old_state, uint32_t new_state)
-{
-	/* Ignore if we're the one that set SMSM_RESET */
-	if (crash_shutdown)
-		return;
-
-	if (new_state & SMSM_RESET) {
-		pr_err("Probable fatal error on the modem.\n");
-		restart_modem();
-	}
-}
-
-#define Q6_FW_WDOG_ENABLE		0x08882024
-#define Q6_SW_WDOG_ENABLE		0x08982024
-
-static int modem_shutdown(const struct subsys_desc *subsys)
-{
-	void __iomem *q6_fw_wdog_addr;
-	void __iomem *q6_sw_wdog_addr;
-
-	/*
-	 * Disable the modem watchdog since it keeps running even after the
-	 * modem is shutdown.
-	 */
-	q6_fw_wdog_addr = ioremap_nocache(Q6_FW_WDOG_ENABLE, 4);
-	if (!q6_fw_wdog_addr)
-		return -ENOMEM;
-
-	q6_sw_wdog_addr = ioremap_nocache(Q6_SW_WDOG_ENABLE, 4);
-	if (!q6_sw_wdog_addr) {
-		iounmap(q6_fw_wdog_addr);
-		return -ENOMEM;
-	}
-
-	writel_relaxed(0x0, q6_fw_wdog_addr);
-	writel_relaxed(0x0, q6_sw_wdog_addr);
-	mb();
-	iounmap(q6_sw_wdog_addr);
-	iounmap(q6_fw_wdog_addr);
-
-	pil_force_shutdown("modem");
-	pil_force_shutdown("modem_fw");
-	disable_irq_nosync(Q6FW_WDOG_EXPIRED_IRQ);
-	disable_irq_nosync(Q6SW_WDOG_EXPIRED_IRQ);
-
-	return 0;
-}
-
-#define MODEM_WDOG_CHECK_TIMEOUT_MS 10000
-
-static int modem_powerup(const struct subsys_desc *subsys)
-{
-	pil_force_boot("modem_fw");
-	pil_force_boot("modem");
-	enable_irq(Q6FW_WDOG_EXPIRED_IRQ);
-	enable_irq(Q6SW_WDOG_EXPIRED_IRQ);
-	return 0;
-}
-
-void modem_crash_shutdown(const struct subsys_desc *subsys)
-{
-	crash_shutdown = 1;
-	smsm_reset_modem(SMSM_RESET);
-}
-
-/* FIXME: Get address, size from PIL */
-static struct ramdump_segment modemsw_segments[] = {
-	{0x89000000, 0x8D400000 - 0x89000000},
-};
-
-static struct ramdump_segment modemfw_segments[] = {
-	{0x8D400000, 0x8DA00000 - 0x8D400000},
-};
-
-static struct ramdump_segment smem_segments[] = {
-	{0x80000000, 0x00200000},
-};
-
-static void *modemfw_ramdump_dev;
-static void *modemsw_ramdump_dev;
-static void *smem_ramdump_dev;
-
-static int modem_ramdump(int enable, const struct subsys_desc *crashed_subsys)
-{
-	int ret = 0;
-
-	if (enable) {
-		ret = do_ramdump(modemsw_ramdump_dev, modemsw_segments,
-			ARRAY_SIZE(modemsw_segments));
-
-		if (ret < 0) {
-			pr_err("Unable to dump modem sw memory (rc = %d).\n",
-			       ret);
-			goto out;
-		}
-
-		ret = do_ramdump(modemfw_ramdump_dev, modemfw_segments,
-			ARRAY_SIZE(modemfw_segments));
-
-		if (ret < 0) {
-			pr_err("Unable to dump modem fw memory (rc = %d).\n",
-				ret);
-			goto out;
-		}
-
-		ret = do_ramdump(smem_ramdump_dev, smem_segments,
-			ARRAY_SIZE(smem_segments));
-
-		if (ret < 0) {
-			pr_err("Unable to dump smem memory (rc = %d).\n", ret);
-			goto out;
-		}
-	}
-
-out:
-	return ret;
-}
-
-static irqreturn_t modem_wdog_bite_irq(int irq, void *dev_id)
-{
-	switch (irq) {
-
-	case Q6SW_WDOG_EXPIRED_IRQ:
-		pr_err("Watchdog bite received from modem software!\n");
-		restart_modem();
-		break;
-	case Q6FW_WDOG_EXPIRED_IRQ:
-		pr_err("Watchdog bite received from modem firmware!\n");
-		restart_modem();
-		break;
-	break;
-
-	default:
-		pr_err("%s: Unknown IRQ!\n", __func__);
-	}
-
-	return IRQ_HANDLED;
-}
-
-static struct subsys_desc modem_8960 = {
-	.name = "modem",
-	.shutdown = modem_shutdown,
-	.powerup = modem_powerup,
-	.ramdump = modem_ramdump,
-	.crash_shutdown = modem_crash_shutdown
-};
-
-static int modem_subsystem_restart_init(void)
-{
-	modem_8960_dev = subsys_register(&modem_8960);
-	if (IS_ERR(modem_8960_dev))
-		return PTR_ERR(modem_8960_dev);
-	return 0;
-}
-
-static int modem_debug_set(void *data, u64 val)
-{
-	if (val == 1)
-		subsystem_restart_dev(modem_8960_dev);
-
-	return 0;
-}
-
-static int modem_debug_get(void *data, u64 *val)
-{
-	*val = 0;
-	return 0;
-}
-
-DEFINE_SIMPLE_ATTRIBUTE(modem_debug_fops, modem_debug_get, modem_debug_set,
-				"%llu\n");
-
-static int modem_debugfs_init(void)
-{
-	struct dentry *dent;
-	dent = debugfs_create_dir("modem_debug", 0);
-
-	if (IS_ERR(dent))
-		return PTR_ERR(dent);
-
-	debugfs_create_file("reset_modem", 0644, dent, NULL,
-		&modem_debug_fops);
-	return 0;
-}
-
-static int __init modem_8960_init(void)
-{
-	int ret;
-
-	if (cpu_is_apq8064() || cpu_is_apq8064ab())
-		return -ENODEV;
-
-	ret = smsm_state_cb_register(SMSM_MODEM_STATE, SMSM_RESET,
-		smsm_state_cb, 0);
-
-	if (ret < 0)
-		pr_err("%s: Unable to register SMSM callback! (%d)\n",
-				__func__, ret);
-
-	ret = request_irq(Q6FW_WDOG_EXPIRED_IRQ, modem_wdog_bite_irq,
-			IRQF_TRIGGER_RISING, "modem_wdog_fw", NULL);
-
-	if (ret < 0) {
-		pr_err("%s: Unable to request q6fw watchdog IRQ. (%d)\n",
-				__func__, ret);
-		goto out;
-	}
-
-	ret = request_irq(Q6SW_WDOG_EXPIRED_IRQ, modem_wdog_bite_irq,
-			IRQF_TRIGGER_RISING, "modem_wdog_sw", NULL);
-
-	if (ret < 0) {
-		pr_err("%s: Unable to request q6sw watchdog IRQ. (%d)\n",
-				__func__, ret);
-		disable_irq_nosync(Q6FW_WDOG_EXPIRED_IRQ);
-		goto out;
-	}
-
-	ret = modem_subsystem_restart_init();
-
-	if (ret < 0) {
-		pr_err("%s: Unable to reg with subsystem restart. (%d)\n",
-				__func__, ret);
-		goto out;
-	}
-
-	modemfw_ramdump_dev = create_ramdump_device("modem_fw");
-
-	if (!modemfw_ramdump_dev) {
-		pr_err("%s: Unable to create modem fw ramdump device. (%d)\n",
-				__func__, -ENOMEM);
-		ret = -ENOMEM;
-		goto out;
-	}
-
-	modemsw_ramdump_dev = create_ramdump_device("modem_sw");
-
-	if (!modemsw_ramdump_dev) {
-		pr_err("%s: Unable to create modem sw ramdump device. (%d)\n",
-				__func__, -ENOMEM);
-		ret = -ENOMEM;
-		goto out;
-	}
-
-	smem_ramdump_dev = create_ramdump_device("smem-modem");
-
-	if (!smem_ramdump_dev) {
-		pr_err("%s: Unable to create smem ramdump device. (%d)\n",
-				__func__, -ENOMEM);
-		ret = -ENOMEM;
-		goto out;
-	}
-
-	ret = modem_debugfs_init();
-
-	pr_info("%s: modem fatal driver init'ed.\n", __func__);
-out:
-	return ret;
-}
-
-module_init(modem_8960_init);
diff --git a/arch/arm/mach-msm/modem-ssr-8974.c b/arch/arm/mach-msm/modem-ssr-8974.c
deleted file mode 100644
index 942eca5..0000000
--- a/arch/arm/mach-msm/modem-ssr-8974.c
+++ /dev/null
@@ -1,212 +0,0 @@
-/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- */
-
-#include <linux/kernel.h>
-#include <linux/interrupt.h>
-#include <linux/module.h>
-#include <linux/err.h>
-
-#include <mach/peripheral-loader.h>
-#include <mach/subsystem_restart.h>
-#include <mach/msm_smsm.h>
-
-#include "ramdump.h"
-
-static int crash_shutdown;
-static int modem_ssr_ignore_errors;
-static struct subsys_device *modem_ssr_dev;
-
-#define MAX_SSR_REASON_LEN 81U
-#define Q6SS_WDOG_ENABLE		0xFC802004
-#define MSS_Q6SS_WDOG_EXP_IRQ		56
-
-static void log_modem_sfr(void)
-{
-	u32 size;
-	char *smem_reason, reason[MAX_SSR_REASON_LEN];
-
-	smem_reason = smem_get_entry(SMEM_SSR_REASON_MSS0, &size);
-	if (!smem_reason || !size) {
-		pr_err("modem subsystem failure reason: (unknown, smem_get_entry failed).\n");
-		return;
-	}
-	if (!smem_reason[0]) {
-		pr_err("modem subsystem failure reason: (unknown, empty string found).\n");
-		return;
-	}
-
-	strlcpy(reason, smem_reason, min(size, sizeof(reason)));
-	pr_err("modem subsystem failure reason: %s.\n", reason);
-
-	smem_reason[0] = '\0';
-	wmb();
-}
-
-static void restart_modem(void)
-{
-	log_modem_sfr();
-	modem_ssr_ignore_errors = 1;
-	subsystem_restart("modem");
-}
-
-static void smsm_state_cb(void *data, uint32_t old_state, uint32_t new_state)
-{
-	/* Ignore if we're the one that set SMSM_RESET */
-	if (crash_shutdown)
-		return;
-
-	if (new_state & SMSM_RESET) {
-		pr_err("Probable fatal error on the modem.\n");
-		restart_modem();
-	}
-}
-
-static int modem_shutdown(const struct subsys_desc *subsys)
-{
-	pil_force_shutdown("modem");
-	pil_force_shutdown("mba");
-	return 0;
-}
-
-static int modem_powerup(const struct subsys_desc *subsys)
-{
-	/*
-	 * At this time, the modem is shutdown. Therefore this function cannot
-	 * run concurrently with either the watchdog bite error handler or the
-	 * SMSM callback, making it safe to unset the flag below.
-	 */
-	modem_ssr_ignore_errors = 0;
-	pil_force_boot("mba");
-	pil_force_boot("modem");
-	return 0;
-}
-
-void modem_crash_shutdown(const struct subsys_desc *subsys)
-{
-	crash_shutdown = 1;
-	smsm_reset_modem(SMSM_RESET);
-}
-
-static struct ramdump_segment modem_segments[] = {
-	{0x08400000, 0x0D100000 - 0x08400000},
-};
-
-static struct ramdump_segment smem_segments[] = {
-	{0x0FA00000, 0x0FC00000 - 0x0FA00000},
-};
-
-static void *modem_ramdump_dev;
-static void *smem_ramdump_dev;
-
-static int modem_ramdump(int enable, const struct subsys_desc *crashed_subsys)
-{
-	int ret = 0;
-
-	if (!enable)
-		return ret;
-
-	pil_force_boot("mba");
-
-	ret = do_ramdump(modem_ramdump_dev, modem_segments,
-				ARRAY_SIZE(modem_segments));
-
-	if (ret < 0) {
-		pr_err("Unable to dump modem fw memory (rc = %d).\n",
-			ret);
-		goto out;
-	}
-
-	ret = do_ramdump(smem_ramdump_dev, smem_segments,
-		ARRAY_SIZE(smem_segments));
-
-	if (ret < 0) {
-		pr_err("Unable to dump smem memory (rc = %d).\n", ret);
-		goto out;
-	}
-
-out:
-	pil_force_shutdown("mba");
-	return ret;
-}
-
-static irqreturn_t modem_wdog_bite_irq(int irq, void *dev_id)
-{
-	if (modem_ssr_ignore_errors)
-		return IRQ_HANDLED;
-	pr_err("Watchdog bite received from the modem!\n");
-	restart_modem();
-	return IRQ_HANDLED;
-}
-
-static struct subsys_desc modem_8974 = {
-	.name = "modem",
-	.shutdown = modem_shutdown,
-	.powerup = modem_powerup,
-	.ramdump = modem_ramdump,
-	.crash_shutdown = modem_crash_shutdown
-};
-
-static int __init modem_8974_init(void)
-{
-	int ret;
-
-	ret = smsm_state_cb_register(SMSM_MODEM_STATE, SMSM_RESET,
-		smsm_state_cb, 0);
-
-	if (ret < 0) {
-		pr_err("%s: Unable to register SMSM callback! (%d)\n",
-				__func__, ret);
-		goto out;
-	}
-
-	ret = request_irq(MSS_Q6SS_WDOG_EXP_IRQ, modem_wdog_bite_irq,
-			IRQF_TRIGGER_RISING, "modem_wdog_sw", NULL);
-
-	if (ret < 0) {
-		pr_err("%s: Unable to request q6sw watchdog IRQ. (%d)\n",
-				__func__, ret);
-		goto out;
-	}
-
-	modem_ssr_dev = subsys_register(&modem_8974);
-
-	if (IS_ERR_OR_NULL(modem_ssr_dev)) {
-		pr_err("%s: Unable to reg with subsystem restart. (%ld)\n",
-				__func__, PTR_ERR(modem_ssr_dev));
-		ret = PTR_ERR(modem_ssr_dev);
-		goto out;
-	}
-
-	modem_ramdump_dev = create_ramdump_device("modem");
-
-	if (!modem_ramdump_dev) {
-		pr_err("%s: Unable to create a modem ramdump device.\n",
-			__func__);
-		ret = -ENOMEM;
-		goto out;
-	}
-
-	smem_ramdump_dev = create_ramdump_device("smem-modem");
-
-	if (!smem_ramdump_dev) {
-		pr_err("%s: Unable to create an smem ramdump device.\n",
-			__func__);
-		ret = -ENOMEM;
-		goto out;
-	}
-
-	pr_info("%s: modem subsystem restart driver init'ed.\n", __func__);
-out:
-	return ret;
-}
-
-module_init(modem_8974_init);
diff --git a/arch/arm/mach-msm/mpm-8625.c b/arch/arm/mach-msm/mpm-8625.c
index fe7ffff..c70ff5c 100644
--- a/arch/arm/mach-msm/mpm-8625.c
+++ b/arch/arm/mach-msm/mpm-8625.c
@@ -92,6 +92,7 @@
 	[MSM8625_INT_GPIO_GROUP2]	= SMSM_FAKE_IRQ,
 	[MSM8625_INT_A9_M2A_0]		= SMSM_FAKE_IRQ,
 	[MSM8625_INT_A9_M2A_1]		= SMSM_FAKE_IRQ,
+	[MSM8625_INT_A9_M2A_2]          = SMSM_FAKE_IRQ,
 	[MSM8625_INT_A9_M2A_5]		= SMSM_FAKE_IRQ,
 	[MSM8625_INT_GP_TIMER_EXP]	= SMSM_FAKE_IRQ,
 	[MSM8625_INT_DEBUG_TIMER_EXP]	= SMSM_FAKE_IRQ,
diff --git a/arch/arm/mach-msm/msm-krait-l2-accessors.c b/arch/arm/mach-msm/msm-krait-l2-accessors.c
index 3da155a..2c66ea0 100644
--- a/arch/arm/mach-msm/msm-krait-l2-accessors.c
+++ b/arch/arm/mach-msm/msm-krait-l2-accessors.c
@@ -18,75 +18,12 @@
 
 DEFINE_RAW_SPINLOCK(l2_access_lock);
 
-#define L2CPMR		0x500
-#define L2CPUCPMR	0x501
-#define L2CPUVRF8	0x708
-#define CPUNDX_MASK	(0x7 << 12)
-
-/*
- * For Krait versions found in APQ8064v1.x, save L2CPUVRF8 before
- * L2CPMR or L2CPUCPMR writes and restore it after to work around an
- * issue where L2CPUVRF8 becomes corrupt.
- */
-static bool l2cpuvrf8_needs_fix(u32 reg_addr)
-{
-	switch (read_cpuid_id()) {
-	case 0x510F06F0: /* KR28M4A10  */
-	case 0x510F06F1: /* KR28M4A10B */
-	case 0x510F06F2: /* KR28M4A11  */
-		break;
-	default:
-		return false;
-	};
-
-	switch (reg_addr & ~CPUNDX_MASK) {
-	case L2CPMR:
-	case L2CPUCPMR:
-		return true;
-	default:
-		return false;
-	}
-}
-
-static u32 l2cpuvrf8_fix_save(u32 reg_addr, u32 *l2cpuvrf8_val)
-{
-	u32 l2cpuvrf8_addr = L2CPUVRF8 | (reg_addr & CPUNDX_MASK);
-
-	mb();
-	asm volatile ("mcr     p15, 3, %[l2cpselr], c15, c0, 6\n\t"
-		      "isb\n\t"
-		      "mrc     p15, 3, %[l2cpdr],   c15, c0, 7\n\t"
-			: [l2cpdr]"=r" (*l2cpuvrf8_val)
-			: [l2cpselr]"r" (l2cpuvrf8_addr)
-	);
-
-	return l2cpuvrf8_addr;
-}
-
-static void l2cpuvrf8_fix_restore(u32 l2cpuvrf8_addr, u32 l2cpuvrf8_val)
-{
-	mb();
-	asm volatile ("mcr     p15, 3, %[l2cpselr], c15, c0, 6\n\t"
-		      "isb\n\t"
-		      "mcr     p15, 3, %[l2cpdr],   c15, c0, 7\n\t"
-		      "isb\n\t"
-			:
-			: [l2cpselr]"r" (l2cpuvrf8_addr),
-			  [l2cpdr]"r" (l2cpuvrf8_val)
-	);
-}
-
 u32 set_get_l2_indirect_reg(u32 reg_addr, u32 val)
 {
 	unsigned long flags;
-	u32 uninitialized_var(l2cpuvrf8_val), l2cpuvrf8_addr = 0;
 	u32 ret_val;
 
 	raw_spin_lock_irqsave(&l2_access_lock, flags);
-
-	if (l2cpuvrf8_needs_fix(reg_addr))
-		l2cpuvrf8_addr = l2cpuvrf8_fix_save(reg_addr, &l2cpuvrf8_val);
-
 	mb();
 	asm volatile ("mcr     p15, 3, %[l2cpselr], c15, c0, 6\n\t"
 		      "isb\n\t"
@@ -96,10 +33,6 @@
 			: [l2cpdr_read]"=r" (ret_val)
 			: [l2cpselr]"r" (reg_addr), [l2cpdr]"r" (val)
 	);
-
-	if (l2cpuvrf8_addr)
-		l2cpuvrf8_fix_restore(l2cpuvrf8_addr, l2cpuvrf8_val);
-
 	raw_spin_unlock_irqrestore(&l2_access_lock, flags);
 
 	return ret_val;
@@ -109,13 +42,8 @@
 void set_l2_indirect_reg(u32 reg_addr, u32 val)
 {
 	unsigned long flags;
-	u32 uninitialized_var(l2cpuvrf8_val), l2cpuvrf8_addr = 0;
 
 	raw_spin_lock_irqsave(&l2_access_lock, flags);
-
-	if (l2cpuvrf8_needs_fix(reg_addr))
-		l2cpuvrf8_addr = l2cpuvrf8_fix_save(reg_addr, &l2cpuvrf8_val);
-
 	mb();
 	asm volatile ("mcr     p15, 3, %[l2cpselr], c15, c0, 6\n\t"
 		      "isb\n\t"
@@ -124,10 +52,6 @@
 			:
 			: [l2cpselr]"r" (reg_addr), [l2cpdr]"r" (val)
 	);
-
-	if (l2cpuvrf8_addr)
-		l2cpuvrf8_fix_restore(l2cpuvrf8_addr, l2cpuvrf8_val);
-
 	raw_spin_unlock_irqrestore(&l2_access_lock, flags);
 }
 EXPORT_SYMBOL(set_l2_indirect_reg);
diff --git a/arch/arm/mach-msm/msm7k_fiq.c b/arch/arm/mach-msm/msm7k_fiq.c
new file mode 100644
index 0000000..421b4f9
--- /dev/null
+++ b/arch/arm/mach-msm/msm7k_fiq.c
@@ -0,0 +1,86 @@
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/irq.h>
+#include <asm/fiq.h>
+#include <asm/hardware/gic.h>
+#include <asm/cacheflush.h>
+#include <mach/irqs-8625.h>
+#include <mach/socinfo.h>
+
+#include "msm_watchdog.h"
+
+#define MODULE_NAME "MSM7K_FIQ"
+
+struct msm_watchdog_dump msm_dump_cpu_ctx;
+static int fiq_counter;
+void *msm7k_fiq_stack;
+
+/* Called from the FIQ asm handler */
+void msm7k_fiq_handler(void)
+{
+	struct irq_data *d;
+	struct irq_chip *c;
+
+	pr_info("Fiq is received %s\n", __func__);
+	fiq_counter++;
+	d = irq_get_irq_data(MSM8625_INT_A9_M2A_2);
+	c = irq_data_get_irq_chip(d);
+	c->irq_mask(d);
+	local_irq_disable();
+
+	/* Clear the IRQ from the ENABLE_SET */
+	gic_clear_irq_pending(MSM8625_INT_A9_M2A_2);
+	local_irq_enable();
+	flush_cache_all();
+	outer_flush_all();
+	return;
+}
+
+struct fiq_handler msm7k_fh = {
+	.name = MODULE_NAME,
+};
+
+static int __init msm_setup_fiq_handler(void)
+{
+	int ret = 0;
+
+	claim_fiq(&msm7k_fh);
+	set_fiq_handler(&msm7k_fiq_start, msm7k_fiq_length);
+	msm7k_fiq_stack = (void *)__get_free_pages(GFP_KERNEL,
+				THREAD_SIZE_ORDER);
+	if (msm7k_fiq_stack == NULL) {
+		pr_err("FIQ STACK SETUP IS NOT SUCCESSFUL\n");
+		return -ENOMEM;
+	}
+
+	fiq_set_type(MSM8625_INT_A9_M2A_2, IRQF_TRIGGER_RISING);
+	gic_set_irq_secure(MSM8625_INT_A9_M2A_2);
+	enable_irq(MSM8625_INT_A9_M2A_2);
+	pr_info("%s : msm7k fiq setup--done\n", __func__);
+	return ret;
+}
+
+static int __init init7k_fiq(void)
+{
+	if (!cpu_is_msm8625())
+		return 0;
+
+	if (msm_setup_fiq_handler())
+		pr_err("MSM7K FIQ INIT FAILED\n");
+
+	return 0;
+}
+late_initcall(init7k_fiq);
diff --git a/arch/arm/mach-msm/msm7k_fiq_handler.S b/arch/arm/mach-msm/msm7k_fiq_handler.S
new file mode 100644
index 0000000..e2da9cf
--- /dev/null
+++ b/arch/arm/mach-msm/msm7k_fiq_handler.S
@@ -0,0 +1,94 @@
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/linkage.h>
+#include <asm/assembler.h>
+
+#define VERSION_ID 0x1
+#define MAGIC 0xDEAD0000 | VERSION_ID
+	.text
+	.align 3
+
+ENTRY(msm7k_fiq_start)
+	sub    r14, r14, #4 @return address
+	ldr    r8, Lmsm_fiq_stack
+	ldr    sp, [r8]       @get stack
+	stmfa  sp!, {r0-r7, lr}
+	stmfa  sp!, {r8-r9}
+	ldr    r8, Ldump_cpu_ctx
+	@ store magic to indicate a valid dump
+	ldr    r9, Lmagic
+	str    r9, [r8], #4
+	@ get the current cpsr
+	mrs    r9, cpsr
+	str    r9, [r8],#4
+	stmia  r8!, {r0-r7}   @ get the USR r0-r7
+	mov    r4, r8
+	mov    r5, #PSR_I_BIT | PSR_F_BIT | SYSTEM_MODE
+	msr    cpsr_c, r5     @ select SYSTEM mode
+	stmia  r4!, {r8-r14}
+	mov    r5, #PSR_I_BIT | PSR_F_BIT | IRQ_MODE
+	msr    cpsr_c, r5     @ select IRQ mode
+	mrs    r5, spsr
+	str    r5, [r4], #4
+	stmia  r4!, {r13-r14}
+	mov    r5, #PSR_I_BIT | PSR_F_BIT | SVC_MODE
+	msr    cpsr_c, r5     @ select SVC mode
+	mrs    r5, spsr
+	str    r5, [r4], #4
+	stmia  r4!, {r13-r14}
+	mov    r0, r13
+	mov    r1, r14
+	mov    r5, #PSR_I_BIT | PSR_F_BIT | FIQ_MODE
+	msr    cpsr_c, r5     @ select FIQ mode
+	stmfa  sp!, {r0-r1}
+	mov    r5, #PSR_I_BIT | PSR_F_BIT | ABT_MODE
+	msr    cpsr_c, r5     @ select ABT mode
+	mrs    r5, spsr
+	str    r5, [r4], #4
+	stmia  r4!, {r13-r14}
+	mov    r5, #PSR_I_BIT | PSR_F_BIT | UND_MODE
+	msr    cpsr_c, r5     @ select UND mode
+	mrs    r5, spsr
+	str    r5, [r4], #4
+	stmia  r4!, {r13-r14}
+	mov    r5, #PSR_I_BIT | PSR_F_BIT | FIQ_MODE
+	msr    cpsr_c, r5     @ select FIQ mode
+	mrs    r5, spsr
+	str    r5, [r4], #4
+	stmia  r4!, {r8-r14}
+	dsb
+	mov    r5, #PSR_I_BIT | PSR_F_BIT | SVC_MODE
+	msr    cpsr_c, r5     @ select SVC mode
+	ldr    r2, Lmsm_fiq_handler
+	blx    r2
+	mov    r5, #PSR_I_BIT | PSR_F_BIT | FIQ_MODE
+	msr    cpsr_c, r5     @ select FIQ mode
+	ldmfa sp!, {r0, r1}
+	mov    r5, #PSR_I_BIT | PSR_F_BIT | SVC_MODE
+	msr    cpsr_c, r5     @ select SVC mode
+	mov    r13, r0
+	mov    r14, r1
+	mov    r5, #PSR_I_BIT | PSR_F_BIT | FIQ_MODE
+	msr    cpsr_c, r5     @ select SVC mode
+	ldmfa sp!, {r8-r9}
+	ldmfa sp!, {r0-r7, pc}^
+Ldump_cpu_ctx:
+	.word  msm_dump_cpu_ctx
+Lmsm_fiq_stack:
+	.word  msm7k_fiq_stack
+Lmagic:
+	.word  MAGIC
+Lmsm_fiq_handler:
+	.word  msm7k_fiq_handler
+ENTRY(msm7k_fiq_length)
+	.word  . - msm7k_fiq_start
diff --git a/arch/arm/mach-msm/msm_bus/msm_bus_arb.c b/arch/arm/mach-msm/msm_bus/msm_bus_arb.c
index 07082b7..65539c6 100644
--- a/arch/arm/mach-msm/msm_bus/msm_bus_arb.c
+++ b/arch/arm/mach-msm/msm_bus/msm_bus_arb.c
@@ -32,8 +32,9 @@
 #define SEL_FAB_CLK 1
 #define SEL_SLAVE_CLK 0
 
-#define BW_TO_CLK_FREQ_HZ(width, bw) ((unsigned long)\
-	DIV_ROUND_UP((bw), (width)))
+#define BW_TO_CLK_FREQ_HZ(width, bw) \
+	msm_bus_div64(width, bw)
+
 #define IS_MASTER_VALID(mas) \
 	(((mas >= MSM_BUS_MASTER_FIRST) && (mas <= MSM_BUS_MASTER_LAST)) \
 	 ? 1 : 0)
@@ -43,6 +44,33 @@
 
 static DEFINE_MUTEX(msm_bus_lock);
 
+/* This function uses shift operations to divide 64 bit value for higher
+ * efficiency. The divisor expected are number of ports or bus-width.
+ * These are expected to be 1, 2, 4, 8, 16 and 32 in most cases.
+ *
+ * To account for exception to the above divisor values, the standard
+ * do_div function is used.
+ * */
+uint64_t msm_bus_div64(unsigned int w, uint64_t bw)
+{
+	uint64_t *b = &bw;
+
+	if ((bw > 0) && (bw < w))
+		return 1;
+
+	switch (w) {
+	case 1: return bw;
+	case 2:	return (bw >> 1);
+	case 4:	return (bw >> 2);
+	case 8:	return (bw >> 3);
+	case 16: return (bw >> 4);
+	case 32: return (bw >> 5);
+	}
+
+	do_div(*b, w);
+	return *b;
+}
+
 /**
  * add_path_node: Adds the path information to the current node
  * @info: Internal node info structure
@@ -278,21 +306,21 @@
  * frequencies is calculated at each node on the path. Commit data to be sent
  * to RPM for each master and slave is also calculated here.
  */
-static int update_path(int curr, int pnode, unsigned long req_clk, unsigned
-	long req_bw, unsigned long curr_clk, unsigned long curr_bw,
-	unsigned int ctx, unsigned int cl_active_flag)
+static int update_path(int curr, int pnode, uint64_t req_clk, uint64_t req_bw,
+	uint64_t curr_clk, uint64_t curr_bw, unsigned int ctx, unsigned int
+	cl_active_flag)
 {
 	int index, ret = 0;
 	struct msm_bus_inode_info *info;
 	int next_pnode;
-	long int add_bw = req_bw - curr_bw;
+	int64_t add_bw = req_bw - curr_bw;
 	unsigned bwsum = 0;
-	unsigned req_clk_hz, curr_clk_hz, bwsum_hz;
+	uint64_t req_clk_hz, curr_clk_hz, bwsum_hz;
 	int *master_tiers;
 	struct msm_bus_fabric_device *fabdev = msm_bus_get_fabric_device
 		(GET_FABID(curr));
 
-	MSM_BUS_DBG("args: %d %d %d %lu %lu %lu %lu %u\n",
+	MSM_BUS_DBG("args: %d %d %d %llu %llu %llu %llu %u\n",
 		curr, GET_NODE(pnode), GET_INDEX(pnode), req_clk, req_bw,
 		curr_clk, curr_bw, ctx);
 	index = GET_INDEX(pnode);
@@ -378,8 +406,8 @@
 			req_clk);
 		bwsum_hz = BW_TO_CLK_FREQ_HZ(hop->node_info->buswidth,
 			bwsum);
-		MSM_BUS_DBG("Calling update-clks: curr_hz: %lu, req_hz: %lu,"
-			" bw_hz %u\n", curr_clk, req_clk, bwsum_hz);
+		MSM_BUS_DBG("up-clk: curr_hz: %llu, req_hz: %llu, bw_hz %llu\n",
+			curr_clk, req_clk, bwsum_hz);
 		ret = fabdev->algo->update_clks(fabdev, hop, index,
 			curr_clk_hz, req_clk_hz, bwsum_hz, SEL_FAB_CLK,
 			ctx, cl_active_flag);
@@ -532,7 +560,7 @@
 	int i, ret = 0;
 	struct msm_bus_scale_pdata *pdata;
 	int pnode, src, curr, ctx;
-	unsigned long req_clk, req_bw, curr_clk, curr_bw;
+	uint64_t req_clk, req_bw, curr_clk, curr_bw;
 	struct msm_bus_client *client = (struct msm_bus_client *)cl;
 	if (IS_ERR(client)) {
 		MSM_BUS_ERR("msm_bus_scale_client update req error %d\n",
@@ -554,9 +582,8 @@
 		goto err;
 	}
 
-	MSM_BUS_DBG("cl: %u index: %d curr: %d"
-			" num_paths: %d\n", cl, index, client->curr,
-			client->pdata->usecase->num_paths);
+	MSM_BUS_DBG("cl: %u index: %d curr: %d num_paths: %d\n",
+		cl, index, client->curr, client->pdata->usecase->num_paths);
 
 	for (i = 0; i < pdata->usecase->num_paths; i++) {
 		src = msm_bus_board_get_iid(client->pdata->usecase[index].
@@ -584,7 +611,7 @@
 		} else {
 			curr_clk = client->pdata->usecase[curr].vectors[i].ib;
 			curr_bw = client->pdata->usecase[curr].vectors[i].ab;
-			MSM_BUS_DBG("ab: %lu ib: %lu\n", curr_bw, curr_clk);
+			MSM_BUS_DBG("ab: %llu ib: %llu\n", curr_bw, curr_clk);
 		}
 
 		if (!pdata->active_only) {
diff --git a/arch/arm/mach-msm/msm_bus/msm_bus_bimc.c b/arch/arm/mach-msm/msm_bus/msm_bus_bimc.c
index 2072cb1..e0ab983 100644
--- a/arch/arm/mach-msm/msm_bus/msm_bus_bimc.c
+++ b/arch/arm/mach-msm/msm_bus/msm_bus_bimc.c
@@ -1803,7 +1803,7 @@
 	struct msm_bus_inode_info *info,
 	struct msm_bus_fabric_registration *fab_pdata,
 	void *sel_cdata, int *master_tiers,
-	long int add_bw)
+	int64_t add_bw)
 {
 	struct msm_bus_bimc_info *binfo;
 	struct msm_bus_bimc_qos_bw qbw;
@@ -1813,7 +1813,7 @@
 	struct msm_bus_bimc_commit *sel_cd =
 		(struct msm_bus_bimc_commit *)sel_cdata;
 
-	MSM_BUS_DBG("BIMC: Update bw for ID %d, with IID: %d: %ld\n",
+	MSM_BUS_DBG("BIMC: Update bw for ID %d, with IID: %d: %lld\n",
 		info->node_info->id, info->node_info->priv_id, add_bw);
 
 	binfo = (struct msm_bus_bimc_info *)fab_pdata->hw_data;
diff --git a/arch/arm/mach-msm/msm_bus/msm_bus_board_8974.c b/arch/arm/mach-msm/msm_bus/msm_bus_board_8974.c
index 1b8c07e..f0f5cd8 100644
--- a/arch/arm/mach-msm/msm_bus/msm_bus_board_8974.c
+++ b/arch/arm/mach-msm/msm_bus/msm_bus_board_8974.c
@@ -641,6 +641,7 @@
 		.mode = NOC_QOS_MODE_FIXED,
 		.qport = qports_crypto_c0,
 		.mas_hw_id = MAS_CRYPTO_CORE0,
+		.hw_sel = MSM_BUS_NOC,
 	},
 	{
 		.id = MSM_BUS_MASTER_CRYPTO_CORE1,
@@ -651,6 +652,7 @@
 		.mode = NOC_QOS_MODE_FIXED,
 		.qport = qports_crypto_c1,
 		.mas_hw_id = MAS_CRYPTO_CORE1,
+		.hw_sel = MSM_BUS_NOC,
 	},
 	{
 		.id = MSM_BUS_MASTER_LPASS_PROC,
@@ -719,6 +721,7 @@
 		.mas_hw_id = MAS_USB3,
 		.prio_rd = 2,
 		.prio_wr = 2,
+		.hw_sel = MSM_BUS_NOC,
 	},
 	{
 		.id = MSM_BUS_SLAVE_AMPSS,
diff --git a/arch/arm/mach-msm/msm_bus/msm_bus_core.h b/arch/arm/mach-msm/msm_bus/msm_bus_core.h
index 333fe4b..12d6862 100644
--- a/arch/arm/mach-msm/msm_bus/msm_bus_core.h
+++ b/arch/arm/mach-msm/msm_bus/msm_bus_core.h
@@ -36,7 +36,7 @@
 	(((slv >= MSM_BUS_SLAVE_FIRST) && (slv <= MSM_BUS_SLAVE_LAST)) ? 1 : 0)
 
 #define INTERLEAVED_BW(fab_pdata, bw, ports) \
-	((fab_pdata->il_flag) ? DIV_ROUND_UP((bw), (ports)) : (bw))
+	((fab_pdata->il_flag) ? msm_bus_div64((bw), (ports)) : (bw))
 #define INTERLEAVED_VAL(fab_pdata, n) \
 	((fab_pdata->il_flag) ? (n) : 1)
 
@@ -83,34 +83,34 @@
 };
 
 struct path_node {
-	unsigned long clk[NUM_CTX];
-	unsigned long bw[NUM_CTX];
-	unsigned long *sel_clk;
-	unsigned long *sel_bw;
+	uint64_t clk[NUM_CTX];
+	uint64_t bw[NUM_CTX];
+	uint64_t *sel_clk;
+	uint64_t *sel_bw;
 	int next;
 };
 
 struct msm_bus_link_info {
-	unsigned long clk[NUM_CTX];
-	unsigned long *sel_clk;
-	unsigned long memclk;
-	long bw[NUM_CTX];
-	long *sel_bw;
+	uint64_t clk[NUM_CTX];
+	uint64_t *sel_clk;
+	uint64_t memclk;
+	int64_t bw[NUM_CTX];
+	int64_t *sel_bw;
 	int *tier;
 	int num_tiers;
 };
 
 struct nodeclk {
 	struct clk *clk;
-	unsigned long rate;
+	uint64_t rate;
 	bool dirty;
 	bool enable;
 };
 
 struct msm_bus_inode_info {
 	struct msm_bus_node_info *node_info;
-	unsigned long max_bw;
-	unsigned long max_clk;
+	uint64_t max_bw;
+	uint64_t max_clk;
 	struct msm_bus_link_info link_info;
 	int num_pnodes;
 	struct path_node *pnode;
@@ -137,7 +137,7 @@
 		struct msm_bus_inode_info *info,
 		struct msm_bus_fabric_registration *fab_pdata,
 		void *sel_cdata, int *master_tiers,
-		long int add_bw);
+		int64_t add_bw);
 	void (*fill_cdata_buffer)(int *curr, char *buf, const int max_size,
 		void *cdata, int nmasters, int nslaves, int ntslaves);
 	int (*commit)(struct msm_bus_fabric_registration
@@ -162,8 +162,8 @@
 struct msm_bus_fab_algorithm {
 	int (*update_clks)(struct msm_bus_fabric_device *fabdev,
 		struct msm_bus_inode_info *pme, int index,
-		unsigned long curr_clk, unsigned long req_clk,
-		unsigned long bwsum, int flag, int ctx,
+		uint64_t curr_clk, uint64_t req_clk,
+		uint64_t bwsum, int flag, int ctx,
 		unsigned int cl_active_flag);
 	int (*port_halt)(struct msm_bus_fabric_device *fabdev, int portid);
 	int (*port_unhalt)(struct msm_bus_fabric_device *fabdev, int portid);
@@ -175,7 +175,7 @@
 	struct list_head *(*get_gw_list)(struct msm_bus_fabric_device *fabdev);
 	void (*update_bw)(struct msm_bus_fabric_device *fabdev, struct
 		msm_bus_inode_info * hop, struct msm_bus_inode_info *info,
-		long int add_bw, int *master_tiers, int ctx);
+		int64_t add_bw, int *master_tiers, int ctx);
 };
 
 struct msm_bus_board_algorithm {
@@ -202,6 +202,7 @@
 	int curr;
 };
 
+uint64_t msm_bus_div64(unsigned int width, uint64_t bw);
 int msm_bus_remote_hw_commit(struct msm_bus_fabric_registration
 	*fab_pdata, void *hw_data, void **cdata);
 int msm_bus_fabric_device_register(struct msm_bus_fabric_device *fabric);
diff --git a/arch/arm/mach-msm/msm_bus/msm_bus_dbg.c b/arch/arm/mach-msm/msm_bus/msm_bus_dbg.c
index 76f85c6..a44c53a 100644
--- a/arch/arm/mach-msm/msm_bus/msm_bus_dbg.c
+++ b/arch/arm/mach-msm/msm_bus/msm_bus_dbg.c
@@ -385,11 +385,11 @@
 			pdata->usecase[index].vectors[j].dst);
 	i += scnprintf(buf + i, MAX_BUFF_SIZE - i, "\nab     : ");
 	for (j = 0; j < pdata->usecase->num_paths; j++)
-		i += scnprintf(buf + i, MAX_BUFF_SIZE - i, "%u  ",
+		i += scnprintf(buf + i, MAX_BUFF_SIZE - i, "%llu  ",
 			pdata->usecase[index].vectors[j].ab);
 	i += scnprintf(buf + i, MAX_BUFF_SIZE - i, "\nib     : ");
 	for (j = 0; j < pdata->usecase->num_paths; j++)
-		i += scnprintf(buf + i, MAX_BUFF_SIZE - i, "%u  ",
+		i += scnprintf(buf + i, MAX_BUFF_SIZE - i, "%llu  ",
 			pdata->usecase[index].vectors[j].ib);
 	i += scnprintf(buf + i, MAX_BUFF_SIZE - i, "\n");
 
diff --git a/arch/arm/mach-msm/msm_bus/msm_bus_fabric.c b/arch/arm/mach-msm/msm_bus/msm_bus_fabric.c
index be3e06f..7169440 100644
--- a/arch/arm/mach-msm/msm_bus/msm_bus_fabric.c
+++ b/arch/arm/mach-msm/msm_bus/msm_bus_fabric.c
@@ -227,13 +227,13 @@
  */
 static int msm_bus_fabric_update_clks(struct msm_bus_fabric_device *fabdev,
 		struct msm_bus_inode_info *slave, int index,
-		unsigned long curr_clk_hz, unsigned long req_clk_hz,
-		unsigned long bwsum_hz, int clk_flag, int ctx,
+		uint64_t curr_clk_hz, uint64_t req_clk_hz,
+		uint64_t bwsum_hz, int clk_flag, int ctx,
 		unsigned int cl_active_flag)
 {
 	int i, status = 0;
-	unsigned long max_pclk = 0, rate;
-	unsigned long *pclk = NULL;
+	uint64_t max_pclk = 0, rate;
+	uint64_t *pclk = NULL;
 	struct msm_bus_fabric *fabric = to_msm_bus_fabric(fabdev);
 	struct nodeclk *nodeclk;
 
@@ -266,7 +266,7 @@
 			info->link_info.sel_clk = &info->link_info.clk[ctx];
 			max_pclk = max(max_pclk, *info->link_info.sel_clk);
 		}
-		MSM_BUS_DBG("max_pclk from gateways: %lu\n", max_pclk);
+		MSM_BUS_DBG("max_pclk from gateways: %llu\n", max_pclk);
 
 		/* Maximum of all slave clocks. */
 
@@ -283,7 +283,7 @@
 		}
 
 
-		MSM_BUS_DBG("max_pclk from slaves & gws: %lu\n", max_pclk);
+		MSM_BUS_DBG("max_pclk from slaves & gws: %llu\n", max_pclk);
 		fabric->info.link_info.sel_clk =
 			&fabric->info.link_info.clk[ctx];
 		pclk = fabric->info.link_info.sel_clk;
@@ -301,7 +301,7 @@
 	if (clk_flag) {
 		nodeclk = &fabric->info.nodeclk[ctx];
 		if (nodeclk->clk) {
-			MSM_BUS_DBG("clks: id: %d set-clk: %lu bwsum_hz:%lu\n",
+			MSM_BUS_DBG("clks: id: %d set-clk: %llu bws_hz:%llu\n",
 			fabric->fabdev.id, *pclk, bwsum_hz);
 			if (nodeclk->rate != *pclk) {
 				nodeclk->dirty = true;
@@ -313,8 +313,8 @@
 		nodeclk = &slave->nodeclk[ctx];
 		if (nodeclk->clk) {
 			rate = *pclk;
-			MSM_BUS_DBG("AXI_clks: id: %d set-clk: %lu "
-			"bwsum_hz: %lu\n" , slave->node_info->priv_id, rate,
+			MSM_BUS_DBG("clks: id: %d set-clk: %llu bws_hz: %llu\n",
+				slave->node_info->priv_id, rate,
 			bwsum_hz);
 			if (nodeclk->rate != rate) {
 				nodeclk->dirty = true;
@@ -337,7 +337,7 @@
 
 void msm_bus_fabric_update_bw(struct msm_bus_fabric_device *fabdev,
 	struct msm_bus_inode_info *hop, struct msm_bus_inode_info *info,
-	long int add_bw, int *master_tiers, int ctx)
+	int64_t add_bw, int *master_tiers, int ctx)
 {
 	struct msm_bus_fabric *fabric = to_msm_bus_fabric(fabdev);
 	void *sel_cdata;
diff --git a/arch/arm/mach-msm/msm_bus/msm_bus_noc.c b/arch/arm/mach-msm/msm_bus/msm_bus_noc.c
index 049e8c7..fb2e5da 100644
--- a/arch/arm/mach-msm/msm_bus/msm_bus_noc.c
+++ b/arch/arm/mach-msm/msm_bus/msm_bus_noc.c
@@ -504,7 +504,7 @@
 	struct msm_bus_inode_info *info,
 	struct msm_bus_fabric_registration *fab_pdata,
 	void *sel_cdata, int *master_tiers,
-	long int add_bw)
+	int64_t add_bw)
 {
 	struct msm_bus_noc_info *ninfo;
 	struct msm_bus_noc_qos_bw qos_bw;
@@ -527,7 +527,7 @@
 	ports = info->node_info->num_mports;
 	bw = INTERLEAVED_BW(fab_pdata, add_bw, ports);
 
-	MSM_BUS_DBG("NOC: Update bw for: %d: %ld\n",
+	MSM_BUS_DBG("NOC: Update bw for: %d: %lld\n",
 		info->node_info->priv_id, add_bw);
 	for (i = 0; i < ports; i++) {
 		sel_cd->mas[info->node_info->masterp[i]].bw += bw;
diff --git a/arch/arm/mach-msm/msm_bus/msm_bus_of.c b/arch/arm/mach-msm/msm_bus/msm_bus_of.c
index 24b0ce2..8ae1b46 100644
--- a/arch/arm/mach-msm/msm_bus/msm_bus_of.c
+++ b/arch/arm/mach-msm/msm_bus/msm_bus_of.c
@@ -19,6 +19,7 @@
 #include <linux/platform_device.h>
 #include <mach/msm_bus.h>
 
+#define KBTOMB(a) (a * 1000ULL)
 /**
  * msm_bus_cl_get_pdata() - Generate bus client data from device tree
  * provided by clients.
@@ -53,22 +54,22 @@
 		goto err;
 	}
 
-	ret = of_property_read_string(of_node, "qcom,msm_bus,name",
+	ret = of_property_read_string(of_node, "qcom,msm-bus,name",
 		&pdata->name);
 	if (ret) {
 		pr_err("Error: Client name not found\n");
 		goto err;
 	}
 
-	ret = of_property_read_u32(of_node, "qcom,msm_bus,num_cases",
+	ret = of_property_read_u32(of_node, "qcom,msm-bus,num-cases",
 		&num_usecases);
 	if (ret) {
-		pr_err("Error: num_usecases not found\n");
+		pr_err("Error: num-usecases not found\n");
 		goto err;
 	}
 
 	pdata->num_usecases = num_usecases;
-	ret = of_property_read_u32(of_node, "qcom,msm_bus,active_only",
+	ret = of_property_read_u32(of_node, "qcom,msm-bus,active-only",
 		&pdata->active_only);
 	if (ret) {
 		pr_info("active_only flag absent.\n");
@@ -83,15 +84,15 @@
 		goto err;
 	}
 
-	ret = of_property_read_u32(of_node, "qcom,msm_bus,num_paths",
+	ret = of_property_read_u32(of_node, "qcom,msm-bus,num-paths",
 		&num_paths);
 	if (ret) {
 		pr_err("Error: num_paths not found\n");
 		goto err;
 	}
 
-	vec_arr = of_get_property(of_node, "qcom,msm_bus,vectors", &len);
-	if (len != num_usecases * num_paths * sizeof(struct msm_bus_vectors)) {
+	vec_arr = of_get_property(of_node, "qcom,msm-bus,vectors-KBps", &len);
+	if (len != num_usecases * num_paths * sizeof(uint32_t) * 4) {
 		pr_err("Error: Length-error on getting vectors\n");
 		goto err;
 	}
@@ -111,10 +112,10 @@
 			usecase[i].vectors[j].src = be32_to_cpu(vec_arr[index]);
 			usecase[i].vectors[j].dst =
 				be32_to_cpu(vec_arr[index + 1]);
-			usecase[i].vectors[j].ab =
-				be32_to_cpu(vec_arr[index + 2]);
-			usecase[i].vectors[j].ib =
-				be32_to_cpu(vec_arr[index + 3]);
+			usecase[i].vectors[j].ab = (uint64_t)
+				KBTOMB(be32_to_cpu(vec_arr[index + 2]));
+			usecase[i].vectors[j].ib = (uint64_t)
+				KBTOMB(be32_to_cpu(vec_arr[index + 3]));
 		}
 	}
 
diff --git a/arch/arm/mach-msm/msm_bus/msm_bus_rpm.c b/arch/arm/mach-msm/msm_bus/msm_bus_rpm.c
index 2213132..fc38ef7 100644
--- a/arch/arm/mach-msm/msm_bus/msm_bus_rpm.c
+++ b/arch/arm/mach-msm/msm_bus/msm_bus_rpm.c
@@ -239,7 +239,7 @@
 	struct msm_bus_inode_info *info,
 	struct msm_bus_fabric_registration *fab_pdata,
 	void *sel_cdata, int *master_tiers,
-	long int add_bw)
+	int64_t add_bw)
 {
 	int index, i, j, tiers, ports;
 	struct commit_data *sel_cd = (struct commit_data *)sel_cdata;
@@ -302,9 +302,9 @@
 					msm_bus_create_bw_tier_pair_bytes(tier,
 					tieredbw);
 				sel_cd->actarb[index] = tieredbw;
-				MSM_BUS_DBG("tier:%d mport: %d tiered_bw:%ld "
-				"bwsum: %ld\n", hop_tier, info->node_info->
-				masterp[i], tieredbw, *hop->link_info.sel_bw);
+				MSM_BUS_DBG("tr:%d mpor:%d tbw:%ld bws: %lld\n",
+					hop_tier, info->node_info->masterp[i],
+					tieredbw, *hop->link_info.sel_bw);
 			}
 		}
 	}
@@ -314,10 +314,12 @@
 	for (i = 0; i < ports; i++) {
 		sel_cd->bwsum[hop->node_info->slavep[i]]
 			= (uint16_t)msm_bus_create_bw_tier_pair_bytes(0,
-			(*hop->link_info.sel_bw/hop->node_info->num_sports));
-		MSM_BUS_DBG("slavep:%d, link_bw: %ld\n",
-			hop->node_info->slavep[i], (*hop->link_info.sel_bw/
-			hop->node_info->num_sports));
+			(uint32_t)msm_bus_div64(hop->node_info->num_sports,
+			*hop->link_info.sel_bw));
+		MSM_BUS_DBG("slavep:%d, link_bw: %u\n",
+			hop->node_info->slavep[i], (uint32_t)
+			msm_bus_div64(hop->node_info->num_sports,
+			*hop->link_info.sel_bw));
 	}
 }
 
@@ -756,7 +758,7 @@
 	struct msm_bus_inode_info *info,
 	struct msm_bus_fabric_registration *fab_pdata,
 	void *sel_cdata, int *master_tiers,
-	long int add_bw)
+	int64_t add_bw)
 {
 	int index, i, j, tiers, ports;
 	struct commit_data *sel_cd = (struct commit_data *)sel_cdata;
@@ -808,9 +810,9 @@
 				sel_cd->arb[tier][index] =
 				msm_bus_create_bw_tier_pair_bytes(0, tieredbw);
 				sel_cd->actarb[tier][index] = tieredbw;
-				MSM_BUS_DBG("tier:%d mport: %d tiered_bw:%lu "
-				"bwsum: %ld\n", hop_tier, info->node_info->
-				masterp[i], tieredbw, *hop->link_info.sel_bw);
+				MSM_BUS_DBG("tr:%d mpor:%d tbw:%lu bws: %lld\n",
+				hop_tier, info->node_info->masterp[i], tieredbw,
+				*hop->link_info.sel_bw);
 			}
 		}
 	}
@@ -820,11 +822,13 @@
 	ports = INTERLEAVED_VAL(fab_pdata, hop->node_info->num_sports);
 	for (i = 0; i < ports; i++) {
 		sel_cd->bwsum[hop->node_info->slavep[i]]
-			= msm_bus_pack_bwsum_bytes((*hop->link_info.
-			sel_bw/hop->node_info->num_sports));
-		MSM_BUS_DBG("slavep:%d, link_bw: %ld\n",
-			hop->node_info->slavep[i], (*hop->link_info.sel_bw/
-			hop->node_info->num_sports));
+			= msm_bus_pack_bwsum_bytes((uint32_t)
+			msm_bus_div64(hop->node_info->num_sports,
+			*hop->link_info.sel_bw));
+		MSM_BUS_DBG("slavep:%d, link_bw: %lld\n",
+			hop->node_info->slavep[i],
+			msm_bus_div64(hop->node_info->num_sports,
+			*hop->link_info.sel_bw));
 	}
 }
 
diff --git a/arch/arm/mach-msm/msm_cpr.c b/arch/arm/mach-msm/msm_cpr.c
index c00352d..b68a8db 100644
--- a/arch/arm/mach-msm/msm_cpr.c
+++ b/arch/arm/mach-msm/msm_cpr.c
@@ -50,6 +50,7 @@
 static struct platform_device *cpr_pdev;
 
 static bool enable = 1;
+static bool disable_cpr;
 module_param(enable, bool, 0644);
 MODULE_PARM_DESC(enable, "CPR Enable");
 
@@ -329,12 +330,12 @@
 	cpr_write_reg(cpr, RBIF_CONT_NACK_CMD, 0x1);
 }
 
-static void cpr_irq_set(struct msm_cpr *cpr, uint32_t irq, bool enable)
+static void cpr_irq_set(struct msm_cpr *cpr, uint32_t irq, bool enable_irq)
 {
 	uint32_t irq_enabled;
 
 	irq_enabled = cpr_read_reg(cpr, RBIF_IRQ_EN(cpr->config->irq_line));
-	if (enable == 1)
+	if (enable_irq == 1)
 		irq_enabled |= irq;
 	else
 		irq_enabled &= ~irq;
@@ -832,7 +833,7 @@
 
 void msm_cpr_pm_resume(void)
 {
-	if (!enable)
+	if (!enable || disable_cpr)
 		return;
 
 	msm_cpr_resume(&cpr_pdev->dev);
@@ -841,7 +842,7 @@
 
 void msm_cpr_pm_suspend(void)
 {
-	if (!enable)
+	if (!enable || disable_cpr)
 		return;
 
 	msm_cpr_suspend(&cpr_pdev->dev);
@@ -853,7 +854,7 @@
 {
 	struct msm_cpr *cpr;
 
-	if (!enable)
+	if (!enable || disable_cpr)
 		return;
 
 	cpr = platform_get_drvdata(cpr_pdev);
@@ -866,7 +867,7 @@
 {
 	struct msm_cpr *cpr;
 
-	if (!enable)
+	if (!enable || disable_cpr)
 		return;
 
 	cpr = platform_get_drvdata(cpr_pdev);
@@ -893,6 +894,12 @@
 		return -EIO;
 	}
 
+	if (pdata->disable_cpr == true) {
+		pr_err("CPR disabled by modem\n");
+		disable_cpr = true;
+		return -EPERM;
+	}
+
 	cpr = devm_kzalloc(&pdev->dev, sizeof(struct msm_cpr), GFP_KERNEL);
 	if (!cpr) {
 		enable = false;
diff --git a/arch/arm/mach-msm/msm_cpr.h b/arch/arm/mach-msm/msm_cpr.h
index 005d9b1..3d10478 100644
--- a/arch/arm/mach-msm/msm_cpr.h
+++ b/arch/arm/mach-msm/msm_cpr.h
@@ -188,6 +188,7 @@
 	uint32_t max_nom_freq;
 	uint32_t max_freq;
 	uint32_t max_quot;
+	bool disable_cpr;
 	struct msm_cpr_vp_data *vp_data;
 	uint32_t (*get_quot)(uint32_t max_quot, uint32_t max_freq,
 				uint32_t new_freq);
diff --git a/arch/arm/mach-msm/msm_dcvs.c b/arch/arm/mach-msm/msm_dcvs.c
index 9601b7e..b2160c5 100644
--- a/arch/arm/mach-msm/msm_dcvs.c
+++ b/arch/arm/mach-msm/msm_dcvs.c
@@ -14,7 +14,6 @@
 #include <linux/kernel.h>
 #include <linux/init.h>
 #include <linux/module.h>
-#include <linux/mutex.h>
 #include <linux/kthread.h>
 #include <linux/kobject.h>
 #include <linux/ktime.h>
@@ -23,26 +22,18 @@
 #include <linux/spinlock.h>
 #include <linux/stringify.h>
 #include <linux/debugfs.h>
+#include <linux/msm_tsens.h>
 #include <asm/atomic.h>
 #include <asm/page.h>
 #include <mach/msm_dcvs.h>
+#include <trace/events/mpdcvs_trace.h>
 
 #define CORE_HANDLE_OFFSET (0xA0)
 #define __err(f, ...) pr_err("MSM_DCVS: %s: " f, __func__, __VA_ARGS__)
 #define __info(f, ...) pr_info("MSM_DCVS: %s: " f, __func__, __VA_ARGS__)
 #define MAX_PENDING	(5)
 
-enum {
-	MSM_DCVS_DEBUG_NOTIFIER    = BIT(0),
-	MSM_DCVS_DEBUG_IDLE_PULSE  = BIT(1),
-	MSM_DCVS_DEBUG_FREQ_CHANGE = BIT(2),
-};
-
 struct core_attribs {
-	struct kobj_attribute core_id;
-	struct kobj_attribute idle_enabled;
-	struct kobj_attribute freq_change_enabled;
-	struct kobj_attribute actual_freq;
 	struct kobj_attribute freq_change_us;
 
 	struct kobj_attribute disable_pc_threshold;
@@ -68,12 +59,49 @@
 	struct kobj_attribute leakage_coeff_c;
 	struct kobj_attribute leakage_coeff_d;
 
+	struct kobj_attribute thermal_poll_ms;
+
+	struct kobj_attribute freq_tbl;
+
 	struct attribute_group attrib_group;
 };
 
+enum pending_freq_state {
+	/*
+	 * used by the thread to check if pending_freq was updated while it was
+	 * setting previous frequency - this is written to and used by the
+	 * freq updating thread
+	 */
+	NO_OUTSTANDING_FREQ_CHANGE = 0,
+
+	/*
+	 * This request is set to indicate that the governor is stopped and no
+	 * more frequency change requests are accepted untill it starts again.
+	 * This is checked/used by the threads that want to change the freq
+	 */
+	STOP_FREQ_CHANGE = -1,
+
+	/*
+	 * Any other +ve value means that a freq change was requested and the
+	 * thread has not gotten around to update it
+	 *
+	 * Any other -ve value means that this is the last freq change i.e. a
+	 * freq change was requested but the thread has not run yet and
+	 * meanwhile the governor was stopped.
+	 */
+};
+
 struct dcvs_core {
+	spinlock_t	idle_state_change_lock;
+	/* 0 when not idle (busy)  1 when idle and -1 when governor starts and
+	 * we dont know whether the next call is going to be idle enter or exit
+	 */
+	int		idle_entered;
+
+	enum msm_dcvs_core_type type;
+	/* this is the number in each type for example cpu 0,1,2 and gpu 0,1 */
+	int type_core_num;
 	char core_name[CORE_NAME_MAX];
-	uint32_t new_freq[MAX_PENDING];
 	uint32_t actual_freq;
 	uint32_t freq_change_us;
 
@@ -81,122 +109,200 @@
 
 	struct msm_dcvs_algo_param algo_param;
 	struct msm_dcvs_energy_curve_coeffs coeffs;
-	struct msm_dcvs_idle *idle_driver;
-	struct msm_dcvs_freq *freq_driver;
 
 	/* private */
-	int64_t time_start;
-	struct mutex lock;
-	spinlock_t cpu_lock;
+	ktime_t time_start;
 	struct task_struct *task;
 	struct core_attribs attrib;
-	uint32_t handle;
-	uint32_t freq_pending;
-	struct hrtimer timer;
-	int32_t timer_disabled;
-	/* track if kthread for change_freq is active */
-	int32_t change_freq_activated;
+	uint32_t dcvs_core_id;
 	struct msm_dcvs_core_info *info;
+	int sensor;
+	wait_queue_head_t wait_q;
+
+	int (*set_frequency)(int type_core_num, unsigned int freq);
+	unsigned int (*get_frequency)(int type_core_num);
+	int (*idle_enable)(int type_core_num,
+			enum msm_core_control_event event);
+
+	spinlock_t	pending_freq_lock;
+	int pending_freq;
+
+	struct hrtimer	slack_timer;
+	struct delayed_work	temperature_work;
 };
 
-static int msm_dcvs_debug;
 static int msm_dcvs_enabled = 1;
 module_param_named(enable, msm_dcvs_enabled, int, S_IRUGO | S_IWUSR | S_IWGRP);
 
-static struct dentry *debugfs_base;
+static struct dentry		*debugfs_base;
 
 static struct dcvs_core core_list[CORES_MAX];
-static DEFINE_MUTEX(core_list_lock);
 
 static struct kobject *cores_kobj;
-static struct dcvs_core *core_handles[CORES_MAX];
 
-/* Change core frequency, called with core mutex locked */
+static void force_stop_slack_timer(struct dcvs_core *core)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&core->idle_state_change_lock, flags);
+	hrtimer_cancel(&core->slack_timer);
+	spin_unlock_irqrestore(&core->idle_state_change_lock, flags);
+}
+
+static void force_start_slack_timer(struct dcvs_core *core, int slack_us)
+{
+	unsigned long flags;
+	int ret;
+
+	spin_lock_irqsave(&core->idle_state_change_lock, flags);
+
+	/*
+	 * only start the timer if governor is not stopped
+	 */
+	if (slack_us != 0) {
+		ret = hrtimer_start(&core->slack_timer,
+				ktime_set(0, slack_us * 1000),
+				HRTIMER_MODE_REL_PINNED);
+		if (ret) {
+			pr_err("%s Failed to start timer ret = %d\n",
+					core->core_name, ret);
+		}
+	}
+
+	spin_unlock_irqrestore(&core->idle_state_change_lock, flags);
+}
+
+static void stop_slack_timer(struct dcvs_core *core)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&core->idle_state_change_lock, flags);
+	/* err only for cpu type's GPU's can do idle exit consecutively */
+	if (core->idle_entered == 1 && !(core->dcvs_core_id >= GPU_OFFSET))
+		__err("%s trying to reenter idle", core->core_name);
+	core->idle_entered = 1;
+	hrtimer_cancel(&core->slack_timer);
+	core->idle_entered = 1;
+	spin_unlock_irqrestore(&core->idle_state_change_lock, flags);
+}
+
+static void start_slack_timer(struct dcvs_core *core, int slack_us)
+{
+	unsigned long flags1, flags2;
+	int ret;
+
+	spin_lock_irqsave(&core->idle_state_change_lock, flags2);
+
+	spin_lock_irqsave(&core->pending_freq_lock, flags1);
+
+	/* err only for cpu type's GPU's can do idle enter consecutively */
+	if (core->idle_entered == 0 && !(core->dcvs_core_id >= GPU_OFFSET))
+		__err("%s trying to reexit idle", core->core_name);
+	core->idle_entered = 0;
+	/*
+	 * only start the timer if governor is not stopped
+	 */
+	if (slack_us != 0
+		&& !(core->pending_freq < NO_OUTSTANDING_FREQ_CHANGE)) {
+		ret = hrtimer_start(&core->slack_timer,
+				ktime_set(0, slack_us * 1000),
+				HRTIMER_MODE_REL_PINNED);
+		if (ret) {
+			pr_err("%s Failed to start timer ret = %d\n",
+					core->core_name, ret);
+		}
+	}
+	spin_unlock_irqrestore(&core->pending_freq_lock, flags1);
+
+	spin_unlock_irqrestore(&core->idle_state_change_lock, flags2);
+}
+
+static void restart_slack_timer(struct dcvs_core *core, int slack_us)
+{
+	unsigned long flags1, flags2;
+	int ret;
+
+	spin_lock_irqsave(&core->idle_state_change_lock, flags2);
+
+	hrtimer_cancel(&core->slack_timer);
+
+	spin_lock_irqsave(&core->pending_freq_lock, flags1);
+
+	/*
+	 * only start the timer if idle is not entered
+	 * and governor is not stopped
+	 */
+	if (slack_us != 0 && (core->idle_entered != 1)
+		&& !(core->pending_freq < NO_OUTSTANDING_FREQ_CHANGE)) {
+		ret = hrtimer_start(&core->slack_timer,
+				ktime_set(0, slack_us * 1000),
+				HRTIMER_MODE_REL_PINNED);
+		if (ret) {
+			pr_err("%s Failed to start timer ret = %d\n",
+					core->core_name, ret);
+		}
+	}
+	spin_unlock_irqrestore(&core->pending_freq_lock, flags1);
+	spin_unlock_irqrestore(&core->idle_state_change_lock, flags2);
+}
+
 static int __msm_dcvs_change_freq(struct dcvs_core *core)
 {
 	int ret = 0;
 	unsigned long flags = 0;
-	unsigned int requested_freq = 0;
-	unsigned int prev_freq = 0;
-	int64_t time_start = 0;
-	int64_t time_end = 0;
+	int requested_freq = 0;
+	ktime_t time_start;
 	uint32_t slack_us = 0;
 	uint32_t ret1 = 0;
 
-	if (!core->freq_driver || !core->freq_driver->set_frequency) {
-		/* Core may have unregistered or hotplugged */
-		return -ENODEV;
-	}
+	spin_lock_irqsave(&core->pending_freq_lock, flags);
 repeat:
-	spin_lock_irqsave(&core->cpu_lock, flags);
-	if (unlikely(!core->freq_pending)) {
-		spin_unlock_irqrestore(&core->cpu_lock, flags);
-		return ret;
-	}
-	requested_freq = core->new_freq[core->freq_pending - 1];
-	if (unlikely(core->freq_pending > 1) &&
-		(msm_dcvs_debug & MSM_DCVS_DEBUG_FREQ_CHANGE)) {
-		int i;
-		for (i = 0; i < core->freq_pending - 1; i++) {
-			__info("Core %s missing freq %u\n",
-				core->core_name, core->new_freq[i]);
-		}
-	}
+	BUG_ON(!core->pending_freq);
+	if (core->pending_freq == STOP_FREQ_CHANGE)
+		BUG();
+
+	requested_freq = core->pending_freq;
 	time_start = core->time_start;
-	core->time_start = 0;
-	core->freq_pending = 0;
-	/**
-	 * Cancel the timers, we dont want the timer firing as we are
-	 * changing the clock rate. Dont let idle_exit and others setup
-	 * timers as well.
-	 */
-	hrtimer_cancel(&core->timer);
-	core->timer_disabled = 1;
-	spin_unlock_irqrestore(&core->cpu_lock, flags);
+	core->time_start = ns_to_ktime(0);
+
+	if (requested_freq < 0) {
+		requested_freq = -1 * requested_freq;
+		core->pending_freq = STOP_FREQ_CHANGE;
+	} else {
+		core->pending_freq = NO_OUTSTANDING_FREQ_CHANGE;
+	}
 
 	if (requested_freq == core->actual_freq)
-		return ret;
+		goto out;
+
+	spin_unlock_irqrestore(&core->pending_freq_lock, flags);
 
 	/**
 	 * Call the frequency sink driver to change the frequency
 	 * We will need to get back the actual frequency in KHz and
 	 * the record the time taken to change it.
 	 */
-	ret = core->freq_driver->set_frequency(core->freq_driver,
-				requested_freq);
-	if (ret <= 0) {
+	ret = core->set_frequency(core->type_core_num, requested_freq);
+	if (ret <= 0)
 		__err("Core %s failed to set freq %u\n",
 				core->core_name, requested_freq);
 		/* continue to call TZ to get updated slack timer */
-	} else {
-		prev_freq = core->actual_freq;
+	else
 		core->actual_freq = ret;
-	}
 
-	time_end = ktime_to_ns(ktime_get());
-	if (msm_dcvs_debug & MSM_DCVS_DEBUG_FREQ_CHANGE)
-		__info("Core %s Time end %llu Time start: %llu\n",
-			core->core_name, time_end, time_start);
-	time_end -= time_start;
-	do_div(time_end, NSEC_PER_USEC);
-	core->freq_change_us = (uint32_t)time_end;
+	core->freq_change_us = (uint32_t)ktime_to_us(
+					ktime_sub(ktime_get(), time_start));
 
 	/**
 	 * Disable low power modes if the actual frequency is >
 	 * disable_pc_threshold.
 	 */
-	if (core->actual_freq >
-			core->algo_param.disable_pc_threshold) {
-		core->idle_driver->enable(core->idle_driver,
+	if (core->actual_freq > core->algo_param.disable_pc_threshold) {
+		core->idle_enable(core->type_core_num,
 				MSM_DCVS_DISABLE_HIGH_LATENCY_MODES);
-		if (msm_dcvs_debug & MSM_DCVS_DEBUG_IDLE_PULSE)
-			__info("Disabling LPM for %s\n", core->core_name);
-	} else if (core->actual_freq <=
-			core->algo_param.disable_pc_threshold) {
-		core->idle_driver->enable(core->idle_driver,
+	} else if (core->actual_freq <= core->algo_param.disable_pc_threshold) {
+		core->idle_enable(core->type_core_num,
 				MSM_DCVS_ENABLE_HIGH_LATENCY_MODES);
-		if (msm_dcvs_debug & MSM_DCVS_DEBUG_IDLE_PULSE)
-			__info("Enabling LPM for %s\n", core->core_name);
 	}
 
 	/**
@@ -204,108 +310,165 @@
 	 * to this frequency and that will get us the new slack
 	 * timer
 	 */
-	ret = msm_dcvs_scm_event(core->handle, MSM_DCVS_SCM_CLOCK_FREQ_UPDATE,
-		core->actual_freq, (uint32_t)time_end, &slack_us, &ret1);
-	if (!ret) {
-		/* Reset the slack timer */
-		if (slack_us) {
-			core->timer_disabled = 0;
-			ret = hrtimer_start(&core->timer,
-				ktime_set(0, slack_us * 1000),
-				HRTIMER_MODE_REL_PINNED);
-			if (ret)
-				__err("Failed to register timer for core %s\n",
-						core->core_name);
-		}
-	} else {
-		__err("Error sending core (%s) freq change (%u)\n",
-				core->core_name, core->actual_freq);
+	ret = msm_dcvs_scm_event(core->dcvs_core_id,
+			MSM_DCVS_SCM_CLOCK_FREQ_UPDATE,
+			core->actual_freq, core->freq_change_us,
+			&slack_us, &ret1);
+	if (ret) {
+		__err("Error sending core (%s) dcvs_core_id = %d freq change (%u) reqfreq = %d slack_us=%d ret = %d\n",
+				core->core_name, core->dcvs_core_id,
+				core->actual_freq, requested_freq,
+				slack_us, ret);
 	}
 
-	if (msm_dcvs_debug & MSM_DCVS_DEBUG_FREQ_CHANGE)
-		__info("Freq %u requested for core %s (actual %u prev %u) "
-			"change time %u us slack time %u us\n",
-			requested_freq, core->core_name,
-			core->actual_freq, prev_freq,
-			core->freq_change_us, slack_us);
+	/* TODO confirm that we get a valid freq from SM even when the above
+	 * FREQ_UPDATE fails
+	 */
+	restart_slack_timer(core, slack_us);
+	spin_lock_irqsave(&core->pending_freq_lock, flags);
 
 	/**
 	 * By the time we are done with freq changes, we could be asked to
 	 * change again. Check before exiting.
 	 */
-	if (core->freq_pending)
+	if (core->pending_freq != NO_OUTSTANDING_FREQ_CHANGE
+		&& core->pending_freq != STOP_FREQ_CHANGE) {
 		goto repeat;
+	}
 
-	core->change_freq_activated = 0;
+out:   /* should always be jumped to with the spin_lock held */
+	spin_unlock_irqrestore(&core->pending_freq_lock, flags);
+
 	return ret;
 }
 
+static void msm_dcvs_report_temp_work(struct work_struct *work)
+{
+	struct dcvs_core *core = container_of(work,
+					struct dcvs_core,
+					temperature_work.work);
+	struct msm_dcvs_core_info *info = core->info;
+	struct tsens_device tsens_dev;
+	int ret;
+	unsigned long temp = 0;
+	int interval_ms;
+
+	tsens_dev.sensor_num = core->sensor;
+	ret = tsens_get_temp(&tsens_dev, &temp);
+	if (!temp) {
+		tsens_dev.sensor_num = 0;
+		ret = tsens_get_temp(&tsens_dev, &temp);
+		if (!temp)
+			goto out;
+	}
+
+	if (temp == info->power_param.current_temp)
+		goto out;
+	info->power_param.current_temp = temp;
+
+	ret = msm_dcvs_scm_set_power_params(core->dcvs_core_id,
+			&info->power_param,
+			&info->freq_tbl[0], &core->coeffs);
+out:
+	if (info->thermal_poll_ms == 0)
+		interval_ms = 60000;
+	else if (info->thermal_poll_ms < 1000)
+		interval_ms = 1000;
+	else
+		interval_ms = info->thermal_poll_ms;
+
+	schedule_delayed_work(&core->temperature_work,
+			msecs_to_jiffies(interval_ms));
+}
+
 static int msm_dcvs_do_freq(void *data)
 {
 	struct dcvs_core *core = (struct dcvs_core *)data;
 	static struct sched_param param = {.sched_priority = MAX_RT_PRIO - 1};
 
 	sched_setscheduler(current, SCHED_FIFO, &param);
-	set_current_state(TASK_UNINTERRUPTIBLE);
 
 	while (!kthread_should_stop()) {
-		mutex_lock(&core->lock);
-		__msm_dcvs_change_freq(core);
-		mutex_unlock(&core->lock);
-
-		schedule();
+		wait_event(core->wait_q, !(core->pending_freq == 0 ||
+					  core->pending_freq == -1) ||
+					  kthread_should_stop());
 
 		if (kthread_should_stop())
 			break;
 
-		set_current_state(TASK_UNINTERRUPTIBLE);
+		__msm_dcvs_change_freq(core);
 	}
 
-	__set_current_state(TASK_RUNNING);
-
 	return 0;
 }
 
+/* freq_pending_lock should be held */
+static void request_freq_change(struct dcvs_core *core, int new_freq)
+{
+	if (new_freq == NO_OUTSTANDING_FREQ_CHANGE) {
+		if (core->pending_freq != STOP_FREQ_CHANGE) {
+			__err("%s gov started with earlier pending freq %d\n",
+					core->core_name, core->pending_freq);
+		}
+		core->pending_freq = NO_OUTSTANDING_FREQ_CHANGE;
+		return;
+	}
+
+	if (new_freq == STOP_FREQ_CHANGE) {
+		if (core->pending_freq == NO_OUTSTANDING_FREQ_CHANGE)
+			core->pending_freq = STOP_FREQ_CHANGE;
+		else if (core->pending_freq > 0)
+			core->pending_freq = -1 * core->pending_freq;
+		return;
+	}
+
+	if (core->pending_freq < 0) {
+		/* a value less than 0 means that the governor has stopped
+		 * and no more freq changes should be requested
+		 */
+		return;
+	}
+
+	if (core->actual_freq != new_freq && core->pending_freq != new_freq) {
+		core->pending_freq = new_freq;
+		core->time_start = ktime_get();
+		wake_up(&core->wait_q);
+	}
+}
+
 static int msm_dcvs_update_freq(struct dcvs_core *core,
 		enum msm_dcvs_scm_event event, uint32_t param0,
-		uint32_t *ret1, int *freq_changed)
+		uint32_t *ret1)
 {
 	int ret = 0;
 	unsigned long flags = 0;
-	uint32_t new_freq = 0;
+	uint32_t new_freq = -EINVAL;
 
-	spin_lock_irqsave(&core->cpu_lock, flags);
-	ret = msm_dcvs_scm_event(core->handle, event, param0,
+	spin_lock_irqsave(&core->pending_freq_lock, flags);
+
+	ret = msm_dcvs_scm_event(core->dcvs_core_id, event, param0,
 				core->actual_freq, &new_freq, ret1);
 	if (ret) {
-		__err("Error (%d) sending SCM event %d for core %s\n",
+		if (ret == -13)
+			ret = 0;
+		else
+			__err("Error (%d) sending SCM event %d for core %s\n",
 				ret, event, core->core_name);
-		goto freq_done;
+		goto out;
 	}
 
-	if ((core->actual_freq != new_freq) &&
-			(core->new_freq[core->freq_pending] != new_freq)) {
-		if (core->freq_pending >= MAX_PENDING - 1)
-			core->freq_pending = MAX_PENDING - 1;
-		core->new_freq[core->freq_pending++] = new_freq;
-		core->time_start = ktime_to_ns(ktime_get());
-
-		/* Schedule the frequency change */
-		if (!core->task)
-			__err("Uninitialized task for core %s\n",
-					core->core_name);
-		else {
-			if (freq_changed)
-				*freq_changed = 1;
-			core->change_freq_activated = 1;
-			wake_up_process(core->task);
-		}
-	} else {
-		if (freq_changed)
-			*freq_changed = 0;
+	if (new_freq == 0) {
+		/*
+		 * sometimes TZ gives us a 0 freq back,
+		 * do not queue up a request
+		 */
+		goto out;
 	}
-freq_done:
-	spin_unlock_irqrestore(&core->cpu_lock, flags);
+
+	request_freq_change(core, new_freq);
+
+out:
+	spin_unlock_irqrestore(&core->pending_freq_lock, flags);
 
 	return ret;
 }
@@ -313,19 +476,17 @@
 static enum hrtimer_restart msm_dcvs_core_slack_timer(struct hrtimer *timer)
 {
 	int ret = 0;
-	struct dcvs_core *core = container_of(timer, struct dcvs_core, timer);
+	struct dcvs_core *core = container_of(timer,
+					struct dcvs_core, slack_timer);
 	uint32_t ret1;
-	uint32_t ret2;
 
-	if (msm_dcvs_debug & MSM_DCVS_DEBUG_FREQ_CHANGE)
-		__info("Slack timer fired for core %s\n", core->core_name);
-
+	trace_printk("dcvs: Slack timer fired for core=%s\n", core->core_name);
 	/**
 	 * Timer expired, notify TZ
 	 * Dont care about the third arg.
 	 */
 	ret = msm_dcvs_update_freq(core, MSM_DCVS_SCM_QOS_TIMER_EXPIRED, 0,
-				   &ret1, &ret2);
+				   &ret1);
 	if (ret)
 		__err("Timer expired for core %s but failed to notify.\n",
 				core->core_name);
@@ -346,6 +507,28 @@
 	return snprintf(buf, PAGE_SIZE, "%d\n", v); \
 }
 
+#define DCVS_PARAM_STORE(_name) \
+static ssize_t msm_dcvs_attr_##_name##_show(struct kobject *kobj,\
+		struct kobj_attribute *attr, char *buf) \
+{ \
+	struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
+	return snprintf(buf, PAGE_SIZE, "%d\n", core->info->_name); \
+} \
+static ssize_t msm_dcvs_attr_##_name##_store(struct kobject *kobj, \
+		struct kobj_attribute *attr, const char *buf, size_t count) \
+{ \
+	int ret = 0; \
+	uint32_t val = 0; \
+	struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
+	ret = kstrtouint(buf, 10, &val); \
+	if (ret) { \
+		__err("Invalid input %s for %s\n", buf, __stringify(_name));\
+	} else { \
+		core->info->_name = val; \
+	} \
+	return count; \
+}
+
 #define DCVS_ALGO_PARAM(_name) \
 static ssize_t msm_dcvs_attr_##_name##_show(struct kobject *kobj,\
 		struct kobj_attribute *attr, char *buf) \
@@ -359,14 +542,13 @@
 	int ret = 0; \
 	uint32_t val = 0; \
 	struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
-	mutex_lock(&core->lock); \
 	ret = kstrtouint(buf, 10, &val); \
 	if (ret) { \
 		__err("Invalid input %s for %s\n", buf, __stringify(_name));\
 	} else { \
 		uint32_t old_val = core->algo_param._name; \
 		core->algo_param._name = val; \
-		ret = msm_dcvs_scm_set_algo_params(core->handle, \
+		ret = msm_dcvs_scm_set_algo_params(core->dcvs_core_id, \
 				&core->algo_param); \
 		if (ret) { \
 			core->algo_param._name = old_val; \
@@ -374,7 +556,6 @@
 					ret, val, __stringify(_name)); \
 		} \
 	} \
-	mutex_unlock(&core->lock); \
 	return count; \
 }
 
@@ -391,14 +572,13 @@
 	int ret = 0; \
 	int32_t val = 0; \
 	struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, _name); \
-	mutex_lock(&core->lock); \
 	ret = kstrtoint(buf, 10, &val); \
 	if (ret) { \
 		__err("Invalid input %s for %s\n", buf, __stringify(_name));\
 	} else { \
 		int32_t old_val = core->coeffs._name; \
 		core->coeffs._name = val; \
-		ret = msm_dcvs_scm_set_power_params(core->handle, \
+		ret = msm_dcvs_scm_set_power_params(core->dcvs_core_id, \
 			&core->info->power_param, &core->info->freq_tbl[0], \
 				&core->coeffs); \
 		if (ret) { \
@@ -407,7 +587,6 @@
 					ret, val, __stringify(_name)); \
 		} \
 	} \
-	mutex_unlock(&core->lock); \
 	return count; \
 }
 
@@ -429,10 +608,6 @@
  * Function declarations for different attributes.
  * Gets used when setting the attribute show and store parameters.
  */
-DCVS_PARAM_SHOW(core_id, core->handle)
-DCVS_PARAM_SHOW(idle_enabled, (core->idle_driver != NULL))
-DCVS_PARAM_SHOW(freq_change_enabled, (core->freq_driver != NULL))
-DCVS_PARAM_SHOW(actual_freq, (core->actual_freq))
 DCVS_PARAM_SHOW(freq_change_us, (core->freq_change_us))
 
 DCVS_ALGO_PARAM(disable_pc_threshold)
@@ -458,11 +633,85 @@
 DCVS_ENERGY_PARAM(leakage_coeff_c)
 DCVS_ENERGY_PARAM(leakage_coeff_d)
 
+DCVS_PARAM_STORE(thermal_poll_ms)
+
+static ssize_t msm_dcvs_attr_freq_tbl_show(struct kobject *kobj,
+					   struct kobj_attribute *attr,
+					   char *buf)
+{
+	struct msm_dcvs_freq_entry *freq_tbl;
+	char *buf_idx = buf;
+	int i, len;
+	struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, freq_tbl);
+
+	freq_tbl = core->info->freq_tbl;
+	*buf_idx = '\0';
+
+	/* limit the number of frequencies we will print into
+	 * the PAGE_SIZE sysfs show buffer. */
+	if (core->info->power_param.num_freq > 64)
+		return 0;
+
+	for (i = 0; i < core->info->power_param.num_freq; i++) {
+		if (freq_tbl[i].is_trans_level) {
+			len = snprintf(buf_idx, 10, "%7d ", freq_tbl[i].freq);
+			/* buf_idx always points at terminating null */
+			buf_idx += len;
+		}
+	}
+	/* overwrite final trailing space with newline */
+	if (buf_idx > buf)
+		*(buf_idx - 1) = '\n';
+
+	return buf_idx - buf;
+}
+
+static ssize_t msm_dcvs_attr_freq_tbl_store(struct kobject *kobj,
+					    struct kobj_attribute *attr,
+					    const char *buf,
+					    size_t count)
+{
+	struct msm_dcvs_freq_entry *freq_tbl;
+	uint32_t freq;
+	int i, ret;
+	struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, freq_tbl);
+
+	freq_tbl = core->info->freq_tbl;
+
+	ret = kstrtouint(buf, 10, &freq);
+	if (ret) {
+		__err("Invalid input %s for freq_tbl\n", buf);
+		return count;
+	}
+
+	for (i = 0; i < core->info->power_param.num_freq; i++)
+		if (freq_tbl[i].freq == freq) {
+			freq_tbl[i].is_trans_level ^= 1;
+			break;
+		}
+
+	if (i >= core->info->power_param.num_freq) {
+		__err("Invalid frequency for freq_tbl: %d\n", freq);
+		return count;
+	}
+
+	ret = msm_dcvs_scm_set_power_params(core->dcvs_core_id,
+					    &core->info->power_param,
+					    &core->info->freq_tbl[0],
+					    &core->coeffs);
+	if (ret) {
+		freq_tbl[i].is_trans_level ^= 1;
+		__err("Error %d in toggling freq %d (orig enable val %d)\n",
+		      ret, freq_tbl[i].freq, freq_tbl[i].is_trans_level);
+	}
+	return count;
+}
+
 static int msm_dcvs_setup_core_sysfs(struct dcvs_core *core)
 {
 	int ret = 0;
 	struct kobject *core_kobj = NULL;
-	const int attr_count = 27;
+	const int attr_count = 25;
 
 	BUG_ON(!cores_kobj);
 
@@ -474,37 +723,35 @@
 		goto done;
 	}
 
+	DCVS_RO_ATTRIB(0, freq_change_us);
 
-	DCVS_RO_ATTRIB(0, core_id);
-	DCVS_RO_ATTRIB(1, idle_enabled);
-	DCVS_RO_ATTRIB(2, freq_change_enabled);
-	DCVS_RO_ATTRIB(3, actual_freq);
-	DCVS_RO_ATTRIB(4, freq_change_us);
+	DCVS_RW_ATTRIB(1, disable_pc_threshold);
+	DCVS_RW_ATTRIB(2, em_win_size_min_us);
+	DCVS_RW_ATTRIB(3, em_win_size_max_us);
+	DCVS_RW_ATTRIB(4, em_max_util_pct);
+	DCVS_RW_ATTRIB(5, group_id);
+	DCVS_RW_ATTRIB(6, max_freq_chg_time_us);
+	DCVS_RW_ATTRIB(7, slack_mode_dynamic);
+	DCVS_RW_ATTRIB(8, slack_weight_thresh_pct);
+	DCVS_RW_ATTRIB(9, slack_time_min_us);
+	DCVS_RW_ATTRIB(10, slack_time_max_us);
+	DCVS_RW_ATTRIB(11, ss_iobusy_conv);
+	DCVS_RW_ATTRIB(12, ss_win_size_min_us);
+	DCVS_RW_ATTRIB(13, ss_win_size_max_us);
+	DCVS_RW_ATTRIB(14, ss_util_pct);
 
-	DCVS_RW_ATTRIB(5, disable_pc_threshold);
-	DCVS_RW_ATTRIB(6, em_win_size_min_us);
-	DCVS_RW_ATTRIB(7, em_win_size_max_us);
-	DCVS_RW_ATTRIB(8, em_max_util_pct);
-	DCVS_RW_ATTRIB(9, group_id);
-	DCVS_RW_ATTRIB(10, max_freq_chg_time_us);
-	DCVS_RW_ATTRIB(11, slack_mode_dynamic);
-	DCVS_RW_ATTRIB(12, slack_time_min_us);
-	DCVS_RW_ATTRIB(13, slack_time_max_us);
-	DCVS_RW_ATTRIB(14, slack_weight_thresh_pct);
-	DCVS_RW_ATTRIB(15, ss_iobusy_conv);
-	DCVS_RW_ATTRIB(16, ss_win_size_min_us);
-	DCVS_RW_ATTRIB(17, ss_win_size_max_us);
-	DCVS_RW_ATTRIB(18, ss_util_pct);
+	DCVS_RW_ATTRIB(15, active_coeff_a);
+	DCVS_RW_ATTRIB(16, active_coeff_b);
+	DCVS_RW_ATTRIB(17, active_coeff_c);
+	DCVS_RW_ATTRIB(18, leakage_coeff_a);
+	DCVS_RW_ATTRIB(19, leakage_coeff_b);
+	DCVS_RW_ATTRIB(20, leakage_coeff_c);
+	DCVS_RW_ATTRIB(21, leakage_coeff_d);
+	DCVS_RW_ATTRIB(22, thermal_poll_ms);
 
-	DCVS_RW_ATTRIB(19, active_coeff_a);
-	DCVS_RW_ATTRIB(20, active_coeff_b);
-	DCVS_RW_ATTRIB(21, active_coeff_c);
-	DCVS_RW_ATTRIB(22, leakage_coeff_a);
-	DCVS_RW_ATTRIB(23, leakage_coeff_b);
-	DCVS_RW_ATTRIB(24, leakage_coeff_c);
-	DCVS_RW_ATTRIB(25, leakage_coeff_d);
+	DCVS_RW_ATTRIB(23, freq_tbl);
 
-	core->attrib.attrib_group.attrs[26] = NULL;
+	core->attrib.attrib_group.attrs[24] = NULL;
 
 	core_kobj = kobject_create_and_add(core->core_name, cores_kobj);
 	if (!core_kobj) {
@@ -515,8 +762,6 @@
 	ret = sysfs_create_group(core_kobj, &core->attrib.attrib_group);
 	if (ret)
 		__err("Cannot create core %s attr group\n", core->core_name);
-	else if (msm_dcvs_debug & MSM_DCVS_DEBUG_NOTIFIER)
-		__info("Setting up attributes for core %s\n", core->core_name);
 
 done:
 	if (ret) {
@@ -527,65 +772,95 @@
 	return ret;
 }
 
-/* Return the core if found or add to list if @add_to_list is true */
-static struct dcvs_core *msm_dcvs_get_core(const char *name, int add_to_list)
+static int get_core_offset(enum msm_dcvs_core_type type, int num)
+{
+	int offset = -EINVAL;
+
+	switch (type) {
+	case MSM_DCVS_CORE_TYPE_CPU:
+		offset = CPU_OFFSET + num;
+		BUG_ON(offset >= GPU_OFFSET);
+		break;
+	case MSM_DCVS_CORE_TYPE_GPU:
+		offset = GPU_OFFSET + num;
+		BUG_ON(offset >= CORES_MAX);
+		break;
+	default:
+		BUG();
+	}
+
+	return offset;
+}
+
+/* Return the core and initialize non platform data specific numbers in it */
+static struct dcvs_core *msm_dcvs_add_core(enum msm_dcvs_core_type type,
+								int num)
 {
 	struct dcvs_core *core = NULL;
 	int i;
-	int empty = -1;
+	char name[CORE_NAME_MAX];
 
-	if (!name[0] ||
-		(strnlen(name, CORE_NAME_MAX - 1) == CORE_NAME_MAX - 1))
-		return core;
-
-	mutex_lock(&core_list_lock);
-	for (i = 0; i < CORES_MAX; i++) {
-		core = &core_list[i];
-		if ((empty < 0) && !core->core_name[0]) {
-			empty = i;
-			continue;
-		}
-		if (!strncmp(name, core->core_name, CORE_NAME_MAX))
-			break;
-	}
-
-	/* Check for core_list full */
-	if ((i == CORES_MAX) && (empty < 0)) {
-		mutex_unlock(&core_list_lock);
+	i = get_core_offset(type, num);
+	if (i < 0)
 		return NULL;
-	}
 
-	if (i == CORES_MAX && add_to_list) {
-		core = &core_list[empty];
-		strlcpy(core->core_name, name, CORE_NAME_MAX);
-		mutex_init(&core->lock);
-		spin_lock_init(&core->cpu_lock);
-		core->handle = empty + CORE_HANDLE_OFFSET;
-		hrtimer_init(&core->timer,
-				CLOCK_MONOTONIC, HRTIMER_MODE_REL_PINNED);
-		core->timer.function = msm_dcvs_core_slack_timer;
-	}
-	mutex_unlock(&core_list_lock);
+	if (type == MSM_DCVS_CORE_TYPE_CPU)
+		snprintf(name, CORE_NAME_MAX, "cpu%d", num);
+	else
+		snprintf(name, CORE_NAME_MAX, "gpu%d", num);
 
+	core = &core_list[i];
+	core->dcvs_core_id = i;
+	strlcpy(core->core_name, name, CORE_NAME_MAX);
+	spin_lock_init(&core->pending_freq_lock);
+	spin_lock_init(&core->idle_state_change_lock);
+	hrtimer_init(&core->slack_timer,
+			CLOCK_MONOTONIC, HRTIMER_MODE_REL_PINNED);
+	core->slack_timer.function = msm_dcvs_core_slack_timer;
 	return core;
 }
 
-int msm_dcvs_register_core(const char *core_name,
-		struct msm_dcvs_core_info *info)
+/* Return the core if found or add to list if @add_to_list is true */
+static struct dcvs_core *msm_dcvs_get_core(int offset)
+{
+	/* if the handle is still not set bug */
+	BUG_ON(core_list[offset].dcvs_core_id == -1);
+	return &core_list[offset];
+}
+
+
+int msm_dcvs_register_core(
+	enum msm_dcvs_core_type type,
+	int type_core_num,
+	struct msm_dcvs_core_info *info,
+	int (*set_frequency)(int type_core_num, unsigned int freq),
+	unsigned int (*get_frequency)(int type_core_num),
+	int (*idle_enable)(int type_core_num,
+					enum msm_core_control_event event),
+	int sensor)
 {
 	int ret = -EINVAL;
+	int offset;
 	struct dcvs_core *core = NULL;
 	uint32_t ret1;
 	uint32_t ret2;
 
-	if (!core_name || !core_name[0])
+	offset = get_core_offset(type, type_core_num);
+	if (offset < 0)
 		return ret;
+	if (core_list[offset].dcvs_core_id != -1)
+		return core_list[offset].dcvs_core_id;
 
-	core = msm_dcvs_get_core(core_name, true);
+	core = msm_dcvs_add_core(type, type_core_num);
 	if (!core)
 		return ret;
 
-	mutex_lock(&core->lock);
+	core->type = type;
+	core->type_core_num = type_core_num;
+	core->set_frequency = set_frequency;
+	core->get_frequency = get_frequency;
+	core->idle_enable = idle_enable;
+	core->pending_freq = STOP_FREQ_CHANGE;
 
 	core->info = info;
 	memcpy(&core->algo_param, &info->algo_param,
@@ -594,20 +869,39 @@
 	memcpy(&core->coeffs, &info->energy_coeffs,
 			sizeof(struct msm_dcvs_energy_curve_coeffs));
 
-	ret = msm_dcvs_scm_register_core(core->handle, &info->core_param);
-	if (ret)
-		goto bail;
+	/*
+	 * The tz expects cpu0 to represent bit 0 in the mask, however the
+	 * dcvs_core_id needs to start from 1, dcvs_core_id = 0 is used to
+	 * indicate that this request is not associated with any core.
+	 * mpdecision
+	 */
+	info->core_param.core_bitmask_id
+				= 1 << (core->dcvs_core_id - CPU_OFFSET);
+	core->sensor = sensor;
 
-	ret = msm_dcvs_scm_set_algo_params(core->handle, &info->algo_param);
-	if (ret)
+	ret = msm_dcvs_scm_register_core(core->dcvs_core_id, &info->core_param);
+	if (ret) {
+		__err("%s: scm register core fail handle = %d ret = %d\n",
+					__func__, core->dcvs_core_id, ret);
 		goto bail;
+	}
 
-	ret = msm_dcvs_scm_set_power_params(core->handle, &info->power_param,
+	ret = msm_dcvs_scm_set_algo_params(core->dcvs_core_id,
+							&info->algo_param);
+	if (ret) {
+		__err("%s: scm algo params failed ret = %d\n", __func__, ret);
+		goto bail;
+	}
+
+	ret = msm_dcvs_scm_set_power_params(core->dcvs_core_id,
+				&info->power_param,
 				&info->freq_tbl[0], &core->coeffs);
-	if (ret)
+	if (ret) {
+		__err("%s: scm power params failed ret = %d\n", __func__, ret);
 		goto bail;
+	}
 
-	ret = msm_dcvs_scm_event(core->handle, MSM_DCVS_SCM_CORE_ONLINE,
+	ret = msm_dcvs_scm_event(core->dcvs_core_id, MSM_DCVS_SCM_CORE_ONLINE,
 				core->actual_freq, 0, &ret1, &ret2);
 	if (ret)
 		goto bail;
@@ -615,187 +909,164 @@
 	ret = msm_dcvs_setup_core_sysfs(core);
 	if (ret) {
 		__err("Unable to setup core %s sysfs\n", core->core_name);
-		core_handles[core->handle - CORE_HANDLE_OFFSET] = NULL;
 		goto bail;
 	}
+	core->idle_entered = -1;
+	init_waitqueue_head(&core->wait_q);
+	core->task = kthread_run(msm_dcvs_do_freq, (void *)core,
+			"msm_dcvs/%d", core->dcvs_core_id);
+	ret = core->dcvs_core_id;
 
-bail:
-	mutex_unlock(&core->lock);
+	INIT_DELAYED_WORK(&core->temperature_work, msm_dcvs_report_temp_work);
+	schedule_delayed_work(&core->temperature_work,
+			msecs_to_jiffies(info->thermal_poll_ms));
 	return ret;
+bail:
+	core->dcvs_core_id = -1;
+	return -EINVAL;
 }
 EXPORT_SYMBOL(msm_dcvs_register_core);
 
-int msm_dcvs_freq_sink_register(struct msm_dcvs_freq *drv)
+void msm_dcvs_update_limits(int dcvs_core_id)
+{
+	struct dcvs_core *core;
+
+	if (dcvs_core_id < CPU_OFFSET || dcvs_core_id > CORES_MAX) {
+		__err("%s invalid dcvs_core_id = %d returning -EINVAL\n",
+				__func__, dcvs_core_id);
+		return;
+	}
+
+	core = msm_dcvs_get_core(dcvs_core_id);
+	core->actual_freq = core->get_frequency(core->type_core_num);
+}
+
+int msm_dcvs_freq_sink_start(int dcvs_core_id)
 {
 	int ret = -EINVAL;
 	struct dcvs_core *core = NULL;
 	uint32_t ret1;
-	uint32_t ret2;
+	unsigned long flags;
+	int new_freq;
+	int timer_interval_us;
 
-	if (!drv || !drv->core_name)
-		return ret;
+	if (dcvs_core_id < CPU_OFFSET || dcvs_core_id > CORES_MAX) {
+		__err("%s invalid dcvs_core_id = %d returning -EINVAL\n",
+				__func__, dcvs_core_id);
+		return -EINVAL;
+	}
 
-	core = msm_dcvs_get_core(drv->core_name, true);
+	core = msm_dcvs_get_core(dcvs_core_id);
 	if (!core)
 		return ret;
 
-	mutex_lock(&core->lock);
-	if (core->freq_driver && (msm_dcvs_debug & MSM_DCVS_DEBUG_NOTIFIER))
-		__info("Frequency notifier for %s being replaced\n",
-				core->core_name);
-	core->freq_driver = drv;
-	core->task = kthread_create(msm_dcvs_do_freq, (void *)core,
-			"msm_dcvs/%d", core->handle);
-	if (IS_ERR(core->task)) {
-		mutex_unlock(&core->lock);
-		return -EFAULT;
+	core->actual_freq = core->get_frequency(core->type_core_num);
+
+	spin_lock_irqsave(&core->pending_freq_lock, flags);
+	/* mark that we are ready to accept new frequencies */
+	request_freq_change(core, NO_OUTSTANDING_FREQ_CHANGE);
+	spin_unlock_irqrestore(&core->pending_freq_lock, flags);
+
+	spin_lock_irqsave(&core->idle_state_change_lock, flags);
+	core->idle_entered = -1;
+	spin_unlock_irqrestore(&core->idle_state_change_lock, flags);
+
+	/* Notify TZ to start receiving idle info for the core */
+	ret = msm_dcvs_update_freq(core, MSM_DCVS_SCM_DCVS_ENABLE, 1, &ret1);
+
+	ret = msm_dcvs_scm_event(
+		core->dcvs_core_id, MSM_DCVS_SCM_CORE_ONLINE, core->actual_freq,
+		0, &new_freq, &timer_interval_us);
+	if (ret)
+		__err("Error (%d) DCVS sending online for %s\n",
+				ret, core->core_name);
+
+	if (new_freq != 0) {
+		spin_lock_irqsave(&core->pending_freq_lock, flags);
+		request_freq_change(core, new_freq);
+		spin_unlock_irqrestore(&core->pending_freq_lock, flags);
 	}
+	force_start_slack_timer(core, timer_interval_us);
 
-	if (msm_dcvs_debug & MSM_DCVS_DEBUG_IDLE_PULSE)
-		__info("Enabling idle pulse for %s\n", core->core_name);
 
-	if (core->idle_driver) {
-		core->actual_freq = core->freq_driver->get_frequency(drv);
-		/* Notify TZ to start receiving idle info for the core */
-		ret = msm_dcvs_update_freq(core, MSM_DCVS_SCM_DCVS_ENABLE, 1,
-					   &ret1, &ret2);
-		core->idle_driver->enable(core->idle_driver,
-				MSM_DCVS_ENABLE_IDLE_PULSE);
-	}
-
-	mutex_unlock(&core->lock);
-
-	return core->handle;
+	core->idle_enable(core->type_core_num, MSM_DCVS_ENABLE_IDLE_PULSE);
+	return 0;
 }
-EXPORT_SYMBOL(msm_dcvs_freq_sink_register);
+EXPORT_SYMBOL(msm_dcvs_freq_sink_start);
 
-int msm_dcvs_freq_sink_unregister(struct msm_dcvs_freq *drv)
+int msm_dcvs_freq_sink_stop(int dcvs_core_id)
 {
 	int ret = -EINVAL;
 	struct dcvs_core *core = NULL;
 	uint32_t ret1;
-	uint32_t ret2;
+	uint32_t freq;
+	unsigned long flags;
 
-	if (!drv || !drv->core_name)
-		return ret;
-
-	core = msm_dcvs_get_core(drv->core_name, false);
-	if (!core)
-		return ret;
-
-	mutex_lock(&core->lock);
-	if (msm_dcvs_debug & MSM_DCVS_DEBUG_IDLE_PULSE)
-		__info("Disabling idle pulse for %s\n", core->core_name);
-	if (core->idle_driver) {
-		core->idle_driver->enable(core->idle_driver,
-				MSM_DCVS_DISABLE_IDLE_PULSE);
-		/* Notify TZ to stop receiving idle info for the core */
-		ret = msm_dcvs_update_freq(core, MSM_DCVS_SCM_DCVS_ENABLE, 0,
-					   &ret1, &ret2);
-		hrtimer_cancel(&core->timer);
-		core->idle_driver->enable(core->idle_driver,
-				MSM_DCVS_ENABLE_HIGH_LATENCY_MODES);
-		if (msm_dcvs_debug & MSM_DCVS_DEBUG_IDLE_PULSE)
-			__info("Enabling LPM for %s\n", core->core_name);
+	if (dcvs_core_id < 0 || dcvs_core_id > CORES_MAX) {
+		pr_err("%s invalid dcvs_core_id = %d returning -EINVAL\n",
+				__func__, dcvs_core_id);
+		return -EINVAL;
 	}
-	core->freq_pending = 0;
-	core->freq_driver = NULL;
-	mutex_unlock(&core->lock);
-	kthread_stop(core->task);
+
+	core = msm_dcvs_get_core(dcvs_core_id);
+	if (!core) {
+		__err("couldn't find core for coreid = %d\n", dcvs_core_id);
+		return ret;
+	}
+
+	core->idle_enable(core->type_core_num, MSM_DCVS_DISABLE_IDLE_PULSE);
+	/* Notify TZ to stop receiving idle info for the core */
+	ret = msm_dcvs_scm_event(core->dcvs_core_id, MSM_DCVS_SCM_DCVS_ENABLE,
+				0, core->actual_freq, &freq, &ret1);
+	core->idle_enable(core->type_core_num,
+			MSM_DCVS_ENABLE_HIGH_LATENCY_MODES);
+	spin_lock_irqsave(&core->pending_freq_lock, flags);
+	/* flush out all the pending freq changes */
+	request_freq_change(core, STOP_FREQ_CHANGE);
+	spin_unlock_irqrestore(&core->pending_freq_lock, flags);
+	force_stop_slack_timer(core);
 
 	return 0;
 }
-EXPORT_SYMBOL(msm_dcvs_freq_sink_unregister);
+EXPORT_SYMBOL(msm_dcvs_freq_sink_stop);
 
-int msm_dcvs_idle_source_register(struct msm_dcvs_idle *drv)
-{
-	int ret = -EINVAL;
-	struct dcvs_core *core = NULL;
-
-	if (!drv || !drv->core_name)
-		return ret;
-
-	core = msm_dcvs_get_core(drv->core_name, true);
-	if (!core)
-		return ret;
-
-	mutex_lock(&core->lock);
-	if (core->idle_driver && (msm_dcvs_debug & MSM_DCVS_DEBUG_NOTIFIER))
-		__info("Idle notifier for %s being replaced\n",
-				core->core_name);
-	core->idle_driver = drv;
-	mutex_unlock(&core->lock);
-
-	return core->handle;
-}
-EXPORT_SYMBOL(msm_dcvs_idle_source_register);
-
-int msm_dcvs_idle_source_unregister(struct msm_dcvs_idle *drv)
-{
-	int ret = -EINVAL;
-	struct dcvs_core *core = NULL;
-
-	if (!drv || !drv->core_name)
-		return ret;
-
-	core = msm_dcvs_get_core(drv->core_name, false);
-	if (!core)
-		return ret;
-
-	mutex_lock(&core->lock);
-	core->idle_driver = NULL;
-	mutex_unlock(&core->lock);
-
-	return 0;
-}
-EXPORT_SYMBOL(msm_dcvs_idle_source_unregister);
-
-int msm_dcvs_idle(int handle, enum msm_core_idle_state state, uint32_t iowaited)
+int msm_dcvs_idle(int dcvs_core_id, enum msm_core_idle_state state,
+						uint32_t iowaited)
 {
 	int ret = 0;
 	struct dcvs_core *core = NULL;
 	uint32_t timer_interval_us = 0;
 	uint32_t r0, r1;
-	uint32_t freq_changed = 0;
 
-	if (handle >= CORE_HANDLE_OFFSET &&
-			(handle - CORE_HANDLE_OFFSET) < CORES_MAX)
-		core = &core_list[handle - CORE_HANDLE_OFFSET];
+	if (dcvs_core_id < CPU_OFFSET || dcvs_core_id > CORES_MAX) {
+		pr_err("invalid dcvs_core_id = %d ret -EINVAL\n", dcvs_core_id);
+		return -EINVAL;
+	}
 
-	BUG_ON(!core);
-
-	if (msm_dcvs_debug & MSM_DCVS_DEBUG_IDLE_PULSE)
-		__info("Core %s idle state %d\n", core->core_name, state);
+	core = msm_dcvs_get_core(dcvs_core_id);
 
 	switch (state) {
 	case MSM_DCVS_IDLE_ENTER:
-		hrtimer_cancel(&core->timer);
-		ret = msm_dcvs_scm_event(core->handle,
+		stop_slack_timer(core);
+		ret = msm_dcvs_scm_event(core->dcvs_core_id,
 				MSM_DCVS_SCM_IDLE_ENTER, 0, 0, &r0, &r1);
-		if (ret)
+		if (ret < 0 && ret != -13)
 			__err("Error (%d) sending idle enter for %s\n",
 					ret, core->core_name);
+		trace_msm_dcvs_idle("idle_enter_exit", core->core_name, 1);
 		break;
 
 	case MSM_DCVS_IDLE_EXIT:
-		hrtimer_cancel(&core->timer);
 		ret = msm_dcvs_update_freq(core, MSM_DCVS_SCM_IDLE_EXIT,
-				iowaited, &timer_interval_us, &freq_changed);
+						iowaited, &timer_interval_us);
 		if (ret)
 			__err("Error (%d) sending idle exit for %s\n",
 					ret, core->core_name);
-		/* only start slack timer if change_freq won't */
-		if (freq_changed || core->change_freq_activated)
-			break;
-		if (timer_interval_us && !core->timer_disabled) {
-			ret = hrtimer_start(&core->timer,
-				ktime_set(0, timer_interval_us * 1000),
-				HRTIMER_MODE_REL_PINNED);
-
-			if (ret)
-				__err("Failed to register timer for core %s\n",
-				      core->core_name);
-		}
+		start_slack_timer(core, timer_interval_us);
+		trace_msm_dcvs_idle("idle_enter_exit", core->core_name, 0);
+		trace_msm_dcvs_iowait("iowait", core->core_name, iowaited);
+		trace_msm_dcvs_slack_time("slack_timer_dcvs", core->core_name,
+							timer_interval_us);
 		break;
 	}
 
@@ -830,13 +1101,6 @@
 		goto err;
 	}
 
-	if (!debugfs_create_u32("debug_mask", S_IRUGO | S_IWUSR,
-				debugfs_base, &msm_dcvs_debug)) {
-		__err("Cannot create debugfs entry %s\n", "debug_mask");
-		ret = -ENOMEM;
-		goto err;
-	}
-
 err:
 	if (ret) {
 		kobject_del(cores_kobj);
@@ -851,16 +1115,24 @@
 static int __init msm_dcvs_early_init(void)
 {
 	int ret = 0;
+	int i;
 
 	if (!msm_dcvs_enabled) {
 		__info("Not enabled (%d)\n", msm_dcvs_enabled);
 		return 0;
 	}
 
-	ret = msm_dcvs_scm_init(10 * 1024);
-	if (ret)
-		__err("Unable to initialize DCVS err=%d\n", ret);
 
+	/* Only need about 32kBytes for normal operation */
+	ret = msm_dcvs_scm_init(SZ_32K);
+	if (ret) {
+		__err("Unable to initialize DCVS err=%d\n", ret);
+		goto done;
+	}
+
+	for (i = 0; i < CORES_MAX; i++)
+		core_list[i].dcvs_core_id = -1;
+done:
 	return ret;
 }
 postcore_initcall(msm_dcvs_early_init);
diff --git a/arch/arm/mach-msm/msm_dcvs_idle.c b/arch/arm/mach-msm/msm_dcvs_idle.c
deleted file mode 100644
index 179e170..0000000
--- a/arch/arm/mach-msm/msm_dcvs_idle.c
+++ /dev/null
@@ -1,170 +0,0 @@
-/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- */
-
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/cpu_pm.h>
-#include <linux/platform_device.h>
-#include <linux/pm_qos.h>
-#include <linux/hrtimer.h>
-#include <linux/tick.h>
-#include <mach/msm_dcvs.h>
-
-struct cpu_idle_info {
-	int cpu;
-	int enabled;
-	int handle;
-	struct msm_dcvs_idle dcvs_notifier;
-};
-
-static DEFINE_PER_CPU_SHARED_ALIGNED(struct cpu_idle_info, cpu_idle_info);
-static DEFINE_PER_CPU_SHARED_ALIGNED(u64, iowait_on_cpu);
-static char core_name[NR_CPUS][10];
-static struct pm_qos_request qos_req;
-static uint32_t latency;
-
-static int msm_dcvs_idle_notifier(struct msm_dcvs_idle *self,
-		enum msm_core_control_event event)
-{
-	struct cpu_idle_info *info = container_of(self,
-				struct cpu_idle_info, dcvs_notifier);
-
-	switch (event) {
-	case MSM_DCVS_ENABLE_IDLE_PULSE:
-		info->enabled = true;
-		break;
-
-	case MSM_DCVS_DISABLE_IDLE_PULSE:
-		info->enabled = false;
-		break;
-
-	case MSM_DCVS_ENABLE_HIGH_LATENCY_MODES:
-		pm_qos_update_request(&qos_req, PM_QOS_DEFAULT_VALUE);
-		break;
-
-	case MSM_DCVS_DISABLE_HIGH_LATENCY_MODES:
-		pm_qos_update_request(&qos_req, latency);
-		break;
-	}
-
-	return 0;
-}
-
-static int msm_cpuidle_notifier(struct notifier_block *self, unsigned long cmd,
-		void *v)
-{
-	struct cpu_idle_info *info =
-		&per_cpu(cpu_idle_info, smp_processor_id());
-	u64 io_wait_us = 0;
-	u64 prev_io_wait_us = 0;
-	u64 last_update_time = 0;
-	u64 val = 0;
-	uint32_t iowaited = 0;
-
-	if (!info->enabled)
-		return NOTIFY_OK;
-
-	switch (cmd) {
-	case CPU_PM_ENTER:
-		val = get_cpu_iowait_time_us(smp_processor_id(),
-					&last_update_time);
-		/* val could be -1 when NOHZ is not enabled */
-		if (val == (u64)-1)
-			val = 0;
-		per_cpu(iowait_on_cpu, smp_processor_id()) = val;
-		msm_dcvs_idle(info->handle, MSM_DCVS_IDLE_ENTER, 0);
-		break;
-
-	case CPU_PM_ENTER_FAILED:
-	case CPU_PM_EXIT:
-		prev_io_wait_us = per_cpu(iowait_on_cpu, smp_processor_id());
-		val = get_cpu_iowait_time_us(smp_processor_id(),
-				&last_update_time);
-		if (val == (u64)-1)
-			val = 0;
-		io_wait_us = val;
-		iowaited = (io_wait_us - prev_io_wait_us);
-		msm_dcvs_idle(info->handle, MSM_DCVS_IDLE_EXIT, iowaited);
-		break;
-	}
-
-	return NOTIFY_OK;
-}
-
-static struct notifier_block idle_nb = {
-	.notifier_call = msm_cpuidle_notifier,
-};
-
-static int msm_dcvs_idle_probe(struct platform_device *pdev)
-{
-	int cpu;
-	struct cpu_idle_info *info = NULL;
-	struct msm_dcvs_idle *inotify = NULL;
-
-	for_each_possible_cpu(cpu) {
-		info = &per_cpu(cpu_idle_info, cpu);
-		info->cpu = cpu;
-		inotify = &info->dcvs_notifier;
-		snprintf(core_name[cpu], 10, "cpu%d", cpu);
-		inotify->core_name = core_name[cpu];
-		inotify->enable = msm_dcvs_idle_notifier;
-		info->handle = msm_dcvs_idle_source_register(inotify);
-		BUG_ON(info->handle < 0);
-	}
-
-	latency = *((uint32_t *)pdev->dev.platform_data);
-	pm_qos_add_request(&qos_req, PM_QOS_CPU_DMA_LATENCY,
-				PM_QOS_DEFAULT_VALUE);
-
-	return cpu_pm_register_notifier(&idle_nb);
-}
-
-static int msm_dcvs_idle_remove(struct platform_device *pdev)
-{
-	int ret = 0;
-	int rc = 0;
-	int cpu = 0;
-	struct msm_dcvs_idle *inotify = NULL;
-	struct cpu_idle_info *info = NULL;
-
-	rc = cpu_pm_unregister_notifier(&idle_nb);
-
-	for_each_possible_cpu(cpu) {
-		info = &per_cpu(cpu_idle_info, cpu);
-		inotify = &info->dcvs_notifier;
-		ret = msm_dcvs_idle_source_unregister(inotify);
-		if (ret) {
-			rc = -EFAULT;
-			pr_err("Error de-registering core %d idle notifier.\n",
-					cpu);
-		}
-	}
-
-	return rc;
-}
-
-static struct platform_driver idle_pdrv = {
-	.probe = msm_dcvs_idle_probe,
-	.remove = __devexit_p(msm_dcvs_idle_remove),
-	.driver = {
-		.name  = "msm_cpu_idle",
-		.owner = THIS_MODULE,
-	},
-};
-
-static int msm_dcvs_idle_init(void)
-{
-	return platform_driver_register(&idle_pdrv);
-}
-late_initcall(msm_dcvs_idle_init);
diff --git a/arch/arm/mach-msm/msm_dcvs_scm.c b/arch/arm/mach-msm/msm_dcvs_scm.c
index df6c44f..78d62ac 100644
--- a/arch/arm/mach-msm/msm_dcvs_scm.c
+++ b/arch/arm/mach-msm/msm_dcvs_scm.c
@@ -20,6 +20,7 @@
 #include <mach/memory.h>
 #include <mach/scm.h>
 #include <mach/msm_dcvs_scm.h>
+#include <trace/events/mpdcvs_trace.h>
 
 #define DCVS_CMD_REGISTER_CORE		2
 #define DCVS_CMD_SET_ALGO_PARAM		3
@@ -225,6 +226,9 @@
 	ret = scm_call_atomic4_3(SCM_SVC_DCVS, DCVS_CMD_EVENT,
 			core_id, event_id, param0, param1, ret0, ret1);
 
+	trace_msm_dcvs_scm_event(core_id, (int)event_id, param0, param1,
+							*ret0, *ret1);
+
 	return ret;
 }
 EXPORT_SYMBOL(msm_dcvs_scm_event);
diff --git a/arch/arm/mach-msm/msm_dsps.c b/arch/arm/mach-msm/msm_dsps.c
index c39829b..0551130 100644
--- a/arch/arm/mach-msm/msm_dsps.c
+++ b/arch/arm/mach-msm/msm_dsps.c
@@ -15,8 +15,6 @@
  *
  */
 
-#include <asm/atomic.h>
-
 #include <linux/types.h>
 #include <linux/slab.h>
 #include <linux/module.h>
@@ -34,12 +32,10 @@
 #include <linux/msm_dsps.h>
 
 #include <mach/irqs.h>
-#include <mach/peripheral-loader.h>
 #include <mach/msm_iomap.h>
 #include <mach/msm_smsm.h>
 #include <mach/msm_dsps.h>
 #include <mach/subsystem_restart.h>
-#include <mach/subsystem_notif.h>
 
 #include "ramdump.h"
 #include "timer.h"
@@ -66,8 +62,6 @@
  *  @smem_ramdump_segments - Ramdump segment information for smem
  *  @is_on - DSPS is on.
  *  @ref_count - open/close reference count.
- *  @wdog_irq - DSPS Watchdog IRQ
- *  @crash_in_progress - 1 if crash recovery is in progress
  *  @ppss_base - ppss registers virtual base address.
  */
 struct dsps_drv {
@@ -81,17 +75,9 @@
 
 	void *pil;
 
-	void *dspsfw_ramdump_dev;
-	struct ramdump_segment dspsfw_ramdump_segments[4];
-
-	void *smem_ramdump_dev;
-	struct ramdump_segment smem_ramdump_segments[1];
-
 	int is_on;
 	int ref_count;
-	int wdog_irq;
 
-	atomic_t crash_in_progress;
 	void __iomem *ppss_base;
 };
 
@@ -101,23 +87,15 @@
 static struct dsps_drv *drv;
 
 /**
- * self-initiated shutdown flag
- */
-static int dsps_crash_shutdown_g;
-
-static void dsps_restart_handler(void);
-
-/**
  *  Load DSPS Firmware.
  */
-static int dsps_load(const char *name)
+static int dsps_load(void)
 {
 	pr_debug("%s.\n", __func__);
 
-	drv->pil = pil_get(name);
-
+	drv->pil = subsystem_get("dsps");
 	if (IS_ERR(drv->pil)) {
-		pr_err("%s: fail to load DSPS firmware %s.\n", __func__, name);
+		pr_err("%s: fail to load DSPS firmware.\n", __func__);
 		return -ENODEV;
 	}
 	msleep(20);
@@ -131,7 +109,7 @@
 {
 	pr_debug("%s.\n", __func__);
 
-	pil_put(drv->pil);
+	subsystem_put(drv->pil);
 }
 
 /**
@@ -382,41 +360,6 @@
 }
 
 /**
- *
- * Log subsystem restart failure reason
- */
-static void dsps_log_sfr(void)
-{
-	const char dflt_reason[] = "Died too early due to unknown reason";
-	char *smem_reset_reason;
-	unsigned smem_reset_size;
-
-	smem_reset_reason = smem_get_entry(SMEM_SSR_REASON_DSPS0,
-		&smem_reset_size);
-	if (smem_reset_reason != NULL && smem_reset_reason[0] != 0) {
-		smem_reset_reason[smem_reset_size-1] = 0;
-		pr_err("%s: DSPS failure: %s\nResetting DSPS\n",
-			__func__, smem_reset_reason);
-		memset(smem_reset_reason, 0, smem_reset_size);
-		wmb();
-	} else
-		pr_err("%s: DSPS failure: %s\nResetting DSPS\n",
-			__func__, dflt_reason);
-}
-
-/**
- *  Watchdog interrupt handler
- *
- */
-static irqreturn_t dsps_wdog_bite_irq(int irq, void *dev_id)
-{
-	pr_err("%s\n", __func__);
-	dsps_log_sfr();
-	dsps_restart_handler();
-	return IRQ_HANDLED;
-}
-
-/**
  * IO Control - handle commands from client.
  *
  */
@@ -452,7 +395,7 @@
 	case DSPS_IOCTL_RESET:
 		pr_err("%s: User-initiated DSPS reset.\nResetting DSPS\n",
 		       __func__);
-		dsps_restart_handler();
+		subsystem_restart("dsps");
 		ret = 0;
 		break;
 	default:
@@ -471,7 +414,6 @@
 {
 	int ret = -ENODEV;
 	struct resource *ppss_res;
-	struct resource *ppss_wdog;
 	int i;
 
 	pr_debug("%s.\n", __func__);
@@ -541,58 +483,11 @@
 	drv->ppss_base = ioremap(ppss_res->start,
 				 resource_size(ppss_res));
 
-	ppss_wdog = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
-						"ppss_wdog");
-	if (ppss_wdog) {
-		drv->wdog_irq = ppss_wdog->start;
-		ret = request_irq(drv->wdog_irq, dsps_wdog_bite_irq,
-				  IRQF_TRIGGER_RISING, "dsps_wdog", NULL);
-		if (ret) {
-			pr_err("%s: request_irq fail %d\n", __func__, ret);
-			goto request_irq_err;
-		}
-	} else {
-		drv->wdog_irq = -1;
-		pr_debug("%s: ppss_wdog not supported.\n", __func__);
-	}
-
-	drv->dspsfw_ramdump_segments[0].address = drv->pdata->tcm_code_start;
-	drv->dspsfw_ramdump_segments[0].size =  drv->pdata->tcm_code_size;
-	drv->dspsfw_ramdump_segments[1].address = drv->pdata->tcm_buf_start;
-	drv->dspsfw_ramdump_segments[1].size =  drv->pdata->tcm_buf_size;
-	drv->dspsfw_ramdump_segments[2].address = drv->pdata->pipe_start;
-	drv->dspsfw_ramdump_segments[2].size =  drv->pdata->pipe_size;
-	drv->dspsfw_ramdump_segments[3].address = drv->pdata->ddr_start;
-	drv->dspsfw_ramdump_segments[3].size =  drv->pdata->ddr_size;
-
-	drv->dspsfw_ramdump_dev = create_ramdump_device("dsps");
-	if (!drv->dspsfw_ramdump_dev) {
-		pr_err("%s: create_ramdump_device(\"dsps\") fail\n",
-			      __func__);
-		goto create_ramdump_err;
-	}
-
-	drv->smem_ramdump_segments[0].address = drv->pdata->smem_start;
-	drv->smem_ramdump_segments[0].size =  drv->pdata->smem_size;
-	drv->smem_ramdump_dev = create_ramdump_device("smem-dsps");
-	if (!drv->smem_ramdump_dev) {
-		pr_err("%s: create_ramdump_device(\"smem\") fail\n",
-		       __func__);
-		goto create_ramdump_err;
-	}
-
 	if (drv->pdata->init)
 		drv->pdata->init(drv->pdata);
 
 	return 0;
 
-create_ramdump_err:
-	disable_irq_nosync(drv->wdog_irq);
-	free_irq(drv->wdog_irq, NULL);
-
-request_irq_err:
-	iounmap(drv->ppss_base);
-
 reg_err:
 	for (i = 0; i < drv->pdata->regs_num; i++) {
 		if (drv->pdata->regs[i].reg) {
@@ -634,7 +529,7 @@
 		if (ret)
 			return ret;
 
-		ret = dsps_load(drv->pdata->pil_name);
+		ret = dsps_load();
 
 		if (ret) {
 			dsps_power_off_handler();
@@ -678,8 +573,6 @@
 		}
 	}
 
-	free_irq(drv->wdog_irq, NULL);
-
 	iounmap(drv->ppss_base);
 }
 
@@ -692,7 +585,7 @@
  *
  * If the DSPS is running, then we must reset DSPS CPU & HW before
  * setting the clocks off.
- * The DSPS reset should be done as part of the pil_put().
+ * The DSPS reset should be done as part of the subsystem_put().
  * The DSPS reset should be used for error recovery if the DSPS firmware
  * has crashed and re-loading the firmware is required.
  */
@@ -722,138 +615,6 @@
 	.unlocked_ioctl = dsps_ioctl,
 };
 
-static struct subsys_device *dsps_dev;
-
-/**
- *  Fatal error handler
- *  Resets DSPS.
- */
-static void dsps_restart_handler(void)
-{
-	pr_debug("%s: Restart lvl %d\n",
-		__func__, get_restart_level());
-
-	if (atomic_add_return(1, &drv->crash_in_progress) > 1) {
-		pr_err("%s: DSPS already resetting. Count %d\n", __func__,
-		       atomic_read(&drv->crash_in_progress));
-	} else {
-		subsystem_restart_dev(dsps_dev);
-	}
-}
-
-
-/**
- *  SMSM state change callback
- *
- */
-static void dsps_smsm_state_cb(void *data, uint32_t old_state,
-			       uint32_t new_state)
-{
-	pr_debug("%s\n", __func__);
-	if (dsps_crash_shutdown_g == 1) {
-		pr_debug("%s: SMSM_RESET state change ignored\n",
-			 __func__);
-		dsps_crash_shutdown_g = 0;
-		return;
-	}
-	if (new_state & SMSM_RESET) {
-		dsps_log_sfr();
-		dsps_restart_handler();
-	}
-}
-
-/**
- *  Shutdown function
- * called by the restart notifier
- *
- */
-static int dsps_shutdown(const struct subsys_desc *subsys)
-{
-	pr_debug("%s\n", __func__);
-	disable_irq_nosync(drv->wdog_irq);
-	if (drv->pdata->ppss_wdog_unmasked_int_en_reg) {
-		writel_relaxed(0, (drv->ppss_base+
-			drv->pdata->ppss_wdog_unmasked_int_en_reg));
-		mb(); /* Make sure wdog is disabled before shutting down */
-	}
-	pil_force_shutdown(drv->pdata->pil_name);
-	dsps_power_off_handler();
-	return 0;
-}
-
-/**
- *  Powerup function
- * called by the restart notifier
- *
- */
-static int dsps_powerup(const struct subsys_desc *subsys)
-{
-	pr_debug("%s\n", __func__);
-	dsps_power_on_handler();
-	pil_force_boot(drv->pdata->pil_name);
-	atomic_set(&drv->crash_in_progress, 0);
-	enable_irq(drv->wdog_irq);
-	return 0;
-}
-
-/**
- *  Crash shutdown function
- * called by the restart notifier
- *
- */
-static void dsps_crash_shutdown(const struct subsys_desc *subsys)
-{
-	pr_debug("%s\n", __func__);
-	disable_irq_nosync(drv->wdog_irq);
-	dsps_crash_shutdown_g = 1;
-	smsm_change_state(SMSM_DSPS_STATE, SMSM_RESET, SMSM_RESET);
-}
-
-/**
- *  Ramdump function
- * called by the restart notifier
- *
- */
-static int dsps_ramdump(int enable, const struct subsys_desc *subsys)
-{
-	int ret = 0;
-	pr_debug("%s\n", __func__);
-
-	if (enable) {
-		if (drv->dspsfw_ramdump_dev != NULL) {
-			ret = do_ramdump(drv->dspsfw_ramdump_dev,
-				drv->dspsfw_ramdump_segments,
-				ARRAY_SIZE(drv->dspsfw_ramdump_segments));
-			if (ret < 0) {
-				pr_err("%s: Unable to dump DSPS memory (rc = %d).\n",
-				       __func__, ret);
-				goto dsps_ramdump_out;
-			}
-		}
-		if (drv->smem_ramdump_dev != NULL) {
-			ret = do_ramdump(drv->smem_ramdump_dev,
-				drv->smem_ramdump_segments,
-				ARRAY_SIZE(drv->smem_ramdump_segments));
-			if (ret < 0) {
-				pr_err("%s: Unable to dump smem memory (rc = %d).\n",
-				       __func__, ret);
-				goto dsps_ramdump_out;
-			}
-		}
-	}
-
-dsps_ramdump_out:
-	return ret;
-}
-
-static struct subsys_desc dsps_ssrops = {
-	.name = "dsps",
-	.shutdown = dsps_shutdown,
-	.powerup = dsps_powerup,
-	.ramdump = dsps_ramdump,
-	.crash_shutdown = dsps_crash_shutdown
-};
-
 /**
  * platform driver
  *
@@ -874,8 +635,6 @@
 		pr_err("%s: kzalloc fail.\n", __func__);
 		goto alloc_err;
 	}
-	atomic_set(&drv->crash_in_progress, 0);
-
 	drv->pdata = pdev->dev.platform_data;
 
 	drv->dev_class = class_create(THIS_MODULE, DRV_NAME);
@@ -918,31 +677,8 @@
 		goto cdev_add_err;
 	}
 
-	ret =
-	    smsm_state_cb_register(SMSM_DSPS_STATE, SMSM_RESET,
-				   dsps_smsm_state_cb, 0);
-	if (ret) {
-		pr_err("%s: smsm_state_cb_register fail %d\n", __func__,
-		       ret);
-		goto smsm_register_err;
-	}
-
-	dsps_dev = subsys_register(&dsps_ssrops);
-	if (IS_ERR(dsps_dev)) {
-		ret = PTR_ERR(dsps_dev);
-		pr_err("%s: subsys_register fail %d\n", __func__,
-		       ret);
-		goto ssr_register_err;
-	}
-
 	return 0;
 
-ssr_register_err:
-	smsm_state_cb_deregister(SMSM_DSPS_STATE, SMSM_RESET,
-				 dsps_smsm_state_cb,
-				 0);
-smsm_register_err:
-	cdev_del(drv->cdev);
 cdev_add_err:
 	kfree(drv->cdev);
 cdev_alloc_err:
@@ -962,7 +698,6 @@
 {
 	pr_debug("%s.\n", __func__);
 
-	subsys_unregister(dsps_dev);
 	dsps_power_off_handler();
 	dsps_free_resources();
 
diff --git a/arch/arm/mach-msm/msm_mem_hole.c b/arch/arm/mach-msm/msm_mem_hole.c
new file mode 100644
index 0000000..736219b
--- /dev/null
+++ b/arch/arm/mach-msm/msm_mem_hole.c
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+/**
+ * This module exists for the express purpose of removing memory
+ * via the msm memory-remove mechanism (see
+ * Documentation/devicetree/bindings/arm/msm/memory-reserve.txt). Compiling
+ * this module into a kernel is essentially the means by which any
+ * nodes in the device tree with compatible =
+ * "qcom,msm-mem-hole" will be "activated", thus providing a
+ * convenient mechanism for enabling/disabling memory removal
+ * (qcom,memory-*).
+ */
+
+#include <linux/module.h>
+
+#define MSM_MEM_HOLE_COMPAT_STR	"qcom,msm-mem-hole"
+
+EXPORT_COMPAT(MSM_MEM_HOLE_COMPAT_STR);
diff --git a/arch/arm/mach-msm/msm_mpdecision.c b/arch/arm/mach-msm/msm_mpdecision.c
index 056e4eb..9f6cc11 100644
--- a/arch/arm/mach-msm/msm_mpdecision.c
+++ b/arch/arm/mach-msm/msm_mpdecision.c
@@ -37,6 +37,8 @@
 #include <asm/page.h>
 #include <mach/msm_dcvs.h>
 #include <mach/msm_dcvs_scm.h>
+#define CREATE_TRACE_POINTS
+#include <trace/events/mpdcvs_trace.h>
 
 #define DEFAULT_RQ_AVG_POLL_MS    (1)
 
@@ -168,6 +170,8 @@
 	if (nr > num_present_hundreds)
 		nr = num_present_hundreds;
 
+	trace_msm_mp_runq("nr_running", nr);
+
 	if (ok_to_update_tz(nr, last_nr)) {
 		hrtimer_try_to_cancel(&msm_mpd.slack_timer);
 		msm_mpd.data.nr = nr;
@@ -197,9 +201,6 @@
 		time_taken_ms = ktime_to_ms(ktime_get()) - cpu_action_time_ms;
 		if (time_taken_ms > hp_latencies.hp_up_max_ms)
 			hp_latencies.hp_up_max_ms = time_taken_ms;
-		if (time_taken_ms > 5)
-			pr_warn("cpu_up for cpu%d exceeded 5ms (%d)\n",
-					cpu, time_taken_ms);
 		hp_latencies.hp_up_ms += time_taken_ms;
 		hp_latencies.hp_up_count++;
 		ret = msm_dcvs_scm_event(
@@ -228,9 +229,6 @@
 		time_taken_ms = ktime_to_ms(ktime_get()) - cpu_action_time_ms;
 		if (time_taken_ms > hp_latencies.hp_dw_max_ms)
 			hp_latencies.hp_dw_max_ms = time_taken_ms;
-		if (time_taken_ms > 5)
-			pr_warn("cpu_down for cpu%d exceeded 5ms (%d)\n",
-						cpu, time_taken_ms);
 		hp_latencies.hp_dw_ms += time_taken_ms;
 		hp_latencies.hp_dw_count++;
 		ret = msm_dcvs_scm_event(
@@ -263,6 +261,8 @@
 		return ret;
 	}
 
+	trace_msm_mp_cpusonline("cpu_online_mp", req_cpu_mask);
+	trace_msm_mp_slacktime("slack_time_mp", slack_us);
 	msm_mpd.slack_us = slack_us;
 	atomic_set(&msm_mpd.algo_cpu_mask, req_cpu_mask);
 	msm_mpd.hpupdate = HPUPDATE_SCHEDULED;
@@ -286,6 +286,8 @@
 {
 	unsigned long flags;
 
+	trace_printk("mpd:slack_timer_fired!\n");
+
 	spin_lock_irqsave(&rq_avg_lock, flags);
 	if (msm_mpd.data.event == MSM_DCVS_SCM_RUNQ_UPDATE)
 		goto out;
diff --git a/arch/arm/mach-msm/msm_smem_iface.c b/arch/arm/mach-msm/msm_smem_iface.c
index b09fda5..5ae5772 100644
--- a/arch/arm/mach-msm/msm_smem_iface.c
+++ b/arch/arm/mach-msm/msm_smem_iface.c
@@ -41,4 +41,6 @@
 	cpr_info->ring_osc = temp_cpr_info->ring_osc;
 	cpr_info->turbo_quot = temp_cpr_info->turbo_quot;
 	cpr_info->pvs_fuse = temp_cpr_info->pvs_fuse;
+	cpr_info->floor_fuse = temp_cpr_info->floor_fuse;
+	cpr_info->disable_cpr = temp_cpr_info->disable_cpr;
 }
diff --git a/arch/arm/mach-msm/msm_smem_iface.h b/arch/arm/mach-msm/msm_smem_iface.h
index 2da0232..2daf76d 100644
--- a/arch/arm/mach-msm/msm_smem_iface.h
+++ b/arch/arm/mach-msm/msm_smem_iface.h
@@ -16,7 +16,6 @@
 #define __ARCH_ARM_MACH_MSM_SMEM_IFACE_H
 
 #include <mach/msm_smsm.h>
-#include "smd_private.h"
 
 #define MAX_KEY_EVENTS 10
 #define MAX_SEC_KEY_PAYLOAD 32
@@ -33,10 +32,13 @@
 	uint8_t  iv[MAX_SEC_KEY_PAYLOAD]; /* Initialization Vector */
 };
 
+/* floor_fuse to re-use the fuse bit earlier used by ring_osc */
 struct cpr_info_type {
-	uint8_t ring_osc;         /* CPR FUSE [0]: TURBO RO SEL BIT */
-	uint8_t turbo_quot;        /* CPRFUSE[1:7] : TURBO QUOT*/
-	uint8_t pvs_fuse;         /* TURBO PVS FUSE */
+	uint8_t ring_osc;   /* CPR FUSE [0]: TURBO RO SEL BIT */
+	uint8_t turbo_quot; /* CPRFUSE[1:7] : TURBO QUOT*/
+	uint8_t pvs_fuse;   /* TURBO PVS FUSE */
+	uint8_t floor_fuse; /* Vmin Selection. b1: FAB_ID(2), b0: CPR_fuse[0] */
+	bool    disable_cpr;
 };
 
 struct boot_info_for_apps {
@@ -47,7 +49,7 @@
 	uint16_t boot_keys_pressed[MAX_KEY_EVENTS]; /* Log of key presses */
 	uint32_t timetick; /* Modem tick timer value before apps out of reset */
 	struct cpr_info_type cpr_info;
-	uint8_t PAD[25];
+	uint8_t PAD[23];
 };
 
 void msm_smem_get_cpr_info(struct cpr_info_type *cpr_info);
diff --git a/arch/arm/mach-msm/msm_watchdog.h b/arch/arm/mach-msm/msm_watchdog.h
index 5fb82ee..7bf97d9 100644
--- a/arch/arm/mach-msm/msm_watchdog.h
+++ b/arch/arm/mach-msm/msm_watchdog.h
@@ -72,6 +72,7 @@
 
 void msm_wdog_fiq_setup(void *stack);
 extern unsigned int msm_wdog_fiq_length, msm_wdog_fiq_start;
+extern unsigned int msm7k_fiq_start, msm7k_fiq_length;
 
 #ifdef CONFIG_MSM_WATCHDOG
 void pet_watchdog(void);
diff --git a/arch/arm/mach-msm/msm_watchdog_v2.c b/arch/arm/mach-msm/msm_watchdog_v2.c
index 48a57cd..f88c611 100644
--- a/arch/arm/mach-msm/msm_watchdog_v2.c
+++ b/arch/arm/mach-msm/msm_watchdog_v2.c
@@ -228,13 +228,19 @@
 
 static void pet_watchdog(struct msm_watchdog_data *wdog_dd)
 {
-	int slack;
+	int slack, i, count, prev_count = 0;
 	unsigned long long time_ns;
 	unsigned long long slack_ns;
 	unsigned long long bark_time_ns = wdog_dd->bark_time * 1000000ULL;
 
-	slack = __raw_readl(wdog_dd->base + WDT0_STS) >> 3;
-	slack = ((wdog_dd->bark_time*WDT_HZ)/1000) - slack;
+	for (i = 0; i < 2; i++) {
+		count = (__raw_readl(wdog_dd->base + WDT0_STS) >> 1) & 0xFFFFF;
+		if (count != prev_count) {
+			prev_count = count;
+			i = 0;
+		}
+	}
+	slack = ((wdog_dd->bark_time * WDT_HZ) / 1000) - count;
 	if (slack < wdog_dd->min_slack_ticks)
 		wdog_dd->min_slack_ticks = slack;
 	__raw_writel(1, wdog_dd->base + WDT0_RST);
diff --git a/arch/arm/mach-msm/msm_xo.c b/arch/arm/mach-msm/msm_xo.c
index 404b350..46d4a12 100644
--- a/arch/arm/mach-msm/msm_xo.c
+++ b/arch/arm/mach-msm/msm_xo.c
@@ -233,10 +233,9 @@
 	int ret;
 	struct msm_xo *xo = xo_voter->xo;
 	int is_d0 = xo == &msm_xo_sources[MSM_XO_TCXO_D0];
-	int needs_workaround = cpu_is_msm8960() || cpu_is_apq8064() ||
-			       cpu_is_msm8930() || cpu_is_msm8930aa() ||
-			       cpu_is_msm9615() || cpu_is_msm8627() ||
-			       cpu_is_msm8960ab() || cpu_is_apq8064ab();
+	int needs_workaround = soc_class_is_msm8960() ||
+			       soc_class_is_apq8064() ||
+			       soc_class_is_msm8930() || cpu_is_msm9615();
 
 	if (xo_voter->mode == mode)
 		return 0;
diff --git a/arch/arm/mach-msm/ocmem.c b/arch/arm/mach-msm/ocmem.c
index 793fcc5..7829d8d 100644
--- a/arch/arm/mach-msm/ocmem.c
+++ b/arch/arm/mach-msm/ocmem.c
@@ -82,6 +82,8 @@
 	"Transfer failures",
 	"Evictions",
 	"Restorations",
+	"Dump requests",
+	"Dump completed",
 };
 
 struct ocmem_quota_table {
diff --git a/arch/arm/mach-msm/ocmem_api.c b/arch/arm/mach-msm/ocmem_api.c
index 6e094fd..689e015 100644
--- a/arch/arm/mach-msm/ocmem_api.c
+++ b/arch/arm/mach-msm/ocmem_api.c
@@ -399,6 +399,36 @@
 }
 EXPORT_SYMBOL(ocmem_unmap);
 
+int ocmem_dump(int client_id, struct ocmem_buf *buffer,
+			unsigned long dst_phys_addr)
+{
+	int ret = 0;
+	struct ocmem_handle *handle = NULL;
+
+	if (!check_id(client_id)) {
+		pr_err("ocmem: Invalid client id: %d\n", client_id);
+		return -EINVAL;
+	}
+
+	if (!zone_active(client_id)) {
+		pr_err("ocmem: Client id: %s (id: %d) not allowed to use OCMEM\n",
+					get_name(client_id), client_id);
+		return -EINVAL;
+	}
+
+	if (!buffer) {
+		pr_err("ocmem: Invalid buffer\n");
+		return -EINVAL;
+	}
+
+	handle = buffer_to_handle(buffer);
+	mutex_lock(&handle->handle_mutex);
+	ret = process_dump(client_id, handle, dst_phys_addr);
+	mutex_unlock(&handle->handle_mutex);
+	return ret;
+}
+EXPORT_SYMBOL(ocmem_dump);
+
 unsigned long get_max_quota(int client_id)
 {
 	if (!check_id(client_id)) {
diff --git a/arch/arm/mach-msm/ocmem_core.c b/arch/arm/mach-msm/ocmem_core.c
index 9a85a17..3d9639f 100644
--- a/arch/arm/mach-msm/ocmem_core.c
+++ b/arch/arm/mach-msm/ocmem_core.c
@@ -404,7 +404,7 @@
 	if (offset < 0)
 		return -EINVAL;
 
-	if (len < region_size)
+	if (len < OCMEM_MIN_ALLOC)
 		return -EINVAL;
 
 	pr_debug("ocmem: mode_transistion to %x\n", new_mode);
@@ -425,7 +425,7 @@
 			/* Set the region to its new mode */
 			region->mode = new_mode;
 			atomic_inc(&region->mode_counter);
-			pr_debug("Region  (%d) switching to mode %d\n",
+			pr_debug("Region (%d) switching to mode %d\n",
 					i, new_mode);
 			continue;
 		} else if (region->mode != new_mode) {
@@ -856,7 +856,7 @@
 /* Interfaces invoked from the scheduler */
 int ocmem_memory_off(int id, unsigned long offset, unsigned long len)
 {
-	return switch_power_state(id, offset, len, REGION_DEFAULT_ON);
+	return switch_power_state(id, offset, len, REGION_DEFAULT_OFF);
 }
 
 int ocmem_memory_on(int id, unsigned long offset, unsigned long len)
@@ -866,7 +866,7 @@
 
 int ocmem_memory_retain(int id, unsigned long offset, unsigned long len)
 {
-	return switch_power_state(id, offset, len, REGION_DEFAULT_ON);
+	return switch_power_state(id, offset, len, REGION_DEFAULT_RETENTION);
 }
 
 static int ocmem_power_show_sw_state(struct seq_file *f, void *dummy)
diff --git a/arch/arm/mach-msm/ocmem_rdm.c b/arch/arm/mach-msm/ocmem_rdm.c
index 4aba69c..818a20a 100644
--- a/arch/arm/mach-msm/ocmem_rdm.c
+++ b/arch/arm/mach-msm/ocmem_rdm.c
@@ -58,6 +58,7 @@
 #define BR_CLIENT_n_IDX(x) ((x) * 0x4)
 #define BR_CLIENT_n_ctrl(x) (BR_CLIENT_BASE + (BR_CLIENT_n_IDX(x)))
 #define BR_STATUS (0x14)
+#define BR_LAST_ADDR (0x18)
 /* 16 entries per client are supported */
 /* Use entries 0 - 15 for client0 */
 #define BR_CLIENT0_MASK	(0x1000)
@@ -76,7 +77,7 @@
 #define BR_TBL_ENTRY_ENABLE 0x1
 #define BR_TBL_START 0x0
 #define BR_TBL_END 0x8
-#define BR_RW_SHIFT 0x2
+#define BR_RW_SHIFT 0x1
 
 #define DM_TBL_START 0x10
 #define DM_TBL_END 0x18
@@ -134,13 +135,13 @@
 	pr_debug("irq:dm_status %x irq_status %x\n", status, irq_status);
 	if (irq_status & BIT(0)) {
 		pr_debug("Data mover completed\n");
-		irq_status &= ~BIT(0);
-		ocmem_write(irq_status, dm_base + DM_INTR_CLR);
+		ocmem_write(BIT(0), dm_base + DM_INTR_CLR);
+		pr_debug("Last re-mapped address block %x\n",
+				ocmem_read(br_base + BR_LAST_ADDR));
 		complete(&dm_transfer_event);
 	} else if (irq_status & BIT(1)) {
 		pr_debug("Data clear engine completed\n");
-		irq_status &= ~BIT(1);
-		ocmem_write(irq_status, dm_base + DM_INTR_CLR);
+		ocmem_write(BIT(1), dm_base + DM_INTR_CLR);
 		complete(&dm_clear_event);
 	} else {
 		BUG_ON(1);
@@ -259,6 +260,7 @@
 	pr_debug("ocmem: rdm: dm_ctrl %x br_ctrl %x\n", dm_ctrl, br_ctrl);
 
 	wait_for_completion(&dm_transfer_event);
+	pr_debug("Completed transferring %d segments\n", num_chunks);
 	ocmem_disable_core_clock();
 	return 0;
 }
diff --git a/arch/arm/mach-msm/ocmem_sched.c b/arch/arm/mach-msm/ocmem_sched.c
index e8854d5..37dec30 100644
--- a/arch/arm/mach-msm/ocmem_sched.c
+++ b/arch/arm/mach-msm/ocmem_sched.c
@@ -45,6 +45,7 @@
 	OP_COMPLETE = 0x0,
 	OP_RESCHED,
 	OP_PARTIAL,
+	OP_EVICT,
 	OP_FAIL = ~0x0,
 };
 
@@ -65,6 +66,7 @@
 	MAX_OCMEM_PRIO = PRIO_OCMEM + 1,
 };
 
+static void __iomem *ocmem_vaddr;
 static struct list_head sched_queue[MAX_OCMEM_PRIO];
 static struct mutex sched_queue_mutex;
 
@@ -73,7 +75,8 @@
  * hardware state changes can occur. The value will be tweaked on actual
  * hardware.
 */
-#define SCHED_DELAY 10
+/* Delay in ms for switching to low power mode for OCMEM */
+#define SCHED_DELAY 5000
 
 static struct list_head rdm_queue;
 static struct mutex rdm_mutex;
@@ -116,7 +119,7 @@
 	int hw_interconnect;
 } ocmem_client_table[OCMEM_CLIENT_MAX] = {
 	{OCMEM_GRAPHICS, PRIO_GFX, OCMEM_PERFORMANCE, OCMEM_PORT},
-	{OCMEM_VIDEO, PRIO_VIDEO, OCMEM_PERFORMANCE, OCMEM_PORT},
+	{OCMEM_VIDEO, PRIO_VIDEO, OCMEM_PERFORMANCE, OCMEM_OCMEMNOC},
 	{OCMEM_CAMERA, NO_PRIO, OCMEM_PERFORMANCE, OCMEM_OCMEMNOC},
 	{OCMEM_HP_AUDIO, PRIO_HP_AUDIO, OCMEM_PASSIVE, OCMEM_BLOCKED},
 	{OCMEM_VOICE, PRIO_VOICE, OCMEM_PASSIVE, OCMEM_BLOCKED},
@@ -127,6 +130,7 @@
 
 static struct rb_root sched_tree;
 static struct mutex sched_mutex;
+static struct mutex allocation_mutex;
 
 /* A region represents a continuous interval in OCMEM address space */
 struct ocmem_region {
@@ -153,6 +157,16 @@
 		return 0;
 }
 
+static inline int is_iface_access(int id)
+{
+	return ocmem_client_table[id].hw_interconnect == OCMEM_OCMEMNOC ? 1 : 0;
+}
+
+static inline int is_remapped_access(int id)
+{
+	return ocmem_client_table[id].hw_interconnect == OCMEM_SYSNOC ? 1 : 0;
+}
+
 static inline int is_blocked(int id)
 {
 	return ocmem_client_table[id].hw_interconnect == OCMEM_BLOCKED ? 1 : 0;
@@ -219,9 +233,9 @@
 
 	switch (hw_interconnect) {
 	case OCMEM_PORT:
+	case OCMEM_OCMEMNOC:
 		ret_addr = phys_to_offset(addr);
 		break;
-	case OCMEM_OCMEMNOC:
 	case OCMEM_SYSNOC:
 		ret_addr = addr;
 		break;
@@ -240,9 +254,9 @@
 
 	switch (hw_interconnect) {
 	case OCMEM_PORT:
+	case OCMEM_OCMEMNOC:
 		ret_addr = offset_to_phys(addr);
 		break;
-	case OCMEM_OCMEMNOC:
 	case OCMEM_SYSNOC:
 		ret_addr = addr;
 		break;
@@ -306,6 +320,7 @@
 	INIT_LIST_HEAD(&p->sched_list);
 	init_rwsem(&p->rw_sem);
 	SET_STATE(p, R_FREE);
+	pr_debug("request %p created\n", p);
 	return p;
 }
 
@@ -583,15 +598,29 @@
 	if (rc < 0)
 		goto core_clock_fail;
 
-	rc = ocmem_enable_iface_clock();
 
-	if (rc < 0)
-		goto iface_clock_fail;
+	if (is_iface_access(req->owner)) {
+		rc = ocmem_enable_iface_clock();
 
-	rc = ocmem_enable_br_clock();
+		if (rc < 0)
+			goto iface_clock_fail;
+	}
 
-	if (rc < 0)
-		goto br_clock_fail;
+	if (is_remapped_access(req->owner)) {
+		rc = ocmem_enable_br_clock();
+
+		if (rc < 0)
+			goto br_clock_fail;
+	}
+
+	rc = ocmem_lock(req->owner, phys_to_offset(req->req_start), req->req_sz,
+							get_mode(req->owner));
+
+	if (rc < 0) {
+		pr_err("ocmem: Failed to secure request %p for %d\n", req,
+				req->owner);
+		goto lock_failed;
+	}
 
 	rc = do_map(req);
 
@@ -601,22 +630,17 @@
 		goto process_map_fail;
 
 	}
-
-	if (ocmem_lock(req->owner, phys_to_offset(req->req_start), req->req_sz,
-							get_mode(req->owner))) {
-		pr_err("ocmem: Failed to secure request %p for %d\n", req,
-				req->owner);
-		rc = -EINVAL;
-		goto lock_failed;
-	}
-
+	pr_debug("ocmem: Mapped request %p\n", req);
 	return 0;
-lock_failed:
-	do_unmap(req);
+
 process_map_fail:
-	ocmem_disable_br_clock();
+	ocmem_unlock(req->owner, phys_to_offset(req->req_start), req->req_sz);
+lock_failed:
+	if (is_remapped_access(req->owner))
+		ocmem_disable_br_clock();
 br_clock_fail:
-	ocmem_disable_iface_clock();
+	if (is_iface_access(req->owner))
+		ocmem_disable_iface_clock();
 iface_clock_fail:
 	ocmem_disable_core_clock();
 core_clock_fail:
@@ -629,22 +653,26 @@
 {
 	int rc = 0;
 
-	if (ocmem_unlock(req->owner, phys_to_offset(req->req_start),
-							req->req_sz)) {
-		pr_err("ocmem: Failed to un-secure request %p for %d\n", req,
-				req->owner);
-		rc = -EINVAL;
-		goto unlock_failed;
-	}
-
 	rc = do_unmap(req);
 
 	if (rc < 0)
 		goto process_unmap_fail;
 
-	ocmem_disable_br_clock();
-	ocmem_disable_iface_clock();
+	rc = ocmem_unlock(req->owner, phys_to_offset(req->req_start),
+						req->req_sz);
+
+	if (rc < 0) {
+		pr_err("ocmem: Failed to un-secure request %p for %d\n", req,
+				req->owner);
+		goto unlock_failed;
+	}
+
+	if (is_remapped_access(req->owner))
+		ocmem_disable_br_clock();
+	if (is_iface_access(req->owner))
+		ocmem_disable_iface_clock();
 	ocmem_disable_core_clock();
+	pr_debug("ocmem: Unmapped request %p\n", req);
 	return 0;
 
 unlock_failed:
@@ -1008,7 +1036,8 @@
 
 	retry = false;
 
-	pr_debug("ocmem: ALLOCATE: request size %lx\n", sz);
+	pr_debug("ocmem: do_allocate: %s request size %lx\n",
+						get_name(owner), sz);
 
 retry_next_step:
 
@@ -1035,6 +1064,7 @@
 
 		/* update request state */
 		CLEAR_STATE(req, R_FREE);
+		CLEAR_STATE(req, R_PENDING);
 		SET_STATE(req, R_ALLOCATED);
 		SET_STATE(req, R_MUST_MAP);
 		req->op = SCHED_NOP;
@@ -1064,8 +1094,11 @@
 		/* resolve conflicting regions based on priority */
 		if (overlap_r->max_prio < prio) {
 			if (min == max) {
-				pr_err("ocmem: Requires eviction support\n");
-				goto err_not_supported;
+				req->req_start = zone->z_head;
+				req->req_end = zone->z_head + sz - 1;
+				req->req_sz = 0x0;
+				req->edata = NULL;
+				goto trigger_eviction;
 			} else {
 			/* Try to allocate atleast >= 'min' immediately */
 				sz -= step;
@@ -1108,6 +1141,11 @@
 
 	return OP_COMPLETE;
 
+trigger_eviction:
+	pr_debug("Trigger eviction of region %p\n", overlap_r);
+	destroy_region(region);
+	return OP_EVICT;
+
 err_not_supported:
 	pr_err("ocmem: Scheduled unsupported operation\n");
 	return OP_FAIL;
@@ -1133,6 +1171,37 @@
 	return 0;
 }
 
+static void sched_dequeue(struct ocmem_req *victim_req)
+{
+	struct ocmem_req *req = NULL;
+	struct ocmem_req *next = NULL;
+	int id;
+
+	if (!victim_req)
+		return;
+
+	id = victim_req->owner;
+
+	mutex_lock(&sched_queue_mutex);
+
+	if (list_empty(&sched_queue[id]))
+		goto dequeue_done;
+
+	list_for_each_entry_safe(req, next, &sched_queue[id], sched_list)
+	{
+		if (req == victim_req) {
+			pr_debug("ocmem: Cancelling pending request %p\n",
+							req);
+			list_del(&req->sched_list);
+			goto dequeue_done;
+		}
+	}
+
+dequeue_done:
+	mutex_unlock(&sched_queue_mutex);
+	return;
+}
+
 static struct ocmem_req *ocmem_fetch_req(void)
 {
 	int i;
@@ -1218,12 +1287,9 @@
 	if (rc < 0)
 		return -EINVAL;
 
-	/* Map the newly grown region */
-	if (is_tcm(req->owner)) {
-		rc = process_map(req, req->req_start, req->req_end);
-		if (rc < 0)
-			return -EINVAL;
-	}
+	rc = process_map(req, req->req_start, req->req_end);
+	if (rc < 0)
+		return -EINVAL;
 
 	offset = phys_to_offset(req->req_start);
 
@@ -1280,8 +1346,23 @@
 
 static int ocmem_schedule_pending(void)
 {
-	schedule_delayed_work(&ocmem_sched_thread,
-				msecs_to_jiffies(SCHED_DELAY));
+
+	bool need_sched = false;
+	int i = 0;
+
+	for (i = MIN_PRIO; i < MAX_OCMEM_PRIO; i++) {
+		if (!list_empty(&sched_queue[i])) {
+			need_sched = true;
+			break;
+		}
+	}
+
+	if (need_sched == true) {
+		cancel_delayed_work(&ocmem_sched_thread);
+		schedule_delayed_work(&ocmem_sched_thread,
+					msecs_to_jiffies(SCHED_DELAY));
+		pr_debug("ocmem: Scheduled delayed work\n");
+	}
 	return 0;
 }
 
@@ -1297,6 +1378,8 @@
 		goto err_free_fail;
 	}
 
+	pr_debug("ocmem: do_free: client %s req %p\n", get_name(req->owner),
+					req);
 	/* Grab the sched mutex */
 	mutex_lock(&sched_mutex);
 	rc = __sched_free(req);
@@ -1345,10 +1428,19 @@
 		return -EINVAL;
 	}
 
-	if (is_tcm(req->owner)) {
+	mutex_lock(&sched_mutex);
+	sched_dequeue(req);
+	mutex_unlock(&sched_mutex);
+
+	if (!TEST_STATE(req, R_FREE)) {
+
 		rc = process_unmap(req, req->req_start, req->req_end);
 		if (rc < 0)
 			return -EINVAL;
+
+		rc = do_free(req);
+		if (rc < 0)
+			return -EINVAL;
 	}
 
 	if (req->req_sz != 0) {
@@ -1364,10 +1456,6 @@
 
 	}
 
-	rc = do_free(req);
-	if (rc < 0)
-		return -EINVAL;
-
 	inc_ocmem_stat(zone_of(req), NR_FREES);
 
 	ocmem_destroy_req(req);
@@ -1436,18 +1524,10 @@
 		return -EINVAL;
 
 	if (!is_mapped(req)) {
-		pr_err("Buffer is not already mapped\n");
+		pr_err("Buffer is not currently mapped\n");
 		goto transfer_out_error;
 	}
 
-	rc = process_unmap(req, req->req_start, req->req_end);
-	if (rc < 0) {
-		pr_err("Unmapping the buffer failed\n");
-		goto transfer_out_error;
-	}
-
-	inc_ocmem_stat(zone_of(req), NR_TRANSFERS_TO_DDR);
-
 	rc = queue_transfer(req, handle, list, TO_DDR);
 
 	if (rc < 0) {
@@ -1456,6 +1536,7 @@
 		goto transfer_out_error;
 	}
 
+	inc_ocmem_stat(zone_of(req), NR_TRANSFERS_TO_DDR);
 	return 0;
 
 transfer_out_error:
@@ -1473,19 +1554,14 @@
 	if (!req)
 		return -EINVAL;
 
-	if (is_mapped(req)) {
-		pr_err("Buffer is already mapped\n");
+
+	if (!is_mapped(req)) {
+		pr_err("Buffer is not already mapped for transfer\n");
 		goto transfer_in_error;
 	}
 
-	rc = process_map(req, req->req_start, req->req_end);
-	if (rc < 0) {
-		pr_err("Mapping the buffer failed\n");
-		goto transfer_in_error;
-	}
 
 	inc_ocmem_stat(zone_of(req), NR_TRANSFERS_TO_OCMEM);
-
 	rc = queue_transfer(req, handle, list, TO_OCMEM);
 
 	if (rc < 0) {
@@ -1524,13 +1600,21 @@
 
 	edata = req->edata;
 
-	if (is_tcm(req->owner))
-		do_unmap(req);
+	if (!edata) {
+		pr_err("Unable to find eviction data\n");
+		return -EINVAL;
+	}
+
+	pr_debug("Found edata %p in request %p\n", edata, req);
 
 	inc_ocmem_stat(zone_of(req), NR_SHRINKS);
 
 	if (size == 0) {
-		pr_info("req %p being shrunk to zero\n", req);
+		pr_debug("req %p being shrunk to zero\n", req);
+		if (is_mapped(req))
+			rc = process_unmap(req, req->req_start, req->req_end);
+			if (rc < 0)
+				return -EINVAL;
 		rc = do_free(req);
 		if (rc < 0)
 			return -EINVAL;
@@ -1540,9 +1624,12 @@
 			return -EINVAL;
 	}
 
-	edata->pending--;
-	if (edata->pending == 0) {
-		pr_debug("All regions evicted");
+	req->edata = NULL;
+	CLEAR_STATE(req, R_ALLOCATED);
+	SET_STATE(req, R_FREE);
+
+	if (atomic_dec_and_test(&edata->pending)) {
+		pr_debug("ocmem: All conflicting allocations were shrunk\n");
 		complete(&edata->completion);
 	}
 
@@ -1566,82 +1653,313 @@
 	return rc;
 }
 
-int ocmem_eviction_thread(struct work_struct *work)
+static struct ocmem_eviction_data *init_eviction(int id)
 {
+	struct ocmem_eviction_data *edata = NULL;
+	int prio = ocmem_client_table[id].priority;
+
+	edata = kzalloc(sizeof(struct ocmem_eviction_data), GFP_ATOMIC);
+
+	if (!edata) {
+		pr_err("ocmem: Could not allocate eviction data\n");
+		return NULL;
+	}
+
+	INIT_LIST_HEAD(&edata->victim_list);
+	INIT_LIST_HEAD(&edata->req_list);
+	edata->prio = prio;
+	atomic_set(&edata->pending, 0);
+	return edata;
+}
+
+static void free_eviction(struct ocmem_eviction_data *edata)
+{
+
+	if (!edata)
+		return;
+
+	if (!list_empty(&edata->req_list))
+		pr_err("ocmem: Eviction data %p not empty\n", edata);
+
+	kfree(edata);
+	edata = NULL;
+}
+
+static bool is_overlapping(struct ocmem_req *new, struct ocmem_req *old)
+{
+
+	if (!new || !old)
+		return false;
+
+	pr_debug("check overlap [%lx -- %lx] on [%lx -- %lx]\n",
+			new->req_start, new->req_end,
+			old->req_start, old->req_end);
+
+	if ((new->req_start < old->req_start &&
+		new->req_end >= old->req_start) ||
+		(new->req_start >= old->req_start &&
+		 new->req_start <= old->req_end &&
+		 new->req_end >= old->req_end)) {
+		pr_debug("request %p overlaps with existing req %p\n",
+						new, old);
+		return true;
+	}
+	return false;
+}
+
+static int __evict_common(struct ocmem_eviction_data *edata,
+						struct ocmem_req *req)
+{
+	struct rb_node *rb_node = NULL;
+	struct ocmem_req *e_req = NULL;
+	bool needs_eviction = false;
+	int j = 0;
+
+	for (rb_node = rb_first(&sched_tree); rb_node;
+			rb_node = rb_next(rb_node)) {
+
+		struct ocmem_region *tmp_region = NULL;
+
+		tmp_region = rb_entry(rb_node, struct ocmem_region, region_rb);
+
+		if (tmp_region->max_prio < edata->prio) {
+			for (j = edata->prio - 1; j > NO_PRIO; j--) {
+				needs_eviction = false;
+				e_req = find_req_match(j, tmp_region);
+				if (!e_req)
+					continue;
+				if (edata->passive == true) {
+					needs_eviction = true;
+				} else {
+					needs_eviction = is_overlapping(req,
+								e_req);
+				}
+
+				if (needs_eviction) {
+					pr_debug("adding %p in region %p to eviction list\n",
+							e_req, tmp_region);
+					list_add_tail(
+						&e_req->eviction_list,
+						&edata->req_list);
+					atomic_inc(&edata->pending);
+					e_req->edata = edata;
+				}
+			}
+		} else {
+			pr_debug("Skipped region %p\n", tmp_region);
+		}
+	}
+
+	pr_debug("%d requests will be evicted\n", atomic_read(&edata->pending));
+
+	if (!atomic_read(&edata->pending))
+		return -EINVAL;
 	return 0;
 }
 
+static void trigger_eviction(struct ocmem_eviction_data *edata)
+{
+	struct ocmem_req *req = NULL;
+	struct ocmem_req *next = NULL;
+	struct ocmem_buf buffer;
+
+	if (!edata)
+		return;
+
+	BUG_ON(atomic_read(&edata->pending) == 0);
+
+	init_completion(&edata->completion);
+
+	list_for_each_entry_safe(req, next, &edata->req_list, eviction_list)
+	{
+		if (req) {
+			pr_debug("ocmem: Evicting request %p\n", req);
+			buffer.addr = req->req_start;
+			buffer.len = 0x0;
+			dispatch_notification(req->owner, OCMEM_ALLOC_SHRINK,
+								&buffer);
+		}
+	}
+	return;
+}
+
 int process_evict(int id)
 {
 	struct ocmem_eviction_data *edata = NULL;
-	int prio = ocmem_client_table[id].priority;
-	struct rb_node *rb_node = NULL;
-	struct ocmem_req *req = NULL;
-	struct ocmem_buf buffer;
-	int j = 0;
+	int rc = 0;
 
-	edata = kzalloc(sizeof(struct ocmem_eviction_data), GFP_ATOMIC);
+	edata = init_eviction(id);
 
-	INIT_LIST_HEAD(&edata->victim_list);
-	INIT_LIST_HEAD(&edata->req_list);
-	edata->prio = prio;
-	edata->pending = 0;
-	edata->passive = 1;
-	evictions[id] = edata;
+	if (!edata)
+		return -EINVAL;
+
+	edata->passive = true;
 
 	mutex_lock(&sched_mutex);
 
-	for (rb_node = rb_first(&sched_tree); rb_node;
-				rb_node = rb_next(rb_node)) {
-		struct ocmem_region *tmp_region = NULL;
-		tmp_region = rb_entry(rb_node, struct ocmem_region, region_rb);
-		if (tmp_region->max_prio < prio) {
-			for (j = id - 1; j > NO_PRIO; j--) {
-				req = find_req_match(j, tmp_region);
-				if (req) {
-					pr_info("adding %p to eviction list\n",
-							tmp_region);
-					list_add_tail(
-						&tmp_region->eviction_list,
-						&edata->victim_list);
-					list_add_tail(
-						&req->eviction_list,
-						&edata->req_list);
-					edata->pending++;
-					req->edata = edata;
-					buffer.addr = req->req_start;
-					buffer.len = 0x0;
-					inc_ocmem_stat(zone_of(req),
-								NR_EVICTIONS);
-					dispatch_notification(req->owner,
-						OCMEM_ALLOC_SHRINK, &buffer);
-				}
-			}
-		} else {
-			pr_info("skipping %p from eviction\n", tmp_region);
+	rc = __evict_common(edata, NULL);
+
+	if (rc < 0)
+		goto skip_eviction;
+
+	trigger_eviction(edata);
+
+	evictions[id] = edata;
+
+	mutex_unlock(&sched_mutex);
+
+	wait_for_completion(&edata->completion);
+
+	return 0;
+
+skip_eviction:
+	evictions[id] = NULL;
+	mutex_unlock(&sched_mutex);
+	return 0;
+}
+
+static int run_evict(struct ocmem_req *req)
+{
+	struct ocmem_eviction_data *edata = NULL;
+	int rc = 0;
+
+	if (!req)
+		return -EINVAL;
+
+	edata = init_eviction(req->owner);
+
+	if (!edata)
+		return -EINVAL;
+
+	edata->passive = false;
+
+	rc = __evict_common(edata, req);
+
+	if (rc < 0)
+		goto skip_eviction;
+
+	trigger_eviction(edata);
+
+	pr_debug("ocmem: attaching eviction %p to request %p", edata, req);
+	req->edata = edata;
+
+	wait_for_completion(&edata->completion);
+
+	pr_debug("ocmem: eviction completed successfully\n");
+	return 0;
+
+skip_eviction:
+	pr_err("ocmem: Unable to run eviction\n");
+	free_eviction(edata);
+	return -EINVAL;
+}
+
+static int __restore_common(struct ocmem_eviction_data *edata)
+{
+
+	struct ocmem_req *req = NULL;
+	struct ocmem_req *next = NULL;
+
+	if (!edata)
+		return -EINVAL;
+
+	list_for_each_entry_safe(req, next, &edata->req_list, eviction_list)
+	{
+		if (req) {
+			pr_debug("ocmem: restoring evicted request %p\n",
+								req);
+			list_del(&req->eviction_list);
+			req->op = SCHED_ALLOCATE;
+			sched_enqueue(req);
+			inc_ocmem_stat(zone_of(req), NR_RESTORES);
 		}
 	}
-	mutex_unlock(&sched_mutex);
-	pr_debug("Waiting for all regions to be shrunk\n");
-	if (edata->pending > 0) {
-		init_completion(&edata->completion);
-		wait_for_completion(&edata->completion);
+
+	pr_debug("Scheduled all evicted regions\n");
+
+	return 0;
+}
+
+static int sched_restore(struct ocmem_req *req)
+{
+
+	int rc = 0;
+
+	if (!req)
+		return -EINVAL;
+
+	if (!req->edata)
+		return 0;
+
+	rc = __restore_common(req->edata);
+
+	if (rc < 0)
+		return -EINVAL;
+
+	free_eviction(req->edata);
+	return 0;
+}
+
+int process_restore(int id)
+{
+	struct ocmem_eviction_data *edata = evictions[id];
+	int rc = 0;
+
+	if (!edata)
+		return -EINVAL;
+
+	rc = __restore_common(edata);
+
+	if (rc < 0) {
+		pr_err("Failed to restore evicted requests\n");
+		return -EINVAL;
 	}
+
+	free_eviction(edata);
+	evictions[id] = NULL;
+	ocmem_schedule_pending();
 	return 0;
 }
 
 static int do_allocate(struct ocmem_req *req, bool can_block, bool can_wait)
 {
 	int rc = 0;
+	int ret = 0;
 	struct ocmem_buf *buffer = req->buffer;
 
 	down_write(&req->rw_sem);
 
+	mutex_lock(&allocation_mutex);
+retry_allocate:
+
 	/* Take the scheduler mutex */
 	mutex_lock(&sched_mutex);
 	rc = __sched_allocate(req, can_block, can_wait);
 	mutex_unlock(&sched_mutex);
 
+	if (rc == OP_EVICT) {
+
+		ret = run_evict(req);
+
+		if (ret == 0) {
+			rc = sched_restore(req);
+			if (rc < 0) {
+				pr_err("Failed to restore for req %p\n", req);
+				goto err_allocate_fail;
+			}
+			req->edata = NULL;
+
+			pr_debug("Attempting to re-allocate req %p\n", req);
+			req->req_start = 0x0;
+			req->req_end = 0x0;
+			goto retry_allocate;
+		} else {
+			goto err_allocate_fail;
+		}
+	}
+
+	mutex_unlock(&allocation_mutex);
+
 	if (rc == OP_FAIL) {
 		inc_ocmem_stat(zone_of(req), NR_ALLOCATION_FAILS);
 		goto err_allocate_fail;
@@ -1666,35 +1984,37 @@
 	up_write(&req->rw_sem);
 	return 0;
 err_allocate_fail:
+	mutex_unlock(&allocation_mutex);
 	up_write(&req->rw_sem);
 	return -EINVAL;
 }
 
-int process_restore(int id)
+static int do_dump(struct ocmem_req *req, unsigned long addr)
 {
-	struct ocmem_req *req = NULL;
-	struct ocmem_req *next = NULL;
-	struct ocmem_eviction_data *edata = evictions[id];
 
-	if (!edata)
-		return 0;
+	void __iomem *req_vaddr;
+	unsigned long offset = 0x0;
 
-	list_for_each_entry_safe(req, next, &edata->req_list, eviction_list)
-	{
-		if (req) {
-			pr_debug("ocmem: Fetched evicted request %p\n",
-								req);
-			list_del(&req->sched_list);
-			req->op = SCHED_ALLOCATE;
-			sched_enqueue(req);
-			inc_ocmem_stat(zone_of(req), NR_RESTORES);
-		}
-	}
-	kfree(edata);
-	evictions[id] = NULL;
-	pr_debug("Restore all evicted regions\n");
-	ocmem_schedule_pending();
+	down_write(&req->rw_sem);
+
+	offset = phys_to_offset(req->req_start);
+
+	req_vaddr = ocmem_vaddr + offset;
+
+	if (!req_vaddr)
+		goto err_do_dump;
+
+	pr_debug("Dumping client %s buffer ocmem p: %lx (v: %p) to ddr %lx\n",
+				get_name(req->owner), req->req_start,
+				req_vaddr, addr);
+
+	memcpy((void *)addr, req_vaddr, req->req_sz);
+
+	up_write(&req->rw_sem);
 	return 0;
+err_do_dump:
+	up_write(&req->rw_sem);
+	return -EINVAL;
 }
 
 int process_allocate(int id, struct ocmem_handle *handle,
@@ -1745,13 +2065,11 @@
 
 	handle->req = req;
 
-	if (is_tcm(id)) {
+	if (req->req_sz != 0) {
+
 		rc = process_map(req, req->req_start, req->req_end);
 		if (rc < 0)
 			goto map_error;
-	}
-
-	if (req->req_sz != 0) {
 
 		offset = phys_to_offset(req->req_start);
 
@@ -1766,6 +2084,7 @@
 	return 0;
 
 power_ctl_error:
+	process_unmap(req, req->req_start, req->req_end);
 map_error:
 	handle->req = NULL;
 	do_free(req);
@@ -1790,15 +2109,18 @@
 	if (rc < 0)
 		goto do_allocate_error;
 
+	/* The request can still be pending */
+	if (TEST_STATE(req, R_PENDING))
+		return 0;
+
 	inc_ocmem_stat(zone_of(req), NR_ASYNC_ALLOCATIONS);
 
-	if (is_tcm(id)) {
+	if (req->req_sz != 0) {
+
 		rc = process_map(req, req->req_start, req->req_end);
 		if (rc < 0)
 			goto map_error;
-	}
 
-	if (req->req_sz != 0) {
 
 		offset = phys_to_offset(req->req_start);
 
@@ -1820,6 +2142,7 @@
 	return 0;
 
 power_ctl_error:
+	process_unmap(req, req->req_start, req->req_end);
 map_error:
 	handle->req = NULL;
 	do_free(req);
@@ -1828,6 +2151,38 @@
 	return -EINVAL;
 }
 
+int process_dump(int id, struct ocmem_handle *handle, unsigned long addr)
+{
+	struct ocmem_req *req = NULL;
+	int rc = 0;
+
+	req = handle_to_req(handle);
+
+	if (!req)
+		return -EINVAL;
+
+	if (!is_mapped(req)) {
+		pr_err("Buffer is not mapped\n");
+		goto dump_error;
+	}
+
+	inc_ocmem_stat(zone_of(req), NR_DUMP_REQUESTS);
+
+	mutex_lock(&sched_mutex);
+	rc = do_dump(req, addr);
+	mutex_unlock(&sched_mutex);
+
+	if (rc < 0)
+		goto dump_error;
+
+	inc_ocmem_stat(zone_of(req), NR_DUMP_COMPLETE);
+	return 0;
+
+dump_error:
+	pr_err("Dumping OCMEM memory failed for client %d\n", id);
+	return -EINVAL;
+}
+
 static void ocmem_sched_wk_func(struct work_struct *work)
 {
 
@@ -1904,8 +2259,10 @@
 
 	sched_tree = RB_ROOT;
 	pdata = platform_get_drvdata(pdev);
+	mutex_init(&allocation_mutex);
 	mutex_init(&sched_mutex);
 	mutex_init(&sched_queue_mutex);
+	ocmem_vaddr = pdata->vbase;
 	for (i = MIN_PRIO; i < MAX_OCMEM_PRIO; i++)
 		INIT_LIST_HEAD(&sched_queue[i]);
 
diff --git a/arch/arm/mach-msm/peripheral-loader.c b/arch/arm/mach-msm/peripheral-loader.c
index 4ff34bf..65e05a9 100644
--- a/arch/arm/mach-msm/peripheral-loader.c
+++ b/arch/arm/mach-msm/peripheral-loader.c
@@ -12,28 +12,33 @@
 
 #include <linux/module.h>
 #include <linux/string.h>
-#include <linux/device.h>
 #include <linux/firmware.h>
 #include <linux/io.h>
-#include <linux/debugfs.h>
 #include <linux/elf.h>
 #include <linux/mutex.h>
 #include <linux/memblock.h>
 #include <linux/slab.h>
-#include <linux/atomic.h>
 #include <linux/suspend.h>
 #include <linux/rwsem.h>
 #include <linux/sysfs.h>
 #include <linux/workqueue.h>
 #include <linux/jiffies.h>
 #include <linux/wakelock.h>
+#include <linux/err.h>
+#include <linux/msm_ion.h>
+#include <linux/list.h>
+#include <linux/list_sort.h>
 
 #include <asm/uaccess.h>
 #include <asm/setup.h>
-#include <mach/peripheral-loader.h>
 
 #include "peripheral-loader.h"
 
+#define pil_err(desc, fmt, ...)						\
+	dev_err(desc->dev, "%s: " fmt, desc->name, ##__VA_ARGS__)
+#define pil_info(desc, fmt, ...)					\
+	dev_info(desc->dev, "%s: " fmt, desc->name, ##__VA_ARGS__)
+
 /**
  * proxy_timeout - Override for proxy vote timeouts
  * -1: Use driver-specified timeout
@@ -43,146 +48,376 @@
 static int proxy_timeout_ms = -1;
 module_param(proxy_timeout_ms, int, S_IRUGO | S_IWUSR);
 
-enum pil_state {
-	PIL_OFFLINE,
-	PIL_ONLINE,
+/**
+ * struct pil_mdt - Representation of <name>.mdt file in memory
+ * @hdr: ELF32 header
+ * @phdr: ELF32 program headers
+ */
+struct pil_mdt {
+	struct elf32_hdr hdr;
+	struct elf32_phdr phdr[];
 };
 
-static const char *pil_states[] = {
-	[PIL_OFFLINE] = "OFFLINE",
-	[PIL_ONLINE] = "ONLINE",
+/**
+ * struct pil_seg - memory map representing one segment
+ * @next: points to next seg mentor NULL if last segment
+ * @paddr: start address of segment
+ * @sz: size of segment
+ * @filesz: size of segment on disk
+ * @num: segment number
+ * @relocated: true if segment is relocated, false otherwise
+ *
+ * Loosely based on an elf program header. Contains all necessary information
+ * to load and initialize a segment of the image in memory.
+ */
+struct pil_seg {
+	phys_addr_t paddr;
+	unsigned long sz;
+	unsigned long filesz;
+	int num;
+	struct list_head list;
+	bool relocated;
 };
 
-struct pil_device {
-	struct pil_desc *desc;
-	int count;
-	enum pil_state state;
-	struct mutex lock;
-	struct device dev;
-	struct module *owner;
-#ifdef CONFIG_DEBUG_FS
-	struct dentry *dentry;
-#endif
+/**
+ * struct pil_priv - Private state for a pil_desc
+ * @proxy: work item used to run the proxy unvoting routine
+ * @wlock: wakelock to prevent suspend during pil_boot
+ * @wname: name of @wlock
+ * @desc: pointer to pil_desc this is private data for
+ * @seg: list of segments sorted by physical address
+ * @entry_addr: physical address where processor starts booting at
+ * @base_addr: smallest start address among all segments that are relocatable
+ * @region_start: address where relocatable region starts or lowest address
+ * for non-relocatable images
+ * @region_end: address where relocatable region ends or highest address for
+ * non-relocatable images
+ * @region: region allocated for relocatable images
+ *
+ * This struct contains data for a pil_desc that should not be exposed outside
+ * of this file. This structure points to the descriptor and the descriptor
+ * points to this structure so that PIL drivers can't access the private
+ * data of a descriptor but this file can access both.
+ */
+struct pil_priv {
 	struct delayed_work proxy;
 	struct wake_lock wlock;
-	char wake_name[32];
+	char wname[32];
+	struct pil_desc *desc;
+	struct list_head segs;
+	phys_addr_t entry_addr;
+	phys_addr_t base_addr;
+	phys_addr_t region_start;
+	phys_addr_t region_end;
+	struct ion_handle *region;
 };
 
-#define to_pil_device(d) container_of(d, struct pil_device, dev)
+static struct ion_client *ion;
 
-static ssize_t name_show(struct device *dev, struct device_attribute *attr,
-		char *buf)
+/**
+ * pil_get_entry_addr() - Retrieve the entry address of a peripheral image
+ * @desc: descriptor from pil_desc_init()
+ *
+ * Returns the physical address where the image boots at or 0 if unknown.
+ */
+phys_addr_t pil_get_entry_addr(struct pil_desc *desc)
 {
-	return snprintf(buf, PAGE_SIZE, "%s\n", to_pil_device(dev)->desc->name);
+	return desc->priv ? desc->priv->entry_addr : 0;
 }
-
-static ssize_t state_show(struct device *dev, struct device_attribute *attr,
-		char *buf)
-{
-	enum pil_state state = to_pil_device(dev)->state;
-	return snprintf(buf, PAGE_SIZE, "%s\n", pil_states[state]);
-}
-
-static struct device_attribute pil_attrs[] = {
-	__ATTR_RO(name),
-	__ATTR_RO(state),
-	{ },
-};
-
-struct bus_type pil_bus_type = {
-	.name		= "pil",
-	.dev_attrs	= pil_attrs,
-};
-
-static int __find_peripheral(struct device *dev, void *data)
-{
-	struct pil_device *pdev = to_pil_device(dev);
-	return !strncmp(pdev->desc->name, data, INT_MAX);
-}
-
-static struct pil_device *find_peripheral(const char *str)
-{
-	struct device *dev;
-
-	if (!str)
-		return NULL;
-
-	dev = bus_find_device(&pil_bus_type, NULL, (void *)str,
-			__find_peripheral);
-	return dev ? to_pil_device(dev) : NULL;
-}
+EXPORT_SYMBOL(pil_get_entry_addr);
 
 static void pil_proxy_work(struct work_struct *work)
 {
-	struct pil_device *pil;
+	struct delayed_work *delayed = to_delayed_work(work);
+	struct pil_priv *priv = container_of(delayed, struct pil_priv, proxy);
+	struct pil_desc *desc = priv->desc;
 
-	pil = container_of(work, struct pil_device, proxy.work);
-	pil->desc->ops->proxy_unvote(pil->desc);
-	wake_unlock(&pil->wlock);
+	desc->ops->proxy_unvote(desc);
+	wake_unlock(&priv->wlock);
+	module_put(desc->owner);
 }
 
-static int pil_proxy_vote(struct pil_device *pil)
+static int pil_proxy_vote(struct pil_desc *desc)
 {
 	int ret = 0;
+	struct pil_priv *priv = desc->priv;
 
-	if (pil->desc->ops->proxy_vote) {
-		wake_lock(&pil->wlock);
-		ret = pil->desc->ops->proxy_vote(pil->desc);
+	if (desc->ops->proxy_vote) {
+		wake_lock(&priv->wlock);
+		ret = desc->ops->proxy_vote(desc);
 		if (ret)
-			wake_unlock(&pil->wlock);
+			wake_unlock(&priv->wlock);
 	}
 	return ret;
 }
 
-static void pil_proxy_unvote(struct pil_device *pil, unsigned long timeout)
+static void pil_proxy_unvote(struct pil_desc *desc, unsigned long timeout)
 {
+	struct pil_priv *priv = desc->priv;
+
 	if (proxy_timeout_ms >= 0)
 		timeout = proxy_timeout_ms;
 
-	if (timeout && pil->desc->ops->proxy_unvote)
-		schedule_delayed_work(&pil->proxy, msecs_to_jiffies(timeout));
+	if (timeout && desc->ops->proxy_unvote) {
+		if (WARN_ON(!try_module_get(desc->owner)))
+			return;
+		schedule_delayed_work(&priv->proxy, msecs_to_jiffies(timeout));
+	}
+}
+
+static bool segment_is_relocatable(const struct elf32_phdr *p)
+{
+	return !!(p->p_flags & BIT(27));
+}
+
+static phys_addr_t pil_reloc(const struct pil_priv *priv, phys_addr_t addr)
+{
+	return addr - priv->base_addr + priv->region_start;
+}
+
+static struct pil_seg *pil_init_seg(const struct pil_desc *desc,
+				  const struct elf32_phdr *phdr, int num)
+{
+	bool reloc = segment_is_relocatable(phdr);
+	const struct pil_priv *priv = desc->priv;
+	struct pil_seg *seg;
+
+	if (!reloc && memblock_overlaps_memory(phdr->p_paddr, phdr->p_memsz)) {
+		pil_err(desc, "kernel memory would be overwritten [%#08lx, %#08lx)\n",
+			(unsigned long)phdr->p_paddr,
+			(unsigned long)(phdr->p_paddr + phdr->p_memsz));
+		return ERR_PTR(-EPERM);
+	}
+
+	seg = kmalloc(sizeof(*seg), GFP_KERNEL);
+	if (!seg)
+		return ERR_PTR(-ENOMEM);
+	seg->num = num;
+	seg->paddr = reloc ? pil_reloc(priv, phdr->p_paddr) : phdr->p_paddr;
+	seg->filesz = phdr->p_filesz;
+	seg->sz = phdr->p_memsz;
+	seg->relocated = reloc;
+	INIT_LIST_HEAD(&seg->list);
+
+	return seg;
+}
+
+#define segment_is_hash(flag) (((flag) & (0x7 << 24)) == (0x2 << 24))
+
+static int segment_is_loadable(const struct elf32_phdr *p)
+{
+	return (p->p_type == PT_LOAD) && !segment_is_hash(p->p_flags) &&
+		p->p_memsz;
+}
+
+static void pil_dump_segs(const struct pil_priv *priv)
+{
+	struct pil_seg *seg;
+
+	list_for_each_entry(seg, &priv->segs, list) {
+		pil_info(priv->desc, "%d: %#08zx %#08lx\n", seg->num,
+				seg->paddr, seg->paddr + seg->sz);
+	}
+}
+
+/*
+ * Ensure the entry address lies within the image limits and if the image is
+ * relocatable ensure it lies within a relocatable segment.
+ */
+static int pil_init_entry_addr(struct pil_priv *priv, const struct pil_mdt *mdt)
+{
+	struct pil_seg *seg;
+	phys_addr_t entry = mdt->hdr.e_entry;
+	bool image_relocated = priv->region;
+
+	if (image_relocated)
+		entry = pil_reloc(priv, entry);
+	priv->entry_addr = entry;
+
+	if (priv->desc->flags & PIL_SKIP_ENTRY_CHECK)
+		return 0;
+
+	list_for_each_entry(seg, &priv->segs, list) {
+		if (entry >= seg->paddr && entry < seg->paddr + seg->sz) {
+			if (!image_relocated)
+				return 0;
+			else if (seg->relocated)
+				return 0;
+		}
+	}
+	pil_err(priv->desc, "entry address %08zx not within range\n", entry);
+	pil_dump_segs(priv);
+	return -EADDRNOTAVAIL;
+}
+
+static int pil_alloc_region(struct pil_priv *priv, phys_addr_t min_addr,
+				phys_addr_t max_addr, size_t align)
+{
+	struct ion_handle *region;
+	int ret;
+	unsigned int mask;
+	size_t size = round_up(max_addr - min_addr, align);
+
+	if (!ion) {
+		WARN_ON_ONCE("No ION client, can't support relocation\n");
+		return -ENOMEM;
+	}
+
+	/* Force alignment due to linker scripts not getting it right */
+	if (align > SZ_1M) {
+		mask = ION_HEAP(ION_PIL2_HEAP_ID);
+		align = SZ_4M;
+	} else {
+		mask = ION_HEAP(ION_PIL1_HEAP_ID);
+		align = SZ_1M;
+	}
+
+	region = ion_alloc(ion, size, align, mask, 0);
+	if (IS_ERR(region)) {
+		pil_err(priv->desc, "Failed to allocate relocatable region\n");
+		return PTR_ERR(region);
+	}
+
+	ret = ion_phys(ion, region, (ion_phys_addr_t *)&priv->region_start,
+			&size);
+	if (ret) {
+		ion_free(ion, region);
+		return ret;
+	}
+
+	priv->region = region;
+	priv->region_end = priv->region_start + size;
+	priv->base_addr = min_addr;
+
+	return 0;
+}
+
+static int pil_setup_region(struct pil_priv *priv, const struct pil_mdt *mdt)
+{
+	const struct elf32_phdr *phdr;
+	phys_addr_t min_addr_r, min_addr_n, max_addr_r, max_addr_n, start, end;
+	size_t align = 0;
+	int i, ret = 0;
+	bool relocatable = false;
+
+	min_addr_n = min_addr_r = (phys_addr_t)ULLONG_MAX;
+	max_addr_n = max_addr_r = 0;
+
+	/* Find the image limits */
+	for (i = 0; i < mdt->hdr.e_phnum; i++) {
+		phdr = &mdt->phdr[i];
+		if (!segment_is_loadable(phdr))
+			continue;
+
+		start = phdr->p_paddr;
+		end = start + phdr->p_memsz;
+
+		if (segment_is_relocatable(phdr)) {
+			min_addr_r = min(min_addr_r, start);
+			max_addr_r = max(max_addr_r, end);
+			/*
+			 * Lowest relocatable segment dictates alignment of
+			 * relocatable region
+			 */
+			if (min_addr_r == start)
+				align = phdr->p_align;
+			relocatable = true;
+		} else {
+			min_addr_n = min(min_addr_n, start);
+			max_addr_n = max(max_addr_n, end);
+		}
+
+	}
+
+	if (relocatable) {
+		ret = pil_alloc_region(priv, min_addr_r, max_addr_r, align);
+	} else {
+		priv->region_start = min_addr_n;
+		priv->region_end = max_addr_n;
+		priv->base_addr = min_addr_n;
+	}
+
+	return ret;
+}
+
+static int pil_cmp_seg(void *priv, struct list_head *a, struct list_head *b)
+{
+	struct pil_seg *seg_a = list_entry(a, struct pil_seg, list);
+	struct pil_seg *seg_b = list_entry(b, struct pil_seg, list);
+
+	return seg_a->paddr - seg_b->paddr;
+}
+
+static int pil_init_mmap(struct pil_desc *desc, const struct pil_mdt *mdt)
+{
+	struct pil_priv *priv = desc->priv;
+	const struct elf32_phdr *phdr;
+	struct pil_seg *seg;
+	int i, ret;
+
+	ret = pil_setup_region(priv, mdt);
+	if (ret)
+		return ret;
+
+	for (i = 0; i < mdt->hdr.e_phnum; i++) {
+		phdr = &mdt->phdr[i];
+		if (!segment_is_loadable(phdr))
+			continue;
+
+		seg = pil_init_seg(desc, phdr, i);
+		if (IS_ERR(seg))
+			return PTR_ERR(seg);
+
+		list_add_tail(&seg->list, &priv->segs);
+	}
+	list_sort(NULL, &priv->segs, pil_cmp_seg);
+
+	return pil_init_entry_addr(priv, mdt);
+}
+
+static void pil_release_mmap(struct pil_desc *desc)
+{
+	struct pil_priv *priv = desc->priv;
+	struct pil_seg *p, *tmp;
+
+	if (priv->region)
+		ion_free(ion, priv->region);
+	list_for_each_entry_safe(p, tmp, &priv->segs, list) {
+		list_del(&p->list);
+		kfree(p);
+	}
 }
 
 #define IOMAP_SIZE SZ_4M
 
-static int load_segment(const struct elf32_phdr *phdr, unsigned num,
-		struct pil_device *pil)
+static int pil_load_seg(struct pil_desc *desc, struct pil_seg *seg)
 {
 	int ret = 0, count, paddr;
 	char fw_name[30];
 	const struct firmware *fw = NULL;
 	const u8 *data;
+	int num = seg->num;
 
-	if (memblock_overlaps_memory(phdr->p_paddr, phdr->p_memsz)) {
-		dev_err(&pil->dev, "%s: kernel memory would be overwritten "
-			"[%#08lx, %#08lx)\n", pil->desc->name,
-			(unsigned long)phdr->p_paddr,
-			(unsigned long)(phdr->p_paddr + phdr->p_memsz));
-		return -EPERM;
-	}
-
-	if (phdr->p_filesz) {
+	if (seg->filesz) {
 		snprintf(fw_name, ARRAY_SIZE(fw_name), "%s.b%02d",
-				pil->desc->name, num);
-		ret = request_firmware(&fw, fw_name, &pil->dev);
+				desc->name, num);
+		ret = request_firmware(&fw, fw_name, desc->dev);
 		if (ret) {
-			dev_err(&pil->dev, "%s: Failed to locate blob %s\n",
-					pil->desc->name, fw_name);
+			pil_err(desc, "Failed to locate blob %s\n", fw_name);
 			return ret;
 		}
 
-		if (fw->size != phdr->p_filesz) {
-			dev_err(&pil->dev, "%s: Blob size %u doesn't match "
-					"%u\n", pil->desc->name, fw->size,
-					phdr->p_filesz);
+		if (fw->size != seg->filesz) {
+			pil_err(desc, "Blob size %u doesn't match %lu\n",
+					fw->size, seg->filesz);
 			ret = -EPERM;
 			goto release_fw;
 		}
 	}
 
 	/* Load the segment into memory */
-	count = phdr->p_filesz;
-	paddr = phdr->p_paddr;
+	count = seg->filesz;
+	paddr = seg->paddr;
 	data = fw ? fw->data : NULL;
 	while (count > 0) {
 		int size;
@@ -191,8 +426,7 @@
 		size = min_t(size_t, IOMAP_SIZE, count);
 		buf = ioremap(paddr, size);
 		if (!buf) {
-			dev_err(&pil->dev, "%s: Failed to map memory\n",
-					pil->desc->name);
+			pil_err(desc, "Failed to map memory\n");
 			ret = -ENOMEM;
 			goto release_fw;
 		}
@@ -205,7 +439,7 @@
 	}
 
 	/* Zero out trailing memory */
-	count = phdr->p_memsz - phdr->p_filesz;
+	count = seg->sz - seg->filesz;
 	while (count > 0) {
 		int size;
 		u8 __iomem *buf;
@@ -213,8 +447,7 @@
 		size = min_t(size_t, IOMAP_SIZE, count);
 		buf = ioremap(paddr, size);
 		if (!buf) {
-			dev_err(&pil->dev, "%s: Failed to map memory\n",
-					pil->desc->name);
+			pil_err(desc, "Failed to map memory\n");
 			ret = -ENOMEM;
 			goto release_fw;
 		}
@@ -225,12 +458,10 @@
 		paddr += size;
 	}
 
-	if (pil->desc->ops->verify_blob) {
-		ret = pil->desc->ops->verify_blob(pil->desc, phdr->p_paddr,
-					  phdr->p_memsz);
+	if (desc->ops->verify_blob) {
+		ret = desc->ops->verify_blob(desc, seg->paddr, seg->sz);
 		if (ret)
-			dev_err(&pil->dev, "%s: Blob%u failed verification\n",
-				pil->desc->name, num);
+			pil_err(desc, "Blob%u failed verification\n", num);
 	}
 
 release_fw:
@@ -238,423 +469,180 @@
 	return ret;
 }
 
-#define segment_is_hash(flag) (((flag) & (0x7 << 24)) == (0x2 << 24))
-
-static int segment_is_loadable(const struct elf32_phdr *p)
-{
-	return (p->p_type == PT_LOAD) && !segment_is_hash(p->p_flags);
-}
-
-/* Sychronize request_firmware() with suspend */
+/* Synchronize request_firmware() with suspend */
 static DECLARE_RWSEM(pil_pm_rwsem);
 
-static int load_image(struct pil_device *pil)
+/**
+ * pil_boot() - Load a peripheral image into memory and boot it
+ * @desc: descriptor from pil_desc_init()
+ *
+ * Returns 0 on success or -ERROR on failure.
+ */
+int pil_boot(struct pil_desc *desc)
 {
-	int i, ret;
+	int ret;
 	char fw_name[30];
-	struct elf32_hdr *ehdr;
-	const struct elf32_phdr *phdr;
+	const struct pil_mdt *mdt;
+	const struct elf32_hdr *ehdr;
+	struct pil_seg *seg;
 	const struct firmware *fw;
-	unsigned long proxy_timeout = pil->desc->proxy_timeout;
+	unsigned long proxy_timeout = desc->proxy_timeout;
+	struct pil_priv *priv = desc->priv;
+
+	/* Reinitialize for new image */
+	pil_release_mmap(desc);
 
 	down_read(&pil_pm_rwsem);
-	snprintf(fw_name, sizeof(fw_name), "%s.mdt", pil->desc->name);
-	ret = request_firmware(&fw, fw_name, &pil->dev);
+	snprintf(fw_name, sizeof(fw_name), "%s.mdt", desc->name);
+	ret = request_firmware(&fw, fw_name, desc->dev);
 	if (ret) {
-		dev_err(&pil->dev, "%s: Failed to locate %s\n",
-				pil->desc->name, fw_name);
+		pil_err(desc, "Failed to locate %s\n", fw_name);
 		goto out;
 	}
 
 	if (fw->size < sizeof(*ehdr)) {
-		dev_err(&pil->dev, "%s: Not big enough to be an elf header\n",
-				pil->desc->name);
+		pil_err(desc, "Not big enough to be an elf header\n");
 		ret = -EIO;
 		goto release_fw;
 	}
 
-	ehdr = (struct elf32_hdr *)fw->data;
+	mdt = (const struct pil_mdt *)fw->data;
+	ehdr = &mdt->hdr;
+
 	if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG)) {
-		dev_err(&pil->dev, "%s: Not an elf header\n", pil->desc->name);
+		pil_err(desc, "Not an elf header\n");
 		ret = -EIO;
 		goto release_fw;
 	}
 
 	if (ehdr->e_phnum == 0) {
-		dev_err(&pil->dev, "%s: No loadable segments\n",
-				pil->desc->name);
+		pil_err(desc, "No loadable segments\n");
 		ret = -EIO;
 		goto release_fw;
 	}
 	if (sizeof(struct elf32_phdr) * ehdr->e_phnum +
 	    sizeof(struct elf32_hdr) > fw->size) {
-		dev_err(&pil->dev, "%s: Program headers not within mdt\n",
-				pil->desc->name);
+		pil_err(desc, "Program headers not within mdt\n");
 		ret = -EIO;
 		goto release_fw;
 	}
 
-	ret = pil->desc->ops->init_image(pil->desc, fw->data, fw->size);
+	ret = pil_init_mmap(desc, mdt);
+	if (ret)
+		goto release_fw;
+
+	if (desc->ops->init_image)
+		ret = desc->ops->init_image(desc, fw->data, fw->size);
 	if (ret) {
-		dev_err(&pil->dev, "%s: Invalid firmware metadata\n",
-				pil->desc->name);
+		pil_err(desc, "Invalid firmware metadata\n");
 		goto release_fw;
 	}
 
-	phdr = (const struct elf32_phdr *)(fw->data + sizeof(struct elf32_hdr));
-	for (i = 0; i < ehdr->e_phnum; i++, phdr++) {
-		if (!segment_is_loadable(phdr))
-			continue;
+	if (desc->ops->mem_setup)
+		ret = desc->ops->mem_setup(desc, priv->region_start,
+				priv->region_end - priv->region_start);
+	if (ret) {
+		pil_err(desc, "Memory setup error\n");
+		goto release_fw;
+	}
 
-		ret = load_segment(phdr, i, pil);
-		if (ret) {
-			dev_err(&pil->dev, "%s: Failed to load segment %d\n",
-					pil->desc->name, i);
+	list_for_each_entry(seg, &desc->priv->segs, list) {
+		ret = pil_load_seg(desc, seg);
+		if (ret)
 			goto release_fw;
-		}
 	}
 
-	ret = pil_proxy_vote(pil);
+	ret = pil_proxy_vote(desc);
 	if (ret) {
-		dev_err(&pil->dev, "%s: Failed to proxy vote\n",
-					pil->desc->name);
+		pil_err(desc, "Failed to proxy vote\n");
 		goto release_fw;
 	}
 
-	ret = pil->desc->ops->auth_and_reset(pil->desc);
+	ret = desc->ops->auth_and_reset(desc);
 	if (ret) {
-		dev_err(&pil->dev, "%s: Failed to bring out of reset\n",
-				pil->desc->name);
+		pil_err(desc, "Failed to bring out of reset\n");
 		proxy_timeout = 0; /* Remove proxy vote immediately on error */
 		goto err_boot;
 	}
-	dev_info(&pil->dev, "%s: Brought out of reset\n", pil->desc->name);
+	pil_info(desc, "Brought out of reset\n");
 err_boot:
-	pil_proxy_unvote(pil, proxy_timeout);
+	pil_proxy_unvote(desc, proxy_timeout);
 release_fw:
 	release_firmware(fw);
 out:
 	up_read(&pil_pm_rwsem);
+	if (ret)
+		pil_release_mmap(desc);
 	return ret;
 }
-
-static void pil_set_state(struct pil_device *pil, enum pil_state state)
-{
-	if (pil->state != state) {
-		pil->state = state;
-		sysfs_notify(&pil->dev.kobj, NULL, "state");
-	}
-}
+EXPORT_SYMBOL(pil_boot);
 
 /**
- * pil_get() - Load a peripheral into memory and take it out of reset
- * @name: pointer to a string containing the name of the peripheral to load
- *
- * This function returns a pointer if it succeeds. If an error occurs an
- * ERR_PTR is returned.
- *
- * If PIL is not enabled in the kernel, the value %NULL will be returned.
+ * pil_shutdown() - Shutdown a peripheral
+ * @desc: descriptor from pil_desc_init()
  */
-void *pil_get(const char *name)
+void pil_shutdown(struct pil_desc *desc)
 {
-	int ret;
-	struct pil_device *pil;
-	struct pil_device *pil_d;
-	void *retval;
-
-	if (!name)
-		return NULL;
-
-	pil = retval = find_peripheral(name);
-	if (!pil)
-		return ERR_PTR(-ENODEV);
-	if (!try_module_get(pil->owner)) {
-		put_device(&pil->dev);
-		return ERR_PTR(-ENODEV);
-	}
-
-	pil_d = pil_get(pil->desc->depends_on);
-	if (IS_ERR(pil_d)) {
-		retval = pil_d;
-		goto err_depends;
-	}
-
-	mutex_lock(&pil->lock);
-	if (!pil->count) {
-		ret = load_image(pil);
-		if (ret) {
-			retval = ERR_PTR(ret);
-			goto err_load;
-		}
-	}
-	pil->count++;
-	pil_set_state(pil, PIL_ONLINE);
-	mutex_unlock(&pil->lock);
-out:
-	return retval;
-err_load:
-	mutex_unlock(&pil->lock);
-	pil_put(pil_d);
-err_depends:
-	put_device(&pil->dev);
-	module_put(pil->owner);
-	goto out;
-}
-EXPORT_SYMBOL(pil_get);
-
-static void pil_shutdown(struct pil_device *pil)
-{
-	pil->desc->ops->shutdown(pil->desc);
-	if (proxy_timeout_ms == 0 && pil->desc->ops->proxy_unvote)
-		pil->desc->ops->proxy_unvote(pil->desc);
+	struct pil_priv *priv = desc->priv;
+	desc->ops->shutdown(desc);
+	if (proxy_timeout_ms == 0 && desc->ops->proxy_unvote)
+		desc->ops->proxy_unvote(desc);
 	else
-		flush_delayed_work(&pil->proxy);
-
-	pil_set_state(pil, PIL_OFFLINE);
+		flush_delayed_work(&priv->proxy);
 }
+EXPORT_SYMBOL(pil_shutdown);
 
 /**
- * pil_put() - Inform PIL the peripheral no longer needs to be active
- * @peripheral_handle: pointer from a previous call to pil_get()
+ * pil_desc_init() - Initialize a pil descriptor
+ * @desc: descriptor to intialize
  *
- * This doesn't imply that a peripheral is shutdown or in reset since another
- * driver could be using the peripheral.
+ * Initialize a pil descriptor for use by other pil functions. This function
+ * must be called before calling pil_boot() or pil_shutdown().
+ *
+ * Returns 0 for success and -ERROR on failure.
  */
-void pil_put(void *peripheral_handle)
+int pil_desc_init(struct pil_desc *desc)
 {
-	struct pil_device *pil_d, *pil = peripheral_handle;
-
-	if (IS_ERR_OR_NULL(pil))
-		return;
-
-	mutex_lock(&pil->lock);
-	if (WARN(!pil->count, "%s: %s: Reference count mismatch\n",
-			pil->desc->name, __func__))
-		goto err_out;
-	if (!--pil->count)
-		pil_shutdown(pil);
-	mutex_unlock(&pil->lock);
-
-	pil_d = find_peripheral(pil->desc->depends_on);
-	module_put(pil->owner);
-	if (pil_d) {
-		pil_put(pil_d);
-		put_device(&pil_d->dev);
-	}
-	put_device(&pil->dev);
-	return;
-err_out:
-	mutex_unlock(&pil->lock);
-	return;
-}
-EXPORT_SYMBOL(pil_put);
-
-void pil_force_shutdown(const char *name)
-{
-	struct pil_device *pil;
-
-	pil = find_peripheral(name);
-	if (!pil) {
-		pr_err("%s: Couldn't find %s\n", __func__, name);
-		return;
-	}
-
-	mutex_lock(&pil->lock);
-	if (!WARN(!pil->count, "%s: %s: Reference count mismatch\n",
-			pil->desc->name, __func__))
-		pil_shutdown(pil);
-	mutex_unlock(&pil->lock);
-
-	put_device(&pil->dev);
-}
-EXPORT_SYMBOL(pil_force_shutdown);
-
-int pil_force_boot(const char *name)
-{
-	int ret = -EINVAL;
-	struct pil_device *pil;
-
-	pil = find_peripheral(name);
-	if (!pil) {
-		pr_err("%s: Couldn't find %s\n", __func__, name);
-		return -EINVAL;
-	}
-
-	mutex_lock(&pil->lock);
-	if (!WARN(!pil->count, "%s: %s: Reference count mismatch\n",
-			pil->desc->name, __func__))
-		ret = load_image(pil);
-	if (!ret)
-		pil_set_state(pil, PIL_ONLINE);
-	mutex_unlock(&pil->lock);
-	put_device(&pil->dev);
-
-	return ret;
-}
-EXPORT_SYMBOL(pil_force_boot);
-
-#ifdef CONFIG_DEBUG_FS
-static int msm_pil_debugfs_open(struct inode *inode, struct file *filp)
-{
-	filp->private_data = inode->i_private;
-	return 0;
-}
-
-static ssize_t msm_pil_debugfs_read(struct file *filp, char __user *ubuf,
-		size_t cnt, loff_t *ppos)
-{
-	int r;
-	char buf[40];
-	struct pil_device *pil = filp->private_data;
-
-	mutex_lock(&pil->lock);
-	r = snprintf(buf, sizeof(buf), "%d\n", pil->count);
-	mutex_unlock(&pil->lock);
-	return simple_read_from_buffer(ubuf, cnt, ppos, buf, r);
-}
-
-static ssize_t msm_pil_debugfs_write(struct file *filp,
-		const char __user *ubuf, size_t cnt, loff_t *ppos)
-{
-	struct pil_device *pil = filp->private_data;
-	char buf[4];
-
-	if (cnt > sizeof(buf))
-		return -EINVAL;
-
-	if (copy_from_user(&buf, ubuf, cnt))
-		return -EFAULT;
-
-	if (!strncmp(buf, "get", 3)) {
-		if (IS_ERR(pil_get(pil->desc->name)))
-			return -EIO;
-	} else if (!strncmp(buf, "put", 3))
-		pil_put(pil);
-	else
-		return -EINVAL;
-
-	return cnt;
-}
-
-static const struct file_operations msm_pil_debugfs_fops = {
-	.open	= msm_pil_debugfs_open,
-	.read	= msm_pil_debugfs_read,
-	.write	= msm_pil_debugfs_write,
-};
-
-static struct dentry *pil_base_dir;
-
-static int __init msm_pil_debugfs_init(void)
-{
-	pil_base_dir = debugfs_create_dir("pil", NULL);
-	if (!pil_base_dir) {
-		pil_base_dir = NULL;
-		return -ENOMEM;
-	}
-
-	return 0;
-}
-
-static void __exit msm_pil_debugfs_exit(void)
-{
-	debugfs_remove_recursive(pil_base_dir);
-}
-
-static int msm_pil_debugfs_add(struct pil_device *pil)
-{
-	if (!pil_base_dir)
-		return -ENOMEM;
-
-	pil->dentry = debugfs_create_file(pil->desc->name, S_IRUGO | S_IWUSR,
-				pil_base_dir, pil, &msm_pil_debugfs_fops);
-	return !pil->dentry ? -ENOMEM : 0;
-}
-
-static void msm_pil_debugfs_remove(struct pil_device *pil)
-{
-	debugfs_remove(pil->dentry);
-}
-#else
-static int __init msm_pil_debugfs_init(void) { return 0; };
-static void __exit msm_pil_debugfs_exit(void) { return 0; };
-static int msm_pil_debugfs_add(struct pil_device *pil) { return 0; }
-static void msm_pil_debugfs_remove(struct pil_device *pil) { }
-#endif
-
-static void pil_device_release(struct device *dev)
-{
-	struct pil_device *pil = to_pil_device(dev);
-	wake_lock_destroy(&pil->wlock);
-	mutex_destroy(&pil->lock);
-	kfree(pil);
-}
-
-struct pil_device *msm_pil_register(struct pil_desc *desc)
-{
-	int err;
-	static atomic_t pil_count = ATOMIC_INIT(-1);
-	struct pil_device *pil;
+	struct pil_priv *priv;
 
 	/* Ignore users who don't make any sense */
+	WARN(desc->ops->proxy_unvote && !desc->proxy_timeout,
+			"A proxy timeout of 0 was specified.\n");
 	if (WARN(desc->ops->proxy_unvote && !desc->ops->proxy_vote,
-				"invalid proxy voting. ignoring\n"))
+				"Invalid proxy voting. Ignoring\n"))
 		((struct pil_reset_ops *)desc->ops)->proxy_unvote = NULL;
 
-	WARN(desc->ops->proxy_unvote && !desc->proxy_timeout,
-		"A proxy timeout of 0 ms was specified for %s. Specify one in "
-		"desc->proxy_timeout.\n", desc->name);
+	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+	desc->priv = priv;
+	priv->desc = desc;
 
-	pil = kzalloc(sizeof(*pil), GFP_KERNEL);
-	if (!pil)
-		return ERR_PTR(-ENOMEM);
+	snprintf(priv->wname, sizeof(priv->wname), "pil-%s", desc->name);
+	wake_lock_init(&priv->wlock, WAKE_LOCK_SUSPEND, priv->wname);
+	INIT_DELAYED_WORK(&priv->proxy, pil_proxy_work);
+	INIT_LIST_HEAD(&priv->segs);
 
-	mutex_init(&pil->lock);
-	pil->desc = desc;
-	pil->owner = desc->owner;
-	pil->dev.parent = desc->dev;
-	pil->dev.bus = &pil_bus_type;
-	pil->dev.release = pil_device_release;
-
-	snprintf(pil->wake_name, sizeof(pil->wake_name), "pil-%s", desc->name);
-	wake_lock_init(&pil->wlock, WAKE_LOCK_SUSPEND, pil->wake_name);
-	INIT_DELAYED_WORK(&pil->proxy, pil_proxy_work);
-
-	dev_set_name(&pil->dev, "pil%d", atomic_inc_return(&pil_count));
-	err = device_register(&pil->dev);
-	if (err) {
-		put_device(&pil->dev);
-		wake_lock_destroy(&pil->wlock);
-		mutex_destroy(&pil->lock);
-		kfree(pil);
-		return ERR_PTR(err);
-	}
-
-	err = msm_pil_debugfs_add(pil);
-	if (err) {
-		device_unregister(&pil->dev);
-		return ERR_PTR(err);
-	}
-
-	return pil;
+	return 0;
 }
-EXPORT_SYMBOL(msm_pil_register);
+EXPORT_SYMBOL(pil_desc_init);
 
-void msm_pil_unregister(struct pil_device *pil)
+/**
+ * pil_desc_release() - Release a pil descriptor
+ * @desc: descriptor to free
+ */
+void pil_desc_release(struct pil_desc *desc)
 {
-	if (IS_ERR_OR_NULL(pil))
-		return;
+	struct pil_priv *priv = desc->priv;
 
-	if (get_device(&pil->dev)) {
-		mutex_lock(&pil->lock);
-		WARN_ON(pil->count);
-		flush_delayed_work_sync(&pil->proxy);
-		msm_pil_debugfs_remove(pil);
-		device_unregister(&pil->dev);
-		mutex_unlock(&pil->lock);
-		put_device(&pil->dev);
+	if (priv) {
+		flush_delayed_work(&priv->proxy);
+		wake_lock_destroy(&priv->wlock);
 	}
+	desc->priv = NULL;
+	kfree(priv);
 }
-EXPORT_SYMBOL(msm_pil_unregister);
+EXPORT_SYMBOL(pil_desc_release);
 
 static int pil_pm_notify(struct notifier_block *b, unsigned long event, void *p)
 {
@@ -675,19 +663,18 @@
 
 static int __init msm_pil_init(void)
 {
-	int ret = msm_pil_debugfs_init();
-	if (ret)
-		return ret;
-	register_pm_notifier(&pil_pm_notifier);
-	return bus_register(&pil_bus_type);
+	ion = msm_ion_client_create(UINT_MAX, "pil");
+	if (IS_ERR(ion)) /* Can't support relocatable images */
+		ion = NULL;
+	return register_pm_notifier(&pil_pm_notifier);
 }
-subsys_initcall(msm_pil_init);
+device_initcall(msm_pil_init);
 
 static void __exit msm_pil_exit(void)
 {
-	bus_unregister(&pil_bus_type);
 	unregister_pm_notifier(&pil_pm_notifier);
-	msm_pil_debugfs_exit();
+	if (ion)
+		ion_client_destroy(ion);
 }
 module_exit(msm_pil_exit);
 
diff --git a/arch/arm/mach-msm/peripheral-loader.h b/arch/arm/mach-msm/peripheral-loader.h
index 405b73f..1c2faf7 100644
--- a/arch/arm/mach-msm/peripheral-loader.h
+++ b/arch/arm/mach-msm/peripheral-loader.h
@@ -14,28 +14,33 @@
 
 struct device;
 struct module;
+struct pil_priv;
 
 /**
  * struct pil_desc - PIL descriptor
  * @name: string used for pil_get()
- * @depends_on: booted before this peripheral
  * @dev: parent device
  * @ops: callback functions
  * @owner: module the descriptor belongs to
  * @proxy_timeout: delay in ms until proxy vote is removed
+ * @flags: bitfield for image flags
+ * @priv: DON'T USE - internal only
  */
 struct pil_desc {
 	const char *name;
-	const char *depends_on;
 	struct device *dev;
 	const struct pil_reset_ops *ops;
 	struct module *owner;
 	unsigned long proxy_timeout;
+	unsigned long flags;
+#define PIL_SKIP_ENTRY_CHECK	BIT(0)
+	struct pil_priv *priv;
 };
 
 /**
  * struct pil_reset_ops - PIL operations
  * @init_image: prepare an image for authentication
+ * @mem_setup: prepare the image memory region
  * @verify_blob: authenticate a program segment, called once for each loadable
  *		 program segment (optional)
  * @proxy_vote: make proxy votes before auth_and_reset (optional)
@@ -46,6 +51,7 @@
 struct pil_reset_ops {
 	int (*init_image)(struct pil_desc *pil, const u8 *metadata,
 			  size_t size);
+	int (*mem_setup)(struct pil_desc *pil, phys_addr_t addr, size_t size);
 	int (*verify_blob)(struct pil_desc *pil, u32 phy_addr, size_t size);
 	int (*proxy_vote)(struct pil_desc *pil);
 	int (*auth_and_reset)(struct pil_desc *pil);
@@ -53,17 +59,21 @@
 	int (*shutdown)(struct pil_desc *pil);
 };
 
-struct pil_device;
-
 #ifdef CONFIG_MSM_PIL
-extern struct pil_device *msm_pil_register(struct pil_desc *desc);
-extern void msm_pil_unregister(struct pil_device *pil);
+extern int pil_desc_init(struct pil_desc *desc);
+extern int pil_boot(struct pil_desc *desc);
+extern void pil_shutdown(struct pil_desc *desc);
+extern void pil_desc_release(struct pil_desc *desc);
+extern phys_addr_t pil_get_entry_addr(struct pil_desc *desc);
 #else
-static inline struct pil_device *msm_pil_register(struct pil_desc *desc)
+static inline int pil_desc_init(struct pil_desc *desc) { return 0; }
+static inline int pil_boot(struct pil_desc *desc) { return 0; }
+static inline void pil_shutdown(struct pil_desc *desc) { }
+static inline void pil_desc_release(struct pil_desc *desc) { }
+static inline phys_addr_t pil_get_entry_addr(struct pil_desc *desc)
 {
-	return NULL;
+	return 0;
 }
-static inline void msm_pil_unregister(struct pil_device *pil) { }
 #endif
 
 #endif
diff --git a/arch/arm/mach-msm/pil-dsps.c b/arch/arm/mach-msm/pil-dsps.c
index 81f5330..519e1c9 100644
--- a/arch/arm/mach-msm/pil-dsps.c
+++ b/arch/arm/mach-msm/pil-dsps.c
@@ -13,50 +13,83 @@
 #include <linux/init.h>
 #include <linux/module.h>
 #include <linux/platform_device.h>
-#include <linux/elf.h>
 #include <linux/err.h>
 #include <linux/io.h>
 #include <linux/delay.h>
+#include <linux/atomic.h>
+#include <linux/interrupt.h>
 
-#include <mach/msm_iomap.h>
+#include <mach/subsystem_restart.h>
+#include <mach/msm_smsm.h>
 
 #include "peripheral-loader.h"
 #include "scm-pas.h"
+#include "ramdump.h"
 
-#define PPSS_RESET			(MSM_CLK_CTL_BASE + 0x2594)
+#define PPSS_RESET			0x2594
 #define PPSS_RESET_PROC_RESET		0x2
 #define PPSS_RESET_RESET		0x1
-#define PPSS_PROC_CLK_CTL		(MSM_CLK_CTL_BASE + 0x2588)
+#define PPSS_PROC_CLK_CTL		0x2588
 #define CLK_BRANCH_ENA			0x10
-#define PPSS_HCLK_CTL			(MSM_CLK_CTL_BASE + 0x2580)
-#define CLK_HALT_DFAB_STATE		(MSM_CLK_CTL_BASE + 0x2FC8)
+#define PPSS_HCLK_CTL			0x2580
+#define CLK_HALT_DFAB_STATE		0x2FC8
+
+#define PPSS_WDOG_UNMASKED_INT_EN	0x1808
+
+struct dsps_data {
+	void __iomem *base;
+	struct pil_desc desc;
+	struct subsys_device *subsys;
+	struct subsys_desc subsys_desc;
+	int crash;
+	int wdog_irq;
+	atomic_t wd_crash;
+	atomic_t crash_in_progress;
+	void __iomem *ppss_base;
+
+	void *ramdump_dev;
+	struct ramdump_segment fw_ramdump_segments[4];
+
+	void *smem_ramdump_dev;
+	struct ramdump_segment smem_ramdump_segments[1];
+};
+
+#define desc_to_drv(d) container_of(d, struct dsps_data, subsys_desc)
+#define pil_to_drv(d) container_of(d, struct dsps_data, desc)
 
 static int init_image_dsps(struct pil_desc *pil, const u8 *metadata,
 				     size_t size)
 {
+	struct dsps_data *drv = pil_to_drv(pil);
+
 	/* Bring memory and bus interface out of reset */
-	writel_relaxed(PPSS_RESET_PROC_RESET, PPSS_RESET);
-	writel_relaxed(CLK_BRANCH_ENA, PPSS_HCLK_CTL);
+	writel_relaxed(PPSS_RESET_PROC_RESET, drv->base + PPSS_RESET);
+	writel_relaxed(CLK_BRANCH_ENA, drv->base + PPSS_HCLK_CTL);
 	mb();
 	return 0;
 }
 
 static int reset_dsps(struct pil_desc *pil)
 {
-	writel_relaxed(CLK_BRANCH_ENA, PPSS_PROC_CLK_CTL);
-	while (readl_relaxed(CLK_HALT_DFAB_STATE) & BIT(18))
+	struct dsps_data *drv = pil_to_drv(pil);
+
+	writel_relaxed(CLK_BRANCH_ENA, drv->base + PPSS_PROC_CLK_CTL);
+	while (readl_relaxed(drv->base + CLK_HALT_DFAB_STATE) & BIT(18))
 		cpu_relax();
 	/* Bring DSPS out of reset */
-	writel_relaxed(0x0, PPSS_RESET);
+	writel_relaxed(0x0, drv->base + PPSS_RESET);
 	return 0;
 }
 
 static int shutdown_dsps(struct pil_desc *pil)
 {
-	writel_relaxed(PPSS_RESET_PROC_RESET | PPSS_RESET_RESET, PPSS_RESET);
+	struct dsps_data *drv = pil_to_drv(pil);
+
+	writel_relaxed(PPSS_RESET_PROC_RESET | PPSS_RESET_RESET,
+			drv->base + PPSS_RESET);
 	usleep_range(1000, 2000);
-	writel_relaxed(PPSS_RESET_PROC_RESET, PPSS_RESET);
-	writel_relaxed(0x0, PPSS_PROC_CLK_CTL);
+	writel_relaxed(PPSS_RESET_PROC_RESET, drv->base + PPSS_RESET);
+	writel_relaxed(0x0, drv->base + PPSS_PROC_CLK_CTL);
 	return 0;
 }
 
@@ -88,18 +121,167 @@
 	.shutdown = shutdown_dsps_trusted,
 };
 
+static void dsps_log_sfr(void)
+{
+	const char dflt_reason[] = "Died too early due to unknown reason";
+	char *smem_reset_reason;
+	unsigned smem_reset_size;
+
+	smem_reset_reason = smem_get_entry(SMEM_SSR_REASON_DSPS0,
+		&smem_reset_size);
+	if (smem_reset_reason != NULL && smem_reset_reason[0] != 0) {
+		smem_reset_reason[smem_reset_size-1] = 0;
+		pr_err("%s: DSPS failure: %s\nResetting DSPS\n",
+			__func__, smem_reset_reason);
+		memset(smem_reset_reason, 0, smem_reset_size);
+		wmb();
+	} else
+		pr_err("%s: DSPS failure: %s\nResetting DSPS\n",
+			__func__, dflt_reason);
+}
+
+
+static void dsps_restart_handler(struct dsps_data *drv)
+{
+	pr_debug("%s: Restart lvl %d\n",
+		__func__, get_restart_level());
+
+	if (atomic_add_return(1, &drv->crash_in_progress) > 1) {
+		pr_err("%s: DSPS already resetting. Count %d\n", __func__,
+		       atomic_read(&drv->crash_in_progress));
+	} else {
+		subsystem_restart_dev(drv->subsys);
+	}
+}
+
+static void dsps_smsm_state_cb(void *data, uint32_t old_state,
+			       uint32_t new_state)
+{
+	struct dsps_data *drv = data;
+
+	if (drv->crash == 1) {
+		pr_debug("SMSM_RESET state change ignored\n");
+		drv->crash = 0;
+	} else if (new_state & SMSM_RESET) {
+		dsps_log_sfr();
+		dsps_restart_handler(drv);
+	}
+}
+
+static int dsps_start(const struct subsys_desc *desc)
+{
+	struct dsps_data *drv = desc_to_drv(desc);
+
+	return pil_boot(&drv->desc);
+}
+
+static void dsps_stop(const struct subsys_desc *desc)
+{
+	struct dsps_data *drv = desc_to_drv(desc);
+	pil_shutdown(&drv->desc);
+}
+
+static int dsps_shutdown(const struct subsys_desc *desc)
+{
+	struct dsps_data *drv = desc_to_drv(desc);
+	disable_irq_nosync(drv->wdog_irq);
+	if (drv->ppss_base) {
+		writel_relaxed(0, drv->ppss_base + PPSS_WDOG_UNMASKED_INT_EN);
+		mb(); /* Make sure wdog is disabled before shutting down */
+	}
+	pil_shutdown(&drv->desc);
+	return 0;
+}
+
+static int dsps_powerup(const struct subsys_desc *desc)
+{
+	struct dsps_data *drv = desc_to_drv(desc);
+
+	pil_boot(&drv->desc);
+	atomic_set(&drv->crash_in_progress, 0);
+	enable_irq(drv->wdog_irq);
+
+	return 0;
+}
+
+static int dsps_ramdump(int enable, const struct subsys_desc *desc)
+{
+	int ret;
+	struct dsps_data *drv = desc_to_drv(desc);
+
+	if (!enable)
+		return 0;
+
+	ret = do_ramdump(drv->ramdump_dev,
+		drv->fw_ramdump_segments,
+		ARRAY_SIZE(drv->fw_ramdump_segments));
+	if (ret < 0) {
+		pr_err("%s: Unable to dump DSPS memory (rc = %d).\n",
+		       __func__, ret);
+		return ret;
+	}
+	ret = do_ramdump(drv->smem_ramdump_dev,
+		drv->smem_ramdump_segments,
+		ARRAY_SIZE(drv->smem_ramdump_segments));
+	if (ret < 0) {
+		pr_err("%s: Unable to dump smem memory (rc = %d).\n",
+		       __func__, ret);
+		return ret;
+	}
+	return 0;
+}
+
+static void dsps_crash_shutdown(const struct subsys_desc *desc)
+{
+	struct dsps_data *drv = desc_to_drv(desc);
+
+	disable_irq_nosync(drv->wdog_irq);
+	drv->crash = 1;
+	smsm_change_state(SMSM_DSPS_STATE, SMSM_RESET, SMSM_RESET);
+}
+
+static irqreturn_t dsps_wdog_bite_irq(int irq, void *dev_id)
+{
+	struct dsps_data *drv = dev_id;
+
+	atomic_set(&drv->wd_crash, 1);
+	dsps_log_sfr();
+	dsps_restart_handler(drv);
+	return IRQ_HANDLED;
+}
+
 static int __devinit pil_dsps_driver_probe(struct platform_device *pdev)
 {
+	struct dsps_data *drv;
 	struct pil_desc *desc;
-	struct pil_device *pil;
+	struct resource *res;
+	int ret;
 
-	desc = devm_kzalloc(&pdev->dev, sizeof(*desc), GFP_KERNEL);
-	if (!desc)
+	drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL);
+	if (!drv)
+		return -ENOMEM;
+	platform_set_drvdata(pdev, drv);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res)
+		return -EINVAL;
+	drv->base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
+	if (!drv->base)
 		return -ENOMEM;
 
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+	if (res) {
+		drv->ppss_base = devm_ioremap(&pdev->dev, res->start,
+					      resource_size(res));
+		if (!drv->ppss_base)
+			return -ENOMEM;
+	}
+
+	desc = &drv->desc;
 	desc->name = pdev->dev.platform_data;
 	desc->dev = &pdev->dev;
 	desc->owner = THIS_MODULE;
+	desc->flags = PIL_SKIP_ENTRY_CHECK;
 	if (pas_supported(PAS_DSPS) > 0) {
 		desc->ops = &pil_dsps_ops_trusted;
 		dev_info(&pdev->dev, "using secure boot\n");
@@ -107,17 +289,89 @@
 		desc->ops = &pil_dsps_ops;
 		dev_info(&pdev->dev, "using non-secure boot\n");
 	}
-	pil = msm_pil_register(desc);
-	if (IS_ERR(pil))
-		return PTR_ERR(pil);
-	platform_set_drvdata(pdev, pil);
+	ret = pil_desc_init(desc);
+	if (ret)
+		return ret;
+
+	drv->fw_ramdump_segments[0].address = 0x12000000;
+	drv->fw_ramdump_segments[0].size = 0x28000;
+	drv->fw_ramdump_segments[1].address = 0x12040000;
+	drv->fw_ramdump_segments[1].size = 0x4000;
+	drv->fw_ramdump_segments[2].address = 0x12800000;
+	drv->fw_ramdump_segments[2].size = 0x4000;
+	drv->fw_ramdump_segments[3].address = 0x8fe00000;
+	drv->fw_ramdump_segments[3].size = 0x100000;
+	drv->ramdump_dev = create_ramdump_device("dsps", &pdev->dev);
+	if (!drv->ramdump_dev) {
+		ret = -ENOMEM;
+		goto err_ramdump;
+	}
+
+	drv->smem_ramdump_segments[0].address = PHYS_OFFSET - SZ_2M;
+	drv->smem_ramdump_segments[0].size =  SZ_2M;
+	drv->smem_ramdump_dev = create_ramdump_device("smem-dsps", &pdev->dev);
+	if (!drv->smem_ramdump_dev) {
+		ret = -ENOMEM;
+		goto err_smem_ramdump;
+	}
+
+	drv->subsys_desc.name = "dsps";
+	drv->subsys_desc.dev = &pdev->dev;
+	drv->subsys_desc.owner = THIS_MODULE;
+	drv->subsys_desc.start = dsps_start;
+	drv->subsys_desc.stop = dsps_stop;
+	drv->subsys_desc.shutdown = dsps_shutdown;
+	drv->subsys_desc.powerup = dsps_powerup;
+	drv->subsys_desc.ramdump = dsps_ramdump,
+	drv->subsys_desc.crash_shutdown = dsps_crash_shutdown;
+
+	drv->subsys = subsys_register(&drv->subsys_desc);
+	if (IS_ERR(drv->subsys)) {
+		ret = PTR_ERR(drv->subsys);
+		goto err_subsys;
+	}
+
+	ret = smsm_state_cb_register(SMSM_DSPS_STATE, SMSM_RESET,
+				     dsps_smsm_state_cb, drv);
+	if (ret)
+		goto err_smsm;
+
+	drv->wdog_irq = platform_get_irq(pdev, 0);
+	if (drv->wdog_irq >= 0) {
+		ret = devm_request_irq(&pdev->dev, drv->wdog_irq,
+				dsps_wdog_bite_irq, IRQF_TRIGGER_RISING,
+				"dsps_wdog", drv);
+		if (ret) {
+			dev_err(&pdev->dev, "request_irq failed\n");
+			goto err_smsm;
+		}
+	} else {
+		drv->wdog_irq = -1;
+		dev_dbg(&pdev->dev, "ppss_wdog not supported\n");
+	}
+
 	return 0;
+
+err_smsm:
+	subsys_unregister(drv->subsys);
+err_subsys:
+	destroy_ramdump_device(drv->smem_ramdump_dev);
+err_smem_ramdump:
+	destroy_ramdump_device(drv->ramdump_dev);
+err_ramdump:
+	pil_desc_release(desc);
+	return ret;
 }
 
 static int __devexit pil_dsps_driver_exit(struct platform_device *pdev)
 {
-	struct pil_device *pil = platform_get_drvdata(pdev);
-	msm_pil_unregister(pil);
+	struct dsps_data *drv = platform_get_drvdata(pdev);
+	smsm_state_cb_deregister(SMSM_DSPS_STATE, SMSM_RESET,
+				 dsps_smsm_state_cb, drv);
+	subsys_unregister(drv->subsys);
+	destroy_ramdump_device(drv->smem_ramdump_dev);
+	destroy_ramdump_device(drv->ramdump_dev);
+	pil_desc_release(&drv->desc);
 	return 0;
 }
 
diff --git a/arch/arm/mach-msm/pil-gss.c b/arch/arm/mach-msm/pil-gss.c
index bccbce2..a6d13d0 100644
--- a/arch/arm/mach-msm/pil-gss.c
+++ b/arch/arm/mach-msm/pil-gss.c
@@ -14,7 +14,6 @@
 #include <linux/kernel.h>
 #include <linux/err.h>
 #include <linux/io.h>
-#include <linux/elf.h>
 #include <linux/delay.h>
 #include <linux/module.h>
 #include <linux/slab.h>
@@ -25,13 +24,11 @@
 #include <linux/reboot.h>
 #include <linux/interrupt.h>
 
-#include <mach/msm_iomap.h>
 #include <mach/msm_xo.h>
 #include <mach/socinfo.h>
 #include <mach/msm_bus_board.h>
 #include <mach/msm_bus.h>
 #include <mach/subsystem_restart.h>
-#include <mach/peripheral-loader.h>
 
 #include "peripheral-loader.h"
 #include "scm-pas.h"
@@ -46,13 +43,13 @@
 #define GSS_CSR_POWER_UP_DOWN	0x18
 #define GSS_CSR_CFG_HID		0x2C
 
-#define GSS_SLP_CLK_CTL		(MSM_CLK_CTL_BASE + 0x2C60)
-#define GSS_RESET		(MSM_CLK_CTL_BASE + 0x2C64)
-#define GSS_CLAMP_ENA		(MSM_CLK_CTL_BASE + 0x2C68)
-#define GSS_CXO_SRC_CTL		(MSM_CLK_CTL_BASE + 0x2C74)
+#define GSS_SLP_CLK_CTL		0x2C60
+#define GSS_RESET		0x2C64
+#define GSS_CLAMP_ENA		0x2C68
+#define GSS_CXO_SRC_CTL		0x2C74
 
-#define PLL5_STATUS		(MSM_CLK_CTL_BASE + 0x30F8)
-#define PLL_ENA_GSS		(MSM_CLK_CTL_BASE + 0x3480)
+#define PLL5_STATUS		0x30F8
+#define PLL_ENA_GSS		0x3480
 
 #define PLL5_VOTE		BIT(5)
 #define PLL_STATUS		BIT(16)
@@ -67,28 +64,19 @@
 struct gss_data {
 	void __iomem *base;
 	void __iomem *qgic2_base;
-	unsigned long start_addr;
+	void __iomem *cbase;
 	struct clk *xo;
-	struct pil_device *pil;
+	struct pil_desc pil_desc;
 	struct miscdevice misc_dev;
 	struct subsys_device *subsys;
 	struct subsys_desc subsys_desc;
 	int crash_shutdown;
 	int irq;
-	void *pil_handle;
+	void *subsys_handle;
 	struct ramdump_device *ramdump_dev;
 	struct ramdump_device *smem_ramdump_dev;
 };
 
-static int pil_gss_init_image(struct pil_desc *pil, const u8 *metadata,
-		size_t size)
-{
-	const struct elf32_hdr *ehdr = (struct elf32_hdr *)metadata;
-	struct gss_data *drv = dev_get_drvdata(pil->dev);
-	drv->start_addr = ehdr->e_entry;
-	return 0;
-}
-
 static int make_gss_proxy_votes(struct pil_desc *pil)
 {
 	int ret;
@@ -111,14 +99,15 @@
 static void gss_init(struct gss_data *drv)
 {
 	void __iomem *base = drv->base;
+	void __iomem *cbase = drv->cbase;
 
 	/* Supply clocks to GSS. */
-	writel_relaxed(XO_CLK_BRANCH_ENA, GSS_CXO_SRC_CTL);
-	writel_relaxed(SLP_CLK_BRANCH_ENA, GSS_SLP_CLK_CTL);
+	writel_relaxed(XO_CLK_BRANCH_ENA, cbase + GSS_CXO_SRC_CTL);
+	writel_relaxed(SLP_CLK_BRANCH_ENA, cbase + GSS_SLP_CLK_CTL);
 
 	/* Deassert GSS reset and clamps. */
-	writel_relaxed(0x0, GSS_RESET);
-	writel_relaxed(0x0, GSS_CLAMP_ENA);
+	writel_relaxed(0x0, cbase + GSS_RESET);
+	writel_relaxed(0x0, cbase + GSS_CLAMP_ENA);
 	mb();
 
 	/*
@@ -159,6 +148,7 @@
 {
 	struct gss_data *drv = dev_get_drvdata(pil->dev);
 	void __iomem *base = drv->base;
+	void __iomem *cbase = drv->cbase;
 	u32 regval;
 	int ret;
 
@@ -175,8 +165,8 @@
 	 * Vote PLL on in GSS's voting register and wait for it to enable.
 	 * The PLL must be enable to switch the GFMUX to a low-power source.
 	 */
-	writel_relaxed(PLL5_VOTE, PLL_ENA_GSS);
-	while ((readl_relaxed(PLL5_STATUS) & PLL_STATUS) == 0)
+	writel_relaxed(PLL5_VOTE, cbase + PLL_ENA_GSS);
+	while ((readl_relaxed(cbase + PLL5_STATUS) & PLL_STATUS) == 0)
 		cpu_relax();
 
 	/* Perform one-time GSS initialization. */
@@ -201,7 +191,7 @@
 	writel_relaxed(0x1F, base + GSS_CSR_CLK_ENABLE);
 
 	/* Clear GSS PLL votes. */
-	writel_relaxed(0, PLL_ENA_GSS);
+	writel_relaxed(0, cbase + PLL_ENA_GSS);
 	mb();
 
 	clk_disable_unprepare(drv->xo);
@@ -213,7 +203,8 @@
 {
 	struct gss_data *drv = dev_get_drvdata(pil->dev);
 	void __iomem *base = drv->base;
-	unsigned long start_addr = drv->start_addr;
+	unsigned long start_addr = pil_get_entry_addr(pil);
+	void __iomem *cbase = drv->cbase;
 	int ret;
 
 	/* Unhalt bus port. */
@@ -224,8 +215,8 @@
 	}
 
 	/* Vote PLL on in GSS's voting register and wait for it to enable. */
-	writel_relaxed(PLL5_VOTE, PLL_ENA_GSS);
-	while ((readl_relaxed(PLL5_STATUS) & PLL_STATUS) == 0)
+	writel_relaxed(PLL5_VOTE, cbase + PLL_ENA_GSS);
+	while ((readl_relaxed(cbase + PLL5_STATUS) & PLL_STATUS) == 0)
 		cpu_relax();
 
 	/* Perform GSS initialization. */
@@ -258,7 +249,6 @@
 }
 
 static struct pil_reset_ops pil_gss_ops = {
-	.init_image = pil_gss_init_image,
 	.auth_and_reset = pil_gss_reset,
 	.shutdown = pil_gss_shutdown,
 	.proxy_vote = make_gss_proxy_votes,
@@ -371,11 +361,27 @@
 	}
 }
 
+static int gss_start(const struct subsys_desc *desc)
+{
+	struct gss_data *drv;
+
+	drv = container_of(desc, struct gss_data, subsys_desc);
+	return pil_boot(&drv->pil_desc);
+}
+
+static void gss_stop(const struct subsys_desc *desc)
+{
+	struct gss_data *drv;
+
+	drv = container_of(desc, struct gss_data, subsys_desc);
+	pil_shutdown(&drv->pil_desc);
+}
+
 static int gss_shutdown(const struct subsys_desc *desc)
 {
 	struct gss_data *drv = container_of(desc, struct gss_data, subsys_desc);
 
-	pil_force_shutdown("gss");
+	pil_shutdown(&drv->pil_desc);
 	disable_irq_nosync(drv->irq);
 
 	return 0;
@@ -385,7 +391,7 @@
 {
 	struct gss_data *drv = container_of(desc, struct gss_data, subsys_desc);
 
-	pil_force_boot("gss");
+	pil_boot(&drv->pil_desc);
 	enable_irq(drv->irq);
 	return 0;
 }
@@ -443,13 +449,14 @@
 
 static int gss_open(struct inode *inode, struct file *filp)
 {
-	void *ret;
 	struct miscdevice *c = filp->private_data;
 	struct gss_data *drv = container_of(c, struct gss_data, misc_dev);
 
-	drv->pil_handle = ret = pil_get("gss");
-	if (!ret)
-		pr_debug("%s - pil_get returned NULL\n", __func__);
+	drv->subsys_handle = subsystem_get("gss");
+	if (IS_ERR(drv->subsys_handle)) {
+		pr_debug("%s - subsystem_get returned error\n", __func__);
+		return PTR_ERR(drv->subsys_handle);
+	}
 
 	return 0;
 }
@@ -459,8 +466,8 @@
 	struct miscdevice *c = filp->private_data;
 	struct gss_data *drv = container_of(c, struct gss_data, misc_dev);
 
-	pil_put(drv->pil_handle);
-	pr_debug("%s pil_put called on GSS\n", __func__);
+	subsystem_put(drv->subsys_handle);
+	pr_debug("%s subsystem_put called on GSS\n", __func__);
 
 	return 0;
 }
@@ -478,30 +485,26 @@
 	struct pil_desc *desc;
 	int ret;
 
-	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-	if (!res)
-		return -EINVAL;
-
 	drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL);
 	if (!drv)
 		return -ENOMEM;
 	platform_set_drvdata(pdev, drv);
 
-	drv->base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	drv->base = devm_request_and_ioremap(&pdev->dev, res);
 	if (!drv->base)
 		return -ENOMEM;
 
-	desc = devm_kzalloc(&pdev->dev, sizeof(*desc), GFP_KERNEL);
-	if (!desc)
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+	drv->qgic2_base = devm_request_and_ioremap(&pdev->dev, res);
+	if (!drv->qgic2_base)
 		return -ENOMEM;
 
-	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
 	if (!res)
 		return -EINVAL;
-
-	drv->qgic2_base = devm_ioremap(&pdev->dev, res->start,
-					resource_size(res));
-	if (!drv->qgic2_base)
+	drv->cbase = devm_ioremap(&pdev->dev, res->start, resource_size(res));
+	if (!drv->cbase)
 		return -ENOMEM;
 
 	drv->xo = devm_clk_get(&pdev->dev, "xo");
@@ -512,6 +515,7 @@
 	if (drv->irq < 0)
 		return drv->irq;
 
+	desc = &drv->pil_desc;
 	desc->name = "gss";
 	desc->dev = &pdev->dev;
 	desc->owner = THIS_MODULE;
@@ -524,20 +528,23 @@
 		desc->ops = &pil_gss_ops;
 		dev_info(&pdev->dev, "using non-secure boot\n");
 	}
+	ret = pil_desc_init(desc);
+	if (ret)
+		return ret;
+
 	/* Force into low power mode because hardware doesn't do this */
 	desc->ops->shutdown(desc);
 
-	drv->pil = msm_pil_register(desc);
-	if (IS_ERR(drv->pil)) {
-		return PTR_ERR(drv->pil);
-	}
-
 	ret = smsm_state_cb_register(SMSM_MODEM_STATE, SMSM_RESET,
 			smsm_state_cb, drv);
 	if (ret < 0)
 		dev_warn(&pdev->dev, "Unable to register SMSM callback\n");
 
 	drv->subsys_desc.name = "gss";
+	drv->subsys_desc.dev = &pdev->dev;
+	drv->subsys_desc.owner = THIS_MODULE;
+	drv->subsys_desc.start = gss_start;
+	drv->subsys_desc.stop = gss_stop;
 	drv->subsys_desc.shutdown = gss_shutdown;
 	drv->subsys_desc.powerup = gss_powerup;
 	drv->subsys_desc.ramdump = gss_ramdump;
@@ -556,13 +563,13 @@
 	if (ret)
 		goto err_misc;
 
-	drv->ramdump_dev = create_ramdump_device("gss");
+	drv->ramdump_dev = create_ramdump_device("gss", &pdev->dev);
 	if (!drv->ramdump_dev) {
 		ret = -ENOMEM;
 		goto err_ramdump;
 	}
 
-	drv->smem_ramdump_dev = create_ramdump_device("smem-gss");
+	drv->smem_ramdump_dev = create_ramdump_device("smem-gss", &pdev->dev);
 	if (!drv->smem_ramdump_dev) {
 		ret = -ENOMEM;
 		goto err_smem;
@@ -582,7 +589,7 @@
 err_misc:
 	subsys_unregister(drv->subsys);
 err_subsys:
-	msm_pil_unregister(drv->pil);
+	pil_desc_release(desc);
 	return ret;
 }
 
@@ -594,7 +601,7 @@
 	destroy_ramdump_device(drv->ramdump_dev);
 	misc_deregister(&drv->misc_dev);
 	subsys_unregister(drv->subsys);
-	msm_pil_unregister(drv->pil);
+	pil_desc_release(&drv->pil_desc);
 
 	return 0;
 }
diff --git a/arch/arm/mach-msm/pil-mba.c b/arch/arm/mach-msm/pil-mba.c
deleted file mode 100644
index 0207f0b..0000000
--- a/arch/arm/mach-msm/pil-mba.c
+++ /dev/null
@@ -1,256 +0,0 @@
-/*
- * Copyright (c) 2012, Code Aurora Forum. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- */
-
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/io.h>
-#include <linux/iopoll.h>
-#include <linux/ioport.h>
-#include <linux/elf.h>
-#include <linux/delay.h>
-#include <linux/sched.h>
-#include <linux/clk.h>
-#include <linux/err.h>
-#include <linux/of.h>
-
-#include "peripheral-loader.h"
-
-#define RMB_MBA_COMMAND			0x08
-#define RMB_MBA_STATUS			0x0C
-#define RMB_PMI_META_DATA		0x10
-#define RMB_PMI_CODE_START		0x14
-#define RMB_PMI_CODE_LENGTH		0x18
-
-#define CMD_META_DATA_READY		0x1
-#define CMD_LOAD_READY			0x2
-
-#define STATUS_META_DATA_AUTH_SUCCESS	0x3
-#define STATUS_AUTH_COMPLETE		0x4
-
-#define PROXY_TIMEOUT_MS		10000
-#define POLL_INTERVAL_US		50
-
-static int modem_auth_timeout_ms = 10000;
-module_param(modem_auth_timeout_ms, int, S_IRUGO | S_IWUSR);
-
-struct mba_data {
-	void __iomem *reg_base;
-	void __iomem *metadata_base;
-	unsigned long metadata_phys;
-	struct pil_device *pil;
-	struct clk *xo;
-	u32 img_length;
-};
-
-static int pil_mba_make_proxy_votes(struct pil_desc *pil)
-{
-	int ret;
-	struct mba_data *drv = dev_get_drvdata(pil->dev);
-
-	ret = clk_prepare_enable(drv->xo);
-	if (ret) {
-		dev_err(pil->dev, "Failed to enable XO\n");
-		return ret;
-	}
-	return 0;
-}
-
-static void pil_mba_remove_proxy_votes(struct pil_desc *pil)
-{
-	struct mba_data *drv = dev_get_drvdata(pil->dev);
-	clk_disable_unprepare(drv->xo);
-}
-
-static int pil_mba_init_image(struct pil_desc *pil,
-			      const u8 *metadata, size_t size)
-{
-	struct mba_data *drv = dev_get_drvdata(pil->dev);
-	s32 status;
-	int ret;
-
-	/* Copy metadata to assigned shared buffer location */
-	memcpy(drv->metadata_base, metadata, size);
-
-	/* Initialize length counter to 0 */
-	writel_relaxed(0, drv->reg_base + RMB_PMI_CODE_LENGTH);
-	drv->img_length = 0;
-
-	/* Pass address of meta-data to the MBA and perform authentication */
-	writel_relaxed(drv->metadata_phys, drv->reg_base + RMB_PMI_META_DATA);
-	writel_relaxed(CMD_META_DATA_READY, drv->reg_base + RMB_MBA_COMMAND);
-	ret = readl_poll_timeout(drv->reg_base + RMB_MBA_STATUS, status,
-		status == STATUS_META_DATA_AUTH_SUCCESS || status < 0,
-		POLL_INTERVAL_US, modem_auth_timeout_ms * 1000);
-	if (ret) {
-		dev_err(pil->dev, "MBA authentication timed out\n");
-	} else if (status < 0) {
-		dev_err(pil->dev, "MBA returned error %d\n", status);
-		ret = -EINVAL;
-	}
-
-	return ret;
-}
-
-static int pil_mba_verify_blob(struct pil_desc *pil, u32 phy_addr,
-			       size_t size)
-{
-	struct mba_data *drv = dev_get_drvdata(pil->dev);
-	s32 status;
-
-	/* Begin image authentication */
-	if (drv->img_length == 0) {
-		writel_relaxed(phy_addr, drv->reg_base + RMB_PMI_CODE_START);
-		writel_relaxed(CMD_LOAD_READY, drv->reg_base + RMB_MBA_COMMAND);
-	}
-	/* Increment length counter */
-	drv->img_length += size;
-	writel_relaxed(drv->img_length, drv->reg_base + RMB_PMI_CODE_LENGTH);
-
-	status = readl_relaxed(drv->reg_base + RMB_MBA_STATUS);
-	if (status < 0) {
-		dev_err(pil->dev, "MBA returned error %d\n", status);
-		return -EINVAL;
-	}
-
-	return 0;
-}
-
-static int pil_mba_auth(struct pil_desc *pil)
-{
-	struct mba_data *drv = dev_get_drvdata(pil->dev);
-	int ret;
-	s32 status;
-
-	/* Wait for all segments to be authenticated or an error to occur */
-	ret = readl_poll_timeout(drv->reg_base + RMB_MBA_STATUS, status,
-			status == STATUS_AUTH_COMPLETE || status < 0,
-			50, modem_auth_timeout_ms * 1000);
-	if (ret) {
-		dev_err(pil->dev, "MBA authentication timed out\n");
-	} else if (status < 0) {
-		dev_err(pil->dev, "MBA returned error %d\n", status);
-		ret = -EINVAL;
-	}
-
-	return ret;
-}
-
-static int pil_mba_shutdown(struct pil_desc *pil)
-{
-	return 0;
-}
-
-static struct pil_reset_ops pil_mba_ops = {
-	.init_image = pil_mba_init_image,
-	.proxy_vote = pil_mba_make_proxy_votes,
-	.proxy_unvote = pil_mba_remove_proxy_votes,
-	.verify_blob = pil_mba_verify_blob,
-	.auth_and_reset = pil_mba_auth,
-	.shutdown = pil_mba_shutdown,
-};
-
-static int __devinit pil_mba_driver_probe(struct platform_device *pdev)
-{
-	struct mba_data *drv;
-	struct resource *res;
-	struct pil_desc *desc;
-	int ret;
-
-	drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL);
-	if (!drv)
-		return -ENOMEM;
-	platform_set_drvdata(pdev, drv);
-
-	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "rmb_base");
-	if (!res)
-		return -EINVAL;
-	drv->reg_base = devm_ioremap(&pdev->dev, res->start,
-				     resource_size(res));
-	if (!drv->reg_base)
-		return -ENOMEM;
-
-	res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
-					    "metadata_base");
-	if (res) {
-		drv->metadata_base = devm_ioremap(&pdev->dev, res->start,
-						  resource_size(res));
-		if (!drv->metadata_base)
-			return -ENOMEM;
-		drv->metadata_phys = res->start;
-	}
-
-	desc = devm_kzalloc(&pdev->dev, sizeof(*desc), GFP_KERNEL);
-	if (!drv)
-		return -ENOMEM;
-
-	ret = of_property_read_string(pdev->dev.of_node, "qcom,firmware-name",
-				      &desc->name);
-	if (ret)
-		return ret;
-
-	of_property_read_string(pdev->dev.of_node, "qcom,depends-on",
-				      &desc->depends_on);
-
-	drv->xo = devm_clk_get(&pdev->dev, "xo");
-	if (IS_ERR(drv->xo))
-		return PTR_ERR(drv->xo);
-
-	desc->dev = &pdev->dev;
-	desc->ops = &pil_mba_ops;
-	desc->owner = THIS_MODULE;
-	desc->proxy_timeout = PROXY_TIMEOUT_MS;
-
-	drv->pil = msm_pil_register(desc);
-	if (IS_ERR(drv->pil))
-		return PTR_ERR(drv->pil);
-
-	return 0;
-}
-
-static int __devexit pil_mba_driver_exit(struct platform_device *pdev)
-{
-	struct mba_data *drv = platform_get_drvdata(pdev);
-	msm_pil_unregister(drv->pil);
-	return 0;
-}
-
-static struct of_device_id mba_match_table[] = {
-	{ .compatible = "qcom,pil-mba" },
-	{}
-};
-
-struct platform_driver pil_mba_driver = {
-	.probe = pil_mba_driver_probe,
-	.remove = __devexit_p(pil_mba_driver_exit),
-	.driver = {
-		.name = "pil-mba",
-		.of_match_table = mba_match_table,
-		.owner = THIS_MODULE,
-	},
-};
-
-static int __init pil_mba_init(void)
-{
-	return platform_driver_register(&pil_mba_driver);
-}
-module_init(pil_mba_init);
-
-static void __exit pil_mba_exit(void)
-{
-	platform_driver_unregister(&pil_mba_driver);
-}
-module_exit(pil_mba_exit);
-
-MODULE_DESCRIPTION("Support for modem boot using the Modem Boot Authenticator");
-MODULE_LICENSE("GPL v2");
diff --git a/arch/arm/mach-msm/pil-modem.c b/arch/arm/mach-msm/pil-modem.c
index ecb3800..d3c832b 100644
--- a/arch/arm/mach-msm/pil-modem.c
+++ b/arch/arm/mach-msm/pil-modem.c
@@ -15,7 +15,6 @@
 #include <linux/platform_device.h>
 #include <linux/io.h>
 #include <linux/ioport.h>
-#include <linux/elf.h>
 #include <linux/delay.h>
 #include <linux/err.h>
 #include <linux/clk.h>
@@ -23,10 +22,8 @@
 #include <linux/interrupt.h>
 #include <linux/reboot.h>
 
-#include <mach/msm_iomap.h>
 #include <mach/subsystem_restart.h>
 #include <mach/msm_smsm.h>
-#include <mach/peripheral-loader.h>
 
 #include "modem_notifier.h"
 #include "peripheral-loader.h"
@@ -34,37 +31,38 @@
 #include "ramdump.h"
 
 #define MARM_BOOT_CONTROL		0x0010
-#define MARM_RESET			(MSM_CLK_CTL_BASE + 0x2BD4)
-#define MAHB0_SFAB_PORT_RESET		(MSM_CLK_CTL_BASE + 0x2304)
-#define MARM_CLK_BRANCH_ENA_VOTE	(MSM_CLK_CTL_BASE + 0x3000)
-#define MARM_CLK_SRC0_NS		(MSM_CLK_CTL_BASE + 0x2BC0)
-#define MARM_CLK_SRC1_NS		(MSM_CLK_CTL_BASE + 0x2BC4)
-#define MARM_CLK_SRC_CTL		(MSM_CLK_CTL_BASE + 0x2BC8)
-#define MARM_CLK_CTL			(MSM_CLK_CTL_BASE + 0x2BCC)
-#define SFAB_MSS_S_HCLK_CTL		(MSM_CLK_CTL_BASE + 0x2C00)
-#define MSS_MODEM_CXO_CLK_CTL		(MSM_CLK_CTL_BASE + 0x2C44)
-#define MSS_SLP_CLK_CTL			(MSM_CLK_CTL_BASE + 0x2C60)
-#define MSS_MARM_SYS_REF_CLK_CTL	(MSM_CLK_CTL_BASE + 0x2C64)
-#define MAHB0_CLK_CTL			(MSM_CLK_CTL_BASE + 0x2300)
-#define MAHB1_CLK_CTL			(MSM_CLK_CTL_BASE + 0x2BE4)
-#define MAHB2_CLK_CTL			(MSM_CLK_CTL_BASE + 0x2C20)
-#define MAHB1_NS			(MSM_CLK_CTL_BASE + 0x2BE0)
-#define MARM_CLK_FS			(MSM_CLK_CTL_BASE + 0x2BD0)
-#define MAHB2_CLK_FS			(MSM_CLK_CTL_BASE + 0x2C24)
-#define PLL_ENA_MARM			(MSM_CLK_CTL_BASE + 0x3500)
-#define PLL8_STATUS			(MSM_CLK_CTL_BASE + 0x3158)
-#define CLK_HALT_MSS_SMPSS_MISC_STATE	(MSM_CLK_CTL_BASE + 0x2FDC)
-#define MSS_MODEM_RESET			(MSM_CLK_CTL_BASE + 0x2C48)
+#define MARM_RESET			0x2BD4
+#define MAHB0_SFAB_PORT_RESET		0x2304
+#define MARM_CLK_BRANCH_ENA_VOTE	0x3000
+#define MARM_CLK_SRC0_NS		0x2BC0
+#define MARM_CLK_SRC1_NS		0x2BC4
+#define MARM_CLK_SRC_CTL		0x2BC8
+#define MARM_CLK_CTL			0x2BCC
+#define SFAB_MSS_S_HCLK_CTL		0x2C00
+#define MSS_MODEM_CXO_CLK_CTL		0x2C44
+#define MSS_SLP_CLK_CTL			0x2C60
+#define MSS_MARM_SYS_REF_CLK_CTL	0x2C64
+#define MAHB0_CLK_CTL			0x2300
+#define MAHB1_CLK_CTL			0x2BE4
+#define MAHB2_CLK_CTL			0x2C20
+#define MAHB1_NS			0x2BE0
+#define MARM_CLK_FS			0x2BD0
+#define MAHB2_CLK_FS			0x2C24
+#define PLL_ENA_MARM			0x3500
+#define PLL8_STATUS			0x3158
+#define CLK_HALT_MSS_SMPSS_MISC_STATE	0x2FDC
+#define MSS_MODEM_RESET			0x2C48
 
 struct modem_data {
 	void __iomem *base;
 	void __iomem *wdog;
-	unsigned long start_addr;
+	void __iomem *cbase;
 	struct pil_device *pil;
 	struct clk *xo;
 	struct notifier_block notifier;
 	int ignore_smsm_ack;
 	int irq;
+	struct pil_desc pil_desc;
 	struct subsys_device *subsys;
 	struct subsys_desc subsys_desc;
 	struct delayed_work unlock_work;
@@ -91,90 +89,82 @@
 	clk_disable_unprepare(drv->xo);
 }
 
-static int modem_init_image(struct pil_desc *pil, const u8 *metadata,
-		size_t size)
-{
-	const struct elf32_hdr *ehdr = (struct elf32_hdr *)metadata;
-	struct modem_data *drv = dev_get_drvdata(pil->dev);
-	drv->start_addr = ehdr->e_entry;
-	return 0;
-}
-
 static int modem_reset(struct pil_desc *pil)
 {
 	u32 reg;
 	const struct modem_data *drv = dev_get_drvdata(pil->dev);
+	unsigned long start_addr = pil_get_entry_addr(pil);
 
 	/* Put modem AHB0,1,2 clocks into reset */
-	writel_relaxed(BIT(0) | BIT(1), MAHB0_SFAB_PORT_RESET);
-	writel_relaxed(BIT(7), MAHB1_CLK_CTL);
-	writel_relaxed(BIT(7), MAHB2_CLK_CTL);
+	writel_relaxed(BIT(0) | BIT(1), drv->cbase + MAHB0_SFAB_PORT_RESET);
+	writel_relaxed(BIT(7), drv->cbase + MAHB1_CLK_CTL);
+	writel_relaxed(BIT(7), drv->cbase + MAHB2_CLK_CTL);
 
 	/* Vote for pll8 on behalf of the modem */
-	reg = readl_relaxed(PLL_ENA_MARM);
+	reg = readl_relaxed(drv->cbase + PLL_ENA_MARM);
 	reg |= BIT(8);
-	writel_relaxed(reg, PLL_ENA_MARM);
+	writel_relaxed(reg, drv->cbase + PLL_ENA_MARM);
 
 	/* Wait for PLL8 to enable */
-	while (!(readl_relaxed(PLL8_STATUS) & BIT(16)))
+	while (!(readl_relaxed(drv->cbase + PLL8_STATUS) & BIT(16)))
 		cpu_relax();
 
 	/* Set MAHB1 divider to Div-5 to run MAHB1,2 and sfab at 79.8 Mhz*/
-	writel_relaxed(0x4, MAHB1_NS);
+	writel_relaxed(0x4, drv->cbase + MAHB1_NS);
 
 	/* Vote for modem AHB1 and 2 clocks to be on on behalf of the modem */
-	reg = readl_relaxed(MARM_CLK_BRANCH_ENA_VOTE);
+	reg = readl_relaxed(drv->cbase + MARM_CLK_BRANCH_ENA_VOTE);
 	reg |= BIT(0) | BIT(1);
-	writel_relaxed(reg, MARM_CLK_BRANCH_ENA_VOTE);
+	writel_relaxed(reg, drv->cbase + MARM_CLK_BRANCH_ENA_VOTE);
 
 	/* Source marm_clk off of PLL8 */
-	reg = readl_relaxed(MARM_CLK_SRC_CTL);
+	reg = readl_relaxed(drv->cbase + MARM_CLK_SRC_CTL);
 	if ((reg & 0x1) == 0) {
-		writel_relaxed(0x3, MARM_CLK_SRC1_NS);
+		writel_relaxed(0x3, drv->cbase + MARM_CLK_SRC1_NS);
 		reg |= 0x1;
 	} else {
-		writel_relaxed(0x3, MARM_CLK_SRC0_NS);
+		writel_relaxed(0x3, drv->cbase + MARM_CLK_SRC0_NS);
 		reg &= ~0x1;
 	}
-	writel_relaxed(reg | 0x2, MARM_CLK_SRC_CTL);
+	writel_relaxed(reg | 0x2, drv->cbase + MARM_CLK_SRC_CTL);
 
 	/*
 	 * Force core on and periph on signals to remain active during halt
 	 * for marm_clk and mahb2_clk
 	 */
-	writel_relaxed(0x6F, MARM_CLK_FS);
-	writel_relaxed(0x6F, MAHB2_CLK_FS);
+	writel_relaxed(0x6F, drv->cbase + MARM_CLK_FS);
+	writel_relaxed(0x6F, drv->cbase + MAHB2_CLK_FS);
 
 	/*
 	 * Enable all of the marm_clk branches, cxo sourced marm branches,
 	 * and sleep clock branches
 	 */
-	writel_relaxed(0x10, MARM_CLK_CTL);
-	writel_relaxed(0x10, MAHB0_CLK_CTL);
-	writel_relaxed(0x10, SFAB_MSS_S_HCLK_CTL);
-	writel_relaxed(0x10, MSS_MODEM_CXO_CLK_CTL);
-	writel_relaxed(0x10, MSS_SLP_CLK_CTL);
-	writel_relaxed(0x10, MSS_MARM_SYS_REF_CLK_CTL);
+	writel_relaxed(0x10, drv->cbase + MARM_CLK_CTL);
+	writel_relaxed(0x10, drv->cbase + MAHB0_CLK_CTL);
+	writel_relaxed(0x10, drv->cbase + SFAB_MSS_S_HCLK_CTL);
+	writel_relaxed(0x10, drv->cbase + MSS_MODEM_CXO_CLK_CTL);
+	writel_relaxed(0x10, drv->cbase + MSS_SLP_CLK_CTL);
+	writel_relaxed(0x10, drv->cbase + MSS_MARM_SYS_REF_CLK_CTL);
 
 	/* Wait for above clocks to be turned on */
-	while (readl_relaxed(CLK_HALT_MSS_SMPSS_MISC_STATE) & (BIT(7) | BIT(8) |
-				BIT(9) | BIT(10) | BIT(4) | BIT(6)))
+	while (readl_relaxed(drv->cbase + CLK_HALT_MSS_SMPSS_MISC_STATE) &
+			(BIT(7) | BIT(8) | BIT(9) | BIT(10) | BIT(4) | BIT(6)))
 		cpu_relax();
 
 	/* Take MAHB0,1,2 clocks out of reset */
-	writel_relaxed(0x0, MAHB2_CLK_CTL);
-	writel_relaxed(0x0, MAHB1_CLK_CTL);
-	writel_relaxed(0x0, MAHB0_SFAB_PORT_RESET);
+	writel_relaxed(0x0, drv->cbase + MAHB2_CLK_CTL);
+	writel_relaxed(0x0, drv->cbase + MAHB1_CLK_CTL);
+	writel_relaxed(0x0, drv->cbase + MAHB0_SFAB_PORT_RESET);
 	mb();
 
 	/* Setup exception vector table base address */
-	writel_relaxed(drv->start_addr | 0x1, drv->base + MARM_BOOT_CONTROL);
+	writel_relaxed(start_addr | 0x1, drv->base + MARM_BOOT_CONTROL);
 
 	/* Wait for vector table to be setup */
 	mb();
 
 	/* Bring modem out of reset */
-	writel_relaxed(0x0, MARM_RESET);
+	writel_relaxed(0x0, drv->cbase + MARM_RESET);
 
 	return 0;
 }
@@ -182,44 +172,44 @@
 static int modem_pil_shutdown(struct pil_desc *pil)
 {
 	u32 reg;
+	const struct modem_data *drv = dev_get_drvdata(pil->dev);
 
 	/* Put modem into reset */
-	writel_relaxed(0x1, MARM_RESET);
+	writel_relaxed(0x1, drv->cbase + MARM_RESET);
 	mb();
 
 	/* Put modem AHB0,1,2 clocks into reset */
-	writel_relaxed(BIT(0) | BIT(1), MAHB0_SFAB_PORT_RESET);
-	writel_relaxed(BIT(7), MAHB1_CLK_CTL);
-	writel_relaxed(BIT(7), MAHB2_CLK_CTL);
+	writel_relaxed(BIT(0) | BIT(1), drv->cbase + MAHB0_SFAB_PORT_RESET);
+	writel_relaxed(BIT(7), drv->cbase + MAHB1_CLK_CTL);
+	writel_relaxed(BIT(7), drv->cbase + MAHB2_CLK_CTL);
 	mb();
 
 	/*
 	 * Disable all of the marm_clk branches, cxo sourced marm branches,
 	 * and sleep clock branches
 	 */
-	writel_relaxed(0x0, MARM_CLK_CTL);
-	writel_relaxed(0x0, MAHB0_CLK_CTL);
-	writel_relaxed(0x0, SFAB_MSS_S_HCLK_CTL);
-	writel_relaxed(0x0, MSS_MODEM_CXO_CLK_CTL);
-	writel_relaxed(0x0, MSS_SLP_CLK_CTL);
-	writel_relaxed(0x0, MSS_MARM_SYS_REF_CLK_CTL);
+	writel_relaxed(0x0, drv->cbase + MARM_CLK_CTL);
+	writel_relaxed(0x0, drv->cbase + MAHB0_CLK_CTL);
+	writel_relaxed(0x0, drv->cbase + SFAB_MSS_S_HCLK_CTL);
+	writel_relaxed(0x0, drv->cbase + MSS_MODEM_CXO_CLK_CTL);
+	writel_relaxed(0x0, drv->cbase + MSS_SLP_CLK_CTL);
+	writel_relaxed(0x0, drv->cbase + MSS_MARM_SYS_REF_CLK_CTL);
 
 	/* Disable marm_clk */
-	reg = readl_relaxed(MARM_CLK_SRC_CTL);
+	reg = readl_relaxed(drv->cbase + MARM_CLK_SRC_CTL);
 	reg &= ~0x2;
-	writel_relaxed(reg, MARM_CLK_SRC_CTL);
+	writel_relaxed(reg, drv->cbase + MARM_CLK_SRC_CTL);
 
 	/* Clear modem's votes for ahb clocks */
-	writel_relaxed(0x0, MARM_CLK_BRANCH_ENA_VOTE);
+	writel_relaxed(0x0, drv->cbase + MARM_CLK_BRANCH_ENA_VOTE);
 
 	/* Clear modem's votes for PLLs */
-	writel_relaxed(0x0, PLL_ENA_MARM);
+	writel_relaxed(0x0, drv->cbase + PLL_ENA_MARM);
 
 	return 0;
 }
 
 static struct pil_reset_ops pil_modem_ops = {
-	.init_image = modem_init_image,
 	.auth_and_reset = modem_reset,
 	.shutdown = modem_pil_shutdown,
 	.proxy_vote = make_modem_proxy_votes,
@@ -284,7 +274,7 @@
 
 	drv = container_of(dwork, struct modem_data, unlock_work);
 	/* The unlock didn't work, clear the reset */
-	writel_relaxed(0x0, MSS_MODEM_RESET);
+	writel_relaxed(0x0, drv->cbase + MSS_MODEM_RESET);
 	mb();
 
 	subsystem_restart_dev(drv->subsys);
@@ -316,7 +306,7 @@
 
 		pr_err("Modem AHB locked up. Trying to free up modem!\n");
 
-		writel_relaxed(0x3, MSS_MODEM_RESET);
+		writel_relaxed(0x3, drv->cbase + MSS_MODEM_RESET);
 		/*
 		 * If we are still alive (allowing for the 5 second
 		 * delayed-panic-reboot), the modem is either still wedged or
@@ -342,6 +332,22 @@
 	return NOTIFY_DONE;
 }
 
+static int modem_start(const struct subsys_desc *subsys)
+{
+	struct modem_data *drv;
+
+	drv = container_of(subsys, struct modem_data, subsys_desc);
+	return pil_boot(&drv->pil_desc);
+}
+
+static void modem_stop(const struct subsys_desc *subsys)
+{
+	struct modem_data *drv;
+
+	drv = container_of(subsys, struct modem_data, subsys_desc);
+	pil_shutdown(&drv->pil_desc);
+}
+
 static int modem_shutdown(const struct subsys_desc *subsys)
 {
 	struct modem_data *drv;
@@ -369,7 +375,7 @@
 	/* Wait here to allow the modem to clean up caches, etc. */
 	msleep(20);
 
-	pil_force_shutdown("modem");
+	pil_shutdown(&drv->pil_desc);
 	disable_irq_nosync(drv->irq);
 
 	return 0;
@@ -381,7 +387,7 @@
 	int ret;
 
 	drv = container_of(subsys, struct modem_data, subsys_desc);
-	ret = pil_force_boot("modem");
+	ret = pil_boot(&drv->pil_desc);
 	enable_irq(drv->irq);
 
 	return ret;
@@ -411,10 +417,6 @@
 	struct pil_desc *desc;
 	int ret;
 
-	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-	if (!res)
-		return -EINVAL;
-
 	drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL);
 	if (!drv)
 		return -ENOMEM;
@@ -424,28 +426,30 @@
 	if (drv->irq < 0)
 		return drv->irq;
 
-	drv->base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
-	if (!drv->base)
-		return -ENOMEM;
-
 	drv->xo = devm_clk_get(&pdev->dev, "xo");
 	if (IS_ERR(drv->xo))
 		return PTR_ERR(drv->xo);
 
-	desc = devm_kzalloc(&pdev->dev, sizeof(*desc), GFP_KERNEL);
-	if (!desc)
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	drv->base = devm_request_and_ioremap(&pdev->dev, res);
+	if (!drv->base)
 		return -ENOMEM;
 
 	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
-	if (!res)
-		return -EINVAL;
-
-	drv->wdog = devm_ioremap(&pdev->dev, res->start, resource_size(res));
+	drv->wdog = devm_request_and_ioremap(&pdev->dev, res);
 	if (!drv->wdog)
 		return -ENOMEM;
 
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
+	if (!res)
+		return -EINVAL;
+
+	drv->cbase = devm_ioremap(&pdev->dev, res->start, resource_size(res));
+	if (!drv->cbase)
+		return -ENOMEM;
+
+	desc = &drv->pil_desc;
 	desc->name = "modem";
-	desc->depends_on = "q6";
 	desc->dev = &pdev->dev;
 	desc->owner = THIS_MODULE;
 	desc->proxy_timeout = 10000;
@@ -457,9 +461,9 @@
 		desc->ops = &pil_modem_ops;
 		dev_info(&pdev->dev, "using non-secure boot\n");
 	}
-	drv->pil = msm_pil_register(desc);
-	if (IS_ERR(drv->pil))
-		return PTR_ERR(drv->pil);
+	ret = pil_desc_init(desc);
+	if (ret)
+		return ret;
 
 	drv->notifier.notifier_call = modem_notif_handler,
 	ret = modem_register_notifier(&drv->notifier);
@@ -467,6 +471,11 @@
 		goto err_notify;
 
 	drv->subsys_desc.name = "modem";
+	drv->subsys_desc.depends_on = "adsp";
+	drv->subsys_desc.dev = &pdev->dev;
+	drv->subsys_desc.owner = THIS_MODULE;
+	drv->subsys_desc.start = modem_start;
+	drv->subsys_desc.stop = modem_stop;
 	drv->subsys_desc.shutdown = modem_shutdown;
 	drv->subsys_desc.powerup = modem_powerup;
 	drv->subsys_desc.ramdump = modem_ramdump;
@@ -481,7 +490,7 @@
 		goto err_subsys;
 	}
 
-	drv->ramdump_dev = create_ramdump_device("modem");
+	drv->ramdump_dev = create_ramdump_device("modem", &pdev->dev);
 	if (!drv->ramdump_dev) {
 		ret = -ENOMEM;
 		goto err_ramdump;
@@ -500,7 +509,7 @@
 err_subsys:
 	modem_unregister_notifier(&drv->notifier);
 err_notify:
-	msm_pil_unregister(drv->pil);
+	pil_desc_release(desc);
 	return ret;
 }
 
@@ -511,7 +520,7 @@
 	destroy_ramdump_device(drv->ramdump_dev);
 	subsys_unregister(drv->subsys);
 	modem_unregister_notifier(&drv->notifier);
-	msm_pil_unregister(drv->pil);
+	pil_desc_release(&drv->pil_desc);
 
 	return 0;
 }
diff --git a/arch/arm/mach-msm/pil-pronto.c b/arch/arm/mach-msm/pil-pronto.c
index 1e39043..49fe182 100644
--- a/arch/arm/mach-msm/pil-pronto.c
+++ b/arch/arm/mach-msm/pil-pronto.c
@@ -13,7 +13,6 @@
 #include <linux/kernel.h>
 #include <linux/err.h>
 #include <linux/io.h>
-#include <linux/elf.h>
 #include <linux/delay.h>
 #include <linux/module.h>
 #include <linux/slab.h>
@@ -22,6 +21,13 @@
 #include <linux/iopoll.h>
 #include <linux/of.h>
 #include <linux/regulator/consumer.h>
+#include <linux/interrupt.h>
+#include <linux/jiffies.h>
+#include <linux/workqueue.h>
+#include <linux/wcnss_wlan.h>
+
+#include <mach/subsystem_restart.h>
+#include <mach/msm_smsm.h>
 
 #include "peripheral-loader.h"
 #include "scm-pas.h"
@@ -65,10 +71,16 @@
 	void __iomem *base;
 	void __iomem *reset_base;
 	void __iomem *axi_halt_base;
-	unsigned long start_addr;
 	struct pil_device *pil;
+	struct pil_desc desc;
+	struct subsys_device *subsys;
+	struct subsys_desc subsys_desc;
 	struct clk *cxo;
 	struct regulator *vreg;
+	bool restart_inprogress;
+	bool crash;
+	struct delayed_work cancel_vote_work;
+	int irq;
 };
 
 static int pil_pronto_make_proxy_vote(struct pil_desc *pil)
@@ -100,22 +112,13 @@
 	clk_disable_unprepare(drv->cxo);
 }
 
-static int pil_pronto_init_image(struct pil_desc *pil, const u8 *metadata,
-		size_t size)
-{
-	const struct elf32_hdr *ehdr = (struct elf32_hdr *)metadata;
-	struct pronto_data *drv = dev_get_drvdata(pil->dev);
-	drv->start_addr = ehdr->e_entry;
-	return 0;
-}
-
 static int pil_pronto_reset(struct pil_desc *pil)
 {
 	u32 reg;
 	int rc;
 	struct pronto_data *drv = dev_get_drvdata(pil->dev);
 	void __iomem *base = drv->base;
-	unsigned long start_addr = drv->start_addr;
+	unsigned long start_addr = pil_get_entry_addr(pil);
 
 	/* Deassert reset to subsystem and wait for propagation */
 	reg = readl_relaxed(drv->reset_base);
@@ -218,13 +221,149 @@
 }
 
 static struct pil_reset_ops pil_pronto_ops = {
-	.init_image = pil_pronto_init_image,
 	.auth_and_reset = pil_pronto_reset,
 	.shutdown = pil_pronto_shutdown,
 	.proxy_vote = pil_pronto_make_proxy_vote,
 	.proxy_unvote = pil_pronto_remove_proxy_vote,
 };
 
+#define subsys_to_drv(d) container_of(d, struct pronto_data, subsys_desc)
+
+static int pronto_start(const struct subsys_desc *desc)
+{
+	struct pronto_data *drv = subsys_to_drv(desc);
+	return pil_boot(&drv->desc);
+}
+
+static void pronto_stop(const struct subsys_desc *desc)
+{
+	struct pronto_data *drv = subsys_to_drv(desc);
+	pil_shutdown(&drv->desc);
+}
+
+static void log_wcnss_sfr(void)
+{
+	char *smem_reset_reason;
+	unsigned smem_reset_size;
+
+	smem_reset_reason = smem_get_entry(SMEM_SSR_REASON_WCNSS0,
+					   &smem_reset_size);
+
+	if (!smem_reset_reason || !smem_reset_size) {
+		pr_err("wcnss subsystem failure reason:\n"
+		       "(unknown, smem_get_entry failed)");
+	} else if (!smem_reset_reason[0]) {
+		pr_err("wcnss subsystem failure reason:\n"
+		       "(unknown, init string found)");
+	} else {
+		pr_err("wcnss subsystem failure reason: %.81s\n",
+				smem_reset_reason);
+		memset(smem_reset_reason, 0, smem_reset_size);
+		wmb();
+	}
+}
+
+static void restart_wcnss(struct pronto_data *drv)
+{
+	log_wcnss_sfr();
+	subsystem_restart_dev(drv->subsys);
+}
+
+static void smsm_state_cb_hdlr(void *data, uint32_t old_state,
+					uint32_t new_state)
+{
+	struct pronto_data *drv = data;
+
+	drv->crash = true;
+
+	pr_err("wcnss smsm state changed\n");
+
+	if (!(new_state & SMSM_RESET))
+		return;
+
+	if (drv->restart_inprogress) {
+		pr_err("wcnss: Ignoring smsm reset req, restart in progress\n");
+		return;
+	}
+
+	drv->restart_inprogress = true;
+	restart_wcnss(drv);
+}
+
+static irqreturn_t wcnss_wdog_bite_irq_hdlr(int irq, void *dev_id)
+{
+	struct pronto_data *drv = dev_id;
+
+	drv->crash = true;
+
+	if (drv->restart_inprogress) {
+		pr_err("Ignoring wcnss bite irq, restart in progress\n");
+		return IRQ_HANDLED;
+	}
+
+	drv->restart_inprogress = true;
+	restart_wcnss(drv);
+
+	return IRQ_HANDLED;
+}
+
+static void wcnss_post_bootup(struct work_struct *work)
+{
+	struct platform_device *pdev = wcnss_get_platform_device();
+	struct wcnss_wlan_config *pwlanconfig = wcnss_get_wlan_config();
+
+	wcnss_wlan_power(&pdev->dev, pwlanconfig, WCNSS_WLAN_SWITCH_OFF);
+}
+
+static int wcnss_shutdown(const struct subsys_desc *subsys)
+{
+	struct pronto_data *drv = subsys_to_drv(subsys);
+
+	pil_shutdown(&drv->desc);
+	flush_delayed_work(&drv->cancel_vote_work);
+	wcnss_flush_delayed_boot_votes();
+	disable_irq_nosync(drv->irq);
+
+	return 0;
+}
+
+static int wcnss_powerup(const struct subsys_desc *subsys)
+{
+	struct pronto_data *drv = subsys_to_drv(subsys);
+	struct platform_device *pdev = wcnss_get_platform_device();
+	struct wcnss_wlan_config *pwlanconfig = wcnss_get_wlan_config();
+	int    ret = -1;
+
+	if (pdev && pwlanconfig)
+		ret = wcnss_wlan_power(&pdev->dev, pwlanconfig,
+					WCNSS_WLAN_SWITCH_ON);
+	if (!ret) {
+		msleep(1000);
+		ret = pil_boot(&drv->desc);
+		if (ret)
+			return ret;
+	}
+	drv->restart_inprogress = false;
+	enable_irq(drv->irq);
+	schedule_delayed_work(&drv->cancel_vote_work, msecs_to_jiffies(5000));
+
+	return 0;
+}
+
+static void crash_shutdown(const struct subsys_desc *subsys)
+{
+	struct pronto_data *drv = subsys_to_drv(subsys);
+
+	pr_err("wcnss crash shutdown %d\n", drv->crash);
+	if (!drv->crash)
+		smsm_change_state(SMSM_APPS_STATE, SMSM_RESET, SMSM_RESET);
+}
+
+static int wcnss_ramdump(int enable, const struct subsys_desc *crashed_subsys)
+{
+	return 0;
+}
+
 static int __devinit pil_pronto_probe(struct platform_device *pdev)
 {
 	struct pronto_data *drv;
@@ -233,37 +372,31 @@
 	int ret;
 	uint32_t regval;
 
-	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pmu_base");
-	if (!res)
-		return -EINVAL;
-
 	drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL);
 	if (!drv)
 		return -ENOMEM;
 	platform_set_drvdata(pdev, drv);
 
-	drv->base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
+	drv->irq = platform_get_irq(pdev, 0);
+	if (drv->irq < 0)
+		return drv->irq;
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pmu_base");
+	drv->base = devm_request_and_ioremap(&pdev->dev, res);
 	if (!drv->base)
 		return -ENOMEM;
 
 	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "clk_base");
-	if (!res)
-		return -EINVAL;
-
-	drv->reset_base = devm_ioremap(&pdev->dev, res->start,
-					resource_size(res));
-
-	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "halt_base");
-	if (!res)
-		return -EINVAL;
-
-	drv->axi_halt_base = devm_ioremap(&pdev->dev, res->start,
-					  resource_size(res));
-
-	desc = devm_kzalloc(&pdev->dev, sizeof(*desc), GFP_KERNEL);
-	if (!desc)
+	drv->reset_base = devm_request_and_ioremap(&pdev->dev, res);
+	if (!drv->reset_base)
 		return -ENOMEM;
 
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "halt_base");
+	drv->axi_halt_base = devm_request_and_ioremap(&pdev->dev, res);
+	if (!drv->axi_halt_base)
+		return -ENOMEM;
+
+	desc = &drv->desc;
 	ret = of_property_read_string(pdev->dev.of_node, "qcom,firmware-name",
 				      &desc->name);
 	if (ret)
@@ -299,9 +432,37 @@
 	if (IS_ERR(drv->cxo))
 		return PTR_ERR(drv->cxo);
 
-	drv->pil = msm_pil_register(desc);
-	if (IS_ERR(drv->pil))
-		return PTR_ERR(drv->pil);
+	ret = pil_desc_init(desc);
+	if (ret)
+		return ret;
+
+	ret = smsm_state_cb_register(SMSM_WCNSS_STATE, SMSM_RESET,
+					smsm_state_cb_hdlr, drv);
+	if (ret < 0)
+		goto err_smsm;
+
+	drv->subsys_desc.name = desc->name;
+	drv->subsys_desc.dev = &pdev->dev;
+	drv->subsys_desc.owner = THIS_MODULE;
+	drv->subsys_desc.shutdown = wcnss_shutdown;
+	drv->subsys_desc.powerup = wcnss_powerup;
+	drv->subsys_desc.ramdump = wcnss_ramdump;
+	drv->subsys_desc.crash_shutdown = crash_shutdown;
+	drv->subsys_desc.start = pronto_start;
+	drv->subsys_desc.stop = pronto_stop;
+
+	INIT_DELAYED_WORK(&drv->cancel_vote_work, wcnss_post_bootup);
+
+	drv->subsys = subsys_register(&drv->subsys_desc);
+	if (IS_ERR(drv->subsys)) {
+		ret = PTR_ERR(drv->subsys);
+		goto err_subsys;
+	}
+
+	ret = devm_request_irq(&pdev->dev, drv->irq, wcnss_wdog_bite_irq_hdlr,
+			IRQF_TRIGGER_HIGH, "wcnss_wdog", drv);
+	if (ret < 0)
+		goto err_irq;
 
 	/* Initialize common_ss GDSCR to wait 4 cycles between states */
 	regval = readl_relaxed(drv->base + PRONTO_PMU_COMMON_GDSCR)
@@ -311,12 +472,23 @@
 	writel_relaxed(regval, drv->base + PRONTO_PMU_COMMON_GDSCR);
 
 	return 0;
+err_irq:
+	subsys_unregister(drv->subsys);
+err_subsys:
+	smsm_state_cb_deregister(SMSM_WCNSS_STATE, SMSM_RESET,
+					smsm_state_cb_hdlr, drv);
+err_smsm:
+	pil_desc_release(desc);
+	return ret;
 }
 
 static int __devexit pil_pronto_remove(struct platform_device *pdev)
 {
 	struct pronto_data *drv = platform_get_drvdata(pdev);
-	msm_pil_unregister(drv->pil);
+	subsys_unregister(drv->subsys);
+	smsm_state_cb_deregister(SMSM_WCNSS_STATE, SMSM_RESET,
+					smsm_state_cb_hdlr, drv);
+	pil_desc_release(&drv->desc);
 	return 0;
 }
 
diff --git a/arch/arm/mach-msm/pil-q6v3.c b/arch/arm/mach-msm/pil-q6v3.c
index 1a226de..d7e712c 100644
--- a/arch/arm/mach-msm/pil-q6v3.c
+++ b/arch/arm/mach-msm/pil-q6v3.c
@@ -16,16 +16,13 @@
 #include <linux/io.h>
 #include <linux/ioport.h>
 #include <linux/delay.h>
-#include <linux/elf.h>
 #include <linux/err.h>
 #include <linux/clk.h>
 #include <linux/workqueue.h>
 #include <linux/interrupt.h>
 
-#include <mach/msm_iomap.h>
 #include <mach/subsystem_restart.h>
 #include <mach/scm.h>
-#include <mach/peripheral-loader.h>
 
 #include "ramdump.h"
 #include "peripheral-loader.h"
@@ -35,7 +32,7 @@
 #define QDSP6SS_STRAP_TCM	0x001C
 #define QDSP6SS_STRAP_AHB	0x0020
 
-#define LCC_Q6_FUNC		(MSM_LPASS_CLK_CTL_BASE + 0x001C)
+#define LCC_Q6_FUNC		0x001C
 #define LV_EN			BIT(27)
 #define STOP_CORE		BIT(26)
 #define CLAMP_IO		BIT(25)
@@ -71,9 +68,9 @@
 /**
  * struct q6v3_data - LPASS driver data
  * @base: register base
+ * @cbase: clock base
  * @wk_base: wakeup register base
  * @wd_base: watchdog register base
- * @start_addr: address that processor starts running at
  * @irq: watchdog irq
  * @pil: peripheral handle
  * @subsys: subsystem restart handle
@@ -84,11 +81,11 @@
  */
 struct q6v3_data {
 	void __iomem *base;
+	void __iomem *cbase;
 	void __iomem *wk_base;
 	void __iomem *wd_base;
-	unsigned long start_addr;
 	int irq;
-	struct pil_device *pil;
+	struct pil_desc pil_desc;
 	struct subsys_device *subsys;
 	struct subsys_desc subsys_desc;
 	struct work_struct fatal_wrk;
@@ -96,15 +93,6 @@
 	struct ramdump_device *ramdump_dev;
 };
 
-static int pil_q6v3_init_image(struct pil_desc *pil, const u8 *metadata,
-		size_t size)
-{
-	const struct elf32_hdr *ehdr = (struct elf32_hdr *)metadata;
-	struct q6v3_data *drv = dev_get_drvdata(pil->dev);
-	drv->start_addr = ehdr->e_entry;
-	return 0;
-}
-
 static void pil_q6v3_remove_proxy_votes(struct pil_desc *pil)
 {
 	struct q6v3_data *drv = dev_get_drvdata(pil->dev);
@@ -128,13 +116,14 @@
 {
 	u32 reg;
 	struct q6v3_data *drv = dev_get_drvdata(pil->dev);
+	unsigned long start_addr = pil_get_entry_addr(pil);
 
 	/* Put Q6 into reset */
-	reg = readl_relaxed(LCC_Q6_FUNC);
+	reg = readl_relaxed(drv->cbase + LCC_Q6_FUNC);
 	reg |= Q6SS_SS_ARES | Q6SS_ISDB_ARES | Q6SS_ETM_ARES | STOP_CORE |
 		CORE_ARES;
 	reg &= ~CORE_GFM4_CLK_EN;
-	writel_relaxed(reg, LCC_Q6_FUNC);
+	writel_relaxed(reg, drv->cbase + LCC_Q6_FUNC);
 
 	/* Wait 8 AHB cycles for Q6 to be fully reset (AHB = 1.5Mhz) */
 	usleep_range(20, 30);
@@ -142,17 +131,17 @@
 	/* Turn on Q6 memory */
 	reg |= CORE_GFM4_CLK_EN | CORE_L1_MEM_CORE_EN | CORE_TCM_MEM_CORE_EN |
 		CORE_TCM_MEM_PERPH_EN;
-	writel_relaxed(reg, LCC_Q6_FUNC);
+	writel_relaxed(reg, drv->cbase + LCC_Q6_FUNC);
 
 	/* Turn on Q6 core clocks and take core out of reset */
 	reg &= ~(CLAMP_IO | Q6SS_SS_ARES | Q6SS_ISDB_ARES | Q6SS_ETM_ARES |
 			CORE_ARES);
-	writel_relaxed(reg, LCC_Q6_FUNC);
+	writel_relaxed(reg, drv->cbase + LCC_Q6_FUNC);
 
 	/* Wait for clocks to be enabled */
 	mb();
 	/* Program boot address */
-	writel_relaxed((drv->start_addr >> 12) & 0xFFFFF,
+	writel_relaxed((start_addr >> 12) & 0xFFFFF,
 			drv->base + QDSP6SS_RST_EVB);
 
 	writel_relaxed(Q6_STRAP_TCM_CONFIG | Q6_STRAP_TCM_BASE,
@@ -165,7 +154,7 @@
 
 	/* Start Q6 instruction execution */
 	reg &= ~STOP_CORE;
-	writel_relaxed(reg, LCC_Q6_FUNC);
+	writel_relaxed(reg, drv->cbase + LCC_Q6_FUNC);
 
 	return 0;
 }
@@ -173,13 +162,14 @@
 static int pil_q6v3_shutdown(struct pil_desc *pil)
 {
 	u32 reg;
+	struct q6v3_data *drv = dev_get_drvdata(pil->dev);
 
 	/* Put Q6 into reset */
-	reg = readl_relaxed(LCC_Q6_FUNC);
+	reg = readl_relaxed(drv->cbase + LCC_Q6_FUNC);
 	reg |= Q6SS_SS_ARES | Q6SS_ISDB_ARES | Q6SS_ETM_ARES | STOP_CORE |
 		CORE_ARES;
 	reg &= ~CORE_GFM4_CLK_EN;
-	writel_relaxed(reg, LCC_Q6_FUNC);
+	writel_relaxed(reg, drv->cbase + LCC_Q6_FUNC);
 
 	/* Wait 8 AHB cycles for Q6 to be fully reset (AHB = 1.5Mhz) */
 	usleep_range(20, 30);
@@ -187,16 +177,15 @@
 	/* Turn off Q6 memory */
 	reg &= ~(CORE_L1_MEM_CORE_EN | CORE_TCM_MEM_CORE_EN |
 		CORE_TCM_MEM_PERPH_EN);
-	writel_relaxed(reg, LCC_Q6_FUNC);
+	writel_relaxed(reg, drv->cbase + LCC_Q6_FUNC);
 
 	reg |= CLAMP_IO;
-	writel_relaxed(reg, LCC_Q6_FUNC);
+	writel_relaxed(reg, drv->cbase + LCC_Q6_FUNC);
 
 	return 0;
 }
 
 static struct pil_reset_ops pil_q6v3_ops = {
-	.init_image = pil_q6v3_init_image,
 	.auth_and_reset = pil_q6v3_reset,
 	.shutdown = pil_q6v3_shutdown,
 	.proxy_vote = pil_q6v3_make_proxy_votes,
@@ -248,6 +237,22 @@
 	pr_info("Q6 NMI was sent.\n");
 }
 
+static int lpass_q6_start(const struct subsys_desc *subsys)
+{
+	struct q6v3_data *drv;
+
+	drv = container_of(subsys, struct q6v3_data, subsys_desc);
+	return pil_boot(&drv->pil_desc);
+}
+
+static void lpass_q6_stop(const struct subsys_desc *subsys)
+{
+	struct q6v3_data *drv;
+
+	drv = container_of(subsys, struct q6v3_data, subsys_desc);
+	pil_shutdown(&drv->pil_desc);
+}
+
 static int lpass_q6_shutdown(const struct subsys_desc *subsys)
 {
 	struct q6v3_data *drv;
@@ -257,7 +262,7 @@
 	writel_relaxed(0x0, drv->wd_base + 0x24);
 	mb();
 
-	pil_force_shutdown("q6");
+	pil_shutdown(&drv->pil_desc);
 	disable_irq_nosync(drv->irq);
 
 	return 0;
@@ -269,7 +274,7 @@
 	int ret;
 
 	drv = container_of(subsys, struct q6v3_data, subsys_desc);
-	ret = pil_force_boot("q6");
+	ret = pil_boot(&drv->pil_desc);
 	enable_irq(drv->irq);
 	return ret;
 }
@@ -318,33 +323,31 @@
 	struct pil_desc *desc;
 	int ret;
 
-	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-	if (!res)
-		return -EINVAL;
-
 	drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL);
 	if (!drv)
 		return -ENOMEM;
 	platform_set_drvdata(pdev, drv);
 
-	drv->base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	drv->base = devm_request_and_ioremap(&pdev->dev, res);
 	if (!drv->base)
 		return -ENOMEM;
 
 	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
-	if (!res)
-		return -EINVAL;
-
-	drv->wk_base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
+	drv->wk_base = devm_request_and_ioremap(&pdev->dev, res);
 	if (!drv->wk_base)
 		return -ENOMEM;
 
 	res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
+	drv->wd_base = devm_request_and_ioremap(&pdev->dev, res);
+	if (!drv->wd_base)
+		return -ENOMEM;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 3);
 	if (!res)
 		return -EINVAL;
-
-	drv->wd_base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
-	if (!drv->wd_base)
+	drv->cbase = devm_ioremap(&pdev->dev, res->start, resource_size(res));
+	if (!drv->cbase)
 		return -ENOMEM;
 
 	drv->irq = platform_get_irq(pdev, 0);
@@ -355,10 +358,7 @@
 	if (IS_ERR(drv->pll))
 		return PTR_ERR(drv->pll);
 
-	desc = devm_kzalloc(&pdev->dev, sizeof(*desc), GFP_KERNEL);
-	if (!drv)
-		return -ENOMEM;
-
+	desc = &drv->pil_desc;
 	desc->name = "q6";
 	desc->dev = &pdev->dev;
 	desc->owner = THIS_MODULE;
@@ -372,11 +372,15 @@
 		dev_info(&pdev->dev, "using non-secure boot\n");
 	}
 
-	drv->pil = msm_pil_register(desc);
-	if (IS_ERR(drv->pil))
-		return PTR_ERR(drv->pil);
+	ret = pil_desc_init(desc);
+	if (ret)
+		return ret;
 
-	drv->subsys_desc.name = "lpass";
+	drv->subsys_desc.name = "adsp";
+	drv->subsys_desc.dev = &pdev->dev;
+	drv->subsys_desc.owner = THIS_MODULE;
+	drv->subsys_desc.start = lpass_q6_start;
+	drv->subsys_desc.stop = lpass_q6_stop;
 	drv->subsys_desc.shutdown = lpass_q6_shutdown;
 	drv->subsys_desc.powerup = lpass_q6_powerup;
 	drv->subsys_desc.ramdump = lpass_q6_ramdump;
@@ -384,7 +388,7 @@
 
 	INIT_WORK(&drv->fatal_wrk, q6_fatal_fn);
 
-	drv->ramdump_dev = create_ramdump_device("lpass");
+	drv->ramdump_dev = create_ramdump_device("lpass", &pdev->dev);
 	if (!drv->ramdump_dev) {
 		ret = -ENOMEM;
 		goto err_ramdump;
@@ -409,7 +413,7 @@
 err_subsys:
 	destroy_ramdump_device(drv->ramdump_dev);
 err_ramdump:
-	msm_pil_unregister(drv->pil);
+	pil_desc_release(desc);
 	return ret;
 }
 
@@ -418,7 +422,7 @@
 	struct q6v3_data *drv = platform_get_drvdata(pdev);
 	subsys_unregister(drv->subsys);
 	destroy_ramdump_device(drv->ramdump_dev);
-	msm_pil_unregister(drv->pil);
+	pil_desc_release(&drv->pil_desc);
 	return 0;
 }
 
diff --git a/arch/arm/mach-msm/pil-q6v4-lpass.c b/arch/arm/mach-msm/pil-q6v4-lpass.c
new file mode 100644
index 0000000..1e6c1f6
--- /dev/null
+++ b/arch/arm/mach-msm/pil-q6v4-lpass.c
@@ -0,0 +1,420 @@
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/workqueue.h>
+#include <linux/delay.h>
+
+#include <mach/scm.h>
+#include <mach/subsystem_restart.h>
+#include <mach/subsystem_notif.h>
+
+#include "smd_private.h"
+#include "ramdump.h"
+#include "sysmon.h"
+#include "peripheral-loader.h"
+#include "pil-q6v4.h"
+#include "scm-pas.h"
+
+struct lpass_q6v4 {
+	struct q6v4_data q6;
+	void *riva_notif_hdle;
+	void *modem_notif_hdle;
+	struct subsys_device *subsys;
+	struct subsys_desc subsys_desc;
+	int crash_shutdown;
+	void *ramdump_dev;
+	struct work_struct work;
+	int loadable;
+};
+
+static int pil_q6v4_lpass_boot(struct pil_desc *pil)
+{
+	struct q6v4_data *drv = pil_to_q6v4_data(pil);
+	int err;
+
+	err = pil_q6v4_power_up(drv);
+	if (err)
+		return err;
+
+	return pil_q6v4_boot(pil);
+}
+
+static int pil_q6v4_lpass_shutdown(struct pil_desc *pil)
+{
+	struct q6v4_data *drv = pil_to_q6v4_data(pil);
+	int ret;
+
+	ret = pil_q6v4_shutdown(pil);
+	if (ret)
+		return ret;
+	pil_q6v4_power_down(drv);
+	return 0;
+}
+
+static struct pil_reset_ops pil_q6v4_lpass_ops = {
+	.auth_and_reset = pil_q6v4_lpass_boot,
+	.shutdown = pil_q6v4_lpass_shutdown,
+	.proxy_vote = pil_q6v4_make_proxy_votes,
+	.proxy_unvote = pil_q6v4_remove_proxy_votes,
+};
+
+static struct pil_reset_ops pil_q6v4_lpass_ops_trusted = {
+	.init_image = pil_q6v4_init_image_trusted,
+	.auth_and_reset = pil_q6v4_boot_trusted,
+	.shutdown = pil_q6v4_shutdown_trusted,
+	.proxy_vote = pil_q6v4_make_proxy_votes,
+	.proxy_unvote = pil_q6v4_remove_proxy_votes,
+};
+
+static int riva_notifier_cb(struct notifier_block *this, unsigned long code,
+								void *ss_handle)
+{
+	int ret;
+	switch (code) {
+	case SUBSYS_BEFORE_SHUTDOWN:
+		pr_debug("%s: R-Notify: Shutdown started\n", __func__);
+		ret = sysmon_send_event(SYSMON_SS_LPASS, "wcnss",
+				SUBSYS_BEFORE_SHUTDOWN);
+		if (ret < 0)
+			pr_err("%s: sysmon_send_event error %d", __func__, ret);
+		break;
+	}
+	return NOTIFY_DONE;
+}
+
+static struct notifier_block rnb = {
+	.notifier_call = riva_notifier_cb,
+};
+
+static int modem_notifier_cb(struct notifier_block *this, unsigned long code,
+								void *ss_handle)
+{
+	int ret;
+	switch (code) {
+	case SUBSYS_BEFORE_SHUTDOWN:
+		pr_debug("%s: M-Notify: Shutdown started\n", __func__);
+		ret = sysmon_send_event(SYSMON_SS_LPASS, "modem",
+				SUBSYS_BEFORE_SHUTDOWN);
+		if (ret < 0)
+			pr_err("%s: sysmon_send_event error %d", __func__, ret);
+		break;
+	}
+	return NOTIFY_DONE;
+}
+
+static struct notifier_block mnb = {
+	.notifier_call = modem_notifier_cb,
+};
+
+static void lpass_log_failure_reason(void)
+{
+	char *reason;
+	char buffer[81];
+	unsigned size;
+
+	reason = smem_get_entry(SMEM_SSR_REASON_LPASS0, &size);
+
+	if (!reason) {
+		pr_err("LPASS subsystem failure reason: (unknown, smem_get_entry failed).");
+		return;
+	}
+
+	if (reason[0] == '\0') {
+		pr_err("LPASS subsystem failure reason: (unknown, init value found)");
+		return;
+	}
+
+	size = min(size, sizeof(buffer) - 1);
+	memcpy(buffer, reason, size);
+	buffer[size] = '\0';
+	pr_err("LPASS subsystem failure reason: %s", buffer);
+	memset((void *)reason, 0x0, size);
+	wmb();
+}
+
+static void lpass_fatal_fn(struct work_struct *work)
+{
+	pr_err("Watchdog bite received from lpass Q6!\n");
+	lpass_log_failure_reason();
+	panic("Q6 Resetting the SoC");
+}
+
+static void lpass_smsm_state_cb(void *data, uint32_t old_state,
+				uint32_t new_state)
+{
+	struct lpass_q6v4 *drv = data;
+
+	/* Ignore if we're the one that set SMSM_RESET */
+	if (drv->crash_shutdown)
+		return;
+
+	if (new_state & SMSM_RESET) {
+		pr_err("%s: LPASS SMSM state changed to SMSM_RESET, new_state = %#x, old_state = %#x\n",
+				__func__, new_state, old_state);
+		lpass_log_failure_reason();
+		panic("Q6 Resetting the SoC");
+	}
+}
+
+#define SCM_Q6_NMI_CMD 0x1
+
+static void send_q6_nmi(void)
+{
+	/* Send NMI to QDSP6 via an SCM call. */
+	uint32_t cmd = 0x1;
+
+	scm_call(SCM_SVC_UTIL, SCM_Q6_NMI_CMD,
+	&cmd, sizeof(cmd), NULL, 0);
+
+	/* Q6 requires worstcase 100ms to dump caches etc.*/
+	mdelay(100);
+	pr_debug("%s: Q6 NMI was sent.\n", __func__);
+}
+
+#define subsys_to_lpass(d) container_of(d, struct lpass_q6v4, subsys_desc)
+
+static int lpass_start(const struct subsys_desc *desc)
+{
+	struct lpass_q6v4 *drv = subsys_to_lpass(desc);
+
+	if (drv->loadable)
+		return pil_boot(&drv->q6.desc);
+	return 0;
+}
+
+static void lpass_stop(const struct subsys_desc *desc)
+{
+	struct lpass_q6v4 *drv = subsys_to_lpass(desc);
+
+	if (drv->loadable)
+		pil_shutdown(&drv->q6.desc);
+}
+
+static int lpass_shutdown(const struct subsys_desc *subsys)
+{
+	struct lpass_q6v4 *drv = subsys_to_lpass(subsys);
+
+	send_q6_nmi();
+	if (drv->loadable)
+		pil_shutdown(&drv->q6.desc);
+	disable_irq_nosync(drv->q6.wdog_irq);
+
+	return 0;
+}
+
+static int lpass_powerup(const struct subsys_desc *subsys)
+{
+	struct lpass_q6v4 *drv = subsys_to_lpass(subsys);
+	int ret = 0;
+
+	if (drv->loadable)
+		ret = pil_boot(&drv->q6.desc);
+	enable_irq(drv->q6.wdog_irq);
+
+	return ret;
+}
+
+static struct ramdump_segment segments[] = {
+	{0x8da00000, 0x8f200000 - 0x8da00000},
+	{0x28400000, 0x20000}
+};
+
+static int lpass_ramdump(int enable, const struct subsys_desc *subsys)
+{
+	struct lpass_q6v4 *drv = subsys_to_lpass(subsys);
+
+	if (!enable)
+		return 0;
+	return do_ramdump(drv->ramdump_dev, segments, ARRAY_SIZE(segments));
+}
+
+static void lpass_crash_shutdown(const struct subsys_desc *subsys)
+{
+	struct lpass_q6v4 *drv = subsys_to_lpass(subsys);
+
+	drv->crash_shutdown = 1;
+	send_q6_nmi();
+}
+
+static irqreturn_t lpass_wdog_bite_irq(int irq, void *dev_id)
+{
+	struct lpass_q6v4 *drv = dev_id;
+
+	disable_irq_nosync(drv->q6.wdog_irq);
+	schedule_work(&drv->work);
+
+	return IRQ_HANDLED;
+}
+
+static int __devinit pil_q6v4_lpass_driver_probe(struct platform_device *pdev)
+{
+	const struct pil_q6v4_pdata *pdata = pdev->dev.platform_data;
+	struct lpass_q6v4 *drv;
+	struct q6v4_data *q6;
+	struct pil_desc *desc;
+	struct resource *res;
+	int ret;
+
+	drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL);
+	if (!drv)
+		return -ENOMEM;
+	platform_set_drvdata(pdev, drv);
+	q6 = &drv->q6;
+	desc = &q6->desc;
+
+	q6->wdog_irq = platform_get_irq(pdev, 0);
+	if (q6->wdog_irq < 0)
+		return q6->wdog_irq;
+
+	drv->loadable = !!pdata; /* No pdata = don't use PIL */
+	if (drv->loadable) {
+		res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+		q6->base = devm_request_and_ioremap(&pdev->dev, res);
+		if (!q6->base)
+			return -ENOMEM;
+
+		q6->vreg = devm_regulator_get(&pdev->dev, "core_vdd");
+		if (IS_ERR(q6->vreg))
+			return PTR_ERR(q6->vreg);
+
+		q6->xo = devm_clk_get(&pdev->dev, "xo");
+		if (IS_ERR(q6->xo))
+			return PTR_ERR(q6->xo);
+
+		desc->name = pdata->name;
+		desc->dev = &pdev->dev;
+		desc->owner = THIS_MODULE;
+		desc->proxy_timeout = 10000;
+		pil_q6v4_init(q6, pdata);
+
+		if (pas_supported(pdata->pas_id) > 0) {
+			desc->ops = &pil_q6v4_lpass_ops_trusted;
+			dev_info(&pdev->dev, "using secure boot\n");
+		} else {
+			desc->ops = &pil_q6v4_lpass_ops;
+			dev_info(&pdev->dev, "using non-secure boot\n");
+		}
+
+		ret = pil_desc_init(desc);
+		if (ret)
+			return ret;
+	}
+
+	drv->subsys_desc.name = "adsp";
+	drv->subsys_desc.dev = &pdev->dev;
+	drv->subsys_desc.owner = THIS_MODULE;
+	drv->subsys_desc.start = lpass_start;
+	drv->subsys_desc.stop = lpass_stop;
+	drv->subsys_desc.shutdown = lpass_shutdown;
+	drv->subsys_desc.powerup = lpass_powerup;
+	drv->subsys_desc.ramdump = lpass_ramdump;
+	drv->subsys_desc.crash_shutdown = lpass_crash_shutdown;
+
+	INIT_WORK(&drv->work, lpass_fatal_fn);
+
+	drv->ramdump_dev = create_ramdump_device("lpass", &pdev->dev);
+	if (!drv->ramdump_dev) {
+		ret = -ENOMEM;
+		goto err_ramdump;
+	}
+
+	drv->subsys = subsys_register(&drv->subsys_desc);
+	if (IS_ERR(drv->subsys)) {
+		ret = PTR_ERR(drv->subsys);
+		goto err_subsys;
+	}
+	if (!drv->loadable)
+		subsys_default_online(drv->subsys);
+
+	ret = devm_request_irq(&pdev->dev, q6->wdog_irq, lpass_wdog_bite_irq,
+			IRQF_TRIGGER_RISING, dev_name(&pdev->dev), drv);
+	if (ret)
+		goto err_irq;
+
+	ret = smsm_state_cb_register(SMSM_Q6_STATE, SMSM_RESET,
+			lpass_smsm_state_cb, drv);
+	if (ret < 0)
+		goto err_smsm;
+
+	drv->riva_notif_hdle = subsys_notif_register_notifier("riva", &rnb);
+	if (IS_ERR(drv->riva_notif_hdle)) {
+		ret = PTR_ERR(drv->riva_notif_hdle);
+		goto err_notif_riva;
+	}
+
+	drv->modem_notif_hdle = subsys_notif_register_notifier("modem", &mnb);
+	if (IS_ERR(drv->modem_notif_hdle)) {
+		ret = PTR_ERR(drv->modem_notif_hdle);
+		goto err_notif_modem;
+	}
+	return 0;
+err_notif_modem:
+	subsys_notif_unregister_notifier(drv->riva_notif_hdle, &rnb);
+err_notif_riva:
+	smsm_state_cb_deregister(SMSM_Q6_STATE, SMSM_RESET,
+			lpass_smsm_state_cb, drv);
+err_smsm:
+err_irq:
+	subsys_unregister(drv->subsys);
+err_subsys:
+	destroy_ramdump_device(drv->ramdump_dev);
+err_ramdump:
+	if (drv->loadable)
+		pil_desc_release(desc);
+	return ret;
+}
+
+static int __devexit pil_q6v4_lpass_driver_exit(struct platform_device *pdev)
+{
+	struct lpass_q6v4 *drv = platform_get_drvdata(pdev);
+	subsys_notif_unregister_notifier(drv->riva_notif_hdle, &rnb);
+	subsys_notif_unregister_notifier(drv->modem_notif_hdle, &mnb);
+	smsm_state_cb_deregister(SMSM_Q6_STATE, SMSM_RESET,
+			lpass_smsm_state_cb, drv);
+	subsys_unregister(drv->subsys);
+	destroy_ramdump_device(drv->ramdump_dev);
+	if (drv->loadable)
+		pil_desc_release(&drv->q6.desc);
+	return 0;
+}
+
+static struct platform_driver pil_q6v4_lpass_driver = {
+	.probe = pil_q6v4_lpass_driver_probe,
+	.remove = __devexit_p(pil_q6v4_lpass_driver_exit),
+	.driver = {
+		.name = "pil-q6v4-lpass",
+		.owner = THIS_MODULE,
+	},
+};
+
+static int __init pil_q6v4_lpass_init(void)
+{
+	return platform_driver_register(&pil_q6v4_lpass_driver);
+}
+module_init(pil_q6v4_lpass_init);
+
+static void __exit pil_q6v4_lpass_exit(void)
+{
+	platform_driver_unregister(&pil_q6v4_lpass_driver);
+}
+module_exit(pil_q6v4_lpass_exit);
+
+MODULE_DESCRIPTION("Support for booting QDSP6v4 (Hexagon) processors");
+MODULE_LICENSE("GPL v2");
diff --git a/arch/arm/mach-msm/pil-q6v4-mss.c b/arch/arm/mach-msm/pil-q6v4-mss.c
new file mode 100644
index 0000000..ee01f04
--- /dev/null
+++ b/arch/arm/mach-msm/pil-q6v4-mss.c
@@ -0,0 +1,535 @@
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+
+#include <mach/subsystem_restart.h>
+#include <mach/msm_smsm.h>
+
+#include "smd_private.h"
+#include "ramdump.h"
+#include "peripheral-loader.h"
+#include "pil-q6v4.h"
+#include "scm-pas.h"
+
+#define MSS_S_HCLK_CTL		0x2C70
+#define MSS_SLP_CLK_CTL		0x2C60
+#define SFAB_MSS_M_ACLK_CTL	0x2340
+#define SFAB_MSS_S_HCLK_CTL	0x2C00
+#define MSS_RESET		0x2C64
+
+struct q6v4_modem {
+	struct q6v4_data q6_fw;
+	struct q6v4_data q6_sw;
+	void __iomem *modem_base;
+	void __iomem *cbase;
+	void *fw_ramdump_dev;
+	void *sw_ramdump_dev;
+	void *smem_ramdump_dev;
+	struct subsys_device *subsys;
+	struct subsys_desc subsys_desc;
+	int crash_shutdown;
+	int loadable;
+	void *pil;
+};
+
+static DEFINE_MUTEX(pil_q6v4_modem_lock);
+static unsigned pil_q6v4_modem_count;
+
+/* Bring modem subsystem out of reset */
+static void pil_q6v4_init_modem(void __iomem *base, void __iomem *cbase,
+				void __iomem *jtag_clk)
+{
+	mutex_lock(&pil_q6v4_modem_lock);
+	if (!pil_q6v4_modem_count) {
+		/* Enable MSS clocks */
+		writel_relaxed(0x10, cbase + SFAB_MSS_M_ACLK_CTL);
+		writel_relaxed(0x10, cbase + SFAB_MSS_S_HCLK_CTL);
+		writel_relaxed(0x10, cbase + MSS_S_HCLK_CTL);
+		writel_relaxed(0x10, cbase + MSS_SLP_CLK_CTL);
+		/* Wait for clocks to enable */
+		mb();
+		udelay(10);
+
+		/* De-assert MSS reset */
+		writel_relaxed(0x0, cbase + MSS_RESET);
+		mb();
+		udelay(10);
+		/* Enable MSS */
+		writel_relaxed(0x7, base);
+	}
+
+	/* Enable JTAG clocks */
+	/* TODO: Remove if/when Q6 software enables them? */
+	writel_relaxed(0x10, jtag_clk);
+
+	pil_q6v4_modem_count++;
+	mutex_unlock(&pil_q6v4_modem_lock);
+}
+
+/* Put modem subsystem back into reset */
+static void pil_q6v4_shutdown_modem(struct q6v4_modem *mdm)
+{
+	mutex_lock(&pil_q6v4_modem_lock);
+	if (pil_q6v4_modem_count)
+		pil_q6v4_modem_count--;
+	if (pil_q6v4_modem_count == 0)
+		writel_relaxed(0x1, mdm->cbase + MSS_RESET);
+	mutex_unlock(&pil_q6v4_modem_lock);
+}
+
+static int pil_q6v4_modem_boot(struct pil_desc *pil)
+{
+	struct q6v4_data *drv = pil_to_q6v4_data(pil);
+	struct q6v4_modem *mdm = dev_get_drvdata(pil->dev);
+	int err;
+
+	err = pil_q6v4_power_up(drv);
+	if (err)
+		return err;
+
+	pil_q6v4_init_modem(mdm->modem_base, mdm->cbase, drv->jtag_clk_reg);
+	return pil_q6v4_boot(pil);
+}
+
+static int pil_q6v4_modem_shutdown(struct pil_desc *pil)
+{
+	struct q6v4_data *drv = pil_to_q6v4_data(pil);
+	struct q6v4_modem *mdm = dev_get_drvdata(pil->dev);
+	int ret;
+
+	ret = pil_q6v4_shutdown(pil);
+	if (ret)
+		return ret;
+	pil_q6v4_shutdown_modem(mdm);
+	pil_q6v4_power_down(drv);
+	return 0;
+}
+
+static struct pil_reset_ops pil_q6v4_modem_ops = {
+	.auth_and_reset = pil_q6v4_modem_boot,
+	.shutdown = pil_q6v4_modem_shutdown,
+	.proxy_vote = pil_q6v4_make_proxy_votes,
+	.proxy_unvote = pil_q6v4_remove_proxy_votes,
+};
+
+static struct pil_reset_ops pil_q6v4_modem_ops_trusted = {
+	.init_image = pil_q6v4_init_image_trusted,
+	.auth_and_reset = pil_q6v4_boot_trusted,
+	.shutdown = pil_q6v4_shutdown_trusted,
+	.proxy_vote = pil_q6v4_make_proxy_votes,
+	.proxy_unvote = pil_q6v4_remove_proxy_votes,
+};
+
+static void log_modem_sfr(void)
+{
+	u32 size;
+	char *smem_reason, reason[81];
+
+	smem_reason = smem_get_entry(SMEM_SSR_REASON_MSS0, &size);
+	if (!smem_reason || !size) {
+		pr_err("modem subsystem failure reason: (unknown, smem_get_entry failed).\n");
+		return;
+	}
+	if (!smem_reason[0]) {
+		pr_err("modem subsystem failure reason: (unknown, init string found).\n");
+		return;
+	}
+
+	size = min(size, sizeof(reason)-1);
+	memcpy(reason, smem_reason, size);
+	reason[size] = '\0';
+	pr_err("modem subsystem failure reason: %s.\n", reason);
+
+	smem_reason[0] = '\0';
+	wmb();
+}
+
+static void restart_modem(struct q6v4_modem *drv)
+{
+	log_modem_sfr();
+	subsystem_restart_dev(drv->subsys);
+}
+
+#define desc_to_modem(d) container_of(d, struct q6v4_modem, subsys_desc)
+
+static int modem_start(const struct subsys_desc *desc)
+{
+	struct q6v4_modem *drv = desc_to_modem(desc);
+	int ret = 0;
+
+	if (drv->loadable) {
+		ret = pil_boot(&drv->q6_fw.desc);
+		if (ret)
+			return ret;
+		ret = pil_boot(&drv->q6_sw.desc);
+		if (ret)
+			pil_shutdown(&drv->q6_fw.desc);
+	}
+	return ret;
+}
+
+static void modem_stop(const struct subsys_desc *desc)
+{
+	struct q6v4_modem *drv = desc_to_modem(desc);
+	if (drv->loadable) {
+		pil_shutdown(&drv->q6_sw.desc);
+		pil_shutdown(&drv->q6_fw.desc);
+	}
+}
+
+static int modem_shutdown(const struct subsys_desc *subsys)
+{
+	struct q6v4_modem *drv = desc_to_modem(subsys);
+
+	/* The watchdogs keep running even after the modem is shutdown */
+	writel_relaxed(0x0, drv->q6_fw.wdog_base + 0x24);
+	writel_relaxed(0x0, drv->q6_sw.wdog_base + 0x24);
+	mb();
+
+	if (drv->loadable) {
+		pil_shutdown(&drv->q6_sw.desc);
+		pil_shutdown(&drv->q6_fw.desc);
+	}
+
+	disable_irq_nosync(drv->q6_fw.wdog_irq);
+	disable_irq_nosync(drv->q6_sw.wdog_irq);
+
+	return 0;
+}
+
+static int modem_powerup(const struct subsys_desc *subsys)
+{
+	struct q6v4_modem *drv = desc_to_modem(subsys);
+	int ret;
+
+	if (drv->loadable) {
+		ret = pil_boot(&drv->q6_fw.desc);
+		if (ret)
+			return ret;
+		ret = pil_boot(&drv->q6_sw.desc);
+		if (ret) {
+			pil_shutdown(&drv->q6_fw.desc);
+			return ret;
+		}
+	}
+	enable_irq(drv->q6_fw.wdog_irq);
+	enable_irq(drv->q6_sw.wdog_irq);
+	return 0;
+}
+
+void modem_crash_shutdown(const struct subsys_desc *subsys)
+{
+	struct q6v4_modem *drv = desc_to_modem(subsys);
+
+	drv->crash_shutdown = 1;
+	smsm_reset_modem(SMSM_RESET);
+}
+
+static struct ramdump_segment sw_segments[] = {
+	{0x89000000, 0x8D400000 - 0x89000000},
+};
+
+static struct ramdump_segment fw_segments[] = {
+	{0x8D400000, 0x8DA00000 - 0x8D400000},
+};
+
+static struct ramdump_segment smem_segments[] = {
+	{0x80000000, 0x00200000},
+};
+
+static int modem_ramdump(int enable, const struct subsys_desc *subsys)
+{
+	struct q6v4_modem *drv = desc_to_modem(subsys);
+	int ret;
+
+	if (!enable)
+		return 0;
+
+	ret = do_ramdump(drv->sw_ramdump_dev, sw_segments,
+		ARRAY_SIZE(sw_segments));
+	if (ret < 0)
+		return ret;
+
+	ret = do_ramdump(drv->fw_ramdump_dev, fw_segments,
+		ARRAY_SIZE(fw_segments));
+	if (ret < 0)
+		return ret;
+
+	ret = do_ramdump(drv->smem_ramdump_dev, smem_segments,
+		ARRAY_SIZE(smem_segments));
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static void smsm_state_cb(void *data, uint32_t old_state, uint32_t new_state)
+{
+	struct q6v4_modem *drv = data;
+
+	/* Ignore if we're the one that set SMSM_RESET */
+	if (drv->crash_shutdown)
+		return;
+
+	if (new_state & SMSM_RESET) {
+		pr_err("Probable fatal error on the modem.\n");
+		restart_modem(drv);
+	}
+}
+
+static irqreturn_t modem_wdog_bite_irq(int irq, void *dev_id)
+{
+	struct q6v4_modem *drv = dev_id;
+	restart_modem(drv);
+	return IRQ_HANDLED;
+}
+
+static int __devinit
+pil_q6v4_proc_init(struct q6v4_data *drv, struct platform_device *pdev, int i)
+{
+	static const char *name[2] = { "fw", "sw" };
+	const struct pil_q6v4_pdata *pdata_p = pdev->dev.platform_data;
+	const struct pil_q6v4_pdata *pdata = pdata_p + i;
+	char reg_name[12];
+	struct pil_desc *desc;
+	struct resource *res;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 2 + (i * 2));
+	drv->base = devm_request_and_ioremap(&pdev->dev, res);
+	if (!drv->base)
+		return -ENOMEM;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 3 + (i * 2));
+	drv->wdog_base = devm_request_and_ioremap(&pdev->dev, res);
+	if (!drv->wdog_base)
+		return -ENOMEM;
+
+	snprintf(reg_name, sizeof(reg_name), "%s_core_vdd", name[i]);
+	drv->vreg = devm_regulator_get(&pdev->dev, reg_name);
+	if (IS_ERR(drv->vreg))
+		return PTR_ERR(drv->vreg);
+
+	drv->xo = devm_clk_get(&pdev->dev, "xo");
+	if (IS_ERR(drv->xo))
+		return PTR_ERR(drv->xo);
+
+	desc = &drv->desc;
+	desc->name = pdata->name;
+	desc->dev = &pdev->dev;
+	desc->owner = THIS_MODULE;
+	desc->proxy_timeout = 10000;
+	pil_q6v4_init(drv, pdata);
+
+	if (pas_supported(pdata->pas_id) > 0) {
+		desc->ops = &pil_q6v4_modem_ops_trusted;
+		dev_info(&pdev->dev, "using secure boot for %s\n", name[i]);
+	} else {
+		desc->ops = &pil_q6v4_modem_ops;
+		dev_info(&pdev->dev, "using non-secure boot for %s\n", name[i]);
+	}
+	return 0;
+}
+
+static int __devinit pil_q6v4_modem_driver_probe(struct platform_device *pdev)
+{
+	struct q6v4_data *drv_fw, *drv_sw;
+	struct q6v4_modem *drv;
+	struct resource *res;
+	struct regulator *pll_supply;
+	int ret;
+	const struct pil_q6v4_pdata *pdata = pdev->dev.platform_data;
+
+	drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL);
+	if (!drv)
+		return -ENOMEM;
+	platform_set_drvdata(pdev, drv);
+
+	drv_fw = &drv->q6_fw;
+	drv_sw = &drv->q6_sw;
+
+	drv_fw->wdog_irq = platform_get_irq(pdev, 0);
+	if (drv_fw->wdog_irq < 0)
+		return drv_fw->wdog_irq;
+
+	drv_sw->wdog_irq = platform_get_irq(pdev, 1);
+	if (drv_sw->wdog_irq < 0)
+		return drv_sw->wdog_irq;
+
+	drv->loadable = !!pdata; /* No pdata = don't use PIL */
+	if (drv->loadable) {
+		ret = pil_q6v4_proc_init(drv_fw, pdev, 0);
+		if (ret)
+			return ret;
+
+		ret = pil_q6v4_proc_init(drv_sw, pdev, 1);
+		if (ret)
+			return ret;
+
+		pll_supply = devm_regulator_get(&pdev->dev, "pll_vdd");
+		drv_fw->pll_supply = drv_sw->pll_supply = pll_supply;
+		if (IS_ERR(pll_supply))
+			return PTR_ERR(pll_supply);
+
+		ret = regulator_set_voltage(pll_supply, 1800000, 1800000);
+		if (ret) {
+			dev_err(&pdev->dev, "failed to set pll voltage\n");
+			return ret;
+		}
+
+		ret = regulator_set_optimum_mode(pll_supply, 100000);
+		if (ret < 0) {
+			dev_err(&pdev->dev, "failed to set pll optimum mode\n");
+			return ret;
+		}
+
+		res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+		drv->modem_base = devm_request_and_ioremap(&pdev->dev, res);
+		if (!drv->modem_base)
+			return -ENOMEM;
+
+		res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+		if (!res)
+			return -EINVAL;
+		drv->cbase = devm_ioremap(&pdev->dev, res->start,
+					  resource_size(res));
+		if (!drv->cbase)
+			return -ENOMEM;
+
+		ret = pil_desc_init(&drv_fw->desc);
+		if (ret)
+			return ret;
+
+		ret = pil_desc_init(&drv_sw->desc);
+		if (ret)
+			goto err_pil_sw;
+	}
+
+	drv->subsys_desc.name = "modem";
+	drv->subsys_desc.depends_on = "adsp";
+	drv->subsys_desc.dev = &pdev->dev;
+	drv->subsys_desc.owner = THIS_MODULE;
+	drv->subsys_desc.start = modem_start;
+	drv->subsys_desc.stop = modem_stop;
+	drv->subsys_desc.shutdown = modem_shutdown;
+	drv->subsys_desc.powerup = modem_powerup;
+	drv->subsys_desc.ramdump = modem_ramdump;
+	drv->subsys_desc.crash_shutdown = modem_crash_shutdown;
+
+	drv->fw_ramdump_dev = create_ramdump_device("modem_fw", &pdev->dev);
+	if (!drv->fw_ramdump_dev) {
+		ret = -ENOMEM;
+		goto err_fw_ramdump;
+	}
+
+	drv->sw_ramdump_dev = create_ramdump_device("modem_sw", &pdev->dev);
+	if (!drv->sw_ramdump_dev) {
+		ret = -ENOMEM;
+		goto err_sw_ramdump;
+	}
+
+	drv->smem_ramdump_dev = create_ramdump_device("smem-modem", &pdev->dev);
+	if (!drv->smem_ramdump_dev) {
+		ret = -ENOMEM;
+		goto err_smem_ramdump;
+	}
+
+	drv->subsys = subsys_register(&drv->subsys_desc);
+	if (IS_ERR(drv->subsys)) {
+		ret = PTR_ERR(drv->subsys);
+		goto err_subsys;
+	}
+	if (!drv->loadable)
+		subsys_default_online(drv->subsys);
+
+	ret = devm_request_irq(&pdev->dev, drv_fw->wdog_irq,
+			modem_wdog_bite_irq, IRQF_TRIGGER_RISING,
+			dev_name(&pdev->dev), drv);
+	if (ret)
+		goto err_irq;
+
+	ret = devm_request_irq(&pdev->dev, drv_sw->wdog_irq,
+			modem_wdog_bite_irq, IRQF_TRIGGER_RISING,
+			dev_name(&pdev->dev), drv);
+	if (ret)
+		goto err_irq;
+
+	ret = smsm_state_cb_register(SMSM_MODEM_STATE, SMSM_RESET,
+			smsm_state_cb, drv);
+	if (ret)
+		goto err_irq;
+	return 0;
+
+err_irq:
+	subsys_unregister(drv->subsys);
+err_subsys:
+	destroy_ramdump_device(drv->smem_ramdump_dev);
+err_smem_ramdump:
+	destroy_ramdump_device(drv->sw_ramdump_dev);
+err_sw_ramdump:
+	destroy_ramdump_device(drv->fw_ramdump_dev);
+err_fw_ramdump:
+	if (drv->loadable)
+		pil_desc_release(&drv_sw->desc);
+err_pil_sw:
+	pil_desc_release(&drv_fw->desc);
+	return ret;
+}
+
+static int __devexit pil_q6v4_modem_driver_exit(struct platform_device *pdev)
+{
+	struct q6v4_modem *drv = platform_get_drvdata(pdev);
+
+	smsm_state_cb_deregister(SMSM_MODEM_STATE, SMSM_RESET,
+			smsm_state_cb, drv);
+	subsys_unregister(drv->subsys);
+	destroy_ramdump_device(drv->smem_ramdump_dev);
+	destroy_ramdump_device(drv->sw_ramdump_dev);
+	destroy_ramdump_device(drv->fw_ramdump_dev);
+	if (drv->loadable) {
+		pil_desc_release(&drv->q6_sw.desc);
+		pil_desc_release(&drv->q6_fw.desc);
+	}
+	return 0;
+}
+
+static struct platform_driver pil_q6v4_modem_driver = {
+	.probe = pil_q6v4_modem_driver_probe,
+	.remove = __devexit_p(pil_q6v4_modem_driver_exit),
+	.driver = {
+		.name = "pil-q6v4-modem",
+		.owner = THIS_MODULE,
+	},
+};
+
+static int __init pil_q6v4_modem_init(void)
+{
+	return platform_driver_register(&pil_q6v4_modem_driver);
+}
+module_init(pil_q6v4_modem_init);
+
+static void __exit pil_q6v4_modem_exit(void)
+{
+	platform_driver_unregister(&pil_q6v4_modem_driver);
+}
+module_exit(pil_q6v4_modem_exit);
+
+MODULE_DESCRIPTION("Support for booting QDSP6v4 (Hexagon) processors");
+MODULE_LICENSE("GPL v2");
diff --git a/arch/arm/mach-msm/pil-q6v4.c b/arch/arm/mach-msm/pil-q6v4.c
index 32cce1d..7f04c64 100644
--- a/arch/arm/mach-msm/pil-q6v4.c
+++ b/arch/arm/mach-msm/pil-q6v4.c
@@ -16,13 +16,11 @@
 #include <linux/io.h>
 #include <linux/ioport.h>
 #include <linux/regulator/consumer.h>
-#include <linux/elf.h>
 #include <linux/delay.h>
 #include <linux/err.h>
 #include <linux/clk.h>
 
 #include <mach/msm_bus.h>
-#include <mach/msm_iomap.h>
 
 #include "peripheral-loader.h"
 #include "pil-q6v4.h"
@@ -35,12 +33,6 @@
 #define QDSP6SS_GFMUX_CTL	0x30
 #define QDSP6SS_PWR_CTL		0x38
 
-#define MSS_S_HCLK_CTL		(MSM_CLK_CTL_BASE + 0x2C70)
-#define MSS_SLP_CLK_CTL		(MSM_CLK_CTL_BASE + 0x2C60)
-#define SFAB_MSS_M_ACLK_CTL	(MSM_CLK_CTL_BASE + 0x2340)
-#define SFAB_MSS_S_HCLK_CTL	(MSM_CLK_CTL_BASE + 0x2C00)
-#define MSS_RESET		(MSM_CLK_CTL_BASE + 0x2C64)
-
 #define Q6SS_SS_ARES		BIT(0)
 #define Q6SS_CORE_ARES		BIT(1)
 #define Q6SS_ISDB_ARES		BIT(2)
@@ -59,29 +51,9 @@
 #define Q6SS_CLK_ENA		BIT(1)
 #define Q6SS_SRC_SWITCH_CLK_OVR	BIT(8)
 
-struct q6v4_data {
-	void __iomem *base;
-	void __iomem *modem_base;
-	unsigned long start_addr;
-	struct regulator *vreg;
-	struct regulator *pll_supply;
-	bool vreg_enabled;
-	struct clk *xo;
-	struct pil_device *pil;
-};
-
-static int pil_q6v4_init_image(struct pil_desc *pil, const u8 *metadata,
-		size_t size)
+int pil_q6v4_make_proxy_votes(struct pil_desc *pil)
 {
-	const struct elf32_hdr *ehdr = (struct elf32_hdr *)metadata;
-	struct q6v4_data *drv = dev_get_drvdata(pil->dev);
-	drv->start_addr = ehdr->e_entry;
-	return 0;
-}
-
-static int pil_q6v4_make_proxy_votes(struct pil_desc *pil)
-{
-	const struct q6v4_data *drv = dev_get_drvdata(pil->dev);
+	const struct q6v4_data *drv = pil_to_q6v4_data(pil);
 	int ret;
 
 	ret = clk_prepare_enable(drv->xo);
@@ -102,19 +74,21 @@
 err:
 	return ret;
 }
+EXPORT_SYMBOL(pil_q6v4_make_proxy_votes);
 
-static void pil_q6v4_remove_proxy_votes(struct pil_desc *pil)
+void pil_q6v4_remove_proxy_votes(struct pil_desc *pil)
 {
-	const struct q6v4_data *drv = dev_get_drvdata(pil->dev);
+	const struct q6v4_data *drv = pil_to_q6v4_data(pil);
 	if (drv->pll_supply)
 		regulator_disable(drv->pll_supply);
 	clk_disable_unprepare(drv->xo);
 }
+EXPORT_SYMBOL(pil_q6v4_remove_proxy_votes);
 
-static int pil_q6v4_power_up(struct device *dev)
+int pil_q6v4_power_up(struct q6v4_data *drv)
 {
 	int err;
-	struct q6v4_data *drv = dev_get_drvdata(dev);
+	struct device *dev = drv->desc.dev;
 
 	err = regulator_set_voltage(drv->vreg, 743750, 743750);
 	if (err) {
@@ -141,68 +115,28 @@
 	drv->vreg_enabled = true;
 	return 0;
 }
+EXPORT_SYMBOL(pil_q6v4_power_up);
 
-static DEFINE_MUTEX(pil_q6v4_modem_lock);
-static unsigned pil_q6v4_modem_count;
-
-/* Bring modem subsystem out of reset */
-static void pil_q6v4_init_modem(void __iomem *base, void __iomem *jtag_clk)
+void pil_q6v4_power_down(struct q6v4_data *drv)
 {
-	mutex_lock(&pil_q6v4_modem_lock);
-	if (!pil_q6v4_modem_count) {
-		/* Enable MSS clocks */
-		writel_relaxed(0x10, SFAB_MSS_M_ACLK_CTL);
-		writel_relaxed(0x10, SFAB_MSS_S_HCLK_CTL);
-		writel_relaxed(0x10, MSS_S_HCLK_CTL);
-		writel_relaxed(0x10, MSS_SLP_CLK_CTL);
-		/* Wait for clocks to enable */
-		mb();
-		udelay(10);
-
-		/* De-assert MSS reset */
-		writel_relaxed(0x0, MSS_RESET);
-		mb();
-		udelay(10);
-		/* Enable MSS */
-		writel_relaxed(0x7, base);
+	if (drv->vreg_enabled) {
+		regulator_disable(drv->vreg);
+		drv->vreg_enabled = false;
 	}
-
-	/* Enable JTAG clocks */
-	/* TODO: Remove if/when Q6 software enables them? */
-	writel_relaxed(0x10, jtag_clk);
-
-	pil_q6v4_modem_count++;
-	mutex_unlock(&pil_q6v4_modem_lock);
 }
+EXPORT_SYMBOL(pil_q6v4_power_down);
 
-/* Put modem subsystem back into reset */
-static void pil_q6v4_shutdown_modem(void)
-{
-	mutex_lock(&pil_q6v4_modem_lock);
-	if (pil_q6v4_modem_count)
-		pil_q6v4_modem_count--;
-	if (pil_q6v4_modem_count == 0)
-		writel_relaxed(0x1, MSS_RESET);
-	mutex_unlock(&pil_q6v4_modem_lock);
-}
-
-static int pil_q6v4_reset(struct pil_desc *pil)
+int pil_q6v4_boot(struct pil_desc *pil)
 {
 	u32 reg, err;
-	const struct q6v4_data *drv = dev_get_drvdata(pil->dev);
-	const struct pil_q6v4_pdata *pdata = pil->dev->platform_data;
+	const struct q6v4_data *drv = pil_to_q6v4_data(pil);
+	unsigned long start_addr = pil_get_entry_addr(pil);
 
-	err = pil_q6v4_power_up(pil->dev);
-	if (err)
-		return err;
 	/* Enable Q6 ACLK */
-	writel_relaxed(0x10, pdata->aclk_reg);
-
-	if (drv->modem_base)
-		pil_q6v4_init_modem(drv->modem_base, pdata->jtag_clk_reg);
+	writel_relaxed(0x10, drv->aclk_reg);
 
 	/* Unhalt bus port */
-	err = msm_bus_axi_portunhalt(pdata->bus_port);
+	err = msm_bus_axi_portunhalt(drv->bus_port);
 	if (err)
 		dev_err(pil->dev, "Failed to unhalt bus port\n");
 
@@ -212,12 +146,12 @@
 	writel_relaxed(reg, drv->base + QDSP6SS_RESET);
 
 	/* Program boot address */
-	writel_relaxed((drv->start_addr >> 8) & 0xFFFFFF,
+	writel_relaxed((start_addr >> 8) & 0xFFFFFF,
 			drv->base + QDSP6SS_RST_EVB);
 
 	/* Program TCM and AHB address ranges */
-	writel_relaxed(pdata->strap_tcm_base, drv->base + QDSP6SS_STRAP_TCM);
-	writel_relaxed(pdata->strap_ahb_upper | pdata->strap_ahb_lower,
+	writel_relaxed(drv->strap_tcm_base, drv->base + QDSP6SS_STRAP_TCM);
+	writel_relaxed(drv->strap_ahb_upper | drv->strap_ahb_lower,
 		       drv->base + QDSP6SS_STRAP_AHB);
 
 	/* Turn off Q6 core clock */
@@ -257,15 +191,15 @@
 
 	return 0;
 }
+EXPORT_SYMBOL(pil_q6v4_boot);
 
-static int pil_q6v4_shutdown(struct pil_desc *pil)
+int pil_q6v4_shutdown(struct pil_desc *pil)
 {
 	u32 reg;
-	struct q6v4_data *drv = dev_get_drvdata(pil->dev);
-	const struct pil_q6v4_pdata *pdata = pil->dev->platform_data;
+	struct q6v4_data *drv = pil_to_q6v4_data(pil);
 
 	/* Make sure bus port is halted */
-	msm_bus_axi_porthalt(pdata->bus_port);
+	msm_bus_axi_porthalt(drv->bus_port);
 
 	/* Turn off Q6 core clock */
 	writel_relaxed(Q6SS_SRC_SWITCH_CLK_OVR,
@@ -279,188 +213,67 @@
 	/* Turn off Q6 memories */
 	writel_relaxed(Q6SS_CLAMP_IO, drv->base + QDSP6SS_PWR_CTL);
 
-	if (drv->modem_base)
-		pil_q6v4_shutdown_modem();
-
-	if (drv->vreg_enabled) {
-		regulator_disable(drv->vreg);
-		drv->vreg_enabled = false;
-	}
-
 	return 0;
 }
+EXPORT_SYMBOL(pil_q6v4_shutdown);
 
-static struct pil_reset_ops pil_q6v4_ops = {
-	.init_image = pil_q6v4_init_image,
-	.auth_and_reset = pil_q6v4_reset,
-	.shutdown = pil_q6v4_shutdown,
-	.proxy_vote = pil_q6v4_make_proxy_votes,
-	.proxy_unvote = pil_q6v4_remove_proxy_votes,
-};
-
-static int pil_q6v4_init_image_trusted(struct pil_desc *pil,
+int pil_q6v4_init_image_trusted(struct pil_desc *pil,
 		const u8 *metadata, size_t size)
 {
-	const struct pil_q6v4_pdata *pdata = pil->dev->platform_data;
-	return pas_init_image(pdata->pas_id, metadata, size);
+	struct q6v4_data *drv = pil_to_q6v4_data(pil);
+	return pas_init_image(drv->pas_id, metadata, size);
 }
+EXPORT_SYMBOL(pil_q6v4_init_image_trusted);
 
-static int pil_q6v4_reset_trusted(struct pil_desc *pil)
+int pil_q6v4_boot_trusted(struct pil_desc *pil)
 {
-	const struct pil_q6v4_pdata *pdata = pil->dev->platform_data;
+	struct q6v4_data *drv = pil_to_q6v4_data(pil);
 	int err;
 
-	err = pil_q6v4_power_up(pil->dev);
+	err = pil_q6v4_power_up(drv);
 	if (err)
 		return err;
 
 	/* Unhalt bus port */
-	err = msm_bus_axi_portunhalt(pdata->bus_port);
+	err = msm_bus_axi_portunhalt(drv->bus_port);
 	if (err)
 		dev_err(pil->dev, "Failed to unhalt bus port\n");
-	return pas_auth_and_reset(pdata->pas_id);
+	return pas_auth_and_reset(drv->pas_id);
 }
+EXPORT_SYMBOL(pil_q6v4_boot_trusted);
 
-static int pil_q6v4_shutdown_trusted(struct pil_desc *pil)
+int pil_q6v4_shutdown_trusted(struct pil_desc *pil)
 {
 	int ret;
-	struct q6v4_data *drv = dev_get_drvdata(pil->dev);
-	struct pil_q6v4_pdata *pdata = pil->dev->platform_data;
+	struct q6v4_data *drv = pil_to_q6v4_data(pil);
 
 	/* Make sure bus port is halted */
-	msm_bus_axi_porthalt(pdata->bus_port);
+	msm_bus_axi_porthalt(drv->bus_port);
 
-	ret = pas_shutdown(pdata->pas_id);
+	ret = pas_shutdown(drv->pas_id);
 	if (ret)
 		return ret;
 
-	if (drv->vreg_enabled) {
-		regulator_disable(drv->vreg);
-		drv->vreg_enabled = false;
-	}
+	pil_q6v4_power_down(drv);
 
 	return ret;
 }
+EXPORT_SYMBOL(pil_q6v4_shutdown_trusted);
 
-static struct pil_reset_ops pil_q6v4_ops_trusted = {
-	.init_image = pil_q6v4_init_image_trusted,
-	.auth_and_reset = pil_q6v4_reset_trusted,
-	.shutdown = pil_q6v4_shutdown_trusted,
-	.proxy_vote = pil_q6v4_make_proxy_votes,
-	.proxy_unvote = pil_q6v4_remove_proxy_votes,
-};
-
-static int __devinit pil_q6v4_driver_probe(struct platform_device *pdev)
+void __devinit
+pil_q6v4_init(struct q6v4_data *drv, const struct pil_q6v4_pdata *pdata)
 {
-	const struct pil_q6v4_pdata *pdata = pdev->dev.platform_data;
-	struct q6v4_data *drv;
-	struct resource *res;
-	struct pil_desc *desc;
-	int ret;
+	drv->strap_tcm_base	= pdata->strap_tcm_base;
+	drv->strap_ahb_upper	= pdata->strap_ahb_upper;
+	drv->strap_ahb_lower	= pdata->strap_ahb_lower;
+	drv->aclk_reg		= pdata->aclk_reg;
+	drv->jtag_clk_reg	= pdata->jtag_clk_reg;
+	drv->pas_id		= pdata->pas_id;
+	drv->bus_port		= pdata->bus_port;
 
-	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-	if (!res)
-		return -EINVAL;
-
-	drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL);
-	if (!drv)
-		return -ENOMEM;
-	platform_set_drvdata(pdev, drv);
-
-	drv->base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
-	if (!drv->base)
-		return -ENOMEM;
-
-	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
-	if (res) {
-		drv->modem_base = devm_ioremap(&pdev->dev, res->start,
-				resource_size(res));
-		if (!drv->modem_base)
-			return -ENOMEM;
-	}
-
-	desc = devm_kzalloc(&pdev->dev, sizeof(*desc), GFP_KERNEL);
-	if (!desc)
-		return -ENOMEM;
-
-	drv->pll_supply = devm_regulator_get(&pdev->dev, "pll_vdd");
-	if (IS_ERR(drv->pll_supply)) {
-		drv->pll_supply = NULL;
-	} else {
-		ret = regulator_set_voltage(drv->pll_supply, 1800000, 1800000);
-		if (ret) {
-			dev_err(&pdev->dev, "failed to set pll voltage\n");
-			return ret;
-		}
-
-		ret = regulator_set_optimum_mode(drv->pll_supply, 100000);
-		if (ret < 0) {
-			dev_err(&pdev->dev, "failed to set pll optimum mode\n");
-			return ret;
-		}
-	}
-
-	desc->name = pdata->name;
-	desc->depends_on = pdata->depends;
-	desc->dev = &pdev->dev;
-	desc->owner = THIS_MODULE;
-	desc->proxy_timeout = 10000;
-
-	if (pas_supported(pdata->pas_id) > 0) {
-		desc->ops = &pil_q6v4_ops_trusted;
-		dev_info(&pdev->dev, "using secure boot\n");
-	} else {
-		desc->ops = &pil_q6v4_ops;
-		dev_info(&pdev->dev, "using non-secure boot\n");
-	}
-
-	drv->vreg = devm_regulator_get(&pdev->dev, "core_vdd");
-	if (IS_ERR(drv->vreg))
-		return PTR_ERR(drv->vreg);
-
-	ret = regulator_set_optimum_mode(drv->vreg, 100000);
-	if (ret < 0) {
-		dev_err(&pdev->dev, "Failed to set regulator's mode.\n");
-		return ret;
-	}
-
-	drv->xo = devm_clk_get(&pdev->dev, "xo");
-	if (IS_ERR(drv->xo))
-		return PTR_ERR(drv->xo);
-
-	drv->pil = msm_pil_register(desc);
-	if (IS_ERR(drv->pil))
-		return PTR_ERR(drv->pil);
-	return 0;
+	regulator_set_optimum_mode(drv->vreg, 100000);
 }
-
-static int __devexit pil_q6v4_driver_exit(struct platform_device *pdev)
-{
-	struct q6v4_data *drv = platform_get_drvdata(pdev);
-	msm_pil_unregister(drv->pil);
-	return 0;
-}
-
-static struct platform_driver pil_q6v4_driver = {
-	.probe = pil_q6v4_driver_probe,
-	.remove = __devexit_p(pil_q6v4_driver_exit),
-	.driver = {
-		.name = "pil_qdsp6v4",
-		.owner = THIS_MODULE,
-	},
-};
-
-static int __init pil_q6v4_init(void)
-{
-	return platform_driver_register(&pil_q6v4_driver);
-}
-module_init(pil_q6v4_init);
-
-static void __exit pil_q6v4_exit(void)
-{
-	platform_driver_unregister(&pil_q6v4_driver);
-}
-module_exit(pil_q6v4_exit);
+EXPORT_SYMBOL(pil_q6v4_init);
 
 MODULE_DESCRIPTION("Support for booting QDSP6v4 (Hexagon) processors");
 MODULE_LICENSE("GPL v2");
diff --git a/arch/arm/mach-msm/pil-q6v4.h b/arch/arm/mach-msm/pil-q6v4.h
index b0b97d0..86e55ea 100644
--- a/arch/arm/mach-msm/pil-q6v4.h
+++ b/arch/arm/mach-msm/pil-q6v4.h
@@ -12,6 +12,8 @@
 #ifndef __MSM_PIL_Q6V4_H
 #define __MSM_PIL_Q6V4_H
 
+#include "peripheral-loader.h"
+
 struct pil_q6v4_pdata {
 	const unsigned long strap_tcm_base;
 	const unsigned long strap_ahb_upper;
@@ -19,8 +21,51 @@
 	void __iomem *aclk_reg;
 	void __iomem *jtag_clk_reg;
 	const char *name;
-	const char *depends;
 	const unsigned pas_id;
 	int bus_port;
 };
+
+struct clk;
+struct pil_device;
+struct regulator;
+
+/**
+ * struct q6v4_data - Q6 processor
+ */
+struct q6v4_data {
+	void __iomem *base;
+	void __iomem *wdog_base;
+	unsigned long strap_tcm_base;
+	unsigned long strap_ahb_upper;
+	unsigned long strap_ahb_lower;
+	void __iomem *aclk_reg;
+	void __iomem *jtag_clk_reg;
+	unsigned pas_id;
+	int bus_port;
+	int wdog_irq;
+
+	struct regulator *vreg;
+	struct regulator *pll_supply;
+	bool vreg_enabled;
+	struct clk *xo;
+
+	struct pil_desc desc;
+};
+
+#define pil_to_q6v4_data(p) container_of(p, struct q6v4_data, desc)
+
+extern int pil_q6v4_make_proxy_votes(struct pil_desc *pil);
+extern void pil_q6v4_remove_proxy_votes(struct pil_desc *pil);
+extern int pil_q6v4_power_up(struct q6v4_data *drv);
+extern void pil_q6v4_power_down(struct q6v4_data *drv);
+extern int pil_q6v4_boot(struct pil_desc *pil);
+extern int pil_q6v4_shutdown(struct pil_desc *pil);
+
+extern int pil_q6v4_init_image_trusted(struct pil_desc *pil,
+		const u8 *metadata, size_t size);
+extern int pil_q6v4_boot_trusted(struct pil_desc *pil);
+extern int pil_q6v4_shutdown_trusted(struct pil_desc *pil);
+extern void __devinit
+pil_q6v4_init(struct q6v4_data *drv, const struct pil_q6v4_pdata *p);
+
 #endif
diff --git a/arch/arm/mach-msm/pil-q6v5-lpass.c b/arch/arm/mach-msm/pil-q6v5-lpass.c
index ed072ea..662377d 100644
--- a/arch/arm/mach-msm/pil-q6v5-lpass.c
+++ b/arch/arm/mach-msm/pil-q6v5-lpass.c
@@ -18,14 +18,38 @@
 #include <linux/err.h>
 #include <linux/of.h>
 #include <linux/clk.h>
+#include <linux/workqueue.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+
 #include <mach/clk.h>
+#include <mach/subsystem_restart.h>
+#include <mach/subsystem_notif.h>
+#include <mach/scm.h>
+
 #include "peripheral-loader.h"
 #include "pil-q6v5.h"
 #include "scm-pas.h"
+#include "ramdump.h"
+#include "sysmon.h"
 
 #define QDSP6SS_RST_EVB			0x010
 #define PROXY_TIMEOUT_MS		10000
 
+struct lpass_data {
+	struct q6v5_data *q6;
+	struct subsys_device *subsys;
+	struct subsys_desc subsys_desc;
+	void *ramdump_dev;
+	int wdog_irq;
+	struct work_struct work;
+	void *riva_notif_hdle;
+	void *modem_notif_hdle;
+	int crash_shutdown;
+};
+
+#define subsys_to_drv(d) container_of(d, struct lpass_data, subsys_desc)
+
 static int pil_lpass_enable_clks(struct q6v5_data *drv)
 {
 	int ret;
@@ -71,7 +95,7 @@
 
 static int pil_lpass_shutdown(struct pil_desc *pil)
 {
-	struct q6v5_data *drv = dev_get_drvdata(pil->dev);
+	struct q6v5_data *drv = container_of(pil, struct q6v5_data, desc);
 
 	pil_q6v5_halt_axi_port(pil, drv->axi_halt_base);
 
@@ -93,7 +117,8 @@
 
 static int pil_lpass_reset(struct pil_desc *pil)
 {
-	struct q6v5_data *drv = dev_get_drvdata(pil->dev);
+	struct q6v5_data *drv = container_of(pil, struct q6v5_data, desc);
+	unsigned long start_addr = pil_get_entry_addr(pil);
 	int ret;
 
 	ret = pil_lpass_enable_clks(drv);
@@ -101,7 +126,7 @@
 		return ret;
 
 	/* Program Image Address */
-	writel_relaxed(((drv->start_addr >> 4) & 0x0FFFFFF0),
+	writel_relaxed((start_addr >> 4) & 0x0FFFFFF0,
 				drv->reg_base + QDSP6SS_RST_EVB);
 
 	ret = pil_q6v5_reset(pil);
@@ -116,7 +141,6 @@
 }
 
 static struct pil_reset_ops pil_lpass_ops = {
-	.init_image = pil_q6v5_init_image,
 	.proxy_vote = pil_q6v5_make_proxy_votes,
 	.proxy_unvote = pil_q6v5_remove_proxy_votes,
 	.auth_and_reset = pil_lpass_reset,
@@ -147,38 +171,224 @@
 	.shutdown = pil_lpass_shutdown_trusted,
 };
 
+static int riva_notifier_cb(struct notifier_block *this, unsigned long code,
+								void *ss_handle)
+{
+	int ret;
+	switch (code) {
+	case SUBSYS_BEFORE_SHUTDOWN:
+		pr_debug("%s: R-Notify: Shutdown started\n", __func__);
+		ret = sysmon_send_event(SYSMON_SS_LPASS, "wcnss",
+				SUBSYS_BEFORE_SHUTDOWN);
+		if (ret < 0)
+			pr_err("%s: sysmon_send_event error %d", __func__, ret);
+		break;
+	}
+	return NOTIFY_DONE;
+}
+
+static struct notifier_block rnb = {
+	.notifier_call = riva_notifier_cb,
+};
+
+static int modem_notifier_cb(struct notifier_block *this, unsigned long code,
+								void *ss_handle)
+{
+	int ret;
+	switch (code) {
+	case SUBSYS_BEFORE_SHUTDOWN:
+		pr_debug("%s: M-Notify: Shutdown started\n", __func__);
+		ret = sysmon_send_event(SYSMON_SS_LPASS, "modem",
+				SUBSYS_BEFORE_SHUTDOWN);
+		if (ret < 0)
+			pr_err("%s: sysmon_send_event error %d", __func__, ret);
+		break;
+	}
+	return NOTIFY_DONE;
+}
+
+static struct notifier_block mnb = {
+	.notifier_call = modem_notifier_cb,
+};
+
+static void adsp_log_failure_reason(void)
+{
+	char *reason;
+	char buffer[81];
+	unsigned size;
+
+	reason = smem_get_entry(SMEM_SSR_REASON_LPASS0, &size);
+
+	if (!reason) {
+		pr_err("ADSP subsystem failure reason: (unknown, smem_get_entry failed).");
+		return;
+	}
+
+	if (reason[0] == '\0') {
+		pr_err("ADSP subsystem failure reason: (unknown, init value found)");
+		return;
+	}
+
+	size = min(size, sizeof(buffer) - 1);
+	memcpy(buffer, reason, size);
+	buffer[size] = '\0';
+	pr_err("ADSP subsystem failure reason: %s", buffer);
+	memset((void *)reason, 0x0, size);
+	wmb();
+}
+
+static void restart_adsp(struct lpass_data *drv)
+{
+	adsp_log_failure_reason();
+	subsystem_restart_dev(drv->subsys);
+}
+
+static void adsp_fatal_fn(struct work_struct *work)
+{
+	struct lpass_data *drv = container_of(work, struct lpass_data, work);
+
+	pr_err("Watchdog bite received from ADSP!\n");
+	restart_adsp(drv);
+}
+
+static void adsp_smsm_state_cb(void *data, uint32_t old_state,
+				uint32_t new_state)
+{
+	struct lpass_data *drv = data;
+
+	/* Ignore if we're the one that set SMSM_RESET */
+	if (drv->crash_shutdown)
+		return;
+
+	if (new_state & SMSM_RESET) {
+		pr_err("%s: ADSP SMSM state changed to SMSM_RESET, new_state = %#x, old_state = %#x\n",
+				__func__, new_state, old_state);
+		restart_adsp(drv);
+	}
+}
+
+#define SCM_Q6_NMI_CMD 0x1
+
+static void send_q6_nmi(void)
+{
+	/* Send NMI to QDSP6 via an SCM call. */
+	scm_call_atomic1(SCM_SVC_UTIL, SCM_Q6_NMI_CMD, 0x1);
+	pr_debug("%s: Q6 NMI was sent.\n", __func__);
+}
+
+#define subsys_to_lpass(d) container_of(d, struct lpass_data, subsys_desc)
+
+static int adsp_shutdown(const struct subsys_desc *subsys)
+{
+	struct lpass_data *drv = subsys_to_lpass(subsys);
+
+	send_q6_nmi();
+	/* The write needs to go through before the q6 is shutdown. */
+	mb();
+	pil_shutdown(&drv->q6->desc);
+	disable_irq_nosync(drv->wdog_irq);
+
+	return 0;
+}
+
+static int adsp_powerup(const struct subsys_desc *subsys)
+{
+	struct lpass_data *drv = subsys_to_lpass(subsys);
+	int ret = 0;
+
+	if (get_restart_level() == RESET_SUBSYS_INDEPENDENT) {
+		pr_debug("%s: Wait for ADSP power up!", __func__);
+		msleep(10000);
+	}
+
+	ret = pil_boot(&drv->q6->desc);
+	enable_irq(drv->wdog_irq);
+
+	return ret;
+}
+
+static struct ramdump_segment segments = { 0xdc00000, 0x1800000 };
+
+static int adsp_ramdump(int enable, const struct subsys_desc *subsys)
+{
+	struct lpass_data *drv = subsys_to_lpass(subsys);
+
+	if (!enable)
+		return 0;
+	return do_ramdump(drv->ramdump_dev, &segments, 1);
+}
+
+static void adsp_crash_shutdown(const struct subsys_desc *subsys)
+{
+	struct lpass_data *drv = subsys_to_lpass(subsys);
+
+	drv->crash_shutdown = 1;
+	send_q6_nmi();
+}
+
+static irqreturn_t adsp_wdog_bite_irq(int irq, void *dev_id)
+{
+	struct lpass_data *drv = dev_id;
+
+	disable_irq_nosync(drv->wdog_irq);
+	schedule_work(&drv->work);
+
+	return IRQ_HANDLED;
+}
+
+static int lpass_start(const struct subsys_desc *desc)
+{
+	struct lpass_data *drv = subsys_to_drv(desc);
+
+	return pil_boot(&drv->q6->desc);
+}
+
+static void lpass_stop(const struct subsys_desc *desc)
+{
+	struct lpass_data *drv = subsys_to_drv(desc);
+	pil_shutdown(&drv->q6->desc);
+}
+
 static int __devinit pil_lpass_driver_probe(struct platform_device *pdev)
 {
-	struct q6v5_data *drv;
+	struct lpass_data *drv;
+	struct q6v5_data *q6;
 	struct pil_desc *desc;
+	int ret;
 
-	desc = pil_q6v5_init(pdev);
-	if (IS_ERR(desc))
-		return PTR_ERR(desc);
+	drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL);
+	if (!drv)
+		return -ENOMEM;
+	platform_set_drvdata(pdev, drv);
 
-	drv = platform_get_drvdata(pdev);
-	if (drv == NULL)
-		return -ENODEV;
+	drv->wdog_irq = platform_get_irq(pdev, 0);
+	if (drv->wdog_irq < 0)
+		return drv->wdog_irq;
 
-	desc->ops = &pil_lpass_ops;
+	q6 = pil_q6v5_init(pdev);
+	if (IS_ERR(q6))
+		return PTR_ERR(q6);
+	drv->q6 = q6;
+
+	desc = &q6->desc;
 	desc->owner = THIS_MODULE;
 	desc->proxy_timeout = PROXY_TIMEOUT_MS;
 
-	drv->core_clk = devm_clk_get(&pdev->dev, "core_clk");
-	if (IS_ERR(drv->core_clk))
-		return PTR_ERR(drv->core_clk);
+	q6->core_clk = devm_clk_get(&pdev->dev, "core_clk");
+	if (IS_ERR(q6->core_clk))
+		return PTR_ERR(q6->core_clk);
 
-	drv->ahb_clk = devm_clk_get(&pdev->dev, "iface_clk");
-	if (IS_ERR(drv->ahb_clk))
-		return PTR_ERR(drv->ahb_clk);
+	q6->ahb_clk = devm_clk_get(&pdev->dev, "iface_clk");
+	if (IS_ERR(q6->ahb_clk))
+		return PTR_ERR(q6->ahb_clk);
 
-	drv->axi_clk = devm_clk_get(&pdev->dev, "bus_clk");
-	if (IS_ERR(drv->axi_clk))
-		return PTR_ERR(drv->axi_clk);
+	q6->axi_clk = devm_clk_get(&pdev->dev, "bus_clk");
+	if (IS_ERR(q6->axi_clk))
+		return PTR_ERR(q6->axi_clk);
 
-	drv->reg_clk = devm_clk_get(&pdev->dev, "reg_clk");
-	if (IS_ERR(drv->reg_clk))
-		return PTR_ERR(drv->reg_clk);
+	q6->reg_clk = devm_clk_get(&pdev->dev, "reg_clk");
+	if (IS_ERR(q6->reg_clk))
+		return PTR_ERR(q6->reg_clk);
 
 	if (pas_supported(PAS_Q6) > 0) {
 		desc->ops = &pil_lpass_ops_trusted;
@@ -188,17 +398,81 @@
 		dev_info(&pdev->dev, "using non-secure boot\n");
 	}
 
-	drv->pil = msm_pil_register(desc);
-	if (IS_ERR(drv->pil))
-		return PTR_ERR(drv->pil);
+	ret = pil_desc_init(desc);
+	if (ret)
+		return ret;
 
+	drv->subsys_desc.name = desc->name;
+	drv->subsys_desc.owner = THIS_MODULE;
+	drv->subsys_desc.dev = &pdev->dev;
+	drv->subsys_desc.shutdown = adsp_shutdown;
+	drv->subsys_desc.powerup = adsp_powerup;
+	drv->subsys_desc.ramdump = adsp_ramdump;
+	drv->subsys_desc.crash_shutdown = adsp_crash_shutdown;
+	drv->subsys_desc.start = lpass_start;
+	drv->subsys_desc.stop = lpass_stop;
+
+	INIT_WORK(&drv->work, adsp_fatal_fn);
+
+	drv->ramdump_dev = create_ramdump_device("adsp", &pdev->dev);
+	if (!drv->ramdump_dev) {
+		ret = -ENOMEM;
+		goto err_ramdump;
+	}
+
+	drv->subsys = subsys_register(&drv->subsys_desc);
+	if (IS_ERR(drv->subsys)) {
+		ret = PTR_ERR(drv->subsys);
+		goto err_subsys;
+	}
+
+	ret = devm_request_irq(&pdev->dev, drv->wdog_irq, adsp_wdog_bite_irq,
+			IRQF_TRIGGER_RISING, dev_name(&pdev->dev), drv);
+	if (ret)
+		goto err_irq;
+
+	ret = smsm_state_cb_register(SMSM_Q6_STATE, SMSM_RESET,
+			adsp_smsm_state_cb, drv);
+	if (ret < 0)
+		goto err_smsm;
+
+	drv->riva_notif_hdle = subsys_notif_register_notifier("riva", &rnb);
+	if (IS_ERR(drv->riva_notif_hdle)) {
+		ret = PTR_ERR(drv->riva_notif_hdle);
+		goto err_notif_riva;
+	}
+
+	drv->modem_notif_hdle = subsys_notif_register_notifier("modem", &mnb);
+	if (IS_ERR(drv->modem_notif_hdle)) {
+		ret = PTR_ERR(drv->modem_notif_hdle);
+		goto err_notif_modem;
+	}
+	return 0;
+err_notif_modem:
+	subsys_notif_unregister_notifier(drv->riva_notif_hdle, &rnb);
+err_notif_riva:
+	smsm_state_cb_deregister(SMSM_Q6_STATE, SMSM_RESET,
+			adsp_smsm_state_cb, drv);
+err_smsm:
+err_irq:
+	subsys_unregister(drv->subsys);
+err_subsys:
+	destroy_ramdump_device(drv->ramdump_dev);
+err_ramdump:
+	pil_desc_release(desc);
 	return 0;
 }
 
 static int __devexit pil_lpass_driver_exit(struct platform_device *pdev)
 {
-	struct q6v5_data *drv = platform_get_drvdata(pdev);
-	msm_pil_unregister(drv->pil);
+	struct lpass_data *drv = platform_get_drvdata(pdev);
+	subsys_notif_unregister_notifier(drv->riva_notif_hdle, &rnb);
+	subsys_notif_unregister_notifier(drv->modem_notif_hdle, &mnb);
+	smsm_state_cb_deregister(SMSM_Q6_STATE, SMSM_RESET,
+			adsp_smsm_state_cb, drv);
+	subsys_unregister(drv->subsys);
+	destroy_ramdump_device(drv->ramdump_dev);
+	pil_desc_release(&drv->q6->desc);
 	return 0;
 }
 
diff --git a/arch/arm/mach-msm/pil-q6v5-mss.c b/arch/arm/mach-msm/pil-q6v5-mss.c
index 7b45a0e..2e25076 100644
--- a/arch/arm/mach-msm/pil-q6v5-mss.c
+++ b/arch/arm/mach-msm/pil-q6v5-mss.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2012, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -17,18 +17,21 @@
 #include <linux/io.h>
 #include <linux/iopoll.h>
 #include <linux/ioport.h>
-#include <linux/elf.h>
 #include <linux/delay.h>
 #include <linux/sched.h>
 #include <linux/clk.h>
 #include <linux/err.h>
 #include <linux/of.h>
 #include <linux/regulator/consumer.h>
+#include <linux/interrupt.h>
 
+#include <mach/subsystem_restart.h>
 #include <mach/clk.h>
+#include <mach/msm_smsm.h>
 
 #include "peripheral-loader.h"
 #include "pil-q6v5.h"
+#include "ramdump.h"
 
 /* Q6 Register Offsets */
 #define QDSP6SS_RST_EVB			0x010
@@ -38,9 +41,6 @@
 #define MSS_MODEM_HALT_BASE		0x200
 #define MSS_NC_HALT_BASE		0x280
 
-/* MSS_CLAMP_IO Register Value */
-#define MSS_IO_UNCLAMP_ALL		0x40
-
 /* RMB Status Register Values */
 #define STATUS_PBL_SUCCESS		0x1
 #define STATUS_XPU_UNLOCKED		0x1
@@ -49,18 +49,50 @@
 /* PBL/MBA interface registers */
 #define RMB_MBA_IMAGE			0x00
 #define RMB_PBL_STATUS			0x04
+#define RMB_MBA_COMMAND			0x08
 #define RMB_MBA_STATUS			0x0C
+#define RMB_PMI_META_DATA		0x10
+#define RMB_PMI_CODE_START		0x14
+#define RMB_PMI_CODE_LENGTH		0x18
 
 #define PROXY_TIMEOUT_MS		10000
 #define POLL_INTERVAL_US		50
 
+#define CMD_META_DATA_READY		0x1
+#define CMD_LOAD_READY			0x2
+
+#define STATUS_META_DATA_AUTH_SUCCESS	0x3
+#define STATUS_AUTH_COMPLETE		0x4
+
+#define MAX_SSR_REASON_LEN 81U
+
+struct mba_data {
+	void __iomem *metadata_base;
+	void __iomem *rmb_base;
+	void __iomem *io_clamp_reg;
+	unsigned long metadata_phys;
+	struct pil_desc desc;
+	struct subsys_device *subsys;
+	struct subsys_desc subsys_desc;
+	u32 img_length;
+	struct q6v5_data *q6;
+	int self_auth;
+	void *ramdump_dev;
+	void *smem_ramdump_dev;
+	bool crash_shutdown;
+	bool ignore_errors;
+};
+
 static int pbl_mba_boot_timeout_ms = 100;
 module_param(pbl_mba_boot_timeout_ms, int, S_IRUGO | S_IWUSR);
 
-static int pil_mss_power_up(struct device *dev)
+static int modem_auth_timeout_ms = 10000;
+module_param(modem_auth_timeout_ms, int, S_IRUGO | S_IWUSR);
+
+static int pil_mss_power_up(struct q6v5_data *drv)
 {
 	int ret;
-	struct q6v5_data *drv = dev_get_drvdata(dev);
+	struct device *dev = drv->desc.dev;
 
 	ret = regulator_enable(drv->vreg);
 	if (ret)
@@ -69,17 +101,14 @@
 	return ret;
 }
 
-static int pil_mss_power_down(struct device *dev)
+static int pil_mss_power_down(struct q6v5_data *drv)
 {
-	struct q6v5_data *drv = dev_get_drvdata(dev);
-
 	return regulator_disable(drv->vreg);
 }
 
 static int pil_mss_enable_clks(struct q6v5_data *drv)
 {
 	int ret;
-	void __iomem *mpll1_config_ctl;
 
 	ret = clk_prepare_enable(drv->ahb_clk);
 	if (ret)
@@ -91,12 +120,6 @@
 	if (ret)
 		goto err_rom_clk;
 
-	/* TODO: Remove when support for 8974v1.0 HW is dropped. */
-	mpll1_config_ctl = ioremap(0xFC981034, 0x4);
-	writel_relaxed(0x0300403D, mpll1_config_ctl);
-	mb();
-	iounmap(mpll1_config_ctl);
-
 	return 0;
 
 err_rom_clk:
@@ -114,14 +137,15 @@
 	clk_disable_unprepare(drv->ahb_clk);
 }
 
-static int wait_for_mba_ready(struct device *dev)
+static int wait_for_mba_ready(struct q6v5_data *drv)
 {
-	struct q6v5_data *drv = dev_get_drvdata(dev);
+	struct device *dev = drv->desc.dev;
+	struct mba_data *mba = platform_get_drvdata(to_platform_device(dev));
 	int ret;
 	u32 status;
 
 	/* Wait for PBL completion. */
-	ret = readl_poll_timeout(drv->rmb_base + RMB_PBL_STATUS, status,
+	ret = readl_poll_timeout(mba->rmb_base + RMB_PBL_STATUS, status,
 		status != 0, POLL_INTERVAL_US, pbl_mba_boot_timeout_ms * 1000);
 	if (ret) {
 		dev_err(dev, "PBL boot timed out\n");
@@ -133,7 +157,7 @@
 	}
 
 	/* Wait for MBA completion. */
-	ret = readl_poll_timeout(drv->rmb_base + RMB_MBA_STATUS, status,
+	ret = readl_poll_timeout(mba->rmb_base + RMB_MBA_STATUS, status,
 		status != 0, POLL_INTERVAL_US, pbl_mba_boot_timeout_ms * 1000);
 	if (ret) {
 		dev_err(dev, "MBA boot timed out\n");
@@ -150,7 +174,7 @@
 
 static int pil_mss_shutdown(struct pil_desc *pil)
 {
-	struct q6v5_data *drv = dev_get_drvdata(pil->dev);
+	struct q6v5_data *drv = container_of(pil, struct q6v5_data, desc);
 
 	pil_q6v5_halt_axi_port(pil, drv->axi_halt_base + MSS_Q6_HALT_BASE);
 	pil_q6v5_halt_axi_port(pil, drv->axi_halt_base + MSS_MODEM_HALT_BASE);
@@ -162,13 +186,13 @@
 	 * writes performed during the shutdown succeed.
 	 */
 	if (drv->is_booted == false) {
-		pil_mss_power_up(pil->dev);
+		pil_mss_power_up(drv);
 		pil_mss_enable_clks(drv);
 	}
 	pil_q6v5_shutdown(pil);
 
 	pil_mss_disable_clks(drv);
-	pil_mss_power_down(pil->dev);
+	pil_mss_power_down(drv);
 
 	writel_relaxed(1, drv->restart_reg);
 
@@ -179,7 +203,10 @@
 
 static int pil_mss_reset(struct pil_desc *pil)
 {
-	struct q6v5_data *drv = dev_get_drvdata(pil->dev);
+	struct q6v5_data *drv = container_of(pil, struct q6v5_data, desc);
+	struct platform_device *pdev = to_platform_device(pil->dev);
+	struct mba_data *mba = platform_get_drvdata(pdev);
+	unsigned long start_addr = pil_get_entry_addr(pil);
 	int ret;
 
 	/* Deassert reset to subsystem and wait for propagation */
@@ -191,7 +218,7 @@
 	 * Bring subsystem out of reset and enable required
 	 * regulators and clocks.
 	 */
-	ret = pil_mss_power_up(pil->dev);
+	ret = pil_mss_power_up(drv);
 	if (ret)
 		goto err_power;
 
@@ -200,25 +227,22 @@
 		goto err_clks;
 
 	/* Program Image Address */
-	if (drv->self_auth) {
-		writel_relaxed(drv->start_addr, drv->rmb_base + RMB_MBA_IMAGE);
+	if (mba->self_auth) {
+		writel_relaxed(start_addr, mba->rmb_base + RMB_MBA_IMAGE);
 		/* Ensure write to RMB base occurs before reset is released. */
 		mb();
 	} else {
-		writel_relaxed((drv->start_addr >> 4) & 0x0FFFFFF0,
+		writel_relaxed((start_addr >> 4) & 0x0FFFFFF0,
 				drv->reg_base + QDSP6SS_RST_EVB);
 	}
 
-	/* De-assert MSS IO clamps */
-	writel_relaxed(MSS_IO_UNCLAMP_ALL, drv->io_clamp_reg);
-
 	ret = pil_q6v5_reset(pil);
 	if (ret)
 		goto err_q6v5_reset;
 
 	/* Wait for MBA to start. Check for PBL and MBA errors while waiting. */
-	if (drv->self_auth) {
-		ret = wait_for_mba_ready(pil->dev);
+	if (mba->self_auth) {
+		ret = wait_for_mba_ready(drv);
 		if (ret)
 			goto err_auth;
 	}
@@ -232,97 +256,433 @@
 err_q6v5_reset:
 	pil_mss_disable_clks(drv);
 err_clks:
-	pil_mss_power_down(pil->dev);
+	pil_mss_power_down(drv);
 err_power:
 	return ret;
 }
 
 static struct pil_reset_ops pil_mss_ops = {
-	.init_image = pil_q6v5_init_image,
 	.proxy_vote = pil_q6v5_make_proxy_votes,
 	.proxy_unvote = pil_q6v5_remove_proxy_votes,
 	.auth_and_reset = pil_mss_reset,
 	.shutdown = pil_mss_shutdown,
 };
 
-static int __devinit pil_mss_driver_probe(struct platform_device *pdev)
+static int pil_mba_make_proxy_votes(struct pil_desc *pil)
 {
-	struct q6v5_data *drv;
-	struct pil_desc *desc;
-	struct resource *res;
+	int ret;
+	struct mba_data *drv = dev_get_drvdata(pil->dev);
+
+	ret = clk_prepare_enable(drv->q6->xo);
+	if (ret) {
+		dev_err(pil->dev, "Failed to enable XO\n");
+		return ret;
+	}
+	return 0;
+}
+
+static void pil_mba_remove_proxy_votes(struct pil_desc *pil)
+{
+	struct mba_data *drv = dev_get_drvdata(pil->dev);
+	clk_disable_unprepare(drv->q6->xo);
+}
+
+static int pil_mba_init_image(struct pil_desc *pil,
+			      const u8 *metadata, size_t size)
+{
+	struct mba_data *drv = dev_get_drvdata(pil->dev);
+	s32 status;
 	int ret;
 
-	desc = pil_q6v5_init(pdev);
-	if (IS_ERR(desc))
-		return PTR_ERR(desc);
-	drv = platform_get_drvdata(pdev);
-	if (drv == NULL)
-		return -ENODEV;
+	/* Copy metadata to assigned shared buffer location */
+	memcpy(drv->metadata_base, metadata, size);
 
-	desc->ops = &pil_mss_ops;
-	desc->owner = THIS_MODULE;
-	desc->proxy_timeout = PROXY_TIMEOUT_MS;
+	/* Initialize length counter to 0 */
+	writel_relaxed(0, drv->rmb_base + RMB_PMI_CODE_LENGTH);
+	drv->img_length = 0;
+
+	/* Pass address of meta-data to the MBA and perform authentication */
+	writel_relaxed(drv->metadata_phys, drv->rmb_base + RMB_PMI_META_DATA);
+	writel_relaxed(CMD_META_DATA_READY, drv->rmb_base + RMB_MBA_COMMAND);
+	ret = readl_poll_timeout(drv->rmb_base + RMB_MBA_STATUS, status,
+		status == STATUS_META_DATA_AUTH_SUCCESS || status < 0,
+		POLL_INTERVAL_US, modem_auth_timeout_ms * 1000);
+	if (ret) {
+		dev_err(pil->dev, "MBA authentication of headers timed out\n");
+	} else if (status < 0) {
+		dev_err(pil->dev, "MBA returned error %d for headers\n",
+				status);
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+static int pil_mba_verify_blob(struct pil_desc *pil, u32 phy_addr,
+			       size_t size)
+{
+	struct mba_data *drv = dev_get_drvdata(pil->dev);
+	s32 status;
+
+	/* Begin image authentication */
+	if (drv->img_length == 0) {
+		writel_relaxed(phy_addr, drv->rmb_base + RMB_PMI_CODE_START);
+		writel_relaxed(CMD_LOAD_READY, drv->rmb_base + RMB_MBA_COMMAND);
+	}
+	/* Increment length counter */
+	drv->img_length += size;
+	writel_relaxed(drv->img_length, drv->rmb_base + RMB_PMI_CODE_LENGTH);
+
+	status = readl_relaxed(drv->rmb_base + RMB_MBA_STATUS);
+	if (status < 0) {
+		dev_err(pil->dev, "MBA returned error %d\n", status);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int pil_mba_auth(struct pil_desc *pil)
+{
+	struct mba_data *drv = dev_get_drvdata(pil->dev);
+	int ret;
+	s32 status;
+
+	/* Wait for all segments to be authenticated or an error to occur */
+	ret = readl_poll_timeout(drv->rmb_base + RMB_MBA_STATUS, status,
+			status == STATUS_AUTH_COMPLETE || status < 0,
+			50, modem_auth_timeout_ms * 1000);
+	if (ret) {
+		dev_err(pil->dev, "MBA authentication of image timed out\n");
+	} else if (status < 0) {
+		dev_err(pil->dev, "MBA returned error %d for image\n", status);
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+static struct pil_reset_ops pil_mba_ops = {
+	.init_image = pil_mba_init_image,
+	.proxy_vote = pil_mba_make_proxy_votes,
+	.proxy_unvote = pil_mba_remove_proxy_votes,
+	.verify_blob = pil_mba_verify_blob,
+	.auth_and_reset = pil_mba_auth,
+};
+
+#define subsys_to_drv(d) container_of(d, struct mba_data, subsys_desc)
+
+static void log_modem_sfr(void)
+{
+	u32 size;
+	char *smem_reason, reason[MAX_SSR_REASON_LEN];
+
+	smem_reason = smem_get_entry(SMEM_SSR_REASON_MSS0, &size);
+	if (!smem_reason || !size) {
+		pr_err("modem subsystem failure reason: (unknown, smem_get_entry failed).\n");
+		return;
+	}
+	if (!smem_reason[0]) {
+		pr_err("modem subsystem failure reason: (unknown, empty string found).\n");
+		return;
+	}
+
+	strlcpy(reason, smem_reason, min(size, sizeof(reason)));
+	pr_err("modem subsystem failure reason: %s.\n", reason);
+
+	smem_reason[0] = '\0';
+	wmb();
+}
+
+static void restart_modem(struct mba_data *drv)
+{
+	log_modem_sfr();
+	drv->ignore_errors = true;
+	subsystem_restart_dev(drv->subsys);
+}
+
+static void smsm_state_cb(void *data, uint32_t old_state, uint32_t new_state)
+{
+	struct mba_data *drv = data;
+
+	/* Ignore if we're the one that set SMSM_RESET */
+	if (drv->crash_shutdown)
+		return;
+
+	if (new_state & SMSM_RESET) {
+		pr_err("Probable fatal error on the modem.\n");
+		restart_modem(drv);
+	}
+}
+
+static int modem_shutdown(const struct subsys_desc *subsys)
+{
+	struct mba_data *drv = subsys_to_drv(subsys);
+
+	/* MBA doesn't support shutdown */
+	pil_shutdown(&drv->q6->desc);
+	return 0;
+}
+
+static int modem_powerup(const struct subsys_desc *subsys)
+{
+	struct mba_data *drv = subsys_to_drv(subsys);
+	int ret;
+	/*
+	 * At this time, the modem is shutdown. Therefore this function cannot
+	 * run concurrently with either the watchdog bite error handler or the
+	 * SMSM callback, making it safe to unset the flag below.
+	 */
+	drv->ignore_errors = false;
+	ret = pil_boot(&drv->q6->desc);
+	if (ret)
+		return ret;
+	ret = pil_boot(&drv->desc);
+	if (ret)
+		pil_shutdown(&drv->q6->desc);
+	return ret;
+}
+
+static void modem_crash_shutdown(const struct subsys_desc *subsys)
+{
+	struct mba_data *drv = subsys_to_drv(subsys);
+	drv->crash_shutdown = true;
+	smsm_reset_modem(SMSM_RESET);
+}
+
+static struct ramdump_segment modem_segments[] = {
+	{0x08400000, 0x0D100000 - 0x08400000},
+};
+
+static struct ramdump_segment smem_segments[] = {
+	{0x0FA00000, 0x0FC00000 - 0x0FA00000},
+};
+
+static int modem_ramdump(int enable, const struct subsys_desc *subsys)
+{
+	struct mba_data *drv = subsys_to_drv(subsys);
+	int ret;
+
+	if (!enable)
+		return 0;
+
+	ret = pil_boot(&drv->q6->desc);
+	if (ret)
+		return ret;
+
+	ret = do_ramdump(drv->ramdump_dev, modem_segments,
+				ARRAY_SIZE(modem_segments));
+	if (ret < 0) {
+		pr_err("Unable to dump modem fw memory (rc = %d).\n", ret);
+		goto out;
+	}
+
+	ret = do_ramdump(drv->smem_ramdump_dev, smem_segments,
+		ARRAY_SIZE(smem_segments));
+	if (ret < 0) {
+		pr_err("Unable to dump smem memory (rc = %d).\n", ret);
+		goto out;
+	}
+
+out:
+	pil_shutdown(&drv->q6->desc);
+	return ret;
+}
+
+static irqreturn_t modem_wdog_bite_irq(int irq, void *dev_id)
+{
+	struct mba_data *drv = dev_id;
+	if (drv->ignore_errors)
+		return IRQ_HANDLED;
+	pr_err("Watchdog bite received from modem software!\n");
+	restart_modem(drv);
+	return IRQ_HANDLED;
+}
+
+static int mss_start(const struct subsys_desc *desc)
+{
+	int ret;
+	struct mba_data *drv = subsys_to_drv(desc);
+
+	ret = pil_boot(&drv->q6->desc);
+	if (ret)
+		return ret;
+	ret = pil_boot(&drv->desc);
+	if (ret)
+		pil_shutdown(&drv->q6->desc);
+	return ret;
+}
+
+static void mss_stop(const struct subsys_desc *desc)
+{
+	struct mba_data *drv = subsys_to_drv(desc);
+	/* MBA doesn't support shutdown */
+	pil_shutdown(&drv->q6->desc);
+}
+
+static int __devinit pil_mss_driver_probe(struct platform_device *pdev)
+{
+	struct mba_data *drv;
+	struct q6v5_data *q6;
+	struct pil_desc *q6_desc, *mba_desc;
+	struct resource *res;
+	int ret, irq;
+
+	drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL);
+	if (!drv)
+		return -ENOMEM;
+	platform_set_drvdata(pdev, drv);
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0)
+		return irq;
+
+	q6 = pil_q6v5_init(pdev);
+	if (IS_ERR(q6))
+		return PTR_ERR(q6);
+	drv->q6 = q6;
+
+	q6_desc = &q6->desc;
+	q6_desc->ops = &pil_mss_ops;
+	q6_desc->owner = THIS_MODULE;
+	q6_desc->proxy_timeout = PROXY_TIMEOUT_MS;
 
 	of_property_read_u32(pdev->dev.of_node, "qcom,pil-self-auth",
 			     &drv->self_auth);
 	if (drv->self_auth) {
 		res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
 						    "rmb_base");
-		drv->rmb_base = devm_ioremap(&pdev->dev, res->start,
-					     resource_size(res));
+		drv->rmb_base = devm_request_and_ioremap(&pdev->dev, res);
 		if (!drv->rmb_base)
 			return -ENOMEM;
+		res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+					    "metadata_base");
+		if (res) {
+			drv->metadata_base = devm_ioremap(&pdev->dev,
+						res->start, resource_size(res));
+			if (!drv->metadata_base)
+				return -ENOMEM;
+			drv->metadata_phys = res->start;
+		}
 	}
 
 	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "restart_reg");
-	drv->restart_reg = devm_ioremap(&pdev->dev, res->start,
-					resource_size(res));
-	if (!drv->restart_reg)
+	q6->restart_reg = devm_request_and_ioremap(&pdev->dev, res);
+	if (!q6->restart_reg)
 		return -ENOMEM;
 
-	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "clamp_reg");
-	drv->io_clamp_reg = devm_ioremap(&pdev->dev, res->start,
-					resource_size(res));
-	if (!drv->io_clamp_reg)
-		return -ENOMEM;
+	q6->vreg = devm_regulator_get(&pdev->dev, "vdd_mss");
+	if (IS_ERR(q6->vreg))
+		return PTR_ERR(q6->vreg);
 
-	drv->vreg = devm_regulator_get(&pdev->dev, "vdd_mss");
-	if (IS_ERR(drv->vreg))
-		return PTR_ERR(drv->vreg);
-
-	ret = regulator_set_voltage(drv->vreg, 1050000, 1050000);
+	ret = regulator_set_voltage(q6->vreg, 1050000, 1050000);
 	if (ret)
 		dev_err(&pdev->dev, "Failed to set regulator's voltage.\n");
 
-	ret = regulator_set_optimum_mode(drv->vreg, 100000);
+	ret = regulator_set_optimum_mode(q6->vreg, 100000);
 	if (ret < 0) {
 		dev_err(&pdev->dev, "Failed to set regulator's mode.\n");
 		return ret;
 	}
 
-	drv->ahb_clk = devm_clk_get(&pdev->dev, "iface_clk");
-	if (IS_ERR(drv->ahb_clk))
-		return PTR_ERR(drv->ahb_clk);
+	q6->ahb_clk = devm_clk_get(&pdev->dev, "iface_clk");
+	if (IS_ERR(q6->ahb_clk))
+		return PTR_ERR(q6->ahb_clk);
 
-	drv->axi_clk = devm_clk_get(&pdev->dev, "bus_clk");
-	if (IS_ERR(drv->axi_clk))
-		return PTR_ERR(drv->axi_clk);
+	q6->axi_clk = devm_clk_get(&pdev->dev, "bus_clk");
+	if (IS_ERR(q6->axi_clk))
+		return PTR_ERR(q6->axi_clk);
 
-	drv->rom_clk = devm_clk_get(&pdev->dev, "mem_clk");
-	if (IS_ERR(drv->rom_clk))
-		return PTR_ERR(drv->rom_clk);
+	q6->rom_clk = devm_clk_get(&pdev->dev, "mem_clk");
+	if (IS_ERR(q6->rom_clk))
+		return PTR_ERR(q6->rom_clk);
 
-	drv->pil = msm_pil_register(desc);
-	if (IS_ERR(drv->pil))
-		return PTR_ERR(drv->pil);
+	ret = pil_desc_init(q6_desc);
+	if (ret)
+		return ret;
+
+	mba_desc = &drv->desc;
+	mba_desc->name = "modem";
+	mba_desc->dev = &pdev->dev;
+	mba_desc->ops = &pil_mba_ops;
+	mba_desc->owner = THIS_MODULE;
+	mba_desc->proxy_timeout = PROXY_TIMEOUT_MS;
+
+	ret = pil_desc_init(mba_desc);
+	if (ret)
+		goto err_mba_desc;
+
+	drv->subsys_desc.name = "modem";
+	drv->subsys_desc.dev = &pdev->dev;
+	drv->subsys_desc.owner = THIS_MODULE;
+	drv->subsys_desc.shutdown = modem_shutdown;
+	drv->subsys_desc.powerup = modem_powerup;
+	drv->subsys_desc.ramdump = modem_ramdump;
+	drv->subsys_desc.crash_shutdown = modem_crash_shutdown;
+	drv->subsys_desc.start = mss_start;
+	drv->subsys_desc.stop = mss_stop;
+
+	drv->ramdump_dev = create_ramdump_device("modem", &pdev->dev);
+	if (!drv->ramdump_dev) {
+		pr_err("%s: Unable to create a modem ramdump device.\n",
+			__func__);
+		ret = -ENOMEM;
+		goto err_ramdump;
+	}
+
+	drv->smem_ramdump_dev = create_ramdump_device("smem-modem", &pdev->dev);
+	if (!drv->smem_ramdump_dev) {
+		pr_err("%s: Unable to create an smem ramdump device.\n",
+			__func__);
+		ret = -ENOMEM;
+		goto err_ramdump_smem;
+	}
+
+	drv->subsys = subsys_register(&drv->subsys_desc);
+	if (IS_ERR(drv->subsys)) {
+		ret = PTR_ERR(drv->subsys);
+		goto err_subsys;
+	}
+
+	ret = devm_request_irq(&pdev->dev, irq, modem_wdog_bite_irq,
+				IRQF_TRIGGER_RISING, "modem_wdog", drv);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "Unable to request watchdog IRQ.\n");
+		goto err_irq;
+	}
+
+	ret = smsm_state_cb_register(SMSM_MODEM_STATE, SMSM_RESET,
+		smsm_state_cb, drv);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "Unable to register SMSM callback!\n");
+		goto err_irq;
+	}
 
 	return 0;
+
+err_irq:
+	subsys_unregister(drv->subsys);
+err_subsys:
+	destroy_ramdump_device(drv->smem_ramdump_dev);
+err_ramdump_smem:
+	destroy_ramdump_device(drv->ramdump_dev);
+err_ramdump:
+	pil_desc_release(mba_desc);
+err_mba_desc:
+	pil_desc_release(q6_desc);
+	return ret;
 }
 
 static int __devexit pil_mss_driver_exit(struct platform_device *pdev)
 {
-	struct q6v5_data *drv = platform_get_drvdata(pdev);
-	msm_pil_unregister(drv->pil);
+	struct mba_data *drv = platform_get_drvdata(pdev);
+	smsm_state_cb_deregister(SMSM_MODEM_STATE, SMSM_RESET,
+			smsm_state_cb, drv);
+	subsys_unregister(drv->subsys);
+	destroy_ramdump_device(drv->smem_ramdump_dev);
+	destroy_ramdump_device(drv->ramdump_dev);
+	pil_desc_release(&drv->desc);
+	pil_desc_release(&drv->q6->desc);
 	return 0;
 }
 
diff --git a/arch/arm/mach-msm/pil-q6v5.c b/arch/arm/mach-msm/pil-q6v5.c
index f4e8844..ab88749 100644
--- a/arch/arm/mach-msm/pil-q6v5.c
+++ b/arch/arm/mach-msm/pil-q6v5.c
@@ -16,7 +16,6 @@
 #include <linux/platform_device.h>
 #include <linux/io.h>
 #include <linux/iopoll.h>
-#include <linux/elf.h>
 #include <linux/err.h>
 #include <linux/of.h>
 #include <linux/clk.h>
@@ -61,7 +60,7 @@
 int pil_q6v5_make_proxy_votes(struct pil_desc *pil)
 {
 	int ret;
-	struct q6v5_data *drv = dev_get_drvdata(pil->dev);
+	struct q6v5_data *drv = container_of(pil, struct q6v5_data, desc);
 
 	ret = clk_prepare_enable(drv->xo);
 	if (ret) {
@@ -74,7 +73,7 @@
 
 void pil_q6v5_remove_proxy_votes(struct pil_desc *pil)
 {
-	struct q6v5_data *drv = dev_get_drvdata(pil->dev);
+	struct q6v5_data *drv = container_of(pil, struct q6v5_data, desc);
 	clk_disable_unprepare(drv->xo);
 }
 EXPORT_SYMBOL(pil_q6v5_remove_proxy_votes);
@@ -100,20 +99,10 @@
 }
 EXPORT_SYMBOL(pil_q6v5_halt_axi_port);
 
-int pil_q6v5_init_image(struct pil_desc *pil, const u8 *metadata,
-			       size_t size)
-{
-	const struct elf32_hdr *ehdr = (struct elf32_hdr *)metadata;
-	struct q6v5_data *drv = dev_get_drvdata(pil->dev);
-	drv->start_addr = ehdr->e_entry;
-	return 0;
-}
-EXPORT_SYMBOL(pil_q6v5_init_image);
-
 void pil_q6v5_shutdown(struct pil_desc *pil)
 {
 	u32 val;
-	struct q6v5_data *drv = dev_get_drvdata(pil->dev);
+	struct q6v5_data *drv = container_of(pil, struct q6v5_data, desc);
 
 	/* Turn off core clock */
 	val = readl_relaxed(drv->reg_base + QDSP6SS_GFMUX_CTL);
@@ -145,7 +134,7 @@
 
 int pil_q6v5_reset(struct pil_desc *pil)
 {
-	struct q6v5_data *drv = dev_get_drvdata(pil->dev);
+	struct q6v5_data *drv = container_of(pil, struct q6v5_data, desc);
 	u32 val;
 
 	/* Assert resets, stop core */
@@ -193,7 +182,7 @@
 }
 EXPORT_SYMBOL(pil_q6v5_reset);
 
-struct pil_desc __devinit *pil_q6v5_init(struct platform_device *pdev)
+struct q6v5_data __devinit *pil_q6v5_init(struct platform_device *pdev)
 {
 	struct q6v5_data *drv;
 	struct resource *res;
@@ -203,25 +192,19 @@
 	drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL);
 	if (!drv)
 		return ERR_PTR(-ENOMEM);
-	platform_set_drvdata(pdev, drv);
 
 	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "qdsp6_base");
-	if (!res)
-		return ERR_PTR(-EINVAL);
-	drv->reg_base = devm_ioremap(&pdev->dev, res->start,
-				     resource_size(res));
+	drv->reg_base = devm_request_and_ioremap(&pdev->dev, res);
 	if (!drv->reg_base)
 		return ERR_PTR(-ENOMEM);
+
 	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "halt_base");
 	drv->axi_halt_base = devm_ioremap(&pdev->dev, res->start,
 					  resource_size(res));
 	if (!drv->axi_halt_base)
 		return ERR_PTR(-ENOMEM);
 
-	desc = devm_kzalloc(&pdev->dev, sizeof(*desc), GFP_KERNEL);
-	if (!desc)
-		return ERR_PTR(-ENOMEM);
-
+	desc = &drv->desc;
 	ret = of_property_read_string(pdev->dev.of_node, "qcom,firmware-name",
 				      &desc->name);
 	if (ret)
@@ -233,6 +216,6 @@
 
 	desc->dev = &pdev->dev;
 
-	return desc;
+	return drv;
 }
 EXPORT_SYMBOL(pil_q6v5_init);
diff --git a/arch/arm/mach-msm/pil-q6v5.h b/arch/arm/mach-msm/pil-q6v5.h
index 03f93fa..ecdaf9b 100644
--- a/arch/arm/mach-msm/pil-q6v5.h
+++ b/arch/arm/mach-msm/pil-q6v5.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2012, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -13,10 +13,11 @@
 #ifndef __MSM_PIL_Q6V5_H
 #define __MSM_PIL_Q6V5_H
 
+#include "peripheral-loader.h"
+
 struct regulator;
 struct clk;
 struct pil_device;
-struct pil_desc;
 struct platform_device;
 
 struct q6v5_data {
@@ -28,23 +29,17 @@
 	struct clk *reg_clk;	/* CPU access registers */
 	struct clk *rom_clk;	/* Boot ROM */
 	void __iomem *axi_halt_base;
-	void __iomem *rmb_base;
 	void __iomem *restart_reg;
-	void __iomem *io_clamp_reg;
-	unsigned long start_addr;
 	struct regulator *vreg;
 	bool is_booted;
-	int self_auth;
-	struct pil_device *pil;
+	struct pil_desc desc;
 };
 
 int pil_q6v5_make_proxy_votes(struct pil_desc *pil);
 void pil_q6v5_remove_proxy_votes(struct pil_desc *pil);
 void pil_q6v5_halt_axi_port(struct pil_desc *pil, void __iomem *halt_base);
-int pil_q6v5_init_image(struct pil_desc *pil, const u8 *metadata,
-			size_t size);
 void pil_q6v5_shutdown(struct pil_desc *pil);
 int pil_q6v5_reset(struct pil_desc *pil);
-struct pil_desc *pil_q6v5_init(struct platform_device *pdev);
+struct q6v5_data *pil_q6v5_init(struct platform_device *pdev);
 
 #endif
diff --git a/arch/arm/mach-msm/pil-riva.c b/arch/arm/mach-msm/pil-riva.c
index dbb4408..7993090 100644
--- a/arch/arm/mach-msm/pil-riva.c
+++ b/arch/arm/mach-msm/pil-riva.c
@@ -13,7 +13,6 @@
 #include <linux/kernel.h>
 #include <linux/err.h>
 #include <linux/io.h>
-#include <linux/elf.h>
 #include <linux/delay.h>
 #include <linux/module.h>
 #include <linux/slab.h>
@@ -23,9 +22,7 @@
 #include <linux/interrupt.h>
 #include <linux/wcnss_wlan.h>
 
-#include <mach/msm_iomap.h>
 #include <mach/subsystem_restart.h>
-#include <mach/peripheral-loader.h>
 
 #include "peripheral-loader.h"
 #include "scm-pas.h"
@@ -54,19 +51,18 @@
 
 #define RIVA_PMU_CCPU_BOOT_REMAP_ADDR	0xA0
 
-#define RIVA_PLL_MODE			(MSM_CLK_CTL_BASE + 0x31A0)
+#define RIVA_PLL_MODE			0x31A0
 #define PLL_MODE_OUTCTRL		BIT(0)
 #define PLL_MODE_BYPASSNL		BIT(1)
 #define PLL_MODE_RESET_N		BIT(2)
 #define PLL_MODE_REF_XO_SEL		0x30
 #define PLL_MODE_REF_XO_SEL_CXO		(2 << 4)
 #define PLL_MODE_REF_XO_SEL_RF		(3 << 4)
-#define RIVA_PLL_L_VAL			(MSM_CLK_CTL_BASE + 0x31A4)
-#define RIVA_PLL_M_VAL			(MSM_CLK_CTL_BASE + 0x31A8)
-#define RIVA_PLL_N_VAL			(MSM_CLK_CTL_BASE + 0x31Ac)
-#define RIVA_PLL_CONFIG			(MSM_CLK_CTL_BASE + 0x31B4)
-#define RIVA_PLL_STATUS			(MSM_CLK_CTL_BASE + 0x31B8)
-#define RIVA_RESET			(MSM_CLK_CTL_BASE + 0x35E0)
+#define RIVA_PLL_L_VAL			0x31A4
+#define RIVA_PLL_M_VAL			0x31A8
+#define RIVA_PLL_N_VAL			0x31Ac
+#define RIVA_PLL_CONFIG			0x31B4
+#define RIVA_RESET			0x35E0
 
 #define RIVA_PMU_ROOT_CLK_SEL		0xC8
 #define RIVA_PMU_ROOT_CLK_SEL_3		BIT(2)
@@ -84,10 +80,10 @@
 
 struct riva_data {
 	void __iomem *base;
-	unsigned long start_addr;
+	void __iomem *cbase;
 	struct clk *xo;
 	struct regulator *pll_supply;
-	struct pil_device *pil;
+	struct pil_desc pil_desc;
 	int irq;
 	int crash;
 	int rst_in_progress;
@@ -133,21 +129,13 @@
 	clk_disable_unprepare(drv->xo);
 }
 
-static int pil_riva_init_image(struct pil_desc *pil, const u8 *metadata,
-		size_t size)
-{
-	const struct elf32_hdr *ehdr = (struct elf32_hdr *)metadata;
-	struct riva_data *drv = dev_get_drvdata(pil->dev);
-	drv->start_addr = ehdr->e_entry;
-	return 0;
-}
-
 static int pil_riva_reset(struct pil_desc *pil)
 {
 	u32 reg, sel;
 	struct riva_data *drv = dev_get_drvdata(pil->dev);
 	void __iomem *base = drv->base;
-	unsigned long start_addr = drv->start_addr;
+	unsigned long start_addr = pil_get_entry_addr(pil);
+	void __iomem *cbase = drv->cbase;
 	bool use_cxo = cxo_is_needed(drv);
 
 	/* Enable A2XB bridge */
@@ -156,26 +144,26 @@
 	writel_relaxed(reg, base + RIVA_PMU_A2XB_CFG);
 
 	/* Program PLL 13 to 960 MHz */
-	reg = readl_relaxed(RIVA_PLL_MODE);
+	reg = readl_relaxed(cbase + RIVA_PLL_MODE);
 	reg &= ~(PLL_MODE_BYPASSNL | PLL_MODE_OUTCTRL | PLL_MODE_RESET_N);
-	writel_relaxed(reg, RIVA_PLL_MODE);
+	writel_relaxed(reg, cbase + RIVA_PLL_MODE);
 
 	if (use_cxo)
-		writel_relaxed(0x40000C00 | 50, RIVA_PLL_L_VAL);
+		writel_relaxed(0x40000C00 | 50, cbase + RIVA_PLL_L_VAL);
 	else
-		writel_relaxed(0x40000C00 | 40, RIVA_PLL_L_VAL);
-	writel_relaxed(0, RIVA_PLL_M_VAL);
-	writel_relaxed(1, RIVA_PLL_N_VAL);
-	writel_relaxed(0x01495227, RIVA_PLL_CONFIG);
+		writel_relaxed(0x40000C00 | 40, cbase + RIVA_PLL_L_VAL);
+	writel_relaxed(0, cbase + RIVA_PLL_M_VAL);
+	writel_relaxed(1, cbase + RIVA_PLL_N_VAL);
+	writel_relaxed(0x01495227, cbase + RIVA_PLL_CONFIG);
 
-	reg = readl_relaxed(RIVA_PLL_MODE);
+	reg = readl_relaxed(cbase + RIVA_PLL_MODE);
 	reg &= ~(PLL_MODE_REF_XO_SEL);
 	reg |= use_cxo ? PLL_MODE_REF_XO_SEL_CXO : PLL_MODE_REF_XO_SEL_RF;
-	writel_relaxed(reg, RIVA_PLL_MODE);
+	writel_relaxed(reg, cbase + RIVA_PLL_MODE);
 
 	/* Enable PLL 13 */
 	reg |= PLL_MODE_BYPASSNL;
-	writel_relaxed(reg, RIVA_PLL_MODE);
+	writel_relaxed(reg, cbase + RIVA_PLL_MODE);
 
 	/*
 	 * H/W requires a 5us delay between disabling the bypass and
@@ -185,9 +173,9 @@
 	usleep_range(10, 20);
 
 	reg |= PLL_MODE_RESET_N;
-	writel_relaxed(reg, RIVA_PLL_MODE);
+	writel_relaxed(reg, cbase + RIVA_PLL_MODE);
 	reg |= PLL_MODE_OUTCTRL;
-	writel_relaxed(reg, RIVA_PLL_MODE);
+	writel_relaxed(reg, cbase + RIVA_PLL_MODE);
 
 	/* Wait for PLL to settle */
 	mb();
@@ -241,20 +229,22 @@
 
 static int pil_riva_shutdown(struct pil_desc *pil)
 {
+	struct riva_data *drv = dev_get_drvdata(pil->dev);
+	void __iomem *cbase = drv->cbase;
+
 	/* Assert reset to Riva */
-	writel_relaxed(1, RIVA_RESET);
+	writel_relaxed(1, cbase + RIVA_RESET);
 	mb();
 	usleep_range(1000, 2000);
 
 	/* Deassert reset to Riva */
-	writel_relaxed(0, RIVA_RESET);
+	writel_relaxed(0, cbase + RIVA_RESET);
 	mb();
 
 	return 0;
 }
 
 static struct pil_reset_ops pil_riva_ops = {
-	.init_image = pil_riva_init_image,
 	.auth_and_reset = pil_riva_reset,
 	.shutdown = pil_riva_shutdown,
 	.proxy_vote = pil_riva_make_proxy_vote,
@@ -372,12 +362,28 @@
 	wcnss_wlan_power(&pdev->dev, pwlanconfig, WCNSS_WLAN_SWITCH_OFF);
 }
 
+static int riva_start(const struct subsys_desc *desc)
+{
+	struct riva_data *drv;
+
+	drv = container_of(desc, struct riva_data, subsys_desc);
+	return pil_boot(&drv->pil_desc);
+}
+
+static void riva_stop(const struct subsys_desc *desc)
+{
+	struct riva_data *drv;
+
+	drv = container_of(desc, struct riva_data, subsys_desc);
+	pil_shutdown(&drv->pil_desc);
+}
+
 static int riva_shutdown(const struct subsys_desc *desc)
 {
 	struct riva_data *drv;
 
 	drv = container_of(desc, struct riva_data, subsys_desc);
-	pil_force_shutdown("wcnss");
+	pil_shutdown(&drv->pil_desc);
 	flush_delayed_work(&drv->cancel_work);
 	wcnss_flush_delayed_boot_votes();
 	disable_irq_nosync(drv->irq);
@@ -397,7 +403,7 @@
 		ret = wcnss_wlan_power(&pdev->dev, pwlanconfig,
 					WCNSS_WLAN_SWITCH_ON);
 		if (!ret)
-			pil_force_boot("wcnss");
+			pil_boot(&drv->pil_desc);
 	}
 	drv->rst_in_progress = 0;
 	enable_irq(drv->irq);
@@ -446,21 +452,20 @@
 	struct pil_desc *desc;
 	int ret;
 
-	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-	if (!res)
-		return -EINVAL;
-
 	drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL);
 	if (!drv)
 		return -ENOMEM;
 	platform_set_drvdata(pdev, drv);
 
-	drv->base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	drv->base = devm_request_and_ioremap(&pdev->dev, res);
 	if (!drv->base)
 		return -ENOMEM;
 
-	desc = devm_kzalloc(&pdev->dev, sizeof(*desc), GFP_KERNEL);
-	if (!desc)
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+	drv->cbase = devm_request_and_ioremap(&pdev->dev, res);
+	if (!drv->cbase)
 		return -ENOMEM;
 
 	drv->pll_supply = devm_regulator_get(&pdev->dev, "pll_vdd");
@@ -488,6 +493,11 @@
 	if (drv->irq < 0)
 		return drv->irq;
 
+	drv->xo = devm_clk_get(&pdev->dev, "cxo");
+	if (IS_ERR(drv->xo))
+		return PTR_ERR(drv->xo);
+
+	desc = &drv->pil_desc;
 	desc->name = "wcnss";
 	desc->dev = &pdev->dev;
 	desc->owner = THIS_MODULE;
@@ -500,14 +510,7 @@
 		desc->ops = &pil_riva_ops;
 		dev_info(&pdev->dev, "using non-secure boot\n");
 	}
-
-	drv->xo = devm_clk_get(&pdev->dev, "cxo");
-	if (IS_ERR(drv->xo))
-		return PTR_ERR(drv->xo);
-
-	drv->pil = msm_pil_register(desc);
-	if (IS_ERR(drv->pil))
-		return PTR_ERR(drv->pil);
+	ret = pil_desc_init(desc);
 
 	ret = smsm_state_cb_register(SMSM_WCNSS_STATE, SMSM_RESET,
 					smsm_state_cb_hdlr, drv);
@@ -515,6 +518,10 @@
 		goto err_smsm;
 
 	drv->subsys_desc.name = "wcnss";
+	drv->subsys_desc.dev = &pdev->dev;
+	drv->subsys_desc.owner = THIS_MODULE;
+	drv->subsys_desc.start = riva_start;
+	drv->subsys_desc.stop = riva_stop;
 	drv->subsys_desc.shutdown = riva_shutdown;
 	drv->subsys_desc.powerup = riva_powerup;
 	drv->subsys_desc.ramdump = riva_ramdump;
@@ -522,7 +529,7 @@
 
 	INIT_DELAYED_WORK(&drv->cancel_work, riva_post_bootup);
 
-	drv->ramdump_dev = create_ramdump_device("riva");
+	drv->ramdump_dev = create_ramdump_device("riva", &pdev->dev);
 	if (!drv->ramdump_dev) {
 		ret = -ENOMEM;
 		goto err_ramdump;
@@ -548,7 +555,7 @@
 	smsm_state_cb_deregister(SMSM_WCNSS_STATE, SMSM_RESET,
 					smsm_state_cb_hdlr, drv);
 err_smsm:
-	msm_pil_unregister(drv->pil);
+	pil_desc_release(desc);
 	return ret;
 }
 
@@ -560,7 +567,7 @@
 	destroy_ramdump_device(drv->ramdump_dev);
 	smsm_state_cb_deregister(SMSM_WCNSS_STATE, SMSM_RESET,
 					smsm_state_cb_hdlr, drv);
-	msm_pil_unregister(drv->pil);
+	pil_desc_release(&drv->pil_desc);
 
 	return 0;
 }
diff --git a/arch/arm/mach-msm/pil-tzapps.c b/arch/arm/mach-msm/pil-tzapps.c
index 2345453..8658e6e 100644
--- a/arch/arm/mach-msm/pil-tzapps.c
+++ b/arch/arm/mach-msm/pil-tzapps.c
@@ -13,12 +13,19 @@
 #include <linux/init.h>
 #include <linux/module.h>
 #include <linux/platform_device.h>
-#include <linux/elf.h>
 #include <linux/err.h>
 
+#include <mach/subsystem_restart.h>
+
 #include "peripheral-loader.h"
 #include "scm-pas.h"
 
+struct tzapps_data {
+	struct pil_desc pil_desc;
+	struct subsys_device *subsys;
+	struct subsys_desc subsys_desc;
+};
+
 static int pil_tzapps_init_image(struct pil_desc *pil, const u8 *metadata,
 		size_t size)
 {
@@ -41,33 +48,63 @@
 	.shutdown = pil_tzapps_shutdown,
 };
 
+#define subsys_to_drv(d) container_of(d, struct tzapps_data, subsys_desc)
+
+static int tzapps_start(const struct subsys_desc *desc)
+{
+	struct tzapps_data *drv = subsys_to_drv(desc);
+
+	return pil_boot(&drv->pil_desc);
+}
+
+static void tzapps_stop(const struct subsys_desc *desc)
+{
+	struct tzapps_data *drv = subsys_to_drv(desc);
+	pil_shutdown(&drv->pil_desc);
+}
+
 static int __devinit pil_tzapps_driver_probe(struct platform_device *pdev)
 {
 	struct pil_desc *desc;
-	struct pil_device *pil;
+	struct tzapps_data *drv;
+	int ret;
 
 	if (pas_supported(PAS_TZAPPS) < 0)
 		return -ENOSYS;
 
-	desc = devm_kzalloc(&pdev->dev, sizeof(*desc), GFP_KERNEL);
-	if (!desc)
+	drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL);
+	if (!drv)
 		return -ENOMEM;
+	platform_set_drvdata(pdev, drv);
 
+	desc = &drv->pil_desc;
 	desc->name = "tzapps";
 	desc->dev = &pdev->dev;
 	desc->ops = &pil_tzapps_ops;
 	desc->owner = THIS_MODULE;
-	pil = msm_pil_register(desc);
-	if (IS_ERR(pil))
-		return PTR_ERR(pil);
-	platform_set_drvdata(pdev, pil);
+	ret = pil_desc_init(desc);
+	if (ret)
+		return ret;
+
+	drv->subsys_desc.name = "tzapps";
+	drv->subsys_desc.dev = &pdev->dev;
+	drv->subsys_desc.owner = THIS_MODULE;
+	drv->subsys_desc.start = tzapps_start;
+	drv->subsys_desc.stop = tzapps_stop;
+
+	drv->subsys = subsys_register(&drv->subsys_desc);
+	if (IS_ERR(drv->subsys)) {
+		pil_desc_release(desc);
+		return PTR_ERR(drv->subsys);
+	}
 	return 0;
 }
 
 static int __devexit pil_tzapps_driver_exit(struct platform_device *pdev)
 {
-	struct pil_device *pil = platform_get_drvdata(pdev);
-	msm_pil_unregister(pil);
+	struct tzapps_data *drv = platform_get_drvdata(pdev);
+	subsys_unregister(drv->subsys);
+	pil_desc_release(&drv->pil_desc);
 	return 0;
 }
 
diff --git a/arch/arm/mach-msm/pil-venus.c b/arch/arm/mach-msm/pil-venus.c
index e331296..47799cc 100644
--- a/arch/arm/mach-msm/pil-venus.c
+++ b/arch/arm/mach-msm/pil-venus.c
@@ -13,7 +13,6 @@
 #include <linux/kernel.h>
 #include <linux/err.h>
 #include <linux/io.h>
-#include <linux/elf.h>
 #include <linux/delay.h>
 #include <linux/module.h>
 #include <linux/slab.h>
@@ -28,6 +27,7 @@
 
 #include <mach/iommu.h>
 #include <mach/iommu_domains.h>
+#include <mach/subsystem_restart.h>
 
 #include "peripheral-loader.h"
 #include "scm-pas.h"
@@ -65,9 +65,10 @@
 struct venus_data {
 	void __iomem *venus_wrapper_base;
 	void __iomem *venus_vbif_base;
-	struct pil_device *pil;
+	struct pil_desc desc;
+	struct subsys_device *subsys;
+	struct subsys_desc subsys_desc;
 	struct regulator *gdsc;
-	phys_addr_t start_addr;
 	struct clk *clks[ARRAY_SIZE(clk_names)];
 	struct device *iommu_fw_ctx;
 	struct iommu_domain *iommu_fw_domain;
@@ -78,6 +79,8 @@
 	u32 fw_max_paddr;
 };
 
+#define subsys_to_drv(d) container_of(d, struct venus_data, subsys_desc)
+
 static int venus_register_domain(u32 fw_max_sz)
 {
 	struct msm_iova_partition venus_fw_partition = {
@@ -178,22 +181,29 @@
 	regulator_disable(drv->gdsc);
 }
 
-static int pil_venus_init_image(struct pil_desc *pil, const u8 *metadata,
-		size_t size)
+static int pil_venus_mem_setup(struct pil_desc *pil, phys_addr_t addr,
+			       size_t size)
 {
-	const struct elf32_hdr *ehdr = (struct elf32_hdr *)metadata;
+	int domain;
 	struct venus_data *drv = dev_get_drvdata(pil->dev);
 
-	drv->start_addr = ehdr->e_entry;
-
-	if (drv->start_addr < drv->fw_min_paddr ||
-	    drv->start_addr >= drv->fw_max_paddr) {
-		dev_err(pil->dev, "fw start addr is not within valid range\n");
-		return -EINVAL;
+	/* TODO: unregister? */
+	if (!drv->venus_domain_num) {
+		size = round_up(size, SZ_4K);
+		domain = venus_register_domain(size);
+		if (domain < 0) {
+			dev_err(pil->dev, "Venus fw iommu domain register failed\n");
+			return -ENODEV;
+		}
+		drv->iommu_fw_domain = msm_get_iommu_domain(domain);
+		if (!drv->iommu_fw_domain) {
+			dev_err(pil->dev, "No iommu fw domain found\n");
+			return -ENODEV;
+		}
+		drv->venus_domain_num = domain;
+		drv->fw_sz = size;
 	}
 
-	drv->fw_sz = drv->fw_max_paddr - drv->start_addr;
-
 	return 0;
 }
 
@@ -202,7 +212,7 @@
 	int rc;
 	struct venus_data *drv = dev_get_drvdata(pil->dev);
 	void __iomem *wrapper_base = drv->venus_wrapper_base;
-	phys_addr_t pa = drv->start_addr;
+	phys_addr_t pa = pil_get_entry_addr(pil);
 	unsigned long iova;
 
 	/*
@@ -321,7 +331,7 @@
 }
 
 static struct pil_reset_ops pil_venus_ops = {
-	.init_image = pil_venus_init_image,
+	.mem_setup = pil_venus_mem_setup,
 	.auth_and_reset = pil_venus_reset,
 	.shutdown = pil_venus_shutdown,
 	.proxy_vote = pil_venus_make_proxy_vote,
@@ -381,6 +391,19 @@
 	.proxy_unvote = pil_venus_remove_proxy_vote,
 };
 
+static int venus_start(const struct subsys_desc *desc)
+{
+	struct venus_data *drv = subsys_to_drv(desc);
+
+	return pil_boot(&drv->desc);
+}
+
+static void venus_stop(const struct subsys_desc *desc)
+{
+	struct venus_data *drv = subsys_to_drv(desc);
+	pil_shutdown(&drv->desc);
+}
+
 static int __devinit pil_venus_probe(struct platform_device *pdev)
 {
 	struct venus_data *drv;
@@ -388,27 +411,20 @@
 	struct pil_desc *desc;
 	int rc;
 
-	res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
-					    "wrapper_base");
-	if (!res)
-		return -EINVAL;
-
 	drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL);
 	if (!drv)
 		return -ENOMEM;
 	platform_set_drvdata(pdev, drv);
 
-	drv->venus_wrapper_base = devm_ioremap(&pdev->dev, res->start,
-					resource_size(res));
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+					    "wrapper_base");
+	drv->venus_wrapper_base = devm_request_and_ioremap(&pdev->dev, res);
 	if (!drv->venus_wrapper_base)
 		return -ENOMEM;
 
 	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "vbif_base");
-	if (!res)
-		return -EINVAL;
-
-	drv->venus_vbif_base = devm_ioremap(&pdev->dev, res->start,
-					resource_size(res));
+	drv->venus_vbif_base = devm_request_and_ioremap(&pdev->dev, res);
 	if (!drv->venus_vbif_base)
 		return -ENOMEM;
 
@@ -428,45 +444,7 @@
 		return -ENODEV;
 	}
 
-	/* Get fw address boundaries */
-	rc = of_property_read_u32(pdev->dev.of_node,
-				  "qcom,firmware-max-paddr",
-				  &drv->fw_max_paddr);
-	if (rc) {
-		dev_err(&pdev->dev, "Failed to get fw max paddr\n");
-		return rc;
-	}
-
-	rc = of_property_read_u32(pdev->dev.of_node,
-				  "qcom,firmware-min-paddr",
-				  &drv->fw_min_paddr);
-	if (rc) {
-		dev_err(&pdev->dev, "Failed to get fw min paddr\n");
-		return rc;
-	}
-
-	if (drv->fw_max_paddr <= drv->fw_min_paddr) {
-		dev_err(&pdev->dev, "Invalid fw max paddr or min paddr\n");
-		return -EINVAL;
-	}
-
-	drv->venus_domain_num =
-		venus_register_domain(drv->fw_max_paddr - drv->fw_min_paddr);
-	if (drv->venus_domain_num < 0) {
-		dev_err(&pdev->dev, "Venus fw iommu domain register failed\n");
-		return -ENODEV;
-	}
-
-	drv->iommu_fw_domain = msm_get_iommu_domain(drv->venus_domain_num);
-	if (!drv->iommu_fw_domain) {
-		dev_err(&pdev->dev, "No iommu fw domain found\n");
-		return -ENODEV;
-	}
-
-	desc = devm_kzalloc(&pdev->dev, sizeof(*desc), GFP_KERNEL);
-	if (!desc)
-		return -ENOMEM;
-
+	desc = &drv->desc;
 	rc = of_property_read_string(pdev->dev.of_node, "qcom,firmware-name",
 				      &desc->name);
 	if (rc)
@@ -484,9 +462,21 @@
 		dev_info(&pdev->dev, "using non-secure boot\n");
 	}
 
-	drv->pil = msm_pil_register(desc);
-	if (IS_ERR(drv->pil))
-		return PTR_ERR(drv->pil);
+	rc = pil_desc_init(desc);
+	if (rc)
+		return rc;
+
+	drv->subsys_desc.name = desc->name;
+	drv->subsys_desc.owner = THIS_MODULE;
+	drv->subsys_desc.dev = &pdev->dev;
+	drv->subsys_desc.start = venus_start;
+	drv->subsys_desc.stop = venus_stop;
+
+	drv->subsys = subsys_register(&drv->subsys_desc);
+	if (IS_ERR(drv->subsys)) {
+		pil_desc_release(desc);
+		return PTR_ERR(drv->subsys);
+	}
 
 	return 0;
 }
@@ -494,7 +484,8 @@
 static int __devexit pil_venus_remove(struct platform_device *pdev)
 {
 	struct venus_data *drv = platform_get_drvdata(pdev);
-	msm_pil_unregister(drv->pil);
+	subsys_unregister(drv->subsys);
+	pil_desc_release(&drv->desc);
 
 	return 0;
 }
diff --git a/arch/arm/mach-msm/pil-vidc.c b/arch/arm/mach-msm/pil-vidc.c
index e4c6a2d..42bb51c 100644
--- a/arch/arm/mach-msm/pil-vidc.c
+++ b/arch/arm/mach-msm/pil-vidc.c
@@ -13,17 +13,20 @@
 #include <linux/init.h>
 #include <linux/module.h>
 #include <linux/platform_device.h>
-#include <linux/elf.h>
 #include <linux/err.h>
 #include <linux/clk.h>
 
+#include <mach/subsystem_restart.h>
+
 #include "peripheral-loader.h"
 #include "scm-pas.h"
 
 struct vidc_data {
 	struct clk *smmu_iface;
 	struct clk *core;
-	struct pil_device *pil;
+	struct pil_desc pil_desc;
+	struct subsys_device *subsys;
+	struct subsys_desc subsys_desc;
 };
 
 static int pil_vidc_init_image(struct pil_desc *pil, const u8 *metadata,
@@ -63,18 +66,29 @@
 	.shutdown = pil_vidc_shutdown,
 };
 
+#define subsys_to_drv(d) container_of(d, struct vidc_data, subsys_desc)
+
+static int vidc_start(const struct subsys_desc *desc)
+{
+	struct vidc_data *drv = subsys_to_drv(desc);
+	return pil_boot(&drv->pil_desc);
+}
+
+static void vidc_stop(const struct subsys_desc *desc)
+{
+	struct vidc_data *drv = subsys_to_drv(desc);
+	pil_shutdown(&drv->pil_desc);
+}
+
 static int __devinit pil_vidc_driver_probe(struct platform_device *pdev)
 {
 	struct pil_desc *desc;
 	struct vidc_data *drv;
+	int ret;
 
 	if (pas_supported(PAS_VIDC) < 0)
 		return -ENOSYS;
 
-	desc = devm_kzalloc(&pdev->dev, sizeof(*desc), GFP_KERNEL);
-	if (!desc)
-		return -ENOMEM;
-
 	drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL);
 	if (!drv)
 		return -ENOMEM;
@@ -88,20 +102,34 @@
 	if (IS_ERR(drv->core))
 		return PTR_ERR(drv->core);
 
+	desc = &drv->pil_desc;
 	desc->name = "vidc";
 	desc->dev = &pdev->dev;
 	desc->ops = &pil_vidc_ops;
 	desc->owner = THIS_MODULE;
-	drv->pil = msm_pil_register(desc);
-	if (IS_ERR(drv->pil))
-		return PTR_ERR(drv->pil);
+	ret = pil_desc_init(desc);
+	if (ret)
+		return ret;
+
+	drv->subsys_desc.name = "vidc";
+	drv->subsys_desc.dev = &pdev->dev;
+	drv->subsys_desc.owner = THIS_MODULE;
+	drv->subsys_desc.start = vidc_start;
+	drv->subsys_desc.stop = vidc_stop;
+
+	drv->subsys = subsys_register(&drv->subsys_desc);
+	if (IS_ERR(drv->subsys)) {
+		pil_desc_release(desc);
+		return PTR_ERR(drv->subsys);
+	}
 	return 0;
 }
 
 static int __devexit pil_vidc_driver_exit(struct platform_device *pdev)
 {
 	struct vidc_data *drv = platform_get_drvdata(pdev);
-	msm_pil_unregister(drv->pil);
+	subsys_unregister(drv->subsys);
+	pil_desc_release(&drv->pil_desc);
 	return 0;
 }
 
diff --git a/arch/arm/mach-msm/platsmp-8625.c b/arch/arm/mach-msm/platsmp-8625.c
index e8f8c59..3b31b9f 100644
--- a/arch/arm/mach-msm/platsmp-8625.c
+++ b/arch/arm/mach-msm/platsmp-8625.c
@@ -120,7 +120,7 @@
 	 */
 	write_pen_release(-1);
 
-	/* clear the IPC1(SPI-8) pending SPI */
+	/* clear the IPC pending SPI */
 	if (power_collapsed) {
 		raise_clear_spi(cpu, false);
 		clear_pending_spi(cpu_data[cpu].ipc_irq);
@@ -173,9 +173,9 @@
 	return 0;
 }
 
-void __iomem *core1_reset_base(void)
+void __iomem *core_reset_base(unsigned int cpu)
 {
-	return cpu_data[1].reset_core_base;
+	return cpu_data[cpu].reset_core_base;
 }
 
 int __cpuinit boot_secondary(unsigned int cpu, struct task_struct *idle)
@@ -217,7 +217,7 @@
 	 */
 
 	if (power_collapsed) {
-		core1_gic_configure_and_raise();
+		gic_configure_and_raise(cpu_data[cpu].ipc_irq, cpu);
 		raise_clear_spi(cpu, true);
 	} else {
 		gic_raise_softirq(cpumask_of(cpu), 1);
diff --git a/arch/arm/mach-msm/platsmp-8910.c b/arch/arm/mach-msm/platsmp-8910.c
new file mode 100644
index 0000000..af2f496
--- /dev/null
+++ b/arch/arm/mach-msm/platsmp-8910.c
@@ -0,0 +1,190 @@
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/jiffies.h>
+#include <linux/smp.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+
+#include <asm/cacheflush.h>
+#include <asm/smp_plat.h>
+#include <asm/hardware/gic.h>
+#include <mach/msm_iomap.h>
+#include "pm.h"
+
+#define BOOT_REMAP_ENABLE 0x01
+
+/*
+ * control for which core is the next to come out of the secondary
+ * boot "holding pen"
+ */
+volatile int pen_release = -1;
+
+/*
+ * Write pen_release in a way that is guaranteed to be visible to all
+ * observers, irrespective of whether they're taking part in coherency
+ * or not.  This is necessary for the hotplug code to work reliably.
+ */
+static void __cpuinit write_pen_release(int val)
+{
+	pen_release = val;
+	smp_wmb();
+	__cpuc_flush_dcache_area((void *)&pen_release, sizeof(pen_release));
+	outer_clean_range(__pa(&pen_release), __pa(&pen_release + 1));
+}
+
+static DEFINE_SPINLOCK(boot_lock);
+
+void __cpuinit platform_secondary_init(unsigned int cpu)
+{
+	WARN_ON(msm_platform_secondary_init(cpu));
+
+	/*
+	 * if any interrupts are already enabled for the primary
+	 * core (e.g. timer irq), then they will not have been enabled
+	 * for us: do so
+	 */
+	gic_secondary_init(0);
+
+	/*
+	 * let the primary processor know we're out of the
+	 * pen, then head off into the C entry point
+	 */
+	write_pen_release(-1);
+
+	/*
+	 * Synchronise with the boot thread.
+	 */
+	spin_lock(&boot_lock);
+	spin_unlock(&boot_lock);
+}
+
+static int  __cpuinit release_secondary_sim(unsigned long base, int cpu)
+{
+	void *base_ptr;
+
+	base_ptr = ioremap_nocache(base + (cpu * 0x10000), SZ_4K);
+	if (!base_ptr) {
+		pr_err("Failed to release core %u\n", cpu);
+		return -ENODEV;
+	}
+
+	writel_relaxed(0x800, base_ptr+0x04);
+	writel_relaxed(0x3FFF, base_ptr+0x14);
+	mb();
+
+	return 0;
+}
+
+DEFINE_PER_CPU(int, cold_boot_done);
+
+int __cpuinit boot_secondary(unsigned int cpu, struct task_struct *idle)
+{
+	unsigned long timeout;
+
+	preset_lpj = loops_per_jiffy;
+
+	if (per_cpu(cold_boot_done, cpu) == false) {
+		release_secondary_sim(0xF9088000, cpu);
+		per_cpu(cold_boot_done, cpu) = true;
+	}
+
+	/*
+	 * Set synchronisation state between this boot processor
+	 * and the secondary one
+	 */
+	spin_lock(&boot_lock);
+
+	/*
+	 * The secondary processor is waiting to be released from
+	 * the holding pen - release it, then wait for it to flag
+	 * that it has been released by resetting pen_release.
+	 *
+	 * Note that "pen_release" is the hardware CPU ID, whereas
+	 * "cpu" is Linux's internal ID.
+	 */
+	write_pen_release(cpu_logical_map(cpu));
+
+	/*
+	 * Send the secondary CPU a soft interrupt, thereby causing
+	 * the boot monitor to read the system wide flags register,
+	 * and branch to the address found there.
+	 */
+
+	gic_raise_softirq(cpumask_of(cpu), 1);
+
+	timeout = jiffies + (1 * HZ);
+	while (time_before(jiffies, timeout)) {
+		smp_rmb();
+		if (pen_release == -1)
+			break;
+
+		udelay(10);
+	}
+
+	/*
+	 * now the secondary core is starting up let it run its
+	 * calibrations, then wait for it to finish
+	 */
+	spin_unlock(&boot_lock);
+
+	return 0;
+}
+
+/*
+ * Initialise the CPU possible map early - this describes the CPUs
+ * which may be present or become present in the system
+ */
+void __init smp_init_cpus(void)
+{
+	unsigned int i, ncores;
+
+	ncores = (__raw_readl(MSM_APCS_GCC_BASE + 0x30)) & 0xF;
+
+	for (i = 0; i < ncores; i++)
+		set_cpu_possible(i, true);
+
+	set_smp_cross_call(gic_raise_softirq);
+}
+
+void __init platform_smp_prepare_cpus(unsigned int max_cpus)
+{
+	int i;
+	void __iomem *remap_ptr;
+
+	/*
+	 * Initialise the present map, which describes the set of CPUs
+	 * actually populated at the present time
+	 */
+	for (i = 0; i < max_cpus; i++)
+		set_cpu_present(i, true);
+
+	/*
+	 * Enable boot remapping and write the address of secondary
+	 * startup into boot remapper register
+	 */
+	remap_ptr = ioremap_nocache(0xF9010000, SZ_4K); /* APCS_CFG_SECURE */
+	if (!remap_ptr) {
+		pr_err("Failed to ioremap for secondary cores\n");
+		return;
+	}
+
+	__raw_writel((virt_to_phys(msm_secondary_startup)|BOOT_REMAP_ENABLE),
+			(remap_ptr + 0x4));
+	mb();
+	iounmap(remap_ptr);
+}
diff --git a/arch/arm/mach-msm/platsmp.c b/arch/arm/mach-msm/platsmp.c
index 89003cf..0933d20 100644
--- a/arch/arm/mach-msm/platsmp.c
+++ b/arch/arm/mach-msm/platsmp.c
@@ -103,8 +103,7 @@
 	if (!base_ptr)
 		return -ENODEV;
 
-	if (machine_is_msm8974_sim() || machine_is_mpq8092_sim() ||
-	    machine_is_msm8226_sim()) {
+	if (machine_is_msm8974_sim() || machine_is_mpq8092_sim()) {
 		writel_relaxed(0x800, base_ptr+0x04);
 		writel_relaxed(0x3FFF, base_ptr+0x14);
 	}
@@ -179,13 +178,11 @@
 	if (cpu_is_msm8x60())
 		return scorpion_release_secondary();
 
-	if (machine_is_msm8974_sim() || machine_is_mpq8092_sim() ||
-	    machine_is_msm8226_sim())
+	if (machine_is_msm8974_sim() || machine_is_mpq8092_sim())
 		return krait_release_secondary_sim(0xf9088000, cpu);
 
-	if (cpu_is_msm8960() || cpu_is_msm8930() || cpu_is_msm8930aa() ||
-	    cpu_is_apq8064() || cpu_is_msm8627() || cpu_is_msm8960ab() ||
-						cpu_is_apq8064ab())
+	if (soc_class_is_msm8960() || soc_class_is_msm8930() ||
+	    soc_class_is_apq8064())
 		return krait_release_secondary(0x02088000, cpu);
 
 	if (cpu_is_msm8974())
diff --git a/arch/arm/mach-msm/pm-8x60.c b/arch/arm/mach-msm/pm-8x60.c
index 60ee8f0..2eac6b7 100644
--- a/arch/arm/mach-msm/pm-8x60.c
+++ b/arch/arm/mach-msm/pm-8x60.c
@@ -30,11 +30,12 @@
 #include <mach/system.h>
 #include <mach/scm.h>
 #include <mach/socinfo.h>
+#include <mach/msm-krait-l2-accessors.h>
 #include <asm/cacheflush.h>
 #include <asm/hardware/gic.h>
 #include <asm/pgtable.h>
 #include <asm/pgalloc.h>
-#include <asm/hardware/cache-l2x0.h>
+#include <asm/outercache.h>
 #ifdef CONFIG_VFP
 #include <asm/vfp.h>
 #endif
@@ -113,6 +114,7 @@
 		"standalone_power_collapse",
 };
 
+static struct msm_pm_init_data_type msm_pm_init_data;
 static struct hrtimer pm_hrtimer;
 static struct msm_pm_sleep_ops pm_sleep_ops;
 /*
@@ -413,13 +415,65 @@
 }
 EXPORT_SYMBOL(msm_pm_set_max_sleep_time);
 
+struct reg_data {
+	uint32_t reg;
+	uint32_t val;
+};
 
-/******************************************************************************
- *
- *****************************************************************************/
+static struct reg_data reg_saved_state[] = {
+	{ .reg = 0x4501, },
+	{ .reg = 0x5501, },
+	{ .reg = 0x6501, },
+	{ .reg = 0x7501, },
+	{ .reg = 0x0500, },
+};
+
+static unsigned int active_vdd;
+static bool msm_pm_save_cp15;
+static const unsigned int pc_vdd = 0x98;
+
+static void msm_pm_save_cpu_reg(void)
+{
+	int i;
+
+	/* Only on core0 */
+	if (smp_processor_id())
+		return;
+
+	/**
+	 * On some targets, L2 PC will turn off may reset the core
+	 * configuration for the mux and the default may not make the core
+	 * happy when it resumes.
+	 * Save the active vdd, and set the core vdd to QSB max vdd, so that
+	 * when the core resumes, it is capable of supporting the current QSB
+	 * rate. Then restore the active vdd before switching the acpuclk rate.
+	 */
+	if (msm_pm_get_l2_flush_flag() == 1) {
+		active_vdd = msm_spm_get_vdd(0);
+		for (i = 0; i < ARRAY_SIZE(reg_saved_state); i++)
+			reg_saved_state[i].val =
+				get_l2_indirect_reg(reg_saved_state[i].reg);
+		msm_spm_set_vdd(0, pc_vdd);
+	}
+}
+
+static void msm_pm_restore_cpu_reg(void)
+{
+	int i;
+
+	/* Only on core0 */
+	if (smp_processor_id())
+		return;
+
+	if (msm_pm_get_l2_flush_flag() == 1) {
+		for (i = 0; i < ARRAY_SIZE(reg_saved_state); i++)
+			set_l2_indirect_reg(reg_saved_state[i].reg,
+					reg_saved_state[i].val);
+		msm_spm_set_vdd(0, active_vdd);
+	}
+}
 
 static void *msm_pm_idle_rs_limits;
-static bool msm_pm_use_qtimer;
 
 static void msm_pm_swfi(void)
 {
@@ -445,24 +499,6 @@
 	msm_pm_config_hw_after_retention();
 }
 
-#ifdef CONFIG_CACHE_L2X0
-static inline bool msm_pm_l2x0_power_collapse(void)
-{
-	bool collapsed = 0;
-
-	l2cc_suspend();
-	collapsed = msm_pm_collapse();
-	l2cc_resume();
-
-	return collapsed;
-}
-#else
-static inline bool msm_pm_l2x0_power_collapse(void)
-{
-	return msm_pm_collapse();
-}
-#endif
-
 static bool __ref msm_pm_spm_power_collapse(
 	unsigned int cpu, bool from_idle, bool notify_rpm)
 {
@@ -493,7 +529,7 @@
 #ifdef CONFIG_VFP
 	vfp_pm_suspend();
 #endif
-	collapsed = msm_pm_l2x0_power_collapse();
+	collapsed = msm_pm_collapse();
 
 	msm_pm_boot_config_after_pc(cpu);
 
@@ -522,11 +558,13 @@
 {
 	unsigned int cpu = smp_processor_id();
 	unsigned int avsdscr_setting;
+	unsigned int avscsr_enable;
 	bool collapsed;
 
 	avsdscr_setting = avs_get_avsdscr();
-	avs_disable();
+	avscsr_enable = avs_disable();
 	collapsed = msm_pm_spm_power_collapse(cpu, from_idle, false);
+	avs_enable(avscsr_enable);
 	avs_reset_delays(avsdscr_setting);
 	return collapsed;
 }
@@ -536,6 +574,7 @@
 	unsigned int cpu = smp_processor_id();
 	unsigned long saved_acpuclk_rate;
 	unsigned int avsdscr_setting;
+	unsigned int avscsr_enable;
 	bool collapsed;
 
 	if (MSM_PM_DEBUG_POWER_COLLAPSE & msm_pm_debug_mask)
@@ -547,7 +586,7 @@
 		pr_info("CPU%u: %s: pre power down\n", cpu, __func__);
 
 	avsdscr_setting = avs_get_avsdscr();
-	avs_disable();
+	avscsr_enable = avs_disable();
 
 	if (cpu_online(cpu))
 		saved_acpuclk_rate = acpuclk_power_collapse();
@@ -558,8 +597,14 @@
 		pr_info("CPU%u: %s: change clock rate (old rate = %lu)\n",
 			cpu, __func__, saved_acpuclk_rate);
 
+	if (msm_pm_save_cp15)
+		msm_pm_save_cpu_reg();
+
 	collapsed = msm_pm_spm_power_collapse(cpu, from_idle, true);
 
+	if (msm_pm_save_cp15)
+		msm_pm_restore_cpu_reg();
+
 	if (cpu_online(cpu)) {
 		if (MSM_PM_DEBUG_CLOCK & msm_pm_debug_mask)
 			pr_info("CPU%u: %s: restore clock rate to %lu\n",
@@ -583,6 +628,7 @@
 	}
 
 
+	avs_enable(avscsr_enable);
 	avs_reset_delays(avsdscr_setting);
 	msm_pm_config_hw_after_power_up();
 	if (MSM_PM_DEBUG_POWER_COLLAPSE & msm_pm_debug_mask)
@@ -593,15 +639,15 @@
 	return collapsed;
 }
 
-static void msm_pm_qtimer_available(void)
+static void msm_pm_target_init(void)
 {
-	if (machine_is_msm8974())
-		msm_pm_use_qtimer = true;
+	if (cpu_is_apq8064())
+		msm_pm_save_cp15 = true;
 }
 
 static int64_t msm_pm_timer_enter_idle(void)
 {
-	if (msm_pm_use_qtimer)
+	if (msm_pm_init_data.use_sync_timer)
 		return ktime_to_ns(tick_nohz_get_sleep_length());
 
 	return msm_timer_enter_idle();
@@ -609,7 +655,7 @@
 
 static void msm_pm_timer_exit_idle(bool timer_halted)
 {
-	if (msm_pm_use_qtimer)
+	if (msm_pm_init_data.use_sync_timer)
 		return;
 
 	msm_timer_exit_idle((int) timer_halted);
@@ -619,7 +665,7 @@
 {
 	int64_t time = 0;
 
-	if (msm_pm_use_qtimer)
+	if (msm_pm_init_data.use_sync_timer)
 		return sched_clock();
 
 	time = msm_timer_get_sclk_time(period);
@@ -631,7 +677,7 @@
 
 static int64_t msm_pm_timer_exit_suspend(int64_t time, int64_t period)
 {
-	if (msm_pm_use_qtimer)
+	if (msm_pm_init_data.use_sync_timer)
 		return sched_clock() - time;
 
 	if (time != 0) {
@@ -718,11 +764,17 @@
 
 		switch (mode) {
 		case MSM_PM_SLEEP_MODE_POWER_COLLAPSE:
+			if (num_online_cpus() > 1) {
+				allow = false;
+				break;
+			}
+			/* fall through */
 		case MSM_PM_SLEEP_MODE_RETENTION:
 			if (!allow)
 				break;
 
-			if (num_online_cpus() > 1) {
+			if (msm_pm_retention_tz_call &&
+				num_online_cpus() > 1) {
 				allow = false;
 				break;
 			}
@@ -1002,7 +1054,7 @@
 
 	msm_pc_debug_counters_phys = res->start;
 	WARN_ON(resource_size(res) < SZ_64);
-	msm_pc_debug_counters = devm_ioremap(&pdev->dev, res->start,
+	msm_pc_debug_counters = devm_ioremap_nocache(&pdev->dev, res->start,
 					resource_size(res));
 
 	if (!msm_pc_debug_counters)
@@ -1087,7 +1139,7 @@
 	msm_pm_add_stats(enable_stats, ARRAY_SIZE(enable_stats));
 
 	suspend_set_ops(&msm_pm_ops);
-	msm_pm_qtimer_available();
+	msm_pm_target_init();
 	hrtimer_init(&pm_hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
 	msm_cpuidle_init();
 	platform_driver_register(&msm_pc_counter_driver);
@@ -1096,3 +1148,73 @@
 }
 
 late_initcall(msm_pm_init);
+
+static void __devinit msm_pm_set_flush_fn(uint32_t pc_mode)
+{
+	msm_pm_disable_l2_fn = NULL;
+	msm_pm_enable_l2_fn = NULL;
+	msm_pm_flush_l2_fn = outer_flush_all;
+
+	if (pc_mode == MSM_PM_PC_NOTZ_L2_EXT) {
+		msm_pm_disable_l2_fn = outer_disable;
+		msm_pm_enable_l2_fn = outer_resume;
+	}
+}
+
+static int __devinit msm_pm_8x60_probe(struct platform_device *pdev)
+{
+	char *key = NULL;
+	uint32_t val = 0;
+	int ret = 0;
+
+	if (!pdev->dev.of_node) {
+		struct msm_pm_init_data_type *d = pdev->dev.platform_data;
+
+		if (!d)
+			goto pm_8x60_probe_done;
+
+		msm_pm_init_data.pc_mode = d->pc_mode;
+		msm_pm_set_flush_fn(msm_pm_init_data.pc_mode);
+		msm_pm_init_data.use_sync_timer = d->use_sync_timer;
+	} else {
+		key = "qcom,pc-mode";
+		ret = of_property_read_u32(pdev->dev.of_node, key, &val);
+
+		if (ret) {
+			pr_debug("%s: Cannot read %s,defaulting to 0",
+					__func__, key);
+			val = MSM_PM_PC_TZ_L2_INT;
+			ret = 0;
+		}
+
+		msm_pm_init_data.pc_mode = val;
+		msm_pm_set_flush_fn(msm_pm_init_data.pc_mode);
+
+		key = "qcom,use-sync-timer";
+		msm_pm_init_data.use_sync_timer =
+			of_property_read_bool(pdev->dev.of_node, key);
+	}
+
+pm_8x60_probe_done:
+	return ret;
+}
+
+static struct of_device_id msm_pm_8x60_table[] = {
+		{.compatible = "qcom,pm-8x60"},
+		{},
+};
+
+static struct platform_driver msm_pm_8x60_driver = {
+		.probe = msm_pm_8x60_probe,
+		.driver = {
+			.name = "pm-8x60",
+			.owner = THIS_MODULE,
+			.of_match_table = msm_pm_8x60_table,
+		},
+};
+
+static int __init msm_pm_8x60_init(void)
+{
+	return platform_driver_register(&msm_pm_8x60_driver);
+}
+module_init(msm_pm_8x60_init);
diff --git a/arch/arm/mach-msm/pm-boot.c b/arch/arm/mach-msm/pm-boot.c
index ed15a0c..f32e149 100644
--- a/arch/arm/mach-msm/pm-boot.c
+++ b/arch/arm/mach-msm/pm-boot.c
@@ -158,13 +158,17 @@
 			msm_pm_boot_after_pc
 				= msm_pm_config_rst_vector_after_pc;
 		} else {
+			uint32_t mpa5_boot_remap_addr[2] = {0x34, 0x4C};
+			uint32_t mpa5_cfg_ctl[2] = {0x30, 0x48};
+
 			warm_boot_ptr = ioremap_nocache(
 						MSM8625_WARM_BOOT_PHYS, SZ_64);
 			ret = msm_pm_boot_reset_vector_init(warm_boot_ptr);
 
 			entry = virt_to_phys(msm_pm_boot_entry);
 
-			/* Below sequence is a work around for cores
+			/*
+			 * Below sequence is a work around for cores
 			 * to come out of GDFS properly on 8625 target.
 			 * On 8625 while cores coming out of GDFS observed
 			 * the memory corruption at very first memory read.
@@ -176,7 +180,8 @@
 			msm_pm_reset_vector[4] = 0xE12FFF10; /* bx  r0 */
 			msm_pm_reset_vector[5] = entry; /* 0x14 */
 
-			/* Here upper 16bits[16:31] used by CORE1
+			/*
+			 * Here upper 16bits[16:31] used by CORE1
 			 * lower 16bits[0:15] used by CORE0
 			 */
 			entry = (MSM8625_WARM_BOOT_PHYS |
@@ -184,17 +189,30 @@
 
 			/* write 'entry' to boot remapper register */
 			__raw_writel(entry, (pdata->v_addr +
-						MPA5_BOOT_REMAP_ADDR));
+						mpa5_boot_remap_addr[0]));
 
-			/* Enable boot remapper for C0 [bit:25th] */
+			/*
+			 * Enable boot remapper for C0 [bit:25th]
+			 * Enable boot remapper for C1 [bit:26th]
+			 */
 			__raw_writel(readl_relaxed(pdata->v_addr +
-					MPA5_CFG_CTL_REG) | BIT(25),
-					pdata->v_addr + MPA5_CFG_CTL_REG);
+					mpa5_cfg_ctl[0]) | (0x3 << 25),
+					pdata->v_addr + mpa5_cfg_ctl[0]);
 
-			/* Enable boot remapper for C1 [bit:26th] */
-			__raw_writel(readl_relaxed(pdata->v_addr +
-					MPA5_CFG_CTL_REG) | BIT(26),
-					pdata->v_addr + MPA5_CFG_CTL_REG);
+			/* 8x25Q changes */
+			if (SOCINFO_VERSION_MAJOR(socinfo_get_version()) >= 3) {
+				/* write 'entry' to boot remapper register */
+				__raw_writel(entry, (pdata->v_addr +
+						mpa5_boot_remap_addr[1]));
+
+				/*
+				 * Enable boot remapper for C2 [bit:25th]
+				 * Enable boot remapper for C3 [bit:26th]
+				 */
+				__raw_writel(readl_relaxed(pdata->v_addr +
+					mpa5_cfg_ctl[1]) | (0x3 << 25),
+					pdata->v_addr + mpa5_cfg_ctl[1]);
+			}
 			msm_pm_boot_before_pc = msm_pm_write_boot_vector;
 		}
 		break;
@@ -211,7 +229,10 @@
 	char *key = NULL;
 	uint32_t val = 0;
 	int ret = 0;
-	int flag = 0;
+	uint32_t vaddr_val;
+
+	pdata.p_addr = 0;
+	vaddr_val = 0;
 
 	key = "qcom,mode";
 	ret = of_property_read_u32(pdev->dev.of_node, key, &val);
@@ -223,24 +244,43 @@
 
 	key = "qcom,phy-addr";
 	ret = of_property_read_u32(pdev->dev.of_node, key, &val);
-	if (ret && pdata.mode == MSM_PM_BOOT_CONFIG_RESET_VECTOR_PHYS)
-		goto fail;
-	if (!ret) {
+	if (!ret)
 		pdata.p_addr = val;
-		flag++;
-	}
+
 
 	key = "qcom,virt-addr";
-	ret = of_property_read_u32(pdev->dev.of_node, key, &val);
-	if (ret && pdata.mode == MSM_PM_BOOT_CONFIG_RESET_VECTOR_VIRT)
-		goto fail;
-	if (!ret) {
-		pdata.v_addr = (void *)val;
-		flag++;
-	}
+	ret = of_property_read_u32(pdev->dev.of_node, key, &vaddr_val);
 
-	if (pdata.mode == MSM_PM_BOOT_CONFIG_REMAP_BOOT_ADDR && (flag != 2)) {
-		key = "addresses for boot remap";
+	switch (pdata.mode) {
+	case MSM_PM_BOOT_CONFIG_RESET_VECTOR_PHYS:
+		if (!pdata.p_addr) {
+			key = "qcom,phy-addr";
+			goto fail;
+		}
+		break;
+	case MSM_PM_BOOT_CONFIG_RESET_VECTOR_VIRT:
+		if (!vaddr_val)
+			goto fail;
+
+		pdata.v_addr = (void *)vaddr_val;
+		break;
+	case MSM_PM_BOOT_CONFIG_REMAP_BOOT_ADDR:
+		if (!vaddr_val)
+			goto fail;
+
+		pdata.v_addr = ioremap_nocache(vaddr_val, SZ_8);
+
+		pdata.p_addr = allocate_contiguous_ebi_nomap(SZ_8, SZ_64K);
+		if (!pdata.p_addr) {
+			key = "qcom,phy-addr";
+			goto fail;
+		}
+		break;
+	case MSM_PM_BOOT_CONFIG_TZ:
+		break;
+	default:
+		pr_err("%s: Unsupported boot mode %d",
+			__func__, pdata.mode);
 		goto fail;
 	}
 
diff --git a/arch/arm/mach-msm/pm-boot.h b/arch/arm/mach-msm/pm-boot.h
index 30b67c21..e39ca75 100644
--- a/arch/arm/mach-msm/pm-boot.h
+++ b/arch/arm/mach-msm/pm-boot.h
@@ -15,7 +15,6 @@
 
 /* 8x25 specific macros */
 #define MPA5_CFG_CTL_REG	0x30
-#define MPA5_BOOT_REMAP_ADDR	0x34
 /* end */
 
 enum {
diff --git a/arch/arm/mach-msm/pm.h b/arch/arm/mach-msm/pm.h
index 51256ca..faefe34 100644
--- a/arch/arm/mach-msm/pm.h
+++ b/arch/arm/mach-msm/pm.h
@@ -87,6 +87,20 @@
 			bool notify_rpm, bool collapsed);
 };
 
+enum msm_pm_pc_mode_type {
+	MSM_PM_PC_TZ_L2_INT = 0,   /*Power collapse terminates in TZ;
+					integrated L2 cache controller */
+	MSM_PM_PC_NOTZ_L2_EXT = 1, /* Power collapse doesn't terminate in
+					TZ; external L2 cache controller */
+	MSM_PM_PC_TZ_L2_EXT = 2,   /* Power collapse terminates in TZ;
+					external L2 cache controller */
+};
+
+struct msm_pm_init_data_type {
+	enum msm_pm_pc_mode_type pc_mode;
+	bool use_sync_timer;
+};
+
 struct msm_pm_cpr_ops {
 	void (*cpr_suspend)(void);
 	void (*cpr_resume)(void);
diff --git a/arch/arm/mach-msm/pm2.c b/arch/arm/mach-msm/pm2.c
index 10c5445..ae2a4bc 100644
--- a/arch/arm/mach-msm/pm2.c
+++ b/arch/arm/mach-msm/pm2.c
@@ -482,66 +482,71 @@
  * Program the top csr from core0 context to put the
  * core1 into GDFS, as core1 is not running yet.
  */
-static void configure_top_csr(void)
+static void msm_pm_configure_top_csr(void)
 {
+	/*
+	 * Enable TCSR for core
+	 * Set reset bit for SPM
+	 * Set CLK_OFF bit
+	 * Set clamps bit
+	 * Set power_up bit
+	 * Disable TSCR for core
+	 */
+	uint32_t bit_pos[][6] = {
+		/* c2 */
+		{17, 15, 13, 16, 14, 17},
+		/* c1 & c3*/
+		{22, 20, 18, 21, 19, 22},
+	};
+	uint32_t mpa5_cfg_ctl[2] = {0x30, 0x48};
 	void __iomem *base_ptr;
 	unsigned int value = 0;
+	unsigned int cpu;
+	int i;
 
-	base_ptr = core1_reset_base();
-	if (!base_ptr)
-		return;
-
-	/* bring the core1 out of reset */
-	__raw_writel(0x3, base_ptr);
-	mb();
-	/*
-	 * override DBGNOPOWERDN and program the GDFS
-	 * count val
-	 */
-
-	 __raw_writel(0x00030002, (MSM_CFG_CTL_BASE + 0x38));
-	mb();
-
-	/* Initialize the SPM0 and SPM1 registers */
+	/* Initialize all the SPM registers */
 	msm_spm_reinit();
 
-	/* enable TCSR for core1 */
-	value = __raw_readl((MSM_CFG_CTL_BASE + MPA5_CFG_CTL_REG));
-	value |= BIT(22);
-	__raw_writel(value,  MSM_CFG_CTL_BASE + MPA5_CFG_CTL_REG);
-	mb();
+	for_each_possible_cpu(cpu) {
+		/* skip for C0 */
+		if (!cpu)
+			continue;
 
-	/* set reset bit for SPM1 */
-	value = __raw_readl((MSM_CFG_CTL_BASE + MPA5_CFG_CTL_REG));
-	value |= BIT(20);
-	__raw_writel(value,  MSM_CFG_CTL_BASE + MPA5_CFG_CTL_REG);
-	mb();
+		base_ptr = core_reset_base(cpu);
+		if (!base_ptr)
+			return;
 
-	/* set CLK_OFF bit */
-	value = __raw_readl((MSM_CFG_CTL_BASE + MPA5_CFG_CTL_REG));
-	value |= BIT(18);
-	__raw_writel(value,  MSM_CFG_CTL_BASE + MPA5_CFG_CTL_REG);
-	mb();
+		/* bring the core out of reset */
+		__raw_writel(0x3, base_ptr);
+		mb();
 
-	/* set clamps bit */
-	value = __raw_readl((MSM_CFG_CTL_BASE + MPA5_CFG_CTL_REG));
-	value |= BIT(21);
-	__raw_writel(value,  MSM_CFG_CTL_BASE + MPA5_CFG_CTL_REG);
-	mb();
+		/*
+		 * i == 0, Enable TCSR for core
+		 * i == 1, Set reset bit for SPM
+		 * i == 2, Set CLK_OFF bit
+		 * i == 3, Set clamps bit
+		 * i == 4, Set power_up bit
+		 */
+		for (i = 0; i < 5; i++) {
+			value = __raw_readl(MSM_CFG_CTL_BASE +
+							mpa5_cfg_ctl[cpu/2]);
+			value |= BIT(bit_pos[cpu%2][i]);
+			__raw_writel(value,  MSM_CFG_CTL_BASE +
+							mpa5_cfg_ctl[cpu/2]);
+			mb();
+		}
 
-	/* set power_up bit */
-	value = __raw_readl((MSM_CFG_CTL_BASE + MPA5_CFG_CTL_REG));
-	value |= BIT(19);
-	__raw_writel(value,  MSM_CFG_CTL_BASE + MPA5_CFG_CTL_REG);
-	mb();
+		/* i == 5, Disable TCSR for core */
+		value = __raw_readl(MSM_CFG_CTL_BASE +
+						mpa5_cfg_ctl[cpu/2]);
+		value &= ~BIT(bit_pos[cpu%2][i]);
+		__raw_writel(value,  MSM_CFG_CTL_BASE +
+						mpa5_cfg_ctl[cpu/2]);
+		mb();
 
-	/* Disable TSCR for core0 */
-	value = __raw_readl((MSM_CFG_CTL_BASE + MPA5_CFG_CTL_REG));
-	value &= ~BIT(22);
-	__raw_writel(value,  MSM_CFG_CTL_BASE + MPA5_CFG_CTL_REG);
-	mb();
-	__raw_writel(0x0, base_ptr);
-	mb();
+		__raw_writel(0x0, base_ptr);
+		mb();
+	}
 }
 
 /*
@@ -569,7 +574,7 @@
 			/*
 			 * Program the top csr to put the core1 into GDFS.
 			 */
-			configure_top_csr();
+			msm_pm_configure_top_csr();
 		}
 	} else {
 		__raw_writel(0, APPS_PWRDOWN);
@@ -981,17 +986,38 @@
 		/*
 		 * on system reset, default value of MPA5_GDFS_CNT_VAL
 		 * is = 0x0, later modem reprogram this value to
-		 * 0x00030004. Once APPS did a power collapse and
-		 * coming out of it expected value of this register
-		 * always be 0x00030004. Incase if APPS sees the value
-		 * as 0x00030002 consider this case as a modem early
-		 * exit.
+		 * 0x00030004/0x000F0004(8x25Q). Once APPS did
+		 * a power collapse and coming out of it expected value
+		 * of this register always be 0x00030004/0x000F0004(8x25Q).
+		 * Incase if APPS sees the value as 0x00030002/0x000F0002(8x25Q)
+		 * consider this case as a modem early exit.
 		 */
 		val = __raw_readl(MSM_CFG_CTL_BASE + 0x38);
-		if (val != 0x00030002)
-			power_collapsed = 1;
-		else
-			modem_early_exit = 1;
+
+		/* 8x25Q */
+		if (SOCINFO_VERSION_MAJOR(socinfo_get_version()) >= 3) {
+			if (val != 0x000F0002) {
+				power_collapsed = 1;
+				/*
+				 * override DBGNOPOWERDN and program the GDFS
+				 * count val
+				 */
+				 __raw_writel(0x000F0002,
+						 (MSM_CFG_CTL_BASE + 0x38));
+			} else
+				modem_early_exit = 1;
+		} else {
+			if (val != 0x00030002) {
+				power_collapsed = 1;
+				/*
+				 * override DBGNOPOWERDN and program the GDFS
+				 * count val
+				 */
+				 __raw_writel(0x00030002,
+						 (MSM_CFG_CTL_BASE + 0x38));
+			} else
+				modem_early_exit = 1;
+		}
 	}
 
 #ifdef CONFIG_CACHE_L2X0
@@ -1684,12 +1710,16 @@
 
 		/*
 		 * Configure the MPA5_GDFS_CNT_VAL register for
-		 * DBGPWRUPEREQ_OVERRIDE[17:16] = Override the
+		 * DBGPWRUPEREQ_OVERRIDE[19:16] = Override the
 		 * DBGNOPOWERDN for each cpu.
 		 * MPA5_GDFS_CNT_VAL[9:0] = Delay counter for
 		 * GDFS control.
 		 */
-		val = 0x00030002;
+		if (SOCINFO_VERSION_MAJOR(socinfo_get_version()) >= 3)
+			val = 0x000F0002;
+		else
+			val = 0x00030002;
+
 		__raw_writel(val, (MSM_CFG_CTL_BASE + 0x38));
 
 		l2x0_base_addr = MSM_L2CC_BASE;
diff --git a/arch/arm/mach-msm/qdsp5v2/audio_a2dp_in.c b/arch/arm/mach-msm/qdsp5v2/audio_a2dp_in.c
index 733b7a1..e396186 100644
--- a/arch/arm/mach-msm/qdsp5v2/audio_a2dp_in.c
+++ b/arch/arm/mach-msm/qdsp5v2/audio_a2dp_in.c
@@ -5,6 +5,7 @@
  *
  * Copyright (C) 2008 HTC Corporation
  * Copyright (C) 2008 Google, Inc.
+ * Copyright (c) 2012 The Linux Foundation. All rights reserved.
  *
  * All source code in this file is licensed under the following license except
  * where indicated.
@@ -34,7 +35,7 @@
 #include <linux/dma-mapping.h>
 #include <linux/msm_audio.h>
 #include <linux/msm_audio_sbc.h>
-#include <linux/android_pmem.h>
+#include <linux/msm_ion.h>
 #include <linux/memory_alloc.h>
 
 #include <mach/iommu.h>
@@ -115,6 +116,8 @@
 	int stopped; /* set when stopped, cleared on flush */
 	int abort; /* set when error, like sample rate mismatch */
 	char *build_id;
+	struct ion_client *client;
+	struct ion_handle *output_buff_handle;
 };
 
 static struct audio_a2dp_in the_audio_a2dp_in;
@@ -848,10 +851,11 @@
 	audio->audrec = NULL;
 	audio->opened = 0;
 	if (audio->data) {
-		iounmap(audio->msm_map);
-		free_contiguous_memory_by_paddr(audio->phys);
+		ion_unmap_kernel(audio->client, audio->output_buff_handle);
+		ion_free(audio->client, audio->output_buff_handle);
 		audio->data = NULL;
 	}
+	ion_client_destroy(audio->client);
 	mutex_unlock(&audio->lock);
 	return 0;
 }
@@ -861,6 +865,11 @@
 	struct audio_a2dp_in *audio = &the_audio_a2dp_in;
 	int rc;
 	int encid;
+	int len = 0;
+	unsigned long ionflag = 0;
+	ion_phys_addr_t addr = 0;
+	struct ion_handle *handle = NULL;
+	struct ion_client *client = NULL;
 
 	mutex_lock(&audio->lock);
 	if (audio->opened) {
@@ -868,22 +877,56 @@
 		goto done;
 	}
 
-	audio->phys = allocate_contiguous_ebi_nomap(DMASZ, SZ_4K);
-	if (audio->phys) {
-		audio->msm_map = ioremap(audio->phys, DMASZ);
-		if (IS_ERR(audio->msm_map)) {
-			MM_ERR("could not map the phys address to kernel"
-							"space\n");
+	client = msm_ion_client_create(UINT_MAX, "Audio_a2dp_in_client");
+	if (IS_ERR_OR_NULL(client)) {
+		MM_ERR("Unable to create ION client\n");
 			rc = -ENOMEM;
-			free_contiguous_memory_by_paddr(audio->phys);
-			goto done;
-		}
-		audio->data = (u8 *)audio->msm_map;
-	} else {
-		MM_ERR("could not allocate DMA buffers\n");
-		rc = -ENOMEM;
-		goto done;
+		goto client_create_error;
 	}
+	audio->client = client;
+
+	MM_DBG("allocating mem sz = %d\n", DMASZ);
+	handle = ion_alloc(client, DMASZ, SZ_4K,
+		ION_HEAP(ION_AUDIO_HEAP_ID), 0);
+	if (IS_ERR_OR_NULL(handle)) {
+		MM_ERR("Unable to create allocate O/P buffers\n");
+		rc = -ENOMEM;
+		goto output_buff_alloc_error;
+		}
+
+	audio->output_buff_handle = handle;
+
+	rc = ion_phys(client , handle, &addr, &len);
+	if (rc) {
+		MM_ERR("O/P buffers:Invalid phy: %x sz: %x\n",
+			(unsigned int) addr, (unsigned int) len);
+		rc = -ENOMEM;
+		goto output_buff_get_phys_error;
+	} else {
+		MM_INFO("O/P buffers:valid phy: %x sz: %x\n",
+			(unsigned int) addr, (unsigned int) len);
+	}
+	audio->phys = (int32_t)addr;
+
+	rc = ion_handle_get_flags(client, handle, &ionflag);
+	if (rc) {
+		MM_ERR("could not get flags for the handle\n");
+		rc = -ENOMEM;
+		goto output_buff_get_flags_error;
+	}
+
+	audio->msm_map = ion_map_kernel(client, handle);
+	if (IS_ERR(audio->data)) {
+		MM_ERR("could not map read buffers,freeing instance 0x%08x\n",
+				(int)audio);
+		rc = -ENOMEM;
+		goto output_buff_map_error;
+	}
+	MM_DBG("read buf: phy addr 0x%08x kernel addr 0x%08x\n",
+		audio->phys, (int)audio->data);
+
+	audio->data = (char *)audio->msm_map;
+
 	MM_DBG("Memory addr = 0x%8x  phy addr = 0x%8x\n",\
 		(int) audio->data, (int) audio->phys);
 
@@ -953,6 +996,13 @@
 	mutex_unlock(&audio->lock);
 	return rc;
 evt_error:
+output_buff_map_error:
+output_buff_get_phys_error:
+output_buff_get_flags_error:
+	 ion_free(client, audio->output_buff_handle);
+output_buff_alloc_error:
+	    ion_client_destroy(client);
+client_create_error:
 	msm_adsp_put(audio->audrec);
 	audpreproc_aenc_free(audio->enc_id);
 	mutex_unlock(&audio->lock);
diff --git a/arch/arm/mach-msm/qdsp5v2/audio_lpa.c b/arch/arm/mach-msm/qdsp5v2/audio_lpa.c
index 60f43b9..7ec0617 100644
--- a/arch/arm/mach-msm/qdsp5v2/audio_lpa.c
+++ b/arch/arm/mach-msm/qdsp5v2/audio_lpa.c
@@ -2,7 +2,7 @@
  *
  * Copyright (C) 2008 Google, Inc.
  * Copyright (C) 2008 HTC Corporation
- * Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
  *
  * This software is licensed under the terms of the GNU General Public
  * License version 2, as published by the Free Software Foundation, and
@@ -27,7 +27,7 @@
 #include <linux/delay.h>
 #include <linux/earlysuspend.h>
 #include <linux/list.h>
-#include <linux/android_pmem.h>
+#include <linux/msm_ion.h>
 #include <asm/atomic.h>
 #include <asm/ioctls.h>
 #include <mach/msm_adsp.h>
@@ -136,9 +136,9 @@
 	union msm_audio_event_payload payload;
 };
 
-struct audlpa_pmem_region {
+struct audlpa_ion_region {
 	struct list_head list;
-	struct file *file;
+	struct ion_handle *handle;
 	int fd;
 	void *vaddr;
 	unsigned long paddr;
@@ -170,7 +170,7 @@
 static void audio_dsp_event(void *private, unsigned id, uint16_t *msg);
 static void audlpa_post_event(struct audio *audio, int type,
 	union msm_audio_event_payload payload);
-static unsigned long audlpa_pmem_fixup(struct audio *audio, void *addr,
+static unsigned long audlpa_ion_fixup(struct audio *audio, void *addr,
 				unsigned long len, int ref_up);
 static void audlpa_async_send_data(struct audio *audio, unsigned needed,
 				uint32_t *payload);
@@ -778,7 +778,7 @@
 	if (drv_evt->event_type == AUDIO_EVENT_WRITE_DONE ||
 	    drv_evt->event_type == AUDIO_EVENT_READ_DONE) {
 		mutex_lock(&audio->lock);
-		audlpa_pmem_fixup(audio, drv_evt->payload.aio_buf.buf_addr,
+		audlpa_ion_fixup(audio, drv_evt->payload.aio_buf.buf_addr,
 				  drv_evt->payload.aio_buf.buf_len, 0);
 		mutex_unlock(&audio->lock);
 	}
@@ -788,94 +788,118 @@
 	return rc;
 }
 
-static int audlpa_pmem_check(struct audio *audio,
+static int audlpa_ion_check(struct audio *audio,
 		void *vaddr, unsigned long len)
 {
-	struct audlpa_pmem_region *region_elt;
-	struct audlpa_pmem_region t = { .vaddr = vaddr, .len = len };
+	struct audlpa_ion_region *region_elt;
+	struct audlpa_ion_region t = {.vaddr = vaddr, .len = len };
 
-	list_for_each_entry(region_elt, &audio->pmem_region_queue, list) {
+	list_for_each_entry(region_elt, &audio->ion_region_queue, list) {
 		if (CONTAINS(region_elt, &t) || CONTAINS(&t, region_elt) ||
 		    OVERLAPS(region_elt, &t)) {
-			MM_ERR("region (vaddr %p len %ld)"
+			MM_ERR("[%p]:region (vaddr %p len %ld)"
 				" clashes with registered region"
 				" (vaddr %p paddr %p len %ld)\n",
-				vaddr, len,
+				audio, vaddr, len,
 				region_elt->vaddr,
-				(void *)region_elt->paddr,
-				region_elt->len);
+				(void *)region_elt->paddr, region_elt->len);
 			return -EINVAL;
 		}
 	}
 
 	return 0;
 }
-
-static int audlpa_pmem_add(struct audio *audio,
-	struct msm_audio_pmem_info *info)
+static int audlpa_ion_add(struct audio *audio,
+			struct msm_audio_ion_info *info)
 {
-	unsigned long paddr, kvaddr, len;
-	struct file *file;
-	struct audlpa_pmem_region *region;
+	ion_phys_addr_t paddr;
+	size_t len;
+	unsigned long kvaddr;
+	struct audlpa_ion_region *region;
 	int rc = -EINVAL;
+	struct ion_handle *handle;
+	unsigned long ionflag;
 
-	MM_DBG("\n"); /* Macro prints the file name and function */
+	MM_ERR("\n"); /* Macro prints the file name and function */
 	region = kmalloc(sizeof(*region), GFP_KERNEL);
 
 	if (!region) {
 		rc = -ENOMEM;
 		goto end;
 	}
-
-	if (get_pmem_file(info->fd, &paddr, &kvaddr, &len, &file)) {
-		kfree(region);
-		goto end;
+	handle = ion_import_dma_buf(audio->client, info->fd);
+	if (IS_ERR_OR_NULL(handle)) {
+		pr_err("%s: could not get handle of the given fd\n", __func__);
+		goto import_error;
 	}
-
-	rc = audlpa_pmem_check(audio, info->vaddr, len);
+	rc = ion_handle_get_flags(audio->client, handle, &ionflag);
+	if (rc) {
+		pr_err("%s: could not get flags for the handle\n", __func__);
+		goto flag_error;
+	}
+	kvaddr = (unsigned long)ion_map_kernel(audio->client, handle);
+	if (IS_ERR_OR_NULL((void *)kvaddr)) {
+		pr_err("%s: could not get virtual address\n", __func__);
+		goto map_error;
+	}
+	rc = ion_phys(audio->client, handle, &paddr, &len);
+	if (rc) {
+		pr_err("%s: could not get physical address\n", __func__);
+		goto ion_error;
+	}
+	rc = audlpa_ion_check(audio, info->vaddr, len);
 	if (rc < 0) {
-		put_pmem_file(file);
-		kfree(region);
-		goto end;
+		MM_ERR("audpcm_ion_check failed\n");
+		goto ion_error;
 	}
-
+	region->handle = handle;
 	region->vaddr = info->vaddr;
 	region->fd = info->fd;
 	region->paddr = paddr;
 	region->kvaddr = kvaddr;
 	region->len = len;
-	region->file = file;
 	region->ref_cnt = 0;
-	MM_DBG("add region paddr %lx vaddr %p, len %lu\n", region->paddr,
-			region->vaddr, region->len);
-	list_add_tail(&region->list, &audio->pmem_region_queue);
+	MM_DBG("[%p]:add region paddr %lx vaddr %p, len %lu kvaddr %lx\n",
+		audio, region->paddr, region->vaddr,
+		region->len, region->kvaddr);
+	list_add_tail(&region->list, &audio->ion_region_queue);
+
+	return rc;
+
+ion_error:
+	ion_unmap_kernel(audio->client, handle);
+map_error:
+flag_error:
+	ion_free(audio->client, handle);
+import_error:
+	kfree(region);
 end:
 	return rc;
 }
 
-static int audlpa_pmem_remove(struct audio *audio,
-	struct msm_audio_pmem_info *info)
+static int audlpa_ion_remove(struct audio *audio,
+			struct msm_audio_ion_info *info)
 {
-	struct audlpa_pmem_region *region;
+	struct audlpa_ion_region *region;
 	struct list_head *ptr, *next;
 	int rc = -EINVAL;
 
-	MM_DBG("info fd %d vaddr %p\n", info->fd, info->vaddr);
+	list_for_each_safe(ptr, next, &audio->ion_region_queue) {
+		region = list_entry(ptr, struct audlpa_ion_region, list);
 
-	list_for_each_safe(ptr, next, &audio->pmem_region_queue) {
-		region = list_entry(ptr, struct audlpa_pmem_region, list);
-
-		if ((region->fd == info->fd) &&
+		if (region != NULL && (region->fd == info->fd) &&
 		    (region->vaddr == info->vaddr)) {
 			if (region->ref_cnt) {
-				MM_DBG("region %p in use ref_cnt %d\n",
-						region, region->ref_cnt);
+				MM_DBG("%s[%p]:region %p in use ref_cnt %d\n",
+					__func__, audio, region,
+					region->ref_cnt);
 				break;
 			}
 			MM_DBG("remove region fd %d vaddr %p\n",
 				info->fd, info->vaddr);
 			list_del(&region->list);
-			put_pmem_file(region->file);
+			ion_unmap_kernel(audio->client, region->handle);
+			ion_free(audio->client, region->handle);
 			kfree(region);
 			rc = 0;
 			break;
@@ -885,23 +909,20 @@
 	return rc;
 }
 
-static int audlpa_pmem_lookup_vaddr(struct audio *audio, void *addr,
-		     unsigned long len, struct audlpa_pmem_region **region)
+static int audlpa_ion_lookup_vaddr(struct audio *audio, void *addr,
+			unsigned long len, struct audlpa_ion_region **region)
 {
-	struct audlpa_pmem_region *region_elt;
-
+	struct audlpa_ion_region *region_elt;
 	int match_count = 0;
-
 	*region = NULL;
 
 	/* returns physical address or zero */
-	list_for_each_entry(region_elt, &audio->pmem_region_queue,
-		list) {
+	list_for_each_entry(region_elt, &audio->ion_region_queue, list) {
 		if (addr >= region_elt->vaddr &&
 		    addr < region_elt->vaddr + region_elt->len &&
 		    addr + len <= region_elt->vaddr + region_elt->len) {
 			/* offset since we could pass vaddr inside a registerd
-			 * pmem buffer
+			 * ion buffer
 			 */
 
 			match_count++;
@@ -911,13 +932,16 @@
 	}
 
 	if (match_count > 1) {
-		MM_ERR("multiple hits for vaddr %p, len %ld\n", addr, len);
-		list_for_each_entry(region_elt,
-		  &audio->pmem_region_queue, list) {
+		MM_ERR("%s[%p]:multiple hits for vaddr %p, len %ld\n",
+			 __func__, audio, addr, len);
+		list_for_each_entry(region_elt, &audio->ion_region_queue,
+					list) {
 			if (addr >= region_elt->vaddr &&
 			    addr < region_elt->vaddr + region_elt->len &&
 			    addr + len <= region_elt->vaddr + region_elt->len)
-				MM_ERR("\t%p, %ld --> %p\n", region_elt->vaddr,
+					MM_ERR("\t%s[%p]:%p, %ld --> %p\n",
+						__func__, audio,
+						region_elt->vaddr,
 						region_elt->len,
 						(void *)region_elt->paddr);
 		}
@@ -925,17 +949,17 @@
 
 	return *region ? 0 : -1;
 }
-
-unsigned long audlpa_pmem_fixup(struct audio *audio, void *addr,
+static unsigned long audlpa_ion_fixup(struct audio *audio, void *addr,
 		    unsigned long len, int ref_up)
 {
-	struct audlpa_pmem_region *region;
+	struct audlpa_ion_region *region;
 	unsigned long paddr;
 	int ret;
 
-	ret = audlpa_pmem_lookup_vaddr(audio, addr, len, &region);
+	ret = audlpa_ion_lookup_vaddr(audio, addr, len, &region);
 	if (ret) {
-		MM_ERR("lookup (%p, %ld) failed\n", addr, len);
+		MM_ERR("%s[%p]:lookup (%p, %ld) failed\n",
+			__func__, audio, addr, len);
 		return 0;
 	}
 	if (ref_up)
@@ -969,7 +993,7 @@
 			buf_node->buf.buf_addr, buf_node->buf.buf_len,
 			buf_node->buf.data_len);
 
-	buf_node->paddr = audlpa_pmem_fixup(
+	buf_node->paddr = audlpa_ion_fixup(
 		audio, buf_node->buf.buf_addr,
 		buf_node->buf.buf_len, 1);
 
@@ -1269,25 +1293,26 @@
 			audio->drv_status &= ~ADRV_STATUS_PAUSE;
 		break;
 
-	case AUDIO_REGISTER_PMEM: {
-			struct msm_audio_pmem_info info;
-			MM_DBG("AUDIO_REGISTER_PMEM\n");
-			if (copy_from_user(&info, (void *) arg, sizeof(info)))
+	case AUDIO_REGISTER_ION: {
+		struct msm_audio_ion_info info;
+		MM_DBG("AUDIO_REGISTER_ION\n");
+		if (copy_from_user(&info, (void *) arg, sizeof(info)))
 				rc = -EFAULT;
 			else
-				rc = audlpa_pmem_add(audio, &info);
+				rc = audlpa_ion_add(audio, &info);
 			break;
 		}
 
-	case AUDIO_DEREGISTER_PMEM: {
-			struct msm_audio_pmem_info info;
-			MM_DBG("AUDIO_DEREGISTER_PMEM\n");
-			if (copy_from_user(&info, (void *) arg, sizeof(info)))
+	case AUDIO_DEREGISTER_ION: {
+		struct msm_audio_ion_info info;
+		MM_DBG("AUDIO_DEREGISTER_ION\n");
+		if (copy_from_user(&info, (void *) arg, sizeof(info)))
 				rc = -EFAULT;
 			else
-				rc = audlpa_pmem_remove(audio, &info);
+				rc = audlpa_ion_remove(audio, &info);
 			break;
 		}
+
 	case AUDIO_ASYNC_WRITE:
 		if (audio->drv_status & ADRV_STATUS_FSYNC)
 			rc = -EBUSY;
@@ -1373,15 +1398,16 @@
 	return audlpa_async_fsync(audio);
 }
 
-static void audlpa_reset_pmem_region(struct audio *audio)
+static void audpcm_reset_ion_region(struct audio *audio)
 {
-	struct audlpa_pmem_region *region;
+	struct audlpa_ion_region *region;
 	struct list_head *ptr, *next;
 
-	list_for_each_safe(ptr, next, &audio->pmem_region_queue) {
-		region = list_entry(ptr, struct audlpa_pmem_region, list);
+	list_for_each_safe(ptr, next, &audio->ion_region_queue) {
+		region = list_entry(ptr, struct audlpa_ion_region, list);
 		list_del(&region->list);
-		put_pmem_file(region->file);
+		ion_unmap_kernel(audio->client, region->handle);
+		ion_free(audio->client, region->handle);
 		kfree(region);
 	}
 
@@ -1399,7 +1425,7 @@
 	auddev_unregister_evt_listner(AUDDEV_CLNT_DEC, audio->dec_id);
 	audio_disable(audio);
 	audlpa_async_flush(audio);
-	audlpa_reset_pmem_region(audio);
+	audpcm_reset_ion_region(audio);
 
 	msm_adsp_put(audio->audplay);
 	audpp_adec_free(audio->dec_id);
@@ -1410,13 +1436,12 @@
 	audio->event_abort = 1;
 	wake_up(&audio->event_wait);
 	audlpa_reset_event_queue(audio);
-	iounmap(audio->data);
-	free_contiguous_memory_by_paddr(audio->phys);
 	mutex_unlock(&audio->lock);
 #ifdef CONFIG_DEBUG_FS
 	if (audio->dentry)
 		debugfs_remove(audio->dentry);
 #endif
+	ion_client_destroy(audio->client);
 	kfree(audio);
 	return 0;
 }
@@ -1589,7 +1614,7 @@
 	spin_lock_init(&audio->dsp_lock);
 	init_waitqueue_head(&audio->write_wait);
 	INIT_LIST_HEAD(&audio->out_queue);
-	INIT_LIST_HEAD(&audio->pmem_region_queue);
+	INIT_LIST_HEAD(&audio->ion_region_queue);
 	INIT_LIST_HEAD(&audio->free_event_queue);
 	INIT_LIST_HEAD(&audio->event_queue);
 	init_waitqueue_head(&audio->wait);
@@ -1650,13 +1675,19 @@
 			break;
 		}
 	}
+
+	audio->client = msm_ion_client_create(UINT_MAX, "Audio_LPA_Client");
+	if (IS_ERR_OR_NULL(audio->client)) {
+		pr_err("Unable to create ION client\n");
+		goto err;
+	}
+	MM_DBG("Ion client created\n");
+
 done:
 	return rc;
 event_err:
 	msm_adsp_put(audio->audplay);
 err:
-	iounmap(audio->data);
-	free_contiguous_memory_by_paddr(audio->phys);
 	audpp_adec_free(audio->dec_id);
 	MM_INFO("audio instance 0x%08x freeing\n", (int)audio);
 	kfree(audio);
diff --git a/arch/arm/mach-msm/qdsp5v2/audio_out.c b/arch/arm/mach-msm/qdsp5v2/audio_out.c
index 147ac77..e5c59ba 100644
--- a/arch/arm/mach-msm/qdsp5v2/audio_out.c
+++ b/arch/arm/mach-msm/qdsp5v2/audio_out.c
@@ -3,7 +3,7 @@
  *
  * Copyright (C) 2008 Google, Inc.
  * Copyright (C) 2008 HTC Corporation
- * Copyright (c) 2009-2012, Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2009-2012, The Linux Foundation. All rights reserved.
  *
  * This software is licensed under the terms of the GNU General Public
  * License version 2, as published by the Free Software Foundation, and
@@ -43,7 +43,7 @@
 #include <mach/qdsp5v2/audio_dev_ctl.h>
 #include <mach/msm_memtypes.h>
 #include <mach/cpuidle.h>
-
+#include <linux/msm_ion.h>
 #include <mach/htc_pwrsink.h>
 #include <mach/debug_mm.h>
 
@@ -98,6 +98,8 @@
 	struct pm_qos_request pm_qos_req;
 
 	struct audpp_cmd_cfg_object_params_volume vol_pan;
+	struct ion_client *client;
+	struct ion_handle *buff_handle;
 };
 
 static void audio_out_listener(u32 evt_id, union auddev_evt_data *evt_payload,
@@ -702,19 +704,53 @@
 
 static int __init audio_init(void)
 {
-	the_audio.phys = allocate_contiguous_ebi_nomap(DMASZ, SZ_4K);
-	if (the_audio.phys) {
-		the_audio.map_v_write = ioremap(the_audio.phys, DMASZ);
-		if (IS_ERR(the_audio.map_v_write)) {
-			MM_ERR("could not map physical buffers\n");
-			free_contiguous_memory_by_paddr(the_audio.phys);
-			return -ENOMEM;
-		}
-		the_audio.data = the_audio.map_v_write;
-	} else {
-			MM_ERR("could not allocate physical buffers\n");
-			return -ENOMEM;
+	unsigned long ionflag = 0;
+	ion_phys_addr_t addr = 0;
+	int rc;
+	int len = 0;
+	struct ion_handle *handle = NULL;
+	struct ion_client *client = NULL;
+
+	client = msm_ion_client_create(UINT_MAX, "HostPCM");
+	if (IS_ERR_OR_NULL(client)) {
+		MM_ERR("Unable to create ION client\n");
+		rc = -ENOMEM;
+		goto client_create_error;
 	}
+	the_audio.client = client;
+
+	handle = ion_alloc(client, DMASZ, SZ_4K,
+		ION_HEAP(ION_AUDIO_HEAP_ID), 0);
+	if (IS_ERR_OR_NULL(handle)) {
+		MM_ERR("Unable to create allocate O/P buffers\n");
+		rc = -ENOMEM;
+		goto buff_alloc_error;
+		}
+	the_audio.buff_handle = handle;
+
+	rc = ion_phys(client, handle, &addr, &len);
+	if (rc) {
+		MM_ERR("O/P buffers:Invalid phy: %x sz: %x\n",
+			(unsigned int) addr, (unsigned int) len);
+		goto buff_get_phys_error;
+	} else
+		MM_INFO("O/P buffers:valid phy: %x sz: %x\n",
+			(unsigned int) addr, (unsigned int) len);
+	the_audio.phys = (int32_t)addr;
+
+	rc = ion_handle_get_flags(client, handle, &ionflag);
+	if (rc) {
+		MM_ERR("could not get flags for the handle\n");
+		goto buff_get_flags_error;
+	}
+
+	the_audio.map_v_write = ion_map_kernel(client, handle);
+	if (IS_ERR(the_audio.map_v_write)) {
+		MM_ERR("could not map write buffers\n");
+		rc = -ENOMEM;
+		goto buff_map_error;
+	}
+	the_audio.data = (char *)the_audio.map_v_write;
 	MM_DBG("Memory addr = 0x%8x  phy addr = 0x%8x\n",\
 		(int) the_audio.data, (int) the_audio.phys);
 	mutex_init(&the_audio.lock);
@@ -725,6 +761,15 @@
 	pm_qos_add_request(&the_audio.pm_qos_req, PM_QOS_CPU_DMA_LATENCY,
 				PM_QOS_DEFAULT_VALUE);
 	return misc_register(&audio_misc);
+buff_map_error:
+buff_get_phys_error:
+buff_get_flags_error:
+	ion_free(client, the_audio.buff_handle);
+buff_alloc_error:
+	ion_client_destroy(client);
+client_create_error:
+	return rc;
+
 }
 
 late_initcall(audio_init);
diff --git a/arch/arm/mach-msm/qdsp5v2/audio_pcm_in.c b/arch/arm/mach-msm/qdsp5v2/audio_pcm_in.c
index ce67ebb..ff3a696 100644
--- a/arch/arm/mach-msm/qdsp5v2/audio_pcm_in.c
+++ b/arch/arm/mach-msm/qdsp5v2/audio_pcm_in.c
@@ -3,7 +3,7 @@
  *
  * Copyright (C) 2008 Google, Inc.
  * Copyright (C) 2008 HTC Corporation
- * Copyright (c) 2009-2012, Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2009-2012, The Linux Foundation. All rights reserved.
  *
  * This software is licensed under the terms of the GNU General Public
  * License version 2, as published by the Free Software Foundation, and
@@ -27,7 +27,7 @@
 #include <linux/wait.h>
 #include <linux/dma-mapping.h>
 #include <linux/msm_audio.h>
-#include <linux/android_pmem.h>
+#include <linux/msm_ion.h>
 #include <linux/memory_alloc.h>
 #include <mach/msm_memtypes.h>
 
@@ -121,6 +121,8 @@
 	int abort; /* set when error, like sample rate mismatch */
 	int dual_mic_config;
 	char *build_id;
+	struct ion_client *client;
+	struct ion_handle *output_buff_handle;
 };
 
 static struct audio_in the_audio_in;
@@ -842,10 +844,11 @@
 	audio->audrec = NULL;
 	audio->opened = 0;
 	if (audio->data) {
-		iounmap(audio->map_v_read);
-		free_contiguous_memory_by_paddr(audio->phys);
+		ion_unmap_kernel(audio->client, audio->output_buff_handle);
+		ion_free(audio->client, audio->output_buff_handle);
 		audio->data = NULL;
 	}
+	ion_client_destroy(audio->client);
 	mutex_unlock(&audio->lock);
 	return 0;
 }
@@ -855,27 +858,68 @@
 	struct audio_in *audio = &the_audio_in;
 	int rc;
 	int encid;
+	int len = 0;
+	unsigned long ionflag = 0;
+	ion_phys_addr_t addr = 0;
+	struct ion_handle *handle = NULL;
+	struct ion_client *client = NULL;
 
 	mutex_lock(&audio->lock);
 	if (audio->opened) {
 		rc = -EBUSY;
 		goto done;
 	}
-	audio->phys = allocate_contiguous_ebi_nomap(DMASZ, SZ_4K);
-	if (audio->phys) {
-		audio->map_v_read = ioremap(audio->phys, DMASZ);
-		if (IS_ERR(audio->map_v_read)) {
-			MM_ERR("could not map read phys buffers\n");
+
+	client = msm_ion_client_create(UINT_MAX, "Audio_PCM_in_client");
+	if (IS_ERR_OR_NULL(client)) {
+		MM_ERR("Unable to create ION client\n");
 			rc = -ENOMEM;
-			free_contiguous_memory_by_paddr(audio->phys);
-			goto done;
-		}
-		audio->data = audio->map_v_read;
-	} else {
-		MM_ERR("could not allocate read buffers\n");
-		rc = -ENOMEM;
-		goto done;
+		goto client_create_error;
 	}
+	audio->client = client;
+
+	MM_DBG("allocating mem sz = %d\n", DMASZ);
+	handle = ion_alloc(client, DMASZ, SZ_4K,
+		ION_HEAP(ION_AUDIO_HEAP_ID), 0);
+	if (IS_ERR_OR_NULL(handle)) {
+		MM_ERR("Unable to create allocate O/P buffers\n");
+		rc = -ENOMEM;
+		goto output_buff_alloc_error;
+	}
+
+	audio->output_buff_handle = handle;
+
+	rc = ion_phys(client , handle, &addr, &len);
+	if (rc) {
+		MM_ERR("O/P buffers:Invalid phy: %x sz: %x\n",
+			(unsigned int) addr, (unsigned int) len);
+		rc = -ENOMEM;
+		goto output_buff_get_phys_error;
+	} else {
+		MM_INFO("O/P buffers:valid phy: %x sz: %x\n",
+			(unsigned int) addr, (unsigned int) len);
+	}
+	audio->phys = (int32_t)addr;
+
+	rc = ion_handle_get_flags(client, handle, &ionflag);
+	if (rc) {
+		MM_ERR("could not get flags for the handle\n");
+		rc = -ENOMEM;
+		goto output_buff_get_flags_error;
+	}
+
+	audio->map_v_read = ion_map_kernel(client, handle);
+	if (IS_ERR(audio->data)) {
+		MM_ERR("could not map read buffers,freeing instance 0x%08x\n",
+				(int)audio);
+		rc = -ENOMEM;
+		goto output_buff_map_error;
+	}
+	MM_DBG("read buf: phy addr 0x%08x kernel addr 0x%08x\n",
+		audio->phys, (int)audio->data);
+
+	audio->data = (char *)audio->map_v_read;
+
 	MM_DBG("Memory addr = 0x%8x  phy addr = 0x%8x\n",\
 		(int) audio->data, (int) audio->phys);
 	if ((file->f_mode & FMODE_WRITE) &&
@@ -941,6 +985,13 @@
 	mutex_unlock(&audio->lock);
 	return rc;
 evt_error:
+output_buff_map_error:
+output_buff_get_phys_error:
+output_buff_get_flags_error:
+	ion_free(client, audio->output_buff_handle);
+output_buff_alloc_error:
+	ion_client_destroy(client);
+client_create_error:
 	msm_adsp_put(audio->audrec);
 	audpreproc_aenc_free(audio->enc_id);
 	mutex_unlock(&audio->lock);
diff --git a/arch/arm/mach-msm/qdsp6v2/Makefile b/arch/arm/mach-msm/qdsp6v2/Makefile
index ed8cb345..66d6bda 100644
--- a/arch/arm/mach-msm/qdsp6v2/Makefile
+++ b/arch/arm/mach-msm/qdsp6v2/Makefile
@@ -8,6 +8,7 @@
 obj-y += q6voice.o
 obj-y += snddev_hdmi.o
 obj-y += audio_mvs.o
+obj-$(CONFIG_ARCH_MSM8X60) += pcm_in_proxy.o
 obj-$(CONFIG_FB_MSM_HDMI_MSM_PANEL) += lpa_if_hdmi.o
 endif
 obj-$(CONFIG_MSM_QDSP6_APR) += apr.o apr_v1.o apr_tal.o q6core.o dsp_debug.o
@@ -25,5 +26,4 @@
 obj-$(CONFIG_MSM_QDSP6V2_CODECS) += rtac_v2.o q6audio_v2.o q6audio_v2_aio.o
 obj-$(CONFIG_MSM_QDSP6V2_CODECS) += audio_mp3.o audio_amrnb.o audio_amrwb.o audio_evrc.o audio_qcelp.o amrwb_in.o
 obj-$(CONFIG_MSM_ADSP_LOADER) += adsp-loader.o
-obj-$(CONFIG_MSM_ULTRASOUND) += ultrasound/
-obj-m += adsprpc.o
+obj-$(CONFIG_MSM_ULTRASOUND_A) += ultrasound/version_a/
diff --git a/arch/arm/mach-msm/qdsp6v2/adsp-loader.c b/arch/arm/mach-msm/qdsp6v2/adsp-loader.c
index 9924b52..c28e403 100644
--- a/arch/arm/mach-msm/qdsp6v2/adsp-loader.c
+++ b/arch/arm/mach-msm/qdsp6v2/adsp-loader.c
@@ -17,7 +17,7 @@
 #include <linux/err.h>
 #include <linux/delay.h>
 #include <linux/platform_device.h>
-#include <mach/peripheral-loader.h>
+#include <mach/subsystem_restart.h>
 #include <mach/qdsp6v2/apr.h>
 
 #define Q6_PIL_GET_DELAY_MS 100
@@ -37,7 +37,7 @@
 
 	platform_set_drvdata(pdev, priv);
 
-	priv->pil_h = pil_get("adsp");
+	priv->pil_h = subsystem_get("adsp");
 	if (IS_ERR(priv->pil_h)) {
 		pr_err("%s: pil get adsp failed, error:%d\n", __func__, rc);
 		devm_kfree(&pdev->dev, priv);
@@ -62,7 +62,7 @@
 	struct adsp_loader_private *priv;
 
 	priv = platform_get_drvdata(pdev);
-	pil_put(priv->pil_h);
+	subsystem_put(priv->pil_h);
 	pr_info("%s: Q6/ADSP image is unloaded\n", __func__);
 
 	return 0;
diff --git a/arch/arm/mach-msm/qdsp6v2/apr.c b/arch/arm/mach-msm/qdsp6v2/apr.c
index 8ac1fea..39bec8e 100644
--- a/arch/arm/mach-msm/qdsp6v2/apr.c
+++ b/arch/arm/mach-msm/qdsp6v2/apr.c
@@ -27,7 +27,7 @@
 #include <linux/device.h>
 #include <linux/slab.h>
 #include <asm/mach-types.h>
-#include <mach/peripheral-loader.h>
+#include <mach/subsystem_restart.h>
 #include <mach/msm_smd.h>
 #include <mach/qdsp6v2/apr.h>
 #include <mach/qdsp6v2/apr_tal.h>
@@ -223,7 +223,7 @@
 	int rc = 0;
 	mutex_lock(&q6.lock);
 	if (apr_get_q6_state() == APR_SUBSYS_UP) {
-		q6.pil = pil_get("q6");
+		q6.pil = subsystem_get("adsp");
 		if (IS_ERR(q6.pil)) {
 			rc = PTR_ERR(q6.pil);
 			pr_err("APR: Unable to load q6 image, error:%d\n", rc);
@@ -231,8 +231,11 @@
 			apr_set_q6_state(APR_SUBSYS_LOADED);
 			pr_debug("APR: Image is loaded, stated\n");
 		}
-	} else
+	} else if (apr_get_q6_state() == APR_SUBSYS_LOADED) {
+		pr_debug("APR: q6 image already loaded\n");
+	} else {
 		pr_debug("APR: cannot load state %d\n", apr_get_q6_state());
+	}
 	mutex_unlock(&q6.lock);
 	return rc;
 }
@@ -658,8 +661,8 @@
 		pr_debug("L-notify: Bootup started\n");
 		break;
 	case SUBSYS_AFTER_POWERUP:
-		if (apr_cmpxchg_q6_state(APR_SUBSYS_DOWN, APR_SUBSYS_UP) ==
-					     APR_SUBSYS_DOWN)
+		if (apr_cmpxchg_q6_state(APR_SUBSYS_DOWN,
+				APR_SUBSYS_LOADED) == APR_SUBSYS_DOWN)
 			wake_up(&dsp_wait);
 		pr_debug("L-Notify: Bootup Completed\n");
 		break;
@@ -703,7 +706,7 @@
 	init_waitqueue_head(&dsp_wait);
 	init_waitqueue_head(&modem_wait);
 	subsys_notif_register_notifier("modem", &mnb);
-	subsys_notif_register_notifier("lpass", &lnb);
+	subsys_notif_register_notifier(apr_get_lpass_subsys_name(), &lnb);
 	return ret;
 }
 late_initcall(apr_late_init);
diff --git a/arch/arm/mach-msm/qdsp6v2/apr_v1.c b/arch/arm/mach-msm/qdsp6v2/apr_v1.c
index 9535968..870bbb4 100644
--- a/arch/arm/mach-msm/qdsp6v2/apr_v1.c
+++ b/arch/arm/mach-msm/qdsp6v2/apr_v1.c
@@ -19,7 +19,8 @@
 #include <mach/qdsp6v2/apr.h>
 #include <mach/qdsp6v2/apr_tal.h>
 #include <mach/qdsp6v2/dsp_debug.h>
-#include <mach/peripheral-loader.h>
+
+static const char *lpass_subsys_name = "lpass";
 
 struct apr_svc *apr_register(char *dest, char *svc_name, apr_fn svc_fn,
 			     uint32_t src_port, void *priv)
@@ -131,3 +132,8 @@
 	apr_set_q6_state(APR_SUBSYS_UP);
 	apr_set_modem_state(APR_SUBSYS_UP);
 }
+
+const char *apr_get_lpass_subsys_name(void)
+{
+	return lpass_subsys_name;
+}
diff --git a/arch/arm/mach-msm/qdsp6v2/apr_v2.c b/arch/arm/mach-msm/qdsp6v2/apr_v2.c
index 1ef189f..ed494e4 100644
--- a/arch/arm/mach-msm/qdsp6v2/apr_v2.c
+++ b/arch/arm/mach-msm/qdsp6v2/apr_v2.c
@@ -19,6 +19,8 @@
 #include <mach/qdsp6v2/apr_tal.h>
 #include <mach/qdsp6v2/dsp_debug.h>
 
+static const char *lpass_subsys_name = "adsp";
+
 struct apr_svc *apr_register(char *dest, char *svc_name, apr_fn svc_fn,
 			     uint32_t src_port, void *priv)
 {
@@ -48,7 +50,7 @@
 			pr_err("%s: adsp not up\n", __func__);
 			return NULL;
 		}
-		pr_info("%s: Lpass Up\n", __func__);
+		pr_info("%s: adsp Up\n", __func__);
 	} else if ((dest_id == APR_DEST_MODEM) &&
 		   (apr_get_modem_state() == APR_SUBSYS_DOWN)) {
 		pr_info("%s: Wait for modem to bootup\n", __func__);
@@ -125,3 +127,8 @@
 	apr_set_q6_state(APR_SUBSYS_DOWN);
 	apr_set_modem_state(APR_SUBSYS_UP);
 }
+
+const char *apr_get_lpass_subsys_name(void)
+{
+	return lpass_subsys_name;
+}
diff --git a/arch/arm/mach-msm/qdsp6v2/audio_acdb.c b/arch/arm/mach-msm/qdsp6v2/audio_acdb.c
index 7272f97..a24b9ec 100644
--- a/arch/arm/mach-msm/qdsp6v2/audio_acdb.c
+++ b/arch/arm/mach-msm/qdsp6v2/audio_acdb.c
@@ -10,6 +10,7 @@
  * GNU General Public License for more details.
  *
  */
+#include <linux/slab.h>
 #include <linux/fs.h>
 #include <linux/module.h>
 #include <linux/miscdevice.h>
@@ -20,7 +21,14 @@
 #include <mach/qdsp6v2/audio_acdb.h>
 
 
-#define MAX_NETWORKS		15
+#define MAX_NETWORKS			15
+#define MAX_IOCTL_DATA			(MAX_NETWORKS * 2)
+#define MAX_COL_SIZE			324
+
+#define ACDB_BLOCK_SIZE			4096
+#define NUM_VOCPROC_BLOCKS		(6 * MAX_NETWORKS)
+#define ACDB_TOTAL_VOICE_ALLOCATION	(ACDB_BLOCK_SIZE * NUM_VOCPROC_BLOCKS)
+
 
 struct sidetone_atomic_cal {
 	atomic_t	enable;
@@ -56,6 +64,15 @@
 	atomic_t			vocstrm_total_cal_size;
 	atomic_t			vocvol_total_cal_size;
 
+	/* Voice Column data */
+	struct acdb_atomic_cal_block	vocproc_col_cal[MAX_VOCPROC_TYPES];
+	uint32_t			*col_data[MAX_VOCPROC_TYPES];
+
+	/* VocProc dev cfg cal*/
+	struct acdb_atomic_cal_block	vocproc_dev_cal[MAX_NETWORKS];
+	atomic_t			vocproc_dev_cal_size;
+	atomic_t			vocproc_dev_total_cal_size;
+
 	/* AFE cal */
 	struct acdb_atomic_cal_block	afe_cal[MAX_AUDPROC_TYPES];
 
@@ -124,6 +141,15 @@
 	atomic_set(&acdb_data.asm_topology, topology);
 }
 
+void get_voice_cal_allocation(struct acdb_cal_block *cal_block)
+{
+	cal_block->cal_kvaddr =
+		atomic_read(&acdb_data.vocproc_cal[0].cal_kvaddr);
+	cal_block->cal_paddr =
+		atomic_read(&acdb_data.vocproc_cal[0].cal_paddr);
+	cal_block->cal_size = ACDB_TOTAL_VOICE_ALLOCATION;
+}
+
 void get_all_voice_cal(struct acdb_cal_block *cal_block)
 {
 	cal_block->cal_kvaddr =
@@ -177,6 +203,45 @@
 		atomic_read(&acdb_data.vocvol_total_cal_size);
 }
 
+void get_voice_col_data(uint32_t vocproc_type,
+			struct acdb_cal_block *cal_block)
+{
+	if (cal_block == NULL) {
+		pr_err("ACDB=> NULL pointer sent to %s\n", __func__);
+		goto done;
+	}
+
+	cal_block->cal_kvaddr = atomic_read(&acdb_data.
+				vocproc_col_cal[vocproc_type].cal_kvaddr);
+	cal_block->cal_paddr = atomic_read(&acdb_data.
+				vocproc_col_cal[vocproc_type].cal_paddr);
+	cal_block->cal_size = atomic_read(&acdb_data.
+				vocproc_col_cal[vocproc_type].cal_size);
+done:
+	return;
+}
+
+void store_voice_col_data(uint32_t vocproc_type, uint32_t cal_size,
+			  uint32_t *cal_data)
+{
+	if (cal_size > MAX_COL_SIZE) {
+		pr_err("%s: col size is to big %d\n", __func__,
+				cal_size);
+		goto done;
+	}
+	if (copy_from_user(acdb_data.col_data[vocproc_type],
+			(void *)((uint8_t *)cal_data + sizeof(cal_size)),
+			cal_size)) {
+		pr_err("%s: fail to copy col size %d\n",
+			__func__, cal_size);
+		goto done;
+	}
+	atomic_set(&acdb_data.vocproc_col_cal[vocproc_type].cal_size,
+		cal_size);
+done:
+	return;
+}
+
 void get_anc_cal(struct acdb_cal_block *cal_block)
 {
 	pr_debug("%s\n", __func__);
@@ -417,6 +482,56 @@
 	return;
 }
 
+void store_vocproc_dev_cfg_cal(int32_t len, struct cal_block *cal_blocks)
+{
+	int i;
+	pr_debug("%s\n", __func__);
+
+	if (len > MAX_NETWORKS) {
+		pr_err("%s: Calibration sent for %d networks, only %d are supported!\n",
+			__func__, len, MAX_NETWORKS);
+		goto done;
+	}
+
+	atomic_set(&acdb_data.vocproc_dev_total_cal_size, 0);
+	for (i = 0; i < len; i++) {
+		if (cal_blocks[i].cal_offset >
+					atomic64_read(&acdb_data.mem_len)) {
+			pr_err("%s: offset %d is > mem_len %ld\n",
+				__func__, cal_blocks[i].cal_offset,
+				(long)atomic64_read(&acdb_data.mem_len));
+			atomic_set(&acdb_data.vocproc_dev_cal[i].cal_size, 0);
+		} else {
+			atomic_add(cal_blocks[i].cal_size,
+				&acdb_data.vocproc_dev_total_cal_size);
+			atomic_set(&acdb_data.vocproc_dev_cal[i].cal_size,
+				cal_blocks[i].cal_size);
+			atomic_set(&acdb_data.vocproc_dev_cal[i].cal_paddr,
+				cal_blocks[i].cal_offset +
+				atomic64_read(&acdb_data.paddr));
+			atomic_set(&acdb_data.vocproc_dev_cal[i].cal_kvaddr,
+				cal_blocks[i].cal_offset +
+				atomic64_read(&acdb_data.kvaddr));
+		}
+	}
+	atomic_set(&acdb_data.vocproc_dev_cal_size, len);
+done:
+	return;
+}
+
+void get_vocproc_dev_cfg_cal(struct acdb_cal_block *cal_block)
+{
+	pr_debug("%s\n", __func__);
+
+	cal_block->cal_kvaddr =
+		atomic_read(&acdb_data.vocproc_dev_cal[0].cal_kvaddr);
+	cal_block->cal_paddr =
+		atomic_read(&acdb_data.vocproc_dev_cal[0].cal_paddr);
+	cal_block->cal_size =
+		atomic_read(&acdb_data.vocproc_dev_total_cal_size);
+}
+
+
 
 void store_vocproc_cal(int32_t len, struct cal_block *cal_blocks)
 {
@@ -698,7 +813,7 @@
 	int32_t			size;
 	int32_t			map_fd;
 	uint32_t		topology;
-	struct cal_block	data[MAX_NETWORKS];
+	uint32_t		data[MAX_IOCTL_DATA];
 	pr_debug("%s\n", __func__);
 
 	switch (cmd) {
@@ -777,6 +892,18 @@
 		goto done;
 	}
 
+	switch (cmd) {
+	case AUDIO_SET_VOCPROC_COL_CAL:
+		store_voice_col_data(VOCPROC_CAL, size, (uint32_t *)arg);
+		goto done;
+	case AUDIO_SET_VOCSTRM_COL_CAL:
+		store_voice_col_data(VOCSTRM_CAL, size, (uint32_t *)arg);
+		goto done;
+	case AUDIO_SET_VOCVOL_COL_CAL:
+		store_voice_col_data(VOCVOL_CAL, size, (uint32_t *)arg);
+		goto done;
+	}
+
 	if (copy_from_user(data, (void *)(arg + sizeof(size)), size)) {
 
 		pr_err("%s: fail to copy table size %d\n", __func__, size);
@@ -795,58 +922,65 @@
 		if (size > sizeof(struct cal_block))
 			pr_err("%s: More Audproc Cal then expected, "
 				"size received: %d\n", __func__, size);
-		store_audproc_cal(TX_CAL, data);
+		store_audproc_cal(TX_CAL, (struct cal_block *)data);
 		break;
 	case AUDIO_SET_AUDPROC_RX_CAL:
 		if (size > sizeof(struct cal_block))
 			pr_err("%s: More Audproc Cal then expected, "
 				"size received: %d\n", __func__, size);
-		store_audproc_cal(RX_CAL, data);
+		store_audproc_cal(RX_CAL, (struct cal_block *)data);
 		break;
 	case AUDIO_SET_AUDPROC_TX_STREAM_CAL:
 		if (size > sizeof(struct cal_block))
 			pr_err("%s: More Audproc Cal then expected, "
 				"size received: %d\n", __func__, size);
-		store_audstrm_cal(TX_CAL, data);
+		store_audstrm_cal(TX_CAL, (struct cal_block *)data);
 		break;
 	case AUDIO_SET_AUDPROC_RX_STREAM_CAL:
 		if (size > sizeof(struct cal_block))
 			pr_err("%s: More Audproc Cal then expected, "
 				"size received: %d\n", __func__, size);
-		store_audstrm_cal(RX_CAL, data);
+		store_audstrm_cal(RX_CAL, (struct cal_block *)data);
 		break;
 	case AUDIO_SET_AUDPROC_TX_VOL_CAL:
 		if (size > sizeof(struct cal_block))
 			pr_err("%s: More Audproc Cal then expected, "
 				"size received: %d\n", __func__, size);
-		store_audvol_cal(TX_CAL, data);
+		store_audvol_cal(TX_CAL, (struct cal_block *)data);
 		break;
 	case AUDIO_SET_AUDPROC_RX_VOL_CAL:
 		if (size > sizeof(struct cal_block))
 			pr_err("%s: More Audproc Cal then expected, "
 				"size received: %d\n", __func__, size);
-		store_audvol_cal(RX_CAL, data);
+		store_audvol_cal(RX_CAL, (struct cal_block *)data);
 		break;
 	case AUDIO_SET_AFE_TX_CAL:
 		if (size > sizeof(struct cal_block))
 			pr_err("%s: More AFE Cal then expected, "
 				"size received: %d\n", __func__, size);
-		store_afe_cal(TX_CAL, data);
+		store_afe_cal(TX_CAL, (struct cal_block *)data);
 		break;
 	case AUDIO_SET_AFE_RX_CAL:
 		if (size > sizeof(struct cal_block))
 			pr_err("%s: More AFE Cal then expected, "
 				"size received: %d\n", __func__, size);
-		store_afe_cal(RX_CAL, data);
+		store_afe_cal(RX_CAL, (struct cal_block *)data);
 		break;
 	case AUDIO_SET_VOCPROC_CAL:
-		store_vocproc_cal(size / sizeof(struct cal_block), data);
+		store_vocproc_cal(size / sizeof(struct cal_block),
+						(struct cal_block *)data);
 		break;
 	case AUDIO_SET_VOCPROC_STREAM_CAL:
-		store_vocstrm_cal(size / sizeof(struct cal_block), data);
+		store_vocstrm_cal(size / sizeof(struct cal_block),
+						(struct cal_block *)data);
 		break;
 	case AUDIO_SET_VOCPROC_VOL_CAL:
-		store_vocvol_cal(size / sizeof(struct cal_block), data);
+		store_vocvol_cal(size / sizeof(struct cal_block),
+						(struct cal_block *)data);
+		break;
+	case AUDIO_SET_VOCPROC_DEV_CFG_CAL:
+		store_vocproc_dev_cfg_cal(size / sizeof(struct cal_block),
+						(struct cal_block *)data);
 		break;
 	case AUDIO_SET_SIDETONE_CAL:
 		if (size > sizeof(struct sidetone_cal))
@@ -855,7 +989,7 @@
 		store_sidetone_cal((struct sidetone_cal *)data);
 		break;
 	case AUDIO_SET_ANC_CAL:
-		store_anc_cal(data);
+		store_anc_cal((struct cal_block *)data);
 		break;
 	default:
 		pr_err("ACDB=> ACDB ioctl not found!\n");
@@ -895,6 +1029,7 @@
 
 static int acdb_release(struct inode *inode, struct file *f)
 {
+	int i;
 	s32 result = 0;
 
 	atomic_dec(&usage_count);
@@ -903,6 +1038,11 @@
 	pr_debug("%s: ref count %d!\n", __func__,
 		atomic_read(&usage_count));
 
+	for (i = 0; i < MAX_VOCPROC_TYPES; i++) {
+		kfree(acdb_data.col_data[i]);
+		acdb_data.col_data[i] = NULL;
+	}
+
 	if (atomic_read(&usage_count) >= 1)
 		result = -EBUSY;
 	else
@@ -927,9 +1067,16 @@
 
 static int __init acdb_init(void)
 {
+	int i;
 	memset(&acdb_data, 0, sizeof(acdb_data));
 	mutex_init(&acdb_data.acdb_mutex);
 	atomic_set(&usage_count, 0);
+
+	for (i = 0; i < MAX_VOCPROC_TYPES; i++) {
+		acdb_data.col_data[i] = kmalloc(MAX_COL_SIZE, GFP_KERNEL);
+		atomic_set(&acdb_data.vocproc_col_cal[i].cal_kvaddr,
+			(uint32_t)acdb_data.col_data[i]);
+	}
 	return misc_register(&acdb_misc);
 }
 
diff --git a/arch/arm/mach-msm/qdsp6v2/audio_lpa.c b/arch/arm/mach-msm/qdsp6v2/audio_lpa.c
index 176f364..edf8f77 100644
--- a/arch/arm/mach-msm/qdsp6v2/audio_lpa.c
+++ b/arch/arm/mach-msm/qdsp6v2/audio_lpa.c
@@ -707,15 +707,6 @@
 	case RESET_EVENTS:
 		reset_device();
 		break;
-	case APR_BASIC_RSP_RESULT:
-		switch (payload[0]) {
-		case ASM_STREAM_CMD_CLOSE:
-			audlpa_unmap_ion_region(audio);
-			break;
-		default:
-			break;
-		}
-		break;
 	default:
 		break;
 	}
@@ -1136,6 +1127,7 @@
 		audlpa_async_flush(audio);
 	audio->wflush = 0;
 	audio_disable(audio);
+	audlpa_unmap_ion_region(audio);
 	msm_clear_session_id(audio->ac->session);
 	auddev_unregister_evt_listner(AUDDEV_CLNT_DEC, audio->ac->session);
 	q6asm_audio_client_free(audio->ac);
diff --git a/arch/arm/mach-msm/qdsp6v2/audio_utils_aio.c b/arch/arm/mach-msm/qdsp6v2/audio_utils_aio.c
index 28bf5c6..fb0ace7 100644
--- a/arch/arm/mach-msm/qdsp6v2/audio_utils_aio.c
+++ b/arch/arm/mach-msm/qdsp6v2/audio_utils_aio.c
@@ -403,7 +403,7 @@
 	return;
 }
 
-void audio_aio_unmap_ion_region(struct q6audio_aio *audio)
+static void audio_aio_unmap_ion_region(struct q6audio_aio *audio)
 {
 	struct audio_aio_ion_region *region;
 	struct list_head *ptr, *next;
@@ -436,6 +436,7 @@
 	audio->drv_ops.out_flush(audio);
 	audio->drv_ops.in_flush(audio);
 	audio_aio_disable(audio);
+	audio_aio_unmap_ion_region(audio);
 	audio_aio_reset_ion_region(audio);
 	ion_client_destroy(audio->client);
 	audio->event_abort = 1;
@@ -778,6 +779,8 @@
 		__func__, audio, buf_node, buf_node->paddr,
 		buf_node->buf.data_len,
 		audio->buf_cfg.meta_info_enable);
+	pr_debug("%s[%p]: flags = 0x%x\n", __func__, audio,
+		buf_node->meta_info.meta_in.nflags);
 
 	ac = audio->ac;
 	/* Offset with  appropriate meta */
@@ -797,6 +800,11 @@
 		param.flags = 0;
 	else
 		param.flags = 0xFF00;
+
+	if ((buf_node != NULL) &&
+		(buf_node->meta_info.meta_in.nflags & AUDIO_DEC_EOF_SET))
+		param.flags |= AUDIO_DEC_EOF_SET;
+
 	param.uid = param.paddr;
 	/* Read command will populate paddr as token */
 	buf_node->token = param.paddr;
diff --git a/arch/arm/mach-msm/qdsp6v2/audio_utils_aio.h b/arch/arm/mach-msm/qdsp6v2/audio_utils_aio.h
index 2b936c5..dedf991 100644
--- a/arch/arm/mach-msm/qdsp6v2/audio_utils_aio.h
+++ b/arch/arm/mach-msm/qdsp6v2/audio_utils_aio.h
@@ -36,6 +36,7 @@
 #define ADRV_STATUS_FSYNC 0x00000008
 #define ADRV_STATUS_PAUSE 0x00000010
 #define AUDIO_DEC_EOS_SET  0x00000001
+#define AUDIO_DEC_EOF_SET  0x00000010
 #define AUDIO_EVENT_NUM		10
 
 #define __CONTAINS(r, v, l) ({                                  \
@@ -210,7 +211,6 @@
 int audio_aio_fsync(struct file *file, loff_t start, loff_t end, int datasync);
 void audio_aio_async_out_flush(struct q6audio_aio *audio);
 void audio_aio_async_in_flush(struct q6audio_aio *audio);
-void audio_aio_unmap_ion_region(struct q6audio_aio *audio);
 #ifdef CONFIG_DEBUG_FS
 ssize_t audio_aio_debug_open(struct inode *inode, struct file *file);
 ssize_t audio_aio_debug_read(struct file *file, char __user *buf,
diff --git a/arch/arm/mach-msm/qdsp6v2/pcm_in_proxy.c b/arch/arm/mach-msm/qdsp6v2/pcm_in_proxy.c
new file mode 100644
index 0000000..84f136a
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp6v2/pcm_in_proxy.c
@@ -0,0 +1,596 @@
+
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License version 2 and
+* only version 2 as published by the Free Software Foundation.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+* GNU General Public License for more details.
+*/
+
+#include <linux/fs.h>
+#include <linux/module.h>
+#include <linux/miscdevice.h>
+#include <linux/mutex.h>
+#include <linux/sched.h>
+#include <linux/uaccess.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/time.h>
+#include <linux/wait.h>
+#include <linux/msm_audio.h>
+
+#include <asm/atomic.h>
+#include <mach/debug_mm.h>
+#include <mach/qdsp6v2/audio_dev_ctl.h>
+#include <sound/q6asm.h>
+#include <sound/apr_audio.h>
+#include <linux/wakelock.h>
+#include <mach/cpuidle.h>
+
+#define MAX_BUF 4
+
+struct dma_buf {
+	uint32_t addr;
+	uint32_t v_addr;
+	uint32_t used;
+};
+struct pcm {
+	struct mutex lock;
+	struct mutex read_lock;
+	wait_queue_head_t wait;
+	spinlock_t dsp_lock;
+	struct audio_client *ac;
+	uint32_t sample_rate;
+	uint32_t channel_count;
+	uint32_t buffer_size;
+	uint32_t buffer_count;
+	uint32_t cpu_idx;
+	uint32_t dsp_idx;
+	uint32_t start;
+	uint32_t dma_addr;
+	uint32_t dma_virt;
+	struct dma_buf dma_buf[MAX_BUF];
+	atomic_t in_count;
+	atomic_t in_enabled;
+	atomic_t in_opened;
+	atomic_t in_stopped;
+	int poll_time;
+	struct hrtimer hrt;
+};
+
+static enum hrtimer_restart afe_hrtimer_callback(struct hrtimer *hrt);
+
+static enum hrtimer_restart afe_hrtimer_callback(struct hrtimer *hrt)
+{
+	struct pcm *pcm =
+		container_of(hrt, struct pcm, hrt);
+	int rc = 0;
+	if (pcm->start) {
+		if (pcm->dsp_idx == pcm->buffer_count)
+			pcm->dsp_idx = 0;
+		rc = wait_event_timeout(pcm->wait,
+				(pcm->dma_buf[pcm->dsp_idx].used == 0) ||
+				atomic_read(&pcm->in_stopped), 1 * HZ);
+		if (!rc) {
+			pr_err("%s: wait_event_timeout failed\n", __func__);
+			goto fail;
+		}
+		if (atomic_read(&pcm->in_stopped)) {
+			pr_err("%s: Driver closed - return\n", __func__);
+			return HRTIMER_NORESTART;
+		}
+		rc = afe_rt_proxy_port_read(
+			pcm->dma_buf[pcm->dsp_idx].addr,
+			pcm->buffer_size);
+		if (rc < 0) {
+			pr_err("%s afe_rt_proxy_port_read fail\n", __func__);
+			goto fail;
+		}
+		pcm->dma_buf[pcm->dsp_idx].used = 1;
+		pcm->dsp_idx++;
+		pr_debug("%s: sending frame rec to DSP: poll_time: %d\n",
+				__func__, pcm->poll_time);
+fail:
+		hrtimer_forward_now(hrt, ns_to_ktime(pcm->poll_time
+				* 1000));
+
+		return HRTIMER_RESTART;
+	} else {
+		return HRTIMER_NORESTART;
+	}
+}
+
+static void pcm_afe_callback(uint32_t opcode,
+		uint32_t token, uint32_t *payload,
+		 void *priv)
+{
+	struct pcm *pcm = (struct pcm *)priv;
+	unsigned long dsp_flags;
+	uint16_t event;
+
+	if (pcm == NULL)
+		return;
+	pr_debug("%s\n", __func__);
+	spin_lock_irqsave(&pcm->dsp_lock, dsp_flags);
+	switch (opcode) {
+	case AFE_EVENT_RT_PROXY_PORT_STATUS: {
+		event = (uint16_t)((0xFFFF0000 & payload[0]) >> 0x10);
+		switch (event) {
+		case AFE_EVENT_RTPORT_START: {
+			pcm->dsp_idx = 0;
+			pcm->cpu_idx = 0;
+			pcm->poll_time = (unsigned long)
+						(((pcm->buffer_size*1000)/
+						(pcm->channel_count *
+						pcm->sample_rate * 2))*1000);
+			pr_debug("%s: poll_time:%d\n", __func__,
+						pcm->poll_time);
+			pcm->start = 1;
+			wake_up(&pcm->wait);
+			break;
+		}
+		case AFE_EVENT_RTPORT_STOP:
+			pr_debug("%s: event!=0\n", __func__);
+			pcm->start = 0;
+			atomic_set(&pcm->in_stopped, 1);
+			break;
+		case AFE_EVENT_RTPORT_LOW_WM:
+			pr_debug("%s: Underrun\n", __func__);
+			break;
+		case AFE_EVENT_RTPORT_HI_WM:
+			pr_debug("%s: Overrun\n", __func__);
+			break;
+		default:
+			break;
+		}
+		break;
+	}
+	case APR_BASIC_RSP_RESULT: {
+		switch (payload[0]) {
+		case AFE_SERVICE_CMD_RTPORT_RD:
+			pr_debug("%s: Read done\n", __func__);
+			atomic_inc(&pcm->in_count);
+			wake_up(&pcm->wait);
+			break;
+		default:
+			break;
+		}
+		break;
+	}
+	default:
+		break;
+	}
+	spin_unlock_irqrestore(&pcm->dsp_lock, dsp_flags);
+}
+
+static uint32_t getbuffersize(uint32_t samplerate)
+{
+	if (samplerate == 8000)
+		return 480*8;
+	else if (samplerate == 16000)
+		return 480*16;
+	else if (samplerate == 48000)
+		return 480*48;
+	return 0;
+}
+
+static int pcm_in_open(struct inode *inode, struct file *file)
+{
+	struct pcm *pcm;
+	int rc = 0;
+
+	pr_debug("%s: pcm proxy in open session\n", __func__);
+	pcm = kzalloc(sizeof(struct pcm), GFP_KERNEL);
+	if (!pcm)
+		return -ENOMEM;
+
+	pcm->channel_count = 1;
+	pcm->sample_rate = 8000;
+	pcm->buffer_size = getbuffersize(pcm->sample_rate);
+	pcm->buffer_count = MAX_BUF;
+
+	pcm->ac = q6asm_audio_client_alloc(NULL, (void *)pcm);
+	if (!pcm->ac) {
+		pr_err("%s: Could not allocate memory\n", __func__);
+		rc = -ENOMEM;
+		goto fail;
+	}
+
+	mutex_init(&pcm->lock);
+	mutex_init(&pcm->read_lock);
+	spin_lock_init(&pcm->dsp_lock);
+	init_waitqueue_head(&pcm->wait);
+
+	hrtimer_init(&pcm->hrt, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+	pcm->hrt.function = afe_hrtimer_callback;
+
+	atomic_set(&pcm->in_stopped, 0);
+	atomic_set(&pcm->in_enabled, 0);
+	atomic_set(&pcm->in_count, 0);
+	atomic_set(&pcm->in_opened, 1);
+
+	file->private_data = pcm;
+	pr_debug("%s: pcm proxy open success session id:%d\n",
+				__func__, pcm->ac->session);
+	return 0;
+fail:
+	if (pcm->ac)
+		q6asm_audio_client_free(pcm->ac);
+	kfree(pcm);
+	return rc;
+}
+
+static int pcm_in_disable(struct pcm *pcm)
+{
+	int rc = 0;
+
+	if (atomic_read(&pcm->in_opened)) {
+		atomic_set(&pcm->in_enabled, 0);
+		atomic_set(&pcm->in_opened, 0);
+		atomic_set(&pcm->in_stopped, 1);
+		wake_up(&pcm->wait);
+	}
+	return rc;
+}
+
+static int config(struct pcm *pcm)
+{
+
+	int ret = 0, i;
+	struct audio_buffer *buf;
+
+	pr_debug("%s\n", __func__);
+
+	ret = q6asm_audio_client_buf_alloc_contiguous(OUT,
+			pcm->ac,
+			pcm->buffer_size,
+			pcm->buffer_count);
+	if (ret < 0) {
+		pr_err("%s: Audio Start: Buffer Allocation failed rc = %d\n",
+								__func__, ret);
+		return -ENOMEM;
+	}
+	buf = pcm->ac->port[OUT].buf;
+
+	if (buf == NULL || buf[0].data == NULL)
+		return -ENOMEM;
+
+	memset(buf[0].data, 0, pcm->buffer_size * pcm->buffer_count);
+	pcm->dma_addr = (u32) buf[0].phys;
+	pcm->dma_virt = (u32) buf[0].data;
+
+	for (i = 0; i < pcm->buffer_count; i++) {
+		pcm->dma_buf[i].addr = (u32) (buf[i].phys);
+		pcm->dma_buf[i].v_addr = (u32) (buf[i].data);
+		pcm->dma_buf[i].used = 0;
+	}
+
+	ret = afe_register_get_events(RT_PROXY_DAI_001_TX,
+			pcm_afe_callback, pcm);
+	if (ret < 0) {
+		pr_err("%s: afe-pcm:register for events failed\n", __func__);
+		return ret;
+	}
+	ret = afe_cmd_memory_map(pcm->dma_addr,
+			pcm->buffer_size * pcm->buffer_count);
+	if (ret < 0) {
+		pr_err("%s: fail to map memory to DSP\n", __func__);
+		return ret;
+	}
+
+	pr_debug("%s:success\n", __func__);
+	return ret;
+}
+static bool is_dma_buf_avail(struct pcm *pcm)
+{
+	return (pcm->dma_buf[pcm->cpu_idx].used == 1);
+}
+static ssize_t pcm_in_read(struct file *file, char __user *buf,
+			  size_t count, loff_t *pos)
+{
+	struct pcm *pcm = file->private_data;
+	const char __user *start = buf;
+	int rc = 0;
+	bool rc1 = false;
+	int len = 0;
+
+	if (!atomic_read(&pcm->in_enabled))
+		return -EFAULT;
+	mutex_lock(&pcm->read_lock);
+	while (count > 0) {
+		rc = wait_event_timeout(pcm->wait,
+				(atomic_read(&pcm->in_count) ||
+				atomic_read(&pcm->in_stopped)), 2 * HZ);
+		if (!rc) {
+			pr_err("%s: wait_event_timeout failed\n", __func__);
+			goto fail;
+		}
+		if (atomic_read(&pcm->in_stopped) &&
+					!atomic_read(&pcm->in_count)) {
+			pr_err("%s: count:%d/stopped:%d failed\n", __func__,
+					atomic_read(&pcm->in_count),
+					atomic_read(&pcm->in_stopped));
+			mutex_unlock(&pcm->read_lock);
+			return 0;
+		}
+
+		rc1 = is_dma_buf_avail(pcm);
+		if (!rc1) {
+			pr_err("%s: DMA buf not ready-returning from read\n",
+								__func__);
+			goto fail;
+		}
+		if (count >= pcm->buffer_size)
+			len = pcm->buffer_size;
+		else {
+			len = count;
+			pr_err("%s: short bytesavail[%d]"\
+				"bytesrequest[%d]"\
+				"bytesrejected%d]\n",\
+				__func__, pcm->buffer_size,
+				count, (pcm->buffer_size - count));
+		}
+		if (len) {
+			if (copy_to_user(buf,
+				(char *)(pcm->dma_buf[pcm->cpu_idx].v_addr),
+				len)) {
+				pr_err("%s copy_to_user failed len[%d]\n",
+							__func__, len);
+				rc = -EFAULT;
+				goto fail;
+			}
+			count -= len;
+			buf += len;
+		}
+		atomic_dec(&pcm->in_count);
+		memset((char *)(pcm->dma_buf[pcm->cpu_idx].v_addr),
+						0, pcm->buffer_size);
+		pcm->dma_buf[pcm->cpu_idx].used = 0;
+		wake_up(&pcm->wait);
+		pcm->cpu_idx++;
+		if (pcm->cpu_idx == pcm->buffer_count)
+			pcm->cpu_idx = 0;
+
+	}
+	rc = buf-start;
+	pr_debug("%s: pcm_in_read:rc:%d\n", __func__, rc);
+
+fail:
+	mutex_unlock(&pcm->read_lock);
+	return rc;
+}
+
+static int afe_start(struct pcm *pcm)
+{
+	union afe_port_config port_config;
+	port_config.rtproxy.num_ch =
+			pcm->channel_count;
+
+	pr_debug("%s: channel %d entered,port: %d,rate: %d\n", __func__,
+	port_config.rtproxy.num_ch, RT_PROXY_DAI_001_TX, pcm->sample_rate);
+
+	port_config.rtproxy.bitwidth = 16; /* Q6 only supports 16 */
+	port_config.rtproxy.interleaved = 1;
+	port_config.rtproxy.frame_sz = pcm->buffer_size;
+	port_config.rtproxy.jitter =
+				port_config.rtproxy.frame_sz/2;
+	port_config.rtproxy.lw_mark = 0;
+	port_config.rtproxy.hw_mark = 0;
+	port_config.rtproxy.rsvd = 0;
+	afe_open(RT_PROXY_DAI_001_TX, &port_config, pcm->sample_rate);
+	return 0;
+
+}
+
+static long pcm_in_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+	struct pcm *pcm = file->private_data;
+	int rc = 0;
+
+	mutex_lock(&pcm->lock);
+	switch (cmd) {
+	case AUDIO_START: {
+		pr_debug("%s: AUDIO_START\n", __func__);
+		if (atomic_read(&pcm->in_enabled)) {
+			pr_info("%s:AUDIO_START already over\n", __func__);
+			rc = 0;
+			break;
+		}
+		rc = config(pcm);
+		if (rc) {
+			pr_err("%s: IN Configuration failed\n", __func__);
+			rc = -EFAULT;
+			break;
+		}
+		pr_debug("%s: call config done\n", __func__);
+		atomic_set(&pcm->in_enabled, 1);
+		afe_start(pcm);
+		rc = wait_event_timeout(pcm->wait,
+				((pcm->start == 1) ||
+				atomic_read(&pcm->in_stopped)), 5 * HZ);
+		if (!rc) {
+			pr_err("%s: wait_event_timeout failed\n", __func__);
+			goto fail;
+		}
+		pr_debug("%s: afe start done\n", __func__);
+		if (atomic_read(&pcm->in_stopped)) {
+			pr_err("%s: stopped unexpected before start!!\n",
+								__func__);
+			mutex_unlock(&pcm->lock);
+			return 0;
+		}
+
+		hrtimer_start(&pcm->hrt, ns_to_ktime(0),
+					HRTIMER_MODE_REL);
+		break;
+	}
+	case AUDIO_STOP:
+		break;
+	case AUDIO_FLUSH:
+		break;
+	case AUDIO_SET_CONFIG: {
+		struct msm_audio_config config;
+
+		if (copy_from_user(&config, (void *) arg, sizeof(config))) {
+			rc = -EFAULT;
+			break;
+		}
+		pr_debug("%s: SET_CONFIG: channel_count:%d"\
+			"sample_rate:%d\n", __func__,
+			config.channel_count,
+			config.sample_rate);
+
+		if (!config.channel_count || config.channel_count > 2) {
+			pr_err("%s: Channels(%d) not supported\n",
+				__func__, config.channel_count);
+			rc = -EINVAL;
+			break;
+		}
+
+		if (config.sample_rate != 8000 &&
+			config.sample_rate != 16000 &&
+			config.sample_rate != 48000) {
+			pr_err("%s: Sample rate(%d) not supported\n",
+				__func__, config.sample_rate);
+			rc = -EINVAL;
+			break;
+		}
+
+		pcm->sample_rate = config.sample_rate;
+		pcm->channel_count = config.channel_count;
+		pcm->buffer_size = getbuffersize(pcm->sample_rate);
+
+		pr_debug("%s: Calculated buff size %d", __func__,
+						pcm->buffer_size);
+		break;
+	}
+	case AUDIO_GET_CONFIG: {
+		struct msm_audio_config config;
+		config.buffer_size = pcm->buffer_size;
+		config.buffer_count = pcm->buffer_count;
+		config.sample_rate = pcm->sample_rate;
+		config.channel_count = pcm->channel_count;
+		config.unused[0] = 0;
+		config.unused[1] = 0;
+		config.unused[2] = 0;
+		if (copy_to_user((void *) arg, &config, sizeof(config)))
+			rc = -EFAULT;
+		break;
+	}
+	case AUDIO_PAUSE:
+		pr_debug("%s: AUDIO_PAUSE %ld\n", __func__, arg);
+		if (arg == 1) {
+			pcm->start = 0;
+		} else if (arg == 0) {
+			pcm->start = 1;
+			hrtimer_start(&pcm->hrt, ns_to_ktime(0),
+					HRTIMER_MODE_REL);
+		}
+	break;
+
+	default:
+		rc = -EINVAL;
+		break;
+	}
+fail:
+	mutex_unlock(&pcm->lock);
+	return rc;
+}
+
+static int pcm_in_release(struct inode *inode, struct file *file)
+{
+	int rc = 0;
+	struct pcm *pcm = file->private_data;
+
+	pr_debug("[%s:%s] release session id[%d]\n", __MM_FILE__,
+		__func__, pcm->ac->session);
+	mutex_lock(&pcm->lock);
+
+
+	/* remove this session from topology list */
+	auddev_cfg_tx_copp_topology(pcm->ac->session,
+				DEFAULT_COPP_TOPOLOGY);
+
+	rc = pcm_in_disable(pcm);
+	hrtimer_cancel(&pcm->hrt);
+	rc = afe_cmd_memory_unmap(pcm->dma_addr);
+	if (rc < 0)
+		pr_err("%s: AFE memory unmap failed\n", __func__);
+	rc =  afe_unregister_get_events(RT_PROXY_DAI_001_TX);
+	if (rc < 0)
+		pr_err("%s: AFE unregister for events failed\n", __func__);
+
+	afe_close(RT_PROXY_DAI_001_TX);
+	pr_debug("%s: release all buffer\n", __func__);
+	q6asm_audio_client_buf_free_contiguous(OUT,
+				pcm->ac);
+	msm_clear_session_id(pcm->ac->session);
+	q6asm_audio_client_free(pcm->ac);
+	mutex_unlock(&pcm->lock);
+	mutex_destroy(&pcm->lock);
+	mutex_destroy(&pcm->read_lock);
+	kfree(pcm);
+	return rc;
+}
+
+static const struct file_operations pcm_in_proxy_fops = {
+	.owner		= THIS_MODULE,
+	.open		= pcm_in_open,
+	.read		= pcm_in_read,
+	.release	= pcm_in_release,
+	.unlocked_ioctl	= pcm_in_ioctl,
+};
+
+struct miscdevice pcm_in_proxy_misc = {
+	.minor	= MISC_DYNAMIC_MINOR,
+	.name	= "msm_pcm_in_proxy",
+	.fops	= &pcm_in_proxy_fops,
+};
+
+static int snddev_rtproxy_open(struct msm_snddev_info *dev_info)
+{
+	return 0;
+}
+
+static int snddev_rtproxy_close(struct msm_snddev_info *dev_info)
+{
+	return 0;
+}
+
+static int snddev_rtproxy_set_freq(struct msm_snddev_info *dev_info,
+				u32 req_freq)
+{
+	return 48000;
+}
+
+static int __init pcm_in_proxy_init(void)
+{
+	struct msm_snddev_info *dev_info;
+
+	dev_info = kzalloc(sizeof(struct msm_snddev_info), GFP_KERNEL);
+	if (!dev_info) {
+		pr_err("unable to allocate memeory for msm_snddev_info\n");
+		return -ENOMEM;
+	}
+	dev_info->name = "rtproxy_rx";
+	dev_info->copp_id = RT_PROXY_PORT_001_RX;
+	dev_info->acdb_id = 0;
+	dev_info->private_data = NULL;
+	dev_info->dev_ops.open = snddev_rtproxy_open;
+	dev_info->dev_ops.close = snddev_rtproxy_close;
+	dev_info->dev_ops.set_freq = snddev_rtproxy_set_freq;
+	dev_info->capability = SNDDEV_CAP_RX;
+	dev_info->opened = 0;
+	msm_snddev_register(dev_info);
+	dev_info->sample_rate = 48000;
+
+	pr_debug("%s: init done for proxy\n", __func__);
+
+	return misc_register(&pcm_in_proxy_misc);
+}
+
+device_initcall(pcm_in_proxy_init);
diff --git a/arch/arm/mach-msm/qdsp6v2/q6audio_v1_aio.c b/arch/arm/mach-msm/qdsp6v2/q6audio_v1_aio.c
index b46e0d3..96f823f 100644
--- a/arch/arm/mach-msm/qdsp6v2/q6audio_v1_aio.c
+++ b/arch/arm/mach-msm/qdsp6v2/q6audio_v1_aio.c
@@ -37,7 +37,6 @@
 	case ASM_STREAM_CMD_SET_ENCDEC_PARAM:
 	case ASM_DATA_EVENT_SR_CM_CHANGE_NOTIFY:
 	case ASM_DATA_EVENT_ENC_SR_CM_NOTIFY:
-	case APR_BASIC_RSP_RESULT:
 		audio_aio_cb(opcode, token, payload, audio);
 		break;
 	default:
@@ -107,15 +106,6 @@
 		e_payload.stream_info.sample_rate = audio->pcm_cfg.sample_rate;
 		audio_aio_post_event(audio, AUDIO_EVENT_STREAM_INFO, e_payload);
 		break;
-	case APR_BASIC_RSP_RESULT:
-		switch (payload[0]) {
-		case ASM_STREAM_CMD_CLOSE:
-			audio_aio_unmap_ion_region(audio);
-			break;
-		default:
-			break;
-		}
-		break;
 	default:
 		break;
 	}
diff --git a/arch/arm/mach-msm/qdsp6v2/ultrasound/Makefile b/arch/arm/mach-msm/qdsp6v2/ultrasound/Makefile
deleted file mode 100644
index 0be1303..0000000
--- a/arch/arm/mach-msm/qdsp6v2/ultrasound/Makefile
+++ /dev/null
@@ -1,2 +0,0 @@
-obj-y += q6usm.o usf.o usfcdev.o
-EXTRA_CFLAGS += -I$(src)/..
diff --git a/arch/arm/mach-msm/qdsp6v2/ultrasound/q6usm.h b/arch/arm/mach-msm/qdsp6v2/ultrasound/q6usm.h
index 1338e86..c68ad68 100644
--- a/arch/arm/mach-msm/qdsp6v2/ultrasound/q6usm.h
+++ b/arch/arm/mach-msm/qdsp6v2/ultrasound/q6usm.h
@@ -15,11 +15,17 @@
 
 #include <mach/qdsp6v2/apr_us.h>
 
+#define Q6USM_EVENT_UNDEF                0
+#define Q6USM_EVENT_READ_DONE            1
+#define Q6USM_EVENT_WRITE_DONE           2
+#define Q6USM_EVENT_SIGNAL_DETECT_RESULT 3
+
 /* cyclic buffer with 1 gap support */
 #define USM_MIN_BUF_CNT 3
 
 #define FORMAT_USPS_EPOS	0x00000000
 #define FORMAT_USRAW		0x00000001
+#define FORMAT_USPROX		0x00000002
 #define INVALID_FORMAT		0xffffffff
 
 #define IN			0x000
@@ -38,16 +44,6 @@
 /* bit 4 represents META enable of encoded data buffer */
 #define BUFFER_META_ENABLE	0x0010
 
-struct us_region {
-	dma_addr_t	phys;
-	/* If == NULL, the region isn't allocated */
-	void		*data;
-	/* number of buffers in the region */
-	uint32_t	buf_cnt;
-	/* size of buffer */
-	uint32_t	buf_size;
-};
-
 struct us_port_data {
 	dma_addr_t	phys;
 	/* cyclic region of buffers with 1 gap */
@@ -56,15 +52,17 @@
 	uint32_t	buf_cnt;
 	/* size of buffer */
 	uint32_t	buf_size;
-	/* TX: write index */
+	/* write index */
 	uint32_t	dsp_buf;
-	/* TX: read index */
+	/* read index */
 	uint32_t	cpu_buf;
 	/* expected token from dsp */
 	uint32_t	expected_token;
 	/* read or write locks */
 	struct mutex	lock;
 	spinlock_t	dsp_lock;
+	/* extended parameters, related to q6 variants */
+	void			*ext;
 };
 
 struct us_client {
@@ -96,19 +94,11 @@
 	void *priv);
 int q6usm_open_read(struct us_client *usc, uint32_t format);
 void q6usm_us_client_free(struct us_client *usc);
-int q6usm_memory_map(struct us_client *usc, uint32_t buf_add,
-		     int dir, uint32_t bufsz, uint32_t bufcnt);
-int q6usm_memory_unmap(struct us_client *usc, uint32_t buf_add,
-		       int dir);
-
-uint32_t q6usm_get_ready_data(int dir, struct us_client *usc);
 uint32_t q6usm_get_virtual_address(int dir, struct us_client *usc,
 				   struct vm_area_struct *vms);
-
 int q6usm_open_write(struct us_client *usc,  uint32_t format);
 int q6usm_write(struct us_client *usc, uint32_t write_ind);
-bool q6usm_is_write_buf_full(struct us_client *usc, uint32_t* free_region);
-
+bool q6usm_is_write_buf_full(struct us_client *usc, uint32_t *free_region);
 int q6usm_set_us_detection(struct us_client *usc,
 			   struct usm_session_cmd_detect_info *detect_info,
 			   uint16_t detect_info_size);
diff --git a/arch/arm/mach-msm/qdsp6v2/ultrasound/usf.c b/arch/arm/mach-msm/qdsp6v2/ultrasound/usf.c
index a973b92..d00eae8 100644
--- a/arch/arm/mach-msm/qdsp6v2/ultrasound/usf.c
+++ b/arch/arm/mach-msm/qdsp6v2/ultrasound/usf.c
@@ -27,8 +27,8 @@
 #include "usfcdev.h"
 
 /* The driver version*/
-#define DRV_VERSION "1.4.0"
-#define USF_VERSION_ID 0x0140
+#define DRV_VERSION "1.4.1"
+#define USF_VERSION_ID 0x0141
 
 /* Standard timeout in the asynchronous ops */
 #define USF_TIMEOUT_JIFFIES (1*HZ) /* 1 sec */
@@ -351,7 +351,7 @@
 	}
 
 	switch (opcode) {
-	case USM_DATA_EVENT_WRITE_DONE:
+	case Q6USM_EVENT_WRITE_DONE:
 		wake_up(&usf_xx->wait);
 		break;
 	default:
@@ -370,14 +370,14 @@
 	}
 
 	switch (opcode) {
-	case USM_DATA_EVENT_READ_DONE:
+	case Q6USM_EVENT_READ_DONE:
 		if (token == USM_WRONG_TOKEN)
 			usf_xx->usf_state = USF_ERROR_STATE;
 		usf_xx->new_region = token;
 		wake_up(&usf_xx->wait);
 		break;
 
-	case USM_SESSION_EVENT_SIGNAL_DETECT_RESULT:
+	case Q6USM_EVENT_SIGNAL_DETECT_RESULT:
 		usf_xx->us_detect_type = (payload[APR_US_DETECT_RESULT_IND]) ?
 					USF_US_DETECT_YES :
 					USF_US_DETECT_NO;
@@ -1043,8 +1043,7 @@
 
 	if ((usf_xx->usf_state != USF_WORK_STATE) ||
 	    (rc == -ERESTARTSYS)) {
-		pr_err("%s: Getting ready region failed "
-			"work state[%d]; rc[%d]\n",
+		pr_err("%s: Get ready region failure; state[%d]; rc[%d]\n",
 		       __func__, usf_xx->usf_state, rc);
 		return -EINTR;
 	}
@@ -1459,4 +1458,3 @@
 device_initcall(usf_init);
 
 MODULE_DESCRIPTION("Ultrasound framework driver");
-MODULE_VERSION(DRV_VERSION);
diff --git a/arch/arm/mach-msm/qdsp6v2/ultrasound/version_a/Makefile b/arch/arm/mach-msm/qdsp6v2/ultrasound/version_a/Makefile
new file mode 100644
index 0000000..38d7d51
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp6v2/ultrasound/version_a/Makefile
@@ -0,0 +1,2 @@
+obj-y += q6usm_a.o ../usf.o ../usfcdev.o
+ccflags-y := -I$(src)/.. -I$(src)/../..
diff --git a/arch/arm/mach-msm/qdsp6v2/ultrasound/q6usm.c b/arch/arm/mach-msm/qdsp6v2/ultrasound/version_a/q6usm_a.c
similarity index 96%
rename from arch/arm/mach-msm/qdsp6v2/ultrasound/q6usm.c
rename to arch/arm/mach-msm/qdsp6v2/ultrasound/version_a/q6usm_a.c
index 5400ccc..5d30eb1 100644
--- a/arch/arm/mach-msm/qdsp6v2/ultrasound/q6usm.c
+++ b/arch/arm/mach-msm/qdsp6v2/ultrasound/version_a/q6usm_a.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -19,11 +19,9 @@
 #include <linux/slab.h>
 #include <linux/msm_audio.h>
 #include <sound/apr_audio.h>
+#include <mach/qdsp6v2/apr_us_a.h>
 #include "q6usm.h"
 
-/* The driver version*/
-#define DRV_VERSION "1.2"
-
 #define SESSION_MAX 0x02 /* aDSP:USM limit */
 
 #define READDONE_IDX_STATUS     0
@@ -58,6 +56,96 @@
 
 static struct usm_mmap this_mmap;
 
+static void q6usm_add_mmaphdr(struct us_client *usc, struct apr_hdr *hdr,
+			      uint32_t pkt_size, bool cmd_flg)
+{
+	hdr->hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, \
+				       APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+	hdr->src_port = 0;
+	hdr->dest_port = 0;
+	if (cmd_flg) {
+		hdr->token = 0;
+		atomic_set(&this_mmap.cmd_state, 1);
+	}
+	hdr->pkt_size  = pkt_size;
+	return;
+}
+
+static int q6usm_memory_map(struct us_client *usc, uint32_t buf_add, int dir,
+		     uint32_t bufsz, uint32_t bufcnt)
+{
+	struct usm_stream_cmd_memory_map mem_map;
+	int rc = 0;
+
+	if ((usc == NULL) || (usc->apr == NULL) || (this_mmap.apr == NULL)) {
+		pr_err("%s: APR handle NULL\n", __func__);
+		return -EINVAL;
+	}
+
+	q6usm_add_mmaphdr(usc, &mem_map.hdr,
+			  sizeof(struct usm_stream_cmd_memory_map), true);
+	mem_map.hdr.opcode = USM_SESSION_CMD_MEMORY_MAP;
+
+	mem_map.buf_add = buf_add;
+	mem_map.buf_size = bufsz * bufcnt;
+	mem_map.mempool_id = 0;
+
+	pr_debug("%s: buf add[%x]  buf_add_parameter[%x]\n",
+		 __func__, mem_map.buf_add, buf_add);
+
+	rc = apr_send_pkt(this_mmap.apr, (uint32_t *) &mem_map);
+	if (rc < 0) {
+		pr_err("%s: mem_map op[0x%x]rc[%d]\n",
+		       __func__, mem_map.hdr.opcode, rc);
+		goto fail_cmd;
+	}
+
+	rc = wait_event_timeout(this_mmap.cmd_wait,
+				(atomic_read(&this_mmap.cmd_state) == 0),
+				Q6USM_TIMEOUT_JIFFIES);
+	if (!rc) {
+		rc = -ETIME;
+		pr_err("%s: timeout. waited for memory_map\n", __func__);
+	} else
+		rc = 0;
+fail_cmd:
+	return rc;
+}
+
+int q6usm_memory_unmap(struct us_client *usc, uint32_t buf_add, int dir)
+{
+	struct usm_stream_cmd_memory_unmap mem_unmap;
+	int rc = 0;
+
+	if ((usc == NULL) || (usc->apr == NULL) || (this_mmap.apr == NULL)) {
+		pr_err("%s: APR handle NULL\n", __func__);
+		return -EINVAL;
+	}
+
+	q6usm_add_mmaphdr(usc, &mem_unmap.hdr,
+			  sizeof(struct usm_stream_cmd_memory_unmap), true);
+	mem_unmap.hdr.opcode = USM_SESSION_CMD_MEMORY_UNMAP;
+	mem_unmap.buf_add = buf_add;
+
+	rc = apr_send_pkt(this_mmap.apr, (uint32_t *) &mem_unmap);
+	if (rc < 0) {
+		pr_err("%s:mem_unmap op[0x%x]rc[%d]\n",
+		       __func__, mem_unmap.hdr.opcode, rc);
+		goto fail_cmd;
+	}
+
+	rc = wait_event_timeout(this_mmap.cmd_wait,
+				(atomic_read(&this_mmap.cmd_state) == 0),
+				Q6USM_TIMEOUT_JIFFIES);
+	if (!rc) {
+		rc = -ETIME;
+		pr_err("%s: timeout. waited for memory_map\n", __func__);
+	} else
+		rc = 0;
+fail_cmd:
+	return rc;
+}
+
 static int q6usm_session_alloc(struct us_client *usc)
 {
 	int ind = 0;
@@ -276,10 +364,11 @@
 	uint32_t token;
 	uint32_t *payload = data->payload;
 
-	pr_debug("%s: ptr0[0x%x]; ptr1[0x%x]; opcode[0x%x];"
-		 "token[0x%x]; payload_s[%d]; src[%d]; dest[%d];\n",
-		 __func__, payload[0], payload[1], data->opcode, data->token,
-		 data->payload_size, data->src_port, data->dest_port);
+	pr_debug("%s: ptr0[0x%x]; ptr1[0x%x]; opcode[0x%x]\n",
+		 __func__, payload[0], payload[1], data->opcode);
+	pr_debug("%s: token[0x%x]; payload_size[%d]; src[%d]; dest[%d];\n",
+		 __func__, data->token, data->payload_size,
+		 data->src_port, data->dest_port);
 
 	if (data->opcode == APR_BASIC_RSP_RESULT) {
 		/* status field check */
@@ -315,6 +404,7 @@
 	unsigned long dsp_flags;
 	uint32_t *payload = data->payload;
 	uint32_t token = data->token;
+	uint32_t opcode = Q6USM_EVENT_UNDEF;
 
 	if (usc == NULL) {
 		pr_err("%s: client info is NULL\n", __func__);
@@ -363,6 +453,7 @@
 	case USM_DATA_EVENT_READ_DONE: {
 		struct us_port_data *port = &usc->port[OUT];
 
+		opcode = Q6USM_EVENT_READ_DONE;
 		spin_lock_irqsave(&port->dsp_lock, dsp_flags);
 		if (payload[READDONE_IDX_STATUS]) {
 			pr_err("%s: wrong READDONE[%d]; token[%d]\n",
@@ -408,6 +499,7 @@
 	case USM_DATA_EVENT_WRITE_DONE: {
 		struct us_port_data *port = &usc->port[IN];
 
+		opcode = Q6USM_EVENT_WRITE_DONE;
 		if (payload[WRITEDONE_IDX_STATUS]) {
 			pr_err("%s: wrong WRITEDONE_IDX_STATUS[%d]\n",
 			       __func__,
@@ -428,6 +520,7 @@
 		pr_debug("%s: US detect result: result=%d",
 			 __func__,
 			 payload[0]);
+		opcode = Q6USM_EVENT_SIGNAL_DETECT_RESULT;
 
 		break;
 	} /* case USM_SESSION_EVENT_SIGNAL_DETECT_RESULT */
@@ -438,21 +531,12 @@
 	} /* switch */
 
 	if (usc->cb)
-		usc->cb(data->opcode, token,
+		usc->cb(opcode, token,
 			data->payload, usc->priv);
 
 	return 0;
 }
 
-uint32_t q6usm_get_ready_data(int dir, struct us_client *usc)
-{
-	uint32_t ret = 0xffffffff;
-
-	if ((usc != NULL) && ((dir == IN) || (dir == OUT)))
-		ret = usc->port[dir].dsp_buf;
-	return ret;
-}
-
 uint32_t q6usm_get_virtual_address(int dir,
 				   struct us_client *usc,
 				   struct vm_area_struct *vms)
@@ -490,21 +574,6 @@
 	return;
 }
 
-static void q6usm_add_mmaphdr(struct us_client *usc, struct apr_hdr *hdr,
-			      uint32_t pkt_size, bool cmd_flg)
-{
-	hdr->hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, \
-				       APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
-	hdr->src_port = 0;
-	hdr->dest_port = 0;
-	if (cmd_flg) {
-		hdr->token = 0;
-		atomic_set(&this_mmap.cmd_state, 1);
-	}
-	hdr->pkt_size  = pkt_size;
-	return;
-}
-
 static uint32_t q6usm_ext2int_format(uint32_t ext_format)
 {
 	uint32_t int_format = INVALID_FORMAT;
@@ -515,6 +584,9 @@
 	case FORMAT_USRAW:
 		int_format = US_RAW_FORMAT;
 		break;
+	case FORMAT_USPROX:
+		int_format = US_PROX_FORMAT;
+		break;
 	default:
 		pr_err("%s: Invalid format[%d]\n", __func__, ext_format);
 		break;
@@ -570,7 +642,7 @@
 }
 
 
-int q6usm_enc_cfg_blk(struct us_client *usc, struct us_encdec_cfg* us_cfg)
+int q6usm_enc_cfg_blk(struct us_client *usc, struct us_encdec_cfg *us_cfg)
 {
 	uint32_t int_format = INVALID_FORMAT;
 	struct usm_stream_cmd_encdec_cfg_blk  enc_cfg_obj;
@@ -846,80 +918,6 @@
 }
 
 
-int q6usm_memory_map(struct us_client *usc, uint32_t buf_add, int dir,
-		     uint32_t bufsz, uint32_t bufcnt)
-{
-	struct usm_stream_cmd_memory_map mem_map;
-	int rc = 0;
-
-	if ((usc == NULL) || (usc->apr == NULL) || (this_mmap.apr == NULL)) {
-		pr_err("%s: APR handle NULL\n", __func__);
-		return -EINVAL;
-	}
-
-	q6usm_add_mmaphdr(usc, &mem_map.hdr,
-			  sizeof(struct usm_stream_cmd_memory_map), true);
-	mem_map.hdr.opcode = USM_SESSION_CMD_MEMORY_MAP;
-
-	mem_map.buf_add = buf_add;
-	mem_map.buf_size = bufsz * bufcnt;
-	mem_map.mempool_id = 0;
-
-	pr_debug("%s: buf add[%x]  buf_add_parameter[%x]\n",
-		 __func__, mem_map.buf_add, buf_add);
-
-	rc = apr_send_pkt(this_mmap.apr, (uint32_t *) &mem_map);
-	if (rc < 0) {
-		pr_err("%s: mem_map op[0x%x]rc[%d]\n",
-		       __func__, mem_map.hdr.opcode, rc);
-		goto fail_cmd;
-	}
-
-	rc = wait_event_timeout(this_mmap.cmd_wait,
-				(atomic_read(&this_mmap.cmd_state) == 0),
-				Q6USM_TIMEOUT_JIFFIES);
-	if (!rc) {
-		rc = -ETIME;
-		pr_err("%s: timeout. waited for memory_map\n", __func__);
-	} else
-		rc = 0;
-fail_cmd:
-	return rc;
-}
-
-int q6usm_memory_unmap(struct us_client *usc, uint32_t buf_add, int dir)
-{
-	struct usm_stream_cmd_memory_unmap mem_unmap;
-	int rc = 0;
-
-	if ((usc == NULL) || (usc->apr == NULL) || (this_mmap.apr == NULL)) {
-		pr_err("%s: APR handle NULL\n", __func__);
-		return -EINVAL;
-	}
-
-	q6usm_add_mmaphdr(usc, &mem_unmap.hdr,
-			  sizeof(struct usm_stream_cmd_memory_unmap), true);
-	mem_unmap.hdr.opcode = USM_SESSION_CMD_MEMORY_UNMAP;
-	mem_unmap.buf_add = buf_add;
-
-	rc = apr_send_pkt(this_mmap.apr, (uint32_t *) &mem_unmap);
-	if (rc < 0) {
-		pr_err("%s:mem_unmap op[0x%x]rc[%d]\n",
-		       __func__, mem_unmap.hdr.opcode, rc);
-		goto fail_cmd;
-	}
-
-	rc = wait_event_timeout(this_mmap.cmd_wait,
-				(atomic_read(&this_mmap.cmd_state) == 0),
-				Q6USM_TIMEOUT_JIFFIES);
-	if (!rc) {
-		rc = -ETIME;
-		pr_err("%s: timeout. waited for memory_map\n", __func__);
-	} else
-		rc = 0;
-fail_cmd:
-	return rc;
-}
 
 int q6usm_read(struct us_client *usc, uint32_t read_ind)
 {
@@ -1054,7 +1052,7 @@
 	return rc;
 }
 
-bool q6usm_is_write_buf_full(struct us_client *usc, uint32_t* free_region)
+bool q6usm_is_write_buf_full(struct us_client *usc, uint32_t *free_region)
 {
 	struct us_port_data *port = NULL;
 	u32 cpu_buf = 0;
diff --git a/arch/arm/mach-msm/ramdump.c b/arch/arm/mach-msm/ramdump.c
index 21e81dd..e33ec48 100644
--- a/arch/arm/mach-msm/ramdump.c
+++ b/arch/arm/mach-msm/ramdump.c
@@ -181,7 +181,7 @@
 	.poll = ramdump_poll
 };
 
-void *create_ramdump_device(const char *dev_name)
+void *create_ramdump_device(const char *dev_name, struct device *parent)
 {
 	int ret;
 	struct ramdump_device *rd_dev;
@@ -207,6 +207,7 @@
 	rd_dev->device.minor = MISC_DYNAMIC_MINOR;
 	rd_dev->device.name = rd_dev->name;
 	rd_dev->device.fops = &ramdump_file_ops;
+	rd_dev->device.parent = parent;
 
 	init_waitqueue_head(&rd_dev->dump_wait_q);
 
diff --git a/arch/arm/mach-msm/ramdump.h b/arch/arm/mach-msm/ramdump.h
index 9006010..3e5bfaf 100644
--- a/arch/arm/mach-msm/ramdump.h
+++ b/arch/arm/mach-msm/ramdump.h
@@ -13,12 +13,14 @@
 #ifndef _RAMDUMP_HEADER
 #define _RAMDUMP_HEADER
 
+struct device;
+
 struct ramdump_segment {
 	unsigned long address;
 	unsigned long size;
 };
 
-void *create_ramdump_device(const char *dev_name);
+void *create_ramdump_device(const char *dev_name, struct device *parent);
 void destroy_ramdump_device(void *dev);
 int do_ramdump(void *handle, struct ramdump_segment *segments,
 		int nsegments);
diff --git a/arch/arm/mach-msm/restart.c b/arch/arm/mach-msm/restart.c
index 2189747..7966177 100644
--- a/arch/arm/mach-msm/restart.c
+++ b/arch/arm/mach-msm/restart.c
@@ -228,6 +228,7 @@
 	}
 
 	flush_cache_all();
+	outer_flush_all();
 }
 
 void msm_restart(char mode, const char *cmd)
diff --git a/arch/arm/mach-msm/restart_7k.c b/arch/arm/mach-msm/restart_7k.c
index dc9edf4..9675b61 100644
--- a/arch/arm/mach-msm/restart_7k.c
+++ b/arch/arm/mach-msm/restart_7k.c
@@ -21,13 +21,11 @@
 #include <mach/proc_comm.h>
 
 #include "devices-msm7x2xa.h"
-#include "smd_rpcrouter.h"
 
 static uint32_t restart_reason = 0x776655AA;
 
 static void msm_pm_power_off(void)
 {
-	msm_rpcrouter_close();
 	msm_proc_comm(PCOM_POWER_DOWN, 0, 0);
 	for (;;)
 		;
@@ -35,7 +33,6 @@
 
 static void msm_pm_restart(char str, const char *cmd)
 {
-	msm_rpcrouter_close();
 	pr_debug("The reset reason is %x\n", restart_reason);
 
 	/* Disable interrupts */
diff --git a/arch/arm/mach-msm/rpm-regulator-smd.c b/arch/arm/mach-msm/rpm-regulator-smd.c
index 9c621c4..d1c61fe 100644
--- a/arch/arm/mach-msm/rpm-regulator-smd.c
+++ b/arch/arm/mach-msm/rpm-regulator-smd.c
@@ -112,7 +112,7 @@
 	PARAM(HEAD_ROOM,       1,  0,  0,  1, "hr",   0, 0x7FFFFFFF, "qcom,init-head-room"),
 	PARAM(QUIET_MODE,      0,  1,  0,  0, "qm",   0, 2,          "qcom,init-quiet-mode"),
 	PARAM(FREQ_REASON,     0,  1,  0,  1, "resn", 0, 8,          "qcom,init-freq-reason"),
-	PARAM(CORNER,          0,  1,  0,  0, "corn", 0, 6,          "qcom,init-voltage-corner"),
+	PARAM(CORNER,          1,  1,  0,  0, "corn", 0, 6,          "qcom,init-voltage-corner"),
 	PARAM(BYPASS,          1,  0,  0,  0, "bypa", 0, 1,          "qcom,init-disallow-bypass"),
 };
 
@@ -1037,6 +1037,19 @@
 	.enable_time		= rpm_vreg_enable_time,
 };
 
+static struct regulator_ops ldo_corner_ops = {
+	.enable			= rpm_vreg_enable,
+	.disable		= rpm_vreg_disable,
+	.is_enabled		= rpm_vreg_is_enabled,
+	.set_voltage		= rpm_vreg_set_voltage_corner,
+	.get_voltage		= rpm_vreg_get_voltage_corner,
+	.list_voltage		= rpm_vreg_list_voltage,
+	.set_mode		= rpm_vreg_set_mode,
+	.get_mode		= rpm_vreg_get_mode,
+	.get_optimum_mode	= rpm_vreg_get_optimum_mode,
+	.enable_time		= rpm_vreg_enable_time,
+};
+
 static struct regulator_ops smps_ops = {
 	.enable			= rpm_vreg_enable,
 	.disable		= rpm_vreg_disable,
@@ -1194,11 +1207,14 @@
 
 	/*
 	 * Switch to voltage corner regulator ops if qcom,use-voltage-corner
-	 * is specified in the device node (SMPS only).
+	 * is specified in the device node (SMPS and LDO only).
 	 */
-	if (of_find_property(node, "qcom,use-voltage-corner", NULL)
-	    && regulator_type == RPM_REGULATOR_SMD_TYPE_SMPS)
-		reg->rdesc.ops = &smps_corner_ops;
+	if (of_property_read_bool(node, "qcom,use-voltage-corner")) {
+		if (regulator_type == RPM_REGULATOR_SMD_TYPE_SMPS)
+			reg->rdesc.ops = &smps_corner_ops;
+		else if (regulator_type == RPM_REGULATOR_SMD_TYPE_LDO)
+			reg->rdesc.ops = &ldo_corner_ops;
+	}
 
 	if (regulator_type == RPM_REGULATOR_SMD_TYPE_VS)
 		reg->rdesc.n_voltages = 0;
diff --git a/arch/arm/mach-msm/rpm-regulator.c b/arch/arm/mach-msm/rpm-regulator.c
index 01543a2..4e5281d 100644
--- a/arch/arm/mach-msm/rpm-regulator.c
+++ b/arch/arm/mach-msm/rpm-regulator.c
@@ -19,7 +19,6 @@
 #include <linux/kernel.h>
 #include <linux/init.h>
 #include <linux/slab.h>
-#include <linux/spinlock.h>
 #include <linux/string.h>
 #include <linux/platform_device.h>
 #include <linux/wakelock.h>
@@ -299,12 +298,9 @@
 }
 
 static bool requires_tcxo_workaround;
-static bool tcxo_workaround_noirq;
 static struct clk *tcxo_handle;
 static struct wake_lock tcxo_wake_lock;
 static DEFINE_MUTEX(tcxo_mutex);
-/* Spin lock needed for sleep-selectable regulators. */
-static DEFINE_SPINLOCK(tcxo_noirq_lock);
 static bool tcxo_is_enabled;
 /*
  * TCXO must be kept on for at least the duration of its warmup (4 ms);
@@ -314,19 +310,10 @@
 
 static void tcxo_get_handle(void)
 {
-	int rc;
-
 	if (!tcxo_handle) {
 		tcxo_handle = clk_get_sys("rpm-regulator", "vref_buff");
-		if (IS_ERR(tcxo_handle)) {
+		if (IS_ERR(tcxo_handle))
 			tcxo_handle = NULL;
-		} else {
-			rc = clk_prepare(tcxo_handle);
-			if (rc) {
-				clk_put(tcxo_handle);
-				tcxo_handle = NULL;
-			}
-		}
 	}
 }
 
@@ -342,7 +329,7 @@
 	int rc;
 
 	if (tcxo_handle && !tcxo_is_enabled) {
-		rc = clk_enable(tcxo_handle);
+		rc = clk_prepare_enable(tcxo_handle);
 		if (!rc) {
 			tcxo_is_enabled = true;
 			wake_lock(&tcxo_wake_lock);
@@ -355,21 +342,13 @@
 
 static void tcxo_delayed_disable_work(struct work_struct *work)
 {
-	unsigned long flags = 0;
+	mutex_lock(&tcxo_mutex);
 
-	if (tcxo_workaround_noirq)
-		spin_lock_irqsave(&tcxo_noirq_lock, flags);
-	else
-		mutex_lock(&tcxo_mutex);
-
-	clk_disable(tcxo_handle);
+	clk_disable_unprepare(tcxo_handle);
 	tcxo_is_enabled = false;
 	wake_unlock(&tcxo_wake_lock);
 
-	if (tcxo_workaround_noirq)
-		spin_unlock_irqrestore(&tcxo_noirq_lock, flags);
-	else
-		mutex_unlock(&tcxo_mutex);
+	mutex_unlock(&tcxo_mutex);
 }
 
 static DECLARE_DELAYED_WORK(tcxo_disable_work, tcxo_delayed_disable_work);
@@ -387,8 +366,8 @@
 				msecs_to_jiffies(TCXO_WARMUP_TIME_MS) + 1);
 }
 
-/* Spin lock needed for sleep-selectable regulators. */
-static DEFINE_SPINLOCK(rpm_noirq_lock);
+/* Mutex lock needed for sleep-selectable regulators. */
+static DEFINE_MUTEX(rpm_sleep_sel_lock);
 
 static int voltage_from_req(struct vreg *vreg)
 {
@@ -421,7 +400,6 @@
 {
 	struct msm_rpm_iv_pair *prev_req;
 	int rc = 0, max_uV_vote = 0;
-	unsigned long flags = 0;
 	bool tcxo_enabled = false;
 	bool voltage_increased = false;
 	unsigned prev0, prev1;
@@ -470,17 +448,19 @@
 		if (requires_tcxo_workaround && vreg->requires_cxo
 		    && (set == MSM_RPM_CTX_SET_0)
 		    && (GET_PART(vreg, uV) > GET_PART_PREV_ACT(vreg, uV))) {
+			mutex_lock(&tcxo_mutex);
+			if (!tcxo_handle)
+				tcxo_get_handle();
 			voltage_increased = true;
-			spin_lock_irqsave(&tcxo_noirq_lock, flags);
 			tcxo_enabled = tcxo_enable();
 		}
 
-		rc = msm_rpmrs_set_noirq(set, vreg->req, cnt);
+		rc = msm_rpmrs_set(set, vreg->req, cnt);
 		if (rc) {
 			vreg->req[0].value = prev0;
 			vreg->req[1].value = prev1;
 
-			vreg_err(vreg, "msm_rpmrs_set_noirq failed - "
+			vreg_err(vreg, "msm_rpmrs_set failed - "
 				"set=%s, id=%d, rc=%d\n",
 				(set == MSM_RPM_CTX_SET_0 ? "active" : "sleep"),
 				vreg->req[0].id, rc);
@@ -502,7 +482,7 @@
 		if (voltage_increased) {
 			if (tcxo_enabled)
 				tcxo_delayed_disable();
-			spin_unlock_irqrestore(&tcxo_noirq_lock, flags);
+			mutex_unlock(&tcxo_mutex);
 		}
 	} else if (msm_rpm_vreg_debug_mask & MSM_RPM_VREG_DEBUG_DUPLICATE) {
 		rpm_regulator_duplicate(vreg, set, cnt);
@@ -511,19 +491,18 @@
 	return rc;
 }
 
-static int vreg_set_noirq(struct vreg *vreg, enum rpm_vreg_voter voter,
+static int vreg_set_sleep_sel(struct vreg *vreg, enum rpm_vreg_voter voter,
 			  int sleep, unsigned mask0, unsigned val0,
 			  unsigned mask1, unsigned val1, unsigned cnt,
 			  int update_voltage)
 {
 	unsigned int s_mask[2] = {mask0, mask1}, s_val[2] = {val0, val1};
-	unsigned long flags;
 	int rc;
 
 	if (voter < 0 || voter >= RPM_VREG_VOTER_COUNT)
 		return -EINVAL;
 
-	spin_lock_irqsave(&rpm_noirq_lock, flags);
+	mutex_lock(&rpm_sleep_sel_lock);
 
 	/*
 	 * Send sleep set request first so that subsequent set_mode, etc calls
@@ -559,7 +538,7 @@
 	rc = vreg_send_request(vreg, voter, MSM_RPM_CTX_SET_0, mask0, val0,
 					mask1, val1, cnt, update_voltage);
 
-	spin_unlock_irqrestore(&rpm_noirq_lock, flags);
+	mutex_unlock(&rpm_sleep_sel_lock);
 
 	return rc;
 }
@@ -575,10 +554,8 @@
  * Returns 0 on success or errno.
  *
  * This function is used to vote for the voltage of a regulator without
- * using the regulator framework.  It is needed by consumers which hold spin
- * locks or have interrupts disabled because the regulator framework can sleep.
- * It is also needed by consumers which wish to only vote for active set
- * regulator voltage.
+ * using the regulator framework.  It is needed for consumers which wish to only
+ * vote for active set regulator voltage.
  *
  * If sleep_also == 0, then a sleep-set value of 0V will be voted for.
  *
@@ -693,10 +670,10 @@
 		    = vreg->part->enable_state.mask;
 	}
 
-	rc = vreg_set_noirq(vreg, voter, sleep_also, mask[0], val[0], mask[1],
-			    val[1], vreg->part->request_len, 1);
+	rc = vreg_set_sleep_sel(vreg, voter, sleep_also, mask[0], val[0],
+				mask[1], val[1], vreg->part->request_len, 1);
 	if (rc)
-		vreg_err(vreg, "vreg_set_noirq failed, rc=%d\n", rc);
+		vreg_err(vreg, "vreg_set_sleep_sel failed, rc=%d\n", rc);
 
 	return rc;
 }
@@ -743,10 +720,10 @@
 	val[vreg->part->freq.word] = freq << vreg->part->freq.shift;
 	mask[vreg->part->freq.word] = vreg->part->freq.mask;
 
-	rc = vreg_set_noirq(vreg, RPM_VREG_VOTER_REG_FRAMEWORK, 1, mask[0],
+	rc = vreg_set_sleep_sel(vreg, RPM_VREG_VOTER_REG_FRAMEWORK, 1, mask[0],
 			   val[0], mask[1], val[1], vreg->part->request_len, 0);
 	if (rc)
-		vreg_err(vreg, "vreg_set failed, rc=%d\n", rc);
+		vreg_err(vreg, "vreg_set_sleep_sel failed, rc=%d\n", rc);
 
 	return rc;
 }
@@ -1018,10 +995,8 @@
 static int vreg_store(struct vreg *vreg, unsigned mask0, unsigned val0,
 		unsigned mask1, unsigned val1)
 {
-	unsigned long flags = 0;
-
 	if (vreg->pdata.sleep_selectable)
-		spin_lock_irqsave(&rpm_noirq_lock, flags);
+		mutex_lock(&rpm_sleep_sel_lock);
 
 	vreg->req[0].value &= ~mask0;
 	vreg->req[0].value |= val0 & mask0;
@@ -1030,7 +1005,7 @@
 	vreg->req[1].value |= val1 & mask1;
 
 	if (vreg->pdata.sleep_selectable)
-		spin_unlock_irqrestore(&rpm_noirq_lock, flags);
+		mutex_unlock(&rpm_sleep_sel_lock);
 
 	return 0;
 }
@@ -1039,7 +1014,6 @@
 		unsigned mask1, unsigned val1, unsigned cnt)
 {
 	unsigned prev0 = 0, prev1 = 0;
-	unsigned long flags = 0;
 	bool tcxo_enabled = false;
 	bool voltage_increased = false;
 	int rc;
@@ -1049,7 +1023,7 @@
 	 * just the active set values.
 	 */
 	if (vreg->pdata.sleep_selectable)
-		return vreg_set_noirq(vreg, RPM_VREG_VOTER_REG_FRAMEWORK, 1,
+		return vreg_set_sleep_sel(vreg, RPM_VREG_VOTER_REG_FRAMEWORK, 1,
 					mask0, val0, mask1, val1, cnt, 1);
 
 	prev0 = vreg->req[0].value;
@@ -1071,21 +1045,14 @@
 	/* Enable CXO clock if necessary for TCXO workaround. */
 	if (requires_tcxo_workaround && vreg->requires_cxo
 	    && (GET_PART(vreg, uV) > GET_PART_PREV_ACT(vreg, uV))) {
+		mutex_lock(&tcxo_mutex);
 		if (!tcxo_handle)
 			tcxo_get_handle();
-		if (tcxo_workaround_noirq)
-			spin_lock_irqsave(&tcxo_noirq_lock, flags);
-		else
-			mutex_lock(&tcxo_mutex);
-
 		voltage_increased = true;
 		tcxo_enabled = tcxo_enable();
 	}
 
-	if (voltage_increased && tcxo_workaround_noirq)
-		rc = msm_rpmrs_set_noirq(MSM_RPM_CTX_SET_0, vreg->req, cnt);
-	else
-		rc = msm_rpm_set(MSM_RPM_CTX_SET_0, vreg->req, cnt);
+	rc = msm_rpm_set(MSM_RPM_CTX_SET_0, vreg->req, cnt);
 
 	if (rc) {
 		vreg->req[0].value = prev0;
@@ -1107,11 +1074,7 @@
 	if (voltage_increased) {
 		if (tcxo_enabled)
 			tcxo_delayed_disable();
-
-		if (tcxo_workaround_noirq)
-			spin_unlock_irqrestore(&tcxo_noirq_lock, flags);
-		else
-			mutex_unlock(&tcxo_mutex);
+		mutex_unlock(&tcxo_mutex);
 	}
 
 	return rc;
@@ -1794,7 +1757,6 @@
 	struct rpm_regulator_platform_data *platform_data;
 	static struct rpm_regulator_consumer_mapping *prev_consumer_map;
 	static int prev_consumer_map_len;
-	struct vreg *vreg;
 	int rc = 0;
 	int i, id;
 
@@ -1880,18 +1842,6 @@
 				"rpm_regulator_tcxo");
 	}
 
-	if (requires_tcxo_workaround && !tcxo_workaround_noirq) {
-		for (i = 0; i < platform_data->num_regulators; i++) {
-			vreg = rpm_vreg_get_vreg(
-					platform_data->init_data[i].id);
-			if (vreg && vreg->requires_cxo
-			    && platform_data->init_data[i].sleep_selectable) {
-				tcxo_workaround_noirq = true;
-				break;
-			}
-		}
-	}
-
 	/* Initialize all of the regulators listed in the platform data. */
 	for (i = 0; i < platform_data->num_regulators; i++) {
 		rc = rpm_vreg_init_regulator(&platform_data->init_data[i],
diff --git a/arch/arm/mach-msm/rpm-smd.c b/arch/arm/mach-msm/rpm-smd.c
index 7c31e76..f97eb9a 100644
--- a/arch/arm/mach-msm/rpm-smd.c
+++ b/arch/arm/mach-msm/rpm-smd.c
@@ -943,7 +943,7 @@
 
 static bool msm_rpm_set_standalone(void)
 {
-	if (machine_is_msm8974()) {
+	if (machine_is_msm9625()) {
 		pr_warn("%s(): Running in standalone mode, requests "
 				"will not be sent to RPM\n", __func__);
 		standalone = true;
diff --git a/arch/arm/mach-msm/rpm_master_stat.c b/arch/arm/mach-msm/rpm_master_stat.c
new file mode 100644
index 0000000..4dcf5eb
--- /dev/null
+++ b/arch/arm/mach-msm/rpm_master_stat.c
@@ -0,0 +1,247 @@
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/mm.h>
+#include <asm/uaccess.h>
+
+#include <mach/msm_iomap.h>
+#include "rpm_stats.h"
+#define MSG_RAM_SIZE_PER_MASTER	32
+
+enum {
+	NUMSHUTDOWNS,
+	ACTIVECORES,
+	MASTER_ID_MAX,
+};
+
+static char *msm_rpm_master_stats_id_labels[MASTER_ID_MAX] = {
+	[NUMSHUTDOWNS] = "num_shutdowns",
+	[ACTIVECORES] = "active_cores",
+};
+
+
+struct msm_rpm_master_stats {
+	unsigned long numshutdowns;
+	unsigned long active_cores;
+};
+
+struct msm_rpm_master_stats_private_data {
+	void __iomem *reg_base;
+	u32 len;
+	char **master_names;
+	u32 nomasters;
+	char buf[256];
+	struct msm_rpm_master_stats_platform_data *platform_data;
+};
+
+static int msm_rpm_master_stats_file_close(struct inode *inode,
+		struct file *file)
+{
+	struct msm_rpm_master_stats_private_data *private = file->private_data;
+
+	if (private->reg_base)
+		iounmap(private->reg_base);
+	kfree(file->private_data);
+
+	return 0;
+}
+
+static int msm_rpm_master_copy_stats(
+		struct msm_rpm_master_stats_private_data *pdata)
+{
+	struct msm_rpm_master_stats record;
+	static int nomasters;
+	int count;
+	static DEFINE_MUTEX(msm_rpm_master_stats_mutex);
+	int j = 0;
+
+	mutex_lock(&msm_rpm_master_stats_mutex);
+	/*
+	 * iterrate possible nomasters times.
+	 * 8960, 8064 have 5 masters.
+	 * 8930 has 4 masters.
+	 * 9x15 has 3 masters.
+	 */
+	if (nomasters > pdata->nomasters - 1) {
+		nomasters = 0;
+		mutex_unlock(&msm_rpm_master_stats_mutex);
+		return 0;
+	}
+
+	record.numshutdowns = readl_relaxed(pdata->reg_base +
+			(nomasters * MSG_RAM_SIZE_PER_MASTER));
+	record.active_cores = readl_relaxed(pdata->reg_base +
+				(nomasters * MSG_RAM_SIZE_PER_MASTER + 4));
+
+	count = snprintf(pdata->buf, sizeof(pdata->buf),
+		"%s\n\t%s:%lu\n\t%s:%lu\n",
+		pdata->master_names[nomasters],
+		msm_rpm_master_stats_id_labels[0],
+		record.numshutdowns,
+		msm_rpm_master_stats_id_labels[1],
+		record.active_cores);
+
+	j = find_first_bit(&record.active_cores, BITS_PER_LONG);
+	while (j < BITS_PER_LONG) {
+		count += snprintf(pdata->buf + count,
+			sizeof(pdata->buf) - count,
+			"\t\tcore%d\n", j);
+		j = find_next_bit(&record.active_cores,
+				BITS_PER_LONG, j + 1);
+	}
+
+
+	nomasters++;
+	mutex_unlock(&msm_rpm_master_stats_mutex);
+	return count;
+}
+
+static int msm_rpm_master_stats_file_read(struct file *file, char __user *bufu,
+				  size_t count, loff_t *ppos)
+{
+	struct msm_rpm_master_stats_private_data *prvdata;
+	struct msm_rpm_master_stats_platform_data *pdata;
+
+	prvdata = file->private_data;
+	if (!prvdata)
+		return -EINVAL;
+
+	pdata = prvdata->platform_data;
+	if (!pdata)
+		return -EINVAL;
+
+	if (!bufu || count < 0)
+		return -EINVAL;
+
+	if ((*ppos <= pdata->phys_size)) {
+		prvdata->len = msm_rpm_master_copy_stats(prvdata);
+		*ppos = 0;
+	}
+
+	return simple_read_from_buffer(bufu, count, ppos,
+			prvdata->buf, prvdata->len);
+}
+
+static int msm_rpm_master_stats_file_open(struct inode *inode,
+		struct file *file)
+{
+	struct msm_rpm_master_stats_private_data *prvdata;
+	struct msm_rpm_master_stats_platform_data *pdata;
+
+	pdata = inode->i_private;
+
+	file->private_data =
+		kmalloc(sizeof(struct msm_rpm_master_stats_private_data),
+			GFP_KERNEL);
+
+	if (!file->private_data)
+		return -ENOMEM;
+	prvdata = file->private_data;
+
+	prvdata->reg_base = ioremap(pdata->phys_addr_base,
+		pdata->phys_size);
+	if (!prvdata->reg_base) {
+		kfree(file->private_data);
+		prvdata = NULL;
+		pr_err("%s: ERROR could not ioremap start=%p, len=%u\n",
+			__func__, (void *)pdata->phys_addr_base,
+			pdata->phys_size);
+		return -EBUSY;
+	}
+
+	prvdata->len = 0;
+	prvdata->nomasters = pdata->nomasters;
+	prvdata->master_names = pdata->masters;
+	prvdata->platform_data = pdata;
+	return 0;
+}
+
+static const struct file_operations msm_rpm_master_stats_fops = {
+	.owner	  = THIS_MODULE,
+	.open	  = msm_rpm_master_stats_file_open,
+	.read	  = msm_rpm_master_stats_file_read,
+	.release  = msm_rpm_master_stats_file_close,
+	.llseek   = no_llseek,
+};
+
+static  int __devinit msm_rpm_master_stats_probe(struct platform_device *pdev)
+{
+	struct dentry *dent;
+	struct msm_rpm_master_stats_platform_data *pdata;
+	struct resource *res;
+
+	pdata = pdev->dev.platform_data;
+	if (!pdata)
+		return -EINVAL;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	pdata->phys_addr_base = res->start;
+	pdata->phys_size = resource_size(res);
+
+	dent = debugfs_create_file("rpm_master_stats", S_IRUGO, NULL,
+			pdev->dev.platform_data, &msm_rpm_master_stats_fops);
+
+	if (!dent) {
+		pr_err("%s: ERROR debugfs_create_file failed\n", __func__);
+		return -ENOMEM;
+	}
+	platform_set_drvdata(pdev, dent);
+	return 0;
+}
+
+static int __devexit msm_rpm_master_stats_remove(struct platform_device *pdev)
+{
+	struct dentry *dent;
+
+	dent = platform_get_drvdata(pdev);
+	debugfs_remove(dent);
+	platform_set_drvdata(pdev, NULL);
+	return 0;
+}
+
+static struct platform_driver msm_rpm_master_stats_driver = {
+	.probe	= msm_rpm_master_stats_probe,
+	.remove = __devexit_p(msm_rpm_master_stats_remove),
+	.driver = {
+		.name = "msm_rpm_master_stat",
+		.owner = THIS_MODULE,
+	},
+};
+
+static int __init msm_rpm_master_stats_init(void)
+{
+	return platform_driver_register(&msm_rpm_master_stats_driver);
+}
+
+static void __exit msm_rpm_master_stats_exit(void)
+{
+	platform_driver_unregister(&msm_rpm_master_stats_driver);
+}
+
+module_init(msm_rpm_master_stats_init);
+module_exit(msm_rpm_master_stats_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("MSM RPM Master Statistics driver");
+MODULE_VERSION("1.0");
+MODULE_ALIAS("platform:msm_master_stat_log");
diff --git a/arch/arm/mach-msm/rpm_resources.c b/arch/arm/mach-msm/rpm_resources.c
index dfed3aa..43073d3 100644
--- a/arch/arm/mach-msm/rpm_resources.c
+++ b/arch/arm/mach-msm/rpm_resources.c
@@ -1131,9 +1131,8 @@
 
 static int __init msm_rpmrs_l2_init(void)
 {
-	if (cpu_is_msm8960() || cpu_is_msm8930() || cpu_is_msm8930aa() ||
-	    cpu_is_apq8064() || cpu_is_msm8627() || cpu_is_msm8960ab() ||
-						cpu_is_apq8064ab()) {
+	if (soc_class_is_msm8960() || soc_class_is_msm8930() ||
+	    soc_class_is_apq8064()) {
 
 		msm_pm_set_l2_flush_flag(0);
 
diff --git a/arch/arm/mach-msm/rpm_stats.c b/arch/arm/mach-msm/rpm_stats.c
index a831bd5..032633c 100644
--- a/arch/arm/mach-msm/rpm_stats.c
+++ b/arch/arm/mach-msm/rpm_stats.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -22,6 +22,7 @@
 #include <linux/slab.h>
 #include <linux/types.h>
 #include <linux/mm.h>
+#include <linux/types.h>
 #include <asm/uaccess.h>
 
 #include <mach/msm_iomap.h>
@@ -44,7 +45,6 @@
 	uint32_t	id;
 	uint32_t	val;
 };
-
 struct msm_rpmstats_private_data{
 	void __iomem *reg_base;
 	u32 num_records;
@@ -53,7 +53,6 @@
 	char buf[128];
 	struct msm_rpmstats_platform_data *platform_data;
 };
-
 static inline unsigned long  msm_rpmstats_read_register(void __iomem *regbase,
 		int index, int offset)
 {
@@ -120,7 +119,6 @@
 			msm_rpmstats_id_labels[record.id],
 			usec);
 }
-
 static int msm_rpmstats_file_read(struct file *file, char __user *bufu,
 				  size_t count, loff_t *ppos)
 {
@@ -133,13 +131,16 @@
 	if (!bufu || count < 0)
 		return -EINVAL;
 
-	if (!prvdata->num_records)
-		prvdata->num_records = readl_relaxed(prvdata->reg_base);
+	if (prvdata->platform_data->version == 1) {
+		if (!prvdata->num_records)
+			prvdata->num_records = readl_relaxed(prvdata->reg_base);
+	}
 
 	if ((*ppos >= prvdata->len)
-			&& (prvdata->read_idx < prvdata->num_records)) {
-		prvdata->len = msm_rpmstats_copy_stats(prvdata);
-		*ppos = 0;
+		&& (prvdata->read_idx < prvdata->num_records)) {
+			if (prvdata->platform_data->version == 1)
+				prvdata->len = msm_rpmstats_copy_stats(prvdata);
+			*ppos = 0;
 	}
 
 	return simple_read_from_buffer(bufu, count, ppos,
@@ -198,15 +199,37 @@
 {
 	struct dentry *dent;
 	struct msm_rpmstats_platform_data *pdata;
+	struct msm_rpmstats_platform_data *pd;
+	struct resource *res = NULL;
 
-	pdata = pdev->dev.platform_data;
-	if (!pdata)
+	if (!pdev)
 		return -EINVAL;
+
+	pdata = kzalloc(sizeof(struct msm_rpmstats_platform_data), GFP_KERNEL);
+
+	if (!pdata)
+		return -ENOMEM;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+	if (!res)
+		return -EINVAL;
+
+	pdata->phys_addr_base  = res->start;
+
+	pdata->phys_size = resource_size(res);
+
+	if (pdev->dev.platform_data) {
+		pd = pdev->dev.platform_data;
+		pdata->version = pd->version ;
+	}
+
 	dent = debugfs_create_file("rpm_stats", S_IRUGO, NULL,
-			pdev->dev.platform_data, &msm_rpmstats_fops);
+			pdata, &msm_rpmstats_fops);
 
 	if (!dent) {
 		pr_err("%s: ERROR debugfs_create_file failed\n", __func__);
+		kfree(pdata);
 		return -ENOMEM;
 	}
 	platform_set_drvdata(pdev, dent);
diff --git a/arch/arm/mach-msm/rpm_stats.h b/arch/arm/mach-msm/rpm_stats.h
index 918d4fb..c1dfe34 100644
--- a/arch/arm/mach-msm/rpm_stats.h
+++ b/arch/arm/mach-msm/rpm_stats.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -19,5 +19,21 @@
 struct msm_rpmstats_platform_data {
 	phys_addr_t phys_addr_base;
 	u32 phys_size;
+	u32 version;
+};
+
+struct msm_rpm_master_stats_platform_data {
+	phys_addr_t phys_addr_base;
+	u32 phys_size;
+	char **masters;
+	/*
+	 * RPM maintains PC stats for each master in MSG RAM,
+	 * it allocates 256 bytes for this use.
+	 * No of masters differs for different targets.
+	 * Based on the number of masters, linux rpm stat
+	 * driver reads (32 * nomasters) bytes to display
+	 * master stats.
+	 */
+	 u32 nomasters;
 };
 #endif
diff --git a/arch/arm/mach-msm/scm-pas.c b/arch/arm/mach-msm/scm-pas.c
index 43436e5..e248917 100644
--- a/arch/arm/mach-msm/scm-pas.c
+++ b/arch/arm/mach-msm/scm-pas.c
@@ -24,6 +24,7 @@
 #include "scm-pas.h"
 
 #define PAS_INIT_IMAGE_CMD	1
+#define PAS_MEM_SETUP_CMD	2
 #define PAS_AUTH_AND_RESET_CMD	5
 #define PAS_SHUTDOWN_CMD	6
 #define PAS_IS_SUPPORTED_CMD	7
@@ -55,6 +56,28 @@
 }
 EXPORT_SYMBOL(pas_init_image);
 
+int pas_mem_setup(enum pas_id id, u32 start_addr, u32 len)
+{
+	int ret;
+	struct pas_init_image_req {
+		u32	proc;
+		u32	start_addr;
+		u32	len;
+	} request;
+	u32 scm_ret = 0;
+
+	request.proc = id;
+	request.start_addr = start_addr;
+	request.len = len;
+
+	ret = scm_call(SCM_SVC_PIL, PAS_MEM_SETUP_CMD, &request,
+			sizeof(request), &scm_ret, sizeof(scm_ret));
+	if (ret)
+		return ret;
+	return scm_ret;
+}
+EXPORT_SYMBOL(pas_mem_setup);
+
 static struct msm_bus_paths scm_pas_bw_tbl[] = {
 	{
 		.vectors = (struct msm_bus_vectors[]){
@@ -204,11 +227,7 @@
 		}
 	}
 
-	/* TODO : Remove once bus scaling driver is in place */
-	if (!cpu_is_msm8226())
-		scm_perf_client = msm_bus_scale_register_client(
-				&scm_pas_bus_pdata);
-
+	scm_perf_client = msm_bus_scale_register_client(&scm_pas_bus_pdata);
 	if (!scm_perf_client)
 		pr_warn("unable to register bus client\n");
 
diff --git a/arch/arm/mach-msm/scm-pas.h b/arch/arm/mach-msm/scm-pas.h
index 8da1d75..6441a18 100644
--- a/arch/arm/mach-msm/scm-pas.h
+++ b/arch/arm/mach-msm/scm-pas.h
@@ -27,6 +27,7 @@
 
 #ifdef CONFIG_MSM_PIL
 extern int pas_init_image(enum pas_id id, const u8 *metadata, size_t size);
+extern int pas_mem_setup(enum pas_id id, u32 start_addr, u32 len);
 extern int pas_auth_and_reset(enum pas_id id);
 extern int pas_shutdown(enum pas_id id);
 extern int pas_supported(enum pas_id id);
@@ -36,6 +37,10 @@
 {
 	return 0;
 }
+static inline int pas_mem_setup(enum pas_id id, u32 start_addr, u32 len)
+{
+	return 0;
+}
 static inline int pas_auth_and_reset(enum pas_id id)
 {
 	return 0;
diff --git a/arch/arm/mach-msm/scm.c b/arch/arm/mach-msm/scm.c
index 6052918..6013efc 100644
--- a/arch/arm/mach-msm/scm.c
+++ b/arch/arm/mach-msm/scm.c
@@ -196,6 +196,7 @@
 	 * side in the buffer.
 	 */
 	flush_cache_all();
+	outer_flush_all();
 	ret = smc(cmd_addr);
 	if (ret < 0)
 		ret = scm_remap_error(ret);
@@ -209,6 +210,7 @@
 {
 	start = round_down(start, cacheline_size);
 	end = round_up(end, cacheline_size);
+	outer_inv_range(start, end);
 	while (start < end) {
 		asm ("mcr p15, 0, %0, c7, c6, 1" : : "r" (start)
 		     : "memory");
diff --git a/arch/arm/mach-msm/smd.c b/arch/arm/mach-msm/smd.c
index c1e2421..3c0cbf7 100644
--- a/arch/arm/mach-msm/smd.c
+++ b/arch/arm/mach-msm/smd.c
@@ -706,7 +706,7 @@
  */
 static struct edge_to_pid edge_to_pids[] = {
 	[SMD_APPS_MODEM] = {SMD_APPS, SMD_MODEM, "modem"},
-	[SMD_APPS_QDSP] = {SMD_APPS, SMD_Q6, "q6"},
+	[SMD_APPS_QDSP] = {SMD_APPS, SMD_Q6, "adsp"},
 	[SMD_MODEM_QDSP] = {SMD_MODEM, SMD_Q6},
 	[SMD_APPS_DSPS] = {SMD_APPS, SMD_DSPS, "dsps"},
 	[SMD_MODEM_DSPS] = {SMD_MODEM, SMD_DSPS},
@@ -871,6 +871,7 @@
 /*
  * Returns a pointer to the subsystem name given the
  * remote processor ID.
+ * subsystem is not necessarily PIL-loadable
  *
  * @pid     Remote processor ID
  * @returns Pointer to subsystem name or NULL if not found
@@ -881,11 +882,14 @@
 	int i;
 
 	for (i = 0; i < ARRAY_SIZE(edge_to_pids); ++i) {
-		if (pid == edge_to_pids[i].remote_pid &&
-			edge_to_pids[i].subsys_name[0] != 0x0
-			) {
-			subsys = edge_to_pids[i].subsys_name;
-			break;
+		if (pid == edge_to_pids[i].remote_pid) {
+			if (edge_to_pids[i].subsys_name[0] != 0x0) {
+				subsys = edge_to_pids[i].subsys_name;
+				break;
+			} else if (pid == SMD_RPM) {
+				subsys = "rpm";
+				break;
+			}
 		}
 	}
 
@@ -3191,6 +3195,7 @@
 			flags, "smd_dev", 0);
 	if (r < 0)
 		return r;
+	interrupt_stats[SMD_MODEM].smd_interrupt_id = INT_A9_M2A_0;
 	r = enable_irq_wake(INT_A9_M2A_0);
 	if (r < 0)
 		pr_err("smd_core_init: "
@@ -3202,6 +3207,7 @@
 		free_irq(INT_A9_M2A_0, 0);
 		return r;
 	}
+	interrupt_stats[SMD_MODEM].smsm_interrupt_id = INT_A9_M2A_5;
 	r = enable_irq_wake(INT_A9_M2A_5);
 	if (r < 0)
 		pr_err("smd_core_init: "
@@ -3219,6 +3225,7 @@
 		return r;
 	}
 
+	interrupt_stats[SMD_Q6].smd_interrupt_id = INT_ADSP_A11;
 	r = request_irq(INT_ADSP_A11_SMSM, smsm_dsp_irq_handler,
 			flags, "smsm_dev", smsm_dsp_irq_handler);
 	if (r < 0) {
@@ -3228,6 +3235,7 @@
 		return r;
 	}
 
+	interrupt_stats[SMD_Q6].smsm_interrupt_id = INT_ADSP_A11_SMSM;
 	r = enable_irq_wake(INT_ADSP_A11);
 	if (r < 0)
 		pr_err("smd_core_init: "
@@ -3253,6 +3261,7 @@
 		return r;
 	}
 
+	interrupt_stats[SMD_DSPS].smd_interrupt_id = INT_DSPS_A11;
 	r = enable_irq_wake(INT_DSPS_A11);
 	if (r < 0)
 		pr_err("smd_core_init: "
@@ -3271,6 +3280,7 @@
 		return r;
 	}
 
+	interrupt_stats[SMD_WCNSS].smd_interrupt_id = INT_WCNSS_A11;
 	r = enable_irq_wake(INT_WCNSS_A11);
 	if (r < 0)
 		pr_err("smd_core_init: "
@@ -3288,6 +3298,7 @@
 		return r;
 	}
 
+	interrupt_stats[SMD_WCNSS].smsm_interrupt_id = INT_WCNSS_A11_SMSM;
 	r = enable_irq_wake(INT_WCNSS_A11_SMSM);
 	if (r < 0)
 		pr_err("smd_core_init: "
@@ -3308,6 +3319,7 @@
 		return r;
 	}
 
+	interrupt_stats[SMD_DSPS].smsm_interrupt_id = INT_DSPS_A11_SMSM;
 	r = enable_irq_wake(INT_DSPS_A11_SMSM);
 	if (r < 0)
 		pr_err("smd_core_init: "
@@ -3437,6 +3449,8 @@
 			goto intr_failed;
 		}
 
+		interrupt_stats[cfg->irq_config_id].smd_interrupt_id
+						 = cfg->smd_int.irq_id;
 		/* only init smsm structs if this edge supports smsm */
 		if (cfg->smsm_int.irq_id)
 			ret = intr_init(
@@ -3452,6 +3466,9 @@
 			goto intr_failed;
 		}
 
+		if (cfg->smsm_int.irq_id)
+			interrupt_stats[cfg->irq_config_id].smsm_interrupt_id
+						 = cfg->smsm_int.irq_id;
 		if (cfg->subsys_name)
 			strlcpy(edge_to_pids[cfg->edge].subsys_name,
 				cfg->subsys_name, SMD_MAX_CH_NAME_LEN);
diff --git a/arch/arm/mach-msm/smd_debug.c b/arch/arm/mach-msm/smd_debug.c
index d64bcf2..cc82a01 100644
--- a/arch/arm/mach-msm/smd_debug.c
+++ b/arch/arm/mach-msm/smd_debug.c
@@ -103,22 +103,24 @@
 	const char *subsys_name;
 
 	i += scnprintf(buf + i, max - i,
-		"   Subsystem    |     In    | Out (Hardcoded) |"
+		"   Subsystem    | Interrupt ID |     In    | Out (Hardcoded) |"
 		" Out (Configured) |\n");
 
 	for (subsys = 0; subsys < NUM_SMD_SUBSYSTEMS; ++subsys) {
 		subsys_name = smd_pid_to_subsystem(subsys);
 		if (subsys_name) {
 			i += scnprintf(buf + i, max - i,
-				"%-10s %4s | %9u |       %9u |        %9u |\n",
+				"%-10s %4s |    %9d | %9u |       %9u |        %9u |\n",
 				smd_pid_to_subsystem(subsys), "smd",
+				stats->smd_interrupt_id,
 				stats->smd_in_count,
 				stats->smd_out_hardcode_count,
 				stats->smd_out_config_count);
 
 			i += scnprintf(buf + i, max - i,
-				"%-10s %4s | %9u |       %9u |        %9u |\n",
+				"%-10s %4s |    %9d | %9u |       %9u |        %9u |\n",
 				smd_pid_to_subsystem(subsys), "smsm",
+				stats->smsm_interrupt_id,
 				stats->smsm_in_count,
 				stats->smsm_out_hardcode_count,
 				stats->smsm_out_config_count);
diff --git a/arch/arm/mach-msm/smd_pkt.c b/arch/arm/mach-msm/smd_pkt.c
index 928e59b..73ebdf6 100644
--- a/arch/arm/mach-msm/smd_pkt.c
+++ b/arch/arm/mach-msm/smd_pkt.c
@@ -34,14 +34,14 @@
 #include <linux/wakelock.h>
 
 #include <mach/msm_smd.h>
-#include <mach/peripheral-loader.h>
+#include <mach/subsystem_restart.h>
 #include <mach/msm_ipc_logging.h>
 
 #include "smd_private.h"
 #ifdef CONFIG_ARCH_FSM9XXX
 #define NUM_SMD_PKT_PORTS 4
 #else
-#define NUM_SMD_PKT_PORTS 15
+#define NUM_SMD_PKT_PORTS 24
 #endif
 
 #define PDRIVER_NAME_MAX_SIZE 32
@@ -711,6 +711,15 @@
 	"smdcntl6",
 	"smdcntl7",
 	"smd22",
+	"smdcnt_rev0",
+	"smdcnt_rev1",
+	"smdcnt_rev2",
+	"smdcnt_rev3",
+	"smdcnt_rev4",
+	"smdcnt_rev5",
+	"smdcnt_rev6",
+	"smdcnt_rev7",
+	"smdcnt_rev8",
 	"smd_sns_dsps",
 	"apr_apps2",
 	"smdcntl8",
@@ -729,6 +738,15 @@
 	"DATA13_CNTL",
 	"DATA14_CNTL",
 	"DATA22",
+	"DATA23_CNTL",
+	"DATA24_CNTL",
+	"DATA25_CNTL",
+	"DATA26_CNTL",
+	"DATA27_CNTL",
+	"DATA28_CNTL",
+	"DATA29_CNTL",
+	"DATA30_CNTL",
+	"DATA31_CNTL",
 	"SENSOR",
 	"apr_apps2",
 	"DATA40_CNTL",
@@ -747,6 +765,15 @@
 	SMD_APPS_MODEM,
 	SMD_APPS_MODEM,
 	SMD_APPS_MODEM,
+	SMD_APPS_MODEM,
+	SMD_APPS_MODEM,
+	SMD_APPS_MODEM,
+	SMD_APPS_MODEM,
+	SMD_APPS_MODEM,
+	SMD_APPS_MODEM,
+	SMD_APPS_MODEM,
+	SMD_APPS_MODEM,
+	SMD_APPS_MODEM,
 	SMD_APPS_DSPS,
 	SMD_APPS_QDSP,
 	SMD_APPS_MODEM,
@@ -823,12 +850,11 @@
 		peripheral = smd_edge_to_subsystem(
 				smd_ch_edge[smd_pkt_devp->i]);
 		if (peripheral) {
-			smd_pkt_devp->pil = pil_get(peripheral);
+			smd_pkt_devp->pil = subsystem_get(peripheral);
 			if (IS_ERR(smd_pkt_devp->pil)) {
 				r = PTR_ERR(smd_pkt_devp->pil);
-				pr_err("%s failed on smd_pkt_dev id:%d -"
-				       " pil_get failed for %s\n", __func__,
-					smd_pkt_devp->i, peripheral);
+				pr_err("%s failed on smd_pkt_dev id:%d - subsystem_get failed for %s\n",
+					__func__, smd_pkt_devp->i, peripheral);
 				goto release_pd;
 			}
 
@@ -908,7 +934,7 @@
 	}
 release_pil:
 	if (peripheral && (r < 0))
-		pil_put(smd_pkt_devp->pil);
+		subsystem_put(smd_pkt_devp->pil);
 
 release_pd:
 	if (r < 0) {
@@ -952,7 +978,7 @@
 		platform_driver_unregister(&smd_pkt_devp->driver);
 		smd_pkt_devp->driver.probe = NULL;
 		if (smd_pkt_devp->pil)
-			pil_put(smd_pkt_devp->pil);
+			subsystem_put(smd_pkt_devp->pil);
 		smd_pkt_devp->has_reset = 0;
 		smd_pkt_devp->do_reset_notification = 0;
 		smd_pkt_devp->wakelock_locked = 0;
diff --git a/arch/arm/mach-msm/smd_private.h b/arch/arm/mach-msm/smd_private.h
index 9117280..7f39a24 100644
--- a/arch/arm/mach-msm/smd_private.h
+++ b/arch/arm/mach-msm/smd_private.h
@@ -255,10 +255,12 @@
 	uint32_t smd_in_count;
 	uint32_t smd_out_hardcode_count;
 	uint32_t smd_out_config_count;
+	uint32_t smd_interrupt_id;
 
 	uint32_t smsm_in_count;
 	uint32_t smsm_out_hardcode_count;
 	uint32_t smsm_out_config_count;
+	uint32_t smsm_interrupt_id;
 };
 extern struct interrupt_stat interrupt_stats[NUM_SMD_SUBSYSTEMS];
 
diff --git a/arch/arm/mach-msm/smd_rpcrouter.c b/arch/arm/mach-msm/smd_rpcrouter.c
index 32a2d9f..1bea82a 100644
--- a/arch/arm/mach-msm/smd_rpcrouter.c
+++ b/arch/arm/mach-msm/smd_rpcrouter.c
@@ -1,7 +1,7 @@
 /* arch/arm/mach-msm/smd_rpcrouter.c
  *
  * Copyright (C) 2007 Google, Inc.
- * Copyright (c) 2007-2011, Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2007-2012, The Linux Foundation. All rights reserved.
  * Author: San Mehat <san@android.com>
  *
  * This software is licensed under the terms of the GNU General Public
@@ -40,6 +40,7 @@
 #include <linux/platform_device.h>
 #include <linux/uaccess.h>
 #include <linux/debugfs.h>
+#include <linux/reboot.h>
 
 #include <asm/byteorder.h>
 
@@ -158,6 +159,7 @@
 static void do_read_data(struct work_struct *work);
 static void do_create_pdevs(struct work_struct *work);
 static void do_create_rpcrouter_pdev(struct work_struct *work);
+static int msm_rpcrouter_close(void);
 
 static DECLARE_WORK(work_create_pdevs, do_create_pdevs);
 static DECLARE_WORK(work_create_rpcrouter_pdev, do_create_rpcrouter_pdev);
@@ -213,6 +215,24 @@
 DECLARE_COMPLETION(rpc_remote_router_up);
 static atomic_t pending_close_count = ATOMIC_INIT(0);
 
+static int msm_rpc_reboot_call(struct notifier_block *this,
+			unsigned long code, void *_cmd)
+{
+	 switch (code) {
+	 case SYS_RESTART:
+	 case SYS_HALT:
+	 case SYS_POWER_OFF:
+		msm_rpcrouter_close();
+		break;
+	 }
+	 return NOTIFY_DONE;
+}
+
+static struct notifier_block msm_rpc_reboot_notifier = {
+	.notifier_call = msm_rpc_reboot_call,
+	.priority = 100
+};
+
 /*
  * Search for transport (xprt) that matches the provided PID.
  *
@@ -2128,7 +2148,7 @@
 	return rc;
 }
 
-int msm_rpcrouter_close(void)
+static int msm_rpcrouter_close(void)
 {
 	struct rpcrouter_xprt_info *xprt_info;
 	union rr_control_msg ctl;
@@ -2515,7 +2535,9 @@
 	msm_rpc_connect_timeout_ms = 0;
 	smd_rpcrouter_debug_mask |= SMEM_LOG;
 	debugfs_init();
-
+	ret = register_reboot_notifier(&msm_rpc_reboot_notifier);
+	if (ret)
+		pr_err("%s: Failed to register reboot notifier", __func__);
 
 	/* Initialize what we need to start processing */
 	rpcrouter_workqueue =
diff --git a/arch/arm/mach-msm/smd_rpcrouter.h b/arch/arm/mach-msm/smd_rpcrouter.h
index 1da7579..2ad85a4 100644
--- a/arch/arm/mach-msm/smd_rpcrouter.h
+++ b/arch/arm/mach-msm/smd_rpcrouter.h
@@ -1,7 +1,7 @@
 /** arch/arm/mach-msm/smd_rpcrouter.h
  *
  * Copyright (C) 2007 Google, Inc.
- * Copyright (c) 2007-2011, Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2007-2012, The Linux Foundation. All rights reserved.
  * Author: San Mehat <san@android.com>
  *
  * This software is licensed under the terms of the GNU General Public
@@ -240,7 +240,6 @@
 		   struct rr_fragment **frag,
 		   unsigned len, long timeout);
 
-int msm_rpcrouter_close(void);
 struct msm_rpc_endpoint *msm_rpcrouter_create_local_endpoint(dev_t dev);
 int msm_rpcrouter_destroy_local_endpoint(struct msm_rpc_endpoint *ept);
 
diff --git a/arch/arm/mach-msm/smd_rpcrouter_device.c b/arch/arm/mach-msm/smd_rpcrouter_device.c
index e84d213..7b51beb 100644
--- a/arch/arm/mach-msm/smd_rpcrouter_device.c
+++ b/arch/arm/mach-msm/smd_rpcrouter_device.c
@@ -1,7 +1,7 @@
 /* arch/arm/mach-msm/smd_rpcrouter_device.c
  *
  * Copyright (C) 2007 Google, Inc.
- * Copyright (c) 2007-2011, Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2007-2012, Code Aurora Forum. All rights reserved.
  * Author: San Mehat <san@android.com>
  *
  * This software is licensed under the terms of the GNU General Public
@@ -34,7 +34,7 @@
 #include <asm/uaccess.h>
 #include <asm/byteorder.h>
 
-#include <mach/peripheral-loader.h>
+#include <mach/subsystem_restart.h>
 #include "smd_rpcrouter.h"
 
 /* Support 64KB of data plus some space for headers */
@@ -61,7 +61,7 @@
 static void msm_rpcrouter_unload_modem(void *pil)
 {
 	if (pil)
-		pil_put(pil);
+		subsystem_put(pil);
 }
 
 static void *msm_rpcrouter_load_modem(void)
@@ -69,7 +69,7 @@
 	void *pil;
 	int rc;
 
-	pil = pil_get("modem");
+	pil = subsystem_get("modem");
 	if (IS_ERR(pil))
 		pr_err("%s: modem load failed\n", __func__);
 	else {
diff --git a/arch/arm/mach-msm/smd_tty.c b/arch/arm/mach-msm/smd_tty.c
index 44ef822..881da18 100644
--- a/arch/arm/mach-msm/smd_tty.c
+++ b/arch/arm/mach-msm/smd_tty.c
@@ -30,7 +30,7 @@
 #include <linux/tty_flip.h>
 
 #include <mach/msm_smd.h>
-#include <mach/peripheral-loader.h>
+#include <mach/subsystem_restart.h>
 #include <mach/socinfo.h>
 
 #include "smd_private.h"
@@ -245,7 +245,7 @@
 	if (info->open_count++ == 0) {
 		peripheral = smd_edge_to_subsystem(smd_tty[n].smd->edge);
 		if (peripheral) {
-			info->pil = pil_get(peripheral);
+			info->pil = subsystem_get(peripheral);
 			if (IS_ERR(info->pil)) {
 				res = PTR_ERR(info->pil);
 				goto out;
@@ -325,7 +325,7 @@
 
 release_pil:
 	if (res < 0)
-		pil_put(info->pil);
+		subsystem_put(info->pil);
 	else
 		smd_disable_read_intr(info->ch);
 out:
@@ -357,7 +357,7 @@
 		if (info->ch) {
 			smd_close(info->ch);
 			info->ch = 0;
-			pil_put(info->pil);
+			subsystem_put(info->pil);
 		}
 	}
 	mutex_unlock(&smd_tty_lock);
diff --git a/arch/arm/mach-msm/socinfo.c b/arch/arm/mach-msm/socinfo.c
index ac077e9..6cb9339 100644
--- a/arch/arm/mach-msm/socinfo.c
+++ b/arch/arm/mach-msm/socinfo.c
@@ -35,6 +35,8 @@
 	HW_PLATFORM_LIQUID  = 9,
 	/* Dragonboard platform id is assigned as 10 in CDT */
 	HW_PLATFORM_DRAGON	= 10,
+	HW_PLATFORM_HRD	= 13,
+	HW_PLATFORM_DTV	= 14,
 	HW_PLATFORM_INVALID
 };
 
@@ -47,7 +49,9 @@
 	[HW_PLATFORM_SVLTE_SURF] = "SLVTE_SURF",
 	[HW_PLATFORM_MTP] = "MTP",
 	[HW_PLATFORM_LIQUID] = "Liquid",
-	[HW_PLATFORM_DRAGON] = "Dragon"
+	[HW_PLATFORM_DRAGON] = "Dragon",
+	[HW_PLATFORM_HRD] = "HRD",
+	[HW_PLATFORM_DTV] = "DTV",
 };
 
 enum {
@@ -256,6 +260,7 @@
 	[128] = MSM_CPU_8625,
 	[129] = MSM_CPU_8625,
 	[137] = MSM_CPU_8625,
+	[167] = MSM_CPU_8625,
 
 	/* 8064 MPQ ID */
 	[130] = MSM_CPU_8064,
@@ -268,6 +273,7 @@
 
 	/* 9625 IDs */
 	[134] = MSM_CPU_9625,
+	[152] = MSM_CPU_9625,
 
 	/* 8960AB IDs */
 	[138] = MSM_CPU_8960AB,
@@ -279,6 +285,7 @@
 	[142] = MSM_CPU_8930AA,
 	[143] = MSM_CPU_8930AA,
 	[144] = MSM_CPU_8930AA,
+	[160] = MSM_CPU_8930AA,
 
 	/* 8226 IDs */
 	[145] = MSM_CPU_8226,
@@ -286,9 +293,18 @@
 	/* 8092 IDs */
 	[146] = MSM_CPU_8092,
 
+	/* 8910 IDs */
+	[147] = MSM_CPU_8910,
+
 	/* 8064AB IDs */
 	[153] = MSM_CPU_8064AB,
 
+	/* 8930AB IDs */
+	[154] = MSM_CPU_8930AB,
+	[155] = MSM_CPU_8930AB,
+	[156] = MSM_CPU_8930AB,
+	[157] = MSM_CPU_8930AB
+
 	/* Uninitialized IDs are not known to run Linux.
 	   MSM_CPU_UNKNOWN is set to 0 to ensure these IDs are
 	   considered as unknown CPU. */
@@ -732,6 +748,10 @@
 		dummy_socinfo.id = 146;
 		strlcpy(dummy_socinfo.build_id, "mpq8092 - ",
 		sizeof(dummy_socinfo.build_id));
+	} else if (early_machine_is_msm8910()) {
+		dummy_socinfo.id = 147;
+		strlcpy(dummy_socinfo.build_id, "msm8910 - ",
+			sizeof(dummy_socinfo.build_id));
 	}
 	strlcat(dummy_socinfo.build_id, "Dummy socinfo",
 		sizeof(dummy_socinfo.build_id));
diff --git a/arch/arm/mach-msm/spm-v2.c b/arch/arm/mach-msm/spm-v2.c
index f0d3d06..620aa1d 100644
--- a/arch/arm/mach-msm/spm-v2.c
+++ b/arch/arm/mach-msm/spm-v2.c
@@ -84,10 +84,6 @@
 	[MSM_SPM_REG_SAW2_VERSION]		= 0xFD0,
 };
 
-/******************************************************************************
- * Internal helper functions
- *****************************************************************************/
-
 static inline uint32_t msm_spm_drv_get_num_spm_entry(
 		struct msm_spm_driver_data *dev)
 {
@@ -150,6 +146,12 @@
 {
 	unsigned int pmic_data = 0;
 
+	/**
+	 * VCTL_PORT has to be 0, for PMIC_STS register to be updated.
+	 * Ensure that vctl_port is always set to 0.
+	 */
+	WARN_ON(dev->vctl_port);
+
 	pmic_data |= vlevel;
 	pmic_data |= (dev->vctl_port & 0x7) << 16;
 
@@ -213,10 +215,6 @@
 	return ret;
 }
 
-/******************************************************************************
- * Public functions
- *****************************************************************************/
-
 inline int msm_spm_drv_set_spm_enable(
 		struct msm_spm_driver_data *dev, bool enable)
 {
@@ -311,6 +309,15 @@
 }
 
 #ifdef CONFIG_MSM_AVS_HW
+static bool msm_spm_drv_is_avs_enabled(struct msm_spm_driver_data *dev)
+{
+	msm_spm_drv_load_shadow(dev, MSM_SPM_REG_SAW2_AVS_CTL);
+	if (dev->major == SAW2_MAJOR_2)
+		return dev->reg_shadow[MSM_SPM_REG_SAW2_AVS_CTL] & BIT(0);
+	else
+		return dev->reg_shadow[MSM_SPM_REG_SAW2_AVS_CTL] & BIT(27);
+}
+
 static void msm_spm_drv_disable_avs(struct msm_spm_driver_data *dev)
 {
 	msm_spm_drv_load_shadow(dev, MSM_SPM_REG_SAW2_AVS_CTL);
@@ -335,6 +342,10 @@
 }
 
 #else
+static bool msm_spm_drv_is_avs_enabled(struct msm_spm_driver_data *dev)
+{
+	return false;
+}
 
 static void msm_spm_drv_disable_avs(struct msm_spm_driver_data *dev) { }
 
@@ -347,6 +358,7 @@
 int msm_spm_drv_set_vdd(struct msm_spm_driver_data *dev, unsigned int vlevel)
 {
 	uint32_t timeout_us, new_level;
+	bool avs_enabled = msm_spm_drv_is_avs_enabled(dev);
 
 	if (!dev)
 		return -EINVAL;
@@ -357,7 +369,8 @@
 	if (msm_spm_debug_mask & MSM_SPM_DEBUG_VCTL)
 		pr_info("%s: requesting vlevel %#x\n", __func__, vlevel);
 
-	msm_spm_drv_disable_avs(dev);
+	if (avs_enabled)
+		msm_spm_drv_disable_avs(dev);
 
 	/* Kick the state machine back to idle */
 	dev->reg_shadow[MSM_SPM_REG_SAW2_RST] = 1;
@@ -381,69 +394,96 @@
 		goto set_vdd_bail;
 	}
 
-	/* Set AVS min/max */
-	msm_spm_drv_set_avs_vlevel(dev, vlevel);
-
 	if (msm_spm_debug_mask & MSM_SPM_DEBUG_VCTL)
-		pr_info("%s: done, remaining timeout %uus\n",
+		pr_info("%s: done, remaining timeout %u us\n",
 			__func__, timeout_us);
 
-	msm_spm_drv_enable_avs(dev);
+	/* Set AVS min/max */
+	if (avs_enabled) {
+		msm_spm_drv_set_avs_vlevel(dev, vlevel);
+		msm_spm_drv_enable_avs(dev);
+	}
+
 	return 0;
 
 set_vdd_bail:
-	msm_spm_drv_enable_avs(dev);
+	if (avs_enabled)
+		msm_spm_drv_enable_avs(dev);
+
 	pr_err("%s: failed %#x, remaining timeout %uus, vlevel %#x\n",
 		__func__, vlevel, timeout_us, new_level);
 	return -EIO;
 }
 
-int msm_spm_drv_set_phase(struct msm_spm_driver_data *dev,
-		unsigned int phase_cnt)
+static int msm_spm_drv_get_pmic_port(struct msm_spm_driver_data *dev,
+		enum msm_spm_pmic_port port)
+{
+	int index = -1;
+
+	switch (port) {
+	case MSM_SPM_PMIC_VCTL_PORT:
+		index = dev->vctl_port;
+		break;
+	case MSM_SPM_PMIC_PHASE_PORT:
+		index = dev->phase_port;
+		break;
+	case MSM_SPM_PMIC_PFM_PORT:
+		index = dev->pfm_port;
+		break;
+	default:
+		break;
+	}
+
+	return index;
+}
+
+int msm_spm_drv_set_pmic_data(struct msm_spm_driver_data *dev,
+		enum msm_spm_pmic_port port, unsigned int data)
 {
 	unsigned int pmic_data = 0;
 	unsigned int timeout_us = 0;
+	int index = 0;
 
 	if (dev->major != SAW2_MAJOR_2)
 		return -ENODEV;
 
-	pmic_data |= phase_cnt & 0xFF;
-	pmic_data |= (dev->phase_port & 0x7) << 16;
+	if (!msm_spm_pmic_arb_present(dev))
+		return -ENOSYS;
+
+	index = msm_spm_drv_get_pmic_port(dev, port);
+	if (index < 0)
+		return -ENODEV;
+
+	pmic_data |= data & 0xFF;
+	pmic_data |= (index & 0x7) << 16;
 
 	dev->reg_shadow[MSM_SPM_REG_SAW2_VCTL] &= ~0x700FF;
 	dev->reg_shadow[MSM_SPM_REG_SAW2_VCTL] |= pmic_data;
 	msm_spm_drv_flush_shadow(dev, MSM_SPM_REG_SAW2_VCTL);
 	mb();
 
-	/* Wait for PMIC state to return to idle or until timeout */
 	timeout_us = dev->vctl_timeout_us;
-	while (msm_spm_drv_get_sts_pmic_state(dev) != MSM_SPM_PMIC_STATE_IDLE) {
-		if (!timeout_us)
-			goto set_phase_bail;
+	/**
+	 * Confirm the pmic data set was what hardware sent by
+	 * checking the PMIC FSM state.
+	 * We cannot use the sts_pmic_data and check it against
+	 * the value like we do fot set_vdd, since the PMIC_STS
+	 * is only updated for SAW_VCTL sent with port index 0.
+	 */
+	do {
+		if (msm_spm_drv_get_sts_pmic_state(dev) ==
+				MSM_SPM_PMIC_STATE_IDLE)
+			break;
+		udelay(1);
+	} while (--timeout_us);
 
-		if (timeout_us > 10) {
-			udelay(10);
-			timeout_us -= 10;
-		} else {
-			udelay(timeout_us);
-			timeout_us = 0;
-		}
+	if (!timeout_us) {
+		pr_err("%s: failed, remaining timeout %u us, data %d\n",
+				__func__, timeout_us, data);
+		return -EIO;
 	}
 
-	if (msm_spm_drv_get_sts_curr_pmic_data(dev) != phase_cnt)
-		goto set_phase_bail;
-
-	if (msm_spm_debug_mask & MSM_SPM_DEBUG_VCTL)
-		pr_info("%s: done, remaining timeout %uus\n",
-			__func__, timeout_us);
-
 	return 0;
-
-set_phase_bail:
-	pr_err("%s: failed, remaining timeout %uus, phase count %d\n",
-	       __func__, timeout_us, msm_spm_drv_get_sts_curr_pmic_data(dev));
-	return -EIO;
-
 }
 
 void msm_spm_drv_reinit(struct msm_spm_driver_data *dev)
@@ -472,6 +512,7 @@
 
 	dev->vctl_port = data->vctl_port;
 	dev->phase_port = data->phase_port;
+	dev->pfm_port = data->pfm_port;
 	dev->reg_base_addr = data->reg_base_addr;
 	memcpy(dev->reg_shadow, data->reg_init_values,
 			sizeof(data->reg_init_values));
diff --git a/arch/arm/mach-msm/spm.c b/arch/arm/mach-msm/spm.c
index 3d90678..ea0b56c 100644
--- a/arch/arm/mach-msm/spm.c
+++ b/arch/arm/mach-msm/spm.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2010-2012, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -132,6 +132,11 @@
 /******************************************************************************
  * Public functions
  *****************************************************************************/
+/**
+ * msm_spm_set_low_power_mode() - Configure SPM start address for low power mode
+ * @mode: SPM LPM mode to enter
+ * @notify_rpm: Notify RPM in this mode
+ */
 int msm_spm_set_low_power_mode(unsigned int mode, bool notify_rpm)
 {
 	struct msm_spm_device *dev = &__get_cpu_var(msm_spm_devices);
@@ -185,6 +190,11 @@
 	return 0;
 }
 
+/**
+ * msm_spm_set_vdd(): Set core voltage
+ * @cpu: core id
+ * @vlevel: Encoded PMIC data.
+ */
 int msm_spm_set_vdd(unsigned int cpu, unsigned int vlevel)
 {
 	struct msm_spm_device *dev;
@@ -235,6 +245,17 @@
 	return -EIO;
 }
 
+/**
+ * msm_spm_get_vdd(): Get core voltage
+ * @cpu: core id
+ * @return: Returns encoded PMIC data.
+ */
+unsigned int msm_spm_get_vdd(unsigned int cpu)
+{
+	struct msm_spm_device *dev = &per_cpu(msm_spm_devices, cpu);
+	return dev->reg_shadow[MSM_SPM_REG_SAW_VCTL];
+}
+
 void msm_spm_reinit(void)
 {
 	struct msm_spm_device *dev = &__get_cpu_var(msm_spm_devices);
@@ -247,6 +268,11 @@
 	mb();
 }
 
+/**
+ * msm_spm_init(): Board initalization function
+ * @data: platform specific SPM register configuration data
+ * @nr_devs: Number of SPM devices being initialized
+ */
 int __init msm_spm_init(struct msm_spm_platform_data *data, int nr_devs)
 {
 	unsigned int cpu;
diff --git a/arch/arm/mach-msm/spm.h b/arch/arm/mach-msm/spm.h
index 09ee26a..a353ce0 100644
--- a/arch/arm/mach-msm/spm.h
+++ b/arch/arm/mach-msm/spm.h
@@ -112,6 +112,7 @@
 	uint32_t ver_reg;
 	uint32_t vctl_port;
 	uint32_t phase_port;
+	uint32_t pfm_port;
 
 	uint8_t awake_vlevel;
 	uint32_t vctl_timeout_us;
@@ -126,87 +127,29 @@
 
 /* Public functions */
 
-/**
- * msm_spm_set_low_power_mode() - Configure SPM start address for low power mode
- * @mode: SPM LPM mode to enter
- * @notify_rpm: Notify RPM in this mode
- */
 int msm_spm_set_low_power_mode(unsigned int mode, bool notify_rpm);
-
-/**
- * msm_spm_set_vdd(): Set core voltage
- * @cpu: core id
- * @vlevel: Encoded PMIC data.
- */
 int msm_spm_set_vdd(unsigned int cpu, unsigned int vlevel);
-
-/**
- * msm_spm_get_vdd(): Get core voltage
- * @cpu: core id
- * @return: Returns encoded PMIC data.
- */
 unsigned int msm_spm_get_vdd(unsigned int cpu);
-
-/**
- * msm_spm_turn_on_cpu_rail(): Power on cpu rail before turning on core
- * @cpu: core id
- */
 int msm_spm_turn_on_cpu_rail(unsigned int cpu);
 
 /* Internal low power management specific functions */
 
-/**
- * msm_spm_reinit(): Reinitialize SPM registers
- */
 void msm_spm_reinit(void);
-
-/**
- * msm_spm_init(): Board initalization function
- * @data: platform specific SPM register configuration data
- * @nr_devs: Number of SPM devices being initialized
- */
 int msm_spm_init(struct msm_spm_platform_data *data, int nr_devs);
-
-/**
- * msm_spm_device_init(): Device tree initialization function
- */
 int msm_spm_device_init(void);
 
 #if defined(CONFIG_MSM_L2_SPM)
 
 /* Public functions */
 
-/**
- * msm_spm_l2_set_low_power_mode(): Configure L2 SPM start address
- *                                  for low power mode
- * @mode: SPM LPM mode to enter
- * @notify_rpm: Notify RPM in this mode
- */
 int msm_spm_l2_set_low_power_mode(unsigned int mode, bool notify_rpm);
-
-/**
- * msm_spm_apcs_set_vdd(): Set Apps processor core sub-system voltage
- * @vlevel: Encoded PMIC data.
- */
 int msm_spm_apcs_set_vdd(unsigned int vlevel);
-
-/**
- * msm_spm_apcs_set_phase(): Set number of SMPS phases.
- * phase_cnt: Number of phases to be set active
- */
 int msm_spm_apcs_set_phase(unsigned int phase_cnt);
+int msm_spm_enable_fts_lpm(uint32_t mode);
 
 /* Internal low power management specific functions */
 
-/**
- * msm_spm_l2_init(): Board initialization function
- * @data: SPM target specific register configuration
- */
 int msm_spm_l2_init(struct msm_spm_platform_data *data);
-
-/**
- * msm_spm_l2_reinit(): Reinitialize L2 SPM registers
- */
 void msm_spm_l2_reinit(void);
 
 #else
@@ -234,6 +177,11 @@
 {
 	return -ENOSYS;
 }
+
+static inline int msm_spm_enable_fts_lpm(uint32_t mode)
+{
+	return -ENOSYS;
+}
 #endif /* defined(CONFIG_MSM_L2_SPM) */
 #else /* defined(CONFIG_MSM_SPM_V1) || defined(CONFIG_MSM_SPM_V2) */
 static inline int msm_spm_set_low_power_mode(unsigned int mode, bool notify_rpm)
diff --git a/arch/arm/mach-msm/spm_devices.c b/arch/arm/mach-msm/spm_devices.c
index 2cbed94..b378d3b 100644
--- a/arch/arm/mach-msm/spm_devices.c
+++ b/arch/arm/mach-msm/spm_devices.c
@@ -48,7 +48,6 @@
 static DEFINE_PER_CPU_SHARED_ALIGNED(struct msm_spm_device, msm_cpu_spm_device);
 
 
-/* Must be called on the same cpu as the one being set to */
 static void msm_spm_smp_set_vdd(void *data)
 {
 	struct msm_spm_device *dev;
@@ -58,6 +57,11 @@
 	info->err = msm_spm_drv_set_vdd(&dev->reg_data, info->vlevel);
 }
 
+/**
+ * msm_spm_set_vdd(): Set core voltage
+ * @cpu: core id
+ * @vlevel: Encoded PMIC data.
+ */
 int msm_spm_set_vdd(unsigned int cpu, unsigned int vlevel)
 {
 	struct msm_spm_vdd_info info;
@@ -66,15 +70,37 @@
 	info.cpu = cpu;
 	info.vlevel = vlevel;
 
-	/* Set to true to block on vdd change */
-	ret = smp_call_function_single(cpu, msm_spm_smp_set_vdd, &info, true);
-	if (!ret)
+	if (cpu_online(cpu)) {
+		/**
+		 * We do not want to set the voltage of another core from
+		 * this core, as its possible that we may race the vdd change
+		 * with the SPM state machine of that core, which could also
+		 * be changing the voltage of that core during power collapse.
+		 * Hence, set the function to be executed on that core and block
+		 * until the vdd change is complete.
+		 */
+		ret = smp_call_function_single(cpu, msm_spm_smp_set_vdd,
+				&info, true);
+		if (!ret)
+			ret = info.err;
+	} else {
+		/**
+		 * Since the core is not online, it is safe to set the vdd
+		 * directly.
+		 */
+		msm_spm_smp_set_vdd(&info);
 		ret = info.err;
+	}
 
 	return ret;
 }
 EXPORT_SYMBOL(msm_spm_set_vdd);
 
+/**
+ * msm_spm_get_vdd(): Get core voltage
+ * @cpu: core id
+ * @return: Returns encoded PMIC data.
+ */
 unsigned int msm_spm_get_vdd(unsigned int cpu)
 {
 	struct msm_spm_device *dev;
@@ -150,6 +176,10 @@
 	return ret;
 }
 
+/**
+ * msm_spm_turn_on_cpu_rail(): Power on cpu rail before turning on core
+ * @cpu: core id
+ */
 int msm_spm_turn_on_cpu_rail(unsigned int cpu)
 {
 	uint32_t val = 0;
@@ -167,9 +197,8 @@
 
 	reg = saw_bases[cpu];
 
-	if (cpu_is_msm8960() || cpu_is_msm8930() || cpu_is_msm8930aa() ||
-	    cpu_is_apq8064() || cpu_is_msm8627() || cpu_is_msm8960ab() ||
-						cpu_is_apq8064ab()) {
+	if (soc_class_is_msm8960() || soc_class_is_msm8930() ||
+	    soc_class_is_apq8064()) {
 		val = 0xA4;
 		reg += 0x14;
 		timeout = 512;
@@ -193,6 +222,11 @@
 }
 EXPORT_SYMBOL(msm_spm_reinit);
 
+/**
+ * msm_spm_set_low_power_mode() - Configure SPM start address for low power mode
+ * @mode: SPM LPM mode to enter
+ * @notify_rpm: Notify RPM in this mode
+ */
 int msm_spm_set_low_power_mode(unsigned int mode, bool notify_rpm)
 {
 	struct msm_spm_device *dev = &__get_cpu_var(msm_cpu_spm_device);
@@ -200,7 +234,11 @@
 }
 EXPORT_SYMBOL(msm_spm_set_low_power_mode);
 
-/* Board file init function */
+/**
+ * msm_spm_init(): Board initalization function
+ * @data: platform specific SPM register configuration data
+ * @nr_devs: Number of SPM devices being initialized
+ */
 int __init msm_spm_init(struct msm_spm_platform_data *data, int nr_devs)
 {
 	unsigned int cpu;
@@ -223,6 +261,12 @@
 
 #ifdef CONFIG_MSM_L2_SPM
 
+/**
+ * msm_spm_l2_set_low_power_mode(): Configure L2 SPM start address
+ *                                  for low power mode
+ * @mode: SPM LPM mode to enter
+ * @notify_rpm: Notify RPM in this mode
+ */
 int msm_spm_l2_set_low_power_mode(unsigned int mode, bool notify_rpm)
 {
 	return msm_spm_dev_set_low_power_mode(
@@ -236,19 +280,42 @@
 }
 EXPORT_SYMBOL(msm_spm_l2_reinit);
 
+/**
+ * msm_spm_apcs_set_vdd(): Set Apps processor core sub-system voltage
+ * @vlevel: Encoded PMIC data.
+ */
 int msm_spm_apcs_set_vdd(unsigned int vlevel)
 {
 	return msm_spm_drv_set_vdd(&msm_spm_l2_device.reg_data, vlevel);
 }
 EXPORT_SYMBOL(msm_spm_apcs_set_vdd);
 
+/**
+ * msm_spm_apcs_set_phase(): Set number of SMPS phases.
+ * phase_cnt: Number of phases to be set active
+ */
 int msm_spm_apcs_set_phase(unsigned int phase_cnt)
 {
-	return msm_spm_drv_set_phase(&msm_spm_l2_device.reg_data, phase_cnt);
+	return msm_spm_drv_set_pmic_data(&msm_spm_l2_device.reg_data,
+			MSM_SPM_PMIC_PHASE_PORT, phase_cnt);
 }
 EXPORT_SYMBOL(msm_spm_apcs_set_phase);
 
-/* Board file init function */
+/** msm_spm_enable_fts_lpm() : Enable FTS to switch to low power
+ *                             when the cores are in low power modes
+ * @mode: The mode configuration for FTS
+ */
+int msm_spm_enable_fts_lpm(uint32_t mode)
+{
+	return msm_spm_drv_set_pmic_data(&msm_spm_l2_device.reg_data,
+			MSM_SPM_PMIC_PFM_PORT, mode);
+}
+EXPORT_SYMBOL(msm_spm_enable_fts_lpm);
+
+/**
+ * msm_spm_l2_init(): Board initialization function
+ * @data: SPM target specific register configuration
+ */
 int __init msm_spm_l2_init(struct msm_spm_platform_data *data)
 {
 	return msm_spm_dev_init(&msm_spm_l2_device, data);
@@ -345,25 +412,6 @@
 	if (!ret)
 		spm_data.vctl_timeout_us = val;
 
-	/* optional */
-	key = "qcom,vctl-port";
-	ret = of_property_read_u32(node, key, &val);
-	if (!ret)
-		spm_data.vctl_port = val;
-
-	/* optional */
-	key = "qcom,phase-port";
-	ret = of_property_read_u32(node, key, &val);
-	if (!ret)
-		spm_data.phase_port = val;
-
-	for (i = 0; i < ARRAY_SIZE(spm_of_data); i++) {
-		ret = of_property_read_u32(node, spm_of_data[i].key, &val);
-		if (ret)
-			continue;
-		spm_data.reg_init_values[spm_of_data[i].id] = val;
-	}
-
 	/*
 	 * Device with id 0..NR_CPUS are SPM for apps cores
 	 * Device with id 0xFFFF is for L2 SPM.
@@ -379,6 +427,35 @@
 		dev = &msm_spm_l2_device;
 	}
 
+	spm_data.vctl_port = -1;
+	spm_data.phase_port = -1;
+	spm_data.pfm_port = -1;
+
+	/* optional */
+	if (dev == &msm_spm_l2_device) {
+		key = "qcom,vctl-port";
+		ret = of_property_read_u32(node, key, &val);
+		if (!ret)
+			spm_data.vctl_port = val;
+
+		key = "qcom,phase-port";
+		ret = of_property_read_u32(node, key, &val);
+		if (!ret)
+			spm_data.phase_port = val;
+
+		key = "qcom,pfm-port";
+		ret = of_property_read_u32(node, key, &val);
+		if (!ret)
+			spm_data.pfm_port = val;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(spm_of_data); i++) {
+		ret = of_property_read_u32(node, spm_of_data[i].key, &val);
+		if (ret)
+			continue;
+		spm_data.reg_init_values[spm_of_data[i].id] = val;
+	}
+
 	for (i = 0; i < num_modes; i++) {
 		key = mode_of_data[i].key;
 		modes[mode_count].cmd =
@@ -420,6 +497,9 @@
 	},
 };
 
+/**
+ * msm_spm_device_init(): Device tree initialization function
+ */
 int __init msm_spm_device_init(void)
 {
 	return platform_driver_register(&msm_spm_device_driver);
diff --git a/arch/arm/mach-msm/spm_driver.h b/arch/arm/mach-msm/spm_driver.h
index 4cdfd33..1beaffb 100644
--- a/arch/arm/mach-msm/spm_driver.h
+++ b/arch/arm/mach-msm/spm_driver.h
@@ -14,12 +14,19 @@
 
 #include "spm.h"
 
+enum msm_spm_pmic_port {
+	MSM_SPM_PMIC_VCTL_PORT,
+	MSM_SPM_PMIC_PHASE_PORT,
+	MSM_SPM_PMIC_PFM_PORT,
+};
+
 struct msm_spm_driver_data {
 	uint32_t major;
 	uint32_t minor;
 	uint32_t ver_reg;
 	uint32_t vctl_port;
 	uint32_t phase_port;
+	uint32_t pfm_port;
 	void __iomem *reg_base_addr;
 	uint32_t vctl_timeout_us;
 	uint32_t avs_timeout_us;
@@ -42,6 +49,6 @@
 void msm_spm_drv_flush_seq_entry(struct msm_spm_driver_data *dev);
 int msm_spm_drv_set_spm_enable(struct msm_spm_driver_data *dev,
 		bool enable);
-int msm_spm_drv_set_phase(struct msm_spm_driver_data *dev,
-		unsigned int phase_cnt);
+int msm_spm_drv_set_pmic_data(struct msm_spm_driver_data *dev,
+		enum msm_spm_pmic_port port, unsigned int data);
 #endif
diff --git a/arch/arm/mach-msm/subsystem_restart.c b/arch/arm/mach-msm/subsystem_restart.c
index bae1ab0..ea9337d 100644
--- a/arch/arm/mach-msm/subsystem_restart.c
+++ b/arch/arm/mach-msm/subsystem_restart.c
@@ -33,19 +33,74 @@
 
 #include <asm/current.h>
 
-#include <mach/peripheral-loader.h>
 #include <mach/socinfo.h>
 #include <mach/subsystem_notif.h>
 #include <mach/subsystem_restart.h>
 
 #include "smd_private.h"
 
+/**
+ * enum p_subsys_state - state of a subsystem (private)
+ * @SUBSYS_NORMAL: subsystem is operating normally
+ * @SUBSYS_CRASHED: subsystem has crashed and hasn't been shutdown
+ * @SUBSYS_RESTARTING: subsystem has been shutdown and is now restarting
+ *
+ * The 'private' side of the subsytem state used to determine where in the
+ * restart process the subsystem is.
+ */
+enum p_subsys_state {
+	SUBSYS_NORMAL,
+	SUBSYS_CRASHED,
+	SUBSYS_RESTARTING,
+};
+
+/**
+ * enum subsys_state - state of a subsystem (public)
+ * @SUBSYS_OFFLINE: subsystem is offline
+ * @SUBSYS_ONLINE: subsystem is online
+ *
+ * The 'public' side of the subsytem state, exposed to userspace.
+ */
+enum subsys_state {
+	SUBSYS_OFFLINE,
+	SUBSYS_ONLINE,
+};
+
+static const char * const subsys_states[] = {
+	[SUBSYS_OFFLINE] = "OFFLINE",
+	[SUBSYS_ONLINE] = "ONLINE",
+};
+
+/**
+ * struct subsys_tracking - track state of a subsystem or restart order
+ * @p_state: private state of subsystem/order
+ * @state: public state of subsystem/order
+ * @s_lock: protects p_state
+ * @lock: protects subsystem/order callbacks and state
+ *
+ * Tracks the state of a subsystem or a set of subsystems (restart order).
+ * Doing this avoids the need to grab each subsystem's lock and update
+ * each subsystems state when restarting an order.
+ */
+struct subsys_tracking {
+	enum p_subsys_state p_state;
+	spinlock_t s_lock;
+	enum subsys_state state;
+	struct mutex lock;
+};
+
+/**
+ * struct subsys_soc_restart_order - subsystem restart order
+ * @subsystem_list: names of subsystems in this restart order
+ * @count: number of subsystems in order
+ * @track: state tracking and locking
+ * @subsys_ptrs: pointers to subsystems in this restart order
+ */
 struct subsys_soc_restart_order {
 	const char * const *subsystem_list;
 	int count;
 
-	struct mutex shutdown_lock;
-	struct mutex powerup_lock;
+	struct subsys_tracking track;
 	struct subsys_device *subsys_ptrs[];
 };
 
@@ -55,37 +110,35 @@
 	struct list_head list;
 };
 
-enum subsys_state {
-	SUBSYS_OFFLINE,
-	SUBSYS_ONLINE,
-	SUBSYS_CRASHED,
-};
-
-static const char * const subsys_states[] = {
-	[SUBSYS_OFFLINE] = "OFFLINE",
-	[SUBSYS_ONLINE] = "ONLINE",
-	[SUBSYS_CRASHED] = "CRASHED",
-};
-
+/**
+ * struct subsys_device - subsystem device
+ * @desc: subsystem descriptor
+ * @wake_lock: prevents suspend during subsystem_restart()
+ * @wlname: name of @wake_lock
+ * @work: context for subsystem_restart_wq_func() for this device
+ * @track: state tracking and locking
+ * @notify: subsys notify handle
+ * @dev: device
+ * @owner: module that provides @desc
+ * @count: reference count of subsystem_get()/subsystem_put()
+ * @id: ida
+ * @restart_order: order of other devices this devices restarts with
+ * @dentry: debugfs directory for this device
+ */
 struct subsys_device {
 	struct subsys_desc *desc;
 	struct wake_lock wake_lock;
 	char wlname[64];
 	struct work_struct work;
-	spinlock_t restart_lock;
-	bool restarting;
+
+	struct subsys_tracking track;
 
 	void *notify;
 	struct device dev;
 	struct module *owner;
 	int count;
-	enum subsys_state state;
 	int id;
-
-	struct mutex shutdown_lock;
-	struct mutex powerup_lock;
-
-	void *restart_order;
+	struct subsys_soc_restart_order *restart_order;
 #ifdef CONFIG_DEBUG_FS
 	struct dentry *dentry;
 #endif
@@ -105,7 +158,7 @@
 static ssize_t state_show(struct device *dev, struct device_attribute *attr,
 		char *buf)
 {
-	enum subsys_state state = to_subsys(dev)->state;
+	enum subsys_state state = to_subsys(dev)->track.state;
 	return snprintf(buf, PAGE_SIZE, "%s\n", subsys_states[state]);
 }
 
@@ -114,16 +167,30 @@
 {
 	unsigned long flags;
 
-	spin_lock_irqsave(&subsys->restart_lock, flags);
-	if (subsys->state != state) {
-		subsys->state = state;
-		spin_unlock_irqrestore(&subsys->restart_lock, flags);
+	spin_lock_irqsave(&subsys->track.s_lock, flags);
+	if (subsys->track.state != state) {
+		subsys->track.state = state;
+		spin_unlock_irqrestore(&subsys->track.s_lock, flags);
 		sysfs_notify(&subsys->dev.kobj, NULL, "state");
 		return;
 	}
-	spin_unlock_irqrestore(&subsys->restart_lock, flags);
+	spin_unlock_irqrestore(&subsys->track.s_lock, flags);
 }
 
+/**
+ * subsytem_default_online() - Mark a subsystem as online by default
+ * @dev: subsystem to mark as online
+ *
+ * Marks a subsystem as "online" without increasing the reference count
+ * on the subsystem. This is typically used by subsystems that are already
+ * online when the kernel boots up.
+ */
+void subsys_default_online(struct subsys_device *dev)
+{
+	subsys_set_state(dev, SUBSYS_ONLINE);
+}
+EXPORT_SYMBOL(subsys_default_online);
+
 static struct device_attribute subsys_attrs[] = {
 	__ATTR_RO(name),
 	__ATTR_RO(state),
@@ -160,7 +227,7 @@
 
 /* MSM 8x60 restart ordering info */
 static const char * const _order_8x60_all[] = {
-	"external_modem",  "modem", "lpass"
+	"external_modem",  "modem", "adsp"
 };
 DEFINE_SINGLE_RESTART_ORDER(orders_8x60_all, _order_8x60_all);
 
@@ -390,62 +457,154 @@
 	return dev ? to_subsys(dev) : NULL;
 }
 
+static int subsys_start(struct subsys_device *subsys)
+{
+	int ret;
+
+	ret = subsys->desc->start(subsys->desc);
+	if (!ret)
+		subsys_set_state(subsys, SUBSYS_ONLINE);
+
+	return ret;
+}
+
+static void subsys_stop(struct subsys_device *subsys)
+{
+	subsys->desc->stop(subsys->desc);
+	subsys_set_state(subsys, SUBSYS_OFFLINE);
+}
+
+static struct subsys_tracking *subsys_get_track(struct subsys_device *subsys)
+{
+	struct subsys_soc_restart_order *order = subsys->restart_order;
+
+	if (restart_level != RESET_SUBSYS_INDEPENDENT && order)
+		return &order->track;
+	else
+		return &subsys->track;
+}
+
+/**
+ * subsytem_get() - Boot a subsystem
+ * @name: pointer to a string containing the name of the subsystem to boot
+ *
+ * This function returns a pointer if it succeeds. If an error occurs an
+ * ERR_PTR is returned.
+ *
+ * If this feature is disable, the value %NULL will be returned.
+ */
+void *subsystem_get(const char *name)
+{
+	struct subsys_device *subsys;
+	struct subsys_device *subsys_d;
+	int ret;
+	void *retval;
+	struct subsys_tracking *track;
+
+	if (!name)
+		return NULL;
+
+	subsys = retval = find_subsys(name);
+	if (!subsys)
+		return ERR_PTR(-ENODEV);
+	if (!try_module_get(subsys->owner)) {
+		retval = ERR_PTR(-ENODEV);
+		goto err_module;
+	}
+
+	subsys_d = subsystem_get(subsys->desc->depends_on);
+	if (IS_ERR(subsys_d)) {
+		retval = subsys_d;
+		goto err_depends;
+	}
+
+	track = subsys_get_track(subsys);
+	mutex_lock(&track->lock);
+	if (!subsys->count) {
+		ret = subsys_start(subsys);
+		if (ret) {
+			retval = ERR_PTR(ret);
+			goto err_start;
+		}
+	}
+	subsys->count++;
+	mutex_unlock(&track->lock);
+	return retval;
+err_start:
+	mutex_unlock(&track->lock);
+	subsystem_put(subsys_d);
+err_depends:
+	module_put(subsys->owner);
+err_module:
+	put_device(&subsys->dev);
+	return retval;
+}
+EXPORT_SYMBOL(subsystem_get);
+
+/**
+ * subsystem_put() - Shutdown a subsystem
+ * @peripheral_handle: pointer from a previous call to subsystem_get()
+ *
+ * This doesn't imply that a subsystem is shutdown until all callers of
+ * subsystem_get() have called subsystem_put().
+ */
+void subsystem_put(void *subsystem)
+{
+	struct subsys_device *subsys_d, *subsys = subsystem;
+	struct subsys_tracking *track;
+
+	if (IS_ERR_OR_NULL(subsys))
+		return;
+
+	track = subsys_get_track(subsys);
+	mutex_lock(&track->lock);
+	if (WARN(!subsys->count, "%s: %s: Reference count mismatch\n",
+			subsys->desc->name, __func__))
+		goto err_out;
+	if (!--subsys->count)
+		subsys_stop(subsys);
+	mutex_unlock(&track->lock);
+
+	subsys_d = find_subsys(subsys->desc->depends_on);
+	if (subsys_d) {
+		subsystem_put(subsys_d);
+		put_device(&subsys_d->dev);
+	}
+	module_put(subsys->owner);
+	put_device(&subsys->dev);
+	return;
+err_out:
+	mutex_unlock(&track->lock);
+}
+EXPORT_SYMBOL(subsystem_put);
+
 static void subsystem_restart_wq_func(struct work_struct *work)
 {
 	struct subsys_device *dev = container_of(work,
 						struct subsys_device, work);
 	struct subsys_device **list;
 	struct subsys_desc *desc = dev->desc;
-	struct subsys_soc_restart_order *soc_restart_order = NULL;
-	struct mutex *powerup_lock;
-	struct mutex *shutdown_lock;
+	struct subsys_soc_restart_order *order = dev->restart_order;
+	struct subsys_tracking *track;
 	unsigned count;
 	unsigned long flags;
 
-	if (restart_level != RESET_SUBSYS_INDEPENDENT)
-		soc_restart_order = dev->restart_order;
-
 	/*
 	 * It's OK to not take the registration lock at this point.
 	 * This is because the subsystem list inside the relevant
 	 * restart order is not being traversed.
 	 */
-	if (!soc_restart_order) {
+	if (restart_level != RESET_SUBSYS_INDEPENDENT && order) {
+		list = order->subsys_ptrs;
+		count = order->count;
+		track = &order->track;
+	} else {
 		list = &dev;
 		count = 1;
-		powerup_lock = &dev->powerup_lock;
-		shutdown_lock = &dev->shutdown_lock;
-	} else {
-		list = soc_restart_order->subsys_ptrs;
-		count = soc_restart_order->count;
-		powerup_lock = &soc_restart_order->powerup_lock;
-		shutdown_lock = &soc_restart_order->shutdown_lock;
+		track = &dev->track;
 	}
 
-	pr_debug("[%p]: Attempting to get shutdown lock!\n", current);
-
-	/*
-	 * Try to acquire shutdown_lock. If this fails, these subsystems are
-	 * already being restarted - return.
-	 */
-	if (!mutex_trylock(shutdown_lock))
-		goto out;
-
-	pr_debug("[%p]: Attempting to get powerup lock!\n", current);
-
-	/*
-	 * Now that we've acquired the shutdown lock, either we're the first to
-	 * restart these subsystems or some other thread is doing the powerup
-	 * sequence for these subsystems. In the latter case, panic and bail
-	 * out, since a subsystem died in its powerup sequence. This catches
-	 * the case where a subsystem in a restart order isn't the one
-	 * who initiated the original restart but has crashed while the restart
-	 * order is being rebooted.
-	 */
-	if (!mutex_trylock(powerup_lock))
-		panic("%s[%p]: Subsystem died during powerup!",
-						__func__, current);
-
+	mutex_lock(&track->lock);
 	do_epoch_check(dev);
 
 	/*
@@ -461,13 +620,9 @@
 	for_each_subsys_device(list, count, NULL, subsystem_shutdown);
 	send_notification_to_order(list, count, SUBSYS_AFTER_SHUTDOWN);
 
-	/*
-	 * Now that we've finished shutting down these subsystems, release the
-	 * shutdown lock. If a subsystem restart request comes in for a
-	 * subsystem in _this_ restart order after the unlock below, and
-	 * before the powerup lock is released, panic and bail out.
-	 */
-	mutex_unlock(shutdown_lock);
+	spin_lock_irqsave(&track->s_lock, flags);
+	track->p_state = SUBSYS_RESTARTING;
+	spin_unlock_irqrestore(&track->s_lock, flags);
 
 	/* Collect ram dumps for all subsystems in order here */
 	for_each_subsys_device(list, count, NULL, subsystem_ramdump);
@@ -479,44 +634,41 @@
 	pr_info("[%p]: Restart sequence for %s completed.\n",
 			current, desc->name);
 
-	mutex_unlock(powerup_lock);
-
 	mutex_unlock(&soc_order_reg_lock);
+	mutex_unlock(&track->lock);
 
-	pr_debug("[%p]: Released powerup lock!\n", current);
-
-out:
-	spin_lock_irqsave(&dev->restart_lock, flags);
-	dev->restarting = false;
+	spin_lock_irqsave(&track->s_lock, flags);
+	track->p_state = SUBSYS_NORMAL;
 	wake_unlock(&dev->wake_lock);
-	spin_unlock_irqrestore(&dev->restart_lock, flags);
+	spin_unlock_irqrestore(&track->s_lock, flags);
 }
 
 static void __subsystem_restart_dev(struct subsys_device *dev)
 {
 	struct subsys_desc *desc = dev->desc;
 	const char *name = dev->desc->name;
+	struct subsys_tracking *track;
 	unsigned long flags;
 
 	pr_debug("Restarting %s [level=%d]!\n", desc->name, restart_level);
 
+	track = subsys_get_track(dev);
 	/*
-	 * We want to allow drivers to call subsystem_restart{_dev}() as many
-	 * times as they want up until the point where the subsystem is
-	 * shutdown.
+	 * Allow drivers to call subsystem_restart{_dev}() as many times as
+	 * they want up until the point where the subsystem is shutdown.
 	 */
-	spin_lock_irqsave(&dev->restart_lock, flags);
-	if (dev->state != SUBSYS_CRASHED) {
-		if (dev->state == SUBSYS_ONLINE && !dev->restarting) {
-			dev->restarting = true;
-			dev->state = SUBSYS_CRASHED;
+	spin_lock_irqsave(&track->s_lock, flags);
+	if (track->p_state != SUBSYS_CRASHED) {
+		if (dev->track.state == SUBSYS_ONLINE &&
+		    track->p_state != SUBSYS_RESTARTING) {
+			track->p_state = SUBSYS_CRASHED;
 			wake_lock(&dev->wake_lock);
 			queue_work(ssr_wq, &dev->work);
 		} else {
 			panic("Subsystem %s crashed during SSR!", name);
 		}
 	}
-	spin_unlock_irqrestore(&dev->restart_lock, flags);
+	spin_unlock_irqrestore(&track->s_lock, flags);
 }
 
 int subsystem_restart_dev(struct subsys_device *dev)
@@ -532,6 +684,18 @@
 	}
 
 	name = dev->desc->name;
+
+	/*
+	 * If a system reboot/shutdown is underway, ignore subsystem errors.
+	 * However, print a message so that we know that a subsystem behaved
+	 * unexpectedly here.
+	 */
+	if (system_state == SYSTEM_RESTART
+		|| system_state == SYSTEM_POWER_OFF) {
+		pr_err("%s crashed during a system poweroff/shutdown.\n", name);
+		return -EBUSY;
+	}
+
 	pr_info("Restart sequence requested for %s, restart_level = %d.\n",
 		name, restart_level);
 
@@ -597,6 +761,11 @@
 	if (!strcmp(cmp, "restart")) {
 		if (subsystem_restart_dev(subsys))
 			return -EIO;
+	} else if (!strcmp(cmp, "get")) {
+		if (subsystem_get(subsys->desc->name))
+			return -EIO;
+	} else if (!strcmp(cmp, "put")) {
+		subsystem_put(subsys);
 	} else {
 		return -EINVAL;
 	}
@@ -650,8 +819,7 @@
 	struct subsys_device *subsys = to_subsys(dev);
 
 	wake_lock_destroy(&subsys->wake_lock);
-	mutex_destroy(&subsys->shutdown_lock);
-	mutex_destroy(&subsys->powerup_lock);
+	mutex_destroy(&subsys->track.lock);
 	ida_simple_remove(&subsys_ida, subsys->id);
 	kfree(subsys);
 }
@@ -670,7 +838,6 @@
 	subsys->dev.parent = desc->dev;
 	subsys->dev.bus = &subsys_bus_type;
 	subsys->dev.release = subsys_device_release;
-	subsys->state = SUBSYS_ONLINE; /* Until proper refcounting appears */
 
 	subsys->notify = subsys_notif_add_subsys(desc->name);
 	subsys->restart_order = update_restart_order(subsys);
@@ -678,7 +845,7 @@
 	snprintf(subsys->wlname, sizeof(subsys->wlname), "ssr(%s)", desc->name);
 	wake_lock_init(&subsys->wake_lock, WAKE_LOCK_SUSPEND, subsys->wlname);
 	INIT_WORK(&subsys->work, subsystem_restart_wq_func);
-	spin_lock_init(&subsys->restart_lock);
+	spin_lock_init(&subsys->track.s_lock);
 
 	subsys->id = ida_simple_get(&subsys_ida, 0, 0, GFP_KERNEL);
 	if (subsys->id < 0) {
@@ -687,8 +854,7 @@
 	}
 	dev_set_name(&subsys->dev, "subsys%d", subsys->id);
 
-	mutex_init(&subsys->shutdown_lock);
-	mutex_init(&subsys->powerup_lock);
+	mutex_init(&subsys->track.lock);
 
 	ret = subsys_debugfs_add(subsys);
 	if (ret)
@@ -705,8 +871,7 @@
 err_register:
 	subsys_debugfs_remove(subsys);
 err_debugfs:
-	mutex_destroy(&subsys->shutdown_lock);
-	mutex_destroy(&subsys->powerup_lock);
+	mutex_destroy(&subsys->track.lock);
 	ida_simple_remove(&subsys_ida, subsys->id);
 err_ida:
 	wake_lock_destroy(&subsys->wake_lock);
@@ -721,10 +886,10 @@
 		return;
 
 	if (get_device(&subsys->dev)) {
-		mutex_lock(&subsys->powerup_lock);
+		mutex_lock(&subsys->track.lock);
 		WARN_ON(subsys->count);
 		device_unregister(&subsys->dev);
-		mutex_unlock(&subsys->powerup_lock);
+		mutex_unlock(&subsys->track.lock);
 		subsys_debugfs_remove(subsys);
 		put_device(&subsys->dev);
 	}
@@ -760,13 +925,13 @@
 
 	if (cpu_is_msm8x60()) {
 		for (i = 0; i < ARRAY_SIZE(orders_8x60_all); i++) {
-			mutex_init(&orders_8x60_all[i]->powerup_lock);
-			mutex_init(&orders_8x60_all[i]->shutdown_lock);
+			mutex_init(&orders_8x60_all[i]->track.lock);
+			spin_lock_init(&orders_8x60_all[i]->track.s_lock);
 		}
 
 		for (i = 0; i < ARRAY_SIZE(orders_8x60_modems); i++) {
-			mutex_init(&orders_8x60_modems[i]->powerup_lock);
-			mutex_init(&orders_8x60_modems[i]->shutdown_lock);
+			mutex_init(&orders_8x60_modems[i]->track.lock);
+			spin_lock_init(&orders_8x60_modems[i]->track.s_lock);
 		}
 
 		restart_orders = orders_8x60_all;
@@ -779,13 +944,12 @@
 	}
 
 	for (i = 0; i < n_restart_orders; i++) {
-		mutex_init(&restart_orders[i]->powerup_lock);
-		mutex_init(&restart_orders[i]->shutdown_lock);
+		mutex_init(&restart_orders[i]->track.lock);
+		spin_lock_init(&restart_orders[i]->track.s_lock);
 	}
 
-	if (restart_orders == NULL || n_restart_orders < 1) {
+	if (restart_orders == NULL || n_restart_orders < 1)
 		WARN_ON(1);
-	}
 
 	return 0;
 }
diff --git a/arch/arm/mach-msm/sysmon.c b/arch/arm/mach-msm/sysmon.c
index 02ba5ea..112daca 100644
--- a/arch/arm/mach-msm/sysmon.c
+++ b/arch/arm/mach-msm/sysmon.c
@@ -43,6 +43,7 @@
 	struct completion	resp_ready;
 	char			rx_buf[RX_BUF_SIZE];
 	enum transports		transport;
+	struct device		*dev;
 };
 
 static struct sysmon_subsys subsys[SYSMON_NUM_SS] = {
@@ -138,6 +139,9 @@
 	char tx_buf[TX_BUF_SIZE];
 	int ret;
 
+	if (ss->dev == NULL)
+		return -ENODEV;
+
 	if (dest_ss < 0 || dest_ss >= SYSMON_NUM_SS ||
 	    notif < 0 || notif >= SUBSYS_NOTIF_TYPE_COUNT ||
 	    event_ss == NULL)
@@ -178,6 +182,9 @@
 	size_t prefix_len = ARRAY_SIZE(expect) - 1;
 	int ret;
 
+	if (ss->dev == NULL)
+		return -ENODEV;
+
 	if (dest_ss < 0 || dest_ss >= SYSMON_NUM_SS)
 		return -EINVAL;
 
@@ -214,6 +221,9 @@
 	size_t prefix_len = ARRAY_SIZE(expect) - 1;
 	int ret;
 
+	if (ss->dev == NULL)
+		return -ENODEV;
+
 	if (dest_ss < 0 || dest_ss >= SYSMON_NUM_SS ||
 	    buf == NULL || len == 0)
 		return -EINVAL;
@@ -293,6 +303,7 @@
 	default:
 		return -EINVAL;
 	}
+	ss->dev = &pdev->dev;
 
 	return 0;
 }
@@ -301,6 +312,9 @@
 {
 	struct sysmon_subsys *ss = &subsys[pdev->id];
 
+	ss->dev = NULL;
+
+	mutex_lock(&ss->lock);
 	switch (ss->transport) {
 	case TRANSPORT_SMD:
 		smd_close(ss->chan);
@@ -309,6 +323,7 @@
 		hsic_sysmon_close(HSIC_SYSMON_DEV_EXT_MODEM);
 		break;
 	}
+	mutex_unlock(&ss->lock);
 
 	return 0;
 }
diff --git a/arch/arm/mach-msm/timer.c b/arch/arm/mach-msm/timer.c
index b361d9d..b61604a 100644
--- a/arch/arm/mach-msm/timer.c
+++ b/arch/arm/mach-msm/timer.c
@@ -964,9 +964,8 @@
 	if (!smp_processor_id())
 		return 0;
 
-	if (cpu_is_msm8x60() || cpu_is_msm8960() || cpu_is_apq8064() ||
-	    cpu_is_msm8930() || cpu_is_msm8930aa() || cpu_is_msm8627() ||
-	    cpu_is_msm8960ab() || cpu_is_apq8064ab())
+	if (cpu_is_msm8x60() || soc_class_is_msm8960() ||
+	    soc_class_is_apq8064() || soc_class_is_msm8930())
 		__raw_writel(DGT_CLK_CTL_DIV_4, MSM_TMR_BASE + DGT_CLK_CTL);
 
 	if (__get_cpu_var(first_boot)) {
@@ -1062,9 +1061,8 @@
 		sclk_hz = 32765;
 		gpt->flags |= MSM_CLOCK_FLAGS_UNSTABLE_COUNT;
 		dgt->flags |= MSM_CLOCK_FLAGS_UNSTABLE_COUNT;
-	} else if (cpu_is_msm8960() || cpu_is_apq8064() || cpu_is_msm8930() ||
-		   cpu_is_msm8930aa() || cpu_is_msm8627() ||
-		   cpu_is_msm8960ab() || cpu_is_apq8064ab()) {
+	} else if (soc_class_is_msm8960() || soc_class_is_apq8064() ||
+		   soc_class_is_msm8930()) {
 		global_timer_offset = MSM_TMR0_BASE - MSM_TMR_BASE;
 		dgt->freq = 6750000;
 		__raw_writel(DGT_CLK_CTL_DIV_4, MSM_TMR_BASE + DGT_CLK_CTL);
@@ -1073,8 +1071,7 @@
 		gpt->freq = 32765;
 		gpt_hz = 32765;
 		sclk_hz = 32765;
-		if (!cpu_is_msm8930() && !cpu_is_msm8930aa() &&
-		    !cpu_is_msm8627() && !cpu_is_msm8960ab()) {
+		if (!soc_class_is_msm8930() && !cpu_is_msm8960ab()) {
 			gpt->flags |= MSM_CLOCK_FLAGS_UNSTABLE_COUNT;
 			dgt->flags |= MSM_CLOCK_FLAGS_UNSTABLE_COUNT;
 		}
@@ -1124,10 +1121,9 @@
 			       "failed for %s\n", cs->name);
 
 		ce->irq = clock->irq;
-		if (cpu_is_msm8x60() || cpu_is_msm8960() || cpu_is_apq8064() ||
-		    cpu_is_msm8930() || cpu_is_msm9615() || cpu_is_msm8625() ||
-		    cpu_is_msm8627() || cpu_is_msm8930aa() ||
-		    cpu_is_msm8960ab() || cpu_is_apq8064ab()) {
+		if (cpu_is_msm8x60() || cpu_is_msm9615() || cpu_is_msm8625() ||
+		    soc_class_is_msm8960() || soc_class_is_apq8064() ||
+		    soc_class_is_msm8930()) {
 			clock->percpu_evt = alloc_percpu(struct clock_event_device *);
 			if (!clock->percpu_evt) {
 				pr_err("msm_timer_init: memory allocation "
diff --git a/arch/arm/mach-msm/tz_log.c b/arch/arm/mach-msm/tz_log.c
index db797cd..5a7ec69 100644
--- a/arch/arm/mach-msm/tz_log.c
+++ b/arch/arm/mach-msm/tz_log.c
@@ -496,17 +496,23 @@
 	 */
 	tzdiag_phy_iobase = readl_relaxed(virt_iobase);
 
-	/*
-	 * Map the 4KB diagnostic information area
-	 */
-	tzdbg.virt_iobase = devm_ioremap_nocache(&pdev->dev,
-				tzdiag_phy_iobase, DEBUG_MAX_RW_BUF);
+	if (!pdev->dev.of_node) {
 
-	if (!tzdbg.virt_iobase) {
-		dev_err(&pdev->dev,
-			"%s: ERROR could not ioremap: start=%p, len=%u\n",
-			__func__, (void *) tzdiag_phy_iobase, DEBUG_MAX_RW_BUF);
-		return -ENXIO;
+		/*
+		 * Map the 4KB diagnostic information area
+		 */
+		tzdbg.virt_iobase = devm_ioremap_nocache(&pdev->dev,
+					tzdiag_phy_iobase, DEBUG_MAX_RW_BUF);
+
+		if (!tzdbg.virt_iobase) {
+			dev_err(&pdev->dev,
+				"%s: ERROR could not ioremap: start=%p, len=%u\n",
+				__func__, (void *) tzdiag_phy_iobase,
+				DEBUG_MAX_RW_BUF);
+			return -ENXIO;
+		}
+	} else {
+		tzdbg.virt_iobase = virt_iobase;
 	}
 
 	ptr = kzalloc(DEBUG_MAX_RW_BUF, GFP_KERNEL);
diff --git a/arch/arm/mach-msm/wcnss-ssr-8974.c b/arch/arm/mach-msm/wcnss-ssr-8974.c
deleted file mode 100644
index b837efc..0000000
--- a/arch/arm/mach-msm/wcnss-ssr-8974.c
+++ /dev/null
@@ -1,163 +0,0 @@
-/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- */
-
-#include <linux/kernel.h>
-#include <linux/interrupt.h>
-#include <linux/module.h>
-#include <linux/err.h>
-
-#include <mach/subsystem_restart.h>
-#include <mach/msm_smsm.h>
-
-#define MODULE_NAME			"wcnss_8974"
-#define MAX_SSR_REASON_LEN			0x51
-
-static int ss_restart_inprogress;
-static int wcnss_crash;
-static struct subsys_device *wcnss_ssr_dev;
-
-#define WCNSS_APSS_WDOG_BITE_RESET_RDY_IRQ		181
-
-static void log_wcnss_sfr(void)
-{
-	char *smem_reset_reason;
-	char buffer[MAX_SSR_REASON_LEN];
-	unsigned smem_reset_size;
-	unsigned size;
-
-	smem_reset_reason = smem_get_entry(SMEM_SSR_REASON_WCNSS0,
-			&smem_reset_size);
-
-	if (!smem_reset_reason || !smem_reset_size) {
-		pr_err("%s: wcnss subsystem failure reason: %s\n",
-				__func__, "(unknown, smem_get_entry failed)");
-	} else if (!smem_reset_reason[0]) {
-		pr_err("%s: wcnss subsystem failure reason: %s\n",
-				__func__, "(unknown, init string found)");
-	} else {
-		size = smem_reset_size < MAX_SSR_REASON_LEN ? smem_reset_size :
-			(MAX_SSR_REASON_LEN - 1);
-		memcpy(buffer, smem_reset_reason, size);
-		buffer[size] = '\0';
-		pr_err("%s: wcnss subsystem failure reason: %s\n",
-				__func__, buffer);
-		memset(smem_reset_reason, 0, smem_reset_size);
-		wmb();
-	}
-}
-
-static void restart_wcnss(void)
-{
-	log_wcnss_sfr();
-	subsystem_restart("wcnss");
-}
-
-static void smsm_state_cb_hdlr(void *data, uint32_t old_state,
-					uint32_t new_state)
-{
-	wcnss_crash = true;
-
-	pr_err("%s: smsm state changed\n", MODULE_NAME);
-
-	if (!(new_state & SMSM_RESET))
-		return;
-
-	if (ss_restart_inprogress) {
-		pr_err("%s: Ignoring smsm reset req, restart in progress\n",
-						MODULE_NAME);
-		return;
-	}
-
-	ss_restart_inprogress = true;
-	restart_wcnss();
-}
-
-
-static irqreturn_t wcnss_wdog_bite_irq_hdlr(int irq, void *dev_id)
-{
-	wcnss_crash = true;
-
-	if (ss_restart_inprogress) {
-		pr_err("%s: Ignoring wcnss bite irq, restart in progress\n",
-						MODULE_NAME);
-		return IRQ_HANDLED;
-	}
-
-	ss_restart_inprogress = true;
-	restart_wcnss();
-
-	return IRQ_HANDLED;
-}
-
-
-static int wcnss_shutdown(const struct subsys_desc *subsys)
-{
-	return 0;
-}
-
-static int wcnss_powerup(const struct subsys_desc *subsys)
-{
-	return 0;
-}
-
-/* wcnss crash handler */
-static void wcnss_crash_shutdown(const struct subsys_desc *subsys)
-{
-	pr_err("%s: crash shutdown : %d\n", MODULE_NAME, wcnss_crash);
-	if (wcnss_crash != true)
-		smsm_change_state(SMSM_APPS_STATE, SMSM_RESET, SMSM_RESET);
-}
-
-static int wcnss_ramdump(int enable,
-				const struct subsys_desc *crashed_subsys)
-{
-	return 0;
-}
-
-static struct subsys_desc wcnss_ssr = {
-	.name = "wcnss",
-	.shutdown = wcnss_shutdown,
-	.powerup = wcnss_powerup,
-	.ramdump = wcnss_ramdump,
-	.crash_shutdown = wcnss_crash_shutdown
-};
-
-static int __init wcnss_ssr_init(void)
-{
-	int ret;
-
-	ret = smsm_state_cb_register(SMSM_WCNSS_STATE, SMSM_RESET,
-					smsm_state_cb_hdlr, 0);
-	if (ret < 0) {
-		pr_err("%s: Unable to register smsm callback for wcnss Reset! %d\n",
-				MODULE_NAME, ret);
-		goto out;
-	}
-	ret = request_irq(WCNSS_APSS_WDOG_BITE_RESET_RDY_IRQ,
-			wcnss_wdog_bite_irq_hdlr, IRQF_TRIGGER_HIGH,
-				"wcnss_wdog", NULL);
-
-	if (ret < 0) {
-		pr_err("%s: Unable to register for wcnss bite interrupt (%d)\n",
-				MODULE_NAME, ret);
-		goto out;
-	}
-	wcnss_ssr_dev = subsys_register(&wcnss_ssr);
-	if (IS_ERR(wcnss_ssr_dev))
-		return PTR_ERR(wcnss_ssr_dev);
-
-	pr_info("%s: module initialized\n", MODULE_NAME);
-out:
-	return ret;
-}
-
-arch_initcall(wcnss_ssr_init);
diff --git a/arch/arm/mach-msm/wdog_debug.c b/arch/arm/mach-msm/wdog_debug.c
index 82800cf..08dd9ce 100644
--- a/arch/arm/mach-msm/wdog_debug.c
+++ b/arch/arm/mach-msm/wdog_debug.c
@@ -110,7 +110,6 @@
 		goto err;
 	wdog_data->dev = &pdev->dev;
 	platform_set_drvdata(pdev, wdog_data);
-	msm_enable_wdog_debug();
 	return 0;
 err:
 	kzfree(wdog_data);
diff --git a/arch/arm/mm/Kconfig b/arch/arm/mm/Kconfig
index cb245ee..e351eb0 100644
--- a/arch/arm/mm/Kconfig
+++ b/arch/arm/mm/Kconfig
@@ -880,6 +880,16 @@
 	  This option enables optimisations for the PL310 cache
 	  controller.
 
+config CACHE_PL310_ERP
+	tristate "PL310 CACHE Error Reporting"
+	depends on CACHE_PL310
+	help
+	  Say 'Y' here to enable reporting of external L2 cache errors.
+	  This feature can be used as a system debugging technique if cache
+	  corruption is suspected.
+	  Cache error statistics will also be reported in sysfs
+	  /sys/devices/platform/pl310_erp/cache_erp
+
 config CACHE_TAUROS2
 	bool "Enable the Tauros2 L2 cache controller"
 	depends on (ARCH_DOVE || ARCH_MMP || CPU_PJ4)
diff --git a/arch/arm/mm/Makefile b/arch/arm/mm/Makefile
index 1c415af..6314e94 100644
--- a/arch/arm/mm/Makefile
+++ b/arch/arm/mm/Makefile
@@ -100,6 +100,7 @@
 
 obj-$(CONFIG_CACHE_FEROCEON_L2)	+= cache-feroceon-l2.o
 obj-$(CONFIG_CACHE_L2X0)	+= cache-l2x0.o
+obj-$(CONFIG_CACHE_PL310_ERP)   += cache-pl310-erp.o
 obj-$(CONFIG_CACHE_XSC3L2)	+= cache-xsc3l2.o
 obj-$(CONFIG_CACHE_TAUROS2)	+= cache-tauros2.o
 obj-$(CONFIG_VCM)		+= vcm.o vcm_alloc.o
diff --git a/arch/arm/mm/cache-l2x0.c b/arch/arm/mm/cache-l2x0.c
index cb9fc76..bb4da0f 100644
--- a/arch/arm/mm/cache-l2x0.c
+++ b/arch/arm/mm/cache-l2x0.c
@@ -38,6 +38,8 @@
 static unsigned int l2x0_ways;
 static unsigned long sync_reg_offset = L2X0_CACHE_SYNC;
 static void pl310_save(void);
+static void pl310_resume(void);
+static void l2x0_resume(void);
 
 static inline bool is_pl310_rev(int rev)
 {
@@ -378,15 +380,18 @@
 		sync_reg_offset = L2X0_DUMMY_REG;
 #endif
 		outer_cache.set_debug = pl310_set_debug;
+		outer_cache.resume = pl310_resume;
 		break;
 	case L2X0_CACHE_ID_PART_L210:
 		l2x0_ways = (aux >> 13) & 0xf;
 		type = "L210";
+		outer_cache.resume = l2x0_resume;
 		break;
 	default:
 		/* Assume unknown chips have 8 ways */
 		l2x0_ways = 8;
 		type = "L2x0 series";
+		outer_cache.resume = l2x0_resume;
 		break;
 	}
 
diff --git a/arch/arm/mm/cache-pl310-erp.c b/arch/arm/mm/cache-pl310-erp.c
new file mode 100644
index 0000000..ad75143
--- /dev/null
+++ b/arch/arm/mm/cache-pl310-erp.c
@@ -0,0 +1,283 @@
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/cpu.h>
+#include <linux/io.h>
+#include <asm/cputype.h>
+#include <asm/hardware/cache-l2x0.h>
+
+#define MODULE_NAME "pl310_erp"
+
+struct pl310_drv_data {
+	unsigned int irq;
+	unsigned int ecntr;
+	unsigned int parrt;
+	unsigned int parrd;
+	unsigned int errwd;
+	unsigned int errwt;
+	unsigned int errrt;
+	unsigned int errrd;
+	unsigned int slverr;
+	unsigned int decerr;
+	void __iomem *base;
+};
+
+#define ECNTR	BIT(0)
+#define PARRT	BIT(1)
+#define PARRD	BIT(2)
+#define ERRWT	BIT(3)
+#define ERRWD	BIT(4)
+#define ERRRT	BIT(5)
+#define ERRRD	BIT(6)
+#define SLVERR	BIT(7)
+#define DECERR	BIT(8)
+
+static irqreturn_t pl310_erp_irq(int irq, void *dev_id)
+{
+	struct pl310_drv_data *p = platform_get_drvdata(dev_id);
+	uint16_t mask_int_stat, int_clear = 0, error = 0;
+
+	mask_int_stat = readl_relaxed(p->base + L2X0_MASKED_INTR_STAT);
+
+	if (mask_int_stat & ECNTR) {
+		pr_alert("Event Counter1/0 Overflow Increment error\n");
+		p->ecntr++;
+		int_clear = mask_int_stat & ECNTR;
+	}
+
+	if (mask_int_stat & PARRT) {
+		pr_alert("Read parity error on L2 Tag RAM\n");
+		p->parrt++;
+		error = 1;
+		int_clear = mask_int_stat & PARRT;
+	}
+
+	if (mask_int_stat & PARRD) {
+		pr_alert("Read parity error on L2 Tag RAM\n");
+		p->parrd++;
+		error = 1;
+		int_clear = mask_int_stat & PARRD;
+	}
+
+	if (mask_int_stat & ERRWT) {
+		pr_alert("Write error on L2 Tag RAM\n");
+		p->errwt++;
+		int_clear = mask_int_stat & ERRWT;
+	}
+
+	if (mask_int_stat & ERRWD) {
+		pr_alert("Write error on L2 Data RAM\n");
+		p->errwd++;
+		int_clear = mask_int_stat & ERRWD;
+	}
+
+	if (mask_int_stat & ERRRT) {
+		pr_alert("Read error on L2 Tag RAM\n");
+		p->errrt++;
+		int_clear = mask_int_stat & ERRRT;
+	}
+
+	if (mask_int_stat & ERRRD) {
+		pr_alert("Read error on L2 Data RAM\n");
+		p->errrd++;
+		int_clear = mask_int_stat & ERRRD;
+	}
+
+	if (mask_int_stat & DECERR) {
+		pr_alert("L2 master port decode error\n");
+		p->decerr++;
+		int_clear = mask_int_stat & DECERR;
+	}
+
+	if (mask_int_stat & SLVERR) {
+		pr_alert("L2 slave port error\n");
+		p->slverr++;
+		int_clear = mask_int_stat & SLVERR;
+	}
+
+	writel_relaxed(int_clear, p->base + L2X0_INTR_CLEAR);
+
+	/* Make sure the interrupts are cleared */
+	mb();
+
+	/* WARNING will be thrown whenever we receive any L2 interrupt.
+	 * Other than parity on tag/data ram, irrespective of the bits
+	 * set we will throw a warning.
+	 */
+	WARN_ON(!error);
+
+	/* Panic in case we encounter parity error in TAG/DATA Ram */
+	BUG_ON(error);
+
+	return IRQ_HANDLED;
+}
+
+static void pl310_mask_int(struct pl310_drv_data *p, bool enable)
+{
+	uint16_t mask;
+
+	if (enable)
+		mask = 0x1FF;
+	else
+		mask = 0x0;
+
+	writel_relaxed(mask, p->base + L2X0_INTR_MASK);
+
+	/* Make sure Mask is updated */
+	mb();
+
+	pr_debug("Mask interrupt %x\n",
+			readl_relaxed(p->base + L2X0_INTR_MASK));
+}
+
+static int pl310_erp_show(struct device *dev, struct device_attribute *attr,
+				char *buf)
+{
+	struct pl310_drv_data *p = dev_get_drvdata(dev);
+
+	return snprintf(buf, PAGE_SIZE,
+		"L2CC Interrupt Number:\t\t\t%d\n"\
+		"Event Counter1/0 Overflow Increment:\t%u\n"\
+		"Parity Error on L2 Tag RAM (Read):\t%u\n"\
+		"Parity Error on L2 Data RAM (Read):\t%u\n"\
+		"Error on L2 Tag RAM (Write):\t\t%u\n"\
+		"Error on L2 Data RAM (Write):\t\t%u\n"\
+		"Error on L2 Tag RAM (Read):\t\t%u\n"\
+		"Error on L2 Data RAM (Read):\t\t%u\n"\
+		"SLave Error from L3 Port:\t\t%u\n"\
+		"Decode Error from L3 Port:\t\t%u\n",
+		p->irq, p->ecntr, p->parrt, p->parrd, p->errwt, p->errwd,
+		p->errrt, p->errrd, p->slverr, p->decerr);
+}
+
+static DEVICE_ATTR(cache_erp, 0664, pl310_erp_show, NULL);
+
+static int __init pl310_create_sysfs(struct device *dev)
+{
+	/* create a sysfs entry at
+	 * /sys/devices/platform/pl310_erp/cache_erp
+	 */
+	return device_create_file(dev, &dev_attr_cache_erp);
+}
+
+static int __devinit pl310_cache_erp_probe(struct platform_device *pdev)
+{
+	struct resource *r;
+	struct pl310_drv_data *drv_data;
+	int ret;
+
+	drv_data = devm_kzalloc(&pdev->dev, sizeof(struct pl310_drv_data),
+						GFP_KERNEL);
+	if  (drv_data == NULL) {
+		dev_err(&pdev->dev, "cannot allocate memory\n");
+		ret = -ENOMEM;
+		goto error;
+	}
+
+	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!r) {
+		dev_err(&pdev->dev, "No L2 base address\n");
+		ret = -ENODEV;
+		goto error;
+	}
+
+	if (!devm_request_mem_region(&pdev->dev, r->start, resource_size(r),
+					"erp")) {
+		ret = -EBUSY;
+		goto error;
+	}
+
+	drv_data->base = devm_ioremap_nocache(&pdev->dev, r->start,
+						resource_size(r));
+	if (!drv_data->base) {
+		dev_err(&pdev->dev, "errored to ioremap 0x%x\n", r->start);
+		ret = -ENOMEM;
+		goto error;
+	}
+	dev_dbg(&pdev->dev, "L2CC base 0x%p\n", drv_data->base);
+
+	r = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "l2_irq");
+	if (!r) {
+		dev_err(&pdev->dev, "No L2 IRQ resource\n");
+		ret = -ENODEV;
+		goto error;
+	}
+
+	drv_data->irq = r->start;
+
+	ret = devm_request_irq(&pdev->dev, drv_data->irq, pl310_erp_irq,
+			IRQF_TRIGGER_RISING, "l2cc_intr", pdev);
+	if (ret) {
+		dev_err(&pdev->dev, "request irq for L2 interrupt failed\n");
+		goto error;
+	}
+
+	platform_set_drvdata(pdev, drv_data);
+
+	pl310_mask_int(drv_data, true);
+
+	ret = pl310_create_sysfs(&pdev->dev);
+	if (ret) {
+		dev_err(&pdev->dev, "Failed to create sysfs entry\n");
+		goto sysfs_err;
+	}
+
+	return 0;
+
+sysfs_err:
+	platform_set_drvdata(pdev, NULL);
+	pl310_mask_int(drv_data, false);
+error:
+	return  ret;
+}
+
+static int __devexit pl310_cache_erp_remove(struct platform_device *pdev)
+{
+	struct pl310_drv_data *p = platform_get_drvdata(pdev);
+
+	pl310_mask_int(p, false);
+
+	device_remove_file(&pdev->dev, &dev_attr_cache_erp);
+
+	platform_set_drvdata(pdev, NULL);
+
+	return 0;
+}
+
+static struct platform_driver pl310_cache_erp_driver = {
+	.probe = pl310_cache_erp_probe,
+	.remove = __devexit_p(pl310_cache_erp_remove),
+	.driver = {
+		.name = MODULE_NAME,
+		.owner = THIS_MODULE,
+	},
+};
+
+static int __init pl310_cache_erp_init(void)
+{
+	return platform_driver_register(&pl310_cache_erp_driver);
+}
+module_init(pl310_cache_erp_init);
+
+static void __exit pl310_cache_erp_exit(void)
+{
+	platform_driver_unregister(&pl310_cache_erp_driver);
+}
+module_exit(pl310_cache_erp_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("PL310 cache error reporting driver");
diff --git a/arch/arm/tools/mach-types b/arch/arm/tools/mach-types
index d283705..2b31f47 100644
--- a/arch/arm/tools/mach-types
+++ b/arch/arm/tools/mach-types
@@ -1196,3 +1196,4 @@
 msm8625_qrd7		MACH_MSM8625_QRD7	MSM8625_QRD7		4095
 msm8625_ffa		MACH_MSM8625_FFA	MSM8625_FFA		4166
 msm8625_evt		MACH_MSM8625_EVT	MSM8625_EVT		4193
+qrd_skud_prime		MACH_QRD_SKUD_PRIME	QRD_SKUD_PRIME		4393
diff --git a/block/row-iosched.c b/block/row-iosched.c
index f24437c..b7965c6 100644
--- a/block/row-iosched.c
+++ b/block/row-iosched.c
@@ -387,12 +387,19 @@
 	if (list_empty(&rd->row_queues[currq].rqueue.fifo)) {
 		/* check idling */
 		if (delayed_work_pending(&rd->read_idle.idle_work)) {
-			row_log_rowq(rd, currq,
-				     "Delayed work pending. Exiting");
-			goto done;
+			if (force) {
+				(void)cancel_delayed_work(
+				&rd->read_idle.idle_work);
+				row_log_rowq(rd, currq,
+					"Canceled delayed work - forced dispatch");
+			} else {
+				row_log_rowq(rd, currq,
+						 "Delayed work pending. Exiting");
+				goto done;
+			}
 		}
 
-		if (queue_idling_enabled[currq] &&
+		if (!force && queue_idling_enabled[currq] &&
 		    rd->row_queues[currq].rqueue.idle_data.begin_idling) {
 			if (!queue_delayed_work(rd->read_idle.idle_workqueue,
 			    &rd->read_idle.idle_work,
@@ -603,29 +610,27 @@
 	return ret;							\
 }
 STORE_FUNCTION(row_hp_read_quantum_store,
-		&rowd->row_queues[ROWQ_PRIO_HIGH_READ].disp_quantum, 0,
-		INT_MAX, 0);
+&rowd->row_queues[ROWQ_PRIO_HIGH_READ].disp_quantum, 1, INT_MAX, 0);
 STORE_FUNCTION(row_rp_read_quantum_store,
-		&rowd->row_queues[ROWQ_PRIO_REG_READ].disp_quantum, 0,
-		INT_MAX, 0);
+			&rowd->row_queues[ROWQ_PRIO_REG_READ].disp_quantum,
+			1, INT_MAX, 0);
 STORE_FUNCTION(row_hp_swrite_quantum_store,
-		&rowd->row_queues[ROWQ_PRIO_HIGH_SWRITE].disp_quantum, 0,
-		INT_MAX, 0);
+			&rowd->row_queues[ROWQ_PRIO_HIGH_SWRITE].disp_quantum,
+			1, INT_MAX, 0);
 STORE_FUNCTION(row_rp_swrite_quantum_store,
-		&rowd->row_queues[ROWQ_PRIO_REG_SWRITE].disp_quantum, 0,
-		INT_MAX, 0);
+			&rowd->row_queues[ROWQ_PRIO_REG_SWRITE].disp_quantum,
+			1, INT_MAX, 0);
 STORE_FUNCTION(row_rp_write_quantum_store,
-		&rowd->row_queues[ROWQ_PRIO_REG_WRITE].disp_quantum, 0,
-		INT_MAX, 0);
+			&rowd->row_queues[ROWQ_PRIO_REG_WRITE].disp_quantum,
+			1, INT_MAX, 0);
 STORE_FUNCTION(row_lp_read_quantum_store,
-		&rowd->row_queues[ROWQ_PRIO_LOW_READ].disp_quantum, 0,
-		INT_MAX, 0);
+			&rowd->row_queues[ROWQ_PRIO_LOW_READ].disp_quantum,
+			1, INT_MAX, 0);
 STORE_FUNCTION(row_lp_swrite_quantum_store,
-		&rowd->row_queues[ROWQ_PRIO_LOW_SWRITE].disp_quantum, 0,
-		INT_MAX, 1);
+			&rowd->row_queues[ROWQ_PRIO_LOW_SWRITE].disp_quantum,
+			1, INT_MAX, 1);
 STORE_FUNCTION(row_read_idle_store, &rowd->read_idle.idle_time, 1, INT_MAX, 1);
-STORE_FUNCTION(row_read_idle_freq_store, &rowd->read_idle.freq,
-				1, INT_MAX, 1);
+STORE_FUNCTION(row_read_idle_freq_store, &rowd->read_idle.freq, 1, INT_MAX, 1);
 
 #undef STORE_FUNCTION
 
diff --git a/block/test-iosched.c b/block/test-iosched.c
index 0a033dc..52070ac 100644
--- a/block/test-iosched.c
+++ b/block/test-iosched.c
@@ -95,6 +95,9 @@
 			return;
 	}
 
+	ptd->test_info.test_duration = jiffies -
+				ptd->test_info.test_duration;
+
 	test_pr_info("%s: Test is completed", __func__);
 
 	test_iosched_mark_test_completion();
@@ -124,7 +127,7 @@
 	test_rq = (struct test_request *)rq->elv.priv[0];
 	BUG_ON(!test_rq);
 
-	test_pr_info("%s: request %d completed, err=%d",
+	test_pr_debug("%s: request %d completed, err=%d",
 	       __func__, test_rq->req_id, err);
 
 	test_rq->req_completed = true;
@@ -669,6 +672,7 @@
 			goto error;
 		}
 
+		ptd->test_info.test_duration = jiffies;
 		ret = run_test(ptd);
 		if (ret) {
 			test_pr_err("%s: failed to run the test\n", __func__);
@@ -678,6 +682,7 @@
 		test_pr_info("%s: Waiting for the test completion", __func__);
 
 		wait_event(ptd->wait_q, ptd->test_state == TEST_COMPLETED);
+		t_info->test_duration = ptd->test_info.test_duration;
 		del_timer_sync(&ptd->timeout_timer);
 
 		ret = check_test_result(ptd);
diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig
index 00a07a0..0b3ffef 100644
--- a/drivers/char/Kconfig
+++ b/drivers/char/Kconfig
@@ -649,6 +649,16 @@
           block.  Or some systems may want the iMem to be dedicated to a
           different function.
 
+config MSM_ADSPRPC
+        tristate "Qualcomm ADSP RPC driver"
+        depends on MSM_AUDIO_QDSP6 || MSM_AUDIO_QDSP6V2
+        default m
+        help
+          Provides a communication mechanism that allows for clients to
+          make remote method invocations across processor boundary to
+          applications DSP processor. Say M if you want to enable this
+          module.
+
 config MMC_GENERIC_CSDIO
 	tristate "Generic sdio driver"
 	default n
diff --git a/drivers/char/Makefile b/drivers/char/Makefile
index c38c26c..8032f0b 100644
--- a/drivers/char/Makefile
+++ b/drivers/char/Makefile
@@ -66,4 +66,5 @@
 obj-$(CONFIG_TILE_SROM)		+= tile-srom.o
 obj-$(CONFIG_MSM_ROTATOR)	+= msm_rotator.o
 obj-$(CONFIG_MMC_GENERIC_CSDIO)	+= csdio.o
-obj-$(CONFIG_DIAG_CHAR)		+= diag/
\ No newline at end of file
+obj-$(CONFIG_DIAG_CHAR)		+= diag/
+obj-$(CONFIG_MSM_ADSPRPC)       += adsprpc.o
diff --git a/arch/arm/mach-msm/qdsp6v2/adsprpc.c b/drivers/char/adsprpc.c
similarity index 68%
rename from arch/arm/mach-msm/qdsp6v2/adsprpc.c
rename to drivers/char/adsprpc.c
index 49008de..822da91 100644
--- a/arch/arm/mach-msm/qdsp6v2/adsprpc.c
+++ b/drivers/char/adsprpc.c
@@ -11,6 +11,7 @@
  * GNU General Public License for more details.
  *
  */
+#include <linux/scatterlist.h>
 #include "adsprpc.h"
 
 struct smq_invoke_ctx {
@@ -70,14 +71,25 @@
 static int alloc_mem(struct fastrpc_buf *buf)
 {
 	struct ion_client *clnt = gfa.iclient;
+	struct sg_table *sg;
 	int err = 0;
 
 	buf->handle = ion_alloc(clnt, buf->size, SZ_4K,
 				ION_HEAP(ION_AUDIO_HEAP_ID), 0);
-	VERIFY(0 == IS_ERR_OR_NULL(buf->handle));
+	VERIFY(err, 0 == IS_ERR_OR_NULL(buf->handle));
+	if (err)
+		goto bail;
 	buf->virt = 0;
-	VERIFY(0 != (buf->virt = ion_map_kernel(clnt, buf->handle)));
-	VERIFY(0 == ion_phys(clnt, buf->handle, &buf->phys, &buf->size));
+	VERIFY(err, 0 != (buf->virt = ion_map_kernel(clnt, buf->handle)));
+	if (err)
+		goto bail;
+	VERIFY(err, 0 != (sg = ion_sg_table(clnt, buf->handle)));
+	if (err)
+		goto bail;
+	VERIFY(err, 1 == sg->nents);
+	if (err)
+		goto bail;
+	buf->phys = sg_dma_address(sg->sgl);
  bail:
 	if (err && !IS_ERR_OR_NULL(buf->handle))
 		free_mem(buf);
@@ -87,7 +99,9 @@
 static int context_list_ctor(struct smq_context_list *me, int size)
 {
 	int err = 0;
-	VERIFY(0 != (me->ls = kzalloc(size, GFP_KERNEL)));
+	VERIFY(err, 0 != (me->ls = kzalloc(size, GFP_KERNEL)));
+	if (err)
+		goto bail;
 	me->size = size / sizeof(*me->ls);
 	me->last = 0;
  bail:
@@ -103,18 +117,18 @@
 static void context_list_alloc_ctx(struct smq_context_list *me,
 					struct smq_invoke_ctx **po)
 {
-	int ii = me->last;
+	int i = me->last;
 	struct smq_invoke_ctx *ctx;
 
 	for (;;) {
-		ii = ii % me->size;
-		ctx = &me->ls[ii];
+		i = i % me->size;
+		ctx = &me->ls[i];
 		if (atomic_read(&ctx->free) == 0)
-			if (0 == atomic_cmpxchg(&ctx->free, 0, 1))
+			if (atomic_cmpxchg(&ctx->free, 0, 1) == 0)
 				break;
-		ii++;
+		i++;
 	}
-	me->last = ii;
+	me->last = i;
 	ctx->retval = -1;
 	init_completion(&ctx->work);
 	*po = ctx;
@@ -134,13 +148,13 @@
 
 static void context_notify_all_users(struct smq_context_list *me)
 {
-	int ii;
+	int i;
 
 	if (!me->ls)
 		return;
-	for (ii = 0; ii < me->size; ++ii) {
-		if (atomic_read(&me->ls[ii].free) != 0)
-			complete(&me->ls[ii].work);
+	for (i = 0; i < me->size; ++i) {
+		if (atomic_read(&me->ls[i].free) != 0)
+			complete(&me->ls[i].work);
 	}
 }
 
@@ -149,11 +163,10 @@
 {
 	struct smq_phy_page *pgstart, *pages;
 	struct smq_invoke_buf *list;
-	int ii, rlen, err = 0;
+	int i, rlen, err = 0;
 	int inbufs = REMOTE_SCALARS_INBUFS(sc);
 	int outbufs = REMOTE_SCALARS_OUTBUFS(sc);
 
-	VERIFY(0 != try_module_get(THIS_MODULE));
 	LOCK_MMAP(kernel);
 	*obuf = *ibuf;
  retry:
@@ -165,38 +178,46 @@
 		rlen = ((uint32_t)pages - (uint32_t)obuf->virt) - obuf->size;
 		obuf->size += buf_page_size(rlen);
 		obuf->handle = 0;
-		VERIFY(0 == alloc_mem(obuf));
+		VERIFY(err, 0 == alloc_mem(obuf));
+		if (err)
+			goto bail;
 		goto retry;
 	}
 	pgstart->addr = obuf->phys;
 	pgstart->size = obuf->size;
-	for (ii = 0; ii < inbufs + outbufs; ++ii) {
+	for (i = 0; i < inbufs + outbufs; ++i) {
 		void *buf;
 		int len, num;
 
-		len = pra[ii].buf.len;
+		list[i].num = 0;
+		list[i].pgidx = 0;
+		len = pra[i].buf.len;
 		if (!len)
 			continue;
-		buf = pra[ii].buf.pv;
+		buf = pra[i].buf.pv;
 		num = buf_num_pages(buf, len);
 		if (!kernel)
-			list[ii].num = buf_get_pages(buf, len, num,
-				ii >= inbufs, pages, rlen / sizeof(*pages));
+			list[i].num = buf_get_pages(buf, len, num,
+				i >= inbufs, pages, rlen / sizeof(*pages));
 		else
-			list[ii].num = 0;
-		VERIFY(list[ii].num >= 0);
-		if (list[ii].num) {
-			list[ii].pgidx = pages - pgstart;
-			pages = pages + list[ii].num;
+			list[i].num = 0;
+		VERIFY(err, list[i].num >= 0);
+		if (err)
+			goto bail;
+		if (list[i].num) {
+			list[i].pgidx = pages - pgstart;
+			pages = pages + list[i].num;
 		} else if (rlen > sizeof(*pages)) {
-			list[ii].pgidx = pages - pgstart;
+			list[i].pgidx = pages - pgstart;
 			pages = pages + 1;
 		} else {
 			if (obuf->handle != ibuf->handle)
 				free_mem(obuf);
 			obuf->size += buf_page_size(sizeof(*pages));
 			obuf->handle = 0;
-			VERIFY(0 == alloc_mem(obuf));
+			VERIFY(err, 0 == alloc_mem(obuf));
+			if (err)
+				goto bail;
 			goto retry;
 		}
 		rlen = obuf->size - ((uint32_t) pages - (uint32_t) obuf->virt);
@@ -206,7 +227,6 @@
 	if (err && (obuf->handle != ibuf->handle))
 		free_mem(obuf);
 	UNLOCK_MMAP(kernel);
-	module_put(THIS_MODULE);
 	return err;
 }
 
@@ -219,74 +239,86 @@
 	struct fastrpc_buf *pbuf = ibuf, *obufs = 0;
 	struct smq_phy_page *pages;
 	void *args;
-	int ii, rlen, size, used, inh, bufs = 0, err = 0;
+	int i, rlen, size, used, inh, bufs = 0, err = 0;
 	int inbufs = REMOTE_SCALARS_INBUFS(sc);
 	int outbufs = REMOTE_SCALARS_OUTBUFS(sc);
 
 	list = smq_invoke_buf_start(rpra, sc);
 	pages = smq_phy_page_start(sc, list);
-	used = ALIGN_8(pbuf->used);
+	used = ALIGN(pbuf->used, BALIGN);
 	args = (void *)((char *)pbuf->virt + used);
 	rlen = pbuf->size - used;
-	for (ii = 0; ii < inbufs + outbufs; ++ii) {
+	for (i = 0; i < inbufs + outbufs; ++i) {
 		int num;
 
-		rpra[ii].buf.len = pra[ii].buf.len;
-		if (list[ii].num) {
-			rpra[ii].buf.pv = pra[ii].buf.pv;
+		rpra[i].buf.len = pra[i].buf.len;
+		if (!rpra[i].buf.len)
+			continue;
+		if (list[i].num) {
+			rpra[i].buf.pv = pra[i].buf.pv;
 			continue;
 		}
-		if (rlen < pra[ii].buf.len) {
+		if (rlen < pra[i].buf.len) {
 			struct fastrpc_buf *b;
 			pbuf->used = pbuf->size - rlen;
-			VERIFY(0 != (b = krealloc(obufs,
+			VERIFY(err, 0 != (b = krealloc(obufs,
 				 (bufs + 1) * sizeof(*obufs), GFP_KERNEL)));
+			if (err)
+				goto bail;
 			obufs = b;
 			pbuf = obufs + bufs;
-			pbuf->size = buf_num_pages(0, pra[ii].buf.len) *
+			pbuf->size = buf_num_pages(0, pra[i].buf.len) *
 								PAGE_SIZE;
-			VERIFY(0 == alloc_mem(pbuf));
+			VERIFY(err, 0 == alloc_mem(pbuf));
+			if (err)
+				goto bail;
 			bufs++;
 			args = pbuf->virt;
 			rlen = pbuf->size;
 		}
-		num = buf_num_pages(args, pra[ii].buf.len);
+		num = buf_num_pages(args, pra[i].buf.len);
 		if (pbuf == ibuf) {
-			list[ii].num = num;
-			list[ii].pgidx = 0;
+			list[i].num = num;
+			list[i].pgidx = 0;
 		} else {
-			list[ii].num = 1;
-			pages[list[ii].pgidx].addr =
+			list[i].num = 1;
+			pages[list[i].pgidx].addr =
 				buf_page_start((void *)(pbuf->phys +
 							 (pbuf->size - rlen)));
-			pages[list[ii].pgidx].size =
-				buf_page_size(pra[ii].buf.len);
+			pages[list[i].pgidx].size =
+				buf_page_size(pra[i].buf.len);
 		}
-		if (ii < inbufs) {
-			if (!kernel)
-				VERIFY(0 == copy_from_user(args, pra[ii].buf.pv,
-							pra[ii].buf.len));
-			else
-				memmove(args, pra[ii].buf.pv, pra[ii].buf.len);
+		if (i < inbufs) {
+			if (!kernel) {
+				VERIFY(err, 0 == copy_from_user(args,
+						pra[i].buf.pv, pra[i].buf.len));
+				if (err)
+					goto bail;
+			} else {
+				memmove(args, pra[i].buf.pv, pra[i].buf.len);
+			}
 		}
-		rpra[ii].buf.pv = args;
-		args = (void *)((char *)args + ALIGN_8(pra[ii].buf.len));
-		rlen -= ALIGN_8(pra[ii].buf.len);
+		rpra[i].buf.pv = args;
+		args = (void *)((char *)args + ALIGN(pra[i].buf.len, BALIGN));
+		rlen -= ALIGN(pra[i].buf.len, BALIGN);
 	}
-	for (ii = 0; ii < inbufs; ++ii) {
-		if (rpra[ii].buf.len)
-			dmac_flush_range(rpra[ii].buf.pv,
-				  (char *)rpra[ii].buf.pv + rpra[ii].buf.len);
+	for (i = 0; i < inbufs; ++i) {
+		if (rpra[i].buf.len)
+			dmac_flush_range(rpra[i].buf.pv,
+				  (char *)rpra[i].buf.pv + rpra[i].buf.len);
 	}
 	pbuf->used = pbuf->size - rlen;
 	size = sizeof(*rpra) * REMOTE_SCALARS_INHANDLES(sc);
 	if (size) {
 		inh = inbufs + outbufs;
-		if (!kernel)
-			VERIFY(0 == copy_from_user(&rpra[inh], &upra[inh],
+		if (!kernel) {
+			VERIFY(err, 0 == copy_from_user(&rpra[inh], &upra[inh],
 							size));
-		else
+			if (err)
+				goto bail;
+		} else {
 			memmove(&rpra[inh], &upra[inh], size);
+		}
 	}
 	dmac_flush_range(rpra, (char *)rpra + used);
  bail:
@@ -298,24 +330,30 @@
 static int put_args(uint32_t kernel, uint32_t sc, remote_arg_t *pra,
 			remote_arg_t *rpra, remote_arg_t *upra)
 {
-	int ii, inbufs, outbufs, outh, size;
+	int i, inbufs, outbufs, outh, size;
 	int err = 0;
 
 	inbufs = REMOTE_SCALARS_INBUFS(sc);
 	outbufs = REMOTE_SCALARS_OUTBUFS(sc);
-	for (ii = inbufs; ii < inbufs + outbufs; ++ii) {
-		if (rpra[ii].buf.pv != pra[ii].buf.pv)
-			VERIFY(0 == copy_to_user(pra[ii].buf.pv,
-					rpra[ii].buf.pv, rpra[ii].buf.len));
+	for (i = inbufs; i < inbufs + outbufs; ++i) {
+		if (rpra[i].buf.pv != pra[i].buf.pv) {
+			VERIFY(err, 0 == copy_to_user(pra[i].buf.pv,
+					rpra[i].buf.pv, rpra[i].buf.len));
+			if (err)
+				goto bail;
+		}
 	}
 	size = sizeof(*rpra) * REMOTE_SCALARS_OUTHANDLES(sc);
 	if (size) {
 		outh = inbufs + outbufs + REMOTE_SCALARS_INHANDLES(sc);
-		if (!kernel)
-			VERIFY(0 == copy_to_user(&upra[outh], &rpra[outh],
+		if (!kernel) {
+			VERIFY(err, 0 == copy_to_user(&upra[outh], &rpra[outh],
 						size));
-		else
+			if (err)
+				goto bail;
+		} else {
 			memmove(&upra[outh], &rpra[outh], size);
+		}
 	}
  bail:
 	return err;
@@ -323,24 +361,24 @@
 
 static void inv_args(uint32_t sc, remote_arg_t *rpra, int used)
 {
-	int ii, inbufs, outbufs;
+	int i, inbufs, outbufs;
 	int inv = 0;
 
 	inbufs = REMOTE_SCALARS_INBUFS(sc);
 	outbufs = REMOTE_SCALARS_OUTBUFS(sc);
-	for (ii = inbufs; ii < inbufs + outbufs; ++ii) {
-		if (buf_page_start(rpra) == buf_page_start(rpra[ii].buf.pv))
+	for (i = inbufs; i < inbufs + outbufs; ++i) {
+		if (buf_page_start(rpra) == buf_page_start(rpra[i].buf.pv))
 			inv = 1;
-		else
-			dmac_inv_range(rpra[ii].buf.pv,
-				(char *)rpra[ii].buf.pv + rpra[ii].buf.len);
+		else if (rpra[i].buf.len)
+			dmac_inv_range(rpra[i].buf.pv,
+				(char *)rpra[i].buf.pv + rpra[i].buf.len);
 	}
 
 	if (inv || REMOTE_SCALARS_OUTHANDLES(sc))
 		dmac_inv_range(rpra, (char *)rpra + used);
 }
 
-static int fastrpc_invoke_send(struct fastrpc_apps *me, remote_handle_t handle,
+static int fastrpc_invoke_send(struct fastrpc_apps *me, uint32_t handle,
 				 uint32_t sc, struct smq_invoke_ctx *ctx,
 				 struct fastrpc_buf *buf)
 {
@@ -357,8 +395,7 @@
 	spin_lock(&me->wrlock);
 	len = smd_write(me->chan, &msg, sizeof(msg));
 	spin_unlock(&me->wrlock);
-	VERIFY(len == sizeof(msg));
- bail:
+	VERIFY(err, len == sizeof(msg));
 	return err;
 }
 
@@ -369,7 +406,8 @@
 	if (me->chan)
 		(void)smd_close(me->chan);
 	context_list_dtor(&me->clst);
-	ion_client_destroy(me->iclient);
+	if (me->iclient)
+		ion_client_destroy(me->iclient);
 	me->iclient = 0;
 	me->chan = 0;
 }
@@ -381,8 +419,10 @@
 	int err = 0;
 
 	do {
-		VERIFY(sizeof(rsp) ==
+		VERIFY(err, sizeof(rsp) ==
 				 smd_read_from_cb(me->chan, &rsp, sizeof(rsp)));
+		if (err)
+			goto bail;
 		context_notify_user(rsp.ctx, rsp.retval);
 	} while (!err);
  bail:
@@ -412,21 +452,29 @@
 	struct fastrpc_apps *me = &gfa;
 
 	if (me->chan == 0) {
-		int ii;
+		int i;
 		spin_lock_init(&me->hlock);
 		spin_lock_init(&me->wrlock);
 		init_completion(&me->work);
-		for (ii = 0; ii < RPC_HASH_SZ; ++ii)
-			INIT_HLIST_HEAD(&me->htbl[ii]);
-		VERIFY(0 == context_list_ctor(&me->clst, SZ_4K));
+		for (i = 0; i < RPC_HASH_SZ; ++i)
+			INIT_HLIST_HEAD(&me->htbl[i]);
+		VERIFY(err, 0 == context_list_ctor(&me->clst, SZ_4K));
+		if (err)
+			goto bail;
 		me->iclient = msm_ion_client_create(ION_HEAP_CARVEOUT_MASK,
 							DEVICE_NAME);
-		VERIFY(0 == IS_ERR_OR_NULL(me->iclient));
-		VERIFY(0 == smd_named_open_on_edge(FASTRPC_SMD_GUID,
+		VERIFY(err, 0 == IS_ERR_OR_NULL(me->iclient));
+		if (err)
+			goto bail;
+		VERIFY(err, 0 == smd_named_open_on_edge(FASTRPC_SMD_GUID,
 						SMD_APPS_QDSP, &me->chan,
 						me, smd_event_handler));
-		VERIFY(0 != wait_for_completion_timeout(&me->work,
+		if (err)
+			goto bail;
+		VERIFY(err, 0 != wait_for_completion_timeout(&me->work,
 							RPC_TIMEOUT));
+		if (err)
+			goto bail;
 	}
  bail:
 	if (err)
@@ -448,10 +496,16 @@
 	int err = 0;
 	struct fastrpc_device *fd = 0;
 
-	VERIFY(0 != try_module_get(THIS_MODULE));
-	VERIFY(0 != (fd = kzalloc(sizeof(*fd), GFP_KERNEL)));
+	VERIFY(err, 0 != try_module_get(THIS_MODULE));
+	if (err)
+		goto bail;
+	VERIFY(err, 0 != (fd = kzalloc(sizeof(*fd), GFP_KERNEL)));
+	if (err)
+		goto bail;
 	fd->buf.size = PAGE_SIZE;
-	VERIFY(0 == alloc_mem(&fd->buf));
+	VERIFY(err, 0 == alloc_mem(&fd->buf));
+	if (err)
+		goto bail;
 	fd->tgid = current->tgid;
 	INIT_HLIST_NODE(&fd->hn);
 	*dev = fd;
@@ -478,7 +532,9 @@
 		}
 	}
 	spin_unlock(&me->hlock);
-	VERIFY(dev != 0);
+	VERIFY(err, dev != 0);
+	if (err)
+		goto bail;
 	*rdev = dev;
  bail:
 	if (err) {
@@ -511,34 +567,49 @@
 	struct fastrpc_buf obuf, *abufs = 0, *b;
 	int interrupted = 0;
 	uint32_t sc;
-	int ii, nbufs = 0, err = 0;
+	int i, nbufs = 0, err = 0;
 
 	sc = invoke->sc;
 	obuf.handle = 0;
 	if (REMOTE_SCALARS_LENGTH(sc)) {
-		VERIFY(0 == get_dev(me, &dev));
-		VERIFY(0 == get_page_list(kernel, sc, pra, &dev->buf, &obuf));
+		VERIFY(err, 0 == get_dev(me, &dev));
+		if (err)
+			goto bail;
+		VERIFY(err, 0 == get_page_list(kernel, sc, pra, &dev->buf,
+						&obuf));
+		if (err)
+			goto bail;
 		rpra = (remote_arg_t *)obuf.virt;
-		VERIFY(0 == get_args(kernel, sc, pra, rpra, invoke->pra, &obuf,
-					&abufs, &nbufs));
+		VERIFY(err, 0 == get_args(kernel, sc, pra, rpra, invoke->pra,
+					&obuf, &abufs, &nbufs));
+		if (err)
+			goto bail;
 	}
 
 	context_list_alloc_ctx(&me->clst, &ctx);
-	VERIFY(0 == fastrpc_invoke_send(me, invoke->handle, sc, ctx, &obuf));
+	VERIFY(err, 0 == fastrpc_invoke_send(me, invoke->handle, sc, ctx,
+						&obuf));
+	if (err)
+		goto bail;
 	inv_args(sc, rpra, obuf.used);
-	VERIFY(0 == (interrupted =
+	VERIFY(err, 0 == (interrupted =
 			wait_for_completion_interruptible(&ctx->work)));
-	VERIFY(0 == (err = ctx->retval));
-	VERIFY(0 == put_args(kernel, sc, pra, rpra, invoke->pra));
+	if (err)
+		goto bail;
+	VERIFY(err, 0 == (err = ctx->retval));
+	if (err)
+		goto bail;
+	VERIFY(err, 0 == put_args(kernel, sc, pra, rpra, invoke->pra));
+	if (err)
+		goto bail;
  bail:
 	if (interrupted) {
-		init_completion(&ctx->work);
 		if (!kernel)
 			(void)fastrpc_release_current_dsp_process();
 		wait_for_completion(&ctx->work);
 	}
 	context_free(ctx);
-	for (ii = 0, b = abufs; ii < nbufs; ++ii, ++b)
+	for (i = 0, b = abufs; i < nbufs; ++i, ++b)
 		free_mem(b);
 	kfree(abufs);
 	if (dev) {
@@ -563,8 +634,7 @@
 	ioctl.handle = 1;
 	ioctl.sc = REMOTE_SCALARS_MAKE(0, 1, 0);
 	ioctl.pra = ra;
-	VERIFY(0 == fastrpc_internal_invoke(me, 1, &ioctl, ra));
- bail:
+	VERIFY(err, 0 == fastrpc_internal_invoke(me, 1, &ioctl, ra));
 	return err;
 }
 
@@ -582,8 +652,7 @@
 	ioctl.handle = 1;
 	ioctl.sc = REMOTE_SCALARS_MAKE(1, 1, 0);
 	ioctl.pra = ra;
-	VERIFY(0 == fastrpc_internal_invoke(me, 1, &ioctl, ra));
- bail:
+	VERIFY(err, 0 == fastrpc_internal_invoke(me, 1, &ioctl, ra));
 	return err;
 }
 
@@ -628,8 +697,7 @@
 		/* This call will cause a dev to be created
 		 * which will addref this module
 		 */
-		VERIFY(0 == fastrpc_create_current_dsp_process());
- bail:
+		VERIFY(err, 0 == fastrpc_create_current_dsp_process());
 		if (err)
 			cleanup_current_dev();
 		module_put(THIS_MODULE);
@@ -649,19 +717,28 @@
 
 	switch (ioctl_num) {
 	case FASTRPC_IOCTL_INVOKE:
-		VERIFY(0 == copy_from_user(&invoke, param, sizeof(invoke)));
+		VERIFY(err, 0 == copy_from_user(&invoke, param,
+						sizeof(invoke)));
+		if (err)
+			goto bail;
 		bufs = REMOTE_SCALARS_INBUFS(invoke.sc) +
 			REMOTE_SCALARS_OUTBUFS(invoke.sc);
 		if (bufs) {
 			bufs = bufs * sizeof(*pra);
-			VERIFY(0 != (pra = kmalloc(bufs, GFP_KERNEL)));
+			VERIFY(err, 0 != (pra = kmalloc(bufs, GFP_KERNEL)));
+			if (err)
+				goto bail;
 		}
-		VERIFY(0 == copy_from_user(pra, invoke.pra, bufs));
-		VERIFY(0 == (err = fastrpc_internal_invoke(me, 0, &invoke,
+		VERIFY(err, 0 == copy_from_user(pra, invoke.pra, bufs));
+		if (err)
+			goto bail;
+		VERIFY(err, 0 == (err = fastrpc_internal_invoke(me, 0, &invoke,
 								pra)));
+		if (err)
+			goto bail;
 		break;
 	default:
-		err = -EINVAL;
+		err = -ENOTTY;
 		break;
 	}
  bail:
@@ -680,13 +757,24 @@
 	struct fastrpc_apps *me = &gfa;
 	int err = 0;
 
-	VERIFY(0 == fastrpc_init());
-	VERIFY(0 == alloc_chrdev_region(&me->dev_no, 0, 1, DEVICE_NAME));
+	VERIFY(err, 0 == fastrpc_init());
+	if (err)
+		goto bail;
+	VERIFY(err, 0 == alloc_chrdev_region(&me->dev_no, 0, 1, DEVICE_NAME));
+	if (err)
+		goto bail;
 	cdev_init(&me->cdev, &fops);
 	me->cdev.owner = THIS_MODULE;
-	VERIFY(0 == cdev_add(&me->cdev, MKDEV(MAJOR(me->dev_no), 0), 1));
+	VERIFY(err, 0 == cdev_add(&me->cdev, MKDEV(MAJOR(me->dev_no), 0), 1));
+	if (err)
+		goto bail;
 	pr_info("'mknod /dev/%s c %d 0'\n", DEVICE_NAME, MAJOR(me->dev_no));
  bail:
+	if (err) {
+		if (me->dev_no)
+			unregister_chrdev_region(me->dev_no, 1);
+		fastrpc_deinit();
+	}
 	return err;
 }
 
diff --git a/arch/arm/mach-msm/qdsp6v2/adsprpc.h b/drivers/char/adsprpc.h
similarity index 84%
rename from arch/arm/mach-msm/qdsp6v2/adsprpc.h
rename to drivers/char/adsprpc.h
index c6c7d23..3f1b4a7 100644
--- a/arch/arm/mach-msm/qdsp6v2/adsprpc.h
+++ b/drivers/char/adsprpc.h
@@ -36,8 +36,7 @@
 #define RPC_TIMEOUT	(5 * HZ)
 #define RPC_HASH_BITS	5
 #define RPC_HASH_SZ	(1 << RPC_HASH_BITS)
-
-#define ALIGN_8(a)	ALIGN(a, 8)
+#define BALIGN		32
 
 #define LOCK_MMAP(kernel)\
 		do {\
@@ -84,18 +83,26 @@
 	struct vm_area_struct *vma;
 	uint32_t start = buf_page_start(addr);
 	uint32_t len = nr_pages << PAGE_SHIFT;
-	uint32_t pfn;
+	unsigned long pfn;
 	int n = -1, err = 0;
 
-	VERIFY(0 != access_ok(access ? VERIFY_WRITE : VERIFY_READ,
+	VERIFY(err, 0 != access_ok(access ? VERIFY_WRITE : VERIFY_READ,
 			      (void __user *)start, len));
-	VERIFY(0 != (vma = find_vma(current->mm, start)));
-	VERIFY(((uint32_t)addr + sz) <= vma->vm_end);
+	if (err)
+		goto bail;
+	VERIFY(err, 0 != (vma = find_vma(current->mm, start)));
+	if (err)
+		goto bail;
+	VERIFY(err, ((uint32_t)addr + sz) <= vma->vm_end);
+	if (err)
+		goto bail;
 	n = 0;
-	VERIFY(0 != (vma->vm_flags & VM_PFNMAP));
-	VERIFY(0 != (vma->vm_flags & VM_PFN_AT_MMAP));
-	VERIFY(nr_elems > 0);
-	pfn = vma->vm_pgoff + ((start - vma->vm_start) >> PAGE_SHIFT);
+	VERIFY(err, 0 == follow_pfn(vma, start, &pfn));
+	if (err)
+		goto bail;
+	VERIFY(err, nr_elems > 0);
+	if (err)
+		goto bail;
 	pages->addr = __pfn_to_phys(pfn);
 	pages->size = len;
 	n++;
diff --git a/arch/arm/mach-msm/qdsp6v2/adsprpc_shared.h b/drivers/char/adsprpc_shared.h
similarity index 92%
rename from arch/arm/mach-msm/qdsp6v2/adsprpc_shared.h
rename to drivers/char/adsprpc_shared.h
index 04b1d4a..dc6ab6f 100644
--- a/arch/arm/mach-msm/qdsp6v2/adsprpc_shared.h
+++ b/drivers/char/adsprpc_shared.h
@@ -60,20 +60,18 @@
 #define __TOSTR__(x) __STR__(x)
 #define __FILE_LINE__ __FILE__ ":" __TOSTR__(__LINE__)
 
-#define VERIFY(val) \
+#define VERIFY(err, val) \
 do {\
 	VERIFY_IPRINTF(__FILE_LINE__"info: calling: " #val "\n");\
 	if (0 == (val)) {\
-		err = err == 0 ? -1 : err;\
-		VERIFY_EPRINTF(__FILE_LINE__"error: %d: " #val "\n", err);\
-		goto bail;\
+		(err) = (err) == 0 ? -1 : (err);\
+		VERIFY_EPRINTF(__FILE_LINE__"error: %d: " #val "\n", (err));\
 	} else {\
 		VERIFY_IPRINTF(__FILE_LINE__"info: passed: " #val "\n");\
 	} \
 } while (0)
 #endif
 
-#define remote_handle_t uint32_t
 #define remote_arg_t    union remote_arg
 
 struct remote_buf {
@@ -83,18 +81,18 @@
 
 union remote_arg {
 	struct remote_buf buf;	/* buffer info */
-	remote_handle_t h;	/* remote handle */
+	uint32_t h;		/* remote handle */
 };
 
 struct fastrpc_ioctl_invoke {
-	remote_handle_t handle;	/* remote handle */
+	uint32_t handle;	/* remote handle */
 	uint32_t sc;		/* scalars describing the data */
 	remote_arg_t *pra;	/* remote arguments list */
 };
 
 struct smq_null_invoke {
 	struct smq_invoke_ctx *ctx; /* invoke caller context */
-	remote_handle_t handle;	    /* handle to invoke */
+	uint32_t handle;	    /* handle to invoke */
 	uint32_t sc;		    /* scalars structure describing the data */
 };
 
diff --git a/drivers/char/diag/diag_dci.c b/drivers/char/diag/diag_dci.c
index 2860055..5cd5ce9 100644
--- a/drivers/char/diag/diag_dci.c
+++ b/drivers/char/diag/diag_dci.c
@@ -28,17 +28,25 @@
 #include "diagmem.h"
 #include "diagchar.h"
 #include "diagfwd.h"
+#include "diagfwd_cntl.h"
 #include "diag_dci.h"
 
 unsigned int dci_max_reg = 100;
 unsigned int dci_max_clients = 10;
+unsigned char dci_cumulative_log_mask[DCI_LOG_MASK_SIZE];
+unsigned char dci_cumulative_event_mask[DCI_EVENT_MASK_SIZE];
+struct mutex dci_log_mask_mutex;
+struct mutex dci_event_mask_mutex;
+
+#define DCI_CHK_CAPACITY(entry, new_data_len)				\
+((entry->data_len + new_data_len > entry->total_capacity) ? 1 : 0)	\
 
 static void diag_smd_dci_send_req(int proc_num)
 {
 	void *buf = NULL;
 	smd_channel_t *smd_ch = NULL;
-	int i, r, found = 1;
-	int cmd_code_len = 1;
+	int recd_bytes, read_bytes, dci_pkt_len, i;
+	uint8_t recv_pkt_cmd_code;
 
 	if (driver->in_busy_dci)
 		return;
@@ -51,58 +59,241 @@
 	if (!smd_ch || !buf)
 		return;
 
-	r = smd_read_avail(smd_ch);
-	if (r > IN_BUF_SIZE) {
-		if (r < MAX_IN_BUF_SIZE) {
-			pr_err("diag: SMD DCI sending pkt upto %d bytes", r);
-			buf = krealloc(buf, r, GFP_KERNEL);
+	recd_bytes = smd_read_avail(smd_ch);
+	if (recd_bytes > IN_BUF_SIZE) {
+		if (recd_bytes < MAX_IN_BUF_SIZE) {
+			pr_err("diag: SMD DCI sending pkt upto %d bytes",
+				recd_bytes);
+			buf = krealloc(buf, recd_bytes, GFP_KERNEL);
 		} else {
 			pr_err("diag: DCI pkt > %d bytes", MAX_IN_BUF_SIZE);
 			return;
 		}
 	}
-	if (buf && r > 0) {
-		smd_read(smd_ch, buf, r);
-		pr_debug("diag: data received ---\n");
-		for (i = 0; i < r; i++)
-			pr_debug("\t %x \t", *(((unsigned char *)buf)+i));
+	if (buf && recd_bytes > 0) {
+		smd_read(smd_ch, buf, recd_bytes);
+		pr_debug("diag: data received %d bytes\n", recd_bytes);
+		/* Each SMD read can have multiple DCI packets */
+		read_bytes = 0;
+		while (read_bytes < recd_bytes) {
+			/* read actual length of dci pkt */
+			dci_pkt_len = *(uint16_t *)(buf+2);
+			/* process one dci packet */
+			pr_debug("diag: bytes read = %d, single dci pkt len = %d\n",
+				read_bytes, dci_pkt_len);
+			/* print_hex_dump(KERN_DEBUG, "Single DCI packet :",
+			 DUMP_PREFIX_ADDRESS, 16, 1, buf, 5 + dci_pkt_len, 1);*/
+			recv_pkt_cmd_code = *(uint8_t *)(buf+4);
+			if (recv_pkt_cmd_code == LOG_CMD_CODE)
+				extract_dci_log(buf+4);
+			else if (recv_pkt_cmd_code == EVENT_CMD_CODE)
+				extract_dci_events(buf+4);
+			else
+				extract_dci_pkt_rsp(buf); /* pkt response */
+			read_bytes += 5 + dci_pkt_len;
+			buf += 5 + dci_pkt_len; /* advance to next DCI pkt */
+		}
+		/* wake up all sleeping DCI clients which have some data */
+		for (i = 0; i < MAX_DCI_CLIENTS; i++)
+			if (driver->dci_client_tbl[i].client &&
+				driver->dci_client_tbl[i].data_len) {
+				driver->in_busy_dci = 1;
+				diag_update_sleeping_process(
+					driver->dci_client_tbl[i].client->tgid,
+						 DCI_DATA_TYPE);
+			}
+	}
+}
 
-		if (*(uint8_t *)(buf+4) != DCI_CMD_CODE)
-			cmd_code_len = 4; /* delayed response */
-		driver->write_ptr_dci->length =
-			 (int)(*(uint16_t *)(buf+2)) - (4+cmd_code_len);
-		pr_debug("diag: len = %d\n", (int)(*(uint16_t *)(buf+2))
-							 - (4+cmd_code_len));
-		/* look up DCI client with tag */
-		for (i = 0; i < dci_max_reg; i++) {
-			if (driver->dci_tbl[i].tag ==
-			    *(int *)(buf+(4+cmd_code_len))) {
-				found = 0;
-				break;
+void extract_dci_pkt_rsp(unsigned char *buf)
+{
+	int i = 0, index = -1, cmd_code_len = 1;
+	int curr_client_pid = 0, write_len;
+	struct diag_dci_client_tbl *entry;
+	void *temp_buf = NULL;
+	uint8_t recv_pkt_cmd_code;
+
+	recv_pkt_cmd_code = *(uint8_t *)(buf+4);
+	if (recv_pkt_cmd_code != DCI_PKT_RSP_CODE)
+		cmd_code_len = 4; /* delayed response */
+	write_len = (int)(*(uint16_t *)(buf+2)) - cmd_code_len;
+	pr_debug("diag: len = %d\n", write_len);
+	/* look up DCI client with tag */
+	for (i = 0; i < dci_max_reg; i++) {
+		if (driver->req_tracking_tbl[i].tag ==
+					 *(int *)(buf+(4+cmd_code_len))) {
+			*(int *)(buf+4+cmd_code_len) =
+					driver->req_tracking_tbl[i].uid;
+			curr_client_pid =
+					 driver->req_tracking_tbl[i].pid;
+			index = i;
+			break;
+		}
+	}
+	if (index == -1)
+		pr_alert("diag: No matching PID for DCI data\n");
+	/* Using PID of client process, find client buffer */
+	for (i = 0; i < MAX_DCI_CLIENTS; i++) {
+		if (curr_client_pid == driver->dci_client_tbl[i].client->tgid) {
+			/* copy pkt rsp in client buf */
+			entry = &(driver->dci_client_tbl[i]);
+			if (DCI_CHK_CAPACITY(entry, 8+write_len)) {
+				pr_alert("diag: create capacity for pkt rsp\n");
+				entry->total_capacity += 8+write_len;
+				temp_buf = krealloc(entry->dci_data,
+					 entry->total_capacity, GFP_KERNEL);
+				if (!temp_buf) {
+					pr_err("diag: DCI realloc failed\n");
+					break;
+				} else {
+					entry->dci_data = temp_buf;
+				}
+			}
+			*(int *)(entry->dci_data+entry->data_len) =
+							DCI_PKT_RSP_TYPE;
+			entry->data_len += 4;
+			*(int *)(entry->dci_data+entry->data_len) = write_len;
+			entry->data_len += 4;
+			memcpy(entry->dci_data+entry->data_len,
+				 buf+4+cmd_code_len, write_len);
+			entry->data_len += write_len;
+			/* delete immediate response entry */
+			if (driver->buf_in_dci[8+cmd_code_len] != 0x80)
+				driver->req_tracking_tbl[index].pid = 0;
+			break;
+		}
+	}
+}
+
+void extract_dci_events(unsigned char *buf)
+{
+	uint16_t event_id, event_id_packet;
+	uint8_t *event_mask_ptr, byte_mask, payload_len;
+	uint8_t event_data[MAX_EVENT_SIZE], timestamp[8];
+	int i, byte_index, bit_index, length, temp_len;
+	int total_event_len, payload_len_field, timestamp_len;
+	struct diag_dci_client_tbl *entry;
+
+	length =  *(uint16_t *)(buf+1); /* total length of event series */
+	temp_len = 0;
+	buf = buf + 3; /* start of event series */
+	while (temp_len < length-1) {
+		*event_data = EVENT_CMD_CODE;
+		event_id_packet = *(uint16_t *)(buf+temp_len);
+		event_id = event_id_packet & 0x0FFF; /* extract 12 bits */
+		if (event_id_packet & 0x8000) {
+			timestamp_len = 2;
+		} else {
+			timestamp_len = 8;
+			memcpy(timestamp, buf+temp_len+2, 8);
+		}
+		if (((event_id_packet & 0x6000) >> 13) == 3) {
+			payload_len_field = 1;
+			payload_len = *(uint8_t *)
+					(buf+temp_len+2+timestamp_len);
+			memcpy(event_data+13, buf+temp_len+2+timestamp_len, 1);
+			memcpy(event_data+14, buf+temp_len+2+timestamp_len+1,
+								 payload_len);
+		} else {
+			payload_len_field = 0;
+			payload_len = (event_id_packet & 0x6000) >> 13;
+			if (payload_len < MAX_EVENT_SIZE)
+				memcpy(event_data+13,
+				 buf+temp_len+2+timestamp_len, payload_len);
+			else
+				pr_alert("diag: event > %d\n", MAX_EVENT_SIZE);
+		}
+		/* 2 bytes for the event id & timestamp len is hard coded to 8,
+		   as individual events have full timestamp */
+		*(uint16_t *)(event_data+1) = 10+payload_len_field+payload_len;
+		*(uint16_t *)(event_data+3) = event_id_packet & 0x7FFF;
+		memcpy(event_data+5, timestamp, 8);
+		total_event_len = 3 + 10 + payload_len_field + payload_len;
+		byte_index = event_id/8;
+		bit_index = event_id % 8;
+		byte_mask = 0x1 << bit_index;
+		/* parse through event mask tbl of each client and check mask */
+		for (i = 0; i < MAX_DCI_CLIENTS; i++) {
+			if (driver->dci_client_tbl[i].client) {
+				entry = &(driver->dci_client_tbl[i]);
+				event_mask_ptr = entry->dci_event_mask +
+								 byte_index;
+				if (*event_mask_ptr & byte_mask) {
+					/* copy to client buffer */
+					if (DCI_CHK_CAPACITY(entry,
+							 4 + total_event_len)) {
+						pr_err("diag:DCI event drop\n");
+						driver->dci_client_tbl[i].
+							dropped_events++;
+						return;
+					}
+					driver->dci_client_tbl[i].
+							received_events++;
+					*(int *)(entry->dci_data+
+					entry->data_len) = DCI_EVENT_TYPE;
+					memcpy(entry->dci_data+
+				entry->data_len+4, event_data, total_event_len);
+					entry->data_len += 4 + total_event_len;
+				}
 			}
 		}
-		if (found)
-			pr_alert("diag: No matching PID for DCI data\n");
-		pr_debug("\n diag PID = %d", driver->dci_tbl[i].pid);
-		if (driver->dci_tbl[i].pid == 0)
-			pr_alert("diag: Receiving DCI process deleted\n");
-		*(int *)(buf+4+cmd_code_len) = driver->dci_tbl[i].uid;
-		/* update len after adding UID */
-		driver->write_ptr_dci->length =
-			driver->write_ptr_dci->length + 4;
-		pr_debug("diag: data receivd, wake process\n");
-		driver->in_busy_dci = 1;
-		diag_update_sleeping_process(driver->dci_tbl[i].pid,
-							DCI_DATA_TYPE);
-		/* delete immediate response entry */
-		if (driver->buf_in_dci[8+cmd_code_len] != 0x80)
-			driver->dci_tbl[i].pid = 0;
-		for (i = 0; i < dci_max_reg; i++)
-			if (driver->dci_tbl[i].pid != 0)
-				pr_debug("diag: PID = %d, UID = %d, tag = %d\n",
-				driver->dci_tbl[i].pid, driver->dci_tbl[i].uid,
-				 driver->dci_tbl[i].tag);
-		pr_debug("diag: completed clearing table\n");
+		temp_len += 2 + timestamp_len + payload_len_field + payload_len;
+	}
+}
+
+void extract_dci_log(unsigned char *buf)
+{
+	uint16_t log_code, item_num;
+	uint8_t equip_id, *log_mask_ptr, byte_mask;
+	int i, byte_index, found = 0;
+	struct diag_dci_client_tbl *entry;
+
+	log_code = *(uint16_t *)(buf+6);
+	equip_id = LOG_GET_EQUIP_ID(log_code);
+	item_num = LOG_GET_ITEM_NUM(log_code);
+	byte_index = item_num/8 + 2;
+	byte_mask = 0x01 << (item_num % 8);
+
+	/* parse through log mask table of each client and check mask */
+	for (i = 0; i < MAX_DCI_CLIENTS; i++) {
+		if (driver->dci_client_tbl[i].client) {
+			entry = &(driver->dci_client_tbl[i]);
+			log_mask_ptr = entry->dci_log_mask;
+			found = 0;
+			while (log_mask_ptr) {
+				if (*log_mask_ptr == equip_id) {
+					found = 1;
+					pr_debug("diag: find equip id = %x at %p\n",
+					equip_id, log_mask_ptr);
+					break;
+				} else {
+					pr_debug("diag: did not find equip id = %x at %p\n",
+						 equip_id, log_mask_ptr);
+					log_mask_ptr += 514;
+				}
+			}
+			if (!found)
+				pr_err("diag: dci equip id not found\n");
+			log_mask_ptr = log_mask_ptr + byte_index;
+			if (*log_mask_ptr & byte_mask) {
+				pr_debug("\t log code %x needed by client %d",
+					 log_code, entry->client->tgid);
+				/* copy to client buffer */
+				if (DCI_CHK_CAPACITY(entry,
+						 4 + *(uint16_t *)(buf+2))) {
+						pr_err("diag:DCI log drop\n");
+						driver->dci_client_tbl[i].
+								dropped_logs++;
+						return;
+				}
+				driver->dci_client_tbl[i].received_logs++;
+				*(int *)(entry->dci_data+entry->data_len) =
+								DCI_LOG_TYPE;
+				memcpy(entry->dci_data+entry->data_len+4, buf+4,
+						 *(uint16_t *)(buf+2));
+				entry->data_len += 4 + *(uint16_t *)(buf+2);
+			}
+		}
 	}
 }
 
@@ -113,7 +304,7 @@
 
 static void diag_smd_dci_notify(void *ctxt, unsigned event)
 {
-	queue_work(driver->diag_wq, &(driver->diag_read_smd_dci_work));
+	queue_work(driver->diag_dci_wq, &(driver->diag_read_smd_dci_work));
 }
 
 void diag_dci_notify_client(int peripheral_mask)
@@ -121,11 +312,11 @@
 	int i, stat;
 
 	/* Notify the DCI process that the peripheral DCI Channel is up */
-	for (i = 0; i < MAX_DCI_CLIENT; i++) {
-		if (driver->dci_notify_tbl[i].list & peripheral_mask) {
-			pr_debug("diag: sending signal now\n");
-			stat = send_sig(driver->dci_notify_tbl[i].signal_type,
-					 driver->dci_notify_tbl[i].client, 0);
+	for (i = 0; i < MAX_DCI_CLIENTS; i++) {
+		if (driver->dci_client_tbl[i].list & peripheral_mask) {
+			pr_info("diag: sending signal now\n");
+			stat = send_sig(driver->dci_client_tbl[i].signal_type,
+					 driver->dci_client_tbl[i].client, 0);
 			if (stat)
 				pr_err("diag: Err send sig stat: %d\n", stat);
 			break;
@@ -139,7 +330,7 @@
 
 	if (pdev->id == SMD_APPS_MODEM) {
 		err = smd_open("DIAG_2", &driver->ch_dci, driver,
-					    diag_smd_dci_notify);
+						diag_smd_dci_notify);
 		if (err)
 			pr_err("diag: cannot open DCI port, Id = %d, err ="
 				" %d\n", pdev->id, err);
@@ -163,10 +354,12 @@
 	driver->apps_dci_buf[0] = CONTROL_CHAR; /* start */
 	driver->apps_dci_buf[1] = 1; /* version */
 	*(uint16_t *)(driver->apps_dci_buf + 2) = len + 4 + 1; /* length */
-	driver->apps_dci_buf[4] = DCI_CMD_CODE; /* DCI ID */
-	*(int *)(driver->apps_dci_buf + 5) = driver->dci_tbl[index].tag;
+	driver->apps_dci_buf[4] = DCI_PKT_RSP_CODE;
+	*(int *)(driver->apps_dci_buf + 5) =
+		driver->req_tracking_tbl[index].tag;
 	for (i = 0; i < len; i++)
 		driver->apps_dci_buf[i+9] = *(buf+i);
+
 	driver->apps_dci_buf[9+len] = CONTROL_CHAR; /* end */
 
 	if (entry.client_id == MODEM_PROC && driver->ch_dci) {
@@ -185,27 +378,19 @@
 	int i, new_dci_client = 1, ret = -1;
 
 	for (i = 0; i < dci_max_reg; i++) {
-		if (driver->dci_tbl[i].pid == current->tgid) {
+		if (driver->req_tracking_tbl[i].pid == current->tgid) {
 			new_dci_client = 0;
 			break;
 		}
 	}
 	mutex_lock(&driver->dci_mutex);
-	if (new_dci_client)
-		driver->num_dci_client++;
-	if (driver->num_dci_client > MAX_DCI_CLIENT) {
-		pr_info("diag: Max DCI Client limit reached\n");
-		driver->num_dci_client--;
-		mutex_unlock(&driver->dci_mutex);
-		return ret;
-	}
 	/* Make an entry in kernel DCI table */
 	driver->dci_tag++;
 	for (i = 0; i < dci_max_reg; i++) {
-		if (driver->dci_tbl[i].pid == 0) {
-			driver->dci_tbl[i].pid = current->tgid;
-			driver->dci_tbl[i].uid = uid;
-			driver->dci_tbl[i].tag = driver->dci_tag;
+		if (driver->req_tracking_tbl[i].pid == 0) {
+			driver->req_tracking_tbl[i].pid = current->tgid;
+			driver->req_tracking_tbl[i].uid = uid;
+			driver->req_tracking_tbl[i].tag = driver->dci_tag;
 			ret = i;
 			break;
 		}
@@ -214,62 +399,317 @@
 	return ret;
 }
 
-int diag_process_dci_client(unsigned char *buf, int len)
+int diag_process_dci_transaction(unsigned char *buf, int len)
 {
 	unsigned char *temp = buf;
-	uint16_t subsys_cmd_code;
-	int subsys_id, cmd_code, i, ret = -1, index = -1;
+	uint16_t subsys_cmd_code, log_code, item_num;
+	int subsys_id, cmd_code, i, ret = -1, index = -1, found = 0;
 	struct diag_master_table entry;
+	int count, set_mask, num_codes, byte_index, bit_index, event_id;
+	uint8_t equip_id, *log_mask_ptr, *head_log_mask_ptr, byte_mask;
+	uint8_t *event_mask_ptr;
 
-	/* enter this UID into kernel table and return index */
-	index = diag_register_dci_transaction(*(int *)temp);
-	if (index < 0) {
-		pr_alert("diag: registering new DCI transaction failed\n");
-		return DIAG_DCI_NO_REG;
-	}
-	temp += 4;
-	/* Check for registered peripheral and fwd pkt to apropriate proc */
-	cmd_code = (int)(*(char *)buf);
-	temp++;
-	subsys_id = (int)(*(char *)temp);
-	temp++;
-	subsys_cmd_code = *(uint16_t *)temp;
-	temp += 2;
-	pr_debug("diag: %d %d %d", cmd_code, subsys_id, subsys_cmd_code);
-	for (i = 0; i < diag_max_reg; i++) {
-		entry = driver->table[i];
-		if (entry.process_id != NO_PROCESS) {
-			if (entry.cmd_code == cmd_code && entry.subsys_id ==
-				 subsys_id && entry.cmd_code_lo <=
-							 subsys_cmd_code &&
-				  entry.cmd_code_hi >= subsys_cmd_code) {
-				ret = diag_send_dci_pkt(entry, buf, len, index);
-			} else if (entry.cmd_code == 255
-				  && cmd_code == 75) {
-				if (entry.subsys_id ==
-					subsys_id &&
-				   entry.cmd_code_lo <=
-					subsys_cmd_code &&
-					 entry.cmd_code_hi >=
-					subsys_cmd_code) {
-					ret = diag_send_dci_pkt(entry, buf, len,
-								 index);
-				}
-			} else if (entry.cmd_code == 255 &&
-				  entry.subsys_id == 255) {
-				if (entry.cmd_code_lo <=
-						 cmd_code &&
-						 entry.
-						cmd_code_hi >= cmd_code) {
-					ret = diag_send_dci_pkt(entry, buf, len,
-								 index);
+	/* This is Pkt request/response transaction */
+	if (*(int *)temp > 0) {
+		/* enter this UID into kernel table and return index */
+		index = diag_register_dci_transaction(*(int *)temp);
+		if (index < 0) {
+			pr_alert("diag: registering new DCI transaction failed\n");
+			return DIAG_DCI_NO_REG;
+		}
+		temp += 4;
+		/*
+		 * Check for registered peripheral and fwd pkt to
+		 * appropriate proc
+		 */
+		cmd_code = (int)(*(char *)temp);
+		temp++;
+		subsys_id = (int)(*(char *)temp);
+		temp++;
+		subsys_cmd_code = *(uint16_t *)temp;
+		temp += 2;
+		pr_debug("diag: %d %d %d", cmd_code, subsys_id,
+			subsys_cmd_code);
+		for (i = 0; i < diag_max_reg; i++) {
+			entry = driver->table[i];
+			if (entry.process_id != NO_PROCESS) {
+				if (entry.cmd_code == cmd_code &&
+					entry.subsys_id == subsys_id &&
+					entry.cmd_code_lo <= subsys_cmd_code &&
+					entry.cmd_code_hi >= subsys_cmd_code) {
+					ret = diag_send_dci_pkt(entry, buf,
+								len, index);
+				} else if (entry.cmd_code == 255
+					  && cmd_code == 75) {
+					if (entry.subsys_id == subsys_id &&
+						entry.cmd_code_lo <=
+						subsys_cmd_code &&
+						entry.cmd_code_hi >=
+						subsys_cmd_code) {
+						ret = diag_send_dci_pkt(entry,
+							buf, len, index);
+					}
+				} else if (entry.cmd_code == 255 &&
+					entry.subsys_id == 255) {
+					if (entry.cmd_code_lo <= cmd_code &&
+						entry.cmd_code_hi >=
+							cmd_code) {
+						ret = diag_send_dci_pkt(entry,
+							buf, len, index);
+					}
 				}
 			}
 		}
+	} else if (*(int *)temp == DCI_LOG_TYPE) {
+		/* find client id and table */
+		for (i = 0; i < MAX_DCI_CLIENTS; i++) {
+			if (driver->dci_client_tbl[i].client->tgid ==
+							 current->tgid) {
+				found = 1;
+				break;
+			}
+		}
+		if (!found) {
+			pr_err("diag: dci client not registered/found\n");
+			return ret;
+		}
+		/* Extract each log code and put in client table */
+		temp += 4;
+		set_mask = *(int *)temp;
+		temp += 4;
+		num_codes = *(int *)temp;
+		temp += 4;
+
+		head_log_mask_ptr = driver->dci_client_tbl[i].dci_log_mask;
+		pr_debug("diag: head of dci log mask %p\n", head_log_mask_ptr);
+		count = 0; /* iterator for extracting log codes */
+		while (count < num_codes) {
+			log_code = *(uint16_t *)temp;
+			equip_id = LOG_GET_EQUIP_ID(log_code);
+			item_num = LOG_GET_ITEM_NUM(log_code);
+			byte_index = item_num/8 + 2;
+			byte_mask = 0x01 << (item_num % 8);
+			/*
+			 * Parse through log mask table and find
+			 * relevant range
+			 */
+			log_mask_ptr = head_log_mask_ptr;
+			found = 0;
+			while (log_mask_ptr) {
+				if (*log_mask_ptr == equip_id) {
+					found = 1;
+					pr_debug("diag: find equip id = %x at %p\n",
+						 equip_id, log_mask_ptr);
+					break;
+				} else {
+					pr_debug("diag: did not find equip id = %x at %p\n",
+						 equip_id, log_mask_ptr);
+					log_mask_ptr += 514;
+				}
+			}
+			if (!found) {
+				pr_err("diag: dci equip id not found\n");
+				return ret;
+			}
+			*(log_mask_ptr+1) = 1; /* set the dirty byte */
+			log_mask_ptr = log_mask_ptr + byte_index;
+			if (set_mask)
+				*log_mask_ptr |= byte_mask;
+			else
+				*log_mask_ptr &= ~byte_mask;
+			temp += 2;
+			count++;
+			ret = DIAG_DCI_NO_ERROR;
+		}
+		/* add to cumulative mask */
+		update_dci_cumulative_log_mask(i);
+		/* send updated mask to peripherals */
+		diag_send_dci_log_mask(driver->ch_cntl);
+	} else if (*(int *)temp == DCI_EVENT_TYPE) {
+		/* find client id and table */
+		for (i = 0; i < MAX_DCI_CLIENTS; i++) {
+			if (driver->dci_client_tbl[i].client->tgid ==
+							 current->tgid) {
+				found = 1;
+				break;
+			}
+		}
+		if (!found) {
+			pr_err("diag: dci client not registered/found\n");
+			return ret;
+		}
+		/* Extract each log code and put in client table */
+		temp += 4;
+		set_mask = *(int *)temp;
+		temp += 4;
+		num_codes = *(int *)temp;
+		temp += 4;
+
+		event_mask_ptr = driver->dci_client_tbl[i].dci_event_mask;
+		pr_debug("diag: head of dci event mask %p\n", event_mask_ptr);
+		count = 0; /* iterator for extracting log codes */
+		while (count < num_codes) {
+			event_id = *(int *)temp;
+			byte_index = event_id/8;
+			bit_index = event_id % 8;
+			byte_mask = 0x1 << bit_index;
+			/*
+			 * Parse through event mask table and set
+			 * relevant byte & bit combination
+			 */
+			if (set_mask)
+				*(event_mask_ptr + byte_index) |= byte_mask;
+			else
+				*(event_mask_ptr + byte_index) &= ~byte_mask;
+			temp += sizeof(int);
+			count++;
+			ret = DIAG_DCI_NO_ERROR;
+		}
+		/* add to cumulative mask */
+		update_dci_cumulative_event_mask(i);
+		/* send updated mask to peripherals */
+		diag_send_dci_event_mask(driver->ch_cntl);
+	} else {
+		pr_alert("diag: Incorrect DCI transaction\n");
 	}
 	return ret;
 }
 
+void update_dci_cumulative_event_mask(int client_index)
+{
+	int i;
+	uint8_t *update_ptr = dci_cumulative_event_mask;
+	uint8_t *event_mask_ptr;
+
+	mutex_lock(&dci_event_mask_mutex);
+	event_mask_ptr = driver->dci_client_tbl[client_index].dci_event_mask;
+	for (i = 0; i < DCI_EVENT_MASK_SIZE; i++)
+		*(update_ptr+i) |= *(event_mask_ptr+i);
+	mutex_unlock(&dci_event_mask_mutex);
+}
+
+void diag_send_dci_event_mask(smd_channel_t *ch)
+{
+	void *buf = driver->buf_event_mask_update;
+	int header_size = sizeof(struct diag_ctrl_event_mask);
+	int wr_size = -ENOMEM, retry_count = 0, timer;
+
+	mutex_lock(&driver->diag_cntl_mutex);
+	/* send event mask update */
+	driver->event_mask->cmd_type = DIAG_CTRL_MSG_EVENT_MASK;
+	driver->event_mask->data_len = 7 + DCI_EVENT_MASK_SIZE;
+	driver->event_mask->stream_id = DCI_MASK_STREAM;
+	driver->event_mask->status = 3; /* status for valid mask */
+	driver->event_mask->event_config = 1; /* event config */
+	driver->event_mask->event_mask_size = DCI_EVENT_MASK_SIZE;
+	memcpy(buf, driver->event_mask, header_size);
+	memcpy(buf+header_size, dci_cumulative_event_mask, DCI_EVENT_MASK_SIZE);
+	if (ch) {
+		while (retry_count < 3) {
+			wr_size = smd_write(ch, buf,
+					 header_size + DCI_EVENT_MASK_SIZE);
+			if (wr_size == -ENOMEM) {
+				retry_count++;
+				for (timer = 0; timer < 5; timer++)
+					udelay(2000);
+			} else {
+				break;
+			}
+		}
+		if (wr_size != header_size + DCI_EVENT_MASK_SIZE)
+			pr_err("diag: error writing dci event mask %d, tried %d\n",
+				 wr_size, header_size + DCI_EVENT_MASK_SIZE);
+	} else
+		pr_err("diag: ch not valid for dci event mask update\n");
+	mutex_unlock(&driver->diag_cntl_mutex);
+}
+
+void update_dci_cumulative_log_mask(int client_index)
+{
+	int i, j;
+	uint8_t *update_ptr = dci_cumulative_log_mask;
+	uint8_t *log_mask_ptr =
+	driver->dci_client_tbl[client_index].dci_log_mask;
+
+	mutex_lock(&dci_log_mask_mutex);
+	*update_ptr = 0; /* add first equip id */
+	/* skip the first equip id */
+	update_ptr++; log_mask_ptr++;
+	for (i = 0; i < 16; i++) {
+		for (j = 0; j < 513; j++) {
+			*update_ptr |= *log_mask_ptr;
+			update_ptr++;
+			log_mask_ptr++;
+		}
+		*update_ptr = i+1;
+		update_ptr++;
+		log_mask_ptr++;
+	}
+	mutex_unlock(&dci_log_mask_mutex);
+}
+
+void diag_send_dci_log_mask(smd_channel_t *ch)
+{
+	void *buf = driver->buf_log_mask_update;
+	int header_size = sizeof(struct diag_ctrl_log_mask);
+	uint8_t *log_mask_ptr = dci_cumulative_log_mask;
+	int i, wr_size = -ENOMEM, retry_count = 0, timer;
+
+	mutex_lock(&driver->diag_cntl_mutex);
+	for (i = 0; i < 16; i++) {
+		driver->log_mask->cmd_type = DIAG_CTRL_MSG_LOG_MASK;
+		driver->log_mask->num_items = 512;
+		driver->log_mask->data_len  = 11 + 512;
+		driver->log_mask->stream_id = DCI_MASK_STREAM;
+		driver->log_mask->status = 3; /* status for valid mask */
+		driver->log_mask->equip_id = *log_mask_ptr;
+		driver->log_mask->log_mask_size = 512;
+		memcpy(buf, driver->log_mask, header_size);
+		memcpy(buf+header_size, log_mask_ptr+2, 512);
+		/* if dirty byte is set and channel is valid */
+		if (ch && *(log_mask_ptr+1)) {
+			while (retry_count < 3) {
+				wr_size = smd_write(ch, buf, header_size + 512);
+				if (wr_size == -ENOMEM) {
+					retry_count++;
+					for (timer = 0; timer < 5; timer++)
+						udelay(2000);
+				} else
+					break;
+			}
+			if (wr_size != header_size + 512)
+				pr_err("diag: dci log mask update failed %d, tried %d",
+					 wr_size, header_size + 512);
+			else {
+				*(log_mask_ptr+1) = 0; /* clear dirty byte */
+				pr_debug("diag: updated dci log equip ID %d\n",
+						 *log_mask_ptr);
+			}
+		}
+		log_mask_ptr += 514;
+	}
+	mutex_unlock(&driver->diag_cntl_mutex);
+}
+
+void create_dci_log_mask_tbl(unsigned char *tbl_buf)
+{
+	uint8_t i; int count = 0;
+
+	/* create hard coded table for log mask with 16 categories */
+	for (i = 0; i < 16; i++) {
+		*(uint8_t *)tbl_buf = i;
+		pr_debug("diag: put value %x at %p\n", i, tbl_buf);
+		memset(tbl_buf+1, 0, 513); /* set dirty bit as 0 */
+		tbl_buf += 514;
+		count += 514;
+	}
+}
+
+void create_dci_event_mask_tbl(unsigned char *tbl_buf)
+{
+	memset(tbl_buf, 0, 512);
+}
+
 static int diag_dci_runtime_suspend(struct device *dev)
 {
 	dev_dbg(dev, "pm_runtime: suspending...\n");
@@ -290,10 +730,10 @@
 struct platform_driver msm_diag_dci_driver = {
 	.probe = diag_dci_probe,
 	.driver = {
-		   .name = "DIAG_2",
-		   .owner = THIS_MODULE,
-		   .pm   = &diag_dci_dev_pm_ops,
-		   },
+			.name = "DIAG_2",
+			.owner = THIS_MODULE,
+			.pm   = &diag_dci_dev_pm_ops,
+	},
 };
 
 int diag_dci_init(void)
@@ -305,6 +745,8 @@
 	driver->num_dci_client = 0;
 	driver->in_busy_dci = 0;
 	mutex_init(&driver->dci_mutex);
+	mutex_init(&dci_log_mask_mutex);
+	mutex_init(&dci_event_mask_mutex);
 	if (driver->buf_in_dci == NULL) {
 		driver->buf_in_dci = kzalloc(IN_BUF_SIZE, GFP_KERNEL);
 		if (driver->buf_in_dci == NULL)
@@ -316,16 +758,10 @@
 		if (driver->write_ptr_dci == NULL)
 			goto err;
 	}
-	if (driver->dci_tbl == NULL) {
-		driver->dci_tbl = kzalloc(dci_max_reg *
-			sizeof(struct diag_dci_tbl), GFP_KERNEL);
-		if (driver->dci_tbl == NULL)
-			goto err;
-	}
-	if (driver->dci_notify_tbl == NULL) {
-		driver->dci_notify_tbl = kzalloc(MAX_DCI_CLIENT *
-			sizeof(struct dci_notification_tbl), GFP_KERNEL);
-		if (driver->dci_notify_tbl == NULL)
+	if (driver->req_tracking_tbl == NULL) {
+		driver->req_tracking_tbl = kzalloc(dci_max_reg *
+			sizeof(struct dci_pkt_req_tracking_tbl), GFP_KERNEL);
+		if (driver->req_tracking_tbl == NULL)
 			goto err;
 	}
 	if (driver->apps_dci_buf == NULL) {
@@ -333,6 +769,13 @@
 		if (driver->apps_dci_buf == NULL)
 			goto err;
 	}
+	if (driver->dci_client_tbl == NULL) {
+		driver->dci_client_tbl = kzalloc(MAX_DCI_CLIENTS *
+			sizeof(struct diag_dci_client_tbl), GFP_KERNEL);
+		if (driver->dci_client_tbl == NULL)
+			goto err;
+	}
+	driver->diag_dci_wq = create_singlethread_workqueue("diag_dci_wq");
 	success = platform_driver_register(&msm_diag_dci_driver);
 	if (success) {
 		pr_err("diag: Could not register DCI driver\n");
@@ -341,11 +784,13 @@
 	return DIAG_DCI_NO_ERROR;
 err:
 	pr_err("diag: Could not initialize diag DCI buffers");
-	kfree(driver->dci_tbl);
-	kfree(driver->dci_notify_tbl);
+	kfree(driver->req_tracking_tbl);
+	kfree(driver->dci_client_tbl);
 	kfree(driver->apps_dci_buf);
 	kfree(driver->buf_in_dci);
 	kfree(driver->write_ptr_dci);
+	if (driver->diag_dci_wq)
+		destroy_workqueue(driver->diag_dci_wq);
 	return DIAG_DCI_NO_REG;
 }
 
@@ -354,10 +799,10 @@
 	smd_close(driver->ch_dci);
 	driver->ch_dci = 0;
 	platform_driver_unregister(&msm_diag_dci_driver);
-	kfree(driver->dci_tbl);
-	kfree(driver->dci_notify_tbl);
+	kfree(driver->req_tracking_tbl);
+	kfree(driver->dci_client_tbl);
 	kfree(driver->apps_dci_buf);
 	kfree(driver->buf_in_dci);
 	kfree(driver->write_ptr_dci);
+	destroy_workqueue(driver->diag_dci_wq);
 }
-
diff --git a/drivers/char/diag/diag_dci.h b/drivers/char/diag/diag_dci.h
index b70efe3..afcabcc 100644
--- a/drivers/char/diag/diag_dci.h
+++ b/drivers/char/diag/diag_dci.h
@@ -11,21 +11,59 @@
  */
 #ifndef DIAG_DCI_H
 #define DIAG_DCI_H
-#define MAX_DCI_CLIENT 10
-#define DCI_CMD_CODE 0x93
+
+#define MAX_DCI_CLIENTS		10
+#define DCI_PKT_RSP_CODE	0x93
+#define DCI_DELAYED_RSP_CODE	0x94
+#define LOG_CMD_CODE		0x10
+#define EVENT_CMD_CODE		0x60
+#define DCI_PKT_RSP_TYPE	0
+#define DCI_LOG_TYPE		-1
+#define DCI_EVENT_TYPE		-2
+#define SET_LOG_MASK		1
+#define DISABLE_LOG_MASK	0
+#define MAX_EVENT_SIZE		100
+
+/* 16 log code categories, each has:
+ * 1 bytes equip id + 1 dirty byte + 512 byte max log mask
+ */
+#define DCI_LOG_MASK_SIZE		(16*514)
+#define DCI_EVENT_MASK_SIZE		512
+#define DCI_MASK_STREAM			2
+#define DCI_MAX_LOG_CODES		16
+#define DCI_MAX_ITEMS_PER_LOG_CODE	512
 
 extern unsigned int dci_max_reg;
 extern unsigned int dci_max_clients;
-struct diag_dci_tbl {
+
+struct dci_pkt_req_tracking_tbl {
 	int pid;
 	int uid;
 	int tag;
 };
 
-struct dci_notification_tbl {
+struct diag_dci_client_tbl {
 	struct task_struct *client;
 	uint16_t list; /* bit mask */
 	int signal_type;
+	unsigned char dci_log_mask[DCI_LOG_MASK_SIZE];
+	unsigned char dci_event_mask[DCI_EVENT_MASK_SIZE];
+	unsigned char *dci_data;
+	int data_len;
+	int total_capacity;
+	int dropped_logs;
+	int dropped_events;
+	int received_logs;
+	int received_events;
+};
+
+/* This is used for DCI health stats */
+struct diag_dci_health_stats {
+	int dropped_logs;
+	int dropped_events;
+	int received_logs;
+	int received_events;
+	int reset_status;
 };
 
 enum {
@@ -41,7 +79,18 @@
 int diag_dci_init(void);
 void diag_dci_exit(void);
 void diag_read_smd_dci_work_fn(struct work_struct *);
-int diag_process_dci_client(unsigned char *buf, int len);
+int diag_process_dci_transaction(unsigned char *buf, int len);
 int diag_send_dci_pkt(struct diag_master_table entry, unsigned char *buf,
 							 int len, int index);
+void extract_dci_pkt_rsp(unsigned char *buf);
+/* DCI Log streaming functions */
+void create_dci_log_mask_tbl(unsigned char *tbl_buf);
+void update_dci_cumulative_log_mask(int client_index);
+void diag_send_dci_log_mask(smd_channel_t *ch);
+void extract_dci_log(unsigned char *buf);
+/* DCI event streaming functions */
+void update_dci_cumulative_event_mask(int client_index);
+void diag_send_dci_event_mask(smd_channel_t *ch);
+void extract_dci_events(unsigned char *buf);
+void create_dci_event_mask_tbl(unsigned char *tbl_buf);
 #endif
diff --git a/drivers/char/diag/diagchar.h b/drivers/char/diag/diagchar.h
index a8e33b5..28d0565 100644
--- a/drivers/char/diag/diagchar.h
+++ b/drivers/char/diag/diagchar.h
@@ -140,6 +140,7 @@
 	int ref_count;
 	struct mutex diagchar_mutex;
 	wait_queue_head_t wait_q;
+	wait_queue_head_t smd_wait_q;
 	struct diag_client_map *client_map;
 	int *data_ready;
 	int num_clients;
@@ -147,14 +148,15 @@
 	struct diag_write_device *buf_tbl;
 	int use_device_tree;
 	/* DCI related variables */
-	struct diag_dci_tbl *dci_tbl;
-	struct dci_notification_tbl *dci_notify_tbl;
+	struct dci_pkt_req_tracking_tbl *req_tracking_tbl;
+	struct diag_dci_client_tbl *dci_client_tbl;
 	int dci_tag;
 	int dci_client_id;
 	struct mutex dci_mutex;
 	int num_dci_client;
 	unsigned char *apps_dci_buf;
 	int dci_state;
+	struct workqueue_struct *diag_dci_wq;
 	/* Memory pool parameters */
 	unsigned int itemsize;
 	unsigned int poolsize;
@@ -255,6 +257,7 @@
 	int mask_check;
 	int logging_process_id;
 	struct task_struct *socket_process;
+	struct task_struct *callback_process;
 #ifdef CONFIG_DIAG_SDIO_PIPE
 	unsigned char *buf_in_sdio;
 	unsigned char *usb_buf_mdm_out;
diff --git a/drivers/char/diag/diagchar_core.c b/drivers/char/diag/diagchar_core.c
index 5c6cdc6..92efd94 100644
--- a/drivers/char/diag/diagchar_core.c
+++ b/drivers/char/diag/diagchar_core.c
@@ -246,22 +246,32 @@
 	int i = 0;
 	struct diagchar_priv *diagpriv_data = file->private_data;
 
+	pr_debug("diag: process exit %s\n", current->comm);
 	if (!(file->private_data)) {
 		pr_alert("diag: Invalid file pointer");
 		return -ENOMEM;
 	}
-
-	/* clean up any DCI registrations for this client
+	/* clean up any DCI registrations, if this is a DCI client
 	* This will specially help in case of ungraceful exit of any DCI client
 	* This call will remove any pending registrations of such client
 	*/
-	diagchar_ioctl(NULL, DIAG_IOCTL_DCI_DEINIT, 0);
-
+	for (i = 0; i < MAX_DCI_CLIENTS; i++) {
+		if (driver->dci_client_tbl[i].client &&
+			driver->dci_client_tbl[i].client->tgid ==
+							 current->tgid) {
+			diagchar_ioctl(NULL, DIAG_IOCTL_DCI_DEINIT, 0);
+			break;
+		}
+	}
 	/* If the exiting process is the socket process */
 	if (driver->socket_process &&
 		(driver->socket_process->tgid == current->tgid)) {
 		driver->socket_process = NULL;
 	}
+	if (driver->callback_process &&
+		(driver->callback_process->tgid == current->tgid)) {
+		driver->callback_process = NULL;
+	}
 
 #ifdef CONFIG_DIAG_OVER_USB
 	/* If the SD logging process exits, change logging to USB mode */
@@ -377,7 +387,9 @@
 	int success = -1;
 	void *temp_buf;
 	uint16_t support_list = 0;
-	struct dci_notification_tbl *notify_params;
+	struct diag_dci_client_tbl *params =
+		kzalloc(sizeof(struct diag_dci_client_tbl), GFP_KERNEL);
+	struct diag_dci_health_stats stats;
 	int status;
 
 	if (iocmd == DIAG_IOCTL_COMMAND_REG) {
@@ -449,20 +461,37 @@
 	} else if (iocmd == DIAG_IOCTL_DCI_REG) {
 		if (driver->dci_state == DIAG_DCI_NO_REG)
 			return DIAG_DCI_NO_REG;
-		if (driver->num_dci_client >= MAX_DCI_CLIENT)
+		if (driver->num_dci_client >= MAX_DCI_CLIENTS)
 			return DIAG_DCI_NO_REG;
-		notify_params = (struct dci_notification_tbl *) ioarg;
+		if (copy_from_user(params, (void *)ioarg,
+				 sizeof(struct diag_dci_client_tbl)))
+			return -EFAULT;
 		mutex_lock(&driver->dci_mutex);
+		if (!(driver->num_dci_client))
+			driver->in_busy_dci = 0;
 		driver->num_dci_client++;
 		pr_debug("diag: id = %d\n", driver->dci_client_id);
 		driver->dci_client_id++;
-		for (i = 0; i < MAX_DCI_CLIENT; i++) {
-			if (driver->dci_notify_tbl[i].client == NULL) {
-				driver->dci_notify_tbl[i].client = current;
-				driver->dci_notify_tbl[i].list =
-							 notify_params->list;
-				driver->dci_notify_tbl[i].signal_type =
-					 notify_params->signal_type;
+		for (i = 0; i < MAX_DCI_CLIENTS; i++) {
+			if (driver->dci_client_tbl[i].client == NULL) {
+				driver->dci_client_tbl[i].client = current;
+				driver->dci_client_tbl[i].list =
+							 params->list;
+				driver->dci_client_tbl[i].signal_type =
+					 params->signal_type;
+				create_dci_log_mask_tbl(driver->
+					dci_client_tbl[i].dci_log_mask);
+				create_dci_event_mask_tbl(driver->
+					dci_client_tbl[i].dci_event_mask);
+				driver->dci_client_tbl[i].data_len = 0;
+				driver->dci_client_tbl[i].dci_data =
+					 kzalloc(IN_BUF_SIZE, GFP_KERNEL);
+				driver->dci_client_tbl[i].total_capacity =
+								 IN_BUF_SIZE;
+				driver->dci_client_tbl[i].dropped_logs = 0;
+				driver->dci_client_tbl[i].dropped_events = 0;
+				driver->dci_client_tbl[i].received_logs = 0;
+				driver->dci_client_tbl[i].received_events = 0;
 				break;
 			}
 		}
@@ -472,36 +501,52 @@
 		success = -1;
 		/* Delete this process from DCI table */
 		mutex_lock(&driver->dci_mutex);
-		for (i = 0; i < dci_max_reg; i++) {
-			if (driver->dci_tbl[i].pid == current->tgid) {
-				pr_debug("diag: delete %d\n", current->tgid);
-				driver->dci_tbl[i].pid = 0;
+		for (i = 0; i < dci_max_reg; i++)
+			if (driver->req_tracking_tbl[i].pid == current->tgid)
+				driver->req_tracking_tbl[i].pid = 0;
+		for (i = 0; i < MAX_DCI_CLIENTS; i++) {
+			if (driver->dci_client_tbl[i].client &&
+			driver->dci_client_tbl[i].client->tgid ==
+							 current->tgid) {
+				driver->dci_client_tbl[i].client = NULL;
 				success = i;
-			}
-		}
-		for (i = 0; i < MAX_DCI_CLIENT; i++) {
-			if (driver->dci_notify_tbl[i].client == current) {
-				driver->dci_notify_tbl[i].client = NULL;
 				break;
 			}
 		}
-		/* if any registrations were deleted successfully OR a valid
-		   client_id was sent in DEINIT call , then its DCI client */
-		if (success >= 0 || ioarg)
+		if (success >= 0)
 			driver->num_dci_client--;
-		driver->num_dci_client--;
 		mutex_unlock(&driver->dci_mutex);
-		for (i = 0; i < dci_max_reg; i++)
-			if (driver->dci_tbl[i].pid != 0)
-				pr_debug("diag: PID = %d, UID = %d, tag = %d\n",
-	driver->dci_tbl[i].pid, driver->dci_tbl[i].uid, driver->dci_tbl[i].tag);
-		pr_debug("diag: complete deleting registrations\n");
 		return success;
 	} else if (iocmd == DIAG_IOCTL_DCI_SUPPORT) {
 		if (driver->ch_dci)
 			support_list = support_list | DIAG_CON_MPSS;
 		*(uint16_t *)ioarg = support_list;
 		return DIAG_DCI_NO_ERROR;
+	} else if (iocmd == DIAG_IOCTL_DCI_HEALTH_STATS) {
+		if (copy_from_user(&stats, (void *)ioarg,
+				 sizeof(struct diag_dci_health_stats)))
+			return -EFAULT;
+		for (i = 0; i < MAX_DCI_CLIENTS; i++) {
+			params = &(driver->dci_client_tbl[i]);
+			if (params->client &&
+				params->client->tgid == current->tgid) {
+				stats.dropped_logs = params->dropped_logs;
+				stats.dropped_events = params->dropped_events;
+				stats.received_logs = params->received_logs;
+				stats.received_events = params->received_events;
+				if (stats.reset_status) {
+					params->dropped_logs = 0;
+					params->dropped_events = 0;
+					params->received_logs = 0;
+					params->received_events = 0;
+				}
+				break;
+			}
+		}
+		if (copy_to_user((void *)ioarg, &stats,
+				   sizeof(struct diag_dci_health_stats)))
+			return -EFAULT;
+		return DIAG_DCI_NO_ERROR;
 	} else if (iocmd == DIAG_IOCTL_LSM_DEINIT) {
 		for (i = 0; i < driver->num_clients; i++)
 			if (driver->client_map[i].pid == current->tgid)
@@ -515,6 +560,11 @@
 		mutex_lock(&driver->diagchar_mutex);
 		temp = driver->logging_mode;
 		driver->logging_mode = (int)ioarg;
+		if (temp == driver->logging_mode) {
+			mutex_unlock(&driver->diagchar_mutex);
+			pr_alert("diag: forbidden logging change requested\n");
+			return 0;
+		}
 		if (driver->logging_mode == MEMORY_DEVICE_MODE) {
 			diag_clear_hsic_tbl();
 			driver->mask_check = 1;
@@ -533,17 +583,17 @@
 				}
 			}
 		}
-		if (driver->logging_mode == UART_MODE) {
+		if (driver->logging_mode == UART_MODE ||
+			driver->logging_mode == SOCKET_MODE ||
+			driver->logging_mode == CALLBACK_MODE) {
 			diag_clear_hsic_tbl();
 			driver->mask_check = 0;
 			driver->logging_mode = MEMORY_DEVICE_MODE;
 		}
-		if (driver->logging_mode == SOCKET_MODE) {
-			diag_clear_hsic_tbl();
+		if (driver->logging_mode == SOCKET_MODE)
 			driver->socket_process = current;
-			driver->mask_check = 0;
-			driver->logging_mode = MEMORY_DEVICE_MODE;
-		}
+		if (driver->logging_mode == CALLBACK_MODE)
+			driver->callback_process = current;
 		driver->logging_process_id = current->tgid;
 		mutex_unlock(&driver->diagchar_mutex);
 		if (temp == MEMORY_DEVICE_MODE && driver->logging_mode
@@ -653,6 +703,7 @@
 static int diagchar_read(struct file *file, char __user *buf, size_t count,
 			  loff_t *ppos)
 {
+	struct diag_dci_client_tbl *entry;
 	int index = -1, i = 0, ret = 0;
 	int num_data = 0, data_type;
 #if defined(CONFIG_DIAG_SDIO_PIPE) || defined(CONFIG_DIAG_BRIDGE_CODE)
@@ -672,7 +723,7 @@
 				  driver->data_ready[index]);
 	mutex_lock(&driver->diagchar_mutex);
 
-	if ((driver->data_ready[index] & USER_SPACE_LOG_TYPE) && (driver->
+	if ((driver->data_ready[index] & USER_SPACE_DATA_TYPE) && (driver->
 					logging_mode == MEMORY_DEVICE_MODE)) {
 #ifdef CONFIG_DIAG_BRIDGE_CODE
 		unsigned long spin_lock_flags;
@@ -681,7 +732,7 @@
 
 		pr_debug("diag: process woken up\n");
 		/*Copy the type of data being passed*/
-		data_type = driver->data_ready[index] & USER_SPACE_LOG_TYPE;
+		data_type = driver->data_ready[index] & USER_SPACE_DATA_TYPE;
 		COPY_USER_SPACE_OR_EXIT(buf, data_type, 4);
 		/* place holder for number of data field */
 		ret += 4;
@@ -884,7 +935,7 @@
 		/* copy number of data fields */
 		COPY_USER_SPACE_OR_EXIT(buf+4, num_data, 4);
 		ret -= 4;
-		driver->data_ready[index] ^= USER_SPACE_LOG_TYPE;
+		driver->data_ready[index] ^= USER_SPACE_DATA_TYPE;
 		if (driver->ch)
 			queue_work(driver->diag_wq,
 					 &(driver->diag_read_smd_work));
@@ -901,10 +952,10 @@
 #endif
 		APPEND_DEBUG('n');
 		goto exit;
-	} else if (driver->data_ready[index] & USER_SPACE_LOG_TYPE) {
+	} else if (driver->data_ready[index] & USER_SPACE_DATA_TYPE) {
 		/* In case, the thread wakes up and the logging mode is
 		not memory device any more, the condition needs to be cleared */
-		driver->data_ready[index] ^= USER_SPACE_LOG_TYPE;
+		driver->data_ready[index] ^= USER_SPACE_DATA_TYPE;
 	}
 
 	if (driver->data_ready[index] & DEINIT_TYPE) {
@@ -956,23 +1007,26 @@
 	}
 
 	if (driver->data_ready[index] & DCI_DATA_TYPE) {
-		/*Copy the type of data being passed*/
+		/* Copy the type of data being passed */
 		data_type = driver->data_ready[index] & DCI_DATA_TYPE;
 		COPY_USER_SPACE_OR_EXIT(buf, data_type, 4);
-		COPY_USER_SPACE_OR_EXIT(buf+4,
-			 driver->write_ptr_dci->length, 4);
-		/* check delayed vs immediate response */
-		if (*(uint8_t *)(driver->buf_in_dci+4) == DCI_CMD_CODE)
-			COPY_USER_SPACE_OR_EXIT(buf+8,
-		*(driver->buf_in_dci + 5), driver->write_ptr_dci->length);
-		else
-			COPY_USER_SPACE_OR_EXIT(buf+8,
-		*(driver->buf_in_dci + 8), driver->write_ptr_dci->length);
-		driver->in_busy_dci = 0;
+		/* check the current client and copy its data */
+		for (i = 0; i < MAX_DCI_CLIENTS; i++) {
+			entry = &(driver->dci_client_tbl[i]);
+			if (entry && (current->tgid == entry->client->tgid)) {
+				COPY_USER_SPACE_OR_EXIT(buf+4,
+						entry->data_len, 4);
+				COPY_USER_SPACE_OR_EXIT(buf+8,
+					 *(entry->dci_data), entry->data_len);
+				entry->data_len = 0;
+				break;
+			}
+		}
 		driver->data_ready[index] ^= DCI_DATA_TYPE;
+		driver->in_busy_dci = 0;
 		if (driver->ch_dci)
-			queue_work(driver->diag_wq,
-				 &(driver->diag_read_smd_dci_work));
+			queue_work(driver->diag_dci_wq,
+				&(driver->diag_read_smd_dci_work));
 		goto exit;
 	}
 exit:
@@ -981,7 +1035,7 @@
 }
 
 static int diagchar_write(struct file *file, const char __user *buf,
-			      size_t count, loff_t *ppos)
+				size_t count, loff_t *ppos)
 {
 	int err, ret = 0, pkt_type;
 	bool mdm_mask = false;
@@ -1011,11 +1065,11 @@
 			pr_alert("diag: copy failed for DCI data\n");
 			return DIAG_DCI_SEND_DATA_FAIL;
 		}
-		err = diag_process_dci_client(driver->user_space_data,
+		err = diag_process_dci_transaction(driver->user_space_data,
 							payload_size);
 		return err;
 	}
-	if (pkt_type == USER_SPACE_LOG_TYPE) {
+	if (pkt_type == USER_SPACE_DATA_TYPE) {
 		err = copy_from_user(driver->user_space_data, buf + 4,
 							 payload_size);
 		/* Check masks for On-Device logging */
@@ -1398,9 +1452,11 @@
 		driver->num_clients = max_clients;
 		driver->logging_mode = USB_MODE;
 		driver->socket_process = NULL;
+		driver->callback_process = NULL;
 		driver->mask_check = 0;
 		mutex_init(&driver->diagchar_mutex);
 		init_waitqueue_head(&driver->wait_q);
+		init_waitqueue_head(&driver->smd_wait_q);
 		INIT_WORK(&(driver->diag_drain_work), diag_drain_work_fn);
 		INIT_WORK(&(driver->diag_read_smd_work), diag_read_smd_work_fn);
 		INIT_WORK(&(driver->diag_read_smd_cntl_work),
diff --git a/drivers/char/diag/diagfwd.c b/drivers/char/diag/diagfwd.c
index a537bb3..978b63b 100644
--- a/drivers/char/diag/diagfwd.c
+++ b/drivers/char/diag/diagfwd.c
@@ -115,6 +115,7 @@
 			return APQ8064_TOOLS_ID;
 		case MSM_CPU_8930:
 		case MSM_CPU_8930AA:
+		case MSM_CPU_8930AB:
 			return MSM8930_TOOLS_ID;
 		case MSM_CPU_8974:
 			return MSM8974_TOOLS_ID;
@@ -142,6 +143,7 @@
 	case MSM_CPU_8064AB:
 	case MSM_CPU_8930:
 	case MSM_CPU_8930AA:
+	case MSM_CPU_8930AB:
 	case MSM_CPU_8627:
 	case MSM_CPU_9615:
 	case MSM_CPU_8974:
@@ -160,9 +162,8 @@
 {
 	if (driver->use_device_tree)
 		return 1;
-	else if (cpu_is_msm8960() || cpu_is_msm8930() || cpu_is_msm8930aa() ||
-		cpu_is_msm9615() || cpu_is_apq8064() || cpu_is_msm8627() ||
-		cpu_is_msm8960ab() || cpu_is_apq8064ab())
+	else if (soc_class_is_msm8960() || soc_class_is_msm8930() ||
+		 soc_class_is_apq8064() || cpu_is_msm9615())
 		return 1;
 	else
 		return 0;
@@ -211,8 +212,8 @@
 		 * have their data read/logged.  Detect and remedy this
 		 * situation.
 		 */
-		if ((driver->data_ready[i] & USER_SPACE_LOG_TYPE) == 0) {
-			driver->data_ready[i] |= USER_SPACE_LOG_TYPE;
+		if ((driver->data_ready[i] & USER_SPACE_DATA_TYPE) == 0) {
+			driver->data_ready[i] |= USER_SPACE_DATA_TYPE;
 			pr_debug("diag: Force wakeup of logging process\n");
 			wake_up_interruptible(&driver->wait_q);
 		}
@@ -221,8 +222,9 @@
 
 void __diag_smd_send_req(void)
 {
-	void *buf = NULL;
-	int *in_busy_ptr = NULL;
+	void *buf = NULL, *temp_buf = NULL;
+	int total_recd = 0, r = 0, pkt_len, *in_busy_ptr = NULL;
+	int loop_count = 0;
 	struct diag_request *write_ptr_modem = NULL;
 
 	if (!driver->in_busy_1) {
@@ -236,27 +238,63 @@
 	}
 
 	if (driver->ch && buf) {
-		int r = smd_read_avail(driver->ch);
+		temp_buf = buf;
+		pkt_len = smd_cur_packet_size(driver->ch);
 
-		if (r > IN_BUF_SIZE) {
-			if (r < MAX_IN_BUF_SIZE) {
-				pr_err("diag: SMD sending in "
-						   "packets upto %d bytes", r);
-				buf = krealloc(buf, r, GFP_KERNEL);
-			} else {
-				pr_err("diag: SMD sending in "
-				"packets more than %d bytes", MAX_IN_BUF_SIZE);
+		while (pkt_len && (pkt_len != total_recd)) {
+			loop_count++;
+			r = smd_read_avail(driver->ch);
+			pr_debug("diag: In %s, received pkt %d %d\n",
+				__func__, r, total_recd);
+			if (!r) {
+				/* Nothing to read from SMD */
+				wait_event(driver->smd_wait_q,
+					((driver->ch == 0) ||
+					smd_read_avail(driver->ch)));
+				/* If the smd channel is open */
+				if (driver->ch) {
+					pr_debug("diag: In %s, return from wait_event\n",
+						__func__);
+					continue;
+				} else {
+					pr_debug("diag: In %s, return from wait_event ch closed\n",
+						__func__);
+					return;
+				}
+			}
+			total_recd += r;
+			if (total_recd > IN_BUF_SIZE) {
+				if (total_recd < MAX_IN_BUF_SIZE) {
+					pr_err("diag: In %s, SMD sending in packets up to %d bytes\n",
+						__func__, total_recd);
+					buf = krealloc(buf, total_recd,
+							GFP_KERNEL);
+				} else {
+					pr_err("diag: In %s, SMD sending in packets more than %d bytes\n",
+						__func__, MAX_IN_BUF_SIZE);
+					return;
+				}
+			}
+			if (pkt_len < r) {
+				pr_err("diag: In %s, SMD sending incorrect pkt\n",
+					__func__);
 				return;
 			}
+			if (pkt_len > r)
+				pr_debug("diag: In %s, SMD sending partial pkt %d %d %d %d\n",
+					__func__, pkt_len, r, total_recd,
+					loop_count);
+			/* keep reading for complete packet */
+			smd_read(driver->ch, temp_buf, r);
+			temp_buf += r;
 		}
-		if (r > 0) {
+
+		if (total_recd > 0) {
 			if (!buf)
-				pr_info("Out of diagmem for Modem\n");
+				pr_err("diag: Out of diagmem for Modem\n");
 			else {
-				APPEND_DEBUG('i');
-				smd_read(driver->ch, buf, r);
 				APPEND_DEBUG('j');
-				write_ptr_modem->length = r;
+				write_ptr_modem->length = total_recd;
 				*in_busy_ptr = 1;
 				diag_device_write(buf, MODEM_DATA,
 							 write_ptr_modem);
@@ -319,7 +357,7 @@
 						 driver->logging_process_id)
 				break;
 		if (i < driver->num_clients) {
-			driver->data_ready[i] |= USER_SPACE_LOG_TYPE;
+			driver->data_ready[i] |= USER_SPACE_DATA_TYPE;
 			pr_debug("diag: wake up logging process\n");
 			wake_up_interruptible(&driver->wait_q);
 		} else
@@ -443,9 +481,10 @@
 
 void __diag_smd_wcnss_send_req(void)
 {
-	void *buf = NULL;
-	int *in_busy_wcnss_ptr = NULL;
+	void *buf = NULL, *temp_buf = NULL;
+	int total_recd = 0, r = 0, pkt_len, *in_busy_wcnss_ptr = NULL;
 	struct diag_request *write_ptr_wcnss = NULL;
+	int loop_count = 0;
 
 	if (!driver->in_busy_wcnss_1) {
 		buf = driver->buf_in_wcnss_1;
@@ -458,24 +497,64 @@
 	}
 
 	if (driver->ch_wcnss && buf) {
-		int r = smd_read_avail(driver->ch_wcnss);
-		if (r > IN_BUF_SIZE) {
-			if (r < MAX_IN_BUF_SIZE) {
-				pr_err("diag: wcnss packets > %d bytes", r);
-				buf = krealloc(buf, r, GFP_KERNEL);
-			} else {
-				pr_err("diag: wcnss pkt > %d", MAX_IN_BUF_SIZE);
+		temp_buf = buf;
+		pkt_len = smd_cur_packet_size(driver->ch_wcnss);
+
+		while (pkt_len && (pkt_len != total_recd)) {
+			loop_count++;
+			r = smd_read_avail(driver->ch_wcnss);
+			pr_debug("diag: In %s, received pkt %d %d\n",
+				__func__, r, total_recd);
+			if (!r) {
+				/* Nothing to read from SMD */
+				wait_event(driver->smd_wait_q,
+					((driver->ch_wcnss == 0) ||
+					smd_read_avail(driver->ch_wcnss)));
+				/* If the smd channel is open */
+				if (driver->ch_wcnss) {
+					pr_debug("diag: In %s, return from wait_event\n",
+						__func__);
+					continue;
+				} else {
+					pr_debug("diag: In %s, return from wait_event ch_wcnss closed\n",
+						__func__);
+					return;
+				}
+			}
+			total_recd += r;
+			if (total_recd > IN_BUF_SIZE) {
+				if (total_recd < MAX_IN_BUF_SIZE) {
+					pr_err("diag: In %s, SMD sending in packets up to %d bytes\n",
+						__func__, total_recd);
+					buf = krealloc(buf, total_recd,
+								 GFP_KERNEL);
+				} else {
+					pr_err("diag: In %s, SMD sending in packets more than %d bytes\n",
+						__func__, MAX_IN_BUF_SIZE);
+					return;
+				}
+			}
+			if (pkt_len < r) {
+				pr_err("diag: In %s, SMD sending incorrect pkt\n",
+					__func__);
 				return;
 			}
+			if (pkt_len > r) {
+				pr_debug("diag: In %s, SMD sending partial pkt %d %d %d %d\n",
+					__func__, pkt_len, r, total_recd,
+					loop_count);
+			}
+			/* keep reading for complete packet */
+			smd_read(driver->ch_wcnss, temp_buf, r);
+			temp_buf += r;
 		}
-		if (r > 0) {
+
+		if (total_recd > 0) {
 			if (!buf) {
-				pr_err("Out of diagmem for wcnss\n");
+				pr_err("diag: Out of diagmem for wcnss\n");
 			} else {
-				APPEND_DEBUG('i');
-				smd_read(driver->ch_wcnss, buf, r);
 				APPEND_DEBUG('j');
-				write_ptr_wcnss->length = r;
+				write_ptr_wcnss->length = total_recd;
 				*in_busy_wcnss_ptr = 1;
 				diag_device_write(buf, WCNSS_DATA,
 					 write_ptr_wcnss);
@@ -489,9 +568,10 @@
 
 void __diag_smd_lpass_send_req(void)
 {
-	void *buf = NULL;
-	int *in_busy_lpass_ptr = NULL;
+	void *buf = NULL, *temp_buf = NULL;
+	int total_recd = 0, r = 0, pkt_len, *in_busy_lpass_ptr = NULL;
 	struct diag_request *write_ptr_lpass = NULL;
+	int loop_count = 0;
 
 	if (!driver->in_busy_lpass_1) {
 		buf = driver->buf_in_lpass_1;
@@ -504,27 +584,63 @@
 	}
 
 	if (driver->chlpass && buf) {
-		int r = smd_read_avail(driver->chlpass);
+		temp_buf = buf;
+		pkt_len = smd_cur_packet_size(driver->chlpass);
 
-		if (r > IN_BUF_SIZE) {
-			if (r < MAX_IN_BUF_SIZE) {
-				pr_err("diag: SMD sending in "
-						   "packets upto %d bytes", r);
-				buf = krealloc(buf, r, GFP_KERNEL);
-			} else {
-				pr_err("diag: SMD sending in "
-				"packets more than %d bytes", MAX_IN_BUF_SIZE);
+		while (pkt_len && (pkt_len != total_recd)) {
+			loop_count++;
+			r = smd_read_avail(driver->chlpass);
+			pr_debug("diag: In %s, received pkt %d %d\n",
+				__func__, r, total_recd);
+			if (!r) {
+				/* Nothing to read from SMD */
+				wait_event(driver->smd_wait_q,
+					((driver->chlpass == 0) ||
+					smd_read_avail(driver->chlpass)));
+				/* If the smd channel is open */
+				if (driver->chlpass) {
+					pr_debug("diag: In %s, return from wait_event\n",
+						__func__);
+					continue;
+				} else {
+					pr_debug("diag: In %s, return from wait_event chlpass closed\n",
+						__func__);
+					return;
+				}
+			}
+			total_recd += r;
+			if (total_recd > IN_BUF_SIZE) {
+				if (total_recd < MAX_IN_BUF_SIZE) {
+					pr_err("diag: In %s, SMD sending in packets up to %d bytes\n",
+						__func__, total_recd);
+					buf = krealloc(buf, total_recd,
+								 GFP_KERNEL);
+				} else {
+					pr_err("diag: In %s, SMD sending in packets more than %d bytes\n",
+						__func__, MAX_IN_BUF_SIZE);
+					return;
+				}
+			}
+			if (pkt_len < r) {
+				pr_err("diag: In %s, SMD sending incorrect pkt\n",
+					__func__);
 				return;
 			}
+			if (pkt_len > r)
+				pr_debug("diag: In %s, SMD sending partial pkt %d %d %d %d\n",
+					__func__, pkt_len, r, total_recd,
+					loop_count);
+			/* keep reading for complete packet */
+			smd_read(driver->chlpass, temp_buf, r);
+			temp_buf += r;
 		}
-		if (r > 0) {
+
+		if (total_recd > 0) {
 			if (!buf)
-				printk(KERN_INFO "Out of diagmem for LPASS\n");
+				pr_err("diag: Out of diagmem for LPASS\n");
 			else {
-				APPEND_DEBUG('i');
-				smd_read(driver->chlpass, buf, r);
 				APPEND_DEBUG('j');
-				write_ptr_lpass->length = r;
+				write_ptr_lpass->length = total_recd;
 				*in_busy_lpass_ptr = 1;
 				diag_device_write(buf, LPASS_DATA,
 							 write_ptr_lpass);
@@ -1223,14 +1339,16 @@
 static void diag_smd_notify(void *ctxt, unsigned event)
 {
 	if (event == SMD_EVENT_CLOSE) {
+		driver->ch = 0;
+		wake_up(&driver->smd_wait_q);
 		queue_work(driver->diag_cntl_wq,
 			 &(driver->diag_clean_modem_reg_work));
-		driver->ch = 0;
 		return;
 	} else if (event == SMD_EVENT_OPEN) {
 		if (ch_temp)
 			driver->ch = ch_temp;
 	}
+	wake_up(&driver->smd_wait_q);
 	queue_work(driver->diag_wq, &(driver->diag_read_smd_work));
 }
 
@@ -1238,14 +1356,16 @@
 static void diag_smd_lpass_notify(void *ctxt, unsigned event)
 {
 	if (event == SMD_EVENT_CLOSE) {
+		driver->chlpass = 0;
+		wake_up(&driver->smd_wait_q);
 		queue_work(driver->diag_cntl_wq,
 			 &(driver->diag_clean_lpass_reg_work));
-		driver->chlpass = 0;
 		return;
 	} else if (event == SMD_EVENT_OPEN) {
 		if (chlpass_temp)
 			driver->chlpass = chlpass_temp;
 	}
+	wake_up(&driver->smd_wait_q);
 	queue_work(driver->diag_wq, &(driver->diag_read_smd_lpass_work));
 }
 #endif
@@ -1253,14 +1373,16 @@
 static void diag_smd_wcnss_notify(void *ctxt, unsigned event)
 {
 	if (event == SMD_EVENT_CLOSE) {
+		driver->ch_wcnss = 0;
+		wake_up(&driver->smd_wait_q);
 		queue_work(driver->diag_cntl_wq,
 			 &(driver->diag_clean_wcnss_reg_work));
-		driver->ch_wcnss = 0;
 		return;
 	} else if (event == SMD_EVENT_OPEN) {
 		if (ch_wcnss_temp)
 			driver->ch_wcnss = ch_wcnss_temp;
 	}
+	wake_up(&driver->smd_wait_q);
 	queue_work(driver->diag_wq, &(driver->diag_read_smd_wcnss_work));
 }
 
diff --git a/drivers/coresight/coresight-csr.c b/drivers/coresight/coresight-csr.c
index e9ac904..1f6bd1d 100644
--- a/drivers/coresight/coresight-csr.c
+++ b/drivers/coresight/coresight-csr.c
@@ -86,7 +86,7 @@
 	CSR_UNLOCK(drvdata);
 
 	usbbamctrl = csr_readl(drvdata, CSR_USBBAMCTRL);
-	usbbamctrl = (usbbamctrl & ~0x3) | BLKSIZE_256;
+	usbbamctrl = (usbbamctrl & ~0x3) | BLKSIZE_2048;
 	csr_writel(drvdata, usbbamctrl, CSR_USBBAMCTRL);
 
 	usbflshctrl = csr_readl(drvdata, CSR_USBFLSHCTRL);
diff --git a/drivers/coresight/coresight-etm.c b/drivers/coresight/coresight-etm.c
index 50bae55..e2a16ad 100644
--- a/drivers/coresight/coresight-etm.c
+++ b/drivers/coresight/coresight-etm.c
@@ -25,8 +25,9 @@
 #include <linux/wakelock.h>
 #include <linux/sysfs.h>
 #include <linux/stat.h>
-#include <linux/mutex.h>
+#include <linux/spinlock.h>
 #include <linux/clk.h>
+#include <linux/cpu.h>
 #include <linux/of_coresight.h>
 #include <linux/coresight.h>
 #include <asm/sections.h>
@@ -191,10 +192,12 @@
 	struct device			*dev;
 	struct coresight_device		*csdev;
 	struct clk			*clk;
-	struct mutex			mutex;
+	spinlock_t			spinlock;
 	struct wake_lock		wake_lock;
 	int				cpu;
 	uint8_t				arch;
+	bool				enable;
+	bool				os_unlock;
 	uint8_t				nr_addr_cmp;
 	uint8_t				nr_cntr;
 	uint8_t				nr_ext_inp;
@@ -203,7 +206,6 @@
 	uint8_t				reset;
 	uint32_t			mode;
 	uint32_t			ctrl;
-	uint8_t				ctrl_pwrdwn;
 	uint32_t			trigger_event;
 	uint32_t			startstop_ctrl;
 	uint32_t			enable_event;
@@ -230,12 +232,22 @@
 	uint32_t			ctxid_mask;
 	uint32_t			sync_freq;
 	uint32_t			timestamp_event;
-	uint8_t				pdcr_pwrup;
 	bool				pcsave_impl;
 	bool				pcsave_enable;
 };
 
-static struct etm_drvdata *etm0drvdata;
+static struct etm_drvdata *etmdrvdata[NR_CPUS];
+
+/*
+ * Memory mapped writes to clear os lock are not supported on Krait v1, v2
+ * and OS lock must be unlocked before any memory mapped access, otherwise
+ * memory mapped reads/writes will be invalid.
+ */
+static void etm_os_unlock(void *info)
+{
+	etm_writel_cp14(0x0, ETMOSLAR);
+	isb();
+}
 
 /*
  * ETM clock is derived from the processor clock and gets enabled on a
@@ -339,48 +351,18 @@
 	     etm_readl(drvdata, ETMSR));
 }
 
-static void etm_save_pwrdwn(struct etm_drvdata *drvdata)
-{
-	drvdata->ctrl_pwrdwn = BVAL(etm_readl(drvdata, ETMCR), 0);
-}
-
-static void etm_restore_pwrdwn(struct etm_drvdata *drvdata)
-{
-	uint32_t etmcr;
-
-	etmcr = etm_readl(drvdata, ETMCR);
-	etmcr = (etmcr & ~BIT(0)) | drvdata->ctrl_pwrdwn;
-	etm_writel(drvdata, etmcr, ETMCR);
-}
-
-static void etm_save_pwrup(struct etm_drvdata *drvdata)
-{
-	drvdata->pdcr_pwrup = BVAL(etm_readl_mm(drvdata, ETMPDCR), 3);
-}
-
-static void etm_restore_pwrup(struct etm_drvdata *drvdata)
-{
-	uint32_t etmpdcr;
-
-	etmpdcr = etm_readl_mm(drvdata, ETMPDCR);
-	etmpdcr = (etmpdcr & ~BIT(3)) | (drvdata->pdcr_pwrup << 3);
-	etm_writel_mm(drvdata, etmpdcr, ETMPDCR);
-}
-
 static void etm_enable_pcsave(void *info)
 {
 	struct etm_drvdata *drvdata = info;
 
 	ETM_UNLOCK(drvdata);
 
-	etm_save_pwrup(drvdata);
 	/*
 	 * ETMPDCR is only accessible via memory mapped interface and so use
 	 * it first to enable power/clock to allow subsequent cp14 accesses.
 	 */
 	etm_set_pwrup(drvdata);
 	etm_clr_pwrdwn(drvdata);
-	etm_restore_pwrup(drvdata);
 
 	ETM_LOCK(drvdata);
 }
@@ -391,14 +373,10 @@
 
 	ETM_UNLOCK(drvdata);
 
-	etm_save_pwrup(drvdata);
-	/*
-	 * ETMPDCR is only accessible via memory mapped interface and so use
-	 * it first to enable power/clock to allow subsequent cp14 accesses.
-	 */
-	etm_set_pwrup(drvdata);
-	etm_set_pwrdwn(drvdata);
-	etm_restore_pwrup(drvdata);
+	if (!drvdata->enable) {
+		etm_set_pwrdwn(drvdata);
+		etm_clr_pwrup(drvdata);
+	}
 
 	ETM_LOCK(drvdata);
 }
@@ -416,10 +394,10 @@
 	 * to allow subsequent cp14 accesses.
 	 */
 	etm_set_pwrup(drvdata);
-	etm_save_pwrdwn(drvdata);
 	/*
 	 * Clear power down bit since when this bit is set writes to
-	 * certain registers might be ignored.
+	 * certain registers might be ignored. This is also a pre-requisite
+	 * for trace enable.
 	 */
 	etm_clr_pwrdwn(drvdata);
 	etm_set_prog(drvdata);
@@ -463,7 +441,6 @@
 	etm_writel(drvdata, 0x00000000, ETMVMIDCVR);
 
 	etm_clr_prog(drvdata);
-	etm_restore_pwrdwn(drvdata);
 	ETM_LOCK(drvdata);
 
 	dev_dbg(drvdata->dev, "cpu: %d enable smp call done\n", drvdata->cpu);
@@ -480,17 +457,29 @@
 	if (ret)
 		goto err_clk;
 
-	mutex_lock(&drvdata->mutex);
-	/* executing __etm_enable on the cpu whose ETM is being enabled
+	get_online_cpus();
+	spin_lock(&drvdata->spinlock);
+
+	/*
+	 * Executing __etm_enable on the cpu whose ETM is being enabled
 	 * ensures that register writes occur when cpu is powered.
 	 */
-	smp_call_function_single(drvdata->cpu, __etm_enable, drvdata, 1);
-	mutex_unlock(&drvdata->mutex);
+	ret = smp_call_function_single(drvdata->cpu, __etm_enable, drvdata, 1);
+	if (ret)
+		goto err;
+	drvdata->enable = true;
+
+	spin_unlock(&drvdata->spinlock);
+	put_online_cpus();
 
 	wake_unlock(&drvdata->wake_lock);
 
 	dev_info(drvdata->dev, "ETM tracing enabled\n");
 	return 0;
+err:
+	spin_unlock(&drvdata->spinlock);
+	put_online_cpus();
+	clk_disable_unprepare(drvdata->clk);
 err_clk:
 	wake_unlock(&drvdata->wake_lock);
 	return ret;
@@ -501,20 +490,15 @@
 	struct etm_drvdata *drvdata = info;
 
 	ETM_UNLOCK(drvdata);
-	etm_save_pwrdwn(drvdata);
-	/*
-	 * Clear power down bit since when this bit is set writes to
-	 * certain registers might be ignored.
-	 */
-	etm_clr_pwrdwn(drvdata);
 	etm_set_prog(drvdata);
 
 	/* program trace enable to low by using always false event */
 	etm_writel(drvdata, 0x6F | BIT(14), ETMTEEVR);
 
-	etm_restore_pwrdwn(drvdata);
-	/* Vote for ETM power/clock disable */
-	etm_clr_pwrup(drvdata);
+	if (!drvdata->pcsave_enable) {
+		etm_set_pwrdwn(drvdata);
+		etm_clr_pwrup(drvdata);
+	}
 	ETM_LOCK(drvdata);
 
 	dev_dbg(drvdata->dev, "cpu: %d disable smp call done\n", drvdata->cpu);
@@ -526,12 +510,18 @@
 
 	wake_lock(&drvdata->wake_lock);
 
-	mutex_lock(&drvdata->mutex);
-	/* executing __etm_disable on the cpu whose ETM is being disabled
+	get_online_cpus();
+	spin_lock(&drvdata->spinlock);
+
+	/*
+	 * Executing __etm_disable on the cpu whose ETM is being disabled
 	 * ensures that register writes occur when cpu is powered.
 	 */
 	smp_call_function_single(drvdata->cpu, __etm_disable, drvdata, 1);
-	mutex_unlock(&drvdata->mutex);
+	drvdata->enable = false;
+
+	spin_unlock(&drvdata->spinlock);
+	put_online_cpus();
 
 	clk_disable_unprepare(drvdata->clk);
 
@@ -600,7 +590,7 @@
 	if (sscanf(buf, "%lx", &val) != 1)
 		return -EINVAL;
 
-	mutex_lock(&drvdata->mutex);
+	spin_lock(&drvdata->spinlock);
 	if (val) {
 		drvdata->mode = ETM_MODE_EXCLUDE;
 		drvdata->ctrl = 0x0;
@@ -644,7 +634,7 @@
 			drvdata->sync_freq = 0x80;
 		drvdata->timestamp_event = 0x406F;
 	}
-	mutex_unlock(&drvdata->mutex);
+	spin_unlock(&drvdata->spinlock);
 	return size;
 }
 static DEVICE_ATTR(reset, S_IRUGO | S_IWUSR, etm_show_reset, etm_store_reset);
@@ -667,7 +657,7 @@
 	if (sscanf(buf, "%lx", &val) != 1)
 		return -EINVAL;
 
-	mutex_lock(&drvdata->mutex);
+	spin_lock(&drvdata->spinlock);
 	drvdata->mode = val & ETM_MODE_ALL;
 
 	if (drvdata->mode & ETM_MODE_EXCLUDE)
@@ -694,7 +684,7 @@
 		drvdata->ctrl |= (BIT(14) | BIT(15));
 	else
 		drvdata->ctrl &= ~(BIT(14) | BIT(15));
-	mutex_unlock(&drvdata->mutex);
+	spin_unlock(&drvdata->spinlock);
 
 	return size;
 }
@@ -796,12 +786,13 @@
 	if (val >= drvdata->nr_addr_cmp)
 		return -EINVAL;
 
-	/* Use mutex to ensure index doesn't change while it gets dereferenced
-	 * multiple times within a mutex block elsewhere.
+	/*
+	 * Use spinlock to ensure index doesn't change while it gets
+	 * dereferenced multiple times within a spinlock block elsewhere.
 	 */
-	mutex_lock(&drvdata->mutex);
+	spin_lock(&drvdata->spinlock);
 	drvdata->addr_idx = val;
-	mutex_unlock(&drvdata->mutex);
+	spin_unlock(&drvdata->spinlock);
 	return size;
 }
 static DEVICE_ATTR(addr_idx, S_IRUGO | S_IWUSR, etm_show_addr_idx,
@@ -814,16 +805,16 @@
 	unsigned long val;
 	uint8_t idx;
 
-	mutex_lock(&drvdata->mutex);
+	spin_lock(&drvdata->spinlock);
 	idx = drvdata->addr_idx;
 	if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE ||
 	      drvdata->addr_type[idx] == ETM_ADDR_TYPE_SINGLE)) {
-		mutex_unlock(&drvdata->mutex);
+		spin_unlock(&drvdata->spinlock);
 		return -EPERM;
 	}
 
 	val = drvdata->addr_val[idx];
-	mutex_unlock(&drvdata->mutex);
+	spin_unlock(&drvdata->spinlock);
 	return scnprintf(buf, PAGE_SIZE, "%#lx\n", val);
 }
 
@@ -838,17 +829,17 @@
 	if (sscanf(buf, "%lx", &val) != 1)
 		return -EINVAL;
 
-	mutex_lock(&drvdata->mutex);
+	spin_lock(&drvdata->spinlock);
 	idx = drvdata->addr_idx;
 	if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE ||
 	      drvdata->addr_type[idx] == ETM_ADDR_TYPE_SINGLE)) {
-		mutex_unlock(&drvdata->mutex);
+		spin_unlock(&drvdata->spinlock);
 		return -EPERM;
 	}
 
 	drvdata->addr_val[idx] = val;
 	drvdata->addr_type[idx] = ETM_ADDR_TYPE_SINGLE;
-	mutex_unlock(&drvdata->mutex);
+	spin_unlock(&drvdata->spinlock);
 	return size;
 }
 static DEVICE_ATTR(addr_single, S_IRUGO | S_IWUSR, etm_show_addr_single,
@@ -861,23 +852,23 @@
 	unsigned long val1, val2;
 	uint8_t idx;
 
-	mutex_lock(&drvdata->mutex);
+	spin_lock(&drvdata->spinlock);
 	idx = drvdata->addr_idx;
 	if (idx % 2 != 0) {
-		mutex_unlock(&drvdata->mutex);
+		spin_unlock(&drvdata->spinlock);
 		return -EPERM;
 	}
 	if (!((drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE &&
 	       drvdata->addr_type[idx + 1] == ETM_ADDR_TYPE_NONE) ||
 	      (drvdata->addr_type[idx] == ETM_ADDR_TYPE_RANGE &&
 	       drvdata->addr_type[idx + 1] == ETM_ADDR_TYPE_RANGE))) {
-		mutex_unlock(&drvdata->mutex);
+		spin_unlock(&drvdata->spinlock);
 		return -EPERM;
 	}
 
 	val1 = drvdata->addr_val[idx];
 	val2 = drvdata->addr_val[idx + 1];
-	mutex_unlock(&drvdata->mutex);
+	spin_unlock(&drvdata->spinlock);
 	return scnprintf(buf, PAGE_SIZE, "%#lx %#lx\n", val1, val2);
 }
 
@@ -895,17 +886,17 @@
 	if (val1 > val2)
 		return -EINVAL;
 
-	mutex_lock(&drvdata->mutex);
+	spin_lock(&drvdata->spinlock);
 	idx = drvdata->addr_idx;
 	if (idx % 2 != 0) {
-		mutex_unlock(&drvdata->mutex);
+		spin_unlock(&drvdata->spinlock);
 		return -EPERM;
 	}
 	if (!((drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE &&
 	       drvdata->addr_type[idx + 1] == ETM_ADDR_TYPE_NONE) ||
 	      (drvdata->addr_type[idx] == ETM_ADDR_TYPE_RANGE &&
 	       drvdata->addr_type[idx + 1] == ETM_ADDR_TYPE_RANGE))) {
-		mutex_unlock(&drvdata->mutex);
+		spin_unlock(&drvdata->spinlock);
 		return -EPERM;
 	}
 
@@ -914,7 +905,7 @@
 	drvdata->addr_val[idx + 1] = val2;
 	drvdata->addr_type[idx + 1] = ETM_ADDR_TYPE_RANGE;
 	drvdata->enable_ctrl1 |= (1 << (idx/2));
-	mutex_unlock(&drvdata->mutex);
+	spin_unlock(&drvdata->spinlock);
 	return size;
 }
 static DEVICE_ATTR(addr_range, S_IRUGO | S_IWUSR, etm_show_addr_range,
@@ -927,16 +918,16 @@
 	unsigned long val;
 	uint8_t idx;
 
-	mutex_lock(&drvdata->mutex);
+	spin_lock(&drvdata->spinlock);
 	idx = drvdata->addr_idx;
 	if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE ||
 	      drvdata->addr_type[idx] == ETM_ADDR_TYPE_START)) {
-		mutex_unlock(&drvdata->mutex);
+		spin_unlock(&drvdata->spinlock);
 		return -EPERM;
 	}
 
 	val = drvdata->addr_val[idx];
-	mutex_unlock(&drvdata->mutex);
+	spin_unlock(&drvdata->spinlock);
 	return scnprintf(buf, PAGE_SIZE, "%#lx\n", val);
 }
 
@@ -951,11 +942,11 @@
 	if (sscanf(buf, "%lx", &val) != 1)
 		return -EINVAL;
 
-	mutex_lock(&drvdata->mutex);
+	spin_lock(&drvdata->spinlock);
 	idx = drvdata->addr_idx;
 	if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE ||
 	      drvdata->addr_type[idx] == ETM_ADDR_TYPE_START)) {
-		mutex_unlock(&drvdata->mutex);
+		spin_unlock(&drvdata->spinlock);
 		return -EPERM;
 	}
 
@@ -963,7 +954,7 @@
 	drvdata->addr_type[idx] = ETM_ADDR_TYPE_START;
 	drvdata->startstop_ctrl |= (1 << idx);
 	drvdata->enable_ctrl1 |= BIT(25);
-	mutex_unlock(&drvdata->mutex);
+	spin_unlock(&drvdata->spinlock);
 	return size;
 }
 static DEVICE_ATTR(addr_start, S_IRUGO | S_IWUSR, etm_show_addr_start,
@@ -976,16 +967,16 @@
 	unsigned long val;
 	uint8_t idx;
 
-	mutex_lock(&drvdata->mutex);
+	spin_lock(&drvdata->spinlock);
 	idx = drvdata->addr_idx;
 	if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE ||
 	      drvdata->addr_type[idx] == ETM_ADDR_TYPE_STOP)) {
-		mutex_unlock(&drvdata->mutex);
+		spin_unlock(&drvdata->spinlock);
 		return -EPERM;
 	}
 
 	val = drvdata->addr_val[idx];
-	mutex_unlock(&drvdata->mutex);
+	spin_unlock(&drvdata->spinlock);
 	return scnprintf(buf, PAGE_SIZE, "%#lx\n", val);
 }
 
@@ -1000,11 +991,11 @@
 	if (sscanf(buf, "%lx", &val) != 1)
 		return -EINVAL;
 
-	mutex_lock(&drvdata->mutex);
+	spin_lock(&drvdata->spinlock);
 	idx = drvdata->addr_idx;
 	if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE ||
 	      drvdata->addr_type[idx] == ETM_ADDR_TYPE_STOP)) {
-		mutex_unlock(&drvdata->mutex);
+		spin_unlock(&drvdata->spinlock);
 		return -EPERM;
 	}
 
@@ -1012,7 +1003,7 @@
 	drvdata->addr_type[idx] = ETM_ADDR_TYPE_STOP;
 	drvdata->startstop_ctrl |= (1 << (idx + 16));
 	drvdata->enable_ctrl1 |= BIT(25);
-	mutex_unlock(&drvdata->mutex);
+	spin_unlock(&drvdata->spinlock);
 	return size;
 }
 static DEVICE_ATTR(addr_stop, S_IRUGO | S_IWUSR, etm_show_addr_stop,
@@ -1024,9 +1015,9 @@
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
 	unsigned long val;
 
-	mutex_lock(&drvdata->mutex);
+	spin_lock(&drvdata->spinlock);
 	val = drvdata->addr_acctype[drvdata->addr_idx];
-	mutex_unlock(&drvdata->mutex);
+	spin_unlock(&drvdata->spinlock);
 	return scnprintf(buf, PAGE_SIZE, "%#lx\n", val);
 }
 
@@ -1040,9 +1031,9 @@
 	if (sscanf(buf, "%lx", &val) != 1)
 		return -EINVAL;
 
-	mutex_lock(&drvdata->mutex);
+	spin_lock(&drvdata->spinlock);
 	drvdata->addr_acctype[drvdata->addr_idx] = val;
-	mutex_unlock(&drvdata->mutex);
+	spin_unlock(&drvdata->spinlock);
 	return size;
 }
 static DEVICE_ATTR(addr_acctype, S_IRUGO | S_IWUSR, etm_show_addr_acctype,
@@ -1069,12 +1060,13 @@
 	if (val >= drvdata->nr_cntr)
 		return -EINVAL;
 
-	/* Use mutex to ensure index doesn't change while it gets dereferenced
-	 * multiple times within a mutex block elsewhere.
+	/*
+	 * Use spinlock to ensure index doesn't change while it gets
+	 * dereferenced multiple times within a spinlock block elsewhere.
 	 */
-	mutex_lock(&drvdata->mutex);
+	spin_lock(&drvdata->spinlock);
 	drvdata->cntr_idx = val;
-	mutex_unlock(&drvdata->mutex);
+	spin_unlock(&drvdata->spinlock);
 	return size;
 }
 static DEVICE_ATTR(cntr_idx, S_IRUGO | S_IWUSR, etm_show_cntr_idx,
@@ -1086,9 +1078,9 @@
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
 	unsigned long val;
 
-	mutex_lock(&drvdata->mutex);
+	spin_lock(&drvdata->spinlock);
 	val = drvdata->cntr_rld_val[drvdata->cntr_idx];
-	mutex_unlock(&drvdata->mutex);
+	spin_unlock(&drvdata->spinlock);
 	return scnprintf(buf, PAGE_SIZE, "%#lx\n", val);
 }
 
@@ -1102,9 +1094,9 @@
 	if (sscanf(buf, "%lx", &val) != 1)
 		return -EINVAL;
 
-	mutex_lock(&drvdata->mutex);
+	spin_lock(&drvdata->spinlock);
 	drvdata->cntr_rld_val[drvdata->cntr_idx] = val;
-	mutex_unlock(&drvdata->mutex);
+	spin_unlock(&drvdata->spinlock);
 	return size;
 }
 static DEVICE_ATTR(cntr_rld_val, S_IRUGO | S_IWUSR, etm_show_cntr_rld_val,
@@ -1116,9 +1108,9 @@
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
 	unsigned long val;
 
-	mutex_lock(&drvdata->mutex);
+	spin_lock(&drvdata->spinlock);
 	val = drvdata->cntr_event[drvdata->cntr_idx];
-	mutex_unlock(&drvdata->mutex);
+	spin_unlock(&drvdata->spinlock);
 	return scnprintf(buf, PAGE_SIZE, "%#lx\n", val);
 }
 
@@ -1132,9 +1124,9 @@
 	if (sscanf(buf, "%lx", &val) != 1)
 		return -EINVAL;
 
-	mutex_lock(&drvdata->mutex);
+	spin_lock(&drvdata->spinlock);
 	drvdata->cntr_event[drvdata->cntr_idx] = val & ETM_EVENT_MASK;
-	mutex_unlock(&drvdata->mutex);
+	spin_unlock(&drvdata->spinlock);
 	return size;
 }
 static DEVICE_ATTR(cntr_event, S_IRUGO | S_IWUSR, etm_show_cntr_event,
@@ -1146,9 +1138,9 @@
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
 	unsigned long val;
 
-	mutex_lock(&drvdata->mutex);
+	spin_lock(&drvdata->spinlock);
 	val = drvdata->cntr_rld_event[drvdata->cntr_idx];
-	mutex_unlock(&drvdata->mutex);
+	spin_unlock(&drvdata->spinlock);
 	return scnprintf(buf, PAGE_SIZE, "%#lx\n", val);
 }
 
@@ -1162,9 +1154,9 @@
 	if (sscanf(buf, "%lx", &val) != 1)
 		return -EINVAL;
 
-	mutex_lock(&drvdata->mutex);
+	spin_lock(&drvdata->spinlock);
 	drvdata->cntr_rld_event[drvdata->cntr_idx] = val & ETM_EVENT_MASK;
-	mutex_unlock(&drvdata->mutex);
+	spin_unlock(&drvdata->spinlock);
 	return size;
 }
 static DEVICE_ATTR(cntr_rld_event, S_IRUGO | S_IWUSR, etm_show_cntr_rld_event,
@@ -1176,9 +1168,9 @@
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
 	unsigned long val;
 
-	mutex_lock(&drvdata->mutex);
+	spin_lock(&drvdata->spinlock);
 	val = drvdata->cntr_val[drvdata->cntr_idx];
-	mutex_unlock(&drvdata->mutex);
+	spin_unlock(&drvdata->spinlock);
 	return scnprintf(buf, PAGE_SIZE, "%#lx\n", val);
 }
 
@@ -1192,9 +1184,9 @@
 	if (sscanf(buf, "%lx", &val) != 1)
 		return -EINVAL;
 
-	mutex_lock(&drvdata->mutex);
+	spin_lock(&drvdata->spinlock);
 	drvdata->cntr_val[drvdata->cntr_idx] = val;
-	mutex_unlock(&drvdata->mutex);
+	spin_unlock(&drvdata->spinlock);
 	return size;
 }
 static DEVICE_ATTR(cntr_val, S_IRUGO | S_IWUSR, etm_show_cntr_val,
@@ -1398,12 +1390,13 @@
 	if (val >= drvdata->nr_ctxid_cmp)
 		return -EINVAL;
 
-	/* Use mutex to ensure index doesn't change while it gets dereferenced
-	 * multiple times within a mutex block elsewhere.
+	/*
+	 * Use spinlock to ensure index doesn't change while it gets
+	 * dereferenced multiple times within a spinlock block elsewhere.
 	 */
-	mutex_lock(&drvdata->mutex);
+	spin_lock(&drvdata->spinlock);
 	drvdata->ctxid_idx = val;
-	mutex_unlock(&drvdata->mutex);
+	spin_unlock(&drvdata->spinlock);
 	return size;
 }
 static DEVICE_ATTR(ctxid_idx, S_IRUGO | S_IWUSR, etm_show_ctxid_idx,
@@ -1415,9 +1408,9 @@
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
 	unsigned long val;
 
-	mutex_lock(&drvdata->mutex);
+	spin_lock(&drvdata->spinlock);
 	val = drvdata->ctxid_val[drvdata->ctxid_idx];
-	mutex_unlock(&drvdata->mutex);
+	spin_unlock(&drvdata->spinlock);
 	return scnprintf(buf, PAGE_SIZE, "%#lx\n", val);
 }
 
@@ -1431,9 +1424,9 @@
 	if (sscanf(buf, "%lx", &val) != 1)
 		return -EINVAL;
 
-	mutex_lock(&drvdata->mutex);
+	spin_lock(&drvdata->spinlock);
 	drvdata->ctxid_val[drvdata->ctxid_idx] = val;
-	mutex_unlock(&drvdata->mutex);
+	spin_unlock(&drvdata->spinlock);
 	return size;
 }
 static DEVICE_ATTR(ctxid_val, S_IRUGO | S_IWUSR, etm_show_ctxid_val,
@@ -1527,26 +1520,43 @@
 
 static int __etm_store_pcsave(struct etm_drvdata *drvdata, unsigned long val)
 {
-	int ret;
+	int ret = 0;
 
 	ret = clk_prepare_enable(drvdata->clk);
 	if (ret)
 		return ret;
 
-	mutex_lock(&drvdata->mutex);
+	get_online_cpus();
+	spin_lock(&drvdata->spinlock);
 	if (val) {
-		smp_call_function_single(drvdata->cpu, etm_enable_pcsave,
-					 drvdata, 1);
+		if (drvdata->pcsave_enable)
+			goto out;
+
+		ret = smp_call_function_single(drvdata->cpu, etm_enable_pcsave,
+					       drvdata, 1);
+		if (ret)
+			goto out;
 		drvdata->pcsave_enable = true;
+
+		dev_info(drvdata->dev, "PC save enabled\n");
 	} else {
-		smp_call_function_single(drvdata->cpu, etm_disable_pcsave,
-					 drvdata, 1);
+		if (!drvdata->pcsave_enable)
+			goto out;
+
+		ret = smp_call_function_single(drvdata->cpu, etm_disable_pcsave,
+					       drvdata, 1);
+		if (ret)
+			goto out;
 		drvdata->pcsave_enable = false;
+
+		dev_info(drvdata->dev, "PC save disabled\n");
 	}
-	mutex_unlock(&drvdata->mutex);
+out:
+	spin_unlock(&drvdata->spinlock);
+	put_online_cpus();
 
 	clk_disable_unprepare(drvdata->clk);
-	return 0;
+	return ret;
 }
 
 static ssize_t etm_store_pcsave(struct device *dev,
@@ -1613,15 +1623,42 @@
 	NULL,
 };
 
-/* Memory mapped writes to clear os lock not supported */
-static void etm_os_unlock(void *unused)
+static int etm_cpu_callback(struct notifier_block *nfb, unsigned long action,
+			    void *hcpu)
 {
-	unsigned long value = 0x0;
+	unsigned int cpu = (unsigned long)hcpu;
 
-	asm("mcr p14, 1, %0, c1, c0, 4\n\t" : : "r" (value));
-	asm("isb\n\t");
+	switch (action & (~CPU_TASKS_FROZEN)) {
+	case CPU_STARTING:
+		if (etmdrvdata[cpu] && !etmdrvdata[cpu]->os_unlock) {
+			spin_lock(&etmdrvdata[cpu]->spinlock);
+			etm_os_unlock(etmdrvdata[cpu]);
+			etmdrvdata[cpu]->os_unlock = true;
+			spin_unlock(&etmdrvdata[cpu]->spinlock);
+		}
+
+		if (etmdrvdata[cpu] && etmdrvdata[cpu]->enable) {
+			spin_lock(&etmdrvdata[cpu]->spinlock);
+			__etm_enable(etmdrvdata[cpu]);
+			spin_unlock(&etmdrvdata[cpu]->spinlock);
+		}
+		break;
+
+	case CPU_DYING:
+		if (etmdrvdata[cpu] && etmdrvdata[cpu]->enable) {
+			spin_lock(&etmdrvdata[cpu]->spinlock);
+			__etm_disable(etmdrvdata[cpu]);
+			spin_unlock(&etmdrvdata[cpu]->spinlock);
+		}
+		break;
+	}
+	return NOTIFY_OK;
 }
 
+static struct notifier_block etm_cpu_notifier = {
+	.notifier_call = etm_cpu_callback,
+};
+
 static bool __devinit etm_arch_supported(uint8_t arch)
 {
 	switch (arch) {
@@ -1633,15 +1670,6 @@
 	return true;
 }
 
-static void __devinit etm_prepare_arch(struct etm_drvdata *drvdata)
-{
-	/* Unlock OS lock first to allow memory mapped reads and writes. This
-	 * is required for Krait pass1
-	 * */
-	etm_os_unlock(NULL);
-	smp_call_function(etm_os_unlock, NULL, 1);
-}
-
 static void __devinit etm_init_arch_data(void *info)
 {
 	uint32_t etmidr;
@@ -1677,19 +1705,18 @@
 	drvdata->nr_ctxid_cmp = BMVAL(etmccr, 24, 25);
 
 	etm_set_pwrdwn(drvdata);
-	/* Vote for ETM power/clock disable */
 	etm_clr_pwrup(drvdata);
 	ETM_LOCK(drvdata);
 }
 
 static void __devinit etm_copy_arch_data(struct etm_drvdata *drvdata)
 {
-	drvdata->arch = etm0drvdata->arch;
-	drvdata->nr_addr_cmp = etm0drvdata->nr_addr_cmp;
-	drvdata->nr_cntr = etm0drvdata->nr_cntr;
-	drvdata->nr_ext_inp = etm0drvdata->nr_ext_inp;
-	drvdata->nr_ext_out = etm0drvdata->nr_ext_out;
-	drvdata->nr_ctxid_cmp = etm0drvdata->nr_ctxid_cmp;
+	drvdata->arch = etmdrvdata[0]->arch;
+	drvdata->nr_addr_cmp = etmdrvdata[0]->nr_addr_cmp;
+	drvdata->nr_cntr = etmdrvdata[0]->nr_cntr;
+	drvdata->nr_ext_inp = etmdrvdata[0]->nr_ext_inp;
+	drvdata->nr_ext_out = etmdrvdata[0]->nr_ext_out;
+	drvdata->nr_ctxid_cmp = etmdrvdata[0]->nr_ctxid_cmp;
 }
 
 static void __devinit etm_init_default_data(struct etm_drvdata *drvdata)
@@ -1774,7 +1801,7 @@
 	if (!drvdata->base)
 		return -ENOMEM;
 
-	mutex_init(&drvdata->mutex);
+	spin_lock_init(&drvdata->spinlock);
 	wake_lock_init(&drvdata->wake_lock, WAKE_LOCK_SUSPEND, "coresight-etm");
 
 	drvdata->clk = devm_clk_get(dev, "core_clk");
@@ -1787,23 +1814,32 @@
 	if (ret)
 		goto err0;
 
-	drvdata->cpu = count++;
-
 	ret = clk_prepare_enable(drvdata->clk);
 	if (ret)
 		goto err0;
 
-	/* Use CPU0 to populate read-only configuration data for ETM0. For other
-	 * ETMs copy it over from ETM0.
+	drvdata->cpu = count++;
+
+	get_online_cpus();
+	etmdrvdata[drvdata->cpu] = drvdata;
+
+	if (!smp_call_function_single(drvdata->cpu, etm_os_unlock, NULL, 1))
+		drvdata->os_unlock = true;
+	/*
+	 * Use CPU0 to populate read-only configuration data for ETM0. For
+	 * other ETMs copy it over from ETM0.
 	 */
 	if (drvdata->cpu == 0) {
-		etm_prepare_arch(drvdata);
-		smp_call_function_single(drvdata->cpu, etm_init_arch_data,
-					 drvdata, 1);
-		etm0drvdata = drvdata;
+		register_hotcpu_notifier(&etm_cpu_notifier);
+		if (smp_call_function_single(drvdata->cpu, etm_init_arch_data,
+					     drvdata, 1))
+			dev_err(dev, "ETM arch init failed\n");
 	} else {
 		etm_copy_arch_data(drvdata);
 	}
+
+	put_online_cpus();
+
 	if (etm_arch_supported(drvdata->arch) == false) {
 		ret = -EINVAL;
 		goto err1;
@@ -1821,7 +1857,7 @@
 		ret = msm_dump_table_register(&dump);
 		if (ret) {
 			devm_kfree(dev, baddr);
-			dev_err(dev, "ETM REG dump setup failed\n");
+			dev_err(dev, "ETM REG dump setup failed/unsupported\n");
 		}
 	} else {
 		dev_err(dev, "ETM REG dump space allocation failed\n");
@@ -1830,7 +1866,7 @@
 	desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL);
 	if (!desc) {
 		ret = -ENOMEM;
-		goto err0;
+		goto err2;
 	}
 	desc->type = CORESIGHT_DEV_TYPE_SOURCE;
 	desc->subtype.source_subtype = CORESIGHT_DEV_SUBTYPE_SOURCE_PROC;
@@ -1842,7 +1878,7 @@
 	drvdata->csdev = coresight_register(desc);
 	if (IS_ERR(drvdata->csdev)) {
 		ret = PTR_ERR(drvdata->csdev);
-		goto err0;
+		goto err2;
 	}
 
 	if (pdev->dev.of_node)
@@ -1864,11 +1900,17 @@
 		__etm_store_pcsave(drvdata, true);
 
 	return 0;
+err2:
+	if (drvdata->cpu == 0)
+		unregister_hotcpu_notifier(&etm_cpu_notifier);
+	wake_lock_destroy(&drvdata->wake_lock);
+	return ret;
 err1:
+	if (drvdata->cpu == 0)
+		unregister_hotcpu_notifier(&etm_cpu_notifier);
 	clk_disable_unprepare(drvdata->clk);
 err0:
 	wake_lock_destroy(&drvdata->wake_lock);
-	mutex_destroy(&drvdata->mutex);
 	return ret;
 }
 
@@ -1878,8 +1920,9 @@
 
 	device_remove_file(&drvdata->csdev->dev, &dev_attr_pcsave);
 	coresight_unregister(drvdata->csdev);
+	if (drvdata->cpu == 0)
+		unregister_hotcpu_notifier(&etm_cpu_notifier);
 	wake_lock_destroy(&drvdata->wake_lock);
-	mutex_destroy(&drvdata->mutex);
 	return 0;
 }
 
diff --git a/drivers/coresight/coresight-stm.c b/drivers/coresight/coresight-stm.c
index f6a948b..1379c55 100644
--- a/drivers/coresight/coresight-stm.c
+++ b/drivers/coresight/coresight-stm.c
@@ -24,6 +24,7 @@
 #include <linux/slab.h>
 #include <linux/delay.h>
 #include <linux/clk.h>
+#include <linux/bitmap.h>
 #include <linux/of_coresight.h>
 #include <linux/coresight.h>
 #include <linux/coresight-stm.h>
@@ -45,44 +46,47 @@
 	mb();								\
 } while (0)
 
-#define STMDMASTARTR		(0xC04)
-#define STMDMASTOPR		(0xC08)
-#define STMDMASTATR		(0xC0C)
-#define STMDMACTLR		(0xC10)
-#define STMDMAIDR		(0xCFC)
-#define STMHEER			(0xD00)
-#define STMHETER		(0xD20)
-#define STMHEMCR		(0xD64)
-#define STMHEMASTR		(0xDF4)
-#define STMHEFEAT1R		(0xDF8)
-#define STMHEIDR		(0xDFC)
-#define STMSPER			(0xE00)
-#define STMSPTER		(0xE20)
-#define STMSPSCR		(0xE60)
-#define STMSPMSCR		(0xE64)
-#define STMSPOVERRIDER		(0xE68)
-#define STMSPMOVERRIDER		(0xE6C)
-#define STMSPTRIGCSR		(0xE70)
-#define STMTCSR			(0xE80)
-#define STMTSSTIMR		(0xE84)
-#define STMTSFREQR		(0xE8C)
-#define STMSYNCR		(0xE90)
-#define STMAUXCR		(0xE94)
-#define STMSPFEAT1R		(0xEA0)
-#define STMSPFEAT2R		(0xEA4)
-#define STMSPFEAT3R		(0xEA8)
-#define STMITTRIGGER		(0xEE8)
-#define STMITATBDATA0		(0xEEC)
-#define STMITATBCTR2		(0xEF0)
-#define STMITATBID		(0xEF4)
-#define STMITATBCTR0		(0xEF8)
+#define STMDMASTARTR			(0xC04)
+#define STMDMASTOPR			(0xC08)
+#define STMDMASTATR			(0xC0C)
+#define STMDMACTLR			(0xC10)
+#define STMDMAIDR			(0xCFC)
+#define STMHEER				(0xD00)
+#define STMHETER			(0xD20)
+#define STMHEMCR			(0xD64)
+#define STMHEMASTR			(0xDF4)
+#define STMHEFEAT1R			(0xDF8)
+#define STMHEIDR			(0xDFC)
+#define STMSPER				(0xE00)
+#define STMSPTER			(0xE20)
+#define STMSPSCR			(0xE60)
+#define STMSPMSCR			(0xE64)
+#define STMSPOVERRIDER			(0xE68)
+#define STMSPMOVERRIDER			(0xE6C)
+#define STMSPTRIGCSR			(0xE70)
+#define STMTCSR				(0xE80)
+#define STMTSSTIMR			(0xE84)
+#define STMTSFREQR			(0xE8C)
+#define STMSYNCR			(0xE90)
+#define STMAUXCR			(0xE94)
+#define STMSPFEAT1R			(0xEA0)
+#define STMSPFEAT2R			(0xEA4)
+#define STMSPFEAT3R			(0xEA8)
+#define STMITTRIGGER			(0xEE8)
+#define STMITATBDATA0			(0xEEC)
+#define STMITATBCTR2			(0xEF0)
+#define STMITATBID			(0xEF4)
+#define STMITATBCTR0			(0xEF8)
 
-#define NR_STM_CHANNEL		(32)
-#define BYTES_PER_CHANNEL	(256)
-#define STM_TRACE_BUF_SIZE	(1024)
+#define NR_STM_CHANNEL			(32)
+#define BYTES_PER_CHANNEL		(256)
+#define STM_TRACE_BUF_SIZE		(4096)
+#define STM_USERSPACE_HEADER_SIZE	(8)
+#define STM_USERSPACE_MAGIC1_VAL	(0xf0)
+#define STM_USERSPACE_MAGIC2_VAL	(0xf1)
 
-#define OST_START_TOKEN		(0x30)
-#define OST_VERSION		(0x1)
+#define OST_START_TOKEN			(0x30)
+#define OST_VERSION			(0x1)
 
 enum stm_pkt_type {
 	STM_PKT_TYPE_DATA	= 0x98,
@@ -128,7 +132,7 @@
 	spinlock_t		spinlock;
 	struct channel_space	chs;
 	bool			enable;
-	uint32_t		entity;
+	DECLARE_BITMAP(entities, OST_ENTITY_MAX);
 };
 
 static struct stm_drvdata *stmdrvdata;
@@ -482,7 +486,8 @@
 	struct stm_drvdata *drvdata = stmdrvdata;
 
 	/* we don't support sizes more than 24bits (0 to 23) */
-	if (!(drvdata && drvdata->enable && (drvdata->entity & entity_id) &&
+	if (!(drvdata && drvdata->enable &&
+	      test_bit(entity_id, drvdata->entities) && size &&
 	      (size < 0x1000000)))
 		return 0;
 
@@ -496,13 +501,12 @@
 	struct stm_drvdata *drvdata = container_of(file->private_data,
 						   struct stm_drvdata, miscdev);
 	char *buf;
+	uint8_t entity_id, proto_id;
+	uint32_t options;
 
-	if (!drvdata->enable)
+	if (!drvdata->enable || !size)
 		return -EINVAL;
 
-	if (!(drvdata->entity & OST_ENTITY_DEV_NODE))
-		return size;
-
 	if (size > STM_TRACE_BUF_SIZE)
 		size = STM_TRACE_BUF_SIZE;
 
@@ -516,7 +520,32 @@
 		return -EFAULT;
 	}
 
-	__stm_trace(STM_OPTION_TIMESTAMPED, OST_ENTITY_DEV_NODE, 0, buf, size);
+	if (size >= STM_USERSPACE_HEADER_SIZE &&
+	    buf[0] == STM_USERSPACE_MAGIC1_VAL &&
+	    buf[1] == STM_USERSPACE_MAGIC2_VAL) {
+
+		entity_id = buf[2];
+		proto_id = buf[3];
+		options = *(uint32_t *)(buf + 4);
+
+		if (!test_bit(entity_id, drvdata->entities) ||
+		    !(size - STM_USERSPACE_HEADER_SIZE)) {
+			kfree(buf);
+			return size;
+		}
+
+		__stm_trace(options, entity_id, proto_id,
+			    buf + STM_USERSPACE_HEADER_SIZE,
+			    size - STM_USERSPACE_HEADER_SIZE);
+	} else {
+		if (!test_bit(OST_ENTITY_DEV_NODE, drvdata->entities)) {
+			kfree(buf);
+			return size;
+		}
+
+		__stm_trace(STM_OPTION_TIMESTAMPED, OST_ENTITY_DEV_NODE, 0,
+			    buf, size);
+	}
 
 	kfree(buf);
 
@@ -594,35 +623,50 @@
 static DEVICE_ATTR(port_enable, S_IRUGO | S_IWUSR, stm_show_port_enable,
 		   stm_store_port_enable);
 
-static ssize_t stm_show_entity(struct device *dev,
+static ssize_t stm_show_entities(struct device *dev,
 			       struct device_attribute *attr, char *buf)
 {
 	struct stm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-	unsigned long val = drvdata->entity;
+	ssize_t len;
 
-	return scnprintf(buf, PAGE_SIZE, "%#lx\n", val);
+	len = bitmap_scnprintf(buf, PAGE_SIZE, drvdata->entities,
+			       OST_ENTITY_MAX);
+
+	if (PAGE_SIZE - len < 2)
+		len = -EINVAL;
+	else
+		len += scnprintf(buf + len, 2, "\n");
+
+	return len;
 }
 
-static ssize_t stm_store_entity(struct device *dev,
+static ssize_t stm_store_entities(struct device *dev,
 				struct device_attribute *attr,
 				const char *buf, size_t size)
 {
 	struct stm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-	unsigned long val;
+	unsigned long val1, val2;
 
-	if (sscanf(buf, "%lx", &val) != 1)
+	if (sscanf(buf, "%lx %lx", &val1, &val2) != 2)
 		return -EINVAL;
 
-	drvdata->entity = val;
+	if (val1 >= OST_ENTITY_MAX)
+		return -EINVAL;
+
+	if (val2)
+		__set_bit(val1, drvdata->entities);
+	else
+		__clear_bit(val1, drvdata->entities);
+
 	return size;
 }
-static DEVICE_ATTR(entity, S_IRUGO | S_IWUSR, stm_show_entity,
-		   stm_store_entity);
+static DEVICE_ATTR(entities, S_IRUGO | S_IWUSR, stm_show_entities,
+		   stm_store_entities);
 
 static struct attribute *stm_attrs[] = {
 	&dev_attr_hwevent_enable.attr,
 	&dev_attr_port_enable.attr,
-	&dev_attr_entity.attr,
+	&dev_attr_entities.attr,
 	NULL,
 };
 
@@ -698,7 +742,7 @@
 	if (ret)
 		return ret;
 
-	drvdata->entity = OST_ENTITY_ALL;
+	bitmap_fill(drvdata->entities, OST_ENTITY_MAX);
 
 	desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL);
 	if (!desc)
diff --git a/drivers/coresight/coresight-tmc.c b/drivers/coresight/coresight-tmc.c
index 13f69cd..3bb9ec7 100644
--- a/drivers/coresight/coresight-tmc.c
+++ b/drivers/coresight/coresight-tmc.c
@@ -1043,8 +1043,7 @@
 	if (drvdata->config_type == TMC_CONFIG_TYPE_ETR)
 		drvdata->size = SZ_1M;
 	else
-		drvdata->size = (tmc_readl(drvdata, TMC_RSZ) * BYTES_PER_WORD)
-				+ PAGE_SIZE;
+		drvdata->size = tmc_readl(drvdata, TMC_RSZ) * BYTES_PER_WORD;
 
 	clk_disable_unprepare(drvdata->clk);
 
@@ -1067,7 +1066,8 @@
 		if (ret)
 			goto err0;
 	} else {
-		baddr = devm_kzalloc(dev, drvdata->size, GFP_KERNEL);
+		baddr = devm_kzalloc(dev, PAGE_SIZE + drvdata->size,
+				     GFP_KERNEL);
 		if (!baddr)
 			return -ENOMEM;
 		drvdata->buf = baddr + PAGE_SIZE;
@@ -1075,7 +1075,7 @@
 							TMC_ETFETB_DUMP_VER;
 		dump.id = MSM_TMC_ETFETB + etfetb_count;
 		dump.start_addr = virt_to_phys(baddr);
-		dump.end_addr = dump.start_addr + drvdata->size;
+		dump.end_addr = dump.start_addr + PAGE_SIZE + drvdata->size;
 		ret = msm_dump_table_register(&dump);
 		/* Don't free the buffer in case of error since it can still
 		 * be used to provide dump collection via the device node
diff --git a/drivers/cpufreq/cpufreq_gov_msm.c b/drivers/cpufreq/cpufreq_gov_msm.c
index 4eeff35..6ddbf4e 100644
--- a/drivers/cpufreq/cpufreq_gov_msm.c
+++ b/drivers/cpufreq/cpufreq_gov_msm.c
@@ -18,20 +18,114 @@
 #include <linux/kobject.h>
 #include <linux/cpufreq.h>
 #include <linux/platform_device.h>
+#include <linux/cpu_pm.h>
+#include <linux/pm_qos.h>
+#include <linux/hrtimer.h>
+#include <linux/tick.h>
 #include <mach/msm_dcvs.h>
 
+struct cpu_idle_info {
+	int			enabled;
+	int			dcvs_core_id;
+	struct pm_qos_request	pm_qos_req;
+};
+
+static DEFINE_PER_CPU_SHARED_ALIGNED(struct cpu_idle_info, cpu_idle_info);
+static DEFINE_PER_CPU_SHARED_ALIGNED(u64, iowait_on_cpu);
+static uint32_t latency;
+
+static int msm_dcvs_idle_notifier(int core_num,
+		enum msm_core_control_event event)
+{
+	struct cpu_idle_info *info = &per_cpu(cpu_idle_info, core_num);
+
+	switch (event) {
+	case MSM_DCVS_ENABLE_IDLE_PULSE:
+		info->enabled = true;
+		break;
+
+	case MSM_DCVS_DISABLE_IDLE_PULSE:
+		info->enabled = false;
+		break;
+
+	case MSM_DCVS_ENABLE_HIGH_LATENCY_MODES:
+		pm_qos_update_request(&info->pm_qos_req, PM_QOS_DEFAULT_VALUE);
+		break;
+
+	case MSM_DCVS_DISABLE_HIGH_LATENCY_MODES:
+		pm_qos_update_request(&info->pm_qos_req, latency);
+		break;
+	}
+
+	return 0;
+}
+
+static int msm_cpuidle_notifier(struct notifier_block *self, unsigned long cmd,
+		void *v)
+{
+	struct cpu_idle_info *info =
+		&per_cpu(cpu_idle_info, smp_processor_id());
+	u64 io_wait_us = 0;
+	u64 prev_io_wait_us = 0;
+	u64 last_update_time = 0;
+	u64 val = 0;
+	uint32_t iowaited = 0;
+
+	if (!info->enabled)
+		return NOTIFY_OK;
+
+	switch (cmd) {
+	case CPU_PM_ENTER:
+		val = get_cpu_iowait_time_us(smp_processor_id(),
+					&last_update_time);
+		/* val could be -1 when NOHZ is not enabled */
+		if (val == (u64)-1)
+			val = 0;
+		per_cpu(iowait_on_cpu, smp_processor_id()) = val;
+		msm_dcvs_idle(info->dcvs_core_id, MSM_DCVS_IDLE_ENTER, 0);
+		break;
+
+	case CPU_PM_EXIT:
+		prev_io_wait_us = per_cpu(iowait_on_cpu, smp_processor_id());
+		val = get_cpu_iowait_time_us(smp_processor_id(),
+				&last_update_time);
+		if (val == (u64)-1)
+			val = 0;
+		io_wait_us = val;
+		iowaited = (io_wait_us - prev_io_wait_us);
+		msm_dcvs_idle(info->dcvs_core_id, MSM_DCVS_IDLE_EXIT, iowaited);
+		break;
+	}
+
+	return NOTIFY_OK;
+}
+
+static struct notifier_block idle_nb = {
+	.notifier_call = msm_cpuidle_notifier,
+};
+
+static void msm_gov_idle_source_init(int cpu, int dcvs_core_id)
+{
+	struct cpu_idle_info *info = NULL;
+
+	info = &per_cpu(cpu_idle_info, cpu);
+	info->dcvs_core_id = dcvs_core_id;
+
+	pm_qos_add_request(&info->pm_qos_req, PM_QOS_CPU_DMA_LATENCY,
+				PM_QOS_DEFAULT_VALUE);
+}
+
 struct msm_gov {
-	int cpu;
-	unsigned int cur_freq;
-	unsigned int min_freq;
-	unsigned int max_freq;
-	struct msm_dcvs_freq gov_notifier;
-	struct cpufreq_policy *policy;
+	int			cpu;
+	unsigned int		cur_freq;
+	unsigned int		min_freq;
+	unsigned int		max_freq;
+	struct cpufreq_policy	*policy;
+	int			dcvs_core_id;
 };
 
 static DEFINE_PER_CPU_SHARED_ALIGNED(struct mutex, gov_mutex);
 static DEFINE_PER_CPU_SHARED_ALIGNED(struct msm_gov, msm_gov_info);
-static char core_name[NR_CPUS][10];
 
 static void msm_gov_check_limits(struct cpufreq_policy *policy)
 {
@@ -40,7 +134,7 @@
 	if (policy->max < gov->cur_freq)
 		__cpufreq_driver_target(policy, policy->max,
 				CPUFREQ_RELATION_H);
-	else if (policy->min > gov->min_freq)
+	else if (policy->min > gov->cur_freq)
 		__cpufreq_driver_target(policy, policy->min,
 				CPUFREQ_RELATION_L);
 	else
@@ -50,14 +144,14 @@
 	gov->cur_freq = policy->cur;
 	gov->min_freq = policy->min;
 	gov->max_freq = policy->max;
+	msm_dcvs_update_limits(gov->dcvs_core_id);
 }
 
-static int msm_dcvs_freq_set(struct msm_dcvs_freq *self,
+static int msm_dcvs_freq_set(int core_num,
 		unsigned int freq)
 {
 	int ret = -EINVAL;
-	struct msm_gov *gov =
-		container_of(self, struct msm_gov, gov_notifier);
+	struct msm_gov *gov = &per_cpu(msm_gov_info, core_num);
 
 	mutex_lock(&per_cpu(gov_mutex, gov->cpu));
 
@@ -66,23 +160,30 @@
 	if (freq > gov->max_freq)
 		freq = gov->max_freq;
 
-	ret = __cpufreq_driver_target(gov->policy, freq, CPUFREQ_RELATION_L);
-	gov->cur_freq = gov->policy->cur;
-
 	mutex_unlock(&per_cpu(gov_mutex, gov->cpu));
 
-	if (!ret)
-		return gov->cur_freq;
+	ret = cpufreq_driver_target(gov->policy, freq, CPUFREQ_RELATION_L);
+
+	if (!ret) {
+		gov->cur_freq = cpufreq_quick_get(gov->cpu);
+		if (freq != gov->cur_freq)
+			pr_err("cpu %d freq %u gov->cur_freq %u didn't match",
+						gov->cpu, freq, gov->cur_freq);
+	}
+	ret = gov->cur_freq;
 
 	return ret;
 }
 
-static unsigned int msm_dcvs_freq_get(struct msm_dcvs_freq *self)
+static unsigned int msm_dcvs_freq_get(int core_num)
 {
-	struct msm_gov *gov =
-		container_of(self, struct msm_gov, gov_notifier);
-
-	return gov->cur_freq;
+	struct msm_gov *gov = &per_cpu(msm_gov_info, core_num);
+	/*
+	 * the rw_sem in cpufreq is always held when this is called.
+	 * The policy->cur won't be updated in this case - so it is safe to
+	 * access policy->cur
+	 */
+	return gov->policy->cur;
 }
 
 static int cpufreq_governor_msm(struct cpufreq_policy *policy,
@@ -92,8 +193,6 @@
 	int ret = 0;
 	int handle = 0;
 	struct msm_gov *gov = &per_cpu(msm_gov_info, policy->cpu);
-	struct msm_dcvs_freq *dcvs_notifier =
-			&(per_cpu(msm_gov_info, cpu).gov_notifier);
 
 	switch (event) {
 	case CPUFREQ_GOV_START:
@@ -103,19 +202,14 @@
 		mutex_lock(&per_cpu(gov_mutex, cpu));
 		per_cpu(msm_gov_info, cpu).cpu = cpu;
 		gov->policy = policy;
-		dcvs_notifier->core_name = core_name[cpu];
-		dcvs_notifier->set_frequency = msm_dcvs_freq_set;
-		dcvs_notifier->get_frequency = msm_dcvs_freq_get;
-		handle = msm_dcvs_freq_sink_register(dcvs_notifier);
+		handle = msm_dcvs_freq_sink_start(gov->dcvs_core_id);
 		BUG_ON(handle < 0);
 		msm_gov_check_limits(policy);
 		mutex_unlock(&per_cpu(gov_mutex, cpu));
 		break;
 
 	case CPUFREQ_GOV_STOP:
-		mutex_lock(&per_cpu(gov_mutex, cpu));
-		msm_dcvs_freq_sink_unregister(dcvs_notifier);
-		mutex_unlock(&per_cpu(gov_mutex, cpu));
+		msm_dcvs_freq_sink_stop(gov->dcvs_core_id);
 		break;
 
 	case CPUFREQ_GOV_LIMITS:
@@ -136,20 +230,40 @@
 
 static int __devinit msm_gov_probe(struct platform_device *pdev)
 {
-	int ret = 0;
 	int cpu;
 	struct msm_dcvs_core_info *core = NULL;
+	struct msm_dcvs_core_info *core_info = NULL;
+	struct msm_gov_platform_data *pdata = pdev->dev.platform_data;
+	int sensor = 0;
 
 	core = pdev->dev.platform_data;
+	core_info = pdata->info;
+	latency = pdata->latency;
 
 	for_each_possible_cpu(cpu) {
+		struct msm_gov *gov = &per_cpu(msm_gov_info, cpu);
+
 		mutex_init(&per_cpu(gov_mutex, cpu));
-		snprintf(core_name[cpu], 10, "cpu%d", cpu);
-		ret = msm_dcvs_register_core(core_name[cpu], core);
-		if (ret)
+		if (cpu < core->num_cores)
+			sensor = core_info->sensors[cpu];
+		gov->dcvs_core_id = msm_dcvs_register_core(
+						MSM_DCVS_CORE_TYPE_CPU,
+						cpu,
+						core_info,
+						msm_dcvs_freq_set,
+						msm_dcvs_freq_get,
+						msm_dcvs_idle_notifier,
+						sensor);
+		if (gov->dcvs_core_id < 0) {
 			pr_err("Unable to register core for %d\n", cpu);
+			return -EINVAL;
+		}
+
+		msm_gov_idle_source_init(cpu, gov->dcvs_core_id);
 	}
 
+	cpu_pm_register_notifier(&idle_nb);
+
 	return cpufreq_register_governor(&cpufreq_gov_msm);
 }
 
diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c
index f834ea8..63cdc68 100644
--- a/drivers/cpufreq/cpufreq_interactive.c
+++ b/drivers/cpufreq/cpufreq_interactive.c
@@ -972,6 +972,9 @@
 	spin_lock_init(&down_cpumask_lock);
 	mutex_init(&set_speed_lock);
 
+	/* Kick the kthread to idle */
+	wake_up_process(up_task);
+
 	idle_notifier_register(&cpufreq_interactive_idle_nb);
 	INIT_WORK(&inputopen.inputopen_work, cpufreq_interactive_input_open);
 	return cpufreq_register_governor(&cpufreq_gov_interactive);
diff --git a/drivers/cpufreq/cpufreq_ondemand.c b/drivers/cpufreq/cpufreq_ondemand.c
index 785ba6c..4b03cfd 100644
--- a/drivers/cpufreq/cpufreq_ondemand.c
+++ b/drivers/cpufreq/cpufreq_ondemand.c
@@ -115,7 +115,12 @@
 
 static struct workqueue_struct *input_wq;
 
-static DEFINE_PER_CPU(struct work_struct, dbs_refresh_work);
+struct dbs_work_struct {
+	struct work_struct work;
+	unsigned int cpu;
+};
+
+static DEFINE_PER_CPU(struct dbs_work_struct, dbs_refresh_work);
 
 static struct dbs_tuners {
 	unsigned int sampling_rate;
@@ -831,11 +836,15 @@
 	return 0;
 }
 
-static void dbs_refresh_callback(struct work_struct *unused)
+static void dbs_refresh_callback(struct work_struct *work)
 {
 	struct cpufreq_policy *policy;
 	struct cpu_dbs_info_s *this_dbs_info;
-	unsigned int cpu = smp_processor_id();
+	struct dbs_work_struct *dbs_work;
+	unsigned int cpu;
+
+	dbs_work = container_of(work, struct dbs_work_struct, work);
+	cpu = dbs_work->cpu;
 
 	get_online_cpus();
 
@@ -877,9 +886,8 @@
 		return;
 	}
 
-	for_each_online_cpu(i) {
-		queue_work_on(i, input_wq, &per_cpu(dbs_refresh_work, i));
-	}
+	for_each_online_cpu(i)
+		queue_work_on(i, input_wq, &per_cpu(dbs_refresh_work, i).work);
 }
 
 static int dbs_input_connect(struct input_handler *handler,
@@ -1072,8 +1080,12 @@
 	for_each_possible_cpu(i) {
 		struct cpu_dbs_info_s *this_dbs_info =
 			&per_cpu(od_cpu_dbs_info, i);
+		struct dbs_work_struct *dbs_work =
+			&per_cpu(dbs_refresh_work, i);
+
 		mutex_init(&this_dbs_info->timer_mutex);
-		INIT_WORK(&per_cpu(dbs_refresh_work, i), dbs_refresh_callback);
+		INIT_WORK(&dbs_work->work, dbs_refresh_callback);
+		dbs_work->cpu = i;
 	}
 
 	return cpufreq_register_governor(&cpufreq_gov_ondemand);
diff --git a/drivers/crypto/msm/qce50.c b/drivers/crypto/msm/qce50.c
index de5f10f..1312448 100644
--- a/drivers/crypto/msm/qce50.c
+++ b/drivers/crypto/msm/qce50.c
@@ -77,7 +77,8 @@
 	int dst_nents;
 
 	dma_addr_t phy_iv_in;
-
+	unsigned char dec_iv[16];
+	int dir;
 	void *areq;
 	enum qce_cipher_mode_enum mode;
 	struct ce_sps_data ce_sps;
@@ -381,7 +382,7 @@
 			break;
 
 		case QCE_MODE_XTS:
-			if (creq->encklen ==  AES128_KEY_SIZE)
+			if (creq->encklen/2 ==  AES128_KEY_SIZE)
 				*cmdlistinfo = &cmdlistptr->cipher_aes_128_xts;
 			else
 				*cmdlistinfo = &cmdlistptr->cipher_aes_256_xts;
@@ -713,16 +714,19 @@
 {
 	struct ahash_request *areq;
 	unsigned char digest[SHA256_DIGEST_SIZE];
+	uint32_t bytecount32[2];
 
 	areq = (struct ahash_request *) pce_dev->areq;
 	dma_unmap_sg(pce_dev->pdev, areq->src, pce_dev->src_nents,
 				DMA_TO_DEVICE);
 	memcpy(digest, (char *)(&pce_dev->ce_sps.result->auth_iv[0]),
 						SHA256_DIGEST_SIZE);
+	_byte_stream_to_net_words(bytecount32,
+		(unsigned char *)pce_dev->ce_sps.result->auth_byte_count,
+					2 * CRYPTO_REG_SIZE);
 	if (_qce_unlock_other_pipes(pce_dev))
 		return -EINVAL;
-	pce_dev->qce_cb(areq, digest,
-			(char *)pce_dev->ce_sps.result->auth_byte_count,
+	pce_dev->qce_cb(areq, digest, (char *)bytecount32,
 				pce_dev->ce_sps.consumer_status);
 	return 0;
 };
@@ -750,22 +754,43 @@
 					pce_dev->ce_sps.producer_status);
 	} else {
 		if (pce_dev->ce_sps.minor_version == 0) {
-			if (pce_dev->mode == QCE_MODE_CBC)
-				memcpy(iv, (char *)sg_virt(areq->src),
-							sizeof(iv));
-
+			if (pce_dev->mode == QCE_MODE_CBC) {
+				if  (pce_dev->dir == QCE_DECRYPT)
+					memcpy(iv, (char *)pce_dev->dec_iv,
+								sizeof(iv));
+				else
+					memcpy(iv, (unsigned char *)
+						(sg_virt(areq->src) +
+						areq->src->length - 16),
+						sizeof(iv));
+			}
 			if ((pce_dev->mode == QCE_MODE_CTR) ||
 				(pce_dev->mode == QCE_MODE_XTS)) {
 				uint32_t num_blk = 0;
-				uint32_t cntr_iv = 0;
+				uint32_t cntr_iv3 = 0;
+				unsigned long long cntr_iv64 = 0;
+				unsigned char *b = (unsigned char *)(&cntr_iv3);
 
 				memcpy(iv, areq->info, sizeof(iv));
-				if (pce_dev->mode == QCE_MODE_CTR)
+				if (pce_dev->mode != QCE_MODE_XTS)
 					num_blk = areq->nbytes/16;
-				cntr_iv = (u32)(((u32)(*(iv + 14))) << 8) |
-							(u32)(*(iv + 15));
-				*(iv + 14) = (char)((cntr_iv + num_blk) >> 8);
-				*(iv + 15) = (char)((cntr_iv + num_blk) & 0xFF);
+				else
+					num_blk = 1;
+				cntr_iv3 =  ((*(iv + 12) << 24) & 0xff000000) |
+					(((*(iv + 13)) << 16) & 0xff0000) |
+					(((*(iv + 14)) << 8) & 0xff00) |
+					(*(iv + 15) & 0xff);
+				cntr_iv64 =
+					(((unsigned long long)cntr_iv3 &
+					(unsigned long long)0xFFFFFFFFULL) +
+					(unsigned long long)num_blk) %
+					(unsigned long long)(0x100000000ULL);
+
+				cntr_iv3 = (u32)(cntr_iv64 & 0xFFFFFFFF);
+				*(iv + 15) = (char)(*b);
+				*(iv + 14) = (char)(*(b + 1));
+				*(iv + 13) = (char)(*(b + 2));
+				*(iv + 12) = (char)(*(b + 3));
 			}
 		} else {
 			memcpy(iv,
@@ -1446,10 +1471,11 @@
 					0, &pcl_info->encr_xts_key);
 		for (i = 1; i < xts_key_reg; i++)
 			qce_add_cmd_element(pdev, &ce_vaddr,
-				(CRYPTO_ENCR_KEY0_REG + i * sizeof(uint32_t)),
-				0, NULL);
+				(CRYPTO_ENCR_XTS_KEY0_REG +
+						i * sizeof(uint32_t)), 0, NULL);
 		qce_add_cmd_element(pdev, &ce_vaddr,
-				CRYPTO_ENCR_XTS_DU_SIZE_REG, 0, NULL);
+				CRYPTO_ENCR_XTS_DU_SIZE_REG, 0,
+					&pcl_info->encr_xts_du_size);
 	}
 	if (iv_reg) {
 		qce_add_cmd_element(pdev, &ce_vaddr, CRYPTO_CNTR0_IV0_REG, 0,
@@ -1614,13 +1640,6 @@
 						0, &pcl_info->auth_seg_size);
 		qce_add_cmd_element(pdev, &ce_vaddr, CRYPTO_AUTH_SEG_CFG_REG,
 						0, &pcl_info->auth_seg_size);
-	} else {
-		qce_add_cmd_element(pdev, &ce_vaddr, CRYPTO_AUTH_SEG_SIZE_REG,
-						0, &pcl_info->auth_seg_size);
-		qce_add_cmd_element(pdev, &ce_vaddr, CRYPTO_AUTH_SEG_CFG_REG,
-						0, &pcl_info->auth_seg_size);
-		qce_add_cmd_element(pdev, &ce_vaddr, CRYPTO_AUTH_SEG_START_REG,
-						0, &pcl_info->auth_seg_size);
 	}
 	qce_add_cmd_element(pdev, &ce_vaddr, CRYPTO_CONFIG_REG,
 			(crypto_cfg | CRYPTO_LITTLE_ENDIAN_MASK),
@@ -2326,6 +2345,16 @@
 	} else {
 		pce_dev->dst_nents = pce_dev->src_nents;
 	}
+	pce_dev->dir = c_req->dir;
+	if  ((pce_dev->ce_sps.minor_version == 0) && (c_req->dir == QCE_DECRYPT)
+			&& (c_req->mode == QCE_MODE_CBC)) {
+		struct ablkcipher_request *areq =
+				(struct ablkcipher_request *)pce_dev->areq;
+		memcpy(pce_dev->dec_iv, (unsigned char *)sg_virt(areq->src) +
+					 areq->src->length - 16,
+			NUM_OF_CRYPTO_CNTR_IV_REG * CRYPTO_REG_SIZE);
+	}
+
 	/* set up crypto device */
 	rc = _ce_setup_cipher(pce_dev, c_req, areq->nbytes, 0, cmdlistinfo);
 	if (rc < 0)
diff --git a/drivers/crypto/msm/qce50.h b/drivers/crypto/msm/qce50.h
index c9eba82..8533636 100644
--- a/drivers/crypto/msm/qce50.h
+++ b/drivers/crypto/msm/qce50.h
@@ -36,7 +36,7 @@
 
 /* QCE max number of descriptor in a descriptor list */
 #define QCE_MAX_NUM_DESC    128
-#define SPS_MAX_PKT_SIZE  (64 * 1024  - 1)
+#define SPS_MAX_PKT_SIZE  (32 * 1024  - 64)
 
 /* State of consumer/producer Pipe */
 enum qce_pipe_st_enum {
diff --git a/drivers/crypto/msm/qcryptohw_50.h b/drivers/crypto/msm/qcryptohw_50.h
index 1c904ed..d77311d 100644
--- a/drivers/crypto/msm/qcryptohw_50.h
+++ b/drivers/crypto/msm/qcryptohw_50.h
@@ -144,7 +144,7 @@
 #define CRYPTO_ENCR_CCM_INT_CNTR2_REG		0x1A228
 #define CRYPTO_ENCR_CCM_INT_CNTR3_REG		0x1A22C
 
-#define CRYPTO_ENCR_XTS_DU_SIZE_REG		0xA1230
+#define CRYPTO_ENCR_XTS_DU_SIZE_REG		0x1A230
 
 #define CRYPTO_AUTH_SEG_CFG_REG			0x1A300
 #define CRYPTO_AUTH_SEG_SIZE_REG		0x1A304
diff --git a/drivers/gpio/qpnp-pin.c b/drivers/gpio/qpnp-pin.c
index 67a2e6b..527fd1b 100644
--- a/drivers/gpio/qpnp-pin.c
+++ b/drivers/gpio/qpnp-pin.c
@@ -128,7 +128,7 @@
 	Q_PIN_CFG_PULL,
 	Q_PIN_CFG_VIN_SEL,
 	Q_PIN_CFG_OUT_STRENGTH,
-	Q_PIN_CFG_SELECT,
+	Q_PIN_CFG_SRC_SEL,
 	Q_PIN_CFG_MASTER_EN,
 	Q_PIN_CFG_AOUT_REF,
 	Q_PIN_CFG_AIN_ROUTE,
@@ -289,7 +289,7 @@
 		    val == 0)
 			return -EINVAL;
 		break;
-	case Q_PIN_CFG_SELECT:
+	case Q_PIN_CFG_SRC_SEL:
 		if (q_spec->type == Q_MPP_TYPE &&
 		    (val == QPNP_PIN_SEL_FUNC_1 ||
 		     val == QPNP_PIN_SEL_FUNC_2))
@@ -348,9 +348,9 @@
 	else if (Q_CHK_INVALID(Q_PIN_CFG_INVERT, q_spec, param->invert))
 		pr_err("invalid invert polarity value %d for %s %d\n",
 						param->invert,  name, pin);
-	else if (Q_CHK_INVALID(Q_PIN_CFG_SELECT, q_spec, param->select))
+	else if (Q_CHK_INVALID(Q_PIN_CFG_SRC_SEL, q_spec, param->src_sel))
 		pr_err("invalid source select value %d for %s %d\n",
-						param->select, name, pin);
+						param->src_sel, name, pin);
 	else if (Q_CHK_INVALID(Q_PIN_CFG_OUT_STRENGTH,
 						q_spec, param->out_strength))
 		pr_err("invalid out strength value %d for %s %d\n",
@@ -506,10 +506,10 @@
 		q_reg_clr_set(&q_spec->regs[Q_REG_I_MODE_CTL],
 			  Q_REG_OUT_INVERT_SHIFT, Q_REG_OUT_INVERT_MASK,
 			  param->invert);
-	if (Q_HAVE_HW_SP(Q_PIN_CFG_SELECT, q_spec, param->select))
+	if (Q_HAVE_HW_SP(Q_PIN_CFG_SRC_SEL, q_spec, param->src_sel))
 		q_reg_clr_set(&q_spec->regs[Q_REG_I_MODE_CTL],
 			  Q_REG_SRC_SEL_SHIFT, Q_REG_SRC_SEL_MASK,
-			  param->select);
+			  param->src_sel);
 	if (Q_HAVE_HW_SP(Q_PIN_CFG_OUT_STRENGTH, q_spec, param->out_strength))
 		q_reg_clr_set(&q_spec->regs[Q_REG_I_DIG_OUT_CTL],
 			  Q_REG_OUT_STRENGTH_SHIFT, Q_REG_OUT_STRENGTH_MASK,
@@ -828,7 +828,7 @@
 	param.out_strength = q_reg_get(&q_spec->regs[Q_REG_I_DIG_OUT_CTL],
 				       Q_REG_OUT_STRENGTH_SHIFT,
 				       Q_REG_OUT_STRENGTH_MASK);
-	param.select   = q_reg_get(&q_spec->regs[Q_REG_I_MODE_CTL],
+	param.src_sel   = q_reg_get(&q_spec->regs[Q_REG_I_MODE_CTL],
 				       Q_REG_SRC_SEL_SHIFT, Q_REG_SRC_SEL_MASK);
 	param.master_en    = q_reg_get(&q_spec->regs[Q_REG_I_EN_CTL],
 				       Q_REG_MASTER_EN_SHIFT,
@@ -855,8 +855,8 @@
 		&param.vin_sel);
 	of_property_read_u32(node, "qcom,out-strength",
 		&param.out_strength);
-	of_property_read_u32(node, "qcom,src-select",
-		&param.select);
+	of_property_read_u32(node, "qcom,src-sel",
+		&param.src_sel);
 	of_property_read_u32(node, "qcom,master-en",
 		&param.master_en);
 	of_property_read_u32(node, "qcom,aout-ref",
@@ -942,7 +942,7 @@
 		cfg->shift = Q_REG_OUT_STRENGTH_SHIFT;
 		cfg->mask = Q_REG_OUT_STRENGTH_MASK;
 		break;
-	case Q_PIN_CFG_SELECT:
+	case Q_PIN_CFG_SRC_SEL:
 		cfg->addr = Q_REG_MODE_CTL;
 		cfg->idx = Q_REG_I_MODE_CTL;
 		cfg->shift = Q_REG_SRC_SEL_SHIFT;
@@ -1036,7 +1036,7 @@
 	{ Q_PIN_CFG_PULL, "pull" },
 	{ Q_PIN_CFG_VIN_SEL, "vin_sel" },
 	{ Q_PIN_CFG_OUT_STRENGTH, "out_strength" },
-	{ Q_PIN_CFG_SELECT, "select" },
+	{ Q_PIN_CFG_SRC_SEL, "src_sel" },
 	{ Q_PIN_CFG_MASTER_EN, "master_en" },
 	{ Q_PIN_CFG_AOUT_REF, "aout_ref" },
 	{ Q_PIN_CFG_AIN_ROUTE, "ain_route" },
diff --git a/drivers/gpu/ion/ion.c b/drivers/gpu/ion/ion.c
index a48e6b2..b3df752 100644
--- a/drivers/gpu/ion/ion.c
+++ b/drivers/gpu/ion/ion.c
@@ -261,6 +261,12 @@
 	mutex_unlock(&buffer->lock);
 }
 
+static void ion_delayed_unsecure(struct ion_buffer *buffer)
+{
+	if (buffer->heap->ops->unsecure_buffer)
+		buffer->heap->ops->unsecure_buffer(buffer, 1);
+}
+
 static void ion_buffer_destroy(struct kref *kref)
 {
 	struct ion_buffer *buffer = container_of(kref, struct ion_buffer, ref);
@@ -271,6 +277,7 @@
 
 	buffer->heap->ops->unmap_dma(buffer->heap, buffer);
 
+	ion_delayed_unsecure(buffer);
 	ion_iommu_delayed_unmap(buffer);
 	buffer->heap->ops->free(buffer);
 	mutex_lock(&dev->lock);
@@ -1644,6 +1651,73 @@
 	mutex_unlock(&dev->lock);
 }
 
+int ion_secure_handle(struct ion_client *client, struct ion_handle *handle,
+			int version, void *data, int flags)
+{
+	int ret = -EINVAL;
+	struct ion_heap *heap;
+	struct ion_buffer *buffer;
+
+	mutex_lock(&client->lock);
+	if (!ion_handle_validate(client, handle)) {
+		WARN(1, "%s: invalid handle passed to secure.\n", __func__);
+		goto out_unlock;
+	}
+
+	buffer = handle->buffer;
+	heap = buffer->heap;
+
+	if (heap->type != (enum ion_heap_type) ION_HEAP_TYPE_CP) {
+		pr_err("%s: cannot secure buffer from non secure heap\n",
+			__func__);
+		goto out_unlock;
+	}
+
+	BUG_ON(!buffer->heap->ops->secure_buffer);
+	/*
+	 * Protect the handle via the client lock to ensure we aren't
+	 * racing with free
+	 */
+	ret = buffer->heap->ops->secure_buffer(buffer, version, data, flags);
+
+out_unlock:
+	mutex_unlock(&client->lock);
+	return ret;
+}
+
+int ion_unsecure_handle(struct ion_client *client, struct ion_handle *handle)
+{
+	int ret = -EINVAL;
+	struct ion_heap *heap;
+	struct ion_buffer *buffer;
+
+	mutex_lock(&client->lock);
+	if (!ion_handle_validate(client, handle)) {
+		WARN(1, "%s: invalid handle passed to secure.\n", __func__);
+		goto out_unlock;
+	}
+
+	buffer = handle->buffer;
+	heap = buffer->heap;
+
+	if (heap->type != (enum ion_heap_type) ION_HEAP_TYPE_CP) {
+		pr_err("%s: cannot secure buffer from non secure heap\n",
+			__func__);
+		goto out_unlock;
+	}
+
+	BUG_ON(!buffer->heap->ops->unsecure_buffer);
+	/*
+	 * Protect the handle via the client lock to ensure we aren't
+	 * racing with free
+	 */
+	ret = buffer->heap->ops->unsecure_buffer(buffer, 0);
+
+out_unlock:
+	mutex_unlock(&client->lock);
+	return ret;
+}
+
 int ion_secure_heap(struct ion_device *dev, int heap_id, int version,
 			void *data)
 {
diff --git a/drivers/gpu/ion/ion_cp_heap.c b/drivers/gpu/ion/ion_cp_heap.c
index 2070abf..aa3469c 100644
--- a/drivers/gpu/ion/ion_cp_heap.c
+++ b/drivers/gpu/ion/ion_cp_heap.c
@@ -85,8 +85,8 @@
 	unsigned int heap_protected;
 	unsigned long allocated_bytes;
 	unsigned long total_size;
-	int (*request_region)(void *);
-	int (*release_region)(void *);
+	int (*heap_request_region)(void *);
+	int (*heap_release_region)(void *);
 	void *bus_id;
 	unsigned long kmap_cached_count;
 	unsigned long kmap_uncached_count;
@@ -106,6 +106,26 @@
 	HEAP_PROTECTED = 1,
 };
 
+struct ion_cp_buffer {
+	phys_addr_t buffer;
+	atomic_t secure_cnt;
+	int is_secure;
+	int want_delayed_unsecure;
+	/*
+	 * Currently all user/kernel mapping is protected by the heap lock.
+	 * This is sufficient to protect the map count as well. The lock
+	 * should be used to protect map_cnt if the whole heap lock is
+	 * ever removed.
+	 */
+	atomic_t map_cnt;
+	/*
+	 * protects secure_cnt for securing.
+	 */
+	struct mutex lock;
+	int version;
+	void *data;
+};
+
 static int ion_cp_protect_mem(unsigned int phy_base, unsigned int size,
 			unsigned int permission_type, int version,
 			void *data);
@@ -124,6 +144,170 @@
 	return cp_heap->kmap_cached_count + cp_heap->kmap_uncached_count;
 }
 
+static int ion_on_first_alloc(struct ion_heap *heap)
+{
+	struct ion_cp_heap *cp_heap =
+		container_of(heap, struct ion_cp_heap, heap);
+	int ret_value;
+
+	if (cp_heap->reusable) {
+		ret_value = fmem_set_state(FMEM_C_STATE);
+		if (ret_value)
+			return 1;
+	}
+	return 0;
+}
+
+static void ion_on_last_free(struct ion_heap *heap)
+{
+	struct ion_cp_heap *cp_heap =
+		container_of(heap, struct ion_cp_heap, heap);
+
+	if (cp_heap->reusable)
+		if (fmem_set_state(FMEM_T_STATE) != 0)
+			pr_err("%s: unable to transition heap to T-state\n",
+				__func__);
+}
+
+/* Must be protected by ion_cp_buffer lock */
+static int __ion_cp_protect_buffer(struct ion_buffer *buffer, int version,
+					void *data, int flags)
+{
+	struct ion_cp_buffer *buf = buffer->priv_virt;
+	int ret_value = 0;
+
+	if (atomic_inc_return(&buf->secure_cnt) == 1) {
+		ret_value = ion_cp_protect_mem(buf->buffer,
+				buffer->size, 0,
+				version, data);
+
+		if (ret_value) {
+			pr_err("Failed to secure buffer %p, error %d\n",
+				buffer, ret_value);
+			atomic_dec(&buf->secure_cnt);
+		} else {
+			pr_debug("Protected buffer %p from %x-%x\n",
+				buffer, buf->buffer,
+				buf->buffer + buffer->size);
+			buf->want_delayed_unsecure |=
+				flags & ION_UNSECURE_DELAYED ? 1 : 0;
+			buf->data = data;
+			buf->version = version;
+		}
+	}
+	pr_debug("buffer %p protect count %d\n", buffer,
+		atomic_read(&buf->secure_cnt));
+	BUG_ON(atomic_read(&buf->secure_cnt) < 0);
+	return ret_value;
+}
+
+/* Must be protected by ion_cp_buffer lock */
+static int __ion_cp_unprotect_buffer(struct ion_buffer *buffer, int version,
+					void *data, int force_unsecure)
+{
+	struct ion_cp_buffer *buf = buffer->priv_virt;
+	int ret_value = 0;
+
+	if (force_unsecure) {
+		if (!buf->is_secure || atomic_read(&buf->secure_cnt) == 0)
+			return 0;
+
+		if (atomic_read(&buf->secure_cnt) != 1) {
+			WARN(1, "Forcing unsecure of buffer with outstanding secure count %d!\n",
+				atomic_read(&buf->secure_cnt));
+			atomic_set(&buf->secure_cnt, 1);
+		}
+	}
+
+	if (atomic_dec_and_test(&buf->secure_cnt)) {
+		ret_value = ion_cp_unprotect_mem(
+			buf->buffer, buffer->size,
+			0, version, data);
+
+		if (ret_value) {
+			pr_err("Failed to unsecure buffer %p, error %d\n",
+				buffer, ret_value);
+			/*
+			 * If the force unsecure is happening, the buffer
+			 * is being destroyed. We failed to unsecure the
+			 * buffer even though the memory is given back.
+			 * Just die now rather than discovering later what
+			 * happens when trying to use the secured memory as
+			 * unsecured...
+			 */
+			BUG_ON(force_unsecure);
+			/* Bump the count back up one to try again later */
+			atomic_inc(&buf->secure_cnt);
+		} else {
+			buf->version = -1;
+			buf->data = NULL;
+		}
+	}
+	pr_debug("buffer %p unprotect count %d\n", buffer,
+		atomic_read(&buf->secure_cnt));
+	BUG_ON(atomic_read(&buf->secure_cnt) < 0);
+	return ret_value;
+}
+
+int ion_cp_secure_buffer(struct ion_buffer *buffer, int version, void *data,
+				int flags)
+{
+	int ret_value;
+	struct ion_cp_buffer *buf = buffer->priv_virt;
+
+	mutex_lock(&buf->lock);
+	if (!buf->is_secure) {
+		pr_err("%s: buffer %p was not allocated as secure\n",
+			__func__, buffer);
+		ret_value = -EINVAL;
+		goto out_unlock;
+	}
+
+	if (ION_IS_CACHED(buffer->flags)) {
+		pr_err("%s: buffer %p was allocated as cached\n",
+			__func__, buffer);
+		ret_value = -EINVAL;
+		goto out_unlock;
+	}
+
+	if (atomic_read(&buf->map_cnt)) {
+		pr_err("%s: cannot secure buffer %p with outstanding mappings. Total count: %d",
+			__func__, buffer, atomic_read(&buf->map_cnt));
+		ret_value = -EINVAL;
+		goto out_unlock;
+	}
+
+	if (atomic_read(&buf->secure_cnt)) {
+		if (buf->version != version || buf->data != data) {
+			pr_err("%s: Trying to re-secure buffer with different values",
+				__func__);
+			pr_err("Last secured version: %d Currrent %d\n",
+				buf->version, version);
+			pr_err("Last secured data: %p current %p\n",
+				buf->data, data);
+			ret_value = -EINVAL;
+			goto out_unlock;
+		}
+	}
+	ret_value = __ion_cp_protect_buffer(buffer, version, data, flags);
+
+out_unlock:
+	mutex_unlock(&buf->lock);
+	return ret_value;
+}
+
+int ion_cp_unsecure_buffer(struct ion_buffer *buffer, int force_unsecure)
+{
+	int ret_value = 0;
+	struct ion_cp_buffer *buf = buffer->priv_virt;
+
+	mutex_lock(&buf->lock);
+	ret_value = __ion_cp_unprotect_buffer(buffer, buf->version, buf->data,
+						force_unsecure);
+	mutex_unlock(&buf->lock);
+	return ret_value;
+}
+
 /**
  * Protects memory if heap is unsecured heap. Also ensures that we are in
  * the correct FMEM state if this heap is a reusable heap.
@@ -137,11 +321,9 @@
 
 	if (atomic_inc_return(&cp_heap->protect_cnt) == 1) {
 		/* Make sure we are in C state when the heap is protected. */
-		if (cp_heap->reusable && !cp_heap->allocated_bytes) {
-			ret_value = fmem_set_state(FMEM_C_STATE);
-			if (ret_value)
+		if (!cp_heap->allocated_bytes)
+			if (ion_on_first_alloc(heap))
 				goto out;
-		}
 
 		ret_value = ion_cp_protect_mem(cp_heap->secure_base,
 				cp_heap->secure_size, cp_heap->permission_type,
@@ -150,11 +332,9 @@
 			pr_err("Failed to protect memory for heap %s - "
 				"error code: %d\n", heap->name, ret_value);
 
-			if (cp_heap->reusable && !cp_heap->allocated_bytes) {
-				if (fmem_set_state(FMEM_T_STATE) != 0)
-					pr_err("%s: unable to transition heap to T-state\n",
-						__func__);
-			}
+			if (!cp_heap->allocated_bytes)
+				ion_on_last_free(heap);
+
 			atomic_dec(&cp_heap->protect_cnt);
 		} else {
 			cp_heap->heap_protected = HEAP_PROTECTED;
@@ -191,11 +371,8 @@
 			pr_debug("Un-protected heap %s @ 0x%x\n", heap->name,
 				(unsigned int) cp_heap->base);
 
-			if (cp_heap->reusable && !cp_heap->allocated_bytes) {
-				if (fmem_set_state(FMEM_T_STATE) != 0)
-					pr_err("%s: unable to transition heap to T-state",
-						__func__);
-			}
+			if (!cp_heap->allocated_bytes)
+				ion_on_last_free(heap);
 		}
 	}
 	pr_debug("%s: protect count is %d\n", __func__,
@@ -222,26 +399,27 @@
 		return ION_CP_ALLOCATE_FAIL;
 	}
 
-	if (secure_allocation &&
-	    (cp_heap->umap_count > 0 || cp_heap->kmap_cached_count > 0)) {
-		mutex_unlock(&cp_heap->lock);
-		pr_err("ION cannot allocate secure memory from heap with "
-			"outstanding mappings: User space: %lu, kernel space "
-			"(cached): %lu\n", cp_heap->umap_count,
-					   cp_heap->kmap_cached_count);
-		return ION_CP_ALLOCATE_FAIL;
+	/*
+	 * The check above already checked for non-secure allocations when the
+	 * heap is protected. HEAP_PROTECTED implies that this must be a secure
+	 * allocation. If the heap is protected and there are userspace or
+	 * cached kernel mappings, something has gone wrong in the security
+	 * model.
+	 */
+	if (cp_heap->heap_protected == HEAP_PROTECTED) {
+		BUG_ON(cp_heap->umap_count != 0);
+		BUG_ON(cp_heap->kmap_cached_count != 0);
 	}
 
 	/*
 	 * if this is the first reusable allocation, transition
 	 * the heap
 	 */
-	if (cp_heap->reusable && !cp_heap->allocated_bytes) {
-		if (fmem_set_state(FMEM_C_STATE) != 0) {
+	if (!cp_heap->allocated_bytes)
+		if (ion_on_first_alloc(heap)) {
 			mutex_unlock(&cp_heap->lock);
 			return ION_RESERVED_ALLOCATE_FAIL;
 		}
-	}
 
 	cp_heap->allocated_bytes += size;
 	mutex_unlock(&cp_heap->lock);
@@ -260,13 +438,9 @@
 				__func__, heap->name,
 				cp_heap->total_size -
 				cp_heap->allocated_bytes, size);
-
-		if (cp_heap->reusable && !cp_heap->allocated_bytes &&
-		    cp_heap->heap_protected == HEAP_NOT_PROTECTED) {
-			if (fmem_set_state(FMEM_T_STATE) != 0)
-				pr_err("%s: unable to transition heap to T-state\n",
-					__func__);
-		}
+		if (!cp_heap->allocated_bytes &&
+			cp_heap->heap_protected == HEAP_NOT_PROTECTED)
+			ion_on_last_free(heap);
 		mutex_unlock(&cp_heap->lock);
 
 		return ION_CP_ALLOCATE_FAIL;
@@ -311,12 +485,9 @@
 	mutex_lock(&cp_heap->lock);
 	cp_heap->allocated_bytes -= size;
 
-	if (cp_heap->reusable && !cp_heap->allocated_bytes &&
-	    cp_heap->heap_protected == HEAP_NOT_PROTECTED) {
-		if (fmem_set_state(FMEM_T_STATE) != 0)
-			pr_err("%s: unable to transition heap to T-state\n",
-				__func__);
-	}
+	if (!cp_heap->allocated_bytes &&
+		cp_heap->heap_protected == HEAP_NOT_PROTECTED)
+		ion_on_last_free(heap);
 
 	/* Unmap everything if we previously mapped the whole heap at once. */
 	if (!cp_heap->allocated_bytes) {
@@ -344,7 +515,9 @@
 				  struct ion_buffer *buffer,
 				  ion_phys_addr_t *addr, size_t *len)
 {
-	*addr = buffer->priv_phys;
+	struct ion_cp_buffer *buf = buffer->priv_virt;
+
+	*addr = buf->buffer;
 	*len = buffer->size;
 	return 0;
 }
@@ -354,34 +527,76 @@
 				      unsigned long size, unsigned long align,
 				      unsigned long flags)
 {
-	buffer->priv_phys = ion_cp_allocate(heap, size, align, flags);
-	return buffer->priv_phys == ION_CP_ALLOCATE_FAIL ? -ENOMEM : 0;
+	struct ion_cp_buffer *buf;
+	phys_addr_t addr;
+
+	buf = kzalloc(sizeof(*buf), GFP_KERNEL);
+	if (!buf)
+		return ION_CP_ALLOCATE_FAIL;
+
+	addr = ion_cp_allocate(heap, size, align, flags);
+	if (addr == ION_CP_ALLOCATE_FAIL)
+		return -ENOMEM;
+
+	buf->buffer = addr;
+	buf->want_delayed_unsecure = 0;
+	atomic_set(&buf->secure_cnt, 0);
+	mutex_init(&buf->lock);
+	buf->is_secure = flags & ION_SECURE ? 1 : 0;
+	buffer->priv_virt = buf;
+
+	return 0;
 }
 
 static void ion_cp_heap_free(struct ion_buffer *buffer)
 {
 	struct ion_heap *heap = buffer->heap;
+	struct ion_cp_buffer *buf = buffer->priv_virt;
 
-	ion_cp_free(heap, buffer->priv_phys, buffer->size);
-	buffer->priv_phys = ION_CP_ALLOCATE_FAIL;
+	ion_cp_free(heap, buf->buffer, buffer->size);
+	WARN_ON(atomic_read(&buf->secure_cnt));
+	WARN_ON(atomic_read(&buf->map_cnt));
+	kfree(buf);
+
+	buffer->priv_virt = NULL;
 }
 
 struct sg_table *ion_cp_heap_create_sg_table(struct ion_buffer *buffer)
 {
 	struct sg_table *table;
 	int ret;
+	struct ion_cp_buffer *buf = buffer->priv_virt;
 
 	table = kzalloc(sizeof(struct sg_table), GFP_KERNEL);
 	if (!table)
 		return ERR_PTR(-ENOMEM);
 
-	ret = sg_alloc_table(table, 1, GFP_KERNEL);
-	if (ret)
-		goto err0;
+	if (buf->is_secure && IS_ALIGNED(buffer->size, SZ_1M)) {
+		int n_chunks;
+		int i;
+		struct scatterlist *sg;
 
-	table->sgl->length = buffer->size;
-	table->sgl->offset = 0;
-	table->sgl->dma_address = buffer->priv_phys;
+		/* Count number of 1MB chunks. Alignment is already checked. */
+		n_chunks = buffer->size >> 20;
+
+		ret = sg_alloc_table(table, n_chunks, GFP_KERNEL);
+		if (ret)
+			goto err0;
+
+		for_each_sg(table->sgl, sg, table->nents, i) {
+			sg_dma_address(sg) = buf->buffer + i * SZ_1M;
+			sg->length = SZ_1M;
+			sg->offset = 0;
+		}
+	} else {
+		ret = sg_alloc_table(table, 1, GFP_KERNEL);
+		if (ret)
+			goto err0;
+
+		table->sgl->length = buffer->size;
+		table->sgl->offset = 0;
+		table->sgl->dma_address = buf->buffer;
+	}
 
 	return table;
 err0:
@@ -411,8 +626,9 @@
 {
 	int ret_value = 0;
 	if ((cp_heap->umap_count + ion_cp_get_total_kmap_count(cp_heap)) == 0)
-		if (cp_heap->request_region)
-			ret_value = cp_heap->request_region(cp_heap->bus_id);
+		if (cp_heap->heap_request_region)
+			ret_value = cp_heap->heap_request_region(
+					cp_heap->bus_id);
 	return ret_value;
 }
 
@@ -423,8 +639,9 @@
 {
 	int ret_value = 0;
 	if ((cp_heap->umap_count + ion_cp_get_total_kmap_count(cp_heap)) == 0)
-		if (cp_heap->release_region)
-			ret_value = cp_heap->release_region(cp_heap->bus_id);
+		if (cp_heap->heap_release_region)
+			ret_value = cp_heap->heap_release_region(
+					cp_heap->bus_id);
 	return ret_value;
 }
 
@@ -432,17 +649,18 @@
 				void *virt_base, unsigned long flags)
 {
 	int ret;
-	unsigned int offset = buffer->priv_phys - phys_base;
+	struct ion_cp_buffer *buf = buffer->priv_virt;
+	unsigned int offset = buf->buffer - phys_base;
 	unsigned long start = ((unsigned long)virt_base) + offset;
 	const struct mem_type *type = ION_IS_CACHED(flags) ?
 				get_mem_type(MT_DEVICE_CACHED) :
 				get_mem_type(MT_DEVICE);
 
-	if (phys_base > buffer->priv_phys)
+	if (phys_base > buf->buffer)
 		return NULL;
 
 
-	ret = ioremap_pages(start, buffer->priv_phys, buffer->size, type);
+	ret = ioremap_pages(start, buf->buffer, buffer->size, type);
 
 	if (!ret)
 		return (void *)start;
@@ -455,6 +673,7 @@
 	struct ion_cp_heap *cp_heap =
 		container_of(heap, struct ion_cp_heap, heap);
 	void *ret_value = NULL;
+	struct ion_cp_buffer *buf = buffer->priv_virt;
 
 	mutex_lock(&cp_heap->lock);
 	if ((cp_heap->heap_protected == HEAP_NOT_PROTECTED) ||
@@ -472,10 +691,10 @@
 
 		} else {
 			if (ION_IS_CACHED(buffer->flags))
-				ret_value = ioremap_cached(buffer->priv_phys,
+				ret_value = ioremap_cached(buf->buffer,
 							   buffer->size);
 			else
-				ret_value = ioremap(buffer->priv_phys,
+				ret_value = ioremap(buf->buffer,
 						    buffer->size);
 		}
 
@@ -486,6 +705,7 @@
 				++cp_heap->kmap_cached_count;
 			else
 				++cp_heap->kmap_uncached_count;
+			atomic_inc(&buf->map_cnt);
 		}
 	}
 	mutex_unlock(&cp_heap->lock);
@@ -497,6 +717,7 @@
 {
 	struct ion_cp_heap *cp_heap =
 		container_of(heap, struct ion_cp_heap, heap);
+	struct ion_cp_buffer *buf = buffer->priv_virt;
 
 	if (cp_heap->reusable)
 		unmap_kernel_range((unsigned long)buffer->vaddr, buffer->size);
@@ -510,6 +731,8 @@
 		--cp_heap->kmap_cached_count;
 	else
 		--cp_heap->kmap_uncached_count;
+
+	atomic_dec(&buf->map_cnt);
 	ion_cp_release_region(cp_heap);
 	mutex_unlock(&cp_heap->lock);
 
@@ -522,6 +745,7 @@
 	int ret_value = -EAGAIN;
 	struct ion_cp_heap *cp_heap =
 		container_of(heap, struct ion_cp_heap, heap);
+	struct ion_cp_buffer *buf = buffer->priv_virt;
 
 	mutex_lock(&cp_heap->lock);
 	if (cp_heap->heap_protected == HEAP_NOT_PROTECTED) {
@@ -535,14 +759,17 @@
 							vma->vm_page_prot);
 
 		ret_value =  remap_pfn_range(vma, vma->vm_start,
-			__phys_to_pfn(buffer->priv_phys) + vma->vm_pgoff,
+			__phys_to_pfn(buf->buffer) + vma->vm_pgoff,
 			vma->vm_end - vma->vm_start,
 			vma->vm_page_prot);
 
-		if (ret_value)
+		if (ret_value) {
 			ion_cp_release_region(cp_heap);
-		else
+		} else {
+			atomic_inc(&buf->map_cnt);
 			++cp_heap->umap_count;
+		}
+
 	}
 	mutex_unlock(&cp_heap->lock);
 	return ret_value;
@@ -553,9 +780,11 @@
 {
 	struct ion_cp_heap *cp_heap =
 			container_of(heap, struct ion_cp_heap, heap);
+	struct ion_cp_buffer *buf = buffer->priv_virt;
 
 	mutex_lock(&cp_heap->lock);
 	--cp_heap->umap_count;
+	atomic_dec(&buf->map_cnt);
 	ion_cp_release_region(cp_heap);
 	mutex_unlock(&cp_heap->lock);
 }
@@ -567,6 +796,7 @@
 	void (*outer_cache_op)(phys_addr_t, phys_addr_t);
 	struct ion_cp_heap *cp_heap =
 	     container_of(heap, struct  ion_cp_heap, heap);
+	struct ion_cp_buffer *buf = buffer->priv_virt;
 
 	switch (cmd) {
 	case ION_IOC_CLEAN_CACHES:
@@ -586,7 +816,7 @@
 	}
 
 	if (cp_heap->has_outer_cache) {
-		unsigned long pstart = buffer->priv_phys + offset;
+		unsigned long pstart = buf->buffer + offset;
 		outer_cache_op(pstart, pstart + length);
 	}
 	return 0;
@@ -774,25 +1004,26 @@
 	struct ion_cp_heap *cp_heap =
 		container_of(buffer->heap, struct ion_cp_heap, heap);
 	int prot = IOMMU_WRITE | IOMMU_READ;
+	struct ion_cp_buffer *buf = buffer->priv_virt;
 	prot |= ION_IS_CACHED(flags) ? IOMMU_CACHE : 0;
 
 	data->mapped_size = iova_length;
 
 	if (!msm_use_iommu()) {
-		data->iova_addr = buffer->priv_phys;
+		data->iova_addr = buf->buffer;
 		return 0;
 	}
 
 	if (cp_heap->iommu_iova[domain_num]) {
 		/* Already mapped. */
-		unsigned long offset = buffer->priv_phys - cp_heap->base;
+		unsigned long offset = buf->buffer - cp_heap->base;
 		data->iova_addr = cp_heap->iommu_iova[domain_num] + offset;
 		return 0;
 	} else if (cp_heap->iommu_map_all) {
 		ret = iommu_map_all(domain_num, cp_heap, partition_num, prot);
 		if (!ret) {
 			unsigned long offset =
-					buffer->priv_phys - cp_heap->base;
+					buf->buffer - cp_heap->base;
 			data->iova_addr =
 				cp_heap->iommu_iova[domain_num] + offset;
 			cp_heap->iommu_partition[domain_num] = partition_num;
@@ -902,6 +1133,8 @@
 	.unsecure_heap = ion_cp_unsecure_heap,
 	.map_iommu = ion_cp_heap_map_iommu,
 	.unmap_iommu = ion_cp_heap_unmap_iommu,
+	.secure_buffer = ion_cp_secure_buffer,
+	.unsecure_buffer = ion_cp_unsecure_buffer,
 };
 
 struct ion_heap *ion_cp_heap_create(struct ion_platform_heap *heap_data)
@@ -949,9 +1182,11 @@
 		if (extra_data->setup_region)
 			cp_heap->bus_id = extra_data->setup_region();
 		if (extra_data->request_region)
-			cp_heap->request_region = extra_data->request_region;
+			cp_heap->heap_request_region =
+				extra_data->request_region;
 		if (extra_data->release_region)
-			cp_heap->release_region = extra_data->release_region;
+			cp_heap->heap_release_region =
+				extra_data->release_region;
 		cp_heap->iommu_map_all =
 				extra_data->iommu_map_all;
 		cp_heap->iommu_2x_map_domain =
diff --git a/drivers/gpu/ion/ion_priv.h b/drivers/gpu/ion/ion_priv.h
index 273e57e..991a310 100644
--- a/drivers/gpu/ion/ion_priv.h
+++ b/drivers/gpu/ion/ion_priv.h
@@ -147,6 +147,9 @@
 			   const struct rb_root *mem_map);
 	int (*secure_heap)(struct ion_heap *heap, int version, void *data);
 	int (*unsecure_heap)(struct ion_heap *heap, int version, void *data);
+	int (*secure_buffer)(struct ion_buffer *buffer, int version,
+				void *data, int flags);
+	int (*unsecure_buffer)(struct ion_buffer *buffer, int force_unsecure);
 };
 
 /**
@@ -307,4 +310,10 @@
 
 void ion_mem_map_show(struct ion_heap *heap);
 
+
+
+int ion_secure_handle(struct ion_client *client, struct ion_handle *handle,
+			int version, void *data, int flags);
+
+int ion_unsecure_handle(struct ion_client *client, struct ion_handle *handle);
 #endif /* _ION_PRIV_H */
diff --git a/drivers/gpu/ion/msm/ion_cp_common.c b/drivers/gpu/ion/msm/ion_cp_common.c
index b274ba2..803b04a 100644
--- a/drivers/gpu/ion/msm/ion_cp_common.c
+++ b/drivers/gpu/ion/msm/ion_cp_common.c
@@ -37,6 +37,7 @@
 				int lock)
 {
 	struct cp2_lock_req request;
+	u32 resp;
 
 	request.mem_usage = usage;
 	request.lock = lock;
@@ -46,7 +47,7 @@
 	request.chunks.chunk_size = chunk_size;
 
 	return scm_call(SCM_SVC_CP, MEM_PROTECT_LOCK_ID,
-			&request, sizeof(request), NULL, 0);
+			&request, sizeof(request), &resp, sizeof(resp));
 
 }
 
diff --git a/drivers/gpu/ion/msm/msm_ion.c b/drivers/gpu/ion/msm/msm_ion.c
index deff514..20f84d6 100644
--- a/drivers/gpu/ion/msm/msm_ion.c
+++ b/drivers/gpu/ion/msm/msm_ion.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -24,6 +24,7 @@
 #include <linux/sched.h>
 #include <linux/rwsem.h>
 #include <linux/uaccess.h>
+#include <linux/memblock.h>
 #include <mach/ion.h>
 #include <mach/msm_memtypes.h>
 #include "../ion_priv.h"
@@ -88,6 +89,16 @@
 		.name	= ION_AUDIO_HEAP_NAME,
 	},
 	{
+		.id	= ION_PIL1_HEAP_ID,
+		.type	= ION_HEAP_TYPE_CARVEOUT,
+		.name	= ION_PIL1_HEAP_NAME,
+	},
+	{
+		.id	= ION_PIL2_HEAP_ID,
+		.type	= ION_HEAP_TYPE_CARVEOUT,
+		.name	= ION_PIL2_HEAP_NAME,
+	},
+	{
 		.id	= ION_CP_WB_HEAP_ID,
 		.type	= ION_HEAP_TYPE_CP,
 		.name	= ION_WB_HEAP_NAME,
@@ -130,6 +141,22 @@
 }
 EXPORT_SYMBOL(msm_ion_unsecure_heap_2_0);
 
+int msm_ion_secure_buffer(struct ion_client *client, struct ion_handle *handle,
+				enum cp_mem_usage usage,
+				int flags)
+{
+	return ion_secure_handle(client, handle, ION_CP_V2,
+				(void *)usage, flags);
+}
+EXPORT_SYMBOL(msm_ion_secure_buffer);
+
+int msm_ion_unsecure_buffer(struct ion_client *client,
+				struct ion_handle *handle)
+{
+	return ion_unsecure_handle(client, handle);
+}
+EXPORT_SYMBOL(msm_ion_unsecure_buffer);
+
 int msm_ion_do_cache_op(struct ion_client *client, struct ion_handle *handle,
 			void *vaddr, unsigned long len, unsigned int cmd)
 {
@@ -442,6 +469,7 @@
 {
 	unsigned int val;
 	int ret = 0;
+	u32 out_values[2];
 	const char *memory_name_prop;
 
 	ret = of_property_read_u32(node, "qcom,memory-reservation-size", &val);
@@ -465,12 +493,29 @@
 			ret = -EINVAL;
 		}
 	} else {
-		ret = 0;
+		ret = of_property_read_u32_array(node, "qcom,memory-fixed",
+								out_values, 2);
+		if (!ret)
+			heap->size = out_values[1];
+		else
+			ret = 0;
 	}
 out:
 	return ret;
 }
 
+static void msm_ion_get_heap_base(struct device_node *node,
+				 struct ion_platform_heap *heap)
+{
+	u32 out_values[2];
+	int ret = 0;
+
+	ret = of_property_read_u32_array(node, "qcom,memory-fixed",
+							out_values, 2);
+	if (!ret)
+		heap->base = out_values[0];
+	return;
+}
 
 static void msm_ion_get_heap_adjacent(struct device_node *node,
 				      struct ion_platform_heap *heap)
@@ -544,6 +589,7 @@
 		if (ret)
 			goto free_heaps;
 
+		msm_ion_get_heap_base(node, &pdata->heaps[idx]);
 		msm_ion_get_heap_align(node, &pdata->heaps[idx]);
 
 		ret = msm_ion_get_heap_size(node, &pdata->heaps[idx]);
@@ -718,6 +764,19 @@
 	if (pdata_needs_to_be_freed)
 		free_pdata(pdata);
 
+	/* Check if each heap has been removed from the memblock */
+	for (i = 0; i < num_heaps; i++) {
+		struct ion_platform_heap *heap_data = &pdata->heaps[i];
+		if (!heap_data->base)
+			continue;
+		err = memblock_overlaps_memory(heap_data->base,
+						heap_data->size);
+		if (err) {
+			panic("ION heap %s not removed from memblock\n",
+							heap_data->name);
+		}
+	}
+
 	check_for_heap_overlap(pdata->heaps, num_heaps);
 	platform_set_drvdata(pdev, idev);
 	return 0;
diff --git a/drivers/gpu/msm/a3xx_reg.h b/drivers/gpu/msm/a3xx_reg.h
index 6a010a9..90f14e6 100644
--- a/drivers/gpu/msm/a3xx_reg.h
+++ b/drivers/gpu/msm/a3xx_reg.h
@@ -531,7 +531,11 @@
 #define RBBM_BLOCK_ID_MARB_3           0x2b
 
 /* RBBM_CLOCK_CTL default value */
-#define A3XX_RBBM_CLOCK_CTL_DEFAULT 0xBFFFFFFF
+#define A305_RBBM_CLOCK_CTL_DEFAULT 0xAAAAAAAA
+#define A320_RBBM_CLOCK_CTL_DEFAULT 0xBFFFFFFF
+#define A330_RBBM_CLOCK_CTL_DEFAULT 0xBFFCFFFF
+
+#define A330_RBBM_GPR0_CTL_DEFAULT  0x00000000
 
 /* COUNTABLE FOR SP PERFCOUNTER */
 #define SP_FS_FULL_ALU_INSTRUCTIONS    0x0E
diff --git a/drivers/gpu/msm/adreno.c b/drivers/gpu/msm/adreno.c
index 573e0a6..67cb34a 100644
--- a/drivers/gpu/msm/adreno.c
+++ b/drivers/gpu/msm/adreno.c
@@ -731,101 +731,6 @@
 	return ret;
 
 }
-static void adreno_of_free_bus_scale_info(struct msm_bus_scale_pdata *pdata)
-{
-	int i;
-
-	if (pdata == NULL)
-		return;
-
-	for (i = 0;  pdata->usecase && i < pdata->num_usecases; i++)
-		kfree(pdata->usecase[i].vectors);
-
-	kfree(pdata->usecase);
-	kfree(pdata);
-}
-
-struct msm_bus_scale_pdata *adreno_of_get_bus_scale(struct device_node *node)
-{
-	static int bus_vectors_src[3] = {MSM_BUS_MASTER_GRAPHICS_3D,
-		MSM_BUS_MASTER_GRAPHICS_3D_PORT1, MSM_BUS_MASTER_V_OCMEM_GFX3D};
-	static int bus_vectors_dst[2] = {MSM_BUS_SLAVE_EBI_CH0,
-		MSM_BUS_SLAVE_OCMEM};
-	const unsigned int *vectors;
-	struct msm_bus_scale_pdata *pdata;
-	int i, j, len, num_paths;
-	int ret = -EINVAL;
-
-	pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
-
-	if (!pdata) {
-		KGSL_CORE_ERR("kzalloc(%d) failed\n", sizeof(*pdata));
-		return ERR_PTR(-ENOMEM);
-	}
-
-	if (adreno_of_read_property(node, "qcom,grp3d-num-bus-scale-usecases",
-		&pdata->num_usecases)) {
-		pdata->num_usecases = 0;
-		goto err;
-	}
-
-	pdata->usecase =  kzalloc(pdata->num_usecases *
-		sizeof(struct msm_bus_paths), GFP_KERNEL);
-
-	if (pdata->usecase == NULL) {
-		KGSL_CORE_ERR("kzalloc (%d) failed\n",
-			pdata->num_usecases * sizeof(struct msm_bus_paths));
-		ret = -ENOMEM;
-		goto err;
-	}
-
-	if (adreno_of_read_property(node, "qcom,grp3d-num-vectors-per-usecase",
-		&num_paths))
-		goto err;
-
-	vectors = of_get_property(node, "qcom,grp3d-vectors", &len);
-
-	if (len != pdata->num_usecases * num_paths *
-		sizeof(struct msm_bus_vectors)) {
-		KGSL_CORE_ERR("Invalid size for the bus scale vectors\n");
-		goto err;
-	}
-
-	for (i = 0; i < pdata->num_usecases; i++) {
-		pdata->usecase[i].num_paths = num_paths;
-		pdata->usecase[i].vectors = kzalloc(num_paths *
-						sizeof(struct msm_bus_vectors),
-						GFP_KERNEL);
-		if (!pdata->usecase[i].vectors) {
-			KGSL_CORE_ERR("kzalloc(%d) failed\n",
-				num_paths * sizeof(struct msm_bus_vectors));
-			ret = -ENOMEM;
-			goto err;
-		}
-		for (j = 0; j < num_paths; j++) {
-			int index = (i * num_paths + j) * 4;
-			pdata->usecase[i].vectors[j].src =
-				bus_vectors_src[be32_to_cpu(vectors[index])];
-			pdata->usecase[i].vectors[j].dst =
-				bus_vectors_dst[
-					be32_to_cpu(vectors[index + 1])];
-			pdata->usecase[i].vectors[j].ab =
-				be32_to_cpu(vectors[index + 2]);
-			pdata->usecase[i].vectors[j].ib =
-				KGSL_CONVERT_TO_MBPS(
-					be32_to_cpu(vectors[index + 3]));
-		}
-	}
-
-	pdata->name = "grp3d";
-
-	return pdata;
-
-err:
-	adreno_of_free_bus_scale_info(pdata);
-
-	return ERR_PTR(ret);
-}
 
 static struct msm_dcvs_core_info *adreno_of_get_dcvs(struct device_node *parent)
 {
@@ -896,6 +801,19 @@
 			info->freq_tbl[index].leakage_energy_offset = 0;
 	}
 
+	if (adreno_of_read_property(node, "qcom,num-cores", &info->num_cores))
+		goto err;
+
+	info->sensors = kzalloc(info->num_cores *
+			sizeof(int),
+			GFP_KERNEL);
+
+	for (count = 0; count < info->num_cores; count++) {
+		if (adreno_of_read_property(node, "qcom,sensors",
+			&(info->sensors[count])))
+			goto err;
+	}
+
 	if (adreno_of_read_property(node, "qcom,core-core-type",
 		&info->core_param.core_type))
 		goto err;
@@ -1094,6 +1012,9 @@
 		&pdata->nap_allowed))
 		pdata->nap_allowed = 1;
 
+	pdata->strtstp_sleepwake = of_property_read_bool(pdev->dev.of_node,
+						"qcom,strtstp-sleepwake");
+
 	if (adreno_of_read_property(pdev->dev.of_node, "qcom,clk-map",
 		&pdata->clk_map))
 		goto err;
@@ -1105,7 +1026,7 @@
 
 	/* Bus Scale Data */
 
-	pdata->bus_scale_table = adreno_of_get_bus_scale(pdev->dev.of_node);
+	pdata->bus_scale_table = msm_bus_cl_get_pdata(pdev);
 	if (IS_ERR_OR_NULL(pdata->bus_scale_table)) {
 		ret = PTR_ERR(pdata->bus_scale_table);
 		goto err;
@@ -1126,7 +1047,6 @@
 
 err:
 	if (pdata) {
-		adreno_of_free_bus_scale_info(pdata->bus_scale_table);
 		if (pdata->core_info)
 			kfree(pdata->core_info->freq_tbl);
 		kfree(pdata->core_info);
@@ -1451,6 +1371,7 @@
 		ret = -ENOMEM;
 		goto done;
 	}
+	rec_data->fault = device->mmu.fault;
 
 done:
 	if (ret) {
@@ -1479,6 +1400,11 @@
 	} else {
 		adreno_context = context->devctxt;
 		adreno_context->flags |= CTXT_FLAGS_GPU_HANG;
+		/*
+		 * set the invalid ts flag to 0 for this context since we have
+		 * detected a hang for it
+		 */
+		context->wait_on_invalid_ts = false;
 	}
 
 	/* Extract valid contents from rb which can still be executed after
@@ -1513,9 +1439,9 @@
 			goto done;
 	}
 
-	/* Do not try the bad caommands if recovery has failed bad commands
-	 * once already */
-	if (!try_bad_commands)
+	/* Do not try the bad commands if recovery has failed bad commands
+	 * once already or if hang is due to a fault */
+	if (!try_bad_commands || rec_data->fault)
 		rec_data->bad_rb_size = 0;
 
 	if (rec_data->bad_rb_size) {
@@ -1914,7 +1840,7 @@
 		goto err;
 
 	/* now, wait for the GPU to finish its operations */
-	wait_time = jiffies + ADRENO_IDLE_TIMEOUT;
+	wait_time = jiffies + msecs_to_jiffies(ADRENO_IDLE_TIMEOUT);
 	wait_time_part = jiffies + msecs_to_jiffies(KGSL_TIMEOUT_PART);
 
 	while (time_before(jiffies, wait_time)) {
@@ -1943,18 +1869,46 @@
 	KGSL_DRV_ERR(device, "spun too long waiting for RB to idle\n");
 	if (KGSL_STATE_DUMP_AND_RECOVER != device->state &&
 		!adreno_dump_and_recover(device)) {
-		wait_time = jiffies + ADRENO_IDLE_TIMEOUT;
+		wait_time = jiffies + msecs_to_jiffies(ADRENO_IDLE_TIMEOUT);
 		goto retry;
 	}
 	return -ETIMEDOUT;
 }
 
+/**
+ * is_adreno_rbbm_status_idle - Check if GPU core is idle by probing
+ * rbbm_status register
+ * @device - Pointer to the GPU device whose idle status is to be
+ * checked
+ * @returns - Returns whether the core is idle (based on rbbm_status)
+ * false if the core is active, true if the core is idle
+ */
+static bool is_adreno_rbbm_status_idle(struct kgsl_device *device)
+{
+	unsigned int reg_rbbm_status;
+	bool status = false;
+	struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
+
+	/* Is the core idle? */
+	adreno_regread(device,
+		adreno_dev->gpudev->reg_rbbm_status,
+		&reg_rbbm_status);
+
+	if (adreno_is_a2xx(adreno_dev)) {
+		if (reg_rbbm_status == 0x110)
+			status = true;
+	} else {
+		if (!(reg_rbbm_status & 0x80000000))
+			status = true;
+	}
+	return status;
+}
+
 static unsigned int adreno_isidle(struct kgsl_device *device)
 {
 	int status = false;
 	struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
 	struct adreno_ringbuffer *rb = &adreno_dev->ringbuffer;
-	unsigned int rbbm_status;
 
 	WARN_ON(device->state == KGSL_STATE_INIT);
 	/* If the device isn't active, don't force it on. */
@@ -1963,17 +1917,7 @@
 		GSL_RB_GET_READPTR(rb, &rb->rptr);
 		if (!device->active_cnt && (rb->rptr == rb->wptr)) {
 			/* Is the core idle? */
-			adreno_regread(device,
-				adreno_dev->gpudev->reg_rbbm_status,
-				&rbbm_status);
-
-			if (adreno_is_a2xx(adreno_dev)) {
-				if (rbbm_status == 0x110)
-					status = true;
-			} else {
-				if (!(rbbm_status & 0x80000000))
-					status = true;
-			}
+			status = is_adreno_rbbm_status_idle(device);
 		}
 	} else {
 		status = true;
@@ -2216,7 +2160,7 @@
 	if (!adreno_dev->fast_hang_detect)
 		return 0;
 
-	if (device->ftbl->isidle(device))
+	if (is_adreno_rbbm_status_idle(device))
 		return 0;
 
 	for (i = 0; i < hang_detect_regs_count; i++) {
@@ -2247,12 +2191,15 @@
 	static uint io_cnt;
 	struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
 	struct kgsl_pwrctrl *pwr = &device->pwrctrl;
+	struct adreno_context *adreno_ctx = context ? context->devctxt : NULL;
 	int retries = 0;
 	unsigned int ts_issued;
 	unsigned int context_id = _get_context_id(context);
 	unsigned int time_elapsed = 0;
 	unsigned int prev_reg_val[hang_detect_regs_count];
 	unsigned int wait;
+	unsigned int retry_ts_cmp = 0;
+	unsigned int retry_ts_cmp_msecs = KGSL_SYNCOBJ_SERVER_TIMEOUT;
 
 	memset(prev_reg_val, 0, sizeof(prev_reg_val));
 
@@ -2262,12 +2209,31 @@
 	if (msecs == KGSL_TIMEOUT_DEFAULT)
 		msecs = adreno_dev->wait_timeout;
 
+	/*
+	 * With user generated ts, if this check fails perform this check
+	 * again after 'retry_ts_cmp_msecs' milliseconds.
+	 */
 	if (timestamp_cmp(timestamp, ts_issued) > 0) {
-		KGSL_DRV_ERR(device, "Cannot wait for invalid ts <%d:0x%x>, "
-			"last issued ts <%d:0x%x>\n",
-			context_id, timestamp, context_id, ts_issued);
-		status = -EINVAL;
-		goto done;
+		if (adreno_ctx == NULL ||
+			!(adreno_ctx->flags & CTXT_FLAGS_USER_GENERATED_TS)) {
+			if (context && !context->wait_on_invalid_ts) {
+				KGSL_DRV_ERR(device,
+				"Cannot wait for invalid ts <%d:0x%x>, "
+				"last issued ts <%d:0x%x>\n",
+				context_id, timestamp, context_id, ts_issued);
+				/*
+				 * Prevent the above message from spamming the
+				 * kernel logs and causing a watchdog
+				 */
+				context->wait_on_invalid_ts = true;
+			}
+			status = -EINVAL;
+			goto done;
+		} else
+			retry_ts_cmp = 1;
+	} else if (context && context->wait_on_invalid_ts) {
+		/* Once we wait for a valid ts reset the invalid wait flag */
+		context->wait_on_invalid_ts = false;
 	}
 
 	/*
@@ -2337,7 +2303,28 @@
 		time_elapsed += wait;
 		wait = KGSL_TIMEOUT_PART;
 
-		retries++;
+		if (!retry_ts_cmp)
+			retries++;
+		else if (time_elapsed >= retry_ts_cmp_msecs) {
+			ts_issued =
+				adreno_dev->ringbuffer.timestamp[context_id];
+			if (timestamp_cmp(timestamp, ts_issued) > 0) {
+				if (context && !context->wait_on_invalid_ts) {
+					KGSL_DRV_ERR(device,
+					"Cannot wait for user-generated ts <%d:0x%x>, "
+					"not submitted within server timeout period. "
+					"last issued ts <%d:0x%x>\n",
+					context_id, timestamp, context_id,
+					ts_issued);
+					context->wait_on_invalid_ts = true;
+				}
+				status = -EINVAL;
+				goto done;
+			} else if (context && context->wait_on_invalid_ts) {
+				context->wait_on_invalid_ts = false;
+			}
+			retry_ts_cmp = 0;
+		}
 
 	} while (!msecs || time_elapsed < msecs);
 
diff --git a/drivers/gpu/msm/adreno.h b/drivers/gpu/msm/adreno.h
index 66402fe..f9d0316 100644
--- a/drivers/gpu/msm/adreno.h
+++ b/drivers/gpu/msm/adreno.h
@@ -33,11 +33,12 @@
 /* Flags to control command packet settings */
 #define KGSL_CMD_FLAGS_NONE             0x00000000
 #define KGSL_CMD_FLAGS_PMODE		0x00000001
-#define KGSL_CMD_FLAGS_DUMMY_INTR_CMD	0x00000002
+#define KGSL_CMD_FLAGS_INTERNAL_ISSUE	0x00000002
 
 /* Command identifiers */
 #define KGSL_CONTEXT_TO_MEM_IDENTIFIER	0x2EADBEEF
 #define KGSL_CMD_IDENTIFIER		0x2EEDFACE
+#define KGSL_CMD_INTERNAL_IDENTIFIER	0x2EEDD00D
 #define KGSL_START_OF_IB_IDENTIFIER	0x2EADEABE
 #define KGSL_END_OF_IB_IDENTIFIER	0x2ABEDEAD
 
@@ -138,6 +139,7 @@
  * bad_rb_size - Number of valid dwords in bad_rb_buffer
  * @last_valid_ctx_id - The last context from which commands were placed in
  * ringbuffer before the GPU hung
+ * @fault - Indicates whether the hang was caused due to a pagefault
  */
 struct adreno_recovery_data {
 	unsigned int ib1;
@@ -148,6 +150,7 @@
 	unsigned int *bad_rb_buffer;
 	unsigned int bad_rb_size;
 	unsigned int last_valid_ctx_id;
+	int fault;
 };
 
 extern struct adreno_gpudev adreno_a2xx_gpudev;
@@ -179,6 +182,8 @@
 				unsigned int value);
 
 int adreno_dump(struct kgsl_device *device, int manual);
+unsigned int adreno_a3xx_rbbm_clock_ctl_default(struct adreno_device
+							*adreno_dev);
 
 struct kgsl_memdesc *adreno_find_region(struct kgsl_device *device,
 						unsigned int pt_base,
diff --git a/drivers/gpu/msm/adreno_a2xx_snapshot.c b/drivers/gpu/msm/adreno_a2xx_snapshot.c
index 282440c..ce74c1b 100644
--- a/drivers/gpu/msm/adreno_a2xx_snapshot.c
+++ b/drivers/gpu/msm/adreno_a2xx_snapshot.c
@@ -224,6 +224,31 @@
 	return DEBUG_SECTION_SZ(MIUDEBUG_COUNT);
 }
 
+/* Snapshot the istore memory */
+static int a2xx_snapshot_istore(struct kgsl_device *device, void *snapshot,
+	int remain, void *priv)
+{
+	struct kgsl_snapshot_istore *header = snapshot;
+	unsigned int *data = snapshot + sizeof(*header);
+	struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
+	int count, i;
+
+	count = adreno_dev->istore_size * adreno_dev->instruction_size;
+
+	if (remain < (count * 4) + sizeof(*header)) {
+		KGSL_DRV_ERR(device,
+			"snapshot: Not enough memory for the istore section");
+		return 0;
+	}
+
+	header->count = adreno_dev->istore_size;
+
+	for (i = 0; i < count; i++)
+		kgsl_regread(device, ADRENO_ISTORE_START + i, &data[i]);
+
+	return (count * 4) + sizeof(*header);
+}
+
 /* A2XX GPU snapshot function - this is where all of the A2XX specific
  * bits and pieces are grabbed into the snapshot memory
  */
@@ -338,6 +363,18 @@
 		}
 	}
 
+	/*
+	 * Only dump the istore on a hang - reading it on a running system
+	 * has a non zero chance of hanging the GPU.
+	 */
+
+	if (adreno_is_a2xx(adreno_dev) && hang) {
+		snapshot = kgsl_snapshot_add_section(device,
+			KGSL_SNAPSHOT_SECTION_ISTORE, snapshot, remain,
+			a2xx_snapshot_istore, NULL);
+	}
+
+
 	/* Reset the clock gating */
 	adreno_regwrite(device, REG_RBBM_PM_OVERRIDE2, pmoverride);
 
diff --git a/drivers/gpu/msm/adreno_a3xx.c b/drivers/gpu/msm/adreno_a3xx.c
index 104baf8..4c7534c 100644
--- a/drivers/gpu/msm/adreno_a3xx.c
+++ b/drivers/gpu/msm/adreno_a3xx.c
@@ -433,6 +433,19 @@
 	tmp_ctx.cmd = cmd;
 }
 
+unsigned int adreno_a3xx_rbbm_clock_ctl_default(struct adreno_device
+							*adreno_dev)
+{
+	if (adreno_is_a305(adreno_dev))
+		return A305_RBBM_CLOCK_CTL_DEFAULT;
+	else if (adreno_is_a320(adreno_dev))
+		return A320_RBBM_CLOCK_CTL_DEFAULT;
+	else if (adreno_is_a330(adreno_dev))
+		return A330_RBBM_CLOCK_CTL_DEFAULT;
+
+	BUG_ON(1);
+}
+
 /* Copy GMEM contents to system memory shadow. */
 static unsigned int *build_gmem2sys_cmds(struct adreno_device *adreno_dev,
 					 struct adreno_context *drawctxt,
@@ -442,7 +455,7 @@
 	unsigned int *start = cmds;
 
 	*cmds++ = cp_type0_packet(A3XX_RBBM_CLOCK_CTL, 1);
-	*cmds++ = A3XX_RBBM_CLOCK_CTL_DEFAULT;
+	*cmds++ = adreno_a3xx_rbbm_clock_ctl_default(adreno_dev);
 
 	*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 3);
 	*cmds++ = CP_REG(A3XX_RB_MODE_CONTROL);
@@ -1238,7 +1251,7 @@
 	unsigned int *start = cmds;
 
 	*cmds++ = cp_type0_packet(A3XX_RBBM_CLOCK_CTL, 1);
-	*cmds++ = A3XX_RBBM_CLOCK_CTL_DEFAULT;
+	*cmds++ = adreno_a3xx_rbbm_clock_ctl_default(adreno_dev);
 
 	*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 5);
 	*cmds++ = CP_REG(A3XX_HLSQ_CONTROL_0_REG);
@@ -2826,7 +2839,11 @@
 
 	/* Enable Clock gating */
 	adreno_regwrite(device, A3XX_RBBM_CLOCK_CTL,
-			A3XX_RBBM_CLOCK_CTL_DEFAULT);
+		adreno_a3xx_rbbm_clock_ctl_default(adreno_dev));
+
+	if (adreno_is_a330(adreno_dev))
+		adreno_regwrite(device, A3XX_RBBM_GPR0_CTL,
+		A330_RBBM_GPR0_CTL_DEFAULT);
 
 	/* Set the OCMEM base address for A330 */
 	if (adreno_is_a330(adreno_dev)) {
diff --git a/drivers/gpu/msm/adreno_a3xx_snapshot.c b/drivers/gpu/msm/adreno_a3xx_snapshot.c
index d49fc23..e4f5733 100644
--- a/drivers/gpu/msm/adreno_a3xx_snapshot.c
+++ b/drivers/gpu/msm/adreno_a3xx_snapshot.c
@@ -220,30 +220,46 @@
 	return DEBUG_SECTION_SZ(size);
 }
 
-#define DEBUGFS_BLOCK_SIZE 0x40
+struct debugbus_block {
+	unsigned int block_id;
+	unsigned int dwords;
+};
 
 static int a3xx_snapshot_debugbus_block(struct kgsl_device *device,
 	void *snapshot, int remain, void *priv)
 {
+	struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
+
 	struct kgsl_snapshot_debugbus *header = snapshot;
-	unsigned int id = (unsigned int) priv;
+	struct debugbus_block *block = priv;
 	unsigned int val;
 	int i;
 	unsigned int *data = snapshot + sizeof(*header);
-	int size =
-		(DEBUGFS_BLOCK_SIZE * sizeof(unsigned int)) + sizeof(*header);
+	unsigned int dwords;
+	int size;
+
+	/*
+	 * For A305 and A320 all debug bus regions are the same size (0x40). For
+	 * A330, they can be different sizes - most are still 0x40, but some
+	 * like CP are larger
+	 */
+
+	dwords = adreno_is_a330(adreno_dev) ?
+		block->dwords : 0x40;
+
+	size = (dwords * sizeof(unsigned int)) + sizeof(*header);
 
 	if (remain < size) {
 		SNAPSHOT_ERR_NOMEM(device, "DEBUGBUS");
 		return 0;
 	}
 
-	val = (id << 8) | (1 << 16);
+	val = (block->block_id << 8) | (1 << 16);
 
-	header->id = id;
-	header->count = DEBUGFS_BLOCK_SIZE;
+	header->id = block->block_id;
+	header->count = dwords;
 
-	for (i = 0; i < DEBUGFS_BLOCK_SIZE; i++) {
+	for (i = 0; i < dwords; i++) {
 		adreno_regwrite(device, A3XX_RBBM_DEBUG_BUS_CTL, val | i);
 		adreno_regread(device, A3XX_RBBM_DEBUG_BUS_DATA_STATUS,
 			&data[i]);
@@ -252,34 +268,34 @@
 	return size;
 }
 
-static unsigned int debugbus_blocks[] = {
-	RBBM_BLOCK_ID_CP,
-	RBBM_BLOCK_ID_RBBM,
-	RBBM_BLOCK_ID_VBIF,
-	RBBM_BLOCK_ID_HLSQ,
-	RBBM_BLOCK_ID_UCHE,
-	RBBM_BLOCK_ID_PC,
-	RBBM_BLOCK_ID_VFD,
-	RBBM_BLOCK_ID_VPC,
-	RBBM_BLOCK_ID_TSE,
-	RBBM_BLOCK_ID_RAS,
-	RBBM_BLOCK_ID_VSC,
-	RBBM_BLOCK_ID_SP_0,
-	RBBM_BLOCK_ID_SP_1,
-	RBBM_BLOCK_ID_SP_2,
-	RBBM_BLOCK_ID_SP_3,
-	RBBM_BLOCK_ID_TPL1_0,
-	RBBM_BLOCK_ID_TPL1_1,
-	RBBM_BLOCK_ID_TPL1_2,
-	RBBM_BLOCK_ID_TPL1_3,
-	RBBM_BLOCK_ID_RB_0,
-	RBBM_BLOCK_ID_RB_1,
-	RBBM_BLOCK_ID_RB_2,
-	RBBM_BLOCK_ID_RB_3,
-	RBBM_BLOCK_ID_MARB_0,
-	RBBM_BLOCK_ID_MARB_1,
-	RBBM_BLOCK_ID_MARB_2,
-	RBBM_BLOCK_ID_MARB_3,
+static struct debugbus_block debugbus_blocks[] = {
+	{ RBBM_BLOCK_ID_CP, 0x52, },
+	{ RBBM_BLOCK_ID_RBBM, 0x40, },
+	{ RBBM_BLOCK_ID_VBIF, 0x40, },
+	{ RBBM_BLOCK_ID_HLSQ, 0x40, },
+	{ RBBM_BLOCK_ID_UCHE, 0x40, },
+	{ RBBM_BLOCK_ID_PC, 0x40, },
+	{ RBBM_BLOCK_ID_VFD, 0x40, },
+	{ RBBM_BLOCK_ID_VPC, 0x40, },
+	{ RBBM_BLOCK_ID_TSE, 0x40, },
+	{ RBBM_BLOCK_ID_RAS, 0x40, },
+	{ RBBM_BLOCK_ID_VSC, 0x40, },
+	{ RBBM_BLOCK_ID_SP_0, 0x40, },
+	{ RBBM_BLOCK_ID_SP_1, 0x40, },
+	{ RBBM_BLOCK_ID_SP_2, 0x40, },
+	{ RBBM_BLOCK_ID_SP_3, 0x40, },
+	{ RBBM_BLOCK_ID_TPL1_0, 0x40, },
+	{ RBBM_BLOCK_ID_TPL1_1, 0x40, },
+	{ RBBM_BLOCK_ID_TPL1_2, 0x40, },
+	{ RBBM_BLOCK_ID_TPL1_3, 0x40, },
+	{ RBBM_BLOCK_ID_RB_0, 0x40, },
+	{ RBBM_BLOCK_ID_RB_1, 0x40, },
+	{ RBBM_BLOCK_ID_RB_2, 0x40, },
+	{ RBBM_BLOCK_ID_RB_3, 0x40, },
+	{ RBBM_BLOCK_ID_MARB_0, 0x40, },
+	{ RBBM_BLOCK_ID_MARB_1, 0x40, },
+	{ RBBM_BLOCK_ID_MARB_2, 0x40, },
+	{ RBBM_BLOCK_ID_MARB_3, 0x40, },
 };
 
 static void *a3xx_snapshot_debugbus(struct kgsl_device *device,
@@ -291,7 +307,7 @@
 		snapshot = kgsl_snapshot_add_section(device,
 			KGSL_SNAPSHOT_SECTION_DEBUGBUS, snapshot, remain,
 			a3xx_snapshot_debugbus_block,
-			(void *) debugbus_blocks[i]);
+			(void *) &debugbus_blocks[i]);
 	}
 
 	return snapshot;
@@ -307,6 +323,7 @@
 	struct kgsl_device *device = &adreno_dev->dev;
 	struct kgsl_snapshot_registers_list list;
 	struct kgsl_snapshot_registers regs[2];
+	int size;
 
 	regs[0].regs = (unsigned int *) a3xx_registers;
 	regs[0].count = a3xx_registers_count;
@@ -326,10 +343,14 @@
 		KGSL_SNAPSHOT_SECTION_REGS, snapshot, remain,
 		kgsl_snapshot_dump_regs, &list);
 
-	/* CP_STATE_DEBUG indexed registers */
+	/*
+	 * CP_STATE_DEBUG indexed registers - 20 on 305 and 320 and 46 on A330
+	 */
+	size = adreno_is_a330(adreno_dev) ? 0x2E : 0x14;
+
 	snapshot = kgsl_snapshot_indexed_registers(device, snapshot,
 			remain, REG_CP_STATE_DEBUG_INDEX,
-			REG_CP_STATE_DEBUG_DATA, 0x0, 0x14);
+			REG_CP_STATE_DEBUG_DATA, 0x0, size);
 
 	/* CP_ME indexed registers */
 	snapshot = kgsl_snapshot_indexed_registers(device, snapshot,
@@ -383,7 +404,7 @@
 
 	/* Enable Clock gating */
 	adreno_regwrite(device, A3XX_RBBM_CLOCK_CTL,
-			A3XX_RBBM_CLOCK_CTL_DEFAULT);
+		adreno_a3xx_rbbm_clock_ctl_default(adreno_dev));
 
 	return snapshot;
 }
diff --git a/drivers/gpu/msm/adreno_drawctxt.c b/drivers/gpu/msm/adreno_drawctxt.c
index 7cbc7a8..a107a27 100644
--- a/drivers/gpu/msm/adreno_drawctxt.c
+++ b/drivers/gpu/msm/adreno_drawctxt.c
@@ -169,6 +169,14 @@
 	if (flags & KGSL_CONTEXT_PER_CONTEXT_TS)
 		drawctxt->flags |= CTXT_FLAGS_PER_CONTEXT_TS;
 
+	if (flags & KGSL_CONTEXT_USER_GENERATED_TS) {
+		if (!(flags & KGSL_CONTEXT_PER_CONTEXT_TS)) {
+			ret = -EINVAL;
+			goto err;
+		}
+		drawctxt->flags |= CTXT_FLAGS_USER_GENERATED_TS;
+	}
+
 	ret = adreno_dev->gpudev->ctxt_create(adreno_dev, drawctxt);
 	if (ret)
 		goto err;
diff --git a/drivers/gpu/msm/adreno_drawctxt.h b/drivers/gpu/msm/adreno_drawctxt.h
index 034d6e9..58e4791 100644
--- a/drivers/gpu/msm/adreno_drawctxt.h
+++ b/drivers/gpu/msm/adreno_drawctxt.h
@@ -48,6 +48,8 @@
 #define CTXT_FLAGS_GPU_HANG_RECOVERED	BIT(12)
 /* Context is being destroyed so dont save it */
 #define CTXT_FLAGS_BEING_DESTROYED	BIT(13)
+/* User mode generated timestamps enabled */
+#define CTXT_FLAGS_USER_GENERATED_TS    BIT(14)
 
 struct kgsl_device;
 struct adreno_device;
diff --git a/drivers/gpu/msm/adreno_postmortem.c b/drivers/gpu/msm/adreno_postmortem.c
index daa78ed..e069fa5 100644
--- a/drivers/gpu/msm/adreno_postmortem.c
+++ b/drivers/gpu/msm/adreno_postmortem.c
@@ -70,6 +70,14 @@
 	{CP_WAIT_FOR_IDLE,		"WAIT4IDL"},
 };
 
+static const struct pm_id_name pm3_nop_values[] = {
+	{KGSL_CONTEXT_TO_MEM_IDENTIFIER,	"CTX_SWCH"},
+	{KGSL_CMD_IDENTIFIER,			"CMD__EXT"},
+	{KGSL_CMD_INTERNAL_IDENTIFIER,		"CMD__INT"},
+	{KGSL_START_OF_IB_IDENTIFIER,		"IB_START"},
+	{KGSL_END_OF_IB_IDENTIFIER,		"IB___END"},
+};
+
 static uint32_t adreno_is_pm4_len(uint32_t word)
 {
 	if (word == INVALID_RB_CMD)
@@ -129,6 +137,28 @@
 	return "????????";
 }
 
+static bool adreno_is_pm3_nop_value(uint32_t word)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(pm3_nop_values); ++i) {
+		if (word == pm3_nop_values[i].id)
+			return 1;
+	}
+	return 0;
+}
+
+static const char *adreno_pm3_nop_name(uint32_t word)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(pm3_nop_values); ++i) {
+		if (word == pm3_nop_values[i].id)
+			return pm3_nop_values[i].name;
+	}
+	return "????????";
+}
+
 static void adreno_dump_regs(struct kgsl_device *device,
 			   const int *registers, int size)
 {
@@ -245,8 +275,13 @@
 				"%s", adreno_pm4_name(ptr4[j]));
 			*argp = -(adreno_is_pm4_len(ptr4[j])+1);
 		} else {
-			lx += scnprintf(linebuf + lx, linebuflen - lx,
-				"%8.8X", ptr4[j]);
+			if (adreno_is_pm3_nop_value(ptr4[j]))
+				lx += scnprintf(linebuf + lx, linebuflen - lx,
+					"%s", adreno_pm3_nop_name(ptr4[j]));
+			else
+				lx += scnprintf(linebuf + lx, linebuflen - lx,
+					"%8.8X", ptr4[j]);
+
 			if (*argp > 1)
 				--*argp;
 			else if (*argp == 1) {
diff --git a/drivers/gpu/msm/adreno_ringbuffer.c b/drivers/gpu/msm/adreno_ringbuffer.c
index 0dd140b..e5a790f 100644
--- a/drivers/gpu/msm/adreno_ringbuffer.c
+++ b/drivers/gpu/msm/adreno_ringbuffer.c
@@ -487,11 +487,10 @@
 adreno_ringbuffer_addcmds(struct adreno_ringbuffer *rb,
 				struct adreno_context *context,
 				unsigned int flags, unsigned int *cmds,
-				int sizedwords)
+				int sizedwords, uint32_t timestamp)
 {
 	struct adreno_device *adreno_dev = ADRENO_DEVICE(rb->device);
 	unsigned int *ringcmds;
-	unsigned int timestamp;
 	unsigned int total_sizedwords = sizedwords;
 	unsigned int i;
 	unsigned int rcmd_gpu;
@@ -506,19 +505,41 @@
 	if (context && context->flags & CTXT_FLAGS_PER_CONTEXT_TS)
 		context_id = context->id;
 
+	if ((context->flags & CTXT_FLAGS_USER_GENERATED_TS) &&
+			(!(flags & KGSL_CMD_FLAGS_INTERNAL_ISSUE))) {
+		if (timestamp_cmp(rb->timestamp[context_id],
+						timestamp) >= 0) {
+			KGSL_DRV_ERR(rb->device,
+				"Invalid user generated ts <%d:0x%x>, "
+				"less than last issued ts <%d:0x%x>\n",
+				context_id, timestamp, context_id,
+				rb->timestamp[context_id]);
+			return -ERANGE;
+		}
+	}
+
 	/* reserve space to temporarily turn off protected mode
 	*  error checking if needed
 	*/
 	total_sizedwords += flags & KGSL_CMD_FLAGS_PMODE ? 4 : 0;
 	/* 2 dwords to store the start of command sequence */
 	total_sizedwords += 2;
-	total_sizedwords += context ? 7 : 0;
+	/* internal ib command identifier for the ringbuffer */
+	total_sizedwords += (flags & KGSL_CMD_FLAGS_INTERNAL_ISSUE) ? 2 : 0;
+
+	/*
+	 * Add CP_COND_EXEC commands to generate CP_INTERRUPT only
+	 * for submissions from userspace.
+	 */
+	total_sizedwords += (context &&
+			!(flags & KGSL_CMD_FLAGS_INTERNAL_ISSUE)) ? 7 : 0;
 
 	if (adreno_is_a3xx(adreno_dev))
 		total_sizedwords += 7;
 
 	total_sizedwords += 2; /* scratchpad ts for recovery */
-	if (context && context->flags & CTXT_FLAGS_PER_CONTEXT_TS) {
+	if (context && context->flags & CTXT_FLAGS_PER_CONTEXT_TS &&
+			!(flags & KGSL_CMD_FLAGS_INTERNAL_ISSUE)) {
 		total_sizedwords += 3; /* sop timestamp */
 		total_sizedwords += 4; /* eop timestamp */
 		total_sizedwords += 3; /* global timestamp without cache
@@ -542,6 +563,11 @@
 	GSL_RB_WRITE(ringcmds, rcmd_gpu, cp_nop_packet(1));
 	GSL_RB_WRITE(ringcmds, rcmd_gpu, KGSL_CMD_IDENTIFIER);
 
+	if (flags & KGSL_CMD_FLAGS_INTERNAL_ISSUE) {
+		GSL_RB_WRITE(ringcmds, rcmd_gpu, cp_nop_packet(1));
+		GSL_RB_WRITE(ringcmds, rcmd_gpu, KGSL_CMD_INTERNAL_IDENTIFIER);
+	}
+
 	if (flags & KGSL_CMD_FLAGS_PMODE) {
 		/* disable protected mode error checking */
 		GSL_RB_WRITE(ringcmds, rcmd_gpu,
@@ -564,10 +590,13 @@
 	/* always increment the global timestamp. once. */
 	rb->timestamp[KGSL_MEMSTORE_GLOBAL]++;
 
-	if (context && !(flags & KGSL_CMD_FLAGS_DUMMY_INTR_CMD)) {
+	/* Do not update context's timestamp for internal submissions */
+	if (context && !(flags & KGSL_CMD_FLAGS_INTERNAL_ISSUE)) {
 		if (context_id == KGSL_MEMSTORE_GLOBAL)
 			rb->timestamp[context->id] =
 				rb->timestamp[KGSL_MEMSTORE_GLOBAL];
+		else if (context->flags & CTXT_FLAGS_USER_GENERATED_TS)
+			rb->timestamp[context_id] = timestamp;
 		else
 			rb->timestamp[context_id]++;
 	}
@@ -591,7 +620,8 @@
 		GSL_RB_WRITE(ringcmds, rcmd_gpu, 0x00);
 	}
 
-	if (context && context->flags & CTXT_FLAGS_PER_CONTEXT_TS) {
+	if (context && context->flags & CTXT_FLAGS_PER_CONTEXT_TS
+			&& !(flags & KGSL_CMD_FLAGS_INTERNAL_ISSUE)) {
 		/* start-of-pipeline timestamp */
 		GSL_RB_WRITE(ringcmds, rcmd_gpu,
 			cp_type3_packet(CP_MEM_WRITE, 2));
@@ -619,11 +649,13 @@
 			cp_type3_packet(CP_EVENT_WRITE, 3));
 		GSL_RB_WRITE(ringcmds, rcmd_gpu, CACHE_FLUSH_TS);
 		GSL_RB_WRITE(ringcmds, rcmd_gpu, (gpuaddr +
-			KGSL_MEMSTORE_OFFSET(context_id, eoptimestamp)));
-		GSL_RB_WRITE(ringcmds, rcmd_gpu, rb->timestamp[context_id]);
+			KGSL_MEMSTORE_OFFSET(KGSL_MEMSTORE_GLOBAL,
+						eoptimestamp)));
+		GSL_RB_WRITE(ringcmds, rcmd_gpu,
+				rb->timestamp[KGSL_MEMSTORE_GLOBAL]);
 	}
 
-	if (context) {
+	if (context && !(flags & KGSL_CMD_FLAGS_INTERNAL_ISSUE)) {
 		/* Conditional execution based on memory values */
 		GSL_RB_WRITE(ringcmds, rcmd_gpu,
 			cp_type3_packet(CP_COND_EXEC, 4));
@@ -675,8 +707,8 @@
 		device->state & KGSL_STATE_HUNG)
 		return;
 
-	adreno_ringbuffer_addcmds(rb, a_ctxt, KGSL_CMD_FLAGS_DUMMY_INTR_CMD,
-			cmds, sizedwords);
+	adreno_ringbuffer_addcmds(rb, a_ctxt, KGSL_CMD_FLAGS_INTERNAL_ISSUE,
+					cmds, sizedwords, 0);
 }
 
 unsigned int
@@ -692,7 +724,11 @@
 	if (device->state & KGSL_STATE_HUNG)
 		return kgsl_readtimestamp(device, KGSL_MEMSTORE_GLOBAL,
 					KGSL_TIMESTAMP_RETIRED);
-	return adreno_ringbuffer_addcmds(rb, drawctxt, flags, cmds, sizedwords);
+
+	flags |= KGSL_CMD_FLAGS_INTERNAL_ISSUE;
+
+	return adreno_ringbuffer_addcmds(rb, drawctxt, flags, cmds,
+							sizedwords, 0);
 }
 
 static bool _parse_ibs(struct kgsl_device_private *dev_priv, uint gpuaddr,
@@ -973,8 +1009,9 @@
 	adreno_drawctxt_switch(adreno_dev, drawctxt, flags);
 
 	*timestamp = adreno_ringbuffer_addcmds(&adreno_dev->ringbuffer,
-					drawctxt, 0,
-					&link[0], (cmds - link));
+					drawctxt,
+					0,
+					&link[0], (cmds - link), *timestamp);
 
 	KGSL_CMD_INFO(device, "<%d:0x%x> g %08x numibs %d\n",
 		context->id, *timestamp, (unsigned int)ibdesc, numibs);
diff --git a/drivers/gpu/msm/adreno_snapshot.c b/drivers/gpu/msm/adreno_snapshot.c
index 93be980..696073f 100644
--- a/drivers/gpu/msm/adreno_snapshot.c
+++ b/drivers/gpu/msm/adreno_snapshot.c
@@ -510,10 +510,28 @@
 			break;
 
 		if (pkt_is_type3(src[i])) {
-			if (adreno_cmd_is_ib(src[i]))
-				ib_add_gpu_object(device, ptbase,
-					src[i + 1], src[i + 2]);
-			else
+			if (adreno_cmd_is_ib(src[i])) {
+				unsigned int gpuaddr = src[i + 1];
+				unsigned int size = src[i + 2];
+				unsigned int ibbase;
+
+				/* Address of the last processed IB2 */
+				kgsl_regread(device, REG_CP_IB2_BASE, &ibbase);
+
+				/*
+				 * If this is the last IB2 that was executed,
+				 * then push it to make sure it goes into the
+				 * static space
+				 */
+
+				if (ibbase == gpuaddr)
+					push_object(device,
+						SNAPSHOT_OBJ_TYPE_IB, ptbase,
+						gpuaddr, size);
+				else
+					ib_add_gpu_object(device, ptbase,
+						gpuaddr, size);
+			} else
 				ib_parse_type3(device, &src[i], ptbase);
 		} else if (pkt_is_type0(src[i])) {
 			ib_parse_type0(device, &src[i], ptbase);
@@ -529,31 +547,6 @@
 	snapshot_frozen_objsize += ret;
 }
 
-/* Snapshot the istore memory */
-static int snapshot_istore(struct kgsl_device *device, void *snapshot,
-	int remain, void *priv)
-{
-	struct kgsl_snapshot_istore *header = snapshot;
-	unsigned int *data = snapshot + sizeof(*header);
-	struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
-	int count, i;
-
-	count = adreno_dev->istore_size * adreno_dev->instruction_size;
-
-	if (remain < (count * 4) + sizeof(*header)) {
-		KGSL_DRV_ERR(device,
-			"snapshot: Not enough memory for the istore section");
-		return 0;
-	}
-
-	header->count = adreno_dev->istore_size;
-
-	for (i = 0; i < count; i++)
-		kgsl_regread(device, ADRENO_ISTORE_START + i, &data[i]);
-
-	return (count * 4) + sizeof(*header);
-}
-
 /* Snapshot the ringbuffer memory */
 static int snapshot_rb(struct kgsl_device *device, void *snapshot,
 	int remain, void *priv)
@@ -870,17 +863,6 @@
 	for (i = 0; i < objbufptr; i++)
 		snapshot = dump_object(device, i, snapshot, remain);
 
-	/*
-	 * Only dump the istore on a hang - reading it on a running system
-	 * has a non 0 chance of hanging the GPU
-	 */
-
-	if (hang) {
-		snapshot = kgsl_snapshot_add_section(device,
-			KGSL_SNAPSHOT_SECTION_ISTORE, snapshot, remain,
-			snapshot_istore, NULL);
-	}
-
 	/* Add GPU specific sections - registers mainly, but other stuff too */
 	if (adreno_dev->gpudev->snapshot)
 		snapshot = adreno_dev->gpudev->snapshot(adreno_dev, snapshot,
diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c
index 5ba844a..b8adbe67 100644
--- a/drivers/gpu/msm/kgsl.c
+++ b/drivers/gpu/msm/kgsl.c
@@ -333,14 +333,19 @@
 
 	context = kzalloc(sizeof(*context), GFP_KERNEL);
 
-	if (context == NULL)
-		return NULL;
+	if (context == NULL) {
+		KGSL_DRV_INFO(dev_priv->device, "kzalloc(%d) failed\n",
+				sizeof(*context));
+		return ERR_PTR(-ENOMEM);
+	}
 
 	while (1) {
 		if (idr_pre_get(&dev_priv->device->context_idr,
 				GFP_KERNEL) == 0) {
-			kfree(context);
-			return NULL;
+			KGSL_DRV_INFO(dev_priv->device,
+					"idr_pre_get: ENOMEM\n");
+			ret = -ENOMEM;
+			goto func_end;
 		}
 
 		ret = idr_get_new_above(&dev_priv->device->context_idr,
@@ -350,10 +355,8 @@
 			break;
 	}
 
-	if (ret) {
-		kfree(context);
-		return NULL;
-	}
+	if (ret)
+		goto func_end;
 
 	/* MAX - 1, there is one memdesc in memstore for device info */
 	if (id >= KGSL_MEMSTORE_MAX) {
@@ -361,18 +364,24 @@
 				"ctxts due to memstore limitation\n",
 				KGSL_MEMSTORE_MAX);
 		idr_remove(&dev_priv->device->context_idr, id);
-		kfree(context);
-		return NULL;
+		ret = -ENOSPC;
+		goto func_end;
 	}
 
 	kref_init(&context->refcount);
 	context->id = id;
 	context->dev_priv = dev_priv;
 
-	if (kgsl_sync_timeline_create(context)) {
+	ret = kgsl_sync_timeline_create(context);
+	if (ret) {
 		idr_remove(&dev_priv->device->context_idr, id);
+		goto func_end;
+	}
+
+func_end:
+	if (ret) {
 		kfree(context);
-		return NULL;
+		return ERR_PTR(ret);
 	}
 
 	return context;
@@ -722,6 +731,7 @@
 	list_add(&private->list, &kgsl_driver.process_list);
 
 	kgsl_process_init_sysfs(private);
+	kgsl_process_init_debugfs(private);
 
 out:
 	mutex_unlock(&kgsl_driver.process_mutex);
@@ -744,6 +754,7 @@
 		goto unlock;
 
 	kgsl_process_uninit_sysfs(private);
+	debugfs_remove_recursive(private->debug_root);
 
 	list_del(&private->list);
 
@@ -1080,6 +1091,7 @@
 				      unsigned int cmd, void *data)
 {
 	int result = 0;
+	int i = 0;
 	struct kgsl_ringbuffer_issueibcmds *param = data;
 	struct kgsl_ibdesc *ibdesc;
 	struct kgsl_context *context;
@@ -1141,6 +1153,16 @@
 		param->numibs = 1;
 	}
 
+	for (i = 0; i < param->numibs; i++) {
+		if (!kgsl_mmu_gpuaddr_in_range(ibdesc[i].gpuaddr)) {
+			result = -ERANGE;
+			KGSL_DRV_ERR(dev_priv->device,
+				     "invalid ib base GPU virtual addr %x\n",
+				     ibdesc[i].gpuaddr);
+			goto free_ibdesc;
+		}
+	}
+
 	result = dev_priv->device->ftbl->issueibcmds(dev_priv,
 					     context,
 					     ibdesc,
@@ -1270,8 +1292,8 @@
 
 	context = kgsl_create_context(dev_priv);
 
-	if (context == NULL) {
-		result = -ENOMEM;
+	if (IS_ERR(context)) {
+		result = PTR_ERR(context);
 		goto done;
 	}
 
@@ -1285,7 +1307,7 @@
 	trace_kgsl_context_create(dev_priv->device, context, param->flags);
 	param->drawctxt_id = context->id;
 done:
-	if (result && context)
+	if (result && !IS_ERR(context))
 		kgsl_context_detach(context);
 
 	return result;
@@ -1456,11 +1478,10 @@
 }
 
 static int memdesc_sg_virt(struct kgsl_memdesc *memdesc,
-	void *addr, int size)
+	unsigned long paddr, int size)
 {
 	int i;
 	int sglen = PAGE_ALIGN(size) / PAGE_SIZE;
-	unsigned long paddr = (unsigned long) addr;
 
 	memdesc->sg = kgsl_sg_alloc(sglen);
 
@@ -1468,6 +1489,8 @@
 		return -ENOMEM;
 
 	memdesc->sglen = sglen;
+	memdesc->sglen_alloc = sglen;
+
 	sg_init_table(memdesc->sg, sglen);
 
 	spin_lock(&current->mm->page_table_lock);
@@ -1509,34 +1532,33 @@
 	return -EINVAL;
 }
 
-static int kgsl_setup_hostptr(struct kgsl_mem_entry *entry,
+static int kgsl_setup_useraddr(struct kgsl_mem_entry *entry,
 			      struct kgsl_pagetable *pagetable,
-			      void *hostptr, unsigned int offset,
+			      unsigned long useraddr, unsigned int offset,
 			      size_t size)
 {
 	struct vm_area_struct *vma;
 	unsigned int len;
 
 	down_read(&current->mm->mmap_sem);
-	vma = find_vma(current->mm, (unsigned int) hostptr);
+	vma = find_vma(current->mm, useraddr);
 	up_read(&current->mm->mmap_sem);
 
 	if (!vma) {
-		KGSL_CORE_ERR("find_vma(%p) failed\n", hostptr);
+		KGSL_CORE_ERR("find_vma(%lx) failed\n", useraddr);
 		return -EINVAL;
 	}
 
 	/* We don't necessarily start at vma->vm_start */
-	len = vma->vm_end - (unsigned long) hostptr;
+	len = vma->vm_end - useraddr;
 
 	if (offset >= len)
 		return -EINVAL;
 
-	if (!KGSL_IS_PAGE_ALIGNED((unsigned long) hostptr) ||
+	if (!KGSL_IS_PAGE_ALIGNED(useraddr) ||
 	    !KGSL_IS_PAGE_ALIGNED(len)) {
-		KGSL_CORE_ERR("user address len(%u)"
-			      "and start(%p) must be page"
-			      "aligned\n", len, hostptr);
+		KGSL_CORE_ERR("bad alignment: start(%lx) len(%u)\n",
+			      useraddr, len);
 		return -EINVAL;
 	}
 
@@ -1557,28 +1579,27 @@
 
 	entry->memdesc.pagetable = pagetable;
 	entry->memdesc.size = size;
-	entry->memdesc.hostptr = hostptr + (offset & PAGE_MASK);
+	entry->memdesc.useraddr = useraddr + (offset & PAGE_MASK);
 
-	return memdesc_sg_virt(&entry->memdesc,
-		hostptr + (offset & PAGE_MASK), size);
+	return memdesc_sg_virt(&entry->memdesc, entry->memdesc.useraddr,
+				size);
 }
 
 #ifdef CONFIG_ASHMEM
 static int kgsl_setup_ashmem(struct kgsl_mem_entry *entry,
 			     struct kgsl_pagetable *pagetable,
-			     int fd, void *hostptr, size_t size)
+			     int fd, unsigned long useraddr, size_t size)
 {
 	int ret;
 	struct vm_area_struct *vma;
 	struct file *filep, *vmfile;
 	unsigned long len;
-	unsigned int hostaddr = (unsigned int) hostptr;
 
-	vma = kgsl_get_vma_from_start_addr(hostaddr);
+	vma = kgsl_get_vma_from_start_addr(useraddr);
 	if (vma == NULL)
 		return -EINVAL;
 
-	if (vma->vm_pgoff || vma->vm_start != hostaddr) {
+	if (vma->vm_pgoff || vma->vm_start != useraddr) {
 		KGSL_CORE_ERR("Invalid vma region\n");
 		return -EINVAL;
 	}
@@ -1589,8 +1610,8 @@
 		size = len;
 
 	if (size != len) {
-		KGSL_CORE_ERR("Invalid size %d for vma region %p\n",
-			      size, hostptr);
+		KGSL_CORE_ERR("Invalid size %d for vma region %lx\n",
+			      size, useraddr);
 		return -EINVAL;
 	}
 
@@ -1610,9 +1631,9 @@
 	entry->priv_data = filep;
 	entry->memdesc.pagetable = pagetable;
 	entry->memdesc.size = ALIGN(size, PAGE_SIZE);
-	entry->memdesc.hostptr = hostptr;
+	entry->memdesc.useraddr = useraddr;
 
-	ret = memdesc_sg_virt(&entry->memdesc, hostptr, size);
+	ret = memdesc_sg_virt(&entry->memdesc, useraddr, size);
 	if (ret)
 		goto err;
 
@@ -1625,18 +1646,23 @@
 #else
 static int kgsl_setup_ashmem(struct kgsl_mem_entry *entry,
 			     struct kgsl_pagetable *pagetable,
-			     int fd, void *hostptr, size_t size)
+			     int fd, unsigned long useraddr, size_t size)
 {
 	return -EINVAL;
 }
 #endif
 
 static int kgsl_setup_ion(struct kgsl_mem_entry *entry,
-		struct kgsl_pagetable *pagetable, int fd)
+		struct kgsl_pagetable *pagetable, void *data)
 {
 	struct ion_handle *handle;
 	struct scatterlist *s;
 	struct sg_table *sg_table;
+	struct kgsl_map_user_mem *param = data;
+	int fd = param->fd;
+
+	if (!param->len)
+		return -EINVAL;
 
 	if (IS_ERR_OR_NULL(kgsl_ion_client))
 		return -ENODEV;
@@ -1693,6 +1719,8 @@
 	else
 		memtype = param->memtype;
 
+	entry->memdesc.flags = param->flags;
+
 	switch (memtype) {
 	case KGSL_USER_MEM_TYPE_PMEM:
 		if (param->fd == 0 || param->len == 0)
@@ -1717,8 +1745,8 @@
 		if (param->hostptr == 0)
 			break;
 
-		result = kgsl_setup_hostptr(entry, private->pagetable,
-					    (void *) param->hostptr,
+		result = kgsl_setup_useraddr(entry, private->pagetable,
+					    param->hostptr,
 					    param->offset, param->len);
 		entry->memtype = KGSL_MEM_ENTRY_USER;
 		break;
@@ -1735,14 +1763,13 @@
 			break;
 
 		result = kgsl_setup_ashmem(entry, private->pagetable,
-					   param->fd, (void *) param->hostptr,
+					   param->fd, param->hostptr,
 					   param->len);
 
 		entry->memtype = KGSL_MEM_ENTRY_ASHMEM;
 		break;
 	case KGSL_USER_MEM_TYPE_ION:
-		result = kgsl_setup_ion(entry, private->pagetable,
-			param->fd);
+		result = kgsl_setup_ion(entry, private->pagetable, data);
 		break;
 	default:
 		KGSL_CORE_ERR("Invalid memory type: %x\n", memtype);
@@ -1752,7 +1779,10 @@
 	if (result)
 		goto error;
 
-	entry->memdesc.priv |= param->flags & KGSL_MEMTYPE_MASK;
+	if (entry->memdesc.size >= SZ_1M)
+		kgsl_memdesc_set_align(&entry->memdesc, ilog2(SZ_1M));
+	else if (entry->memdesc.size >= SZ_64K)
+		kgsl_memdesc_set_align(&entry->memdesc, ilog2(SZ_64));
 
 	result = kgsl_mmu_map(private->pagetable,
 			      &entry->memdesc,
@@ -2208,6 +2238,8 @@
 kgsl_gpumem_vm_close(struct vm_area_struct *vma)
 {
 	struct kgsl_mem_entry *entry  = vma->vm_private_data;
+
+	entry->memdesc.useraddr = 0;
 	kgsl_mem_entry_put(entry);
 }
 
@@ -2219,6 +2251,7 @@
 
 static int kgsl_mmap(struct file *file, struct vm_area_struct *vma)
 {
+	unsigned int ret;
 	unsigned long vma_offset = vma->vm_pgoff << PAGE_SHIFT;
 	struct kgsl_device_private *dev_priv = file->private_data;
 	struct kgsl_process_private *private = dev_priv->process_priv;
@@ -2245,8 +2278,20 @@
 
 	if (!entry->memdesc.ops ||
 		!entry->memdesc.ops->vmflags ||
-		!entry->memdesc.ops->vmfault)
-		return -EINVAL;
+		!entry->memdesc.ops->vmfault) {
+		ret = -EINVAL;
+		goto err_put;
+	}
+
+	if (entry->memdesc.useraddr != 0) {
+		ret = -EBUSY;
+		goto err_put;
+	}
+
+	if (entry->memdesc.size != (vma->vm_end - vma->vm_start)) {
+		ret = -ERANGE;
+		goto err_put;
+	}
 
 	vma->vm_flags |= entry->memdesc.ops->vmflags(&entry->memdesc);
 
@@ -2255,7 +2300,12 @@
 	vma->vm_ops = &kgsl_gpumem_vm_ops;
 	vma->vm_file = file;
 
+	entry->memdesc.useraddr = vma->vm_start;
+
 	return 0;
+err_put:
+	kgsl_mem_entry_put(entry);
+	return ret;
 }
 
 static irqreturn_t kgsl_irq_handler(int irq, void *data)
@@ -2497,6 +2547,9 @@
 	KGSL_LOG_DUMP(device, "POWER: INTERVAL TIMEOUT = %08X ",
 		pwr->interval_timeout);
 
+	KGSL_LOG_DUMP(device, "POWER: NAP ALLOWED = %d | START_STOP_SLEEP_WAKE = %d\n",
+		pwr->nap_allowed, pwr->strtstp_sleepwake);
+
 	KGSL_LOG_DUMP(device, "GRP_CLK = %lu ",
 				  kgsl_get_clkrate(pwr->grp_clks[0]));
 
diff --git a/drivers/gpu/msm/kgsl.h b/drivers/gpu/msm/kgsl.h
index 416eda9..17a5b67 100644
--- a/drivers/gpu/msm/kgsl.h
+++ b/drivers/gpu/msm/kgsl.h
@@ -124,20 +124,25 @@
 	int (*map_kernel_mem)(struct kgsl_memdesc *);
 };
 
+/* Internal definitions for memdesc->priv */
 #define KGSL_MEMDESC_GUARD_PAGE BIT(0)
+/* Set if the memdesc is mapped into all pagetables */
+#define KGSL_MEMDESC_GLOBAL BIT(1)
 
 /* shared memory allocation */
 struct kgsl_memdesc {
 	struct kgsl_pagetable *pagetable;
-	void *hostptr;
+	void *hostptr; /* kernel virtual address */
+	unsigned long useraddr; /* userspace address */
 	unsigned int gpuaddr;
 	unsigned int physaddr;
 	unsigned int size;
-	unsigned int priv;
+	unsigned int priv; /* Internal flags and settings */
 	struct scatterlist *sg;
-	unsigned int sglen;
+	unsigned int sglen; /* Active entries in the sglist */
+	unsigned int sglen_alloc;  /* Allocated entries in the sglist */
 	struct kgsl_memdesc_ops *ops;
-	int flags;
+	unsigned int flags; /* Flags set from userspace */
 };
 
 /* List of different memory entry types */
@@ -182,6 +187,8 @@
 	struct kgsl_process_private *private, unsigned int gpuaddr,
 	size_t size);
 
+void kgsl_get_memory_usage(char *str, size_t len, unsigned int memflags);
+
 int kgsl_add_event(struct kgsl_device *device, u32 id, u32 ts,
 	void (*cb)(struct kgsl_device *, void *, u32, u32), void *priv,
 	void *owner);
diff --git a/drivers/gpu/msm/kgsl_debugfs.c b/drivers/gpu/msm/kgsl_debugfs.c
index 545d2b3..52097dc 100644
--- a/drivers/gpu/msm/kgsl_debugfs.c
+++ b/drivers/gpu/msm/kgsl_debugfs.c
@@ -16,6 +16,7 @@
 
 #include "kgsl.h"
 #include "kgsl_device.h"
+#include "kgsl_sharedmem.h"
 
 /*default log levels is error for everything*/
 #define KGSL_LOG_LEVEL_DEFAULT 3
@@ -23,6 +24,7 @@
 
 struct dentry *kgsl_debugfs_dir;
 static struct dentry *pm_d_debugfs;
+struct dentry *proc_d_debugfs;
 
 static int pm_dump_set(void *data, u64 val)
 {
@@ -146,9 +148,91 @@
 
 }
 
+static const char * const memtype_strings[] = {
+	"gpumem",
+	"pmem",
+	"ashmem",
+	"usermap",
+	"ion",
+};
+
+static const char *memtype_str(int memtype)
+{
+	if (memtype < ARRAY_SIZE(memtype_strings))
+		return memtype_strings[memtype];
+	return "unknown";
+}
+
+static char get_alignflag(const struct kgsl_memdesc *m)
+{
+	int align = kgsl_memdesc_get_align(m);
+	if (align >= ilog2(SZ_1M))
+		return 'L';
+	else if (align >= ilog2(SZ_64K))
+		return 'l';
+	return '-';
+}
+
+static int process_mem_print(struct seq_file *s, void *unused)
+{
+	struct kgsl_mem_entry *entry;
+	struct rb_node *node;
+	struct kgsl_process_private *private = s->private;
+	char flags[4];
+	char usage[16];
+
+	spin_lock(&private->mem_lock);
+	seq_printf(s, "%8s %8s %5s %10s %16s %5s\n",
+		   "gpuaddr", "size", "flags", "type", "usage", "sglen");
+	for (node = rb_first(&private->mem_rb); node; node = rb_next(node)) {
+		struct kgsl_memdesc *m;
+
+		entry = rb_entry(node, struct kgsl_mem_entry, node);
+		m = &entry->memdesc;
+
+		flags[0] = m->priv & KGSL_MEMDESC_GLOBAL ?  'g' : '-';
+		flags[1] = m->flags & KGSL_MEMFLAGS_GPUREADONLY ? 'r' : '-';
+		flags[2] = get_alignflag(m);
+		flags[3] = '\0';
+
+		kgsl_get_memory_usage(usage, sizeof(usage), m->flags);
+
+		seq_printf(s, "%08x %8d %5s %10s %16s %5d\n",
+			   m->gpuaddr, m->size, flags,
+			   memtype_str(entry->memtype), usage, m->sglen);
+	}
+	spin_unlock(&private->mem_lock);
+	return 0;
+}
+
+static int process_mem_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, process_mem_print, inode->i_private);
+}
+
+static const struct file_operations process_mem_fops = {
+	.open = process_mem_open,
+	.read = seq_read,
+	.llseek = seq_lseek,
+	.release = single_release,
+};
+
+void
+kgsl_process_init_debugfs(struct kgsl_process_private *private)
+{
+	unsigned char name[16];
+
+	snprintf(name, sizeof(name), "%d", private->pid);
+
+	private->debug_root = debugfs_create_dir(name, proc_d_debugfs);
+	debugfs_create_file("mem", 0400, private->debug_root, private,
+			    &process_mem_fops);
+}
+
 void kgsl_core_debugfs_init(void)
 {
 	kgsl_debugfs_dir = debugfs_create_dir("kgsl", 0);
+	proc_d_debugfs = debugfs_create_dir("proc", kgsl_debugfs_dir);
 }
 
 void kgsl_core_debugfs_close(void)
diff --git a/drivers/gpu/msm/kgsl_debugfs.h b/drivers/gpu/msm/kgsl_debugfs.h
index 5e10988..898c4e9 100644
--- a/drivers/gpu/msm/kgsl_debugfs.h
+++ b/drivers/gpu/msm/kgsl_debugfs.h
@@ -15,6 +15,7 @@
 #define _KGSL_DEBUGFS_H
 
 struct kgsl_device;
+struct kgsl_process_private;
 
 #ifdef CONFIG_DEBUG_FS
 void kgsl_core_debugfs_init(void);
@@ -28,11 +29,16 @@
 	return kgsl_debugfs_dir;
 }
 
+int kgsl_process_init_debugfs(struct kgsl_process_private *);
 #else
 static inline void kgsl_core_debugfs_init(void) { }
 static inline void kgsl_device_debugfs_init(struct kgsl_device *device) { }
 static inline void kgsl_core_debugfs_close(void) { }
 static inline struct dentry *kgsl_get_debugfs_dir(void) { return NULL; }
+static inline int kgsl_process_init_debugfs(struct kgsl_process_private *)
+{
+	return 0;
+}
 
 #endif
 
diff --git a/drivers/gpu/msm/kgsl_device.h b/drivers/gpu/msm/kgsl_device.h
index dc597f5..d962bf1 100644
--- a/drivers/gpu/msm/kgsl_device.h
+++ b/drivers/gpu/msm/kgsl_device.h
@@ -235,7 +235,8 @@
 	 * context was responsible for causing it
 	 */
 	unsigned int reset_status;
-
+	/* Flag indicating if we tried to wait for bad timestamp for this ctx */
+	bool wait_on_invalid_ts;
 	/*
 	 * Timeline used to create fences that can be signaled when a
 	 * sync_pt timestamp expires.
@@ -251,6 +252,7 @@
 	struct kgsl_pagetable *pagetable;
 	struct list_head list;
 	struct kobject kobj;
+	struct dentry *debug_root;
 
 	struct {
 		unsigned int cur;
diff --git a/drivers/gpu/msm/kgsl_drm.c b/drivers/gpu/msm/kgsl_drm.c
index 870a7d7..119e25d 100644
--- a/drivers/gpu/msm/kgsl_drm.c
+++ b/drivers/gpu/msm/kgsl_drm.c
@@ -17,6 +17,7 @@
 #include "drmP.h"
 #include "drm.h"
 #include <linux/android_pmem.h>
+#include <linux/msm_ion.h>
 
 #include "kgsl.h"
 #include "kgsl_device.h"
@@ -106,6 +107,7 @@
 	uint32_t type;
 	struct kgsl_memdesc memdesc;
 	struct kgsl_pagetable *pagetable;
+	struct ion_handle *ion_handle;
 	uint64_t mmap_offset;
 	int bufcount;
 	int flags;
@@ -129,6 +131,8 @@
 	struct list_head wait_list;
 };
 
+static struct ion_client *kgsl_drm_ion_phys_client;
+
 static int kgsl_drm_inited = DRM_KGSL_NOT_INITED;
 
 /* This is a global list of all the memory currently mapped in the MMU */
@@ -237,19 +241,56 @@
 		}
 	}
 
+	/* Set the flags for the memdesc (probably 0, unless it is cached) */
+	priv->memdesc.priv = 0;
+
 	if (TYPE_IS_PMEM(priv->type)) {
 		if (priv->type == DRM_KGSL_GEM_TYPE_EBI ||
 		    priv->type & DRM_KGSL_GEM_PMEM_EBI) {
-				result = kgsl_sharedmem_ebimem_user(
-						&priv->memdesc,
-						priv->pagetable,
-						obj->size * priv->bufcount,
-						0);
-				if (result) {
-					DRM_ERROR(
-					"Unable to allocate PMEM memory\n");
-					return result;
-				}
+			priv->ion_handle = ion_alloc(kgsl_drm_ion_phys_client,
+				obj->size * priv->bufcount, PAGE_SIZE,
+				ION_HEAP(ION_SF_HEAP_ID), 0);
+			if (IS_ERR_OR_NULL(priv->ion_handle)) {
+				DRM_ERROR(
+				"Unable to allocate ION Phys memory handle\n");
+				return -ENOMEM;
+			}
+
+			priv->memdesc.pagetable = priv->pagetable;
+
+			result = ion_phys(kgsl_drm_ion_phys_client,
+				priv->ion_handle, (ion_phys_addr_t *)
+				&priv->memdesc.physaddr, &priv->memdesc.size);
+			if (result) {
+				DRM_ERROR(
+				"Unable to get ION Physical memory address\n");
+				ion_free(kgsl_drm_ion_phys_client,
+					priv->ion_handle);
+				priv->ion_handle = NULL;
+				return result;
+			}
+
+			result = memdesc_sg_phys(&priv->memdesc,
+				priv->memdesc.physaddr, priv->memdesc.size);
+			if (result) {
+				DRM_ERROR(
+				"Unable to get sg list\n");
+				ion_free(kgsl_drm_ion_phys_client,
+					priv->ion_handle);
+				priv->ion_handle = NULL;
+				return result;
+			}
+
+			result = kgsl_mmu_map(priv->pagetable, &priv->memdesc,
+					GSL_PT_PAGE_RV | GSL_PT_PAGE_WV);
+			if (result) {
+				DRM_ERROR(
+				"Unable to map GPU\n");
+				ion_free(kgsl_drm_ion_phys_client,
+					priv->ion_handle);
+				priv->ion_handle = NULL;
+				return result;
+			}
 		}
 		else
 			return -EINVAL;
@@ -262,7 +303,7 @@
 
 		result = kgsl_sharedmem_page_alloc_user(&priv->memdesc,
 					priv->pagetable,
-					obj->size * priv->bufcount, 0);
+					obj->size * priv->bufcount);
 
 		if (result != 0) {
 				DRM_ERROR(
@@ -294,7 +335,16 @@
 	kgsl_gem_mem_flush(&priv->memdesc,  priv->type,
 			   DRM_KGSL_GEM_CACHE_OP_FROM_DEV);
 
-	kgsl_sharedmem_free(&priv->memdesc);
+	if (priv->memdesc.gpuaddr)
+		kgsl_mmu_unmap(priv->memdesc.pagetable, &priv->memdesc);
+
+	kgsl_sg_free(priv->memdesc.sg, priv->memdesc.sglen);
+
+	if (priv->ion_handle)
+		ion_free(kgsl_drm_ion_phys_client, priv->ion_handle);
+	priv->ion_handle = NULL;
+
+	memset(&priv->memdesc, 0, sizeof(priv->memdesc));
 
 	kgsl_mmu_putpagetable(priv->pagetable);
 	priv->pagetable = NULL;
@@ -1445,6 +1495,16 @@
 		      DRM_MASTER),
 };
 
+static const struct file_operations kgsl_drm_driver_fops = {
+	.owner = THIS_MODULE,
+	.open = drm_open,
+	.release = drm_release,
+	.unlocked_ioctl = drm_ioctl,
+	.mmap = msm_drm_gem_mmap,
+	.poll = drm_poll,
+	.fasync = drm_fasync,
+};
+
 static struct drm_driver driver = {
 	.driver_features = DRIVER_GEM,
 	.load = kgsl_drm_load,
@@ -1456,17 +1516,7 @@
 	.gem_init_object = kgsl_gem_init_object,
 	.gem_free_object = kgsl_gem_free_object,
 	.ioctls = kgsl_drm_ioctls,
-
-	.fops = {
-		 .owner = THIS_MODULE,
-		 .open = drm_open,
-		 .release = drm_release,
-		 .unlocked_ioctl = drm_ioctl,
-		 .mmap = msm_drm_gem_mmap,
-		 .poll = drm_poll,
-		 .fasync = drm_fasync,
-		 },
-
+	.fops = &kgsl_drm_driver_fops,
 	.name = DRIVER_NAME,
 	.desc = DRIVER_DESC,
 	.date = DRIVER_DATE,
@@ -1495,6 +1545,14 @@
 		gem_buf_fence[i].fence_id = ENTRY_EMPTY;
 	}
 
+	/* Create ION Client */
+	kgsl_drm_ion_phys_client = msm_ion_client_create(
+			ION_HEAP_CARVEOUT_MASK, ION_SF_HEAP_NAME);
+	if (!kgsl_drm_ion_phys_client) {
+		DRM_ERROR("Unable to create ION client\n");
+		return -ENOMEM;
+	}
+
 	return drm_platform_init(&driver, dev);
 }
 
diff --git a/drivers/gpu/msm/kgsl_iommu.c b/drivers/gpu/msm/kgsl_iommu.c
index 87e8746..07ea48e 100644
--- a/drivers/gpu/msm/kgsl_iommu.c
+++ b/drivers/gpu/msm/kgsl_iommu.c
@@ -34,6 +34,7 @@
 	{ 0x14, 0x0003FFFF, 14 },		/* TTBR1 */
 	{ 0x20, 0, 0 },				/* FSR */
 	{ 0x800, 0, 0 },			/* TLBIALL */
+	{ 0x820, 0, 0 },			/* RESUME */
 };
 
 static struct kgsl_iommu_register_list kgsl_iommuv2_reg[KGSL_IOMMU_REG_MAX] = {
@@ -41,7 +42,8 @@
 	{ 0x20, 0x00FFFFFF, 14 },		/* TTBR0 */
 	{ 0x28, 0x00FFFFFF, 14 },		/* TTBR1 */
 	{ 0x58, 0, 0 },				/* FSR */
-	{ 0x618, 0, 0 }				/* TLBIALL */
+	{ 0x618, 0, 0 },			/* TLBIALL */
+	{ 0x008, 0, 0 }				/* RESUME */
 };
 
 static int get_iommu_unit(struct device *dev, struct kgsl_mmu **mmu_out,
@@ -124,9 +126,19 @@
 	KGSL_MEM_CRIT(iommu_dev->kgsldev, "context = %d FSR = %X\n",
 		iommu_dev->ctx_id, fsr);
 
+	mmu->fault = 1;
+	iommu_dev->fault = 1;
+
 	trace_kgsl_mmu_pagefault(iommu_dev->kgsldev, addr,
 			kgsl_mmu_get_ptname_from_ptbase(mmu, ptbase), 0);
 
+	/*
+	 * We do not want the h/w to resume fetching data from an iommu unit
+	 * that has faulted, this is better for debugging as it will stall
+	 * the GPU and trigger a snapshot. To stall the transaction return
+	 * EBUSY error.
+	 */
+	ret = -EBUSY;
 done:
 	return ret;
 }
@@ -780,13 +792,13 @@
 	if (msm_soc_version_supports_iommu_v1()) {
 		for (i = 0; i < iommu->unit_count; i++) {
 			iommu->iommu_units[i].reg_map.priv |=
-						KGSL_MEMFLAGS_GLOBAL;
+						KGSL_MEMDESC_GLOBAL;
 			status = kgsl_mmu_map(pagetable,
 				&(iommu->iommu_units[i].reg_map),
 				GSL_PT_PAGE_RV | GSL_PT_PAGE_WV);
 			if (status) {
 				iommu->iommu_units[i].reg_map.priv &=
-							~KGSL_MEMFLAGS_GLOBAL;
+							~KGSL_MEMDESC_GLOBAL;
 				goto err;
 			}
 		}
@@ -796,7 +808,7 @@
 	for (i--; i >= 0; i--) {
 		kgsl_mmu_unmap(pagetable,
 				&(iommu->iommu_units[i].reg_map));
-		iommu->iommu_units[i].reg_map.priv &= ~KGSL_MEMFLAGS_GLOBAL;
+		iommu->iommu_units[i].reg_map.priv &= ~KGSL_MEMDESC_GLOBAL;
 	}
 	if (mmu->priv_bank_table) {
 		kgsl_mmu_putpagetable(mmu->priv_bank_table);
@@ -859,12 +871,13 @@
 	 */
 	for (i = 0; i < iommu->unit_count; i++) {
 		struct kgsl_iommu_unit *iommu_unit = &iommu->iommu_units[i];
-		for (j = 0; j < iommu_unit->dev_count; j++)
+		for (j = 0; j < iommu_unit->dev_count; j++) {
 			iommu_unit->dev[j].pt_lsb = KGSL_IOMMMU_PT_LSB(iommu,
 						KGSL_IOMMU_GET_CTX_REG(iommu,
 						iommu_unit,
 						iommu_unit->dev[j].ctx_id,
 						TTBR0));
+		}
 	}
 
 	kgsl_iommu_disable_clk_on_ts(mmu, 0, false);
@@ -923,19 +936,22 @@
 	unsigned int iommu_virt_addr;
 	struct kgsl_iommu_pt *iommu_pt = mmu_specific_pt;
 	int size = kgsl_sg_size(memdesc->sg, memdesc->sglen);
+	unsigned int iommu_flags = IOMMU_READ;
 
 	BUG_ON(NULL == iommu_pt);
 
+	if (protflags & GSL_PT_PAGE_WV)
+		iommu_flags |= IOMMU_WRITE;
 
 	iommu_virt_addr = memdesc->gpuaddr;
 
 	ret = iommu_map_range(iommu_pt->domain, iommu_virt_addr, memdesc->sg,
-				size, (IOMMU_READ | IOMMU_WRITE));
+				size, iommu_flags);
 	if (ret) {
 		KGSL_CORE_ERR("iommu_map_range(%p, %x, %p, %d, %d) "
 				"failed with err: %d\n", iommu_pt->domain,
 				iommu_virt_addr, memdesc->sg, size,
-				(IOMMU_READ | IOMMU_WRITE), ret);
+				iommu_flags, ret);
 		return ret;
 	}
 
@@ -945,6 +961,7 @@
 static void kgsl_iommu_stop(struct kgsl_mmu *mmu)
 {
 	struct kgsl_iommu *iommu = mmu->priv;
+	int i, j;
 	/*
 	 *  stop device mmu
 	 *
@@ -957,8 +974,25 @@
 		mmu->hwpagetable = NULL;
 
 		mmu->flags &= ~KGSL_FLAGS_STARTED;
-	}
 
+		if (mmu->fault) {
+			for (i = 0; i < iommu->unit_count; i++) {
+				struct kgsl_iommu_unit *iommu_unit =
+					&iommu->iommu_units[i];
+				for (j = 0; j < iommu_unit->dev_count; j++) {
+					if (iommu_unit->dev[j].fault) {
+						kgsl_iommu_enable_clk(mmu, j);
+						KGSL_IOMMU_SET_CTX_REG(iommu,
+						iommu_unit,
+						iommu_unit->dev[j].ctx_id,
+						RESUME, 1);
+						iommu_unit->dev[j].fault = 0;
+					}
+				}
+			}
+			mmu->fault = 0;
+		}
+	}
 	/* switch off MMU clocks and cancel any events it has queued */
 	iommu->clk_event_queued = false;
 	kgsl_cancel_events(mmu->device, mmu);
diff --git a/drivers/gpu/msm/kgsl_iommu.h b/drivers/gpu/msm/kgsl_iommu.h
index eafba7b..661b4f0 100644
--- a/drivers/gpu/msm/kgsl_iommu.h
+++ b/drivers/gpu/msm/kgsl_iommu.h
@@ -25,6 +25,7 @@
 	KGSL_IOMMU_CTX_TTBR1,
 	KGSL_IOMMU_CTX_FSR,
 	KGSL_IOMMU_CTX_TLBIALL,
+	KGSL_IOMMU_CTX_RESUME,
 	KGSL_IOMMU_REG_MAX
 };
 
@@ -77,6 +78,8 @@
  * @ctx_id: This iommu units context id. It can be either 0 or 1
  * @clk_enabled: If set indicates that iommu clocks of this iommu context
  * are on, else the clocks are off
+ * fault: Flag when set indicates that this iommu device has caused a page
+ * fault
  */
 struct kgsl_iommu_device {
 	struct device *dev;
@@ -85,6 +88,7 @@
 	enum kgsl_iommu_context_id ctx_id;
 	bool clk_enabled;
 	struct kgsl_device *kgsldev;
+	int fault;
 };
 
 /*
diff --git a/drivers/gpu/msm/kgsl_mmu.c b/drivers/gpu/msm/kgsl_mmu.c
index 54ba5ad..68cd167 100644
--- a/drivers/gpu/msm/kgsl_mmu.c
+++ b/drivers/gpu/msm/kgsl_mmu.c
@@ -593,7 +593,7 @@
 _get_pool(struct kgsl_pagetable *pagetable, unsigned int flags)
 {
 	if (pagetable->kgsl_pool &&
-		(KGSL_MEMFLAGS_GLOBAL & flags))
+		(KGSL_MEMDESC_GLOBAL & flags))
 		return pagetable->kgsl_pool;
 	return pagetable->pool;
 }
@@ -606,6 +606,7 @@
 	int ret;
 	struct gen_pool *pool;
 	int size;
+	int page_align = ilog2(PAGE_SIZE);
 
 	if (kgsl_mmu_type == KGSL_MMU_TYPE_NONE) {
 		if (memdesc->sglen == 1) {
@@ -630,7 +631,16 @@
 	/* Allocate from kgsl pool if it exists for global mappings */
 	pool = _get_pool(pagetable, memdesc->priv);
 
-	memdesc->gpuaddr = gen_pool_alloc(pool, size);
+	/* Allocate aligned virtual addresses for iommu. This allows
+	 * more efficient pagetable entries if the physical memory
+	 * is also aligned. Don't do this for GPUMMU, because
+	 * the address space is so small.
+	 */
+	if (KGSL_MMU_TYPE_IOMMU == kgsl_mmu_get_mmutype() &&
+	    kgsl_memdesc_get_align(memdesc) > 0)
+		page_align = kgsl_memdesc_get_align(memdesc);
+
+	memdesc->gpuaddr = gen_pool_alloc_aligned(pool, size, page_align);
 	if (memdesc->gpuaddr == 0) {
 		KGSL_CORE_ERR("gen_pool_alloc(%d) failed from pool: %s\n",
 			size,
@@ -708,7 +718,7 @@
 	 * Don't clear the gpuaddr on global mappings because they
 	 * may be in use by other pagetables
 	 */
-	if (!(memdesc->priv & KGSL_MEMFLAGS_GLOBAL))
+	if (!(memdesc->priv & KGSL_MEMDESC_GLOBAL))
 		memdesc->gpuaddr = 0;
 	return 0;
 }
@@ -729,8 +739,7 @@
 		return 0;
 
 	gpuaddr = memdesc->gpuaddr;
-	memdesc->priv |= KGSL_MEMFLAGS_GLOBAL
-			| (KGSL_MEMTYPE_KERNEL << KGSL_MEMTYPE_SHIFT);
+	memdesc->priv |= KGSL_MEMDESC_GLOBAL;
 
 	result = kgsl_mmu_map(pagetable, memdesc, protflags);
 	if (result)
diff --git a/drivers/gpu/msm/kgsl_mmu.h b/drivers/gpu/msm/kgsl_mmu.h
index 234629b..b8b9149 100644
--- a/drivers/gpu/msm/kgsl_mmu.h
+++ b/drivers/gpu/msm/kgsl_mmu.h
@@ -175,6 +175,7 @@
 	struct kgsl_pagetable  *hwpagetable;
 	const struct kgsl_mmu_ops *mmu_ops;
 	void *priv;
+	int fault;
 };
 
 #include "kgsl_gpummu.h"
diff --git a/drivers/gpu/msm/kgsl_pwrctrl.c b/drivers/gpu/msm/kgsl_pwrctrl.c
index 739dcb5..7a7a8dc 100644
--- a/drivers/gpu/msm/kgsl_pwrctrl.c
+++ b/drivers/gpu/msm/kgsl_pwrctrl.c
@@ -23,6 +23,7 @@
 #include "kgsl_pwrscale.h"
 #include "kgsl_device.h"
 #include "kgsl_trace.h"
+#include "kgsl_sharedmem.h"
 
 #define KGSL_PWRFLAGS_POWER_ON 0
 #define KGSL_PWRFLAGS_CLK_ON   1
@@ -168,7 +169,7 @@
 	if (pwr->pwrlevels[pwr->active_pwrlevel].gpu_freq >
 	    pwr->pwrlevels[pwr->thermal_pwrlevel].gpu_freq)
 		kgsl_pwrctrl_pwrlevel_change(device, pwr->thermal_pwrlevel);
-	else if (!max)
+	else if (!max || (NULL == device->pwrscale.policy))
 		kgsl_pwrctrl_pwrlevel_change(device, i);
 
 done:
@@ -951,6 +952,11 @@
 void kgsl_pwrctrl_wake(struct kgsl_device *device)
 {
 	int status;
+	unsigned int context_id;
+	unsigned int state = device->state;
+	unsigned int ts_processed = 0xdeaddead;
+	struct kgsl_context *context;
+
 	kgsl_pwrctrl_request_state(device, KGSL_STATE_ACTIVE);
 	switch (device->state) {
 	case KGSL_STATE_SLUMBER:
@@ -964,6 +970,17 @@
 	case KGSL_STATE_SLEEP:
 		kgsl_pwrctrl_axi(device, KGSL_PWRFLAGS_ON);
 		kgsl_pwrscale_wake(device);
+		kgsl_sharedmem_readl(&device->memstore,
+			(unsigned int *) &context_id,
+			KGSL_MEMSTORE_OFFSET(KGSL_MEMSTORE_GLOBAL,
+				current_context));
+		context = idr_find(&device->context_idr, context_id);
+		if (context)
+			ts_processed = kgsl_readtimestamp(device, context,
+				KGSL_TIMESTAMP_RETIRED);
+		KGSL_PWR_INFO(device, "Wake from %s state. CTXT: %d RTRD TS: %08X\n",
+			kgsl_pwrstate_to_str(state),
+			context ? context->id : -1, ts_processed);
 		/* fall through */
 	case KGSL_STATE_NAP:
 		/* Turn on the core clocks */
diff --git a/drivers/gpu/msm/kgsl_pwrscale_msm.c b/drivers/gpu/msm/kgsl_pwrscale_msm.c
index acf22ac..b302bee 100644
--- a/drivers/gpu/msm/kgsl_pwrscale_msm.c
+++ b/drivers/gpu/msm/kgsl_pwrscale_msm.c
@@ -20,21 +20,21 @@
 #include "kgsl_trace.h"
 
 struct msm_priv {
-	struct kgsl_device *device;
-	int enabled;
-	int handle;
-	unsigned int cur_freq;
-	struct msm_dcvs_idle idle_source;
-	struct msm_dcvs_freq freq_sink;
-	struct msm_dcvs_core_info *core_info;
-	int gpu_busy;
+	struct kgsl_device		*device;
+	int				enabled;
+	unsigned int			cur_freq;
+	struct msm_dcvs_core_info	*core_info;
+	int				gpu_busy;
+	int				dcvs_core_id;
 };
 
-static int msm_idle_enable(struct msm_dcvs_idle *self,
-					enum msm_core_control_event event)
+/* reference to be used in idle and freq callbacks */
+static struct msm_priv *the_msm_priv;
+
+static int msm_idle_enable(int type_core_num,
+		enum msm_core_control_event event)
 {
-	struct msm_priv *priv = container_of(self, struct msm_priv,
-								idle_source);
+	struct msm_priv *priv = the_msm_priv;
 
 	switch (event) {
 	case MSM_DCVS_ENABLE_IDLE_PULSE:
@@ -53,12 +53,10 @@
 /* Set the requested frequency if it is within 5MHz (delta) of a
  * supported frequency.
  */
-static int msm_set_freq(struct msm_dcvs_freq *self,
-						unsigned int freq)
+static int msm_set_freq(int core_num, unsigned int freq)
 {
 	int i, delta = 5000000;
-	struct msm_priv *priv = container_of(self, struct msm_priv,
-								freq_sink);
+	struct msm_priv *priv = the_msm_priv;
 	struct kgsl_device *device = priv->device;
 	struct kgsl_pwrctrl *pwr = &device->pwrctrl;
 
@@ -79,10 +77,10 @@
 	return priv->cur_freq / 1000;
 }
 
-static unsigned int msm_get_freq(struct msm_dcvs_freq *self)
+static unsigned int msm_get_freq(int core_num)
 {
-	struct msm_priv *priv = container_of(self, struct msm_priv,
-								freq_sink);
+	struct msm_priv *priv = the_msm_priv;
+
 	/* return current frequency in kHz */
 	return priv->cur_freq / 1000;
 }
@@ -92,7 +90,7 @@
 {
 	struct msm_priv *priv = pwrscale->priv;
 	if (priv->enabled && !priv->gpu_busy) {
-		msm_dcvs_idle(priv->handle, MSM_DCVS_IDLE_EXIT, 0);
+		msm_dcvs_idle(priv->dcvs_core_id, MSM_DCVS_IDLE_EXIT, 0);
 		trace_kgsl_mpdcvs(device, 1);
 		priv->gpu_busy = 1;
 	}
@@ -106,7 +104,8 @@
 
 	if (priv->enabled && priv->gpu_busy)
 		if (device->ftbl->isidle(device)) {
-			msm_dcvs_idle(priv->handle, MSM_DCVS_IDLE_ENTER, 0);
+			msm_dcvs_idle(priv->dcvs_core_id,
+					MSM_DCVS_IDLE_ENTER, 0);
 			trace_kgsl_mpdcvs(device, 0);
 			priv->gpu_busy = 0;
 		}
@@ -119,7 +118,7 @@
 	struct msm_priv *priv = pwrscale->priv;
 
 	if (priv->enabled && priv->gpu_busy) {
-		msm_dcvs_idle(priv->handle, MSM_DCVS_IDLE_ENTER, 0);
+		msm_dcvs_idle(priv->dcvs_core_id, MSM_DCVS_IDLE_ENTER, 0);
 		trace_kgsl_mpdcvs(device, 0);
 		priv->gpu_busy = 0;
 	}
@@ -127,13 +126,14 @@
 	return;
 }
 
-static void msm_remove_io_fraction(struct kgsl_device *device)
+static void msm_set_io_fraction(struct kgsl_device *device,
+				unsigned int value)
 {
 	int i;
 	struct kgsl_pwrctrl *pwr = &device->pwrctrl;
 
 	for (i = 0; i < pwr->num_pwrlevels; i++)
-		pwr->pwrlevels[i].io_fraction = 100;
+		pwr->pwrlevels[i].io_fraction = value;
 
 }
 
@@ -154,59 +154,58 @@
 {
 	struct msm_priv *priv;
 	struct msm_dcvs_freq_entry *tbl;
-	int i, ret, low_level;
+	int i, ret = -EINVAL, low_level;
 	struct kgsl_pwrctrl *pwr = &device->pwrctrl;
 	struct platform_device *pdev =
 		container_of(device->parentdev, struct platform_device, dev);
 	struct kgsl_device_platform_data *pdata = pdev->dev.platform_data;
 
-	priv = pwrscale->priv = kzalloc(sizeof(struct msm_priv),
-		GFP_KERNEL);
-	if (pwrscale->priv == NULL)
-		return -ENOMEM;
+	if (the_msm_priv) {
+		priv = pwrscale->priv = the_msm_priv;
+	} else {
+		priv = pwrscale->priv = kzalloc(sizeof(struct msm_priv),
+			GFP_KERNEL);
+		if (pwrscale->priv == NULL)
+			return -ENOMEM;
 
-	priv->core_info = pdata->core_info;
-	tbl = priv->core_info->freq_tbl;
-	/* Fill in frequency table from low to high, reversing order. */
-	low_level = pwr->num_pwrlevels - KGSL_PWRLEVEL_LAST_OFFSET;
-	for (i = 0; i <= low_level; i++)
-		tbl[i].freq = pwr->pwrlevels[low_level - i].gpu_freq / 1000;
-	ret = msm_dcvs_register_core(device->name, priv->core_info);
-	if (ret) {
-		KGSL_PWR_ERR(device, "msm_dcvs_register_core failed");
-		goto err;
+		priv->core_info = pdata->core_info;
+		tbl = priv->core_info->freq_tbl;
+		/* Fill in frequency table from low to high, reversing order. */
+		low_level = pwr->num_pwrlevels - KGSL_PWRLEVEL_LAST_OFFSET;
+		for (i = 0; i <= low_level; i++)
+			tbl[i].freq =
+				pwr->pwrlevels[low_level - i].gpu_freq / 1000;
+		priv->dcvs_core_id =
+				msm_dcvs_register_core(MSM_DCVS_CORE_TYPE_GPU,
+				0,
+				priv->core_info,
+				msm_set_freq, msm_get_freq, msm_idle_enable,
+				priv->core_info->sensors[0]);
+		if (priv->dcvs_core_id < 0) {
+			KGSL_PWR_ERR(device, "msm_dcvs_register_core failed");
+			goto err;
+		}
+		the_msm_priv = priv;
 	}
-
 	priv->device = device;
-	priv->idle_source.enable = msm_idle_enable;
-	priv->idle_source.core_name = device->name;
-	priv->handle = msm_dcvs_idle_source_register(&priv->idle_source);
-	if (priv->handle < 0) {
-		ret = priv->handle;
-		KGSL_PWR_ERR(device, "msm_dcvs_idle_source_register failed\n");
-		goto err;
-	}
-
-	priv->freq_sink.core_name = device->name;
-	priv->freq_sink.set_frequency = msm_set_freq;
-	priv->freq_sink.get_frequency = msm_get_freq;
-	ret = msm_dcvs_freq_sink_register(&priv->freq_sink);
+	ret = msm_dcvs_freq_sink_start(priv->dcvs_core_id);
 	if (ret >= 0) {
 		if (device->ftbl->isidle(device)) {
 			priv->gpu_busy = 0;
-			msm_dcvs_idle(priv->handle, MSM_DCVS_IDLE_ENTER, 0);
+			msm_dcvs_idle(priv->dcvs_core_id,
+					MSM_DCVS_IDLE_ENTER, 0);
 		} else {
 			priv->gpu_busy = 1;
 		}
-		msm_remove_io_fraction(device);
+		msm_set_io_fraction(device, 0);
 		return 0;
 	}
 
 	KGSL_PWR_ERR(device, "msm_dcvs_freq_sink_register failed\n");
-	msm_dcvs_idle_source_unregister(&priv->idle_source);
 
 err:
-	kfree(pwrscale->priv);
+	if (!the_msm_priv)
+		kfree(pwrscale->priv);
 	pwrscale->priv = NULL;
 
 	return ret;
@@ -219,9 +218,7 @@
 
 	if (pwrscale->priv == NULL)
 		return;
-	msm_dcvs_idle_source_unregister(&priv->idle_source);
-	msm_dcvs_freq_sink_unregister(&priv->freq_sink);
-	kfree(pwrscale->priv);
+	msm_dcvs_freq_sink_stop(priv->dcvs_core_id);
 	pwrscale->priv = NULL;
 	msm_restore_io_fraction(device);
 }
diff --git a/drivers/gpu/msm/kgsl_sharedmem.c b/drivers/gpu/msm/kgsl_sharedmem.c
index bdc5686..be51c11 100644
--- a/drivers/gpu/msm/kgsl_sharedmem.c
+++ b/drivers/gpu/msm/kgsl_sharedmem.c
@@ -317,21 +317,42 @@
 				struct vm_area_struct *vma,
 				struct vm_fault *vmf)
 {
-	unsigned long offset;
-	struct page *page;
-	int i;
+	int i, pgoff;
+	struct scatterlist *s = memdesc->sg;
+	unsigned int offset;
 
-	offset = (unsigned long) vmf->virtual_address - vma->vm_start;
+	offset = ((unsigned long) vmf->virtual_address - vma->vm_start);
 
-	i = offset >> PAGE_SHIFT;
-	page = sg_page(&memdesc->sg[i]);
-	if (page == NULL)
+	if (offset >= memdesc->size)
 		return VM_FAULT_SIGBUS;
 
-	get_page(page);
+	pgoff = offset >> PAGE_SHIFT;
 
-	vmf->page = page;
-	return 0;
+	/*
+	 * The sglist might be comprised of mixed blocks of memory depending
+	 * on how many 64K pages were allocated.  This means we have to do math
+	 * to find the actual 4K page to map in user space
+	 */
+
+	for (i = 0; i < memdesc->sglen; i++) {
+		int npages = s->length >> PAGE_SHIFT;
+
+		if (pgoff < npages) {
+			struct page *page = sg_page(s);
+
+			page = nth_page(page, pgoff);
+
+			get_page(page);
+			vmf->page = page;
+
+			return 0;
+		}
+
+		pgoff -= npages;
+		s = sg_next(s);
+	}
+
+	return VM_FAULT_SIGBUS;
 }
 
 static int kgsl_page_alloc_vmflags(struct kgsl_memdesc *memdesc)
@@ -346,7 +367,7 @@
 	int sglen = memdesc->sglen;
 
 	/* Don't free the guard page if it was used */
-	if (memdesc->flags & KGSL_MEMDESC_GUARD_PAGE)
+	if (memdesc->priv & KGSL_MEMDESC_GUARD_PAGE)
 		sglen--;
 
 	kgsl_driver.stats.page_alloc -= memdesc->size;
@@ -357,7 +378,7 @@
 	}
 	if (memdesc->sg)
 		for_each_sg(memdesc->sg, sg, sglen, i)
-			__free_page(sg_page(sg));
+			__free_pages(sg_page(sg), get_order(sg->length));
 }
 
 static int kgsl_contiguous_vmflags(struct kgsl_memdesc *memdesc)
@@ -379,23 +400,32 @@
 		pgprot_t page_prot = pgprot_writecombine(PAGE_KERNEL);
 		struct page **pages = NULL;
 		struct scatterlist *sg;
+		int npages = PAGE_ALIGN(memdesc->size) >> PAGE_SHIFT;
 		int sglen = memdesc->sglen;
-		int i;
+		int i, count = 0;
 
 		/* Don't map the guard page if it exists */
-		if (memdesc->flags & KGSL_MEMDESC_GUARD_PAGE)
+		if (memdesc->priv & KGSL_MEMDESC_GUARD_PAGE)
 			sglen--;
 
 		/* create a list of pages to call vmap */
-		pages = vmalloc(sglen * sizeof(struct page *));
+		pages = vmalloc(npages * sizeof(struct page *));
 		if (!pages) {
 			KGSL_CORE_ERR("vmalloc(%d) failed\n",
-				sglen * sizeof(struct page *));
+				npages * sizeof(struct page *));
 			return -ENOMEM;
 		}
-		for_each_sg(memdesc->sg, sg, sglen, i)
-			pages[i] = sg_page(sg);
-		memdesc->hostptr = vmap(pages, sglen,
+
+		for_each_sg(memdesc->sg, sg, sglen, i) {
+			struct page *page = sg_page(sg);
+			int j;
+
+			for (j = 0; j < sg->length >> PAGE_SHIFT; j++)
+				pages[count++] = page++;
+		}
+
+
+		memdesc->hostptr = vmap(pages, count,
 					VM_IOREMAP, page_prot);
 		KGSL_STATS_ADD(memdesc->size, kgsl_driver.stats.vmalloc,
 				kgsl_driver.stats.vmalloc_max);
@@ -484,18 +514,19 @@
 	void *addr = memdesc->hostptr;
 	int size = memdesc->size;
 
-	switch (op) {
-	case KGSL_CACHE_OP_FLUSH:
-		dmac_flush_range(addr, addr + size);
-		break;
-	case KGSL_CACHE_OP_CLEAN:
-		dmac_clean_range(addr, addr + size);
-		break;
-	case KGSL_CACHE_OP_INV:
-		dmac_inv_range(addr, addr + size);
-		break;
+	if (addr !=  NULL) {
+		switch (op) {
+		case KGSL_CACHE_OP_FLUSH:
+			dmac_flush_range(addr, addr + size);
+			break;
+		case KGSL_CACHE_OP_CLEAN:
+			dmac_clean_range(addr, addr + size);
+			break;
+		case KGSL_CACHE_OP_INV:
+			dmac_inv_range(addr, addr + size);
+			break;
+		}
 	}
-
 	outer_cache_range_op_sg(memdesc->sg, memdesc->sglen, op);
 }
 EXPORT_SYMBOL(kgsl_cache_range_op);
@@ -505,30 +536,26 @@
 			struct kgsl_pagetable *pagetable,
 			size_t size, unsigned int protflags)
 {
-	int i, order, ret = 0;
-	int sglen = PAGE_ALIGN(size) / PAGE_SIZE;
+	int pcount = 0, order, ret = 0;
+	int j, len, page_size, sglen_alloc, sglen = 0;
 	struct page **pages = NULL;
 	pgprot_t page_prot = pgprot_writecombine(PAGE_KERNEL);
 	void *ptr;
-	struct sysinfo si;
+	unsigned int align;
+
+	align = (memdesc->flags & KGSL_MEMALIGN_MASK) >> KGSL_MEMALIGN_SHIFT;
+
+	page_size = (align >= ilog2(SZ_64K) && size >= SZ_64K)
+			? SZ_64K : PAGE_SIZE;
+	/* update align flags for what we actually use */
+	kgsl_memdesc_set_align(memdesc, ilog2(page_size));
 
 	/*
-	 * Get the current memory information to be used in deciding if we
-	 * should go ahead with this allocation
+	 * There needs to be enough room in the sg structure to be able to
+	 * service the allocation entirely with PAGE_SIZE sized chunks
 	 */
 
-	si_meminfo(&si);
-
-	/*
-	 * Limit the size of the allocation to the amount of free memory minus
-	 * 32MB. Why 32MB?  Because thats the buffer that page_alloc uses and
-	 * it just seems like a reasonable limit that won't make the OOM killer
-	 * go all serial on us.  Of course, if we are down this low all bets
-	 * are off but above all do no harm.
-	 */
-
-	if (size >= ((si.freeram << PAGE_SHIFT) - SZ_32M))
-		return -ENOMEM;
+	sglen_alloc = PAGE_ALIGN(size) >> PAGE_SHIFT;
 
 	/*
 	 * Add guard page to the end of the allocation when the
@@ -536,17 +563,17 @@
 	 */
 
 	if (kgsl_mmu_get_mmutype() == KGSL_MMU_TYPE_IOMMU)
-		sglen++;
+		sglen_alloc++;
 
 	memdesc->size = size;
 	memdesc->pagetable = pagetable;
 	memdesc->ops = &kgsl_page_alloc_ops;
 
-	memdesc->sg = kgsl_sg_alloc(sglen);
+	memdesc->sg = kgsl_sg_alloc(sglen_alloc);
 
 	if (memdesc->sg == NULL) {
 		KGSL_CORE_ERR("vmalloc(%d) failed\n",
-			sglen * sizeof(struct scatterlist));
+			sglen_alloc * sizeof(struct scatterlist));
 		ret = -ENOMEM;
 		goto done;
 	}
@@ -558,38 +585,59 @@
 	 * two pages; well within the acceptable limits for using kmalloc.
 	 */
 
-	pages = kmalloc(sglen * sizeof(struct page *), GFP_KERNEL);
+	pages = kmalloc(sglen_alloc * sizeof(struct page *), GFP_KERNEL);
 
 	if (pages == NULL) {
 		KGSL_CORE_ERR("kmalloc (%d) failed\n",
-			sglen * sizeof(struct page *));
+			sglen_alloc * sizeof(struct page *));
 		ret = -ENOMEM;
 		goto done;
 	}
 
 	kmemleak_not_leak(memdesc->sg);
 
-	memdesc->sglen = sglen;
-	sg_init_table(memdesc->sg, sglen);
+	memdesc->sglen_alloc = sglen_alloc;
+	sg_init_table(memdesc->sg, sglen_alloc);
 
-	for (i = 0; i < PAGE_ALIGN(size) / PAGE_SIZE; i++) {
+	len = size;
 
-		/*
-		 * Don't use GFP_ZERO here because it is faster to memset the
-		 * range ourselves (see below)
-		 */
+	while (len > 0) {
+		struct page *page;
+		unsigned int gfp_mask = GFP_KERNEL | __GFP_HIGHMEM |
+			__GFP_NOWARN | __GFP_NORETRY;
+		int j;
 
-		pages[i] = alloc_page(GFP_KERNEL | __GFP_HIGHMEM);
-		if (pages[i] == NULL) {
+		/* don't waste space at the end of the allocation*/
+		if (len < page_size)
+			page_size = PAGE_SIZE;
+
+		if (page_size != PAGE_SIZE)
+			gfp_mask |= __GFP_COMP;
+
+		page = alloc_pages(gfp_mask, get_order(page_size));
+
+		if (page == NULL) {
+			if (page_size != PAGE_SIZE) {
+				page_size = PAGE_SIZE;
+				continue;
+			}
+
+			KGSL_CORE_ERR(
+				"Out of memory: only allocated %dKB of %dKB requested\n",
+				(size - len) >> 10, size >> 10);
+
 			ret = -ENOMEM;
-			memdesc->sglen = i;
 			goto done;
 		}
 
-		sg_set_page(&memdesc->sg[i], pages[i], PAGE_SIZE, 0);
+		for (j = 0; j < page_size >> PAGE_SHIFT; j++)
+			pages[pcount++] = nth_page(page, j);
+
+		sg_set_page(&memdesc->sg[sglen++], page, page_size, 0);
+		len -= page_size;
 	}
 
-	/* ADd the guard page to the end of the sglist */
+	/* Add the guard page to the end of the sglist */
 
 	if (kgsl_mmu_get_mmutype() == KGSL_MMU_TYPE_IOMMU) {
 		/*
@@ -603,13 +651,14 @@
 				__GFP_HIGHMEM);
 
 		if (kgsl_guard_page != NULL) {
-			sg_set_page(&memdesc->sg[sglen - 1], kgsl_guard_page,
+			sg_set_page(&memdesc->sg[sglen++], kgsl_guard_page,
 				PAGE_SIZE, 0);
-			memdesc->flags |= KGSL_MEMDESC_GUARD_PAGE;
-		} else
-			memdesc->sglen--;
+			memdesc->priv |= KGSL_MEMDESC_GUARD_PAGE;
+		}
 	}
 
+	memdesc->sglen = sglen;
+
 	/*
 	 * All memory that goes to the user has to be zeroed out before it gets
 	 * exposed to userspace. This means that the memory has to be mapped in
@@ -629,18 +678,16 @@
 	 * path
 	 */
 
-	ptr = vmap(pages, i, VM_IOREMAP, page_prot);
+	ptr = vmap(pages, pcount, VM_IOREMAP, page_prot);
 
 	if (ptr != NULL) {
 		memset(ptr, 0, memdesc->size);
 		dmac_flush_range(ptr, ptr + memdesc->size);
 		vunmap(ptr);
 	} else {
-		int j;
-
 		/* Very, very, very slow path */
 
-		for (j = 0; j < i; j++) {
+		for (j = 0; j < pcount; j++) {
 			ptr = kmap_atomic(pages[j]);
 			memset(ptr, 0, PAGE_SIZE);
 			dmac_flush_range(ptr, ptr + PAGE_SIZE);
@@ -695,7 +742,7 @@
 int
 kgsl_sharedmem_page_alloc_user(struct kgsl_memdesc *memdesc,
 			    struct kgsl_pagetable *pagetable,
-			    size_t size, int flags)
+			    size_t size)
 {
 	unsigned int protflags;
 
@@ -703,7 +750,7 @@
 		return -EINVAL;
 
 	protflags = GSL_PT_PAGE_RV;
-	if (!(flags & KGSL_MEMFLAGS_GPUREADONLY))
+	if (!(memdesc->flags & KGSL_MEMFLAGS_GPUREADONLY))
 		protflags |= GSL_PT_PAGE_WV;
 
 	return _kgsl_sharedmem_page_alloc(memdesc, pagetable, size,
@@ -757,7 +804,7 @@
 	if (memdesc->ops && memdesc->ops->free)
 		memdesc->ops->free(memdesc);
 
-	kgsl_sg_free(memdesc->sg, memdesc->sglen);
+	kgsl_sg_free(memdesc->sg, memdesc->sglen_alloc);
 
 	memset(memdesc, 0, sizeof(*memdesc));
 }
@@ -804,7 +851,7 @@
 int
 kgsl_sharedmem_ebimem_user(struct kgsl_memdesc *memdesc,
 			struct kgsl_pagetable *pagetable,
-			size_t size, int flags)
+			size_t size)
 {
 	size = ALIGN(size, PAGE_SIZE);
 	return _kgsl_sharedmem_ebimem(memdesc, pagetable, size);
@@ -919,3 +966,42 @@
 	return 0;
 }
 EXPORT_SYMBOL(kgsl_sharedmem_map_vma);
+
+static const char * const memtype_str[] = {
+	[KGSL_MEMTYPE_OBJECTANY] = "any(0)",
+	[KGSL_MEMTYPE_FRAMEBUFFER] = "framebuffer",
+	[KGSL_MEMTYPE_RENDERBUFFER] = "renderbuffer",
+	[KGSL_MEMTYPE_ARRAYBUFFER] = "arraybuffer",
+	[KGSL_MEMTYPE_ELEMENTARRAYBUFFER] = "elementarraybuffer",
+	[KGSL_MEMTYPE_VERTEXARRAYBUFFER] = "vertexarraybuffer",
+	[KGSL_MEMTYPE_TEXTURE] = "texture",
+	[KGSL_MEMTYPE_SURFACE] = "surface",
+	[KGSL_MEMTYPE_EGL_SURFACE] = "egl_surface",
+	[KGSL_MEMTYPE_GL] = "gl",
+	[KGSL_MEMTYPE_CL] = "cl",
+	[KGSL_MEMTYPE_CL_BUFFER_MAP] = "cl_buffer_map",
+	[KGSL_MEMTYPE_CL_BUFFER_NOMAP] = "cl_buffer_nomap",
+	[KGSL_MEMTYPE_CL_IMAGE_MAP] = "cl_image_map",
+	[KGSL_MEMTYPE_CL_IMAGE_NOMAP] = "cl_image_nomap",
+	[KGSL_MEMTYPE_CL_KERNEL_STACK] = "cl_kernel_stack",
+	[KGSL_MEMTYPE_COMMAND] = "command",
+	[KGSL_MEMTYPE_2D] = "2d",
+	[KGSL_MEMTYPE_EGL_IMAGE] = "egl_image",
+	[KGSL_MEMTYPE_EGL_SHADOW] = "egl_shadow",
+	[KGSL_MEMTYPE_MULTISAMPLE] = "egl_multisample",
+	/* KGSL_MEMTYPE_KERNEL handled below, to avoid huge array */
+};
+
+void kgsl_get_memory_usage(char *name, size_t name_size, unsigned int memflags)
+{
+	unsigned char type;
+
+	type = (memflags & KGSL_MEMTYPE_MASK) >> KGSL_MEMTYPE_SHIFT;
+	if (type == KGSL_MEMTYPE_KERNEL)
+		strlcpy(name, "kernel", name_size);
+	else if (type < ARRAY_SIZE(memtype_str) && memtype_str[type] != NULL)
+		strlcpy(name, memtype_str[type], name_size);
+	else
+		snprintf(name, name_size, "unknown(%3d)", type);
+}
+EXPORT_SYMBOL(kgsl_get_memory_usage);
diff --git a/drivers/gpu/msm/kgsl_sharedmem.h b/drivers/gpu/msm/kgsl_sharedmem.h
index 5a6c4c2..92a6f27 100644
--- a/drivers/gpu/msm/kgsl_sharedmem.h
+++ b/drivers/gpu/msm/kgsl_sharedmem.h
@@ -20,6 +20,8 @@
 #include <linux/slab.h>
 #include <linux/kmemleak.h>
 
+#include "kgsl_log.h"
+
 struct kgsl_device;
 struct kgsl_process_private;
 
@@ -27,9 +29,6 @@
 #define KGSL_CACHE_OP_FLUSH     0x02
 #define KGSL_CACHE_OP_CLEAN     0x03
 
-/** Set if the memdesc is mapped into all pagetables */
-#define KGSL_MEMFLAGS_GLOBAL    0x00000002
-
 extern struct kgsl_memdesc_ops kgsl_page_alloc_ops;
 
 int kgsl_sharedmem_page_alloc(struct kgsl_memdesc *memdesc,
@@ -37,13 +36,13 @@
 
 int kgsl_sharedmem_page_alloc_user(struct kgsl_memdesc *memdesc,
 				struct kgsl_pagetable *pagetable,
-				size_t size, int flags);
+				size_t size);
 
 int kgsl_sharedmem_alloc_coherent(struct kgsl_memdesc *memdesc, size_t size);
 
 int kgsl_sharedmem_ebimem_user(struct kgsl_memdesc *memdesc,
 			     struct kgsl_pagetable *pagetable,
-			     size_t size, int flags);
+			     size_t size);
 
 int kgsl_sharedmem_ebimem(struct kgsl_memdesc *memdesc,
 			struct kgsl_pagetable *pagetable,
@@ -71,6 +70,36 @@
 int kgsl_sharedmem_init_sysfs(void);
 void kgsl_sharedmem_uninit_sysfs(void);
 
+/*
+ * kgsl_memdesc_get_align - Get alignment flags from a memdesc
+ * @memdesc - the memdesc
+ *
+ * Returns the alignment requested, as power of 2 exponent.
+ */
+static inline int
+kgsl_memdesc_get_align(const struct kgsl_memdesc *memdesc)
+{
+	return (memdesc->flags & KGSL_MEMALIGN_MASK) >> KGSL_MEMALIGN_SHIFT;
+}
+
+/*
+ * kgsl_memdesc_set_align - Set alignment flags of a memdesc
+ * @memdesc - the memdesc
+ * @align - alignment requested, as a power of 2 exponent.
+ */
+static inline int
+kgsl_memdesc_set_align(struct kgsl_memdesc *memdesc, unsigned int align)
+{
+	if (align > 32) {
+		KGSL_CORE_ERR("Alignment too big, restricting to 2^32\n");
+		align = 32;
+	}
+
+	memdesc->flags &= ~KGSL_MEMALIGN_MASK;
+	memdesc->flags |= (align << KGSL_MEMALIGN_SHIFT) & KGSL_MEMALIGN_MASK;
+	return 0;
+}
+
 static inline unsigned int kgsl_get_sg_pa(struct scatterlist *sg)
 {
 	/*
@@ -134,7 +163,7 @@
 {
 	if (kgsl_mmu_get_mmutype() == KGSL_MMU_TYPE_NONE)
 		return kgsl_sharedmem_ebimem(memdesc, pagetable, size);
-	memdesc->priv |= (KGSL_MEMTYPE_KERNEL << KGSL_MEMTYPE_SHIFT);
+	memdesc->flags |= (KGSL_MEMTYPE_KERNEL << KGSL_MEMTYPE_SHIFT);
 	return kgsl_sharedmem_page_alloc(memdesc, pagetable, size);
 }
 
@@ -144,15 +173,14 @@
 		size_t size, unsigned int flags)
 {
 	int ret;
-	unsigned int mask = (KGSL_MEMTYPE_MASK | KGSL_MEMFLAGS_GPUREADONLY);
+
+	memdesc->flags = flags;
+
 	if (kgsl_mmu_get_mmutype() == KGSL_MMU_TYPE_NONE)
-		ret = kgsl_sharedmem_ebimem_user(memdesc, pagetable, size,
-						  flags);
+		ret = kgsl_sharedmem_ebimem_user(memdesc, pagetable, size);
 	else
-		ret = kgsl_sharedmem_page_alloc_user(memdesc, pagetable, size,
-							flags);
-	if (ret == 0)
-		memdesc->priv |= flags & mask;
+		ret = kgsl_sharedmem_page_alloc_user(memdesc, pagetable, size);
+
 	return ret;
 }
 
@@ -162,6 +190,8 @@
 	int ret  = kgsl_sharedmem_alloc_coherent(memdesc, size);
 	if (!ret && (kgsl_mmu_get_mmutype() == KGSL_MMU_TYPE_NONE))
 		memdesc->gpuaddr = memdesc->physaddr;
+
+	memdesc->flags |= (KGSL_MEMTYPE_KERNEL << KGSL_MEMTYPE_SHIFT);
 	return ret;
 }
 
diff --git a/drivers/gpu/msm/kgsl_trace.h b/drivers/gpu/msm/kgsl_trace.h
index bba06bc..0b247e5 100644
--- a/drivers/gpu/msm/kgsl_trace.h
+++ b/drivers/gpu/msm/kgsl_trace.h
@@ -316,17 +316,21 @@
 		__field(unsigned int, gpuaddr)
 		__field(unsigned int, size)
 		__field(unsigned int, tgid)
+		__array(char, usage, 16)
 	),
 
 	TP_fast_assign(
 		__entry->gpuaddr = mem_entry->memdesc.gpuaddr;
 		__entry->size = mem_entry->memdesc.size;
 		__entry->tgid = mem_entry->priv->pid;
+		kgsl_get_memory_usage(__entry->usage, sizeof(__entry->usage),
+				     mem_entry->memdesc.flags);
 	),
 
 	TP_printk(
-		"gpuaddr=0x%08x size=%d tgid=%d",
-		__entry->gpuaddr, __entry->size, __entry->tgid
+		"gpuaddr=0x%08x size=%d tgid=%d usage=%s",
+		__entry->gpuaddr, __entry->size, __entry->tgid,
+		__entry->usage
 	)
 );
 
@@ -342,6 +346,7 @@
 		__field(int, fd)
 		__field(int, type)
 		__field(unsigned int, tgid)
+		__array(char, usage, 16)
 	),
 
 	TP_fast_assign(
@@ -350,12 +355,15 @@
 		__entry->fd = fd;
 		__entry->type = mem_entry->memtype;
 		__entry->tgid = mem_entry->priv->pid;
+		kgsl_get_memory_usage(__entry->usage, sizeof(__entry->usage),
+				     mem_entry->memdesc.flags);
 	),
 
 	TP_printk(
-		"gpuaddr=0x%08x size=%d type=%d fd=%d tgid=%d",
+		"gpuaddr=0x%08x size=%d type=%d fd=%d tgid=%d usage %s",
 		__entry->gpuaddr, __entry->size,
-		__entry->type, __entry->fd, __entry->tgid
+		__entry->type, __entry->fd, __entry->tgid,
+		__entry->usage
 	)
 );
 
@@ -371,6 +379,7 @@
 		__field(int, type)
 		__field(int, fd)
 		__field(unsigned int, tgid)
+		__array(char, usage, 16)
 	),
 
 	TP_fast_assign(
@@ -378,12 +387,14 @@
 		__entry->size = mem_entry->memdesc.size;
 		__entry->type = mem_entry->memtype;
 		__entry->tgid = mem_entry->priv->pid;
+		kgsl_get_memory_usage(__entry->usage, sizeof(__entry->usage),
+				     mem_entry->memdesc.flags);
 	),
 
 	TP_printk(
-		"gpuaddr=0x%08x size=%d type=%d tgid=%d",
+		"gpuaddr=0x%08x size=%d type=%d tgid=%d usage=%s",
 		__entry->gpuaddr, __entry->size, __entry->type,
-		__entry->tgid
+		__entry->tgid, __entry->usage
 	)
 );
 
@@ -399,6 +410,7 @@
 		__field(unsigned int, gpuaddr)
 		__field(unsigned int, size)
 		__field(int, type)
+		__array(char, usage, 16)
 		__field(unsigned int, drawctxt_id)
 		__field(unsigned int, curr_ts)
 		__field(unsigned int, free_ts)
@@ -408,6 +420,8 @@
 		__assign_str(device_name, device->name);
 		__entry->gpuaddr = mem_entry->memdesc.gpuaddr;
 		__entry->size = mem_entry->memdesc.size;
+		kgsl_get_memory_usage(__entry->usage, sizeof(__entry->usage),
+				     mem_entry->memdesc.flags);
 		__entry->drawctxt_id = id;
 		__entry->type = mem_entry->memtype;
 		__entry->curr_ts = curr_ts;
@@ -415,12 +429,13 @@
 	),
 
 	TP_printk(
-		"d_name=%s gpuaddr=0x%08x size=%d type=%d ctx=%u"
+		"d_name=%s gpuaddr=0x%08x size=%d type=%d usage=%s ctx=%u"
 		" curr_ts=0x%x free_ts=0x%x",
 		__get_str(device_name),
 		__entry->gpuaddr,
 		__entry->size,
 		__entry->type,
+		__entry->usage,
 		__entry->drawctxt_id,
 		__entry->curr_ts,
 		__entry->free_ts
diff --git a/drivers/hwmon/epm_adc.c b/drivers/hwmon/epm_adc.c
index 368aac4..69a2f1c 100644
--- a/drivers/hwmon/epm_adc.c
+++ b/drivers/hwmon/epm_adc.c
@@ -1703,7 +1703,7 @@
 }
 
 static const struct of_device_id epm_adc_psoc_match_table[] = {
-	{	.compatible = "qcom,epm-adc",
+	{	.compatible = "cy,epm-adc-cy8c5568lti-114",
 	},
 	{}
 };
diff --git a/drivers/hwmon/qpnp-adc-current.c b/drivers/hwmon/qpnp-adc-current.c
index aa375d7..10c5a17 100644
--- a/drivers/hwmon/qpnp-adc-current.c
+++ b/drivers/hwmon/qpnp-adc-current.c
@@ -111,6 +111,13 @@
 #define QPNP_DATA1					0x61
 #define QPNP_CONV_TIMEOUT_ERR				2
 
+#define QPNP_IADC_SEC_ACCESS				0xD0
+#define QPNP_IADC_SEC_ACCESS_DATA			0xA5
+#define QPNP_IADC_MSB_OFFSET				0xF2
+#define QPNP_IADC_LSB_OFFSET				0xF3
+#define QPNP_IADC_NOMINAL_RSENSE			0xF4
+#define QPNP_IADC_ATE_GAIN_CALIB_OFFSET			0xF5
+
 #define QPNP_IADC_ADC_CH_SEL_CTL			0x48
 #define QPNP_IADC_ADC_CHX_SEL_SHIFT			3
 
@@ -127,15 +134,27 @@
 #define QPNP_ADC_CONV_TIME_MIN				8000
 #define QPNP_ADC_CONV_TIME_MAX				8200
 
-#define QPNP_ADC_GAIN_CALCULATION_UV			17857
-#define QPNP_IADC_RSENSE_MILLI_FACTOR			1000
+#define QPNP_ADC_GAIN_NV				17857
+#define QPNP_OFFSET_CALIBRATION_SHORT_CADC_LEADS_IDEAL	0
+#define QPNP_IADC_INTERNAL_RSENSE_N_OHMS_FACTOR		10000000
+#define QPNP_IADC_NANO_VOLTS_FACTOR			1000000000
+#define QPNP_IADC_CALIB_SECONDS				300000
+#define QPNP_IADC_RSENSE_LSB_N_OHMS_PER_BIT		15625
+#define QPNP_IADC_DIE_TEMP_CALIB_OFFSET			5000
+
+#define QPNP_RAW_CODE_16_BIT_MSB_MASK			0xff00
+#define QPNP_RAW_CODE_16_BIT_LSB_MASK			0xff
+#define QPNP_BIT_SHIFT_8				8
+#define QPNP_RSENSE_MSB_SIGN_CHECK			0x80
 
 struct qpnp_iadc_drv {
-	struct qpnp_adc_drv		*adc;
-	int32_t				rsense;
-	struct device			*iadc_hwmon;
-	bool				iadc_init_calib;
-	bool				iadc_initialized;
+	struct qpnp_adc_drv			*adc;
+	int32_t					rsense;
+	struct device				*iadc_hwmon;
+	bool					iadc_init_calib;
+	bool					iadc_initialized;
+	int64_t					die_temp_calib_offset;
+	struct delayed_work			iadc_work;
 	struct sensor_device_attribute		sens_attr[0];
 };
 
@@ -253,9 +272,10 @@
 	return 0;
 }
 
-static int32_t qpnp_iadc_read_conversion_result(int32_t *data)
+static int32_t qpnp_iadc_read_conversion_result(uint16_t *data)
 {
 	uint8_t rslt_lsb, rslt_msb;
+	uint16_t rslt;
 	int32_t rc;
 
 	rc = qpnp_iadc_read_reg(QPNP_IADC_DATA0, &rslt_lsb);
@@ -270,16 +290,18 @@
 		return rc;
 	}
 
-	*data = (rslt_msb << 8) | rslt_lsb;
+	rslt = (rslt_msb << 8) | rslt_lsb;
+	*data = rslt;
 
 	rc = qpnp_iadc_enable(false);
 	if (rc)
 		return rc;
+
 	return 0;
 }
 
 static int32_t qpnp_iadc_configure(enum qpnp_iadc_channels channel,
-						int32_t *result)
+						uint16_t *raw_code)
 {
 	struct qpnp_iadc_drv *iadc = qpnp_iadc;
 	u8 qpnp_iadc_mode_reg = 0, qpnp_iadc_ch_sel_reg = 0;
@@ -346,7 +368,7 @@
 
 	wait_for_completion(&iadc->adc->adc_rslt_completion);
 
-	rc = qpnp_iadc_read_conversion_result(result);
+	rc = qpnp_iadc_read_conversion_result(raw_code);
 	if (rc) {
 		pr_err("qpnp adc read adc failed with %d\n", rc);
 		return rc;
@@ -355,32 +377,109 @@
 	return 0;
 }
 
-static int32_t qpnp_iadc_init_calib(void)
+static int32_t qpnp_convert_raw_offset_voltage(void)
 {
 	struct qpnp_iadc_drv *iadc = qpnp_iadc;
-	int32_t rc = 0, result;
+	uint32_t num = 0;
 
-	rc = qpnp_iadc_configure(GAIN_CALIBRATION_25MV, &result);
+	num = iadc->adc->calib.offset_raw - iadc->adc->calib.offset_raw;
+
+	iadc->adc->calib.offset_uv = (num * QPNP_ADC_GAIN_NV)/
+		(iadc->adc->calib.gain_raw - iadc->adc->calib.offset_raw);
+
+	num = iadc->adc->calib.gain_raw - iadc->adc->calib.offset_raw;
+
+	iadc->adc->calib.gain_uv = (num * QPNP_ADC_GAIN_NV)/
+		(iadc->adc->calib.gain_raw - iadc->adc->calib.offset_raw);
+
+	return 0;
+}
+
+static int32_t qpnp_iadc_calibrate_for_trim(void)
+{
+	struct qpnp_iadc_drv *iadc = qpnp_iadc;
+	uint8_t rslt_lsb, rslt_msb;
+	int32_t rc = 0;
+	uint16_t raw_data;
+
+	rc = qpnp_iadc_configure(GAIN_CALIBRATION_17P857MV, &raw_data);
 	if (rc < 0) {
 		pr_err("qpnp adc result read failed with %d\n", rc);
 		goto fail;
 	}
 
-	iadc->adc->calib.gain = result;
+	iadc->adc->calib.gain_raw = raw_data;
 
 	rc = qpnp_iadc_configure(OFFSET_CALIBRATION_SHORT_CADC_LEADS,
-								&result);
+								&raw_data);
 	if (rc < 0) {
 		pr_err("qpnp adc result read failed with %d\n", rc);
 		goto fail;
 	}
 
-	iadc->adc->calib.offset = result;
+	iadc->adc->calib.offset_raw = raw_data;
+	if (rc < 0) {
+		pr_err("qpnp adc offset/gain calculation failed\n");
+		goto fail;
+	}
 
+	rc = qpnp_convert_raw_offset_voltage();
+
+	rslt_msb = (raw_data & QPNP_RAW_CODE_16_BIT_MSB_MASK) >>
+							QPNP_BIT_SHIFT_8;
+	rslt_lsb = raw_data & QPNP_RAW_CODE_16_BIT_LSB_MASK;
+
+	rc = qpnp_iadc_write_reg(QPNP_IADC_SEC_ACCESS,
+					QPNP_IADC_SEC_ACCESS_DATA);
+	if (rc < 0) {
+		pr_err("qpnp iadc configure error for sec access\n");
+		goto fail;
+	}
+
+	rc = qpnp_iadc_write_reg(QPNP_IADC_MSB_OFFSET,
+						rslt_msb);
+	if (rc < 0) {
+		pr_err("qpnp iadc configure error for MSB write\n");
+		goto fail;
+	}
+
+	rc = qpnp_iadc_write_reg(QPNP_IADC_SEC_ACCESS,
+					QPNP_IADC_SEC_ACCESS_DATA);
+	if (rc < 0) {
+		pr_err("qpnp iadc configure error for sec access\n");
+		goto fail;
+	}
+
+	rc = qpnp_iadc_write_reg(QPNP_IADC_LSB_OFFSET,
+						rslt_lsb);
+	if (rc < 0) {
+		pr_err("qpnp iadc configure error for LSB write\n");
+		goto fail;
+	}
 fail:
 	return rc;
 }
 
+static void qpnp_iadc_work(struct work_struct *work)
+{
+	struct qpnp_iadc_drv *iadc = qpnp_iadc;
+	int rc = 0;
+
+	mutex_lock(&iadc->adc->adc_lock);
+
+	rc = qpnp_iadc_calibrate_for_trim();
+	if (rc)
+		pr_err("periodic IADC calibration failed\n");
+
+	mutex_unlock(&iadc->adc->adc_lock);
+
+	schedule_delayed_work(&iadc->iadc_work,
+			round_jiffies_relative(msecs_to_jiffies
+					(QPNP_IADC_CALIB_SECONDS)));
+
+	return;
+}
+
 static int32_t qpnp_iadc_version_check(void)
 {
 	uint8_t revision;
@@ -411,40 +510,102 @@
 }
 EXPORT_SYMBOL(qpnp_iadc_is_ready);
 
-int32_t qpnp_iadc_read(enum qpnp_iadc_channels channel,
-						int32_t *result)
+int32_t qpnp_iadc_get_rsense(int32_t *rsense)
+{
+	uint8_t	rslt_rsense;
+	int32_t	rc, sign_bit = 0;
+
+	rc = qpnp_iadc_read_reg(QPNP_IADC_NOMINAL_RSENSE, &rslt_rsense);
+	if (rc < 0) {
+		pr_err("qpnp adc rsense read failed with %d\n", rc);
+		return rc;
+	}
+
+	if (rslt_rsense & QPNP_RSENSE_MSB_SIGN_CHECK)
+		sign_bit = 1;
+
+	rslt_rsense &= ~QPNP_RSENSE_MSB_SIGN_CHECK;
+
+	if (sign_bit)
+		*rsense = QPNP_IADC_INTERNAL_RSENSE_N_OHMS_FACTOR -
+			(rslt_rsense * QPNP_IADC_RSENSE_LSB_N_OHMS_PER_BIT);
+	else
+		*rsense = QPNP_IADC_INTERNAL_RSENSE_N_OHMS_FACTOR +
+			(rslt_rsense * QPNP_IADC_RSENSE_LSB_N_OHMS_PER_BIT);
+
+	return rc;
+}
+
+int32_t qpnp_check_pmic_temp(void)
 {
 	struct qpnp_iadc_drv *iadc = qpnp_iadc;
-	int32_t vsense_mv = 0, rc;
+	struct qpnp_vadc_result result_pmic_therm;
+	int rc;
+
+	rc = qpnp_vadc_read(DIE_TEMP, &result_pmic_therm);
+	if (rc < 0)
+		return rc;
+
+	if (((uint64_t) (result_pmic_therm.physical -
+				iadc->die_temp_calib_offset))
+			> QPNP_IADC_DIE_TEMP_CALIB_OFFSET) {
+		mutex_lock(&iadc->adc->adc_lock);
+
+		rc = qpnp_iadc_calibrate_for_trim();
+		if (rc)
+			pr_err("periodic IADC calibration failed\n");
+
+		mutex_unlock(&iadc->adc->adc_lock);
+	}
+
+	return 0;
+}
+
+int32_t qpnp_iadc_read(enum qpnp_iadc_channels channel,
+				struct qpnp_iadc_result *result)
+{
+	struct qpnp_iadc_drv *iadc = qpnp_iadc;
+	int32_t rc, rsense_n_ohms, sign = 0, num;
+	int64_t result_current;
+	uint16_t raw_data;
 
 	if (!iadc || !iadc->iadc_initialized)
 		return -EPROBE_DEFER;
 
-	mutex_lock(&iadc->adc->adc_lock);
-
-	if (!iadc->iadc_init_calib) {
-		rc = qpnp_iadc_version_check();
-		if (rc)
-			goto fail;
-		rc = qpnp_iadc_init_calib();
-		if (rc) {
-			pr_err("Calibration failed\n");
-			goto fail;
-		} else
-			iadc->iadc_init_calib = true;
+	rc = qpnp_check_pmic_temp();
+	if (rc) {
+		pr_err("Error checking pmic therm temp\n");
+		return rc;
 	}
 
-	rc = qpnp_iadc_configure(channel, result);
+	mutex_lock(&iadc->adc->adc_lock);
+
+	rc = qpnp_iadc_configure(channel, &raw_data);
 	if (rc < 0) {
 		pr_err("qpnp adc result read failed with %d\n", rc);
 		goto fail;
 	}
 
-	*result = ((vsense_mv - iadc->adc->calib.offset) *
-				QPNP_ADC_GAIN_CALCULATION_UV)/
-			(iadc->adc->calib.gain - iadc->adc->calib.offset);
+	rc = qpnp_iadc_get_rsense(&rsense_n_ohms);
 
-	*result = (*result / (qpnp_iadc->rsense));
+	num = raw_data - iadc->adc->calib.offset_raw;
+	if (num < 0) {
+		sign = 1;
+		num = -num;
+	}
+
+	result->result_uv = (num * QPNP_ADC_GAIN_NV)/
+		(iadc->adc->calib.gain_raw - iadc->adc->calib.offset_raw);
+	result_current = result->result_uv;
+	result_current *= QPNP_IADC_NANO_VOLTS_FACTOR;
+	do_div(result_current, rsense_n_ohms);
+
+	if (sign) {
+		result->result_uv = -result->result_uv;
+		result_current = -result_current;
+	}
+
+	result->result_ua = (int32_t) result_current;
 fail:
 	mutex_unlock(&iadc->adc->adc_lock);
 
@@ -452,24 +613,39 @@
 }
 EXPORT_SYMBOL(qpnp_iadc_read);
 
-int32_t qpnp_iadc_get_gain(int32_t *result)
+int32_t qpnp_iadc_get_gain_and_offset(struct qpnp_iadc_calib *result)
 {
-	return qpnp_iadc_read(GAIN_CALIBRATION_25MV, result);
-}
-EXPORT_SYMBOL(qpnp_iadc_get_gain);
+	struct qpnp_iadc_drv *iadc = qpnp_iadc;
+	int rc;
 
-int32_t qpnp_iadc_get_offset(enum qpnp_iadc_channels channel,
-						int32_t *result)
-{
-	return qpnp_iadc_read(channel, result);
+	if (!iadc || !iadc->iadc_initialized)
+		return -EPROBE_DEFER;
+
+	rc = qpnp_check_pmic_temp();
+	if (rc) {
+		pr_err("Error checking pmic therm temp\n");
+		return rc;
+	}
+
+	mutex_lock(&iadc->adc->adc_lock);
+	result->gain_raw = iadc->adc->calib.gain_raw;
+	result->ideal_gain_nv = QPNP_ADC_GAIN_NV;
+	result->gain_uv = iadc->adc->calib.gain_uv;
+	result->offset_raw = iadc->adc->calib.offset_raw;
+	result->ideal_offset_uv =
+				QPNP_OFFSET_CALIBRATION_SHORT_CADC_LEADS_IDEAL;
+	result->offset_uv = iadc->adc->calib.offset_uv;
+	mutex_unlock(&iadc->adc->adc_lock);
+
+	return 0;
 }
-EXPORT_SYMBOL(qpnp_iadc_get_offset);
+EXPORT_SYMBOL(qpnp_iadc_get_gain_and_offset);
 
 static ssize_t qpnp_iadc_show(struct device *dev,
 			struct device_attribute *devattr, char *buf)
 {
 	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
-	int32_t result;
+	struct qpnp_iadc_result result;
 	int rc = -1;
 
 	rc = qpnp_iadc_read(attr->index, &result);
@@ -478,7 +654,7 @@
 		return 0;
 
 	return snprintf(buf, QPNP_ADC_HWMON_NAME_LENGTH,
-					"Result:%d\n", result);
+					"Result:%d\n", result.result_ua);
 }
 
 static struct sensor_device_attribute qpnp_adc_attr =
@@ -496,9 +672,9 @@
 		qpnp_adc_attr.index = iadc->adc->adc_channels[i].channel_num;
 		qpnp_adc_attr.dev_attr.attr.name =
 						iadc->adc->adc_channels[i].name;
-		sysfs_attr_init(&iadc->sens_attr[i].dev_attr.attr);
 		memcpy(&iadc->sens_attr[i], &qpnp_adc_attr,
 						sizeof(qpnp_adc_attr));
+		sysfs_attr_init(&iadc->sens_attr[i].dev_attr.attr);
 		rc = device_create_file(&spmi->dev,
 				&iadc->sens_attr[i].dev_attr);
 		if (rc) {
@@ -592,10 +768,28 @@
 
 	rc = qpnp_iadc_configure_interrupt();
 	if (rc) {
-		dev_err(&spmi->dev, "failed to configure interrupt");
+		dev_err(&spmi->dev, "failed to configure interrupt\n");
 		return rc;
 	}
 
+	rc = qpnp_iadc_version_check();
+	if (rc) {
+		dev_err(&spmi->dev, "IADC version not supported\n");
+		return rc;
+	}
+
+	rc = qpnp_iadc_calibrate_for_trim();
+	if (rc) {
+		dev_err(&spmi->dev, "failed to calibrate for USR trim\n");
+		return rc;
+	}
+	iadc->iadc_init_calib = true;
+	INIT_DELAYED_WORK(&iadc->iadc_work, qpnp_iadc_work);
+	schedule_delayed_work(&iadc->iadc_work,
+			round_jiffies_relative(msecs_to_jiffies
+					(QPNP_IADC_CALIB_SECONDS)));
+	iadc->iadc_initialized = true;
+
 	return 0;
 }
 
diff --git a/drivers/hwmon/qpnp-adc-voltage.c b/drivers/hwmon/qpnp-adc-voltage.c
index 5690c88..5eef34f 100644
--- a/drivers/hwmon/qpnp-adc-voltage.c
+++ b/drivers/hwmon/qpnp-adc-voltage.c
@@ -714,9 +714,9 @@
 		qpnp_adc_attr.index = vadc->adc->adc_channels[i].channel_num;
 		qpnp_adc_attr.dev_attr.attr.name =
 						vadc->adc->adc_channels[i].name;
-		sysfs_attr_init(&vadc->sens_attr[i].dev_attr.attr);
 		memcpy(&vadc->sens_attr[i], &qpnp_adc_attr,
 						sizeof(qpnp_adc_attr));
+		sysfs_attr_init(&vadc->sens_attr[i].dev_attr.attr);
 		rc = device_create_file(&spmi->dev,
 				&vadc->sens_attr[i].dev_attr);
 		if (rc) {
diff --git a/drivers/input/keyboard/gpio_keys.c b/drivers/input/keyboard/gpio_keys.c
index 62bfce4..ce1a18e 100644
--- a/drivers/input/keyboard/gpio_keys.c
+++ b/drivers/input/keyboard/gpio_keys.c
@@ -557,6 +557,7 @@
 	memset(pdata, 0, sizeof *pdata);
 
 	pdata->rep = !!of_get_property(node, "autorepeat", NULL);
+	pdata->name = of_get_property(node, "input-name", NULL);
 
 	/* First count the subnodes */
 	pdata->nbuttons = 0;
diff --git a/drivers/input/misc/pmic8xxx-pwrkey.c b/drivers/input/misc/pmic8xxx-pwrkey.c
index 775e95d..abd66f4 100644
--- a/drivers/input/misc/pmic8xxx-pwrkey.c
+++ b/drivers/input/misc/pmic8xxx-pwrkey.c
@@ -104,14 +104,75 @@
 static SIMPLE_DEV_PM_OPS(pm8xxx_pwr_key_pm_ops,
 		pmic8xxx_pwrkey_suspend, pmic8xxx_pwrkey_resume);
 
+static int pmic8xxx_set_pon1(struct device *dev, u32 debounce_us, bool pull_up)
+{
+	int err;
+	u32 delay;
+	u8 pon_cntl;
+
+	/* Valid range of pwr key trigger delay is 1/64 sec to 2 seconds. */
+	if (debounce_us > USEC_PER_SEC * 2 ||
+		debounce_us < USEC_PER_SEC / 64) {
+		dev_err(dev, "invalid power key trigger delay\n");
+		return -EINVAL;
+	}
+
+	delay = (debounce_us << 6) / USEC_PER_SEC;
+	delay = ilog2(delay);
+
+	err = pm8xxx_readb(dev->parent, PON_CNTL_1, &pon_cntl);
+	if (err < 0) {
+		dev_err(dev, "failed reading PON_CNTL_1 err=%d\n", err);
+		return err;
+	}
+
+	pon_cntl &= ~PON_CNTL_TRIG_DELAY_MASK;
+	pon_cntl |= (delay & PON_CNTL_TRIG_DELAY_MASK);
+
+	if (pull_up)
+		pon_cntl |= PON_CNTL_PULL_UP;
+	else
+		pon_cntl &= ~PON_CNTL_PULL_UP;
+
+	err = pm8xxx_writeb(dev->parent, PON_CNTL_1, pon_cntl);
+	if (err < 0) {
+		dev_err(dev, "failed writing PON_CNTL_1 err=%d\n", err);
+		return err;
+	}
+
+	return 0;
+}
+
+static ssize_t pmic8xxx_debounce_store(struct device *dev,
+			struct device_attribute *attr,
+			const char *buf, size_t size)
+{
+	struct pmic8xxx_pwrkey *pwrkey = dev_get_drvdata(dev);
+	int err;
+	unsigned long val;
+
+	if (size > 8)
+		return -EINVAL;
+
+	err = kstrtoul(buf, 10, &val);
+	if (err < 0)
+		return err;
+
+	err = pmic8xxx_set_pon1(dev, val, pwrkey->pdata->pull_up);
+	if (err < 0)
+		return err;
+
+	return size;
+}
+
+static DEVICE_ATTR(debounce_us, 0664, NULL, pmic8xxx_debounce_store);
+
 static int __devinit pmic8xxx_pwrkey_probe(struct platform_device *pdev)
 {
 	struct input_dev *pwr;
 	int key_release_irq = platform_get_irq(pdev, 0);
 	int key_press_irq = platform_get_irq(pdev, 1);
 	int err;
-	unsigned int delay;
-	u8 pon_cntl;
 	struct pmic8xxx_pwrkey *pwrkey;
 	const struct pm8xxx_pwrkey_platform_data *pdata =
 					dev_get_platdata(&pdev->dev);
@@ -121,13 +182,6 @@
 		return -EINVAL;
 	}
 
-	/* Valid range of pwr key trigger delay is 1/64 sec to 2 seconds. */
-	if (pdata->kpd_trigger_delay_us > USEC_PER_SEC * 2 ||
-		pdata->kpd_trigger_delay_us < USEC_PER_SEC / 64) {
-		dev_err(&pdev->dev, "invalid power key trigger delay\n");
-		return -EINVAL;
-	}
-
 	pwrkey = kzalloc(sizeof(*pwrkey), GFP_KERNEL);
 	if (!pwrkey)
 		return -ENOMEM;
@@ -147,25 +201,10 @@
 	pwr->phys = "pmic8xxx_pwrkey/input0";
 	pwr->dev.parent = &pdev->dev;
 
-	delay = (pdata->kpd_trigger_delay_us << 6) / USEC_PER_SEC;
-	delay = ilog2(delay);
-
-	err = pm8xxx_readb(pdev->dev.parent, PON_CNTL_1, &pon_cntl);
-	if (err < 0) {
-		dev_err(&pdev->dev, "failed reading PON_CNTL_1 err=%d\n", err);
-		goto free_input_dev;
-	}
-
-	pon_cntl &= ~PON_CNTL_TRIG_DELAY_MASK;
-	pon_cntl |= (delay & PON_CNTL_TRIG_DELAY_MASK);
-	if (pdata->pull_up)
-		pon_cntl |= PON_CNTL_PULL_UP;
-	else
-		pon_cntl &= ~PON_CNTL_PULL_UP;
-
-	err = pm8xxx_writeb(pdev->dev.parent, PON_CNTL_1, pon_cntl);
-	if (err < 0) {
-		dev_err(&pdev->dev, "failed writing PON_CNTL_1 err=%d\n", err);
+	err = pmic8xxx_set_pon1(&pdev->dev,
+			pdata->kpd_trigger_delay_us, pdata->pull_up);
+	if (err) {
+		dev_dbg(&pdev->dev, "Can't set PON CTRL1 register: %d\n", err);
 		goto free_input_dev;
 	}
 
@@ -211,12 +250,22 @@
 		goto free_press_irq;
 	}
 
+	err = device_create_file(&pdev->dev, &dev_attr_debounce_us);
+	if (err < 0) {
+		dev_err(&pdev->dev,
+				"dev file creation for debounce failed: %d\n",
+				err);
+		goto free_rel_irq;
+	}
+
 	device_init_wakeup(&pdev->dev, pdata->wakeup);
 
 	return 0;
 
+free_rel_irq:
+	free_irq(key_release_irq, pwrkey);
 free_press_irq:
-	free_irq(key_press_irq, NULL);
+	free_irq(key_press_irq, pwrkey);
 unreg_input_dev:
 	platform_set_drvdata(pdev, NULL);
 	input_unregister_device(pwr);
@@ -236,6 +285,7 @@
 
 	device_init_wakeup(&pdev->dev, 0);
 
+	device_remove_file(&pdev->dev, &dev_attr_debounce_us);
 	free_irq(key_press_irq, pwrkey);
 	free_irq(key_release_irq, pwrkey);
 	input_unregister_device(pwrkey->pwr);
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index 5a1eec7..7b28e9d 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -79,3 +79,4 @@
 obj-$(CONFIG_TOUCHSCREEN_MSM_LEGACY)		+= msm_touch.o
 obj-$(CONFIG_TOUCHSCREEN_CY8C_TS)	+= cy8c_ts.o
 obj-$(CONFIG_TOUCHSCREEN_CYTTSP_I2C_QC)       += cyttsp-i2c-qc.o
+obj-$(CONFIG_TOUCHSCREEN_FT5X06)	+= ft5x06_ts.o
diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c
index f671806..b3bd8a0 100644
--- a/drivers/input/touchscreen/atmel_mxt_ts.c
+++ b/drivers/input/touchscreen/atmel_mxt_ts.c
@@ -363,6 +363,7 @@
 	int t38_start_addr;
 	bool update_cfg;
 	const char *fw_name;
+	bool no_force_update;
 };
 
 static struct dentry *debug_base;
@@ -984,9 +985,9 @@
 			continue;
 		}
 
-		/* check whether report id is part of T9 or T15 */
 		id = reportid - data->t9_min_reportid;
 
+		 /* check whether report id is part of T9,T15 or T42*/
 		if (reportid >= data->t9_min_reportid &&
 					reportid <= data->t9_max_reportid)
 			mxt_input_touchevent(data, &message, id);
@@ -1273,25 +1274,18 @@
 			data->cfg_version[0], data->cfg_version[1],
 			data->cfg_version[2]);
 
-	/* It is possible that the config data on the controller is not
-	 * versioned and the version number returns 0. In this case,
-	 * find a match without the config version checking.
-	 */
-	error = mxt_search_config_array(data,
-				data->cfg_version[0] != 0 ? true : false);
+	/* configuration update requires major match */
+	error = mxt_search_config_array(data, true);
+
+	/* if no_force_update is false , try again with false
+	as the second parameter to mxt_search_config_array */
+	if (error && (data->no_force_update == false))
+		error = mxt_search_config_array(data, false);
+
 	if (error) {
-		/* If a match wasn't found for a non-zero config version,
-		 * it means the controller has the wrong config data. Search
-		 * for a best match based on controller and firmware version,
-		 * but not config version.
-		 */
-		if (data->cfg_version[0])
-			error = mxt_search_config_array(data, false);
-		if (error) {
-			dev_err(dev,
-				"Unable to find matching config in pdata\n");
-			return error;
-		}
+		dev_err(dev,
+			"Unable to find matching config in pdata\n");
+		return error;
 	}
 
 	return 0;
@@ -1418,13 +1412,62 @@
 	return 0;
 }
 
+static int mxt_update_cfg(struct mxt_data *data)
+{
+	int error;
+	const u8 *cfg_ver;
+
+	/* Get config data from platform data */
+	error = mxt_get_config(data);
+	if (error)
+		dev_dbg(&data->client->dev, "Config info not found.\n");
+
+	/* Check register init values */
+	if (data->config_info && data->config_info->config) {
+		if (data->update_cfg) {
+			error = mxt_check_reg_init(data);
+			if (error) {
+				dev_err(&data->client->dev,
+					"Failed to check reg init value\n");
+				return error;
+			}
+
+			error = mxt_backup_nv(data);
+			if (error) {
+				dev_err(&data->client->dev, "Failed to back up NV\n");
+				return error;
+			}
+
+			cfg_ver = data->config_info->config +
+						data->cfg_version_idx;
+			dev_info(&data->client->dev,
+				"Config updated from %d.%d.%d to %d.%d.%d\n",
+				data->cfg_version[0], data->cfg_version[1],
+				data->cfg_version[2],
+				cfg_ver[0], cfg_ver[1], cfg_ver[2]);
+
+			memcpy(data->cfg_version, cfg_ver, MXT_CFG_VERSION_LEN);
+		}
+	} else {
+		dev_info(&data->client->dev,
+			"No cfg data defined, skipping check reg init\n");
+	}
+
+	error = mxt_save_objects(data);
+	if (error)
+		return error;
+
+	return 0;
+}
+
+
+
 static int mxt_initialize(struct mxt_data *data)
 {
 	struct i2c_client *client = data->client;
 	struct mxt_info *info = &data->info;
 	int error;
 	u8 val;
-	const u8 *cfg_ver;
 
 	error = mxt_get_info(data);
 	if (error) {
@@ -1465,46 +1508,9 @@
 	if (error)
 		goto free_object_table;
 
-	/* Get config data from platform data */
-	error = mxt_get_config(data);
-	if (error)
-		dev_dbg(&client->dev, "Config info not found.\n");
-
-	/* Check register init values */
-	if (data->config_info && data->config_info->config) {
-		if (data->update_cfg) {
-			error = mxt_check_reg_init(data);
-			if (error) {
-				dev_err(&client->dev,
-					"Failed to check reg init value\n");
-				goto free_object_table;
-			}
-
-			error = mxt_backup_nv(data);
-			if (error) {
-				dev_err(&client->dev, "Failed to back up NV\n");
-				goto free_object_table;
-			}
-
-			cfg_ver = data->config_info->config +
-							data->cfg_version_idx;
-			dev_info(&client->dev,
-				"Config updated from %d.%d.%d to %d.%d.%d\n",
-				data->cfg_version[0], data->cfg_version[1],
-				data->cfg_version[2],
-				cfg_ver[0], cfg_ver[1], cfg_ver[2]);
-
-			memcpy(data->cfg_version, cfg_ver, MXT_CFG_VERSION_LEN);
-		}
-	} else {
-		dev_info(&client->dev,
-			"No cfg data defined, skipping check reg init\n");
-	}
-
-	error = mxt_save_objects(data);
+	error = mxt_update_cfg(data);
 	if (error)
 		goto free_object_table;
-
 	/* Update matrix size at info struct */
 	error = mxt_read_reg(client, MXT_MATRIX_X_SIZE, &val);
 	if (error)
@@ -1732,6 +1738,30 @@
 	return fw_name;
 }
 
+static ssize_t mxt_force_cfg_update_store(struct device *dev,
+					struct device_attribute *attr,
+					const char *buf, size_t count)
+{
+	struct mxt_data *data = dev_get_drvdata(dev);
+	int flag = buf[0]-'0';
+	int error;
+	data->no_force_update = !flag;
+
+	if (data->state == APPMODE) {
+		disable_irq(data->irq);
+		error = mxt_update_cfg(data);
+		enable_irq(data->irq);
+		if (error)
+			return error;
+	} else {
+		dev_err(dev,
+		"Not in APPMODE, Unable to force cfg update\n");
+		return -EINVAL;
+	}
+
+	return count;
+}
+
 static ssize_t mxt_update_fw_store(struct device *dev,
 					struct device_attribute *attr,
 					const char *buf, size_t count)
@@ -1742,7 +1772,7 @@
 	u8 bootldr_id;
 	u8 cfg_version[MXT_CFG_VERSION_LEN] = {0};
 
-
+	data->no_force_update = false;
 	/* If fw_name is set, then the existing firmware has an upgrade */
 	if (!data->fw_name) {
 		/*
@@ -1824,10 +1854,12 @@
 
 static DEVICE_ATTR(object, 0444, mxt_object_show, NULL);
 static DEVICE_ATTR(update_fw, 0664, NULL, mxt_update_fw_store);
+static DEVICE_ATTR(force_cfg_update, 0664, NULL, mxt_force_cfg_update_store);
 
 static struct attribute *mxt_attrs[] = {
 	&dev_attr_object.attr,
 	&dev_attr_update_fw.attr,
+	&dev_attr_force_cfg_update.attr,
 	NULL
 };
 
@@ -2433,6 +2465,10 @@
 	pdata->i2c_pull_up = of_property_read_bool(np, "atmel,i2c-pull-up");
 	pdata->digital_pwr_regulator = of_property_read_bool(np,
 						"atmel,dig-reg-support");
+
+	pdata->no_force_update = of_property_read_bool(np,
+						"atmel,no-force-update");
+
 	/* reset, irq gpio info */
 	pdata->reset_gpio = of_get_named_gpio_flags(np, "atmel,reset-gpio",
 				0, &pdata->reset_gpio_flags);
@@ -2583,6 +2619,7 @@
 	data->client = client;
 	data->input_dev = input_dev;
 	data->pdata = pdata;
+	data->no_force_update = pdata->no_force_update;
 
 	__set_bit(EV_ABS, input_dev->evbit);
 	__set_bit(EV_KEY, input_dev->evbit);
diff --git a/drivers/input/touchscreen/cyttsp-i2c-qc.c b/drivers/input/touchscreen/cyttsp-i2c-qc.c
index e54a24a..f96348e 100644
--- a/drivers/input/touchscreen/cyttsp-i2c-qc.c
+++ b/drivers/input/touchscreen/cyttsp-i2c-qc.c
@@ -3,7 +3,6 @@
  * drivers/input/touchscreen/cyttsp-i2c.c
  *
  * Copyright (C) 2009, 2010 Cypress Semiconductor, Inc.
- * Copyright (c) 2012, Code Aurora Forum. 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
@@ -43,7 +42,6 @@
 #include <linux/pm_runtime.h>
 #include <linux/firmware.h>
 #include <linux/mutex.h>
-#include <linux/completion.h>
 #include <linux/regulator/consumer.h>
 #ifdef CONFIG_HAS_EARLYSUSPEND
 #include <linux/earlysuspend.h>
@@ -59,30 +57,6 @@
 #define FW_FNAME_LEN 40
 #define TTSP_BUFF_SIZE 50
 
-enum cyttsp_powerstate {
-	CY_IDLE = 0,	/* IC cannot be reached */
-	CY_READY,		/* pre-operational; ready to go to ACTIVE */
-	CY_ACTIVE,	/* app is running, IC is scanning */
-	CY_LOW_PWR,	/* not currently used  */
-	CY_SLEEP,		/* app is running, IC is idle */
-	CY_BL,		/* bootloader is running */
-	CY_LDR,		/* loader is running */
-	CY_SYSINFO,	/* switching to sysinfo mode */
-	CY_INVALID,	/* always last in the list */
-};
-static char *cyttsp_powerstate_string[] = {
-	/* Order must match enum cyttsp_powerstate above */
-	"IDLE",
-	"READY",
-	"ACTIVE",
-	"LOW_PWR",
-	"SLEEP",
-	"BOOTLOADER",
-	"LOADER",
-	"SYSINFO",
-	"INVALID",
-};
-
 /* CY TTSP I2C Driver private data */
 struct cyttsp {
 	struct i2c_client *client;
@@ -92,15 +66,12 @@
 	char phys[32];
 	struct cyttsp_platform_data *platform_data;
 	u8 num_prv_st_tch;
-	u8 power_settings[3];
 	u16 fw_start_addr;
-	enum cyttsp_powerstate power_state;
 	u16 act_trk[CY_NUM_TRK_ID];
 	u16 prv_st_tch[CY_NUM_ST_TCH_ID];
 	u16 prv_mt_tch[CY_NUM_MT_TCH_ID];
 	u16 prv_mt_pos[CY_NUM_TRK_ID][2];
 	atomic_t irq_enabled;
-	struct completion si_int_running;
 	bool cyttsp_update_fw;
 	bool cyttsp_fwloader_mode;
 	bool is_suspended;
@@ -188,67 +159,6 @@
 				atomic_read(&ts->irq_enabled));
 }
 
-static int cyttsp_hndshk(struct cyttsp *ts, u8 hst_mode)
-{
-	int retval = 0;
-	u8 mode = 0;
-
-	mode = hst_mode & CY_HNDSHK_BIT ?
-		hst_mode & ~CY_HNDSHK_BIT :
-		hst_mode | CY_HNDSHK_BIT;
-
-	retval = i2c_smbus_write_i2c_block_data(ts->client,
-			CY_REG_BASE, sizeof(mode), &mode);
-
-	if (retval < 0) {
-		pr_err("%s: bus write fail on handshake r=%d\n",
-			__func__, retval);
-	}
-
-	return retval;
-}
-
-static void cyttsp_change_state(struct cyttsp *ts,
-	enum cyttsp_powerstate new_state)
-{
-	ts->power_state = new_state;
-	pr_info("%s: %s\n", __func__,
-		(ts->power_state < CY_INVALID) ?
-		cyttsp_powerstate_string[ts->power_state] :
-		"INVALID");
-}
-
-static int cyttsp_wait_ready(struct cyttsp *ts, struct completion *complete,
-	u8 *cmd, size_t cmd_size, unsigned long timeout_ms)
-{
-	unsigned long timeout = 0;
-	unsigned long uretval = 0;
-	int retval = 0;
-
-	timeout = msecs_to_jiffies(timeout_ms);
-	INIT_COMPLETION(*complete);
-	if ((cmd != NULL) && (cmd_size != 0)) {
-		retval = i2c_smbus_write_i2c_block_data(ts->client,
-				CY_REG_BASE, cmd_size, cmd);
-		if (retval < 0) {
-			pr_err("%s: bus write fail switch mode r=%d\n",
-				__func__, retval);
-			cyttsp_change_state(ts, CY_IDLE);
-			goto _cyttsp_wait_ready_exit;
-		}
-	}
-	uretval = wait_for_completion_interruptible_timeout(complete, timeout);
-	if (uretval == 0) {
-		pr_err("%s: Switch Mode Timeout waiting " \
-			"for ready interrupt - try reading regs\n", __func__);
-		/* continue anyway */
-		retval = 0;
-	}
-
-_cyttsp_wait_ready_exit:
-	return retval;
-}
-
 static ssize_t cyttsp_irq_enable(struct device *dev,
 				struct device_attribute *attr,
 				const char *buf, size_t size)
@@ -420,77 +330,39 @@
 	} while (tries++ < 10 && (retval < 0));
 }
 
-static int cyttsp_set_sysinfo_mode(struct cyttsp *ts, u8 sleep)
-{
-	int retval;
-	u8 mode = CY_SYSINFO_MODE | sleep;
-
-	cyttsp_change_state(ts, CY_SYSINFO);
-
-	retval = cyttsp_wait_ready(ts, &ts->si_int_running,
-		&mode, sizeof(mode), CY_HALF_SEC_TMO_MS);
-
-	if (retval < 0) {
-		pr_err("%s: fail wait ready r=%d\n", __func__, retval);
-		goto cyttsp_set_sysinfo_mode_exit;
-	}
-
-	if (GET_HSTMODE(g_sysinfo_data.hst_mode) !=
-		GET_HSTMODE(CY_SYSINFO_MODE)) {
-		pr_err("%s: Fail enter Sysinfo mode hst_mode=0x%02X\n",
-			__func__, g_sysinfo_data.hst_mode);
-		retval = -EIO;
-	} else {
-		cyttsp_debug("%s: Enter Sysinfo mode hst_mode=0x%02X\n",
-			__func__, g_sysinfo_data.hst_mode);
-	}
-
-cyttsp_set_sysinfo_mode_exit:
-	return retval;
-}
-
-static int cyttsp_set_opmode(struct cyttsp *ts, u8 sleep)
+static void cyttsp_set_sysinfo_mode(struct cyttsp *ts)
 {
 	int retval, tries = 0;
-	u8 host_reg = CY_OP_MODE | sleep;
+	u8 host_reg = CY_SYSINFO_MODE;
 
-	cyttsp_change_state(ts, CY_ACTIVE);
+	do {
+		retval = i2c_smbus_write_i2c_block_data(ts->client,
+			CY_REG_BASE, sizeof(host_reg), &host_reg);
+		if (retval < 0)
+			msleep(20);
+	} while (tries++ < 10 && (retval < 0));
+
+	/* wait for TTSP Device to complete switch to SysInfo mode */
+	if (!(retval < 0)) {
+		retval = i2c_smbus_read_i2c_block_data(ts->client,
+				CY_REG_BASE,
+				sizeof(struct cyttsp_sysinfo_data_t),
+				(u8 *)&g_sysinfo_data);
+	} else
+		pr_err("%s: failed\n", __func__);
+}
+
+static void cyttsp_set_opmode(struct cyttsp *ts)
+{
+	int retval, tries = 0;
+	u8 host_reg = CY_OP_MODE;
+
 	do {
 		retval = i2c_smbus_write_i2c_block_data(ts->client,
 				CY_REG_BASE, sizeof(host_reg), &host_reg);
 		if (retval < 0)
 			msleep(20);
 	} while (tries++ < 10 && (retval < 0));
-
-	return retval;
-}
-
-static int cyttsp_set_lp_mode(struct cyttsp *ts)
-{
-	int retval = 0, tries = 0;
-
-	retval = cyttsp_set_sysinfo_mode(ts, CY_LOW_PWR_MODE);
-	if (retval < 0) {
-		pr_err("%s: failed to enter sysinfo mode, retval =%x\n",
-			__func__, retval);
-		goto exit_low_power_mode;
-	}
-	do {
-		retval = i2c_smbus_write_i2c_block_data(
-			ts->client,
-			CY_REG_ACT_INTRVL,
-			sizeof(ts->power_settings), ts->power_settings);
-		if (retval < 0)
-			msleep(20);
-	} while ((retval < 0) && (tries++ < 5));
-	if (retval < 0)
-		pr_err("%s: failed to write power_settings, retval =%x\n",
-			__func__, retval);
-	msleep(CY_DLY_SYSINFO);
-exit_low_power_mode:
-	cyttsp_set_opmode(ts, CY_LOW_PWR_MODE);
-	return retval;
-
 }
 
 static int str2uc(char *str, u8 *val)
@@ -869,20 +741,21 @@
 			pr_info("%s: firmware upgrade success\n", __func__);
 	}
 
-	/* enable interrupts */
-	if (ts->client->irq == 0)
-		mod_timer(&ts->timer, jiffies + TOUCHSCREEN_TIMEOUT);
-	else
-		enable_irq(ts->client->irq);
-
 	/* enter bootloader idle mode */
 	cyttsp_soft_reset(ts);
 	/* exit bootloader mode */
 	cyttsp_exit_bl_mode(ts);
 	msleep(100);
-	/* set low power mode and enter application mode*/
-	if (ts->platform_data->use_sleep & CY_LOW_PWR_MODE)
-		cyttsp_set_lp_mode(ts);
+	/* set sysinfo details */
+	cyttsp_set_sysinfo_mode(ts);
+	/* enter application mode */
+	cyttsp_set_opmode(ts);
+
+	/* enable interrupts */
+	if (ts->client->irq == 0)
+		mod_timer(&ts->timer, jiffies + TOUCHSCREEN_TIMEOUT);
+	else
+		enable_irq(ts->client->irq);
 }
 
 static void cyttspfw_upgrade_start(struct cyttsp *ts, const u8 *data,
@@ -1086,14 +959,15 @@
 
 	/* compare own irq counter with the device irq counter */
 	if (ts->client->irq) {
+		u8 host_reg;
 		u8 cur_cnt;
 		if (ts->platform_data->use_hndshk) {
-			retval = cyttsp_hndshk(ts, g_xy_data.hst_mode);
-			if (retval < 0) {
-				pr_err("%s: Fail write handshake r=%d\n",
-					__func__, retval);
-				retval = 0;
-			}
+
+			host_reg = g_xy_data.hst_mode & CY_HNDSHK_BIT ?
+				g_xy_data.hst_mode & ~CY_HNDSHK_BIT :
+				g_xy_data.hst_mode | CY_HNDSHK_BIT;
+			retval = i2c_smbus_write_i2c_block_data(ts->client,
+				CY_REG_BASE, sizeof(host_reg), &host_reg);
 		}
 		cur_cnt = g_xy_data.tt_undef[CY_IRQ_CNT_REG];
 		irq_cnt_total++;
@@ -1158,9 +1032,6 @@
 				tries++ < 100);
 			cyttsp_putbl(ts, 2, true, false, false);
 		}
-		if (ts->platform_data->use_sleep & CY_LOW_PWR_MODE)
-			cyttsp_set_lp_mode(ts);
-
 		goto exit_xy_handler;
 	} else {
 		cur_tch = GET_NUM_TOUCHES(g_xy_data.tt_stat);
@@ -2001,40 +1872,10 @@
 static irqreturn_t cyttsp_irq(int irq, void *handle)
 {
 	struct cyttsp *ts = (struct cyttsp *) handle;
-	int retval = 0;
 
 	cyttsp_xdebug("%s: Got IRQ\n", CY_I2C_NAME);
-	switch (ts->power_state) {
-	case CY_SYSINFO:
-		retval = i2c_smbus_read_i2c_block_data(ts->client,
-			CY_REG_BASE,
-			sizeof(struct cyttsp_sysinfo_data_t),
-			(u8 *)&g_sysinfo_data);
-		if (retval < 0) {
-			pr_err("%s: Fail read status and version regs r=%d\n",
-				__func__, retval);
-			goto cyttsp_irq_sysinfo_exit;
-		}
-		if (ts->platform_data->use_hndshk) {
-			retval = cyttsp_hndshk(ts, g_sysinfo_data.hst_mode);
-			if (retval < 0) {
-				pr_err("%s: Fail write handshake r=%d\n",
-					__func__, retval);
-				retval = 0;
-			}
-		}
-		udelay(100);	/* irq pulse: sysinfo mode switch=50us */
-		complete(&ts->si_int_running);
-cyttsp_irq_sysinfo_exit:
-		break;
-	case CY_ACTIVE:
-		cyttsp_xy_handler(ts);
-		break;
-	default:
-		pr_err("%s: Unexpected power state with interrupt ps=%d\n",
-			__func__, ts->power_state);
-		break;
-	}
+
+	cyttsp_xy_handler(ts);
 
 	return IRQ_HANDLED;
 }
@@ -2400,6 +2241,106 @@
 	}
 
 bypass:
+	/* switch to System Information mode to read versions
+	 * and set interval registers */
+	if (!(retval < CY_OK)) {
+		cyttsp_debug("switch to sysinfo mode\n");
+		host_reg = CY_SYSINFO_MODE;
+		retval = i2c_smbus_write_i2c_block_data(ts->client,
+			CY_REG_BASE, sizeof(host_reg), &host_reg);
+		/* wait for TTSP Device to complete switch to SysInfo mode */
+		msleep(100);
+		if (!(retval < CY_OK)) {
+			retval = i2c_smbus_read_i2c_block_data(ts->client,
+				CY_REG_BASE,
+				sizeof(struct cyttsp_sysinfo_data_t),
+				(u8 *)&g_sysinfo_data);
+			cyttsp_debug("SI2: hst_mode=0x%02X mfg_cmd=0x%02X"\
+				"mfg_stat=0x%02X\n",
+				g_sysinfo_data.hst_mode,
+				g_sysinfo_data.mfg_cmd,
+				g_sysinfo_data.mfg_stat);
+			cyttsp_debug("SI2: bl_ver=0x%02X%02X\n",
+				g_sysinfo_data.bl_verh,
+				g_sysinfo_data.bl_verl);
+			pr_debug("SI2: sysinfo act_int=0x%02X tch_tmout=0x%02X lp_int=0x%02X\n",
+				g_sysinfo_data.act_intrvl,
+				g_sysinfo_data.tch_tmout,
+				g_sysinfo_data.lp_intrvl);
+			pr_info("SI%d: tver=%02X%02X a_id=%02X%02X aver=%02X%02X\n",
+				102,
+				g_sysinfo_data.tts_verh,
+				g_sysinfo_data.tts_verl,
+				g_sysinfo_data.app_idh,
+				g_sysinfo_data.app_idl,
+				g_sysinfo_data.app_verh,
+				g_sysinfo_data.app_verl);
+			cyttsp_info("SI%d: c_id=%02X%02X%02X\n",
+				103,
+				g_sysinfo_data.cid[0],
+				g_sysinfo_data.cid[1],
+				g_sysinfo_data.cid[2]);
+			if (!(retval < CY_OK) &&
+				(CY_DIFF(ts->platform_data->act_intrvl,
+					CY_ACT_INTRVL_DFLT)  ||
+				CY_DIFF(ts->platform_data->tch_tmout,
+					CY_TCH_TMOUT_DFLT) ||
+				CY_DIFF(ts->platform_data->lp_intrvl,
+					CY_LP_INTRVL_DFLT))) {
+				if (!(retval < CY_OK)) {
+					u8 intrvl_ray[sizeof(\
+						ts->platform_data->act_intrvl) +
+						sizeof(\
+						ts->platform_data->tch_tmout) +
+						sizeof(\
+						ts->platform_data->lp_intrvl)];
+					u8 i = 0;
+
+					intrvl_ray[i++] =
+						ts->platform_data->act_intrvl;
+					intrvl_ray[i++] =
+						ts->platform_data->tch_tmout;
+					intrvl_ray[i++] =
+						ts->platform_data->lp_intrvl;
+
+					pr_debug("SI2: platinfo act_intrvl=0x%02X tch_tmout=0x%02X lp_intrvl=0x%02X\n",
+						ts->platform_data->act_intrvl,
+						ts->platform_data->tch_tmout,
+						ts->platform_data->lp_intrvl);
+					/* set intrvl registers */
+					retval = i2c_smbus_write_i2c_block_data(
+						ts->client,
+						CY_REG_ACT_INTRVL,
+						sizeof(intrvl_ray), intrvl_ray);
+					msleep(CY_DLY_SYSINFO);
+				}
+			}
+		}
+		/* switch back to Operational mode */
+		cyttsp_debug("switch back to operational mode\n");
+		if (!(retval < CY_OK)) {
+			host_reg = CY_OP_MODE/* + CY_LOW_PWR_MODE*/;
+			retval = i2c_smbus_write_i2c_block_data(ts->client,
+				CY_REG_BASE,
+				sizeof(host_reg), &host_reg);
+			/* wait for TTSP Device to complete
+			 * switch to Operational mode */
+			msleep(100);
+		}
+	}
+	/* init gesture setup;
+	 * this is required even if not using gestures
+	 * in order to set the active distance */
+	if (!(retval < CY_OK)) {
+		u8 gesture_setup;
+		cyttsp_debug("init gesture setup\n");
+		gesture_setup = ts->platform_data->gest_set;
+		retval = i2c_smbus_write_i2c_block_data(ts->client,
+			CY_REG_GEST_SET,
+			sizeof(gesture_setup), &gesture_setup);
+		msleep(CY_DLY_DFLT);
+	}
+
 	if (!(retval < CY_OK))
 		ts->platform_data->power_state = CY_ACTIVE_STATE;
 	else
@@ -2498,98 +2439,6 @@
 	return rc;
 }
 
-static void sysinfo_debug_msg(struct cyttsp *ts)
-{
-	cyttsp_debug("SI2: hst_mode=0x%02X mfg_cmd=0x%02X " \
-		"mfg_stat=0x%02X\n", \
-		g_sysinfo_data.hst_mode, \
-		g_sysinfo_data.mfg_cmd, \
-		g_sysinfo_data.mfg_stat);
-	cyttsp_debug("SI2: bl_ver=0x%02X%02X\n", \
-		g_sysinfo_data.bl_verh, \
-		g_sysinfo_data.bl_verl);
-	cyttsp_debug("SI2: sysinfo act_int=0x%02X " \
-		"tch_tmout=0x%02X lp_int=0x%02X\n", \
-		g_sysinfo_data.act_intrvl, \
-		g_sysinfo_data.tch_tmout, \
-		g_sysinfo_data.lp_intrvl);
-	cyttsp_info("SI%d: tver=%02X%02X a_id=%02X%02X " \
-		"aver=%02X%02X\n", \
-		102, \
-		g_sysinfo_data.tts_verh, \
-		g_sysinfo_data.tts_verl, \
-		g_sysinfo_data.app_idh, \
-		g_sysinfo_data.app_idl, \
-		g_sysinfo_data.app_verh, \
-		g_sysinfo_data.app_verl);
-	cyttsp_info("SI%d: c_id=%02X%02X%02X\n", \
-		103, \
-		g_sysinfo_data.cid[0], \
-		g_sysinfo_data.cid[1], \
-		g_sysinfo_data.cid[2]);
-	cyttsp_debug("SI2: platinfo " \
-		"act_intrvl=0x%02X tch_tmout=0x%02X " \
-		"lp_intrvl=0x%02X\n", \
-		ts->platform_data->act_intrvl, \
-		ts->platform_data->tch_tmout, \
-		ts->platform_data->lp_intrvl);
-}
-
-static int set_bypass_modes(struct cyttsp *ts)
-{
-	int retval = 0, tries = 0;
-
-	/* switch to System Information mode to read versions
-	 * and set interval registers */
-	if (ts->platform_data->use_sleep & CY_LOW_PWR_MODE)
-		retval = cyttsp_set_sysinfo_mode(ts, CY_LOW_PWR_MODE);
-	else
-		retval = cyttsp_set_opmode(ts, CY_OP_MODE);
-
-	if (!(retval < CY_OK)) {
-		retval = i2c_smbus_read_i2c_block_data(ts->client,
-			CY_REG_BASE,
-			sizeof(struct cyttsp_sysinfo_data_t),
-			(u8 *)&g_sysinfo_data);
-		sysinfo_debug_msg(ts);
-		/* set power settings registers */
-		do {
-			retval = i2c_smbus_write_i2c_block_data(ts->client,
-				CY_REG_ACT_INTRVL, sizeof(ts->power_settings),
-				ts->power_settings);
-			if (retval < 0)
-				msleep(20);
-		} while ((retval < 0) && (tries++ < 5));
-		if (retval < 0)
-			pr_err("%s: failed to write  power_settings, " \
-				"retval =%x\n", __func__, retval);
-	}
-	/* switch back to Operational mode */
-	cyttsp_debug("switch back to operational mode\n");
-	if (!(retval < CY_OK)) {
-		if (ts->platform_data->use_sleep & CY_LOW_PWR_MODE)
-			cyttsp_set_opmode(ts, CY_LOW_PWR_MODE);
-		else
-			cyttsp_set_opmode(ts, CY_OP_MODE);
-		/* wait for TTSP Device to complete
-		 * switch to Operational mode */
-		msleep(100);
-	}
-	/* init gesture setup;
-	 * this is required even if not using gestures
-	 * in order to set the active distance */
-	if (!(retval < CY_OK)) {
-		u8 gesture_setup;
-		cyttsp_debug("init gesture setup\n");
-		gesture_setup = ts->platform_data->gest_set;
-		retval = i2c_smbus_write_i2c_block_data(ts->client,
-			CY_REG_GEST_SET,
-			sizeof(gesture_setup), &gesture_setup);
-		msleep(CY_DLY_DFLT);
-	}
-	return retval;
-}
-
 /* cyttsp_initialize: Driver Initialization. This function takes
  * care of the following tasks:
  * 1. Create and register an input device with input layer
@@ -2627,21 +2476,6 @@
 	input_device->phys = ts->phys;
 	input_device->dev.parent = &client->dev;
 
-	ts->power_state = CY_ACTIVE;
-
-	if (ts->platform_data->act_intrvl)
-		ts->power_settings[0] = ts->platform_data->act_intrvl;
-	else
-		ts->power_settings[0] = CY_ACT_INTRVL_DFLT;
-	if (ts->platform_data->tch_tmout)
-		ts->power_settings[1] = ts->platform_data->tch_tmout;
-	else
-		ts->power_settings[1] = CY_TCH_TMOUT_DFLT;
-	if (ts->platform_data->lp_intrvl)
-		ts->power_settings[2] = ts->platform_data->lp_intrvl;
-	else
-		ts->power_settings[2] = CY_LP_INTRVL_DFLT;
-
 	/* init the touch structures */
 	ts->num_prv_st_tch = CY_NTCH;
 	for (id = 0; id < CY_NUM_TRK_ID; id++) {
@@ -2859,7 +2693,6 @@
 		goto error_rm_dev_file_fupdate_fw;
 	}
 
-	set_bypass_modes(ts);
 	cyttsp_info("%s: Successful registration\n", CY_I2C_NAME);
 
 	goto success;
@@ -2947,8 +2780,6 @@
 
 		i2c_set_clientdata(client, ts);
 
-		init_completion(&ts->si_int_running);
-
 		error = cyttsp_initialize(client, ts);
 		if (error) {
 			cyttsp_xdebug1("err cyttsp_initialize\n");
@@ -2971,8 +2802,6 @@
 #endif /* CONFIG_HAS_EARLYSUSPEND */
 	device_init_wakeup(&client->dev, ts->platform_data->wakeup);
 	mutex_init(&ts->mutex);
-	if (ts->platform_data->use_sleep & CY_LOW_PWR_MODE)
-		retval = cyttsp_set_lp_mode(ts);
 
 	cyttsp_info("Start Probe %s\n", \
 		(retval < CY_OK) ? "FAIL" : "PASS");
@@ -3109,9 +2938,6 @@
 	cyttsp_debug("Wake Up %s\n", \
 		(retval < CY_OK) ? "FAIL" : "PASS");
 
-	if (ts->platform_data->use_sleep & CY_LOW_PWR_MODE)
-		retval = cyttsp_set_lp_mode(ts);
-
 	return retval;
 }
 
diff --git a/drivers/iommu/Makefile b/drivers/iommu/Makefile
index 066bc3e..11d50c1 100644
--- a/drivers/iommu/Makefile
+++ b/drivers/iommu/Makefile
@@ -1,7 +1,7 @@
 obj-$(CONFIG_IOMMU_API) += iommu.o
 obj-$(CONFIG_MSM_IOMMU) += msm_iommu.o msm_iommu_dev.o
 ifdef CONFIG_OF
-obj-$(CONFIG_MSM_IOMMU) += msm_iommu-v2.o msm_iommu_dev-v2.o msm_iommu_pagetable.o
+obj-$(CONFIG_MSM_IOMMU) += msm_iommu-v2.o msm_iommu_dev-v2.o msm_iommu_pagetable.o msm_iommu_sec.o
 endif
 obj-$(CONFIG_AMD_IOMMU) += amd_iommu.o amd_iommu_init.o
 obj-$(CONFIG_AMD_IOMMU_V2) += amd_iommu_v2.o
diff --git a/drivers/iommu/msm_iommu-v2.c b/drivers/iommu/msm_iommu-v2.c
index f49d009..9d88fdd 100644
--- a/drivers/iommu/msm_iommu-v2.c
+++ b/drivers/iommu/msm_iommu-v2.c
@@ -148,9 +148,9 @@
 	return ret;
 }
 
-static void __reset_iommu(void __iomem *base, int smt_size)
+static void __reset_iommu(void __iomem *base)
 {
-	int i;
+	int i, smt_size;
 
 	SET_ACR(base, 0);
 	SET_NSACR(base, 0);
@@ -162,6 +162,7 @@
 	SET_PMCR(base, 0);
 	SET_SCR1(base, 0);
 	SET_SSDR_N(base, 0, 0);
+	smt_size = GET_IDR0_NUMSMRG(base);
 
 	for (i = 0; i < smt_size; i++)
 		SET_SMR_VALID(base, i, 0);
@@ -169,11 +170,11 @@
 	mb();
 }
 
-static void __program_iommu(void __iomem *base, int smt_size,
+static void __program_iommu(void __iomem *base,
 			    struct msm_iommu_bfb_settings *bfb_settings)
 {
 	int i;
-	__reset_iommu(base, smt_size);
+	__reset_iommu(base);
 
 	SET_CR0_SMCFCFG(base, 1);
 	SET_CR0_USFCFG(base, 1);
@@ -208,9 +209,10 @@
 	mb();
 }
 
-static void __release_smg(void __iomem *base, int ctx, int smt_size)
+static void __release_smg(void __iomem *base, int ctx)
 {
-	int i;
+	int i, smt_size;
+	smt_size = GET_IDR0_NUMSMRG(base);
 
 	/* Invalidate any SMGs associated with this context */
 	for (i = 0; i < smt_size; i++)
@@ -221,14 +223,14 @@
 
 static void __program_context(void __iomem *base, int ctx, int ncb,
 				phys_addr_t pgtable, int redirect,
-				u32 *sids, int len, int smt_size)
+				u32 *sids, int len)
 {
 	unsigned int prrr, nmrr;
 	unsigned int pn;
-	int i, j, found, num = 0;
+	int i, j, found, num = 0, smt_size;
 
 	__reset_context(base, ctx);
-
+	smt_size = GET_IDR0_NUMSMRG(base);
 	pn = pgtable >> CB_TTBR0_ADDR_SHIFT;
 	SET_TTBCR(base, ctx, 0);
 	SET_CB_TTBR0_ADDR(base, ctx, pn);
@@ -435,13 +437,12 @@
 	}
 
 	if (!msm_iommu_ctx_attached(dev->parent))
-		__program_iommu(iommu_drvdata->base, iommu_drvdata->nsmr,
+		__program_iommu(iommu_drvdata->base,
 				iommu_drvdata->bfb_settings);
 
 	__program_context(iommu_drvdata->base, ctx_drvdata->num,
 		iommu_drvdata->ncb, __pa(priv->pt.fl_table),
-		priv->pt.redirect, ctx_drvdata->sids, ctx_drvdata->nsid,
-		iommu_drvdata->nsmr);
+		priv->pt.redirect, ctx_drvdata->sids, ctx_drvdata->nsid);
 	__disable_clocks(iommu_drvdata);
 
 	list_add(&(ctx_drvdata->attached_elm), &priv->list_attached);
@@ -478,8 +479,7 @@
 		GET_CB_CONTEXTIDR_ASID(iommu_drvdata->base, ctx_drvdata->num));
 
 	__reset_context(iommu_drvdata->base, ctx_drvdata->num);
-	__release_smg(iommu_drvdata->base, ctx_drvdata->num,
-		      iommu_drvdata->nsmr);
+	__release_smg(iommu_drvdata->base, ctx_drvdata->num);
 
 	__disable_clocks(iommu_drvdata);
 
diff --git a/drivers/iommu/msm_iommu.c b/drivers/iommu/msm_iommu.c
index e17e1f8..bf173b3 100644
--- a/drivers/iommu/msm_iommu.c
+++ b/drivers/iommu/msm_iommu.c
@@ -48,6 +48,9 @@
 #define MSM_IOMMU_ATTR_CACHED_WB_NWA	0x2
 #define MSM_IOMMU_ATTR_CACHED_WT	0x3
 
+struct bus_type msm_iommu_sec_bus_type = {
+	.name = "msm_iommu_sec_bus",
+};
 
 static inline void clean_pte(unsigned long *start, unsigned long *end,
 			     int redirect)
@@ -467,6 +470,92 @@
 	return pgprot;
 }
 
+static unsigned long *make_second_level(struct msm_priv *priv,
+					unsigned long *fl_pte)
+{
+	unsigned long *sl;
+	sl = (unsigned long *) __get_free_pages(GFP_KERNEL,
+			get_order(SZ_4K));
+
+	if (!sl) {
+		pr_debug("Could not allocate second level table\n");
+		goto fail;
+	}
+	memset(sl, 0, SZ_4K);
+	clean_pte(sl, sl + NUM_SL_PTE, priv->redirect);
+
+	*fl_pte = ((((int)__pa(sl)) & FL_BASE_MASK) | \
+			FL_TYPE_TABLE);
+
+	clean_pte(fl_pte, fl_pte + 1, priv->redirect);
+fail:
+	return sl;
+}
+
+static int sl_4k(unsigned long *sl_pte, phys_addr_t pa, unsigned int pgprot)
+{
+	int ret = 0;
+
+	if (*sl_pte) {
+		ret = -EBUSY;
+		goto fail;
+	}
+
+	*sl_pte = (pa & SL_BASE_MASK_SMALL) | SL_NG | SL_SHARED
+		| SL_TYPE_SMALL | pgprot;
+fail:
+	return ret;
+}
+
+static int sl_64k(unsigned long *sl_pte, phys_addr_t pa, unsigned int pgprot)
+{
+	int ret = 0;
+
+	int i;
+
+	for (i = 0; i < 16; i++)
+		if (*(sl_pte+i)) {
+			ret = -EBUSY;
+			goto fail;
+		}
+
+	for (i = 0; i < 16; i++)
+		*(sl_pte+i) = (pa & SL_BASE_MASK_LARGE) | SL_NG
+				| SL_SHARED | SL_TYPE_LARGE | pgprot;
+
+fail:
+	return ret;
+}
+
+
+static inline int fl_1m(unsigned long *fl_pte, phys_addr_t pa, int pgprot)
+{
+	if (*fl_pte)
+		return -EBUSY;
+
+	*fl_pte = (pa & 0xFFF00000) | FL_NG | FL_TYPE_SECT | FL_SHARED
+		| pgprot;
+
+	return 0;
+}
+
+
+static inline int fl_16m(unsigned long *fl_pte, phys_addr_t pa, int pgprot)
+{
+	int i;
+	int ret = 0;
+	for (i = 0; i < 16; i++)
+		if (*(fl_pte+i)) {
+			ret = -EBUSY;
+			goto fail;
+		}
+	for (i = 0; i < 16; i++)
+		*(fl_pte+i) = (pa & 0xFF000000) | FL_SUPERSECTION
+			| FL_TYPE_SECT | FL_SHARED | FL_NG | pgprot;
+fail:
+	return ret;
+}
+
 static int msm_iommu_map(struct iommu_domain *domain, unsigned long va,
 			 phys_addr_t pa, size_t len, int prot)
 {
@@ -514,28 +603,16 @@
 	fl_pte = fl_table + fl_offset;	/* int pointers, 4 bytes */
 
 	if (len == SZ_16M) {
-		int i = 0;
-
-		for (i = 0; i < 16; i++)
-			if (*(fl_pte+i)) {
-				ret = -EBUSY;
-				goto fail;
-			}
-
-		for (i = 0; i < 16; i++)
-			*(fl_pte+i) = (pa & 0xFF000000) | FL_SUPERSECTION
-				  | FL_TYPE_SECT | FL_SHARED | FL_NG | pgprot;
+		ret = fl_16m(fl_pte, pa, pgprot);
+		if (ret)
+			goto fail;
 		clean_pte(fl_pte, fl_pte + 16, priv->redirect);
 	}
 
 	if (len == SZ_1M) {
-		if (*fl_pte) {
-			ret = -EBUSY;
+		ret = fl_1m(fl_pte, pa, pgprot);
+		if (ret)
 			goto fail;
-		}
-
-		*fl_pte = (pa & 0xFFF00000) | FL_NG | FL_TYPE_SECT | FL_SHARED
-					    | pgprot;
 		clean_pte(fl_pte, fl_pte + 1, priv->redirect);
 	}
 
@@ -543,22 +620,10 @@
 	if (len == SZ_4K || len == SZ_64K) {
 
 		if (*fl_pte == 0) {
-			unsigned long *sl;
-			sl = (unsigned long *) __get_free_pages(GFP_KERNEL,
-							get_order(SZ_4K));
-
-			if (!sl) {
-				pr_debug("Could not allocate second level table\n");
+			if (make_second_level(priv, fl_pte) == NULL) {
 				ret = -ENOMEM;
 				goto fail;
 			}
-			memset(sl, 0, SZ_4K);
-			clean_pte(sl, sl + NUM_SL_PTE, priv->redirect);
-
-			*fl_pte = ((((int)__pa(sl)) & FL_BASE_MASK) | \
-						      FL_TYPE_TABLE);
-
-			clean_pte(fl_pte, fl_pte + 1, priv->redirect);
 		}
 
 		if (!(*fl_pte & FL_TYPE_TABLE)) {
@@ -572,29 +637,17 @@
 	sl_pte = sl_table + sl_offset;
 
 	if (len == SZ_4K) {
-		if (*sl_pte) {
-			ret = -EBUSY;
+		ret = sl_4k(sl_pte, pa, pgprot);
+		if (ret)
 			goto fail;
-		}
 
-		*sl_pte = (pa & SL_BASE_MASK_SMALL) | SL_NG | SL_SHARED
-						    | SL_TYPE_SMALL | pgprot;
 		clean_pte(sl_pte, sl_pte + 1, priv->redirect);
 	}
 
 	if (len == SZ_64K) {
-		int i;
-
-		for (i = 0; i < 16; i++)
-			if (*(sl_pte+i)) {
-				ret = -EBUSY;
-				goto fail;
-			}
-
-		for (i = 0; i < 16; i++)
-			*(sl_pte+i) = (pa & SL_BASE_MASK_LARGE) | SL_NG
-					  | SL_SHARED | SL_TYPE_LARGE | pgprot;
-
+		ret = sl_64k(sl_pte, pa, pgprot);
+		if (ret)
+			goto fail;
 		clean_pte(sl_pte, sl_pte + 16, priv->redirect);
 	}
 
@@ -712,22 +765,28 @@
 	return pa;
 }
 
+static inline int is_fully_aligned(unsigned int va, phys_addr_t pa, size_t len,
+				   int align)
+{
+	return  IS_ALIGNED(va, align) && IS_ALIGNED(pa, align)
+		&& (len >= align);
+}
+
 static int msm_iommu_map_range(struct iommu_domain *domain, unsigned int va,
 			       struct scatterlist *sg, unsigned int len,
 			       int prot)
 {
 	unsigned int pa;
 	unsigned int offset = 0;
-	unsigned int pgprot;
 	unsigned long *fl_table;
 	unsigned long *fl_pte;
 	unsigned long fl_offset;
-	unsigned long *sl_table;
+	unsigned long *sl_table = NULL;
 	unsigned long sl_offset, sl_start;
-	unsigned int chunk_offset = 0;
-	unsigned int chunk_pa;
+	unsigned int chunk_size, chunk_offset = 0;
 	int ret = 0;
 	struct msm_priv *priv;
+	unsigned int pgprot4k, pgprot64k, pgprot1m, pgprot16m;
 
 	mutex_lock(&msm_iommu_lock);
 
@@ -736,49 +795,78 @@
 	priv = domain->priv;
 	fl_table = priv->pgtable;
 
-	pgprot = __get_pgprot(prot, SZ_4K);
+	pgprot4k = __get_pgprot(prot, SZ_4K);
+	pgprot64k = __get_pgprot(prot, SZ_64K);
+	pgprot1m = __get_pgprot(prot, SZ_1M);
+	pgprot16m = __get_pgprot(prot, SZ_16M);
 
-	if (!pgprot) {
+	if (!pgprot4k || !pgprot64k || !pgprot1m || !pgprot16m) {
 		ret = -EINVAL;
 		goto fail;
 	}
 
 	fl_offset = FL_OFFSET(va);	/* Upper 12 bits */
 	fl_pte = fl_table + fl_offset;	/* int pointers, 4 bytes */
-
-	sl_table = (unsigned long *) __va(((*fl_pte) & FL_BASE_MASK));
-	sl_offset = SL_OFFSET(va);
-
-	chunk_pa = get_phys_addr(sg);
-	if (chunk_pa == 0) {
-		pr_debug("No dma address for sg %p\n", sg);
-		ret = -EINVAL;
-		goto fail;
-	}
+	pa = get_phys_addr(sg);
 
 	while (offset < len) {
-		/* Set up a 2nd level page table if one doesn't exist */
-		if (*fl_pte == 0) {
-			sl_table = (unsigned long *)
-				 __get_free_pages(GFP_KERNEL, get_order(SZ_4K));
+		chunk_size = SZ_4K;
 
-			if (!sl_table) {
-				pr_debug("Could not allocate second level table\n");
+		if (is_fully_aligned(va, pa, sg->length - chunk_offset,
+				     SZ_16M))
+			chunk_size = SZ_16M;
+		else if (is_fully_aligned(va, pa, sg->length - chunk_offset,
+					  SZ_1M))
+			chunk_size = SZ_1M;
+		/* 64k or 4k determined later */
+
+		/* for 1M and 16M, only first level entries are required */
+		if (chunk_size >= SZ_1M) {
+			if (chunk_size == SZ_16M) {
+				ret = fl_16m(fl_pte, pa, pgprot16m);
+				if (ret)
+					goto fail;
+				clean_pte(fl_pte, fl_pte + 16, priv->redirect);
+				fl_pte += 16;
+			} else if (chunk_size == SZ_1M) {
+				ret = fl_1m(fl_pte, pa, pgprot1m);
+				if (ret)
+					goto fail;
+				clean_pte(fl_pte, fl_pte + 1, priv->redirect);
+				fl_pte++;
+			}
+
+			offset += chunk_size;
+			chunk_offset += chunk_size;
+			va += chunk_size;
+			pa += chunk_size;
+
+			if (chunk_offset >= sg->length && offset < len) {
+				chunk_offset = 0;
+				sg = sg_next(sg);
+				pa = get_phys_addr(sg);
+				if (pa == 0) {
+					pr_debug("No dma address for sg %p\n",
+							sg);
+					ret = -EINVAL;
+					goto fail;
+				}
+			}
+			continue;
+		}
+		/* for 4K or 64K, make sure there is a second level table */
+		if (*fl_pte == 0) {
+			if (!make_second_level(priv, fl_pte)) {
 				ret = -ENOMEM;
 				goto fail;
 			}
-
-			memset(sl_table, 0, SZ_4K);
-			clean_pte(sl_table, sl_table + NUM_SL_PTE,
-				  priv->redirect);
-
-			*fl_pte = ((((int)__pa(sl_table)) & FL_BASE_MASK) |
-							    FL_TYPE_TABLE);
-			clean_pte(fl_pte, fl_pte + 1, priv->redirect);
-		} else
-			sl_table = (unsigned long *)
-					       __va(((*fl_pte) & FL_BASE_MASK));
-
+		}
+		if (!(*fl_pte & FL_TYPE_TABLE)) {
+			ret = -EBUSY;
+			goto fail;
+		}
+		sl_table = __va(((*fl_pte) & FL_BASE_MASK));
+		sl_offset = SL_OFFSET(va);
 		/* Keep track of initial position so we
 		 * don't clean more than we have to
 		 */
@@ -786,21 +874,39 @@
 
 		/* Build the 2nd level page table */
 		while (offset < len && sl_offset < NUM_SL_PTE) {
-			pa = chunk_pa + chunk_offset;
-			sl_table[sl_offset] = (pa & SL_BASE_MASK_SMALL) |
-				     pgprot | SL_NG | SL_SHARED | SL_TYPE_SMALL;
-			sl_offset++;
-			offset += SZ_4K;
 
-			chunk_offset += SZ_4K;
+			/* Map a large 64K page if the chunk is large enough and
+			 * the pa and va are aligned
+			 */
+
+			if (is_fully_aligned(va, pa, sg->length - chunk_offset,
+					     SZ_64K))
+				chunk_size = SZ_64K;
+			else
+				chunk_size = SZ_4K;
+
+			if (chunk_size == SZ_4K) {
+				sl_4k(&sl_table[sl_offset], pa, pgprot4k);
+				sl_offset++;
+			} else {
+				BUG_ON(sl_offset + 16 > NUM_SL_PTE);
+				sl_64k(&sl_table[sl_offset], pa, pgprot64k);
+				sl_offset += 16;
+			}
+
+
+			offset += chunk_size;
+			chunk_offset += chunk_size;
+			va += chunk_size;
+			pa += chunk_size;
 
 			if (chunk_offset >= sg->length && offset < len) {
 				chunk_offset = 0;
 				sg = sg_next(sg);
-				chunk_pa = get_phys_addr(sg);
-				if (chunk_pa == 0) {
+				pa = get_phys_addr(sg);
+				if (pa == 0) {
 					pr_debug("No dma address for sg %p\n",
-						 sg);
+							sg);
 					ret = -EINVAL;
 					goto fail;
 				}
@@ -808,7 +914,7 @@
 		}
 
 		clean_pte(sl_table + sl_start, sl_table + sl_offset,
-			  priv->redirect);
+				priv->redirect);
 
 		fl_pte++;
 		sl_offset = 0;
@@ -842,45 +948,53 @@
 	fl_offset = FL_OFFSET(va);	/* Upper 12 bits */
 	fl_pte = fl_table + fl_offset;	/* int pointers, 4 bytes */
 
-	sl_start = SL_OFFSET(va);
-
 	while (offset < len) {
-		sl_table = (unsigned long *) __va(((*fl_pte) & FL_BASE_MASK));
-		sl_end = ((len - offset) / SZ_4K) + sl_start;
+		if (*fl_pte & FL_TYPE_TABLE) {
+			sl_start = SL_OFFSET(va);
+			sl_table =  __va(((*fl_pte) & FL_BASE_MASK));
+			sl_end = ((len - offset) / SZ_4K) + sl_start;
 
-		if (sl_end > NUM_SL_PTE)
-			sl_end = NUM_SL_PTE;
+			if (sl_end > NUM_SL_PTE)
+				sl_end = NUM_SL_PTE;
 
-		memset(sl_table + sl_start, 0, (sl_end - sl_start) * 4);
-		clean_pte(sl_table + sl_start, sl_table + sl_end,
-			  priv->redirect);
+			memset(sl_table + sl_start, 0, (sl_end - sl_start) * 4);
+			clean_pte(sl_table + sl_start, sl_table + sl_end,
+					priv->redirect);
 
-		offset += (sl_end - sl_start) * SZ_4K;
+			offset += (sl_end - sl_start) * SZ_4K;
+			va += (sl_end - sl_start) * SZ_4K;
 
-		/* Unmap and free the 2nd level table if all mappings in it
-		 * were removed. This saves memory, but the table will need
-		 * to be re-allocated the next time someone tries to map these
-		 * VAs.
-		 */
-		used = 0;
+			/* Unmap and free the 2nd level table if all mappings
+			 * in it were removed. This saves memory, but the table
+			 * will need to be re-allocated the next time someone
+			 * tries to map these VAs.
+			 */
+			used = 0;
 
-		/* If we just unmapped the whole table, don't bother
-		 * seeing if there are still used entries left.
-		 */
-		if (sl_end - sl_start != NUM_SL_PTE)
-			for (i = 0; i < NUM_SL_PTE; i++)
-				if (sl_table[i]) {
-					used = 1;
-					break;
-				}
-		if (!used) {
-			free_page((unsigned long)sl_table);
+			/* If we just unmapped the whole table, don't bother
+			 * seeing if there are still used entries left.
+			 */
+			if (sl_end - sl_start != NUM_SL_PTE)
+				for (i = 0; i < NUM_SL_PTE; i++)
+					if (sl_table[i]) {
+						used = 1;
+						break;
+					}
+			if (!used) {
+				free_page((unsigned long)sl_table);
+				*fl_pte = 0;
+
+				clean_pte(fl_pte, fl_pte + 1, priv->redirect);
+			}
+
+			sl_start = 0;
+		} else {
 			*fl_pte = 0;
-
 			clean_pte(fl_pte, fl_pte + 1, priv->redirect);
+			va += SZ_1M;
+			offset += SZ_1M;
+			sl_start = 0;
 		}
-
-		sl_start = 0;
 		fl_pte++;
 	}
 
@@ -1011,7 +1125,12 @@
 		}
 
 		SET_FSR(base, num, fsr);
-		SET_RESUME(base, num, 1);
+		/*
+		 * Only resume fetches if the registered fault handler
+		 * allows it
+		 */
+		if (ret != -EBUSY)
+			SET_RESUME(base, num, 1);
 
 		ret = IRQ_HANDLED;
 	} else
diff --git a/drivers/iommu/msm_iommu_dev-v2.c b/drivers/iommu/msm_iommu_dev-v2.c
index 237d601..5a0b593 100644
--- a/drivers/iommu/msm_iommu_dev-v2.c
+++ b/drivers/iommu/msm_iommu_dev-v2.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012 Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2012 The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -21,7 +21,6 @@
 #include <linux/interrupt.h>
 #include <linux/err.h>
 #include <linux/slab.h>
-#include <linux/atomic.h>
 #include <linux/of.h>
 #include <linux/of_address.h>
 #include <linux/of_device.h>
@@ -94,28 +93,14 @@
 {
 	struct device_node *child;
 	int ret = 0;
-	u32 nsmr;
 
-	ret = device_move(&pdev->dev, &msm_iommu_root_dev->dev, DPM_ORDER_NONE);
-	if (ret)
-		goto fail;
+	drvdata->dev = &pdev->dev;
+	msm_iommu_add_drv(drvdata);
 
 	ret = msm_iommu_parse_bfb_settings(pdev, drvdata);
 	if (ret)
 		goto fail;
 
-	ret = of_property_read_u32(pdev->dev.of_node, "qcom,iommu-smt-size",
-				   &nsmr);
-	if (ret)
-		goto fail;
-
-	if (nsmr > MAX_NUM_SMR) {
-		pr_err("Invalid SMT size: %d\n", nsmr);
-		ret = -EINVAL;
-		goto fail;
-	}
-
-	drvdata->nsmr = nsmr;
 	for_each_child_of_node(pdev->dev.of_node, child) {
 		drvdata->ncb++;
 		if (!of_platform_device_create(child, NULL, &pdev->dev))
@@ -123,24 +108,20 @@
 	}
 
 	drvdata->name = dev_name(&pdev->dev);
+	drvdata->sec_id = -1;
+	of_property_read_u32(pdev->dev.of_node, "qcom,iommu-secure-id",
+				&drvdata->sec_id);
+	return 0;
 fail:
 	return ret;
 }
 
-static atomic_t msm_iommu_next_id = ATOMIC_INIT(-1);
-
 static int __devinit msm_iommu_probe(struct platform_device *pdev)
 {
 	struct msm_iommu_drvdata *drvdata;
 	struct resource *r;
 	int ret, needs_alt_core_clk;
 
-	if (msm_iommu_root_dev == pdev)
-		return 0;
-
-	if (pdev->id == -1)
-		pdev->id = atomic_inc_return(&msm_iommu_next_id) - 1;
-
 	drvdata = devm_kzalloc(&pdev->dev, sizeof(*drvdata), GFP_KERNEL);
 	if (!drvdata)
 		return -ENOMEM;
@@ -201,6 +182,7 @@
 
 	drv = platform_get_drvdata(pdev);
 	if (drv) {
+		msm_iommu_remove_drv(drv);
 		if (drv->clk)
 			clk_put(drv->clk);
 		clk_put(drv->pclk);
@@ -324,25 +306,8 @@
 
 static int __init msm_iommu_driver_init(void)
 {
-	struct device_node *node;
 	int ret;
 
-	node = of_find_compatible_node(NULL, NULL, "qcom,msm-smmu-v2");
-	if (!node)
-		return -ENODEV;
-
-	of_node_put(node);
-
-	msm_iommu_root_dev = platform_device_register_simple(
-						"msm_iommu", -1, 0, 0);
-	if (!msm_iommu_root_dev) {
-		pr_err("Failed to create root IOMMU device\n");
-		ret = -ENODEV;
-		goto error;
-	}
-
-	atomic_inc(&msm_iommu_next_id);
-
 	ret = platform_driver_register(&msm_iommu_driver);
 	if (ret != 0) {
 		pr_err("Failed to register IOMMU driver\n");
@@ -363,7 +328,6 @@
 {
 	platform_driver_unregister(&msm_iommu_ctx_driver);
 	platform_driver_unregister(&msm_iommu_driver);
-	platform_device_unregister(msm_iommu_root_dev);
 }
 
 subsys_initcall(msm_iommu_driver_init);
diff --git a/drivers/iommu/msm_iommu_dev.c b/drivers/iommu/msm_iommu_dev.c
index 967283d..d9eddcd 100644
--- a/drivers/iommu/msm_iommu_dev.c
+++ b/drivers/iommu/msm_iommu_dev.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2010-2012, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -21,62 +21,63 @@
 #include <linux/interrupt.h>
 #include <linux/err.h>
 #include <linux/slab.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
 
 #include <mach/iommu_hw-8xxx.h>
 #include <mach/iommu.h>
 
-struct iommu_ctx_iter_data {
-	/* input */
-	const char *name;
+static DEFINE_MUTEX(iommu_list_lock);
+static LIST_HEAD(iommu_list);
 
-	/* output */
-	struct device *dev;
-};
-
-struct platform_device *msm_iommu_root_dev;
-
-static int each_iommu_ctx(struct device *dev, void *data)
+void msm_iommu_add_drv(struct msm_iommu_drvdata *drv)
 {
-	struct iommu_ctx_iter_data *res = data;
+	mutex_lock(&iommu_list_lock);
+	list_add(&drv->list, &iommu_list);
+	mutex_unlock(&iommu_list_lock);
+}
+
+void msm_iommu_remove_drv(struct msm_iommu_drvdata *drv)
+{
+	mutex_lock(&iommu_list_lock);
+	list_del(&drv->list);
+	mutex_unlock(&iommu_list_lock);
+}
+
+static int find_iommu_ctx(struct device *dev, void *data)
+{
 	struct msm_iommu_ctx_drvdata *c;
 
 	c = dev_get_drvdata(dev);
-	if (!res || !c || !c->name || !res->name)
-		return -EINVAL;
+	if (!c || !c->name)
+		return 0;
 
-	if (!strcmp(res->name, c->name)) {
-		res->dev = dev;
-		return 1;
-	}
-	return 0;
+	return !strcmp(data, c->name);
 }
 
-static int each_iommu(struct device *dev, void *data)
+static struct device *find_context(struct device *dev, const char *name)
 {
-	return device_for_each_child(dev, data, each_iommu_ctx);
+	return device_find_child(dev, (void *)name, find_iommu_ctx);
 }
 
 struct device *msm_iommu_get_ctx(const char *ctx_name)
 {
-	struct iommu_ctx_iter_data r;
-	int found;
+	struct msm_iommu_drvdata *drv;
+	struct device *dev = NULL;
 
-	if (!msm_iommu_root_dev) {
-		pr_err("No root IOMMU device.\n");
-		goto fail;
+	mutex_lock(&iommu_list_lock);
+	list_for_each_entry(drv, &iommu_list, list) {
+		dev = find_context(drv->dev, ctx_name);
+		if (dev)
+			break;
 	}
+	mutex_unlock(&iommu_list_lock);
 
-	r.name = ctx_name;
-	found = device_for_each_child(&msm_iommu_root_dev->dev, &r, each_iommu);
-
-	if (found <= 0 || !dev_get_drvdata(r.dev)) {
+	if (!dev || !dev_get_drvdata(dev))
 		pr_err("Could not find context <%s>\n", ctx_name);
-		goto fail;
-	}
+	put_device(dev);
 
-	return r.dev;
-fail:
-	return NULL;
+	return dev;
 }
 EXPORT_SYMBOL(msm_iommu_get_ctx);
 
@@ -134,11 +135,6 @@
 	resource_size_t	len;
 	int ret, par;
 
-	if (pdev->id == -1) {
-		msm_iommu_root_dev = pdev;
-		return 0;
-	}
-
 	drvdata = kzalloc(sizeof(*drvdata), GFP_KERNEL);
 
 	if (!drvdata) {
@@ -227,6 +223,9 @@
 	drvdata->ncb = iommu_dev->ncb;
 	drvdata->ttbr_split = iommu_dev->ttbr_split;
 	drvdata->name = iommu_dev->name;
+	drvdata->dev = &pdev->dev;
+
+	msm_iommu_add_drv(drvdata);
 
 	pr_info("device %s mapped at %p, with %d ctx banks\n",
 		iommu_dev->name, regs_base, iommu_dev->ncb);
@@ -263,6 +262,7 @@
 
 	drv = platform_get_drvdata(pdev);
 	if (drv) {
+		msm_iommu_remove_drv(drv);
 		if (drv->clk)
 			clk_put(drv->clk);
 		clk_put(drv->pclk);
diff --git a/drivers/iommu/msm_iommu_sec.c b/drivers/iommu/msm_iommu_sec.c
new file mode 100644
index 0000000..a89c4a8
--- /dev/null
+++ b/drivers/iommu/msm_iommu_sec.c
@@ -0,0 +1,556 @@
+/* Copyright (c) 2012 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.
+ */
+
+#define pr_fmt(fmt)	KBUILD_MODNAME ": " fmt
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/errno.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/iommu.h>
+#include <linux/clk.h>
+#include <linux/scatterlist.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+
+#include <asm/sizes.h>
+
+#include <mach/iommu_hw-v2.h>
+#include <mach/iommu.h>
+#include <mach/scm.h>
+
+/* bitmap of the page sizes currently supported */
+#define MSM_IOMMU_PGSIZES	(SZ_4K | SZ_64K | SZ_1M | SZ_16M)
+
+#define IOMMU_SECURE_CFG	2
+#define IOMMU_SECURE_PTBL_SIZE  3
+#define IOMMU_SECURE_PTBL_INIT  4
+#define IOMMU_SECURE_MAP	6
+#define IOMMU_SECURE_UNMAP      7
+
+static DEFINE_MUTEX(msm_iommu_lock);
+
+struct msm_priv {
+	struct list_head list_attached;
+};
+
+struct msm_scm_paddr_list {
+	unsigned int list;
+	unsigned int list_size;
+	unsigned int size;
+};
+
+struct msm_scm_mapping_info {
+	unsigned int id;
+	unsigned int ctx_id;
+	unsigned int va;
+	unsigned int size;
+};
+
+struct msm_scm_map_req {
+	struct msm_scm_paddr_list plist;
+	struct msm_scm_mapping_info info;
+};
+
+static int msm_iommu_sec_ptbl_init(void)
+{
+	struct device_node *np;
+	struct msm_scm_ptbl_init {
+		unsigned int paddr;
+		unsigned int size;
+		unsigned int spare;
+	} pinit;
+	unsigned int *buf;
+	int psize[2] = {0};
+	unsigned int spare;
+	int ret, ptbl_ret;
+
+	for_each_compatible_node(np, NULL, "qcom,msm-smmu-v2")
+		if (of_find_property(np, "qcom,iommu-secure-id", NULL))
+			break;
+
+	if (!np)
+		return 0;
+
+	of_node_put(np);
+	ret = scm_call(SCM_SVC_CP, IOMMU_SECURE_PTBL_SIZE, &spare,
+			sizeof(spare), psize, sizeof(psize));
+	if (ret) {
+		pr_err("scm call IOMMU_SECURE_PTBL_SIZE failed\n");
+		goto fail;
+	}
+
+	if (psize[1]) {
+		pr_err("scm call IOMMU_SECURE_PTBL_SIZE failed\n");
+		goto fail;
+	}
+
+	buf = kmalloc(psize[0], GFP_KERNEL);
+	if (!buf) {
+		pr_err("%s: Failed to allocate %d bytes for PTBL\n",
+			__func__, psize[0]);
+		ret = -ENOMEM;
+		goto fail;
+	}
+
+	pinit.paddr = virt_to_phys(buf);
+	pinit.size = psize[0];
+
+	ret = scm_call(SCM_SVC_CP, IOMMU_SECURE_PTBL_INIT, &pinit,
+			sizeof(pinit), &ptbl_ret, sizeof(ptbl_ret));
+	if (ret) {
+		pr_err("scm call IOMMU_SECURE_PTBL_INIT failed\n");
+		goto fail_mem;
+	}
+	if (ptbl_ret) {
+		pr_err("scm call IOMMU_SECURE_PTBL_INIT extended ret fail\n");
+		goto fail_mem;
+	}
+
+	return 0;
+
+fail_mem:
+	kfree(buf);
+fail:
+	return ret;
+}
+
+static int msm_iommu_sec_program_iommu(int sec_id)
+{
+	struct msm_scm_sec_cfg {
+		unsigned int id;
+		unsigned int spare;
+	} cfg;
+	int ret, scm_ret;
+
+	cfg.id = sec_id;
+
+	ret = scm_call(SCM_SVC_CP, IOMMU_SECURE_CFG, &cfg, sizeof(cfg),
+			&scm_ret, sizeof(scm_ret));
+	if (ret || scm_ret) {
+		pr_err("scm call IOMMU_SECURE_CFG failed\n");
+		return ret ? ret : -EINVAL;
+	}
+
+	return ret;
+}
+
+static int msm_iommu_sec_ptbl_map(struct msm_iommu_drvdata *iommu_drvdata,
+			struct msm_iommu_ctx_drvdata *ctx_drvdata,
+			unsigned long va, phys_addr_t pa, size_t len)
+{
+	struct msm_scm_map_req map;
+	int ret = 0;
+
+	map.plist.list = virt_to_phys(&pa);
+	map.plist.list_size = 1;
+	map.plist.size = len;
+	map.info.id = iommu_drvdata->sec_id;
+	map.info.ctx_id = ctx_drvdata->num;
+	map.info.va = va;
+	map.info.size = len;
+
+	if (scm_call(SCM_SVC_CP, IOMMU_SECURE_MAP, &map, sizeof(map), &ret,
+								sizeof(ret)))
+		return -EINVAL;
+	if (ret)
+		return -EINVAL;
+
+	return 0;
+}
+
+static unsigned int get_phys_addr(struct scatterlist *sg)
+{
+	/*
+	 * Try sg_dma_address first so that we can
+	 * map carveout regions that do not have a
+	 * struct page associated with them.
+	 */
+	unsigned int pa = sg_dma_address(sg);
+	if (pa == 0)
+		pa = sg_phys(sg);
+	return pa;
+}
+
+static int msm_iommu_sec_ptbl_map_range(struct msm_iommu_drvdata *iommu_drvdata,
+			struct msm_iommu_ctx_drvdata *ctx_drvdata,
+			unsigned long va, struct scatterlist *sg, size_t len)
+{
+	struct scatterlist *sgiter;
+	struct msm_scm_map_req map;
+	unsigned int *pa_list = 0;
+	unsigned int pa, cnt;
+	unsigned int offset = 0, chunk_offset = 0;
+	int ret, scm_ret;
+
+	map.info.id = iommu_drvdata->sec_id;
+	map.info.ctx_id = ctx_drvdata->num;
+	map.info.va = va;
+	map.info.size = len;
+
+	if (sg->length == len) {
+		pa = get_phys_addr(sg);
+		map.plist.list = virt_to_phys(&pa);
+		map.plist.list_size = 1;
+		map.plist.size = len;
+	} else {
+		sgiter = sg;
+		cnt = sg->length / SZ_1M;
+		while ((sgiter = sg_next(sgiter)))
+			cnt += sgiter->length / SZ_1M;
+
+		pa_list = kmalloc(cnt * sizeof(*pa_list), GFP_KERNEL);
+		if (!pa_list)
+			return -ENOMEM;
+
+		sgiter = sg;
+		cnt = 0;
+		pa = get_phys_addr(sgiter);
+		while (offset < len) {
+			pa += chunk_offset;
+			pa_list[cnt] = pa;
+			chunk_offset += SZ_1M;
+			offset += SZ_1M;
+			cnt++;
+
+			if (chunk_offset >= sgiter->length && offset < len) {
+				chunk_offset = 0;
+				sgiter = sg_next(sgiter);
+				pa = get_phys_addr(sgiter);
+			}
+		}
+
+		map.plist.list = virt_to_phys(pa_list);
+		map.plist.list_size = cnt;
+		map.plist.size = SZ_1M;
+	}
+
+	ret = scm_call(SCM_SVC_CP, IOMMU_SECURE_MAP, &map, sizeof(map),
+			&scm_ret, sizeof(scm_ret));
+	kfree(pa_list);
+	return ret;
+}
+
+static int msm_iommu_sec_ptbl_unmap(struct msm_iommu_drvdata *iommu_drvdata,
+			struct msm_iommu_ctx_drvdata *ctx_drvdata,
+			unsigned long va, size_t len)
+{
+	struct msm_scm_mapping_info mi;
+	int ret, scm_ret;
+
+	mi.id = iommu_drvdata->sec_id;
+	mi.ctx_id = ctx_drvdata->num;
+	mi.va = va;
+	mi.size = len;
+
+	ret = scm_call(SCM_SVC_CP, IOMMU_SECURE_UNMAP, &mi, sizeof(mi),
+			&scm_ret, sizeof(scm_ret));
+	return ret;
+}
+
+static int __enable_clocks(struct msm_iommu_drvdata *drvdata)
+{
+	int ret;
+
+	ret = clk_prepare_enable(drvdata->pclk);
+	if (ret)
+		goto fail;
+
+	ret = clk_prepare_enable(drvdata->clk);
+	if (ret)
+		clk_disable_unprepare(drvdata->pclk);
+
+	if (drvdata->aclk) {
+		ret = clk_prepare_enable(drvdata->aclk);
+		if (ret) {
+			clk_disable_unprepare(drvdata->clk);
+			clk_disable_unprepare(drvdata->pclk);
+		}
+	}
+fail:
+	return ret;
+}
+
+static void __disable_clocks(struct msm_iommu_drvdata *drvdata)
+{
+	if (drvdata->aclk)
+		clk_disable_unprepare(drvdata->aclk);
+	clk_disable_unprepare(drvdata->clk);
+	clk_disable_unprepare(drvdata->pclk);
+}
+
+static int msm_iommu_domain_init(struct iommu_domain *domain, int flags)
+{
+	struct msm_priv *priv;
+
+	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	INIT_LIST_HEAD(&priv->list_attached);
+	domain->priv = priv;
+	return 0;
+}
+
+static void msm_iommu_domain_destroy(struct iommu_domain *domain)
+{
+	struct msm_priv *priv;
+
+	mutex_lock(&msm_iommu_lock);
+	priv = domain->priv;
+	domain->priv = NULL;
+
+	kfree(priv);
+	mutex_unlock(&msm_iommu_lock);
+}
+
+static int msm_iommu_attach_dev(struct iommu_domain *domain, struct device *dev)
+{
+	struct msm_priv *priv;
+	struct msm_iommu_drvdata *iommu_drvdata;
+	struct msm_iommu_ctx_drvdata *ctx_drvdata;
+	struct msm_iommu_ctx_drvdata *tmp_drvdata;
+	int ret = 0;
+
+	mutex_lock(&msm_iommu_lock);
+
+	priv = domain->priv;
+	if (!priv || !dev) {
+		ret = -EINVAL;
+		goto fail;
+	}
+
+	iommu_drvdata = dev_get_drvdata(dev->parent);
+	ctx_drvdata = dev_get_drvdata(dev);
+	if (!iommu_drvdata || !ctx_drvdata) {
+		ret = -EINVAL;
+		goto fail;
+	}
+
+	if (!list_empty(&ctx_drvdata->attached_elm)) {
+		ret = -EBUSY;
+		goto fail;
+	}
+
+	list_for_each_entry(tmp_drvdata, &priv->list_attached, attached_elm)
+		if (tmp_drvdata == ctx_drvdata) {
+			ret = -EBUSY;
+			goto fail;
+		}
+
+	ret = regulator_enable(iommu_drvdata->gdsc);
+	if (ret)
+		goto fail;
+
+	ret = __enable_clocks(iommu_drvdata);
+	if (ret) {
+		regulator_disable(iommu_drvdata->gdsc);
+		goto fail;
+	}
+
+	ret = msm_iommu_sec_program_iommu(iommu_drvdata->sec_id);
+	__disable_clocks(iommu_drvdata);
+	if (ret) {
+		regulator_disable(iommu_drvdata->gdsc);
+		goto fail;
+	}
+
+	list_add(&(ctx_drvdata->attached_elm), &priv->list_attached);
+	ctx_drvdata->attached_domain = domain;
+
+fail:
+	mutex_unlock(&msm_iommu_lock);
+	return ret;
+}
+
+static void msm_iommu_detach_dev(struct iommu_domain *domain,
+				 struct device *dev)
+{
+	struct msm_iommu_drvdata *iommu_drvdata;
+	struct msm_iommu_ctx_drvdata *ctx_drvdata;
+
+	mutex_lock(&msm_iommu_lock);
+	if (!dev)
+		goto fail;
+
+	iommu_drvdata = dev_get_drvdata(dev->parent);
+	ctx_drvdata = dev_get_drvdata(dev);
+	if (!iommu_drvdata || !ctx_drvdata || !ctx_drvdata->attached_domain)
+		goto fail;
+
+	list_del_init(&ctx_drvdata->attached_elm);
+	ctx_drvdata->attached_domain = NULL;
+
+	regulator_disable(iommu_drvdata->gdsc);
+
+fail:
+	mutex_unlock(&msm_iommu_lock);
+}
+
+static int get_drvdata(struct iommu_domain *domain,
+			struct msm_iommu_drvdata **iommu_drvdata,
+			struct msm_iommu_ctx_drvdata **ctx_drvdata)
+{
+	struct msm_priv *priv = domain->priv;
+	struct msm_iommu_ctx_drvdata *ctx;
+
+	list_for_each_entry(ctx, &priv->list_attached, attached_elm) {
+		if (ctx->attached_domain == domain)
+			break;
+	}
+
+	if (ctx->attached_domain != domain)
+		return -EINVAL;
+
+	*ctx_drvdata = ctx;
+	*iommu_drvdata = dev_get_drvdata(ctx->pdev->dev.parent);
+	return 0;
+}
+
+static int msm_iommu_map(struct iommu_domain *domain, unsigned long va,
+			 phys_addr_t pa, size_t len, int prot)
+{
+	struct msm_iommu_drvdata *iommu_drvdata;
+	struct msm_iommu_ctx_drvdata *ctx_drvdata;
+	int ret = 0;
+
+	mutex_lock(&msm_iommu_lock);
+
+	ret = get_drvdata(domain, &iommu_drvdata, &ctx_drvdata);
+	if (ret)
+		goto fail;
+
+	ret = msm_iommu_sec_ptbl_map(iommu_drvdata, ctx_drvdata,
+					va, pa, len);
+fail:
+	mutex_unlock(&msm_iommu_lock);
+	return ret;
+}
+
+static size_t msm_iommu_unmap(struct iommu_domain *domain, unsigned long va,
+			    size_t len)
+{
+	struct msm_iommu_drvdata *iommu_drvdata;
+	struct msm_iommu_ctx_drvdata *ctx_drvdata;
+	int ret = -ENODEV;
+
+	mutex_lock(&msm_iommu_lock);
+
+	ret = get_drvdata(domain, &iommu_drvdata, &ctx_drvdata);
+	if (ret)
+		goto fail;
+
+	ret = msm_iommu_sec_ptbl_unmap(iommu_drvdata, ctx_drvdata,
+					va, len);
+fail:
+	mutex_unlock(&msm_iommu_lock);
+
+	/* the IOMMU API requires us to return how many bytes were unmapped */
+	len = ret ? 0 : len;
+	return len;
+}
+
+static int msm_iommu_map_range(struct iommu_domain *domain, unsigned int va,
+			       struct scatterlist *sg, unsigned int len,
+			       int prot)
+{
+	int ret;
+	struct msm_iommu_drvdata *iommu_drvdata;
+	struct msm_iommu_ctx_drvdata *ctx_drvdata;
+
+	mutex_lock(&msm_iommu_lock);
+
+	ret = get_drvdata(domain, &iommu_drvdata, &ctx_drvdata);
+	if (ret)
+		goto fail;
+	ret = msm_iommu_sec_ptbl_map_range(iommu_drvdata, ctx_drvdata,
+						va, sg, len);
+fail:
+	mutex_unlock(&msm_iommu_lock);
+	return ret;
+}
+
+
+static int msm_iommu_unmap_range(struct iommu_domain *domain, unsigned int va,
+				 unsigned int len)
+{
+	struct msm_iommu_drvdata *iommu_drvdata;
+	struct msm_iommu_ctx_drvdata *ctx_drvdata;
+	int ret;
+
+	mutex_lock(&msm_iommu_lock);
+
+	ret = get_drvdata(domain, &iommu_drvdata, &ctx_drvdata);
+	if (ret)
+		goto fail;
+
+	ret = msm_iommu_sec_ptbl_unmap(iommu_drvdata, ctx_drvdata, va, len);
+
+fail:
+	mutex_unlock(&msm_iommu_lock);
+	return 0;
+}
+
+static phys_addr_t msm_iommu_iova_to_phys(struct iommu_domain *domain,
+					  unsigned long va)
+{
+	return 0;
+}
+
+static int msm_iommu_domain_has_cap(struct iommu_domain *domain,
+				    unsigned long cap)
+{
+	return 0;
+}
+
+static phys_addr_t msm_iommu_get_pt_base_addr(struct iommu_domain *domain)
+{
+	return 0;
+}
+
+static struct iommu_ops msm_iommu_ops = {
+	.domain_init = msm_iommu_domain_init,
+	.domain_destroy = msm_iommu_domain_destroy,
+	.attach_dev = msm_iommu_attach_dev,
+	.detach_dev = msm_iommu_detach_dev,
+	.map = msm_iommu_map,
+	.unmap = msm_iommu_unmap,
+	.map_range = msm_iommu_map_range,
+	.unmap_range = msm_iommu_unmap_range,
+	.iova_to_phys = msm_iommu_iova_to_phys,
+	.domain_has_cap = msm_iommu_domain_has_cap,
+	.get_pt_base_addr = msm_iommu_get_pt_base_addr,
+	.pgsize_bitmap = MSM_IOMMU_PGSIZES,
+};
+
+static int __init msm_iommu_sec_init(void)
+{
+	int ret;
+
+	ret = bus_register(&msm_iommu_sec_bus_type);
+	if (ret)
+		goto fail;
+
+	bus_set_iommu(&msm_iommu_sec_bus_type, &msm_iommu_ops);
+	ret = msm_iommu_sec_ptbl_init();
+fail:
+	return ret;
+}
+
+subsys_initcall(msm_iommu_sec_init);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("MSM SMMU Secure Driver");
diff --git a/drivers/leds/leds-qpnp.c b/drivers/leds/leds-qpnp.c
index 13493b8..ba122bb 100644
--- a/drivers/leds/leds-qpnp.c
+++ b/drivers/leds/leds-qpnp.c
@@ -1,5 +1,5 @@
 
-/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -68,8 +68,6 @@
 #define WLED_SYNC_VAL			0x07
 #define WLED_SYNC_RESET_VAL		0x00
 
-#define WLED_TRIGGER_DEFAULT		"none"
-#define WLED_FLAGS_DEFAULT		0x00
 #define WLED_DEFAULT_STRINGS		0x01
 #define WLED_DEFAULT_OVP_VAL		0x02
 #define WLED_BOOST_LIM_DEFAULT		0x03
@@ -77,12 +75,67 @@
 #define WLED_CTRL_DLY_DEFAULT		0x00
 #define WLED_SWITCH_FREQ_DEFAULT	0x02
 
+#define FLASH_SAFETY_TIMER(base)	(base + 0x40)
+#define FLASH_MAX_CURR(base)		(base + 0x41)
+#define FLASH_LED_0_CURR(base)		(base + 0x42)
+#define FLASH_LED_1_CURR(base)		(base + 0x43)
+#define FLASH_CLAMP_CURR(base)		(base + 0x44)
+#define FLASH_LED_TMR_CTRL(base)	(base + 0x48)
+#define FLASH_HEADROOM(base)		(base + 0x49)
+#define FLASH_STARTUP_DELAY(base)	(base + 0x4B)
+#define FLASH_MASK_ENABLE(base)		(base + 0x4C)
+#define FLASH_VREG_OK_FORCE(base)	(base + 0x4F)
+#define FLASH_ENABLE_CONTROL(base)	(base + 0x46)
+#define FLASH_LED_STROBE_CTRL(base)	(base + 0x47)
+
+#define FLASH_MAX_LEVEL			0x4F
+#define	FLASH_NO_MASK			0x00
+
+#define FLASH_MASK_1			0x20
+#define FLASH_MASK_REG_MASK		0xE0
+#define FLASH_HEADROOM_MASK		0x03
+#define FLASH_SAFETY_TIMER_MASK		0x7F
+#define FLASH_CURRENT_MASK		0xFF
+#define FLASH_TMR_MASK			0x03
+#define FLASH_TMR_WATCHDOG		0x03
+#define FLASH_TMR_SAFETY		0x00
+
+#define FLASH_HW_VREG_OK		0x80
+#define FLASH_VREG_MASK			0xC0
+
+#define FLASH_STARTUP_DLY_MASK		0x02
+
+#define FLASH_ENABLE_ALL		0xE0
+#define FLASH_ENABLE_MODULE		0x80
+#define FLASH_ENABLE_MODULE_MASK	0x80
+#define FLASH_DISABLE_ALL		0x00
+#define FLASH_ENABLE_MASK		0x60
+#define FLASH_ENABLE_LED_0		0x40
+#define FLASH_ENABLE_LED_1		0x20
+#define FLASH_INIT_MASK			0xE0
+
+#define FLASH_STROBE_ALL		0xC0
+#define FLASH_STROBE_MASK		0xC0
+#define FLASH_LED_0_OUTPUT		0x80
+#define FLASH_LED_1_OUTPUT		0x40
+
+#define FLASH_CURRENT_PRGM_MIN		1
+#define FLASH_CURRENT_PRGM_SHIFT	1
+
+#define FLASH_DURATION_200ms		0x13
+#define FLASH_CLAMP_200mA		0x0F
+
+#define LED_TRIGGER_DEFAULT		"none"
+
 /**
  * enum qpnp_leds - QPNP supported led ids
  * @QPNP_ID_WLED - White led backlight
  */
 enum qpnp_leds {
-	QPNP_ID_WLED,
+	QPNP_ID_WLED = 0,
+	QPNP_ID_FLASH1_LED0,
+	QPNP_ID_FLASH1_LED1,
+	QPNP_ID_MAX,
 };
 
 /* current boost limit */
@@ -113,18 +166,38 @@
 	WLED_3200kHz,
 };
 
+enum flash_headroom {
+	HEADROOM_250mV = 0,
+	HEADROOM_300mV,
+	HEADROOM_400mV,
+	HEADROOM_500mV,
+};
+
+enum flash_startup_dly {
+	DELAY_10us = 0,
+	DELAY_32us,
+	DELAY_64us,
+	DELAY_128us,
+};
+
 static u8 wled_debug_regs[] = {
 	/* common registers */
 	0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4d, 0x4e, 0x4f,
 	0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59,
 	/* LED1 */
 	0x60, 0x61, 0x62, 0x63, 0x66,
-	/* LED1 */
+	/* LED2 */
 	0x70, 0x71, 0x72, 0x73, 0x76,
-	/* LED1 */
+	/* LED3 */
 	0x80, 0x81, 0x82, 0x83, 0x86,
 };
 
+static u8 flash_debug_regs[] = {
+	0x40, 0x41, 0x42, 0x43, 0x44, 0x48, 0x49, 0x4b, 0x4c,
+	0x4f, 0x46, 0x47,
+};
+
+
 /**
  *  wled_config_data - wled configuration data
  *  @num_strings - number of wled strings supported
@@ -149,12 +222,39 @@
 };
 
 /**
+ *  flash_config_data - flash configuration data
+ *  @current_prgm - current to be programmed, scaled by max level
+ *  @clamp_curr - clamp current to use
+ *  @headroom - headroom value to use
+ *  @duration - duration of the flash
+ *  @enable_module - enable address for particular flash
+ *  @trigger_flash - trigger flash
+ *  @startup_dly - startup delay for flash
+ *  @current_addr - address to write for current
+ *  @second_addr - address of secondary flash to be written
+ *  @safety_timer - enable safety timer or watchdog timer
+ */
+struct flash_config_data {
+	u8	current_prgm;
+	u8	clamp_curr;
+	u8	headroom;
+	u8	duration;
+	u8	enable_module;
+	u8	trigger_flash;
+	u8	startup_dly;
+	u16	current_addr;
+	u16	second_addr;
+	bool	safety_timer;
+};
+
+/**
  * struct qpnp_led_data - internal led data structure
  * @led_classdev - led class device
  * @id - led index
  * @base_reg - base register given in device tree
  * @lock - to protect the transactions
  * @reg - cached value of led register
+ * @num_leds - number of leds in the module
  * @max_current - maximum current supported by LED
  * @default_on - true: default state max, false, default state 0
  */
@@ -164,8 +264,10 @@
 	int			id;
 	u16			base;
 	u8			reg;
+	u8			num_leds;
 	spinlock_t		lock;
 	struct wled_config_data *wled_cfg;
+	struct flash_config_data	*flash_cfg;
 	int			max_current;
 	bool			default_on;
 };
@@ -194,6 +296,22 @@
 	return rc;
 }
 
+static void qpnp_dump_regs(struct qpnp_led_data *led, u8 regs[], u8 array_size)
+{
+	int i;
+	u8 val;
+
+	pr_debug("===== %s LED register dump start =====\n", led->cdev.name);
+	for (i = 0; i < array_size; i++) {
+		spmi_ext_register_readl(led->spmi_dev->ctrl,
+					led->spmi_dev->sid,
+					led->base + regs[i],
+					&val, sizeof(val));
+		pr_debug("0x%x = 0x%x\n", led->base + regs[i], val);
+	}
+	pr_debug("===== %s LED register dump end =====\n", led->cdev.name);
+}
+
 static int qpnp_wled_set(struct qpnp_led_data *led)
 {
 	int rc, duty;
@@ -271,20 +389,76 @@
 	return 0;
 }
 
-static void qpnp_wled_dump_regs(struct qpnp_led_data *led)
+static int qpnp_flash_set(struct qpnp_led_data *led)
 {
-	int i;
-	u8 val;
+	int rc;
+	int val = led->cdev.brightness;
 
-	pr_debug("===== WLED register dump start =====\n");
-	for (i = 0; i < ARRAY_SIZE(wled_debug_regs); i++) {
-		spmi_ext_register_readl(led->spmi_dev->ctrl,
-					led->spmi_dev->sid,
-					led->base + wled_debug_regs[i],
-					&val, sizeof(val));
-		pr_debug("0x%x = 0x%x\n", led->base + wled_debug_regs[i], val);
+	led->flash_cfg->current_prgm = (val * FLASH_MAX_LEVEL /
+						led->max_current);
+
+	led->flash_cfg->current_prgm =
+		led->flash_cfg->current_prgm >> FLASH_CURRENT_PRGM_SHIFT;
+	if (!led->flash_cfg->current_prgm)
+		led->flash_cfg->current_prgm = FLASH_CURRENT_PRGM_MIN;
+
+	/* Set led current */
+	if (val > 0) {
+		rc = qpnp_led_masked_write(led, led->flash_cfg->current_addr,
+			FLASH_CURRENT_MASK, led->flash_cfg->current_prgm);
+		if (rc) {
+			dev_err(&led->spmi_dev->dev,
+				"Current reg write failed(%d)\n", rc);
+			return rc;
+		}
+
+		rc = qpnp_led_masked_write(led, led->flash_cfg->second_addr,
+			FLASH_CURRENT_MASK, led->flash_cfg->current_prgm);
+		if (rc) {
+			dev_err(&led->spmi_dev->dev,
+				"Current reg write failed(%d)\n", rc);
+			return rc;
+		}
+
+		rc = qpnp_led_masked_write(led, FLASH_ENABLE_CONTROL(led->base),
+			FLASH_ENABLE_MASK,
+			FLASH_ENABLE_ALL);
+		if (rc) {
+			dev_err(&led->spmi_dev->dev,
+				"Enable reg write failed(%d)\n", rc);
+			return rc;
+		}
+		rc = qpnp_led_masked_write(led,
+			FLASH_LED_STROBE_CTRL(led->base),
+			FLASH_STROBE_MASK, FLASH_STROBE_ALL);
+
+		if (rc) {
+			dev_err(&led->spmi_dev->dev,
+				"LED %d flash write failed(%d)\n", led->id, rc);
+			return rc;
+		}
+	} else {
+		rc = qpnp_led_masked_write(led, FLASH_ENABLE_CONTROL(led->base),
+			FLASH_ENABLE_MASK,
+			FLASH_DISABLE_ALL);
+		if (rc) {
+			dev_err(&led->spmi_dev->dev,
+				"Enable reg write failed(%d)\n", rc);
+			return rc;
+		}
+
+		rc = qpnp_led_masked_write(led,
+			FLASH_LED_STROBE_CTRL(led->base),
+			FLASH_STROBE_MASK,
+			FLASH_DISABLE_ALL);
+		if (rc) {
+			dev_err(&led->spmi_dev->dev,
+				"LED %d flash write failed(%d)\n", led->id, rc);
+			return rc;
+		}
 	}
-	pr_debug("===== WLED register dump end =====\n");
+
+	return 0;
 }
 
 static void qpnp_led_set(struct led_classdev *led_cdev,
@@ -294,9 +468,8 @@
 	struct qpnp_led_data *led;
 
 	led = container_of(led_cdev, struct qpnp_led_data, cdev);
-
 	if (value < LED_OFF || value > led->cdev.max_brightness) {
-		dev_err(led->cdev.dev, "Invalid brightness value\n");
+		dev_err(&led->spmi_dev->dev, "Invalid brightness value\n");
 		return;
 	}
 
@@ -307,11 +480,18 @@
 	case QPNP_ID_WLED:
 		rc = qpnp_wled_set(led);
 		if (rc < 0)
-			dev_err(led->cdev.dev,
+			dev_err(&led->spmi_dev->dev,
 				"WLED set brightness failed (%d)\n", rc);
 		break;
+	case QPNP_ID_FLASH1_LED0:
+	case QPNP_ID_FLASH1_LED1:
+		rc = qpnp_flash_set(led);
+		if (rc < 0)
+			dev_err(&led->spmi_dev->dev,
+				"FLASH set brightness failed (%d)\n", rc);
+		break;
 	default:
-		dev_err(led->cdev.dev, "Invalid LED(%d)\n", led->id);
+		dev_err(&led->spmi_dev->dev, "Invalid LED(%d)\n", led->id);
 		break;
 	}
 	spin_unlock(&led->lock);
@@ -323,8 +503,12 @@
 	case QPNP_ID_WLED:
 		led->cdev.max_brightness = WLED_MAX_LEVEL;
 		break;
+	case QPNP_ID_FLASH1_LED0:
+	case QPNP_ID_FLASH1_LED1:
+		led->cdev.max_brightness = led->max_current;
+		break;
 	default:
-		dev_err(led->cdev.dev, "Invalid LED(%d)\n", led->id);
+		dev_err(&led->spmi_dev->dev, "Invalid LED(%d)\n", led->id);
 		return -EINVAL;
 	}
 
@@ -465,7 +649,122 @@
 	}
 
 	/* dump wled registers */
-	qpnp_wled_dump_regs(led);
+	qpnp_dump_regs(led, wled_debug_regs, ARRAY_SIZE(wled_debug_regs));
+
+	return 0;
+}
+
+static int __devinit qpnp_flash_init(struct qpnp_led_data *led)
+{
+	int rc;
+
+	rc = qpnp_led_masked_write(led,
+		FLASH_LED_STROBE_CTRL(led->base),
+		FLASH_STROBE_MASK, FLASH_DISABLE_ALL);
+	if (rc) {
+		dev_err(&led->spmi_dev->dev,
+			"LED %d flash write failed(%d)\n", led->id, rc);
+		return rc;
+	}
+	rc = qpnp_led_masked_write(led, FLASH_ENABLE_CONTROL(led->base),
+		FLASH_INIT_MASK, FLASH_ENABLE_MODULE);
+	if (rc) {
+		dev_err(&led->spmi_dev->dev,
+			"Enable reg write failed(%d)\n", rc);
+		return rc;
+	}
+
+	/* Set flash safety timer */
+	rc = qpnp_led_masked_write(led, FLASH_SAFETY_TIMER(led->base),
+		FLASH_SAFETY_TIMER_MASK, led->flash_cfg->duration);
+	if (rc) {
+		dev_err(&led->spmi_dev->dev,
+			"Safety timer reg write failed(%d)\n", rc);
+		return rc;
+	}
+
+	/* Set max current */
+	rc = qpnp_led_masked_write(led, FLASH_MAX_CURR(led->base),
+		FLASH_CURRENT_MASK, FLASH_MAX_LEVEL);
+	if (rc) {
+		dev_err(&led->spmi_dev->dev,
+			"Max current reg write failed(%d)\n", rc);
+		return rc;
+	}
+	/* Set clamp current */
+	rc = qpnp_led_masked_write(led, FLASH_CLAMP_CURR(led->base),
+		FLASH_CURRENT_MASK, led->flash_cfg->clamp_curr);
+	if (rc) {
+		dev_err(&led->spmi_dev->dev,
+			"Clamp current reg write failed(%d)\n", rc);
+		return rc;
+	}
+
+	/* Set timer control - safety or watchdog */
+	if (led->flash_cfg->safety_timer)
+		rc = qpnp_led_masked_write(led, FLASH_LED_TMR_CTRL(led->base),
+			FLASH_TMR_MASK, FLASH_TMR_SAFETY);
+	else
+		rc = qpnp_led_masked_write(led, FLASH_LED_TMR_CTRL(led->base),
+			FLASH_TMR_MASK, FLASH_TMR_WATCHDOG);
+	if (rc) {
+		dev_err(&led->spmi_dev->dev,
+			"LED timer ctrl reg write failed(%d)\n", rc);
+		return rc;
+	}
+	/* Set headroom */
+	rc = qpnp_led_masked_write(led, FLASH_HEADROOM(led->base),
+		FLASH_HEADROOM_MASK, led->flash_cfg->headroom);
+	if (rc) {
+		dev_err(&led->spmi_dev->dev,
+			"Headroom reg write failed(%d)\n", rc);
+		return rc;
+	}
+
+	/* Set mask enable */
+	rc = qpnp_led_masked_write(led, FLASH_MASK_ENABLE(led->base),
+		FLASH_MASK_REG_MASK, FLASH_MASK_1);
+	if (rc) {
+		dev_err(&led->spmi_dev->dev,
+			"Mask enable reg write failed(%d)\n", rc);
+		return rc;
+	}
+
+	/* Set startup delay */
+	rc = qpnp_led_masked_write(led, FLASH_STARTUP_DELAY(led->base),
+		FLASH_STARTUP_DLY_MASK, led->flash_cfg->startup_dly);
+	if (rc) {
+		dev_err(&led->spmi_dev->dev,
+			"Startup delay reg write failed(%d)\n", rc);
+		return rc;
+	}
+
+	rc = qpnp_led_masked_write(led, FLASH_VREG_OK_FORCE(led->base),
+		FLASH_VREG_MASK, FLASH_HW_VREG_OK);
+	if (rc) {
+		dev_err(&led->spmi_dev->dev,
+			"Vreg OK reg write failed(%d)\n", rc);
+		return rc;
+	}
+
+	/* Set led current and enable module */
+	rc = qpnp_led_masked_write(led, led->flash_cfg->current_addr,
+		FLASH_CURRENT_MASK, led->flash_cfg->current_prgm);
+	if (rc) {
+		dev_err(&led->spmi_dev->dev,
+			"Current reg write failed(%d)\n", rc);
+		return rc;
+	}
+
+	rc = qpnp_led_masked_write(led, FLASH_ENABLE_CONTROL(led->base),
+		FLASH_ENABLE_MODULE_MASK, FLASH_ENABLE_MODULE);
+	if (rc) {
+		dev_err(&led->spmi_dev->dev,
+			"Enable reg write failed(%d)\n", rc);
+		return rc;
+	}
+	/* dump flash registers */
+	qpnp_dump_regs(led, flash_debug_regs, ARRAY_SIZE(flash_debug_regs));
 
 	return 0;
 }
@@ -478,17 +777,50 @@
 	case QPNP_ID_WLED:
 		rc = qpnp_wled_init(led);
 		if (rc)
-			dev_err(led->cdev.dev,
+			dev_err(&led->spmi_dev->dev,
 				"WLED initialize failed(%d)\n", rc);
 		break;
+	case QPNP_ID_FLASH1_LED0:
+	case QPNP_ID_FLASH1_LED1:
+		rc = qpnp_flash_init(led);
+		if (rc)
+			dev_err(&led->spmi_dev->dev,
+				"FLASH initialize failed(%d)\n", rc);
+		break;
 	default:
-		dev_err(led->cdev.dev, "Invalid LED(%d)\n", led->id);
+		dev_err(&led->spmi_dev->dev, "Invalid LED(%d)\n", led->id);
 		rc = -EINVAL;
 	}
 
 	return rc;
 }
 
+static int __devinit qpnp_get_common_configs(struct qpnp_led_data *led,
+				struct device_node *node)
+{
+	int rc;
+	const char *temp_string;
+
+	led->cdev.default_trigger = LED_TRIGGER_DEFAULT;
+	rc = of_property_read_string(node, "linux,default-trigger",
+		&temp_string);
+	if (!rc)
+		led->cdev.default_trigger = temp_string;
+	else if (rc != -EINVAL)
+		return rc;
+
+	led->default_on = false;
+	rc = of_property_read_string(node, "qcom,default-state",
+		&temp_string);
+	if (!rc) {
+		if (strncmp(temp_string, "on", sizeof("on")) == 0)
+			led->default_on = true;
+	} else if (rc != -EINVAL)
+		return rc;
+
+	return 0;
+}
+
 /*
  * Handlers for alternative sources of platform_data
  */
@@ -497,9 +829,6 @@
 {
 	u32 val;
 	int rc;
-	const char *temp_string;
-
-	led->id = QPNP_ID_WLED;
 
 	led->wled_cfg = devm_kzalloc(&led->spmi_dev->dev,
 				sizeof(struct wled_config_data), GFP_KERNEL);
@@ -508,31 +837,6 @@
 		return -ENOMEM;
 	}
 
-	led->cdev.default_trigger = WLED_TRIGGER_DEFAULT;
-	rc = of_property_read_string(node, "linux,default-trigger",
-		&temp_string);
-	if (!rc)
-		led->cdev.default_trigger = temp_string;
-	else if (rc != -EINVAL)
-		return rc;
-
-
-	led->cdev.flags = WLED_FLAGS_DEFAULT;
-	rc = of_property_read_u32(node, "qcom,flags", &val);
-	if (!rc)
-		led->cdev.flags = (int) val;
-	else if (rc != -EINVAL)
-		return rc;
-
-	led->default_on = true;
-	rc = of_property_read_string(node, "qcom,default-state",
-		&temp_string);
-	if (!rc) {
-		if (!strncmp(temp_string, "off", sizeof("off")))
-			led->default_on = false;
-	} else if (rc != -EINVAL)
-		return rc;
-
 	led->wled_cfg->num_strings = WLED_DEFAULT_STRINGS;
 	rc = of_property_read_u32(node, "qcom,num-strings", &val);
 	if (!rc)
@@ -587,109 +891,221 @@
 	return 0;
 }
 
+static int __devinit qpnp_get_config_flash(struct qpnp_led_data *led,
+				struct device_node *node)
+{
+	int rc;
+	u32 val;
+
+	led->flash_cfg = devm_kzalloc(&led->spmi_dev->dev,
+				sizeof(struct flash_config_data), GFP_KERNEL);
+	if (!led->flash_cfg) {
+		dev_err(&led->spmi_dev->dev, "Unable to allocate memory\n");
+		return -ENOMEM;
+	}
+
+	if (led->id == QPNP_ID_FLASH1_LED0) {
+		led->flash_cfg->enable_module = FLASH_ENABLE_ALL;
+		led->flash_cfg->current_addr = FLASH_LED_0_CURR(led->base);
+		led->flash_cfg->second_addr = FLASH_LED_1_CURR(led->base);
+		led->flash_cfg->trigger_flash = FLASH_LED_0_OUTPUT;
+	} else if (led->id == QPNP_ID_FLASH1_LED1) {
+		led->flash_cfg->enable_module = FLASH_ENABLE_ALL;
+		led->flash_cfg->current_addr = FLASH_LED_1_CURR(led->base);
+		led->flash_cfg->second_addr = FLASH_LED_0_CURR(led->base);
+		led->flash_cfg->trigger_flash = FLASH_LED_1_OUTPUT;
+	} else {
+		dev_err(&led->spmi_dev->dev, "Unknown flash LED name given\n");
+		return -EINVAL;
+	}
+
+	rc = of_property_read_u32(node, "qcom,current", &val);
+	if (!rc)
+		led->flash_cfg->current_prgm = (val *
+				FLASH_MAX_LEVEL / led->max_current);
+	else
+		return -EINVAL;
+
+	rc = of_property_read_u32(node, "qcom,headroom", &val);
+	if (!rc)
+		led->flash_cfg->headroom = (u8) val;
+	else if (rc == -EINVAL)
+		led->flash_cfg->headroom = HEADROOM_300mV;
+	else
+		return rc;
+
+	rc = of_property_read_u32(node, "qcom,duration", &val);
+	if (!rc)
+		led->flash_cfg->duration = (((u8) val) - 10) / 10;
+	else if (rc == -EINVAL)
+		led->flash_cfg->duration = FLASH_DURATION_200ms;
+	else
+		return rc;
+
+	rc = of_property_read_u32(node, "qcom,clamp-curr", &val);
+	if (!rc)
+		led->flash_cfg->clamp_curr = (val *
+				FLASH_MAX_LEVEL / led->max_current);
+	else if (rc == -EINVAL)
+		led->flash_cfg->clamp_curr = FLASH_CLAMP_200mA;
+	else
+		return rc;
+
+	rc = of_property_read_u32(node, "qcom,startup-dly", &val);
+	if (!rc)
+		led->flash_cfg->startup_dly = (u8) val;
+	else if (rc == -EINVAL)
+		led->flash_cfg->startup_dly = DELAY_32us;
+	else
+		return rc;
+
+	led->flash_cfg->safety_timer =
+		of_property_read_bool(node, "qcom,safety-timer");
+
+	return 0;
+}
+
 static int __devinit qpnp_leds_probe(struct spmi_device *spmi)
 {
 	struct qpnp_led_data *led;
 	struct resource *led_resource;
-	struct device_node *node;
-	int rc;
+	struct device_node *node, *temp;
+	int rc, i, num_leds = 0;
 	const char *led_label;
 
-	led = devm_kzalloc(&spmi->dev, (sizeof(struct qpnp_led_data)),
-		GFP_KERNEL);
+	node = spmi->dev.of_node;
+	if (node == NULL)
+		return -ENODEV;
+
+	temp = NULL;
+	while ((temp = of_get_next_child(node, temp)))
+		num_leds++;
+
+	led = devm_kzalloc(&spmi->dev,
+		(sizeof(struct qpnp_led_data) * num_leds), GFP_KERNEL);
 	if (!led) {
 		dev_err(&spmi->dev, "Unable to allocate memory\n");
 		return -ENOMEM;
 	}
 
-	led->spmi_dev = spmi;
+	led->num_leds = num_leds;
+	num_leds = 0;
 
-	led_resource = spmi_get_resource(spmi, NULL, IORESOURCE_MEM, 0);
-	if (!led_resource) {
-		dev_err(&spmi->dev, "Unable to get LED base address\n");
-		return -ENXIO;
-	}
-	led->base = led_resource->start;
+	for_each_child_of_node(node, temp) {
+		led->spmi_dev = spmi;
 
-	dev_set_drvdata(&spmi->dev, led);
+		led_resource = spmi_get_resource(spmi, NULL, IORESOURCE_MEM, 0);
+		if (!led_resource) {
+			dev_err(&spmi->dev, "Unable to get LED base address\n");
+			return -ENXIO;
+		}
+		led->base = led_resource->start;
 
-	node = led->spmi_dev->dev.of_node;
-	if (node == NULL)
-		return -ENODEV;
+		dev_set_drvdata(&spmi->dev, led);
 
-	rc = of_property_read_string(node, "qcom,label", &led_label);
-	if (rc < 0) {
-		dev_err(&led->spmi_dev->dev,
-			"Failure reading label, rc = %d\n", rc);
-		return rc;
-	}
 
-	rc = of_property_read_string(node, "qcom,name", &led->cdev.name);
-	if (rc < 0) {
-		dev_err(&led->spmi_dev->dev,
-			"Failure reading led name, rc = %d\n", rc);
-		return rc;
-	}
-
-	rc = of_property_read_u32(node, "qcom,max-current", &led->max_current);
-	if (rc < 0) {
-		dev_err(&led->spmi_dev->dev,
-			"Failure reading max_current, rc =  %d\n", rc);
-		return rc;
-	}
-
-	led->cdev.brightness_set    = qpnp_led_set;
-	led->cdev.brightness_get    = qpnp_led_get;
-	led->cdev.brightness	= LED_OFF;
-
-	if (strncmp(led_label, "wled", sizeof("wled")) == 0) {
-		rc = qpnp_get_config_wled(led, node);
+		rc = of_property_read_string(temp, "label", &led_label);
 		if (rc < 0) {
 			dev_err(&led->spmi_dev->dev,
-				"Unable to read wled config data\n");
+				"Failure reading label, rc = %d\n", rc);
 			return rc;
 		}
-	} else {
-		dev_err(&led->spmi_dev->dev, "No LED matching label\n");
-		return -EINVAL;
+
+		rc = of_property_read_string(temp, "linux,name",
+			&led->cdev.name);
+		if (rc < 0) {
+			dev_err(&led->spmi_dev->dev,
+				"Failure reading led name, rc = %d\n", rc);
+			return rc;
+		}
+
+		rc = of_property_read_u32(temp, "qcom,max-current",
+			&led->max_current);
+		if (rc < 0) {
+			dev_err(&led->spmi_dev->dev,
+				"Failure reading max_current, rc =  %d\n", rc);
+			return rc;
+		}
+
+		rc = of_property_read_u32(temp, "qcom,id", &led->id);
+		if (rc < 0) {
+			dev_err(&led->spmi_dev->dev,
+				"Failure reading led id, rc =  %d\n", rc);
+			return rc;
+		}
+
+		rc = qpnp_get_common_configs(led, temp);
+		if (rc) {
+			dev_err(&led->spmi_dev->dev,
+				"Failure reading common led configuration," \
+				" rc = %d\n", rc);
+			return rc;
+		}
+
+		led->cdev.brightness_set    = qpnp_led_set;
+		led->cdev.brightness_get    = qpnp_led_get;
+
+		if (strncmp(led_label, "wled", sizeof("wled")) == 0) {
+			rc = qpnp_get_config_wled(led, temp);
+			if (rc < 0) {
+				dev_err(&led->spmi_dev->dev,
+					"Unable to read wled config data\n");
+				return rc;
+			}
+		} else if (strncmp(led_label, "flash", sizeof("flash"))
+				== 0) {
+			rc = qpnp_get_config_flash(led, temp);
+			if (rc < 0) {
+				dev_err(&led->spmi_dev->dev,
+					"Unable to read flash config data\n");
+				return rc;
+			}
+		} else {
+			dev_err(&led->spmi_dev->dev, "No LED matching label\n");
+			return -EINVAL;
+		}
+
+		spin_lock_init(&led->lock);
+
+		rc =  qpnp_led_initialize(led);
+		if (rc < 0)
+			goto fail_id_check;
+
+		rc = qpnp_led_set_max_brightness(led);
+		if (rc < 0)
+			goto fail_id_check;
+
+		rc = led_classdev_register(&spmi->dev, &led->cdev);
+		if (rc) {
+			dev_err(&spmi->dev, "unable to register led %d,rc=%d\n",
+						 led->id, rc);
+			goto fail_id_check;
+		}
+		/* configure default state */
+		if (led->default_on)
+			led->cdev.brightness = led->cdev.max_brightness;
+		else
+			led->cdev.brightness = LED_OFF;
+
+		qpnp_led_set(&led->cdev, led->cdev.brightness);
+		led++;
+		num_leds++;
 	}
-
-	spin_lock_init(&led->lock);
-
-	rc =  qpnp_led_initialize(led);
-	if (rc < 0)
-		goto fail_id_check;
-
-	rc = qpnp_led_set_max_brightness(led);
-	if (rc < 0)
-		goto fail_id_check;
-
-
-	rc = led_classdev_register(&spmi->dev, &led->cdev);
-	if (rc) {
-		dev_err(&spmi->dev, "unable to register led %d,rc=%d\n",
-					 led->id, rc);
-		goto fail_id_check;
-	}
-
-	/* configure default state */
-	if (led->default_on)
-		led->cdev.brightness = led->cdev.max_brightness;
-
-	qpnp_led_set(&led->cdev, led->cdev.brightness);
-
 	return 0;
 
 fail_id_check:
-	led_classdev_unregister(&led->cdev);
+	for (i = 0; i < num_leds; i++)
+		led_classdev_unregister(&led[i].cdev);
 	return rc;
 }
 
 static int __devexit qpnp_leds_remove(struct spmi_device *spmi)
 {
 	struct qpnp_led_data *led  = dev_get_drvdata(&spmi->dev);
+	int i;
 
-	led_classdev_unregister(&led->cdev);
+	for (i = 0; i < led->num_leds; i++)
+		led_classdev_unregister(&led[i].cdev);
 
 	return 0;
 }
@@ -722,3 +1138,4 @@
 MODULE_DESCRIPTION("QPNP LEDs driver");
 MODULE_LICENSE("GPL v2");
 MODULE_ALIAS("leds:leds-qpnp");
+
diff --git a/drivers/media/dvb/dvb-core/dmxdev.c b/drivers/media/dvb/dvb-core/dmxdev.c
index d9f62ad..cbd3b38 100644
--- a/drivers/media/dvb/dvb-core/dmxdev.c
+++ b/drivers/media/dvb/dvb-core/dmxdev.c
@@ -98,6 +98,18 @@
 	return index;
 }
 
+static inline int dvb_dmxdev_events_is_full(struct dmxdev_events_queue *events)
+{
+	int new_write_index;
+
+	new_write_index = dvb_dmxdev_advance_event_idx(events->write_index);
+	if (new_write_index == events->read_index)
+		return 1;
+
+	return 0;
+
+}
+
 static inline void dvb_dmxdev_flush_events(struct dmxdev_events_queue *events)
 {
 	events->read_index = 0;
@@ -788,6 +800,13 @@
 		spin_lock_irq(&dmxdev->lock);
 		dvb_dmxdev_update_events(&dmxdev->dvr_output_events, res);
 		spin_unlock_irq(&dmxdev->lock);
+
+		/*
+		 * in PULL mode, we might be stalling on
+		 * event queue, so need to wake-up waiters
+		 */
+		if (dmxdev->playback_mode == DMX_PB_MODE_PULL)
+			wake_up_all(&dmxdev->dvr_buffer.queue);
 	} else if (res == -EOVERFLOW) {
 		/*
 		 * When buffer overflowed, demux-dev flushed the
@@ -1037,6 +1056,13 @@
 
 	spin_unlock_irq(&dmxdev->lock);
 
+	/*
+	 * in PULL mode, we might be stalling on
+	 * event queue, so need to wake-up waiters
+	 */
+	if (dmxdev->playback_mode == DMX_PB_MODE_PULL)
+		wake_up_all(&dmxdev->dvr_buffer.queue);
+
 	return res;
 }
 
@@ -1303,13 +1329,17 @@
 {
 	struct dmxdev_filter *dmxdevfilter = filter->priv;
 	struct dvb_ringbuffer *src;
+	struct dmxdev_events_queue *events;
 	int ret;
 
 	if (dmxdevfilter->params.pes.output == DMX_OUT_TAP
-		|| dmxdevfilter->params.pes.output == DMX_OUT_TSDEMUX_TAP)
+		|| dmxdevfilter->params.pes.output == DMX_OUT_TSDEMUX_TAP) {
 		src = &dmxdevfilter->buffer;
-	else
+		events = &dmxdevfilter->events;
+	} else {
 		src = &dmxdevfilter->dev->dvr_buffer;
+		events = &dmxdevfilter->dev->dvr_output_events;
+	}
 
 	do {
 		ret = 0;
@@ -1330,7 +1360,8 @@
 			return ret;
 		}
 
-		if (required_space <= dvb_ringbuffer_free(src)) {
+		if ((required_space <= dvb_ringbuffer_free(src)) &&
+			(!dvb_dmxdev_events_is_full(events))) {
 			spin_unlock(&dmxdevfilter->dev->lock);
 			return 0;
 		}
@@ -1339,7 +1370,8 @@
 
 		ret = wait_event_interruptible(src->queue,
 				(!src->data) ||
-				(dvb_ringbuffer_free(src) >= required_space) ||
+				((dvb_ringbuffer_free(src) >= required_space) &&
+				 (!dvb_dmxdev_events_is_full(events))) ||
 				(src->error != 0) ||
 				(dmxdevfilter->state != DMXDEV_STATE_GO) ||
 				dmxdevfilter->dev->dvr_in_exit);
@@ -1355,6 +1387,7 @@
 {
 	struct dmxdev_filter *dmxdevfilter = filter->priv;
 	struct dvb_ringbuffer *src = &dmxdevfilter->buffer;
+	struct dmxdev_events_queue *events = &dmxdevfilter->events;
 	int ret;
 
 	do {
@@ -1376,7 +1409,8 @@
 			return ret;
 		}
 
-		if (required_space <= dvb_ringbuffer_free(src)) {
+		if ((required_space <= dvb_ringbuffer_free(src)) &&
+			(!dvb_dmxdev_events_is_full(events))) {
 			spin_unlock(&dmxdevfilter->dev->lock);
 			return 0;
 		}
@@ -1385,7 +1419,8 @@
 
 		ret = wait_event_interruptible(src->queue,
 				(!src->data) ||
-				(dvb_ringbuffer_free(src) >= required_space) ||
+				((dvb_ringbuffer_free(src) >= required_space) &&
+				 (!dvb_dmxdev_events_is_full(events))) ||
 				(src->error != 0) ||
 				(dmxdevfilter->state != DMXDEV_STATE_GO) ||
 				dmxdevfilter->dev->dvr_in_exit);
@@ -1537,6 +1572,13 @@
 
 	spin_unlock_irq(&dmxdevfilter->dev->lock);
 
+	/*
+	 * in PULL mode, we might be stalling on
+	 * event queue, so need to wake-up waiters
+	 */
+	if (dmxdevfilter->dev->playback_mode == DMX_PB_MODE_PULL)
+		wake_up_all(&dmxdevfilter->buffer.queue);
+
 	return res;
 
 }
@@ -2608,6 +2650,13 @@
 		spin_lock_irq(&dmxdevfilter->dev->lock);
 		dvb_dmxdev_update_events(&dmxdevfilter->events, ret);
 		spin_unlock_irq(&dmxdevfilter->dev->lock);
+
+		/*
+		 * in PULL mode, we might be stalling on
+		 * event queue, so need to wake-up waiters
+		 */
+		if (dmxdevfilter->dev->playback_mode == DMX_PB_MODE_PULL)
+			wake_up_all(&dmxdevfilter->buffer.queue);
 	} else if (ret == -EOVERFLOW) {
 		/*
 		 * When buffer overflowed, demux-dev flushed the
diff --git a/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_tsif.c b/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_tsif.c
index 627c5a2..2e783f6 100644
--- a/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_tsif.c
+++ b/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_tsif.c
@@ -30,15 +30,16 @@
 #define DMX_TSIF_CHUNKS_IN_BUF			16
 #define DMX_TSIF_TIME_LIMIT			10000
 
-/* TSIF_DRIVER_MODE: 3 means manual control from debugfs. use 1 normally. */
-#define DMX_TSIF_DRIVER_MODE_DEF		1
-
+/* TSIF_DRIVER_MODE: 3 means manual control from debugfs. use 2 normally. */
+#define DMX_TSIF_DRIVER_MODE_DEF		2
 
 /* module parameters for load time configuration: */
 static int threshold = DMX_TSIF_PACKETS_IN_CHUNK_DEF;
-static int mode = DMX_TSIF_DRIVER_MODE_DEF;
+static int tsif_mode = DMX_TSIF_DRIVER_MODE_DEF;
+static int clock_inv;
 module_param(threshold, int, S_IRUGO);
-module_param(mode, int, S_IRUGO);
+module_param(tsif_mode, int, S_IRUGO);
+module_param(clock_inv, int, S_IRUGO);
 
 /*
  * Work scheduled each time TSIF notifies dmx
@@ -273,7 +274,6 @@
 		tsif_driver = &(mpq_dmx_tsif_info.tsif[tsif].tsif_driver);
 
 		/* Attach to TSIF driver */
-
 		tsif_driver->tsif_handler =
 			tsif_attach(tsif, mpq_tsif_callback, (void *)tsif);
 		if (IS_ERR_OR_NULL(tsif_driver->tsif_handler)) {
@@ -284,12 +284,19 @@
 			return -ENODEV;
 		}
 
+		ret = tsif_set_clk_inverse(tsif_driver->tsif_handler,
+					clock_inv);
+		if (ret < 0) {
+			MPQ_DVB_ERR_PRINT(
+				"%s: tsif_set_clk_inverse (%d) failed\n",
+				__func__, clock_inv);
+		}
+
 		/* Set TSIF driver mode */
-		ret = tsif_set_mode(tsif_driver->tsif_handler,
-					mode);
+		ret = tsif_set_mode(tsif_driver->tsif_handler, tsif_mode);
 		if (ret < 0) {
 			MPQ_DVB_ERR_PRINT("%s: tsif_set_mode (%d) failed\n",
-				__func__, mode);
+				__func__, tsif_mode);
 		}
 
 		/* Set TSIF buffer configuration */
@@ -304,18 +311,6 @@
 			MPQ_DVB_ERR_PRINT("Using default TSIF driver values\n");
 		}
 
-
-		/* Set TSIF driver time limit */
-		/* TODO: needed?? */
-/*		ret = tsif_set_time_limit(tsif_driver->tsif_handler,
-						DMX_TSIF_TIME_LIMIT);
-		if (ret < 0) {
-			MPQ_DVB_ERR_PRINT(
-				"%s: tsif_set_time_limit (%d) failed\n",
-				__func__, DMX_TSIF_TIME_LIMIT);
-		}
-*/
-
 		/* Start TSIF driver */
 		ret = tsif_start(tsif_driver->tsif_handler);
 		if (ret < 0) {
@@ -705,11 +700,11 @@
 			__func__, DMX_TSIF_PACKETS_IN_CHUNK_DEF);
 		threshold = DMX_TSIF_PACKETS_IN_CHUNK_DEF;
 	}
-	if ((mode < 1) || (mode > 3)) {
+	if ((tsif_mode < 1) || (tsif_mode > 3)) {
 		MPQ_DVB_ERR_PRINT(
 			"%s: invalid mode parameter, using %d instead\n",
 			__func__, DMX_TSIF_DRIVER_MODE_DEF);
-		mode = DMX_TSIF_DRIVER_MODE_DEF;
+		tsif_mode = DMX_TSIF_DRIVER_MODE_DEF;
 	}
 
 	for (i = 0; i < TSIF_COUNT; i++) {
diff --git a/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_tspp_v1.c b/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_tspp_v1.c
index e4f00c0..9fb6004 100644
--- a/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_tspp_v1.c
+++ b/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_tspp_v1.c
@@ -18,15 +18,15 @@
 #include "mpq_dmx_plugin_common.h"
 
 
-#define TSIF_COUNT					2
+#define TSIF_COUNT			2
 
 #define TSPP_MAX_PID_FILTER_NUM		16
 
 /* Max number of section filters */
-#define TSPP_MAX_SECTION_FILTER_NUM		64
+#define TSPP_MAX_SECTION_FILTER_NUM	64
 
 /* For each TSIF we allocate two pipes, one for PES and one for sections */
-#define TSPP_PES_CHANNEL				0
+#define TSPP_PES_CHANNEL			0
 #define TSPP_SECTION_CHANNEL			1
 
 /* the channel_id set to TSPP driver based on TSIF number and channel type */
@@ -35,7 +35,7 @@
 #define TSPP_GET_TSIF_NUM(ch_id)		(ch_id >> 1)
 
 /* mask that set to care for all bits in pid filter */
-#define TSPP_PID_MASK					0x1FFF
+#define TSPP_PID_MASK				0x1FFF
 
 /* dvb-demux defines pid 0x2000 as full capture pid */
 #define TSPP_PASS_THROUGH_PID			0x2000
@@ -53,6 +53,7 @@
  * Meanning about 82 notifications per second.
  */
 #define MAX_BAM_DESCRIPTOR_SIZE		(32*1024 - 1)
+
 #define TSPP_BUFFER_SIZE			\
 	((MAX_BAM_DESCRIPTOR_SIZE / TSPP_RAW_TTS_SIZE) * TSPP_RAW_TTS_SIZE)
 
@@ -65,11 +66,18 @@
 /* Channel timeout in msec */
 #define TSPP_CHANNEL_TIMEOUT			16
 
+enum mem_buffer_allocation_mode {
+	MPQ_DMX_TSPP_INTERNAL_ALLOC = 0,
+	MPQ_DMX_TSPP_CONTIGUOUS_PHYS_ALLOC = 1
+};
+
 /* module parameters for load time configuration */
-static int tsif0_mode = TSPP_TSIF_MODE_2;
-static int tsif1_mode = TSPP_TSIF_MODE_2;
-module_param(tsif0_mode, int, S_IRUGO);
-module_param(tsif1_mode, int, S_IRUGO);
+static int clock_inv;
+static int tsif_mode = 2;
+static int allocation_mode = MPQ_DMX_TSPP_INTERNAL_ALLOC;
+module_param(tsif_mode, int, S_IRUGO);
+module_param(clock_inv, int, S_IRUGO);
+module_param(allocation_mode, int, S_IRUGO);
 
 /*
  * Work scheduled each time TSPP notifies dmx
@@ -97,6 +105,15 @@
 		/* work used to submit to workqueue to process pes channel */
 		struct tspp_work pes_work;
 
+		/* ION handle used for TSPP data buffer allocation */
+		struct ion_handle *pes_mem_heap_handle;
+		/* TSPP data buffer heap virtual base address */
+		void *pes_mem_heap_virt_base;
+		/* TSPP data buffer heap physical base address */
+		ion_phys_addr_t pes_mem_heap_phys_base;
+		/* buffer allocation index */
+		int pes_index;
+
 		/*
 		 * TSPP pipe holding all TS packets with section data.
 		 * The following is reference count for number of feeds
@@ -107,6 +124,15 @@
 		/* work used to submit to workqueue to process pes channel */
 		struct tspp_work section_work;
 
+		/* ION handle used for TSPP data buffer allocation */
+		struct ion_handle *section_mem_heap_handle;
+		/* TSPP data buffer heap virtual base address */
+		void *section_mem_heap_virt_base;
+		/* TSPP data buffer heap physical base address */
+		ion_phys_addr_t section_mem_heap_phys_base;
+		/* buffer allocation index */
+		int section_index;
+
 		/*
 		 * Holds PIDs of allocated TSPP filters along with
 		 * how many feeds are opened on same PID.
@@ -128,8 +154,65 @@
 		/* mutex protecting the data-structure */
 		struct mutex mutex;
 	} tsif[TSIF_COUNT];
+
+	/* ION client used for TSPP data buffer allocation */
+	struct ion_client *ion_client;
 } mpq_dmx_tspp_info;
 
+static void *tspp_mem_allocator(int channel_id, u32 size,
+				u32 *phys_base, void *user)
+{
+	void *virt_addr = NULL;
+	int i = TSPP_GET_TSIF_NUM(channel_id);
+
+	if (TSPP_IS_PES_CHANNEL(channel_id)) {
+		if (mpq_dmx_tspp_info.tsif[i].pes_index == TSPP_BUFFER_COUNT)
+			return NULL;
+		virt_addr =
+			(mpq_dmx_tspp_info.tsif[i].pes_mem_heap_virt_base +
+			(mpq_dmx_tspp_info.tsif[i].pes_index * size));
+		*phys_base =
+			(mpq_dmx_tspp_info.tsif[i].pes_mem_heap_phys_base +
+			(mpq_dmx_tspp_info.tsif[i].pes_index * size));
+		mpq_dmx_tspp_info.tsif[i].pes_index++;
+	} else {
+		if (mpq_dmx_tspp_info.tsif[i].section_index ==
+						TSPP_BUFFER_COUNT)
+			return NULL;
+		virt_addr =
+			(mpq_dmx_tspp_info.tsif[i].section_mem_heap_virt_base +
+			(mpq_dmx_tspp_info.tsif[i].section_index * size));
+		*phys_base =
+			(mpq_dmx_tspp_info.tsif[i].section_mem_heap_phys_base +
+			(mpq_dmx_tspp_info.tsif[i].section_index * size));
+		mpq_dmx_tspp_info.tsif[i].section_index++;
+	}
+
+	return virt_addr;
+}
+
+static void tspp_mem_free(int channel_id, u32 size,
+			void *virt_base, u32 phys_base, void *user)
+{
+	int i = TSPP_GET_TSIF_NUM(channel_id);
+
+	/*
+	 * actual buffer heap free is done in mpq_dmx_tspp_plugin_exit().
+	 * we update index here, so if this function is called repetitively
+	 * for all the buffers, then afterwards tspp_mem_allocator()
+	 * can be called again.
+	 * Note: it would be incorrect to call tspp_mem_allocator()
+	 * a few times, then call tspp_mem_free(), then call
+	 * tspp_mem_allocator() again.
+	 */
+	if (TSPP_IS_PES_CHANNEL(channel_id)) {
+		if (mpq_dmx_tspp_info.tsif[i].pes_index > 0)
+			mpq_dmx_tspp_info.tsif[i].pes_index--;
+	} else {
+		if (mpq_dmx_tspp_info.tsif[i].section_index > 0)
+			mpq_dmx_tspp_info.tsif[i].section_index--;
+	}
+}
 
 /**
  * Returns a free filter slot that can be used.
@@ -272,23 +355,37 @@
 static int mpq_tspp_dmx_add_channel(struct dvb_demux_feed *feed)
 {
 	struct mpq_demux *mpq_demux = feed->demux->priv;
-	enum tspp_source tspp_source;
+	struct tspp_select_source tspp_source;
 	struct tspp_filter tspp_filter;
 	int tsif;
 	int ret;
 	int channel_id;
 	int *channel_ref_count;
-	enum tspp_tsif_mode mode;
+
+	tspp_source.clk_inverse = clock_inv;
+	tspp_source.data_inverse = 0;
+	tspp_source.sync_inverse = 0;
+	tspp_source.enable_inverse = 0;
+
+	switch (tsif_mode) {
+	case 1:
+		tspp_source.mode = TSPP_TSIF_MODE_1;
+		break;
+	case 2:
+		tspp_source.mode = TSPP_TSIF_MODE_2;
+		break;
+	default:
+		tspp_source.mode = TSPP_TSIF_MODE_LOOPBACK;
+		break;
+	}
 
 	/* determine the TSIF we are reading from */
 	if (mpq_demux->source == DMX_SOURCE_FRONT0) {
 		tsif = 0;
-		tspp_source = TSPP_SOURCE_TSIF0;
-		mode = (enum tspp_tsif_mode)tsif0_mode;
+		tspp_source.source = TSPP_SOURCE_TSIF0;
 	} else if (mpq_demux->source == DMX_SOURCE_FRONT1) {
 		tsif = 1;
-		tspp_source = TSPP_SOURCE_TSIF1;
-		mode = (enum tspp_tsif_mode)tsif1_mode;
+		tspp_source.source = TSPP_SOURCE_TSIF1;
 	} else {
 		/* invalid source */
 		MPQ_DVB_ERR_PRINT(
@@ -341,13 +438,13 @@
 		}
 
 		/* set TSPP source */
-		ret = tspp_open_stream(0, channel_id, tspp_source, mode);
+		ret = tspp_open_stream(0, channel_id, &tspp_source);
 		if (ret < 0) {
 			MPQ_DVB_ERR_PRINT(
 				"%s: tspp_select_source(%d,%d) failed (%d)\n",
 				__func__,
 				channel_id,
-				tspp_source,
+				tspp_source.source,
 				ret);
 
 			goto add_channel_close_ch;
@@ -360,20 +457,34 @@
 					   (void *)tsif,
 					   TSPP_CHANNEL_TIMEOUT);
 
-		/* TODO: register allocater and provide allocation function
-		 * that allocate from continous memory so that we can have
+		/* register allocater and provide allocation function
+		 * that allocates from continous memory so that we can have
 		 * big notification size, smallest descriptor, and still provide
 		 * TZ with single big buffer based on notification size.
 		 */
 
-		/* set buffer/descriptor size and count */
-		ret = tspp_allocate_buffers(0,
-					    channel_id,
-					    TSPP_BUFFER_COUNT,
-					    TSPP_BUFFER_SIZE,
-					    TSPP_NOTIFICATION_SIZE,
-					    NULL,
-					    NULL);
+		/* set buffer/descriptor size and count,
+		 * allocate TSPP data buffers
+		 */
+		if (allocation_mode == MPQ_DMX_TSPP_CONTIGUOUS_PHYS_ALLOC) {
+			ret = tspp_allocate_buffers(0,
+						    channel_id,
+						    TSPP_BUFFER_COUNT,
+						    TSPP_BUFFER_SIZE,
+						    TSPP_NOTIFICATION_SIZE,
+						    tspp_mem_allocator,
+						    tspp_mem_free,
+						    NULL);
+		} else {
+			ret = tspp_allocate_buffers(0,
+						    channel_id,
+						    TSPP_BUFFER_COUNT,
+						    TSPP_BUFFER_SIZE,
+						    TSPP_NOTIFICATION_SIZE,
+						    NULL,
+						    NULL,
+						    NULL);
+		}
 		if (ret < 0) {
 			MPQ_DVB_ERR_PRINT(
 				"%s: tspp_allocate_buffers(%d) failed (%d)\n",
@@ -418,7 +529,7 @@
 	 * accordingly.
 	 */
 	tspp_filter.mode = TSPP_MODE_RAW;
-	tspp_filter.source = tspp_source;
+	tspp_filter.source = tspp_source.source;
 	tspp_filter.decrypt = 0;
 	ret = tspp_add_filter(0, channel_id, &tspp_filter);
 	if (ret < 0) {
@@ -553,6 +664,7 @@
 		/* channel is not used any more, release it */
 		tspp_unregister_notification(0, channel_id);
 		tspp_close_channel(0, channel_id);
+		tspp_close_stream(0, channel_id);
 	}
 
 	mutex_unlock(&mpq_dmx_tspp_info.tsif[tsif].mutex);
@@ -730,20 +842,152 @@
 	return 0;
 }
 
+static void mpq_dmx_tsif_ion_cleanup(int i)
+{
+	mpq_dmx_tspp_info.tsif[i].pes_mem_heap_phys_base = 0;
+	mpq_dmx_tspp_info.tsif[i].section_mem_heap_phys_base = 0;
+
+	if (!IS_ERR_OR_NULL(mpq_dmx_tspp_info.tsif[i].pes_mem_heap_handle)) {
+		if (!IS_ERR_OR_NULL(mpq_dmx_tspp_info.tsif[i].
+				pes_mem_heap_virt_base))
+			ion_unmap_kernel(mpq_dmx_tspp_info.ion_client,
+				mpq_dmx_tspp_info.tsif[i].pes_mem_heap_handle);
+
+		ion_free(mpq_dmx_tspp_info.ion_client,
+			mpq_dmx_tspp_info.tsif[i].pes_mem_heap_handle);
+	}
+
+	if (!IS_ERR_OR_NULL(mpq_dmx_tspp_info.tsif[i].
+			section_mem_heap_handle)) {
+		if (!IS_ERR_OR_NULL(mpq_dmx_tspp_info.tsif[i].
+					section_mem_heap_virt_base))
+			ion_unmap_kernel(mpq_dmx_tspp_info.ion_client,
+					mpq_dmx_tspp_info.tsif[i].
+						section_mem_heap_handle);
+
+		ion_free(mpq_dmx_tspp_info.ion_client,
+			mpq_dmx_tspp_info.tsif[i].section_mem_heap_handle);
+	}
+
+	mpq_dmx_tspp_info.tsif[i].pes_mem_heap_virt_base = NULL;
+	mpq_dmx_tspp_info.tsif[i].section_mem_heap_virt_base = NULL;
+	mpq_dmx_tspp_info.tsif[i].pes_mem_heap_handle = NULL;
+	mpq_dmx_tspp_info.tsif[i].section_mem_heap_handle = NULL;
+}
+
+static void mpq_dmx_tspp_ion_cleanup(void)
+{
+	int i;
+
+	for (i = 0; i < TSIF_COUNT; i++)
+		mpq_dmx_tsif_ion_cleanup(i);
+}
+
 static int mpq_tspp_dmx_init(
 			struct dvb_adapter *mpq_adapter,
 			struct mpq_demux *mpq_demux)
 {
-	int result;
+	int i, result;
+	size_t len;
 
 	MPQ_DVB_DBG_PRINT("%s executed\n", __func__);
 
+	if (allocation_mode == MPQ_DMX_TSPP_CONTIGUOUS_PHYS_ALLOC) {
+		/*
+		 * Save ION client, used to allocate memory
+		 * for TSPP's buffers.
+		 */
+		mpq_dmx_tspp_info.ion_client = mpq_demux->ion_client;
+
+		if (IS_ERR_OR_NULL(mpq_dmx_tspp_info.ion_client))
+			return -EINVAL;
+
+		for (i = 0; i < TSIF_COUNT; i++) {
+			mpq_dmx_tspp_info.tsif[i].pes_mem_heap_handle =
+				ion_alloc(mpq_dmx_tspp_info.ion_client,
+					(TSPP_BUFFER_COUNT * TSPP_BUFFER_SIZE),
+					TSPP_RAW_TTS_SIZE,
+					ION_HEAP(ION_CP_MM_HEAP_ID),
+					0); /* non-cached */
+			if (IS_ERR_OR_NULL(mpq_dmx_tspp_info.tsif[i].
+						pes_mem_heap_handle)) {
+				MPQ_DVB_ERR_PRINT("%s: ion_alloc() failed\n",
+						__func__);
+				mpq_dmx_tspp_ion_cleanup();
+				return -ENOMEM;
+			}
+			/* save virtual base address of heap */
+			mpq_dmx_tspp_info.tsif[i].pes_mem_heap_virt_base =
+				ion_map_kernel(mpq_dmx_tspp_info.ion_client,
+					mpq_dmx_tspp_info.tsif[i].
+						pes_mem_heap_handle);
+			if (IS_ERR_OR_NULL(mpq_dmx_tspp_info.tsif[i].
+						pes_mem_heap_virt_base)) {
+				MPQ_DVB_ERR_PRINT(
+					"%s: ion_map_kernel() failed\n",
+					__func__);
+				mpq_dmx_tspp_ion_cleanup();
+				return -ENOMEM;
+			}
+			/* save physical base address of heap */
+			result = ion_phys(mpq_dmx_tspp_info.ion_client,
+				mpq_dmx_tspp_info.tsif[i].pes_mem_heap_handle,
+				&(mpq_dmx_tspp_info.tsif[i].
+					pes_mem_heap_phys_base), &len);
+			if (result < 0) {
+				MPQ_DVB_ERR_PRINT("%s: ion_phys() failed\n",
+						__func__);
+				mpq_dmx_tspp_ion_cleanup();
+				return -ENOMEM;
+			}
+
+			mpq_dmx_tspp_info.tsif[i].section_mem_heap_handle =
+				ion_alloc(mpq_dmx_tspp_info.ion_client,
+					(TSPP_BUFFER_COUNT * TSPP_BUFFER_SIZE),
+					TSPP_RAW_TTS_SIZE,
+					ION_HEAP(ION_CP_MM_HEAP_ID),
+					0); /* non-cached */
+			if (IS_ERR_OR_NULL(mpq_dmx_tspp_info.tsif[i].
+						section_mem_heap_handle)) {
+				MPQ_DVB_ERR_PRINT("%s: ion_alloc() failed\n",
+						__func__);
+				mpq_dmx_tspp_ion_cleanup();
+				return -ENOMEM;
+			}
+			/* save virtual base address of heap */
+			mpq_dmx_tspp_info.tsif[i].section_mem_heap_virt_base =
+				ion_map_kernel(mpq_dmx_tspp_info.ion_client,
+				mpq_dmx_tspp_info.tsif[i].
+					section_mem_heap_handle);
+			if (IS_ERR_OR_NULL(mpq_dmx_tspp_info.tsif[i].
+					section_mem_heap_virt_base)) {
+				MPQ_DVB_ERR_PRINT(
+					"%s: ion_map_kernel() failed\n",
+					__func__);
+				mpq_dmx_tspp_ion_cleanup();
+				return -ENOMEM;
+			}
+			/* save physical base address of heap */
+			result = ion_phys(mpq_dmx_tspp_info.ion_client,
+				mpq_dmx_tspp_info.tsif[i].
+					section_mem_heap_handle,
+				&(mpq_dmx_tspp_info.tsif[i].
+					section_mem_heap_phys_base), &len);
+			if (result < 0) {
+				MPQ_DVB_ERR_PRINT("%s: ion_phys() failed\n",
+						__func__);
+				mpq_dmx_tspp_ion_cleanup();
+				return -ENOMEM;
+			}
+		}
+	}
+
 	/* Set the kernel-demux object capabilities */
 	mpq_demux->demux.dmx.capabilities =
 		DMX_TS_FILTERING			|
 		DMX_PES_FILTERING			|
-		DMX_SECTION_FILTERING		|
-		DMX_MEMORY_BASED_FILTERING	|
+		DMX_SECTION_FILTERING			|
+		DMX_MEMORY_BASED_FILTERING		|
 		DMX_CRC_CHECKING			|
 		DMX_TS_DESCRAMBLING;
 
@@ -803,6 +1047,7 @@
 init_failed_dmx_release:
 	dvb_dmx_release(&mpq_demux->demux);
 init_failed:
+	mpq_dmx_tspp_ion_cleanup();
 	return result;
 }
 
@@ -816,6 +1061,10 @@
 
 	for (i = 0; i < TSIF_COUNT; i++) {
 		mpq_dmx_tspp_info.tsif[i].pes_channel_ref = 0;
+		mpq_dmx_tspp_info.tsif[i].pes_index = 0;
+		mpq_dmx_tspp_info.tsif[i].pes_mem_heap_handle = NULL;
+		mpq_dmx_tspp_info.tsif[i].pes_mem_heap_virt_base = NULL;
+		mpq_dmx_tspp_info.tsif[i].pes_mem_heap_phys_base = 0;
 
 		mpq_dmx_tspp_info.tsif[i].pes_work.channel_id =
 			TSPP_CHANNEL_ID(i, TSPP_PES_CHANNEL);
@@ -824,6 +1073,10 @@
 				  mpq_dmx_tspp_work);
 
 		mpq_dmx_tspp_info.tsif[i].section_channel_ref = 0;
+		mpq_dmx_tspp_info.tsif[i].section_index = 0;
+		mpq_dmx_tspp_info.tsif[i].section_mem_heap_handle = NULL;
+		mpq_dmx_tspp_info.tsif[i].section_mem_heap_virt_base = NULL;
+		mpq_dmx_tspp_info.tsif[i].section_mem_heap_phys_base = 0;
 
 		mpq_dmx_tspp_info.tsif[i].section_work.channel_id =
 			TSPP_CHANNEL_ID(i, TSPP_SECTION_CHANNEL);
@@ -846,14 +1099,12 @@
 				mpq_dmx_tspp_info.tsif[i].name);
 
 		if (mpq_dmx_tspp_info.tsif[i].workqueue == NULL) {
-
 			for (j = 0; j < i; j++) {
 				destroy_workqueue(
 					mpq_dmx_tspp_info.tsif[j].workqueue);
 
 				mutex_destroy(&mpq_dmx_tspp_info.tsif[j].mutex);
 			}
-
 			MPQ_DVB_ERR_PRINT(
 				"%s: create_singlethread_workqueue failed\n",
 				__func__);
@@ -890,6 +1141,11 @@
 	for (i = 0; i < TSIF_COUNT; i++) {
 		mutex_lock(&mpq_dmx_tspp_info.tsif[i].mutex);
 
+		/*
+		 * Note: tspp_close_channel will also free the TSPP buffers
+		 * even if we allocated them ourselves,
+		 * using our free function.
+		 */
 		if (mpq_dmx_tspp_info.tsif[i].pes_channel_ref) {
 			tspp_unregister_notification(0, TSPP_PES_CHANNEL);
 			tspp_close_channel(0,
@@ -902,9 +1158,11 @@
 				TSPP_CHANNEL_ID(i, TSPP_SECTION_CHANNEL));
 		}
 
-		/* TODO: if we allocate buffer
-		 * to TSPP ourself, need to free those as well
+		/* if we allocated buffer pools
+		 * to TSPP, need to free those as well
 		 */
+		if (allocation_mode == MPQ_DMX_TSPP_CONTIGUOUS_PHYS_ALLOC)
+			mpq_dmx_tsif_ion_cleanup(i);
 
 		mutex_unlock(&mpq_dmx_tspp_info.tsif[i].mutex);
 		flush_workqueue(mpq_dmx_tspp_info.tsif[i].workqueue);
diff --git a/drivers/media/dvb/mpq/video/mpq_dvb_video.c b/drivers/media/dvb/mpq/video/mpq_dvb_video.c
index bd8c4a4..229a81a 100644
--- a/drivers/media/dvb/mpq/video/mpq_dvb_video.c
+++ b/drivers/media/dvb/mpq/video/mpq_dvb_video.c
@@ -53,6 +53,7 @@
 				"dvb-vid-3",
 };
 
+static enum scan_format map_scan_type(enum vdec_interlaced_format type);
 static int mpq_int_vid_dec_decode_frame(struct video_client_ctx *client_ctx,
 				struct video_data_buffer *input_frame);
 static int mpq_int_vid_dec_get_buffer_req(struct video_client_ctx *client_ctx,
@@ -846,7 +847,6 @@
 {
 	struct vdec_picsize pic_res;
 	int rc;
-	struct video_buffer_req vdec_buf_req;
 
 	pic_res.frame_height = 1080;
 	pic_res.frame_width  = 1920;
@@ -864,15 +864,6 @@
 		DBG("Failed in mpq_int_vid_dec_set_cont_on_reconfig : %d\n",\
 			rc);
 
-	rc = mpq_int_vid_dec_get_buffer_req(client_ctx, &vdec_buf_req);
-	if (rc)
-		DBG("Failed in mpq_int_vid_dec_get_buffer_req : %d\n", rc);
-
-	vdec_buf_req.num_output_buffers = 12;
-	rc = mpq_int_set_out_buffer_req(client_ctx, &vdec_buf_req);
-	if (rc)
-		DBG("Failed in mpq_int_set_out_buffer_req (15) : %d\n", rc);
-
 	return rc;
 
 }
@@ -1285,6 +1276,27 @@
 	return 0;
 }
 
+static int mpq_int_vid_dec_set_buffer_req(struct video_client_ctx *client_ctx,
+				  struct video_buffer_req vdec_buf_req)
+{
+	int rc = 0;
+	struct video_buffer_req vdec_req;
+
+	rc = mpq_int_vid_dec_get_buffer_req(client_ctx, &vdec_req);
+	if (rc)
+		DBG("Failed in mpq_int_vid_dec_get_buffer_req : %d\n", rc);
+
+	vdec_req.num_output_buffers = vdec_buf_req.num_output_buffers;
+	DBG(" num_output_buffers Set to %u\n", vdec_buf_req.num_output_buffers);
+	if (!vdec_buf_req.num_output_buffers)
+		return -EINVAL;
+	rc = mpq_int_set_out_buffer_req(client_ctx, &vdec_req);
+	if (rc)
+		DBG("Failed in mpq_int_set_out_buffer_req  %d\n", rc);
+
+	return 0;
+}
+
 static int mpq_int_vid_dec_set_buffer(struct mpq_dvb_video_inst *dev_inst,
 			      struct video_data_buffer *data_buffer,
 			      enum buffer_dir dir_buffer)
@@ -2006,6 +2018,10 @@
 		DBG("cmd : VIDEO_CMD_GET_BUFFER_REQ\n");
 		rc = mpq_int_vid_dec_get_buffer_req(client_ctx, &cmd->buf_req);
 		break;
+	case VIDEO_CMD_SET_BUFFER_COUNT:
+		DBG("cmd : VIDEO_CMD_SET_BUFFER_COUNT\n");
+		rc = mpq_int_vid_dec_set_buffer_req(client_ctx, cmd->buf_req);
+		break;
 	case VIDEO_CMD_READ_RAW_OUTPUT:
 		DBG("cmd : VIDEO_CMD_READ_RAW_OUTPUT\n");
 		rc = mpq_int_vid_dec_fill_output_buffer(client_ctx,
@@ -2070,7 +2086,7 @@
 				struct video_event *ev)
 {
 	int rc;
-	struct vdec_msginfo vdec_msg_info;
+	struct vdec_msginfo vdec_msg_info = {};
 
 	memset(ev, 0, sizeof(struct video_event));
 
@@ -2105,6 +2121,8 @@
 				vdec_msg_info.msgdata.output_frame.time_stamp;
 		ev->u.buffer.offset      =
 				vdec_msg_info.msgdata.output_frame.offset;
+		ev->u.buffer.interlaced_format = map_scan_type(vdec_msg_info.\
+				msgdata.output_frame.interlaced_format);
 		break;
 	case VDEC_MSG_RESP_START_DONE:
 		DBG("VIDEO_EVENT_DECODER_PLAYING\n");
@@ -2146,6 +2164,8 @@
 				vdec_msg_info.msgdata.output_frame.time_stamp;
 		ev->u.buffer.offset      =
 				vdec_msg_info.msgdata.output_frame.offset;
+		ev->u.buffer.interlaced_format = map_scan_type(vdec_msg_info.\
+				msgdata.output_frame.interlaced_format);
 		break;
 	case VDEC_MSG_RESP_FLUSH_INPUT_DONE:
 		DBG("VIDEO_EVENT_INPUT_FLUSH_DONE\n");
@@ -2161,6 +2181,17 @@
 	return 0;
 }
 
+static enum scan_format map_scan_type(enum vdec_interlaced_format type)
+{
+	if (type == VDEC_InterlaceFrameProgressive)
+		return INTERLACE_FRAME_PROGRESSIVE;
+	if (type == VDEC_InterlaceInterleaveFrameTopFieldFirst)
+		return INTERLACE_INTERLEAVE_FRAME_TOP_FIELD_FIRST;
+	if (type == VDEC_InterlaceInterleaveFrameBottomFieldFirst)
+		return INTERLACE_INTERLEAVE_FRAME_BOTTOM_FIELD_FIRST;
+	return INTERLACE_FRAME_PROGRESSIVE;
+}
+
 static int mpq_dvb_video_play(struct mpq_dvb_video_inst *dev_inst)
 {
 	return mpq_int_vid_dec_start_stop(dev_inst->client_ctx, true);
diff --git a/drivers/media/radio/radio-iris.c b/drivers/media/radio/radio-iris.c
index ac143b1..fde7cb7 100644
--- a/drivers/media/radio/radio-iris.c
+++ b/drivers/media/radio/radio-iris.c
@@ -39,6 +39,17 @@
 #include <asm/unaligned.h>
 
 static unsigned int rds_buf = 100;
+static int oda_agt;
+static int grp_mask;
+static int rt_plus_carrier = -1;
+static int ert_carrier = -1;
+static unsigned char ert_buf[256];
+static unsigned char ert_len;
+static unsigned char c_byt_pair_index;
+static char utf_8_flag;
+static char rt_ert_flag;
+static char formatting_dir;
+
 module_param(rds_buf, uint, 0);
 MODULE_PARM_DESC(rds_buf, "RDS buffer entries: *100*");
 
@@ -108,7 +119,11 @@
 
 static struct video_device *priv_videodev;
 static int iris_do_calibration(struct iris_device *radio);
-
+static void hci_buff_ert(struct iris_device *radio,
+		struct rds_grp_data *rds_buf);
+static void hci_ev_rt_plus(struct iris_device *radio,
+		struct rds_grp_data rds_buf);
+static void hci_ev_ert(struct iris_device *radio);
 static int update_spur_table(struct iris_device *radio);
 static struct v4l2_queryctrl iris_v4l2_queryctrl[] = {
 	{
@@ -921,6 +936,20 @@
 	return radio_hci_send_cmd(hdev, opcode, 0, NULL);
 }
 
+static int hci_fm_rds_grp_mask_req(struct radio_hci_dev *hdev,
+		unsigned long param)
+{
+	 __u16 opcode = 0;
+
+	struct hci_fm_rds_grp_req *fm_grp_mask =
+		(struct hci_fm_rds_grp_req *)param;
+
+	opcode = hci_opcode_pack(HCI_OGF_FM_RECV_CTRL_CMD_REQ,
+		HCI_OCF_FM_RDS_GRP);
+	return radio_hci_send_cmd(hdev, opcode, sizeof(*fm_grp_mask),
+		fm_grp_mask);
+}
+
 static int hci_fm_rds_grp_process_req(struct radio_hci_dev *hdev,
 		unsigned long param)
 {
@@ -1313,7 +1342,13 @@
 static int hci_fm_rds_grp(struct hci_fm_rds_grp_req *arg,
 	struct radio_hci_dev *hdev)
 {
-	return 0;
+	int ret = 0;
+	struct hci_fm_rds_grp_req *fm_grp_mask = arg;
+
+	ret = radio_hci_request(hdev, hci_fm_rds_grp_mask_req, (unsigned
+		long)fm_grp_mask, RADIO_HCI_TIMEOUT);
+
+	return ret;
 }
 
 static int hci_fm_rds_grps_process(__u32 *arg, struct radio_hci_dev *hdev)
@@ -2078,6 +2113,234 @@
 		iris_q_event(radio, IRIS_EVT_MONO);
 }
 
+static void hci_ev_raw_rds_group_data(struct radio_hci_dev *hdev,
+		struct sk_buff *skb)
+{
+	struct iris_device *radio;
+	unsigned char blocknum, index;
+	struct rds_grp_data temp;
+	unsigned int mask_bit;
+	unsigned short int aid, agt, gtc;
+	unsigned short int carrier;
+
+	radio = video_get_drvdata(video_get_dev());
+	index = RDSGRP_DATA_OFFSET;
+
+	for (blocknum = 0; blocknum < RDS_BLOCKS_NUM; blocknum++) {
+		temp.rdsBlk[blocknum].rdsLsb =
+			(skb->data[index]);
+		temp.rdsBlk[blocknum].rdsMsb =
+			(skb->data[index+1]);
+		index = index + 2;
+	}
+
+	aid = AID(temp.rdsBlk[3].rdsLsb, temp.rdsBlk[3].rdsMsb);
+	gtc = GTC(temp.rdsBlk[1].rdsMsb);
+	agt = AGT(temp.rdsBlk[1].rdsLsb);
+
+	if (gtc == GRP_3A) {
+		switch (aid) {
+		case ERT_AID:
+			/* calculate the grp mask for RDS grp
+			 * which will contain actual eRT text
+			 *
+			 * Bit Pos  0  1  2  3  4   5  6   7
+			 * Grp Type 0A 0B 1A 1B 2A  2B 3A  3B
+			 *
+			 * similary for rest grps
+			 */
+			mask_bit = (((agt >> 1) << 1) + (agt & 1));
+			oda_agt = (1 << mask_bit);
+			utf_8_flag = (temp.rdsBlk[2].rdsLsb & 1);
+			formatting_dir = EXTRACT_BIT(temp.rdsBlk[2].rdsLsb,
+							ERT_FORMAT_DIR_BIT);
+			if (ert_carrier != agt)
+				iris_q_event(radio, IRIS_EVT_NEW_ODA);
+			ert_carrier = agt;
+			break;
+		case RT_PLUS_AID:
+			/* calculate the grp mask for RDS grp
+			 * which will contain actual eRT text
+			 *
+			 * Bit Pos  0  1  2  3  4   5  6   7
+			 * Grp Type 0A 0B 1A 1B 2A  2B 3A  3B
+			 *
+			 * similary for rest grps
+			 */
+			mask_bit = (((agt >> 1) << 1) + (agt & 1));
+			oda_agt =  (1 << mask_bit);
+			/*Extract 5th bit of MSB (b7b6b5b4b3b2b1b0)*/
+			rt_ert_flag = EXTRACT_BIT(temp.rdsBlk[2].rdsMsb,
+					 RT_ERT_FLAG_BIT);
+			if (rt_plus_carrier != agt)
+				iris_q_event(radio, IRIS_EVT_NEW_ODA);
+			rt_plus_carrier = agt;
+			break;
+		default:
+			oda_agt = 0;
+			break;
+		}
+	} else {
+		carrier = gtc;
+		if ((carrier == rt_plus_carrier))
+			hci_ev_rt_plus(radio, temp);
+		else if (carrier == ert_carrier)
+			hci_buff_ert(radio, &temp);
+	}
+}
+
+static void hci_buff_ert(struct iris_device *radio,
+	struct rds_grp_data *rds_buf)
+{
+	int i;
+	unsigned short int info_byte = 0;
+	unsigned short int byte_pair_index;
+
+	byte_pair_index = AGT(rds_buf->rdsBlk[1].rdsLsb);
+	if (byte_pair_index == 0) {
+		c_byt_pair_index = 0;
+		ert_len = 0;
+	}
+	if (c_byt_pair_index == byte_pair_index) {
+		c_byt_pair_index++;
+		for (i = 2; i <= 3; i++) {
+			info_byte = rds_buf->rdsBlk[i].rdsLsb;
+			info_byte |= (rds_buf->rdsBlk[i].rdsMsb << 8);
+			ert_buf[ert_len++] = rds_buf->rdsBlk[i].rdsMsb;
+			ert_buf[ert_len++] = rds_buf->rdsBlk[i].rdsLsb;
+			if ((utf_8_flag == 0)
+				 && (info_byte == CARRIAGE_RETURN)) {
+				ert_len -= 2;
+				break;
+			} else if ((utf_8_flag == 1)
+					&&
+					(rds_buf->rdsBlk[i].rdsMsb
+						 == CARRIAGE_RETURN)) {
+				info_byte = CARRIAGE_RETURN;
+				ert_len -= 2;
+				break;
+			} else if ((utf_8_flag == 1)
+					&&
+					(rds_buf->rdsBlk[i].rdsLsb
+						 == CARRIAGE_RETURN)) {
+				info_byte = CARRIAGE_RETURN;
+				ert_len--;
+				break;
+			}
+		}
+		if ((byte_pair_index == MAX_ERT_SEGMENT) ||
+			(info_byte == CARRIAGE_RETURN)) {
+			hci_ev_ert(radio);
+			c_byt_pair_index = 0;
+			ert_len = 0;
+		}
+	} else {
+		ert_len = 0;
+		c_byt_pair_index = 0;
+	}
+}
+static void hci_ev_ert(struct iris_device *radio)
+
+{
+	char *data = NULL;
+
+	if (ert_len <= 0)
+		return;
+	data = kmalloc((ert_len + 3), GFP_ATOMIC);
+	if (data != NULL) {
+		data[0] = ert_len;
+		data[1] = utf_8_flag;
+		data[2] = formatting_dir;
+		memcpy((data + 3), ert_buf, ert_len);
+		iris_q_evt_data(radio, data, (ert_len + 3), IRIS_BUF_ERT);
+		iris_q_event(radio, IRIS_EVT_NEW_ERT);
+		kfree(data);
+	}
+}
+
+static void hci_ev_rt_plus(struct iris_device *radio,
+		 struct rds_grp_data rds_buf)
+{
+	char tag_type1, tag_type2;
+	char *data = NULL;
+	int len = 0;
+	unsigned short int agt;
+
+	agt = AGT(rds_buf.rdsBlk[1].rdsLsb);
+	/*right most 3 bits of Lsb of block 2
+	 * and left most 3 bits of Msb of block 3
+	 */
+	tag_type1 = (((agt & TAG1_MSB_MASK) << TAG1_MSB_OFFSET) |
+			 (rds_buf.rdsBlk[2].rdsMsb >> TAG1_LSB_OFFSET));
+
+	/*right most 1 bit of lsb of 3rd block
+	 * and left most 5 bits of Msb of 4th block
+	*/
+	tag_type2 = (((rds_buf.rdsBlk[2].rdsLsb & TAG2_MSB_MASK)
+			 << TAG2_MSB_OFFSET) |
+			 (rds_buf.rdsBlk[3].rdsMsb >> TAG2_LSB_OFFSET));
+
+	if (tag_type1 != DUMMY_CLASS)
+		len += RT_PLUS_LEN_1_TAG;
+	if (tag_type2 != DUMMY_CLASS)
+		len += RT_PLUS_LEN_1_TAG;
+
+	if (len != 0) {
+		len += 2;
+		data = kmalloc(len, GFP_ATOMIC);
+	} else {
+		FMDERR("Len is zero\n");
+		return ;
+	}
+	if (data != NULL) {
+		data[0] = len;
+		len = 1;
+		data[len++] = rt_ert_flag;
+		if (tag_type1 != DUMMY_CLASS) {
+			data[len++] = tag_type1;
+			/*start position of tag1
+			 *right most 5 bits of msb of 3rd block
+			 *and left most bit of lsb of 3rd block
+			 */
+			data[len++] = (((rds_buf.rdsBlk[2].rdsMsb &
+						 TAG1_POS_MSB_MASK)
+						<< TAG1_POS_MSB_OFFSET)
+						|
+					(rds_buf.rdsBlk[2].rdsLsb >>
+						TAG1_POS_LSB_OFFSET));
+			/*length of tag1
+			 *left most 6 bits of lsb of 3rd block
+			 */
+			data[len++] = ((rds_buf.rdsBlk[2].rdsLsb
+						>> TAG1_LEN_OFFSET)
+							 &
+						TAG1_LEN_MASK) + 1;
+		}
+		if (tag_type2 != DUMMY_CLASS) {
+			data[len++] = tag_type2;
+			/*start position of tag2
+			 *right most 3 bit of msb of 4th block
+			 *and left most 3 bits of lsb of 4th block
+			 */
+			data[len++] = (((rds_buf.rdsBlk[3].rdsMsb
+						& TAG2_POS_MSB_MASK)
+						<< TAG2_POS_MSB_OFFSET)
+						|
+					(rds_buf.rdsBlk[3].rdsLsb
+						>> TAG2_POS_LSB_OFFSET));
+			/*length of tag2
+			 *right most 5 bits of lsb of 4th block
+			 */
+			data[len++] = (rds_buf.rdsBlk[3].rdsLsb
+						& TAG2_LEN_MASK) + 1;
+		}
+		iris_q_evt_data(radio, data, len, IRIS_BUF_RT_PLUS);
+		iris_q_event(radio,  IRIS_EVT_NEW_RT_PLUS);
+		kfree(data);
+	} else {
+		FMDERR("memory allocation failed\n");
+	}
+}
 
 static inline void hci_ev_program_service(struct radio_hci_dev *hdev,
 		struct sk_buff *skb)
@@ -2217,6 +2480,7 @@
 		hci_ev_service_available(hdev, skb);
 		break;
 	case HCI_EV_RDS_RX_DATA:
+		hci_ev_raw_rds_group_data(hdev, skb);
 		break;
 	case HCI_EV_PROGRAM_SERVICE:
 		hci_ev_program_service(hdev, skb);
@@ -2984,8 +3248,13 @@
 		}
 		break;
 	case V4L2_CID_PRIVATE_IRIS_RDSGROUP_MASK:
-		radio->rds_grp.rds_grp_enable_mask = ctrl->value;
+		grp_mask = (grp_mask | oda_agt | ctrl->value);
+		radio->rds_grp.rds_grp_enable_mask = grp_mask;
+		radio->rds_grp.rds_buf_size = 1;
+		radio->rds_grp.en_rds_change_filter = 0;
 		retval = hci_fm_rds_grp(&radio->rds_grp, radio->fm_hdev);
+		if (retval < 0)
+			FMDERR("error in setting group mask\n");
 		break;
 	case V4L2_CID_PRIVATE_IRIS_RDSGROUP_PROC:
 		rds_grps_proc = radio->g_rds_grp_proc_ps | ctrl->value;
diff --git a/drivers/media/rc/gpio-ir-recv.c b/drivers/media/rc/gpio-ir-recv.c
index d7dc67d..8a99968 100644
--- a/drivers/media/rc/gpio-ir-recv.c
+++ b/drivers/media/rc/gpio-ir-recv.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -18,17 +18,35 @@
 #include <linux/slab.h>
 #include <linux/platform_device.h>
 #include <linux/irq.h>
+#include <linux/pm_qos.h>
+#include <linux/timer.h>
 #include <media/rc-core.h>
 #include <media/gpio-ir-recv.h>
 
 #define GPIO_IR_DRIVER_NAME	"gpio-rc-recv"
 #define GPIO_IR_DEVICE_NAME	"gpio_ir_recv"
 
+static int gpio_ir_timeout = 200;
+module_param_named(gpio_ir_timeout, gpio_ir_timeout, int, 0664);
+
+static int __init gpio_ir_timeout_setup(char *p)
+{
+	gpio_ir_timeout = memparse(p, NULL);
+	return 0;
+}
+
+early_param("gpio_ir_timeout", gpio_ir_timeout_setup);
+
 struct gpio_rc_dev {
 	struct rc_dev *rcdev;
+	struct pm_qos_request pm_qos_req;
+	struct timer_list gpio_ir_timer;
 	unsigned int gpio_nr;
 	bool active_low;
 	int can_sleep;
+	bool can_wakeup;
+	bool pm_qos_vote;
+	int gpio_irq_latency;
 };
 
 static irqreturn_t gpio_ir_recv_irq(int irq, void *dev_id)
@@ -38,6 +56,12 @@
 	int rc = 0;
 	enum raw_event_type type = IR_SPACE;
 
+	if (!gpio_dev->pm_qos_vote && gpio_dev->can_wakeup) {
+		gpio_dev->pm_qos_vote = 1;
+		pm_qos_update_request(&gpio_dev->pm_qos_req,
+					 gpio_dev->gpio_irq_latency);
+	}
+
 	if (gpio_dev->can_sleep)
 		gval = gpio_get_value_cansleep(gpio_dev->gpio_nr);
 	else
@@ -58,10 +82,22 @@
 
 	ir_raw_event_handle(gpio_dev->rcdev);
 
+	if (gpio_dev->can_wakeup)
+		mod_timer(&gpio_dev->gpio_ir_timer,
+				jiffies + msecs_to_jiffies(gpio_ir_timeout));
 err_get_value:
 	return IRQ_HANDLED;
 }
 
+static void gpio_ir_timer(unsigned long data)
+{
+	struct gpio_rc_dev *gpio_dev = (struct gpio_rc_dev *)data;
+
+	pm_qos_update_request(&gpio_dev->pm_qos_req, PM_QOS_DEFAULT_VALUE);
+	pm_qos_request_active(&gpio_dev->pm_qos_req);
+	gpio_dev->pm_qos_vote = 0;
+}
+
 static int __devinit gpio_ir_recv_probe(struct platform_device *pdev)
 {
 	struct gpio_rc_dev *gpio_dev;
@@ -96,6 +132,9 @@
 	gpio_dev->rcdev = rcdev;
 	gpio_dev->gpio_nr = pdata->gpio_nr;
 	gpio_dev->active_low = pdata->active_low;
+	gpio_dev->can_wakeup = pdata->can_wakeup;
+	gpio_dev->gpio_irq_latency = pdata->swfi_latency + 1;
+	gpio_dev->pm_qos_vote = 0;
 
 	rc = gpio_request(pdata->gpio_nr, "gpio-ir-recv");
 	if (rc < 0)
@@ -122,7 +161,14 @@
 	if (rc < 0)
 		goto err_request_irq;
 
-	device_init_wakeup(&pdev->dev, pdata->can_wakeup);
+	if (gpio_dev->can_wakeup) {
+		pm_qos_add_request(&gpio_dev->pm_qos_req,
+					PM_QOS_CPU_DMA_LATENCY,
+					PM_QOS_DEFAULT_VALUE);
+		device_init_wakeup(&pdev->dev, pdata->can_wakeup);
+		setup_timer(&gpio_dev->gpio_ir_timer, gpio_ir_timer,
+						(unsigned long)gpio_dev);
+	}
 
 	return 0;
 
@@ -144,6 +190,10 @@
 {
 	struct gpio_rc_dev *gpio_dev = platform_get_drvdata(pdev);
 
+	if (gpio_dev->can_wakeup) {
+		del_timer_sync(&gpio_dev->gpio_ir_timer);
+		pm_qos_remove_request(&gpio_dev->pm_qos_req);
+	}
 	free_irq(gpio_to_irq(gpio_dev->gpio_nr), gpio_dev);
 	platform_set_drvdata(pdev, NULL);
 	rc_unregister_device(gpio_dev->rcdev);
diff --git a/drivers/media/video/msm/Kconfig b/drivers/media/video/msm/Kconfig
index e9b4e2b..be9c43c 100644
--- a/drivers/media/video/msm/Kconfig
+++ b/drivers/media/video/msm/Kconfig
@@ -208,6 +208,15 @@
 	  two mipi lanes, required for msm8625 platform.
 	  Say Y here if this is msm8625 variant platform.
 
+config IMX135
+	bool "Sensor imx135 (Sony 13MP)"
+	depends on MSM_CAMERA
+	---help---
+	  Support for IMX135 sensor driver.
+	  This is a Sony 13MP Bayer Sensor with autofocus and video HDR
+	  support.
+	  Say Y if the platform uses IMX135 sensor.
+
 config VB6801
 	bool "Sensor vb6801"
 	depends on MSM_CAMERA && !ARCH_MSM8X60 && !MSM_CAMERA_V4L2
diff --git a/drivers/media/video/msm/jpeg_10/msm_jpeg_core.c b/drivers/media/video/msm/jpeg_10/msm_jpeg_core.c
index b67245c..a2fc813 100644
--- a/drivers/media/video/msm/jpeg_10/msm_jpeg_core.c
+++ b/drivers/media/video/msm/jpeg_10/msm_jpeg_core.c
@@ -159,7 +159,7 @@
 void *msm_jpeg_core_err_irq(int jpeg_irq_status,
 	struct msm_jpeg_device *pgmn_dev)
 {
-	JPEG_PR_ERR("%s:%d]\n", __func__, jpeg_irq_status);
+	JPEG_PR_ERR("%s: Error %x\n", __func__, jpeg_irq_status);
 	return NULL;
 }
 
@@ -211,6 +211,7 @@
 
 	if (msm_jpeg_hw_irq_is_frame_done(jpeg_irq_status)) {
 		/* send fe ping pong irq */
+		JPEG_DBG_HIGH("%s:%d] Session done\n", __func__, __LINE__);
 		data = msm_jpeg_core_fe_pingpong_irq(jpeg_irq_status,
 			pgmn_dev);
 		if (msm_jpeg_irq_handler)
diff --git a/drivers/media/video/msm/jpeg_10/msm_jpeg_hw.c b/drivers/media/video/msm/jpeg_10/msm_jpeg_hw.c
index e311e4c..c38771b 100644
--- a/drivers/media/video/msm/jpeg_10/msm_jpeg_hw.c
+++ b/drivers/media/video/msm/jpeg_10/msm_jpeg_hw.c
@@ -132,6 +132,10 @@
 		JPEG_PLN1_RD_OFFSET_BMSK, {0} },
 	{MSM_JPEG_HW_CMD_TYPE_WRITE, 1, JPEG_PLN1_RD_PNTR_ADDR,
 		JPEG_PLN1_RD_PNTR_BMSK, {0} },
+	{MSM_JPEG_HW_CMD_TYPE_WRITE, 1, JPEG_PLN2_RD_OFFSET_ADDR,
+		JPEG_PLN1_RD_OFFSET_BMSK, {0} },
+	{MSM_JPEG_HW_CMD_TYPE_WRITE, 1, JPEG_PLN2_RD_PNTR_ADDR,
+		JPEG_PLN2_RD_PNTR_BMSK, {0} },
 };
 
 void msm_jpeg_hw_fe_buffer_update(struct msm_jpeg_hw_buf *p_input,
@@ -156,7 +160,11 @@
 		hw_cmd_p->data = p_input->cbcr_buffer_addr;
 		msm_jpeg_hw_write(hw_cmd_p++, base);
 		wmb();
-
+		msm_jpeg_hw_write(hw_cmd_p++, base);
+		wmb();
+		hw_cmd_p->data = p_input->pln2_addr;
+		msm_jpeg_hw_write(hw_cmd_p++, base);
+		wmb();
 	}
 	return;
 }
@@ -215,6 +223,7 @@
 		JPEG_PR_ERR("%s Output pln1 buffer address is %x\n", __func__,
 			p_input->cbcr_buffer_addr);
 		msm_jpeg_hw_write(hw_cmd_p++, base);
+		hw_cmd_p->data = p_input->pln2_addr;
 		JPEG_PR_ERR("%s Output pln2 buffer address is %x\n", __func__,
 			p_input->pln2_addr);
 		msm_jpeg_hw_write(hw_cmd_p++, base);
diff --git a/drivers/media/video/msm/jpeg_10/msm_jpeg_hw.h b/drivers/media/video/msm/jpeg_10/msm_jpeg_hw.h
index e90b941..084e36b 100644
--- a/drivers/media/video/msm/jpeg_10/msm_jpeg_hw.h
+++ b/drivers/media/video/msm/jpeg_10/msm_jpeg_hw.h
@@ -29,7 +29,7 @@
 	uint32_t num_of_mcu_rows;
 	struct ion_handle *handle;
 	uint32_t pln2_addr;
-	uint32_t pln2_offset;
+	uint32_t pln2_len;
 };
 
 struct msm_jpeg_hw_pingpong {
diff --git a/drivers/media/video/msm/jpeg_10/msm_jpeg_hw_reg.h b/drivers/media/video/msm/jpeg_10/msm_jpeg_hw_reg.h
index 928d59e..ff99aa3 100644
--- a/drivers/media/video/msm/jpeg_10/msm_jpeg_hw_reg.h
+++ b/drivers/media/video/msm/jpeg_10/msm_jpeg_hw_reg.h
@@ -72,6 +72,12 @@
 #define JPEG_PLN1_RD_OFFSET_ADDR 0x00000048
 #define JPEG_PLN1_RD_OFFSET_BMSK 0x1FFFFFFF
 
+#define JPEG_PLN2_RD_PNTR_ADDR (JPEG_REG_BASE + 0x00000050)
+#define JPEG_PLN2_RD_PNTR_BMSK 0xFFFFFFFF
+
+#define JPEG_PLN2_RD_OFFSET_ADDR 0x00000054
+#define JPEG_PLN2_RD_OFFSET_BMSK 0x1FFFFFFF
+
 #define JPEG_CMD_ADDR (JPEG_REG_BASE + 0x00000010)
 #define JPEG_CMD_BMSK 0x00000FFF
 #define JPEG_CMD_CLEAR_WRITE_PLN_QUEUES 0x700
diff --git a/drivers/media/video/msm/jpeg_10/msm_jpeg_sync.c b/drivers/media/video/msm/jpeg_10/msm_jpeg_sync.c
index a0aaf03..a7a9d70 100644
--- a/drivers/media/video/msm/jpeg_10/msm_jpeg_sync.c
+++ b/drivers/media/video/msm/jpeg_10/msm_jpeg_sync.c
@@ -368,8 +368,8 @@
 		buf_cmd.fd);
 
 	buf_p->y_buffer_addr = msm_jpeg_platform_v2p(pgmn_dev, buf_cmd.fd,
-		buf_cmd.y_len, &buf_p->file, &buf_p->handle,
-		pgmn_dev->domain_num);
+		buf_cmd.y_len + buf_cmd.cbcr_len + buf_cmd.pln2_len,
+		&buf_p->file, &buf_p->handle, pgmn_dev->domain_num);
 	if (!buf_p->y_buffer_addr) {
 		JPEG_PR_ERR("%s:%d] v2p wrong\n", __func__, __LINE__);
 		kfree(buf_p);
@@ -382,11 +382,23 @@
 	else
 		buf_p->cbcr_buffer_addr = 0x0;
 
-	JPEG_DBG("%s:%d] After v2p pln0_addr =0x%x,pln0_len %d pl1_len %d",
+	if (buf_cmd.pln2_len)
+		buf_p->pln2_addr = buf_p->cbcr_buffer_addr +
+			buf_cmd.cbcr_len;
+	else
+		buf_p->pln2_addr = 0x0;
+
+	JPEG_DBG("%s:%d]After v2p pln0_addr %x pln0_len %d",
 		__func__, __LINE__, buf_p->y_buffer_addr,
-		buf_cmd.y_len, buf_cmd.cbcr_len);
+		buf_cmd.y_len);
+
+	JPEG_DBG("pl1_len %d, pln1_addr %x, pln2_adrr %x,pln2_len %d",
+		buf_cmd.cbcr_len, buf_p->cbcr_buffer_addr,
+		buf_p->pln2_addr, buf_cmd.pln2_len);
+
 	buf_p->y_len = buf_cmd.y_len;
 	buf_p->cbcr_len = buf_cmd.cbcr_len;
+	buf_p->pln2_len = buf_cmd.pln2_len;
 	buf_p->vbuf = buf_cmd;
 
 	msm_jpeg_q_in(&pgmn_dev->output_buf_q, buf_p);
@@ -489,23 +501,31 @@
 		(int) buf_cmd.vaddr, buf_cmd.y_len);
 
 	buf_p->y_buffer_addr    = msm_jpeg_platform_v2p(pgmn_dev, buf_cmd.fd,
-		buf_cmd.y_len + buf_cmd.cbcr_len, &buf_p->file,
-		&buf_p->handle, pgmn_dev->domain_num) + buf_cmd.offset
-		+ buf_cmd.y_off;
+		buf_cmd.y_len + buf_cmd.cbcr_len + buf_cmd.pln2_len,
+		&buf_p->file, &buf_p->handle, pgmn_dev->domain_num) +
+		buf_cmd.offset + buf_cmd.y_off;
 	buf_p->y_len          = buf_cmd.y_len;
 	buf_p->cbcr_len       = buf_cmd.cbcr_len;
+	buf_p->pln2_len       = buf_cmd.pln2_len;
 	buf_p->num_of_mcu_rows = buf_cmd.num_of_mcu_rows;
-	buf_p->y_len = buf_cmd.y_len;
-	buf_p->cbcr_len = buf_cmd.cbcr_len;
+
 	if (buf_cmd.cbcr_len)
-		buf_p->cbcr_buffer_addr = buf_p->y_buffer_addr + buf_cmd.y_len
-			+ buf_cmd.cbcr_off;
+		buf_p->cbcr_buffer_addr = buf_p->y_buffer_addr +
+		buf_cmd.y_len + buf_cmd.cbcr_off;
 	else
 		buf_p->cbcr_buffer_addr = 0x0;
 
-	JPEG_DBG("%s: y_addr=%x, y_len=%x, cbcr_addr=%x, cbcr_len=%x, fd =%d\n",
+	if (buf_cmd.pln2_len)
+		buf_p->pln2_addr = buf_p->cbcr_buffer_addr +
+		buf_cmd.cbcr_len + buf_cmd.pln2_off;
+	else
+		buf_p->pln2_addr = 0x0;
+
+	JPEG_DBG("%s: y_addr=%x, y_len=%x, cbcr_addr=%x, cbcr_len=%d",
 		__func__, buf_p->y_buffer_addr, buf_p->y_len,
-		buf_p->cbcr_buffer_addr, buf_p->cbcr_len, buf_cmd.fd);
+		buf_p->cbcr_buffer_addr, buf_p->cbcr_len);
+	JPEG_DBG("pln2_addr = %x, pln2_len = %d, fd =%d\n",
+		buf_p->pln2_addr, buf_p->pln2_len, buf_cmd.fd);
 
 	if (!buf_p->y_buffer_addr) {
 		JPEG_PR_ERR("%s:%d] v2p wrong\n", __func__, __LINE__);
@@ -733,9 +753,11 @@
 	for (i = 0; i < 2; i++)
 		kfree(buf_out_free[i]);
 
-	msm_jpeg_io_dump(pgmn_dev->base, JPEG_REG_SIZE);
+	JPEG_DBG_HIGH("%s:%d] START\n", __func__, __LINE__);
+	wmb();
 	rc = msm_jpeg_ioctl_hw_cmds(pgmn_dev, arg);
-	JPEG_DBG("%s:%d]\n", __func__, __LINE__);
+	wmb();
+	JPEG_DBG("%s:%d]", __func__, __LINE__);
 	return rc;
 }
 
diff --git a/drivers/media/video/msm/msm_isp.c b/drivers/media/video/msm/msm_isp.c
index 77922e2..48ce577 100644
--- a/drivers/media/video/msm/msm_isp.c
+++ b/drivers/media/video/msm/msm_isp.c
@@ -426,6 +426,10 @@
 			stats.aec.buff = stats.buffer;
 			stats.aec.fd = stats.fd;
 			break;
+		case MSG_ID_STATS_BE:
+			stats.be.buff = stats.buffer;
+			stats.be.fd = stats.fd;
+			break;
 		case MSG_ID_STATS_AF:
 		case MSG_ID_STATS_BF:
 			stats.af.buff = stats.buffer;
@@ -514,6 +518,7 @@
 	CDBG("%s: cmd_type %d\n", __func__, cfgcmd.cmd_type);
 	switch (cfgcmd.cmd_type) {
 	case CMD_STATS_BG_ENABLE:
+	case CMD_STATS_BE_ENABLE:
 	case CMD_STATS_BF_ENABLE:
 	case CMD_STATS_BHIST_ENABLE:
 	case CMD_STATS_AF_ENABLE:
@@ -611,6 +616,8 @@
 			cfgcmd.cmd_type = CMD_STATS_BUF_RELEASE;
 		else if (buf.type == STAT_BG)
 			cfgcmd.cmd_type = CMD_STATS_BG_BUF_RELEASE;
+		else if (buf.type == STAT_BE)
+			cfgcmd.cmd_type = CMD_STATS_BE_BUF_RELEASE;
 		else if (buf.type == STAT_BF)
 			cfgcmd.cmd_type = CMD_STATS_BF_BUF_RELEASE;
 		else if (buf.type == STAT_BHIST)
diff --git a/drivers/media/video/msm/msm_mctl.c b/drivers/media/video/msm/msm_mctl.c
index 3b678c4..36fb849 100644
--- a/drivers/media/video/msm/msm_mctl.c
+++ b/drivers/media/video/msm/msm_mctl.c
@@ -222,7 +222,7 @@
 		pr_err("%s Copy from user failed ", __func__);
 		rc = -EFAULT;
 	} else {
-		pr_info("%s: mctl=0x%p, vfe output mode =0x%x",
+		pr_debug("%s: mctl=0x%p, vfe output mode =0x%x\n",
 		  __func__, p_mctl, p_mctl->vfe_output_mode);
 	}
 	return rc;
diff --git a/drivers/media/video/msm/msm_mctl_pp.c b/drivers/media/video/msm/msm_mctl_pp.c
index 105426e..7155d4c 100644
--- a/drivers/media/video/msm/msm_mctl_pp.c
+++ b/drivers/media/video/msm/msm_mctl_pp.c
@@ -681,6 +681,7 @@
 	ret_frame.dirty = 0;
 	ret_frame.node_type = frame.node_type;
 	ret_frame.timestamp = frame.timestamp;
+	ret_frame.frame_id  = frame.frame_id;
 	D("%s Frame done id: %d\n", __func__, frame.frame_id);
 	rc = msm_mctl_buf_done_pp(p_mctl, &buf_handle, &buf, &ret_frame);
 	return rc;
diff --git a/drivers/media/video/msm/msm_mem.c b/drivers/media/video/msm/msm_mem.c
index 1875df2..e131193 100644
--- a/drivers/media/video/msm/msm_mem.c
+++ b/drivers/media/video/msm/msm_mem.c
@@ -210,6 +210,7 @@
 	case MSM_PMEM_SKIN:
 	case MSM_PMEM_AEC_AWB:
 	case MSM_PMEM_BAYER_GRID:
+	case MSM_PMEM_BAYER_EXPOSURE:
 	case MSM_PMEM_BAYER_FOCUS:
 	case MSM_PMEM_BAYER_HIST:
 		rc = msm_pmem_table_add(ptype, pinfo, client, domain_num);
@@ -241,6 +242,7 @@
 	case MSM_PMEM_SKIN:
 	case MSM_PMEM_AEC_AWB:
 	case MSM_PMEM_BAYER_GRID:
+	case MSM_PMEM_BAYER_EXPOSURE:
 	case MSM_PMEM_BAYER_FOCUS:
 	case MSM_PMEM_BAYER_HIST:
 		hlist_for_each_entry_safe(region, node, n,
diff --git a/drivers/media/video/msm/sensors/Makefile b/drivers/media/video/msm/sensors/Makefile
index cd228a1..a70a632 100644
--- a/drivers/media/video/msm/sensors/Makefile
+++ b/drivers/media/video/msm/sensors/Makefile
@@ -8,6 +8,7 @@
 obj-$(CONFIG_OV8825) += ov8825_v4l2.o
 obj-$(CONFIG_IMX074) += imx074_v4l2.o
 obj-$(CONFIG_S5K3L1YX) += s5k3l1yx.o
+obj-$(CONFIG_IMX135) += imx135_v4l2.o
 obj-$(CONFIG_OV2720) += ov2720.o
 obj-$(CONFIG_MT9M114) += mt9m114_v4l2.o
 obj-$(CONFIG_S5K4E1) += s5k4e1_v4l2.o
diff --git a/drivers/media/video/msm/sensors/imx135_v4l2.c b/drivers/media/video/msm/sensors/imx135_v4l2.c
new file mode 100644
index 0000000..f480923
--- /dev/null
+++ b/drivers/media/video/msm/sensors/imx135_v4l2.c
@@ -0,0 +1,553 @@
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include "msm_sensor.h"
+#define SENSOR_NAME "imx135"
+#define PLATFORM_DRIVER_NAME "msm_camera_imx135"
+#define imx135_obj imx135_##obj
+
+DEFINE_MUTEX(imx135_mut);
+static struct msm_sensor_ctrl_t imx135_s_ctrl;
+
+static struct msm_camera_i2c_reg_conf imx135_start_settings[] = {
+	{0x0100, 0x01},
+};
+
+static struct msm_camera_i2c_reg_conf imx135_stop_settings[] = {
+	{0x0100, 0x00},
+};
+
+static struct msm_camera_i2c_reg_conf imx135_groupon_settings[] = {
+	{0x104, 0x01},
+};
+
+static struct msm_camera_i2c_reg_conf imx135_groupoff_settings[] = {
+	{0x104, 0x00},
+};
+
+static struct msm_camera_i2c_reg_conf imx135_recommend_settings[] = {
+/* Recommended global settings */
+	{0x0220, 0x01},
+	{0x3008, 0xB0},
+	{0x320A, 0x01},
+	{0x320D, 0x10},
+	{0x3216, 0x2E},
+	{0x3230, 0x0A},
+	{0x3228, 0x05},
+	{0x3229, 0x02},
+	{0x322C, 0x02},
+	{0x3302, 0x10},
+	{0x3390, 0x45},
+	{0x3409, 0x0C},
+	{0x340B, 0xF5},
+	{0x340C, 0x2D},
+	{0x3412, 0x41},
+	{0x3413, 0xAD},
+	{0x3414, 0x1E},
+	{0x3427, 0x04},
+	{0x3480, 0x1E},
+	{0x3484, 0x1E},
+	{0x3488, 0x1E},
+	{0x348C, 0x1E},
+	{0x3490, 0x1E},
+	{0x3494, 0x1E},
+	{0x349C, 0x38},
+	{0x34A3, 0x38},
+	{0x3511, 0x8F},
+	{0x3518, 0x00},
+	{0x3519, 0x94},
+	{0x3833, 0x20},
+	{0x3893, 0x01},
+	{0x38C2, 0x08},
+	{0x3C09, 0x01},
+	{0x4300, 0x00},
+	{0x4316, 0x12},
+	{0x4317, 0x22},
+	{0x431A, 0x00},
+	{0x4324, 0x03},
+	{0x4325, 0x20},
+	{0x4326, 0x03},
+	{0x4327, 0x84},
+	{0x4328, 0x03},
+	{0x4329, 0x20},
+	{0x432A, 0x03},
+	{0x432B, 0x84},
+	{0x4401, 0x3F},
+	{0x4412, 0x3F},
+	{0x4413, 0xFF},
+	{0x4446, 0x3F},
+	{0x4447, 0xFF},
+	{0x4452, 0x00},
+	{0x4453, 0xA0},
+	{0x4454, 0x08},
+	{0x4455, 0x00},
+	{0x4458, 0x18},
+	{0x4459, 0x18},
+	{0x445A, 0x3F},
+	{0x445B, 0x3A},
+	{0x4463, 0x00},
+	{0x4465, 0x00},
+	{0x446E, 0x01},
+/* Image Quality Settings */
+/* Bypass Settings */
+	{0x4203, 0x48},
+/* Defect Correction Recommended Setting */
+	{0x4100, 0xE0},
+	{0x4102, 0x0B},
+/* RGB Filter Recommended Setting */
+	{0x4281, 0x22},
+	{0x4282, 0x82},
+	{0x4284, 0x00},
+	{0x4287, 0x18},
+	{0x4288, 0x00},
+	{0x428B, 0x1E},
+	{0x428C, 0x00},
+	{0x428F, 0x08},
+/* DLC/ADP Recommended Setting */
+	{0x4207, 0x00},
+	{0x4218, 0x02},
+	{0x421B, 0x00},
+	{0x4222, 0x04},
+	{0x4223, 0x44},
+	{0x4224, 0x46},
+	{0x4225, 0xFF},
+	{0x4226, 0x14},
+	{0x4227, 0xF2},
+	{0x4228, 0xFC},
+	{0x4229, 0x60},
+	{0x422A, 0xFA},
+	{0x422B, 0xFE},
+	{0x422C, 0xFE},
+/* Color Artifact Recommended Setting */
+	{0x4243, 0xAA}
+};
+
+/* IMX135 mode 1/2 HV at 24MHz */
+static struct msm_camera_i2c_reg_conf imx135_prev_settings[] = {
+/* Clock Setting */
+	{0x011E, 0x18},
+	{0x011F, 0x00},
+	{0x0301, 0x05},
+	{0x0303, 0x01},
+	{0x0304, 0x0B},
+	{0x0305, 0x03},
+	{0x0306, 0x01},
+	{0x0307, 0x5E},
+	{0x0309, 0x05},
+	{0x030B, 0x02},
+	{0x030C, 0x00},
+	{0x030D, 0x71},
+	{0x030E, 0x01},
+	{0x3A06, 0x12},
+/* Mode setting */
+	{0x0101, 0x00},
+	{0x0105, 0x00},
+	{0x0108, 0x03},
+	{0x0109, 0x30},
+	{0x010B, 0x32},
+	{0x0112, 0x0A},
+	{0x0113, 0x0A},
+	{0x0381, 0x01},
+	{0x0383, 0x01},
+	{0x0385, 0x01},
+	{0x0387, 0x01},
+	{0x0390, 0x01}, /* binning_en = 1 */
+	{0x0391, 0x22}, /* binning_type */
+	{0x0392, 0x00}, /* binning_mode = 0 (average) */
+	{0x0401, 0x00},
+	{0x0404, 0x00},
+	{0x0405, 0x10},
+	{0x4083, 0x01},
+/* Size setting*/
+	{0x0340, 0x0A}, /* frame_length_lines = 2680*/
+	{0x0341, 0x78},
+	{0x034C, 0x08},
+	{0x034D, 0x38},
+	{0x034E, 0x06},
+	{0x034F, 0x18},
+	{0x0354, 0x08},
+	{0x0355, 0x38},
+	{0x0356, 0x06},
+	{0x0357, 0x18},
+	{0x3310, 0x08},
+	{0x3311, 0x38},
+	{0x3312, 0x06},
+	{0x3313, 0x18},
+	{0x331C, 0x02},
+	{0x331D, 0xC0},
+	{0x33B0, 0x04},
+	{0x33B1, 0x00},
+	{0x33B3, 0x00},
+	{0x7006, 0x04},
+/* Global Timing Setting */
+	{0x0830, 0x67},
+	{0x0831, 0x27},
+	{0x0832, 0x47},
+	{0x0833, 0x27},
+	{0x0834, 0x27},
+	{0x0835, 0x1F},
+	{0x0836, 0x87},
+	{0x0837, 0x2F},
+	{0x0839, 0x1F},
+	{0x083A, 0x17},
+	{0x083B, 0x02},
+/* Integration Time Setting */
+	{0x0254, 0x00},
+/* Gain Setting */
+	{0x0205, 0x33}
+};
+
+/* IMX135 Mode Fullsize at 24MHz */
+static struct msm_camera_i2c_reg_conf imx135_snap_settings[] = {
+/* Clock Setting */
+	{0x011E, 0x18},
+	{0x011F, 0x00},
+	{0x0301, 0x05},
+	{0x0303, 0x01},
+	{0x0304, 0x0B},
+	{0x0305, 0x03},
+	{0x0306, 0x01},
+	{0x0307, 0x5E},
+	{0x0309, 0x05},
+	{0x030B, 0x01},
+	{0x030C, 0x00},
+	{0x030D, 0x60}, /* pll_multiplier = 96 */
+	{0x030E, 0x01},
+	{0x3A06, 0x11},
+/* Mode setting */
+	{0x0101, 0x00},
+	{0x0105, 0x00},
+	{0x0108, 0x03},
+	{0x0109, 0x30},
+	{0x010B, 0x32},
+	{0x0112, 0x0A},
+	{0x0113, 0x0A},
+	{0x0381, 0x01},
+	{0x0383, 0x01},
+	{0x0385, 0x01},
+	{0x0387, 0x01},
+	{0x0390, 0x00},
+	{0x0391, 0x11},
+	{0x0392, 0x00},
+	{0x0401, 0x00},
+	{0x0404, 0x00},
+	{0x0405, 0x10},
+	{0x4083, 0x01},
+/* Size setting */
+	{0x0340, 0x0C},
+	{0x0341, 0x46},
+	{0x034C, 0x10},
+	{0x034D, 0x70},
+	{0x034E, 0x0C},
+	{0x034F, 0x30},
+	{0x0354, 0x10},
+	{0x0355, 0x70},
+	{0x0356, 0x0C},
+	{0x0357, 0x30},
+	{0x3310, 0x10},
+	{0x3311, 0x70},
+	{0x3312, 0x0C},
+	{0x3313, 0x30},
+	{0x331C, 0x06},
+	{0x331D, 0x00},
+	{0x33B0, 0x04},
+	{0x33B1, 0x00},
+	{0x33B3, 0x00},
+	{0x7006, 0x04},
+/* Global Timing Setting */
+	{0x0830, 0x7F},
+	{0x0831, 0x37},
+	{0x0832, 0x5F},
+	{0x0833, 0x37},
+	{0x0834, 0x37},
+	{0x0835, 0x3F},
+	{0x0836, 0xC7},
+	{0x0837, 0x3F},
+	{0x0839, 0x1F},
+	{0x083A, 0x17},
+	{0x083B, 0x02},
+/* Integration Time Setting */
+	{0x0250, 0x0B},
+/* Gain Setting */
+	{0x0205, 0x33}
+};
+
+
+static struct msm_camera_i2c_reg_conf imx135_hdr_settings[] = {
+/* Clock Setting */
+	{0x011E, 0x18},
+	{0x011F, 0x00},
+	{0x0301, 0x05},
+	{0x0303, 0x01},
+	{0x0304, 0x0B},
+	{0x0305, 0x03},
+	{0x0306, 0x01},
+	{0x0307, 0x5E},
+	{0x0309, 0x05},
+	{0x030B, 0x02},
+	{0x030C, 0x00},
+	{0x030D, 0x71},
+	{0x030E, 0x01},
+	{0x3A06, 0x12},
+/* Mode setting */
+	{0x0101, 0x00},
+	{0x0105, 0x00},
+	{0x0108, 0x03},
+	{0x0109, 0x30},
+	{0x010B, 0x32},
+	{0x0112, 0x0E},
+	{0x0113, 0x0A},
+	{0x0381, 0x01},
+	{0x0383, 0x01},
+	{0x0385, 0x01},
+	{0x0387, 0x01},
+	{0x0390, 0x00},
+	{0x0391, 0x11},
+	{0x0392, 0x00},
+	{0x0401, 0x00},
+	{0x0404, 0x00},
+	{0x0405, 0x10},
+	{0x4083, 0x01},
+/* Size setting */
+	{0x0340, 0x0C},
+	{0x0341, 0x48},
+	{0x034C, 0x08},
+	{0x034D, 0x38},
+	{0x034E, 0x06},
+	{0x034F, 0x18},
+	{0x0354, 0x08},
+	{0x0355, 0x38},
+	{0x0356, 0x06},
+	{0x0357, 0x18},
+	{0x3310, 0x08},
+	{0x3311, 0x38},
+	{0x3312, 0x06},
+	{0x3313, 0x18},
+	{0x331C, 0x02},
+	{0x331D, 0xA0},
+	{0x33B0, 0x08},
+	{0x33B1, 0x38},
+	{0x33B3, 0x01},
+	{0x7006, 0x04},
+/* Global Timing Setting */
+	{0x0830, 0x67},
+	{0x0831, 0x27},
+	{0x0832, 0x47},
+	{0x0833, 0x27},
+	{0x0834, 0x27},
+	{0x0835, 0x1F},
+	{0x0836, 0x87},
+	{0x0837, 0x2F},
+	{0x0839, 0x1F},
+	{0x083A, 0x17},
+	{0x083B, 0x02},
+/* Integration Time Setting */
+	{0x0250, 0x0B},
+/* Gain Setting */
+	{0x0205, 0x33}
+};
+
+static struct v4l2_subdev_info imx135_subdev_info[] = {
+	{
+	.code		= V4L2_MBUS_FMT_SBGGR10_1X10,
+	.colorspace	= V4L2_COLORSPACE_JPEG,
+	.fmt		= 1,
+	.order		= 0,
+	},
+	/* more can be supported, to be added later */
+};
+
+static struct msm_camera_i2c_conf_array imx135_init_conf[] = {
+	{&imx135_recommend_settings[0],
+	ARRAY_SIZE(imx135_recommend_settings), 0, MSM_CAMERA_I2C_BYTE_DATA}
+};
+
+static struct msm_camera_i2c_conf_array imx135_confs[] = {
+	{&imx135_snap_settings[0],
+	ARRAY_SIZE(imx135_snap_settings), 0, MSM_CAMERA_I2C_BYTE_DATA},
+	{&imx135_prev_settings[0],
+	ARRAY_SIZE(imx135_prev_settings), 0, MSM_CAMERA_I2C_BYTE_DATA},
+	{&imx135_hdr_settings[0],
+	ARRAY_SIZE(imx135_hdr_settings), 0, MSM_CAMERA_I2C_BYTE_DATA},
+};
+
+static struct msm_sensor_output_info_t imx135_dimensions[] = {
+	/* RES0 snapshot(FULL SIZE) */
+	{
+		.x_output = 4208,
+		.y_output = 3120,
+		.line_length_pclk = 4572,
+		.frame_length_lines = 3142,
+		.vt_pixel_clk = 307200000,
+		.op_pixel_clk = 307200000,
+		.binning_factor = 1,
+	},
+	/* RES1 4:3 preview(1/2HV QTR SIZE) */
+	{
+		.x_output = 2104,
+		.y_output = 1560,
+		.line_length_pclk = 4572,
+		.frame_length_lines = 2680,
+		.vt_pixel_clk = 361600000,
+		.op_pixel_clk = 180800000,
+		.binning_factor = 1,
+	},
+	/* RES2 4:3 HDR movie mode */
+	{
+		.x_output = 2104,
+		.y_output = 1560,
+		.line_length_pclk = 4572,
+		.frame_length_lines = 3144,
+		.vt_pixel_clk = 361600000,
+		.op_pixel_clk = 180800000,
+		.binning_factor = 1,
+	},
+};
+
+static struct msm_sensor_output_reg_addr_t imx135_reg_addr = {
+	.x_output = 0x34C,
+	.y_output = 0x34E,
+	.line_length_pclk = 0x342,
+	.frame_length_lines = 0x340,
+};
+
+static struct msm_sensor_id_info_t imx135_id_info = {
+	.sensor_id_reg_addr = 0x0000,
+	.sensor_id = 0x0087,
+};
+
+static struct msm_sensor_exp_gain_info_t imx135_exp_gain_info = {
+	.coarse_int_time_addr = 0x202,
+	.global_gain_addr = 0x205,
+	.vert_offset = 4,
+};
+
+static const struct i2c_device_id imx135_i2c_id[] = {
+	{SENSOR_NAME, (kernel_ulong_t)&imx135_s_ctrl},
+	{ }
+};
+
+static struct i2c_driver imx135_i2c_driver = {
+	.id_table = imx135_i2c_id,
+	.probe  = msm_sensor_i2c_probe,
+	.driver = {
+		.name = SENSOR_NAME,
+	},
+};
+
+static struct msm_camera_i2c_client imx135_sensor_i2c_client = {
+	.addr_type = MSM_CAMERA_I2C_WORD_ADDR,
+};
+
+static int __init msm_sensor_init_module(void)
+{
+	return i2c_add_driver(&imx135_i2c_driver);
+}
+
+static struct v4l2_subdev_core_ops imx135_subdev_core_ops = {
+	.ioctl = msm_sensor_subdev_ioctl,
+	.s_power = msm_sensor_power,
+};
+
+static struct v4l2_subdev_video_ops imx135_subdev_video_ops = {
+	.enum_mbus_fmt = msm_sensor_v4l2_enum_fmt,
+};
+
+static struct v4l2_subdev_ops imx135_subdev_ops = {
+	.core = &imx135_subdev_core_ops,
+	.video  = &imx135_subdev_video_ops,
+};
+
+int32_t imx135_write_exp_gain(struct msm_sensor_ctrl_t *s_ctrl,
+		uint16_t gain, uint32_t line)
+{
+	uint32_t fl_lines;
+	uint8_t offset;
+	fl_lines = s_ctrl->curr_frame_length_lines;
+	fl_lines = (fl_lines * s_ctrl->fps_divider) / Q10;
+	offset = s_ctrl->sensor_exp_gain_info->vert_offset;
+	if (line > (fl_lines - offset))
+		fl_lines = line + offset;
+
+	s_ctrl->func_tbl->sensor_group_hold_on(s_ctrl);
+	msm_camera_i2c_write(s_ctrl->sensor_i2c_client,
+		s_ctrl->sensor_output_reg_addr->frame_length_lines, fl_lines,
+		MSM_CAMERA_I2C_WORD_DATA);
+	msm_camera_i2c_write(s_ctrl->sensor_i2c_client,
+		s_ctrl->sensor_exp_gain_info->coarse_int_time_addr, line,
+		MSM_CAMERA_I2C_WORD_DATA);
+	msm_camera_i2c_write(s_ctrl->sensor_i2c_client,
+		s_ctrl->sensor_exp_gain_info->global_gain_addr, gain,
+		MSM_CAMERA_I2C_BYTE_DATA);
+	s_ctrl->func_tbl->sensor_group_hold_off(s_ctrl);
+	return 0;
+}
+
+static struct msm_sensor_fn_t imx135_func_tbl = {
+	.sensor_start_stream = msm_sensor_start_stream,
+	.sensor_stop_stream = msm_sensor_stop_stream,
+	.sensor_group_hold_on = msm_sensor_group_hold_on,
+	.sensor_group_hold_off = msm_sensor_group_hold_off,
+	.sensor_set_fps = msm_sensor_set_fps,
+	.sensor_write_exp_gain = imx135_write_exp_gain,
+	.sensor_write_snapshot_exp_gain = imx135_write_exp_gain,
+	.sensor_setting = msm_sensor_setting,
+	.sensor_csi_setting = msm_sensor_setting1,
+	.sensor_set_sensor_mode = msm_sensor_set_sensor_mode,
+	.sensor_mode_init = msm_sensor_mode_init,
+	.sensor_get_output_info = msm_sensor_get_output_info,
+	.sensor_config = msm_sensor_config,
+	.sensor_power_up = msm_sensor_power_up,
+	.sensor_power_down = msm_sensor_power_down,
+	.sensor_adjust_frame_lines = msm_sensor_adjust_frame_lines1,
+	.sensor_get_csi_params = msm_sensor_get_csi_params,
+};
+
+static struct msm_sensor_reg_t imx135_regs = {
+	.default_data_type = MSM_CAMERA_I2C_BYTE_DATA,
+	.start_stream_conf = imx135_start_settings,
+	.start_stream_conf_size = ARRAY_SIZE(imx135_start_settings),
+	.stop_stream_conf = imx135_stop_settings,
+	.stop_stream_conf_size = ARRAY_SIZE(imx135_stop_settings),
+	.group_hold_on_conf = imx135_groupon_settings,
+	.group_hold_on_conf_size = ARRAY_SIZE(imx135_groupon_settings),
+	.group_hold_off_conf = imx135_groupoff_settings,
+	.group_hold_off_conf_size =
+		ARRAY_SIZE(imx135_groupoff_settings),
+	.init_settings = &imx135_init_conf[0],
+	.init_size = ARRAY_SIZE(imx135_init_conf),
+	.mode_settings = &imx135_confs[0],
+	.output_settings = &imx135_dimensions[0],
+	.num_conf = ARRAY_SIZE(imx135_confs),
+};
+
+static struct msm_sensor_ctrl_t imx135_s_ctrl = {
+	.msm_sensor_reg = &imx135_regs,
+	.sensor_i2c_client = &imx135_sensor_i2c_client,
+	.sensor_i2c_addr = 0x20,
+	.sensor_output_reg_addr = &imx135_reg_addr,
+	.sensor_id_info = &imx135_id_info,
+	.sensor_exp_gain_info = &imx135_exp_gain_info,
+	.cam_mode = MSM_SENSOR_MODE_INVALID,
+	.msm_sensor_mutex = &imx135_mut,
+	.sensor_i2c_driver = &imx135_i2c_driver,
+	.sensor_v4l2_subdev_info = imx135_subdev_info,
+	.sensor_v4l2_subdev_info_size = ARRAY_SIZE(imx135_subdev_info),
+	.sensor_v4l2_subdev_ops = &imx135_subdev_ops,
+	.func_tbl = &imx135_func_tbl,
+	.clk_rate = MSM_SENSOR_MCLK_24HZ,
+};
+
+module_init(msm_sensor_init_module);
+MODULE_DESCRIPTION("Sony 13MP Bayer sensor driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/msm/server/msm_cam_server.c b/drivers/media/video/msm/server/msm_cam_server.c
index 84aaa69..b8b1d51 100644
--- a/drivers/media/video/msm/server/msm_cam_server.c
+++ b/drivers/media/video/msm/server/msm_cam_server.c
@@ -152,6 +152,27 @@
 						mctl_handle = 0;
 				}
 			}
+
+			if (!is_bayer_sensor && interface == PIX_0) {
+				if (g_server_dev.
+					interface_map_table[i].mctl_handle &&
+					g_server_dev.interface_map_table[i].
+						is_bayer_sensor) {
+					/* In case of simultaneous camera,
+					 * the YUV sensor could use PIX
+					 * interface to only queue the preview
+					 * or video buffers, but does not
+					 * expect any notifications directly.
+					 * (preview/video data is updated from
+					 * postprocessing in such scenario).
+					 * In such case, there is no need to
+					 * update the mctl_handle in the intf
+					 * map table, since the notification
+					 * will not be sent directly. */
+					break;
+				}
+			}
+
 			old_handle =
 				g_server_dev.interface_map_table[i].mctl_handle;
 			if (old_handle == 0) {
diff --git a/drivers/media/video/msm/vfe/msm_vfe40.c b/drivers/media/video/msm/vfe/msm_vfe40.c
index e958241..287c77c 100644
--- a/drivers/media/video/msm/vfe/msm_vfe40.c
+++ b/drivers/media/video/msm/vfe/msm_vfe40.c
@@ -254,6 +254,8 @@
 		V40_COLORXFORM_ENC_CFG_OFF, 0xFF},
 	[161] = {VFE_CMD_COLORXFORM_VIEW_UPDATE, V40_COLORXFORM_VIEW_CFG_LEN,
 		V40_COLORXFORM_VIEW_CFG_OFF, 0xFF},
+	[163] = {VFE_CMD_STATS_BE_START, V40_STATS_BE_LEN, V40_STATS_BE_OFF},
+	[164] = {VFE_CMD_STATS_BE_STOP},
 };
 
 static const uint32_t vfe40_AXI_WM_CFG[] = {
@@ -421,7 +423,16 @@
 	{
 		.src = MSM_BUS_MASTER_VFE,
 		.dst = MSM_BUS_SLAVE_EBI_CH0,
-		.ab  = 154275840,
+		.ab  = 274406400,
+		.ib  = 617103360,
+	},
+};
+
+static struct msm_bus_vectors vfe_liveshot_vectors[] = {
+	{
+		.src = MSM_BUS_MASTER_VFE,
+		.dst = MSM_BUS_SLAVE_EBI_CH0,
+		.ab  = 348192000,
 		.ib  = 617103360,
 	},
 };
@@ -465,6 +476,10 @@
 		ARRAY_SIZE(vfe_zsl_vectors),
 		vfe_zsl_vectors,
 	},
+	{
+		ARRAY_SIZE(vfe_liveshot_vectors),
+		vfe_liveshot_vectors,
+	},
 };
 
 static struct msm_bus_scale_pdata vfe_bus_client_pdata = {
@@ -564,23 +579,6 @@
 			VFE_IRQ_MASK_0);
 	}
 
-	if (share_ctrl->axi_ref_cnt == 1) {
-		atomic_set(&share_ctrl->handle_common_irq, 0);
-	msm_camera_io_w(VFE_DISABLE_ALL_IRQS,
-		share_ctrl->vfebase + VFE_IRQ_MASK_0);
-	msm_camera_io_w(VFE_DISABLE_ALL_IRQS,
-		share_ctrl->vfebase + VFE_IRQ_MASK_1);
-
-	/* clear all pending interrupts*/
-	msm_camera_io_w(0xFFFFFFFF,
-		share_ctrl->vfebase + VFE_IRQ_CLEAR_0);
-	msm_camera_io_w(0xFFFFFFFF,
-		share_ctrl->vfebase + VFE_IRQ_CLEAR_1);
-	/* Ensure the write order while writing
-	to the command register using the barrier */
-	msm_camera_io_w_mb(1,
-		share_ctrl->vfebase + VFE_IRQ_CMD);
-	}
 }
 
 static void vfe40_stop(struct vfe40_ctrl_type *vfe40_ctrl)
@@ -821,24 +819,87 @@
 	/* stats UB config */
 	CDBG("%s: Use bayer stats = %d\n", __func__,
 		 vfe40_use_bayer_stats(vfe40_ctrl));
-	msm_camera_io_w(0x350001F,
-	vfe40_ctrl->share_ctrl->vfebase +
-			VFE_BUS_STATS_HIST_WR_UB_CFG);
-	msm_camera_io_w(0x370002F,
-		vfe40_ctrl->share_ctrl->vfebase +
-			VFE_BUS_STATS_BG_WR_UB_CFG);
-	msm_camera_io_w(0x3A0002F,
-		vfe40_ctrl->share_ctrl->vfebase +
-			VFE_BUS_STATS_BF_WR_UB_CFG);
-	msm_camera_io_w(0x3D00007,
+
+	msm_camera_io_w(0x82F80007,
 		vfe40_ctrl->share_ctrl->vfebase +
 			VFE_BUS_STATS_RS_WR_UB_CFG);
-	msm_camera_io_w(0x3D8001F,
+	msm_camera_io_w(0x8300000F,
 		vfe40_ctrl->share_ctrl->vfebase +
 			VFE_BUS_STATS_CS_WR_UB_CFG);
-	msm_camera_io_w(0x3F80007,
+
+	msm_camera_io_w(0x8310003F,
+		vfe40_ctrl->share_ctrl->vfebase +
+			VFE_BUS_STATS_BG_WR_UB_CFG);
+	msm_camera_io_w(0x8350003F,
+		vfe40_ctrl->share_ctrl->vfebase +
+			VFE_BUS_STATS_BE_WR_UB_CFG);
+	msm_camera_io_w(0x8390003F,
+		vfe40_ctrl->share_ctrl->vfebase +
+			VFE_BUS_STATS_BF_WR_UB_CFG);
+
+	msm_camera_io_w(0x83D0000F,
+	vfe40_ctrl->share_ctrl->vfebase +
+			VFE_BUS_STATS_HIST_WR_UB_CFG);
+	msm_camera_io_w(0x83E0000F,
 		vfe40_ctrl->share_ctrl->vfebase +
 			VFE_BUS_STATS_SKIN_WR_UB_CFG);
+
+	msm_camera_io_w(0x83F0000F,
+		vfe40_ctrl->share_ctrl->vfebase +
+			VFE_BUS_STATS_AWB_WR_UB_CFG);
+
+	/* stats frame subsample config*/
+	msm_camera_io_w(0xFFFFFFFF,
+		vfe40_ctrl->share_ctrl->vfebase +
+			VFE_BUS_STATS_HIST_WR_FRAMEDROP_PATTERN);
+	msm_camera_io_w(0xFFFFFFFF,
+		vfe40_ctrl->share_ctrl->vfebase +
+			VFE_BUS_STATS_BG_WR_FRAMEDROP_PATTERN);
+	msm_camera_io_w(0xFFFFFFFF,
+		vfe40_ctrl->share_ctrl->vfebase +
+			VFE_BUS_STATS_BE_WR_FRAMEDROP_PATTERN);
+	msm_camera_io_w(0xFFFFFFFF,
+		vfe40_ctrl->share_ctrl->vfebase +
+			VFE_BUS_STATS_BF_WR_FRAMEDROP_PATTERN);
+	msm_camera_io_w(0xFFFFFFFF,
+		vfe40_ctrl->share_ctrl->vfebase +
+			VFE_BUS_STATS_RS_WR_FRAMEDROP_PATTERN);
+	msm_camera_io_w(0xFFFFFFFF,
+		vfe40_ctrl->share_ctrl->vfebase +
+			VFE_BUS_STATS_CS_WR_FRAMEDROP_PATTERN);
+	msm_camera_io_w(0xFFFFFFFF,
+		vfe40_ctrl->share_ctrl->vfebase +
+			VFE_BUS_STATS_SKIN_WR_FRAMEDROP_PATTERN);
+	msm_camera_io_w(0xFFFFFFFF,
+		vfe40_ctrl->share_ctrl->vfebase +
+			VFE_BUS_STATS_AWB_WR_FRAMEDROP_PATTERN);
+
+	/* stats irq subsample config*/
+	msm_camera_io_w(0xFFFFFFFF,
+		vfe40_ctrl->share_ctrl->vfebase +
+			VFE_BUS_STATS_HIST_WR_IRQ_SUBSAMPLE_PATTERN);
+	msm_camera_io_w(0xFFFFFFFF,
+		vfe40_ctrl->share_ctrl->vfebase +
+			VFE_BUS_STATS_BG_WR_IRQ_SUBSAMPLE_PATTERN);
+	msm_camera_io_w(0xFFFFFFFF,
+		vfe40_ctrl->share_ctrl->vfebase +
+			VFE_BUS_STATS_BE_WR_IRQ_SUBSAMPLE_PATTERN);
+	msm_camera_io_w(0xFFFFFFFF,
+		vfe40_ctrl->share_ctrl->vfebase +
+			VFE_BUS_STATS_BF_WR_IRQ_SUBSAMPLE_PATTERN);
+	msm_camera_io_w(0xFFFFFFFF,
+		vfe40_ctrl->share_ctrl->vfebase +
+			VFE_BUS_STATS_RS_WR_IRQ_SUBSAMPLE_PATTERN);
+	msm_camera_io_w(0xFFFFFFFF,
+		vfe40_ctrl->share_ctrl->vfebase +
+			VFE_BUS_STATS_CS_WR_IRQ_SUBSAMPLE_PATTERN);
+	msm_camera_io_w(0xFFFFFFFF,
+		vfe40_ctrl->share_ctrl->vfebase +
+			VFE_BUS_STATS_SKIN_WR_IRQ_SUBSAMPLE_PATTERN);
+	msm_camera_io_w(0xFFFFFFFF,
+		vfe40_ctrl->share_ctrl->vfebase +
+			VFE_BUS_STATS_AWB_WR_IRQ_SUBSAMPLE_PATTERN);
+
 	vfe40_reset_dmi_tables(vfe40_ctrl);
 }
 
@@ -846,13 +907,16 @@
 	struct vfe40_ctrl_type *vfe40_ctrl)
 {
 	/* Stats control variables. */
-	memset(&(vfe40_ctrl->afbfStatsControl), 0,
+	memset(&(vfe40_ctrl->bfStatsControl), 0,
 		sizeof(struct vfe_stats_control));
 
 	memset(&(vfe40_ctrl->awbStatsControl), 0,
 		sizeof(struct vfe_stats_control));
 
-	memset(&(vfe40_ctrl->aecbgStatsControl), 0,
+	memset(&(vfe40_ctrl->bgStatsControl), 0,
+		sizeof(struct vfe_stats_control));
+
+	memset(&(vfe40_ctrl->beStatsControl), 0,
 		sizeof(struct vfe_stats_control));
 
 	memset(&(vfe40_ctrl->bhistStatsControl), 0,
@@ -967,7 +1031,7 @@
 	rc = vfe40_ctrl->stats_ops.dqbuf(
 			vfe40_ctrl->stats_ops.stats_ctrl, stats_type, &buf);
 	if (rc < 0) {
-		pr_err("%s: dq stats buf (type = %d) err = %d",
+		pr_err("%s: dq stats buf (type = %d) err = %d\n",
 			__func__, stats_type, rc);
 		return 0L;
 	}
@@ -1038,12 +1102,11 @@
 {
 	uint32_t addr;
 	unsigned long flags;
-
 	spin_lock_irqsave(&vfe40_ctrl->stats_bufq_lock, flags);
 	addr = (uint32_t)vfe40_stats_dqbuf(vfe40_ctrl, MSM_STATS_TYPE_AWB);
 	spin_unlock_irqrestore(&vfe40_ctrl->stats_bufq_lock, flags);
 	if (!addr) {
-		pr_err("%s: dq awb ping buf from free buf queue", __func__);
+		pr_err("%s: dq awb ping buf from free buf queue\n", __func__);
 		return -ENOMEM;
 	}
 	msm_camera_io_w(addr,
@@ -1053,7 +1116,7 @@
 	addr = (uint32_t)vfe40_stats_dqbuf(vfe40_ctrl, MSM_STATS_TYPE_AWB);
 	spin_unlock_irqrestore(&vfe40_ctrl->stats_bufq_lock, flags);
 	if (!addr) {
-		pr_err("%s: dq awb ping buf from free buf queue",
+		pr_err("%s: dq awb ping buf from free buf queue\n",
 			__func__);
 		return -ENOMEM;
 	}
@@ -1063,21 +1126,52 @@
 	return 0;
 }
 
-static uint32_t vfe_stats_aec_bg_buf_init(
+static uint32_t vfe_stats_be_buf_init(
 	struct vfe40_ctrl_type *vfe40_ctrl)
 {
 	uint32_t addr;
 	unsigned long flags;
 	uint32_t stats_type;
 
-	stats_type =
-		(!vfe40_use_bayer_stats(vfe40_ctrl)) ? MSM_STATS_TYPE_AEC
-			: MSM_STATS_TYPE_BG;
+	stats_type = MSM_STATS_TYPE_BE;
 	spin_lock_irqsave(&vfe40_ctrl->stats_bufq_lock, flags);
 	addr = (uint32_t)vfe40_stats_dqbuf(vfe40_ctrl, stats_type);
 	spin_unlock_irqrestore(&vfe40_ctrl->stats_bufq_lock, flags);
 	if (!addr) {
-		pr_err("%s: dq aec ping buf from free buf queue",
+		pr_err("%s: dq BE ping buf from free buf queue\n",
+			__func__);
+		return -ENOMEM;
+	}
+	msm_camera_io_w(addr,
+		vfe40_ctrl->share_ctrl->vfebase +
+		VFE_BUS_STATS_BE_WR_PING_ADDR);
+	spin_lock_irqsave(&vfe40_ctrl->stats_bufq_lock, flags);
+	addr = (uint32_t)vfe40_stats_dqbuf(vfe40_ctrl, stats_type);
+	spin_unlock_irqrestore(&vfe40_ctrl->stats_bufq_lock, flags);
+	if (!addr) {
+		pr_err("%s: dq BE pong buf from free buf queue\n",
+			__func__);
+		return -ENOMEM;
+	}
+	msm_camera_io_w(addr,
+		vfe40_ctrl->share_ctrl->vfebase +
+		VFE_BUS_STATS_BE_WR_PONG_ADDR);
+	return 0;
+}
+
+static uint32_t vfe_stats_bg_buf_init(
+	struct vfe40_ctrl_type *vfe40_ctrl)
+{
+	uint32_t addr;
+	unsigned long flags;
+	uint32_t stats_type;
+
+	stats_type = MSM_STATS_TYPE_BG;
+	spin_lock_irqsave(&vfe40_ctrl->stats_bufq_lock, flags);
+	addr = (uint32_t)vfe40_stats_dqbuf(vfe40_ctrl, stats_type);
+	spin_unlock_irqrestore(&vfe40_ctrl->stats_bufq_lock, flags);
+	if (!addr) {
+		pr_err("%s: dq aec/Bg ping buf from free buf queue\n",
 			__func__);
 		return -ENOMEM;
 	}
@@ -1088,7 +1182,7 @@
 	addr = (uint32_t)vfe40_stats_dqbuf(vfe40_ctrl, stats_type);
 	spin_unlock_irqrestore(&vfe40_ctrl->stats_bufq_lock, flags);
 	if (!addr) {
-		pr_err("%s: dq aec pong buf from free buf queue",
+		pr_err("%s: dq aec/Bg pong buf from free buf queue\n",
 			__func__);
 		return -ENOMEM;
 	}
@@ -1098,7 +1192,7 @@
 	return 0;
 }
 
-static int vfe_stats_af_bf_buf_init(
+static int vfe_stats_bf_buf_init(
 	struct vfe40_ctrl_type *vfe40_ctrl)
 {
 	uint32_t addr;
@@ -1106,9 +1200,7 @@
 	int rc = 0;
 
 	uint32_t stats_type;
-	stats_type =
-		(!vfe40_use_bayer_stats(vfe40_ctrl)) ? MSM_STATS_TYPE_AF
-			: MSM_STATS_TYPE_BF;
+	stats_type = MSM_STATS_TYPE_BF;
 
 	spin_lock_irqsave(&vfe40_ctrl->stats_bufq_lock, flags);
 	rc = vfe40_stats_flush_enqueue(vfe40_ctrl, stats_type);
@@ -1145,7 +1237,6 @@
 {
 	uint32_t addr;
 	unsigned long flags;
-
 	spin_lock_irqsave(&vfe40_ctrl->stats_bufq_lock, flags);
 	addr = (uint32_t)vfe40_stats_dqbuf(vfe40_ctrl, MSM_STATS_TYPE_BHIST);
 	spin_unlock_irqrestore(&vfe40_ctrl->stats_bufq_lock, flags);
@@ -1193,7 +1284,7 @@
 	addr = (uint32_t)vfe40_stats_dqbuf(vfe40_ctrl, MSM_STATS_TYPE_IHIST);
 	spin_unlock_irqrestore(&vfe40_ctrl->stats_bufq_lock, flags);
 	if (!addr) {
-		pr_err("%s: dq ihist pong buf from free buf queue",
+		pr_err("%s: dq Ihist pong buf from free buf queue",
 			__func__);
 		return -ENOMEM;
 	}
@@ -1299,6 +1390,7 @@
 		msm_camera_io_w_mb(1, vfe40_ctrl->share_ctrl->vfebase +
 			VFE_CAMIF_COMMAND);
 	}
+
 }
 
 static int vfe40_start_recording(
@@ -1811,7 +1903,7 @@
 			rc = -EFAULT;
 			goto proc_general_done;
 		}
-		rc = vfe_stats_aec_bg_buf_init(vfe40_ctrl);
+		rc = vfe_stats_bg_buf_init(vfe40_ctrl);
 		if (rc < 0) {
 			pr_err("%s: cannot config ping/pong address of AEC",
 				 __func__);
@@ -1845,7 +1937,7 @@
 			rc = -EFAULT;
 			goto proc_general_done;
 		}
-		rc = vfe_stats_af_bf_buf_init(vfe40_ctrl);
+		rc = vfe_stats_bf_buf_init(vfe40_ctrl);
 		if (rc < 0) {
 			pr_err("%s: cannot config ping/pong address of AF",
 				__func__);
@@ -1874,11 +1966,6 @@
 		}
 		break;
 	case VFE_CMD_STATS_AWB_START: {
-		if (vfe40_use_bayer_stats(vfe40_ctrl)) {
-			/* Error */
-			rc = -EFAULT;
-			goto proc_general_done;
-		}
 		rc = vfe_stats_awb_buf_init(vfe40_ctrl, NULL);
 		if (rc < 0) {
 			pr_err("%s: cannot config ping/pong address of AWB",
@@ -1990,30 +2077,35 @@
 		break;
 
 	case VFE_CMD_STATS_BG_START:
+	case VFE_CMD_STATS_BE_START:
 	case VFE_CMD_STATS_BF_START:
 	case VFE_CMD_STATS_BHIST_START: {
-		if (!vfe40_use_bayer_stats(vfe40_ctrl)) {
-			/* Error */
-			rc = -EFAULT;
-			goto proc_general_done;
-		}
 		old_val = msm_camera_io_r(
 			vfe40_ctrl->share_ctrl->vfebase + VFE_STATS_CFG);
 		module_val = msm_camera_io_r(
 			vfe40_ctrl->share_ctrl->vfebase + VFE_MODULE_CFG);
-		if (VFE_CMD_STATS_BG_START == cmd->id) {
-			module_val |= BG_ENABLE_MASK;
-			rc = vfe_stats_aec_bg_buf_init(vfe40_ctrl);
+
+		if (VFE_CMD_STATS_BE_START == cmd->id) {
+			module_val |= BE_ENABLE_MASK;
+			rc = vfe_stats_be_buf_init(vfe40_ctrl);
 			if (rc < 0) {
-				pr_err("%s: cannot config ping/pong address of CS",
+				pr_err("%s: cannot config ping/pong address of BG",
+					__func__);
+				goto proc_general_done;
+			}
+		} else if (VFE_CMD_STATS_BG_START == cmd->id) {
+			module_val |= BG_ENABLE_MASK;
+			rc = vfe_stats_bg_buf_init(vfe40_ctrl);
+			if (rc < 0) {
+				pr_err("%s: cannot config ping/pong address of BG",
 					__func__);
 				goto proc_general_done;
 			}
 		} else if (VFE_CMD_STATS_BF_START == cmd->id) {
 			module_val |= BF_ENABLE_MASK;
-			rc = vfe_stats_af_bf_buf_init(vfe40_ctrl);
+			rc = vfe_stats_bf_buf_init(vfe40_ctrl);
 			if (rc < 0) {
-				pr_err("%s: cannot config ping/pong address of CS",
+				pr_err("%s: cannot config ping/pong address of BF",
 					__func__);
 				goto proc_general_done;
 			}
@@ -2022,7 +2114,7 @@
 			old_val |= STATS_BHIST_ENABLE_MASK;
 			rc = vfe_stats_bhist_buf_init(vfe40_ctrl);
 			if (rc < 0) {
-				pr_err("%s: cannot config ping/pong address of CS",
+				pr_err("%s: cannot config ping/pong address of BHist",
 					__func__);
 				goto proc_general_done;
 			}
@@ -2644,11 +2736,6 @@
 		break;
 
 	case VFE_CMD_STATS_AWB_STOP: {
-		if (vfe40_use_bayer_stats(vfe40_ctrl)) {
-			/* Error */
-			rc = -EFAULT;
-			goto proc_general_done;
-		}
 		old_val = msm_camera_io_r(
 			vfe40_ctrl->share_ctrl->vfebase + VFE_MODULE_CFG);
 		old_val &= ~AWB_ENABLE_MASK;
@@ -2656,29 +2743,36 @@
 			vfe40_ctrl->share_ctrl->vfebase + VFE_MODULE_CFG);
 		}
 		break;
-	case VFE_CMD_STATS_AE_STOP: {
-		if (vfe40_use_bayer_stats(vfe40_ctrl)) {
-			/* Error */
-			rc = -EFAULT;
-			goto proc_general_done;
-		}
+	case VFE_CMD_STATS_BG_STOP: {
 		old_val = msm_camera_io_r(
 			vfe40_ctrl->share_ctrl->vfebase + VFE_MODULE_CFG);
-		old_val &= BG_ENABLE_MASK;
+		old_val &= ~BG_ENABLE_MASK;
 		msm_camera_io_w(old_val,
 			vfe40_ctrl->share_ctrl->vfebase + VFE_MODULE_CFG);
 		}
 		break;
-	case VFE_CMD_STATS_AF_STOP: {
-		if (vfe40_use_bayer_stats(vfe40_ctrl)) {
-			/* Error */
-			rc = -EFAULT;
-			goto proc_general_done;
-		}
+	case VFE_CMD_STATS_BF_STOP: {
 		old_val = msm_camera_io_r(
 			vfe40_ctrl->share_ctrl->vfebase + VFE_MODULE_CFG);
 		old_val &= ~BF_ENABLE_MASK;
 		msm_camera_io_w(old_val,
+		vfe40_ctrl->share_ctrl->vfebase + VFE_MODULE_CFG);
+
+		rc = vfe40_stats_flush_enqueue(vfe40_ctrl,
+				MSM_STATS_TYPE_BF);
+		if (rc < 0) {
+			pr_err("%s: dq stats buf err = %d",
+				   __func__, rc);
+			return -EINVAL;
+			}
+		}
+		break;
+
+	case VFE_CMD_STATS_BE_STOP: {
+		old_val = msm_camera_io_r(
+			vfe40_ctrl->share_ctrl->vfebase + VFE_MODULE_CFG);
+		old_val &= ~BE_ENABLE_MASK;
+		msm_camera_io_w(old_val,
 			vfe40_ctrl->share_ctrl->vfebase + VFE_MODULE_CFG);
 		}
 		break;
@@ -2710,8 +2804,6 @@
 		}
 		break;
 
-	case VFE_CMD_STATS_BG_STOP:
-	case VFE_CMD_STATS_BF_STOP:
 	case VFE_CMD_STATS_BHIST_STOP: {
 		if (!vfe40_use_bayer_stats(vfe40_ctrl)) {
 			/* Error */
@@ -2726,15 +2818,6 @@
 
 		msm_camera_io_w(old_val,
 			vfe40_ctrl->share_ctrl->vfebase + VFE_STATS_CFG);
-		if (VFE_CMD_STATS_BF_STOP == cmd->id) {
-			rc = vfe40_stats_flush_enqueue(vfe40_ctrl,
-					MSM_STATS_TYPE_BF);
-			if (rc < 0) {
-				pr_err("%s: dq stats buf err = %d",
-					   __func__, rc);
-				return -EINVAL;
-			}
-		}
 		}
 		break;
 
@@ -3299,42 +3382,51 @@
 
 				share_ctrl->liveshot_state =
 				VFE_STATE_STARTED;
+				msm_camera_io_w_mb(1, share_ctrl->vfebase +
+				VFE_REG_UPDATE_CMD);
 		}
 		break;
 	case VFE_STATE_STARTED:
-			share_ctrl->vfe_capture_count--;
-			if (!share_ctrl->vfe_capture_count &&
+		CDBG("%s disabling liveshot output\n", __func__);
+		if (share_ctrl->vfe_capture_count >= 1) {
+			if (share_ctrl->vfe_capture_count == 1 &&
 				(share_ctrl->comp_output_mode &
 				VFE40_OUTPUT_MODE_PRIMARY)) {
 				msm_camera_io_w(0, share_ctrl->vfebase +
 					vfe40_AXI_WM_CFG[
-				share_ctrl->outpath.out0.ch0]);
+					share_ctrl->outpath.out0.ch0]);
 				msm_camera_io_w(0, share_ctrl->vfebase +
 					vfe40_AXI_WM_CFG[
-				share_ctrl->outpath.out0.ch1]);
+					share_ctrl->outpath.out0.ch1]);
+				msm_camera_io_w_mb(1, share_ctrl->vfebase +
+					VFE_REG_UPDATE_CMD);
+			}
+			share_ctrl->vfe_capture_count--;
 		}
 		break;
 	case VFE_STATE_STOP_REQUESTED:
+		CDBG("%s disabling liveshot output from stream off\n",
+			__func__);
 		if (share_ctrl->comp_output_mode &
 			VFE40_OUTPUT_MODE_PRIMARY) {
 			/* Stop requested, stop write masters, and
 			 * trigger REG_UPDATE. Send STOP_LS_ACK in
 			 * next reg update. */
-				msm_camera_io_w(0, share_ctrl->vfebase +
-					vfe40_AXI_WM_CFG[
-				share_ctrl->outpath.out0.ch0]);
-				msm_camera_io_w(0, share_ctrl->vfebase +
-					vfe40_AXI_WM_CFG[
-				share_ctrl->outpath.out0.ch1]);
-				share_ctrl->liveshot_state = VFE_STATE_STOPPED;
-				msm_camera_io_w_mb(1, share_ctrl->vfebase +
+			msm_camera_io_w(0, share_ctrl->vfebase +
+				vfe40_AXI_WM_CFG[
+			share_ctrl->outpath.out0.ch0]);
+			msm_camera_io_w(0, share_ctrl->vfebase +
+				vfe40_AXI_WM_CFG[
+			share_ctrl->outpath.out0.ch1]);
+			share_ctrl->liveshot_state = VFE_STATE_STOPPED;
+			msm_camera_io_w_mb(1, share_ctrl->vfebase +
 				VFE_REG_UPDATE_CMD);
 		}
 		break;
 	case VFE_STATE_STOPPED:
 		CDBG("%s Sending STOP_LS ACK\n", __func__);
 		vfe40_send_isp_msg(&vfe40_ctrl->subdev,
-				share_ctrl->vfeFrameId, MSG_ID_STOP_LS_ACK);
+			share_ctrl->vfeFrameId, MSG_ID_STOP_LS_ACK);
 			share_ctrl->liveshot_state = VFE_STATE_IDLE;
 		break;
 	default:
@@ -3933,11 +4025,13 @@
 		VFE_BUS_PING_PONG_STATUS))
 	& ((uint32_t)(1<<(statsNum + 7)))) >> (statsNum + 7);
 	/* stats bits starts at 7 */
-	CDBG("statsNum %d, pingpongStatus %d\n", statsNum, pingpongStatus);
+	CDBG("%s:statsNum %d, pingpongStatus %d\n", __func__,
+		 statsNum, pingpongStatus);
 	pingpongAddr =
 		((uint32_t)(vfe40_ctrl->share_ctrl->vfebase +
 				VFE_BUS_STATS_PING_PONG_BASE)) +
-				(3*statsNum)*4 + (1-pingpongStatus)*4;
+				(VFE_STATS_BUS_REG_NUM*statsNum)*4 +
+				(1-pingpongStatus)*4;
 	returnAddr = msm_camera_io_r((uint32_t *)pingpongAddr);
 	msm_camera_io_w(newAddr, (uint32_t *)pingpongAddr);
 	return returnAddr;
@@ -3959,13 +4053,9 @@
 		msgStats.frameCounter--;
 	msgStats.buffer = bufAddress;
 	switch (statsNum) {
-	case statsAeNum:{
-		msgStats.id =
-			(!vfe40_use_bayer_stats(vfe40_ctrl)) ? MSG_ID_STATS_AEC
-				: MSG_ID_STATS_BG;
-		stats_type =
-			(!vfe40_use_bayer_stats(vfe40_ctrl)) ?
-				MSM_STATS_TYPE_AEC : MSM_STATS_TYPE_BG;
+	case statsBgNum:{
+		msgStats.id = MSG_ID_STATS_BG;
+		stats_type = MSM_STATS_TYPE_BG;
 		rc = vfe40_ctrl->stats_ops.dispatch(
 				vfe40_ctrl->stats_ops.stats_ctrl,
 				stats_type, bufAddress,
@@ -3973,13 +4063,19 @@
 				vfe40_ctrl->stats_ops.client);
 		}
 		break;
-	case statsAfNum:{
-		msgStats.id =
-			(!vfe40_use_bayer_stats(vfe40_ctrl)) ? MSG_ID_STATS_AF
-				: MSG_ID_STATS_BF;
-		stats_type =
-			(!vfe40_use_bayer_stats(vfe40_ctrl)) ? MSM_STATS_TYPE_AF
-				: MSM_STATS_TYPE_BF;
+	case statsBeNum:{
+		msgStats.id = MSG_ID_STATS_BE;
+		stats_type = MSM_STATS_TYPE_BE;
+		rc = vfe40_ctrl->stats_ops.dispatch(
+				vfe40_ctrl->stats_ops.stats_ctrl,
+				stats_type, bufAddress,
+				&msgStats.buf_idx, &vaddr, &msgStats.fd,
+				vfe40_ctrl->stats_ops.client);
+		}
+		break;
+	case statsBfNum:{
+		msgStats.id = MSG_ID_STATS_BF;
+		stats_type =  MSM_STATS_TYPE_BF;
 		rc = vfe40_ctrl->stats_ops.dispatch(
 				vfe40_ctrl->stats_ops.stats_ctrl,
 				stats_type, bufAddress,
@@ -4063,9 +4159,9 @@
 
 	msgStats.status_bits = status_bits;
 
-	msgStats.aec.buff = vfe40_ctrl->aecbgStatsControl.bufToRender;
+	msgStats.aec.buff = vfe40_ctrl->bgStatsControl.bufToRender;
 	msgStats.awb.buff = vfe40_ctrl->awbStatsControl.bufToRender;
-	msgStats.af.buff = vfe40_ctrl->afbfStatsControl.bufToRender;
+	msgStats.af.buff = vfe40_ctrl->bfStatsControl.bufToRender;
 
 	msgStats.ihist.buff = vfe40_ctrl->ihistStatsControl.bufToRender;
 	msgStats.rs.buff = vfe40_ctrl->rsStatsControl.bufToRender;
@@ -4080,28 +4176,49 @@
 				&msgStats);
 }
 
+static void vfe40_process_stats_be_irq(struct vfe40_ctrl_type *vfe40_ctrl)
+{
+	unsigned long flags;
+	uint32_t addr;
+	uint32_t stats_type;
+	stats_type = MSM_STATS_TYPE_BE;
+	spin_lock_irqsave(&vfe40_ctrl->stats_bufq_lock, flags);
+	addr = (uint32_t)vfe40_stats_dqbuf(vfe40_ctrl, stats_type);
+	spin_unlock_irqrestore(&vfe40_ctrl->stats_bufq_lock, flags);
+	if (addr) {
+		vfe40_ctrl->beStatsControl.bufToRender =
+			vfe40_process_stats_irq_common(vfe40_ctrl, statsBeNum,
+			addr);
+
+		vfe_send_stats_msg(vfe40_ctrl,
+			vfe40_ctrl->beStatsControl.bufToRender, statsBeNum);
+	} else{
+		vfe40_ctrl->beStatsControl.droppedStatsFrameCount++;
+		CDBG("%s: droppedStatsFrameCount = %d", __func__,
+			vfe40_ctrl->beStatsControl.droppedStatsFrameCount);
+	}
+}
+
 static void vfe40_process_stats_bg_irq(struct vfe40_ctrl_type *vfe40_ctrl)
 {
 	unsigned long flags;
 	uint32_t addr;
 	uint32_t stats_type;
-	stats_type =
-		(!vfe40_use_bayer_stats(vfe40_ctrl)) ? MSM_STATS_TYPE_AEC
-			: MSM_STATS_TYPE_BG;
+	stats_type = MSM_STATS_TYPE_BG;
 	spin_lock_irqsave(&vfe40_ctrl->stats_bufq_lock, flags);
 	addr = (uint32_t)vfe40_stats_dqbuf(vfe40_ctrl, stats_type);
 	spin_unlock_irqrestore(&vfe40_ctrl->stats_bufq_lock, flags);
 	if (addr) {
-		vfe40_ctrl->aecbgStatsControl.bufToRender =
-			vfe40_process_stats_irq_common(vfe40_ctrl, statsAeNum,
+		vfe40_ctrl->bgStatsControl.bufToRender =
+			vfe40_process_stats_irq_common(vfe40_ctrl, statsBgNum,
 			addr);
 
 		vfe_send_stats_msg(vfe40_ctrl,
-			vfe40_ctrl->aecbgStatsControl.bufToRender, statsAeNum);
+			vfe40_ctrl->bgStatsControl.bufToRender, statsBgNum);
 	} else{
-		vfe40_ctrl->aecbgStatsControl.droppedStatsFrameCount++;
+		vfe40_ctrl->bgStatsControl.droppedStatsFrameCount++;
 		CDBG("%s: droppedStatsFrameCount = %d", __func__,
-			vfe40_ctrl->aecbgStatsControl.droppedStatsFrameCount);
+			vfe40_ctrl->bgStatsControl.droppedStatsFrameCount);
 	}
 }
 
@@ -4131,23 +4248,21 @@
 	unsigned long flags;
 	uint32_t addr;
 	uint32_t stats_type;
-	stats_type =
-		(!vfe40_use_bayer_stats(vfe40_ctrl)) ? MSM_STATS_TYPE_AF
-			: MSM_STATS_TYPE_BF;
+	stats_type = MSM_STATS_TYPE_BF;
 	spin_lock_irqsave(&vfe40_ctrl->stats_bufq_lock, flags);
 	addr = (uint32_t)vfe40_stats_dqbuf(vfe40_ctrl, stats_type);
 	spin_unlock_irqrestore(&vfe40_ctrl->stats_bufq_lock, flags);
 	if (addr) {
-		vfe40_ctrl->afbfStatsControl.bufToRender =
-			vfe40_process_stats_irq_common(vfe40_ctrl, statsAfNum,
+		vfe40_ctrl->bfStatsControl.bufToRender =
+			vfe40_process_stats_irq_common(vfe40_ctrl, statsBfNum,
 			addr);
 
 		vfe_send_stats_msg(vfe40_ctrl,
-			vfe40_ctrl->afbfStatsControl.bufToRender, statsAfNum);
+			vfe40_ctrl->bfStatsControl.bufToRender, statsBfNum);
 	} else{
-		vfe40_ctrl->afbfStatsControl.droppedStatsFrameCount++;
+		vfe40_ctrl->bfStatsControl.droppedStatsFrameCount++;
 		CDBG("%s: droppedStatsFrameCount = %d", __func__,
-			vfe40_ctrl->afbfStatsControl.droppedStatsFrameCount);
+			vfe40_ctrl->bfStatsControl.droppedStatsFrameCount);
 	}
 }
 
@@ -4248,24 +4363,39 @@
 
 	CDBG("%s, stats = 0x%x\n", __func__, status_bits);
 	spin_lock_irqsave(&vfe40_ctrl->stats_bufq_lock, flags);
-	stats_type =
-		(!vfe40_use_bayer_stats(vfe40_ctrl)) ? MSM_STATS_TYPE_AEC
-			: MSM_STATS_TYPE_BG;
 
+	stats_type = MSM_STATS_TYPE_BE;
+	if (status_bits & VFE_IRQ_STATUS0_STATS_BE) {
+		addr = (uint32_t)vfe40_stats_dqbuf(vfe40_ctrl,
+				stats_type);
+		if (addr) {
+			vfe40_ctrl->beStatsControl.bufToRender =
+				vfe40_process_stats_irq_common(
+				vfe40_ctrl, statsBeNum, addr);
+			process_stats = true;
+		} else{
+			vfe40_ctrl->beStatsControl.bufToRender = 0;
+			vfe40_ctrl->beStatsControl.droppedStatsFrameCount++;
+		}
+	} else {
+		vfe40_ctrl->beStatsControl.bufToRender = 0;
+	}
+
+	stats_type = MSM_STATS_TYPE_BG;
 	if (status_bits & VFE_IRQ_STATUS0_STATS_BG) {
 		addr = (uint32_t)vfe40_stats_dqbuf(vfe40_ctrl,
 				stats_type);
 		if (addr) {
-			vfe40_ctrl->aecbgStatsControl.bufToRender =
+			vfe40_ctrl->bgStatsControl.bufToRender =
 				vfe40_process_stats_irq_common(
-				vfe40_ctrl, statsAeNum,	addr);
+				vfe40_ctrl, statsBgNum, addr);
 			process_stats = true;
 		} else{
-			vfe40_ctrl->aecbgStatsControl.bufToRender = 0;
-			vfe40_ctrl->aecbgStatsControl.droppedStatsFrameCount++;
+			vfe40_ctrl->bgStatsControl.bufToRender = 0;
+			vfe40_ctrl->bgStatsControl.droppedStatsFrameCount++;
 		}
 	} else {
-		vfe40_ctrl->aecbgStatsControl.bufToRender = 0;
+		vfe40_ctrl->bgStatsControl.bufToRender = 0;
 	}
 
 	if (status_bits & VFE_IRQ_STATUS0_STATS_AWB) {
@@ -4285,24 +4415,22 @@
 		vfe40_ctrl->awbStatsControl.bufToRender = 0;
 	}
 
-	stats_type =
-		(!vfe40_use_bayer_stats(vfe40_ctrl)) ? MSM_STATS_TYPE_AF
-			: MSM_STATS_TYPE_BF;
+	stats_type = MSM_STATS_TYPE_BF;
 	if (status_bits & VFE_IRQ_STATUS0_STATS_BF) {
 		addr = (uint32_t)vfe40_stats_dqbuf(vfe40_ctrl,
 					stats_type);
 		if (addr) {
-			vfe40_ctrl->afbfStatsControl.bufToRender =
+			vfe40_ctrl->bfStatsControl.bufToRender =
 				vfe40_process_stats_irq_common(
-				vfe40_ctrl, statsAfNum,
+				vfe40_ctrl, statsBfNum,
 				addr);
 			process_stats = true;
 		} else {
-			vfe40_ctrl->afbfStatsControl.bufToRender = 0;
-			vfe40_ctrl->afbfStatsControl.droppedStatsFrameCount++;
+			vfe40_ctrl->bfStatsControl.bufToRender = 0;
+			vfe40_ctrl->bfStatsControl.droppedStatsFrameCount++;
 		}
 	} else {
-		vfe40_ctrl->afbfStatsControl.bufToRender = 0;
+		vfe40_ctrl->bfStatsControl.bufToRender = 0;
 	}
 
 	if (status_bits & VFE_IRQ_STATUS0_STATS_IHIST) {
@@ -4411,6 +4539,10 @@
 		CDBG("Stats BG irq occured.\n");
 		vfe40_process_stats_bg_irq(vfe40_ctrl);
 		break;
+	case VFE_IRQ_STATUS0_STATS_BE:
+		CDBG("Stats BE irq occured.\n");
+		vfe40_process_stats_be_irq(vfe40_ctrl);
+		break;
 	case VFE_IRQ_STATUS0_STATS_BF:
 		CDBG("Stats BF irq occured.\n");
 		vfe40_process_stats_bf_irq(vfe40_ctrl);
@@ -4492,6 +4624,8 @@
 				(qcmd->vfeInterruptStatus0 &
 					VFE_IRQ_STATUS0_STATS_BG) |
 				(qcmd->vfeInterruptStatus0 &
+					VFE_IRQ_STATUS0_STATS_BE) |
+				(qcmd->vfeInterruptStatus0 &
 					VFE_IRQ_STATUS0_STATS_AWB) |
 				(qcmd->vfeInterruptStatus0 &
 					VFE_IRQ_STATUS0_STATS_BF) |
@@ -4580,6 +4714,12 @@
 					(void *)VFE_IRQ_STATUS0_STATS_BG);
 
 				if (qcmd->vfeInterruptStatus0 &
+						VFE_IRQ_STATUS0_STATS_BE)
+					v4l2_subdev_notify(&vfe40_ctrl->subdev,
+					NOTIFY_VFE_IRQ,
+					(void *)VFE_IRQ_STATUS0_STATS_BE);
+
+				if (qcmd->vfeInterruptStatus0 &
 						VFE_IRQ_STATUS0_STATS_AWB)
 					v4l2_subdev_notify(&vfe40_ctrl->subdev,
 					NOTIFY_VFE_IRQ,
@@ -4615,19 +4755,19 @@
 					NOTIFY_VFE_IRQ,
 					(void *)VFE_IRQ_STATUS0_STATS_CS);
 
-				if (qcmd->vfeInterruptStatus0 &
+				if (qcmd->vfeInterruptStatus1 &
 						VFE_IRQ_STATUS1_SYNC_TIMER0)
 					v4l2_subdev_notify(&vfe40_ctrl->subdev,
 					NOTIFY_VFE_IRQ,
 					(void *)VFE_IRQ_STATUS1_SYNC_TIMER0);
 
-				if (qcmd->vfeInterruptStatus0 &
+				if (qcmd->vfeInterruptStatus1 &
 						VFE_IRQ_STATUS1_SYNC_TIMER1)
 					v4l2_subdev_notify(&vfe40_ctrl->subdev,
 					NOTIFY_VFE_IRQ,
 					(void *)VFE_IRQ_STATUS1_SYNC_TIMER1);
 
-				if (qcmd->vfeInterruptStatus0 &
+				if (qcmd->vfeInterruptStatus1 &
 						VFE_IRQ_STATUS1_SYNC_TIMER2)
 					v4l2_subdev_notify(&vfe40_ctrl->subdev,
 					NOTIFY_VFE_IRQ,
@@ -4849,6 +4989,7 @@
 		cmd->cmd_type != CMD_STATS_CS_BUF_RELEASE &&
 		cmd->cmd_type != CMD_STATS_AF_BUF_RELEASE &&
 		cmd->cmd_type != CMD_STATS_BG_BUF_RELEASE &&
+		cmd->cmd_type != CMD_STATS_BE_BUF_RELEASE &&
 		cmd->cmd_type != CMD_STATS_BF_BUF_RELEASE &&
 		cmd->cmd_type != CMD_STATS_BHIST_BUF_RELEASE &&
 		cmd->cmd_type != CMD_VFE_PIX_SOF_COUNT_UPDATE &&
@@ -4892,6 +5033,7 @@
 		(cmd->cmd_type == CMD_STATS_CS_ENABLE)    ||
 		(cmd->cmd_type == CMD_STATS_AEC_ENABLE)   ||
 		(cmd->cmd_type == CMD_STATS_BG_ENABLE)    ||
+		(cmd->cmd_type == CMD_STATS_BE_ENABLE)    ||
 		(cmd->cmd_type == CMD_STATS_BF_ENABLE)    ||
 		(cmd->cmd_type == CMD_STATS_BHIST_ENABLE)) {
 		struct axidata *axid;
@@ -4915,6 +5057,7 @@
 		switch (cmd->cmd_type) {
 		case CMD_STATS_AEC_ENABLE:
 		case CMD_STATS_BG_ENABLE:
+		case CMD_STATS_BE_ENABLE:
 		case CMD_STATS_BF_ENABLE:
 		case CMD_STATS_BHIST_ENABLE:
 		case CMD_STATS_AWB_ENABLE:
@@ -6288,8 +6431,8 @@
 		axi40_do_tasklet, (unsigned long)axi_ctrl);
 
 	vfe40_ctrl->pdev = pdev;
-	/*disable bayer stats by default*/
-	vfe40_ctrl->ver_num.main = 0;
+	/*enable bayer stats by default*/
+	vfe40_ctrl->ver_num.main = 4;
 
 	return 0;
 
diff --git a/drivers/media/video/msm/vfe/msm_vfe40.h b/drivers/media/video/msm/vfe/msm_vfe40.h
index 8201d18..4acc7e4 100644
--- a/drivers/media/video/msm/vfe/msm_vfe40.h
+++ b/drivers/media/video/msm/vfe/msm_vfe40.h
@@ -796,7 +796,7 @@
 #define VFE_BUS_STATS_BE_WR_PING_ADDR    0x00000168
 #define VFE_BUS_STATS_BE_WR_PONG_ADDR    0x0000016C
 #define VFE_BUS_STATS_BE_WR_ADDR_CFG    0x00000170
-#define VFE_BUS_STATS_BE_UB_CFG          0x00000174
+#define VFE_BUS_STATS_BE_WR_UB_CFG          0x00000174
 #define VFE_BUS_STATS_BE_WR_FRAMEDROP_PATTERN  0x00000178
 #define VFE_BUS_STATS_BE_WR_IRQ_SUBSAMPLE_PATTERN 0x0000017C
 
@@ -1014,9 +1014,10 @@
 	uint32_t sync_timer_number;
 
 	struct msm_ver_num_info ver_num;
-	struct vfe_stats_control afbfStatsControl;
+	struct vfe_stats_control beStatsControl;
+	struct vfe_stats_control bfStatsControl;
 	struct vfe_stats_control awbStatsControl;
-	struct vfe_stats_control aecbgStatsControl;
+	struct vfe_stats_control bgStatsControl;
 	struct vfe_stats_control ihistStatsControl;
 	struct vfe_stats_control rsStatsControl;
 	struct vfe_stats_control csStatsControl;
@@ -1035,13 +1036,16 @@
 	uint32_t simultaneous_sof_stat;
 };
 
-#define statsAeNum      0
-#define statsAfNum      1
-#define statsAwbNum     2
-#define statsRsNum      3
-#define statsCsNum      4
-#define statsIhistNum   5
-#define statsSkinNum    6
+#define statsBeNum      0
+#define statsBgNum      1
+#define statsBfNum      2
+#define statsAwbNum     3
+#define statsRsNum      4
+#define statsCsNum      5
+#define statsIhistNum   6
+#define statsSkinNum    7
+
+#define VFE_STATS_BUS_REG_NUM  6
 
 struct vfe_cmd_stats_ack {
 	uint32_t  nextStatsBuf;
diff --git a/drivers/media/video/msm_vidc/Makefile b/drivers/media/video/msm_vidc/Makefile
index 2a1f40f..9b3af9d 100644
--- a/drivers/media/video/msm_vidc/Makefile
+++ b/drivers/media/video/msm_vidc/Makefile
@@ -5,5 +5,6 @@
 				msm_venc.o \
 				msm_smem.o \
 				msm_vidc_debug.o \
+				msm_vidc_ssr.o \
 				vidc_hal.o \
 				vidc_hal_interrupt_handler.o \
diff --git a/drivers/media/video/msm_vidc/msm_v4l2_vidc.c b/drivers/media/video/msm_vidc/msm_v4l2_vidc.c
index fda03de..80fdac5 100644
--- a/drivers/media/video/msm_vidc/msm_v4l2_vidc.c
+++ b/drivers/media/video/msm_vidc/msm_v4l2_vidc.c
@@ -29,6 +29,7 @@
 #include "msm_vidc_debug.h"
 #include "vidc_hal_api.h"
 #include "msm_smem.h"
+#include "msm_vidc_ssr.h"
 
 #define BASE_DEVICE_NUMBER 32
 #define SHARED_QSIZE 0x1000000
@@ -441,12 +442,13 @@
 struct buffer_info {
 	struct list_head list;
 	int type;
-	int fd;
-	int buff_off;
-	int size;
-	u32 uvaddr;
-	u32 device_addr;
-	struct msm_smem *handle;
+	int num_planes;
+	int fd[VIDEO_MAX_PLANES];
+	int buff_off[VIDEO_MAX_PLANES];
+	int size[VIDEO_MAX_PLANES];
+	u32 uvaddr[VIDEO_MAX_PLANES];
+	u32 device_addr[VIDEO_MAX_PLANES];
+	struct msm_smem *handle[VIDEO_MAX_PLANES];
 };
 
 struct msm_v4l2_vid_inst {
@@ -471,26 +473,37 @@
 }
 
 struct buffer_info *get_registered_buf(struct list_head *list,
-				int fd, u32 buff_off, u32 size)
+				int fd, u32 buff_off, u32 size, int *plane)
 {
 	struct buffer_info *temp;
 	struct buffer_info *ret = NULL;
-	if (!list || fd < 0) {
+	int i;
+	if (!list || fd < 0 || !plane) {
 		dprintk(VIDC_ERR, "Invalid input\n");
 		goto err_invalid_input;
 	}
+	*plane = 0;
 	if (!list_empty(list)) {
 		list_for_each_entry(temp, list, list) {
-			if (temp && temp->fd == fd &&
-			(CONTAINS(temp->buff_off, temp->size, buff_off)
-			|| CONTAINS(buff_off, size, temp->buff_off)
-			|| OVERLAPS(buff_off, size,
-				temp->buff_off, temp->size))) {
-				dprintk(VIDC_WARN,
-				"This memory region is already mapped\n");
-				ret = temp;
-				break;
+			for (i = 0; (i < temp->num_planes)
+				&& (i < VIDEO_MAX_PLANES); i++) {
+				if (temp && temp->fd[i] == fd &&
+						(CONTAINS(temp->buff_off[i],
+						temp->size[i], buff_off)
+						 || CONTAINS(buff_off,
+						 size, temp->buff_off[i])
+						 || OVERLAPS(buff_off, size,
+						 temp->buff_off[i],
+						 temp->size[i]))) {
+					dprintk(VIDC_DBG,
+							"This memory region is already mapped\n");
+					ret = temp;
+					*plane = i;
+					break;
+				}
 			}
+			if (ret)
+				break;
 		}
 	}
 err_invalid_input:
@@ -498,25 +511,62 @@
 }
 
 struct buffer_info *get_same_fd_buffer(struct list_head *list,
-		int fd)
+		int fd, int *plane)
 {
 	struct buffer_info *temp;
 	struct buffer_info *ret = NULL;
-	if (!list || fd < 0) {
+	int i;
+	if (!list || fd < 0 || !plane) {
+		dprintk(VIDC_ERR, "Invalid input\n");
+		goto err_invalid_input;
+	}
+	*plane = 0;
+	if (!list_empty(list)) {
+		list_for_each_entry(temp, list, list) {
+			for (i = 0; (i < temp->num_planes)
+				&& (i < VIDEO_MAX_PLANES); i++) {
+				if (temp && temp->fd[i] == fd)  {
+					dprintk(VIDC_INFO,
+					"Found same fd buffer\n");
+					ret = temp;
+					*plane = i;
+					break;
+				}
+			}
+			if (ret)
+				break;
+		}
+	}
+err_invalid_input:
+	return ret;
+}
+static u32 device_to_uvaddr(struct list_head *list, u32 device_addr)
+{
+	struct buffer_info *temp;
+	u32 uvaddr = 0;
+	int i;
+	if (!list || !device_addr) {
 		dprintk(VIDC_ERR, "Invalid input\n");
 		goto err_invalid_input;
 	}
 	if (!list_empty(list)) {
 		list_for_each_entry(temp, list, list) {
-			if (temp && temp->fd == fd)  {
-				dprintk(VIDC_ERR, "Found same fd buffer\n");
-				ret = temp;
-				break;
+			for (i = 0; (i < temp->num_planes)
+				&& (i < VIDEO_MAX_PLANES); i++) {
+				if (temp && temp->device_addr[i]
+						== device_addr)  {
+					dprintk(VIDC_INFO,
+					"Found same fd buffer\n");
+					uvaddr = temp->uvaddr[i];
+					break;
+				}
 			}
+			if (uvaddr)
+				break;
 		}
 	}
 err_invalid_input:
-	return ret;
+	return uvaddr;
 }
 
 static int msm_v4l2_open(struct file *filp)
@@ -566,25 +616,29 @@
 	struct list_head *ptr, *next;
 	struct buffer_info *bi;
 	struct v4l2_buffer buffer_info;
-	struct v4l2_plane plane;
+	struct v4l2_plane plane[VIDEO_MAX_PLANES];
 	int rc = 0;
+	int i;
 	list_for_each_safe(ptr, next, &v4l2_inst->registered_bufs) {
 		bi = list_entry(ptr, struct buffer_info, list);
 		if (bi->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
 			buffer_info.type = bi->type;
-			plane.reserved[0] = bi->fd;
-			plane.reserved[1] = bi->buff_off;
-			plane.length = bi->size;
-			plane.m.userptr = bi->device_addr;
-			buffer_info.m.planes = &plane;
-			buffer_info.length = 1;
-			dprintk(VIDC_DBG,
-				"Releasing buffer: %d, %d, %d\n",
-				buffer_info.m.planes[0].reserved[0],
-				buffer_info.m.planes[0].reserved[1],
-				buffer_info.m.planes[0].length);
+			for (i = 0; (i < bi->num_planes)
+				&& (i < VIDEO_MAX_PLANES); i++) {
+				plane[i].reserved[0] = bi->fd[i];
+				plane[i].reserved[1] = bi->buff_off[i];
+				plane[i].length = bi->size[i];
+				plane[i].m.userptr = bi->device_addr[i];
+				buffer_info.m.planes = plane;
+				dprintk(VIDC_DBG,
+					"Releasing buffer: %d, %d, %d\n",
+					buffer_info.m.planes[i].reserved[0],
+					buffer_info.m.planes[i].reserved[1],
+					buffer_info.m.planes[i].length);
+			}
+			buffer_info.length = bi->num_planes;
 			rc = msm_vidc_release_buf(v4l2_inst->vidc_inst,
-				&buffer_info);
+					&buffer_info);
 			if (rc)
 				dprintk(VIDC_ERR,
 					"Failed Release buffer: %d, %d, %d\n",
@@ -592,11 +646,13 @@
 					buffer_info.m.planes[0].reserved[1],
 					buffer_info.m.planes[0].length);
 			list_del(&bi->list);
-			if (bi->handle)
-				msm_smem_free(v4l2_inst->mem_client,
-					bi->handle);
-			kfree(bi);
+			for (i = 0; i < bi->num_planes; i++) {
+				if (bi->handle[i])
+					msm_smem_free(v4l2_inst->mem_client,
+							bi->handle[i]);
 			}
+			kfree(bi);
+		}
 	}
 	return rc;
 }
@@ -608,24 +664,28 @@
 	struct buffer_info *bi;
 	struct msm_vidc_inst *vidc_inst;
 	struct msm_v4l2_vid_inst *v4l2_inst;
+	int i;
 	vidc_inst = get_vidc_inst(filp, NULL);
 	v4l2_inst = get_v4l2_inst(filp, NULL);
 	rc = msm_v4l2_release_output_buffers(v4l2_inst);
 	if (rc)
 		dprintk(VIDC_WARN,
 			"Failed in %s for release output buffers\n", __func__);
-	rc = msm_vidc_close(vidc_inst);
 	list_for_each_safe(ptr, next, &v4l2_inst->registered_bufs) {
 		bi = list_entry(ptr, struct buffer_info, list);
 		if (bi->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
 			list_del(&bi->list);
-			if (bi->handle)
-				msm_smem_free(v4l2_inst->mem_client,
-					bi->handle);
+			for (i = 0; (i < bi->num_planes)
+				&& (i < VIDEO_MAX_PLANES); i++) {
+				if (bi->handle[i])
+					msm_smem_free(v4l2_inst->mem_client,
+							bi->handle[i]);
+			}
 			kfree(bi);
 		}
 	}
 	msm_smem_delete_client(v4l2_inst->mem_client);
+	rc = msm_vidc_close(vidc_inst);
 	kfree(v4l2_inst);
 	return rc;
 }
@@ -695,6 +755,7 @@
 	struct buffer_info *temp;
 	struct msm_vidc_inst *vidc_inst;
 	struct msm_v4l2_vid_inst *v4l2_inst;
+	int plane = 0;
 	int i, rc = 0;
 	vidc_inst = get_vidc_inst(file, fh);
 	v4l2_inst = get_v4l2_inst(file, fh);
@@ -703,34 +764,46 @@
 		rc = -ENOMEM;
 		goto exit;
 	}
+	binfo = kzalloc(sizeof(*binfo), GFP_KERNEL);
+	if (!binfo) {
+		dprintk(VIDC_ERR, "Out of memory\n");
+		rc = -ENOMEM;
+		goto exit;
+	}
+	if (b->length > VIDEO_MAX_PLANES) {
+		dprintk(VIDC_ERR, "Num planes exceeds max: %d, %d\n",
+			b->length, VIDEO_MAX_PLANES);
+		rc = -EINVAL;
+		goto exit;
+	}
 	for (i = 0; i < b->length; ++i) {
-		binfo = get_registered_buf(&v4l2_inst->registered_bufs,
+		if (EXTRADATA_IDX(b->length) &&
+			(i == EXTRADATA_IDX(b->length)) &&
+			!b->m.planes[i].length) {
+			continue;
+		}
+		temp = get_registered_buf(&v4l2_inst->registered_bufs,
 				b->m.planes[i].reserved[0],
 				b->m.planes[i].reserved[1],
-				b->m.planes[i].length);
-		if (binfo) {
-			dprintk(VIDC_WARN,
+				b->m.planes[i].length, &plane);
+		if (temp) {
+			dprintk(VIDC_DBG,
 				"This memory region has already been prepared\n");
 			rc = -EINVAL;
-			goto exit;
-		}
-		binfo = kzalloc(sizeof(*binfo), GFP_KERNEL);
-		if (!binfo) {
-			dprintk(VIDC_ERR, "Out of memory\n");
-			rc = -ENOMEM;
+			kfree(binfo);
 			goto exit;
 		}
 		temp = get_same_fd_buffer(&v4l2_inst->registered_bufs,
-				b->m.planes[i].reserved[0]);
+				b->m.planes[i].reserved[0], &plane);
 		if (temp) {
 			binfo->type = b->type;
-			binfo->fd = b->m.planes[i].reserved[0];
-			binfo->buff_off = b->m.planes[i].reserved[1];
-			binfo->size = b->m.planes[i].length;
-			binfo->uvaddr = b->m.planes[i].m.userptr;
-			binfo->device_addr =
-				temp->handle->device_addr + binfo->buff_off;
-			binfo->handle = NULL;
+			binfo->fd[i] = b->m.planes[i].reserved[0];
+			binfo->buff_off[i] = b->m.planes[i].reserved[1];
+			binfo->size[i] = b->m.planes[i].length;
+			binfo->uvaddr[i] = b->m.planes[i].m.userptr;
+			binfo->device_addr[i] =
+			temp->handle[plane]->device_addr + binfo->buff_off[i];
+			binfo->handle[i] = NULL;
 		} else {
 			handle = msm_smem_user_to_kernel(v4l2_inst->mem_client,
 			b->m.planes[i].reserved[0],
@@ -744,21 +817,22 @@
 				goto exit;
 			}
 			binfo->type = b->type;
-			binfo->fd = b->m.planes[i].reserved[0];
-			binfo->buff_off = b->m.planes[i].reserved[1];
-			binfo->size = b->m.planes[i].length;
-			binfo->uvaddr = b->m.planes[i].m.userptr;
-			binfo->device_addr =
-				handle->device_addr + binfo->buff_off;
-			binfo->handle = handle;
+			binfo->fd[i] = b->m.planes[i].reserved[0];
+			binfo->buff_off[i] = b->m.planes[i].reserved[1];
+			binfo->size[i] = b->m.planes[i].length;
+			binfo->uvaddr[i] = b->m.planes[i].m.userptr;
+			binfo->device_addr[i] =
+				handle->device_addr + binfo->buff_off[i];
+			binfo->handle[i] = handle;
 			dprintk(VIDC_DBG, "Registering buffer: %d, %d, %d\n",
 					b->m.planes[i].reserved[0],
 					b->m.planes[i].reserved[1],
 					b->m.planes[i].length);
 		}
-		list_add_tail(&binfo->list, &v4l2_inst->registered_bufs);
-		b->m.planes[i].m.userptr = binfo->device_addr;
+		b->m.planes[i].m.userptr = binfo->device_addr[i];
 	}
+	binfo->num_planes = b->length;
+	list_add_tail(&binfo->list, &v4l2_inst->registered_bufs);
 	rc = msm_vidc_prepare_buf(v4l2_inst->vidc_inst, b);
 exit:
 	return rc;
@@ -770,15 +844,27 @@
 	struct msm_vidc_inst *vidc_inst;
 	struct msm_v4l2_vid_inst *v4l2_inst;
 	struct buffer_info *binfo;
+	int plane = 0;
 	int rc = 0;
 	int i;
+	if (b->length > VIDEO_MAX_PLANES) {
+		dprintk(VIDC_ERR, "num planes exceeds max: %d\n",
+			b->length);
+		return -EINVAL;
+	}
 	vidc_inst = get_vidc_inst(file, fh);
 	v4l2_inst = get_v4l2_inst(file, fh);
 	for (i = 0; i < b->length; ++i) {
+		if (EXTRADATA_IDX(b->length) &&
+			(i == EXTRADATA_IDX(b->length)) &&
+			!b->m.planes[i].length) {
+			b->m.planes[i].m.userptr = 0;
+			continue;
+		}
 		binfo = get_registered_buf(&v4l2_inst->registered_bufs,
 				b->m.planes[i].reserved[0],
 				b->m.planes[i].reserved[1],
-				b->m.planes[i].length);
+				b->m.planes[i].length, &plane);
 		if (!binfo) {
 			dprintk(VIDC_ERR,
 				"This buffer is not registered: %d, %d, %d\n",
@@ -788,12 +874,12 @@
 			rc = -EINVAL;
 			goto err_invalid_buff;
 		}
-		b->m.planes[i].m.userptr = binfo->device_addr;
+		b->m.planes[i].m.userptr = binfo->device_addr[i];
 		dprintk(VIDC_DBG, "Queueing device address = 0x%x\n",
-				binfo->device_addr);
-		if (binfo->handle) {
+				binfo->device_addr[i]);
+		if (binfo->handle[i]) {
 			rc = msm_smem_clean_invalidate(v4l2_inst->mem_client,
-					binfo->handle);
+					binfo->handle[i]);
 			if (rc) {
 				dprintk(VIDC_ERR,
 					"Failed to clean caches: %d\n", rc);
@@ -809,8 +895,42 @@
 int msm_v4l2_dqbuf(struct file *file, void *fh,
 				struct v4l2_buffer *b)
 {
+	int rc = 0;
+	int i;
+	struct msm_v4l2_vid_inst *v4l2_inst;
 	struct msm_vidc_inst *vidc_inst = get_vidc_inst(file, fh);
-	return msm_vidc_dqbuf((void *)vidc_inst, b);
+	if (b->length > VIDEO_MAX_PLANES) {
+		dprintk(VIDC_ERR, "num planes exceed maximum: %d\n",
+			b->length);
+		return -EINVAL;
+	}
+	v4l2_inst = get_v4l2_inst(file, fh);
+	rc = msm_vidc_dqbuf((void *)vidc_inst, b);
+	if (rc) {
+		dprintk(VIDC_DBG,
+			"Failed to dqbuf, capability: %d, rc: %d\n",
+			b->type, rc);
+		goto fail_dq_buf;
+	}
+	for (i = 0; i < b->length; i++) {
+		if (EXTRADATA_IDX(b->length) &&
+				(i == EXTRADATA_IDX(b->length)) &&
+				!b->m.planes[i].m.userptr) {
+			continue;
+		}
+		b->m.planes[i].m.userptr = device_to_uvaddr(
+				&v4l2_inst->registered_bufs,
+				b->m.planes[i].m.userptr);
+		if (!b->m.planes[i].m.userptr) {
+			dprintk(VIDC_ERR,
+			"Failed to find user virtual address, 0x%lx, %d, %d\n",
+			b->m.planes[i].m.userptr, b->type, i);
+			rc = -EINVAL;
+			goto fail_dq_buf;
+		}
+	}
+fail_dq_buf:
+	return rc;
 }
 
 int msm_v4l2_streamon(struct file *file, void *fh,
@@ -854,17 +974,35 @@
 		rc = msm_v4l2_release_output_buffers(v4l2_inst);
 	if (rc)
 		dprintk(VIDC_WARN,
-			"Failed in %s for release output buffers\n", __func__);
+			"Failed to release dec output buffers: %d\n", rc);
 	return msm_vidc_decoder_cmd((void *)vidc_inst, dec);
 }
 
 static int msm_v4l2_encoder_cmd(struct file *file, void *fh,
 				struct v4l2_encoder_cmd *enc)
 {
+	struct msm_v4l2_vid_inst *v4l2_inst;
 	struct msm_vidc_inst *vidc_inst = get_vidc_inst(file, fh);
+	int rc = 0;
+	v4l2_inst = get_v4l2_inst(file, NULL);
+	if (enc->cmd == V4L2_ENC_CMD_STOP)
+		rc = msm_v4l2_release_output_buffers(v4l2_inst);
+	if (rc)
+		dprintk(VIDC_WARN,
+			"Failed to release enc output buffers: %d\n", rc);
 	return msm_vidc_encoder_cmd((void *)vidc_inst, enc);
 }
-
+static int msm_v4l2_s_parm(struct file *file, void *fh,
+			struct v4l2_streamparm *a)
+{
+	struct msm_vidc_inst *vidc_inst = get_vidc_inst(file, fh);
+	return msm_vidc_s_parm((void *)vidc_inst, a);
+}
+static int msm_v4l2_g_parm(struct file *file, void *fh,
+		struct v4l2_streamparm *a)
+{
+	return 0;
+}
 static const struct v4l2_ioctl_ops msm_v4l2_ioctl_ops = {
 	.vidioc_querycap = msm_v4l2_querycap,
 	.vidioc_enum_fmt_vid_cap_mplane = msm_v4l2_enum_fmt,
@@ -885,6 +1023,8 @@
 	.vidioc_unsubscribe_event = msm_v4l2_unsubscribe_event,
 	.vidioc_decoder_cmd = msm_v4l2_decoder_cmd,
 	.vidioc_encoder_cmd = msm_v4l2_encoder_cmd,
+	.vidioc_s_parm = msm_v4l2_s_parm,
+	.vidioc_g_parm = msm_v4l2_g_parm
 };
 
 static const struct v4l2_ioctl_ops msm_v4l2_enc_ioctl_ops = {
@@ -1153,7 +1293,7 @@
 		ocmem_notifier_register(OCMEM_VIDEO, &ocmem->vidc_ocmem_nb);
 	if (!ocmem->handle) {
 		dprintk(VIDC_WARN, "Failed to register OCMEM notifier.");
-		dprintk(VIDC_WARN, " Performance will be impacted\n");
+		dprintk(VIDC_INFO, " Performance will be impacted\n");
 	}
 	return rc;
 fail_register_domains:
@@ -1243,6 +1383,9 @@
 	core->debugfs_root = msm_vidc_debugfs_init_core(
 		core, vidc_driver->debugfs_root);
 	pdev->dev.platform_data = core;
+	rc = msm_vidc_ssr_init(core);
+	if (rc < 0)
+		dprintk(VIDC_ERR, "msm_vidc : Sub Systrem Restart failed\n");
 	return rc;
 
 err_cores_exceeded:
@@ -1271,6 +1414,7 @@
 	vidc_hal_delete_device(core->device);
 	video_unregister_device(&core->vdev[MSM_VIDC_ENCODER].vdev);
 	video_unregister_device(&core->vdev[MSM_VIDC_DECODER].vdev);
+	rc = msm_vidc_ssr_uninit(core);
 	v4l2_device_unregister(&core->v4l2_dev);
 	if (core->resources.ocmem.handle)
 		ocmem_notifier_unregister(core->resources.ocmem.handle,
diff --git a/drivers/media/video/msm_vidc/msm_vdec.c b/drivers/media/video/msm_vidc/msm_vdec.c
index d843d87..22063d4 100644
--- a/drivers/media/video/msm_vidc/msm_vdec.c
+++ b/drivers/media/video/msm_vidc/msm_vdec.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -20,7 +20,6 @@
 #include "msm_vidc_debug.h"
 
 #define MSM_VDEC_DVC_NAME "msm_vdec_8974"
-#define MAX_PLANES 1
 #define DEFAULT_HEIGHT 720
 #define DEFAULT_WIDTH 1280
 #define MAX_SUPPORTED_WIDTH 1920
@@ -150,6 +149,17 @@
 		.menu_skip_mask = 0,
 		.qmenu = NULL,
 	},
+	{
+		.id = V4L2_CID_MPEG_VIDC_VIDEO_SYNC_FRAME_DECODE,
+		.name = "Sync Frame Decode",
+		.type = V4L2_CTRL_TYPE_BOOLEAN,
+		.minimum = V4L2_MPEG_VIDC_VIDEO_SYNC_FRAME_DECODE_DISABLE,
+		.maximum = V4L2_MPEG_VIDC_VIDEO_SYNC_FRAME_DECODE_ENABLE,
+		.default_value = V4L2_MPEG_VIDC_VIDEO_SYNC_FRAME_DECODE_DISABLE,
+		.step = 1,
+		.menu_skip_mask = 0,
+		.qmenu = NULL,
+	},
 };
 
 #define NUM_CTRLS ARRAY_SIZE(msm_vdec_ctrls)
@@ -157,28 +167,7 @@
 static u32 get_frame_size_nv12(int plane,
 					u32 height, u32 width)
 {
-	int size;
-	int luma_h, luma_w, luma_stride, luma_scanl, luma_size;
-	int chroma_h, chroma_w, chroma_stride, chroma_scanl, chroma_size;
-
-	luma_w = width;
-	luma_h = height;
-
-	chroma_w = luma_w;
-	chroma_h = luma_h/2;
-	NV12_IL_CALC_Y_STRIDE(luma_stride, luma_w, 32);
-	NV12_IL_CALC_Y_BUFHEIGHT(luma_scanl, luma_h, 32);
-	NV12_IL_CALC_UV_STRIDE(chroma_stride, chroma_w, 32);
-	NV12_IL_CALC_UV_BUFHEIGHT(chroma_scanl, luma_h, 32);
-	NV12_IL_CALC_BUF_SIZE(size, luma_size, luma_stride,
-		luma_scanl, chroma_size, chroma_stride, chroma_scanl, 32);
-	size = ALIGN(size, SZ_4K);
-	return size;
-}
-static u32 get_frame_size_nv21(int plane,
-					u32 height, u32 width)
-{
-	return height * width * 2;
+	return VENUS_BUFFER_SIZE(COLOR_FMT_NV12, width, height);
 }
 
 static u32 get_frame_size_compressed(int plane,
@@ -192,7 +181,7 @@
 		.name = "YCbCr Semiplanar 4:2:0",
 		.description = "Y/CbCr 4:2:0",
 		.fourcc = V4L2_PIX_FMT_NV12,
-		.num_planes = 1,
+		.num_planes = 2,
 		.get_frame_size = get_frame_size_nv12,
 		.type = CAPTURE_PORT,
 	},
@@ -253,14 +242,6 @@
 		.type = OUTPUT_PORT,
 	},
 	{
-		.name = "YCrCb Semiplanar 4:2:0",
-		.description = "Y/CrCb 4:2:0",
-		.fourcc = V4L2_PIX_FMT_NV21,
-		.num_planes = 1,
-		.get_frame_size = get_frame_size_nv21,
-		.type = CAPTURE_PORT,
-	},
-	{
 		.name = "DIVX 311",
 		.description = "DIVX 311 compressed format",
 		.fourcc = V4L2_PIX_FMT_DIVX_311,
@@ -321,45 +302,52 @@
 					struct v4l2_buffer *b)
 {
 	int rc = 0;
-	int i;
 	struct vidc_buffer_addr_info buffer_info;
+	int extra_idx = 0;
+	int i;
 	switch (b->type) {
 	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
 		break;
 	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
-		for (i = 0; i < b->length; i++) {
-			dprintk(VIDC_DBG, "device_addr = %ld, size = %d\n",
-				b->m.planes[i].m.userptr,
+			if (b->length != inst->fmts[CAPTURE_PORT]->num_planes) {
+				dprintk(VIDC_ERR,
+				"Planes mismatch: needed: %d, allocated: %d\n",
+				inst->fmts[CAPTURE_PORT]->num_planes,
+				b->length);
+				rc = -EINVAL;
+				break;
+			}
+			for (i = 0; (i < b->length)
+				&& (i < VIDEO_MAX_PLANES); ++i) {
+				dprintk(VIDC_DBG,
+				"prepare plane: %d, device_addr = 0x%lx, size = %d\n",
+				i, b->m.planes[i].m.userptr,
 				b->m.planes[i].length);
-			buffer_info.buffer_size = b->m.planes[i].length;
+			}
+			buffer_info.buffer_size = b->m.planes[0].length;
 			buffer_info.buffer_type = HAL_BUFFER_OUTPUT;
 			buffer_info.num_buffers = 1;
 			buffer_info.align_device_addr =
-				b->m.planes[i].m.userptr;
-			if (!inst->extradata_handle) {
-				inst->extradata_handle =
-				msm_smem_alloc(inst->mem_client,
-				4096 * 1024, 1, SMEM_UNCACHED,
-				inst->core->resources.io_map[NS_MAP].domain,
-				0, 0);
-				if (!inst->extradata_handle) {
-					dprintk(VIDC_ERR,
-						"Failed to allocate extradta memory\n");
-					rc = -ENOMEM;
-					break;
-				}
+				b->m.planes[0].m.userptr;
+			extra_idx = EXTRADATA_IDX(b->length);
+			if (extra_idx && (extra_idx < VIDEO_MAX_PLANES) &&
+				b->m.planes[extra_idx].m.userptr) {
+				buffer_info.extradata_addr =
+					b->m.planes[extra_idx].m.userptr;
+				dprintk(VIDC_DBG,
+				"extradata: 0x%lx\n",
+				b->m.planes[extra_idx].m.userptr);
+				buffer_info.extradata_size =
+					b->m.planes[extra_idx].length;
+			} else {
+				buffer_info.extradata_addr = 0;
+				buffer_info.extradata_size = 0;
 			}
-			buffer_info.extradata_addr =
-				inst->extradata_handle->device_addr;
-			buffer_info.extradata_size = 4096 * 1024;
 			rc = vidc_hal_session_set_buffers((void *)inst->session,
 					&buffer_info);
-			if (rc) {
+			if (rc)
 				dprintk(VIDC_ERR,
-					"vidc_hal_session_set_buffers failed\n");
-				break;
-			}
-		}
+				"vidc_hal_session_set_buffers failed");
 		break;
 	default:
 		dprintk(VIDC_ERR, "Buffer type not recognized: %d\n", b->type);
@@ -372,36 +360,61 @@
 					struct v4l2_buffer *b)
 {
 	int rc = 0;
-	int i;
 	struct vidc_buffer_addr_info buffer_info;
+	struct msm_vidc_core *core = inst->core;
+	int extra_idx = 0;
+	int i;
+	if (inst->state == MSM_VIDC_CORE_INVALID ||
+			core->state == VIDC_CORE_INVALID) {
+		dprintk(VIDC_ERR,
+			"Core %p in bad state, ignoring release output buf\n",
+				core);
+		goto exit;
+	}
 
 	switch (b->type) {
 	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
 		break;
 	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
-		for (i = 0; i < b->length; i++) {
-			dprintk(VIDC_DBG,
-				"Release device_addr = %ld, size = %d\n",
-				b->m.planes[i].m.userptr,
+			if (b->length !=
+				inst->fmts[CAPTURE_PORT]->num_planes) {
+				dprintk(VIDC_ERR,
+				"Planes mismatch: needed: %d, to release: %d\n",
+				inst->fmts[CAPTURE_PORT]->num_planes,
+				b->length);
+				rc = -EINVAL;
+				break;
+			}
+			for (i = 0; i < b->length; ++i) {
+				dprintk(VIDC_DBG,
+				"Release plane: %d device_addr = 0x%lx, size = %d\n",
+				i, b->m.planes[i].m.userptr,
 				b->m.planes[i].length);
-			buffer_info.buffer_size = b->m.planes[i].length;
+			}
+			buffer_info.buffer_size = b->m.planes[0].length;
 			buffer_info.buffer_type = HAL_BUFFER_OUTPUT;
 			buffer_info.num_buffers = 1;
 			buffer_info.align_device_addr =
-				 b->m.planes[i].m.userptr;
-			buffer_info.extradata_addr =
-				inst->extradata_handle->device_addr;
+				 b->m.planes[0].m.userptr;
+			extra_idx = EXTRADATA_IDX(b->length);
+			if (extra_idx && (extra_idx < VIDEO_MAX_PLANES)
+				&& b->m.planes[extra_idx].m.userptr)
+				buffer_info.extradata_addr =
+					b->m.planes[extra_idx].m.userptr;
+			else
+				buffer_info.extradata_addr = 0;
+
 			rc = vidc_hal_session_release_buffers(
-				(void *)inst->session, &buffer_info);
+					(void *)inst->session, &buffer_info);
 			if (rc)
 				dprintk(VIDC_ERR,
-					"vidc_hal_session_release_buffers failed\n");
-		}
+				"vidc_hal_session_release_buffers failed");
 		break;
 	default:
 		dprintk(VIDC_ERR, "Buffer type not recognized: %d\n", b->type);
 		break;
 	}
+exit:
 	return rc;
 }
 
@@ -436,7 +449,7 @@
 	rc = vb2_dqbuf(&q->vb2_bufq, b, true);
 	mutex_unlock(&q->lock);
 	if (rc)
-		dprintk(VIDC_WARN, "Failed to dqbuf, %d\n", rc);
+		dprintk(VIDC_DBG, "Failed to dqbuf, %d\n", rc);
 	return rc;
 }
 
@@ -467,7 +480,10 @@
 int msm_vdec_g_fmt(struct msm_vidc_inst *inst, struct v4l2_format *f)
 {
 	const struct msm_vidc_format *fmt = NULL;
+	struct hal_frame_size frame_sz;
+	int extra_idx = 0;
 	int rc = 0;
+	int ret;
 	int i;
 	if (!inst || !f) {
 		dprintk(VIDC_ERR,
@@ -481,30 +497,88 @@
 
 	if (fmt) {
 		f->fmt.pix_mp.pixelformat = fmt->fourcc;
+		f->fmt.pix_mp.num_planes = fmt->num_planes;
 		if (inst->in_reconfig == true) {
 			inst->prop.height = inst->reconfig_height;
 			inst->prop.width = inst->reconfig_width;
 		}
 		f->fmt.pix_mp.height = inst->prop.height;
 		f->fmt.pix_mp.width = inst->prop.width;
-		f->fmt.pix_mp.num_planes = fmt->num_planes;
-		for (i = 0; i < fmt->num_planes; ++i) {
-			f->fmt.pix_mp.plane_fmt[i].sizeimage =
-			fmt->get_frame_size(i, inst->prop.height,
-				inst->prop.width);
+		frame_sz.buffer_type = HAL_BUFFER_OUTPUT;
+		frame_sz.width = inst->prop.width;
+		frame_sz.height = inst->prop.height;
+		dprintk(VIDC_DBG, "width = %d, height = %d\n",
+				frame_sz.width, frame_sz.height);
+		ret = msm_comm_try_set_prop(inst,
+			HAL_PARAM_FRAME_SIZE, &frame_sz);
+		ret = ret || msm_comm_try_get_bufreqs(inst);
+		if (ret || (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)) {
+			for (i = 0; i < fmt->num_planes; ++i) {
+				f->fmt.pix_mp.plane_fmt[i].sizeimage =
+					fmt->get_frame_size(i,
+						f->fmt.pix_mp.height,
+						f->fmt.pix_mp.width);
+			}
+		} else {
+			f->fmt.pix_mp.plane_fmt[0].sizeimage =
+			inst->buff_req.buffer[HAL_BUFFER_OUTPUT].buffer_size;
+			extra_idx = EXTRADATA_IDX(fmt->num_planes);
+			if (extra_idx && (extra_idx < VIDEO_MAX_PLANES)) {
+				f->fmt.pix_mp.plane_fmt[extra_idx].sizeimage =
+		inst->buff_req.buffer[HAL_BUFFER_EXTRADATA_OUTPUT].buffer_size;
+			}
 		}
 	} else {
-		dprintk(VIDC_ERR, "Buf type not recognized, type = %d\n",
-					f->type);
+		dprintk(VIDC_ERR,
+			"Buf type not recognized, type = %d\n",
+			f->type);
 		rc = -EINVAL;
 	}
 	return rc;
 }
-
+int msm_vdec_s_parm(struct msm_vidc_inst *inst, struct v4l2_streamparm *a)
+{
+	u32 us_per_frame = 0;
+	int rc = 0;
+	if (a->parm.output.timeperframe.denominator) {
+		switch (a->type) {
+		case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+			us_per_frame = a->parm.output.timeperframe.numerator/
+				a->parm.output.timeperframe.denominator;
+			break;
+		case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+			us_per_frame = a->parm.capture.timeperframe.numerator/
+				a->parm.capture.timeperframe.denominator;
+			break;
+		default:
+			dprintk(VIDC_ERR,
+				"Scale clocks : Unknown buffer type\n");
+			break;
+		}
+	}
+	if (!us_per_frame) {
+		dprintk(VIDC_ERR,
+				"Failed to scale clocks : time between frames is 0\n");
+		rc = -EINVAL;
+		goto exit;
+	}
+	inst->prop.fps = (u8) (USEC_PER_SEC / us_per_frame);
+	if (inst->prop.fps) {
+		if (msm_comm_scale_clocks(inst->core, inst->session_type)) {
+			dprintk(VIDC_WARN,
+					"Failed to scale clocks\n");
+		}
+	}
+exit:
+	return rc;
+}
 int msm_vdec_s_fmt(struct msm_vidc_inst *inst, struct v4l2_format *f)
 {
 	const struct msm_vidc_format *fmt = NULL;
+	struct hal_frame_size frame_sz;
+	int extra_idx = 0;
 	int rc = 0;
+	int ret = 0;
 	int i;
 	if (!inst || !f) {
 		dprintk(VIDC_ERR,
@@ -517,64 +591,67 @@
 		fmt = msm_comm_get_pixel_fmt_fourcc(vdec_formats,
 			ARRAY_SIZE(vdec_formats), f->fmt.pix_mp.pixelformat,
 			CAPTURE_PORT);
-		if (fmt && fmt->type != CAPTURE_PORT) {
+		if (!fmt || (fmt && fmt->type != CAPTURE_PORT)) {
 			dprintk(VIDC_ERR,
-				"Format: %d not supported on CAPTURE"
-				"port\n", f->fmt.pix_mp.pixelformat);
-			rc = -EINVAL;
-			goto err_invalid_fmt;
-		}
-
-		inst->prop.width = f->fmt.pix_mp.width;
-		inst->prop.height = f->fmt.pix_mp.height;
-
-		frame_sz.buffer_type = HAL_BUFFER_OUTPUT;
-		frame_sz.width = inst->prop.width;
-		frame_sz.height = inst->prop.height;
-		dprintk(VIDC_DBG,
-			"width = %d, height = %d\n",
-			frame_sz.width, frame_sz.height);
-		rc = vidc_hal_session_set_property((void *)inst->session,
-				HAL_PARAM_FRAME_SIZE, &frame_sz);
-		if (rc) {
-			dprintk(VIDC_ERR,
-				"Failed to set hal property for framesize\n");
-			goto err_invalid_fmt;
-		}
-	} else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
-		inst->prop.width = f->fmt.pix_mp.width;
-		inst->prop.height = f->fmt.pix_mp.height;
-		fmt = msm_comm_get_pixel_fmt_fourcc(vdec_formats,
-			ARRAY_SIZE(vdec_formats), f->fmt.pix_mp.pixelformat,
-			OUTPUT_PORT);
-		if (fmt && fmt->type != OUTPUT_PORT) {
-			dprintk(VIDC_ERR,
-				"Format: %d not supported on OUTPUT port\n",
+				"Format: %d not supported on CAPTURE port\n",
 				f->fmt.pix_mp.pixelformat);
 			rc = -EINVAL;
 			goto err_invalid_fmt;
 		}
-	}
-
-	if (fmt) {
-		f->fmt.pix_mp.num_planes = fmt->num_planes;
-		for (i = 0; i < fmt->num_planes; ++i) {
-			f->fmt.pix_mp.plane_fmt[i].sizeimage =
-				fmt->get_frame_size(i, f->fmt.pix_mp.height,
-						f->fmt.pix_mp.width);
-		}
 		inst->fmts[fmt->type] = fmt;
-		if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
-			rc = msm_comm_try_state(inst, MSM_VIDC_OPEN);
-			if (rc) {
-				dprintk(VIDC_ERR, "Failed to open instance\n");
-				goto err_invalid_fmt;
+		frame_sz.buffer_type = HAL_BUFFER_OUTPUT;
+		frame_sz.width = inst->prop.width;
+		frame_sz.height = inst->prop.height;
+		dprintk(VIDC_DBG, "width = %d, height = %d\n",
+				frame_sz.width, frame_sz.height);
+		ret = msm_comm_try_set_prop(inst,
+			HAL_PARAM_FRAME_SIZE, &frame_sz);
+		ret = ret || msm_comm_try_get_bufreqs(inst);
+		if (ret) {
+			for (i = 0; i < fmt->num_planes; ++i) {
+				f->fmt.pix_mp.plane_fmt[i].sizeimage =
+					fmt->get_frame_size(i,
+						f->fmt.pix_mp.height,
+						f->fmt.pix_mp.width);
+			}
+		} else {
+			f->fmt.pix_mp.plane_fmt[0].sizeimage =
+			inst->buff_req.buffer[HAL_BUFFER_OUTPUT].buffer_size;
+			extra_idx = EXTRADATA_IDX(fmt->num_planes);
+			if (extra_idx && (extra_idx < VIDEO_MAX_PLANES)) {
+				f->fmt.pix_mp.plane_fmt[1].sizeimage =
+		inst->buff_req.buffer[HAL_BUFFER_EXTRADATA_OUTPUT].buffer_size;
 			}
 		}
-	} else {
-		dprintk(VIDC_ERR,
-			"Buf type not recognized, type = %d\n", f->type);
-		rc = -EINVAL;
+		f->fmt.pix_mp.num_planes = fmt->num_planes;
+	} else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+		inst->prop.width = f->fmt.pix_mp.width;
+		inst->prop.height = f->fmt.pix_mp.height;
+		fmt = msm_comm_get_pixel_fmt_fourcc(vdec_formats,
+				ARRAY_SIZE(vdec_formats),
+				f->fmt.pix_mp.pixelformat,
+				OUTPUT_PORT);
+		if (!fmt || fmt->type != OUTPUT_PORT) {
+			dprintk(VIDC_ERR,
+			"Format: %d not supported on OUTPUT port\n",
+			f->fmt.pix_mp.pixelformat);
+			rc = -EINVAL;
+			goto err_invalid_fmt;
+		}
+		inst->fmts[fmt->type] = fmt;
+		rc = msm_comm_try_state(inst, MSM_VIDC_OPEN_DONE);
+		if (rc) {
+			dprintk(VIDC_ERR, "Failed to open instance\n");
+			goto err_invalid_fmt;
+		}
+		frame_sz.buffer_type = HAL_BUFFER_INPUT;
+		frame_sz.width = inst->prop.width;
+		frame_sz.height = inst->prop.height;
+		msm_comm_try_set_prop(inst, HAL_PARAM_FRAME_SIZE, &frame_sz);
+		f->fmt.pix_mp.plane_fmt[0].sizeimage =
+			fmt->get_frame_size(0, f->fmt.pix_mp.height,
+					f->fmt.pix_mp.width);
+		f->fmt.pix_mp.num_planes = fmt->num_planes;
 	}
 err_invalid_fmt:
 	return rc;
@@ -622,7 +699,7 @@
 				sizeof(f->description));
 		f->pixelformat = fmt->fourcc;
 	} else {
-		dprintk(VIDC_WARN, "No more formats found\n");
+		dprintk(VIDC_INFO, "No more formats found\n");
 		rc = -EINVAL;
 	}
 	return rc;
@@ -638,14 +715,17 @@
 	struct msm_vidc_inst *inst;
 	unsigned long flags;
 	struct hal_buffer_requirements *bufreq;
-	if (!q || !q->drv_priv) {
-		dprintk(VIDC_ERR, "Invalid input, q = %p\n", q);
+	int extra_idx = 0;
+	if (!q || !num_buffers || !num_planes
+		|| !sizes || !q->drv_priv) {
+		dprintk(VIDC_ERR, "Invalid input, q = %p, %p, %p\n",
+			q, num_buffers, num_planes);
 		return -EINVAL;
 	}
 	inst = q->drv_priv;
 	switch (q->type) {
 	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
-		*num_planes = 1;
+		*num_planes = inst->fmts[OUTPUT_PORT]->num_planes;
 		if (*num_buffers < MIN_NUM_OUTPUT_BUFFERS ||
 				*num_buffers > MAX_NUM_OUTPUT_BUFFERS)
 			*num_buffers = MIN_NUM_OUTPUT_BUFFERS;
@@ -656,6 +736,7 @@
 		break;
 	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
 		dprintk(VIDC_DBG, "Getting bufreqs on capture plane\n");
+		*num_planes = inst->fmts[CAPTURE_PORT]->num_planes;
 		rc = msm_comm_try_state(inst, MSM_VIDC_OPEN_DONE);
 		if (rc) {
 			dprintk(VIDC_ERR, "Failed to open instance\n");
@@ -667,7 +748,6 @@
 				"Failed to get buffer requirements: %d\n", rc);
 			break;
 		}
-		*num_planes = 1;
 		spin_lock_irqsave(&inst->lock, flags);
 		if (*num_buffers && *num_buffers >
 			inst->buff_req.buffer[HAL_BUFFER_OUTPUT].
@@ -692,11 +772,13 @@
 				inst->buff_req.buffer[1].buffer_count_actual,
 				inst->buff_req.buffer[1].buffer_size,
 				inst->buff_req.buffer[1].buffer_alignment);
-		for (i = 0; i < *num_planes; i++) {
-			sizes[i] = inst->fmts[CAPTURE_PORT]->get_frame_size(
-					i, inst->prop.height, inst->prop.width);
+		sizes[0] = inst->buff_req.buffer[HAL_BUFFER_OUTPUT].buffer_size;
+		extra_idx =
+			EXTRADATA_IDX(inst->fmts[CAPTURE_PORT]->num_planes);
+		if (extra_idx && (extra_idx < VIDEO_MAX_PLANES)) {
+			sizes[extra_idx] =
+		inst->buff_req.buffer[HAL_BUFFER_EXTRADATA_OUTPUT].buffer_size;
 		}
-
 		break;
 	default:
 		dprintk(VIDC_ERR, "Invalid q type = %d\n", q->type);
@@ -709,7 +791,6 @@
 static inline int start_streaming(struct msm_vidc_inst *inst)
 {
 	int rc = 0;
-	unsigned long flags;
 	struct vb2_buf_entry *temp;
 	struct list_head *ptr, *next;
 	inst->in_reconfig = false;
@@ -737,7 +818,7 @@
 		goto fail_start;
 	}
 
-	spin_lock_irqsave(&inst->lock, flags);
+	mutex_lock(&inst->sync_lock);
 	if (!list_empty(&inst->pendingq)) {
 		list_for_each_safe(ptr, next, &inst->pendingq) {
 			temp = list_entry(ptr, struct vb2_buf_entry, list);
@@ -751,7 +832,7 @@
 			kfree(temp);
 		}
 	}
-	spin_unlock_irqrestore(&inst->lock, flags);
+	mutex_unlock(&inst->sync_lock);
 	return rc;
 fail_start:
 	return rc;
@@ -843,6 +924,8 @@
 int msm_vdec_cmd(struct msm_vidc_inst *inst, struct v4l2_decoder_cmd *dec)
 {
 	int rc = 0;
+	struct v4l2_event dqevent = {0};
+	struct msm_vidc_core *core = inst->core;
 	switch (dec->cmd) {
 	case V4L2_DEC_QCOM_CMD_FLUSH:
 		rc = msm_comm_flush(inst, dec->flags);
@@ -854,6 +937,15 @@
 		rc = msm_comm_release_persist_buffers(inst);
 		if (rc)
 			pr_err("Failed to release persist buffers: %d\n", rc);
+		if (inst->state == MSM_VIDC_CORE_INVALID ||
+			core->state == VIDC_CORE_INVALID) {
+			dprintk(VIDC_ERR,
+				"Core %p in bad state, Sending CLOSE event\n",
+					core);
+			dqevent.type = V4L2_EVENT_MSM_VIDC_CLOSE_DONE;
+			v4l2_event_queue_fh(&inst->event_handler, &dqevent);
+			goto exit;
+		}
 		rc = msm_comm_try_state(inst, MSM_VIDC_CLOSE_DONE);
 		break;
 	default:
@@ -969,6 +1061,12 @@
 		hal_property.enable = control.value;
 		pdata = &hal_property;
 		break;
+	case V4L2_CID_MPEG_VIDC_VIDEO_SYNC_FRAME_DECODE:
+		property_id =
+			HAL_PARAM_VDEC_SYNC_FRAME_DECODE;
+		hal_property.enable = control.value;
+		pdata = &hal_property;
+		break;
 	default:
 		break;
 		}
diff --git a/drivers/media/video/msm_vidc/msm_vdec.h b/drivers/media/video/msm_vidc/msm_vdec.h
index b8326d8..419c8c1 100644
--- a/drivers/media/video/msm_vidc/msm_vdec.h
+++ b/drivers/media/video/msm_vidc/msm_vdec.h
@@ -32,6 +32,7 @@
 int msm_vdec_streamon(struct msm_vidc_inst *inst, enum v4l2_buf_type i);
 int msm_vdec_streamoff(struct msm_vidc_inst *inst, enum v4l2_buf_type i);
 int msm_vdec_cmd(struct msm_vidc_inst *inst, struct v4l2_decoder_cmd *dec);
+int msm_vdec_s_parm(struct msm_vidc_inst *inst, struct v4l2_streamparm *a);
 struct vb2_ops *msm_vdec_get_vb2q_ops(void);
 
 #endif
diff --git a/drivers/media/video/msm_vidc/msm_venc.c b/drivers/media/video/msm_vidc/msm_venc.c
index 948676a..fdadb36 100644
--- a/drivers/media/video/msm_vidc/msm_venc.c
+++ b/drivers/media/video/msm_vidc/msm_venc.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -461,23 +461,7 @@
 
 static u32 get_frame_size_nv12(int plane, u32 height, u32 width)
 {
-	int size;
-	int luma_h, luma_w, luma_stride, luma_scanl, luma_size;
-	int chroma_h, chroma_w, chroma_stride, chroma_scanl, chroma_size;
-
-	luma_w = width;
-	luma_h = height;
-
-	chroma_w = luma_w;
-	chroma_h = luma_h/2;
-	NV12_IL_CALC_Y_STRIDE(luma_stride, luma_w, 32);
-	NV12_IL_CALC_Y_BUFHEIGHT(luma_scanl, luma_h, 32);
-	NV12_IL_CALC_UV_STRIDE(chroma_stride, chroma_w, 32);
-	NV12_IL_CALC_UV_BUFHEIGHT(chroma_scanl, luma_h, 32);
-	NV12_IL_CALC_BUF_SIZE(size, luma_size, luma_stride,
-		luma_scanl, chroma_size, chroma_stride, chroma_scanl, 32);
-	size = ALIGN(size, SZ_4K);
-	return size;
+	return VENUS_BUFFER_SIZE(COLOR_FMT_NV12, width, height);
 }
 
 static u32 get_frame_size_nv21(int plane, u32 height, u32 width)
@@ -574,7 +558,6 @@
 {
 	int i, rc = 0;
 	struct msm_vidc_inst *inst;
-	struct hal_frame_size frame_sz;
 	unsigned long flags;
 	if (!q || !q->drv_priv) {
 		dprintk(VIDC_ERR, "Invalid input, q = %p\n", q);
@@ -598,26 +581,6 @@
 			dprintk(VIDC_ERR, "Failed to open instance\n");
 			break;
 		}
-		frame_sz.buffer_type = HAL_BUFFER_INPUT;
-		frame_sz.width = inst->prop.width;
-		frame_sz.height = inst->prop.height;
-		dprintk(VIDC_DBG, "width = %d, height = %d\n",
-				frame_sz.width, frame_sz.height);
-		rc = vidc_hal_session_set_property((void *)inst->session,
-				HAL_PARAM_FRAME_SIZE, &frame_sz);
-		if (rc) {
-			dprintk(VIDC_ERR,
-				"Failed to set framesize for Output port\n");
-			break;
-		}
-		frame_sz.buffer_type = HAL_BUFFER_OUTPUT;
-		rc = vidc_hal_session_set_property((void *)inst->session,
-				HAL_PARAM_FRAME_SIZE, &frame_sz);
-		if (rc) {
-			dprintk(VIDC_ERR,
-				"Failed to set hal property for framesize\n");
-			break;
-		}
 		rc = msm_comm_try_get_bufreqs(inst);
 		if (rc) {
 			dprintk(VIDC_ERR,
@@ -626,7 +589,9 @@
 		}
 		*num_planes = 1;
 		spin_lock_irqsave(&inst->lock, flags);
-		*num_buffers = inst->buff_req.buffer[0].buffer_count_actual;
+		*num_buffers = inst->buff_req.buffer[0].buffer_count_actual =
+			max(*num_buffers, inst->buff_req.buffer[0].
+			buffer_count_actual);
 		spin_unlock_irqrestore(&inst->lock, flags);
 		dprintk(VIDC_DBG, "size = %d, alignment = %d, count = %d\n",
 				inst->buff_req.buffer[0].buffer_size,
@@ -648,7 +613,6 @@
 static inline int start_streaming(struct msm_vidc_inst *inst)
 {
 	int rc = 0;
-	unsigned long flags;
 	struct vb2_buf_entry *temp;
 	struct list_head *ptr, *next;
 
@@ -679,7 +643,7 @@
 			"Failed to move inst: %p to start done state\n", inst);
 		goto fail_start;
 	}
-	spin_lock_irqsave(&inst->lock, flags);
+	mutex_lock(&inst->sync_lock);
 	if (!list_empty(&inst->pendingq)) {
 		list_for_each_safe(ptr, next, &inst->pendingq) {
 			temp = list_entry(ptr, struct vb2_buf_entry, list);
@@ -693,7 +657,7 @@
 			kfree(temp);
 		}
 	}
-	spin_unlock_irqrestore(&inst->lock, flags);
+	mutex_unlock(&inst->sync_lock);
 	return rc;
 fail_start:
 	return rc;
@@ -1280,11 +1244,28 @@
 int msm_venc_cmd(struct msm_vidc_inst *inst, struct v4l2_encoder_cmd *enc)
 {
 	int rc = 0;
+	struct v4l2_event dqevent = {0};
+	struct msm_vidc_core *core;
+	core = inst->core;
 	switch (enc->cmd) {
 	case V4L2_ENC_QCOM_CMD_FLUSH:
 		rc = msm_comm_flush(inst, enc->flags);
 		break;
 	case V4L2_ENC_CMD_STOP:
+		if (inst->state == MSM_VIDC_CORE_INVALID ||
+			core->state == VIDC_CORE_INVALID) {
+			dqevent.type = V4L2_EVENT_MSM_VIDC_CLOSE_DONE;
+			v4l2_event_queue_fh(&inst->event_handler, &dqevent);
+			return rc;
+		}
+		rc = msm_comm_release_scratch_buffers(inst);
+		if (rc)
+			dprintk(VIDC_ERR, "Failed to release scratch buf:%d\n",
+				rc);
+		rc = msm_comm_release_persist_buffers(inst);
+		if (rc)
+			dprintk(VIDC_ERR, "Failed to release persist buf:%d\n",
+				rc);
 		rc = msm_comm_try_state(inst, MSM_VIDC_CLOSE_DONE);
 		break;
 	}
@@ -1342,9 +1323,59 @@
 	return rc;
 }
 
+int msm_venc_s_parm(struct msm_vidc_inst *inst, struct v4l2_streamparm *a)
+{
+	u32 property_id = 0, us_per_frame = 0;
+	void *pdata;
+	int rc = 0;
+	struct hal_frame_rate frame_rate;
+	property_id = HAL_CONFIG_FRAME_RATE;
+	if (a->parm.output.timeperframe.denominator) {
+		switch (a->type) {
+		case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+			us_per_frame = a->parm.output.timeperframe.numerator/
+				a->parm.output.timeperframe.denominator;
+			break;
+		case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+			us_per_frame = a->parm.capture.timeperframe.numerator/
+				a->parm.capture.timeperframe.denominator;
+			break;
+		default:
+			dprintk(VIDC_ERR,
+				"Scale clocks : Unknown buffer type\n");
+			break;
+		}
+	}
+
+	if (!us_per_frame) {
+		dprintk(VIDC_ERR,
+			"Failed to scale clocks : time between frames is 0\n");
+		rc = -EINVAL;
+		goto exit;
+	}
+	inst->prop.fps = (u8) (USEC_PER_SEC / us_per_frame);
+	if (inst->prop.fps) {
+		frame_rate.frame_rate = inst->prop.fps * (0x1<<16);
+		frame_rate.buffer_type = HAL_BUFFER_OUTPUT;
+		pdata = &frame_rate;
+		rc = vidc_hal_session_set_property((void *)inst->session,
+				property_id, pdata);
+		if (rc) {
+			dprintk(VIDC_WARN,
+				"Failed to set frame rate %d\n", rc);
+		}
+		if (msm_comm_scale_clocks(inst->core, inst->session_type)) {
+			dprintk(VIDC_WARN,
+				"Failed to scale clocks\n");
+		}
+	}
+exit:
+	return rc;
+}
 int msm_venc_s_fmt(struct msm_vidc_inst *inst, struct v4l2_format *f)
 {
 	const struct msm_vidc_format *fmt = NULL;
+	struct hal_frame_size frame_sz;
 	int rc = 0;
 	int i;
 	if (!inst || !f) {
@@ -1366,6 +1397,26 @@
 	} else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
 		inst->prop.width = f->fmt.pix_mp.width;
 		inst->prop.height = f->fmt.pix_mp.height;
+		frame_sz.buffer_type = HAL_BUFFER_INPUT;
+		frame_sz.width = inst->prop.width;
+		frame_sz.height = inst->prop.height;
+		dprintk(VIDC_DBG, "width = %d, height = %d\n",
+				frame_sz.width, frame_sz.height);
+		rc = vidc_hal_session_set_property((void *)inst->session,
+				HAL_PARAM_FRAME_SIZE, &frame_sz);
+		if (rc) {
+			dprintk(VIDC_ERR,
+				"Failed to set framesize for Output port\n");
+			goto exit;
+		}
+		frame_sz.buffer_type = HAL_BUFFER_OUTPUT;
+		rc = vidc_hal_session_set_property((void *)inst->session,
+				HAL_PARAM_FRAME_SIZE, &frame_sz);
+		if (rc) {
+			dprintk(VIDC_ERR,
+				"Failed to set hal property for framesize\n");
+			goto exit;
+		}
 		fmt = msm_comm_get_pixel_fmt_fourcc(venc_formats,
 			ARRAY_SIZE(venc_formats), f->fmt.pix_mp.pixelformat,
 			OUTPUT_PORT);
@@ -1497,6 +1548,43 @@
 	return rc;
 }
 
+int msm_venc_release_buf(struct msm_vidc_inst *inst,
+					struct v4l2_buffer *b)
+{
+	int rc = 0;
+	int i;
+	struct vidc_buffer_addr_info buffer_info;
+
+	switch (b->type) {
+	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+		break;
+	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+		for (i = 0; i < b->length; i++) {
+			dprintk(VIDC_DBG,
+				"Release device_addr = %ld, size = %d, %d\n",
+				b->m.planes[i].m.userptr,
+				b->m.planes[i].length, inst->state);
+			buffer_info.buffer_size = b->m.planes[i].length;
+			buffer_info.buffer_type = HAL_BUFFER_OUTPUT;
+			buffer_info.num_buffers = 1;
+			buffer_info.align_device_addr =
+				 b->m.planes[i].m.userptr;
+			buffer_info.extradata_size = 0;
+			buffer_info.extradata_addr = 0;
+			rc = vidc_hal_session_release_buffers(
+				(void *)inst->session, &buffer_info);
+			if (rc)
+				dprintk(VIDC_ERR,
+					"vidc_hal_session_release_buffers failed\n");
+		}
+		break;
+	default:
+		dprintk(VIDC_ERR, "Buffer type not recognized: %d\n", b->type);
+		break;
+	}
+	return rc;
+}
+
 int msm_venc_qbuf(struct msm_vidc_inst *inst, struct v4l2_buffer *b)
 {
 	struct buf_queue *q = NULL;
diff --git a/drivers/media/video/msm_vidc/msm_venc.h b/drivers/media/video/msm_vidc/msm_venc.h
index 83610b3..ad63e7d 100644
--- a/drivers/media/video/msm_vidc/msm_venc.h
+++ b/drivers/media/video/msm_vidc/msm_venc.h
@@ -26,11 +26,13 @@
 int msm_venc_g_ctrl(void *instance, struct v4l2_control *a);
 int msm_venc_reqbufs(void *instance, struct v4l2_requestbuffers *b);
 int msm_venc_prepare_buf(struct msm_vidc_inst *inst, struct v4l2_buffer *b);
+int msm_venc_release_buf(struct msm_vidc_inst *inst, struct v4l2_buffer *b);
 int msm_venc_qbuf(struct msm_vidc_inst *inst, struct v4l2_buffer *b);
 int msm_venc_dqbuf(struct msm_vidc_inst *inst, struct v4l2_buffer *b);
 int msm_venc_streamon(struct msm_vidc_inst *inst, enum v4l2_buf_type i);
 int msm_venc_streamoff(struct msm_vidc_inst *inst, enum v4l2_buf_type i);
 int msm_venc_cmd(struct msm_vidc_inst *inst, struct v4l2_encoder_cmd *enc);
+int msm_venc_s_parm(struct msm_vidc_inst *inst, struct v4l2_streamparm *a);
 struct vb2_ops *msm_venc_get_vb2q_ops(void);
 
 #endif
diff --git a/drivers/media/video/msm_vidc/msm_vidc.c b/drivers/media/video/msm_vidc/msm_vidc.c
index 8ff7714..1d0124f 100644
--- a/drivers/media/video/msm_vidc/msm_vidc.c
+++ b/drivers/media/video/msm_vidc/msm_vidc.c
@@ -109,6 +109,19 @@
 		return msm_venc_querycap(instance, cap);
 	return -EINVAL;
 }
+int msm_vidc_s_parm(void *instance,
+		struct v4l2_streamparm *a)
+{
+	struct msm_vidc_inst *inst = instance;
+
+	if (!inst || !a)
+		return -EINVAL;
+	if (inst->session_type == MSM_VIDC_DECODER)
+		return msm_vdec_s_parm(instance, a);
+	else if (inst->session_type == MSM_VIDC_ENCODER)
+		return msm_venc_s_parm(instance, a);
+	return -EINVAL;
+}
 int msm_vidc_enum_fmt(void *instance, struct v4l2_fmtdesc *f)
 {
 	struct msm_vidc_inst *inst = instance;
@@ -211,6 +224,8 @@
 
 	if (inst->session_type == MSM_VIDC_DECODER)
 		return msm_vdec_release_buf(instance, b);
+	if (inst->session_type == MSM_VIDC_ENCODER)
+		return msm_venc_release_buf(instance, b);
 	return -EINVAL;
 }
 
@@ -403,6 +418,8 @@
 		goto err_invalid_core;
 	}
 
+	pr_info(VIDC_DBG_TAG "Opening video instance: %p, %d\n",
+		VIDC_INFO, inst, session_type);
 	mutex_init(&inst->sync_lock);
 	mutex_init(&inst->bufq[CAPTURE_PORT].lock);
 	mutex_init(&inst->bufq[OUTPUT_PORT].lock);
@@ -538,12 +555,14 @@
 			list_del(&inst->list);
 	}
 	mutex_unlock(&core->sync_lock);
-	rc = msm_comm_try_state(inst, MSM_VIDC_CORE_UNINIT);
+	cleanup_instance(inst);
+	if (inst->state != MSM_VIDC_CORE_INVALID &&
+		core->state != VIDC_CORE_INVALID)
+		rc = msm_comm_try_state(inst, MSM_VIDC_CORE_UNINIT);
 	if (rc)
 		dprintk(VIDC_ERR,
 			"Failed to move video instance to uninit state\n");
-	cleanup_instance(inst);
+	pr_info(VIDC_DBG_TAG "Closed video instance: %p\n", VIDC_INFO, inst);
 	kfree(inst);
-	dprintk(VIDC_DBG, "Closed the instance\n");
 	return 0;
 }
diff --git a/drivers/media/video/msm_vidc/msm_vidc_common.c b/drivers/media/video/msm_vidc/msm_vidc_common.c
index 1cad40f..772f0de 100644
--- a/drivers/media/video/msm_vidc/msm_vidc_common.c
+++ b/drivers/media/video/msm_vidc/msm_vidc_common.c
@@ -17,12 +17,13 @@
 #include <asm/div64.h>
 #include <mach/iommu.h>
 #include <mach/iommu_domains.h>
-#include <mach/peripheral-loader.h>
+#include <mach/subsystem_restart.h>
 
 #include "msm_vidc_common.h"
 #include "vidc_hal_api.h"
 #include "msm_smem.h"
 #include "msm_vidc_debug.h"
+#include "msm_vidc_ssr.h"
 
 #define HW_RESPONSE_TIMEOUT (5 * 60 * 1000)
 
@@ -65,8 +66,8 @@
 	int i;
 	if (!load)
 		return 0;
-	for (i = num_rows - 1; i > 1; i--) {
-		if (load >= bus_table[i])
+	for (i = 0; i < num_rows; i++) {
+		if (load <= bus_table[i])
 			break;
 	}
 	dprintk(VIDC_DBG, "Required bus = %d\n", i);
@@ -234,7 +235,7 @@
 		k++;
 	}
 	if (i == size) {
-		dprintk(VIDC_WARN, "Format not found\n");
+		dprintk(VIDC_INFO, "Format not found\n");
 		return NULL;
 	}
 	return &fmt[i];
@@ -252,7 +253,7 @@
 				break;
 	}
 	if (i == size) {
-		dprintk(VIDC_WARN, "Format not found\n");
+		dprintk(VIDC_INFO, "Format not found\n");
 		return NULL;
 	}
 	return &fmt[i];
@@ -312,7 +313,7 @@
 	complete(&core->completions[SYS_MSG_INDEX(cmd)]);
 }
 
-static inline void change_inst_state(struct msm_vidc_inst *inst,
+void change_inst_state(struct msm_vidc_inst *inst,
 	enum instance_state state)
 {
 	unsigned long flags;
@@ -387,7 +388,9 @@
 	struct msm_vidc_cb_cmd_done *response = data;
 	struct msm_vidc_inst *inst;
 	struct v4l2_event dqevent;
+	struct v4l2_control control = {0};
 	struct msm_vidc_cb_event *event_notify;
+	int rc = 0;
 	if (response) {
 		inst = (struct msm_vidc_inst *)response->session_id;
 		dqevent.id = 0;
@@ -395,7 +398,16 @@
 		switch (event_notify->hal_event_type) {
 		case HAL_EVENT_SEQ_CHANGED_SUFFICIENT_RESOURCES:
 			dqevent.type =
-				V4L2_EVENT_SEQ_CHANGED_SUFFICIENT;
+				V4L2_EVENT_SEQ_CHANGED_INSUFFICIENT;
+			control.id =
+				V4L2_CID_MPEG_VIDC_VIDEO_CONTINUE_DATA_TRANSFER;
+			rc = v4l2_g_ctrl(&inst->ctrl_handler, &control);
+			if (rc)
+				dprintk(VIDC_WARN,
+					"Failed to get Smooth streamng flag\n");
+			if (!rc && control.value == true)
+				dqevent.type =
+					V4L2_EVENT_SEQ_CHANGED_SUFFICIENT;
 			break;
 		case HAL_EVENT_SEQ_CHANGED_INSUFFICIENT_RESOURCES:
 			dqevent.type =
@@ -508,6 +520,78 @@
 	}
 }
 
+static void handle_session_error(enum command_response cmd, void *data)
+{
+	struct msm_vidc_cb_cmd_done *response = data;
+	struct msm_vidc_inst *inst = NULL;
+	struct v4l2_event dqevent;
+	if (response) {
+		inst = (struct msm_vidc_inst *)response->session_id;
+		if (inst) {
+			dprintk(VIDC_WARN,
+				"Session error receivd for session %p\n", inst);
+			mutex_lock(&inst->sync_lock);
+			inst->state = MSM_VIDC_CORE_INVALID;
+			mutex_unlock(&inst->sync_lock);
+			dqevent.type = V4L2_EVENT_MSM_VIDC_SYS_ERROR;
+			dqevent.id = 0;
+			v4l2_event_queue_fh(&inst->event_handler, &dqevent);
+			wake_up(&inst->kernel_event_queue);
+		}
+	} else {
+		dprintk(VIDC_ERR,
+			"Failed to get valid response for session error\n");
+	}
+}
+static void handle_sys_error(enum command_response cmd, void *data)
+{
+	struct msm_vidc_cb_cmd_done *response = data;
+	struct msm_vidc_inst *inst = NULL ;
+	struct msm_vidc_core *core = NULL;
+	struct v4l2_event dqevent;
+	unsigned long flags;
+	if (response) {
+		inst = (struct msm_vidc_inst *)response->session_id;
+		dprintk(VIDC_WARN,
+				"Sys error received for session %p\n", inst);
+		if (inst) {
+			core = inst->core;
+			if (core) {
+				spin_lock_irqsave(&core->lock, flags);
+				core->state = VIDC_CORE_INVALID;
+				spin_unlock_irqrestore(&core->lock, flags);
+				dqevent.type = V4L2_EVENT_MSM_VIDC_SYS_ERROR;
+				dqevent.id = 0;
+				list_for_each_entry(inst, &core->instances,
+					list) {
+					if (inst) {
+						v4l2_event_queue_fh(
+							&inst->event_handler,
+								&dqevent);
+						spin_lock_irqsave(&inst->lock,
+							flags);
+						inst->state =
+							MSM_VIDC_CORE_INVALID;
+						spin_unlock_irqrestore(
+							&inst->lock, flags);
+					}
+				}
+			wake_up(&inst->kernel_event_queue);
+			}
+		}
+	} else {
+		dprintk(VIDC_ERR,
+			"Failed to get valid response for sys error\n");
+	}
+}
+
+static void handle_sys_watchdog_timeout(enum command_response cmd, void *data)
+{
+	subsystem_restart("msm_vidc");
+	dprintk(VIDC_ERR,
+		"msm_vidc: Sub System Restart initiated\n");
+}
+
 
 static void handle_session_close(enum command_response cmd, void *data)
 {
@@ -520,7 +604,9 @@
 		dqevent.type = V4L2_EVENT_MSM_VIDC_CLOSE_DONE;
 		dqevent.id = 0;
 		v4l2_event_queue_fh(&inst->event_handler, &dqevent);
+		inst->session = NULL;
 		wake_up(&inst->kernel_event_queue);
+		show_stats(inst);
 	} else {
 		dprintk(VIDC_ERR,
 			"Failed to get valid response for session close\n");
@@ -571,6 +657,7 @@
 		vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
 		mutex_unlock(&inst->bufq[OUTPUT_PORT].lock);
 		wake_up(&inst->kernel_event_queue);
+		msm_vidc_debugfs_update(inst, MSM_VIDC_DEBUGFS_EVENT_EBD);
 	}
 }
 
@@ -636,6 +723,7 @@
 		vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
 		mutex_unlock(&inst->bufq[CAPTURE_PORT].lock);
 		wake_up(&inst->kernel_event_queue);
+		msm_vidc_debugfs_update(inst, MSM_VIDC_DEBUGFS_EVENT_FBD);
 	} else {
 		/*
 		 * FIXME:
@@ -674,7 +762,7 @@
 	struct vb2_buffer *vb;
 	struct vidc_hal_fbd *fill_buf_done;
 	if (!response) {
-		pr_err("Invalid response from vidc_hal\n");
+		dprintk(VIDC_ERR, "Invalid response from vidc_hal\n");
 		return;
 	}
 	inst = (struct msm_vidc_inst *)response->session_id;
@@ -740,6 +828,15 @@
 	case SESSION_GET_SEQ_HDR_DONE:
 		handle_seq_hdr_done(cmd, data);
 		break;
+	case SYS_WATCHDOG_TIMEOUT:
+		handle_sys_watchdog_timeout(cmd, data);
+		break;
+	case SYS_ERROR:
+		handle_sys_error(cmd, data);
+		break;
+	case SESSION_ERROR:
+		handle_session_error(cmd, data);
+		break;
 	default:
 		dprintk(VIDC_ERR, "response unhandled\n");
 		break;
@@ -822,12 +919,12 @@
 	}
 
 	if (!core->resources.fw.cookie)
-		core->resources.fw.cookie = pil_get("venus");
+		core->resources.fw.cookie = subsystem_get("venus");
 
 	if (IS_ERR_OR_NULL(core->resources.fw.cookie)) {
 		dprintk(VIDC_ERR, "Failed to download firmware\n");
 		rc = -ENOMEM;
-		goto fail_pil_get;
+		goto fail_subsystem_get;
 	}
 
 	rc = msm_comm_enable_clks(core);
@@ -845,20 +942,20 @@
 fail_iommu_attach:
 	msm_comm_disable_clks(core);
 fail_enable_clks:
-	pil_put(core->resources.fw.cookie);
+	subsystem_put(core->resources.fw.cookie);
 	core->resources.fw.cookie = NULL;
-fail_pil_get:
+fail_subsystem_get:
 	return rc;
 }
 
-static void msm_comm_unload_fw(struct msm_vidc_core *core)
+void msm_comm_unload_fw(struct msm_vidc_core *core)
 {
 	if (!core) {
 		dprintk(VIDC_ERR, "Invalid paramter: %p\n", core);
 		return;
 	}
 	if (core->resources.fw.cookie) {
-		pil_put(core->resources.fw.cookie);
+		subsystem_put(core->resources.fw.cookie);
 		core->resources.fw.cookie = NULL;
 		msm_comm_iommu_detach(core);
 		msm_comm_disable_clks(core);
@@ -898,7 +995,7 @@
 	return rc;
 }
 
-static int msm_comm_unset_ocmem(struct msm_vidc_core *core)
+int msm_comm_unset_ocmem(struct msm_vidc_core *core)
 {
 	struct vidc_resource_hdr rhdr;
 	int rc = 0;
@@ -964,7 +1061,7 @@
 	return rc;
 }
 
-static int msm_comm_free_ocmem(struct msm_vidc_core *core)
+int msm_comm_free_ocmem(struct msm_vidc_core *core)
 {
 	int rc = 0;
 	if (core->resources.ocmem.buf) {
@@ -1094,7 +1191,7 @@
 	}
 	if (msm_comm_scale_clocks(core, inst->session_type)) {
 		dprintk(VIDC_WARN, "Failed to scale clocks while closing\n");
-		dprintk(VIDC_WARN, "Power might be impacted\n");
+		dprintk(VIDC_INFO, "Power might be impacted\n");
 	}
 	if (list_empty(&core->instances)) {
 		msm_comm_unset_ocmem(core);
@@ -1258,7 +1355,7 @@
 	rc = vidc_hal_session_start((void *) inst->session);
 	if (rc) {
 		dprintk(VIDC_ERR,
-			"Failed to send load resources\n");
+			"Failed to send start\n");
 		goto exit;
 	}
 	change_inst_state(inst, MSM_VIDC_START);
@@ -1304,7 +1401,7 @@
 	rc = vidc_hal_session_release_res((void *) inst->session);
 	if (rc) {
 		dprintk(VIDC_ERR,
-			"Failed to send load resources\n");
+			"Failed to send release resources\n");
 		goto exit;
 	}
 	change_inst_state(inst, MSM_VIDC_RELEASE_RESOURCES);
@@ -1328,7 +1425,7 @@
 	rc = vidc_hal_session_end((void *) inst->session);
 	if (rc) {
 		dprintk(VIDC_ERR,
-			"Failed to send load resources\n");
+			"Failed to send close\n");
 		goto exit;
 	}
 	change_inst_state(inst, MSM_VIDC_OPEN);
@@ -1340,30 +1437,43 @@
 {
 	int rc = 0;
 	int flipped_state;
+	struct msm_vidc_core *core;
 	if (!inst) {
 		dprintk(VIDC_ERR,
-			"Invalid instance pointer = %p\n", inst);
+				"Invalid instance pointer = %p\n", inst);
 		return -EINVAL;
 	}
 	dprintk(VIDC_DBG,
-		"Trying to move inst: %p from: 0x%x to 0x%x\n",
-				inst, inst->state, state);
+			"Trying to move inst: %p from: 0x%x to 0x%x\n",
+			inst, inst->state, state);
+	core = inst->core;
+	if (!core) {
+		dprintk(VIDC_ERR,
+				"Invalid core pointer = %p\n", inst);
+		return -EINVAL;
+	}
 	mutex_lock(&inst->sync_lock);
+	if (inst->state == MSM_VIDC_CORE_INVALID ||
+			core->state == VIDC_CORE_INVALID) {
+		dprintk(VIDC_ERR,
+				"Core is in bad state can't change the state");
+		goto exit;
+	}
 	flipped_state = inst->state;
 	if (flipped_state < MSM_VIDC_STOP
-		&& state > MSM_VIDC_STOP) {
+			&& state > MSM_VIDC_STOP) {
 		flipped_state = MSM_VIDC_STOP + (MSM_VIDC_STOP - flipped_state);
 		flipped_state &= 0xFFFE;
 		flipped_state = flipped_state - 1;
 	} else if (flipped_state > MSM_VIDC_STOP
-		&& state < MSM_VIDC_STOP) {
+			&& state < MSM_VIDC_STOP) {
 		flipped_state = MSM_VIDC_STOP -
-				(flipped_state - MSM_VIDC_STOP + 1);
+			(flipped_state - MSM_VIDC_STOP + 1);
 		flipped_state &= 0xFFFE;
 		flipped_state = flipped_state - 1;
 	}
 	dprintk(VIDC_DBG,
-		"flipped_state = 0x%x\n", flipped_state);
+			"flipped_state = 0x%x\n", flipped_state);
 	switch (flipped_state) {
 	case MSM_VIDC_CORE_UNINIT_DONE:
 	case MSM_VIDC_CORE_INIT:
@@ -1418,14 +1528,14 @@
 		if (rc || state <= inst->state)
 			break;
 		dprintk(VIDC_DBG,
-			"Moving to release resources done state\n");
+				"Moving to release resources done state\n");
 	case MSM_VIDC_CLOSE:
 		rc = msm_comm_session_close(flipped_state, inst);
 		if (rc || state <= inst->state)
 			break;
 	case MSM_VIDC_CLOSE_DONE:
 		rc = wait_for_state(inst, flipped_state, MSM_VIDC_CLOSE_DONE,
-			SESSION_END_DONE);
+				SESSION_END_DONE);
 		if (rc || state <= inst->state)
 			break;
 	case MSM_VIDC_CORE_UNINIT:
@@ -1438,11 +1548,12 @@
 		rc = -EINVAL;
 		break;
 	}
+exit:
 	mutex_unlock(&inst->sync_lock);
 	if (rc)
 		dprintk(VIDC_ERR,
-			"Failed to move from state: %d to %d\n",
-			inst->state, state);
+				"Failed to move from state: %d to %d\n",
+				inst->state, state);
 	return rc;
 }
 
@@ -1451,16 +1562,27 @@
 	int rc = 0;
 	struct vb2_queue *q;
 	struct msm_vidc_inst *inst;
-	unsigned long flags;
 	struct vb2_buf_entry *entry;
 	struct vidc_frame_data frame_data;
+	struct msm_vidc_core *core;
 	q = vb->vb2_queue;
 	inst = q->drv_priv;
-
 	if (!inst || !vb) {
 		dprintk(VIDC_ERR, "Invalid input: %p, %p\n", inst, vb);
 		return -EINVAL;
 	}
+	core = inst->core;
+	if (!core) {
+		dprintk(VIDC_ERR,
+			"Invalid input: %p, %p, %p\n", inst, core, vb);
+		return -EINVAL;
+	}
+
+	if (inst->state == MSM_VIDC_CORE_INVALID ||
+		core->state == VIDC_CORE_INVALID) {
+		dprintk(VIDC_ERR, "Core is in bad state. Can't Queue\n");
+		return -EINVAL;
+	}
 	if (inst->state != MSM_VIDC_START_DONE) {
 			entry = kzalloc(sizeof(*entry), GFP_KERNEL);
 			if (!entry) {
@@ -1468,10 +1590,9 @@
 				goto err_no_mem;
 			}
 			entry->vb = vb;
-			dprintk(VIDC_DBG, "Queueing buffer in pendingq\n");
-			spin_lock_irqsave(&inst->lock, flags);
+			mutex_lock(&inst->sync_lock);
 			list_add_tail(&entry->list, &inst->pendingq);
-			spin_unlock_irqrestore(&inst->lock, flags);
+			mutex_unlock(&inst->sync_lock);
 	} else {
 		int64_t time_usec = timeval_to_ns(&vb->v4l2_buf.timestamp);
 		do_div(time_usec, NSEC_PER_USEC);
@@ -1501,17 +1622,21 @@
 				frame_data.alloc_len, frame_data.filled_len);
 			rc = vidc_hal_session_etb((void *) inst->session,
 					&frame_data);
+			if (!rc)
+				msm_vidc_debugfs_update(inst,
+					MSM_VIDC_DEBUGFS_EVENT_ETB);
 			dprintk(VIDC_DBG, "Sent etb to HAL\n");
 		} else if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
 			struct vidc_seq_hdr seq_hdr;
+			int extra_idx = 0;
 			frame_data.filled_len = 0;
 			frame_data.buffer_type = HAL_BUFFER_OUTPUT;
-			if (inst->extradata_handle) {
+			extra_idx =
+			EXTRADATA_IDX(inst->fmts[CAPTURE_PORT]->num_planes);
+			if (extra_idx && (extra_idx < VIDEO_MAX_PLANES) &&
+				vb->v4l2_planes[extra_idx].m.userptr)
 				frame_data.extradata_addr =
-					inst->extradata_handle->device_addr;
-			} else {
-				frame_data.extradata_addr = 0;
-			}
+					vb->v4l2_planes[extra_idx].m.userptr;
 			dprintk(VIDC_DBG,
 				"Sending ftb to hal: Alloc: %d :filled: %d",
 				frame_data.alloc_len, frame_data.filled_len);
@@ -1533,6 +1658,9 @@
 			} else {
 				rc = vidc_hal_session_ftb((void *)
 					inst->session, &frame_data);
+			if (!rc)
+				msm_vidc_debugfs_update(inst,
+					MSM_VIDC_DEBUGFS_EVENT_FTB);
 			}
 			inst->ftb_count++;
 		} else {
@@ -1552,6 +1680,12 @@
 {
 	int rc = 0;
 	mutex_lock(&inst->sync_lock);
+	if (inst->state < MSM_VIDC_OPEN_DONE || inst->state >= MSM_VIDC_CLOSE) {
+		dprintk(VIDC_ERR,
+			"Not in proper state to query buffer requirements\n");
+		rc = -EAGAIN;
+		goto exit;
+	}
 	init_completion(
 		&inst->completions[SESSION_MSG_INDEX(SESSION_PROPERTY_INFO)]);
 	rc = vidc_hal_session_get_buf_req((void *) inst->session);
@@ -1573,7 +1707,6 @@
 	mutex_unlock(&inst->sync_lock);
 	return rc;
 }
-
 int msm_comm_release_scratch_buffers(struct msm_vidc_inst *inst)
 {
 	struct msm_smem *handle;
@@ -1582,6 +1715,18 @@
 	struct vidc_buffer_addr_info buffer_info;
 	int rc = 0;
 	unsigned long flags;
+	struct msm_vidc_core *core;
+	if (!inst) {
+		dprintk(VIDC_ERR,
+				"Invalid instance pointer = %p\n", inst);
+		return -EINVAL;
+	}
+	core = inst->core;
+	if (!core) {
+		dprintk(VIDC_ERR,
+				"Invalid core pointer = %p\n", core);
+		return -EINVAL;
+	}
 	spin_lock_irqsave(&inst->lock, flags);
 	if (!list_empty(&inst->internalbufs)) {
 		list_for_each_safe(ptr, next, &inst->internalbufs) {
@@ -1592,13 +1737,17 @@
 			buffer_info.buffer_type = HAL_BUFFER_INTERNAL_SCRATCH;
 			buffer_info.num_buffers = 1;
 			buffer_info.align_device_addr = handle->device_addr;
-			rc = vidc_hal_session_release_buffers(
-				(void *) inst->session,	&buffer_info);
-			if (rc)
-				dprintk(VIDC_WARN,
-					"Failed to release scratch buffer: 0x%x, %d",
-					buffer_info.align_device_addr,
-					buffer_info.buffer_size);
+			if (inst->state != MSM_VIDC_CORE_INVALID &&
+					core->state != VIDC_CORE_INVALID) {
+				rc = vidc_hal_session_release_buffers(
+						(void *) inst->session,
+							&buffer_info);
+				if (rc)
+					dprintk(VIDC_WARN,
+						"Rel scrtch buf fail:0x%x, %d",
+						buffer_info.align_device_addr,
+						buffer_info.buffer_size);
+			}
 			list_del(&buf->list);
 			spin_unlock_irqrestore(&inst->lock, flags);
 			msm_smem_free(inst->mem_client, buf->handle);
@@ -1618,23 +1767,39 @@
 	struct vidc_buffer_addr_info buffer_info;
 	int rc = 0;
 	unsigned long flags;
+	struct msm_vidc_core *core;
+	if (!inst) {
+		dprintk(VIDC_ERR,
+				"Invalid instance pointer = %p\n", inst);
+		return -EINVAL;
+	}
+	core = inst->core;
+	if (!core) {
+		dprintk(VIDC_ERR,
+				"Invalid core pointer = %p\n", core);
+		return -EINVAL;
+	}
 	spin_lock_irqsave(&inst->lock, flags);
 	if (!list_empty(&inst->persistbufs)) {
 		list_for_each_safe(ptr, next, &inst->persistbufs) {
 			buf = list_entry(ptr, struct internal_buf,
-				list);
+					list);
 			handle = buf->handle;
 			buffer_info.buffer_size = handle->size;
 			buffer_info.buffer_type = HAL_BUFFER_INTERNAL_PERSIST;
 			buffer_info.num_buffers = 1;
 			buffer_info.align_device_addr = handle->device_addr;
-			rc = vidc_hal_session_release_buffers(
-				(void *) inst->session,	&buffer_info);
-			if (rc)
-				dprintk(VIDC_WARN,
-					"Failed to release persist buffer 0x%x, %d\n",
-					buffer_info.align_device_addr,
-					buffer_info.buffer_size);
+			if (inst->state != MSM_VIDC_CORE_INVALID &&
+					core->state != VIDC_CORE_INVALID) {
+				rc = vidc_hal_session_release_buffers(
+						(void *) inst->session,
+							&buffer_info);
+				if (rc)
+					dprintk(VIDC_WARN,
+						"Rel prst buf fail:0x%x, %d",
+						buffer_info.align_device_addr,
+						buffer_info.buffer_size);
+			}
 			list_del(&buf->list);
 			spin_unlock_irqrestore(&inst->lock, flags);
 			msm_smem_free(inst->mem_client, buf->handle);
@@ -1646,6 +1811,29 @@
 	return rc;
 }
 
+int msm_comm_try_set_prop(struct msm_vidc_inst *inst,
+	enum hal_property ptype, void *pdata)
+{
+	int rc = 0;
+	if (!inst) {
+		dprintk(VIDC_ERR, "Invalid input: %p\n", inst);
+		return -EINVAL;
+	}
+	mutex_lock(&inst->sync_lock);
+	if (inst->state < MSM_VIDC_OPEN_DONE || inst->state >= MSM_VIDC_CLOSE) {
+		dprintk(VIDC_ERR, "Not in proper state to set property\n");
+		rc = -EAGAIN;
+		goto exit;
+	}
+	rc = vidc_hal_session_set_property((void *)inst->session,
+			ptype, pdata);
+	if (rc)
+		dprintk(VIDC_ERR, "Failed to set hal property for framesize\n");
+exit:
+	mutex_unlock(&inst->sync_lock);
+	return rc;
+}
+
 int msm_comm_set_scratch_buffers(struct msm_vidc_inst *inst)
 {
 	int rc = 0;
@@ -1772,11 +1960,71 @@
 	return rc;
 }
 
+static void msm_comm_flush_in_invalid_state(struct msm_vidc_inst *inst)
+{
+	struct v4l2_event dqevent = {0};
+	struct list_head *ptr, *next;
+	struct vb2_buffer *vb;
+	if (!list_empty(&inst->bufq[CAPTURE_PORT].
+				vb2_bufq.queued_list)) {
+		list_for_each_safe(ptr, next,
+				&inst->bufq[CAPTURE_PORT].
+				vb2_bufq.queued_list) {
+			vb = container_of(ptr,
+					struct vb2_buffer,
+					queued_entry);
+			if (vb) {
+				vb->v4l2_planes[0].bytesused = 0;
+				mutex_lock(&inst->bufq[CAPTURE_PORT].lock);
+				vb2_buffer_done(vb,
+						VB2_BUF_STATE_DONE);
+				mutex_unlock(&inst->bufq[CAPTURE_PORT].lock);
+			}
+		}
+	}
+	if (!list_empty(&inst->bufq[OUTPUT_PORT].
+				vb2_bufq.queued_list)) {
+		list_for_each_safe(ptr, next,
+				&inst->bufq[OUTPUT_PORT].
+				vb2_bufq.queued_list) {
+			vb = container_of(ptr,
+					struct vb2_buffer,
+					queued_entry);
+			if (vb) {
+				vb->v4l2_planes[0].bytesused = 0;
+				mutex_lock(&inst->bufq[OUTPUT_PORT].lock);
+				vb2_buffer_done(vb,
+						VB2_BUF_STATE_DONE);
+				mutex_unlock(&inst->bufq[OUTPUT_PORT].lock);
+			}
+		}
+	}
+	dqevent.type = V4L2_EVENT_MSM_VIDC_FLUSH_DONE;
+	dqevent.id = 0;
+	v4l2_event_queue_fh(&inst->event_handler, &dqevent);
+	return;
+}
 int msm_comm_flush(struct msm_vidc_inst *inst, u32 flags)
 {
 	int rc =  0;
 	bool ip_flush = false;
 	bool op_flush = false;
+	struct list_head *ptr, *next;
+	struct vb2_buf_entry *temp;
+	struct mutex *lock;
+	struct msm_vidc_core *core;
+	if (!inst) {
+		dprintk(VIDC_ERR,
+				"Invalid instance pointer = %p\n", inst);
+		return -EINVAL;
+	}
+	core = inst->core;
+	if (!core) {
+		dprintk(VIDC_ERR,
+				"Invalid core pointer = %p\n", core);
+		return -EINVAL;
+	}
+
 	ip_flush = flags & V4L2_QCOM_CMD_FLUSH_OUTPUT;
 	op_flush = flags & V4L2_QCOM_CMD_FLUSH_CAPTURE;
 
@@ -1784,11 +2032,45 @@
 		dprintk(VIDC_WARN, "Input only flush not supported\n");
 		return 0;
 	}
+	if (inst->state == MSM_VIDC_CORE_INVALID ||
+			core->state == VIDC_CORE_INVALID) {
+		dprintk(VIDC_ERR,
+				"Core %p and inst %p are in bad state\n",
+					core, inst);
+		msm_comm_flush_in_invalid_state(inst);
+	}
+
 	mutex_lock(&inst->sync_lock);
 	if (inst->in_reconfig && !ip_flush && op_flush) {
+		if (!list_empty(&inst->pendingq)) {
+			/*Execution can never reach here since port reconfig
+			 * wont happen unless pendingq is emptied out
+			 * (both pendingq and flush being secured with same
+			 * lock). Printing a message here incase this breaks.*/
+			dprintk(VIDC_WARN,
+			"FLUSH BUG: Pending q not empty! It should be empty\n");
+		}
 		rc = vidc_hal_session_flush(inst->session,
 				HAL_FLUSH_OUTPUT);
 	} else {
+		if (!list_empty(&inst->pendingq)) {
+			/*If flush is called after queueing buffers but before
+			 * streamon driver should flush the pending queue*/
+			list_for_each_safe(ptr, next, &inst->pendingq) {
+				temp =
+				list_entry(ptr, struct vb2_buf_entry, list);
+				if (temp->vb->v4l2_buf.type ==
+					V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+					lock = &inst->bufq[CAPTURE_PORT].lock;
+				else
+					lock = &inst->bufq[OUTPUT_PORT].lock;
+				mutex_lock(lock);
+				vb2_buffer_done(temp->vb, VB2_BUF_STATE_DONE);
+				mutex_unlock(lock);
+				list_del(&temp->list);
+				kfree(temp);
+			}
+		}
 		rc = vidc_hal_session_flush(inst->session,
 				HAL_FLUSH_ALL);
 	}
diff --git a/drivers/media/video/msm_vidc/msm_vidc_common.h b/drivers/media/video/msm_vidc/msm_vidc_common.h
index 0708724..7562058 100644
--- a/drivers/media/video/msm_vidc/msm_vidc_common.h
+++ b/drivers/media/video/msm_vidc/msm_vidc_common.h
@@ -27,6 +27,8 @@
 		struct msm_vidc_inst *inst, enum v4l2_buf_type type);
 int msm_comm_try_state(struct msm_vidc_inst *inst, int state);
 int msm_comm_try_get_bufreqs(struct msm_vidc_inst *inst);
+int msm_comm_try_set_prop(struct msm_vidc_inst *inst,
+	enum hal_property ptype, void *pdata);
 int msm_comm_set_scratch_buffers(struct msm_vidc_inst *inst);
 int msm_comm_set_persist_buffers(struct msm_vidc_inst *inst);
 int msm_comm_qbuf(struct vb2_buffer *vb);
@@ -34,6 +36,11 @@
 int msm_comm_flush(struct msm_vidc_inst *inst, u32 flags);
 int msm_comm_release_scratch_buffers(struct msm_vidc_inst *inst);
 int msm_comm_release_persist_buffers(struct msm_vidc_inst *inst);
+void msm_comm_unload_fw(struct msm_vidc_core *core);
+void change_inst_state(struct msm_vidc_inst *inst,
+	enum instance_state state);
+int msm_comm_unset_ocmem(struct msm_vidc_core *core);
+int msm_comm_free_ocmem(struct msm_vidc_core *core);
 #define IS_PRIV_CTRL(idx) (\
 		(V4L2_CTRL_ID2CLASS(idx) == V4L2_CTRL_CLASS_MPEG) && \
 		V4L2_CTRL_DRIVER_PRIV(idx))
diff --git a/drivers/media/video/msm_vidc/msm_vidc_debug.c b/drivers/media/video/msm_vidc/msm_vidc_debug.c
index 7921f84..914c422 100644
--- a/drivers/media/video/msm_vidc/msm_vidc_debug.c
+++ b/drivers/media/video/msm_vidc/msm_vidc_debug.c
@@ -15,6 +15,7 @@
 
 #define MAX_DBG_BUF_SIZE 4096
 int msm_vidc_debug;
+int msm_fw_debug;
 
 struct debug_buffer {
 	char ptr[MAX_DBG_BUF_SIZE];
@@ -89,6 +90,7 @@
 		goto failed_create_dir;
 	}
 	msm_vidc_debug = 0;
+	msm_fw_debug = 0;
 	snprintf(debugfs_name, MAX_DEBUGFS_NAME, "core%d", core->id);
 	dir = debugfs_create_dir(debugfs_name, parent);
 	if (!dir) {
@@ -104,6 +106,13 @@
 		dprintk(VIDC_ERR, "debugfs_create_file: fail\n");
 		goto failed_create_dir;
 	}
+	msm_vidc_debug = 0x3;
+	if (!debugfs_create_u32("fw_level", S_IRUGO | S_IWUSR,
+			parent, &msm_fw_debug)) {
+		dprintk(VIDC_ERR, "debugfs_create_file: fail\n");
+		goto failed_create_dir;
+	}
+	msm_fw_debug = 0x18;
 failed_create_dir:
 	return dir;
 }
@@ -180,7 +189,46 @@
 		dprintk(VIDC_ERR, "debugfs_create_file: fail\n");
 		goto failed_create_dir;
 	}
-
+	inst->debug.pdata[FRAME_PROCESSING].sampling = true;
 failed_create_dir:
 	return dir;
 }
+
+void msm_vidc_debugfs_update(struct msm_vidc_inst *inst,
+	enum msm_vidc_debugfs_event e)
+{
+	struct msm_vidc_debug *d = &inst->debug;
+	char a[64] = "Frame processing";
+	switch (e) {
+	case MSM_VIDC_DEBUGFS_EVENT_ETB:
+		inst->count.etb++;
+		if (inst->count.ftb > inst->count.fbd) {
+			d->pdata[FRAME_PROCESSING].name[0] = '\0';
+			tic(inst, FRAME_PROCESSING, a);
+		}
+	break;
+	case MSM_VIDC_DEBUGFS_EVENT_EBD:
+		inst->count.ebd++;
+		if (inst->count.ebd == inst->count.etb)
+			toc(inst, FRAME_PROCESSING);
+	break;
+	case MSM_VIDC_DEBUGFS_EVENT_FTB: {
+		inst->count.ftb++;
+		if (inst->count.etb > inst->count.ebd) {
+			d->pdata[FRAME_PROCESSING].name[0] = '\0';
+			tic(inst, FRAME_PROCESSING, a);
+		}
+	}
+	break;
+	case MSM_VIDC_DEBUGFS_EVENT_FBD:
+		inst->count.fbd++;
+		inst->debug.counter++;
+		if (inst->count.fbd == inst->count.ftb)
+			toc(inst, FRAME_PROCESSING);
+		break;
+	default:
+		dprintk(VIDC_ERR, "Invalid state in debugfs: %d\n", e);
+		break;
+	}
+}
+
diff --git a/drivers/media/video/msm_vidc/msm_vidc_debug.h b/drivers/media/video/msm_vidc/msm_vidc_debug.h
index b7928e9..1a51173 100644
--- a/drivers/media/video/msm_vidc/msm_vidc_debug.h
+++ b/drivers/media/video/msm_vidc/msm_vidc_debug.h
@@ -14,28 +14,107 @@
 #ifndef __MSM_VIDC_DEBUG__
 #define __MSM_VIDC_DEBUG__
 #include <linux/debugfs.h>
+#include <linux/delay.h>
 #include "msm_vidc_internal.h"
 
 #define VIDC_DBG_TAG "msm_vidc: %d: "
 
+/* To enable messages OR these values and
+ * echo the result to debugfs file.
+ *
+ * To enable all messages set debug_level = 0x101F
+ */
+
 enum vidc_msg_prio {
-	VIDC_ERR,
-	VIDC_WARN,
-	VIDC_INFO,
-	VIDC_DBG,
+	VIDC_ERR  = 0x0001,
+	VIDC_WARN = 0x0002,
+	VIDC_INFO = 0x0004,
+	VIDC_DBG  = 0x0008,
+	VIDC_PROF = 0x0010,
+	VIDC_FW   = 0x1000,
+};
+
+enum msm_vidc_debugfs_event {
+	MSM_VIDC_DEBUGFS_EVENT_ETB,
+	MSM_VIDC_DEBUGFS_EVENT_EBD,
+	MSM_VIDC_DEBUGFS_EVENT_FTB,
+	MSM_VIDC_DEBUGFS_EVENT_FBD,
 };
 
 extern int msm_vidc_debug;
-#define dprintk(level, fmt, arg...)	\
-	do {							\
-		if (msm_vidc_debug >= level) \
-			printk(KERN_DEBUG VIDC_DBG_TAG fmt, \
-				level, ## arg); \
+extern int msm_fw_debug;
+
+#define dprintk(__level, __fmt, arg...)	\
+	do { \
+		if (msm_vidc_debug & __level) \
+			printk(KERN_DEBUG VIDC_DBG_TAG \
+				__fmt, __level, ## arg); \
 	} while (0)
 
 struct dentry *msm_vidc_debugfs_init_core(struct msm_vidc_core *core,
 		struct dentry *parent);
 struct dentry *msm_vidc_debugfs_init_inst(struct msm_vidc_inst *inst,
 		struct dentry *parent);
+void msm_vidc_debugfs_update(struct msm_vidc_inst *inst,
+		enum msm_vidc_debugfs_event e);
+
+static inline void tic(struct msm_vidc_inst *i, enum profiling_points p,
+				 char *b)
+{
+	struct timeval __ddl_tv;
+	if (!i->debug.pdata[p].name[0])
+		memcpy(i->debug.pdata[p].name, b, 64);
+	if ((msm_vidc_debug & VIDC_PROF) &&
+		i->debug.pdata[p].sampling) {
+		do_gettimeofday(&__ddl_tv);
+		i->debug.pdata[p].start =
+			(__ddl_tv.tv_sec * 1000) + (__ddl_tv.tv_usec / 1000);
+			i->debug.pdata[p].sampling = false;
+	}
+}
+
+static inline void toc(struct msm_vidc_inst *i, enum profiling_points p)
+{
+	struct timeval __ddl_tv;
+	if ((msm_vidc_debug & VIDC_PROF) &&
+		!i->debug.pdata[p].sampling) {
+		do_gettimeofday(&__ddl_tv);
+		i->debug.pdata[p].stop = (__ddl_tv.tv_sec * 1000)
+		+ (__ddl_tv.tv_usec / 1000);
+		i->debug.pdata[p].cumulative =
+		(i->debug.pdata[p].stop - i->debug.pdata[p].start);
+		if (i->count.fbd) {
+			if (i->debug.pdata[p].average != 0) {
+				i->debug.pdata[p].average = ((i->debug.pdata[p].
+					average * (i->count.fbd -
+					i->debug.counter) +
+					i->debug.pdata[p].cumulative)
+					/ i->count.fbd);
+			} else {
+				i->debug.pdata[p].average =
+					i->debug.pdata[p].cumulative
+					/ i->count.fbd;
+			}
+		}
+		i->debug.counter = 0;
+		i->debug.pdata[p].cumulative = 0;
+		i->debug.pdata[p].sampling = true;
+	}
+}
+
+static inline void show_stats(struct msm_vidc_inst *i)
+{
+	int x;
+	for (x = 0; x < MAX_PROFILING_POINTS; x++) {
+		if ((i->debug.pdata[x].name[0])  &&
+			(msm_vidc_debug & VIDC_PROF)) {
+			dprintk(VIDC_PROF, "%s averaged %d ms/sample\n",
+				i->debug.pdata[x].name,
+				i->debug.pdata[x].average);
+			dprintk(VIDC_PROF, "%s Samples: %d",
+					i->debug.pdata[x].name, i->count.fbd);
+		}
+	}
+}
 
 #endif
diff --git a/drivers/media/video/msm_vidc/msm_vidc_internal.h b/drivers/media/video/msm_vidc/msm_vidc_internal.h
index 1ea92fc..8e1a99e 100644
--- a/drivers/media/video/msm_vidc/msm_vidc_internal.h
+++ b/drivers/media/video/msm_vidc/msm_vidc_internal.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -30,6 +30,7 @@
 #include <media/v4l2-ctrls.h>
 #include <media/videobuf2-core.h>
 #include <media/msm_vidc.h>
+#include <media/msm_media_info.h>
 
 #include "vidc_hal_api.h"
 
@@ -49,31 +50,7 @@
 
 #define MAX_NAME_LENGTH 64
 
-#define NV12_IL_CALC_Y_STRIDE(stride, frame_width, stride_multiple) \
-	{ stride = (frame_width + stride_multiple - 1) & \
-	(0xffffffff - (stride_multiple - 1)); }
-
-#define NV12_IL_CALC_Y_BUFHEIGHT(buf_height, frame_height,\
-	min_buf_height_multiple) \
-	{ buf_height = (frame_height + min_buf_height_multiple - 1) & \
-	(0xffffffff - (min_buf_height_multiple - 1)); }
-
-#define NV12_IL_CALC_UV_STRIDE(stride, frame_width, stride_multiple) \
-	{ stride = ((((frame_width + 1) >> 1) + stride_multiple - 1) & \
-	(0xffffffff - (stride_multiple - 1))) << 1; }
-
-#define NV12_IL_CALC_UV_BUFHEIGHT(buf_height, frame_height,\
-	min_buf_height_multiple) \
-	{ buf_height = ((((frame_height + 1) >> 1) + \
-	min_buf_height_multiple - 1) & (0xffffffff - \
-	(min_buf_height_multiple - 1))); }
-
-#define NV12_IL_CALC_BUF_SIZE(buf_size, y_buf_size, y_stride, \
-	y_buf_height, uv_buf_size, uv_stride, uv_buf_height, uv_alignment) \
-	{ y_buf_size = (y_stride * y_buf_height); \
-	uv_buf_size = (uv_stride * uv_buf_height) + uv_alignment; \
-	buf_size = y_buf_size + uv_buf_size; }
-
+#define EXTRADATA_IDX(__num_planes) (__num_planes - 1)
 enum vidc_ports {
 	OUTPUT_PORT,
 	CAPTURE_PORT,
@@ -84,6 +61,7 @@
 	VIDC_CORE_UNINIT = 0,
 	VIDC_CORE_INIT,
 	VIDC_CORE_INIT_DONE,
+	VIDC_CORE_INVALID
 };
 
 /*Donot change the enum values unless
@@ -105,6 +83,7 @@
 	MSM_VIDC_CLOSE,
 	MSM_VIDC_CLOSE_DONE,
 	MSM_VIDC_CORE_UNINIT,
+	MSM_VIDC_CORE_INVALID
 };
 
 struct buf_info {
@@ -193,6 +172,44 @@
 	struct mutex lock;
 };
 
+enum profiling_points {
+	SYS_INIT = 0,
+	SESSION_INIT,
+	LOAD_RESOURCES,
+	FRAME_PROCESSING,
+	FW_IDLE,
+	MAX_PROFILING_POINTS,
+};
+
+struct buf_count {
+	int etb;
+	int ftb;
+	int fbd;
+	int ebd;
+};
+
+struct profile_data {
+	int start;
+	int stop;
+	int cumulative;
+	char name[64];
+	int sampling;
+	int average;
+};
+
+struct msm_vidc_debug {
+	struct profile_data pdata[MAX_PROFILING_POINTS];
+	int profile;
+	int counter;
+};
+
+struct msm_vidc_ssr_info {
+	struct subsys_device *msm_vidc_dev;
+	struct subsys_desc *msm_vidc_subsys_desc;
+	void *msm_vidc_ramdump_dev;
+	bool ssr_in_progress;
+};
+
 struct msm_vidc_core {
 	struct list_head list;
 	struct mutex sync_lock;
@@ -210,6 +227,7 @@
 	enum vidc_core_state state;
 	struct msm_vidc_resources resources;
 	struct completion completions[SYS_MSG_END - SYS_MSG_START + 1];
+	struct msm_vidc_ssr_info ssr_info;
 };
 
 struct msm_vidc_inst {
@@ -240,6 +258,8 @@
 	u32 ftb_count;
 	struct vb2_buffer *vb2_seq_hdr;
 	void *priv;
+	struct msm_vidc_debug debug;
+	struct buf_count count;
 };
 
 extern struct msm_vidc_drv *vidc_driver;
@@ -259,5 +279,4 @@
 void handle_cmd_response(enum command_response cmd, void *data);
 int msm_vidc_ocmem_notify_handler(struct notifier_block *this,
 		unsigned long event, void *data);
-
 #endif
diff --git a/drivers/media/video/msm_vidc/msm_vidc_ssr.c b/drivers/media/video/msm_vidc/msm_vidc_ssr.c
new file mode 100644
index 0000000..33464c6f
--- /dev/null
+++ b/drivers/media/video/msm_vidc/msm_vidc_ssr.c
@@ -0,0 +1,175 @@
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#include <msm_vidc_ssr.h>
+
+static struct msm_vidc_core *get_vidc_core_from_dev(struct device *dev)
+{
+	struct video_device *vdev;
+	struct msm_video_device *videodev;
+	struct msm_vidc_core *core;
+	vdev = container_of(dev, struct video_device, dev);
+	videodev = container_of(vdev, struct msm_video_device, vdev);
+	core = container_of(videodev, struct msm_vidc_core,
+		vdev[MSM_VIDC_DECODER]);
+	return core;
+}
+int msm_vidc_shutdown(const struct subsys_desc *subsys)
+{
+	struct msm_vidc_inst *inst;
+	struct msm_vidc_core *core = NULL;
+	struct v4l2_event dqevent;
+	struct device *dev;
+	unsigned long flags;
+	int rc = 0;
+	if (!subsys) {
+		dprintk(VIDC_ERR, "Invalid subsys: %p\n", subsys);
+		rc = -EINVAL;
+		goto exit;
+	}
+	dev = subsys->dev;
+	if (dev)
+		core = get_vidc_core_from_dev(dev);
+	if (!core) {
+		dprintk(VIDC_ERR, "Invalid core: %p\n", core);
+		rc = -EINVAL;
+		goto exit;
+	}
+	core->ssr_info.ssr_in_progress = true;
+	spin_lock_irqsave(&core->lock, flags);
+	core->state = VIDC_CORE_INVALID;
+	spin_unlock_irqrestore(&core->lock, flags);
+	dqevent.type = V4L2_EVENT_MSM_VIDC_SYS_ERROR;
+	dqevent.id = 0;
+	list_for_each_entry(inst, &core->instances, list) {
+		if (inst) {
+			v4l2_event_queue_fh(&inst->event_handler, &dqevent);
+			spin_lock_irqsave(&inst->lock, flags);
+			inst->state = MSM_VIDC_CORE_INVALID;
+			spin_unlock_irqrestore(&inst->lock, flags);
+		}
+	}
+exit:
+	return rc;
+}
+int msm_vidc_ramdump(int enable, const struct subsys_desc *subsys)
+{
+	struct ramdump_segment memory_segments[] = {{0x0f500000, 0xFF000} };
+	struct msm_vidc_core *core = NULL;
+	void *dump_addr = NULL;
+	int rc = 0;
+	struct device *dev;
+	if (!subsys) {
+		dprintk(VIDC_ERR, "Invalid subsys: %p\n", subsys);
+		rc = -EINVAL;
+		goto exit;
+	}
+	dev = subsys->dev;
+	if (dev)
+		core = get_vidc_core_from_dev(dev);
+	if (!core) {
+		dprintk(VIDC_ERR, "Invalid core: %p\n", core);
+		rc = -EINVAL;
+		goto exit;
+	}
+	if (enable) {
+		rc = do_ramdump(core->ssr_info.msm_vidc_ramdump_dev,
+			memory_segments,
+			ARRAY_SIZE(memory_segments));
+		if (rc < 0)
+			dprintk(VIDC_DBG, "Failed : FW image memory dump\n");
+		dump_addr = kzalloc(core->resources.ocmem.buf->len, GFP_KERNEL);
+		if (dump_addr)
+			rc = ocmem_dump(OCMEM_VIDEO, core->resources.ocmem.buf,
+				(unsigned long)dump_addr);
+		if (rc < 0) {
+			dprintk(VIDC_DBG, "Failed : OCMEM copy\n");
+		} else	{
+			memory_segments[0].address = (unsigned long)dump_addr;
+			memory_segments[0].size =
+				(unsigned long)core->resources.ocmem.buf->len;
+			rc = do_ramdump(core->ssr_info.msm_vidc_ramdump_dev,
+				memory_segments,
+				ARRAY_SIZE(memory_segments));
+			if (rc < 0)
+				dprintk(VIDC_DBG, "Failed : OCMEM dump\n");
+		}
+		kfree(dump_addr);
+	}
+exit:
+	return rc;
+}
+int msm_vidc_powerup(const struct subsys_desc *subsys)
+{
+	unsigned long flags;
+	struct msm_vidc_core *core = NULL;
+	int rc = 0;
+	struct device *dev;
+	if (!subsys) {
+		dprintk(VIDC_ERR, "Invalid subsys: %p\n", subsys);
+		rc = -EINVAL;
+		goto exit;
+	}
+	dev = subsys->dev;
+	if (dev)
+		core = get_vidc_core_from_dev(dev);
+	if (!core) {
+		dprintk(VIDC_ERR, "Invalid core: %p\n", core);
+		rc = -EINVAL;
+		goto exit;
+	}
+	msm_comm_free_ocmem(core);
+	vidc_hal_core_release(core->device);
+	spin_lock_irqsave(&core->lock, flags);
+	core->state = VIDC_CORE_UNINIT;
+	spin_unlock_irqrestore(&core->lock, flags);
+	msm_comm_unload_fw(core);
+exit:
+	return rc;
+}
+void msm_vidc_crash_shutdown(const struct subsys_desc *subsys)
+{
+	dprintk(VIDC_DBG, "Nothing implemented in crash shutdown\n");
+}
+static struct subsys_desc msm_vidc_subsystem = {
+	.name = "msm_vidc",
+	.dev = NULL,
+	.shutdown = msm_vidc_shutdown,
+	.powerup = msm_vidc_powerup,
+	.ramdump = msm_vidc_ramdump,
+	.crash_shutdown = msm_vidc_crash_shutdown
+};
+int msm_vidc_ssr_init(struct msm_vidc_core *core)
+{
+	int rc = 0;
+	msm_vidc_subsystem.dev = &core->vdev[MSM_VIDC_DECODER].vdev.dev;
+	core->ssr_info.msm_vidc_dev = subsys_register(&msm_vidc_subsystem);
+	if (IS_ERR_OR_NULL(core->ssr_info.msm_vidc_dev)) {
+		dprintk(VIDC_ERR, "msm_vidc Sub System registration failed\n");
+		rc = -ENODEV;
+	}
+	core->ssr_info.msm_vidc_ramdump_dev = create_ramdump_device("msm_vidc",
+			msm_vidc_subsystem.dev);
+	if (!core->ssr_info.msm_vidc_ramdump_dev) {
+		dprintk(VIDC_ERR, "Unable to create msm_vidc ramdump device\n");
+		rc = -ENODEV;
+	}
+	core->ssr_info.ssr_in_progress = false;
+	return rc;
+}
+
+int msm_vidc_ssr_uninit(struct msm_vidc_core *core)
+{
+	subsys_unregister(core->ssr_info.msm_vidc_dev);
+	destroy_ramdump_device(core->ssr_info.msm_vidc_ramdump_dev);
+	return 0;
+}
diff --git a/drivers/media/video/msm_vidc/msm_vidc_ssr.h b/drivers/media/video/msm_vidc/msm_vidc_ssr.h
new file mode 100644
index 0000000..90f7380
--- /dev/null
+++ b/drivers/media/video/msm_vidc/msm_vidc_ssr.h
@@ -0,0 +1,28 @@
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef __MSM_VIDC_SSR__
+#define __MSM_VIDC_SSR__
+
+#include <../ramdump.h>
+#include <mach/subsystem_restart.h>
+#include <mach/subsystem_notif.h>
+#include <media/msm_vidc.h>
+#include "msm_vidc_internal.h"
+#include "msm_vidc_common.h"
+#include "msm_vidc_debug.h"
+#include "vidc_hal_api.h"
+int msm_vidc_ssr_init(struct msm_vidc_core *core);
+int msm_vidc_ssr_uninit(struct msm_vidc_core *core);
+
+#endif
diff --git a/drivers/media/video/msm_vidc/vidc_hal.c b/drivers/media/video/msm_vidc/vidc_hal.c
index f0d0e73..55f9a07 100644
--- a/drivers/media/video/msm_vidc/vidc_hal.c
+++ b/drivers/media/video/msm_vidc/vidc_hal.c
@@ -248,7 +248,7 @@
 	struct vidc_iface_q_info *qinfo;
 
 	if (!info || !packet || !pb_tx_req_is_set) {
-		dprintk(VIDC_ERR, "Invalid Params in ");
+		dprintk(VIDC_ERR, "Invalid Params");
 		return -EINVAL;
 	}
 
@@ -311,11 +311,11 @@
 	int rc = 0;
 
 	if (!mem || !clnt || !size) {
-		dprintk(VIDC_ERR, "Invalid Params in ");
+		dprintk(VIDC_ERR, "Invalid Params");
 		return -EINVAL;
 	}
 	vmem = (struct vidc_mem_addr *)mem;
-	dprintk(VIDC_WARN, "start to alloc: size:%d, Flags: %d", size, flags);
+	dprintk(VIDC_INFO, "start to alloc: size:%d, Flags: %d", size, flags);
 
 	alloc  = msm_smem_alloc(clnt, size, align, flags, domain, 1, 1);
 	dprintk(VIDC_DBG, "Alloc done");
@@ -390,7 +390,7 @@
 	int result = -EPERM;
 
 	if (!device || !pkt) {
-		dprintk(VIDC_ERR, "Invalid Params in ");
+		dprintk(VIDC_ERR, "Invalid Params");
 		return -EINVAL;
 	}
 
@@ -422,7 +422,7 @@
 	struct vidc_iface_q_info *q_info;
 
 	if (!pkt) {
-		dprintk(VIDC_ERR, "Invalid Params in ");
+		dprintk(VIDC_ERR, "Invalid Params");
 		return -EINVAL;
 	}
 	spin_lock(&device->read_lock);
@@ -456,7 +456,7 @@
 	struct vidc_iface_q_info *q_info;
 
 	if (!pkt) {
-		dprintk(VIDC_ERR, "Invalid Params in ");
+		dprintk(VIDC_ERR, "Invalid Params");
 		return -EINVAL;
 	}
 	spin_lock(&device->read_lock);
@@ -592,7 +592,7 @@
 	u32 ctrl_status = 0, count = 0, rc = 0;
 	int max_tries = 100;
 	write_register(device->hal_data->register_base_addr,
-			VIDC_WRAPPER_INTR_MASK, 0, 0);
+			VIDC_WRAPPER_INTR_MASK, 0x8, 0);
 	write_register(device->hal_data->register_base_addr,
 			VIDC_CPU_CS_SCIACMDARG3, 1, 0);
 	while (!ctrl_status && count < max_tries) {
@@ -613,11 +613,9 @@
 	write_register(device->hal_data->register_base_addr,
 				   VIDC_VENUS_VBIF_CLK_ON, 1, 0);
 	write_register(device->hal_data->register_base_addr,
-			VIDC_VBIF_OUT_AXI_AOOO_EN, 0x00000FFF, 0);
+			VIDC_VBIF_OUT_AXI_AOOO_EN, 0x00001FFF, 0);
 	write_register(device->hal_data->register_base_addr,
-			VIDC_VBIF_OUT_AXI_AOOO, 0x0FFF0FFF, 0);
-	write_register(device->hal_data->register_base_addr,
-			VIDC_VENUS_VBIF_CLK_ON, 1, 0);
+			VIDC_VBIF_OUT_AXI_AOOO, 0x1FFF1FFF, 0);
 	write_register(device->hal_data->register_base_addr,
 			VIDC_VBIF_IN_RD_LIM_CONF0, 0x10101001, 0);
 	write_register(device->hal_data->register_base_addr,
@@ -641,7 +639,33 @@
 	write_register(device->hal_data->register_base_addr,
 			VIDC_VBIF_ARB_CTL, 0x00000030, 0);
 	write_register(device->hal_data->register_base_addr,
+			VIDC_VENUS_VBIF_DDR_OUT_MAX_BURST, 0x00000707, 0);
+	write_register(device->hal_data->register_base_addr,
+			VIDC_VENUS_VBIF_OCMEM_OUT_MAX_BURST, 0x00000707, 0);
+	write_register(device->hal_data->register_base_addr,
+			VIDC_VENUS_VBIF_ROUND_ROBIN_QOS_ARB, 0x00000001, 0);
+	write_register(device->hal_data->register_base_addr,
 			VIDC_VENUS0_WRAPPER_VBIF_REQ_PRIORITY, 0x5555556, 0);
+	write_register(device->hal_data->register_base_addr,
+			VIDC_VENUS0_WRAPPER_VBIF_PRIORITY_LEVEL, 0, 0);
+}
+
+static int vidc_hal_sys_set_debug(struct hal_device *device, int debug)
+{
+	struct hfi_debug_config *hfi;
+	u8 packet[VIDC_IFACEQ_VAR_SMALL_PKT_SIZE];
+	struct hfi_cmd_sys_set_property_packet *pkt =
+		(struct hfi_cmd_sys_set_property_packet *) &packet;
+	pkt->size = sizeof(struct hfi_cmd_sys_set_property_packet) +
+		sizeof(struct hfi_debug_config) + sizeof(u32);
+	pkt->packet_type = HFI_CMD_SYS_SET_PROPERTY;
+	pkt->num_properties = 1;
+	pkt->rg_property_data[0] = HFI_PROPERTY_SYS_DEBUG_CONFIG;
+	hfi = (struct hfi_debug_config *) &pkt->rg_property_data[1];
+	hfi->debug_config = debug;
+	if (vidc_hal_iface_cmdq_write(device, pkt))
+		return -ENOTEMPTY;
+	return 0;
 }
 
 int vidc_hal_core_init(void *device, int domain)
@@ -760,7 +784,7 @@
 			"times: %d interrupt_status: %d",
 			(u32) device, ++device->reg_count, intr_status);
 	} else {
-		dprintk(VIDC_WARN, "SPURIOUS_INTR for device: 0x%x: "
+		dprintk(VIDC_INFO, "SPURIOUS_INTR for device: 0x%x: "
 			"times: %d interrupt_status: %d",
 			(u32) device, ++device->spur_count, intr_status);
 	}
@@ -1170,6 +1194,16 @@
 		pkt->size += sizeof(u32) * 2;
 		break;
 	}
+	case HAL_PARAM_VDEC_SYNC_FRAME_DECODE:
+	{
+		struct hfi_enable *hfi;
+		pkt->rg_property_data[0] =
+			HFI_PROPERTY_PARAM_VDEC_THUMBNAIL_MODE;
+		hfi = (struct hfi_enable *) &pkt->rg_property_data[1];
+		hfi->enable = ((struct hfi_enable *) pdata)->enable;
+		pkt->size += sizeof(u32) * 2;
+		break;
+	}
 	case HAL_PARAM_VENC_SYNC_FRAME_SEQUENCE_HEADER:
 	{
 		struct hfi_enable *hfi;
@@ -1437,23 +1471,13 @@
 				prop->multi_slice);
 			break;
 		}
+		hfi->slice_size = prop->slice_size;
 		pkt->size += sizeof(u32) + sizeof(struct
 					hfi_multi_slice_control);
 		break;
 	}
 	case HAL_CONFIG_VPE_DEINTERLACE:
 		break;
-	case HAL_SYS_DEBUG_CONFIG:
-	{
-		struct hfi_debug_config *hfi;
-		pkt->rg_property_data[0] = HFI_PROPERTY_SYS_DEBUG_CONFIG;
-		hfi = (struct hfi_debug_config *) &pkt->rg_property_data[1];
-		hfi->debug_config = ((struct hal_debug_config *)
-					pdata)->debug_config;
-		pkt->size = sizeof(struct hfi_cmd_sys_set_property_packet) +
-			sizeof(struct hfi_debug_config);
-		break;
-	}
 	/* FOLLOWING PROPERTIES ARE NOT IMPLEMENTED IN CORE YET */
 	case HAL_CONFIG_BUFFER_REQUIREMENTS:
 	case HAL_CONFIG_PRIORITY:
@@ -1624,7 +1648,7 @@
 	if (device) {
 		dev = device;
 	} else {
-		dprintk(VIDC_ERR, ":invalid device");
+		dprintk(VIDC_ERR, "invalid device");
 		return NULL;
 	}
 
@@ -1644,6 +1668,8 @@
 	pkt.session_codec = codec_type;
 	if (vidc_hal_iface_cmdq_write(dev, &pkt))
 		return NULL;
+	if (vidc_hal_sys_set_debug(dev, msm_fw_debug))
+		dprintk(VIDC_ERR, "Setting fw_debug msg ON failed");
 	return (void *) new_session;
 }
 
@@ -1657,7 +1683,7 @@
 	if (session_id) {
 		session = session_id;
 	} else {
-		dprintk(VIDC_ERR, ":invalid session");
+		dprintk(VIDC_ERR, "invalid session");
 		return -ENODEV;
 	}
 
@@ -1693,7 +1719,7 @@
 	struct hal_session *session;
 
 	if (!sess || !buffer_info) {
-		dprintk(VIDC_ERR, "Invalid Params in ");
+		dprintk(VIDC_ERR, "Invalid Params");
 		return -EINVAL;
 	} else {
 		session = sess;
@@ -1757,7 +1783,7 @@
 	struct hal_session *session;
 
 	if (!sess || !buffer_info) {
-		dprintk(VIDC_ERR, "Invalid Params in ");
+		dprintk(VIDC_ERR, "Invalid Params");
 		return -EINVAL;
 	} else {
 		session = sess;
@@ -1849,7 +1875,7 @@
 	struct hal_session *session;
 
 	if (!sess || !input_frame) {
-		dprintk(VIDC_ERR, "Invalid Params in ");
+		dprintk(VIDC_ERR, "Invalid Params");
 		return -EINVAL;
 	} else {
 		session = sess;
@@ -1907,7 +1933,7 @@
 	struct hal_session *session;
 
 	if (!sess || !output_frame) {
-		dprintk(VIDC_ERR, "Invalid Params in ");
+		dprintk(VIDC_ERR, "Invalid Params");
 		return -EINVAL;
 	} else {
 		session = sess;
@@ -1939,7 +1965,7 @@
 	struct hal_session *session;
 
 	if (!sess || !seq_hdr) {
-		dprintk(VIDC_ERR, "Invalid Params in ");
+		dprintk(VIDC_ERR, "Invalid Params");
 		return -EINVAL;
 	} else {
 		session = sess;
@@ -1966,7 +1992,7 @@
 	struct hal_session *session;
 
 	if (!sess || !seq_hdr) {
-		dprintk(VIDC_ERR, "Invalid Params in ");
+		dprintk(VIDC_ERR, "Invalid Params");
 		return -EINVAL;
 	} else {
 		session = sess;
@@ -1993,7 +2019,7 @@
 	if (sess) {
 		session = sess;
 	} else {
-		dprintk(VIDC_ERR, ":invalid session");
+		dprintk(VIDC_ERR, "invalid session");
 		return -ENODEV;
 	}
 
@@ -2016,7 +2042,7 @@
 	if (sess) {
 		session = sess;
 	} else {
-		dprintk(VIDC_ERR, ":invalid session");
+		dprintk(VIDC_ERR, "invalid session");
 		return -ENODEV;
 	}
 
diff --git a/drivers/media/video/msm_vidc/vidc_hal.h b/drivers/media/video/msm_vidc/vidc_hal.h
index c586172..0e70e30 100644
--- a/drivers/media/video/msm_vidc/vidc_hal.h
+++ b/drivers/media/video/msm_vidc/vidc_hal.h
@@ -224,6 +224,15 @@
 	(HFI_PROPERTY_PARAM_VDEC_OX_START + 0x008)
 #define HFI_PROPERTY_PARAM_VDEC_OUTPUT2_KEEP_ASPECT_RATIO\
 	(HFI_PROPERTY_PARAM_VDEC_OX_START + 0x009)
+#define HFI_PROPERTY_PARAM_VDEC_FRAME_RATE_EXTRADATA  \
+	(HFI_PROPERTY_PARAM_VDEC_OX_START + 0x00A)
+#define HFI_PROPERTY_PARAM_VDEC_PANSCAN_WNDW_EXTRADATA \
+	(HFI_PROPERTY_PARAM_VDEC_OX_START + 0x00B)
+#define HFI_PROPERTY_PARAM_VDEC_RECOVERY_POINT_SEI_EXTRADATA \
+	(HFI_PROPERTY_PARAM_VDEC_OX_START + 0x00C)
+#define HFI_PROPERTY_PARAM_VDEC_THUMBNAIL_MODE   \
+	(HFI_PROPERTY_PARAM_VDEC_OX_START + 0x00D)
+
 
 #define HFI_PROPERTY_CONFIG_VDEC_OX_START				\
 	(HFI_DOMAIN_BASE_VDEC + HFI_ARCH_OX_OFFSET + 0x0000)
diff --git a/drivers/media/video/msm_vidc/vidc_hal_api.h b/drivers/media/video/msm_vidc/vidc_hal_api.h
index 659cf7e..8aff5af 100644
--- a/drivers/media/video/msm_vidc/vidc_hal_api.h
+++ b/drivers/media/video/msm_vidc/vidc_hal_api.h
@@ -138,6 +138,7 @@
 	HAL_CONFIG_VENC_TIMESTAMP_SCALE,
 	HAL_PARAM_VENC_LOW_LATENCY,
 	HAL_PARAM_VENC_SYNC_FRAME_SEQUENCE_HEADER,
+	HAL_PARAM_VDEC_SYNC_FRAME_DECODE,
 };
 
 enum hal_domain {
@@ -210,7 +211,7 @@
 
 enum hal_mpeg4_profile {
 	HAL_MPEG4_PROFILE_SIMPLE           = 0x00000001,
-	HAL_MPEG4_PROFILE_SIMPLESCALABLE   = 0x00000002,
+	HAL_MPEG4_PROFILE_ADVANCEDSIMPLE   = 0x00000002,
 	HAL_MPEG4_PROFILE_CORE             = 0x00000004,
 	HAL_MPEG4_PROFILE_MAIN             = 0x00000008,
 	HAL_MPEG4_PROFILE_NBIT             = 0x00000010,
@@ -224,7 +225,7 @@
 	HAL_MPEG4_PROFILE_ADVANCEDCODING   = 0x00001000,
 	HAL_MPEG4_PROFILE_ADVANCEDCORE     = 0x00002000,
 	HAL_MPEG4_PROFILE_ADVANCEDSCALABLE = 0x00004000,
-	HAL_MPEG4_PROFILE_ADVANCEDSIMPLE   = 0x00008000,
+	HAL_MPEG4_PROFILE_SIMPLESCALABLE   = 0x00008000,
 	HAL_UNUSED_MPEG4_PROFILE = 0x10000000,
 };
 
@@ -358,7 +359,7 @@
 	HAL_BUFFER_EXTRADATA_OUTPUT2,
 	HAL_BUFFER_INTERNAL_SCRATCH,
 	HAL_BUFFER_INTERNAL_PERSIST,
-	HAL_UNUSED_BUFFER = 0x10000000,
+	HAL_BUFFER_MAX
 };
 
 struct hal_frame_rate {
@@ -812,6 +813,8 @@
 	PC_PREP_DONE,
 	SYS_IDLE,
 	SYS_DEBUG,
+	SYS_WATCHDOG_TIMEOUT,
+	SYS_ERROR,
 /* SESSION COMMANDS_DONE */
 	SESSION_LOAD_RESOURCE_DONE,
 	SESSION_INIT_DONE,
@@ -831,6 +834,7 @@
 	SESSION_RELEASE_BUFFER_DONE,
 	SESSION_RELEASE_RESOURCE_DONE,
 	SESSION_PROPERTY_INFO,
+	SESSION_ERROR,
 	RESPONSE_UNUSED = 0x10000000,
 };
 
@@ -940,7 +944,7 @@
 };
 
 struct buffer_requirements {
-	struct hal_buffer_requirements buffer[8];
+	struct hal_buffer_requirements buffer[HAL_BUFFER_MAX];
 };
 
 /* VIDC_HAL CORE API's */
diff --git a/drivers/media/video/msm_vidc/vidc_hal_interrupt_handler.c b/drivers/media/video/msm_vidc/vidc_hal_interrupt_handler.c
index 795024d..ba599ec 100644
--- a/drivers/media/video/msm_vidc/vidc_hal_interrupt_handler.c
+++ b/drivers/media/video/msm_vidc/vidc_hal_interrupt_handler.c
@@ -13,7 +13,9 @@
 
 #include <linux/slab.h>
 #include <linux/list.h>
+#include <linux/interrupt.h>
 #include "vidc_hal.h"
+#include "vidc_hal_io.h"
 #include "msm_vidc_debug.h"
 
 static enum vidc_status vidc_map_hal_err_status(int hfi_err)
@@ -136,7 +138,29 @@
 	cmd_done.data = &event_notify;
 	device->callback(VIDC_EVENT_CHANGE, &cmd_done);
 }
-
+static void hal_process_sys_watchdog_timeout(struct hal_device *device)
+{
+	struct msm_vidc_cb_cmd_done cmd_done;
+	disable_irq_nosync(device->hal_data->irq);
+	memset(&cmd_done, 0, sizeof(struct msm_vidc_cb_cmd_done));
+	cmd_done.device_id = device->device_id;
+	device->callback(SYS_WATCHDOG_TIMEOUT, &cmd_done);
+}
+static void hal_process_sys_error(struct hal_device *device)
+{
+	struct msm_vidc_cb_cmd_done cmd_done;
+	disable_irq_nosync(device->hal_data->irq);
+	memset(&cmd_done, 0, sizeof(struct msm_vidc_cb_cmd_done));
+	cmd_done.device_id = device->device_id;
+	device->callback(SYS_ERROR, &cmd_done);
+}
+static void hal_process_session_error(struct hal_device *device)
+{
+	struct msm_vidc_cb_cmd_done cmd_done;
+	memset(&cmd_done, 0, sizeof(struct msm_vidc_cb_cmd_done));
+	cmd_done.device_id = device->device_id;
+	device->callback(SESSION_ERROR, &cmd_done);
+}
 static void hal_process_event_notify(struct hal_device *device,
 	struct hfi_msg_event_notify_packet *pkt)
 {
@@ -151,9 +175,11 @@
 	switch (pkt->event_id) {
 	case HFI_EVENT_SYS_ERROR:
 		dprintk(VIDC_INFO, "HFI_EVENT_SYS_ERROR");
+		hal_process_sys_error(device);
 		break;
 	case HFI_EVENT_SESSION_ERROR:
 		dprintk(VIDC_INFO, "HFI_EVENT_SESSION_ERROR");
+		hal_process_session_error(device);
 		break;
 	case HFI_EVENT_SESSION_SEQUENCE_CHANGED:
 		dprintk(VIDC_INFO, "HFI_EVENT_SESSION_SEQUENCE_CHANGED");
@@ -167,7 +193,6 @@
 		break;
 	}
 }
-
 static void hal_process_sys_init_done(struct hal_device *device,
 		struct hfi_msg_sys_init_done_packet *pkt)
 {
@@ -530,7 +555,7 @@
 	struct hal_session *session;
 
 	if (!msg_hdr) {
-		dprintk(VIDC_ERR, "Invalid Params in ");
+		dprintk(VIDC_ERR, "Invalid Params");
 		return;
 	}
 
@@ -703,7 +728,6 @@
 	struct hfi_msg_sys_session_end_done_packet *pkt)
 {
 	struct msm_vidc_cb_cmd_done cmd_done;
-	struct list_head *curr, *next;
 	struct hal_session *sess_close;
 
 	dprintk(VIDC_DBG, "RECEIVED:SESSION_END_DONE");
@@ -715,13 +739,11 @@
 		return;
 	}
 
-	list_for_each_safe(curr, next, &device->sess_head) {
-		sess_close = list_entry(curr, struct hal_session, list);
-		dprintk(VIDC_INFO, "deleted the session: 0x%x",
-					   sess_close->session_id);
-		list_del(&sess_close->list);
-		kfree(sess_close);
-	}
+	sess_close = (struct hal_session *)pkt->session_id;
+	dprintk(VIDC_INFO, "deleted the session: 0x%x",
+			sess_close->session_id);
+	list_del(&sess_close->list);
+	kfree(sess_close);
 
 	memset(&cmd_done, 0, sizeof(struct msm_vidc_cb_cmd_done));
 	cmd_done.device_id = device->device_id;
@@ -767,6 +789,11 @@
 	}
 
 	dprintk(VIDC_INFO, "Received: 0x%x in ", msg_hdr->packet);
+	if ((device->intr_status & VIDC_WRAPPER_INTR_CLEAR_A2HWD_BMSK)) {
+		dprintk(VIDC_ERR, "Received: Watchdog timeout %s", __func__);
+		hal_process_sys_watchdog_timeout(device);
+	}
+
 	switch (msg_hdr->packet) {
 	case HFI_MSG_EVENT_NOTIFY:
 		hal_process_event_notify(device,
@@ -849,7 +876,12 @@
 	if (device) {
 		while (!vidc_hal_iface_msgq_read(device, packet)) {
 			hal_process_msg_packet(device,
-				(struct vidc_hal_msg_pkt_hdr *)	packet);
+				(struct vidc_hal_msg_pkt_hdr *) packet);
+		}
+		while (!vidc_hal_iface_dbgq_read(device, packet)) {
+			struct hfi_msg_sys_debug_packet *pkt =
+				(struct hfi_msg_sys_debug_packet *) packet;
+			dprintk(VIDC_FW, "FW-SAYS: %s", pkt->rg_msg_data);
 		}
 	} else {
 		dprintk(VIDC_ERR, "SPURIOUS_INTERRUPT");
diff --git a/drivers/media/video/msm_vidc/vidc_hal_io.h b/drivers/media/video/msm_vidc/vidc_hal_io.h
index b85e015..6845ac5 100644
--- a/drivers/media/video/msm_vidc/vidc_hal_io.h
+++ b/drivers/media/video/msm_vidc/vidc_hal_io.h
@@ -22,6 +22,12 @@
 
 #define VIDC_VBIF_BASE_OFFS			0x00080000
 #define VIDC_VBIF_VERSION			(VIDC_VBIF_BASE_OFFS + 0x00)
+#define VIDC_VENUS_VBIF_DDR_OUT_MAX_BURST		\
+			(VIDC_VBIF_BASE_OFFS + 0xD8)
+#define VIDC_VENUS_VBIF_OCMEM_OUT_MAX_BURST		\
+			(VIDC_VBIF_BASE_OFFS + 0xDC)
+#define VIDC_VENUS_VBIF_ROUND_ROBIN_QOS_ARB		\
+			(VIDC_VBIF_BASE_OFFS + 0x124)
 
 #define VIDC_CPU_BASE_OFFS			0x000C0000
 #define VIDC_CPU_CS_BASE_OFFS		(VIDC_CPU_BASE_OFFS + 0x00012000)
@@ -128,7 +134,5 @@
 	(VIDC_WRAPPER_BASE_OFFS + 0x20)
 #define VIDC_VENUS0_WRAPPER_VBIF_PRIORITY_LEVEL \
 	(VIDC_WRAPPER_BASE_OFFS + 0x24)
-#define VIDC_VENUS_VBIF_REQ_PRIORITY    (VIDC_WRAPPER_BASE_OFFS + 0x20)
-#define VIDC_VENUS_VBIF_PRIORITY_LEVEL  (VIDC_WRAPPER_BASE_OFFS + 0x24)
 
 #endif
diff --git a/drivers/media/video/msm_wfd/enc-mfc-subdev.c b/drivers/media/video/msm_wfd/enc-mfc-subdev.c
index 09a5e32..6db2ad1 100644
--- a/drivers/media/video/msm_wfd/enc-mfc-subdev.c
+++ b/drivers/media/video/msm_wfd/enc-mfc-subdev.c
@@ -1975,6 +1975,7 @@
 	unsigned long phy_addr;
 	int i = 0;
 	int heap_mask = 0;
+	u32 ion_flags = 0;
 	u32 len;
 	control.width = inst->width;
 	control.height = inst->height;
@@ -1988,7 +1989,8 @@
 		goto err;
 	}
 	heap_mask = ION_HEAP(ION_CP_MM_HEAP_ID);
-	heap_mask |= inst->secure ? ION_SECURE : ION_HEAP(ION_IOMMU_HEAP_ID);
+	heap_mask |= inst->secure ? 0 : ION_HEAP(ION_IOMMU_HEAP_ID);
+	ion_flags |= inst->secure ? ION_SECURE : 0;
 
 	if (vcd_get_ion_status()) {
 		for (i = 0; i < 4; ++i) {
@@ -1999,7 +2001,7 @@
 			ctrl->user_virtual_addr = (void *)i;
 			client_ctx->recon_buffer_ion_handle[i]
 				= ion_alloc(client_ctx->user_ion_client,
-			control.size, SZ_8K, heap_mask, 0);
+			control.size, SZ_8K, heap_mask, ion_flags);
 
 			ctrl->kernel_virtual_addr = ion_map_kernel(
 				client_ctx->user_ion_client,
diff --git a/drivers/media/video/msm_wfd/wfd-ioctl.c b/drivers/media/video/msm_wfd/wfd-ioctl.c
index 04b787a..371ae3c 100644
--- a/drivers/media/video/msm_wfd/wfd-ioctl.c
+++ b/drivers/media/video/msm_wfd/wfd-ioctl.c
@@ -91,6 +91,7 @@
 	u32 out_buf_size;
 	struct list_head input_mem_list;
 	struct wfd_stats stats;
+	struct completion stop_mdp_thread;
 };
 
 struct wfd_vid_buffer {
@@ -154,13 +155,15 @@
 	struct ion_handle *handle = NULL;
 	void *kvaddr = NULL;
 	unsigned int alloc_regions = 0;
+	unsigned int ion_flags = 0;
 	int rc = 0;
 
 	alloc_regions = ION_HEAP(ION_CP_MM_HEAP_ID);
-	alloc_regions |= secure ? ION_SECURE :
+	alloc_regions |= secure ? 0 :
 				ION_HEAP(ION_IOMMU_HEAP_ID);
+	ion_flags |= secure ? ION_SECURE : 0;
 	handle = ion_alloc(client,
-			mregion->size, SZ_4K, alloc_regions, 0);
+			mregion->size, SZ_4K, alloc_regions, ion_flags);
 
 	if (IS_ERR_OR_NULL(handle)) {
 		WFD_MSG_ERR("Failed to allocate input buffer\n");
@@ -522,7 +525,7 @@
 
 static int mdp_output_thread(void *data)
 {
-	int rc = 0;
+	int rc = 0, no_sig_wait = 0;
 	struct file *filp = (struct file *)data;
 	struct wfd_inst *inst = filp->private_data;
 	struct wfd_device *wfd_dev =
@@ -531,6 +534,14 @@
 	struct mem_region *mregion;
 	struct vsg_buf_info ibuf_vsg;
 	while (!kthread_should_stop()) {
+		if (rc) {
+			WFD_MSG_DBG("%s() error in output thread\n", __func__);
+			if (!no_sig_wait) {
+				wait_for_completion(&inst->stop_mdp_thread);
+				no_sig_wait = 1;
+			}
+			continue;
+		}
 		WFD_MSG_DBG("waiting for mdp output\n");
 		rc = v4l2_subdev_call(&wfd_dev->mdp_sdev,
 			core, ioctl, MDP_DQ_BUFFER, (void *)&obuf_mdp);
@@ -540,7 +551,7 @@
 				WFD_MSG_ERR("MDP reported err %d\n", rc);
 
 			WFD_MSG_ERR("Streamoff called\n");
-			break;
+			continue;
 		} else {
 			wfd_stats_update(&inst->stats,
 				WFD_STAT_EVENT_MDP_DEQUEUE);
@@ -550,7 +561,7 @@
 		if (!mregion) {
 			WFD_MSG_ERR("mdp cookie is null\n");
 			rc = -EINVAL;
-			break;
+			continue;
 		}
 
 		ibuf_vsg.mdp_buf_info = obuf_mdp;
@@ -565,7 +576,7 @@
 
 		if (rc) {
 			WFD_MSG_ERR("Failed to queue frame to vsg\n");
-			break;
+			continue;
 		} else {
 			wfd_stats_update(&inst->stats,
 				WFD_STAT_EVENT_VSG_QUEUE);
@@ -599,7 +610,7 @@
 		WFD_MSG_ERR("Failed to start vsg\n");
 		goto subdev_start_fail;
 	}
-
+	init_completion(&inst->stop_mdp_thread);
 	inst->mdp_task = kthread_run(mdp_output_thread, priv_data,
 				"mdp_output_thread");
 	if (IS_ERR(inst->mdp_task)) {
@@ -634,6 +645,7 @@
 	if (rc)
 		WFD_MSG_ERR("Failed to stop VSG\n");
 
+	complete(&inst->stop_mdp_thread);
 	kthread_stop(inst->mdp_task);
 	rc = v4l2_subdev_call(&wfd_dev->enc_sdev, core, ioctl,
 			ENCODE_FLUSH, (void *)inst->venc_inst);
diff --git a/drivers/media/video/msm_wfd/wfd-util.c b/drivers/media/video/msm_wfd/wfd-util.c
index 233668b0..5c00e5c 100644
--- a/drivers/media/video/msm_wfd/wfd-util.c
+++ b/drivers/media/video/msm_wfd/wfd-util.c
@@ -159,10 +159,10 @@
 	}
 	case WFD_STAT_EVENT_MDP_QUEUE:
 		stats->mdp_buf_count++;
-		stats->mdp_updates++;
 		break;
 	case WFD_STAT_EVENT_MDP_DEQUEUE:
 		stats->mdp_buf_count--;
+		stats->mdp_updates++;
 		break;
 	case WFD_STAT_EVENT_ENC_QUEUE: {
 		struct wfd_stats_encode_sample *sample = NULL;
diff --git a/drivers/media/video/vcap_v4l2.c b/drivers/media/video/vcap_v4l2.c
index 753171c..2b73b11 100644
--- a/drivers/media/video/vcap_v4l2.c
+++ b/drivers/media/video/vcap_v4l2.c
@@ -19,13 +19,23 @@
 #include <linux/videodev2.h>
 #include <linux/platform_device.h>
 #include <linux/memory_alloc.h>
+#include <linux/ctype.h>
+#include <linux/debugfs.h>
+#include <linux/regulator/consumer.h>
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/iommu.h>
 
 #include <mach/board.h>
 #include <mach/gpio.h>
 #include <mach/irqs.h>
+#include <mach/clk.h>
+#include <mach/msm_bus.h>
+#include <mach/msm_bus_board.h>
+#include <mach/iommu.h>
+#include <mach/iommu_domains.h>
 
 #include <media/videobuf2-msm-mem.h>
-
 #include <media/videobuf2-vmalloc.h>
 #include <media/v4l2-device.h>
 #include <media/v4l2-ioctl.h>
@@ -33,16 +43,9 @@
 #include <media/v4l2-fh.h>
 #include <media/v4l2-common.h>
 #include <media/v4l2-event.h>
-#include <linux/regulator/consumer.h>
-#include <mach/clk.h>
-#include <linux/clk.h>
-#include <linux/interrupt.h>
-#include <mach/msm_bus.h>
-#include <mach/msm_bus_board.h>
-#include <mach/iommu_domains.h>
-
 #include <media/vcap_v4l2.h>
 #include <media/vcap_fmt.h>
+
 #include "vcap_vc.h"
 #include "vcap_vp.h"
 
@@ -51,17 +54,35 @@
 
 static struct vcap_dev *vcap_ctrl;
 
-static unsigned debug;
+#ifdef CONFIG_DEBUG_FS
+static struct dentry *vcap_debugfs_base;
+static struct reg_range debug_reg_range[] = {
+	{
+		VCAP_REG_RANGE_1_MIN,
+		VCAP_REG_RANGE_1_MAX,
+	},
+	{
+		VCAP_REG_RANGE_2_MIN,
+		VCAP_REG_RANGE_2_MAX,
+	},
+	{
+		VCAP_REG_RANGE_3_MIN,
+		VCAP_REG_RANGE_3_MAX,
+	},
+	{
+		VCAP_REG_RANGE_4_MIN,
+		VCAP_REG_RANGE_4_MAX,
+	},
+	{
+		VCAP_REG_RANGE_5_MIN,
+		VCAP_REG_RANGE_5_MAX,
+	},
+};
+#endif
 
-#define dprintk(level, fmt, arg...)					\
-	do {								\
-		if (debug >= level)					\
-			printk(KERN_DEBUG "VCAP: " fmt, ## arg);	\
-	} while (0)
-
-int vcap_reg_powerup(struct vcap_dev *dev)
+static int vcap_reg_powerup(struct vcap_dev *dev)
 {
-	dev->fs_vcap = regulator_get(NULL, "fs_vcap");
+	dev->fs_vcap = regulator_get(dev->ddev, "fs_vcap");
 	if (IS_ERR(dev->fs_vcap)) {
 		pr_err("%s: Regulator FS_VCAP get failed %ld\n", __func__,
 			PTR_ERR(dev->fs_vcap));
@@ -75,7 +96,7 @@
 	return 0;
 }
 
-void vcap_reg_powerdown(struct vcap_dev *dev)
+static void vcap_reg_powerdown(struct vcap_dev *dev)
 {
 	if (dev->fs_vcap == NULL)
 		return;
@@ -85,13 +106,13 @@
 	return;
 }
 
-int config_gpios(int on, struct vcap_platform_data *pdata)
+static int vcap_config_gpios(int on, struct vcap_platform_data *pdata)
 {
 	int i, ret;
 	int num_gpios = pdata->num_gpios;
 	unsigned *gpios = pdata->gpios;
 
-	dprintk(4, "GPIO config start\n");
+	pr_debug("GPIO config start\n");
 	if (on) {
 		for (i = 0; i < num_gpios; i++) {
 			ret = gpio_request(gpios[i], "vcap:vc");
@@ -112,7 +133,7 @@
 		for (i = 0; i < num_gpios; i++)
 			gpio_free(gpios[i]);
 	}
-	dprintk(4, "GPIO config exit\n");
+	pr_debug("GPIO config exit\n");
 	return 0;
 gpio_failed:
 	for (i--; i >= 0; i--)
@@ -120,7 +141,7 @@
 	return -EINVAL;
 }
 
-int vcap_clk_powerup(struct vcap_dev *dev, struct device *ddev,
+static int vcap_clk_powerup(struct vcap_dev *dev, struct device *ddev,
 		unsigned long rate)
 {
 	int ret = 0;
@@ -151,6 +172,7 @@
 		pr_err("%s: Failed core set_rate %d\n", __func__, ret);
 		goto fail_vcap_clk;
 	}
+	dev->dbg_p.clk_rate = (uint32_t) rate;
 
 	dev->vcap_npl_clk = clk_get(ddev, "vcap_npl_clk");
 	if (IS_ERR(dev->vcap_npl_clk)) {
@@ -198,6 +220,7 @@
 	dev->vcap_npl_clk = NULL;
 
 fail_vcap_clk:
+	dev->dbg_p.clk_rate = 0;
 	clk_disable(dev->vcap_clk);
 fail_vcap_clk_unprep:
 	clk_unprepare(dev->vcap_clk);
@@ -206,7 +229,7 @@
 	return -EINVAL;
 }
 
-void vcap_clk_powerdown(struct vcap_dev *dev)
+static void vcap_clk_powerdown(struct vcap_dev *dev)
 {
 	if (dev->vcap_p_clk != NULL) {
 		clk_disable(dev->vcap_p_clk);
@@ -228,9 +251,11 @@
 		clk_put(dev->vcap_clk);
 		dev->vcap_clk = NULL;
 	}
+
+	dev->dbg_p.clk_rate = 0;
 }
 
-int vcap_get_bus_client_handle(struct vcap_dev *dev)
+static int vcap_get_bus_client_handle(struct vcap_dev *dev)
 {
 	struct msm_bus_scale_pdata *vcap_axi_client_pdata =
 			dev->vcap_pdata->bus_client_pdata;
@@ -240,10 +265,11 @@
 	return 0;
 }
 
-int vcap_enable(struct vcap_dev *dev, struct device *ddev,
+static int vcap_enable(struct vcap_dev *dev, struct device *ddev,
 		unsigned long rate)
 {
 	int rc;
+	pr_debug("Enter %s", __func__);
 
 	rc = vcap_reg_powerup(dev);
 	if (rc < 0)
@@ -254,13 +280,24 @@
 	rc = vcap_get_bus_client_handle(dev);
 	if (rc < 0)
 		goto bus_r_failed;
-	rc = config_gpios(1, dev->vcap_pdata);
+	rc = vcap_config_gpios(1, dev->vcap_pdata);
 	if (rc < 0)
 		goto gpio_failed;
+	rc = iommu_attach_device(dev->iommu_vcap_domain, dev->vc_iommu_ctx);
+	if (rc < 0)
+		goto vc_iommu_attach_failed;
+	rc = iommu_attach_device(dev->iommu_vcap_domain, dev->vp_iommu_ctx);
+	if (rc < 0)
+		goto vp_iommu_attach_failed;
 	writel_relaxed(0x00030003, VCAP_OFFSET(0xD78));
 	writel_relaxed(0x00030003, VCAP_OFFSET(0xD7C));
+	pr_debug("Success Exit %s", __func__);
 	return 0;
 
+vp_iommu_attach_failed:
+	iommu_detach_device(dev->iommu_vcap_domain, dev->vc_iommu_ctx);
+vc_iommu_attach_failed:
+	vcap_config_gpios(0, dev->vcap_pdata);
 gpio_failed:
 	msm_bus_scale_unregister_client(dev->bus_client_handle);
 	dev->bus_client_handle = 0;
@@ -272,17 +309,38 @@
 	return rc;
 }
 
-int vcap_disable(struct vcap_dev *dev)
+static int vcap_disable(struct vcap_dev *dev)
 {
-	config_gpios(0, dev->vcap_pdata);
+	pr_debug("Enter %s", __func__);
+	iommu_detach_device(dev->iommu_vcap_domain, dev->vp_iommu_ctx);
+	iommu_detach_device(dev->iommu_vcap_domain, dev->vc_iommu_ctx);
+
+	vcap_config_gpios(0, dev->vcap_pdata);
 
 	msm_bus_scale_unregister_client(dev->bus_client_handle);
 	dev->bus_client_handle = 0;
+	dev->dbg_p.bw_request = 0;
 	vcap_clk_powerdown(dev);
 	vcap_reg_powerdown(dev);
 	return 0;
 }
 
+static int vcap_register_domain(void)
+{
+	struct msm_iova_partition vcap_partition = {
+		.start = 0,
+		.size = SZ_2G,
+	};
+	struct msm_iova_layout vcap_layout = {
+		.partitions = &vcap_partition,
+		.npartitions = 1,
+		.client_name = "vcap",
+		.domain_flags = 0,
+	};
+
+	return msm_register_domain(&vcap_layout);
+}
+
 enum vcap_op_mode determine_mode(struct vcap_client_data *cd)
 {
 	if (cd->set_cap == 1 && cd->set_vp_o == 0 &&
@@ -311,34 +369,34 @@
 	struct vb2_buffer *vb;
 
 	if (q->fileio) {
-		dprintk(1, "%s: file io in progress\n", __func__);
+		pr_debug("%s: file io in progress\n", __func__);
 		return -EBUSY;
 	}
 
 	if (b->type != q->type) {
-		dprintk(1, "%s: invalid buffer type\n", __func__);
+		pr_debug("%s: invalid buffer type\n", __func__);
 		return -EINVAL;
 	}
 
 	if (b->index >= q->num_buffers) {
-		dprintk(1, "%s: buffer index out of range\n", __func__);
+		pr_debug("%s: buffer index out of range\n", __func__);
 		return -EINVAL;
 	}
 
 	vb = q->bufs[b->index];
 	if (NULL == vb) {
-		dprintk(1, "%s: buffer is NULL\n", __func__);
+		pr_debug("%s: buffer is NULL\n", __func__);
 		return -EINVAL;
 	}
 
 	if (b->memory != q->memory) {
-		dprintk(1, "%s: invalid memory type\n", __func__);
+		pr_debug("%s: invalid memory type\n", __func__);
 		return -EINVAL;
 	}
 
 	if (vb->state != VB2_BUF_STATE_DEQUEUED &&
 			vb->state != VB2_BUF_STATE_PREPARED) {
-		dprintk(1, "%s: buffer already in use\n", __func__);
+		pr_err("%s: buffer already in use\n", __func__);
 		return -EINVAL;
 	}
 
@@ -361,17 +419,17 @@
 	unsigned long flags;
 
 	if (q->fileio) {
-		dprintk(1, "%s: file io in progress\n", __func__);
+		pr_debug("%s: file io in progress\n", __func__);
 		return -EBUSY;
 	}
 
 	if (b->type != q->type) {
-		dprintk(1, "%s: invalid buffer type\n", __func__);
+		pr_debug("%s: invalid buffer type\n", __func__);
 		return -EINVAL;
 	}
 
 	if (!q->streaming) {
-		dprintk(1, "Streaming off, will not wait for buffers\n");
+		pr_debug("Streaming off, will not wait for buffers\n");
 		return -EINVAL;
 	}
 
@@ -384,13 +442,13 @@
 
 		switch (vb->state) {
 		case VB2_BUF_STATE_DONE:
-			dprintk(3, "%s: Returning done buffer\n", __func__);
+			pr_debug("%s: Returning done buffer\n", __func__);
 			break;
 		case VB2_BUF_STATE_ERROR:
-			dprintk(3, "%s: Ret done buf with err\n", __func__);
+			pr_debug("%s: Ret done buf with err\n", __func__);
 			break;
 		default:
-			dprintk(1, "%s: Invalid buffer state\n", __func__);
+			pr_debug("%s: Invalid buffer state\n", __func__);
 			return -EINVAL;
 		}
 
@@ -402,7 +460,7 @@
 		return 0;
 	}
 
-	dprintk(1, "No buffers to dequeue\n");
+	pr_debug("%s: No buffers to dequeue\n", __func__);
 	return -EAGAIN;
 }
 
@@ -415,41 +473,42 @@
 	int rc;
 
 	if (q->fileio) {
-		dprintk(1, "%s: file io in progress\n", __func__);
+		pr_debug("%s: file io in progress\n", __func__);
 		return -EBUSY;
 	}
 
 	if (b->type != q->type) {
-		dprintk(1, "%s: invalid buffer type\n", __func__);
+		pr_debug("%s: invalid buffer type\n", __func__);
 		return -EINVAL;
 	}
 
 	if (b->index >= q->num_buffers) {
-		dprintk(1, "%s: buffer index out of range\n", __func__);
+		pr_debug("%s: buffer index out of range\n", __func__);
 		return -EINVAL;
 	}
 
 	vb = q->bufs[b->index];
 	if (NULL == vb) {
-		dprintk(1, "%s: buffer is NULL\n", __func__);
+		pr_debug("%s: buffer is NULL\n", __func__);
 		return -EINVAL;
 	}
 
 	if (vb->state != VB2_BUF_STATE_DEQUEUED) {
-		dprintk(1, "%s: buffer already in use\n", __func__);
+		pr_debug("%s: buffer already in use\n", __func__);
 		return -EINVAL;
 	}
 
 	buf = container_of(vb, struct vcap_buffer, vb);
 
 	buf->ion_handle = ion_import_dma_buf(dev->ion_client, b->m.userptr);
-	if (IS_ERR((void *)buf->ion_handle)) {
+	if (IS_ERR_OR_NULL((void *)buf->ion_handle)) {
 		pr_err("%s: Could not alloc memory\n", __func__);
 		buf->ion_handle = NULL;
 		return -ENOMEM;
 	}
-	rc = ion_phys(dev->ion_client, buf->ion_handle,
-			&buf->paddr, (size_t *)&len);
+	rc = ion_map_iommu(dev->ion_client, buf->ion_handle,
+		dev->domain_num, 0, SZ_4K, 0, &buf->paddr, &len,
+		0, 0);
 	if (rc < 0) {
 		pr_err("%s: Could not get phys addr\n", __func__);
 		ion_free(dev->ion_client, buf->ion_handle);
@@ -462,22 +521,32 @@
 	return 0;
 }
 
-void free_ion_handle_work(struct vcap_dev *dev, struct vb2_buffer *vb)
+void free_ion_handle_work(struct vcap_client_data *c_data,
+	struct vb2_buffer *vb)
 {
 	struct vcap_buffer *buf;
+	struct vcap_dev *dev = c_data->dev;
+	struct ion_handle *handle;
+	unsigned long flags = 0;
 
 	buf = container_of(vb, struct vcap_buffer, vb);
-	if (buf->ion_handle == NULL) {
-		dprintk(1, "%s: no ION handle to free\n", __func__);
+
+	spin_lock_irqsave(&c_data->cap_slock, flags);
+	handle = buf->ion_handle;
+	buf->ion_handle = NULL;
+	spin_unlock_irqrestore(&c_data->cap_slock, flags);
+
+	if (handle == NULL) {
+		pr_debug("%s: no ION handle to free\n", __func__);
 		return;
 	}
 	buf->paddr = 0;
-	ion_free(dev->ion_client, buf->ion_handle);
-	buf->ion_handle = NULL;
+	ion_unmap_iommu(dev->ion_client, handle, dev->domain_num, 0);
+	ion_free(dev->ion_client, handle);
 	return;
 }
 
-int free_ion_handle(struct vcap_dev *dev, struct vb2_queue *q,
+int free_ion_handle(struct vcap_client_data *c_data, struct vb2_queue *q,
 					 struct v4l2_buffer *b)
 {
 	struct vb2_buffer *vb;
@@ -495,7 +564,7 @@
 	if (NULL == vb)
 		return -EINVAL;
 
-	free_ion_handle_work(dev, vb);
+	free_ion_handle_work(c_data, vb);
 	return 0;
 }
 
@@ -546,7 +615,7 @@
 static int capture_start_streaming(struct vb2_queue *vq, unsigned int count)
 {
 	struct vcap_client_data *c_data = vb2_get_drv_priv(vq);
-	dprintk(2, "VC start streaming\n");
+	pr_debug("VC start streaming\n");
 	return vc_start_capture(c_data);
 }
 
@@ -567,7 +636,7 @@
 
 	/* clean ion handles */
 	list_for_each_entry(vb, &vq->queued_list, queued_entry)
-		free_ion_handle_work(c_data->dev, vb);
+		free_ion_handle_work(c_data, vb);
 	return 0;
 }
 
@@ -643,7 +712,7 @@
 
 static int vp_in_start_streaming(struct vb2_queue *vq, unsigned int count)
 {
-	dprintk(2, "VP IN start streaming\n");
+	pr_debug("VP IN start streaming\n");
 	return 0;
 }
 
@@ -652,7 +721,8 @@
 	struct vcap_client_data *c_data = vb2_get_drv_priv(vq);
 	struct vb2_buffer *vb;
 
-	dprintk(2, "VP stop streaming\n");
+	pr_debug("VP IN stop streaming\n");
+	vp_stop_capture(c_data);
 
 	while (!list_empty(&c_data->vp_action.in_active)) {
 		struct vcap_buffer *buf;
@@ -664,7 +734,7 @@
 
 	/* clean ion handles */
 	list_for_each_entry(vb, &vq->queued_list, queued_entry)
-		free_ion_handle_work(c_data->dev, vb);
+		free_ion_handle_work(c_data, vb);
 	return 0;
 }
 
@@ -749,7 +819,7 @@
 	struct vcap_client_data *c_data = vb2_get_drv_priv(vq);
 	struct vb2_buffer *vb;
 
-	dprintk(2, "VP out q stop streaming\n");
+	pr_debug("VP OUT q stop streaming\n");
 	vp_stop_capture(c_data);
 
 	while (!list_empty(&c_data->vp_action.out_active)) {
@@ -762,7 +832,7 @@
 
 	/* clean ion handles */
 	list_for_each_entry(vb, &vq->queued_list, queued_entry)
-		free_ion_handle_work(c_data->dev, vb);
+		free_ion_handle_work(c_data, vb);
 	return 0;
 }
 
@@ -838,6 +908,7 @@
 
 		size = (c_data->vc_format.hactive_end -
 			c_data->vc_format.hactive_start);
+		size = VCAP_STRIDE_CALC(size);
 
 		if (c_data->vc_format.color_space)
 			size *= 3;
@@ -890,7 +961,7 @@
 	struct vcap_dev *dev = c_data->dev;
 	int rc;
 
-	dprintk(3, "In Req Buf %08x\n", (unsigned int)rb->type);
+	pr_debug("VCAP: In Req Buf %08x\n", (unsigned int)rb->type);
 	c_data->op_mode = determine_mode(c_data);
 	if (c_data->op_mode == UNKNOWN_VCAP_OP) {
 		pr_err("VCAP Error: %s: VCAP in unknown mode\n", __func__);
@@ -968,25 +1039,25 @@
 	struct vb2_queue *q;
 	int rc;
 
-	dprintk(3, "In Q Buf %08x\n", (unsigned int)p->type);
+	pr_debug("VCAP In Q Buf %08x\n", (unsigned int)p->type);
 	switch (p->type) {
 	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
 		if (c_data->op_mode == VC_AND_VP_VCAP_OP) {
 			/* If buffer in vp_in_q it will be coming back */
 			q = &c_data->vp_in_vidq;
 			if (p->index >= q->num_buffers) {
-				dprintk(1, "qbuf: buffer index out of range\n");
+				pr_debug("VCAP qbuf: buffer index out of range\n");
 				return -EINVAL;
 			}
 
 			vb = q->bufs[p->index];
 			if (NULL == vb) {
-				dprintk(1, "qbuf: buffer is NULL\n");
+				pr_debug("VCAP qbuf: buffer is NULL\n");
 				return -EINVAL;
 			}
 
 			if (vb->state != VB2_BUF_STATE_DEQUEUED) {
-				dprintk(1, "qbuf: buffer already in use\n");
+				pr_debug("VCAP qbuf: buffer already in use\n");
 				return -EINVAL;
 			}
 			rc = get_phys_addr(c_data->dev, &c_data->vc_vidq, p);
@@ -994,7 +1065,7 @@
 				return rc;
 			rc = vcvp_qbuf(&c_data->vc_vidq, p);
 			if (rc < 0)
-				free_ion_handle(c_data->dev,
+				free_ion_handle(c_data,
 					&c_data->vc_vidq, p);
 			return rc;
 		}
@@ -1003,7 +1074,7 @@
 			return rc;
 		rc = vb2_qbuf(&c_data->vc_vidq, p);
 		if (rc < 0)
-			free_ion_handle(c_data->dev, &c_data->vc_vidq, p);
+			free_ion_handle(c_data, &c_data->vc_vidq, p);
 		return rc;
 	case V4L2_BUF_TYPE_INTERLACED_IN_DECODER:
 		if (c_data->op_mode == VC_AND_VP_VCAP_OP)
@@ -1013,7 +1084,7 @@
 			return rc;
 		rc = vb2_qbuf(&c_data->vp_in_vidq, p);
 		if (rc < 0)
-			free_ion_handle(c_data->dev, &c_data->vp_in_vidq, p);
+			free_ion_handle(c_data, &c_data->vp_in_vidq, p);
 		return rc;
 	case V4L2_BUF_TYPE_VIDEO_OUTPUT:
 		rc = get_phys_addr(c_data->dev, &c_data->vp_out_vidq, p);
@@ -1021,7 +1092,7 @@
 			return rc;
 		rc = vb2_qbuf(&c_data->vp_out_vidq, p);
 		if (rc < 0)
-			free_ion_handle(c_data->dev, &c_data->vp_out_vidq, p);
+			free_ion_handle(c_data, &c_data->vp_out_vidq, p);
 		return rc;
 	default:
 		pr_err("VCAP Error: %s: Unknown buffer type\n", __func__);
@@ -1035,7 +1106,10 @@
 	struct vcap_client_data *c_data = to_client_data(file->private_data);
 	int rc;
 
-	dprintk(3, "In DQ Buf %08x\n", (unsigned int)p->type);
+	if (c_data->streaming == 0)
+		return -EPERM;
+
+	pr_debug("VCAP In DQ Buf %08x\n", (unsigned int)p->type);
 	switch (p->type) {
 	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
 		if (c_data->op_mode == VC_AND_VP_VCAP_OP)
@@ -1043,7 +1117,7 @@
 		rc = vb2_dqbuf(&c_data->vc_vidq, p, file->f_flags & O_NONBLOCK);
 		if (rc < 0)
 			return rc;
-		return free_ion_handle(c_data->dev, &c_data->vc_vidq, p);
+		return free_ion_handle(c_data, &c_data->vc_vidq, p);
 	case V4L2_BUF_TYPE_INTERLACED_IN_DECODER:
 		if (c_data->op_mode == VC_AND_VP_VCAP_OP)
 			return -EINVAL;
@@ -1051,13 +1125,13 @@
 				O_NONBLOCK);
 		if (rc < 0)
 			return rc;
-		return free_ion_handle(c_data->dev, &c_data->vp_in_vidq, p);
+		return free_ion_handle(c_data, &c_data->vp_in_vidq, p);
 	case V4L2_BUF_TYPE_VIDEO_OUTPUT:
 		rc = vb2_dqbuf(&c_data->vp_out_vidq, p, file->f_flags &
 				O_NONBLOCK);
 		if (rc < 0)
 			return rc;
-		return free_ion_handle(c_data->dev, &c_data->vp_out_vidq, p);
+		return free_ion_handle(c_data, &c_data->vp_out_vidq, p);
 	default:
 		pr_err("VCAP Error: %s: Unknown buffer type", __func__);
 		return -EINVAL;
@@ -1072,18 +1146,18 @@
 int streamon_validate_q(struct vb2_queue *q)
 {
 	if (q->fileio) {
-		dprintk(1, "streamon: file io in progress\n");
+		pr_debug("%s: file io in progress\n", __func__);
 		return -EBUSY;
 	}
 
 	if (q->streaming) {
-		dprintk(1, "streamon: already streaming\n");
+		pr_debug("%s: already streaming\n", __func__);
 		return -EBUSY;
 	}
 
 	if (V4L2_TYPE_IS_OUTPUT(q->type)) {
 		if (list_empty(&q->queued_list)) {
-			dprintk(1, "streamon: no output buffers queued\n");
+			pr_debug("%s: no output buffers queued\n", __func__);
 			return -EINVAL;
 		}
 	}
@@ -1103,10 +1177,11 @@
 		idx++;
 	} while (idx < length);
 	if (idx == length) {
-		pr_err("VCAP: Defaulting to highest BW request\n");
+		pr_info("VCAP: Defaulting to highest BW request\n");
 		idx--;
 	}
 	msm_bus_scale_client_update_request(dev->bus_client_handle, idx);
+	dev->dbg_p.bw_request = bus_vectors[idx].vectors[0].ab;
 	return 0;
 }
 
@@ -1118,7 +1193,7 @@
 	unsigned long rate;
 	long rate_rc;
 
-	dprintk(3, "In Stream ON\n");
+	pr_debug("VCAP: In Stream ON\n");
 	if (determine_mode(c_data) != c_data->op_mode) {
 		pr_err("VCAP Error: %s: s_fmt called after req_buf", __func__);
 		return -ENOTRECOVERABLE;
@@ -1160,6 +1235,8 @@
 		if (rc < 0)
 			goto free_res;
 
+		dev->dbg_p.clk_rate = (uint32_t) rate;
+
 		rate = (c_data->vc_format.hactive_end -
 			c_data->vc_format.hactive_start);
 
@@ -1204,6 +1281,8 @@
 		}
 		rate = (unsigned long)rate_rc;
 		rc = clk_set_rate(dev->vcap_clk, rate);
+
+		dev->dbg_p.clk_rate = (uint32_t) rate;
 		if (rc < 0)
 			goto free_res;
 
@@ -1279,6 +1358,8 @@
 		if (rc < 0)
 			goto free_res;
 
+		dev->dbg_p.clk_rate = (uint32_t) rate;
+
 		rate = (c_data->vc_format.hactive_end -
 			c_data->vc_format.hactive_start);
 
@@ -1376,12 +1457,12 @@
 int streamoff_validate_q(struct vb2_queue *q)
 {
 	if (q->fileio) {
-		dprintk(1, "streamoff: file io in progress\n");
+		pr_debug("%s: file io in progress\n", __func__);
 		return -EBUSY;
 	}
 
 	if (!q->streaming) {
-		dprintk(1, "streamoff: not streaming\n");
+		pr_debug("%s: not streaming\n", __func__);
 		return -EINVAL;
 	}
 	return 0;
@@ -1791,7 +1872,10 @@
 	struct vb2_queue *q;
 	unsigned int mask = 0;
 
-	dprintk(1, "Enter slect/poll\n");
+	if (c_data->streaming == 0)
+		return 0;
+
+	pr_debug("%s: Enter slect/poll\n", __func__);
 
 	switch (c_data->op_mode) {
 	case VC_VCAP_OP:
@@ -1866,13 +1950,276 @@
 	return vc_handler(vcap_ctrl);
 }
 
+#ifdef CONFIG_DEBUG_FS
+/* Query VCAP resource usage */
+static ssize_t read_dump_info(struct file *file, char __user *user_buf,
+	size_t len, loff_t *ppos)
+{
+	struct vcap_dev *dev = file->private_data;
+	char str_buf[512];
+	size_t tot_size = 0, size;
+
+	if (dev->vc_client) {
+		size = scnprintf(str_buf + tot_size, sizeof(str_buf) - tot_size,
+			"VCAP: VC\n");
+		tot_size += size;
+		size = scnprintf(str_buf + tot_size, sizeof(str_buf) - tot_size,
+			"vc_resourse = %d\n", dev->vc_resource);
+		tot_size += size;
+		size = scnprintf(str_buf + tot_size, sizeof(str_buf) - tot_size,
+			"vc_enabled = %d\n", atomic_read(&dev->vc_enabled));
+		tot_size += size;
+		size = scnprintf(str_buf + tot_size, sizeof(str_buf) - tot_size,
+			"vc_client id = %p\n", dev->vc_client);
+		tot_size += size;
+		size = scnprintf(str_buf + tot_size, sizeof(str_buf) - tot_size,
+			"vc_queue_count = %d\n",
+			atomic_read(&dev->vc_client->vc_vidq.queued_count));
+		tot_size += size;
+		size = scnprintf(str_buf + tot_size, sizeof(str_buf) - tot_size,
+			"vc_total_buffers = %d\n",
+			dev->vc_client->vc_action.tot_buf);
+		tot_size += size;
+	} else {
+		size = scnprintf(str_buf + tot_size, sizeof(str_buf) - tot_size,
+				"VCAP: VC not in use\n");
+		tot_size += size;
+	}
+	if (dev->vp_client) {
+		size = scnprintf(str_buf + tot_size, sizeof(str_buf) - tot_size,
+			"VCAP: VP\n");
+		tot_size += size;
+		size = scnprintf(str_buf + tot_size, sizeof(str_buf) - tot_size,
+			"vp_resourse = %d\n", dev->vp_resource);
+		tot_size += size;
+		size = scnprintf(str_buf + tot_size, sizeof(str_buf) - tot_size,
+			"vp_enabled = %d\n", atomic_read(&dev->vp_enabled));
+		tot_size += size;
+		size = scnprintf(str_buf + tot_size, sizeof(str_buf) - tot_size,
+			"vp_client id = %p\n", dev->vp_client);
+		tot_size += size;
+		size = scnprintf(str_buf + tot_size, sizeof(str_buf) - tot_size,
+			"vp_in_queue_count = %d\n",
+			atomic_read(
+				&dev->vp_client->vp_in_vidq.queued_count));
+		tot_size += size;
+		size = scnprintf(str_buf + tot_size, sizeof(str_buf) - tot_size,
+			"vp_out_queue_count = %d\n",
+			atomic_read(
+				&dev->vp_client->vp_out_vidq.queued_count));
+		tot_size += size;
+	} else {
+		size = scnprintf(str_buf + tot_size, sizeof(str_buf) - tot_size,
+			"VCAP: VP not in use\n");
+		tot_size += size;
+	}
+
+	return simple_read_from_buffer(user_buf, len, ppos, str_buf, tot_size);
+}
+
+static const struct file_operations dump_info_fops = {
+	.read =		read_dump_info,
+	.open =		simple_open,
+	.llseek =	default_llseek,
+};
+
+static int vcap_debug_clk_rate_get(void *data, u64 *val)
+{
+	struct vcap_dev *dev = data;
+	*val = (u64)dev->dbg_p.clk_rate;
+	return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(clk_rate_fops, vcap_debug_clk_rate_get,
+	NULL, "%llu\n");
+
+static int vcap_debug_bw_req_get(void *data, u64 *val)
+{
+	struct vcap_dev *dev = data;
+	*val = (u64)dev->dbg_p.bw_request;
+	return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(bw_req_fops, vcap_debug_bw_req_get,
+	NULL, "%llu\n");
+
+static int vcap_debug_drop_frames_get(void *data, u64 *val)
+{
+	struct vcap_dev *dev = data;
+	struct timeval tv;
+	int drop_count;
+
+	if (!dev->vc_resource)
+		return -EPERM;
+	drop_count = atomic_read(&dev->dbg_p.vc_drop_count);
+	atomic_set(&dev->dbg_p.vc_drop_count, 0);
+
+	do_gettimeofday(&tv);
+	dev->dbg_p.vc_timestamp = (uint32_t) (tv.tv_sec * VCAP_USEC +
+		tv.tv_usec);
+
+	*val = (u64)drop_count;
+	return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(tot_frame_drop_fops, vcap_debug_drop_frames_get,
+	NULL, "%llu\n");
+
+static int vcap_debug_drop_fps_get(void *data, u64 *val)
+{
+	struct vcap_dev *dev = data;
+	struct timeval tv;
+	int drop_count;
+	uint32_t new_ts;
+
+	if (!dev->vc_resource)
+		return -EPERM;
+	drop_count = atomic_read(&dev->dbg_p.vc_drop_count);
+	atomic_set(&dev->dbg_p.vc_drop_count, 0);
+
+	do_gettimeofday(&tv);
+	new_ts = (uint32_t) (tv.tv_sec * VCAP_USEC +
+		tv.tv_usec);
+
+	if ((new_ts - dev->dbg_p.vc_timestamp) / VCAP_USEC &&
+				new_ts > dev->dbg_p.vc_timestamp)
+		drop_count /= ((new_ts - dev->dbg_p.vc_timestamp) / VCAP_USEC);
+	else
+		drop_count = 0;
+
+	dev->dbg_p.vc_timestamp = new_ts;
+	*val = (u64)drop_count;
+	return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(drop_fps_fops, vcap_debug_drop_fps_get,
+	NULL, "%llu\n");
+
+static int vcap_debug_vp_lat_get(void *data, u64 *val)
+{
+	struct vcap_dev *dev = data;
+
+	if (!dev->vp_resource)
+		return -EPERM;
+	*val = (u64)dev->dbg_p.vp_ewma;
+	return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(vp_lat_fops, vcap_debug_vp_lat_get,
+	NULL, "%llu\n");
+
+/* Read/Write to VCAP Registers */
+static int vcap_debug_reg_set(void *data, u64 val)
+{
+	struct vcap_dev *dev = data;
+	int i;
+	for (i = 0; i < ARRAY_SIZE(debug_reg_range); i++) {
+		if (val >= debug_reg_range[i].min_val && val <=
+				debug_reg_range[i].max_val)
+			break;
+	}
+	if (i == ARRAY_SIZE(debug_reg_range))
+		return -EINVAL;
+	dev->dbg_p.reg_addr = (uint32_t) val;
+	return 0;
+}
+
+static int vcap_debug_reg_get(void *data, u64 *val)
+{
+	struct vcap_dev *dev = data;
+	*val = (u64)dev->dbg_p.reg_addr;
+	return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(vcap_reg_fops, vcap_debug_reg_get,
+	vcap_debug_reg_set, "0x%08llx\n")
+
+static int vcap_debug_reg_rdwr_set(void *data, u64 val)
+{
+	struct vcap_dev *dev = data;
+	u32 reg_val = (u32) val;
+
+	writel_iowmb(reg_val, VCAP_OFFSET(dev->dbg_p.reg_addr));
+	return 0;
+}
+
+static int vcap_debug_reg_rdwr_get(void *data, u64 *val)
+{
+	struct vcap_dev *dev = data;
+	*val = (u64)readl_relaxed(VCAP_OFFSET(dev->dbg_p.reg_addr));
+	return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(vcap_reg_rdwr_fops, vcap_debug_reg_rdwr_get,
+	vcap_debug_reg_rdwr_set, "0x%08llx\n");
+
+static int vcap_debugfs_init(struct vcap_dev *dev)
+{
+	vcap_debugfs_base = debugfs_create_dir("vcap", NULL);
+	if (!vcap_debugfs_base)
+		return -ENOMEM;
+
+	if (!debugfs_create_file("dump_info", S_IRUGO,
+			vcap_debugfs_base, dev, &dump_info_fops))
+		goto error;
+
+	if (!debugfs_create_file("vcap_core_clk_rate", S_IRUGO,
+			vcap_debugfs_base, dev, &clk_rate_fops))
+		goto error;
+
+	if (!debugfs_create_file("vcap_bw_req", S_IRUGO,
+			vcap_debugfs_base, dev, &bw_req_fops))
+		goto error;
+
+	if (!debugfs_create_file("vc_total_frames_drop", S_IRUGO,
+			vcap_debugfs_base, dev, &tot_frame_drop_fops))
+		goto error;
+
+	if (!debugfs_create_file("vc_drop_fps", S_IRUGO,
+			vcap_debugfs_base, dev, &drop_fps_fops))
+		goto error;
+
+	if (!debugfs_create_file("vp_avg_completion_t", S_IRUGO,
+			vcap_debugfs_base, dev, &vp_lat_fops))
+		goto error;
+
+	if (!debugfs_create_file("vcap_reg_addr", S_IRUGO | S_IWUSR,
+			vcap_debugfs_base, dev, &vcap_reg_fops))
+		goto error;
+
+	if (!debugfs_create_file("vcap_reg_val", S_IRUGO | S_IWUSR,
+			vcap_debugfs_base, dev, &vcap_reg_rdwr_fops))
+		goto error;
+	return 0;
+
+error:
+	debugfs_remove_recursive(vcap_debugfs_base);
+	vcap_debugfs_base = NULL;
+	return -ENOMEM;
+}
+
+static void vcap_debugfs_remove(void)
+{
+	if (vcap_debugfs_base) {
+		debugfs_remove_recursive(vcap_debugfs_base);
+		vcap_debugfs_base = NULL;
+	}
+}
+#else
+
+static int vcap_debugfs_init(struct vcap_dev *dev)
+{
+	return 0;
+}
+static void vcap_debugfs_remove(void) {}
+#endif
+
 static int __devinit vcap_probe(struct platform_device *pdev)
 {
 	struct vcap_dev *dev;
 	struct video_device *vfd;
 	int ret;
 
-	dprintk(1, "Probe started\n");
 	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
 	if (!dev)
 		return -ENOMEM;
@@ -1920,7 +2267,7 @@
 
 
 	ret = request_irq(dev->vcirq->start, vcap_vc_handler,
-		IRQF_TRIGGER_RISING, "vc_irq", 0);
+		IRQF_TRIGGER_HIGH, "vc_irq", 0);
 	if (ret < 0) {
 		pr_err("%s: vc irq request fail\n", __func__);
 		ret = -EBUSY;
@@ -1945,10 +2292,39 @@
 	if (ret)
 		goto free_resource;
 
+	dev->vc_iommu_ctx = msm_iommu_get_ctx("vcap_vc");
+	if (!dev->vc_iommu_ctx) {
+		pr_err("%s: No iommu vc context found\n", __func__);
+		ret = -ENODEV;
+		goto free_resource;
+	}
+
+	dev->vp_iommu_ctx = msm_iommu_get_ctx("vcap_vp");
+	if (!dev->vp_iommu_ctx) {
+		pr_err("%s: No iommu vp context found\n", __func__);
+		ret = -ENODEV;
+		goto free_resource;
+	}
+
+	dev->domain_num = vcap_register_domain();
+	if (dev->domain_num < 0) {
+		pr_err("%s: VCAP iommu domain register failed\n", __func__);
+		ret = -ENODEV;
+		goto free_resource;
+	}
+
+	dev->iommu_vcap_domain = msm_get_iommu_domain(dev->domain_num);
+	if (!dev->iommu_vcap_domain) {
+		pr_err("%s: No iommu vcap domain found\n", __func__);
+		ret = -ENODEV;
+		goto free_resource;
+	}
+
 	ret = vcap_enable(dev, &pdev->dev, 54860000);
 	if (ret)
 		goto unreg_dev;
 	msm_bus_scale_client_update_request(dev->bus_client_handle, 0);
+	dev->dbg_p.bw_request = 0;
 
 	ret = detect_vc(dev);
 
@@ -1987,6 +2363,11 @@
 		goto rel_vcap_wq;
 	}
 
+	atomic_set(&dev->dbg_p.vc_drop_count, 0);
+	ret = vcap_debugfs_init(dev);
+	if (ret < 0)
+		pr_err("VCAP debugfs failed to load");
+
 	dev->vc_tot_buf = 2;
 	atomic_set(&dev->vc_enabled, 0);
 	atomic_set(&dev->vp_enabled, 0);
@@ -1996,7 +2377,6 @@
 	init_waitqueue_head(&dev->vp_dummy_waitq);
 	vcap_disable(dev);
 
-	dprintk(1, "Exit probe succesfully");
 	return 0;
 rel_vcap_wq:
 	destroy_workqueue(dev->vcap_wq);
@@ -2020,6 +2400,7 @@
 static int __devexit vcap_remove(struct platform_device *pdev)
 {
 	struct vcap_dev *dev = vcap_ctrl;
+	vcap_debugfs_remove();
 	ion_client_destroy(dev->ion_client);
 	flush_workqueue(dev->vcap_wq);
 	destroy_workqueue(dev->vcap_wq);
diff --git a/drivers/media/video/vcap_vc.c b/drivers/media/video/vcap_vc.c
index 572c272..08dcdf3 100644
--- a/drivers/media/video/vcap_vc.c
+++ b/drivers/media/video/vcap_vc.c
@@ -27,14 +27,6 @@
 #include <media/vcap_fmt.h>
 #include "vcap_vc.h"
 
-static unsigned debug;
-
-#define dprintk(level, fmt, arg...)					\
-	do {								\
-		if (debug >= level)					\
-			printk(KERN_DEBUG "VC: " fmt, ## arg);		\
-	} while (0)
-
 void config_buffer(struct vcap_client_data *c_data,
 			struct vcap_buffer *buf,
 			void __iomem *y_addr,
@@ -43,10 +35,11 @@
 	if (c_data->vc_format.color_space == HAL_VCAP_RGB) {
 		writel_relaxed(buf->paddr, y_addr);
 	} else {
-		int size = ((c_data->vc_format.hactive_end -
-				c_data->vc_format.hactive_start) *
-				(c_data->vc_format.vactive_end -
-				c_data->vc_format.vactive_start));
+		int size = (c_data->vc_format.hactive_end -
+				c_data->vc_format.hactive_start);
+		size = VCAP_STRIDE_CALC(size);
+		size *= (c_data->vc_format.vactive_end -
+				c_data->vc_format.vactive_start);
 		writel_relaxed(buf->paddr, y_addr);
 		writel_relaxed(buf->paddr + size, c_addr);
 	}
@@ -73,7 +66,7 @@
 
 		vb_vc = vp_work->cd->vc_vidq.bufs[p.index];
 		if (NULL == vb_vc) {
-			dprintk(1, "%s: buffer is NULL\n", __func__);
+			pr_debug("%s: buffer is NULL\n", __func__);
 			vcvp_qbuf(&vp_work->cd->vc_vidq, &p);
 			return;
 		}
@@ -81,7 +74,7 @@
 
 		vb_vp = vp_work->cd->vp_in_vidq.bufs[p.index];
 		if (NULL == vb_vp) {
-			dprintk(1, "%s: buffer is NULL\n", __func__);
+			pr_debug("%s: buffer is NULL\n", __func__);
 			vcvp_qbuf(&vp_work->cd->vc_vidq, &p);
 			return;
 		}
@@ -133,28 +126,9 @@
 	return tv;
 }
 
-irqreturn_t vc_handler(struct vcap_dev *dev)
+inline void vc_isr_error_checking(struct vcap_dev *dev,
+		struct v4l2_event v4l2_evt, uint32_t irq)
 {
-	uint32_t irq, timestamp;
-	struct vcap_buffer *buf;
-	struct vb2_buffer *vb = NULL;
-	struct vcap_client_data *c_data;
-	struct v4l2_event v4l2_evt;
-	uint8_t i, idx, buf_num, tot, done_count = 0;
-	bool work_todo = false;
-
-	irq = readl_relaxed(VCAP_VC_INT_STATUS);
-
-	dprintk(1, "%s: irq=0x%08x\n", __func__, irq);
-
-	c_data = dev->vc_client;
-	if (!c_data->streaming) {
-		writel_iowmb(irq, VCAP_VC_INT_CLEAR);
-		pr_err("VC no longer streaming\n");
-		return IRQ_HANDLED;
-	}
-
-	v4l2_evt.id = 0;
 	if (irq & 0x8000200) {
 		writel_iowmb(0x00000102, VCAP_VC_NPL_CTRL);
 		v4l2_evt.type = V4L2_EVENT_PRIVATE_START +
@@ -173,6 +147,12 @@
 			VCAP_VC_VSYNC_ERR_EVENT;
 		v4l2_event_queue(dev->vfd, &v4l2_evt);
 	}
+	if (irq & 0x00001000) {
+		writel_iowmb(0x00000102, VCAP_VC_NPL_CTRL);
+		v4l2_evt.type = V4L2_EVENT_PRIVATE_START +
+			VCAP_VC_VSYNC_SEQ_ERR;
+		v4l2_event_queue(dev->vfd, &v4l2_evt);
+	}
 	if (irq & 0x00000800) {
 		writel_iowmb(0x00000102, VCAP_VC_NPL_CTRL);
 		v4l2_evt.type = V4L2_EVENT_PRIVATE_START +
@@ -185,33 +165,26 @@
 			VCAP_VC_LBUF_OFLOW_ERR_EVENT;
 		v4l2_event_queue(dev->vfd, &v4l2_evt);
 	}
+}
 
-	if (!(irq & VC_BUFFER_MASK)) {
-		writel_relaxed(irq, VCAP_VC_INT_CLEAR);
-		pr_err("VC IRQ shows some error\n");
-		return IRQ_HANDLED;
-	}
-
-	if (dev->vc_client == NULL) {
-		/* This should never happen */
-		writel_relaxed(irq, VCAP_VC_INT_CLEAR);
-		pr_err("VC: There is no active vc client\n");
-		return IRQ_HANDLED;
-	}
-	c_data = dev->vc_client;
-
+inline uint8_t vc_isr_buffer_done_count(struct vcap_dev *dev,
+		struct vcap_client_data *c_data, uint32_t irq)
+{
+	int i;
+	uint8_t done_count = 0;
 	for (i = 0; i < VCAP_VC_MAX_BUF; i++) {
 		if (0x2 & (irq >> i))
 			done_count++;
 	}
+	return done_count;
+}
 
-	/* Assign field value in case somehow got out of sync */
-	if (c_data->vc_format.mode == HAL_VCAP_MODE_INT && done_count == 1)
-		c_data->vc_action.top_field = !(irq & 0x1);
-
+inline bool vc_isr_verify_expect_buf_rdy(struct vcap_dev *dev,
+		struct vcap_client_data *c_data, struct v4l2_event v4l2_evt,
+		uint32_t irq, uint8_t done_count, uint8_t tot, uint8_t buf_num)
+{
+	int i;
 	/* Double check expected buffers are done */
-	buf_num = c_data->vc_action.buf_num;
-	tot = c_data->vc_action.tot_buf;
 	for (i = 0; i < done_count; i++) {
 		if (!(irq & (0x1 << (((buf_num + i) % tot) + 1)))) {
 			v4l2_evt.type = V4L2_EVENT_PRIVATE_START +
@@ -220,12 +193,17 @@
 			pr_debug("Unexpected buffer done\n");
 			c_data->vc_action.buf_num =
 				correct_buf_num(irq) % tot;
-			writel_relaxed(irq, VCAP_VC_INT_CLEAR);
-			return IRQ_HANDLED;
+			return true;
 		}
 	}
+	return false;
+}
 
-	/* If here we know which buffers are done */
+inline void vc_isr_update_timestamp(struct vcap_dev *dev,
+		struct vcap_client_data *c_data)
+{
+	uint32_t timestamp;
+
 	timestamp = readl_relaxed(VCAP_VC_TIMESTAMP);
 	if (timestamp < c_data->vc_action.last_ts) {
 		c_data->vc_action.vc_ts.tv_usec +=
@@ -241,49 +219,142 @@
 	c_data->vc_action.vc_ts.tv_usec =
 		c_data->vc_action.vc_ts.tv_usec % VCAP_USEC;
 	c_data->vc_action.last_ts = timestamp;
+}
 
-	c_data->vc_action.buf_num = (buf_num + done_count) % tot;
+inline void vc_isr_no_new_buffer(struct vcap_dev *dev,
+		struct vcap_client_data *c_data, struct v4l2_event v4l2_evt)
+{
+	v4l2_evt.type = V4L2_EVENT_PRIVATE_START +
+		VCAP_VC_BUF_OVERWRITE_EVENT;
+	v4l2_event_queue(dev->vfd, &v4l2_evt);
+
+	c_data->vc_action.field_dropped =
+		!c_data->vc_action.field_dropped;
+
+	c_data->vc_action.field1 =
+		!c_data->vc_action.field1;
+	atomic_inc(&dev->dbg_p.vc_drop_count);
+}
+
+inline void vc_isr_switch_buffers(struct vcap_dev *dev,
+		struct vcap_client_data *c_data, struct vcap_buffer *buf,
+		struct vb2_buffer *vb, uint8_t idx, int done_count, int i)
+{
+	/* Config vc with this new buffer */
+	config_buffer(c_data, buf, VCAP_VC_Y_ADDR_1 + 0x8 * idx,
+			VCAP_VC_C_ADDR_1 + 0x8 * idx);
+	vb->v4l2_buf.timestamp = interpolate_ts(
+		c_data->vc_action.vc_ts,
+		1000000 / c_data->vc_format.frame_rate *
+		(done_count - 1 - i));
+	if (c_data->vc_format.mode == HAL_VCAP_MODE_INT) {
+		if (c_data->vc_action.field1)
+			vb->v4l2_buf.field = V4L2_FIELD_TOP;
+		else
+			vb->v4l2_buf.field = V4L2_FIELD_BOTTOM;
+
+		c_data->vc_action.field1 =
+			!c_data->vc_action.field1;
+	}
+	vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
+	c_data->vc_action.buf[idx] = buf;
+}
+
+inline bool vc_isr_change_buffers(struct vcap_dev *dev,
+		struct vcap_client_data *c_data, struct v4l2_event v4l2_evt,
+		int done_count, uint8_t tot, uint8_t buf_num)
+{
+	struct vb2_buffer *vb = NULL;
+	struct vcap_buffer *buf;
+	bool schedule_work = false;
+	uint8_t idx;
+	int i;
+
 	for (i = 0; i < done_count; i++) {
 		idx = (buf_num + i) % tot;
 		vb = &c_data->vc_action.buf[idx]->vb;
 		spin_lock(&c_data->cap_slock);
 		if (list_empty(&c_data->vc_action.active)) {
 			spin_unlock(&c_data->cap_slock);
-			v4l2_evt.type = V4L2_EVENT_PRIVATE_START +
-				VCAP_VC_BUF_OVERWRITE_EVENT;
-			v4l2_event_queue(dev->vfd, &v4l2_evt);
-			c_data->vc_action.top_field =
-				!c_data->vc_action.top_field;
+			vc_isr_no_new_buffer(dev, c_data, v4l2_evt);
+			continue;
+		}
+		if (c_data->vc_format.mode == HAL_VCAP_MODE_INT &&
+				c_data->vc_action.field_dropped) {
+			spin_unlock(&c_data->cap_slock);
+			vc_isr_no_new_buffer(dev, c_data, v4l2_evt);
 			continue;
 		}
 		buf = list_entry(c_data->vc_action.active.next,
 				struct vcap_buffer, list);
 		list_del(&buf->list);
 		spin_unlock(&c_data->cap_slock);
-		/* Config vc with this new buffer */
-		config_buffer(c_data, buf, VCAP_VC_Y_ADDR_1 + 0x8 * idx,
-				VCAP_VC_C_ADDR_1 + 0x8 * idx);
-		vb->v4l2_buf.timestamp = interpolate_ts(
-			c_data->vc_action.vc_ts,
-			1000000 / c_data->vc_format.frame_rate *
-			(done_count - 1 - i));
-		if (c_data->vc_format.mode == HAL_VCAP_MODE_INT) {
-			if (c_data->vc_action.top_field)
-				vb->v4l2_buf.field = V4L2_FIELD_TOP;
-			else
-				vb->v4l2_buf.field = V4L2_FIELD_BOTTOM;
-			c_data->vc_action.top_field =
-				!c_data->vc_action.top_field;
-		}
-		vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
-		work_todo = true;
-		c_data->vc_action.buf[idx] = buf;
+		vc_isr_switch_buffers(dev, c_data, buf, vb, idx, done_count, i);
+		schedule_work = true;
+	}
+	return schedule_work;
+}
+
+irqreturn_t vc_handler(struct vcap_dev *dev)
+{
+	uint32_t irq;
+	struct vcap_client_data *c_data;
+	struct v4l2_event v4l2_evt;
+	uint8_t done_count = 0, buf_num, tot;
+	bool schedule_work = false;
+
+	v4l2_evt.id = 0;
+	irq = readl_relaxed(VCAP_VC_INT_STATUS);
+	writel_relaxed(irq, VCAP_VC_INT_CLEAR);
+
+	pr_debug("%s: irq=0x%08x\n", __func__, irq);
+
+	if (dev->vc_client == NULL) {
+		/* This should never happen */
+		pr_err("VC: There is no active vc client\n");
+		return IRQ_HANDLED;
 	}
 
-	if (work_todo && c_data->op_mode == VC_AND_VP_VCAP_OP)
+	c_data = dev->vc_client;
+	if (!c_data->streaming) {
+		pr_err("VC no longer streaming\n");
+		return IRQ_HANDLED;
+	}
+
+	if (irq == VC_VSYNC_MASK) {
+		if (c_data->vc_format.mode == HAL_VCAP_MODE_INT)
+			c_data->vc_action.field1 = irq & 0x1;
+		return IRQ_HANDLED;
+	}
+
+	if (irq & VC_ERR_MASK) {
+		vc_isr_error_checking(dev, v4l2_evt, irq);
+		return IRQ_HANDLED;
+	}
+
+	if (!(irq & VC_BUFFER_MASK)) {
+		pr_debug("No frames done\n");
+		return IRQ_HANDLED;
+	}
+
+	done_count = vc_isr_buffer_done_count(dev, c_data, irq);
+	buf_num = c_data->vc_action.buf_num;
+	tot = c_data->vc_action.tot_buf;
+
+	if (vc_isr_verify_expect_buf_rdy(dev, c_data,
+			v4l2_evt, irq, done_count, tot, buf_num))
+		return IRQ_HANDLED;
+
+	vc_isr_update_timestamp(dev, c_data);
+
+	c_data->vc_action.buf_num = (buf_num + done_count) % tot;
+
+	schedule_work = vc_isr_change_buffers(dev, c_data, v4l2_evt,
+		done_count, tot, buf_num);
+
+	if (schedule_work && c_data->op_mode == VC_AND_VP_VCAP_OP)
 		queue_work(dev->vcap_wq, &dev->vc_to_vp_work.work);
 
-	writel_relaxed(irq, VCAP_VC_INT_CLEAR);
 	return IRQ_HANDLED;
 }
 
@@ -296,12 +367,13 @@
 {
 	struct vc_action *vc_action = &c_data->vc_action;
 	struct vcap_dev *dev;
+	struct timeval tv;
 	unsigned long flags = 0;
 	int rc, i, counter = 0;
 	struct vcap_buffer *buf;
 
 	dev = c_data->dev;
-	dprintk(2, "Start Kickoff\n");
+	pr_debug("Start Kickoff\n");
 
 	if (dev->vc_client == NULL) {
 		pr_err("No active vc client\n");
@@ -344,9 +416,16 @@
 	c_data->vc_action.vc_ts.tv_usec =
 		c_data->vc_action.last_ts % VCAP_USEC;
 
+	atomic_set(&dev->dbg_p.vc_drop_count, 0);
+	do_gettimeofday(&tv);
+	dev->dbg_p.vc_timestamp = (uint32_t) (tv.tv_sec * VCAP_USEC +
+		tv.tv_usec);
+
 	rc = 0;
 	for (i = 0; i < c_data->vc_action.tot_buf; i++)
 		rc = rc << 1 | 0x2;
+	rc |= VC_ERR_MASK;
+	rc |= VC_VSYNC_MASK;
 	writel_relaxed(rc, VCAP_VC_INT_MASK);
 
 	enable_irq(dev->vcirq->start);
@@ -416,7 +495,7 @@
 	rc = readl_relaxed(VCAP_VC_NPL_CTRL);
 	writel_iowmb(0x00000002, VCAP_VC_NPL_CTRL);
 
-	dprintk(2, "%s: Starting VC configuration\n", __func__);
+	pr_debug("%s: Starting VC configuration\n", __func__);
 	writel_iowmb(0x00000002, VCAP_VC_NPL_CTRL);
 	writel_iowmb(0x00000004 | vc_format->color_space << 1 |
 			vc_format->mode << 3 |
@@ -424,11 +503,10 @@
 			vc_format->mode << 10,
 			VCAP_VC_CTRL);
 
-	writel_relaxed(vc_format->h_polar << 4 |
+	writel_relaxed(vc_format->d_polar << 8 |
+			vc_format->h_polar << 4 |
 			vc_format->v_polar << 0, VCAP_VC_POLARITY);
 
-	writel_relaxed(vc_format->h_polar << 4 |
-			vc_format->v_polar << 0, VCAP_VC_POLARITY);
 	writel_relaxed(((vc_format->htotal << 16) | vc_format->vtotal),
 			VCAP_VC_V_H_TOTAL);
 	writel_relaxed(((vc_format->hactive_end << 16) |
@@ -449,6 +527,7 @@
 	writel_iowmb(0x000033FF, VCAP_VC_BUF_CTRL);
 
 	rc = vc_format->hactive_end - vc_format->hactive_start;
+	rc = VCAP_STRIDE_CALC(rc);
 	if (vc_format->color_space)
 		rc *= 3;
 
@@ -464,7 +543,7 @@
 	writel_relaxed(0x00006b38, VCAP_VC_IN_CTRL5);
 
 	writel_iowmb(0x00000001 , VCAP_OFFSET(0x0d00));
-	dprintk(2, "%s: Done VC configuration\n", __func__);
+	pr_debug("%s: Done VC configuration\n", __func__);
 
 	return 0;
 }
@@ -473,7 +552,7 @@
 {
 	int result;
 	result = readl_relaxed(VCAP_HARDWARE_VERSION_REG);
-	dprintk(1, "Hardware version: %08x\n", result);
+	pr_debug("Hardware version: %08x\n", result);
 	if (result != VCAP_HARDWARE_VERSION)
 		return -ENODEV;
 	INIT_WORK(&dev->vc_to_vp_work.work, mov_buf_to_vp);
diff --git a/drivers/media/video/vcap_vc.h b/drivers/media/video/vcap_vc.h
index 7f42c7f..9c3f5a7 100644
--- a/drivers/media/video/vcap_vc.h
+++ b/drivers/media/video/vcap_vc.h
@@ -63,6 +63,8 @@
 
 #define VC_BUFFER_WRITTEN (0x3 << 1)
 #define VC_BUFFER_MASK 0x7E
+#define VC_ERR_MASK 0xE0001E00
+#define VC_VSYNC_MASK 0x1
 
 int vc_start_capture(struct vcap_client_data *c_data);
 int vc_hw_kick_off(struct vcap_client_data *c_data);
diff --git a/drivers/media/video/vcap_vp.c b/drivers/media/video/vcap_vp.c
index a017cf2..82f9e58 100644
--- a/drivers/media/video/vcap_vp.c
+++ b/drivers/media/video/vcap_vp.c
@@ -15,24 +15,18 @@
 #include <linux/sched.h>
 #include <linux/kthread.h>
 #include <linux/freezer.h>
-#include <mach/camera.h>
-#include <linux/io.h>
-#include <mach/clk.h>
 #include <linux/clk.h>
+#include <linux/io.h>
+
+#include <mach/camera.h>
+#include <mach/clk.h>
 
 #include <media/v4l2-event.h>
 #include <media/vcap_v4l2.h>
 #include <media/vcap_fmt.h>
+
 #include "vcap_vp.h"
 
-static unsigned debug;
-
-#define dprintk(level, fmt, arg...)					\
-	do {								\
-		if (debug >= level)					\
-			printk(KERN_DEBUG "VP: " fmt, ## arg);		\
-	} while (0)
-
 void config_nr_buffer(struct vcap_client_data *c_data,
 			struct vcap_buffer *buf)
 {
@@ -72,10 +66,10 @@
 	if (!c_data->streaming)
 		return -ENOEXEC;
 	dev = c_data->dev;
-	dprintk(2, "Start setup buffers\n");
+	pr_debug("VP: Start setup buffers\n");
 
 	if (dev->vp_shutdown) {
-		dprintk(1, "%s: VP shutting down, no buf setup\n",
+		pr_debug("%s: VP shutting down, no buf setup\n",
 			__func__);
 		return -EPERM;
 	}
@@ -86,7 +80,7 @@
 	spin_lock_irqsave(&dev->vp_client->cap_slock, flags);
 	if (list_empty(&vp_act->in_active)) {
 		spin_unlock_irqrestore(&dev->vp_client->cap_slock, flags);
-		dprintk(1, "%s: VP We have no more input buffers\n",
+		pr_debug("%s: VP We have no more input buffers\n",
 				__func__);
 		return -EAGAIN;
 	}
@@ -94,7 +88,7 @@
 	if (list_empty(&vp_act->out_active)) {
 		spin_unlock_irqrestore(&dev->vp_client->cap_slock,
 			flags);
-		dprintk(1, "%s: VP We have no more output buffers\n",
+		pr_debug("%s: VP We have no more output buffers\n",
 		   __func__);
 		return -EAGAIN;
 	}
@@ -136,7 +130,7 @@
 
 		vb_vc = vp_work->cd->vc_vidq.bufs[p.index];
 		if (NULL == vb_vc) {
-			dprintk(1, "%s: buffer is NULL\n", __func__);
+			pr_debug("%s: buffer is NULL\n", __func__);
 			vcvp_qbuf(&vp_work->cd->vp_in_vidq, &p);
 			return;
 		}
@@ -144,7 +138,7 @@
 
 		vb_vp = vp_work->cd->vp_in_vidq.bufs[p.index];
 		if (NULL == vb_vp) {
-			dprintk(1, "%s: buffer is NULL\n", __func__);
+			pr_debug("%s: buffer is NULL\n", __func__);
 			vcvp_qbuf(&vp_work->cd->vp_in_vidq, &p);
 			return;
 		}
@@ -158,7 +152,7 @@
 		/* This call should not fail */
 		rc = vcvp_qbuf(&vp_work->cd->vc_vidq, &p);
 		if (rc < 0) {
-			dprintk(1, "%s: qbuf to vc failed\n", __func__);
+			pr_err("%s: qbuf to vc failed\n", __func__);
 			buf_vp->ion_handle = buf_vc->ion_handle;
 			buf_vp->paddr = buf_vc->paddr;
 			buf_vc->ion_handle = NULL;
@@ -197,6 +191,7 @@
 	struct vp_work_t *vp_work = container_of(work, struct vp_work_t, work);
 	struct vcap_dev *dev;
 	struct vp_action *vp_act;
+	struct timeval tv;
 	unsigned long flags = 0;
 	uint32_t irq;
 	int rc;
@@ -272,12 +267,17 @@
 	}
 
 	/* Config VP */
-	if (vp_act->bufT2->vb.v4l2_buf.field == V4L2_FIELD_TOP)
+	if (vp_act->bufT2->vb.v4l2_buf.field == V4L2_FIELD_BOTTOM)
 		top_field = 1;
 
 	writel_iowmb(0x00000000 | top_field, VCAP_VP_CTRL);
 	writel_iowmb(0x00010000 | top_field, VCAP_VP_CTRL);
 	enable_irq(dev->vpirq->start);
+
+	do_gettimeofday(&tv);
+	dev->dbg_p.vp_timestamp = (uint32_t) (tv.tv_sec * VCAP_USEC +
+	tv.tv_usec);
+
 	writel_iowmb(irq, VCAP_VP_INT_CLEAR);
 }
 
@@ -288,6 +288,8 @@
 	struct v4l2_event v4l2_evt;
 	uint32_t irq;
 	int rc;
+	struct timeval tv;
+	uint32_t new_ts;
 
 	irq = readl_relaxed(VCAP_VP_INT_STATUS);
 	if (dev->vp_dummy_event == true) {
@@ -318,7 +320,7 @@
 		v4l2_event_queue(dev->vfd, &v4l2_evt);
 	}
 
-	dprintk(1, "%s: irq=0x%08x\n", __func__, irq);
+	pr_debug("%s: irq=0x%08x\n", __func__, irq);
 	if (!(irq & (VP_PIC_DONE | VP_MODE_CHANGE))) {
 		writel_relaxed(irq, VCAP_VP_INT_CLEAR);
 		pr_err("VP IRQ shows some error\n");
@@ -341,6 +343,17 @@
 		return -EAGAIN;
 	}
 
+	do_gettimeofday(&tv);
+	new_ts = (uint32_t) (tv.tv_sec * VCAP_USEC +
+		tv.tv_usec);
+	if (new_ts > dev->dbg_p.vp_timestamp) {
+		dev->dbg_p.vp_ewma = ((new_ts - dev->dbg_p.vp_timestamp) /
+			10 + (dev->dbg_p.vp_ewma / 10 * 9));
+	}
+
+	dev->dbg_p.vp_timestamp = (uint32_t) (tv.tv_sec * VCAP_USEC +
+	tv.tv_usec);
+
 	INIT_WORK(&dev->vp_work.work, vp_wq_fnc);
 	dev->vp_work.cd = c_data;
 	rc = queue_work(dev->vcap_wq, &dev->vp_work.work);
@@ -382,6 +395,7 @@
 				msecs_to_jiffies(50));
 		if (rc == 0 && atomic_read(&dev->vp_enabled) == 1) {
 			/* This should not happen, if it does hw is stuck */
+			disable_irq_nosync(dev->vpirq->start);
 			pr_err("%s: VP Timeout and VP still running\n",
 				__func__);
 		}
@@ -452,9 +466,8 @@
 	int rc;
 	struct vcap_dev *dev = c_data->dev;
 	struct ion_handle *handle = NULL;
-	unsigned long paddr, ionflag = 0;
+	unsigned long paddr, len, ionflag = 0;
 	void *vaddr;
-	size_t len;
 	size_t size = ((c_data->vp_out_fmt.width + 63) >> 6) *
 		((c_data->vp_out_fmt.height + 7) >> 3) * 16;
 
@@ -469,12 +482,6 @@
 		pr_err("%s: ion_alloc failed\n", __func__);
 		return -ENOMEM;
 	}
-	rc = ion_phys(dev->ion_client, handle, &paddr, &len);
-	if (rc < 0) {
-		pr_err("%s: ion_phys failed\n", __func__);
-		ion_free(dev->ion_client, handle);
-		return rc;
-	}
 
 	rc = ion_handle_get_flags(dev->ion_client, handle, &ionflag);
 	if (rc) {
@@ -492,10 +499,20 @@
 	}
 
 	memset(vaddr, 0, size);
+	ion_unmap_kernel(dev->ion_client, handle);
+
+	rc = ion_map_iommu(dev->ion_client, handle,
+		dev->domain_num, 0, SZ_4K, 0, &paddr, &len,
+		0, 0);
+	if (rc < 0) {
+		pr_err("%s: map_iommu failed\n", __func__);
+		ion_free(dev->ion_client, handle);
+		return rc;
+	}
+
 	c_data->vp_action.motionHandle = handle;
 
 	vaddr = NULL;
-	ion_unmap_kernel(dev->ion_client, handle);
 
 	writel_iowmb(paddr, VCAP_VP_MOTION_EST_ADDR);
 	return 0;
@@ -510,6 +527,8 @@
 	}
 
 	writel_iowmb(0x00000000, VCAP_VP_MOTION_EST_ADDR);
+	ion_unmap_iommu(dev->ion_client, c_data->vp_action.motionHandle,
+			dev->domain_num, 0);
 	ion_free(dev->ion_client, c_data->vp_action.motionHandle);
 	c_data->vp_action.motionHandle = NULL;
 	return;
@@ -519,8 +538,8 @@
 {
 	struct vcap_dev *dev = c_data->dev;
 	struct ion_handle *handle = NULL;
-	size_t frame_size, tot_size, len;
-	unsigned long paddr;
+	size_t frame_size, tot_size;
+	unsigned long paddr, len;
 	int rc;
 
 	if (c_data->vp_action.bufNR.nr_handle) {
@@ -541,9 +560,11 @@
 		return -ENOMEM;
 	}
 
-	rc = ion_phys(dev->ion_client, handle, &paddr, &len);
+	rc = ion_map_iommu(dev->ion_client, handle,
+		dev->domain_num, 0, SZ_4K, 0, &paddr, &len,
+		0, 0);
 	if (rc < 0) {
-		pr_err("%s: ion_phys failed\n", __func__);
+		pr_err("%s: map_iommu failed\n", __func__);
 		ion_free(dev->ion_client, handle);
 		return rc;
 	}
@@ -577,6 +598,7 @@
 	rc &= !(0x0FF00001);
 	writel_relaxed(rc, VCAP_VP_NR_CONFIG2);
 
+	ion_unmap_iommu(dev->ion_client, buf->nr_handle, dev->domain_num, 0);
 	ion_free(dev->ion_client, buf->nr_handle);
 	buf->nr_handle = NULL;
 	buf->paddr = 0;
@@ -658,12 +680,11 @@
 	struct vcap_dev *dev = c_data->dev;
 	unsigned int width, height;
 	struct ion_handle *handle = NULL;
-	unsigned long paddr;
-	size_t len;
+	unsigned long paddr, len;
 	uint32_t reg;
 	int rc = 0;
 
-	dprintk(2, "%s: Start VP dummy event\n", __func__);
+	pr_debug("%s: Start VP dummy event\n", __func__);
 	handle = ion_alloc(dev->ion_client, 0x1200, SZ_4K,
 			ION_HEAP(ION_CP_MM_HEAP_ID), 0);
 	if (IS_ERR_OR_NULL(handle)) {
@@ -671,9 +692,11 @@
 		return -ENOMEM;
 	}
 
-	rc = ion_phys(dev->ion_client, handle, &paddr, &len);
+	rc = ion_map_iommu(dev->ion_client, handle,
+		dev->domain_num, 0, SZ_4K, 0, &paddr, &len,
+		0, 0);
 	if (rc < 0) {
-		pr_err("%s: ion_phys failed\n", __func__);
+		pr_err("%s: map_iommu failed\n", __func__);
 		ion_free(dev->ion_client, handle);
 		return rc;
 	}
@@ -721,9 +744,10 @@
 
 	c_data->vp_out_fmt.width = width;
 	c_data->vp_out_fmt.height = height;
+	ion_unmap_iommu(dev->ion_client, handle, dev->domain_num, 0);
 	ion_free(dev->ion_client, handle);
 
-	dprintk(2, "%s: Exit VP dummy event\n", __func__);
+	pr_debug("%s: Exit VP dummy event\n", __func__);
 	return rc;
 }
 
@@ -731,6 +755,7 @@
 {
 	struct vcap_dev *dev;
 	struct vp_action *vp_act;
+	struct timeval tv;
 	unsigned long flags = 0;
 	unsigned int chroma_fmt = 0;
 	int size;
@@ -740,7 +765,7 @@
 		return -ENOEXEC;
 
 	dev = c_data->dev;
-	dprintk(2, "Start Kickoff\n");
+	pr_debug("Start VP Kickoff\n");
 
 	if (dev->vp_client == NULL) {
 		pr_err("No active vp client\n");
@@ -807,7 +832,7 @@
 			chroma_fmt << 11 | 0x1 << 4, VCAP_VP_OUT_CONFIG);
 
 	/* Enable Interrupt */
-	if (vp_act->bufT2->vb.v4l2_buf.field == V4L2_FIELD_TOP)
+	if (vp_act->bufT2->vb.v4l2_buf.field == V4L2_FIELD_BOTTOM)
 		top_field = 1;
 	vp_act->vp_state = VP_FRAME2;
 	writel_relaxed(0x01100001, VCAP_VP_INTERRUPT_ENABLE);
@@ -815,6 +840,11 @@
 	writel_iowmb(0x00010000 | top_field, VCAP_VP_CTRL);
 	atomic_set(&c_data->dev->vp_enabled, 1);
 	enable_irq(dev->vpirq->start);
+
+	do_gettimeofday(&tv);
+	dev->dbg_p.vp_timestamp = (uint32_t) (tv.tv_sec * VCAP_USEC +
+	tv.tv_usec);
+
 	return 0;
 }
 
@@ -822,10 +852,11 @@
 {
 	struct vcap_dev *dev;
 	struct vp_action *vp_act;
+	struct timeval tv;
 	int rc;
 	bool top_field = 0;
 
-	dprintk(2, "Start Continue\n");
+	pr_debug("Start VP Continue\n");
 	dev = c_data->dev;
 
 	if (dev->vp_client == NULL) {
@@ -844,7 +875,7 @@
 	if (rc < 0)
 		return rc;
 
-	if (vp_act->bufT2->vb.v4l2_buf.field == V4L2_FIELD_TOP)
+	if (vp_act->bufT2->vb.v4l2_buf.field == V4L2_FIELD_BOTTOM)
 		top_field = 1;
 
 	/* Config VP & Enable Interrupt */
@@ -854,5 +885,10 @@
 
 	atomic_set(&c_data->dev->vp_enabled, 1);
 	enable_irq(dev->vpirq->start);
+
+	do_gettimeofday(&tv);
+	dev->dbg_p.vp_timestamp = (uint32_t) (tv.tv_sec * VCAP_USEC +
+	tv.tv_usec);
+
 	return 0;
 }
diff --git a/drivers/mfd/marimba-core.c b/drivers/mfd/marimba-core.c
index 6a8ea6e..26f3ece 100644
--- a/drivers/mfd/marimba-core.c
+++ b/drivers/mfd/marimba-core.c
@@ -173,13 +173,14 @@
 	u8 data[num_bytes + 1];
 	u8 mask_value[num_bytes];
 
+	memset(mask_value, 0, sizeof(mask_value));
+
 	marimba = &marimba_modules[marimba->mod_id];
 	if (marimba == NULL) {
 		pr_err("%s: Unable to access Marimba core\n", __func__);
 		return -ENODEV;
 	}
 
-
 	mutex_lock(&marimba->xfer_lock);
 
 	for (i = 0; i < num_bytes; i++)
@@ -619,7 +620,7 @@
 static int __devinit marimba_dbg_init(int adie_type)
 {
 	struct adie_dbg_device *dbgdev;
-	struct dentry *dent;
+	struct dentry *dent = NULL;
 	struct dentry *temp;
 
 	dbgdev = kzalloc(sizeof *dbgdev, GFP_KERNEL);
@@ -856,8 +857,7 @@
 			ssbi_adap = NULL;
 
 		if (!marimba->client) {
-			dev_err(&marimba->client->dev,
-				"can't attach client %d\n", i);
+			pr_err("can't attach client %d\n", i);
 			status = -ENOMEM;
 			goto fail;
 		}
diff --git a/drivers/mfd/pm8038-core.c b/drivers/mfd/pm8038-core.c
index 9815f6e..48bc92d 100644
--- a/drivers/mfd/pm8038-core.c
+++ b/drivers/mfd/pm8038-core.c
@@ -33,6 +33,11 @@
 #define REG_RTC_BASE		0x11D
 #define REG_IRQ_BASE            0x1BB
 
+#define REG_BATT_ALARM_THRESH	0x023
+#define REG_BATT_ALARM_CTRL1	0x024
+#define REG_BATT_ALARM_CTRL2	0x021
+#define REG_BATT_ALARM_PWM_CTRL	0x020
+
 #define REG_SPK_BASE		0x253
 #define REG_SPK_REGISTERS	6
 
@@ -336,6 +341,27 @@
 	.pdata_size	= sizeof(struct pm8xxx_tm_core_data),
 };
 
+static const struct resource batt_alarm_cell_resources[] __devinitconst = {
+	SINGLE_IRQ_RESOURCE("pm8921_batt_alarm_irq", PM8038_BATT_ALARM_IRQ),
+};
+
+static struct pm8xxx_batt_alarm_core_data batt_alarm_cdata = {
+	.irq_name		= "pm8921_batt_alarm_irq",
+	.reg_addr_threshold	= REG_BATT_ALARM_THRESH,
+	.reg_addr_ctrl1		= REG_BATT_ALARM_CTRL1,
+	.reg_addr_ctrl2		= REG_BATT_ALARM_CTRL2,
+	.reg_addr_pwm_ctrl	= REG_BATT_ALARM_PWM_CTRL,
+};
+
+static struct mfd_cell batt_alarm_cell __devinitdata = {
+	.name		= PM8XXX_BATT_ALARM_DEV_NAME,
+	.id		= -1,
+	.resources	= batt_alarm_cell_resources,
+	.num_resources	= ARRAY_SIZE(batt_alarm_cell_resources),
+	.platform_data	= &batt_alarm_cdata,
+	.pdata_size	= sizeof(struct pm8xxx_batt_alarm_core_data),
+};
+
 static const struct resource ccadc_cell_resources[] __devinitconst = {
 	SINGLE_IRQ_RESOURCE("PM8921_BMS_CCADC_EOC", PM8921_BMS_CCADC_EOC),
 };
@@ -661,6 +687,13 @@
 		goto bail;
 	}
 
+	ret = mfd_add_devices(pmic->dev, 0, &batt_alarm_cell, 1, NULL,
+				irq_base);
+	if (ret) {
+		pr_err("Failed to add battery alarm subdevice ret=%d\n", ret);
+		goto bail;
+	}
+
 	if (pdata->ccadc_pdata) {
 		ccadc_cell.platform_data = pdata->ccadc_pdata;
 		ccadc_cell.pdata_size =
diff --git a/drivers/mfd/pm8xxx-pwm.c b/drivers/mfd/pm8xxx-pwm.c
index 70f4cd5..0e4240c 100644
--- a/drivers/mfd/pm8xxx-pwm.c
+++ b/drivers/mfd/pm8xxx-pwm.c
@@ -216,6 +216,7 @@
 	struct mutex			pwm_mutex;
 	struct device			*dev;
 	bool				is_lpg_supported;
+	bool				is_pwm_enable_sync_workaround_needed;
 };
 
 static struct pm8xxx_pwm_chip	*pwm_chip;
@@ -815,9 +816,18 @@
 		if (pwm_chip->is_lpg_supported) {
 			if (pwm->dtest_mode_supported)
 				pm8xxx_pwm_set_dtest(pwm, 1);
+
 			pm8xxx_pwm_bank_sel(pwm);
 			rc = pm8xxx_pwm_bank_enable(pwm, 1);
 			pm8xxx_pwm_start(pwm, 1, 0);
+
+			/* In PM8038, due to hardware bug, PWM_VALUE register
+			 * needs to be written one more time after enabling
+			 * PWM mode.
+			 */
+			if (pwm->chip->is_pwm_enable_sync_workaround_needed)
+				rc = pm8xxx_lpg_pwm_write(pwm, 3, 4);
+
 		} else {
 			pm8xxx_pwm_enable(pwm);
 		}
@@ -1391,6 +1401,12 @@
 			version == PM8XXX_VERSION_8038) {
 		chip->is_lpg_supported = 1;
 	}
+
+	if (version == PM8XXX_VERSION_8038)
+		chip->is_pwm_enable_sync_workaround_needed = 1;
+	else
+		chip->is_pwm_enable_sync_workaround_needed = 0;
+
 	if (chip->is_lpg_supported) {
 		if (version == PM8XXX_VERSION_8922 ||
 				version == PM8XXX_VERSION_8038) {
diff --git a/drivers/mfd/pm8xxx-spk.c b/drivers/mfd/pm8xxx-spk.c
index 8ba7372..4366717 100644
--- a/drivers/mfd/pm8xxx-spk.c
+++ b/drivers/mfd/pm8xxx-spk.c
@@ -118,8 +118,6 @@
 	}
 
 	val = pm8xxx_spk_read(PM8XXX_SPK_CTL1_REG_OFF);
-	if (val < 0)
-		return val;
 	val |= mute << 2;
 	ret = pm8xxx_spk_write(PM8XXX_SPK_CTL1_REG_OFF, val);
 	return ret;
@@ -137,8 +135,6 @@
 	}
 
 	val = pm8xxx_spk_read(PM8XXX_SPK_CTL1_REG_OFF);
-	if (val < 0)
-		return val;
 	val = (gain << 4) | (val & 0xF);
 	ret = pm8xxx_spk_write(PM8XXX_SPK_CTL1_REG_OFF, val);
 	if (!ret) {
diff --git a/drivers/mfd/wcd9xxx-core.c b/drivers/mfd/wcd9xxx-core.c
index 0ea843c..1f7b67a 100644
--- a/drivers/mfd/wcd9xxx-core.c
+++ b/drivers/mfd/wcd9xxx-core.c
@@ -13,6 +13,7 @@
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/of_gpio.h>
+#include <linux/of_irq.h>
 #include <linux/slab.h>
 #include <linux/mfd/core.h>
 #include <linux/mfd/wcd9xxx/wcd9xxx-slimslave.h>
@@ -257,12 +258,20 @@
 	u8 byte[4];
 	struct mfd_cell *dev;
 	int size;
+	int num_irqs;
 } wcd9xxx_codecs[] = {
-	{{0x2, 0x0, 0x0, 0x1}, tabla_devs, ARRAY_SIZE(tabla_devs)},
-	{{0x1, 0x0, 0x0, 0x1}, tabla1x_devs, ARRAY_SIZE(tabla1x_devs)},
-	{{0x0, 0x0, 0x2, 0x1}, taiko_devs, ARRAY_SIZE(taiko_devs)},
-	{{0x0, 0x0, 0x0, 0x1}, sitar_devs, ARRAY_SIZE(sitar_devs)},
-	{{0x1, 0x0, 0x1, 0x1}, sitar_devs, ARRAY_SIZE(sitar_devs)},
+	{{0x2, 0x0, 0x0, 0x1}, tabla_devs, ARRAY_SIZE(tabla_devs),
+	 TABLA_NUM_IRQS},
+	{{0x1, 0x0, 0x0, 0x1}, tabla1x_devs, ARRAY_SIZE(tabla1x_devs),
+	 TABLA_NUM_IRQS},
+	{{0x0, 0x0, 0x2, 0x1}, taiko_devs, ARRAY_SIZE(taiko_devs),
+	 TAIKO_NUM_IRQS},
+	{{0x0, 0x0, 0x0, 0x1}, sitar_devs, ARRAY_SIZE(sitar_devs),
+	 SITAR_NUM_IRQS},
+	{{0x1, 0x0, 0x1, 0x1}, sitar_devs, ARRAY_SIZE(sitar_devs),
+	 SITAR_NUM_IRQS},
+	{{0x2, 0x0, 0x1, 0x1}, sitar_devs, ARRAY_SIZE(sitar_devs),
+	 SITAR_NUM_IRQS},
 };
 
 static void wcd9xxx_bring_up(struct wcd9xxx *wcd9xxx)
@@ -311,21 +320,21 @@
 	}
 }
 static int wcd9xxx_check_codec_type(struct wcd9xxx *wcd9xxx,
-					struct mfd_cell **wcd9xxx_dev,
-					int *wcd9xxx_dev_size)
+				    struct mfd_cell **wcd9xxx_dev,
+				    int *wcd9xxx_dev_size,
+				    int *wcd9xxx_dev_num_irqs)
 {
-	struct wcd9xx_codec_type *cdc = wcd9xxx_codecs;
-	int index;
+	int i;
 	int ret;
-	index = WCD9XXX_A_CHIP_ID_BYTE_0;
-	while (index <= WCD9XXX_A_CHIP_ID_BYTE_3) {
-		ret = wcd9xxx_reg_read(wcd9xxx, index);
+	i = WCD9XXX_A_CHIP_ID_BYTE_0;
+	while (i <= WCD9XXX_A_CHIP_ID_BYTE_3) {
+		ret = wcd9xxx_reg_read(wcd9xxx, i);
 		if (ret < 0)
 			goto exit;
-		wcd9xxx->idbyte[index-WCD9XXX_A_CHIP_ID_BYTE_0] = (u8)ret;
+		wcd9xxx->idbyte[i-WCD9XXX_A_CHIP_ID_BYTE_0] = (u8)ret;
 		pr_debug("%s: wcd9xx read = %x, byte = %x\n", __func__, ret,
-			index);
-		index++;
+			i);
+		i++;
 	}
 
 	/* Read codec version */
@@ -333,18 +342,20 @@
 	if (ret < 0)
 		goto exit;
 	wcd9xxx->version = (u8)ret & 0x1F;
-
-	while (cdc < (cdc + ARRAY_SIZE(wcd9xxx_codecs)) && cdc != NULL) {
-		if ((cdc->byte[0] == wcd9xxx->idbyte[0]) &&
-		    (cdc->byte[1] == wcd9xxx->idbyte[1]) &&
-		    (cdc->byte[2] == wcd9xxx->idbyte[2]) &&
-		    (cdc->byte[3] == wcd9xxx->idbyte[3])) {
-			pr_info("%s: codec is %s", __func__, cdc->dev->name);
-			*wcd9xxx_dev = cdc->dev;
-			*wcd9xxx_dev_size = cdc->size;
+	i = 0;
+	while (i < ARRAY_SIZE(wcd9xxx_codecs)) {
+		if ((wcd9xxx_codecs[i].byte[0] == wcd9xxx->idbyte[0]) &&
+		    (wcd9xxx_codecs[i].byte[1] == wcd9xxx->idbyte[1]) &&
+		    (wcd9xxx_codecs[i].byte[2] == wcd9xxx->idbyte[2]) &&
+		    (wcd9xxx_codecs[i].byte[3] == wcd9xxx->idbyte[3])) {
+			pr_info("%s: codec is %s", __func__,
+				wcd9xxx_codecs[i].dev->name);
+			*wcd9xxx_dev = wcd9xxx_codecs[i].dev;
+			*wcd9xxx_dev_size = wcd9xxx_codecs[i].size;
+			*wcd9xxx_dev_num_irqs = wcd9xxx_codecs[i].num_irqs;
 			break;
 		}
-		cdc++;
+		i++;
 	}
 	if (*wcd9xxx_dev == NULL || *wcd9xxx_dev_size == 0)
 		ret = -ENODEV;
@@ -358,7 +369,7 @@
 	return ret;
 }
 
-static int wcd9xxx_device_init(struct wcd9xxx *wcd9xxx, int irq)
+static int wcd9xxx_device_init(struct wcd9xxx *wcd9xxx)
 {
 	int ret;
 	struct mfd_cell *wcd9xxx_dev = NULL;
@@ -378,6 +389,11 @@
 
 	wcd9xxx_bring_up(wcd9xxx);
 
+	ret = wcd9xxx_check_codec_type(wcd9xxx, &wcd9xxx_dev, &wcd9xxx_dev_size,
+				       &wcd9xxx->num_irqs);
+	if (ret < 0)
+		goto err_irq;
+
 	if (wcd9xxx->irq != -1) {
 		ret = wcd9xxx_irq_init(wcd9xxx);
 		if (ret) {
@@ -385,11 +401,7 @@
 			goto err;
 		}
 	}
-	ret = wcd9xxx_check_codec_type(wcd9xxx, &wcd9xxx_dev,
-					&wcd9xxx_dev_size);
 
-	if (ret < 0)
-		goto err_irq;
 	ret = mfd_add_devices(wcd9xxx->dev, -1, wcd9xxx_dev, wcd9xxx_dev_size,
 			      NULL, 0);
 	if (ret != 0) {
@@ -538,6 +550,13 @@
 	}
 
 	wcd9xxx->num_of_supplies = 0;
+
+	if (ARRAY_SIZE(pdata->regulator) > MAX_REGULATOR) {
+		pr_err("%s: Array Size out of bound\n", __func__);
+		ret = -EINVAL;
+		goto err;
+	}
+
 	for (i = 0; i < ARRAY_SIZE(pdata->regulator); i++) {
 		if (pdata->regulator[i].name) {
 			wcd9xxx->supplies[i].supply = pdata->regulator[i].name;
@@ -794,10 +813,12 @@
 
 	wcd9xxx->read_dev = wcd9xxx_i2c_read;
 	wcd9xxx->write_dev = wcd9xxx_i2c_write;
-	wcd9xxx->irq = pdata->irq;
-	wcd9xxx->irq_base = pdata->irq_base;
+	if (!wcd9xxx->dev->of_node) {
+		wcd9xxx->irq = pdata->irq;
+		wcd9xxx->irq_base = pdata->irq_base;
+	}
 
-	ret = wcd9xxx_device_init(wcd9xxx, wcd9xxx->irq);
+	ret = wcd9xxx_device_init(wcd9xxx);
 	if (ret) {
 		pr_err("%s: error, initializing device failed\n", __func__);
 		goto err_device_init;
@@ -974,6 +995,20 @@
 	}
 	micbias->bias4_cfilt_sel = (u8)prop_val;
 
+	/* micbias external cap */
+	micbias->bias1_cap_mode =
+	    (of_property_read_bool(dev->of_node, "qcom,cdc-micbias1-ext-cap") ?
+	     MICBIAS_EXT_BYP_CAP : MICBIAS_NO_EXT_BYP_CAP);
+	micbias->bias2_cap_mode =
+	    (of_property_read_bool(dev->of_node, "qcom,cdc-micbias2-ext-cap") ?
+	     MICBIAS_EXT_BYP_CAP : MICBIAS_NO_EXT_BYP_CAP);
+	micbias->bias3_cap_mode =
+	    (of_property_read_bool(dev->of_node, "qcom,cdc-micbias3-ext-cap") ?
+	     MICBIAS_EXT_BYP_CAP : MICBIAS_NO_EXT_BYP_CAP);
+	micbias->bias4_cap_mode =
+	    (of_property_read_bool(dev->of_node, "qcom,cdc-micbias4-ext-cap") ?
+	     MICBIAS_EXT_BYP_CAP : MICBIAS_NO_EXT_BYP_CAP);
+
 	dev_dbg(dev, "ldoh_v  %u cfilt1_mv %u cfilt2_mv %u cfilt3_mv %u",
 		(u32)micbias->ldoh_v, (u32)micbias->cfilt1_mv,
 		(u32)micbias->cfilt2_mv, (u32)micbias->cfilt3_mv);
@@ -984,6 +1019,11 @@
 	dev_dbg(dev, "bias3_cfilt_sel %u bias4_cfilt_sel %u\n",
 		(u32)micbias->bias3_cfilt_sel, (u32)micbias->bias4_cfilt_sel);
 
+	dev_dbg(dev, "bias1_ext_cap %d bias2_ext_cap %d\n",
+		micbias->bias1_cap_mode, micbias->bias2_cap_mode);
+	dev_dbg(dev, "bias3_ext_cap %d bias4_ext_cap %d\n",
+		micbias->bias3_cap_mode, micbias->bias4_cap_mode);
+
 	return 0;
 }
 
@@ -1071,7 +1111,6 @@
 			pdata->reset_gpio);
 		goto err;
 	}
-	pdata->irq = -1;
 
 	ret = wcd9xxx_dt_parse_slim_interface_dev_info(dev,
 			&pdata->slimbus_slave_device);
@@ -1162,14 +1201,12 @@
 	}
 	wcd9xxx->read_dev = wcd9xxx_slim_read_device;
 	wcd9xxx->write_dev = wcd9xxx_slim_write_device;
-	wcd9xxx->irq = pdata->irq;
-	wcd9xxx->irq_base = pdata->irq_base;
 	wcd9xxx_pgd_la = wcd9xxx->slim->laddr;
-
-	if (pdata->num_irqs < TABLA_NUM_IRQS)
-		pr_warn("%s: Not enough interrupt lines allocated\n", __func__);
-
 	wcd9xxx->slim_slave = &pdata->slimbus_slave_device;
+	if (!wcd9xxx->dev->of_node) {
+		wcd9xxx->irq = pdata->irq;
+		wcd9xxx->irq_base = pdata->irq_base;
+	}
 
 	ret = slim_add_device(slim->ctrl, wcd9xxx->slim_slave);
 	if (ret) {
@@ -1189,14 +1226,11 @@
 	wcd9xxx_inf_la = wcd9xxx->slim_slave->laddr;
 	wcd9xxx_intf = WCD9XXX_INTERFACE_TYPE_SLIMBUS;
 
-	ret = wcd9xxx_device_init(wcd9xxx, wcd9xxx->irq);
+	ret = wcd9xxx_device_init(wcd9xxx);
 	if (ret) {
 		pr_err("%s: error, initializing device failed\n", __func__);
 		goto err_slim_add;
 	}
-
-	wcd9xxx_init_slimslave(wcd9xxx, wcd9xxx_pgd_la);
-
 #ifdef CONFIG_DEBUG_FS
 	debugCodec = wcd9xxx;
 
diff --git a/drivers/mfd/wcd9xxx-irq.c b/drivers/mfd/wcd9xxx-irq.c
index 68c4557..23e0fcc 100644
--- a/drivers/mfd/wcd9xxx-irq.c
+++ b/drivers/mfd/wcd9xxx-irq.c
@@ -18,26 +18,32 @@
 #include <linux/mfd/wcd9xxx/core.h>
 #include <linux/mfd/wcd9xxx/wcd9xxx_registers.h>
 #include <linux/mfd/wcd9xxx/wcd9310_registers.h>
+#include <linux/mfd/wcd9xxx/wcd9xxx-slimslave.h>
+#include <linux/delay.h>
+#include <linux/irqdomain.h>
 #include <linux/interrupt.h>
-
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/slab.h>
 #include <mach/cpuidle.h>
 
 #define BYTE_BIT_MASK(nr)		(1UL << ((nr) % BITS_PER_BYTE))
 #define BIT_BYTE(nr)			((nr) / BITS_PER_BYTE)
 
-struct wcd9xxx_irq {
-	bool level;
-};
+#define WCD9XXX_SYSTEM_RESUME_TIMEOUT_MS 100
 
-static struct wcd9xxx_irq wcd9xxx_irqs[TABLA_NUM_IRQS] = {
-	[0] = { .level = 1},
-/* All other wcd9xxx interrupts are edge triggered */
+#ifdef CONFIG_OF
+struct wcd9xxx_irq_drv_data {
+	struct irq_domain *domain;
+	int irq;
 };
+#endif
 
-static inline int irq_to_wcd9xxx_irq(struct wcd9xxx *wcd9xxx, int irq)
-{
-	return irq - wcd9xxx->irq_base;
-}
+static int virq_to_phyirq(struct wcd9xxx *wcd9xxx, int virq);
+static int phyirq_to_virq(struct wcd9xxx *wcd9xxx, int irq);
+static unsigned int wcd9xxx_irq_get_upstream_irq(struct wcd9xxx *wcd9xxx);
+static void wcd9xxx_irq_put_upstream_irq(struct wcd9xxx *wcd9xxx);
+static int wcd9xxx_map_irq(struct wcd9xxx *wcd9xxx, int irq);
 
 static void wcd9xxx_irq_lock(struct irq_data *data)
 {
@@ -50,14 +56,21 @@
 	struct wcd9xxx *wcd9xxx = irq_data_get_irq_chip_data(data);
 	int i;
 
+	if (ARRAY_SIZE(wcd9xxx->irq_masks_cur) > WCD9XXX_NUM_IRQ_REGS ||
+		ARRAY_SIZE(wcd9xxx->irq_masks_cache) > WCD9XXX_NUM_IRQ_REGS) {
+			pr_err("%s: Array Size out of bound\n", __func__);
+			 return;
+	}
+
 	for (i = 0; i < ARRAY_SIZE(wcd9xxx->irq_masks_cur); i++) {
 		/* If there's been a change in the mask write it back
 		 * to the hardware.
 		 */
 		if (wcd9xxx->irq_masks_cur[i] != wcd9xxx->irq_masks_cache[i]) {
 			wcd9xxx->irq_masks_cache[i] = wcd9xxx->irq_masks_cur[i];
-			wcd9xxx_reg_write(wcd9xxx, TABLA_A_INTR_MASK0+i,
-				wcd9xxx->irq_masks_cur[i]);
+			wcd9xxx_reg_write(wcd9xxx,
+					  WCD9XXX_A_INTR_MASK0 + i,
+					  wcd9xxx->irq_masks_cur[i]);
 		}
 	}
 
@@ -67,7 +80,7 @@
 static void wcd9xxx_irq_enable(struct irq_data *data)
 {
 	struct wcd9xxx *wcd9xxx = irq_data_get_irq_chip_data(data);
-	int wcd9xxx_irq = irq_to_wcd9xxx_irq(wcd9xxx, data->irq);
+	int wcd9xxx_irq = virq_to_phyirq(wcd9xxx, data->irq);
 	wcd9xxx->irq_masks_cur[BIT_BYTE(wcd9xxx_irq)] &=
 		~(BYTE_BIT_MASK(wcd9xxx_irq));
 }
@@ -75,9 +88,14 @@
 static void wcd9xxx_irq_disable(struct irq_data *data)
 {
 	struct wcd9xxx *wcd9xxx = irq_data_get_irq_chip_data(data);
-	int wcd9xxx_irq = irq_to_wcd9xxx_irq(wcd9xxx, data->irq);
+	int wcd9xxx_irq = virq_to_phyirq(wcd9xxx, data->irq);
 	wcd9xxx->irq_masks_cur[BIT_BYTE(wcd9xxx_irq)]
-			|= BYTE_BIT_MASK(wcd9xxx_irq);
+		|= BYTE_BIT_MASK(wcd9xxx_irq);
+}
+
+static void wcd9xxx_irq_mask(struct irq_data *d)
+{
+	/* do nothing but required as linux calls irq_mask without NULL check */
 }
 
 static struct irq_chip wcd9xxx_irq_chip = {
@@ -86,6 +104,7 @@
 	.irq_bus_sync_unlock = wcd9xxx_irq_sync_unlock,
 	.irq_disable = wcd9xxx_irq_disable,
 	.irq_enable = wcd9xxx_irq_enable,
+	.irq_mask = wcd9xxx_irq_mask,
 };
 
 enum wcd9xxx_pm_state wcd9xxx_pm_cmpxchg(struct wcd9xxx *wcd9xxx,
@@ -106,11 +125,17 @@
 {
 	enum wcd9xxx_pm_state os;
 
-	/* wcd9xxx_{lock/unlock}_sleep will be called by wcd9xxx_irq_thread
+	/*
+	 * wcd9xxx_{lock/unlock}_sleep will be called by wcd9xxx_irq_thread
 	 * and its subroutines only motly.
 	 * but btn0_lpress_fn is not wcd9xxx_irq_thread's subroutine and
-	 * it can race with wcd9xxx_irq_thread.
-	 * so need to embrace wlock_holders with mutex.
+	 * It can race with wcd9xxx_irq_thread.
+	 * So need to embrace wlock_holders with mutex.
+	 *
+	 * If system didn't resume, we can simply return false so codec driver's
+	 * IRQ handler can return without handling IRQ.
+	 * As interrupt line is still active, codec will have another IRQ to
+	 * retry shortly.
 	 */
 	mutex_lock(&wcd9xxx->pm_lock);
 	if (wcd9xxx->wlock_holders++ == 0) {
@@ -124,11 +149,11 @@
 						WCD9XXX_PM_AWAKE)) ==
 						    WCD9XXX_PM_SLEEPABLE ||
 			 (os == WCD9XXX_PM_AWAKE)),
-			5 * HZ)) {
-		pr_err("%s: system didn't resume within 5000ms, state %d, "
-		       "wlock %d\n", __func__, wcd9xxx->pm_state,
-		       wcd9xxx->wlock_holders);
-		WARN_ON(1);
+			msecs_to_jiffies(WCD9XXX_SYSTEM_RESUME_TIMEOUT_MS))) {
+		pr_warn("%s: system didn't resume within %dms, s %d, w %d\n",
+			__func__,
+			WCD9XXX_SYSTEM_RESUME_TIMEOUT_MS, wcd9xxx->pm_state,
+			wcd9xxx->wlock_holders);
 		wcd9xxx_unlock_sleep(wcd9xxx);
 		return false;
 	}
@@ -141,8 +166,14 @@
 {
 	mutex_lock(&wcd9xxx->pm_lock);
 	if (--wcd9xxx->wlock_holders == 0) {
-		wcd9xxx->pm_state = WCD9XXX_PM_SLEEPABLE;
-		pr_debug("%s: releasing wake lock\n", __func__);
+		pr_debug("%s: releasing wake lock pm_state %d -> %d\n",
+			 __func__, wcd9xxx->pm_state, WCD9XXX_PM_SLEEPABLE);
+		/*
+		 * if wcd9xxx_lock_sleep failed, pm_state would be still
+		 * WCD9XXX_PM_ASLEEP, don't overwrite
+		 */
+		if (likely(wcd9xxx->pm_state == WCD9XXX_PM_AWAKE))
+			wcd9xxx->pm_state = WCD9XXX_PM_SLEEPABLE;
 		pm_qos_update_request(&wcd9xxx->pm_qos_req,
 				PM_QOS_DEFAULT_VALUE);
 	}
@@ -153,62 +184,75 @@
 
 static void wcd9xxx_irq_dispatch(struct wcd9xxx *wcd9xxx, int irqbit)
 {
-	if ((irqbit <= TABLA_IRQ_MBHC_INSERTION) &&
-	    (irqbit >= TABLA_IRQ_MBHC_REMOVAL)) {
-		wcd9xxx_reg_write(wcd9xxx, TABLA_A_INTR_CLEAR0 +
-				  BIT_BYTE(irqbit), BYTE_BIT_MASK(irqbit));
+	if ((irqbit <= WCD9XXX_IRQ_MBHC_INSERTION) &&
+	    (irqbit >= WCD9XXX_IRQ_MBHC_REMOVAL)) {
+		wcd9xxx_reg_write(wcd9xxx, WCD9XXX_A_INTR_CLEAR0 +
+					   BIT_BYTE(irqbit),
+				  BYTE_BIT_MASK(irqbit));
 		if (wcd9xxx_get_intf_type() == WCD9XXX_INTERFACE_TYPE_I2C)
-			wcd9xxx_reg_write(wcd9xxx, TABLA_A_INTR_MODE, 0x02);
-		handle_nested_irq(wcd9xxx->irq_base + irqbit);
+			wcd9xxx_reg_write(wcd9xxx, WCD9XXX_A_INTR_MODE, 0x02);
+		handle_nested_irq(phyirq_to_virq(wcd9xxx, irqbit));
 	} else {
-		handle_nested_irq(wcd9xxx->irq_base + irqbit);
-		wcd9xxx_reg_write(wcd9xxx, TABLA_A_INTR_CLEAR0 +
-				  BIT_BYTE(irqbit), BYTE_BIT_MASK(irqbit));
+		handle_nested_irq(phyirq_to_virq(wcd9xxx, irqbit));
+		wcd9xxx_reg_write(wcd9xxx, WCD9XXX_A_INTR_CLEAR0 +
+					   BIT_BYTE(irqbit),
+				  BYTE_BIT_MASK(irqbit));
 		if (wcd9xxx_get_intf_type() == WCD9XXX_INTERFACE_TYPE_I2C)
-			wcd9xxx_reg_write(wcd9xxx, TABLA_A_INTR_MODE, 0x02);
+			wcd9xxx_reg_write(wcd9xxx, WCD9XXX_A_INTR_MODE, 0x02);
 	}
 }
 
+static int wcd9xxx_num_irq_regs(const struct wcd9xxx *wcd9xxx)
+{
+	return (wcd9xxx->num_irqs / 8) + ((wcd9xxx->num_irqs % 8) ? 1 : 0);
+}
+
 static irqreturn_t wcd9xxx_irq_thread(int irq, void *data)
 {
 	int ret;
-	struct wcd9xxx *wcd9xxx = data;
-	u8 status[WCD9XXX_NUM_IRQ_REGS];
 	int i;
+	struct wcd9xxx *wcd9xxx = data;
+	int num_irq_regs = wcd9xxx_num_irq_regs(wcd9xxx);
+	u8 status[num_irq_regs];
 
 	if (unlikely(wcd9xxx_lock_sleep(wcd9xxx) == false)) {
 		dev_err(wcd9xxx->dev, "Failed to hold suspend\n");
 		return IRQ_NONE;
 	}
-	ret = wcd9xxx_bulk_read(wcd9xxx, TABLA_A_INTR_STATUS0,
-			       WCD9XXX_NUM_IRQ_REGS, status);
+	ret = wcd9xxx_bulk_read(wcd9xxx, WCD9XXX_A_INTR_STATUS0,
+				num_irq_regs, status);
 	if (ret < 0) {
 		dev_err(wcd9xxx->dev, "Failed to read interrupt status: %d\n",
 			ret);
+		dev_err(wcd9xxx->dev, "Disable irq %d\n", wcd9xxx->irq);
+		disable_irq_wake(wcd9xxx->irq);
+		disable_irq_nosync(wcd9xxx->irq);
 		wcd9xxx_unlock_sleep(wcd9xxx);
 		return IRQ_NONE;
 	}
+
 	/* Apply masking */
-	for (i = 0; i < WCD9XXX_NUM_IRQ_REGS; i++)
+	for (i = 0; i < num_irq_regs; i++)
 		status[i] &= ~wcd9xxx->irq_masks_cur[i];
 
 	/* Find out which interrupt was triggered and call that interrupt's
 	 * handler function
 	 */
-	if (status[BIT_BYTE(TABLA_IRQ_SLIMBUS)] &
-	    BYTE_BIT_MASK(TABLA_IRQ_SLIMBUS))
-		wcd9xxx_irq_dispatch(wcd9xxx, TABLA_IRQ_SLIMBUS);
+	if (status[BIT_BYTE(WCD9XXX_IRQ_SLIMBUS)] &
+	    BYTE_BIT_MASK(WCD9XXX_IRQ_SLIMBUS))
+		wcd9xxx_irq_dispatch(wcd9xxx, WCD9XXX_IRQ_SLIMBUS);
 
 	/* Since codec has only one hardware irq line which is shared by
 	 * codec's different internal interrupts, so it's possible master irq
 	 * handler dispatches multiple nested irq handlers after breaking
 	 * order.  Dispatch MBHC interrupts order to follow MBHC state
 	 * machine's order */
-	for (i = TABLA_IRQ_MBHC_INSERTION; i >= TABLA_IRQ_MBHC_REMOVAL; i--) {
+	for (i = WCD9XXX_IRQ_MBHC_INSERTION;
+	     i >= WCD9XXX_IRQ_MBHC_REMOVAL; i--) {
 		if (status[BIT_BYTE(i)] & BYTE_BIT_MASK(i))
 			wcd9xxx_irq_dispatch(wcd9xxx, i);
 	}
-	for (i = TABLA_IRQ_BG_PRECHARGE; i < TABLA_NUM_IRQS; i++) {
+	for (i = WCD9XXX_IRQ_BG_PRECHARGE; i < wcd9xxx->num_irqs; i++) {
 		if (status[BIT_BYTE(i)] & BYTE_BIT_MASK(i))
 			wcd9xxx_irq_dispatch(wcd9xxx, i);
 	}
@@ -217,59 +261,105 @@
 	return IRQ_HANDLED;
 }
 
+void wcd9xxx_free_irq(struct wcd9xxx *wcd9xxx, int irq, void *data)
+{
+	free_irq(phyirq_to_virq(wcd9xxx, irq), data);
+}
+
+void wcd9xxx_enable_irq(struct wcd9xxx *wcd9xxx, int irq)
+{
+	enable_irq(phyirq_to_virq(wcd9xxx, irq));
+}
+
+void wcd9xxx_disable_irq(struct wcd9xxx *wcd9xxx, int irq)
+{
+	disable_irq_nosync(phyirq_to_virq(wcd9xxx, irq));
+}
+
+void wcd9xxx_disable_irq_sync(struct wcd9xxx *wcd9xxx, int irq)
+{
+	disable_irq(phyirq_to_virq(wcd9xxx, irq));
+}
+
+static int wcd9xxx_irq_setup_downstream_irq(struct wcd9xxx *wcd9xxx)
+{
+	int irq, virq, ret;
+
+	pr_debug("%s: enter\n", __func__);
+
+	for (irq = 0; irq < wcd9xxx->num_irqs; irq++) {
+		/* Map OF irq */
+		virq = wcd9xxx_map_irq(wcd9xxx, irq);
+		pr_debug("%s: irq %d -> %d\n", __func__, irq, virq);
+		if (virq == NO_IRQ) {
+			pr_err("%s, No interrupt specifier for irq %d\n",
+			       __func__, irq);
+			return NO_IRQ;
+		}
+
+		ret = irq_set_chip_data(virq, wcd9xxx);
+		if (ret) {
+			pr_err("%s: Failed to configure irq %d (%d)\n",
+			       __func__, irq, ret);
+			return ret;
+		}
+
+		if (wcd9xxx->irq_level_high[irq])
+			irq_set_chip_and_handler(virq, &wcd9xxx_irq_chip,
+						 handle_level_irq);
+		else
+			irq_set_chip_and_handler(virq, &wcd9xxx_irq_chip,
+						 handle_edge_irq);
+
+		irq_set_nested_thread(virq, 1);
+	}
+
+	pr_debug("%s: leave\n", __func__);
+
+	return 0;
+}
+
 int wcd9xxx_irq_init(struct wcd9xxx *wcd9xxx)
 {
-	int ret;
-	unsigned int i, cur_irq;
+	int i, ret;
+	u8 irq_level[wcd9xxx_num_irq_regs(wcd9xxx)];
 
 	mutex_init(&wcd9xxx->irq_lock);
 
+	wcd9xxx->irq = wcd9xxx_irq_get_upstream_irq(wcd9xxx);
 	if (!wcd9xxx->irq) {
-		dev_warn(wcd9xxx->dev,
-			 "No interrupt specified, no interrupts\n");
-		wcd9xxx->irq_base = 0;
-		return 0;
+		pr_warn("%s: irq driver is not yet initialized\n", __func__);
+		mutex_destroy(&wcd9xxx->irq_lock);
+		return -EPROBE_DEFER;
+	}
+	pr_debug("%s: probed irq %d\n", __func__, wcd9xxx->irq);
+
+	/* Setup downstream IRQs */
+	ret = wcd9xxx_irq_setup_downstream_irq(wcd9xxx);
+	if (ret) {
+		pr_err("%s: Failed to setup downstream IRQ\n", __func__);
+		wcd9xxx_irq_put_upstream_irq(wcd9xxx);
+		return ret;
 	}
 
-	if (!wcd9xxx->irq_base) {
-		dev_err(wcd9xxx->dev,
-			"No interrupt base specified, no interrupts\n");
-		return 0;
-	}
-	/* Mask the individual interrupt sources */
-	for (i = 0, cur_irq = wcd9xxx->irq_base; i < TABLA_NUM_IRQS; i++,
-		cur_irq++) {
+	/* All other wcd9xxx interrupts are edge triggered */
+	wcd9xxx->irq_level_high[0] = true;
 
-		irq_set_chip_data(cur_irq, wcd9xxx);
-
-		if (wcd9xxx_irqs[i].level)
-			irq_set_chip_and_handler(cur_irq, &wcd9xxx_irq_chip,
-					 handle_level_irq);
-		else
-			irq_set_chip_and_handler(cur_irq, &wcd9xxx_irq_chip,
-					 handle_edge_irq);
-
-		irq_set_nested_thread(cur_irq, 1);
-
-		/* ARM needs us to explicitly flag the IRQ as valid
-		 * and will set them noprobe when we do so. */
-#ifdef CONFIG_ARM
-		set_irq_flags(cur_irq, IRQF_VALID);
-#else
-		set_irq_noprobe(cur_irq);
-#endif
-
+	/* mask all the interrupts */
+	memset(irq_level, 0, wcd9xxx_num_irq_regs(wcd9xxx));
+	for (i = 0; i < wcd9xxx->num_irqs; i++) {
 		wcd9xxx->irq_masks_cur[BIT_BYTE(i)] |= BYTE_BIT_MASK(i);
 		wcd9xxx->irq_masks_cache[BIT_BYTE(i)] |= BYTE_BIT_MASK(i);
-		wcd9xxx->irq_level[BIT_BYTE(i)] |= wcd9xxx_irqs[i].level <<
-			(i % BITS_PER_BYTE);
+		irq_level[BIT_BYTE(i)] |=
+		    wcd9xxx->irq_level_high[i] << (i % BITS_PER_BYTE);
 	}
-	for (i = 0; i < WCD9XXX_NUM_IRQ_REGS; i++) {
+
+	for (i = 0; i < wcd9xxx_num_irq_regs(wcd9xxx); i++) {
 		/* Initialize interrupt mask and level registers */
-		wcd9xxx_reg_write(wcd9xxx, TABLA_A_INTR_LEVEL0 + i,
-			wcd9xxx->irq_level[i]);
-		wcd9xxx_reg_write(wcd9xxx, TABLA_A_INTR_MASK0 + i,
-			wcd9xxx->irq_masks_cur[i]);
+		wcd9xxx_reg_write(wcd9xxx, WCD9XXX_A_INTR_LEVEL0 + i,
+				  irq_level[i]);
+		wcd9xxx_reg_write(wcd9xxx, WCD9XXX_A_INTR_MASK0 + i,
+				  wcd9xxx->irq_masks_cur[i]);
 	}
 
 	ret = request_threaded_irq(wcd9xxx->irq, NULL, wcd9xxx_irq_thread,
@@ -294,18 +384,226 @@
 			free_irq(wcd9xxx->irq, wcd9xxx);
 	}
 
-	if (ret)
+	if (ret) {
+		pr_err("%s: Failed to init wcd9xxx irq\n", __func__);
+		wcd9xxx_irq_put_upstream_irq(wcd9xxx);
 		mutex_destroy(&wcd9xxx->irq_lock);
+	}
 
 	return ret;
 }
 
+int wcd9xxx_request_irq(struct wcd9xxx *wcd9xxx, int irq, irq_handler_t handler,
+			const char *name, void *data)
+{
+	int virq;
+
+	virq = phyirq_to_virq(wcd9xxx, irq);
+
+	/*
+	 * ARM needs us to explicitly flag the IRQ as valid
+	 * and will set them noprobe when we do so.
+	 */
+#ifdef CONFIG_ARM
+	set_irq_flags(virq, IRQF_VALID);
+#else
+	set_irq_noprobe(virq);
+#endif
+
+	return request_threaded_irq(virq, NULL, handler, IRQF_TRIGGER_RISING,
+				    name, data);
+}
+
 void wcd9xxx_irq_exit(struct wcd9xxx *wcd9xxx)
 {
 	if (wcd9xxx->irq) {
 		disable_irq_wake(wcd9xxx->irq);
 		free_irq(wcd9xxx->irq, wcd9xxx);
+		/* Release parent's of node */
+		wcd9xxx_irq_put_upstream_irq(wcd9xxx);
 		device_init_wakeup(wcd9xxx->dev, 0);
 	}
 	mutex_destroy(&wcd9xxx->irq_lock);
 }
+
+#ifndef CONFIG_OF
+static int phyirq_to_virq(struct wcd9xxx *wcd9xxx, int offset)
+{
+	return wcd9xxx->irq_base + offset;
+}
+
+static int virq_to_phyirq(struct wcd9xxx *wcd9xxx, int virq)
+{
+	return virq - wcd9xxx->irq_base;
+}
+
+static unsigned int wcd9xxx_irq_get_upstream_irq(struct wcd9xxx *wcd9xxx)
+{
+	return wcd9xxx->irq;
+}
+
+static void wcd9xxx_irq_put_upstream_irq(struct wcd9xxx *wcd9xxx)
+{
+	/* Do nothing */
+}
+
+static int wcd9xxx_map_irq(struct wcd9xxx *wcd9xxx, int irq)
+{
+	return phyirq_to_virq(wcd9xxx, irq);
+}
+#else
+int __init wcd9xxx_irq_of_init(struct device_node *node,
+			       struct device_node *parent)
+{
+	struct wcd9xxx_irq_drv_data *data;
+
+	pr_debug("%s: node %s, node parent %s\n", __func__,
+		 node->name, node->parent->name);
+
+	data = kzalloc(sizeof(*data), GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	/*
+	 * wcd9xxx_intc interrupt controller supports N to N irq mapping with
+	 * single cell binding with irq numbers(offsets) only.
+	 * Use irq_domain_simple_ops that has irq_domain_simple_map and
+	 * irq_domain_xlate_onetwocell.
+	 */
+	data->domain = irq_domain_add_linear(node, WCD9XXX_MAX_NUM_IRQS,
+					     &irq_domain_simple_ops, data);
+	if (!data->domain) {
+		kfree(data);
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+static struct wcd9xxx_irq_drv_data *
+wcd9xxx_get_irq_drv_d(const struct wcd9xxx *wcd9xxx)
+{
+	struct device_node *pnode;
+	struct irq_domain *domain;
+
+	pnode = of_irq_find_parent(wcd9xxx->dev->of_node);
+	/* Shouldn't happen */
+	if (unlikely(!pnode))
+		return NULL;
+
+	domain = irq_find_host(pnode);
+	return (struct wcd9xxx_irq_drv_data *)domain->host_data;
+}
+
+static int phyirq_to_virq(struct wcd9xxx *wcd9xxx, int offset)
+{
+	struct wcd9xxx_irq_drv_data *data;
+
+	data = wcd9xxx_get_irq_drv_d(wcd9xxx);
+	if (!data) {
+		pr_warn("%s: not registered to interrupt controller\n",
+			__func__);
+		return -EINVAL;
+	}
+	return irq_linear_revmap(data->domain, offset);
+}
+
+static int virq_to_phyirq(struct wcd9xxx *wcd9xxx, int virq)
+{
+	struct irq_data *irq_data = irq_get_irq_data(virq);
+	return irq_data->hwirq;
+}
+
+static unsigned int wcd9xxx_irq_get_upstream_irq(struct wcd9xxx *wcd9xxx)
+{
+	struct wcd9xxx_irq_drv_data *data;
+
+	/* Hold parent's of node */
+	if (!of_node_get(of_irq_find_parent(wcd9xxx->dev->of_node)))
+		return -EINVAL;
+
+	data = wcd9xxx_get_irq_drv_d(wcd9xxx);
+	if (!data) {
+		pr_err("%s: interrupt controller is not registerd\n", __func__);
+		return 0;
+	}
+
+	rmb();
+	return data->irq;
+}
+
+static void wcd9xxx_irq_put_upstream_irq(struct wcd9xxx *wcd9xxx)
+{
+	/* Hold parent's of node */
+	of_node_put(of_irq_find_parent(wcd9xxx->dev->of_node));
+}
+
+static int wcd9xxx_map_irq(struct wcd9xxx *wcd9xxx, int irq)
+{
+	return of_irq_to_resource(wcd9xxx->dev->of_node, irq, NULL);
+}
+
+static int __devinit wcd9xxx_irq_probe(struct platform_device *pdev)
+{
+	int irq;
+	struct irq_domain *domain;
+	struct wcd9xxx_irq_drv_data *data;
+	int ret = -EINVAL;
+
+	irq = platform_get_irq_byname(pdev, "cdc-int");
+	if (irq < 0) {
+		dev_err(&pdev->dev, "%s: Couldn't find cdc-int node(%d)\n",
+			__func__, irq);
+		return -EINVAL;
+	} else {
+		dev_dbg(&pdev->dev, "%s: virq = %d\n", __func__, irq);
+		domain = irq_find_host(pdev->dev.of_node);
+		data = (struct wcd9xxx_irq_drv_data *)domain->host_data;
+		data->irq = irq;
+		wmb();
+		ret = 0;
+	}
+
+	return ret;
+}
+
+static int wcd9xxx_irq_remove(struct platform_device *pdev)
+{
+	struct irq_domain *domain;
+	struct wcd9xxx_irq_drv_data *data;
+
+	domain = irq_find_host(pdev->dev.of_node);
+	data = (struct wcd9xxx_irq_drv_data *)domain->host_data;
+	data->irq = 0;
+	wmb();
+
+	return 0;
+}
+
+static const struct of_device_id of_match[] = {
+	{ .compatible = "qcom,wcd9xxx-irq" },
+	{ }
+};
+
+static struct platform_driver wcd9xxx_irq_driver = {
+	.probe = wcd9xxx_irq_probe,
+	.remove = wcd9xxx_irq_remove,
+	.driver = {
+		.name = "wcd9xxx_intc",
+		.owner = THIS_MODULE,
+		.of_match_table = of_match_ptr(of_match),
+	},
+};
+
+static int wcd9xxx_irq_drv_init(void)
+{
+	return platform_driver_register(&wcd9xxx_irq_driver);
+}
+subsys_initcall(wcd9xxx_irq_drv_init);
+
+static void wcd9xxx_irq_drv_exit(void)
+{
+	platform_driver_unregister(&wcd9xxx_irq_driver);
+}
+module_exit(wcd9xxx_irq_drv_exit);
+#endif /* CONFIG_OF */
diff --git a/drivers/mfd/wcd9xxx-slimslave.c b/drivers/mfd/wcd9xxx-slimslave.c
index b4cf435..948cb6e 100644
--- a/drivers/mfd/wcd9xxx-slimslave.c
+++ b/drivers/mfd/wcd9xxx-slimslave.c
@@ -16,44 +16,20 @@
 
 #define WCD9XXX_CHIP_ID_TAIKO 0x00000201
 
-struct wcd9xxx_slim_sch_rx {
-	u32 sph;
-	u32 ch_num;
-	u16 ch_h;
-	u16 grph;
-};
-
-struct wcd9xxx_slim_sch_tx {
-	u32 sph;
-	u32 ch_num;
-	u16 ch_h;
-	u16 grph;
-};
-
 struct wcd9xxx_slim_sch {
-	struct wcd9xxx_slim_sch_rx rx[SLIM_MAX_RX_PORTS];
-	struct wcd9xxx_slim_sch_tx tx[SLIM_MAX_TX_PORTS];
-
-	u16 rx_port_start_offset;
-	u16 num_rx_slave_port;
-	u16 port_ch_0_start_port_id;
-	u16 port_ch_0_end_port_id;
-	u16 pgd_tx_port_ch_1_end_port_id;
 	u16 rx_port_ch_reg_base;
 	u16 port_tx_cfg_reg_base;
 	u16 port_rx_cfg_reg_base;
-	int number_of_tx_slave_dev_ports;
-	int number_of_rx_slave_dev_ports;
 };
 
 static struct wcd9xxx_slim_sch sh_ch;
 
-static int wcd9xxx_alloc_slim_sh_ch_rx(struct wcd9xxx *wcd9xxx,
-				       u8 wcd9xxx_pgd_la);
-static int wcd9xxx_alloc_slim_sh_ch_tx(struct wcd9xxx *wcd9xxx,
-					u8 wcd9xxx_pgd_la);
-static int wcd9xxx_dealloc_slim_sh_ch_rx(struct wcd9xxx *wcd9xxx);
-static int wcd9xxx_dealloc_slim_sh_ch_tx(struct wcd9xxx *wcd9xxx);
+static int wcd9xxx_alloc_slim_sh_ch(struct wcd9xxx *wcd9xxx,
+				    u8 wcd9xxx_pgd_la, u32 cnt,
+				    struct wcd9xxx_ch *channels, u32 path);
+
+static int wcd9xxx_dealloc_slim_sh_ch(struct slim_device *slim,
+				      u32 cnt, struct wcd9xxx_ch *channels);
 
 static int wcd9xxx_configure_ports(struct wcd9xxx *wcd9xxx)
 {
@@ -65,55 +41,27 @@
 	id = cpu_to_be32(id);
 	pr_debug("%s: chip id 0x%08x\n", __func__, id);
 	if (id != WCD9XXX_CHIP_ID_TAIKO) {
-		sh_ch.rx_port_start_offset =
-		    TABLA_SB_PGD_OFFSET_OF_RX_SLAVE_DEV_PORTS;
-		sh_ch.num_rx_slave_port =
-		    TABLA_SB_PGD_MAX_NUMBER_OF_RX_SLAVE_DEV_PORTS;
-		sh_ch.port_ch_0_start_port_id =
-		    TABLA_SB_PGD_RX_PORT_MULTI_CHANNEL_0_START_PORT_ID;
-		sh_ch.port_ch_0_end_port_id =
-		    TABLA_SB_PGD_RX_PORT_MULTI_CHANNEL_0_END_PORT_ID;
-		sh_ch.pgd_tx_port_ch_1_end_port_id =
-		    TABLA_SB_PGD_TX_PORT_MULTI_CHANNEL_1_END_PORT_ID;
-
-		sh_ch.rx_port_ch_reg_base =
-		    0x180 + (TABLA_SB_PGD_OFFSET_OF_RX_SLAVE_DEV_PORTS * 4);
-		sh_ch.port_rx_cfg_reg_base =
-		    0x040 + (TABLA_SB_PGD_OFFSET_OF_RX_SLAVE_DEV_PORTS);
-		sh_ch.port_tx_cfg_reg_base = 0x040;
-
-		sh_ch.number_of_tx_slave_dev_ports =
-		    TABLA_SB_PGD_MAX_NUMBER_OF_TX_SLAVE_DEV_PORTS;
-		sh_ch.number_of_rx_slave_dev_ports =
-		    TABLA_SB_PGD_MAX_NUMBER_OF_RX_SLAVE_DEV_PORTS;
-	} else {
-		sh_ch.rx_port_start_offset =
-		    TAIKO_SB_PGD_OFFSET_OF_RX_SLAVE_DEV_PORTS;
-		sh_ch.num_rx_slave_port =
-		    TAIKO_SB_PGD_MAX_NUMBER_OF_RX_SLAVE_DEV_PORTS;
-		sh_ch.port_ch_0_start_port_id =
-		    TAIKO_SB_PGD_RX_PORT_MULTI_CHANNEL_0_START_PORT_ID;
-		sh_ch.port_ch_0_end_port_id =
-		    TAIKO_SB_PGD_RX_PORT_MULTI_CHANNEL_0_END_PORT_ID;
-		sh_ch.pgd_tx_port_ch_1_end_port_id =
-		    TAIKO_SB_PGD_TX_PORT_MULTI_CHANNEL_1_END_PORT_ID;
-
-		sh_ch.rx_port_ch_reg_base = 0x180;
+		sh_ch.rx_port_ch_reg_base = 0x180 ;
 		sh_ch.port_rx_cfg_reg_base = 0x040;
+		sh_ch.port_tx_cfg_reg_base = 0x040;
+	} else {
+		sh_ch.rx_port_ch_reg_base =
+			0x180 - (TAIKO_SB_PGD_OFFSET_OF_RX_SLAVE_DEV_PORTS * 4);
+		sh_ch.port_rx_cfg_reg_base =
+			0x040 - TAIKO_SB_PGD_OFFSET_OF_RX_SLAVE_DEV_PORTS ;
 		sh_ch.port_tx_cfg_reg_base = 0x050;
-
-		sh_ch.number_of_tx_slave_dev_ports =
-		    TAIKO_SB_PGD_MAX_NUMBER_OF_TX_SLAVE_DEV_PORTS;
-		sh_ch.number_of_rx_slave_dev_ports =
-		    TAIKO_SB_PGD_MAX_NUMBER_OF_RX_SLAVE_DEV_PORTS;
 	}
 
 	return 0;
 }
 
-int wcd9xxx_init_slimslave(struct wcd9xxx *wcd9xxx, u8 wcd9xxx_pgd_la)
+
+int wcd9xxx_init_slimslave(struct wcd9xxx *wcd9xxx, u8 wcd9xxx_pgd_la,
+			   unsigned int tx_num, unsigned int *tx_slot,
+			   unsigned int rx_num, unsigned int *rx_slot)
 {
 	int ret = 0;
+	int i;
 
 	ret = wcd9xxx_configure_ports(wcd9xxx);
 	if (ret) {
@@ -122,125 +70,106 @@
 		goto err;
 	}
 
-	ret = wcd9xxx_alloc_slim_sh_ch_rx(wcd9xxx, wcd9xxx_pgd_la);
-	if (ret) {
-		pr_err("%s: Failed to alloc rx slimbus shared channels\n",
-		       __func__);
-		goto err;
+	if (wcd9xxx->rx_chs) {
+		wcd9xxx->num_rx_port = rx_num;
+		for (i = 0; i < rx_num; i++) {
+			wcd9xxx->rx_chs[i].ch_num = rx_slot[i];
+			INIT_LIST_HEAD(&wcd9xxx->rx_chs[i].list);
+		}
+		ret = wcd9xxx_alloc_slim_sh_ch(wcd9xxx, wcd9xxx_pgd_la,
+						wcd9xxx->num_rx_port,
+						wcd9xxx->rx_chs,
+						SLIM_SINK);
+		if (ret) {
+			pr_err("%s: Failed to alloc %d rx slimbus channels\n",
+				__func__, wcd9xxx->num_rx_port);
+			kfree(wcd9xxx->rx_chs);
+			wcd9xxx->rx_chs = NULL;
+			wcd9xxx->num_rx_port = 0;
+		}
+	} else {
+		pr_err("Not able to allocate memory for %d slimbus rx ports\n",
+			wcd9xxx->num_rx_port);
 	}
-	ret = wcd9xxx_alloc_slim_sh_ch_tx(wcd9xxx, wcd9xxx_pgd_la);
-	if (ret) {
-		pr_err("%s: Failed to alloc tx slimbus shared channels\n",
-		       __func__);
-		goto tx_err;
+
+	if (wcd9xxx->tx_chs) {
+		wcd9xxx->num_tx_port = tx_num;
+		for (i = 0; i < tx_num; i++) {
+			wcd9xxx->tx_chs[i].ch_num = tx_slot[i];
+			INIT_LIST_HEAD(&wcd9xxx->tx_chs[i].list);
+		}
+		ret = wcd9xxx_alloc_slim_sh_ch(wcd9xxx, wcd9xxx_pgd_la,
+						wcd9xxx->num_tx_port,
+						wcd9xxx->tx_chs,
+						SLIM_SRC);
+		if (ret) {
+			pr_err("%s: Failed to alloc %d tx slimbus channels\n",
+				__func__, wcd9xxx->num_tx_port);
+			kfree(wcd9xxx->tx_chs);
+			wcd9xxx->tx_chs = NULL;
+			wcd9xxx->num_tx_port = 0;
+		}
+	} else {
+		pr_err("Not able to allocate memory for %d slimbus tx ports\n",
+			wcd9xxx->num_tx_port);
 	}
+
 	return 0;
-tx_err:
-	wcd9xxx_dealloc_slim_sh_ch_rx(wcd9xxx);
 err:
 	return ret;
 }
 
-
 int wcd9xxx_deinit_slimslave(struct wcd9xxx *wcd9xxx)
 {
-	int ret = 0;
-	ret = wcd9xxx_dealloc_slim_sh_ch_rx(wcd9xxx);
-	if (ret < 0) {
-		pr_err("%s: fail to dealloc rx slim ports\n", __func__);
-		goto err;
+	if (wcd9xxx->num_rx_port) {
+		wcd9xxx_dealloc_slim_sh_ch(wcd9xxx->slim,
+					wcd9xxx->num_rx_port,
+					wcd9xxx->rx_chs);
+		wcd9xxx->num_rx_port = 0;
 	}
-	ret = wcd9xxx_dealloc_slim_sh_ch_tx(wcd9xxx);
-	if (ret < 0) {
-		pr_err("%s: fail to dealloc tx slim ports\n", __func__);
-		goto err;
+	if (wcd9xxx->num_tx_port) {
+		wcd9xxx_dealloc_slim_sh_ch(wcd9xxx->slim,
+					wcd9xxx->num_tx_port,
+					wcd9xxx->tx_chs);
+		wcd9xxx->num_tx_port = 0;
 	}
-err:
-	return ret;
-}
-
-int wcd9xxx_get_channel(struct wcd9xxx *wcd9xxx, unsigned int *rx_ch,
-			unsigned int *tx_ch)
-{
-	int ch_idx = 0;
-	struct wcd9xxx_slim_sch_rx *rx = sh_ch.rx;
-	struct wcd9xxx_slim_sch_tx *tx = sh_ch.tx;
-
-	for (ch_idx = 0; ch_idx < sh_ch.number_of_rx_slave_dev_ports; ch_idx++)
-		rx_ch[ch_idx] = rx[ch_idx].ch_num;
-	for (ch_idx = 0; ch_idx < sh_ch.number_of_tx_slave_dev_ports; ch_idx++)
-		tx_ch[ch_idx] = tx[ch_idx].ch_num;
 	return 0;
 }
 
-static int wcd9xxx_alloc_slim_sh_ch_rx(struct wcd9xxx *wcd9xxx,
-				       u8 wcd9xxx_pgd_la)
+
+static int wcd9xxx_alloc_slim_sh_ch(struct wcd9xxx *wcd9xxx,
+				    u8 wcd9xxx_pgd_la, u32 cnt,
+				    struct wcd9xxx_ch *channels, u32 path)
 {
 	int ret = 0;
-	u8 ch_idx ;
-	u16 slave_port_id = 0;
-	struct wcd9xxx_slim_sch_rx *rx = sh_ch.rx;
+	u32 ch_idx ;
 
-	/*
-	 * DSP requires channel number to be between 128 and 255.
+	/* The slimbus channel allocation seem take longer time
+	 * so do the allocation up front to avoid delay in start of
+	 * playback
 	 */
 	pr_debug("%s: pgd_la[%d]\n", __func__, wcd9xxx_pgd_la);
-	for (ch_idx = 0; ch_idx < sh_ch.number_of_rx_slave_dev_ports;
-	     ch_idx++) {
-		slave_port_id = (ch_idx + sh_ch.rx_port_start_offset);
-		rx[ch_idx].ch_num = slave_port_id + BASE_CH_NUM;
-		ret = slim_get_slaveport(wcd9xxx_pgd_la, slave_port_id,
-					&rx[ch_idx].sph, SLIM_SINK);
+	for (ch_idx = 0; ch_idx < cnt; ch_idx++) {
+		ret = slim_get_slaveport(wcd9xxx_pgd_la,
+					channels[ch_idx].port,
+					&channels[ch_idx].sph, path);
+		pr_debug("%s: pgd_la[%d] channels[%d].port[%d]\n"
+			"channels[%d].sph[%d] path[%d]\n",
+			__func__, wcd9xxx_pgd_la, ch_idx,
+			channels[ch_idx].port,
+			ch_idx, channels[ch_idx].sph, path);
 		if (ret < 0) {
 			pr_err("%s: slave port failure id[%d] ret[%d]\n",
-			       __func__, slave_port_id, ret);
+				__func__, channels[ch_idx].ch_num, ret);
 			goto err;
 		}
 
-		ret = slim_query_ch(wcd9xxx->slim, rx[ch_idx].ch_num,
-				    &rx[ch_idx].ch_h);
+		ret = slim_query_ch(wcd9xxx->slim,
+				    channels[ch_idx].ch_num,
+				    &channels[ch_idx].ch_h);
 		if (ret < 0) {
 			pr_err("%s: slim_query_ch failed ch-num[%d] ret[%d]\n",
-			       __func__, rx[ch_idx].ch_num, ret);
-			goto err;
-		}
-		pr_debug("%s:ch_num=%d ch_h=%d sph=%d la=%d slave_port_id %d\n",
-			 __func__, rx[ch_idx].ch_num, rx[ch_idx].ch_h,
-			 rx[ch_idx].sph, wcd9xxx_pgd_la, slave_port_id);
-	}
-err:
-	return ret;
-}
-
-static int wcd9xxx_alloc_slim_sh_ch_tx(struct wcd9xxx *wcd9xxx,
-				       u8 wcd9xxx_pgd_la)
-{
-	int ret = 0;
-	u8 ch_idx;
-	struct wcd9xxx_slim_sch_tx *tx = sh_ch.tx;
-	u16 slave_port_id = 0;
-
-	pr_debug("%s: pgd_la[%d]\n", __func__, wcd9xxx_pgd_la);
-	/* DSP requires channel number to be between 128 and 255. For RX port
-	 * use channel numbers from 138 to 144, for TX port
-	 * use channel numbers from 128 to 137
-	 */
-	for (ch_idx = 0; ch_idx < sh_ch.number_of_tx_slave_dev_ports;
-	     ch_idx++) {
-		slave_port_id = ch_idx;
-		tx[ch_idx].ch_num = slave_port_id + BASE_CH_NUM;
-		ret = slim_get_slaveport(wcd9xxx_pgd_la, slave_port_id,
-					 &tx[ch_idx].sph, SLIM_SRC);
-		if (ret < 0) {
-			pr_err("%s: slave port failure id[%d] ret[%d]\n",
-			       __func__, slave_port_id, ret);
-			goto err;
-		}
-		ret = slim_query_ch(wcd9xxx->slim, tx[ch_idx].ch_num,
-				    &tx[ch_idx].ch_h);
-		if (ret < 0) {
-			pr_err("%s: slim_query_ch failed ch-num[%d] ret[%d]\n",
-			       __func__, tx[ch_idx].ch_num, ret);
+				__func__, channels[ch_idx].ch_num, ret);
 			goto err;
 		}
 	}
@@ -248,116 +177,46 @@
 	return ret;
 }
 
-static int wcd9xxx_dealloc_slim_sh_ch_rx(struct wcd9xxx *wcd9xxx)
+static int wcd9xxx_dealloc_slim_sh_ch(struct slim_device *slim,
+			u32 cnt, struct wcd9xxx_ch *channels)
 {
 	int idx = 0;
 	int ret = 0;
-	struct wcd9xxx_slim_sch_rx *rx = sh_ch.rx;
 	/* slim_dealloc_ch */
-	for (idx = 0; idx < sh_ch.number_of_rx_slave_dev_ports; idx++) {
-		ret = slim_dealloc_ch(wcd9xxx->slim, rx[idx].ch_h);
+	for (idx = 0; idx < cnt; idx++) {
+		ret = slim_dealloc_ch(slim, channels[idx].ch_h);
 		if (ret < 0) {
 			pr_err("%s: slim_dealloc_ch fail ret[%d] ch_h[%d]\n",
-				__func__, ret, rx[idx].ch_h);
+				__func__, ret, channels[idx].ch_h);
 		}
 	}
-	memset(sh_ch.rx, 0, sizeof(sh_ch.rx));
-	return ret;
-}
-
-static int wcd9xxx_dealloc_slim_sh_ch_tx(struct wcd9xxx *wcd9xxx)
-{
-	int idx = 0;
-	int ret = 0;
-	struct wcd9xxx_slim_sch_tx *tx = sh_ch.tx;
-	/* slim_dealloc_ch */
-	for (idx = 0; idx < sh_ch.number_of_tx_slave_dev_ports; idx++) {
-		ret = slim_dealloc_ch(wcd9xxx->slim, tx[idx].ch_h);
-		if (ret < 0) {
-			pr_err("%s: slim_dealloc_ch fail ret[%d] ch_h[%d]\n",
-				__func__, ret, tx[idx].ch_h);
-		}
-	}
-	memset(sh_ch.tx, 0, sizeof(sh_ch.tx));
 	return ret;
 }
 
 /* Enable slimbus slave device for RX path */
-int wcd9xxx_cfg_slim_sch_rx(struct wcd9xxx *wcd9xxx, unsigned int *ch_num,
-			    unsigned int ch_cnt, unsigned int rate)
+int wcd9xxx_cfg_slim_sch_rx(struct wcd9xxx *wcd9xxx,
+			    struct list_head *wcd9xxx_ch_list,
+			    unsigned int rate, unsigned int bit_width,
+			    u16 *grph)
 {
-	u8 i;
-	u16 grph;
-	u32 sph[SLIM_MAX_RX_PORTS] = {0};
+	u8 ch_cnt = 0;
 	u16 ch_h[SLIM_MAX_RX_PORTS] = {0};
-	u16 slave_port_id;
-	u8  payload_rx = 0, wm_payload = 0;
-	int ret, idx = 0;
-	unsigned short  multi_chan_cfg_reg_addr;
-	struct wcd9xxx_slim_sch_rx *rx = sh_ch.rx;
+	u8  payload = 0;
+	u16 codec_port = 0;
+	int ret;
 	struct slim_ch prop;
+	struct wcd9xxx_ch *rx;
 
 	/* Configure slave interface device */
-	pr_debug("%s: ch_cnt[%d] rate=%d\n", __func__, ch_cnt, rate);
 
-	for (i = 0; i < ch_cnt; i++) {
-		idx = (ch_num[i] - BASE_CH_NUM - sh_ch.rx_port_start_offset);
-		ch_h[i] = rx[idx].ch_h;
-		sph[i] = rx[idx].sph;
-		slave_port_id = idx;
-		pr_debug("%s: idx %d, ch_h %d, sph %d\n",
-			 __func__, idx, ch_h[i], sph[i]);
-		if ((slave_port_id > sh_ch.num_rx_slave_port)) {
-			pr_err("Slimbus: invalid slave port id: %d",
-			       slave_port_id);
-			ret = -EINVAL;
-			goto err;
-		}
-		slave_port_id += sh_ch.rx_port_start_offset;
-		pr_debug("%s: slave_port_id %d\n", __func__, slave_port_id);
-		/* look for the valid port range and chose the
-		 * payload accordingly
-		 */
-		if ((slave_port_id > sh_ch.pgd_tx_port_ch_1_end_port_id) &&
-		    (slave_port_id <= sh_ch.port_ch_0_end_port_id)) {
-			payload_rx = payload_rx |
-				(1 << (slave_port_id -
-				      sh_ch.port_ch_0_start_port_id));
-		} else {
-			ret = -EINVAL;
-			goto err;
-		}
-
-		multi_chan_cfg_reg_addr =
-		    SB_PGD_RX_PORT_MULTI_CHANNEL_0(sh_ch.rx_port_ch_reg_base,
-						   idx);
-		pr_debug("%s: multi_chan_cfg_reg_addr 0x%x\n", __func__,
-			 multi_chan_cfg_reg_addr);
-
-		/* write to interface device */
-		ret = wcd9xxx_interface_reg_write(wcd9xxx,
-						  multi_chan_cfg_reg_addr,
-						  payload_rx);
-		if (ret < 0) {
-			pr_err("%s:Intf-dev fail reg[%d] payload[%d] ret[%d]\n",
-			       __func__, multi_chan_cfg_reg_addr,
-			       payload_rx, ret);
-			goto err;
-		}
-		/* configure the slave port for water mark and enable*/
-		wm_payload = (SLAVE_PORT_WATER_MARK_VALUE <<
-			      SLAVE_PORT_WATER_MARK_SHIFT) + SLAVE_PORT_ENABLE;
-		ret = wcd9xxx_interface_reg_write(
-				wcd9xxx,
-				SB_PGD_PORT_CFG_BYTE_ADDR(
-				    sh_ch.port_rx_cfg_reg_base, idx),
-				wm_payload);
-		if (ret < 0) {
-			pr_err("%s:watermark set failure for port[%d] ret[%d]",
-						__func__, slave_port_id, ret);
-		}
+	list_for_each_entry(rx, wcd9xxx_ch_list, list) {
+		payload |= 1 << rx->shift;
+		ch_h[ch_cnt] = rx->ch_h;
+		ch_cnt++;
+		pr_debug("list ch->ch_h %d ch->sph %d\n", rx->ch_h, rx->sph);
 	}
-
+	pr_debug("%s: ch_cnt[%d] rate=%d WATER_MARK_VAL %d\n",
+		 __func__, ch_cnt, rate, WATER_MARK_VAL);
 	/* slim_define_ch api */
 	prop.prot = SLIM_AUTO_ISO;
 	prop.baser = SLIM_RATE_4000HZ;
@@ -366,130 +225,97 @@
 	prop.ratem = (rate/4000);
 	prop.sampleszbits = 16;
 
-	ret = slim_define_ch(wcd9xxx->slim, &prop, ch_h, ch_cnt, true, &grph);
+	pr_debug("Before slim_define_ch:\n"
+		 "ch_cnt %d,ch_h[0] %d ch_h[1] %d, grph %d\n",
+		 ch_cnt, ch_h[0], ch_h[1], *grph);
+	ret = slim_define_ch(wcd9xxx->slim, &prop, ch_h, ch_cnt,
+			     true, grph);
 	if (ret < 0) {
 		pr_err("%s: slim_define_ch failed ret[%d]\n",
-					__func__, ret);
+		       __func__, ret);
 		goto err;
 	}
-	for (i = 0; i < ch_cnt; i++) {
-		ret = slim_connect_sink(wcd9xxx->slim, &sph[i], 1, ch_h[i]);
+
+	list_for_each_entry(rx, wcd9xxx_ch_list, list) {
+		codec_port = rx->port;
+		pr_debug("%s: codec_port %d rx 0x%x, payload %d\n"
+			 "sh_ch.rx_port_ch_reg_base0 0x%x\n"
+			 "sh_ch.port_rx_cfg_reg_base 0x%x\n",
+			 __func__, codec_port, (u32)rx, payload,
+			 sh_ch.rx_port_ch_reg_base,
+			sh_ch.port_rx_cfg_reg_base);
+
+		/* look for the valid port range and chose the
+		 * payload accordingly
+		 */
+		/* write to interface device */
+		ret = wcd9xxx_interface_reg_write(wcd9xxx,
+				SB_PGD_RX_PORT_MULTI_CHANNEL_0(
+				sh_ch.rx_port_ch_reg_base, codec_port),
+				payload);
+
+		if (ret < 0) {
+			pr_err("%s:Intf-dev fail reg[%d] payload[%d] ret[%d]\n",
+				__func__,
+				SB_PGD_RX_PORT_MULTI_CHANNEL_0(
+				sh_ch.rx_port_ch_reg_base, codec_port),
+				payload, ret);
+			goto err;
+		}
+		/* configure the slave port for water mark and enable*/
+		ret = wcd9xxx_interface_reg_write(wcd9xxx,
+				SB_PGD_PORT_CFG_BYTE_ADDR(
+				sh_ch.port_rx_cfg_reg_base, codec_port),
+				WATER_MARK_VAL);
+		if (ret < 0) {
+			pr_err("%s:watermark set failure for port[%d] ret[%d]",
+				__func__, codec_port, ret);
+		}
+
+		ret = slim_connect_sink(wcd9xxx->slim, &rx->sph, 1, rx->ch_h);
 		if (ret < 0) {
 			pr_err("%s: slim_connect_sink failed ret[%d]\n",
-						__func__, ret);
+				__func__, ret);
 			goto err_close_slim_sch;
 		}
 	}
 	/* slim_control_ch */
-	ret = slim_control_ch(wcd9xxx->slim, grph, SLIM_CH_ACTIVATE, true);
+	ret = slim_control_ch(wcd9xxx->slim, *grph, SLIM_CH_ACTIVATE,
+			      true);
 	if (ret < 0) {
 		pr_err("%s: slim_control_ch failed ret[%d]\n",
-				__func__, ret);
+			__func__, ret);
 		goto err_close_slim_sch;
 	}
-	for (i = 0; i < ch_cnt; i++) {
-		idx = (ch_num[i] - BASE_CH_NUM - sh_ch.rx_port_start_offset);
-		rx[idx].grph = grph;
-	}
 	return 0;
 
 err_close_slim_sch:
 	/*  release all acquired handles */
-	wcd9xxx_close_slim_sch_rx(wcd9xxx, ch_num, ch_cnt);
+	wcd9xxx_close_slim_sch_rx(wcd9xxx, wcd9xxx_ch_list, *grph);
 err:
 	return ret;
 }
 EXPORT_SYMBOL_GPL(wcd9xxx_cfg_slim_sch_rx);
 
 /* Enable slimbus slave device for RX path */
-int wcd9xxx_cfg_slim_sch_tx(struct wcd9xxx *wcd9xxx, unsigned int *ch_num,
-			    unsigned int ch_cnt, unsigned int rate)
+int wcd9xxx_cfg_slim_sch_tx(struct wcd9xxx *wcd9xxx,
+			    struct list_head *wcd9xxx_ch_list,
+			    unsigned int rate, unsigned int bit_width,
+			    u16 *grph)
 {
-	u8 i = 0;
-	u8  payload_tx_0 = 0, payload_tx_1 = 0, wm_payload = 0;
-	u16 grph;
-	u32 sph[SLIM_MAX_TX_PORTS] = {0};
+	u16 ch_cnt = 0;
+	u16 payload = 0;
 	u16 ch_h[SLIM_MAX_TX_PORTS] = {0};
-	u16 idx = 0, slave_port_id;
+	u16 codec_port;
 	int ret = 0;
-	unsigned short multi_chan_cfg_reg_addr;
+	struct wcd9xxx_ch *tx;
 
-	struct wcd9xxx_slim_sch_tx *tx = sh_ch.tx;
 	struct slim_ch prop;
 
-	pr_debug("%s: ch_cnt[%d] rate[%d]\n", __func__, ch_cnt, rate);
-	for (i = 0; i < ch_cnt; i++) {
-		idx = (ch_num[i] - BASE_CH_NUM);
-		ch_h[i] = tx[idx].ch_h;
-		sph[i] = tx[idx].sph;
-		slave_port_id = idx;
-		pr_debug("%s: idx %d, ch_h %d, sph %d, slave_port_id %d\n",
-			 __func__, idx, ch_h[i], sph[i], slave_port_id);
-		if (slave_port_id > sh_ch.number_of_tx_slave_dev_ports) {
-			pr_err("SLIMbus: invalid slave port id: %d",
-			       slave_port_id);
-			ret = -EINVAL;
-			goto err;
-		}
-		/* look for the valid port range and chose the
-		 *  payload accordingly
-		 */
-		if (slave_port_id <=
-		    SB_PGD_TX_PORT_MULTI_CHANNEL_0_END_PORT_ID) {
-			payload_tx_0 = payload_tx_0 | (1 << slave_port_id);
-		} else if (slave_port_id <=
-			   sh_ch.pgd_tx_port_ch_1_end_port_id) {
-			payload_tx_1 = payload_tx_1 |
-			    (1 << (slave_port_id -
-				 SB_PGD_TX_PORT_MULTI_CHANNEL_1_START_PORT_ID));
-		} else {
-			pr_err("%s: slave port id %d error\n", __func__,
-			       slave_port_id);
-			ret = -EINVAL;
-			goto err;
-		}
-		multi_chan_cfg_reg_addr =
-		    SB_PGD_TX_PORT_MULTI_CHANNEL_0(slave_port_id);
-		pr_debug("%s: multi_chan_cfg_reg_addr 0x%x\n", __func__,
-			 multi_chan_cfg_reg_addr);
-		/* write to interface device */
-		ret = wcd9xxx_interface_reg_write(wcd9xxx,
-				multi_chan_cfg_reg_addr,
-				payload_tx_0);
-		if (ret < 0) {
-			pr_err("%s:Intf-dev fail reg[%d] payload[%d] ret[%d]\n",
-			       __func__, multi_chan_cfg_reg_addr, payload_tx_0,
-			       ret);
-			goto err;
-		}
-		multi_chan_cfg_reg_addr =
-		    SB_PGD_TX_PORT_MULTI_CHANNEL_1(slave_port_id);
-		/* ports 8,9 */
-		ret = wcd9xxx_interface_reg_write(wcd9xxx,
-						  multi_chan_cfg_reg_addr,
-						  payload_tx_1);
-		if (ret < 0) {
-			pr_err("%s:Intf-dev fail reg[%d] payload[%d] ret[%d]\n",
-			       __func__, multi_chan_cfg_reg_addr,
-			       payload_tx_1, ret);
-			goto err;
-		}
-		/* configure the slave port for water mark and enable*/
-		wm_payload = (SLAVE_PORT_WATER_MARK_VALUE <<
-			      SLAVE_PORT_WATER_MARK_SHIFT) + SLAVE_PORT_ENABLE;
-		pr_debug("%s: tx_cfg_reg 0x%x wm 0x%x\n", __func__,
-			 SB_PGD_PORT_CFG_BYTE_ADDR(sh_ch.port_tx_cfg_reg_base,
-						   slave_port_id), wm_payload);
-		ret = wcd9xxx_interface_reg_write(
-					wcd9xxx,
-					SB_PGD_PORT_CFG_BYTE_ADDR(
-					    sh_ch.port_tx_cfg_reg_base,
-					    slave_port_id),
-					wm_payload);
-		if (ret < 0) {
-			pr_err("%s: watermark set failure for port[%d] ret[%d]",
-			       __func__, slave_port_id, ret);
-		}
+	list_for_each_entry(tx, wcd9xxx_ch_list, list) {
+		payload |= 1 << tx->shift;
+		ch_h[ch_cnt] = tx->ch_h;
+		ch_cnt++;
 	}
 
 	/* slim_define_ch api */
@@ -499,13 +325,53 @@
 	prop.auxf = SLIM_CH_AUXF_NOT_APPLICABLE;
 	prop.ratem = (rate/4000);
 	prop.sampleszbits = 16;
-	ret = slim_define_ch(wcd9xxx->slim, &prop, ch_h, ch_cnt, true, &grph);
+	ret = slim_define_ch(wcd9xxx->slim, &prop, ch_h, ch_cnt,
+			     true, grph);
 	if (ret < 0) {
-		pr_err("%s: slim_define_ch failed ret[%d]\n", __func__, ret);
+		pr_err("%s: slim_define_ch failed ret[%d]\n",
+		       __func__, ret);
 		goto err;
 	}
-	for (i = 0; i < ch_cnt; i++) {
-		ret = slim_connect_src(wcd9xxx->slim, sph[i], ch_h[i]);
+
+	pr_debug("%s: ch_cnt[%d] rate[%d]\n", __func__, ch_cnt, rate);
+	list_for_each_entry(tx, wcd9xxx_ch_list, list) {
+		codec_port = tx->port;
+		pr_debug("%s: codec_port %d rx 0x%x, payload 0x%x\n",
+			 __func__, codec_port, (u32)tx, payload);
+		/* write to interface device */
+		ret = wcd9xxx_interface_reg_write(wcd9xxx,
+				SB_PGD_TX_PORT_MULTI_CHANNEL_0(codec_port),
+				payload & 0x00FF);
+		if (ret < 0) {
+			pr_err("%s:Intf-dev fail reg[%d] payload[%d] ret[%d]\n",
+				__func__,
+				SB_PGD_TX_PORT_MULTI_CHANNEL_0(codec_port),
+				payload, ret);
+			goto err;
+		}
+		/* ports 8,9 */
+		ret = wcd9xxx_interface_reg_write(wcd9xxx,
+				SB_PGD_TX_PORT_MULTI_CHANNEL_1(codec_port),
+				(payload & 0xFF00)>>8);
+		if (ret < 0) {
+			pr_err("%s:Intf-dev fail reg[%d] payload[%d] ret[%d]\n",
+				__func__,
+				SB_PGD_TX_PORT_MULTI_CHANNEL_1(codec_port),
+				payload, ret);
+			goto err;
+		}
+		/* configure the slave port for water mark and enable*/
+		ret = wcd9xxx_interface_reg_write(wcd9xxx,
+				SB_PGD_PORT_CFG_BYTE_ADDR(
+				sh_ch.port_tx_cfg_reg_base, codec_port),
+				WATER_MARK_VAL);
+		if (ret < 0) {
+			pr_err("%s:watermark set failure for port[%d] ret[%d]",
+				__func__, codec_port, ret);
+		}
+
+		ret = slim_connect_src(wcd9xxx->slim, tx->sph, tx->ch_h);
+
 		if (ret < 0) {
 			pr_err("%s: slim_connect_src failed ret[%d]\n",
 			       __func__, ret);
@@ -513,91 +379,69 @@
 		}
 	}
 	/* slim_control_ch */
-	ret = slim_control_ch(wcd9xxx->slim, grph, SLIM_CH_ACTIVATE, true);
+	ret = slim_control_ch(wcd9xxx->slim, *grph, SLIM_CH_ACTIVATE,
+			      true);
 	if (ret < 0) {
 		pr_err("%s: slim_control_ch failed ret[%d]\n",
-				__func__, ret);
+			__func__, ret);
 		goto err;
 	}
-	for (i = 0; i < ch_cnt; i++) {
-		idx = (ch_num[i] - BASE_CH_NUM);
-		tx[idx].grph = grph;
-	}
 	return 0;
 err:
 	/* release all acquired handles */
-	wcd9xxx_close_slim_sch_tx(wcd9xxx, ch_num, ch_cnt);
+	wcd9xxx_close_slim_sch_tx(wcd9xxx, wcd9xxx_ch_list, *grph);
 	return ret;
 }
 EXPORT_SYMBOL_GPL(wcd9xxx_cfg_slim_sch_tx);
 
-int wcd9xxx_close_slim_sch_rx(struct wcd9xxx *wcd9xxx, unsigned int *ch_num,
-				unsigned int ch_cnt)
+int wcd9xxx_close_slim_sch_rx(struct wcd9xxx *wcd9xxx,
+			      struct list_head *wcd9xxx_ch_list, u16 grph)
 {
-	u16 grph = 0;
-	int i = 0 , idx = 0;
+	u32 sph[SLIM_MAX_RX_PORTS] = {0};
+	int ch_cnt = 0 ;
 	int ret = 0;
-	struct wcd9xxx_slim_sch_rx *rx = sh_ch.rx;
+	struct wcd9xxx_ch *rx;
 
-	pr_debug("%s: ch_cnt[%d]\n", __func__, ch_cnt);
-	for (i = 0; i < ch_cnt; i++) {
-		idx = (ch_num[i] - BASE_CH_NUM - sh_ch.rx_port_start_offset);
-		if (idx < 0) {
-			pr_err("%s: Error:-Invalid index found = %d\n",
-			       __func__, idx);
-			ret = -EINVAL;
-			goto err;
-		}
-		grph = rx[idx].grph;
-		pr_debug("%s: ch_num[%d] %d, idx %d, grph %x\n",
-			 __func__, i, ch_num[i], idx, grph);
-	}
+	list_for_each_entry(rx, wcd9xxx_ch_list, list)
+		sph[ch_cnt++] = rx->sph;
+
+	pr_debug("%s ch_cht %d, sph[0] %d sph[1] %d\n", __func__, ch_cnt,
+		sph[0], sph[1]);
 
 	/* slim_control_ch (REMOVE) */
+	pr_debug("%s before slim_control_ch grph %d\n", __func__, grph);
 	ret = slim_control_ch(wcd9xxx->slim, grph, SLIM_CH_REMOVE, true);
 	if (ret < 0) {
 		pr_err("%s: slim_control_ch failed ret[%d]\n", __func__, ret);
 		goto err;
 	}
-	for (i = 0; i < ch_cnt; i++) {
-		idx = (ch_num[i] - BASE_CH_NUM - sh_ch.rx_port_start_offset);
-		rx[idx].grph = 0;
-	}
 err:
 	return ret;
 }
 EXPORT_SYMBOL_GPL(wcd9xxx_close_slim_sch_rx);
 
-int wcd9xxx_close_slim_sch_tx(struct wcd9xxx *wcd9xxx, unsigned int *ch_num,
-			      unsigned int ch_cnt)
+int wcd9xxx_close_slim_sch_tx(struct wcd9xxx *wcd9xxx,
+			      struct list_head *wcd9xxx_ch_list,
+			      u16 grph)
 {
-	u16 grph = 0;
+	u32 sph[SLIM_MAX_TX_PORTS] = {0};
 	int ret = 0;
-	int i = 0 , idx = 0;
-	struct wcd9xxx_slim_sch_tx *tx = sh_ch.tx;
+	int ch_cnt = 0 ;
+	struct wcd9xxx_ch *tx;
 
-	pr_debug("%s: ch_cnt[%d]\n", __func__, ch_cnt);
-	for (i = 0; i < ch_cnt; i++) {
-		idx = (ch_num[i] - BASE_CH_NUM);
-		if (idx < 0) {
-			pr_err("%s: Error:- Invalid index found = %d\n",
-				__func__, idx);
-			ret = -EINVAL;
-			goto err;
-		}
-		grph = tx[idx].grph;
-	}
+	pr_debug("%s\n", __func__);
+	list_for_each_entry(tx, wcd9xxx_ch_list, list)
+		sph[ch_cnt++] = tx->sph;
+
+	pr_debug("%s ch_cht %d, sph[0] %d sph[1] %d\n",
+		__func__, ch_cnt, sph[0], sph[1]);
 	/* slim_control_ch (REMOVE) */
 	ret = slim_control_ch(wcd9xxx->slim, grph, SLIM_CH_REMOVE, true);
 	if (ret < 0) {
 		pr_err("%s: slim_control_ch failed ret[%d]\n",
-				__func__, ret);
+			__func__, ret);
 		goto err;
 	}
-	for (i = 0; i < ch_cnt; i++) {
-		idx = (ch_num[i] - BASE_CH_NUM);
-		tx[idx].grph = 0;
-	}
 err:
 	return ret;
 }
@@ -607,8 +451,8 @@
 {
 	int ret = 0;
 
-	pr_debug("%s: ch_num[%d]\n", __func__, ch_num);
 	ret = (ch_num - BASE_CH_NUM);
+	pr_debug("%s: ch_num[%d] slave port[%d]\n", __func__, ch_num, ret);
 	if (ret < 0) {
 		pr_err("%s: Error:- Invalid slave port found = %d\n",
 			__func__, ret);
@@ -618,39 +462,16 @@
 }
 EXPORT_SYMBOL_GPL(wcd9xxx_get_slave_port);
 
-int wcd9xxx_disconnect_port(struct wcd9xxx *wcd9xxx, unsigned int *ch_num,
-				unsigned int ch_cnt, unsigned int rx_tx)
+int wcd9xxx_disconnect_port(struct wcd9xxx *wcd9xxx,
+			    struct list_head *wcd9xxx_ch_list, u16 grph)
 {
-	u32 sph[SLIM_MAX_TX_PORTS] = {0};
-	int i = 0 , idx = 0;
+	u32 sph[SLIM_MAX_TX_PORTS + SLIM_MAX_RX_PORTS] = {0};
+	int ch_cnt = 0 ;
 	int ret = 0;
-	struct wcd9xxx_slim_sch_rx *rx = sh_ch.rx;
-	struct wcd9xxx_slim_sch_tx *tx = sh_ch.tx;
+	struct wcd9xxx_ch *slim_ch;
 
-	pr_debug("%s: ch_cnt[%d], rx_tx flag = %d\n", __func__, ch_cnt, rx_tx);
-	for (i = 0; i < ch_cnt; i++) {
-		/* rx_tx will be 1 for rx, 0 for tx */
-		if (rx_tx) {
-			idx = (ch_num[i] - BASE_CH_NUM -
-				sh_ch.rx_port_start_offset);
-			if (idx < 0) {
-				pr_err("%s: Invalid index found for RX = %d\n",
-					__func__, idx);
-				ret = -EINVAL;
-				goto err;
-			}
-			sph[i] = rx[idx].sph;
-		} else {
-			idx = (ch_num[i] - BASE_CH_NUM);
-			if (idx < 0) {
-				pr_err("%s:Invalid index found for TX = %d\n",
-					__func__, idx);
-				ret = -EINVAL;
-				goto err;
-			}
-			sph[i] = tx[idx].sph;
-		}
-	}
+	list_for_each_entry(slim_ch, wcd9xxx_ch_list, list)
+		sph[ch_cnt++] = slim_ch->sph;
 
 	/* slim_disconnect_port */
 	ret = slim_disconnect_ports(wcd9xxx->slim, sph, ch_cnt);
@@ -658,7 +479,59 @@
 		pr_err("%s: slim_disconnect_ports failed ret[%d]\n",
 			__func__, ret);
 	}
-err:
 	return ret;
 }
 EXPORT_SYMBOL_GPL(wcd9xxx_disconnect_port);
+
+/* This function is called with mutex acquired */
+int wcd9xxx_rx_vport_validation(u32 port_id,
+				struct list_head *codec_dai_list)
+{
+	struct wcd9xxx_ch *ch;
+	int ret = 0;
+
+	pr_debug("%s: port_id %u\n", __func__, port_id);
+
+	list_for_each_entry(ch,
+		codec_dai_list, list) {
+		pr_debug("%s: ch->port %u\n", __func__, ch->port);
+		if (ch->port == port_id) {
+			ret = -EINVAL;
+			break;
+		}
+	}
+	return ret;
+}
+EXPORT_SYMBOL_GPL(wcd9xxx_rx_vport_validation);
+
+
+/* This function is called with mutex acquired */
+int wcd9xxx_tx_vport_validation(u32 vtable, u32 port_id,
+				struct wcd9xxx_codec_dai_data *codec_dai)
+{
+	struct wcd9xxx_ch *ch;
+	int ret = 0;
+	u32 index;
+	u32 size = sizeof(vtable) * 8;
+	pr_debug("%s: vtable 0x%x port_id %u size %d\n", __func__,
+		 vtable, port_id, size);
+	for_each_set_bit(index, (unsigned long *)&vtable, size) {
+		list_for_each_entry(ch,
+				    &codec_dai[index].wcd9xxx_ch_list,
+				    list) {
+			pr_debug("%s: index %u ch->port %u vtable 0x%x\n",
+				 __func__, index, ch->port, vtable);
+			if (ch->port == port_id) {
+				pr_err("%s: TX%u is used by AIF%u_CAP Mixer\n",
+					__func__, port_id + 1,
+					(index + 1)/2);
+				ret = -EINVAL;
+				break;
+			}
+		}
+		if (ret)
+			break;
+	}
+	return ret;
+}
+EXPORT_SYMBOL_GPL(wcd9xxx_tx_vport_validation);
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 3c28447..6ab3a66 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -578,6 +578,17 @@
 	processing of MPEG transport streams from the main processor.
 	This can also be compiled as a loadable module.
 
+config CI_BRIDGE_SPI
+	depends on SPI_QUP
+	tristate "CI Bridge SPI Driver Support"
+	---help---
+	This driver provides a simple SPI read/write interface to
+	an external CI bridge. It implements a character device
+	driver interface which allows making SPI transactions
+	using the Linux SPI framework.
+
+	To compile this driver as module, choose M here.
+
 config HAPTIC_ISA1200
 	tristate "ISA1200 haptic support"
 	depends on I2C
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index be3d0a0..e92e119 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -61,6 +61,7 @@
 msm_tsif-objs := tsif.o
 obj-$(CONFIG_TSIF_CHRDEV) += tsif_chrdev.o
 obj-$(CONFIG_TSPP) += tspp.o
+obj-$(CONFIG_CI_BRIDGE_SPI) += ci-bridge-spi.o
 obj-$(CONFIG_HAPTIC_ISA1200)		+= isa1200.o
 obj-$(CONFIG_PMIC8058_PWM) += pmic8058-pwm.o
 obj-$(CONFIG_PMIC8XXX_VIBRATOR) += pm8xxx-vibrator.o
diff --git a/drivers/misc/ci-bridge-spi.c b/drivers/misc/ci-bridge-spi.c
new file mode 100644
index 0000000..368bef7
--- /dev/null
+++ b/drivers/misc/ci-bridge-spi.c
@@ -0,0 +1,428 @@
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+
+/* This driver implements a simple SPI read/write interface to access
+ * an external device over SPI.
+ */
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/spi/spi.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/device.h>
+#include <linux/cdev.h>
+#include <linux/fs.h>
+#include <linux/mutex.h>
+#include <linux/uaccess.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+
+#include <linux/ci-bridge-spi.h>
+
+#define CI_MAX_BUFFER_SIZE	(64 * 1024)
+
+struct ci_bridge {
+	dev_t ci_bridge_dev;
+	struct cdev cdev;
+	struct class *bridge_class;
+	struct device *bridge_dev;
+	char *write_buffer;
+	char *read_buffer;
+	struct mutex lock;
+	struct spi_device *spi;
+	unsigned int gpio_reset_pin;
+	unsigned int gpio_interrupt_pin;
+	int num_opened;
+
+};
+
+static struct ci_bridge ci;
+
+static int __devinit ci_bridge_spi_probe(struct spi_device *spi)
+{
+	int ret;
+	struct ci_bridge_platform_data *pdata;
+
+	if (spi->dev.platform_data == NULL) {
+		pr_err("%s: platform data is missing\n", __func__);
+		return -EINVAL;
+	}
+
+	ci.spi = spi;
+	ci.num_opened = 0;
+	mutex_init(&ci.lock);
+	spi_set_drvdata(spi, &ci);
+	pdata = spi->dev.platform_data;
+	ci.gpio_reset_pin = pdata->reset_pin;
+	ci.gpio_interrupt_pin = pdata->interrupt_pin;
+
+	ret = gpio_request(ci.gpio_reset_pin, "ci_bridge_spi");
+	if (ret) {
+		pr_err("%s: GPIO request for pin number %u failed\n",
+			   __func__, ci.gpio_reset_pin);
+		return ret;
+	}
+	ret = gpio_direction_output(ci.gpio_reset_pin, 1);
+	if (ret) {
+		pr_err("%s: unable to set GPIO direction, err=%d\n",
+			  __func__, ret);
+		goto err_free_reset_pin;
+	}
+
+	ret = gpio_request(ci.gpio_interrupt_pin, "ci_bridge_spi");
+	if (ret) {
+		pr_err("%s: GPIO request for pin number %u failed\n",
+			   __func__, ci.gpio_interrupt_pin);
+		goto err_free_reset_pin;
+	}
+	ret = gpio_direction_input(ci.gpio_interrupt_pin);
+	if (ret) {
+		pr_err("%s: unable to set GPIO direction, err=%d\n",
+			   __func__, ret);
+		goto err_free_int_pin;
+	}
+
+	return 0;
+
+err_free_int_pin:
+	gpio_free(ci.gpio_interrupt_pin);
+err_free_reset_pin:
+	gpio_free(ci.gpio_reset_pin);
+
+	return ret;
+}
+
+static int __devexit ci_bridge_spi_remove(struct spi_device *spi)
+{
+	struct ci_bridge *bridge = spi_get_drvdata(spi);
+
+	spi_set_drvdata(bridge->spi, NULL);
+	bridge->spi = NULL;
+	mutex_destroy(&ci.lock);
+
+	gpio_free(ci.gpio_reset_pin);
+	gpio_free(ci.gpio_interrupt_pin);
+
+	return 0;
+}
+
+static struct spi_driver ci_bridge_driver = {
+	.driver = {
+		.name = "ci_bridge_spi",
+		.owner = THIS_MODULE,
+	},
+	.probe = ci_bridge_spi_probe,
+	.remove = __devexit_p(ci_bridge_spi_remove),
+};
+
+static void ci_bridge_spi_completion_cb(void *arg)
+{
+	complete(arg);
+}
+
+static ssize_t ci_bridge_spi_read(struct file *filp,
+				char __user *buf,
+				size_t count,
+				loff_t *f_pos)
+{
+	int ret = 0;
+	unsigned long not_copied = 0;
+	struct spi_transfer spi_transfer;
+	struct spi_message spi_message;
+	DECLARE_COMPLETION_ONSTACK(context);
+	struct ci_bridge *bridge = filp->private_data;
+
+	if ((bridge == NULL) || (bridge->spi == NULL))
+		return -ENODEV;
+
+	if (count > CI_MAX_BUFFER_SIZE)
+		return -EMSGSIZE;
+
+	memset(&spi_transfer, 0, sizeof(struct spi_transfer));
+	memset(&spi_message, 0, sizeof(struct spi_message));
+
+	mutex_lock(&bridge->lock);
+
+	spi_transfer.rx_buf = bridge->read_buffer;
+	spi_transfer.len =  count;
+	spi_message_init(&spi_message);
+	spi_message_add_tail(&spi_transfer, &spi_message);
+	spi_message.complete = ci_bridge_spi_completion_cb;
+	spi_message.context = &context;
+
+	/* must use spi_async in a context that may sleep */
+	ret = spi_async(bridge->spi, &spi_message);
+	if (ret == 0) {
+		wait_for_completion(&context);
+
+		if (spi_message.status == 0) {
+			/* spi_message.actual_length should contain the number
+			 * of bytes actually read and should update ret to be
+			 * the actual length, but since our driver doesn't
+			 * support this, assume all count bytes were read.
+			 */
+			ret = count;
+		}
+
+		if (ret > 0) {
+			not_copied =
+				copy_to_user(buf, bridge->read_buffer, ret);
+			if (not_copied == ret)
+				ret = -EFAULT;
+			else
+				ret -= not_copied;
+		}
+	} else {
+		pr_err("%s: Error calling spi_async, ret = %d\n",
+			__func__, ret);
+	}
+
+	mutex_unlock(&bridge->lock);
+
+	return ret;
+}
+
+static ssize_t ci_bridge_spi_write(struct file *filp,
+				const char __user *buf,
+				size_t count,
+				loff_t *f_pos)
+{
+	int ret = 0;
+	unsigned long not_copied = 0;
+	struct spi_transfer spi_transfer;
+	struct spi_message spi_message;
+	DECLARE_COMPLETION_ONSTACK(context);
+	struct ci_bridge *bridge = filp->private_data;
+
+	if ((bridge == NULL) || (bridge->spi == NULL))
+		return -ENODEV;
+
+	if (count > CI_MAX_BUFFER_SIZE)
+		return -EMSGSIZE;
+
+	memset(&spi_transfer, 0, sizeof(struct spi_transfer));
+	memset(&spi_message, 0, sizeof(struct spi_message));
+
+	mutex_lock(&bridge->lock);
+	/* copy user data to our SPI Tx buffer */
+	not_copied = copy_from_user(bridge->write_buffer, buf, count);
+	if (not_copied != 0) {
+		ret = -EFAULT;
+	} else {
+		spi_transfer.tx_buf = bridge->write_buffer;
+		spi_transfer.len = count;
+
+		spi_message_init(&spi_message);
+		spi_message_add_tail(&spi_transfer, &spi_message);
+		spi_message.complete = ci_bridge_spi_completion_cb;
+		spi_message.context = &context;
+
+		/* must use spi_async in a context that may sleep */
+		ret = spi_async(bridge->spi, &spi_message);
+		if (ret == 0) {
+			wait_for_completion(&context);
+			/* update ret to contain
+			 * the number of bytes actually written
+			 */
+			if (spi_message.status == 0)
+				ret = spi_transfer.len;
+			else
+				pr_err("%s: SPI transfer error, spi_message.status = %d\n",
+					__func__, spi_message.status);
+		} else {
+			pr_err("%s: Error calling spi_async, ret = %d\n",
+				__func__, ret);
+		}
+	}
+	mutex_unlock(&bridge->lock);
+
+	return ret;
+}
+
+static int ci_bridge_spi_open(struct inode *inode, struct file *filp)
+{
+	/* forbid opening more then one instance at a time,
+	   parallel execution can still be problematic */
+	if (ci.num_opened != 0)
+		return -EBUSY;
+
+	/* allocate write buffer */
+	ci.write_buffer =
+		kzalloc((CI_MAX_BUFFER_SIZE * sizeof(char)), GFP_KERNEL);
+	if (ci.write_buffer == NULL) {
+		pr_err("%s: Error allocating memory for write buffer\n",
+			__func__);
+		return -ENOMEM;
+	}
+	/* allocate read buffer */
+	ci.read_buffer =
+		kzalloc((CI_MAX_BUFFER_SIZE * sizeof(char)), GFP_KERNEL);
+	if (ci.read_buffer == NULL) {
+		pr_err("%s: Error allocating memory for read buffer\n",
+			__func__);
+		kfree(ci.write_buffer);
+		return -ENOMEM;
+	}
+	/* device is non-seekable */
+	nonseekable_open(inode, filp);
+
+	filp->private_data = &ci;
+	ci.num_opened = 1;
+
+	return 0;
+}
+
+static int ci_bridge_ioctl_get_int(void *arg)
+{
+	int state;
+
+	if (arg == NULL)
+		return -EINVAL;
+
+	state = gpio_get_value_cansleep(ci.gpio_interrupt_pin);
+	if (copy_to_user(arg, &state, sizeof(state)))
+		return -EFAULT;
+
+	return 0;
+}
+
+static int ci_bridge_ioctl_reset(unsigned long arg)
+{
+	if ((arg != 0) && (arg != 1))
+		return -EINVAL;
+
+	gpio_set_value_cansleep(ci.gpio_reset_pin, arg);
+
+	return 0;
+}
+
+static long ci_bridge_spi_ioctl(struct file *file, unsigned int cmd,
+	unsigned long arg)
+{
+	int ret;
+
+	switch (cmd) {
+
+	case CI_BRIDGE_IOCTL_RESET:
+		ret = ci_bridge_ioctl_reset(arg);
+		break;
+
+	case CI_BRIDGE_IOCTL_GET_INT_STATE:
+		ret = ci_bridge_ioctl_get_int((void *) arg);
+		break;
+
+	default:
+		ret = -EINVAL;
+		break;
+	}
+
+	return ret;
+}
+
+static int ci_bridge_spi_release(struct inode *inode, struct file *filp)
+{
+	struct ci_bridge *bridge = filp->private_data;
+
+	if ((bridge == NULL) || (bridge->spi == NULL))
+		return -ENODEV;
+
+	kfree(bridge->write_buffer);
+	kfree(bridge->read_buffer);
+	filp->private_data = NULL;
+	ci.num_opened = 0;
+
+	return 0;
+}
+
+static const struct file_operations ci_bridge_spi_fops = {
+	.owner   = THIS_MODULE,
+	.read    = ci_bridge_spi_read,
+	.write   = ci_bridge_spi_write,
+	.open    = ci_bridge_spi_open,
+	.unlocked_ioctl = ci_bridge_spi_ioctl,
+	.release = ci_bridge_spi_release,
+	.llseek  = no_llseek,
+};
+
+static int __init ci_bridge_init(void)
+{
+	int ret = 0;
+
+	ret = alloc_chrdev_region(&ci.ci_bridge_dev, 0, 1, "ci_bridge_spi");
+	if (ret != 0)
+		return ret;
+
+	ci.bridge_class = class_create(THIS_MODULE, "ci_bridge_spi");
+	if (IS_ERR(ci.bridge_class)) {
+		ret = PTR_ERR(ci.bridge_class);
+		pr_err("Error creating ci.bridge_class: %d\n", ret);
+		goto free_region;
+	}
+
+	cdev_init(&ci.cdev, &ci_bridge_spi_fops);
+	ci.cdev.owner = THIS_MODULE;
+	ret = cdev_add(&ci.cdev, ci.ci_bridge_dev, 1);
+	if (ret != 0) {
+		pr_err("Error calling cdev_add: %d\n", ret);
+		goto class_destroy;
+	}
+
+
+	ci.bridge_dev = device_create(ci.bridge_class, NULL, ci.cdev.dev,
+				     &ci, "ci_bridge_spi0");
+	if (IS_ERR(ci.bridge_dev)) {
+		ret = PTR_ERR(ci.bridge_dev);
+		pr_err("device_create failed: %d\n", ret);
+		goto del_cdev;
+	}
+
+	ret = spi_register_driver(&ci_bridge_driver);
+	if (ret != 0) {
+		pr_err("Error registering spi driver: %d\n", ret);
+		goto device_destroy;
+	}
+
+	/* successful return */
+	return 0;
+
+device_destroy:
+	device_destroy(ci.bridge_class, ci.ci_bridge_dev);
+
+del_cdev:
+	cdev_del(&ci.cdev);
+
+class_destroy:
+	class_destroy(ci.bridge_class);
+
+free_region:
+	unregister_chrdev_region(ci.ci_bridge_dev, 1);
+
+	return ret;
+}
+
+static void __exit ci_bridge_exit(void)
+{
+	spi_unregister_driver(&ci_bridge_driver);
+	device_destroy(ci.bridge_class, ci.ci_bridge_dev);
+	cdev_del(&ci.cdev);
+	class_destroy(ci.bridge_class);
+	unregister_chrdev_region(ci.ci_bridge_dev, 1);
+}
+
+module_init(ci_bridge_init);
+module_exit(ci_bridge_exit);
+
+MODULE_DESCRIPTION("CI Bridge SPI Driver");
+MODULE_LICENSE("GPL v2");
+
diff --git a/drivers/misc/qseecom.c b/drivers/misc/qseecom.c
index bc8eccf..e71c95d 100644
--- a/drivers/misc/qseecom.c
+++ b/drivers/misc/qseecom.c
@@ -1,6 +1,6 @@
 
 
-/* Qualcomm Secure Execution Environment Communicator (QSEECOM) driver
+/*Qualcomm Secure Execution Environment Communicator (QSEECOM) driver
  *
  * Copyright (c) 2012, Code Aurora Forum. All rights reserved.
  *
@@ -32,18 +32,24 @@
 #include <linux/types.h>
 #include <linux/clk.h>
 #include <linux/qseecom.h>
+#include <linux/elf.h>
+#include <linux/firmware.h>
 #include <linux/freezer.h>
 #include <mach/board.h>
 #include <mach/msm_bus.h>
 #include <mach/msm_bus_board.h>
 #include <mach/scm.h>
-#include <mach/peripheral-loader.h>
+#include <mach/subsystem_restart.h>
+#include <mach/socinfo.h>
 #include "qseecom_legacy.h"
+#include "qseecom_kernel.h"
 
 #define QSEECOM_DEV			"qseecom"
 #define QSEOS_VERSION_13		0x13
 #define QSEOS_VERSION_14		0x14
-#define QSEOS_CHECK_VERSION_CMD		0x00001803;
+#define QSEEE_VERSION_00		0x400000
+
+#define QSEOS_CHECK_VERSION_CMD		0x00001803
 
 enum qseecom_command_scm_resp_type {
 	QSEOS_APP_ID = 0xEE01,
@@ -60,6 +66,9 @@
 	QSEOS_LISTENER_DATA_RSP_COMMAND,
 	QSEOS_LOAD_EXTERNAL_ELF_COMMAND,
 	QSEOS_UNLOAD_EXTERNAL_ELF_COMMAND,
+	QSEOS_GET_APP_STATE_COMMAND,
+	QSEOS_LOAD_SERV_IMAGE_COMMAND,
+	QSEOS_UNLOAD_SERV_IMAGE_COMMAND,
 	QSEOS_CMD_MAX     = 0xEFFFFFFF
 };
 
@@ -92,6 +101,17 @@
 	uint32_t  app_id;
 };
 
+__packed struct qseecom_load_lib_image_ireq {
+	uint32_t qsee_cmd_id;
+	uint32_t mdt_len;
+	uint32_t img_len;
+	uint32_t phy_addr;
+};
+
+__packed struct qseecom_unload_lib_image_ireq {
+	uint32_t qsee_cmd_id;
+};
+
 __packed struct qseecom_register_listener_ireq {
 	uint32_t qsee_cmd_id;
 	uint32_t listener_id;
@@ -167,6 +187,11 @@
 	u32  ref_cnt;
 };
 
+struct qseecom_registered_kclient_list {
+	struct list_head list;
+	struct qseecom_handle *handle;
+};
+
 struct qseecom_control {
 	struct ion_client *ion_clnt;		/* Ion client */
 	struct list_head  registered_listener_list_head;
@@ -175,10 +200,16 @@
 	struct list_head  registered_app_list_head;
 	spinlock_t        registered_app_list_lock;
 
+	struct list_head   registered_kclient_list_head;
+	spinlock_t        registered_kclient_list_lock;
+
 	wait_queue_head_t send_resp_wq;
 	int               send_resp_flag;
 
 	uint32_t          qseos_version;
+	uint32_t          qsee_version;
+	struct device *pdev;
+	bool  commonlib_loaded;
 };
 
 struct qseecom_client_handle {
@@ -208,9 +239,16 @@
 	atomic_t          ioctl_count;
 };
 
+struct clk *ce_core_clk;
+struct clk *ce_clk;
+struct clk *ce_core_src_clk;
+struct clk *ce_bus_clk;
+
 /* Function proto types */
 static int qsee_vote_for_clock(int32_t);
 static void qsee_disable_clock_vote(int32_t);
+static int __qseecom_init_clk(void);
+static void __qseecom_disable_clk(void);
 
 static int __qseecom_is_svc_unique(struct qseecom_dev_handle *data,
 		struct qseecom_register_listener_req *svc)
@@ -633,7 +671,6 @@
 	ion_phys_addr_t pa = 0;
 	uint32_t len;
 	struct qseecom_command_scm_resp resp;
-	struct qseecom_check_app_ireq req;
 	struct qseecom_load_app_ireq load_req;
 
 	/* Copy the relevant information needed for loading the image */
@@ -648,11 +685,8 @@
 	if (ret)
 		pr_warning("Unable to vote for SFPB clock");
 
-	req.qsee_cmd_id = QSEOS_APP_LOOKUP_COMMAND;
-	memcpy(req.app_name, load_img_req.img_name, MAX_APP_NAME_SIZE);
-
 	pr_warn("App (%s) does not exist, loading apps for first time\n",
-			(char *)(req.app_name));
+			(char *)(load_img_req.img_name));
 	/* Get the handle of the shared fd */
 	ihandle = ion_import_dma_buf(qseecom.ion_clnt,
 					load_img_req.ifd_data_fd);
@@ -666,6 +700,7 @@
 	ret = ion_phys(qseecom.ion_clnt, ihandle, &pa, &len);
 
 	/* Populate the structure for sending scm call to load image */
+	memcpy(load_req.app_name, load_img_req.img_name, MAX_APP_NAME_SIZE);
 	load_req.qsee_cmd_id = QSEOS_APP_START_COMMAND;
 	load_req.mdt_len = load_img_req.mdt_len;
 	load_req.img_len = load_img_req.img_len;
@@ -729,7 +764,7 @@
 	spin_unlock_irqrestore(&qseecom.registered_app_list_lock, flags);
 
 	pr_warn("App with id %d (%s) now loaded\n", app_id,
-		(char *)(req.app_name));
+		(char *)(load_img_req.img_name));
 
 	data->client.app_id = app_id;
 	load_img_req.app_id = app_id;
@@ -1162,6 +1197,489 @@
 	return ret;
 }
 
+static bool __qseecom_is_fw_image_valid(const struct firmware *fw_entry)
+{
+	struct elf32_hdr *ehdr;
+
+	if (fw_entry->size < sizeof(*ehdr)) {
+		pr_err("%s: Not big enough to be an elf header\n",
+				 qseecom.pdev->init_name);
+		return false;
+	}
+	ehdr = (struct elf32_hdr *)fw_entry->data;
+	if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG)) {
+		pr_err("%s: Not an elf header\n",
+				 qseecom.pdev->init_name);
+		return false;
+	}
+
+	if (ehdr->e_phnum == 0) {
+		pr_err("%s: No loadable segments\n",
+				 qseecom.pdev->init_name);
+		return false;
+	}
+	if (sizeof(struct elf32_phdr) * ehdr->e_phnum +
+	    sizeof(struct elf32_hdr) > fw_entry->size) {
+		pr_err("%s: Program headers not within mdt\n",
+				 qseecom.pdev->init_name);
+		return false;
+	}
+	return true;
+}
+
+static int __qseecom_get_fw_size(char *appname, uint32_t *fw_size)
+{
+	int ret = -1;
+	int i = 0, rc = 0;
+	const struct firmware *fw_entry = NULL;
+	struct elf32_phdr *phdr;
+	char fw_name[MAX_APP_NAME_SIZE];
+	struct elf32_hdr *ehdr;
+	int num_images = 0;
+
+	snprintf(fw_name, sizeof(fw_name), "%s.mdt", appname);
+	rc = request_firmware(&fw_entry, fw_name,  qseecom.pdev);
+	if (rc) {
+		pr_err("error with request_firmware\n");
+		ret = -EIO;
+		goto err;
+	}
+	if (!__qseecom_is_fw_image_valid(fw_entry)) {
+		ret = -EIO;
+		goto err;
+	}
+	*fw_size = fw_entry->size;
+	phdr = (struct elf32_phdr *)(fw_entry->data + sizeof(struct elf32_hdr));
+	ehdr = (struct elf32_hdr *)fw_entry->data;
+	num_images = ehdr->e_phnum;
+	release_firmware(fw_entry);
+	for (i = 0; i < num_images; i++, phdr++) {
+		memset(fw_name, 0, sizeof(fw_name));
+		snprintf(fw_name, ARRAY_SIZE(fw_name), "%s.b%02d", appname, i);
+		ret = request_firmware(&fw_entry, fw_name, qseecom.pdev);
+		if (ret)
+			goto err;
+		*fw_size += fw_entry->size;
+		release_firmware(fw_entry);
+	}
+	return ret;
+err:
+	if (fw_entry)
+		release_firmware(fw_entry);
+	*fw_size = 0;
+	return ret;
+}
+
+static int __qseecom_get_fw_data(char *appname, u8 *img_data,
+					struct qseecom_load_app_ireq *load_req)
+{
+	int ret = -1;
+	int i = 0, rc = 0;
+	const struct firmware *fw_entry = NULL;
+	char fw_name[MAX_APP_NAME_SIZE];
+	u8 *img_data_ptr = img_data;
+	struct elf32_hdr *ehdr;
+	int num_images = 0;
+
+	snprintf(fw_name, sizeof(fw_name), "%s.mdt", appname);
+	rc = request_firmware(&fw_entry, fw_name,  qseecom.pdev);
+	if (rc) {
+		ret = -EIO;
+		goto err;
+	}
+	load_req->img_len = fw_entry->size;
+	memcpy(img_data_ptr, fw_entry->data, fw_entry->size);
+	img_data_ptr = img_data_ptr + fw_entry->size;
+	load_req->mdt_len = fw_entry->size; /*Get MDT LEN*/
+	ehdr = (struct elf32_hdr *)fw_entry->data;
+	num_images = ehdr->e_phnum;
+	release_firmware(fw_entry);
+	for (i = 0; i < num_images; i++) {
+		snprintf(fw_name, ARRAY_SIZE(fw_name), "%s.b%02d", appname, i);
+		ret = request_firmware(&fw_entry, fw_name,  qseecom.pdev);
+		if (ret) {
+			pr_err("Failed to locate blob %s\n", fw_name);
+			goto err;
+		}
+		memcpy(img_data_ptr, fw_entry->data, fw_entry->size);
+		img_data_ptr = img_data_ptr + fw_entry->size;
+		load_req->img_len += fw_entry->size;
+		release_firmware(fw_entry);
+	}
+	load_req->phy_addr = virt_to_phys(img_data);
+	return ret;
+err:
+	release_firmware(fw_entry);
+	return ret;
+}
+
+static int __qseecom_load_fw(struct qseecom_dev_handle *data, char *appname)
+{
+	int ret = -1;
+	uint32_t fw_size = 0;
+	struct qseecom_load_app_ireq load_req = {0, 0, 0, 0};
+	struct qseecom_command_scm_resp resp;
+	u8 *img_data = NULL;
+
+	if (__qseecom_get_fw_size(appname, &fw_size))
+		return -EIO;
+
+	img_data = kzalloc(fw_size, GFP_KERNEL);
+	if (!img_data) {
+		pr_err("Failied to allocate memory for copying image data\n");
+		return -ENOMEM;
+	}
+	ret = __qseecom_get_fw_data(appname, img_data, &load_req);
+	if (ret) {
+		kzfree(img_data);
+		return -EIO;
+	}
+
+	/* Populate the remaining parameters */
+	load_req.qsee_cmd_id = QSEOS_APP_START_COMMAND;
+	memcpy(load_req.app_name, appname, MAX_APP_NAME_SIZE);
+	/* SCM_CALL to load the image */
+	ret = scm_call(SCM_SVC_TZSCHEDULER, 1,	&load_req,
+			sizeof(struct qseecom_load_app_ireq),
+			&resp, sizeof(resp));
+	kzfree(img_data);
+	if (ret) {
+		pr_err("scm_call to load failed : ret %d\n", ret);
+		return -EIO;
+	}
+
+	switch (resp.result) {
+	case QSEOS_RESULT_SUCCESS:
+		ret = resp.data;
+		break;
+	case QSEOS_RESULT_INCOMPLETE:
+		ret = __qseecom_process_incomplete_cmd(data, &resp);
+		if (ret)
+			pr_err("process_incomplete_cmd FAILED\n");
+		else
+			ret = resp.data;
+		break;
+	case QSEOS_RESULT_FAILURE:
+		pr_err("scm call failed with response QSEOS_RESULT FAILURE\n");
+		break;
+	default:
+		pr_err("scm call return unknown response %d\n", resp.result);
+		ret = -EINVAL;
+		break;
+	}
+	return ret;
+}
+
+static int qseecom_load_commonlib_image(void)
+{
+	int32_t ret = 0;
+	uint32_t fw_size = 0;
+	struct qseecom_load_app_ireq load_req = {0, 0, 0, 0};
+	struct qseecom_command_scm_resp resp;
+	u8 *img_data = NULL;
+
+	if (__qseecom_get_fw_size("commonlib", &fw_size))
+		return -EIO;
+
+	img_data = kzalloc(fw_size, GFP_KERNEL);
+	if (!img_data) {
+		pr_err("Mem allocation for lib image data failed\n");
+		return -ENOMEM;
+	}
+	ret = __qseecom_get_fw_data("commonlib", img_data, &load_req);
+	if (ret) {
+		kzfree(img_data);
+		return -EIO;
+	}
+	/* Populate the remaining parameters */
+	load_req.qsee_cmd_id = QSEOS_LOAD_SERV_IMAGE_COMMAND;
+	/* SCM_CALL to load the image */
+	ret = scm_call(SCM_SVC_TZSCHEDULER, 1, &load_req,
+				sizeof(struct qseecom_load_lib_image_ireq),
+							&resp, sizeof(resp));
+	kzfree(img_data);
+	if (ret) {
+		pr_err("scm_call to load failed : ret %d\n", ret);
+		ret = -EIO;
+	} else {
+		switch (resp.result) {
+		case QSEOS_RESULT_SUCCESS:
+			break;
+		case QSEOS_RESULT_FAILURE:
+			pr_err("scm call failed w/response result%d\n",
+						resp.result);
+			ret = -EINVAL;
+			break;
+		default:
+			pr_err("scm call return unknown response %d\n",
+						resp.result);
+			ret = -EINVAL;
+			break;
+		}
+	}
+	return ret;
+}
+
+static int qseecom_unload_commonlib_image(void)
+{
+	int ret = -EINVAL;
+	struct qseecom_unload_lib_image_ireq unload_req = {0};
+	struct qseecom_command_scm_resp resp;
+
+	/* Populate the remaining parameters */
+	unload_req.qsee_cmd_id = QSEOS_UNLOAD_SERV_IMAGE_COMMAND;
+	/* SCM_CALL to load the image */
+	ret = scm_call(SCM_SVC_TZSCHEDULER, 1,	&unload_req,
+			sizeof(struct qseecom_unload_lib_image_ireq),
+						&resp, sizeof(resp));
+	if (ret) {
+		pr_err("scm_call to unload lib failed : ret %d\n", ret);
+		ret = -EIO;
+	} else {
+		switch (resp.result) {
+		case QSEOS_RESULT_SUCCESS:
+			break;
+		case QSEOS_RESULT_FAILURE:
+			pr_err("scm fail resp.result QSEOS_RESULT FAILURE\n");
+			break;
+		default:
+			pr_err("scm call return unknown response %d\n",
+					resp.result);
+			ret = -EINVAL;
+			break;
+		}
+	}
+	return ret;
+}
+
+int qseecom_start_app(struct qseecom_handle **handle,
+						char *app_name, uint32_t size)
+{
+	int32_t ret = 0;
+	unsigned long flags = 0;
+	struct qseecom_dev_handle *data = NULL;
+	struct qseecom_check_app_ireq app_ireq;
+	struct qseecom_registered_app_list *entry = NULL;
+	struct qseecom_registered_kclient_list *kclient_entry = NULL;
+	bool found_app = false;
+	uint32_t len;
+	ion_phys_addr_t pa;
+
+	if (qseecom.qseos_version == QSEOS_VERSION_13) {
+		pr_err("This functionality is UNSUPPORTED in version 1.3\n");
+		return -EINVAL;
+	}
+
+	if (qseecom.qsee_version > QSEEE_VERSION_00) {
+		mutex_lock(&app_access_lock);
+		if (qseecom.commonlib_loaded == false) {
+			ret = qseecom_load_commonlib_image();
+			if (ret == 0)
+				qseecom.commonlib_loaded = true;
+		}
+		mutex_unlock(&app_access_lock);
+	}
+
+	if (ret)
+		return -EIO;
+
+	*handle = kzalloc(sizeof(struct qseecom_handle), GFP_KERNEL);
+	if (!(*handle)) {
+		pr_err("failed to allocate memory for kernel client handle\n");
+		return -ENOMEM;
+	}
+
+	app_ireq.qsee_cmd_id = QSEOS_APP_LOOKUP_COMMAND;
+	memcpy(app_ireq.app_name, app_name, MAX_APP_NAME_SIZE);
+	ret = __qseecom_check_app_exists(app_ireq);
+	if (ret < 0)
+		return -EINVAL;
+
+	data = kzalloc(sizeof(*data), GFP_KERNEL);
+	if (!data) {
+		pr_err("kmalloc failed\n");
+		if (ret == 0) {
+			kfree(*handle);
+			*handle = NULL;
+		}
+		return -ENOMEM;
+	}
+	data->abort = 0;
+	data->service = false;
+	data->released = false;
+	data->client.app_id = ret;
+	data->client.sb_length = size;
+	data->client.user_virt_sb_base = 0;
+	data->client.ihandle = NULL;
+
+	init_waitqueue_head(&data->abort_wq);
+	atomic_set(&data->ioctl_count, 0);
+
+	data->client.ihandle = ion_alloc(qseecom.ion_clnt, size, 4096,
+				ION_HEAP(ION_QSECOM_HEAP_ID), 0);
+	if (IS_ERR_OR_NULL(data->client.ihandle)) {
+		pr_err("Ion client could not retrieve the handle\n");
+		kfree(data);
+		kfree(*handle);
+		*handle = NULL;
+		return -EINVAL;
+	}
+
+	if (ret > 0) {
+		pr_warn("App id %d for [%s] app exists\n", ret,
+			(char *)app_ireq.app_name);
+		spin_lock_irqsave(&qseecom.registered_app_list_lock, flags);
+		list_for_each_entry(entry,
+				&qseecom.registered_app_list_head, list){
+			if (entry->app_id == ret) {
+				entry->ref_cnt++;
+				found_app = true;
+				break;
+			}
+		}
+		spin_unlock_irqrestore(
+				&qseecom.registered_app_list_lock, flags);
+		if (!found_app)
+			pr_warn("App_id %d [%s] was loaded but not registered\n",
+					ret, (char *)app_ireq.app_name);
+	} else {
+		/* load the app and get the app_id  */
+		pr_debug("%s: Loading app for the first time'\n",
+				qseecom.pdev->init_name);
+		mutex_lock(&app_access_lock);
+		ret = __qseecom_load_fw(data, app_name);
+		mutex_unlock(&app_access_lock);
+
+		if (ret < 0) {
+			kfree(*handle);
+			*handle = NULL;
+			return ret;
+		}
+		data->client.app_id = ret;
+	}
+	if (!found_app) {
+		entry = kmalloc(sizeof(*entry), GFP_KERNEL);
+		if (!entry) {
+			pr_err("kmalloc failed\n");
+			return -ENOMEM;
+		}
+		entry->app_id = ret;
+		entry->ref_cnt = 1;
+
+		spin_lock_irqsave(&qseecom.registered_app_list_lock, flags);
+		list_add_tail(&entry->list, &qseecom.registered_app_list_head);
+		spin_unlock_irqrestore(&qseecom.registered_app_list_lock,
+									flags);
+	}
+
+	/* Get the physical address of the ION BUF */
+	ret = ion_phys(qseecom.ion_clnt, data->client.ihandle, &pa, &len);
+	/* Populate the structure for sending scm call to load image */
+	data->client.sb_virt = (char *) ion_map_kernel(qseecom.ion_clnt,
+							data->client.ihandle);
+	data->client.sb_phys = pa;
+	(*handle)->dev = (void *)data;
+	(*handle)->sbuf = (unsigned char *)data->client.sb_virt;
+	(*handle)->sbuf_len = data->client.sb_length;
+
+	kclient_entry = kzalloc(sizeof(*kclient_entry), GFP_KERNEL);
+	if (!kclient_entry) {
+		pr_err("kmalloc failed\n");
+		return -ENOMEM;
+	}
+	kclient_entry->handle = *handle;
+
+	spin_lock_irqsave(&qseecom.registered_kclient_list_lock, flags);
+	list_add_tail(&kclient_entry->list,
+			&qseecom.registered_kclient_list_head);
+	spin_unlock_irqrestore(&qseecom.registered_kclient_list_lock, flags);
+
+	return 0;
+}
+EXPORT_SYMBOL(qseecom_start_app);
+
+int qseecom_shutdown_app(struct qseecom_handle **handle)
+{
+	int ret = -EINVAL;
+	struct qseecom_dev_handle *data =
+			(struct qseecom_dev_handle *) ((*handle)->dev);
+	struct qseecom_registered_kclient_list *kclient = NULL;
+	unsigned long flags = 0;
+	bool found_handle = false;
+
+	if (qseecom.qseos_version == QSEOS_VERSION_13) {
+		pr_err("This functionality is UNSUPPORTED in version 1.3\n");
+		return -EINVAL;
+	}
+	if (*handle == NULL) {
+		pr_err("Handle is not initialized\n");
+		return -EINVAL;
+	}
+
+	spin_lock_irqsave(&qseecom.registered_kclient_list_lock, flags);
+	list_for_each_entry(kclient, &qseecom.registered_kclient_list_head,
+				list) {
+		if (kclient->handle == (*handle)) {
+			list_del(&kclient->list);
+			found_handle = true;
+			break;
+		}
+	}
+	spin_unlock_irqrestore(&qseecom.registered_kclient_list_lock, flags);
+	if (!found_handle)
+		pr_err("Unable to find the handle, exiting\n");
+	else
+		ret = qseecom_unload_app(data);
+	if (ret == 0) {
+		kzfree(data);
+		kzfree(*handle);
+		kzfree(kclient);
+		*handle = NULL;
+	}
+	return ret;
+}
+EXPORT_SYMBOL(qseecom_shutdown_app);
+
+int qseecom_send_command(struct qseecom_handle *handle, void *send_buf,
+			uint32_t sbuf_len, void *resp_buf, uint32_t rbuf_len)
+{
+	int ret = 0;
+	struct qseecom_send_cmd_req req = {0, 0, 0, 0};
+	struct qseecom_dev_handle *data;
+
+	if (qseecom.qseos_version == QSEOS_VERSION_13) {
+		pr_err("This functionality is UNSUPPORTED in version 1.3\n");
+		return -EINVAL;
+	}
+
+	if (handle == NULL) {
+		pr_err("Handle is not initialized\n");
+		return -EINVAL;
+	}
+	data = handle->dev;
+
+	req.cmd_req_len = sbuf_len;
+	req.resp_len = rbuf_len;
+	req.cmd_req_buf = send_buf;
+	req.resp_buf = resp_buf;
+
+	mutex_lock(&app_access_lock);
+	atomic_inc(&data->ioctl_count);
+
+	ret = __qseecom_send_cmd(data, &req);
+
+	atomic_dec(&data->ioctl_count);
+	mutex_unlock(&app_access_lock);
+
+	if (ret)
+		return ret;
+
+	pr_debug("sending cmd_req->rsp size: %u, ptr: 0x%p\n",
+			req.resp_len, req.resp_buf);
+	return ret;
+}
+EXPORT_SYMBOL(qseecom_send_command);
+
 static int qseecom_send_resp(void)
 {
 	qseecom.send_resp_flag = 1;
@@ -1579,7 +2097,15 @@
 	case QSEECOM_IOCTL_LOAD_APP_REQ: {
 		mutex_lock(&app_access_lock);
 		atomic_inc(&data->ioctl_count);
-		ret = qseecom_load_app(data, argp);
+		if (qseecom.qsee_version > QSEEE_VERSION_00) {
+			if (qseecom.commonlib_loaded == false) {
+				ret = qseecom_load_commonlib_image();
+				if (ret == 0)
+					qseecom.commonlib_loaded = true;
+			}
+		}
+		if (ret == 0)
+			ret = qseecom_load_app(data, argp);
 		atomic_dec(&data->ioctl_count);
 		mutex_unlock(&app_access_lock);
 		if (ret)
@@ -1684,7 +2210,7 @@
 		int pil_error;
 		mutex_lock(&pil_access_lock);
 		if (pil_ref_cnt == 0) {
-			pil = pil_get("tzapps");
+			pil = subsystem_get("tzapps");
 			if (IS_ERR(pil)) {
 				pr_err("Playready PIL image load failed\n");
 				pil_error = PTR_ERR(pil);
@@ -1719,7 +2245,7 @@
 	if (qseecom.qseos_version == QSEOS_VERSION_13) {
 		mutex_lock(&pil_access_lock);
 		if (pil_ref_cnt == 1)
-			pil_put(pil);
+			subsystem_put(pil);
 		pil_ref_cnt--;
 		mutex_unlock(&pil_access_lock);
 	}
@@ -1735,12 +2261,134 @@
 		.release = qseecom_release
 };
 
+static int __qseecom_init_clk()
+{
+	int rc = 0;
+	struct device *pdev;
+
+	pdev = qseecom.pdev;
+	/* Get CE3 src core clk. */
+	ce_core_src_clk = clk_get(pdev, "core_clk_src");
+	if (!IS_ERR(ce_core_src_clk)) {
+		ce_core_src_clk = ce_core_src_clk;
+
+		/* Set the core src clk @100Mhz */
+		rc = clk_set_rate(ce_core_src_clk, 100000000);
+		if (rc) {
+			clk_put(ce_core_src_clk);
+			pr_err("Unable to set the core src clk @100Mhz.\n");
+			goto err_clk;
+		}
+	} else {
+		pr_warn("Unable to get CE core src clk, set to NULL\n");
+		ce_core_src_clk = NULL;
+	}
+
+	/* Get CE core clk */
+	ce_core_clk = clk_get(pdev, "core_clk");
+	if (IS_ERR(ce_core_clk)) {
+		rc = PTR_ERR(ce_core_clk);
+		pr_err("Unable to get CE core clk\n");
+		if (ce_core_src_clk != NULL)
+			clk_put(ce_core_src_clk);
+		goto err_clk;
+	}
+
+	/* Get CE Interface clk */
+	ce_clk = clk_get(pdev, "iface_clk");
+	if (IS_ERR(ce_clk)) {
+		rc = PTR_ERR(ce_clk);
+		pr_err("Unable to get CE interface clk\n");
+		if (ce_core_src_clk != NULL)
+			clk_put(ce_core_src_clk);
+		clk_put(ce_core_clk);
+		goto err_clk;
+	}
+
+	/* Get CE AXI clk */
+	ce_bus_clk = clk_get(pdev, "bus_clk");
+	if (IS_ERR(ce_bus_clk)) {
+		rc = PTR_ERR(ce_bus_clk);
+		pr_err("Unable to get CE BUS interface clk\n");
+		if (ce_core_src_clk != NULL)
+			clk_put(ce_core_src_clk);
+		clk_put(ce_core_clk);
+		clk_put(ce_clk);
+		goto err_clk;
+	}
+
+	/* Enable CE core clk */
+	rc = clk_prepare_enable(ce_core_clk);
+	if (rc) {
+		pr_err("Unable to enable/prepare CE core clk\n");
+		if (ce_core_src_clk != NULL)
+			clk_put(ce_core_src_clk);
+		clk_put(ce_core_clk);
+		clk_put(ce_clk);
+		goto err_clk;
+	} else {
+		/* Enable CE clk */
+		rc = clk_prepare_enable(ce_clk);
+		if (rc) {
+			pr_err("Unable to enable/prepare CE iface clk\n");
+			clk_disable_unprepare(ce_core_clk);
+			if (ce_core_src_clk != NULL)
+				clk_put(ce_core_src_clk);
+			clk_put(ce_core_clk);
+			clk_put(ce_clk);
+			goto err_clk;
+		} else {
+			/* Enable AXI clk */
+			rc = clk_prepare_enable(ce_bus_clk);
+			if (rc) {
+				pr_err("Unable to enable/prepare CE iface clk\n");
+				clk_disable_unprepare(ce_core_clk);
+				clk_disable_unprepare(ce_clk);
+				if (ce_core_src_clk != NULL)
+					clk_put(ce_core_src_clk);
+				clk_put(ce_core_clk);
+				clk_put(ce_clk);
+				goto err_clk;
+			}
+		}
+	}
+	return rc;
+
+err_clk:
+	if (rc)
+		pr_err("Unable to init CE clks, rc = %d\n", rc);
+	clk_disable_unprepare(ce_clk);
+	clk_disable_unprepare(ce_core_clk);
+	clk_disable_unprepare(ce_bus_clk);
+	if (ce_core_src_clk != NULL)
+		clk_put(ce_core_src_clk);
+	clk_put(ce_clk);
+	clk_put(ce_core_clk);
+	clk_put(ce_bus_clk);
+	return rc;
+}
+
+
+
+static void __qseecom_disable_clk()
+{
+	clk_disable_unprepare(ce_clk);
+	clk_disable_unprepare(ce_core_clk);
+	clk_disable_unprepare(ce_bus_clk);
+	if (ce_core_src_clk != NULL)
+		clk_put(ce_core_src_clk);
+	clk_put(ce_clk);
+	clk_put(ce_core_clk);
+	clk_put(ce_bus_clk);
+}
+
 static int __devinit qseecom_probe(struct platform_device *pdev)
 {
 	int rc;
+	int ret;
 	struct device *class_dev;
 	char qsee_not_legacy = 0;
-	struct msm_bus_scale_pdata *qseecom_platform_support;
+	struct msm_bus_scale_pdata *qseecom_platform_support = NULL;
 	uint32_t system_call_id = QSEOS_CHECK_VERSION_CMD;
 
 	qsee_bw_count = 0;
@@ -1780,24 +2428,38 @@
 	spin_lock_init(&qseecom.registered_listener_list_lock);
 	INIT_LIST_HEAD(&qseecom.registered_app_list_head);
 	spin_lock_init(&qseecom.registered_app_list_lock);
+	INIT_LIST_HEAD(&qseecom.registered_kclient_list_head);
+	spin_lock_init(&qseecom.registered_kclient_list_lock);
 	init_waitqueue_head(&qseecom.send_resp_wq);
 	qseecom.send_resp_flag = 0;
 
 	rc = scm_call(6, 1, &system_call_id, sizeof(system_call_id),
 				&qsee_not_legacy, sizeof(qsee_not_legacy));
 	if (rc) {
-		pr_err("Failed to retrieve QSEE version information %d\n", rc);
+		pr_err("Failed to retrieve QSEOS version information %d\n", rc);
 		goto err;
 	}
-	if (qsee_not_legacy)
+	if (qsee_not_legacy) {
+		uint32_t feature = 10;
+
+		qseecom.qsee_version = QSEEE_VERSION_00;
+		rc = scm_call(6, 3, &feature, sizeof(feature),
+			&qseecom.qsee_version, sizeof(qseecom.qsee_version));
+		if (rc) {
+			pr_err("Failed to get QSEE version info %d\n", rc);
+			goto err;
+		}
 		qseecom.qseos_version = QSEOS_VERSION_14;
-	else {
+	} else {
 		qseecom.qseos_version = QSEOS_VERSION_13;
+		qseecom.qsee_version = 0;
 		pil = NULL;
 		pil_ref_cnt = 0;
 	}
+	qseecom.commonlib_loaded = false;
+	qseecom.pdev = class_dev;
 	/* Create ION msm client */
-	qseecom.ion_clnt = msm_ion_client_create(0x03, "qseecom-kernel");
+	qseecom.ion_clnt = msm_ion_client_create(-1, "qseecom-kernel");
 	if (qseecom.ion_clnt == NULL) {
 		pr_err("Ion client cannot be created\n");
 		rc = -ENOMEM;
@@ -1805,17 +2467,23 @@
 	}
 
 	/* register client for bus scaling */
-	if (!pdev->dev.of_node) {
+	if (pdev->dev.of_node) {
+		ret = __qseecom_init_clk();
+		if (ret)
+			goto err;
+		qseecom_platform_support = (struct msm_bus_scale_pdata *)
+						msm_bus_cl_get_pdata(pdev);
+	} else {
 		qseecom_platform_support = (struct msm_bus_scale_pdata *)
 						pdev->dev.platform_data;
-		qsee_perf_client = msm_bus_scale_register_client(
-						qseecom_platform_support);
-
-		if (!qsee_perf_client)
-			pr_err("Unable to register bus client\n");
 	}
-	return 0;
 
+	qsee_perf_client = msm_bus_scale_register_client(
+					qseecom_platform_support);
+
+	if (!qsee_perf_client)
+		pr_err("Unable to register bus client\n");
+	return 0;
 err:
 	device_destroy(driver_class, qseecom_device_no);
 class_destroy:
@@ -1827,9 +2495,62 @@
 
 static int __devinit qseecom_remove(struct platform_device *pdev)
 {
+	struct qseecom_registered_kclient_list *kclient = NULL;
+	unsigned long flags = 0;
+	int ret = 0;
+
 	if (pdev->dev.platform_data != NULL)
 		msm_bus_scale_unregister_client(qsee_perf_client);
-	return 0;
+
+	spin_lock_irqsave(&qseecom.registered_kclient_list_lock, flags);
+	kclient = list_entry((&qseecom.registered_kclient_list_head)->next,
+		struct qseecom_registered_kclient_list, list);
+	if (list_empty(&kclient->list)) {
+		spin_unlock_irqrestore(&qseecom.registered_kclient_list_lock,
+			flags);
+		return 0;
+	}
+	list_for_each_entry(kclient, &qseecom.registered_kclient_list_head,
+				list) {
+			if (kclient)
+				list_del(&kclient->list);
+			break;
+	}
+	spin_unlock_irqrestore(&qseecom.registered_kclient_list_lock, flags);
+
+
+	while (kclient->handle != NULL) {
+		ret = qseecom_unload_app(kclient->handle->dev);
+		if (ret == 0) {
+			kzfree(kclient->handle->dev);
+			kzfree(kclient->handle);
+			kzfree(kclient);
+		}
+		spin_lock_irqsave(&qseecom.registered_kclient_list_lock, flags);
+		kclient = list_entry(
+				(&qseecom.registered_kclient_list_head)->next,
+				struct qseecom_registered_kclient_list, list);
+		if (list_empty(&kclient->list)) {
+			spin_unlock_irqrestore(
+				&qseecom.registered_kclient_list_lock, flags);
+			return 0;
+		}
+		list_for_each_entry(kclient,
+				&qseecom.registered_kclient_list_head, list) {
+			if (kclient)
+				list_del(&kclient->list);
+			break;
+		}
+		spin_unlock_irqrestore(&qseecom.registered_kclient_list_lock,
+				flags);
+		if (!kclient) {
+			ret = 0;
+			break;
+		}
+	}
+	if (qseecom.qseos_version  > QSEEE_VERSION_00)
+		qseecom_unload_commonlib_image();
+	return ret;
 };
 
 static struct of_device_id qseecom_match[] = {
@@ -1856,6 +2577,9 @@
 
 static void __devexit qseecom_exit(void)
 {
+
+	__qseecom_disable_clk();
+
 	device_destroy(driver_class, qseecom_device_no);
 	class_destroy(driver_class);
 	unregister_chrdev_region(qseecom_device_no, 1);
diff --git a/drivers/misc/qseecom_kernel.h b/drivers/misc/qseecom_kernel.h
new file mode 100644
index 0000000..bfa5709
--- /dev/null
+++ b/drivers/misc/qseecom_kernel.h
@@ -0,0 +1,36 @@
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __QSEECOM_KERNEL_H_
+#define __QSEECOM_KERNEL_H_
+
+#include <linux/types.h>
+/*
+ * struct qseecom_handle -
+ *      Handle to the qseecom device for kernel clients
+ * @sbuf - shared buffer pointer
+ * @sbbuf_len - shared buffer size
+ */
+struct qseecom_handle {
+	void *dev; /* in/out */
+	unsigned char *sbuf; /* in/out */
+	uint32_t sbuf_len; /* in/out */
+};
+
+int qseecom_start_app(struct qseecom_handle **handle,
+						char *app_name, uint32_t size);
+int qseecom_shutdown_app(struct qseecom_handle **handle);
+int qseecom_send_command(struct qseecom_handle *handle, void *send_buf,
+			uint32_t sbuf_len, void *resp_buf, uint32_t rbuf_len);
+
+
+#endif /* __QSEECOM_KERNEL_H_ */
diff --git a/drivers/misc/tsif.c b/drivers/misc/tsif.c
index 1ff4468..b7b1203 100644
--- a/drivers/misc/tsif.c
+++ b/drivers/misc/tsif.c
@@ -140,6 +140,10 @@
 	unsigned int irq;
 	int mode;
 	u32 time_limit;
+	int clock_inverse;
+	int data_inverse;
+	int sync_inverse;
+	int enable_inverse;
 	enum tsif_state state;
 	struct wake_lock wake_lock;
 	/* clocks */
@@ -358,6 +362,19 @@
 		  TSIF_STS_CTL_EN_TIME_LIM |
 		  TSIF_STS_CTL_EN_TCR |
 		  TSIF_STS_CTL_EN_DM;
+
+	if (tsif_device->clock_inverse)
+		ctl |= TSIF_STS_CTL_INV_CLOCK;
+
+	if (tsif_device->data_inverse)
+		ctl |= TSIF_STS_CTL_INV_DATA;
+
+	if (tsif_device->sync_inverse)
+		ctl |= TSIF_STS_CTL_INV_SYNC;
+
+	if (tsif_device->enable_inverse)
+		ctl |= TSIF_STS_CTL_INV_ENABLE;
+
 	dev_info(&tsif_device->pdev->dev, "%s\n", __func__);
 	switch (tsif_device->mode) {
 	case 1: /* mode 1 */
@@ -805,6 +822,10 @@
 			"Client     = %p\n"
 			"Pkt/Buf    = %d\n"
 			"Pkt/chunk  = %d\n"
+			"Clock inv  = %d\n"
+			"Data inv   = %d\n"
+			"Sync inv   = %d\n"
+			"Enable inv = %d\n"
 			"--statistics--\n"
 			"Rx chunks  = %d\n"
 			"Overflow   = %d\n"
@@ -827,6 +848,10 @@
 			tsif_device->client_data,
 			TSIF_PKTS_IN_BUF,
 			TSIF_PKTS_IN_CHUNK,
+			tsif_device->clock_inverse,
+			tsif_device->data_inverse,
+			tsif_device->sync_inverse,
+			tsif_device->enable_inverse,
 			tsif_device->stat_rx,
 			tsif_device->stat_overflow,
 			tsif_device->stat_lost_sync,
@@ -950,11 +975,120 @@
 static DEVICE_ATTR(buf_config, S_IRUGO | S_IWUSR,
 		   show_buf_config, set_buf_config);
 
+static ssize_t show_clk_inverse(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	struct msm_tsif_device *tsif_device = dev_get_drvdata(dev);
+	return snprintf(buf, PAGE_SIZE, "%d\n", tsif_device->clock_inverse);
+}
+
+static ssize_t set_clk_inverse(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct msm_tsif_device *tsif_device = dev_get_drvdata(dev);
+	int value;
+	int rc;
+	if (1 != sscanf(buf, "%d", &value)) {
+		dev_err(&tsif_device->pdev->dev,
+			"Failed to parse integer: <%s>\n", buf);
+		return -EINVAL;
+	}
+	rc = tsif_set_clk_inverse(tsif_device, value);
+	if (!rc)
+		rc = count;
+	return rc;
+}
+static DEVICE_ATTR(clk_inverse, S_IRUGO | S_IWUSR,
+	show_clk_inverse, set_clk_inverse);
+
+static ssize_t show_data_inverse(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	struct msm_tsif_device *tsif_device = dev_get_drvdata(dev);
+	return snprintf(buf, PAGE_SIZE, "%d\n", tsif_device->data_inverse);
+}
+
+static ssize_t set_data_inverse(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct msm_tsif_device *tsif_device = dev_get_drvdata(dev);
+	int value;
+	int rc;
+	if (1 != sscanf(buf, "%d", &value)) {
+		dev_err(&tsif_device->pdev->dev,
+			"Failed to parse integer: <%s>\n", buf);
+		return -EINVAL;
+	}
+	rc = tsif_set_data_inverse(tsif_device, value);
+	if (!rc)
+		rc = count;
+	return rc;
+}
+static DEVICE_ATTR(data_inverse, S_IRUGO | S_IWUSR,
+	show_data_inverse, set_data_inverse);
+
+static ssize_t show_sync_inverse(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	struct msm_tsif_device *tsif_device = dev_get_drvdata(dev);
+	return snprintf(buf, PAGE_SIZE, "%d\n", tsif_device->sync_inverse);
+}
+
+static ssize_t set_sync_inverse(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct msm_tsif_device *tsif_device = dev_get_drvdata(dev);
+	int value;
+	int rc;
+	if (1 != sscanf(buf, "%d", &value)) {
+		dev_err(&tsif_device->pdev->dev,
+			"Failed to parse integer: <%s>\n", buf);
+		return -EINVAL;
+	}
+	rc = tsif_set_sync_inverse(tsif_device, value);
+	if (!rc)
+		rc = count;
+	return rc;
+}
+static DEVICE_ATTR(sync_inverse, S_IRUGO | S_IWUSR,
+	show_sync_inverse, set_sync_inverse);
+
+static ssize_t show_enable_inverse(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	struct msm_tsif_device *tsif_device = dev_get_drvdata(dev);
+	return snprintf(buf, PAGE_SIZE, "%d\n", tsif_device->enable_inverse);
+}
+
+static ssize_t set_enable_inverse(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct msm_tsif_device *tsif_device = dev_get_drvdata(dev);
+	int value;
+	int rc;
+	if (1 != sscanf(buf, "%d", &value)) {
+		dev_err(&tsif_device->pdev->dev,
+			"Failed to parse integer: <%s>\n", buf);
+		return -EINVAL;
+	}
+	rc = tsif_set_enable_inverse(tsif_device, value);
+	if (!rc)
+		rc = count;
+	return rc;
+}
+static DEVICE_ATTR(enable_inverse, S_IRUGO | S_IWUSR,
+	show_enable_inverse, set_enable_inverse);
+
+
 static struct attribute *dev_attrs[] = {
 	&dev_attr_stats.attr,
 	&dev_attr_mode.attr,
 	&dev_attr_time_limit.attr,
 	&dev_attr_buf_config.attr,
+	&dev_attr_clk_inverse.attr,
+	&dev_attr_data_inverse.attr,
+	&dev_attr_sync_inverse.attr,
+	&dev_attr_enable_inverse.attr,
 	NULL,
 };
 static struct attribute_group dev_attr_grp = {
@@ -1287,6 +1421,10 @@
 	tsif_device->pdev = pdev;
 	platform_set_drvdata(pdev, tsif_device);
 	tsif_device->mode = 1;
+	tsif_device->clock_inverse = 0;
+	tsif_device->data_inverse = 0;
+	tsif_device->sync_inverse = 0;
+	tsif_device->enable_inverse = 0;
 	tsif_device->pkts_per_chunk = TSIF_PKTS_IN_CHUNK_DEFAULT;
 	tsif_device->chunks_per_buf = TSIF_CHUNKS_IN_BUF_DEFAULT;
 	tasklet_init(&tsif_device->dma_refill, tsif_dma_refill,
@@ -1534,6 +1672,78 @@
 }
 EXPORT_SYMBOL(tsif_set_buf_config);
 
+int tsif_set_clk_inverse(void *cookie, int value)
+{
+	struct msm_tsif_device *tsif_device = cookie;
+	if (tsif_device->state != tsif_state_stopped) {
+		dev_err(&tsif_device->pdev->dev,
+			"Can't change clock inverse while device is active\n");
+		return -EBUSY;
+	}
+	if ((value != 0) && (value != 1)) {
+		dev_err(&tsif_device->pdev->dev,
+			"Invalid parameter, either 0 or 1: %#x\n", value);
+		return -EINVAL;
+	}
+	tsif_device->clock_inverse = value;
+	return 0;
+}
+EXPORT_SYMBOL(tsif_set_clk_inverse);
+
+int tsif_set_data_inverse(void *cookie, int value)
+{
+	struct msm_tsif_device *tsif_device = cookie;
+	if (tsif_device->state != tsif_state_stopped) {
+		dev_err(&tsif_device->pdev->dev,
+			"Can't change data inverse while device is active\n");
+		return -EBUSY;
+	}
+	if ((value != 0) && (value != 1)) {
+		dev_err(&tsif_device->pdev->dev,
+			"Invalid parameter, either 0 or 1: %#x\n", value);
+		return -EINVAL;
+	}
+	tsif_device->data_inverse = value;
+	return 0;
+}
+EXPORT_SYMBOL(tsif_set_data_inverse);
+
+int tsif_set_sync_inverse(void *cookie, int value)
+{
+	struct msm_tsif_device *tsif_device = cookie;
+	if (tsif_device->state != tsif_state_stopped) {
+		dev_err(&tsif_device->pdev->dev,
+			"Can't change sync inverse while device is active\n");
+		return -EBUSY;
+	}
+	if ((value != 0) && (value != 1)) {
+		dev_err(&tsif_device->pdev->dev,
+			"Invalid parameter, either 0 or 1: %#x\n", value);
+		return -EINVAL;
+	}
+	tsif_device->sync_inverse = value;
+	return 0;
+}
+EXPORT_SYMBOL(tsif_set_sync_inverse);
+
+int tsif_set_enable_inverse(void *cookie, int value)
+{
+	struct msm_tsif_device *tsif_device = cookie;
+	if (tsif_device->state != tsif_state_stopped) {
+		dev_err(&tsif_device->pdev->dev,
+			"Can't change enable inverse while device is active\n");
+		return -EBUSY;
+	}
+	if ((value != 0) && (value != 1)) {
+		dev_err(&tsif_device->pdev->dev,
+			"Invalid parameter, either 0 or 1: %#x\n", value);
+		return -EINVAL;
+	}
+	tsif_device->enable_inverse = value;
+	return 0;
+}
+EXPORT_SYMBOL(tsif_set_enable_inverse);
+
 void tsif_get_state(void *cookie, int *ri, int *wi, enum tsif_state *state)
 {
 	struct msm_tsif_device *tsif_device = cookie;
diff --git a/drivers/misc/tspp.c b/drivers/misc/tspp.c
index 1792104..3b678c5 100644
--- a/drivers/misc/tspp.c
+++ b/drivers/misc/tspp.c
@@ -103,29 +103,29 @@
 /*
  * TSPP register offsets
  */
-#define TSPP_RST					0x00
+#define TSPP_RST			0x00
 #define TSPP_CLK_CONTROL		0x04
-#define TSPP_CONFIG				0x08
-#define TSPP_CONTROL				0x0C
+#define TSPP_CONFIG			0x08
+#define TSPP_CONTROL			0x0C
 #define TSPP_PS_DISABLE			0x10
-#define TSPP_MSG_IRQ_STATUS	0x14
+#define TSPP_MSG_IRQ_STATUS		0x14
 #define TSPP_MSG_IRQ_MASK		0x18
 #define TSPP_IRQ_STATUS			0x1C
 #define TSPP_IRQ_MASK			0x20
 #define TSPP_IRQ_CLEAR			0x24
 #define TSPP_PIPE_ERROR_STATUS(_n)	(0x28 + (_n << 2))
-#define TSPP_STATUS				0x68
-#define TSPP_CURR_TSP_HEADER	0x6C
-#define TSPP_CURR_PID_FILTER	0x70
-#define TSPP_SYSTEM_KEY(_n)	(0x74 + (_n << 2))
-#define TSPP_CBC_INIT_VAL(_n)	(0x94 + (_n << 2))
-#define TSPP_DATA_KEY_RESET	0x9C
+#define TSPP_STATUS			0x68
+#define TSPP_CURR_TSP_HEADER		0x6C
+#define TSPP_CURR_PID_FILTER		0x70
+#define TSPP_SYSTEM_KEY(_n)		(0x74 + (_n << 2))
+#define TSPP_CBC_INIT_VAL(_n)		(0x94 + (_n << 2))
+#define TSPP_DATA_KEY_RESET		0x9C
 #define TSPP_KEY_VALID			0xA0
 #define TSPP_KEY_ERROR			0xA4
 #define TSPP_TEST_CTRL			0xA8
-#define TSPP_VERSION				0xAC
+#define TSPP_VERSION			0xAC
 #define TSPP_GENERICS			0xB0
-#define TSPP_NOP					0xB4
+#define TSPP_NOP			0xB4
 
 /*
  * Register bit definitions
@@ -172,30 +172,30 @@
 #define TSPP_MSG_TSIF_0_IRQ               BIT(0)
 
 /* TSPP_IRQ_STATUS + TSPP_IRQ_MASK + TSPP_IRQ_CLEAR */
-#define TSPP_IRQ_STATUS_TSP_RD_CMPL			BIT(19)
-#define TSPP_IRQ_STATUS_KEY_ERROR			BIT(18)
+#define TSPP_IRQ_STATUS_TSP_RD_CMPL		BIT(19)
+#define TSPP_IRQ_STATUS_KEY_ERROR		BIT(18)
 #define TSPP_IRQ_STATUS_KEY_SWITCHED_BAD	BIT(17)
 #define TSPP_IRQ_STATUS_KEY_SWITCHED		BIT(16)
 #define TSPP_IRQ_STATUS_PS_BROKEN(_n)		BIT((_n))
 
 /* TSPP_PIPE_ERROR_STATUS */
-#define TSPP_PIPE_PES_SYNC_ERROR				BIT(3)
-#define TSPP_PIPE_PS_LENGTH_ERROR			BIT(2)
+#define TSPP_PIPE_PES_SYNC_ERROR		BIT(3)
+#define TSPP_PIPE_PS_LENGTH_ERROR		BIT(2)
 #define TSPP_PIPE_PS_CONTINUITY_ERROR		BIT(1)
-#define TSPP_PIP_PS_LOST_START				BIT(0)
+#define TSPP_PIP_PS_LOST_START			BIT(0)
 
 /* TSPP_STATUS			*/
-#define TSPP_STATUS_TSP_PKT_AVAIL			BIT(10)
-#define TSPP_STATUS_TSIF1_DM_REQ				BIT(6)
-#define TSPP_STATUS_TSIF0_DM_REQ				BIT(2)
-#define TSPP_CURR_FILTER_TABLE				BIT(0)
+#define TSPP_STATUS_TSP_PKT_AVAIL		BIT(10)
+#define TSPP_STATUS_TSIF1_DM_REQ		BIT(6)
+#define TSPP_STATUS_TSIF0_DM_REQ		BIT(2)
+#define TSPP_CURR_FILTER_TABLE			BIT(0)
 
 /* TSPP_GENERICS		*/
-#define TSPP_GENERICS_CRYPTO_GEN				BIT(12)
+#define TSPP_GENERICS_CRYPTO_GEN		BIT(12)
 #define TSPP_GENERICS_MAX_CONS_PIPES		BIT(7)
-#define TSPP_GENERICS_MAX_PIPES				BIT(2)
-#define TSPP_GENERICS_TSIF_1_GEN				BIT(1)
-#define TSPP_GENERICS_TSIF_0_GEN				BIT(0)
+#define TSPP_GENERICS_MAX_PIPES			BIT(2)
+#define TSPP_GENERICS_TSIF_1_GEN		BIT(1)
+#define TSPP_GENERICS_TSIF_0_GEN		BIT(0)
 
 /*
  * TSPP memory regions
@@ -323,6 +323,10 @@
 	u32 time_limit;
 	u32 ref_count;
 	enum tspp_tsif_mode mode;
+	int clock_inverse;
+	int data_inverse;
+	int sync_inverse;
+	int enable_inverse;
 
 	/* debugfs */
 	struct dentry *dent_tsif;
@@ -371,6 +375,8 @@
 	tspp_notifier *notifier; /* used only with kernel api */
 	void *notify_data;       /* data to be passed with the notifier */
 	u32 notify_timer;        /* notification for partially filled buffers */
+	tspp_memfree *memfree;   /* user defined memory free function */
+	void *user_info; /* user cookie passed to memory alloc/free function */
 };
 
 struct tspp_pid_filter_table {
@@ -580,8 +586,7 @@
 		g = table + i;
 		tmp = gpio_tlmm_config(g->gpio_cfg, GPIO_CFG_DISABLE);
 		if (tmp) {
-			pr_err("tspp_gpios_disable(0x%08x, GPIO_CFG_DISABLE)"
-			       " <%s> failed: %d\n",
+			pr_err("tspp_gpios_disable(0x%08x, GPIO_CFG_DISABLE) <%s> failed: %d\n",
 			       g->gpio_cfg, g->label ?: "?", rc);
 			pr_err("tspp: pin %d func %d dir %d pull %d drvstr %d\n",
 			       GPIO_PIN(g->gpio_cfg), GPIO_FUNC(g->gpio_cfg),
@@ -604,8 +609,7 @@
 		g = table + i;
 		rc = gpio_tlmm_config(g->gpio_cfg, GPIO_CFG_ENABLE);
 		if (rc) {
-			pr_err("tspp: gpio_tlmm_config(0x%08x, GPIO_CFG_ENABLE)"
-			       " <%s> failed: %d\n",
+			pr_err("tspp: gpio_tlmm_config(0x%08x, GPIO_CFG_ENABLE) <%s> failed: %d\n",
 			       g->gpio_cfg, g->label ?: "?", rc);
 			pr_err("tspp: pin %d func %d dir %d pull %d drvstr %d\n",
 			       GPIO_PIN(g->gpio_cfg), GPIO_FUNC(g->gpio_cfg),
@@ -698,6 +702,19 @@
 	if (start_hardware) {
 		ctl = TSIF_STS_CTL_EN_IRQ |
 				TSIF_STS_CTL_EN_DM;
+
+		if (tsif_device->clock_inverse)
+			ctl |= TSIF_STS_CTL_INV_CLOCK;
+
+		if (tsif_device->data_inverse)
+			ctl |= TSIF_STS_CTL_INV_DATA;
+
+		if (tsif_device->sync_inverse)
+			ctl |= TSIF_STS_CTL_INV_SYNC;
+
+		if (tsif_device->enable_inverse)
+			ctl |= TSIF_STS_CTL_INV_ENABLE;
+
 		switch (tsif_device->mode) {
 		case TSPP_TSIF_MODE_LOOPBACK:
 			ctl |= TSIF_STS_CTL_EN_NULL |
@@ -803,12 +820,12 @@
 		desc->virt_base = alloc(channel_id, size,
 			&desc->phys_base, user);
 	} else {
-	desc->virt_base = dma_alloc_coherent(NULL, size,
-		&desc->phys_base, GFP_KERNEL);
-	if (desc->virt_base == 0) {
-		pr_err("tspp dma alloc coherent failed %i", size);
-		return -ENOMEM;
-	}
+		desc->virt_base = dma_alloc_coherent(NULL, size,
+			&desc->phys_base, GFP_KERNEL);
+		if (desc->virt_base == 0) {
+			pr_err("tspp dma alloc coherent failed %i", size);
+			return -ENOMEM;
+		}
 	}
 
 	desc->size = size;
@@ -852,6 +869,10 @@
 		pdev->tsif[i].ref_count = 1; /* allows stopping hw */
 		tspp_stop_tsif(&pdev->tsif[i]); /* will reset ref_count to 0 */
 		pdev->tsif[i].time_limit = TSPP_TSIF_DEFAULT_TIME_LIMIT;
+		pdev->tsif[i].clock_inverse = 0;
+		pdev->tsif[i].data_inverse = 0;
+		pdev->tsif[i].sync_inverse = 0;
+		pdev->tsif[i].enable_inverse = 0;
 	}
 	writel_relaxed(TSPP_RST_RESET, pdev->base + TSPP_RST);
 	wmb();
@@ -913,7 +934,7 @@
 	}
 
 	/* open the stream */
-	tspp_open_stream(dev, channel_id, src->source, src->mode);
+	tspp_open_stream(dev, channel_id, src);
 
 	return 0;
 }
@@ -956,9 +977,13 @@
 	channel->buffer_count = 0;
 	channel->filter_count = 0;
 	channel->int_freq = 1;
+	channel->src = TSPP_SOURCE_NONE;
+	channel->mode = TSPP_MODE_DISABLED;
 	channel->notifier = NULL;
 	channel->notify_data = NULL;
 	channel->notify_timer = 0;
+	channel->memfree = NULL;
+	channel->user_info = NULL;
 	init_waitqueue_head(&channel->in_queue);
 
 	if (cdev_add(&channel->cdev, tspp_minor++, 1) != 0) {
@@ -981,6 +1006,11 @@
 static int tspp_set_buffer_size(struct tspp_channel *channel,
 	struct tspp_buffer *buf)
 {
+	if (channel->buffer_count > 0) {
+		pr_err("tspp: cannot set buffer size - buffers already allocated\n");
+		return -EPERM;
+	}
+
 	if (buf->size < TSPP_MIN_BUFFER_SIZE)
 		channel->buffer_size = TSPP_MIN_BUFFER_SIZE;
 	else if (buf->size > TSPP_MAX_BUFFER_SIZE)
@@ -1011,14 +1041,136 @@
 	channel->pdev->tsif[index].mode = mode;
 }
 
+static void tspp_set_signal_inversion(struct tspp_channel *channel,
+					int clock_inverse, int data_inverse,
+					int sync_inverse, int enable_inverse)
+{
+	int index;
+
+	switch (channel->src) {
+	case TSPP_SOURCE_TSIF0:
+		index = 0;
+		break;
+	case TSPP_SOURCE_TSIF1:
+		index = 1;
+		break;
+	default:
+		return;
+	}
+	channel->pdev->tsif[index].clock_inverse = clock_inverse;
+	channel->pdev->tsif[index].data_inverse = data_inverse;
+	channel->pdev->tsif[index].sync_inverse = sync_inverse;
+	channel->pdev->tsif[index].enable_inverse = enable_inverse;
+}
+
+static int tspp_is_buffer_size_aligned(u32 size, enum tspp_mode mode)
+{
+	u32 alignment;
+
+	switch (mode) {
+	case TSPP_MODE_RAW:
+		/* must be a multiple of 192 */
+		alignment = (TSPP_PACKET_LENGTH + 4);
+		if (size % alignment)
+			return 0;
+		return 1;
+
+	case TSPP_MODE_RAW_NO_SUFFIX:
+		/* must be a multiple of 188 */
+		alignment = TSPP_PACKET_LENGTH;
+		if (size % alignment)
+			return 0;
+		return 1;
+
+	case TSPP_MODE_DISABLED:
+	case TSPP_MODE_PES:
+	default:
+		/* no alignment requirement */
+		return 1;
+	}
+
+}
+
+static u32 tspp_align_buffer_size_by_mode(u32 size, enum tspp_mode mode)
+{
+	u32 new_size;
+	u32 alignment;
+
+	switch (mode) {
+	case TSPP_MODE_RAW:
+		/* must be a multiple of 192 */
+		alignment = (TSPP_PACKET_LENGTH + 4);
+		break;
+
+	case TSPP_MODE_RAW_NO_SUFFIX:
+		/* must be a multiple of 188 */
+		alignment = TSPP_PACKET_LENGTH;
+		break;
+
+	case TSPP_MODE_DISABLED:
+	case TSPP_MODE_PES:
+	default:
+		/* no alignment requirement - give the user what he asks for */
+		alignment = 1;
+		break;
+	}
+	/* align up */
+	new_size = (((size + alignment - 1) / alignment) * alignment);
+	return new_size;
+}
+
+static void tspp_destroy_buffers(u32 channel_id, struct tspp_channel *channel)
+{
+	int i;
+	struct tspp_mem_buffer *pbuf, *temp;
+
+	pbuf = channel->data;
+	for (i = 0; i < channel->buffer_count; i++) {
+		if (pbuf->desc.phys_base) {
+			if (channel->memfree) {
+				channel->memfree(channel_id,
+					pbuf->desc.size,
+					pbuf->desc.virt_base,
+					pbuf->desc.phys_base,
+					channel->user_info);
+			} else {
+				dma_free_coherent(NULL,
+					pbuf->desc.size,
+					pbuf->desc.virt_base,
+					pbuf->desc.phys_base);
+			}
+			pbuf->desc.phys_base = 0;
+		}
+		pbuf->desc.virt_base = 0;
+		pbuf->state = TSPP_BUF_STATE_EMPTY;
+		temp = pbuf;
+		pbuf = pbuf->next;
+		kfree(temp);
+	}
+}
+
 /*** TSPP API functions ***/
-int tspp_open_stream(u32 dev, u32 channel_id, enum tspp_source src, enum tspp_tsif_mode mode)
+
+/**
+ * tspp_open_stream - open a TSPP stream for use.
+ *
+ * @dev: TSPP device (up to TSPP_MAX_DEVICES)
+ * @channel_id: Channel ID number (up to TSPP_NUM_CHANNELS)
+ * @source: stream source parameters.
+ *
+ * Return  error status
+ *
+ */
+int tspp_open_stream(u32 dev, u32 channel_id,
+			struct tspp_select_source *source)
 {
 	u32 val;
 	struct tspp_device *pdev;
 	struct tspp_channel *channel;
 
-	TSPP_DEBUG("tspp_open_stream %i %i %i %i", dev, channel_id, src, mode);
+	TSPP_DEBUG("tspp_open_stream %i %i %i %i",
+		dev, channel_id, source->source, source->mode);
+
 	if (dev >= TSPP_MAX_DEVICES) {
 		pr_err("tspp: device id out of range");
 		return -ENODEV;
@@ -1035,10 +1187,13 @@
 		return -ENODEV;
 	}
 	channel = &pdev->channels[channel_id];
-	channel->src = src;
-	tspp_set_tsif_mode(channel, mode);
+	channel->src = source->source;
+	tspp_set_tsif_mode(channel, source->mode);
+	tspp_set_signal_inversion(channel, source->clk_inverse,
+			source->data_inverse, source->sync_inverse,
+			source->enable_inverse);
 
-	switch (src) {
+	switch (source->source) {
 	case TSPP_SOURCE_TSIF0:
 		/* make sure TSIF0 is running & enabled */
 		if (tspp_start_tsif(&pdev->tsif[0]) != 0) {
@@ -1064,7 +1219,8 @@
 	case TSPP_SOURCE_MEM:
 		break;
 	default:
-		pr_err("tspp: channel %i invalid source %i", channel->id, src);
+		pr_err("tspp: channel %i invalid source %i",
+			channel->id, source->source);
 		return -EBUSY;
 	}
 
@@ -1072,6 +1228,15 @@
 }
 EXPORT_SYMBOL(tspp_open_stream);
 
+/**
+ * tspp_close_stream - close a TSPP stream.
+ *
+ * @dev: TSPP device (up to TSPP_MAX_DEVICES)
+ * @channel_id: Channel ID number (up to TSPP_NUM_CHANNELS)
+ *
+ * Return  error status
+ *
+ */
 int tspp_close_stream(u32 dev, u32 channel_id)
 {
 	u32 val;
@@ -1114,6 +1279,15 @@
 }
 EXPORT_SYMBOL(tspp_close_stream);
 
+/**
+ * tspp_open_channel - open a TSPP channel.
+ *
+ * @dev: TSPP device (up to TSPP_MAX_DEVICES)
+ * @channel_id: Channel ID number (up to TSPP_NUM_CHANNELS)
+ *
+ * Return  error status
+ *
+ */
 int tspp_open_channel(u32 dev, u32 channel_id)
 {
 	int rc = 0;
@@ -1221,6 +1395,15 @@
 }
 EXPORT_SYMBOL(tspp_open_channel);
 
+/**
+ * tspp_close_channel - close a TSPP channel.
+ *
+ * @dev: TSPP device (up to TSPP_MAX_DEVICES)
+ * @channel_id: Channel ID number (up to TSPP_NUM_CHANNELS)
+ *
+ * Return  error status
+ *
+ */
 int tspp_close_channel(u32 dev, u32 channel_id)
 {
 	int i;
@@ -1230,7 +1413,6 @@
 	struct sps_connect *config;
 	struct tspp_device *pdev;
 	struct tspp_channel *channel;
-	struct tspp_mem_buffer *pbuf, *temp;
 
 	if (channel_id >= TSPP_NUM_CHANNELS) {
 		pr_err("tspp: channel id out of range");
@@ -1285,21 +1467,12 @@
 	dma_free_coherent(NULL, config->desc.size, config->desc.base,
 		config->desc.phys_base);
 
-	pbuf = channel->data;
-	for (i = 0; i < channel->buffer_count; i++) {
-		if (pbuf->desc.phys_base) {
-			dma_free_coherent(NULL,
-				pbuf->desc.size,
-				pbuf->desc.virt_base,
-				pbuf->desc.phys_base);
-			pbuf->desc.phys_base = 0;
-		}
-		pbuf->desc.virt_base = 0;
-		pbuf->state = TSPP_BUF_STATE_EMPTY;
-		temp = pbuf;
-		pbuf = pbuf->next;
-		kfree(temp);
-	}
+	tspp_destroy_buffers(channel_id, channel);
+
+	channel->src = TSPP_SOURCE_NONE;
+	channel->mode = TSPP_MODE_DISABLED;
+	channel->memfree = NULL;
+	channel->user_info = NULL;
 	channel->buffer_count = 0;
 	channel->data = NULL;
 	channel->read = NULL;
@@ -1315,10 +1488,20 @@
 }
 EXPORT_SYMBOL(tspp_close_channel);
 
+/**
+ * tspp_add_filter - add a TSPP filter to a channel.
+ *
+ * @dev: TSPP device (up to TSPP_MAX_DEVICES)
+ * @channel_id: Channel ID number (up to TSPP_NUM_CHANNELS)
+ * @filter: TSPP filter parameters
+ *
+ * Return  error status
+ *
+ */
 int tspp_add_filter(u32 dev, u32 channel_id,
 	struct tspp_filter *filter)
 {
-	int i;
+	int i, rc;
 	int other_channel;
 	int entry;
 	u32 val, pid, enabled;
@@ -1349,19 +1532,14 @@
 		return -ENOSR;
 	}
 
-	/* make sure this filter mode matches the channel mode */
-	switch (channel->mode) {
-	case TSPP_MODE_DISABLED:
-		channel->mode = filter->mode;
-		break;
-	case TSPP_MODE_RAW:
-	case TSPP_MODE_PES:
-	case TSPP_MODE_RAW_NO_SUFFIX:
-		if (filter->mode != channel->mode) {
-			pr_err("tspp: wrong filter mode");
-			return -EBADSLT;
-		}
-	}
+	channel->mode = filter->mode;
+	/*
+	 * if buffers are already allocated, verify they fulfil
+	 * the alignment requirements.
+	 */
+	if ((channel->buffer_count > 0) &&
+	   (!tspp_is_buffer_size_aligned(channel->buffer_size, channel->mode)))
+		pr_warn("tspp: buffers allocated with incorrect alignment\n");
 
 	if (filter->mode == TSPP_MODE_PES) {
 		for (i = 0; i < TSPP_NUM_PRIORITIES; i++) {
@@ -1420,13 +1598,22 @@
 	pdev->filters[channel->src]->
 		filter[filter->priority].filter = p.filter;
 
-	/* allocate buffers if needed */
-	tspp_allocate_buffers(dev, channel->id, channel->max_buffers,
-		channel->buffer_size, channel->int_freq, 0, 0);
-	if (channel->buffer_count < MIN_ACCEPTABLE_BUFFER_COUNT) {
-		pr_err("tspp: failed to allocate at least %i buffers",
-			MIN_ACCEPTABLE_BUFFER_COUNT);
-		return -ENOMEM;
+	/*
+	 * allocate buffers if needed (i.e. if user did has not already called
+	 * tspp_allocate_buffers() explicitly).
+	 */
+	if (channel->buffer_count == 0) {
+		channel->buffer_size =
+			tspp_align_buffer_size_by_mode(channel->buffer_size,
+							channel->mode);
+		rc = tspp_allocate_buffers(dev, channel->id,
+					channel->max_buffers,
+					channel->buffer_size,
+					channel->int_freq, NULL, NULL, NULL);
+		if (rc != 0) {
+			pr_err("tspp: tspp_allocate_buffers failed\n");
+			return rc;
+		}
 	}
 
 	/* reenable pipe */
@@ -1441,6 +1628,16 @@
 }
 EXPORT_SYMBOL(tspp_add_filter);
 
+/**
+ * tspp_remove_filter - remove a TSPP filter from a channel.
+ *
+ * @dev: TSPP device (up to TSPP_MAX_DEVICES)
+ * @channel_id: Channel ID number (up to TSPP_NUM_CHANNELS)
+ * @filter: TSPP filter parameters
+ *
+ * Return  error status
+ *
+ */
 int tspp_remove_filter(u32 dev, u32 channel_id,
 	struct tspp_filter *filter)
 {
@@ -1493,6 +1690,16 @@
 }
 EXPORT_SYMBOL(tspp_remove_filter);
 
+/**
+ * tspp_set_key - set TSPP key in key table.
+ *
+ * @dev: TSPP device (up to TSPP_MAX_DEVICES)
+ * @channel_id: Channel ID number (up to TSPP_NUM_CHANNELS)
+ * @key: TSPP key parameters
+ *
+ * Return  error status
+ *
+ */
 int tspp_set_key(u32 dev, u32 channel_id, struct tspp_key *key)
 {
 	int i;
@@ -1543,6 +1750,18 @@
 }
 EXPORT_SYMBOL(tspp_set_key);
 
+/**
+ * tspp_register_notification - register TSPP channel notification function.
+ *
+ * @dev: TSPP device (up to TSPP_MAX_DEVICES)
+ * @channel_id: Channel ID number (up to TSPP_NUM_CHANNELS)
+ * @pNotify: notification function
+ * @userdata: user data to pass to notification function
+ * @timer_ms: notification for partially filled buffers
+ *
+ * Return  error status
+ *
+ */
 int tspp_register_notification(u32 dev, u32 channel_id,
 	tspp_notifier *pNotify, void *userdata, u32 timer_ms)
 {
@@ -1566,6 +1785,15 @@
 }
 EXPORT_SYMBOL(tspp_register_notification);
 
+/**
+ * tspp_unregister_notification - unregister TSPP channel notification function.
+ *
+ * @dev: TSPP device (up to TSPP_MAX_DEVICES)
+ * @channel_id: Channel ID number (up to TSPP_NUM_CHANNELS)
+ *
+ * Return  error status
+ *
+ */
 int tspp_unregister_notification(u32 dev, u32 channel_id)
 {
 	struct tspp_channel *channel;
@@ -1587,6 +1815,15 @@
 }
 EXPORT_SYMBOL(tspp_unregister_notification);
 
+/**
+ * tspp_get_buffer - get TSPP data buffer.
+ *
+ * @dev: TSPP device (up to TSPP_MAX_DEVICES)
+ * @channel_id: Channel ID number (up to TSPP_NUM_CHANNELS)
+ *
+ * Return  error status
+ *
+ */
 const struct tspp_data_descriptor *tspp_get_buffer(u32 dev, u32 channel_id)
 {
 	struct tspp_mem_buffer *buffer;
@@ -1627,6 +1864,16 @@
 }
 EXPORT_SYMBOL(tspp_get_buffer);
 
+/**
+ * tspp_release_buffer - release TSPP data buffer back to TSPP.
+ *
+ * @dev: TSPP device (up to TSPP_MAX_DEVICES)
+ * @channel_id: Channel ID number (up to TSPP_NUM_CHANNELS)
+ * @descriptor_id: buffer descriptor ID
+ *
+ * Return  error status
+ *
+ */
 int tspp_release_buffer(u32 dev, u32 channel_id, u32 descriptor_id)
 {
 	int i, found = 0;
@@ -1678,8 +1925,27 @@
 }
 EXPORT_SYMBOL(tspp_release_buffer);
 
-int tspp_allocate_buffers(u32 dev, u32 channel_id,	u32 count,
-	u32 size, u32 int_freq, tspp_allocator *alloc, void *user)
+/**
+ * tspp_allocate_buffers - allocate TSPP data buffers.
+ *
+ * @dev: TSPP device (up to TSPP_MAX_DEVICES)
+ * @channel_id: Channel ID number (up to TSPP_NUM_CHANNELS)
+ * @count: number of buffers to allocate
+ * @size: size of each buffer to allocate
+ * @int_freq: interrupt frequency
+ * @alloc: user defined memory allocator function. Pass NULL for default.
+ * @memfree: user defined memory free function. Pass NULL for default.
+ * @user: user data to pass to the memory allocator/free function
+ *
+ * Return  error status
+ *
+ * The user can optionally call this function explicitly to allocate the TSPP
+ * data buffers. Alternatively, if the user did not call this function, it
+ * is called implicitly by tspp_add_filter().
+ */
+int tspp_allocate_buffers(u32 dev, u32 channel_id, u32 count, u32 size,
+			u32 int_freq, tspp_allocator *alloc,
+			tspp_memfree *memfree, void *user)
 {
 	struct tspp_channel *channel;
 	struct tspp_device *pdev;
@@ -1688,56 +1954,62 @@
 	TSPP_DEBUG("tspp_allocate_buffers");
 
 	if (channel_id >= TSPP_NUM_CHANNELS) {
-		pr_err("tspp: channel id out of range");
+		pr_err("%s: channel id out of range", __func__);
 		return -ECHRNG;
 	}
+
 	pdev = tspp_find_by_id(dev);
 	if (!pdev) {
-		pr_err("tspp_alloc: can't find device %i", dev);
+		pr_err("%s: can't find device %i", __func__, dev);
 		return -ENODEV;
 	}
+
+	if (count < MIN_ACCEPTABLE_BUFFER_COUNT) {
+		pr_err("%s: tspp requires a minimum of %i buffers\n",
+			__func__, MIN_ACCEPTABLE_BUFFER_COUNT);
+		return -EINVAL;
+	}
+
 	channel = &pdev->channels[channel_id];
+	/* allow buffer allocation only if there was no previous buffer
+	 * allocation for this channel.
+	 */
+	if (channel->buffer_count > 0) {
+		pr_err("%s: buffers already allocated for channel %u",
+			__func__, channel_id);
+		return -EINVAL;
+	}
 
 	channel->max_buffers = count;
 
 	/* set up interrupt frequency */
-	if (int_freq > channel->max_buffers)
+	if (int_freq > channel->max_buffers) {
 		int_freq = channel->max_buffers;
-	channel->int_freq = int_freq;
-
-	switch (channel->mode) {
-	case TSPP_MODE_DISABLED:
-	case TSPP_MODE_PES:
-		/* give the user what he asks for */
-		channel->buffer_size = size;
-		break;
-
-	case TSPP_MODE_RAW:
-		/* must be a multiple of 192 */
-		if (size < (TSPP_PACKET_LENGTH+4))
-			channel->buffer_size = (TSPP_PACKET_LENGTH+4);
-		else
-			channel->buffer_size = (size /
-				(TSPP_PACKET_LENGTH+4)) *
-				(TSPP_PACKET_LENGTH+4);
-		break;
-
-	case TSPP_MODE_RAW_NO_SUFFIX:
-		/* must be a multiple of 188 */
-		channel->buffer_size = (size / TSPP_PACKET_LENGTH) *
-			TSPP_PACKET_LENGTH;
-		break;
+		pr_warn("%s: setting interrupt frequency to %u\n",
+			__func__, int_freq);
 	}
+	channel->int_freq = int_freq;
+	/*
+	 * it is the responsibility of the caller to tspp_allocate_buffers(),
+	 * whether it's the user or the driver, to make sure the size parameter
+	 * is compatible to the channel mode.
+	 */
+	channel->buffer_size = size;
 
-	for (; channel->buffer_count < channel->max_buffers;
+	/* save user defined memory free function for later use */
+	channel->memfree = memfree;
+	channel->user_info = user;
+
+	for (channel->buffer_count = 0;
+		channel->buffer_count < channel->max_buffers;
 		channel->buffer_count++) {
 
 		/* allocate the descriptor */
 		struct tspp_mem_buffer *desc = (struct tspp_mem_buffer *)
 			kmalloc(sizeof(struct tspp_mem_buffer), GFP_KERNEL);
 		if (!desc) {
-			pr_warn("tspp: Can't allocate desc %i",
-			channel->buffer_count);
+			pr_warn("%s: Can't allocate desc %i",
+				__func__, channel->buffer_count);
 			break;
 		}
 
@@ -1746,8 +2018,8 @@
 		if (tspp_alloc_buffer(channel_id, &desc->desc,
 			channel->buffer_size, alloc, user) != 0) {
 			kfree(desc);
-			pr_warn("tspp: Can't allocate buffer %i",
-				channel->buffer_count);
+			pr_warn("%s: Can't allocate buffer %i",
+				__func__, channel->buffer_count);
 			break;
 		}
 
@@ -1770,12 +2042,24 @@
 
 		/* start the transfer */
 		if (tspp_queue_buffer(channel, desc))
-			pr_err("tspp: can't queue buffer %i", desc->desc.id);
+			pr_err("%s: can't queue buffer %i",
+				__func__, desc->desc.id);
+	}
+
+	if (channel->buffer_count < channel->max_buffers) {
+		/*
+		 * we failed to allocate the requested number of buffers.
+		 * we don't allow a partial success, so need to clean up here.
+		 */
+		tspp_destroy_buffers(channel_id, channel);
+		channel->buffer_count = 0;
+		return -ENOMEM;
 	}
 
 	channel->waiting = channel->data;
 	channel->read = channel->data;
 	channel->locked = channel->data;
+
 	return 0;
 }
 EXPORT_SYMBOL(tspp_allocate_buffers);
@@ -1894,8 +2178,10 @@
 		transferred += size;
 		buffer->read_index += size;
 
-		/* after reading the end of the buffer, requeue it,
-			and set up for reading the next one */
+		/*
+		 * after reading the end of the buffer, requeue it,
+		 * and set up for reading the next one
+		 */
 		if (buffer->read_index == buffer->filled) {
 			buffer->state = TSPP_BUF_STATE_WAITING;
 			if (tspp_queue_buffer(channel, buffer))
@@ -1994,8 +2280,10 @@
 		pr_err("tspp: Unknown ioctl %i", param0);
 	}
 
-	/* normalize the return code in case one of the subfunctions does
-		something weird */
+	/*
+	 * normalize the return code in case one of the subfunctions does
+	 * something weird
+	 */
 	if (rc != 0)
 		rc = -ENOIOCTLCMD;
 
@@ -2088,13 +2376,14 @@
 {
 	int rc = -ENODEV;
 	u32 version;
-	u32 i;
+	u32 i, j;
 	struct msm_tspp_platform_data *data;
 	struct tspp_device *device;
 	struct resource *mem_tsif0;
 	struct resource *mem_tsif1;
 	struct resource *mem_tspp;
 	struct resource *mem_bam;
+	struct tspp_channel *channel;
 
 	/* must have platform data */
 	data = pdev->dev.platform_data;
@@ -2290,6 +2579,12 @@
 	return 0;
 
 err_channel:
+	/* uninitialize channels */
+	for (j = 0; j < i; j++) {
+		channel = &(device->channels[i]);
+		device_destroy(tspp_class, channel->cdev.dev);
+		cdev_del(&channel->cdev);
+	}
 err_clock:
 	sps_deregister_bam_device(device->bam_handle);
 err_bam:
diff --git a/drivers/mmc/card/Kconfig b/drivers/mmc/card/Kconfig
index ebb4afe..33f0600 100644
--- a/drivers/mmc/card/Kconfig
+++ b/drivers/mmc/card/Kconfig
@@ -76,3 +76,13 @@
 
 	  This driver is only of interest to those developing or
 	  testing a host driver. Most people should say N here.
+
+config MMC_BLOCK_TEST
+	tristate "MMC block test"
+	depends on MMC_BLOCK && IOSCHED_TEST
+	help
+	  MMC block test can be used with test iosched to test the MMC block
+	  device.
+	  Currently used to test eMMC 4.5 features (packed commands, sanitize,
+	  BKOPs).
+
diff --git a/drivers/mmc/card/Makefile b/drivers/mmc/card/Makefile
index c73b406..d55107f 100644
--- a/drivers/mmc/card/Makefile
+++ b/drivers/mmc/card/Makefile
@@ -8,3 +8,4 @@
 
 obj-$(CONFIG_SDIO_UART)		+= sdio_uart.o
 
+obj-$(CONFIG_MMC_BLOCK_TEST)		+= mmc_block_test.o
diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
index a9f1b53..ade2b97 100644
--- a/drivers/mmc/card/block.c
+++ b/drivers/mmc/card/block.c
@@ -65,6 +65,11 @@
 			(rq_data_dir(req) == WRITE))
 #define PACKED_CMD_VER		0x01
 #define PACKED_CMD_WR		0x02
+#define MMC_BLK_UPDATE_STOP_REASON(stats, reason)			\
+	do {								\
+		if (stats->enabled)					\
+			stats->pack_stop_reason[reason]++;		\
+	} while (0)
 
 static DEFINE_MUTEX(block_mutex);
 
@@ -115,22 +120,13 @@
 	unsigned int	part_curr;
 	struct device_attribute force_ro;
 	struct device_attribute power_ro_lock;
+	struct device_attribute num_wr_reqs_to_start_packing;
+	struct device_attribute min_sectors_to_check_bkops_status;
 	int	area_type;
 };
 
 static DEFINE_MUTEX(open_lock);
 
-enum mmc_blk_status {
-	MMC_BLK_SUCCESS = 0,
-	MMC_BLK_PARTIAL,
-	MMC_BLK_CMD_ERR,
-	MMC_BLK_RETRY,
-	MMC_BLK_ABORT,
-	MMC_BLK_DATA_ERR,
-	MMC_BLK_ECC_ERR,
-	MMC_BLK_NOMEDIUM,
-};
-
 enum {
 	MMC_PACKED_N_IDX = -1,
 	MMC_PACKED_N_ZERO,
@@ -279,6 +275,80 @@
 	return ret;
 }
 
+static ssize_t
+num_wr_reqs_to_start_packing_show(struct device *dev,
+				  struct device_attribute *attr, char *buf)
+{
+	struct mmc_blk_data *md = mmc_blk_get(dev_to_disk(dev));
+	int num_wr_reqs_to_start_packing;
+	int ret;
+
+	num_wr_reqs_to_start_packing = md->queue.num_wr_reqs_to_start_packing;
+
+	ret = snprintf(buf, PAGE_SIZE, "%d\n", num_wr_reqs_to_start_packing);
+
+	mmc_blk_put(md);
+	return ret;
+}
+
+static ssize_t
+num_wr_reqs_to_start_packing_store(struct device *dev,
+				 struct device_attribute *attr,
+				 const char *buf, size_t count)
+{
+	int value;
+	struct mmc_blk_data *md = mmc_blk_get(dev_to_disk(dev));
+
+	sscanf(buf, "%d", &value);
+	if (value >= 0)
+		md->queue.num_wr_reqs_to_start_packing = value;
+
+	mmc_blk_put(md);
+	return count;
+}
+
+static ssize_t
+min_sectors_to_check_bkops_status_show(struct device *dev,
+				  struct device_attribute *attr, char *buf)
+{
+	struct mmc_blk_data *md = mmc_blk_get(dev_to_disk(dev));
+	unsigned int min_sectors_to_check_bkops_status;
+	struct mmc_card *card = md->queue.card;
+	int ret;
+
+	if (!card)
+		return -EINVAL;
+
+	min_sectors_to_check_bkops_status =
+		card->bkops_info.min_sectors_to_queue_delayed_work;
+
+	ret = snprintf(buf, PAGE_SIZE, "%d\n",
+		       min_sectors_to_check_bkops_status);
+
+	mmc_blk_put(md);
+	return ret;
+}
+
+static ssize_t
+min_sectors_to_check_bkops_status_store(struct device *dev,
+				 struct device_attribute *attr,
+				 const char *buf, size_t count)
+{
+	int value;
+	struct mmc_blk_data *md = mmc_blk_get(dev_to_disk(dev));
+	struct mmc_card *card = md->queue.card;
+
+	if (!card)
+		return -EINVAL;
+
+	sscanf(buf, "%d", &value);
+	if (value >= 0)
+		card->bkops_info.min_sectors_to_queue_delayed_work = value;
+
+	mmc_blk_put(md);
+	return count;
+}
+
 static int mmc_blk_open(struct block_device *bdev, fmode_t mode)
 {
 	struct mmc_blk_data *md = mmc_blk_get(bdev->bd_disk);
@@ -846,6 +916,9 @@
 	from = blk_rq_pos(req);
 	nr = blk_rq_sectors(req);
 
+	if (card->ext_csd.bkops_en)
+		card->bkops_info.sectors_changed += blk_rq_sectors(req);
+
 	if (mmc_can_discard(card))
 		arg = MMC_DISCARD_ARG;
 	else if (mmc_can_trim(card))
@@ -1319,11 +1392,86 @@
 	}
 
 	mqrq->mmc_active.mrq = &brq->mrq;
-	mqrq->mmc_active.err_check = mmc_blk_err_check;
+	if (mq->err_check_fn)
+		mqrq->mmc_active.err_check = mq->err_check_fn;
+	else
+		mqrq->mmc_active.err_check = mmc_blk_err_check;
 
 	mmc_queue_bounce_pre(mqrq);
 }
 
+static void mmc_blk_write_packing_control(struct mmc_queue *mq,
+					  struct request *req)
+{
+	struct mmc_host *host = mq->card->host;
+	int data_dir;
+
+	if (!(host->caps2 & MMC_CAP2_PACKED_WR))
+		return;
+
+	/*
+	 * In case the packing control is not supported by the host, it should
+	 * not have an effect on the write packing. Therefore we have to enable
+	 * the write packing
+	 */
+	if (!(host->caps2 & MMC_CAP2_PACKED_WR_CONTROL)) {
+		mq->wr_packing_enabled = true;
+		return;
+	}
+
+	if (!req || (req && (req->cmd_flags & REQ_FLUSH))) {
+		if (mq->num_of_potential_packed_wr_reqs >
+				mq->num_wr_reqs_to_start_packing)
+			mq->wr_packing_enabled = true;
+		mq->num_of_potential_packed_wr_reqs = 0;
+		return;
+	}
+
+	data_dir = rq_data_dir(req);
+
+	if (data_dir == READ) {
+		mq->num_of_potential_packed_wr_reqs = 0;
+		mq->wr_packing_enabled = false;
+		return;
+	} else if (data_dir == WRITE) {
+		mq->num_of_potential_packed_wr_reqs++;
+	}
+
+	if (mq->num_of_potential_packed_wr_reqs >
+			mq->num_wr_reqs_to_start_packing)
+		mq->wr_packing_enabled = true;
+
+}
+
+struct mmc_wr_pack_stats *mmc_blk_get_packed_statistics(struct mmc_card *card)
+{
+	if (!card)
+		return NULL;
+
+	return &card->wr_pack_stats;
+}
+EXPORT_SYMBOL(mmc_blk_get_packed_statistics);
+
+void mmc_blk_init_packed_statistics(struct mmc_card *card)
+{
+	int max_num_of_packed_reqs = 0;
+
+	if (!card || !card->wr_pack_stats.packing_events)
+		return;
+
+	max_num_of_packed_reqs = card->ext_csd.max_packed_writes;
+
+	spin_lock(&card->wr_pack_stats.lock);
+	memset(card->wr_pack_stats.packing_events, 0,
+		(max_num_of_packed_reqs + 1) *
+	       sizeof(*card->wr_pack_stats.packing_events));
+	memset(&card->wr_pack_stats.pack_stop_reason, 0,
+		sizeof(card->wr_pack_stats.pack_stop_reason));
+	card->wr_pack_stats.enabled = true;
+	spin_unlock(&card->wr_pack_stats.lock);
+}
+EXPORT_SYMBOL(mmc_blk_init_packed_statistics);
+
 static u8 mmc_blk_prep_packed_list(struct mmc_queue *mq, struct request *req)
 {
 	struct request_queue *q = mq->queue;
@@ -1336,6 +1484,7 @@
 	u8 put_back = 0;
 	u8 max_packed_rw = 0;
 	u8 reqs = 0;
+	struct mmc_wr_pack_stats *stats = &card->wr_pack_stats;
 
 	mmc_blk_clear_packed(mq->mqrq_cur);
 
@@ -1343,6 +1492,9 @@
 			!card->ext_csd.packed_event_en)
 		goto no_packed;
 
+	if (!mq->wr_packing_enabled)
+		goto no_packed;
+
 	if ((rq_data_dir(cur) == WRITE) &&
 			(card->host->caps2 & MMC_CAP2_PACKED_WR))
 		max_packed_rw = card->ext_csd.max_packed_writes;
@@ -1373,26 +1525,33 @@
 		phys_segments++;
 	}
 
+	spin_lock(&stats->lock);
+
 	while (reqs < max_packed_rw - 1) {
 		spin_lock_irq(q->queue_lock);
 		next = blk_fetch_request(q);
 		spin_unlock_irq(q->queue_lock);
-		if (!next)
+		if (!next) {
+			MMC_BLK_UPDATE_STOP_REASON(stats, EMPTY_QUEUE);
 			break;
+		}
 
 		if (mmc_large_sec(card) &&
 				!IS_ALIGNED(blk_rq_sectors(next), 8)) {
+			MMC_BLK_UPDATE_STOP_REASON(stats, LARGE_SEC_ALIGN);
 			put_back = 1;
 			break;
 		}
 
 		if (next->cmd_flags & REQ_DISCARD ||
 				next->cmd_flags & REQ_FLUSH) {
+			MMC_BLK_UPDATE_STOP_REASON(stats, FLUSH_OR_DISCARD);
 			put_back = 1;
 			break;
 		}
 
 		if (rq_data_dir(cur) != rq_data_dir(next)) {
+			MMC_BLK_UPDATE_STOP_REASON(stats, WRONG_DATA_DIR);
 			put_back = 1;
 			break;
 		}
@@ -1400,22 +1559,32 @@
 		if (mmc_req_rel_wr(next) &&
 				(md->flags & MMC_BLK_REL_WR) &&
 				!en_rel_wr) {
+			MMC_BLK_UPDATE_STOP_REASON(stats, REL_WRITE);
 			put_back = 1;
 			break;
 		}
 
 		req_sectors += blk_rq_sectors(next);
 		if (req_sectors > max_blk_count) {
+			if (stats->enabled)
+				stats->pack_stop_reason[EXCEEDS_SECTORS]++;
 			put_back = 1;
 			break;
 		}
 
 		phys_segments +=  next->nr_phys_segments;
 		if (phys_segments > max_phys_segs) {
+			MMC_BLK_UPDATE_STOP_REASON(stats, EXCEEDS_SEGMENTS);
 			put_back = 1;
 			break;
 		}
 
+		if (rq_data_dir(next) == WRITE) {
+			mq->num_of_potential_packed_wr_reqs++;
+			if (card->ext_csd.bkops_en)
+				card->bkops_info.sectors_changed +=
+					blk_rq_sectors(next);
+		}
 		list_add_tail(&next->queuelist, &mq->mqrq_cur->packed_list);
 		cur = next;
 		reqs++;
@@ -1427,6 +1596,15 @@
 		spin_unlock_irq(q->queue_lock);
 	}
 
+	if (stats->enabled) {
+		if (reqs + 1 <= card->ext_csd.max_packed_writes)
+			stats->packing_events[reqs + 1]++;
+		if (reqs + 1 == max_packed_rw)
+			MMC_BLK_UPDATE_STOP_REASON(stats, THRESHOLD);
+	}
+
+	spin_unlock(&stats->lock);
+
 	if (reqs > 0) {
 		list_add(&req->queuelist, &mq->mqrq_cur->packed_list);
 		mq->mqrq_cur->packed_num = ++reqs;
@@ -1501,6 +1679,7 @@
 	brq->data.blksz = 512;
 	brq->data.blocks = mqrq->packed_blocks + 1;
 	brq->data.flags |= MMC_DATA_WRITE;
+	brq->data.fault_injected = false;
 
 	brq->stop.opcode = MMC_STOP_TRANSMISSION;
 	brq->stop.arg = 0;
@@ -1512,7 +1691,18 @@
 	brq->data.sg_len = mmc_queue_map_sg(mq, mqrq);
 
 	mqrq->mmc_active.mrq = &brq->mrq;
-	mqrq->mmc_active.err_check = mmc_blk_packed_err_check;
+
+	/*
+	 * This is intended for packed commands tests usage - in case these
+	 * functions are not in use the respective pointers are NULL
+	 */
+	if (mq->err_check_fn)
+		mqrq->mmc_active.err_check = mq->err_check_fn;
+	else
+		mqrq->mmc_active.err_check = mmc_blk_packed_err_check;
+
+	if (mq->packed_test_fn)
+		mq->packed_test_fn(mq->queue, mqrq);
 
 	mmc_queue_bounce_pre(mqrq);
 }
@@ -1625,8 +1815,11 @@
 	if (!rqc && !mq->mqrq_prev->req)
 		return 0;
 
-	if (rqc)
+	if (rqc) {
+		if ((card->ext_csd.bkops_en) && (rq_data_dir(rqc) == WRITE))
+			card->bkops_info.sectors_changed += blk_rq_sectors(rqc);
 		reqs = mmc_blk_prep_packed_list(mq, rqc);
+	}
 
 	do {
 		if (rqc) {
@@ -1797,6 +1990,8 @@
 		goto out;
 	}
 
+	mmc_blk_write_packing_control(mq, req);
+
 	if (req && req->cmd_flags & REQ_SANITIZE) {
 		/* complete ongoing async transfer before issuing sanitize */
 		if (card->host && card->host->areq)
@@ -2028,6 +2223,8 @@
 
 	if (md) {
 		card = md->queue.card;
+		device_remove_file(disk_to_dev(md->disk),
+				   &md->num_wr_reqs_to_start_packing);
 		if (md->disk->flags & GENHD_FL_UP) {
 			device_remove_file(disk_to_dev(md->disk), &md->force_ro);
 			if ((md->area_type & MMC_BLK_DATA_AREA_BOOT) &&
@@ -2095,15 +2292,43 @@
 			goto power_ro_lock_fail;
 	}
 
+	md->num_wr_reqs_to_start_packing.show =
+		num_wr_reqs_to_start_packing_show;
+	md->num_wr_reqs_to_start_packing.store =
+		num_wr_reqs_to_start_packing_store;
+	sysfs_attr_init(&md->num_wr_reqs_to_start_packing.attr);
+	md->num_wr_reqs_to_start_packing.attr.name =
+		"num_wr_reqs_to_start_packing";
+	md->num_wr_reqs_to_start_packing.attr.mode = S_IRUGO | S_IWUSR;
+	ret = device_create_file(disk_to_dev(md->disk),
+				 &md->num_wr_reqs_to_start_packing);
 	if (ret)
-		goto power_ro_lock_fail;
+		goto num_wr_reqs_to_start_packing_fail;
+
+	md->min_sectors_to_check_bkops_status.show =
+		min_sectors_to_check_bkops_status_show;
+	md->min_sectors_to_check_bkops_status.store =
+		min_sectors_to_check_bkops_status_store;
+	sysfs_attr_init(&md->min_sectors_to_check_bkops_status.attr);
+	md->min_sectors_to_check_bkops_status.attr.name =
+		"min_sectors_to_check_bkops_status";
+	md->min_sectors_to_check_bkops_status.attr.mode = S_IRUGO | S_IWUSR;
+	ret = device_create_file(disk_to_dev(md->disk),
+				 &md->min_sectors_to_check_bkops_status);
+	if (ret)
+		goto min_sectors_to_check_bkops_status_fails;
 
 	return ret;
 
+min_sectors_to_check_bkops_status_fails:
+	device_remove_file(disk_to_dev(md->disk),
+			   &md->num_wr_reqs_to_start_packing);
+num_wr_reqs_to_start_packing_fail:
+	device_remove_file(disk_to_dev(md->disk), &md->power_ro_lock);
 power_ro_lock_fail:
-		device_remove_file(disk_to_dev(md->disk), &md->force_ro);
+	device_remove_file(disk_to_dev(md->disk), &md->force_ro);
 force_ro_fail:
-		del_gendisk(md->disk);
+	del_gendisk(md->disk);
 
 	return ret;
 }
diff --git a/drivers/mmc/card/mmc_block_test.c b/drivers/mmc/card/mmc_block_test.c
new file mode 100644
index 0000000..c5551b8
--- /dev/null
+++ b/drivers/mmc/card/mmc_block_test.c
@@ -0,0 +1,2931 @@
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+/* MMC block test */
+
+#include <linux/module.h>
+#include <linux/blkdev.h>
+#include <linux/debugfs.h>
+#include <linux/mmc/card.h>
+#include <linux/mmc/host.h>
+#include <linux/delay.h>
+#include <linux/test-iosched.h>
+#include <linux/jiffies.h>
+#include "queue.h"
+#include <linux/mmc/mmc.h>
+
+#define MODULE_NAME "mmc_block_test"
+#define TEST_MAX_SECTOR_RANGE		(600*1024*1024) /* 600 MB */
+#define TEST_MAX_BIOS_PER_REQ		120
+#define CMD23_PACKED_BIT	(1 << 30)
+#define LARGE_PRIME_1	1103515367
+#define LARGE_PRIME_2	35757
+#define PACKED_HDR_VER_MASK 0x000000FF
+#define PACKED_HDR_RW_MASK 0x0000FF00
+#define PACKED_HDR_NUM_REQS_MASK 0x00FF0000
+#define PACKED_HDR_BITS_16_TO_29_SET 0x3FFF0000
+#define SECTOR_SIZE 512
+#define NUM_OF_SECTORS_PER_BIO		((BIO_U32_SIZE * 4) / SECTOR_SIZE)
+#define BIO_TO_SECTOR(x)		(x * NUM_OF_SECTORS_PER_BIO)
+/* the desired long test size to be written or read */
+#define LONG_TEST_MAX_NUM_BYTES (50*1024*1024) /* 50MB */
+/* request queue limitation is 128 requests, and we leave 10 spare requests */
+#define TEST_MAX_REQUESTS 118
+#define LONG_TEST_MAX_NUM_REQS	(LONG_TEST_MAX_NUM_BYTES / \
+		(TEST_MAX_BIOS_PER_REQ * sizeof(int) * BIO_U32_SIZE))
+/* this doesn't allow the test requests num to be greater than the maximum */
+#define LONG_TEST_ACTUAL_NUM_REQS  \
+			((TEST_MAX_REQUESTS < LONG_TEST_MAX_NUM_REQS) ? \
+				TEST_MAX_REQUESTS : LONG_TEST_MAX_NUM_REQS)
+#define MB_MSEC_RATIO_APPROXIMATION ((1024 * 1024) / 1000)
+/* actual number of bytes in test */
+#define LONG_TEST_ACTUAL_BYTE_NUM  (LONG_TEST_ACTUAL_NUM_REQS *  \
+			(TEST_MAX_BIOS_PER_REQ * sizeof(int) * BIO_U32_SIZE))
+/* actual number of MiB in test multiplied by 10, for single digit precision*/
+#define LONG_TEST_ACTUAL_MB_NUM_X_10 ((LONG_TEST_ACTUAL_BYTE_NUM * 10) / \
+					(1024 * 1024))
+/* extract integer value */
+#define LONG_TEST_SIZE_INTEGER (LONG_TEST_ACTUAL_MB_NUM_X_10 / 10)
+/* and calculate the MiB value fraction */
+#define LONG_TEST_SIZE_FRACTION (LONG_TEST_ACTUAL_MB_NUM_X_10 - \
+		(LONG_TEST_SIZE_INTEGER * 10))
+
+#define test_pr_debug(fmt, args...) pr_debug("%s: "fmt"\n", MODULE_NAME, args)
+#define test_pr_info(fmt, args...) pr_info("%s: "fmt"\n", MODULE_NAME, args)
+#define test_pr_err(fmt, args...) pr_err("%s: "fmt"\n", MODULE_NAME, args)
+
+#define SANITIZE_TEST_TIMEOUT 240000
+#define TEST_REQUEST_NUM_OF_BIOS	3
+
+
+#define CHECK_BKOPS_STATS(stats, exp_bkops, exp_hpi, exp_suspend)	\
+				   ((stats.bkops != exp_bkops) ||	\
+				    (stats.hpi != exp_hpi) ||		\
+				    (stats.suspend != exp_suspend))
+#define BKOPS_TEST_TIMEOUT 60000
+
+enum is_random {
+	NON_RANDOM_TEST,
+	RANDOM_TEST,
+};
+
+enum mmc_block_test_testcases {
+	/* Start of send write packing test group */
+	SEND_WRITE_PACKING_MIN_TESTCASE,
+	TEST_STOP_DUE_TO_READ = SEND_WRITE_PACKING_MIN_TESTCASE,
+	TEST_STOP_DUE_TO_READ_AFTER_MAX_REQS,
+	TEST_STOP_DUE_TO_FLUSH,
+	TEST_STOP_DUE_TO_FLUSH_AFTER_MAX_REQS,
+	TEST_STOP_DUE_TO_EMPTY_QUEUE,
+	TEST_STOP_DUE_TO_MAX_REQ_NUM,
+	TEST_STOP_DUE_TO_THRESHOLD,
+	SEND_WRITE_PACKING_MAX_TESTCASE = TEST_STOP_DUE_TO_THRESHOLD,
+
+	/* Start of err check test group */
+	ERR_CHECK_MIN_TESTCASE,
+	TEST_RET_ABORT = ERR_CHECK_MIN_TESTCASE,
+	TEST_RET_PARTIAL_FOLLOWED_BY_SUCCESS,
+	TEST_RET_PARTIAL_FOLLOWED_BY_ABORT,
+	TEST_RET_PARTIAL_MULTIPLE_UNTIL_SUCCESS,
+	TEST_RET_PARTIAL_MAX_FAIL_IDX,
+	TEST_RET_RETRY,
+	TEST_RET_CMD_ERR,
+	TEST_RET_DATA_ERR,
+	ERR_CHECK_MAX_TESTCASE = TEST_RET_DATA_ERR,
+
+	/* Start of send invalid test group */
+	INVALID_CMD_MIN_TESTCASE,
+	TEST_HDR_INVALID_VERSION = INVALID_CMD_MIN_TESTCASE,
+	TEST_HDR_WRONG_WRITE_CODE,
+	TEST_HDR_INVALID_RW_CODE,
+	TEST_HDR_DIFFERENT_ADDRESSES,
+	TEST_HDR_REQ_NUM_SMALLER_THAN_ACTUAL,
+	TEST_HDR_REQ_NUM_LARGER_THAN_ACTUAL,
+	TEST_HDR_CMD23_PACKED_BIT_SET,
+	TEST_CMD23_MAX_PACKED_WRITES,
+	TEST_CMD23_ZERO_PACKED_WRITES,
+	TEST_CMD23_PACKED_BIT_UNSET,
+	TEST_CMD23_REL_WR_BIT_SET,
+	TEST_CMD23_BITS_16TO29_SET,
+	TEST_CMD23_HDR_BLK_NOT_IN_COUNT,
+	INVALID_CMD_MAX_TESTCASE = TEST_CMD23_HDR_BLK_NOT_IN_COUNT,
+
+	/*
+	 * Start of packing control test group.
+	 * in these next testcases the abbreviation FB = followed by
+	 */
+	PACKING_CONTROL_MIN_TESTCASE,
+	TEST_PACKING_EXP_ONE_OVER_TRIGGER_FB_READ =
+				PACKING_CONTROL_MIN_TESTCASE,
+	TEST_PACKING_EXP_N_OVER_TRIGGER,
+	TEST_PACKING_EXP_N_OVER_TRIGGER_FB_READ,
+	TEST_PACKING_EXP_N_OVER_TRIGGER_FLUSH_N,
+	TEST_PACKING_EXP_THRESHOLD_OVER_TRIGGER,
+	TEST_PACKING_NOT_EXP_LESS_THAN_TRIGGER_REQUESTS,
+	TEST_PACKING_NOT_EXP_TRIGGER_REQUESTS,
+	TEST_PACKING_NOT_EXP_TRIGGER_READ_TRIGGER,
+	TEST_PACKING_NOT_EXP_TRIGGER_FLUSH_TRIGGER,
+	TEST_PACK_MIX_PACKED_NO_PACKED_PACKED,
+	TEST_PACK_MIX_NO_PACKED_PACKED_NO_PACKED,
+	PACKING_CONTROL_MAX_TESTCASE = TEST_PACK_MIX_NO_PACKED_PACKED_NO_PACKED,
+
+	TEST_WRITE_DISCARD_SANITIZE_READ,
+
+	/* Start of bkops test group */
+	BKOPS_MIN_TESTCASE,
+	BKOPS_DELAYED_WORK_LEVEL_1 = BKOPS_MIN_TESTCASE,
+	BKOPS_DELAYED_WORK_LEVEL_1_HPI,
+	BKOPS_CANCEL_DELAYED_WORK,
+	BKOPS_URGENT_LEVEL_2,
+	BKOPS_URGENT_LEVEL_2_TWO_REQS,
+	BKOPS_URGENT_LEVEL_3,
+	BKOPS_MAX_TESTCASE = BKOPS_URGENT_LEVEL_3,
+
+	TEST_LONG_SEQUENTIAL_READ,
+	TEST_LONG_SEQUENTIAL_WRITE,
+};
+
+enum mmc_block_test_group {
+	TEST_NO_GROUP,
+	TEST_GENERAL_GROUP,
+	TEST_SEND_WRITE_PACKING_GROUP,
+	TEST_ERR_CHECK_GROUP,
+	TEST_SEND_INVALID_GROUP,
+	TEST_PACKING_CONTROL_GROUP,
+	TEST_BKOPS_GROUP,
+};
+
+enum bkops_test_stages {
+	BKOPS_STAGE_1,
+	BKOPS_STAGE_2,
+	BKOPS_STAGE_3,
+	BKOPS_STAGE_4,
+};
+
+struct mmc_block_test_debug {
+	struct dentry *send_write_packing_test;
+	struct dentry *err_check_test;
+	struct dentry *send_invalid_packed_test;
+	struct dentry *random_test_seed;
+	struct dentry *packing_control_test;
+	struct dentry *discard_sanitize_test;
+	struct dentry *bkops_test;
+	struct dentry *long_sequential_read_test;
+	struct dentry *long_sequential_write_test;
+};
+
+struct mmc_block_test_data {
+	/* The number of write requests that the test will issue */
+	int num_requests;
+	/* The expected write packing statistics for the current test */
+	struct mmc_wr_pack_stats exp_packed_stats;
+	/*
+	 * A user-defined seed for random choices of number of bios written in
+	 * a request, and of number of requests issued in a test
+	 * This field is randomly updated after each use
+	 */
+	unsigned int random_test_seed;
+	/* A retry counter used in err_check tests */
+	int err_check_counter;
+	/* Can be one of the values of enum test_group */
+	enum mmc_block_test_group test_group;
+	/*
+	 * Indicates if the current testcase is running with random values of
+	 * num_requests and num_bios (in each request)
+	 */
+	int is_random;
+	/* Data structure for debugfs dentrys */
+	struct mmc_block_test_debug debug;
+	/*
+	 * Data structure containing individual test information, including
+	 * self-defined specific data
+	 */
+	struct test_info test_info;
+	/* mmc block device test */
+	struct blk_dev_test_type bdt;
+	/* Current BKOPs test stage */
+	enum bkops_test_stages	bkops_stage;
+	/* A wait queue for BKOPs tests */
+	wait_queue_head_t bkops_wait_q;
+};
+
+static struct mmc_block_test_data *mbtd;
+
+void print_mmc_packing_stats(struct mmc_card *card)
+{
+	int i;
+	int max_num_of_packed_reqs = 0;
+
+	if ((!card) || (!card->wr_pack_stats.packing_events))
+		return;
+
+	max_num_of_packed_reqs = card->ext_csd.max_packed_writes;
+
+	spin_lock(&card->wr_pack_stats.lock);
+
+	pr_info("%s: write packing statistics:\n",
+		mmc_hostname(card->host));
+
+	for (i = 1 ; i <= max_num_of_packed_reqs ; ++i) {
+		if (card->wr_pack_stats.packing_events[i] != 0)
+			pr_info("%s: Packed %d reqs - %d times\n",
+				mmc_hostname(card->host), i,
+				card->wr_pack_stats.packing_events[i]);
+	}
+
+	pr_info("%s: stopped packing due to the following reasons:\n",
+		mmc_hostname(card->host));
+
+	if (card->wr_pack_stats.pack_stop_reason[EXCEEDS_SEGMENTS])
+		pr_info("%s: %d times: exceedmax num of segments\n",
+			mmc_hostname(card->host),
+			card->wr_pack_stats.pack_stop_reason[EXCEEDS_SEGMENTS]);
+	if (card->wr_pack_stats.pack_stop_reason[EXCEEDS_SECTORS])
+		pr_info("%s: %d times: exceeding the max num of sectors\n",
+			mmc_hostname(card->host),
+			card->wr_pack_stats.pack_stop_reason[EXCEEDS_SECTORS]);
+	if (card->wr_pack_stats.pack_stop_reason[WRONG_DATA_DIR])
+		pr_info("%s: %d times: wrong data direction\n",
+			mmc_hostname(card->host),
+			card->wr_pack_stats.pack_stop_reason[WRONG_DATA_DIR]);
+	if (card->wr_pack_stats.pack_stop_reason[FLUSH_OR_DISCARD])
+		pr_info("%s: %d times: flush or discard\n",
+			mmc_hostname(card->host),
+			card->wr_pack_stats.pack_stop_reason[FLUSH_OR_DISCARD]);
+	if (card->wr_pack_stats.pack_stop_reason[EMPTY_QUEUE])
+		pr_info("%s: %d times: empty queue\n",
+			mmc_hostname(card->host),
+			card->wr_pack_stats.pack_stop_reason[EMPTY_QUEUE]);
+	if (card->wr_pack_stats.pack_stop_reason[REL_WRITE])
+		pr_info("%s: %d times: rel write\n",
+			mmc_hostname(card->host),
+			card->wr_pack_stats.pack_stop_reason[REL_WRITE]);
+	if (card->wr_pack_stats.pack_stop_reason[THRESHOLD])
+		pr_info("%s: %d times: Threshold\n",
+			mmc_hostname(card->host),
+			card->wr_pack_stats.pack_stop_reason[THRESHOLD]);
+
+	spin_unlock(&card->wr_pack_stats.lock);
+}
+
+/*
+ * A callback assigned to the packed_test_fn field.
+ * Called from block layer in mmc_blk_packed_hdr_wrq_prep.
+ * Here we alter the packed header or CMD23 in order to send an invalid
+ * packed command to the card.
+ */
+static void test_invalid_packed_cmd(struct request_queue *q,
+				    struct mmc_queue_req *mqrq)
+{
+	struct mmc_queue *mq = q->queuedata;
+	u32 *packed_cmd_hdr = mqrq->packed_cmd_hdr;
+	struct request *req = mqrq->req;
+	struct request *second_rq;
+	struct test_request *test_rq;
+	struct mmc_blk_request *brq = &mqrq->brq;
+	int num_requests;
+	int max_packed_reqs;
+
+	if (!mq) {
+		test_pr_err("%s: NULL mq", __func__);
+		return;
+	}
+
+	test_rq = (struct test_request *)req->elv.priv[0];
+	if (!test_rq) {
+		test_pr_err("%s: NULL test_rq", __func__);
+		return;
+	}
+	max_packed_reqs = mq->card->ext_csd.max_packed_writes;
+
+	switch (mbtd->test_info.testcase) {
+	case TEST_HDR_INVALID_VERSION:
+		test_pr_info("%s: set invalid header version", __func__);
+		/* Put 0 in header version field (1 byte, offset 0 in header) */
+		packed_cmd_hdr[0] = packed_cmd_hdr[0] & ~PACKED_HDR_VER_MASK;
+		break;
+	case TEST_HDR_WRONG_WRITE_CODE:
+		test_pr_info("%s: wrong write code", __func__);
+		/* Set R/W field with R value (1 byte, offset 1 in header) */
+		packed_cmd_hdr[0] = packed_cmd_hdr[0] & ~PACKED_HDR_RW_MASK;
+		packed_cmd_hdr[0] = packed_cmd_hdr[0] | 0x00000100;
+		break;
+	case TEST_HDR_INVALID_RW_CODE:
+		test_pr_info("%s: invalid r/w code", __func__);
+		/* Set R/W field with invalid value */
+		packed_cmd_hdr[0] = packed_cmd_hdr[0] & ~PACKED_HDR_RW_MASK;
+		packed_cmd_hdr[0] = packed_cmd_hdr[0] | 0x00000400;
+		break;
+	case TEST_HDR_DIFFERENT_ADDRESSES:
+		test_pr_info("%s: different addresses", __func__);
+		second_rq = list_entry(req->queuelist.next, struct request,
+				queuelist);
+		test_pr_info("%s: test_rq->sector=%ld, second_rq->sector=%ld",
+			      __func__, (long)req->__sector,
+			     (long)second_rq->__sector);
+		/*
+		 * Put start sector of second write request in the first write
+		 * request's cmd25 argument in the packed header
+		 */
+		packed_cmd_hdr[3] = second_rq->__sector;
+		break;
+	case TEST_HDR_REQ_NUM_SMALLER_THAN_ACTUAL:
+		test_pr_info("%s: request num smaller than actual" , __func__);
+		num_requests = (packed_cmd_hdr[0] & PACKED_HDR_NUM_REQS_MASK)
+									>> 16;
+		/* num of entries is decremented by 1 */
+		num_requests = (num_requests - 1) << 16;
+		/*
+		 * Set number of requests field in packed write header to be
+		 * smaller than the actual number (1 byte, offset 2 in header)
+		 */
+		packed_cmd_hdr[0] = (packed_cmd_hdr[0] &
+				     ~PACKED_HDR_NUM_REQS_MASK) + num_requests;
+		break;
+	case TEST_HDR_REQ_NUM_LARGER_THAN_ACTUAL:
+		test_pr_info("%s: request num larger than actual" , __func__);
+		num_requests = (packed_cmd_hdr[0] & PACKED_HDR_NUM_REQS_MASK)
+									>> 16;
+		/* num of entries is incremented by 1 */
+		num_requests = (num_requests + 1) << 16;
+		/*
+		 * Set number of requests field in packed write header to be
+		 * larger than the actual number (1 byte, offset 2 in header).
+		 */
+		packed_cmd_hdr[0] = (packed_cmd_hdr[0] &
+				     ~PACKED_HDR_NUM_REQS_MASK) + num_requests;
+		break;
+	case TEST_HDR_CMD23_PACKED_BIT_SET:
+		test_pr_info("%s: header CMD23 packed bit set" , __func__);
+		/*
+		 * Set packed bit (bit 30) in cmd23 argument of first and second
+		 * write requests in packed write header.
+		 * These are located at bytes 2 and 4 in packed write header
+		 */
+		packed_cmd_hdr[2] = packed_cmd_hdr[2] | CMD23_PACKED_BIT;
+		packed_cmd_hdr[4] = packed_cmd_hdr[4] | CMD23_PACKED_BIT;
+		break;
+	case TEST_CMD23_MAX_PACKED_WRITES:
+		test_pr_info("%s: CMD23 request num > max_packed_reqs",
+			      __func__);
+		/*
+		 * Set the individual packed cmd23 request num to
+		 * max_packed_reqs + 1
+		 */
+		brq->sbc.arg = MMC_CMD23_ARG_PACKED | (max_packed_reqs + 1);
+		break;
+	case TEST_CMD23_ZERO_PACKED_WRITES:
+		test_pr_info("%s: CMD23 request num = 0", __func__);
+		/* Set the individual packed cmd23 request num to zero */
+		brq->sbc.arg = MMC_CMD23_ARG_PACKED;
+		break;
+	case TEST_CMD23_PACKED_BIT_UNSET:
+		test_pr_info("%s: CMD23 packed bit unset", __func__);
+		/*
+		 * Set the individual packed cmd23 packed bit to 0,
+		 *  although there is a packed write request
+		 */
+		brq->sbc.arg &= ~CMD23_PACKED_BIT;
+		break;
+	case TEST_CMD23_REL_WR_BIT_SET:
+		test_pr_info("%s: CMD23 REL WR bit set", __func__);
+		/* Set the individual packed cmd23 reliable write bit */
+		brq->sbc.arg = MMC_CMD23_ARG_PACKED | MMC_CMD23_ARG_REL_WR;
+		break;
+	case TEST_CMD23_BITS_16TO29_SET:
+		test_pr_info("%s: CMD23 bits [16-29] set", __func__);
+		brq->sbc.arg = MMC_CMD23_ARG_PACKED |
+			PACKED_HDR_BITS_16_TO_29_SET;
+		break;
+	case TEST_CMD23_HDR_BLK_NOT_IN_COUNT:
+		test_pr_info("%s: CMD23 hdr not in block count", __func__);
+		brq->sbc.arg = MMC_CMD23_ARG_PACKED |
+		((rq_data_dir(req) == READ) ? 0 : mqrq->packed_blocks);
+		break;
+	default:
+		test_pr_err("%s: unexpected testcase %d",
+			__func__, mbtd->test_info.testcase);
+		break;
+	}
+}
+
+/*
+ * A callback assigned to the err_check_fn field of the mmc_request by the
+ * MMC/card/block layer.
+ * Called upon request completion by the MMC/core layer.
+ * Here we emulate an error return value from the card.
+ */
+static int test_err_check(struct mmc_card *card, struct mmc_async_req *areq)
+{
+	struct mmc_queue_req *mq_rq = container_of(areq, struct mmc_queue_req,
+			mmc_active);
+	struct request_queue *req_q = test_iosched_get_req_queue();
+	struct mmc_queue *mq;
+	int max_packed_reqs;
+	int ret = 0;
+	struct mmc_blk_request *brq;
+
+	if (req_q)
+		mq = req_q->queuedata;
+	else {
+		test_pr_err("%s: NULL request_queue", __func__);
+		return 0;
+	}
+
+	if (!mq) {
+		test_pr_err("%s: %s: NULL mq", __func__,
+			mmc_hostname(card->host));
+		return 0;
+	}
+
+	max_packed_reqs = mq->card->ext_csd.max_packed_writes;
+
+	if (!mq_rq) {
+		test_pr_err("%s: %s: NULL mq_rq", __func__,
+			mmc_hostname(card->host));
+		return 0;
+	}
+	brq = &mq_rq->brq;
+
+	switch (mbtd->test_info.testcase) {
+	case TEST_RET_ABORT:
+		test_pr_info("%s: return abort", __func__);
+		ret = MMC_BLK_ABORT;
+		break;
+	case TEST_RET_PARTIAL_FOLLOWED_BY_SUCCESS:
+		test_pr_info("%s: return partial followed by success",
+			      __func__);
+		/*
+		 * Since in this testcase num_requests is always >= 2,
+		 * we can be sure that packed_fail_idx is always >= 1
+		 */
+		mq_rq->packed_fail_idx = (mbtd->num_requests / 2);
+		test_pr_info("%s: packed_fail_idx = %d"
+			, __func__, mq_rq->packed_fail_idx);
+		mq->err_check_fn = NULL;
+		ret = MMC_BLK_PARTIAL;
+		break;
+	case TEST_RET_PARTIAL_FOLLOWED_BY_ABORT:
+		if (!mbtd->err_check_counter) {
+			test_pr_info("%s: return partial followed by abort",
+				      __func__);
+			mbtd->err_check_counter++;
+			/*
+			 * Since in this testcase num_requests is always >= 3,
+			 * we have that packed_fail_idx is always >= 1
+			 */
+			mq_rq->packed_fail_idx = (mbtd->num_requests / 2);
+			test_pr_info("%s: packed_fail_idx = %d"
+				, __func__, mq_rq->packed_fail_idx);
+			ret = MMC_BLK_PARTIAL;
+			break;
+		}
+		mbtd->err_check_counter = 0;
+		mq->err_check_fn = NULL;
+		ret = MMC_BLK_ABORT;
+		break;
+	case TEST_RET_PARTIAL_MULTIPLE_UNTIL_SUCCESS:
+		test_pr_info("%s: return partial multiple until success",
+			     __func__);
+		if (++mbtd->err_check_counter >= (mbtd->num_requests)) {
+			mq->err_check_fn = NULL;
+			mbtd->err_check_counter = 0;
+			ret = MMC_BLK_PARTIAL;
+			break;
+		}
+		mq_rq->packed_fail_idx = 1;
+		ret = MMC_BLK_PARTIAL;
+		break;
+	case TEST_RET_PARTIAL_MAX_FAIL_IDX:
+		test_pr_info("%s: return partial max fail_idx", __func__);
+		mq_rq->packed_fail_idx = max_packed_reqs - 1;
+		mq->err_check_fn = NULL;
+		ret = MMC_BLK_PARTIAL;
+		break;
+	case TEST_RET_RETRY:
+		test_pr_info("%s: return retry", __func__);
+		ret = MMC_BLK_RETRY;
+		break;
+	case TEST_RET_CMD_ERR:
+		test_pr_info("%s: return cmd err", __func__);
+		ret = MMC_BLK_CMD_ERR;
+		break;
+	case TEST_RET_DATA_ERR:
+		test_pr_info("%s: return data err", __func__);
+		ret = MMC_BLK_DATA_ERR;
+		break;
+	case BKOPS_URGENT_LEVEL_2:
+	case BKOPS_URGENT_LEVEL_3:
+	case BKOPS_URGENT_LEVEL_2_TWO_REQS:
+		if (mbtd->err_check_counter++ == 0) {
+			test_pr_info("%s: simulate an exception from the card",
+				     __func__);
+			brq->cmd.resp[0] |= R1_EXCEPTION_EVENT;
+		}
+		mq->err_check_fn = NULL;
+		break;
+	default:
+		test_pr_err("%s: unexpected testcase %d",
+			__func__, mbtd->test_info.testcase);
+	}
+
+	return ret;
+}
+
+/*
+ * This is a specific implementation for the get_test_case_str_fn function
+ * pointer in the test_info data structure. Given a valid test_data instance,
+ * the function returns a string resembling the test name, based on the testcase
+ */
+static char *get_test_case_str(struct test_data *td)
+{
+	if (!td) {
+		test_pr_err("%s: NULL td", __func__);
+		return NULL;
+	}
+
+	switch (td->test_info.testcase) {
+	case TEST_STOP_DUE_TO_FLUSH:
+		return " stop due to flush";
+	case TEST_STOP_DUE_TO_FLUSH_AFTER_MAX_REQS:
+		return " stop due to flush after max-1 reqs";
+	case TEST_STOP_DUE_TO_READ:
+		return " stop due to read";
+	case TEST_STOP_DUE_TO_READ_AFTER_MAX_REQS:
+		return "Test stop due to read after max-1 reqs";
+	case TEST_STOP_DUE_TO_EMPTY_QUEUE:
+		return "Test stop due to empty queue";
+	case TEST_STOP_DUE_TO_MAX_REQ_NUM:
+		return "Test stop due to max req num";
+	case TEST_STOP_DUE_TO_THRESHOLD:
+		return "Test stop due to exceeding threshold";
+	case TEST_RET_ABORT:
+		return "Test err_check return abort";
+	case TEST_RET_PARTIAL_FOLLOWED_BY_SUCCESS:
+		return "Test err_check return partial followed by success";
+	case TEST_RET_PARTIAL_FOLLOWED_BY_ABORT:
+		return "Test err_check return partial followed by abort";
+	case TEST_RET_PARTIAL_MULTIPLE_UNTIL_SUCCESS:
+		return "Test err_check return partial multiple until success";
+	case TEST_RET_PARTIAL_MAX_FAIL_IDX:
+		return "Test err_check return partial max fail index";
+	case TEST_RET_RETRY:
+		return "Test err_check return retry";
+	case TEST_RET_CMD_ERR:
+		return "Test err_check return cmd error";
+	case TEST_RET_DATA_ERR:
+		return "Test err_check return data error";
+	case TEST_HDR_INVALID_VERSION:
+		return "Test invalid - wrong header version";
+	case TEST_HDR_WRONG_WRITE_CODE:
+		return "Test invalid - wrong write code";
+	case TEST_HDR_INVALID_RW_CODE:
+		return "Test invalid - wrong R/W code";
+	case TEST_HDR_DIFFERENT_ADDRESSES:
+		return "Test invalid - header different addresses";
+	case TEST_HDR_REQ_NUM_SMALLER_THAN_ACTUAL:
+		return "Test invalid - header req num smaller than actual";
+	case TEST_HDR_REQ_NUM_LARGER_THAN_ACTUAL:
+		return "Test invalid - header req num larger than actual";
+	case TEST_HDR_CMD23_PACKED_BIT_SET:
+		return "Test invalid - header cmd23 packed bit set";
+	case TEST_CMD23_MAX_PACKED_WRITES:
+		return "Test invalid - cmd23 max packed writes";
+	case TEST_CMD23_ZERO_PACKED_WRITES:
+		return "Test invalid - cmd23 zero packed writes";
+	case TEST_CMD23_PACKED_BIT_UNSET:
+		return "Test invalid - cmd23 packed bit unset";
+	case TEST_CMD23_REL_WR_BIT_SET:
+		return "Test invalid - cmd23 rel wr bit set";
+	case TEST_CMD23_BITS_16TO29_SET:
+		return "Test invalid - cmd23 bits [16-29] set";
+	case TEST_CMD23_HDR_BLK_NOT_IN_COUNT:
+		return "Test invalid - cmd23 header block not in count";
+	case TEST_PACKING_EXP_N_OVER_TRIGGER:
+		return "\nTest packing control - pack n";
+	case TEST_PACKING_EXP_N_OVER_TRIGGER_FB_READ:
+		return "\nTest packing control - pack n followed by read";
+	case TEST_PACKING_EXP_N_OVER_TRIGGER_FLUSH_N:
+		return "\nTest packing control - pack n followed by flush";
+	case TEST_PACKING_EXP_ONE_OVER_TRIGGER_FB_READ:
+		return "\nTest packing control - pack one followed by read";
+	case TEST_PACKING_EXP_THRESHOLD_OVER_TRIGGER:
+		return "\nTest packing control - pack threshold";
+	case TEST_PACKING_NOT_EXP_LESS_THAN_TRIGGER_REQUESTS:
+		return "\nTest packing control - no packing";
+	case TEST_PACKING_NOT_EXP_TRIGGER_REQUESTS:
+		return "\nTest packing control - no packing, trigger requests";
+	case TEST_PACKING_NOT_EXP_TRIGGER_READ_TRIGGER:
+		return "\nTest packing control - no pack, trigger-read-trigger";
+	case TEST_PACKING_NOT_EXP_TRIGGER_FLUSH_TRIGGER:
+		return "\nTest packing control- no pack, trigger-flush-trigger";
+	case TEST_PACK_MIX_PACKED_NO_PACKED_PACKED:
+		return "\nTest packing control - mix: pack -> no pack -> pack";
+	case TEST_PACK_MIX_NO_PACKED_PACKED_NO_PACKED:
+		return "\nTest packing control - mix: no pack->pack->no pack";
+	case TEST_WRITE_DISCARD_SANITIZE_READ:
+		return "\nTest write, discard, sanitize";
+	case BKOPS_DELAYED_WORK_LEVEL_1:
+		return "\nTest delayed work BKOPS level 1";
+	case BKOPS_DELAYED_WORK_LEVEL_1_HPI:
+		return "\nTest delayed work BKOPS level 1 with HPI";
+	case BKOPS_CANCEL_DELAYED_WORK:
+		return "\nTest cancel delayed BKOPS work";
+	case BKOPS_URGENT_LEVEL_2:
+		return "\nTest urgent BKOPS level 2";
+	case BKOPS_URGENT_LEVEL_2_TWO_REQS:
+		return "\nTest urgent BKOPS level 2, followed by a request";
+	case BKOPS_URGENT_LEVEL_3:
+		return "\nTest urgent BKOPS level 3";
+	case TEST_LONG_SEQUENTIAL_READ:
+		return "Test long sequential read";
+	case TEST_LONG_SEQUENTIAL_WRITE:
+		return "Test long sequential write";
+	default:
+		 return "Unknown testcase";
+	}
+
+	return NULL;
+}
+
+/*
+ * Compare individual testcase's statistics to the expected statistics:
+ * Compare stop reason and number of packing events
+ */
+static int check_wr_packing_statistics(struct test_data *td)
+{
+	struct mmc_wr_pack_stats *mmc_packed_stats;
+	struct mmc_queue *mq = td->req_q->queuedata;
+	int max_packed_reqs = mq->card->ext_csd.max_packed_writes;
+	int i;
+	struct mmc_card *card = mq->card;
+	struct mmc_wr_pack_stats expected_stats;
+	int *stop_reason;
+	int ret = 0;
+
+	if (!mq) {
+		test_pr_err("%s: NULL mq", __func__);
+		return -EINVAL;
+	}
+
+	expected_stats = mbtd->exp_packed_stats;
+
+	mmc_packed_stats = mmc_blk_get_packed_statistics(card);
+	if (!mmc_packed_stats) {
+		test_pr_err("%s: NULL mmc_packed_stats", __func__);
+		return -EINVAL;
+	}
+
+	if (!mmc_packed_stats->packing_events) {
+		test_pr_err("%s: NULL packing_events", __func__);
+		return -EINVAL;
+	}
+
+	spin_lock(&mmc_packed_stats->lock);
+
+	if (!mmc_packed_stats->enabled) {
+		test_pr_err("%s write packing statistics are not enabled",
+			     __func__);
+		ret = -EINVAL;
+		goto exit_err;
+	}
+
+	stop_reason = mmc_packed_stats->pack_stop_reason;
+
+	for (i = 1; i <= max_packed_reqs; ++i) {
+		if (mmc_packed_stats->packing_events[i] !=
+		    expected_stats.packing_events[i]) {
+			test_pr_err(
+			"%s: Wrong pack stats in index %d, got %d, expected %d",
+			__func__, i, mmc_packed_stats->packing_events[i],
+			       expected_stats.packing_events[i]);
+			if (td->fs_wr_reqs_during_test)
+				goto cancel_round;
+			ret = -EINVAL;
+			goto exit_err;
+		}
+	}
+
+	if (mmc_packed_stats->pack_stop_reason[EXCEEDS_SEGMENTS] !=
+	    expected_stats.pack_stop_reason[EXCEEDS_SEGMENTS]) {
+		test_pr_err(
+		"%s: Wrong pack stop reason EXCEEDS_SEGMENTS %d, expected %d",
+			__func__, stop_reason[EXCEEDS_SEGMENTS],
+		       expected_stats.pack_stop_reason[EXCEEDS_SEGMENTS]);
+		if (td->fs_wr_reqs_during_test)
+			goto cancel_round;
+		ret = -EINVAL;
+		goto exit_err;
+	}
+
+	if (mmc_packed_stats->pack_stop_reason[EXCEEDS_SECTORS] !=
+	    expected_stats.pack_stop_reason[EXCEEDS_SECTORS]) {
+		test_pr_err(
+		"%s: Wrong pack stop reason EXCEEDS_SECTORS %d, expected %d",
+			__func__, stop_reason[EXCEEDS_SECTORS],
+		       expected_stats.pack_stop_reason[EXCEEDS_SECTORS]);
+		if (td->fs_wr_reqs_during_test)
+			goto cancel_round;
+		ret = -EINVAL;
+		goto exit_err;
+	}
+
+	if (mmc_packed_stats->pack_stop_reason[WRONG_DATA_DIR] !=
+	    expected_stats.pack_stop_reason[WRONG_DATA_DIR]) {
+		test_pr_err(
+		"%s: Wrong pack stop reason WRONG_DATA_DIR %d, expected %d",
+		       __func__, stop_reason[WRONG_DATA_DIR],
+		       expected_stats.pack_stop_reason[WRONG_DATA_DIR]);
+		if (td->fs_wr_reqs_during_test)
+			goto cancel_round;
+		ret = -EINVAL;
+		goto exit_err;
+	}
+
+	if (mmc_packed_stats->pack_stop_reason[FLUSH_OR_DISCARD] !=
+	    expected_stats.pack_stop_reason[FLUSH_OR_DISCARD]) {
+		test_pr_err(
+		"%s: Wrong pack stop reason FLUSH_OR_DISCARD %d, expected %d",
+		       __func__, stop_reason[FLUSH_OR_DISCARD],
+		       expected_stats.pack_stop_reason[FLUSH_OR_DISCARD]);
+		if (td->fs_wr_reqs_during_test)
+			goto cancel_round;
+		ret = -EINVAL;
+		goto exit_err;
+	}
+
+	if (mmc_packed_stats->pack_stop_reason[EMPTY_QUEUE] !=
+	    expected_stats.pack_stop_reason[EMPTY_QUEUE]) {
+		test_pr_err(
+		"%s: Wrong pack stop reason EMPTY_QUEUE %d, expected %d",
+		       __func__, stop_reason[EMPTY_QUEUE],
+		       expected_stats.pack_stop_reason[EMPTY_QUEUE]);
+		if (td->fs_wr_reqs_during_test)
+			goto cancel_round;
+		ret = -EINVAL;
+		goto exit_err;
+	}
+
+	if (mmc_packed_stats->pack_stop_reason[REL_WRITE] !=
+	    expected_stats.pack_stop_reason[REL_WRITE]) {
+		test_pr_err(
+			"%s: Wrong pack stop reason REL_WRITE %d, expected %d",
+		       __func__, stop_reason[REL_WRITE],
+		       expected_stats.pack_stop_reason[REL_WRITE]);
+		if (td->fs_wr_reqs_during_test)
+			goto cancel_round;
+		ret = -EINVAL;
+		goto exit_err;
+	}
+
+exit_err:
+	spin_unlock(&mmc_packed_stats->lock);
+	if (ret && mmc_packed_stats->enabled)
+		print_mmc_packing_stats(card);
+	return ret;
+cancel_round:
+	spin_unlock(&mmc_packed_stats->lock);
+	test_iosched_set_ignore_round(true);
+	return 0;
+}
+
+/*
+ * Pseudo-randomly choose a seed based on the last seed, and update it in
+ * seed_number. then return seed_number (mod max_val), or min_val.
+ */
+static unsigned int pseudo_random_seed(unsigned int *seed_number,
+				       unsigned int min_val,
+				       unsigned int max_val)
+{
+	int ret = 0;
+
+	if (!seed_number)
+		return 0;
+
+	*seed_number = ((unsigned int)(((unsigned long)*seed_number *
+				(unsigned long)LARGE_PRIME_1) + LARGE_PRIME_2));
+	ret = (unsigned int)((*seed_number) % max_val);
+
+	return (ret > min_val ? ret : min_val);
+}
+
+/*
+ * Given a pseudo-random seed, find a pseudo-random num_of_bios.
+ * Make sure that num_of_bios is not larger than TEST_MAX_SECTOR_RANGE
+ */
+static void pseudo_rnd_num_of_bios(unsigned int *num_bios_seed,
+				   unsigned int *num_of_bios)
+{
+	do {
+		*num_of_bios = pseudo_random_seed(num_bios_seed, 1,
+						  TEST_MAX_BIOS_PER_REQ);
+		if (!(*num_of_bios))
+			*num_of_bios = 1;
+	} while ((*num_of_bios) * BIO_U32_SIZE * 4 > TEST_MAX_SECTOR_RANGE);
+}
+
+/* Add a single read request to the given td's request queue */
+static int prepare_request_add_read(struct test_data *td)
+{
+	int ret;
+	int start_sec;
+
+	if (td)
+		start_sec = td->start_sector;
+	else {
+		test_pr_err("%s: NULL td", __func__);
+		return 0;
+	}
+
+	test_pr_info("%s: Adding a read request, first req_id=%d", __func__,
+		     td->wr_rd_next_req_id);
+
+	ret = test_iosched_add_wr_rd_test_req(0, READ, start_sec, 2,
+					      TEST_PATTERN_5A, NULL);
+	if (ret) {
+		test_pr_err("%s: failed to add a read request", __func__);
+		return ret;
+	}
+
+	return 0;
+}
+
+/* Add a single flush request to the given td's request queue */
+static int prepare_request_add_flush(struct test_data *td)
+{
+	int ret;
+
+	if (!td) {
+		test_pr_err("%s: NULL td", __func__);
+		return 0;
+	}
+
+	test_pr_info("%s: Adding a flush request, first req_id=%d", __func__,
+		     td->unique_next_req_id);
+	ret = test_iosched_add_unique_test_req(0, REQ_UNIQUE_FLUSH,
+				  0, 0, NULL);
+	if (ret) {
+		test_pr_err("%s: failed to add a flush request", __func__);
+		return ret;
+	}
+
+	return ret;
+}
+
+/*
+ * Add num_requets amount of write requests to the given td's request queue.
+ * If random test mode is chosen we pseudo-randomly choose the number of bios
+ * for each write request, otherwise add between 1 to 5 bio per request.
+ */
+static int prepare_request_add_write_reqs(struct test_data *td,
+					  int num_requests, int is_err_expected,
+					  int is_random)
+{
+	int i;
+	unsigned int start_sec;
+	int num_bios;
+	int ret = 0;
+	unsigned int *bio_seed = &mbtd->random_test_seed;
+
+	if (td)
+		start_sec = td->start_sector;
+	else {
+		test_pr_err("%s: NULL td", __func__);
+		return ret;
+	}
+
+	test_pr_info("%s: Adding %d write requests, first req_id=%d", __func__,
+		     num_requests, td->wr_rd_next_req_id);
+
+	for (i = 1 ; i <= num_requests ; i++) {
+		start_sec =
+			td->start_sector + sizeof(int) *
+			BIO_U32_SIZE * td->num_of_write_bios;
+		if (is_random)
+			pseudo_rnd_num_of_bios(bio_seed, &num_bios);
+		else
+			/*
+			 * For the non-random case, give num_bios a value
+			 * between 1 and 5, to keep a small number of BIOs
+			 */
+			num_bios = (i%5)+1;
+
+		ret = test_iosched_add_wr_rd_test_req(is_err_expected, WRITE,
+				start_sec, num_bios, TEST_PATTERN_5A, NULL);
+
+		if (ret) {
+			test_pr_err("%s: failed to add a write request",
+				    __func__);
+			return ret;
+		}
+	}
+	return 0;
+}
+
+/*
+ * Prepare the write, read and flush requests for a generic packed commands
+ * testcase
+ */
+static int prepare_packed_requests(struct test_data *td, int is_err_expected,
+				   int num_requests, int is_random)
+{
+	int ret = 0;
+	struct mmc_queue *mq;
+	int max_packed_reqs;
+	struct request_queue *req_q;
+
+	if (!td) {
+		pr_err("%s: NULL td", __func__);
+		return -EINVAL;
+	}
+
+	req_q = td->req_q;
+
+	if (!req_q) {
+		pr_err("%s: NULL request queue", __func__);
+		return -EINVAL;
+	}
+
+	mq = req_q->queuedata;
+	if (!mq) {
+		test_pr_err("%s: NULL mq", __func__);
+		return -EINVAL;
+	}
+
+	max_packed_reqs = mq->card->ext_csd.max_packed_writes;
+
+	if (mbtd->random_test_seed <= 0) {
+		mbtd->random_test_seed =
+			(unsigned int)(get_jiffies_64() & 0xFFFF);
+		test_pr_info("%s: got seed from jiffies %d",
+			     __func__, mbtd->random_test_seed);
+	}
+
+	mmc_blk_init_packed_statistics(mq->card);
+
+	ret = prepare_request_add_write_reqs(td, num_requests, is_err_expected,
+					     is_random);
+	if (ret)
+		return ret;
+
+	/* Avoid memory corruption in upcoming stats set */
+	if (td->test_info.testcase == TEST_STOP_DUE_TO_THRESHOLD)
+		num_requests--;
+
+	memset((void *)mbtd->exp_packed_stats.pack_stop_reason, 0,
+		sizeof(mbtd->exp_packed_stats.pack_stop_reason));
+	memset(mbtd->exp_packed_stats.packing_events, 0,
+		(max_packed_reqs + 1) * sizeof(u32));
+	if (num_requests <= max_packed_reqs)
+		mbtd->exp_packed_stats.packing_events[num_requests] = 1;
+
+	switch (td->test_info.testcase) {
+	case TEST_STOP_DUE_TO_FLUSH:
+	case TEST_STOP_DUE_TO_FLUSH_AFTER_MAX_REQS:
+		ret = prepare_request_add_flush(td);
+		if (ret)
+			return ret;
+
+		mbtd->exp_packed_stats.pack_stop_reason[FLUSH_OR_DISCARD] = 1;
+		break;
+	case TEST_STOP_DUE_TO_READ:
+	case TEST_STOP_DUE_TO_READ_AFTER_MAX_REQS:
+		ret = prepare_request_add_read(td);
+		if (ret)
+			return ret;
+
+		mbtd->exp_packed_stats.pack_stop_reason[WRONG_DATA_DIR] = 1;
+		break;
+	case TEST_STOP_DUE_TO_THRESHOLD:
+		mbtd->exp_packed_stats.packing_events[num_requests] = 1;
+		mbtd->exp_packed_stats.packing_events[1] = 1;
+		mbtd->exp_packed_stats.pack_stop_reason[THRESHOLD] = 1;
+		mbtd->exp_packed_stats.pack_stop_reason[EMPTY_QUEUE] = 1;
+		break;
+	case TEST_STOP_DUE_TO_MAX_REQ_NUM:
+	case TEST_RET_PARTIAL_MAX_FAIL_IDX:
+		mbtd->exp_packed_stats.pack_stop_reason[THRESHOLD] = 1;
+		break;
+	default:
+		mbtd->exp_packed_stats.pack_stop_reason[EMPTY_QUEUE] = 1;
+	}
+	mbtd->num_requests = num_requests;
+
+	return 0;
+}
+
+/*
+ * Prepare the write, read and flush requests for the packing control
+ * testcases
+ */
+static int prepare_packed_control_tests_requests(struct test_data *td,
+			int is_err_expected, int num_requests, int is_random)
+{
+	int ret = 0;
+	struct mmc_queue *mq;
+	int max_packed_reqs;
+	int temp_num_req = num_requests;
+	struct request_queue *req_q;
+	int test_packed_trigger;
+	int num_packed_reqs;
+
+	if (!td) {
+		test_pr_err("%s: NULL td\n", __func__);
+		return -EINVAL;
+	}
+
+	req_q = td->req_q;
+
+	if (!req_q) {
+		test_pr_err("%s: NULL request queue\n", __func__);
+		return -EINVAL;
+	}
+
+	mq = req_q->queuedata;
+	if (!mq) {
+		test_pr_err("%s: NULL mq", __func__);
+		return -EINVAL;
+	}
+
+	max_packed_reqs = mq->card->ext_csd.max_packed_writes;
+	test_packed_trigger = mq->num_wr_reqs_to_start_packing;
+	num_packed_reqs = num_requests - test_packed_trigger;
+
+	if (mbtd->random_test_seed == 0) {
+		mbtd->random_test_seed =
+			(unsigned int)(get_jiffies_64() & 0xFFFF);
+		test_pr_info("%s: got seed from jiffies %d",
+			     __func__, mbtd->random_test_seed);
+	}
+
+	mmc_blk_init_packed_statistics(mq->card);
+
+	if (td->test_info.testcase ==
+			TEST_PACK_MIX_NO_PACKED_PACKED_NO_PACKED) {
+		temp_num_req = num_requests;
+		num_requests = test_packed_trigger - 1;
+	}
+
+	/* Verify that the packing is disabled before starting the test */
+	mq->wr_packing_enabled = false;
+	mq->num_of_potential_packed_wr_reqs = 0;
+
+	if (td->test_info.testcase == TEST_PACK_MIX_PACKED_NO_PACKED_PACKED) {
+		mq->num_of_potential_packed_wr_reqs = test_packed_trigger + 1;
+		mq->wr_packing_enabled = true;
+		num_requests = test_packed_trigger + 2;
+	}
+
+	ret = prepare_request_add_write_reqs(td, num_requests, is_err_expected,
+					     is_random);
+	if (ret)
+		goto exit;
+
+	if (td->test_info.testcase == TEST_PACK_MIX_NO_PACKED_PACKED_NO_PACKED)
+		num_requests = temp_num_req;
+
+	memset((void *)mbtd->exp_packed_stats.pack_stop_reason, 0,
+		sizeof(mbtd->exp_packed_stats.pack_stop_reason));
+	memset(mbtd->exp_packed_stats.packing_events, 0,
+		(max_packed_reqs + 1) * sizeof(u32));
+
+	switch (td->test_info.testcase) {
+	case TEST_PACKING_EXP_N_OVER_TRIGGER_FB_READ:
+	case TEST_PACKING_EXP_ONE_OVER_TRIGGER_FB_READ:
+		ret = prepare_request_add_read(td);
+		if (ret)
+			goto exit;
+
+		mbtd->exp_packed_stats.pack_stop_reason[WRONG_DATA_DIR] = 1;
+		mbtd->exp_packed_stats.packing_events[num_packed_reqs] = 1;
+		break;
+	case TEST_PACKING_EXP_N_OVER_TRIGGER_FLUSH_N:
+		ret = prepare_request_add_flush(td);
+		if (ret)
+			goto exit;
+
+		ret = prepare_request_add_write_reqs(td, num_packed_reqs,
+					     is_err_expected, is_random);
+		if (ret)
+			goto exit;
+
+		mbtd->exp_packed_stats.pack_stop_reason[EMPTY_QUEUE] = 1;
+		mbtd->exp_packed_stats.pack_stop_reason[FLUSH_OR_DISCARD] = 1;
+		mbtd->exp_packed_stats.packing_events[num_packed_reqs] = 2;
+		break;
+	case TEST_PACKING_NOT_EXP_TRIGGER_READ_TRIGGER:
+		ret = prepare_request_add_read(td);
+		if (ret)
+			goto exit;
+
+		ret = prepare_request_add_write_reqs(td, test_packed_trigger,
+						    is_err_expected, is_random);
+		if (ret)
+			goto exit;
+
+		mbtd->exp_packed_stats.packing_events[num_packed_reqs] = 1;
+		break;
+	case TEST_PACKING_NOT_EXP_TRIGGER_FLUSH_TRIGGER:
+		ret = prepare_request_add_flush(td);
+		if (ret)
+			goto exit;
+
+		ret = prepare_request_add_write_reqs(td, test_packed_trigger,
+						    is_err_expected, is_random);
+		if (ret)
+			goto exit;
+
+		mbtd->exp_packed_stats.packing_events[num_packed_reqs] = 1;
+		break;
+	case TEST_PACK_MIX_PACKED_NO_PACKED_PACKED:
+		ret = prepare_request_add_read(td);
+		if (ret)
+			goto exit;
+
+		ret = prepare_request_add_write_reqs(td, test_packed_trigger-1,
+						    is_err_expected, is_random);
+		if (ret)
+			goto exit;
+
+		ret = prepare_request_add_write_reqs(td, num_requests,
+						    is_err_expected, is_random);
+		if (ret)
+			goto exit;
+
+		mbtd->exp_packed_stats.packing_events[num_requests] = 1;
+		mbtd->exp_packed_stats.packing_events[num_requests-1] = 1;
+		mbtd->exp_packed_stats.pack_stop_reason[WRONG_DATA_DIR] = 1;
+		mbtd->exp_packed_stats.pack_stop_reason[EMPTY_QUEUE] = 1;
+		break;
+	case TEST_PACK_MIX_NO_PACKED_PACKED_NO_PACKED:
+		ret = prepare_request_add_read(td);
+		if (ret)
+			goto exit;
+
+		ret = prepare_request_add_write_reqs(td, num_requests,
+						    is_err_expected, is_random);
+		if (ret)
+			goto exit;
+
+		ret = prepare_request_add_read(td);
+		if (ret)
+			goto exit;
+
+		ret = prepare_request_add_write_reqs(td, test_packed_trigger-1,
+						    is_err_expected, is_random);
+		if (ret)
+			goto exit;
+
+		mbtd->exp_packed_stats.pack_stop_reason[WRONG_DATA_DIR] = 1;
+		mbtd->exp_packed_stats.packing_events[num_packed_reqs] = 1;
+		break;
+	case TEST_PACKING_NOT_EXP_LESS_THAN_TRIGGER_REQUESTS:
+	case TEST_PACKING_NOT_EXP_TRIGGER_REQUESTS:
+		mbtd->exp_packed_stats.packing_events[num_packed_reqs] = 1;
+		break;
+	default:
+		mbtd->exp_packed_stats.pack_stop_reason[EMPTY_QUEUE] = 1;
+		mbtd->exp_packed_stats.packing_events[num_packed_reqs] = 1;
+	}
+	mbtd->num_requests = num_requests;
+
+exit:
+	return ret;
+}
+
+/*
+ * Prepare requests for the TEST_RET_PARTIAL_FOLLOWED_BY_ABORT testcase.
+ * In this testcase we have mixed error expectations from different
+ * write requests, hence the special prepare function.
+ */
+static int prepare_partial_followed_by_abort(struct test_data *td,
+					      int num_requests)
+{
+	int i, start_address;
+	int is_err_expected = 0;
+	int ret = 0;
+	struct mmc_queue *mq = test_iosched_get_req_queue()->queuedata;
+	int max_packed_reqs;
+
+	if (!mq) {
+		test_pr_err("%s: NULL mq", __func__);
+		return -EINVAL;
+	}
+
+	max_packed_reqs = mq->card->ext_csd.max_packed_writes;
+
+	mmc_blk_init_packed_statistics(mq->card);
+
+	for (i = 1; i <= num_requests; i++) {
+		if (i > (num_requests / 2))
+			is_err_expected = 1;
+
+		start_address = td->start_sector +
+			sizeof(int) * BIO_U32_SIZE * td->num_of_write_bios;
+		ret = test_iosched_add_wr_rd_test_req(is_err_expected, WRITE,
+				start_address, (i % 5) + 1, TEST_PATTERN_5A,
+				NULL);
+		if (ret) {
+			test_pr_err("%s: failed to add a write request",
+				    __func__);
+			return ret;
+		}
+	}
+
+	memset((void *)&mbtd->exp_packed_stats.pack_stop_reason, 0,
+		sizeof(mbtd->exp_packed_stats.pack_stop_reason));
+	memset(mbtd->exp_packed_stats.packing_events, 0,
+		(max_packed_reqs + 1) * sizeof(u32));
+	mbtd->exp_packed_stats.packing_events[num_requests] = 1;
+	mbtd->exp_packed_stats.pack_stop_reason[EMPTY_QUEUE] = 1;
+
+	mbtd->num_requests = num_requests;
+
+	return ret;
+}
+
+/*
+ * Get number of write requests for current testcase. If random test mode was
+ * chosen, pseudo-randomly choose the number of requests, otherwise set to
+ * two less than the packing threshold.
+ */
+static int get_num_requests(struct test_data *td)
+{
+	int *seed = &mbtd->random_test_seed;
+	struct request_queue *req_q;
+	struct mmc_queue *mq;
+	int max_num_requests;
+	int num_requests;
+	int min_num_requests = 2;
+	int is_random = mbtd->is_random;
+	int max_for_double;
+	int test_packed_trigger;
+
+	req_q = test_iosched_get_req_queue();
+	if (req_q)
+		mq = req_q->queuedata;
+	else {
+		test_pr_err("%s: NULL request queue", __func__);
+		return 0;
+	}
+
+	if (!mq) {
+		test_pr_err("%s: NULL mq", __func__);
+		return -EINVAL;
+	}
+
+	max_num_requests = mq->card->ext_csd.max_packed_writes;
+	num_requests = max_num_requests - 2;
+	test_packed_trigger = mq->num_wr_reqs_to_start_packing;
+
+	/*
+	 * Here max_for_double is intended for packed control testcases
+	 * in which we issue many write requests. It's purpose is to prevent
+	 * exceeding max number of req_queue requests.
+	 */
+	max_for_double = max_num_requests - 10;
+
+	if (td->test_info.testcase ==
+				TEST_PACKING_NOT_EXP_LESS_THAN_TRIGGER_REQUESTS)
+		/* Don't expect packing, so issue up to trigger-1 reqs */
+		num_requests = test_packed_trigger - 1;
+
+	if (is_random) {
+		if (td->test_info.testcase ==
+		    TEST_RET_PARTIAL_FOLLOWED_BY_ABORT)
+			/*
+			 * Here we don't want num_requests to be less than 1
+			 * as a consequence of division by 2.
+			 */
+			min_num_requests = 3;
+
+		if (td->test_info.testcase ==
+				TEST_PACKING_NOT_EXP_LESS_THAN_TRIGGER_REQUESTS)
+			/* Don't expect packing, so issue up to trigger reqs */
+			max_num_requests = test_packed_trigger;
+
+		num_requests = pseudo_random_seed(seed, min_num_requests,
+						  max_num_requests - 1);
+	}
+
+	if (td->test_info.testcase ==
+				TEST_PACKING_NOT_EXP_LESS_THAN_TRIGGER_REQUESTS)
+		num_requests -= test_packed_trigger;
+
+	if (td->test_info.testcase == TEST_PACKING_EXP_N_OVER_TRIGGER_FLUSH_N)
+		num_requests =
+		num_requests > max_for_double ? max_for_double : num_requests;
+
+	if (mbtd->test_group == TEST_PACKING_CONTROL_GROUP)
+		num_requests += test_packed_trigger;
+
+	if (td->test_info.testcase == TEST_PACKING_NOT_EXP_TRIGGER_REQUESTS)
+		num_requests = test_packed_trigger;
+
+	return num_requests;
+}
+
+static int prepare_long_test_requests(struct test_data *td)
+{
+
+	int ret;
+	int start_sec;
+	int j;
+	int test_direction;
+
+	if (td)
+		start_sec = td->start_sector;
+	else {
+		test_pr_err("%s: NULL td\n", __func__);
+		return -EINVAL;
+	}
+
+	if (td->test_info.testcase == TEST_LONG_SEQUENTIAL_WRITE)
+		test_direction = WRITE;
+	else
+		test_direction = READ;
+
+	test_pr_info("%s: Adding %d write requests, first req_id=%d", __func__,
+		     LONG_TEST_ACTUAL_NUM_REQS, td->wr_rd_next_req_id);
+
+	for (j = 0; j < LONG_TEST_ACTUAL_NUM_REQS; j++) {
+
+		ret = test_iosched_add_wr_rd_test_req(0, test_direction,
+						start_sec,
+						TEST_MAX_BIOS_PER_REQ,
+						TEST_NO_PATTERN, NULL);
+		if (ret) {
+			test_pr_err("%s: failed to add a bio request",
+				     __func__);
+			return ret;
+		}
+
+		start_sec +=
+			(TEST_MAX_BIOS_PER_REQ * sizeof(int) * BIO_U32_SIZE);
+	}
+
+	return 0;
+}
+
+/*
+ * An implementation for the prepare_test_fn pointer in the test_info
+ * data structure. According to the testcase we add the right number of requests
+ * and decide if an error is expected or not.
+ */
+static int prepare_test(struct test_data *td)
+{
+	struct mmc_queue *mq = test_iosched_get_req_queue()->queuedata;
+	int max_num_requests;
+	int num_requests = 0;
+	int ret = 0;
+	int is_random = mbtd->is_random;
+	int test_packed_trigger = mq->num_wr_reqs_to_start_packing;
+
+	if (!mq) {
+		test_pr_err("%s: NULL mq", __func__);
+		return -EINVAL;
+	}
+
+	max_num_requests = mq->card->ext_csd.max_packed_writes;
+
+	if (is_random && mbtd->random_test_seed == 0) {
+		mbtd->random_test_seed =
+			(unsigned int)(get_jiffies_64() & 0xFFFF);
+		test_pr_info("%s: got seed from jiffies %d",
+			__func__, mbtd->random_test_seed);
+	}
+
+	num_requests = get_num_requests(td);
+
+	if (mbtd->test_group == TEST_SEND_INVALID_GROUP)
+		mq->packed_test_fn =
+				test_invalid_packed_cmd;
+
+	if (mbtd->test_group == TEST_ERR_CHECK_GROUP)
+		mq->err_check_fn = test_err_check;
+
+	switch (td->test_info.testcase) {
+	case TEST_STOP_DUE_TO_FLUSH:
+	case TEST_STOP_DUE_TO_READ:
+	case TEST_RET_PARTIAL_FOLLOWED_BY_SUCCESS:
+	case TEST_RET_PARTIAL_MULTIPLE_UNTIL_SUCCESS:
+	case TEST_STOP_DUE_TO_EMPTY_QUEUE:
+	case TEST_CMD23_PACKED_BIT_UNSET:
+		ret = prepare_packed_requests(td, 0, num_requests, is_random);
+		break;
+	case TEST_STOP_DUE_TO_FLUSH_AFTER_MAX_REQS:
+	case TEST_STOP_DUE_TO_READ_AFTER_MAX_REQS:
+		ret = prepare_packed_requests(td, 0, max_num_requests - 1,
+					      is_random);
+		break;
+	case TEST_RET_PARTIAL_FOLLOWED_BY_ABORT:
+		ret = prepare_partial_followed_by_abort(td, num_requests);
+		break;
+	case TEST_STOP_DUE_TO_MAX_REQ_NUM:
+	case TEST_RET_PARTIAL_MAX_FAIL_IDX:
+		ret = prepare_packed_requests(td, 0, max_num_requests,
+					      is_random);
+		break;
+	case TEST_STOP_DUE_TO_THRESHOLD:
+		ret = prepare_packed_requests(td, 0, max_num_requests + 1,
+					      is_random);
+		break;
+	case TEST_RET_ABORT:
+	case TEST_RET_RETRY:
+	case TEST_RET_CMD_ERR:
+	case TEST_RET_DATA_ERR:
+	case TEST_HDR_INVALID_VERSION:
+	case TEST_HDR_WRONG_WRITE_CODE:
+	case TEST_HDR_INVALID_RW_CODE:
+	case TEST_HDR_DIFFERENT_ADDRESSES:
+	case TEST_HDR_REQ_NUM_SMALLER_THAN_ACTUAL:
+	case TEST_HDR_REQ_NUM_LARGER_THAN_ACTUAL:
+	case TEST_CMD23_MAX_PACKED_WRITES:
+	case TEST_CMD23_ZERO_PACKED_WRITES:
+	case TEST_CMD23_REL_WR_BIT_SET:
+	case TEST_CMD23_BITS_16TO29_SET:
+	case TEST_CMD23_HDR_BLK_NOT_IN_COUNT:
+	case TEST_HDR_CMD23_PACKED_BIT_SET:
+		ret = prepare_packed_requests(td, 1, num_requests, is_random);
+		break;
+	case TEST_PACKING_EXP_N_OVER_TRIGGER:
+	case TEST_PACKING_EXP_N_OVER_TRIGGER_FB_READ:
+	case TEST_PACKING_NOT_EXP_TRIGGER_REQUESTS:
+	case TEST_PACKING_NOT_EXP_LESS_THAN_TRIGGER_REQUESTS:
+	case TEST_PACK_MIX_PACKED_NO_PACKED_PACKED:
+	case TEST_PACK_MIX_NO_PACKED_PACKED_NO_PACKED:
+		ret = prepare_packed_control_tests_requests(td, 0, num_requests,
+			is_random);
+		break;
+	case TEST_PACKING_EXP_THRESHOLD_OVER_TRIGGER:
+		ret = prepare_packed_control_tests_requests(td, 0,
+			max_num_requests, is_random);
+		break;
+	case TEST_PACKING_EXP_ONE_OVER_TRIGGER_FB_READ:
+		ret = prepare_packed_control_tests_requests(td, 0,
+			test_packed_trigger + 1,
+					is_random);
+		break;
+	case TEST_PACKING_EXP_N_OVER_TRIGGER_FLUSH_N:
+		ret = prepare_packed_control_tests_requests(td, 0, num_requests,
+			is_random);
+		break;
+	case TEST_PACKING_NOT_EXP_TRIGGER_READ_TRIGGER:
+	case TEST_PACKING_NOT_EXP_TRIGGER_FLUSH_TRIGGER:
+		ret = prepare_packed_control_tests_requests(td, 0,
+			test_packed_trigger, is_random);
+		break;
+	case TEST_LONG_SEQUENTIAL_WRITE:
+		ret = prepare_long_test_requests(td);
+		break;
+	case TEST_LONG_SEQUENTIAL_READ:
+		ret = prepare_long_test_requests(td);
+		break;
+	default:
+		test_pr_info("%s: Invalid test case...", __func__);
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+/*
+ * An implementation for the post_test_fn in the test_info data structure.
+ * In our case we just reset the function pointers in the mmc_queue in order for
+ * the FS to be able to dispatch it's requests correctly after the test is
+ * finished.
+ */
+static int post_test(struct test_data *td)
+{
+	struct mmc_queue *mq;
+
+	if (!td)
+		return -EINVAL;
+
+	mq = td->req_q->queuedata;
+
+	if (!mq) {
+		test_pr_err("%s: NULL mq", __func__);
+		return -EINVAL;
+	}
+
+	mq->packed_test_fn = NULL;
+	mq->err_check_fn = NULL;
+
+	return 0;
+}
+
+/*
+ * This function checks, based on the current test's test_group, that the
+ * packed commands capability and control are set right. In addition, we check
+ * if the card supports the packed command feature.
+ */
+static int validate_packed_commands_settings(void)
+{
+	struct request_queue *req_q;
+	struct mmc_queue *mq;
+	int max_num_requests;
+	struct mmc_host *host;
+
+	req_q = test_iosched_get_req_queue();
+	if (!req_q) {
+		test_pr_err("%s: test_iosched_get_req_queue failed", __func__);
+		test_iosched_set_test_result(TEST_FAILED);
+		return -EINVAL;
+	}
+
+	mq = req_q->queuedata;
+	if (!mq) {
+		test_pr_err("%s: NULL mq", __func__);
+		return -EINVAL;
+	}
+
+	max_num_requests = mq->card->ext_csd.max_packed_writes;
+	host = mq->card->host;
+
+	if (!(host->caps2 && MMC_CAP2_PACKED_WR)) {
+		test_pr_err("%s: Packed Write capability disabled, exit test",
+			    __func__);
+		test_iosched_set_test_result(TEST_NOT_SUPPORTED);
+		return -EINVAL;
+	}
+
+	if (max_num_requests == 0) {
+		test_pr_err(
+		"%s: no write packing support, ext_csd.max_packed_writes=%d",
+		__func__, mq->card->ext_csd.max_packed_writes);
+		test_iosched_set_test_result(TEST_NOT_SUPPORTED);
+		return -EINVAL;
+	}
+
+	test_pr_info("%s: max number of packed requests supported is %d ",
+		     __func__, max_num_requests);
+
+	switch (mbtd->test_group) {
+	case TEST_SEND_WRITE_PACKING_GROUP:
+	case TEST_ERR_CHECK_GROUP:
+	case TEST_SEND_INVALID_GROUP:
+		/* disable the packing control */
+		host->caps2 &= ~MMC_CAP2_PACKED_WR_CONTROL;
+		break;
+	case TEST_PACKING_CONTROL_GROUP:
+		host->caps2 |=  MMC_CAP2_PACKED_WR_CONTROL;
+		break;
+	default:
+		break;
+	}
+
+	return 0;
+}
+
+static void pseudo_rnd_sector_and_size(unsigned int *seed,
+				       unsigned int min_start_sector,
+				       unsigned int *start_sector,
+				       unsigned int *num_of_bios)
+{
+	unsigned int max_sec = min_start_sector + TEST_MAX_SECTOR_RANGE;
+	do {
+		*start_sector = pseudo_random_seed(seed,
+						   1, max_sec);
+		*num_of_bios = pseudo_random_seed(seed,
+						  1, TEST_MAX_BIOS_PER_REQ);
+		if (!(*num_of_bios))
+			*num_of_bios = 1;
+	} while ((*start_sector < min_start_sector) ||
+		 (*start_sector + (*num_of_bios * BIO_U32_SIZE * 4)) > max_sec);
+}
+
+/* sanitize test functions */
+static int prepare_write_discard_sanitize_read(struct test_data *td)
+{
+	unsigned int start_sector;
+	unsigned int num_of_bios = 0;
+	static unsigned int total_bios;
+	unsigned int *num_bios_seed;
+	int i = 0;
+
+	if (mbtd->random_test_seed == 0) {
+		mbtd->random_test_seed =
+			(unsigned int)(get_jiffies_64() & 0xFFFF);
+		test_pr_info("%s: got seed from jiffies %d",
+			     __func__, mbtd->random_test_seed);
+	}
+	num_bios_seed = &mbtd->random_test_seed;
+
+	do {
+		pseudo_rnd_sector_and_size(num_bios_seed, td->start_sector,
+					   &start_sector, &num_of_bios);
+
+		/* DISCARD */
+		total_bios += num_of_bios;
+		test_pr_info("%s: discard req: id=%d, startSec=%d, NumBios=%d",
+		       __func__, td->unique_next_req_id, start_sector,
+			     num_of_bios);
+		test_iosched_add_unique_test_req(0, REQ_UNIQUE_DISCARD,
+				    start_sector, BIO_TO_SECTOR(num_of_bios),
+						 NULL);
+
+	} while (++i < (BLKDEV_MAX_RQ-10));
+
+	test_pr_info("%s: total discard bios = %d", __func__, total_bios);
+
+	test_pr_info("%s: add sanitize req", __func__);
+	test_iosched_add_unique_test_req(0, REQ_UNIQUE_SANITIZE, 0, 0, NULL);
+
+	return 0;
+}
+
+/*
+ * Post test operations for BKOPs test
+ * Disable the BKOPs statistics and clear the feature flags
+ */
+static int bkops_post_test(struct test_data *td)
+{
+	struct request_queue *q = td->req_q;
+	struct mmc_queue *mq = (struct mmc_queue *)q->queuedata;
+	struct mmc_card *card = mq->card;
+
+	mmc_card_clr_doing_bkops(mq->card);
+	card->ext_csd.raw_bkops_status = 0;
+
+	spin_lock(&card->bkops_info.bkops_stats.lock);
+	card->bkops_info.bkops_stats.enabled = false;
+	spin_unlock(&card->bkops_info.bkops_stats.lock);
+
+	return 0;
+}
+
+/*
+ * Verify the BKOPs statsistics
+ */
+static int check_bkops_result(struct test_data *td)
+{
+	struct request_queue *q = td->req_q;
+	struct mmc_queue *mq = (struct mmc_queue *)q->queuedata;
+	struct mmc_card *card = mq->card;
+	struct mmc_bkops_stats *bkops_stat;
+
+	if (!card)
+		goto fail;
+
+	bkops_stat = &card->bkops_info.bkops_stats;
+
+	test_pr_info("%s: Test results: bkops:(%d,%d,%d) hpi:%d, suspend:%d",
+			__func__,
+			bkops_stat->bkops_level[BKOPS_SEVERITY_1_INDEX],
+			bkops_stat->bkops_level[BKOPS_SEVERITY_2_INDEX],
+			bkops_stat->bkops_level[BKOPS_SEVERITY_3_INDEX],
+			bkops_stat->hpi,
+			bkops_stat->suspend);
+
+	switch (mbtd->test_info.testcase) {
+	case BKOPS_DELAYED_WORK_LEVEL_1:
+		if ((bkops_stat->bkops_level[BKOPS_SEVERITY_1_INDEX] == 1) &&
+		    (bkops_stat->suspend == 1) &&
+		    (bkops_stat->hpi == 0))
+			goto exit;
+		else
+			goto fail;
+		break;
+	case BKOPS_DELAYED_WORK_LEVEL_1_HPI:
+		if ((bkops_stat->bkops_level[BKOPS_SEVERITY_1_INDEX] == 1) &&
+		    (bkops_stat->suspend == 0) &&
+		    (bkops_stat->hpi == 1))
+			goto exit;
+		else
+			goto fail;
+		break;
+	case BKOPS_CANCEL_DELAYED_WORK:
+		if ((bkops_stat->bkops_level[BKOPS_SEVERITY_1_INDEX] == 0) &&
+		    (bkops_stat->bkops_level[BKOPS_SEVERITY_2_INDEX] == 0) &&
+		    (bkops_stat->bkops_level[BKOPS_SEVERITY_3_INDEX] == 0) &&
+			(bkops_stat->suspend == 0) &&
+			  (bkops_stat->hpi == 0))
+			goto exit;
+		else
+			goto fail;
+	case BKOPS_URGENT_LEVEL_2:
+	case BKOPS_URGENT_LEVEL_2_TWO_REQS:
+		if ((bkops_stat->bkops_level[BKOPS_SEVERITY_2_INDEX] == 1) &&
+		    (bkops_stat->suspend == 0) &&
+		    (bkops_stat->hpi == 0))
+			goto exit;
+		else
+			goto fail;
+	case BKOPS_URGENT_LEVEL_3:
+		if ((bkops_stat->bkops_level[BKOPS_SEVERITY_3_INDEX] == 1) &&
+		    (bkops_stat->suspend == 0) &&
+		    (bkops_stat->hpi == 0))
+			goto exit;
+		else
+			goto fail;
+	default:
+		return -EINVAL;
+	}
+
+exit:
+	return 0;
+fail:
+	if (td->fs_wr_reqs_during_test) {
+		test_pr_info("%s: wr reqs during test, cancel the round",
+		     __func__);
+		test_iosched_set_ignore_round(true);
+		return 0;
+	}
+
+	test_pr_info("%s: BKOPs statistics are not as expected, test failed",
+		     __func__);
+	return -EINVAL;
+}
+
+static void bkops_end_io_final_fn(struct request *rq, int err)
+{
+	struct test_request *test_rq =
+		(struct test_request *)rq->elv.priv[0];
+	BUG_ON(!test_rq);
+
+	test_rq->req_completed = 1;
+	test_rq->req_result = err;
+
+	test_pr_info("%s: request %d completed, err=%d",
+		     __func__, test_rq->req_id, err);
+
+	mbtd->bkops_stage = BKOPS_STAGE_4;
+	wake_up(&mbtd->bkops_wait_q);
+}
+
+static void bkops_end_io_fn(struct request *rq, int err)
+{
+	struct test_request *test_rq =
+		(struct test_request *)rq->elv.priv[0];
+	BUG_ON(!test_rq);
+
+	test_rq->req_completed = 1;
+	test_rq->req_result = err;
+
+	test_pr_info("%s: request %d completed, err=%d",
+		     __func__, test_rq->req_id, err);
+	mbtd->bkops_stage = BKOPS_STAGE_2;
+	wake_up(&mbtd->bkops_wait_q);
+
+}
+
+static int prepare_bkops(struct test_data *td)
+{
+	int ret = 0;
+	struct request_queue *q = td->req_q;
+	struct mmc_queue *mq = (struct mmc_queue *)q->queuedata;
+	struct mmc_card  *card = mq->card;
+	struct mmc_bkops_stats *bkops_stat;
+
+	if (!card)
+		return -EINVAL;
+
+	bkops_stat = &card->bkops_info.bkops_stats;
+
+	if (!card->ext_csd.bkops_en) {
+		test_pr_err("%s: BKOPS is not enabled by card or host)",
+				__func__);
+		return -ENOTSUPP;
+	}
+	if (mmc_card_doing_bkops(card)) {
+		test_pr_err("%s: BKOPS in progress, try later", __func__);
+		return -EAGAIN;
+	}
+
+	mmc_blk_init_bkops_statistics(card);
+
+	if ((mbtd->test_info.testcase == BKOPS_URGENT_LEVEL_2) ||
+	    (mbtd->test_info.testcase ==  BKOPS_URGENT_LEVEL_2_TWO_REQS) ||
+	    (mbtd->test_info.testcase == BKOPS_URGENT_LEVEL_3))
+		mq->err_check_fn = test_err_check;
+	mbtd->err_check_counter = 0;
+
+	return ret;
+}
+
+static int run_bkops(struct test_data *td)
+{
+	int ret = 0;
+	struct request_queue *q = td->req_q;
+	struct mmc_queue *mq = (struct mmc_queue *)q->queuedata;
+	struct mmc_card  *card = mq->card;
+	struct mmc_bkops_stats *bkops_stat;
+
+	if (!card)
+		return -EINVAL;
+
+	bkops_stat = &card->bkops_info.bkops_stats;
+
+	switch (mbtd->test_info.testcase) {
+	case BKOPS_DELAYED_WORK_LEVEL_1:
+		bkops_stat->ignore_card_bkops_status = true;
+		card->ext_csd.raw_bkops_status = 1;
+		card->bkops_info.sectors_changed =
+			card->bkops_info.min_sectors_to_queue_delayed_work + 1;
+		mbtd->bkops_stage = BKOPS_STAGE_1;
+
+		__blk_run_queue(q);
+		/* this long sleep makes sure the host starts bkops and
+		   also, gets into suspend */
+		msleep(10000);
+
+		bkops_stat->ignore_card_bkops_status = false;
+		card->ext_csd.raw_bkops_status = 0;
+
+		test_iosched_mark_test_completion();
+		break;
+
+	case BKOPS_DELAYED_WORK_LEVEL_1_HPI:
+		bkops_stat->ignore_card_bkops_status = true;
+		card->ext_csd.raw_bkops_status = 1;
+		card->bkops_info.sectors_changed =
+			card->bkops_info.min_sectors_to_queue_delayed_work + 1;
+		mbtd->bkops_stage = BKOPS_STAGE_1;
+
+		__blk_run_queue(q);
+		msleep(card->bkops_info.delay_ms);
+
+		ret = test_iosched_add_wr_rd_test_req(0, WRITE,
+				      td->start_sector,
+				      TEST_REQUEST_NUM_OF_BIOS,
+				      TEST_PATTERN_5A,
+				      bkops_end_io_final_fn);
+		if (ret) {
+			test_pr_err("%s: failed to add a write request",
+					__func__);
+			ret = -EINVAL;
+			break;
+		}
+
+		td->next_req = list_entry(td->test_queue.prev,
+				struct test_request, queuelist);
+		__blk_run_queue(q);
+		wait_event(mbtd->bkops_wait_q,
+			   mbtd->bkops_stage == BKOPS_STAGE_4);
+		bkops_stat->ignore_card_bkops_status = false;
+
+		test_iosched_mark_test_completion();
+		break;
+
+	case BKOPS_CANCEL_DELAYED_WORK:
+		bkops_stat->ignore_card_bkops_status = true;
+		card->ext_csd.raw_bkops_status = 1;
+		card->bkops_info.sectors_changed =
+			card->bkops_info.min_sectors_to_queue_delayed_work + 1;
+		mbtd->bkops_stage = BKOPS_STAGE_1;
+
+		__blk_run_queue(q);
+
+		ret = test_iosched_add_wr_rd_test_req(0, WRITE,
+				td->start_sector,
+				TEST_REQUEST_NUM_OF_BIOS,
+				TEST_PATTERN_5A,
+				bkops_end_io_final_fn);
+		if (ret) {
+			test_pr_err("%s: failed to add a write request",
+					__func__);
+			ret = -EINVAL;
+			break;
+		}
+
+		td->next_req = list_entry(td->test_queue.prev,
+				struct test_request, queuelist);
+		__blk_run_queue(q);
+		wait_event(mbtd->bkops_wait_q,
+			   mbtd->bkops_stage == BKOPS_STAGE_4);
+		bkops_stat->ignore_card_bkops_status = false;
+
+		test_iosched_mark_test_completion();
+		break;
+
+	case BKOPS_URGENT_LEVEL_2:
+	case BKOPS_URGENT_LEVEL_3:
+		bkops_stat->ignore_card_bkops_status = true;
+		if (mbtd->test_info.testcase == BKOPS_URGENT_LEVEL_2)
+			card->ext_csd.raw_bkops_status = 2;
+		else
+			card->ext_csd.raw_bkops_status = 3;
+		mbtd->bkops_stage = BKOPS_STAGE_1;
+
+		ret = test_iosched_add_wr_rd_test_req(0, WRITE,
+				td->start_sector,
+				TEST_REQUEST_NUM_OF_BIOS,
+				TEST_PATTERN_5A,
+				bkops_end_io_fn);
+		if (ret) {
+			test_pr_err("%s: failed to add a write request",
+					__func__);
+			ret = -EINVAL;
+			break;
+		}
+
+		td->next_req = list_entry(td->test_queue.prev,
+				struct test_request, queuelist);
+		__blk_run_queue(q);
+		wait_event(mbtd->bkops_wait_q,
+			   mbtd->bkops_stage == BKOPS_STAGE_2);
+		card->ext_csd.raw_bkops_status = 0;
+
+		ret = test_iosched_add_wr_rd_test_req(0, WRITE,
+				td->start_sector,
+				TEST_REQUEST_NUM_OF_BIOS,
+				TEST_PATTERN_5A,
+				bkops_end_io_final_fn);
+		if (ret) {
+			test_pr_err("%s: failed to add a write request",
+					__func__);
+			ret = -EINVAL;
+			break;
+		}
+
+		td->next_req = list_entry(td->test_queue.prev,
+				struct test_request, queuelist);
+		__blk_run_queue(q);
+
+		wait_event(mbtd->bkops_wait_q,
+			   mbtd->bkops_stage == BKOPS_STAGE_4);
+
+		bkops_stat->ignore_card_bkops_status = false;
+		test_iosched_mark_test_completion();
+		break;
+
+	case BKOPS_URGENT_LEVEL_2_TWO_REQS:
+		mq->wr_packing_enabled = false;
+		bkops_stat->ignore_card_bkops_status = true;
+		card->ext_csd.raw_bkops_status = 2;
+		mbtd->bkops_stage = BKOPS_STAGE_1;
+
+		ret = test_iosched_add_wr_rd_test_req(0, WRITE,
+				td->start_sector,
+				TEST_REQUEST_NUM_OF_BIOS,
+				TEST_PATTERN_5A,
+				NULL);
+		if (ret) {
+			test_pr_err("%s: failed to add a write request",
+					__func__);
+			ret = -EINVAL;
+			break;
+		}
+
+		ret = test_iosched_add_wr_rd_test_req(0, WRITE,
+				td->start_sector,
+				TEST_REQUEST_NUM_OF_BIOS,
+				TEST_PATTERN_5A,
+				bkops_end_io_fn);
+		if (ret) {
+			test_pr_err("%s: failed to add a write request",
+					__func__);
+			ret = -EINVAL;
+			break;
+		}
+
+		td->next_req = list_entry(td->test_queue.next,
+				struct test_request, queuelist);
+		__blk_run_queue(q);
+		wait_event(mbtd->bkops_wait_q,
+			   mbtd->bkops_stage == BKOPS_STAGE_2);
+		card->ext_csd.raw_bkops_status = 0;
+
+		ret = test_iosched_add_wr_rd_test_req(0, WRITE,
+				td->start_sector,
+				TEST_REQUEST_NUM_OF_BIOS,
+				TEST_PATTERN_5A,
+				bkops_end_io_final_fn);
+		if (ret) {
+			test_pr_err("%s: failed to add a write request",
+					__func__);
+			ret = -EINVAL;
+			break;
+		}
+
+		td->next_req = list_entry(td->test_queue.prev,
+				struct test_request, queuelist);
+		__blk_run_queue(q);
+
+		wait_event(mbtd->bkops_wait_q,
+			   mbtd->bkops_stage == BKOPS_STAGE_4);
+
+		bkops_stat->ignore_card_bkops_status = false;
+		test_iosched_mark_test_completion();
+
+		break;
+	default:
+		test_pr_err("%s: wrong testcase: %d", __func__,
+			    mbtd->test_info.testcase);
+		ret = -EINVAL;
+	}
+	return ret;
+}
+
+static bool message_repeat;
+static int test_open(struct inode *inode, struct file *file)
+{
+	file->private_data = inode->i_private;
+	message_repeat = 1;
+	return 0;
+}
+
+/* send_packing TEST */
+static ssize_t send_write_packing_test_write(struct file *file,
+				const char __user *buf,
+				size_t count,
+				loff_t *ppos)
+{
+	int ret = 0;
+	int i = 0;
+	int number = -1;
+	int j = 0;
+
+	test_pr_info("%s: -- send_write_packing TEST --", __func__);
+
+	sscanf(buf, "%d", &number);
+
+	if (number <= 0)
+		number = 1;
+
+
+	mbtd->test_group = TEST_SEND_WRITE_PACKING_GROUP;
+
+	if (validate_packed_commands_settings())
+		return count;
+
+	if (mbtd->random_test_seed > 0)
+		test_pr_info("%s: Test seed: %d", __func__,
+			      mbtd->random_test_seed);
+
+	memset(&mbtd->test_info, 0, sizeof(struct test_info));
+
+	mbtd->test_info.data = mbtd;
+	mbtd->test_info.prepare_test_fn = prepare_test;
+	mbtd->test_info.check_test_result_fn = check_wr_packing_statistics;
+	mbtd->test_info.get_test_case_str_fn = get_test_case_str;
+	mbtd->test_info.post_test_fn = post_test;
+
+	for (i = 0; i < number; ++i) {
+		test_pr_info("%s: Cycle # %d / %d", __func__, i+1, number);
+		test_pr_info("%s: ====================", __func__);
+
+		for (j = SEND_WRITE_PACKING_MIN_TESTCASE;
+		      j <= SEND_WRITE_PACKING_MAX_TESTCASE; j++) {
+
+			mbtd->test_info.testcase = j;
+			mbtd->is_random = RANDOM_TEST;
+			ret = test_iosched_start_test(&mbtd->test_info);
+			if (ret)
+				break;
+			/* Allow FS requests to be dispatched */
+			msleep(1000);
+			mbtd->test_info.testcase = j;
+			mbtd->is_random = NON_RANDOM_TEST;
+			ret = test_iosched_start_test(&mbtd->test_info);
+			if (ret)
+				break;
+			/* Allow FS requests to be dispatched */
+			msleep(1000);
+		}
+	}
+
+	test_pr_info("%s: Completed all the test cases.", __func__);
+
+	return count;
+}
+
+static ssize_t send_write_packing_test_read(struct file *file,
+			       char __user *buffer,
+			       size_t count,
+			       loff_t *offset)
+{
+	memset((void *)buffer, 0, count);
+
+	snprintf(buffer, count,
+		 "\nsend_write_packing_test\n"
+		 "=========\n"
+		 "Description:\n"
+		 "This test checks the following scenarios\n"
+		 "- Pack due to FLUSH message\n"
+		 "- Pack due to FLUSH after threshold writes\n"
+		 "- Pack due to READ message\n"
+		 "- Pack due to READ after threshold writes\n"
+		 "- Pack due to empty queue\n"
+		 "- Pack due to threshold writes\n"
+		 "- Pack due to one over threshold writes\n");
+
+	if (message_repeat == 1) {
+		message_repeat = 0;
+		return strnlen(buffer, count);
+	} else {
+		return 0;
+	}
+}
+
+const struct file_operations send_write_packing_test_ops = {
+	.open = test_open,
+	.write = send_write_packing_test_write,
+	.read = send_write_packing_test_read,
+};
+
+/* err_check TEST */
+static ssize_t err_check_test_write(struct file *file,
+				const char __user *buf,
+				size_t count,
+				loff_t *ppos)
+{
+	int ret = 0;
+	int i = 0;
+	int number = -1;
+	int j = 0;
+
+	test_pr_info("%s: -- err_check TEST --", __func__);
+
+	sscanf(buf, "%d", &number);
+
+	if (number <= 0)
+		number = 1;
+
+	mbtd->test_group = TEST_ERR_CHECK_GROUP;
+
+	if (validate_packed_commands_settings())
+		return count;
+
+	if (mbtd->random_test_seed > 0)
+		test_pr_info("%s: Test seed: %d", __func__,
+			      mbtd->random_test_seed);
+
+	memset(&mbtd->test_info, 0, sizeof(struct test_info));
+
+	mbtd->test_info.data = mbtd;
+	mbtd->test_info.prepare_test_fn = prepare_test;
+	mbtd->test_info.check_test_result_fn = check_wr_packing_statistics;
+	mbtd->test_info.get_test_case_str_fn = get_test_case_str;
+	mbtd->test_info.post_test_fn = post_test;
+
+	for (i = 0; i < number; ++i) {
+		test_pr_info("%s: Cycle # %d / %d", __func__, i+1, number);
+		test_pr_info("%s: ====================", __func__);
+
+		for (j = ERR_CHECK_MIN_TESTCASE;
+					j <= ERR_CHECK_MAX_TESTCASE ; j++) {
+			mbtd->test_info.testcase = j;
+			mbtd->is_random = RANDOM_TEST;
+			ret = test_iosched_start_test(&mbtd->test_info);
+			if (ret)
+				break;
+			/* Allow FS requests to be dispatched */
+			msleep(1000);
+			mbtd->test_info.testcase = j;
+			mbtd->is_random = NON_RANDOM_TEST;
+			ret = test_iosched_start_test(&mbtd->test_info);
+			if (ret)
+				break;
+			/* Allow FS requests to be dispatched */
+			msleep(1000);
+		}
+	}
+
+	test_pr_info("%s: Completed all the test cases.", __func__);
+
+	return count;
+}
+
+static ssize_t err_check_test_read(struct file *file,
+			       char __user *buffer,
+			       size_t count,
+			       loff_t *offset)
+{
+	memset((void *)buffer, 0, count);
+
+	snprintf(buffer, count,
+		 "\nerr_check_TEST\n"
+		 "=========\n"
+		 "Description:\n"
+		 "This test checks the following scenarios\n"
+		 "- Return ABORT\n"
+		 "- Return PARTIAL followed by success\n"
+		 "- Return PARTIAL followed by abort\n"
+		 "- Return PARTIAL multiple times until success\n"
+		 "- Return PARTIAL with fail index = threshold\n"
+		 "- Return RETRY\n"
+		 "- Return CMD_ERR\n"
+		 "- Return DATA_ERR\n");
+
+	if (message_repeat == 1) {
+		message_repeat = 0;
+		return strnlen(buffer, count);
+	} else {
+		return 0;
+	}
+}
+
+const struct file_operations err_check_test_ops = {
+	.open = test_open,
+	.write = err_check_test_write,
+	.read = err_check_test_read,
+};
+
+/* send_invalid_packed TEST */
+static ssize_t send_invalid_packed_test_write(struct file *file,
+				const char __user *buf,
+				size_t count,
+				loff_t *ppos)
+{
+	int ret = 0;
+	int i = 0;
+	int number = -1;
+	int j = 0;
+	int num_of_failures = 0;
+
+	test_pr_info("%s: -- send_invalid_packed TEST --", __func__);
+
+	sscanf(buf, "%d", &number);
+
+	if (number <= 0)
+		number = 1;
+
+	mbtd->test_group = TEST_SEND_INVALID_GROUP;
+
+	if (validate_packed_commands_settings())
+		return count;
+
+	if (mbtd->random_test_seed > 0)
+		test_pr_info("%s: Test seed: %d", __func__,
+			      mbtd->random_test_seed);
+
+	memset(&mbtd->test_info, 0, sizeof(struct test_info));
+
+	mbtd->test_info.data = mbtd;
+	mbtd->test_info.prepare_test_fn = prepare_test;
+	mbtd->test_info.check_test_result_fn = check_wr_packing_statistics;
+	mbtd->test_info.get_test_case_str_fn = get_test_case_str;
+	mbtd->test_info.post_test_fn = post_test;
+
+	for (i = 0; i < number; ++i) {
+		test_pr_info("%s: Cycle # %d / %d", __func__, i+1, number);
+		test_pr_info("%s: ====================", __func__);
+
+		for (j = INVALID_CMD_MIN_TESTCASE;
+				j <= INVALID_CMD_MAX_TESTCASE ; j++) {
+
+			mbtd->test_info.testcase = j;
+			mbtd->is_random = RANDOM_TEST;
+			ret = test_iosched_start_test(&mbtd->test_info);
+			if (ret)
+				num_of_failures++;
+			/* Allow FS requests to be dispatched */
+			msleep(1000);
+
+			mbtd->test_info.testcase = j;
+			mbtd->is_random = NON_RANDOM_TEST;
+			ret = test_iosched_start_test(&mbtd->test_info);
+			if (ret)
+				num_of_failures++;
+			/* Allow FS requests to be dispatched */
+			msleep(1000);
+		}
+	}
+
+	test_pr_info("%s: Completed all the test cases.", __func__);
+
+	if (num_of_failures > 0) {
+		test_iosched_set_test_result(TEST_FAILED);
+		test_pr_err(
+			"There were %d failures during the test, TEST FAILED",
+			num_of_failures);
+	}
+	return count;
+}
+
+static ssize_t send_invalid_packed_test_read(struct file *file,
+			       char __user *buffer,
+			       size_t count,
+			       loff_t *offset)
+{
+	memset((void *)buffer, 0, count);
+
+	snprintf(buffer, count,
+		 "\nsend_invalid_packed_TEST\n"
+		 "=========\n"
+		 "Description:\n"
+		 "This test checks the following scenarios\n"
+		 "- Send an invalid header version\n"
+		 "- Send the wrong write code\n"
+		 "- Send an invalid R/W code\n"
+		 "- Send wrong start address in header\n"
+		 "- Send header with block_count smaller than actual\n"
+		 "- Send header with block_count larger than actual\n"
+		 "- Send header CMD23 packed bit set\n"
+		 "- Send CMD23 with block count over threshold\n"
+		 "- Send CMD23 with block_count equals zero\n"
+		 "- Send CMD23 packed bit unset\n"
+		 "- Send CMD23 reliable write bit set\n"
+		 "- Send CMD23 bits [16-29] set\n"
+		 "- Send CMD23 header block not in block_count\n");
+
+	if (message_repeat == 1) {
+		message_repeat = 0;
+		return strnlen(buffer, count);
+	} else {
+		return 0;
+	}
+}
+
+const struct file_operations send_invalid_packed_test_ops = {
+	.open = test_open,
+	.write = send_invalid_packed_test_write,
+	.read = send_invalid_packed_test_read,
+};
+
+/* packing_control TEST */
+static ssize_t write_packing_control_test_write(struct file *file,
+				const char __user *buf,
+				size_t count,
+				loff_t *ppos)
+{
+	int ret = 0;
+	int i = 0;
+	int number = -1;
+	int j = 0;
+	struct mmc_queue *mq = test_iosched_get_req_queue()->queuedata;
+	int max_num_requests = mq->card->ext_csd.max_packed_writes;
+	int test_successful = 1;
+
+	test_pr_info("%s: -- write_packing_control TEST --", __func__);
+
+	sscanf(buf, "%d", &number);
+
+	if (number <= 0)
+		number = 1;
+
+	test_pr_info("%s: max_num_requests = %d ", __func__,
+			max_num_requests);
+
+	memset(&mbtd->test_info, 0, sizeof(struct test_info));
+	mbtd->test_group = TEST_PACKING_CONTROL_GROUP;
+
+	if (validate_packed_commands_settings())
+		return count;
+
+	mbtd->test_info.data = mbtd;
+	mbtd->test_info.prepare_test_fn = prepare_test;
+	mbtd->test_info.check_test_result_fn = check_wr_packing_statistics;
+	mbtd->test_info.get_test_case_str_fn = get_test_case_str;
+
+	for (i = 0; i < number; ++i) {
+		test_pr_info("%s: Cycle # %d / %d", __func__, i+1, number);
+		test_pr_info("%s: ====================", __func__);
+
+		for (j = PACKING_CONTROL_MIN_TESTCASE;
+				j <= PACKING_CONTROL_MAX_TESTCASE; j++) {
+
+			test_successful = 1;
+			mbtd->test_info.testcase = j;
+			mbtd->is_random = RANDOM_TEST;
+			ret = test_iosched_start_test(&mbtd->test_info);
+			if (ret) {
+				test_successful = 0;
+				break;
+			}
+			/* Allow FS requests to be dispatched */
+			msleep(1000);
+
+			mbtd->test_info.testcase = j;
+			mbtd->is_random = NON_RANDOM_TEST;
+			ret = test_iosched_start_test(&mbtd->test_info);
+			if (ret) {
+				test_successful = 0;
+				break;
+			}
+			/* Allow FS requests to be dispatched */
+			msleep(1000);
+		}
+
+		if (!test_successful)
+			break;
+	}
+
+	test_pr_info("%s: Completed all the test cases.", __func__);
+
+	return count;
+}
+
+static ssize_t write_packing_control_test_read(struct file *file,
+			       char __user *buffer,
+			       size_t count,
+			       loff_t *offset)
+{
+	memset((void *)buffer, 0, count);
+
+	snprintf(buffer, count,
+		 "\nwrite_packing_control_test\n"
+		 "=========\n"
+		 "Description:\n"
+		 "This test checks the following scenarios\n"
+		 "- Packing expected - one over trigger\n"
+		 "- Packing expected - N over trigger\n"
+		 "- Packing expected - N over trigger followed by read\n"
+		 "- Packing expected - N over trigger followed by flush\n"
+		 "- Packing expected - threshold over trigger FB by flush\n"
+		 "- Packing not expected - less than trigger\n"
+		 "- Packing not expected - trigger requests\n"
+		 "- Packing not expected - trigger, read, trigger\n"
+		 "- Mixed state - packing -> no packing -> packing\n"
+		 "- Mixed state - no packing -> packing -> no packing\n");
+
+	if (message_repeat == 1) {
+		message_repeat = 0;
+		return strnlen(buffer, count);
+	} else {
+		return 0;
+	}
+}
+
+const struct file_operations write_packing_control_test_ops = {
+	.open = test_open,
+	.write = write_packing_control_test_write,
+	.read = write_packing_control_test_read,
+};
+
+static ssize_t write_discard_sanitize_test_write(struct file *file,
+				const char __user *buf,
+				size_t count,
+				loff_t *ppos)
+{
+	int ret = 0;
+	int i = 0;
+	int number = -1;
+
+	sscanf(buf, "%d", &number);
+	if (number <= 0)
+		number = 1;
+
+	test_pr_info("%s: -- write_discard_sanitize TEST --\n", __func__);
+
+	memset(&mbtd->test_info, 0, sizeof(struct test_info));
+
+	mbtd->test_group = TEST_GENERAL_GROUP;
+
+	mbtd->test_info.data = mbtd;
+	mbtd->test_info.prepare_test_fn = prepare_write_discard_sanitize_read;
+	mbtd->test_info.get_test_case_str_fn = get_test_case_str;
+	mbtd->test_info.timeout_msec = SANITIZE_TEST_TIMEOUT;
+
+	for (i = 0 ; i < number ; ++i) {
+		test_pr_info("%s: Cycle # %d / %d\n", __func__, i+1, number);
+		test_pr_info("%s: ===================", __func__);
+
+		mbtd->test_info.testcase = TEST_WRITE_DISCARD_SANITIZE_READ;
+		ret = test_iosched_start_test(&mbtd->test_info);
+
+		if (ret)
+			break;
+	}
+
+	return count;
+}
+
+const struct file_operations write_discard_sanitize_test_ops = {
+	.open = test_open,
+	.write = write_discard_sanitize_test_write,
+};
+
+static ssize_t bkops_test_write(struct file *file,
+				const char __user *buf,
+				size_t count,
+				loff_t *ppos)
+{
+	int ret = 0;
+	int i = 0, j;
+	int number = -1;
+
+	test_pr_info("%s: -- bkops_test TEST --", __func__);
+
+	sscanf(buf, "%d", &number);
+
+	if (number <= 0)
+		number = 1;
+
+	mbtd->test_group = TEST_BKOPS_GROUP;
+
+	memset(&mbtd->test_info, 0, sizeof(struct test_info));
+
+	mbtd->test_info.data = mbtd;
+	mbtd->test_info.prepare_test_fn = prepare_bkops;
+	mbtd->test_info.check_test_result_fn = check_bkops_result;
+	mbtd->test_info.get_test_case_str_fn = get_test_case_str;
+	mbtd->test_info.run_test_fn = run_bkops;
+	mbtd->test_info.timeout_msec = BKOPS_TEST_TIMEOUT;
+	mbtd->test_info.post_test_fn = bkops_post_test;
+
+	for (i = 0 ; i < number ; ++i) {
+		test_pr_info("%s: Cycle # %d / %d", __func__, i+1, number);
+		test_pr_info("%s: ===================", __func__);
+		for (j = BKOPS_MIN_TESTCASE ;
+				j <= BKOPS_MAX_TESTCASE ; j++) {
+			mbtd->test_info.testcase = j;
+			ret = test_iosched_start_test(&mbtd->test_info);
+			if (ret)
+				break;
+		}
+	}
+
+	test_pr_info("%s: Completed all the test cases.", __func__);
+
+	return count;
+}
+
+static ssize_t bkops_test_read(struct file *file,
+			       char __user *buffer,
+			       size_t count,
+			       loff_t *offset)
+{
+	memset((void *)buffer, 0, count);
+
+	snprintf(buffer, count,
+		 "\nbkops_test\n========================\n"
+		 "Description:\n"
+		 "This test simulates BKOPS status from card\n"
+		 "and verifies that:\n"
+		 " - Starting BKOPS delayed work, level 1\n"
+		 " - Starting BKOPS delayed work, level 1, with HPI\n"
+		 " - Cancel starting BKOPS delayed work, "
+		 " when a request is received\n"
+		 " - Starting BKOPS urgent, level 2,3\n"
+		 " - Starting BKOPS urgent with 2 requests\n");
+	return strnlen(buffer, count);
+}
+
+const struct file_operations bkops_test_ops = {
+	.open = test_open,
+	.write = bkops_test_write,
+	.read = bkops_test_read,
+};
+
+static ssize_t long_sequential_read_test_write(struct file *file,
+				const char __user *buf,
+				size_t count,
+				loff_t *ppos)
+{
+	int ret = 0;
+	int i = 0;
+	int number = -1;
+	unsigned int mtime, integer, fraction;
+
+	test_pr_info("%s: -- Long Sequential Read TEST --", __func__);
+
+	sscanf(buf, "%d", &number);
+
+	if (number <= 0)
+		number = 1;
+
+	memset(&mbtd->test_info, 0, sizeof(struct test_info));
+	mbtd->test_group = TEST_GENERAL_GROUP;
+
+	mbtd->test_info.data = mbtd;
+	mbtd->test_info.prepare_test_fn = prepare_test;
+	mbtd->test_info.get_test_case_str_fn = get_test_case_str;
+
+	for (i = 0 ; i < number ; ++i) {
+		test_pr_info("%s: Cycle # %d / %d", __func__, i+1, number);
+		test_pr_info("%s: ====================", __func__);
+
+		mbtd->test_info.testcase = TEST_LONG_SEQUENTIAL_READ;
+		mbtd->is_random = NON_RANDOM_TEST;
+		ret = test_iosched_start_test(&mbtd->test_info);
+		if (ret)
+			break;
+
+		mtime = jiffies_to_msecs(mbtd->test_info.test_duration);
+
+		test_pr_info("%s: time is %u msec, size is %u.%u MiB",
+			__func__, mtime, LONG_TEST_SIZE_INTEGER,
+			      LONG_TEST_SIZE_FRACTION);
+
+		/* we first multiply in order not to lose precision */
+		mtime *= MB_MSEC_RATIO_APPROXIMATION;
+		/* divide values to get a MiB/sec integer value with one
+		   digit of precision. Multiply by 10 for one digit precision
+		 */
+		fraction = integer = (LONG_TEST_ACTUAL_BYTE_NUM * 10) / mtime;
+		integer /= 10;
+		/* and calculate the MiB value fraction */
+		fraction -= integer * 10;
+
+		test_pr_info("%s: Throughput: %u.%u MiB/sec\n"
+			, __func__, integer, fraction);
+
+		/* Allow FS requests to be dispatched */
+		msleep(1000);
+	}
+
+	return count;
+}
+
+static ssize_t long_sequential_read_test_read(struct file *file,
+			       char __user *buffer,
+			       size_t count,
+			       loff_t *offset)
+{
+	memset((void *)buffer, 0, count);
+
+	snprintf(buffer, count,
+		 "\nlong_sequential_read_test\n"
+		 "=========\n"
+		 "Description:\n"
+		 "This test runs the following scenarios\n"
+		 "- Long Sequential Read Test: this test measures read "
+		 "throughput at the driver level by sequentially reading many "
+		 "large requests.\n");
+
+	if (message_repeat == 1) {
+		message_repeat = 0;
+		return strnlen(buffer, count);
+	} else
+		return 0;
+}
+
+const struct file_operations long_sequential_read_test_ops = {
+	.open = test_open,
+	.write = long_sequential_read_test_write,
+	.read = long_sequential_read_test_read,
+};
+
+static ssize_t long_sequential_write_test_write(struct file *file,
+				const char __user *buf,
+				size_t count,
+				loff_t *ppos)
+{
+	int ret = 0;
+	int i = 0;
+	int number = -1;
+	unsigned int mtime, integer, fraction;
+
+	test_pr_info("%s: -- Long Sequential Write TEST --", __func__);
+
+	sscanf(buf, "%d", &number);
+
+	if (number <= 0)
+		number = 1;
+
+	memset(&mbtd->test_info, 0, sizeof(struct test_info));
+	mbtd->test_group = TEST_GENERAL_GROUP;
+
+	mbtd->test_info.data = mbtd;
+	mbtd->test_info.prepare_test_fn = prepare_test;
+	mbtd->test_info.get_test_case_str_fn = get_test_case_str;
+
+	for (i = 0 ; i < number ; ++i) {
+		test_pr_info("%s: Cycle # %d / %d", __func__, i+1, number);
+		test_pr_info("%s: ====================", __func__);
+
+		mbtd->test_info.testcase = TEST_LONG_SEQUENTIAL_WRITE;
+		mbtd->is_random = NON_RANDOM_TEST;
+		ret = test_iosched_start_test(&mbtd->test_info);
+		if (ret)
+			break;
+
+		mtime = jiffies_to_msecs(mbtd->test_info.test_duration);
+
+		test_pr_info("%s: time is %u msec, size is %u.%u MiB",
+			__func__, mtime, LONG_TEST_SIZE_INTEGER,
+			      LONG_TEST_SIZE_FRACTION);
+
+		/* we first multiply in order not to lose precision */
+		mtime *= MB_MSEC_RATIO_APPROXIMATION;
+		/* divide values to get a MiB/sec integer value with one
+		   digit of precision
+		 */
+		fraction = integer = (LONG_TEST_ACTUAL_BYTE_NUM * 10) / mtime;
+		integer /= 10;
+		/* and calculate the MiB value fraction */
+		fraction -= integer * 10;
+
+		test_pr_info("%s: Throughput: %u.%u MiB/sec\n",
+			__func__, integer, fraction);
+
+		/* Allow FS requests to be dispatched */
+		msleep(1000);
+	}
+
+	return count;
+}
+
+static ssize_t long_sequential_write_test_read(struct file *file,
+			       char __user *buffer,
+			       size_t count,
+			       loff_t *offset)
+{
+	memset((void *)buffer, 0, count);
+
+	snprintf(buffer, count,
+		 "\nlong_sequential_write_test\n"
+		 "=========\n"
+		 "Description:\n"
+		 "This test runs the following scenarios\n"
+		 "- Long Sequential Write Test: this test measures write "
+		 "throughput at the driver level by sequentially writing many "
+		 "large requests\n");
+
+	if (message_repeat == 1) {
+		message_repeat = 0;
+		return strnlen(buffer, count);
+	} else
+		return 0;
+}
+
+const struct file_operations long_sequential_write_test_ops = {
+	.open = test_open,
+	.write = long_sequential_write_test_write,
+	.read = long_sequential_write_test_read,
+};
+
+
+static void mmc_block_test_debugfs_cleanup(void)
+{
+	debugfs_remove(mbtd->debug.random_test_seed);
+	debugfs_remove(mbtd->debug.send_write_packing_test);
+	debugfs_remove(mbtd->debug.err_check_test);
+	debugfs_remove(mbtd->debug.send_invalid_packed_test);
+	debugfs_remove(mbtd->debug.packing_control_test);
+	debugfs_remove(mbtd->debug.discard_sanitize_test);
+	debugfs_remove(mbtd->debug.bkops_test);
+	debugfs_remove(mbtd->debug.long_sequential_read_test);
+	debugfs_remove(mbtd->debug.long_sequential_write_test);
+}
+
+static int mmc_block_test_debugfs_init(void)
+{
+	struct dentry *utils_root, *tests_root;
+
+	utils_root = test_iosched_get_debugfs_utils_root();
+	tests_root = test_iosched_get_debugfs_tests_root();
+
+	if (!utils_root || !tests_root)
+		return -EINVAL;
+
+	mbtd->debug.random_test_seed = debugfs_create_u32(
+					"random_test_seed",
+					S_IRUGO | S_IWUGO,
+					utils_root,
+					&mbtd->random_test_seed);
+
+	if (!mbtd->debug.random_test_seed)
+		goto err_nomem;
+
+	mbtd->debug.send_write_packing_test =
+		debugfs_create_file("send_write_packing_test",
+				    S_IRUGO | S_IWUGO,
+				    tests_root,
+				    NULL,
+				    &send_write_packing_test_ops);
+
+	if (!mbtd->debug.send_write_packing_test)
+		goto err_nomem;
+
+	mbtd->debug.err_check_test =
+		debugfs_create_file("err_check_test",
+				    S_IRUGO | S_IWUGO,
+				    tests_root,
+				    NULL,
+				    &err_check_test_ops);
+
+	if (!mbtd->debug.err_check_test)
+		goto err_nomem;
+
+	mbtd->debug.send_invalid_packed_test =
+		debugfs_create_file("send_invalid_packed_test",
+				    S_IRUGO | S_IWUGO,
+				    tests_root,
+				    NULL,
+				    &send_invalid_packed_test_ops);
+
+	if (!mbtd->debug.send_invalid_packed_test)
+		goto err_nomem;
+
+	mbtd->debug.packing_control_test = debugfs_create_file(
+					"packing_control_test",
+					S_IRUGO | S_IWUGO,
+					tests_root,
+					NULL,
+					&write_packing_control_test_ops);
+
+	if (!mbtd->debug.packing_control_test)
+		goto err_nomem;
+
+	mbtd->debug.discard_sanitize_test =
+		debugfs_create_file("write_discard_sanitize_test",
+				    S_IRUGO | S_IWUGO,
+				    tests_root,
+				    NULL,
+				    &write_discard_sanitize_test_ops);
+	if (!mbtd->debug.discard_sanitize_test) {
+		mmc_block_test_debugfs_cleanup();
+		return -ENOMEM;
+	}
+
+	mbtd->debug.bkops_test =
+		debugfs_create_file("bkops_test",
+				    S_IRUGO | S_IWUGO,
+				    tests_root,
+				    NULL,
+				    &bkops_test_ops);
+
+	if (!mbtd->debug.bkops_test)
+		goto err_nomem;
+
+	mbtd->debug.long_sequential_read_test = debugfs_create_file(
+					"long_sequential_read_test",
+					S_IRUGO | S_IWUGO,
+					tests_root,
+					NULL,
+					&long_sequential_read_test_ops);
+
+	if (!mbtd->debug.long_sequential_read_test)
+		goto err_nomem;
+
+	mbtd->debug.long_sequential_write_test = debugfs_create_file(
+					"long_sequential_write_test",
+					S_IRUGO | S_IWUGO,
+					tests_root,
+					NULL,
+					&long_sequential_write_test_ops);
+
+	if (!mbtd->debug.long_sequential_write_test)
+		goto err_nomem;
+
+	return 0;
+
+err_nomem:
+	mmc_block_test_debugfs_cleanup();
+	return -ENOMEM;
+}
+
+static void mmc_block_test_probe(void)
+{
+	struct request_queue *q = test_iosched_get_req_queue();
+	struct mmc_queue *mq;
+	int max_packed_reqs;
+
+	if (!q) {
+		test_pr_err("%s: NULL request queue", __func__);
+		return;
+	}
+
+	mq = q->queuedata;
+	if (!mq) {
+		test_pr_err("%s: NULL mq", __func__);
+		return;
+	}
+
+	max_packed_reqs = mq->card->ext_csd.max_packed_writes;
+	mbtd->exp_packed_stats.packing_events =
+			kzalloc((max_packed_reqs + 1) *
+				sizeof(*mbtd->exp_packed_stats.packing_events),
+				GFP_KERNEL);
+
+	mmc_block_test_debugfs_init();
+}
+
+static void mmc_block_test_remove(void)
+{
+	mmc_block_test_debugfs_cleanup();
+}
+
+static int __init mmc_block_test_init(void)
+{
+	mbtd = kzalloc(sizeof(struct mmc_block_test_data), GFP_KERNEL);
+	if (!mbtd) {
+		test_pr_err("%s: failed to allocate mmc_block_test_data",
+			    __func__);
+		return -ENODEV;
+	}
+
+	init_waitqueue_head(&mbtd->bkops_wait_q);
+	mbtd->bdt.init_fn = mmc_block_test_probe;
+	mbtd->bdt.exit_fn = mmc_block_test_remove;
+	INIT_LIST_HEAD(&mbtd->bdt.list);
+	test_iosched_register(&mbtd->bdt);
+
+	return 0;
+}
+
+static void __exit mmc_block_test_exit(void)
+{
+	test_iosched_unregister(&mbtd->bdt);
+	kfree(mbtd);
+}
+
+module_init(mmc_block_test_init);
+module_exit(mmc_block_test_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("MMC block test");
diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c
index d818fc4..8897f18a 100644
--- a/drivers/mmc/card/queue.c
+++ b/drivers/mmc/card/queue.c
@@ -25,6 +25,13 @@
 #define MMC_QUEUE_SUSPENDED	(1 << 0)
 
 /*
+ * Based on benchmark tests the default num of requests to trigger the write
+ * packing was determined, to keep the read latency as low as possible and
+ * manage to keep the high write throughput.
+ */
+#define DEFAULT_NUM_REQS_TO_START_PACK 17
+
+/*
  * Prepare a MMC request. This just filters out odd stuff.
  */
 static int mmc_prep_request(struct request_queue *q, struct request *req)
@@ -52,6 +59,7 @@
 	struct mmc_queue *mq = d;
 	struct request_queue *q = mq->queue;
 	struct request *req;
+	struct mmc_card *card = mq->card;
 
 	current->flags |= PF_MEMALLOC;
 
@@ -67,6 +75,17 @@
 		spin_unlock_irq(q->queue_lock);
 
 		if (req || mq->mqrq_prev->req) {
+			/*
+			 * If this is the first request, BKOPs might be in
+			 * progress and needs to be stopped before issuing the
+			 * request
+			 */
+			if (card->ext_csd.bkops_en &&
+			    card->bkops_info.started_delayed_bkops) {
+				card->bkops_info.started_delayed_bkops = false;
+				mmc_stop_bkops(card);
+			}
+
 			set_current_state(TASK_RUNNING);
 			mq->issue_fn(mq, req);
 		} else {
@@ -74,6 +93,7 @@
 				set_current_state(TASK_RUNNING);
 				break;
 			}
+			mmc_start_delayed_bkops(card);
 			up(&mq->thread_sem);
 			schedule();
 			down(&mq->thread_sem);
@@ -181,14 +201,16 @@
 	if (!mq->queue)
 		return -ENOMEM;
 
+	memset(&mq->mqrq_cur, 0, sizeof(mq->mqrq_cur));
+	memset(&mq->mqrq_prev, 0, sizeof(mq->mqrq_prev));
+
 	INIT_LIST_HEAD(&mqrq_cur->packed_list);
 	INIT_LIST_HEAD(&mqrq_prev->packed_list);
 
-	memset(&mq->mqrq_cur, 0, sizeof(mq->mqrq_cur));
-	memset(&mq->mqrq_prev, 0, sizeof(mq->mqrq_prev));
 	mq->mqrq_cur = mqrq_cur;
 	mq->mqrq_prev = mqrq_prev;
 	mq->queue->queuedata = mq;
+	mq->num_wr_reqs_to_start_packing = DEFAULT_NUM_REQS_TO_START_PACK;
 
 	blk_queue_prep_rq(mq->queue, mmc_prep_request);
 	queue_flag_set_unlocked(QUEUE_FLAG_NONROT, mq->queue);
diff --git a/drivers/mmc/card/queue.h b/drivers/mmc/card/queue.h
index 5e04938..a8c104e 100644
--- a/drivers/mmc/card/queue.h
+++ b/drivers/mmc/card/queue.h
@@ -12,6 +12,17 @@
 	struct mmc_data		data;
 };
 
+enum mmc_blk_status {
+	MMC_BLK_SUCCESS = 0,
+	MMC_BLK_PARTIAL,
+	MMC_BLK_CMD_ERR,
+	MMC_BLK_RETRY,
+	MMC_BLK_ABORT,
+	MMC_BLK_DATA_ERR,
+	MMC_BLK_ECC_ERR,
+	MMC_BLK_NOMEDIUM,
+};
+
 enum mmc_packed_cmd {
 	MMC_PACKED_NONE = 0,
 	MMC_PACKED_WRITE,
@@ -45,6 +56,11 @@
 	struct mmc_queue_req	mqrq[2];
 	struct mmc_queue_req	*mqrq_cur;
 	struct mmc_queue_req	*mqrq_prev;
+	bool			wr_packing_enabled;
+	int			num_of_potential_packed_wr_reqs;
+	int			num_wr_reqs_to_start_packing;
+	int (*err_check_fn) (struct mmc_card *, struct mmc_async_req *);
+	void (*packed_test_fn) (struct request_queue *, struct mmc_queue_req *);
 };
 
 extern int mmc_init_queue(struct mmc_queue *, struct mmc_card *, spinlock_t *,
@@ -58,4 +74,6 @@
 extern void mmc_queue_bounce_pre(struct mmc_queue_req *);
 extern void mmc_queue_bounce_post(struct mmc_queue_req *);
 
+extern void print_mmc_packing_stats(struct mmc_card *card);
+
 #endif
diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c
index 33f7d29..b24620b 100644
--- a/drivers/mmc/core/bus.c
+++ b/drivers/mmc/core/bus.c
@@ -254,6 +254,9 @@
 	card->dev.release = mmc_release_card;
 	card->dev.type = type;
 
+	spin_lock_init(&card->bkops_info.bkops_stats.lock);
+	spin_lock_init(&card->wr_pack_stats.lock);
+
 	return card;
 }
 
@@ -356,6 +359,8 @@
 		device_del(&card->dev);
 	}
 
+	kfree(card->wr_pack_stats.packing_events);
+
 	put_device(&card->dev);
 }
 
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index 9b316bb..48516b6 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -77,6 +77,30 @@
 	removable,
 	"MMC/SD cards are removable and may be removed during suspend");
 
+#define MMC_UPDATE_BKOPS_STATS_HPI(stats)	\
+	do {					\
+		spin_lock(&stats.lock);		\
+		if (stats.enabled)		\
+			stats.hpi++;		\
+		spin_unlock(&stats.lock);	\
+	} while (0);
+#define MMC_UPDATE_BKOPS_STATS_SUSPEND(stats)	\
+	do {					\
+		spin_lock(&stats.lock);		\
+		if (stats.enabled)		\
+			stats.suspend++;	\
+		spin_unlock(&stats.lock);	\
+	} while (0);
+#define MMC_UPDATE_STATS_BKOPS_SEVERITY_LEVEL(stats, level)		\
+	do {								\
+		if (level <= 0 || level > BKOPS_NUM_OF_SEVERITY_LEVELS)	\
+			break;						\
+		spin_lock(&stats.lock);					\
+		if (stats.enabled)					\
+			stats.bkops_level[level-1]++;			\
+		spin_unlock(&stats.lock);				\
+	} while (0);
+
 /*
  * Internal function. Schedule delayed work in the MMC work queue.
  */
@@ -279,10 +303,66 @@
 	host->ops->request(host, mrq);
 }
 
+void mmc_blk_init_bkops_statistics(struct mmc_card *card)
+{
+	int i;
+	struct mmc_bkops_stats *bkops_stats;
+
+	if (!card)
+		return;
+
+	bkops_stats = &card->bkops_info.bkops_stats;
+
+	spin_lock(&bkops_stats->lock);
+
+	for (i = 0 ; i < BKOPS_NUM_OF_SEVERITY_LEVELS ; ++i)
+		bkops_stats->bkops_level[i] = 0;
+
+	bkops_stats->suspend = 0;
+	bkops_stats->hpi = 0;
+	bkops_stats->enabled = true;
+
+	spin_unlock(&bkops_stats->lock);
+}
+EXPORT_SYMBOL(mmc_blk_init_bkops_statistics);
+
+/**
+ * mmc_start_delayed_bkops() - Start a delayed work to check for
+ *      the need of non urgent BKOPS
+ *
+ * @card: MMC card to start BKOPS on
+ */
+void mmc_start_delayed_bkops(struct mmc_card *card)
+{
+	if (!card || !card->ext_csd.bkops_en || mmc_card_doing_bkops(card))
+		return;
+
+	if (card->bkops_info.sectors_changed <
+	    card->bkops_info.min_sectors_to_queue_delayed_work)
+		return;
+
+	pr_debug("%s: %s: queueing delayed_bkops_work\n",
+		 mmc_hostname(card->host), __func__);
+
+	card->bkops_info.sectors_changed = 0;
+
+	/*
+	 * cancel_delayed_bkops_work will prevent a race condition between
+	 * fetching a request by the mmcqd and the delayed work, in case
+	 * it was removed from the queue work but not started yet
+	 */
+	card->bkops_info.cancel_delayed_work = false;
+	card->bkops_info.started_delayed_bkops = true;
+	queue_delayed_work(system_nrt_wq, &card->bkops_info.dw,
+			   msecs_to_jiffies(
+				   card->bkops_info.delay_ms));
+}
+EXPORT_SYMBOL(mmc_start_delayed_bkops);
+
 /**
  *	mmc_start_bkops - start BKOPS for supported cards
  *	@card: MMC card to start BKOPS
- *	@form_exception: A flag to indicate if this function was
+ *	@from_exception: A flag to indicate if this function was
  *			 called due to an exception raised by the card
  *
  *	Start background operations whenever requested.
@@ -296,25 +376,50 @@
 	bool use_busy_signal;
 
 	BUG_ON(!card);
-
-	if (!card->ext_csd.bkops_en || mmc_card_doing_bkops(card))
-		return;
-
-	err = mmc_read_bkops_status(card);
-	if (err) {
-		pr_err("%s: Failed to read bkops status: %d\n",
-		       mmc_hostname(card->host), err);
-		return;
-	}
-
-	if (!card->ext_csd.raw_bkops_status)
-		return;
-
-	if (card->ext_csd.raw_bkops_status < EXT_CSD_BKOPS_LEVEL_2 &&
-	    from_exception)
+	if (!card->ext_csd.bkops_en)
 		return;
 
 	mmc_claim_host(card->host);
+
+	if ((card->bkops_info.cancel_delayed_work) && !from_exception) {
+		pr_debug("%s: %s: cancel_delayed_work was set, exit\n",
+			 mmc_hostname(card->host), __func__);
+		card->bkops_info.cancel_delayed_work = false;
+		goto out;
+	}
+
+	if (mmc_card_doing_bkops(card)) {
+		pr_debug("%s: %s: already doing bkops, exit\n",
+			 mmc_hostname(card->host), __func__);
+		goto out;
+	}
+
+	err = mmc_read_bkops_status(card);
+	if (err) {
+		pr_err("%s: %s: Failed to read bkops status: %d\n",
+		       mmc_hostname(card->host), __func__, err);
+		goto out;
+	}
+
+	if (!card->ext_csd.raw_bkops_status)
+		goto out;
+
+	pr_info("%s: %s: card->ext_csd.raw_bkops_status = 0x%x\n",
+		mmc_hostname(card->host), __func__,
+		card->ext_csd.raw_bkops_status);
+
+	/*
+	 * If the function was called due to exception but there is no need
+	 * for urgent BKOPS, BKOPs will be performed by the delayed BKOPs
+	 * work, before going to suspend
+	 */
+	if (card->ext_csd.raw_bkops_status < EXT_CSD_BKOPS_LEVEL_2 &&
+	    from_exception) {
+		pr_debug("%s: %s: Level 1 from exception, exit",
+			 mmc_hostname(card->host), __func__);
+		goto out;
+	}
+
 	if (card->ext_csd.raw_bkops_status >= EXT_CSD_BKOPS_LEVEL_2) {
 		timeout = MMC_BKOPS_MAX_TIMEOUT;
 		use_busy_signal = true;
@@ -326,23 +431,120 @@
 	err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
 			EXT_CSD_BKOPS_START, 1, timeout, use_busy_signal);
 	if (err) {
-		pr_warn("%s: Error %d starting bkops\n",
-			mmc_hostname(card->host), err);
+		pr_warn("%s: %s: Error %d when starting bkops\n",
+			mmc_hostname(card->host), __func__, err);
 		goto out;
 	}
+	MMC_UPDATE_STATS_BKOPS_SEVERITY_LEVEL(card->bkops_info.bkops_stats,
+					card->ext_csd.raw_bkops_status);
 
 	/*
 	 * For urgent bkops status (LEVEL_2 and more)
 	 * bkops executed synchronously, otherwise
 	 * the operation is in progress
 	 */
-	if (!use_busy_signal)
+	if (!use_busy_signal) {
 		mmc_card_set_doing_bkops(card);
+		pr_debug("%s: %s: starting the polling thread\n",
+			 mmc_hostname(card->host), __func__);
+		queue_work(system_nrt_wq,
+			   &card->bkops_info.poll_for_completion);
+	}
+
 out:
 	mmc_release_host(card->host);
 }
 EXPORT_SYMBOL(mmc_start_bkops);
 
+/**
+ * mmc_bkops_completion_polling() - Poll on the card status to
+ * wait for the non-blocking BKOPS completion
+ * @work:	The completion polling work
+ *
+ * The on-going reading of the card status will prevent the card
+ * from getting into suspend while it is in the middle of
+ * performing BKOPS.
+ * Since the non blocking BKOPS can be interrupted by a fetched
+ * request we also check IF mmc_card_doing_bkops in each
+ * iteration.
+ */
+void mmc_bkops_completion_polling(struct work_struct *work)
+{
+	struct mmc_card *card = container_of(work, struct mmc_card,
+			bkops_info.poll_for_completion);
+	unsigned long timeout_jiffies = jiffies +
+		msecs_to_jiffies(BKOPS_COMPLETION_POLLING_TIMEOUT_MS);
+	u32 status;
+	int err;
+
+	/*
+	 * Wait for the BKOPs to complete. Keep reading the status to prevent
+	 * the host from getting into suspend
+	 */
+	do {
+		mmc_claim_host(card->host);
+
+		if (!mmc_card_doing_bkops(card))
+			goto out;
+
+		err = mmc_send_status(card, &status);
+		if (err) {
+			pr_err("%s: error %d requesting status\n",
+			       mmc_hostname(card->host), err);
+			goto out;
+		}
+
+		/*
+		 * Some cards mishandle the status bits, so make sure to check
+		 * both the busy indication and the card state.
+		 */
+		if ((status & R1_READY_FOR_DATA) &&
+		    (R1_CURRENT_STATE(status) != R1_STATE_PRG)) {
+			pr_debug("%s: %s: completed BKOPs, exit polling\n",
+				 mmc_hostname(card->host), __func__);
+			mmc_card_clr_doing_bkops(card);
+			card->bkops_info.started_delayed_bkops = false;
+			goto out;
+		}
+
+		mmc_release_host(card->host);
+
+		/*
+		 * Sleep before checking the card status again to allow the
+		 * card to complete the BKOPs operation
+		 */
+		msleep(BKOPS_COMPLETION_POLLING_INTERVAL_MS);
+	} while (time_before(jiffies, timeout_jiffies));
+
+	pr_err("%s: %s: exit polling due to timeout\n",
+	       mmc_hostname(card->host), __func__);
+
+	return;
+out:
+	mmc_release_host(card->host);
+}
+
+/**
+ * mmc_start_idle_time_bkops() - check if a non urgent BKOPS is
+ * needed
+ * @work:	The idle time BKOPS work
+ */
+void mmc_start_idle_time_bkops(struct work_struct *work)
+{
+	struct mmc_card *card = container_of(work, struct mmc_card,
+			bkops_info.dw.work);
+
+	/*
+	 * Prevent a race condition between mmc_stop_bkops and the delayed
+	 * BKOPS work in case the delayed work is executed on another CPU
+	 */
+	if (card->bkops_info.cancel_delayed_work)
+		return;
+
+	mmc_start_bkops(card, false);
+}
+EXPORT_SYMBOL(mmc_start_idle_time_bkops);
+
 static void mmc_wait_done(struct mmc_request *mrq)
 {
 	complete(&mrq->completion);
@@ -458,8 +660,11 @@
 		if (host->card && mmc_card_mmc(host->card) &&
 		    ((mmc_resp_type(host->areq->mrq->cmd) == MMC_RSP_R1) ||
 		     (mmc_resp_type(host->areq->mrq->cmd) == MMC_RSP_R1B)) &&
-		    (host->areq->mrq->cmd->resp[0] & R1_EXCEPTION_EVENT))
+		    (host->areq->mrq->cmd->resp[0] & R1_EXCEPTION_EVENT)) {
 			mmc_start_bkops(host->card, true);
+			pr_debug("%s: %s: completed BKOPs due to exception",
+				 mmc_hostname(host), __func__);
+		}
 	}
 
 	if (!err && areq)
@@ -470,7 +675,7 @@
 
 	/* Cancel a prepared request if it was not started. */
 	if ((err || start_err) && areq)
-			mmc_post_req(host, areq->mrq, -EINVAL);
+		mmc_post_req(host, areq->mrq, -EINVAL);
 
 	if (err)
 		host->areq = NULL;
@@ -599,6 +804,19 @@
 	int err = 0;
 
 	BUG_ON(!card);
+
+	mmc_claim_host(card->host);
+
+	/*
+	 * Notify the delayed work to be cancelled, in case it was already
+	 * removed from the queue, but was not started yet
+	 */
+	card->bkops_info.cancel_delayed_work = true;
+	if (delayed_work_pending(&card->bkops_info.dw))
+		cancel_delayed_work_sync(&card->bkops_info.dw);
+	if (!mmc_card_doing_bkops(card))
+		goto out;
+
 	err = mmc_interrupt_hpi(card);
 
 	/*
@@ -610,6 +828,10 @@
 		err = 0;
 	}
 
+	MMC_UPDATE_BKOPS_STATS_HPI(card->bkops_info.bkops_stats);
+
+out:
+	mmc_release_host(card->host);
 	return err;
 }
 EXPORT_SYMBOL(mmc_stop_bkops);
@@ -629,6 +851,12 @@
 		return -ENOMEM;
 	}
 
+	if (card->bkops_info.bkops_stats.ignore_card_bkops_status) {
+		pr_debug("%s: skipping read raw_bkops_status in unittest mode",
+			 __func__);
+		return 0;
+	}
+
 	mmc_claim_host(card->host);
 	err = mmc_send_ext_csd(card, ext_csd);
 	mmc_release_host(card->host);
@@ -1273,6 +1501,7 @@
 	mmc_set_ios(host);
 	mmc_host_clk_release(host);
 }
+
 /*
  * Apply power to the MMC stack.  This is a two-stage process.
  * First, we enable power to the card without the clock running.
@@ -1335,7 +1564,6 @@
 	host->ios.clock = 0;
 	host->ios.vdd = 0;
 
-
 	/*
 	 * Reset ocr mask to be the highest possible voltage supported for
 	 * this mmc host. This value will be used at next power up.
@@ -1865,15 +2093,6 @@
 }
 EXPORT_SYMBOL(mmc_can_secure_erase_trim);
 
-int mmc_can_poweroff_notify(const struct mmc_card *card)
-{
-	return card &&
-		mmc_card_mmc(card) &&
-		card->host->bus_ops->poweroff_notify &&
-		(card->poweroff_notify_state == MMC_POWERED_ON);
-}
-EXPORT_SYMBOL(mmc_can_poweroff_notify);
-
 int mmc_erase_group_aligned(struct mmc_card *card, unsigned int from,
 			    unsigned int nr)
 {
@@ -2266,15 +2485,6 @@
 
 	mmc_bus_get(host);
 	if (host->bus_ops && !host->bus_dead) {
-		mmc_claim_host(host);
-		if (mmc_can_poweroff_notify(host->card)) {
-			int err = host->bus_ops->poweroff_notify(host,
-						MMC_PW_OFF_NOTIFY_LONG);
-			if (err)
-				pr_info("%s: error [%d] in poweroff notify\n",
-					mmc_hostname(host), err);
-		}
-		mmc_release_host(host);
 		/* Calling bus_ops->remove() with a claimed host can deadlock */
 		if (host->bus_ops->remove)
 			host->bus_ops->remove(host);
@@ -2310,15 +2520,6 @@
 
 	if (host->bus_ops->power_save)
 		ret = host->bus_ops->power_save(host);
-	mmc_claim_host(host);
-	if (mmc_can_poweroff_notify(host->card)) {
-		int err = host->bus_ops->poweroff_notify(host,
-					MMC_PW_OFF_NOTIFY_SHORT);
-		if (err)
-			pr_info("%s: error [%d] in poweroff notify\n",
-				mmc_hostname(host), err);
-	}
-	mmc_release_host(host);
 
 	mmc_bus_put(host);
 
@@ -2361,11 +2562,8 @@
 
 	mmc_bus_get(host);
 
-	if (host->bus_ops && !host->bus_dead && host->bus_ops->awake) {
+	if (host->bus_ops && !host->bus_dead && host->bus_ops->awake)
 		err = host->bus_ops->awake(host);
-		if (!err)
-			mmc_card_clr_sleep(host->card);
-	}
 
 	mmc_bus_put(host);
 
@@ -2382,11 +2580,8 @@
 
 	mmc_bus_get(host);
 
-	if (host->bus_ops && !host->bus_dead && host->bus_ops->sleep) {
+	if (host->bus_ops && !host->bus_dead && host->bus_ops->sleep)
 		err = host->bus_ops->sleep(host);
-		if (!err)
-			mmc_card_set_sleep(host->card);
-	}
 
 	mmc_bus_put(host);
 
@@ -2444,7 +2639,9 @@
 			mmc_card_is_removable(host))
 		return err;
 
-	mmc_claim_host(host);
+	if (!mmc_try_claim_host(host))
+		return -EBUSY;
+
 	if (card && mmc_card_mmc(card) &&
 			(card->ext_csd.cache_size > 0)) {
 		enable = !!enable;
@@ -2514,6 +2711,8 @@
 						goto stop_bkops_err;
 				}
 				err = host->bus_ops->suspend(host);
+				MMC_UPDATE_BKOPS_STATS_SUSPEND(host->
+						card->bkops_info.bkops_stats);
 			}
 			if (!(host->card && mmc_card_sdio(host->card)))
 				mmc_release_host(host);
@@ -2615,15 +2814,13 @@
 	switch (mode) {
 	case PM_HIBERNATION_PREPARE:
 	case PM_SUSPEND_PREPARE:
-		if (host->card && mmc_card_mmc(host->card) &&
-		    mmc_card_doing_bkops(host->card)) {
+		if (host->card && mmc_card_mmc(host->card)) {
 			err = mmc_stop_bkops(host->card);
 			if (err) {
 				pr_err("%s: didn't stop bkops\n",
 					mmc_hostname(host));
 				return err;
 			}
-			mmc_card_clr_doing_bkops(host->card);
 		}
 
 		spin_lock_irqsave(&host->lock, flags);
@@ -2638,15 +2835,6 @@
 
 		if (!host->bus_ops || host->bus_ops->suspend)
 			break;
-		mmc_claim_host(host);
-		if (mmc_can_poweroff_notify(host->card)) {
-			int err = host->bus_ops->poweroff_notify(host,
-						MMC_PW_OFF_NOTIFY_SHORT);
-			if (err)
-				pr_info("%s: error [%d] in poweroff notify\n",
-					mmc_hostname(host), err);
-		}
-		mmc_release_host(host);
 
 		/* Calling bus_ops->remove() with a claimed host can deadlock */
 		if (host->bus_ops->remove)
diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h
index 22f6043..85d2737 100644
--- a/drivers/mmc/core/core.h
+++ b/drivers/mmc/core/core.h
@@ -25,7 +25,6 @@
 	int (*power_save)(struct mmc_host *);
 	int (*power_restore)(struct mmc_host *);
 	int (*alive)(struct mmc_host *);
-	int (*poweroff_notify)(struct mmc_host *, int notify);
 };
 
 void mmc_attach_bus(struct mmc_host *host, const struct mmc_bus_ops *ops);
diff --git a/drivers/mmc/core/debugfs.c b/drivers/mmc/core/debugfs.c
index 9ab5b17..ddb562e 100644
--- a/drivers/mmc/core/debugfs.c
+++ b/drivers/mmc/core/debugfs.c
@@ -318,6 +318,279 @@
 	.llseek		= default_llseek,
 };
 
+static int mmc_wr_pack_stats_open(struct inode *inode, struct file *filp)
+{
+	struct mmc_card *card = inode->i_private;
+
+	filp->private_data = card;
+	card->wr_pack_stats.print_in_read = 1;
+	return 0;
+}
+
+#define TEMP_BUF_SIZE 256
+static ssize_t mmc_wr_pack_stats_read(struct file *filp, char __user *ubuf,
+				size_t cnt, loff_t *ppos)
+{
+	struct mmc_card *card = filp->private_data;
+	struct mmc_wr_pack_stats *pack_stats;
+	int i;
+	int max_num_of_packed_reqs = 0;
+	char *temp_buf;
+
+	if (!card)
+		return cnt;
+
+	if (!card->wr_pack_stats.print_in_read)
+		return 0;
+
+	if (!card->wr_pack_stats.enabled) {
+		pr_info("%s: write packing statistics are disabled\n",
+			 mmc_hostname(card->host));
+		goto exit;
+	}
+
+	pack_stats = &card->wr_pack_stats;
+
+	if (!pack_stats->packing_events) {
+		pr_info("%s: NULL packing_events\n", mmc_hostname(card->host));
+		goto exit;
+	}
+
+	max_num_of_packed_reqs = card->ext_csd.max_packed_writes;
+
+	temp_buf = kmalloc(TEMP_BUF_SIZE, GFP_KERNEL);
+	if (!temp_buf)
+		goto exit;
+
+	spin_lock(&pack_stats->lock);
+
+	snprintf(temp_buf, TEMP_BUF_SIZE, "%s: write packing statistics:\n",
+		mmc_hostname(card->host));
+	strlcat(ubuf, temp_buf, cnt);
+
+	for (i = 1 ; i <= max_num_of_packed_reqs ; ++i) {
+		if (pack_stats->packing_events[i]) {
+			snprintf(temp_buf, TEMP_BUF_SIZE,
+				 "%s: Packed %d reqs - %d times\n",
+				mmc_hostname(card->host), i,
+				pack_stats->packing_events[i]);
+			strlcat(ubuf, temp_buf, cnt);
+		}
+	}
+
+	snprintf(temp_buf, TEMP_BUF_SIZE,
+		 "%s: stopped packing due to the following reasons:\n",
+		 mmc_hostname(card->host));
+	strlcat(ubuf, temp_buf, cnt);
+
+	if (pack_stats->pack_stop_reason[EXCEEDS_SEGMENTS]) {
+		snprintf(temp_buf, TEMP_BUF_SIZE,
+			 "%s: %d times: exceed max num of segments\n",
+			 mmc_hostname(card->host),
+			 pack_stats->pack_stop_reason[EXCEEDS_SEGMENTS]);
+		strlcat(ubuf, temp_buf, cnt);
+	}
+	if (pack_stats->pack_stop_reason[EXCEEDS_SECTORS]) {
+		snprintf(temp_buf, TEMP_BUF_SIZE,
+			 "%s: %d times: exceed max num of sectors\n",
+			mmc_hostname(card->host),
+			pack_stats->pack_stop_reason[EXCEEDS_SECTORS]);
+		strlcat(ubuf, temp_buf, cnt);
+	}
+	if (pack_stats->pack_stop_reason[WRONG_DATA_DIR]) {
+		snprintf(temp_buf, TEMP_BUF_SIZE,
+			 "%s: %d times: wrong data direction\n",
+			mmc_hostname(card->host),
+			pack_stats->pack_stop_reason[WRONG_DATA_DIR]);
+		strlcat(ubuf, temp_buf, cnt);
+	}
+	if (pack_stats->pack_stop_reason[FLUSH_OR_DISCARD]) {
+		snprintf(temp_buf, TEMP_BUF_SIZE,
+			 "%s: %d times: flush or discard\n",
+			mmc_hostname(card->host),
+			pack_stats->pack_stop_reason[FLUSH_OR_DISCARD]);
+		strlcat(ubuf, temp_buf, cnt);
+	}
+	if (pack_stats->pack_stop_reason[EMPTY_QUEUE]) {
+		snprintf(temp_buf, TEMP_BUF_SIZE,
+			 "%s: %d times: empty queue\n",
+			mmc_hostname(card->host),
+			pack_stats->pack_stop_reason[EMPTY_QUEUE]);
+		strlcat(ubuf, temp_buf, cnt);
+	}
+	if (pack_stats->pack_stop_reason[REL_WRITE]) {
+		snprintf(temp_buf, TEMP_BUF_SIZE,
+			 "%s: %d times: rel write\n",
+			mmc_hostname(card->host),
+			pack_stats->pack_stop_reason[REL_WRITE]);
+		strlcat(ubuf, temp_buf, cnt);
+	}
+	if (pack_stats->pack_stop_reason[THRESHOLD]) {
+		snprintf(temp_buf, TEMP_BUF_SIZE,
+			 "%s: %d times: Threshold\n",
+			mmc_hostname(card->host),
+			pack_stats->pack_stop_reason[THRESHOLD]);
+		strlcat(ubuf, temp_buf, cnt);
+	}
+
+	if (pack_stats->pack_stop_reason[LARGE_SEC_ALIGN]) {
+		snprintf(temp_buf, TEMP_BUF_SIZE,
+			 "%s: %d times: Large sector alignment\n",
+			mmc_hostname(card->host),
+			pack_stats->pack_stop_reason[LARGE_SEC_ALIGN]);
+		strlcat(ubuf, temp_buf, cnt);
+	}
+
+	spin_unlock(&pack_stats->lock);
+
+	kfree(temp_buf);
+
+	pr_info("%s", ubuf);
+
+exit:
+	if (card->wr_pack_stats.print_in_read == 1) {
+		card->wr_pack_stats.print_in_read = 0;
+		return strnlen(ubuf, cnt);
+	}
+
+	return 0;
+}
+
+static ssize_t mmc_wr_pack_stats_write(struct file *filp,
+				       const char __user *ubuf, size_t cnt,
+				       loff_t *ppos)
+{
+	struct mmc_card *card = filp->private_data;
+	int value;
+
+	if (!card)
+		return cnt;
+
+	sscanf(ubuf, "%d", &value);
+	if (value) {
+		mmc_blk_init_packed_statistics(card);
+	} else {
+		spin_lock(&card->wr_pack_stats.lock);
+		card->wr_pack_stats.enabled = false;
+		spin_unlock(&card->wr_pack_stats.lock);
+	}
+
+	return cnt;
+}
+
+static const struct file_operations mmc_dbg_wr_pack_stats_fops = {
+	.open		= mmc_wr_pack_stats_open,
+	.read		= mmc_wr_pack_stats_read,
+	.write		= mmc_wr_pack_stats_write,
+};
+
+static int mmc_bkops_stats_open(struct inode *inode, struct file *filp)
+{
+	struct mmc_card *card = inode->i_private;
+
+	filp->private_data = card;
+
+	card->bkops_info.bkops_stats.print_stats = 1;
+	return 0;
+}
+
+static ssize_t mmc_bkops_stats_read(struct file *filp, char __user *ubuf,
+				     size_t cnt, loff_t *ppos)
+{
+	struct mmc_card *card = filp->private_data;
+	struct mmc_bkops_stats *bkops_stats;
+	int i;
+	char *temp_buf;
+
+	if (!card)
+		return cnt;
+
+	bkops_stats = &card->bkops_info.bkops_stats;
+
+	if (!bkops_stats->print_stats)
+		return 0;
+
+	if (!bkops_stats->enabled) {
+		pr_info("%s: bkops statistics are disabled\n",
+			 mmc_hostname(card->host));
+		goto exit;
+	}
+
+	temp_buf = kmalloc(TEMP_BUF_SIZE, GFP_KERNEL);
+	if (!temp_buf)
+		goto exit;
+
+	spin_lock(&bkops_stats->lock);
+
+	memset(ubuf, 0, cnt);
+
+	snprintf(temp_buf, TEMP_BUF_SIZE, "%s: bkops statistics:\n",
+		mmc_hostname(card->host));
+	strlcat(ubuf, temp_buf, cnt);
+
+	for (i = 0 ; i < BKOPS_NUM_OF_SEVERITY_LEVELS ; ++i) {
+		snprintf(temp_buf, TEMP_BUF_SIZE,
+			 "%s: BKOPS: due to level %d: %u\n",
+		 mmc_hostname(card->host), i, bkops_stats->bkops_level[i]);
+		strlcat(ubuf, temp_buf, cnt);
+	}
+
+	snprintf(temp_buf, TEMP_BUF_SIZE,
+		 "%s: BKOPS: stopped due to HPI: %u\n",
+		 mmc_hostname(card->host), bkops_stats->hpi);
+	strlcat(ubuf, temp_buf, cnt);
+
+	snprintf(temp_buf, TEMP_BUF_SIZE,
+		 "%s: BKOPS: how many time host was suspended: %u\n",
+		 mmc_hostname(card->host), bkops_stats->suspend);
+	strlcat(ubuf, temp_buf, cnt);
+
+	spin_unlock(&bkops_stats->lock);
+
+	kfree(temp_buf);
+
+	pr_info("%s", ubuf);
+
+exit:
+	if (bkops_stats->print_stats == 1) {
+		bkops_stats->print_stats = 0;
+		return strnlen(ubuf, cnt);
+	}
+
+	return 0;
+}
+
+static ssize_t mmc_bkops_stats_write(struct file *filp,
+				      const char __user *ubuf, size_t cnt,
+				      loff_t *ppos)
+{
+	struct mmc_card *card = filp->private_data;
+	int value;
+	struct mmc_bkops_stats *bkops_stats;
+
+	if (!card)
+		return cnt;
+
+	bkops_stats = &card->bkops_info.bkops_stats;
+
+	sscanf(ubuf, "%d", &value);
+	if (value) {
+		mmc_blk_init_bkops_statistics(card);
+	} else {
+		spin_lock(&bkops_stats->lock);
+		bkops_stats->enabled = false;
+		spin_unlock(&bkops_stats->lock);
+	}
+
+	return cnt;
+}
+
+static const struct file_operations mmc_dbg_bkops_stats_fops = {
+	.open		= mmc_bkops_stats_open,
+	.read		= mmc_bkops_stats_read,
+	.write		= mmc_bkops_stats_write,
+};
+
 void mmc_add_card_debugfs(struct mmc_card *card)
 {
 	struct mmc_host	*host = card->host;
@@ -350,6 +623,18 @@
 					&mmc_dbg_ext_csd_fops))
 			goto err;
 
+	if (mmc_card_mmc(card) && (card->ext_csd.rev >= 6) &&
+	    (card->host->caps2 & MMC_CAP2_PACKED_WR))
+		if (!debugfs_create_file("wr_pack_stats", S_IRUSR, root, card,
+					 &mmc_dbg_wr_pack_stats_fops))
+			goto err;
+
+	if (mmc_card_mmc(card) && (card->ext_csd.rev >= 6) &&
+	    card->ext_csd.bkops_en)
+		if (!debugfs_create_file("bkops_stats", S_IRUSR, root, card,
+					 &mmc_dbg_bkops_stats_fops))
+			goto err;
+
 	return;
 
 err:
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
index e2172c5..47fd9b9 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -1028,7 +1028,7 @@
 		 * so check for success and update the flag
 		 */
 		if (!err)
-			card->poweroff_notify_state = MMC_POWERED_ON;
+			card->ext_csd.power_off_notification = EXT_CSD_POWER_ON;
 	}
 
 	/*
@@ -1297,6 +1297,48 @@
 		} else {
 			card->ext_csd.packed_event_en = 1;
 		}
+
+	}
+
+	if (!oldcard) {
+		if ((host->caps2 & MMC_CAP2_PACKED_CMD) &&
+		    (card->ext_csd.max_packed_writes > 0)) {
+			/*
+			 * We would like to keep the statistics in an index
+			 * that equals the num of packed requests
+			 * (1 to max_packed_writes)
+			 */
+			card->wr_pack_stats.packing_events = kzalloc(
+				(card->ext_csd.max_packed_writes + 1) *
+				sizeof(*card->wr_pack_stats.packing_events),
+				GFP_KERNEL);
+			if (!card->wr_pack_stats.packing_events)
+				goto free_card;
+		}
+
+		if (card->ext_csd.bkops_en) {
+			INIT_DELAYED_WORK(&card->bkops_info.dw,
+					  mmc_start_idle_time_bkops);
+			INIT_WORK(&card->bkops_info.poll_for_completion,
+				  mmc_bkops_completion_polling);
+
+			/*
+			 * Calculate the time to start the BKOPs checking.
+			 * The idle time of the host controller should be taken
+			 * into account in order to prevent a race condition
+			 * before starting BKOPs and going into suspend.
+			 * If the host controller didn't set its idle time,
+			 * a default value is used.
+			 */
+			card->bkops_info.delay_ms = MMC_IDLE_BKOPS_TIME_MS;
+			if (card->bkops_info.host_suspend_tout_ms)
+				card->bkops_info.delay_ms = min(
+					card->bkops_info.delay_ms,
+				      card->bkops_info.host_suspend_tout_ms/2);
+
+			card->bkops_info.min_sectors_to_queue_delayed_work =
+				BKOPS_MIN_SECTORS_TO_QUEUE_DELAYED_WORK;
+		}
 	}
 
 	if (!oldcard)
@@ -1314,40 +1356,35 @@
 	return err;
 }
 
-static int mmc_poweroff_notify(struct mmc_host *host, int notify)
+static int mmc_can_poweroff_notify(const struct mmc_card *card)
 {
-	struct mmc_card *card;
-	unsigned int timeout;
-	unsigned int notify_type = EXT_CSD_NO_POWER_NOTIFICATION;
+	return card &&
+		mmc_card_mmc(card) &&
+		(card->ext_csd.power_off_notification == EXT_CSD_POWER_ON);
+}
+
+static int mmc_poweroff_notify(struct mmc_card *card, unsigned int notify_type)
+{
+	unsigned int timeout = card->ext_csd.generic_cmd6_time;
 	int err;
 
-	card = host->card;
-
-	if (notify == MMC_PW_OFF_NOTIFY_SHORT) {
-		notify_type = EXT_CSD_POWER_OFF_SHORT;
-		timeout = card->ext_csd.generic_cmd6_time;
-	} else if (notify == MMC_PW_OFF_NOTIFY_LONG) {
-		notify_type = EXT_CSD_POWER_OFF_LONG;
+	/* Use EXT_CSD_POWER_OFF_SHORT as default notification type. */
+	if (notify_type == EXT_CSD_POWER_OFF_LONG)
 		timeout = card->ext_csd.power_off_longtime;
-	} else {
-		pr_info("%s: mmc_poweroff_notify called "
-		        "with notify type %d\n", mmc_hostname(host), notify);
-		return -EINVAL;
-	}
 
 	err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
 			 EXT_CSD_POWER_OFF_NOTIFICATION,
 			 notify_type, timeout);
-
 	if (err)
-		pr_err("%s: Device failed to respond within %d "
-		       "poweroff timeout.\n", mmc_hostname(host), timeout);
-	else
-		card->poweroff_notify_state =
-					MMC_NO_POWER_NOTIFICATION;
+		pr_err("%s: Power Off Notification timed out, %u\n",
+		       mmc_hostname(card->host), timeout);
+
+	/* Disable the power off notification after the switch operation. */
+	card->ext_csd.power_off_notification = EXT_CSD_NO_POWER_NOTIFICATION;
 
 	return err;
 }
+
 /*
  * Host is being removed. Free up the current card.
  */
@@ -1411,26 +1448,13 @@
 	BUG_ON(!host->card);
 
 	mmc_claim_host(host);
-	if (mmc_can_poweroff_notify(host->card) &&
-		(host->caps2 & MMC_CAP2_POWER_OFF_VCCQ_DURING_SUSPEND)) {
-		err = mmc_poweroff_notify(host, MMC_PW_OFF_NOTIFY_SHORT);
-	} else {
-		if (mmc_card_can_sleep(host))
-			/*
-			 * If sleep command has error it doesn't mean host
-			 * cannot suspend, but a deeper low power state
-			 * transition for the card has failed. Ignore
-			 * sleep errors so that the suspend is not aborted.
-			 * In error case, mmc_resume() takes care of
-			 * complete intialization of the card.
-			 */
-			mmc_card_sleep(host);
-		else if (!mmc_host_is_spi(host))
-			mmc_deselect_cards(host);
-	}
-	if (!err)
-		host->card->state &=
-			~(MMC_STATE_HIGHSPEED | MMC_STATE_HIGHSPEED_200);
+	if (mmc_can_poweroff_notify(host->card))
+		err = mmc_poweroff_notify(host->card, EXT_CSD_POWER_OFF_SHORT);
+	else if (mmc_card_can_sleep(host))
+		err = mmc_card_sleep(host);
+	else if (!mmc_host_is_spi(host))
+		mmc_deselect_cards(host);
+	host->card->state &= ~(MMC_STATE_HIGHSPEED | MMC_STATE_HIGHSPEED_200);
 	mmc_release_host(host);
 
 	return err;
@@ -1461,7 +1485,6 @@
 	int ret;
 
 	host->card->state &= ~(MMC_STATE_HIGHSPEED | MMC_STATE_HIGHSPEED_200);
-	mmc_card_clr_sleep(host->card);
 	mmc_claim_host(host);
 	ret = mmc_init_card(host, host->ocr, host->card);
 	mmc_release_host(host);
@@ -1508,7 +1531,6 @@
 	.resume = NULL,
 	.power_restore = mmc_power_restore,
 	.alive = mmc_alive,
-	.poweroff_notify = mmc_poweroff_notify,
 };
 
 static const struct mmc_bus_ops mmc_ops_unsafe = {
@@ -1520,7 +1542,6 @@
 	.resume = mmc_resume,
 	.power_restore = mmc_power_restore,
 	.alive = mmc_alive,
-	.poweroff_notify = mmc_poweroff_notify,
 };
 
 static void mmc_attach_bus_ops(struct mmc_host *host)
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index 0c3f994..da38122 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -500,6 +500,10 @@
 	help
 	  Select Y to enable Slot 3.
 
+config MMC_MSM_SDC3_POLLING
+	boolean "Qualcomm SDC3 support"
+	depends on MMC_MSM
+
 config MMC_MSM_SDC3_8_BIT_SUPPORT
 	boolean "Qualcomm SDC3 8bit support"
 	depends on MMC_MSM_SDC3_SUPPORT
diff --git a/drivers/mmc/host/msm_sdcc.c b/drivers/mmc/host/msm_sdcc.c
index 595bc5d..7725715 100644
--- a/drivers/mmc/host/msm_sdcc.c
+++ b/drivers/mmc/host/msm_sdcc.c
@@ -3,7 +3,8 @@
  *
  *  Copyright (C) 2007 Google Inc,
  *  Copyright (C) 2003 Deep Blue Solutions, Ltd, All Rights Reserved.
- *  Copyright (c) 2009-2012, Code Aurora Forum. All rights reserved.
+ *  Copyright (c) 2009-2012, The Linux Foundation. All rights reserved.
+ *
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -75,6 +76,7 @@
 #define SPS_MIN_XFER_SIZE		MCI_FIFOSIZE
 
 #define MSM_MMC_BUS_VOTING_DELAY	200 /* msecs */
+#define INVALID_TUNING_PHASE		-1
 
 #if defined(CONFIG_DEBUG_FS)
 static void msmsdcc_dbg_createhost(struct msmsdcc_host *);
@@ -212,9 +214,9 @@
 #endif /* CONFIG_MMC_MSM_SPS_SUPPORT */
 
 /**
- * Apply soft reset to all SDCC BAM pipes
+ * Apply reset
  *
- * This function applies soft reset to SDCC BAM pipe.
+ * This function resets SPS BAM and DML cores.
  *
  * This function should be called to recover from error
  * conditions encountered during CMD/DATA tranfsers with card.
@@ -222,43 +224,64 @@
  * @host - Pointer to driver's host structure
  *
  */
-static void msmsdcc_sps_pipes_reset_and_restore(struct msmsdcc_host *host)
+static int msmsdcc_bam_dml_reset_and_restore(struct msmsdcc_host *host)
 {
 	int rc;
 
+	/* Reset and init DML */
+	rc = msmsdcc_dml_init(host);
+	if (rc) {
+		pr_err("%s: msmsdcc_dml_init error=%d\n",
+				mmc_hostname(host->mmc), rc);
+		goto out;
+	}
+
 	/* Reset all SDCC BAM pipes */
 	rc = msmsdcc_sps_reset_ep(host, &host->sps.prod);
-	if (rc)
-		pr_err("%s:msmsdcc_sps_reset_ep(prod) error=%d\n",
+	if (rc) {
+		pr_err("%s: msmsdcc_sps_reset_ep(prod) error=%d\n",
 				mmc_hostname(host->mmc), rc);
-	rc = msmsdcc_sps_reset_ep(host, &host->sps.cons);
-	if (rc)
-		pr_err("%s:msmsdcc_sps_reset_ep(cons) error=%d\n",
-				mmc_hostname(host->mmc), rc);
+		goto out;
+	}
 
-	if (host->sps.reset_device) {
-		rc = sps_device_reset(host->sps.bam_handle);
-		if (rc)
-			pr_err("%s: sps_device_reset error=%d\n",
+	rc = msmsdcc_sps_reset_ep(host, &host->sps.cons);
+	if (rc) {
+		pr_err("%s: msmsdcc_sps_reset_ep(cons) error=%d\n",
 				mmc_hostname(host->mmc), rc);
-		host->sps.reset_device = false;
+		goto out;
+	}
+
+	/* Reset BAM */
+	rc = sps_device_reset(host->sps.bam_handle);
+	if (rc) {
+		pr_err("%s: sps_device_reset error=%d\n",
+				mmc_hostname(host->mmc), rc);
+		goto out;
 	}
 
 	/* Restore all BAM pipes connections */
 	rc = msmsdcc_sps_restore_ep(host, &host->sps.prod);
-	if (rc)
-		pr_err("%s:msmsdcc_sps_restore_ep(prod) error=%d\n",
+	if (rc) {
+		pr_err("%s: msmsdcc_sps_restore_ep(prod) error=%d\n",
 				mmc_hostname(host->mmc), rc);
+		goto out;
+	}
+
 	rc = msmsdcc_sps_restore_ep(host, &host->sps.cons);
 	if (rc)
-		pr_err("%s:msmsdcc_sps_restore_ep(cons) error=%d\n",
+		pr_err("%s: msmsdcc_sps_restore_ep(cons) error=%d\n",
 				mmc_hostname(host->mmc), rc);
+	else
+		host->sps.reset_bam = false;
+
+out:
+	return rc;
 }
 
 /**
  * Apply soft reset
  *
- * This function applies soft reset to SDCC core and DML core.
+ * This function applies soft reset to SDCC core.
  *
  * This function should be called to recover from error
  * conditions encountered with CMD/DATA tranfsers with card.
@@ -276,6 +299,11 @@
 	 */
 	if (is_sw_reset_save_config(host)) {
 		ktime_t start;
+		uint32_t dll_config = 0;
+
+
+		if (is_sw_reset_save_config_broken(host))
+			dll_config = readl_relaxed(host->base + MCI_DLL_CONFIG);
 
 		writel_relaxed(readl_relaxed(host->base + MMCIPOWER)
 				| MCI_SW_RST_CFG, host->base + MMCIPOWER);
@@ -295,6 +323,11 @@
 				BUG();
 			}
 		}
+
+		if (is_sw_reset_save_config_broken(host)) {
+			writel_relaxed(dll_config, host->base + MCI_DLL_CONFIG);
+			mb();
+		}
 	} else {
 		writel_relaxed(0, host->base + MMCICOMMAND);
 		msmsdcc_sync_reg_wr(host);
@@ -353,25 +386,25 @@
 static void msmsdcc_reset_and_restore(struct msmsdcc_host *host)
 {
 	if (is_soft_reset(host)) {
-		if (is_sps_mode(host)) {
-			/* Reset DML first */
-			msmsdcc_dml_reset(host);
+		if (is_sps_mode(host))
 			/*
-			 * delay the SPS pipe reset in thread context as
+			 * delay the SPS BAM reset in thread context as
 			 * sps_connect/sps_disconnect APIs can be called
 			 * only from non-atomic context.
 			 */
-			host->sps.pipe_reset_pending = true;
-		}
-		mb();
+			host->sps.reset_bam = true;
+
 		msmsdcc_soft_reset(host);
 
 		pr_debug("%s: Applied soft reset to Controller\n",
 				mmc_hostname(host->mmc));
-
-		if (is_sps_mode(host))
-			msmsdcc_dml_init(host);
 	} else {
+		/*
+		 * When there is a requirement to use this hard reset,
+		 * BAM needs to be reconfigured as well by calling
+		 * msmsdcc_sps_exit and msmsdcc_sps_init.
+		 */
+
 		/* Give Clock reset (hard reset) to controller */
 		u32	mci_clk = 0;
 		u32	mci_mask0 = 0;
@@ -1347,6 +1380,9 @@
 		    (host->tuning_in_progress &&
 		    (opcode == MMC_SEND_TUNING_BLOCK_HS200 ||
 		     opcode == MMC_SEND_TUNING_BLOCK)))) {
+			/* Execute full tuning in case of CRC/timeout errors */
+			host->saved_tuning_phase = INVALID_TUNING_PHASE;
+
 			if (status & MCI_DATACRCFAIL) {
 				pr_err("%s: Data CRC error\n",
 				       mmc_hostname(host->mmc));
@@ -1725,6 +1761,8 @@
 		pr_err("%s: CMD%d: Command CRC error\n",
 			mmc_hostname(host->mmc), cmd->opcode);
 		msmsdcc_dump_sdcc_state(host);
+		/* Execute full tuning in case of CRC errors */
+		host->saved_tuning_phase = INVALID_TUNING_PHASE;
 		cmd->error = -EILSEQ;
 	}
 
@@ -1821,7 +1859,7 @@
 				 */
 				wake_lock(&host->sdio_wlock);
 			} else {
-				if (!mmc->card || !mmc_card_sdio(mmc->card)) {
+				if (mmc->card && !mmc_card_sdio(mmc->card)) {
 					WARN(1, "%s: SDCC core interrupt received for non-SDIO cards when SDCC clocks are off\n",
 					     mmc_hostname(mmc));
 					ret = 1;
@@ -1855,7 +1893,7 @@
 #endif
 
 		if (status & MCI_SDIOINTROPE) {
-			if (!mmc->card || mmc_card_sdio(mmc->card)) {
+			if (mmc->card && !mmc_card_sdio(mmc->card)) {
 				WARN(1, "%s: SDIO interrupt received for non-SDIO card\n",
 					mmc_hostname(mmc));
 				ret = 1;
@@ -2085,6 +2123,7 @@
 {
 	struct msmsdcc_host *host = mmc_priv(mmc);
 	unsigned long		flags;
+	int retries = 5;
 
 	/*
 	 * Get the SDIO AL client out of LPM.
@@ -2093,10 +2132,14 @@
 	if (host->plat->is_sdio_al_client)
 		msmsdcc_sdio_al_lpm(mmc, false);
 
-	/* check if sps pipe reset is pending? */
-	if (is_sps_mode(host) && host->sps.pipe_reset_pending) {
-		msmsdcc_sps_pipes_reset_and_restore(host);
-		host->sps.pipe_reset_pending = false;
+	/* check if sps bam needs to be reset */
+	if (is_sps_mode(host) && host->sps.reset_bam) {
+		while (retries) {
+			if (!msmsdcc_bam_dml_reset_and_restore(host))
+				break;
+			pr_err("%s: msmsdcc_bam_dml_reset_and_restore returned error. %d attempts left.\n",
+					mmc_hostname(host->mmc), --retries);
+		}
 	}
 
 	spin_lock_irqsave(&host->lock, flags);
@@ -2117,8 +2160,9 @@
 	/*
 	 * Don't start the request if SDCC is not in proper state to handle it
 	 */
-	if (!host->pwr || !atomic_read(&host->clks_on)
-			|| host->sdcc_irq_disabled) {
+	if (!host->pwr || !atomic_read(&host->clks_on) ||
+			host->sdcc_irq_disabled ||
+			host->sps.reset_bam) {
 		WARN(1, "%s: %s: SDCC is in bad state. don't process"
 		     " new request (CMD%d)\n", mmc_hostname(host->mmc),
 		     __func__, mrq->cmd->opcode);
@@ -2177,7 +2221,9 @@
 		}
 
 		if ((mrq->cmd->opcode == MMC_WRITE_BLOCK) ||
-		    (mrq->cmd->opcode == MMC_WRITE_MULTIPLE_BLOCK))
+		    (mrq->cmd->opcode == MMC_WRITE_MULTIPLE_BLOCK) ||
+		    ((mrq->cmd->opcode == SD_IO_RW_EXTENDED) &&
+		     is_data_pend_for_cmd53(host)))
 			host->curr.use_wr_data_pend = true;
 	}
 
@@ -3196,8 +3242,21 @@
 		/*
 		 * For DDR50 mode, controller needs clock rate to be
 		 * double than what is required on the SD card CLK pin.
+		 *
+		 * Setting DDR timing mode in controller before setting the
+		 * clock rate will make sure that card don't see the double
+		 * clock rate even for very small duration. Some eMMC
+		 * cards seems to lock up if they see clock frequency > 52MHz.
 		 */
 		if (ios->timing == MMC_TIMING_UHS_DDR50) {
+			u32 clk;
+
+			clk = readl_relaxed(host->base + MMCICLOCK);
+			clk &= ~(0x7 << 14); /* clear SELECT_IN field */
+			clk |= (3 << 14); /* set DDR timing mode */
+			writel_relaxed(clk, host->base + MMCICLOCK);
+			msmsdcc_sync_reg_wr(host);
+
 			/*
 			 * Make sure that we don't double the clock if
 			 * doubled clock rate is already set
@@ -3979,6 +4038,7 @@
 	u8 phase, *data_buf, tuned_phases[16], tuned_phase_cnt = 0;
 	const u32 *tuning_block_pattern = tuning_block_64;
 	int size = sizeof(tuning_block_64); /* Tuning pattern size in bytes */
+	bool is_tuning_all_phases;
 
 	pr_debug("%s: Enter %s\n", mmc_hostname(mmc), __func__);
 
@@ -4012,7 +4072,13 @@
 		goto out;
 	}
 
-	phase = 0;
+	is_tuning_all_phases = !(host->mmc->card &&
+		(host->saved_tuning_phase != INVALID_TUNING_PHASE));
+retry:
+	if (is_tuning_all_phases)
+		phase = 0; /* start from phase 0 during init */
+	else
+		phase = (u8)host->saved_tuning_phase;
 	do {
 		struct mmc_command cmd = {0};
 		struct mmc_data data = {0};
@@ -4044,9 +4110,16 @@
 		if (!cmd.error && !data.error &&
 			!memcmp(data_buf, tuning_block_pattern, size)) {
 			/* tuning is successful at this tuning point */
+			if (!is_tuning_all_phases)
+				goto kfree;
 			tuned_phases[tuned_phase_cnt++] = phase;
 			pr_debug("%s: %s: found good phase = %d\n",
 				mmc_hostname(mmc), __func__, phase);
+		} else if (!is_tuning_all_phases) {
+			pr_debug("%s: tuning failed at saved phase (%d), retrying\n",
+					mmc_hostname(mmc), (u32)phase);
+			is_tuning_all_phases = true;
+			goto retry;
 		}
 	} while (++phase < 16);
 
@@ -4065,6 +4138,8 @@
 		rc = msmsdcc_config_cm_sdc4_dll_phase(host, phase);
 		if (rc)
 			goto kfree;
+		else
+			host->saved_tuning_phase = phase;
 		pr_debug("%s: %s: finally setting the tuning phase to %d\n",
 				mmc_hostname(mmc), __func__, phase);
 	} else {
@@ -4145,25 +4220,35 @@
 	.hw_reset = msmsdcc_hw_reset,
 };
 
+static void msmsdcc_enable_status_gpio(struct msmsdcc_host *host)
+{
+	unsigned int gpio_no = host->plat->status_gpio;
+	int status;
+
+	if (!gpio_is_valid(gpio_no))
+		return;
+
+	status = gpio_request(gpio_no, "SD_HW_Detect");
+	if (status)
+		pr_err("%s: %s: gpio_request(%d) failed\n",
+			mmc_hostname(host->mmc), __func__, gpio_no);
+}
+
+static void msmsdcc_disable_status_gpio(struct msmsdcc_host *host)
+{
+	if (gpio_is_valid(host->plat->status_gpio))
+		gpio_free(host->plat->status_gpio);
+}
+
 static unsigned int
 msmsdcc_slot_status(struct msmsdcc_host *host)
 {
 	int status;
-	unsigned int gpio_no = host->plat->status_gpio;
 
-	status = gpio_request(gpio_no, "SD_HW_Detect");
-	if (status) {
-		pr_err("%s: %s: Failed to request GPIO %d\n",
-			mmc_hostname(host->mmc), __func__, gpio_no);
-	} else {
-		status = gpio_direction_input(gpio_no);
-		if (!status) {
-			status = gpio_get_value_cansleep(gpio_no);
-			if (host->plat->is_status_gpio_active_low)
-				status = !status;
-		}
-		gpio_free(gpio_no);
-	}
+	status = gpio_get_value_cansleep(host->plat->status_gpio);
+	if (host->plat->is_status_gpio_active_low)
+		status = !status;
+
 	return status;
 }
 
@@ -4575,11 +4660,8 @@
 	BUG_ON(!is_sps_mode(host));
 
 	if (sps_cb_case == SPS_CALLBACK_BAM_ERROR_IRQ) {
-		/**
-		 * Reset the all endpoints along with reseting the sps device.
-		 */
-		host->sps.pipe_reset_pending = true;
-		host->sps.reset_device = true;
+		/* Reset all endpoints along with resetting bam. */
+		host->sps.reset_bam = true;
 
 		pr_err("%s: BAM Global ERROR IRQ happened\n",
 			mmc_hostname(host->mmc));
@@ -4995,6 +5077,10 @@
 			host->curr.data_xfered, host->curr.xfer_remain);
 	}
 
+	if (host->sps.reset_bam)
+		pr_err("%s: SPS BAM reset failed: sps reset_bam=%d\n",
+			mmc_hostname(host->mmc), host->sps.reset_bam);
+
 	pr_err("%s: got_dataend=%d, prog_enable=%d,"
 		" wait_for_auto_prog_done=%d, got_auto_prog_done=%d,"
 		" req_tout_ms=%d\n", mmc_hostname(host->mmc),
@@ -5440,7 +5526,7 @@
 	struct mmc_platform_data *pdata;
 	struct device_node *np = dev->of_node;
 	u32 bus_width = 0, current_limit = 0;
-	u32 *clk_table, *sup_voltages;
+	u32 *clk_table = NULL, *sup_voltages = NULL;
 	int clk_table_len, sup_volt_len, len;
 
 	pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
@@ -5739,6 +5825,7 @@
 		dev_err(&pdev->dev, "Failed to read MCLK\n");
 
 	set_default_hw_caps(host);
+	host->saved_tuning_phase = INVALID_TUNING_PHASE;
 
 	/*
 	 * Set the register write delay according to min. clock frequency
@@ -5824,10 +5911,13 @@
 		mmc->caps |= (MMC_CAP_SET_XPC_330 | MMC_CAP_SET_XPC_300 |
 				MMC_CAP_SET_XPC_180);
 
+	mmc->caps2 |= MMC_CAP2_PACKED_WR;
+	mmc->caps2 |= MMC_CAP2_PACKED_WR_CONTROL;
 	mmc->caps2 |= (MMC_CAP2_BOOTPART_NOACC | MMC_CAP2_DETECT_ON_ERR);
 	mmc->caps2 |= MMC_CAP2_SANITIZE;
 	mmc->caps2 |= MMC_CAP2_CACHE_CTRL;
 	mmc->caps2 |= MMC_CAP2_INIT_BKOPS;
+	mmc->caps2 |= MMC_CAP2_POWEROFF_NOTIFY;
 
 	if (plat->nonremovable)
 		mmc->caps |= MMC_CAP_NONREMOVABLE;
@@ -5910,11 +6000,12 @@
 		plat->wpswitch_gpio = -ENOENT;
 
 	if (plat->status || gpio_is_valid(plat->status_gpio)) {
-		if (plat->status)
+		if (plat->status) {
 			host->oldstat = plat->status(mmc_dev(host->mmc));
-		else
+		} else {
+			msmsdcc_enable_status_gpio(host);
 			host->oldstat = msmsdcc_slot_status(host);
-
+		}
 		host->eject = !host->oldstat;
 	}
 
@@ -6097,6 +6188,7 @@
 
 	if (plat->status_irq)
 		free_irq(plat->status_irq, host);
+	msmsdcc_disable_status_gpio(host);
  sdiowakeup_irq_free:
 	wake_lock_destroy(&host->sdio_suspend_wlock);
 	if (plat->sdiowakeup_irq)
@@ -6193,6 +6285,7 @@
 
 	if (plat->status_irq)
 		free_irq(plat->status_irq, host);
+	msmsdcc_disable_status_gpio(host);
 
 	wake_lock_destroy(&host->sdio_suspend_wlock);
 	if (plat->sdiowakeup_irq) {
@@ -6521,8 +6614,10 @@
 		rc = 0;
 		goto out;
 	}
-	if (host->plat->status_irq)
+	if (host->plat->status_irq) {
 		disable_irq(host->plat->status_irq);
+		msmsdcc_disable_status_gpio(host);
+	}
 
 	if (!pm_runtime_suspended(dev))
 		rc = msmsdcc_runtime_suspend(dev);
@@ -6578,6 +6673,7 @@
 		host->pending_resume = true;
 
 	if (host->plat->status_irq) {
+		msmsdcc_enable_status_gpio(host);
 		msmsdcc_check_status((unsigned long)host);
 		enable_irq(host->plat->status_irq);
 	}
diff --git a/drivers/mmc/host/msm_sdcc.h b/drivers/mmc/host/msm_sdcc.h
index c66d1a5..d6cba2d 100644
--- a/drivers/mmc/host/msm_sdcc.h
+++ b/drivers/mmc/host/msm_sdcc.h
@@ -2,7 +2,7 @@
  *  linux/drivers/mmc/host/msmsdcc.h - QCT MSM7K SDC Controller
  *
  *  Copyright (C) 2008 Google, All Rights Reserved.
- *  Copyright (c) 2009-2012, Code Aurora Forum. All rights reserved.
+ *  Copyright (c) 2009-2012, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -321,8 +321,7 @@
 	unsigned int			dest_pipe_index;
 	unsigned int			busy;
 	unsigned int			xfer_req_cnt;
-	bool				pipe_reset_pending;
-	bool				reset_device;
+	bool				reset_bam;
 	struct tasklet_struct		tlet;
 };
 
@@ -428,6 +427,7 @@
 	struct dentry *debugfs_idle_tout;
 	struct dentry *debugfs_pio_mode;
 	struct dentry *debugfs_pm_stats;
+	int saved_tuning_phase;
 };
 
 #define MSMSDCC_VERSION_STEP_MASK	0x0000FFFF
@@ -446,6 +446,8 @@
 #define MSMSDCC_IO_PAD_PWR_SWITCH	(1 << 8)
 #define MSMSDCC_AUTO_CMD19	(1 << 9)
 #define MSMSDCC_AUTO_CMD21	(1 << 10)
+#define MSMSDCC_SW_RST_CFG_BROKEN	(1 << 11)
+#define MSMSDCC_DATA_PEND_FOR_CMD53	(1 << 12)
 
 #define set_hw_caps(h, val)		((h)->hw_caps |= val)
 #define is_sps_mode(h)			((h)->hw_caps & MSMSDCC_SPS_BAM_SUP)
@@ -459,6 +461,9 @@
 #define is_io_pad_pwr_switch(h)	((h)->hw_caps & MSMSDCC_IO_PAD_PWR_SWITCH)
 #define is_auto_cmd19(h)		((h)->hw_caps & MSMSDCC_AUTO_CMD19)
 #define is_auto_cmd21(h)		((h)->hw_caps & MSMSDCC_AUTO_CMD21)
+#define is_sw_reset_save_config_broken(h) \
+				((h)->hw_caps & MSMSDCC_SW_RST_CFG_BROKEN)
+#define is_data_pend_for_cmd53(h) ((h)->hw_caps & MSMSDCC_DATA_PEND_FOR_CMD53)
 
 /* Set controller capabilities based on version */
 static inline void set_default_hw_caps(struct msmsdcc_host *host)
@@ -490,7 +495,12 @@
 		host->hw_caps |= MSMSDCC_AUTO_CMD21;
 
 	if (step >= 0x2b) /* SDCC v4 2.1.0 and greater */
-		host->hw_caps |= MSMSDCC_SW_RST | MSMSDCC_AUTO_CMD21;
+		host->hw_caps |= MSMSDCC_SW_RST | MSMSDCC_SW_RST_CFG |
+				 MSMSDCC_AUTO_CMD21 |
+				 MSMSDCC_DATA_PEND_FOR_CMD53;
+
+	if (step == 0x2b)
+		host->hw_caps |= MSMSDCC_SW_RST_CFG_BROKEN;
 }
 
 int msmsdcc_set_pwrsave(struct mmc_host *mmc, int pwrsave);
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 2f1a0dc..43f7e77 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -2581,6 +2581,10 @@
 	if (caps[1] & SDHCI_DRIVER_TYPE_D)
 		mmc->caps |= MMC_CAP_DRIVER_TYPE_D;
 
+	/* Initial value for re-tuning timer count */
+	host->tuning_count = (caps[1] & SDHCI_RETUNING_TIMER_COUNT_MASK) >>
+			      SDHCI_RETUNING_TIMER_COUNT_SHIFT;
+
 	/*
 	 * In case Re-tuning Timer is not disabled, the actual value of
 	 * re-tuning timer will be 2 ^ (n - 1).
diff --git a/drivers/net/ethernet/msm/msm_rmnet.c b/drivers/net/ethernet/msm/msm_rmnet.c
index 41ad8af..4af2d8c 100644
--- a/drivers/net/ethernet/msm/msm_rmnet.c
+++ b/drivers/net/ethernet/msm/msm_rmnet.c
@@ -3,7 +3,7 @@
  * Virtual Ethernet Interface for MSM7K Networking
  *
  * Copyright (C) 2007 Google, Inc.
- * Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
  * Author: Brian Swetland <swetland@google.com>
  *
  * This software is licensed under the terms of the GNU General Public
@@ -37,7 +37,7 @@
 #endif
 
 #include <mach/msm_smd.h>
-#include <mach/peripheral-loader.h>
+#include <mach/subsystem_restart.h>
 
 /* Debug message support */
 static int msm_rmnet_debug_mask;
@@ -403,7 +403,7 @@
 static void msm_rmnet_unload_modem(void *pil)
 {
 	if (pil)
-		pil_put(pil);
+		subsystem_put(pil);
 }
 
 static void *msm_rmnet_load_modem(struct net_device *dev)
@@ -412,7 +412,7 @@
 	int rc;
 	struct rmnet_private *p = netdev_priv(dev);
 
-	pil = pil_get("modem");
+	pil = subsystem_get("modem");
 	if (IS_ERR(pil))
 		pr_err("[%s] %s: modem load failed\n",
 			dev->name, __func__);
diff --git a/drivers/net/ethernet/msm/msm_rmnet_smux.c b/drivers/net/ethernet/msm/msm_rmnet_smux.c
index 5f29406..e56a64e 100644
--- a/drivers/net/ethernet/msm/msm_rmnet_smux.c
+++ b/drivers/net/ethernet/msm/msm_rmnet_smux.c
@@ -482,7 +482,7 @@
 		p = netdev_priv(priv);
 		DBG0("[%s] Low WM hit dev:%s\n", __func__, dev->name);
 		spin_lock_irqsave(&p->tx_queue_lock, flags);
-		netif_start_queue(dev);
+		netif_wake_queue(dev);
 		spin_unlock_irqrestore(&p->tx_queue_lock, flags);
 		break;
 
diff --git a/drivers/net/usb/rmnet_usb_data.c b/drivers/net/usb/rmnet_usb_data.c
index 17ff067..fdfe468 100644
--- a/drivers/net/usb/rmnet_usb_data.c
+++ b/drivers/net/usb/rmnet_usb_data.c
@@ -556,10 +556,6 @@
 		/* allow modem and roothub to wake up suspended system */
 		device_set_wakeup_enable(&udev->dev, 1);
 		device_set_wakeup_enable(&udev->parent->dev, 1);
-
-		/* set default autosuspend timeout for modem and roothub */
-		pm_runtime_set_autosuspend_delay(&udev->dev, 1000);
-		pm_runtime_set_autosuspend_delay(&udev->parent->dev, 200);
 	}
 
 out:
diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c
index 6de0a77..740c717 100644
--- a/drivers/net/usb/usbnet.c
+++ b/drivers/net/usb/usbnet.c
@@ -1168,6 +1168,7 @@
 		usb_anchor_urb(urb, &dev->deferred);
 		/* no use to process more packets */
 		netif_stop_queue(net);
+		usb_put_urb(urb);
 		spin_unlock_irqrestore(&dev->txq.lock, flags);
 		netdev_dbg(dev->net, "Delaying transmission for resumption\n");
 		goto deferred;
@@ -1317,6 +1318,8 @@
 
 	cancel_work_sync(&dev->kevent);
 
+	usb_scuttle_anchored_urbs(&dev->deferred);
+
 	if (dev->driver_info->unbind)
 		dev->driver_info->unbind (dev, intf);
 
diff --git a/drivers/net/wireless/wcnss/wcnss_wlan.c b/drivers/net/wireless/wcnss/wcnss_wlan.c
index 2bf857c..b9459dd 100644
--- a/drivers/net/wireless/wcnss/wcnss_wlan.c
+++ b/drivers/net/wireless/wcnss/wcnss_wlan.c
@@ -25,9 +25,11 @@
 #include <linux/delay.h>
 #include <linux/of.h>
 #include <linux/of_gpio.h>
-#include <mach/peripheral-loader.h>
+
 #include <mach/msm_smd.h>
 #include <mach/msm_iomap.h>
+#include <mach/subsystem_restart.h>
+
 #ifdef CONFIG_WCNSS_MEM_PRE_ALLOC
 #include "wcnss_prealloc.h"
 #endif
@@ -664,7 +666,7 @@
 	}
 
 	/* trigger initialization of the WCNSS */
-	penv->pil = pil_get(WCNSS_PIL_DEVICE);
+	penv->pil = subsystem_get(WCNSS_PIL_DEVICE);
 	if (IS_ERR(penv->pil)) {
 		dev_err(&pdev->dev, "Peripheral Loader failed on WCNSS.\n");
 		ret = PTR_ERR(penv->pil);
@@ -694,7 +696,7 @@
 
 fail_res:
 	if (penv->pil)
-		pil_put(penv->pil);
+		subsystem_put(penv->pil);
 fail_pil:
 	wcnss_wlan_power(&pdev->dev, &penv->wlan_config,
 				WCNSS_WLAN_SWITCH_OFF);
@@ -840,7 +842,7 @@
 {
 	if (penv) {
 		if (penv->pil)
-			pil_put(penv->pil);
+			subsystem_put(penv->pil);
 
 
 		kfree(penv);
diff --git a/drivers/platform/msm/qpnp-power-on.c b/drivers/platform/msm/qpnp-power-on.c
index 33b12ae..1907adc 100644
--- a/drivers/platform/msm/qpnp-power-on.c
+++ b/drivers/platform/msm/qpnp-power-on.c
@@ -39,8 +39,10 @@
 
 #define QPNP_PON_RESIN_PULL_UP		BIT(0)
 #define QPNP_PON_KPDPWR_PULL_UP		BIT(1)
+#define QPNP_PON_CBLPWR_PULL_UP		BIT(2)
 #define QPNP_PON_S2_CNTL_EN		BIT(7)
 #define QPNP_PON_S2_RESET_ENABLE	BIT(7)
+#define QPNP_PON_DELAY_BIT_SHIFT	6
 
 #define QPNP_PON_S1_TIMER_MASK		(0xF)
 #define QPNP_PON_S2_TIMER_MASK		(0x7)
@@ -49,6 +51,7 @@
 #define QPNP_PON_DBC_DELAY_MASK		(0x7)
 #define QPNP_PON_KPDPWR_N_SET		BIT(0)
 #define QPNP_PON_RESIN_N_SET		BIT(1)
+#define QPNP_PON_CBLPWR_N_SET		BIT(2)
 #define QPNP_PON_RESIN_BARK_N_SET	BIT(4)
 
 #define QPNP_PON_RESET_EN		BIT(7)
@@ -61,11 +64,12 @@
 #define QPNP_PON_RESET_TYPE_MAX		0xF
 #define PON_S1_COUNT_MAX		0xF
 
-#define QPNP_KEY_STATUS_DELAY		msecs_to_jiffies(500)
+#define QPNP_KEY_STATUS_DELAY		msecs_to_jiffies(250)
 
 enum pon_type {
 	PON_KPDPWR,
 	PON_RESIN,
+	PON_CBLPWR,
 };
 
 struct qpnp_pon_config {
@@ -214,6 +218,9 @@
 	case PON_RESIN:
 		pon_rt_bit = QPNP_PON_RESIN_N_SET;
 		break;
+	case PON_CBLPWR:
+		pon_rt_bit = QPNP_PON_CBLPWR_N_SET;
+		break;
 	default:
 		return -EINVAL;
 	}
@@ -253,6 +260,18 @@
 	return IRQ_HANDLED;
 }
 
+static irqreturn_t qpnp_cblpwr_irq(int irq, void *_pon)
+{
+	int rc;
+	struct qpnp_pon *pon = _pon;
+
+	rc = qpnp_pon_input_dispatch(pon, PON_CBLPWR);
+	if (rc)
+		dev_err(&pon->spmi->dev, "Unable to send input event\n");
+
+	return IRQ_HANDLED;
+}
+
 static void bark_work_func(struct work_struct *work)
 {
 	int rc;
@@ -351,6 +370,9 @@
 	case PON_RESIN:
 		pull_bit = QPNP_PON_RESIN_PULL_UP;
 		break;
+	case PON_CBLPWR:
+		pull_bit = QPNP_PON_CBLPWR_PULL_UP;
+		break;
 	default:
 		return -EINVAL;
 	}
@@ -489,6 +511,17 @@
 			}
 		}
 		break;
+	case PON_CBLPWR:
+		rc = devm_request_irq(&pon->spmi->dev, cfg->state_irq,
+							qpnp_cblpwr_irq,
+				IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+					"qpnp_cblpwr_status", pon);
+		if (rc < 0) {
+			dev_err(&pon->spmi->dev, "Can't request %d IRQ\n",
+							cfg->state_irq);
+			return rc;
+		}
+		break;
 	default:
 		return -EINVAL;
 	}
@@ -595,6 +628,15 @@
 				}
 			}
 			break;
+		case PON_CBLPWR:
+			cfg->state_irq = spmi_get_irq_byname(pon->spmi,
+							NULL, "cblpwr");
+			if (cfg->state_irq < 0) {
+				dev_err(&pon->spmi->dev,
+						"Unable to get cblpwr irq\n");
+				return rc;
+			}
+			break;
 		default:
 			dev_err(&pon->spmi->dev, "PON RESET %d not supported",
 								cfg->pon_type);
@@ -763,11 +805,13 @@
 
 	rc = of_property_read_u32(pon->spmi->dev.of_node,
 				"qcom,pon-dbc-delay", &delay);
-	if (rc && rc != -EINVAL) {
-		dev_err(&spmi->dev, "Unable to read debounce delay\n");
-		return rc;
+	if (rc) {
+		if (rc != -EINVAL) {
+			dev_err(&spmi->dev, "Unable to read debounce delay\n");
+			return rc;
+		}
 	} else {
-		delay = (delay << 6) / USEC_PER_SEC;
+		delay = (delay << QPNP_PON_DELAY_BIT_SHIFT) / USEC_PER_SEC;
 		delay = ilog2(delay);
 		rc = qpnp_pon_masked_write(pon, QPNP_PON_DBC_CTL(pon->base),
 						QPNP_PON_DBC_DELAY_MASK, delay);
diff --git a/drivers/platform/msm/qpnp-pwm.c b/drivers/platform/msm/qpnp-pwm.c
index 6f9af36..1729b49 100644
--- a/drivers/platform/msm/qpnp-pwm.c
+++ b/drivers/platform/msm/qpnp-pwm.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
 
  *
  * This program is free software; you can redistribute it and/or modify
@@ -107,8 +107,10 @@
 /* LPG Control for RAMP_CONTROL */
 #define QPNP_RAMP_START_MASK			0x01
 
-#define QPNP_ENABLE_LUT(value) (value |= QPNP_RAMP_START_MASK)
-#define QPNP_DISABLE_LUT(value) (value &= ~QPNP_RAMP_START_MASK)
+#define QPNP_ENABLE_LUT_V0(value) (value |= QPNP_RAMP_START_MASK)
+#define QPNP_DISABLE_LUT_V0(value) (value &= ~QPNP_RAMP_START_MASK)
+#define QPNP_ENABLE_LUT_V1(value, id) (value |= BIT(id))
+#define QPNP_DISABLE_LUT_V1(value, id) (value &= ~BIT(id))
 
 /* LPG Control for RAMP_STEP_DURATION_LSB */
 #define QPNP_RAMP_STEP_DURATION_LSB_MASK	0xFF
@@ -154,10 +156,30 @@
 #define PRE_DIVIDE_5		5
 #define PRE_DIVIDE_6		6
 
-#define SPMI_LPG_REG_ADDR_BASE	0x40
-#define SPMI_LPG_REG_ADDR(b, n)	(b + SPMI_LPG_REG_ADDR_BASE + (n))
+#define SPMI_LPG_REG_BASE_OFFSET	0x40
+#define SPMI_LPG_REVISION2_OFFSET	0x1
+#define SPMI_LPG_REV1_RAMP_CONTROL_OFFSET	0x86
+#define SPMI_LPG_REG_ADDR(b, n)	(b + SPMI_LPG_REG_BASE_OFFSET + (n))
 #define SPMI_MAX_BUF_LEN	8
 
+/* LPG revisions */
+enum qpnp_lpg_revision {
+	QPNP_LPG_REVISION_0 = 0x0,
+	QPNP_LPG_REVISION_1 = 0x1,
+};
+
+/* LPG LUT MODE STATE */
+enum qpnp_lut_state {
+	QPNP_LUT_ENABLE = 0x0,
+	QPNP_LUT_DISABLE = 0x1,
+};
+
+/* PWM MODE STATE */
+enum qpnp_pwm_state {
+	QPNP_PWM_ENABLE = 0x0,
+	QPNP_PWM_DISABLE = 0x1,
+};
+
 /* SPMI LPG registers */
 enum qpnp_lpg_registers_list {
 	QPNP_LPG_PATTERN_CONFIG,
@@ -251,9 +273,10 @@
 struct qpnp_lpg_chip {
 	struct	spmi_device	*spmi_dev;
 	struct	pwm_device	pwm_dev;
-	struct	mutex		lpg_mutex;
+	spinlock_t		lpg_lock;
 	struct	qpnp_lpg_config	lpg_config;
 	u8	qpnp_lpg_registers[QPNP_TOTAL_LPG_SPMI_REGISTERS];
+	enum qpnp_lpg_revision	revision;
 };
 
 /* Internal functions */
@@ -283,21 +306,22 @@
 				QPNP_EN_GLITCH_REMOVAL_MASK;
 }
 
-static inline void qpnp_set_control(u8 *val, bool pwm_hi, bool pwm_lo,
-			bool pwm_out, bool pwm_src, bool ramp_gen)
+static int qpnp_set_control(bool pwm_hi, bool pwm_lo, bool pwm_out,
+					bool pwm_src, bool ramp_gen)
 {
-	*val = (ramp_gen << QPNP_PWM_EN_RAMP_GEN_SHIFT) &
-				QPNP_PWM_EN_RAMP_GEN_MASK;
-	*val |= (pwm_src << QPNP_PWM_SRC_SELECT_SHIFT) &
-				QPNP_PWM_SRC_SELECT_MASK;
-	*val |= (pwm_out << QPNP_EN_PWM_OUTPUT_SHIFT) &
-				QPNP_EN_PWM_OUTPUT_MASK;
-	*val |= (pwm_lo << QPNP_EN_PWM_LO_SHIFT) & QPNP_EN_PWM_LO_MASK;
-	*val |= (pwm_hi << QPNP_EN_PWM_HIGH_SHIFT) & QPNP_EN_PWM_HIGH_MASK;
+	return (ramp_gen << QPNP_PWM_EN_RAMP_GEN_SHIFT)
+		| (pwm_src << QPNP_PWM_SRC_SELECT_SHIFT)
+		| (pwm_out << QPNP_EN_PWM_OUTPUT_SHIFT)
+		| (pwm_lo << QPNP_EN_PWM_LO_SHIFT)
+		| (pwm_hi << QPNP_EN_PWM_HIGH_SHIFT);
 }
 
-#define QPNP_ENABLE_LUT_CONTROL(p_val)	qpnp_set_control(p_val, 1, 1, 1, 0, 1)
-#define QPNP_ENABLE_PWM_CONTROL(p_val)	qpnp_set_control(p_val, 1, 1, 0, 1, 0)
+#define QPNP_ENABLE_LUT_CONTROL		qpnp_set_control(0, 0, 0, 0, 1)
+#define QPNP_ENABLE_PWM_CONTROL		qpnp_set_control(0, 0, 0, 1, 0)
+#define QPNP_ENABLE_PWM_MODE		qpnp_set_control(1, 1, 1, 1, 0)
+#define QPNP_ENABLE_LPG_MODE		qpnp_set_control(1, 1, 1, 0, 1)
+#define QPNP_DISABLE_PWM_MODE		qpnp_set_control(0, 0, 0, 1, 0)
+#define QPNP_DISABLE_LPG_MODE		qpnp_set_control(0, 0, 0, 0, 1)
 #define QPNP_IS_PWM_CONFIG_SELECTED(val) (val & QPNP_PWM_SRC_SELECT_MASK)
 
 
@@ -328,13 +352,13 @@
 	*u8p |= val & mask;
 }
 
-static int qpnp_lpg_save_and_write(u8 value, u8 mask, u8 *reg, u16 base_addr,
-			u16 offset, u16 size, struct qpnp_lpg_chip *chip)
+static int qpnp_lpg_save_and_write(u8 value, u8 mask, u8 *reg, u16 addr,
+				u16 size, struct qpnp_lpg_chip *chip)
 {
 	qpnp_lpg_save(reg, mask, value);
 
 	return spmi_ext_register_writel(chip->spmi_dev->ctrl,
-	chip->spmi_dev->sid, SPMI_LPG_REG_ADDR(base_addr, offset), reg, size);
+			chip->spmi_dev->sid, addr, reg, size);
 }
 
 /*
@@ -439,7 +463,7 @@
 	int			i, pwm_size, rc = 0;
 	int			burst_size = SPMI_MAX_BUF_LEN;
 	int			list_len = lut->list_len << 1;
-	int			offset = lut->lo_index << 2;
+	int			offset = lut->lo_index << 1;
 
 	pwm_size = QPNP_GET_PWM_SIZE(
 			chip->qpnp_lpg_registers[QPNP_LPG_PWM_SIZE_CLK]) &
@@ -455,7 +479,7 @@
 		return -EINVAL;
 	}
 
-	for (i = 0; i <= lut->list_len; i++) {
+	for (i = 0; i < lut->list_len; i++) {
 		if (raw_value)
 			pwm_value = duty_pct[i];
 		else
@@ -530,7 +554,8 @@
 
 	rc = qpnp_lpg_save_and_write(value, mask,
 			&pwm->chip->qpnp_lpg_registers[QPNP_PWM_VALUE_LSB],
-			lpg_config->base_addr, QPNP_PWM_VALUE_LSB, 1, chip);
+			SPMI_LPG_REG_ADDR(lpg_config->base_addr,
+			QPNP_PWM_VALUE_LSB), 1, chip);
 	if (rc)
 		return rc;
 
@@ -541,7 +566,8 @@
 
 	return qpnp_lpg_save_and_write(value, mask,
 			&pwm->chip->qpnp_lpg_registers[QPNP_PWM_VALUE_MSB],
-			lpg_config->base_addr, QPNP_PWM_VALUE_MSB, 1, chip);
+			SPMI_LPG_REG_ADDR(lpg_config->base_addr,
+			QPNP_PWM_VALUE_MSB), 1, chip);
 }
 
 static int qpnp_lpg_configure_pattern(struct pwm_device *pwm)
@@ -559,7 +585,8 @@
 
 	return qpnp_lpg_save_and_write(value, mask,
 		&pwm->chip->qpnp_lpg_registers[QPNP_LPG_PATTERN_CONFIG],
-		lpg_config->base_addr, QPNP_LPG_PATTERN_CONFIG, 1, chip);
+		SPMI_LPG_REG_ADDR(lpg_config->base_addr,
+		QPNP_LPG_PATTERN_CONFIG), 1, chip);
 }
 
 static int qpnp_lpg_configure_pwm(struct pwm_device *pwm)
@@ -590,7 +617,8 @@
 
 	return qpnp_lpg_save_and_write(value, mask,
 		&pwm->chip->qpnp_lpg_registers[QPNP_LPG_PWM_TYPE_CONFIG],
-		lpg_config->base_addr, QPNP_LPG_PWM_TYPE_CONFIG, 1, chip);
+		SPMI_LPG_REG_ADDR(lpg_config->base_addr,
+		QPNP_LPG_PWM_TYPE_CONFIG), 1, chip);
 }
 
 static int qpnp_configure_pwm_control(struct pwm_device *pwm)
@@ -599,7 +627,7 @@
 	struct qpnp_lpg_chip	*chip = pwm->chip;
 	u8			value, mask;
 
-	QPNP_ENABLE_PWM_CONTROL(&value);
+	value = QPNP_ENABLE_PWM_CONTROL;
 
 	mask = QPNP_EN_PWM_HIGH_MASK | QPNP_EN_PWM_LO_MASK |
 		QPNP_EN_PWM_OUTPUT_MASK | QPNP_PWM_SRC_SELECT_MASK |
@@ -607,7 +635,8 @@
 
 	return qpnp_lpg_save_and_write(value, mask,
 		&pwm->chip->qpnp_lpg_registers[QPNP_ENABLE_CONTROL],
-		lpg_config->base_addr, QPNP_ENABLE_CONTROL, 1, chip);
+		SPMI_LPG_REG_ADDR(lpg_config->base_addr,
+		QPNP_ENABLE_CONTROL), 1, chip);
 
 }
 
@@ -617,7 +646,7 @@
 	struct qpnp_lpg_chip	*chip = pwm->chip;
 	u8			value, mask;
 
-	QPNP_ENABLE_LUT_CONTROL(&value);
+	value = QPNP_ENABLE_LUT_CONTROL;
 
 	mask = QPNP_EN_PWM_HIGH_MASK | QPNP_EN_PWM_LO_MASK |
 		QPNP_EN_PWM_OUTPUT_MASK | QPNP_PWM_SRC_SELECT_MASK |
@@ -625,7 +654,8 @@
 
 	return qpnp_lpg_save_and_write(value, mask,
 		&pwm->chip->qpnp_lpg_registers[QPNP_ENABLE_CONTROL],
-		lpg_config->base_addr, QPNP_ENABLE_CONTROL, 1, chip);
+		SPMI_LPG_REG_ADDR(lpg_config->base_addr,
+		QPNP_ENABLE_CONTROL), 1, chip);
 
 }
 
@@ -643,7 +673,8 @@
 
 	rc = qpnp_lpg_save_and_write(val, mask,
 		&pwm->chip->qpnp_lpg_registers[QPNP_RAMP_STEP_DURATION_LSB],
-		lpg_config->base_addr, QPNP_RAMP_STEP_DURATION_LSB, 1, chip);
+		SPMI_LPG_REG_ADDR(lpg_config->base_addr,
+		QPNP_RAMP_STEP_DURATION_LSB), 1, chip);
 	if (rc)
 		return rc;
 
@@ -654,7 +685,8 @@
 
 	return qpnp_lpg_save_and_write(val, mask,
 		&pwm->chip->qpnp_lpg_registers[QPNP_RAMP_STEP_DURATION_MSB],
-		lpg_config->base_addr, QPNP_RAMP_STEP_DURATION_MSB, 1, chip);
+		SPMI_LPG_REG_ADDR(lpg_config->base_addr,
+		QPNP_RAMP_STEP_DURATION_MSB), 1, chip);
 }
 
 static int qpnp_lpg_configure_pause(struct pwm_device *pwm)
@@ -671,7 +703,8 @@
 
 		rc = qpnp_lpg_save_and_write(value, mask,
 		&pwm->chip->qpnp_lpg_registers[QPNP_PAUSE_HI_MULTIPLIER_LSB],
-		lpg_config->base_addr, QPNP_PAUSE_HI_MULTIPLIER_LSB, 1, chip);
+		SPMI_LPG_REG_ADDR(lpg_config->base_addr,
+		QPNP_PAUSE_HI_MULTIPLIER_LSB), 1, chip);
 		if (rc)
 			return rc;
 
@@ -683,14 +716,16 @@
 
 		rc = qpnp_lpg_save_and_write(value, mask,
 		&pwm->chip->qpnp_lpg_registers[QPNP_PAUSE_HI_MULTIPLIER_MSB],
-		lpg_config->base_addr, QPNP_PAUSE_HI_MULTIPLIER_MSB, 1, chip);
+		SPMI_LPG_REG_ADDR(lpg_config->base_addr,
+		QPNP_PAUSE_HI_MULTIPLIER_MSB), 1, chip);
 	} else {
 		value = 0;
 		mask = QPNP_PAUSE_HI_MULTIPLIER_LSB_MASK;
 
 		rc = qpnp_lpg_save_and_write(value, mask,
 		&pwm->chip->qpnp_lpg_registers[QPNP_PAUSE_HI_MULTIPLIER_LSB],
-		lpg_config->base_addr, QPNP_PAUSE_HI_MULTIPLIER_LSB, 1, chip);
+		SPMI_LPG_REG_ADDR(lpg_config->base_addr,
+		QPNP_PAUSE_HI_MULTIPLIER_LSB), 1, chip);
 		if (rc)
 			return rc;
 
@@ -698,7 +733,8 @@
 
 		rc = qpnp_lpg_save_and_write(value, mask,
 		&pwm->chip->qpnp_lpg_registers[QPNP_PAUSE_HI_MULTIPLIER_MSB],
-		lpg_config->base_addr, QPNP_PAUSE_HI_MULTIPLIER_MSB, 1, chip);
+		SPMI_LPG_REG_ADDR(lpg_config->base_addr,
+		QPNP_PAUSE_HI_MULTIPLIER_MSB), 1, chip);
 		if (rc)
 			return rc;
 
@@ -710,7 +746,8 @@
 
 		rc = qpnp_lpg_save_and_write(value, mask,
 		&pwm->chip->qpnp_lpg_registers[QPNP_PAUSE_LO_MULTIPLIER_LSB],
-		lpg_config->base_addr, QPNP_PAUSE_LO_MULTIPLIER_LSB, 1, chip);
+		SPMI_LPG_REG_ADDR(lpg_config->base_addr,
+		QPNP_PAUSE_LO_MULTIPLIER_LSB), 1, chip);
 		if (rc)
 			return rc;
 
@@ -722,14 +759,16 @@
 
 		rc = qpnp_lpg_save_and_write(value, mask,
 		&pwm->chip->qpnp_lpg_registers[QPNP_PAUSE_LO_MULTIPLIER_MSB],
-		lpg_config->base_addr, QPNP_PAUSE_LO_MULTIPLIER_MSB, 1, chip);
+		SPMI_LPG_REG_ADDR(lpg_config->base_addr,
+		QPNP_PAUSE_LO_MULTIPLIER_MSB), 1, chip);
 	} else {
 		value = 0;
 		mask = QPNP_PAUSE_LO_MULTIPLIER_LSB_MASK;
 
 		rc = qpnp_lpg_save_and_write(value, mask,
 		&pwm->chip->qpnp_lpg_registers[QPNP_PAUSE_LO_MULTIPLIER_LSB],
-		lpg_config->base_addr, QPNP_PAUSE_LO_MULTIPLIER_LSB, 1, chip);
+		SPMI_LPG_REG_ADDR(lpg_config->base_addr,
+		QPNP_PAUSE_LO_MULTIPLIER_LSB), 1, chip);
 		if (rc)
 			return rc;
 
@@ -737,7 +776,8 @@
 
 		rc = qpnp_lpg_save_and_write(value, mask,
 		&pwm->chip->qpnp_lpg_registers[QPNP_PAUSE_LO_MULTIPLIER_MSB],
-		lpg_config->base_addr, QPNP_PAUSE_LO_MULTIPLIER_MSB, 1, chip);
+		SPMI_LPG_REG_ADDR(lpg_config->base_addr,
+		QPNP_PAUSE_LO_MULTIPLIER_MSB), 1, chip);
 		return rc;
 	}
 
@@ -757,7 +797,8 @@
 
 	rc = qpnp_lpg_save_and_write(value, mask,
 		&pwm->chip->qpnp_lpg_registers[QPNP_HI_INDEX],
-		lpg_config->base_addr, QPNP_HI_INDEX, 1, chip);
+		SPMI_LPG_REG_ADDR(lpg_config->base_addr,
+		QPNP_HI_INDEX), 1, chip);
 	if (rc)
 		return rc;
 
@@ -766,7 +807,8 @@
 
 	rc = qpnp_lpg_save_and_write(value, mask,
 		&pwm->chip->qpnp_lpg_registers[QPNP_LO_INDEX],
-		lpg_config->base_addr, QPNP_LO_INDEX, 1, chip);
+		SPMI_LPG_REG_ADDR(lpg_config->base_addr,
+		QPNP_LO_INDEX), 1, chip);
 
 	return rc;
 }
@@ -808,72 +850,97 @@
 	return rc;
 }
 
-static int qpnp_lpg_enable_lut(struct pwm_device *pwm)
+static int qpnp_lpg_configure_lut_state(struct pwm_device *pwm,
+				enum qpnp_lut_state state)
 {
 	struct qpnp_lpg_config	*lpg_config = &pwm->chip->lpg_config;
 	struct qpnp_lpg_chip	*chip = pwm->chip;
-	u8			value, mask;
+	u8			value1, value2, mask1, mask2;
+	u8			*reg1, *reg2;
+	u16			addr;
+	int			rc;
 
-	value = pwm->chip->qpnp_lpg_registers[QPNP_RAMP_CONTROL];
+	value1 = pwm->chip->qpnp_lpg_registers[QPNP_RAMP_CONTROL];
+	reg1 = &pwm->chip->qpnp_lpg_registers[QPNP_RAMP_CONTROL];
+	reg2 = &pwm->chip->qpnp_lpg_registers[QPNP_ENABLE_CONTROL];
+	mask2 = QPNP_EN_PWM_HIGH_MASK | QPNP_EN_PWM_LO_MASK |
+		QPNP_EN_PWM_OUTPUT_MASK | QPNP_PWM_SRC_SELECT_MASK |
+					QPNP_PWM_EN_RAMP_GEN_MASK;
 
-	QPNP_ENABLE_LUT(value);
+	switch (chip->revision) {
+	case QPNP_LPG_REVISION_0:
+		if (state == QPNP_LUT_ENABLE) {
+			QPNP_ENABLE_LUT_V0(value1);
+			value2 = QPNP_ENABLE_LPG_MODE;
+		} else {
+			QPNP_DISABLE_LUT_V0(value1);
+			value2 = QPNP_DISABLE_LPG_MODE;
+		}
+		mask1 = QPNP_RAMP_START_MASK;
+		addr = SPMI_LPG_REG_ADDR(lpg_config->base_addr,
+					QPNP_RAMP_CONTROL);
+		break;
+	case QPNP_LPG_REVISION_1:
+		if (state == QPNP_LUT_ENABLE) {
+			QPNP_ENABLE_LUT_V1(value1, pwm->pwm_config.channel_id);
+			value2 = QPNP_ENABLE_LPG_MODE;
+		} else {
+			QPNP_DISABLE_LUT_V1(value1, pwm->pwm_config.channel_id);
+			value2 = QPNP_DISABLE_LPG_MODE;
+		}
+		mask1 = BIT(pwm->pwm_config.channel_id);
+		addr = lpg_config->lut_base_addr +
+			SPMI_LPG_REV1_RAMP_CONTROL_OFFSET;
+		break;
+	default:
+		pr_err("Invalid LPG revision\n");
+		return -EINVAL;
+	}
 
-	mask = QPNP_RAMP_START_MASK;
+	rc = qpnp_lpg_save_and_write(value1, mask1, reg1,
+					addr, 1, chip);
+	if (rc)
+		return rc;
+	addr = SPMI_LPG_REG_ADDR(lpg_config->base_addr,
+				QPNP_ENABLE_CONTROL);
+	return qpnp_lpg_save_and_write(value2, mask2, reg2,
+					addr, 1, chip);
 
-	return qpnp_lpg_save_and_write(value, mask,
-		&pwm->chip->qpnp_lpg_registers[QPNP_RAMP_CONTROL],
-		lpg_config->base_addr, QPNP_RAMP_CONTROL, 1, chip);
 }
 
-static int qpnp_disable_lut(struct pwm_device *pwm)
+static int qpnp_lpg_configure_pwm_state(struct pwm_device *pwm,
+					enum qpnp_pwm_state state)
 {
 	struct qpnp_lpg_config	*lpg_config = &pwm->chip->lpg_config;
 	struct qpnp_lpg_chip	*chip = pwm->chip;
 	u8			value, mask;
+	int			rc;
 
-	value = pwm->chip->qpnp_lpg_registers[QPNP_RAMP_CONTROL];
+	if (state == QPNP_PWM_ENABLE)
+		value = QPNP_ENABLE_PWM_MODE;
+	else
+		value = QPNP_DISABLE_PWM_MODE;
 
-	QPNP_DISABLE_LUT(value);
+	mask = QPNP_EN_PWM_HIGH_MASK | QPNP_EN_PWM_LO_MASK |
+		QPNP_EN_PWM_OUTPUT_MASK | QPNP_PWM_SRC_SELECT_MASK |
+					QPNP_PWM_EN_RAMP_GEN_MASK;
 
-	mask = QPNP_RAMP_START_MASK;
-
-	return qpnp_lpg_save_and_write(value, mask,
-		&pwm->chip->qpnp_lpg_registers[QPNP_RAMP_CONTROL],
-		lpg_config->base_addr, QPNP_RAMP_CONTROL, 1, chip);
-}
-
-static int qpnp_lpg_enable_pwm(struct pwm_device *pwm)
-{
-	struct qpnp_lpg_config	*lpg_config = &pwm->chip->lpg_config;
-	struct qpnp_lpg_chip	*chip = pwm->chip;
-	u8			value, mask;
-
-	value = pwm->chip->qpnp_lpg_registers[QPNP_ENABLE_CONTROL];
-
-	QPNP_ENABLE_PWM(value);
-
-	mask = QPNP_EN_PWM_OUTPUT_MASK;
-
-	return qpnp_lpg_save_and_write(value, mask,
+	rc = qpnp_lpg_save_and_write(value, mask,
 		&pwm->chip->qpnp_lpg_registers[QPNP_ENABLE_CONTROL],
-		lpg_config->base_addr, QPNP_RAMP_CONTROL, 1, chip);
-}
+		SPMI_LPG_REG_ADDR(lpg_config->base_addr,
+		QPNP_ENABLE_CONTROL), 1, chip);
+	if (rc)
+		goto out;
 
-static int qpnp_disable_pwm(struct pwm_device *pwm)
-{
-	struct qpnp_lpg_config	*lpg_config = &pwm->chip->lpg_config;
-	struct qpnp_lpg_chip	*chip = pwm->chip;
-	u8			value, mask;
+	/*
+	 * Due to LPG hardware bug, in the PWM mode, having enabled PWM,
+	 * We have to write PWM values one more time.
+	 */
+	if (state == QPNP_PWM_ENABLE)
+		return qpnp_lpg_save_pwm_value(pwm);
 
-	value = pwm->chip->qpnp_lpg_registers[QPNP_ENABLE_CONTROL];
-
-	QPNP_DISABLE_PWM(value);
-
-	mask = QPNP_EN_PWM_OUTPUT_MASK;
-
-	return qpnp_lpg_save_and_write(value, mask,
-		&pwm->chip->qpnp_lpg_registers[QPNP_ENABLE_CONTROL],
-		lpg_config->base_addr, QPNP_RAMP_CONTROL, 1, chip);
+out:
+	return rc;
 }
 
 static int _pwm_config(struct pwm_device *pwm, int duty_us, int period_us)
@@ -999,18 +1066,23 @@
 {
 	int rc;
 	struct qpnp_lpg_chip *chip;
+	unsigned long flags;
 
 	chip = pwm->chip;
 
-	mutex_lock(&pwm->chip->lpg_mutex);
+	spin_lock_irqsave(&pwm->chip->lpg_lock, flags);
 
 	if (QPNP_IS_PWM_CONFIG_SELECTED(
 		chip->qpnp_lpg_registers[QPNP_ENABLE_CONTROL]))
-		rc = qpnp_lpg_enable_pwm(pwm);
+		rc = qpnp_lpg_configure_pwm_state(pwm, QPNP_PWM_ENABLE);
 	else
-		rc = qpnp_lpg_enable_lut(pwm);
+		rc = qpnp_lpg_configure_lut_state(pwm, QPNP_LUT_ENABLE);
 
-	mutex_unlock(&pwm->chip->lpg_mutex);
+	spin_unlock_irqrestore(&pwm->chip->lpg_lock, flags);
+
+	if (rc)
+		pr_err("Failed to enable PWM channel: %d\n",
+				pwm->pwm_config.channel_id);
 
 	return rc;
 }
@@ -1025,6 +1097,7 @@
 {
 	struct qpnp_lpg_chip	*chip;
 	struct pwm_device	*pwm;
+	unsigned long		flags;
 
 	chip = radix_tree_lookup(&lpg_dev_tree, pwm_id);
 
@@ -1034,7 +1107,7 @@
 		return ERR_PTR(-EINVAL);
 	}
 
-	mutex_lock(&chip->lpg_mutex);
+	spin_lock_irqsave(&chip->lpg_lock, flags);
 
 	pwm = &chip->pwm_dev;
 
@@ -1048,7 +1121,7 @@
 		pwm->pwm_config.lable  = lable;
 	}
 
-	mutex_unlock(&chip->lpg_mutex);
+	spin_unlock_irqrestore(&chip->lpg_lock, flags);
 
 	return pwm;
 }
@@ -1061,24 +1134,25 @@
 void pwm_free(struct pwm_device *pwm)
 {
 	struct qpnp_pwm_config	*pwm_config;
+	unsigned long		flags;
 
 	if (pwm == NULL || IS_ERR(pwm) || pwm->chip == NULL) {
 		pr_err("Invalid pwm handle or no pwm_chip\n");
 		return;
 	}
 
-	mutex_lock(&pwm->chip->lpg_mutex);
+	spin_lock_irqsave(&pwm->chip->lpg_lock, flags);
 
 	pwm_config = &pwm->pwm_config;
 
 	if (pwm_config->in_use) {
-		qpnp_disable_pwm(pwm);
-		qpnp_disable_lut(pwm);
+		qpnp_lpg_configure_pwm_state(pwm, QPNP_PWM_DISABLE);
+		qpnp_lpg_configure_lut_state(pwm, QPNP_LUT_DISABLE);
 		pwm_config->in_use = 0;
 		pwm_config->lable = NULL;
 	}
 
-	mutex_unlock(&pwm->chip->lpg_mutex);
+	spin_unlock_irqrestore(&pwm->chip->lpg_lock, flags);
 }
 EXPORT_SYMBOL_GPL(pwm_free);
 
@@ -1091,6 +1165,7 @@
 int pwm_config(struct pwm_device *pwm, int duty_us, int period_us)
 {
 	int rc;
+	unsigned long flags;
 
 	if (pwm == NULL || IS_ERR(pwm) ||
 		duty_us > period_us ||
@@ -1103,9 +1178,12 @@
 	if (!pwm->pwm_config.in_use)
 		return -EINVAL;
 
-	mutex_lock(&pwm->chip->lpg_mutex);
+	spin_lock_irqsave(&pwm->chip->lpg_lock, flags);
 	rc = _pwm_config(pwm, duty_us, period_us);
-	mutex_unlock(&pwm->chip->lpg_mutex);
+	spin_unlock_irqrestore(&pwm->chip->lpg_lock, flags);
+
+	if (rc)
+		pr_err("Failed to configure PWM mode\n");
 
 	return rc;
 }
@@ -1143,13 +1221,15 @@
 {
 	struct qpnp_pwm_config	*pwm_config;
 	struct qpnp_lpg_chip	*chip;
+	unsigned long		flags;
+	int rc = 0;
 
 	if (pwm == NULL || IS_ERR(pwm) || pwm->chip == NULL) {
 		pr_err("Invalid pwm handle or no pwm_chip\n");
 		return;
 	}
 
-	mutex_lock(&pwm->chip->lpg_mutex);
+	spin_lock_irqsave(&pwm->chip->lpg_lock, flags);
 
 	chip = pwm->chip;
 	pwm_config = &pwm->pwm_config;
@@ -1157,12 +1237,18 @@
 	if (pwm_config->in_use) {
 		if (QPNP_IS_PWM_CONFIG_SELECTED(
 			chip->qpnp_lpg_registers[QPNP_ENABLE_CONTROL]))
-			qpnp_disable_pwm(pwm);
+			rc = qpnp_lpg_configure_pwm_state(pwm,
+						QPNP_PWM_DISABLE);
 		else
-			qpnp_disable_lut(pwm);
+			rc = qpnp_lpg_configure_lut_state(pwm,
+						QPNP_LUT_DISABLE);
 	}
 
-	mutex_unlock(&pwm->chip->lpg_mutex);
+	spin_unlock_irqrestore(&pwm->chip->lpg_lock, flags);
+
+	if (rc)
+		pr_err("Failed to disable PWM channel: %d\n",
+					pwm_config->channel_id);
 }
 EXPORT_SYMBOL_GPL(pwm_disable);
 
@@ -1174,6 +1260,7 @@
 int pwm_change_mode(struct pwm_device *pwm, enum pm_pwm_mode mode)
 {
 	int rc;
+	unsigned long flags;
 
 	if (pwm == NULL || IS_ERR(pwm) || pwm->chip == NULL) {
 		pr_err("Invalid pwm handle or no pwm_chip\n");
@@ -1185,15 +1272,17 @@
 		return -EINVAL;
 	}
 
-	mutex_lock(&pwm->chip->lpg_mutex);
+	spin_lock_irqsave(&pwm->chip->lpg_lock, flags);
 
 	if (mode)
 		rc = qpnp_configure_lpg_control(pwm);
 	else
 		rc = qpnp_configure_pwm_control(pwm);
 
-	mutex_unlock(&pwm->chip->lpg_mutex);
+	spin_unlock_irqrestore(&pwm->chip->lpg_lock, flags);
 
+	if (rc)
+		pr_err("Failed to change the mode\n");
 	return rc;
 }
 EXPORT_SYMBOL_GPL(pwm_change_mode);
@@ -1210,6 +1299,7 @@
 	struct qpnp_pwm_config	*pwm_config;
 	struct qpnp_lpg_config	*lpg_config;
 	struct qpnp_lpg_chip	*chip;
+	unsigned long		flags;
 	int			rc = 0;
 
 	if (pwm == NULL || IS_ERR(pwm) || period == NULL)
@@ -1217,7 +1307,7 @@
 	if (pwm->chip == NULL)
 		return -ENODEV;
 
-	mutex_lock(&pwm->chip->lpg_mutex);
+	spin_lock_irqsave(&pwm->chip->lpg_lock, flags);
 
 	chip = pwm->chip;
 	pwm_config = &pwm->pwm_config;
@@ -1256,7 +1346,7 @@
 	}
 
 out_unlock:
-	mutex_unlock(&pwm->chip->lpg_mutex);
+	spin_unlock_irqrestore(&pwm->chip->lpg_lock, flags);
 	return rc;
 }
 EXPORT_SYMBOL(pwm_config_period);
@@ -1270,21 +1360,27 @@
 {
 	struct qpnp_lpg_config	*lpg_config;
 	struct qpnp_pwm_config	*pwm_config;
+	unsigned long		flags;
 	int			rc = 0;
 
-	if (pwm == NULL || IS_ERR(pwm))
+	if (pwm == NULL || IS_ERR(pwm)) {
+		pr_err("Invalid parameter passed\n");
 		return -EINVAL;
+	}
 
-	if (pwm->chip == NULL)
+	if (pwm->chip == NULL) {
+		pr_err("Invalid device handle\n");
 		return -ENODEV;
+	}
 
 	lpg_config = &pwm->chip->lpg_config;
 	pwm_config = &pwm->pwm_config;
 
-	mutex_lock(&pwm->chip->lpg_mutex);
+	spin_lock_irqsave(&pwm->chip->lpg_lock, flags);
 
 	if (!pwm_config->in_use || !pwm_config->pwm_period) {
 		rc = -EINVAL;
+		pr_err("PWM channel isn't in use or period value missing\n");
 		goto out_unlock;
 	}
 
@@ -1300,7 +1396,7 @@
 						pwm_config->channel_id, rc);
 
 out_unlock:
-	mutex_unlock(&pwm->chip->lpg_mutex);
+	spin_unlock_irqrestore(&pwm->chip->lpg_lock, flags);
 	return rc;
 }
 EXPORT_SYMBOL_GPL(pwm_config_pwm_value);
@@ -1315,6 +1411,7 @@
 int pwm_lut_config(struct pwm_device *pwm, int period_us,
 		int duty_pct[], struct lut_params lut_params)
 {
+	unsigned long flags;
 	int rc = 0;
 
 	if (pwm == NULL || IS_ERR(pwm) || !lut_params.idx_len) {
@@ -1348,11 +1445,14 @@
 		return -EINVAL;
 	}
 
-	mutex_lock(&pwm->chip->lpg_mutex);
+	spin_lock_irqsave(&pwm->chip->lpg_lock, flags);
 
 	rc = _pwm_lut_config(pwm, period_us, duty_pct, lut_params);
 
-	mutex_unlock(&pwm->chip->lpg_mutex);
+	spin_unlock_irqrestore(&pwm->chip->lpg_lock, flags);
+
+	if (rc)
+		pr_err("Failed to configure LUT\n");
 
 	return rc;
 }
@@ -1583,7 +1683,7 @@
 		return -ENOMEM;
 	}
 
-	mutex_init(&chip->lpg_mutex);
+	spin_lock_init(&chip->lpg_lock);
 
 	chip->spmi_dev = spmi;
 	chip->pwm_dev.chip = chip;
@@ -1596,6 +1696,19 @@
 
 	id = chip->pwm_dev.pwm_config.channel_id;
 
+	spmi_ext_register_readl(chip->spmi_dev->ctrl,
+		chip->spmi_dev->sid,
+		chip->lpg_config.base_addr + SPMI_LPG_REVISION2_OFFSET,
+		(u8 *) &chip->revision, 1);
+
+	if (chip->revision < QPNP_LPG_REVISION_0 ||
+		chip->revision > QPNP_LPG_REVISION_1) {
+		pr_err("Unknown LPG revision detected, rev:%d\n",
+						chip->revision);
+		rc = -EINVAL;
+		goto failed_insert;
+	}
+
 	rc = radix_tree_insert(&lpg_dev_tree, id, chip);
 
 	if (rc) {
@@ -1610,7 +1723,6 @@
 	kfree(chip->lpg_config.lut_config.duty_pct_list);
 failed_config:
 	dev_set_drvdata(&spmi->dev, NULL);
-	mutex_destroy(&chip->lpg_mutex);
 	kfree(chip);
 	return rc;
 }
@@ -1627,7 +1739,6 @@
 	if (chip) {
 		lpg_config = &chip->lpg_config;
 		kfree(lpg_config->lut_config.duty_pct_list);
-		mutex_destroy(&chip->lpg_mutex);
 		kfree(chip);
 	}
 
diff --git a/drivers/platform/msm/sps/sps.c b/drivers/platform/msm/sps/sps.c
index 5bbcc84..25febff 100644
--- a/drivers/platform/msm/sps/sps.c
+++ b/drivers/platform/msm/sps/sps.c
@@ -1828,19 +1828,6 @@
 	if (virt_addr != NULL)
 		bam->props.virt_addr = virt_addr;
 
-	if ((bam_props->manage & SPS_BAM_MGR_DEVICE_REMOTE) != 0 &&
-	    (bam_props->manage & SPS_BAM_MGR_MULTI_EE) != 0 &&
-	    bam_props->ee == 0) {
-		/*
-		 * BAM global is owned by a remote processor, so force EE index
-		 * to a non-zero value to insure EE zero globals are not
-		 * modified.
-		 */
-		SPS_DBG2("sps:Setting EE for BAM %x to non-zero",
-				  bam_props->phys_addr);
-		bam->props.ee = 1;
-	}
-
 	ok = sps_bam_device_init(bam);
 	mutex_unlock(&bam->lock);
 	if (ok) {
diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
index c474e36..7b7e05e 100644
--- a/drivers/power/Kconfig
+++ b/drivers/power/Kconfig
@@ -290,8 +290,7 @@
 
 config BATTERY_MSM
 	tristate "MSM battery"
-	depends on ARCH_MSM
-	default m
+	depends on ARCH_MSM && MSM_ONCRPCROUTER
 	help
 	  Say Y to enable support for the battery in Qualcomm MSM.
 
@@ -344,6 +343,18 @@
 	  SMB349 be operated as a slave device via the power supply
 	  framework.
 
+config SMB350_CHARGER
+	tristate "smb350 charger"
+	depends on I2C
+	help
+	  Say Y to enable battery charging by SMB350 switching mode based
+	  external charger. The device supports stack-cell battery charging.
+	  The driver configures the device volatile parameters
+	  and the charger device works autonomously.
+	  The driver supports charger-enable and charger-suspend/resume.
+	  The driver reports the charger status via the power supply framework.
+	  A charger status change triggers an IRQ via the device STAT pin.
+
 config BATTERY_MSM_FAKE
 	tristate "Fake MSM battery"
 	depends on ARCH_MSM && BATTERY_MSM
@@ -389,6 +400,18 @@
 	help
 	  Say Y here to enable Test sysfs Interface for BQ27520 Drivers.
 
+config BATTERY_BQ28400
+	tristate "BQ28400 battery driver"
+	depends on I2C
+	default n
+	help
+	  Say Y here to enable support for batteries with BQ28400 (I2C) chips.
+	  The bq28400 Texas Instruments Inc device monitors the battery
+	  charging/discharging status via Rsens resistor, typically 10 mohm.
+	  It monitors the battery temperature via Thermistor.
+	  The device monitors the battery level (Relative-State-Of-Charge).
+	  The device is SBS compliant, providing battery info over I2C.
+
 config PM8921_CHARGER
 	tristate "PM8921 Charger driver"
 	depends on MFD_PM8921_CORE
diff --git a/drivers/power/Makefile b/drivers/power/Makefile
index 3521cfd..3e74f35 100644
--- a/drivers/power/Makefile
+++ b/drivers/power/Makefile
@@ -48,10 +48,12 @@
 obj-$(CONFIG_PM8058_CHARGER)    += pmic8058-charger.o
 obj-$(CONFIG_ISL9519_CHARGER)   += isl9519q.o
 obj-$(CONFIG_SMB349_CHARGER)   += smb349.o
+obj-$(CONFIG_SMB350_CHARGER)   += smb350_charger.o
 obj-$(CONFIG_PM8058_FIX_USB)    += pm8058_usb_fix.o
 obj-$(CONFIG_BATTERY_QCIBAT)    += qci_battery.o
 obj-$(CONFIG_BATTERY_BQ27520)	+= bq27520_fuelgauger.o
 obj-$(CONFIG_BATTERY_BQ27541)	+= bq27541_fuelgauger.o
+obj-$(CONFIG_BATTERY_BQ28400)	+= bq28400_battery.o
 obj-$(CONFIG_SMB137B_CHARGER)   += smb137b.o
 obj-$(CONFIG_PM8XXX_CCADC)	+= pm8xxx-ccadc.o
 obj-$(CONFIG_PM8921_BMS)	+= pm8921-bms.o
diff --git a/drivers/power/bq28400_battery.c b/drivers/power/bq28400_battery.c
new file mode 100644
index 0000000..39eac60
--- /dev/null
+++ b/drivers/power/bq28400_battery.c
@@ -0,0 +1,930 @@
+/* Copyright (c) 2012 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+/*
+ * High Level description:
+ * http://www.ti.com/lit/ds/symlink/bq28400.pdf
+ * Thechnical Reference:
+ * http://www.ti.com/lit/ug/sluu431/sluu431.pdf
+ */
+
+#define pr_fmt(fmt)	"%s: " fmt, __func__
+
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/debugfs.h>
+#include <linux/workqueue.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/power_supply.h>
+#include <linux/bitops.h>
+#include <linux/regulator/consumer.h>
+#include <linux/printk.h>
+
+#define BQ28400_NAME "bq28400"
+#define BQ28400_REV  "1.0"
+
+/* SBS Commands (page 63)  */
+
+#define SBS_MANUFACTURER_ACCESS		0x00
+#define SBS_BATTERY_MODE		0x03
+#define SBS_TEMPERATURE			0x08
+#define SBS_VOLTAGE			0x09
+#define SBS_CURRENT			0x0A
+#define SBS_AVG_CURRENT			0x0B
+#define SBS_MAX_ERROR			0x0C
+#define SBS_RSOC			0x0D	/* Relative State Of Charge */
+#define SBS_REMAIN_CAPACITY		0x0F
+#define SBS_FULL_CAPACITY		0x10
+#define SBS_CHG_CURRENT			0x14
+#define SBS_CHG_VOLTAGE			0x15
+#define SBS_BATTERY_STATUS		0x16
+#define SBS_CYCLE_COUNT			0x17
+#define SBS_DESIGN_CAPACITY		0x18
+#define SBS_DESIGN_VOLTAGE		0x19
+#define SBS_SPEC_INFO			0x1A
+#define SBS_MANUFACTURE_DATE		0x1B
+#define SBS_SERIAL_NUMBER		0x1C
+#define SBS_MANUFACTURER_NAME		0x20
+#define SBS_DEVICE_NAME			0x21
+#define SBS_DEVICE_CHEMISTRY		0x22
+#define SBS_MANUFACTURER_DATA		0x23
+#define SBS_AUTHENTICATE		0x2F
+#define SBS_CELL_VOLTAGE1		0x3E
+#define SBS_CELL_VOLTAGE2		0x3F
+
+/* Extended SBS Commands (page 71)  */
+
+#define SBS_FET_CONTROL			0x46
+#define SBS_SAFETY_ALERT		0x50
+#define SBS_SAFETY_STATUS		0x51
+#define SBS_PE_ALERT			0x52
+#define SBS_PE_STATUS			0x53
+#define SBS_OPERATION_STATUS		0x54
+#define SBS_CHARGING_STATUS		0x55
+#define SBS_FET_STATUS			0x56
+#define SBS_PACK_VOLTAGE		0x5A
+#define SBS_TS0_TEMPERATURE		0x5E
+#define SBS_FULL_ACCESS_KEY		0x61
+#define SBS_PF_KEY			0x62
+#define SBS_AUTH_KEY3			0x63
+#define SBS_AUTH_KEY2			0x64
+#define SBS_AUTH_KEY1			0x65
+#define SBS_AUTH_KEY0			0x66
+#define SBS_MANUFACTURER_INFO		0x70
+#define SBS_SENSE_RESISTOR		0x71
+#define SBS_TEMP_RANGE			0x72
+
+/* SBS Sub-Commands (16 bits) */
+/* SBS_MANUFACTURER_ACCESS CMD */
+#define SUBCMD_DEVICE_TYPE		0x01
+#define SUBCMD_FIRMWARE_VERSION		0x02
+#define SUBCMD_HARDWARE_VERSION		0x03
+#define SUBCMD_DF_CHECKSUM		0x04
+#define SUBCMD_EDV			0x05
+#define SUBCMD_CHEMISTRY_ID		0x08
+
+/* SBS_CHARGING_STATUS */
+#define CHG_STATUS_BATTERY_DEPLETED	BIT(0)
+#define CHG_STATUS_OVERCHARGE		BIT(1)
+#define CHG_STATUS_OVERCHARGE_CURRENT	BIT(2)
+#define CHG_STATUS_OVERCHARGE_VOLTAGE	BIT(3)
+#define CHG_STATUS_CELL_BALANCING	BIT(6)
+#define CHG_STATUS_HOT_TEMP_CHARGING	BIT(8)
+#define CHG_STATUS_STD1_TEMP_CHARGING	BIT(9)
+#define CHG_STATUS_STD2_TEMP_CHARGING	BIT(10)
+#define CHG_STATUS_LOW_TEMP_CHARGING	BIT(11)
+#define CHG_STATUS_PRECHARGING_EXIT	BIT(13)
+#define CHG_STATUS_SUSPENDED		BIT(14)
+#define CHG_STATUS_DISABLED		BIT(15)
+
+/* SBS_FET_STATUS */
+#define FET_STATUS_DISCHARGE		BIT(1)
+#define FET_STATUS_CHARGE		BIT(2)
+#define FET_STATUS_PRECHARGE		BIT(3)
+
+/* SBS_BATTERY_STATUS */
+#define BAT_STATUS_SBS_ERROR		0x0F
+#define BAT_STATUS_EMPTY		BIT(4)
+#define BAT_STATUS_FULL			BIT(5)
+#define BAT_STATUS_DISCHARGING		BIT(6)
+#define BAT_STATUS_OVER_TEMPERATURE	BIT(12)
+#define BAT_STATUS_OVER_CHARGED		BIT(15)
+
+#define ZERO_DEGREE_CELSIUS_IN_TENTH_KELVIN   (-2731)
+#define BQ_TERMINATION_CURRENT_MA	200
+
+#define BQ_MAX_STR_LEN	32
+
+struct bq28400_device {
+	struct i2c_client	*client;
+	struct delayed_work	periodic_user_space_update_work;
+	struct dentry		*dent;
+	struct power_supply	batt_psy;
+	struct power_supply	*dc_psy;
+	bool			is_charging_enabled;
+};
+
+static struct bq28400_device *bq28400_dev;
+
+static enum power_supply_property pm_power_props[] = {
+	POWER_SUPPLY_PROP_STATUS,
+	POWER_SUPPLY_PROP_CHARGE_TYPE,
+	POWER_SUPPLY_PROP_PRESENT,
+	POWER_SUPPLY_PROP_TECHNOLOGY,
+	POWER_SUPPLY_PROP_VOLTAGE_NOW,
+	POWER_SUPPLY_PROP_CAPACITY,
+	POWER_SUPPLY_PROP_CURRENT_NOW,
+	POWER_SUPPLY_PROP_CURRENT_AVG,
+	POWER_SUPPLY_PROP_TEMP,
+	POWER_SUPPLY_PROP_CHARGE_FULL,
+	POWER_SUPPLY_PROP_CHARGE_NOW,
+	POWER_SUPPLY_PROP_MODEL_NAME,
+	POWER_SUPPLY_PROP_MANUFACTURER,
+};
+
+struct debug_reg {
+	char	*name;
+	u8	reg;
+	u16	subcmd;
+};
+
+#define BQ28400_DEBUG_REG(x) {#x, SBS_##x, 0}
+#define BQ28400_DEBUG_SUBREG(x, y) {#y, SBS_##x, SUBCMD_##y}
+
+/* Note: Some register can be read only in Unsealed mode */
+static struct debug_reg bq28400_debug_regs[] = {
+	BQ28400_DEBUG_REG(MANUFACTURER_ACCESS),
+	BQ28400_DEBUG_REG(BATTERY_MODE),
+	BQ28400_DEBUG_REG(TEMPERATURE),
+	BQ28400_DEBUG_REG(VOLTAGE),
+	BQ28400_DEBUG_REG(CURRENT),
+	BQ28400_DEBUG_REG(AVG_CURRENT),
+	BQ28400_DEBUG_REG(MAX_ERROR),
+	BQ28400_DEBUG_REG(RSOC),
+	BQ28400_DEBUG_REG(REMAIN_CAPACITY),
+	BQ28400_DEBUG_REG(FULL_CAPACITY),
+	BQ28400_DEBUG_REG(CHG_CURRENT),
+	BQ28400_DEBUG_REG(CHG_VOLTAGE),
+	BQ28400_DEBUG_REG(BATTERY_STATUS),
+	BQ28400_DEBUG_REG(CYCLE_COUNT),
+	BQ28400_DEBUG_REG(DESIGN_CAPACITY),
+	BQ28400_DEBUG_REG(DESIGN_VOLTAGE),
+	BQ28400_DEBUG_REG(SPEC_INFO),
+	BQ28400_DEBUG_REG(MANUFACTURE_DATE),
+	BQ28400_DEBUG_REG(SERIAL_NUMBER),
+	BQ28400_DEBUG_REG(MANUFACTURER_NAME),
+	BQ28400_DEBUG_REG(DEVICE_NAME),
+	BQ28400_DEBUG_REG(DEVICE_CHEMISTRY),
+	BQ28400_DEBUG_REG(MANUFACTURER_DATA),
+	BQ28400_DEBUG_REG(AUTHENTICATE),
+	BQ28400_DEBUG_REG(CELL_VOLTAGE1),
+	BQ28400_DEBUG_REG(CELL_VOLTAGE2),
+	BQ28400_DEBUG_REG(SAFETY_ALERT),
+	BQ28400_DEBUG_REG(SAFETY_STATUS),
+	BQ28400_DEBUG_REG(PE_ALERT),
+	BQ28400_DEBUG_REG(PE_STATUS),
+	BQ28400_DEBUG_REG(OPERATION_STATUS),
+	BQ28400_DEBUG_REG(CHARGING_STATUS),
+	BQ28400_DEBUG_REG(FET_STATUS),
+	BQ28400_DEBUG_REG(FULL_ACCESS_KEY),
+	BQ28400_DEBUG_REG(PF_KEY),
+	BQ28400_DEBUG_REG(MANUFACTURER_INFO),
+	BQ28400_DEBUG_REG(SENSE_RESISTOR),
+	BQ28400_DEBUG_REG(TEMP_RANGE),
+	BQ28400_DEBUG_SUBREG(MANUFACTURER_ACCESS, DEVICE_TYPE),
+	BQ28400_DEBUG_SUBREG(MANUFACTURER_ACCESS, FIRMWARE_VERSION),
+	BQ28400_DEBUG_SUBREG(MANUFACTURER_ACCESS, HARDWARE_VERSION),
+	BQ28400_DEBUG_SUBREG(MANUFACTURER_ACCESS, DF_CHECKSUM),
+	BQ28400_DEBUG_SUBREG(MANUFACTURER_ACCESS, EDV),
+	BQ28400_DEBUG_SUBREG(MANUFACTURER_ACCESS, CHEMISTRY_ID),
+};
+
+static int bq28400_read_reg(struct i2c_client *client, u8 reg)
+{
+	int val;
+
+	val = i2c_smbus_read_word_data(client, reg);
+	if (val < 0)
+		pr_err("i2c read fail. reg = 0x%x.ret = %d.\n", reg, val);
+	else
+		pr_debug("reg = 0x%02X.val = 0x%04X.\n", reg , val);
+
+	return val;
+}
+
+static int bq28400_write_reg(struct i2c_client *client, u8 reg, u16 val)
+{
+	int ret;
+
+	ret = i2c_smbus_write_word_data(client, reg, val);
+	if (ret < 0)
+		pr_err("i2c read fail. reg = 0x%x.val = 0x%x.ret = %d.\n",
+		       reg, val, ret);
+	else
+		pr_debug("reg = 0x%02X.val = 0x%02X.\n", reg , val);
+
+	return ret;
+}
+
+static int bq28400_read_subcmd(struct i2c_client *client, u8 reg, u16 subcmd)
+{
+	int ret;
+	u8 buf[4];
+	u16 val = 0;
+
+	buf[0] = reg;
+	buf[1] = subcmd & 0xFF;
+	buf[2] = (subcmd >> 8) & 0xFF;
+
+	/* Control sub-command */
+	ret = i2c_master_send(client, buf, 3);
+	if (ret < 0) {
+		pr_err("i2c tx fail. reg = 0x%x.ret = %d.\n", reg, ret);
+		return ret;
+	}
+	udelay(66);
+
+	/* Read Result of subcmd */
+	ret = i2c_master_send(client, buf, 1);
+	memset(buf, 0xAA, sizeof(buf));
+	ret = i2c_master_recv(client, buf, 2);
+	if (ret < 0) {
+		pr_err("i2c rx fail. reg = 0x%x.ret = %d.\n", reg, ret);
+		return ret;
+	}
+	val = (buf[1] << 8) + buf[0];
+
+	pr_debug("reg = 0x%02X.subcmd = 0x%x.val = 0x%04X.\n",
+		 reg , subcmd, val);
+
+	return val;
+}
+
+static int bq28400_read_block(struct i2c_client *client, u8 reg,
+			      u8 len, u8 *buf)
+{
+	int ret;
+	u32 val;
+
+	ret = i2c_smbus_read_i2c_block_data(client, reg, len, buf);
+	val = buf[0] + (buf[1] << 8) + (buf[2] << 16) + (buf[3] << 24);
+
+	if (ret < 0)
+		pr_err("i2c read fail. reg = 0x%x.ret = %d.\n", reg, ret);
+	else
+		pr_debug("reg = 0x%02X.val = 0x%04X.\n", reg , val);
+
+	return val;
+}
+
+/*
+ * Read a string from a device.
+ * Returns string length on success or error on failure (negative value).
+ */
+static int bq28400_read_string(struct i2c_client *client, u8 reg, char *str,
+			       u8 max_len)
+{
+	int ret;
+	int len;
+
+	ret = bq28400_read_block(client, reg, max_len, str);
+	if (ret < 0)
+		return ret;
+
+	len = str[0]; /* Actual length */
+	if (len > max_len - 2) { /* reduce len byte and null */
+		pr_err("len = %d invalid.\n", len);
+		return -EINVAL;
+	}
+
+	memcpy(&str[0], &str[1], len); /* Move sting to the start */
+	str[len] = 0; /* put NULL after actual size */
+
+	pr_debug("len = %d.str = %s.\n", len, str);
+
+	return len;
+}
+
+#define BQ28400_INVALID_TEMPERATURE	-999
+/*
+ * Return the battery temperature in tenths of degree Celsius
+ * Or -99.9 C if something fails.
+ */
+static int bq28400_read_temperature(struct i2c_client *client)
+{
+	int temp;
+
+	/* temperature resolution 0.1 Kelvin */
+	temp = bq28400_read_reg(client, SBS_TEMPERATURE);
+	if (temp < 0)
+		return BQ28400_INVALID_TEMPERATURE;
+
+	temp = temp + ZERO_DEGREE_CELSIUS_IN_TENTH_KELVIN;
+
+	pr_debug("temp = %d C\n", temp/10);
+
+	return temp;
+}
+
+/*
+ * Return the battery Voltage in milivolts 0..20 V
+ * Or < 0 if something fails.
+ */
+static int bq28400_read_voltage(struct i2c_client *client)
+{
+	int mvolt = 0;
+
+	mvolt = bq28400_read_reg(client, SBS_VOLTAGE);
+	if (mvolt < 0)
+		return mvolt;
+
+	pr_debug("volt = %d mV.\n", mvolt);
+
+	return mvolt;
+}
+
+/*
+ * Return the battery Current in miliamps
+ * Or 0 if something fails.
+ * Positive current indicates charging
+ * Negative current indicates discharging.
+ * Current-now is calculated every second.
+ */
+static int bq28400_read_current(struct i2c_client *client)
+{
+	s16 current_ma = 0;
+
+	current_ma = bq28400_read_reg(client, SBS_CURRENT);
+
+	pr_debug("current = %d mA.\n", current_ma);
+
+	return current_ma;
+}
+
+/*
+ * Return the Average battery Current in miliamps
+ * Or 0 if something fails.
+ * Positive current indicates charging
+ * Negative current indicates discharging.
+ * Average Current is the rolling 1 minute average current.
+ */
+static int bq28400_read_avg_current(struct i2c_client *client)
+{
+	s16 current_ma = 0;
+
+	current_ma = bq28400_read_reg(client, SBS_AVG_CURRENT);
+
+	pr_debug("avg_current=%d mA.\n", current_ma);
+
+	return current_ma;
+}
+
+/*
+ * Return the battery Relative-State-Of-Charge 0..100 %
+ * Or 0 if something fails.
+ */
+static int bq28400_read_rsoc(struct i2c_client *client)
+{
+	int percentage = 0;
+
+	/* This register is only 1 byte */
+	percentage = i2c_smbus_read_byte_data(client, SBS_RSOC);
+
+	if (percentage < 0)
+		return 0;
+
+	pr_debug("percentage = %d.\n", percentage);
+
+	return percentage;
+}
+
+/*
+ * Return the battery Capacity in mAh.
+ * Or 0 if something fails.
+ */
+static int bq28400_read_full_capacity(struct i2c_client *client)
+{
+	int capacity = 0;
+
+	capacity = bq28400_read_reg(client, SBS_FULL_CAPACITY);
+	if (capacity < 0)
+		return 0;
+
+	pr_debug("full-capacity = %d mAh.\n", capacity);
+
+	return capacity;
+}
+
+/*
+ * Return the battery Capacity in mAh.
+ * Or 0 if something fails.
+ */
+static int bq28400_read_remain_capacity(struct i2c_client *client)
+{
+	int capacity = 0;
+
+	capacity = bq28400_read_reg(client, SBS_REMAIN_CAPACITY);
+	if (capacity < 0)
+		return 0;
+
+	pr_debug("remain-capacity = %d mAh.\n", capacity);
+
+	return capacity;
+}
+
+static int bq28400_enable_charging(struct bq28400_device *bq28400_dev,
+				   bool enable)
+{
+	int ret;
+	static bool is_charging_enabled;
+
+	if (bq28400_dev->dc_psy == NULL) {
+		bq28400_dev->dc_psy = power_supply_get_by_name("dc");
+		if (bq28400_dev->dc_psy == NULL) {
+			pr_err("fail to get dc-psy.\n");
+			return -ENODEV;
+		}
+	}
+
+	if (is_charging_enabled == enable) {
+		pr_debug("Charging enable already = %d.\n", enable);
+		return 0;
+	}
+
+	ret = power_supply_set_online(bq28400_dev->dc_psy, enable);
+	if (ret < 0) {
+		pr_err("fail to set dc-psy online to %d.\n", enable);
+		return ret;
+	}
+
+	is_charging_enabled = enable;
+
+	pr_debug("Charging enable = %d.\n", enable);
+
+	return 0;
+}
+
+static int bq28400_get_prop_status(struct i2c_client *client)
+{
+	int status = POWER_SUPPLY_STATUS_UNKNOWN;
+	int rsoc;
+	s16 current_ma = 0;
+	u16 battery_status;
+
+	battery_status = bq28400_read_reg(client, SBS_BATTERY_STATUS);
+
+	if (battery_status & BAT_STATUS_EMPTY)
+		pr_debug("Battery report Empty.\n");
+
+	/* Battery may report FULL before rsoc is 100%
+	 * for protection and cell-balancing.
+	 * The FULL report may remain when rsoc drops from 100%.
+	 */
+	if (battery_status & BAT_STATUS_FULL) {
+		pr_debug("Battery report Full.\n");
+		bq28400_enable_charging(bq28400_dev, false);
+		return POWER_SUPPLY_STATUS_FULL;
+	}
+
+	rsoc = bq28400_read_rsoc(client);
+	current_ma = bq28400_read_current(client);
+
+	if (rsoc == 100) {
+		bq28400_enable_charging(bq28400_dev, false);
+		pr_debug("Full.\n");
+		return POWER_SUPPLY_STATUS_FULL;
+	}
+
+	/* Enable charging when battery is not full */
+	bq28400_enable_charging(bq28400_dev, true);
+
+	/*
+	* Positive current indicates charging
+	* Negative current indicates discharging.
+	* Charging is stopped at termination-current.
+	*/
+	if (current_ma < 0) {
+		pr_debug("Discharging.\n");
+		status = POWER_SUPPLY_STATUS_DISCHARGING;
+	} else if (current_ma > BQ_TERMINATION_CURRENT_MA) {
+		pr_debug("Charging.\n");
+		status = POWER_SUPPLY_STATUS_CHARGING;
+	} else {
+		pr_debug("Not Charging.\n");
+		status = POWER_SUPPLY_STATUS_NOT_CHARGING;
+	}
+
+	return status;
+}
+
+static int bq28400_get_prop_charge_type(struct i2c_client *client)
+{
+	u16 battery_status;
+	u16 chg_status;
+	u16 fet_status;
+
+	battery_status = bq28400_read_reg(client, SBS_BATTERY_STATUS);
+	chg_status = bq28400_read_reg(client, SBS_CHARGING_STATUS);
+	fet_status = bq28400_read_reg(client, SBS_FET_STATUS);
+
+	if (battery_status & BAT_STATUS_DISCHARGING) {
+		pr_debug("Discharging.\n");
+		return POWER_SUPPLY_CHARGE_TYPE_NONE;
+	}
+
+	if (fet_status & FET_STATUS_PRECHARGE) {
+		pr_debug("Pre-Charging.\n");
+		return POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
+	}
+
+	if (chg_status & CHG_STATUS_HOT_TEMP_CHARGING) {
+		pr_debug("Hot-Temp-Charging.\n");
+		return POWER_SUPPLY_CHARGE_TYPE_FAST;
+	}
+
+	if (chg_status & CHG_STATUS_LOW_TEMP_CHARGING) {
+		pr_debug("Low-Temp-Charging.\n");
+		return POWER_SUPPLY_CHARGE_TYPE_FAST;
+	}
+
+	if (chg_status & CHG_STATUS_STD1_TEMP_CHARGING) {
+		pr_debug("STD1-Temp-Charging.\n");
+		return POWER_SUPPLY_CHARGE_TYPE_FAST;
+	}
+
+	if (chg_status & CHG_STATUS_STD2_TEMP_CHARGING) {
+		pr_debug("STD2-Temp-Charging.\n");
+		return POWER_SUPPLY_CHARGE_TYPE_FAST;
+	}
+
+	if (chg_status & CHG_STATUS_BATTERY_DEPLETED)
+		pr_debug("battery_depleted.\n");
+
+	if (chg_status & CHG_STATUS_CELL_BALANCING)
+		pr_debug("cell_balancing.\n");
+
+	if (chg_status & CHG_STATUS_OVERCHARGE) {
+		pr_err("overcharge fault.\n");
+		return POWER_SUPPLY_CHARGE_TYPE_NONE;
+	}
+
+	if (chg_status & CHG_STATUS_SUSPENDED) {
+		pr_info("Suspended.\n");
+		return POWER_SUPPLY_CHARGE_TYPE_NONE;
+	}
+
+	if (chg_status & CHG_STATUS_DISABLED) {
+		pr_info("Disabled.\n");
+		return POWER_SUPPLY_CHARGE_TYPE_NONE;
+	}
+
+	return POWER_SUPPLY_CHARGE_TYPE_UNKNOWN;
+}
+
+static bool bq28400_get_prop_present(struct i2c_client *client)
+{
+	int val;
+
+	val = bq28400_read_reg(client, SBS_BATTERY_STATUS);
+
+	/* If the bq28400 is inside the battery pack
+	 * then when battery is removed the i2c transfer will fail.
+	 */
+
+	if (val < 0)
+		return false;
+
+	/* TODO - support when bq28400 is not embedded in battery pack */
+
+	return true;
+}
+
+/*
+ * User sapce read the battery info.
+ * Get data online via I2C from the battery gauge.
+ */
+static int bq28400_get_property(struct power_supply *psy,
+				  enum power_supply_property psp,
+				  union power_supply_propval *val)
+{
+	int ret = 0;
+	struct bq28400_device *dev = container_of(psy,
+						  struct bq28400_device,
+						  batt_psy);
+	struct i2c_client *client = dev->client;
+	static char str[BQ_MAX_STR_LEN];
+
+	switch (psp) {
+	case POWER_SUPPLY_PROP_STATUS:
+		val->intval = bq28400_get_prop_status(client);
+		break;
+	case POWER_SUPPLY_PROP_CHARGE_TYPE:
+		val->intval = bq28400_get_prop_charge_type(client);
+		break;
+	case POWER_SUPPLY_PROP_PRESENT:
+		val->intval = bq28400_get_prop_present(client);
+		break;
+	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+		val->intval = bq28400_read_voltage(client);
+		val->intval *= 1000; /* mV to uV */
+		break;
+	case POWER_SUPPLY_PROP_CAPACITY:
+		val->intval = bq28400_read_rsoc(client);
+		break;
+	case POWER_SUPPLY_PROP_CURRENT_NOW:
+		/* Positive current indicates drawing */
+		val->intval = -bq28400_read_current(client);
+		val->intval *= 1000; /* mA to uA */
+		break;
+	case POWER_SUPPLY_PROP_CURRENT_AVG:
+		/* Positive current indicates drawing */
+		val->intval = -bq28400_read_avg_current(client);
+		val->intval *= 1000; /* mA to uA */
+		break;
+	case POWER_SUPPLY_PROP_TEMP:
+		val->intval = bq28400_read_temperature(client);
+		break;
+	case POWER_SUPPLY_PROP_CHARGE_FULL:
+		val->intval = bq28400_read_full_capacity(client);
+		break;
+	case POWER_SUPPLY_PROP_CHARGE_NOW:
+		val->intval = bq28400_read_remain_capacity(client);
+		break;
+	case POWER_SUPPLY_PROP_TECHNOLOGY:
+		val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
+		break;
+	case POWER_SUPPLY_PROP_MODEL_NAME:
+		bq28400_read_string(client, SBS_DEVICE_NAME, str, 20);
+		val->strval = str;
+		break;
+	case POWER_SUPPLY_PROP_MANUFACTURER:
+		bq28400_read_string(client, SBS_MANUFACTURER_NAME, str, 20);
+		val->strval = str;
+		break;
+	default:
+		pr_err(" psp %d Not supoprted.\n", psp);
+		ret = -EINVAL;
+		break;
+	}
+
+	return ret;
+}
+
+static int bq28400_set_reg(void *data, u64 val)
+{
+	struct debug_reg *dbg = data;
+	u8 reg = dbg->reg;
+	int ret;
+	struct i2c_client *client = bq28400_dev->client;
+
+	ret = bq28400_write_reg(client, reg, val);
+
+	return ret;
+}
+
+static int bq28400_get_reg(void *data, u64 *val)
+{
+	struct debug_reg *dbg = data;
+	u8 reg = dbg->reg;
+	u16 subcmd = dbg->subcmd;
+	int ret;
+	struct i2c_client *client = bq28400_dev->client;
+
+	if (subcmd)
+		ret = bq28400_read_subcmd(client, reg, subcmd);
+	else
+		ret = bq28400_read_reg(client, reg);
+	if (ret < 0)
+		return ret;
+
+	*val = ret;
+
+	return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(reg_fops, bq28400_get_reg, bq28400_set_reg,
+			"0x%04llx\n");
+
+static int bq28400_create_debugfs_entries(struct bq28400_device *bq28400_dev)
+{
+	int i;
+
+	bq28400_dev->dent = debugfs_create_dir(BQ28400_NAME, NULL);
+	if (IS_ERR(bq28400_dev->dent)) {
+		pr_err("bq28400 driver couldn't create debugfs dir\n");
+		return -EFAULT;
+	}
+
+	for (i = 0 ; i < ARRAY_SIZE(bq28400_debug_regs) ; i++) {
+		char *name = bq28400_debug_regs[i].name;
+		struct dentry *file;
+		void *data = &bq28400_debug_regs[i];
+
+		file = debugfs_create_file(name, 0644, bq28400_dev->dent,
+					   data, &reg_fops);
+		if (IS_ERR(file)) {
+			pr_err("debugfs_create_file %s failed.\n", name);
+			return -EFAULT;
+		}
+	}
+
+	return 0;
+}
+
+static int bq28400_set_property(struct power_supply *psy,
+				  enum power_supply_property psp,
+				  const union power_supply_propval *val)
+{
+	pr_debug("psp = %d.val = %d.\n", psp, val->intval);
+
+	return -EINVAL;
+}
+
+static void bq28400_external_power_changed(struct power_supply *psy)
+{
+	pr_debug("Notify power_supply_changed.\n");
+
+	/* The battery gauge monitors the current and voltage every 1 second.
+	 * Therefore a delay from the time that the charger start/stop charging
+	 * until the battery gauge detects it.
+	 */
+	msleep(1000);
+	/* Update LEDs and notify uevents */
+	power_supply_changed(&bq28400_dev->batt_psy);
+}
+
+static int __devinit bq28400_register_psy(struct bq28400_device *bq28400_dev)
+{
+	int ret;
+
+	bq28400_dev->batt_psy.name = "battery";
+	bq28400_dev->batt_psy.type = POWER_SUPPLY_TYPE_BATTERY;
+	bq28400_dev->batt_psy.num_supplicants = 0;
+	bq28400_dev->batt_psy.properties = pm_power_props;
+	bq28400_dev->batt_psy.num_properties = ARRAY_SIZE(pm_power_props);
+	bq28400_dev->batt_psy.get_property = bq28400_get_property;
+	bq28400_dev->batt_psy.set_property = bq28400_set_property;
+	bq28400_dev->batt_psy.external_power_changed =
+		bq28400_external_power_changed;
+
+	ret = power_supply_register(&bq28400_dev->client->dev,
+				&bq28400_dev->batt_psy);
+	if (ret) {
+		pr_err("failed to register power_supply. ret=%d.\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+/**
+ * Update userspace every 1 minute.
+ * Normally it takes more than 120 minutes (two hours) to
+ * charge/discahrge the battery,
+ * so updating every 1 minute should be enough for 1% change
+ * detection.
+ * Any immidiate change detected by the DC charger is notified
+ * by the bq28400_external_power_changed callback, which notify
+ * the user space.
+ */
+static void bq28400_periodic_user_space_update_worker(struct work_struct *work)
+{
+	u32 delay_msec = 60*1000;
+
+	pr_debug("Notify user space.\n");
+
+	/* Notify user space via kobject_uevent change notification */
+	power_supply_changed(&bq28400_dev->batt_psy);
+
+	schedule_delayed_work(&bq28400_dev->periodic_user_space_update_work,
+			      round_jiffies_relative(msecs_to_jiffies
+						     (delay_msec)));
+}
+
+static int __devinit bq28400_probe(struct i2c_client *client,
+				   const struct i2c_device_id *id)
+{
+	int ret = 0;
+
+	if (!i2c_check_functionality(client->adapter,
+				I2C_FUNC_SMBUS_BYTE_DATA)) {
+		pr_err(" i2c func fail.\n");
+		return -EIO;
+	}
+
+	if (bq28400_read_reg(client, SBS_BATTERY_STATUS) < 0) {
+		pr_err("Device doesn't exist.\n");
+		return -ENODEV;
+	}
+
+	bq28400_dev = kzalloc(sizeof(*bq28400_dev), GFP_KERNEL);
+	if (!bq28400_dev) {
+		pr_err(" alloc fail.\n");
+		return -ENOMEM;
+	}
+
+	bq28400_dev->client = client;
+	i2c_set_clientdata(client, bq28400_dev);
+
+	ret = bq28400_register_psy(bq28400_dev);
+	if (ret) {
+		pr_err(" bq28400_register_psy fail.\n");
+		goto err_register_psy;
+	}
+
+	ret = bq28400_create_debugfs_entries(bq28400_dev);
+	if (ret) {
+		pr_err(" bq28400_create_debugfs_entries fail.\n");
+		goto err_debugfs;
+	}
+
+	INIT_DELAYED_WORK(&bq28400_dev->periodic_user_space_update_work,
+			  bq28400_periodic_user_space_update_worker);
+
+	schedule_delayed_work(&bq28400_dev->periodic_user_space_update_work,
+			      msecs_to_jiffies(1000));
+
+	pr_info("Device is ready.\n");
+
+	return 0;
+
+err_debugfs:
+	if (bq28400_dev->dent)
+		debugfs_remove_recursive(bq28400_dev->dent);
+	power_supply_unregister(&bq28400_dev->batt_psy);
+err_register_psy:
+	kfree(bq28400_dev);
+	bq28400_dev = NULL;
+
+	pr_info("FAIL.\n");
+
+	return ret;
+}
+
+static int __devexit bq28400_remove(struct i2c_client *client)
+{
+	struct bq28400_device *bq28400_dev = i2c_get_clientdata(client);
+
+	power_supply_unregister(&bq28400_dev->batt_psy);
+	if (bq28400_dev->dent)
+		debugfs_remove_recursive(bq28400_dev->dent);
+	kfree(bq28400_dev);
+	bq28400_dev = NULL;
+
+	return 0;
+}
+
+static const struct of_device_id bq28400_match[] = {
+	{ .compatible = "ti,bq28400-battery", },
+	{ },
+	};
+
+static const struct i2c_device_id bq28400_id[] = {
+	{BQ28400_NAME, 0},
+	{},
+};
+
+MODULE_DEVICE_TABLE(i2c, bq28400_id);
+
+static struct i2c_driver bq28400_driver = {
+	.driver	= {
+		.name	= BQ28400_NAME,
+		.owner	= THIS_MODULE,
+		.of_match_table = of_match_ptr(bq28400_match),
+	},
+	.probe		= bq28400_probe,
+	.remove		= __devexit_p(bq28400_remove),
+	.id_table	= bq28400_id,
+};
+
+static int __init bq28400_init(void)
+{
+	pr_info(" bq28400 driver rev %s.\n", BQ28400_REV);
+
+	return i2c_add_driver(&bq28400_driver);
+}
+module_init(bq28400_init);
+
+static void __exit bq28400_exit(void)
+{
+	return i2c_del_driver(&bq28400_driver);
+}
+module_exit(bq28400_exit);
+
+MODULE_DESCRIPTION("Driver for BQ28400 charger chip");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("i2c:" BQ28400_NAME);
diff --git a/drivers/power/msm_battery.c b/drivers/power/msm_battery.c
index 5abc032..f8186b1 100644
--- a/drivers/power/msm_battery.c
+++ b/drivers/power/msm_battery.c
@@ -1405,8 +1405,10 @@
 	msm_batt_info.msm_psy_batt = &msm_psy_batt;
 
 #ifndef CONFIG_BATTERY_MSM_FAKE
-	rc = msm_batt_register(BATTERY_LOW, BATTERY_ALL_ACTIVITY,
-			       BATTERY_CB_ID_ALL_ACTIV, BATTERY_ALL_ACTIVITY);
+	rc = msm_batt_register(msm_batt_info.voltage_fail_safe,
+			       BATTERY_ALL_ACTIVITY,
+			       BATTERY_CB_ID_ALL_ACTIV,
+			       BATTERY_ALL_ACTIVITY);
 	if (rc < 0) {
 		dev_err(&pdev->dev,
 			"%s: msm_batt_register failed rc = %d\n", __func__, rc);
diff --git a/drivers/power/pm8921-bms.c b/drivers/power/pm8921-bms.c
index e6e2f30..60eee64 100644
--- a/drivers/power/pm8921-bms.c
+++ b/drivers/power/pm8921-bms.c
@@ -640,6 +640,26 @@
 		raw->last_good_ocv_raw -= MBG_TRANSIENT_ERROR_RAW;
 }
 
+#define SEL_ALT_OREG_BIT  BIT(2)
+static int ocv_ir_compensation(struct pm8921_bms_chip *chip, int ocv)
+{
+	int compensated_ocv;
+	int ibatt_ua;
+	int rbatt_mohm = chip->default_rbatt_mohm + chip->rconn_mohm;
+
+	pm_bms_masked_write(chip, BMS_TEST1,
+			SEL_ALT_OREG_BIT, SEL_ALT_OREG_BIT);
+
+	/* since the SEL_ALT_OREG_BIT is set this will give us VSENSE_OCV */
+	pm8921_bms_get_battery_current(&ibatt_ua);
+	compensated_ocv = ocv + div_s64((s64)ibatt_ua * rbatt_mohm, 1000);
+	pr_debug("comp ocv = %d, ocv = %d, ibatt_ua = %d, rbatt_mohm = %d\n",
+			compensated_ocv, ocv, ibatt_ua, rbatt_mohm);
+
+	pm_bms_masked_write(chip, BMS_TEST1, SEL_ALT_OREG_BIT, 0);
+	return compensated_ocv;
+}
+
 static int read_soc_params_raw(struct pm8921_bms_chip *chip,
 				struct pm8921_soc_params *raw)
 {
@@ -662,6 +682,8 @@
 		adjust_pon_ocv_raw(chip, raw);
 		convert_vbatt_raw_to_uv(chip, usb_chg,
 			raw->last_good_ocv_raw, &raw->last_good_ocv_uv);
+		raw->last_good_ocv_uv = ocv_ir_compensation(chip,
+						raw->last_good_ocv_uv);
 		chip->last_ocv_uv = raw->last_good_ocv_uv;
 		pr_debug("PON_OCV_UV = %d\n", chip->last_ocv_uv);
 	} else if (chip->prev_last_good_ocv_raw != raw->last_good_ocv_raw) {
@@ -834,6 +856,22 @@
 	*val = cc_uah;
 }
 
+int pm8921_bms_cc_uah(int *cc_uah)
+{
+	int cc;
+
+	*cc_uah = 0;
+
+	if (!the_chip)
+		return -EINVAL;
+
+	read_cc(the_chip, &cc);
+	calculate_cc_uah(the_chip, cc, cc_uah);
+
+	return 0;
+}
+EXPORT_SYMBOL(pm8921_bms_cc_uah);
+
 static int calculate_termination_uuc(struct pm8921_bms_chip *chip,
 				 int batt_temp, int chargecycles,
 				int fcc_uah, int i_ma,
@@ -1012,7 +1050,7 @@
 	 * samples with the the shutdown_iavg_ua
 	 */
 	if (firsttime && chip->shutdown_iavg_ua != 0) {
-		pr_emerg("Using shutdown_iavg_ua = %d in all samples\n",
+		pr_debug("Using shutdown_iavg_ua = %d in all samples\n",
 				chip->shutdown_iavg_ua);
 		for (i = 0; i < IAVG_SAMPLES; i++)
 			iavg_samples[i] = chip->shutdown_iavg_ua;
@@ -1376,7 +1414,7 @@
 						(s64)fcc_uah - uuc_uah);
 	soc_est = bound_soc(soc_est);
 
-	if (ibat_ua < 0) {
+	if (ibat_ua < 0 && pm8921_is_batfet_closed()) {
 		soc = charging_adjustments(chip, soc, vbat_uv, ibat_ua,
 				batt_temp, chargecycles,
 				fcc_uah, cc_uah, uuc_uah);
@@ -2332,12 +2370,6 @@
 	return IRQ_HANDLED;
 }
 
-static irqreturn_t pm8921_bms_vsense_avg_handler(int irq, void *data)
-{
-	pr_debug("irq = %d triggered", irq);
-	return IRQ_HANDLED;
-}
-
 struct pm_bms_irq_init_data {
 	unsigned int	irq_id;
 	char		*name;
@@ -2366,8 +2398,6 @@
 				pm8921_bms_ocv_for_r_handler),
 	BMS_IRQ(PM8921_BMS_GOOD_OCV, IRQF_TRIGGER_RISING,
 				pm8921_bms_good_ocv_handler),
-	BMS_IRQ(PM8921_BMS_VSENSE_AVG, IRQF_TRIGGER_RISING,
-				pm8921_bms_vsense_avg_handler),
 };
 
 static void free_irqs(struct pm8921_bms_chip *chip)
diff --git a/drivers/power/pm8921-charger.c b/drivers/power/pm8921-charger.c
index 19454ca..8a36d6c 100644
--- a/drivers/power/pm8921-charger.c
+++ b/drivers/power/pm8921-charger.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -28,6 +28,7 @@
 #include <linux/debugfs.h>
 #include <linux/slab.h>
 #include <linux/mfd/pm8xxx/batt-alarm.h>
+#include <linux/ratelimit.h>
 
 #include <mach/msm_xo.h>
 #include <mach/msm_hsusb.h>
@@ -247,6 +248,7 @@
 	unsigned int			is_bat_cool;
 	unsigned int			is_bat_warm;
 	unsigned int			resume_voltage_delta;
+	int				resume_charge_percent;
 	unsigned int			term_current;
 	unsigned int			vbat_channel;
 	unsigned int			batt_temp_channel;
@@ -287,6 +289,7 @@
 	bool				has_dc_supply;
 	u8				active_path;
 	int				recent_reported_soc;
+	int				battery_less_hardware;
 };
 
 /* user space parameter to limit usb current */
@@ -983,30 +986,6 @@
 					PM8921_CHG_LED_SRC_CONFIG_MASK, temp);
 }
 
-static void disable_input_voltage_regulation(struct pm8921_chg_chip *chip)
-{
-	u8 temp;
-	int rc;
-
-	rc = pm8xxx_writeb(chip->dev->parent, CHG_BUCK_CTRL_TEST3, 0x70);
-	if (rc) {
-		pr_err("Failed to write 0x70 to CTRL_TEST3 rc = %d\n", rc);
-		return;
-	}
-	rc = pm8xxx_readb(chip->dev->parent, CHG_BUCK_CTRL_TEST3, &temp);
-	if (rc) {
-		pr_err("Failed to read CTRL_TEST3 rc = %d\n", rc);
-		return;
-	}
-	/* set the input voltage disable bit and the write bit */
-	temp |= 0x81;
-	rc = pm8xxx_writeb(chip->dev->parent, CHG_BUCK_CTRL_TEST3, temp);
-	if (rc) {
-		pr_err("Failed to write 0x%x to CTRL_TEST3 rc=%d\n", temp, rc);
-		return;
-	}
-}
-
 static void enable_input_voltage_regulation(struct pm8921_chg_chip *chip)
 {
 	u8 temp;
@@ -1375,6 +1354,7 @@
 	POWER_SUPPLY_PROP_CURRENT_NOW,
 	POWER_SUPPLY_PROP_TEMP,
 	POWER_SUPPLY_PROP_ENERGY_FULL,
+	POWER_SUPPLY_PROP_CHARGE_NOW,
 };
 
 static int get_prop_battery_uvolts(struct pm8921_chg_chip *chip)
@@ -1418,6 +1398,9 @@
 {
 	int percent_soc;
 
+	if (chip->battery_less_hardware)
+		return 100;
+
 	if (!get_prop_batt_present(chip))
 		percent_soc = voltage_based_capacity(chip);
 	else
@@ -1427,7 +1410,24 @@
 		percent_soc = voltage_based_capacity(chip);
 
 	if (percent_soc <= 10)
-		pr_warn("low battery charge = %d%%\n", percent_soc);
+		pr_warn_ratelimited("low battery charge = %d%%\n",
+						percent_soc);
+
+	if (chip->recent_reported_soc == (chip->resume_charge_percent + 1)
+			&& percent_soc == chip->resume_charge_percent) {
+		pr_debug("soc fell below %d. charging enabled.\n",
+						chip->resume_charge_percent);
+		if (chip->is_bat_warm)
+			pr_warn_ratelimited("battery is warm = %d, do not resume charging at %d%%.\n",
+					chip->is_bat_warm,
+					chip->resume_charge_percent);
+		else if (chip->is_bat_cool)
+			pr_warn_ratelimited("battery is cool = %d, do not resume charging at %d%%.\n",
+					chip->is_bat_cool,
+					chip->resume_charge_percent);
+		else
+			pm_chg_vbatdet_set(the_chip, PM8921_CHG_VBATDET_MAX);
+	}
 
 	chip->recent_reported_soc = percent_soc;
 	return percent_soc;
@@ -1490,6 +1490,20 @@
 	return rc;
 }
 
+static int get_prop_batt_charge_now(struct pm8921_chg_chip *chip)
+{
+	int rc;
+	int cc_uah;
+
+	rc = pm8921_bms_cc_uah(&cc_uah);
+
+	if (rc == 0)
+		return cc_uah;
+
+	pr_err("unable to get batt fcc rc = %d\n", rc);
+	return rc;
+}
+
 static int get_prop_batt_health(struct pm8921_chg_chip *chip)
 {
 	int temp;
@@ -1563,6 +1577,9 @@
 	int rc;
 	struct pm8xxx_adc_chan_result result;
 
+	if (chip->battery_less_hardware)
+		return 300;
+
 	rc = pm8xxx_adc_read(chip->batt_temp_channel, &result);
 	if (rc) {
 		pr_err("error reading adc channel = %d, rc = %d\n",
@@ -1625,6 +1642,9 @@
 	case POWER_SUPPLY_PROP_ENERGY_FULL:
 		val->intval = get_prop_batt_fcc(chip) * 1000;
 		break;
+	case POWER_SUPPLY_PROP_CHARGE_NOW:
+		val->intval = get_prop_batt_charge_now(chip);
+		break;
 	default:
 		return -EINVAL;
 	}
@@ -1691,7 +1711,7 @@
 		}
 
 		/* Check if IUSB_FINE_RES is available */
-		if ((usb_ma_table[i].value & PM8917_IUSB_FINE_RES)
+		while ((usb_ma_table[i].value & PM8917_IUSB_FINE_RES)
 				&& !the_chip->iusb_fine_res)
 			i--;
 		if (i < 0)
@@ -2568,29 +2588,49 @@
 	return IRQ_HANDLED;
 }
 
-static int param_vin_disable_counter = 5;
-module_param(param_vin_disable_counter, int, 0644);
+enum {
+	PON_TIME_25NS	= 0x04,
+	PON_TIME_50NS	= 0x08,
+	PON_TIME_100NS	= 0x0C,
+};
 
-static void attempt_reverse_boost_fix(struct pm8921_chg_chip *chip,
-							int count, int usb_ma)
+static void set_min_pon_time(struct pm8921_chg_chip *chip, int pon_time_ns)
 {
-	if (usb_ma)
-		__pm8921_charger_vbus_draw(500);
-	pr_debug("count = %d iusb=500mA\n", count);
-	disable_input_voltage_regulation(chip);
-	pr_debug("count = %d disable_input_regulation\n", count);
+	u8 temp;
+	int rc;
 
-	msleep(20);
+	rc = pm8xxx_writeb(chip->dev->parent, CHG_BUCK_CTRL_TEST3, 0x40);
+	if (rc) {
+		pr_err("Failed to write 0x70 to CTRL_TEST3 rc = %d\n", rc);
+		return;
+	}
+	rc = pm8xxx_readb(chip->dev->parent, CHG_BUCK_CTRL_TEST3, &temp);
+	if (rc) {
+		pr_err("Failed to read CTRL_TEST3 rc = %d\n", rc);
+		return;
+	}
+	/* clear the min pon time select bit */
+	temp &= 0xF3;
+	/* set the pon time */
+	temp |= (u8)pon_time_ns;
+	/* write enable bank 4 */
+	temp |= 0x80;
+	rc = pm8xxx_writeb(chip->dev->parent, CHG_BUCK_CTRL_TEST3, temp);
+	if (rc) {
+		pr_err("Failed to write 0x%x to CTRL_TEST3 rc=%d\n", temp, rc);
+		return;
+	}
+}
 
-	pr_debug("count = %d end sleep 20ms chg_gone=%d, usb_valid = %d\n",
-				count,
-				pm_chg_get_rt_status(chip, CHG_GONE_IRQ),
-				is_usb_chg_plugged_in(chip));
-	pr_debug("count = %d restoring input regulation and usb_ma = %d\n",
-		 count, usb_ma);
-	enable_input_voltage_regulation(chip);
-	if (usb_ma)
-		__pm8921_charger_vbus_draw(usb_ma);
+static void attempt_reverse_boost_fix(struct pm8921_chg_chip *chip)
+{
+	pr_debug("Start\n");
+	set_min_pon_time(chip, PON_TIME_100NS);
+	pm_chg_vinmin_set(chip, chip->vin_min + 200);
+	msleep(250);
+	pm_chg_vinmin_set(chip, chip->vin_min);
+	set_min_pon_time(chip, PON_TIME_25NS);
+	pr_debug("End\n");
 }
 
 #define VIN_ACTIVE_BIT BIT(0)
@@ -2630,8 +2670,10 @@
 		}
 	} else if (active_path & DC_ACTIVE_BIT) {
 		pr_debug("DC charger active\n");
-		/* Some board designs are not prone to reverse boost on DC
-		 * charging path */
+		/*
+		 * Some board designs are not prone to reverse boost on DC
+		 * charging path
+		 */
 		if (!chip->dc_unplug_check)
 			return;
 	} else {
@@ -2670,27 +2712,17 @@
 
 	ibat = get_prop_batt_current(chip);
 	if (reg_loop & VIN_ACTIVE_BIT) {
-		pr_debug("ibat = %d fsm = %d reg_loop = 0x%x\n",
-				ibat, pm_chg_get_fsm_state(chip), reg_loop);
 		if (ibat > 0) {
-			int count = 0;
-
-			while (count++ < param_vin_disable_counter
-					&& active_chg_plugged_in == 1) {
-				if (active_path & USB_ACTIVE_BIT)
-					attempt_reverse_boost_fix(chip,
-								count, usb_ma);
-				else
-					attempt_reverse_boost_fix(chip,
-								count, 0);
-				/* after reverse boost fix check if the active
-				 * charger was detected as removed */
-				active_chg_plugged_in
-					= is_active_chg_plugged_in(chip,
-						active_path);
-				pr_debug("active_chg_plugged_in = %d\n",
-						active_chg_plugged_in);
-			}
+			pr_debug("revboost ibat = %d fsm = %d loop = 0x%x\n",
+				ibat, pm_chg_get_fsm_state(chip), reg_loop);
+			attempt_reverse_boost_fix(chip);
+			/* after reverse boost fix check if the active
+			 * charger was detected as removed */
+			active_chg_plugged_in
+				= is_active_chg_plugged_in(chip,
+					active_path);
+			pr_debug("revboost post: active_chg_plugged_in = %d\n",
+					active_chg_plugged_in);
 		}
 	}
 
@@ -2898,6 +2930,12 @@
 		handle_start_ext_chg(chip);
 	else
 		handle_stop_ext_chg(chip);
+
+	if (!chip->ext_psy) {
+		power_supply_changed(&chip->dc_psy);
+		power_supply_changed(&chip->batt_psy);
+	}
+
 	return IRQ_HANDLED;
 }
 
@@ -2980,12 +3018,10 @@
 module_param(ichg_threshold_ua, int, 0644);
 
 #define PM8921_CHG_VDDMAX_RES_MV	10
-static void adjust_vdd_max_for_fastchg(struct pm8921_chg_chip *chip)
+static void adjust_vdd_max_for_fastchg(struct pm8921_chg_chip *chip,
+						int vbat_batt_terminal_uv)
 {
-	int ichg_meas_ua, vbat_uv;
-	int ichg_meas_ma;
 	int adj_vdd_max_mv, programmed_vdd_max;
-	int vbat_batt_terminal_uv;
 	int vbat_batt_terminal_mv;
 	int reg_loop;
 	int delta_mv = 0;
@@ -3007,18 +3043,6 @@
 			reg_loop);
 		return;
 	}
-
-	pm8921_bms_get_simultaneous_battery_voltage_and_current(&ichg_meas_ua,
-								&vbat_uv);
-	if (ichg_meas_ua >= 0) {
-		pr_debug("Exiting ichg_meas_ua = %d > 0\n", ichg_meas_ua);
-		return;
-	}
-
-	ichg_meas_ma = ichg_meas_ua / 1000;
-
-	/* rconn_mohm is in milliOhms */
-	vbat_batt_terminal_uv = vbat_uv + ichg_meas_ma * the_chip->rconn_mohm;
 	vbat_batt_terminal_mv = vbat_batt_terminal_uv/1000;
 	pm_chg_vddmax_get(the_chip, &programmed_vdd_max);
 
@@ -3054,10 +3078,10 @@
 
 #define VBAT_TOLERANCE_MV	70
 #define CHG_DISABLE_MSLEEP	100
-static int is_charging_finished(struct pm8921_chg_chip *chip)
+static int is_charging_finished(struct pm8921_chg_chip *chip,
+			int vbat_batt_terminal_uv, int ichg_meas_ma)
 {
-	int vbat_meas_uv, vbat_meas_mv, vbat_programmed, vbatdet_low;
-	int ichg_meas_ma, iterm_programmed;
+	int vbat_programmed, iterm_programmed, vbat_intended;
 	int regulation_loop, fast_chg, vcp;
 	int rc;
 	static int last_vbat_programmed = -EINVAL;
@@ -3074,30 +3098,19 @@
 		if (vcp == 1)
 			return CHG_IN_PROGRESS;
 
-		vbatdet_low = pm_chg_get_rt_status(chip, VBATDET_LOW_IRQ);
-		pr_debug("vbatdet_low = %d\n", vbatdet_low);
-		if (vbatdet_low == 1)
-			return CHG_IN_PROGRESS;
-
 		/* reset count if battery is hot/cold */
 		rc = pm_chg_get_rt_status(chip, BAT_TEMP_OK_IRQ);
 		pr_debug("batt_temp_ok = %d\n", rc);
 		if (rc == 0)
 			return CHG_IN_PROGRESS;
 
-		/* reset count if battery voltage is less than vddmax */
-		vbat_meas_uv = get_prop_battery_uvolts(chip);
-		if (vbat_meas_uv < 0)
-			return CHG_IN_PROGRESS;
-		vbat_meas_mv = vbat_meas_uv / 1000;
-
 		rc = pm_chg_vddmax_get(chip, &vbat_programmed);
 		if (rc) {
 			pr_err("couldnt read vddmax rc = %d\n", rc);
 			return CHG_IN_PROGRESS;
 		}
-		pr_debug("vddmax = %d vbat_meas_mv=%d\n",
-			 vbat_programmed, vbat_meas_mv);
+		pr_debug("vddmax = %d vbat_batt_terminal_uv=%d\n",
+			 vbat_programmed, vbat_batt_terminal_uv);
 
 		if (last_vbat_programmed == -EINVAL)
 			last_vbat_programmed = vbat_programmed;
@@ -3109,6 +3122,20 @@
 			return CHG_IN_PROGRESS;
 		}
 
+		if (chip->is_bat_cool)
+			vbat_intended = chip->cool_bat_voltage;
+		else if (chip->is_bat_warm)
+			vbat_intended = chip->warm_bat_voltage;
+		else
+			vbat_intended = chip->max_voltage_mv;
+
+		if (vbat_batt_terminal_uv / 1000 < vbat_intended) {
+			pr_debug("terminal_uv:%d < vbat_intended:%d.\n",
+							vbat_batt_terminal_uv,
+							vbat_intended);
+			return CHG_IN_PROGRESS;
+		}
+
 		regulation_loop = pm_chg_get_regulation_loop(chip);
 		if (regulation_loop < 0) {
 			pr_err("couldnt read the regulation loop err=%d\n",
@@ -3128,7 +3155,6 @@
 		return CHG_IN_PROGRESS;
 	}
 
-	ichg_meas_ma = (get_prop_batt_current(chip)) / 1000;
 	pr_debug("iterm_programmed = %d ichg_meas_ma=%d\n",
 				iterm_programmed, ichg_meas_ma);
 	/*
@@ -3163,9 +3189,22 @@
 				struct pm8921_chg_chip, eoc_work);
 	static int count;
 	int end;
+	int vbat_meas_uv, vbat_meas_mv;
+	int ichg_meas_ua, ichg_meas_ma;
+	int vbat_batt_terminal_uv;
 
 	pm_chg_failed_clear(chip, 1);
-	end = is_charging_finished(chip);
+
+	pm8921_bms_get_simultaneous_battery_voltage_and_current(
+					&ichg_meas_ua,	&vbat_meas_uv);
+	vbat_meas_mv = vbat_meas_uv / 1000;
+	/* rconn_mohm is in milliOhms */
+	ichg_meas_ma = ichg_meas_ua / 1000;
+	vbat_batt_terminal_uv = vbat_meas_uv
+					+ ichg_meas_ma
+					* the_chip->rconn_mohm;
+
+	end = is_charging_finished(chip, vbat_batt_terminal_uv, ichg_meas_ma);
 
 	if (end == CHG_NOT_IN_PROGRESS) {
 		count = 0;
@@ -3194,6 +3233,21 @@
 	if (count == CONSECUTIVE_COUNT) {
 		count = 0;
 		pr_info("End of Charging\n");
+		/* set the vbatdet back, in case it was changed
+		 * to trigger charging */
+		if (chip->is_bat_cool) {
+			pm_chg_vbatdet_set(the_chip,
+				the_chip->cool_bat_voltage
+				- the_chip->resume_voltage_delta);
+		} else if (chip->is_bat_warm) {
+			pm_chg_vbatdet_set(the_chip,
+				the_chip->warm_bat_voltage
+				- the_chip->resume_voltage_delta);
+		} else {
+			pm_chg_vbatdet_set(the_chip,
+				the_chip->max_voltage_mv
+				- the_chip->resume_voltage_delta);
+		}
 
 		pm_chg_auto_enable(chip, 0);
 
@@ -3208,7 +3262,7 @@
 		chgdone_irq_handler(chip->pmic_chg_irq[CHGDONE_IRQ], chip);
 		wake_unlock(&chip->eoc_wake_lock);
 	} else {
-		adjust_vdd_max_for_fastchg(chip);
+		adjust_vdd_max_for_fastchg(chip, vbat_batt_terminal_uv);
 		pr_debug("EOC count = %d\n", count);
 		schedule_delayed_work(&chip->eoc_work,
 			      round_jiffies_relative(msecs_to_jiffies
@@ -3702,10 +3756,12 @@
 #define CHG_VCP_EN		BIT(0)
 #define CHG_BAT_TEMP_DIS_BIT	BIT(2)
 #define SAFE_CURRENT_MA		1500
+#define PM_SUB_REV		0x001
 static int __devinit pm8921_chg_hw_init(struct pm8921_chg_chip *chip)
 {
 	int rc;
 	int vdd_safe;
+	u8 subrev;
 
 	/* forcing 19p2mhz before accessing any charger registers */
 	pm8921_chg_force_19p2mhz_clk(chip);
@@ -3884,8 +3940,21 @@
 
 	/* Workarounds for die 3.0 */
 	if (pm8xxx_get_revision(chip->dev->parent) == PM8XXX_REVISION_8921_3p0
-	&& pm8xxx_get_version(chip->dev->parent) == PM8XXX_VERSION_8921)
-		pm8xxx_writeb(chip->dev->parent, CHG_BUCK_CTRL_TEST3, 0xAC);
+	&& pm8xxx_get_version(chip->dev->parent) == PM8XXX_VERSION_8921) {
+		rc = pm8xxx_readb(chip->dev->parent, PM_SUB_REV, &subrev);
+		if (rc) {
+			pr_err("read failed: addr=%03X, rc=%d\n",
+				PM_SUB_REV, rc);
+			return rc;
+		}
+		/* Check if die 3.0.1 is present */
+		if (subrev == 0x1)
+			pm8xxx_writeb(chip->dev->parent,
+				CHG_BUCK_CTRL_TEST3, 0xA4);
+		else
+			pm8xxx_writeb(chip->dev->parent,
+				CHG_BUCK_CTRL_TEST3, 0xAC);
+	}
 
 	/* Enable isub_fine resolution AICL for PM8917 */
 	if (pm8xxx_get_version(chip->dev->parent) == PM8XXX_VERSION_8917) {
@@ -4189,6 +4258,7 @@
 	chip->min_voltage_mv = pdata->min_voltage;
 	chip->uvd_voltage_mv = pdata->uvd_thresh_voltage;
 	chip->resume_voltage_delta = pdata->resume_voltage_delta;
+	chip->resume_charge_percent = pdata->resume_charge_percent;
 	chip->term_current = pdata->term_current;
 	chip->vbat_channel = pdata->charger_cdata.vbat_channel;
 	chip->batt_temp_channel = pdata->charger_cdata.batt_temp_channel;
@@ -4226,6 +4296,10 @@
 	chip->rconn_mohm = pdata->rconn_mohm;
 	chip->led_src_config = pdata->led_src_config;
 	chip->has_dc_supply = pdata->has_dc_supply;
+	chip->battery_less_hardware = pdata->battery_less_hardware;
+
+	if (chip->battery_less_hardware)
+		charging_disabled = 1;
 
 	rc = pm8921_chg_hw_init(chip);
 	if (rc) {
@@ -4283,6 +4357,11 @@
 						vin_collapse_check_worker);
 	INIT_DELAYED_WORK(&chip->unplug_check_work, unplug_check_worker);
 
+	INIT_WORK(&chip->bms_notify.work, bms_notify);
+	INIT_WORK(&chip->battery_id_valid_work, battery_id_valid);
+
+	INIT_DELAYED_WORK(&chip->update_heartbeat_work, update_heartbeat);
+
 	rc = request_irqs(chip, pdev);
 	if (rc) {
 		pr_err("couldn't register interrupts rc=%d\n", rc);
@@ -4319,19 +4398,13 @@
 	}
 	create_debugfs_entries(chip);
 
-	INIT_WORK(&chip->bms_notify.work, bms_notify);
-	INIT_WORK(&chip->battery_id_valid_work, battery_id_valid);
-
 	/* determine what state the charger is in */
 	determine_initial_state(chip);
 
-	if (chip->update_time) {
-		INIT_DELAYED_WORK(&chip->update_heartbeat_work,
-							update_heartbeat);
+	if (chip->update_time)
 		schedule_delayed_work(&chip->update_heartbeat_work,
 				      round_jiffies_relative(msecs_to_jiffies
 							(chip->update_time)));
-	}
 	return 0;
 
 free_irq:
diff --git a/drivers/power/pm8xxx-ccadc.c b/drivers/power/pm8xxx-ccadc.c
index e48257a..1b9426a 100644
--- a/drivers/power/pm8xxx-ccadc.c
+++ b/drivers/power/pm8xxx-ccadc.c
@@ -480,6 +480,9 @@
 	struct pm8xxx_ccadc_chip *chip = data;
 	int rc;
 
+	if (!the_chip)
+		goto out;
+
 	pr_debug("irq = %d triggered\n", irq);
 	data_msb = chip->ccadc_offset >> 8;
 	data_lsb = chip->ccadc_offset;
@@ -488,6 +491,7 @@
 						data_msb, data_lsb, 0);
 	disable_irq_nosync(chip->eoc_irq);
 
+out:
 	return IRQ_HANDLED;
 }
 
@@ -685,7 +689,6 @@
 		goto free_chip;
 	}
 
-
 	disable_irq_nosync(chip->eoc_irq);
 
 	platform_set_drvdata(pdev, chip);
diff --git a/drivers/power/power_supply_core.c b/drivers/power/power_supply_core.c
index 352e60e..61f4946 100644
--- a/drivers/power/power_supply_core.c
+++ b/drivers/power/power_supply_core.c
@@ -47,6 +47,41 @@
 EXPORT_SYMBOL_GPL(power_supply_set_current_limit);
 
 /**
+ * power_supply_set_charging_enabled - enable or disable charging
+ * @psy:	the power supply to control
+ * @enable:	sets enable property of power supply
+ */
+int power_supply_set_charging_enabled(struct power_supply *psy, bool enable)
+{
+	const union power_supply_propval ret = {enable,};
+
+	if (psy->set_property)
+		return psy->set_property(psy,
+				POWER_SUPPLY_PROP_CHARGING_ENABLED,
+				&ret);
+
+	return -ENXIO;
+}
+EXPORT_SYMBOL_GPL(power_supply_set_charging_enabled);
+
+/**
+ * power_supply_set_present - set present state of the power supply
+ * @psy:	the power supply to control
+ * @enable:	sets present property of power supply
+ */
+int power_supply_set_present(struct power_supply *psy, bool enable)
+{
+	const union power_supply_propval ret = {enable,};
+
+	if (psy->set_property)
+		return psy->set_property(psy, POWER_SUPPLY_PROP_PRESENT,
+								&ret);
+
+	return -ENXIO;
+}
+EXPORT_SYMBOL_GPL(power_supply_set_present);
+
+/**
  * power_supply_set_online - set online state of the power supply
  * @psy:	the power supply to control
  * @enable:	sets online property of power supply
diff --git a/drivers/power/power_supply_sysfs.c b/drivers/power/power_supply_sysfs.c
index 4368e7d..7eb285b 100644
--- a/drivers/power/power_supply_sysfs.c
+++ b/drivers/power/power_supply_sysfs.c
@@ -138,6 +138,7 @@
 	POWER_SUPPLY_ATTR(health),
 	POWER_SUPPLY_ATTR(present),
 	POWER_SUPPLY_ATTR(online),
+	POWER_SUPPLY_ATTR(charging_enabled),
 	POWER_SUPPLY_ATTR(technology),
 	POWER_SUPPLY_ATTR(cycle_count),
 	POWER_SUPPLY_ATTR(voltage_max),
diff --git a/drivers/power/qpnp-charger.c b/drivers/power/qpnp-charger.c
index dda8976..bc012ca 100644
--- a/drivers/power/qpnp-charger.c
+++ b/drivers/power/qpnp-charger.c
@@ -20,6 +20,7 @@
 #include <linux/of_device.h>
 #include <linux/radix-tree.h>
 #include <linux/interrupt.h>
+#include <linux/delay.h>
 #include <linux/qpnp/qpnp-adc.h>
 #include <linux/power_supply.h>
 #include <linux/bitops.h>
@@ -69,14 +70,17 @@
 #define CHGR_CHG_WDOG_PET			0x64
 #define CHGR_CHG_WDOG_EN			0x65
 #define CHGR_USB_IUSB_MAX			0x44
+#define CHGR_USB_USB_SUSP			0x47
 #define CHGR_USB_ENUM_T_STOP			0x4E
 #define CHGR_CHG_TEMP_THRESH			0x66
 #define CHGR_BAT_IF_PRES_STATUS			0x08
-#define CHGR_BAT_TEMP_STATUS			0x09
+#define CHGR_STATUS				0x09
 #define CHGR_BAT_IF_VCP				0x42
 #define CHGR_BAT_IF_BATFET_CTRL1		0x90
 #define CHGR_MISC_BOOT_DONE			0x42
+#define CHGR_BUCK_COMPARATOR_OVRIDE_3		0xED
 #define MISC_REVISION2				0x01
+#define SEC_ACCESS				0xD0
 
 /* SMBB peripheral subtype values */
 #define REG_OFFSET_PERP_SUBTYPE			0x05
@@ -90,6 +94,11 @@
 
 #define QPNP_CHARGER_DEV_NAME	"qcom,qpnp-charger"
 
+/* Status bits and masks */
+#define CHGR_BOOT_DONE			BIT(7)
+#define CHGR_CHG_EN			BIT(7)
+#define CHGR_ON_BAT_FORCE_BIT		BIT(0)
+
 /* Interrupt definitions */
 /* smbb_chg_interrupts */
 #define CHG_DONE_IRQ			BIT(7)
@@ -133,6 +142,9 @@
 /* smbb_misc_interrupts */
 #define TFTWDOG_IRQ			BIT(0)
 
+/* Workaround flags */
+#define CHG_FLAGS_VCP_WA		BIT(0)
+
 /**
  * struct qpnp_chg_chip - device information
  * @dev:			device pointer to access the parent
@@ -156,6 +168,8 @@
  * @usb_psy			power supply to export information to userspace
  * @bms_psy			power supply to export information to userspace
  * @batt_psy:			power supply to export information to userspace
+ * @flags:			flags to activate specific workarounds
+ *				throughout the driver
  *
  */
 struct qpnp_chg_chip {
@@ -175,6 +189,7 @@
 	bool				chg_done;
 	bool				usb_present;
 	bool				dc_present;
+	bool				charging_disabled;
 	unsigned int			max_bat_chg_current;
 	unsigned int			safe_voltage_mv;
 	unsigned int			max_voltage_mv;
@@ -185,11 +200,9 @@
 	struct power_supply		*usb_psy;
 	struct power_supply		*bms_psy;
 	struct power_supply		batt_psy;
+	uint32_t			flags;
 };
 
-static struct qpnp_chg_chip *the_chip;
-static int charging_disabled;
-
 static struct of_device_id qpnp_charger_match_table[] = {
 	{ .compatible = QPNP_CHARGER_DEV_NAME, },
 	{}
@@ -258,6 +271,7 @@
 	return 0;
 }
 
+#define USB_VALID_BIT	BIT(7)
 static int
 qpnp_chg_is_usb_chg_plugged_in(struct qpnp_chg_chip *chip)
 {
@@ -265,16 +279,16 @@
 	int rc;
 
 	rc = qpnp_chg_read(chip, &usbin_valid_rt_sts,
-				 INT_RT_STS(chip->usb_chgpth_base), 1);
+				 chip->usb_chgpth_base + CHGR_STATUS , 1);
 
 	if (rc) {
 		pr_err("spmi read failed: addr=%03X, rc=%d\n",
-				INT_RT_STS(chip->usb_chgpth_base), rc);
+				chip->usb_chgpth_base + CHGR_STATUS, rc);
 		return rc;
 	}
 	pr_debug("chgr usb sts 0x%x\n", usbin_valid_rt_sts);
 
-	return (usbin_valid_rt_sts & USBIN_VALID_IRQ) ? 1 : 0;
+	return (usbin_valid_rt_sts & USB_VALID_BIT) ? 1 : 0;
 }
 
 static int
@@ -294,7 +308,6 @@
 	return (dcin_valid_rt_sts & DCIN_VALID_IRQ) ? 1 : 0;
 }
 
-#define VCP_IUSBMAX_SETTING_MA			2000
 #define QPNP_CHG_IUSB_MAX_MIN_100		100
 #define QPNP_CHG_IUSB_MAX_MIN_150		150
 #define QPNP_CHG_IUSB_MAX_MIN_MA		200
@@ -303,7 +316,8 @@
 static int
 qpnp_chg_iusbmax_set(struct qpnp_chg_chip *chip, int mA)
 {
-	u8 usb_reg = 0;
+	int rc = 0;
+	u8 usb_reg = 0, temp = 8;
 
 	if (mA == QPNP_CHG_IUSB_MAX_MIN_100) {
 		usb_reg = 0x00;
@@ -323,17 +337,42 @@
 		return -EINVAL;
 	}
 
-	/* Hack for VCP issue make sure IUSBMAX setting
-	 * is at least 2 A to not brown out device */
-	mA = VCP_IUSBMAX_SETTING_MA;
-
 	usb_reg = mA / QPNP_CHG_IUSB_MAX_STEP_MA;
 
+	if (chip->flags & CHG_FLAGS_VCP_WA) {
+		temp = 0xA5;
+		rc =  qpnp_chg_write(chip, &temp,
+			chip->buck_base + SEC_ACCESS, 1);
+		rc =  qpnp_chg_masked_write(chip,
+			chip->buck_base + CHGR_BUCK_COMPARATOR_OVRIDE_3,
+			0x0C, 0x0C, 1);
+	}
+
 	pr_debug("current=%d setting 0x%x\n", mA, usb_reg);
-	return qpnp_chg_write(chip, &usb_reg,
+	rc = qpnp_chg_write(chip, &usb_reg,
 		chip->usb_chgpth_base + CHGR_USB_IUSB_MAX, 1);
-	pr_debug("done\n");
-	return 0;
+
+	if (chip->flags & CHG_FLAGS_VCP_WA) {
+		temp = 0xA5;
+		udelay(200);
+		rc =  qpnp_chg_write(chip, &temp,
+			chip->buck_base + SEC_ACCESS, 1);
+		rc =  qpnp_chg_masked_write(chip,
+			chip->buck_base + CHGR_BUCK_COMPARATOR_OVRIDE_3,
+			0x0C, 0x00, 1);
+	}
+
+	return rc;
+}
+
+#define USB_SUSPEND_BIT	BIT(0)
+static int
+qpnp_chg_usb_suspend_enable(struct qpnp_chg_chip *chip, int enable)
+{
+	return qpnp_chg_masked_write(chip,
+			chip->usb_chgpth_base + CHGR_USB_USB_SUSP,
+			USB_SUSPEND_BIT,
+			enable ? USB_SUSPEND_BIT : 0, 1);
 }
 
 #define ENUM_T_STOP_BIT		BIT(0)
@@ -350,11 +389,6 @@
 		chip->usb_present = usb_present;
 		power_supply_set_present(chip->usb_psy,
 			chip->usb_present);
-	} else if (!(chip->usb_present && usb_present)) {
-			qpnp_chg_masked_write(chip,
-				chip->usb_chgpth_base + CHGR_USB_ENUM_T_STOP,
-				ENUM_T_STOP_BIT,
-				ENUM_T_STOP_BIT, 1);
 	}
 
 	return IRQ_HANDLED;
@@ -365,23 +399,15 @@
 qpnp_chg_chgr_chg_failed_irq_handler(int irq, void *_chip)
 {
 	struct qpnp_chg_chip *chip = _chip;
-	int rc, usb_present;
+	int rc;
 
 	rc = qpnp_chg_masked_write(chip,
-		chip->usb_chgpth_base + CHGR_CHG_FAILED,
+		chip->chgr_base + CHGR_CHG_FAILED,
 		CHGR_CHG_FAILED_BIT,
 		CHGR_CHG_FAILED_BIT, 1);
 	if (rc)
 		pr_err("Failed to write chg_fail clear bit!\n");
 
-	/* Hack: recheck usbin_valid status after chg_fail triggered */
-	usb_present = qpnp_chg_is_usb_chg_plugged_in(chip);
-	pr_debug("usb_status: %d\n", usb_present);
-	if (usb_present)
-		qpnp_chg_usb_usbin_valid_irq_handler(chip->usbin_valid_irq,
-			_chip);
-
-
 	return IRQ_HANDLED;
 }
 
@@ -396,12 +422,27 @@
 	return IRQ_HANDLED;
 }
 
+static int
+qpnp_batt_property_is_writeable(struct power_supply *psy,
+						enum power_supply_property psp)
+{
+	switch (psp) {
+	case POWER_SUPPLY_PROP_CHARGING_ENABLED:
+		return 1;
+	default:
+		break;
+	}
+
+	return 0;
+}
+
 static enum power_supply_property pm_power_props_mains[] = {
 	POWER_SUPPLY_PROP_PRESENT,
 	POWER_SUPPLY_PROP_ONLINE,
 };
 
 static enum power_supply_property msm_batt_power_props[] = {
+	POWER_SUPPLY_PROP_CHARGING_ENABLED,
 	POWER_SUPPLY_PROP_STATUS,
 	POWER_SUPPLY_PROP_CHARGE_TYPE,
 	POWER_SUPPLY_PROP_HEALTH,
@@ -433,7 +474,7 @@
 	case POWER_SUPPLY_PROP_PRESENT:
 	case POWER_SUPPLY_PROP_ONLINE:
 		val->intval = 0;
-		if (charging_disabled)
+		if (chip->charging_disabled)
 			return 0;
 
 		val->intval = qpnp_chg_is_dc_chg_plugged_in(chip);
@@ -488,7 +529,7 @@
 	int rc;
 
 	rc = qpnp_chg_read(chip, &batt_health,
-				chip->bat_if_base + CHGR_BAT_TEMP_STATUS, 1);
+				chip->bat_if_base + CHGR_STATUS, 1);
 	if (rc) {
 		pr_err("Couldn't read battery health read failed rc=%d\n", rc);
 		return POWER_SUPPLY_HEALTH_UNKNOWN;
@@ -647,8 +688,13 @@
 		chip->usb_psy->get_property(chip->usb_psy,
 			  POWER_SUPPLY_PROP_CURRENT_MAX, &ret);
 		qpnp_chg_iusbmax_set(chip, ret.intval / 1000);
+		if ((ret.intval / 1000) <= QPNP_CHG_IUSB_MAX_MIN_MA)
+			qpnp_chg_usb_suspend_enable(chip, 1);
+		else
+			qpnp_chg_usb_suspend_enable(chip, 0);
 	} else {
-		qpnp_chg_iusbmax_set(chip, QPNP_CHG_IUSB_MAX_MIN_MA);
+		qpnp_chg_iusbmax_set(chip, QPNP_CHG_IUSB_MAX_MIN_100);
+		qpnp_chg_usb_suspend_enable(chip, 0);
 	}
 
 	pr_debug("end of power supply changed\n");
@@ -656,6 +702,24 @@
 }
 
 static int
+qpnp_chg_charge_dis(struct qpnp_chg_chip *chip, int disable)
+{
+	/* This bit forces the charger to run off of the battery rather
+	 * than a connected charger */
+	return qpnp_chg_masked_write(chip, chip->chgr_base + CHGR_CHG_CTRL,
+			CHGR_ON_BAT_FORCE_BIT,
+			disable ? CHGR_ON_BAT_FORCE_BIT : 0, 1);
+}
+
+static int
+qpnp_chg_charge_en(struct qpnp_chg_chip *chip, int enable)
+{
+	return qpnp_chg_masked_write(chip, chip->chgr_base + CHGR_CHG_CTRL,
+			CHGR_CHG_EN,
+			enable ? CHGR_CHG_EN : 0, 1);
+}
+
+static int
 qpnp_batt_power_get_property(struct power_supply *psy,
 				       enum power_supply_property psp,
 				       union power_supply_propval *val)
@@ -700,6 +764,9 @@
 	case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
 		val->intval = get_prop_full_design(chip);
 		break;
+	case POWER_SUPPLY_PROP_CHARGING_ENABLED:
+		val->intval = !(chip->charging_disabled);
+		break;
 	default:
 		return -EINVAL;
 	}
@@ -707,47 +774,29 @@
 	return 0;
 }
 
-#define CHGR_BOOT_DONE	BIT(7)
-#define CHGR_CHG_EN	BIT(7)
-#define CHGR_ON_BAT_FORCE_BIT	BIT(0)
-#define CHGR_BAT_IF_CONST_RDS_EN	BIT(7)
-#define CHGR_BAT_IF_VCP_EN	BIT(0)
 static int
-qpnp_chg_charge_dis(struct qpnp_chg_chip *chip, int disable)
+qpnp_batt_power_set_property(struct power_supply *psy,
+				  enum power_supply_property psp,
+				  const union power_supply_propval *val)
 {
-	/* This bit forces the charger to run off of the battery rather
-	 * than a connected charger */
-	return qpnp_chg_masked_write(chip, chip->chgr_base + CHGR_CHG_CTRL,
-			CHGR_ON_BAT_FORCE_BIT,
-			disable ? CHGR_ON_BAT_FORCE_BIT : 0, 1);
-}
+	struct qpnp_chg_chip *chip = container_of(psy, struct qpnp_chg_chip,
+								batt_psy);
 
-static int
-qpnp_chg_charge_en(struct qpnp_chg_chip *chip, int enable)
-{
-	return qpnp_chg_masked_write(chip, chip->chgr_base + CHGR_CHG_CTRL,
-			CHGR_CHG_EN,
-			enable ? CHGR_CHG_EN : 0, 1);
-}
-
-static int
-qpnp_chg_set_disable_status_param(const char *val, struct kernel_param *kp)
-{
-	int ret;
-	struct qpnp_chg_chip *chip = the_chip;
-
-	ret = param_set_int(val, kp);
-	if (ret) {
-		pr_err("error setting value %d\n", ret);
-		return ret;
+	switch (psp) {
+	case POWER_SUPPLY_PROP_CHARGING_ENABLED:
+		if (val->intval)
+			qpnp_chg_charge_en(chip, val->intval);
+		else
+			qpnp_chg_charge_dis(chip, val->intval);
+		chip->charging_disabled = !(val->intval);
+		break;
+	default:
+		return -EINVAL;
 	}
-	pr_info("factory set disable param to %d\n", charging_disabled);
-	if (chip)
-		qpnp_chg_charge_dis(chip, charging_disabled);
+
+	power_supply_changed(&chip->batt_psy);
 	return 0;
 }
-module_param_call(disabled, qpnp_chg_set_disable_status_param, param_get_uint,
-					&charging_disabled, 0644);
 
 #define QPNP_CHG_VINMIN_MIN_MV		3400
 #define QPNP_CHG_VINMIN_HIGH_MIN_MV	5600
@@ -863,6 +912,14 @@
 		chip->chgr_base + CHGR_VDD_MAX, 1);
 }
 
+
+static void
+qpnp_chg_setup_flags(struct qpnp_chg_chip *chip)
+{
+	if (chip->revision > 0)
+		chip->flags |= CHG_FLAGS_VCP_WA;
+}
+
 #define WDOG_EN_BIT	BIT(7)
 static int
 qpnp_chg_hwinit(struct qpnp_chg_chip *chip, u8 subtype,
@@ -944,7 +1001,7 @@
 	case SMBB_BAT_IF_SUBTYPE:
 		/* HACK: Unlock secure access to override temp comparator */
 		rc = qpnp_chg_masked_write(chip,
-				chip->bat_if_base + 0xD0,
+				chip->bat_if_base + SEC_ACCESS,
 				0xA5, 0xA5, 1);
 		pr_debug("override hot cold\n");
 		rc = qpnp_chg_masked_write(chip,
@@ -980,6 +1037,12 @@
 				return -ENXIO;
 			}
 		}
+
+		rc = qpnp_chg_masked_write(chip,
+			chip->usb_chgpth_base + CHGR_USB_ENUM_T_STOP,
+			ENUM_T_STOP_BIT,
+			ENUM_T_STOP_BIT, 1);
+
 		break;
 	case SMBB_DC_CHGPTH_SUBTYPE:
 		break;
@@ -1075,6 +1138,10 @@
 		goto fail_chg_enable;
 	}
 
+	/* Get the charging-disabled property */
+	chip->charging_disabled = of_property_read_bool(spmi->dev.of_node,
+					"qcom,chg-charging-disabled");
+
 	spmi_for_each_container_dev(spmi_resource, spmi) {
 		if (!spmi_resource) {
 			pr_err("qpnp_chg: spmi resource absent\n");
@@ -1184,6 +1251,8 @@
 	chip->batt_psy.properties = msm_batt_power_props;
 	chip->batt_psy.num_properties = ARRAY_SIZE(msm_batt_power_props);
 	chip->batt_psy.get_property = qpnp_batt_power_get_property;
+	chip->batt_psy.set_property = qpnp_batt_power_set_property;
+	chip->batt_psy.property_is_writeable = qpnp_batt_property_is_writeable;
 	chip->batt_psy.external_power_changed =
 				qpnp_batt_external_power_changed;
 
@@ -1199,11 +1268,15 @@
 		goto unregister_dc;
 	}
 
+	/* Turn on appropriate workaround flags */
+	qpnp_chg_setup_flags(chip);
+
 	power_supply_set_present(chip->usb_psy,
 			qpnp_chg_is_usb_chg_plugged_in(chip));
 
-	qpnp_chg_charge_en(chip, 1);
-	the_chip = chip;
+	qpnp_chg_charge_en(chip, !chip->charging_disabled);
+	qpnp_chg_charge_dis(chip, chip->charging_disabled);
+
 	pr_info("Probe success !\n");
 	return 0;
 
@@ -1219,7 +1292,6 @@
 qpnp_charger_remove(struct spmi_device *spmi)
 {
 	struct qpnp_chg_chip *chip = dev_get_drvdata(&spmi->dev);
-
 	dev_set_drvdata(&spmi->dev, NULL);
 	kfree(chip);
 
diff --git a/drivers/power/smb350_charger.c b/drivers/power/smb350_charger.c
new file mode 100644
index 0000000..319caba
--- /dev/null
+++ b/drivers/power/smb350_charger.c
@@ -0,0 +1,873 @@
+/* Copyright (c) 2012 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#define pr_fmt(fmt)	"%s: " fmt, __func__
+
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/debugfs.h>
+#include <linux/workqueue.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/power_supply.h>
+#include <linux/i2c/smb350.h>
+#include <linux/bitops.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/printk.h>
+
+/* Register definitions */
+#define CHG_CURRENT_REG			0x00	/* Non-Volatile + mirror */
+#define CHG_OTHER_CURRENT_REG		0x01	/* Non-Volatile + mirror */
+#define VAR_FUNC_REG			0x02	/* Non-Volatile + mirror */
+#define FLOAT_VOLTAGE_REG		0x03	/* Non-Volatile + mirror */
+#define CHG_CTRL_REG			0x04	/* Non-Volatile + mirror */
+#define STAT_TIMER_REG			0x05	/* Non-Volatile + mirror */
+#define PIN_ENABLE_CTRL_REG		0x06	/* Non-Volatile + mirror */
+#define THERM_CTRL_A_REG		0x07	/* Non-Volatile + mirror */
+#define SYSOK_USB3_SELECT_REG		0x08	/* Non-Volatile + mirror */
+#define CTRL_FUNCTIONS_REG		0x09	/* Non-Volatile + mirror */
+#define OTG_TLIM_THERM_CNTRL_REG	0x0A	/* Non-Volatile + mirror */
+#define TEMP_MONITOR_REG		0x0B	/* Non-Volatile + mirror */
+#define FAULT_IRQ_REG			0x0C	/* Non-Volatile */
+#define IRQ_ENABLE_REG			0x0D	/* Non-Volatile */
+#define SYSOK_REG			0x0E	/* Non-Volatile + mirror */
+
+#define AUTO_INPUT_VOLT_DETECT_REG	0x10	/* Non-Volatile Read-Only */
+#define STATUS_IRQ_REG			0x11	/* Non-Volatile Read-Only */
+#define I2C_SLAVE_ADDR_REG		0x12	/* Non-Volatile Read-Only */
+
+#define CMD_A_REG			0x30	/* Volatile Read-Write */
+#define CMD_B_REG			0x31	/* Volatile Read-Write */
+#define CMD_C_REG			0x33	/* Volatile Read-Write */
+
+#define IRQ_STATUS_A_REG		0x35	/* Volatile Read-Only */
+#define IRQ_STATUS_B_REG		0x36	/* Volatile Read-Only */
+#define IRQ_STATUS_C_REG		0x37	/* Volatile Read-Only */
+#define IRQ_STATUS_D_REG		0x38	/* Volatile Read-Only */
+#define IRQ_STATUS_E_REG		0x39	/* Volatile Read-Only */
+#define IRQ_STATUS_F_REG		0x3A	/* Volatile Read-Only */
+
+#define STATUS_A_REG			0x3B	/* Volatile Read-Only */
+#define STATUS_B_REG			0x3D	/* Volatile Read-Only */
+/* Note: STATUS_C_REG was removed from SMB349 to SMB350 */
+#define STATUS_D_REG			0x3E	/* Volatile Read-Only */
+#define STATUS_E_REG			0x3F	/* Volatile Read-Only */
+
+#define IRQ_STATUS_NUM (IRQ_STATUS_F_REG - IRQ_STATUS_A_REG + 1)
+
+/* Status bits and masks */
+#define SMB350_MASK(BITS, POS)		((u8)(((1 << BITS) - 1) << POS))
+#define FAST_CHG_CURRENT_MASK		SMB350_MASK(4, 4)
+
+#define SMB350_FAST_CHG_MIN_MA		1000
+#define SMB350_FAST_CHG_STEP_MA		200
+#define SMB350_FAST_CHG_MAX_MA		3600
+
+#define TERM_CURRENT_MASK		SMB350_MASK(3, 2)
+
+#define SMB350_TERM_CUR_MIN_MA		200
+#define SMB350_TERM_CUR_STEP_MA		100
+#define SMB350_TERM_CUR_MAX_MA		700
+
+#define CMD_A_VOLATILE_WR_PERM		BIT(7)
+#define CHG_CTRL_CURR_TERM_END_CHG	BIT(6)
+
+enum smb350_chg_status {
+	SMB_CHG_STATUS_NONE		= 0,
+	SMB_CHG_STATUS_PRE_CHARGE	= 1,
+	SMB_CHG_STATUS_FAST_CHARGE	= 2,
+	SMB_CHG_STATUS_TAPER_CHARGE	= 3,
+};
+
+static const char * const smb350_chg_status[] = {
+	"none",
+	"pre-charge",
+	"fast-charge",
+	"taper-charge"
+};
+
+struct smb350_device {
+	/* setup */
+	int			chg_current_ma;
+	int			term_current_ma;
+	int			chg_en_n_gpio;
+	int			chg_susp_n_gpio;
+	int			stat_gpio;
+	int			irq;
+	/* internal */
+	enum smb350_chg_status	chg_status;
+	struct i2c_client	*client;
+	struct delayed_work	irq_work;
+	struct dentry		*dent;
+	struct wake_lock	chg_wake_lock;
+	struct power_supply	dc_psy;
+};
+
+static struct smb350_device *smb350_dev;
+
+struct debug_reg {
+	char	*name;
+	u8	reg;
+};
+
+#define SMB350_DEBUG_REG(x) {#x, x##_REG}
+
+static struct debug_reg smb350_debug_regs[] = {
+	SMB350_DEBUG_REG(CHG_CURRENT),
+	SMB350_DEBUG_REG(CHG_OTHER_CURRENT),
+	SMB350_DEBUG_REG(VAR_FUNC),
+	SMB350_DEBUG_REG(FLOAT_VOLTAGE),
+	SMB350_DEBUG_REG(CHG_CTRL),
+	SMB350_DEBUG_REG(STAT_TIMER),
+	SMB350_DEBUG_REG(PIN_ENABLE_CTRL),
+	SMB350_DEBUG_REG(THERM_CTRL_A),
+	SMB350_DEBUG_REG(SYSOK_USB3_SELECT),
+	SMB350_DEBUG_REG(CTRL_FUNCTIONS),
+	SMB350_DEBUG_REG(OTG_TLIM_THERM_CNTRL),
+	SMB350_DEBUG_REG(TEMP_MONITOR),
+	SMB350_DEBUG_REG(FAULT_IRQ),
+	SMB350_DEBUG_REG(IRQ_ENABLE),
+	SMB350_DEBUG_REG(SYSOK),
+	SMB350_DEBUG_REG(AUTO_INPUT_VOLT_DETECT),
+	SMB350_DEBUG_REG(STATUS_IRQ),
+	SMB350_DEBUG_REG(I2C_SLAVE_ADDR),
+	SMB350_DEBUG_REG(CMD_A),
+	SMB350_DEBUG_REG(CMD_B),
+	SMB350_DEBUG_REG(CMD_C),
+	SMB350_DEBUG_REG(IRQ_STATUS_A),
+	SMB350_DEBUG_REG(IRQ_STATUS_B),
+	SMB350_DEBUG_REG(IRQ_STATUS_C),
+	SMB350_DEBUG_REG(IRQ_STATUS_D),
+	SMB350_DEBUG_REG(IRQ_STATUS_E),
+	SMB350_DEBUG_REG(IRQ_STATUS_F),
+	SMB350_DEBUG_REG(STATUS_A),
+	SMB350_DEBUG_REG(STATUS_B),
+	SMB350_DEBUG_REG(STATUS_D),
+	SMB350_DEBUG_REG(STATUS_E),
+};
+
+/*
+ * Read 8-bit register value. return negative value on error.
+ */
+static int smb350_read_reg(struct i2c_client *client, u8 reg)
+{
+	int val;
+
+	val = i2c_smbus_read_byte_data(client, reg);
+	if (val < 0)
+		pr_err("i2c read fail. reg=0x%x.ret=%d.\n", reg, val);
+	else
+		pr_debug("reg=0x%02X.val=0x%02X.\n", reg , val);
+
+	return val;
+}
+
+/*
+ * Write 8-bit register value. return negative value on error.
+ */
+static int smb350_write_reg(struct i2c_client *client, u8 reg, u8 val)
+{
+	int ret;
+
+	ret = i2c_smbus_write_byte_data(client, reg, val);
+	if (ret < 0)
+		pr_err("i2c read fail. reg=0x%x.val=0x%x.ret=%d.\n",
+		       reg, val, ret);
+	else
+		pr_debug("reg=0x%02X.val=0x%02X.\n", reg , val);
+
+	return ret;
+}
+
+static int smb350_masked_write(struct i2c_client *client, int reg, u8 mask,
+			       u8 val)
+{
+	int ret;
+	int temp;
+	int shift = find_first_bit((unsigned long *) &mask, 8);
+
+	temp = smb350_read_reg(client, reg);
+	if (temp < 0)
+		return temp;
+
+	temp &= ~mask;
+	temp |= (val << shift) & mask;
+	ret = smb350_write_reg(client, reg, temp);
+
+	return ret;
+}
+
+#define SMB350_FLOAT_VOLT_BASE_MV 6920
+#define SMB350_FLOAT_VOLT_STEP_MV   40
+#define SMB350_FLOAT_VOLT_MAX_MV  (6920 + 0x2F * 40)
+
+/* Fast-to-Taper charging volatge */
+static int smb350_get_float_voltage(struct i2c_client *client)
+{
+	u16 val = smb350_read_reg(client, STATUS_A_REG);
+
+	val = SMB350_FLOAT_VOLT_BASE_MV +
+		((val & 0x2F) * SMB350_FLOAT_VOLT_STEP_MV);
+
+	return val;
+}
+
+static bool smb350_is_dc_present(struct i2c_client *client)
+{
+	u16 irq_status_f = smb350_read_reg(client, IRQ_STATUS_F_REG);
+	bool power_ok = irq_status_f & 0x01;
+
+	/* Power-ok , IRQ_STATUS_F_REG bit#0 */
+	if (power_ok)
+		pr_debug("DC is present.\n");
+	else
+		pr_debug("DC is missing.\n");
+
+	return power_ok;
+}
+
+static bool smb350_is_charging(struct i2c_client *client)
+{
+	int val;
+	bool is_charging;
+
+	val = smb350_read_reg(client, STATUS_B_REG);
+	if (val < 0)
+		return false;
+
+	val = (val >> 1) & 0x3;
+
+	is_charging = (val != 0);
+
+	return is_charging;
+}
+
+static int smb350_get_prop_charge_type(struct smb350_device *dev)
+{
+	int status_b;
+	enum smb350_chg_status status;
+	int chg_type = POWER_SUPPLY_CHARGE_TYPE_UNKNOWN;
+	bool chg_enabled;
+	bool charger_err;
+	struct i2c_client *client = dev->client;
+
+	status_b = smb350_read_reg(client, STATUS_B_REG);
+	if (status_b < 0) {
+		pr_err("failed to read STATUS_B_REG.\n");
+		return POWER_SUPPLY_CHARGE_TYPE_UNKNOWN;
+	}
+
+	chg_enabled = (bool) (status_b & 0x01);
+	charger_err = (bool) (status_b & (1<<6));
+
+	if (!chg_enabled) {
+		pr_warn("Charging not enabled.\n");
+		return POWER_SUPPLY_CHARGE_TYPE_NONE;
+	}
+
+	if (charger_err) {
+		pr_warn("Charger error detected.\n");
+		return POWER_SUPPLY_CHARGE_TYPE_NONE;
+	}
+
+	status = (status_b >> 1) & 0x3;
+
+	if (status == SMB_CHG_STATUS_NONE)
+		chg_type = POWER_SUPPLY_CHARGE_TYPE_NONE;
+	else if (status == SMB_CHG_STATUS_FAST_CHARGE) /* constant current */
+		chg_type = POWER_SUPPLY_CHARGE_TYPE_FAST;
+	else if (status == SMB_CHG_STATUS_TAPER_CHARGE) /* constant voltage */
+		chg_type = POWER_SUPPLY_CHARGE_TYPE_FAST;
+	else if (status == SMB_CHG_STATUS_PRE_CHARGE)
+		chg_type = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
+
+	pr_debug("smb-chg-status=%d=%s.\n", status, smb350_chg_status[status]);
+
+	if (dev->chg_status != status) { /* Status changed */
+		if (status == SMB_CHG_STATUS_NONE) {
+			pr_debug("Charging stopped.\n");
+			wake_unlock(&dev->chg_wake_lock);
+		} else {
+			pr_debug("Charging started.\n");
+			wake_lock(&dev->chg_wake_lock);
+		}
+	}
+
+	dev->chg_status = status;
+
+	return chg_type;
+}
+
+static void smb350_enable_charging(struct smb350_device *dev, bool enable)
+{
+	int val = !enable; /* active low */
+
+	pr_debug("enable=%d.\n", enable);
+
+	gpio_set_value_cansleep(dev->chg_en_n_gpio, val);
+}
+
+/* When the status bit of a certain condition is read,
+ * the corresponding IRQ signal is cleared.
+ */
+static int smb350_clear_irq(struct i2c_client *client)
+{
+	int ret;
+
+	ret = smb350_read_reg(client, IRQ_STATUS_A_REG);
+	if (ret < 0)
+		return ret;
+	ret = smb350_read_reg(client, IRQ_STATUS_B_REG);
+	if (ret < 0)
+		return ret;
+	ret = smb350_read_reg(client, IRQ_STATUS_C_REG);
+	if (ret < 0)
+		return ret;
+	ret = smb350_read_reg(client, IRQ_STATUS_D_REG);
+	if (ret < 0)
+		return ret;
+	ret = smb350_read_reg(client, IRQ_STATUS_E_REG);
+	if (ret < 0)
+		return ret;
+	ret = smb350_read_reg(client, IRQ_STATUS_F_REG);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+/*
+ * Do the IRQ work from a thread context rather than interrupt context.
+ * Read status registers to clear interrupt source.
+ * Notify the power-supply driver about change detected.
+ * Relevant events for start/stop charging:
+ * 1. DC insert/remove
+ * 2. End-Of-Charging
+ * 3. Battery insert/remove
+ * 4. Temperture too hot/cold
+ * 5. Charging timeout expired.
+ */
+static void smb350_irq_worker(struct work_struct *work)
+{
+	int ret = 0;
+	struct smb350_device *dev =
+		container_of(work, struct smb350_device, irq_work.work);
+
+	ret = smb350_clear_irq(dev->client);
+	if (ret == 0) { /* Cleared ok */
+		/* Notify Battery-psy about status changed */
+		pr_debug("Notify power_supply_changed.\n");
+		power_supply_changed(&dev->dc_psy);
+	}
+}
+
+/*
+ * The STAT pin is low when charging and high when not charging.
+ * When the smb350 start/stop charging the STAT pin triggers an interrupt.
+ * Interrupt is triggered on both rising or falling edge.
+ */
+static irqreturn_t smb350_irq(int irq, void *dev_id)
+{
+	struct smb350_device *dev = dev_id;
+
+	pr_debug("\n");
+
+	/* I2C transfers API should not run in interrupt context */
+	schedule_delayed_work(&dev->irq_work, msecs_to_jiffies(100));
+
+	return IRQ_HANDLED;
+}
+
+static enum power_supply_property pm_power_props[] = {
+	/* real time */
+	POWER_SUPPLY_PROP_PRESENT,
+	POWER_SUPPLY_PROP_ONLINE,
+	POWER_SUPPLY_PROP_CHARGE_TYPE,
+	POWER_SUPPLY_PROP_VOLTAGE_NOW,
+	/* fixed */
+	POWER_SUPPLY_PROP_MANUFACTURER,
+	POWER_SUPPLY_PROP_MODEL_NAME,
+	POWER_SUPPLY_PROP_CURRENT_MAX,
+};
+
+static char *pm_power_supplied_to[] = {
+	"battery",
+};
+
+static int smb350_get_property(struct power_supply *psy,
+			       enum power_supply_property psp,
+			       union power_supply_propval *val)
+{
+	int ret = 0;
+	struct smb350_device *dev = container_of(psy,
+						 struct smb350_device,
+						 dc_psy);
+	struct i2c_client *client = dev->client;
+
+	switch (psp) {
+	case POWER_SUPPLY_PROP_PRESENT:
+		val->intval = smb350_is_dc_present(client);
+		break;
+	case POWER_SUPPLY_PROP_ONLINE:
+		val->intval = smb350_is_charging(client);
+		break;
+	case POWER_SUPPLY_PROP_CHARGE_TYPE:
+		val->intval = smb350_get_prop_charge_type(dev);
+		break;
+	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+		val->intval = smb350_get_float_voltage(client);
+		val->intval *= 1000; /* mV to uV */
+		break;
+	case POWER_SUPPLY_PROP_MODEL_NAME:
+		val->strval = SMB350_NAME;
+		break;
+	case POWER_SUPPLY_PROP_MANUFACTURER:
+		val->strval = "Summit Microelectronics";
+		break;
+	case POWER_SUPPLY_PROP_CURRENT_MAX:
+		val->intval = dev->chg_current_ma;
+		break;
+	default:
+		pr_err("Invalid prop = %d.\n", psp);
+		ret = -EINVAL;
+		break;
+	}
+
+	return ret;
+}
+
+static int smb350_set_property(struct power_supply *psy,
+			       enum power_supply_property psp,
+			       const union power_supply_propval *val)
+{
+	int ret = 0;
+	struct smb350_device *dev =
+		container_of(psy, struct smb350_device, dc_psy);
+
+	switch (psp) {
+	/*
+	 *  Allow a smart battery to Start/Stop charging.
+	 *  i.e. when End-Of-Charging detected.
+	 *  The SMB350 can be configured to terminate charging
+	 *  when charge-current reaching Termination-Current.
+	 */
+	case POWER_SUPPLY_PROP_ONLINE:
+		smb350_enable_charging(dev, val->intval);
+		break;
+	default:
+		pr_err("Invalid prop = %d.\n", psp);
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+static int smb350_set_chg_current(struct i2c_client *client, int current_ma)
+{
+	int ret;
+	u8 temp;
+
+	if ((current_ma < SMB350_FAST_CHG_MIN_MA) ||
+	    (current_ma >  SMB350_FAST_CHG_MAX_MA)) {
+		pr_err("invalid current %d mA.\n", current_ma);
+		return -EINVAL;
+	}
+
+	temp = (current_ma - SMB350_FAST_CHG_MIN_MA) / SMB350_FAST_CHG_STEP_MA;
+
+	pr_debug("fast-chg-current=%d mA setting %02x\n", current_ma, temp);
+
+	ret = smb350_masked_write(client, CHG_CURRENT_REG,
+				  FAST_CHG_CURRENT_MASK, temp);
+
+	return ret;
+}
+
+static int smb350_set_term_current(struct i2c_client *client, int current_ma)
+{
+	int ret;
+	u8 temp;
+
+	if ((current_ma < SMB350_TERM_CUR_MIN_MA) ||
+	    (current_ma >  SMB350_TERM_CUR_MAX_MA)) {
+		pr_err("invalid current %d mA to set\n", current_ma);
+		return -EINVAL;
+	}
+
+	temp = (current_ma - SMB350_TERM_CUR_MIN_MA) / SMB350_TERM_CUR_STEP_MA;
+
+	pr_debug("term-current=%d mA setting %02x\n", current_ma, temp);
+
+	ret = smb350_masked_write(client, CHG_OTHER_CURRENT_REG,
+				  TERM_CURRENT_MASK, temp);
+
+	return ret;
+}
+
+static int smb350_set_reg(void *data, u64 val)
+{
+	u32 addr = (u32) data;
+	int ret;
+	struct i2c_client *client = smb350_dev->client;
+
+	ret = smb350_write_reg(client, addr, (u8) val);
+
+	return ret;
+}
+
+static int smb350_get_reg(void *data, u64 *val)
+{
+	u32 addr = (u32) data;
+	int ret;
+	struct i2c_client *client = smb350_dev->client;
+
+	ret = smb350_read_reg(client, addr);
+	if (ret < 0)
+		return ret;
+
+	*val = ret;
+
+	return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(reg_fops, smb350_get_reg, smb350_set_reg, "0x%02llx\n");
+
+static int smb350_create_debugfs_entries(struct smb350_device *dev)
+{
+	int i;
+	dev->dent = debugfs_create_dir(SMB350_NAME, NULL);
+	if (IS_ERR(dev->dent)) {
+		pr_err("smb350 driver couldn't create debugfs dir\n");
+		return -EFAULT;
+	}
+
+	for (i = 0 ; i < ARRAY_SIZE(smb350_debug_regs) ; i++) {
+		char *name = smb350_debug_regs[i].name;
+		u32 reg = smb350_debug_regs[i].reg;
+		struct dentry *file;
+
+		file = debugfs_create_file(name, 0644, dev->dent,
+					(void *) reg, &reg_fops);
+		if (IS_ERR(file)) {
+			pr_err("debugfs_create_file %s failed.\n", name);
+			return -EFAULT;
+		}
+	}
+
+	return 0;
+}
+
+static int smb350_set_volatile_params(struct smb350_device *dev)
+{
+	int ret;
+	struct i2c_client *client = dev->client;
+
+	pr_debug("\n");
+
+	ret = smb350_write_reg(client, CMD_A_REG, CMD_A_VOLATILE_WR_PERM);
+	if (ret) {
+		pr_err("Failed to set VOLATILE_WR_PERM ret=%d\n", ret);
+		return ret;
+	}
+
+	/* Disable SMB350 pulse-IRQ mechanism,
+	 * we use interrupts based on charging-status-transition
+	 */
+	/* Enable STATUS output (regardless of IRQ-pulses) */
+	smb350_masked_write(client, CMD_A_REG, BIT(0), 0);
+
+	/* Disable LED blinking - avoid periodic irq */
+	smb350_masked_write(client, PIN_ENABLE_CTRL_REG, BIT(7), 0);
+
+	/* Disable Failure SMB-IRQ */
+	ret = smb350_write_reg(client, FAULT_IRQ_REG, 0x00);
+	if (ret) {
+		pr_err("Failed to set FAULT_IRQ_REG ret=%d\n", ret);
+		return ret;
+	}
+
+	/* Disable Event IRQ */
+	ret = smb350_write_reg(client, IRQ_ENABLE_REG, 0x00);
+	if (ret) {
+		pr_err("Failed to set IRQ_ENABLE_REG ret=%d\n", ret);
+		return ret;
+	}
+
+	/* Enable charging/not-charging status output via STAT pin */
+	smb350_masked_write(client, STAT_TIMER_REG, BIT(5), 0);
+
+	/* Disable Automatic Recharge */
+	smb350_masked_write(client, CHG_CTRL_REG, BIT(7), 1);
+
+	/* Set fast-charge current */
+	ret = smb350_set_chg_current(client, dev->chg_current_ma);
+	if (ret) {
+		pr_err("Failed to set FAST_CHG_CURRENT ret=%d\n", ret);
+		return ret;
+	}
+
+	if (dev->term_current_ma > 0) {
+		/* Enable Current Termination */
+		smb350_masked_write(client, CHG_CTRL_REG, BIT(6), 0);
+
+		/* Set Termination current */
+		smb350_set_term_current(client, dev->term_current_ma);
+	} else {
+		/* Disable Current Termination */
+		smb350_masked_write(client, CHG_CTRL_REG, BIT(6), 1);
+	}
+
+	return 0;
+}
+
+static int __devinit smb350_register_psy(struct smb350_device *dev)
+{
+	int ret;
+
+	dev->dc_psy.name = "dc";
+	dev->dc_psy.type = POWER_SUPPLY_TYPE_MAINS;
+	dev->dc_psy.supplied_to = pm_power_supplied_to;
+	dev->dc_psy.num_supplicants = ARRAY_SIZE(pm_power_supplied_to);
+	dev->dc_psy.properties = pm_power_props;
+	dev->dc_psy.num_properties = ARRAY_SIZE(pm_power_props);
+	dev->dc_psy.get_property = smb350_get_property;
+	dev->dc_psy.set_property = smb350_set_property;
+
+	ret = power_supply_register(&dev->client->dev,
+				&dev->dc_psy);
+	if (ret) {
+		pr_err("failed to register power_supply. ret=%d.\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int __devinit smb350_probe(struct i2c_client *client,
+				  const struct i2c_device_id *id)
+{
+	int ret = 0;
+	const struct smb350_platform_data *pdata;
+	struct device_node *dev_node = client->dev.of_node;
+	struct smb350_device *dev;
+
+	/* STAT pin change on start/stop charging */
+	u32 irq_flags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING;
+
+	if (!i2c_check_functionality(client->adapter,
+				I2C_FUNC_SMBUS_BYTE_DATA)) {
+		pr_err("i2c func fail.\n");
+		return -EIO;
+	}
+
+	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+	if (!dev) {
+		pr_err("alloc fail.\n");
+		return -ENOMEM;
+	}
+
+	smb350_dev = dev;
+	dev->client = client;
+
+	if (dev_node) {
+		dev->chg_en_n_gpio =
+			of_get_named_gpio(dev_node, "summit,chg-en-n-gpio", 0);
+		pr_debug("chg_en_n_gpio = %d.\n", dev->chg_en_n_gpio);
+
+		dev->chg_susp_n_gpio =
+			of_get_named_gpio(dev_node,
+					  "summit,chg-susp-n-gpio", 0);
+		pr_debug("chg_susp_n_gpio = %d.\n", dev->chg_susp_n_gpio);
+
+		dev->stat_gpio =
+			of_get_named_gpio(dev_node, "summit,stat-gpio", 0);
+		pr_debug("stat_gpio = %d.\n", dev->stat_gpio);
+
+		ret = of_property_read_u32(dev_node, "summit,chg-current-ma",
+					   &(dev->chg_current_ma));
+		pr_debug("chg_current_ma = %d.\n", dev->chg_current_ma);
+		if (ret) {
+			pr_err("Unable to read chg_current.\n");
+			return ret;
+		}
+		ret = of_property_read_u32(dev_node, "summit,term-current-ma",
+					   &(dev->term_current_ma));
+		pr_debug("term_current_ma = %d.\n", dev->term_current_ma);
+		if (ret) {
+			pr_err("Unable to read term_current_ma.\n");
+			return ret;
+		}
+	} else {
+		pdata = client->dev.platform_data;
+
+		if (pdata == NULL) {
+			pr_err("no platform data.\n");
+			return -EINVAL;
+		}
+
+		dev->chg_en_n_gpio = pdata->chg_en_n_gpio;
+		dev->chg_susp_n_gpio = pdata->chg_susp_n_gpio;
+		dev->stat_gpio = pdata->stat_gpio;
+
+		dev->chg_current_ma = pdata->chg_current_ma;
+		dev->term_current_ma = pdata->term_current_ma;
+	}
+
+	ret = gpio_request(dev->stat_gpio, "smb350_stat");
+	if (ret) {
+		pr_err("gpio_request failed for %d ret=%d\n",
+		       dev->stat_gpio, ret);
+		goto err_stat_gpio;
+	}
+	dev->irq = gpio_to_irq(dev->stat_gpio);
+	pr_debug("irq#=%d.\n", dev->irq);
+
+	ret = gpio_request(dev->chg_susp_n_gpio, "smb350_suspend");
+	if (ret) {
+		pr_err("gpio_request failed for %d ret=%d\n",
+			dev->chg_susp_n_gpio, ret);
+		goto err_susp_gpio;
+	}
+
+	ret = gpio_request(dev->chg_en_n_gpio, "smb350_charger_enable");
+	if (ret) {
+		pr_err("gpio_request failed for %d ret=%d\n",
+			dev->chg_en_n_gpio, ret);
+		goto err_en_gpio;
+	}
+
+	i2c_set_clientdata(client, dev);
+
+	/* Disable battery charging by default on power up.
+	 * Battery charging is enabled by BMS or Battery-Gauge
+	 * by using the set_property callback.
+	 */
+	smb350_enable_charging(dev, false);
+	msleep(100);
+	gpio_set_value_cansleep(dev->chg_susp_n_gpio, 1); /* Normal */
+	msleep(100); /* Allow the device to exist shutdown */
+
+	/* I2C transaction allowed only after device exit suspend */
+	ret = smb350_read_reg(client, I2C_SLAVE_ADDR_REG);
+	if ((ret>>1) != client->addr) {
+		pr_err("No device.\n");
+		ret = -ENODEV;
+		goto err_no_dev;
+	}
+
+	ret = smb350_set_volatile_params(dev);
+	if (ret)
+		goto err_set_params;
+
+	ret = smb350_register_psy(dev);
+	if (ret)
+		goto err_set_params;
+
+	ret = smb350_create_debugfs_entries(dev);
+	if (ret)
+		goto err_debugfs;
+
+	INIT_DELAYED_WORK(&dev->irq_work, smb350_irq_worker);
+	wake_lock_init(&dev->chg_wake_lock,
+		       WAKE_LOCK_SUSPEND, SMB350_NAME);
+
+	ret = request_irq(dev->irq, smb350_irq, irq_flags,
+			  "smb350_irq", dev);
+	if (ret) {
+		pr_err("request_irq %d failed.ret=%d\n", dev->irq, ret);
+		goto err_irq;
+	}
+
+	return 0;
+
+err_irq:
+err_debugfs:
+	if (dev->dent)
+		debugfs_remove_recursive(dev->dent);
+err_no_dev:
+err_set_params:
+	gpio_free(dev->chg_en_n_gpio);
+err_en_gpio:
+	gpio_free(dev->chg_susp_n_gpio);
+err_susp_gpio:
+	gpio_free(dev->stat_gpio);
+err_stat_gpio:
+	kfree(smb350_dev);
+	smb350_dev = NULL;
+
+	pr_info("FAIL.\n");
+
+	return ret;
+}
+
+static int __devexit smb350_remove(struct i2c_client *client)
+{
+	struct smb350_device *dev = i2c_get_clientdata(client);
+
+	power_supply_unregister(&dev->dc_psy);
+	gpio_free(dev->chg_en_n_gpio);
+	gpio_free(dev->chg_susp_n_gpio);
+	if (dev->stat_gpio)
+		gpio_free(dev->stat_gpio);
+	if (dev->irq)
+		free_irq(dev->irq, dev);
+	if (dev->dent)
+		debugfs_remove_recursive(dev->dent);
+	kfree(smb350_dev);
+	smb350_dev = NULL;
+
+	return 0;
+}
+
+static const struct i2c_device_id smb350_id[] = {
+	{SMB350_NAME, 0},
+	{},
+};
+MODULE_DEVICE_TABLE(i2c, smb350_id);
+
+static const struct of_device_id smb350_match[] = {
+	{ .compatible = "summit,smb350-charger", },
+	{ },
+};
+
+static struct i2c_driver smb350_driver = {
+	.driver	= {
+			.name	= SMB350_NAME,
+			.owner	= THIS_MODULE,
+			.of_match_table = of_match_ptr(smb350_match),
+	},
+	.probe		= smb350_probe,
+	.remove		= __devexit_p(smb350_remove),
+	.id_table	= smb350_id,
+};
+
+static int __init smb350_init(void)
+{
+	return i2c_add_driver(&smb350_driver);
+}
+module_init(smb350_init);
+
+static void __exit smb350_exit(void)
+{
+	return i2c_del_driver(&smb350_driver);
+}
+module_exit(smb350_exit);
+
+MODULE_DESCRIPTION("Driver for SMB350 charger chip");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("i2c:" SMB350_NAME);
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index 6b0916e..8f924d6 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -195,6 +195,15 @@
 	  via I2C bus. The provided regulator is suitable for S3C6410
 	  and S5PC1XX chips to control VCC_CORE and VCC_USIM voltages.
 
+config REGULATOR_ONSEMI_NCP6335D
+	tristate "OnSemi NCP6335D regulator support"
+	depends on I2C
+	help
+	 This driver supports the OnSemi NCP6335D switching voltage regulator
+	 (buck convertor). The regulator is controlled using an I2C interface
+	 and supports a programmable voltage range from 0.6V to 1.4V in steps
+	 of 6.25mV.
+
 config REGULATOR_PCAP
 	tristate "Motorola PCAP2 regulator driver"
 	depends on EZX_PCAP
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
index 7fa396f..054ce42 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -33,6 +33,7 @@
 obj-$(CONFIG_REGULATOR_MC13783) += mc13783-regulator.o
 obj-$(CONFIG_REGULATOR_MC13892) += mc13892-regulator.o
 obj-$(CONFIG_REGULATOR_MC13XXX_CORE) +=  mc13xxx-regulator-core.o
+obj-$(CONFIG_REGULATOR_ONSEMI_NCP6335D) += onsemi-ncp6335d.o
 obj-$(CONFIG_REGULATOR_PCAP) += pcap-regulator.o
 obj-$(CONFIG_REGULATOR_PCF50633) += pcf50633-regulator.o
 obj-$(CONFIG_REGULATOR_S5M8767) += s5m8767.o
diff --git a/drivers/regulator/onsemi-ncp6335d.c b/drivers/regulator/onsemi-ncp6335d.c
new file mode 100644
index 0000000..a0c90f0
--- /dev/null
+++ b/drivers/regulator/onsemi-ncp6335d.c
@@ -0,0 +1,376 @@
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/regulator/driver.h>
+#include <linux/regmap.h>
+#include <linux/regulator/onsemi-ncp6335d.h>
+
+/* registers */
+#define REG_NCP6335D_PID		0x03
+#define REG_NCP6335D_PROGVSEL1		0x10
+#define REG_NCP6335D_PROGVSEL0		0x11
+#define REG_NCP6335D_PGOOD		0x12
+#define REG_NCP6335D_TIMING		0x13
+#define REG_NCP6335D_COMMAND		0x14
+
+/* constraints */
+#define NCP6335D_MIN_VOLTAGE_UV		600000
+#define NCP6335D_STEP_VOLTAGE_UV	6250
+#define NCP6335D_MIN_SLEW_NS		166
+#define NCP6335D_MAX_SLEW_NS		1333
+
+/* bits */
+#define NCP6335D_ENABLE			BIT(7)
+#define NCP6335D_DVS_PWM_MODE		BIT(5)
+#define NCP6335D_PWM_MODE1		BIT(6)
+#define NCP6335D_PWM_MODE0		BIT(7)
+#define NCP6335D_PGOOD_DISCHG		BIT(4)
+
+#define NCP6335D_VOUT_SEL_MASK		0x7F
+#define NCP6335D_SLEW_MASK		0x18
+#define NCP6335D_SLEW_SHIFT		0x3
+
+struct ncp6335d_info {
+	struct regulator_dev *regulator;
+	struct regulator_init_data *init_data;
+	struct regmap *regmap;
+	struct device *dev;
+	unsigned int vsel_reg;
+	unsigned int mode_bit;
+	int curr_voltage;
+	int slew_rate;
+};
+
+static void dump_registers(struct ncp6335d_info *dd,
+			unsigned int reg, const char *func)
+{
+	unsigned int val = 0;
+
+	regmap_read(dd->regmap, reg, &val);
+	dev_dbg(dd->dev, "%s: NCP6335D: Reg = %x, Val = %x\n", func, reg, val);
+}
+
+static void ncp633d_slew_delay(struct ncp6335d_info *dd,
+					int prev_uV, int new_uV)
+{
+	u8 val;
+	int delay;
+
+	val = abs(prev_uV - new_uV) / NCP6335D_STEP_VOLTAGE_UV;
+	delay =  (val * dd->slew_rate / 1000) + 1;
+
+	dev_dbg(dd->dev, "Slew Delay = %d\n", delay);
+
+	udelay(delay);
+}
+
+static int ncp6335d_enable(struct regulator_dev *rdev)
+{
+	int rc;
+	struct ncp6335d_info *dd = rdev_get_drvdata(rdev);
+
+	rc = regmap_update_bits(dd->regmap, dd->vsel_reg,
+				NCP6335D_ENABLE, NCP6335D_ENABLE);
+	if (rc)
+		dev_err(dd->dev, "Unable to enable regualtor rc(%d)", rc);
+
+	dump_registers(dd, dd->vsel_reg, __func__);
+
+	return rc;
+}
+
+static int ncp6335d_disable(struct regulator_dev *rdev)
+{
+	int rc;
+	struct ncp6335d_info *dd = rdev_get_drvdata(rdev);
+
+	rc = regmap_update_bits(dd->regmap, dd->vsel_reg,
+					NCP6335D_ENABLE, 0);
+	if (rc)
+		dev_err(dd->dev, "Unable to disable regualtor rc(%d)", rc);
+
+	dump_registers(dd, dd->vsel_reg, __func__);
+
+	return rc;
+}
+
+static int ncp6335d_get_voltage(struct regulator_dev *rdev)
+{
+	unsigned int val;
+	int rc;
+	struct ncp6335d_info *dd = rdev_get_drvdata(rdev);
+
+	rc = regmap_read(dd->regmap, dd->vsel_reg, &val);
+	if (rc) {
+		dev_err(dd->dev, "Unable to get volatge rc(%d)", rc);
+		return rc;
+	}
+	dd->curr_voltage = ((val & NCP6335D_VOUT_SEL_MASK) *
+			NCP6335D_STEP_VOLTAGE_UV) + NCP6335D_MIN_VOLTAGE_UV;
+
+	dump_registers(dd, dd->vsel_reg, __func__);
+
+	return dd->curr_voltage;
+}
+
+static int ncp6335d_set_voltage(struct regulator_dev *rdev,
+			int min_uV, int max_uV, unsigned *selector)
+{
+	int rc, set_val, new_uV;
+	struct ncp6335d_info *dd = rdev_get_drvdata(rdev);
+
+	set_val = DIV_ROUND_UP(min_uV - NCP6335D_MIN_VOLTAGE_UV,
+					NCP6335D_STEP_VOLTAGE_UV);
+	new_uV = (set_val * NCP6335D_STEP_VOLTAGE_UV) +
+					NCP6335D_MIN_VOLTAGE_UV;
+	if (new_uV > max_uV) {
+		dev_err(dd->dev, "Unable to set volatge (%d %d)\n",
+							min_uV, max_uV);
+		return -EINVAL;
+	}
+
+	rc = regmap_update_bits(dd->regmap, dd->vsel_reg,
+		NCP6335D_VOUT_SEL_MASK, (set_val & NCP6335D_VOUT_SEL_MASK));
+	if (rc) {
+		dev_err(dd->dev, "Unable to set volatge (%d %d)\n",
+							min_uV, max_uV);
+	} else {
+		ncp633d_slew_delay(dd, dd->curr_voltage, new_uV);
+		dd->curr_voltage = new_uV;
+	}
+
+	dump_registers(dd, dd->vsel_reg, __func__);
+
+	return rc;
+}
+
+static int ncp6335d_set_mode(struct regulator_dev *rdev,
+					unsigned int mode)
+{
+	int rc;
+	struct ncp6335d_info *dd = rdev_get_drvdata(rdev);
+
+	/* only FAST and NORMAL mode types are supported */
+	if (mode != REGULATOR_MODE_FAST && mode != REGULATOR_MODE_NORMAL) {
+		dev_err(dd->dev, "Mode %d not supported\n", mode);
+		return -EINVAL;
+	}
+
+	rc = regmap_update_bits(dd->regmap, REG_NCP6335D_COMMAND, dd->mode_bit,
+			(mode == REGULATOR_MODE_FAST) ? dd->mode_bit : 0);
+	if (rc) {
+		dev_err(dd->dev, "Unable to set operating mode rc(%d)", rc);
+		return rc;
+	}
+
+	rc = regmap_update_bits(dd->regmap, REG_NCP6335D_COMMAND,
+					NCP6335D_DVS_PWM_MODE,
+					(mode == REGULATOR_MODE_FAST) ?
+					NCP6335D_DVS_PWM_MODE : 0);
+	if (rc)
+		dev_err(dd->dev, "Unable to set DVS trans. mode rc(%d)", rc);
+
+	dump_registers(dd, REG_NCP6335D_COMMAND, __func__);
+
+	return rc;
+}
+
+static unsigned int ncp6335d_get_mode(struct regulator_dev *rdev)
+{
+	unsigned int val;
+	int rc;
+	struct ncp6335d_info *dd = rdev_get_drvdata(rdev);
+
+	rc = regmap_read(dd->regmap, REG_NCP6335D_COMMAND, &val);
+	if (rc) {
+		dev_err(dd->dev, "Unable to get regulator mode rc(%d)\n", rc);
+		return rc;
+	}
+
+	dump_registers(dd, REG_NCP6335D_COMMAND, __func__);
+
+	if (val & dd->mode_bit)
+		return REGULATOR_MODE_FAST;
+
+	return REGULATOR_MODE_NORMAL;
+}
+
+static struct regulator_ops ncp6335d_ops = {
+	.set_voltage = ncp6335d_set_voltage,
+	.get_voltage = ncp6335d_get_voltage,
+	.enable = ncp6335d_enable,
+	.disable = ncp6335d_disable,
+	.set_mode = ncp6335d_set_mode,
+	.get_mode = ncp6335d_get_mode,
+};
+
+static struct regulator_desc rdesc = {
+	.name = "ncp6335d",
+	.owner = THIS_MODULE,
+	.n_voltages = 128,
+	.ops = &ncp6335d_ops,
+};
+
+static int __devinit ncp6335d_init(struct ncp6335d_info *dd,
+			const struct ncp6335d_platform_data *pdata)
+{
+	int rc;
+	unsigned int val;
+
+	switch (pdata->default_vsel) {
+	case NCP6335D_VSEL0:
+		dd->vsel_reg = REG_NCP6335D_PROGVSEL0;
+		dd->mode_bit = NCP6335D_PWM_MODE0;
+	break;
+	case NCP6335D_VSEL1:
+		dd->vsel_reg = REG_NCP6335D_PROGVSEL1;
+		dd->mode_bit = NCP6335D_PWM_MODE1;
+	break;
+	default:
+		dev_err(dd->dev, "Invalid VSEL ID %d\n", pdata->default_vsel);
+		return -EINVAL;
+	}
+
+	/* get the current programmed voltage */
+	rc = regmap_read(dd->regmap, dd->vsel_reg, &val);
+	if (rc) {
+		dev_err(dd->dev, "Unable to get volatge rc(%d)", rc);
+		return rc;
+	}
+	dd->curr_voltage = ((val & NCP6335D_VOUT_SEL_MASK) *
+			NCP6335D_STEP_VOLTAGE_UV) + NCP6335D_MIN_VOLTAGE_UV;
+
+	/* set discharge */
+	rc = regmap_update_bits(dd->regmap, REG_NCP6335D_PGOOD,
+					NCP6335D_PGOOD_DISCHG,
+					(pdata->discharge_enable ?
+					NCP6335D_PGOOD_DISCHG : 0));
+	if (rc) {
+		dev_err(dd->dev, "Unable to set Active Discharge rc(%d)\n", rc);
+		return -EINVAL;
+	}
+
+	/* set slew rate */
+	if (pdata->slew_rate_ns < NCP6335D_MIN_SLEW_NS ||
+			pdata->slew_rate_ns > NCP6335D_MAX_SLEW_NS) {
+		dev_err(dd->dev, "Invalid slew rate %d\n", pdata->slew_rate_ns);
+		return -EINVAL;
+	}
+	val = DIV_ROUND_UP(pdata->slew_rate_ns - NCP6335D_MIN_SLEW_NS,
+						NCP6335D_MIN_SLEW_NS);
+	val >>= 1;
+	dd->slew_rate = val * NCP6335D_MIN_SLEW_NS;
+
+	rc = regmap_update_bits(dd->regmap, REG_NCP6335D_TIMING,
+			NCP6335D_SLEW_MASK, val << NCP6335D_SLEW_SHIFT);
+	if (rc)
+		dev_err(dd->dev, "Unable to set slew rate rc(%d)\n", rc);
+
+	dump_registers(dd, REG_NCP6335D_PROGVSEL0, __func__);
+	dump_registers(dd, REG_NCP6335D_TIMING, __func__);
+	dump_registers(dd, REG_NCP6335D_PGOOD, __func__);
+
+	return rc;
+}
+
+static struct regmap_config ncp6335d_regmap_config = {
+	.reg_bits = 8,
+	.val_bits = 8,
+};
+
+static int __devinit ncp6335d_regulator_probe(struct i2c_client *client,
+					const struct i2c_device_id *id)
+{
+	int rc;
+	unsigned int val = 0;
+	struct ncp6335d_info *dd;
+	const struct ncp6335d_platform_data *pdata;
+
+	pdata = client->dev.platform_data;
+	if (!pdata) {
+		dev_err(&client->dev, "Platform data not specified\n");
+		return -EINVAL;
+	}
+
+	dd = devm_kzalloc(&client->dev, sizeof(*dd), GFP_KERNEL);
+	if (!dd) {
+		dev_err(&client->dev, "Unable to allocate memory\n");
+		return -ENOMEM;
+	}
+
+	dd->regmap = devm_regmap_init_i2c(client, &ncp6335d_regmap_config);
+	if (IS_ERR(dd->regmap)) {
+		dev_err(&client->dev, "Error allocating regmap\n");
+		return PTR_ERR(dd->regmap);
+	}
+
+	rc = regmap_read(dd->regmap, REG_NCP6335D_PID, &val);
+	if (rc) {
+		dev_err(&client->dev, "Unable to identify NCP6335D, rc(%d)\n",
+									rc);
+		return rc;
+	}
+	dev_info(&client->dev, "Detected Regulator NCP6335D PID = %d\n", val);
+
+	dd->init_data = pdata->init_data;
+	dd->dev = &client->dev;
+	i2c_set_clientdata(client, dd);
+
+	rc = ncp6335d_init(dd, pdata);
+	if (rc) {
+		dev_err(&client->dev, "Unable to intialize the regulator\n");
+		return -EINVAL;
+	}
+
+	dd->regulator = regulator_register(&rdesc, &client->dev,
+					dd->init_data, dd, NULL);
+	if (IS_ERR(dd->regulator)) {
+		dev_err(&client->dev, "Unable to register regulator rc(%ld)",
+						PTR_ERR(dd->regulator));
+		return PTR_ERR(dd->regulator);
+	}
+
+	return 0;
+}
+
+static int __devexit ncp6335d_regulator_remove(struct i2c_client *client)
+{
+	struct ncp6335d_info *dd = i2c_get_clientdata(client);
+
+	regulator_unregister(dd->regulator);
+
+	return 0;
+}
+
+static const struct i2c_device_id ncp6335d_id[] = {
+	{"ncp6335d", -1},
+	{ },
+};
+
+static struct i2c_driver ncp6335d_regulator_driver = {
+	.driver = {
+		.name = "ncp6335d-regulator",
+	},
+	.probe = ncp6335d_regulator_probe,
+	.remove = __devexit_p(ncp6335d_regulator_remove),
+	.id_table = ncp6335d_id,
+};
+
+module_i2c_driver(ncp6335d_regulator_driver);
+
+MODULE_DESCRIPTION("OnSemi-NCP6335D regulator driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/regulator/stub-regulator.c b/drivers/regulator/stub-regulator.c
index 1c4b935..85c5972 100644
--- a/drivers/regulator/stub-regulator.c
+++ b/drivers/regulator/stub-regulator.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2012, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -16,11 +16,14 @@
 #include <linux/module.h>
 #include <linux/err.h>
 #include <linux/kernel.h>
-#include <linux/regulator/driver.h>
-#include <linux/regulator/stub-regulator.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
 #include <linux/platform_device.h>
 #include <linux/slab.h>
 #include <linux/types.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/regulator/stub-regulator.h>
 
 #define STUB_REGULATOR_MAX_NAME 40
 
@@ -138,27 +141,74 @@
 
 static int __devinit regulator_stub_probe(struct platform_device *pdev)
 {
+	struct regulator_init_data *init_data = NULL;
+	struct device *dev = &pdev->dev;
 	struct stub_regulator_pdata *vreg_pdata;
 	struct regulator_desc *rdesc;
 	struct regulator_stub *vreg_priv;
 	int rc;
 
-	vreg_pdata = pdev->dev.platform_data;
-	if (!vreg_pdata) {
-		dev_err(&pdev->dev, "%s: no platform data\n", __func__);
-		return -EINVAL;
-	}
-
 	vreg_priv = kzalloc(sizeof(*vreg_priv), GFP_KERNEL);
 	if (!vreg_priv) {
-		dev_err(&pdev->dev, "%s: Unable to allocate memory\n",
+		dev_err(dev, "%s: Unable to allocate memory\n",
 				__func__);
 		return -ENOMEM;
 	}
-	dev_set_drvdata(&pdev->dev, vreg_priv);
+
+	if (dev->of_node) {
+		/* Use device tree. */
+		init_data = of_get_regulator_init_data(dev,
+						       dev->of_node);
+		if (!init_data) {
+			dev_err(dev, "%s: unable to allocate memory\n",
+					__func__);
+			rc = -ENOMEM;
+			goto err_probe;
+		}
+
+		if (init_data->constraints.name == NULL) {
+			dev_err(dev, "%s: regulator name not specified\n",
+				__func__);
+			rc = -EINVAL;
+			goto err_probe;
+		}
+
+		if (of_get_property(dev->of_node, "parent-supply", NULL))
+			init_data->supply_regulator = "parent";
+
+		of_property_read_u32(dev->of_node, "qcom,system-load",
+					&vreg_priv->system_uA);
+		of_property_read_u32(dev->of_node, "qcom,hpm-min-load",
+					&vreg_priv->hpm_min_load);
+
+		init_data->constraints.input_uV	= init_data->constraints.max_uV;
+
+		init_data->constraints.valid_ops_mask
+			|= REGULATOR_CHANGE_STATUS;
+		init_data->constraints.valid_ops_mask
+			|= REGULATOR_CHANGE_VOLTAGE;
+		init_data->constraints.valid_ops_mask
+			|= REGULATOR_CHANGE_MODE | REGULATOR_CHANGE_DRMS;
+		init_data->constraints.valid_modes_mask
+			= REGULATOR_MODE_NORMAL | REGULATOR_MODE_IDLE;
+	} else {
+		/* Use platform data. */
+		vreg_pdata = dev->platform_data;
+		if (!vreg_pdata) {
+			dev_err(dev, "%s: no platform data\n", __func__);
+			rc = -EINVAL;
+			goto err_probe;
+		}
+		init_data = &vreg_pdata->init_data;
+
+		vreg_priv->system_uA = vreg_pdata->system_uA;
+		vreg_priv->hpm_min_load = vreg_pdata->hpm_min_load;
+	}
+
+	dev_set_drvdata(dev, vreg_priv);
 
 	rdesc = &vreg_priv->rdesc;
-	strncpy(vreg_priv->name, vreg_pdata->init_data.constraints.name,
+	strlcpy(vreg_priv->name, init_data->constraints.name,
 						   STUB_REGULATOR_MAX_NAME);
 	rdesc->name = vreg_priv->name;
 	rdesc->ops = &regulator_stub_ops;
@@ -168,8 +218,8 @@
 	 * which have a specified voltage constraint range, as well as those
 	 * that do not.
 	 */
-	if (vreg_pdata->init_data.constraints.min_uV == 0 &&
-	    vreg_pdata->init_data.constraints.max_uV == 0)
+	if (init_data->constraints.min_uV == 0 &&
+	    init_data->constraints.max_uV == 0)
 		rdesc->n_voltages = 0;
 	else
 		rdesc->n_voltages = 2;
@@ -177,16 +227,20 @@
 	rdesc->id    = pdev->id;
 	rdesc->owner = THIS_MODULE;
 	rdesc->type  = REGULATOR_VOLTAGE;
-	vreg_priv->system_uA = vreg_pdata->system_uA;
-	vreg_priv->hpm_min_load = vreg_pdata->hpm_min_load;
-	vreg_priv->voltage = vreg_pdata->init_data.constraints.min_uV;
+	vreg_priv->voltage = init_data->constraints.min_uV;
+	if (vreg_priv->system_uA >= vreg_priv->hpm_min_load)
+		vreg_priv->mode = REGULATOR_MODE_NORMAL;
+	else
+		vreg_priv->mode = REGULATOR_MODE_IDLE;
 
-	vreg_priv->rdev = regulator_register(rdesc, &pdev->dev,
-			&(vreg_pdata->init_data), vreg_priv, NULL);
+	vreg_priv->rdev = regulator_register(rdesc, dev, init_data, vreg_priv,
+						dev->of_node);
+
 	if (IS_ERR(vreg_priv->rdev)) {
 		rc = PTR_ERR(vreg_priv->rdev);
 		vreg_priv->rdev = NULL;
-		dev_err(&pdev->dev, "%s: regulator_register failed\n",
+		if (rc != -EPROBE_DEFER)
+			dev_err(dev, "%s: regulator_register failed\n",
 				__func__);
 		goto err_probe;
 	}
@@ -206,12 +260,18 @@
 	return 0;
 }
 
+static struct of_device_id regulator_stub_match_table[] = {
+	{ .compatible = "qcom," STUB_REGULATOR_DRIVER_NAME, },
+	{}
+};
+
 static struct platform_driver regulator_stub_driver = {
 	.probe	= regulator_stub_probe,
 	.remove	= __devexit_p(regulator_stub_remove),
 	.driver	= {
 		.name	= STUB_REGULATOR_DRIVER_NAME,
 		.owner	= THIS_MODULE,
+		.of_match_table = regulator_stub_match_table,
 	},
 };
 
diff --git a/drivers/slimbus/Kconfig b/drivers/slimbus/Kconfig
index a6a068d..72bd28a 100644
--- a/drivers/slimbus/Kconfig
+++ b/drivers/slimbus/Kconfig
@@ -16,4 +16,13 @@
 	help
 	  Select driver for Qualcomm's Slimbus Master Component.
 
+config SLIMBUS_MSM_NGD
+	tristate "Qualcomm Slimbus Satellite Component"
+	help
+	  Select driver for Qualcomm's Slimbus Satellite Component.
+	  This is light-weight slimbus controller driver responsible for
+	  communicating with slave HW directly over the bus using messaging
+	  interface, and communicating with master component residing on ADSP
+	  for bandwidth and data-channel management.
+
 endif
diff --git a/drivers/slimbus/Makefile b/drivers/slimbus/Makefile
index 674f057..45d6e6e 100644
--- a/drivers/slimbus/Makefile
+++ b/drivers/slimbus/Makefile
@@ -3,3 +3,4 @@
 #
 obj-$(CONFIG_SLIMBUS)			+= slimbus.o
 obj-$(CONFIG_SLIMBUS_MSM_CTRL)		+= slim-msm.o slim-msm-ctrl.o
+obj-$(CONFIG_SLIMBUS_MSM_NGD)		+= slim-msm.o slim-msm-ngd.o
diff --git a/drivers/slimbus/slim-msm-ctrl.c b/drivers/slimbus/slim-msm-ctrl.c
index e2f37cc..7efe40d 100644
--- a/drivers/slimbus/slim-msm-ctrl.c
+++ b/drivers/slimbus/slim-msm-ctrl.c
@@ -111,15 +111,16 @@
 static int msm_sat_enqueue(struct msm_slim_sat *sat, u32 *buf, u8 len)
 {
 	struct msm_slim_ctrl *dev = sat->dev;
-	spin_lock(&sat->lock);
+	unsigned long flags;
+	spin_lock_irqsave(&sat->lock, flags);
 	if ((sat->stail + 1) % SAT_CONCUR_MSG == sat->shead) {
-		spin_unlock(&sat->lock);
+		spin_unlock_irqrestore(&sat->lock, flags);
 		dev_err(dev->dev, "SAT QUEUE full!");
 		return -EXFULL;
 	}
 	memcpy(sat->sat_msgs[sat->stail], (u8 *)buf, len);
 	sat->stail = (sat->stail + 1) % SAT_CONCUR_MSG;
-	spin_unlock(&sat->lock);
+	spin_unlock_irqrestore(&sat->lock, flags);
 	return 0;
 }
 
@@ -1257,7 +1258,7 @@
 	else
 		clk_prepare_enable(dev->hclk);
 
-	ret = msm_slim_sps_init(dev, bam_mem, MGR_STATUS);
+	ret = msm_slim_sps_init(dev, bam_mem, MGR_STATUS, false);
 	if (ret != 0) {
 		dev_err(dev->dev, "error SPS init\n");
 		goto err_sps_init_failed;
diff --git a/drivers/slimbus/slim-msm-ngd.c b/drivers/slimbus/slim-msm-ngd.c
new file mode 100644
index 0000000..1f2a95e
--- /dev/null
+++ b/drivers/slimbus/slim-msm-ngd.c
@@ -0,0 +1,962 @@
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/irq.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/slimbus/slimbus.h>
+#include <linux/delay.h>
+#include <linux/kthread.h>
+#include <linux/clk.h>
+#include <linux/pm_runtime.h>
+#include <linux/of.h>
+#include <linux/of_slimbus.h>
+#include <linux/timer.h>
+#include <mach/sps.h>
+#include "slim-msm.h"
+#include <mach/qdsp6v2/apr.h>
+
+#define NGD_SLIM_NAME	"ngd_msm_ctrl"
+#define SLIM_LA_MGR	0xFF
+#define SLIM_ROOT_FREQ	24576000
+
+#define NGD_BASE_V1(r)	(((r) % 2) ? 0x800 : 0xA00)
+#define NGD_BASE_V2(r)	(((r) % 2) ? 0x1000 : 0x2000)
+#define NGD_BASE(r, v) ((v) ? NGD_BASE_V2(r) : NGD_BASE_V1(r))
+/* NGD (Non-ported Generic Device) registers */
+enum ngd_reg {
+	NGD_CFG		= 0x0,
+	NGD_STATUS	= 0x4,
+	NGD_RX_MSGQ_CFG	= 0x8,
+	NGD_INT_EN	= 0x10,
+	NGD_INT_STAT	= 0x14,
+	NGD_INT_CLR	= 0x18,
+	NGD_TX_MSG	= 0x30,
+	NGD_RX_MSG	= 0x70,
+	NGD_IE_STAT	= 0xF0,
+	NGD_VE_STAT	= 0x100,
+};
+
+enum ngd_msg_cfg {
+	NGD_CFG_ENABLE		= 1,
+	NGD_CFG_RX_MSGQ_EN	= 1 << 1,
+	NGD_CFG_TX_MSGQ_EN	= 1 << 2,
+};
+
+enum ngd_intr {
+	NGD_INT_RECFG_DONE	= 1 << 24,
+	NGD_INT_TX_NACKED_2	= 1 << 25,
+	NGD_INT_MSG_BUF_CONTE	= 1 << 26,
+	NGD_INT_MSG_TX_INVAL	= 1 << 27,
+	NGD_INT_IE_VE_CHG	= 1 << 28,
+	NGD_INT_DEV_ERR		= 1 << 29,
+	NGD_INT_RX_MSG_RCVD	= 1 << 30,
+	NGD_INT_TX_MSG_SENT	= 1 << 31,
+};
+
+enum ngd_offsets {
+	NGD_NACKED_MC		= 0x7F00000,
+	NGD_ACKED_MC		= 0xFE000,
+	NGD_ERROR		= 0x1800,
+	NGD_MSGQ_SUPPORT	= 0x400,
+	NGD_RX_MSGQ_TIME_OUT	= 0x16,
+	NGD_ENUMERATED		= 0x1,
+	NGD_TX_BUSY		= 0x0,
+};
+
+static irqreturn_t ngd_slim_interrupt(int irq, void *d)
+{
+	struct msm_slim_ctrl *dev = (struct msm_slim_ctrl *)d;
+	void __iomem *ngd = dev->base + NGD_BASE(dev->ctrl.nr, dev->ver);
+	u32 stat = readl_relaxed(ngd + NGD_INT_STAT);
+
+	if (stat & NGD_INT_TX_MSG_SENT) {
+		writel_relaxed(NGD_INT_TX_MSG_SENT, ngd + NGD_INT_CLR);
+		/* Make sure interrupt is cleared */
+		mb();
+		if (dev->wr_comp)
+			complete(dev->wr_comp);
+	} else if ((stat & NGD_INT_MSG_BUF_CONTE) ||
+		(stat & NGD_INT_MSG_TX_INVAL) || (stat & NGD_INT_DEV_ERR) ||
+		(stat & NGD_INT_TX_NACKED_2)) {
+		dev_err(dev->dev, "NGD interrupt error:0x%x", stat);
+		writel_relaxed(stat, ngd + NGD_INT_CLR);
+		/* Guarantee that error interrupts are cleared */
+		mb();
+		if (((stat & NGD_INT_TX_NACKED_2) ||
+			(stat & NGD_INT_MSG_TX_INVAL))) {
+			dev->err = -EIO;
+		if (dev->wr_comp)
+			complete(dev->wr_comp);
+		}
+	}
+	if (stat & NGD_INT_RX_MSG_RCVD) {
+		u32 rx_buf[10];
+		u8 len, i;
+		rx_buf[0] = readl_relaxed(ngd + NGD_RX_MSG);
+		len = rx_buf[0] & 0x1F;
+		for (i = 1; i < ((len + 3) >> 2); i++) {
+			rx_buf[i] = readl_relaxed(ngd + NGD_RX_MSG +
+						(4 * i));
+			dev_dbg(dev->dev, "REG-RX data: %x\n", rx_buf[i]);
+		}
+		msm_slim_rx_enqueue(dev, rx_buf, len);
+		writel_relaxed(NGD_INT_RX_MSG_RCVD,
+				ngd + NGD_INT_CLR);
+		/*
+		 * Guarantee that CLR bit write goes through before
+		 * queuing work
+		 */
+		mb();
+		if (dev->use_rx_msgqs)
+			dev_err(dev->dev,
+				"direct message received even with RX MSGQs");
+		else
+			complete(&dev->rx_msgq_notify);
+	}
+	if (stat & NGD_INT_RECFG_DONE) {
+		writel_relaxed(NGD_INT_RECFG_DONE, ngd + NGD_INT_CLR);
+		/* Guarantee RECONFIG DONE interrupt is cleared */
+		mb();
+		/* In satellite mode, just log the reconfig done IRQ */
+		dev_dbg(dev->dev, "reconfig done IRQ for NGD");
+	}
+	if (stat & NGD_INT_IE_VE_CHG) {
+		writel_relaxed(NGD_INT_IE_VE_CHG, ngd + NGD_INT_CLR);
+		/* Guarantee IE VE change interrupt is cleared */
+		mb();
+		dev_err(dev->dev, "NGD IE VE change");
+	}
+	return IRQ_HANDLED;
+}
+
+static int ngd_get_tid(struct slim_controller *ctrl, struct slim_msg_txn *txn,
+				u8 *tid, struct completion *done)
+{
+	struct msm_slim_ctrl *dev = slim_get_ctrldata(ctrl);
+	if (ctrl->last_tid <= 255) {
+		ctrl->txnt = krealloc(ctrl->txnt,
+				(ctrl->last_tid + 1) *
+				sizeof(struct slim_msg_txn *),
+				GFP_KERNEL);
+		if (!ctrl->txnt)
+			return -ENOMEM;
+		dev->msg_cnt = ctrl->last_tid;
+		ctrl->last_tid++;
+	} else {
+		int i;
+		for (i = 0; i < 256; i++) {
+			dev->msg_cnt = ((dev->msg_cnt + 1) & 0xFF);
+			if (ctrl->txnt[dev->msg_cnt] == NULL)
+				break;
+		}
+		if (i >= 256) {
+			dev_err(&ctrl->dev, "out of TID");
+			return -ENOMEM;
+		}
+	}
+	ctrl->txnt[dev->msg_cnt] = txn;
+	txn->tid = dev->msg_cnt;
+	txn->comp = done;
+	*tid = dev->msg_cnt;
+	return 0;
+}
+static int ngd_xfer_msg(struct slim_controller *ctrl, struct slim_msg_txn *txn)
+{
+	DECLARE_COMPLETION_ONSTACK(done);
+	DECLARE_COMPLETION_ONSTACK(tx_sent);
+
+	struct msm_slim_ctrl *dev = slim_get_ctrldata(ctrl);
+	u32 *pbuf;
+	u8 *puc;
+	int ret = 0;
+	int msgv = -1;
+	u8 la = txn->la;
+	u8 wbuf[SLIM_RX_MSGQ_BUF_LEN];
+
+	if (txn->mt == SLIM_MSG_MT_CORE &&
+		(txn->mc >= SLIM_MSG_MC_BEGIN_RECONFIGURATION &&
+		 txn->mc <= SLIM_MSG_MC_RECONFIGURE_NOW)) {
+		return 0;
+	}
+	msgv = msm_slim_get_ctrl(dev);
+	mutex_lock(&dev->tx_lock);
+	if (txn->mc != SLIM_USR_MC_REPORT_SATELLITE &&
+		(dev->state == MSM_CTRL_ASLEEP ||
+		dev->state == MSM_CTRL_SLEEPING)) {
+		int timeout;
+		dev_err(dev->dev, "controller not ready");
+		mutex_unlock(&dev->tx_lock);
+		/* Reconf is signalled when master responds */
+		timeout = wait_for_completion_timeout(&dev->reconf, HZ);
+		if (timeout) {
+			mutex_lock(&dev->tx_lock);
+		} else {
+			if (msgv >= 0)
+				msm_slim_put_ctrl(dev);
+			return -EBUSY;
+		}
+	}
+	if (txn->mt == SLIM_MSG_MT_CORE &&
+		(txn->mc == SLIM_MSG_MC_CONNECT_SOURCE ||
+		txn->mc == SLIM_MSG_MC_CONNECT_SINK ||
+		txn->mc == SLIM_MSG_MC_DISCONNECT_PORT)) {
+		int i = 0;
+		txn->mt = SLIM_MSG_MT_DEST_REFERRED_USER;
+		if (txn->mc == SLIM_MSG_MC_CONNECT_SOURCE)
+			txn->mc = SLIM_USR_MC_CONNECT_SRC;
+		else if (txn->mc == SLIM_MSG_MC_CONNECT_SINK)
+			txn->mc = SLIM_USR_MC_CONNECT_SINK;
+		else if (txn->mc == SLIM_MSG_MC_DISCONNECT_PORT)
+			txn->mc = SLIM_USR_MC_DISCONNECT_PORT;
+		if (txn->la == SLIM_LA_MGR)
+			txn->la = dev->pgdla;
+		wbuf[i++] = txn->la;
+		la = SLIM_LA_MGR;
+		wbuf[i++] = txn->wbuf[0];
+		if (txn->mc != SLIM_USR_MC_DISCONNECT_PORT)
+			wbuf[i++] = txn->wbuf[1];
+		ret = ngd_get_tid(ctrl, txn, &wbuf[i++], &done);
+		if (ret) {
+			pr_err("TID for connect/disconnect fail:%d", ret);
+			goto ngd_xfer_err;
+		}
+		txn->len = i;
+		txn->wbuf = wbuf;
+		txn->rl = txn->len + 4;
+	}
+	txn->rl--;
+	pbuf = msm_get_msg_buf(dev, txn->rl);
+	if (!pbuf) {
+		dev_err(dev->dev, "Message buffer unavailable");
+		ret = -ENOMEM;
+		goto ngd_xfer_err;
+	}
+	dev->err = 0;
+
+	if (txn->dt == SLIM_MSG_DEST_ENUMADDR) {
+		ret = -EPROTONOSUPPORT;
+		goto ngd_xfer_err;
+	}
+	if (txn->dt == SLIM_MSG_DEST_LOGICALADDR)
+		*pbuf = SLIM_MSG_ASM_FIRST_WORD(txn->rl, txn->mt, txn->mc, 0,
+				la);
+	else
+		*pbuf = SLIM_MSG_ASM_FIRST_WORD(txn->rl, txn->mt, txn->mc, 1,
+				la);
+	if (txn->dt == SLIM_MSG_DEST_LOGICALADDR)
+		puc = ((u8 *)pbuf) + 3;
+	else
+		puc = ((u8 *)pbuf) + 2;
+	if (txn->rbuf)
+		*(puc++) = txn->tid;
+	if ((txn->mt == SLIM_MSG_MT_CORE) &&
+		((txn->mc >= SLIM_MSG_MC_REQUEST_INFORMATION &&
+		txn->mc <= SLIM_MSG_MC_REPORT_INFORMATION) ||
+		(txn->mc >= SLIM_MSG_MC_REQUEST_VALUE &&
+		 txn->mc <= SLIM_MSG_MC_CHANGE_VALUE))) {
+		*(puc++) = (txn->ec & 0xFF);
+		*(puc++) = (txn->ec >> 8)&0xFF;
+	}
+	if (txn->wbuf)
+		memcpy(puc, txn->wbuf, txn->len);
+	if (txn->mt == SLIM_MSG_MT_DEST_REFERRED_USER &&
+		(txn->mc == SLIM_USR_MC_CONNECT_SRC ||
+		 txn->mc == SLIM_USR_MC_CONNECT_SINK ||
+		 txn->mc == SLIM_USR_MC_DISCONNECT_PORT) && txn->wbuf &&
+		wbuf[0] == dev->pgdla) {
+		if (txn->mc != SLIM_MSG_MC_DISCONNECT_PORT)
+			dev->err = msm_slim_connect_pipe_port(dev, wbuf[1]);
+		else {
+			struct msm_slim_endp *endpoint = &dev->pipes[wbuf[1]];
+			struct sps_register_event sps_event;
+			memset(&sps_event, 0, sizeof(sps_event));
+			sps_register_event(endpoint->sps, &sps_event);
+			sps_disconnect(endpoint->sps);
+			/*
+			 * Remove channel disconnects master-side ports from
+			 * channel. No need to send that again on the bus
+			 */
+			dev->pipes[wbuf[1]].connected = false;
+			mutex_unlock(&dev->tx_lock);
+			if (msgv >= 0)
+				msm_slim_put_ctrl(dev);
+			return 0;
+		}
+		if (dev->err) {
+			dev_err(dev->dev, "pipe-port connect err:%d", dev->err);
+			goto ngd_xfer_err;
+		}
+	}
+	dev->err = 0;
+	dev->wr_comp = &tx_sent;
+	ret = msm_send_msg_buf(dev, pbuf, txn->rl,
+			NGD_BASE(dev->ctrl.nr, dev->ver) + NGD_TX_MSG);
+	if (!ret) {
+		int timeout = wait_for_completion_timeout(&tx_sent, HZ);
+		if (!timeout)
+			ret = -ETIMEDOUT;
+		else
+			ret = dev->err;
+	}
+	dev->wr_comp = NULL;
+	if (ret) {
+		u32 conf, stat, rx_msgq, int_stat, int_en, int_clr;
+		void __iomem *ngd = dev->base + NGD_BASE(dev->ctrl.nr,
+							dev->ver);
+		dev_err(dev->dev, "TX failed :MC:0x%x,mt:0x%x, ret:%d, ver:%d",
+				txn->mc, txn->mt, ret, dev->ver);
+		conf = readl_relaxed(ngd);
+		stat = readl_relaxed(ngd + NGD_STATUS);
+		rx_msgq = readl_relaxed(ngd + NGD_RX_MSGQ_CFG);
+		int_stat = readl_relaxed(ngd + NGD_INT_STAT);
+		int_en = readl_relaxed(ngd + NGD_INT_EN);
+		int_clr = readl_relaxed(ngd + NGD_INT_CLR);
+
+		pr_err("conf:0x%x,stat:0x%x,rxmsgq:0x%x", conf, stat, rx_msgq);
+		pr_err("int_stat:0x%x,int_en:0x%x,int_cll:0x%x", int_stat,
+						int_en, int_clr);
+	} else if (txn->mt == SLIM_MSG_MT_DEST_REFERRED_USER &&
+		(txn->mc == SLIM_USR_MC_CONNECT_SRC ||
+		 txn->mc == SLIM_USR_MC_CONNECT_SINK ||
+		 txn->mc == SLIM_USR_MC_DISCONNECT_PORT)) {
+		int timeout;
+		mutex_unlock(&dev->tx_lock);
+		if (msgv >= 0)
+			msm_slim_put_ctrl(dev);
+		timeout = wait_for_completion_timeout(txn->comp, HZ);
+		if (!timeout) {
+			pr_err("connect/disc :0x%x, tid:%d timed out", txn->mc,
+					txn->tid);
+			ret = -ETIMEDOUT;
+		} else {
+			ret = txn->ec;
+		}
+		if (ret)
+			pr_err("connect/disconnect:0x%x,tid:%d err:%d", txn->mc,
+					txn->tid, ret);
+		return ret ? ret : dev->err;
+	}
+ngd_xfer_err:
+	mutex_unlock(&dev->tx_lock);
+	if (msgv >= 0)
+		msm_slim_put_ctrl(dev);
+	return ret ? ret : dev->err;
+}
+
+static int ngd_xferandwait_ack(struct slim_controller *ctrl,
+				struct slim_msg_txn *txn)
+{
+	int ret = ngd_xfer_msg(ctrl, txn);
+	if (!ret) {
+		int timeout;
+		timeout = wait_for_completion_timeout(txn->comp, HZ);
+		if (!timeout) {
+			pr_err("master req:0x%x, tid:%d timed out", txn->mc,
+					txn->tid);
+			ret = -ETIMEDOUT;
+		} else {
+			ret = txn->ec;
+		}
+	}
+	if (ret)
+		pr_err("master msg:0x%x,tid:%d ret:%d", txn->mc,
+				txn->tid, ret);
+
+	return ret;
+}
+
+static int ngd_allocbw(struct slim_device *sb, int *subfrmc, int *clkgear)
+{
+	int ret;
+	struct slim_pending_ch *pch;
+	struct slim_msg_txn txn;
+	struct slim_controller *ctrl = sb->ctrl;
+	DECLARE_COMPLETION_ONSTACK(done);
+	u8 wbuf[SLIM_RX_MSGQ_BUF_LEN];
+
+	txn.mt = SLIM_MSG_MT_DEST_REFERRED_USER;
+	txn.dt = SLIM_MSG_DEST_LOGICALADDR;
+	txn.la = SLIM_LA_MGR;
+	txn.len = 0;
+	txn.ec = 0;
+	txn.wbuf = wbuf;
+	txn.rbuf = NULL;
+
+	list_for_each_entry(pch, &sb->mark_define, pending) {
+		struct slim_ich *slc;
+		slc = &ctrl->chans[pch->chan];
+		if (!slc) {
+			pr_err("no channel in define?");
+			return -ENXIO;
+		}
+		if (txn.len == 0) {
+			wbuf[txn.len++] = (u8) (slc->prop.dataf << 5) |
+					sb->laddr;
+			wbuf[txn.len] = slc->seglen;
+			if (slc->coeff == SLIM_COEFF_3)
+				wbuf[txn.len] |= 1 << 5;
+			wbuf[txn.len++] |= slc->prop.auxf << 6;
+			wbuf[txn.len++] = slc->rootexp << 4 | slc->prop.prot;
+			wbuf[txn.len++] = slc->prrate;
+			ret = ngd_get_tid(ctrl, &txn, &wbuf[txn.len++], &done);
+			if (ret) {
+				pr_err("no tid for channel define?");
+				return -ENXIO;
+			}
+		}
+		wbuf[txn.len++] = slc->chan;
+	}
+	if (txn.len) {
+		txn.mc = SLIM_USR_MC_DEF_ACT_CHAN;
+		txn.rl = txn.len + 4;
+		ret = ngd_xferandwait_ack(ctrl, &txn);
+		if (ret)
+			return ret;
+
+		txn.mc = SLIM_USR_MC_RECONFIG_NOW;
+		txn.len = 2;
+		wbuf[1] = sb->laddr;
+		txn.rl = txn.len + 4;
+		ret = ngd_get_tid(ctrl, &txn, &wbuf[0], &done);
+		if (ret)
+			return ret;
+		ret = ngd_xferandwait_ack(ctrl, &txn);
+		if (ret)
+			return ret;
+	}
+	txn.len = 0;
+	list_for_each_entry(pch, &sb->mark_removal, pending) {
+		struct slim_ich *slc;
+		slc = &ctrl->chans[pch->chan];
+		if (!slc) {
+			pr_err("no channel in removal?");
+			return -ENXIO;
+		}
+		if (txn.len == 0) {
+			wbuf[txn.len++] = (u8) (SLIM_CH_REMOVE << 6) |
+					sb->laddr;
+			ret = ngd_get_tid(ctrl, &txn, &wbuf[txn.len++], &done);
+			if (ret) {
+				pr_err("no tid for channel define?");
+				return -ENXIO;
+			}
+		}
+		wbuf[txn.len++] = slc->chan;
+	}
+	if (txn.len) {
+		txn.mc = SLIM_USR_MC_CHAN_CTRL;
+		txn.rl = txn.len + 4;
+		ret = ngd_xferandwait_ack(ctrl, &txn);
+		if (ret)
+			return ret;
+
+		txn.mc = SLIM_USR_MC_RECONFIG_NOW;
+		txn.len = 2;
+		wbuf[1] = sb->laddr;
+		txn.rl = txn.len + 4;
+		ret = ngd_get_tid(ctrl, &txn, &wbuf[0], &done);
+		if (ret)
+			return ret;
+		ret = ngd_xferandwait_ack(ctrl, &txn);
+		if (ret)
+			return ret;
+		txn.len = 0;
+	}
+	return ret;
+}
+
+static int ngd_set_laddr(struct slim_controller *ctrl, const u8 *ea,
+				u8 elen, u8 laddr)
+{
+	return 0;
+}
+
+static int ngd_get_laddr(struct slim_controller *ctrl, const u8 *ea,
+				u8 elen, u8 *laddr)
+{
+	int ret;
+	u8 wbuf[10];
+	struct slim_msg_txn txn;
+	DECLARE_COMPLETION_ONSTACK(done);
+	txn.mt = SLIM_MSG_MT_DEST_REFERRED_USER;
+	txn.dt = SLIM_MSG_DEST_LOGICALADDR;
+	txn.la = SLIM_LA_MGR;
+	txn.ec = 0;
+	mutex_lock(&ctrl->m_ctrl);
+	ret = ngd_get_tid(ctrl, &txn, &wbuf[0], &done);
+	if (ret) {
+		mutex_unlock(&ctrl->m_ctrl);
+		return ret;
+	}
+	memcpy(&wbuf[1], ea, elen);
+	txn.mc = SLIM_USR_MC_ADDR_QUERY;
+	txn.rl = 11;
+	txn.len = 7;
+	txn.wbuf = wbuf;
+	txn.rbuf = NULL;
+	ret = ngd_xferandwait_ack(ctrl, &txn);
+	if (!ret && txn.la == 0xFF)
+		ret = -ENXIO;
+	else if (!ret)
+		*laddr = txn.la;
+	mutex_unlock(&ctrl->m_ctrl);
+	return ret;
+}
+
+static void ngd_slim_rx(struct msm_slim_ctrl *dev, u8 *buf)
+{
+	u8 mc, mt, len;
+	int ret;
+	u32 msgq_en = 1;
+
+	len = buf[0] & 0x1F;
+	mt = (buf[0] >> 5) & 0x7;
+	mc = buf[1];
+	if (mc == SLIM_USR_MC_MASTER_CAPABILITY &&
+		mt == SLIM_MSG_MT_SRC_REFERRED_USER) {
+		struct slim_msg_txn txn;
+		u8 wbuf[8];
+		txn.dt = SLIM_MSG_DEST_LOGICALADDR;
+		txn.ec = 0;
+		txn.rbuf = NULL;
+		txn.mc = SLIM_USR_MC_REPORT_SATELLITE;
+		txn.mt = SLIM_MSG_MT_SRC_REFERRED_USER;
+		txn.la = SLIM_LA_MGR;
+		txn.rl = 8;
+		wbuf[0] = SAT_MAGIC_LSB;
+		wbuf[1] = SAT_MAGIC_MSB;
+		wbuf[2] = SAT_MSG_VER;
+		wbuf[3] = SAT_MSG_PROT;
+		txn.wbuf = wbuf;
+		txn.len = 4;
+		dev->use_rx_msgqs = 1;
+		msm_slim_sps_init(dev, dev->bam_mem,
+			NGD_BASE(dev->ctrl.nr, dev->ver) + NGD_STATUS, true);
+		if (dev->use_rx_msgqs)
+			msgq_en |= NGD_CFG_RX_MSGQ_EN;
+		writel_relaxed(msgq_en, dev->base +
+				NGD_BASE(dev->ctrl.nr, dev->ver));
+		/* make sure NGD MSG-Q config goes through */
+		mb();
+
+		ret = ngd_xfer_msg(&dev->ctrl, &txn);
+		if (!ret) {
+			dev->state = MSM_CTRL_AWAKE;
+			complete(&dev->reconf);
+		}
+	}
+	if (mc == SLIM_MSG_MC_REPLY_INFORMATION ||
+			mc == SLIM_MSG_MC_REPLY_VALUE) {
+		u8 tid = buf[3];
+		dev_dbg(dev->dev, "tid:%d, len:%d\n", tid, len);
+		slim_msg_response(&dev->ctrl, &buf[4], tid,
+					len - 4);
+		pm_runtime_mark_last_busy(dev->dev);
+	}
+	if (mc == SLIM_USR_MC_ADDR_REPLY &&
+		mt == SLIM_MSG_MT_SRC_REFERRED_USER) {
+		struct slim_msg_txn *txn = dev->ctrl.txnt[buf[3]];
+		u8 failed_ea[6] = {0, 0, 0, 0, 0, 0};
+		if (!txn)
+			return;
+		if (memcmp(&buf[4], failed_ea, 6))
+			txn->la = buf[10];
+		dev->ctrl.txnt[buf[3]] = NULL;
+		complete(txn->comp);
+	}
+	if (mc == SLIM_USR_MC_GENERIC_ACK &&
+		mt == SLIM_MSG_MT_SRC_REFERRED_USER) {
+		struct slim_msg_txn *txn = dev->ctrl.txnt[buf[3]];
+		if (!txn)
+			return;
+		dev_dbg(dev->dev, "got response:tid:%d, response:0x%x",
+				(int)buf[3], buf[4]);
+		if (!(buf[4] & MSM_SAT_SUCCSS)) {
+			dev_err(dev->dev, "TID:%d, NACK code:0x%x", (int)buf[3],
+						buf[4]);
+			txn->ec = -EIO;
+		}
+		dev->ctrl.txnt[buf[3]] = NULL;
+		complete(txn->comp);
+	}
+}
+static int ngd_slim_rx_msgq_thread(void *data)
+{
+	struct msm_slim_ctrl *dev = (struct msm_slim_ctrl *)data;
+	struct completion *notify = &dev->rx_msgq_notify;
+	int ret = 0, index = 0;
+	u32 mc = 0;
+	u32 mt = 0;
+	u32 buffer[10];
+	u8 msg_len = 0;
+
+	while (!kthread_should_stop()) {
+		set_current_state(TASK_INTERRUPTIBLE);
+		ret = wait_for_completion_interruptible(notify);
+		if (ret) {
+			dev_err(dev->dev, "rx thread wait err:%d", ret);
+			continue;
+		}
+		/* 1 irq notification per message */
+		if (!dev->use_rx_msgqs) {
+			msm_slim_rx_dequeue(dev, (u8 *)buffer);
+			ngd_slim_rx(dev, (u8 *)buffer);
+			continue;
+		}
+		ret = msm_slim_rx_msgq_get(dev, buffer, index);
+		if (ret) {
+			dev_err(dev->dev, "rx_msgq_get() failed 0x%x\n", ret);
+			continue;
+		}
+
+		/* Wait for complete message */
+		if (index++ == 0) {
+			msg_len = *buffer & 0x1F;
+			mt = (buffer[0] >> 5) & 0x7;
+			mc = (buffer[0] >> 8) & 0xff;
+			dev_dbg(dev->dev, "MC: %x, MT: %x\n", mc, mt);
+		}
+		if ((index * 4) >= msg_len) {
+			index = 0;
+			ngd_slim_rx(dev, (u8 *)buffer);
+		} else
+			continue;
+	}
+	return 0;
+}
+
+static int __devinit ngd_slim_probe(struct platform_device *pdev)
+{
+	struct msm_slim_ctrl *dev;
+	int ret;
+	struct resource		*bam_mem;
+	struct resource		*slim_mem;
+	struct resource		*irq, *bam_irq;
+	enum apr_subsys_state q6_state;
+	u32 ngd_int;
+
+	q6_state = apr_get_q6_state();
+	if (q6_state == APR_SUBSYS_DOWN) {
+		dev_dbg(&pdev->dev, "defering %s, adsp_state %d\n", __func__,
+			q6_state);
+		return -EPROBE_DEFER;
+	} else
+		dev_dbg(&pdev->dev, "adsp is ready\n");
+
+	slim_mem = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+						"slimbus_physical");
+	if (!slim_mem) {
+		dev_err(&pdev->dev, "no slimbus physical memory resource\n");
+		return -ENODEV;
+	}
+	bam_mem = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+						"slimbus_bam_physical");
+	if (!bam_mem) {
+		dev_err(&pdev->dev, "no slimbus BAM memory resource\n");
+		return -ENODEV;
+	}
+	irq = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
+						"slimbus_irq");
+	if (!irq) {
+		dev_err(&pdev->dev, "no slimbus IRQ resource\n");
+		return -ENODEV;
+	}
+	bam_irq = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
+						"slimbus_bam_irq");
+	if (!bam_irq) {
+		dev_err(&pdev->dev, "no slimbus BAM IRQ resource\n");
+		return -ENODEV;
+	}
+
+	dev = kzalloc(sizeof(struct msm_slim_ctrl), GFP_KERNEL);
+	if (IS_ERR(dev)) {
+		dev_err(&pdev->dev, "no memory for MSM slimbus controller\n");
+		return PTR_ERR(dev);
+	}
+	dev->dev = &pdev->dev;
+	platform_set_drvdata(pdev, dev);
+	slim_set_ctrldata(&dev->ctrl, dev);
+	dev->base = ioremap(slim_mem->start, resource_size(slim_mem));
+	if (!dev->base) {
+		dev_err(&pdev->dev, "IOremap failed\n");
+		ret = -ENOMEM;
+		goto err_ioremap_failed;
+	}
+	dev->bam.base = ioremap(bam_mem->start, resource_size(bam_mem));
+	if (!dev->bam.base) {
+		dev_err(&pdev->dev, "BAM IOremap failed\n");
+		ret = -ENOMEM;
+		goto err_ioremap_bam_failed;
+	}
+	if (pdev->dev.of_node) {
+
+		ret = of_property_read_u32(pdev->dev.of_node, "cell-index",
+					&dev->ctrl.nr);
+		if (ret) {
+			dev_err(&pdev->dev, "Cell index not specified:%d", ret);
+			goto err_ctrl_failed;
+		}
+	} else {
+		dev->ctrl.nr = pdev->id;
+	}
+	dev->ctrl.nchans = MSM_SLIM_NCHANS;
+	dev->ctrl.nports = MSM_SLIM_NPORTS;
+	dev->framer.rootfreq = SLIM_ROOT_FREQ >> 3;
+	dev->framer.superfreq =
+		dev->framer.rootfreq / SLIM_CL_PER_SUPERFRAME_DIV8;
+	dev->ctrl.a_framer = &dev->framer;
+	dev->ctrl.clkgear = SLIM_MAX_CLK_GEAR;
+	dev->ctrl.set_laddr = ngd_set_laddr;
+	dev->ctrl.get_laddr = ngd_get_laddr;
+	dev->ctrl.allocbw = ngd_allocbw;
+	dev->ctrl.xfer_msg = ngd_xfer_msg;
+	dev->ctrl.wakeup =  NULL;
+	dev->ctrl.config_port = msm_config_port;
+	dev->ctrl.port_xfer = msm_slim_port_xfer;
+	dev->ctrl.port_xfer_status = msm_slim_port_xfer_status;
+	/* Reserve some messaging BW for satellite-apps driver communication */
+	dev->ctrl.sched.pending_msgsl = 30;
+	dev->bam_mem = bam_mem;
+
+	init_completion(&dev->reconf);
+	mutex_init(&dev->tx_lock);
+	spin_lock_init(&dev->rx_lock);
+	dev->ee = 1;
+	dev->irq = irq->start;
+	dev->bam.irq = bam_irq->start;
+
+	dev->ver = readl_relaxed(dev->base);
+	/* Version info in 16 MSbits */
+	dev->ver >>= 16;
+	ngd_int = (NGD_INT_RECFG_DONE | NGD_INT_TX_NACKED_2 |
+			NGD_INT_MSG_BUF_CONTE | NGD_INT_MSG_TX_INVAL |
+			NGD_INT_IE_VE_CHG | NGD_INT_DEV_ERR |
+			NGD_INT_TX_MSG_SENT | NGD_INT_RX_MSG_RCVD);
+	init_completion(&dev->rx_msgq_notify);
+
+	/* Register with framework */
+	ret = slim_add_numbered_controller(&dev->ctrl);
+	if (ret) {
+		dev_err(dev->dev, "error adding controller\n");
+		goto err_ctrl_failed;
+	}
+
+	dev->ctrl.dev.parent = &pdev->dev;
+	dev->ctrl.dev.of_node = pdev->dev.of_node;
+	dev->state = MSM_CTRL_ASLEEP;
+
+	ret = request_irq(dev->irq, ngd_slim_interrupt,
+			IRQF_TRIGGER_HIGH, "ngd_slim_irq", dev);
+
+	if (ret) {
+		dev_err(&pdev->dev, "request IRQ failed\n");
+		goto err_request_irq_failed;
+	}
+
+	/* Fire up the Rx message queue thread */
+	dev->rx_msgq_thread = kthread_run(ngd_slim_rx_msgq_thread, dev,
+					NGD_SLIM_NAME "_ngd_msgq_thread");
+	if (IS_ERR(dev->rx_msgq_thread)) {
+		ret = PTR_ERR(dev->rx_msgq_thread);
+		dev_err(dev->dev, "Failed to start Rx message queue thread\n");
+		goto err_thread_create_failed;
+	}
+
+	writel_relaxed(ngd_int, dev->base + NGD_INT_EN +
+				NGD_BASE(dev->ctrl.nr, dev->ver));
+	/*
+	 * Enable NGD. Configure NGD in register access mode until master
+	 * announcement is received
+	 */
+	writel_relaxed(1, dev->base + NGD_BASE(dev->ctrl.nr, dev->ver));
+	/* make sure NGD enabling goes through */
+	mb();
+
+	if (pdev->dev.of_node)
+		of_register_slim_devices(&dev->ctrl);
+
+	/* Add devices registered with board-info now that controller is up */
+	slim_ctrl_add_boarddevs(&dev->ctrl);
+
+	pm_runtime_use_autosuspend(&pdev->dev);
+	pm_runtime_set_autosuspend_delay(&pdev->dev, MSM_SLIM_AUTOSUSPEND);
+	pm_runtime_set_active(&pdev->dev);
+
+	dev_dbg(dev->dev, "NGD SB controller is up!\n");
+	return 0;
+
+err_thread_create_failed:
+	free_irq(dev->irq, dev);
+err_request_irq_failed:
+	slim_del_controller(&dev->ctrl);
+err_ctrl_failed:
+	iounmap(dev->bam.base);
+err_ioremap_bam_failed:
+	iounmap(dev->base);
+err_ioremap_failed:
+	kfree(dev);
+	return ret;
+}
+
+static int __devexit ngd_slim_remove(struct platform_device *pdev)
+{
+	struct msm_slim_ctrl *dev = platform_get_drvdata(pdev);
+	pm_runtime_disable(&pdev->dev);
+	pm_runtime_set_suspended(&pdev->dev);
+	free_irq(dev->irq, dev);
+	slim_del_controller(&dev->ctrl);
+	kthread_stop(dev->rx_msgq_thread);
+	iounmap(dev->bam.base);
+	iounmap(dev->base);
+	kfree(dev);
+	return 0;
+}
+
+#ifdef CONFIG_PM_RUNTIME
+static int ngd_slim_runtime_idle(struct device *device)
+{
+	dev_dbg(device, "pm_runtime: idle...\n");
+	pm_request_autosuspend(device);
+	return -EAGAIN;
+}
+#endif
+
+/*
+ * If PM_RUNTIME is not defined, these 2 functions become helper
+ * functions to be called from system suspend/resume. So they are not
+ * inside ifdef CONFIG_PM_RUNTIME
+ */
+#ifdef CONFIG_PM_SLEEP
+static int ngd_slim_runtime_suspend(struct device *device)
+{
+	struct platform_device *pdev = to_platform_device(device);
+	struct msm_slim_ctrl *dev = platform_get_drvdata(pdev);
+	int ret;
+	dev_dbg(device, "pm_runtime: suspending...\n");
+	dev->state = MSM_CTRL_SLEEPING;
+	ret = slim_ctrl_clk_pause(&dev->ctrl, false, SLIM_CLK_UNSPECIFIED);
+	if (ret) {
+		dev_err(device, "clk pause not entered:%d", ret);
+		dev->state = MSM_CTRL_AWAKE;
+	} else {
+		dev->state = MSM_CTRL_ASLEEP;
+	}
+	return ret;
+}
+
+static int ngd_slim_runtime_resume(struct device *device)
+{
+	struct platform_device *pdev = to_platform_device(device);
+	struct msm_slim_ctrl *dev = platform_get_drvdata(pdev);
+	int ret = 0;
+	dev_dbg(device, "pm_runtime: resuming...\n");
+	if (dev->state == MSM_CTRL_ASLEEP)
+		ret = slim_ctrl_clk_pause(&dev->ctrl, true, 0);
+	if (ret) {
+		dev_err(device, "clk pause not exited:%d", ret);
+		dev->state = MSM_CTRL_ASLEEP;
+	} else {
+		dev->state = MSM_CTRL_AWAKE;
+	}
+	return ret;
+}
+
+static int ngd_slim_suspend(struct device *dev)
+{
+	int ret = -EBUSY;
+	if (!pm_runtime_enabled(dev) || !pm_runtime_suspended(dev)) {
+		dev_dbg(dev, "system suspend");
+		ret = ngd_slim_runtime_suspend(dev);
+	}
+	if (ret == -EBUSY) {
+		/*
+		* There is a possibility that some audio stream is active
+		* during suspend. We dont want to return suspend failure in
+		* that case so that display and relevant components can still
+		* go to suspend.
+		* If there is some other error, then it should be passed-on
+		* to system level suspend
+		*/
+		ret = 0;
+	}
+	return ret;
+}
+
+static int ngd_slim_resume(struct device *dev)
+{
+	/* If runtime_pm is enabled, this resume shouldn't do anything */
+	if (!pm_runtime_enabled(dev) || !pm_runtime_suspended(dev)) {
+		int ret;
+		dev_dbg(dev, "system resume");
+		ret = ngd_slim_runtime_resume(dev);
+		if (!ret) {
+			pm_runtime_mark_last_busy(dev);
+			pm_request_autosuspend(dev);
+		}
+		return ret;
+
+	}
+	return 0;
+}
+#endif /* CONFIG_PM_SLEEP */
+
+static const struct dev_pm_ops ngd_slim_dev_pm_ops = {
+	SET_SYSTEM_SLEEP_PM_OPS(
+		ngd_slim_suspend,
+		ngd_slim_resume
+	)
+	SET_RUNTIME_PM_OPS(
+		ngd_slim_runtime_suspend,
+		ngd_slim_runtime_resume,
+		ngd_slim_runtime_idle
+	)
+};
+
+static struct of_device_id ngd_slim_dt_match[] = {
+	{
+		.compatible = "qcom,slim-ngd",
+	},
+	{}
+};
+
+static struct platform_driver ngd_slim_driver = {
+	.probe = ngd_slim_probe,
+	.remove = ngd_slim_remove,
+	.driver	= {
+		.name = NGD_SLIM_NAME,
+		.owner = THIS_MODULE,
+		.pm = &ngd_slim_dev_pm_ops,
+		.of_match_table = ngd_slim_dt_match,
+	},
+};
+
+static int ngd_slim_init(void)
+{
+	return platform_driver_register(&ngd_slim_driver);
+}
+late_initcall(ngd_slim_init);
+
+static void ngd_slim_exit(void)
+{
+	platform_driver_unregister(&ngd_slim_driver);
+}
+module_exit(ngd_slim_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("MSM Slimbus controller");
+MODULE_ALIAS("platform:msm-slim-ngd");
diff --git a/drivers/slimbus/slim-msm.c b/drivers/slimbus/slim-msm.c
index 8a1ea84..7cd34d3 100644
--- a/drivers/slimbus/slim-msm.c
+++ b/drivers/slimbus/slim-msm.c
@@ -494,7 +494,7 @@
 
 /* Registers BAM h/w resource with SPS driver and initializes msgq endpoints */
 int msm_slim_sps_init(struct msm_slim_ctrl *dev, struct resource *bam_mem,
-			u32 pipe_reg)
+			u32 pipe_reg, bool remote)
 {
 	int i, ret;
 	u32 bam_handle;
@@ -521,10 +521,16 @@
 	bam_props.virt_addr = dev->bam.base;
 	bam_props.phys_addr = bam_mem->start;
 	bam_props.irq = dev->bam.irq;
-	bam_props.manage = SPS_BAM_MGR_LOCAL;
+	if (!remote) {
+		bam_props.manage = SPS_BAM_MGR_LOCAL;
+		bam_props.sec_config = SPS_BAM_SEC_DO_CONFIG;
+	} else {
+		bam_props.manage = SPS_BAM_MGR_DEVICE_REMOTE |
+					SPS_BAM_MGR_MULTI_EE;
+		bam_props.sec_config = SPS_BAM_SEC_DO_NOT_CONFIG;
+	}
 	bam_props.summing_threshold = MSM_SLIM_PERF_SUMM_THRESHOLD;
 
-	bam_props.sec_config = SPS_BAM_SEC_DO_CONFIG;
 	bam_props.p_sec_config_props = &sec_props;
 
 	bam_props.options = SPS_O_DESC_DONE | SPS_O_ERROR |
diff --git a/drivers/slimbus/slim-msm.h b/drivers/slimbus/slim-msm.h
index f68475a..7d50620 100644
--- a/drivers/slimbus/slim-msm.h
+++ b/drivers/slimbus/slim-msm.h
@@ -167,6 +167,7 @@
 	struct device		*dev;
 	void __iomem		*base;
 	struct resource		*slew_mem;
+	struct resource		*bam_mem;
 	u32			curr_bw;
 	u8			msg_cnt;
 	u32			tx_buf[10];
@@ -246,6 +247,6 @@
 u32 *msm_get_msg_buf(struct msm_slim_ctrl *dev, int len);
 int msm_slim_rx_msgq_get(struct msm_slim_ctrl *dev, u32 *data, int offset);
 int msm_slim_sps_init(struct msm_slim_ctrl *dev, struct resource *bam_mem,
-			u32 pipe_reg);
+			u32 pipe_reg, bool remote);
 void msm_slim_sps_exit(struct msm_slim_ctrl *dev);
 #endif
diff --git a/drivers/slimbus/slimbus.c b/drivers/slimbus/slimbus.c
index bd25875..1e79dce 100644
--- a/drivers/slimbus/slimbus.c
+++ b/drivers/slimbus/slimbus.c
@@ -2875,6 +2875,9 @@
 	mutex_lock(&sb->sldev_reconf);
 	mutex_lock(&ctrl->m_ctrl);
 	do {
+		struct slim_pending_ch *pch;
+		u8 add_mark_removal  = true;
+
 		slc = &ctrl->chans[chan];
 		dev_dbg(&ctrl->dev, "chan:%d,ctrl:%d,def:%d", chan, chctrl,
 					slc->def);
@@ -2899,9 +2902,30 @@
 				ret = -ENOTCONN;
 				break;
 			}
-			ret = add_pending_ch(&sb->mark_removal, chan);
-			if (ret)
-				break;
+			/* If channel removal request comes when pending
+			 * in the mark_define, remove it from the define
+			 * list instead of adding it to removal list
+			 */
+			if (!list_empty(&sb->mark_define)) {
+				struct list_head *pos, *next;
+				list_for_each_safe(pos, next,
+						  &sb->mark_define) {
+					pch = list_entry(pos,
+						struct slim_pending_ch,
+						pending);
+					if (pch->chan == slc->chan) {
+						list_del(&pch->pending);
+						kfree(pch);
+						add_mark_removal = false;
+						break;
+					}
+				}
+			}
+			if (add_mark_removal == true) {
+				ret = add_pending_ch(&sb->mark_removal, chan);
+				if (ret)
+					break;
+			}
 		}
 
 		if (!(slc->nextgrp & SLIM_END_GRP))
diff --git a/drivers/staging/android/lowmemorykiller.c b/drivers/staging/android/lowmemorykiller.c
index e73caf1..432abe5 100644
--- a/drivers/staging/android/lowmemorykiller.c
+++ b/drivers/staging/android/lowmemorykiller.c
@@ -62,6 +62,81 @@
 			printk(x);			\
 	} while (0)
 
+static int nr_free_zone_mtype_pages(struct zone *zone, int mtype)
+{
+	int order;
+	int sum = 0;
+
+	for (order = 0; order < MAX_ORDER; ++order) {
+		unsigned long freecount = 0;
+		struct free_area *area;
+		struct list_head *curr;
+
+		area = &(zone->free_area[order]);
+
+		list_for_each(curr, &area->free_list[mtype])
+			freecount++;
+
+		sum += freecount << order;
+	}
+	return sum;
+}
+
+static int nr_free_zone_pages(struct zone *zone, gfp_t gfp_mask)
+{
+	int sum = 0;
+	int mtype = allocflags_to_migratetype(gfp_mask);
+	int i = 0;
+	int *mtype_fallbacks = get_migratetype_fallbacks(mtype);
+
+	sum = nr_free_zone_mtype_pages(zone, mtype);
+
+	/*
+	 * Also count the fallback pages
+	 */
+	for (i = 0;; i++) {
+		int fallbacktype = mtype_fallbacks[i];
+		sum += nr_free_zone_mtype_pages(zone, fallbacktype);
+
+		if (fallbacktype == MIGRATE_RESERVE)
+			break;
+	}
+
+	return sum;
+}
+
+static int nr_free_pages(gfp_t gfp_mask)
+{
+	struct zoneref *z;
+	struct zone *zone;
+	int sum = 0;
+
+	struct zonelist *zonelist = node_zonelist(numa_node_id(), gfp_mask);
+
+	for_each_zone_zonelist(zone, z, zonelist, gfp_zone(gfp_mask)) {
+		sum += nr_free_zone_pages(zone, gfp_mask);
+	}
+
+	return sum;
+}
+
+
+static int test_task_flag(struct task_struct *p, int flag)
+{
+	struct task_struct *t = p;
+
+	do {
+		task_lock(t);
+		if (test_tsk_thread_flag(t, flag)) {
+			task_unlock(t);
+			return 1;
+		}
+		task_unlock(t);
+	} while_each_thread(p, t);
+
+	return 0;
+}
+
 static int lowmem_shrink(struct shrinker *s, struct shrink_control *sc)
 {
 	struct task_struct *tsk;
@@ -77,6 +152,15 @@
 	int other_file = global_page_state(NR_FILE_PAGES) -
 						global_page_state(NR_SHMEM);
 
+	if (sc->nr_to_scan > 0 && other_free > other_file) {
+		/*
+		 * If the number of free pages is going to affect the decision
+		 * of which process is selected then ensure only free pages
+		 * which can satisfy the request are considered.
+		 */
+		other_free = nr_free_pages(sc->gfp_mask);
+	}
+
 	if (lowmem_adj_size < array_size)
 		array_size = lowmem_adj_size;
 	if (lowmem_minfree_size < array_size)
@@ -111,16 +195,17 @@
 		if (tsk->flags & PF_KTHREAD)
 			continue;
 
+		if (time_before_eq(jiffies, lowmem_deathpending_timeout)) {
+			if (test_task_flag(tsk, TIF_MEMDIE)) {
+				rcu_read_unlock();
+				return 0;
+			}
+		}
+
 		p = find_lock_task_mm(tsk);
 		if (!p)
 			continue;
 
-		if (test_tsk_thread_flag(p, TIF_MEMDIE) &&
-		    time_before_eq(jiffies, lowmem_deathpending_timeout)) {
-			task_unlock(p);
-			rcu_read_unlock();
-			return 0;
-		}
 		oom_score_adj = p->signal->oom_score_adj;
 		if (oom_score_adj < min_score_adj) {
 			task_unlock(p);
diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
index d082273..e70924c 100644
--- a/drivers/thermal/Kconfig
+++ b/drivers/thermal/Kconfig
@@ -91,3 +91,19 @@
 	help
 	  Enable this to plug the SPEAr thermal sensor driver into the Linux
 	  thermal framework
+
+config THERMAL_QPNP
+	tristate "Qualcomm Plug-and-Play PMIC Temperature Alarm"
+	depends on THERMAL
+	depends on OF
+	depends on SPMI
+	depends on OF_SPMI
+	help
+	  This enables a thermal Sysfs driver for Qualcomm plug-and-play (QPNP)
+	  PMIC devices. It shows up in Sysfs as a thermal zone with multiple
+	  trip points. The temperature reported by the thermal zone reflects the
+	  real time die temperature if an ADC is present or an estimate of the
+	  temperature based upon the over temperature stage value if no ADC is
+	  available. If allowed via compile time configuration; enabling the
+	  thermal zone device via the mode file results in shifting PMIC over
+	  temperature shutdown control from hardware to software.
diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile
index f7e7cc6..3b2b3a8 100644
--- a/drivers/thermal/Makefile
+++ b/drivers/thermal/Makefile
@@ -10,3 +10,4 @@
 obj-$(CONFIG_THERMAL_MONITOR)	+= msm_thermal.o
 obj-$(CONFIG_SPEAR_THERMAL)		+= spear_thermal.o
 obj-$(CONFIG_THERMAL_TSENS8974)	+= msm8974-tsens.o
+obj-$(CONFIG_THERMAL_QPNP)	+= qpnp-temp-alarm.o
diff --git a/drivers/thermal/msm8960_tsens.c b/drivers/thermal/msm8960_tsens.c
index f60e318..a932f6b 100644
--- a/drivers/thermal/msm8960_tsens.c
+++ b/drivers/thermal/msm8960_tsens.c
@@ -64,7 +64,7 @@
 #define TSENS_UPPER_STATUS_CLR		BIT((tsens_status_cntl_start + 2))
 #define TSENS_MAX_STATUS_MASK		BIT((tsens_status_cntl_start + 3))
 
-#define TSENS_MEASURE_PERIOD				4 /* 1 sec. default */
+#define TSENS_MEASURE_PERIOD				1
 #define TSENS_8960_SLP_CLK_ENA				BIT(26)
 
 #define TSENS_THRESHOLD_ADDR		(MSM_CLK_CTL_BASE + 0x00003624)
diff --git a/drivers/thermal/msm8974-tsens.c b/drivers/thermal/msm8974-tsens.c
index 77cc1f9..8e13fbf 100644
--- a/drivers/thermal/msm8974-tsens.c
+++ b/drivers/thermal/msm8974-tsens.c
@@ -50,12 +50,25 @@
 #define TSENS_TRDY_MASK			BIT(0)
 
 #define TSENS_CTRL_ADDR(n)		(n)
+#define TSENS_EN			BIT(0)
 #define TSENS_SW_RST			BIT(1)
+#define TSENS_ADC_CLK_SEL		BIT(2)
+#define TSENS_SENSOR0_SHIFT		3
+#define TSENS_312_5_MS_MEAS_PERIOD	2
+#define TSENS_MEAS_PERIOD_SHIFT		18
+
 #define TSENS_SN_MIN_MAX_STATUS_CTRL(n)	((n) + 4)
 #define TSENS_GLOBAL_CONFIG(n)		((n) + 0x34)
 #define TSENS_S0_MAIN_CONFIG(n)		((n) + 0x38)
 #define TSENS_SN_REMOTE_CONFIG(n)	((n) + 0x3c)
 
+#define TSENS_EEPROM(n)			((n) + 0xd0)
+#define TSENS_EEPROM_REDUNDANCY_SEL(n)	((n) + 0x1cc)
+#define TSENS_EEPROM_BACKUP_REGION(n)	((n) + 0x440)
+
+#define TSENS_MAIN_CALIB_ADDR_RANGE	6
+#define TSENS_BACKUP_CALIB_ADDR_RANGE	4
+
 /* TSENS calibration Mask data */
 #define TSENS_BASE1_MASK		0xff
 #define TSENS0_POINT1_MASK		0x3f00
@@ -67,8 +80,11 @@
 #define TSENS6_POINT1_MASK		0x3f000
 #define TSENS7_POINT1_MASK		0xfc0000
 #define TSENS8_POINT1_MASK		0x3f000000
+#define TSENS8_POINT1_MASK_BACKUP	0x3f
 #define TSENS9_POINT1_MASK		0x3f
+#define TSENS9_POINT1_MASK_BACKUP	0xfc0
 #define TSENS10_POINT1_MASK		0xfc00
+#define TSENS10_POINT1_MASK_BACKUP	0x3f000
 #define TSENS_CAL_SEL_0_1		0xc0000000
 #define TSENS_CAL_SEL_2			0x40000000
 #define TSENS_CAL_SEL_SHIFT		30
@@ -85,31 +101,55 @@
 #define TSENS6_POINT1_SHIFT		12
 #define TSENS7_POINT1_SHIFT		18
 #define TSENS8_POINT1_SHIFT		24
+#define TSENS9_POINT1_BACKUP_SHIFT	6
 #define TSENS10_POINT1_SHIFT		6
+#define TSENS10_POINT1_BACKUP_SHIFT	12
 
 #define TSENS_POINT2_BASE_SHIFT		12
+#define TSENS_POINT2_BASE_BACKUP_SHIFT	18
 #define TSENS0_POINT2_SHIFT		20
+#define TSENS0_POINT2_BACKUP_SHIFT	26
 #define TSENS1_POINT2_SHIFT		26
+#define TSENS2_POINT2_BACKUP_SHIFT	6
 #define TSENS3_POINT2_SHIFT		6
+#define TSENS3_POINT2_BACKUP_SHIFT	12
 #define TSENS4_POINT2_SHIFT		12
+#define TSENS4_POINT2_BACKUP_SHIFT	18
 #define TSENS5_POINT2_SHIFT		18
+#define TSENS5_POINT2_BACKUP_SHIFT	24
 #define TSENS6_POINT2_SHIFT		24
+#define TSENS7_POINT2_BACKUP_SHIFT	6
 #define TSENS8_POINT2_SHIFT		6
+#define TSENS8_POINT2_BACKUP_SHIFT	12
 #define TSENS9_POINT2_SHIFT		12
+#define TSENS9_POINT2_BACKUP_SHIFT	18
 #define TSENS10_POINT2_SHIFT		18
+#define TSENS10_POINT2_BACKUP_SHIFT	24
 
 #define TSENS_BASE2_MASK		0xff000
+#define TSENS_BASE2_BACKUP_MASK		0xfc0000
 #define TSENS0_POINT2_MASK		0x3f00000
+#define TSENS0_POINT2_BACKUP_MASK	0xfc000000
 #define TSENS1_POINT2_MASK		0xfc000000
+#define TSENS1_POINT2_BACKUP_MASK	0x3f
 #define TSENS2_POINT2_MASK		0x3f
+#define TSENS2_POINT2_BACKUP_MASK	0xfc0
 #define TSENS3_POINT2_MASK		0xfc00
+#define TSENS3_POINT2_BACKUP_MASK	0x3f000
 #define TSENS4_POINT2_MASK		0x3f000
+#define TSENS4_POINT2_BACKUP_MASK	0xfc0000
 #define TSENS5_POINT2_MASK		0xfc0000
+#define TSENS5_POINT2_BACKUP_MASK	0x3f000000
 #define TSENS6_POINT2_MASK		0x3f000000
+#define TSENS6_POINT2_BACKUP_MASK	0x3f
 #define TSENS7_POINT2_MASK		0x3f
+#define TSENS7_POINT2_BACKUP_MASK	0xfc00
 #define TSENS8_POINT2_MASK		0xfc00
+#define TSENS8_POINT2_BACKUP_MASK	0x3f000
 #define TSENS9_POINT2_MASK		0x3f000
+#define TSENS9_POINT2_BACKUP_MASK	0xfc0000
 #define TSENS10_POINT2_MASK		0xfc0000
+#define TSENS10_POINT2_BACKUP_MASK	0x3f000000
 
 #define TSENS_BIT_APPEND		0x3
 #define TSENS_CAL_DEGC_POINT1		30
@@ -122,12 +162,15 @@
 #define TSENS_THRESHOLD_MAX_CODE	0x3ff
 #define TSENS_THRESHOLD_MIN_CODE	0x0
 
-#define TSENS_CTRL_INIT_DATA1		0x1cfff9
 #define TSENS_GLOBAL_INIT_DATA		0x302f16c
 #define TSENS_S0_MAIN_CFG_INIT_DATA	0x1c3
 #define TSENS_SN_MIN_MAX_STATUS_CTRL_DATA	0x3ffc00
 #define TSENS_SN_REMOTE_CFG_DATA	0x11c3
 
+#define TSENS_QFPROM_BACKUP_SEL		0x3
+#define TSENS_QFPROM_BACKUP_REDUN_SEL	0xe0000000
+#define TSENS_QFPROM_BACKUP_REDUN_SHIFT	29
+
 /* Trips: warm and cool */
 enum tsens_trip_type {
 	TSENS_TRIP_WARM = 0,
@@ -149,6 +192,7 @@
 struct tsens_tm_device {
 	struct platform_device		*pdev;
 	bool				prev_reading_avail;
+	bool				calibration_less_mode;
 	int				tsens_factor;
 	uint32_t			tsens_num_sensor;
 	int				tsens_irq;
@@ -166,19 +210,15 @@
 
 static int tsens_tz_code_to_degc(int adc_code, int sensor_num)
 {
-	int degcbeforefactor, degc;
-	degcbeforefactor = ((adc_code * tmdev->tsens_factor) -
-				tmdev->sensor[sensor_num].offset)/
-			tmdev->sensor[sensor_num].slope_mul_tsens_factor;
+	int degc, num, den;
 
-	if (degcbeforefactor == 0)
-		degc = degcbeforefactor;
-	else if (degcbeforefactor > 0)
-		degc = ((degcbeforefactor * tmdev->tsens_factor) +
-				tmdev->tsens_factor/2)/tmdev->tsens_factor;
-	else
-		degc = ((degcbeforefactor * tmdev->tsens_factor) -
-				tmdev->tsens_factor/2)/tmdev->tsens_factor;
+	num = ((adc_code * tmdev->tsens_factor) -
+				tmdev->sensor[sensor_num].offset);
+	den = (int) tmdev->sensor[sensor_num].slope_mul_tsens_factor;
+	degc = num/den;
+
+	if ((degc >= 0) && (num % den != 0))
+		degc++;
 
 	return degc;
 }
@@ -486,8 +526,10 @@
 	reg_cntl = readl_relaxed(TSENS_CTRL_ADDR(tmdev->tsens_addr));
 	writel_relaxed(reg_cntl | TSENS_SW_RST,
 			TSENS_CTRL_ADDR(tmdev->tsens_addr));
-	writel_relaxed(TSENS_CTRL_INIT_DATA1,
-			TSENS_CTRL_ADDR(tmdev->tsens_addr));
+	reg_cntl |= ((TSENS_312_5_MS_MEAS_PERIOD << TSENS_MEAS_PERIOD_SHIFT) |
+		(((1 << tmdev->tsens_num_sensor) - 1) << TSENS_SENSOR0_SHIFT) |
+		TSENS_EN);
+	writel_relaxed(reg_cntl, TSENS_CTRL_ADDR(tmdev->tsens_addr));
 	writel_relaxed(TSENS_GLOBAL_INIT_DATA,
 			TSENS_GLOBAL_CONFIG(tmdev->tsens_addr));
 	writel_relaxed(TSENS_S0_MAIN_CFG_INIT_DATA,
@@ -515,78 +557,185 @@
 	int tsens6_point2 = 0, tsens7_point2 = 0, tsens8_point2 = 0;
 	int tsens9_point2 = 0, tsens10_point2 = 0;
 	int tsens_base2_data = 0, tsens_calibration_mode = 0, temp = 0;
-	uint32_t calib_data[5];
+	uint32_t calib_data[6], calib_redun_sel, calib_data_backup[4];
 
-	for (i = 0; i < 5; i++)
-		calib_data[i] = readl_relaxed(tmdev->tsens_calib_addr
+	if (tmdev->calibration_less_mode)
+		goto calibration_less_mode;
+
+	calib_redun_sel = readl_relaxed(
+			TSENS_EEPROM_REDUNDANCY_SEL(tmdev->tsens_calib_addr));
+	calib_redun_sel = calib_redun_sel & TSENS_QFPROM_BACKUP_REDUN_SEL;
+	calib_redun_sel >>= TSENS_QFPROM_BACKUP_REDUN_SHIFT;
+
+	for (i = 0; i < TSENS_MAIN_CALIB_ADDR_RANGE; i++)
+		calib_data[i] = readl_relaxed(
+			(TSENS_EEPROM(tmdev->tsens_calib_addr))
 					+ (i * TSENS_SN_ADDR_OFFSET));
 
-	tsens_calibration_mode = (calib_data[1] & TSENS_CAL_SEL_0_1)
-			>> TSENS_CAL_SEL_SHIFT;
-	temp = (calib_data[3] & TSENS_CAL_SEL_2)
-			>> TSENS_CAL_SEL_SHIFT_2;
-	tsens_calibration_mode |= temp;
+	if (calib_redun_sel == TSENS_QFPROM_BACKUP_SEL) {
+		tsens_calibration_mode = (calib_data[4] & TSENS_CAL_SEL_0_1)
+				>> TSENS_CAL_SEL_SHIFT;
+		temp = (calib_data[5] & TSENS_CAL_SEL_2)
+				>> TSENS_CAL_SEL_SHIFT_2;
+		tsens_calibration_mode |= temp;
 
-	if (tsens_calibration_mode == 0) {
-		pr_debug("TSENS is calibrationless mode\n");
+		for (i = 0; i < TSENS_BACKUP_CALIB_ADDR_RANGE; i++)
+			calib_data_backup[i] = readl_relaxed(
+				(TSENS_EEPROM_BACKUP_REGION(
+					tmdev->tsens_calib_addr))
+				+ (i * TSENS_SN_ADDR_OFFSET));
+
+		if ((tsens_calibration_mode == TSENS_ONE_POINT_CALIB)
+				|| (tsens_calibration_mode ==
+				TSENS_TWO_POINT_CALIB) ||
+				(tsens_calibration_mode ==
+				TSENS_ONE_POINT_CALIB_OPTION_2)) {
+			pr_debug("backup one point calibrationless mode\n");
+			tsens_base1_data = (calib_data_backup[0] &
+						TSENS_BASE1_MASK);
+			tsens0_point1 = (calib_data_backup[0] &
+						TSENS0_POINT1_MASK) >>
+						TSENS0_POINT1_SHIFT;
+			tsens1_point1 = (calib_data_backup[0] &
+				TSENS1_POINT1_MASK) >> TSENS1_POINT1_SHIFT;
+			tsens2_point1 = (calib_data_backup[0] &
+				TSENS2_POINT1_MASK) >> TSENS2_POINT1_SHIFT;
+			tsens3_point1 = (calib_data_backup[0] &
+				TSENS3_POINT1_MASK) >> TSENS3_POINT1_SHIFT;
+			tsens4_point1 = (calib_data_backup[1] &
+				TSENS4_POINT1_MASK);
+			tsens5_point1 = (calib_data_backup[1] &
+				TSENS5_POINT1_MASK) >> TSENS5_POINT1_SHIFT;
+			tsens6_point1 = (calib_data_backup[1] &
+				TSENS6_POINT1_MASK) >> TSENS6_POINT1_SHIFT;
+			tsens7_point1 = (calib_data_backup[1] &
+				TSENS7_POINT1_MASK) >> TSENS7_POINT1_SHIFT;
+			tsens8_point1 = (calib_data_backup[2] &
+						TSENS8_POINT1_MASK_BACKUP) >>
+						TSENS8_POINT1_SHIFT;
+			tsens9_point1 = (calib_data_backup[2] &
+					TSENS9_POINT1_MASK_BACKUP) >>
+					TSENS9_POINT1_BACKUP_SHIFT;
+			tsens10_point1 = (calib_data_backup[2] &
+				TSENS10_POINT1_MASK_BACKUP) >>
+				TSENS10_POINT1_BACKUP_SHIFT;
+	} else if (tsens_calibration_mode == TSENS_TWO_POINT_CALIB) {
+		pr_debug("backup two point calibrationless mode\n");
+		tsens_base2_data = (calib_data_backup[2] &
+				TSENS_BASE2_BACKUP_MASK) >>
+				TSENS_POINT2_BASE_BACKUP_SHIFT;
+		tsens0_point2 = (calib_data_backup[2] &
+					TSENS0_POINT2_BACKUP_MASK) >>
+					TSENS0_POINT2_BACKUP_SHIFT;
+		tsens1_point2 = (calib_data_backup[3] &
+					TSENS1_POINT2_BACKUP_MASK);
+		tsens2_point2 = (calib_data_backup[3] &
+					TSENS2_POINT2_BACKUP_MASK) >>
+					TSENS2_POINT2_BACKUP_SHIFT;
+		tsens3_point2 = (calib_data_backup[3] &
+					TSENS3_POINT2_BACKUP_MASK) >>
+					TSENS3_POINT2_BACKUP_SHIFT;
+		tsens4_point2 = (calib_data_backup[3] &
+					TSENS4_POINT2_BACKUP_MASK) >>
+					TSENS4_POINT2_BACKUP_SHIFT;
+		tsens5_point2 = (calib_data[4] & TSENS5_POINT2_BACKUP_MASK) >>
+						TSENS5_POINT2_BACKUP_SHIFT;
+		tsens6_point2 = (calib_data[5] & TSENS6_POINT2_BACKUP_MASK);
+		tsens7_point2 = (calib_data[5] & TSENS7_POINT2_BACKUP_MASK) >>
+						TSENS7_POINT2_BACKUP_SHIFT;
+		tsens8_point2 = (calib_data[5] & TSENS8_POINT2_BACKUP_MASK) >>
+						TSENS8_POINT2_BACKUP_SHIFT;
+		tsens9_point2 = (calib_data[5] & TSENS9_POINT2_BACKUP_MASK) >>
+						TSENS9_POINT2_BACKUP_SHIFT;
+		tsens10_point2 = (calib_data[5] & TSENS10_POINT2_BACKUP_MASK)
+						>> TSENS10_POINT2_BACKUP_SHIFT;
+	} else {
+		pr_debug("TSENS:backup is calibrationless mode\n");
 		for (i = 0; i < tmdev->tsens_num_sensor; i++) {
 			tmdev->sensor[i].calib_data_point2 = 780;
 			tmdev->sensor[i].calib_data_point1 = 492;
 		}
+		tsens_calibration_mode = 0;
 		goto compute_intercept_slope;
-	} else if ((tsens_calibration_mode == TSENS_ONE_POINT_CALIB) ||
-			(tsens_calibration_mode == TSENS_TWO_POINT_CALIB)) {
-		tsens_base1_data = (calib_data[0] & TSENS_BASE1_MASK);
-		tsens0_point1 = (calib_data[0] & TSENS0_POINT1_MASK) >>
-							TSENS0_POINT1_SHIFT;
-		tsens1_point1 = (calib_data[0] & TSENS1_POINT1_MASK) >>
-							TSENS1_POINT1_SHIFT;
-		tsens2_point1 = (calib_data[0] & TSENS2_POINT1_MASK) >>
-							TSENS2_POINT1_SHIFT;
-		tsens3_point1 = (calib_data[0] & TSENS3_POINT1_MASK) >>
-							TSENS3_POINT1_SHIFT;
-		tsens4_point1 = (calib_data[1] & TSENS4_POINT1_MASK);
-		tsens5_point1 = (calib_data[1] & TSENS5_POINT1_MASK) >>
-							TSENS5_POINT1_SHIFT;
-		tsens6_point1 = (calib_data[1] & TSENS6_POINT1_MASK) >>
-							TSENS6_POINT1_SHIFT;
-		tsens7_point1 = (calib_data[1] & TSENS7_POINT1_MASK) >>
-							TSENS7_POINT1_SHIFT;
-		tsens8_point1 = (calib_data[1] & TSENS8_POINT1_MASK) >>
-							TSENS8_POINT1_SHIFT;
-		tsens9_point1 = (calib_data[2] & TSENS9_POINT1_MASK);
-		tsens10_point1 = (calib_data[2] & TSENS10_POINT1_MASK) >>
-							TSENS10_POINT1_SHIFT;
-	} else if (tsens_calibration_mode == TSENS_TWO_POINT_CALIB) {
-		tsens_base2_data = (calib_data[2] & TSENS_BASE2_MASK) >>
-						TSENS_POINT2_BASE_SHIFT;
-		tsens0_point2 = (calib_data[2] & TSENS0_POINT2_MASK) >>
-						TSENS0_POINT2_SHIFT;
-		tsens1_point2 = (calib_data[2] & TSENS1_POINT2_MASK) >>
-						TSENS1_POINT2_SHIFT;
-		tsens2_point2 = (calib_data[3] & TSENS2_POINT2_MASK);
-		tsens3_point2 = (calib_data[3] & TSENS3_POINT2_MASK) >>
-						TSENS3_POINT2_SHIFT;
-		tsens4_point2 = (calib_data[3] & TSENS4_POINT2_MASK) >>
-						TSENS4_POINT2_SHIFT;
-		tsens5_point2 = (calib_data[3] & TSENS5_POINT2_MASK) >>
-						TSENS5_POINT2_SHIFT;
-		tsens6_point2 = (calib_data[3] & TSENS6_POINT2_MASK) >>
-						TSENS6_POINT2_SHIFT;
-		tsens7_point2 = (calib_data[4] & TSENS7_POINT2_MASK);
-		tsens8_point2 = (calib_data[4] & TSENS8_POINT2_MASK) >>
-						TSENS8_POINT2_SHIFT;
-		tsens9_point2 = (calib_data[4] & TSENS9_POINT2_MASK) >>
-						TSENS9_POINT2_SHIFT;
-		tsens10_point2 = (calib_data[4] & TSENS10_POINT2_MASK) >>
-						TSENS10_POINT2_SHIFT;
+	}
 	} else {
-		pr_debug("Calibration mode is unknown: %d\n",
-						tsens_calibration_mode);
-		return -ENODEV;
+		tsens_calibration_mode = (calib_data[1] & TSENS_CAL_SEL_0_1)
+			>> TSENS_CAL_SEL_SHIFT;
+		temp = (calib_data[3] & TSENS_CAL_SEL_2)
+			>> TSENS_CAL_SEL_SHIFT_2;
+		tsens_calibration_mode |= temp;
+		if ((tsens_calibration_mode == TSENS_ONE_POINT_CALIB) ||
+			(tsens_calibration_mode ==
+					TSENS_ONE_POINT_CALIB_OPTION_2) ||
+			(tsens_calibration_mode == TSENS_TWO_POINT_CALIB)) {
+			pr_debug("TSENS is one point calibrationless mode\n");
+			tsens_base1_data = (calib_data[0] & TSENS_BASE1_MASK);
+			tsens0_point1 = (calib_data[0] & TSENS0_POINT1_MASK) >>
+							TSENS0_POINT1_SHIFT;
+			tsens1_point1 = (calib_data[0] & TSENS1_POINT1_MASK) >>
+							TSENS1_POINT1_SHIFT;
+			tsens2_point1 = (calib_data[0] & TSENS2_POINT1_MASK) >>
+							TSENS2_POINT1_SHIFT;
+			tsens3_point1 = (calib_data[0] & TSENS3_POINT1_MASK) >>
+							TSENS3_POINT1_SHIFT;
+			tsens4_point1 = (calib_data[1] & TSENS4_POINT1_MASK);
+			tsens5_point1 = (calib_data[1] & TSENS5_POINT1_MASK) >>
+							TSENS5_POINT1_SHIFT;
+			tsens6_point1 = (calib_data[1] & TSENS6_POINT1_MASK) >>
+							TSENS6_POINT1_SHIFT;
+			tsens7_point1 = (calib_data[1] & TSENS7_POINT1_MASK) >>
+							TSENS7_POINT1_SHIFT;
+			tsens8_point1 = (calib_data[1] & TSENS8_POINT1_MASK) >>
+							TSENS8_POINT1_SHIFT;
+			tsens9_point1 = (calib_data[2] & TSENS9_POINT1_MASK);
+			tsens10_point1 = (calib_data[2] & TSENS10_POINT1_MASK)
+							>> TSENS10_POINT1_SHIFT;
+		} else if (tsens_calibration_mode == TSENS_TWO_POINT_CALIB) {
+			pr_debug("TSENS is two point calibrationless mode\n");
+			tsens_base2_data = (calib_data[2] & TSENS_BASE2_MASK) >>
+						TSENS_POINT2_BASE_SHIFT;
+			tsens0_point2 = (calib_data[2] & TSENS0_POINT2_MASK) >>
+						TSENS0_POINT2_SHIFT;
+			tsens1_point2 = (calib_data[2] & TSENS1_POINT2_MASK) >>
+						TSENS1_POINT2_SHIFT;
+			tsens2_point2 = (calib_data[3] & TSENS2_POINT2_MASK);
+			tsens3_point2 = (calib_data[3] & TSENS3_POINT2_MASK) >>
+						TSENS3_POINT2_SHIFT;
+			tsens4_point2 = (calib_data[3] & TSENS4_POINT2_MASK) >>
+						TSENS4_POINT2_SHIFT;
+			tsens5_point2 = (calib_data[3] & TSENS5_POINT2_MASK) >>
+						TSENS5_POINT2_SHIFT;
+			tsens6_point2 = (calib_data[3] & TSENS6_POINT2_MASK) >>
+						TSENS6_POINT2_SHIFT;
+			tsens7_point2 = (calib_data[4] & TSENS7_POINT2_MASK);
+			tsens8_point2 = (calib_data[4] & TSENS8_POINT2_MASK) >>
+						TSENS8_POINT2_SHIFT;
+			tsens9_point2 = (calib_data[4] & TSENS9_POINT2_MASK) >>
+						TSENS9_POINT2_SHIFT;
+			tsens10_point2 = (calib_data[4] & TSENS10_POINT2_MASK)
+						>> TSENS10_POINT2_SHIFT;
+		} else {
+calibration_less_mode:
+			pr_debug("TSENS is calibrationless mode\n");
+			for (i = 0; i < tmdev->tsens_num_sensor; i++)
+				tmdev->sensor[i].calib_data_point2 = 780;
+			tmdev->sensor[0].calib_data_point1 = 502;
+			tmdev->sensor[1].calib_data_point1 = 509;
+			tmdev->sensor[2].calib_data_point1 = 503;
+			tmdev->sensor[3].calib_data_point1 = 509;
+			tmdev->sensor[4].calib_data_point1 = 505;
+			tmdev->sensor[5].calib_data_point1 = 509;
+			tmdev->sensor[6].calib_data_point1 = 507;
+			tmdev->sensor[7].calib_data_point1 = 510;
+			tmdev->sensor[8].calib_data_point1 = 508;
+			tmdev->sensor[9].calib_data_point1 = 509;
+			tmdev->sensor[10].calib_data_point1 = 508;
+			goto compute_intercept_slope;
+		}
 	}
 
 	if (tsens_calibration_mode == TSENS_ONE_POINT_CALIB) {
+		pr_debug("old one point calibration calculation\n");
 		tmdev->sensor[0].calib_data_point1 =
 		(((tsens_base1_data) << 2) | TSENS_BIT_APPEND) + tsens0_point1;
 		tmdev->sensor[1].calib_data_point1 =
@@ -613,6 +762,8 @@
 
 	if ((tsens_calibration_mode == TSENS_ONE_POINT_CALIB_OPTION_2) ||
 			(tsens_calibration_mode == TSENS_TWO_POINT_CALIB)) {
+		pr_debug("one and two point calibration calculation\n");
+
 		tmdev->sensor[0].calib_data_point1 =
 		((((tsens_base1_data) + tsens0_point1) << 2) |
 						TSENS_BIT_APPEND);
@@ -649,6 +800,7 @@
 	}
 
 	if (tsens_calibration_mode == TSENS_TWO_POINT_CALIB) {
+		pr_debug("two point calibration calculation\n");
 		tmdev->sensor[0].calib_data_point2 =
 		(((tsens_base2_data + tsens0_point2) << 2) | TSENS_BIT_APPEND);
 		tmdev->sensor[1].calib_data_point2 =
@@ -708,7 +860,7 @@
 	}
 
 	tsens_slope_data = devm_kzalloc(&pdev->dev,
-				tsens_num_sensors, GFP_KERNEL);
+			tsens_num_sensors * sizeof(u32), GFP_KERNEL);
 	if (!tsens_slope_data) {
 		dev_err(&pdev->dev, "can not allocate slope data\n");
 		return -ENOMEM;
@@ -735,18 +887,23 @@
 		tmdev->sensor[i].slope_mul_tsens_factor = tsens_slope_data[i];
 	tmdev->tsens_factor = TSENS_SLOPE_FACTOR;
 	tmdev->tsens_num_sensor = tsens_num_sensors;
+	tmdev->calibration_less_mode = of_property_read_bool(of_node,
+				"qcom,calibration-less-mode");
 
 	tmdev->tsens_irq = platform_get_irq(pdev, 0);
 	if (tmdev->tsens_irq < 0) {
 		pr_err("Invalid get irq\n");
-		return tmdev->tsens_irq;
+		rc = tmdev->tsens_irq;
+		goto fail_tmdev;
 	}
 
+	/* TSENS register region */
 	tmdev->res_tsens_mem = platform_get_resource_byname(pdev,
 					IORESOURCE_MEM, "tsens_physical");
 	if (!tmdev->res_tsens_mem) {
 		pr_err("Could not get tsens physical address resource\n");
-		return -EINVAL;
+		rc = -EINVAL;
+		goto fail_tmdev;
 	}
 
 	tmdev->tsens_len = tmdev->res_tsens_mem->end -
@@ -756,7 +913,8 @@
 				tmdev->tsens_len, tmdev->res_tsens_mem->name);
 	if (!res_mem) {
 		pr_err("Request tsens physical memory region failed\n");
-		return -EINVAL;
+		rc = -EINVAL;
+		goto fail_tmdev;
 	}
 
 	tmdev->tsens_addr = ioremap(res_mem->start, tmdev->tsens_len);
@@ -766,6 +924,7 @@
 		goto fail_unmap_tsens_region;
 	}
 
+	/* TSENS calibration region */
 	tmdev->res_calib_mem = platform_get_resource_byname(pdev,
 				IORESOURCE_MEM, "tsens_eeprom_physical");
 	if (!tmdev->res_calib_mem) {
@@ -806,6 +965,8 @@
 	if (tmdev->res_tsens_mem)
 		release_mem_region(tmdev->res_tsens_mem->start,
 					tmdev->tsens_len);
+fail_tmdev:
+	tmdev = NULL;
 	return rc;
 }
 
@@ -818,9 +979,13 @@
 		return -EBUSY;
 	}
 
-	if (pdev->dev.of_node)
+	if (pdev->dev.of_node) {
 		rc = get_device_tree_data(pdev);
-	else
+		if (rc) {
+			pr_err("Error reading TSENS DT\n");
+			return rc;
+		}
+	} else
 		return -ENODEV;
 
 	tmdev->pdev = pdev;
@@ -899,14 +1064,12 @@
 		iounmap(tmdev->tsens_calib_addr);
 	if (tmdev->res_calib_mem)
 		release_mem_region(tmdev->res_calib_mem->start,
-				tmdev->calib_len);
+					tmdev->calib_len);
 	if (tmdev->tsens_addr)
 		iounmap(tmdev->tsens_addr);
 	if (tmdev->res_tsens_mem)
 		release_mem_region(tmdev->res_tsens_mem->start,
-				tmdev->tsens_len);
-	kfree(tmdev);
-
+			tmdev->tsens_len);
 	return rc;
 }
 
@@ -921,12 +1084,12 @@
 		iounmap(tmdev->tsens_calib_addr);
 	if (tmdev->res_calib_mem)
 		release_mem_region(tmdev->res_calib_mem->start,
-				tmdev->calib_len);
+					tmdev->calib_len);
 	if (tmdev->tsens_addr)
 		iounmap(tmdev->tsens_addr);
 	if (tmdev->res_tsens_mem)
 		release_mem_region(tmdev->res_tsens_mem->start,
-				tmdev->tsens_len);
+			tmdev->tsens_len);
 	free_irq(tmdev->tsens_irq, tmdev);
 	platform_set_drvdata(pdev, NULL);
 
diff --git a/drivers/thermal/qpnp-temp-alarm.c b/drivers/thermal/qpnp-temp-alarm.c
new file mode 100644
index 0000000..499d67e
--- /dev/null
+++ b/drivers/thermal/qpnp-temp-alarm.c
@@ -0,0 +1,721 @@
+/*
+ * Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/string.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/spmi.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/thermal.h>
+#include <linux/qpnp/qpnp-adc.h>
+
+#define QPNP_TM_DRIVER_NAME "qcom,qpnp-temp-alarm"
+
+enum qpnp_tm_registers {
+	QPNP_TM_REG_TYPE		= 0x04,
+	QPNP_TM_REG_SUBTYPE		= 0x05,
+	QPNP_TM_REG_STATUS		= 0x08,
+	QPNP_TM_REG_SHUTDOWN_CTRL1	= 0x40,
+	QPNP_TM_REG_SHUTDOWN_CTRL2	= 0x42,
+	QPNP_TM_REG_ALARM_CTRL		= 0x46,
+};
+
+#define QPNP_TM_TYPE			0x09
+#define QPNP_TM_SUBTYPE			0x08
+
+#define STATUS_STAGE_MASK		0x03
+
+#define SHUTDOWN_CTRL1_OVERRIDE_STAGE3	0x80
+#define SHUTDOWN_CTRL1_OVERRIDE_STAGE2	0x40
+#define SHUTDOWN_CTRL1_THRESHOLD_MASK	0x03
+
+#define SHUTDOWN_CTRL2_CLEAR_STAGE3	0x80
+#define SHUTDOWN_CTRL2_CLEAR_STAGE2	0x40
+
+#define ALARM_CTRL_FORCE_ENABLE		0x80
+#define ALARM_CTRL_FOLLOW_HW_ENABLE	0x01
+
+#define TEMP_STAGE_STEP			20000	/* Stage step: 20.000 C */
+#define TEMP_STAGE_HYSTERESIS		2000
+
+#define TEMP_THRESH_MIN			105000	/* Threshold Min: 105 C */
+#define TEMP_THRESH_STEP		5000	/* Threshold step: 5 C */
+
+#define THRESH_MIN			0
+#define THRESH_MAX			3
+
+/* Trip points from most critical to least critical */
+#define TRIP_STAGE3			0
+#define TRIP_STAGE2			1
+#define TRIP_STAGE1			2
+#define TRIP_NUM			3
+
+enum qpnp_tm_adc_type {
+	QPNP_TM_ADC_NONE,	/* Estimates temp based on overload level. */
+	QPNP_TM_ADC_QPNP_ADC,
+};
+
+/*
+ * Temperature in millicelcius reported during stage 0 if no ADC is present and
+ * no value has been specified via device tree.
+ */
+#define DEFAULT_NO_ADC_TEMP		37000
+
+struct qpnp_tm_chip {
+	struct delayed_work		irq_work;
+	struct spmi_device		*spmi_dev;
+	struct thermal_zone_device	*tz_dev;
+	const char			*tm_name;
+	enum qpnp_tm_adc_type		adc_type;
+	unsigned long			temperature;
+	enum thermal_device_mode	mode;
+	unsigned int			thresh;
+	unsigned int			stage;
+	unsigned int			prev_stage;
+	int				irq;
+	enum qpnp_vadc_channels		adc_channel;
+	u16				base_addr;
+	bool				allow_software_override;
+};
+
+/* Delay between TEMP_STAT IRQ going high and status value changing in ms. */
+#define STATUS_REGISTER_DELAY_MS       40
+
+enum pmic_thermal_override_mode {
+	SOFTWARE_OVERRIDE_DISABLED = 0,
+	SOFTWARE_OVERRIDE_ENABLED,
+};
+
+static inline int qpnp_tm_read(struct qpnp_tm_chip *chip, u16 addr, u8 *buf,
+				int len)
+{
+	int rc;
+
+	rc = spmi_ext_register_readl(chip->spmi_dev->ctrl,
+			chip->spmi_dev->sid, chip->base_addr + addr, buf, len);
+
+	if (rc)
+		dev_err(&chip->spmi_dev->dev, "%s: spmi_ext_register_readl() failed. sid=%d, addr=%04X, len=%d, rc=%d\n",
+			__func__, chip->spmi_dev->sid, chip->base_addr + addr,
+			len, rc);
+
+	return rc;
+}
+
+static inline int qpnp_tm_write(struct qpnp_tm_chip *chip, u16 addr, u8 *buf,
+				int len)
+{
+	int rc;
+
+	rc = spmi_ext_register_writel(chip->spmi_dev->ctrl,
+			chip->spmi_dev->sid, chip->base_addr + addr, buf, len);
+
+	if (rc)
+		dev_err(&chip->spmi_dev->dev, "%s: spmi_ext_register_writel() failed. sid=%d, addr=%04X, len=%d, rc=%d\n",
+			__func__, chip->spmi_dev->sid, chip->base_addr + addr,
+			len, rc);
+
+	return rc;
+}
+
+
+static inline int qpnp_tm_shutdown_override(struct qpnp_tm_chip *chip,
+			    enum pmic_thermal_override_mode mode)
+{
+	int rc = 0;
+	u8 reg;
+
+	if (chip->allow_software_override) {
+		reg = chip->thresh & SHUTDOWN_CTRL1_THRESHOLD_MASK;
+
+		if (mode == SOFTWARE_OVERRIDE_ENABLED)
+			reg |= SHUTDOWN_CTRL1_OVERRIDE_STAGE2
+				| SHUTDOWN_CTRL1_OVERRIDE_STAGE3;
+
+		rc = qpnp_tm_write(chip, QPNP_TM_REG_SHUTDOWN_CTRL1, &reg, 1);
+	}
+
+	return rc;
+}
+
+static int qpnp_tm_update_temp(struct qpnp_tm_chip *chip)
+{
+	struct qpnp_vadc_result adc_result;
+	int rc;
+
+	rc = qpnp_vadc_read(chip->adc_channel, &adc_result);
+	if (!rc)
+		chip->temperature = adc_result.physical;
+	else
+		dev_err(&chip->spmi_dev->dev, "%s: qpnp_vadc_read(%d) failed, rc=%d\n",
+			__func__, chip->adc_channel, rc);
+
+	return rc;
+}
+
+/*
+ * This function initializes the internal temperature value based on only the
+ * current thermal stage and threshold.
+ */
+static int qpnp_tm_init_temp_no_adc(struct qpnp_tm_chip *chip)
+{
+	int rc;
+	u8 reg;
+
+	rc = qpnp_tm_read(chip, QPNP_TM_REG_STATUS, &reg, 1);
+	if (rc < 0)
+		return rc;
+
+	chip->stage = reg & STATUS_STAGE_MASK;
+
+	if (chip->stage)
+		chip->temperature = chip->thresh * TEMP_THRESH_STEP +
+			   (chip->stage - 1) * TEMP_STAGE_STEP +
+			   TEMP_THRESH_MIN;
+
+	return 0;
+}
+
+/*
+ * This function updates the internal temperature value based on the
+ * current thermal stage and threshold as well as the previous stage
+ */
+static int qpnp_tm_update_temp_no_adc(struct qpnp_tm_chip *chip)
+{
+	unsigned int stage;
+	int rc;
+	u8 reg;
+
+	rc = qpnp_tm_read(chip, QPNP_TM_REG_STATUS, &reg, 1);
+	if (rc < 0)
+		return rc;
+
+	stage = reg & STATUS_STAGE_MASK;
+
+	if (stage > chip->stage) {
+		/* increasing stage, use lower bound */
+		chip->temperature = (stage - 1) * TEMP_STAGE_STEP
+				+ chip->thresh * TEMP_THRESH_STEP
+				+ TEMP_STAGE_HYSTERESIS + TEMP_THRESH_MIN;
+	} else if (stage < chip->stage) {
+		/* decreasing stage, use upper bound */
+		chip->temperature = stage * TEMP_STAGE_STEP
+				+ chip->thresh * TEMP_THRESH_STEP
+				- TEMP_STAGE_HYSTERESIS + TEMP_THRESH_MIN;
+	}
+
+	chip->stage = stage;
+
+	return 0;
+}
+
+static int qpnp_tz_get_temp_no_adc(struct thermal_zone_device *thermal,
+				     unsigned long *temperature)
+{
+	struct qpnp_tm_chip *chip = thermal->devdata;
+	int rc;
+
+	if (!temperature)
+		return -EINVAL;
+
+	rc = qpnp_tm_update_temp_no_adc(chip);
+	if (rc < 0)
+		return rc;
+
+	*temperature = chip->temperature;
+
+	return 0;
+}
+
+static int qpnp_tz_get_temp_qpnp_adc(struct thermal_zone_device *thermal,
+				      unsigned long *temperature)
+{
+	struct qpnp_tm_chip *chip = thermal->devdata;
+	int rc;
+
+	if (!temperature)
+		return -EINVAL;
+
+	rc = qpnp_tm_update_temp(chip);
+	if (rc < 0) {
+		dev_err(&chip->spmi_dev->dev, "%s: %s: adc read failed, rc = %d\n",
+			__func__, chip->tm_name, rc);
+		return rc;
+	}
+
+	*temperature = chip->temperature;
+
+	return 0;
+}
+
+static int qpnp_tz_get_mode(struct thermal_zone_device *thermal,
+			      enum thermal_device_mode *mode)
+{
+	struct qpnp_tm_chip *chip = thermal->devdata;
+
+	if (!mode)
+		return -EINVAL;
+
+	*mode = chip->mode;
+
+	return 0;
+}
+
+static int qpnp_tz_set_mode(struct thermal_zone_device *thermal,
+			      enum thermal_device_mode mode)
+{
+	struct qpnp_tm_chip *chip = thermal->devdata;
+	int rc = 0;
+
+	if (mode != chip->mode) {
+		if (mode == THERMAL_DEVICE_ENABLED)
+			rc = qpnp_tm_shutdown_override(chip,
+				SOFTWARE_OVERRIDE_ENABLED);
+		else
+			rc = qpnp_tm_shutdown_override(chip,
+				SOFTWARE_OVERRIDE_DISABLED);
+
+		chip->mode = mode;
+	}
+
+	return rc;
+}
+
+static int qpnp_tz_get_trip_type(struct thermal_zone_device *thermal,
+				   int trip, enum thermal_trip_type *type)
+{
+	if (trip < 0 || !type)
+		return -EINVAL;
+
+	switch (trip) {
+	case TRIP_STAGE3:
+		*type = THERMAL_TRIP_CRITICAL;
+		break;
+	case TRIP_STAGE2:
+		*type = THERMAL_TRIP_HOT;
+		break;
+	case TRIP_STAGE1:
+		*type = THERMAL_TRIP_HOT;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int qpnp_tz_get_trip_temp(struct thermal_zone_device *thermal,
+				   int trip, unsigned long *temperature)
+{
+	struct qpnp_tm_chip *chip = thermal->devdata;
+	int thresh_temperature;
+
+	if (trip < 0 || !temperature)
+		return -EINVAL;
+
+	thresh_temperature = chip->thresh * TEMP_THRESH_STEP + TEMP_THRESH_MIN;
+
+	switch (trip) {
+	case TRIP_STAGE3:
+		thresh_temperature += 2 * TEMP_STAGE_STEP;
+		break;
+	case TRIP_STAGE2:
+		thresh_temperature += TEMP_STAGE_STEP;
+		break;
+	case TRIP_STAGE1:
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	*temperature = thresh_temperature;
+
+	return 0;
+}
+
+static int qpnp_tz_get_crit_temp(struct thermal_zone_device *thermal,
+				   unsigned long *temperature)
+{
+	struct qpnp_tm_chip *chip = thermal->devdata;
+
+	if (!temperature)
+		return -EINVAL;
+
+	*temperature = chip->thresh * TEMP_THRESH_STEP + TEMP_THRESH_MIN +
+		2 * TEMP_STAGE_STEP;
+
+	return 0;
+}
+
+static struct thermal_zone_device_ops qpnp_thermal_zone_ops_no_adc = {
+	.get_temp = qpnp_tz_get_temp_no_adc,
+	.get_mode = qpnp_tz_get_mode,
+	.set_mode = qpnp_tz_set_mode,
+	.get_trip_type = qpnp_tz_get_trip_type,
+	.get_trip_temp = qpnp_tz_get_trip_temp,
+	.get_crit_temp = qpnp_tz_get_crit_temp,
+};
+
+static struct thermal_zone_device_ops qpnp_thermal_zone_ops_qpnp_adc = {
+	.get_temp = qpnp_tz_get_temp_qpnp_adc,
+	.get_mode = qpnp_tz_get_mode,
+	.set_mode = qpnp_tz_set_mode,
+	.get_trip_type = qpnp_tz_get_trip_type,
+	.get_trip_temp = qpnp_tz_get_trip_temp,
+	.get_crit_temp = qpnp_tz_get_crit_temp,
+};
+
+static void qpnp_tm_work(struct work_struct *work)
+{
+	struct delayed_work *dwork
+		= container_of(work, struct delayed_work, work);
+	struct qpnp_tm_chip *chip
+		= container_of(dwork, struct qpnp_tm_chip, irq_work);
+	int rc;
+	u8 reg;
+
+	if (chip->adc_type == QPNP_TM_ADC_NONE) {
+		rc = qpnp_tm_update_temp_no_adc(chip);
+		if (rc < 0)
+			goto bail;
+	} else {
+		rc = qpnp_tm_read(chip, QPNP_TM_REG_STATUS, &reg, 1);
+		if (rc < 0)
+			goto bail;
+
+		chip->stage = reg & STATUS_STAGE_MASK;
+
+		rc = qpnp_tm_update_temp(chip);
+		if (rc < 0)
+			goto bail;
+	}
+
+	if (chip->stage != chip->prev_stage) {
+		chip->prev_stage = chip->stage;
+
+		pr_crit("%s: PMIC Temp Alarm - stage=%u, threshold=%u, temperature=%lu mC\n",
+			chip->tm_name, chip->stage, chip->thresh,
+			chip->temperature);
+
+		thermal_zone_device_update(chip->tz_dev);
+
+		/* Notify user space */
+		sysfs_notify(&chip->tz_dev->device.kobj, NULL, "type");
+	}
+
+bail:
+	return;
+}
+
+static irqreturn_t qpnp_tm_isr(int irq, void *data)
+{
+	struct qpnp_tm_chip *chip = data;
+
+	schedule_delayed_work(&chip->irq_work,
+			msecs_to_jiffies(STATUS_REGISTER_DELAY_MS) + 1);
+
+	return IRQ_HANDLED;
+}
+
+static int qpnp_tm_init_reg(struct qpnp_tm_chip *chip)
+{
+	int rc = 0;
+	u8 reg;
+
+	if (chip->thresh < THRESH_MIN || chip->thresh > THRESH_MAX) {
+		/* Read hardware threshold value if configuration is invalid. */
+		rc = qpnp_tm_read(chip, QPNP_TM_REG_SHUTDOWN_CTRL1, &reg, 1);
+		if (rc < 0)
+			return rc;
+		chip->thresh = reg & SHUTDOWN_CTRL1_THRESHOLD_MASK;
+	}
+
+	/*
+	 * Set threshold and disable software override of stage 2 and 3
+	 * shutdowns.
+	 */
+	reg = chip->thresh & SHUTDOWN_CTRL1_THRESHOLD_MASK;
+	rc = qpnp_tm_write(chip, QPNP_TM_REG_SHUTDOWN_CTRL1, &reg, 1);
+	if (rc < 0)
+		return rc;
+
+	/* Enable the thermal alarm PMIC module in always-on mode. */
+	reg = ALARM_CTRL_FORCE_ENABLE;
+	rc = qpnp_tm_write(chip, QPNP_TM_REG_ALARM_CTRL, &reg, 1);
+
+	return rc;
+}
+
+static int __devinit qpnp_tm_probe(struct spmi_device *spmi)
+{
+	struct device_node *node;
+	struct resource *res;
+	struct qpnp_tm_chip *chip;
+	struct thermal_zone_device_ops *tz_ops;
+	char *tm_name;
+	u32 default_temperature;
+	int rc = 0;
+	u8 raw_type[2], type, subtype;
+
+	if (!spmi || !(&spmi->dev) || !spmi->dev.of_node) {
+		dev_err(&spmi->dev, "%s: device tree node not found\n",
+			__func__);
+		return -EINVAL;
+	}
+
+	node = spmi->dev.of_node;
+
+	chip = kzalloc(sizeof(struct qpnp_tm_chip), GFP_KERNEL);
+	if (!chip) {
+		dev_err(&spmi->dev, "%s: Can't allocate qpnp_tm_chip\n",
+			__func__);
+		return -ENOMEM;
+	}
+
+	dev_set_drvdata(&spmi->dev, chip);
+
+	res = spmi_get_resource(spmi, NULL, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(&spmi->dev, "%s: node is missing base address\n",
+			__func__);
+		rc = -EINVAL;
+		goto free_chip;
+	}
+	chip->base_addr	= res->start;
+	chip->spmi_dev	= spmi;
+
+	chip->irq = spmi_get_irq(spmi, NULL, 0);
+	if (chip->irq < 0) {
+		rc = chip->irq;
+		dev_err(&spmi->dev, "%s: node is missing irq, rc=%d\n",
+			__func__, rc);
+		goto free_chip;
+	}
+
+	chip->tm_name = of_get_property(node, "label", NULL);
+	if (chip->tm_name == NULL) {
+		dev_err(&spmi->dev, "%s: node is missing label\n",
+			__func__);
+		rc = -EINVAL;
+		goto free_chip;
+	}
+
+	tm_name = kstrdup(chip->tm_name, GFP_KERNEL);
+	if (tm_name == NULL) {
+		dev_err(&spmi->dev, "%s: could not allocate memory for label\n",
+			__func__);
+		rc = -ENOMEM;
+		goto free_chip;
+	}
+	chip->tm_name = tm_name;
+
+	INIT_DELAYED_WORK(&chip->irq_work, qpnp_tm_work);
+
+	/* These bindings are optional, so it is okay if they are not found. */
+	chip->thresh = THRESH_MAX + 1;
+	rc = of_property_read_u32(node, "qcom,threshold-set", &chip->thresh);
+	if (!rc && (chip->thresh < THRESH_MIN || chip->thresh > THRESH_MAX))
+		dev_err(&spmi->dev, "%s: invalid qcom,threshold-set=%u specified\n",
+			__func__, chip->thresh);
+
+	chip->adc_type = QPNP_TM_ADC_NONE;
+	rc = of_property_read_u32(node, "qcom,channel-num", &chip->adc_channel);
+	if (!rc) {
+		if (chip->adc_channel < 0 || chip->adc_channel >= ADC_MAX_NUM) {
+			dev_err(&spmi->dev, "%s: invalid qcom,channel-num=%d specified\n",
+				__func__, chip->adc_channel);
+		} else {
+			chip->adc_type = QPNP_TM_ADC_QPNP_ADC;
+			rc = qpnp_vadc_is_ready();
+			if (rc) {
+				/* Probe retry, do not print an error message */
+				goto err_cancel_work;
+			}
+		}
+	}
+
+	if (chip->adc_type == QPNP_TM_ADC_QPNP_ADC)
+		tz_ops = &qpnp_thermal_zone_ops_qpnp_adc;
+	else
+		tz_ops = &qpnp_thermal_zone_ops_no_adc;
+
+	chip->allow_software_override
+		= of_property_read_bool(node, "qcom,allow-override");
+
+	default_temperature = DEFAULT_NO_ADC_TEMP;
+	rc = of_property_read_u32(node, "qcom,default-temp",
+					&default_temperature);
+	chip->temperature = default_temperature;
+
+	rc = qpnp_tm_read(chip, QPNP_TM_REG_TYPE, raw_type, 2);
+	if (rc) {
+		dev_err(&spmi->dev, "%s: could not read type register, rc=%d\n",
+			__func__, rc);
+		goto err_cancel_work;
+	}
+	type = raw_type[0];
+	subtype = raw_type[1];
+
+	if (type != QPNP_TM_TYPE || subtype != QPNP_TM_SUBTYPE) {
+		dev_err(&spmi->dev, "%s: invalid type=%02X or subtype=%02X register value\n",
+			__func__, type, subtype);
+		rc = -ENODEV;
+		goto err_cancel_work;
+	}
+
+	rc = qpnp_tm_init_reg(chip);
+	if (rc) {
+		dev_err(&spmi->dev, "%s: qpnp_tm_init_reg() failed, rc=%d\n",
+			__func__, rc);
+		goto err_cancel_work;
+	}
+
+	if (chip->adc_type == QPNP_TM_ADC_NONE) {
+		rc = qpnp_tm_init_temp_no_adc(chip);
+		if (rc) {
+			dev_err(&spmi->dev, "%s: qpnp_tm_init_temp_no_adc() failed, rc=%d\n",
+				__func__, rc);
+			goto err_cancel_work;
+		}
+	}
+
+	/* Start in HW control; switch to SW control when user changes mode. */
+	chip->mode = THERMAL_DEVICE_DISABLED;
+	rc = qpnp_tm_shutdown_override(chip, SOFTWARE_OVERRIDE_DISABLED);
+	if (rc) {
+		dev_err(&spmi->dev, "%s: qpnp_tm_shutdown_override() failed, rc=%d\n",
+			__func__, rc);
+		goto err_cancel_work;
+	}
+
+	chip->tz_dev = thermal_zone_device_register(tm_name, TRIP_NUM, chip,
+			tz_ops, 0, 0, 0, 0);
+	if (chip->tz_dev == NULL) {
+		dev_err(&spmi->dev, "%s: thermal_zone_device_register() failed.\n",
+			__func__);
+		rc = -ENODEV;
+		goto err_cancel_work;
+	}
+
+	rc = request_irq(chip->irq, qpnp_tm_isr, IRQF_TRIGGER_RISING, tm_name,
+			chip);
+	if (rc < 0) {
+		dev_err(&spmi->dev, "%s: request_irq(%d) failed: %d\n",
+			__func__, chip->irq, rc);
+		goto err_free_tz;
+	}
+
+	return 0;
+
+err_free_tz:
+	thermal_zone_device_unregister(chip->tz_dev);
+err_cancel_work:
+	cancel_delayed_work_sync(&chip->irq_work);
+	kfree(chip->tm_name);
+free_chip:
+	dev_set_drvdata(&spmi->dev, NULL);
+	kfree(chip);
+	return rc;
+}
+
+static int __devexit qpnp_tm_remove(struct spmi_device *spmi)
+{
+	struct qpnp_tm_chip *chip = dev_get_drvdata(&spmi->dev);
+
+	dev_set_drvdata(&spmi->dev, NULL);
+	thermal_zone_device_unregister(chip->tz_dev);
+	kfree(chip->tm_name);
+	qpnp_tm_shutdown_override(chip, SOFTWARE_OVERRIDE_DISABLED);
+	free_irq(chip->irq, chip);
+	cancel_delayed_work_sync(&chip->irq_work);
+	kfree(chip);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int qpnp_tm_suspend(struct device *dev)
+{
+	struct qpnp_tm_chip *chip = dev_get_drvdata(dev);
+
+	/* Clear override bits in suspend to allow hardware control */
+	qpnp_tm_shutdown_override(chip, SOFTWARE_OVERRIDE_DISABLED);
+
+	return 0;
+}
+
+static int qpnp_tm_resume(struct device *dev)
+{
+	struct qpnp_tm_chip *chip = dev_get_drvdata(dev);
+
+	/* Override hardware actions so software can control */
+	if (chip->mode == THERMAL_DEVICE_ENABLED)
+		qpnp_tm_shutdown_override(chip, SOFTWARE_OVERRIDE_ENABLED);
+
+	return 0;
+}
+
+static const struct dev_pm_ops qpnp_tm_pm_ops = {
+	.suspend = qpnp_tm_suspend,
+	.resume = qpnp_tm_resume,
+};
+
+#define QPNP_TM_PM_OPS	(&qpnp_tm_pm_ops)
+#else
+#define QPNP_TM_PM_OPS	NULL
+#endif
+
+static struct of_device_id qpnp_tm_match_table[] = {
+	{ .compatible = QPNP_TM_DRIVER_NAME, },
+	{}
+};
+
+static const struct spmi_device_id qpnp_tm_id[] = {
+	{ QPNP_TM_DRIVER_NAME, 0 },
+	{}
+};
+
+static struct spmi_driver qpnp_tm_driver = {
+	.driver = {
+		.name		= QPNP_TM_DRIVER_NAME,
+		.of_match_table	= qpnp_tm_match_table,
+		.owner		= THIS_MODULE,
+		.pm		= QPNP_TM_PM_OPS,
+	},
+	.probe	  = qpnp_tm_probe,
+	.remove	  = __devexit_p(qpnp_tm_remove),
+	.id_table = qpnp_tm_id,
+};
+
+int __init qpnp_tm_init(void)
+{
+	return spmi_driver_register(&qpnp_tm_driver);
+}
+
+static void __exit qpnp_tm_exit(void)
+{
+	spmi_driver_unregister(&qpnp_tm_driver);
+}
+
+module_init(qpnp_tm_init);
+module_exit(qpnp_tm_exit);
+
+MODULE_DESCRIPTION("QPNP PMIC Temperature Alarm driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/tty/serial/msm_serial_hs_hwreg.h b/drivers/tty/serial/msm_serial_hs_hwreg.h
index 81f3d54..8debc36 100644
--- a/drivers/tty/serial/msm_serial_hs_hwreg.h
+++ b/drivers/tty/serial/msm_serial_hs_hwreg.h
@@ -183,8 +183,8 @@
 
 /* Parity configuration */
 #define NO_PARITY 0x0
-#define EVEN_PARITY 0x1
-#define ODD_PARITY 0x2
+#define EVEN_PARITY 0x2
+#define ODD_PARITY 0x1
 #define SPACE_PARITY 0x3
 
 #define UARTDM_IPR_STALE_TIMEOUT_MSB_BMSK 0xffffff80
diff --git a/drivers/tty/serial/msm_serial_hs_lite.c b/drivers/tty/serial/msm_serial_hs_lite.c
index f065eaa..2f3f83d 100644
--- a/drivers/tty/serial/msm_serial_hs_lite.c
+++ b/drivers/tty/serial/msm_serial_hs_lite.c
@@ -149,12 +149,9 @@
 
 static int get_line(struct platform_device *pdev)
 {
-	const struct msm_serial_hslite_platform_data *pdata =
-					pdev->dev.platform_data;
-	if (pdata)
-		return pdata->line;
+	struct msm_hsl_port *msm_hsl_port = platform_get_drvdata(pdev);
 
-	return pdev->id;
+	return msm_hsl_port->uart.line;
 }
 
 static int clk_en(struct uart_port *port, int enable)
@@ -1357,18 +1354,32 @@
 	struct resource *uart_resource;
 	struct resource *gsbi_resource;
 	struct uart_port *port;
+	const struct msm_serial_hslite_platform_data *pdata;
 	const struct of_device_id *match;
+	u32 line;
 	int ret;
 
 	if (pdev->id == -1)
 		pdev->id = atomic_inc_return(&msm_serial_hsl_next_id) - 1;
 
-	if (unlikely(get_line(pdev) < 0 || get_line(pdev) >= UART_NR))
+	/* Use line (ttyHSLx) number from pdata or device tree if specified */
+	pdata = pdev->dev.platform_data;
+	if (pdata)
+		line = pdata->line;
+	else
+		line = pdev->id;
+
+	/* Use line number from device tree if present */
+	if (pdev->dev.of_node)
+		of_property_read_u32(pdev->dev.of_node, "cell-index", &line);
+
+	if (unlikely(line < 0 || line >= UART_NR))
 		return -ENXIO;
 
-	printk(KERN_INFO "msm_serial_hsl: detected port #%d\n", pdev->id);
+	printk(KERN_INFO "msm_serial_hsl: detected port #%d (ttyHSL%d)\n",
+	       pdev->id, line);
 
-	port = get_port_from_line(get_line(pdev));
+	port = get_port_from_line(line);
 	port->dev = &pdev->dev;
 	msm_hsl_port = UART_TO_MSM(port);
 
diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c
index c74ba7b..056ce18 100644
--- a/drivers/usb/core/usb.c
+++ b/drivers/usb/core/usb.c
@@ -451,8 +451,11 @@
 	INIT_LIST_HEAD(&dev->filelist);
 
 #ifdef	CONFIG_PM
-	pm_runtime_set_autosuspend_delay(&dev->dev,
-			usb_autosuspend_delay * 1000);
+	if (usb_hcd->driver->set_autosuspend_delay)
+		usb_hcd->driver->set_autosuspend_delay(dev);
+	else
+		pm_runtime_set_autosuspend_delay(&dev->dev,
+				usb_autosuspend_delay * 1000);
 	dev->connect_time = jiffies;
 	dev->active_duration = -jiffies;
 #endif
diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig
index 882eb97..423e104 100644
--- a/drivers/usb/dwc3/Kconfig
+++ b/drivers/usb/dwc3/Kconfig
@@ -2,8 +2,7 @@
 	tristate "DesignWare USB3 DRD Core Support"
 	depends on (USB || USB_GADGET)
 	select USB_OTG_UTILS
-	select USB_GADGET_DUALSPEED
-	select USB_XHCI_PLATFORM
+	select USB_XHCI_PLATFORM if USB_SUPPORT && USB_XHCI_HCD
 	help
 	  Say Y or M here if your system has a Dual Role SuperSpeed
 	  USB controller based on the DesignWare USB3 IP Core.
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index f517340..c0b4b57 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -99,6 +99,7 @@
 
 	ret = test_bit(id, dwc3_devs);
 	WARN(!ret, "dwc3: ID %d not in use\n", id);
+	smp_mb__before_clear_bit();
 	clear_bit(id, dwc3_devs);
 }
 EXPORT_SYMBOL_GPL(dwc3_put_device_id);
@@ -148,6 +149,8 @@
 	reg &= ~DWC3_GUSB2PHYCFG_PHYSOFTRST;
 	dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
 
+	mdelay(100);
+
 	/* After PHYs are stable we can take Core out of reset state */
 	reg = dwc3_readl(dwc->regs, DWC3_GCTL);
 	reg &= ~DWC3_GCTL_CORESOFTRESET;
@@ -255,7 +258,7 @@
  *
  * Returns 0 on success otherwise negative errno.
  */
-static int __devinit dwc3_event_buffers_setup(struct dwc3 *dwc)
+static int dwc3_event_buffers_setup(struct dwc3 *dwc)
 {
 	struct dwc3_event_buffer	*evt;
 	int				n;
@@ -266,6 +269,8 @@
 				evt->buf, (unsigned long long) evt->dma,
 				evt->length);
 
+		evt->lpos = 0;
+
 		dwc3_writel(dwc->regs, DWC3_GEVNTADRLO(n),
 				lower_32_bits(evt->dma));
 		dwc3_writel(dwc->regs, DWC3_GEVNTADRHI(n),
@@ -285,6 +290,9 @@
 
 	for (n = 0; n < dwc->num_event_buffers; n++) {
 		evt = dwc->ev_buffs[n];
+
+		evt->lpos = 0;
+
 		dwc3_writel(dwc->regs, DWC3_GEVNTADRLO(n), 0);
 		dwc3_writel(dwc->regs, DWC3_GEVNTADRHI(n), 0);
 		dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(n), 0);
@@ -292,6 +300,18 @@
 	}
 }
 
+/* XHCI reset, resets other CORE registers as well, re-init those */
+void dwc3_post_host_reset_core_init(struct dwc3 *dwc)
+{
+	/*
+	 * XHCI reset clears EVENT buffer register as well, re-init
+	 * EVENT buffers and also do device specific re-initialization
+	 */
+	dwc3_event_buffers_setup(dwc);
+
+	dwc3_gadget_restart(dwc);
+}
+
 static void __devinit dwc3_cache_hwparams(struct dwc3 *dwc)
 {
 	struct dwc3_hwparams	*parms = &dwc->hwparams;
@@ -328,8 +348,6 @@
 	}
 	dwc->revision = reg;
 
-	dwc3_core_soft_reset(dwc);
-
 	/* issue device SoftReset too */
 	timeout = jiffies + msecs_to_jiffies(500);
 	dwc3_writel(dwc->regs, DWC3_DCTL, DWC3_DCTL_CSFTRST);
@@ -347,6 +365,8 @@
 		cpu_relax();
 	} while (true);
 
+	dwc3_core_soft_reset(dwc);
+
 	dwc3_cache_hwparams(dwc);
 
 	reg = dwc3_readl(dwc->regs, DWC3_GCTL);
@@ -471,16 +491,21 @@
 		dev_err(dev, "missing IRQ\n");
 		return -ENODEV;
 	}
-	dwc->xhci_resources[1] = *res;
+	dwc->xhci_resources[1].start = res->start;
+	dwc->xhci_resources[1].end = res->end;
+	dwc->xhci_resources[1].flags = res->flags;
+	dwc->xhci_resources[1].name = res->name;
 
 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	if (!res) {
 		dev_err(dev, "missing memory resource\n");
 		return -ENODEV;
 	}
-	dwc->xhci_resources[0] = *res;
+	dwc->xhci_resources[0].start = res->start;
 	dwc->xhci_resources[0].end = dwc->xhci_resources[0].start +
 					DWC3_XHCI_REGS_END;
+	dwc->xhci_resources[0].flags = res->flags;
+	dwc->xhci_resources[0].name = res->name;
 
 	 /*
 	  * Request memory region but exclude xHCI regs,
@@ -495,7 +520,7 @@
 		return -ENOMEM;
 	}
 
-	regs = devm_ioremap(dev, res->start, resource_size(res));
+	regs = devm_ioremap_nocache(dev, res->start, resource_size(res));
 	if (!regs) {
 		dev_err(dev, "ioremap failed\n");
 		return -ENOMEM;
@@ -559,6 +584,13 @@
 			goto err1;
 		}
 
+		ret = dwc3_host_init(dwc);
+		if (ret) {
+			dev_err(dev, "failed to initialize host\n");
+			dwc3_otg_exit(dwc);
+			goto err1;
+		}
+
 		ret = dwc3_gadget_init(dwc);
 		if (ret) {
 			dev_err(dev, "failed to initialize gadget\n");
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index 92e28f5..3fb89cd 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -53,6 +53,7 @@
 #include "dwc3_otg.h"
 
 /* Global constants */
+#define DWC3_EP0_BOUNCE_SIZE	512
 #define DWC3_ENDPOINTS_NUM	32
 #define DWC3_XHCI_RESOURCES_NUM	2
 
@@ -68,6 +69,7 @@
 #define DWC3_DEVICE_EVENT_CONNECT_DONE		2
 #define DWC3_DEVICE_EVENT_LINK_STATUS_CHANGE	3
 #define DWC3_DEVICE_EVENT_WAKEUP		4
+#define DWC3_DEVICE_EVENT_HIBER_REQ		5
 #define DWC3_DEVICE_EVENT_EOPF			6
 #define DWC3_DEVICE_EVENT_SOF			7
 #define DWC3_DEVICE_EVENT_ERRATIC_ERROR		9
@@ -174,38 +176,47 @@
 #define DWC3_GCTL_PRTCAP_DEVICE	2
 #define DWC3_GCTL_PRTCAP_OTG	3
 
-#define DWC3_GCTL_CORESOFTRESET	(1 << 11)
-#define DWC3_GCTL_SCALEDOWN(n)	((n) << 4)
-#define DWC3_GCTL_SCALEDOWN_MASK DWC3_GCTL_SCALEDOWN(3)
-#define DWC3_GCTL_DISSCRAMBLE	(1 << 3)
-#define DWC3_GCTL_DSBLCLKGTNG	(1 << 0)
+#define DWC3_GCTL_CORESOFTRESET		(1 << 11)
+#define DWC3_GCTL_SCALEDOWN(n)		((n) << 4)
+#define DWC3_GCTL_SCALEDOWN_MASK	DWC3_GCTL_SCALEDOWN(3)
+#define DWC3_GCTL_DISSCRAMBLE		(1 << 3)
+#define DWC3_GCTL_GBLHIBERNATIONEN	(1 << 1)
+#define DWC3_GCTL_DSBLCLKGTNG		(1 << 0)
 
 /* Global User Control Register */
 #define DWC3_GUCTL_REFCLKPER (0x3FF << 22)
 
 /* Global USB2 PHY Configuration Register */
-#define DWC3_GUSB2PHYCFG_PHYSOFTRST (1 << 31)
-#define DWC3_GUSB2PHYCFG_SUSPHY	(1 << 6)
+#define DWC3_GUSB2PHYCFG_PHYSOFTRST	(1 << 31)
+#define DWC3_GUSB2PHYCFG_SUSPHY		(1 << 6)
 
 /* Global USB3 PIPE Control Register */
-#define DWC3_GUSB3PIPECTL_PHYSOFTRST (1 << 31)
-#define DWC3_GUSB3PIPECTL_SUSPHY (1 << 17)
-#define DWC3_GUSB3PIPECTL_DELAY_P1P2P3 (7 << 19)
-#define DWC3_GUSB3PIPECTL_DIS_RXDET_U3_RXDET (1 << 22)
+#define DWC3_GUSB3PIPECTL_PHYSOFTRST	(1 << 31)
+#define DWC3_GUSB3PIPECTL_SUSPHY	(1 << 17)
+#define DWC3_GUSB3PIPECTL_DELAY_P1P2P3	(7 << 19)
+#define DWC3_GUSB3PIPECTL_DIS_RXDET_U3_RXDET	(1 << 22)
 
 /* Global TX Fifo Size Register */
-#define DWC3_GTXFIFOSIZ_TXFDEF(n) ((n) & 0xffff)
-#define DWC3_GTXFIFOSIZ_TXFSTADDR(n) ((n) & 0xffff0000)
+#define DWC3_GTXFIFOSIZ_TXFDEF(n)	((n) & 0xffff)
+#define DWC3_GTXFIFOSIZ_TXFSTADDR(n)	((n) & 0xffff0000)
 
 /* Global HWPARAMS1 Register */
 #define DWC3_GHWPARAMS1_EN_PWROPT(n)	(((n) & (3 << 24)) >> 24)
 #define DWC3_GHWPARAMS1_EN_PWROPT_NO	0
 #define DWC3_GHWPARAMS1_EN_PWROPT_CLK	1
+#define DWC3_GHWPARAMS1_EN_PWROPT_HIB	2
+#define DWC3_GHWPARAMS1_PWROPT(n)	((n) << 24)
+#define DWC3_GHWPARAMS1_PWROPT_MASK	DWC3_GHWPARAMS1_PWROPT(3)
+
+/* Global HWPARAMS4 Register */
+#define DWC3_GHWPARAMS4_HIBER_SCRATCHBUFS(n)	(((n) & (0x0f << 13)) >> 13)
+#define DWC3_MAX_HIBER_SCRATCHBUFS		15
 
 /* Global HWPARAMS6 Register */
 #define DWC3_GHWPARAMS6_SRP_SUPPORT	(1 << 10)
 
 /* Device Configuration Register */
+#define DWC3_DCFG_LPM_CAP	(1 << 22)
 #define DWC3_DCFG_DEVADDR(addr)	((addr) << 3)
 #define DWC3_DCFG_DEVADDR_MASK	DWC3_DCFG_DEVADDR(0x7f)
 
@@ -216,24 +227,32 @@
 #define DWC3_DCFG_LOWSPEED	(2 << 0)
 #define DWC3_DCFG_FULLSPEED1	(3 << 0)
 
+#define DWC3_DCFG_LPM_CAP	(1 << 22)
+
 /* Device Control Register */
 #define DWC3_DCTL_RUN_STOP	(1 << 31)
 #define DWC3_DCTL_CSFTRST	(1 << 30)
 #define DWC3_DCTL_LSFTRST	(1 << 29)
 
 #define DWC3_DCTL_HIRD_THRES_MASK	(0x1f << 24)
-#define DWC3_DCTL_HIRD_THRES(n)	(((n) & DWC3_DCTL_HIRD_THRES_MASK) >> 24)
+#define DWC3_DCTL_HIRD_THRES(n)	((n) << 24)
 
 #define DWC3_DCTL_APPL1RES	(1 << 23)
 
-#define DWC3_DCTL_TRGTULST_MASK	(0x0f << 17)
-#define DWC3_DCTL_TRGTULST(n)	((n) << 17)
+/* These apply for core versions 1.87a and earlier */
+#define DWC3_DCTL_TRGTULST_MASK		(0x0f << 17)
+#define DWC3_DCTL_TRGTULST(n)		((n) << 17)
+#define DWC3_DCTL_TRGTULST_U2		(DWC3_DCTL_TRGTULST(2))
+#define DWC3_DCTL_TRGTULST_U3		(DWC3_DCTL_TRGTULST(3))
+#define DWC3_DCTL_TRGTULST_SS_DIS	(DWC3_DCTL_TRGTULST(4))
+#define DWC3_DCTL_TRGTULST_RX_DET	(DWC3_DCTL_TRGTULST(5))
+#define DWC3_DCTL_TRGTULST_SS_INACT	(DWC3_DCTL_TRGTULST(6))
 
-#define DWC3_DCTL_TRGTULST_U2	(DWC3_DCTL_TRGTULST(2))
-#define DWC3_DCTL_TRGTULST_U3	(DWC3_DCTL_TRGTULST(3))
-#define DWC3_DCTL_TRGTULST_SS_DIS (DWC3_DCTL_TRGTULST(4))
-#define DWC3_DCTL_TRGTULST_RX_DET (DWC3_DCTL_TRGTULST(5))
-#define DWC3_DCTL_TRGTULST_SS_INACT (DWC3_DCTL_TRGTULST(6))
+/* These apply for core versions 1.94a and later */
+#define DWC3_DCTL_KEEP_CONNECT	(1 << 19)
+#define DWC3_DCTL_L1_HIBER_EN	(1 << 18)
+#define DWC3_DCTL_CRS		(1 << 17)
+#define DWC3_DCTL_CSS		(1 << 16)
 
 #define DWC3_DCTL_INITU2ENA	(1 << 12)
 #define DWC3_DCTL_ACCEPTU2ENA	(1 << 11)
@@ -259,6 +278,7 @@
 #define DWC3_DEVTEN_ERRTICERREN		(1 << 9)
 #define DWC3_DEVTEN_SOFEN		(1 << 7)
 #define DWC3_DEVTEN_EOPFEN		(1 << 6)
+#define DWC3_DEVTEN_HIBERNATIONREQEVTEN	(1 << 5)
 #define DWC3_DEVTEN_WKUPEVTEN		(1 << 4)
 #define DWC3_DEVTEN_ULSTCNGEN		(1 << 3)
 #define DWC3_DEVTEN_CONNECTDONEEN	(1 << 2)
@@ -266,7 +286,15 @@
 #define DWC3_DEVTEN_DISCONNEVTEN	(1 << 0)
 
 /* Device Status Register */
+#define DWC3_DSTS_DCNRD			(1 << 29)
+
+/* This applies for core versions 1.87a and earlier */
 #define DWC3_DSTS_PWRUPREQ		(1 << 24)
+
+/* These apply for core versions 1.94a and later */
+#define DWC3_DSTS_RSS			(1 << 25)
+#define DWC3_DSTS_SSS			(1 << 24)
+
 #define DWC3_DSTS_COREIDLE		(1 << 23)
 #define DWC3_DSTS_DEVCTRLHLT		(1 << 22)
 
@@ -275,7 +303,7 @@
 
 #define DWC3_DSTS_RXFIFOEMPTY		(1 << 17)
 
-#define DWC3_DSTS_SOFFN_MASK		(0x3ff << 3)
+#define DWC3_DSTS_SOFFN_MASK		(0x3fff << 3)
 #define DWC3_DSTS_SOFFN(n)		(((n) & DWC3_DSTS_SOFFN_MASK) >> 3)
 
 #define DWC3_DSTS_CONNECTSPD		(7 << 0)
@@ -290,17 +318,33 @@
 #define DWC3_DGCMD_SET_LMP		0x01
 #define DWC3_DGCMD_SET_PERIODIC_PAR	0x02
 #define DWC3_DGCMD_XMIT_FUNCTION	0x03
+
+/* These apply for core versions 1.94a and later */
+#define DWC3_DGCMD_SET_SCRATCHPAD_ADDR_LO	0x04
+#define DWC3_DGCMD_SET_SCRATCHPAD_ADDR_HI	0x05
+
 #define DWC3_DGCMD_SELECTED_FIFO_FLUSH	0x09
 #define DWC3_DGCMD_ALL_FIFO_FLUSH	0x0a
 #define DWC3_DGCMD_SET_ENDPOINT_NRDY	0x0c
 #define DWC3_DGCMD_RUN_SOC_BUS_LOOPBACK	0x10
 
+#define DWC3_DGCMD_STATUS(n)		(((n) >> 15) & 1)
+#define DWC3_DGCMD_CMDACT		(1 << 10)
+#define DWC3_DGCMD_CMDIOC		(1 << 8)
+
+/* Device Generic Command Parameter Register */
+#define DWC3_DGCMDPAR_FORCE_LINKPM_ACCEPT	(1 << 0)
+#define DWC3_DGCMDPAR_FIFO_NUM(n)		((n) << 0)
+#define DWC3_DGCMDPAR_RX_FIFO			(0 << 5)
+#define DWC3_DGCMDPAR_TX_FIFO			(1 << 5)
+#define DWC3_DGCMDPAR_LOOPBACK_DIS		(0 << 0)
+#define DWC3_DGCMDPAR_LOOPBACK_ENA		(1 << 0)
+
 /* Device Endpoint Command Register */
 #define DWC3_DEPCMD_PARAM_SHIFT		16
 #define DWC3_DEPCMD_PARAM(x)		((x) << DWC3_DEPCMD_PARAM_SHIFT)
 #define DWC3_DEPCMD_GET_RSC_IDX(x)     (((x) >> DWC3_DEPCMD_PARAM_SHIFT) & 0x7f)
-#define DWC3_DEPCMD_STATUS_MASK		(0x0f << 12)
-#define DWC3_DEPCMD_STATUS(x)		(((x) & DWC3_DEPCMD_STATUS_MASK) >> 12)
+#define DWC3_DEPCMD_STATUS(x)		(((x) >> 15) & 1)
 #define DWC3_DEPCMD_HIPRI_FORCERM	(1 << 11)
 #define DWC3_DEPCMD_CMDACT		(1 << 10)
 #define DWC3_DEPCMD_CMDIOC		(1 << 8)
@@ -311,7 +355,10 @@
 #define DWC3_DEPCMD_STARTTRANSFER	(0x06 << 0)
 #define DWC3_DEPCMD_CLEARSTALL		(0x05 << 0)
 #define DWC3_DEPCMD_SETSTALL		(0x04 << 0)
+/* This applies for core versions 1.90a and earlier */
 #define DWC3_DEPCMD_GETSEQNUMBER	(0x03 << 0)
+/* This applies for core versions 1.94a and later */
+#define DWC3_DEPCMD_GETEPSTATE		(0x03 << 0)
 #define DWC3_DEPCMD_SETTRANSFRESOURCE	(0x02 << 0)
 #define DWC3_DEPCMD_SETEPCONFIG		(0x01 << 0)
 
@@ -400,7 +447,8 @@
  * @current_trb: index of current used trb
  * @number: endpoint number (1 - 15)
  * @type: set to bmAttributes & USB_ENDPOINT_XFERTYPE_MASK
- * @res_trans_idx: Resource transfer index
+ * @resource_index: Resource transfer index
+ * @current_uf: Current uf received through last event parameter
  * @interval: the intervall on which the ISOC transfer is started
  * @name: a human readable name e.g. ep1out-bulk
  * @direction: true for TX, false for RX
@@ -415,7 +463,6 @@
 	dma_addr_t		trb_pool_dma;
 	u32			free_slot;
 	u32			busy_slot;
-	const struct usb_endpoint_descriptor *desc;
 	const struct usb_ss_ep_comp_descriptor *comp_desc;
 	struct dwc3		*dwc;
 
@@ -425,6 +472,7 @@
 #define DWC3_EP_WEDGE		(1 << 2)
 #define DWC3_EP_BUSY		(1 << 4)
 #define DWC3_EP_PENDING_REQUEST	(1 << 5)
+#define DWC3_EP_MISSED_ISOC	(1 << 6)
 
 	/* This last one is specific to EP0 */
 #define DWC3_EP0_DIR_IN		(1 << 31)
@@ -433,7 +481,8 @@
 
 	u8			number;
 	u8			type;
-	u8			res_trans_idx;
+	u8			resource_index;
+	u16			current_uf;
 	u32			interval;
 
 	char			name[20];
@@ -451,7 +500,6 @@
 enum dwc3_ep0_next {
 	DWC3_EP0_UNKNOWN = 0,
 	DWC3_EP0_COMPLETE,
-	DWC3_EP0_NRDY_SETUP,
 	DWC3_EP0_NRDY_DATA,
 	DWC3_EP0_NRDY_STATUS,
 };
@@ -477,6 +525,8 @@
 	DWC3_LINK_STATE_HRESET		= 0x09,
 	DWC3_LINK_STATE_CMPLY		= 0x0a,
 	DWC3_LINK_STATE_LPBK		= 0x0b,
+	DWC3_LINK_STATE_RESET		= 0x0e,
+	DWC3_LINK_STATE_RESUME		= 0x0f,
 	DWC3_LINK_STATE_MASK		= 0x0f,
 };
 
@@ -490,11 +540,12 @@
 #define DWC3_TRB_SIZE_MASK	(0x00ffffff)
 #define DWC3_TRB_SIZE_LENGTH(n)	((n) & DWC3_TRB_SIZE_MASK)
 #define DWC3_TRB_SIZE_PCM1(n)	(((n) & 0x03) << 24)
-#define DWC3_TRB_SIZE_TRBSTS(n)	(((n) & (0x0f << 28) >> 28))
+#define DWC3_TRB_SIZE_TRBSTS(n)	(((n) & (0x0f << 28)) >> 28)
 
 #define DWC3_TRBSTS_OK			0
 #define DWC3_TRBSTS_MISSED_ISOC		1
 #define DWC3_TRBSTS_SETUP_PENDING	2
+#define DWC3_TRB_STS_XFER_IN_PROG	4
 
 /* TRB Control */
 #define DWC3_TRB_CTRL_HWO		(1 << 0)
@@ -583,6 +634,14 @@
 	unsigned		queued:1;
 };
 
+/*
+ * struct dwc3_scratchpad_array - hibernation scratchpad array
+ * (format defined by hw)
+ */
+struct dwc3_scratchpad_array {
+	__le64	dma_adr[DWC3_MAX_HIBER_SCRATCHBUFS];
+};
+
 /**
  * struct dwc3 - representation of our controller
  * @ctrl_req: usb control request which is used for ep0
@@ -615,6 +674,11 @@
  * @setup_packet_pending: true when there's a Setup Packet in FIFO. Workaround
  * @needs_fifo_resize: not all users might want fifo resizing, flag it
  * @resize_fifos: tells us it's ok to reconfigure our TxFIFO sizes.
+ * @isoch_delay: wValue from Set Isochronous Delay request;
+ * @u2sel: parameter from Set SEL request.
+ * @u2pel: parameter from Set SEL request.
+ * @u1sel: parameter from Set SEL request.
+ * @u1pel: parameter from Set SEL request.
  * @ep0_next_event: hold the next expected event
  * @ep0state: state of endpoint zero
  * @link_state: link state
@@ -660,8 +724,14 @@
 #define DWC3_REVISION_180A	0x5533180a
 #define DWC3_REVISION_183A	0x5533183a
 #define DWC3_REVISION_185A	0x5533185a
+#define DWC3_REVISION_187A	0x5533187a
 #define DWC3_REVISION_188A	0x5533188a
 #define DWC3_REVISION_190A	0x5533190a
+#define DWC3_REVISION_194A	0x5533194a
+#define DWC3_REVISION_200A	0x5533200a
+#define DWC3_REVISION_202A	0x5533202a
+#define DWC3_REVISION_210A	0x5533210a
+#define DWC3_REVISION_220A	0x5533220a
 #define DWC3_REVISION_230A	0x5533230a
 
 	unsigned		is_selfpowered:1;
@@ -679,7 +749,14 @@
 	enum dwc3_link_state	link_state;
 	enum dwc3_device_state	dev_state;
 
+	u16			isoch_delay;
+	u16			u2sel;
+	u16			u2pel;
+	u8			u1sel;
+	u8			u1pel;
+
 	u8			speed;
+
 	void			*mem;
 
 	struct dwc3_hwparams	hwparams;
@@ -752,7 +829,6 @@
 #define DEPEVT_STREAMEVT_NOTFOUND	2
 
 /* Control-only Status */
-#define DEPEVT_STATUS_CONTROL_SETUP	0
 #define DEPEVT_STATUS_CONTROL_DATA	1
 #define DEPEVT_STATUS_CONTROL_STATUS	2
 
@@ -841,6 +917,9 @@
 int dwc3_gadget_init(struct dwc3 *dwc);
 void dwc3_gadget_exit(struct dwc3 *dwc);
 
+void dwc3_gadget_restart(struct dwc3 *dwc);
+void dwc3_post_host_reset_core_init(struct dwc3 *dwc);
+
 extern int dwc3_get_device_id(void);
 extern void dwc3_put_device_id(int id);
 
diff --git a/drivers/usb/dwc3/dwc3-exynos.c b/drivers/usb/dwc3/dwc3-exynos.c
index d190301..b8f0038 100644
--- a/drivers/usb/dwc3/dwc3-exynos.c
+++ b/drivers/usb/dwc3/dwc3-exynos.c
@@ -18,7 +18,6 @@
 #include <linux/platform_device.h>
 #include <linux/platform_data/dwc3-exynos.h>
 #include <linux/dma-mapping.h>
-#include <linux/module.h>
 #include <linux/clk.h>
 
 #include "core.h"
diff --git a/drivers/usb/dwc3/dwc3-msm.c b/drivers/usb/dwc3/dwc3-msm.c
index b71bd3e..98eb0a0 100644
--- a/drivers/usb/dwc3/dwc3-msm.c
+++ b/drivers/usb/dwc3/dwc3-msm.c
@@ -17,6 +17,7 @@
 #include <linux/platform_device.h>
 #include <linux/dma-mapping.h>
 #include <linux/pm_runtime.h>
+#include <linux/ratelimit.h>
 #include <linux/interrupt.h>
 #include <linux/ioport.h>
 #include <linux/clk.h>
@@ -32,8 +33,10 @@
 #include <linux/usb/gadget.h>
 #include <linux/usb/msm_hsusb.h>
 #include <linux/regulator/consumer.h>
+#include <linux/power_supply.h>
 
 #include <mach/rpm-regulator.h>
+#include <mach/msm_xo.h>
 #include <mach/msm_bus.h>
 
 #include "dwc3_otg.h"
@@ -127,6 +130,7 @@
 	u8 ep_num_mapping[DBM_MAX_EPS];
 	const struct usb_ep_ops *original_ep_ops[DWC3_ENDPOINTS_NUM];
 	struct list_head req_complete_list;
+	struct msm_xo_voter	*xo_handle;
 	struct clk		*ref_clk;
 	struct clk		*core_clk;
 	struct clk		*iface_clk;
@@ -143,6 +147,8 @@
 	bool			resume_pending;
 	atomic_t                pm_suspended;
 	atomic_t		in_lpm;
+	int			hs_phy_irq;
+	bool			lpm_irq_seen;
 	struct delayed_work	resume_work;
 	struct wake_lock	wlock;
 	struct dwc3_charger	charger;
@@ -152,6 +158,11 @@
 	u8			dcd_retries;
 	u32			bus_perf_client;
 	struct msm_bus_scale_pdata	*bus_scale_table;
+	struct power_supply	usb_psy;
+	unsigned int		online;
+	unsigned int		host_mode;
+	unsigned int		current_max;
+	bool			vbus_active;
 };
 
 #define USB_HSPHY_3P3_VOL_MIN		3050000 /* uV */
@@ -615,7 +626,8 @@
 	params.param0 = 0; /* TDAddr High */
 	params.param1 = lower_32_bits(req->trb_dma); /* DAddr Low */
 
-	cmd = DWC3_DEPCMD_STARTTRANSFER;
+	/* DBM requires IOC to be set */
+	cmd = DWC3_DEPCMD_STARTTRANSFER | DWC3_DEPCMD_CMDIOC;
 	ret = dwc3_send_gadget_ep_cmd(dep->dwc, dep->number, cmd, &params);
 	if (ret < 0) {
 		dev_dbg(dep->dwc->dev,
@@ -1238,12 +1250,35 @@
 		return 0;
 	}
 
-	clk_disable_unprepare(mdwc->iface_clk);
-	clk_disable_unprepare(mdwc->core_clk);
+	/* Sequence to put hardware in low power state:
+	 * 1. Set OTGDISABLE to disable OTG block in HSPHY (saves power)
+	 * 2. Clear charger detection control fields
+	 * 3. SUSPEND PHY and turn OFF core clock after some delay
+	 * 4. Clear interrupt latch register and enable BSV, ID HV interrupts
+	 * 5. Enable PHY retention
+	 */
+	dwc3_msm_write_readback(mdwc->base, HS_PHY_CTRL_REG, 0x1000, 0x1000);
+	dwc3_msm_write_readback(mdwc->base, CHARGING_DET_CTRL_REG, 0x37, 0x0);
+	dwc3_msm_write_readback(mdwc->base, HS_PHY_CTRL_REG,
+						0xC00000, 0x800000);
+
+	usleep_range(1000, 1200);
 	clk_disable_unprepare(mdwc->ref_clk);
-	dwc3_hsusb_ldo_enable(0);
-	dwc3_ssusb_ldo_enable(0);
-	wake_unlock(&mdwc->wlock);
+
+	dwc3_msm_write_reg(mdwc->base, HS_PHY_IRQ_STAT_REG, 0xFFF);
+	dwc3_msm_write_readback(mdwc->base, HS_PHY_CTRL_REG, 0x18000, 0x18000);
+	dwc3_msm_write_readback(mdwc->base, HS_PHY_CTRL_REG, 0x2, 0x0);
+
+	/* make sure above writes are completed before turning off clocks */
+	wmb();
+	clk_disable_unprepare(mdwc->core_clk);
+	clk_disable_unprepare(mdwc->iface_clk);
+
+	/* USB PHY no more requires TCXO */
+	ret = msm_xo_mode_vote(mdwc->xo_handle, MSM_XO_MODE_OFF);
+	if (ret)
+		dev_err(mdwc->dev, "%s failed to devote for TCXO buffer%d\n",
+						__func__, ret);
 
 	if (mdwc->bus_perf_client) {
 		ret = msm_bus_scale_client_update_request(
@@ -1252,7 +1287,15 @@
 			dev_err(mdwc->dev, "Failed to reset bus bw vote\n");
 	}
 
+	if (mdwc->otg_xceiv && mdwc->ext_xceiv.otg_capability)
+		dwc3_hsusb_ldo_enable(0);
+
+	dwc3_ssusb_ldo_enable(0);
+	dwc3_ssusb_config_vddcx(0);
+	dwc3_hsusb_config_vddcx(0);
+	wake_unlock(&mdwc->wlock);
 	atomic_set(&mdwc->in_lpm, 1);
+
 	dev_info(mdwc->dev, "DWC3 in low power mode\n");
 
 	return 0;
@@ -1269,6 +1312,8 @@
 		return 0;
 	}
 
+	wake_lock(&mdwc->wlock);
+
 	if (mdwc->bus_perf_client) {
 		ret = msm_bus_scale_client_update_request(
 						mdwc->bus_perf_client, 1);
@@ -1276,14 +1321,47 @@
 			dev_err(mdwc->dev, "Failed to vote for bus scaling\n");
 	}
 
-	wake_lock(&mdwc->wlock);
-	clk_prepare_enable(mdwc->ref_clk);
-	clk_prepare_enable(mdwc->core_clk);
-	clk_prepare_enable(mdwc->iface_clk);
-	dwc3_hsusb_ldo_enable(1);
+	/* Vote for TCXO while waking up USB HSPHY */
+	ret = msm_xo_mode_vote(mdwc->xo_handle, MSM_XO_MODE_ON);
+	if (ret)
+		dev_err(mdwc->dev, "%s failed to vote for TCXO buffer%d\n",
+						__func__, ret);
+
+	if (mdwc->otg_xceiv && mdwc->ext_xceiv.otg_capability)
+		dwc3_hsusb_ldo_enable(1);
+
 	dwc3_ssusb_ldo_enable(1);
+	dwc3_ssusb_config_vddcx(1);
+	dwc3_hsusb_config_vddcx(1);
+	clk_prepare_enable(mdwc->ref_clk);
+	usleep_range(1000, 1200);
+
+	clk_prepare_enable(mdwc->iface_clk);
+	clk_prepare_enable(mdwc->core_clk);
+
+	/* Disable HV interrupt */
+	dwc3_msm_write_readback(mdwc->base, HS_PHY_CTRL_REG, 0x18000, 0x0);
+	/* Disable Retention */
+	dwc3_msm_write_readback(mdwc->base, HS_PHY_CTRL_REG, 0x2, 0x2);
+
+	dwc3_msm_write_reg(mdwc->base, DWC3_GUSB2PHYCFG(0),
+	      dwc3_msm_read_reg(mdwc->base, DWC3_GUSB2PHYCFG(0)) | 0xF0000000);
+	/* 20usec delay required before de-asserting PHY RESET */
+	udelay(20);
+	dwc3_msm_write_reg(mdwc->base, DWC3_GUSB2PHYCFG(0),
+	      dwc3_msm_read_reg(mdwc->base, DWC3_GUSB2PHYCFG(0)) & 0x7FFFFFFF);
+
+	/* Bring PHY out of suspend */
+	dwc3_msm_write_readback(mdwc->base, HS_PHY_CTRL_REG, 0xC00000, 0x0);
 
 	atomic_set(&mdwc->in_lpm, 0);
+
+	/* match disable_irq call from isr */
+	if (mdwc->lpm_irq_seen && mdwc->hs_phy_irq) {
+		enable_irq(mdwc->hs_phy_irq);
+		mdwc->lpm_irq_seen = false;
+	}
+
 	dev_info(mdwc->dev, "DWC3 exited from low power mode\n");
 
 	return 0;
@@ -1313,10 +1391,13 @@
 			mdwc->ext_xceiv.notify_ext_events(mdwc->otg_xceiv->otg,
 							DWC3_EVENT_PHY_RESUME);
 		pm_runtime_put_sync(mdwc->dev);
+		if (mdwc->otg_xceiv && (mdwc->ext_xceiv.otg_capability))
+			mdwc->ext_xceiv.notify_ext_events(mdwc->otg_xceiv->otg,
+							DWC3_EVENT_XCEIV_STATE);
 	}
 }
 
-static bool debug_id, debug_bsv, debug_connect;
+static u32 debug_id, debug_bsv, debug_connect;
 
 static int dwc3_connect_show(struct seq_file *s, void *unused)
 {
@@ -1386,11 +1467,11 @@
 		return;
 
 	if (!debugfs_create_bool("id", S_IRUGO | S_IWUSR, dwc3_debugfs_root,
-				 (u32 *)&debug_id))
+				 &debug_id))
 		goto error;
 
 	if (!debugfs_create_bool("bsv", S_IRUGO | S_IWUSR, dwc3_debugfs_root,
-				 (u32 *)&debug_bsv))
+				 &debug_bsv))
 		goto error;
 
 	if (!debugfs_create_file("connect", S_IRUGO | S_IWUSR,
@@ -1403,6 +1484,106 @@
 	debugfs_remove_recursive(dwc3_debugfs_root);
 }
 
+static irqreturn_t msm_dwc3_irq(int irq, void *data)
+{
+	struct dwc3_msm *mdwc = data;
+
+	if (atomic_read(&mdwc->in_lpm)) {
+		dev_dbg(mdwc->dev, "%s received in LPM\n", __func__);
+		mdwc->lpm_irq_seen = true;
+		disable_irq_nosync(irq);
+		queue_delayed_work(system_nrt_wq, &mdwc->resume_work, 0);
+	} else {
+		pr_info_ratelimited("%s: IRQ outside LPM\n", __func__);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static int dwc3_msm_power_get_property_usb(struct power_supply *psy,
+				  enum power_supply_property psp,
+				  union power_supply_propval *val)
+{
+	struct dwc3_msm *mdwc = container_of(psy, struct dwc3_msm,
+								usb_psy);
+	switch (psp) {
+	case POWER_SUPPLY_PROP_SCOPE:
+		val->intval = mdwc->host_mode;
+		break;
+	case POWER_SUPPLY_PROP_CURRENT_MAX:
+		val->intval = mdwc->current_max;
+		break;
+	case POWER_SUPPLY_PROP_PRESENT:
+		val->intval = mdwc->vbus_active;
+		break;
+	case POWER_SUPPLY_PROP_ONLINE:
+		val->intval = mdwc->online;
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int dwc3_msm_power_set_property_usb(struct power_supply *psy,
+				  enum power_supply_property psp,
+				  const union power_supply_propval *val)
+{
+	static bool init;
+	struct dwc3_msm *mdwc = container_of(psy, struct dwc3_msm,
+								usb_psy);
+
+	switch (psp) {
+	case POWER_SUPPLY_PROP_SCOPE:
+		mdwc->host_mode = val->intval;
+		break;
+	/* Process PMIC notification in PRESENT prop */
+	case POWER_SUPPLY_PROP_PRESENT:
+		dev_dbg(mdwc->dev, "%s: notify xceiv event\n", __func__);
+		if (mdwc->otg_xceiv && (mdwc->ext_xceiv.otg_capability ||
+							!init)) {
+			mdwc->ext_xceiv.bsv = val->intval;
+			mdwc->ext_xceiv.id = DWC3_ID_FLOAT;
+			if (atomic_read(&mdwc->in_lpm)) {
+				dev_dbg(mdwc->dev,
+					"%s received in LPM\n", __func__);
+				queue_delayed_work(system_nrt_wq,
+							&mdwc->resume_work, 0);
+			} else {
+				mdwc->ext_xceiv.notify_ext_events(
+							mdwc->otg_xceiv->otg,
+							DWC3_EVENT_XCEIV_STATE);
+			}
+		}
+		if (!init)
+			init = true;
+		mdwc->vbus_active = val->intval;
+		break;
+	case POWER_SUPPLY_PROP_ONLINE:
+		mdwc->online = val->intval;
+		break;
+	case POWER_SUPPLY_PROP_CURRENT_MAX:
+		mdwc->current_max = val->intval;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	power_supply_changed(&mdwc->usb_psy);
+	return 0;
+}
+
+static char *dwc3_msm_pm_power_supplied_to[] = {
+	"battery",
+};
+
+static enum power_supply_property dwc3_msm_pm_power_props_usb[] = {
+	POWER_SUPPLY_PROP_PRESENT,
+	POWER_SUPPLY_PROP_ONLINE,
+	POWER_SUPPLY_PROP_CURRENT_MAX,
+	POWER_SUPPLY_PROP_SCOPE,
+};
+
 static int __devinit dwc3_msm_probe(struct platform_device *pdev)
 {
 	struct device_node *node = pdev->dev.of_node;
@@ -1426,6 +1607,20 @@
 	INIT_DELAYED_WORK(&msm->chg_work, dwc3_chg_detect_work);
 	INIT_DELAYED_WORK(&msm->resume_work, dwc3_resume_work);
 
+	msm->xo_handle = msm_xo_get(MSM_XO_TCXO_D0, "usb");
+	if (IS_ERR(msm->xo_handle)) {
+		dev_err(&pdev->dev, "%s unable to get TCXO buffer handle\n",
+								__func__);
+		return PTR_ERR(msm->xo_handle);
+	}
+
+	ret = msm_xo_mode_vote(msm->xo_handle, MSM_XO_MODE_ON);
+	if (ret) {
+		dev_err(&pdev->dev, "%s failed to vote for TCXO buffer%d\n",
+						__func__, ret);
+		goto free_xo_handle;
+	}
+
 	/*
 	 * DWC3 Core requires its CORE CLK (aka master / bus clk) to
 	 * run at 125Mhz in SSUSB mode and >60MHZ for HSUSB mode.
@@ -1433,7 +1628,8 @@
 	msm->core_clk = devm_clk_get(&pdev->dev, "core_clk");
 	if (IS_ERR(msm->core_clk)) {
 		dev_err(&pdev->dev, "failed to get core_clk\n");
-		return PTR_ERR(msm->core_clk);
+		ret = PTR_ERR(msm->core_clk);
+		goto free_xo_handle;
 	}
 	clk_set_rate(msm->core_clk, 125000000);
 	clk_prepare_enable(msm->core_clk);
@@ -1548,6 +1744,26 @@
 		goto free_hs_ldo_init;
 	}
 
+	msm->ext_xceiv.otg_capability = of_property_read_bool(node,
+				"qcom,dwc-usb3-msm-otg-capability");
+
+	if (!msm->ext_xceiv.otg_capability) {
+		/* DWC3 has separate IRQ line for OTG events (ID/BSV etc.) */
+		msm->hs_phy_irq = platform_get_irq_byname(pdev, "hs_phy_irq");
+		if (msm->hs_phy_irq < 0) {
+			dev_dbg(&pdev->dev, "pget_irq for hs_phy_irq failed\n");
+			msm->hs_phy_irq = 0;
+		} else {
+			ret = request_irq(msm->hs_phy_irq, msm_dwc3_irq,
+					IRQF_TRIGGER_RISING, "msm_dwc3", msm);
+			if (ret) {
+				dev_err(&pdev->dev, "irqreq HSPHYINT failed\n");
+				goto disable_hs_ldo;
+			}
+			enable_irq_wake(msm->hs_phy_irq);
+		}
+	}
+
 	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
 	if (!res) {
 		dev_dbg(&pdev->dev, "missing TCSR memory resource\n");
@@ -1571,7 +1787,7 @@
 	if (!res) {
 		dev_err(&pdev->dev, "missing memory base resource\n");
 		ret = -ENODEV;
-		goto disable_hs_ldo;
+		goto free_hsphy_irq;
 	}
 
 	msm->base = devm_ioremap_nocache(&pdev->dev, res->start,
@@ -1579,14 +1795,14 @@
 	if (!msm->base) {
 		dev_err(&pdev->dev, "ioremap failed\n");
 		ret = -ENODEV;
-		goto disable_hs_ldo;
+		goto free_hsphy_irq;
 	}
 
 	dwc3 = platform_device_alloc("dwc3", -1);
 	if (!dwc3) {
 		dev_err(&pdev->dev, "couldn't allocate dwc3 device\n");
 		ret = -ENODEV;
-		goto disable_hs_ldo;
+		goto free_hsphy_irq;
 	}
 
 	dwc3->dev.parent = &pdev->dev;
@@ -1614,10 +1830,11 @@
 	 */
 	dwc3_msm_write_reg(msm->base, HS_PHY_CTRL_REG, 0x5220bb2);
 	usleep_range(2000, 2200);
-	/* Disable (bypass) VBUS filter */
-	dwc3_msm_write_reg(msm->base, QSCRATCH_GENERAL_CFG, 0x38);
+	/* Disable (bypass) VBUS and ID filters */
+	dwc3_msm_write_reg(msm->base, QSCRATCH_GENERAL_CFG, 0x78);
 
 	pm_runtime_set_active(msm->dev);
+	pm_runtime_enable(msm->dev);
 
 	if (of_property_read_u32(node, "qcom,dwc-usb3-msm-dbm-eps",
 				 &msm->dbm_num_eps)) {
@@ -1635,17 +1852,35 @@
 		goto put_pdev;
 	}
 
+	msm->usb_psy.name = "usb";
+	msm->usb_psy.type = POWER_SUPPLY_TYPE_USB;
+	msm->usb_psy.supplied_to = dwc3_msm_pm_power_supplied_to;
+	msm->usb_psy.num_supplicants = ARRAY_SIZE(
+					dwc3_msm_pm_power_supplied_to);
+	msm->usb_psy.properties = dwc3_msm_pm_power_props_usb;
+	msm->usb_psy.num_properties = ARRAY_SIZE(dwc3_msm_pm_power_props_usb);
+	msm->usb_psy.get_property = dwc3_msm_power_get_property_usb;
+	msm->usb_psy.set_property = dwc3_msm_power_set_property_usb;
+
+	ret = power_supply_register(&pdev->dev, &msm->usb_psy);
+	if (ret < 0) {
+		dev_err(&pdev->dev,
+				"%s:power_supply_register usb failed\n",
+					__func__);
+		goto put_pdev;
+	}
+
 	ret = platform_device_add_resources(dwc3, pdev->resource,
 		pdev->num_resources);
 	if (ret) {
 		dev_err(&pdev->dev, "couldn't add resources to dwc3 device\n");
-		goto put_pdev;
+		goto put_psupply;
 	}
 
 	ret = platform_device_add(dwc3);
 	if (ret) {
 		dev_err(&pdev->dev, "failed to register dwc3 device\n");
-		goto put_pdev;
+		goto put_psupply;
 	}
 
 	msm->bus_scale_table = msm_bus_cl_get_pdata(pdev);
@@ -1665,8 +1900,9 @@
 	usleep_range(1000, 1200);
 	dwc3_msm_dbm_soft_reset(0);
 
-	dwc3_msm_event_buffer_config(dwc3_readl(msm->base, DWC3_GEVNTADRLO(0)),
-		dwc3_readl(msm->base, DWC3_GEVNTSIZ(0)));
+	dwc3_msm_event_buffer_config(dwc3_msm_read_reg(msm->base,
+							DWC3_GEVNTADRLO(0)),
+				dwc3_msm_read_reg(msm->base, DWC3_GEVNTSIZ(0)));
 
 	msm->otg_xceiv = usb_get_transceiver();
 	if (msm->otg_xceiv) {
@@ -1697,8 +1933,13 @@
 put_xcvr:
 	usb_put_transceiver(msm->otg_xceiv);
 	platform_device_del(dwc3);
+put_psupply:
+	power_supply_unregister(&msm->usb_psy);
 put_pdev:
 	platform_device_put(dwc3);
+free_hsphy_irq:
+	if (msm->hs_phy_irq)
+		free_irq(msm->hs_phy_irq, msm);
 disable_hs_ldo:
 	dwc3_hsusb_ldo_enable(0);
 free_hs_ldo_init:
@@ -1725,6 +1966,8 @@
 	clk_disable_unprepare(msm->iface_clk);
 disable_core_clk:
 	clk_disable_unprepare(msm->core_clk);
+free_xo_handle:
+	msm_xo_put(msm->xo_handle);
 
 	return ret;
 }
@@ -1756,6 +1999,7 @@
 	clk_disable_unprepare(msm->sleep_clk);
 	clk_disable_unprepare(msm->hsphy_sleep_clk);
 	clk_disable_unprepare(msm->ref_clk);
+	msm_xo_put(msm->xo_handle);
 
 	return 0;
 }
@@ -1792,9 +2036,14 @@
 		pm_runtime_enable(dev);
 
 		/* Let OTG know about resume event and update pm_count */
-		if (mdwc->otg_xceiv)
+		if (mdwc->otg_xceiv) {
 			mdwc->ext_xceiv.notify_ext_events(mdwc->otg_xceiv->otg,
 							DWC3_EVENT_PHY_RESUME);
+			if (mdwc->ext_xceiv.otg_capability)
+				mdwc->ext_xceiv.notify_ext_events(
+							mdwc->otg_xceiv->otg,
+							DWC3_EVENT_XCEIV_STATE);
+		}
 	}
 
 	return ret;
@@ -1849,7 +2098,7 @@
 	},
 };
 
-MODULE_LICENSE("GPLV2");
+MODULE_LICENSE("GPL v2");
 MODULE_DESCRIPTION("DesignWare USB3 MSM Glue Layer");
 
 static int __devinit dwc3_msm_init(void)
diff --git a/drivers/usb/dwc3/dwc3_otg.c b/drivers/usb/dwc3/dwc3_otg.c
index 4a37f03..1aa8519 100644
--- a/drivers/usb/dwc3/dwc3_otg.c
+++ b/drivers/usb/dwc3/dwc3_otg.c
@@ -16,12 +16,17 @@
 #include <linux/usb.h>
 #include <linux/usb/hcd.h>
 #include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
 
 #include "core.h"
 #include "dwc3_otg.h"
 #include "io.h"
 #include "xhci.h"
 
+static void dwc3_otg_reset(struct dwc3_otg *dotg);
+
+static void dwc3_otg_notify_host_mode(struct usb_otg *otg, int host_mode);
+static void dwc3_otg_reset(struct dwc3_otg *dotg);
 
 /**
  * dwc3_otg_set_host_regs - reset dwc3 otg registers to host operation.
@@ -29,7 +34,7 @@
  * This function sets the OTG registers to work in A-Device host mode.
  * This function should be called just before entering to A-Device mode.
  *
- * @w: Pointer to the dwc3 otg workqueue.
+ * @w: Pointer to the dwc3 otg struct
  */
 static void dwc3_otg_set_host_regs(struct dwc3_otg *dotg)
 {
@@ -39,11 +44,26 @@
 	octl = dwc3_readl(dotg->regs, DWC3_OCTL);
 	octl &= ~DWC3_OTG_OCTL_PERIMODE;
 	dwc3_writel(dotg->regs, DWC3_OCTL, octl);
+}
 
-	/*
-	 * TODO: add more OTG registers writes for HOST mode here,
-	 * see figure 12-10 A-device flow in dwc3 Synopsis spec
-	 */
+/**
+ * dwc3_otg_set_host_power - Enable port power control for host operation
+ *
+ * This function enables the OTG Port Power required to operate in Host mode
+ * This function should be called only after XHCI driver has set the port
+ * power in PORTSC register.
+ *
+ * @w: Pointer to the dwc3 otg struct
+ */
+void dwc3_otg_set_host_power(struct dwc3_otg *dotg)
+{
+	u32 osts;
+
+	osts = dwc3_readl(dotg->regs, DWC3_OSTS);
+	if (!(osts & 0x8))
+		dev_err(dotg->dwc->dev, "%s: xHCIPrtPower not set\n", __func__);
+
+	dwc3_writel(dotg->regs, DWC3_OCTL, DWC3_OTG_OCTL_PRTPWRCTL);
 }
 
 /**
@@ -80,19 +100,13 @@
 static int dwc3_otg_start_host(struct usb_otg *otg, int on)
 {
 	struct dwc3_otg *dotg = container_of(otg, struct dwc3_otg, otg);
-	struct usb_hcd *hcd;
-	struct xhci_hcd *xhci;
 	int ret = 0;
 
-	if (!otg->host)
+	if (!dotg->dwc->xhci)
 		return -EINVAL;
 
-	hcd = bus_to_hcd(otg->host);
-	xhci = hcd_to_xhci(hcd);
 	if (on) {
-		dev_dbg(otg->phy->dev, "%s: turn on host %s\n",
-					__func__, otg->host->bus_name);
-		dwc3_otg_set_host_regs(dotg);
+		dev_dbg(otg->phy->dev, "%s: turn on host\n", __func__);
 
 		/*
 		 * This should be revisited for more testing post-silicon.
@@ -104,25 +118,40 @@
 		 * remove_hcd, But we may not use standard set_host method
 		 * anymore.
 		 */
-		ret = hcd->driver->start(hcd);
+		dwc3_otg_set_host_regs(dotg);
+		ret = platform_device_add(dotg->dwc->xhci);
 		if (ret) {
 			dev_err(otg->phy->dev,
-				"%s: failed to start primary hcd, ret=%d\n",
+				"%s: failed to add XHCI pdev ret=%d\n",
 				__func__, ret);
 			return ret;
 		}
 
-		ret = xhci->shared_hcd->driver->start(xhci->shared_hcd);
+		dwc3_otg_notify_host_mode(otg, on);
+		ret = regulator_enable(dotg->vbus_otg);
 		if (ret) {
-			dev_err(otg->phy->dev,
-				"%s: failed to start secondary hcd, ret=%d\n",
-				__func__, ret);
+			dev_err(otg->phy->dev, "unable to enable vbus_otg\n");
+			platform_device_del(dotg->dwc->xhci);
 			return ret;
 		}
+
+		/* re-init OTG EVTEN register as XHCI reset clears it */
+		dwc3_otg_reset(dotg);
 	} else {
-		dev_dbg(otg->phy->dev, "%s: turn off host %s\n",
-					__func__, otg->host->bus_name);
-		hcd->driver->stop(hcd);
+		dev_dbg(otg->phy->dev, "%s: turn off host\n", __func__);
+
+		platform_device_del(dotg->dwc->xhci);
+
+		ret = regulator_disable(dotg->vbus_otg);
+		if (ret) {
+			dev_err(otg->phy->dev, "unable to disable vbus_otg\n");
+			return ret;
+		}
+		dwc3_otg_notify_host_mode(otg, on);
+
+		/* re-init core and OTG register as XHCI reset clears it */
+		dwc3_post_host_reset_core_init(dotg->dwc);
+		dwc3_otg_reset(dotg);
 	}
 
 	return 0;
@@ -141,26 +170,18 @@
 	struct dwc3_otg *dotg = container_of(otg, struct dwc3_otg, otg);
 
 	if (host) {
-		dev_dbg(otg->phy->dev, "%s: set host %s\n",
+		dev_dbg(otg->phy->dev, "%s: set host %s, portpower\n",
 					__func__, host->bus_name);
 		otg->host = host;
-
 		/*
-		 * Only after both peripheral and host are set then check
-		 * OTG sm. This prevents unnecessary activation of the sm
-		 * in case the ID is high.
+		 * Though XHCI power would be set by now, but some delay is
+		 * required for XHCI controller before setting OTG Port Power
+		 * TODO: Tune this delay
 		 */
-		if (otg->gadget)
-			schedule_work(&dotg->sm_work);
+		msleep(300);
+		dwc3_otg_set_host_power(dotg);
 	} else {
-		if (otg->phy->state == OTG_STATE_A_HOST) {
-			dwc3_otg_start_host(otg, 0);
-			otg->host = NULL;
-			otg->phy->state = OTG_STATE_UNDEFINED;
-			schedule_work(&dotg->sm_work);
-		} else {
-			otg->host = NULL;
-		}
+		otg->host = NULL;
 	}
 
 	return 0;
@@ -212,14 +233,7 @@
 		dev_dbg(otg->phy->dev, "%s: set gadget %s\n",
 					__func__, gadget->name);
 		otg->gadget = gadget;
-
-		/*
-		 * Only after both peripheral and host are set then check
-		 * OTG sm. This prevents unnecessary activation of the sm
-		 * in case the ID is grounded.
-		 */
-		if (otg->host)
-			schedule_work(&dotg->sm_work);
+		schedule_work(&dotg->sm_work);
 	} else {
 		if (otg->phy->state == OTG_STATE_B_PERIPHERAL) {
 			dwc3_otg_start_peripheral(otg, 0);
@@ -281,9 +295,11 @@
 static void dwc3_ext_event_notify(struct usb_otg *otg,
 					enum dwc3_ext_events event)
 {
+	static bool init;
 	struct dwc3_otg *dotg = container_of(otg, struct dwc3_otg, otg);
 	struct dwc3_ext_xceiv *ext_xceiv = dotg->ext_xceiv;
 	struct usb_phy *phy = dotg->otg.phy;
+	int ret = 0;
 
 	if (event == DWC3_EVENT_PHY_RESUME) {
 		if (!pm_runtime_status_suspended(phy->dev)) {
@@ -291,21 +307,39 @@
 		} else {
 			dev_dbg(phy->dev, "ext PHY_RESUME event received\n");
 			/* ext_xceiver would have taken h/w out of LPM by now */
-			pm_runtime_get(phy->dev);
+			ret = pm_runtime_get(phy->dev);
+			if (ret == -EACCES) {
+				/* pm_runtime_get may fail during system
+				   resume with -EACCES error */
+				pm_runtime_disable(phy->dev);
+				pm_runtime_set_active(phy->dev);
+				pm_runtime_enable(phy->dev);
+			} else if (ret < 0) {
+				dev_warn(phy->dev, "pm_runtime_get failed!\n");
+			}
 		}
+	} else if (event == DWC3_EVENT_XCEIV_STATE) {
+		if (ext_xceiv->id == DWC3_ID_FLOAT)
+			set_bit(ID, &dotg->inputs);
+		else
+			clear_bit(ID, &dotg->inputs);
+
+		if (ext_xceiv->bsv) {
+			dev_dbg(phy->dev, "XCVR: BSV set\n");
+			set_bit(B_SESS_VLD, &dotg->inputs);
+		} else {
+			dev_dbg(phy->dev, "XCVR: BSV clear\n");
+			clear_bit(B_SESS_VLD, &dotg->inputs);
+		}
+
+		if (!init) {
+			init = true;
+			complete(&dotg->dwc3_xcvr_vbus_init);
+			dev_dbg(phy->dev, "XCVR: BSV init complete\n");
+			return;
+		}
+		schedule_work(&dotg->sm_work);
 	}
-
-	if (ext_xceiv->id == DWC3_ID_FLOAT)
-		set_bit(ID, &dotg->inputs);
-	else
-		clear_bit(ID, &dotg->inputs);
-
-	if (ext_xceiv->bsv)
-		set_bit(B_SESS_VLD, &dotg->inputs);
-	else
-		clear_bit(B_SESS_VLD, &dotg->inputs);
-
-	schedule_work(&dotg->sm_work);
 }
 
 /**
@@ -326,6 +360,72 @@
 	return 0;
 }
 
+static void dwc3_otg_notify_host_mode(struct usb_otg *otg, int host_mode)
+{
+	struct dwc3_otg *dotg = container_of(otg, struct dwc3_otg, otg);
+
+	if (!dotg->psy) {
+		dev_err(otg->phy->dev, "no usb power supply registered\n");
+		return;
+	}
+
+	if (host_mode)
+		power_supply_set_scope(dotg->psy, POWER_SUPPLY_SCOPE_SYSTEM);
+	else
+		power_supply_set_scope(dotg->psy, POWER_SUPPLY_SCOPE_DEVICE);
+}
+
+static int dwc3_otg_set_power(struct usb_phy *phy, unsigned mA)
+{
+	static int power_supply_type;
+	struct dwc3_otg *dotg = container_of(phy->otg, struct dwc3_otg, otg);
+
+
+	if (!dotg->psy) {
+		dev_err(phy->dev, "no usb power supply registered\n");
+		return 0;
+	}
+
+	if (dotg->charger->chg_type == DWC3_SDP_CHARGER)
+		power_supply_type = POWER_SUPPLY_TYPE_USB;
+	else if (dotg->charger->chg_type == DWC3_CDP_CHARGER)
+		power_supply_type = POWER_SUPPLY_TYPE_USB_CDP;
+	else if (dotg->charger->chg_type == DWC3_DCP_CHARGER)
+		power_supply_type = POWER_SUPPLY_TYPE_USB_DCP;
+	else
+		power_supply_type = POWER_SUPPLY_TYPE_BATTERY;
+
+	power_supply_set_supply_type(dotg->psy, power_supply_type);
+
+	if (dotg->charger->max_power == mA)
+		return 0;
+
+	dev_info(phy->dev, "Avail curr from USB = %u\n", mA);
+
+	if (dotg->charger->max_power <= 2 && mA > 2) {
+		/* Enable charging */
+		if (power_supply_set_online(dotg->psy, true))
+			goto psy_error;
+		if (power_supply_set_current_limit(dotg->psy, 1000*mA))
+			goto psy_error;
+	} else if (dotg->charger->max_power > 0 && (mA == 0 || mA == 2)) {
+		/* Disable charging */
+		if (power_supply_set_online(dotg->psy, false))
+			goto psy_error;
+		/* Set max current limit */
+		if (power_supply_set_current_limit(dotg->psy, 0))
+			goto psy_error;
+	}
+
+	power_supply_changed(dotg->psy);
+	dotg->charger->max_power = mA;
+	return 0;
+
+psy_error:
+	dev_dbg(phy->dev, "power supply error when setting property\n");
+	return -ENXIO;
+}
+
 /* IRQs which OTG driver is interested in handling */
 #define DWC3_OEVT_MASK		(DWC3_OEVTEN_OTGCONIDSTSCHNGEVNT | \
 				 DWC3_OEVTEN_OTGBDEVVBUSCHNGEVNT)
@@ -339,21 +439,10 @@
 static irqreturn_t dwc3_otg_interrupt(int irq, void *_dotg)
 {
 	struct dwc3_otg *dotg = (struct dwc3_otg *)_dotg;
-	struct usb_phy *phy = dotg->otg.phy;
 	u32 osts, oevt_reg;
 	int ret = IRQ_NONE;
 	int handled_irqs = 0;
 
-	/*
-	 * If PHY is in retention mode then this interrupt would not be fired.
-	 * ext_xcvr (parent) is responsible for bringing h/w out of LPM.
-	 * OTG driver just need to increment power count and can safely
-	 * assume that h/w is out of low power state now.
-	 * TODO: explicitly disable OEVTEN interrupts if ext_xceiv is present
-	 */
-	if (pm_runtime_status_suspended(phy->dev))
-		pm_runtime_get(phy->dev);
-
 	oevt_reg = dwc3_readl(dotg->regs, DWC3_OEVT);
 
 	if (!(oevt_reg & DWC3_OEVT_MASK))
@@ -403,23 +492,32 @@
 {
 	u32 osts = dwc3_readl(dotg->regs, DWC3_OSTS);
 	struct usb_phy *phy = dotg->otg.phy;
-
-	/*
-	 * TODO: If using external notifications then wait here till initial
-	 * state is reported
-	 */
+	struct dwc3_ext_xceiv *ext_xceiv;
+	int ret;
 
 	dev_dbg(phy->dev, "Initialize OTG inputs, osts: 0x%x\n", osts);
 
-	if (osts & DWC3_OTG_OSTS_CONIDSTS)
-		set_bit(ID, &dotg->inputs);
-	else
-		clear_bit(ID, &dotg->inputs);
+	/*
+	 * VBUS initial state is reported after PMIC
+	 * driver initialization. Wait for it.
+	 */
+	ret = wait_for_completion_timeout(&dotg->dwc3_xcvr_vbus_init, HZ * 5);
+	if (!ret)
+		dev_err(phy->dev, "%s: completion timeout\n", __func__);
 
-	if (osts & DWC3_OTG_OSTS_BSESVALID)
-		set_bit(B_SESS_VLD, &dotg->inputs);
-	else
-		clear_bit(B_SESS_VLD, &dotg->inputs);
+	ext_xceiv = dotg->ext_xceiv;
+	dwc3_otg_reset(dotg);
+	if (ext_xceiv && !ext_xceiv->otg_capability) {
+		if (osts & DWC3_OTG_OSTS_CONIDSTS)
+			set_bit(ID, &dotg->inputs);
+		else
+			clear_bit(ID, &dotg->inputs);
+
+		if (osts & DWC3_OTG_OSTS_BSESVALID)
+			set_bit(B_SESS_VLD, &dotg->inputs);
+		else
+			clear_bit(B_SESS_VLD, &dotg->inputs);
+	}
 }
 
 /**
@@ -444,8 +542,16 @@
 	switch (phy->state) {
 	case OTG_STATE_UNDEFINED:
 		dwc3_otg_init_sm(dotg);
+		if (!dotg->psy) {
+			dotg->psy = power_supply_get_by_name("usb");
+
+			if (!dotg->psy)
+				dev_err(phy->dev,
+					 "couldn't get usb power supply\n");
+		}
+
 		/* Switch to A or B-Device according to ID / BSV */
-		if (!test_bit(ID, &dotg->inputs) && phy->otg->host) {
+		if (!test_bit(ID, &dotg->inputs)) {
 			dev_dbg(phy->dev, "!id\n");
 			phy->state = OTG_STATE_A_IDLE;
 			work = 1;
@@ -461,7 +567,7 @@
 		break;
 
 	case OTG_STATE_B_IDLE:
-		if (!test_bit(ID, &dotg->inputs) && phy->otg->host) {
+		if (!test_bit(ID, &dotg->inputs)) {
 			dev_dbg(phy->dev, "!id\n");
 			phy->state = OTG_STATE_A_IDLE;
 			work = 1;
@@ -480,9 +586,13 @@
 				switch (charger->chg_type) {
 				case DWC3_DCP_CHARGER:
 					dev_dbg(phy->dev, "lpm, DCP charger\n");
+					dwc3_otg_set_power(phy,
+							DWC3_IDEV_CHG_MAX);
 					pm_runtime_put_sync(phy->dev);
 					break;
 				case DWC3_CDP_CHARGER:
+					dwc3_otg_set_power(phy,
+							DWC3_IDEV_CHG_MAX);
 					dwc3_otg_start_peripheral(&dotg->otg,
 									1);
 					phy->state = OTG_STATE_B_PERIPHERAL;
@@ -523,6 +633,7 @@
 					charger->chg_type =
 							DWC3_INVALID_CHARGER;
 			}
+			dwc3_otg_set_power(phy, 0);
 			dev_dbg(phy->dev, "No device, trying to suspend\n");
 			pm_runtime_put_sync(phy->dev);
 		}
@@ -588,6 +699,9 @@
  */
 static void dwc3_otg_reset(struct dwc3_otg *dotg)
 {
+	static int once;
+	struct dwc3_ext_xceiv *ext_xceiv = dotg->ext_xceiv;
+
 	/*
 	 * OCFG[2] - OTG-Version = 1
 	 * OCFG[1] - HNPCap = 0
@@ -604,15 +718,19 @@
 	 * OCTL[1] - DevSetHNPEn = 0
 	 * OCTL[0] - HstSetHNPEn = 0
 	 */
-	dwc3_writel(dotg->regs, DWC3_OCTL, 0x40);
+	if (!once) {
+		dwc3_writel(dotg->regs, DWC3_OCTL, 0x40);
+		once++;
+	}
 
 	/* Clear all otg events (interrupts) indications  */
 	dwc3_writel(dotg->regs, DWC3_OEVT, 0xFFFF);
 
 	/* Enable ID/BSV StsChngEn event*/
-	dwc3_writel(dotg->regs, DWC3_OEVTEN,
-			DWC3_OEVTEN_OTGCONIDSTSCHNGEVNT |
-			DWC3_OEVTEN_OTGBDEVVBUSCHNGEVNT);
+	if (ext_xceiv && !ext_xceiv->otg_capability)
+		dwc3_writel(dotg->regs, DWC3_OEVTEN,
+				DWC3_OEVTEN_OTGCONIDSTSCHNGEVNT |
+				DWC3_OEVTEN_OTGBDEVVBUSCHNGEVNT);
 }
 
 /**
@@ -651,6 +769,13 @@
 		return -ENOMEM;
 	}
 
+	dotg->vbus_otg = devm_regulator_get(dwc->dev->parent, "vbus_dwc3");
+	if (IS_ERR(dotg->vbus_otg)) {
+		dev_err(dwc->dev, "Unable to get vbus_dwc3 regulator\n");
+		ret = PTR_ERR(dotg->vbus_otg);
+		goto err1;
+	}
+
 	/* DWC3 has separate IRQ line for OTG events (ID/BSV etc.) */
 	dotg->irq = platform_get_irq_byname(to_platform_device(dwc->dev),
 								"otg_irq");
@@ -675,8 +800,10 @@
 		goto err1;
 	}
 
+	dotg->dwc = dwc;
 	dotg->otg.phy->otg = &dotg->otg;
 	dotg->otg.phy->dev = dwc->dev;
+	dotg->otg.phy->set_power = dwc3_otg_set_power;
 
 	ret = usb_set_transceiver(dotg->otg.phy);
 	if (ret) {
@@ -686,10 +813,9 @@
 		goto err2;
 	}
 
-	dwc3_otg_reset(dotg);
-
 	dotg->otg.phy->state = OTG_STATE_UNDEFINED;
 
+	init_completion(&dotg->dwc3_xcvr_vbus_init);
 	INIT_WORK(&dotg->sm_work, dwc3_otg_sm_work);
 
 	ret = request_irq(dotg->irq, dwc3_otg_interrupt, IRQF_SHARED,
diff --git a/drivers/usb/dwc3/dwc3_otg.h b/drivers/usb/dwc3/dwc3_otg.h
index b60b42a..4384888 100644
--- a/drivers/usb/dwc3/dwc3_otg.h
+++ b/drivers/usb/dwc3/dwc3_otg.h
@@ -17,9 +17,12 @@
 #define __LINUX_USB_DWC3_OTG_H
 
 #include <linux/workqueue.h>
+#include <linux/power_supply.h>
 
 #include <linux/usb/otg.h>
 
+#define DWC3_IDEV_CHG_MAX 1500
+
 struct dwc3_charger;
 
 /**
@@ -32,15 +35,19 @@
  * @inputs: OTG state machine inputs
  */
 struct dwc3_otg {
-	struct usb_otg otg;
-	int irq;
-	void __iomem *regs;
+	struct usb_otg		otg;
+	int			irq;
+	struct dwc3		*dwc;
+	void __iomem		*regs;
+	struct regulator	*vbus_otg;
 	struct work_struct	sm_work;
 	struct dwc3_charger	*charger;
 	struct dwc3_ext_xceiv	*ext_xceiv;
 #define ID		0
 #define B_SESS_VLD	1
 	unsigned long inputs;
+	struct power_supply	*psy;
+	struct completion	dwc3_xcvr_vbus_init;
 };
 
 /**
@@ -62,6 +69,7 @@
 
 struct dwc3_charger {
 	enum dwc3_chg_type	chg_type;
+	unsigned		max_power;
 
 	/* start/stop charger detection, provided by external charger module */
 	void	(*start_detection)(struct dwc3_charger *charger, bool start);
@@ -89,6 +97,7 @@
 struct dwc3_ext_xceiv {
 	enum dwc3_id_state	id;
 	bool			bsv;
+	bool			otg_capability;
 
 	/* to notify OTG about LPM exit event, provided by OTG */
 	void	(*notify_ext_events)(struct usb_otg *otg,
diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c
index 3584a16..1512513 100644
--- a/drivers/usb/dwc3/ep0.c
+++ b/drivers/usb/dwc3/ep0.c
@@ -54,7 +54,9 @@
 #include "gadget.h"
 #include "io.h"
 
-static void dwc3_ep0_do_control_status(struct dwc3 *dwc, u32 epnum);
+static void __dwc3_ep0_do_control_status(struct dwc3 *dwc, struct dwc3_ep *dep);
+static void __dwc3_ep0_do_control_data(struct dwc3 *dwc,
+		struct dwc3_ep *dep, struct dwc3_request *req);
 
 static const char *dwc3_ep0_state_string(enum dwc3_ep0_state state)
 {
@@ -111,7 +113,7 @@
 	}
 
 	dep->flags |= DWC3_EP_BUSY;
-	dep->res_trans_idx = dwc3_gadget_ep_get_transfer_index(dwc,
+	dep->resource_index = dwc3_gadget_ep_get_transfer_index(dwc,
 			dep->number);
 
 	dwc->ep0_next_event = DWC3_EP0_COMPLETE;
@@ -123,7 +125,6 @@
 		struct dwc3_request *req)
 {
 	struct dwc3		*dwc = dep->dwc;
-	int			ret = 0;
 
 	req->request.actual	= 0;
 	req->request.status	= -EINPROGRESS;
@@ -150,21 +151,76 @@
 			return 0;
 		}
 
-		ret = dwc3_ep0_start_trans(dwc, direction,
-				req->request.dma, req->request.length,
-				DWC3_TRBCTL_CONTROL_DATA);
+		__dwc3_ep0_do_control_data(dwc, dwc->eps[direction], req);
+
 		dep->flags &= ~(DWC3_EP_PENDING_REQUEST |
 				DWC3_EP0_DIR_IN);
-	} else if (dwc->delayed_status) {
+
+		return 0;
+	}
+
+	/*
+	 * In case gadget driver asked us to delay the STATUS phase,
+	 * handle it here.
+	 */
+	if (dwc->delayed_status) {
+		unsigned	direction;
+
+		direction = !dwc->ep0_expect_in;
 		dwc->delayed_status = false;
 
 		if (dwc->ep0state == EP0_STATUS_PHASE)
-			dwc3_ep0_do_control_status(dwc, 1);
+			__dwc3_ep0_do_control_status(dwc, dwc->eps[direction]);
 		else
 			dev_dbg(dwc->dev, "too early for delayed status\n");
+
+		return 0;
 	}
 
-	return ret;
+	/*
+	 * Unfortunately we have uncovered a limitation wrt the Data Phase.
+	 *
+	 * Section 9.4 says we can wait for the XferNotReady(DATA) event to
+	 * come before issueing Start Transfer command, but if we do, we will
+	 * miss situations where the host starts another SETUP phase instead of
+	 * the DATA phase.  Such cases happen at least on TD.7.6 of the Link
+	 * Layer Compliance Suite.
+	 *
+	 * The problem surfaces due to the fact that in case of back-to-back
+	 * SETUP packets there will be no XferNotReady(DATA) generated and we
+	 * will be stuck waiting for XferNotReady(DATA) forever.
+	 *
+	 * By looking at tables 9-13 and 9-14 of the Databook, we can see that
+	 * it tells us to start Data Phase right away. It also mentions that if
+	 * we receive a SETUP phase instead of the DATA phase, core will issue
+	 * XferComplete for the DATA phase, before actually initiating it in
+	 * the wire, with the TRB's status set to "SETUP_PENDING". Such status
+	 * can only be used to print some debugging logs, as the core expects
+	 * us to go through to the STATUS phase and start a CONTROL_STATUS TRB,
+	 * just so it completes right away, without transferring anything and,
+	 * only then, we can go back to the SETUP phase.
+	 *
+	 * Because of this scenario, SNPS decided to change the programming
+	 * model of control transfers and support on-demand transfers only for
+	 * the STATUS phase. To fix the issue we have now, we will always wait
+	 * for gadget driver to queue the DATA phase's struct usb_request, then
+	 * start it right away.
+	 *
+	 * If we're actually in a 2-stage transfer, we will wait for
+	 * XferNotReady(STATUS).
+	 */
+	if (dwc->three_stage_setup) {
+		unsigned        direction;
+
+		direction = dwc->ep0_expect_in;
+		dwc->ep0state = EP0_DATA_PHASE;
+
+		__dwc3_ep0_do_control_data(dwc, dwc->eps[direction], req);
+
+		dep->flags &= ~DWC3_EP0_DIR_IN;
+	}
+
+	return 0;
 }
 
 int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request,
@@ -179,7 +235,7 @@
 	int				ret;
 
 	spin_lock_irqsave(&dwc->lock, flags);
-	if (!dep->desc) {
+	if (!dep->endpoint.desc) {
 		dev_dbg(dwc->dev, "trying to queue request %p to disabled %s\n",
 				request, dep->name);
 		ret = -ESHUTDOWN;
@@ -206,9 +262,14 @@
 
 static void dwc3_ep0_stall_and_restart(struct dwc3 *dwc)
 {
-	struct dwc3_ep		*dep = dwc->eps[0];
+	struct dwc3_ep		*dep;
+
+	/* reinitialize physical ep1 */
+	dep = dwc->eps[1];
+	dep->flags = DWC3_EP_ENABLED;
 
 	/* stall is always issued on EP0 */
+	dep = dwc->eps[0];
 	__dwc3_gadget_ep_set_halt(dep, 1);
 	dep->flags = DWC3_EP_ENABLED;
 	dwc->delayed_status = false;
@@ -224,6 +285,16 @@
 	dwc3_ep0_out_start(dwc);
 }
 
+int dwc3_gadget_ep0_set_halt(struct usb_ep *ep, int value)
+{
+	struct dwc3_ep			*dep = to_dwc3_ep(ep);
+	struct dwc3			*dwc = dep->dwc;
+
+	dwc3_ep0_stall_and_restart(dwc);
+
+	return 0;
+}
+
 void dwc3_ep0_out_start(struct dwc3 *dwc)
 {
 	int				ret;
@@ -261,6 +332,7 @@
 {
 	struct dwc3_ep		*dep;
 	u32			recip;
+	u32			reg;
 	u16			usb_status = 0;
 	__le16			*response_pkt;
 
@@ -268,10 +340,18 @@
 	switch (recip) {
 	case USB_RECIP_DEVICE:
 		/*
-		 * We are self-powered. U1/U2/LTM will be set later
-		 * once we handle this states. RemoteWakeup is 0 on SS
+		 * LTM will be set once we know how to set this in HW.
 		 */
 		usb_status |= dwc->is_selfpowered << USB_DEVICE_SELF_POWERED;
+
+		if (dwc->speed == DWC3_DSTS_SUPERSPEED) {
+			reg = dwc3_readl(dwc->regs, DWC3_DCTL);
+			if (reg & DWC3_DCTL_INITU1ENA)
+				usb_status |= 1 << USB_DEV_STAT_U1_ENABLED;
+			if (reg & DWC3_DCTL_INITU2ENA)
+				usb_status |= 1 << USB_DEV_STAT_U2_ENABLED;
+		}
+
 		break;
 
 	case USB_RECIP_INTERFACE:
@@ -312,6 +392,7 @@
 	u32			recip;
 	u32			wValue;
 	u32			wIndex;
+	u32			reg;
 	int			ret;
 
 	wValue = le16_to_cpu(ctrl->wValue);
@@ -320,29 +401,43 @@
 	switch (recip) {
 	case USB_RECIP_DEVICE:
 
+		switch (wValue) {
+		case USB_DEVICE_REMOTE_WAKEUP:
+			break;
 		/*
 		 * 9.4.1 says only only for SS, in AddressState only for
 		 * default control pipe
 		 */
-		switch (wValue) {
 		case USB_DEVICE_U1_ENABLE:
-		case USB_DEVICE_U2_ENABLE:
-		case USB_DEVICE_LTM_ENABLE:
 			if (dwc->dev_state != DWC3_CONFIGURED_STATE)
 				return -EINVAL;
 			if (dwc->speed != DWC3_DSTS_SUPERSPEED)
 				return -EINVAL;
-		}
 
-		/* XXX add U[12] & LTM */
-		switch (wValue) {
-		case USB_DEVICE_REMOTE_WAKEUP:
+			reg = dwc3_readl(dwc->regs, DWC3_DCTL);
+			if (set)
+				reg |= DWC3_DCTL_INITU1ENA;
+			else
+				reg &= ~DWC3_DCTL_INITU1ENA;
+			dwc3_writel(dwc->regs, DWC3_DCTL, reg);
 			break;
-		case USB_DEVICE_U1_ENABLE:
-			break;
+
 		case USB_DEVICE_U2_ENABLE:
+			if (dwc->dev_state != DWC3_CONFIGURED_STATE)
+				return -EINVAL;
+			if (dwc->speed != DWC3_DSTS_SUPERSPEED)
+				return -EINVAL;
+
+			reg = dwc3_readl(dwc->regs, DWC3_DCTL);
+			if (set)
+				reg |= DWC3_DCTL_INITU2ENA;
+			else
+				reg &= ~DWC3_DCTL_INITU2ENA;
+			dwc3_writel(dwc->regs, DWC3_DCTL, reg);
 			break;
+
 		case USB_DEVICE_LTM_ENABLE:
+			return -EINVAL;
 			break;
 
 		case USB_DEVICE_TEST_MODE:
@@ -380,6 +475,10 @@
 			dep = dwc3_wIndex_to_dep(dwc, wIndex);
 			if (!dep)
 				return -EINVAL;
+
+			if (!set && (dep->flags & DWC3_EP_WEDGE))
+				return 0;
+
 			ret = __dwc3_gadget_ep_set_halt(dep, set);
 			if (ret)
 				return -EINVAL;
@@ -439,6 +538,7 @@
 {
 	u32 cfg;
 	int ret;
+	u32 reg;
 
 	dwc->start_config_issued = false;
 	cfg = le16_to_cpu(ctrl->wValue);
@@ -453,6 +553,14 @@
 		/* if the cfg matches and the cfg is non zero */
 		if (cfg && (!ret || (ret == USB_GADGET_DELAYED_STATUS))) {
 			dwc->dev_state = DWC3_CONFIGURED_STATE;
+			/*
+			 * Enable transition to U1/U2 state when
+			 * nothing is pending from application.
+			 */
+			reg = dwc3_readl(dwc->regs, DWC3_DCTL);
+			reg |= (DWC3_DCTL_ACCEPTU1ENA | DWC3_DCTL_ACCEPTU2ENA);
+			dwc3_writel(dwc->regs, DWC3_DCTL, reg);
+
 			dwc->resize_fifos = true;
 			dev_dbg(dwc->dev, "resize fifos flag SET\n");
 		}
@@ -469,6 +577,107 @@
 	return ret;
 }
 
+static void dwc3_ep0_set_sel_cmpl(struct usb_ep *ep, struct usb_request *req)
+{
+	struct dwc3_ep	*dep = to_dwc3_ep(ep);
+	struct dwc3	*dwc = dep->dwc;
+
+	u32		param = 0;
+	u32		reg;
+
+	struct timing {
+		u8	u1sel;
+		u8	u1pel;
+		u16	u2sel;
+		u16	u2pel;
+	} __packed timing;
+
+	int		ret;
+
+	memcpy(&timing, req->buf, sizeof(timing));
+
+	dwc->u1sel = timing.u1sel;
+	dwc->u1pel = timing.u1pel;
+	dwc->u2sel = le16_to_cpu(timing.u2sel);
+	dwc->u2pel = le16_to_cpu(timing.u2pel);
+
+	reg = dwc3_readl(dwc->regs, DWC3_DCTL);
+	if (reg & DWC3_DCTL_INITU2ENA)
+		param = dwc->u2pel;
+	if (reg & DWC3_DCTL_INITU1ENA)
+		param = dwc->u1pel;
+
+	/*
+	 * According to Synopsys Databook, if parameter is
+	 * greater than 125, a value of zero should be
+	 * programmed in the register.
+	 */
+	if (param > 125)
+		param = 0;
+
+	/* now that we have the time, issue DGCMD Set Sel */
+	ret = dwc3_send_gadget_generic_command(dwc,
+			DWC3_DGCMD_SET_PERIODIC_PAR, param);
+	WARN_ON(ret < 0);
+}
+
+static int dwc3_ep0_set_sel(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
+{
+	struct dwc3_ep	*dep;
+	u16		wLength;
+	u16		wValue;
+
+	if (dwc->dev_state == DWC3_DEFAULT_STATE)
+		return -EINVAL;
+
+	wValue = le16_to_cpu(ctrl->wValue);
+	wLength = le16_to_cpu(ctrl->wLength);
+
+	if (wLength != 6) {
+		dev_err(dwc->dev, "Set SEL should be 6 bytes, got %d\n",
+				wLength);
+		return -EINVAL;
+	}
+
+	/*
+	 * To handle Set SEL we need to receive 6 bytes from Host. So let's
+	 * queue a usb_request for 6 bytes.
+	 *
+	 * Remember, though, this controller can't handle non-wMaxPacketSize
+	 * aligned transfers on the OUT direction, so we queue a request for
+	 * wMaxPacketSize instead.
+	 */
+	dep = dwc->eps[0];
+	dwc->ep0_usb_req.dep = dep;
+	dwc->ep0_usb_req.request.length = dep->endpoint.maxpacket;
+	dwc->ep0_usb_req.request.buf = dwc->setup_buf;
+	dwc->ep0_usb_req.request.complete = dwc3_ep0_set_sel_cmpl;
+
+	return __dwc3_gadget_ep0_queue(dep, &dwc->ep0_usb_req);
+}
+
+static int dwc3_ep0_set_isoch_delay(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
+{
+	u16		wLength;
+	u16		wValue;
+	u16		wIndex;
+
+	wValue = le16_to_cpu(ctrl->wValue);
+	wLength = le16_to_cpu(ctrl->wLength);
+	wIndex = le16_to_cpu(ctrl->wIndex);
+
+	if (wIndex || wLength)
+		return -EINVAL;
+
+	/*
+	 * REVISIT It's unclear from Databook what to do with this
+	 * value. For now, just cache it.
+	 */
+	dwc->isoch_delay = wValue;
+
+	return 0;
+}
+
 static int dwc3_ep0_std_request(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
 {
 	int ret;
@@ -494,6 +703,14 @@
 		dev_vdbg(dwc->dev, "USB_REQ_SET_CONFIGURATION\n");
 		ret = dwc3_ep0_set_config(dwc, ctrl);
 		break;
+	case USB_REQ_SET_SEL:
+		dev_vdbg(dwc->dev, "USB_REQ_SET_SEL\n");
+		ret = dwc3_ep0_set_sel(dwc, ctrl);
+		break;
+	case USB_REQ_SET_ISOCH_DELAY:
+		dev_vdbg(dwc->dev, "USB_REQ_SET_ISOCH_DELAY\n");
+		ret = dwc3_ep0_set_isoch_delay(dwc, ctrl);
+		break;
 	default:
 		dev_vdbg(dwc->dev, "Forwarding to gadget driver\n");
 		ret = dwc3_ep0_delegate_req(dwc, ctrl);
@@ -507,11 +724,11 @@
 		const struct dwc3_event_depevt *event)
 {
 	struct usb_ctrlrequest *ctrl = dwc->ctrl_req;
-	int ret;
+	int ret = -EINVAL;
 	u32 len;
 
 	if (!dwc->gadget_driver)
-		goto err;
+		goto out;
 
 	len = le16_to_cpu(ctrl->wLength);
 	if (!len) {
@@ -532,11 +749,9 @@
 	if (ret == USB_GADGET_DELAYED_STATUS)
 		dwc->delayed_status = true;
 
-	if (ret >= 0)
-		return;
-
-err:
-	dwc3_ep0_stall_and_restart(dwc);
+out:
+	if (ret < 0)
+		dwc3_ep0_stall_and_restart(dwc);
 }
 
 static void dwc3_ep0_complete_data(struct dwc3 *dwc,
@@ -547,6 +762,7 @@
 	struct dwc3_trb		*trb;
 	struct dwc3_ep		*ep0;
 	u32			transferred;
+	u32			status;
 	u32			length;
 	u8			epnum;
 
@@ -559,6 +775,17 @@
 	ur = &r->request;
 
 	trb = dwc->ep0_trb;
+
+	status = DWC3_TRB_SIZE_TRBSTS(trb->size);
+	if (status == DWC3_TRBSTS_SETUP_PENDING) {
+		dev_dbg(dwc->dev, "Setup Pending received\n");
+
+		if (r)
+			dwc3_gadget_giveback(ep0, r, -ECONNRESET);
+
+		return;
+	}
+
 	length = trb->size & DWC3_TRB_SIZE_MASK;
 
 	if (dwc->ep0_bounced) {
@@ -569,7 +796,6 @@
 		transferred = min_t(u32, ur->length,
 				transfer_size - length);
 		memcpy(ur->buf, dwc->ep0_bounce, transferred);
-		dwc->ep0_bounced = false;
 	} else {
 		transferred = ur->length - length;
 	}
@@ -590,13 +816,16 @@
 	}
 }
 
-static void dwc3_ep0_complete_req(struct dwc3 *dwc,
+static void dwc3_ep0_complete_status(struct dwc3 *dwc,
 		const struct dwc3_event_depevt *event)
 {
 	struct dwc3_request	*r;
 	struct dwc3_ep		*dep;
+	struct dwc3_trb		*trb;
+	u32			status;
 
 	dep = dwc->eps[0];
+	trb = dwc->ep0_trb;
 
 	if (!list_empty(&dep->request_list)) {
 		r = next_request(&dep->request_list);
@@ -612,9 +841,14 @@
 			dev_dbg(dwc->dev, "Invalid Test #%d\n",
 					dwc->test_mode_nr);
 			dwc3_ep0_stall_and_restart(dwc);
+			return;
 		}
 	}
 
+	status = DWC3_TRB_SIZE_TRBSTS(trb->size);
+	if (status == DWC3_TRBSTS_SETUP_PENDING)
+		dev_dbg(dwc->dev, "Setup Pending received\n");
+
 	dwc->ep0state = EP0_SETUP_PHASE;
 	dwc3_ep0_out_start(dwc);
 }
@@ -625,7 +859,7 @@
 	struct dwc3_ep		*dep = dwc->eps[event->endpoint_number];
 
 	dep->flags &= ~DWC3_EP_BUSY;
-	dep->res_trans_idx = 0;
+	dep->resource_index = 0;
 	dwc->setup_packet_pending = false;
 
 	switch (dwc->ep0state) {
@@ -641,76 +875,60 @@
 
 	case EP0_STATUS_PHASE:
 		dev_vdbg(dwc->dev, "Status Phase\n");
-		dwc3_ep0_complete_req(dwc, event);
+		dwc3_ep0_complete_status(dwc, event);
 		break;
 	default:
 		WARN(true, "UNKNOWN ep0state %d\n", dwc->ep0state);
 	}
 }
 
-static void dwc3_ep0_do_control_setup(struct dwc3 *dwc,
-		const struct dwc3_event_depevt *event)
+static void __dwc3_ep0_do_control_data(struct dwc3 *dwc,
+		struct dwc3_ep *dep, struct dwc3_request *req)
 {
-	dwc3_ep0_out_start(dwc);
-}
-
-static void dwc3_ep0_do_control_data(struct dwc3 *dwc,
-		const struct dwc3_event_depevt *event)
-{
-	struct dwc3_ep		*dep;
-	struct dwc3_request	*req;
 	int			ret;
 
-	dep = dwc->eps[0];
-
-	if (list_empty(&dep->request_list)) {
-		dev_vdbg(dwc->dev, "pending request for EP0 Data phase\n");
-		dep->flags |= DWC3_EP_PENDING_REQUEST;
-
-		if (event->endpoint_number)
-			dep->flags |= DWC3_EP0_DIR_IN;
-		return;
-	}
-
-	req = next_request(&dep->request_list);
-	req->direction = !!event->endpoint_number;
+	req->direction = !!dep->number;
 
 	if (req->request.length == 0) {
-		ret = dwc3_ep0_start_trans(dwc, event->endpoint_number,
+		ret = dwc3_ep0_start_trans(dwc, dep->number,
 				dwc->ctrl_req_addr, 0,
 				DWC3_TRBCTL_CONTROL_DATA);
-	} else if ((req->request.length % dep->endpoint.maxpacket)
-			&& (event->endpoint_number == 0)) {
+	} else if (!IS_ALIGNED(req->request.length, dep->endpoint.maxpacket)
+			&& (dep->number == 0)) {
+		u32		transfer_size;
+
 		ret = usb_gadget_map_request(&dwc->gadget, &req->request,
-				event->endpoint_number);
+				dep->number);
 		if (ret) {
 			dev_dbg(dwc->dev, "failed to map request\n");
 			return;
 		}
 
-		WARN_ON(req->request.length > dep->endpoint.maxpacket);
+		WARN_ON(req->request.length > DWC3_EP0_BOUNCE_SIZE);
+
+		transfer_size = roundup(req->request.length,
+				(u32) dep->endpoint.maxpacket);
 
 		dwc->ep0_bounced = true;
 
 		/*
-		 * REVISIT in case request length is bigger than EP0
-		 * wMaxPacketSize, we will need two chained TRBs to handle
-		 * the transfer.
+		 * REVISIT in case request length is bigger than
+		 * DWC3_EP0_BOUNCE_SIZE we will need two chained
+		 * TRBs to handle the transfer.
 		 */
-		ret = dwc3_ep0_start_trans(dwc, event->endpoint_number,
-				dwc->ep0_bounce_addr, dep->endpoint.maxpacket,
+		ret = dwc3_ep0_start_trans(dwc, dep->number,
+				dwc->ep0_bounce_addr, transfer_size,
 				DWC3_TRBCTL_CONTROL_DATA);
 	} else {
 		ret = usb_gadget_map_request(&dwc->gadget, &req->request,
-				event->endpoint_number);
+				dep->number);
 		if (ret) {
 			dev_dbg(dwc->dev, "failed to map request\n");
 			return;
 		}
 
-		ret = dwc3_ep0_start_trans(dwc, event->endpoint_number,
-				req->request.dma, req->request.length,
-				DWC3_TRBCTL_CONTROL_DATA);
+		ret = dwc3_ep0_start_trans(dwc, dep->number, req->request.dma,
+				req->request.length, DWC3_TRBCTL_CONTROL_DATA);
 	}
 
 	WARN_ON(ret < 0);
@@ -728,10 +946,8 @@
 			dwc->ctrl_req_addr, 0, type);
 }
 
-static void dwc3_ep0_do_control_status(struct dwc3 *dwc, u32 epnum)
+static void __dwc3_ep0_do_control_status(struct dwc3 *dwc, struct dwc3_ep *dep)
 {
-	struct dwc3_ep		*dep = dwc->eps[epnum];
-
 	if (dwc->resize_fifos) {
 		dev_dbg(dwc->dev, "starting to resize fifos\n");
 		dwc3_gadget_resize_tx_fifos(dwc);
@@ -741,107 +957,78 @@
 	WARN_ON(dwc3_ep0_start_control_status(dep));
 }
 
+static void dwc3_ep0_do_control_status(struct dwc3 *dwc,
+		const struct dwc3_event_depevt *event)
+{
+	struct dwc3_ep		*dep = dwc->eps[event->endpoint_number];
+
+	__dwc3_ep0_do_control_status(dwc, dep);
+}
+
+static void dwc3_ep0_end_control_data(struct dwc3 *dwc, struct dwc3_ep *dep)
+{
+	struct dwc3_gadget_ep_cmd_params params;
+	u32			cmd;
+	int			ret;
+
+	if (!dep->resource_index)
+		return;
+
+	cmd = DWC3_DEPCMD_ENDTRANSFER;
+	cmd |= DWC3_DEPCMD_CMDIOC;
+	cmd |= DWC3_DEPCMD_PARAM(dep->resource_index);
+	memset(&params, 0, sizeof(params));
+	ret = dwc3_send_gadget_ep_cmd(dwc, dep->number, cmd, &params);
+	WARN_ON_ONCE(ret);
+	dep->resource_index = 0;
+}
+
 static void dwc3_ep0_xfernotready(struct dwc3 *dwc,
 		const struct dwc3_event_depevt *event)
 {
 	dwc->setup_packet_pending = true;
 
-	/*
-	 * This part is very tricky: If we has just handled
-	 * XferNotReady(Setup) and we're now expecting a
-	 * XferComplete but, instead, we receive another
-	 * XferNotReady(Setup), we should STALL and restart
-	 * the state machine.
-	 *
-	 * In all other cases, we just continue waiting
-	 * for the XferComplete event.
-	 *
-	 * We are a little bit unsafe here because we're
-	 * not trying to ensure that last event was, indeed,
-	 * XferNotReady(Setup).
-	 *
-	 * Still, we don't expect any condition where that
-	 * should happen and, even if it does, it would be
-	 * another error condition.
-	 */
-	if (dwc->ep0_next_event == DWC3_EP0_COMPLETE) {
-		switch (event->status) {
-		case DEPEVT_STATUS_CONTROL_SETUP:
-			dev_vdbg(dwc->dev, "Unexpected XferNotReady(Setup)\n");
-			dwc3_ep0_stall_and_restart(dwc);
-			break;
-		case DEPEVT_STATUS_CONTROL_DATA:
-			/* FALLTHROUGH */
-		case DEPEVT_STATUS_CONTROL_STATUS:
-			/* FALLTHROUGH */
-		default:
-			dev_vdbg(dwc->dev, "waiting for XferComplete\n");
-		}
-
-		return;
-	}
-
 	switch (event->status) {
-	case DEPEVT_STATUS_CONTROL_SETUP:
-		dev_vdbg(dwc->dev, "Control Setup\n");
-
-		dwc->ep0state = EP0_SETUP_PHASE;
-
-		dwc3_ep0_do_control_setup(dwc, event);
-		break;
-
 	case DEPEVT_STATUS_CONTROL_DATA:
 		dev_vdbg(dwc->dev, "Control Data\n");
 
-		dwc->ep0state = EP0_DATA_PHASE;
-
-		if (dwc->ep0_next_event != DWC3_EP0_NRDY_DATA) {
-			dev_vdbg(dwc->dev, "Expected %d got %d\n",
-					dwc->ep0_next_event,
-					DWC3_EP0_NRDY_DATA);
-
-			dwc3_ep0_stall_and_restart(dwc);
-			return;
-		}
-
 		/*
-		 * One of the possible error cases is when Host _does_
-		 * request for Data Phase, but it does so on the wrong
-		 * direction.
+		 * We already have a DATA transfer in the controller's cache,
+		 * if we receive a XferNotReady(DATA) we will ignore it, unless
+		 * it's for the wrong direction.
 		 *
-		 * Here, we already know ep0_next_event is DATA (see above),
-		 * so we only need to check for direction.
+		 * In that case, we must issue END_TRANSFER command to the Data
+		 * Phase we already have started and issue SetStall on the
+		 * control endpoint.
 		 */
 		if (dwc->ep0_expect_in != event->endpoint_number) {
+			struct dwc3_ep	*dep = dwc->eps[dwc->ep0_expect_in];
+
 			dev_vdbg(dwc->dev, "Wrong direction for Data phase\n");
+			dwc3_ep0_end_control_data(dwc, dep);
 			dwc3_ep0_stall_and_restart(dwc);
 			return;
 		}
 
-		dwc3_ep0_do_control_data(dwc, event);
 		break;
 
 	case DEPEVT_STATUS_CONTROL_STATUS:
+		if (dwc->ep0_next_event != DWC3_EP0_NRDY_STATUS)
+			return;
+
 		dev_vdbg(dwc->dev, "Control Status\n");
 
 		dwc->ep0state = EP0_STATUS_PHASE;
 
-		if (dwc->ep0_next_event != DWC3_EP0_NRDY_STATUS) {
-			dev_vdbg(dwc->dev, "Expected %d got %d\n",
-					dwc->ep0_next_event,
-					DWC3_EP0_NRDY_STATUS);
-
-			dwc3_ep0_stall_and_restart(dwc);
-			return;
-		}
-
-		if (dwc->delayed_status) {
+		if (dwc->delayed_status &&
+				list_empty(&dwc->eps[0]->request_list)) {
 			WARN_ON_ONCE(event->endpoint_number != 1);
 			dev_vdbg(dwc->dev, "Mass Storage delayed status\n");
 			return;
 		}
+		dwc->delayed_status = false;
 
-		dwc3_ep0_do_control_status(dwc, event->endpoint_number);
+		dwc3_ep0_do_control_status(dwc, event);
 	}
 }
 
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index a3f6e58..9c1ebf8 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -101,6 +101,23 @@
 	int		retries = 10000;
 	u32		reg;
 
+	/*
+	 * Wait until device controller is ready. Only applies to 1.94a and
+	 * later RTL.
+	 */
+	if (dwc->revision >= DWC3_REVISION_194A) {
+		while (--retries) {
+			reg = dwc3_readl(dwc->regs, DWC3_DSTS);
+			if (reg & DWC3_DSTS_DCNRD)
+				udelay(5);
+			else
+				break;
+		}
+
+		if (retries <= 0)
+			return -ETIMEDOUT;
+	}
+
 	reg = dwc3_readl(dwc->regs, DWC3_DCTL);
 	reg &= ~DWC3_DCTL_ULSTCHNGREQ_MASK;
 
@@ -108,7 +125,15 @@
 	reg |= DWC3_DCTL_ULSTCHNGREQ(state);
 	dwc3_writel(dwc->regs, DWC3_DCTL, reg);
 
+	/*
+	 * The following code is racy when called from dwc3_gadget_wakeup,
+	 * and is not needed, at least on newer versions
+	 */
+	if (dwc->revision >= DWC3_REVISION_194A)
+		return 0;
+
 	/* wait for a change in DSTS */
+	retries = 10000;
 	while (--retries) {
 		reg = dwc3_readl(dwc->regs, DWC3_DSTS);
 
@@ -179,8 +204,8 @@
 		if (!(dep->flags & DWC3_EP_ENABLED))
 			continue;
 
-		if (usb_endpoint_xfer_bulk(dep->desc)
-				|| usb_endpoint_xfer_isoc(dep->desc))
+		if (usb_endpoint_xfer_bulk(dep->endpoint.desc)
+				|| usb_endpoint_xfer_isoc(dep->endpoint.desc))
 			mult = 3;
 
 		/*
@@ -230,7 +255,7 @@
 		 * completed (not the LINK TRB).
 		 */
 		if (((dep->busy_slot & DWC3_TRB_MASK) == DWC3_TRB_NUM - 1) &&
-				usb_endpoint_xfer_isoc(dep->desc))
+				usb_endpoint_xfer_isoc(dep->endpoint.desc))
 			dep->busy_slot++;
 	}
 	list_del(&req->list);
@@ -239,8 +264,11 @@
 	if (req->request.status == -EINPROGRESS)
 		req->request.status = status;
 
-	usb_gadget_unmap_request(&dwc->gadget, &req->request,
-			req->direction);
+	if (dwc->ep0_bounced && dep->number == 0)
+		dwc->ep0_bounced = false;
+	else
+		usb_gadget_unmap_request(&dwc->gadget, &req->request,
+				req->direction);
 
 	dev_dbg(dwc->dev, "request %p from %s completed %d/%d ===> %d\n",
 			req, dep->name, req->request.actual,
@@ -266,8 +294,8 @@
 		return "Clear Stall";
 	case DWC3_DEPCMD_SETSTALL:
 		return "Set Stall";
-	case DWC3_DEPCMD_GETSEQNUMBER:
-		return "Get Data Sequence Number";
+	case DWC3_DEPCMD_GETEPSTATE:
+		return "Get Endpoint State";
 	case DWC3_DEPCMD_SETTRANSFRESOURCE:
 		return "Set Endpoint Transfer Resource";
 	case DWC3_DEPCMD_SETEPCONFIG:
@@ -277,6 +305,33 @@
 	}
 }
 
+int dwc3_send_gadget_generic_command(struct dwc3 *dwc, int cmd, u32 param)
+{
+	u32		timeout = 500;
+	u32		reg;
+
+	dwc3_writel(dwc->regs, DWC3_DGCMDPAR, param);
+	dwc3_writel(dwc->regs, DWC3_DGCMD, cmd | DWC3_DGCMD_CMDACT);
+
+	do {
+		reg = dwc3_readl(dwc->regs, DWC3_DGCMD);
+		if (!(reg & DWC3_DGCMD_CMDACT)) {
+			dev_vdbg(dwc->dev, "Command Complete --> %d\n",
+					DWC3_DGCMD_STATUS(reg));
+			return 0;
+		}
+
+		/*
+		 * We can't sleep here, because it's also called from
+		 * interrupt context.
+		 */
+		timeout--;
+		if (!timeout)
+			return -ETIMEDOUT;
+		udelay(1);
+	} while (1);
+}
+
 int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep,
 		unsigned cmd, struct dwc3_gadget_ep_cmd_params *params)
 {
@@ -380,15 +435,25 @@
 
 static int dwc3_gadget_set_ep_config(struct dwc3 *dwc, struct dwc3_ep *dep,
 		const struct usb_endpoint_descriptor *desc,
-		const struct usb_ss_ep_comp_descriptor *comp_desc)
+		const struct usb_ss_ep_comp_descriptor *comp_desc,
+		bool ignore)
 {
 	struct dwc3_gadget_ep_cmd_params params;
 
 	memset(&params, 0x00, sizeof(params));
 
 	params.param0 = DWC3_DEPCFG_EP_TYPE(usb_endpoint_type(desc))
-		| DWC3_DEPCFG_MAX_PACKET_SIZE(usb_endpoint_maxp(desc))
-		| DWC3_DEPCFG_BURST_SIZE(dep->endpoint.maxburst);
+		| DWC3_DEPCFG_MAX_PACKET_SIZE(usb_endpoint_maxp(desc));
+
+	/* Burst size is only needed in SuperSpeed mode */
+	if (dwc->gadget.speed == USB_SPEED_SUPER) {
+		u32 burst = dep->endpoint.maxburst - 1;
+
+		params.param0 |= DWC3_DEPCFG_BURST_SIZE(burst);
+	}
+
+	if (ignore)
+		params.param0 |= DWC3_DEPCFG_IGN_SEQ_NUM;
 
 	params.param1 = DWC3_DEPCFG_XFER_COMPLETE_EN
 		| DWC3_DEPCFG_XFER_NOT_READY_EN;
@@ -447,7 +512,8 @@
  */
 static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep,
 		const struct usb_endpoint_descriptor *desc,
-		const struct usb_ss_ep_comp_descriptor *comp_desc)
+		const struct usb_ss_ep_comp_descriptor *comp_desc,
+		bool ignore)
 {
 	struct dwc3		*dwc = dep->dwc;
 	u32			reg;
@@ -459,7 +525,7 @@
 			return ret;
 	}
 
-	ret = dwc3_gadget_set_ep_config(dwc, dep, desc, comp_desc);
+	ret = dwc3_gadget_set_ep_config(dwc, dep, desc, comp_desc, ignore);
 	if (ret)
 		return ret;
 
@@ -471,7 +537,7 @@
 		if (ret)
 			return ret;
 
-		dep->desc = desc;
+		dep->endpoint.desc = desc;
 		dep->comp_desc = comp_desc;
 		dep->type = usb_endpoint_type(desc);
 		dep->flags |= DWC3_EP_ENABLED;
@@ -504,9 +570,17 @@
 {
 	struct dwc3_request		*req;
 
-	if (!list_empty(&dep->req_queued))
+	if (!list_empty(&dep->req_queued)) {
 		dwc3_stop_active_transfer(dwc, dep->number);
 
+		/* - giveback all requests to gadget driver */
+		while (!list_empty(&dep->req_queued)) {
+			req = next_request(&dep->req_queued);
+
+			dwc3_gadget_giveback(dep, req, -ESHUTDOWN);
+		}
+	}
+
 	while (!list_empty(&dep->request_list)) {
 		req = next_request(&dep->request_list);
 
@@ -534,7 +608,6 @@
 	dwc3_writel(dwc->regs, DWC3_DALEPENA, reg);
 
 	dep->stream_capable = false;
-	dep->desc = NULL;
 	dep->endpoint.desc = NULL;
 	dep->comp_desc = NULL;
 	dep->type = 0;
@@ -579,6 +652,12 @@
 	dep = to_dwc3_ep(ep);
 	dwc = dep->dwc;
 
+	if (dep->flags & DWC3_EP_ENABLED) {
+		dev_WARN_ONCE(dwc->dev, true, "%s is already enabled\n",
+				dep->name);
+		return 0;
+	}
+
 	switch (usb_endpoint_type(desc)) {
 	case USB_ENDPOINT_XFER_CONTROL:
 		strlcat(dep->name, "-control", sizeof(dep->name));
@@ -596,16 +675,10 @@
 		dev_err(dwc->dev, "invalid endpoint transfer type\n");
 	}
 
-	if (dep->flags & DWC3_EP_ENABLED) {
-		dev_WARN_ONCE(dwc->dev, true, "%s is already enabled\n",
-				dep->name);
-		return 0;
-	}
-
 	dev_vdbg(dwc->dev, "Enabling %s\n", dep->name);
 
 	spin_lock_irqsave(&dwc->lock, flags);
-	ret = __dwc3_gadget_ep_enable(dep, desc, ep->comp_desc);
+	ret = __dwc3_gadget_ep_enable(dep, desc, ep->comp_desc, false);
 	spin_unlock_irqrestore(&dwc->lock, flags);
 
 	return ret;
@@ -695,7 +768,7 @@
 
 	/* Skip the LINK-TRB on ISOC */
 	if (((cur_slot & DWC3_TRB_MASK) == DWC3_TRB_NUM - 1) &&
-			usb_endpoint_xfer_isoc(dep->desc))
+			usb_endpoint_xfer_isoc(dep->endpoint.desc))
 		return;
 
 	if (!req->trb) {
@@ -708,7 +781,7 @@
 	trb->bpl = lower_32_bits(dma);
 	trb->bph = upper_32_bits(dma);
 
-	switch (usb_endpoint_type(dep->desc)) {
+	switch (usb_endpoint_type(dep->endpoint.desc)) {
 	case USB_ENDPOINT_XFER_CONTROL:
 		trb->ctrl = DWC3_TRBCTL_CONTROL_SETUP;
 		break;
@@ -716,8 +789,7 @@
 	case USB_ENDPOINT_XFER_ISOC:
 		trb->ctrl = DWC3_TRBCTL_ISOCHRONOUS_FIRST;
 
-		/* IOC every DWC3_TRB_NUM / 4 so we can refill */
-		if (!(cur_slot % (DWC3_TRB_NUM / 4)))
+		if (!req->request.no_interrupt)
 			trb->ctrl |= DWC3_TRB_CTRL_IOC;
 		break;
 
@@ -733,7 +805,7 @@
 		BUG();
 	}
 
-	if (usb_endpoint_xfer_isoc(dep->desc)) {
+	if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
 		trb->ctrl |= DWC3_TRB_CTRL_ISP_IMI;
 		trb->ctrl |= DWC3_TRB_CTRL_CSP;
 	} else {
@@ -744,7 +816,7 @@
 			trb->ctrl |= DWC3_TRB_CTRL_LST;
 	}
 
-	if (usb_endpoint_xfer_bulk(dep->desc) && dep->stream_capable)
+	if (usb_endpoint_xfer_bulk(dep->endpoint.desc) && dep->stream_capable)
 		trb->ctrl |= DWC3_TRB_CTRL_SID_SOFN(req->request.stream_id);
 
 	trb->ctrl |= DWC3_TRB_CTRL_HWO;
@@ -772,7 +844,7 @@
 	trbs_left = (dep->busy_slot - dep->free_slot) & DWC3_TRB_MASK;
 
 	/* Can't wrap around on a non-isoc EP since there's no link TRB */
-	if (!usb_endpoint_xfer_isoc(dep->desc)) {
+	if (!usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
 		max = DWC3_TRB_NUM - (dep->free_slot & DWC3_TRB_MASK);
 		if (trbs_left > max)
 			trbs_left = max;
@@ -798,7 +870,7 @@
 		 * processed from the first TRB until the last one. Since we
 		 * don't wrap around we have to start at the beginning.
 		 */
-		if (usb_endpoint_xfer_isoc(dep->desc)) {
+		if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
 			dep->busy_slot = 1;
 			dep->free_slot = 1;
 		} else {
@@ -808,7 +880,7 @@
 	}
 
 	/* The last TRB is a link TRB, not used for xfer */
-	if ((trbs_left <= 1) && usb_endpoint_xfer_isoc(dep->desc))
+	if ((trbs_left <= 1) && usb_endpoint_xfer_isoc(dep->endpoint.desc))
 		return;
 
 	list_for_each_entry_safe(req, n, &dep->request_list, list) {
@@ -931,14 +1003,45 @@
 	}
 
 	dep->flags |= DWC3_EP_BUSY;
-	dep->res_trans_idx = dwc3_gadget_ep_get_transfer_index(dwc,
-			dep->number);
 
-	WARN_ON_ONCE(!dep->res_trans_idx);
+	if (start_new) {
+		dep->resource_index = dwc3_gadget_ep_get_transfer_index(dwc,
+				dep->number);
+		WARN_ON_ONCE(!dep->resource_index);
+	}
 
 	return 0;
 }
 
+static void __dwc3_gadget_start_isoc(struct dwc3 *dwc,
+		struct dwc3_ep *dep, u32 cur_uf)
+{
+	u32 uf;
+
+	if (list_empty(&dep->request_list)) {
+		dev_vdbg(dwc->dev, "ISOC ep %s run out for requests.\n",
+			dep->name);
+		dep->flags |= DWC3_EP_PENDING_REQUEST;
+		return;
+	}
+
+	/* 4 micro frames in the future */
+	uf = cur_uf + dep->interval * 4;
+
+	__dwc3_gadget_kick_transfer(dep, uf, 1);
+}
+
+static void dwc3_gadget_start_isoc(struct dwc3 *dwc,
+		struct dwc3_ep *dep, const struct dwc3_event_depevt *event)
+{
+	u32 cur_uf, mask;
+
+	mask = ~(dep->interval - 1);
+	cur_uf = event->parameters & mask;
+
+	__dwc3_gadget_start_isoc(dwc, dep, cur_uf);
+}
+
 static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
 {
 	struct dwc3		*dwc = dep->dwc;
@@ -969,34 +1072,61 @@
 	list_add_tail(&req->list, &dep->request_list);
 
 	/*
-	 * There is one special case: XferNotReady with
-	 * empty list of requests. We need to kick the
-	 * transfer here in that situation, otherwise
-	 * we will be NAKing forever.
+	 * There are a few special cases:
 	 *
-	 * If we get XferNotReady before gadget driver
-	 * has a chance to queue a request, we will ACK
-	 * the IRQ but won't be able to receive the data
-	 * until the next request is queued. The following
-	 * code is handling exactly that.
+	 * 1. XferNotReady with empty list of requests. We need to kick the
+	 *    transfer here in that situation, otherwise we will be NAKing
+	 *    forever. If we get XferNotReady before gadget driver has a
+	 *    chance to queue a request, we will ACK the IRQ but won't be
+	 *    able to receive the data until the next request is queued.
+	 *    The following code is handling exactly that.
+	 *
 	 */
 	if (dep->flags & DWC3_EP_PENDING_REQUEST) {
-		int ret;
-		int start_trans;
+		int	ret;
 
-		start_trans = 1;
-		if (usb_endpoint_xfer_isoc(dep->desc) &&
-				(dep->flags & DWC3_EP_BUSY))
-			start_trans = 0;
+		/*
+		 * If xfernotready is already elapsed and it is a case
+		 * of isoc transfer, then issue END TRANSFER, so that
+		 * you can receive xfernotready again and can have
+		 * notion of current microframe.
+		 */
+		if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
+			dwc3_stop_active_transfer(dwc, dep->number);
+			return 0;
+		}
 
-		ret = __dwc3_gadget_kick_transfer(dep, 0, start_trans);
-		if (ret && ret != -EBUSY) {
-			struct dwc3	*dwc = dep->dwc;
-
+		ret = __dwc3_gadget_kick_transfer(dep, 0, true);
+		if (ret && ret != -EBUSY)
 			dev_dbg(dwc->dev, "%s: failed to kick transfers\n",
 					dep->name);
-		}
-	};
+	}
+
+	/*
+	 * 2. XferInProgress on Isoc EP with an active transfer. We need to
+	 *    kick the transfer here after queuing a request, otherwise the
+	 *    core may not see the modified TRB(s).
+	 */
+	if (usb_endpoint_xfer_isoc(dep->endpoint.desc) &&
+			(dep->flags & DWC3_EP_BUSY) &&
+			!(dep->flags & DWC3_EP_MISSED_ISOC)) {
+		WARN_ON_ONCE(!dep->resource_index);
+		ret = __dwc3_gadget_kick_transfer(dep, dep->resource_index,
+				false);
+		if (ret && ret != -EBUSY)
+			dev_dbg(dwc->dev, "%s: failed to kick transfers\n",
+					dep->name);
+	}
+
+	/*
+	 * 3. Missed ISOC Handling. We need to start isoc transfer on the saved
+	 * uframe number.
+	 */
+	if (usb_endpoint_xfer_isoc(dep->endpoint.desc) &&
+		(dep->flags & DWC3_EP_MISSED_ISOC)) {
+			__dwc3_gadget_start_isoc(dwc, dep, dep->current_uf);
+			dep->flags &= ~DWC3_EP_MISSED_ISOC;
+	}
 
 	return 0;
 }
@@ -1012,7 +1142,7 @@
 
 	int				ret;
 
-	if (!dep->desc) {
+	if (!dep->endpoint.desc) {
 		dev_dbg(dwc->dev, "trying to queue request %p to disabled %s\n",
 				request, ep->name);
 		return -ESHUTDOWN;
@@ -1021,6 +1151,9 @@
 	dev_vdbg(dwc->dev, "queing request %p to %s length %d\n",
 			request, ep->name, request->length);
 
+	WARN(!dep->direction && (request->length % ep->desc->wMaxPacketSize),
+		"trying to queue unaligned request (%d)\n", request->length);
+
 	spin_lock_irqsave(&dwc->lock, flags);
 	ret = __dwc3_gadget_ep_queue(dep, req);
 	spin_unlock_irqrestore(&dwc->lock, flags);
@@ -1055,7 +1188,7 @@
 		if (r == req) {
 			/* wait until it is processed */
 			dwc3_stop_active_transfer(dwc, dep->number);
-			goto out0;
+			goto out1;
 		}
 		dev_err(dwc->dev, "request %p was not queued to %s\n",
 				request, ep->name);
@@ -1063,6 +1196,7 @@
 		goto out0;
 	}
 
+out1:
 	/* giveback the request */
 	dwc3_gadget_giveback(dep, req, -ECONNRESET);
 
@@ -1081,15 +1215,6 @@
 	memset(&params, 0x00, sizeof(params));
 
 	if (value) {
-		if (dep->number == 0 || dep->number == 1) {
-			/*
-			 * Whenever EP0 is stalled, we will restart
-			 * the state machine, thus moving back to
-			 * Setup Phase
-			 */
-			dwc->ep0state = EP0_SETUP_PHASE;
-		}
-
 		ret = dwc3_send_gadget_ep_cmd(dwc, dep->number,
 			DWC3_DEPCMD_SETSTALL, &params);
 		if (ret)
@@ -1099,9 +1224,6 @@
 		else
 			dep->flags |= DWC3_EP_STALL;
 	} else {
-		if (dep->flags & DWC3_EP_WEDGE)
-			return 0;
-
 		ret = dwc3_send_gadget_ep_cmd(dwc, dep->number,
 			DWC3_DEPCMD_CLEARSTALL, &params);
 		if (ret)
@@ -1109,7 +1231,7 @@
 					value ? "set" : "clear",
 					dep->name);
 		else
-			dep->flags &= ~DWC3_EP_STALL;
+			dep->flags &= ~(DWC3_EP_STALL | DWC3_EP_WEDGE);
 	}
 
 	return ret;
@@ -1126,7 +1248,7 @@
 
 	spin_lock_irqsave(&dwc->lock, flags);
 
-	if (usb_endpoint_xfer_isoc(dep->desc)) {
+	if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
 		dev_err(dwc->dev, "%s is of Isochronous type\n", dep->name);
 		ret = -EINVAL;
 		goto out;
@@ -1149,7 +1271,10 @@
 	dep->flags |= DWC3_EP_WEDGE;
 	spin_unlock_irqrestore(&dwc->lock, flags);
 
-	return dwc3_gadget_ep_set_halt(ep, 1);
+	if (dep->number == 0 || dep->number == 1)
+		return dwc3_gadget_ep0_set_halt(ep, 1);
+	else
+		return dwc3_gadget_ep_set_halt(ep, 1);
 }
 
 /* -------------------------------------------------------------------------- */
@@ -1167,7 +1292,7 @@
 	.free_request	= dwc3_gadget_ep_free_request,
 	.queue		= dwc3_gadget_ep0_queue,
 	.dequeue	= dwc3_gadget_ep_dequeue,
-	.set_halt	= dwc3_gadget_ep_set_halt,
+	.set_halt	= dwc3_gadget_ep0_set_halt,
 	.set_wedge	= dwc3_gadget_ep_set_wedge,
 };
 
@@ -1243,9 +1368,13 @@
 		goto out;
 	}
 
-	/* write zeroes to Link Change Request */
-	reg &= ~DWC3_DCTL_ULSTCHNGREQ_MASK;
-	dwc3_writel(dwc->regs, DWC3_DCTL, reg);
+	/* Recent versions do this automatically */
+	if (dwc->revision < DWC3_REVISION_194A) {
+		/* write zeroes to Link Change Request */
+		reg = dwc3_readl(dwc->regs, DWC3_DCTL);
+		reg &= ~DWC3_DCTL_ULSTCHNGREQ_MASK;
+		dwc3_writel(dwc->regs, DWC3_DCTL, reg);
+	}
 
 	/* poll until Link State changes to ON */
 	timeout = jiffies + msecs_to_jiffies(100);
@@ -1282,16 +1411,21 @@
 	return 0;
 }
 
-static void dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on)
+static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on)
 {
 	u32			reg;
 	u32			timeout = 500;
 
 	reg = dwc3_readl(dwc->regs, DWC3_DCTL);
 	if (is_on) {
-		reg &= ~DWC3_DCTL_TRGTULST_MASK;
-		reg |= (DWC3_DCTL_RUN_STOP
-				| DWC3_DCTL_TRGTULST_RX_DET);
+		if (dwc->revision <= DWC3_REVISION_187A) {
+			reg &= ~DWC3_DCTL_TRGTULST_MASK;
+			reg |= DWC3_DCTL_TRGTULST_RX_DET;
+		}
+
+		if (dwc->revision >= DWC3_REVISION_194A)
+			reg &= ~DWC3_DCTL_KEEP_CONNECT;
+		reg |= DWC3_DCTL_RUN_STOP;
 	} else {
 		reg &= ~DWC3_DCTL_RUN_STOP;
 	}
@@ -1309,7 +1443,7 @@
 		}
 		timeout--;
 		if (!timeout)
-			break;
+			return -ETIMEDOUT;
 		udelay(1);
 	} while (1);
 
@@ -1317,12 +1451,26 @@
 			dwc->gadget_driver
 			? dwc->gadget_driver->function : "no-function",
 			is_on ? "connect" : "disconnect");
+
+	return 0;
+}
+
+static int dwc3_gadget_vbus_draw(struct usb_gadget *g, unsigned mA)
+{
+	struct dwc3		*dwc = gadget_to_dwc(g);
+	struct dwc3_otg		*dotg = dwc->dotg;
+
+	if (dotg && dotg->otg.phy)
+		return usb_phy_set_power(dotg->otg.phy, mA);
+
+	return -ENOTSUPP;
 }
 
 static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on)
 {
 	struct dwc3		*dwc = gadget_to_dwc(g);
 	unsigned long		flags;
+	int			ret;
 
 	is_on = !!is_on;
 
@@ -1342,17 +1490,18 @@
 		return 0;
 	}
 
-	dwc3_gadget_run_stop(dwc, is_on);
+	ret = dwc3_gadget_run_stop(dwc, is_on);
 
 	spin_unlock_irqrestore(&dwc->lock, flags);
 
-	return 0;
+	return ret;
 }
 
 static int dwc3_gadget_vbus_session(struct usb_gadget *_gadget, int is_active)
 {
 	struct dwc3 *dwc = gadget_to_dwc(_gadget);
 	unsigned long flags;
+	int ret = 0;
 
 	if (!dwc->dotg)
 		return -EPERM;
@@ -1374,15 +1523,46 @@
 			 * Both vbus was activated by otg and pullup was
 			 * signaled by the gadget driver.
 			 */
-			dwc3_gadget_run_stop(dwc, 1);
+			ret = dwc3_gadget_run_stop(dwc, 1);
 		} else {
-			dwc3_gadget_run_stop(dwc, 0);
+			ret = dwc3_gadget_run_stop(dwc, 0);
 		}
 	}
 
 	spin_unlock_irqrestore(&dwc->lock, flags);
 
-	return 0;
+	return ret;
+}
+
+/* Required gadget re-initialization before switching to gadget in OTG mode */
+void dwc3_gadget_restart(struct dwc3 *dwc)
+{
+	struct dwc3_ep		*dep;
+	int			ret = 0;
+
+	/* reinitialize physical ep0-1 */
+
+	dwc->delayed_status = false;
+
+	dep = dwc->eps[0];
+	dep->flags = 0;
+	ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, false);
+	if (ret) {
+		dev_err(dwc->dev, "failed to enable %s\n", dep->name);
+		return;
+	}
+
+	dep = dwc->eps[1];
+	dep->flags = 0;
+	ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, false);
+	if (ret) {
+		dev_err(dwc->dev, "failed to enable %s\n", dep->name);
+		return;
+	}
+
+	/* begin to receive SETUP packets */
+	dwc->ep0state = EP0_SETUP_PHASE;
+	dwc3_ep0_out_start(dwc);
 }
 
 static int dwc3_gadget_start(struct usb_gadget *g,
@@ -1409,7 +1589,24 @@
 
 	reg = dwc3_readl(dwc->regs, DWC3_DCFG);
 	reg &= ~(DWC3_DCFG_SPEED_MASK);
-	reg |= dwc->maximum_speed;
+
+	/**
+	 * WORKAROUND: DWC3 revision < 2.20a have an issue
+	 * which would cause metastability state on Run/Stop
+	 * bit if we try to force the IP to USB2-only mode.
+	 *
+	 * Because of that, we cannot configure the IP to any
+	 * speed other than the SuperSpeed
+	 *
+	 * Refers to:
+	 *
+	 * STAR#9000525659: Clock Domain Crossing on DCTL in
+	 * USB 2.0 Mode
+	 */
+	if (dwc->revision < DWC3_REVISION_220A)
+		reg |= DWC3_DCFG_SUPERSPEED;
+	else
+		reg |= dwc->maximum_speed;
 	dwc3_writel(dwc->regs, DWC3_DCFG, reg);
 
 	dwc->start_config_issued = false;
@@ -1418,14 +1615,14 @@
 	dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512);
 
 	dep = dwc->eps[0];
-	ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL);
+	ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, false);
 	if (ret) {
 		dev_err(dwc->dev, "failed to enable %s\n", dep->name);
 		goto err0;
 	}
 
 	dep = dwc->eps[1];
-	ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL);
+	ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, false);
 	if (ret) {
 		dev_err(dwc->dev, "failed to enable %s\n", dep->name);
 		goto err1;
@@ -1466,11 +1663,13 @@
 
 	return 0;
 }
+
 static const struct usb_gadget_ops dwc3_gadget_ops = {
 	.get_frame		= dwc3_gadget_get_frame,
 	.wakeup			= dwc3_gadget_wakeup,
 	.set_selfpowered	= dwc3_gadget_set_selfpowered,
 	.vbus_session		= dwc3_gadget_vbus_session,
+	.vbus_draw		= dwc3_gadget_vbus_draw,
 	.pullup			= dwc3_gadget_pullup,
 	.udc_start		= dwc3_gadget_start,
 	.udc_stop		= dwc3_gadget_stop,
@@ -1557,6 +1756,7 @@
 	struct dwc3_trb		*trb;
 	unsigned int		count;
 	unsigned int		s_pkt = 0;
+	unsigned int		trb_status;
 
 	do {
 		req = next_request(&dep->req_queued);
@@ -1582,9 +1782,18 @@
 
 		if (dep->direction) {
 			if (count) {
-				dev_err(dwc->dev, "incomplete IN transfer %s\n",
-						dep->name);
-				status = -ECONNRESET;
+				trb_status = DWC3_TRB_SIZE_TRBSTS(trb->size);
+				if (trb_status == DWC3_TRBSTS_MISSED_ISOC) {
+					dev_dbg(dwc->dev, "incomplete IN transfer %s\n",
+							dep->name);
+					dep->current_uf = event->parameters &
+						~(dep->interval - 1);
+					dep->flags |= DWC3_EP_MISSED_ISOC;
+				} else {
+					dev_err(dwc->dev, "incomplete IN transfer %s\n",
+							dep->name);
+					status = -ECONNRESET;
+				}
 			}
 		} else {
 			if (count && (event->status & DEPEVT_STATUS_SHORT))
@@ -1603,7 +1812,8 @@
 		if (s_pkt)
 			break;
 		if ((event->status & DEPEVT_STATUS_LST) &&
-				(trb->ctrl & DWC3_TRB_CTRL_LST))
+				(trb->ctrl & (DWC3_TRB_CTRL_LST |
+						DWC3_TRB_CTRL_HWO)))
 			break;
 		if ((event->status & DEPEVT_STATUS_IOC) &&
 				(trb->ctrl & DWC3_TRB_CTRL_IOC))
@@ -1639,7 +1849,7 @@
 		int		i;
 
 		for (i = 0; i < DWC3_ENDPOINTS_NUM; i++) {
-			struct dwc3_ep	*dep = dwc->eps[i];
+			dep = dwc->eps[i];
 
 			if (!(dep->flags & DWC3_EP_ENABLED))
 				continue;
@@ -1656,65 +1866,6 @@
 	}
 }
 
-static void dwc3_gadget_start_isoc(struct dwc3 *dwc,
-		struct dwc3_ep *dep, const struct dwc3_event_depevt *event)
-{
-	u32 uf, mask;
-
-	if (list_empty(&dep->request_list)) {
-		dev_vdbg(dwc->dev, "ISOC ep %s run out for requests.\n",
-			dep->name);
-		return;
-	}
-
-	mask = ~(dep->interval - 1);
-	uf = event->parameters & mask;
-	/* 4 micro frames in the future */
-	uf += dep->interval * 4;
-
-	__dwc3_gadget_kick_transfer(dep, uf, 1);
-}
-
-static void dwc3_process_ep_cmd_complete(struct dwc3_ep *dep,
-		const struct dwc3_event_depevt *event)
-{
-	struct dwc3 *dwc = dep->dwc;
-	struct dwc3_event_depevt mod_ev = *event;
-
-	/*
-	 * We were asked to remove one request. It is possible that this
-	 * request and a few others were started together and have the same
-	 * transfer index. Since we stopped the complete endpoint we don't
-	 * know how many requests were already completed (and not yet)
-	 * reported and how could be done (later). We purge them all until
-	 * the end of the list.
-	 */
-	mod_ev.status = DEPEVT_STATUS_LST;
-	dwc3_cleanup_done_reqs(dwc, dep, &mod_ev, -ESHUTDOWN);
-	dep->flags &= ~DWC3_EP_BUSY;
-	/* pending requests are ignored and are queued on XferNotReady */
-}
-
-static void dwc3_ep_cmd_compl(struct dwc3_ep *dep,
-		const struct dwc3_event_depevt *event)
-{
-	u32 param = event->parameters;
-	u32 cmd_type = (param >> 8) & ((1 << 5) - 1);
-
-	switch (cmd_type) {
-	case DWC3_DEPCMD_ENDTRANSFER:
-		dwc3_process_ep_cmd_complete(dep, event);
-		break;
-	case DWC3_DEPCMD_STARTTRANSFER:
-		dep->res_trans_idx = param & 0x7f;
-		break;
-	default:
-		printk(KERN_ERR "%s() unknown /unexpected type: %d\n",
-				__func__, cmd_type);
-		break;
-	};
-}
-
 static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
 		const struct dwc3_event_depevt *event)
 {
@@ -1723,6 +1874,9 @@
 
 	dep = dwc->eps[epnum];
 
+	if (!(dep->flags & DWC3_EP_ENABLED))
+		return;
+
 	dev_vdbg(dwc->dev, "%s: %s\n", dep->name,
 			dwc3_ep_event_string(event->endpoint_event));
 
@@ -1733,9 +1887,9 @@
 
 	switch (event->endpoint_event) {
 	case DWC3_DEPEVT_XFERCOMPLETE:
-		dep->res_trans_idx = 0;
+		dep->resource_index = 0;
 
-		if (usb_endpoint_xfer_isoc(dep->desc)) {
+		if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
 			dev_dbg(dwc->dev, "%s is an Isochronous endpoint\n",
 					dep->name);
 			return;
@@ -1744,7 +1898,7 @@
 		dwc3_endpoint_transfer_complete(dwc, dep, event, 1);
 		break;
 	case DWC3_DEPEVT_XFERINPROGRESS:
-		if (!usb_endpoint_xfer_isoc(dep->desc)) {
+		if (!usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
 			dev_dbg(dwc->dev, "%s is not an Isochronous endpoint\n",
 					dep->name);
 			return;
@@ -1753,7 +1907,7 @@
 		dwc3_endpoint_transfer_complete(dwc, dep, event, 0);
 		break;
 	case DWC3_DEPEVT_XFERNOTREADY:
-		if (usb_endpoint_xfer_isoc(dep->desc)) {
+		if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
 			dwc3_gadget_start_isoc(dwc, dep, event);
 		} else {
 			int ret;
@@ -1774,7 +1928,7 @@
 
 		break;
 	case DWC3_DEPEVT_STREAMEVT:
-		if (!usb_endpoint_xfer_bulk(dep->desc)) {
+		if (!usb_endpoint_xfer_bulk(dep->endpoint.desc)) {
 			dev_err(dwc->dev, "Stream event for non-Bulk %s\n",
 					dep->name);
 			return;
@@ -1796,7 +1950,7 @@
 		dev_dbg(dwc->dev, "%s FIFO Overrun\n", dep->name);
 		break;
 	case DWC3_DEPEVT_EPCMDCMPLT:
-		dwc3_ep_cmd_compl(dep, event);
+		dev_vdbg(dwc->dev, "Endpoint Command Complete\n");
 		break;
 	}
 }
@@ -1819,16 +1973,37 @@
 
 	dep = dwc->eps[epnum];
 
-	WARN_ON(!dep->res_trans_idx);
-	if (dep->res_trans_idx) {
-		cmd = DWC3_DEPCMD_ENDTRANSFER;
-		cmd |= DWC3_DEPCMD_HIPRI_FORCERM | DWC3_DEPCMD_CMDIOC;
-		cmd |= DWC3_DEPCMD_PARAM(dep->res_trans_idx);
-		memset(&params, 0, sizeof(params));
-		ret = dwc3_send_gadget_ep_cmd(dwc, dep->number, cmd, &params);
-		WARN_ON_ONCE(ret);
-		dep->res_trans_idx = 0;
-	}
+	if (!dep->resource_index)
+		return;
+
+	/*
+	 * NOTICE: We are violating what the Databook says about the
+	 * EndTransfer command. Ideally we would _always_ wait for the
+	 * EndTransfer Command Completion IRQ, but that's causing too
+	 * much trouble synchronizing between us and gadget driver.
+	 *
+	 * We have discussed this with the IP Provider and it was
+	 * suggested to giveback all requests here, but give HW some
+	 * extra time to synchronize with the interconnect. We're using
+	 * an arbitraty 100us delay for that.
+	 *
+	 * Note also that a similar handling was tested by Synopsys
+	 * (thanks a lot Paul) and nothing bad has come out of it.
+	 * In short, what we're doing is:
+	 *
+	 * - Issue EndTransfer WITH CMDIOC bit set
+	 * - Wait 100us
+	 */
+
+	cmd = DWC3_DEPCMD_ENDTRANSFER;
+	cmd |= DWC3_DEPCMD_HIPRI_FORCERM | DWC3_DEPCMD_CMDIOC;
+	cmd |= DWC3_DEPCMD_PARAM(dep->resource_index);
+	memset(&params, 0, sizeof(params));
+	ret = dwc3_send_gadget_ep_cmd(dwc, dep->number, cmd, &params);
+	WARN_ON_ONCE(ret);
+	dep->resource_index = 0;
+
+	udelay(100);
 }
 
 static void dwc3_stop_active_transfers(struct dwc3 *dwc)
@@ -1871,11 +2046,9 @@
 
 static void dwc3_gadget_disconnect_interrupt(struct dwc3 *dwc)
 {
+	int			reg;
+
 	dev_vdbg(dwc->dev, "%s\n", __func__);
-#if 0
-	XXX
-	U1/U2 is powersave optimization. Skip it for now. Anyway we need to
-	enable it before we can disable it.
 
 	reg = dwc3_readl(dwc->regs, DWC3_DCTL);
 	reg &= ~DWC3_DCTL_INITU1ENA;
@@ -1883,9 +2056,7 @@
 
 	reg &= ~DWC3_DCTL_INITU2ENA;
 	dwc3_writel(dwc->regs, DWC3_DCTL, reg);
-#endif
 
-	dwc3_stop_active_transfers(dwc);
 	dwc3_disconnect_gadget(dwc);
 	dwc->start_config_issued = false;
 
@@ -1893,30 +2064,30 @@
 	dwc->setup_packet_pending = false;
 }
 
-static void dwc3_gadget_usb3_phy_power(struct dwc3 *dwc, int on)
+static void dwc3_gadget_usb3_phy_suspend(struct dwc3 *dwc, int suspend)
 {
 	u32			reg;
 
 	reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0));
 
-	if (on)
-		reg &= ~DWC3_GUSB3PIPECTL_SUSPHY;
-	else
+	if (suspend)
 		reg |= DWC3_GUSB3PIPECTL_SUSPHY;
+	else
+		reg &= ~DWC3_GUSB3PIPECTL_SUSPHY;
 
 	dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), reg);
 }
 
-static void dwc3_gadget_usb2_phy_power(struct dwc3 *dwc, int on)
+static void dwc3_gadget_usb2_phy_suspend(struct dwc3 *dwc, int suspend)
 {
 	u32			reg;
 
 	reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
 
-	if (on)
-		reg &= ~DWC3_GUSB2PHYCFG_SUSPHY;
-	else
+	if (suspend)
 		reg |= DWC3_GUSB2PHYCFG_SUSPHY;
+	else
+		reg &= ~DWC3_GUSB2PHYCFG_SUSPHY;
 
 	dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
 }
@@ -1924,6 +2095,7 @@
 static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc)
 {
 	u32			reg;
+	struct dwc3_otg		*dotg = dwc->dotg;
 
 	dev_vdbg(dwc->dev, "%s\n", __func__);
 
@@ -1961,9 +2133,15 @@
 	/* after reset -> Default State */
 	dwc->dev_state = DWC3_DEFAULT_STATE;
 
-	/* Enable PHYs */
-	dwc3_gadget_usb2_phy_power(dwc, true);
-	dwc3_gadget_usb3_phy_power(dwc, true);
+	/* Recent versions support automatic phy suspend and don't need this */
+	if (dwc->revision < DWC3_REVISION_194A) {
+		/* Resume PHYs */
+		dwc3_gadget_usb2_phy_suspend(dwc, false);
+		dwc3_gadget_usb3_phy_suspend(dwc, false);
+	}
+
+	if (dotg && dotg->otg.phy)
+		usb_phy_set_power(dotg->otg.phy, 0);
 
 	if (dwc->gadget.speed != USB_SPEED_UNKNOWN)
 		dwc3_disconnect_gadget(dwc);
@@ -2008,16 +2186,16 @@
 	dwc3_writel(dwc->regs, DWC3_GCTL, reg);
 }
 
-static void dwc3_gadget_disable_phy(struct dwc3 *dwc, u8 speed)
+static void dwc3_gadget_phy_suspend(struct dwc3 *dwc, u8 speed)
 {
 	switch (speed) {
 	case USB_SPEED_SUPER:
-		dwc3_gadget_usb2_phy_power(dwc, false);
+		dwc3_gadget_usb2_phy_suspend(dwc, true);
 		break;
 	case USB_SPEED_HIGH:
 	case USB_SPEED_FULL:
 	case USB_SPEED_LOW:
-		dwc3_gadget_usb3_phy_power(dwc, false);
+		dwc3_gadget_usb3_phy_suspend(dwc, true);
 		break;
 	}
 }
@@ -2080,18 +2258,21 @@
 		break;
 	}
 
-	/* Disable unneded PHY */
-	dwc3_gadget_disable_phy(dwc, dwc->gadget.speed);
+	/* Recent versions support automatic phy suspend and don't need this */
+	if (dwc->revision < DWC3_REVISION_194A) {
+		/* Suspend unneeded PHY */
+		dwc3_gadget_phy_suspend(dwc, dwc->gadget.speed);
+	}
 
 	dep = dwc->eps[0];
-	ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL);
+	ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, true);
 	if (ret) {
 		dev_err(dwc->dev, "failed to enable %s\n", dep->name);
 		return;
 	}
 
 	dep = dwc->eps[1];
-	ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL);
+	ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, true);
 	if (ret) {
 		dev_err(dwc->dev, "failed to enable %s\n", dep->name);
 		return;
@@ -2169,6 +2350,13 @@
 		}
 	}
 
+	if (next == DWC3_LINK_STATE_U0) {
+		if (dwc->link_state == DWC3_LINK_STATE_U3)
+			dwc->gadget_driver->resume(&dwc->gadget);
+	} else if (next == DWC3_LINK_STATE_U3) {
+		dwc->gadget_driver->suspend(&dwc->gadget);
+	}
+
 	dwc->link_state = next;
 
 	dev_vdbg(dwc->dev, "%s link %d\n", __func__, dwc->link_state);
@@ -2344,8 +2532,7 @@
 		goto err1;
 	}
 
-	dwc->setup_buf = kzalloc(sizeof(*dwc->setup_buf) * 2,
-			GFP_KERNEL);
+	dwc->setup_buf = kzalloc(DWC3_EP0_BOUNCE_SIZE, GFP_KERNEL);
 	if (!dwc->setup_buf) {
 		dev_err(dwc->dev, "failed to allocate setup buffer\n");
 		ret = -ENOMEM;
@@ -2353,7 +2540,8 @@
 	}
 
 	dwc->ep0_bounce = dma_alloc_coherent(dwc->dev,
-			512, &dwc->ep0_bounce_addr, GFP_KERNEL);
+			DWC3_EP0_BOUNCE_SIZE, &dwc->ep0_bounce_addr,
+			GFP_KERNEL);
 	if (!dwc->ep0_bounce) {
 		dev_err(dwc->dev, "failed to allocate ep0 bounce buffer\n");
 		ret = -ENOMEM;
@@ -2363,7 +2551,7 @@
 	dev_set_name(&dwc->gadget.dev, "gadget");
 
 	dwc->gadget.ops			= &dwc3_gadget_ops;
-	dwc->gadget.max_speed		= USB_SPEED_SUPER;
+	dwc->gadget.max_speed		= USB_SPEED_HIGH;
 	dwc->gadget.speed		= USB_SPEED_UNKNOWN;
 	dwc->gadget.dev.parent		= dwc->dev;
 	dwc->gadget.sg_supported	= true;
@@ -2394,6 +2582,10 @@
 		goto err5;
 	}
 
+	reg = dwc3_readl(dwc->regs, DWC3_DCFG);
+	reg |= DWC3_DCFG_LPM_CAP;
+	dwc3_writel(dwc->regs, DWC3_DCFG, reg);
+
 	/* Enable all but Start and End of Frame IRQs */
 	reg = (DWC3_DEVTEN_EVNTOVERFLOWEN |
 			DWC3_DEVTEN_CMDCMPLTEN |
@@ -2405,6 +2597,24 @@
 			DWC3_DEVTEN_DISCONNEVTEN);
 	dwc3_writel(dwc->regs, DWC3_DEVTEN, reg);
 
+	/* Enable USB2 LPM and automatic phy suspend only on recent versions */
+	if (dwc->revision >= DWC3_REVISION_194A) {
+		reg = dwc3_readl(dwc->regs, DWC3_DCFG);
+		reg |= DWC3_DCFG_LPM_CAP;
+		dwc3_writel(dwc->regs, DWC3_DCFG, reg);
+
+		reg = dwc3_readl(dwc->regs, DWC3_DCTL);
+		reg &= ~(DWC3_DCTL_HIRD_THRES_MASK | DWC3_DCTL_L1_HIBER_EN);
+
+		/* TODO: This should be configurable */
+		reg |= DWC3_DCTL_HIRD_THRES(28);
+
+		dwc3_writel(dwc->regs, DWC3_DCTL, reg);
+
+		dwc3_gadget_usb2_phy_suspend(dwc, false);
+		dwc3_gadget_usb3_phy_suspend(dwc, false);
+	}
+
 	ret = device_register(&dwc->gadget.dev);
 	if (ret) {
 		dev_err(dwc->dev, "failed to register gadget device\n");
@@ -2445,8 +2655,8 @@
 	dwc3_gadget_free_endpoints(dwc);
 
 err4:
-	dma_free_coherent(dwc->dev, 512, dwc->ep0_bounce,
-			dwc->ep0_bounce_addr);
+	dma_free_coherent(dwc->dev, DWC3_EP0_BOUNCE_SIZE,
+			dwc->ep0_bounce, dwc->ep0_bounce_addr);
 
 err3:
 	kfree(dwc->setup_buf);
@@ -2480,8 +2690,8 @@
 
 	dwc3_gadget_free_endpoints(dwc);
 
-	dma_free_coherent(dwc->dev, 512, dwc->ep0_bounce,
-			dwc->ep0_bounce_addr);
+	dma_free_coherent(dwc->dev, DWC3_EP0_BOUNCE_SIZE,
+			dwc->ep0_bounce, dwc->ep0_bounce_addr);
 
 	kfree(dwc->setup_buf);
 
diff --git a/drivers/usb/dwc3/gadget.h b/drivers/usb/dwc3/gadget.h
index 662682e..dc7a3c1 100644
--- a/drivers/usb/dwc3/gadget.h
+++ b/drivers/usb/dwc3/gadget.h
@@ -66,7 +66,12 @@
 #define DWC3_DEPCFG_FIFO_NUMBER(n)	((n) << 17)
 #define DWC3_DEPCFG_BURST_SIZE(n)	((n) << 22)
 #define DWC3_DEPCFG_DATA_SEQ_NUM(n)	((n) << 26)
+/* This applies for core versions earlier than 1.94a */
 #define DWC3_DEPCFG_IGN_SEQ_NUM		(1 << 31)
+/* These apply for core versions 1.94a and later */
+#define DWC3_DEPCFG_ACTION_INIT		(0 << 30)
+#define DWC3_DEPCFG_ACTION_RESTORE	(1 << 30)
+#define DWC3_DEPCFG_ACTION_MODIFY	(2 << 30)
 
 /* DEPXFERCFG parameter 0 */
 #define DWC3_DEPXFERCFG_NUM_XFER_RES(n)	((n) & 0xffff)
@@ -106,11 +111,13 @@
 void dwc3_ep0_interrupt(struct dwc3 *dwc,
 		const struct dwc3_event_depevt *event);
 void dwc3_ep0_out_start(struct dwc3 *dwc);
+int dwc3_gadget_ep0_set_halt(struct usb_ep *ep, int value);
 int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request,
 		gfp_t gfp_flags);
 int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value);
 int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep,
 		unsigned cmd, struct dwc3_gadget_ep_cmd_params *params);
+int dwc3_send_gadget_generic_command(struct dwc3 *dwc, int cmd, u32 param);
 dma_addr_t dwc3_trb_dma_offset(struct dwc3_ep *dep,
 		struct dwc3_trb *trb);
 
diff --git a/drivers/usb/dwc3/host.c b/drivers/usb/dwc3/host.c
index 099708b..644a779 100644
--- a/drivers/usb/dwc3/host.c
+++ b/drivers/usb/dwc3/host.c
@@ -78,10 +78,13 @@
 		goto err1;
 	}
 
-	ret = platform_device_add(xhci);
-	if (ret) {
-		dev_err(dwc->dev, "failed to register xHCI device\n");
-		goto err1;
+	/* Add XHCI device if !OTG, otherwise OTG takes care of this */
+	if (!dwc->dotg) {
+		ret = platform_device_add(xhci);
+		if (ret) {
+			dev_err(dwc->dev, "failed to register xHCI device\n");
+			goto err1;
+		}
 	}
 
 	return 0;
@@ -95,5 +98,6 @@
 
 void dwc3_host_exit(struct dwc3 *dwc)
 {
-	platform_device_unregister(dwc->xhci);
+	if (!dwc->dotg)
+		platform_device_unregister(dwc->xhci);
 }
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index de9a7aa..ee1ff46 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -143,7 +143,6 @@
 
 config USB_ATMEL_USBA
 	tristate "Atmel USBA"
-	select USB_GADGET_DUALSPEED
 	depends on AVR32 || ARCH_AT91SAM9RL || ARCH_AT91SAM9G45
 	help
 	  USBA is the integrated high-speed USB Device controller on
@@ -152,7 +151,6 @@
 config USB_FSL_USB2
 	tristate "Freescale Highspeed USB DR Peripheral Controller"
 	depends on FSL_SOC || ARCH_MXC
-	select USB_GADGET_DUALSPEED
 	select USB_FSL_MPH_DR_OF if OF
 	help
 	   Some of Freescale PowerPC processors have a High Speed
@@ -168,7 +166,6 @@
 config USB_FUSB300
 	tristate "Faraday FUSB300 USB Peripheral Controller"
 	depends on !PHYS_ADDR_T_64BIT
-	select USB_GADGET_DUALSPEED
 	help
 	   Faraday usb device controller FUSB300 driver
 
@@ -216,7 +213,6 @@
 
 config USB_R8A66597
 	tristate "Renesas R8A66597 USB Peripheral Controller"
-	select USB_GADGET_DUALSPEED
 	help
 	   R8A66597 is a discrete USB host and peripheral controller chip that
 	   supports both full and high speed USB 2.0 data transfers.
@@ -229,7 +225,6 @@
 config USB_RENESAS_USBHS_UDC
 	tristate 'Renesas USBHS controller'
 	depends on USB_RENESAS_USBHS
-	select USB_GADGET_DUALSPEED
 	help
 	   Renesas USBHS is a discrete USB host and peripheral controller chip
 	   that supports both full and high speed USB 2.0 data transfers.
@@ -257,7 +252,6 @@
 config USB_S3C_HSOTG
 	tristate "S3C HS/OtG USB Device controller"
 	depends on S3C_DEV_USB_HSOTG
-	select USB_GADGET_DUALSPEED
 	help
 	  The Samsung S3C64XX USB2.0 high-speed gadget controller
 	  integrated into the S3C64XX series SoC.
@@ -294,7 +288,6 @@
 config USB_S3C_HSUDC
 	tristate "S3C2416, S3C2443 and S3C2450 USB Device Controller"
 	depends on ARCH_S3C24XX
-	select USB_GADGET_DUALSPEED
 	help
 	  Samsung's S3C2416, S3C2443 and S3C2450 is an ARM9 based SoC
 	  integrated with dual speed USB 2.0 device controller. It has
@@ -304,7 +297,6 @@
 
 config USB_MV_UDC
 	tristate "Marvell USB2.0 Device Controller"
-	select USB_GADGET_DUALSPEED
 	help
 	  Marvell Socs (including PXA and MMP series) include a high speed
 	  USB2.0 OTG controller, which can be configured as high speed or
@@ -318,14 +310,12 @@
 config USB_GADGET_MUSB_HDRC
 	tristate "Inventra HDRC USB Peripheral (TI, ADI, ...)"
 	depends on USB_MUSB_HDRC
-	select USB_GADGET_DUALSPEED
 	help
 	  This OTG-capable silicon IP is used in dual designs including
 	  the TI DaVinci, OMAP 243x, OMAP 343x, TUSB 6010, and ADI Blackfin
 
 config USB_M66592
 	tristate "Renesas M66592 USB Peripheral Controller"
-	select USB_GADGET_DUALSPEED
 	help
 	   M66592 is a discrete USB peripheral controller chip that
 	   supports both full and high speed USB 2.0 data transfers.
@@ -342,7 +332,6 @@
 config USB_AMD5536UDC
 	tristate "AMD5536 UDC"
 	depends on PCI
-	select USB_GADGET_DUALSPEED
 	help
 	   The AMD5536 UDC is part of the AMD Geode CS5536, an x86 southbridge.
 	   It is a USB Highspeed DMA capable USB device controller. Beside ep0
@@ -370,7 +359,6 @@
 config USB_CI13XXX_PCI
 	tristate "MIPS USB CI13xxx PCI UDC"
 	depends on PCI
-	select USB_GADGET_DUALSPEED
 	help
 	  MIPS USB IP core family device controller
 	  Currently it only supports IP part number CI13412
@@ -381,7 +369,6 @@
 
 config USB_NET2272
 	tristate "PLX NET2272"
-	select USB_GADGET_DUALSPEED
 	help
 	  PLX NET2272 is a USB peripheral controller which supports
 	  both full and high speed USB 2.0 data transfers.
@@ -405,7 +392,6 @@
 config USB_NET2280
 	tristate "NetChip 228x"
 	depends on PCI
-	select USB_GADGET_DUALSPEED
 	help
 	   NetChip 2280 / 2282 is a PCI based USB peripheral controller which
 	   supports both full and high speed USB 2.0 data transfers.
@@ -436,7 +422,6 @@
 	tristate "Intel Langwell USB Device Controller"
 	depends on PCI
 	depends on !PHYS_ADDR_T_64BIT
-	select USB_GADGET_DUALSPEED
 	help
 	   Intel Langwell USB Device Controller is a High-Speed USB
 	   On-The-Go device controller.
@@ -451,7 +436,6 @@
 config USB_EG20T
 	tristate "Intel EG20T PCH/LAPIS Semiconductor IOH(ML7213/ML7831) UDC"
 	depends on PCI
-	select USB_GADGET_DUALSPEED
 	help
 	  This is a USB device driver for EG20T PCH.
 	  EG20T PCH is the platform controller hub that is used in Intel's
@@ -474,7 +458,6 @@
 config USB_CI13XXX_MSM
 	tristate "MIPS USB CI13xxx for MSM"
 	depends on ARCH_MSM
-	select USB_GADGET_DUALSPEED
 	select USB_MSM_OTG
 	help
 	  MSM SoC has chipidea USB controller.  This driver uses
@@ -491,7 +474,6 @@
 config USB_CI13XXX_MSM_HSIC
 	tristate "MIPS HSIC CI13xxx for MSM"
 	depends on ARCH_MSM
-	select USB_GADGET_DUALSPEED
 	help
 	  MSM SoC has chipidea USB controller.  This driver uses
 	  ci13xxx_udc core. Support USB-HSIC core.
@@ -504,7 +486,6 @@
 	tristate "DesignWare USB3.0 (DRD) Controller for MSM"
 	depends on ARCH_MSM
 	select USB_DWC3
-	select USB_GADGET_DUALSPEED
 	select USB_GADGET_SELECTED
 	help
 	  The DesignWare USB3.0 controller is a SuperSpeed USB3.0 Controller
@@ -516,8 +497,6 @@
 	tristate "DesignWare USB3.0 (DRD) Controller for OMAP"
 	depends on ARCH_OMAP
 	select USB_DWC3
-	select USB_GADGET_DUALSPEED
-	select USB_GADGET_SUPERSPEED
 	select USB_GADGET_SELECTED
 	help
 	  DesignWare USB3.0 controller is a SuperSpeed USB3.0 Controller
@@ -533,7 +512,6 @@
 config USB_MSM_72K
 	tristate "MSM 72K Device Controller"
 	depends on ARCH_MSM
-	select USB_GADGET_DUALSPEED
 	help
 	   USB gadget driver for Qualcomm MSM 72K architecture.
 
@@ -544,8 +522,6 @@
 config USB_DUMMY_HCD
 	tristate "Dummy HCD (DEVELOPMENT)"
 	depends on USB=y || (USB=m && USB_GADGET=m)
-	select USB_GADGET_DUALSPEED
-	select USB_GADGET_SUPERSPEED
 	help
 	  This host controller driver emulates USB, looping all data transfer
 	  requests back to a USB "gadget driver" in the same host.  The host
@@ -570,22 +546,6 @@
 
 endmenu
 
-# Selected by UDC drivers that support high-speed operation.
-config USB_GADGET_DUALSPEED
-	bool
-
-# Selected by UDC drivers that support super-speed opperation
-config USB_GADGET_SUPERSPEED
-	bool "Operate as superspeed"
-	depends on USB_GADGET
-	depends on USB_GADGET_DUALSPEED
-	default n
-	help
-	 When a superspeed peripheral controller is selected
-	 (for example DesignWare USB3.0 controller), use this flag to
-	 indicate if the device should operate in superspeed(=y)
-	 or not.
-
 #
 # USB Gadget Drivers
 #
diff --git a/drivers/usb/gadget/android.c b/drivers/usb/gadget/android.c
index 817bfbb..52c19cf 100644
--- a/drivers/usb/gadget/android.c
+++ b/drivers/usb/gadget/android.c
@@ -52,6 +52,7 @@
 #include "f_rmnet_sdio.c"
 #include "f_rmnet_smd_sdio.c"
 #include "f_rmnet.c"
+#include "f_audio_source.c"
 #include "f_mass_storage.c"
 #include "u_serial.c"
 #include "u_sdio.c"
@@ -635,7 +636,7 @@
 static void ecm_qc_function_unbind_config(struct android_usb_function *f,
 						struct usb_configuration *c)
 {
-	gether_qc_cleanup();
+	gether_qc_cleanup_name("ecm0");
 }
 
 static ssize_t ecm_ethaddr_show(struct device *dev,
@@ -699,11 +700,19 @@
 	return mbim_bind_config(c, 0);
 }
 
+static int mbim_function_ctrlrequest(struct android_usb_function *f,
+					struct usb_composite_dev *cdev,
+					const struct usb_ctrlrequest *c)
+{
+	return mbim_ctrlrequest(cdev, c);
+}
+
 static struct android_usb_function mbim_function = {
 	.name		= "usb_mbim",
 	.cleanup	= mbim_function_cleanup,
 	.bind_config	= mbim_function_bind_config,
 	.init		= mbim_function_init,
+	.ctrlrequest	= mbim_function_ctrlrequest,
 };
 
 #ifdef CONFIG_SND_PCM
@@ -1172,7 +1181,7 @@
 static void rndis_qc_function_unbind_config(struct android_usb_function *f,
 						struct usb_configuration *c)
 {
-	gether_qc_cleanup();
+	gether_qc_cleanup_name("rndis0");
 }
 
 static ssize_t rndis_manufacturer_show(struct device *dev,
@@ -1475,6 +1484,68 @@
 	.ctrlrequest	= accessory_function_ctrlrequest,
 };
 
+static int audio_source_function_init(struct android_usb_function *f,
+			struct usb_composite_dev *cdev)
+{
+	struct audio_source_config *config;
+
+	config = kzalloc(sizeof(struct audio_source_config), GFP_KERNEL);
+	if (!config)
+		return -ENOMEM;
+	config->card = -1;
+	config->device = -1;
+	f->config = config;
+	return 0;
+}
+
+static void audio_source_function_cleanup(struct android_usb_function *f)
+{
+	kfree(f->config);
+}
+
+static int audio_source_function_bind_config(struct android_usb_function *f,
+						struct usb_configuration *c)
+{
+	struct audio_source_config *config = f->config;
+
+	return audio_source_bind_config(c, config);
+}
+
+static void audio_source_function_unbind_config(struct android_usb_function *f,
+						struct usb_configuration *c)
+{
+	struct audio_source_config *config = f->config;
+
+	config->card = -1;
+	config->device = -1;
+}
+
+static ssize_t audio_source_pcm_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct android_usb_function *f = dev_get_drvdata(dev);
+	struct audio_source_config *config = f->config;
+
+	/* print PCM card and device numbers */
+	return sprintf(buf, "%d %d\n", config->card, config->device);
+}
+
+static DEVICE_ATTR(pcm, S_IRUGO | S_IWUSR, audio_source_pcm_show, NULL);
+
+static struct device_attribute *audio_source_function_attributes[] = {
+	&dev_attr_pcm,
+	NULL
+};
+
+static struct android_usb_function audio_source_function = {
+	.name		= "audio_source",
+	.init		= audio_source_function_init,
+	.cleanup	= audio_source_function_cleanup,
+	.bind_config	= audio_source_function_bind_config,
+	.unbind_config	= audio_source_function_unbind_config,
+	.attributes	= audio_source_function_attributes,
+};
+
 static int android_uasp_connect_cb(bool connect)
 {
 	/*
@@ -1543,6 +1614,7 @@
 	&rndis_qc_function,
 	&mass_storage_function,
 	&accessory_function,
+	&audio_source_function,
 	&uasp_function,
 	NULL
 };
@@ -2175,6 +2247,11 @@
 	unsigned long flags;
 
 	composite_disconnect(gadget);
+	/* accessory HID support can be active while the
+	   accessory function is not actually enabled,
+	   so we need to inform it when we are disconnected.
+	 */
+	acc_disconnect();
 
 	spin_lock_irqsave(&cdev->lock, flags);
 	dev->connected = 0;
diff --git a/drivers/usb/gadget/ci13xxx_udc.c b/drivers/usb/gadget/ci13xxx_udc.c
index 0ace679..9c7b1ec 100644
--- a/drivers/usb/gadget/ci13xxx_udc.c
+++ b/drivers/usb/gadget/ci13xxx_udc.c
@@ -75,7 +75,9 @@
  *****************************************************************************/
 
 #define DMA_ADDR_INVALID	(~(dma_addr_t)0)
-#define ATDTW_SET_DELAY		100 /* 100msec delay */
+#define USB_MAX_TIMEOUT		100 /* 100msec timeout */
+#define EP_PRIME_CHECK_DELAY	(jiffies + msecs_to_jiffies(1000))
+#define MAX_PRIME_CHECK_RETRY	3 /*Wait for 3sec for EP prime failure */
 
 /* ctrl register bank access */
 static DEFINE_SPINLOCK(udc_lock);
@@ -175,7 +177,8 @@
 
 /* maximum number of enpoints: valid only after hw_device_reset() */
 static unsigned hw_ep_max;
-
+static void dbg_usb_op_fail(u8 addr, const char *name,
+				const struct ci13xxx_ep *mep);
 /**
  * hw_ep_bit: calculates the bit number
  * @num: endpoint number
@@ -379,6 +382,27 @@
 	return 0;
 }
 
+static void debug_ept_flush_info(int ep_num, int dir)
+{
+	struct ci13xxx *udc = _udc;
+	struct ci13xxx_ep *mep;
+
+	if (dir)
+		mep = &udc->ci13xxx_ep[ep_num + hw_ep_max/2];
+	else
+		mep = &udc->ci13xxx_ep[ep_num];
+
+	pr_err_ratelimited("USB Registers\n");
+	pr_err_ratelimited("USBCMD:%x\n", hw_cread(CAP_USBCMD, ~0));
+	pr_err_ratelimited("USBSTS:%x\n", hw_cread(CAP_USBSTS, ~0));
+	pr_err_ratelimited("ENDPTLISTADDR:%x\n",
+			hw_cread(CAP_ENDPTLISTADDR, ~0));
+	pr_err_ratelimited("PORTSC:%x\n", hw_cread(CAP_PORTSC, ~0));
+	pr_err_ratelimited("USBMODE:%x\n", hw_cread(CAP_USBMODE, ~0));
+	pr_err_ratelimited("ENDPTSTAT:%x\n", hw_cread(CAP_ENDPTSTAT, ~0));
+
+	dbg_usb_op_fail(0xFF, "FLUSHF", mep);
+}
 /**
  * hw_ep_flush: flush endpoint fifo (execute without interruption)
  * @num: endpoint number
@@ -388,13 +412,25 @@
  */
 static int hw_ep_flush(int num, int dir)
 {
+	ktime_t start, diff;
 	int n = hw_ep_bit(num, dir);
 
+	start = ktime_get();
 	do {
 		/* flush any pending transfer */
 		hw_cwrite(CAP_ENDPTFLUSH, BIT(n), BIT(n));
-		while (hw_cread(CAP_ENDPTFLUSH, BIT(n)))
+		while (hw_cread(CAP_ENDPTFLUSH, BIT(n))) {
 			cpu_relax();
+			diff = ktime_sub(ktime_get(), start);
+			if (ktime_to_ms(diff) > USB_MAX_TIMEOUT) {
+				printk_ratelimited(KERN_ERR
+					"%s: Failed to flush ep#%d %s\n",
+					__func__, num,
+					dir ? "IN" : "OUT");
+				debug_ept_flush_info(num, dir);
+				return 0;
+			}
+		}
 	} while (hw_cread(CAP_ENDPTSTAT, BIT(n)));
 
 	return 0;
@@ -498,8 +534,6 @@
 
 	hw_cwrite(CAP_ENDPTPRIME, BIT(n), BIT(n));
 
-	while (hw_cread(CAP_ENDPTPRIME, BIT(n)))
-		cpu_relax();
 	if (is_ctrl && dir == RX  && hw_cread(CAP_ENDPTSETUPSTAT, BIT(num)))
 		return -EAGAIN;
 
@@ -1006,6 +1040,45 @@
 }
 
 /**
+ * dbg_usb_op_fail: prints USB Operation FAIL event
+ * @addr: endpoint address
+ * @mEp:  endpoint structure
+ */
+static void dbg_usb_op_fail(u8 addr, const char *name,
+				const struct ci13xxx_ep *mep)
+{
+	char msg[DBG_DATA_MSG];
+	struct ci13xxx_req *req;
+	struct list_head *ptr = NULL;
+
+	if (mep != NULL) {
+		scnprintf(msg, sizeof(msg),
+			"%s Fail EP%d%s QH:%08X",
+			name, mep->num,
+			mep->dir ? "IN" : "OUT", mep->qh.ptr->cap);
+		dbg_print(addr, name, 0, msg);
+		scnprintf(msg, sizeof(msg),
+				"cap:%08X %08X %08X\n",
+				mep->qh.ptr->curr, mep->qh.ptr->td.next,
+				mep->qh.ptr->td.token);
+		dbg_print(addr, "QHEAD", 0, msg);
+
+		list_for_each(ptr, &mep->qh.queue) {
+			req = list_entry(ptr, struct ci13xxx_req, queue);
+			scnprintf(msg, sizeof(msg),
+					"%08X:%08X:%08X\n",
+					req->dma, req->ptr->next,
+					req->ptr->token);
+			dbg_print(addr, "REQ", 0, msg);
+			scnprintf(msg, sizeof(msg), "%08X:%d\n",
+					req->ptr->page[0],
+					req->req.status);
+			dbg_print(addr, "REQPAGE", 0, msg);
+		}
+	}
+}
+
+/**
  * show_events: displays the event buffer
  *
  * Check "device.h" for details
@@ -1468,12 +1541,14 @@
 	n = hw_ep_bit(mEp->num, mEp->dir);
 	pr_info("%s: prime:%08x stat:%08x ep#%d dir:%s"
 			"dTD_update_fail_count: %lu "
-			"mEp->dTD_update_fail_count: %lu\n", __func__,
+			"mEp->dTD_update_fail_count: %lu"
+			"mEp->prime_fail_count: %lu\n", __func__,
 			hw_cread(CAP_ENDPTPRIME, ~0),
 			hw_cread(CAP_ENDPTSTAT, ~0),
 			mEp->num, mEp->dir ? "IN" : "OUT",
 			udc->dTD_update_fail_count,
-			mEp->dTD_update_fail_count);
+			mEp->dTD_update_fail_count,
+			mEp->prime_fail_count);
 
 	pr_info("QH: cap:%08x cur:%08x next:%08x token:%08x\n",
 			mEp->qh.ptr->cap, mEp->qh.ptr->curr,
@@ -1661,6 +1736,57 @@
 	return ((ep->dir == TX) ? USB_ENDPOINT_DIR_MASK : 0) | ep->num;
 }
 
+static void ep_prime_timer_func(unsigned long data)
+{
+	struct ci13xxx_ep *mep = (struct ci13xxx_ep *)data;
+	struct ci13xxx_req *req;
+	struct list_head *ptr = NULL;
+	int n = hw_ep_bit(mep->num, mep->dir);
+	unsigned long flags;
+
+
+	spin_lock_irqsave(mep->lock, flags);
+	if (!hw_cread(CAP_ENDPTPRIME, BIT(n)))
+		goto out;
+
+	if (list_empty(&mep->qh.queue))
+		goto out;
+
+	req = list_entry(mep->qh.queue.next, struct ci13xxx_req, queue);
+
+	mb();
+	if (!(TD_STATUS_ACTIVE & req->ptr->token))
+		goto out;
+
+	mep->prime_timer_count++;
+	if (mep->prime_timer_count == MAX_PRIME_CHECK_RETRY) {
+		mep->prime_timer_count = 0;
+		pr_info("ep%d dir:%s QH:cap:%08x cur:%08x next:%08x tkn:%08x\n",
+				mep->num, mep->dir ? "IN" : "OUT",
+				mep->qh.ptr->cap, mep->qh.ptr->curr,
+				mep->qh.ptr->td.next, mep->qh.ptr->td.token);
+		list_for_each(ptr, &mep->qh.queue) {
+			req = list_entry(ptr, struct ci13xxx_req, queue);
+			pr_info("\treq:%08xnext:%08xtkn:%08xpage0:%08xsts:%d\n",
+					req->dma, req->ptr->next,
+					req->ptr->token, req->ptr->page[0],
+					req->req.status);
+		}
+		dbg_usb_op_fail(0xFF, "PRIMEF", mep);
+		mep->prime_fail_count++;
+	} else {
+		mod_timer(&mep->prime_timer, EP_PRIME_CHECK_DELAY);
+	}
+
+	spin_unlock_irqrestore(mep->lock, flags);
+	return;
+
+out:
+	mep->prime_timer_count = 0;
+	spin_unlock_irqrestore(mep->lock, flags);
+
+}
+
 /**
  * _hardware_queue: configures a request at hardware level
  * @gadget: gadget
@@ -1783,7 +1909,7 @@
 			tmp_stat = hw_cread(CAP_ENDPTSTAT, BIT(n));
 			diff = ktime_sub(ktime_get(), start);
 			/* poll for max. 100ms */
-			if (ktime_to_ms(diff) > ATDTW_SET_DELAY) {
+			if (ktime_to_ms(diff) > USB_MAX_TIMEOUT) {
 				if (hw_cread(CAP_USBCMD, USBCMD_ATDTW))
 					break;
 				printk_ratelimited(KERN_ERR
@@ -1852,6 +1978,8 @@
 
 	ret = hw_ep_prime(mEp->num, mEp->dir,
 			   mEp->type == USB_ENDPOINT_XFER_CONTROL);
+	if (!ret)
+		mod_timer(&mEp->prime_timer, EP_PRIME_CHECK_DELAY);
 done:
 	return ret;
 }
@@ -2300,6 +2428,8 @@
 	if (list_empty(&mEp->qh.queue))
 		return 0;
 
+	del_timer(&mEp->prime_timer);
+	mEp->prime_timer_count = 0;
 	list_for_each_entry_safe(mReq, mReqTemp, &mEp->qh.queue,
 			queue) {
 dequeue:
@@ -2311,7 +2441,8 @@
 			 * bits. This is a temporary workaround till
 			 * HW designers come back on this.
 			 */
-			if (retval == -EBUSY && req_dequeue && mEp->dir == 0) {
+			if (retval == -EBUSY && req_dequeue &&
+				(mEp->dir == 0 || mEp->num == 0)) {
 				req_dequeue = 0;
 				udc->dTD_update_fail_count++;
 				mEp->dTD_update_fail_count++;
@@ -2574,6 +2705,7 @@
 	struct ci13xxx_ep *mEp = container_of(ep, struct ci13xxx_ep, ep);
 	int retval = 0;
 	unsigned long flags;
+	unsigned mult = 0;
 
 	trace("ep = %p, desc = %p", ep, desc);
 
@@ -2599,13 +2731,15 @@
 
 	mEp->qh.ptr->cap = 0;
 
-	if (mEp->type == USB_ENDPOINT_XFER_CONTROL)
+	if (mEp->type == USB_ENDPOINT_XFER_CONTROL) {
 		mEp->qh.ptr->cap |=  QH_IOS;
-	else if (mEp->type == USB_ENDPOINT_XFER_ISOC) {
+	} else if (mEp->type == USB_ENDPOINT_XFER_ISOC) {
 		mEp->qh.ptr->cap &= ~QH_MULT;
-		mEp->qh.ptr->cap |= BIT(30);
-	} else
+		mult = ((mEp->ep.maxpacket >> QH_MULT_SHIFT) + 1) & 0x03;
+		mEp->qh.ptr->cap |= (mult << ffs_nr(QH_MULT));
+	} else {
 		mEp->qh.ptr->cap |= QH_ZLT;
+	}
 
 	mEp->qh.ptr->cap |=
 		(mEp->ep.maxpacket << ffs_nr(QH_MAX_PKT)) & QH_MAX_PKT;
@@ -2647,6 +2781,8 @@
 
 	/* only internal SW should disable ctrl endpts */
 
+	del_timer(&mEp->prime_timer);
+	mEp->prime_timer_count = 0;
 	direction = mEp->dir;
 	do {
 		dbg_event(_usb_addr(mEp), "DISABLE", 0);
@@ -2959,6 +3095,8 @@
 
 	spin_lock_irqsave(mEp->lock, flags);
 
+	del_timer(&mEp->prime_timer);
+	mEp->prime_timer_count = 0;
 	dbg_event(_usb_addr(mEp), "FFLUSH", 0);
 	hw_ep_flush(mEp->num, mEp->dir);
 
@@ -3431,6 +3569,8 @@
 	for (i = 0; i < hw_ep_max; i++) {
 		struct ci13xxx_ep *mEp = &udc->ci13xxx_ep[i];
 		INIT_LIST_HEAD(&mEp->ep.ep_list);
+		setup_timer(&mEp->prime_timer, ep_prime_timer_func,
+			(unsigned long) mEp);
 	}
 
 	if (!(udc->udc_driver->flags & CI13XXX_REGS_SHARED)) {
diff --git a/drivers/usb/gadget/ci13xxx_udc.h b/drivers/usb/gadget/ci13xxx_udc.h
index 3162e15..6b3cad8 100644
--- a/drivers/usb/gadget/ci13xxx_udc.h
+++ b/drivers/usb/gadget/ci13xxx_udc.h
@@ -67,6 +67,7 @@
 #define QH_MAX_PKT            (0x07FFUL << 16)
 #define QH_ZLT                BIT(29)
 #define QH_MULT               (0x0003UL << 30)
+#define QH_MULT_SHIFT         11
 	/* 1 */
 	u32 curr;
 	/* 2 - 8 */
@@ -107,6 +108,9 @@
 	struct device                         *device;
 	struct dma_pool                       *td_pool;
 	unsigned long dTD_update_fail_count;
+	unsigned long			      prime_fail_count;
+	int				      prime_timer_count;
+	struct timer_list		      prime_timer;
 };
 
 struct ci13xxx;
diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c
index 86c0e73..4bc0da2 100644
--- a/drivers/usb/gadget/composite.c
+++ b/drivers/usb/gadget/composite.c
@@ -117,6 +117,7 @@
 			struct usb_function *f,
 			struct usb_ep *_ep)
 {
+	struct usb_composite_dev	*cdev = get_gadget_data(g);
 	struct usb_endpoint_descriptor *chosen_desc = NULL;
 	struct usb_descriptor_header **speed_desc = NULL;
 
@@ -177,14 +178,16 @@
 		switch (usb_endpoint_type(_ep->desc)) {
 		case USB_ENDPOINT_XFER_BULK:
 		case USB_ENDPOINT_XFER_INT:
-			_ep->maxburst = comp_desc->bMaxBurst;
+			_ep->maxburst = comp_desc->bMaxBurst + 1;
 			break;
 		case USB_ENDPOINT_XFER_ISOC:
 			/* mult: bits 1:0 of bmAttributes */
 			_ep->mult = comp_desc->bmAttributes & 0x3;
 			break;
 		default:
-			/* Do nothing for control endpoints */
+			if (comp_desc->bMaxBurst != 0)
+				ERROR(cdev, "ep0 bMaxBurst must be 0\n");
+			_ep->maxburst = 1;
 			break;
 		}
 	}
@@ -368,7 +371,8 @@
 	c->bConfigurationValue = config->bConfigurationValue;
 	c->iConfiguration = config->iConfiguration;
 	c->bmAttributes = USB_CONFIG_ATT_ONE | config->bmAttributes;
-	c->bMaxPower = config->bMaxPower ? : (CONFIG_USB_GADGET_VBUS_DRAW / 2);
+	c->bMaxPower = config->bMaxPower ? :
+		(CONFIG_USB_GADGET_VBUS_DRAW / config->cdev->vbus_draw_units);
 
 	/* There may be e.g. OTG descriptors */
 	if (config->descriptors) {
@@ -685,7 +689,8 @@
 	}
 
 	/* when we return, be sure our power usage is valid */
-	power = c->bMaxPower ? (2 * c->bMaxPower) : CONFIG_USB_GADGET_VBUS_DRAW;
+	power = c->bMaxPower ? (cdev->vbus_draw_units * c->bMaxPower) :
+			CONFIG_USB_GADGET_VBUS_DRAW;
 done:
 	usb_gadget_vbus_draw(gadget, power);
 
@@ -1110,12 +1115,16 @@
 				count_configs(cdev, USB_DT_DEVICE);
 			cdev->desc.bMaxPacketSize0 =
 				cdev->gadget->ep0->maxpacket;
+			cdev->vbus_draw_units = 2;
 			if (gadget_is_superspeed(gadget)) {
 				if (gadget->speed >= USB_SPEED_SUPER) {
 					cdev->desc.bcdUSB = cpu_to_le16(0x0300);
 					cdev->desc.bMaxPacketSize0 = 9;
+					cdev->vbus_draw_units = 8;
+					DBG(cdev, "Config SS device in SS\n");
 				} else {
 					cdev->desc.bcdUSB = cpu_to_le16(0x0210);
+					DBG(cdev, "Config SS device in HS\n");
 				}
 			}
 
@@ -1573,7 +1582,8 @@
 		maxpower = cdev->config->bMaxPower;
 
 		usb_gadget_vbus_draw(gadget, maxpower ?
-			(2 * maxpower) : CONFIG_USB_GADGET_VBUS_DRAW);
+			(cdev->vbus_draw_units * maxpower) :
+			CONFIG_USB_GADGET_VBUS_DRAW);
 	}
 
 	cdev->suspended = 0;
@@ -1582,12 +1592,6 @@
 /*-------------------------------------------------------------------------*/
 
 static struct usb_gadget_driver composite_driver = {
-#ifdef CONFIG_USB_GADGET_SUPERSPEED
-	.max_speed	= USB_SPEED_SUPER,
-#else
-	.max_speed	= USB_SPEED_HIGH,
-#endif
-
 	.unbind		= composite_unbind,
 
 	.setup		= composite_setup,
@@ -1634,8 +1638,7 @@
 		driver->iProduct = driver->name;
 	composite_driver.function =  (char *) driver->name;
 	composite_driver.driver.name = driver->name;
-	composite_driver.max_speed =
-		min_t(u8, composite_driver.max_speed, driver->max_speed);
+	composite_driver.max_speed = driver->max_speed;
 	composite = driver;
 	composite_gadget_bind = bind;
 
diff --git a/drivers/usb/gadget/f_accessory.c b/drivers/usb/gadget/f_accessory.c
index 108caf9..5659c79 100644
--- a/drivers/usb/gadget/f_accessory.c
+++ b/drivers/usb/gadget/f_accessory.c
@@ -33,6 +33,8 @@
 #include <linux/device.h>
 #include <linux/miscdevice.h>
 
+#include <linux/hid.h>
+#include <linux/hiddev.h>
 #include <linux/usb.h>
 #include <linux/usb/ch9.h>
 #include <linux/usb/f_accessory.h>
@@ -40,7 +42,7 @@
 #define BULK_BUFFER_SIZE    16384
 #define ACC_STRING_SIZE     256
 
-#define PROTOCOL_VERSION    1
+#define PROTOCOL_VERSION    2
 
 /* String IDs */
 #define INTERFACE_STRING_INDEX	0
@@ -49,6 +51,20 @@
 #define TX_REQ_MAX 4
 #define RX_REQ_MAX 2
 
+struct acc_hid_dev {
+	struct list_head	list;
+	struct hid_device *hid;
+	struct acc_dev *dev;
+	/* accessory defined ID */
+	int id;
+	/* HID report descriptor */
+	u8 *report_desc;
+	/* length of HID report descriptor */
+	int report_desc_len;
+	/* number of bytes of report_desc we have received so far */
+	int report_desc_offset;
+};
+
 struct acc_dev {
 	struct usb_function function;
 	struct usb_composite_dev *cdev;
@@ -78,6 +94,8 @@
 	/* set to 1 if we have a pending start request */
 	int start_requested;
 
+	int audio_mode;
+
 	/* synchronize access to our device file */
 	atomic_t open_excl;
 
@@ -87,7 +105,21 @@
 	wait_queue_head_t write_wq;
 	struct usb_request *rx_req[RX_REQ_MAX];
 	int rx_done;
-	struct delayed_work work;
+
+	/* delayed work for handling ACCESSORY_START */
+	struct delayed_work start_work;
+
+	/* worker for registering and unregistering hid devices */
+	struct work_struct hid_work;
+
+	/* list of active HID devices */
+	struct list_head	hid_list;
+
+	/* list of new HID devices to register */
+	struct list_head	new_hid_list;
+
+	/* list of dead HID devices to unregister */
+	struct list_head	dead_hid_list;
 };
 
 static struct usb_interface_descriptor acc_interface_desc = {
@@ -100,6 +132,41 @@
 	.bInterfaceProtocol     = 0,
 };
 
+static struct usb_endpoint_descriptor acc_superspeed_in_desc = {
+	.bLength                = USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType        = USB_DT_ENDPOINT,
+	.bEndpointAddress       = USB_DIR_IN,
+	.bmAttributes           = USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize         = __constant_cpu_to_le16(1024),
+};
+
+static struct usb_ss_ep_comp_descriptor acc_superspeed_in_comp_desc = {
+	.bLength =		sizeof acc_superspeed_in_comp_desc,
+	.bDescriptorType =	USB_DT_SS_ENDPOINT_COMP,
+
+	/* the following 2 values can be tweaked if necessary */
+	/* .bMaxBurst =		0, */
+	/* .bmAttributes =	0, */
+};
+
+static struct usb_endpoint_descriptor acc_superspeed_out_desc = {
+	.bLength                = USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType        = USB_DT_ENDPOINT,
+	.bEndpointAddress       = USB_DIR_OUT,
+	.bmAttributes           = USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize         = __constant_cpu_to_le16(1024),
+};
+
+static struct usb_ss_ep_comp_descriptor acc_superspeed_out_comp_desc = {
+	.bLength =		sizeof acc_superspeed_out_comp_desc,
+	.bDescriptorType =	USB_DT_SS_ENDPOINT_COMP,
+
+	/* the following 2 values can be tweaked if necessary */
+	/* .bMaxBurst =		0, */
+	/* .bmAttributes =	0, */
+};
+
+
 static struct usb_endpoint_descriptor acc_highspeed_in_desc = {
 	.bLength                = USB_DT_ENDPOINT_SIZE,
 	.bDescriptorType        = USB_DT_ENDPOINT,
@@ -144,6 +211,15 @@
 	NULL,
 };
 
+static struct usb_descriptor_header *ss_acc_descs[] = {
+	(struct usb_descriptor_header *) &acc_interface_desc,
+	(struct usb_descriptor_header *) &acc_superspeed_in_desc,
+	(struct usb_descriptor_header *) &acc_superspeed_in_comp_desc,
+	(struct usb_descriptor_header *) &acc_superspeed_out_desc,
+	(struct usb_descriptor_header *) &acc_superspeed_out_comp_desc,
+	NULL,
+};
+
 static struct usb_string acc_string_defs[] = {
 	[INTERFACE_STRING_INDEX].s	= "Android Accessory Interface",
 	{  },	/* end of list */
@@ -296,6 +372,160 @@
 	}
 }
 
+static void acc_complete_set_hid_report_desc(struct usb_ep *ep,
+		struct usb_request *req)
+{
+	struct acc_hid_dev *hid = req->context;
+	struct acc_dev *dev = hid->dev;
+	int length = req->actual;
+
+	if (req->status != 0) {
+		pr_err("acc_complete_set_hid_report_desc, err %d\n",
+			req->status);
+		return;
+	}
+
+	memcpy(hid->report_desc + hid->report_desc_offset, req->buf, length);
+	hid->report_desc_offset += length;
+	if (hid->report_desc_offset == hid->report_desc_len) {
+		/* After we have received the entire report descriptor
+		 * we schedule work to initialize the HID device
+		 */
+		schedule_work(&dev->hid_work);
+	}
+}
+
+static void acc_complete_send_hid_event(struct usb_ep *ep,
+		struct usb_request *req)
+{
+	struct acc_hid_dev *hid = req->context;
+	int length = req->actual;
+
+	if (req->status != 0) {
+		pr_err("acc_complete_send_hid_event, err %d\n", req->status);
+		return;
+	}
+
+	hid_report_raw_event(hid->hid, HID_INPUT_REPORT, req->buf, length, 1);
+}
+
+static int acc_hid_parse(struct hid_device *hid)
+{
+	struct acc_hid_dev *hdev = hid->driver_data;
+
+	hid_parse_report(hid, hdev->report_desc, hdev->report_desc_len);
+	return 0;
+}
+
+static int acc_hid_start(struct hid_device *hid)
+{
+	return 0;
+}
+
+static void acc_hid_stop(struct hid_device *hid)
+{
+}
+
+static int acc_hid_open(struct hid_device *hid)
+{
+	return 0;
+}
+
+static void acc_hid_close(struct hid_device *hid)
+{
+}
+
+static struct hid_ll_driver acc_hid_ll_driver = {
+	.parse = acc_hid_parse,
+	.start = acc_hid_start,
+	.stop = acc_hid_stop,
+	.open = acc_hid_open,
+	.close = acc_hid_close,
+};
+
+static struct acc_hid_dev *acc_hid_new(struct acc_dev *dev,
+		int id, int desc_len)
+{
+	struct acc_hid_dev *hdev;
+
+	hdev = kzalloc(sizeof(*hdev), GFP_ATOMIC);
+	if (!hdev)
+		return NULL;
+	hdev->report_desc = kzalloc(desc_len, GFP_ATOMIC);
+	if (!hdev->report_desc) {
+		kfree(hdev);
+		return NULL;
+	}
+	hdev->dev = dev;
+	hdev->id = id;
+	hdev->report_desc_len = desc_len;
+
+	return hdev;
+}
+
+static struct acc_hid_dev *acc_hid_get(struct list_head *list, int id)
+{
+	struct acc_hid_dev *hid;
+
+	list_for_each_entry(hid, list, list) {
+		if (hid->id == id)
+			return hid;
+	}
+	return NULL;
+}
+
+static int acc_register_hid(struct acc_dev *dev, int id, int desc_length)
+{
+	struct acc_hid_dev *hid;
+	unsigned long flags;
+
+	/* report descriptor length must be > 0 */
+	if (desc_length <= 0)
+		return -EINVAL;
+
+	spin_lock_irqsave(&dev->lock, flags);
+	/* replace HID if one already exists with this ID */
+	hid = acc_hid_get(&dev->hid_list, id);
+	if (!hid)
+		hid = acc_hid_get(&dev->new_hid_list, id);
+	if (hid)
+		list_move(&hid->list, &dev->dead_hid_list);
+
+	hid = acc_hid_new(dev, id, desc_length);
+	if (!hid) {
+		spin_unlock_irqrestore(&dev->lock, flags);
+		return -ENOMEM;
+	}
+
+	list_add(&hid->list, &dev->new_hid_list);
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	/* schedule work to register the HID device */
+	schedule_work(&dev->hid_work);
+	return 0;
+}
+
+static int acc_unregister_hid(struct acc_dev *dev, int id)
+{
+	struct acc_hid_dev *hid;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->lock, flags);
+	hid = acc_hid_get(&dev->hid_list, id);
+	if (!hid)
+		hid = acc_hid_get(&dev->new_hid_list, id);
+	if (!hid) {
+		spin_unlock_irqrestore(&dev->lock, flags);
+		return -EINVAL;
+	}
+
+	list_move(&hid->list, &dev->dead_hid_list);
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	schedule_work(&dev->hid_work);
+	return 0;
+}
+
 static int create_bulk_endpoints(struct acc_dev *dev,
 				struct usb_endpoint_descriptor *in_desc,
 				struct usb_endpoint_descriptor *out_desc)
@@ -353,7 +583,7 @@
 	return 0;
 
 fail:
-	printk(KERN_ERR "acc_bind() could not allocate requests\n");
+	pr_err("acc_bind() could not allocate requests\n");
 	while ((req = req_get(dev, &dev->tx_idle)))
 		acc_request_free(req, dev->ep_in);
 	for (i = 0; i < RX_REQ_MAX; i++)
@@ -510,6 +740,8 @@
 		break;
 	case ACCESSORY_IS_START_REQUESTED:
 		return dev->start_requested;
+	case ACCESSORY_GET_AUDIO_MODE:
+		return dev->audio_mode;
 	}
 	if (!src)
 		return -EINVAL;
@@ -540,7 +772,7 @@
 	return 0;
 }
 
-/* file operations for /dev/acc_usb */
+/* file operations for /dev/usb_accessory */
 static const struct file_operations acc_fops = {
 	.owner = THIS_MODULE,
 	.read = acc_read,
@@ -550,23 +782,47 @@
 	.release = acc_release,
 };
 
+static int acc_hid_probe(struct hid_device *hdev,
+		const struct hid_device_id *id)
+{
+	int ret;
+
+	ret = hid_parse(hdev);
+	if (ret)
+		return ret;
+	return hid_hw_start(hdev, HID_CONNECT_DEFAULT);
+}
+
 static struct miscdevice acc_device = {
 	.minor = MISC_DYNAMIC_MINOR,
 	.name = "usb_accessory",
 	.fops = &acc_fops,
 };
 
+static const struct hid_device_id acc_hid_table[] = {
+	{ HID_USB_DEVICE(HID_ANY_ID, HID_ANY_ID) },
+	{ }
+};
+
+static struct hid_driver acc_hid_driver = {
+	.name = "USB accessory",
+	.id_table = acc_hid_table,
+	.probe = acc_hid_probe,
+};
 
 static int acc_ctrlrequest(struct usb_composite_dev *cdev,
 				const struct usb_ctrlrequest *ctrl)
 {
 	struct acc_dev	*dev = _acc_dev;
 	int	value = -EOPNOTSUPP;
+	struct acc_hid_dev *hid;
+	int offset;
 	u8 b_requestType = ctrl->bRequestType;
 	u8 b_request = ctrl->bRequest;
 	u16	w_index = le16_to_cpu(ctrl->wIndex);
 	u16	w_value = le16_to_cpu(ctrl->wValue);
 	u16	w_length = le16_to_cpu(ctrl->wLength);
+	unsigned long flags;
 
 /*
 	printk(KERN_INFO "acc_ctrlrequest "
@@ -579,20 +835,56 @@
 		if (b_request == ACCESSORY_START) {
 			dev->start_requested = 1;
 			schedule_delayed_work(
-				&dev->work, msecs_to_jiffies(10));
+				&dev->start_work, msecs_to_jiffies(10));
 			value = 0;
 		} else if (b_request == ACCESSORY_SEND_STRING) {
 			dev->string_index = w_index;
 			cdev->gadget->ep0->driver_data = dev;
 			cdev->req->complete = acc_complete_set_string;
 			value = w_length;
+		} else if (b_request == ACCESSORY_SET_AUDIO_MODE &&
+				w_index == 0 && w_length == 0) {
+			dev->audio_mode = w_value;
+			value = 0;
+		} else if (b_request == ACCESSORY_REGISTER_HID) {
+			value = acc_register_hid(dev, w_value, w_index);
+		} else if (b_request == ACCESSORY_UNREGISTER_HID) {
+			value = acc_unregister_hid(dev, w_value);
+		} else if (b_request == ACCESSORY_SET_HID_REPORT_DESC) {
+			spin_lock_irqsave(&dev->lock, flags);
+			hid = acc_hid_get(&dev->new_hid_list, w_value);
+			spin_unlock_irqrestore(&dev->lock, flags);
+			if (!hid) {
+				value = -EINVAL;
+				goto err;
+			}
+			offset = w_index;
+			if (offset != hid->report_desc_offset
+				|| offset + w_length > hid->report_desc_len) {
+				value = -EINVAL;
+				goto err;
+			}
+			cdev->req->context = hid;
+			cdev->req->complete = acc_complete_set_hid_report_desc;
+			value = w_length;
+		} else if (b_request == ACCESSORY_SEND_HID_EVENT) {
+			spin_lock_irqsave(&dev->lock, flags);
+			hid = acc_hid_get(&dev->hid_list, w_value);
+			spin_unlock_irqrestore(&dev->lock, flags);
+			if (!hid) {
+				value = -EINVAL;
+				goto err;
+			}
+			cdev->req->context = hid;
+			cdev->req->complete = acc_complete_send_hid_event;
+			value = w_length;
 		}
 	} else if (b_requestType == (USB_DIR_IN | USB_TYPE_VENDOR)) {
 		if (b_request == ACCESSORY_GET_PROTOCOL) {
 			*((u16 *)cdev->req->buf) = PROTOCOL_VERSION;
 			value = sizeof(u16);
 
-			/* clear any string left over from a previous session */
+			/* clear strings left over from a previous session */
 			memset(dev->manufacturer, 0, sizeof(dev->manufacturer));
 			memset(dev->model, 0, sizeof(dev->model));
 			memset(dev->description, 0, sizeof(dev->description));
@@ -600,6 +892,7 @@
 			memset(dev->uri, 0, sizeof(dev->uri));
 			memset(dev->serial, 0, sizeof(dev->serial));
 			dev->start_requested = 0;
+			dev->audio_mode = 0;
 		}
 	}
 
@@ -612,6 +905,7 @@
 				__func__);
 	}
 
+err:
 	if (value == -EOPNOTSUPP)
 		VDBG(cdev,
 			"unknown class-specific control req "
@@ -631,6 +925,10 @@
 
 	DBG(cdev, "acc_function_bind dev: %p\n", dev);
 
+	ret = hid_register_driver(&acc_hid_driver);
+	if (ret)
+		return ret;
+
 	dev->start_requested = 0;
 
 	/* allocate interface ID(s) */
@@ -653,6 +951,14 @@
 			acc_fullspeed_out_desc.bEndpointAddress;
 	}
 
+	/* support super speed hardware */
+	if (gadget_is_superspeed(c->cdev->gadget)) {
+		acc_superspeed_in_desc.bEndpointAddress =
+			acc_fullspeed_in_desc.bEndpointAddress;
+		acc_superspeed_out_desc.bEndpointAddress =
+			acc_fullspeed_out_desc.bEndpointAddress;
+	}
+
 	DBG(cdev, "%s speed %s: IN/%s, OUT/%s\n",
 			gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
 			f->name, dev->ep_in->name, dev->ep_out->name);
@@ -660,6 +966,36 @@
 }
 
 static void
+kill_all_hid_devices(struct acc_dev *dev)
+{
+	struct acc_hid_dev *hid;
+	struct list_head *entry, *temp;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->lock, flags);
+	list_for_each_safe(entry, temp, &dev->hid_list) {
+		hid = list_entry(entry, struct acc_hid_dev, list);
+		list_del(&hid->list);
+		list_add(&hid->list, &dev->dead_hid_list);
+	}
+	list_for_each_safe(entry, temp, &dev->new_hid_list) {
+		hid = list_entry(entry, struct acc_hid_dev, list);
+		list_del(&hid->list);
+		list_add(&hid->list, &dev->dead_hid_list);
+	}
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	schedule_work(&dev->hid_work);
+}
+
+static void
+acc_hid_unbind(struct acc_dev *dev)
+{
+	hid_unregister_driver(&acc_hid_driver);
+	kill_all_hid_devices(dev);
+}
+
+static void
 acc_function_unbind(struct usb_configuration *c, struct usb_function *f)
 {
 	struct acc_dev	*dev = func_to_dev(f);
@@ -670,14 +1006,104 @@
 		acc_request_free(req, dev->ep_in);
 	for (i = 0; i < RX_REQ_MAX; i++)
 		acc_request_free(dev->rx_req[i], dev->ep_out);
+
+	acc_hid_unbind(dev);
 }
 
-static void acc_work(struct work_struct *data)
+static void acc_start_work(struct work_struct *data)
 {
 	char *envp[2] = { "ACCESSORY=START", NULL };
 	kobject_uevent_env(&acc_device.this_device->kobj, KOBJ_CHANGE, envp);
 }
 
+static int acc_hid_init(struct acc_hid_dev *hdev)
+{
+	struct hid_device *hid;
+	int ret;
+
+	hid = hid_allocate_device();
+	if (IS_ERR(hid))
+		return PTR_ERR(hid);
+
+	hid->ll_driver = &acc_hid_ll_driver;
+	hid->dev.parent = acc_device.this_device;
+
+	hid->bus = BUS_USB;
+	hid->vendor = HID_ANY_ID;
+	hid->product = HID_ANY_ID;
+	hid->driver_data = hdev;
+	ret = hid_add_device(hid);
+	if (ret) {
+		pr_err("can't add hid device: %d\n", ret);
+		hid_destroy_device(hid);
+		return ret;
+	}
+
+	hdev->hid = hid;
+	return 0;
+}
+
+static void acc_hid_delete(struct acc_hid_dev *hid)
+{
+	kfree(hid->report_desc);
+	kfree(hid);
+}
+
+static void acc_hid_work(struct work_struct *data)
+{
+	struct acc_dev *dev = _acc_dev;
+	struct list_head	*entry, *temp;
+	struct acc_hid_dev *hid;
+	struct list_head	new_list, dead_list;
+	unsigned long flags;
+
+	INIT_LIST_HEAD(&new_list);
+
+	spin_lock_irqsave(&dev->lock, flags);
+
+	/* copy hids that are ready for initialization to new_list */
+	list_for_each_safe(entry, temp, &dev->new_hid_list) {
+		hid = list_entry(entry, struct acc_hid_dev, list);
+		if (hid->report_desc_offset == hid->report_desc_len)
+			list_move(&hid->list, &new_list);
+	}
+
+	if (list_empty(&dev->dead_hid_list)) {
+		INIT_LIST_HEAD(&dead_list);
+	} else {
+		/* move all of dev->dead_hid_list to dead_list */
+		dead_list.prev = dev->dead_hid_list.prev;
+		dead_list.next = dev->dead_hid_list.next;
+		dead_list.next->prev = &dead_list;
+		dead_list.prev->next = &dead_list;
+		INIT_LIST_HEAD(&dev->dead_hid_list);
+	}
+
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	/* register new HID devices */
+	list_for_each_safe(entry, temp, &new_list) {
+		hid = list_entry(entry, struct acc_hid_dev, list);
+		if (acc_hid_init(hid)) {
+			pr_err("can't add HID device %p\n", hid);
+			acc_hid_delete(hid);
+		} else {
+			spin_lock_irqsave(&dev->lock, flags);
+			list_move(&hid->list, &dev->hid_list);
+			spin_unlock_irqrestore(&dev->lock, flags);
+		}
+	}
+
+	/* remove dead HID devices */
+	list_for_each_safe(entry, temp, &dead_list) {
+		hid = list_entry(entry, struct acc_hid_dev, list);
+		list_del(&hid->list);
+		if (hid->hid)
+			hid_destroy_device(hid->hid);
+		acc_hid_delete(hid);
+	}
+}
+
 static int acc_function_set_alt(struct usb_function *f,
 		unsigned intf, unsigned alt)
 {
@@ -761,6 +1187,8 @@
 	dev->function.strings = acc_strings,
 	dev->function.descriptors = fs_acc_descs;
 	dev->function.hs_descriptors = hs_acc_descs;
+	if (gadget_is_superspeed(c->cdev->gadget))
+		dev->function.ss_descriptors = ss_acc_descs;
 	dev->function.bind = acc_function_bind;
 	dev->function.unbind = acc_function_unbind;
 	dev->function.set_alt = acc_function_set_alt;
@@ -783,7 +1211,11 @@
 	init_waitqueue_head(&dev->write_wq);
 	atomic_set(&dev->open_excl, 0);
 	INIT_LIST_HEAD(&dev->tx_idle);
-	INIT_DELAYED_WORK(&dev->work, acc_work);
+	INIT_LIST_HEAD(&dev->hid_list);
+	INIT_LIST_HEAD(&dev->new_hid_list);
+	INIT_LIST_HEAD(&dev->dead_hid_list);
+	INIT_DELAYED_WORK(&dev->start_work, acc_start_work);
+	INIT_WORK(&dev->hid_work, acc_hid_work);
 
 	/* _acc_dev must be set before calling usb_gadget_register_driver */
 	_acc_dev = dev;
@@ -796,10 +1228,16 @@
 
 err:
 	kfree(dev);
-	printk(KERN_ERR "USB accessory gadget driver failed to initialize\n");
+	pr_err("USB accessory gadget driver failed to initialize\n");
 	return ret;
 }
 
+static void acc_disconnect(void)
+{
+	/* unregister all HID devices if USB is disconnected */
+	kill_all_hid_devices(_acc_dev);
+}
+
 static void acc_cleanup(void)
 {
 	misc_deregister(&acc_device);
diff --git a/drivers/usb/gadget/f_adb.c b/drivers/usb/gadget/f_adb.c
index 045fc6c..68c99a3 100644
--- a/drivers/usb/gadget/f_adb.c
+++ b/drivers/usb/gadget/f_adb.c
@@ -55,6 +55,8 @@
 	wait_queue_head_t write_wq;
 	struct usb_request *rx_req;
 	int rx_done;
+	bool notify_close;
+	bool close_notified;
 };
 
 static struct usb_interface_descriptor adb_interface_desc = {
@@ -67,6 +69,40 @@
 	.bInterfaceProtocol     = 1,
 };
 
+static struct usb_endpoint_descriptor adb_superspeed_in_desc = {
+	.bLength                = USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType        = USB_DT_ENDPOINT,
+	.bEndpointAddress       = USB_DIR_IN,
+	.bmAttributes           = USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize         = __constant_cpu_to_le16(1024),
+};
+
+static struct usb_ss_ep_comp_descriptor adb_superspeed_in_comp_desc = {
+	.bLength =		sizeof adb_superspeed_in_comp_desc,
+	.bDescriptorType =	USB_DT_SS_ENDPOINT_COMP,
+
+	/* the following 2 values can be tweaked if necessary */
+	/* .bMaxBurst =		0, */
+	/* .bmAttributes =	0, */
+};
+
+static struct usb_endpoint_descriptor adb_superspeed_out_desc = {
+	.bLength                = USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType        = USB_DT_ENDPOINT,
+	.bEndpointAddress       = USB_DIR_OUT,
+	.bmAttributes           = USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize         = __constant_cpu_to_le16(1024),
+};
+
+static struct usb_ss_ep_comp_descriptor adb_superspeed_out_comp_desc = {
+	.bLength =		sizeof adb_superspeed_out_comp_desc,
+	.bDescriptorType =	USB_DT_SS_ENDPOINT_COMP,
+
+	/* the following 2 values can be tweaked if necessary */
+	/* .bMaxBurst =		0, */
+	/* .bmAttributes =	0, */
+};
+
 static struct usb_endpoint_descriptor adb_highspeed_in_desc = {
 	.bLength                = USB_DT_ENDPOINT_SIZE,
 	.bDescriptorType        = USB_DT_ENDPOINT,
@@ -111,6 +147,15 @@
 	NULL,
 };
 
+static struct usb_descriptor_header *ss_adb_descs[] = {
+	(struct usb_descriptor_header *) &adb_interface_desc,
+	(struct usb_descriptor_header *) &adb_superspeed_in_desc,
+	(struct usb_descriptor_header *) &adb_superspeed_in_comp_desc,
+	(struct usb_descriptor_header *) &adb_superspeed_out_desc,
+	(struct usb_descriptor_header *) &adb_superspeed_out_comp_desc,
+	NULL,
+};
+
 static void adb_ready_callback(void);
 static void adb_closed_callback(void);
 
@@ -423,8 +468,12 @@
 	/* clear the error latch */
 	atomic_set(&_adb_dev->error, 0);
 
-	adb_ready_callback();
+	if (_adb_dev->close_notified) {
+		_adb_dev->close_notified = false;
+		adb_ready_callback();
+	}
 
+	_adb_dev->notify_close = true;
 	return 0;
 }
 
@@ -432,7 +481,18 @@
 {
 	pr_info("adb_release\n");
 
-	adb_closed_callback();
+	/*
+	 * ADB daemon closes the device file after I/O error.  The
+	 * I/O error happen when Rx requests are flushed during
+	 * cable disconnect or bus reset in configured state.  Disabling
+	 * USB configuration and pull-up during these scenarios are
+	 * undesired.  We want to force bus reset only for certain
+	 * commands like "adb root" and "adb usb".
+	 */
+	if (_adb_dev->notify_close) {
+		adb_closed_callback();
+		_adb_dev->close_notified = true;
+	}
 
 	adb_unlock(&_adb_dev->open_excl);
 	return 0;
@@ -486,6 +546,13 @@
 		adb_highspeed_out_desc.bEndpointAddress =
 			adb_fullspeed_out_desc.bEndpointAddress;
 	}
+	/* support super speed hardware */
+	if (gadget_is_superspeed(c->cdev->gadget)) {
+		adb_superspeed_in_desc.bEndpointAddress =
+			adb_fullspeed_in_desc.bEndpointAddress;
+		adb_superspeed_out_desc.bEndpointAddress =
+			adb_fullspeed_out_desc.bEndpointAddress;
+	}
 
 	DBG(cdev, "%s speed %s: IN/%s, OUT/%s\n",
 			gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
@@ -561,6 +628,12 @@
 	struct usb_composite_dev	*cdev = dev->cdev;
 
 	DBG(cdev, "adb_function_disable cdev %p\n", cdev);
+	/*
+	 * Bus reset happened or cable disconnected.  No
+	 * need to disable the configuration now.  We will
+	 * set noify_close to true when device file is re-opened.
+	 */
+	dev->notify_close = false;
 	atomic_set(&dev->online, 0);
 	atomic_set(&dev->error, 1);
 	usb_ep_disable(dev->ep_in);
@@ -582,6 +655,8 @@
 	dev->function.name = "adb";
 	dev->function.descriptors = fs_adb_descs;
 	dev->function.hs_descriptors = hs_adb_descs;
+	if (gadget_is_superspeed(c->cdev->gadget))
+		dev->function.ss_descriptors = ss_adb_descs;
 	dev->function.bind = adb_function_bind;
 	dev->function.unbind = adb_function_unbind;
 	dev->function.set_alt = adb_function_set_alt;
@@ -608,6 +683,9 @@
 	atomic_set(&dev->read_excl, 0);
 	atomic_set(&dev->write_excl, 0);
 
+	/* config is disabled by default if adb is present. */
+	dev->close_notified = true;
+
 	INIT_LIST_HEAD(&dev->tx_idle);
 
 	_adb_dev = dev;
diff --git a/drivers/usb/gadget/f_audio_source.c b/drivers/usb/gadget/f_audio_source.c
new file mode 100644
index 0000000..aae941e
--- /dev/null
+++ b/drivers/usb/gadget/f_audio_source.c
@@ -0,0 +1,832 @@
+/*
+ * Gadget Function Driver for USB audio source device
+ *
+ * Copyright (C) 2012 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/device.h>
+#include <linux/usb/audio.h>
+#include <linux/wait.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+
+#define SAMPLE_RATE 44100
+#define FRAMES_PER_MSEC (SAMPLE_RATE / 1000)
+
+#define IN_EP_MAX_PACKET_SIZE	256
+
+/* Number of requests to allocate */
+#define IN_EP_REQ_COUNT 4
+
+#define AUDIO_AC_INTERFACE	0
+#define AUDIO_AS_INTERFACE	1
+#define AUDIO_NUM_INTERFACES	2
+
+/* B.3.1  Standard AC Interface Descriptor */
+static struct usb_interface_descriptor audio_source_ac_interface_desc = {
+	.bLength =		USB_DT_INTERFACE_SIZE,
+	.bDescriptorType =	USB_DT_INTERFACE,
+	.bNumEndpoints =	0,
+	.bInterfaceClass =	USB_CLASS_AUDIO,
+	.bInterfaceSubClass =	USB_SUBCLASS_AUDIOCONTROL,
+};
+
+
+#define UAC_DT_AC_HEADER_LENGTH	UAC_DT_AC_HEADER_SIZE(AUDIO_NUM_INTERFACES)
+/* 1 input terminal, 1 output terminal and 1 feature unit */
+#define UAC_DT_TOTAL_LENGTH (UAC_DT_AC_HEADER_LENGTH \
+	+ UAC_DT_INPUT_TERMINAL_SIZE + UAC_DT_OUTPUT_TERMINAL_SIZE \
+	+ UAC_DT_FEATURE_UNIT_SIZE(0))
+/* B.3.2  Class-Specific AC Interface Descriptor */
+static struct uac1_ac_header_descriptor_2 audio_source_ac_header_desc = {
+	.bLength =		UAC_DT_AC_HEADER_LENGTH,
+	.bDescriptorType =	USB_DT_CS_INTERFACE,
+	.bDescriptorSubtype =	UAC_HEADER,
+	.bcdADC =		__constant_cpu_to_le16(0x0100),
+	.wTotalLength =		__constant_cpu_to_le16(UAC_DT_TOTAL_LENGTH),
+	.bInCollection =	AUDIO_NUM_INTERFACES,
+	.baInterfaceNr = {
+		[0] =		AUDIO_AC_INTERFACE,
+		[1] =		AUDIO_AS_INTERFACE,
+	}
+};
+
+#define INPUT_TERMINAL_ID	1
+static struct uac_input_terminal_descriptor input_terminal_desc = {
+	.bLength =		UAC_DT_INPUT_TERMINAL_SIZE,
+	.bDescriptorType =	USB_DT_CS_INTERFACE,
+	.bDescriptorSubtype =	UAC_INPUT_TERMINAL,
+	.bTerminalID =		INPUT_TERMINAL_ID,
+	.wTerminalType =	UAC_INPUT_TERMINAL_MICROPHONE,
+	.bAssocTerminal =	0,
+	.wChannelConfig =	0x3,
+};
+
+DECLARE_UAC_FEATURE_UNIT_DESCRIPTOR(0);
+
+#define FEATURE_UNIT_ID		2
+static struct uac_feature_unit_descriptor_0 feature_unit_desc = {
+	.bLength		= UAC_DT_FEATURE_UNIT_SIZE(0),
+	.bDescriptorType	= USB_DT_CS_INTERFACE,
+	.bDescriptorSubtype	= UAC_FEATURE_UNIT,
+	.bUnitID		= FEATURE_UNIT_ID,
+	.bSourceID		= INPUT_TERMINAL_ID,
+	.bControlSize		= 2,
+};
+
+#define OUTPUT_TERMINAL_ID	3
+static struct uac1_output_terminal_descriptor output_terminal_desc = {
+	.bLength		= UAC_DT_OUTPUT_TERMINAL_SIZE,
+	.bDescriptorType	= USB_DT_CS_INTERFACE,
+	.bDescriptorSubtype	= UAC_OUTPUT_TERMINAL,
+	.bTerminalID		= OUTPUT_TERMINAL_ID,
+	.wTerminalType		= UAC_TERMINAL_STREAMING,
+	.bAssocTerminal		= FEATURE_UNIT_ID,
+	.bSourceID		= FEATURE_UNIT_ID,
+};
+
+/* B.4.1  Standard AS Interface Descriptor */
+static struct usb_interface_descriptor as_interface_alt_0_desc = {
+	.bLength =		USB_DT_INTERFACE_SIZE,
+	.bDescriptorType =	USB_DT_INTERFACE,
+	.bAlternateSetting =	0,
+	.bNumEndpoints =	0,
+	.bInterfaceClass =	USB_CLASS_AUDIO,
+	.bInterfaceSubClass =	USB_SUBCLASS_AUDIOSTREAMING,
+};
+
+static struct usb_interface_descriptor as_interface_alt_1_desc = {
+	.bLength =		USB_DT_INTERFACE_SIZE,
+	.bDescriptorType =	USB_DT_INTERFACE,
+	.bAlternateSetting =	1,
+	.bNumEndpoints =	1,
+	.bInterfaceClass =	USB_CLASS_AUDIO,
+	.bInterfaceSubClass =	USB_SUBCLASS_AUDIOSTREAMING,
+};
+
+/* B.4.2  Class-Specific AS Interface Descriptor */
+static struct uac1_as_header_descriptor as_header_desc = {
+	.bLength =		UAC_DT_AS_HEADER_SIZE,
+	.bDescriptorType =	USB_DT_CS_INTERFACE,
+	.bDescriptorSubtype =	UAC_AS_GENERAL,
+	.bTerminalLink =	INPUT_TERMINAL_ID,
+	.bDelay =		1,
+	.wFormatTag =		UAC_FORMAT_TYPE_I_PCM,
+};
+
+static struct uac_format_type_i_discrete_descriptor_1 as_type_i_desc = {
+	.bLength =		UAC_FORMAT_TYPE_I_DISCRETE_DESC_SIZE(1),
+	.bDescriptorType =	USB_DT_CS_INTERFACE,
+	.bDescriptorSubtype =	UAC_FORMAT_TYPE,
+	.bFormatType =		UAC_FORMAT_TYPE_I,
+	.bSubframeSize =	2,
+	.bBitResolution =	16,
+	.bSamFreqType =		1,
+};
+
+/* Standard ISO IN Endpoint Descriptor for highspeed */
+static struct usb_endpoint_descriptor hs_as_in_ep_desc  = {
+	.bLength =		USB_DT_ENDPOINT_AUDIO_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bEndpointAddress =	USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_SYNC_SYNC
+				| USB_ENDPOINT_XFER_ISOC,
+	.wMaxPacketSize =	__constant_cpu_to_le16(IN_EP_MAX_PACKET_SIZE),
+	.bInterval =		4, /* poll 1 per millisecond */
+};
+
+/* Standard ISO IN Endpoint Descriptor for highspeed */
+static struct usb_endpoint_descriptor fs_as_in_ep_desc  = {
+	.bLength =		USB_DT_ENDPOINT_AUDIO_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bEndpointAddress =	USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_SYNC_SYNC
+				| USB_ENDPOINT_XFER_ISOC,
+	.wMaxPacketSize =	__constant_cpu_to_le16(IN_EP_MAX_PACKET_SIZE),
+	.bInterval =		1, /* poll 1 per millisecond */
+};
+
+/* Class-specific AS ISO OUT Endpoint Descriptor */
+static struct uac_iso_endpoint_descriptor as_iso_in_desc = {
+	.bLength =		UAC_ISO_ENDPOINT_DESC_SIZE,
+	.bDescriptorType =	USB_DT_CS_ENDPOINT,
+	.bDescriptorSubtype =	UAC_EP_GENERAL,
+	.bmAttributes =		1,
+	.bLockDelayUnits =	1,
+	.wLockDelay =		__constant_cpu_to_le16(1),
+};
+
+static struct usb_descriptor_header *hs_audio_desc[] = {
+	(struct usb_descriptor_header *)&audio_source_ac_interface_desc,
+	(struct usb_descriptor_header *)&audio_source_ac_header_desc,
+
+	(struct usb_descriptor_header *)&input_terminal_desc,
+	(struct usb_descriptor_header *)&output_terminal_desc,
+	(struct usb_descriptor_header *)&feature_unit_desc,
+
+	(struct usb_descriptor_header *)&as_interface_alt_0_desc,
+	(struct usb_descriptor_header *)&as_interface_alt_1_desc,
+	(struct usb_descriptor_header *)&as_header_desc,
+
+	(struct usb_descriptor_header *)&as_type_i_desc,
+
+	(struct usb_descriptor_header *)&hs_as_in_ep_desc,
+	(struct usb_descriptor_header *)&as_iso_in_desc,
+	NULL,
+};
+
+static struct usb_descriptor_header *fs_audio_desc[] = {
+	(struct usb_descriptor_header *)&audio_source_ac_interface_desc,
+	(struct usb_descriptor_header *)&audio_source_ac_header_desc,
+
+	(struct usb_descriptor_header *)&input_terminal_desc,
+	(struct usb_descriptor_header *)&output_terminal_desc,
+	(struct usb_descriptor_header *)&feature_unit_desc,
+
+	(struct usb_descriptor_header *)&as_interface_alt_0_desc,
+	(struct usb_descriptor_header *)&as_interface_alt_1_desc,
+	(struct usb_descriptor_header *)&as_header_desc,
+
+	(struct usb_descriptor_header *)&as_type_i_desc,
+
+	(struct usb_descriptor_header *)&fs_as_in_ep_desc,
+	(struct usb_descriptor_header *)&as_iso_in_desc,
+	NULL,
+};
+
+static struct snd_pcm_hardware audio_hw_info = {
+	.info =			SNDRV_PCM_INFO_MMAP |
+				SNDRV_PCM_INFO_MMAP_VALID |
+				SNDRV_PCM_INFO_BATCH |
+				SNDRV_PCM_INFO_INTERLEAVED |
+				SNDRV_PCM_INFO_BLOCK_TRANSFER,
+
+	.formats		= SNDRV_PCM_FMTBIT_S16_LE,
+	.channels_min		= 2,
+	.channels_max		= 2,
+	.rate_min		= SAMPLE_RATE,
+	.rate_max		= SAMPLE_RATE,
+
+	.buffer_bytes_max =	1024 * 1024,
+	.period_bytes_min =	64,
+	.period_bytes_max =	512 * 1024,
+	.periods_min =		2,
+	.periods_max =		1024,
+};
+
+/*-------------------------------------------------------------------------*/
+
+struct audio_source_config {
+	int	card;
+	int	device;
+};
+
+struct audio_dev {
+	struct usb_function		func;
+	struct snd_card			*card;
+	struct snd_pcm			*pcm;
+	struct snd_pcm_substream *substream;
+
+	struct list_head		idle_reqs;
+	struct usb_ep			*in_ep;
+
+	spinlock_t			lock;
+
+	/* beginning, end and current position in our buffer */
+	void				*buffer_start;
+	void				*buffer_end;
+	void				*buffer_pos;
+
+	/* byte size of a "period" */
+	unsigned int			period;
+	/* bytes sent since last call to snd_pcm_period_elapsed */
+	unsigned int			period_offset;
+	/* time we started playing */
+	ktime_t				start_time;
+	/* number of frames sent since start_time */
+	s64				frames_sent;
+};
+
+static inline struct audio_dev *func_to_audio_source(struct usb_function *f)
+{
+	return container_of(f, struct audio_dev, func);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static struct usb_request *audio_request_new(struct usb_ep *ep, int buffer_size)
+{
+	struct usb_request *req = usb_ep_alloc_request(ep, GFP_KERNEL);
+	if (!req)
+		return NULL;
+
+	req->buf = kmalloc(buffer_size, GFP_KERNEL);
+	if (!req->buf) {
+		usb_ep_free_request(ep, req);
+		return NULL;
+	}
+	req->length = buffer_size;
+	return req;
+}
+
+static void audio_request_free(struct usb_request *req, struct usb_ep *ep)
+{
+	if (req) {
+		kfree(req->buf);
+		usb_ep_free_request(ep, req);
+	}
+}
+
+static void audio_req_put(struct audio_dev *audio, struct usb_request *req)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&audio->lock, flags);
+	list_add_tail(&req->list, &audio->idle_reqs);
+	spin_unlock_irqrestore(&audio->lock, flags);
+}
+
+static struct usb_request *audio_req_get(struct audio_dev *audio)
+{
+	unsigned long flags;
+	struct usb_request *req;
+
+	spin_lock_irqsave(&audio->lock, flags);
+	if (list_empty(&audio->idle_reqs)) {
+		req = 0;
+	} else {
+		req = list_first_entry(&audio->idle_reqs, struct usb_request,
+				list);
+		list_del(&req->list);
+	}
+	spin_unlock_irqrestore(&audio->lock, flags);
+	return req;
+}
+
+/* send the appropriate number of packets to match our bitrate */
+static void audio_send(struct audio_dev *audio)
+{
+	struct snd_pcm_runtime *runtime;
+	struct usb_request *req;
+	int length, length1, length2, ret;
+	s64 msecs;
+	s64 frames;
+	ktime_t now;
+
+	/* audio->substream will be null if we have been closed */
+	if (!audio->substream)
+		return;
+	/* audio->buffer_pos will be null if we have been stopped */
+	if (!audio->buffer_pos)
+		return;
+
+	runtime = audio->substream->runtime;
+
+	/* compute number of frames to send */
+	now = ktime_get();
+	msecs = ktime_to_ns(now) - ktime_to_ns(audio->start_time);
+	do_div(msecs, 1000000);
+	frames = msecs * SAMPLE_RATE;
+	do_div(frames, 1000);
+
+	/* Readjust our frames_sent if we fall too far behind.
+	 * If we get too far behind it is better to drop some frames than
+	 * to keep sending data too fast in an attempt to catch up.
+	 */
+	if (frames - audio->frames_sent > 10 * FRAMES_PER_MSEC)
+		audio->frames_sent = frames - FRAMES_PER_MSEC;
+
+	frames -= audio->frames_sent;
+
+	/* We need to send something to keep the pipeline going */
+	if (frames <= 0)
+		frames = FRAMES_PER_MSEC;
+
+	while (frames > 0) {
+		req = audio_req_get(audio);
+		if (!req)
+			break;
+
+		length = frames_to_bytes(runtime, frames);
+		if (length > IN_EP_MAX_PACKET_SIZE)
+			length = IN_EP_MAX_PACKET_SIZE;
+
+		if (audio->buffer_pos + length > audio->buffer_end)
+			length1 = audio->buffer_end - audio->buffer_pos;
+		else
+			length1 = length;
+		memcpy(req->buf, audio->buffer_pos, length1);
+		if (length1 < length) {
+			/* Wrap around and copy remaining length
+			 * at beginning of buffer.
+			 */
+			length2 = length - length1;
+			memcpy(req->buf + length1, audio->buffer_start,
+					length2);
+			audio->buffer_pos = audio->buffer_start + length2;
+		} else {
+			audio->buffer_pos += length1;
+			if (audio->buffer_pos >= audio->buffer_end)
+				audio->buffer_pos = audio->buffer_start;
+		}
+
+		req->length = length;
+		ret = usb_ep_queue(audio->in_ep, req, GFP_ATOMIC);
+		if (ret < 0) {
+			pr_err("usb_ep_queue failed ret: %d\n", ret);
+			audio_req_put(audio, req);
+			break;
+		}
+
+		frames -= bytes_to_frames(runtime, length);
+		audio->frames_sent += bytes_to_frames(runtime, length);
+	}
+}
+
+static void audio_control_complete(struct usb_ep *ep, struct usb_request *req)
+{
+	/* nothing to do here */
+}
+
+static void audio_data_complete(struct usb_ep *ep, struct usb_request *req)
+{
+	struct audio_dev *audio = req->context;
+
+	pr_debug("audio_data_complete req->status %d req->actual %d\n",
+		req->status, req->actual);
+
+	audio_req_put(audio, req);
+
+	if (!audio->buffer_start || req->status)
+		return;
+
+	audio->period_offset += req->actual;
+	if (audio->period_offset >= audio->period) {
+		snd_pcm_period_elapsed(audio->substream);
+		audio->period_offset = 0;
+	}
+	audio_send(audio);
+}
+
+static int audio_source_set_endpoint_req(struct usb_function *f,
+		const struct usb_ctrlrequest *ctrl)
+{
+	int value = -EOPNOTSUPP;
+	u16 ep = le16_to_cpu(ctrl->wIndex);
+	u16 len = le16_to_cpu(ctrl->wLength);
+	u16 w_value = le16_to_cpu(ctrl->wValue);
+
+	pr_debug("bRequest 0x%x, w_value 0x%04x, len %d, endpoint %d\n",
+			ctrl->bRequest, w_value, len, ep);
+
+	switch (ctrl->bRequest) {
+	case UAC_SET_CUR:
+	case UAC_SET_MIN:
+	case UAC_SET_MAX:
+	case UAC_SET_RES:
+		value = len;
+		break;
+	default:
+		break;
+	}
+
+	return value;
+}
+
+static int audio_source_get_endpoint_req(struct usb_function *f,
+		const struct usb_ctrlrequest *ctrl)
+{
+	struct usb_composite_dev *cdev = f->config->cdev;
+	int value = -EOPNOTSUPP;
+	u8 ep = ((le16_to_cpu(ctrl->wIndex) >> 8) & 0xFF);
+	u16 len = le16_to_cpu(ctrl->wLength);
+	u16 w_value = le16_to_cpu(ctrl->wValue);
+	u8 *buf = cdev->req->buf;
+
+	pr_debug("bRequest 0x%x, w_value 0x%04x, len %d, endpoint %d\n",
+			ctrl->bRequest, w_value, len, ep);
+
+	if (w_value == UAC_EP_CS_ATTR_SAMPLE_RATE << 8) {
+		switch (ctrl->bRequest) {
+		case UAC_GET_CUR:
+		case UAC_GET_MIN:
+		case UAC_GET_MAX:
+		case UAC_GET_RES:
+			/* return our sample rate */
+			buf[0] = (u8)SAMPLE_RATE;
+			buf[1] = (u8)(SAMPLE_RATE >> 8);
+			buf[2] = (u8)(SAMPLE_RATE >> 16);
+			value = 3;
+			break;
+		default:
+			break;
+		}
+	}
+
+	return value;
+}
+
+static int
+audio_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
+{
+	struct usb_composite_dev *cdev = f->config->cdev;
+	struct usb_request *req = cdev->req;
+	int value = -EOPNOTSUPP;
+	u16 w_index = le16_to_cpu(ctrl->wIndex);
+	u16 w_value = le16_to_cpu(ctrl->wValue);
+	u16 w_length = le16_to_cpu(ctrl->wLength);
+
+	/* composite driver infrastructure handles everything; interface
+	 * activation uses set_alt().
+	 */
+	switch (ctrl->bRequestType) {
+	case USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_ENDPOINT:
+		value = audio_source_set_endpoint_req(f, ctrl);
+		break;
+
+	case USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_ENDPOINT:
+		value = audio_source_get_endpoint_req(f, ctrl);
+		break;
+	}
+
+	/* respond with data transfer or status phase? */
+	if (value >= 0) {
+		pr_debug("audio req%02x.%02x v%04x i%04x l%d\n",
+			ctrl->bRequestType, ctrl->bRequest,
+			w_value, w_index, w_length);
+		req->zero = 0;
+		req->length = value;
+		req->complete = audio_control_complete;
+		value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
+		if (value < 0)
+			pr_err("audio response on err %d\n", value);
+	}
+
+	/* device either stalls (value < 0) or reports success */
+	return value;
+}
+
+static int audio_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
+{
+	struct audio_dev *audio = func_to_audio_source(f);
+	struct usb_composite_dev *cdev = f->config->cdev;
+	int ret;
+
+	pr_debug("audio_set_alt intf %d, alt %d\n", intf, alt);
+
+	ret = config_ep_by_speed(cdev->gadget, f, audio->in_ep);
+	if (ret) {
+		audio->in_ep->desc = NULL;
+		ERROR(cdev, "config_ep_by_speed failes for ep %s, result %d\n",
+				audio->in_ep->name, ret);
+			return ret;
+	}
+	ret = usb_ep_enable(audio->in_ep);
+	if (ret) {
+		ERROR(cdev, "failed to enable ep %s, result %d\n",
+			audio->in_ep->name, ret);
+		return ret;
+	}
+	return 0;
+}
+
+static void audio_disable(struct usb_function *f)
+{
+	struct audio_dev *audio = func_to_audio_source(f);
+
+	pr_debug("audio_disable\n");
+	usb_ep_disable(audio->in_ep);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void audio_build_desc(struct audio_dev *audio)
+{
+	u8 *sam_freq;
+	int rate;
+
+	/* Set channel numbers */
+	input_terminal_desc.bNrChannels = 2;
+	as_type_i_desc.bNrChannels = 2;
+
+	/* Set sample rates */
+	rate = SAMPLE_RATE;
+	sam_freq = as_type_i_desc.tSamFreq[0];
+	memcpy(sam_freq, &rate, 3);
+}
+
+/* audio function driver setup/binding */
+static int
+audio_bind(struct usb_configuration *c, struct usb_function *f)
+{
+	struct usb_composite_dev *cdev = c->cdev;
+	struct audio_dev *audio = func_to_audio_source(f);
+	int status;
+	struct usb_ep *ep;
+	struct usb_request *req;
+	int i;
+
+	audio_build_desc(audio);
+
+	/* allocate instance-specific interface IDs, and patch descriptors */
+	status = usb_interface_id(c, f);
+	if (status < 0)
+		goto fail;
+	audio_source_ac_interface_desc.bInterfaceNumber = status;
+
+	status = usb_interface_id(c, f);
+	if (status < 0)
+		goto fail;
+	as_interface_alt_0_desc.bInterfaceNumber = status;
+	as_interface_alt_1_desc.bInterfaceNumber = status;
+
+	status = -ENODEV;
+
+	/* allocate our endpoint */
+	ep = usb_ep_autoconfig(cdev->gadget, &fs_as_in_ep_desc);
+	if (!ep)
+		goto fail;
+	audio->in_ep = ep;
+	ep->driver_data = audio; /* claim */
+
+	if (gadget_is_dualspeed(c->cdev->gadget))
+		hs_as_in_ep_desc.bEndpointAddress =
+			fs_as_in_ep_desc.bEndpointAddress;
+
+	for (i = 0, status = 0; i < IN_EP_REQ_COUNT && status == 0; i++) {
+		req = audio_request_new(ep, IN_EP_MAX_PACKET_SIZE);
+		if (req) {
+			req->context = audio;
+			req->complete = audio_data_complete;
+			audio_req_put(audio, req);
+		} else
+			status = -ENOMEM;
+	}
+
+fail:
+	return status;
+}
+
+static void
+audio_unbind(struct usb_configuration *c, struct usb_function *f)
+{
+	struct audio_dev *audio = func_to_audio_source(f);
+	struct usb_request *req;
+
+	while ((req = audio_req_get(audio)))
+		audio_request_free(req, audio->in_ep);
+
+	snd_card_free_when_closed(audio->card);
+	audio->card = NULL;
+	audio->pcm = NULL;
+	audio->substream = NULL;
+	audio->in_ep = NULL;
+}
+
+static void audio_pcm_playback_start(struct audio_dev *audio)
+{
+	audio->start_time = ktime_get();
+	audio->frames_sent = 0;
+	audio_send(audio);
+}
+
+static void audio_pcm_playback_stop(struct audio_dev *audio)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&audio->lock, flags);
+	audio->buffer_start = 0;
+	audio->buffer_end = 0;
+	audio->buffer_pos = 0;
+	spin_unlock_irqrestore(&audio->lock, flags);
+}
+
+static int audio_pcm_open(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct audio_dev *audio = substream->private_data;
+
+	runtime->private_data = audio;
+	runtime->hw = audio_hw_info;
+	snd_pcm_limit_hw_rates(runtime);
+	runtime->hw.channels_max = 2;
+
+	audio->substream = substream;
+	return 0;
+}
+
+static int audio_pcm_close(struct snd_pcm_substream *substream)
+{
+	struct audio_dev *audio = substream->private_data;
+	unsigned long flags;
+
+	spin_lock_irqsave(&audio->lock, flags);
+	audio->substream = NULL;
+	spin_unlock_irqrestore(&audio->lock, flags);
+
+	return 0;
+}
+
+static int audio_pcm_hw_params(struct snd_pcm_substream *substream,
+				struct snd_pcm_hw_params *params)
+{
+	unsigned int channels = params_channels(params);
+	unsigned int rate = params_rate(params);
+
+	if (rate != SAMPLE_RATE)
+		return -EINVAL;
+	if (channels != 2)
+		return -EINVAL;
+
+	return snd_pcm_lib_alloc_vmalloc_buffer(substream,
+		params_buffer_bytes(params));
+}
+
+static int audio_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+	return snd_pcm_lib_free_vmalloc_buffer(substream);
+}
+
+static int audio_pcm_prepare(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct audio_dev *audio = runtime->private_data;
+
+	audio->period = snd_pcm_lib_period_bytes(substream);
+	audio->period_offset = 0;
+	audio->buffer_start = runtime->dma_area;
+	audio->buffer_end = audio->buffer_start
+		+ snd_pcm_lib_buffer_bytes(substream);
+	audio->buffer_pos = audio->buffer_start;
+
+	return 0;
+}
+
+static snd_pcm_uframes_t audio_pcm_pointer(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct audio_dev *audio = runtime->private_data;
+	ssize_t bytes = audio->buffer_pos - audio->buffer_start;
+
+	/* return offset of next frame to fill in our buffer */
+	return bytes_to_frames(runtime, bytes);
+}
+
+static int audio_pcm_playback_trigger(struct snd_pcm_substream *substream,
+					int cmd)
+{
+	struct audio_dev *audio = substream->runtime->private_data;
+	int ret = 0;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+		audio_pcm_playback_start(audio);
+		break;
+
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+		audio_pcm_playback_stop(audio);
+		break;
+
+	default:
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+static struct audio_dev _audio_dev = {
+	.func = {
+		.name = "audio_source",
+		.bind = audio_bind,
+		.unbind = audio_unbind,
+		.set_alt = audio_set_alt,
+		.setup = audio_setup,
+		.disable = audio_disable,
+		.descriptors = fs_audio_desc,
+		.hs_descriptors = hs_audio_desc,
+	},
+	.lock = __SPIN_LOCK_UNLOCKED(_audio_dev.lock),
+	.idle_reqs = LIST_HEAD_INIT(_audio_dev.idle_reqs),
+};
+
+static struct snd_pcm_ops audio_playback_ops = {
+	.open		= audio_pcm_open,
+	.close		= audio_pcm_close,
+	.ioctl		= snd_pcm_lib_ioctl,
+	.hw_params	= audio_pcm_hw_params,
+	.hw_free	= audio_pcm_hw_free,
+	.prepare	= audio_pcm_prepare,
+	.trigger	= audio_pcm_playback_trigger,
+	.pointer	= audio_pcm_pointer,
+};
+
+int audio_source_bind_config(struct usb_configuration *c,
+		struct audio_source_config *config)
+{
+	struct audio_dev *audio;
+	struct snd_card *card;
+	struct snd_pcm *pcm;
+	int err;
+
+	config->card = -1;
+	config->device = -1;
+
+	audio = &_audio_dev;
+
+	err = snd_card_create(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
+			THIS_MODULE, 0, &card);
+	if (err)
+		return err;
+
+	snd_card_set_dev(card, &c->cdev->gadget->dev);
+
+	err = snd_pcm_new(card, "USB audio source", 0, 1, 0, &pcm);
+	if (err)
+		goto pcm_fail;
+	pcm->private_data = audio;
+	pcm->info_flags = 0;
+	audio->pcm = pcm;
+
+	strlcpy(pcm->name, "USB gadget audio", sizeof(pcm->name));
+
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &audio_playback_ops);
+	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+				NULL, 0, 64 * 1024);
+
+	strlcpy(card->driver, "audio_source", sizeof(card->driver));
+	strlcpy(card->shortname, card->driver, sizeof(card->shortname));
+	strlcpy(card->longname, "USB accessory audio source",
+		sizeof(card->longname));
+
+	err = snd_card_register(card);
+	if (err)
+		goto register_fail;
+
+	err = usb_add_function(c, &audio->func);
+	if (err)
+		goto add_fail;
+
+	config->card = pcm->card->number;
+	config->device = pcm->device;
+	audio->card = card;
+	return 0;
+
+add_fail:
+register_fail:
+pcm_fail:
+	snd_card_free(audio->card);
+	return err;
+}
diff --git a/drivers/usb/gadget/f_diag.c b/drivers/usb/gadget/f_diag.c
index 87597d5..8f68234 100644
--- a/drivers/usb/gadget/f_diag.c
+++ b/drivers/usb/gadget/f_diag.c
@@ -73,6 +73,40 @@
 	.bInterval        =	0,
 };
 
+static struct usb_endpoint_descriptor ss_bulk_in_desc = {
+	.bLength          =	USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType  =	USB_DT_ENDPOINT,
+	.bEndpointAddress =	USB_DIR_IN,
+	.bmAttributes     =	USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize   = __constant_cpu_to_le16(1024),
+};
+
+static struct usb_ss_ep_comp_descriptor ss_bulk_in_comp_desc = {
+	.bLength =		sizeof ss_bulk_in_comp_desc,
+	.bDescriptorType =	USB_DT_SS_ENDPOINT_COMP,
+
+	/* the following 2 values can be tweaked if necessary */
+	/* .bMaxBurst =		0, */
+	/* .bmAttributes =	0, */
+};
+
+static struct usb_endpoint_descriptor ss_bulk_out_desc = {
+	.bLength          =	USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType  =	USB_DT_ENDPOINT,
+	.bEndpointAddress =	USB_DIR_OUT,
+	.bmAttributes     =	USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize   = __constant_cpu_to_le16(1024),
+};
+
+static struct usb_ss_ep_comp_descriptor ss_bulk_out_comp_desc = {
+	.bLength =		sizeof ss_bulk_out_comp_desc,
+	.bDescriptorType =	USB_DT_SS_ENDPOINT_COMP,
+
+	/* the following 2 values can be tweaked if necessary */
+	/* .bMaxBurst =		0, */
+	/* .bmAttributes =	0, */
+};
+
 static struct usb_descriptor_header *fs_diag_desc[] = {
 	(struct usb_descriptor_header *) &intf_desc,
 	(struct usb_descriptor_header *) &fs_bulk_in_desc,
@@ -86,6 +120,15 @@
 	NULL,
 };
 
+static struct usb_descriptor_header *ss_diag_desc[] = {
+	(struct usb_descriptor_header *) &intf_desc,
+	(struct usb_descriptor_header *) &ss_bulk_in_desc,
+	(struct usb_descriptor_header *) &ss_bulk_in_comp_desc,
+	(struct usb_descriptor_header *) &ss_bulk_out_desc,
+	(struct usb_descriptor_header *) &ss_bulk_out_comp_desc,
+	NULL,
+};
+
 /**
  * struct diag_context - USB diag function driver private structure
  * @function: function structure for USB interface
@@ -551,6 +594,8 @@
 {
 	struct diag_context *ctxt = func_to_diag(f);
 
+	if (gadget_is_superspeed(c->cdev->gadget))
+		usb_free_descriptors(f->ss_descriptors);
 	if (gadget_is_dualspeed(c->cdev->gadget))
 		usb_free_descriptors(f->hs_descriptors);
 
@@ -580,6 +625,7 @@
 	ctxt->out = ep;
 	ep->driver_data = ctxt;
 
+	status = -ENOMEM;
 	/* copy descriptors, and track endpoint copies */
 	f->descriptors = usb_copy_descriptors(fs_diag_desc);
 	if (!f->descriptors)
@@ -593,9 +639,29 @@
 
 		/* copy descriptors, and track endpoint copies */
 		f->hs_descriptors = usb_copy_descriptors(hs_diag_desc);
+		if (!f->hs_descriptors)
+			goto fail;
+	}
+
+	if (gadget_is_superspeed(c->cdev->gadget)) {
+		ss_bulk_in_desc.bEndpointAddress =
+				fs_bulk_in_desc.bEndpointAddress;
+		ss_bulk_out_desc.bEndpointAddress =
+				fs_bulk_out_desc.bEndpointAddress;
+
+		/* copy descriptors, and track endpoint copies */
+		f->ss_descriptors = usb_copy_descriptors(ss_diag_desc);
+		if (!f->ss_descriptors)
+			goto fail;
 	}
 	return 0;
 fail:
+	if (f->ss_descriptors)
+		usb_free_descriptors(f->ss_descriptors);
+	if (f->hs_descriptors)
+		usb_free_descriptors(f->hs_descriptors);
+	if (f->descriptors)
+		usb_free_descriptors(f->descriptors);
 	if (ctxt->out)
 		ctxt->out->driver_data = NULL;
 	if (ctxt->in)
diff --git a/drivers/usb/gadget/f_mbim.c b/drivers/usb/gadget/f_mbim.c
index 7a84ca3..729910d 100644
--- a/drivers/usb/gadget/f_mbim.c
+++ b/drivers/usb/gadget/f_mbim.c
@@ -19,7 +19,6 @@
 #include <linux/usb/cdc.h>
 
 #include <linux/usb/composite.h>
-#include <linux/usb/android_composite.h>
 #include <linux/platform_device.h>
 
 #include <linux/spinlock.h>
@@ -41,6 +40,9 @@
 
 #define NR_MBIM_PORTS			1
 
+/* ID for Microsoft OS String */
+#define MBIM_OS_STRING_ID   0xEE
+
 struct ctrl_pkt {
 	void			*buf;
 	int			len;
@@ -356,6 +358,63 @@
 	NULL,
 };
 
+/* Microsoft OS Descriptors */
+
+/*
+ * We specify our own bMS_VendorCode byte which Windows will use
+ * as the bRequest value in subsequent device get requests.
+ */
+#define MBIM_VENDOR_CODE	0xA5
+
+/* Microsoft OS String */
+static u8 mbim_os_string[] = {
+	18, /* sizeof(mtp_os_string) */
+	USB_DT_STRING,
+	/* Signature field: "MSFT100" */
+	'M', 0, 'S', 0, 'F', 0, 'T', 0, '1', 0, '0', 0, '0', 0,
+	/* vendor code */
+	MBIM_VENDOR_CODE,
+	/* padding */
+	0
+};
+
+/* Microsoft Extended Configuration Descriptor Header Section */
+struct mbim_ext_config_desc_header {
+	__le32	dwLength;
+	__u16	bcdVersion;
+	__le16	wIndex;
+	__u8	bCount;
+	__u8	reserved[7];
+};
+
+/* Microsoft Extended Configuration Descriptor Function Section */
+struct mbim_ext_config_desc_function {
+	__u8	bFirstInterfaceNumber;
+	__u8	bInterfaceCount;
+	__u8	compatibleID[8];
+	__u8	subCompatibleID[8];
+	__u8	reserved[6];
+};
+
+/* Microsoft Extended Configuration Descriptor */
+static struct {
+	struct mbim_ext_config_desc_header	header;
+	struct mbim_ext_config_desc_function    function;
+} mbim_ext_config_desc = {
+	.header = {
+		.dwLength = __constant_cpu_to_le32(sizeof mbim_ext_config_desc),
+		.bcdVersion = __constant_cpu_to_le16(0x0100),
+		.wIndex = __constant_cpu_to_le16(4),
+		.bCount = 1,
+	},
+	.function = {
+		.bFirstInterfaceNumber = 0,
+		.bInterfaceCount = 1,
+		.compatibleID = { 'A', 'L', 'T', 'R', 'C', 'F', 'G' },
+		/* .subCompatibleID = DYNAMIC */
+	},
+};
+
 /*
  * Here are options for the Datagram Pointer table (NDP) parser.
  * There are 2 different formats: NDP16 and NDP32 in the spec (ch. 3),
@@ -482,9 +541,7 @@
 	unsigned long			flags;
 	int				ret;
 
-	int notif_c = 0;
-
-	pr_info("dev:%p portno#%d\n", dev, dev->port_num);
+	pr_debug("dev:%p portno#%d\n", dev, dev->port_num);
 
 	spin_lock_irqsave(&dev->lock, flags);
 
@@ -506,8 +563,12 @@
 		return;
 	}
 
-	notif_c = atomic_inc_return(&dev->not_port.notify_count);
-	pr_info("atomic_inc_return[notif_c] = %d", notif_c);
+	if (atomic_inc_return(&dev->not_port.notify_count) != 1) {
+		pr_debug("delay ep_queue: notifications queue is busy[%d]",
+			atomic_read(&dev->not_port.notify_count));
+		spin_unlock_irqrestore(&dev->lock, flags);
+		return;
+	}
 
 	event = req->buf;
 	event->bmRequestType = USB_DIR_IN | USB_TYPE_CLASS
@@ -518,8 +579,6 @@
 	event->wLength = cpu_to_le16(0);
 	spin_unlock_irqrestore(&dev->lock, flags);
 
-	pr_info("Call usb_ep_queue");
-
 	ret = usb_ep_queue(dev->not_port.notify,
 			   dev->not_port.notify_req, GFP_ATOMIC);
 	if (ret) {
@@ -527,7 +586,7 @@
 		pr_err("ep enqueue error %d\n", ret);
 	}
 
-	pr_info("Succcessfull Exit");
+	pr_debug("Successful Exit");
 }
 
 static int
@@ -613,7 +672,6 @@
 
 	mbim->ntb_input_size = NTB_DEFAULT_IN_SIZE;
 
-	atomic_set(&mbim->not_port.notify_count, 0);
 	atomic_set(&mbim->online, 0);
 }
 
@@ -691,6 +749,21 @@
 	switch (mbim->not_port.notify_state) {
 
 	case NCM_NOTIFY_NONE:
+		pr_debug("Notification %02x sent\n", event->bNotificationType);
+
+		if (atomic_read(&mbim->not_port.notify_count) <= 0) {
+			pr_debug("notify_none: done");
+			return;
+		}
+
+		spin_unlock(&mbim->lock);
+		status = usb_ep_queue(mbim->not_port.notify, req, GFP_ATOMIC);
+		spin_lock(&mbim->lock);
+		if (status) {
+			atomic_dec(&mbim->not_port.notify_count);
+			pr_err("Queue notify request failed, err: %d", status);
+		}
+
 		return;
 
 	case NCM_NOTIFY_CONNECT:
@@ -723,20 +796,22 @@
 		mbim->not_port.notify_state = NCM_NOTIFY_CONNECT;
 		break;
 	}
+
 	event->bmRequestType = 0xA1;
 	event->wIndex = cpu_to_le16(mbim->ctrl_id);
 
-	mbim->not_port.notify_req = NULL;
 	/*
 	 * In double buffering if there is a space in FIFO,
 	 * completion callback can be called right after the call,
 	 * so unlocking
 	 */
+	atomic_inc(&mbim->not_port.notify_count);
+	pr_debug("queue request: notify_count = %d",
+		atomic_read(&mbim->not_port.notify_count));
 	spin_unlock(&mbim->lock);
 	status = usb_ep_queue(mbim->not_port.notify, req, GFP_ATOMIC);
 	spin_lock(&mbim->lock);
-	if (status < 0) {
-		mbim->not_port.notify_req = req;
+	if (status) {
 		atomic_dec(&mbim->not_port.notify_count);
 		pr_err("usb_ep_queue failed, err: %d", status);
 	}
@@ -763,27 +838,14 @@
 	struct f_mbim			*mbim = req->context;
 	struct usb_cdc_notification	*event = req->buf;
 
-	int notif_c = 0;
-
-	pr_info("dev:%p\n", mbim);
+	pr_debug("dev:%p\n", mbim);
 
 	spin_lock(&mbim->lock);
 	switch (req->status) {
 	case 0:
-		pr_info("Notification %02x sent\n",
-			event->bNotificationType);
-
-		notif_c = atomic_dec_return(&mbim->not_port.notify_count);
-
-		if (notif_c != 0) {
-			pr_info("Continue to mbim_do_notify()");
-			break;
-		} else {
-			pr_info("notify_count decreased to 0. Do not notify");
-			spin_unlock(&mbim->lock);
-			return;
-		}
-
+		atomic_dec(&mbim->not_port.notify_count);
+		pr_debug("notify_count = %d",
+			atomic_read(&mbim->not_port.notify_count));
 		break;
 
 	case -ECONNRESET:
@@ -803,9 +865,7 @@
 		break;
 	}
 
-	mbim->not_port.notify_req = req;
 	mbim_do_notify(mbim);
-
 	spin_unlock(&mbim->lock);
 
 	pr_info("dev:%p Exit\n", mbim);
@@ -1108,6 +1168,63 @@
 	return value;
 }
 
+/*
+ * This function handles the Microsoft-specific OS descriptor control
+ * requests that are issued by Windows host drivers to determine the
+ * configuration containing the MBIM function.
+ *
+ * Unlike mbim_setup() this function handles two specific device requests,
+ * and only when a configuration has not yet been selected.
+ */
+static int mbim_ctrlrequest(struct usb_composite_dev *cdev,
+			    const struct usb_ctrlrequest *ctrl)
+{
+	int	value = -EOPNOTSUPP;
+	u16	w_index = le16_to_cpu(ctrl->wIndex);
+	u16	w_value = le16_to_cpu(ctrl->wValue);
+	u16	w_length = le16_to_cpu(ctrl->wLength);
+
+	/* only respond to OS desciptors when no configuration selected */
+	if (cdev->config || !mbim_ext_config_desc.function.subCompatibleID[0])
+		return value;
+
+	pr_debug("%02x.%02x v%04x i%04x l%u",
+			ctrl->bRequestType, ctrl->bRequest,
+			w_value, w_index, w_length);
+
+	/* Handle MSFT OS string */
+	if (ctrl->bRequestType ==
+			(USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE)
+			&& ctrl->bRequest == USB_REQ_GET_DESCRIPTOR
+			&& (w_value >> 8) == USB_DT_STRING
+			&& (w_value & 0xFF) == MBIM_OS_STRING_ID) {
+
+		value = (w_length < sizeof(mbim_os_string) ?
+				w_length : sizeof(mbim_os_string));
+		memcpy(cdev->req->buf, mbim_os_string, value);
+
+	} else if (ctrl->bRequestType ==
+			(USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE)
+			&& ctrl->bRequest == MBIM_VENDOR_CODE && w_index == 4) {
+
+		/* Handle Extended OS descriptor */
+		value = (w_length < sizeof(mbim_ext_config_desc) ?
+				w_length : sizeof(mbim_ext_config_desc));
+		memcpy(cdev->req->buf, &mbim_ext_config_desc, value);
+	}
+
+	/* respond with data transfer or status phase? */
+	if (value >= 0) {
+		int rc;
+		cdev->req->zero = value < w_length;
+		cdev->req->length = value;
+		rc = usb_ep_queue(cdev->gadget->ep0, cdev->req, GFP_ATOMIC);
+		if (rc < 0)
+			pr_err("response queue error: %d", rc);
+	}
+	return value;
+}
+
 static int mbim_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
 {
 	struct f_mbim		*mbim = func_to_mbim(f);
@@ -1192,12 +1309,13 @@
 
 				pr_info("Set mbim port out_desc = 0x%p",
 					mbim->bam_port.out->desc);
+
+				pr_debug("Activate mbim\n");
+				mbim_bam_connect(mbim);
+
 			} else {
 				pr_info("PORTS already SET");
 			}
-
-			pr_info("Activate mbim\n");
-			mbim_bam_connect(mbim);
 		}
 
 		spin_lock(&mbim->lock);
@@ -1252,6 +1370,8 @@
 		mbim->not_port.notify->driver_data = NULL;
 	}
 
+	atomic_set(&mbim->not_port.notify_count, 0);
+
 	pr_info("mbim deactivated\n");
 }
 
@@ -1371,6 +1491,17 @@
 			goto fail;
 	}
 
+	/*
+	 * If MBIM is bound in a config other than the first, tell Windows
+	 * about it by returning the num as a string in the OS descriptor's
+	 * subCompatibleID field. Windows only supports up to config #4.
+	 */
+	if (c->bConfigurationValue >= 2 && c->bConfigurationValue <= 4) {
+		pr_debug("MBIM in configuration %d", c->bConfigurationValue);
+		mbim_ext_config_desc.function.subCompatibleID[0] =
+			c->bConfigurationValue + '0';
+	}
+
 	pr_info("mbim(%d): %s speed IN/%s OUT/%s NOTIFY/%s\n",
 			mbim->port_num,
 			gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
@@ -1412,6 +1543,8 @@
 
 	kfree(mbim->not_port.notify_req->buf);
 	usb_ep_free_request(mbim->not_port.notify, mbim->not_port.notify_req);
+
+	mbim_ext_config_desc.function.subCompatibleID[0] = 0;
 }
 
 /**
@@ -1660,7 +1793,6 @@
 
 	spin_lock(&_mbim_dev->lock);
 	_mbim_dev->is_open = true;
-	mbim_notify(_mbim_dev);
 	spin_unlock(&_mbim_dev->lock);
 
 	pr_info("Exit, mbim file opened\n");
@@ -1676,7 +1808,6 @@
 
 	spin_lock(&mbim->lock);
 	mbim->is_open = false;
-	mbim_notify(mbim);
 	spin_unlock(&mbim->lock);
 
 	mbim_unlock(&_mbim_dev->open_excl);
diff --git a/drivers/usb/gadget/f_mtp.c b/drivers/usb/gadget/f_mtp.c
index 96790c5..82ffbba 100644
--- a/drivers/usb/gadget/f_mtp.c
+++ b/drivers/usb/gadget/f_mtp.c
@@ -129,6 +129,40 @@
 	.bInterfaceProtocol     = 1,
 };
 
+static struct usb_endpoint_descriptor mtp_superspeed_in_desc = {
+	.bLength                = USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType        = USB_DT_ENDPOINT,
+	.bEndpointAddress       = USB_DIR_IN,
+	.bmAttributes           = USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize         = __constant_cpu_to_le16(1024),
+};
+
+static struct usb_ss_ep_comp_descriptor mtp_superspeed_in_comp_desc = {
+	.bLength =		sizeof mtp_superspeed_in_comp_desc,
+	.bDescriptorType =	USB_DT_SS_ENDPOINT_COMP,
+
+	/* the following 2 values can be tweaked if necessary */
+	/* .bMaxBurst =		0, */
+	/* .bmAttributes =	0, */
+};
+
+static struct usb_endpoint_descriptor mtp_superspeed_out_desc = {
+	.bLength                = USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType        = USB_DT_ENDPOINT,
+	.bEndpointAddress       = USB_DIR_OUT,
+	.bmAttributes           = USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize         = __constant_cpu_to_le16(1024),
+};
+
+static struct usb_ss_ep_comp_descriptor mtp_superspeed_out_comp_desc = {
+	.bLength =		sizeof mtp_superspeed_out_comp_desc,
+	.bDescriptorType =	USB_DT_SS_ENDPOINT_COMP,
+
+	/* the following 2 values can be tweaked if necessary */
+	/* .bMaxBurst =		0, */
+	/* .bmAttributes =	0, */
+};
+
 static struct usb_endpoint_descriptor mtp_highspeed_in_desc = {
 	.bLength                = USB_DT_ENDPOINT_SIZE,
 	.bDescriptorType        = USB_DT_ENDPOINT,
@@ -168,6 +202,16 @@
 	.bInterval              = 6,
 };
 
+static struct usb_ss_ep_comp_descriptor mtp_superspeed_intr_comp_desc = {
+	.bLength =		sizeof mtp_superspeed_intr_comp_desc,
+	.bDescriptorType =	USB_DT_SS_ENDPOINT_COMP,
+
+	/* the following 3 values can be tweaked if necessary */
+	/* .bMaxBurst =		0, */
+	/* .bmAttributes =	0, */
+	.wBytesPerInterval =	cpu_to_le16(INTR_BUFFER_SIZE),
+};
+
 static struct usb_descriptor_header *fs_mtp_descs[] = {
 	(struct usb_descriptor_header *) &mtp_interface_desc,
 	(struct usb_descriptor_header *) &mtp_fullspeed_in_desc,
@@ -184,6 +228,17 @@
 	NULL,
 };
 
+static struct usb_descriptor_header *ss_mtp_descs[] = {
+	(struct usb_descriptor_header *) &mtp_interface_desc,
+	(struct usb_descriptor_header *) &mtp_superspeed_in_desc,
+	(struct usb_descriptor_header *) &mtp_superspeed_in_comp_desc,
+	(struct usb_descriptor_header *) &mtp_superspeed_out_desc,
+	(struct usb_descriptor_header *) &mtp_superspeed_out_comp_desc,
+	(struct usb_descriptor_header *) &mtp_intr_desc,
+	(struct usb_descriptor_header *) &mtp_superspeed_intr_comp_desc,
+	NULL,
+};
+
 static struct usb_descriptor_header *fs_ptp_descs[] = {
 	(struct usb_descriptor_header *) &ptp_interface_desc,
 	(struct usb_descriptor_header *) &mtp_fullspeed_in_desc,
@@ -200,6 +255,17 @@
 	NULL,
 };
 
+static struct usb_descriptor_header *ss_ptp_descs[] = {
+	(struct usb_descriptor_header *) &ptp_interface_desc,
+	(struct usb_descriptor_header *) &mtp_superspeed_in_desc,
+	(struct usb_descriptor_header *) &mtp_superspeed_in_comp_desc,
+	(struct usb_descriptor_header *) &mtp_superspeed_out_desc,
+	(struct usb_descriptor_header *) &mtp_superspeed_out_comp_desc,
+	(struct usb_descriptor_header *) &mtp_intr_desc,
+	(struct usb_descriptor_header *) &mtp_superspeed_intr_comp_desc,
+	NULL,
+};
+
 static struct usb_string mtp_string_defs[] = {
 	/* Naming interface "MTP" so libmtp will recognize us */
 	[INTERFACE_STRING_INDEX].s	= "MTP",
@@ -463,6 +529,10 @@
 	if (count > MTP_BULK_BUFFER_SIZE)
 		return -EINVAL;
 
+	if (!IS_ALIGNED(count, dev->ep_out->maxpacket))
+		DBG(cdev, "%s - count(%d) not multiple of mtu(%d)\n", __func__,
+						count, dev->ep_out->maxpacket);
+
 	/* we will block until we're online */
 	DBG(cdev, "mtp_read: waiting for online state\n");
 	ret = wait_event_interruptible(dev->read_wq,
@@ -484,7 +554,7 @@
 requeue_req:
 	/* queue a request */
 	req = dev->rx_req[0];
-	req->length = count;
+	req->length = MTP_BULK_BUFFER_SIZE;
 	dev->rx_done = 0;
 	ret = usb_ep_queue(dev->ep_out, req, GFP_KERNEL);
 	if (ret < 0) {
@@ -751,6 +821,9 @@
 	count = dev->xfer_file_length;
 
 	DBG(cdev, "receive_file_work(%lld)\n", count);
+	if (!IS_ALIGNED(count, dev->ep_out->maxpacket))
+		DBG(cdev, "%s- count(%lld) not multiple of mtu(%d)\n", __func__,
+						count, dev->ep_out->maxpacket);
 
 	while (count > 0 || write_req) {
 		if (count > 0) {
@@ -758,8 +831,9 @@
 			read_req = dev->rx_req[cur_buf];
 			cur_buf = (cur_buf + 1) % RX_REQ_MAX;
 
-			read_req->length = (count > MTP_BULK_BUFFER_SIZE
-					? MTP_BULK_BUFFER_SIZE : count);
+			/* some h/w expects size to be aligned to ep's MTU */
+			read_req->length = MTP_BULK_BUFFER_SIZE;
+
 			dev->rx_done = 0;
 			ret = usb_ep_queue(dev->ep_out, read_req, GFP_KERNEL);
 			if (ret < 0) {
@@ -795,6 +869,10 @@
 					usb_ep_dequeue(dev->ep_out, read_req);
 				break;
 			}
+			/* Check if we aligned the size due to MTU constraint */
+			if (count < read_req->length)
+				read_req->actual = (read_req->actual > count ?
+						count : read_req->actual);
 			/* if xfer_file_length is 0xFFFFFFFF, then we read until
 			 * we get a zero length packet
 			 */
@@ -1114,6 +1192,14 @@
 			mtp_fullspeed_out_desc.bEndpointAddress;
 	}
 
+	/* support super speed hardware */
+	if (gadget_is_superspeed(c->cdev->gadget)) {
+		mtp_superspeed_in_desc.bEndpointAddress =
+			mtp_fullspeed_in_desc.bEndpointAddress;
+		mtp_superspeed_out_desc.bEndpointAddress =
+			mtp_fullspeed_out_desc.bEndpointAddress;
+	}
+
 	DBG(cdev, "%s speed %s: IN/%s, OUT/%s\n",
 			gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
 			f->name, dev->ep_in->name, dev->ep_out->name);
@@ -1227,9 +1313,13 @@
 	if (ptp_config) {
 		dev->function.descriptors = fs_ptp_descs;
 		dev->function.hs_descriptors = hs_ptp_descs;
+		if (gadget_is_superspeed(c->cdev->gadget))
+			dev->function.ss_descriptors = ss_ptp_descs;
 	} else {
 		dev->function.descriptors = fs_mtp_descs;
 		dev->function.hs_descriptors = hs_mtp_descs;
+		if (gadget_is_superspeed(c->cdev->gadget))
+			dev->function.ss_descriptors = ss_mtp_descs;
 	}
 	dev->function.bind = mtp_function_bind;
 	dev->function.unbind = mtp_function_unbind;
diff --git a/drivers/usb/gadget/f_qc_ecm.c b/drivers/usb/gadget/f_qc_ecm.c
index 1c64955..0b41197 100644
--- a/drivers/usb/gadget/f_qc_ecm.c
+++ b/drivers/usb/gadget/f_qc_ecm.c
@@ -518,7 +518,7 @@
 
 		if (ecm->port.in_ep->driver_data) {
 			DBG(cdev, "reset ecm\n");
-			gether_qc_disconnect(&ecm->port);
+			gether_qc_disconnect_name(&ecm->port, "ecm0");
 			ecm_qc_bam_disconnect(ecm);
 		}
 
@@ -548,7 +548,7 @@
 				);
 			ecm->port.cdc_filter = DEFAULT_FILTER;
 			DBG(cdev, "activate ecm\n");
-			net = gether_qc_connect(&ecm->port);
+			net = gether_qc_connect_name(&ecm->port, "ecm0");
 			if (IS_ERR(net))
 				return PTR_ERR(net);
 
@@ -591,7 +591,7 @@
 	DBG(cdev, "ecm deactivated\n");
 
 	if (ecm->port.in_ep->driver_data) {
-		gether_qc_disconnect(&ecm->port);
+		gether_qc_disconnect_name(&ecm->port, "ecm0");
 		ecm_qc_bam_disconnect(ecm);
 	}
 
diff --git a/drivers/usb/gadget/f_qc_rndis.c b/drivers/usb/gadget/f_qc_rndis.c
index 7a181eb..f86bf12 100644
--- a/drivers/usb/gadget/f_qc_rndis.c
+++ b/drivers/usb/gadget/f_qc_rndis.c
@@ -661,7 +661,7 @@
 
 		if (rndis->port.in_ep->driver_data) {
 			DBG(cdev, "reset rndis\n");
-			gether_qc_disconnect(&rndis->port);
+			gether_qc_disconnect_name(&rndis->port, "rndis0");
 			rndis_qc_bam_disconnect(rndis);
 		}
 
@@ -695,7 +695,7 @@
 		rndis->port.cdc_filter = 0;
 
 		DBG(cdev, "RNDIS RX/TX early activation ...\n");
-		net = gether_qc_connect(&rndis->port);
+		net = gether_qc_connect_name(&rndis->port, "rndis0");
 		if (IS_ERR(net))
 			return PTR_ERR(net);
 
@@ -722,7 +722,7 @@
 	pr_info("rndis deactivated\n");
 
 	rndis_uninit(rndis->config);
-	gether_qc_disconnect(&rndis->port);
+	gether_qc_disconnect_name(&rndis->port, "rndis0");
 	rndis_qc_bam_disconnect(rndis);
 
 	usb_ep_disable(rndis->notify);
diff --git a/drivers/usb/gadget/f_qdss.c b/drivers/usb/gadget/f_qdss.c
index 0c81904..fd4f352 100644
--- a/drivers/usb/gadget/f_qdss.c
+++ b/drivers/usb/gadget/f_qdss.c
@@ -436,11 +436,30 @@
 	}
 }
 
+static void usb_qdss_disconnect_work(struct work_struct *work)
+{
+	struct f_qdss *qdss = container_of(work, struct f_qdss, disconnect_w);
+	int status;
+
+	pr_debug("usb_qdss_disconnect_work\n");
+
+	/* notify qdss to cancell all active transfers*/
+	if (qdss->ch.notify) {
+		qdss->ch.notify(qdss->ch.priv, USB_QDSS_DISCONNECT, NULL,
+			NULL);
+		/* If the app was never started, we can skip USB BAM reset */
+		status = set_qdss_data_connection(qdss->data,
+			qdss->data->address, 0);
+		if (status)
+			pr_err("qdss_disconnect error");
+	}
+
+}
+
 static void qdss_disable(struct usb_function *f)
 {
 	struct f_qdss	*qdss = func_to_qdss(f);
 	unsigned long flags;
-	int status;
 
 	pr_debug("qdss_disable\n");
 
@@ -451,24 +470,15 @@
 	/*cancell all active xfers*/
 	qdss_eps_disable(f);
 
-	/* notify qdss to cancell all active transfers*/
-	if (qdss->ch.notify) {
-		qdss->ch.notify(qdss->ch.priv, USB_QDSS_DISCONNECT, NULL,
-			NULL);
-		/* If the app was never started, we can skip USB BAM reset */
-		status = set_qdss_data_connection(qdss->data,
-			qdss->data->address, 0);
-		if (status)
-			pr_err("qdss_disable error");
-	}
+	schedule_work(&qdss->disconnect_w);
 }
 
-static void usb_qdss_work_func(struct work_struct *work)
+static void usb_qdss_connect_work(struct work_struct *work)
 {
-	struct f_qdss *qdss = container_of(work, struct f_qdss, qdss_work);
+	struct f_qdss *qdss = container_of(work, struct f_qdss, connect_w);
 	int status;
 
-	pr_debug("usb_qdss_work_func\n");
+	pr_debug("usb_qdss_connect_work\n");
 
 	status = init_data(qdss->data);
 	if (status) {
@@ -549,7 +559,7 @@
 		qdss->usb_connected = 1;
 
 	if (qdss->usb_connected && ch->app_conn)
-		schedule_work(&qdss->qdss_work);
+		schedule_work(&qdss->connect_w);
 
 	return 0;
 fail:
@@ -615,9 +625,11 @@
 	qdss->function.unbind = qdss_unbind;
 	qdss->function.set_alt = qdss_set_alt;
 	qdss->function.disable = qdss_disable;
+	spin_lock_init(&qdss->lock);
 	INIT_LIST_HEAD(&qdss->ctrl_read_pool);
 	INIT_LIST_HEAD(&qdss->ctrl_write_pool);
-	INIT_WORK(&qdss->qdss_work, usb_qdss_work_func);
+	INIT_WORK(&qdss->connect_w, usb_qdss_connect_work);
+	INIT_WORK(&qdss->disconnect_w, usb_qdss_disconnect_work);
 
 	status = usb_add_function(c, &qdss->function);
 	if (status) {
@@ -766,7 +778,7 @@
 
 	/* the case USB cabel was connected befor qdss called  qdss_open*/
 	if (qdss->usb_connected == 1)
-		schedule_work(&qdss->qdss_work);
+		schedule_work(&qdss->connect_w);
 
 	return ch;
 }
diff --git a/drivers/usb/gadget/f_qdss.h b/drivers/usb/gadget/f_qdss.h
index b61244b..d6be8b7 100644
--- a/drivers/usb/gadget/f_qdss.h
+++ b/drivers/usb/gadget/f_qdss.h
@@ -32,7 +32,8 @@
 	struct usb_qdss_ch ch;
 	struct list_head ctrl_read_pool;
 	struct list_head ctrl_write_pool;
-	struct work_struct qdss_work;
+	struct work_struct connect_w;
+	struct work_struct disconnect_w;
 	spinlock_t lock;
 	unsigned int data_enabled:1;
 	unsigned int ctrl_in_enabled:1;
diff --git a/drivers/usb/gadget/f_rmnet.c b/drivers/usb/gadget/f_rmnet.c
index aa9daf3..4357e0d 100644
--- a/drivers/usb/gadget/f_rmnet.c
+++ b/drivers/usb/gadget/f_rmnet.c
@@ -147,6 +147,71 @@
 	NULL,
 };
 
+/* Super speed support */
+static struct usb_endpoint_descriptor rmnet_ss_notify_desc  = {
+	.bLength =		sizeof rmnet_ss_notify_desc,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bEndpointAddress =	USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_INT,
+	.wMaxPacketSize =	__constant_cpu_to_le16(RMNET_MAX_NOTIFY_SIZE),
+	.bInterval =		RMNET_NOTIFY_INTERVAL + 4,
+};
+
+static struct usb_ss_ep_comp_descriptor rmnet_ss_notify_comp_desc = {
+	.bLength =		sizeof rmnet_ss_notify_comp_desc,
+	.bDescriptorType =	USB_DT_SS_ENDPOINT_COMP,
+
+	/* the following 3 values can be tweaked if necessary */
+	/* .bMaxBurst =		0, */
+	/* .bmAttributes =	0, */
+	.wBytesPerInterval =	cpu_to_le16(RMNET_MAX_NOTIFY_SIZE),
+};
+
+static struct usb_endpoint_descriptor rmnet_ss_in_desc = {
+	.bLength =		sizeof rmnet_ss_in_desc,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bEndpointAddress =	USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize =	__constant_cpu_to_le16(1024),
+};
+
+static struct usb_ss_ep_comp_descriptor rmnet_ss_in_comp_desc = {
+	.bLength =		sizeof rmnet_ss_in_comp_desc,
+	.bDescriptorType =	USB_DT_SS_ENDPOINT_COMP,
+
+	/* the following 2 values can be tweaked if necessary */
+	/* .bMaxBurst =		0, */
+	/* .bmAttributes =	0, */
+};
+
+static struct usb_endpoint_descriptor rmnet_ss_out_desc = {
+	.bLength =		sizeof rmnet_ss_out_desc,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bEndpointAddress =	USB_DIR_OUT,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize =	__constant_cpu_to_le16(1024),
+};
+
+static struct usb_ss_ep_comp_descriptor rmnet_ss_out_comp_desc = {
+	.bLength =		sizeof rmnet_ss_out_comp_desc,
+	.bDescriptorType =	USB_DT_SS_ENDPOINT_COMP,
+
+	/* the following 2 values can be tweaked if necessary */
+	/* .bMaxBurst =		0, */
+	/* .bmAttributes =	0, */
+};
+
+static struct usb_descriptor_header *rmnet_ss_function[] = {
+	(struct usb_descriptor_header *) &rmnet_interface_desc,
+	(struct usb_descriptor_header *) &rmnet_ss_notify_desc,
+	(struct usb_descriptor_header *) &rmnet_ss_notify_comp_desc,
+	(struct usb_descriptor_header *) &rmnet_ss_in_desc,
+	(struct usb_descriptor_header *) &rmnet_ss_in_comp_desc,
+	(struct usb_descriptor_header *) &rmnet_ss_out_desc,
+	(struct usb_descriptor_header *) &rmnet_ss_out_comp_desc,
+	NULL,
+};
+
 /* String descriptors */
 
 static struct usb_string rmnet_string_defs[] = {
@@ -460,6 +525,8 @@
 
 	pr_debug("%s: portno:%d\n", __func__, dev->port_num);
 
+	if (gadget_is_superspeed(c->cdev->gadget))
+		usb_free_descriptors(f->ss_descriptors);
 	if (gadget_is_dualspeed(c->cdev->gadget))
 		usb_free_descriptors(f->hs_descriptors);
 	usb_free_descriptors(f->descriptors);
@@ -964,6 +1031,7 @@
 	dev->notify_req->complete = frmnet_notify_complete;
 	dev->notify_req->context = dev;
 
+	ret = -ENOMEM;
 	f->descriptors = usb_copy_descriptors(rmnet_fs_function);
 
 	if (!f->descriptors)
@@ -984,6 +1052,21 @@
 			goto fail;
 	}
 
+	if (gadget_is_superspeed(cdev->gadget)) {
+		rmnet_ss_in_desc.bEndpointAddress =
+				rmnet_fs_in_desc.bEndpointAddress;
+		rmnet_ss_out_desc.bEndpointAddress =
+				rmnet_fs_out_desc.bEndpointAddress;
+		rmnet_ss_notify_desc.bEndpointAddress =
+				rmnet_fs_notify_desc.bEndpointAddress;
+
+		/* copy descriptors, and track endpoint copies */
+		f->ss_descriptors = usb_copy_descriptors(rmnet_ss_function);
+
+		if (!f->ss_descriptors)
+			goto fail;
+	}
+
 	pr_info("%s: RmNet(%d) %s Speed, IN:%s OUT:%s\n",
 			__func__, dev->port_num,
 			gadget_is_dualspeed(cdev->gadget) ? "dual" : "full",
@@ -992,8 +1075,14 @@
 	return 0;
 
 fail:
+	if (f->ss_descriptors)
+		usb_free_descriptors(f->ss_descriptors);
+	if (f->hs_descriptors)
+		usb_free_descriptors(f->hs_descriptors);
 	if (f->descriptors)
 		usb_free_descriptors(f->descriptors);
+	if (dev->notify_req)
+		frmnet_free_req(dev->notify, dev->notify_req);
 ep_notify_alloc_fail:
 	dev->notify->driver_data = NULL;
 	dev->notify = NULL;
diff --git a/drivers/usb/gadget/f_rmnet_smd.c b/drivers/usb/gadget/f_rmnet_smd.c
index 5e2c6ed..e8c1f2a 100644
--- a/drivers/usb/gadget/f_rmnet_smd.c
+++ b/drivers/usb/gadget/f_rmnet_smd.c
@@ -874,9 +874,6 @@
 	struct rmnet_smd_dev *dev = container_of(f, struct rmnet_smd_dev,
 								function);
 
-	if (!atomic_read(&dev->online))
-		return;
-
 	atomic_set(&dev->online, 0);
 
 	usb_ep_fifo_flush(dev->epnotify);
diff --git a/drivers/usb/gadget/f_serial.c b/drivers/usb/gadget/f_serial.c
index 3d6ceaa..43347b3 100644
--- a/drivers/usb/gadget/f_serial.c
+++ b/drivers/usb/gadget/f_serial.c
@@ -225,27 +225,56 @@
 	NULL,
 };
 
-static struct usb_endpoint_descriptor gser_ss_in_desc __initdata = {
+static struct usb_endpoint_descriptor gser_ss_in_desc = {
 	.bLength =		USB_DT_ENDPOINT_SIZE,
 	.bDescriptorType =	USB_DT_ENDPOINT,
 	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
 	.wMaxPacketSize =	cpu_to_le16(1024),
 };
 
-static struct usb_endpoint_descriptor gser_ss_out_desc __initdata = {
+static struct usb_endpoint_descriptor gser_ss_out_desc = {
 	.bLength =		USB_DT_ENDPOINT_SIZE,
 	.bDescriptorType =	USB_DT_ENDPOINT,
 	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
 	.wMaxPacketSize =	cpu_to_le16(1024),
 };
 
-static struct usb_ss_ep_comp_descriptor gser_ss_bulk_comp_desc __initdata = {
+static struct usb_ss_ep_comp_descriptor gser_ss_bulk_comp_desc = {
 	.bLength =              sizeof gser_ss_bulk_comp_desc,
 	.bDescriptorType =      USB_DT_SS_ENDPOINT_COMP,
 };
 
-static struct usb_descriptor_header *gser_ss_function[] __initdata = {
+#ifdef CONFIG_MODEM_SUPPORT
+static struct usb_endpoint_descriptor gser_ss_notify_desc  = {
+	.bLength =		sizeof gser_ss_notify_desc,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bEndpointAddress =	USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_INT,
+	.wMaxPacketSize =	__constant_cpu_to_le16(GS_NOTIFY_MAXPACKET),
+	.bInterval =		GS_LOG2_NOTIFY_INTERVAL+4,
+};
+
+static struct usb_ss_ep_comp_descriptor gser_ss_notify_comp_desc = {
+	.bLength =		sizeof gser_ss_notify_comp_desc,
+	.bDescriptorType =	USB_DT_SS_ENDPOINT_COMP,
+
+	/* the following 2 values can be tweaked if necessary */
+	/* .bMaxBurst =		0, */
+	/* .bmAttributes =	0, */
+	.wBytesPerInterval =	cpu_to_le16(GS_NOTIFY_MAXPACKET),
+};
+#endif
+
+static struct usb_descriptor_header *gser_ss_function[] = {
 	(struct usb_descriptor_header *) &gser_interface_desc,
+#ifdef CONFIG_MODEM_SUPPORT
+	(struct usb_descriptor_header *) &gser_header_desc,
+	(struct usb_descriptor_header *) &gser_call_mgmt_descriptor,
+	(struct usb_descriptor_header *) &gser_descriptor,
+	(struct usb_descriptor_header *) &gser_union_desc,
+	(struct usb_descriptor_header *) &gser_ss_notify_desc,
+	(struct usb_descriptor_header *) &gser_ss_notify_comp_desc,
+#endif
 	(struct usb_descriptor_header *) &gser_ss_in_desc,
 	(struct usb_descriptor_header *) &gser_ss_bulk_comp_desc,
 	(struct usb_descriptor_header *) &gser_ss_out_desc,
@@ -304,7 +333,6 @@
 		ret = ghsic_ctrl_setup(no_hsic_sports, USB_GADGET_SERIAL);
 		if (ret < 0)
 			return ret;
-		return 0;
 	}
 	if (no_hsuart_sports) {
 		port_idx = ghsuart_data_setup(no_hsuart_sports,
@@ -319,8 +347,6 @@
 				port_idx++;
 			}
 		}
-
-		return 0;
 	}
 	return ret;
 }
@@ -824,6 +850,10 @@
 			gser_fs_in_desc.bEndpointAddress;
 		gser_ss_out_desc.bEndpointAddress =
 			gser_fs_out_desc.bEndpointAddress;
+#ifdef CONFIG_MODEM_SUPPORT
+		gser_ss_notify_desc.bEndpointAddress =
+				gser_fs_notify_desc.bEndpointAddress;
+#endif
 
 		/* copy descriptors, and track endpoint copies */
 		f->ss_descriptors = usb_copy_descriptors(gser_ss_function);
@@ -839,6 +869,10 @@
 	return 0;
 
 fail:
+	if (f->ss_descriptors)
+		usb_free_descriptors(f->ss_descriptors);
+	if (f->hs_descriptors)
+		usb_free_descriptors(f->hs_descriptors);
 	if (f->descriptors)
 		usb_free_descriptors(f->descriptors);
 #ifdef CONFIG_MODEM_SUPPORT
diff --git a/drivers/usb/gadget/f_uac1.c b/drivers/usb/gadget/f_uac1.c
index b385592..8c74381 100644
--- a/drivers/usb/gadget/f_uac1.c
+++ b/drivers/usb/gadget/f_uac1.c
@@ -62,8 +62,6 @@
 static int generic_set_cmd(struct usb_audio_control *con, u8 cmd, int value);
 static int generic_get_cmd(struct usb_audio_control *con, u8 cmd);
 
-DECLARE_UAC_AC_HEADER_DESCRIPTOR(2);
-DECLARE_UAC_FORMAT_TYPE_I_DISCRETE_DESC(1);
 
 #define SPEAKER_INPUT_TERMINAL_ID	3
 #define SPEAKER_OUTPUT_TERMINAL_ID	4
diff --git a/drivers/usb/gadget/inode.c b/drivers/usb/gadget/inode.c
index e58b164..ae13a10 100644
--- a/drivers/usb/gadget/inode.c
+++ b/drivers/usb/gadget/inode.c
@@ -828,7 +828,6 @@
 		if (value == 0)
 			data->state = STATE_EP_ENABLED;
 		break;
-#ifdef	CONFIG_USB_GADGET_DUALSPEED
 	case USB_SPEED_HIGH:
 		/* fails if caller didn't provide that descriptor... */
 		ep->desc = &data->hs_desc;
@@ -836,7 +835,6 @@
 		if (value == 0)
 			data->state = STATE_EP_ENABLED;
 		break;
-#endif
 	default:
 		DBG(data->dev, "unconnected, %s init abandoned\n",
 				data->name);
@@ -1324,7 +1322,6 @@
  * Unrecognized ep0 requests may be handled in user space.
  */
 
-#ifdef	CONFIG_USB_GADGET_DUALSPEED
 static void make_qualifier (struct dev_data *dev)
 {
 	struct usb_qualifier_descriptor		qual;
@@ -1347,7 +1344,6 @@
 
 	memcpy (dev->rbuf, &qual, sizeof qual);
 }
-#endif
 
 static int
 config_buf (struct dev_data *dev, u8 type, unsigned index)
@@ -1427,7 +1423,6 @@
 			dev->dev->bMaxPacketSize0 = dev->gadget->ep0->maxpacket;
 			req->buf = dev->dev;
 			break;
-#ifdef	CONFIG_USB_GADGET_DUALSPEED
 		case USB_DT_DEVICE_QUALIFIER:
 			if (!dev->hs_config)
 				break;
@@ -1437,7 +1432,6 @@
 			break;
 		case USB_DT_OTHER_SPEED_CONFIG:
 			// FALLTHROUGH
-#endif
 		case USB_DT_CONFIG:
 			value = config_buf (dev,
 					w_value >> 8,
@@ -1763,11 +1757,6 @@
 }
 
 static struct usb_gadget_driver gadgetfs_driver = {
-#ifdef	CONFIG_USB_GADGET_DUALSPEED
-	.max_speed	= USB_SPEED_HIGH,
-#else
-	.max_speed	= USB_SPEED_FULL,
-#endif
 	.function	= (char *) driver_desc,
 	.unbind		= gadgetfs_unbind,
 	.setup		= gadgetfs_setup,
@@ -1900,6 +1889,10 @@
 
 	/* triggers gadgetfs_bind(); then we can enumerate. */
 	spin_unlock_irq (&dev->lock);
+	if (dev->hs_config)
+		gadgetfs_driver.max_speed = USB_SPEED_HIGH;
+	else
+		gadgetfs_driver.max_speed = USB_SPEED_FULL;
 	value = usb_gadget_probe_driver(&gadgetfs_driver, gadgetfs_bind);
 	if (value != 0) {
 		kfree (dev->buf);
diff --git a/drivers/usb/gadget/msm72k_udc.c b/drivers/usb/gadget/msm72k_udc.c
index 3f4e428..b408bfd 100644
--- a/drivers/usb/gadget/msm72k_udc.c
+++ b/drivers/usb/gadget/msm72k_udc.c
@@ -516,6 +516,15 @@
 {
 	struct usb_info *ui = ept->ui;
 	unsigned cfg = CONFIG_MAX_PKT(ept->ep.maxpacket) | CONFIG_ZLT;
+	const struct usb_endpoint_descriptor *desc = ept->ep.desc;
+	unsigned mult = 0;
+
+	if (desc && ((desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
+				== USB_ENDPOINT_XFER_ISOC)) {
+		cfg &= ~(CONFIG_MULT);
+		mult = ((ept->ep.maxpacket >> CONFIG_MULT_SHIFT) + 1) & 0x03;
+		cfg |= (mult << (ffs(CONFIG_MULT) - 1));
+	}
 
 	/* ep0 out needs interrupt-on-setup */
 	if (ept->bit == 0)
diff --git a/drivers/usb/gadget/storage_common.c b/drivers/usb/gadget/storage_common.c
index 3c57df4..7e62c19 100644
--- a/drivers/usb/gadget/storage_common.c
+++ b/drivers/usb/gadget/storage_common.c
@@ -925,7 +925,7 @@
 	int		rc = 0;
 
 
-#ifndef CONFIG_USB_ANDROID_MASS_STORAGE
+#if !defined(CONFIG_USB_G_ANDROID)
 	/* disabled in android because we need to allow closing the backing file
 	 * if the media was removed
 	 */
diff --git a/drivers/usb/gadget/u_bam.c b/drivers/usb/gadget/u_bam.c
index 45dfb87..f092329 100644
--- a/drivers/usb/gadget/u_bam.c
+++ b/drivers/usb/gadget/u_bam.c
@@ -50,7 +50,7 @@
 
 #define BAM_MUX_RX_Q_SIZE			16
 #define BAM_MUX_TX_Q_SIZE			200
-#define BAM_MUX_RX_REQ_SIZE			(2048 - BAM_MUX_HDR)
+#define BAM_MUX_RX_REQ_SIZE			2048   /* Must be 1KB aligned */
 
 #define DL_INTR_THRESHOLD			20
 
diff --git a/drivers/usb/gadget/u_ether.c b/drivers/usb/gadget/u_ether.c
index b84c74d..f7b908b 100644
--- a/drivers/usb/gadget/u_ether.c
+++ b/drivers/usb/gadget/u_ether.c
@@ -91,16 +91,10 @@
 
 #define DEFAULT_QLEN	2	/* double buffering by default */
 
-#ifdef CONFIG_USB_GADGET_DUALSPEED
-
 static unsigned qmult = 10;
 module_param(qmult, uint, S_IRUGO|S_IWUSR);
 MODULE_PARM_DESC(qmult, "queue length multiplier at high/super speed");
 
-#else	/* full speed (low speed doesn't do bulk) */
-#define qmult		1
-#endif
-
 /* for dual-speed hardware, use deeper queues at high/super speed */
 static inline int qlen(struct usb_gadget *gadget)
 {
diff --git a/drivers/usb/gadget/u_qc_ether.c b/drivers/usb/gadget/u_qc_ether.c
index 20933b6..4931c1e 100644
--- a/drivers/usb/gadget/u_qc_ether.c
+++ b/drivers/usb/gadget/u_qc_ether.c
@@ -222,8 +222,6 @@
 	return 1;
 }
 
-static struct eth_qc_dev *qc_dev;
-
 static const struct net_device_ops eth_qc_netdev_ops = {
 	.ndo_open		= eth_qc_open,
 	.ndo_stop		= eth_qc_stop,
@@ -276,9 +274,6 @@
 	struct net_device	*net;
 	int			status;
 
-	if (qc_dev)
-		return -EBUSY;
-
 	net = alloc_etherdev(sizeof *dev);
 	if (!net)
 		return -ENOMEM;
@@ -318,32 +313,33 @@
 		INFO(dev, "MAC %pM\n", net->dev_addr);
 		INFO(dev, "HOST MAC %pM\n", dev->host_mac);
 
-		qc_dev = dev;
 	}
 
 	return status;
 }
 
 /**
- * gether_qc_cleanup - remove Ethernet-over-USB device
+ * gether_qc_cleanup_name - remove Ethernet-over-USB device
  * Context: may sleep
  *
  * This is called to free all resources allocated by @gether_qc_setup().
  */
-void gether_qc_cleanup(void)
+void gether_qc_cleanup_name(const char *netname)
 {
-	if (!qc_dev)
-		return;
+	struct net_device *net_dev;
 
-	unregister_netdev(qc_dev->net);
-	free_netdev(qc_dev->net);
+	/* Extract the eth_qc_dev from the net device */
+	net_dev = dev_get_by_name(&init_net, netname);
 
-	qc_dev = NULL;
+	if (net_dev) {
+		unregister_netdev(net_dev);
+		free_netdev(net_dev);
+	}
 }
 
-
 /**
- * gether_qc_connect - notify network layer that USB link is active
+ * gether_qc_connect_name - notify network layer that USB link
+ * is active
  * @link: the USB link, set up with endpoints, descriptors matching
  *	current device speed, and any framing wrapper(s) set up.
  * Context: irqs blocked
@@ -351,9 +347,15 @@
  * This is called to let the network layer know the connection
  * is active ("carrier detect").
  */
-struct net_device *gether_qc_connect(struct qc_gether *link)
+struct net_device *gether_qc_connect_name(struct qc_gether *link,
+		const char *netname)
 {
-	struct eth_qc_dev		*dev = qc_dev;
+	struct net_device *net_dev;
+	struct eth_qc_dev *dev;
+
+	/* Extract the eth_qc_dev from the net device */
+	net_dev = dev_get_by_name(&init_net, netname);
+	dev = netdev_priv(net_dev);
 
 	if (!dev)
 		return ERR_PTR(-EINVAL);
@@ -381,7 +383,8 @@
 }
 
 /**
- * gether_qc_disconnect - notify network layer that USB link is inactive
+ * gether_qc_disconnect_name - notify network layer that USB
+ * link is inactive
  * @link: the USB link, on which gether_connect() was called
  * Context: irqs blocked
  *
@@ -390,9 +393,14 @@
  *
  * On return, the state is as if gether_connect() had never been called.
  */
-void gether_qc_disconnect(struct qc_gether *link)
+void gether_qc_disconnect_name(struct qc_gether *link, const char *netname)
 {
-	struct eth_qc_dev		*dev = link->ioport;
+	struct net_device *net_dev;
+	struct eth_qc_dev *dev;
+
+	/* Extract the eth_qc_dev from the net device */
+	net_dev = dev_get_by_name(&init_net, netname);
+	dev = netdev_priv(net_dev);
 
 	if (!dev)
 		return;
diff --git a/drivers/usb/gadget/u_qc_ether.h b/drivers/usb/gadget/u_qc_ether.h
index b3c281b..d91e805 100644
--- a/drivers/usb/gadget/u_qc_ether.h
+++ b/drivers/usb/gadget/u_qc_ether.h
@@ -78,14 +78,15 @@
 
 /* netdev setup/teardown as directed by the gadget driver */
 int gether_qc_setup(struct usb_gadget *g, u8 ethaddr[ETH_ALEN]);
-void gether_qc_cleanup(void);
+void gether_qc_cleanup_name(const char *netname);
 /* variant of gether_setup that allows customizing network device name */
 int gether_qc_setup_name(struct usb_gadget *g, u8 ethaddr[ETH_ALEN],
 		const char *netname);
 
 /* connect/disconnect is handled by individual functions */
-struct net_device *gether_qc_connect(struct qc_gether *);
-void gether_qc_disconnect(struct qc_gether *);
+struct net_device *gether_qc_connect_name(struct qc_gether *link,
+		const char *netname);
+void gether_qc_disconnect_name(struct qc_gether *link, const char *netname);
 
 /* each configuration may bind one instance of an ethernet link */
 int ecm_qc_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]);
diff --git a/drivers/usb/host/ehci-mem.c b/drivers/usb/host/ehci-mem.c
index 12f70c3..c61591a 100644
--- a/drivers/usb/host/ehci-mem.c
+++ b/drivers/usb/host/ehci-mem.c
@@ -178,12 +178,15 @@
 static int ehci_mem_init (struct ehci_hcd *ehci, gfp_t flags)
 {
 	int i;
+	size_t align;
+
+	align = ((ehci->pool_64_bit_align) ? 64 : 32);
 
 	/* QTDs for control/bulk/intr transfers */
 	ehci->qtd_pool = dma_pool_create ("ehci_qtd",
 			ehci_to_hcd(ehci)->self.controller,
 			sizeof (struct ehci_qtd),
-			32 /* byte alignment (for hw parts) */,
+			align /* byte alignment (for hw parts) */,
 			4096 /* can't cross 4K */);
 	if (!ehci->qtd_pool) {
 		goto fail;
@@ -193,7 +196,7 @@
 	ehci->qh_pool = dma_pool_create ("ehci_qh",
 			ehci_to_hcd(ehci)->self.controller,
 			sizeof(struct ehci_qh_hw),
-			32 /* byte alignment (for hw parts) */,
+			align /* byte alignment (for hw parts) */,
 			4096 /* can't cross 4K */);
 	if (!ehci->qh_pool) {
 		goto fail;
diff --git a/drivers/usb/host/ehci-msm-hsic.c b/drivers/usb/host/ehci-msm-hsic.c
index 6fe9e58..cd02489 100644
--- a/drivers/usb/host/ehci-msm-hsic.c
+++ b/drivers/usb/host/ehci-msm-hsic.c
@@ -1144,6 +1144,14 @@
 
 #endif	/* CONFIG_PM */
 
+static void ehci_msm_set_autosuspend_delay(struct usb_device *dev)
+{
+	if (!dev->parent) /*for root hub no delay*/
+		pm_runtime_set_autosuspend_delay(&dev->dev, 0);
+	else
+		pm_runtime_set_autosuspend_delay(&dev->dev, 200);
+}
+
 static struct hc_driver msm_hsic_driver = {
 	.description		= hcd_name,
 	.product_desc		= "Qualcomm EHCI Host Controller using HSIC",
@@ -1194,6 +1202,8 @@
 
 	.enable_ulpi_control	= ehci_msm_enable_ulpi_control,
 	.disable_ulpi_control	= ehci_msm_disable_ulpi_control,
+
+	.set_autosuspend_delay = ehci_msm_set_autosuspend_delay,
 };
 
 static int msm_hsic_init_clocks(struct msm_hsic_hcd *mehci, u32 init)
diff --git a/drivers/usb/host/ehci-platform.c b/drivers/usb/host/ehci-platform.c
index d238b4e2..07a232a 100644
--- a/drivers/usb/host/ehci-platform.c
+++ b/drivers/usb/host/ehci-platform.c
@@ -30,6 +30,7 @@
 
 	hcd->has_tt = pdata->has_tt;
 	ehci->has_synopsys_hc_bug = pdata->has_synopsys_hc_bug;
+	ehci->pool_64_bit_align = pdata->pool_64_bit_align;
 	ehci->big_endian_desc = pdata->big_endian_desc;
 	ehci->big_endian_mmio = pdata->big_endian_mmio;
 
diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h
index f8b884a..cd17421 100644
--- a/drivers/usb/host/ehci.h
+++ b/drivers/usb/host/ehci.h
@@ -166,6 +166,7 @@
 	unsigned		has_hostpc:1;
 	unsigned		has_lpm:1;  /* support link power management */
 	unsigned		has_ppcd:1; /* support per-port change bits */
+	unsigned		pool_64_bit_align:1; /* for 64 bit alignment */
 	u8			sbrn;		/* packed release number */
 
 	/* irq statistics */
diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c
index d895f27..e55fed7 100644
--- a/drivers/usb/host/xhci-plat.c
+++ b/drivers/usb/host/xhci-plat.c
@@ -166,7 +166,6 @@
 	phy = usb_get_transceiver();
 	if (phy && phy->otg) {
 		dev_dbg(&pdev->dev, "%s otg support available\n", __func__);
-		hcd->driver->stop(hcd);
 		ret = otg_set_host(phy->otg, &hcd->self);
 		if (ret) {
 			dev_err(&pdev->dev, "%s otg_set_host failed\n",
@@ -211,6 +210,7 @@
 
 	usb_remove_hcd(hcd);
 	iounmap(hcd->regs);
+	release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
 	usb_put_hcd(hcd);
 	kfree(xhci);
 
diff --git a/drivers/usb/misc/diag_bridge.c b/drivers/usb/misc/diag_bridge.c
index b1b7763..2d95945 100644
--- a/drivers/usb/misc/diag_bridge.c
+++ b/drivers/usb/misc/diag_bridge.c
@@ -31,6 +31,8 @@
 #define DRIVER_DESC	"USB host diag bridge driver"
 #define DRIVER_VERSION	"1.0"
 
+#define AUTOSUSP_DELAY_WITH_USB 1000
+
 struct diag_bridge {
 	struct usb_device	*udev;
 	struct usb_interface	*ifc;
@@ -42,6 +44,7 @@
 	struct mutex		ifc_mutex;
 	struct diag_bridge_ops	*ops;
 	struct platform_device	*pdev;
+	unsigned		default_autosusp_delay;
 
 	/* debugging counters */
 	unsigned long		bytes_to_host;
@@ -68,6 +71,12 @@
 	dev->ops = ops;
 	dev->err = 0;
 
+#ifdef CONFIG_PM_RUNTIME
+	dev->default_autosusp_delay = dev->udev->dev.power.autosuspend_delay;
+#endif
+	pm_runtime_set_autosuspend_delay(&dev->udev->dev,
+			AUTOSUSP_DELAY_WITH_USB);
+
 	kref_get(&dev->kref);
 
 	return 0;
@@ -101,6 +110,10 @@
 
 	usb_kill_anchored_urbs(&dev->submitted);
 	dev->ops = 0;
+
+	pm_runtime_set_autosuspend_delay(&dev->udev->dev,
+			dev->default_autosusp_delay);
+
 	kref_put(&dev->kref, diag_bridge_delete);
 }
 EXPORT_SYMBOL(diag_bridge_close);
diff --git a/drivers/usb/misc/ks_bridge.c b/drivers/usb/misc/ks_bridge.c
index 656e379..410b5c4 100644
--- a/drivers/usb/misc/ks_bridge.c
+++ b/drivers/usb/misc/ks_bridge.c
@@ -48,6 +48,7 @@
 #define BOOT_BRIDGE_INDEX	0
 #define EFS_BRIDGE_INDEX	1
 #define MAX_DATA_PKT_SIZE	16384
+#define PENDING_URB_TIMEOUT	10
 
 struct ks_bridge {
 	char			*name;
@@ -58,7 +59,10 @@
 	struct list_head	to_mdm_list;
 	struct list_head	to_ks_list;
 	wait_queue_head_t	ks_wait_q;
+	wait_queue_head_t	pending_urb_wait;
 	struct miscdevice	*fs_dev;
+	atomic_t		tx_pending_cnt;
+	atomic_t		rx_pending_cnt;
 
 	/* usb specific */
 	struct usb_device	*udev;
@@ -207,13 +211,16 @@
 	dbg_log_event(ksb, "C TX_URB", urb->status, 0);
 	pr_debug("status:%d", urb->status);
 
-	if (ksb->ifc)
+	if (test_bit(USB_DEV_CONNECTED, &ksb->flags))
 		usb_autopm_put_interface_async(ksb->ifc);
 
 	if (urb->status < 0)
 		pr_err_ratelimited("urb failed with err:%d", urb->status);
 
 	ksb_free_data_pkt(pkt);
+
+	atomic_dec(&ksb->tx_pending_cnt);
+	wake_up(&ksb->pending_urb_wait);
 }
 
 static void ksb_tomdm_work(struct work_struct *w)
@@ -252,6 +259,7 @@
 
 		dbg_log_event(ksb, "S TX_URB", pkt->len, 0);
 
+		atomic_inc(&ksb->tx_pending_cnt);
 		ret = usb_submit_urb(urb, GFP_KERNEL);
 		if (ret) {
 			pr_err("out urb submission failed");
@@ -259,6 +267,8 @@
 			usb_free_urb(urb);
 			ksb_free_data_pkt(pkt);
 			usb_autopm_put_interface(ksb->ifc);
+			atomic_dec(&ksb->tx_pending_cnt);
+			wake_up(&ksb->pending_urb_wait);
 			return;
 		}
 
@@ -277,6 +287,9 @@
 	unsigned long		flags;
 	struct ks_bridge	*ksb = fp->private_data;
 
+	if (!test_bit(USB_DEV_CONNECTED, &ksb->flags))
+		return -ENODEV;
+
 	pkt = ksb_alloc_data_pkt(count, GFP_KERNEL, ksb);
 	if (IS_ERR(pkt)) {
 		pr_err("unable to allocate data packet");
@@ -420,8 +433,15 @@
 			ksb_rx_cb, pkt);
 	usb_anchor_urb(urb, &ksb->submitted);
 
-	dbg_log_event(ksb, "S RX_URB", pkt->len, 0);
+	if (!test_bit(USB_DEV_CONNECTED, &ksb->flags)) {
+		usb_unanchor_urb(urb);
+		usb_free_urb(urb);
+		ksb_free_data_pkt(pkt);
+		ksb->alloced_read_pkts--;
+		return;
+	}
 
+	atomic_inc(&ksb->rx_pending_cnt);
 	ret = usb_submit_urb(urb, GFP_ATOMIC);
 	if (ret) {
 		pr_err("in urb submission failed");
@@ -429,9 +449,13 @@
 		usb_free_urb(urb);
 		ksb_free_data_pkt(pkt);
 		ksb->alloced_read_pkts--;
+		atomic_dec(&ksb->rx_pending_cnt);
+		wake_up(&ksb->pending_urb_wait);
 		return;
 	}
 
+	dbg_log_event(ksb, "S RX_URB", pkt->len, 0);
+
 	usb_free_urb(urb);
 }
 static void ksb_rx_cb(struct urb *urb)
@@ -454,7 +478,7 @@
 					urb->status);
 		ksb_free_data_pkt(pkt);
 		ksb->alloced_read_pkts--;
-		return;
+		goto done;
 	}
 
 	if (urb->actual_length == 0) {
@@ -474,7 +498,9 @@
 
 resubmit_urb:
 	submit_one_urb(ksb);
-
+done:
+	atomic_dec(&ksb->rx_pending_cnt);
+	wake_up(&ksb->pending_urb_wait);
 }
 
 static void ksb_start_rx_work(struct work_struct *w)
@@ -487,6 +513,10 @@
 	int ret;
 
 	for (i = 0; i < NO_RX_REQS; i++) {
+
+		if (!test_bit(USB_DEV_CONNECTED, &ksb->flags))
+			return;
+
 		pkt = ksb_alloc_data_pkt(MAX_DATA_PKT_SIZE, GFP_KERNEL, ksb);
 		if (IS_ERR(pkt)) {
 			pr_err("unable to allocate data pkt");
@@ -516,6 +546,7 @@
 
 		dbg_log_event(ksb, "S RX_URB", pkt->len, 0);
 
+		atomic_inc(&ksb->rx_pending_cnt);
 		ret = usb_submit_urb(urb, GFP_KERNEL);
 		if (ret) {
 			pr_err("in urb submission failed");
@@ -524,6 +555,8 @@
 			ksb_free_data_pkt(pkt);
 			ksb->alloced_read_pkts--;
 			usb_autopm_put_interface(ksb->ifc);
+			atomic_dec(&ksb->rx_pending_cnt);
+			wake_up(&ksb->pending_urb_wait);
 			return;
 		}
 
@@ -540,6 +573,8 @@
 	struct usb_endpoint_descriptor	*ep_desc;
 	int				i;
 	struct ks_bridge		*ksb;
+	unsigned long			flags;
+	struct data_pkt			*pkt;
 
 	ifc_num = ifc->cur_altsetting->desc.bInterfaceNumber;
 
@@ -590,9 +625,28 @@
 
 	usb_set_intfdata(ifc, ksb);
 	set_bit(USB_DEV_CONNECTED, &ksb->flags);
+	atomic_set(&ksb->tx_pending_cnt, 0);
+	atomic_set(&ksb->rx_pending_cnt, 0);
 
 	dbg_log_event(ksb, "PID-ATT", id->idProduct, 0);
 
+	/*free up stale buffers if any from previous disconnect*/
+	spin_lock_irqsave(&ksb->lock, flags);
+	while (!list_empty(&ksb->to_ks_list)) {
+		pkt = list_first_entry(&ksb->to_ks_list,
+				struct data_pkt, list);
+		list_del_init(&pkt->list);
+		ksb_free_data_pkt(pkt);
+		ksb->alloced_read_pkts--;
+	}
+	while (!list_empty(&ksb->to_mdm_list)) {
+		pkt = list_first_entry(&ksb->to_mdm_list,
+				struct data_pkt, list);
+		list_del_init(&pkt->list);
+		ksb_free_data_pkt(pkt);
+	}
+	spin_unlock_irqrestore(&ksb->lock, flags);
+
 	ksb->fs_dev = (struct miscdevice *)id->driver_info;
 	misc_register(ksb->fs_dev);
 
@@ -640,15 +694,25 @@
 	clear_bit(USB_DEV_CONNECTED, &ksb->flags);
 	wake_up(&ksb->ks_wait_q);
 	cancel_work_sync(&ksb->to_mdm_work);
+	cancel_work_sync(&ksb->start_rx_work);
+
+	misc_deregister(ksb->fs_dev);
 
 	usb_kill_anchored_urbs(&ksb->submitted);
 
+	wait_event_interruptible_timeout(
+					ksb->pending_urb_wait,
+					!atomic_read(&ksb->tx_pending_cnt) &&
+					!atomic_read(&ksb->rx_pending_cnt),
+					msecs_to_jiffies(PENDING_URB_TIMEOUT));
+
 	spin_lock_irqsave(&ksb->lock, flags);
 	while (!list_empty(&ksb->to_ks_list)) {
 		pkt = list_first_entry(&ksb->to_ks_list,
 				struct data_pkt, list);
 		list_del_init(&pkt->list);
 		ksb_free_data_pkt(pkt);
+		ksb->alloced_read_pkts--;
 	}
 	while (!list_empty(&ksb->to_mdm_list)) {
 		pkt = list_first_entry(&ksb->to_mdm_list,
@@ -658,7 +722,6 @@
 	}
 	spin_unlock_irqrestore(&ksb->lock, flags);
 
-	misc_deregister(ksb->fs_dev);
 	ifc->needs_remote_wakeup = 0;
 	usb_put_dev(ksb->udev);
 	ksb->ifc = NULL;
@@ -741,6 +804,7 @@
 		INIT_LIST_HEAD(&ksb->to_mdm_list);
 		INIT_LIST_HEAD(&ksb->to_ks_list);
 		init_waitqueue_head(&ksb->ks_wait_q);
+		init_waitqueue_head(&ksb->pending_urb_wait);
 		ksb->wq = create_singlethread_workqueue(ksb->name);
 		if (!ksb->wq) {
 			pr_err("unable to allocate workqueue");
diff --git a/drivers/usb/misc/mdm_data_bridge.c b/drivers/usb/misc/mdm_data_bridge.c
index c78fd0c..e821fda 100644
--- a/drivers/usb/misc/mdm_data_bridge.c
+++ b/drivers/usb/misc/mdm_data_bridge.c
@@ -21,7 +21,7 @@
 #include <linux/ratelimit.h>
 #include <mach/usb_bridge.h>
 
-#define MAX_RX_URBS			50
+#define MAX_RX_URBS			100
 #define RMNET_RX_BUFSIZE		2048
 
 #define STOP_SUBMIT_URB_LIMIT		500
diff --git a/drivers/usb/musb/Kconfig b/drivers/usb/musb/Kconfig
index f70cab3..5d35287 100644
--- a/drivers/usb/musb/Kconfig
+++ b/drivers/usb/musb/Kconfig
@@ -11,7 +11,6 @@
 	select TWL4030_USB if MACH_OMAP_3430SDP
 	select TWL6030_USB if MACH_OMAP_4430SDP || MACH_OMAP4_PANDA
 	select USB_OTG_UTILS
-	select USB_GADGET_DUALSPEED
 	help
 	  Say Y here if your system has a dual role high speed USB
 	  controller based on the Mentor Graphics silicon IP.  Then
diff --git a/drivers/usb/otg/msm72k_otg.c b/drivers/usb/otg/msm72k_otg.c
index ca1b155..36a91f1 100644
--- a/drivers/usb/otg/msm72k_otg.c
+++ b/drivers/usb/otg/msm72k_otg.c
@@ -536,6 +536,9 @@
 				test_bit(ID_B, &dev->inputs))
 		charge = USB_IDCHG_MAX;
 
+	if (dev->curr_power == charge)
+		return 0;
+
 	pr_debug("Charging with %dmA current\n", charge);
 	/* Call vbus_draw only if the charger is of known type and also
 	 * ignore request to stop charging as a result of suspend interrupt
@@ -545,6 +548,8 @@
 		(charge || new_chg != USB_CHG_TYPE__WALLCHARGER))
 			pdata->chg_vbus_draw(charge);
 
+	dev->curr_power = charge;
+
 	if (new_chg == USB_CHG_TYPE__WALLCHARGER) {
 		wake_lock(&dev->wlock);
 		queue_work(dev->wq, &dev->sm_work);
diff --git a/drivers/usb/otg/msm_otg.c b/drivers/usb/otg/msm_otg.c
index 813fc94..92cbe6f 100644
--- a/drivers/usb/otg/msm_otg.c
+++ b/drivers/usb/otg/msm_otg.c
@@ -39,9 +39,9 @@
 #include <linux/regulator/consumer.h>
 #include <linux/mfd/pm8xxx/pm8921-charger.h>
 #include <linux/mfd/pm8xxx/misc.h>
-#include <linux/power_supply.h>
 #include <linux/mhl_8334.h>
 
+#include <mach/scm.h>
 #include <mach/clk.h>
 #include <mach/mpm.h>
 #include <mach/msm_xo.h>
@@ -96,6 +96,7 @@
 static struct power_supply *psy;
 
 static bool aca_id_turned_on;
+static bool legacy_power_supply;
 static inline bool aca_enabled(void)
 {
 #ifdef CONFIG_USB_MSM_ACA
@@ -843,9 +844,14 @@
 		motg->caps & ALLOW_LPM_ON_DEV_SUSPEND;
 	dcp = motg->chg_type == USB_DCP_CHARGER;
 
-	/* charging detection in progress due to cable plug-in */
-	if (test_bit(B_SESS_VLD, &motg->inputs) && !device_bus_suspend &&
-		!dcp) {
+	/*
+	 * Abort suspend when,
+	 * 1. charging detection in progress due to cable plug-in
+	 * 2. host mode activation in progress due to Micro-A cable insertion
+	 */
+
+	if ((test_bit(B_SESS_VLD, &motg->inputs) && !device_bus_suspend &&
+		!dcp) || test_bit(A_BUS_REQ, &motg->inputs)) {
 		enable_irq(motg->irq);
 		return -EBUSY;
 	}
@@ -926,10 +932,15 @@
 	if (motg->caps & ALLOW_PHY_RETENTION && !host_bus_suspend &&
 		!device_bus_suspend && !dcp) {
 		phy_ctrl_val = readl_relaxed(USB_PHY_CTRL);
-		if (motg->pdata->otg_control == OTG_PHY_CONTROL)
+		if (motg->pdata->otg_control == OTG_PHY_CONTROL) {
 			/* Enable PHY HV interrupts to wake MPM/Link */
-			phy_ctrl_val |=
-				(PHY_IDHV_INTEN | PHY_OTGSESSVLDHV_INTEN);
+			if ((motg->pdata->mode == USB_OTG) ||
+					(motg->pdata->mode == USB_HOST))
+				phy_ctrl_val |= (PHY_IDHV_INTEN |
+							PHY_OTGSESSVLDHV_INTEN);
+			else
+				phy_ctrl_val |= PHY_OTGSESSVLDHV_INTEN;
+		}
 
 		writel_relaxed(phy_ctrl_val & ~PHY_RETEN, USB_PHY_CTRL);
 		motg->lpm_flags |= PHY_RETENTIONED;
@@ -1009,6 +1020,7 @@
 	if (!atomic_read(&motg->in_lpm))
 		return 0;
 
+	disable_irq(motg->irq);
 	wake_lock(&motg->wlock);
 
 	/* Vote for TCXO when waking up the phy */
@@ -1099,6 +1111,7 @@
 		enable_irq(motg->async_int);
 		motg->async_int = 0;
 	}
+	enable_irq(motg->irq);
 
 	/* If ASYNC IRQ is present then keep it enabled only during LPM */
 	if (motg->async_irq)
@@ -1110,19 +1123,24 @@
 }
 #endif
 
-static int msm_otg_notify_host_mode(struct msm_otg *motg, bool host_mode)
+static void msm_otg_notify_host_mode(struct msm_otg *motg, bool host_mode)
 {
-	if (!psy)
-		goto psy_not_supported;
+	if (!psy) {
+		pr_err("No USB power supply registered!\n");
+		return;
+	}
 
-	if (host_mode)
-		power_supply_set_scope(psy, POWER_SUPPLY_SCOPE_SYSTEM);
-	else
-		power_supply_set_scope(psy, POWER_SUPPLY_SCOPE_DEVICE);
-
-psy_not_supported:
-	dev_dbg(motg->phy.dev, "Power Supply doesn't support USB charger\n");
-	return -ENXIO;
+	if (legacy_power_supply) {
+		/* legacy support */
+		if (host_mode)
+			power_supply_set_scope(psy, POWER_SUPPLY_SCOPE_SYSTEM);
+		else
+			power_supply_set_scope(psy, POWER_SUPPLY_SCOPE_DEVICE);
+		return;
+	} else {
+		motg->host_mode = host_mode;
+		power_supply_changed(psy);
+	}
 }
 
 static int msm_otg_notify_chg_type(struct msm_otg *motg)
@@ -1163,28 +1181,30 @@
 
 static int msm_otg_notify_power_supply(struct msm_otg *motg, unsigned mA)
 {
+	if (!psy) {
+		dev_dbg(motg->phy.dev, "no usb power supply registered\n");
+		goto psy_error;
+	}
 
-	if (!psy)
-		goto psy_not_supported;
-
-	if (motg->cur_power == 0 && mA > 0) {
+	if (motg->cur_power == 0 && mA > 2) {
 		/* Enable charging */
 		if (power_supply_set_online(psy, true))
-			goto psy_not_supported;
-	} else if (motg->cur_power > 0 && mA == 0) {
+			goto psy_error;
+		if (power_supply_set_current_limit(psy, 1000*mA))
+			goto psy_error;
+	} else if (motg->cur_power > 0 && (mA == 0 || mA == 2)) {
 		/* Disable charging */
 		if (power_supply_set_online(psy, false))
-			goto psy_not_supported;
-		return 0;
+			goto psy_error;
+		/* Set max current limit */
+		if (power_supply_set_current_limit(psy, 0))
+			goto psy_error;
 	}
-	/* Set max current limit */
-	if (power_supply_set_current_limit(psy, 1000*mA))
-		goto psy_not_supported;
-
+	power_supply_changed(psy);
 	return 0;
 
-psy_not_supported:
-	dev_dbg(motg->phy.dev, "Power Supply doesn't support USB charger\n");
+psy_error:
+	dev_dbg(motg->phy.dev, "power supply error when setting property\n");
 	return -ENXIO;
 }
 
@@ -2041,6 +2061,8 @@
 		udelay(20);
 		break;
 	case SNPS_28NM_INTEGRATED_PHY:
+		/* disable DP and DM pull down resistors */
+		ulpi_write(phy, 0x6, 0xC);
 		/* Clear charger detecting control bits */
 		ulpi_write(phy, 0x1F, 0x86);
 		/* Clear alt interrupt latch and enable bits */
@@ -2120,8 +2142,7 @@
 	switch (motg->chg_state) {
 	case USB_CHG_STATE_UNDEFINED:
 		msm_chg_block_on(motg);
-		if (motg->pdata->enable_dcd)
-			msm_chg_enable_dcd(motg);
+		msm_chg_enable_dcd(motg);
 		msm_chg_enable_aca_det(motg);
 		motg->chg_state = USB_CHG_STATE_WAIT_FOR_DCD;
 		motg->dcd_retries = 0;
@@ -2148,12 +2169,10 @@
 				break;
 			}
 		}
-		if (motg->pdata->enable_dcd)
-			is_dcd = msm_chg_check_dcd(motg);
+		is_dcd = msm_chg_check_dcd(motg);
 		tmout = ++motg->dcd_retries == MSM_CHG_DCD_MAX_RETRIES;
 		if (is_dcd || tmout) {
-			if (motg->pdata->enable_dcd)
-				msm_chg_disable_dcd(motg);
+			msm_chg_disable_dcd(motg);
 			msm_chg_enable_primary_det(motg);
 			delay = MSM_CHG_PRIMARY_DET_TIME;
 			motg->chg_state = USB_CHG_STATE_DCD_DONE;
@@ -2316,9 +2335,13 @@
 	case OTG_STATE_UNDEFINED:
 		msm_otg_reset(otg->phy);
 		msm_otg_init_sm(motg);
-		psy = power_supply_get_by_name("usb");
-		if (!psy)
-			pr_err("couldn't get usb power supply\n");
+		if (!psy && legacy_power_supply) {
+			psy = power_supply_get_by_name("usb");
+
+			if (!psy)
+				pr_err("couldn't get usb power supply\n");
+		}
+
 		otg->phy->state = OTG_STATE_B_IDLE;
 		if (!test_bit(B_SESS_VLD, &motg->inputs) &&
 				test_bit(ID, &motg->inputs)) {
@@ -3359,6 +3382,69 @@
 	return count;
 }
 
+static int otg_power_get_property_usb(struct power_supply *psy,
+				  enum power_supply_property psp,
+				  union power_supply_propval *val)
+{
+	struct msm_otg *motg = container_of(psy, struct msm_otg, usb_psy);
+	switch (psp) {
+	case POWER_SUPPLY_PROP_SCOPE:
+		if (motg->host_mode)
+			val->intval = POWER_SUPPLY_SCOPE_SYSTEM;
+		else
+			val->intval = POWER_SUPPLY_SCOPE_DEVICE;
+		break;
+	case POWER_SUPPLY_PROP_CURRENT_MAX:
+			val->intval = motg->current_max;
+		break;
+	/* Reflect USB enumeration */
+	case POWER_SUPPLY_PROP_PRESENT:
+	case POWER_SUPPLY_PROP_ONLINE:
+		val->intval = motg->online;
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int otg_power_set_property_usb(struct power_supply *psy,
+				  enum power_supply_property psp,
+				  const union power_supply_propval *val)
+{
+	struct msm_otg *motg = container_of(psy, struct msm_otg, usb_psy);
+
+	switch (psp) {
+	/* Process PMIC notification in PRESENT prop */
+	case POWER_SUPPLY_PROP_PRESENT:
+		msm_otg_set_vbus_state(val->intval);
+		break;
+	/* The ONLINE property reflects if usb has enumerated */
+	case POWER_SUPPLY_PROP_ONLINE:
+		motg->online = val->intval;
+		break;
+	case POWER_SUPPLY_PROP_CURRENT_MAX:
+		motg->current_max = val->intval;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	power_supply_changed(&motg->usb_psy);
+	return 0;
+}
+
+static char *otg_pm_power_supplied_to[] = {
+	"battery",
+};
+
+static enum power_supply_property otg_pm_power_props_usb[] = {
+	POWER_SUPPLY_PROP_PRESENT,
+	POWER_SUPPLY_PROP_ONLINE,
+	POWER_SUPPLY_PROP_CURRENT_MAX,
+	POWER_SUPPLY_PROP_SCOPE,
+};
+
 const struct file_operations msm_otg_bus_fops = {
 	.open = msm_otg_bus_open,
 	.read = seq_read,
@@ -3434,6 +3520,38 @@
 	debugfs_remove_recursive(msm_otg_dbg_root);
 }
 
+#define MSM_OTG_CMD_ID		0x09
+#define MSM_OTG_DEVICE_ID	0x04
+#define MSM_OTG_VMID_IDX	0xFF
+#define MSM_OTG_MEM_TYPE	0x02
+struct msm_otg_scm_cmd_buf {
+	unsigned int device_id;
+	unsigned int vmid_idx;
+	unsigned int mem_type;
+} __attribute__ ((__packed__));
+
+static void msm_otg_pnoc_errata_fix(struct msm_otg *motg)
+{
+	int ret;
+	struct msm_otg_platform_data *pdata = motg->pdata;
+	struct msm_otg_scm_cmd_buf cmd_buf;
+
+	if (!pdata->pnoc_errata_fix)
+		return;
+
+	dev_dbg(motg->phy.dev, "applying fix for pnoc h/w issue\n");
+
+	cmd_buf.device_id = MSM_OTG_DEVICE_ID;
+	cmd_buf.vmid_idx = MSM_OTG_VMID_IDX;
+	cmd_buf.mem_type = MSM_OTG_MEM_TYPE;
+
+	ret = scm_call(SCM_SVC_CP, MSM_OTG_CMD_ID, &cmd_buf,
+				sizeof(cmd_buf), NULL, 0);
+
+	if (ret)
+		dev_err(motg->phy.dev, "scm command failed to update VMIDMT\n");
+}
+
 static u64 msm_otg_dma_mask = DMA_BIT_MASK(64);
 static struct platform_device *msm_otg_add_pdev(
 		struct platform_device *ofdev, const char *name)
@@ -3513,6 +3631,23 @@
 	return retval;
 }
 
+static int msm_otg_register_power_supply(struct platform_device *pdev,
+					struct msm_otg *motg)
+{
+	int ret;
+
+	ret = power_supply_register(&pdev->dev, &motg->usb_psy);
+	if (ret < 0) {
+		dev_err(motg->phy.dev,
+			"%s:power_supply_register usb failed\n",
+			__func__);
+		return ret;
+	}
+
+	legacy_power_supply = false;
+	return 0;
+}
+
 struct msm_otg_platform_data *msm_otg_dt_to_pdata(struct platform_device *pdev)
 {
 	struct device_node *node = pdev->dev.of_node;
@@ -3547,6 +3682,8 @@
 				&pdata->pmic_id_irq);
 	pdata->disable_reset_on_disconnect = of_property_read_bool(node,
 				"qcom,hsusb-otg-disable-reset");
+	pdata->pnoc_errata_fix = of_property_read_bool(node,
+				"qcom,hsusb-otg-pnoc-errata-fix");
 
 	return pdata;
 }
@@ -3745,6 +3882,9 @@
 	}
 	clk_prepare_enable(motg->core_clk);
 
+	/* Check if USB mem_type change is needed to workaround PNOC hw issue */
+	msm_otg_pnoc_errata_fix(motg);
+
 	writel(0, USB_USBINTR);
 	writel(0, USB_OTGSC);
 	/* Ensure that above STOREs are completed before enabling interrupts */
@@ -3771,8 +3911,8 @@
 	}
 
 	if (motg->async_irq) {
-		ret = request_irq(motg->async_irq, msm_otg_irq, IRQF_SHARED,
-							"msm_otg", motg);
+		ret = request_irq(motg->async_irq, msm_otg_irq,
+					IRQF_TRIGGER_RISING, "msm_otg", motg);
 		if (ret) {
 			dev_err(&pdev->dev, "request irq failed (ASYNC INT)\n");
 			goto free_irq;
@@ -3831,9 +3971,6 @@
 		dev_dbg(&pdev->dev, "mode debugfs file is"
 			"not available\n");
 
-	if (motg->pdata->otg_control == OTG_PMIC_CONTROL)
-		pm8921_charger_register_vbus_sn(&msm_otg_set_vbus_state);
-
 	if (motg->pdata->phy_type == SNPS_28NM_INTEGRATED_PHY) {
 		if (motg->pdata->otg_control == OTG_PMIC_CONTROL &&
 			(!(motg->pdata->mode == USB_OTG) ||
@@ -3863,6 +4000,28 @@
 			debug_bus_voting_enabled = true;
 	}
 
+	motg->usb_psy.name = "usb";
+	motg->usb_psy.type = POWER_SUPPLY_TYPE_USB;
+	motg->usb_psy.supplied_to = otg_pm_power_supplied_to;
+	motg->usb_psy.num_supplicants = ARRAY_SIZE(otg_pm_power_supplied_to);
+	motg->usb_psy.properties = otg_pm_power_props_usb;
+	motg->usb_psy.num_properties = ARRAY_SIZE(otg_pm_power_props_usb);
+	motg->usb_psy.get_property = otg_power_get_property_usb;
+	motg->usb_psy.set_property = otg_power_set_property_usb;
+
+	if (!pm8921_charger_register_vbus_sn(NULL)) {
+		/* if pm8921 use legacy implementation */
+		dev_dbg(motg->phy.dev, "%s: legacy support\n", __func__);
+		legacy_power_supply = true;
+	} else {
+		/* otherwise register our own power supply */
+		if (!msm_otg_register_power_supply(pdev, motg))
+			psy = &motg->usb_psy;
+	}
+
+	if (legacy_power_supply && pdata->otg_control == OTG_PMIC_CONTROL)
+		pm8921_charger_register_vbus_sn(&msm_otg_set_vbus_state);
+
 	return 0;
 
 remove_phy:
diff --git a/drivers/video/msm/hdmi_msm.c b/drivers/video/msm/hdmi_msm.c
index deef4ab..9f30041 100644
--- a/drivers/video/msm/hdmi_msm.c
+++ b/drivers/video/msm/hdmi_msm.c
@@ -3225,6 +3225,9 @@
 	/* HDMI_ACR_PKT_CTRL[0x0024] */
 	uint32 acr_pck_ctrl_reg = HDMI_INP(0x0024);
 
+	/* Clear N/CTS selection bits */
+	acr_pck_ctrl_reg &= ~(3 << 4);
+
 	if (enabled) {
 		const struct hdmi_disp_mode_timing_type *timing =
 			hdmi_common_get_supported_mode(video_format);
@@ -3629,6 +3632,9 @@
 
 void hdmi_msm_audio_sample_rate_reset(int rate)
 {
+	if (msm_hdmi_sample_rate == rate)
+		return;
+
 	msm_hdmi_sample_rate = rate;
 
 	if (hdmi_msm_state->hdcp_enable)
diff --git a/drivers/video/msm/mddi_quickvx.c b/drivers/video/msm/mddi_quickvx.c
index 95e7d41..37c147d 100644
--- a/drivers/video/msm/mddi_quickvx.c
+++ b/drivers/video/msm/mddi_quickvx.c
@@ -263,22 +263,10 @@
 
 int ql_mddi_write(uint32 address, uint32 value)
 {
-	uint32 regval = 0;
 	int ret = 0;
 
 	ret = mddi_queue_register_write(address, value, TRUE, 0);
 
-	if (!ret) {
-		ret = mddi_queue_register_read(address, &regval, TRUE, 0);
-		if (regval != value) {
-			MDDI_MSG_DEBUG("\nMismatch: ql_mddi_write[0x%x]->0x%x "
-				"r0x%x\n", address, value, regval);
-		} else {
-			MDDI_MSG_DEBUG("\nMatch: ql_mddi_write[0x%x]->0x%x "
-				"r0x%x\n", address, value, regval);
-		}
-	}
-
 	return ret;
 }
 
@@ -294,8 +282,6 @@
 
 int ql_send_spi_cmd_to_lcd(uint32 index, uint32 cmd)
 {
-	int retry, ret;
-	uint32 readval;
 
 	MDDI_MSG_DEBUG("\n %s(): index 0x%x, cmd 0x%x", __func__, index, cmd);
 	/* do the index phase */
@@ -308,18 +294,6 @@
 
 	/* set start */
 	ql_mddi_write(QUICKVX_SPI_CTRL_REG,  QL_SPI_CTRL_LCD_START);
-	retry = 0;
-
-	do {
-		ret = ql_mddi_read(QUICKVX_SPI_CTRL_REG, &readval);
-
-		if (ret || ++retry > 5) {
-			MDDI_MSG_DEBUG("\n ql_send_spi_cmd_to_lcd: retry "
-				"timeout at index phase, ret = %d", ret);
-			return -EIO;
-		}
-		mddi_wait(1);
-	} while ((readval & QL_SPI_CTRL_MASK_rTxDone) == 0);
 
 	/* do the command phase */
 	/* send 24 bits in the cmd phase */
@@ -331,18 +305,6 @@
 
 	/* set start */
 	ql_mddi_write(QUICKVX_SPI_CTRL_REG,  QL_SPI_CTRL_LCD_START);
-	retry = 0;
-
-	do {
-		ret = ql_mddi_read(QUICKVX_SPI_CTRL_REG, &readval);
-
-		if (ret || ++retry > 5) {
-			MDDI_MSG_DEBUG("\n ql_send_spi_cmd_to_lcd: retry "
-				"timeout at cmd phase, ret = %d", ret);
-			return -EIO;
-		}
-		mddi_wait(1);
-	} while ((readval & QL_SPI_CTRL_MASK_rTxDone) == 0);
 
 	return 0;
 }
@@ -350,8 +312,6 @@
 
 int ql_send_spi_data_from_lcd(uint32 index, uint32 *value)
 {
-	int retry, ret;
-	uint32 readval;
 
 	MDDI_MSG_DEBUG("\n %s(): index 0x%x", __func__, index);
 	/* do the index phase */
@@ -364,19 +324,6 @@
 
 	/* set start */
 	ql_mddi_write(QUICKVX_SPI_CTRL_REG,  QL_SPI_CTRL_LCD_START);
-	retry = 0;
-
-	do {
-		ret = ql_mddi_read(QUICKVX_SPI_CTRL_REG, &readval);
-
-		if (ret || ++retry > 5) {
-			MDDI_MSG_DEBUG("\n ql_send_spi_cmd_to_lcd: retry "
-				"timeout at index phase, ret = %d", ret);
-			return -EIO;
-		}
-		mddi_wait(1);
-	} while ((readval & QL_SPI_CTRL_MASK_rTxDone) == 0);
-
 	/* do the command phase */
 	/* send 8 bits  and read 24 bits in the cmd phase, so total 32 bits */
 	ql_mddi_write(QUICKVX_SPI_TLEN_REG, 31);
@@ -387,29 +334,9 @@
 
 	/* set start */
 	ql_mddi_write(QUICKVX_SPI_CTRL_REG,  QL_SPI_CTRL_LCD_START);
-	retry = 0;
 
-	do {
-		ret = ql_mddi_read(QUICKVX_SPI_CTRL_REG, &readval);
+	return 0;
 
-		if (ret || ++retry > 5) {
-			MDDI_MSG_DEBUG("\n ql_send_spi_cmd_to_lcd: retry "
-				"timeout at cmd phase, ret = %d", ret);
-			return -EIO;
-		}
-		mddi_wait(1);
-	} while ((readval & QL_SPI_CTRL_MASK_rTxDone) == 0);
-
-	/* value will appear at lower 16 bits */
-	ret = ql_mddi_read(QUICKVX_SPI_RX0_REG, value);
-
-	if (!ret) {
-		*value = *value & 0xffff;
-		MDDI_MSG_DEBUG("\n QUICKVX_SPI_RX0_REG value = 0x%x", *value);
-	} else
-		MDDI_MSG_DEBUG("\n Read QUICKVX_SPI_RX0_REG Failed");
-
-	return ret;
 }
 
 /* Global Variables */
diff --git a/drivers/video/msm/mdp.c b/drivers/video/msm/mdp.c
index 6c0d08d..25da094 100644
--- a/drivers/video/msm/mdp.c
+++ b/drivers/video/msm/mdp.c
@@ -2214,6 +2214,8 @@
 			mfd->panel.type == LCDC_PANEL ||
 			mfd->panel.type == LVDS_PANEL)
 		mdp4_lcdc_off(pdev);
+	else if (mfd->panel.type == MDDI_PANEL)
+		mdp4_mddi_off(pdev);
 
 	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
 	ret = panel_next_off(pdev);
@@ -2268,6 +2270,9 @@
 				mfd->panel.type == LCDC_PANEL ||
 				mfd->panel.type == LVDS_PANEL) {
 			mdp4_lcdc_on(pdev);
+		} else if (mfd->panel.type == MDDI_PANEL) {
+			mdp_vsync_cfg_regs(mfd, FALSE);
+			mdp4_mddi_on(pdev);
 		}
 
 		mdp_clk_ctrl(0);
@@ -2668,10 +2673,12 @@
 		mfd->ov0_wb_buf->size = mdp_pdata->ov0_wb_size;
 		mfd->ov1_wb_buf->size = mdp_pdata->ov1_wb_size;
 		mfd->mem_hid = mdp_pdata->mem_hid;
+		mfd->avtimer_phy = mdp_pdata->avtimer_phy;
 	} else {
 		mfd->ov0_wb_buf->size = 0;
 		mfd->ov1_wb_buf->size = 0;
 		mfd->mem_hid = 0;
+		mfd->avtimer_phy = 0;
 	}
 
 	/* initialize Post Processing data*/
@@ -2706,6 +2713,9 @@
 			  mdp_vsync_resync_workqueue_handler);
 		mfd->hw_refresh = FALSE;
 
+		if (mfd->panel.type == MDDI_PANEL)
+			mdp4_mddi_rdptr_init(0);
+
 		if (mfd->panel.type == EXT_MDDI_PANEL) {
 			/* 15 fps -> 66 msec */
 			mfd->refresh_timer_duration = (66 * HZ / 1000);
@@ -2828,15 +2838,7 @@
 		mfd->start_histogram = mdp_histogram_start;
 		mfd->stop_histogram = mdp_histogram_stop;
 		mdp4_display_intf_sel(if_no, DSI_CMD_INTF);
-
-		mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
-		spin_lock_irqsave(&mdp_spin_lock, flag);
-		mdp_intr_mask |= INTR_OVERLAY0_DONE;
-		outp32(MDP_INTR_ENABLE, mdp_intr_mask);
-		spin_unlock_irqrestore(&mdp_spin_lock, flag);
-		mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
 #else
-
 		mfd->dma_fnc = mdp_dma2_update;
 		mfd->do_histogram = mdp_do_histogram;
 		mfd->start_histogram = mdp_histogram_start;
@@ -2863,7 +2865,7 @@
 		pdata->on = mdp4_dtv_on;
 		pdata->off = mdp4_dtv_off;
 		mfd->hw_refresh = TRUE;
-		mfd->cursor_update = mdp_hw_cursor_update;
+		mfd->cursor_update = mdp_hw_cursor_sync_update;
 		mfd->dma_fnc = mdp4_dtv_overlay;
 		mfd->dma = &dma_e_data;
 		mfd->do_histogram = mdp_do_histogram;
@@ -2948,6 +2950,7 @@
 				mdp_clk_ctrl(0);
 				return -ENODEV;
 			}
+			mdp4_wfd_init(0);
 			pdata->on = mdp4_overlay_writeback_on;
 			pdata->off = mdp4_overlay_writeback_off;
 			mfd->dma_fnc = mdp4_writeback_overlay;
diff --git a/drivers/video/msm/mdp.h b/drivers/video/msm/mdp.h
index d939c62..b4a7f79 100644
--- a/drivers/video/msm/mdp.h
+++ b/drivers/video/msm/mdp.h
@@ -796,6 +796,10 @@
 {
 	return 0;
 }
+static inline int mdp4_mddi_off(struct platform_device *pdev)
+{
+	return 0;
+}
 static inline int mdp4_dsi_cmd_on(struct platform_device *pdev)
 {
 	return 0;
@@ -808,6 +812,19 @@
 {
 	return 0;
 }
+static inline int mdp4_mddi_on(struct platform_device *pdev)
+{
+	return 0;
+}
+#endif
+
+
+#ifndef CONFIG_FB_MSM_MDDI
+static inline void mdp4_mddi_rdptr_init(int cndx)
+{
+	/* empty */
+}
+
 #endif
 
 void set_cont_splashScreen_status(int);
diff --git a/drivers/video/msm/mdp4.h b/drivers/video/msm/mdp4.h
index 180a18a..3ea196a 100644
--- a/drivers/video/msm/mdp4.h
+++ b/drivers/video/msm/mdp4.h
@@ -393,6 +393,7 @@
 	ulong intr_dsi_err;
 	ulong kickoff_ov0;
 	ulong kickoff_ov1;
+	ulong kickoff_ov2;
 	ulong kickoff_dmap;
 	ulong kickoff_dmae;
 	ulong kickoff_dmas;
@@ -593,12 +594,13 @@
 void mdp4_overlay_dmap_xy(struct mdp4_overlay_pipe *pipe);
 void mdp4_overlay_dmae_cfg(struct msm_fb_data_type *mfd, int atv);
 void mdp4_overlay_dmae_xy(struct mdp4_overlay_pipe *pipe);
-int mdp4_overlay_pipe_staged(int mixer);
+int mdp4_overlay_pipe_staged(struct mdp4_overlay_pipe *pipe);
 void mdp4_lcdc_primary_vsyn(void);
 void mdp4_overlay0_done_lcdc(int cndx);
-void mdp4_overlay0_done_mddi(struct mdp_dma_data *dma);
+void mdp4_overlay0_done_mddi(int cndx);
 void mdp4_dma_p_done_mddi(struct mdp_dma_data *dma);
 void mdp4_dmap_done_dsi_cmd(int cndx);
+void mdp4_dmap_done_mddi(int cndx);
 void mdp4_dmap_done_dsi_video(int cndx);
 void mdp4_dmap_done_lcdc(int cndx);
 void mdp4_overlay1_done_dtv(void);
@@ -609,6 +611,7 @@
 void mdp4_overlay_lcdc_vsync_push(struct msm_fb_data_type *mfd,
 				struct mdp4_overlay_pipe *pipe);
 void mdp4_mddi_overlay_dmas_restore(void);
+void mdp4_dtv_set_avparams(struct mdp4_overlay_pipe *pipe, int id);
 
 #ifndef CONFIG_FB_MSM_MIPI_DSI
 void mdp4_mddi_dma_busy_wait(struct msm_fb_data_type *mfd);
@@ -672,6 +675,12 @@
 void mdp4_dsi_video_overlay_blt(struct msm_fb_data_type *mfd,
 					struct msmfb_overlay_blt *req);
 void mdp4_dsi_video_base_swap(int cndx, struct mdp4_overlay_pipe *pipe);
+static inline void mdp4_mddi_blt_start(struct msm_fb_data_type *mfd)
+{
+}
+static inline void mdp4_mddi_blt_stop(struct msm_fb_data_type *mfd)
+{
+}
 
 #ifdef CONFIG_FB_MSM_MDP40
 static inline void mdp3_dsi_cmd_dma_busy_wait(struct msm_fb_data_type *mfd)
@@ -680,6 +689,8 @@
 }
 #endif
 #else     /* CONFIG_FB_MSM_MIPI_DSI */
+void mdp4_mddi_blt_start(struct msm_fb_data_type *mfd);
+void mdp4_mddi_blt_stop(struct msm_fb_data_type *mfd);
 int mdp4_mddi_overlay_blt_offset(struct msm_fb_data_type *mfd,
 					struct msmfb_overlay_blt *req);
 void mdp4_mddi_overlay_blt(struct msm_fb_data_type *mfd,
@@ -687,6 +698,7 @@
 int mdp4_mddi_overlay_blt_start(struct msm_fb_data_type *mfd);
 int mdp4_mddi_overlay_blt_stop(struct msm_fb_data_type *mfd);
 void mdp4_mddi_blt_dmap_busy_wait(struct msm_fb_data_type *mfd);
+void mdp4_mddi_rdptr_init(int cndx);
 static inline int mdp4_dsi_overlay_blt_start(struct msm_fb_data_type *mfd)
 {
 	return -ENODEV;
@@ -776,11 +788,37 @@
 {
 	/* empty */
 }
-#else /* CONFIG_FB_MSM_MIPI_DSI */
+#else /* CONFIG_FB_MSM_MDP303 */
 void mdp4_dsi_cmd_del_timer(void);
+static inline int mdp4_mddi_on(struct platform_device *pdev)
+{
+	return 0;
+}
+static inline int mdp4_mddi_off(struct platform_device *pdev)
+{
+	return 0;
+}
+static inline void mdp4_mddi_wait4vsync(int cndx, long long *vtime)
+{
+}
+static inline void mdp4_mddi_vsync_ctrl(struct fb_info *info,
+				int enable)
+{
+}
+static inline void mdp4_mddi_pipe_queue(int cndx,
+			struct mdp4_overlay_pipe *pipe)
+{
+}
 #endif
 #else  /* CONFIG_FB_MSM_MIPI_DSI */
 
+int mdp4_mddi_off(struct platform_device *pdev);
+int mdp4_mddi_on(struct platform_device *pdev);
+void mdp4_mddi_wait4vsync(int cndx, long long *vtime);
+void mdp4_mddi_vsync_ctrl(struct fb_info *info, int enable);
+void mdp4_mddi_pipe_queue(int cndx, struct mdp4_overlay_pipe *pipe);
+void mdp4_overlay_update_mddi(struct msm_fb_data_type *mfd);
+
 static inline int mdp4_dsi_cmd_on(struct platform_device *pdev)
 {
 	return 0;
@@ -884,7 +922,7 @@
 int mdp4_overlay_writeback_on(struct platform_device *pdev);
 int mdp4_overlay_writeback_off(struct platform_device *pdev);
 void mdp4_writeback_overlay(struct msm_fb_data_type *mfd);
-void mdp4_overlay1_done_writeback(struct mdp_dma_data *dma);
+void mdp4_overlay2_done_wfd(struct mdp_dma_data *dma);
 
 int mdp4_writeback_start(struct fb_info *info);
 int mdp4_writeback_stop(struct fb_info *info);
@@ -934,19 +972,17 @@
 u32 mdp4_get_mixer_num(u32 panel_type);
 
 #ifndef CONFIG_FB_MSM_WRITEBACK_MSM_PANEL
-static inline void mdp4_writeback_dma_busy_wait(struct msm_fb_data_type *mfd)
+static inline void mdp4_wfd_pipe_queue(int cndx, struct mdp4_overlay_pipe *pipe)
 {
 	/* empty */
 }
-static inline void mdp4_writeback_kickoff_video(struct msm_fb_data_type *mfd,
-		struct mdp4_overlay_pipe *pipe)
+static inline void mdp4_wfd_init(int cndx)
 {
 	/* empty */
 }
 #else
-void mdp4_writeback_dma_busy_wait(struct msm_fb_data_type *mfd);
-void mdp4_writeback_kickoff_video(struct msm_fb_data_type *mfd,
-		struct mdp4_overlay_pipe *pipe);
+void mdp4_wfd_pipe_queue(int cndx, struct mdp4_overlay_pipe *pipe);
+void mdp4_wfd_init(int cndx);
 #endif
 
 #endif /* MDP_H */
diff --git a/drivers/video/msm/mdp4_overlay.c b/drivers/video/msm/mdp4_overlay.c
index c7e811f..04da6f5 100644
--- a/drivers/video/msm/mdp4_overlay.c
+++ b/drivers/video/msm/mdp4_overlay.c
@@ -1568,36 +1568,19 @@
 		mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
 }
 
-int mdp4_overlay_pipe_staged(int mixer)
+int mdp4_overlay_pipe_staged(struct mdp4_overlay_pipe *pipe)
 {
-	uint32 data, mask, i, off;
-	int p1, p2;
+	uint32 data, mask;
+	int mixer;
 
-	if (mixer == MDP4_MIXER2)
-		off = 0x100F0;
-	else
-		off = 0x10100;
+	mixer = pipe->mixer_num;
+	data = ctrl->mixer_cfg[mixer];
 
-	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
-	data = inpdw(MDP_BASE + off);
-	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
-	p1 = 0;
-	p2 = 0;
-	for (i = 0; i < 8; i++) {
-		mask = data & 0x0f;
-		if (mask) {
-			if (mask <= 4)
-				p1++;
-			else
-				p2++;
-		}
-		data >>= 4;
-	}
+	mask = 0x0f;
+	mask <<= (4 * pipe->pipe_num);
+	data &= mask;
 
-	if (mixer)
-		return p2;
-	else
-		return p1;
+	return data;
 }
 
 int mdp4_mixer_info(int mixer_num, struct mdp_mixer_info *info)
@@ -1649,8 +1632,14 @@
 		data |= stage;
 	}
 
+	/*
+	 * stage_commit may be called from overlay_unset
+	 * for command panel, mdp clocks may be off at this time.
+	 * so mdp clock enabled is necessary
+	 */
 	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
 	mdp_clk_ctrl(1);
+
 	mdp4_mixer_blend_setup(mixer);
 
 	off = 0;
@@ -1788,6 +1777,7 @@
 	struct mdp4_overlay_pipe *bspipe;
 	int ptype, pnum, pndx, mixer;
 	int format, alpha_enable, alpha;
+	struct mdp4_iommu_pipe_info iom;
 
 	if (pipe->pipe_type != OVERLAY_TYPE_BF)
 		return;
@@ -1802,6 +1792,7 @@
 		return;
 	}
 
+	iom = bspipe->iommu;
 	ptype = bspipe->pipe_type;
 	pnum = bspipe->pipe_num;
 	pndx = bspipe->pipe_ndx;
@@ -1815,6 +1806,7 @@
 	bspipe->src_format = format;
 	bspipe->alpha_enable = alpha_enable;
 	bspipe->alpha = alpha;
+	bspipe->iommu = iom;
 
 	bspipe->pipe_used++;	/* mark base layer pipe used */
 
@@ -1959,7 +1951,7 @@
 	struct mdp4_overlay_pipe *d_pipe;
 	struct mdp4_overlay_pipe *s_pipe;
 	struct blend_cfg *blend;
-	int i, off, ptype, alpha_drop = 0;
+	int i, off, alpha_drop = 0;
 	int d_alpha, s_alpha;
 	unsigned char *overlay_base;
 	uint32 c0, c1, c2, base_premulti;
@@ -1989,7 +1981,8 @@
 		if (s_pipe->pipe_type == OVERLAY_TYPE_VIDEO &&
 			((s_pipe->op_mode & MDP4_OP_SCALEY_EN) ||
 			(s_pipe->op_mode & MDP4_OP_SCALEX_EN)) &&
-			!(s_pipe->op_mode & MDP4_OP_SCALEY_PIXEL_RPT))
+			!(s_pipe->op_mode & (MDP4_OP_SCALEX_PIXEL_RPT |
+						MDP4_OP_SCALEY_PIXEL_RPT)))
 			alpha_drop = 1;
 
 		d_pipe = mdp4_background_layer(mixer, s_pipe);
@@ -2026,25 +2019,17 @@
 			} else
 				blend->op = MDP4_BLEND_BG_ALPHA_FG_CONST;
 		} else if (d_alpha) {
-			ptype = mdp4_overlay_format2type(s_pipe->src_format);
-			if (ptype == OVERLAY_TYPE_VIDEO) {
-				blend->op = (MDP4_BLEND_FG_ALPHA_BG_PIXEL |
-					MDP4_BLEND_FG_INV_ALPHA);
-				if ((!(s_pipe->flags & MDP_BLEND_FG_PREMULT)) &&
-						((i != MDP4_MIXER_STAGE0) ||
-							(!base_premulti)))
-					blend->op |=
-						MDP4_BLEND_BG_ALPHA_BG_PIXEL;
-				else
-					blend->fg_alpha = 0xff;
+			blend->op = (MDP4_BLEND_FG_ALPHA_BG_PIXEL |
+				MDP4_BLEND_FG_INV_ALPHA);
+			if ((!(d_pipe->flags & MDP_BLEND_FG_PREMULT)) &&
+					((i != MDP4_MIXER_STAGE0) ||
+						(!base_premulti)))
+				blend->op |=
+					MDP4_BLEND_BG_ALPHA_BG_PIXEL;
+			else
+				blend->fg_alpha = 0xff;
 
-				blend->co3_sel = 0; /* use bg alpha */
-			} else {
-				/* s_pipe is rgb without alpha */
-				blend->op = (MDP4_BLEND_FG_ALPHA_FG_CONST |
-					    MDP4_BLEND_BG_ALPHA_BG_CONST);
-				blend->bg_alpha = 0;
-			}
+			blend->co3_sel = 0; /* use bg alpha */
 		}
 
 		if (s_pipe->transp != MDP_TRANSP_NOP) {
@@ -2441,6 +2426,11 @@
 	 * zorder 2 == stage 2 == 4
 	 */
 	if (req->id == MSMFB_NEW_REQUEST) {  /* new request */
+		if (mdp4_overlay_pipe_staged(pipe)) {
+			pr_err("%s: ndx=%d still staged\n", __func__,
+						pipe->pipe_ndx);
+			return -EPERM;
+		}
 		pipe->pipe_used++;
 		pipe->mixer_num = mixer;
 		pr_debug("%s: zorder=%d pipe ndx=%d num=%d\n", __func__,
@@ -2876,6 +2866,8 @@
 				mdp4_dsi_video_blt_start(mfd);
 			else if (ctrl->panel_mode & MDP4_PANEL_DSI_CMD)
 				mdp4_dsi_cmd_blt_start(mfd);
+			else if (ctrl->panel_mode & MDP4_PANEL_MDDI)
+				mdp4_mddi_blt_start(mfd);
 			pr_info("%s mixer0 start blt [%d] from %d to %d.\n",
 				__func__,
 				flag,
@@ -2924,6 +2916,8 @@
 				mdp4_dsi_video_blt_stop(mfd);
 			else if (ctrl->panel_mode & MDP4_PANEL_DSI_CMD)
 				mdp4_dsi_cmd_blt_stop(mfd);
+			else if (ctrl->panel_mode & MDP4_PANEL_MDDI)
+				mdp4_mddi_blt_stop(mfd);
 			pr_info("%s mixer0 stop blt [%d] from %d to %d.\n",
 				__func__,
 				flag,
@@ -3188,20 +3182,14 @@
 	else {
 		/* mixer 0 */
 		ctrl->mixer0_played = 0;
-		if (ctrl->panel_mode & MDP4_PANEL_MDDI) {
-			if (mfd->panel_power_on)
-				mdp4_mddi_blt_dmap_busy_wait(mfd);
-		}
+
 	}
 
 	mdp4_overlay_reg_flush(pipe, 1);
 	mdp4_mixer_stage_down(pipe, 0);
 
 	if (pipe->mixer_num == MDP4_MIXER0) {
-		if (ctrl->panel_mode & MDP4_PANEL_MDDI) {
-			if (mfd->panel_power_on)
-				mdp4_mddi_overlay_restore();
-		}
+
 	} else {	/* mixer1, DTV, ATV */
 		if (ctrl->panel_mode & MDP4_PANEL_DTV)
 			mdp4_overlay_dtv_unset(mfd, pipe);
@@ -3225,6 +3213,8 @@
 			mdp4_dsi_cmd_wait4vsync(0, vtime);
 		else if (ctrl->panel_mode & MDP4_PANEL_LCDC)
 			mdp4_lcdc_wait4vsync(0, vtime);
+		else if (ctrl->panel_mode & MDP4_PANEL_MDDI)
+			mdp4_mddi_wait4vsync(0, vtime);
 	} else if (hdmi_prim_display || info->node == 1) {
 		mdp4_dtv_wait4vsync(0, vtime);
 	}
@@ -3248,6 +3238,8 @@
 			mdp4_dsi_cmd_vsync_ctrl(info, cmd);
 		else if (ctrl->panel_mode & MDP4_PANEL_LCDC)
 			mdp4_lcdc_vsync_ctrl(info, cmd);
+		else if (ctrl->panel_mode & MDP4_PANEL_MDDI)
+			mdp4_mddi_vsync_ctrl(info, cmd);
 	} else if (hdmi_prim_display || info->node == 1)
 		mdp4_dtv_vsync_ctrl(info, cmd);
 
@@ -3468,10 +3460,6 @@
 
 	mdp4_overlay_mdp_perf_req(mfd, ctrl->plist);
 
-	if (pipe->mixer_num == MDP4_MIXER2 ||
-				ctrl->panel_mode & MDP4_PANEL_MDDI)
-		goto mddi;
-
 	if (pipe->mixer_num == MDP4_MIXER0) {
 		if (ctrl->panel_mode & MDP4_PANEL_DSI_CMD) {
 			/* cndx = 0 */
@@ -3483,44 +3471,24 @@
 			/* cndx = 0 */
 			mdp4_lcdc_pipe_queue(0, pipe);
 		}
+		if (ctrl->panel_mode & MDP4_PANEL_MDDI) {
+			/* cndx = 0 */
+			mdp4_mddi_pipe_queue(0, pipe);
+		}
 	} else if (pipe->mixer_num == MDP4_MIXER1) {
-		if (ctrl->panel_mode & MDP4_PANEL_DTV)
+		if (ctrl->panel_mode & MDP4_PANEL_DTV) {
 			mdp4_dtv_pipe_queue(0, pipe);/* cndx = 0 */
+			mdp4_dtv_set_avparams(pipe, img->memory_id);
+		}
+	} else if (pipe->mixer_num == MDP4_MIXER2) {
+		ctrl->mixer2_played++;
+		if (ctrl->panel_mode & MDP4_PANEL_WRITEBACK)
+			mdp4_wfd_pipe_queue(0, pipe);/* cndx = 0 */
 	}
 
 	mutex_unlock(&mfd->dma->ov_mutex);
 	return ret;
 
-mddi:
-	if (pipe->pipe_type == OVERLAY_TYPE_VIDEO) {
-		mdp4_overlay_vg_setup(pipe);    /* video/graphic pipe */
-	} else {
-		mdp4_overlay_rgb_setup(pipe);	/* rgb pipe */
-	}
-
-	mdp4_mixer_stage_up(pipe, 0);
-
-	if (pipe->mixer_num == MDP4_MIXER2) {
-		ctrl->mixer2_played++;
-		if (ctrl->panel_mode & MDP4_PANEL_WRITEBACK) {
-			mdp4_writeback_dma_busy_wait(mfd);
-			mdp4_writeback_kickoff_video(mfd, pipe);
-		}
-	} else if (ctrl->panel_mode & MDP4_PANEL_MDDI) {
-		if (pipe->flags & MDP_OV_PLAY_NOWAIT) {
-			mdp4_stat.overlay_play[pipe->mixer_num]++;
-			mutex_unlock(&mfd->dma->ov_mutex);
-			goto end;
-		}
-		mdp4_mixer_stage_commit(pipe->mixer_num);
-		mdp4_mddi_dma_busy_wait(mfd);
-		mdp4_mddi_kickoff_video(mfd, pipe);
-	}
-
-	if (!(pipe->flags & MDP_OV_PLAY_NOWAIT))
-		mdp4_iommu_unmap(pipe);
-	mdp4_stat.overlay_play[pipe->mixer_num]++;
-
 end:
 	mutex_unlock(&mfd->dma->ov_mutex);
 
diff --git a/drivers/video/msm/mdp4_overlay_dsi_cmd.c b/drivers/video/msm/mdp4_overlay_dsi_cmd.c
index ecdd567..10410a7 100644
--- a/drivers/video/msm/mdp4_overlay_dsi_cmd.c
+++ b/drivers/video/msm/mdp4_overlay_dsi_cmd.c
@@ -31,14 +31,14 @@
 #include "mipi_dsi.h"
 #include "mdp4.h"
 
-static int dsi_state;
-
-#define TOUT_PERIOD	HZ	/* 1 second */
-#define MS_100		(HZ/10)	/* 100 ms */
-
 static int vsync_start_y_adjust = 4;
 
 #define MAX_CONTROLLER	1
+
+/*
+ * VSYNC_EXPIRE_TICK == 0 means clock always on
+ * VSYNC_EXPIRE_TICK == 4 is recommended
+ */
 #define VSYNC_EXPIRE_TICK 4
 
 static struct vsycn_ctrl {
@@ -51,10 +51,10 @@
 	u32 ov_done;
 	u32 dmap_koff;
 	u32 dmap_done;
+	u32 pan_display;
 	uint32 rdptr_intr_tot;
 	uint32 rdptr_sirq_tot;
 	atomic_t suspend;
-	atomic_t vsync_resume;
 	int wait_vsync_cnt;
 	int blt_change;
 	int blt_free;
@@ -71,7 +71,6 @@
 	int vsync_enabled;
 	int clk_enabled;
 	int clk_control;
-	int new_update;
 	ktime_t vsync_time;
 	struct work_struct clk_work;
 } vsync_ctrl_db[MAX_CONTROLLER];
@@ -229,8 +228,10 @@
 
 	vctrl = &vsync_ctrl_db[cndx];
 
-	if (atomic_read(&vctrl->suspend) > 0)
+	if (atomic_read(&vctrl->suspend)) {
+		pr_err("%s: suspended, no more pipe queue\n", __func__);
 		return;
+	}
 
 	mutex_lock(&vctrl->update_lock);
 	undx =  vctrl->update_ndx;
@@ -363,12 +364,14 @@
 		mdp4_dsi_cmd_blt_ov_update(pipe);
 		pipe->ov_cnt++;
 		vctrl->ov_koff++;
+		INIT_COMPLETION(vctrl->ov_comp);
 		vsync_irq_enable(INTR_OVERLAY0_DONE, MDP_OVERLAY0_TERM);
 	} else {
+		INIT_COMPLETION(vctrl->dmap_comp);
 		vsync_irq_enable(INTR_DMA_P_DONE, MDP_DMAP_TERM);
 		vctrl->dmap_koff++;
 	}
-	pr_debug("%s: kickoff\n", __func__);
+	pr_debug("%s: kickoff, pid=%d\n", __func__, current->pid);
 	/* kickoff overlay engine */
 	mdp4_stat.kickoff_ov0++;
 	outpdw(MDP_BASE + 0x0004, 0);
@@ -392,15 +395,14 @@
 {
 	struct vsycn_ctrl *vctrl;
 	unsigned long flags;
-	int clk_set_on = 0;
 	int cndx = 0;
+	int clk_set_on = 0;
 
 	vctrl = &vsync_ctrl_db[cndx];
 
-	pr_debug("%s: clk_enabled=%d vsycn_enabeld=%d req=%d\n", __func__,
-		vctrl->clk_enabled, vctrl->vsync_enabled, enable);
-
 	mutex_lock(&vctrl->update_lock);
+	pr_debug("%s: clk_enabled=%d vsync_enabled=%d req=%d\n", __func__,
+		vctrl->clk_enabled, vctrl->vsync_enabled, enable);
 
 	if (vctrl->vsync_enabled == enable) {
 		mutex_unlock(&vctrl->update_lock);
@@ -410,6 +412,10 @@
 	vctrl->vsync_enabled = enable;
 
 	if (enable) {
+		spin_lock_irqsave(&vctrl->spin_lock, flags);
+		vctrl->clk_control = 0;
+		vctrl->expire_tick = 0;
+		spin_unlock_irqrestore(&vctrl->spin_lock, flags);
 		if (vctrl->clk_enabled == 0) {
 			pr_debug("%s: SET_CLK_ON\n", __func__);
 			mipi_dsi_clk_cfg(1);
@@ -417,26 +423,16 @@
 			vctrl->clk_enabled = 1;
 			clk_set_on = 1;
 		}
-		spin_lock_irqsave(&vctrl->spin_lock, flags);
-		vctrl->clk_control = 0;
-		vctrl->expire_tick = 0;
-		vctrl->new_update = 1;
 		if (clk_set_on) {
 			vsync_irq_enable(INTR_PRIMARY_RDPTR,
 						MDP_PRIM_RDPTR_TERM);
 		}
-		spin_unlock_irqrestore(&vctrl->spin_lock, flags);
 	} else {
 		spin_lock_irqsave(&vctrl->spin_lock, flags);
-		vctrl->clk_control = 1;
-		if (vctrl->clk_enabled)
-			vctrl->expire_tick = VSYNC_EXPIRE_TICK;
+		vctrl->expire_tick = VSYNC_EXPIRE_TICK;
 		spin_unlock_irqrestore(&vctrl->spin_lock, flags);
 	}
 	mutex_unlock(&vctrl->update_lock);
-
-	if (vctrl->vsync_enabled &&  atomic_read(&vctrl->suspend) == 0)
-		atomic_set(&vctrl->vsync_resume, 1);
 }
 
 void mdp4_dsi_cmd_wait4vsync(int cndx, long long *vtime)
@@ -514,19 +510,27 @@
 	struct vsycn_ctrl *vctrl;
 
 	vctrl = &vsync_ctrl_db[cndx];
-	pr_debug("%s: ISR, cpu=%d\n", __func__, smp_processor_id());
+	pr_debug("%s: ISR, tick=%d pan=%d cpu=%d\n", __func__,
+		vctrl->expire_tick, vctrl->pan_display, smp_processor_id());
 	vctrl->rdptr_intr_tot++;
-	vctrl->vsync_time = ktime_get();
 
 	spin_lock(&vctrl->spin_lock);
+	vctrl->vsync_time = ktime_get();
 
 	complete_all(&vctrl->vsync_comp);
 	vctrl->wait_vsync_cnt = 0;
 
 	if (vctrl->expire_tick) {
 		vctrl->expire_tick--;
-		if (vctrl->expire_tick == 0)
-			schedule_work(&vctrl->clk_work);
+		if (vctrl->expire_tick == 0) {
+			if (vctrl->pan_display <= 0) {
+				vctrl->clk_control = 1;
+				schedule_work(&vctrl->clk_work);
+			} else {
+				/* wait one more vsycn */
+				vctrl->expire_tick += 1;
+			}
+		}
 	}
 	spin_unlock(&vctrl->spin_lock);
 }
@@ -546,11 +550,15 @@
 	spin_lock(&vctrl->spin_lock);
 	vsync_irq_disable(INTR_DMA_P_DONE, MDP_DMAP_TERM);
 	vctrl->dmap_done++;
+
+	if (vctrl->pan_display)
+		vctrl->pan_display--;
+
 	diff = vctrl->ov_done - vctrl->dmap_done;
 	pr_debug("%s: ov_koff=%d ov_done=%d dmap_koff=%d dmap_done=%d cpu=%d\n",
 		__func__, vctrl->ov_koff, vctrl->ov_done, vctrl->dmap_koff,
 		vctrl->dmap_done, smp_processor_id());
-	complete_all(&vctrl->dmap_comp);
+	complete(&vctrl->dmap_comp);
 	if (diff <= 0) {
 		if (vctrl->blt_wait)
 			vctrl->blt_wait = 0;
@@ -586,7 +594,7 @@
 	spin_lock(&vctrl->spin_lock);
 	vsync_irq_disable(INTR_OVERLAY0_DONE, MDP_OVERLAY0_TERM);
 	vctrl->ov_done++;
-	complete_all(&vctrl->ov_comp);
+	complete(&vctrl->ov_comp);
 	diff = vctrl->ov_done - vctrl->dmap_done;
 
 	pr_debug("%s: ov_koff=%d ov_done=%d dmap_koff=%d dmap_done=%d cpu=%d\n",
@@ -622,20 +630,24 @@
 
 static void clk_ctrl_work(struct work_struct *work)
 {
+	unsigned long flags;
 	struct vsycn_ctrl *vctrl =
 		container_of(work, typeof(*vctrl), clk_work);
-	unsigned long flags;
 
 	mutex_lock(&vctrl->update_lock);
+	spin_lock_irqsave(&vctrl->spin_lock, flags);
 	if (vctrl->clk_control && vctrl->clk_enabled) {
-		pr_debug("%s: SET_CLK_OFF\n", __func__);
-		mdp_clk_ctrl(0);
-		mipi_dsi_clk_cfg(0);
-		spin_lock_irqsave(&vctrl->spin_lock, flags);
 		vsync_irq_disable(INTR_PRIMARY_RDPTR, MDP_PRIM_RDPTR_TERM);
 		vctrl->clk_enabled = 0;
 		vctrl->clk_control = 0;
 		spin_unlock_irqrestore(&vctrl->spin_lock, flags);
+		/* make sure dsi link is idle */
+		mipi_dsi_mdp_busy_wait();
+		mipi_dsi_clk_cfg(0);
+		mdp_clk_ctrl(0);
+		pr_debug("%s: SET_CLK_OFF, pid=%d\n", __func__, current->pid);
+	} else {
+		spin_unlock_irqrestore(&vctrl->spin_lock, flags);
 	}
 	mutex_unlock(&vctrl->update_lock);
 }
@@ -647,12 +659,12 @@
 	struct vsycn_ctrl *vctrl;
 	ssize_t ret = 0;
 	unsigned long flags;
+	u64 vsync_tick;
 
 	cndx = 0;
 	vctrl = &vsync_ctrl_db[0];
 
-	if (atomic_read(&vctrl->suspend) > 0 ||
-		atomic_read(&vctrl->vsync_resume) == 0)
+	if (atomic_read(&vctrl->suspend) > 0)
 		return 0;
 
 	spin_lock_irqsave(&vctrl->spin_lock, flags);
@@ -661,10 +673,17 @@
 	vctrl->wait_vsync_cnt++;
 	spin_unlock_irqrestore(&vctrl->spin_lock, flags);
 
-	wait_for_completion(&vctrl->vsync_comp);
+	ret = wait_for_completion_interruptible(&vctrl->vsync_comp);
+	if (ret)
+		return ret;
 
-	ret = snprintf(buf, PAGE_SIZE, "VSYNC=%llu",
-			ktime_to_ns(vctrl->vsync_time));
+	spin_lock_irqsave(&vctrl->spin_lock, flags);
+	vsync_tick = ktime_to_ns(vctrl->vsync_time);
+	spin_unlock_irqrestore(&vctrl->spin_lock, flags);
+
+	ret = snprintf(buf, PAGE_SIZE, "VSYNC=%llu", vsync_tick);
+	pr_debug("%s: UEVENT\n", __func__);
+
 	buf[strlen(buf) + 1] = '\0';
 	return ret;
 }
@@ -689,7 +708,6 @@
 	init_completion(&vctrl->dmap_comp);
 	init_completion(&vctrl->vsync_comp);
 	spin_lock_init(&vctrl->spin_lock);
-	atomic_set(&vctrl->vsync_resume, 1);
 	INIT_WORK(&vctrl->clk_work, clk_ctrl_work);
 }
 
@@ -698,20 +716,6 @@
 	primary_rdptr_isr(0);
 }
 
-void mdp4_overlay_dsi_state_set(int state)
-{
-	unsigned long flag;
-
-	spin_lock_irqsave(&mdp_spin_lock, flag);
-	dsi_state = state;
-	spin_unlock_irqrestore(&mdp_spin_lock, flag);
-}
-
-int mdp4_overlay_dsi_state_get(void)
-{
-	return dsi_state;
-}
-
 static __u32 msm_fb_line_length(__u32 fb_index, __u32 xres, int bpp)
 {
 	/*
@@ -936,6 +940,7 @@
 	pipe->srcp0_addr = (uint32)src;
 
 	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+	mdp_clk_ctrl(1);
 
 	mdp4_overlay_rgb_setup(pipe);
 
@@ -951,6 +956,7 @@
 
 	mdp4_mixer_stage_commit(pipe->mixer_num);
 	/* MDP cmd block disable */
+	mdp_clk_ctrl(0);
 	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
 }
 
@@ -985,9 +991,10 @@
 	struct msm_fb_data_type *mfd;
 	struct vsycn_ctrl *vctrl;
 
-	pr_debug("%s+:\n", __func__);
+	pr_debug("%s+: pid=%d\n", __func__, current->pid);
 
 	mfd = (struct msm_fb_data_type *)platform_get_drvdata(pdev);
+	mfd->cont_splash_done = 1;
 
 	vctrl = &vsync_ctrl_db[cndx];
 	vctrl->mfd = mfd;
@@ -1000,7 +1007,6 @@
 	mdp4_iommu_attach();
 
 	atomic_set(&vctrl->suspend, 0);
-	pr_debug("%s-:\n", __func__);
 
 	if (!vctrl->sysfs_created) {
 		ret = sysfs_create_group(&vctrl->dev->kobj,
@@ -1016,6 +1022,8 @@
 		vctrl->sysfs_created = 1;
 	}
 
+	pr_debug("%s-:\n", __func__);
+
 	return ret;
 }
 
@@ -1028,8 +1036,9 @@
 	struct mdp4_overlay_pipe *pipe;
 	struct vsync_update *vp;
 	int undx;
+	int need_wait, cnt;
 
-	pr_debug("%s+:\n", __func__);
+	pr_debug("%s+: pid=%d\n", __func__, current->pid);
 
 	mfd = (struct msm_fb_data_type *)platform_get_drvdata(pdev);
 
@@ -1040,26 +1049,40 @@
 		return ret;
 	}
 
+	need_wait = 0;
+	mutex_lock(&vctrl->update_lock);
 	atomic_set(&vctrl->suspend, 1);
-	atomic_set(&vctrl->vsync_resume, 0);
 
 	complete_all(&vctrl->vsync_comp);
 
+	pr_debug("%s: clk=%d pan=%d\n", __func__,
+			vctrl->clk_enabled, vctrl->pan_display);
+	if (vctrl->clk_enabled)
+		need_wait = 1;
+	mutex_unlock(&vctrl->update_lock);
+
+	cnt = 0;
+	if (need_wait) {
+		while (vctrl->clk_enabled) {
+			msleep(20);
+			cnt++;
+			if (cnt > 10)
+				break;
+		}
+	}
+
+	/* message for system suspnded */
+	if (cnt > 10)
+		pr_err("%s:Error,  mdp clocks NOT off\n", __func__);
+	else
+		pr_debug("%s: mdp clocks off at cnt=%d\n", __func__, cnt);
+
 	/* sanity check, free pipes besides base layer */
 	mdp4_overlay_unset_mixer(pipe->mixer_num);
 	mdp4_mixer_stage_down(pipe, 1);
 	mdp4_overlay_pipe_free(pipe);
 	vctrl->base_pipe = NULL;
 
-	if (vctrl->clk_enabled) {
-		/*
-		 * in case of suspend, vsycn_ctrl off is not
-		 * received from frame work which left clock on
-		 * then, clock need to be turned off here
-		 */
-		mdp_clk_ctrl(0);
-	}
-
 	undx =  vctrl->update_ndx;
 	vp = &vctrl->vlist[undx];
 	if (vp->update_cnt) {
@@ -1070,23 +1093,7 @@
 		vp->update_cnt = 0;     /* empty queue */
 	}
 
-	vctrl->clk_enabled = 0;
-	vctrl->vsync_enabled = 0;
-	vctrl->clk_control = 0;
-	vctrl->expire_tick = 0;
-
-	vsync_irq_disable(INTR_PRIMARY_RDPTR, MDP_PRIM_RDPTR_TERM);
-
-
 	pr_debug("%s-:\n", __func__);
-
-	/*
-	 * footswitch off
-	 * this will casue all mdp register
-	 * to be reset to default
-	 * after footswitch on later
-	 */
-
 	return ret;
 }
 
@@ -1121,38 +1128,50 @@
 	struct vsycn_ctrl *vctrl;
 	struct mdp4_overlay_pipe *pipe;
 	unsigned long flags;
-	long long tick;
+	int clk_set_on = 0;
 
+	mutex_lock(&mfd->dma->ov_mutex);
 	vctrl = &vsync_ctrl_db[cndx];
 
-	if (!mfd->panel_power_on)
+	if (!mfd->panel_power_on) {
+		mutex_unlock(&mfd->dma->ov_mutex);
 		return;
+	}
 
 	pipe = vctrl->base_pipe;
 	if (pipe == NULL) {
 		pr_err("%s: NO base pipe\n", __func__);
+		mutex_unlock(&mfd->dma->ov_mutex);
 		return;
 	}
 
 	mutex_lock(&vctrl->update_lock);
-	if (!vctrl->clk_enabled) {
-		pr_err("%s: mdp clocks disabled\n", __func__);
+	if (atomic_read(&vctrl->suspend)) {
 		mutex_unlock(&vctrl->update_lock);
+		mutex_unlock(&mfd->dma->ov_mutex);
+		pr_err("%s: suspended, no more pan display\n", __func__);
 		return;
-
 	}
-	mutex_unlock(&vctrl->update_lock);
 
 	spin_lock_irqsave(&vctrl->spin_lock, flags);
-	if (vctrl->expire_tick) {
-		/*
-		 * in the middle of shutting clocks down
-		 * delay to allow pan display to go through
-		 */
+	vctrl->clk_control = 0;
+	vctrl->pan_display++;
+	if (!vctrl->clk_enabled) {
+		clk_set_on = 1;
+		vctrl->clk_enabled = 1;
 		vctrl->expire_tick = VSYNC_EXPIRE_TICK;
 	}
 	spin_unlock_irqrestore(&vctrl->spin_lock, flags);
 
+	if (clk_set_on) {
+		pr_debug("%s: SET_CLK_ON\n", __func__);
+		mipi_dsi_clk_cfg(1);
+		mdp_clk_ctrl(1);
+		vsync_irq_enable(INTR_PRIMARY_RDPTR, MDP_PRIM_RDPTR_TERM);
+	}
+
+	mutex_unlock(&vctrl->update_lock);
+
 	if (pipe->mixer_stage == MDP4_MIXER_STAGE_BASE) {
 		mdp4_mipi_vsync_enable(mfd, pipe, 0);
 		mdp4_overlay_setup_pipe_addr(mfd, pipe);
@@ -1160,12 +1179,7 @@
 	}
 
 	mdp4_overlay_mdp_perf_upd(mfd, 1);
-
-	mutex_lock(&mfd->dma->ov_mutex);
 	mdp4_dsi_cmd_pipe_commit(cndx, 0);
-	mutex_unlock(&mfd->dma->ov_mutex);
-
-	mdp4_dsi_cmd_wait4vsync(cndx, &tick);
-
 	mdp4_overlay_mdp_perf_upd(mfd, 0);
+	mutex_unlock(&mfd->dma->ov_mutex);
 }
diff --git a/drivers/video/msm/mdp4_overlay_dsi_video.c b/drivers/video/msm/mdp4_overlay_dsi_video.c
index 6aa101f..5551c9d 100644
--- a/drivers/video/msm/mdp4_overlay_dsi_video.c
+++ b/drivers/video/msm/mdp4_overlay_dsi_video.c
@@ -194,8 +194,6 @@
 	}
 	spin_unlock_irqrestore(&vctrl->spin_lock, flags);
 
-	mdp4_overlay_mdp_perf_upd(vctrl->mfd, 1);
-
 	if (vctrl->blt_change) {
 		pipe = vctrl->base_pipe;
 		spin_lock_irqsave(&vctrl->spin_lock, flags);
@@ -382,6 +380,7 @@
 	struct vsycn_ctrl *vctrl;
 	ssize_t ret = 0;
 	unsigned long flags;
+	u64 vsync_tick;
 
 	cndx = 0;
 	vctrl = &vsync_ctrl_db[0];
@@ -395,10 +394,15 @@
 		INIT_COMPLETION(vctrl->vsync_comp);
 	vctrl->wait_vsync_cnt++;
 	spin_unlock_irqrestore(&vctrl->spin_lock, flags);
-	wait_for_completion(&vctrl->vsync_comp);
+	ret = wait_for_completion_interruptible(&vctrl->vsync_comp);
+	if (ret)
+		return ret;
 
-	ret = snprintf(buf, PAGE_SIZE, "VSYNC=%llu",
-			ktime_to_ns(vctrl->vsync_time));
+	spin_lock_irqsave(&vctrl->spin_lock, flags);
+	vsync_tick = ktime_to_ns(vctrl->vsync_time);
+	spin_unlock_irqrestore(&vctrl->spin_lock, flags);
+
+	ret = snprintf(buf, PAGE_SIZE, "VSYNC=%llu", vsync_tick);
 	buf[strlen(buf) + 1] = '\0';
 	return ret;
 }
@@ -918,9 +922,10 @@
 	cndx = 0;
 	vctrl = &vsync_ctrl_db[cndx];
 	pr_debug("%s: cpu=%d\n", __func__, smp_processor_id());
-	vctrl->vsync_time = ktime_get();
 
 	spin_lock(&vctrl->spin_lock);
+	vctrl->vsync_time = ktime_get();
+
 	if (vctrl->wait_vsync_cnt) {
 		complete_all(&vctrl->vsync_comp);
 		vctrl->wait_vsync_cnt = 0;
@@ -1068,15 +1073,19 @@
 	uint8 *buf;
 	unsigned int buf_offset;
 	int bpp;
-	int cndx = 0;
+	int cnt, cndx = 0;
 	struct vsycn_ctrl *vctrl;
 	struct mdp4_overlay_pipe *pipe;
 
+	mutex_lock(&mfd->dma->ov_mutex);
+
 	vctrl = &vsync_ctrl_db[cndx];
 	pipe = vctrl->base_pipe;
 
-	if (!pipe || !mfd->panel_power_on)
+	if (!pipe || !mfd->panel_power_on) {
+		mutex_unlock(&mfd->dma->ov_mutex);
 		return;
+	}
 
 	pr_debug("%s: cpu=%d pid=%d\n", __func__,
 			smp_processor_id(), current->pid);
@@ -1096,15 +1105,15 @@
 	mdp_update_pm(mfd, vsync_ctrl_db[0].vsync_time);
 	mdp4_overlay_mdp_perf_upd(mfd, 1);
 
-	mutex_lock(&mfd->dma->ov_mutex);
-	mdp4_dsi_video_pipe_commit(cndx, 0);
-	mutex_unlock(&mfd->dma->ov_mutex);
-
-	if (pipe->ov_blt_addr)
-		mdp4_dsi_video_wait4ov(cndx);
-	else
-		mdp4_dsi_video_wait4dmap(cndx);
+	cnt = mdp4_dsi_video_pipe_commit(cndx, 0);
+	if (cnt) {
+		if (pipe->ov_blt_addr)
+			mdp4_dsi_video_wait4ov(cndx);
+		else
+			mdp4_dsi_video_wait4dmap(cndx);
+	}
 
 	mdp4_overlay_mdp_perf_upd(mfd, 0);
+	mutex_unlock(&mfd->dma->ov_mutex);
 }
 
diff --git a/drivers/video/msm/mdp4_overlay_dtv.c b/drivers/video/msm/mdp4_overlay_dtv.c
index 21e5d1d..e71f49f 100644
--- a/drivers/video/msm/mdp4_overlay_dtv.c
+++ b/drivers/video/msm/mdp4_overlay_dtv.c
@@ -33,7 +33,6 @@
 #include "mdp4.h"
 
 #define DTV_BASE	0xD0000
-
 static int dtv_enabled;
 
 /*#define DEBUG*/
@@ -55,14 +54,6 @@
 static int first_pixel_start_x;
 static int first_pixel_start_y;
 
-void mdp4_dtv_base_swap(int cndx, struct mdp4_overlay_pipe *pipe)
-{
-#ifdef BYPASS4
-	if (hdmi_prim_display)
-		dtv_pipe = pipe;
-#endif
-}
-
 #define MAX_CONTROLLER	1
 
 static struct vsycn_ctrl {
@@ -85,6 +76,10 @@
 	struct vsync_update vlist[2];
 	int vsync_irq_enabled;
 	ktime_t vsync_time;
+	uint32 *avtimer;
+	int vg1fd;
+	int vg2fd;
+	unsigned long long avtimer_tick;
 } vsync_ctrl_db[MAX_CONTROLLER];
 
 static void vsync_irq_enable(int intr, int term)
@@ -322,9 +317,14 @@
 	struct vsycn_ctrl *vctrl;
 	ssize_t ret = 0;
 	unsigned long flags;
+	char ch = '\0';
+	int vg1fd = -1, vg2fd = -1;
+	unsigned long long avtimer_tick = 0;
+	u64 vsync_tick = 0;
 
 	cndx = 0;
 	vctrl = &vsync_ctrl_db[0];
+	memset(buf, 0, 64);
 
 	if (atomic_read(&vctrl->suspend) > 0 ||
 		!external_common_state->hpd_state ||
@@ -336,13 +336,31 @@
 		INIT_COMPLETION(vctrl->vsync_comp);
 	vctrl->wait_vsync_cnt++;
 	spin_unlock_irqrestore(&vctrl->spin_lock, flags);
-	wait_for_completion(&vctrl->vsync_comp);
 
-	ret = snprintf(buf, PAGE_SIZE, "VSYNC=%llu",
-			ktime_to_ns(vctrl->vsync_time));
-	buf[strlen(buf) + 1] = '\0';
+	ret = wait_for_completion_interruptible(&vctrl->vsync_comp);
+	if (ret)
+		return ret;
+
+	spin_lock_irqsave(&vctrl->spin_lock, flags);
+	vg1fd = vctrl->vg1fd;
+	vg2fd = vctrl->vg2fd;
+	avtimer_tick = vctrl->avtimer_tick;
+	vsync_tick = ktime_to_ns(vctrl->vsync_time);
+	spin_unlock_irqrestore(&vctrl->spin_lock, flags);
+
+	ret = snprintf(buf, PAGE_SIZE,
+			"VSYNC=%llu%c"
+			"AVSYNCTP=%llu%c"
+			"VG1MEMID=%d%c"
+			"VG2MEMID=%d",
+			vsync_tick,
+			ch, avtimer_tick,
+			ch, vg1fd,
+			ch, vg2fd);
+
 	return ret;
 }
+
 void mdp4_dtv_vsync_init(int cndx)
 {
 	struct vsycn_ctrl *vctrl;
@@ -369,6 +387,24 @@
 	spin_lock_init(&vctrl->spin_lock);
 }
 
+void mdp4_dtv_base_swap(int cndx, struct mdp4_overlay_pipe *pipe)
+{
+	struct vsycn_ctrl *vctrl;
+
+	if (!hdmi_prim_display) {
+		pr_err("%s: failed, hdmi is not primary\n", __func__);
+		return;
+	}
+
+	if (cndx >= MAX_CONTROLLER) {
+		pr_err("%s: out or range: cndx=%d\n", __func__, cndx);
+		return;
+	}
+
+	vctrl = &vsync_ctrl_db[cndx];
+	vctrl->base_pipe = pipe;
+}
+
 static int mdp4_dtv_start(struct msm_fb_data_type *mfd)
 {
 	int dtv_width;
@@ -587,6 +623,13 @@
 		pr_debug("%s: kobject_uevent(KOBJ_ADD)\n", __func__);
 		vctrl->sysfs_created = 1;
 	}
+
+	if (mfd->avtimer_phy && (vctrl->avtimer == NULL)) {
+		vctrl->avtimer = (uint32 *)ioremap(mfd->avtimer_phy, 8);
+		if (vctrl->avtimer == NULL)
+			pr_err(" avtimer ioremap fail\n");
+	}
+
 	pr_info("%s:\n", __func__);
 
 	return ret;
@@ -656,6 +699,11 @@
 		vp->update_cnt = 0;     /* empty queue */
 	}
 
+	if (vctrl->avtimer != NULL) {
+		iounmap(vctrl->avtimer);
+		vctrl->avtimer = NULL;
+	}
+
 	ret = panel_next_off(pdev);
 	mdp_footswitch_ctrl(FALSE);
 
@@ -834,7 +882,7 @@
 	struct vsycn_ctrl *vctrl;
 
 	vctrl = &vsync_ctrl_db[cndx];
-	if (vctrl->base_pipe != NULL)
+	if (vctrl->base_pipe == NULL)
 		return 0;
 
 	if (pipe->mixer_stage == MDP4_MIXER_STAGE_BASE &&
@@ -842,6 +890,12 @@
 		result = mdp4_dtv_stop(mfd);
 		vctrl->base_pipe = NULL;
 	}
+
+	if (pipe->pipe_num == OVERLAY_PIPE_VG1)
+		vctrl->vg1fd = -1;
+	else if (pipe->pipe_num == OVERLAY_PIPE_VG2)
+		vctrl->vg2fd = -1;
+
 	return result;
 }
 
@@ -851,13 +905,24 @@
 {
 	int cndx;
 	struct vsycn_ctrl *vctrl;
+	uint32 *tp, LSW;
 
 	cndx = 0;
 	vctrl = &vsync_ctrl_db[cndx];
 	pr_debug("%s: cpu=%d\n", __func__, smp_processor_id());
-	vctrl->vsync_time = ktime_get();
 
 	spin_lock(&vctrl->spin_lock);
+	vctrl->vsync_time = ktime_get();
+	vctrl->avtimer_tick = 0;
+
+	if (vctrl->avtimer && ((vctrl->vg1fd > 0) || (vctrl->vg2fd > 0))) {
+		tp = vctrl->avtimer;
+		LSW = inpdw(tp);
+		tp++;
+		vctrl->avtimer_tick = (unsigned long long) inpdw(tp);
+		vctrl->avtimer_tick = ((vctrl->avtimer_tick << 32) | LSW);
+	}
+
 	if (vctrl->wait_vsync_cnt) {
 		complete_all(&vctrl->vsync_comp);
 		vctrl->wait_vsync_cnt = 0;
@@ -1049,9 +1114,13 @@
 	int cndx = 0;
 	struct vsycn_ctrl *vctrl;
 	struct mdp4_overlay_pipe *pipe;
+	int wait = 0;
 
-	if (!mfd->panel_power_on)
+	mutex_lock(&mfd->dma->ov_mutex);
+	if (!mfd->panel_power_on) {
+		mutex_unlock(&mfd->dma->ov_mutex);
 		return;
+	}
 
 	vctrl = &vsync_ctrl_db[cndx];
 	if (vctrl->base_pipe == NULL)
@@ -1061,6 +1130,7 @@
 
 	if (pipe == NULL) {
 		pr_warn("%s: dtv_pipe == NULL\n", __func__);
+		mutex_unlock(&mfd->dma->ov_mutex);
 		return;
 	}
 
@@ -1077,9 +1147,27 @@
 	}
 	mdp_update_pm(mfd, vsync_ctrl_db[0].vsync_time);
 
-	mutex_lock(&mfd->dma->ov_mutex);
+	if (hdmi_prim_display)
+		wait = 1;
+
 	mdp4_overlay_mdp_perf_upd(mfd, 1);
-	mdp4_dtv_pipe_commit(cndx, 0);
+	mdp4_dtv_pipe_commit(cndx, wait);
 	mdp4_overlay_mdp_perf_upd(mfd, 0);
 	mutex_unlock(&mfd->dma->ov_mutex);
 }
+
+void mdp4_dtv_set_avparams(struct mdp4_overlay_pipe *pipe, int id)
+{
+	struct vsycn_ctrl *vctrl;
+
+	if (pipe == NULL) {
+		pr_warn("%s: dtv_pipe == NULL\n", __func__);
+		return;
+	}
+	vctrl = &vsync_ctrl_db[0];
+	if (pipe->pipe_num == OVERLAY_PIPE_VG1)
+		vctrl->vg1fd = id;
+	else if (pipe->pipe_num == OVERLAY_PIPE_VG2)
+		vctrl->vg2fd = id;
+}
+
diff --git a/drivers/video/msm/mdp4_overlay_lcdc.c b/drivers/video/msm/mdp4_overlay_lcdc.c
index 1f5136f..df5c262 100644
--- a/drivers/video/msm/mdp4_overlay_lcdc.c
+++ b/drivers/video/msm/mdp4_overlay_lcdc.c
@@ -366,6 +366,7 @@
 	struct vsycn_ctrl *vctrl;
 	ssize_t ret = 0;
 	unsigned long flags;
+	u64 vsync_tick;
 
 	cndx = 0;
 	vctrl = &vsync_ctrl_db[0];
@@ -379,10 +380,15 @@
 		INIT_COMPLETION(vctrl->vsync_comp);
 	vctrl->wait_vsync_cnt++;
 	spin_unlock_irqrestore(&vctrl->spin_lock, flags);
-	wait_for_completion(&vctrl->vsync_comp);
+	ret = wait_for_completion_interruptible(&vctrl->vsync_comp);
+	if (ret)
+		return ret;
 
-	ret = snprintf(buf, PAGE_SIZE, "VSYNC=%llu",
-			ktime_to_ns(vctrl->vsync_time));
+	spin_lock_irqsave(&vctrl->spin_lock, flags);
+	vsync_tick = ktime_to_ns(vctrl->vsync_time);
+	spin_unlock_irqrestore(&vctrl->spin_lock, flags);
+
+	ret = snprintf(buf, PAGE_SIZE, "VSYNC=%llu", vsync_tick);
 	buf[strlen(buf) + 1] = '\0';
 	return ret;
 }
@@ -804,9 +810,10 @@
 	cndx = 0;
 	vctrl = &vsync_ctrl_db[cndx];
 	pr_debug("%s: cpu=%d\n", __func__, smp_processor_id());
-	vctrl->vsync_time = ktime_get();
 
 	spin_lock(&vctrl->spin_lock);
+	vctrl->vsync_time = ktime_get();
+
 	if (vctrl->wait_vsync_cnt) {
 		complete_all(&vctrl->vsync_comp);
 		vctrl->wait_vsync_cnt = 0;
@@ -954,16 +961,19 @@
 	uint8 *buf;
 	unsigned int buf_offset;
 	int bpp;
-	int cndx = 0;
+	int cnt, cndx = 0;
 	struct vsycn_ctrl *vctrl;
 	struct mdp4_overlay_pipe *pipe;
 
+	mutex_lock(&mfd->dma->ov_mutex);
 
 	vctrl = &vsync_ctrl_db[cndx];
 	pipe = vctrl->base_pipe;
 
-	if (!pipe || !mfd->panel_power_on)
+	if (!pipe || !mfd->panel_power_on) {
+		mutex_unlock(&mfd->dma->ov_mutex);
 		return;
+	}
 
 	pr_debug("%s: cpu=%d pid=%d\n", __func__,
 			smp_processor_id(), current->pid);
@@ -983,14 +993,14 @@
 
 	mdp4_overlay_mdp_perf_upd(mfd, 1);
 
-	mutex_lock(&mfd->dma->ov_mutex);
-	mdp4_lcdc_pipe_commit(cndx, 0);
-	mutex_unlock(&mfd->dma->ov_mutex);
-
-	if (pipe->ov_blt_addr)
-		mdp4_lcdc_wait4ov(cndx);
-	else
-		mdp4_lcdc_wait4dmap(cndx);
+	cnt = mdp4_lcdc_pipe_commit(cndx, 0);
+	if (cnt) {
+		if (pipe->ov_blt_addr)
+			mdp4_lcdc_wait4ov(cndx);
+		else
+			mdp4_lcdc_wait4dmap(cndx);
+	}
 
 	mdp4_overlay_mdp_perf_upd(mfd, 0);
+	mutex_unlock(&mfd->dma->ov_mutex);
 }
diff --git a/drivers/video/msm/mdp4_overlay_mddi.c b/drivers/video/msm/mdp4_overlay_mddi.c
index be4a89a..ca84eca 100644
--- a/drivers/video/msm/mdp4_overlay_mddi.c
+++ b/drivers/video/msm/mdp4_overlay_mddi.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2009-2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2009-2012, 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
@@ -17,38 +17,680 @@
 #include <linux/time.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
-#include <linux/hrtimer.h>
 #include <linux/delay.h>
-#include <mach/hardware.h>
 #include <linux/io.h>
-
-#include <asm/system.h>
-#include <asm/mach-types.h>
 #include <linux/semaphore.h>
 #include <linux/spinlock.h>
-
 #include <linux/fb.h>
+#include <asm/system.h>
+#include <asm/mach-types.h>
+#include <mach/hardware.h>
 
 #include "mdp.h"
 #include "msm_fb.h"
 #include "mdp4.h"
 
-static struct mdp4_overlay_pipe *mddi_pipe;
-static struct msm_fb_data_type *mddi_mfd;
-static int busy_wait_cnt;
+static int mddi_state;
+
+#define TOUT_PERIOD	HZ	/* 1 second */
+#define MS_100		(HZ/10)	/* 100 ms */
 
 static int vsync_start_y_adjust = 4;
 
-static int dmap_vsync_enable;
+#define MAX_CONTROLLER	1
+#define VSYNC_EXPIRE_TICK 8
 
-void mdp_dmap_vsync_set(int enable)
+static struct vsycn_ctrl {
+	struct device *dev;
+	int inited;
+	int update_ndx;
+	int expire_tick;
+	int blt_wait;
+	u32 ov_koff;
+	u32 ov_done;
+	u32 dmap_koff;
+	u32 dmap_done;
+	uint32 rdptr_intr_tot;
+	uint32 rdptr_sirq_tot;
+	atomic_t suspend;
+	int wait_vsync_cnt;
+	int blt_change;
+	int blt_free;
+	int blt_end;
+	int uevent;
+	struct mutex update_lock;
+	struct completion ov_comp;
+	struct completion dmap_comp;
+	struct completion vsync_comp;
+	spinlock_t spin_lock;
+	struct msm_fb_data_type *mfd;
+	struct mdp4_overlay_pipe *base_pipe;
+	struct vsync_update vlist[2];
+	int vsync_enabled;
+	int clk_enabled;
+	int clk_control;
+	int new_update;
+	ktime_t vsync_time;
+	struct work_struct vsync_work;
+	struct work_struct clk_work;
+} vsync_ctrl_db[MAX_CONTROLLER];
+
+static void vsync_irq_enable(int intr, int term)
 {
-	dmap_vsync_enable = enable;
+	unsigned long flag;
+
+	spin_lock_irqsave(&mdp_spin_lock, flag);
+	/* no need to clear other interrupts for comamnd mode */
+	mdp_intr_mask |= intr;
+	outp32(MDP_INTR_ENABLE, mdp_intr_mask);
+	mdp_enable_irq(term);
+	spin_unlock_irqrestore(&mdp_spin_lock, flag);
 }
 
-int mdp_dmap_vsync_get(void)
+static void vsync_irq_disable(int intr, int term)
 {
-	return dmap_vsync_enable;
+	unsigned long flag;
+
+	spin_lock_irqsave(&mdp_spin_lock, flag);
+	/* no need to clrear other interrupts for comamnd mode */
+	mdp_intr_mask &= ~intr;
+	outp32(MDP_INTR_ENABLE, mdp_intr_mask);
+	mdp_disable_irq_nosync(term);
+	spin_unlock_irqrestore(&mdp_spin_lock, flag);
+}
+
+static void mdp4_mddi_blt_ov_update(struct mdp4_overlay_pipe *pipe)
+{
+	uint32 off, addr;
+	int bpp;
+	char *overlay_base;
+
+	if (pipe->ov_blt_addr == 0)
+		return;
+
+	bpp = 3; /* overlay ouput is RGB888 */
+	off = 0;
+	if (pipe->ov_cnt & 0x01)
+		off = pipe->src_height * pipe->src_width * bpp;
+	addr = pipe->ov_blt_addr + off;
+	/* overlay 0 */
+	overlay_base = MDP_BASE + MDP4_OVERLAYPROC0_BASE;/* 0x10000 */
+	outpdw(overlay_base + 0x000c, addr);
+	outpdw(overlay_base + 0x001c, addr);
+}
+
+static void mdp4_mddi_blt_dmap_update(struct mdp4_overlay_pipe *pipe)
+{
+	uint32 off, addr;
+	int bpp;
+
+	if (pipe->ov_blt_addr == 0)
+		return;
+
+	bpp = 3; /* overlay ouput is RGB888 */
+	off = 0;
+	if (pipe->dmap_cnt & 0x01)
+		off = pipe->src_height * pipe->src_width * bpp;
+	addr = pipe->dma_blt_addr + off;
+
+	/* dmap */
+	MDP_OUTP(MDP_BASE + 0x90008, addr);
+}
+
+static void mdp4_mddi_wait4dmap(int cndx);
+static void mdp4_mddi_wait4ov(int cndx);
+
+static void mdp4_mddi_do_blt(struct msm_fb_data_type *mfd, int enable)
+{
+	unsigned long flags;
+	int cndx = 0;
+	struct vsycn_ctrl *vctrl;
+	struct mdp4_overlay_pipe *pipe;
+	int need_wait;
+
+	vctrl = &vsync_ctrl_db[cndx];
+	pipe = vctrl->base_pipe;
+
+	mdp4_allocate_writeback_buf(mfd, MDP4_MIXER0);
+
+	if (mfd->ov0_wb_buf->write_addr == 0) {
+		pr_err("%s: no blt_base assigned\n", __func__);
+		return;
+	}
+
+	spin_lock_irqsave(&vctrl->spin_lock, flags);
+	if (enable && pipe->ov_blt_addr == 0) {
+		vctrl->blt_change++;
+		if (vctrl->dmap_koff != vctrl->dmap_done) {
+			INIT_COMPLETION(vctrl->dmap_comp);
+			need_wait = 1;
+		}
+	} else if (enable == 0 && pipe->ov_blt_addr) {
+		vctrl->blt_change++;
+		if (vctrl->ov_koff != vctrl->dmap_done) {
+			INIT_COMPLETION(vctrl->dmap_comp);
+			need_wait = 1;
+		}
+	}
+	spin_unlock_irqrestore(&vctrl->spin_lock, flags);
+
+	if (need_wait)
+		mdp4_mddi_wait4dmap(0);
+
+	spin_lock_irqsave(&vctrl->spin_lock, flags);
+	if (enable && pipe->ov_blt_addr == 0) {
+		pipe->ov_blt_addr = mfd->ov0_wb_buf->write_addr;
+		pipe->dma_blt_addr = mfd->ov0_wb_buf->read_addr;
+		pipe->ov_cnt = 0;
+		pipe->dmap_cnt = 0;
+		vctrl->ov_koff = vctrl->dmap_koff;
+		vctrl->ov_done = vctrl->dmap_done;
+		vctrl->blt_free = 0;
+		vctrl->blt_wait = 0;
+		vctrl->blt_end = 0;
+		mdp4_stat.blt_mddi++;
+	} else if (enable == 0 && pipe->ov_blt_addr) {
+		pipe->ov_blt_addr = 0;
+		pipe->dma_blt_addr =  0;
+		vctrl->blt_end = 1;
+		vctrl->blt_free = 4;	/* 4 commits to free wb buf */
+	}
+
+	pr_debug("%s: changed=%d enable=%d ov_blt_addr=%x\n", __func__,
+		vctrl->blt_change, enable, (int)pipe->ov_blt_addr);
+
+	spin_unlock_irqrestore(&vctrl->spin_lock, flags);
+}
+
+/*
+ * mdp4_mddi_do_update:
+ * called from thread context
+ */
+void mdp4_mddi_pipe_queue(int cndx, struct mdp4_overlay_pipe *pipe)
+{
+	struct vsycn_ctrl *vctrl;
+	struct vsync_update *vp;
+	struct mdp4_overlay_pipe *pp;
+	int undx;
+
+	if (cndx >= MAX_CONTROLLER) {
+		pr_err("%s: out or range: cndx=%d\n", __func__, cndx);
+		return;
+	}
+
+	vctrl = &vsync_ctrl_db[cndx];
+
+	if (atomic_read(&vctrl->suspend) > 0)
+		return;
+
+	mutex_lock(&vctrl->update_lock);
+	undx =  vctrl->update_ndx;
+	vp = &vctrl->vlist[undx];
+
+	pp = &vp->plist[pipe->pipe_ndx - 1];	/* ndx start form 1 */
+
+	pr_debug("%s: vndx=%d pipe_ndx=%d expire=%x pid=%d\n", __func__,
+		undx, pipe->pipe_ndx, vctrl->expire_tick, current->pid);
+
+	*pp = *pipe;	/* clone it */
+	vp->update_cnt++;
+
+	mutex_unlock(&vctrl->update_lock);
+	mdp4_stat.overlay_play[pipe->mixer_num]++;
+}
+
+static void mdp4_mddi_blt_ov_update(struct mdp4_overlay_pipe *pipe);
+
+int mdp4_mddi_pipe_commit(void)
+{
+	int  i, undx;
+	int mixer = 0;
+	struct vsycn_ctrl *vctrl;
+	struct vsync_update *vp;
+	struct mdp4_overlay_pipe *pipe;
+	struct mdp4_overlay_pipe *real_pipe;
+	unsigned long flags;
+	int need_dmap_wait = 0;
+	int need_ov_wait = 0;
+	int cnt = 0;
+
+	vctrl = &vsync_ctrl_db[0];
+
+	mutex_lock(&vctrl->update_lock);
+	undx =  vctrl->update_ndx;
+	vp = &vctrl->vlist[undx];
+	pipe = vctrl->base_pipe;
+	mixer = pipe->mixer_num;
+
+	if (vp->update_cnt == 0) {
+		mutex_unlock(&vctrl->update_lock);
+		return cnt;
+	}
+
+	vctrl->update_ndx++;
+	vctrl->update_ndx &= 0x01;
+	vp->update_cnt = 0;     /* reset */
+	if (vctrl->blt_free) {
+		vctrl->blt_free--;
+		if (vctrl->blt_free == 0)
+			mdp4_free_writeback_buf(vctrl->mfd, mixer);
+	}
+	mutex_unlock(&vctrl->update_lock);
+
+	/* free previous committed iommu back to pool */
+	mdp4_overlay_iommu_unmap_freelist(mixer);
+
+	spin_lock_irqsave(&vctrl->spin_lock, flags);
+	if (pipe->ov_blt_addr) {
+		/* Blt */
+		if (vctrl->blt_wait)
+			need_dmap_wait = 1;
+		if (vctrl->ov_koff != vctrl->ov_done) {
+			INIT_COMPLETION(vctrl->ov_comp);
+			need_ov_wait = 1;
+		}
+	} else {
+		/* direct out */
+		if (vctrl->dmap_koff != vctrl->dmap_done) {
+			INIT_COMPLETION(vctrl->dmap_comp);
+			pr_debug("%s: wait, ok=%d od=%d dk=%d dd=%d cpu=%d\n",
+			 __func__, vctrl->ov_koff, vctrl->ov_done,
+			vctrl->dmap_koff, vctrl->dmap_done, smp_processor_id());
+			need_dmap_wait = 1;
+		}
+	}
+	spin_unlock_irqrestore(&vctrl->spin_lock, flags);
+
+	if (need_dmap_wait) {
+		pr_debug("%s: wait4dmap\n", __func__);
+		mdp4_mddi_wait4dmap(0);
+	}
+
+	if (need_ov_wait) {
+		pr_debug("%s: wait4ov\n", __func__);
+		mdp4_mddi_wait4ov(0);
+	}
+
+	if (pipe->ov_blt_addr) {
+		if (vctrl->blt_end) {
+			vctrl->blt_end = 0;
+			pipe->ov_blt_addr = 0;
+			pipe->dma_blt_addr =  0;
+		}
+	}
+
+	if (vctrl->blt_change) {
+		mdp4_overlayproc_cfg(pipe);
+		mdp4_overlay_dmap_xy(pipe);
+		vctrl->blt_change = 0;
+	}
+
+	pipe = vp->plist;
+	for (i = 0; i < OVERLAY_PIPE_MAX; i++, pipe++) {
+		if (pipe->pipe_used) {
+			cnt++;
+			real_pipe = mdp4_overlay_ndx2pipe(pipe->pipe_ndx);
+			if (real_pipe && real_pipe->pipe_used) {
+				/* pipe not unset */
+			mdp4_overlay_vsync_commit(pipe);
+			}
+			/* free previous iommu to freelist
+			* which will be freed at next
+			* pipe_commit
+			*/
+			mdp4_overlay_iommu_pipe_free(pipe->pipe_ndx, 0);
+			pipe->pipe_used = 0; /* clear */
+		}
+	}
+
+	mdp4_mixer_stage_commit(mixer);
+
+	pipe = vctrl->base_pipe;
+	spin_lock_irqsave(&vctrl->spin_lock, flags);
+	if (pipe->ov_blt_addr) {
+		mdp4_mddi_blt_ov_update(pipe);
+		pipe->ov_cnt++;
+		vctrl->ov_koff++;
+		vsync_irq_enable(INTR_OVERLAY0_DONE, MDP_OVERLAY0_TERM);
+	} else {
+		vsync_irq_enable(INTR_DMA_P_DONE, MDP_DMAP_TERM);
+		vctrl->dmap_koff++;
+	}
+	pr_debug("%s: kickoff\n", __func__);
+	/* kickoff overlay engine */
+	mdp4_stat.kickoff_ov0++;
+	outpdw(MDP_BASE + 0x0004, 0);
+	mb(); /* make sure kickoff ececuted */
+	spin_unlock_irqrestore(&vctrl->spin_lock, flags);
+
+	mdp4_stat.overlay_commit[pipe->mixer_num]++;
+
+	return cnt;
+}
+
+void mdp4_mddi_vsync_ctrl(struct fb_info *info, int enable)
+{
+	struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
+	struct vsycn_ctrl *vctrl;
+	unsigned long flags;
+	int clk_set_on = 0;
+	int cndx = 0;
+
+	vctrl = &vsync_ctrl_db[cndx];
+
+	pr_debug("%s: clk_enabled=%d vsycn_enabeld=%d req=%d\n", __func__,
+		vctrl->clk_enabled, vctrl->vsync_enabled, enable);
+
+	mutex_lock(&vctrl->update_lock);
+
+	if (vctrl->vsync_enabled == enable) {
+		mutex_unlock(&vctrl->update_lock);
+		return;
+	}
+
+	vctrl->vsync_enabled = enable;
+
+	if (enable) {
+		if (vctrl->clk_enabled == 0) {
+			pr_debug("%s: SET_CLK_ON\n", __func__);
+			mdp_clk_ctrl(1);
+			vctrl->clk_enabled = 1;
+			clk_set_on = 1;
+		}
+		spin_lock_irqsave(&vctrl->spin_lock, flags);
+		vctrl->clk_control = 0;
+		vctrl->expire_tick = 0;
+		vctrl->uevent = 1;
+		vctrl->new_update = 1;
+		if (clk_set_on) {
+			vsync_irq_enable(INTR_PRIMARY_RDPTR,
+						MDP_PRIM_RDPTR_TERM);
+	}
+		spin_unlock_irqrestore(&vctrl->spin_lock, flags);
+
+		mdp4_overlay_update_mddi(mfd);
+	} else {
+		spin_lock_irqsave(&vctrl->spin_lock, flags);
+		vctrl->clk_control = 1;
+		vctrl->uevent = 0;
+		if (vctrl->clk_enabled)
+			vctrl->expire_tick = VSYNC_EXPIRE_TICK;
+		spin_unlock_irqrestore(&vctrl->spin_lock, flags);
+	}
+	mutex_unlock(&vctrl->update_lock);
+}
+
+void mdp4_mddi_wait4vsync(int cndx, long long *vtime)
+{
+	struct vsycn_ctrl *vctrl;
+	struct mdp4_overlay_pipe *pipe;
+	unsigned long flags;
+
+	if (cndx >= MAX_CONTROLLER) {
+		pr_err("%s: out or range: cndx=%d\n", __func__, cndx);
+		return;
+	}
+
+	vctrl = &vsync_ctrl_db[cndx];
+	pipe = vctrl->base_pipe;
+
+	if (atomic_read(&vctrl->suspend) > 0) {
+		*vtime = -1;
+		return;
+	}
+
+	spin_lock_irqsave(&vctrl->spin_lock, flags);
+	if (vctrl->wait_vsync_cnt == 0)
+		INIT_COMPLETION(vctrl->vsync_comp);
+	vctrl->wait_vsync_cnt++;
+	spin_unlock_irqrestore(&vctrl->spin_lock, flags);
+
+	wait_for_completion(&vctrl->vsync_comp);
+	mdp4_stat.wait4vsync0++;
+
+	*vtime = ktime_to_ns(vctrl->vsync_time);
+}
+
+static void mdp4_mddi_wait4dmap(int cndx)
+{
+	struct vsycn_ctrl *vctrl;
+
+	if (cndx >= MAX_CONTROLLER) {
+		pr_err("%s: out or range: cndx=%d\n", __func__, cndx);
+		return;
+	}
+
+	vctrl = &vsync_ctrl_db[cndx];
+
+	if (atomic_read(&vctrl->suspend) > 0)
+		return;
+
+	wait_for_completion(&vctrl->dmap_comp);
+}
+
+static void mdp4_mddi_wait4ov(int cndx)
+{
+	struct vsycn_ctrl *vctrl;
+
+	if (cndx >= MAX_CONTROLLER) {
+		pr_err("%s: out or range: cndx=%d\n", __func__, cndx);
+		return;
+	}
+
+	vctrl = &vsync_ctrl_db[cndx];
+
+	if (atomic_read(&vctrl->suspend) > 0)
+		return;
+
+	wait_for_completion(&vctrl->ov_comp);
+}
+
+/*
+ * primary_rdptr_isr:
+ * called from interrupt context
+ */
+static void primary_rdptr_isr(int cndx)
+{
+	struct vsycn_ctrl *vctrl;
+
+	vctrl = &vsync_ctrl_db[cndx];
+	pr_debug("%s: ISR, cpu=%d\n", __func__, smp_processor_id());
+	vctrl->rdptr_intr_tot++;
+	vctrl->vsync_time = ktime_get();
+
+	spin_lock(&vctrl->spin_lock);
+
+	if (vctrl->uevent)
+		schedule_work(&vctrl->vsync_work);
+
+	if (vctrl->wait_vsync_cnt) {
+		complete(&vctrl->vsync_comp);
+		vctrl->wait_vsync_cnt = 0;
+	}
+
+	if (vctrl->expire_tick) {
+		vctrl->expire_tick--;
+		if (vctrl->expire_tick == 0)
+			schedule_work(&vctrl->clk_work);
+	}
+	spin_unlock(&vctrl->spin_lock);
+}
+
+void mdp4_dmap_done_mddi(int cndx)
+{
+	struct vsycn_ctrl *vctrl;
+	struct mdp4_overlay_pipe *pipe;
+	int diff;
+
+	vctrl = &vsync_ctrl_db[cndx];
+	pipe = vctrl->base_pipe;
+
+	 /* blt enabled */
+	spin_lock(&vctrl->spin_lock);
+	vsync_irq_disable(INTR_DMA_P_DONE, MDP_DMAP_TERM);
+	vctrl->dmap_done++;
+	diff = vctrl->ov_done - vctrl->dmap_done;
+	pr_debug("%s: ov_koff=%d ov_done=%d dmap_koff=%d dmap_done=%d cpu=%d\n",
+		__func__, vctrl->ov_koff, vctrl->ov_done, vctrl->dmap_koff,
+		vctrl->dmap_done, smp_processor_id());
+	complete_all(&vctrl->dmap_comp);
+	if (diff <= 0) {
+		if (vctrl->blt_wait)
+			vctrl->blt_wait = 0;
+		spin_unlock(&vctrl->spin_lock);
+		return;
+	}
+
+	/* kick dmap */
+	mdp4_mddi_blt_dmap_update(pipe);
+	pipe->dmap_cnt++;
+	mdp4_stat.kickoff_dmap++;
+	vctrl->dmap_koff++;
+	vsync_irq_enable(INTR_DMA_P_DONE, MDP_DMAP_TERM);
+	outpdw(MDP_BASE + 0x000c, 0); /* kickoff dmap engine */
+	mb(); /* make sure kickoff executed */
+	spin_unlock(&vctrl->spin_lock);
+}
+
+/*
+ * mdp4_overlay0_done_mddi: called from isr
+ */
+void mdp4_overlay0_done_mddi(int cndx)
+{
+	struct vsycn_ctrl *vctrl;
+	struct mdp4_overlay_pipe *pipe;
+	int diff;
+
+	vctrl = &vsync_ctrl_db[cndx];
+	pipe = vctrl->base_pipe;
+
+	spin_lock(&vctrl->spin_lock);
+	vsync_irq_disable(INTR_OVERLAY0_DONE, MDP_OVERLAY0_TERM);
+	vctrl->ov_done++;
+	complete_all(&vctrl->ov_comp);
+	diff = vctrl->ov_done - vctrl->dmap_done;
+
+	pr_debug("%s: ov_koff=%d ov_done=%d dmap_koff=%d dmap_done=%d cpu=%d\n",
+		__func__, vctrl->ov_koff, vctrl->ov_done, vctrl->dmap_koff,
+		vctrl->dmap_done, smp_processor_id());
+
+	if (pipe->ov_blt_addr == 0) {
+		/* blt disabled */
+		spin_unlock(&vctrl->spin_lock);
+		return;
+	}
+
+	if (diff > 1) {
+		/*
+		 * two overlay_done and none dmap_done yet
+		 * let dmap_done kickoff dmap
+		 * and put pipe_commit to wait
+		 */
+		vctrl->blt_wait = 1;
+		pr_debug("%s: blt_wait set\n", __func__);
+		spin_unlock(&vctrl->spin_lock);
+		return;
+	}
+	mdp4_mddi_blt_dmap_update(pipe);
+	pipe->dmap_cnt++;
+	mdp4_stat.kickoff_dmap++;
+	vctrl->dmap_koff++;
+	vsync_irq_enable(INTR_DMA_P_DONE, MDP_DMAP_TERM);
+	outpdw(MDP_BASE + 0x000c, 0); /* kickoff dmap engine */
+	mb(); /* make sure kickoff executed */
+	spin_unlock(&vctrl->spin_lock);
+}
+
+static void clk_ctrl_work(struct work_struct *work)
+{
+	struct vsycn_ctrl *vctrl =
+		container_of(work, typeof(*vctrl), clk_work);
+	unsigned long flags;
+
+	mutex_lock(&vctrl->update_lock);
+	if (vctrl->clk_control && vctrl->clk_enabled) {
+		pr_debug("%s: SET_CLK_OFF\n", __func__);
+			mdp_clk_ctrl(0);
+		spin_lock_irqsave(&vctrl->spin_lock, flags);
+		vsync_irq_disable(INTR_PRIMARY_RDPTR, MDP_PRIM_RDPTR_TERM);
+			vctrl->clk_enabled = 0;
+		vctrl->clk_control = 0;
+		spin_unlock_irqrestore(&vctrl->spin_lock, flags);
+	}
+	mutex_unlock(&vctrl->update_lock);
+}
+
+static void send_vsync_work(struct work_struct *work)
+{
+	struct vsycn_ctrl *vctrl =
+		container_of(work, typeof(*vctrl), vsync_work);
+	char buf[64];
+	char *envp[2];
+
+	snprintf(buf, sizeof(buf), "VSYNC=%llu",
+			ktime_to_ns(vctrl->vsync_time));
+	envp[0] = buf;
+	envp[1] = NULL;
+	kobject_uevent_env(&vctrl->dev->kobj, KOBJ_CHANGE, envp);
+}
+
+
+void mdp4_mddi_rdptr_init(int cndx)
+{
+	struct vsycn_ctrl *vctrl;
+
+	if (cndx >= MAX_CONTROLLER) {
+		pr_err("%s: out or range: cndx=%d\n", __func__, cndx);
+		return;
+	}
+
+	vctrl = &vsync_ctrl_db[cndx];
+	if (vctrl->inited)
+		return;
+
+	vctrl->inited = 1;
+	vctrl->update_ndx = 0;
+	mutex_init(&vctrl->update_lock);
+	init_completion(&vctrl->ov_comp);
+	init_completion(&vctrl->dmap_comp);
+	init_completion(&vctrl->vsync_comp);
+	spin_lock_init(&vctrl->spin_lock);
+	INIT_WORK(&vctrl->vsync_work, send_vsync_work);
+	INIT_WORK(&vctrl->clk_work, clk_ctrl_work);
+}
+
+void mdp4_primary_rdptr(void)
+{
+	primary_rdptr_isr(0);
+}
+
+void mdp4_overlay_mddi_state_set(int state)
+{
+	unsigned long flag;
+
+	spin_lock_irqsave(&mdp_spin_lock, flag);
+	mddi_state = state;
+	spin_unlock_irqrestore(&mdp_spin_lock, flag);
+}
+
+int mdp4_overlay_mddi_state_get(void)
+{
+	return mddi_state;
+}
+
+static __u32 msm_fb_line_length(__u32 fb_index, __u32 xres, int bpp)
+{
+	/*
+	 * The adreno GPU hardware requires that the pitch be aligned to
+	 * 32 pixels for color buffers, so for the cases where the GPU
+	 * is writing directly to fb0, the framebuffer pitch
+	 * also needs to be 32 pixel aligned
+	 */
+
+	if (fb_index == 0)
+		return ALIGN(xres, 32) * bpp;
+	else
+		return xres * bpp;
 }
 
 void mdp4_mddi_vsync_enable(struct msm_fb_data_type *mfd,
@@ -61,13 +703,6 @@
 	if ((mfd->use_mdp_vsync) && (mfd->ibuf.vsync_enable) &&
 		(mfd->panel_info.lcd.vsync_enable)) {
 
-		if (mdp_hw_revision < MDP4_REVISION_V2_1) {
-			/* need dmas dmap switch */
-			if (which == 0 && dmap_vsync_enable == 0 &&
-				mfd->panel_info.lcd.rev < 2) /* dma_p */
-				return;
-		}
-
 		if (vsync_start_y_adjust <= pipe->dst_y)
 			start_y = pipe->dst_y - vsync_start_y_adjust;
 		else
@@ -88,633 +723,337 @@
 	}
 }
 
-#define WHOLESCREEN
+void mdp4_mddi_base_swap(int cndx, struct mdp4_overlay_pipe *pipe)
+{
+	struct vsycn_ctrl *vctrl;
 
-void mdp4_overlay_update_lcd(struct msm_fb_data_type *mfd)
+	if (cndx >= MAX_CONTROLLER) {
+		pr_err("%s: out or range: cndx=%d\n", __func__, cndx);
+		return;
+	}
+
+	vctrl = &vsync_ctrl_db[cndx];
+	vctrl->base_pipe = pipe;
+}
+
+static void mdp4_overlay_setup_pipe_addr(struct msm_fb_data_type *mfd,
+			struct mdp4_overlay_pipe *pipe)
 {
 	MDPIBUF *iBuf = &mfd->ibuf;
+	struct fb_info *fbi;
+	int bpp;
 	uint8 *src;
+
+	/* whole screen for base layer */
+	src = (uint8 *) iBuf->buf;
+	fbi = mfd->fbi;
+
+	if (pipe->is_3d) {
+		bpp = fbi->var.bits_per_pixel / 8;
+		pipe->src_height = pipe->src_height_3d;
+		pipe->src_width = pipe->src_width_3d;
+		pipe->src_h = pipe->src_height_3d;
+		pipe->src_w = pipe->src_width_3d;
+		pipe->dst_h = pipe->src_height_3d;
+		pipe->dst_w = pipe->src_width_3d;
+		pipe->srcp0_ystride = msm_fb_line_length(0,
+						pipe->src_width, bpp);
+	} else {
+		 /* 2D */
+		pipe->src_height = fbi->var.yres;
+		pipe->src_width = fbi->var.xres;
+		pipe->src_h = fbi->var.yres;
+		pipe->src_w = fbi->var.xres;
+		pipe->dst_h = fbi->var.yres;
+		pipe->dst_w = fbi->var.xres;
+		pipe->srcp0_ystride = fbi->fix.line_length;
+	}
+	pipe->src_y = 0;
+	pipe->src_x = 0;
+	pipe->dst_y = 0;
+	pipe->dst_x = 0;
+	pipe->srcp0_addr = (uint32)src;
+}
+
+void mdp4_overlay_update_mddi(struct msm_fb_data_type *mfd)
+{
 	int ptype;
 	uint32 mddi_ld_param;
 	uint16 mddi_vdo_packet_reg;
 	struct mdp4_overlay_pipe *pipe;
+	uint32	data;
 	int ret;
+	int cndx = 0;
+	struct vsycn_ctrl *vctrl;
 
 	if (mfd->key != MFD_KEY)
 		return;
 
-	mddi_mfd = mfd;		/* keep it */
+	vctrl = &vsync_ctrl_db[cndx];
 
-	/* MDP cmd block enable */
-	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
-
-	if (mddi_pipe == NULL) {
+	if (vctrl->base_pipe == NULL) {
 		ptype = mdp4_overlay_format2type(mfd->fb_imgType);
+
 		if (ptype < 0)
-			printk(KERN_INFO "%s: format2type failed\n", __func__);
+			pr_err("%s: format2type failed\n", __func__);
+
 		pipe = mdp4_overlay_pipe_alloc(ptype, MDP4_MIXER0);
-		if (pipe == NULL)
-			printk(KERN_INFO "%s: pipe_alloc failed\n", __func__);
+		if (pipe == NULL) {
+			pr_err("%s: pipe_alloc failed\n", __func__);
+			return;
+		}
 		pipe->pipe_used++;
+		pipe->mixer_stage  = MDP4_MIXER_STAGE_BASE;
 		pipe->mixer_num  = MDP4_MIXER0;
 		pipe->src_format = mfd->fb_imgType;
 		mdp4_overlay_panel_mode(pipe->mixer_num, MDP4_PANEL_MDDI);
 		ret = mdp4_overlay_format2pipe(pipe);
 		if (ret < 0)
-			printk(KERN_INFO "%s: format2type failed\n", __func__);
+			pr_err("%s: format2type failed\n", __func__);
 
-		mddi_pipe = pipe; /* keep it */
-		mddi_ld_param = 0;
-		mddi_vdo_packet_reg = mfd->panel_info.mddi.vdopkt;
-
-		if (mdp_hw_revision == MDP4_REVISION_V2_1) {
-			uint32	data;
-
-			data = inpdw(MDP_BASE + 0x0028);
-			data &= ~0x0300;	/* bit 8, 9, MASTER4 */
-			if (mfd->fbi->var.xres == 540) /* qHD, 540x960 */
-				data |= 0x0200;
-			else
-				data |= 0x0100;
-
-			MDP_OUTP(MDP_BASE + 0x00028, data);
-		}
-
-		if (mfd->panel_info.type == MDDI_PANEL) {
-			if (mfd->panel_info.pdest == DISPLAY_1)
-				mddi_ld_param = 0;
-			else
-				mddi_ld_param = 1;
-		} else {
-			mddi_ld_param = 2;
-		}
-
-		MDP_OUTP(MDP_BASE + 0x00090, mddi_ld_param);
-
-		if (mfd->panel_info.bpp == 24)
-			MDP_OUTP(MDP_BASE + 0x00094,
-			 (MDDI_VDO_PACKET_DESC_24 << 16) | mddi_vdo_packet_reg);
-		else if (mfd->panel_info.bpp == 16)
-			MDP_OUTP(MDP_BASE + 0x00094,
-			 (MDDI_VDO_PACKET_DESC_16 << 16) | mddi_vdo_packet_reg);
-		else
-			MDP_OUTP(MDP_BASE + 0x00094,
-			 (MDDI_VDO_PACKET_DESC << 16) | mddi_vdo_packet_reg);
-
-		MDP_OUTP(MDP_BASE + 0x00098, 0x01);
+		vctrl->base_pipe = pipe; /* keep it */
 		mdp4_init_writeback_buf(mfd, MDP4_MIXER0);
 		pipe->ov_blt_addr = 0;
 		pipe->dma_blt_addr = 0;
 	} else {
-		pipe = mddi_pipe;
+		pipe = vctrl->base_pipe;
 	}
 
-	/* 0 for dma_p, client_id = 0 */
-	MDP_OUTP(MDP_BASE + 0x00090, 0);
+	MDP_OUTP(MDP_BASE + 0x021c, 10); /* read pointer */
 
+	mddi_ld_param = 0;
+	mddi_vdo_packet_reg = mfd->panel_info.mddi.vdopkt;
 
-	src = (uint8 *) iBuf->buf;
+	if (mdp_hw_revision == MDP4_REVISION_V2_1) {
+		data = inpdw(MDP_BASE + 0x0028);
+		data &= ~0x0300;	/* bit 8, 9, MASTER4 */
+		if (mfd->fbi->var.xres == 540) /* qHD, 540x960 */
+			data |= 0x0200;
+		else
+			data |= 0x0100;
 
-#ifdef WHOLESCREEN
-
-	{
-		struct fb_info *fbi;
-
-		fbi = mfd->fbi;
-		pipe->src_height = fbi->var.yres;
-		pipe->src_width = fbi->var.xres;
-		pipe->src_h = fbi->var.yres;
-		pipe->src_w = fbi->var.xres;
-		pipe->src_y = 0;
-		pipe->src_x = 0;
-		pipe->dst_h = fbi->var.yres;
-		pipe->dst_w = fbi->var.xres;
-		pipe->dst_y = 0;
-		pipe->dst_x = 0;
-		pipe->srcp0_addr = (uint32)src;
-		pipe->srcp0_ystride = fbi->fix.line_length;
+			MDP_OUTP(MDP_BASE + 0x00028, data);
 	}
 
-#else
-	if (mdp4_overlay_active(MDP4_MIXER0)) {
-		struct fb_info *fbi;
-
-		fbi = mfd->fbi;
-		pipe->src_height = fbi->var.yres;
-		pipe->src_width = fbi->var.xres;
-		pipe->src_h = fbi->var.yres;
-		pipe->src_w = fbi->var.xres;
-		pipe->src_y = 0;
-		pipe->src_x = 0;
-		pipe->dst_h = fbi->var.yres;
-		pipe->dst_w = fbi->var.xres;
-		pipe->dst_y = 0;
-		pipe->dst_x = 0;
-		pipe->srcp0_addr = (uint32) src;
-		pipe->srcp0_ystride = fbi->fix.line_length;
+	if (mfd->panel_info.type == MDDI_PANEL) {
+		if (mfd->panel_info.pdest == DISPLAY_1)
+			mddi_ld_param = 0;
+		else
+			mddi_ld_param = 1;
 	} else {
-		/* starting input address */
-		src += (iBuf->dma_x + iBuf->dma_y * iBuf->ibuf_width)
-					* iBuf->bpp;
-
-		pipe->src_height = iBuf->dma_h;
-		pipe->src_width = iBuf->dma_w;
-		pipe->src_h = iBuf->dma_h;
-		pipe->src_w = iBuf->dma_w;
-		pipe->src_y = 0;
-		pipe->src_x = 0;
-		pipe->dst_h = iBuf->dma_h;
-		pipe->dst_w = iBuf->dma_w;
-		pipe->dst_y = iBuf->dma_y;
-		pipe->dst_x = iBuf->dma_x;
-		pipe->srcp0_addr = (uint32) src;
-		pipe->srcp0_ystride = iBuf->ibuf_width * iBuf->bpp;
+		mddi_ld_param = 2;
 	}
-#endif
 
-	pipe->mixer_stage  = MDP4_MIXER_STAGE_BASE;
+	MDP_OUTP(MDP_BASE + 0x00090, mddi_ld_param);
+
+	if (mfd->panel_info.bpp == 24)
+		MDP_OUTP(MDP_BASE + 0x00094,
+		 (MDDI_VDO_PACKET_DESC_24 << 16) | mddi_vdo_packet_reg);
+	else if (mfd->panel_info.bpp == 16)
+		MDP_OUTP(MDP_BASE + 0x00094,
+		 (MDDI_VDO_PACKET_DESC_16 << 16) | mddi_vdo_packet_reg);
+	else
+		MDP_OUTP(MDP_BASE + 0x00094,
+		 (MDDI_VDO_PACKET_DESC << 16) | mddi_vdo_packet_reg);
+
+		MDP_OUTP(MDP_BASE + 0x00098, 0x01);
+
+
+	mdp4_overlay_setup_pipe_addr(mfd, pipe);
 
 	mdp4_overlay_rgb_setup(pipe);
 
-	mdp4_mixer_stage_up(pipe, 1);
+	mdp4_overlay_reg_flush(pipe, 1);
+
+	mdp4_mixer_stage_up(pipe, 0);
 
 	mdp4_overlayproc_cfg(pipe);
 
 	mdp4_overlay_dmap_xy(pipe);
 
 	mdp4_overlay_dmap_cfg(mfd, 0);
+
 	mdp4_mixer_stage_commit(pipe->mixer_num);
-	mdp4_mddi_vsync_enable(mfd, pipe, 0);
 
-	/* MDP cmd block disable */
-	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+	wmb();
 }
 
-int mdp4_mddi_overlay_blt_start(struct msm_fb_data_type *mfd)
+void mdp4_mddi_blt_start(struct msm_fb_data_type *mfd)
 {
-	unsigned long flag;
-
-	pr_debug("%s: blt_end=%d blt_addr=%x pid=%d\n",
-		__func__, mddi_pipe->blt_end,
-		(int)mddi_pipe->ov_blt_addr, current->pid);
-
-	mdp4_allocate_writeback_buf(mfd, MDP4_MIXER0);
-
-	if (mfd->ov0_wb_buf->write_addr == 0) {
-		pr_info("%s: no blt_base assigned\n", __func__);
-		return -EBUSY;
-	}
-
-	if (mddi_pipe->ov_blt_addr == 0) {
-		mdp4_mddi_dma_busy_wait(mfd);
-		spin_lock_irqsave(&mdp_spin_lock, flag);
-		mddi_pipe->blt_end = 0;
-		mddi_pipe->blt_cnt = 0;
-		mddi_pipe->ov_cnt = 0;
-		mddi_pipe->dmap_cnt = 0;
-		mddi_pipe->ov_blt_addr = mfd->ov0_wb_buf->write_addr;
-		mddi_pipe->dma_blt_addr = mfd->ov0_wb_buf->write_addr;
-		mdp4_stat.blt_mddi++;
-		spin_unlock_irqrestore(&mdp_spin_lock, flag);
-	return 0;
+	mdp4_mddi_do_blt(mfd, 1);
 }
 
-	return -EBUSY;
-}
-
-int mdp4_mddi_overlay_blt_stop(struct msm_fb_data_type *mfd)
+void mdp4_mddi_blt_stop(struct msm_fb_data_type *mfd)
 {
-	unsigned long flag;
-
-	pr_debug("%s: blt_end=%d blt_addr=%x\n",
-		 __func__, mddi_pipe->blt_end, (int)mddi_pipe->ov_blt_addr);
-
-	if ((mddi_pipe->blt_end == 0) && mddi_pipe->ov_blt_addr) {
-		spin_lock_irqsave(&mdp_spin_lock, flag);
-		mddi_pipe->blt_end = 1;	/* mark as end */
-		spin_unlock_irqrestore(&mdp_spin_lock, flag);
-		return 0;
-	}
-
-	return -EBUSY;
-}
-
-int mdp4_mddi_overlay_blt_offset(struct msm_fb_data_type *mfd,
-					struct msmfb_overlay_blt *req)
-{
-	req->offset = 0;
-	req->width = mddi_pipe->src_width;
-	req->height = mddi_pipe->src_height;
-	req->bpp = mddi_pipe->bpp;
-
-	return sizeof(*req);
+	mdp4_mddi_do_blt(mfd, 0);
 }
 
 void mdp4_mddi_overlay_blt(struct msm_fb_data_type *mfd,
 					struct msmfb_overlay_blt *req)
 {
-	if (req->enable)
-		mdp4_mddi_overlay_blt_start(mfd);
-	else if (req->enable == 0)
-		mdp4_mddi_overlay_blt_stop(mfd);
-
+	mdp4_mddi_do_blt(mfd, req->enable);
 }
 
-void mdp4_blt_xy_update(struct mdp4_overlay_pipe *pipe)
+int mdp4_mddi_on(struct platform_device *pdev)
 {
-	uint32 off, addr, addr2;
-	int bpp;
-	char *overlay_base;
+	int ret = 0;
+	int cndx = 0;
+	struct msm_fb_data_type *mfd;
+	struct vsycn_ctrl *vctrl;
 
-	if (pipe->ov_blt_addr == 0)
-		return;
+	pr_debug("%s+:\n", __func__);
 
+	mfd = (struct msm_fb_data_type *)platform_get_drvdata(pdev);
 
-#ifdef BLT_RGB565
-	bpp = 2; /* overlay ouput is RGB565 */
-#else
-	bpp = 3; /* overlay ouput is RGB888 */
-#endif
-	off = 0;
-	if (pipe->dmap_cnt & 0x01)
-		off = pipe->src_height * pipe->src_width * bpp;
+	vctrl = &vsync_ctrl_db[cndx];
+	vctrl->mfd = mfd;
+	vctrl->dev = mfd->fbi->dev;
 
-	addr = pipe->ov_blt_addr + off;
+	mdp_clk_ctrl(1);
+	mdp4_overlay_update_mddi(mfd);
+	mdp_clk_ctrl(0);
 
-	/* dmap */
-	MDP_OUTP(MDP_BASE + 0x90008, addr);
+	mdp4_iommu_attach();
 
-	off = 0;
-	if (pipe->ov_cnt & 0x01)
-		off = pipe->src_height * pipe->src_width * bpp;
-	addr2 = pipe->ov_blt_addr + off;
-	/* overlay 0 */
-	overlay_base = MDP_BASE + MDP4_OVERLAYPROC0_BASE;/* 0x10000 */
-	outpdw(overlay_base + 0x000c, addr2);
-	outpdw(overlay_base + 0x001c, addr2);
+	atomic_set(&vctrl->suspend, 0);
+	pr_debug("%s-:\n", __func__);
+
+	return ret;
 }
 
-void mdp4_primary_rdptr(void)
+int mdp4_mddi_off(struct platform_device *pdev)
 {
-}
+	int ret = 0;
+	int cndx = 0;
+	struct msm_fb_data_type *mfd;
+	struct vsycn_ctrl *vctrl;
+	struct mdp4_overlay_pipe *pipe;
 
-/*
- * mdp4_dmap_done_mddi: called from isr
- */
-void mdp4_dma_p_done_mddi(struct mdp_dma_data *dma)
-{
-	int diff;
+	pr_debug("%s+:\n", __func__);
 
-	mddi_pipe->dmap_cnt++;
-	diff = mddi_pipe->ov_cnt - mddi_pipe->dmap_cnt;
-	pr_debug("%s: ov_cnt=%d dmap_cnt=%d\n",
-			__func__, mddi_pipe->ov_cnt, mddi_pipe->dmap_cnt);
+	mfd = (struct msm_fb_data_type *)platform_get_drvdata(pdev);
 
-	if (diff <= 0) {
-		spin_lock(&mdp_spin_lock);
-		dma->dmap_busy = FALSE;
-		complete(&dma->dmap_comp);
-		spin_unlock(&mdp_spin_lock);
-
-		if (mddi_pipe->blt_end) {
-			mddi_pipe->blt_end = 0;
-			mddi_pipe->ov_blt_addr = 0;
-			mddi_pipe->dma_blt_addr = 0;
-			pr_debug("%s: END, ov_cnt=%d dmap_cnt=%d\n", __func__,
-				mddi_pipe->ov_cnt, mddi_pipe->dmap_cnt);
-			mdp_intr_mask &= ~INTR_DMA_P_DONE;
-			outp32(MDP_INTR_ENABLE, mdp_intr_mask);
-		}
-
-		mdp_pipe_ctrl(MDP_OVERLAY0_BLOCK, MDP_BLOCK_POWER_OFF, TRUE);
-		mdp_disable_irq_nosync(MDP_DMA2_TERM);  /* disable intr */
-		return;
+	vctrl = &vsync_ctrl_db[cndx];
+	pipe = vctrl->base_pipe;
+	if (pipe == NULL) {
+		pr_err("%s: NO base pipe\n", __func__);
+		return ret;
 	}
 
-	spin_lock(&mdp_spin_lock);
-	dma->busy = FALSE;
-	spin_unlock(&mdp_spin_lock);
-	complete(&dma->comp);
-	if (busy_wait_cnt)
-		busy_wait_cnt--;
+	atomic_set(&vctrl->suspend, 1);
 
-	pr_debug("%s: kickoff dmap\n", __func__);
+	/* sanity check, free pipes besides base layer */
+	mdp4_overlay_unset_mixer(pipe->mixer_num);
+	mdp4_mixer_stage_down(pipe, 1);
+	mdp4_overlay_pipe_free(pipe);
+	vctrl->base_pipe = NULL;
 
-	mdp4_blt_xy_update(mddi_pipe);
-	/* kick off dmap */
-	outpdw(MDP_BASE + 0x000c, 0x0);
-	mdp4_stat.kickoff_dmap++;
-	mdp_pipe_ctrl(MDP_OVERLAY0_BLOCK, MDP_BLOCK_POWER_OFF, TRUE);
-}
-
-/*
- * mdp4_overlay0_done_mddi: called from isr
- */
-void mdp4_overlay0_done_mddi(struct mdp_dma_data *dma)
-{
-	int diff;
-
-	if (mddi_pipe->ov_blt_addr == 0) {
-		mdp_pipe_ctrl(MDP_OVERLAY0_BLOCK, MDP_BLOCK_POWER_OFF, TRUE);
-		spin_lock(&mdp_spin_lock);
-		dma->busy = FALSE;
-		spin_unlock(&mdp_spin_lock);
-		complete(&dma->comp);
-
-		if (busy_wait_cnt)
-			busy_wait_cnt--;
-		mdp_disable_irq_nosync(MDP_OVERLAY0_TERM);
-
-		return;
+	if (vctrl->clk_enabled) {
+		/*
+		 * in case of suspend, vsycn_ctrl off is not
+		 * received from frame work which left clock on
+		 * then, clock need to be turned off here
+		 */
+		mdp_clk_ctrl(0);
 	}
 
-	/* blt enabled */
-	if (mddi_pipe->blt_end == 0)
-		mddi_pipe->ov_cnt++;
+	vctrl->clk_enabled = 0;
+	vctrl->vsync_enabled = 0;
+	vctrl->clk_control = 0;
+	vctrl->expire_tick = 0;
+	vctrl->uevent = 0;
 
-	pr_debug("%s: ov_cnt=%d dmap_cnt=%d\n",
-			__func__, mddi_pipe->ov_cnt, mddi_pipe->dmap_cnt);
+	vsync_irq_disable(INTR_PRIMARY_RDPTR, MDP_PRIM_RDPTR_TERM);
 
-	if (mddi_pipe->blt_cnt == 0) {
-		/* first kickoff since blt enabled */
-		mdp_intr_mask |= INTR_DMA_P_DONE;
-		outp32(MDP_INTR_ENABLE, mdp_intr_mask);
-	}
+	pr_debug("%s-:\n", __func__);
 
-	mddi_pipe->blt_cnt++;
-
-	diff = mddi_pipe->ov_cnt - mddi_pipe->dmap_cnt;
-	if (diff >= 2) {
-		mdp_disable_irq_nosync(MDP_OVERLAY0_TERM);
-		return;
-	}
-
-	spin_lock(&mdp_spin_lock);
-	dma->busy = FALSE;
-	dma->dmap_busy = TRUE;
-	spin_unlock(&mdp_spin_lock);
-	complete(&dma->comp);
-
-	if (busy_wait_cnt)
-		busy_wait_cnt--;
-
-	pr_debug("%s: kickoff dmap\n", __func__);
-
-	mdp4_blt_xy_update(mddi_pipe);
-	mdp_enable_irq(MDP_DMA2_TERM);	/* enable intr */
-	/* kick off dmap */
-	outpdw(MDP_BASE + 0x000c, 0x0);
-	mdp4_stat.kickoff_dmap++;
-	mdp_disable_irq_nosync(MDP_OVERLAY0_TERM);
-}
-
-void mdp4_mddi_overlay_restore(void)
-{
-	if (mddi_mfd == NULL)
-		return;
-
-	pr_debug("%s: resotre, pid=%d\n", __func__, current->pid);
-
-	if (mddi_mfd->panel_power_on == 0)
-		return;
-	if (mddi_mfd && mddi_pipe) {
-		mdp4_mddi_dma_busy_wait(mddi_mfd);
-		mdp4_overlay_update_lcd(mddi_mfd);
-
-		if (mddi_pipe->ov_blt_addr)
-			mdp4_mddi_blt_dmap_busy_wait(mddi_mfd);
-		mdp4_mddi_overlay_kickoff(mddi_mfd, mddi_pipe);
-		mddi_mfd->dma_update_flag = 1;
-	}
-	if (mdp_hw_revision < MDP4_REVISION_V2_1) /* need dmas dmap switch */
-		mdp4_mddi_overlay_dmas_restore();
-}
-
-void mdp4_mddi_blt_dmap_busy_wait(struct msm_fb_data_type *mfd)
-{
-	unsigned long flag;
-	int need_wait = 0;
-
-	spin_lock_irqsave(&mdp_spin_lock, flag);
-	if (mfd->dma->dmap_busy == TRUE) {
-		INIT_COMPLETION(mfd->dma->dmap_comp);
-		need_wait++;
-	}
-	spin_unlock_irqrestore(&mdp_spin_lock, flag);
-
-	if (need_wait) {
-		/* wait until DMA finishes the current job */
-		wait_for_completion(&mfd->dma->dmap_comp);
-	}
-}
-
-/*
- * mdp4_mddi_cmd_dma_busy_wait: check mddi link activity
- * mddi link is a shared resource and it can only be used
- * while it is in idle state.
- * ov_mutex need to be acquired before call this function.
- */
-void mdp4_mddi_dma_busy_wait(struct msm_fb_data_type *mfd)
-{
-	unsigned long flag;
-	int need_wait = 0;
-
-	pr_debug("%s: START, pid=%d\n", __func__, current->pid);
-	spin_lock_irqsave(&mdp_spin_lock, flag);
-	if (mfd->dma->busy == TRUE) {
-		if (busy_wait_cnt == 0)
-			INIT_COMPLETION(mfd->dma->comp);
-		busy_wait_cnt++;
-		need_wait++;
-	}
-	spin_unlock_irqrestore(&mdp_spin_lock, flag);
-
-
-	if (need_wait) {
-		/* wait until DMA finishes the current job */
-		pr_debug("%s: PENDING, pid=%d\n", __func__, current->pid);
-		wait_for_completion(&mfd->dma->comp);
-	}
-	pr_debug("%s: DONE, pid=%d\n", __func__, current->pid);
-}
-
-void mdp4_mddi_kickoff_video(struct msm_fb_data_type *mfd,
-				struct mdp4_overlay_pipe *pipe)
-{
 	/*
-	 * a video kickoff may happen before UI kickoff after
-	 * blt enabled. mdp4_overlay_update_lcd() need
-	 * to be called before kickoff.
-	 * vice versa for blt disabled.
+	 * footswitch off
+	 * this will casue all mdp register
+	 * to be reset to default
+	 * after footswitch on later
 	 */
-	if (mddi_pipe->ov_blt_addr && mddi_pipe->blt_cnt == 0)
-		mdp4_overlay_update_lcd(mfd); /* first time */
-	else if (mddi_pipe->ov_blt_addr == 0  && mddi_pipe->blt_cnt) {
-		mdp4_overlay_update_lcd(mfd); /* last time */
-		mddi_pipe->blt_cnt = 0;
-	}
 
-	pr_debug("%s: blt_addr=%d blt_cnt=%d\n",
-		__func__, (int)mddi_pipe->ov_blt_addr, mddi_pipe->blt_cnt);
-
-	if (mddi_pipe->ov_blt_addr)
-		mdp4_mddi_blt_dmap_busy_wait(mddi_mfd);
-	mdp4_mddi_overlay_kickoff(mfd, pipe);
+	return ret;
 }
 
-void mdp4_mddi_kickoff_ui(struct msm_fb_data_type *mfd,
-				struct mdp4_overlay_pipe *pipe)
+void mdp_mddi_overlay_suspend(struct msm_fb_data_type *mfd)
 {
-	pr_debug("%s: pid=%d\n", __func__, current->pid);
-	mdp4_mddi_overlay_kickoff(mfd, pipe);
-}
+	int cndx = 0;
+	struct vsycn_ctrl *vctrl;
+	struct mdp4_overlay_pipe *pipe;
 
+	vctrl = &vsync_ctrl_db[cndx];
+	pipe = vctrl->base_pipe;
+	/* dis-engage rgb0 from mixer0 */
+	if (pipe) {
+		if (mfd->ref_cnt == 0) {
+			/* adb stop */
+			if (pipe->pipe_type == OVERLAY_TYPE_BF)
+				mdp4_overlay_borderfill_stage_down(pipe);
 
-void mdp4_mddi_overlay_kickoff(struct msm_fb_data_type *mfd,
-				struct mdp4_overlay_pipe *pipe)
-{
-	unsigned long flag;
-
-	mdp_enable_irq(MDP_OVERLAY0_TERM);
-	spin_lock_irqsave(&mdp_spin_lock, flag);
-	mfd->dma->busy = TRUE;
-	if (mddi_pipe->ov_blt_addr)
-		mfd->dma->dmap_busy = TRUE;
-	spin_unlock_irqrestore(&mdp_spin_lock, flag);
-	/* start OVERLAY pipe */
-	mdp_pipe_kickoff(MDP_OVERLAY0_TERM, mfd);
-	mdp4_stat.kickoff_ov0++;
-}
-
-void mdp4_dma_s_update_lcd(struct msm_fb_data_type *mfd,
-				struct mdp4_overlay_pipe *pipe)
-{
-	MDPIBUF *iBuf = &mfd->ibuf;
-	uint32 outBpp = iBuf->bpp;
-	uint16 mddi_vdo_packet_reg;
-	uint32 dma_s_cfg_reg;
-
-	dma_s_cfg_reg = 0;
-
-	if (mfd->fb_imgType == MDP_RGBA_8888)
-		dma_s_cfg_reg |= DMA_PACK_PATTERN_BGR; /* on purpose */
-	else if (mfd->fb_imgType == MDP_BGR_565)
-		dma_s_cfg_reg |= DMA_PACK_PATTERN_BGR;
-	else
-		dma_s_cfg_reg |= DMA_PACK_PATTERN_RGB;
-
-	if (outBpp == 4)
-		dma_s_cfg_reg |= (1 << 26); /* xRGB8888 */
-	else if (outBpp == 2)
-		dma_s_cfg_reg |= DMA_IBUF_FORMAT_RGB565;
-
-	dma_s_cfg_reg |= DMA_DITHER_EN;
-
-	/* MDP cmd block enable */
-	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
-	/* PIXELSIZE */
-	MDP_OUTP(MDP_BASE + 0xa0004, (pipe->dst_h << 16 | pipe->dst_w));
-	MDP_OUTP(MDP_BASE + 0xa0008, pipe->srcp0_addr);	/* ibuf address */
-	MDP_OUTP(MDP_BASE + 0xa000c, pipe->srcp0_ystride);/* ystride */
-
-	if (mfd->panel_info.bpp == 24) {
-		dma_s_cfg_reg |= DMA_DSTC0G_8BITS |	/* 666 18BPP */
-		    DMA_DSTC1B_8BITS | DMA_DSTC2R_8BITS;
-	} else if (mfd->panel_info.bpp == 18) {
-		dma_s_cfg_reg |= DMA_DSTC0G_6BITS |	/* 666 18BPP */
-		    DMA_DSTC1B_6BITS | DMA_DSTC2R_6BITS;
-	} else {
-		dma_s_cfg_reg |= DMA_DSTC0G_6BITS |	/* 565 16BPP */
-		    DMA_DSTC1B_5BITS | DMA_DSTC2R_5BITS;
-	}
-
-	MDP_OUTP(MDP_BASE + 0xa0010, (pipe->dst_y << 16) | pipe->dst_x);
-
-	/* 1 for dma_s, client_id = 0 */
-	MDP_OUTP(MDP_BASE + 0x00090, 1);
-
-	mddi_vdo_packet_reg = mfd->panel_info.mddi.vdopkt;
-
-	if (mfd->panel_info.bpp == 24)
-		MDP_OUTP(MDP_BASE + 0x00094,
-			(MDDI_VDO_PACKET_DESC_24 << 16) | mddi_vdo_packet_reg);
-	else if (mfd->panel_info.bpp == 16)
-		MDP_OUTP(MDP_BASE + 0x00094,
-			 (MDDI_VDO_PACKET_DESC_16 << 16) | mddi_vdo_packet_reg);
-	else
-		MDP_OUTP(MDP_BASE + 0x00094,
-			 (MDDI_VDO_PACKET_DESC << 16) | mddi_vdo_packet_reg);
-
-	MDP_OUTP(MDP_BASE + 0x00098, 0x01);
-
-	MDP_OUTP(MDP_BASE + 0xa0000, dma_s_cfg_reg);
-
-	mdp4_mddi_vsync_enable(mfd, pipe, 1);
-
-	/* MDP cmd block disable */
-	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
-}
-
-void mdp4_mddi_dma_s_kickoff(struct msm_fb_data_type *mfd,
-				struct mdp4_overlay_pipe *pipe)
-{
-	mdp_enable_irq(MDP_DMA_S_TERM);
-
-	if (mddi_pipe->ov_blt_addr == 0)
-		mfd->dma->busy = TRUE;
-
-	mfd->ibuf_flushed = TRUE;
-	/* start dma_s pipe */
-	mdp_pipe_kickoff(MDP_DMA_S_TERM, mfd);
-	mdp4_stat.kickoff_dmas++;
-
-	/* wait until DMA finishes the current job */
-	wait_for_completion(&mfd->dma->comp);
-	mdp_disable_irq(MDP_DMA_S_TERM);
-}
-
-void mdp4_mddi_overlay_dmas_restore(void)
-{
-	/* mutex held by caller */
-	if (mddi_mfd && mddi_pipe) {
-		mdp4_mddi_dma_busy_wait(mddi_mfd);
-		mdp4_dma_s_update_lcd(mddi_mfd, mddi_pipe);
-		mdp4_mddi_dma_s_kickoff(mddi_mfd, mddi_pipe);
-		mddi_mfd->dma_update_flag = 1;
+			/* pipe == rgb1 */
+			mdp4_overlay_unset_mixer(pipe->mixer_num);
+			vctrl->base_pipe = NULL;
+		} else {
+			mdp4_mixer_stage_down(pipe, 1);
+			mdp4_overlay_iommu_pipe_free(pipe->pipe_ndx, 1);
+		}
 	}
 }
 
 void mdp4_mddi_overlay(struct msm_fb_data_type *mfd)
 {
-	mutex_lock(&mfd->dma->ov_mutex);
+	int cndx = 0;
+	struct vsycn_ctrl *vctrl;
+	struct mdp4_overlay_pipe *pipe;
+	unsigned long flags;
+	long long xx;
 
-	if (mfd && mfd->panel_power_on) {
-		mdp4_mddi_dma_busy_wait(mfd);
+	vctrl = &vsync_ctrl_db[cndx];
 
-		if (mddi_pipe && mddi_pipe->ov_blt_addr)
-			mdp4_mddi_blt_dmap_busy_wait(mfd);
-		mdp4_overlay_mdp_perf_upd(mfd, 0);
-		mdp4_overlay_update_lcd(mfd);
+	if (!mfd->panel_power_on)
+		return;
 
-		mdp4_overlay_mdp_perf_upd(mfd, 1);
-		if (mdp_hw_revision < MDP4_REVISION_V2_1) {
-			/* dmas dmap switch */
-			if (mdp4_overlay_mixer_play(mddi_pipe->mixer_num)
-						== 0) {
-				mdp4_dma_s_update_lcd(mfd, mddi_pipe);
-				mdp4_mddi_dma_s_kickoff(mfd, mddi_pipe);
-			} else
-				mdp4_mddi_kickoff_ui(mfd, mddi_pipe);
-		} else	/* no dams dmap switch  */
-			mdp4_mddi_kickoff_ui(mfd, mddi_pipe);
-
-	/* signal if pan function is waiting for the update completion */
-		if (mfd->pan_waiting) {
-			mfd->pan_waiting = FALSE;
-			complete(&mfd->pan_comp);
-		}
+	pipe = vctrl->base_pipe;
+	if (pipe == NULL) {
+		pr_err("%s: NO base pipe\n", __func__);
+		return;
 	}
+
+	mutex_lock(&vctrl->update_lock);
+	if (!vctrl->clk_enabled) {
+		pr_err("%s: mdp clocks disabled\n", __func__);
+		mutex_unlock(&vctrl->update_lock);
+		return;
+
+	}
+	mutex_unlock(&vctrl->update_lock);
+
+	spin_lock_irqsave(&vctrl->spin_lock, flags);
+	if (vctrl->expire_tick) {
+		/*
+		 * in the middle of shutting clocks down
+		 * delay to allow pan display to go through
+		 */
+		vctrl->expire_tick = VSYNC_EXPIRE_TICK;
+	}
+	spin_unlock_irqrestore(&vctrl->spin_lock, flags);
+
+	if (pipe->mixer_stage == MDP4_MIXER_STAGE_BASE) {
+		mdp4_mddi_vsync_enable(mfd, pipe, 0);
+		mdp4_overlay_setup_pipe_addr(mfd, pipe);
+		mdp4_mddi_pipe_queue(0, pipe);
+	}
+
+	mdp4_overlay_mdp_perf_upd(mfd, 1);
+
+	mutex_lock(&mfd->dma->ov_mutex);
+	mdp4_mddi_pipe_commit();
 	mutex_unlock(&mfd->dma->ov_mutex);
+	mdp4_mddi_wait4vsync(0, &xx);
+
+	mdp4_overlay_mdp_perf_upd(mfd, 0);
 }
 
 int mdp4_mddi_overlay_cursor(struct fb_info *info, struct fb_cursor *cursor)
@@ -722,7 +1061,6 @@
 	struct msm_fb_data_type *mfd = info->par;
 	mutex_lock(&mfd->dma->ov_mutex);
 	if (mfd && mfd->panel_power_on) {
-		mdp4_mddi_dma_busy_wait(mfd);
 		mdp_hw_cursor_update(info, cursor);
 	}
 	mutex_unlock(&mfd->dma->ov_mutex);
diff --git a/drivers/video/msm/mdp4_overlay_writeback.c b/drivers/video/msm/mdp4_overlay_writeback.c
index ee7e9ce..18c6635 100644
--- a/drivers/video/msm/mdp4_overlay_writeback.c
+++ b/drivers/video/msm/mdp4_overlay_writeback.c
@@ -45,9 +45,49 @@
 	WITH_CLIENT
 };
 
-static struct mdp4_overlay_pipe *writeback_pipe;
-static struct msm_fb_data_type *writeback_mfd;
-static int busy_wait_cnt;
+#define MAX_CONTROLLER	1
+#define VSYNC_EXPIRE_TICK 0
+
+static struct vsycn_ctrl {
+	struct device *dev;
+	int inited;
+	int update_ndx;
+	u32 ov_koff;
+	u32 ov_done;
+	atomic_t suspend;
+	struct mutex update_lock;
+	struct completion ov_comp;
+	spinlock_t spin_lock;
+	struct msm_fb_data_type *mfd;
+	struct mdp4_overlay_pipe *base_pipe;
+	struct vsync_update vlist[2];
+} vsync_ctrl_db[MAX_CONTROLLER];
+
+static void vsync_irq_enable(int intr, int term)
+{
+	unsigned long flag;
+
+	spin_lock_irqsave(&mdp_spin_lock, flag);
+	/* no need to clrear other interrupts for comamnd mode */
+	mdp_intr_mask |= intr;
+	outp32(MDP_INTR_ENABLE, mdp_intr_mask);
+	mdp_enable_irq(term);
+	spin_unlock_irqrestore(&mdp_spin_lock, flag);
+}
+
+static void vsync_irq_disable(int intr, int term)
+{
+	unsigned long flag;
+
+	spin_lock_irqsave(&mdp_spin_lock, flag);
+	/* no need to clrear other interrupts for comamnd mode */
+	mdp_intr_mask &= ~intr;
+	outp32(MDP_INTR_ENABLE, mdp_intr_mask);
+	mdp_disable_irq_nosync(term);
+	spin_unlock_irqrestore(&mdp_spin_lock, flag);
+}
+
+static int mdp4_overlay_writeback_update(struct msm_fb_data_type *mfd);
 
 int mdp4_overlay_writeback_on(struct platform_device *pdev)
 {
@@ -58,6 +98,8 @@
 	int bpp;
 	int ret;
 	uint32 data;
+	struct vsycn_ctrl *vctrl;
+	int cndx = 0;
 
 	mfd = (struct msm_fb_data_type *)platform_get_drvdata(pdev);
 
@@ -67,7 +109,9 @@
 	if (mfd->key != MFD_KEY)
 		return -EINVAL;
 
-	writeback_mfd = mfd;		  /* keep it */
+	vctrl = &vsync_ctrl_db[cndx];
+	vctrl->mfd = mfd;
+	vctrl->dev = mfd->fbi->dev;
 
 	fbi = mfd->fbi;
 
@@ -77,12 +121,14 @@
 		fbi->var.yoffset * fbi->fix.line_length;
 
 	/* MDP cmd block enable */
-	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+	mdp_clk_ctrl(1);
 
-	if (writeback_pipe == NULL) {
+	if (vctrl->base_pipe == NULL) {
 		pipe = mdp4_overlay_pipe_alloc(OVERLAY_TYPE_BF, MDP4_MIXER2);
-		if (pipe == NULL)
+		if (pipe == NULL) {
 			pr_info("%s: pipe_alloc failed\n", __func__);
+			return -EIO;
+		}
 		pipe->pipe_used++;
 		pipe->mixer_stage  = MDP4_MIXER_STAGE_BASE;
 		pipe->mixer_num  = MDP4_MIXER2;
@@ -92,11 +138,12 @@
 		if (ret < 0)
 			pr_info("%s: format2type failed\n", __func__);
 
-		writeback_pipe = pipe; /* keep it */
+		vctrl->base_pipe = pipe; /* keep it */
 
 	} else {
-		pipe = writeback_pipe;
+		pipe = vctrl->base_pipe;
 	}
+
 	ret = panel_next_on(pdev);
 
 	/* MDP_LAYERMIXER_WB_MUX_SEL to use mixer1 axi for mixer2 writeback */
@@ -113,46 +160,68 @@
 	MDP_OUTP(MDP_BASE + MDP4_OVERLAYPROC1_BASE + 0x5008,
 		(0x0 & 0xFFF));         /* 12-bit R */
 
-	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+	mdp_clk_ctrl(0);
 	return ret;
 }
 
 int mdp4_overlay_writeback_off(struct platform_device *pdev)
 {
-	int ret;
-	struct msm_fb_data_type *mfd =
-			(struct msm_fb_data_type *)platform_get_drvdata(pdev);
-	if (mfd && writeback_pipe) {
-		mdp4_writeback_dma_busy_wait(mfd);
-		mdp4_overlay_pipe_free(writeback_pipe);
-		mdp4_overlay_panel_mode_unset(writeback_pipe->mixer_num,
-						MDP4_PANEL_WRITEBACK);
-		writeback_pipe = NULL;
+	int cndx = 0;
+	struct msm_fb_data_type *mfd;
+	struct vsycn_ctrl *vctrl;
+	struct mdp4_overlay_pipe *pipe;
+	int ret = 0;
+
+	pr_debug("%s+:\n", __func__);
+
+	mfd = (struct msm_fb_data_type *)platform_get_drvdata(pdev);
+
+	vctrl = &vsync_ctrl_db[cndx];
+	pipe = vctrl->base_pipe;
+	if (pipe == NULL) {
+		pr_err("%s: NO base pipe\n", __func__);
+		return ret;
 	}
+
+	/* sanity check, free pipes besides base layer */
+	mdp4_overlay_unset_mixer(pipe->mixer_num);
+	mdp4_mixer_stage_down(pipe, 1);
+	mdp4_overlay_pipe_free(pipe);
+	vctrl->base_pipe = NULL;
+
 	ret = panel_next_off(pdev);
-	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+
+	mdp_clk_ctrl(1);
 	/* MDP_LAYERMIXER_WB_MUX_SEL to restore to default cfg*/
 	outpdw(MDP_BASE + 0x100F4, 0x0);
-	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+	mdp_clk_ctrl(0);
+	pr_debug("%s-:\n", __func__);
 	return ret;
 }
-int mdp4_overlay_writeback_update(struct msm_fb_data_type *mfd)
+
+static int mdp4_overlay_writeback_update(struct msm_fb_data_type *mfd)
 {
 	struct fb_info *fbi;
 	uint8 *buf;
 	unsigned int buf_offset;
 	struct mdp4_overlay_pipe *pipe;
 	int bpp;
+	int cndx = 0;
+	struct vsycn_ctrl *vctrl;
 
 	if (mfd->key != MFD_KEY)
 		return -ENODEV;
 
-	if (!writeback_pipe)
-		return -EINVAL;
 
 	fbi = mfd->fbi;
 
-	pipe = writeback_pipe;
+	vctrl = &vsync_ctrl_db[cndx];
+
+	pipe = vctrl->base_pipe;
+	if (!pipe) {
+		pr_err("%s: no base layer pipe\n", __func__);
+		return -EINVAL;
+	}
 
 	bpp = fbi->var.bits_per_pixel / 8;
 	buf = (uint8 *) fbi->fix.smem_start;
@@ -160,7 +229,7 @@
 		fbi->var.yoffset * fbi->fix.line_length;
 
 	/* MDP cmd block enable */
-	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+	mdp_clk_ctrl(1);
 
 	pipe->src_height = fbi->var.yres;
 	pipe->src_width = fbi->var.xres;
@@ -184,142 +253,190 @@
 	mdp4_mixer_stage_up(pipe, 0);
 
 	mdp4_overlayproc_cfg(pipe);
-	mdp4_mixer_stage_commit(pipe->mixer_num);
 	/* MDP cmd block disable */
-	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+	mdp_clk_ctrl(0);
 
 	wmb();
 	return 0;
 }
-void mdp4_writeback_dma_busy_wait(struct msm_fb_data_type *mfd)
+
+/*
+ * mdp4_wfd_piep_queue:
+ * called from thread context
+ */
+void mdp4_wfd_pipe_queue(int cndx, struct mdp4_overlay_pipe *pipe)
 {
-	unsigned long flag;
-	int need_wait = 0;
+	struct vsycn_ctrl *vctrl;
+	struct vsync_update *vp;
+	struct mdp4_overlay_pipe *pp;
+	int undx;
 
-	spin_lock_irqsave(&mdp_spin_lock, flag);
-	if (mfd->dma->busy == TRUE) {
-		if (busy_wait_cnt == 0)
-			INIT_COMPLETION(mfd->dma->comp);
-		busy_wait_cnt = 1;
-		need_wait++;
-	}
-	spin_unlock_irqrestore(&mdp_spin_lock, flag);
-
-	if (need_wait) {
-		/* wait until DMA finishes the current job */
-		pr_debug("%s: pending pid=%d\n",
-				__func__, current->pid);
-		wait_for_completion(&mfd->dma->comp);
-	}
-}
-
-void mdp4_overlay1_done_writeback(struct mdp_dma_data *dma)
-{
-	spin_lock(&mdp_spin_lock);
-	dma->busy = FALSE;
-	if (busy_wait_cnt)
-		busy_wait_cnt = 0;
-	mdp_disable_irq_nosync(MDP_OVERLAY2_TERM);
-	spin_unlock(&mdp_spin_lock);
-	complete_all(&dma->comp);
-	pr_debug("%s ovdone interrupt\n", __func__);
-
-}
-void mdp4_writeback_overlay_kickoff(struct msm_fb_data_type *mfd,
-				    struct mdp4_overlay_pipe *pipe)
-{
-	unsigned long flag;
-	spin_lock_irqsave(&mdp_spin_lock, flag);
-	mdp_enable_irq(MDP_OVERLAY2_TERM);
-
-	mfd->dma->busy = TRUE;
-	outp32(MDP_INTR_CLEAR, INTR_OVERLAY2_DONE);
-	mdp_intr_mask |= INTR_OVERLAY2_DONE;
-	outp32(MDP_INTR_ENABLE, mdp_intr_mask);
-
-	wmb();	/* make sure all registers updated */
-	spin_unlock_irqrestore(&mdp_spin_lock, flag);
-	/* start OVERLAY pipe */
-	mdp_pipe_kickoff(MDP_OVERLAY2_TERM, mfd);
-	wmb();
-	pr_debug("%s: before ov done interrupt\n", __func__);
-}
-void mdp4_writeback_dma_stop(struct msm_fb_data_type *mfd)
-{
-	/* mutex holded by caller */
-	if (mfd && writeback_pipe) {
-		mdp4_writeback_dma_busy_wait(mfd);
-		mdp4_overlay_writeback_update(mfd);
-
-		mdp4_writeback_overlay_kickoff(mfd, writeback_pipe);
-	}
-}
-
-void mdp4_writeback_kickoff_video(struct msm_fb_data_type *mfd,
-		struct mdp4_overlay_pipe *pipe)
-{
-	struct msmfb_writeback_data_list *node = NULL;
-	mutex_lock(&mfd->unregister_mutex);
-	mutex_lock(&mfd->writeback_mutex);
-	if (!list_empty(&mfd->writeback_free_queue)
-		&& mfd->writeback_state != WB_STOPING
-		&& mfd->writeback_state != WB_STOP) {
-		node = list_first_entry(&mfd->writeback_free_queue,
-				struct msmfb_writeback_data_list, active_entry);
-	}
-	if (node) {
-		list_del(&(node->active_entry));
-		node->state = IN_BUSY_QUEUE;
-		mfd->writeback_active_cnt++;
-	}
-	mutex_unlock(&mfd->writeback_mutex);
-
-	writeback_pipe->ov_blt_addr = (ulong) (node ? node->addr : NULL);
-
-	/* free previous iommu at freelist back to pool */
-	mdp4_overlay_iommu_unmap_freelist(writeback_pipe->mixer_num);
-
-	if (!writeback_pipe->ov_blt_addr) {
-		pr_err("%s: no writeback buffer 0x%x, %p\n", __func__,
-			(unsigned int)writeback_pipe->ov_blt_addr, node);
-		mutex_unlock(&mfd->unregister_mutex);
+	if (cndx >= MAX_CONTROLLER) {
+		pr_err("%s: out or range: cndx=%d\n", __func__, cndx);
 		return;
 	}
 
-	if (writeback_pipe->blt_cnt == 0)
-		mdp4_overlay_writeback_update(mfd);
+	vctrl = &vsync_ctrl_db[cndx];
 
-	pr_debug("%s: pid=%d\n", __func__, current->pid);
+	if (atomic_read(&vctrl->suspend) > 0)
+		return;
 
-	mdp4_mixer_stage_commit(pipe->mixer_num);
+	mutex_lock(&vctrl->update_lock);
+	undx =  vctrl->update_ndx;
+	vp = &vctrl->vlist[undx];
 
-	mdp4_writeback_overlay_kickoff(mfd, pipe);
-	mdp4_writeback_dma_busy_wait(mfd);
+	pp = &vp->plist[pipe->pipe_ndx - 1];	/* ndx start form 1 */
 
-	/* move current committed iommu to freelist */
-	mdp4_overlay_iommu_pipe_free(pipe->pipe_ndx, 0);
+	pr_debug("%s: vndx=%d pipe_ndx=%d pid=%d\n", __func__,
+		undx, pipe->pipe_ndx, current->pid);
 
-	mutex_lock(&mfd->writeback_mutex);
-	list_add_tail(&node->active_entry, &mfd->writeback_busy_queue);
-	mutex_unlock(&mfd->writeback_mutex);
-	mfd->writeback_active_cnt--;
-	mutex_unlock(&mfd->unregister_mutex);
-	wake_up(&mfd->wait_q);
+	*pp = *pipe;	/* clone it */
+	vp->update_cnt++;
+
+	mutex_unlock(&vctrl->update_lock);
+	mdp4_stat.overlay_play[pipe->mixer_num]++;
 }
 
-void mdp4_writeback_kickoff_ui(struct msm_fb_data_type *mfd,
-		struct mdp4_overlay_pipe *pipe)
-{
-	mdp4_mixer_stage_commit(pipe->mixer_num);
+static void mdp4_wfd_wait4ov(int cndx);
 
-	pr_debug("%s: pid=%d\n", __func__, current->pid);
-	mdp4_writeback_overlay_kickoff(mfd, pipe);
+int mdp4_wfd_pipe_commit(void)
+{
+	int  i, undx;
+	int mixer = 0;
+	struct vsycn_ctrl *vctrl;
+	struct vsync_update *vp;
+	struct mdp4_overlay_pipe *pipe;
+	struct mdp4_overlay_pipe *real_pipe;
+	unsigned long flags;
+	int cnt = 0;
+
+	vctrl = &vsync_ctrl_db[0];
+
+	mutex_lock(&vctrl->update_lock);
+	undx =  vctrl->update_ndx;
+	vp = &vctrl->vlist[undx];
+	pipe = vctrl->base_pipe;
+	mixer = pipe->mixer_num;
+
+	if (vp->update_cnt == 0) {
+		mutex_unlock(&vctrl->update_lock);
+		return cnt;
+	}
+
+	vctrl->update_ndx++;
+	vctrl->update_ndx &= 0x01;
+	vp->update_cnt = 0;     /* reset */
+	mutex_unlock(&vctrl->update_lock);
+
+	/* free previous committed iommu back to pool */
+	mdp4_overlay_iommu_unmap_freelist(mixer);
+
+	pipe = vp->plist;
+	for (i = 0; i < OVERLAY_PIPE_MAX; i++, pipe++) {
+		if (pipe->pipe_used) {
+			cnt++;
+			real_pipe = mdp4_overlay_ndx2pipe(pipe->pipe_ndx);
+			if (real_pipe && real_pipe->pipe_used) {
+				/* pipe not unset */
+				mdp4_overlay_vsync_commit(pipe);
+			}
+			/* free previous iommu to freelist
+			* which will be freed at next
+			* pipe_commit
+			*/
+			mdp4_overlay_iommu_pipe_free(pipe->pipe_ndx, 0);
+			pipe->pipe_used = 0; /* clear */
+		}
+	}
+
+	mdp4_mixer_stage_commit(mixer);
+
+	pipe = vctrl->base_pipe;
+	spin_lock_irqsave(&vctrl->spin_lock, flags);
+	vctrl->ov_koff++;
+	INIT_COMPLETION(vctrl->ov_comp);
+	vsync_irq_enable(INTR_OVERLAY2_DONE, MDP_OVERLAY2_TERM);
+	pr_debug("%s: kickoff\n", __func__);
+	/* kickoff overlay engine */
+	mdp4_stat.kickoff_ov2++;
+	outpdw(MDP_BASE + 0x00D0, 0);
+	mb(); /* make sure kickoff executed */
+	spin_unlock_irqrestore(&vctrl->spin_lock, flags);
+
+	mdp4_stat.overlay_commit[pipe->mixer_num]++;
+
+	return cnt;
+}
+
+void mdp4_wfd_init(int cndx)
+{
+	struct vsycn_ctrl *vctrl;
+
+	if (cndx >= MAX_CONTROLLER) {
+		pr_err("%s: out or range: cndx=%d\n", __func__, cndx);
+		return;
+	}
+
+	vctrl = &vsync_ctrl_db[cndx];
+	if (vctrl->inited)
+		return;
+
+	vctrl->inited = 1;
+	vctrl->update_ndx = 0;
+	mutex_init(&vctrl->update_lock);
+	init_completion(&vctrl->ov_comp);
+	spin_lock_init(&vctrl->spin_lock);
+}
+
+static void mdp4_wfd_wait4ov(int cndx)
+{
+	struct vsycn_ctrl *vctrl;
+
+	if (cndx >= MAX_CONTROLLER) {
+		pr_err("%s: out or range: cndx=%d\n", __func__, cndx);
+		return;
+	}
+
+	vctrl = &vsync_ctrl_db[cndx];
+
+	if (atomic_read(&vctrl->suspend) > 0)
+		return;
+
+	wait_for_completion(&vctrl->ov_comp);
+}
+
+
+void mdp4_overlay2_done_wfd(struct mdp_dma_data *dma)
+{
+	struct vsycn_ctrl *vctrl;
+	struct mdp4_overlay_pipe *pipe;
+	int cndx = 0;
+
+	vctrl = &vsync_ctrl_db[cndx];
+	pipe = vctrl->base_pipe;
+
+	spin_lock(&vctrl->spin_lock);
+	vsync_irq_disable(INTR_OVERLAY2_DONE, MDP_OVERLAY2_TERM);
+	vctrl->ov_done++;
+	complete(&vctrl->ov_comp);
+
+	pr_debug("%s ovdone interrupt\n", __func__);
+	spin_unlock(&vctrl->spin_lock);
 }
 
 void mdp4_writeback_overlay(struct msm_fb_data_type *mfd)
 {
-	int ret = 0;
 	struct msmfb_writeback_data_list *node = NULL;
+	struct vsycn_ctrl *vctrl;
+	struct mdp4_overlay_pipe *pipe;
+
+	if (mfd && !mfd->panel_power_on)
+		return;
+
+	pr_debug("%s:+ mfd=%x\n", __func__, (int)mfd);
+
+	vctrl = &vsync_ctrl_db[0];
+	pipe = vctrl->base_pipe;
 
 	mutex_lock(&mfd->unregister_mutex);
 	mutex_lock(&mfd->writeback_mutex);
@@ -336,44 +453,36 @@
 	}
 	mutex_unlock(&mfd->writeback_mutex);
 
-	writeback_pipe->ov_blt_addr = (ulong) (node ? node->addr : NULL);
+	pipe->ov_blt_addr = (ulong) (node ? node->addr : NULL);
+
+	if (!pipe->ov_blt_addr) {
+		pr_err("%s: no writeback buffer 0x%x, %p\n", __func__,
+			(unsigned int)pipe->ov_blt_addr, node);
+		mutex_unlock(&mfd->unregister_mutex);
+		return;
+	}
 
 	mutex_lock(&mfd->dma->ov_mutex);
-	pr_debug("%s in writeback\n", __func__);
-	if (writeback_pipe && !writeback_pipe->ov_blt_addr) {
+	if (pipe && !pipe->ov_blt_addr) {
 		pr_err("%s: no writeback buffer 0x%x\n", __func__,
-				(unsigned int)writeback_pipe->ov_blt_addr);
-		ret = mdp4_overlay_writeback_update(mfd);
-		if (ret)
-			pr_err("%s: update failed writeback pipe NULL\n",
-					__func__);
+				(unsigned int)pipe->ov_blt_addr);
 		goto fail_no_blt_addr;
 	}
 
-	if (mfd && mfd->panel_power_on) {
-		pr_debug("%s in before busy wait\n", __func__);
-		mdp4_writeback_dma_busy_wait(mfd);
+	if (pipe->pipe_type == OVERLAY_TYPE_RGB)
+		mdp4_wfd_pipe_queue(0, pipe);
 
-		pr_debug("%s in before update\n", __func__);
-		ret = mdp4_overlay_writeback_update(mfd);
-		if (ret) {
-			pr_err("%s: update failed writeback pipe NULL\n",
-					__func__);
-			goto fail_no_blt_addr;
-		}
+	mdp4_overlay_mdp_perf_upd(mfd, 1);
 
-		pr_debug("%s: in writeback pan display 0x%x\n", __func__,
-				(unsigned int)writeback_pipe->ov_blt_addr);
-		mdp4_writeback_kickoff_ui(mfd, writeback_pipe);
-		mdp4_iommu_unmap(writeback_pipe);
+	mdp_clk_ctrl(1);
+	mdp4_overlay_writeback_update(mfd);
 
-		/* signal if pan function is waiting for the
-		 * update completion */
-		if (mfd->pan_waiting) {
-			mfd->pan_waiting = FALSE;
-			complete(&mfd->pan_comp);
-		}
-	}
+	mdp4_wfd_pipe_commit();
+
+	mdp4_overlay_mdp_perf_upd(mfd, 0);
+
+	mdp4_wfd_wait4ov(0);
+	mdp_clk_ctrl(0);
 
 	mutex_lock(&mfd->writeback_mutex);
 	list_add_tail(&node->active_entry, &mfd->writeback_busy_queue);
@@ -385,7 +494,9 @@
 	  mdp4_overlay_resource_release();*/
 	mutex_unlock(&mfd->dma->ov_mutex);
 	mutex_unlock(&mfd->unregister_mutex);
+	pr_debug("%s:-\n", __func__);
 }
+
 static int mdp4_overlay_writeback_register_buffer(
 	struct msm_fb_data_type *mfd, struct msmfb_writeback_data_list *node)
 {
diff --git a/drivers/video/msm/mdp4_util.c b/drivers/video/msm/mdp4_util.c
index 87921e6..ee9ca3c 100644
--- a/drivers/video/msm/mdp4_util.c
+++ b/drivers/video/msm/mdp4_util.c
@@ -557,7 +557,7 @@
 			mdp4_dmap_done_dsi_cmd(0);
 #else
 		else { /* MDDI */
-			mdp4_dma_p_done_mddi(dma);
+			mdp4_dmap_done_mddi(0);
 			mdp_pipe_ctrl(MDP_DMA2_BLOCK,
 				MDP_BLOCK_POWER_OFF, TRUE);
 			complete(&dma->comp);
@@ -608,7 +608,7 @@
 				mdp4_overlay0_done_dsi_cmd(0);
 #else
 			if (panel & MDP4_PANEL_MDDI)
-				mdp4_overlay0_done_mddi(dma);
+				mdp4_overlay0_done_mddi(0);
 #endif
 		}
 		mdp_hw_cursor_done();
@@ -630,19 +630,14 @@
 		if (panel & MDP4_PANEL_ATV)
 			mdp4_overlay1_done_atv();
 #endif
+		mdp_hw_cursor_done();
 	}
 #if defined(CONFIG_FB_MSM_WRITEBACK_MSM_PANEL)
 	if (isr & INTR_OVERLAY2_DONE) {
 		mdp4_stat.intr_overlay2++;
 		/* disable DTV interrupt */
-		dma = &dma_wb_data;
-		spin_lock(&mdp_spin_lock);
-		mdp_intr_mask &= ~INTR_OVERLAY2_DONE;
-		outp32(MDP_INTR_ENABLE, mdp_intr_mask);
-		dma->waiting = FALSE;
-		spin_unlock(&mdp_spin_lock);
 		if (panel & MDP4_PANEL_WRITEBACK)
-			mdp4_overlay1_done_writeback(dma);
+			mdp4_overlay2_done_wfd(&dma_wb_data);
 	}
 #endif
 #endif	/* OVERLAY */
diff --git a/drivers/video/msm/mdp_cursor.c b/drivers/video/msm/mdp_cursor.c
index f8c08e3..b5930a1 100644
--- a/drivers/video/msm/mdp_cursor.c
+++ b/drivers/video/msm/mdp_cursor.c
@@ -52,7 +52,11 @@
 
 	/* disable vsync */
 	spin_lock_irqsave(&mdp_spin_lock, flag);
-	mdp_disable_irq(MDP_OVERLAY0_TERM);
+	if (hdmi_prim_display)
+		mdp_disable_irq(MDP_OVERLAY1_TERM);
+	else
+		mdp_disable_irq(MDP_OVERLAY0_TERM);
+
 	spin_unlock_irqrestore(&mdp_spin_lock, flag);
 }
 
@@ -78,29 +82,37 @@
 	 *
 	 * Moving this code out of the ISR will cause the MDP to underrun!
 	 */
+	uint32_t base = 0;
+
+	if (hdmi_prim_display)
+		base = ((uint32_t)(MDP_BASE + 0xB0000));
+	else
+		base = ((uint32_t)(MDP_BASE + 0x90000));
+
+
 	spin_lock(&mdp_spin_lock);
 	if (sync_disabled) {
 		spin_unlock(&mdp_spin_lock);
 		return;
 	}
 
-	MDP_OUTP(MDP_BASE + 0x90044, (height << 16) | width);
-	MDP_OUTP(MDP_BASE + 0x90048, cursor_buf_phys);
+	MDP_OUTP(base + 0x44, (height << 16) | width);
+	MDP_OUTP(base + 0x48, cursor_buf_phys);
 
-	MDP_OUTP(MDP_BASE + 0x90060,
+	MDP_OUTP(base + 0x60,
 		 (transp_en << 3) | (calpha_en << 1) |
-		 (inp32(MDP_BASE + 0x90060) & 0x1));
+		 (inp32(base + 0x60) & 0x1));
 
-	MDP_OUTP(MDP_BASE + 0x90064, (alpha << 24));
-	MDP_OUTP(MDP_BASE + 0x90068, (0xffffff & bg_color));
-	MDP_OUTP(MDP_BASE + 0x9006C, (0xffffff & bg_color));
+	MDP_OUTP(base + 0x64, (alpha << 24));
+	MDP_OUTP(base + 0x68, (0xffffff & bg_color));
+	MDP_OUTP(base + 0x6C, (0xffffff & bg_color));
 
 	/* enable/disable the cursor as per the last request */
-	if (cursor_enabled && !(inp32(MDP_BASE + 0x90060) & (0x1)))
-		MDP_OUTP(MDP_BASE + 0x90060, inp32(MDP_BASE + 0x90060) | 0x1);
-	else if (!cursor_enabled && (inp32(MDP_BASE + 0x90060) & (0x1)))
-		MDP_OUTP(MDP_BASE + 0x90060,
-					inp32(MDP_BASE + 0x90060) & (~0x1));
+	if (cursor_enabled && !(inp32(base + 0x60) & (0x1)))
+		MDP_OUTP(base + 0x60, inp32(base + 0x60) | 0x1);
+	else if (!cursor_enabled && (inp32(base + 0x60) & (0x1)))
+		MDP_OUTP(base + 0x60,
+					inp32(base + 0x60) & (~0x1));
 
 	/* enqueue the task to disable MDP interrupts */
 	queue_work(mdp_cursor_ctrl_wq, &mdp_cursor_ctrl_worker);
@@ -119,17 +131,26 @@
 	if (sync_disabled) {
 
 		/* cancel pending task to disable MDP interrupts */
-		if (work_pending(&mdp_cursor_ctrl_worker))
+		if (work_pending(&mdp_cursor_ctrl_worker)) {
 			cancel_work_sync(&mdp_cursor_ctrl_worker);
-		else
+		} else {
 			/* enable irq */
-			mdp_enable_irq(MDP_OVERLAY0_TERM);
+			if (hdmi_prim_display)
+				mdp_enable_irq(MDP_OVERLAY1_TERM);
+			else
+				mdp_enable_irq(MDP_OVERLAY0_TERM);
+		}
 
 		sync_disabled = 0;
 
 		/* enable vsync intr */
-		outp32(MDP_INTR_CLEAR, INTR_OVERLAY0_DONE);
-		mdp_intr_mask |= INTR_OVERLAY0_DONE;
+		if (hdmi_prim_display) {
+			outp32(MDP_INTR_CLEAR, INTR_OVERLAY1_DONE);
+			mdp_intr_mask |= INTR_OVERLAY1_DONE;
+		} else {
+			outp32(MDP_INTR_CLEAR, INTR_OVERLAY0_DONE);
+			mdp_intr_mask |= INTR_OVERLAY0_DONE;
+		}
 		outp32(MDP_INTR_ENABLE, mdp_intr_mask);
 	}
 }
@@ -140,14 +161,20 @@
 	struct fb_image *img = &cursor->image;
 	unsigned long flag;
 	int sync_needed = 0, ret = 0;
+	uint32_t base = 0;
 
 	if ((img->width > MDP_CURSOR_WIDTH) ||
 	    (img->height > MDP_CURSOR_HEIGHT) ||
 	    (img->depth != 32))
 		return -EINVAL;
 
+	if (hdmi_prim_display)
+		base = ((uint32_t)(MDP_BASE + 0xB0000));
+	else
+		base = ((uint32_t)(MDP_BASE + 0x90000));
+
 	if (cursor->set & FB_CUR_SETPOS)
-		MDP_OUTP(MDP_BASE + 0x9004c, (img->dy << 16) | img->dx);
+		MDP_OUTP(base + 0x4c, (img->dy << 16) | img->dx);
 
 	if (cursor->set & FB_CUR_SETIMAGE) {
 		ret = copy_from_user(mfd->cursor_buf, img->data,
diff --git a/drivers/video/msm/mdp_debugfs.c b/drivers/video/msm/mdp_debugfs.c
index 0fad0a7..54f5ef5 100644
--- a/drivers/video/msm/mdp_debugfs.c
+++ b/drivers/video/msm/mdp_debugfs.c
@@ -719,84 +719,6 @@
 	.write = pmdh_reg_write,
 };
 
-
-
-#if defined(CONFIG_FB_MSM_OVERLAY) && defined(CONFIG_FB_MSM_MDDI)
-static int vsync_reg_open(struct inode *inode, struct file *file)
-{
-	/* non-seekable */
-	file->f_mode &= ~(FMODE_LSEEK | FMODE_PREAD | FMODE_PWRITE);
-	return 0;
-}
-
-static int vsync_reg_release(struct inode *inode, struct file *file)
-{
-	return 0;
-}
-
-static ssize_t vsync_reg_write(
-	struct file *file,
-	const char __user *buff,
-	size_t count,
-	loff_t *ppos)
-{
-	uint32 enable;
-	int cnt;
-
-	if (count >= sizeof(debug_buf))
-		return -EFAULT;
-
-	if (copy_from_user(debug_buf, buff, count))
-		return -EFAULT;
-
-	debug_buf[count] = 0;	/* end of string */
-
-	cnt = sscanf(debug_buf, "%x", &enable);
-
-	mdp_dmap_vsync_set(enable);
-
-	return count;
-}
-
-static ssize_t vsync_reg_read(
-	struct file *file,
-	char __user *buff,
-	size_t count,
-	loff_t *ppos)
-{
-	char *bp;
-	int len = 0;
-	int tot = 0;
-	int dlen;
-
-	if (*ppos)
-		return 0;	/* the end */
-
-	bp = debug_buf;
-	dlen = sizeof(debug_buf);
-	len = snprintf(bp, dlen, "%x\n", mdp_dmap_vsync_get());
-	tot += len;
-	bp += len;
-	*bp = 0;
-	tot++;
-
-	if (copy_to_user(buff, debug_buf, tot))
-		return -EFAULT;
-
-	*ppos += tot;	/* increase offset */
-
-	return tot;
-}
-
-
-static const struct file_operations vsync_fops = {
-	.open = vsync_reg_open,
-	.release = vsync_reg_release,
-	.read = vsync_reg_read,
-	.write = vsync_reg_write,
-};
-#endif
-
 static ssize_t emdh_reg_write(
 	struct file *file,
 	const char __user *buff,
@@ -1342,15 +1264,6 @@
 		return -1;
 	}
 
-#if defined(CONFIG_FB_MSM_OVERLAY) && defined(CONFIG_FB_MSM_MDDI)
-	if (debugfs_create_file("vsync", 0644, dent, 0, &vsync_fops)
-			== NULL) {
-		printk(KERN_ERR "%s(%d): debugfs_create_file: debug fail\n",
-			__FILE__, __LINE__);
-		return -1;
-	}
-#endif
-
 	dent = debugfs_create_dir("emdh", NULL);
 
 	if (IS_ERR(dent)) {
diff --git a/drivers/video/msm/mdss/Makefile b/drivers/video/msm/mdss/Makefile
index ddb6dd9..b4bd31e 100644
--- a/drivers/video/msm/mdss/Makefile
+++ b/drivers/video/msm/mdss/Makefile
@@ -7,14 +7,16 @@
 mdss-mdp-objs += mdss_mdp_wb.o
 obj-$(CONFIG_FB_MSM_MDSS) += mdss-mdp.o
 obj-$(CONFIG_FB_MSM_MDSS) += mdss_fb.o
-obj-$(CONFIG_FB_MSM_MDSS) += mdss_io_util.o
 
 mdss-dsi-objs := mdss_dsi.o mdss_dsi_host.o
 mdss-dsi-objs += mdss_dsi_panel.o
 mdss-dsi-objs += msm_mdss_io_8974.o
 obj-$(CONFIG_FB_MSM_MDSS) += mdss-dsi.o
+obj-$(CONFIG_FB_MSM_MDSS) += mdss_edp.o
 
+obj-$(CONFIG_FB_MSM_MDSS) += mdss_io_util.o
 obj-$(CONFIG_FB_MSM_MDSS_HDMI_PANEL) += mdss_hdmi_tx.o
 obj-$(CONFIG_FB_MSM_MDSS_HDMI_PANEL) += mdss_hdmi_util.o
 obj-$(CONFIG_FB_MSM_MDSS_HDMI_PANEL) += mdss_hdmi_edid.o
+
 obj-$(CONFIG_FB_MSM_MDSS_WRITEBACK) += mdss_wb.o
diff --git a/drivers/video/msm/mdss/mdss_edp.c b/drivers/video/msm/mdss/mdss_edp.c
new file mode 100644
index 0000000..b35be75
--- /dev/null
+++ b/drivers/video/msm/mdss/mdss_edp.c
@@ -0,0 +1,626 @@
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/time.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/of_platform.h>
+#include <linux/of_gpio.h>
+#include <linux/gpio.h>
+#include <linux/err.h>
+#include <linux/regulator/consumer.h>
+#include <linux/pwm.h>
+
+#include <asm/system.h>
+#include <asm/mach-types.h>
+
+#include <mach/hardware.h>
+#include <mach/dma.h>
+
+#include "mdss_edp.h"
+
+#define RGB_COMPONENTS		3
+#define VDDA_MIN_UV			1800000	/* uV units */
+#define VDDA_MAX_UV			1800000	/* uV units */
+#define VDDA_UA_ON_LOAD		100000	/* uA units */
+#define VDDA_UA_OFF_LOAD	100		/* uA units */
+
+static int mdss_edp_get_base_address(struct mdss_edp_drv_pdata *edp_drv);
+static int mdss_edp_get_mmss_cc_base_address(struct mdss_edp_drv_pdata
+		*edp_drv);
+static int mdss_edp_regulator_init(struct mdss_edp_drv_pdata *edp_drv);
+static int mdss_edp_regulator_on(struct mdss_edp_drv_pdata *edp_drv);
+static int mdss_edp_regulator_off(struct mdss_edp_drv_pdata *edp_drv);
+static int mdss_edp_gpio_panel_en(struct mdss_edp_drv_pdata *edp_drv);
+static int mdss_edp_pwm_config(struct mdss_edp_drv_pdata *edp_drv);
+
+static void mdss_edp_edid2pinfo(struct mdss_edp_drv_pdata *edp_drv);
+static void mdss_edp_fill_edid_data(struct mdss_edp_drv_pdata *edp_drv);
+static void mdss_edp_fill_dpcd_data(struct mdss_edp_drv_pdata *edp_drv);
+
+static int mdss_edp_device_register(struct mdss_edp_drv_pdata *edp_drv);
+
+static void mdss_edp_config_sync(unsigned char *edp_base);
+static void mdss_edp_config_sw_div(unsigned char *edp_base);
+static void mdss_edp_config_static_mdiv(unsigned char *edp_base);
+static void mdss_edp_enable(unsigned char *edp_base, int enable);
+
+/*
+ * Init regulator needed for edp, 8974_l12
+ */
+static int mdss_edp_regulator_init(struct mdss_edp_drv_pdata *edp_drv)
+{
+	int ret;
+
+	edp_drv->vdda_vreg = devm_regulator_get(&(edp_drv->pdev->dev), "vdda");
+	if (IS_ERR(edp_drv->vdda_vreg)) {
+		pr_err("%s: Could not get 8941_l12, ret = %ld\n", __func__,
+				PTR_ERR(edp_drv->vdda_vreg));
+		return -ENODEV;
+	}
+
+	ret = regulator_set_voltage(edp_drv->vdda_vreg,
+			VDDA_MIN_UV, VDDA_MAX_UV);
+	if (ret) {
+		pr_err("%s: vdda_vreg set_voltage failed, ret=%d\n", __func__,
+				ret);
+		return -EINVAL;
+	}
+
+	ret = mdss_edp_regulator_on(edp_drv);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+/*
+ * Set uA and enable vdda
+ */
+static int mdss_edp_regulator_on(struct mdss_edp_drv_pdata *edp_drv)
+{
+	int ret;
+
+	ret = regulator_set_optimum_mode(edp_drv->vdda_vreg, VDDA_UA_ON_LOAD);
+	if (ret < 0) {
+		pr_err("%s: vdda_vreg set regulator mode failed.\n", __func__);
+		return ret;
+	}
+
+	ret = regulator_enable(edp_drv->vdda_vreg);
+	if (ret) {
+		pr_err("%s: Failed to enable vdda_vreg regulator.\n", __func__);
+		return ret;
+	}
+
+	return 0;
+}
+
+/*
+ * Disable vdda and set uA
+ */
+static int mdss_edp_regulator_off(struct mdss_edp_drv_pdata *edp_drv)
+{
+	int ret;
+
+	ret = regulator_disable(edp_drv->vdda_vreg);
+	if (ret) {
+		pr_err("%s: Failed to disable vdda_vreg regulator.\n",
+				__func__);
+		return ret;
+	}
+
+	ret = regulator_set_optimum_mode(edp_drv->vdda_vreg, VDDA_UA_OFF_LOAD);
+	if (ret < 0) {
+		pr_err("%s: vdda_vreg set regulator mode failed.\n",
+				__func__);
+		return ret;
+	}
+
+	return 0;
+}
+
+/*
+ * Enables the gpio that supply power to the panel and enable the backlight
+ */
+static int mdss_edp_gpio_panel_en(struct mdss_edp_drv_pdata *edp_drv)
+{
+	int ret = 0;
+
+	edp_drv->gpio_panel_en = of_get_named_gpio(edp_drv->pdev->dev.of_node,
+			"gpio-panel-en", 0);
+	if (!gpio_is_valid(edp_drv->gpio_panel_en)) {
+		pr_err("%s: gpio_panel_en=%d not specified\n", __func__,
+				edp_drv->gpio_panel_en);
+		goto gpio_err;
+	}
+
+	ret = gpio_request(edp_drv->gpio_panel_en, "disp_enable");
+	if (ret) {
+		pr_err("%s: Request reset gpio_panel_en failed, ret=%d\n",
+				__func__, ret);
+		goto gpio_free;
+	}
+
+	ret = gpio_direction_output(edp_drv->gpio_panel_en, 1);
+	if (ret) {
+		pr_err("%s: Set direction for gpio_panel_en failed, ret=%d\n",
+				__func__, ret);
+		goto gpio_free;
+	}
+
+	gpio_set_value(edp_drv->gpio_panel_en, 1);
+
+	return 0;
+
+gpio_free:
+	gpio_free(edp_drv->gpio_panel_en);
+gpio_err:
+	return -ENODEV;
+}
+
+static int mdss_edp_pwm_config(struct mdss_edp_drv_pdata *edp_drv)
+{
+	int ret = 0;
+
+	ret = of_property_read_u32(edp_drv->pdev->dev.of_node,
+			"qcom,panel-pwm-period", &edp_drv->pwm_period);
+	if (ret) {
+		pr_err("%s: panel pwm period is not specified, %d", __func__,
+				edp_drv->pwm_period);
+		return -EINVAL;
+	}
+
+	ret = of_property_read_u32(edp_drv->pdev->dev.of_node,
+			"qcom,panel-lpg-channel", &edp_drv->lpg_channel);
+	if (ret) {
+		pr_err("%s: panel lpg channel is not specified, %d", __func__,
+				edp_drv->lpg_channel);
+		return -EINVAL;
+	}
+
+	edp_drv->bl_pwm = pwm_request(edp_drv->lpg_channel, "lcd-backlight");
+	if (edp_drv->bl_pwm == NULL || IS_ERR(edp_drv->bl_pwm)) {
+		pr_err("%s: pwm request failed", __func__);
+		edp_drv->bl_pwm = NULL;
+		return -EIO;
+	}
+
+	edp_drv->gpio_panel_pwm = of_get_named_gpio(edp_drv->pdev->dev.of_node,
+			"gpio-panel-pwm", 0);
+	if (!gpio_is_valid(edp_drv->gpio_panel_pwm)) {
+		pr_err("%s: gpio_panel_pwm=%d not specified\n", __func__,
+				edp_drv->gpio_panel_pwm);
+		goto edp_free_pwm;
+	}
+
+	ret = gpio_request(edp_drv->gpio_panel_pwm, "disp_pwm");
+	if (ret) {
+		pr_err("%s: Request reset gpio_panel_pwm failed, ret=%d\n",
+				__func__, ret);
+		goto edp_free_gpio_pwm;
+	}
+
+	return 0;
+
+edp_free_gpio_pwm:
+	gpio_free(edp_drv->gpio_panel_pwm);
+edp_free_pwm:
+	pwm_free(edp_drv->bl_pwm);
+	return -ENODEV;
+}
+
+void mdss_edp_set_backlight(struct mdss_panel_data *pdata, u32 bl_level)
+{
+	int ret = 0;
+	struct mdss_edp_drv_pdata *edp_drv = NULL;
+	int bl_max;
+
+	edp_drv = container_of(pdata, struct mdss_edp_drv_pdata, panel_data);
+	if (!edp_drv) {
+		pr_err("%s: Invalid input data\n", __func__);
+		return;
+	}
+
+	bl_max = edp_drv->panel_data.panel_info.bl_max;
+	if (bl_level > bl_max)
+		bl_level = bl_max;
+
+	if (edp_drv->bl_pwm == NULL) {
+		pr_err("%s: edp_drv->bl_pwm=NULL.\n", __func__);
+		return;
+	}
+
+	ret = pwm_config(edp_drv->bl_pwm,
+			bl_level * edp_drv->pwm_period / bl_max,
+			edp_drv->pwm_period);
+	if (ret) {
+		pr_err("%s: pwm_config() failed err=%d.\n", __func__, ret);
+		return;
+	}
+
+	ret = pwm_enable(edp_drv->bl_pwm);
+	if (ret) {
+		pr_err("%s: pwm_enable() failed err=%d\n", __func__, ret);
+		return;
+	}
+}
+
+void mdss_edp_config_sync(unsigned char *edp_base)
+{
+	int ret = 0;
+
+	ret = edp_read(edp_base + 0xc); /* EDP_CONFIGURATION_CTRL */
+	ret &= ~0x733;
+	ret |= (0x55 & 0x733);
+	edp_write(edp_base + 0xc, ret);
+	edp_write(edp_base + 0xc, 0x55); /* EDP_CONFIGURATION_CTRL */
+}
+
+static void mdss_edp_config_sw_div(unsigned char *edp_base)
+{
+	edp_write(edp_base + 0x14, 0x13b); /* EDP_SOFTWARE_MVID */
+	edp_write(edp_base + 0x18, 0x266); /* EDP_SOFTWARE_NVID */
+}
+
+static void mdss_edp_config_static_mdiv(unsigned char *edp_base)
+{
+	int ret = 0;
+
+	ret = edp_read(edp_base + 0xc); /* EDP_CONFIGURATION_CTRL */
+	edp_write(edp_base + 0xc, ret | 0x2); /* EDP_CONFIGURATION_CTRL */
+	edp_write(edp_base + 0xc, 0x57); /* EDP_CONFIGURATION_CTRL */
+}
+
+static void mdss_edp_enable(unsigned char *edp_base, int enable)
+{
+	edp_write(edp_base + 0x8, 0x0); /* EDP_STATE_CTRL */
+	edp_write(edp_base + 0x8, 0x40); /* EDP_STATE_CTRL */
+	edp_write(edp_base + 0x94, enable); /* EDP_TIMING_ENGINE_EN */
+	edp_write(edp_base + 0x4, enable); /* EDP_MAINLINK_CTRL */
+}
+
+int mdss_edp_on(struct mdss_panel_data *pdata)
+{
+	struct mdss_edp_drv_pdata *edp_drv = NULL;
+	int i;
+
+	edp_drv = container_of(pdata, struct mdss_edp_drv_pdata,
+			panel_data);
+	if (!edp_drv) {
+		pr_err("%s: Invalid input data\n", __func__);
+		return -EINVAL;
+	}
+
+	mdss_edp_prepare_clocks(edp_drv);
+	mdss_edp_clk_enable(edp_drv);
+	mdss_edp_phy_sw_reset(edp_drv->edp_base);
+	mdss_edp_hw_powerup(edp_drv->edp_base, 1);
+	mdss_edp_pll_configure(edp_drv->edp_base, edp_drv->edid.timing[0].pclk);
+
+	for (i = 0; i < edp_drv->dpcd.max_lane_count; ++i)
+		mdss_edp_enable_lane_bist(edp_drv->edp_base, i, 1);
+
+	mdss_edp_enable_mainlink(edp_drv->edp_base, 1);
+	mdss_edp_config_clk(edp_drv->edp_base, edp_drv->mmss_cc_base);
+
+	mdss_edp_phy_misc_cfg(edp_drv->edp_base);
+	mdss_edp_config_sync(edp_drv->edp_base);
+	mdss_edp_config_sw_div(edp_drv->edp_base);
+	mdss_edp_config_static_mdiv(edp_drv->edp_base);
+	mdss_edp_enable(edp_drv->edp_base, 1);
+
+	return 0;
+}
+
+int mdss_edp_off(struct mdss_panel_data *pdata)
+{
+	struct mdss_edp_drv_pdata *edp_drv = NULL;
+	int ret = 0;
+	int i;
+
+	edp_drv = container_of(pdata, struct mdss_edp_drv_pdata,
+				panel_data);
+	if (!edp_drv) {
+		pr_err("%s: Invalid input data\n", __func__);
+		return -EINVAL;
+	}
+
+	pwm_disable(edp_drv->bl_pwm);
+	mdss_edp_enable(edp_drv->edp_base, 0);
+	mdss_edp_unconfig_clk(edp_drv->edp_base, edp_drv->mmss_cc_base);
+	mdss_edp_enable_mainlink(edp_drv->edp_base, 0);
+
+	for (i = 0; i < edp_drv->dpcd.max_lane_count; ++i)
+		mdss_edp_enable_lane_bist(edp_drv->edp_base, i, 0);
+
+	mdss_edp_hw_powerup(edp_drv->edp_base, 0);
+	mdss_edp_clk_disable(edp_drv);
+	mdss_edp_unprepare_clocks(edp_drv);
+
+	return ret;
+}
+
+/*
+ * Converts from EDID struct to mdss_panel_info
+ */
+static void mdss_edp_edid2pinfo(struct mdss_edp_drv_pdata *edp_drv)
+{
+	struct display_timing_desc *dp;
+	struct mdss_panel_info *pinfo;
+
+	dp = &edp_drv->edid.timing[0];
+	pinfo = &edp_drv->panel_data.panel_info;
+
+	pinfo->clk_rate = dp->pclk;
+
+	pinfo->xres = dp->h_addressable + dp->h_border * 2;
+	pinfo->yres = dp->v_addressable + dp->v_border * 2;
+
+	pinfo->lcdc.h_back_porch = dp->h_blank - dp->h_fporch \
+		- dp->h_sync_pulse;
+	pinfo->lcdc.h_front_porch = dp->h_fporch;
+	pinfo->lcdc.h_pulse_width = dp->h_sync_pulse;
+
+	pinfo->lcdc.v_back_porch = dp->v_blank - dp->v_fporch \
+		- dp->v_sync_pulse;
+	pinfo->lcdc.v_front_porch = dp->v_fporch;
+	pinfo->lcdc.v_pulse_width = dp->v_sync_pulse;
+
+	pinfo->type = EDP_PANEL;
+	pinfo->pdest = DISPLAY_1;
+	pinfo->wait_cycle = 0;
+	pinfo->bpp = edp_drv->edid.color_depth * RGB_COMPONENTS;
+	pinfo->fb_num = 2;
+
+	pinfo->lcdc.border_clr = 0;	 /* black */
+	pinfo->lcdc.underflow_clr = 0xff; /* blue */
+	pinfo->lcdc.hsync_skew = 0;
+}
+
+static int __devexit mdss_edp_remove(struct platform_device *pdev)
+{
+	struct mdss_edp_drv_pdata *edp_drv = NULL;
+
+	edp_drv = platform_get_drvdata(pdev);
+
+	gpio_free(edp_drv->gpio_panel_en);
+	mdss_edp_regulator_off(edp_drv);
+	iounmap(edp_drv->edp_base);
+	iounmap(edp_drv->mmss_cc_base);
+	edp_drv->edp_base = NULL;
+
+	return 0;
+}
+
+static int mdss_edp_device_register(struct mdss_edp_drv_pdata *edp_drv)
+{
+	int ret;
+
+	mdss_edp_edid2pinfo(edp_drv);
+	edp_drv->panel_data.panel_info.bl_min = 1;
+	edp_drv->panel_data.panel_info.bl_max = 255;
+
+	edp_drv->panel_data.on = mdss_edp_on;
+	edp_drv->panel_data.off = mdss_edp_off;
+	edp_drv->panel_data.set_backlight = mdss_edp_set_backlight;
+
+	ret = mdss_register_panel(&edp_drv->panel_data);
+	if (ret) {
+		dev_err(&(edp_drv->pdev->dev), "unable to register eDP\n");
+		return ret;
+	}
+
+	pr_debug("%s: eDP initialized\n", __func__);
+
+	return 0;
+}
+
+/*
+ * Retrieve edp base address
+ */
+static int mdss_edp_get_base_address(struct mdss_edp_drv_pdata *edp_drv)
+{
+	struct resource *res;
+
+	res = platform_get_resource_byname(edp_drv->pdev, IORESOURCE_MEM,
+			"edp_base");
+	if (!res) {
+		pr_err("%s: Unable to get the MDSS EDP resources", __func__);
+		return -ENOMEM;
+	}
+
+	edp_drv->edp_base = ioremap(res->start, resource_size(res));
+	if (!edp_drv->edp_base) {
+		pr_err("%s: Unable to remap EDP resources",  __func__);
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+static int mdss_edp_get_mmss_cc_base_address(struct mdss_edp_drv_pdata
+		*edp_drv)
+{
+	struct resource *res;
+
+	res = platform_get_resource_byname(edp_drv->pdev, IORESOURCE_MEM,
+			"mmss_cc_base");
+	if (!res) {
+		pr_err("%s: Unable to get the MMSS_CC resources", __func__);
+		return -ENOMEM;
+	}
+
+	edp_drv->mmss_cc_base = ioremap(res->start, resource_size(res));
+	if (!edp_drv->mmss_cc_base) {
+		pr_err("%s: Unable to remap MMSS_CC resources",  __func__);
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+static void mdss_edp_fill_edid_data(struct mdss_edp_drv_pdata *edp_drv)
+{
+	struct edp_edid *edid = &edp_drv->edid;
+
+	edid->id_name[0] = 'A';
+	edid->id_name[0] = 'U';
+	edid->id_name[0] = 'O';
+	edid->id_name[0] = 0;
+	edid->id_product = 0x305D;
+	edid->version = 1;
+	edid->revision = 4;
+	edid->ext_block_cnt = 0;
+	edid->video_digital = 0x5;
+	edid->color_depth = 6;
+	edid->dpm = 0;
+	edid->color_format = 0;
+	edid->timing[0].pclk = 138500000;
+	edid->timing[0].h_addressable = 1920;
+	edid->timing[0].h_blank = 160;
+	edid->timing[0].v_addressable = 1080;
+	edid->timing[0].v_blank = 30;
+	edid->timing[0].h_fporch = 48;
+	edid->timing[0].h_sync_pulse = 32;
+	edid->timing[0].v_sync_pulse = 14;
+	edid->timing[0].v_fporch = 8;
+	edid->timing[0].width_mm =  256;
+	edid->timing[0].height_mm = 144;
+	edid->timing[0].h_border = 0;
+	edid->timing[0].v_border = 0;
+	edid->timing[0].interlaced = 0;
+	edid->timing[0].stereo = 0;
+	edid->timing[0].sync_type = 1;
+	edid->timing[0].sync_separate = 1;
+	edid->timing[0].vsync_pol = 0;
+	edid->timing[0].hsync_pol = 0;
+
+}
+
+static void mdss_edp_fill_dpcd_data(struct mdss_edp_drv_pdata *edp_drv)
+{
+	struct dpcd_cap *cap = &edp_drv->dpcd;
+
+	cap->max_lane_count = 2;
+	cap->max_link_clk = 270;
+}
+
+
+static int __devinit mdss_edp_probe(struct platform_device *pdev)
+{
+	int ret;
+	struct mdss_edp_drv_pdata *edp_drv;
+
+	if (!pdev->dev.of_node) {
+		pr_err("%s: Failed\n", __func__);
+		return -EPERM;
+	}
+
+	edp_drv = devm_kzalloc(&pdev->dev, sizeof(*edp_drv), GFP_KERNEL);
+	if (edp_drv == NULL) {
+		pr_err("%s: Failed, could not allocate edp_drv", __func__);
+		return -ENOMEM;
+	}
+
+	edp_drv->pdev = pdev;
+	edp_drv->pdev->id = 1;
+	edp_drv->clk_on = 0;
+
+	ret = mdss_edp_get_base_address(edp_drv);
+	if (ret)
+		goto probe_err;
+
+	ret = mdss_edp_get_mmss_cc_base_address(edp_drv);
+	if (ret)
+		goto edp_base_unmap;
+
+	ret = mdss_edp_regulator_init(edp_drv);
+	if (ret)
+		goto mmss_cc_base_unmap;
+
+	ret = mdss_edp_clk_init(edp_drv);
+	if (ret)
+		goto edp_clk_deinit;
+
+	ret = mdss_edp_gpio_panel_en(edp_drv);
+	if (ret)
+		goto edp_clk_deinit;
+
+	ret = mdss_edp_pwm_config(edp_drv);
+	if (ret)
+		goto edp_free_gpio_panel_en;
+
+	mdss_edp_fill_edid_data(edp_drv);
+	mdss_edp_fill_dpcd_data(edp_drv);
+	mdss_edp_device_register(edp_drv);
+
+	return 0;
+
+
+edp_free_gpio_panel_en:
+	gpio_free(edp_drv->gpio_panel_en);
+edp_clk_deinit:
+	mdss_edp_clk_deinit(edp_drv);
+	mdss_edp_regulator_off(edp_drv);
+mmss_cc_base_unmap:
+	iounmap(edp_drv->mmss_cc_base);
+edp_base_unmap:
+	iounmap(edp_drv->edp_base);
+probe_err:
+	return ret;
+
+}
+
+static const struct of_device_id msm_mdss_edp_dt_match[] = {
+	{.compatible = "qcom,mdss-edp"},
+	{}
+};
+MODULE_DEVICE_TABLE(of, msm_mdss_edp_dt_match);
+
+static struct platform_driver mdss_edp_driver = {
+	.probe = mdss_edp_probe,
+	.remove = __devexit_p(mdss_edp_remove),
+	.shutdown = NULL,
+	.driver = {
+		.name = "mdss_edp",
+		.of_match_table = msm_mdss_edp_dt_match,
+	},
+};
+
+static int __init mdss_edp_init(void)
+{
+	int ret;
+
+	ret = platform_driver_register(&mdss_edp_driver);
+	if (ret) {
+		pr_err("%s driver register failed", __func__);
+		return ret;
+	}
+
+	return ret;
+}
+module_init(mdss_edp_init);
+
+static void __exit mdss_edp_driver_cleanup(void)
+{
+	platform_driver_unregister(&mdss_edp_driver);
+}
+module_exit(mdss_edp_driver_cleanup);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("eDP controller driver");
diff --git a/drivers/video/msm/mdss/mdss_edp.h b/drivers/video/msm/mdss/mdss_edp.h
new file mode 100644
index 0000000..00ef206
--- /dev/null
+++ b/drivers/video/msm/mdss/mdss_edp.h
@@ -0,0 +1,119 @@
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef MDSS_EDP_H
+#define MDSS_EDP_H
+
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/clk.h>
+
+#include "mdss_panel.h"
+
+#define edp_read(offset) readl_relaxed((offset))
+#define edp_write(offset, data) writel_relaxed((data), (offset))
+
+struct display_timing_desc {
+	u32 pclk;
+	u32 h_addressable; /* addressable + boder = active */
+	u32 h_border;
+	u32 h_blank;	/* fporch + bporch + sync_pulse = blank */
+	u32 h_fporch;
+	u32 h_sync_pulse;
+	u32 v_addressable; /* addressable + boder = active */
+	u32 v_border;
+	u32 v_blank;	/* fporch + bporch + sync_pulse = blank */
+	u32 v_fporch;
+	u32 v_sync_pulse;
+	u32 width_mm;
+	u32 height_mm;
+	u32 interlaced;
+	u32 stereo;
+	u32 sync_type;
+	u32 sync_separate;
+	u32 vsync_pol;
+	u32 hsync_pol;
+};
+
+struct edp_edid {
+	char id_name[4];
+	short id_product;
+	char version;
+	char revision;
+	char video_digital;
+	char color_depth;	/* 6, 8, 10, 12 and 14 bits */
+	char color_format;	/* RGB 4:4:4, YCrCb 4:4:4, Ycrcb 4:2:2 */
+	char dpm;		/* display power management */
+	char sync_digital;	/* 1 = digital */
+	char sync_separate;	/* 1 = separate */
+	char vsync_pol;		/* 0 = negative, 1 = positive */
+	char hsync_pol;		/* 0 = negative, 1 = positive */
+	char ext_block_cnt;
+	struct display_timing_desc timing[4];
+};
+
+struct dpcd_cap {
+	char max_lane_count;
+	u32 max_link_clk;  /* 162, 270 and 540 Mb, divided by 10 */
+};
+
+struct mdss_edp_drv_pdata {
+	/* device driver */
+	int (*on) (struct mdss_panel_data *pdata);
+	int (*off) (struct mdss_panel_data *pdata);
+	struct platform_device *pdev;
+
+	/* edp specific */
+	struct mdss_panel_data panel_data;
+	unsigned char *edp_base;
+	unsigned char *mmss_cc_base;
+	struct edp_edid edid;
+	struct dpcd_cap dpcd;
+
+	/* regulators */
+	struct regulator *vdda_vreg;
+
+	/* clocks */
+	struct clk *aux_clk;
+	struct clk *pixel_clk;
+	struct clk *ahb_clk;
+	struct clk *link_clk;
+	int clk_on;
+
+	/* gpios */
+	int gpio_panel_en;
+	int gpio_panel_pwm;
+
+	/* backlight */
+	struct pwm_device *bl_pwm;
+	int lpg_channel;
+	int pwm_period;
+};
+
+void mdss_edp_phy_sw_reset(unsigned char *edp_base);
+void mdss_edp_pll_configure(unsigned char *edp_base, int rate);
+void mdss_edp_enable_lane_bist(unsigned char *edp_base, int lane, int enable);
+void mdss_edp_enable_mainlink(unsigned char *edp_base, int enable);
+void mdss_edp_hw_powerup(unsigned char *edp_base, int enable);
+void mdss_edp_clk_enable(struct mdss_edp_drv_pdata *edp_drv);
+void mdss_edp_clk_disable(struct mdss_edp_drv_pdata *edp_drv);
+int mdss_edp_clk_init(struct mdss_edp_drv_pdata *edp_drv);
+void mdss_edp_clk_deinit(struct mdss_edp_drv_pdata *edp_drv);
+void mdss_edp_prepare_clocks(struct mdss_edp_drv_pdata *edp_drv);
+void mdss_edp_unprepare_clocks(struct mdss_edp_drv_pdata *edp_drv);
+void mdss_edp_config_clk(unsigned char *edp_base, unsigned char *mmss_cc_base);
+void mdss_edp_unconfig_clk(unsigned char *edp_base,
+		unsigned char *mmss_cc_base);
+void mdss_edp_phy_misc_cfg(unsigned char *edp_base);
+
+#endif /* MDSS_EDP_H */
diff --git a/drivers/video/msm/mdss/mdss_fb.c b/drivers/video/msm/mdss/mdss_fb.c
index 9f29887..b711fd9 100644
--- a/drivers/video/msm/mdss/mdss_fb.c
+++ b/drivers/video/msm/mdss/mdss_fb.c
@@ -81,6 +81,42 @@
 			 unsigned long arg);
 static int mdss_fb_mmap(struct fb_info *info, struct vm_area_struct *vma);
 
+void mdss_fb_no_update_notify_timer_cb(unsigned long data)
+{
+	struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)data;
+	if (!mfd)
+		pr_err("%s mfd NULL\n", __func__);
+	complete(&mfd->no_update.comp);
+}
+
+static int mdss_fb_notify_update(struct msm_fb_data_type *mfd,
+							unsigned long *argp)
+{
+	int ret, notify;
+
+	ret = copy_from_user(&notify, argp, sizeof(int));
+	if (ret) {
+		pr_err("%s:ioctl failed\n", __func__);
+		return ret;
+	}
+
+	if (notify > NOTIFY_UPDATE_STOP)
+		return -EINVAL;
+
+	if (notify == NOTIFY_UPDATE_START) {
+		INIT_COMPLETION(mfd->update.comp);
+		ret = wait_for_completion_interruptible_timeout(
+						&mfd->update.comp, 4 * HZ);
+	} else {
+		INIT_COMPLETION(mfd->no_update.comp);
+		ret = wait_for_completion_interruptible_timeout(
+						&mfd->no_update.comp, 4 * HZ);
+	}
+	if (ret == 0)
+		ret = -ETIMEDOUT;
+	return (ret > 0) ? 0 : ret;
+}
+
 #define MAX_BACKLIGHT_BRIGHTNESS 255
 static int lcd_backlight_registered;
 
@@ -101,7 +137,9 @@
 	if (!bl_lvl && value)
 		bl_lvl = 1;
 
+	mutex_lock(&mfd->lock);
 	mdss_fb_set_backlight(mfd, bl_lvl);
+	mutex_unlock(&mfd->lock);
 }
 
 static struct led_classdev backlight_led = {
@@ -139,6 +177,9 @@
 	case WRITEBACK_PANEL:
 		ret = snprintf(buf, PAGE_SIZE, "writeback panel\n");
 		break;
+	case EDP_PANEL:
+		ret = snprintf(buf, PAGE_SIZE, "edp panel\n");
+		break;
 	default:
 		ret = snprintf(buf, PAGE_SIZE, "unknown panel\n");
 		break;
@@ -206,6 +247,8 @@
 	mfd->mdp_fb_page_protection = MDP_FB_PAGE_PROTECTION_WRITECOMBINE;
 	mfd->panel_info.frame_count = 0;
 	mfd->bl_level = 0;
+	mfd->bl_scale = 1024;
+	mfd->bl_min_lvl = 30;
 	mfd->fb_imgType = MDP_RGBA_8888;
 
 	mfd->pdev = pdev;
@@ -266,6 +309,11 @@
 		pr_err("msm_fb_remove: can't stop the device %d\n",
 			    mfd->index);
 
+	if (mfd->no_update.timer.function)
+		del_timer(&mfd->no_update.timer);
+	complete(&mfd->no_update.comp);
+	complete(&mfd->update.comp);
+
 	/* remove /dev/fb* */
 	unregister_framebuffer(mfd->fbi);
 
@@ -413,9 +461,46 @@
 static int unset_bl_level, bl_updated;
 static int bl_level_old;
 
+static int mdss_bl_scale_config(struct msm_fb_data_type *mfd,
+						struct mdp_bl_scale_data *data)
+{
+	int ret = 0;
+	int curr_bl;
+	mutex_lock(&mfd->lock);
+	curr_bl = mfd->bl_level;
+	mfd->bl_scale = data->scale;
+	mfd->bl_min_lvl = data->min_lvl;
+	pr_debug("update scale = %d, min_lvl = %d\n", mfd->bl_scale,
+							mfd->bl_min_lvl);
+
+	/* update current backlight to use new scaling*/
+	mdss_fb_set_backlight(mfd, curr_bl);
+	mutex_unlock(&mfd->lock);
+	return ret;
+}
+
+static void mdss_fb_scale_bl(struct msm_fb_data_type *mfd, u32 *bl_lvl)
+{
+	u32 temp = *bl_lvl;
+	pr_debug("input = %d, scale = %d", temp, mfd->bl_scale);
+	if (temp >= mfd->bl_min_lvl) {
+		/* bl_scale is the numerator of scaling fraction (x/1024)*/
+		temp = (temp * mfd->bl_scale) / 1024;
+
+		/*if less than minimum level, use min level*/
+		if (temp < mfd->bl_min_lvl)
+			temp = mfd->bl_min_lvl;
+	}
+	pr_debug("output = %d", temp);
+
+	(*bl_lvl) = temp;
+}
+
+/* must call this function from within mfd->lock */
 void mdss_fb_set_backlight(struct msm_fb_data_type *mfd, u32 bkl_lvl)
 {
 	struct mdss_panel_data *pdata;
+	u32 temp = bkl_lvl;
 
 	if (!mfd->panel_power_on || !bl_updated) {
 		unset_bl_level = bkl_lvl;
@@ -427,15 +512,22 @@
 	pdata = dev_get_platdata(&mfd->pdev->dev);
 
 	if ((pdata) && (pdata->set_backlight)) {
-		mutex_lock(&mfd->lock);
-		if (bl_level_old == bkl_lvl) {
-			mutex_unlock(&mfd->lock);
+		mdss_fb_scale_bl(mfd, &temp);
+		/*
+		 * Even though backlight has been scaled, want to show that
+		 * backlight has been set to bkl_lvl to those that read from
+		 * sysfs node. Thus, need to set bl_level even if it appears
+		 * the backlight has already been set to the level it is at,
+		 * as well as setting bl_level to bkl_lvl even though the
+		 * backlight has been set to the scaled value.
+		 */
+		if (bl_level_old == temp) {
+			mfd->bl_level = bkl_lvl;
 			return;
 		}
+		pdata->set_backlight(pdata, temp);
 		mfd->bl_level = bkl_lvl;
-		pdata->set_backlight(pdata, mfd->bl_level);
-		bl_level_old = mfd->bl_level;
-		mutex_unlock(&mfd->lock);
+		bl_level_old = temp;
 	}
 }
 
@@ -819,6 +911,13 @@
 
 	mfd->op_enable = true;
 
+	mutex_init(&mfd->no_update.lock);
+	init_timer(&mfd->no_update.timer);
+	mfd->no_update.timer.function = mdss_fb_no_update_notify_timer_cb;
+	mfd->no_update.timer.data = (unsigned long)mfd;
+	init_completion(&mfd->update.comp);
+	init_completion(&mfd->no_update.comp);
+
 	if (mfd->lut_update) {
 		ret = fb_alloc_cmap(&fbi->cmap, 256, 0);
 		if (ret)
@@ -1121,7 +1220,8 @@
 	return 0;
 }
 
-static int mdss_fb_handle_pp_ioctl(void __user *argp)
+static int mdss_fb_handle_pp_ioctl(struct msm_fb_data_type *mfd,
+							void __user *argp)
 {
 	int ret;
 	struct msmfb_mdp_pp mdp_pp;
@@ -1176,6 +1276,10 @@
 		ret = mdss_mdp_gamut_config(&mdp_pp.data.gamut_cfg_data,
 				&copyback);
 		break;
+	case mdp_bl_scale_cfg:
+		ret = mdss_bl_scale_config(mfd, (struct mdp_bl_scale_data *)
+						&mdp_pp.data.bl_scale_data);
+		break;
 	default:
 		pr_err("Unsupported request to MDP_PP IOCTL.\n");
 		ret = -EINVAL;
@@ -1253,7 +1357,11 @@
 		break;
 
 	case MSMFB_MDP_PP:
-		ret = mdss_fb_handle_pp_ioctl(argp);
+		ret = mdss_fb_handle_pp_ioctl(mfd, argp);
+		break;
+
+	case MSMFB_NOTIFY_UPDATE:
+		ret = mdss_fb_notify_update(mfd, argp);
 		break;
 
 	default:
diff --git a/drivers/video/msm/mdss/mdss_fb.h b/drivers/video/msm/mdss/mdss_fb.h
index 80ebc4f..78f2b9a 100644
--- a/drivers/video/msm/mdss/mdss_fb.h
+++ b/drivers/video/msm/mdss/mdss_fb.h
@@ -41,6 +41,13 @@
 	int panel_power_on;
 };
 
+struct disp_info_notify {
+	int type;
+	struct timer_list timer;
+	struct completion comp;
+	struct mutex lock;
+};
+
 struct msm_fb_data_type {
 	u32 key;
 	u32 index;
@@ -83,6 +90,8 @@
 	unsigned long cursor_buf_iova;
 
 	u32 bl_level;
+	u32 bl_scale;
+	u32 bl_min_lvl;
 	struct mutex lock;
 
 	struct platform_device *pdev;
@@ -96,6 +105,10 @@
 	struct mdss_mdp_ctl *ctl;
 	struct mdss_mdp_wb *wb;
 	struct list_head overlay_list;
+	struct list_head pipes_used;
+	struct list_head pipes_cleanup;
+	struct disp_info_notify update;
+	struct disp_info_notify no_update;
 };
 
 int mdss_fb_get_phys_info(unsigned long *start, unsigned long *len, int fb_num);
diff --git a/drivers/video/msm/mdss/mdss_hdmi_tx.c b/drivers/video/msm/mdss/mdss_hdmi_tx.c
index 7f41221..d53240f 100644
--- a/drivers/video/msm/mdss/mdss_hdmi_tx.c
+++ b/drivers/video/msm/mdss/mdss_hdmi_tx.c
@@ -18,6 +18,7 @@
 #include <linux/of_address.h>
 #include <linux/of_gpio.h>
 #include <linux/types.h>
+#include <mach/msm_hdmi_audio.h>
 
 #define REG_DUMP 0
 
@@ -40,6 +41,17 @@
 	((d & 0xff) + ((d >> 8) & 0xff) +	\
 	((d >> 16) & 0xff) + ((d >> 24) & 0xff))
 
+/* parameters for clock regeneration */
+struct hdmi_tx_audio_acr {
+	u32 n;
+	u32 cts;
+};
+
+struct hdmi_tx_audio_acr_arry {
+	u32 pclk;
+	struct hdmi_tx_audio_acr lut[HDMI_SAMPLE_RATE_MAX];
+};
+
 static int hdmi_tx_sysfs_enable_hpd(struct hdmi_tx_ctrl *hdmi_ctrl, int on);
 static irqreturn_t hdmi_tx_isr(int irq, void *data);
 
@@ -90,6 +102,27 @@
 	 0x07,	0x07,	0x07,	0x07,	0x02, 0x02, 0x02}  /*12*/
 };
 
+/* Audio constants lookup table for hdmi_tx_audio_acr_setup */
+/* Valid Pixel-Clock rates: 25.2MHz, 27MHz, 27.03MHz, 74.25MHz, 148.5MHz */
+static const struct hdmi_tx_audio_acr_arry hdmi_tx_audio_acr_lut[] = {
+	/*  25.200MHz  */
+	{25200, {{4096, 25200}, {6272, 28000}, {6144, 25200}, {12544, 28000},
+		{12288, 25200}, {25088, 28000}, {24576, 25200} } },
+	/*  27.000MHz  */
+	{27000, {{4096, 27000}, {6272, 30000}, {6144, 27000}, {12544, 30000},
+		{12288, 27000}, {25088, 30000}, {24576, 27000} } },
+	/*  27.027MHz */
+	{27030, {{4096, 27027}, {6272, 30030}, {6144, 27027}, {12544, 30030},
+		{12288, 27027}, {25088, 30030}, {24576, 27027} } },
+	/*  74.250MHz */
+	{74250, {{4096, 74250}, {6272, 82500}, {6144, 74250}, {12544, 82500},
+		{12288, 74250}, {25088, 82500}, {24576, 74250} } },
+	/* 148.500MHz */
+	{148500, {{4096, 148500}, {6272, 165000}, {6144, 148500},
+		{12544, 165000}, {12288, 148500}, {25088, 165000},
+		{24576, 148500} } },
+};
+
 const char *hdmi_tx_pm_name(enum hdmi_tx_power_module_type module)
 {
 	switch (module) {
@@ -335,8 +368,8 @@
 
 static inline u32 hdmi_tx_is_dvi_mode(struct hdmi_tx_ctrl *hdmi_ctrl)
 {
-	struct dss_io_data *io = &hdmi_ctrl->pdata.io[HDMI_TX_CORE_IO];
-	return !(DSS_REG_R_ND(io, HDMI_CTRL) & BIT(1));
+	return hdmi_edid_get_sink_mode(
+		hdmi_ctrl->feature_data[HDMI_TX_FEAT_EDID]) ? 0 : 1;
 } /* hdmi_tx_is_dvi_mode */
 
 static int hdmi_tx_init_panel_info(uint32_t resolution,
@@ -1173,7 +1206,7 @@
 	if (rc) {
 		DEV_ERR("%s: core hdmi_msm_enable_power failed rc = %d\n",
 			__func__, rc);
-		goto disable_hpd_power;
+		return rc;
 	}
 	rc = hdmi_tx_enable_power(hdmi_ctrl, HDMI_TX_CEC_PM, 1);
 	if (rc) {
@@ -1185,8 +1218,6 @@
 	return rc;
 disable_core_power:
 	hdmi_tx_enable_power(hdmi_ctrl, HDMI_TX_CORE_PM, 0);
-disable_hpd_power:
-	hdmi_tx_enable_power(hdmi_ctrl, HDMI_TX_HPD_PM, 0);
 	return rc;
 } /* hdmi_tx_core_on */
 
@@ -1277,6 +1308,335 @@
 	DSS_REG_W_ND(io, HDMI_PHY_PD_CTRL0, 0x7F);
 } /* hdmi_tx_powerdown_phy */
 
+static int hdmi_tx_audio_acr_setup(struct hdmi_tx_ctrl *hdmi_ctrl,
+	bool enabled, int num_of_channels)
+{
+	/* Read first before writing */
+	u32 acr_pck_ctrl_reg;
+	struct dss_io_data *io = NULL;
+
+	if (!hdmi_ctrl) {
+		DEV_ERR("%s: Invalid input\n", __func__);
+		return -EINVAL;
+	}
+
+	io = &hdmi_ctrl->pdata.io[HDMI_TX_CORE_IO];
+	if (!io->base) {
+		DEV_ERR("%s: core io not inititalized\n", __func__);
+		return -EINVAL;
+	}
+
+	acr_pck_ctrl_reg = DSS_REG_R(io, HDMI_ACR_PKT_CTRL);
+
+	if (enabled) {
+		const struct hdmi_disp_mode_timing_type *timing =
+			hdmi_get_supported_mode(hdmi_ctrl->video_resolution);
+		const struct hdmi_tx_audio_acr_arry *audio_acr =
+			&hdmi_tx_audio_acr_lut[0];
+		const int lut_size = sizeof(hdmi_tx_audio_acr_lut)
+			/ sizeof(*hdmi_tx_audio_acr_lut);
+		u32 i, n, cts, layout, multiplier, aud_pck_ctrl_2_reg;
+
+		if (timing == NULL) {
+			DEV_WARN("%s: video format %d not supported\n",
+				__func__, hdmi_ctrl->video_resolution);
+			return -EPERM;
+		}
+
+		for (i = 0; i < lut_size;
+			audio_acr = &hdmi_tx_audio_acr_lut[++i]) {
+			if (audio_acr->pclk == timing->pixel_freq)
+				break;
+		}
+		if (i >= lut_size) {
+			DEV_WARN("%s: pixel clk %d not supported\n", __func__,
+				timing->pixel_freq);
+			return -EPERM;
+		}
+
+		n = audio_acr->lut[hdmi_ctrl->audio_sample_rate].n;
+		cts = audio_acr->lut[hdmi_ctrl->audio_sample_rate].cts;
+		layout = (MSM_HDMI_AUDIO_CHANNEL_2 == num_of_channels) ? 0 : 1;
+
+		if (
+		(HDMI_SAMPLE_RATE_192KHZ == hdmi_ctrl->audio_sample_rate) ||
+		(HDMI_SAMPLE_RATE_176_4KHZ == hdmi_ctrl->audio_sample_rate)) {
+			multiplier = 4;
+			n >>= 2; /* divide N by 4 and use multiplier */
+		} else if (
+		(HDMI_SAMPLE_RATE_96KHZ == hdmi_ctrl->audio_sample_rate) ||
+		(HDMI_SAMPLE_RATE_88_2KHZ == hdmi_ctrl->audio_sample_rate)) {
+			multiplier = 2;
+			n >>= 1; /* divide N by 2 and use multiplier */
+		} else {
+			multiplier = 1;
+		}
+		DEV_DBG("%s: n=%u, cts=%u, layout=%u\n", __func__, n, cts,
+			layout);
+
+		/* AUDIO_PRIORITY | SOURCE */
+		acr_pck_ctrl_reg |= 0x80000100;
+		/* N_MULTIPLE(multiplier) */
+		acr_pck_ctrl_reg |= (multiplier & 7) << 16;
+
+		if ((HDMI_SAMPLE_RATE_48KHZ == hdmi_ctrl->audio_sample_rate) ||
+		(HDMI_SAMPLE_RATE_96KHZ == hdmi_ctrl->audio_sample_rate) ||
+		(HDMI_SAMPLE_RATE_192KHZ == hdmi_ctrl->audio_sample_rate)) {
+			/* SELECT(3) */
+			acr_pck_ctrl_reg |= 3 << 4;
+			/* CTS_48 */
+			cts <<= 12;
+
+			/* CTS: need to determine how many fractional bits */
+			DSS_REG_W(io, HDMI_ACR_48_0, cts);
+			/* N */
+			DSS_REG_W(io, HDMI_ACR_48_1, n);
+		} else if (
+		(HDMI_SAMPLE_RATE_44_1KHZ == hdmi_ctrl->audio_sample_rate) ||
+		(HDMI_SAMPLE_RATE_88_2KHZ == hdmi_ctrl->audio_sample_rate) ||
+		(HDMI_SAMPLE_RATE_176_4KHZ == hdmi_ctrl->audio_sample_rate)) {
+			/* SELECT(2) */
+			acr_pck_ctrl_reg |= 2 << 4;
+			/* CTS_44 */
+			cts <<= 12;
+
+			/* CTS: need to determine how many fractional bits */
+			DSS_REG_W(io, HDMI_ACR_44_0, cts);
+			/* N */
+			DSS_REG_W(io, HDMI_ACR_44_1, n);
+		} else {	/* default to 32k */
+			/* SELECT(1) */
+			acr_pck_ctrl_reg |= 1 << 4;
+			/* CTS_32 */
+			cts <<= 12;
+
+			/* CTS: need to determine how many fractional bits */
+			DSS_REG_W(io, HDMI_ACR_32_0, cts);
+			/* N */
+			DSS_REG_W(io, HDMI_ACR_32_1, n);
+		}
+		/* Payload layout depends on number of audio channels */
+		/* LAYOUT_SEL(layout) */
+		aud_pck_ctrl_2_reg = 1 | (layout << 1);
+		/* override | layout */
+		DSS_REG_W(io, HDMI_AUDIO_PKT_CTRL2, aud_pck_ctrl_2_reg);
+
+		/* SEND | CONT */
+		acr_pck_ctrl_reg |= 0x00000003;
+	} else {
+		/* ~(SEND | CONT) */
+		acr_pck_ctrl_reg &= ~0x00000003;
+	}
+	DSS_REG_W(io, HDMI_ACR_PKT_CTRL, acr_pck_ctrl_reg);
+
+	return 0;
+} /* hdmi_tx_audio_acr_setup */
+
+static int hdmi_tx_audio_info_setup(void *priv_d, bool enabled,
+	u32 num_of_channels, u32 channel_allocation, u32 level_shift,
+	bool down_mix)
+{
+	struct hdmi_tx_ctrl *hdmi_ctrl = (struct hdmi_tx_ctrl *)priv_d;
+	struct dss_io_data *io = NULL;
+
+	u32 channel_count = 1; /* Def to 2 channels -> Table 17 in CEA-D */
+	u32 check_sum, audio_info_0_reg, audio_info_1_reg;
+	u32 audio_info_ctrl_reg;
+	u32 aud_pck_ctrl_2_reg;
+	u32 layout;
+
+	if (!hdmi_ctrl) {
+		DEV_ERR("%s: invalid input\n", __func__);
+		return -EINVAL;
+	}
+
+	io = &hdmi_ctrl->pdata.io[HDMI_TX_CORE_IO];
+	if (!io->base) {
+		DEV_ERR("%s: core io not inititalized\n", __func__);
+		return -EINVAL;
+	}
+
+	layout = (MSM_HDMI_AUDIO_CHANNEL_2 == num_of_channels) ? 0 : 1;
+	aud_pck_ctrl_2_reg = 1 | (layout << 1);
+	DSS_REG_W(io, HDMI_AUDIO_PKT_CTRL2, aud_pck_ctrl_2_reg);
+
+	/*
+	 * Please see table 20 Audio InfoFrame in HDMI spec
+	 * FL  = front left
+	 * FC  = front Center
+	 * FR  = front right
+	 * FLC = front left center
+	 * FRC = front right center
+	 * RL  = rear left
+	 * RC  = rear center
+	 * RR  = rear right
+	 * RLC = rear left center
+	 * RRC = rear right center
+	 * LFE = low frequency effect
+	 */
+
+	/* Read first then write because it is bundled with other controls */
+	audio_info_ctrl_reg = DSS_REG_R(io, HDMI_INFOFRAME_CTRL0);
+
+	if (enabled) {
+		switch (num_of_channels) {
+		case MSM_HDMI_AUDIO_CHANNEL_2:
+			channel_allocation = 0;	/* Default to FR, FL */
+			break;
+		case MSM_HDMI_AUDIO_CHANNEL_4:
+			channel_count = 3;
+			/* FC, LFE, FR, FL */
+			channel_allocation = 0x3;
+			break;
+		case MSM_HDMI_AUDIO_CHANNEL_6:
+			channel_count = 5;
+			/* RR, RL, FC, LFE, FR, FL */
+			channel_allocation = 0xB;
+			break;
+		case MSM_HDMI_AUDIO_CHANNEL_8:
+			channel_count = 7;
+			/* FRC, FLC, RR, RL, FC, LFE, FR, FL */
+			channel_allocation = 0x1f;
+			break;
+		default:
+			DEV_ERR("%s: Unsupported num_of_channels = %u\n",
+				__func__, num_of_channels);
+			return -EINVAL;
+		}
+
+		/* Program the Channel-Speaker allocation */
+		audio_info_1_reg = 0;
+		/* CA(channel_allocation) */
+		audio_info_1_reg |= channel_allocation & 0xff;
+		/* Program the Level shifter */
+		audio_info_1_reg |= (level_shift << 11) & 0x00007800;
+		/* Program the Down-mix Inhibit Flag */
+		audio_info_1_reg |= (down_mix << 15) & 0x00008000;
+
+		DSS_REG_W(io, HDMI_AUDIO_INFO1, audio_info_1_reg);
+
+		/*
+		 * Calculate CheckSum: Sum of all the bytes in the
+		 * Audio Info Packet (See table 8.4 in HDMI spec)
+		 */
+		check_sum = 0;
+		/* HDMI_AUDIO_INFO_FRAME_PACKET_HEADER_TYPE[0x84] */
+		check_sum += 0x84;
+		/* HDMI_AUDIO_INFO_FRAME_PACKET_HEADER_VERSION[0x01] */
+		check_sum += 1;
+		/* HDMI_AUDIO_INFO_FRAME_PACKET_LENGTH[0x0A] */
+		check_sum += 0x0A;
+		check_sum += channel_count;
+		check_sum += channel_allocation;
+		/* See Table 8.5 in HDMI spec */
+		check_sum += (level_shift & 0xF) << 3 | (down_mix & 0x1) << 7;
+		check_sum &= 0xFF;
+		check_sum = (u8) (256 - check_sum);
+
+		audio_info_0_reg = 0;
+		/* CHECKSUM(check_sum) */
+		audio_info_0_reg |= check_sum & 0xff;
+		/* CC(channel_count) */
+		audio_info_0_reg |= (channel_count << 8) & 0x00000700;
+
+		DSS_REG_W(io, HDMI_AUDIO_INFO0, audio_info_0_reg);
+
+		/*
+		 * Set these flags
+		 * AUDIO_INFO_UPDATE |
+		 * AUDIO_INFO_SOURCE |
+		 * AUDIO_INFO_CONT   |
+		 * AUDIO_INFO_SEND
+		 */
+		audio_info_ctrl_reg |= 0x000000F0;
+	} else {
+		/*Clear these flags
+		 * ~(AUDIO_INFO_UPDATE |
+		 *   AUDIO_INFO_SOURCE |
+		 *   AUDIO_INFO_CONT   |
+		 *   AUDIO_INFO_SEND)
+		 */
+		audio_info_ctrl_reg &= ~0x000000F0;
+	}
+	DSS_REG_W(io, HDMI_INFOFRAME_CTRL0, audio_info_ctrl_reg);
+
+	dss_reg_dump(io->base, io->len, "HDMI-AUDIO-ON: ", REG_DUMP);
+
+	return 0;
+} /* hdmi_tx_audio_info_setup */
+
+static int hdmi_tx_audio_setup(struct hdmi_tx_ctrl *hdmi_ctrl)
+{
+	int rc = 0;
+	const int channels = MSM_HDMI_AUDIO_CHANNEL_2;
+	struct dss_io_data *io = NULL;
+
+	if (!hdmi_ctrl) {
+		DEV_ERR("%s: invalid input\n", __func__);
+		return -EINVAL;
+	}
+
+	io = &hdmi_ctrl->pdata.io[HDMI_TX_CORE_IO];
+	if (!io->base) {
+		DEV_ERR("%s: core io not inititalized\n", __func__);
+		return -EINVAL;
+	}
+
+	rc = hdmi_tx_audio_acr_setup(hdmi_ctrl, true, channels);
+	if (rc) {
+		DEV_ERR("%s: hdmi_tx_audio_acr_setup failed. rc=%d\n",
+			__func__, rc);
+		return rc;
+	}
+
+	rc = hdmi_tx_audio_info_setup(hdmi_ctrl, true, channels, 0, 0, false);
+	if (rc) {
+		DEV_ERR("%s: hdmi_tx_audio_info_setup failed. rc=%d\n",
+			__func__, rc);
+		return rc;
+	}
+
+	DEV_INFO("HDMI Audio: Enabled\n");
+
+	return 0;
+} /* hdmi_tx_audio_setup */
+
+static void hdmi_tx_audio_off(struct hdmi_tx_ctrl *hdmi_ctrl)
+{
+	int i;
+	u32 audio_pkt_ctrl, audio_cfg;
+	struct dss_io_data *io = NULL;
+
+	if (!hdmi_ctrl) {
+		DEV_ERR("%s: invalid input\n", __func__);
+		return;
+	}
+
+	io = &hdmi_ctrl->pdata.io[HDMI_TX_CORE_IO];
+	if (!io->base) {
+		DEV_ERR("%s: core io not inititalized\n", __func__);
+		return;
+	}
+
+	/* Number of wait iterations */
+	i = 10;
+	do {
+		audio_pkt_ctrl = DSS_REG_R_ND(io, HDMI_AUDIO_PKT_CTRL);
+		audio_cfg = DSS_REG_R_ND(io, HDMI_AUDIO_CFG);
+		DEV_DBG("%s: i=%d, AUDIO PACKET=%08x, AUDIO CFG=%08x",
+			__func__, i, audio_pkt_ctrl, audio_cfg);
+		msleep(20);
+	} while (((audio_pkt_ctrl & BIT(0)) || (audio_cfg & BIT(0))) && i--);
+
+	if (hdmi_tx_audio_info_setup(hdmi_ctrl, false, 0, 0, 0, false))
+		DEV_ERR("%s: hdmi_tx_audio_info_setup failed.\n", __func__);
+
+	if (hdmi_tx_audio_acr_setup(hdmi_ctrl, false, 0))
+		DEV_ERR("%s: hdmi_tx_audio_acr_setup failed.\n", __func__);
+
+	DEV_INFO("HDMI Audio: Disabled\n");
+} /* hdmi_tx_audio_off */
+
 static int hdmi_tx_start(struct hdmi_tx_ctrl *hdmi_ctrl)
 {
 	int rc = 0;
@@ -1292,8 +1652,6 @@
 		return -EINVAL;
 	}
 
-	/* todo: Audio */
-
 	hdmi_tx_set_mode(hdmi_ctrl, false);
 	hdmi_tx_init_phy(hdmi_ctrl);
 	DSS_REG_W(io, HDMI_USEC_REFTIMER, 0x0001001B);
@@ -1301,7 +1659,21 @@
 	hdmi_tx_set_mode(hdmi_ctrl, true);
 
 	hdmi_tx_video_setup(hdmi_ctrl, hdmi_ctrl->video_resolution);
-	/* todo: Audio */
+
+	if (!hdmi_tx_is_dvi_mode(hdmi_ctrl)) {
+		rc = hdmi_tx_audio_setup(hdmi_ctrl);
+		if (rc) {
+			DEV_ERR("%s: hdmi_msm_audio_setup failed. rc=%d\n",
+				__func__, rc);
+			hdmi_tx_set_mode(hdmi_ctrl, false);
+			return rc;
+		}
+
+		switch_set_state(&hdmi_ctrl->audio_sdev, 1);
+		DEV_INFO("%s: hdmi_audio state switch to %d\n", __func__,
+			hdmi_ctrl->audio_sdev.state);
+	}
+
 	hdmi_tx_set_avi_infoframe(hdmi_ctrl);
 	/* todo: CONFIG_FB_MSM_HDMI_3D */
 	hdmi_tx_set_spd_infoframe(hdmi_ctrl);
@@ -1326,8 +1698,16 @@
 		return -EINVAL;
 	}
 
-	DEV_INFO("%s: power: OFF (audio off, Reset Core)\n", __func__);
-	/* todo: Audio */
+	DEV_INFO("%s: HDMI Core: OFF\n", __func__);
+
+	if (!hdmi_tx_is_dvi_mode(hdmi_ctrl)) {
+		switch_set_state(&hdmi_ctrl->audio_sdev, 0);
+		DEV_INFO("%s: hdmi_audio state switch to %d\n", __func__,
+			hdmi_ctrl->audio_sdev.state);
+
+		hdmi_tx_audio_off(hdmi_ctrl);
+	}
+
 	hdmi_tx_powerdown_phy(hdmi_ctrl);
 	hdmi_ctrl->panel_power_on = false;
 	hdmi_tx_core_off(hdmi_ctrl);
@@ -1548,6 +1928,7 @@
 	if (hdmi_ctrl->feature_data[HDMI_TX_FEAT_EDID])
 		hdmi_edid_deinit(hdmi_ctrl->feature_data[HDMI_TX_FEAT_EDID]);
 
+	switch_dev_unregister(&hdmi_ctrl->audio_sdev);
 	switch_dev_unregister(&hdmi_ctrl->sdev);
 	del_timer_sync(&hdmi_ctrl->hpd_state_timer);
 	if (hdmi_ctrl->workq)
@@ -1583,6 +1964,7 @@
 	hdmi_ctrl->workq = create_workqueue("hdmi_tx_workq");
 	if (!hdmi_ctrl->workq) {
 		DEV_ERR("%s: hdmi_tx_workq creation failed.\n", __func__);
+		rc = -EPERM;
 		goto fail_create_workq;
 	}
 
@@ -1596,14 +1978,27 @@
 	hdmi_ctrl->hpd_state_timer.data = (u32)hdmi_ctrl;
 	hdmi_ctrl->hpd_state_timer.expires = 0xffffffffL;
 
+	hdmi_ctrl->audio_sample_rate = HDMI_SAMPLE_RATE_48KHZ;
+
 	hdmi_ctrl->sdev.name = "hdmi";
 	if (switch_dev_register(&hdmi_ctrl->sdev) < 0) {
 		DEV_ERR("%s: Hdmi switch registration failed\n", __func__);
+		rc = -ENODEV;
 		goto fail_switch_dev;
 	}
 
+	hdmi_ctrl->audio_sdev.name = "hdmi_audio";
+	if (switch_dev_register(&hdmi_ctrl->audio_sdev) < 0) {
+		DEV_ERR("%s: hdmi_audio switch registration failed\n",
+			__func__);
+		rc = -ENODEV;
+		goto fail_audio_switch_dev;
+	}
+
 	return 0;
 
+fail_audio_switch_dev:
+	switch_dev_unregister(&hdmi_ctrl->sdev);
 fail_switch_dev:
 	del_timer_sync(&hdmi_ctrl->hpd_state_timer);
 fail_create_workq:
diff --git a/drivers/video/msm/mdss/mdss_hdmi_tx.h b/drivers/video/msm/mdss/mdss_hdmi_tx.h
index 94e0fda..437f681 100644
--- a/drivers/video/msm/mdss/mdss_hdmi_tx.h
+++ b/drivers/video/msm/mdss/mdss_hdmi_tx.h
@@ -41,9 +41,12 @@
 	struct hdmi_tx_platform_data pdata;
 	struct mdss_panel_data panel_data;
 
+	int audio_sample_rate;
+
 	struct mutex mutex;
 	struct kobject *kobj;
 	struct switch_dev sdev;
+	struct switch_dev audio_sdev;
 	struct workqueue_struct *workq;
 
 	uint32_t video_resolution;
diff --git a/drivers/video/msm/mdss/mdss_mdp.c b/drivers/video/msm/mdss/mdss_mdp.c
index 0f6cfe9..c2d107a 100644
--- a/drivers/video/msm/mdss/mdss_mdp.c
+++ b/drivers/video/msm/mdss/mdss_mdp.c
@@ -320,7 +320,7 @@
 		vect->ab = ab_quota;
 		vect->ib = ib_quota;
 
-		pr_debug("bus scale idx=%d ab=%u ib=%u\n", bus_idx,
+		pr_debug("bus scale idx=%d ab=%llu ib=%llu\n", bus_idx,
 				vect->ab, vect->ib);
 	}
 	current_bus_idx = bus_idx;
diff --git a/drivers/video/msm/mdss/mdss_mdp.h b/drivers/video/msm/mdss/mdss_mdp.h
index 6fd642f..33028cb 100644
--- a/drivers/video/msm/mdss/mdss_mdp.h
+++ b/drivers/video/msm/mdss/mdss_mdp.h
@@ -241,7 +241,9 @@
 	unsigned long smp[MAX_PLANES];
 
 	struct mdss_mdp_data buffers[2];
-	struct list_head list;
+	struct list_head used_list;
+	struct list_head cleanup_list;
+
 	struct mdp_overlay_pp_params pp_cfg;
 };
 
diff --git a/drivers/video/msm/mdss/mdss_mdp_ctl.c b/drivers/video/msm/mdss/mdss_mdp_ctl.c
index 5966989..73b8c60 100644
--- a/drivers/video/msm/mdss/mdss_mdp_ctl.c
+++ b/drivers/video/msm/mdss/mdss_mdp_ctl.c
@@ -90,18 +90,34 @@
 	*bus_ib_quota = 0;
 	*clk_rate = 0;
 
-	if (mixer->type == MDSS_MDP_MIXER_TYPE_INTF) {
-		struct mdss_panel_info *pinfo = &mixer->ctl->mfd->panel_info;
-		v_total = (pinfo->yres + pinfo->lcdc.v_back_porch +
-			pinfo->lcdc.v_front_porch + pinfo->lcdc.v_pulse_width);
-		v_active = pinfo->yres;
-	} else if (mixer->rotator_mode) {
+	if (mixer->rotator_mode) {
 		pipe = mixer->stage_pipe[0]; /* rotator pipe */
 		v_total = pipe->flags & MDP_ROT_90 ? pipe->dst.w : pipe->dst.h;
 		v_active = v_total;
 	} else {
-		v_total = mixer->height;
-		v_active = v_total;
+		int is_writeback = false;
+		if (mixer->type == MDSS_MDP_MIXER_TYPE_INTF) {
+			struct mdss_panel_info *pinfo;
+			pinfo = &mixer->ctl->mfd->panel_info;
+			v_total = (pinfo->yres + pinfo->lcdc.v_back_porch +
+				   pinfo->lcdc.v_front_porch +
+				   pinfo->lcdc.v_pulse_width);
+			v_active = pinfo->yres;
+
+			if (pinfo->type == WRITEBACK_PANEL)
+				is_writeback = true;
+		} else {
+			v_total = mixer->height;
+			v_active = v_total;
+
+			is_writeback = true;
+		}
+		*clk_rate = mixer->width * v_total * fps;
+		if (is_writeback) {
+			/* perf for bus writeback */
+			*bus_ab_quota = fps * mixer->width * mixer->height * 3;
+			*bus_ib_quota = *bus_ab_quota;
+		}
 	}
 
 	for (i = 0; i < MDSS_MDP_MAX_STAGE; i++) {
@@ -118,7 +134,7 @@
 
 		if (mixer->type == MDSS_MDP_MIXER_TYPE_INTF)
 			quota = (quota / v_active) * v_total;
-		else
+		else if (mixer->rotator_mode)
 			quota *= 2; /* bus read + write */
 
 		rate = pipe->dst.w;
@@ -344,6 +360,8 @@
 	mdss_mdp_mixer_free(mixer);
 	mdss_mdp_ctl_free(ctl);
 
+	mdss_mdp_ctl_perf_commit(MDSS_MDP_PERF_UPDATE_ALL);
+
 	return 0;
 }
 
@@ -378,7 +396,6 @@
 
 	ctl->width = width;
 	ctl->height = height;
-	ctl->dst_format = mfd->panel_info.out_format;
 
 	if (!ctl->mixer_left) {
 		ctl->mixer_left =
@@ -449,6 +466,20 @@
 
 	ctl->opmode |= (ctl->intf_num << 4);
 
+	if (ctl->intf_num == MDSS_MDP_NO_INTF) {
+		ctl->dst_format = mfd->panel_info.out_format;
+	} else {
+		switch (mfd->panel_info.bpp) {
+		case 18:
+			ctl->dst_format = MDSS_MDP_PANEL_FORMAT_RGB666;
+			break;
+		case 24:
+		default:
+			ctl->dst_format = MDSS_MDP_PANEL_FORMAT_RGB888;
+			break;
+		}
+	}
+
 	if (ctl->mixer_right) {
 		ctl->opmode |= MDSS_MDP_CTL_OP_PACK_3D_ENABLE |
 			       MDSS_MDP_CTL_OP_PACK_3D_H_ROW_INT;
@@ -542,6 +573,12 @@
 	temp |= (ctl->intf_type << ((ctl->intf_num - MDSS_MDP_INTF0) * 8));
 	MDSS_MDP_REG_WRITE(MDSS_MDP_REG_DISP_INTF_SEL, temp);
 
+	if (ctl->intf_num != MDSS_MDP_NO_INTF) {
+		off = MDSS_MDP_REG_INTF_OFFSET(ctl->intf_num);
+		MDSS_MDP_REG_WRITE(off + MDSS_MDP_REG_INTF_PANEL_FORMAT,
+				   ctl->dst_format);
+	}
+
 	outsize = (mixer->height << 16) | mixer->width;
 	off = MDSS_MDP_REG_LM_OFFSET(mixer->num);
 	MDSS_MDP_REG_WRITE(off + MDSS_MDP_REG_LM_OUT_SIZE, outsize);
diff --git a/drivers/video/msm/mdss/mdss_mdp_hwio.h b/drivers/video/msm/mdss/mdss_mdp_hwio.h
index 651d595..1da30b8 100644
--- a/drivers/video/msm/mdss/mdss_mdp_hwio.h
+++ b/drivers/video/msm/mdss/mdss_mdp_hwio.h
@@ -395,6 +395,7 @@
 #define MDSS_MDP_REG_INTF_FRAME_COUNT			0x0AC
 #define MDSS_MDP_REG_INTF_LINE_COUNT			0x0B0
 #define MDSS_MDP_PANEL_FORMAT_RGB888			0x213F
+#define MDSS_MDP_PANEL_FORMAT_RGB666			0x212A
 
 enum mdss_mdp_pingpong_index {
 	MDSS_MDP_PINGPONG0,
diff --git a/drivers/video/msm/mdss/mdss_mdp_intf_video.c b/drivers/video/msm/mdss/mdss_mdp_intf_video.c
index 3c7c362..4d3fbf0 100644
--- a/drivers/video/msm/mdss/mdss_mdp_intf_video.c
+++ b/drivers/video/msm/mdss/mdss_mdp_intf_video.c
@@ -153,8 +153,6 @@
 			   p->hsync_skew);
 	MDSS_MDP_REG_WRITE(off + MDSS_MDP_REG_INTF_POLARITY_CTL,
 			   polarity_ctl);
-	MDSS_MDP_REG_WRITE(off + MDSS_MDP_REG_INTF_PANEL_FORMAT,
-			   MDSS_MDP_PANEL_FORMAT_RGB888);
 
 	return 0;
 }
diff --git a/drivers/video/msm/mdss/mdss_mdp_intf_writeback.c b/drivers/video/msm/mdss/mdss_mdp_intf_writeback.c
index 8b4434e..c9acc65 100644
--- a/drivers/video/msm/mdss/mdss_mdp_intf_writeback.c
+++ b/drivers/video/msm/mdss/mdss_mdp_intf_writeback.c
@@ -150,8 +150,11 @@
 		     (fmt->bits[C1_B_Cb] << 2) |
 		     (fmt->bits[C0_G_Y] << 0);
 
-	if (fmt->alpha_enable)
+	if (fmt->bits[C3_ALPHA] || fmt->alpha_enable) {
 		dst_format |= BIT(8); /* DSTC3_EN */
+		if (!fmt->alpha_enable)
+			dst_format |= BIT(14); /* DST_ALPHA_X */
+	}
 
 	if (fmt->fetch_planes != MDSS_MDP_PLANE_PLANAR) {
 		pattern = (fmt->element[3] << 24) | (fmt->element[2] << 15) |
diff --git a/drivers/video/msm/mdss/mdss_mdp_overlay.c b/drivers/video/msm/mdss/mdss_mdp_overlay.c
index f76b508..d52df66 100644
--- a/drivers/video/msm/mdss/mdss_mdp_overlay.c
+++ b/drivers/video/msm/mdss/mdss_mdp_overlay.c
@@ -282,7 +282,7 @@
 		}
 
 		mutex_lock(&mfd->lock);
-		list_add(&pipe->list, &mfd->overlay_list);
+		list_add(&pipe->used_list, &mfd->pipes_used);
 		mutex_unlock(&mfd->lock);
 		pipe->mixer = mixer;
 		pipe->mfd = mfd;
@@ -395,24 +395,40 @@
 
 static int mdss_mdp_overlay_kickoff(struct mdss_mdp_ctl *ctl)
 {
-	int ret;
+	struct mdss_mdp_pipe *pipe, *tmp;
+	struct msm_fb_data_type *mfd = ctl->mfd;
+	int i, ret;
 
-	if (ctl->mfd->kickoff_fnc)
-		ret = ctl->mfd->kickoff_fnc(ctl);
+	if (mfd->kickoff_fnc)
+		ret = mfd->kickoff_fnc(ctl);
 	else
 		ret = mdss_mdp_display_commit(ctl, NULL);
 	if (IS_ERR_VALUE(ret))
 		return ret;
 
-	pr_debug("freeing previous buffers\n");
+	complete(&mfd->update.comp);
+	mutex_lock(&mfd->no_update.lock);
+	if (mfd->no_update.timer.function)
+		del_timer(&(mfd->no_update.timer));
 
-	mutex_lock(&ctl->mfd->lock);
-	if (!list_empty(&ctl->mfd->overlay_list)) {
-		struct mdss_mdp_pipe *pipe;
+	mfd->no_update.timer.expires = jiffies + (2 * HZ);
+	add_timer(&mfd->no_update.timer);
+	mutex_unlock(&mfd->no_update.lock);
+
+	mutex_lock(&mfd->lock);
+	list_for_each_entry_safe(pipe, tmp, &mfd->pipes_cleanup, cleanup_list) {
+		list_del(&pipe->cleanup_list);
+		for (i = 0; i < ARRAY_SIZE(pipe->buffers); i++)
+			mdss_mdp_overlay_free_buf(&pipe->buffers[i]);
+
+		mdss_mdp_pipe_destroy(pipe);
+	}
+
+	if (!list_empty(&mfd->pipes_used)) {
 		struct mdss_mdp_data *data;
 		int buf_ndx;
 
-		list_for_each_entry(pipe, &ctl->mfd->overlay_list, list) {
+		list_for_each_entry(pipe, &mfd->pipes_used, used_list) {
 			buf_ndx = (pipe->play_cnt - 1) & 1; /* prev buffer */
 			data = &pipe->buffers[buf_ndx];
 
@@ -423,9 +439,7 @@
 			}
 		}
 	}
-	mutex_unlock(&ctl->mfd->lock);
-
-	pr_debug("done freeing previous buffers\n");
+	mutex_unlock(&mfd->lock);
 
 	return ret;
 }
@@ -433,8 +447,7 @@
 static int mdss_mdp_overlay_unset(struct msm_fb_data_type *mfd, int ndx)
 {
 	struct mdss_mdp_pipe *pipe;
-	struct mdss_mdp_pipe *cleanup_pipes[MDSS_MDP_MAX_SSPP];
-	int i, ret = 0, clean_cnt = 0;
+	int i, ret = 0;
 	u32 pipe_ndx, unset_ndx = 0;
 
 	if (!mfd || !mfd->ctl)
@@ -460,28 +473,15 @@
 		if (pipe_ndx & ndx) {
 			unset_ndx |= pipe_ndx;
 			pipe = mdss_mdp_pipe_get_locked(pipe_ndx);
-			if (pipe) {
-				mutex_lock(&mfd->lock);
-				list_del(&pipe->list);
-				mutex_unlock(&mfd->lock);
-				mdss_mdp_mixer_pipe_unstage(pipe);
-				cleanup_pipes[clean_cnt++] = pipe;
-			} else {
+			if (!pipe) {
 				pr_warn("unknown pipe ndx=%x\n", pipe_ndx);
+				continue;
 			}
-		}
-	}
-
-	if (clean_cnt) {
-		int j;
-		ret = mdss_mdp_overlay_kickoff(mfd->ctl);
-
-		for (i = 0; i < clean_cnt; i++) {
-			pipe = cleanup_pipes[i];
-			for (j = 0; j < ARRAY_SIZE(pipe->buffers); j++)
-				mdss_mdp_overlay_free_buf(&pipe->buffers[i]);
-
-			mdss_mdp_pipe_destroy(pipe);
+			mutex_lock(&mfd->lock);
+			list_del(&pipe->used_list);
+			list_add(&pipe->cleanup_list, &mfd->pipes_cleanup);
+			mutex_unlock(&mfd->lock);
+			mdss_mdp_mixer_pipe_unstage(pipe);
 		}
 	}
 
@@ -495,8 +495,8 @@
 	int cnt = 0;
 
 	mutex_lock(&mfd->lock);
-	if (!list_empty(&mfd->overlay_list)) {
-		list_for_each_entry(pipe, &mfd->overlay_list, list) {
+	if (!list_empty(&mfd->pipes_used)) {
+		list_for_each_entry(pipe, &mfd->pipes_used, used_list) {
 			if (pipe->ndx & MDSS_MDP_ROT_SESSION_MASK) {
 				struct mdss_mdp_rotator_session *rot;
 				rot = mdss_mdp_rotator_session_get(pipe->ndx);
@@ -513,6 +513,7 @@
 	if (unset_ndx) {
 		pr_debug("%d pipes need cleanup (%x)\n", cnt, unset_ndx);
 		mdss_mdp_overlay_unset(mfd, unset_ndx);
+		mdss_mdp_overlay_kickoff(mfd->ctl);
 	}
 
 	return 0;
@@ -603,7 +604,7 @@
 	ctl = pipe->mixer->ctl;
 	mdss_mdp_pipe_unlock(pipe);
 
-	if (ret == 0 && !(pipe->flags & MDP_OV_PLAY_NOWAIT))
+	if ((ret == 0) && (mfd->panel_info.type == WRITEBACK_PANEL))
 		ret = mdss_mdp_overlay_kickoff(ctl);
 
 	return ret;
@@ -1039,7 +1040,9 @@
 			ret = -EFAULT;
 		}
 		break;
-
+	case MSMFB_OVERLAY_COMMIT:
+		ret = mdss_mdp_overlay_kickoff(mfd->ctl);
+		break;
 	default:
 		if (mfd->panel_info.type == WRITEBACK_PANEL)
 			ret = mdss_mdp_wb_ioctl_handler(mfd, cmd, argp);
@@ -1063,7 +1066,8 @@
 	if (mfd->panel_info.type == WRITEBACK_PANEL)
 		mfd->kickoff_fnc = mdss_mdp_wb_kickoff;
 
-	INIT_LIST_HEAD(&mfd->overlay_list);
+	INIT_LIST_HEAD(&mfd->pipes_used);
+	INIT_LIST_HEAD(&mfd->pipes_cleanup);
 
 	return 0;
 }
diff --git a/drivers/video/msm/mdss/mdss_mdp_pp.c b/drivers/video/msm/mdss/mdss_mdp_pp.c
index 6e04124..e4be407 100644
--- a/drivers/video/msm/mdss/mdss_mdp_pp.c
+++ b/drivers/video/msm/mdss/mdss_mdp_pp.c
@@ -99,6 +99,7 @@
 	u32 hist_cnt_read;
 	u32 hist_cnt_sent;
 	u32 frame_cnt;
+	u32 is_kick_ready;
 	struct completion comp;
 	u32 data[HIST_V_SIZE];
 };
@@ -365,6 +366,7 @@
 	struct pp_hist_col_info *hist_info;
 	struct pp_sts_type *pp_sts;
 	u32 data, tbl_idx, col_state;
+	unsigned long flag;
 	int i;
 	dspp_num = mixer->num;
 	/* no corresponding dspp */
@@ -377,20 +379,19 @@
 		/* HIST_EN & AUTO_CLEAR */
 		opmode |= (1 << 16) | (1 << 17);
 		mutex_lock(&mdss_mdp_hist_mutex);
-		if (hist_info->col_state == HIST_READY)
-			pp_hist_read(base + MDSS_MDP_REG_DSPP_HIST_CTL_BASE +
-				0x1C, hist_info);
-		spin_lock(&mdss_hist_lock);
+		spin_lock_irqsave(&mdss_hist_lock, flag);
 		col_state = hist_info->col_state;
-		if ((col_state == HIST_IDLE) ||
-			(col_state == HIST_READY) ||
-			(col_state == HIST_START)) {
+		if (hist_info->is_kick_ready &&
+				((col_state == HIST_IDLE) ||
+				((false == hist_info->read_request) &&
+						col_state == HIST_READY))) {
 			/* Kick off collection */
 			MDSS_MDP_REG_WRITE(base +
 				MDSS_MDP_REG_DSPP_HIST_CTL_BASE, 1);
 			hist_info->col_state = HIST_START;
 		}
-		spin_unlock(&mdss_hist_lock);
+		hist_info->is_kick_ready = true;
+		spin_unlock_irqrestore(&mdss_hist_lock, flag);
 		mutex_unlock(&mdss_mdp_hist_mutex);
 	}
 
@@ -1263,6 +1264,7 @@
 	int i, ret = 0;
 	u32 disp_num, dspp_num = 0;
 	u32 mixer_cnt, mixer_id[MDSS_MDP_MAX_LAYERMIXER];
+	unsigned long flag;
 
 	if ((req->block < MDP_LOGICAL_BLOCK_DISP_0) ||
 		(req->block >= MDP_BLOCK_MAX))
@@ -1296,7 +1298,7 @@
 				__func__, dspp_num);
 			goto hist_start_exit;
 		}
-		spin_lock(&mdss_hist_lock);
+		spin_lock_irqsave(&mdss_hist_lock, flag);
 		hist_info->frame_cnt = req->frame_cnt;
 		init_completion(&hist_info->comp);
 		hist_info->hist_cnt_read = 0;
@@ -1304,7 +1306,8 @@
 		hist_info->read_request = false;
 		hist_info->col_state = HIST_RESET;
 		hist_info->col_en = true;
-		spin_unlock(&mdss_hist_lock);
+		hist_info->is_kick_ready = false;
+		spin_unlock_irqrestore(&mdss_hist_lock, flag);
 		mdss_pp_res->hist_col[disp_num][i] =
 			&mdss_pp_res->dspp_hist[dspp_num];
 		mdss_mdp_hist_irq_enable(3 << done_shift_bit);
@@ -1329,6 +1332,7 @@
 	u32 dspp_num, disp_num, ctl_base, done_bit;
 	struct pp_hist_col_info *hist_info;
 	u32 mixer_cnt, mixer_id[MDSS_MDP_MAX_LAYERMIXER];
+	unsigned long flag;
 
 	if ((block < MDP_LOGICAL_BLOCK_DISP_0) ||
 		(block >= MDP_BLOCK_MAX))
@@ -1362,10 +1366,11 @@
 			goto hist_stop_exit;
 		}
 		complete_all(&hist_info->comp);
-		spin_lock(&mdss_hist_lock);
+		spin_lock_irqsave(&mdss_hist_lock, flag);
 		hist_info->col_en = false;
 		hist_info->col_state = HIST_UNKNOWN;
-		spin_unlock(&mdss_hist_lock);
+		hist_info->is_kick_ready = false;
+		spin_unlock_irqrestore(&mdss_hist_lock, flag);
 		mdss_mdp_hist_irq_disable(done_bit);
 		MDSS_MDP_REG_WRITE(ctl_base, (1 << 1));/* cancel */
 	}
@@ -1386,6 +1391,7 @@
 	struct pp_hist_col_info *hist_info;
 	u32 dspp_num, disp_num, ctl_base;
 	u32 mixer_cnt, mixer_id[MDSS_MDP_MAX_LAYERMIXER];
+	unsigned long flag;
 
 	if ((hist->block < MDP_LOGICAL_BLOCK_DISP_0) ||
 		(hist->block >= MDP_BLOCK_MAX))
@@ -1418,55 +1424,50 @@
 			ret = -EINVAL;
 			goto hist_collect_exit;
 		}
-		spin_lock(&mdss_hist_lock);
-		if ((hist_info->col_state == HIST_READY) ||
-			(hist_info->hist_cnt_read == 0)) {
-			/* wait for hist done if cache has no data */
-			if ((hist_info->col_state != HIST_READY) &&
-				(hist_info->hist_cnt_read == 0)) {
-				hist_info->read_request = true;
-				spin_unlock(&mdss_hist_lock);
-				timeout = HIST_WAIT_TIMEOUT *
-					hist_info->frame_cnt;
-				mutex_unlock(&mdss_mdp_hist_mutex);
-				wait_ret = wait_for_completion_killable_timeout(
+		spin_lock_irqsave(&mdss_hist_lock, flag);
+		/* wait for hist done if cache has no data */
+		if (hist_info->col_state != HIST_READY) {
+			hist_info->read_request = true;
+			spin_unlock_irqrestore(&mdss_hist_lock, flag);
+			timeout = HIST_WAIT_TIMEOUT *
+				hist_info->frame_cnt;
+			mutex_unlock(&mdss_mdp_hist_mutex);
+			wait_ret = wait_for_completion_killable_timeout(
 					&(hist_info->comp), timeout);
 
-				mutex_lock(&mdss_mdp_hist_mutex);
-				hist_info->read_request = false;
-				if (wait_ret == 0) {
-					ret = -ETIMEDOUT;
-					pr_debug("%s: bin collection timedout",
+			mutex_lock(&mdss_mdp_hist_mutex);
+			if (wait_ret == 0) {
+				ret = -ETIMEDOUT;
+				pr_debug("%s: bin collection timedout",
 						__func__);
-					goto hist_collect_exit;
-				} else if (wait_ret < 0) {
-					ret = -EINTR;
-					pr_debug("%s: bin collection interrupted",
+				goto hist_collect_exit;
+			} else if (wait_ret < 0) {
+				ret = -EINTR;
+				pr_debug("%s: bin collection interrupted",
 						__func__);
-					goto hist_collect_exit;
-				}
-				if (hist_info->col_state != HIST_READY) {
-					ret = -EBUSY;
-					pr_err("%s: collection state is not ready: %d",
-						__func__, hist_info->col_state);
-					goto hist_collect_exit;
-				}
-			} else {
-				spin_unlock(&mdss_hist_lock);
+				goto hist_collect_exit;
 			}
-			if (hist_info->col_state == HIST_READY) {
-				v_base = ctl_base + 0x1C;
-				mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false);
-				pp_hist_read(v_base, hist_info);
-				mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false);
-				spin_lock(&mdss_hist_lock);
-				hist_info->col_state = HIST_IDLE;
-				spin_unlock(&mdss_hist_lock);
+			if (hist_info->col_state != HIST_READY) {
+				ret = -ENODATA;
+				pr_debug("%s: collection state is not ready: %d",
+						__func__, hist_info->col_state);
+				goto hist_collect_exit;
 			}
 		} else {
-			spin_unlock(&mdss_hist_lock);
+			spin_unlock_irqrestore(&mdss_hist_lock, flag);
 		}
-		hist_info->hist_cnt_sent = hist_info->hist_cnt_read;
+		spin_lock_irqsave(&mdss_hist_lock, flag);
+		if (hist_info->col_state == HIST_READY) {
+			spin_unlock_irqrestore(&mdss_hist_lock, flag);
+			v_base = ctl_base + 0x1C;
+			mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false);
+			pp_hist_read(v_base, hist_info);
+			mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false);
+			spin_lock_irqsave(&mdss_hist_lock, flag);
+			hist_info->read_request = false;
+			hist_info->col_state = HIST_IDLE;
+		}
+		spin_unlock_irqrestore(&mdss_hist_lock, flag);
 	}
 	if (mixer_cnt > 1) {
 		memset(&mdss_pp_res->hist_data[disp_num][0],
@@ -1482,7 +1483,7 @@
 	} else {
 		*hist_data_addr = (u32)hist_info->data;
 	}
-
+	hist_info->hist_cnt_sent++;
 hist_collect_exit:
 	mutex_unlock(&mdss_mdp_hist_mutex);
 	return ret;
diff --git a/drivers/video/msm/mdss/mdss_mdp_rotator.h b/drivers/video/msm/mdss/mdss_mdp_rotator.h
index 1e4b81e0..eb5b47a 100644
--- a/drivers/video/msm/mdss/mdss_mdp_rotator.h
+++ b/drivers/video/msm/mdss/mdss_mdp_rotator.h
@@ -17,7 +17,7 @@
 
 #include "mdss_mdp.h"
 
-#define MDSS_MDP_ROT_SESSION_MASK	0x80000000
+#define MDSS_MDP_ROT_SESSION_MASK	0x40000000
 
 struct mdss_mdp_rotator_session {
 	u32 session_id;
diff --git a/drivers/video/msm/mdss/mdss_wb.c b/drivers/video/msm/mdss/mdss_wb.c
index a26d339..d4c924f 100644
--- a/drivers/video/msm/mdss/mdss_wb.c
+++ b/drivers/video/msm/mdss/mdss_wb.c
@@ -73,7 +73,7 @@
 	pdata->panel_info.type = WRITEBACK_PANEL;
 	pdata->panel_info.clk_rate = 74250000;
 	pdata->panel_info.pdest = DISPLAY_3;
-	pdata->panel_info.out_format = MDP_Y_CBCR_H2V2;
+	pdata->panel_info.out_format = MDP_Y_CBCR_H2V2_VENUS;
 
 	pdata->on = mdss_wb_on;
 	pdata->off = mdss_wb_off;
diff --git a/drivers/video/msm/mdss/msm_mdss_io_8974.c b/drivers/video/msm/mdss/msm_mdss_io_8974.c
index 1232ec6..f594b17 100644
--- a/drivers/video/msm/mdss/msm_mdss_io_8974.c
+++ b/drivers/video/msm/mdss/msm_mdss_io_8974.c
@@ -20,6 +20,7 @@
 #include <mach/msm_iomap.h>
 
 #include "mdss_dsi.h"
+#include "mdss_edp.h"
 
 #define SW_RESET BIT(2)
 #define SW_RESET_PLL BIT(0)
@@ -341,3 +342,333 @@
 	}
 
 }
+
+/* EDP phy configuration settings */
+void mdss_edp_phy_sw_reset(unsigned char *edp_base)
+{
+	/* phy sw reset */
+	edp_write(edp_base + 0x74, 0x100); /* EDP_PHY_CTRL */
+	wmb();
+	usleep(1);
+	edp_write(edp_base + 0x74, 0x000); /* EDP_PHY_CTRL */
+	wmb();
+	usleep(1);
+
+	/* phy PLL sw reset */
+	edp_write(edp_base + 0x74, 0x001); /* EDP_PHY_CTRL */
+	wmb();
+	usleep(1);
+	edp_write(edp_base + 0x74, 0x000); /* EDP_PHY_CTRL */
+	wmb();
+	usleep(1);
+}
+
+void mdss_edp_hw_powerup(unsigned char *edp_base, int enable)
+{
+	int ret = 0;
+
+	if (enable) {
+		/* EDP_PHY_EDPPHY_GLB_PD_CTL */
+		edp_write(edp_base + 0x52c, 0x3f);
+		/* EDP_PHY_EDPPHY_GLB_CFG */
+		edp_write(edp_base + 0x528, 0x1);
+		/* EDP_PHY_PLL_UNIPHY_PLL_GLB_CFG */
+		edp_write(edp_base + 0x620, 0xf);
+		/* EDP_AUX_CTRL */
+		ret = edp_read(edp_base + 0x300);
+		edp_write(edp_base + 0x300, ret | 0x1);
+	} else {
+		/* EDP_PHY_EDPPHY_GLB_PD_CTL */
+		edp_write(edp_base + 0x52c, 0xc0);
+	}
+}
+
+void mdss_edp_pll_configure(unsigned char *edp_base, int rate)
+{
+	if (rate == 810000000) {
+		edp_write(edp_base + 0x60c, 0x18);
+		edp_write(edp_base + 0x664, 0x5);
+		edp_write(edp_base + 0x600, 0x0);
+		edp_write(edp_base + 0x638, 0x36);
+		edp_write(edp_base + 0x63c, 0x69);
+		edp_write(edp_base + 0x640, 0xff);
+		edp_write(edp_base + 0x644, 0x2f);
+		edp_write(edp_base + 0x648, 0x0);
+		edp_write(edp_base + 0x66c, 0x0a);
+		edp_write(edp_base + 0x674, 0x01);
+		edp_write(edp_base + 0x684, 0x5a);
+		edp_write(edp_base + 0x688, 0x0);
+		edp_write(edp_base + 0x68c, 0x60);
+		edp_write(edp_base + 0x690, 0x0);
+		edp_write(edp_base + 0x694, 0x2a);
+		edp_write(edp_base + 0x698, 0x3);
+		edp_write(edp_base + 0x65c, 0x10);
+		edp_write(edp_base + 0x660, 0x1a);
+		edp_write(edp_base + 0x604, 0x0);
+		edp_write(edp_base + 0x624, 0x0);
+		edp_write(edp_base + 0x628, 0x0);
+
+		edp_write(edp_base + 0x620, 0x1);
+		edp_write(edp_base + 0x620, 0x5);
+		edp_write(edp_base + 0x620, 0x7);
+		edp_write(edp_base + 0x620, 0xf);
+
+	} else if (rate == 138500000) {
+		edp_write(edp_base + 0x664, 0x5); /* UNIPHY_PLL_LKDET_CFG2 */
+		edp_write(edp_base + 0x600, 0x1); /* UNIPHY_PLL_REFCLK_CFG */
+		edp_write(edp_base + 0x638, 0x36); /* UNIPHY_PLL_SDM_CFG0 */
+		edp_write(edp_base + 0x63c, 0x62); /* UNIPHY_PLL_SDM_CFG1 */
+		edp_write(edp_base + 0x640, 0x0); /* UNIPHY_PLL_SDM_CFG2 */
+		edp_write(edp_base + 0x644, 0x28); /* UNIPHY_PLL_SDM_CFG3 */
+		edp_write(edp_base + 0x648, 0x0); /* UNIPHY_PLL_SDM_CFG4 */
+		edp_write(edp_base + 0x64c, 0x80); /* UNIPHY_PLL_SSC_CFG0 */
+		edp_write(edp_base + 0x650, 0x0); /* UNIPHY_PLL_SSC_CFG1 */
+		edp_write(edp_base + 0x654, 0x0); /* UNIPHY_PLL_SSC_CFG2 */
+		edp_write(edp_base + 0x658, 0x0); /* UNIPHY_PLL_SSC_CFG3 */
+		edp_write(edp_base + 0x66c, 0xa); /* UNIPHY_PLL_CAL_CFG0 */
+		edp_write(edp_base + 0x674, 0x1); /* UNIPHY_PLL_CAL_CFG2 */
+		edp_write(edp_base + 0x684, 0x5a); /* UNIPHY_PLL_CAL_CFG6 */
+		edp_write(edp_base + 0x688, 0x0); /* UNIPHY_PLL_CAL_CFG7 */
+		edp_write(edp_base + 0x68c, 0x60); /* UNIPHY_PLL_CAL_CFG8 */
+		edp_write(edp_base + 0x690, 0x0); /* UNIPHY_PLL_CAL_CFG9 */
+		edp_write(edp_base + 0x694, 0x46); /* UNIPHY_PLL_CAL_CFG10 */
+		edp_write(edp_base + 0x698, 0x5); /* UNIPHY_PLL_CAL_CFG11 */
+		edp_write(edp_base + 0x65c, 0x10); /* UNIPHY_PLL_LKDET_CFG0 */
+		edp_write(edp_base + 0x660, 0x1a); /* UNIPHY_PLL_LKDET_CFG1 */
+		edp_write(edp_base + 0x604, 0x0); /* UNIPHY_PLL_POSTDIV1_CFG */
+		edp_write(edp_base + 0x624, 0x0); /* UNIPHY_PLL_POSTDIV2_CFG */
+		edp_write(edp_base + 0x628, 0x0); /* UNIPHY_PLL_POSTDIV3_CFG */
+
+		edp_write(edp_base + 0x620, 0x1); /* UNIPHY_PLL_GLB_CFG */
+		edp_write(edp_base + 0x620, 0x5); /* UNIPHY_PLL_GLB_CFG */
+		edp_write(edp_base + 0x620, 0x7); /* UNIPHY_PLL_GLB_CFG */
+		edp_write(edp_base + 0x620, 0xf); /* UNIPHY_PLL_GLB_CFG */
+	} else {
+		pr_err("%s: Unknown configuration rate\n", __func__);
+	}
+}
+
+void mdss_edp_enable_aux(unsigned char *edp_base, int enable)
+{
+	if (!enable) {
+		edp_write(edp_base + 0x300, 0); /* EDP_AUX_CTRL */
+		return;
+	}
+
+	/*reset AUX */
+	edp_write(edp_base + 0x300, BIT(1)); /* EDP_AUX_CTRL */
+	edp_write(edp_base + 0x300, 0); /* EDP_AUX_CTRL */
+
+	/* Enable AUX */
+	edp_write(edp_base + 0x300, BIT(0)); /* EDP_AUX_CTRL */
+
+	edp_write(edp_base + 0x550, 0x2c); /* AUX_CFG0 */
+	edp_write(edp_base + 0x308, 0xffffffff); /* INTR_STATUS */
+	edp_write(edp_base + 0x568, 0xff); /* INTR_MASK */
+}
+
+void mdss_edp_enable_mainlink(unsigned char *edp_base, int enable)
+{
+	u32 data;
+
+	data = edp_read(edp_base + 0x004);
+	data &= ~BIT(0);
+
+	if (enable) {
+		data |= 0x1;
+		edp_write(edp_base + 0x004, data);
+		edp_write(edp_base + 0x004, 0x1);
+	} else {
+		data |= 0x0;
+		edp_write(edp_base + 0x004, data);
+	}
+}
+
+void mdss_edp_enable_lane_bist(unsigned char *edp_base, int lane, int enable)
+{
+	unsigned char *addr_ln_bist_cfg, *addr_ln_pd_ctrl;
+
+	/* EDP_PHY_EDPPHY_LNn_PD_CTL */
+	addr_ln_pd_ctrl = edp_base + 0x404 + (0x40 * lane);
+	/* EDP_PHY_EDPPHY_LNn_BIST_CFG0 */
+	addr_ln_bist_cfg = edp_base + 0x408 + (0x40 * lane);
+
+	if (enable) {
+		edp_write(addr_ln_pd_ctrl, 0x0);
+		edp_write(addr_ln_bist_cfg, 0x10);
+
+	} else {
+		edp_write(addr_ln_pd_ctrl, 0xf);
+		edp_write(addr_ln_bist_cfg, 0x10);
+	}
+}
+
+void mdss_edp_clk_deinit(struct mdss_edp_drv_pdata *edp_drv)
+{
+	if (edp_drv->aux_clk)
+		clk_put(edp_drv->aux_clk);
+	if (edp_drv->pixel_clk)
+		clk_put(edp_drv->pixel_clk);
+	if (edp_drv->ahb_clk)
+		clk_put(edp_drv->ahb_clk);
+	if (edp_drv->link_clk)
+		clk_put(edp_drv->link_clk);
+}
+
+int mdss_edp_clk_init(struct mdss_edp_drv_pdata *edp_drv)
+{
+	struct device *dev = &(edp_drv->pdev->dev);
+
+	edp_drv->aux_clk = clk_get(dev, "core_clk");
+	if (IS_ERR(edp_drv->aux_clk)) {
+		pr_err("%s: Can't find aux_clk", __func__);
+		edp_drv->aux_clk = NULL;
+		goto mdss_edp_clk_err;
+	}
+
+	edp_drv->pixel_clk = clk_get(dev, "pixel_clk");
+	if (IS_ERR(edp_drv->pixel_clk)) {
+		pr_err("%s: Can't find pixel_clk", __func__);
+		edp_drv->pixel_clk = NULL;
+		goto mdss_edp_clk_err;
+	}
+
+	edp_drv->ahb_clk = clk_get(dev, "iface_clk");
+	if (IS_ERR(edp_drv->ahb_clk)) {
+		pr_err("%s: Can't find ahb_clk", __func__);
+		edp_drv->ahb_clk = NULL;
+		goto mdss_edp_clk_err;
+	}
+
+	edp_drv->link_clk = clk_get(dev, "link_clk");
+	if (IS_ERR(edp_drv->link_clk)) {
+		pr_err("%s: Can't find link_clk", __func__);
+		edp_drv->link_clk = NULL;
+		goto mdss_edp_clk_err;
+	}
+
+	return 0;
+
+mdss_edp_clk_err:
+	mdss_edp_clk_deinit(edp_drv);
+	return -EPERM;
+}
+
+
+void mdss_edp_clk_enable(struct mdss_edp_drv_pdata *edp_drv)
+{
+	if (edp_drv->clk_on) {
+		pr_info("%s: edp clks are already ON\n", __func__);
+		return;
+	}
+
+	if (clk_set_rate(edp_drv->aux_clk, 19200000) < 0)
+		pr_err("%s: aux_clk - clk_set_rate failed\n",
+					__func__);
+
+	if (clk_set_rate(edp_drv->pixel_clk, 138500000) < 0)
+		pr_err("%s: pixel_clk - clk_set_rate failed\n",
+					__func__);
+
+	if (clk_set_rate(edp_drv->link_clk, 270000000) < 0)
+		pr_err("%s: link_clk - clk_set_rate failed\n",
+					__func__);
+
+	clk_enable(edp_drv->aux_clk);
+	clk_enable(edp_drv->pixel_clk);
+	clk_enable(edp_drv->ahb_clk);
+	clk_enable(edp_drv->link_clk);
+
+	edp_drv->clk_on = 1;
+}
+
+void mdss_edp_clk_disable(struct mdss_edp_drv_pdata *edp_drv)
+{
+	if (edp_drv->clk_on == 0) {
+		pr_info("%s: edp clks are already OFF\n", __func__);
+		return;
+	}
+
+	clk_disable(edp_drv->aux_clk);
+	clk_disable(edp_drv->pixel_clk);
+	clk_disable(edp_drv->ahb_clk);
+	clk_disable(edp_drv->link_clk);
+
+	edp_drv->clk_on = 0;
+}
+
+void mdss_edp_prepare_clocks(struct mdss_edp_drv_pdata *edp_drv)
+{
+	clk_prepare(edp_drv->aux_clk);
+	clk_prepare(edp_drv->pixel_clk);
+	clk_prepare(edp_drv->ahb_clk);
+	clk_prepare(edp_drv->link_clk);
+}
+
+void mdss_edp_unprepare_clocks(struct mdss_edp_drv_pdata *edp_drv)
+{
+	clk_unprepare(edp_drv->aux_clk);
+	clk_unprepare(edp_drv->pixel_clk);
+	clk_unprepare(edp_drv->ahb_clk);
+	clk_unprepare(edp_drv->link_clk);
+}
+
+void mdss_edp_enable_pixel_clk(unsigned char *edp_base,
+		unsigned char *mmss_cc_base, int enable)
+{
+	if (!enable) {
+		edp_write(mmss_cc_base + 0x032c, 0); /* CBCR */
+		return;
+	}
+
+	edp_write(edp_base + 0x624, 0x1); /* PostDiv2 */
+
+	/* Configuring MND for Pixel */
+	edp_write(mmss_cc_base + 0x00a8, 0x3f); /* M value */
+	edp_write(mmss_cc_base + 0x00ac, 0xb); /* N value */
+	edp_write(mmss_cc_base + 0x00b0, 0x0); /* D value */
+
+	/* CFG RCGR */
+	edp_write(mmss_cc_base + 0x00a4, (5 << 8) | (2 << 12));
+	edp_write(mmss_cc_base + 0x00a0, 3); /* CMD RCGR */
+
+	edp_write(mmss_cc_base + 0x032c, 1); /* CBCR */
+}
+
+void mdss_edp_enable_link_clk(unsigned char *mmss_cc_base, int enable)
+{
+	if (!enable) {
+		edp_write(mmss_cc_base + 0x0330, 0); /* CBCR */
+		return;
+	}
+
+	edp_write(mmss_cc_base + 0x00c4, (4 << 8)); /* CFG RCGR */
+	edp_write(mmss_cc_base + 0x00c0, 3); /* CMD RCGR */
+
+	edp_write(mmss_cc_base + 0x0330, 1); /* CBCR */
+}
+
+void mdss_edp_config_clk(unsigned char *edp_base, unsigned char *mmss_cc_base)
+{
+	mdss_edp_enable_link_clk(mmss_cc_base, 1);
+	mdss_edp_enable_pixel_clk(edp_base, mmss_cc_base, 1);
+}
+
+void mdss_edp_unconfig_clk(unsigned char *edp_base,
+		unsigned char *mmss_cc_base)
+{
+	mdss_edp_enable_link_clk(mmss_cc_base, 0);
+	mdss_edp_enable_pixel_clk(edp_base, mmss_cc_base, 0);
+}
+
+void mdss_edp_phy_misc_cfg(unsigned char *edp_base)
+{
+	/* EDP_PHY_EDPPHY_GLB_VM_CFG0 */
+	edp_write(edp_base + 0x510, 0x3);
+	/* EDP_PHY_EDPPHY_GLB_VM_CFG1 */
+	edp_write(edp_base + 0x514, 0x64);
+	/* EDP_PHY_EDPPHY_GLB_MISC9 */
+	edp_write(edp_base + 0x518, 0x6c);
+	/* EDP_MISC1_MISC0 */
+	edp_write(edp_base + 0x2c, 0x1);
+}
diff --git a/drivers/video/msm/mipi_dsi.c b/drivers/video/msm/mipi_dsi.c
index 7d534ed..a58010e 100644
--- a/drivers/video/msm/mipi_dsi.c
+++ b/drivers/video/msm/mipi_dsi.c
@@ -69,6 +69,8 @@
 	struct msm_fb_data_type *mfd;
 	struct msm_panel_info *pinfo;
 
+	pr_debug("%s+:\n", __func__);
+
 	mfd = platform_get_drvdata(pdev);
 	pinfo = &mfd->panel_info;
 
@@ -77,15 +79,14 @@
 	else
 		down(&mfd->dma->mutex);
 
-	mdp4_overlay_dsi_state_set(ST_DSI_SUSPEND);
+	if (mfd->panel_info.type == MIPI_CMD_PANEL) {
+		mipi_dsi_prepare_clocks();
+		mipi_dsi_ahb_ctrl(1);
+		mipi_dsi_clk_enable();
 
-	/* make sure dsi clk is on so that
-	 * dcs commands can be sent
-	 */
-	mipi_dsi_clk_cfg(1);
-
-	/* make sure dsi_cmd_mdp is idle */
-	mipi_dsi_cmd_mdp_busy();
+		/* make sure dsi_cmd_mdp is idle */
+		mipi_dsi_cmd_mdp_busy();
+	}
 
 	/*
 	 * Desctiption: change to DSI_CMD_MODE since it needed to
@@ -146,6 +147,8 @@
 	u32 dummy_xres, dummy_yres;
 	int target_type = 0;
 
+	pr_debug("%s+:\n", __func__);
+
 	mfd = platform_get_drvdata(pdev);
 	fbi = mfd->fbi;
 	var = &fbi->var;
@@ -305,14 +308,15 @@
 			}
 			mipi_dsi_set_tear_on(mfd);
 		}
+		mipi_dsi_clk_disable();
+		mipi_dsi_ahb_ctrl(0);
+		mipi_dsi_unprepare_clocks();
 	}
 
 #ifdef CONFIG_MSM_BUS_SCALING
 	mdp_bus_scale_update_request(2);
 #endif
 
-	mdp4_overlay_dsi_state_set(ST_DSI_RESUME);
-
 	if (mdp_rev >= MDP_REV_41)
 		mutex_unlock(&mfd->dma->ov_mutex);
 	else
diff --git a/drivers/video/msm/mipi_dsi.h b/drivers/video/msm/mipi_dsi.h
index 0f37f6f..2711c1a 100644
--- a/drivers/video/msm/mipi_dsi.h
+++ b/drivers/video/msm/mipi_dsi.h
@@ -319,7 +319,7 @@
 void mipi_dsi_post_kickoff_del(struct dsi_kickoff_action *act);
 void mipi_dsi_controller_cfg(int enable);
 void mipi_dsi_sw_reset(void);
-void mipi_dsi_mdp_busy_wait(struct msm_fb_data_type *mfd);
+void mipi_dsi_mdp_busy_wait(void);
 
 irqreturn_t mipi_dsi_isr(int irq, void *ptr);
 
diff --git a/drivers/video/msm/mipi_dsi_host.c b/drivers/video/msm/mipi_dsi_host.c
index 60311dc..bea6b4e 100644
--- a/drivers/video/msm/mipi_dsi_host.c
+++ b/drivers/video/msm/mipi_dsi_host.c
@@ -50,6 +50,7 @@
 static int dsi_ctrl_lock;
 static int dsi_mdp_busy;
 static struct mutex cmd_mutex;
+static struct mutex clk_mutex;
 
 static struct list_head pre_kickoff_list;
 static struct list_head post_kickoff_list;
@@ -99,6 +100,7 @@
 	spin_lock_init(&dsi_mdp_lock);
 	spin_lock_init(&dsi_clk_lock);
 	mutex_init(&cmd_mutex);
+	mutex_init(&clk_mutex);
 
 	INIT_LIST_HEAD(&pre_kickoff_list);
 	INIT_LIST_HEAD(&post_kickoff_list);
@@ -165,12 +167,12 @@
 
 void mipi_dsi_clk_cfg(int on)
 {
-	unsigned long flags;
 	static int dsi_clk_cnt;
 
-	spin_lock_irqsave(&mdp_spin_lock, flags);
+	mutex_lock(&clk_mutex);
 	if (on) {
 		if (dsi_clk_cnt == 0) {
+			mipi_dsi_prepare_clocks();
 			mipi_dsi_ahb_ctrl(1);
 			mipi_dsi_clk_enable();
 		}
@@ -181,10 +183,13 @@
 			if (dsi_clk_cnt == 0) {
 				mipi_dsi_clk_disable();
 				mipi_dsi_ahb_ctrl(0);
+				mipi_dsi_unprepare_clocks();
 			}
 		}
 	}
-	spin_unlock_irqrestore(&mdp_spin_lock, flags);
+	pr_debug("%s: on=%d clk_cnt=%d pid=%d\n", __func__,
+				on, dsi_clk_cnt, current->pid);
+	mutex_unlock(&clk_mutex);
 }
 
 void mipi_dsi_turn_on_clks(void)
@@ -1022,31 +1027,13 @@
 	wmb();
 }
 
-void mipi_dsi_mdp_busy_wait(struct msm_fb_data_type *mfd)
+void mipi_dsi_mdp_busy_wait(void)
 {
-	unsigned long flag;
-	int need_wait = 0;
-
-	pr_debug("%s: start pid=%d\n",
-				__func__, current->pid);
-	spin_lock_irqsave(&dsi_mdp_lock, flag);
-	if (dsi_mdp_busy == TRUE) {
-		INIT_COMPLETION(dsi_mdp_comp);
-		need_wait++;
-	}
-	spin_unlock_irqrestore(&dsi_mdp_lock, flag);
-
-	if (need_wait) {
-		/* wait until DMA finishes the current job */
-		pr_debug("%s: pending pid=%d\n",
-				__func__, current->pid);
-		wait_for_completion(&dsi_mdp_comp);
-	}
-	pr_debug("%s: done pid=%d\n",
-				__func__, current->pid);
+	mutex_lock(&cmd_mutex);
+	mipi_dsi_cmd_mdp_busy();
+	mutex_unlock(&cmd_mutex);
 }
 
-
 void mipi_dsi_cmd_mdp_start(void)
 {
 	unsigned long flag;
@@ -1054,8 +1041,9 @@
 	mipi_dsi_mdp_stat_inc(STAT_DSI_START);
 
 	spin_lock_irqsave(&dsi_mdp_lock, flag);
-	 mipi_dsi_enable_irq(DSI_MDP_TERM);
-	 dsi_mdp_busy = TRUE;
+	mipi_dsi_enable_irq(DSI_MDP_TERM);
+	dsi_mdp_busy = TRUE;
+	INIT_COMPLETION(dsi_mdp_comp);
 	spin_unlock_irqrestore(&dsi_mdp_lock, flag);
 }
 
@@ -1089,14 +1077,28 @@
 
 void mipi_dsi_set_tear_on(struct msm_fb_data_type *mfd)
 {
-	mipi_dsi_buf_init(&dsi_tx_buf);
-	mipi_dsi_cmds_tx(&dsi_tx_buf, &dsi_tear_on_cmd, 1);
+	struct dcs_cmd_req cmdreq;
+
+	cmdreq.cmds = &dsi_tear_on_cmd;
+	cmdreq.cmds_cnt = 1;
+	cmdreq.flags = CMD_REQ_COMMIT;
+	cmdreq.rlen = 0;
+	cmdreq.cb = NULL;
+
+	mipi_dsi_cmdlist_put(&cmdreq);
 }
 
 void mipi_dsi_set_tear_off(struct msm_fb_data_type *mfd)
 {
-	mipi_dsi_buf_init(&dsi_tx_buf);
-	mipi_dsi_cmds_tx(&dsi_tx_buf, &dsi_tear_off_cmd, 1);
+	struct dcs_cmd_req cmdreq;
+
+	cmdreq.cmds = &dsi_tear_off_cmd;
+	cmdreq.cmds_cnt = 1;
+	cmdreq.flags = CMD_REQ_COMMIT;
+	cmdreq.rlen = 0;
+	cmdreq.cb = NULL;
+
+	mipi_dsi_cmdlist_put(&cmdreq);
 }
 
 int mipi_dsi_cmd_reg_tx(uint32 data)
@@ -1137,7 +1139,6 @@
 	struct dsi_cmd_desc *cm;
 	uint32 dsi_ctrl, ctrl;
 	int i, video_mode;
-	unsigned long flag;
 
 	/* turn on cmd mode
 	* for video mode, do not send cmds more than
@@ -1151,10 +1152,6 @@
 		MIPI_OUTP(MIPI_DSI_BASE + 0x0000, ctrl);
 	}
 
-	spin_lock_irqsave(&dsi_mdp_lock, flag);
-	dsi_mdp_busy = TRUE;
-	spin_unlock_irqrestore(&dsi_mdp_lock, flag);
-
 	cm = cmds;
 	mipi_dsi_buf_init(tp);
 	for (i = 0; i < cnt; i++) {
@@ -1170,11 +1167,6 @@
 	if (video_mode)
 		MIPI_OUTP(MIPI_DSI_BASE + 0x0000, dsi_ctrl); /* restore */
 
-	spin_lock_irqsave(&dsi_mdp_lock, flag);
-	dsi_mdp_busy = FALSE;
-	complete(&dsi_mdp_comp);
-	spin_unlock_irqrestore(&dsi_mdp_lock, flag);
-
 	return cnt;
 }
 
@@ -1204,7 +1196,6 @@
 {
 	int cnt, len, diff, pkt_size;
 	char cmd;
-	unsigned long flag;
 
 	if (mfd->panel_info.mipi.no_max_pkt_size) {
 		/* Only support rlen = 4*n */
@@ -1241,10 +1232,6 @@
 #endif
 	}
 
-	spin_lock_irqsave(&dsi_mdp_lock, flag);
-	dsi_mdp_busy = TRUE;
-	spin_unlock_irqrestore(&dsi_mdp_lock, flag);
-
 	if (!mfd->panel_info.mipi.no_max_pkt_size) {
 		/* packet size need to be set at every read */
 		pkt_size = len;
@@ -1279,11 +1266,6 @@
 
 	mipi_dsi_cmd_dma_rx(rp, cnt);
 
-	spin_lock_irqsave(&dsi_mdp_lock, flag);
-	dsi_mdp_busy = FALSE;
-	complete(&dsi_mdp_comp);
-	spin_unlock_irqrestore(&dsi_mdp_lock, flag);
-
 	if (mfd->panel_info.mipi.no_max_pkt_size) {
 		/*
 		 * remove extra 2 bytes from previous
@@ -1327,7 +1309,6 @@
 	struct dsi_cmd_desc *cmds;
 	int cnt, len, diff, pkt_size;
 	char cmd;
-	unsigned long flag;
 
 	if (req->flags & CMD_REQ_NO_MAX_PKT_SIZE) {
 		/* Only support rlen = 4*n */
@@ -1359,10 +1340,6 @@
 		cnt = len + 6; /* 4 bytes header + 2 bytes crc */
 	}
 
-	spin_lock_irqsave(&dsi_mdp_lock, flag);
-	dsi_mdp_busy = TRUE;
-	spin_unlock_irqrestore(&dsi_mdp_lock, flag);
-
 	if (!(req->flags & CMD_REQ_NO_MAX_PKT_SIZE)) {
 
 
@@ -1399,11 +1376,6 @@
 
 	mipi_dsi_cmd_dma_rx(rp, cnt);
 
-	spin_lock_irqsave(&dsi_mdp_lock, flag);
-	dsi_mdp_busy = FALSE;
-	complete(&dsi_mdp_comp);
-	spin_unlock_irqrestore(&dsi_mdp_lock, flag);
-
 	if (req->flags & CMD_REQ_NO_MAX_PKT_SIZE) {
 		/*
 		 * remove extra 2 bytes from previous
@@ -1515,24 +1487,43 @@
 	return rlen;
 }
 
-void mipi_dsi_cmd_mdp_busy(void)
+static void mipi_dsi_wait_for_video_eng_busy(void)
 {
 	u32 status;
+	int sleep_us = 4000;
+
+	/*
+	 * if video mode engine was not busy (in BLLP)
+	 * wait to pass BLLP
+	 */
+
+	/* check for VIDEO_MODE_ENGINE_BUSY */
+	readl_poll((MIPI_DSI_BASE + 0x0004), /* DSI_STATUS */
+				status,
+				(status & 0x08),
+				sleep_us);
+}
+
+void mipi_dsi_cmd_mdp_busy(void)
+{
 	unsigned long flags;
 	int need_wait = 0;
 
+	pr_debug("%s: start pid=%d\n",
+				__func__, current->pid);
 	spin_lock_irqsave(&dsi_mdp_lock, flags);
-	status = MIPI_INP(MIPI_DSI_BASE + 0x0004);/* DSI_STATUS */
-	if (status & 0x04) {	/* MDP BUSY */
-		INIT_COMPLETION(dsi_mdp_comp);
-		need_wait = 1;
-		pr_debug("%s: status=%x need_wait\n", __func__, (int)status);
-		mipi_dsi_enable_irq(DSI_MDP_TERM);
-	}
+	if (dsi_mdp_busy == TRUE)
+		need_wait++;
 	spin_unlock_irqrestore(&dsi_mdp_lock, flags);
 
-	if (need_wait)
+	if (need_wait) {
+		/* wait until DMA finishes the current job */
+		pr_debug("%s: pending pid=%d\n",
+				__func__, current->pid);
 		wait_for_completion(&dsi_mdp_comp);
+	}
+	pr_debug("%s: done pid=%d\n",
+				__func__, current->pid);
 }
 
 /*
@@ -1589,27 +1580,52 @@
 void mipi_dsi_cmdlist_commit(int from_mdp)
 {
 	struct dcs_cmd_req *req;
+	int video;
+	u32 dsi_ctrl;
 
 	mutex_lock(&cmd_mutex);
 	req = mipi_dsi_cmdlist_get();
-	if (req == NULL) {
-		mutex_unlock(&cmd_mutex);
-		return;
-	}
+
+	/* make sure dsi_cmd_mdp is idle */
+	mipi_dsi_cmd_mdp_busy();
+
+	if (req == NULL)
+		goto need_lock;
+
+	video = MIPI_INP(MIPI_DSI_BASE + 0x0000);
+	video &= 0x02; /* VIDEO_MODE */
+
+	if (!video)
+		mipi_dsi_clk_cfg(1);
 
 	pr_debug("%s:  from_mdp=%d pid=%d\n", __func__, from_mdp, current->pid);
 
-	if (!from_mdp) { /* from put */
-		/* make sure dsi_cmd_mdp is idle */
-		mipi_dsi_cmd_mdp_busy();
+	dsi_ctrl = MIPI_INP(MIPI_DSI_BASE + 0x0000);
+	if (dsi_ctrl & 0x02) {
+		/* video mode, make sure dsi_cmd_mdp is busy
+		 * so dcs command will be txed at start of BLLP
+		 */
+		mipi_dsi_wait_for_video_eng_busy();
+	} else {
+		/* command mode */
+		if (!from_mdp) { /* cmdlist_put */
+			/* make sure dsi_cmd_mdp is idle */
+			mipi_dsi_cmd_mdp_busy();
+		}
 	}
 
-	mipi_dsi_clk_cfg(1);
 	if (req->flags & CMD_REQ_RX)
 		mipi_dsi_cmdlist_rx(req);
 	else
 		mipi_dsi_cmdlist_tx(req);
-	mipi_dsi_clk_cfg(0);
+
+	if (!video)
+		mipi_dsi_clk_cfg(0);
+
+need_lock:
+
+	if (from_mdp) /* from pipe_commit */
+		mipi_dsi_cmd_mdp_start();
 
 	mutex_unlock(&cmd_mutex);
 }
@@ -1762,8 +1778,8 @@
 		mipi_dsi_mdp_stat_inc(STAT_DSI_MDP);
 		spin_lock(&dsi_mdp_lock);
 		dsi_ctrl_lock = FALSE;
-		mipi_dsi_disable_irq_nosync(DSI_MDP_TERM);
 		dsi_mdp_busy = FALSE;
+		mipi_dsi_disable_irq_nosync(DSI_MDP_TERM);
 		complete(&dsi_mdp_comp);
 		spin_unlock(&dsi_mdp_lock);
 	}
diff --git a/drivers/video/msm/mipi_novatek.c b/drivers/video/msm/mipi_novatek.c
index b893cc7..69ca0a3 100644
--- a/drivers/video/msm/mipi_novatek.c
+++ b/drivers/video/msm/mipi_novatek.c
@@ -302,19 +302,29 @@
 static struct dsi_cmd_desc novatek_manufacture_id_cmd = {
 	DTYPE_DCS_READ, 1, 0, 1, 5, sizeof(manufacture_id), manufacture_id};
 
+static u32 manu_id;
+
+static void mipi_novatek_manufacture_cb(u32 data)
+{
+	manu_id = data;
+	pr_info("%s: manufacture_id=%x\n", __func__, manu_id);
+}
+
 static uint32 mipi_novatek_manufacture_id(struct msm_fb_data_type *mfd)
 {
-	struct dsi_buf *rp, *tp;
-	struct dsi_cmd_desc *cmd;
-	uint32 *lp;
+	struct dcs_cmd_req cmdreq;
 
-	tp = &novatek_tx_buf;
-	rp = &novatek_rx_buf;
-	cmd = &novatek_manufacture_id_cmd;
-	mipi_dsi_cmds_rx(mfd, tp, rp, cmd, 3);
-	lp = (uint32 *)rp->data;
-	pr_info("%s: manufacture_id=%x\n", __func__, *lp);
-	return *lp;
+	cmdreq.cmds = &novatek_manufacture_id_cmd;
+	cmdreq.cmds_cnt = 1;
+	cmdreq.flags = CMD_REQ_RX | CMD_REQ_COMMIT;
+	cmdreq.rlen = 3;
+	cmdreq.cb = mipi_novatek_manufacture_cb; /* call back */
+	mipi_dsi_cmdlist_put(&cmdreq);
+	/*
+	 * blocked here, untill call back called
+	 */
+
+	return manu_id;
 }
 
 static int fpga_addr;
@@ -380,6 +390,7 @@
 	struct msm_fb_data_type *mfd;
 	struct mipi_panel_info *mipi;
 	struct msm_panel_info *pinfo;
+	struct dcs_cmd_req cmdreq;
 
 	mfd = platform_get_drvdata(pdev);
 	if (!mfd)
@@ -394,23 +405,31 @@
 	mipi  = &mfd->panel_info.mipi;
 
 	if (mipi->mode == DSI_VIDEO_MODE) {
-		mipi_dsi_cmds_tx(&novatek_tx_buf, novatek_video_on_cmds,
-				ARRAY_SIZE(novatek_video_on_cmds));
+		cmdreq.cmds = novatek_video_on_cmds;
+		cmdreq.cmds_cnt = ARRAY_SIZE(novatek_video_on_cmds);
+		cmdreq.flags = CMD_REQ_COMMIT;
+		cmdreq.rlen = 0;
+		cmdreq.cb = NULL;
+		mipi_dsi_cmdlist_put(&cmdreq);
 	} else {
-		mipi_dsi_cmds_tx(&novatek_tx_buf, novatek_cmd_on_cmds,
-				ARRAY_SIZE(novatek_cmd_on_cmds));
+		cmdreq.cmds = novatek_cmd_on_cmds;
+		cmdreq.cmds_cnt = ARRAY_SIZE(novatek_cmd_on_cmds);
+		cmdreq.flags = CMD_REQ_COMMIT;
+		cmdreq.rlen = 0;
+		cmdreq.cb = NULL;
+		mipi_dsi_cmdlist_put(&cmdreq);
 
 		/* clean up ack_err_status */
 		mipi_dsi_cmd_bta_sw_trigger();
 		mipi_novatek_manufacture_id(mfd);
 	}
-
 	return 0;
 }
 
 static int mipi_novatek_lcd_off(struct platform_device *pdev)
 {
 	struct msm_fb_data_type *mfd;
+	struct dcs_cmd_req cmdreq;
 
 	mfd = platform_get_drvdata(pdev);
 
@@ -419,8 +438,13 @@
 	if (mfd->key != MFD_KEY)
 		return -EINVAL;
 
-	mipi_dsi_cmds_tx(&novatek_tx_buf, novatek_display_off_cmds,
-			ARRAY_SIZE(novatek_display_off_cmds));
+	cmdreq.cmds = novatek_display_off_cmds;
+	cmdreq.cmds_cnt = ARRAY_SIZE(novatek_display_off_cmds);
+	cmdreq.flags = CMD_REQ_COMMIT;
+	cmdreq.rlen = 0;
+	cmdreq.cb = NULL;
+
+	mipi_dsi_cmdlist_put(&cmdreq);
 
 	return 0;
 }
@@ -431,10 +455,10 @@
 static struct dsi_cmd_desc backlight_cmd = {
 	DTYPE_DCS_LWRITE, 1, 0, 0, 1, sizeof(led_pwm1), led_pwm1};
 
-struct dcs_cmd_req cmdreq;
 
 static void mipi_novatek_set_backlight(struct msm_fb_data_type *mfd)
 {
+	struct dcs_cmd_req cmdreq;
 
 	if ((mipi_novatek_pdata->enable_wled_bl_ctrl)
 	    && (wled_trigger_initialized)) {
@@ -446,7 +470,7 @@
 
 	cmdreq.cmds = &backlight_cmd;
 	cmdreq.cmds_cnt = 1;
-	cmdreq.flags = 0;
+	cmdreq.flags = CMD_REQ_COMMIT;
 	cmdreq.rlen = 0;
 	cmdreq.cb = NULL;
 
diff --git a/drivers/video/msm/msm_fb.c b/drivers/video/msm/msm_fb.c
index 1994b1b..527b02f 100644
--- a/drivers/video/msm/msm_fb.c
+++ b/drivers/video/msm/msm_fb.c
@@ -3279,7 +3279,9 @@
 		ret = wait_for_completion_interruptible_timeout(
 		&mfd->msmfb_no_update_notify, 4*HZ);
 	}
-	return (ret > 0) ? 0 : -1;
+	if (ret == 0)
+		ret = -ETIMEDOUT;
+	return (ret > 0) ? 0 : ret;
 }
 
 static int msmfb_handle_pp_ioctl(struct msm_fb_data_type *mfd,
diff --git a/drivers/video/msm/msm_fb.h b/drivers/video/msm/msm_fb.h
index 7dc89ef..1594825 100644
--- a/drivers/video/msm/msm_fb.h
+++ b/drivers/video/msm/msm_fb.h
@@ -189,6 +189,7 @@
 	void *copy_splash_buf;
 	unsigned char *copy_splash_phys;
 	void *cpu_pm_hdl;
+	u32 avtimer_phy;
 };
 
 struct dentry *msm_fb_get_debugfs_root(void);
diff --git a/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_errors.c b/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_errors.c
index 2af76f3..3646e8c 100644
--- a/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_errors.c
+++ b/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_errors.c
@@ -218,6 +218,7 @@
 			}
 			break;
 		}
+	case VIDC_1080P_ERROR_NON_IDR_FRAME_TYPE:
 	case VIDC_1080P_ERROR_BIT_STREAM_BUF_EXHAUST:
 	case VIDC_1080P_ERROR_DESCRIPTOR_TABLE_ENTRY_INVALID:
 	case VIDC_1080P_ERROR_MB_COEFF_NOT_DONE:
@@ -243,14 +244,16 @@
 	case VIDC_1080P_ERROR_NON_PAIRED_FIELD_NOT_SUPPORTED:
 	case VIDC_1080P_ERROR_DESCRIPTOR_BUFFER_EMPTY:
 		vcd_status = VCD_ERR_BITSTREAM_ERR;
-		DDL_MSG_ERROR("VIDC_BIT_STREAM_ERR");
+		DDL_MSG_ERROR("VIDC_BIT_STREAM_ERR (%u)",
+			(u32)ddl_context->cmd_err_status);
 		break;
 	case VIDC_1080P_ERROR_B_FRAME_NOT_SUPPORTED:
 	case VIDC_1080P_ERROR_UNSUPPORTED_FEATURE_IN_PROFILE:
 	case VIDC_1080P_ERROR_RESOLUTION_NOT_SUPPORTED:
 		if (ddl->decoding) {
 			vcd_status = VCD_ERR_BITSTREAM_ERR;
-			DDL_MSG_ERROR("VIDC_BIT_STREAM_ERR");
+			DDL_MSG_ERROR("VIDC_BIT_STREAM_ERR (%u)",
+				(u32)ddl_context->cmd_err_status);
 		}
 		break;
 	default:
diff --git a/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_interrupt_handler.c b/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_interrupt_handler.c
index d94bc5b..2c41ab4 100644
--- a/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_interrupt_handler.c
+++ b/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_interrupt_handler.c
@@ -274,8 +274,8 @@
 		}
 		vidc_sm_get_profile_info(&ddl->shared_mem
 			[ddl->command_channel], &disp_profile_info);
-		disp_profile_info.pic_profile = seq_hdr_info.profile;
-		disp_profile_info.pic_level = seq_hdr_info.level;
+		seq_hdr_info.profile = disp_profile_info.pic_profile;
+		seq_hdr_info.level = disp_profile_info.pic_level;
 		ddl_get_dec_profile_level(decoder, seq_hdr_info.profile,
 			seq_hdr_info.level);
 		switch (decoder->codec.codec) {
@@ -493,6 +493,7 @@
 				ddl_get_state_string(ddl->client_state));
 			ddl_calc_core_proc_time(__func__, DEC_OP_TIME, ddl);
 			ddl_reset_core_time_variables(DEC_OP_TIME);
+			ddl_vidc_decode_reset_avg_time(ddl);
 			ddl->client_state = DDL_CLIENT_WAIT_FOR_FRAME;
 			ddl_vidc_decode_frame_run(ddl);
 			ret_status = false;
diff --git a/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_utils.c b/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_utils.c
index 31f60e5..4b5fbf5 100644
--- a/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_utils.c
+++ b/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_utils.c
@@ -13,7 +13,7 @@
 #include <linux/memory_alloc.h>
 #include <linux/delay.h>
 #include <mach/msm_subsystem_map.h>
-#include <mach/peripheral-loader.h>
+#include <mach/subsystem_restart.h>
 #include "vcd_ddl_utils.h"
 #include "vcd_ddl.h"
 #include "vcd_res_tracker_api.h"
@@ -70,7 +70,7 @@
 		alloc_size = (alloc_size+4095) & ~4095;
 		addr->alloc_handle = ion_alloc(
 		ddl_context->video_ion_client, alloc_size, SZ_4K,
-			res_trk_get_mem_type(), 0);
+			res_trk_get_mem_type(), res_trk_get_ion_flags());
 		if (IS_ERR_OR_NULL(addr->alloc_handle)) {
 			DDL_MSG_ERROR("%s() :DDL ION alloc failed\n",
 						 __func__);
@@ -361,12 +361,12 @@
 			pr_err("Failed to enable iommu clocks\n");
 			return false;
 		}
-		dram_base->pil_cookie = pil_get("vidc");
+		dram_base->pil_cookie = subsystem_get("vidc");
 		if (res_trk_disable_iommu_clocks())
 			pr_err("Failed to disable iommu clocks\n");
 		if (IS_ERR_OR_NULL(dram_base->pil_cookie)) {
 			res_trk_disable_footswitch();
-			pr_err("pil_get failed\n");
+			pr_err("subsystem_get failed\n");
 			return false;
 		}
 	} else {
@@ -398,7 +398,7 @@
 		pr_err("Failed to enable iommu clocks\n");
 		return;
 	}
-	pil_put(cookie);
+	subsystem_put(cookie);
 	if (res_trk_disable_iommu_clocks())
 		pr_err("Failed to disable iommu clocks\n");
 	if (res_trk_disable_footswitch())
diff --git a/drivers/video/msm/vidc/1080p/ddl/vidc.h b/drivers/video/msm/vidc/1080p/ddl/vidc.h
index 22fcd1c..117612b 100644
--- a/drivers/video/msm/vidc/1080p/ddl/vidc.h
+++ b/drivers/video/msm/vidc/1080p/ddl/vidc.h
@@ -103,6 +103,7 @@
 #define VIDC_1080P_ERROR_SPS_PARSE_ERROR         129
 #define VIDC_1080P_ERROR_PPS_PARSE_ERROR         130
 #define VIDC_1080P_ERROR_SLICE_PARSE_ERROR       131
+#define VIDC_1080P_ERROR_NON_IDR_FRAME_TYPE      132
 #define VIDC_1080P_ERROR_SYNC_POINT_NOT_RECEIVED  171
 
 #define VIDC_1080P_WARN_COMMAND_FLUSHED                  145
diff --git a/drivers/video/msm/vidc/1080p/resource_tracker/vcd_res_tracker.c b/drivers/video/msm/vidc/1080p/resource_tracker/vcd_res_tracker.c
index 3670dc81..d9cadef 100644
--- a/drivers/video/msm/vidc/1080p/resource_tracker/vcd_res_tracker.c
+++ b/drivers/video/msm/vidc/1080p/resource_tracker/vcd_res_tracker.c
@@ -209,7 +209,8 @@
 			addr->alloc_handle = ion_alloc(
 					ddl_context->video_ion_client,
 					 alloc_size, SZ_4K,
-					res_trk_get_mem_type(), 0);
+					res_trk_get_mem_type(),
+					res_trk_get_ion_flags());
 			if (IS_ERR_OR_NULL(addr->alloc_handle)) {
 				DDL_MSG_ERROR("%s() :DDL ION alloc failed\n",
 						__func__);
@@ -452,6 +453,10 @@
 static struct ion_client *res_trk_create_ion_client(void){
 	struct ion_client *video_client;
 	video_client = msm_ion_client_create(-1, "video_client");
+	if (IS_ERR_OR_NULL(video_client)) {
+		VCDRES_MSG_ERROR("%s: Unable to create ION client\n", __func__);
+		video_client = NULL;
+	}
 	return video_client;
 }
 
@@ -537,7 +542,6 @@
 	u32 enc_perf_level = 0, dec_perf_level = 0;
 	u32 bus_clk_index, client_type = 0;
 	int rc = 0;
-	bool turbo_enabled = false;
 	bool turbo_supported =
 		!resource_context.vidc_platform_data->disable_turbo;
 
@@ -547,9 +551,6 @@
 			dec_perf_level += cctxt_itr->reqd_perf_lvl;
 		else
 			enc_perf_level += cctxt_itr->reqd_perf_lvl;
-
-		if (cctxt_itr->is_turbo_enabled)
-			turbo_enabled = true;
 		cctxt_itr = cctxt_itr->next;
 	}
 
@@ -566,18 +567,8 @@
 
 	if (dev_ctxt->reqd_perf_lvl + dev_ctxt->curr_perf_lvl == 0)
 		bus_clk_index = 2;
-	else if ((!turbo_supported || !turbo_enabled) && bus_clk_index == 3) {
-		if (!turbo_supported)
-			VCDRES_MSG_MED("Warning: Turbo mode not supported "\
-					" falling back to 1080p bus\n");
+	else if (!turbo_supported && bus_clk_index == 3)
 		bus_clk_index = 2;
-	}
-
-	if (bus_clk_index == 3)
-		dev_ctxt->turbo_mode_set = true;
-	else
-		dev_ctxt->turbo_mode_set = false;
-
 	bus_clk_index = (bus_clk_index << 1) + (client_type + 1);
 	VCDRES_MSG_LOW("%s(), bus_clk_index = %d", __func__, bus_clk_index);
 	VCDRES_MSG_LOW("%s(),context.pcl = %x", __func__, resource_context.pcl);
@@ -633,11 +624,8 @@
 		*pn_set_perf_lvl = RESTRK_1080P_TURBO_PERF_LEVEL;
 	}
 
-	if ((!turbo_supported || !dev_ctxt->turbo_mode_set) &&
+	if (!turbo_supported &&
 		 *pn_set_perf_lvl == RESTRK_1080P_TURBO_PERF_LEVEL) {
-		if (!turbo_supported)
-			VCDRES_MSG_ERROR("Warning: Turbo mode not supported "\
-					" falling back to 1080p clocks\n");
 		vidc_freq = vidc_clk_table[2];
 		*pn_set_perf_lvl = RESTRK_1080P_MAX_PERF_LEVEL;
 	}
@@ -843,17 +831,31 @@
 	if (resource_context.vidc_platform_data->enable_ion) {
 		if (res_trk_check_for_sec_session()) {
 			mem_type = ION_HEAP(mem_type);
-	if (resource_context.res_mem_type != DDL_FW_MEM)
-		mem_type |= ION_SECURE;
-	else if (res_trk_is_cp_enabled())
-		mem_type |= ION_SECURE;
 	} else
 		mem_type = (ION_HEAP(mem_type) |
 			ION_HEAP(ION_IOMMU_HEAP_ID));
 	}
+
 	return mem_type;
 }
 
+unsigned int res_trk_get_ion_flags(void)
+{
+	unsigned int flags = 0;
+	if (resource_context.res_mem_type == DDL_FW_MEM)
+		return flags;
+
+	if (resource_context.vidc_platform_data->enable_ion) {
+		if (res_trk_check_for_sec_session()) {
+			if (resource_context.res_mem_type != DDL_FW_MEM)
+				flags |= ION_SECURE;
+			else if (res_trk_is_cp_enabled())
+				flags |= ION_SECURE;
+		}
+	}
+	return flags;
+}
+
 u32 res_trk_is_cp_enabled(void)
 {
 	if (resource_context.vidc_platform_data->cp_enabled)
diff --git a/drivers/video/msm/vidc/1080p/resource_tracker/vcd_res_tracker_api.h b/drivers/video/msm/vidc/1080p/resource_tracker/vcd_res_tracker_api.h
index 2ae2512..ee876f4 100644
--- a/drivers/video/msm/vidc/1080p/resource_tracker/vcd_res_tracker_api.h
+++ b/drivers/video/msm/vidc/1080p/resource_tracker/vcd_res_tracker_api.h
@@ -30,6 +30,7 @@
 u32 res_trk_get_core_type(void);
 u32 res_trk_get_firmware_addr(struct ddl_buf_addr *firm_addr);
 int res_trk_get_mem_type(void);
+unsigned int res_trk_get_ion_flags(void);
 u32 res_trk_get_enable_ion(void);
 u32 res_trk_is_cp_enabled(void);
 u32 res_trk_get_disable_fullhd(void);
diff --git a/drivers/video/msm/vidc/720p/ddl/vcd_ddl_utils.c b/drivers/video/msm/vidc/720p/ddl/vcd_ddl_utils.c
index 3b40640..9fb8162 100644
--- a/drivers/video/msm/vidc/720p/ddl/vcd_ddl_utils.c
+++ b/drivers/video/msm/vidc/720p/ddl/vcd_ddl_utils.c
@@ -125,9 +125,8 @@
 					alloc_size,
 					SZ_4K,
 					buff_addr->mem_type, 0);
-		if (!buff_addr->alloc_handle) {
-			ERR("\n%s(): DDL ION alloc failed\n",
-					__func__);
+		if (IS_ERR_OR_NULL(buff_addr->alloc_handle)) {
+			ERR("\n%s(): DDL ION alloc failed\n", __func__);
 			goto bailout;
 		}
 		ret = ion_phys(ddl_context->video_ion_client,
diff --git a/drivers/video/msm/vidc/720p/resource_tracker/vcd_res_tracker.c b/drivers/video/msm/vidc/720p/resource_tracker/vcd_res_tracker.c
index aee9dfe..c83faa6 100644
--- a/drivers/video/msm/vidc/720p/resource_tracker/vcd_res_tracker.c
+++ b/drivers/video/msm/vidc/720p/resource_tracker/vcd_res_tracker.c
@@ -681,6 +681,10 @@
 	struct ion_client *video_client;
 	VCDRES_MSG_LOW("%s", __func__);
 	video_client = msm_ion_client_create(-1, "video_client");
+	if (IS_ERR_OR_NULL(video_client)) {
+		VCDRES_MSG_ERROR("%s: Unable to create ION client\n", __func__);
+		video_client = NULL;
+	}
 	return video_client;
 }
 
@@ -760,6 +764,11 @@
 	return 0;
 }
 
+u32 res_trk_get_ion_flags(void)
+{
+	return 0;
+}
+
 int res_trk_check_for_sec_session()
 {
 	return 0;
diff --git a/drivers/video/msm/vidc/720p/resource_tracker/vcd_res_tracker_api.h b/drivers/video/msm/vidc/720p/resource_tracker/vcd_res_tracker_api.h
index 75fdb3e..a20d9f2 100644
--- a/drivers/video/msm/vidc/720p/resource_tracker/vcd_res_tracker_api.h
+++ b/drivers/video/msm/vidc/720p/resource_tracker/vcd_res_tracker_api.h
@@ -29,6 +29,7 @@
 u32 res_trk_get_core_type(void);
 u32 res_trk_get_mem_type(void);
 u32 res_trk_get_disable_fullhd(void);
+u32 res_trk_get_ion_flags(void);
 u32 res_trk_get_enable_ion(void);
 u32 res_trk_is_cp_enabled(void);
 struct ion_client *res_trk_get_ion_client(void);
diff --git a/drivers/video/msm/vidc/common/vcd/vcd_core.h b/drivers/video/msm/vidc/common/vcd/vcd_core.h
index ae97561..aba8119 100644
--- a/drivers/video/msm/vidc/common/vcd/vcd_core.h
+++ b/drivers/video/msm/vidc/common/vcd/vcd_core.h
@@ -147,7 +147,6 @@
 	u32 reqd_perf_lvl;
 	u32 curr_perf_lvl;
 	u32 set_perf_lvl_pending;
-	bool turbo_mode_set;
 };
 
 struct vcd_clnt_status {
diff --git a/drivers/video/msm/vidc/common/vcd/vcd_device_sm.c b/drivers/video/msm/vidc/common/vcd/vcd_device_sm.c
index 0d13028..f670a4a 100644
--- a/drivers/video/msm/vidc/common/vcd/vcd_device_sm.c
+++ b/drivers/video/msm/vidc/common/vcd/vcd_device_sm.c
@@ -219,8 +219,6 @@
 						   VCD_DEVICE_STATE_INITING,
 						   ev_code);
 	}
-	dev_ctxt->turbo_mode_set = 0;
-
 	return rc;
 }
 
diff --git a/drivers/video/msm/vidc/common/vcd/vcd_sub.c b/drivers/video/msm/vidc/common/vcd/vcd_sub.c
index 6e332ef..a9709fb 100644
--- a/drivers/video/msm/vidc/common/vcd/vcd_sub.c
+++ b/drivers/video/msm/vidc/common/vcd/vcd_sub.c
@@ -92,8 +92,8 @@
 	} else {
 		map_buffer->alloc_handle = ion_alloc(
 			    cctxt->vcd_ion_client, sz, SZ_4K,
-			    memtype, 0);
-		if (!map_buffer->alloc_handle) {
+			    memtype, res_trk_get_ion_flags());
+		if (IS_ERR_OR_NULL(map_buffer->alloc_handle)) {
 			pr_err("%s() ION alloc failed", __func__);
 			goto bailout;
 		}
@@ -920,7 +920,7 @@
 	u32 i;
 	u32 found = false;
 
-	for (i = 0; i <= pool->count && !found; i++) {
+	for (i = 1; i <= pool->count && !found; i++) {
 		if (pool->entries[i].virtual == addr)
 			found = true;
 
diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
index abcdeab..9a66ff9 100644
--- a/fs/ext4/extents.c
+++ b/fs/ext4/extents.c
@@ -2088,7 +2088,7 @@
 ext4_ext_in_cache(struct inode *inode, ext4_lblk_t block,
 			struct ext4_extent *ex)
 {
-	struct ext4_ext_cache cex;
+	struct ext4_ext_cache cex = {0, 0, 0};
 	int ret = 0;
 
 	if (ext4_ext_check_cache(inode, block, &cex)) {
diff --git a/include/linux/Kbuild b/include/linux/Kbuild
index 5b07403..98cce5b 100644
--- a/include/linux/Kbuild
+++ b/include/linux/Kbuild
@@ -445,3 +445,4 @@
 header-y += genlock.h
 header-y += msm_audio_amrwb.h
 header-y += coresight-stm.h
+header-y += ci-bridge-spi.h
\ No newline at end of file
diff --git a/include/linux/ci-bridge-spi.h b/include/linux/ci-bridge-spi.h
new file mode 100644
index 0000000..1e531db
--- /dev/null
+++ b/include/linux/ci-bridge-spi.h
@@ -0,0 +1,17 @@
+#ifndef _CI_BRIDGE_SPI_H_
+#define _CI_BRIDGE_SPI_H_
+
+#include <linux/ioctl.h>
+
+#define CI_BRIDGE_IOCTL_MAGIC 'c'
+#define CI_BRIDGE_IOCTL_RESET         _IOW(CI_BRIDGE_IOCTL_MAGIC, 0, unsigned)
+#define CI_BRIDGE_IOCTL_GET_INT_STATE _IOR(CI_BRIDGE_IOCTL_MAGIC, 1, unsigned)
+
+#ifdef __KERNEL__
+struct ci_bridge_platform_data {
+	unsigned int reset_pin;
+	unsigned int interrupt_pin;
+};
+#endif /* __KERNEL__ */
+
+#endif
diff --git a/include/linux/coresight-stm.h b/include/linux/coresight-stm.h
index 7c7c26e..427fae0 100644
--- a/include/linux/coresight-stm.h
+++ b/include/linux/coresight-stm.h
@@ -1,17 +1,5 @@
-/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- */
-
-#ifndef __MACH_STM_H
-#define __MACH_STM_H
+#ifndef _LINUX_CORESIGHT_STM_H
+#define _LINUX_CORESIGHT_STM_H
 
 enum {
 	OST_ENTITY_NONE			= 0x00,
@@ -19,7 +7,7 @@
 	OST_ENTITY_TRACE_PRINTK		= 0x02,
 	OST_ENTITY_TRACE_MARKER		= 0x04,
 	OST_ENTITY_DEV_NODE		= 0x08,
-	OST_ENTITY_ALL			= 0x1F,
+	OST_ENTITY_MAX			= 0xFF,
 };
 
 enum {
diff --git a/include/linux/cyttsp-qc.h b/include/linux/cyttsp-qc.h
index e1ab6fe..0e5cac7 100644
--- a/include/linux/cyttsp-qc.h
+++ b/include/linux/cyttsp-qc.h
@@ -356,7 +356,6 @@
 #define CY_DLY_BL			300
 #define CY_DLY_DNLOAD			100	/* ms */
 #define CY_NUM_RETRY			4	/* max num touch data read */
-#define CY_HALF_SEC_TMO_MS		500
 
 /* handshake bit in the hst_mode reg */
 #define CY_HNDSHK_BIT			0x80
diff --git a/include/linux/diagchar.h b/include/linux/diagchar.h
index 288ed43..bb5f394 100644
--- a/include/linux/diagchar.h
+++ b/include/linux/diagchar.h
@@ -18,14 +18,14 @@
 #define EVENT_MASKS_TYPE		4
 #define PKT_TYPE			8
 #define DEINIT_TYPE			16
-#define USER_SPACE_LOG_TYPE		32
+#define USER_SPACE_DATA_TYPE		32
 #define DCI_DATA_TYPE			64
 #define USB_MODE			1
 #define MEMORY_DEVICE_MODE		2
 #define NO_LOGGING_MODE			3
 #define UART_MODE			4
 #define SOCKET_MODE			5
-
+#define CALLBACK_MODE			6
 /* different values that go in for diag_data_type */
 #define DATA_TYPE_EVENT         	0
 #define DATA_TYPE_F3            	1
@@ -41,6 +41,8 @@
 #define DIAG_IOCTL_DCI_DEINIT		21
 #define DIAG_IOCTL_DCI_SUPPORT		22
 #define DIAG_IOCTL_DCI_REG		23
+#define DIAG_IOCTL_DCI_STREAM_INIT	24
+#define DIAG_IOCTL_DCI_HEALTH_STATS	25
 
 /* PC Tools IDs */
 #define APQ8060_TOOLS_ID	4062
@@ -706,5 +708,6 @@
 #define LOG_15	0x0
 
 #define LOG_GET_ITEM_NUM(xx_code) (xx_code & 0x0FFF)
+#define LOG_GET_EQUIP_ID(xx_code) ((xx_code & 0xF000) >> 12)
 
 #endif
diff --git a/include/linux/dvb/video.h b/include/linux/dvb/video.h
index 81475c2..6c42099 100644
--- a/include/linux/dvb/video.h
+++ b/include/linux/dvb/video.h
@@ -113,6 +113,7 @@
 #define VIDEO_CMD_FREE_H264_MV_BUFFER (17)
 #define VIDEO_CMD_CLEAR_INPUT_BUFFER  (18)
 #define VIDEO_CMD_CLEAR_OUTPUT_BUFFER (19)
+#define VIDEO_CMD_SET_BUFFER_COUNT    (20)
 
 /* Flags for VIDEO_CMD_FREEZE */
 #define VIDEO_CMD_FREEZE_TO_BLACK	(1 << 0)
@@ -150,6 +151,12 @@
 	struct video_buffer_prop output_buf_prop; /* Output Buffer Prop */
 };
 
+enum scan_format {
+	INTERLACE_FRAME_PROGRESSIVE,
+	INTERLACE_INTERLEAVE_FRAME_TOP_FIELD_FIRST,
+	INTERLACE_INTERLEAVE_FRAME_BOTTOM_FIELD_FIRST
+};
+
 /* Video Data Buffer Structure for Input and Output */
 struct video_data_buffer {
 	void __user *bufferaddr; /* Pointer to Buffer */
@@ -160,6 +167,7 @@
 	void *client_data;
 	void *ip_buffer_tag;
 	__u64 pts;
+	enum scan_format interlaced_format;
 };
 
 struct video_h264_mv {
diff --git a/include/linux/i2c/atmel_mxt_ts.h b/include/linux/i2c/atmel_mxt_ts.h
index 348a231..fe23993 100644
--- a/include/linux/i2c/atmel_mxt_ts.h
+++ b/include/linux/i2c/atmel_mxt_ts.h
@@ -74,6 +74,7 @@
 	u32 irq_gpio_flags;
 	int *key_codes;
 	bool need_calibration;
+	bool no_force_update;
 
 	u8(*read_chg) (void);
 	int (*init_hw) (bool);
diff --git a/include/linux/i2c/smb350.h b/include/linux/i2c/smb350.h
new file mode 100644
index 0000000..5bb5cec
--- /dev/null
+++ b/include/linux/i2c/smb350.h
@@ -0,0 +1,35 @@
+/* Copyright (c) 2012 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful;
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#ifndef __SMB350_H__
+#define __SMB350_H__
+
+#define SMB350_NAME		"smb350"
+
+/**
+ * struct smb350_platform_data
+ * structure to pass board specific information to the smb137b charger driver
+ * @chg_current_ma:	maximum fast charge current in mA
+ * @term_current_ma:	charge termination current in mA
+ * @chg_en_n_gpio:	gpio to enable or disable charging
+ * @chg_susp_n_gpio:	put active low to allow chip to suspend and disable I2C
+ * @stat_gpio:		STAT pin, active low, '0' when charging.
+ */
+struct smb350_platform_data {
+	int chg_en_n_gpio;
+	int chg_susp_n_gpio;
+	int chg_current_ma;
+	int term_current_ma;
+	int stat_gpio;
+};
+
+#endif
diff --git a/include/linux/ion.h b/include/linux/ion.h
index 85e5002..73fb995 100644
--- a/include/linux/ion.h
+++ b/include/linux/ion.h
@@ -418,7 +418,7 @@
 }
 
 static inline void *ion_map_kernel(struct ion_client *client,
-	struct ion_handle *handle, unsigned long flags)
+	struct ion_handle *handle)
 {
 	return ERR_PTR(-ENODEV);
 }
@@ -453,6 +453,12 @@
 	return -ENODEV;
 }
 
+static inline int ion_handle_get_size(struct ion_client *client,
+				struct ion_handle *handle, unsigned long *size)
+{
+	return -ENODEV;
+}
+
 static inline void ion_unmap_iommu(struct ion_client *client,
 			struct ion_handle *handle, int domain_num,
 			int partition_num)
diff --git a/include/linux/mfd/pm8xxx/pm8038.h b/include/linux/mfd/pm8xxx/pm8038.h
index 682abc8..574dab6 100644
--- a/include/linux/mfd/pm8xxx/pm8038.h
+++ b/include/linux/mfd/pm8xxx/pm8038.h
@@ -54,6 +54,7 @@
 
 /* PMIC Interrupts */
 #define PM8038_RTC_ALARM_IRQ		PM8038_IRQ_BLOCK_BIT(4, 7)
+#define PM8038_BATT_ALARM_IRQ		PM8921_IRQ_BLOCK_BIT(5, 6)
 #define PM8038_PWRKEY_REL_IRQ		PM8038_IRQ_BLOCK_BIT(6, 2)
 #define PM8038_PWRKEY_PRESS_IRQ		PM8038_IRQ_BLOCK_BIT(6, 3)
 #define PM8038_KEYPAD_IRQ		PM8038_IRQ_BLOCK_BIT(9, 2)
diff --git a/include/linux/mfd/pm8xxx/pm8921-bms.h b/include/linux/mfd/pm8xxx/pm8921-bms.h
index 5f2fe9f..ba70c96 100644
--- a/include/linux/mfd/pm8xxx/pm8921-bms.h
+++ b/include/linux/mfd/pm8xxx/pm8921-bms.h
@@ -127,6 +127,15 @@
  *					soc stored in a coincell backed register
  */
 void pm8921_bms_invalidate_shutdown_soc(void);
+
+/**
+ * pm8921_bms_cc_uah -	function to get the coulomb counter based charge. Note
+ *			that the coulomb counter are reset when the current
+ *			consumption is low (below 8mA for more than 5 minutes),
+ *			This will lead in a very low coulomb counter charge
+ *			value upon wakeup from sleep.
+ */
+int pm8921_bms_cc_uah(int *cc_uah);
 #else
 static inline int pm8921_bms_get_vsense_avg(int *result)
 {
@@ -165,6 +174,10 @@
 static inline void pm8921_bms_invalidate_shutdown_soc(void)
 {
 }
+static inline int pm8921_bms_cc_uah(int *cc_uah)
+{
+	return -ENXIO;
+}
 #endif
 
 #endif
diff --git a/include/linux/mfd/pm8xxx/pm8921-charger.h b/include/linux/mfd/pm8xxx/pm8921-charger.h
index 0e86f2a..130fb54 100644
--- a/include/linux/mfd/pm8xxx/pm8921-charger.h
+++ b/include/linux/mfd/pm8xxx/pm8921-charger.h
@@ -72,6 +72,9 @@
  * @uvd_thresh_voltage:	the USB falling UVD threshold (mV) (PM8917 only)
  * @resume_voltage_delta:	the (mV) drop to wait for before resume charging
  *				after the battery has been fully charged
+ * @resume_charge_percent:	the % SOC the charger will drop to after the
+ *				battery is fully charged before resuming
+ *				charging.
  * @term_current:	the charger current (mA) at which EOC happens
  * @cool_temp:		the temperature (degC) at which the battery is
  *			considered cool charging current and voltage is reduced.
@@ -133,6 +136,7 @@
 	unsigned int			alarm_low_mv;
 	unsigned int			alarm_high_mv;
 	unsigned int			resume_voltage_delta;
+	int				resume_charge_percent;
 	unsigned int			term_current;
 	int				cool_temp;
 	int				warm_temp;
@@ -159,6 +163,7 @@
 	enum pm8921_chg_hot_thr		hot_thr;
 	int				rconn_mohm;
 	enum pm8921_chg_led_src_config	led_src_config;
+	int				battery_less_hardware;
 };
 
 enum pm8921_charger_source {
diff --git a/include/linux/mfd/wcd9xxx/core.h b/include/linux/mfd/wcd9xxx/core.h
index c306c75..2dea611 100644
--- a/include/linux/mfd/wcd9xxx/core.h
+++ b/include/linux/mfd/wcd9xxx/core.h
@@ -13,10 +13,13 @@
 #ifndef __MFD_TABLA_CORE_H__
 #define __MFD_TABLA_CORE_H__
 
+#include <linux/types.h>
 #include <linux/interrupt.h>
 #include <linux/pm_qos.h>
+#include <linux/platform_device.h>
+#include <linux/of_irq.h>
 
-#define WCD9XXX_NUM_IRQ_REGS 3
+#define WCD9XXX_NUM_IRQ_REGS 4
 
 #define WCD9XXX_SLIM_NUM_PORT_REG 3
 
@@ -44,83 +47,52 @@
 
 
 enum {
-	TABLA_IRQ_SLIMBUS = 0,
-	TABLA_IRQ_MBHC_REMOVAL,
-	TABLA_IRQ_MBHC_SHORT_TERM,
-	TABLA_IRQ_MBHC_PRESS,
-	TABLA_IRQ_MBHC_RELEASE,
-	TABLA_IRQ_MBHC_POTENTIAL,
-	TABLA_IRQ_MBHC_INSERTION,
-	TABLA_IRQ_BG_PRECHARGE,
-	TABLA_IRQ_PA1_STARTUP,
-	TABLA_IRQ_PA2_STARTUP,
-	TABLA_IRQ_PA3_STARTUP,
-	TABLA_IRQ_PA4_STARTUP,
-	TABLA_IRQ_PA5_STARTUP,
-	TABLA_IRQ_MICBIAS1_PRECHARGE,
-	TABLA_IRQ_MICBIAS2_PRECHARGE,
-	TABLA_IRQ_MICBIAS3_PRECHARGE,
-	TABLA_IRQ_HPH_PA_OCPL_FAULT,
-	TABLA_IRQ_HPH_PA_OCPR_FAULT,
-	TABLA_IRQ_EAR_PA_OCPL_FAULT,
-	TABLA_IRQ_HPH_L_PA_STARTUP,
-	TABLA_IRQ_HPH_R_PA_STARTUP,
-	TABLA_IRQ_EAR_PA_STARTUP,
-	TABLA_NUM_IRQS,
+	/* INTR_REG 0 */
+	WCD9XXX_IRQ_SLIMBUS = 0,
+	WCD9XXX_IRQ_MBHC_REMOVAL,
+	WCD9XXX_IRQ_MBHC_SHORT_TERM,
+	WCD9XXX_IRQ_MBHC_PRESS,
+	WCD9XXX_IRQ_MBHC_RELEASE,
+	WCD9XXX_IRQ_MBHC_POTENTIAL,
+	WCD9XXX_IRQ_MBHC_INSERTION,
+	WCD9XXX_IRQ_BG_PRECHARGE,
+	/* INTR_REG 1 */
+	WCD9XXX_IRQ_PA1_STARTUP,
+	WCD9XXX_IRQ_PA2_STARTUP,
+	WCD9XXX_IRQ_PA3_STARTUP,
+	WCD9XXX_IRQ_PA4_STARTUP,
+	WCD9XXX_IRQ_PA5_STARTUP,
+	WCD9XXX_IRQ_MICBIAS1_PRECHARGE,
+	WCD9XXX_IRQ_MICBIAS2_PRECHARGE,
+	WCD9XXX_IRQ_MICBIAS3_PRECHARGE,
+	/* INTR_REG 2 */
+	WCD9XXX_IRQ_HPH_PA_OCPL_FAULT,
+	WCD9XXX_IRQ_HPH_PA_OCPR_FAULT,
+	WCD9XXX_IRQ_EAR_PA_OCPL_FAULT,
+	WCD9XXX_IRQ_HPH_L_PA_STARTUP,
+	WCD9XXX_IRQ_HPH_R_PA_STARTUP,
+	WCD9XXX_IRQ_EAR_PA_STARTUP,
+	WCD9XXX_IRQ_RESERVED_0,
+	WCD9XXX_IRQ_RESERVED_1,
+	/* INTR_REG 3 */
+	WCD9XXX_IRQ_MAD_AUDIO,
+	WCD9XXX_IRQ_MAD_BEACON,
+	WCD9XXX_IRQ_MAD_ULTRASOUND,
+	WCD9XXX_IRQ_SPEAKER_CLIPPING,
+	WCD9XXX_IRQ_MBHC_JACK_SWITCH,
+	WCD9XXX_NUM_IRQS,
 };
 
 enum {
-	SITAR_IRQ_SLIMBUS = 0,
-	SITAR_IRQ_MBHC_REMOVAL,
-	SITAR_IRQ_MBHC_SHORT_TERM,
-	SITAR_IRQ_MBHC_PRESS,
-	SITAR_IRQ_MBHC_RELEASE,
-	SITAR_IRQ_MBHC_POTENTIAL,
-	SITAR_IRQ_MBHC_INSERTION,
-	SITAR_IRQ_BG_PRECHARGE,
-	SITAR_IRQ_PA1_STARTUP,
-	SITAR_IRQ_PA2_STARTUP,
-	SITAR_IRQ_PA3_STARTUP,
-	SITAR_IRQ_PA4_STARTUP,
-	SITAR_IRQ_PA5_STARTUP,
-	SITAR_IRQ_MICBIAS1_PRECHARGE,
-	SITAR_IRQ_MICBIAS2_PRECHARGE,
-	SITAR_IRQ_MICBIAS3_PRECHARGE,
-	SITAR_IRQ_HPH_PA_OCPL_FAULT,
-	SITAR_IRQ_HPH_PA_OCPR_FAULT,
-	SITAR_IRQ_EAR_PA_OCPL_FAULT,
-	SITAR_IRQ_HPH_L_PA_STARTUP,
-	SITAR_IRQ_HPH_R_PA_STARTUP,
-	SITAR_IRQ_EAR_PA_STARTUP,
-	SITAR_NUM_IRQS,
+	TABLA_NUM_IRQS = WCD9XXX_NUM_IRQS,
+	SITAR_NUM_IRQS = WCD9XXX_NUM_IRQS,
+	TAIKO_NUM_IRQS = WCD9XXX_NUM_IRQS,
 };
 
 
-enum {
-	TAIKO_IRQ_SLIMBUS = 0,
-	TAIKO_IRQ_MBHC_REMOVAL,
-	TAIKO_IRQ_MBHC_SHORT_TERM,
-	TAIKO_IRQ_MBHC_PRESS,
-	TAIKO_IRQ_MBHC_RELEASE,
-	TAIKO_IRQ_MBHC_POTENTIAL,
-	TAIKO_IRQ_MBHC_INSERTION,
-	TAIKO_IRQ_BG_PRECHARGE,
-	TAIKO_IRQ_PA1_STARTUP,
-	TAIKO_IRQ_PA2_STARTUP,
-	TAIKO_IRQ_PA3_STARTUP,
-	TAIKO_IRQ_PA4_STARTUP,
-	TAIKO_IRQ_PA5_STARTUP,
-	TAIKO_IRQ_MICBIAS1_PRECHARGE,
-	TAIKO_IRQ_MICBIAS2_PRECHARGE,
-	TAIKO_IRQ_MICBIAS3_PRECHARGE,
-	TAIKO_IRQ_HPH_PA_OCPL_FAULT,
-	TAIKO_IRQ_HPH_PA_OCPR_FAULT,
-	TAIKO_IRQ_EAR_PA_OCPL_FAULT,
-	TAIKO_IRQ_HPH_L_PA_STARTUP,
-	TAIKO_IRQ_HPH_R_PA_STARTUP,
-	TAIKO_IRQ_EAR_PA_STARTUP,
-	TAIKO_NUM_IRQS,
-};
+#define MAX(X, Y) (((int)X) >= ((int)Y) ? (X) : (Y))
+#define WCD9XXX_MAX_NUM_IRQS (MAX(MAX(TABLA_NUM_IRQS, SITAR_NUM_IRQS), \
+				  TAIKO_NUM_IRQS))
 
 enum wcd9xxx_pm_state {
 	WCD9XXX_PM_SLEEPABLE,
@@ -128,6 +100,44 @@
 	WCD9XXX_PM_ASLEEP,
 };
 
+/*
+ * data structure for Slimbus and I2S channel.
+ * Some of fields are only used in smilbus mode
+ */
+struct wcd9xxx_ch {
+	u32 sph;		/* share channel handle - slimbus only	*/
+	u32 ch_num;		/*
+				 * vitrual channel number, such as 128 -144.
+				 * apply for slimbus only
+				 */
+	u16 ch_h;		/* chanel handle - slimbus only */
+	u16 port;		/*
+				 * tabla port for RX and TX
+				 * such as 0-9 for TX and 10 -16 for RX
+				 * apply for both i2s and slimbus
+				 */
+	u16 shift;		/*
+				 * shift bit for RX and TX
+				 * apply for both i2s and slimbus
+				 */
+	struct list_head list;	/*
+				 * channel link list
+				 * apply for both i2s and slimbus
+				 */
+};
+
+struct wcd9xxx_codec_dai_data {
+	u32 rate;				/* sample rate          */
+	u32 bit_width;				/* sit width 16,24,32   */
+	struct list_head wcd9xxx_ch_list;	/* channel list         */
+	u16 grph;				/* slimbus group handle */
+	u32 ch_mask;
+	wait_queue_head_t dai_wait;
+};
+
+#define WCD9XXX_CH(xport, xshift) \
+	{.port = xport, .shift = xshift}
+
 struct wcd9xxx {
 	struct device *dev;
 	struct slim_device *slim;
@@ -137,18 +147,12 @@
 	struct mutex irq_lock;
 	u8 version;
 
-	unsigned int irq_base;
-	unsigned int irq;
-	u8 irq_masks_cur[WCD9XXX_NUM_IRQ_REGS];
-	u8 irq_masks_cache[WCD9XXX_NUM_IRQ_REGS];
-	u8 irq_level[WCD9XXX_NUM_IRQ_REGS];
-
 	int reset_gpio;
 
 	int (*read_dev)(struct wcd9xxx *wcd9xxx, unsigned short reg,
 			int bytes, void *dest, bool interface_reg);
 	int (*write_dev)(struct wcd9xxx *wcd9xxx, unsigned short reg,
-			 int bytes, void *src, bool interface_reg);
+			int bytes, void *src, bool interface_reg);
 
 	u32 num_of_supplies;
 	struct regulator_bulk_data *supplies;
@@ -160,10 +164,19 @@
 	struct pm_qos_request pm_qos_req;
 	int wlock_holders;
 
-	int num_rx_port;
-	int num_tx_port;
-
 	u8 idbyte[4];
+
+	unsigned int irq_base;
+	unsigned int irq;
+	u8 irq_masks_cur[WCD9XXX_NUM_IRQ_REGS];
+	u8 irq_masks_cache[WCD9XXX_NUM_IRQ_REGS];
+	bool irq_level_high[WCD9XXX_MAX_NUM_IRQS];
+	int num_irqs;
+	/* Slimbus or I2S port */
+	u32 num_rx_port;
+	u32 num_tx_port;
+	struct wcd9xxx_ch *rx_chs;
+	struct wcd9xxx_ch *tx_chs;
 };
 
 int wcd9xxx_reg_read(struct wcd9xxx *wcd9xxx, unsigned short reg);
@@ -187,40 +200,23 @@
 				enum wcd9xxx_pm_state o,
 				enum wcd9xxx_pm_state n);
 
-static inline int wcd9xxx_request_irq(struct wcd9xxx *wcd9xxx, int irq,
-				     irq_handler_t handler, const char *name,
-				     void *data)
-{
-	if (!wcd9xxx->irq_base)
-		return -EINVAL;
-	return request_threaded_irq(wcd9xxx->irq_base + irq, NULL, handler,
-				    IRQF_TRIGGER_RISING, name,
-				    data);
-}
-static inline void wcd9xxx_free_irq(struct wcd9xxx *wcd9xxx,
-				int irq, void *data)
-{
-	if (!wcd9xxx->irq_base)
-		return;
-	free_irq(wcd9xxx->irq_base + irq, data);
-}
-static inline void wcd9xxx_enable_irq(struct wcd9xxx *wcd9xxx, int irq)
-{
-	if (!wcd9xxx->irq_base)
-		return;
-	enable_irq(wcd9xxx->irq_base + irq);
-}
-static inline void wcd9xxx_disable_irq(struct wcd9xxx *wcd9xxx, int irq)
-{
-	if (!wcd9xxx->irq_base)
-		return;
-	disable_irq_nosync(wcd9xxx->irq_base + irq);
-}
-static inline void wcd9xxx_disable_irq_sync(struct wcd9xxx *wcd9xxx, int irq)
-{
-	if (!wcd9xxx->irq_base)
-		return;
-	disable_irq(wcd9xxx->irq_base + irq);
-}
+int wcd9xxx_request_irq(struct wcd9xxx *wcd9xxx, int irq,
+			irq_handler_t handler, const char *name, void *data);
 
+void wcd9xxx_free_irq(struct wcd9xxx *wcd9xxx, int irq, void *data);
+void wcd9xxx_enable_irq(struct wcd9xxx *wcd9xxx, int irq);
+void wcd9xxx_disable_irq(struct wcd9xxx *wcd9xxx, int irq);
+void wcd9xxx_disable_irq_sync(struct wcd9xxx *wcd9xxx, int irq);
+#if defined(CONFIG_WCD9310_CODEC) || \
+	defined(CONFIG_WCD9304_CODEC) || \
+	defined(CONFIG_WCD9320_CODEC)
+int __init wcd9xxx_irq_of_init(struct device_node *node,
+			       struct device_node *parent);
+#else
+static inline int __init wcd9xxx_irq_of_init(struct device_node *node,
+			       struct device_node *parent)
+{
+	return 0;
+}
+#endif
 #endif
diff --git a/include/linux/mfd/wcd9xxx/pdata.h b/include/linux/mfd/wcd9xxx/pdata.h
index e831f0b..a7ca417 100644
--- a/include/linux/mfd/wcd9xxx/pdata.h
+++ b/include/linux/mfd/wcd9xxx/pdata.h
@@ -28,6 +28,12 @@
 #define SITAR_CFILT2_SEL 0x1
 #define SITAR_CFILT3_SEL 0x2
 
+#define WCD9XXX_LDOH_1P95_V 0x0
+#define WCD9XXX_LDOH_2P35_V 0x1
+#define WCD9XXX_LDOH_2P75_V 0x2
+#define WCD9XXX_LDOH_2P85_V 0x3
+#define WCD9XXX_LDOH_3P0_V 0x3
+
 #define TABLA_LDOH_1P95_V 0x0
 #define TABLA_LDOH_2P35_V 0x1
 #define TABLA_LDOH_2P75_V 0x2
@@ -37,16 +43,6 @@
 #define TABLA_CFILT2_SEL 0x1
 #define TABLA_CFILT3_SEL 0x2
 
-#define TAIKO_CFILT1_SEL 0x0
-#define TAIKO_CFILT2_SEL 0x1
-#define TAIKO_CFILT3_SEL 0x2
-
-#define TAIKO_LDOH_1P95_V 0x0
-#define TAIKO_LDOH_2P35_V 0x1
-#define TAIKO_LDOH_2P75_V 0x2
-#define TAIKO_LDOH_2P85_V 0x3
-
-
 #define MAX_AMIC_CHANNEL 7
 
 #define TABLA_OCP_300_MA 0x0
diff --git a/include/linux/mfd/wcd9xxx/wcd9304_registers.h b/include/linux/mfd/wcd9xxx/wcd9304_registers.h
index f7c483c..73919e0 100644
--- a/include/linux/mfd/wcd9xxx/wcd9304_registers.h
+++ b/include/linux/mfd/wcd9xxx/wcd9304_registers.h
@@ -409,114 +409,163 @@
 #define SITAR_A_CDC_ANC1_SMLPF_CTL__POR			(0x00000000)
 #define SITAR_A_CDC_ANC1_DCFLT_CTL			(0x20B)
 #define SITAR_A_CDC_ANC1_DCFLT_CTL__POR			(0x00000000)
-#define SITAR_A_CDC_TX1_VOL_CTL_TIMER			(0x220)
-#define SITAR_A_CDC_TX1_VOL_CTL_TIMER__POR			(0x00000000)
-#define SITAR_A_CDC_TX1_VOL_CTL_GAIN			(0x221)
-#define SITAR_A_CDC_TX1_VOL_CTL_GAIN__POR			(0x00000000)
-#define SITAR_A_CDC_TX2_VOL_CTL_GAIN			(0x229)
-#define SITAR_A_CDC_TX2_VOL_CTL_GAIN__POR			   (0x00000000)
-#define SITAR_A_CDC_TX3_VOL_CTL_GAIN			(0x231)
-#define SITAR_A_CDC_TX3_VOL_CTL_GAIN__POR			   (0x00000000)
-#define SITAR_A_CDC_TX4_VOL_CTL_GAIN			(0x239)
-#define SITAR_A_CDC_TX4_VOL_CTL_GAIN__POR			   (0x00000000)
-#define SITAR_A_CDC_TX5_VOL_CTL_GAIN			(0x241)
-#define SITAR_A_CDC_TX5_VOL_CTL_GAIN__POR			   (0x00000000)
-#define SITAR_A_CDC_TX1_VOL_CTL_CFG			 (0x222)
-#define SITAR_A_CDC_TX1_VOL_CTL_CFG__POR			    (0x00000000)
-#define SITAR_A_CDC_TX2_VOL_CTL_CFG			 (0x22A)
-#define SITAR_A_CDC_TX2_VOL_CTL_CFG__POR			    (0x00000000)
-#define SITAR_A_CDC_TX3_VOL_CTL_CFG			 (0x232)
-#define SITAR_A_CDC_TX3_VOL_CTL_CFG__POR			    (0x00000000)
-#define SITAR_A_CDC_TX4_VOL_CTL_CFG			 (0x23A)
-#define SITAR_A_CDC_TX4_VOL_CTL_CFG__POR			    (0x00000000)
+#define SITAR_A_CDC_ANC2_CTL			(0x280)
+#define SITAR_A_CDC_ANC2_CTL__POR			(0x00000000)
+#define SITAR_A_CDC_ANC2_SHIFT			(0x281)
+#define SITAR_A_CDC_ANC2_SHIFT__POR			(0x00000000)
+#define SITAR_A_CDC_ANC2_IIR_B1_CTL		(0x282)
+#define SITAR_A_CDC_ANC2_IIR_B1_CTL__POR		(0x00000000)
+#define SITAR_A_CDC_ANC2_IIR_B2_CTL		(0x283)
+#define SITAR_A_CDC_ANC2_IIR_B2_CTL__POR		(0x00000000)
+#define SITAR_A_CDC_ANC2_IIR_B3_CTL		(0x284)
+#define SITAR_A_CDC_ANC2_IIR_B3_CTL__POR		(0x00000000)
+#define SITAR_A_CDC_ANC2_IIR_B4_CTL		(0x285)
+#define SITAR_A_CDC_ANC2_IIR_B4_CTL__POR		(0x00000000)
+#define SITAR_A_CDC_ANC2_LPF_B1_CTL		(0x286)
+#define SITAR_A_CDC_ANC2_LPF_B1_CTL__POR		(0x00000000)
+#define SITAR_A_CDC_ANC2_LPF_B2_CTL		(0x287)
+#define SITAR_A_CDC_ANC2_LPF_B2_CTL__POR		(0x00000000)
+#define SITAR_A_CDC_ANC2_LPF_B3_CTL		(0x288)
+#define SITAR_A_CDC_ANC2_LPF_B3_CTL__POR		(0x00000000)
+#define SITAR_A_CDC_ANC2_SPARE			(0x289)
+#define SITAR_A_CDC_ANC2_SPARE__POR			(0x00000000)
+#define SITAR_A_CDC_ANC2_SMLPF_CTL		(0x28A)
+#define SITAR_A_CDC_ANC2_SMLPF_CTL__POR			(0x00000000)
+#define SITAR_A_CDC_ANC2_DCFLT_CTL		(0x28B)
+#define SITAR_A_CDC_ANC2_DCFLT_CTL__POR			(0x00000000)
 
+#define SITAR_A_CDC_TX1_VOL_CTL_TIMER		(0x220)
+#define SITAR_A_CDC_TX1_VOL_CTL_TIMER__POR		(0x00000000)
+#define SITAR_A_CDC_TX1_VOL_CTL_GAIN		(0x221)
+#define SITAR_A_CDC_TX1_VOL_CTL_GAIN__POR		(0x00000000)
+#define SITAR_A_CDC_TX1_VOL_CTL_CFG		(0x222)
+#define SITAR_A_CDC_TX1_VOL_CTL_CFG__POR		(0x00000000)
 #define SITAR_A_CDC_TX1_MUX_CTL			(0x223)
 #define SITAR_A_CDC_TX1_MUX_CTL__POR			(0x00000008)
-#define SITAR_A_CDC_TX1_CLK_FS_CTL                      (0x00000224)
-#define SITAR_A_CDC_TX1_CLK_FS_CTL__POR                 (0x00000003)
-#define SITAR_A_CDC_TX2_CLK_FS_CTL                      (0x0000022C)
-#define SITAR_A_CDC_TX2_CLK_FS_CTL__POR                 (0x00000003)
-#define SITAR_A_CDC_TX3_CLK_FS_CTL                      (0x00000234)
-#define SITAR_A_CDC_TX3_CLK_FS_CTL__POR                 (0x00000003)
-#define SITAR_A_CDC_TX4_CLK_FS_CTL                      (0x0000023C)
-#define SITAR_A_CDC_TX4_CLK_FS_CTL__POR                 (0x00000003)
-#define SITAR_A_CDC_TX1_DMIC_CTL			(0x225)
+#define SITAR_A_CDC_TX1_CLK_FS_CTL		(0x224)
+#define SITAR_A_CDC_TX1_CLK_FS_CTL__POR			(0x00000003)
+#define SITAR_A_CDC_TX1_DMIC_CTL		(0x225)
 #define SITAR_A_CDC_TX1_DMIC_CTL__POR			(0x00000000)
-#define SITAR_A_CDC_TX2_MUX_CTL                 (0x22B)
+
+#define SITAR_A_CDC_TX2_VOL_CTL_TIMER		(0x228)
+#define SITAR_A_CDC_TX2_VOL_CTL_TIMER__POR		(0x00000000)
+#define SITAR_A_CDC_TX2_VOL_CTL_GAIN		(0x229)
+#define SITAR_A_CDC_TX2_VOL_CTL_GAIN__POR		(0x00000000)
+#define SITAR_A_CDC_TX2_VOL_CTL_CFG		(0x22A)
+#define SITAR_A_CDC_TX2_VOL_CTL_CFG__POR		(0x00000000)
+#define SITAR_A_CDC_TX2_MUX_CTL			(0x22B)
 #define SITAR_A_CDC_TX2_MUX_CTL__POR			(0x00000008)
-#define SITAR_A_CDC_TX3_MUX_CTL                 (0x233)
+#define SITAR_A_CDC_TX2_CLK_FS_CTL		(0x22C)
+#define SITAR_A_CDC_TX2_CLK_FS_CTL__POR			(0x00000003)
+#define SITAR_A_CDC_TX2_DMIC_CTL		(0x22D)
+#define SITAR_A_CDC_TX2_DMIC_CTL__POR			(0x00000000)
+
+#define SITAR_A_CDC_TX3_VOL_CTL_TIMER		(0x230)
+#define SITAR_A_CDC_TX3_VOL_CTL_TIMER__POR		(0x00000000)
+#define SITAR_A_CDC_TX3_VOL_CTL_GAIN		(0x231)
+#define SITAR_A_CDC_TX3_VOL_CTL_GAIN__POR		(0x00000000)
+#define SITAR_A_CDC_TX3_VOL_CTL_CFG		(0x232)
+#define SITAR_A_CDC_TX3_VOL_CTL_CFG__POR		(0x00000000)
+#define SITAR_A_CDC_TX3_MUX_CTL			(0x233)
 #define SITAR_A_CDC_TX3_MUX_CTL__POR			(0x00000008)
-#define SITAR_A_CDC_TX4_MUX_CTL                 (0x23B)
+#define SITAR_A_CDC_TX3_CLK_FS_CTL		(0x234)
+#define SITAR_A_CDC_TX3_CLK_FS_CTL__POR			(0x00000003)
+#define SITAR_A_CDC_TX3_DMIC_CTL		(0x235)
+#define SITAR_A_CDC_TX3_DMIC_CTL__POR			(0x00000000)
+
+#define SITAR_A_CDC_TX4_VOL_CTL_TIMER		(0x239)
+#define SITAR_A_CDC_TX4_VOL_CTL_TIMER__POR		(0x00000000)
+#define SITAR_A_CDC_TX4_VOL_CTL_GAIN		(0x23A)
+#define SITAR_A_CDC_TX4_VOL_CTL_GAIN__POR		(0x00000000)
+#define SITAR_A_CDC_TX4_VOL_CTL_CFG		(0x23B)
+#define SITAR_A_CDC_TX4_VOL_CTL_CFG__POR		(0x00000000)
+#define SITAR_A_CDC_TX4_MUX_CTL			(0x23C)
 #define SITAR_A_CDC_TX4_MUX_CTL__POR			(0x00000008)
-#define SITAR_A_CDC_TX5_MUX_CTL                 (0x243)
+#define SITAR_A_CDC_TX4_CLK_FS_CTL		(0x23D)
+#define SITAR_A_CDC_TX4_CLK_FS_CTL__POR			(0x00000003)
+#define SITAR_A_CDC_TX4_DMIC_CTL		(0x23E)
+#define SITAR_A_CDC_TX4_DMIC_CTL__POR			(0x00000000)
+
+#define SITAR_A_CDC_TX5_VOL_CTL_TIMER		(0x240)
+#define SITAR_A_CDC_TX5_VOL_CTL_TIMER__POR		(0x00000000)
+#define SITAR_A_CDC_TX5_VOL_CTL_GAIN		(0x241)
+#define SITAR_A_CDC_TX5_VOL_CTL_GAIN__POR		(0x00000000)
+#define SITAR_A_CDC_TX5_VOL_CTL_CFG		(0x242)
+#define SITAR_A_CDC_TX5_VOL_CTL_CFG__POR		(0x00000000)
+#define SITAR_A_CDC_TX5_MUX_CTL			(0x243)
 #define SITAR_A_CDC_TX5_MUX_CTL__POR			(0x00000008)
+#define SITAR_A_CDC_TX5_CLK_FS_CTL		(0x244)
+#define SITAR_A_CDC_TX5_CLK_FS_CTL__POR			(0x00000003)
+#define SITAR_A_CDC_TX5_DMIC_CTL		(0x245)
+#define SITAR_A_CDC_TX5_DMIC_CTL__POR			(0x00000000)
 
 #define SITAR_A_CDC_SRC1_PDA_CFG			(0x2A0)
 #define SITAR_A_CDC_SRC1_PDA_CFG__POR			(0x00000000)
 #define SITAR_A_CDC_SRC1_FS_CTL			(0x2A1)
 #define SITAR_A_CDC_SRC1_FS_CTL__POR			(0x0000001b)
+#define SITAR_A_CDC_SRC2_PDA_CFG		(0x2A8)
+#define SITAR_A_CDC_SRC2_PDA_CFG__POR			(0x00000000)
+#define SITAR_A_CDC_SRC2_FS_CTL			(0x2A9)
+#define SITAR_A_CDC_SRC2_FS_CTL__POR			(0x0000001b)
 
-#define SITAR_A_CDC_RX1_B1_CTL                  (0x000002B0)
+#define SITAR_A_CDC_RX1_B1_CTL                  (0x2B0)
 #define SITAR_A_CDC_RX1_B1_CTL__POR			 (0x00000000)
-#define SITAR_A_CDC_RX2_B1_CTL                  (0x000002B8)
-#define SITAR_A_CDC_RX2_B1_CTL__POR			 (0x00000000)
-#define SITAR_A_CDC_RX3_B1_CTL                  (0x000002C0)
-#define SITAR_A_CDC_RX3_B1_CTL__POR			 (0x00000000)
-
-#define SITAR_A_CDC_RX1_B2_CTL                  (0x000002B1)
+#define SITAR_A_CDC_RX1_B2_CTL                  (0x2B1)
 #define SITAR_A_CDC_RX1_B2_CTL__POR			 (0x00000000)
-#define SITAR_A_CDC_RX2_B2_CTL                  (0x000002B9)
-#define SITAR_A_CDC_RX2_B2_CTL__POR			 (0x00000000)
-#define SITAR_A_CDC_RX3_B2_CTL                  (0x000002C1)
-#define SITAR_A_CDC_RX3_B2_CTL__POR			 (0x00000000)
-
-#define SITAR_A_CDC_RX1_B3_CTL                  (0x000002B2)
+#define SITAR_A_CDC_RX1_B3_CTL                  (0x2B2)
 #define SITAR_A_CDC_RX1_B3_CTL__POR			 (0x00000000)
-#define SITAR_A_CDC_RX2_B3_CTL                  (0x000002BA)
-#define SITAR_A_CDC_RX2_B3_CTL__POR			 (0x00000000)
-#define SITAR_A_CDC_RX3_B3_CTL                  (0x000002C2)
-#define SITAR_A_CDC_RX3_B3_CTL__POR			 (0x00000000)
-
-#define SITAR_A_CDC_RX1_B4_CTL                  (0x000002B3)
+#define SITAR_A_CDC_RX1_B4_CTL                  (0x2B3)
 #define SITAR_A_CDC_RX1_B4_CTL__POR			 (0x00000000)
-#define SITAR_A_CDC_RX2_B4_CTL                  (0x000002BB)
-#define SITAR_A_CDC_RX2_B4_CTL__POR			 (0x00000000)
-#define SITAR_A_CDC_RX3_B4_CTL                  (0x000002C3)
-#define SITAR_A_CDC_RX3_B4_CTL__POR			 (0x00000000)
-
-#define SITAR_A_CDC_RX1_B5_CTL                  (0x000002B4)
+#define SITAR_A_CDC_RX1_B5_CTL                  (0x2B4)
 #define SITAR_A_CDC_RX1_B5_CTL__POR			 (0x00000078)
-#define SITAR_A_CDC_RX2_B5_CTL                  (0x000002BC)
-#define SITAR_A_CDC_RX2_B5_CTL__POR			 (0x00000078)
-#define SITAR_A_CDC_RX3_B5_CTL                  (0x000002C4)
-#define SITAR_A_CDC_RX3_B5_CTL__POR			 (0x00000078)
-
-#define SITAR_A_CDC_RX1_B6_CTL                  (0x000002B5)
+#define SITAR_A_CDC_RX1_B6_CTL                  (0x2B5)
 #define SITAR_A_CDC_RX1_B6_CTL__POR			 (0x00000080)
-#define SITAR_A_CDC_RX2_B6_CTL                  (0x000002BD)
+#define SITAR_A_CDC_RX1_VOL_CTL_B1_CTL		(0x2B6)
+#define SITAR_A_CDC_RX1_VOL_CTL_B1_CTL__POR		(0x00000000)
+#define SITAR_A_CDC_RX1_VOL_CTL_B2_CTL		(0x2B7)
+#define SITAR_A_CDC_RX1_VOL_CTL_B2_CTL__POR		(0x00000000)
+#define SITAR_A_CDC_RX2_B1_CTL                  (0x2B8)
+#define SITAR_A_CDC_RX2_B1_CTL__POR			 (0x00000000)
+#define SITAR_A_CDC_RX2_B2_CTL                  (0x2B9)
+#define SITAR_A_CDC_RX2_B2_CTL__POR			 (0x00000000)
+#define SITAR_A_CDC_RX2_B3_CTL                  (0x2BA)
+#define SITAR_A_CDC_RX2_B3_CTL__POR			 (0x00000000)
+#define SITAR_A_CDC_RX2_B4_CTL                  (0x2BB)
+#define SITAR_A_CDC_RX2_B4_CTL__POR			 (0x00000000)
+#define SITAR_A_CDC_RX2_B5_CTL                  (0x2BC)
+#define SITAR_A_CDC_RX2_B5_CTL__POR			 (0x00000078)
+#define SITAR_A_CDC_RX2_B6_CTL                  (0x2BD)
 #define SITAR_A_CDC_RX2_B6_CTL__POR			 (0x00000080)
-#define SITAR_A_CDC_RX3_B6_CTL                  (0x000002C5)
+#define SITAR_A_CDC_RX2_VOL_CTL_B1_CTL		(0x2BE)
+#define SITAR_A_CDC_RX2_VOL_CTL_B1_CTL__POR		(0x00000000)
+#define SITAR_A_CDC_RX2_VOL_CTL_B2_CTL		(0x2BF)
+#define SITAR_A_CDC_RX2_VOL_CTL_B2_CTL__POR		(0x00000000)
+#define SITAR_A_CDC_RX3_B1_CTL                  (0x2C0)
+#define SITAR_A_CDC_RX3_B1_CTL__POR			 (0x00000000)
+#define SITAR_A_CDC_RX3_B2_CTL                  (0x2C1)
+#define SITAR_A_CDC_RX3_B2_CTL__POR			 (0x00000000)
+#define SITAR_A_CDC_RX3_B3_CTL                  (0x2C2)
+#define SITAR_A_CDC_RX3_B3_CTL__POR			 (0x00000000)
+#define SITAR_A_CDC_RX3_B4_CTL                  (0x2C3)
+#define SITAR_A_CDC_RX3_B4_CTL__POR			 (0x00000000)
+#define SITAR_A_CDC_RX3_B5_CTL                  (0x2C4)
+#define SITAR_A_CDC_RX3_B5_CTL__POR			 (0x00000078)
+#define SITAR_A_CDC_RX3_B6_CTL                  (0x2C5)
 #define SITAR_A_CDC_RX3_B6_CTL__POR			 (0x00000080)
+#define SITAR_A_CDC_RX3_VOL_CTL_B1_CTL		(0x2C6)
+#define SITAR_A_CDC_RX3_VOL_CTL_B1_CTL__POR		(0x00000000)
+#define SITAR_A_CDC_RX3_VOL_CTL_B2_CTL		(0x2C7)
+#define SITAR_A_CDC_RX3_VOL_CTL_B2_CTL__POR		(0x00000000)
 
-
-#define SITAR_A_CDC_RX1_VOL_CTL_B1_CTL			(0x2B6)
-#define SITAR_A_CDC_RX1_VOL_CTL_B1_CTL__POR			(0x00000000)
-#define SITAR_A_CDC_RX1_VOL_CTL_B2_CTL			(0x2B7)
-#define SITAR_A_CDC_RX1_VOL_CTL_B2_CTL__POR			(0x00000000)
-#define SITAR_A_CDC_RX2_VOL_CTL_B2_CTL                  (0x2BF)
-#define SITAR_A_CDC_RX2_VOL_CTL_B2_CTL__POR                     (0x00000000)
-#define SITAR_A_CDC_RX3_VOL_CTL_B2_CTL			(0x2C7)
-#define SITAR_A_CDC_RX3_VOL_CTL_B2_CTL__POR                     (0x00000000)
-
-#define SITAR_A_CDC_CLK_ANC_RESET_CTL			(0x300)
-#define SITAR_A_CDC_CLK_ANC_RESET_CTL__POR			(0x00000000)
-#define SITAR_A_CDC_CLK_RX_RESET_CTL			(0x301)
-#define SITAR_A_CDC_CLK_RX_RESET_CTL__POR			(0x00000000)
-#define SITAR_A_CDC_CLK_TX_RESET_B1_CTL			(0x302)
-#define SITAR_A_CDC_CLK_TX_RESET_B1_CTL__POR			(0x00000000)
-#define SITAR_A_CDC_CLK_TX_RESET_B2_CTL			(0x303)
-#define SITAR_A_CDC_CLK_TX_RESET_B2_CTL__POR			(0x00000000)
-#define SITAR_A_CDC_CLK_DMIC_CTL			(0x304)
+#define SITAR_A_CDC_CLK_ANC_RESET_CTL		(0x300)
+#define SITAR_A_CDC_CLK_ANC_RESET_CTL__POR		(0x00000000)
+#define SITAR_A_CDC_CLK_RX_RESET_CTL		(0x301)
+#define SITAR_A_CDC_CLK_RX_RESET_CTL__POR		(0x00000000)
+#define SITAR_A_CDC_CLK_TX_RESET_B1_CTL		(0x302)
+#define SITAR_A_CDC_CLK_TX_RESET_B1_CTL__POR		(0x00000000)
+#define SITAR_A_CDC_CLK_TX_RESET_B2_CTL		(0x303)
+#define SITAR_A_CDC_CLK_TX_RESET_B2_CTL__POR		(0x00000000)
+#define SITAR_A_CDC_CLK_DMIC_CTL		(0x304)
 #define SITAR_A_CDC_CLK_DMIC_CTL__POR			(0x00000000)
 #define SITAR_A_CDC_CLK_RX_I2S_CTL			(0x305)
 #define SITAR_A_CDC_CLK_RX_I2S_CTL__POR			(0x00000003)
@@ -654,7 +703,23 @@
 #define SITAR_A_CDC_COMP1_SHUT_DOWN_STATUS__POR		(0x00000003)
 #define SITAR_A_CDC_COMP1_FS_CFG			(0x377)
 #define SITAR_A_CDC_COMP1_FS_CFG__POR			(0x0000001b)
-#define SITAR_A_CDC_CONN_RX1_B1_CTL			(0x380)
+#define SITAR_A_CDC_COMP2_B1_CTL		(0x378)
+#define SITAR_A_CDC_COMP2_B1_CTL__POR			(0x00000030)
+#define SITAR_A_CDC_COMP2_B2_CTL		(0x379)
+#define SITAR_A_CDC_COMP2_B2_CTL__POR			(0x000000b5)
+#define SITAR_A_CDC_COMP2_B3_CTL		(0x37A)
+#define SITAR_A_CDC_COMP2_B3_CTL__POR			(0x00000028)
+#define SITAR_A_CDC_COMP2_B4_CTL		(0x37B)
+#define SITAR_A_CDC_COMP2_B4_CTL__POR			(0x0000003c)
+#define SITAR_A_CDC_COMP2_B5_CTL		(0x37C)
+#define SITAR_A_CDC_COMP2_B5_CTL__POR			(0x0000001f)
+#define SITAR_A_CDC_COMP2_B6_CTL		(0x37D)
+#define SITAR_A_CDC_COMP2_B6_CTL__POR			(0x00000000)
+#define SITAR_A_CDC_COMP2_SHUT_DOWN_STATUS	(0x37E)
+#define SITAR_A_CDC_COMP2_SHUT_DOWN_STATUS__POR		(0x00000003)
+#define SITAR_A_CDC_COMP2_FS_CFG		(0x37F)
+#define SITAR_A_CDC_COMP2_FS_CFG__POR			(0x0000001b)
+#define SITAR_A_CDC_CONN_RX1_B1_CTL		(0x380)
 #define SITAR_A_CDC_CONN_RX1_B1_CTL__POR		(0x00000000)
 #define SITAR_A_CDC_CONN_RX1_B2_CTL			(0x381)
 #define SITAR_A_CDC_CONN_RX1_B2_CTL__POR		(0x00000000)
diff --git a/include/linux/mfd/wcd9xxx/wcd9xxx-slimslave.h b/include/linux/mfd/wcd9xxx/wcd9xxx-slimslave.h
index 0d5d058..aaa8fd6 100644
--- a/include/linux/mfd/wcd9xxx/wcd9xxx-slimslave.h
+++ b/include/linux/mfd/wcd9xxx/wcd9xxx-slimslave.h
@@ -16,27 +16,6 @@
 #include <linux/slimbus/slimbus.h>
 #include <linux/mfd/wcd9xxx/core.h>
 
-/* Channel numbers to be used for each port */
-enum {
-	SLIM_TX_1   = 128,
-	SLIM_TX_2   = 129,
-	SLIM_TX_3   = 130,
-	SLIM_TX_4   = 131,
-	SLIM_TX_5   = 132,
-	SLIM_TX_6   = 133,
-	SLIM_TX_7   = 134,
-	SLIM_TX_8   = 135,
-	SLIM_TX_9   = 136,
-	SLIM_TX_10  = 137,
-	SLIM_RX_1   = 138,
-	SLIM_RX_2   = 139,
-	SLIM_RX_3   = 140,
-	SLIM_RX_4   = 141,
-	SLIM_RX_5   = 142,
-	SLIM_RX_6   = 143,
-	SLIM_RX_7   = 144,
-	SLIM_MAX    = 145
-};
 
 /*
  *  client is expected to give port ids in the range of
@@ -92,30 +71,46 @@
 /* slave port water mark level
  *   (0: 6bytes, 1: 9bytes, 2: 12 bytes, 3: 15 bytes)
  */
-#define SLAVE_PORT_WATER_MARK_VALUE 2
+#define SLAVE_PORT_WATER_MARK_6BYTES  0
+#define SLAVE_PORT_WATER_MARK_9BYTES  1
+#define SLAVE_PORT_WATER_MARK_12BYTES 2
+#define SLAVE_PORT_WATER_MARK_15BYTES 3
 #define SLAVE_PORT_WATER_MARK_SHIFT 1
 #define SLAVE_PORT_ENABLE           1
 #define SLAVE_PORT_DISABLE          0
-
+#define WATER_MARK_VAL \
+	((SLAVE_PORT_WATER_MARK_12BYTES << SLAVE_PORT_WATER_MARK_SHIFT) | \
+	 (SLAVE_PORT_ENABLE))
 #define BASE_CH_NUM 128
 
 
-int wcd9xxx_init_slimslave(struct wcd9xxx *wcd9xxx, u8 wcd9xxx_pgd_la);
+int wcd9xxx_init_slimslave(struct wcd9xxx *wcd9xxx,
+			   u8 wcd9xxx_pgd_la,
+			   unsigned int tx_num, unsigned int *tx_slot,
+			   unsigned int rx_num, unsigned int *rx_slot);
 
 int wcd9xxx_deinit_slimslave(struct wcd9xxx *wcd9xxx);
 
-int wcd9xxx_cfg_slim_sch_rx(struct wcd9xxx *wcd9xxx, unsigned int *ch_num,
-				unsigned int tot_ch, unsigned int rate);
-int wcd9xxx_cfg_slim_sch_tx(struct wcd9xxx *wcd9xxx, unsigned int *ch_num,
-				unsigned int tot_ch, unsigned int rate);
-int wcd9xxx_close_slim_sch_rx(struct wcd9xxx *wcd9xxx, unsigned int *ch_num,
-				unsigned int tot_ch);
-int wcd9xxx_close_slim_sch_tx(struct wcd9xxx *wcd9xxx, unsigned int *ch_num,
-				unsigned int tot_ch);
+int wcd9xxx_cfg_slim_sch_rx(struct wcd9xxx *wcd9xxx,
+			    struct list_head *wcd9xxx_ch_list,
+			    unsigned int rate, unsigned int bit_width,
+			    u16 *grph);
+int wcd9xxx_cfg_slim_sch_tx(struct wcd9xxx *wcd9xxx,
+			    struct list_head *wcd9xxx_ch_list,
+			    unsigned int rate, unsigned int bit_width,
+				u16 *grph);
+int wcd9xxx_close_slim_sch_rx(struct wcd9xxx *wcd9xxx,
+			      struct list_head *wcd9xxx_ch_list, u16 grph);
+int wcd9xxx_close_slim_sch_tx(struct wcd9xxx *wcd9xxx,
+			      struct list_head *wcd9xxx_ch_list, u16 grph);
 int wcd9xxx_get_channel(struct wcd9xxx *wcd9xxx,
 			unsigned int *rx_ch,
 			unsigned int *tx_ch);
 int wcd9xxx_get_slave_port(unsigned int ch_num);
-int wcd9xxx_disconnect_port(struct wcd9xxx *wcd9xxx, unsigned int *ch_num,
-				unsigned int tot_ch, unsigned int rx_tx);
+int wcd9xxx_disconnect_port(struct wcd9xxx *wcd9xxx,
+			    struct list_head *wcd9xxx_ch_list, u16 grph);
+int wcd9xxx_rx_vport_validation(u32 port_id,
+				struct list_head *codec_dai_list);
+int wcd9xxx_tx_vport_validation(u32 vtable, u32 port_id,
+				struct wcd9xxx_codec_dai_data *codec_dai);
 #endif /* __WCD9310_SLIMSLAVE_H_ */
diff --git a/include/linux/mfd/wcd9xxx/wcd9xxx_registers.h b/include/linux/mfd/wcd9xxx/wcd9xxx_registers.h
index c66e953..4b7a32c 100644
--- a/include/linux/mfd/wcd9xxx/wcd9xxx_registers.h
+++ b/include/linux/mfd/wcd9xxx/wcd9xxx_registers.h
@@ -16,27 +16,191 @@
 #define WCD9XXX_A_CHIP_CTL			(0x00)
 #define WCD9XXX_A_CHIP_CTL__POR			(0x00000000)
 #define WCD9XXX_A_CHIP_STATUS			(0x01)
-#define WCD9XXX_A_CHIP_STATUS__POR			(0x00000000)
-#define WCD9XXX_A_CHIP_ID_BYTE_0			(0x04)
-#define WCD9XXX_A_CHIP_ID_BYTE_0__POR			(0x00000000)
-#define WCD9XXX_A_CHIP_ID_BYTE_1			(0x05)
-#define WCD9XXX_A_CHIP_ID_BYTE_1__POR			(0x00000000)
-#define WCD9XXX_A_CHIP_ID_BYTE_2			(0x06)
-#define WCD9XXX_A_CHIP_ID_BYTE_2__POR			(0x00000000)
-#define WCD9XXX_A_CHIP_ID_BYTE_3			(0x07)
-#define WCD9XXX_A_CHIP_ID_BYTE_3__POR			(0x00000001)
+#define WCD9XXX_A_CHIP_STATUS__POR		(0x00000000)
+#define WCD9XXX_A_CHIP_ID_BYTE_0		(0x04)
+#define WCD9XXX_A_CHIP_ID_BYTE_0__POR		(0x00000000)
+#define WCD9XXX_A_CHIP_ID_BYTE_1		(0x05)
+#define WCD9XXX_A_CHIP_ID_BYTE_1__POR		(0x00000000)
+#define WCD9XXX_A_CHIP_ID_BYTE_2		(0x06)
+#define WCD9XXX_A_CHIP_ID_BYTE_2__POR		(0x00000000)
+#define WCD9XXX_A_CHIP_ID_BYTE_3		(0x07)
+#define WCD9XXX_A_CHIP_ID_BYTE_3__POR		(0x00000001)
 #define WCD9XXX_A_CHIP_VERSION			(0x08)
-#define WCD9XXX_A_CHIP_VERSION__POR			(0x00000020)
+#define WCD9XXX_A_CHIP_VERSION__POR		(0x00000020)
 #define WCD9XXX_A_SB_VERSION			(0x09)
-#define WCD9XXX_A_SB_VERSION__POR			(0x00000010)
+#define WCD9XXX_A_SB_VERSION__POR		(0x00000010)
 #define WCD9XXX_A_SLAVE_ID_1			(0x0C)
-#define WCD9XXX_A_SLAVE_ID_1__POR			(0x00000077)
+#define WCD9XXX_A_SLAVE_ID_1__POR		(0x00000077)
 #define WCD9XXX_A_SLAVE_ID_2			(0x0D)
-#define WCD9XXX_A_SLAVE_ID_2__POR			(0x00000066)
+#define WCD9XXX_A_SLAVE_ID_2__POR		(0x00000066)
 #define WCD9XXX_A_SLAVE_ID_3			(0x0E)
-#define WCD9XXX_A_SLAVE_ID_3__POR			(0x00000055)
+#define WCD9XXX_A_SLAVE_ID_3__POR		(0x00000055)
 #define WCD9XXX_A_CDC_CTL			(0x80)
 #define WCD9XXX_A_CDC_CTL__POR			(0x00000000)
 #define WCD9XXX_A_LEAKAGE_CTL			(0x88)
-#define WCD9XXX_A_LEAKAGE_CTL__POR			(0x00000004)
+#define WCD9XXX_A_LEAKAGE_CTL__POR		(0x00000004)
+#define WCD9XXX_A_INTR_MODE			(0x90)
+#define WCD9XXX_A_INTR_MASK0			(0x94)
+#define WCD9XXX_A_INTR_STATUS0			(0x98)
+#define WCD9XXX_A_INTR_CLEAR0			(0x9C)
+#define WCD9XXX_A_INTR_LEVEL0			(0xA0)
+#define WCD9XXX_A_INTR_LEVEL1			(0xA1)
+#define WCD9XXX_A_INTR_LEVEL2			(0xA2)
+#define WCD9XXX_A_RX_HPH_CNP_EN			(0x1AB)
+#define WCD9XXX_A_RX_HPH_CNP_EN__POR		(0x80)
+#define WCD9XXX_A_RX_HPH_CNP_EN			(0x1AB)
+#define WCD9XXX_A_RX_HPH_CNP_EN__POR		(0x80)
+#define WCD9XXX_A_BIAS_CENTRAL_BG_CTL		(0x101)
+#define WCD9XXX_A_BIAS_CENTRAL_BG_CTL__POR	(0x50)
+#define WCD9XXX_A_CLK_BUFF_EN1			(0x108)
+#define WCD9XXX_A_CLK_BUFF_EN1__POR		(0x04)
+#define WCD9XXX_A_CLK_BUFF_EN2			(0x109)
+#define WCD9XXX_A_CLK_BUFF_EN2__POR		(0x02)
+#define WCD9XXX_A_RX_COM_BIAS			(0x1A2)
+#define WCD9XXX_A_RX_COM_BIAS__POR		(0x00)
+#define WCD9XXX_A_RC_OSC_FREQ			(0x1FA)
+#define WCD9XXX_A_RC_OSC_FREQ__POR		(0x46)
+#define WCD9XXX_A_BIAS_OSC_BG_CTL		(0x105)
+#define WCD9XXX_A_BIAS_OSC_BG_CTL__POR		(0x16)
+#define WCD9XXX_A_RC_OSC_TEST			(0x1FB)
+#define WCD9XXX_A_RC_OSC_TEST__POR		(0x0A)
+#define WCD9XXX_A_CDC_CLK_MCLK_CTL		(0x311)
+#define WCD9XXX_A_CDC_CLK_MCLK_CTL__POR		(0x00)
+
+#define WCD9XXX_A_CDC_MBHC_EN_CTL		(0x3C0)
+#define WCD9XXX_A_CDC_MBHC_EN_CTL__POR		(0x00)
+#define WCD9XXX_A_CDC_MBHC_FIR_B1_CFG		(0x3C1)
+#define WCD9XXX_A_CDC_MBHC_FIR_B1_CFG__POR	(0x00)
+#define WCD9XXX_A_CDC_MBHC_FIR_B2_CFG		(0x3C2)
+#define WCD9XXX_A_CDC_MBHC_FIR_B2_CFG__POR	(0x06)
+#define WCD9XXX_A_CDC_MBHC_TIMER_B1_CTL		(0x3C3)
+#define WCD9XXX_A_CDC_MBHC_TIMER_B1_CTL__POR	(0x03)
+#define WCD9XXX_A_CDC_MBHC_TIMER_B2_CTL		(0x3C4)
+#define WCD9XXX_A_CDC_MBHC_TIMER_B2_CTL__POR	(0x09)
+#define WCD9XXX_A_CDC_MBHC_TIMER_B3_CTL		(0x3C5)
+#define WCD9XXX_A_CDC_MBHC_TIMER_B3_CTL__POR	(0x1E)
+#define WCD9XXX_A_CDC_MBHC_TIMER_B4_CTL		(0x3C6)
+#define WCD9XXX_A_CDC_MBHC_TIMER_B4_CTL__POR	(0x45)
+#define WCD9XXX_A_CDC_MBHC_TIMER_B5_CTL		(0x3C7)
+#define WCD9XXX_A_CDC_MBHC_TIMER_B5_CTL__POR	(0x04)
+#define WCD9XXX_A_CDC_MBHC_TIMER_B6_CTL		(0x3C8)
+#define WCD9XXX_A_CDC_MBHC_TIMER_B6_CTL__POR	(0x78)
+#define WCD9XXX_A_CDC_MBHC_B1_STATUS		(0x3C9)
+#define WCD9XXX_A_CDC_MBHC_B1_STATUS__POR	(0x00)
+#define WCD9XXX_A_CDC_MBHC_B2_STATUS		(0x3CA)
+#define WCD9XXX_A_CDC_MBHC_B2_STATUS__POR	(0x00)
+#define WCD9XXX_A_CDC_MBHC_B3_STATUS		(0x3CB)
+#define WCD9XXX_A_CDC_MBHC_B3_STATUS__POR	(0x00)
+#define WCD9XXX_A_CDC_MBHC_B4_STATUS		(0x3CC)
+#define WCD9XXX_A_CDC_MBHC_B4_STATUS__POR	(0x00)
+#define WCD9XXX_A_CDC_MBHC_B5_STATUS		(0x3CD)
+#define WCD9XXX_A_CDC_MBHC_B5_STATUS__POR	(0x00)
+#define WCD9XXX_A_CDC_MBHC_B1_CTL		(0x3CE)
+#define WCD9XXX_A_CDC_MBHC_B1_CTL__POR		(0xC0)
+#define WCD9XXX_A_CDC_MBHC_B2_CTL		(0x3CF)
+#define WCD9XXX_A_CDC_MBHC_B2_CTL__POR		(0x5D)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B1_CTL		(0x3D0)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B1_CTL__POR	(0x00)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B2_CTL		(0x3D1)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B2_CTL__POR	(0x00)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B3_CTL		(0x3D2)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B3_CTL__POR	(0x00)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B4_CTL		(0x3D3)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B4_CTL__POR	(0x00)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B5_CTL		(0x3D4)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B5_CTL__POR	(0x00)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B6_CTL		(0x3D5)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B6_CTL__POR	(0x00)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B7_CTL		(0x3D6)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B7_CTL__POR	(0xFF)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B8_CTL		(0x3D7)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B8_CTL__POR	(0x07)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B9_CTL		(0x3D8)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B9_CTL__POR	(0xFF)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B10_CTL		(0x3D9)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B10_CTL__POR	(0x7F)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B11_CTL		(0x3DA)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B11_CTL__POR	(0x00)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B12_CTL		(0x3DB)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B12_CTL__POR	(0x80)
+#define WCD9XXX_A_CDC_MBHC_CLK_CTL		(0x3DC)
+#define WCD9XXX_A_CDC_MBHC_CLK_CTL__POR		(0x00)
+#define WCD9XXX_A_CDC_MBHC_INT_CTL		(0x3DD)
+#define WCD9XXX_A_CDC_MBHC_INT_CTL__POR		(0x00)
+#define WCD9XXX_A_CDC_MBHC_DEBUG_CTL		(0x3DE)
+#define WCD9XXX_A_CDC_MBHC_DEBUG_CTL__POR	(0x00)
+#define WCD9XXX_A_CDC_MBHC_SPARE		(0x3DF)
+#define WCD9XXX_A_CDC_MBHC_SPARE__POR		(0x00)
+#define WCD9XXX_A_MBHC_SCALING_MUX_1		(0x14E)
+#define WCD9XXX_A_MBHC_SCALING_MUX_1__POR	(0x00)
+#define WCD9XXX_A_RX_HPH_OCP_CTL		(0x1AA)
+#define WCD9XXX_A_RX_HPH_OCP_CTL__POR		(0x68)
+#define WCD9XXX_A_MICB_1_CTL			(0x12B)
+#define WCD9XXX_A_MICB_1_CTL__POR		(0x16)
+#define WCD9XXX_A_MICB_1_INT_RBIAS		(0x12C)
+#define WCD9XXX_A_MICB_1_INT_RBIAS__POR		(0x24)
+#define WCD9XXX_A_MICB_1_MBHC			(0x12D)
+#define WCD9XXX_A_MICB_1_MBHC__POR		(0x01)
+#define WCD9XXX_A_MICB_CFILT_2_CTL		(0x12E)
+#define WCD9XXX_A_MICB_CFILT_2_CTL__POR		(0x40)
+#define WCD9XXX_A_MICB_CFILT_2_VAL		(0x12F)
+#define WCD9XXX_A_MICB_CFILT_2_VAL__POR		(0x80)
+#define WCD9XXX_A_MICB_CFILT_2_PRECHRG		(0x130)
+#define WCD9XXX_A_MICB_CFILT_2_PRECHRG__POR	(0x38)
+#define WCD9XXX_A_MICB_2_CTL			(0x131)
+#define WCD9XXX_A_MICB_2_CTL__POR		(0x16)
+#define WCD9XXX_A_MICB_2_INT_RBIAS		(0x132)
+#define WCD9XXX_A_MICB_2_INT_RBIAS__POR		(0x24)
+#define WCD9XXX_A_MICB_2_MBHC			(0x133)
+#define WCD9XXX_A_MICB_2_MBHC__POR		(0x02)
+#define WCD9XXX_A_MICB_CFILT_3_CTL		(0x134)
+#define WCD9XXX_A_MICB_CFILT_3_CTL__POR		(0x40)
+#define WCD9XXX_A_MICB_CFILT_3_VAL		(0x135)
+#define WCD9XXX_A_MICB_CFILT_3_VAL__POR		(0x80)
+#define WCD9XXX_A_MICB_CFILT_3_PRECHRG		(0x136)
+#define WCD9XXX_A_MICB_CFILT_3_PRECHRG__POR	(0x38)
+#define WCD9XXX_A_MICB_3_CTL			(0x137)
+#define WCD9XXX_A_MICB_3_CTL__POR		(0x16)
+#define WCD9XXX_A_MICB_3_INT_RBIAS		(0x138)
+#define WCD9XXX_A_MICB_3_INT_RBIAS__POR		(0x24)
+#define WCD9XXX_A_MICB_3_MBHC			(0x139)
+#define WCD9XXX_A_MICB_3_MBHC__POR		(0x00)
+#define WCD9XXX_A_MICB_4_CTL			(0x13D)
+#define WCD9XXX_A_MICB_4_CTL__POR		(0x16)
+#define WCD9XXX_A_MICB_4_INT_RBIAS		(0x13E)
+#define WCD9XXX_A_MICB_4_INT_RBIAS__POR		(0x24)
+#define WCD9XXX_A_MICB_4_MBHC			(0x13F)
+#define WCD9XXX_A_MICB_4_MBHC__POR		(0x01)
+#define WCD9XXX_A_MICB_CFILT_1_VAL		(0x129)
+#define WCD9XXX_A_MICB_CFILT_1_VAL__POR		(0x80)
+#define WCD9XXX_A_MBHC_HPH			(0x1FE)
+#define WCD9XXX_A_MBHC_HPH__POR			(0x44)
+#define WCD9XXX_A_RX_HPH_CNP_WG_TIME		(0x1AD)
+#define WCD9XXX_A_RX_HPH_CNP_WG_TIME__POR	(0x2A)
+#define WCD9XXX_A_RX_HPH_R_DAC_CTL		(0x1B7)
+#define WCD9XXX_A_RX_HPH_R_DAC_CTL__POR		(0x00)
+#define WCD9XXX_A_RX_HPH_L_DAC_CTL		(0x1B1)
+#define WCD9XXX_A_RX_HPH_L_DAC_CTL__POR		(0x00)
+#define WCD9XXX_A_TX_7_MBHC_EN			(0x171)
+#define WCD9XXX_A_TX_7_MBHC_EN__POR		(0x0C)
+#define WCD9XXX_A_PIN_CTL_OE0			(0x010)
+#define WCD9XXX_A_PIN_CTL_OE0__POR		(0x00)
+#define WCD9XXX_A_PIN_CTL_OE1			(0x011)
+#define WCD9XXX_A_PIN_CTL_OE1__POR		(0x00)
+#define WCD9XXX_A_MICB_CFILT_1_CTL		(0x128)
+#define WCD9XXX_A_LDO_H_MODE_1			(0x110)
+#define WCD9XXX_A_LDO_H_MODE_1__POR		(0x65)
+#define WCD9XXX_A_MICB_CFILT_1_CTL__POR		(0x40)
+#define WCD9XXX_A_TX_7_MBHC_TEST_CTL		(0x174)
+#define WCD9XXX_A_TX_7_MBHC_TEST_CTL__POR	(0x38)
+#define WCD9XXX_A_MBHC_SCALING_MUX_2		(0x14F)
+#define WCD9XXX_A_MBHC_SCALING_MUX_2__POR	(0x80)
+#define WCD9XXX_A_TX_COM_BIAS			(0x14C)
+#define WCD9XXX_A_TX_COM_BIAS__POR		(0xF0)
+
+#define WCD9XXX_A_MBHC_INSERT_DETECT		(0x14A) /* TAIKO and later */
+#define WCD9XXX_A_MBHC_INSERT_DETECT__POR	(0x00)
+#define WCD9XXX_A_MBHC_INSERT_DET_STATUS	(0x14B) /* TAIKO and later */
+#define WCD9XXX_A_MBHC_INSERT_DET_STATUS__POR	(0x00)
+
 #endif
diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
index 0330dfb..fb854ba 100644
--- a/include/linux/mmc/card.h
+++ b/include/linux/mmc/card.h
@@ -60,6 +60,7 @@
 	unsigned int		sa_timeout;		/* Units: 100ns */
 	unsigned int		generic_cmd6_time;	/* Units: 10ms */
 	unsigned int            power_off_longtime;     /* Units: ms */
+	u8			power_off_notification;	/* state */
 	unsigned int		hs_max_dtr;
 #define MMC_HIGH_26_MAX_DTR	26000000
 #define MMC_HIGH_52_MAX_DTR	52000000
@@ -187,6 +188,26 @@
 
 #define SDIO_MAX_FUNCS		7
 
+enum mmc_packed_stop_reasons {
+	EXCEEDS_SEGMENTS = 0,
+	EXCEEDS_SECTORS,
+	WRONG_DATA_DIR,
+	FLUSH_OR_DISCARD,
+	EMPTY_QUEUE,
+	REL_WRITE,
+	THRESHOLD,
+	LARGE_SEC_ALIGN,
+	MAX_REASONS,
+};
+
+struct mmc_wr_pack_stats {
+	u32 *packing_events;
+	u32 pack_stop_reason[MAX_REASONS];
+	spinlock_t lock;
+	bool enabled;
+	bool print_in_read;
+};
+
 /* The number of MMC physical partitions.  These consist of:
  * boot partitions (2), general purpose partitions (4) in MMC v4.4.
  */
@@ -209,6 +230,64 @@
 #define MMC_BLK_DATA_AREA_GP	(1<<2)
 };
 
+#define BKOPS_NUM_OF_SEVERITY_LEVELS	3
+#define BKOPS_SEVERITY_1_INDEX		0
+#define BKOPS_SEVERITY_2_INDEX		1
+#define BKOPS_SEVERITY_3_INDEX		2
+struct mmc_bkops_stats {
+	spinlock_t		lock;
+	bool			enabled;
+	unsigned int		hpi;    /* hpi issued   */
+	unsigned int		suspend;/* card sleed issued */
+	bool			print_stats;
+	unsigned int bkops_level[BKOPS_NUM_OF_SEVERITY_LEVELS];
+	bool			ignore_card_bkops_status;
+};
+
+/**
+ * struct mmc_bkops_info - BKOPS data
+ * @dw:	Idle time bkops delayed work
+ * @host_suspend_tout_ms:	The host controller idle time,
+ * before getting into suspend
+ * @delay_ms:	The time to start the BKOPS
+ *        delayed work once MMC thread is idle
+ * @poll_for_completion:	Poll on BKOPS completion
+ * @cancel_delayed_work: A flag to indicate if the delayed work
+ *        should be cancelled
+ * @started_delayed_bkops:  A flag to indicate if the delayed
+ *        work was scheduled
+ * @sectors_changed:  number of  sectors written or
+ *       discard since the last idle BKOPS were scheduled
+ */
+struct mmc_bkops_info {
+	struct delayed_work	dw;
+	unsigned int		host_suspend_tout_ms;
+	unsigned int		delay_ms;
+	unsigned int		min_sectors_to_queue_delayed_work;
+	struct mmc_bkops_stats  bkops_stats;    /* BKOPS statistics */
+/*
+ * A default time for checking the need for non urgent BKOPS once mmcqd
+ * is idle.
+ */
+#define MMC_IDLE_BKOPS_TIME_MS 2000
+	struct work_struct	poll_for_completion;
+/* Polling timeout and interval for waiting on non-blocking BKOPs completion */
+#define BKOPS_COMPLETION_POLLING_TIMEOUT_MS 10000 /* in ms */
+#define BKOPS_COMPLETION_POLLING_INTERVAL_MS 1000 /* in ms */
+	bool			cancel_delayed_work;
+	bool			started_delayed_bkops;
+	unsigned int		sectors_changed;
+/*
+ * Since canceling the delayed work might have significant effect on the
+ * performance of small requests we won't queue the delayed work every time
+ * mmcqd thread is idle.
+ * The delayed work for idle BKOPS will be scheduled only after a significant
+ * amount of write or discard data.
+ * 100MB is chosen based on benchmark tests.
+ */
+#define BKOPS_MIN_SECTORS_TO_QUEUE_DELAYED_WORK 204800 /* 100MB */
+};
+
 /*
  * MMC device
  */
@@ -231,7 +310,6 @@
 #define MMC_CARD_SDXC		(1<<6)		/* card is SDXC */
 #define MMC_CARD_REMOVED	(1<<7)		/* card has been removed */
 #define MMC_STATE_HIGHSPEED_200	(1<<8)		/* card is in HS200 mode */
-#define MMC_STATE_SLEEP		(1<<9)		/* card is in sleep state */
 #define MMC_STATE_DOING_BKOPS	(1<<10)		/* card is doing BKOPS */
 	unsigned int		quirks; 	/* card quirks */
 #define MMC_QUIRK_LENIENT_FN0	(1<<0)		/* allow SDIO FN0 writes outside of the VS CCCR range */
@@ -248,10 +326,6 @@
 #define MMC_QUIRK_LONG_READ_TIME (1<<9)		/* Data read time > CSD says */
 						/* byte mode */
 #define MMC_QUIRK_INAND_DATA_TIMEOUT  (1<<8)    /* For incorrect data timeout */
-	unsigned int    	poweroff_notify_state;	/* eMMC4.5 notify
-							   feature */
-#define MMC_NO_POWER_NOTIFICATION	0
-#define MMC_POWERED_ON			1
 
 	unsigned int		erase_size;	/* erase size in sectors */
  	unsigned int		erase_shift;	/* if erase unit is power 2 */
@@ -283,6 +357,9 @@
 	struct mmc_part	part[MMC_NUM_PHY_PARTITION]; /* physical partitions */
 	unsigned int    nr_parts;
 
+	struct mmc_wr_pack_stats wr_pack_stats; /* packed commands stats*/
+
+	struct mmc_bkops_info	bkops_info;
 };
 
 /*
@@ -404,7 +481,6 @@
 #define mmc_sd_card_uhs(c)	((c)->state & MMC_STATE_ULTRAHIGHSPEED)
 #define mmc_card_ext_capacity(c) ((c)->state & MMC_CARD_SDXC)
 #define mmc_card_removed(c)	((c) && ((c)->state & MMC_CARD_REMOVED))
-#define mmc_card_is_sleep(c)	((c)->state & MMC_STATE_SLEEP)
 #define mmc_card_doing_bkops(c)	((c)->state & MMC_STATE_DOING_BKOPS)
 
 #define mmc_card_set_present(c)	((c)->state |= MMC_STATE_PRESENT)
@@ -417,11 +493,9 @@
 #define mmc_sd_card_set_uhs(c) ((c)->state |= MMC_STATE_ULTRAHIGHSPEED)
 #define mmc_card_set_ext_capacity(c) ((c)->state |= MMC_CARD_SDXC)
 #define mmc_card_set_removed(c) ((c)->state |= MMC_CARD_REMOVED)
-#define mmc_card_set_sleep(c)	((c)->state |= MMC_STATE_SLEEP)
 #define mmc_card_set_doing_bkops(c)	((c)->state |= MMC_STATE_DOING_BKOPS)
-
 #define mmc_card_clr_doing_bkops(c)	((c)->state &= ~MMC_STATE_DOING_BKOPS)
-#define mmc_card_clr_sleep(c)	((c)->state &= ~MMC_STATE_SLEEP)
+
 /*
  * Quirk add/remove for MMC products.
  */
@@ -511,5 +585,8 @@
 
 extern void mmc_fixup_device(struct mmc_card *card,
 			     const struct mmc_fixup *table);
+extern struct mmc_wr_pack_stats *mmc_blk_get_packed_statistics(
+			struct mmc_card *card);
+extern void mmc_blk_init_packed_statistics(struct mmc_card *card);
 
 #endif /* LINUX_MMC_CARD_H */
diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h
index 3f26a80..7247696 100644
--- a/include/linux/mmc/core.h
+++ b/include/linux/mmc/core.h
@@ -149,6 +149,9 @@
 extern int mmc_wait_for_app_cmd(struct mmc_host *, struct mmc_card *,
 	struct mmc_command *, int);
 extern void mmc_start_bkops(struct mmc_card *card, bool from_exception);
+extern void mmc_start_delayed_bkops(struct mmc_card *card);
+extern void mmc_start_idle_time_bkops(struct work_struct *work);
+extern void mmc_bkops_completion_polling(struct work_struct *work);
 extern int __mmc_switch(struct mmc_card *, u8, u8, u8, unsigned int, bool);
 extern int mmc_switch(struct mmc_card *, u8, u8, u8, unsigned int);
 extern int mmc_send_ext_csd(struct mmc_card *card, u8 *ext_csd);
@@ -170,7 +173,6 @@
 extern int mmc_can_discard(struct mmc_card *card);
 extern int mmc_can_sanitize(struct mmc_card *card);
 extern int mmc_can_secure_erase_trim(struct mmc_card *card);
-extern int mmc_can_poweroff_notify(const struct mmc_card *card);
 extern int mmc_erase_group_aligned(struct mmc_card *card, unsigned int from,
 				   unsigned int nr);
 extern unsigned int mmc_calc_max_discard(struct mmc_card *card);
@@ -194,6 +196,8 @@
 
 extern int mmc_detect_card_removed(struct mmc_host *host);
 
+extern void mmc_blk_init_bkops_statistics(struct mmc_card *card);
+
 /**
  *	mmc_claim_host - exclusively claim a host
  *	@host: mmc host to claim
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index 714cc76..f435221 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -241,14 +241,15 @@
 #define MMC_CAP2_BROKEN_VOLTAGE	(1 << 7)	/* Use the broken voltage */
 #define MMC_CAP2_DETECT_ON_ERR	(1 << 8)	/* On I/O err check card removal */
 #define MMC_CAP2_HC_ERASE_SZ	(1 << 9)	/* High-capacity erase size */
+
 #define MMC_CAP2_PACKED_RD	(1 << 10)	/* Allow packed read */
 #define MMC_CAP2_PACKED_WR	(1 << 11)	/* Allow packed write */
 #define MMC_CAP2_PACKED_CMD	(MMC_CAP2_PACKED_RD | \
 				 MMC_CAP2_PACKED_WR) /* Allow packed commands */
+#define MMC_CAP2_PACKED_WR_CONTROL (1 << 12) /* Allow write packing control */
+
 #define MMC_CAP2_SANITIZE	(1 << 13)		/* Support Sanitize */
 #define MMC_CAP2_INIT_BKOPS	    (1 << 15)	/* Need to set BKOPS_EN */
-#define MMC_CAP2_POWER_OFF_VCCQ_DURING_SUSPEND	(1 << 16)
-
 	mmc_pm_flag_t		pm_caps;	/* supported pm features */
 
 	int			clk_requests;	/* internal reference counter */
diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h
index 92888c3..237a92e 100644
--- a/include/linux/mmc/mmc.h
+++ b/include/linux/mmc/mmc.h
@@ -427,11 +427,4 @@
 #define MMC_SWITCH_MODE_CLEAR_BITS	0x02	/* Clear bits which are 1 in value */
 #define MMC_SWITCH_MODE_WRITE_BYTE	0x03	/* Set target to value */
 
-/*
- * MMC Poweroff Notify types
- */
-#define MMC_PW_OFF_NOTIFY_NONE		0
-#define MMC_PW_OFF_NOTIFY_SHORT		1
-#define MMC_PW_OFF_NOTIFY_LONG		2
-
 #endif /* LINUX_MMC_MMC_H */
diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h
index f8a3a10..08f74e6 100644
--- a/include/linux/mmzone.h
+++ b/include/linux/mmzone.h
@@ -61,6 +61,14 @@
 	MIGRATE_TYPES
 };
 
+/*
+ * Returns a list which contains the migrate types on to which
+ * an allocation falls back when the free list for the migrate
+ * type mtype is depleted.
+ * The end of the list is delimited by the type MIGRATE_RESERVE.
+ */
+extern int *get_migratetype_fallbacks(int mtype);
+
 #ifdef CONFIG_CMA
 #  define is_migrate_cma(migratetype) unlikely((migratetype) == MIGRATE_CMA)
 #  define cma_wmark_pages(zone)	zone->min_cma_pages
diff --git a/include/linux/msm_audio_acdb.h b/include/linux/msm_audio_acdb.h
index e7f06b5..e907f4a 100644
--- a/include/linux/msm_audio_acdb.h
+++ b/include/linux/msm_audio_acdb.h
@@ -39,7 +39,14 @@
 			(AUDIO_MAX_COMMON_IOCTL_NUM+16), unsigned)
 #define AUDIO_SET_AFE_RX_CAL		_IOW(AUDIO_IOCTL_MAGIC, \
 			(AUDIO_MAX_COMMON_IOCTL_NUM+17), unsigned)
-
+#define AUDIO_SET_VOCPROC_COL_CAL	_IOW(AUDIO_IOCTL_MAGIC, \
+			(AUDIO_MAX_COMMON_IOCTL_NUM+18), unsigned)
+#define AUDIO_SET_VOCSTRM_COL_CAL	_IOW(AUDIO_IOCTL_MAGIC, \
+			(AUDIO_MAX_COMMON_IOCTL_NUM+19), unsigned)
+#define AUDIO_SET_VOCVOL_COL_CAL	_IOW(AUDIO_IOCTL_MAGIC, \
+			(AUDIO_MAX_COMMON_IOCTL_NUM+20), unsigned)
+#define AUDIO_SET_VOCPROC_DEV_CFG_CAL	_IOW(AUDIO_IOCTL_MAGIC, \
+			(AUDIO_MAX_COMMON_IOCTL_NUM+21), unsigned)
 
 #define	AUDIO_MAX_ACDB_IOCTL	(AUDIO_MAX_COMMON_IOCTL_NUM+30)
 
diff --git a/include/linux/msm_ion.h b/include/linux/msm_ion.h
index 21000f9..ec043dd 100644
--- a/include/linux/msm_ion.h
+++ b/include/linux/msm_ion.h
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2012, The Linux Foundation. All rights reserved.
  *
  * This software is licensed under the terms of the GNU General Public
  * License version 2, as published by the Free Software Foundation, and
@@ -39,8 +39,10 @@
 	ION_CP_MFC_HEAP_ID = 12,
 	ION_CP_WB_HEAP_ID = 16, /* 8660 only */
 	ION_CAMERA_HEAP_ID = 20, /* 8660 only */
+	ION_PIL1_HEAP_ID = 23, /* Currently used for other PIL images */
 	ION_SF_HEAP_ID = 24,
 	ION_IOMMU_HEAP_ID = 25,
+	ION_PIL2_HEAP_ID = 26, /* Currently used for modem firmware images */
 	ION_QSECOM_HEAP_ID = 27,
 	ION_AUDIO_HEAP_ID = 28,
 
@@ -86,6 +88,8 @@
 #define ION_MFC_HEAP_NAME	"mfc"
 #define ION_WB_HEAP_NAME	"wb"
 #define ION_MM_FIRMWARE_HEAP_NAME	"mm_fw"
+#define ION_PIL1_HEAP_NAME  "pil_1"
+#define ION_PIL2_HEAP_NAME  "pil_2"
 #define ION_QSECOM_HEAP_NAME	"qsecom"
 #define ION_FMEM_HEAP_NAME	"fmem"
 
@@ -102,6 +106,12 @@
  */
 #define ION_IOMMU_UNMAP_DELAYED 1
 
+/*
+ * This flag allows clients to defer unsecuring a buffer until the buffer
+ * is actually freed.
+ */
+#define ION_UNSECURE_DELAYED	1
+
 /**
  * struct ion_cp_heap_pdata - defines a content protection heap in the given
  * platform
@@ -216,6 +226,26 @@
  * Returns 0 on success
  */
 int msm_ion_unsecure_heap_2_0(int heap_id, enum cp_mem_usage usage);
+
+/**
+ * msm_ion_secure_buffer - secure an individual buffer
+ *
+ * @client - client who has access to the buffer
+ * @handle - buffer to secure
+ * @usage - usage hint to TZ
+ * @flags - flags for the securing
+ */
+int msm_ion_secure_buffer(struct ion_client *client, struct ion_handle *handle,
+				enum cp_mem_usage usage, int flags);
+
+/**
+ * msm_ion_unsecure_buffer - unsecure an individual buffer
+ *
+ * @client - client who has access to the buffer
+ * @handle - buffer to secure
+ */
+int msm_ion_unsecure_buffer(struct ion_client *client,
+				struct ion_handle *handle);
 #else
 static inline int msm_ion_secure_heap(int heap_id)
 {
diff --git a/include/linux/msm_kgsl.h b/include/linux/msm_kgsl.h
index 5e1395e..6912087 100644
--- a/include/linux/msm_kgsl.h
+++ b/include/linux/msm_kgsl.h
@@ -2,7 +2,7 @@
 #define _MSM_KGSL_H
 
 #define KGSL_VERSION_MAJOR        3
-#define KGSL_VERSION_MINOR        13
+#define KGSL_VERSION_MINOR        14
 
 /*context flags */
 #define KGSL_CONTEXT_SAVE_GMEM		0x00000001
@@ -12,6 +12,7 @@
 #define KGSL_CONTEXT_PREAMBLE		0x00000010
 #define KGSL_CONTEXT_TRASH_STATE	0x00000020
 #define KGSL_CONTEXT_PER_CONTEXT_TS	0x00000040
+#define KGSL_CONTEXT_USER_GENERATED_TS	0x00000080
 
 #define KGSL_CONTEXT_INVALID 0xffffffff
 
@@ -45,6 +46,13 @@
 #define KGSL_MEMTYPE_MULTISAMPLE		20
 #define KGSL_MEMTYPE_KERNEL			255
 
+/*
+ * Alignment hint, passed as the power of 2 exponent.
+ * i.e 4k (2^12) would be 12, 64k (2^16)would be 16.
+ */
+#define KGSL_MEMALIGN_MASK		0x00FF0000
+#define KGSL_MEMALIGN_SHIFT		16
+
 /* generic flag values */
 #define KGSL_FLAGS_NORMALMODE  0x00000000
 #define KGSL_FLAGS_SAFEMODE    0x00000001
@@ -66,6 +74,9 @@
 #define KGSL_CLK_MEM_IFACE 0x00000010
 #define KGSL_CLK_AXI	0x00000020
 
+/* Server Side Sync Timeout in milliseconds */
+#define KGSL_SYNCOBJ_SERVER_TIMEOUT 2000
+
 /*
  * Reset status values for context
  */
diff --git a/include/linux/msm_mdp.h b/include/linux/msm_mdp.h
index 1cdc434..98050ce 100644
--- a/include/linux/msm_mdp.h
+++ b/include/linux/msm_mdp.h
@@ -399,7 +399,7 @@
 	uint32_t block;
 	uint8_t frame_cnt;
 	uint8_t bit_mask;
-	uint8_t num_bins;
+	uint16_t num_bins;
 };
 
 /*
diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h
index 03390b1..46724eb 100644
--- a/include/linux/power_supply.h
+++ b/include/linux/power_supply.h
@@ -89,6 +89,7 @@
 	POWER_SUPPLY_PROP_HEALTH,
 	POWER_SUPPLY_PROP_PRESENT,
 	POWER_SUPPLY_PROP_ONLINE,
+	POWER_SUPPLY_PROP_CHARGING_ENABLED,
 	POWER_SUPPLY_PROP_TECHNOLOGY,
 	POWER_SUPPLY_PROP_CYCLE_COUNT,
 	POWER_SUPPLY_PROP_VOLTAGE_MAX,
@@ -218,6 +219,7 @@
 extern int power_supply_set_battery_charged(struct power_supply *psy);
 extern int power_supply_set_current_limit(struct power_supply *psy, int limit);
 extern int power_supply_set_online(struct power_supply *psy, bool enable);
+extern int power_supply_set_present(struct power_supply *psy, bool enable);
 extern int power_supply_set_scope(struct power_supply *psy, int scope);
 extern int power_supply_set_charge_type(struct power_supply *psy, int type);
 extern int power_supply_set_supply_type(struct power_supply *psy,
@@ -241,6 +243,9 @@
 static inline int power_supply_set_online(struct power_supply *psy,
 							bool enable)
 							{ return -ENOSYS; }
+static inline int power_supply_set_present(struct power_supply *psy,
+							bool enable)
+							{ return -ENOSYS; }
 static inline int power_supply_set_scope(struct power_supply *psy,
 							int scope)
 							{ return -ENOSYS; }
diff --git a/include/linux/qpnp/pin.h b/include/linux/qpnp/pin.h
index fa9c30f..fff29ab 100644
--- a/include/linux/qpnp/pin.h
+++ b/include/linux/qpnp/pin.h
@@ -131,7 +131,7 @@
  *			the input is interpreted as a logical 1.
  * @out_strength:	the amount of current supplied for an output gpio,
  *			should be of the type QPNP_PIN_STRENGTH_*.
- * @select:		select alternate function for the pin. Certain pins
+ * @src_sel:		select alternate function for the pin. Certain pins
  *			can be paired (shorted) with each other. Some pins
  *			can act as alternate functions. In the context of
  *			gpio, this acts as a source select. For mpps,
@@ -159,7 +159,7 @@
 	int pull;
 	int vin_sel;
 	int out_strength;
-	int select;
+	int src_sel;
 	int master_en;
 	int aout_ref;
 	int ain_route;
diff --git a/include/linux/qpnp/qpnp-adc.h b/include/linux/qpnp/qpnp-adc.h
index f74db80..e5516ab 100644
--- a/include/linux/qpnp/qpnp-adc.h
+++ b/include/linux/qpnp/qpnp-adc.h
@@ -129,7 +129,7 @@
 	INTERNAL_RSENSE = 0,
 	EXTERNAL_RSENSE,
 	ALT_LEAD_PAIR,
-	GAIN_CALIBRATION_25MV,
+	GAIN_CALIBRATION_17P857MV,
 	OFFSET_CALIBRATION_SHORT_CADC_LEADS,
 	OFFSET_CALIBRATION_CSP_CSN,
 	OFFSET_CALIBRATION_CSP2_CSN2,
@@ -590,13 +590,31 @@
  * @channel - Channel for which the historical offset and gain is
  *	      calculated. Available channels are internal rsense,
  *	      external rsense and alternate lead pairs.
- * @offset - Offset value for the channel.
- * @gain - Gain of the channel.
+ * @offset_raw - raw Offset value for the channel.
+ * @gain_raw - raw Gain of the channel.
+ * @ideal_offset_uv - ideal offset value for the channel.
+ * @ideal_gain_nv - ideal gain for the channel.
+ * @offset_uv - converted value of offset in uV.
+ * @gain_uv - converted value of gain in uV.
  */
 struct qpnp_iadc_calib {
 	enum qpnp_iadc_channels		channel;
-	int32_t				offset;
-	int32_t				gain;
+	uint16_t			offset_raw;
+	uint16_t			gain_raw;
+	uint32_t			ideal_offset_uv;
+	uint32_t			ideal_gain_nv;
+	uint32_t			offset_uv;
+	uint32_t			gain_uv;
+};
+
+/**
+ * struct qpnp_iadc_result - IADC read result structure.
+ * @oresult_uv - Result of ADC in uV.
+ * @result_ua - Result of ADC in uA.
+ */
+struct qpnp_iadc_result {
+	int32_t				result_uv;
+	int32_t				result_ua;
 };
 
 /**
@@ -854,7 +872,7 @@
 			const struct qpnp_vadc_chan_properties *chan_prop,
 			struct qpnp_vadc_result *chan_rslt);
 { return -ENXIO; }
-static inline int32_t qpnp_vadc_is_read(void)
+static inline int32_t qpnp_vadc_is_ready(void)
 { return -ENXIO; }
 #endif
 
@@ -867,23 +885,18 @@
  * @result:	Current across rsens in mV.
  */
 int32_t qpnp_iadc_read(enum qpnp_iadc_channels channel,
-							int32_t *result);
+				struct qpnp_iadc_result *result);
 /**
- * qpnp_iadc_get_gain() - Performs gain calibration over 25mV reference
- *			  across CCADC.
- * @result:	Gain result across 25mV reference.
+ * qpnp_iadc_get_gain_and_offset() - Performs gain calibration
+ *				over 17.8571mV and offset over selected
+ *				channel. Channel can be internal rsense,
+ *				external rsense and alternate lead pair.
+ * @result:	result structure where the gain and offset is stored of
+ *		type qpnp_iadc_calib.
  */
-int32_t qpnp_iadc_get_gain(int32_t *result);
+int32_t qpnp_iadc_get_gain_and_offset(struct qpnp_iadc_calib *result);
 
 /**
- * qpnp_iadc_get_offset() - Performs offset calibration over selected
- *			    channel. Channel can be internal rsense,
- *			    external rsense and alternate lead pair.
- * @result:	Gain result across 25mV reference.
- */
-int32_t qpnp_iadc_get_offset(enum qpnp_iadc_channels channel,
-						int32_t *result);
-/**
  * qpnp_iadc_is_ready() - Clients can use this API to check if the
  *			  device is ready to use.
  * @result:	0 on success and -EPROBE_DEFER when probe for the device
@@ -892,14 +905,12 @@
 int32_t qpnp_iadc_is_ready(void);
 #else
 static inline int32_t qpnp_iadc_read(enum qpnp_iadc_channels channel,
-							int *result)
+						struct qpnp_iadc_result *result)
 { return -ENXIO; }
-static inline int32_t qpnp_iadc_get_gain(int32_t *result)
+static inline int32_t qpnp_iadc_get_gain_and_offset(struct qpnp_iadc_calib
+									*result)
 { return -ENXIO; }
-static inline int32_t qpnp_iadc_get_offset(enum qpnp_iadc_channels channel,
-						int32_t *result)
-{ return -ENXIO; }
-static inline int32_t qpnp_iadc_is_read(void)
+static inline int32_t qpnp_iadc_is_ready(void)
 { return -ENXIO; }
 #endif
 
diff --git a/include/linux/regulator/onsemi-ncp6335d.h b/include/linux/regulator/onsemi-ncp6335d.h
new file mode 100644
index 0000000..a57c3b7
--- /dev/null
+++ b/include/linux/regulator/onsemi-ncp6335d.h
@@ -0,0 +1,28 @@
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __NCP6335D_H__
+#define __NCP6335D_H__
+
+enum {
+	NCP6335D_VSEL0,
+	NCP6335D_VSEL1,
+};
+
+struct ncp6335d_platform_data {
+	struct regulator_init_data *init_data;
+	int default_vsel;
+	int slew_rate_ns;
+	int discharge_enable;
+};
+
+#endif
diff --git a/include/linux/regulator/stub-regulator.h b/include/linux/regulator/stub-regulator.h
index e7f4110..1155d82 100644
--- a/include/linux/regulator/stub-regulator.h
+++ b/include/linux/regulator/stub-regulator.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -31,5 +31,24 @@
 	int				system_uA;
 };
 
+#ifdef CONFIG_REGULATOR_STUB
+
+/**
+ * regulator_stub_init() - register platform driver for stub-regulator
+ *
+ * This initialization function should be called in systems in which driver
+ * registration ordering must be controlled precisely.
+ */
+
 int __init regulator_stub_init(void);
+
+#else
+
+static inline int __init regulator_stub_init(void)
+{
+	return -ENODEV;
+}
+
+#endif /* CONFIG_REGULATOR_STUB */
+
 #endif
diff --git a/include/linux/test-iosched.h b/include/linux/test-iosched.h
index 1e428c5..b52762c 100644
--- a/include/linux/test-iosched.h
+++ b/include/linux/test-iosched.h
@@ -129,6 +129,8 @@
  * @check_test_result_fn: Test specific test result checking
  *			callback
  * @get_test_case_str_fn: Test specific function to get the test name
+ * @test_duration:	A jiffies value saved for timing
+ *			calculations
  * @data:		Test specific private data
  */
 struct test_info {
@@ -139,6 +141,7 @@
 	check_test_result_fn *check_test_result_fn;
 	post_test_fn *post_test_fn;
 	get_test_case_str_fn *get_test_case_str_fn;
+	unsigned long test_duration;
 	void *data;
 };
 
diff --git a/include/linux/tsif_api.h b/include/linux/tsif_api.h
index fc4d20b..0c18228 100644
--- a/include/linux/tsif_api.h
+++ b/include/linux/tsif_api.h
@@ -3,8 +3,7 @@
  *
  * Kernel API
  *
- * Copyright (c) 2009-2010, Code Aurora Forum. All rights
- * reserved.
+ * Copyright (c) 2009-2010, 2012, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -124,11 +123,13 @@
  * Should be called prior to any other tsif_XXX function.
  */
 void *tsif_attach(int id, void (*notify)(void *client_data), void *client_data);
+
 /**
  * tsif_detach - detach from device
  * @cookie:    TSIF cookie previously obtained with tsif_attach()
  */
 void tsif_detach(void *cookie);
+
 /**
  * tsif_get_info - get data buffer info
  * @cookie:    TSIF cookie previously obtained with tsif_attach()
@@ -140,6 +141,7 @@
  * using data; since data buffer will be re-allocated on tsif_start()
  */
 void tsif_get_info(void *cookie, void **pdata, int *psize);
+
 /**
  * tsif_set_mode - set TSIF mode
  * @cookie:    TSIF cookie previously obtained with tsif_attach()
@@ -150,6 +152,7 @@
  * Mode may be changed only when TSIF device is stopped.
  */
 int tsif_set_mode(void *cookie, int mode);
+
 /**
  * tsif_set_time_limit - set TSIF time limit
  * @cookie:    TSIF cookie previously obtained with tsif_attach()
@@ -160,6 +163,7 @@
  * Time limit may be changed only when TSIF device is stopped.
  */
 int tsif_set_time_limit(void *cookie, u32 value);
+
 /**
  * tsif_set_buf_config - configure data buffer
  *
@@ -180,6 +184,7 @@
  *   stats
  */
 int tsif_set_buf_config(void *cookie, u32 pkts_in_chunk, u32 chunks_in_buf);
+
 /**
  * tsif_get_state - query current data buffer information
  * @cookie:    TSIF cookie previously obtained with tsif_attach()
@@ -188,6 +193,51 @@
  * @state:     if not NULL, state will be stored here
  */
 void tsif_get_state(void *cookie, int *ri, int *wi, enum tsif_state *state);
+
+/**
+ * tsif_set_clk_inverse - set whether to inverse the clock signal.
+ * @cookie:   TSIF cookie previously obtained with tsif_attach()
+ * @inverse:  1 to inverse the clock, 0 otherwise. Default is 0.
+ *
+ * Return      error code
+ *
+ * Setting may be changed only when TSIF device is stopped.
+ */
+int tsif_set_clk_inverse(void *cookie, int inverse);
+
+/**
+ * tsif_set_data_inverse - set whether to inverse the data signal.
+ * @cookie:   TSIF cookie previously obtained with tsif_attach()
+ * @inverse:  1 to inverse the clock, 0 otherwise. Default is 0.
+ *
+ * Return      error code
+ *
+ * Setting may be changed only when TSIF device is stopped.
+ */
+int tsif_set_data_inverse(void *cookie, int inverse);
+
+/**
+ * tsif_set_sync_inverse - set whether to inverse the sync signal.
+ * @cookie:   TSIF cookie previously obtained with tsif_attach()
+ * @inverse:  1 to inverse the clock, 0 otherwise. Default is 0.
+ *
+ * Return      error code
+ *
+ * Setting may be changed only when TSIF device is stopped.
+ */
+int tsif_set_sync_inverse(void *cookie, int inverse);
+
+/**
+ * tsif_set_enable_inverse - set whether to inverse the enable signal.
+ * @cookie:   TSIF cookie previously obtained with tsif_attach()
+ * @inverse:  1 to inverse the clock, 0 otherwise. Default is 0.
+ *
+ * Return      error code
+ *
+ * Setting may be changed only when TSIF device is stopped.
+ */
+int tsif_set_enable_inverse(void *cookie, int inverse);
+
 /**
  * tsif_start - start data acquisition
  * @cookie:    TSIF cookie previously obtained with tsif_attach()
@@ -195,6 +245,7 @@
  * Return      error code
  */
 int tsif_start(void *cookie);
+
 /**
  * tsif_stop - stop data acquisition
  * @cookie:    TSIF cookie previously obtained with tsif_attach()
@@ -203,6 +254,7 @@
  * query data buffer info using tsif_get_info() and reset its data pointers.
  */
 void tsif_stop(void *cookie);
+
 /**
  * tsif_reclaim_packets - inform that buffer space may be reclaimed
  * @cookie:    TSIF cookie previously obtained with tsif_attach()
diff --git a/include/linux/tspp.h b/include/linux/tspp.h
index 3f0cc81..551fbb0 100644
--- a/include/linux/tspp.h
+++ b/include/linux/tspp.h
@@ -42,6 +42,10 @@
 struct tspp_select_source {
 	enum tspp_source source;
 	enum tspp_tsif_mode mode;
+	int clk_inverse;
+	int data_inverse;
+	int sync_inverse;
+	int enable_inverse;
 };
 
 struct tspp_pid {
diff --git a/include/linux/usb/audio.h b/include/linux/usb/audio.h
index a54b825..c3ea237 100644
--- a/include/linux/usb/audio.h
+++ b/include/linux/usb/audio.h
@@ -175,6 +175,7 @@
 	__u8  baInterfaceNr[n];					\
 } __attribute__ ((packed))
 
+DECLARE_UAC_AC_HEADER_DESCRIPTOR(2);
 /* 4.3.2.1 Input Terminal Descriptor */
 struct uac_input_terminal_descriptor {
 	__u8  bLength;			/* in bytes: 12 */
@@ -454,6 +455,7 @@
 	__u8  tSamFreq[n][3];					\
 } __attribute__ ((packed))
 
+DECLARE_UAC_FORMAT_TYPE_I_DISCRETE_DESC(1);
 #define UAC_FORMAT_TYPE_I_DISCRETE_DESC_SIZE(n)	(8 + (n * 3))
 
 struct uac_format_type_i_ext_descriptor {
diff --git a/include/linux/usb/ch9.h b/include/linux/usb/ch9.h
index c918b74..1608e84 100644
--- a/include/linux/usb/ch9.h
+++ b/include/linux/usb/ch9.h
@@ -88,6 +88,8 @@
 #define USB_REQ_GET_INTERFACE		0x0A
 #define USB_REQ_SET_INTERFACE		0x0B
 #define USB_REQ_SYNCH_FRAME		0x0C
+#define USB_REQ_SET_SEL			0x30
+#define USB_REQ_SET_ISOCH_DELAY		0x31
 
 #define USB_REQ_SET_ENCRYPTION		0x0D	/* Wireless USB */
 #define USB_REQ_GET_ENCRYPTION		0x0E
diff --git a/include/linux/usb/composite.h b/include/linux/usb/composite.h
index 6938a86..742b9e4 100644
--- a/include/linux/usb/composite.h
+++ b/include/linux/usb/composite.h
@@ -365,6 +365,13 @@
 
 	/* protects deactivations and delayed_status counts*/
 	spinlock_t			lock;
+
+	/*
+	 * specify the mA units for the bMaxPower field in
+	 * the configuration descriptor. Should be 2mA for HS
+	 * and 8mA for SS.
+	 */
+	int vbus_draw_units;
 };
 
 extern int usb_string_id(struct usb_composite_dev *c);
diff --git a/include/linux/usb/ehci_pdriver.h b/include/linux/usb/ehci_pdriver.h
index 1894f42..4c1b7a0 100644
--- a/include/linux/usb/ehci_pdriver.h
+++ b/include/linux/usb/ehci_pdriver.h
@@ -41,6 +41,7 @@
 	unsigned	big_endian_mmio:1;
 	unsigned	port_power_on:1;
 	unsigned	port_power_off:1;
+	unsigned	pool_64_bit_align:1;
 };
 
 #endif /* __USB_CORE_EHCI_PDRIVER_H */
diff --git a/include/linux/usb/f_accessory.h b/include/linux/usb/f_accessory.h
index 5b2dcf9..61ebe0a 100644
--- a/include/linux/usb/f_accessory.h
+++ b/include/linux/usb/f_accessory.h
@@ -36,13 +36,15 @@
 #define ACCESSORY_STRING_URI            4
 #define ACCESSORY_STRING_SERIAL         5
 
-/* Control request for retrieving device's protocol version (currently 1)
+/* Control request for retrieving device's protocol version
  *
  *	requestType:    USB_DIR_IN | USB_TYPE_VENDOR
  *	request:        ACCESSORY_GET_PROTOCOL
  *	value:          0
  *	index:          0
  *	data            version number (16 bits little endian)
+ *                     1 for original accessory support
+ *                     2 adds HID and device to host audio support
  */
 #define ACCESSORY_GET_PROTOCOL  51
 
@@ -70,6 +72,65 @@
  */
 #define ACCESSORY_START         53
 
+/* Control request for registering a HID device.
+ * Upon registering, a unique ID is sent by the accessory in the
+ * value parameter. This ID will be used for future commands for
+ * the device
+ *
+ *	requestType:    USB_DIR_OUT | USB_TYPE_VENDOR
+ *	request:        ACCESSORY_REGISTER_HID_DEVICE
+ *	value:          Accessory assigned ID for the HID device
+ *	index:          total length of the HID report descriptor
+ *	data            none
+ */
+#define ACCESSORY_REGISTER_HID         54
+
+/* Control request for unregistering a HID device.
+ *
+ *	requestType:    USB_DIR_OUT | USB_TYPE_VENDOR
+ *	request:        ACCESSORY_REGISTER_HID
+ *	value:          Accessory assigned ID for the HID device
+ *	index:          0
+ *	data            none
+ */
+#define ACCESSORY_UNREGISTER_HID         55
+
+/* Control request for sending the HID report descriptor.
+ * If the HID descriptor is longer than the endpoint zero max packet size,
+ * the descriptor will be sent in multiple ACCESSORY_SET_HID_REPORT_DESC
+ * commands. The data for the descriptor must be sent sequentially
+ * if multiple packets are needed.
+ *
+ *	requestType:    USB_DIR_OUT | USB_TYPE_VENDOR
+ *	request:        ACCESSORY_SET_HID_REPORT_DESC
+ *	value:          Accessory assigned ID for the HID device
+ *	index:          offset of data in descriptor
+ *                      (needed when HID descriptor is too big for one packet)
+ *	data            the HID report descriptor
+ */
+#define ACCESSORY_SET_HID_REPORT_DESC         56
+
+/* Control request for sending HID events.
+ *
+ *	requestType:    USB_DIR_OUT | USB_TYPE_VENDOR
+ *	request:        ACCESSORY_SEND_HID_EVENT
+ *	value:          Accessory assigned ID for the HID device
+ *	index:          0
+ *	data            the HID report for the event
+ */
+#define ACCESSORY_SEND_HID_EVENT         57
+
+/* Control request for setting the audio mode.
+ *
+ *	requestType:	USB_DIR_OUT | USB_TYPE_VENDOR
+ *	request:        ACCESSORY_SET_AUDIO_MODE
+ *	value:          0 - no audio
+ *                     1 - device to host, 44100 16-bit stereo PCM
+ *	index:          0
+ *	data            none
+ */
+#define ACCESSORY_SET_AUDIO_MODE         58
+
 /* ioctls for retrieving strings set by the host */
 #define ACCESSORY_GET_STRING_MANUFACTURER   _IOW('M', 1, char[256])
 #define ACCESSORY_GET_STRING_MODEL          _IOW('M', 2, char[256])
@@ -79,5 +140,7 @@
 #define ACCESSORY_GET_STRING_SERIAL         _IOW('M', 6, char[256])
 /* returns 1 if there is a start request pending */
 #define ACCESSORY_IS_START_REQUESTED        _IO('M', 7)
+/* returns audio mode (set via the ACCESSORY_SET_AUDIO_MODE control request) */
+#define ACCESSORY_GET_AUDIO_MODE            _IO('M', 8)
 
 #endif /* __LINUX_USB_F_ACCESSORY_H */
diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h
index 2e51781..82044f7 100644
--- a/include/linux/usb/gadget.h
+++ b/include/linux/usb/gadget.h
@@ -567,14 +567,7 @@
  */
 static inline int gadget_is_dualspeed(struct usb_gadget *g)
 {
-#ifdef CONFIG_USB_GADGET_DUALSPEED
-	/* runtime test would check "g->max_speed" ... that might be
-	 * useful to work around hardware bugs, but is mostly pointless
-	 */
-	return 1;
-#else
-	return 0;
-#endif
+	return g->max_speed >= USB_SPEED_HIGH;
 }
 
 /**
@@ -584,15 +577,7 @@
  */
 static inline int gadget_is_superspeed(struct usb_gadget *g)
 {
-#ifdef CONFIG_USB_GADGET_SUPERSPEED
-	/*
-	 * runtime test would check "g->max_speed" ... that might be
-	 * useful to work around hardware bugs, but is mostly pointless
-	 */
-	return 1;
-#else
-	return 0;
-#endif
+	return g->max_speed >= USB_SPEED_SUPER;
 }
 
 /**
diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h
index dd091cd..268aa48 100644
--- a/include/linux/usb/hcd.h
+++ b/include/linux/usb/hcd.h
@@ -351,6 +351,7 @@
 	void	(*dump_regs)(struct usb_hcd *);
 	void	(*enable_ulpi_control)(struct usb_hcd *hcd, u32 linestate);
 	void	(*disable_ulpi_control)(struct usb_hcd *hcd);
+	void	(*set_autosuspend_delay)(struct usb_device *);
 };
 
 extern int usb_hcd_link_urb_to_ep(struct usb_hcd *hcd, struct urb *urb);
diff --git a/include/linux/usb/msm_hsusb.h b/include/linux/usb/msm_hsusb.h
index 920cf77..a998ac2 100644
--- a/include/linux/usb/msm_hsusb.h
+++ b/include/linux/usb/msm_hsusb.h
@@ -25,7 +25,7 @@
 #include <linux/wakelock.h>
 #include <linux/pm_qos.h>
 #include <linux/hrtimer.h>
-
+#include <linux/power_supply.h>
 /*
  * The following are bit fields describing the usb_request.udc_priv word.
  * These bit fields are set by function drivers that wish to queue
@@ -192,9 +192,8 @@
  * @mhl_enable: indicates MHL connector or not.
  * @disable_reset_on_disconnect: perform USB PHY and LINK reset
  *              on USB cable disconnection.
- * @enable_dcd: Enable Data Contact Detection circuit. if not set
- *              wait for 600msec before proceeding to primary
- *              detection.
+ * @pnoc_errata_fix: workaround needed for PNOC hardware bug that
+ *              affects USB performance.
  * @enable_lpm_on_suspend: Enable the USB core to go into Low
  *              Power Mode, when USB bus is suspended but cable
  *              is connected.
@@ -216,7 +215,7 @@
 	unsigned int mpm_otgsessvld_int;
 	bool mhl_enable;
 	bool disable_reset_on_disconnect;
-	bool enable_dcd;
+	bool pnoc_errata_fix;
 	bool enable_lpm_on_dev_suspend;
 	bool core_clk_always_on_workaround;
 	struct msm_bus_scale_pdata *bus_scale_table;
@@ -384,6 +383,10 @@
 	u8 active_tmout;
 	struct hrtimer timer;
 	enum usb_vdd_type vdd_type;
+	struct power_supply usb_psy;
+	unsigned int online;
+	unsigned int host_mode;
+	unsigned int current_max;
 };
 
 struct msm_hsic_host_platform_data {
diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h
index 07beb50..41ff312 100644
--- a/include/linux/videodev2.h
+++ b/include/linux/videodev2.h
@@ -1804,6 +1804,12 @@
 	V4L2_MPEG_VIDC_VIDEO_H264_AU_DELIMITER_DISABLED = 0,
 	V4L2_MPEG_VIDC_VIDEO_H264_AU_DELIMITER_ENABLED = 1
 };
+#define V4L2_CID_MPEG_VIDC_VIDEO_SYNC_FRAME_DECODE \
+		(V4L2_CID_MPEG_MSM_VIDC_BASE + 23)
+enum v4l2_mpeg_vidc_video_sync_frame_decode {
+	V4L2_MPEG_VIDC_VIDEO_SYNC_FRAME_DECODE_DISABLE = 0,
+	V4L2_MPEG_VIDC_VIDEO_SYNC_FRAME_DECODE_ENABLE = 1
+};
 /*  Camera class control IDs */
 #define V4L2_CID_CAMERA_CLASS_BASE 	(V4L2_CTRL_CLASS_CAMERA | 0x900)
 #define V4L2_CID_CAMERA_CLASS 		(V4L2_CTRL_CLASS_CAMERA | 1)
@@ -2366,6 +2372,7 @@
 #define V4L2_EVENT_MSM_VIDC_PORT_SETTINGS_CHANGED_INSUFFICIENT	\
 		(V4L2_EVENT_MSM_VIDC_START + 3)
 #define V4L2_EVENT_MSM_VIDC_CLOSE_DONE	(V4L2_EVENT_MSM_VIDC_START + 4)
+#define V4L2_EVENT_MSM_VIDC_SYS_ERROR	(V4L2_EVENT_MSM_VIDC_START + 5)
 
 
 /* Payload for V4L2_EVENT_VSYNC */
diff --git a/include/media/Kbuild b/include/media/Kbuild
index 4b6e6a9..70f6334 100644
--- a/include/media/Kbuild
+++ b/include/media/Kbuild
@@ -7,3 +7,4 @@
 header-y += msm_gestures.h
 header-y += msm_mercury.h
 header-y += msm_jpeg.h
+header-y += msm_media_info.h
diff --git a/include/media/gpio-ir-recv.h b/include/media/gpio-ir-recv.h
index 3eab611..ffdf2f0 100644
--- a/include/media/gpio-ir-recv.h
+++ b/include/media/gpio-ir-recv.h
@@ -17,6 +17,7 @@
 	unsigned int gpio_nr;
 	bool active_low;
 	bool can_wakeup;
+	u32 swfi_latency;
 };
 
 #endif /* __GPIO_IR_RECV_H__ */
diff --git a/include/media/msm_camera.h b/include/media/msm_camera.h
index 6658b8c..9af15e3 100644
--- a/include/media/msm_camera.h
+++ b/include/media/msm_camera.h
@@ -536,6 +536,8 @@
 #define CMD_STATS_BHIST_BUF_RELEASE 58
 #define CMD_VFE_PIX_SOF_COUNT_UPDATE 59
 #define CMD_VFE_COUNT_PIX_SOF_ENABLE 60
+#define CMD_STATS_BE_ENABLE 61
+#define CMD_STATS_BE_BUF_RELEASE 62
 
 #define CMD_AXI_CFG_PRIM               BIT(8)
 #define CMD_AXI_CFG_PRIM_ALL_CHNLS     BIT(9)
@@ -599,7 +601,8 @@
 #define MSM_PMEM_BAYER_GRID		20
 #define MSM_PMEM_BAYER_FOCUS	21
 #define MSM_PMEM_BAYER_HIST		22
-#define MSM_PMEM_MAX            23
+#define MSM_PMEM_BAYER_EXPOSURE 23
+#define MSM_PMEM_MAX            24
 
 #define STAT_AEAW			0
 #define STAT_AEC			1
@@ -611,8 +614,9 @@
 #define STAT_SKIN			7
 #define STAT_BG				8
 #define STAT_BF				9
-#define STAT_BHIST			10
-#define STAT_MAX			11
+#define STAT_BE				10
+#define STAT_BHIST			11
+#define STAT_MAX			12
 
 #define FRAME_PREVIEW_OUTPUT1		0
 #define FRAME_PREVIEW_OUTPUT2		1
@@ -631,6 +635,7 @@
 	MSM_STATS_TYPE_SKIN,    /* legacy based SKIN */
 	MSM_STATS_TYPE_BG,  /* Bayer Grids */
 	MSM_STATS_TYPE_BF,  /* Bayer Focus */
+	MSM_STATS_TYPE_BE,  /* Bayer Exposure*/
 	MSM_STATS_TYPE_BHIST,   /* Bayer Hist */
 	MSM_STATS_TYPE_AE_AW,   /* legacy stats for vfe 2.x*/
 	MSM_STATS_TYPE_COMP, /* Composite stats */
@@ -807,6 +812,7 @@
 	struct stats_buff aec;
 	struct stats_buff awb;
 	struct stats_buff af;
+	struct stats_buff be;
 	struct stats_buff ihist;
 	struct stats_buff rs;
 	struct stats_buff cs;
diff --git a/include/media/msm_isp.h b/include/media/msm_isp.h
index faaa522..8b4ae19 100644
--- a/include/media/msm_isp.h
+++ b/include/media/msm_isp.h
@@ -70,6 +70,7 @@
 #define MSG_ID_RDI2_UPDATE_ACK          51
 #define MSG_ID_PIX0_UPDATE_ACK          52
 #define MSG_ID_PREV_STOP_ACK            53
+#define MSG_ID_STATS_BE                 54
 
 
 /* ISP command IDs */
@@ -236,7 +237,8 @@
 #define VFE_CMD_COLORXFORM_ENC_UPDATE                   160
 #define VFE_CMD_COLORXFORM_VIEW_UPDATE                  161
 #define VFE_CMD_TEST_GEN_CFG                            162
-
+#define VFE_CMD_STATS_BE_START                          163
+#define VFE_CMD_STATS_BE_STOP                           164
 struct msm_isp_cmd {
 	int32_t  id;
 	uint16_t length;
diff --git a/include/media/msm_media_info.h b/include/media/msm_media_info.h
new file mode 100644
index 0000000..3098bfe
--- /dev/null
+++ b/include/media/msm_media_info.h
@@ -0,0 +1,113 @@
+#ifndef __MEDIA_INFO_H__
+#define __MEDIA_INFO_H__
+
+#ifndef ALIGN
+#define ALIGN(__sz, __align) (((__sz) + (__align-1)) & (~(__align-1)))
+#endif
+
+enum color_fmts {
+	COLOR_FMT_NV12,
+};
+
+static inline unsigned int VENUS_Y_STRIDE(int color_fmt, int width)
+{
+	unsigned int alignment, stride = 0;
+	if (!width)
+		goto invalid_input;
+
+	switch (color_fmt) {
+	case COLOR_FMT_NV12:
+		alignment = 32;
+		stride = ALIGN(width, alignment);
+		break;
+	default:
+		break;
+	}
+invalid_input:
+	return stride;
+}
+
+static inline unsigned int VENUS_UV_STRIDE(int color_fmt, int width)
+{
+	unsigned int alignment, stride = 0;
+	if (!width)
+		goto invalid_input;
+
+	switch (color_fmt) {
+	case COLOR_FMT_NV12:
+		alignment = 32;
+		stride = ALIGN(((width + 1) >> 1), alignment) << 1;
+		break;
+	default:
+		break;
+	}
+invalid_input:
+	return stride;
+}
+
+static inline unsigned int VENUS_Y_SCANLINES(int color_fmt, int height)
+{
+	unsigned int alignment, sclines = 0;
+	if (!height)
+		goto invalid_input;
+
+	switch (color_fmt) {
+	case COLOR_FMT_NV12:
+		alignment = 32;
+		sclines = ALIGN(height, alignment);
+		break;
+	default:
+		break;
+	}
+invalid_input:
+	return sclines;
+}
+
+static inline unsigned int VENUS_UV_SCANLINES(int color_fmt, int height)
+{
+	unsigned int alignment, sclines = 0;
+	if (!height)
+		goto invalid_input;
+
+	switch (color_fmt) {
+	case COLOR_FMT_NV12:
+		alignment = 32;
+		sclines = ALIGN(((height + 1) >> 1), alignment);
+		break;
+	default:
+		break;
+	}
+invalid_input:
+	return sclines;
+}
+
+static inline unsigned int VENUS_BUFFER_SIZE(
+	int color_fmt, int width, int height)
+{
+	unsigned int uv_alignment;
+	unsigned int size = 0;
+	unsigned int y_plane, uv_plane, y_stride,
+		uv_stride, y_sclines, uv_sclines;
+	if (!width || !height)
+		goto invalid_input;
+
+	y_stride = VENUS_Y_STRIDE(color_fmt, width);
+	uv_stride = VENUS_UV_STRIDE(color_fmt, width);
+	y_sclines = VENUS_Y_SCANLINES(color_fmt, height);
+	uv_sclines = VENUS_UV_SCANLINES(color_fmt, height);
+	switch (color_fmt) {
+	case COLOR_FMT_NV12:
+		uv_alignment = 32;
+		y_plane = y_stride * y_sclines;
+		uv_plane = uv_stride * uv_sclines + uv_alignment;
+		size = y_plane + uv_plane;
+		size = ALIGN(size, 4096);
+		break;
+	default:
+		break;
+	}
+invalid_input:
+	return size;
+}
+
+#endif
diff --git a/include/media/msm_vidc.h b/include/media/msm_vidc.h
index 34464c6..0fd11a3 100644
--- a/include/media/msm_vidc.h
+++ b/include/media/msm_vidc.h
@@ -69,4 +69,5 @@
 		struct v4l2_event_subscription *sub);
 int msm_vidc_dqevent(void *instance, struct v4l2_event *event);
 int msm_vidc_wait(void *instance);
+int msm_vidc_s_parm(void *instance, struct v4l2_streamparm *a);
 #endif
diff --git a/include/media/radio-iris.h b/include/media/radio-iris.h
index db69518..0efeff4 100644
--- a/include/media/radio-iris.h
+++ b/include/media/radio-iris.h
@@ -363,6 +363,53 @@
 #define HCI_REQ_CANCELED  2
 #define HCI_REQ_STATUS    3
 
+#define MAX_RAW_RDS_GRPS	21
+
+#define RDSGRP_DATA_OFFSET	 0x1
+
+/*RT PLUS*/
+#define DUMMY_CLASS		0
+#define RT_PLUS_LEN_1_TAG	3
+#define RT_ERT_FLAG_BIT		5
+
+/*TAG1*/
+#define TAG1_MSB_OFFSET		3
+#define TAG1_MSB_MASK		7
+#define TAG1_LSB_OFFSET		5
+#define TAG1_POS_MSB_MASK	31
+#define TAG1_POS_MSB_OFFSET	1
+#define TAG1_POS_LSB_OFFSET	7
+#define TAG1_LEN_OFFSET		1
+#define TAG1_LEN_MASK		63
+
+/*TAG2*/
+#define TAG2_MSB_OFFSET		5
+#define TAG2_MSB_MASK		1
+#define TAG2_LSB_OFFSET		3
+#define TAG2_POS_MSB_MASK	7
+#define TAG2_POS_MSB_OFFSET	3
+#define TAG2_POS_LSB_OFFSET	5
+#define TAG2_LEN_MASK		31
+
+#define AGT_MASK		31
+/*Extract 5 left most bits of lsb of 2nd block*/
+#define AGT(x)			(x & AGT_MASK)
+/*16 bits of 4th block*/
+#define AID(lsb, msb)		((msb << 8) | (lsb))
+/*Extract 5 right most bits of msb of 2nd block*/
+#define GTC(blk2msb)		(blk2msb >> 3)
+
+#define GRP_3A			0x6
+#define RT_PLUS_AID		0x4bd7
+
+/*ERT*/
+#define ERT_AID			0x6552
+#define CARRIAGE_RETURN		0x000D
+#define MAX_ERT_SEGMENT		31
+#define ERT_FORMAT_DIR_BIT	1
+
+#define EXTRACT_BIT(data, bit_pos) ((data & (1 << bit_pos)) >> bit_pos)
+
 struct hci_ev_tune_status {
 	__u8    sub_event;
 	__le32  station_freq;
@@ -375,9 +422,19 @@
 	__u8	intf_det_th;
 } __packed;
 
+struct rds_blk_data {
+	__u8	rdsMsb;
+	__u8	rdsLsb;
+	__u8	blockStatus;
+} __packed;
+
+struct rds_grp_data {
+	struct rds_blk_data rdsBlk[4];
+} __packed;
+
 struct hci_ev_rds_rx_data {
 	__u8    num_rds_grps;
-	__u8    rds_grp_data[12];
+	struct  rds_grp_data rds_grp_data[MAX_RAW_RDS_GRPS];
 } __packed;
 
 struct hci_ev_prg_service {
@@ -628,7 +685,10 @@
 	IRIS_EVT_NEW_AF_LIST,
 	IRIS_EVT_TXRDSDAT,
 	IRIS_EVT_TXRDSDONE,
-	IRIS_EVT_RADIO_DISABLED
+	IRIS_EVT_RADIO_DISABLED,
+	IRIS_EVT_NEW_ODA,
+	IRIS_EVT_NEW_RT_PLUS,
+	IRIS_EVT_NEW_ERT,
 };
 enum emphasis_type {
 	FM_RX_EMP75 = 0x0,
@@ -660,7 +720,7 @@
 	IRIS_REGION_OTHER
 };
 
-#define STD_BUF_SIZE        (128)
+#define STD_BUF_SIZE        (256)
 
 enum iris_buf_t {
 	IRIS_BUF_SRCH_LIST,
@@ -674,7 +734,9 @@
 	IRIS_BUF_RDS_CNTRS,
 	IRIS_BUF_RD_DEFAULT,
 	IRIS_BUF_CAL_DATA,
-	IRIS_BUF_MAX
+	IRIS_BUF_RT_PLUS,
+	IRIS_BUF_ERT,
+	IRIS_BUF_MAX,
 };
 
 enum iris_xfr_t {
diff --git a/include/media/tavarua.h b/include/media/tavarua.h
index 1cccb2b..881b851 100644
--- a/include/media/tavarua.h
+++ b/include/media/tavarua.h
@@ -52,7 +52,7 @@
 #define SRCH_MASK                  (1 << SRCH200KHZ_OFFSET)
 
 /* Standard buffer size */
-#define STD_BUF_SIZE               (128)
+#define STD_BUF_SIZE               (256)
 /* Search direction */
 #define SRCH_DIR_UP                 (0)
 #define SRCH_DIR_DOWN               (1)
diff --git a/include/media/vcap_fmt.h b/include/media/vcap_fmt.h
index 3b1bd7c2..13b3f05 100644
--- a/include/media/vcap_fmt.h
+++ b/include/media/vcap_fmt.h
@@ -24,12 +24,13 @@
 #define VCAP_VC_NPL_OFLOW_ERR_EVENT 4
 #define VCAP_VC_LBUF_OFLOW_ERR_EVENT 5
 #define VCAP_VC_BUF_OVERWRITE_EVENT 6
-#define VCAP_VP_REG_R_ERR_EVENT 7
-#define VCAP_VP_REG_W_ERR_EVENT 8
-#define VCAP_VP_IN_HEIGHT_ERR_EVENT 9
-#define VCAP_VP_IN_WIDTH_ERR_EVENT 10
-#define VCAP_VC_UNEXPECT_BUF_DONE 11
-#define VCAP_MAX_NOTIFY_EVENT 12
+#define VCAP_VC_VSYNC_SEQ_ERR 7
+#define VCAP_VP_REG_R_ERR_EVENT 8
+#define VCAP_VP_REG_W_ERR_EVENT 9
+#define VCAP_VP_IN_HEIGHT_ERR_EVENT 10
+#define VCAP_VP_IN_WIDTH_ERR_EVENT 11
+#define VCAP_VC_UNEXPECT_BUF_DONE 12
+#define VCAP_MAX_NOTIFY_EVENT 13
 
 enum hal_vcap_mode {
 	HAL_VCAP_MODE_PRO = 0,
diff --git a/include/media/vcap_v4l2.h b/include/media/vcap_v4l2.h
index b2a538c..55d67bf 100644
--- a/include/media/vcap_v4l2.h
+++ b/include/media/vcap_v4l2.h
@@ -38,9 +38,31 @@
 	} while (0)
 
 #define VCAP_USEC (1000000)
+
+#define VCAP_STRIDE_ALIGN 0x10
+#define VCAP_STRIDE_CALC(x) (((x / VCAP_STRIDE_ALIGN) + \
+			(!(!(x % VCAP_STRIDE_ALIGN)))) * \
+			VCAP_STRIDE_ALIGN)
+
 #define VCAP_BASE (dev->vcapbase)
 #define VCAP_OFFSET(off) (VCAP_BASE + off)
 
+struct reg_range {
+	u32 min_val;
+	u32 max_val;
+};
+
+#define VCAP_REG_RANGE_1_MIN	0x0
+#define VCAP_REG_RANGE_1_MAX	0x48
+#define VCAP_REG_RANGE_2_MIN	0x100
+#define VCAP_REG_RANGE_2_MAX	0x104
+#define VCAP_REG_RANGE_3_MIN	0x400
+#define VCAP_REG_RANGE_3_MAX	0x7F0
+#define VCAP_REG_RANGE_4_MIN	0x800
+#define VCAP_REG_RANGE_4_MAX	0x8A0
+#define VCAP_REG_RANGE_5_MIN	0xC00
+#define VCAP_REG_RANGE_5_MAX	0xDF0
+
 #define VCAP_SW_RESET_REQ (VCAP_BASE + 0x024)
 #define VCAP_SW_RESET_STATUS (VCAP_BASE + 0x028)
 
@@ -87,7 +109,8 @@
 	uint8_t					tot_buf;
 	uint8_t					buf_num;
 
-	bool					top_field;
+	bool					field1;
+	bool					field_dropped;
 
 	struct timeval			vc_ts;
 	uint32_t				last_ts;
@@ -128,6 +151,16 @@
 	struct vcap_client_data *cd;
 };
 
+struct vcap_debugfs_params {
+	atomic_t vc_drop_count;
+	uint32_t vc_timestamp;
+	uint32_t vp_timestamp;
+	uint32_t vp_ewma;/* Exponential moving average */
+	uint32_t clk_rate;
+	uint32_t bw_request;
+	uint32_t reg_addr;
+};
+
 struct vcap_dev {
 	struct v4l2_device		v4l2_dev;
 
@@ -152,6 +185,11 @@
 
 	uint32_t				bus_client_handle;
 
+	int						domain_num;
+	struct device			*vc_iommu_ctx;
+	struct device			*vp_iommu_ctx;
+	struct iommu_domain		*iommu_vcap_domain;
+
 	struct vcap_client_data *vc_client;
 	struct vcap_client_data *vp_client;
 
@@ -176,6 +214,7 @@
 
 	struct nr_param			nr_param;
 	bool					nr_update;
+	struct vcap_debugfs_params	dbg_p;
 };
 
 struct vp_format_data {
diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h
index 602fe59..14ccf3e 100644
--- a/include/net/bluetooth/mgmt.h
+++ b/include/net/bluetooth/mgmt.h
@@ -267,6 +267,11 @@
 
 #define MGMT_OP_LE_CANCEL_CREATE_CONN_WHITE_LIST	0xE005
 
+#define MGMT_OP_LE_CANCEL_CREATE_CONN	0xE006
+struct mgmt_cp_le_cancel_create_conn {
+	bdaddr_t	bdaddr;
+} __packed;
+
 #define MGMT_EV_CMD_COMPLETE		0x0001
 struct mgmt_ev_cmd_complete {
 	__le16 opcode;
diff --git a/include/net/scm.h b/include/net/scm.h
index d456f4c..0c0017c 100644
--- a/include/net/scm.h
+++ b/include/net/scm.h
@@ -71,9 +71,11 @@
 }
 
 static __inline__ int scm_send(struct socket *sock, struct msghdr *msg,
-			       struct scm_cookie *scm)
+			       struct scm_cookie *scm, bool forcecreds)
 {
 	memset(scm, 0, sizeof(*scm));
+	if (forcecreds)
+		scm_set_cred(scm, task_tgid(current), current_cred());
 	unix_get_peersec_dgram(sock, scm);
 	if (msg->msg_controllen <= 0)
 		return 0;
diff --git a/include/sound/apr_audio-v2.h b/include/sound/apr_audio-v2.h
index d902881..07179e9 100644
--- a/include/sound/apr_audio-v2.h
+++ b/include/sound/apr_audio-v2.h
@@ -1627,7 +1627,7 @@
  * Supported values: #AFE_API_VERSION_HDMI_CONFIG
  */
 
-u16                  dataype;
+u16                  datatype;
 /* data type
  * Supported values:
  * - #LINEAR_PCM_DATA
diff --git a/include/sound/apr_audio.h b/include/sound/apr_audio.h
index 90872c9..23c1d51 100644
--- a/include/sound/apr_audio.h
+++ b/include/sound/apr_audio.h
@@ -61,7 +61,7 @@
 #define RT_PROXY_PORT_001_TX	0x2001    /* index = 31 */
 #define SECONDARY_PCM_RX 12			/* index = 32 */
 #define SECONDARY_PCM_TX 13			/* index = 33 */
-
+#define PSEUDOPORT_01           0x8001    /* index =34 */
 
 #define AFE_PORT_INVALID 0xFFFF
 #define SLIMBUS_EXTPROC_RX AFE_PORT_INVALID
@@ -299,6 +299,14 @@
 	int	num_ch;		/* 1 to 8 */
 } __packed;
 
+struct afe_port_pseudo_cfg {
+	u16 bit_width;
+	u16 num_channels;
+	u16 data_format;
+	u16 timing_mode;
+	u16 reserved;
+} __packed;
+
 #define AFE_PORT_AUDIO_IF_CONFIG 0x000100d3
 #define AFE_PORT_AUDIO_SLIM_SCH_CONFIG 0x000100e4
 #define AFE_PORT_MULTI_CHAN_HDMI_AUDIO_IF_CONFIG	0x000100D9
@@ -312,6 +320,7 @@
 	struct afe_port_slimbus_cfg	  slimbus;
 	struct afe_port_slimbus_sch_cfg	  slim_sch;
 	struct afe_port_rtproxy_cfg       rtproxy;
+	struct afe_port_pseudo_cfg        pseudo;
 } __attribute__((packed));
 
 struct afe_audioif_config_command {
@@ -574,6 +583,19 @@
 	u32 rate;
 	u8 dev_channel_mapping[8];
 } __packed;
+
+struct adm_multi_channel_copp_open_v3 {
+	struct apr_hdr hdr;
+	u16 flags;
+	u16 mode;
+	u16 endpoint_id1;
+	u16 endpoint_id2;
+	u32 topology_id;
+	u16 channel_config;
+	u16 bit_width;
+	u32 rate;
+	u8  dev_channel_mapping[8];
+};
 #define ADM_CMD_MEMORY_MAP				0x00010C30
 struct adm_cmd_memory_map{
 	struct apr_hdr	hdr;
@@ -914,7 +936,38 @@
 				 * An unused channel is set to zero.
 				 */
 };
+struct asm_dts_enc_cfg {
+	uint32_t	sample_rate;
+	/*
+	* Samples at which input is to be encoded.
+	* Supported values:
+	* 44100 -- encode at 44.1 Khz
+	* 48000 -- encode at 48 Khz
+	*/
 
+	uint32_t	num_channels;
+	/*
+	* Number of channels for multi-channel encoding.
+	* Supported values: 1 to 6
+	*/
+
+	uint8_t		channel_mapping[6];
+	/*
+	* Channel array of size 16. Channel[i] mapping describes channel I.
+	* Each element i of the array describes channel I inside the buffer
+	* where num_channels. An unused channel is set to zero. Only first
+	* num_channels elements are valid
+
+	* Supported values:
+	* - # PCM_CHANNEL_L
+	* - # PCM_CHANNEL_R
+	* - # PCM_CHANNEL_C
+	* - # PCM_CHANNEL_LS
+	* - # PCM_CHANNEL_RS
+	* - # PCM_CHANNEL_LFE
+	*/
+
+};
 struct asm_adpcm_cfg {
 	u16 ch_cfg;
 	u16 bits_per_sample;
@@ -1107,6 +1160,7 @@
 		struct asm_sbc_read_cfg     sbc;
 		struct asm_amrwb_read_cfg   amrwb;
 		struct asm_multi_channel_pcm_fmt_blk      mpcm;
+		struct asm_dts_enc_cfg      dts;
 	} __attribute__((packed)) cfg;
 };
 
@@ -1162,6 +1216,7 @@
 #define EAC3_DECODER 0x00010C3C
 #define DTS	0x00010D88
 #define DTS_LBR	0x00010DBB
+#define MP2          0x00010DBE
 #define ATRAC	0x00010D89
 #define MAT	0x00010D8A
 #define G711_ALAW_FS 0x00010BF7
@@ -1170,6 +1225,7 @@
 #define MPEG4_MULTI_AAC 0x00010D86
 #define US_POINT_EPOS_FORMAT 0x00012310
 #define US_RAW_FORMAT        0x0001127C
+#define US_PROX_FORMAT       0x00012721
 #define MULTI_CHANNEL_PCM    0x00010C66
 
 #define ASM_ENCDEC_SBCRATE         0x00010C13
@@ -1207,6 +1263,148 @@
 	u32	flags;
 	u32	format;
 } __packed;
+#define ASM_STREAM_CMD_OPEN_TRANSCODE_LOOPBACK     0x00010DBA
+struct asm_stream_cmd_open_transcode_loopback {
+	struct apr_hdr hdr;
+	uint32_t	mode_flags;
+	/*
+	* All bits are reserved. Clients must set them to zero.
+	*/
+
+	uint32_t	src_format_id;
+	/*
+	* Specifies the media format of the input audio stream.
+
+	* Supported values:
+	* - #ASM_MEDIA_FMT_LINEAR_PCM
+	* - #ASM_MEDIA_FMT_MULTI_CHANNEL_PCM
+	*/
+
+	uint32_t	sink_format_id;
+	/*
+	* Specifies the media format of the output stream.
+
+	* Supported values:
+	* - #ASM_MEDIA_FMT_LINEAR_PCM
+	* - #ASM_MEDIA_FMT_MULTI_CHANNEL_PCM
+	* - #ASM_MEDIA_FMT_DTS
+	*/
+
+	uint32_t	audproc_topo_id;
+	/*
+	* Postprocessing topology ID, which specifies the topology (order of
+	* processing) of postprocessing algorithms.
+
+	* Supported values:
+	* - #ASM_STREAM_POSTPROC_TOPO_ID_DEFAULT
+	* - #ASM_STREAM_POSTPROC_TOPO_ID_PEAKMETER
+	* - #ASM_STREAM_POSTPROC_TOPO_ID_NONE
+	* - #ASM_STREAM_POSTPROC_TOPO_ID_MCH_PEAK_VOL
+	*/
+
+	uint16_t	src_endpoint_type;
+	/*
+	* Specifies the source endpoint that provides the input samples.
+
+	* Supported values:
+	* - 0 -- Tx device matrix or stream router
+	* (gateway to the hardware ports)
+	* - All other values are reserved
+
+	* Clients must set this field to zero. Otherwise, an error is returned.
+	*/
+
+	uint16_t	sink_endpoint_type;
+	/*
+	* Specifies the sink endpoint type.
+
+	* Supported values:
+	* - 0 -- Rx device matrix or stream router
+	* (gateway to the hardware ports)
+	* - All other values are reserved
+
+	* Clients must set this field to zero. Otherwise, an error is returned.
+	*/
+
+	uint16_t	bits_per_sample;
+	/*
+	* Number of bits per sample processed by the ASM modules.
+	* Supported values: 16, 24
+	*/
+
+	uint16_t	reserved;
+	/*
+	* This field must be set to zero.
+	*/
+} __packed;
+
+/*
+* ID of the DTS mix LFE channel to front channels parameter in the
+* #ASM_STREAM_CMD_SET_ENCDEC_PARAM command.
+* asm_dts_generic_param_t
+* ASM_PARAM_ID_DTS_MIX_LFE_TO_FRONT
+*/
+#define ASM_PARAM_ID_DTS_MIX_LFE_TO_FRONT                          0x00010DB6
+
+/*
+* ID of the DTS DRC ratio parameter in the
+* #ASM_STREAM_CMD_SET_ENCDEC_PARAM command.
+* asm_dts_generic_param_t
+* ASM_PARAM_ID_DTS_DRC_RATIO
+*/
+#define ASM_PARAM_ID_DTS_DRC_RATIO                                   0x00010DB7
+
+/*
+* ID of the DTS enable dialog normalization parameter in the
+* #ASM_STREAM_CMD_SET_ENCDEC_PARAM command.
+
+* asm_dts_generic_param_t
+* ASM_PARAM_ID_DTS_ENABLE_DIALNORM
+*/
+#define ASM_PARAM_ID_DTS_ENABLE_DIALNORM                             0x00010DB8
+
+/*
+* ID of the DTS enable parse REV2AUX parameter in the
+* #ASM_STREAM_CMD_SET_ENCDEC_PARAM command.
+* asm_dts_generic_param_t
+* ASM_PARAM_ID_DTS_ENABLE_PARSE_REV2AUX
+*/
+#define ASM_PARAM_ID_DTS_ENABLE_PARSE_REV2AUX                         0x00010DB9
+
+struct asm_dts_generic_param {
+	int32_t		generic_parameter;
+	/*
+	* #ASM_PARAM_ID_DTS_MIX_LFE_TO_FRONT:
+	* - if enabled, mixes LFE channel to front
+	* while downmixing (if necessary)
+	* - Supported values: 1-> enable, 0-> disable
+	* - Default: disabled
+
+	* #ASM_PARAM_ID_DTS_DRC_RATIO:
+	* - percentage of DRC ratio.
+	* - Supported values: 0-100
+	* - Default: 0, DRC is disabled.
+
+	* #ASM_PARAM_ID_DTS_ENABLE_DIALNORM:
+	* - flag to enable dialog normalization post processing.
+	* - Supported values: 1-> enable, 0-> disable.
+	* - Default: enabled.
+
+	* #ASM_PARAM_ID_DTS_ENABLE_PARSE_REV2AUX:
+	* - flag to enable parsing of rev2aux chunk in the bitstream.
+	* This chunk contains broadcast metadata.
+	* - Supported values: 1-> enable, 0-> disable.
+	* - Default: disabled.
+	*/
+};
+
+struct asm_stream_cmd_dts_dec_param {
+	struct apr_hdr hdr;
+	u32            param_id;
+	u32            param_size;
+	struct asm_dts_generic_param generic_param;
+} __packed;
+
 
 #define ASM_STREAM_CMD_OPEN_READWRITE                    0x00010BCC
 
@@ -1236,7 +1434,7 @@
 	u8	session_id; /*ASM session ID*/
 	u16	afe_port_id;
 	u32	num_channels;
-	u32	sampleing_rate;
+	u32	sampling_rate;
 } __packed;
 
 #define ASM_STREAM_CMD_SET_ENCDEC_PARAM                  0x00010C10
diff --git a/include/sound/asound.h b/include/sound/asound.h
index a2e4ff5..7bf01b6 100644
--- a/include/sound/asound.h
+++ b/include/sound/asound.h
@@ -458,6 +458,36 @@
 	SNDRV_PCM_TSTAMP_TYPE_LAST = SNDRV_PCM_TSTAMP_TYPE_MONOTONIC,
 };
 
+/* channel positions */
+enum {
+	SNDRV_CHMAP_UNKNOWN = 0,
+	SNDRV_CHMAP_FL,		/* front left */
+	SNDRV_CHMAP_FC,		/* front center */
+	SNDRV_CHMAP_FR,		/* front right */
+	SNDRV_CHMAP_FLC,	/* front left center */
+	SNDRV_CHMAP_FRC,	/* front right center */
+	SNDRV_CHMAP_RL,		/* rear left */
+	SNDRV_CHMAP_RC,		/* rear center */
+	SNDRV_CHMAP_RR,		/* rear right */
+	SNDRV_CHMAP_RLC,	/* rear left center */
+	SNDRV_CHMAP_RRC,	/* rear right center */
+	SNDRV_CHMAP_SL,		/* side left */
+	SNDRV_CHMAP_SR,		/* side right */
+	SNDRV_CHMAP_LFE,	/* LFE */
+	SNDRV_CHMAP_FLW,	/* front left wide */
+	SNDRV_CHMAP_FRW,	/* front right wide */
+	SNDRV_CHMAP_FLH,	/* front left high */
+	SNDRV_CHMAP_FCH,	/* front center high */
+	SNDRV_CHMAP_FRH,	/* front right high */
+	SNDRV_CHMAP_TC,		/* top center */
+	SNDRV_CHMAP_NA,		/* N/A, silent */
+	SNDRV_CHMAP_LAST = SNDRV_CHMAP_NA,
+};
+
+#define SNDRV_CHMAP_POSITION_MASK	0xffff
+#define SNDRV_CHMAP_PHASE_INVERSE	(0x01 << 16)
+#define SNDRV_CHMAP_DRIVER_SPEC		(0x02 << 16)
+
 #define SNDRV_PCM_IOCTL_PVERSION	_IOR('A', 0x00, int)
 #define SNDRV_PCM_IOCTL_INFO		_IOR('A', 0x01, struct snd_pcm_info)
 #define SNDRV_PCM_IOCTL_TSTAMP		_IOW('A', 0x02, int)
diff --git a/include/sound/compress_params.h b/include/sound/compress_params.h
index 54af7d6a..b95fa3c 100644
--- a/include/sound/compress_params.h
+++ b/include/sound/compress_params.h
@@ -87,7 +87,8 @@
 #define SND_AUDIOCODEC_DTS_LBR               ((__u32) 0x00000013)
 #define SND_AUDIOCODEC_DTS_TRANSCODE_LOOPBACK ((__u32) 0x00000014)
 #define SND_AUDIOCODEC_PASS_THROUGH          ((__u32) 0x00000015)
-
+#define SND_AUDIOCODEC_MP2                   ((__u32) 0x00000016)
+#define SND_AUDIOCODEC_DTS_LBR_PASS_THROUGH  ((__u32) 0x00000017)
 /*
  * Profile and modes are listed with bit masks. This allows for a
  * more compact representation of fields that will not evolve
@@ -417,6 +418,8 @@
 	__u32 ch_mode;
 	__u32 format;
 	__u32 align;
+	__u32 transcode_dts;
+	struct snd_dec_dts dts;
 	union snd_codec_options options;
 	__u32 reserved[3];
 };
diff --git a/include/sound/pcm.h b/include/sound/pcm.h
index 6cb456e..028e683 100644
--- a/include/sound/pcm.h
+++ b/include/sound/pcm.h
@@ -80,6 +80,7 @@
 			     unsigned long offset);
 	int (*mmap)(struct snd_pcm_substream *substream, struct vm_area_struct *vma);
 	int (*ack)(struct snd_pcm_substream *substream);
+	int (*restart)(struct snd_pcm_substream *substream);
 };
 
 /*
@@ -110,6 +111,12 @@
 
 #define SNDRV_PCM_POS_XRUN		((snd_pcm_uframes_t)-1)
 
+#define SNDRV_DMA_MODE          (0)
+#define SNDRV_NON_DMA_MODE      (1 << 0)
+#define SNDRV_RENDER_STOPPED    (1 << 1)
+#define SNDRV_RENDER_RUNNING    (1 << 2)
+
+
 /* If you change this don't forget to change rates[] table in pcm_native.c */
 #define SNDRV_PCM_RATE_5512		(1<<0)		/* 5512Hz */
 #define SNDRV_PCM_RATE_8000		(1<<1)		/* 8000Hz */
@@ -299,6 +306,7 @@
 	unsigned int rate_num;
 	unsigned int rate_den;
 	unsigned int no_period_wakeup: 1;
+	unsigned int render_flag;
 
 	/* -- SW params -- */
 	int tstamp_mode;		/* mmap timestamp is updated */
@@ -438,6 +446,7 @@
 	struct snd_info_entry *proc_xrun_debug_entry;
 #endif
 #endif
+	struct snd_kcontrol *chmap_kctl; /* channel-mapping controls */
 };
 
 struct snd_pcm {
@@ -1072,4 +1081,51 @@
 
 const char *snd_pcm_format_name(snd_pcm_format_t format);
 
+/*
+ * PCM channel-mapping control API
+ */
+/* array element of channel maps */
+struct snd_pcm_chmap_elem {
+	unsigned char channels;
+	unsigned char map[15];
+};
+
+/* channel map information; retrieved via snd_kcontrol_chip() */
+struct snd_pcm_chmap {
+	struct snd_pcm *pcm;	/* assigned PCM instance */
+	int stream;		/* PLAYBACK or CAPTURE */
+	struct snd_kcontrol *kctl;
+	const struct snd_pcm_chmap_elem *chmap;
+	unsigned int max_channels;
+	unsigned int channel_mask;	/* optional: active channels bitmask */
+	void *private_data;	/* optional: private data pointer */
+};
+
+/* get the PCM substream assigned to the given chmap info */
+static inline struct snd_pcm_substream *
+snd_pcm_chmap_substream(struct snd_pcm_chmap *info, unsigned int idx)
+{
+	struct snd_pcm_substream *s;
+	for (s = info->pcm->streams[info->stream].substream; s; s = s->next)
+		if (s->number == idx)
+			return s;
+	return NULL;
+}
+
+/* ALSA-standard channel maps (RL/RR prior to C/LFE) */
+extern const struct snd_pcm_chmap_elem snd_pcm_std_chmaps[];
+/* Other world's standard channel maps (C/LFE prior to RL/RR) */
+extern const struct snd_pcm_chmap_elem snd_pcm_alt_chmaps[];
+
+/* bit masks to be passed to snd_pcm_chmap.channel_mask field */
+#define SND_PCM_CHMAP_MASK_24	((1U << 2) | (1U << 4))
+#define SND_PCM_CHMAP_MASK_246	(SND_PCM_CHMAP_MASK_24 | (1U << 6))
+#define SND_PCM_CHMAP_MASK_2468	(SND_PCM_CHMAP_MASK_246 | (1U << 8))
+
+int snd_pcm_add_chmap_ctls(struct snd_pcm *pcm, int stream,
+			   const struct snd_pcm_chmap_elem *chmap,
+			   int max_channels,
+			   unsigned long private_value,
+			   struct snd_pcm_chmap **info_ret);
+
 #endif /* __SOUND_PCM_H */
diff --git a/include/sound/q6adm.h b/include/sound/q6adm.h
index 676c4cb..70c68a8 100644
--- a/include/sound/q6adm.h
+++ b/include/sound/q6adm.h
@@ -37,6 +37,8 @@
 
 int adm_close(int port);
 
+int adm_pseudo_close(int port);
+
 int adm_matrix_map(int session_id, int path, int num_copps,
 				unsigned int *port_id, int copp_id);
 
@@ -45,6 +47,12 @@
 
 void adm_ec_ref_rx_id(int  port_id);
 
+int adm_connect_afe_port_v2(int mode, int session_id, int port_id,
+					int sample_rate, int channels);
+
+int adm_multi_ch_copp_pseudo_open_v3(int port_id, int path, int rate,
+				int channel_mode, int topology);
+
 #ifdef CONFIG_RTAC
 int adm_get_copp_id(int port_id);
 #endif
diff --git a/include/sound/q6afe.h b/include/sound/q6afe.h
index a7264e8..8451ac6 100644
--- a/include/sound/q6afe.h
+++ b/include/sound/q6afe.h
@@ -70,6 +70,7 @@
 	IDX_RT_PROXY_PORT_001_TX = 31,
 	IDX_SECONDARY_PCM_RX = 32,
 	IDX_SECONDARY_PCM_TX = 33,
+	IDX_PSEUDOPORT_01 = 34,
 	AFE_MAX_PORTS
 };
 
diff --git a/include/sound/q6asm-v2.h b/include/sound/q6asm-v2.h
index 2ee5ff7..182da1c 100644
--- a/include/sound/q6asm-v2.h
+++ b/include/sound/q6asm-v2.h
@@ -66,6 +66,7 @@
 
 #define SYNC_IO_MODE	0x0001
 #define ASYNC_IO_MODE	0x0002
+#define COMPRESSED_IO	0x0040
 #define NT_MODE        0x0400
 
 
diff --git a/include/sound/q6asm.h b/include/sound/q6asm.h
index 32d3aef..6b4c17b 100644
--- a/include/sound/q6asm.h
+++ b/include/sound/q6asm.h
@@ -50,10 +50,12 @@
 #define FORMAT_AAC	0x0018
 #define FORMAT_DTS_LBR 0x0019
 #define FORMAT_PASS_THROUGH 0x0020
+#define FORMAT_MP2          0x0021
 
 #define ENCDEC_SBCBITRATE   0x0001
 #define ENCDEC_IMMEDIATE_DECODE 0x0002
 #define ENCDEC_CFG_BLK          0x0003
+#define DTS_ENC_SAMPLE_RATE48k	48000
 
 #define CMD_PAUSE          0x0001
 #define CMD_FLUSH          0x0002
@@ -193,6 +195,11 @@
 
 int q6asm_open_write_compressed(struct audio_client *ac, uint32_t format);
 
+int q6asm_open_transcode_loopback(struct audio_client *ac, uint32_t channels);
+
+int q6asm_enc_cfg_blk_dts(struct audio_client *ac,
+				uint32_t sample_rate, uint32_t channels);
+
 int q6asm_open_read_write(struct audio_client *ac,
 			uint32_t rd_format,
 			uint32_t wr_format);
@@ -284,7 +291,8 @@
 			uint32_t rate, uint32_t channels);
 
 int q6asm_media_format_block_multi_ch_pcm(struct audio_client *ac,
-				uint32_t rate, uint32_t channels);
+				uint32_t rate, uint32_t channels,
+				char *channel_map);
 
 int q6asm_media_format_block_aac(struct audio_client *ac,
 			struct asm_aac_cfg *cfg);
diff --git a/include/sound/tlv.h b/include/sound/tlv.h
index 7067e2d..de36aaa 100644
--- a/include/sound/tlv.h
+++ b/include/sound/tlv.h
@@ -73,4 +73,12 @@
 
 #define TLV_DB_GAIN_MUTE	-9999999
 
+/*
+ * channel-mapping TLV items
+ *  TLV length must match with num_channels
+ */
+#define SNDRV_CTL_TLVT_CHMAP_FIXED	0x101	/* fixed channel position */
+#define SNDRV_CTL_TLVT_CHMAP_VAR	0x102	/* channels freely swappable */
+#define SNDRV_CTL_TLVT_CHMAP_PAIRED	0x103	/* pair-wise swappable */
+
 #endif /* __SOUND_TLV_H */
diff --git a/include/trace/events/mpdcvs_trace.h b/include/trace/events/mpdcvs_trace.h
new file mode 100644
index 0000000..0db1378
--- /dev/null
+++ b/include/trace/events/mpdcvs_trace.h
@@ -0,0 +1,156 @@
+/* Copyright (c) 2012, Free Software 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.
+ */
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM  mpdcvs_trace
+
+#if !defined(_TRACE_MPDCVS_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_MPDCVS_H
+
+#include <linux/tracepoint.h>
+
+DECLARE_EVENT_CLASS(msm_mp,
+
+	TP_PROTO(const char *name, int mp_val),
+
+	TP_ARGS(name, mp_val),
+
+	TP_STRUCT__entry(
+		__string(name, name)
+		__field(int, mp_val)
+	),
+
+	TP_fast_assign(
+		__assign_str(name, name);
+		__entry->mp_val = mp_val;
+	),
+
+	TP_printk("ev_name=%s ev_level=%d",
+		__get_str(name),
+		__entry->mp_val)
+);
+
+/* Core function of run_q */
+
+DEFINE_EVENT(msm_mp, msm_mp_runq,
+
+	TP_PROTO(const char *name, int mp_val),
+
+	TP_ARGS(name, mp_val)
+);
+
+DEFINE_EVENT(msm_mp, msm_mp_cpusonline,
+
+	TP_PROTO(const char *name, int mp_val),
+
+	TP_ARGS(name, mp_val)
+);
+
+DEFINE_EVENT(msm_mp, msm_mp_slacktime,
+
+	TP_PROTO(const char *name, int mp_val),
+
+	TP_ARGS(name, mp_val)
+);
+
+DECLARE_EVENT_CLASS(msm_dcvs,
+
+	TP_PROTO(const char *name, const char *cpuid, int val),
+
+	TP_ARGS(name, cpuid, val),
+
+	TP_STRUCT__entry(
+		__string(name, name)
+		__string(cpuid, cpuid)
+		__field(int, val)
+	),
+
+	TP_fast_assign(
+		__assign_str(name, name);
+		__assign_str(cpuid, cpuid);
+		__entry->val = val;
+	),
+
+	TP_printk("ev_name=%s d_name=%s ev_level=%d",
+		__get_str(name),
+		__get_str(cpuid),
+		__entry->val)
+);
+
+/* Core function of dcvs */
+
+DEFINE_EVENT(msm_dcvs, msm_dcvs_idle,
+
+	TP_PROTO(const char *name, const char *cpuid, int val),
+
+	TP_ARGS(name, cpuid, val)
+);
+
+DEFINE_EVENT(msm_dcvs, msm_dcvs_iowait,
+
+	TP_PROTO(const char *name, const char *cpuid, int val),
+
+	TP_ARGS(name, cpuid, val)
+);
+
+DEFINE_EVENT(msm_dcvs, msm_dcvs_slack_time,
+
+	TP_PROTO(const char *name, const char *cpuid, int val),
+
+	TP_ARGS(name, cpuid, val)
+);
+
+DECLARE_EVENT_CLASS(msm_dcvs_scm,
+
+	TP_PROTO(unsigned long cpuid, int ev_type, unsigned long param0,
+		unsigned long param1, unsigned long ret0, unsigned long ret1),
+
+	TP_ARGS(cpuid, ev_type, param0, param1, ret0, ret1),
+
+	TP_STRUCT__entry(
+		__field(unsigned long, cpuid)
+		__field(int, ev_type)
+		__field(unsigned long, param0)
+		__field(unsigned long, param1)
+		__field(unsigned long, ret0)
+		__field(unsigned long, ret1)
+	),
+
+	TP_fast_assign(
+		__entry->cpuid = cpuid;
+		__entry->ev_type = ev_type;
+		__entry->param0 = param0;
+		__entry->param1 = param1;
+		__entry->ret0 = ret0;
+		__entry->ret1 = ret1;
+	),
+
+	TP_printk("dev=%lu ev_type=%d ev_param0=%lu ev_param1=%lu ev_ret0=%lu ev_ret1=%lu",
+		__entry->cpuid,
+		__entry->ev_type,
+		__entry->param0,
+		__entry->param1,
+		__entry->ret0,
+		__entry->ret1)
+);
+
+DEFINE_EVENT(msm_dcvs_scm, msm_dcvs_scm_event,
+
+	TP_PROTO(unsigned long cpuid, int ev_type, unsigned long param0,
+		unsigned long param1, unsigned long ret0, unsigned long ret1),
+
+	TP_ARGS(cpuid, ev_type, param0, param1, ret0, ret1)
+);
+
+#endif /* _TRACE_MPDCVS_H */
+
+/* This part must be outside protection */
+#include <trace/define_trace.h>
diff --git a/kernel/printk.c b/kernel/printk.c
index 4cf4670..7f51b03 100644
--- a/kernel/printk.c
+++ b/kernel/printk.c
@@ -1224,13 +1224,13 @@
 	unsigned long action, void *hcpu)
 {
 	switch (action) {
-	case CPU_ONLINE:
 	case CPU_DEAD:
 	case CPU_DOWN_FAILED:
 	case CPU_UP_CANCELED:
 		console_lock();
 		console_unlock();
 		break;
+	case CPU_ONLINE:
 	case CPU_DYING:
 		/* invoked with preemption disabled, so defer */
 		if (!console_trylock())
diff --git a/kernel/rcutree.c b/kernel/rcutree.c
index d0c5baf..4eec66e 100644
--- a/kernel/rcutree.c
+++ b/kernel/rcutree.c
@@ -295,7 +295,9 @@
 static int
 cpu_needs_another_gp(struct rcu_state *rsp, struct rcu_data *rdp)
 {
-	return *rdp->nxttail[RCU_DONE_TAIL] && !rcu_gp_in_progress(rsp);
+	return *rdp->nxttail[RCU_DONE_TAIL +
+			     ACCESS_ONCE(rsp->completed) != rdp->completed] &&
+	       !rcu_gp_in_progress(rsp);
 }
 
 /*
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c
index b9d1a73..1076be8 100644
--- a/kernel/trace/trace.c
+++ b/kernel/trace/trace.c
@@ -3825,10 +3825,11 @@
 	if (entry->buf[cnt - 1] != '\n') {
 		entry->buf[cnt] = '\n';
 		entry->buf[cnt + 1] = '\0';
-	} else
+		stm_log(OST_ENTITY_TRACE_MARKER, entry->buf, cnt + 2);
+	} else {
 		entry->buf[cnt] = '\0';
-
-	stm_log(OST_ENTITY_TRACE_MARKER, event, size);
+		stm_log(OST_ENTITY_TRACE_MARKER, entry->buf, cnt + 1);
+	}
 	ring_buffer_unlock_commit(buffer, event);
 
 	written = cnt;
diff --git a/mm/Kconfig b/mm/Kconfig
index 84489cd..4cde97f 100644
--- a/mm/Kconfig
+++ b/mm/Kconfig
@@ -379,3 +379,14 @@
 	  in a negligible performance hit.
 
 	  If unsure, say Y to enable cleancache
+
+config MEMORY_HOLE_CARVEOUT
+        bool
+        help
+          MEMORY_HOLE_CARVEOUT is needed to include the msm_mem_hole driver
+          which is needed to enable/disable memblock-remove features for
+          device tree nodes that set compatible="qcom,msm-mem-hole". The
+          corresponding device tree node provides the address and size of
+          the memory corresponding to the hole to be removed using memblock-
+          remove.
+
diff --git a/mm/page_alloc.c b/mm/page_alloc.c
index 831509c..c3142e8 100644
--- a/mm/page_alloc.c
+++ b/mm/page_alloc.c
@@ -913,6 +913,11 @@
 	[MIGRATE_ISOLATE]     = { MIGRATE_RESERVE }, /* Never used */
 };
 
+int *get_migratetype_fallbacks(int mtype)
+{
+	return fallbacks[mtype];
+}
+
 /*
  * Move the free pages in a range to the free lists of the requested type.
  * Note that start_page and end_pages are not aligned on a pageblock
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index d7deaaf..28eb7ea 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -2073,6 +2073,40 @@
 	return err;
 }
 
+static int le_cancel_create_conn(struct sock *sk, u16 index,
+	unsigned char *data, u16 len)
+{
+	struct mgmt_cp_le_cancel_create_conn *cp = (void *) data;
+	struct hci_dev *hdev;
+	int err = 0;
+
+	if (len != sizeof(*cp))
+		return cmd_status(sk, index, MGMT_OP_LE_CANCEL_CREATE_CONN,
+							EINVAL);
+
+	hdev = hci_dev_get(index);
+
+	if (!hdev)
+		return cmd_status(sk, index, MGMT_OP_LE_CANCEL_CREATE_CONN,
+							ENODEV);
+
+	hci_dev_lock_bh(hdev);
+
+	if (!test_bit(HCI_UP, &hdev->flags)) {
+		err = cmd_status(sk, index, MGMT_OP_LE_CANCEL_CREATE_CONN,
+						ENETDOWN);
+		goto failed;
+	}
+
+	hci_le_cancel_create_connect(hdev, &cp->bdaddr);
+
+failed:
+	hci_dev_unlock_bh(hdev);
+	hci_dev_put(hdev);
+
+return err;
+}
+
 static int set_local_name(struct sock *sk, u16 index, unsigned char *data,
 								u16 len)
 {
@@ -2664,6 +2698,9 @@
 	case MGMT_OP_LE_CANCEL_CREATE_CONN_WHITE_LIST:
 		err = le_cancel_create_conn_white_list(sk, index);
 		break;
+	case MGMT_OP_LE_CANCEL_CREATE_CONN:
+		err = le_cancel_create_conn(sk, index, buf + sizeof(*hdr), len);
+		break;
 	default:
 		BT_DBG("Unknown op %u", opcode);
 		err = cmd_status(sk, index, opcode, 0x01);
diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c
index faa48f7..59debb7 100644
--- a/net/netlink/af_netlink.c
+++ b/net/netlink/af_netlink.c
@@ -1329,7 +1329,7 @@
 	if (NULL == siocb->scm)
 		siocb->scm = &scm;
 
-	err = scm_send(sock, msg, siocb->scm);
+	err = scm_send(sock, msg, siocb->scm, true);
 	if (err < 0)
 		return err;
 
diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c
index d510353..109e30b 100644
--- a/net/unix/af_unix.c
+++ b/net/unix/af_unix.c
@@ -1446,7 +1446,7 @@
 	if (NULL == siocb->scm)
 		siocb->scm = &tmp_scm;
 	wait_for_unix_gc();
-	err = scm_send(sock, msg, siocb->scm);
+	err = scm_send(sock, msg, siocb->scm, false);
 	if (err < 0)
 		return err;
 
@@ -1607,7 +1607,7 @@
 	if (NULL == siocb->scm)
 		siocb->scm = &tmp_scm;
 	wait_for_unix_gc();
-	err = scm_send(sock, msg, siocb->scm);
+	err = scm_send(sock, msg, siocb->scm, false);
 	if (err < 0)
 		return err;
 
diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl
index 4c655c2..d2f60a0 100755
--- a/scripts/checkpatch.pl
+++ b/scripts/checkpatch.pl
@@ -1629,6 +1629,8 @@
 			 $exec_file =~ /^.+\.ihex$/ or
 			 $exec_file =~ /^.+\.hex$/ or
 			 $exec_file =~ /^.+\.HEX$/ or
+			 $exec_file =~ /^.+\.dts$/ or
+			 $exec_file =~ /^.+\.dtsi$/ or
 			 $exec_file =~ /^.+defconfig$/ or
 			 $exec_file =~ /^Makefile$/ or
 			 $exec_file =~ /^Kconfig$/) &&
diff --git a/sound/core/pcm.c b/sound/core/pcm.c
index 09bf06e..d98e160 100644
--- a/sound/core/pcm.c
+++ b/sound/core/pcm.c
@@ -1195,6 +1195,10 @@
 			break;
 		}
 		snd_unregister_device(devtype, pcm->card, pcm->device);
+		if (pcm->streams[cidx].chmap_kctl) {
+			snd_ctl_remove(pcm->card, pcm->streams[cidx].chmap_kctl);
+			pcm->streams[cidx].chmap_kctl = NULL;
+		}
 	}
  unlock:
 	mutex_unlock(&register_mutex);
diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c
index b5d5a75..e0ab899 100644
--- a/sound/core/pcm_lib.c
+++ b/sound/core/pcm_lib.c
@@ -25,6 +25,7 @@
 #include <linux/export.h>
 #include <sound/core.h>
 #include <sound/control.h>
+#include <sound/tlv.h>
 #include <sound/info.h>
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
@@ -2289,3 +2290,216 @@
 }
 
 EXPORT_SYMBOL(snd_pcm_lib_readv);
+
+/*
+ * standard channel mapping helpers
+ */
+
+/* default channel maps for multi-channel playbacks, up to 8 channels */
+const struct snd_pcm_chmap_elem snd_pcm_std_chmaps[] = {
+	{ .channels = 1,
+	  .map = { SNDRV_CHMAP_FC } },
+	{ .channels = 2,
+	  .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR } },
+	{ .channels = 4,
+	  .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR,
+		   SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } },
+	{ .channels = 6,
+	  .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR,
+		   SNDRV_CHMAP_RL, SNDRV_CHMAP_RR,
+		   SNDRV_CHMAP_FC, SNDRV_CHMAP_LFE } },
+	{ .channels = 8,
+	  .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR,
+		   SNDRV_CHMAP_RL, SNDRV_CHMAP_RR,
+		   SNDRV_CHMAP_FC, SNDRV_CHMAP_LFE,
+		   SNDRV_CHMAP_SL, SNDRV_CHMAP_SR } },
+	{ }
+};
+EXPORT_SYMBOL_GPL(snd_pcm_std_chmaps);
+
+/* alternative channel maps with CLFE <-> surround swapped for 6/8 channels */
+const struct snd_pcm_chmap_elem snd_pcm_alt_chmaps[] = {
+	{ .channels = 1,
+	  .map = { SNDRV_CHMAP_FC } },
+	{ .channels = 2,
+	  .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR } },
+	{ .channels = 4,
+	  .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR,
+		   SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } },
+	{ .channels = 6,
+	  .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR,
+		   SNDRV_CHMAP_FC, SNDRV_CHMAP_LFE,
+		   SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } },
+	{ .channels = 8,
+	  .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR,
+		   SNDRV_CHMAP_FC, SNDRV_CHMAP_LFE,
+		   SNDRV_CHMAP_RL, SNDRV_CHMAP_RR,
+		   SNDRV_CHMAP_SL, SNDRV_CHMAP_SR } },
+	{ }
+};
+EXPORT_SYMBOL_GPL(snd_pcm_alt_chmaps);
+
+static bool valid_chmap_channels(const struct snd_pcm_chmap *info, int ch)
+{
+	if (ch > info->max_channels)
+		return false;
+	return !info->channel_mask || (info->channel_mask & (1U << ch));
+}
+
+static int pcm_chmap_ctl_info(struct snd_kcontrol *kcontrol,
+			      struct snd_ctl_elem_info *uinfo)
+{
+	struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
+
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 0;
+	uinfo->count = info->max_channels;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = SNDRV_CHMAP_LAST;
+	return 0;
+}
+
+/* get callback for channel map ctl element
+ * stores the channel position firstly matching with the current channels
+ */
+static int pcm_chmap_ctl_get(struct snd_kcontrol *kcontrol,
+			     struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
+	unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+	struct snd_pcm_substream *substream;
+	const struct snd_pcm_chmap_elem *map;
+
+	if (snd_BUG_ON(!info->chmap))
+		return -EINVAL;
+	substream = snd_pcm_chmap_substream(info, idx);
+	if (!substream)
+		return -ENODEV;
+	memset(ucontrol->value.integer.value, 0,
+	       sizeof(ucontrol->value.integer.value));
+	if (!substream->runtime)
+		return 0; /* no channels set */
+	for (map = info->chmap; map->channels; map++) {
+		int i;
+		if (map->channels == substream->runtime->channels &&
+		    valid_chmap_channels(info, map->channels)) {
+			for (i = 0; i < map->channels; i++)
+				ucontrol->value.integer.value[i] = map->map[i];
+			return 0;
+		}
+	}
+	return -EINVAL;
+}
+
+/* tlv callback for channel map ctl element
+ * expands the pre-defined channel maps in a form of TLV
+ */
+static int pcm_chmap_ctl_tlv(struct snd_kcontrol *kcontrol, int op_flag,
+			     unsigned int size, unsigned int __user *tlv)
+{
+	struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
+	const struct snd_pcm_chmap_elem *map;
+	unsigned int __user *dst;
+	int c, count = 0;
+
+	if (snd_BUG_ON(!info->chmap))
+		return -EINVAL;
+	if (size < 8)
+		return -ENOMEM;
+	if (put_user(SNDRV_CTL_TLVT_CONTAINER, tlv))
+		return -EFAULT;
+	size -= 8;
+	dst = tlv + 2;
+	for (map = info->chmap; map->channels; map++) {
+		int chs_bytes = map->channels * 4;
+		if (!valid_chmap_channels(info, map->channels))
+			continue;
+		if (size < 8)
+			return -ENOMEM;
+		if (put_user(SNDRV_CTL_TLVT_CHMAP_FIXED, dst) ||
+		    put_user(chs_bytes, dst + 1))
+			return -EFAULT;
+		dst += 2;
+		size -= 8;
+		count += 8;
+		if (size < chs_bytes)
+			return -ENOMEM;
+		size -= chs_bytes;
+		count += chs_bytes;
+		for (c = 0; c < map->channels; c++) {
+			if (put_user(map->map[c], dst))
+				return -EFAULT;
+			dst++;
+		}
+	}
+	if (put_user(count, tlv + 1))
+		return -EFAULT;
+	return 0;
+}
+
+static void pcm_chmap_ctl_private_free(struct snd_kcontrol *kcontrol)
+{
+	struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
+	info->pcm->streams[info->stream].chmap_kctl = NULL;
+	kfree(info);
+}
+
+/**
+ * snd_pcm_add_chmap_ctls - create channel-mapping control elements
+ * @pcm: the assigned PCM instance
+ * @stream: stream direction
+ * @chmap: channel map elements (for query)
+ * @max_channels: the max number of channels for the stream
+ * @private_value: the value passed to each kcontrol's private_value field
+ * @info_ret: store struct snd_pcm_chmap instance if non-NULL
+ *
+ * Create channel-mapping control elements assigned to the given PCM stream(s).
+ * Returns zero if succeed, or a negative error value.
+ */
+int snd_pcm_add_chmap_ctls(struct snd_pcm *pcm, int stream,
+			   const struct snd_pcm_chmap_elem *chmap,
+			   int max_channels,
+			   unsigned long private_value,
+			   struct snd_pcm_chmap **info_ret)
+{
+	struct snd_pcm_chmap *info;
+	struct snd_kcontrol_new knew = {
+		.iface = SNDRV_CTL_ELEM_IFACE_PCM,
+		.access = SNDRV_CTL_ELEM_ACCESS_READ |
+			SNDRV_CTL_ELEM_ACCESS_TLV_READ |
+			SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK,
+		.info = pcm_chmap_ctl_info,
+		.get = pcm_chmap_ctl_get,
+		.tlv.c = pcm_chmap_ctl_tlv,
+	};
+	int err;
+
+	info = kzalloc(sizeof(*info), GFP_KERNEL);
+	if (!info)
+		return -ENOMEM;
+	info->pcm = pcm;
+	info->stream = stream;
+	info->chmap = chmap;
+	info->max_channels = max_channels;
+	if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+		knew.name = "Playback Channel Map";
+	else
+		knew.name = "Capture Channel Map";
+	knew.device = pcm->device;
+	knew.count = pcm->streams[stream].substream_count;
+	knew.private_value = private_value;
+	info->kctl = snd_ctl_new1(&knew, info);
+	if (!info->kctl) {
+		kfree(info);
+		return -ENOMEM;
+	}
+	info->kctl->private_free = pcm_chmap_ctl_private_free;
+	err = snd_ctl_add(pcm->card, info->kctl);
+	if (err < 0)
+		return err;
+	pcm->streams[stream].chmap_kctl = info->kctl;
+	if (info_ret)
+		*info_ret = info;
+	return 0;
+}
+EXPORT_SYMBOL_GPL(snd_pcm_add_chmap_ctls);
diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c
index b11118f..4636247 100644
--- a/sound/core/pcm_native.c
+++ b/sound/core/pcm_native.c
@@ -2477,6 +2477,7 @@
 	volatile struct snd_pcm_mmap_status *status;
 	volatile struct snd_pcm_mmap_control *control;
 	int err;
+	snd_pcm_uframes_t hw_avail;
 
 	memset(&sync_ptr, 0, sizeof(sync_ptr));
 	if (get_user(sync_ptr.flags, (unsigned __user *)&(_sync_ptr->flags)))
@@ -2499,6 +2500,16 @@
 		control->avail_min = sync_ptr.c.control.avail_min;
 	else
 		sync_ptr.c.control.avail_min = control->avail_min;
+
+	if (runtime->render_flag & SNDRV_NON_DMA_MODE) {
+		hw_avail = snd_pcm_playback_hw_avail(runtime);
+		if ((hw_avail >= runtime->start_threshold)
+			&& (runtime->render_flag &
+				SNDRV_RENDER_STOPPED)) {
+			if (substream->ops->restart)
+				substream->ops->restart(substream);
+		}
+	}
 	sync_ptr.s.status.state = status->state;
 	sync_ptr.s.status.hw_ptr = status->hw_ptr;
 	sync_ptr.s.status.tstamp = status->tstamp;
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 5aabfee..1c9d86b 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -51,7 +51,7 @@
 snd-soc-wcd9304-objs := wcd9304.o wcd9304-tables.o
 snd-soc-wcd9310-objs := wcd9310.o wcd9310-tables.o
 snd-soc-cs8427-objs := cs8427.o
-snd-soc-wcd9320-objs := wcd9320.o wcd9320-tables.o
+snd-soc-wcd9320-objs := wcd9xxx-resmgr.o wcd9320.o wcd9320-tables.o wcd9xxx-mbhc.o
 snd-soc-wl1273-objs := wl1273.o
 snd-soc-wm1250-ev1-objs := wm1250-ev1.o
 snd-soc-wm2000-objs := wm2000.o
diff --git a/sound/soc/codecs/wcd9304-tables.c b/sound/soc/codecs/wcd9304-tables.c
index f0d76e8..ba0b6b6 100644
--- a/sound/soc/codecs/wcd9304-tables.c
+++ b/sound/soc/codecs/wcd9304-tables.c
@@ -419,6 +419,10 @@
 	[SITAR_A_QFUSE_DATA_OUT1] = 1,
 	[SITAR_A_QFUSE_DATA_OUT2] = 1,
 	[SITAR_A_QFUSE_DATA_OUT3] = 1,
+	[SITAR_A_QFUSE_DATA_OUT4] = 1,
+	[SITAR_A_QFUSE_DATA_OUT5] = 1,
+	[SITAR_A_QFUSE_DATA_OUT6] = 1,
+	[SITAR_A_QFUSE_DATA_OUT7] = 1,
 	[SITAR_A_CDC_CTL] = 1,
 	[SITAR_A_LEAKAGE_CTL] = 1,
 	[SITAR_A_INTR_MODE] = 1,
@@ -428,6 +432,9 @@
 	[SITAR_A_INTR_STATUS0] = 1,
 	[SITAR_A_INTR_STATUS1] = 1,
 	[SITAR_A_INTR_STATUS2] = 1,
+	[SITAR_A_INTR_CLEAR0] = 1,
+	[SITAR_A_INTR_CLEAR1] = 1,
+	[SITAR_A_INTR_CLEAR2] = 1,
 	[SITAR_A_INTR_LEVEL0] = 1,
 	[SITAR_A_INTR_LEVEL1] = 1,
 	[SITAR_A_INTR_LEVEL2] = 1,
@@ -593,24 +600,76 @@
 	[SITAR_A_CDC_ANC1_SPARE] = 1,
 	[SITAR_A_CDC_ANC1_SMLPF_CTL] = 1,
 	[SITAR_A_CDC_ANC1_DCFLT_CTL] = 1,
+	[SITAR_A_CDC_ANC2_CTL] = 1,
+	[SITAR_A_CDC_ANC2_SHIFT] = 1,
+	[SITAR_A_CDC_ANC2_IIR_B1_CTL] = 1,
+	[SITAR_A_CDC_ANC2_IIR_B2_CTL] = 1,
+	[SITAR_A_CDC_ANC2_IIR_B3_CTL] = 1,
+	[SITAR_A_CDC_ANC2_IIR_B4_CTL] = 1,
+	[SITAR_A_CDC_ANC2_LPF_B1_CTL] = 1,
+	[SITAR_A_CDC_ANC2_LPF_B2_CTL] = 1,
+	[SITAR_A_CDC_ANC2_LPF_B3_CTL] = 1,
+	[SITAR_A_CDC_ANC2_SPARE] = 1,
+	[SITAR_A_CDC_ANC2_SMLPF_CTL] = 1,
+	[SITAR_A_CDC_ANC2_DCFLT_CTL] = 1,
 	[SITAR_A_CDC_TX1_VOL_CTL_TIMER] = 1,
 	[SITAR_A_CDC_TX1_VOL_CTL_GAIN] = 1,
 	[SITAR_A_CDC_TX1_VOL_CTL_CFG] = 1,
 	[SITAR_A_CDC_TX1_MUX_CTL] = 1,
 	[SITAR_A_CDC_TX1_CLK_FS_CTL] = 1,
 	[SITAR_A_CDC_TX1_DMIC_CTL] = 1,
+	[SITAR_A_CDC_TX2_VOL_CTL_TIMER] = 1,
+	[SITAR_A_CDC_TX2_VOL_CTL_GAIN] = 1,
+	[SITAR_A_CDC_TX2_VOL_CTL_CFG] = 1,
+	[SITAR_A_CDC_TX2_MUX_CTL] = 1,
+	[SITAR_A_CDC_TX2_CLK_FS_CTL] = 1,
+	[SITAR_A_CDC_TX2_DMIC_CTL] = 1,
+	[SITAR_A_CDC_TX3_VOL_CTL_TIMER] = 1,
+	[SITAR_A_CDC_TX3_VOL_CTL_GAIN] = 1,
+	[SITAR_A_CDC_TX3_VOL_CTL_CFG] = 1,
+	[SITAR_A_CDC_TX3_MUX_CTL] = 1,
+	[SITAR_A_CDC_TX3_CLK_FS_CTL] = 1,
+	[SITAR_A_CDC_TX3_DMIC_CTL] = 1,
+	[SITAR_A_CDC_TX4_VOL_CTL_TIMER] = 1,
+	[SITAR_A_CDC_TX4_VOL_CTL_GAIN] = 1,
+	[SITAR_A_CDC_TX4_VOL_CTL_CFG] = 1,
+	[SITAR_A_CDC_TX4_MUX_CTL] = 1,
+	[SITAR_A_CDC_TX4_CLK_FS_CTL] = 1,
+	[SITAR_A_CDC_TX4_DMIC_CTL] = 1,
+	[SITAR_A_CDC_TX5_VOL_CTL_TIMER] = 1,
+	[SITAR_A_CDC_TX5_VOL_CTL_GAIN] = 1,
+	[SITAR_A_CDC_TX5_VOL_CTL_CFG] = 1,
+	[SITAR_A_CDC_TX5_MUX_CTL] = 1,
+	[SITAR_A_CDC_TX5_CLK_FS_CTL] = 1,
+	[SITAR_A_CDC_TX5_DMIC_CTL] = 1,
 	[SITAR_A_CDC_SRC1_PDA_CFG] = 1,
 	[SITAR_A_CDC_SRC1_FS_CTL] = 1,
+	[SITAR_A_CDC_SRC2_PDA_CFG] = 1,
+	[SITAR_A_CDC_SRC2_FS_CTL] = 1,
 	[SITAR_A_CDC_RX1_B1_CTL] = 1,
 	[SITAR_A_CDC_RX1_B2_CTL] = 1,
 	[SITAR_A_CDC_RX1_B3_CTL] = 1,
 	[SITAR_A_CDC_RX1_B4_CTL] = 1,
 	[SITAR_A_CDC_RX1_B5_CTL] = 1,
-	[SITAR_A_CDC_RX2_B5_CTL] = 1,
-	[SITAR_A_CDC_RX3_B5_CTL] = 1,
 	[SITAR_A_CDC_RX1_B6_CTL] = 1,
 	[SITAR_A_CDC_RX1_VOL_CTL_B1_CTL] = 1,
 	[SITAR_A_CDC_RX1_VOL_CTL_B2_CTL] = 1,
+	[SITAR_A_CDC_RX2_B1_CTL] = 1,
+	[SITAR_A_CDC_RX2_B2_CTL] = 1,
+	[SITAR_A_CDC_RX2_B3_CTL] = 1,
+	[SITAR_A_CDC_RX2_B4_CTL] = 1,
+	[SITAR_A_CDC_RX2_B5_CTL] = 1,
+	[SITAR_A_CDC_RX2_B6_CTL] = 1,
+	[SITAR_A_CDC_RX2_VOL_CTL_B1_CTL] = 1,
+	[SITAR_A_CDC_RX2_VOL_CTL_B2_CTL] = 1,
+	[SITAR_A_CDC_RX3_B1_CTL] = 1,
+	[SITAR_A_CDC_RX3_B2_CTL] = 1,
+	[SITAR_A_CDC_RX3_B3_CTL] = 1,
+	[SITAR_A_CDC_RX3_B4_CTL] = 1,
+	[SITAR_A_CDC_RX3_B5_CTL] = 1,
+	[SITAR_A_CDC_RX3_B6_CTL] = 1,
+	[SITAR_A_CDC_RX3_VOL_CTL_B1_CTL] = 1,
+	[SITAR_A_CDC_RX3_VOL_CTL_B2_CTL] = 1,
 	[SITAR_A_CDC_CLK_ANC_RESET_CTL] = 1,
 	[SITAR_A_CDC_CLK_RX_RESET_CTL] = 1,
 	[SITAR_A_CDC_CLK_TX_RESET_B1_CTL] = 1,
@@ -652,6 +711,21 @@
 	[SITAR_A_CDC_IIR1_COEF_B3_CTL] = 1,
 	[SITAR_A_CDC_IIR1_COEF_B4_CTL] = 1,
 	[SITAR_A_CDC_IIR1_COEF_B5_CTL] = 1,
+	[SITAR_A_CDC_IIR2_GAIN_B1_CTL] = 1,
+	[SITAR_A_CDC_IIR2_GAIN_B2_CTL] = 1,
+	[SITAR_A_CDC_IIR2_GAIN_B3_CTL] = 1,
+	[SITAR_A_CDC_IIR2_GAIN_B4_CTL] = 1,
+	[SITAR_A_CDC_IIR2_GAIN_B5_CTL] = 1,
+	[SITAR_A_CDC_IIR2_GAIN_B6_CTL] = 1,
+	[SITAR_A_CDC_IIR2_GAIN_B7_CTL] = 1,
+	[SITAR_A_CDC_IIR2_GAIN_B8_CTL] = 1,
+	[SITAR_A_CDC_IIR2_CTL] = 1,
+	[SITAR_A_CDC_IIR2_GAIN_TIMER_CTL] = 1,
+	[SITAR_A_CDC_IIR2_COEF_B1_CTL] = 1,
+	[SITAR_A_CDC_IIR2_COEF_B2_CTL] = 1,
+	[SITAR_A_CDC_IIR2_COEF_B3_CTL] = 1,
+	[SITAR_A_CDC_IIR2_COEF_B4_CTL] = 1,
+	[SITAR_A_CDC_IIR2_COEF_B5_CTL] = 1,
 	[SITAR_A_CDC_TOP_GAIN_UPDATE] = 1,
 	[SITAR_A_CDC_TOP_RDAC_DOUT_CTL] = 1,
 	[SITAR_A_CDC_DEBUG_B1_CTL] = 1,
@@ -669,6 +743,14 @@
 	[SITAR_A_CDC_COMP1_B6_CTL] = 1,
 	[SITAR_A_CDC_COMP1_SHUT_DOWN_STATUS] = 1,
 	[SITAR_A_CDC_COMP1_FS_CFG] = 1,
+	[SITAR_A_CDC_COMP2_B1_CTL] = 1,
+	[SITAR_A_CDC_COMP2_B2_CTL] = 1,
+	[SITAR_A_CDC_COMP2_B3_CTL] = 1,
+	[SITAR_A_CDC_COMP2_B4_CTL] = 1,
+	[SITAR_A_CDC_COMP2_B5_CTL] = 1,
+	[SITAR_A_CDC_COMP2_B6_CTL] = 1,
+	[SITAR_A_CDC_COMP2_SHUT_DOWN_STATUS] = 1,
+	[SITAR_A_CDC_COMP2_FS_CFG] = 1,
 	[SITAR_A_CDC_CONN_RX1_B1_CTL] = 1,
 	[SITAR_A_CDC_CONN_RX1_B2_CTL] = 1,
 	[SITAR_A_CDC_CONN_RX1_B3_CTL] = 1,
diff --git a/sound/soc/codecs/wcd9304.c b/sound/soc/codecs/wcd9304.c
index b303878..412090f 100644
--- a/sound/soc/codecs/wcd9304.c
+++ b/sound/soc/codecs/wcd9304.c
@@ -41,34 +41,46 @@
 #define ADC_DMIC_SEL_ADC	0
 #define	ADC_DMIC_SEL_DMIC	1
 
+#define NUM_AMIC 3
 #define NUM_DECIMATORS 4
 #define NUM_INTERPOLATORS 3
 #define BITS_PER_REG 8
+
+enum {
+	AIF1_PB = 0,
+	AIF1_CAP,
+	NUM_CODEC_DAIS,
+};
+
+struct wcd9xxx_ch sitar_rx_chs[SITAR_RX_MAX] = {
+	WCD9XXX_CH(10, 0),
+	WCD9XXX_CH(11, 1),
+	WCD9XXX_CH(12, 2),
+	WCD9XXX_CH(13, 3),
+	WCD9XXX_CH(14, 4)
+};
+
+struct wcd9xxx_ch sitar_tx_chs[SITAR_TX_MAX] = {
+	WCD9XXX_CH(0, 0),
+	WCD9XXX_CH(1, 1),
+	WCD9XXX_CH(2, 2),
+	WCD9XXX_CH(3, 3),
+	WCD9XXX_CH(4, 4),
+};
+
 #define SITAR_CFILT_FAST_MODE 0x00
 #define SITAR_CFILT_SLOW_MODE 0x40
 #define MBHC_FW_READ_ATTEMPTS 15
 #define MBHC_FW_READ_TIMEOUT 2000000
 
+#define SLIM_CLOSE_TIMEOUT 1000
+
 #define SITAR_JACK_MASK (SND_JACK_HEADSET | SND_JACK_OC_HPHL | SND_JACK_OC_HPHR)
 
 #define SITAR_I2S_MASTER_MODE_MASK 0x08
 
 #define SITAR_OCP_ATTEMPT 1
 
-#define AIF1_PB 1
-#define AIF1_CAP 2
-#define NUM_CODEC_DAIS 2
-#define SLIM_CLOSE_TIMEOUT 1000
-
-struct sitar_codec_dai_data {
-	u32 rate;
-	u32 *ch_num;
-	u32 ch_act;
-	u32 ch_tot;
-	u32 ch_mask;
-	wait_queue_head_t dai_wait;
-};
-
 #define SITAR_MCLK_RATE_12288KHZ 12288000
 #define SITAR_MCLK_RATE_9600KHZ 9600000
 
@@ -177,6 +189,11 @@
 	MBHC_STATE_RELEASE,
 };
 
+static const u32 vport_check_table[NUM_CODEC_DAIS] = {
+	0,					/* AIF1_PB */
+	0,					/* AIF1_CAP */
+};
+
 struct sitar_priv {
 	struct snd_soc_codec *codec;
 	u32 mclk_freq;
@@ -239,7 +256,7 @@
 	const struct firmware *mbhc_fw;
 
 	/* num of slim ports required */
-	struct sitar_codec_dai_data dai[NUM_CODEC_DAIS];
+	struct wcd9xxx_codec_dai_data dai[NUM_CODEC_DAIS];
 
 	/* Currently, only used for mbhc purpose, to protect
 	 * concurrent execution of mbhc threaded irq handlers and
@@ -934,6 +951,181 @@
 	SOC_DAPM_SINGLE("Switch", SITAR_A_RX_EAR_EN, 5, 1, 0),
 };
 
+static int slim_tx_mixer_get(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+	struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+
+	ucontrol->value.integer.value[0] = widget->value;
+	return 0;
+}
+
+static int slim_tx_mixer_put(struct snd_kcontrol *kcontrol,
+			     struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+	struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+	struct snd_soc_codec *codec = widget->codec;
+	struct sitar_priv *sitar_p = snd_soc_codec_get_drvdata(codec);
+	struct wcd9xxx *core = dev_get_drvdata(codec->dev->parent);
+	struct soc_multi_mixer_control *mixer =
+		((struct soc_multi_mixer_control *)kcontrol->private_value);
+	u32 dai_id = widget->shift;
+	u32 port_id = mixer->shift;
+	u32 enable = ucontrol->value.integer.value[0];
+
+	mutex_lock(&codec->mutex);
+
+	if (sitar_p->intf_type != WCD9XXX_INTERFACE_TYPE_SLIMBUS) {
+		if (dai_id != AIF1_CAP) {
+			dev_err(codec->dev, "%s: invalid AIF for I2C mode\n",
+				__func__);
+			mutex_unlock(&codec->mutex);
+			return -EINVAL;
+		}
+	}
+
+	switch (dai_id) {
+	case AIF1_CAP:
+		if (enable && !(widget->value & 1 << port_id)) {
+			if (wcd9xxx_tx_vport_validation(
+						vport_check_table[dai_id],
+						port_id,
+						sitar_p->dai)) {
+				pr_info("%s: TX%u is used by other virtual port\n",
+					__func__, port_id + 1);
+				mutex_unlock(&codec->mutex);
+				return -EINVAL;
+			}
+			widget->value |= 1 << port_id;
+			list_add_tail(&core->tx_chs[port_id].list,
+			&sitar_p->dai[dai_id].wcd9xxx_ch_list);
+		} else if (!enable && (widget->value & 1 << port_id)) {
+			widget->value &= ~(1<<port_id);
+			list_del_init(&core->tx_chs[port_id].list);
+		} else {
+			if (enable)
+				pr_info("%s: TX%u port is used by this virtual port\n",
+					__func__, port_id + 1);
+			else
+				pr_info("%s: TX%u port is not used by this virtual port\n",
+					__func__, port_id + 1);
+			/* avoid update power function */
+			mutex_unlock(&codec->mutex);
+			return 0;
+		}
+	break;
+	default:
+		pr_err("Unknown AIF %d\n", dai_id);
+		mutex_unlock(&codec->mutex);
+		return -EINVAL;
+	}
+
+	pr_debug("%s: name %s sname %s updated value %u shift %d\n", __func__,
+		widget->name, widget->sname, widget->value, widget->shift);
+	snd_soc_dapm_mixer_update_power(widget, kcontrol, enable);
+	mutex_unlock(&codec->mutex);
+	return 0;
+}
+
+static int slim_rx_mux_get(struct snd_kcontrol *kcontrol,
+			   struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+	struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+
+	ucontrol->value.enumerated.item[0] = widget->value;
+	return 0;
+}
+
+static const char * const slim_rx_mux_text[] = {
+	"ZERO", "AIF1_PB"
+};
+
+static int slim_rx_mux_put(struct snd_kcontrol *kcontrol,
+			   struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+	struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+	struct snd_soc_codec *codec = widget->codec;
+	struct sitar_priv *sitar_p = snd_soc_codec_get_drvdata(codec);
+	struct wcd9xxx *core = dev_get_drvdata(codec->dev->parent);
+	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+	u32 port_id = widget->shift;
+
+	widget->value = ucontrol->value.enumerated.item[0];
+
+	mutex_lock(&codec->mutex);
+
+	if (sitar_p->intf_type != WCD9XXX_INTERFACE_TYPE_SLIMBUS) {
+		if (widget->value > 1) {
+			dev_err(codec->dev, "%s: invalid AIF for I2C mode\n",
+				__func__);
+			goto err;
+		}
+	}
+
+	switch (widget->value) {
+	case 0:
+		list_del_init(&core->rx_chs[port_id].list);
+		break;
+	case 1:
+		if (wcd9xxx_rx_vport_validation(port_id + core->num_tx_port,
+			&sitar_p->dai[AIF1_PB].wcd9xxx_ch_list))
+			goto pr_err;
+		list_add_tail(&core->rx_chs[port_id].list,
+			      &sitar_p->dai[AIF1_PB].wcd9xxx_ch_list);
+		break;
+	break;
+	default:
+		pr_err("Unknown AIF %d\n", widget->value);
+		goto err;
+	}
+
+
+	snd_soc_dapm_mux_update_power(widget, kcontrol, 1, widget->value, e);
+
+	mutex_unlock(&codec->mutex);
+	return 0;
+pr_err:
+	pr_err("%s: RX%u is used by current requesting AIF_PB itself\n",
+		__func__, port_id + 1);
+err:
+	mutex_unlock(&codec->mutex);
+	return -EINVAL;
+}
+
+static const struct soc_enum slim_rx_mux_enum =
+	SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(slim_rx_mux_text), slim_rx_mux_text);
+
+static const struct snd_kcontrol_new sitar_aif_pb_mux[SITAR_RX_MAX] = {
+	SOC_DAPM_ENUM_EXT("SLIM RX1 MUX", slim_rx_mux_enum,
+			  slim_rx_mux_get, slim_rx_mux_put),
+	SOC_DAPM_ENUM_EXT("SLIM RX2 MUX", slim_rx_mux_enum,
+			  slim_rx_mux_get, slim_rx_mux_put),
+	SOC_DAPM_ENUM_EXT("SLIM RX3 MUX", slim_rx_mux_enum,
+			  slim_rx_mux_get, slim_rx_mux_put),
+	SOC_DAPM_ENUM_EXT("SLIM RX4 MUX", slim_rx_mux_enum,
+			  slim_rx_mux_get, slim_rx_mux_put),
+	SOC_DAPM_ENUM_EXT("SLIM RX5 MUX", slim_rx_mux_enum,
+			  slim_rx_mux_get, slim_rx_mux_put)
+};
+
+static const struct snd_kcontrol_new sitar_aif_cap_mixer[SITAR_TX_MAX] = {
+	SOC_SINGLE_EXT("SLIM TX1", SND_SOC_NOPM, SITAR_TX1, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX2", SND_SOC_NOPM, SITAR_TX2, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX3", SND_SOC_NOPM, SITAR_TX3, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX4", SND_SOC_NOPM, SITAR_TX4, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX5", SND_SOC_NOPM, SITAR_TX5, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+};
+
+
 static void sitar_codec_enable_adc_block(struct snd_soc_codec *codec,
 	int enable)
 {
@@ -1046,9 +1238,9 @@
 		snd_soc_update_bits(codec, lineout_gain_reg, 0x10, 0x10);
 		break;
 	case SND_SOC_DAPM_POST_PMU:
-		pr_debug("%s: sleeping 16 ms after %s PA turn on\n",
+		pr_debug("%s: sleeping 32 ms after %s PA turn on\n",
 				__func__, w->name);
-		usleep_range(16000, 16000);
+		usleep_range(32000, 32000);
 		break;
 	case SND_SOC_DAPM_POST_PMD:
 		snd_soc_update_bits(codec, lineout_gain_reg, 0x10, 0x00);
@@ -1712,7 +1904,7 @@
 		/* reset retry counter as PA is turned off signifying
 		* start of new OCP detection session
 		*/
-		if (SITAR_IRQ_HPH_PA_OCPL_FAULT)
+		if (WCD9XXX_IRQ_HPH_PA_OCPL_FAULT)
 			sitar->hphlocp_cnt = 0;
 		else
 			sitar->hphrocp_cnt = 0;
@@ -1724,14 +1916,16 @@
 {
 	struct sitar_priv *sitar = container_of(work, struct sitar_priv,
 		hphlocp_work);
-	hphocp_off_report(sitar, SND_JACK_OC_HPHL, SITAR_IRQ_HPH_PA_OCPL_FAULT);
+	hphocp_off_report(sitar, SND_JACK_OC_HPHL,
+			  WCD9XXX_IRQ_HPH_PA_OCPL_FAULT);
 }
 
 static void hphrocp_off_report(struct work_struct *work)
 {
 	struct sitar_priv *sitar = container_of(work, struct sitar_priv,
 		hphrocp_work);
-	hphocp_off_report(sitar, SND_JACK_OC_HPHR, SITAR_IRQ_HPH_PA_OCPR_FAULT);
+	hphocp_off_report(sitar, SND_JACK_OC_HPHR,
+			  WCD9XXX_IRQ_HPH_PA_OCPR_FAULT);
 }
 
 static int sitar_hph_pa_event(struct snd_soc_dapm_widget *w,
@@ -1862,6 +2056,24 @@
 	return 0;
 }
 
+static int sitar_ear_pa_event(struct snd_soc_dapm_widget *w,
+		struct snd_kcontrol *kcontrol, int event)
+{
+	switch (event) {
+	case SND_SOC_DAPM_POST_PMU:
+		pr_debug("%s: Sleeping 20ms after enabling EAR PA\n",
+				 __func__);
+		msleep(20);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		pr_debug("%s: Sleeping 20ms after disabling EAR PA\n",
+				 __func__);
+		msleep(20);
+		break;
+	}
+	return 0;
+}
+
 static const struct snd_soc_dapm_widget sitar_dapm_i2s_widgets[] = {
 	SND_SOC_DAPM_SUPPLY("RX_I2S_CLK", SITAR_A_CDC_CLK_RX_I2S_CTL,
 	4, 0, NULL, 0),
@@ -1873,19 +2085,33 @@
 	/*RX stuff */
 	SND_SOC_DAPM_OUTPUT("EAR"),
 
-	SND_SOC_DAPM_PGA("EAR PA", SITAR_A_RX_EAR_EN, 4, 0, NULL, 0),
+	SND_SOC_DAPM_PGA_E("EAR PA", SITAR_A_RX_EAR_EN, 4, 0, NULL, 0,
+				sitar_ear_pa_event, SND_SOC_DAPM_POST_PMU |
+				SND_SOC_DAPM_POST_PMD),
 	SND_SOC_DAPM_MIXER("DAC1", SITAR_A_RX_EAR_EN, 6, 0, dac1_switch,
 		ARRAY_SIZE(dac1_switch)),
 	SND_SOC_DAPM_SUPPLY("EAR DRIVER", SITAR_A_RX_EAR_EN, 3, 0, NULL, 0),
-	SND_SOC_DAPM_AIF_IN_E("SLIM RX1", "AIF1 Playback", 0, SND_SOC_NOPM, 0,
-				0, sitar_codec_enable_slimrx,
-				SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
-	SND_SOC_DAPM_AIF_IN_E("SLIM RX2", "AIF1 Playback", 0, SND_SOC_NOPM, 0,
-				0, sitar_codec_enable_slimrx,
-				SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
-	SND_SOC_DAPM_AIF_IN("SLIM RX3", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0),
-	SND_SOC_DAPM_AIF_IN("SLIM RX4", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0),
-	SND_SOC_DAPM_AIF_IN("SLIM RX5", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0),
+
+	SND_SOC_DAPM_AIF_IN_E("AIF1 PB", "AIF1 Playback", 0, SND_SOC_NOPM,
+		AIF1_PB, 0, sitar_codec_enable_slimrx,
+		SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_MUX("SLIM RX1 MUX", SND_SOC_NOPM, SITAR_RX1, 0,
+		&sitar_aif_pb_mux[SITAR_RX1]),
+	SND_SOC_DAPM_MUX("SLIM RX2 MUX", SND_SOC_NOPM, SITAR_RX2, 0,
+		&sitar_aif_pb_mux[SITAR_RX2]),
+	SND_SOC_DAPM_MUX("SLIM RX3 MUX", SND_SOC_NOPM, SITAR_RX3, 0,
+		&sitar_aif_pb_mux[SITAR_RX3]),
+	SND_SOC_DAPM_MUX("SLIM RX4 MUX", SND_SOC_NOPM, SITAR_RX4, 0,
+		&sitar_aif_pb_mux[SITAR_RX4]),
+	SND_SOC_DAPM_MUX("SLIM RX5 MUX", SND_SOC_NOPM, SITAR_RX5, 0,
+		&sitar_aif_pb_mux[SITAR_RX5]),
+
+	SND_SOC_DAPM_MIXER("SLIM RX1", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("SLIM RX2", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("SLIM RX3", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("SLIM RX4", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("SLIM RX5", SND_SOC_NOPM, 0, 0, NULL, 0),
 
 	/* Headphone */
 	SND_SOC_DAPM_OUTPUT("HEADPHONE"),
@@ -2023,26 +2249,23 @@
 
 	SND_SOC_DAPM_MUX("ANC1 FB MUX", SND_SOC_NOPM, 0, 0, &anc1_fb_mux),
 
-	SND_SOC_DAPM_MUX("SLIM TX1 MUX", SND_SOC_NOPM, 0, 0, &sb_tx1_mux),
-	SND_SOC_DAPM_MUX("SLIM TX2 MUX", SND_SOC_NOPM, 0, 0, &sb_tx2_mux),
-	SND_SOC_DAPM_MUX("SLIM TX3 MUX", SND_SOC_NOPM, 0, 0, &sb_tx3_mux),
-	SND_SOC_DAPM_MUX("SLIM TX4 MUX", SND_SOC_NOPM, 0, 0, &sb_tx4_mux),
-	SND_SOC_DAPM_MUX("SLIM TX5 MUX", SND_SOC_NOPM, 0, 0, &sb_tx5_mux),
-
-	SND_SOC_DAPM_AIF_OUT_E("SLIM TX1", "AIF1 Capture", 0, SND_SOC_NOPM, 0,
-				0, sitar_codec_enable_slimtx,
+	SND_SOC_DAPM_AIF_OUT_E("AIF1 CAP", "AIF1 Capture", 0, SND_SOC_NOPM,
+				AIF1_CAP, 0, sitar_codec_enable_slimtx,
 				SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
 
-	SND_SOC_DAPM_AIF_OUT_E("SLIM TX2", "AIF1 Capture", 0, SND_SOC_NOPM, 0,
-				0, sitar_codec_enable_slimtx,
-				SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_MIXER("AIF1_CAP Mixer", SND_SOC_NOPM, AIF1_CAP, 0,
+		sitar_aif_cap_mixer, ARRAY_SIZE(sitar_aif_cap_mixer)),
 
-	SND_SOC_DAPM_AIF_OUT_E("SLIM TX3", "AIF1 Capture", 0, SND_SOC_NOPM, 0,
-				0, sitar_codec_enable_slimtx,
-				SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
-	SND_SOC_DAPM_AIF_OUT_E("SLIM TX4", "AIF1 Capture", 0, SND_SOC_NOPM, 0,
-				0, sitar_codec_enable_slimtx,
-				SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_MUX("SLIM TX1 MUX", SND_SOC_NOPM, SITAR_TX1, 0,
+		&sb_tx1_mux),
+	SND_SOC_DAPM_MUX("SLIM TX2 MUX", SND_SOC_NOPM, SITAR_TX2, 0,
+		&sb_tx2_mux),
+	SND_SOC_DAPM_MUX("SLIM TX3 MUX", SND_SOC_NOPM, SITAR_TX3, 0,
+		&sb_tx3_mux),
+	SND_SOC_DAPM_MUX("SLIM TX4 MUX", SND_SOC_NOPM, SITAR_TX3, 0,
+		&sb_tx4_mux),
+	SND_SOC_DAPM_MUX("SLIM TX5 MUX", SND_SOC_NOPM, SITAR_TX3, 0,
+		&sb_tx5_mux),
 
 	SND_SOC_DAPM_AIF_OUT_E("SLIM TX5", "AIF1 Capture", 0, SND_SOC_NOPM, 0,
 				0, sitar_codec_enable_slimtx,
@@ -2083,6 +2306,18 @@
 	{"SLIM TX3", NULL, "TX_I2S_CLK"},
 	{"SLIM TX4", NULL, "TX_I2S_CLK"},
 };
+#define SLIM_MIXER(x) (\
+	{x, "SLIM TX1", "SLIM TX1 MUX"}, \
+	{x, "SLIM TX2", "SLIM TX2 MUX"}, \
+	{x, "SLIM TX3", "SLIM TX3 MUX"}, \
+	{x, "SLIM TX4", "SLIM TX4 MUX"})
+
+
+#define SLIM_MUX(x, y) (\
+	{"SLIM RX1 MUX", x, y}, \
+	{"SLIM RX2 MUX", x, y}, \
+	{"SLIM RX3 MUX", x, y}, \
+	{"SLIM RX4 MUX", x, y})
 
 static const struct snd_soc_dapm_route audio_map[] = {
 	/* Earpiece (RX MIX1) */
@@ -2161,6 +2396,23 @@
 	{"RX3 MIX1", NULL, "ANC"},
 
 	/* SLIMBUS Connections */
+	{"AIF1 CAP", NULL, "AIF1_CAP Mixer"},
+
+	/* SLIM_MIXER("AIF1_CAP Mixer"),*/
+	{"AIF1_CAP Mixer", "SLIM TX1", "SLIM TX1 MUX"},
+	{"AIF1_CAP Mixer", "SLIM TX2", "SLIM TX2 MUX"},
+	{"AIF1_CAP Mixer", "SLIM TX3", "SLIM TX3 MUX"},
+	{"AIF1_CAP Mixer", "SLIM TX4", "SLIM TX4 MUX"},
+	/* SLIM_MUX("AIF1_PB", "AIF1 PB"), */
+	{"SLIM RX1 MUX", "AIF1_PB", "AIF1 PB"},
+	{"SLIM RX2 MUX", "AIF1_PB", "AIF1 PB"},
+	{"SLIM RX3 MUX", "AIF1_PB", "AIF1 PB"},
+	{"SLIM RX4 MUX", "AIF1_PB", "AIF1 PB"},
+
+	{"SLIM RX1", NULL, "SLIM RX1 MUX"},
+	{"SLIM RX2", NULL, "SLIM RX2 MUX"},
+	{"SLIM RX3", NULL, "SLIM RX3 MUX"},
+	{"SLIM RX4", NULL, "SLIM RX4 MUX"},
 
 	/* Slimbus port 5 is non functional in Sitar 1.0 */
 	{"RX1 MIX1 INP1", "RX1", "SLIM RX1"},
@@ -2202,12 +2454,6 @@
 
 
 	/* TX */
-	{"SLIM TX1", NULL, "SLIM TX1 MUX"},
-	{"SLIM TX2", NULL, "SLIM TX2 MUX"},
-	{"SLIM TX3", NULL, "SLIM TX3 MUX"},
-	{"SLIM TX4", NULL, "SLIM TX4 MUX"},
-	{"SLIM TX5", NULL, "SLIM TX5 MUX"},
-
 	{"SLIM TX1 MUX", "DEC1", "DEC1 MUX"},
 	{"SLIM TX2 MUX", "DEC2", "DEC2 MUX"},
 	{"SLIM TX3 MUX", "DEC3", "DEC3 MUX"},
@@ -2318,6 +2564,9 @@
 {
 	int ret;
 
+	if (reg == SND_SOC_NOPM)
+		return 0;
+
 	BUG_ON(reg > SITAR_MAX_REGISTER);
 
 	if (!sitar_volatile(codec, reg)) {
@@ -2335,6 +2584,9 @@
 	unsigned int val;
 	int ret;
 
+	if (reg == SND_SOC_NOPM)
+		return 0;
+
 	BUG_ON(reg > SITAR_MAX_REGISTER);
 
 	if (!sitar_volatile(codec, reg) && sitar_readable(codec, reg) &&
@@ -2582,11 +2834,11 @@
 		return;
 
 	if (dai->id <= NUM_CODEC_DAIS) {
-		if (sitar->dai[dai->id-1].ch_mask) {
+		if (sitar->dai[dai->id].ch_mask) {
 			active = 1;
 			pr_debug("%s(): Codec DAI: chmask[%d] = 0x%x\n",
-				__func__, dai->id-1,
-				sitar->dai[dai->id-1].ch_mask);
+				__func__, dai->id,
+				sitar->dai[dai->id].ch_mask);
 		}
 	}
 
@@ -2700,38 +2952,16 @@
 
 {
 	struct sitar_priv *sitar = snd_soc_codec_get_drvdata(dai->codec);
-	u32 i = 0;
+	struct wcd9xxx *core = dev_get_drvdata(dai->codec->dev->parent);
 	if (!tx_slot && !rx_slot) {
 		pr_err("%s: Invalid\n", __func__);
 		return -EINVAL;
 	}
 	pr_debug("%s: DAI-ID %x %d %d\n", __func__, dai->id, tx_num, rx_num);
 
-	if (dai->id == AIF1_PB) {
-		for (i = 0; i < rx_num; i++) {
-			sitar->dai[dai->id - 1].ch_num[i]  = rx_slot[i];
-			sitar->dai[dai->id - 1].ch_act = 0;
-			sitar->dai[dai->id - 1].ch_tot = rx_num;
-		}
-	} else if (dai->id == AIF1_CAP) {
-		sitar->dai[dai->id - 1].ch_tot = tx_num;
-		/* If all channels are already active,
-		 * Do not reset ch_act flag
-		 */
-		if ((sitar->dai[dai->id - 1].ch_tot != 0)
-			&& (sitar->dai[dai->id - 1].ch_act ==
-				sitar->dai[dai->id - 1].ch_tot)) {
-			pr_info("%s: ch_act = %d, ch_tot = %d\n", __func__,
-				sitar->dai[dai->id - 1].ch_act,
-				sitar->dai[dai->id - 1].ch_tot);
-
-			return 0;
-		}
-		sitar->dai[dai->id - 1].ch_act = 0;
-
-		for (i = 0; i < tx_num; i++)
-			sitar->dai[dai->id - 1].ch_num[i]  = tx_slot[i];
-	}
+	if (sitar->intf_type == WCD9XXX_INTERFACE_TYPE_SLIMBUS)
+		wcd9xxx_init_slimslave(core, core->slim->laddr,
+			tx_num, tx_slot, rx_num, rx_slot);
 	return 0;
 }
 
@@ -2740,33 +2970,38 @@
 			unsigned int *rx_num, unsigned int *rx_slot)
 
 {
-	struct wcd9xxx *sitar = dev_get_drvdata(dai->codec->control_data);
+	struct sitar_priv *sitar_p = snd_soc_codec_get_drvdata(dai->codec);
+	u32 i = 0;
+	struct wcd9xxx_ch *ch;
 
-	u32 cnt = 0;
-	u32 tx_ch[SLIM_MAX_TX_PORTS];
-	u32 rx_ch[SLIM_MAX_RX_PORTS];
-
-	if (!rx_slot && !tx_slot) {
-		pr_err("%s: Invalid\n", __func__);
-		return -EINVAL;
-	}
-	pr_debug("%s: DAI-ID %x\n", __func__, dai->id);
-	/* for virtual port, codec driver needs to do
-	* housekeeping, for now should be ok
-	*/
-	wcd9xxx_get_channel(sitar, rx_ch, tx_ch);
-	if (dai->id == AIF1_PB) {
-		*rx_num = sitar_dai[dai->id - 1].playback.channels_max;
-		while (cnt < *rx_num) {
-			rx_slot[cnt] = rx_ch[cnt];
-			cnt++;
+	switch (dai->id) {
+	case AIF1_PB:
+		if (!rx_slot || !rx_num) {
+			pr_err("%s: Invalid rx_slot 0x%x or rx_num 0x%x\n",
+				__func__, (u32) rx_slot, (u32) rx_num);
+			return -EINVAL;
 		}
-	} else if (dai->id == AIF1_CAP) {
-		*tx_num = sitar_dai[dai->id - 1].capture.channels_max;
-		tx_slot[0] = tx_ch[cnt];
-		tx_slot[1] = tx_ch[4 + cnt];
-		tx_slot[2] = tx_ch[2 + cnt];
-		tx_slot[3] = tx_ch[3 + cnt];
+		list_for_each_entry(ch, &sitar_p->dai[dai->id].wcd9xxx_ch_list,
+				    list) {
+			rx_slot[i++] = ch->ch_num;
+		}
+		*rx_num = i;
+		break;
+	case AIF1_CAP:
+		if (!tx_slot || !tx_num) {
+			pr_err("%s: Invalid tx_slot 0x%x or tx_num 0x%x\n",
+				__func__, (u32) tx_slot, (u32) tx_num);
+			return -EINVAL;
+		}
+		list_for_each_entry(ch, &sitar_p->dai[dai->id].wcd9xxx_ch_list,
+				    list) {
+			tx_slot[i++] = ch->ch_num;
+		}
+		*tx_num = i;
+		break;
+	default:
+		pr_err("%s: Invalid dai %d", __func__, dai->id);
+		return -EINVAL;
 	}
 	return 0;
 }
@@ -2802,7 +3037,7 @@
 		break;
 	default:
 		pr_err("%s: Invalid sampling rate %d\n", __func__,
-				params_rate(params));
+			params_rate(params));
 		return -EINVAL;
 	}
 
@@ -2839,13 +3074,14 @@
 					0x20, 0x00);
 				break;
 			default:
-				pr_err("invalid format\n");
-				break;
+				pr_err("%s: Unsupport format %d\n", __func__,
+					params_format(params));
+				return -EINVAL;
 			}
 			snd_soc_update_bits(codec, SITAR_A_CDC_CLK_TX_I2S_CTL,
 						0x03, tx_fs_rate);
 		} else {
-			sitar->dai[dai->id - 1].rate   = params_rate(params);
+			sitar->dai[dai->id].rate   = params_rate(params);
 		}
 	}
 
@@ -2886,13 +3122,14 @@
 					0x20, 0x00);
 				break;
 			default:
-				pr_err("invalid format\n");
+				pr_err("%s: Unsupport format %d\n", __func__,
+					params_format(params));
 				break;
 			}
 			snd_soc_update_bits(codec, SITAR_A_CDC_CLK_RX_I2S_CTL,
 						0x03, (rx_fs_rate >> 0x05));
 		} else {
-			sitar->dai[dai->id - 1].rate   = params_rate(params);
+			sitar->dai[dai->id].rate   = params_rate(params);
 		}
 	}
 
@@ -2974,30 +3211,30 @@
 static int sitar_codec_enable_chmask(struct sitar_priv *sitar,
 	int event, int index)
 {
-	int ret = 0;
-	u32 k = 0;
+	int  ret = 0;
+	struct wcd9xxx_ch *ch;
+
 	switch (event) {
 	case SND_SOC_DAPM_POST_PMU:
-		for (k = 0; k < sitar->dai[index].ch_tot; k++) {
-			ret = wcd9xxx_get_slave_port(
-						sitar->dai[index].ch_num[k]);
+		list_for_each_entry(ch,
+			&sitar->dai[index].wcd9xxx_ch_list, list) {
+			ret = wcd9xxx_get_slave_port(ch->ch_num);
 			if (ret < 0) {
-				pr_err("%s: Invalid Slave port ID: %d\n",
-					   __func__, ret);
+				pr_err("%s: Invalid slave port ID: %d\n",
+					__func__, ret);
 				ret = -EINVAL;
 				break;
 			}
 			sitar->dai[index].ch_mask |= 1 << ret;
 		}
-		ret = 0;
 		break;
 	case SND_SOC_DAPM_POST_PMD:
 		ret = wait_event_timeout(sitar->dai[index].dai_wait,
 					(sitar->dai[index].ch_mask == 0),
-					msecs_to_jiffies(SLIM_CLOSE_TIMEOUT));
+					 msecs_to_jiffies(SLIM_CLOSE_TIMEOUT));
 		if (!ret) {
-			pr_err("%s: slim close tx/rx timeout\n",
-				   __func__);
+			pr_err("%s: Slim close tx/rx wait timeout\n",
+				__func__);
 			ret = -EINVAL;
 		} else {
 			ret = 0;
@@ -3010,70 +3247,46 @@
 static int sitar_codec_enable_slimrx(struct snd_soc_dapm_widget *w,
 	struct snd_kcontrol *kcontrol, int event)
 {
-	struct wcd9xxx *sitar;
+	struct wcd9xxx *core;
 	struct snd_soc_codec *codec = w->codec;
 	struct sitar_priv *sitar_p = snd_soc_codec_get_drvdata(codec);
-	u32  j = 0;
-	int ret = 0;
-	codec->control_data = dev_get_drvdata(codec->dev->parent);
-	sitar = codec->control_data;
+	int ret  = 0;
+	struct wcd9xxx_codec_dai_data *dai;
+
+	core = dev_get_drvdata(codec->dev->parent);
 
 	/* Execute the callback only if interface type is slimbus */
 	if (sitar_p->intf_type != WCD9XXX_INTERFACE_TYPE_SLIMBUS) {
-		if (event == SND_SOC_DAPM_POST_PMD && (sitar != NULL))
-			sitar_codec_pm_runtime_put(sitar);
+		if (event == SND_SOC_DAPM_POST_PMD && (core != NULL))
+			sitar_codec_pm_runtime_put(core);
 		return 0;
 	}
 
+	dai = &sitar_p->dai[w->shift];
+
 	switch (event) {
 	case SND_SOC_DAPM_POST_PMU:
-		for (j = 0; j < ARRAY_SIZE(sitar_dai); j++) {
-			if (sitar_dai[j].id == AIF1_CAP)
-				continue;
-			if (!strncmp(w->sname,
-				sitar_dai[j].playback.stream_name, 13)) {
-				++sitar_p->dai[j].ch_act;
-				break;
-			}
-		}
-		if (sitar_p->dai[j].ch_act == sitar_p->dai[j].ch_tot) {
-			ret = sitar_codec_enable_chmask(sitar_p, event, j);
-			ret = wcd9xxx_cfg_slim_sch_rx(sitar,
-					sitar_p->dai[j].ch_num,
-					sitar_p->dai[j].ch_tot,
-					sitar_p->dai[j].rate);
-		}
+		ret = sitar_codec_enable_chmask(sitar_p, SND_SOC_DAPM_POST_PMU,
+						w->shift);
+		ret = wcd9xxx_cfg_slim_sch_rx(core, &dai->wcd9xxx_ch_list,
+					      dai->rate, dai->bit_width,
+					      &dai->grph);
 		break;
 	case SND_SOC_DAPM_POST_PMD:
-		for (j = 0; j < ARRAY_SIZE(sitar_dai); j++) {
-			if (sitar_dai[j].id == AIF1_CAP)
-				continue;
-			if (!strncmp(w->sname,
-				sitar_dai[j].playback.stream_name, 13)) {
-				--sitar_p->dai[j].ch_act;
-				break;
-			}
-		}
-		if (!sitar_p->dai[j].ch_act) {
-			wcd9xxx_close_slim_sch_rx(sitar,
-					sitar_p->dai[j].ch_num,
-					sitar_p->dai[j].ch_tot);
-			ret = sitar_codec_enable_chmask(sitar_p, event, j);
-			if (ret < 0) {
-				ret = wcd9xxx_disconnect_port(sitar,
-						sitar_p->dai[j].ch_num,
-						sitar_p->dai[j].ch_tot,
-						1);
+		ret = wcd9xxx_close_slim_sch_rx(core, &dai->wcd9xxx_ch_list,
+						dai->grph);
+		ret = sitar_codec_enable_chmask(sitar_p, SND_SOC_DAPM_POST_PMD,
+						w->shift);
+		if (ret < 0) {
+			ret = wcd9xxx_disconnect_port(core,
+						      &dai->wcd9xxx_ch_list,
+						      dai->grph);
 				pr_info("%s: Disconnect RX port ret = %d\n",
-						__func__, ret);
-			}
-			sitar_p->dai[j].rate = 0;
-			memset(sitar_p->dai[j].ch_num, 0, (sizeof(u32)*
-					sitar_p->dai[j].ch_tot));
-			sitar_p->dai[j].ch_tot = 0;
-			if (sitar != NULL)
-				sitar_codec_pm_runtime_put(sitar);
+					__func__, ret);
 		}
+		if (core != NULL)
+			sitar_codec_pm_runtime_put(core);
+		break;
 	}
 	return ret;
 }
@@ -3081,72 +3294,45 @@
 static int sitar_codec_enable_slimtx(struct snd_soc_dapm_widget *w,
 	struct snd_kcontrol *kcontrol, int event)
 {
-	struct wcd9xxx *sitar;
+	struct wcd9xxx *core;
 	struct snd_soc_codec *codec = w->codec;
 	struct sitar_priv *sitar_p = snd_soc_codec_get_drvdata(codec);
-	/* index to the DAI ID, for now hardcoding */
-	u32  j = 0;
+	struct wcd9xxx_codec_dai_data *dai;
 	int ret = 0;
 
-	codec->control_data = dev_get_drvdata(codec->dev->parent);
-	sitar = codec->control_data;
+	core = dev_get_drvdata(codec->dev->parent);
 
 	/* Execute the callback only if interface type is slimbus */
 	if (sitar_p->intf_type != WCD9XXX_INTERFACE_TYPE_SLIMBUS) {
-		if (event == SND_SOC_DAPM_POST_PMD && (sitar != NULL))
-			sitar_codec_pm_runtime_put(sitar);
+		if (event == SND_SOC_DAPM_POST_PMD && (core != NULL))
+			sitar_codec_pm_runtime_put(core);
 		return 0;
 	}
 
+	dai = &sitar_p->dai[w->shift];
 	switch (event) {
 	case SND_SOC_DAPM_POST_PMU:
-		for (j = 0; j < ARRAY_SIZE(sitar_dai); j++) {
-			if (sitar_dai[j].id == AIF1_PB)
-				continue;
-			if (!strncmp(w->sname,
-				sitar_dai[j].capture.stream_name, 13)) {
-				++sitar_p->dai[j].ch_act;
-				break;
-			}
-		}
-		if (sitar_p->dai[j].ch_act == sitar_p->dai[j].ch_tot) {
-			ret = sitar_codec_enable_chmask(sitar_p, event, j);
-			ret = wcd9xxx_cfg_slim_sch_tx(sitar,
-					sitar_p->dai[j].ch_num,
-					sitar_p->dai[j].ch_tot,
-					sitar_p->dai[j].rate);
-		}
+		ret = sitar_codec_enable_chmask(sitar_p, SND_SOC_DAPM_POST_PMU,
+						w->shift);
+		ret = wcd9xxx_cfg_slim_sch_tx(core, &dai->wcd9xxx_ch_list,
+					      dai->rate, dai->bit_width,
+					      &dai->grph);
 		break;
 	case SND_SOC_DAPM_POST_PMD:
-		for (j = 0; j < ARRAY_SIZE(sitar_dai); j++) {
-			if (sitar_dai[j].id == AIF1_PB)
-				continue;
-			if (!strncmp(w->sname,
-				sitar_dai[j].capture.stream_name, 13)) {
-				--sitar_p->dai[j].ch_act;
-				break;
-			}
+		ret = wcd9xxx_close_slim_sch_tx(core, &dai->wcd9xxx_ch_list,
+						dai->grph);
+		ret = sitar_codec_enable_chmask(sitar_p, SND_SOC_DAPM_POST_PMD,
+						w->shift);
+		if (ret < 0) {
+			ret = wcd9xxx_disconnect_port(core,
+						      &dai->wcd9xxx_ch_list,
+						      dai->grph);
+				pr_info("%s: Disconnect RX port ret = %d\n",
+					__func__, ret);
 		}
-		if (!sitar_p->dai[j].ch_act) {
-			wcd9xxx_close_slim_sch_tx(sitar,
-					sitar_p->dai[j].ch_num,
-					sitar_p->dai[j].ch_tot);
-			ret = sitar_codec_enable_chmask(sitar_p, event, j);
-			if (ret < 0) {
-				ret = wcd9xxx_disconnect_port(sitar,
-						sitar_p->dai[j].ch_num,
-						sitar_p->dai[j].ch_tot,
-						0);
-				pr_info("%s: Disconnect TX port, ret = %d\n",
-						__func__, ret);
-			}
-			sitar_p->dai[j].rate = 0;
-			memset(sitar_p->dai[j].ch_num, 0, (sizeof(u32)*
-					sitar_p->dai[j].ch_tot));
-			sitar_p->dai[j].ch_tot = 0;
-			if (sitar != NULL)
-				sitar_codec_pm_runtime_put(sitar);
-		}
+		if (core != NULL)
+			sitar_codec_pm_runtime_put(core);
+		break;
 	}
 	return ret;
 }
@@ -3186,7 +3372,7 @@
 	short bias_value;
 	struct sitar_priv *sitar = snd_soc_codec_get_drvdata(codec);
 
-	wcd9xxx_disable_irq(codec->control_data, SITAR_IRQ_MBHC_POTENTIAL);
+	wcd9xxx_disable_irq(codec->control_data, WCD9XXX_IRQ_MBHC_POTENTIAL);
 	if (noreldetection)
 		sitar_turn_onoff_rel_detection(codec, false);
 
@@ -3222,7 +3408,7 @@
 
 	if (noreldetection)
 		sitar_turn_onoff_rel_detection(codec, true);
-	wcd9xxx_enable_irq(codec->control_data, SITAR_IRQ_MBHC_POTENTIAL);
+	wcd9xxx_enable_irq(codec->control_data, WCD9XXX_IRQ_MBHC_POTENTIAL);
 
 	return bias_value;
 }
@@ -3440,7 +3626,7 @@
 	sitar = snd_soc_codec_get_drvdata(codec);
 	calibration = sitar->mbhc_cfg.calibration;
 
-	wcd9xxx_disable_irq(codec->control_data, SITAR_IRQ_MBHC_POTENTIAL);
+	wcd9xxx_disable_irq(codec->control_data, WCD9XXX_IRQ_MBHC_POTENTIAL);
 	sitar_turn_onoff_rel_detection(codec, false);
 
 	/* First compute the DCE / STA wait times
@@ -3523,7 +3709,7 @@
 	snd_soc_write(codec, SITAR_A_MBHC_SCALING_MUX_1, 0x84);
 	usleep_range(100, 100);
 
-	wcd9xxx_enable_irq(codec->control_data, SITAR_IRQ_MBHC_POTENTIAL);
+	wcd9xxx_enable_irq(codec->control_data, WCD9XXX_IRQ_MBHC_POTENTIAL);
 	sitar_turn_onoff_rel_detection(codec, true);
 }
 
@@ -3807,9 +3993,9 @@
 		}
 		sitar_set_and_turnoff_hph_padac(codec);
 		hphocp_off_report(sitar, SND_JACK_OC_HPHR,
-				  SITAR_IRQ_HPH_PA_OCPR_FAULT);
+				  WCD9XXX_IRQ_HPH_PA_OCPR_FAULT);
 		hphocp_off_report(sitar, SND_JACK_OC_HPHL,
-				  SITAR_IRQ_HPH_PA_OCPL_FAULT);
+				  WCD9XXX_IRQ_HPH_PA_OCPL_FAULT);
 		sitar->current_plug = PLUG_TYPE_NONE;
 		sitar->mbhc_polling_active = false;
 	} else {
@@ -4231,9 +4417,9 @@
 		snd_soc_update_bits(codec, SITAR_A_RX_HPH_OCP_CTL,
 							0x10, 0x10);
 		wcd9xxx_enable_irq(codec->control_data,
-					SITAR_IRQ_HPH_PA_OCPL_FAULT);
+				   WCD9XXX_IRQ_HPH_PA_OCPL_FAULT);
 		wcd9xxx_enable_irq(codec->control_data,
-					SITAR_IRQ_HPH_PA_OCPR_FAULT);
+				   WCD9XXX_IRQ_HPH_PA_OCPR_FAULT);
 		/* Bootup time detection */
 		sitar_hs_gpio_handler(codec);
 	}
@@ -4623,7 +4809,7 @@
 					    0x10);
 		} else {
 			wcd9xxx_disable_irq(codec->control_data,
-					  SITAR_IRQ_HPH_PA_OCPL_FAULT);
+					    WCD9XXX_IRQ_HPH_PA_OCPL_FAULT);
 			sitar->hphlocp_cnt = 0;
 			sitar->hph_status |= SND_JACK_OC_HPHL;
 			if (sitar->mbhc_cfg.headset_jack)
@@ -4656,7 +4842,7 @@
 					   0x10);
 		} else {
 			wcd9xxx_disable_irq(codec->control_data,
-					 SITAR_IRQ_HPH_PA_OCPR_FAULT);
+					    WCD9XXX_IRQ_HPH_PA_OCPR_FAULT);
 			sitar->hphrocp_cnt = 0;
 			sitar->hph_status |= SND_JACK_OC_HPHR;
 			if (sitar->mbhc_cfg.headset_jack)
@@ -4679,7 +4865,7 @@
 
 	pr_debug("%s: enter\n", __func__);
 	SITAR_ACQUIRE_LOCK(priv->codec_resource_lock);
-	wcd9xxx_disable_irq(codec->control_data, SITAR_IRQ_MBHC_INSERTION);
+	wcd9xxx_disable_irq(codec->control_data, WCD9XXX_IRQ_MBHC_INSERTION);
 
 	snd_soc_update_bits(codec, SITAR_A_CDC_MBHC_INT_CTL, 0x03, 0x00);
 
@@ -4860,6 +5046,7 @@
 	u8 flag = pdata->amic_settings.use_pdata;
 	u8 i = 0, j = 0;
 	u8 val_txfe = 0, value = 0;
+	int amic_reg_count = 0;
 
 	if (!pdata) {
 		rc = -ENODEV;
@@ -4905,7 +5092,8 @@
 	snd_soc_update_bits(codec, SITAR_A_MICB_2_CTL, 0x10,
 		(pdata->micbias.bias2_cap_mode << 4));
 
-	for (i = 0; i < 6; j++, i += 2) {
+	amic_reg_count = (NUM_AMIC % 2) ? NUM_AMIC + 1 : NUM_AMIC;
+	for (i = 0; i < amic_reg_count; j++, i += 2) {
 		if (flag & (0x01 << i)) {
 			value = (leg_mode & (0x01 << i)) ? 0x10 : 0x00;
 			val_txfe = (txfe_bypass & (0x01 << i)) ? 0x20 : 0x00;
@@ -4969,6 +5157,8 @@
 	SITAR_REG_VAL(SITAR_A_RX_EAR_GAIN, 0x02),
 	SITAR_REG_VAL(SITAR_A_RX_EAR_VCM, 0x03),
 
+	SITAR_REG_VAL(SITAR_A_RX_LINE_BIAS_PA, 0xA7),
+
 	SITAR_REG_VAL(SITAR_A_CDC_RX1_B5_CTL, 0x78),
 	SITAR_REG_VAL(SITAR_A_CDC_RX2_B5_CTL, 0x78),
 	SITAR_REG_VAL(SITAR_A_CDC_RX3_B5_CTL, 0x78),
@@ -5058,16 +5248,16 @@
 
 static int sitar_codec_probe(struct snd_soc_codec *codec)
 {
-	struct sitar *control;
+	struct wcd9xxx *core;
 	struct sitar_priv *sitar;
 	struct snd_soc_dapm_context *dapm = &codec->dapm;
 	int ret = 0;
 	int i;
 	u8 sitar_version;
-	int ch_cnt;
+	void *ptr = NULL;
 
 	codec->control_data = dev_get_drvdata(codec->dev->parent);
-	control = codec->control_data;
+	core = codec->control_data;
 
 	sitar = kzalloc(sizeof(struct sitar_priv), GFP_KERNEL);
 	if (!sitar) {
@@ -5116,12 +5306,35 @@
 		ARRAY_SIZE(sitar_snd_controls));
 	snd_soc_dapm_new_controls(dapm, sitar_dapm_widgets,
 		ARRAY_SIZE(sitar_dapm_widgets));
+
+	ptr = kmalloc((sizeof(sitar_rx_chs) +
+		       sizeof(sitar_tx_chs)), GFP_KERNEL);
+	if (!ptr) {
+		pr_err("%s: no mem for slim chan ctl data\n", __func__);
+		ret = -ENOMEM;
+		goto err_nomem_slimch;
+	}
 	if (sitar->intf_type == WCD9XXX_INTERFACE_TYPE_I2C) {
 		snd_soc_dapm_new_controls(dapm, sitar_dapm_i2s_widgets,
 			ARRAY_SIZE(sitar_dapm_i2s_widgets));
 		snd_soc_dapm_add_routes(dapm, audio_i2s_map,
 		ARRAY_SIZE(audio_i2s_map));
+		for (i = 0; i < ARRAY_SIZE(sitar_i2s_dai); i++)
+			INIT_LIST_HEAD(&sitar->dai[i].wcd9xxx_ch_list);
 	}
+	if (sitar->intf_type == WCD9XXX_INTERFACE_TYPE_SLIMBUS) {
+		for (i = 0; i < NUM_CODEC_DAIS; i++) {
+			INIT_LIST_HEAD(&sitar->dai[i].wcd9xxx_ch_list);
+			init_waitqueue_head(&sitar->dai[i].dai_wait);
+		}
+	}
+	core->num_rx_port = SITAR_RX_MAX;
+	core->rx_chs = ptr;
+	memcpy(core->rx_chs, sitar_rx_chs, sizeof(sitar_rx_chs));
+	core->num_tx_port = SITAR_TX_MAX;
+	core->tx_chs = ptr + sizeof(sitar_rx_chs);
+	memcpy(core->tx_chs, sitar_tx_chs, sizeof(sitar_tx_chs));
+
 	snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map));
 
 	sitar_version = snd_soc_read(codec, WCD9XXX_A_CHIP_VERSION);
@@ -5133,44 +5346,49 @@
 	snd_soc_dapm_sync(dapm);
 
 
-	ret = wcd9xxx_request_irq(codec->control_data, SITAR_IRQ_MBHC_INSERTION,
+	ret = wcd9xxx_request_irq(codec->control_data,
+				  WCD9XXX_IRQ_MBHC_INSERTION,
 		sitar_hs_insert_irq, "Headset insert detect", sitar);
 	if (ret) {
 		pr_err("%s: Failed to request irq %d\n", __func__,
-			SITAR_IRQ_MBHC_INSERTION);
+		       WCD9XXX_IRQ_MBHC_INSERTION);
 		goto err_insert_irq;
 	}
-	wcd9xxx_disable_irq(codec->control_data, SITAR_IRQ_MBHC_INSERTION);
+	wcd9xxx_disable_irq(codec->control_data, WCD9XXX_IRQ_MBHC_INSERTION);
 
-	ret = wcd9xxx_request_irq(codec->control_data, SITAR_IRQ_MBHC_REMOVAL,
+	ret = wcd9xxx_request_irq(codec->control_data,
+				  WCD9XXX_IRQ_MBHC_REMOVAL,
 		sitar_hs_remove_irq, "Headset remove detect", sitar);
 	if (ret) {
 		pr_err("%s: Failed to request irq %d\n", __func__,
-			SITAR_IRQ_MBHC_REMOVAL);
+		       WCD9XXX_IRQ_MBHC_REMOVAL);
 		goto err_remove_irq;
 	}
 
-	ret = wcd9xxx_request_irq(codec->control_data, SITAR_IRQ_MBHC_POTENTIAL,
+	ret = wcd9xxx_request_irq(codec->control_data,
+				  WCD9XXX_IRQ_MBHC_POTENTIAL,
 		sitar_dce_handler, "DC Estimation detect", sitar);
 	if (ret) {
 		pr_err("%s: Failed to request irq %d\n", __func__,
-			SITAR_IRQ_MBHC_POTENTIAL);
+		       WCD9XXX_IRQ_MBHC_POTENTIAL);
 		goto err_potential_irq;
 	}
 
-	ret = wcd9xxx_request_irq(codec->control_data, SITAR_IRQ_MBHC_RELEASE,
-		sitar_release_handler, "Button Release detect", sitar);
+	ret = wcd9xxx_request_irq(codec->control_data,
+				  WCD9XXX_IRQ_MBHC_RELEASE,
+				  sitar_release_handler,
+				  "Button Release detect", sitar);
 	if (ret) {
 		pr_err("%s: Failed to request irq %d\n", __func__,
-			SITAR_IRQ_MBHC_RELEASE);
+		       WCD9XXX_IRQ_MBHC_RELEASE);
 		goto err_release_irq;
 	}
 
-	ret = wcd9xxx_request_irq(codec->control_data, SITAR_IRQ_SLIMBUS,
-		sitar_slimbus_irq, "SLIMBUS Slave", sitar);
+	ret = wcd9xxx_request_irq(codec->control_data, WCD9XXX_IRQ_SLIMBUS,
+				  sitar_slimbus_irq, "SLIMBUS Slave", sitar);
 	if (ret) {
 		pr_err("%s: Failed to request irq %d\n", __func__,
-			SITAR_IRQ_SLIMBUS);
+		       WCD9XXX_IRQ_SLIMBUS);
 		goto err_slimbus_irq;
 	}
 
@@ -5180,40 +5398,27 @@
 
 
 	ret = wcd9xxx_request_irq(codec->control_data,
-		SITAR_IRQ_HPH_PA_OCPL_FAULT, sitar_hphl_ocp_irq,
-		"HPH_L OCP detect", sitar);
+				  WCD9XXX_IRQ_HPH_PA_OCPL_FAULT,
+				  sitar_hphl_ocp_irq,
+				  "HPH_L OCP detect", sitar);
 	if (ret) {
 		pr_err("%s: Failed to request irq %d\n", __func__,
-			SITAR_IRQ_HPH_PA_OCPL_FAULT);
+		       WCD9XXX_IRQ_HPH_PA_OCPL_FAULT);
 		goto err_hphl_ocp_irq;
 	}
-	wcd9xxx_disable_irq(codec->control_data, SITAR_IRQ_HPH_PA_OCPL_FAULT);
+	wcd9xxx_disable_irq(codec->control_data,
+			    WCD9XXX_IRQ_HPH_PA_OCPL_FAULT);
 
 	ret = wcd9xxx_request_irq(codec->control_data,
-		SITAR_IRQ_HPH_PA_OCPR_FAULT, sitar_hphr_ocp_irq,
-		"HPH_R OCP detect", sitar);
+				  WCD9XXX_IRQ_HPH_PA_OCPR_FAULT,
+				  sitar_hphr_ocp_irq, "HPH_R OCP detect",
+				  sitar);
 	if (ret) {
 		pr_err("%s: Failed to request irq %d\n", __func__,
-			SITAR_IRQ_HPH_PA_OCPR_FAULT);
+		       WCD9XXX_IRQ_HPH_PA_OCPR_FAULT);
 		goto err_hphr_ocp_irq;
 	}
-	wcd9xxx_disable_irq(codec->control_data, SITAR_IRQ_HPH_PA_OCPR_FAULT);
-
-	for (i = 0; i < ARRAY_SIZE(sitar_dai); i++) {
-		switch (sitar_dai[i].id) {
-		case AIF1_PB:
-			ch_cnt = sitar_dai[i].playback.channels_max;
-			break;
-		case AIF1_CAP:
-			ch_cnt = sitar_dai[i].capture.channels_max;
-			break;
-		default:
-			continue;
-		}
-		sitar->dai[i].ch_num = kzalloc((sizeof(unsigned int)*
-					ch_cnt), GFP_KERNEL);
-		init_waitqueue_head(&sitar->dai[i].dai_wait);
-	}
+	wcd9xxx_disable_irq(codec->control_data, WCD9XXX_IRQ_HPH_PA_OCPR_FAULT);
 
 	codec->ignore_pmdown_time = 1;
 
@@ -5224,24 +5429,23 @@
 	return ret;
 
 err_hphr_ocp_irq:
-	wcd9xxx_free_irq(codec->control_data,
-			SITAR_IRQ_HPH_PA_OCPL_FAULT, sitar);
+	wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_HPH_PA_OCPL_FAULT,
+			 sitar);
 err_hphl_ocp_irq:
-	wcd9xxx_free_irq(codec->control_data,
-			SITAR_IRQ_SLIMBUS, sitar);
+	wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_SLIMBUS, sitar);
 err_slimbus_irq:
-	wcd9xxx_free_irq(codec->control_data,
-			SITAR_IRQ_MBHC_RELEASE, sitar);
+	wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_MBHC_RELEASE, sitar);
 err_release_irq:
-	wcd9xxx_free_irq(codec->control_data,
-			SITAR_IRQ_MBHC_POTENTIAL, sitar);
+	wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_MBHC_POTENTIAL,
+			 sitar);
 err_potential_irq:
-	wcd9xxx_free_irq(codec->control_data,
-			SITAR_IRQ_MBHC_REMOVAL, sitar);
+	wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_MBHC_REMOVAL, sitar);
 err_remove_irq:
-	wcd9xxx_free_irq(codec->control_data,
-			SITAR_IRQ_MBHC_INSERTION, sitar);
+	wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_MBHC_INSERTION,
+			 sitar);
 err_insert_irq:
+	kfree(ptr);
+err_nomem_slimch:
 err_pdata:
 	mutex_destroy(&sitar->codec_resource_lock);
 	kfree(sitar);
@@ -5249,21 +5453,20 @@
 }
 static int sitar_codec_remove(struct snd_soc_codec *codec)
 {
-	int i;
 	struct sitar_priv *sitar = snd_soc_codec_get_drvdata(codec);
-	wcd9xxx_free_irq(codec->control_data, SITAR_IRQ_SLIMBUS, sitar);
-	wcd9xxx_free_irq(codec->control_data, SITAR_IRQ_MBHC_RELEASE, sitar);
-	wcd9xxx_free_irq(codec->control_data, SITAR_IRQ_MBHC_POTENTIAL, sitar);
-	wcd9xxx_free_irq(codec->control_data, SITAR_IRQ_MBHC_REMOVAL, sitar);
-	wcd9xxx_free_irq(codec->control_data, SITAR_IRQ_MBHC_INSERTION, sitar);
+	wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_SLIMBUS, sitar);
+	wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_MBHC_RELEASE, sitar);
+	wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_MBHC_POTENTIAL,
+			 sitar);
+	wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_MBHC_REMOVAL, sitar);
+	wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_MBHC_INSERTION,
+			 sitar);
 	SITAR_ACQUIRE_LOCK(sitar->codec_resource_lock);
 	sitar_codec_disable_clock_block(codec);
 	SITAR_RELEASE_LOCK(sitar->codec_resource_lock);
 	sitar_codec_enable_bandgap(codec, SITAR_BANDGAP_OFF);
 	if (sitar->mbhc_fw)
 		release_firmware(sitar->mbhc_fw);
-	for (i = 0; i < ARRAY_SIZE(sitar_dai); i++)
-		kfree(sitar->dai[i].ch_num);
 	mutex_destroy(&sitar->codec_resource_lock);
 	kfree(sitar);
 	return 0;
diff --git a/sound/soc/codecs/wcd9304.h b/sound/soc/codecs/wcd9304.h
index 70b3f0b..13336ef 100644
--- a/sound/soc/codecs/wcd9304.h
+++ b/sound/soc/codecs/wcd9304.h
@@ -191,6 +191,26 @@
 extern int sitar_mclk_enable(struct snd_soc_codec *codec, int mclk_enable,
 							 bool dapm);
 
+/* Number of input and output Slimbus ports
+ */
+enum {
+	SITAR_RX1 = 0,
+	SITAR_RX2,
+	SITAR_RX3,
+	SITAR_RX4,
+	SITAR_RX5,
+	SITAR_RX_MAX,
+};
+
+enum {
+	SITAR_TX1 = 0,
+	SITAR_TX2,
+	SITAR_TX3,
+	SITAR_TX4,
+	SITAR_TX5,
+	SITAR_TX_MAX,
+};
+
 extern void *sitar_mbhc_cal_btn_det_mp(const struct sitar_mbhc_btn_detect_cfg
 				       *btn_det,
 				       const enum sitar_mbhc_btn_det_mem mem);
diff --git a/sound/soc/codecs/wcd9310.c b/sound/soc/codecs/wcd9310.c
index deddbe8..6b3287e 100644
--- a/sound/soc/codecs/wcd9310.c
+++ b/sound/soc/codecs/wcd9310.c
@@ -33,6 +33,9 @@
 #include <linux/pm_runtime.h>
 #include <linux/kernel.h>
 #include <linux/gpio.h>
+#include <linux/irq.h>
+#include <linux/wakelock.h>
+#include <linux/suspend.h>
 #include "wcd9310.h"
 
 static int cfilt_adjust_ms = 10;
@@ -71,25 +74,33 @@
 
 #define TABLA_OCP_ATTEMPT 1
 
-#define AIF1_PB 1
-#define AIF1_CAP 2
-#define AIF2_PB 3
-#define AIF2_CAP 4
-#define AIF3_CAP 5
-#define AIF3_PB  6
-
-#define NUM_CODEC_DAIS 6
-#define TABLA_COMP_DIGITAL_GAIN_OFFSET 3
-
-struct tabla_codec_dai_data {
-	u32 rate;
-	u32 *ch_num;
-	u32 ch_act;
-	u32 ch_tot;
-	u32 ch_mask;
-	wait_queue_head_t dai_wait;
+enum {
+	AIF1_PB = 0,
+	AIF1_CAP,
+	AIF2_PB,
+	AIF2_CAP,
+	AIF3_PB,
+	AIF3_CAP,
+	NUM_CODEC_DAIS,
 };
 
+enum {
+	RX_MIX1_INP_SEL_ZERO = 0,
+	RX_MIX1_INP_SEL_SRC1,
+	RX_MIX1_INP_SEL_SRC2,
+	RX_MIX1_INP_SEL_IIR1,
+	RX_MIX1_INP_SEL_IIR2,
+	RX_MIX1_INP_SEL_RX1,
+	RX_MIX1_INP_SEL_RX2,
+	RX_MIX1_INP_SEL_RX3,
+	RX_MIX1_INP_SEL_RX4,
+	RX_MIX1_INP_SEL_RX5,
+	RX_MIX1_INP_SEL_RX6,
+	RX_MIX1_INP_SEL_RX7,
+};
+
+#define TABLA_COMP_DIGITAL_GAIN_OFFSET 3
+
 #define TABLA_MCLK_RATE_12288KHZ 12288000
 #define TABLA_MCLK_RATE_9600KHZ 9600000
 
@@ -252,6 +263,38 @@
 
 static struct hpf_work tx_hpf_work[NUM_DECIMATORS];
 
+static const struct wcd9xxx_ch tabla_rx_chs[TABLA_RX_MAX] = {
+	WCD9XXX_CH(10, 0),
+	WCD9XXX_CH(11, 1),
+	WCD9XXX_CH(12, 2),
+	WCD9XXX_CH(13, 3),
+	WCD9XXX_CH(14, 4),
+	WCD9XXX_CH(15, 5),
+	WCD9XXX_CH(16, 6)
+};
+
+static const struct wcd9xxx_ch tabla_tx_chs[TABLA_TX_MAX] = {
+	WCD9XXX_CH(0, 0),
+	WCD9XXX_CH(1, 1),
+	WCD9XXX_CH(2, 2),
+	WCD9XXX_CH(3, 3),
+	WCD9XXX_CH(4, 4),
+	WCD9XXX_CH(5, 5),
+	WCD9XXX_CH(6, 6),
+	WCD9XXX_CH(7, 7),
+	WCD9XXX_CH(8, 8),
+	WCD9XXX_CH(9, 9)
+};
+
+static const u32 vport_check_table[NUM_CODEC_DAIS] = {
+	0,					/* AIF1_PB */
+	(1 << AIF2_CAP) | (1 << AIF3_CAP),	/* AIF1_CAP */
+	0,					/* AIF2_PB */
+	(1 << AIF1_CAP) | (1 << AIF3_CAP),	/* AIF2_CAP */
+	0,					/* AIF2_PB */
+	(1 << AIF1_CAP) | (1 << AIF2_CAP),	/* AIF2_CAP */
+};
+
 struct tabla_priv {
 	struct snd_soc_codec *codec;
 	struct tabla_reg_address reg_addr;
@@ -307,7 +350,7 @@
 	const struct firmware *mbhc_fw;
 
 	/* num of slim ports required */
-	struct tabla_codec_dai_data dai[NUM_CODEC_DAIS];
+	struct wcd9xxx_codec_dai_data dai[NUM_CODEC_DAIS];
 
 	/*compander*/
 	int comp_enabled[COMPANDER_MAX];
@@ -340,6 +383,9 @@
 	 */
 	struct work_struct hs_correct_plug_work_nogpio;
 
+	bool gpio_irq_resend;
+	struct wake_lock irq_resend_wlock;
+
 #ifdef CONFIG_DEBUG_FS
 	struct dentry *debugfs_poke;
 	struct dentry *debugfs_mbhc;
@@ -1676,6 +1722,221 @@
 static const struct snd_kcontrol_new lineout4_ground_switch =
 	SOC_DAPM_SINGLE("Switch", TABLA_A_RX_LINE_4_DAC_CTL, 6, 1, 0);
 
+/* virtual port entries */
+static int slim_tx_mixer_get(struct snd_kcontrol *kcontrol,
+			     struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+	struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+
+	ucontrol->value.integer.value[0] = widget->value;
+	return 0;
+}
+
+static int slim_tx_mixer_put(struct snd_kcontrol *kcontrol,
+			     struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+	struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+	struct snd_soc_codec *codec = widget->codec;
+	struct tabla_priv *tabla_p = snd_soc_codec_get_drvdata(codec);
+	struct wcd9xxx *core = dev_get_drvdata(codec->dev->parent);
+	struct soc_multi_mixer_control *mixer =
+		((struct soc_multi_mixer_control *)kcontrol->private_value);
+	u32 dai_id = widget->shift;
+	u32 port_id = mixer->shift;
+	u32 enable = ucontrol->value.integer.value[0];
+
+	pr_debug("%s: wname %s cname %s value %u shift %d item %ld\n", __func__,
+		widget->name, ucontrol->id.name, widget->value, widget->shift,
+		ucontrol->value.integer.value[0]);
+
+	mutex_lock(&codec->mutex);
+	if (tabla_p->intf_type != WCD9XXX_INTERFACE_TYPE_SLIMBUS) {
+		if (dai_id != AIF1_CAP) {
+			dev_err(codec->dev, "%s: invalid AIF for I2C mode\n",
+				__func__);
+			mutex_unlock(&codec->mutex);
+			return -EINVAL;
+		}
+	}
+	switch (dai_id) {
+	case AIF1_CAP:
+	case AIF2_CAP:
+	case AIF3_CAP:
+		/* only add to the list if value not set
+		 */
+		if (enable && !(widget->value & 1 << port_id)) {
+			if (wcd9xxx_tx_vport_validation(
+						vport_check_table[dai_id],
+						port_id,
+						tabla_p->dai)) {
+				pr_info("%s: TX%u is used by other virtual port\n",
+					__func__, port_id + 1);
+				mutex_unlock(&codec->mutex);
+				return -EINVAL;
+			}
+			widget->value |= 1 << port_id;
+			list_add_tail(&core->tx_chs[port_id].list,
+				      &tabla_p->dai[dai_id].wcd9xxx_ch_list
+				      );
+		} else if (!enable && (widget->value & 1 << port_id)) {
+			widget->value &= ~(1 << port_id);
+			list_del_init(&core->tx_chs[port_id].list);
+		} else {
+			if (enable)
+				pr_info("%s: TX%u port is used by this virtual port\n",
+					__func__, port_id + 1);
+			else
+				pr_info("%s: TX%u port is not used by this virtual port\n",
+					__func__, port_id + 1);
+			/* avoid update power function */
+			mutex_unlock(&codec->mutex);
+			return 0;
+		}
+		break;
+	default:
+		pr_err("Unknown AIF %d\n", dai_id);
+		mutex_unlock(&codec->mutex);
+		return -EINVAL;
+	}
+	pr_debug("%s: name %s sname %s updated value %u shift %d\n", __func__,
+		widget->name, widget->sname, widget->value, widget->shift);
+
+	snd_soc_dapm_mixer_update_power(widget, kcontrol, enable);
+
+	mutex_unlock(&codec->mutex);
+	return 0;
+}
+
+static int slim_rx_mux_get(struct snd_kcontrol *kcontrol,
+			   struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+	struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+
+	ucontrol->value.enumerated.item[0] = widget->value;
+	return 0;
+}
+
+static const char *const slim_rx_mux_text[] = {
+	"ZERO", "AIF1_PB", "AIF2_PB", "AIF3_PB"
+};
+
+static int slim_rx_mux_put(struct snd_kcontrol *kcontrol,
+			   struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+	struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+	struct snd_soc_codec *codec = widget->codec;
+	struct tabla_priv *tabla_p = snd_soc_codec_get_drvdata(codec);
+	struct wcd9xxx *core = dev_get_drvdata(codec->dev->parent);
+	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+	u32 port_id = widget->shift;
+
+	pr_debug("%s: wname %s cname %s value %u shift %d item %u\n", __func__,
+		widget->name, ucontrol->id.name, widget->value, widget->shift,
+		ucontrol->value.enumerated.item[0]);
+
+	widget->value = ucontrol->value.enumerated.item[0];
+
+	mutex_lock(&codec->mutex);
+
+	if (tabla_p->intf_type != WCD9XXX_INTERFACE_TYPE_SLIMBUS) {
+		if (widget->value > 1) {
+			dev_err(codec->dev, "%s: invalid AIF for I2C mode\n",
+				__func__);
+			goto err;
+		}
+	}
+	/* value need to match the Virtual port and AIF number
+	 */
+	switch (widget->value) {
+	case 0:
+		list_del_init(&core->rx_chs[port_id].list);
+	break;
+	case 1:
+		if (wcd9xxx_rx_vport_validation(port_id + core->num_tx_port,
+			&tabla_p->dai[AIF1_PB].wcd9xxx_ch_list))
+			goto pr_err;
+		list_add_tail(&core->rx_chs[port_id].list,
+			      &tabla_p->dai[AIF1_PB].wcd9xxx_ch_list);
+	break;
+	case 2:
+		if (wcd9xxx_rx_vport_validation(port_id + core->num_tx_port,
+			&tabla_p->dai[AIF1_PB].wcd9xxx_ch_list))
+			goto pr_err;
+		list_add_tail(&core->rx_chs[port_id].list,
+			      &tabla_p->dai[AIF2_PB].wcd9xxx_ch_list);
+	break;
+	case 3:
+		if (wcd9xxx_rx_vport_validation(port_id + core->num_tx_port,
+			&tabla_p->dai[AIF1_PB].wcd9xxx_ch_list))
+			goto pr_err;
+		list_add_tail(&core->rx_chs[port_id].list,
+			      &tabla_p->dai[AIF3_PB].wcd9xxx_ch_list);
+	break;
+	default:
+		pr_err("Unknown AIF %d\n", widget->value);
+		goto err;
+	}
+
+	snd_soc_dapm_mux_update_power(widget, kcontrol, 1, widget->value, e);
+
+	mutex_unlock(&codec->mutex);
+	return 0;
+
+pr_err:
+	pr_err("%s: RX%u is used by current requesting AIF_PB itself\n",
+		__func__, port_id + 1);
+err:
+	mutex_unlock(&codec->mutex);
+	return -EINVAL;
+}
+
+static const struct soc_enum slim_rx_mux_enum =
+	SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(slim_rx_mux_text), slim_rx_mux_text);
+
+static const struct snd_kcontrol_new slim_rx_mux[TABLA_RX_MAX] = {
+	SOC_DAPM_ENUM_EXT("SLIM RX1 Mux", slim_rx_mux_enum,
+			  slim_rx_mux_get, slim_rx_mux_put),
+	SOC_DAPM_ENUM_EXT("SLIM RX2 Mux", slim_rx_mux_enum,
+			  slim_rx_mux_get, slim_rx_mux_put),
+	SOC_DAPM_ENUM_EXT("SLIM RX3 Mux", slim_rx_mux_enum,
+			  slim_rx_mux_get, slim_rx_mux_put),
+	SOC_DAPM_ENUM_EXT("SLIM RX4 Mux", slim_rx_mux_enum,
+			  slim_rx_mux_get, slim_rx_mux_put),
+	SOC_DAPM_ENUM_EXT("SLIM RX5 Mux", slim_rx_mux_enum,
+			  slim_rx_mux_get, slim_rx_mux_put),
+	SOC_DAPM_ENUM_EXT("SLIM RX6 Mux", slim_rx_mux_enum,
+			  slim_rx_mux_get, slim_rx_mux_put),
+	SOC_DAPM_ENUM_EXT("SLIM RX7 Mux", slim_rx_mux_enum,
+			  slim_rx_mux_get, slim_rx_mux_put),
+};
+
+static const struct snd_kcontrol_new aif_cap_mixer[] = {
+	SOC_SINGLE_EXT("SLIM TX1", SND_SOC_NOPM, TABLA_TX1, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX2", SND_SOC_NOPM, TABLA_TX2, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX3", SND_SOC_NOPM, TABLA_TX3, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX4", SND_SOC_NOPM, TABLA_TX4, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX5", SND_SOC_NOPM, TABLA_TX5, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX6", SND_SOC_NOPM, TABLA_TX6, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX7", SND_SOC_NOPM, TABLA_TX7, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX8", SND_SOC_NOPM, TABLA_TX8, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX9", SND_SOC_NOPM, TABLA_TX9, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX10", SND_SOC_NOPM, TABLA_TX10, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+};
+
 static void tabla_codec_enable_adc_block(struct snd_soc_codec *codec,
 					 int enable)
 {
@@ -2905,7 +3166,7 @@
 		/* reset retry counter as PA is turned off signifying
 		 * start of new OCP detection session
 		 */
-		if (TABLA_IRQ_HPH_PA_OCPL_FAULT)
+		if (WCD9XXX_IRQ_HPH_PA_OCPL_FAULT)
 			tabla->hphlocp_cnt = 0;
 		else
 			tabla->hphrocp_cnt = 0;
@@ -2917,14 +3178,16 @@
 {
 	struct tabla_priv *tabla = container_of(work, struct tabla_priv,
 		hphlocp_work);
-	hphocp_off_report(tabla, SND_JACK_OC_HPHL, TABLA_IRQ_HPH_PA_OCPL_FAULT);
+	hphocp_off_report(tabla, SND_JACK_OC_HPHL,
+			  WCD9XXX_IRQ_HPH_PA_OCPL_FAULT);
 }
 
 static void hphrocp_off_report(struct work_struct *work)
 {
 	struct tabla_priv *tabla = container_of(work, struct tabla_priv,
 		hphrocp_work);
-	hphocp_off_report(tabla, SND_JACK_OC_HPHR, TABLA_IRQ_HPH_PA_OCPR_FAULT);
+	hphocp_off_report(tabla, SND_JACK_OC_HPHR,
+			  WCD9XXX_IRQ_HPH_PA_OCPR_FAULT);
 }
 
 static int tabla_hph_pa_event(struct snd_soc_dapm_widget *w,
@@ -3114,13 +3377,47 @@
 static const struct snd_soc_dapm_route audio_map[] = {
 	/* SLIMBUS Connections */
 
-	{"SLIM TX1", NULL, "SLIM TX1 MUX"},
-	{"SLIM TX1 MUX", "DEC1", "DEC1 MUX"},
+	{"AIF1 CAP", NULL, "AIF1_CAP Mixer"},
+	{"AIF2 CAP", NULL, "AIF2_CAP Mixer"},
+	{"AIF3 CAP", NULL, "AIF3_CAP Mixer"},
 
-	{"SLIM TX2", NULL, "SLIM TX2 MUX"},
+	/* SLIM_MIXER("AIF1_CAP Mixer"),*/
+	{"AIF1_CAP Mixer", "SLIM TX1", "SLIM TX1 MUX"},
+	{"AIF1_CAP Mixer", "SLIM TX2", "SLIM TX2 MUX"},
+	{"AIF1_CAP Mixer", "SLIM TX3", "SLIM TX3 MUX"},
+	{"AIF1_CAP Mixer", "SLIM TX4", "SLIM TX4 MUX"},
+	{"AIF1_CAP Mixer", "SLIM TX5", "SLIM TX5 MUX"},
+	{"AIF1_CAP Mixer", "SLIM TX6", "SLIM TX6 MUX"},
+	{"AIF1_CAP Mixer", "SLIM TX7", "SLIM TX7 MUX"},
+	{"AIF1_CAP Mixer", "SLIM TX8", "SLIM TX8 MUX"},
+	{"AIF1_CAP Mixer", "SLIM TX9", "SLIM TX9 MUX"},
+	{"AIF1_CAP Mixer", "SLIM TX10", "SLIM TX10 MUX"},
+	/* SLIM_MIXER("AIF2_CAP Mixer"),*/
+	{"AIF2_CAP Mixer", "SLIM TX1", "SLIM TX1 MUX"},
+	{"AIF2_CAP Mixer", "SLIM TX2", "SLIM TX2 MUX"},
+	{"AIF2_CAP Mixer", "SLIM TX3", "SLIM TX3 MUX"},
+	{"AIF2_CAP Mixer", "SLIM TX4", "SLIM TX4 MUX"},
+	{"AIF2_CAP Mixer", "SLIM TX5", "SLIM TX5 MUX"},
+	{"AIF2_CAP Mixer", "SLIM TX6", "SLIM TX6 MUX"},
+	{"AIF2_CAP Mixer", "SLIM TX7", "SLIM TX7 MUX"},
+	{"AIF2_CAP Mixer", "SLIM TX8", "SLIM TX8 MUX"},
+	{"AIF2_CAP Mixer", "SLIM TX9", "SLIM TX9 MUX"},
+	{"AIF2_CAP Mixer", "SLIM TX10", "SLIM TX10 MUX"},
+	/* SLIM_MIXER("AIF3_CAP Mixer"),*/
+	{"AIF3_CAP Mixer", "SLIM TX1", "SLIM TX1 MUX"},
+	{"AIF3_CAP Mixer", "SLIM TX2", "SLIM TX2 MUX"},
+	{"AIF3_CAP Mixer", "SLIM TX3", "SLIM TX3 MUX"},
+	{"AIF3_CAP Mixer", "SLIM TX4", "SLIM TX4 MUX"},
+	{"AIF3_CAP Mixer", "SLIM TX5", "SLIM TX5 MUX"},
+	{"AIF3_CAP Mixer", "SLIM TX6", "SLIM TX6 MUX"},
+	{"AIF3_CAP Mixer", "SLIM TX7", "SLIM TX7 MUX"},
+	{"AIF3_CAP Mixer", "SLIM TX8", "SLIM TX8 MUX"},
+	{"AIF3_CAP Mixer", "SLIM TX9", "SLIM TX9 MUX"},
+	{"AIF3_CAP Mixer", "SLIM TX10", "SLIM TX10 MUX"},
+
+	{"SLIM TX1 MUX", "DEC1", "DEC1 MUX"},
 	{"SLIM TX2 MUX", "DEC2", "DEC2 MUX"},
 
-	{"SLIM TX3", NULL, "SLIM TX3 MUX"},
 	{"SLIM TX3 MUX", "DEC3", "DEC3 MUX"},
 	{"SLIM TX3 MUX", "RMIX1", "RX1 MIX1"},
 	{"SLIM TX3 MUX", "RMIX2", "RX2 MIX1"},
@@ -3130,10 +3427,8 @@
 	{"SLIM TX3 MUX", "RMIX6", "RX6 MIX1"},
 	{"SLIM TX3 MUX", "RMIX7", "RX7 MIX1"},
 
-	{"SLIM TX4", NULL, "SLIM TX4 MUX"},
 	{"SLIM TX4 MUX", "DEC4", "DEC4 MUX"},
 
-	{"SLIM TX5", NULL, "SLIM TX5 MUX"},
 	{"SLIM TX5 MUX", "DEC5", "DEC5 MUX"},
 	{"SLIM TX5 MUX", "RMIX1", "RX1 MIX1"},
 	{"SLIM TX5 MUX", "RMIX2", "RX2 MIX1"},
@@ -3143,10 +3438,8 @@
 	{"SLIM TX5 MUX", "RMIX6", "RX6 MIX1"},
 	{"SLIM TX5 MUX", "RMIX7", "RX7 MIX1"},
 
-	{"SLIM TX6", NULL, "SLIM TX6 MUX"},
 	{"SLIM TX6 MUX", "DEC6", "DEC6 MUX"},
 
-	{"SLIM TX7", NULL, "SLIM TX7 MUX"},
 	{"SLIM TX7 MUX", "DEC1", "DEC1 MUX"},
 	{"SLIM TX7 MUX", "DEC2", "DEC2 MUX"},
 	{"SLIM TX7 MUX", "DEC3", "DEC3 MUX"},
@@ -3165,7 +3458,6 @@
 	{"SLIM TX7 MUX", "RMIX6", "RX6 MIX1"},
 	{"SLIM TX7 MUX", "RMIX7", "RX7 MIX1"},
 
-	{"SLIM TX8", NULL, "SLIM TX8 MUX"},
 	{"SLIM TX8 MUX", "DEC1", "DEC1 MUX"},
 	{"SLIM TX8 MUX", "DEC2", "DEC2 MUX"},
 	{"SLIM TX8 MUX", "DEC3", "DEC3 MUX"},
@@ -3177,7 +3469,6 @@
 	{"SLIM TX8 MUX", "DEC9", "DEC9 MUX"},
 	{"SLIM TX8 MUX", "DEC10", "DEC10 MUX"},
 
-	{"SLIM TX9", NULL, "SLIM TX9 MUX"},
 	{"SLIM TX9 MUX", "DEC1", "DEC1 MUX"},
 	{"SLIM TX9 MUX", "DEC2", "DEC2 MUX"},
 	{"SLIM TX9 MUX", "DEC3", "DEC3 MUX"},
@@ -3189,7 +3480,6 @@
 	{"SLIM TX9 MUX", "DEC9", "DEC9 MUX"},
 	{"SLIM TX9 MUX", "DEC10", "DEC10 MUX"},
 
-	{"SLIM TX10", NULL, "SLIM TX10 MUX"},
 	{"SLIM TX10 MUX", "DEC1", "DEC1 MUX"},
 	{"SLIM TX10 MUX", "DEC2", "DEC2 MUX"},
 	{"SLIM TX10 MUX", "DEC3", "DEC3 MUX"},
@@ -3304,6 +3594,40 @@
 	{"RX3 MIX2", NULL, "RX3 MIX2 INP1"},
 	{"RX3 MIX2", NULL, "RX3 MIX2 INP2"},
 
+	/* SLIM_MUX("AIF1_PB", "AIF1 PB"),*/
+	{"SLIM RX1 MUX", "AIF1_PB", "AIF1 PB"},
+	{"SLIM RX2 MUX", "AIF1_PB", "AIF1 PB"},
+	{"SLIM RX3 MUX", "AIF1_PB", "AIF1 PB"},
+	{"SLIM RX4 MUX", "AIF1_PB", "AIF1 PB"},
+	{"SLIM RX5 MUX", "AIF1_PB", "AIF1 PB"},
+	{"SLIM RX6 MUX", "AIF1_PB", "AIF1 PB"},
+	{"SLIM RX7 MUX", "AIF1_PB", "AIF1 PB"},
+	/* SLIM_MUX("AIF2_PB", "AIF2 PB"),*/
+	{"SLIM RX1 MUX", "AIF2_PB", "AIF2 PB"},
+	{"SLIM RX2 MUX", "AIF2_PB", "AIF2 PB"},
+	{"SLIM RX3 MUX", "AIF2_PB", "AIF2 PB"},
+	{"SLIM RX4 MUX", "AIF2_PB", "AIF2 PB"},
+	{"SLIM RX5 MUX", "AIF2_PB", "AIF2 PB"},
+	{"SLIM RX6 MUX", "AIF2_PB", "AIF2 PB"},
+	{"SLIM RX7 MUX", "AIF2_PB", "AIF2 PB"},
+	/* SLIM_MUX("AIF3_PB", "AIF3 PB"),*/
+	{"SLIM RX1 MUX", "AIF3_PB", "AIF3 PB"},
+	{"SLIM RX2 MUX", "AIF3_PB", "AIF3 PB"},
+	{"SLIM RX3 MUX", "AIF3_PB", "AIF3 PB"},
+	{"SLIM RX4 MUX", "AIF3_PB", "AIF3 PB"},
+	{"SLIM RX5 MUX", "AIF3_PB", "AIF3 PB"},
+	{"SLIM RX6 MUX", "AIF3_PB", "AIF3 PB"},
+	{"SLIM RX7 MUX", "AIF3_PB", "AIF3 PB"},
+
+	{"SLIM RX1", NULL, "SLIM RX1 MUX"},
+	{"SLIM RX2", NULL, "SLIM RX2 MUX"},
+	{"SLIM RX3", NULL, "SLIM RX3 MUX"},
+	{"SLIM RX4", NULL, "SLIM RX4 MUX"},
+	{"SLIM RX5", NULL, "SLIM RX5 MUX"},
+	{"SLIM RX6", NULL, "SLIM RX6 MUX"},
+	{"SLIM RX7", NULL, "SLIM RX7 MUX"},
+
+	/* Mixer control for output path */
 	{"RX1 MIX1 INP1", "RX1", "SLIM RX1"},
 	{"RX1 MIX1 INP1", "RX2", "SLIM RX2"},
 	{"RX1 MIX1 INP1", "RX3", "SLIM RX3"},
@@ -3653,6 +3977,10 @@
 	unsigned int value)
 {
 	int ret;
+
+	if (reg == SND_SOC_NOPM)
+		return 0;
+
 	BUG_ON(reg > TABLA_MAX_REGISTER);
 
 	if (!tabla_volatile(codec, reg)) {
@@ -3670,6 +3998,9 @@
 	unsigned int val;
 	int ret;
 
+	if (reg == SND_SOC_NOPM)
+		return 0;
+
 	BUG_ON(reg > TABLA_MAX_REGISTER);
 
 	if (!tabla_volatile(codec, reg) && tabla_readable(codec, reg) &&
@@ -3793,10 +4124,10 @@
 		return;
 
 	if (dai->id <= NUM_CODEC_DAIS) {
-		if (tabla->dai[dai->id-1].ch_mask) {
+		if (tabla->dai[dai->id].ch_mask) {
 			active = 1;
 			pr_debug("%s(): Codec DAI: chmask[%d] = 0x%x\n",
-			__func__, dai->id-1, tabla->dai[dai->id-1].ch_mask);
+				__func__, dai->id, tabla->dai[dai->id].ch_mask);
 		}
 	}
 
@@ -3917,39 +4248,20 @@
 
 {
 	struct tabla_priv *tabla = snd_soc_codec_get_drvdata(dai->codec);
-	u32 i = 0;
+	struct wcd9xxx *core = dev_get_drvdata(dai->codec->dev->parent);
+
 	if (!tx_slot && !rx_slot) {
 		pr_err("%s: Invalid\n", __func__);
 		return -EINVAL;
 	}
-	pr_debug("%s(): dai_name = %s DAI-ID %x tx_ch %d rx_ch %d\n",
-			__func__, dai->name, dai->id, tx_num, rx_num);
+	pr_debug("%s(): dai_name = %s DAI-ID %x tx_ch %d rx_ch %d\n"
+		 "tabla->intf_type %d\n",
+		 __func__, dai->name, dai->id, tx_num, rx_num,
+		 tabla->intf_type);
 
-	if (dai->id == AIF1_PB || dai->id == AIF2_PB || dai->id == AIF3_PB) {
-		for (i = 0; i < rx_num; i++) {
-			tabla->dai[dai->id - 1].ch_num[i]  = rx_slot[i];
-			tabla->dai[dai->id - 1].ch_act = 0;
-			tabla->dai[dai->id - 1].ch_tot = rx_num;
-		}
-	} else if (dai->id == AIF1_CAP || dai->id == AIF2_CAP ||
-		   dai->id == AIF3_CAP) {
-		tabla->dai[dai->id - 1].ch_tot = tx_num;
-		/* All channels are already active.
-		 * do not reset ch_act flag
-		 */
-		if ((tabla->dai[dai->id - 1].ch_tot != 0)
-			&& (tabla->dai[dai->id - 1].ch_act ==
-			tabla->dai[dai->id - 1].ch_tot)) {
-			pr_info("%s: ch_act = %d, ch_tot = %d\n", __func__,
-				tabla->dai[dai->id - 1].ch_act,
-				tabla->dai[dai->id - 1].ch_tot);
-			return 0;
-		}
-
-		tabla->dai[dai->id - 1].ch_act = 0;
-		for (i = 0; i < tx_num; i++)
-			tabla->dai[dai->id - 1].ch_num[i]  = tx_slot[i];
-	}
+	if (tabla->intf_type == WCD9XXX_INTERFACE_TYPE_SLIMBUS)
+		wcd9xxx_init_slimslave(core, core->slim->laddr,
+				       tx_num, tx_slot, rx_num, rx_slot);
 	return 0;
 }
 
@@ -3958,189 +4270,97 @@
 				unsigned int *rx_num, unsigned int *rx_slot)
 
 {
-	struct wcd9xxx *tabla = dev_get_drvdata(dai->codec->control_data);
+	struct tabla_priv *tabla_p = snd_soc_codec_get_drvdata(dai->codec);
+	u32 i = 0;
+	struct wcd9xxx_ch *ch;
 
-	u32 cnt = 0;
-	u32 tx_ch[SLIM_MAX_TX_PORTS];
-	u32 rx_ch[SLIM_MAX_RX_PORTS];
+	switch (dai->id) {
+	case AIF1_PB:
+	case AIF2_PB:
+	case AIF3_PB:
+		if (!rx_slot || !rx_num) {
+			pr_err("%s: Invalid rx_slot %d or rx_num %d\n",
+				 __func__, (u32) rx_slot, (u32) rx_num);
+			return -EINVAL;
+		}
+		list_for_each_entry(ch, &tabla_p->dai[dai->id].wcd9xxx_ch_list,
+				    list) {
+			rx_slot[i++] = ch->ch_num;
+		}
+		*rx_num = i;
+		break;
+	case AIF1_CAP:
+	case AIF2_CAP:
+	case AIF3_CAP:
+		if (!tx_slot || !tx_num) {
+			pr_err("%s: Invalid tx_slot %d or tx_num %d\n",
+				 __func__, (u32) tx_slot, (u32) tx_num);
+			return -EINVAL;
+		}
+		list_for_each_entry(ch, &tabla_p->dai[dai->id].wcd9xxx_ch_list,
+				    list) {
+			tx_slot[i++] = ch->ch_num;
+		}
+		*tx_num = i;
+		break;
 
-	if (!rx_slot && !tx_slot) {
-		pr_err("%s: Invalid\n", __func__);
-		return -EINVAL;
+	default:
+		pr_err("%s: Invalid DAI ID %x\n", __func__, dai->id);
+		break;
 	}
-
-	/* for virtual port, codec driver needs to do
-	 * housekeeping, for now should be ok
-	 */
-	wcd9xxx_get_channel(tabla, rx_ch, tx_ch);
-	if (dai->id == AIF1_PB) {
-		*rx_num = tabla_dai[dai->id - 1].playback.channels_max;
-		while (cnt < *rx_num) {
-			rx_slot[cnt] = rx_ch[cnt];
-			cnt++;
-		}
-	} else if (dai->id == AIF1_CAP) {
-		*tx_num = tabla_dai[dai->id - 1].capture.channels_max;
-		while (cnt < *tx_num) {
-			tx_slot[cnt] = tx_ch[6 + cnt];
-			cnt++;
-		}
-	} else if (dai->id == AIF2_PB) {
-		*rx_num = tabla_dai[dai->id - 1].playback.channels_max;
-		while (cnt < *rx_num) {
-			rx_slot[cnt] = rx_ch[5 + cnt];
-			cnt++;
-		}
-	} else if (dai->id == AIF2_CAP) {
-		*tx_num = tabla_dai[dai->id - 1].capture.channels_max;
-		tx_slot[0] = tx_ch[cnt];
-		tx_slot[1] = tx_ch[1 + cnt];
-		tx_slot[2] = tx_ch[5 + cnt];
-		tx_slot[3] = tx_ch[3 + cnt];
-
-	} else if (dai->id == AIF3_PB) {
-		*rx_num = tabla_dai[dai->id - 1].playback.channels_max;
-		rx_slot[0] = rx_ch[3];
-		rx_slot[1] = rx_ch[4];
-
-	} else if (dai->id == AIF3_CAP) {
-		*tx_num = tabla_dai[dai->id - 1].capture.channels_max;
-		tx_slot[cnt] = tx_ch[2 + cnt];
-		tx_slot[cnt + 1] = tx_ch[4 + cnt];
-	}
-	pr_debug("%s(): dai_name = %s DAI-ID %x tx_ch %d rx_ch %d\n",
-			__func__, dai->name, dai->id, *tx_num, *rx_num);
-
-
 	return 0;
 }
 
 
-static struct snd_soc_dapm_widget tabla_dapm_aif_in_widgets[] = {
-
-	SND_SOC_DAPM_AIF_IN_E("SLIM RX1", "AIF1 Playback", 0, SND_SOC_NOPM, 1,
-				0, tabla_codec_enable_slimrx,
-				SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
-
-	SND_SOC_DAPM_AIF_IN_E("SLIM RX2", "AIF1 Playback", 0, SND_SOC_NOPM, 2,
-				0, tabla_codec_enable_slimrx,
-				SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
-
-	SND_SOC_DAPM_AIF_IN_E("SLIM RX3", "AIF1 Playback", 0, SND_SOC_NOPM, 3,
-				0, tabla_codec_enable_slimrx,
-				SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
-
-	SND_SOC_DAPM_AIF_IN_E("SLIM RX4", "AIF3 Playback", 0, SND_SOC_NOPM, 4,
-				0, tabla_codec_enable_slimrx,
-				SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
-
-	SND_SOC_DAPM_AIF_IN_E("SLIM RX5", "AIF3 Playback", 0, SND_SOC_NOPM, 5,
-				0, tabla_codec_enable_slimrx,
-				SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
-
-	SND_SOC_DAPM_AIF_IN_E("SLIM RX6", "AIF2 Playback", 0, SND_SOC_NOPM, 6,
-				0, tabla_codec_enable_slimrx,
-				SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
-
-	SND_SOC_DAPM_AIF_IN_E("SLIM RX7", "AIF2 Playback", 0, SND_SOC_NOPM, 7,
-				0, tabla_codec_enable_slimrx,
-				SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
-};
-
-static struct snd_soc_dapm_widget tabla_dapm_aif_out_widgets[] = {
-
-	SND_SOC_DAPM_AIF_OUT_E("SLIM TX1", "AIF2 Capture", 0, SND_SOC_NOPM, 1,
-				0, tabla_codec_enable_slimtx,
-				SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
-
-	SND_SOC_DAPM_AIF_OUT_E("SLIM TX2", "AIF2 Capture", 0, SND_SOC_NOPM, 2,
-				0, tabla_codec_enable_slimtx,
-				SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
-
-	SND_SOC_DAPM_AIF_OUT_E("SLIM TX3", "AIF3 Capture", 0, SND_SOC_NOPM, 3,
-				0, tabla_codec_enable_slimtx,
-				SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
-
-	SND_SOC_DAPM_AIF_OUT_E("SLIM TX4", "AIF2 Capture", 0, SND_SOC_NOPM, 4,
-				0, tabla_codec_enable_slimtx,
-				SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
-
-	SND_SOC_DAPM_AIF_OUT_E("SLIM TX5", "AIF3 Capture", 0, SND_SOC_NOPM, 5,
-				0, tabla_codec_enable_slimtx,
-				SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
-
-	SND_SOC_DAPM_AIF_OUT_E("SLIM TX6", "AIF2 Capture", 0, SND_SOC_NOPM, 6,
-				0, tabla_codec_enable_slimtx,
-				SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
-
-	SND_SOC_DAPM_AIF_OUT_E("SLIM TX7", "AIF1 Capture", 0, SND_SOC_NOPM, 7,
-				0, tabla_codec_enable_slimtx,
-				SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
-
-	SND_SOC_DAPM_AIF_OUT_E("SLIM TX8", "AIF1 Capture", 0, SND_SOC_NOPM, 8,
-				0, tabla_codec_enable_slimtx,
-				SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
-
-	SND_SOC_DAPM_AIF_OUT_E("SLIM TX9", "AIF1 Capture", 0, SND_SOC_NOPM, 9,
-				0, tabla_codec_enable_slimtx,
-				SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
-
-	SND_SOC_DAPM_AIF_OUT_E("SLIM TX10", "AIF1 Capture", 0, SND_SOC_NOPM, 10,
-				0, tabla_codec_enable_slimtx,
-				SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
-};
-
 static int tabla_set_interpolator_rate(struct snd_soc_dai *dai,
-	u8 rx_fs_rate_reg_val, u32 compander_fs, u32 sample_rate)
+				       u8 rx_fs_rate_reg_val,
+				       u32 compander_fs,
+				       u32 sample_rate)
 {
-	u32 i, j;
+	u32 j;
 	u8 rx_mix1_inp;
 	u16 rx_mix_1_reg_1, rx_mix_1_reg_2;
 	u16 rx_fs_reg;
 	u8 rx_mix_1_reg_1_val, rx_mix_1_reg_2_val;
 	struct snd_soc_codec *codec = dai->codec;
+	struct wcd9xxx_ch *ch;
 	struct tabla_priv *tabla = snd_soc_codec_get_drvdata(codec);
-	struct snd_soc_dapm_widget *w = tabla_dapm_aif_in_widgets;
 
-	for (i = 0; i < ARRAY_SIZE(tabla_dapm_aif_in_widgets); i++) {
+	list_for_each_entry(ch, &tabla->dai[dai->id].wcd9xxx_ch_list, list) {
 
-		if (strncmp(dai->driver->playback.stream_name, w[i].sname, 13))
-			continue;
+		rx_mix1_inp = ch->port - RX_MIX1_INP_SEL_RX1;
 
-		rx_mix1_inp = w[i].shift + 4;
-
-		if ((rx_mix1_inp < 0x5) || (rx_mix1_inp > 0xB)) {
-
-			pr_err("%s: Invalid SLIM RX%u port.  widget = %s\n",
-				__func__,  rx_mix1_inp  - 4 , w[i].name);
+		if ((rx_mix1_inp < RX_MIX1_INP_SEL_RX1) ||
+			(rx_mix1_inp > RX_MIX1_INP_SEL_RX7)) {
+			pr_err("%s: Invalid TABLA_RX%u port. Dai ID is %d\n",
+				__func__,  rx_mix1_inp - 5 , dai->id);
 			return -EINVAL;
 		}
 
 		rx_mix_1_reg_1 = TABLA_A_CDC_CONN_RX1_B1_CTL;
 
 		for (j = 0; j < NUM_INTERPOLATORS; j++) {
-
 			rx_mix_1_reg_2 = rx_mix_1_reg_1 + 1;
 
 			rx_mix_1_reg_1_val = snd_soc_read(codec,
-					rx_mix_1_reg_1);
+							  rx_mix_1_reg_1);
 			rx_mix_1_reg_2_val = snd_soc_read(codec,
-					rx_mix_1_reg_2);
+							  rx_mix_1_reg_2);
 
 			if (((rx_mix_1_reg_1_val & 0x0F) == rx_mix1_inp) ||
-			   (((rx_mix_1_reg_1_val >> 4) & 0x0F) == rx_mix1_inp)
-			   || ((rx_mix_1_reg_2_val & 0x0F) == rx_mix1_inp)) {
+			(((rx_mix_1_reg_1_val >> 4) & 0x0F) == rx_mix1_inp) ||
+			((rx_mix_1_reg_2_val & 0x0F) == rx_mix1_inp)) {
 
 				rx_fs_reg = TABLA_A_CDC_RX1_B5_CTL + 8 * j;
 
-				pr_debug("%s: %s connected to RX%u\n", __func__,
-					w[i].name, j + 1);
+				pr_debug("%s: AIF_PB DAI(%d) connected to RX%u\n",
+					__func__, dai->id, j + 1);
 
 				pr_debug("%s: set RX%u sample rate to %u\n",
 					__func__, j + 1, sample_rate);
 
 				snd_soc_update_bits(codec, rx_fs_reg,
-						0xE0, rx_fs_rate_reg_val);
+						    0xE0, rx_fs_rate_reg_val);
 
 				if (comp_rx_path[j] < COMPANDER_MAX)
 					tabla->comp_fs[comp_rx_path[j]]
@@ -4156,26 +4376,26 @@
 }
 
 static int tabla_set_decimator_rate(struct snd_soc_dai *dai,
-	u8 tx_fs_rate_reg_val, u32 sample_rate)
+				    u8 tx_fs_rate_reg_val,
+				    u32 sample_rate)
 {
 	struct snd_soc_codec *codec = dai->codec;
-	struct snd_soc_dapm_widget *w = tabla_dapm_aif_out_widgets;
-
-	u32 i, tx_port;
+	struct wcd9xxx_ch *ch;
+	struct tabla_priv *tabla = snd_soc_codec_get_drvdata(codec);
+	u32 tx_port;
 	u16 tx_port_reg, tx_fs_reg;
 	u8 tx_port_reg_val;
 	s8 decimator;
 
-	for (i = 0; i < ARRAY_SIZE(tabla_dapm_aif_out_widgets); i++) {
+	list_for_each_entry(ch, &tabla->dai[dai->id].wcd9xxx_ch_list, list) {
 
-		if (strncmp(dai->driver->capture.stream_name, w[i].sname, 12))
-			continue;
-
-		tx_port = w[i].shift;
+		tx_port = ch->port + 1;
+		pr_debug("%s: dai->id = %d, tx_port = %d",
+			__func__, dai->id, tx_port);
 
 		if ((tx_port < 1) || (tx_port > NUM_DECIMATORS)) {
-			pr_err("%s: Invalid SLIM TX%u port.  widget = %s\n",
-				__func__, tx_port, w[i].name);
+			pr_err("%s: Invalid SLIM TX%u port. DAI ID is %d\n",
+				__func__, tx_port, dai->id);
 			return -EINVAL;
 		}
 
@@ -4204,38 +4424,38 @@
 		if (decimator) { /* SLIM_TX port has a DEC as input */
 
 			tx_fs_reg = TABLA_A_CDC_TX1_CLK_FS_CTL +
-				8 * (decimator - 1);
+				    8 * (decimator - 1);
 
 			pr_debug("%s: set DEC%u (-> SLIM_TX%u) rate to %u\n",
 				__func__, decimator, tx_port, sample_rate);
 
 			snd_soc_update_bits(codec, tx_fs_reg, 0x07,
-					tx_fs_rate_reg_val);
+					    tx_fs_rate_reg_val);
 
 		} else {
 			if ((tx_port_reg_val >= 0x1) &&
-					(tx_port_reg_val <= 0x7)) {
+			    (tx_port_reg_val <= 0x7)) {
 
 				pr_debug("%s: RMIX%u going to SLIM TX%u\n",
 					__func__, tx_port_reg_val, tx_port);
 
 			} else if  ((tx_port_reg_val >= 0x8) &&
-					(tx_port_reg_val <= 0x11)) {
+				    (tx_port_reg_val <= 0x11)) {
 
 				pr_err("%s: ERROR: Should not be here\n",
-						__func__);
-				pr_err("%s: ERROR: DEC connected to SLIM TX%u\n"
-						, __func__, tx_port);
+					__func__);
+				pr_err("%s: ERROR: DEC connected to SLIM TX%u\n",
+					__func__, tx_port);
 				return -EINVAL;
 
 			} else if (tx_port_reg_val == 0) {
 				pr_debug("%s: no signal to SLIM TX%u\n",
-						__func__, tx_port);
+					__func__, tx_port);
 			} else {
-				pr_err("%s: ERROR: wrong signal to SLIM TX%u\n"
-						, __func__, tx_port);
-				pr_err("%s: ERROR: wrong signal = %u\n"
-						, __func__, tx_port_reg_val);
+				pr_err("%s: ERROR: wrong signal to SLIM TX%u\n",
+					__func__, tx_port);
+				pr_err("%s: ERROR: wrong signal = %u\n",
+					__func__, tx_port_reg_val);
 				return -EINVAL;
 			}
 		}
@@ -4244,8 +4464,8 @@
 }
 
 static int tabla_hw_params(struct snd_pcm_substream *substream,
-		struct snd_pcm_hw_params *params,
-		struct snd_soc_dai *dai)
+			   struct snd_pcm_hw_params *params,
+			   struct snd_soc_dai *dai)
 {
 	struct snd_soc_codec *codec = dai->codec;
 	struct tabla_priv *tabla = snd_soc_codec_get_drvdata(dai->codec);
@@ -4254,8 +4474,8 @@
 	int ret;
 
 	pr_debug("%s: dai_name = %s DAI-ID %x rate %d num_ch %d\n", __func__,
-			dai->name, dai->id, params_rate(params),
-			params_channels(params));
+		 dai->name, dai->id, params_rate(params),
+		 params_channels(params));
 
 	switch (params_rate(params)) {
 	case 8000:
@@ -4290,7 +4510,7 @@
 		break;
 	default:
 		pr_err("%s: Invalid sampling rate %d\n", __func__,
-				params_rate(params));
+			params_rate(params));
 		return -EINVAL;
 	}
 
@@ -4298,10 +4518,10 @@
 	case SNDRV_PCM_STREAM_CAPTURE:
 
 		ret = tabla_set_decimator_rate(dai, tx_fs_rate_reg_val,
-				params_rate(params));
+					       params_rate(params));
 		if (ret < 0) {
 			pr_err("%s: set decimator rate failed %d\n", __func__,
-					ret);
+			       ret);
 			return ret;
 		}
 
@@ -4316,24 +4536,34 @@
 					TABLA_A_CDC_CLK_TX_I2S_CTL, 0x20, 0x00);
 				break;
 			default:
-				pr_err("%s: invalid TX format %u\n", __func__,
-						params_format(params));
+				pr_err("%s: Invalid format %d\n", __func__,
+					params_format(params));
 				return -EINVAL;
 			}
 			snd_soc_update_bits(codec, TABLA_A_CDC_CLK_TX_I2S_CTL,
-					0x07, tx_fs_rate_reg_val);
+					    0x07, tx_fs_rate_reg_val);
 		} else {
-			tabla->dai[dai->id - 1].rate   = params_rate(params);
+			switch (params_format(params)) {
+			case SNDRV_PCM_FORMAT_S16_LE:
+				tabla->dai[dai->id].bit_width = 16;
+				break;
+			default:
+				pr_err("%s: Invalid TX format %d\n", __func__,
+					params_format(params));
+				return -EINVAL;
+			}
+			tabla->dai[dai->id].rate   = params_rate(params);
 		}
 		break;
 
 	case SNDRV_PCM_STREAM_PLAYBACK:
 
 		ret = tabla_set_interpolator_rate(dai, rx_fs_rate_reg_val,
-				compander_fs, params_rate(params));
+						  compander_fs,
+						  params_rate(params));
 		if (ret < 0) {
 			pr_err("%s: set decimator rate failed %d\n", __func__,
-					ret);
+			       ret);
 			return ret;
 		}
 
@@ -4348,20 +4578,29 @@
 					TABLA_A_CDC_CLK_RX_I2S_CTL, 0x20, 0x00);
 				break;
 			default:
-				pr_err("%s: invalid RX format %u\n", __func__,
-						params_format(params));
+				pr_err("%s: Invalid RX format %d\n", __func__,
+					params_format(params));
 				return -EINVAL;
 			}
 			snd_soc_update_bits(codec, TABLA_A_CDC_CLK_RX_I2S_CTL,
 					0x03, (rx_fs_rate_reg_val >> 0x05));
 		} else {
-			tabla->dai[dai->id - 1].rate   = params_rate(params);
+			switch (params_format(params)) {
+			case SNDRV_PCM_FORMAT_S16_LE:
+				tabla->dai[dai->id].bit_width = 16;
+				break;
+			default:
+				pr_err("%s: Invalid format %d\n", __func__,
+					params_format(params));
+				return -EINVAL;
+			}
+			tabla->dai[dai->id].rate = params_rate(params);
 		}
 		break;
 
 	default:
 		pr_err("%s: Invalid stream type %d\n", __func__,
-				substream->stream);
+			substream->stream);
 		return -EINVAL;
 	}
 	return 0;
@@ -4467,7 +4706,7 @@
 static struct snd_soc_dai_driver tabla_i2s_dai[] = {
 	{
 		.name = "tabla_i2s_rx1",
-		.id = 1,
+		.id = AIF1_PB,
 		.playback = {
 			.stream_name = "AIF1 Playback",
 			.rates = WCD9310_RATES,
@@ -4481,7 +4720,7 @@
 	},
 	{
 		.name = "tabla_i2s_tx1",
-		.id = 2,
+		.id = AIF1_CAP,
 		.capture = {
 			.stream_name = "AIF1 Capture",
 			.rates = WCD9310_RATES,
@@ -4496,15 +4735,16 @@
 };
 
 static int tabla_codec_enable_chmask(struct tabla_priv *tabla_p,
-	int event, int index)
+				     int event, int index)
 {
 	int  ret = 0;
-	u32 k = 0;
+	struct wcd9xxx_ch *ch;
+
 	switch (event) {
 	case SND_SOC_DAPM_POST_PMU:
-		for (k = 0; k < tabla_p->dai[index].ch_tot; k++) {
-			ret = wcd9xxx_get_slave_port(
-					tabla_p->dai[index].ch_num[k]);
+		list_for_each_entry(ch,
+			&tabla_p->dai[index].wcd9xxx_ch_list, list) {
+			ret = wcd9xxx_get_slave_port(ch->ch_num);
 			if (ret < 0) {
 				pr_err("%s: Invalid slave port ID: %d\n",
 					__func__, ret);
@@ -4513,7 +4753,6 @@
 			}
 			tabla_p->dai[index].ch_mask |= 1 << ret;
 		}
-		ret = 0;
 		break;
 	case SND_SOC_DAPM_POST_PMD:
 		ret = wait_event_timeout(tabla_p->dai[index].dai_wait,
@@ -4523,191 +4762,134 @@
 			pr_err("%s: Slim close tx/rx wait timeout\n",
 				__func__);
 			ret = -EINVAL;
-		} else
-			ret = 0;
+		}
 		break;
 	}
 	return ret;
 }
 
 static int tabla_codec_enable_slimrx(struct snd_soc_dapm_widget *w,
-	struct snd_kcontrol *kcontrol, int event)
+				     struct snd_kcontrol *kcontrol,
+				     int event)
 {
-	struct wcd9xxx *tabla;
+	struct wcd9xxx *core;
 	struct snd_soc_codec *codec = w->codec;
 	struct tabla_priv *tabla_p = snd_soc_codec_get_drvdata(codec);
-	u32  j = 0;
-	int  ret = 0;
-	codec->control_data = dev_get_drvdata(codec->dev->parent);
-	tabla = codec->control_data;
+	u32  ret = 0;
+	struct wcd9xxx_codec_dai_data *dai;
+
+	core = dev_get_drvdata(codec->dev->parent);
+
+	pr_debug("%s: event called! codec name %s num_dai %d\n"
+		"stream name %s event %d\n",
+		__func__, w->codec->name, w->codec->num_dai,
+		w->sname, event);
 
 	/* Execute the callback only if interface type is slimbus */
 	if (tabla_p->intf_type != WCD9XXX_INTERFACE_TYPE_SLIMBUS) {
-		if (event == SND_SOC_DAPM_POST_PMD && (tabla != NULL) &&
-		    (tabla->dev != NULL) &&
-		    (tabla->dev->parent != NULL)) {
-			pm_runtime_mark_last_busy(tabla->dev->parent);
-			pm_runtime_put(tabla->dev->parent);
+		if (event == SND_SOC_DAPM_POST_PMD && (core != NULL) &&
+		    (core->dev != NULL) &&
+		    (core->dev->parent != NULL)) {
+			pm_runtime_mark_last_busy(core->dev->parent);
+			pm_runtime_put(core->dev->parent);
 		}
 		return 0;
 	}
-
-	pr_debug("%s: %s %d\n", __func__, w->name, event);
+	pr_debug("%s: w->name %s w->shift %d event %d\n",
+		__func__, w->name, w->shift, event);
+	dai = &tabla_p->dai[w->shift];
 
 	switch (event) {
 	case SND_SOC_DAPM_POST_PMU:
-		for (j = 0; j < ARRAY_SIZE(tabla_dai); j++) {
-			if ((tabla_dai[j].id == AIF1_CAP) ||
-			    (tabla_dai[j].id == AIF2_CAP) ||
-			    (tabla_dai[j].id == AIF3_CAP))
-				continue;
-			if (!strncmp(w->sname,
-				tabla_dai[j].playback.stream_name, 13)) {
-				++tabla_p->dai[j].ch_act;
-				break;
-			}
-		}
-		if (tabla_p->dai[j].ch_act == tabla_p->dai[j].ch_tot) {
-			ret = tabla_codec_enable_chmask(tabla_p,
-							SND_SOC_DAPM_POST_PMU,
-							j);
-			ret = wcd9xxx_cfg_slim_sch_rx(tabla,
-					tabla_p->dai[j].ch_num,
-					tabla_p->dai[j].ch_tot,
-					tabla_p->dai[j].rate);
-		}
+		ret = tabla_codec_enable_chmask(tabla_p, SND_SOC_DAPM_POST_PMU,
+						w->shift);
+		ret = wcd9xxx_cfg_slim_sch_rx(core, &dai->wcd9xxx_ch_list,
+					      dai->rate, dai->bit_width,
+					      &dai->grph);
 		break;
 	case SND_SOC_DAPM_POST_PMD:
-		for (j = 0; j < ARRAY_SIZE(tabla_dai); j++) {
-			if ((tabla_dai[j].id == AIF1_CAP) ||
-			    (tabla_dai[j].id == AIF2_CAP) ||
-			    (tabla_dai[j].id == AIF3_CAP))
-				continue;
-			if (!strncmp(w->sname,
-				tabla_dai[j].playback.stream_name, 13)) {
-				if (tabla_p->dai[j].ch_act)
-					--tabla_p->dai[j].ch_act;
-				break;
-			}
+		ret = wcd9xxx_close_slim_sch_rx(core,
+						&dai->wcd9xxx_ch_list,
+						dai->grph);
+		ret = tabla_codec_enable_chmask(tabla_p, SND_SOC_DAPM_POST_PMD,
+						w->shift);
+		if (ret < 0) {
+			ret = wcd9xxx_disconnect_port(core,
+						      &dai->wcd9xxx_ch_list,
+						      dai->grph);
+			pr_info("%s: Disconnect RX port, ret = %d\n",
+				__func__, ret);
 		}
-		if (!tabla_p->dai[j].ch_act) {
-			ret = wcd9xxx_close_slim_sch_rx(tabla,
-						tabla_p->dai[j].ch_num,
-						tabla_p->dai[j].ch_tot);
-			ret = tabla_codec_enable_chmask(tabla_p,
-							SND_SOC_DAPM_POST_PMD,
-							j);
-			if (ret < 0) {
-				ret = wcd9xxx_disconnect_port(tabla,
-							tabla_p->dai[j].ch_num,
-							tabla_p->dai[j].ch_tot,
-							1);
-				pr_info("%s: Disconnect RX port ret = %d\n",
-					__func__, ret);
-			}
-			tabla_p->dai[j].rate = 0;
-			memset(tabla_p->dai[j].ch_num, 0, (sizeof(u32)*
-				tabla_p->dai[j].ch_tot));
-			tabla_p->dai[j].ch_tot = 0;
-
-			if ((tabla != NULL) &&
-			    (tabla->dev != NULL) &&
-			    (tabla->dev->parent != NULL)) {
-				pm_runtime_mark_last_busy(tabla->dev->parent);
-				pm_runtime_put(tabla->dev->parent);
-			}
+		if ((core != NULL) &&
+			(core->dev != NULL) &&
+			(core->dev->parent != NULL)) {
+			pm_runtime_mark_last_busy(core->dev->parent);
+			pm_runtime_put(core->dev->parent);
 		}
+		break;
 	}
+
 	return ret;
 }
 
 static int tabla_codec_enable_slimtx(struct snd_soc_dapm_widget *w,
-	struct snd_kcontrol *kcontrol, int event)
+				     struct snd_kcontrol *kcontrol,
+				     int event)
 {
-	struct wcd9xxx *tabla;
+	struct wcd9xxx *core;
 	struct snd_soc_codec *codec = w->codec;
 	struct tabla_priv *tabla_p = snd_soc_codec_get_drvdata(codec);
-	/* index to the DAI ID, for now hardcoding */
-	u32  j = 0;
-	int  ret = 0;
+	u32  ret = 0;
+	struct wcd9xxx_codec_dai_data *dai;
 
-	codec->control_data = dev_get_drvdata(codec->dev->parent);
-	tabla = codec->control_data;
+	core = dev_get_drvdata(codec->dev->parent);
+
+	pr_debug("%s: event called! codec name %s num_dai %d\n"
+		 "stream name %s\n", __func__, w->codec->name,
+		 w->codec->num_dai, w->sname);
 
 	/* Execute the callback only if interface type is slimbus */
 	if (tabla_p->intf_type != WCD9XXX_INTERFACE_TYPE_SLIMBUS) {
-		if (event == SND_SOC_DAPM_POST_PMD && (tabla != NULL) &&
-		    (tabla->dev != NULL) &&
-		    (tabla->dev->parent != NULL)) {
-			pm_runtime_mark_last_busy(tabla->dev->parent);
-			pm_runtime_put(tabla->dev->parent);
+		if (event == SND_SOC_DAPM_POST_PMD && (core != NULL) &&
+		    (core->dev != NULL) &&
+		    (core->dev->parent != NULL)) {
+			pm_runtime_mark_last_busy(core->dev->parent);
+			pm_runtime_put(core->dev->parent);
 		}
 		return 0;
 	}
 
 	pr_debug("%s(): %s %d\n", __func__, w->name, event);
 
+	dai = &tabla_p->dai[w->shift];
 	switch (event) {
 	case SND_SOC_DAPM_POST_PMU:
-		for (j = 0; j < ARRAY_SIZE(tabla_dai); j++) {
-			if (tabla_dai[j].id == AIF1_PB ||
-				tabla_dai[j].id == AIF2_PB ||
-				tabla_dai[j].id == AIF3_PB)
-				continue;
-			if (!strncmp(w->sname,
-				tabla_dai[j].capture.stream_name, 13)) {
-				++tabla_p->dai[j].ch_act;
-				break;
-			}
-		}
-		if (tabla_p->dai[j].ch_act == tabla_p->dai[j].ch_tot) {
-			ret = tabla_codec_enable_chmask(tabla_p,
-							SND_SOC_DAPM_POST_PMU,
-							j);
-			ret = wcd9xxx_cfg_slim_sch_tx(tabla,
-						tabla_p->dai[j].ch_num,
-						tabla_p->dai[j].ch_tot,
-						tabla_p->dai[j].rate);
-		}
+		ret = tabla_codec_enable_chmask(tabla_p, SND_SOC_DAPM_POST_PMU,
+						w->shift);
+		ret = wcd9xxx_cfg_slim_sch_tx(core, &dai->wcd9xxx_ch_list,
+					      dai->rate,
+					      dai->bit_width,
+					      &dai->grph);
 		break;
 	case SND_SOC_DAPM_POST_PMD:
-		for (j = 0; j < ARRAY_SIZE(tabla_dai); j++) {
-			if (tabla_dai[j].id == AIF1_PB ||
-				tabla_dai[j].id == AIF2_PB ||
-				tabla_dai[j].id == AIF3_PB)
-				continue;
-			if (!strncmp(w->sname,
-				tabla_dai[j].capture.stream_name, 13)) {
-				--tabla_p->dai[j].ch_act;
-				break;
-			}
+		ret = wcd9xxx_close_slim_sch_tx(core, &dai->wcd9xxx_ch_list,
+						dai->grph);
+		ret = tabla_codec_enable_chmask(tabla_p, SND_SOC_DAPM_POST_PMD,
+						w->shift);
+		if (ret < 0) {
+			ret = wcd9xxx_disconnect_port(core,
+						      &dai->wcd9xxx_ch_list,
+						      dai->grph);
+			pr_info("%s: Disconnect TX port, ret = %d\n",
+				__func__, ret);
 		}
-		if (!tabla_p->dai[j].ch_act) {
-			ret = wcd9xxx_close_slim_sch_tx(tabla,
-						tabla_p->dai[j].ch_num,
-						tabla_p->dai[j].ch_tot);
-			ret = tabla_codec_enable_chmask(tabla_p,
-						SND_SOC_DAPM_POST_PMD,
-						j);
-			if (ret < 0) {
-				ret = wcd9xxx_disconnect_port(tabla,
-						tabla_p->dai[j].ch_num,
-						tabla_p->dai[j].ch_tot, 0);
-				pr_info("%s: Disconnect TX port, ret = %d\n",
-					__func__, ret);
-			}
-
-			tabla_p->dai[j].rate = 0;
-			memset(tabla_p->dai[j].ch_num, 0, (sizeof(u32)*
-					tabla_p->dai[j].ch_tot));
-			tabla_p->dai[j].ch_tot = 0;
-			if ((tabla != NULL) &&
-			    (tabla->dev != NULL) &&
-			    (tabla->dev->parent != NULL)) {
-				pm_runtime_mark_last_busy(tabla->dev->parent);
-				pm_runtime_put(tabla->dev->parent);
-			}
+		if ((core != NULL) &&
+			(core->dev != NULL) &&
+			(core->dev->parent != NULL)) {
+			pm_runtime_mark_last_busy(core->dev->parent);
+			pm_runtime_put(core->dev->parent);
 		}
+		break;
 	}
 	return ret;
 }
@@ -4726,6 +4908,38 @@
 	SND_SOC_DAPM_MIXER("DAC1", SND_SOC_NOPM, 0, 0, dac1_switch,
 		ARRAY_SIZE(dac1_switch)),
 
+	SND_SOC_DAPM_AIF_IN_E("AIF1 PB", "AIF1 Playback", 0, SND_SOC_NOPM,
+				AIF1_PB, 0, tabla_codec_enable_slimrx,
+				SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_AIF_IN_E("AIF2 PB", "AIF2 Playback", 0, SND_SOC_NOPM,
+				AIF2_PB, 0, tabla_codec_enable_slimrx,
+				SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_AIF_IN_E("AIF3 PB", "AIF3 Playback", 0, SND_SOC_NOPM,
+				AIF3_PB, 0, tabla_codec_enable_slimrx,
+				SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_MUX("SLIM RX1 MUX", SND_SOC_NOPM, TABLA_RX1, 0,
+				&slim_rx_mux[TABLA_RX1]),
+	SND_SOC_DAPM_MUX("SLIM RX2 MUX", SND_SOC_NOPM, TABLA_RX2, 0,
+				&slim_rx_mux[TABLA_RX2]),
+	SND_SOC_DAPM_MUX("SLIM RX3 MUX", SND_SOC_NOPM, TABLA_RX3, 0,
+				&slim_rx_mux[TABLA_RX3]),
+	SND_SOC_DAPM_MUX("SLIM RX4 MUX", SND_SOC_NOPM, TABLA_RX4, 0,
+				&slim_rx_mux[TABLA_RX4]),
+	SND_SOC_DAPM_MUX("SLIM RX5 MUX", SND_SOC_NOPM, TABLA_RX5, 0,
+				&slim_rx_mux[TABLA_RX5]),
+	SND_SOC_DAPM_MUX("SLIM RX6 MUX", SND_SOC_NOPM, TABLA_RX6, 0,
+				&slim_rx_mux[TABLA_RX6]),
+	SND_SOC_DAPM_MUX("SLIM RX7 MUX", SND_SOC_NOPM, TABLA_RX7, 0,
+				&slim_rx_mux[TABLA_RX7]),
+
+	SND_SOC_DAPM_MIXER("SLIM RX1", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("SLIM RX2", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("SLIM RX3", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("SLIM RX4", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("SLIM RX5", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("SLIM RX6", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("SLIM RX7", SND_SOC_NOPM, 0, 0, NULL, 0),
 	/* Headphone */
 	SND_SOC_DAPM_OUTPUT("HEADPHONE"),
 	SND_SOC_DAPM_PGA_E("HPHL", TABLA_A_RX_HPH_CNP_EN, 5, 0, NULL, 0,
@@ -5005,16 +5219,47 @@
 		tabla_codec_enable_adc, SND_SOC_DAPM_PRE_PMU |
 		SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
 
-	SND_SOC_DAPM_MUX("SLIM TX1 MUX", SND_SOC_NOPM, 0, 0, &sb_tx1_mux),
-	SND_SOC_DAPM_MUX("SLIM TX2 MUX", SND_SOC_NOPM, 0, 0, &sb_tx2_mux),
-	SND_SOC_DAPM_MUX("SLIM TX3 MUX", SND_SOC_NOPM, 0, 0, &sb_tx3_mux),
-	SND_SOC_DAPM_MUX("SLIM TX4 MUX", SND_SOC_NOPM, 0, 0, &sb_tx4_mux),
-	SND_SOC_DAPM_MUX("SLIM TX5 MUX", SND_SOC_NOPM, 0, 0, &sb_tx5_mux),
-	SND_SOC_DAPM_MUX("SLIM TX6 MUX", SND_SOC_NOPM, 0, 0, &sb_tx6_mux),
-	SND_SOC_DAPM_MUX("SLIM TX7 MUX", SND_SOC_NOPM, 0, 0, &sb_tx7_mux),
-	SND_SOC_DAPM_MUX("SLIM TX8 MUX", SND_SOC_NOPM, 0, 0, &sb_tx8_mux),
-	SND_SOC_DAPM_MUX("SLIM TX9 MUX", SND_SOC_NOPM, 0, 0, &sb_tx9_mux),
-	SND_SOC_DAPM_MUX("SLIM TX10 MUX", SND_SOC_NOPM, 0, 0, &sb_tx10_mux),
+	SND_SOC_DAPM_AIF_OUT_E("AIF1 CAP", "AIF1 Capture", 0, SND_SOC_NOPM,
+		AIF1_CAP, 0, tabla_codec_enable_slimtx,
+		SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_AIF_OUT_E("AIF2 CAP", "AIF2 Capture", 0, SND_SOC_NOPM,
+		AIF2_CAP, 0, tabla_codec_enable_slimtx,
+		SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_AIF_OUT_E("AIF3 CAP", "AIF3 Capture", 0, SND_SOC_NOPM,
+		AIF3_CAP, 0, tabla_codec_enable_slimtx,
+		SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_MIXER("AIF1_CAP Mixer", SND_SOC_NOPM, AIF1_CAP, 0,
+		aif_cap_mixer, ARRAY_SIZE(aif_cap_mixer)),
+
+	SND_SOC_DAPM_MIXER("AIF2_CAP Mixer", SND_SOC_NOPM, AIF2_CAP, 0,
+		aif_cap_mixer, ARRAY_SIZE(aif_cap_mixer)),
+
+	SND_SOC_DAPM_MIXER("AIF3_CAP Mixer", SND_SOC_NOPM, AIF3_CAP, 0,
+		aif_cap_mixer, ARRAY_SIZE(aif_cap_mixer)),
+
+	SND_SOC_DAPM_MUX("SLIM TX1 MUX", SND_SOC_NOPM, TABLA_TX1, 0,
+		&sb_tx1_mux),
+	SND_SOC_DAPM_MUX("SLIM TX2 MUX", SND_SOC_NOPM, TABLA_TX2, 0,
+		&sb_tx2_mux),
+	SND_SOC_DAPM_MUX("SLIM TX3 MUX", SND_SOC_NOPM, TABLA_TX3, 0,
+		&sb_tx3_mux),
+	SND_SOC_DAPM_MUX("SLIM TX4 MUX", SND_SOC_NOPM, TABLA_TX4, 0,
+		&sb_tx4_mux),
+	SND_SOC_DAPM_MUX("SLIM TX5 MUX", SND_SOC_NOPM, TABLA_TX5, 0,
+		&sb_tx5_mux),
+	SND_SOC_DAPM_MUX("SLIM TX6 MUX", SND_SOC_NOPM, TABLA_TX6, 0,
+		&sb_tx6_mux),
+	SND_SOC_DAPM_MUX("SLIM TX7 MUX", SND_SOC_NOPM, TABLA_TX7, 0,
+		&sb_tx7_mux),
+	SND_SOC_DAPM_MUX("SLIM TX8 MUX", SND_SOC_NOPM, TABLA_TX8, 0,
+		&sb_tx8_mux),
+	SND_SOC_DAPM_MUX("SLIM TX9 MUX", SND_SOC_NOPM, TABLA_TX9, 0,
+		&sb_tx9_mux),
+	SND_SOC_DAPM_MUX("SLIM TX10 MUX", SND_SOC_NOPM, TABLA_TX10, 0,
+		&sb_tx10_mux),
 
 	/* Digital Mic Inputs */
 	SND_SOC_DAPM_ADC_E("DMIC1", NULL, SND_SOC_NOPM, 0, 0,
@@ -5114,7 +5359,7 @@
 	short bias_value;
 	struct tabla_priv *tabla = snd_soc_codec_get_drvdata(codec);
 
-	wcd9xxx_disable_irq(codec->control_data, TABLA_IRQ_MBHC_POTENTIAL);
+	wcd9xxx_disable_irq(codec->control_data, WCD9XXX_IRQ_MBHC_POTENTIAL);
 	if (noreldetection)
 		tabla_turn_onoff_rel_detection(codec, false);
 
@@ -5150,7 +5395,7 @@
 
 	if (noreldetection)
 		tabla_turn_onoff_rel_detection(codec, true);
-	wcd9xxx_enable_irq(codec->control_data, TABLA_IRQ_MBHC_POTENTIAL);
+	wcd9xxx_enable_irq(codec->control_data, WCD9XXX_IRQ_MBHC_POTENTIAL);
 
 	return bias_value;
 }
@@ -5339,9 +5584,9 @@
 		}
 		tabla_set_and_turnoff_hph_padac(codec);
 		hphocp_off_report(tabla, SND_JACK_OC_HPHR,
-				  TABLA_IRQ_HPH_PA_OCPR_FAULT);
+				  WCD9XXX_IRQ_HPH_PA_OCPR_FAULT);
 		hphocp_off_report(tabla, SND_JACK_OC_HPHL,
-				  TABLA_IRQ_HPH_PA_OCPL_FAULT);
+				  WCD9XXX_IRQ_HPH_PA_OCPL_FAULT);
 		tabla->current_plug = PLUG_TYPE_NONE;
 		tabla->mbhc_polling_active = false;
 	} else {
@@ -5504,7 +5749,7 @@
 	snd_soc_update_bits(codec, tabla->reg_addr.micb_4_mbhc, 0x3,
 			    tabla->mbhc_cfg.micbias);
 
-	wcd9xxx_enable_irq(codec->control_data, TABLA_IRQ_MBHC_INSERTION);
+	wcd9xxx_enable_irq(codec->control_data, WCD9XXX_IRQ_MBHC_INSERTION);
 	snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_INT_CTL, 0x1, 0x1);
 	pr_debug("%s: leave\n", __func__);
 	return 0;
@@ -5634,7 +5879,7 @@
 	tabla = snd_soc_codec_get_drvdata(codec);
 	calibration = tabla->mbhc_cfg.calibration;
 
-	wcd9xxx_disable_irq(codec->control_data, TABLA_IRQ_MBHC_POTENTIAL);
+	wcd9xxx_disable_irq(codec->control_data, WCD9XXX_IRQ_MBHC_POTENTIAL);
 	tabla_turn_onoff_rel_detection(codec, false);
 
 	/* First compute the DCE / STA wait times
@@ -5740,7 +5985,7 @@
 	snd_soc_write(codec, TABLA_A_MBHC_SCALING_MUX_1, 0x84);
 	usleep_range(100, 100);
 
-	wcd9xxx_enable_irq(codec->control_data, TABLA_IRQ_MBHC_POTENTIAL);
+	wcd9xxx_enable_irq(codec->control_data, WCD9XXX_IRQ_MBHC_POTENTIAL);
 	tabla_turn_onoff_rel_detection(codec, true);
 }
 
@@ -6328,7 +6573,7 @@
 					    0x10);
 		} else {
 			wcd9xxx_disable_irq(codec->control_data,
-					  TABLA_IRQ_HPH_PA_OCPL_FAULT);
+					  WCD9XXX_IRQ_HPH_PA_OCPL_FAULT);
 			tabla->hph_status |= SND_JACK_OC_HPHL;
 			if (tabla->mbhc_cfg.headset_jack)
 				tabla_snd_soc_jack_report(tabla,
@@ -6362,7 +6607,7 @@
 					    0x10);
 		} else {
 			wcd9xxx_disable_irq(codec->control_data,
-					  TABLA_IRQ_HPH_PA_OCPR_FAULT);
+					  WCD9XXX_IRQ_HPH_PA_OCPR_FAULT);
 			tabla->hph_status |= SND_JACK_OC_HPHR;
 			if (tabla->mbhc_cfg.headset_jack)
 				tabla_snd_soc_jack_report(tabla,
@@ -6971,7 +7216,7 @@
 			wcd9xxx_unlock_sleep(core);
 		} else {
 			wcd9xxx_enable_irq(codec->control_data,
-					   TABLA_IRQ_MBHC_INSERTION);
+					   WCD9XXX_IRQ_MBHC_INSERTION);
 			pr_err("%s: Error detecting plug insertion\n",
 			       __func__);
 		}
@@ -7009,7 +7254,7 @@
 
 	pr_debug("%s: enter\n", __func__);
 	TABLA_ACQUIRE_LOCK(priv->codec_resource_lock);
-	wcd9xxx_disable_irq(codec->control_data, TABLA_IRQ_MBHC_INSERTION);
+	wcd9xxx_disable_irq(codec->control_data, WCD9XXX_IRQ_MBHC_INSERTION);
 
 	is_mb_trigger = !!(snd_soc_read(codec, priv->mbhc_bias_regs.mbhc_reg) &
 					0x10);
@@ -7256,7 +7501,8 @@
 	snd_soc_update_bits(codec, tabla->mbhc_bias_regs.mbhc_reg, 0x90, 0x00);
 	snd_soc_update_bits(codec, TABLA_A_MBHC_HPH, 0x13, 0x00);
 	snd_soc_update_bits(codec, tabla->mbhc_bias_regs.ctl_reg, 0x01, 0x00);
-	wcd9xxx_disable_irq_sync(codec->control_data, TABLA_IRQ_MBHC_INSERTION);
+	wcd9xxx_disable_irq_sync(codec->control_data,
+				 WCD9XXX_IRQ_MBHC_INSERTION);
 	tabla_codec_detect_plug_type(codec);
 	wcd9xxx_unlock_sleep(tabla_core);
 }
@@ -7352,9 +7598,18 @@
 {
 	int r = IRQ_HANDLED;
 	struct snd_soc_codec *codec = data;
+	struct tabla_priv *tabla = snd_soc_codec_get_drvdata(codec);
 
 	if (unlikely(wcd9xxx_lock_sleep(codec->control_data) == false)) {
 		pr_warn("%s: failed to hold suspend\n", __func__);
+		/*
+		 * Give up this IRQ for now and resend this IRQ so IRQ can be
+		 * handled after system resume
+		 */
+		TABLA_ACQUIRE_LOCK(tabla->codec_resource_lock);
+		tabla->gpio_irq_resend = true;
+		TABLA_RELEASE_LOCK(tabla->codec_resource_lock);
+		wake_lock_timeout(&tabla->irq_resend_wlock, HZ);
 		r = IRQ_NONE;
 	} else {
 		tabla_hs_gpio_handler(codec);
@@ -7470,9 +7725,9 @@
 	if (!IS_ERR_VALUE(ret)) {
 		snd_soc_update_bits(codec, TABLA_A_RX_HPH_OCP_CTL, 0x10, 0x10);
 		wcd9xxx_enable_irq(codec->control_data,
-				 TABLA_IRQ_HPH_PA_OCPL_FAULT);
+				 WCD9XXX_IRQ_HPH_PA_OCPL_FAULT);
 		wcd9xxx_enable_irq(codec->control_data,
-				 TABLA_IRQ_HPH_PA_OCPR_FAULT);
+				 WCD9XXX_IRQ_HPH_PA_OCPR_FAULT);
 
 		if (tabla->mbhc_cfg.gpio) {
 			ret = request_threaded_irq(tabla->mbhc_cfg.gpio_irq,
@@ -7590,7 +7845,6 @@
 	int i, j, port_id, k, ch_mask_temp;
 	unsigned long slimbus_value;
 	u8 val;
-
 	for (i = 0; i < WCD9XXX_SLIM_NUM_PORT_REG; i++) {
 		slimbus_value = wcd9xxx_interface_reg_read(codec->control_data,
 			TABLA_SLIM_PGD_PORT_INT_STATUS0 + i);
@@ -7604,17 +7858,18 @@
 				pr_err_ratelimited("underflow error on port %x,"
 					" value %x\n", i*8 + j, val);
 			if (val & 0x4) {
-				pr_debug("%s: port %x disconnect value %x\n",
-					__func__, i*8 + j, val);
 				port_id = i*8 + j;
 				for (k = 0; k < ARRAY_SIZE(tabla_dai); k++) {
 					ch_mask_temp = 1 << port_id;
+					pr_debug("%s: tabla_p->dai[%d].ch_mask = 0x%x\n",
+						 __func__, k,
+						 tabla_p->dai[k].ch_mask);
 					if (ch_mask_temp &
 						tabla_p->dai[k].ch_mask) {
 						tabla_p->dai[k].ch_mask &=
-								~ch_mask_temp;
+							~ch_mask_temp;
 					if (!tabla_p->dai[k].ch_mask)
-							wake_up(
+						wake_up(
 						&tabla_p->dai[k].dai_wait);
 					}
 				}
@@ -8096,7 +8351,7 @@
 	struct snd_soc_dapm_context *dapm = &codec->dapm;
 	int ret = 0;
 	int i;
-	int ch_cnt;
+	void *ptr = NULL;
 
 	codec->control_data = dev_get_drvdata(codec->dev->parent);
 	control = codec->control_data;
@@ -8156,8 +8411,6 @@
 		goto err_pdata;
 	}
 
-//	snd_soc_add_codec_controls(codec, tabla_snd_controls,
-//			     ARRAY_SIZE(tabla_snd_controls));
 	if (TABLA_IS_1_X(control->version))
 		snd_soc_add_codec_controls(codec, tabla_1_x_snd_controls,
 				     ARRAY_SIZE(tabla_1_x_snd_controls));
@@ -8165,15 +8418,6 @@
 		snd_soc_add_codec_controls(codec, tabla_2_higher_snd_controls,
 				     ARRAY_SIZE(tabla_2_higher_snd_controls));
 
-//	snd_soc_dapm_new_controls(dapm, tabla_dapm_widgets,
-//				  ARRAY_SIZE(tabla_dapm_widgets));
-
-	snd_soc_dapm_new_controls(dapm, tabla_dapm_aif_in_widgets,
-				  ARRAY_SIZE(tabla_dapm_aif_in_widgets));
-
-	snd_soc_dapm_new_controls(dapm, tabla_dapm_aif_out_widgets,
-				  ARRAY_SIZE(tabla_dapm_aif_out_widgets));
-
 	if (TABLA_IS_1_X(control->version))
 		snd_soc_dapm_new_controls(dapm, tabla_1_x_dapm_widgets,
 					  ARRAY_SIZE(tabla_1_x_dapm_widgets));
@@ -8181,13 +8425,35 @@
 		snd_soc_dapm_new_controls(dapm, tabla_2_higher_dapm_widgets,
 				    ARRAY_SIZE(tabla_2_higher_dapm_widgets));
 
+
+	ptr = kmalloc((sizeof(tabla_rx_chs) +
+		       sizeof(tabla_tx_chs)), GFP_KERNEL);
+	if (!ptr) {
+		pr_err("%s: no mem for slim chan ctl data\n", __func__);
+		ret = -ENOMEM;
+		goto err_nomem_slimch;
+	}
 	if (tabla->intf_type == WCD9XXX_INTERFACE_TYPE_I2C) {
 		snd_soc_dapm_new_controls(dapm, tabla_dapm_i2s_widgets,
 			ARRAY_SIZE(tabla_dapm_i2s_widgets));
 		snd_soc_dapm_add_routes(dapm, audio_i2s_map,
 			ARRAY_SIZE(audio_i2s_map));
+		for (i = 0; i < ARRAY_SIZE(tabla_i2s_dai); i++)
+			INIT_LIST_HEAD(&tabla->dai[i].wcd9xxx_ch_list);
+	} else if (tabla->intf_type == WCD9XXX_INTERFACE_TYPE_SLIMBUS) {
+		for (i = 0; i < NUM_CODEC_DAIS; i++) {
+			INIT_LIST_HEAD(&tabla->dai[i].wcd9xxx_ch_list);
+			init_waitqueue_head(&tabla->dai[i].dai_wait);
+		}
 	}
-//	snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map));
+
+	control->num_rx_port = TABLA_RX_MAX;
+	control->rx_chs = ptr;
+	memcpy(control->rx_chs, tabla_rx_chs, sizeof(tabla_rx_chs));
+	control->num_tx_port = TABLA_TX_MAX;
+	control->tx_chs = ptr + sizeof(tabla_rx_chs);
+	memcpy(control->tx_chs, tabla_tx_chs, sizeof(tabla_tx_chs));
+
 
 	if (TABLA_IS_1_X(control->version)) {
 		snd_soc_dapm_add_routes(dapm, tabla_1_x_lineout_2_to_4_map,
@@ -8203,44 +8469,50 @@
 
 	snd_soc_dapm_sync(dapm);
 
-	ret = wcd9xxx_request_irq(codec->control_data, TABLA_IRQ_MBHC_INSERTION,
+	ret = wcd9xxx_request_irq(codec->control_data,
+				  WCD9XXX_IRQ_MBHC_INSERTION,
 		tabla_hs_insert_irq, "Headset insert detect", tabla);
 	if (ret) {
 		pr_err("%s: Failed to request irq %d\n", __func__,
-			TABLA_IRQ_MBHC_INSERTION);
+		       WCD9XXX_IRQ_MBHC_INSERTION);
 		goto err_insert_irq;
 	}
-	wcd9xxx_disable_irq(codec->control_data, TABLA_IRQ_MBHC_INSERTION);
+	wcd9xxx_disable_irq(codec->control_data, WCD9XXX_IRQ_MBHC_INSERTION);
 
-	ret = wcd9xxx_request_irq(codec->control_data, TABLA_IRQ_MBHC_REMOVAL,
-		tabla_hs_remove_irq, "Headset remove detect", tabla);
+	ret = wcd9xxx_request_irq(codec->control_data,
+				  WCD9XXX_IRQ_MBHC_REMOVAL,
+				  tabla_hs_remove_irq,
+				  "Headset remove detect", tabla);
 	if (ret) {
 		pr_err("%s: Failed to request irq %d\n", __func__,
-			TABLA_IRQ_MBHC_REMOVAL);
+		       WCD9XXX_IRQ_MBHC_REMOVAL);
 		goto err_remove_irq;
 	}
 
-	ret = wcd9xxx_request_irq(codec->control_data, TABLA_IRQ_MBHC_POTENTIAL,
-		tabla_dce_handler, "DC Estimation detect", tabla);
+	ret = wcd9xxx_request_irq(codec->control_data,
+				  WCD9XXX_IRQ_MBHC_POTENTIAL,
+				  tabla_dce_handler, "DC Estimation detect",
+				  tabla);
 	if (ret) {
 		pr_err("%s: Failed to request irq %d\n", __func__,
-			TABLA_IRQ_MBHC_POTENTIAL);
+		       WCD9XXX_IRQ_MBHC_POTENTIAL);
 		goto err_potential_irq;
 	}
 
-	ret = wcd9xxx_request_irq(codec->control_data, TABLA_IRQ_MBHC_RELEASE,
-		tabla_release_handler, "Button Release detect", tabla);
+	ret = wcd9xxx_request_irq(codec->control_data, WCD9XXX_IRQ_MBHC_RELEASE,
+				  tabla_release_handler,
+				  "Button Release detect", tabla);
 	if (ret) {
 		pr_err("%s: Failed to request irq %d\n", __func__,
-			TABLA_IRQ_MBHC_RELEASE);
+		       WCD9XXX_IRQ_MBHC_RELEASE);
 		goto err_release_irq;
 	}
 
-	ret = wcd9xxx_request_irq(codec->control_data, TABLA_IRQ_SLIMBUS,
-		tabla_slimbus_irq, "SLIMBUS Slave", tabla);
+	ret = wcd9xxx_request_irq(codec->control_data, WCD9XXX_IRQ_SLIMBUS,
+				  tabla_slimbus_irq, "SLIMBUS Slave", tabla);
 	if (ret) {
 		pr_err("%s: Failed to request irq %d\n", __func__,
-			TABLA_IRQ_SLIMBUS);
+		       WCD9XXX_IRQ_SLIMBUS);
 		goto err_slimbus_irq;
 	}
 
@@ -8249,51 +8521,35 @@
 			TABLA_SLIM_PGD_PORT_INT_EN0 + i, 0xFF);
 
 	ret = wcd9xxx_request_irq(codec->control_data,
-		TABLA_IRQ_HPH_PA_OCPL_FAULT, tabla_hphl_ocp_irq,
-		"HPH_L OCP detect", tabla);
+				  WCD9XXX_IRQ_HPH_PA_OCPL_FAULT,
+				  tabla_hphl_ocp_irq,
+				  "HPH_L OCP detect", tabla);
 	if (ret) {
 		pr_err("%s: Failed to request irq %d\n", __func__,
-			TABLA_IRQ_HPH_PA_OCPL_FAULT);
+		       WCD9XXX_IRQ_HPH_PA_OCPL_FAULT);
 		goto err_hphl_ocp_irq;
 	}
-	wcd9xxx_disable_irq(codec->control_data, TABLA_IRQ_HPH_PA_OCPL_FAULT);
+	wcd9xxx_disable_irq(codec->control_data, WCD9XXX_IRQ_HPH_PA_OCPL_FAULT);
 
 	ret = wcd9xxx_request_irq(codec->control_data,
-		TABLA_IRQ_HPH_PA_OCPR_FAULT, tabla_hphr_ocp_irq,
-		"HPH_R OCP detect", tabla);
+				  WCD9XXX_IRQ_HPH_PA_OCPR_FAULT,
+				  tabla_hphr_ocp_irq,
+				  "HPH_R OCP detect", tabla);
 	if (ret) {
 		pr_err("%s: Failed to request irq %d\n", __func__,
-			TABLA_IRQ_HPH_PA_OCPR_FAULT);
+		       WCD9XXX_IRQ_HPH_PA_OCPR_FAULT);
 		goto err_hphr_ocp_irq;
 	}
-	wcd9xxx_disable_irq(codec->control_data, TABLA_IRQ_HPH_PA_OCPR_FAULT);
-	for (i = 0; i < ARRAY_SIZE(tabla_dai); i++) {
-		switch (tabla_dai[i].id) {
-		case AIF1_PB:
-			ch_cnt = tabla_dai[i].playback.channels_max;
-			break;
-		case AIF1_CAP:
-			ch_cnt = tabla_dai[i].capture.channels_max;
-			break;
-		case AIF2_PB:
-			ch_cnt = tabla_dai[i].playback.channels_max;
-			break;
-		case AIF2_CAP:
-			ch_cnt = tabla_dai[i].capture.channels_max;
-			break;
-		case AIF3_PB:
-			ch_cnt = tabla_dai[i].playback.channels_max;
-			break;
-		case AIF3_CAP:
-			ch_cnt = tabla_dai[i].capture.channels_max;
-			break;
-		default:
-			continue;
-		}
-		tabla->dai[i].ch_num = kzalloc((sizeof(unsigned int)*
-					ch_cnt), GFP_KERNEL);
-		init_waitqueue_head(&tabla->dai[i].dai_wait);
-	}
+	wcd9xxx_disable_irq(codec->control_data, WCD9XXX_IRQ_HPH_PA_OCPR_FAULT);
+
+	/*
+	 * Register suspend lock and notifier to resend edge triggered
+	 * gpio IRQs
+	 */
+	wake_lock_init(&tabla->irq_resend_wlock, WAKE_LOCK_SUSPEND,
+		       "tabla_gpio_irq_resend");
+	tabla->gpio_irq_resend = false;
+
 
 #ifdef CONFIG_DEBUG_FS
 	if (ret == 0) {
@@ -8310,40 +8566,46 @@
 
 err_hphr_ocp_irq:
 	wcd9xxx_free_irq(codec->control_data,
-			TABLA_IRQ_HPH_PA_OCPL_FAULT, tabla);
+			WCD9XXX_IRQ_HPH_PA_OCPL_FAULT, tabla);
 err_hphl_ocp_irq:
-	wcd9xxx_free_irq(codec->control_data, TABLA_IRQ_SLIMBUS, tabla);
+	wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_SLIMBUS, tabla);
 err_slimbus_irq:
-	wcd9xxx_free_irq(codec->control_data, TABLA_IRQ_MBHC_RELEASE, tabla);
+	wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_MBHC_RELEASE, tabla);
 err_release_irq:
-	wcd9xxx_free_irq(codec->control_data, TABLA_IRQ_MBHC_POTENTIAL, tabla);
+	wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_MBHC_POTENTIAL,
+			 tabla);
 err_potential_irq:
-	wcd9xxx_free_irq(codec->control_data, TABLA_IRQ_MBHC_REMOVAL, tabla);
+	wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_MBHC_REMOVAL, tabla);
 err_remove_irq:
-	wcd9xxx_free_irq(codec->control_data, TABLA_IRQ_MBHC_INSERTION, tabla);
+	wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_MBHC_INSERTION,
+			 tabla);
 err_insert_irq:
 err_pdata:
+	kfree(ptr);
+err_nomem_slimch:
 	mutex_destroy(&tabla->codec_resource_lock);
 	kfree(tabla);
 	return ret;
 }
 static int tabla_codec_remove(struct snd_soc_codec *codec)
 {
-	int i;
 	struct tabla_priv *tabla = snd_soc_codec_get_drvdata(codec);
-	wcd9xxx_free_irq(codec->control_data, TABLA_IRQ_SLIMBUS, tabla);
-	wcd9xxx_free_irq(codec->control_data, TABLA_IRQ_MBHC_RELEASE, tabla);
-	wcd9xxx_free_irq(codec->control_data, TABLA_IRQ_MBHC_POTENTIAL, tabla);
-	wcd9xxx_free_irq(codec->control_data, TABLA_IRQ_MBHC_REMOVAL, tabla);
-	wcd9xxx_free_irq(codec->control_data, TABLA_IRQ_MBHC_INSERTION, tabla);
+
+	wake_lock_destroy(&tabla->irq_resend_wlock);
+
+	wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_SLIMBUS, tabla);
+	wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_MBHC_RELEASE, tabla);
+	wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_MBHC_POTENTIAL,
+			 tabla);
+	wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_MBHC_REMOVAL, tabla);
+	wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_MBHC_INSERTION,
+			 tabla);
 	TABLA_ACQUIRE_LOCK(tabla->codec_resource_lock);
 	tabla_codec_disable_clock_block(codec);
 	TABLA_RELEASE_LOCK(tabla->codec_resource_lock);
 	tabla_codec_enable_bandgap(codec, TABLA_BANDGAP_OFF);
 	if (tabla->mbhc_fw)
 		release_firmware(tabla->mbhc_fw);
-	for (i = 0; i < ARRAY_SIZE(tabla_dai); i++)
-		kfree(tabla->dai[i].ch_num);
 	mutex_destroy(&tabla->codec_resource_lock);
 #ifdef CONFIG_DEBUG_FS
 	debugfs_remove(tabla->debugfs_poke);
@@ -8380,11 +8642,29 @@
 
 static int tabla_resume(struct device *dev)
 {
+	int irq;
 	struct platform_device *pdev = to_platform_device(dev);
 	struct tabla_priv *tabla = platform_get_drvdata(pdev);
+
 	dev_dbg(dev, "%s: system resume tabla %p\n", __func__, tabla);
-	if (tabla)
+	if (tabla) {
+		TABLA_ACQUIRE_LOCK(tabla->codec_resource_lock);
 		tabla->mbhc_last_resume = jiffies;
+		if (tabla->gpio_irq_resend) {
+			WARN_ON(!tabla->mbhc_cfg.gpio_irq);
+			tabla->gpio_irq_resend = false;
+
+			irq = tabla->mbhc_cfg.gpio_irq;
+			pr_debug("%s: Resending GPIO IRQ %d\n", __func__, irq);
+			irq_set_pending(irq);
+			check_irq_resend(irq_to_desc(irq), irq);
+
+			/* release suspend lock */
+			wake_unlock(&tabla->irq_resend_wlock);
+		}
+		TABLA_RELEASE_LOCK(tabla->codec_resource_lock);
+	}
+
 	return 0;
 }
 
diff --git a/sound/soc/codecs/wcd9310.h b/sound/soc/codecs/wcd9310.h
index 4c9f8b4..98c1835 100644
--- a/sound/soc/codecs/wcd9310.h
+++ b/sound/soc/codecs/wcd9310.h
@@ -252,3 +252,29 @@
 				 sizeof(cfg_ptr->_alpha[0]))))
 
 
+/* Number of input and output Slimbus port */
+enum {
+	TABLA_RX1 = 0,
+	TABLA_RX2,
+	TABLA_RX3,
+	TABLA_RX4,
+	TABLA_RX5,
+	TABLA_RX6,
+	TABLA_RX7,
+	TABLA_RX_MAX,
+};
+
+enum {
+	TABLA_TX1 = 0,
+	TABLA_TX2,
+	TABLA_TX3,
+	TABLA_TX4,
+	TABLA_TX5,
+	TABLA_TX6,
+	TABLA_TX7,
+	TABLA_TX8,
+	TABLA_TX9,
+	TABLA_TX10,
+	TABLA_TX_MAX,
+};
+
diff --git a/sound/soc/codecs/wcd9320.c b/sound/soc/codecs/wcd9320.c
index e8bb652..95df162 100644
--- a/sound/soc/codecs/wcd9320.c
+++ b/sound/soc/codecs/wcd9320.c
@@ -33,81 +33,46 @@
 #include <linux/kernel.h>
 #include <linux/gpio.h>
 #include "wcd9320.h"
+#include "wcd9xxx-resmgr.h"
 
 #define WCD9320_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\
 			SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |\
 			SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000)
 
-
 #define NUM_DECIMATORS 10
 #define NUM_INTERPOLATORS 7
 #define BITS_PER_REG 8
-#define TAIKO_CFILT_FAST_MODE 0x00
-#define TAIKO_CFILT_SLOW_MODE 0x40
-#define MBHC_FW_READ_ATTEMPTS 15
-#define MBHC_FW_READ_TIMEOUT 2000000
-
-enum {
-	MBHC_USE_HPHL_TRIGGER = 1,
-	MBHC_USE_MB_TRIGGER = 2
-};
-
-#define MBHC_NUM_DCE_PLUG_DETECT 3
-#define NUM_ATTEMPTS_INSERT_DETECT 25
-#define NUM_ATTEMPTS_TO_REPORT 5
-
-#define TAIKO_JACK_MASK (SND_JACK_HEADSET | SND_JACK_OC_HPHL | \
-			 SND_JACK_OC_HPHR | SND_JACK_UNSUPPORTED)
+#define TAIKO_TX_PORT_NUMBER	16
 
 #define TAIKO_I2S_MASTER_MODE_MASK 0x08
 
-#define TAIKO_OCP_ATTEMPT 1
-
-#define AIF1_PB 1
-#define AIF1_CAP 2
-#define AIF2_PB 3
-#define AIF2_CAP 4
-#define AIF3_CAP 5
-#define AIF3_PB  6
-
-#define NUM_CODEC_DAIS 6
-#define TAIKO_COMP_DIGITAL_GAIN_OFFSET 3
-
-struct taiko_codec_dai_data {
-	u32 rate;
-	u32 *ch_num;
-	u32 ch_act;
-	u32 ch_tot;
+enum {
+	AIF1_PB = 0,
+	AIF1_CAP,
+	AIF2_PB,
+	AIF2_CAP,
+	AIF3_PB,
+	AIF3_CAP,
+	NUM_CODEC_DAIS,
 };
 
-#define TAIKO_MCLK_RATE_12288KHZ 12288000
-#define TAIKO_MCLK_RATE_9600KHZ 9600000
+enum {
+	RX_MIX1_INP_SEL_ZERO = 0,
+	RX_MIX1_INP_SEL_SRC1,
+	RX_MIX1_INP_SEL_SRC2,
+	RX_MIX1_INP_SEL_IIR1,
+	RX_MIX1_INP_SEL_IIR2,
+	RX_MIX1_INP_SEL_RX1,
+	RX_MIX1_INP_SEL_RX2,
+	RX_MIX1_INP_SEL_RX3,
+	RX_MIX1_INP_SEL_RX4,
+	RX_MIX1_INP_SEL_RX5,
+	RX_MIX1_INP_SEL_RX6,
+	RX_MIX1_INP_SEL_RX7,
+	RX_MIX1_INP_SEL_AUXRX,
+};
 
-#define TAIKO_FAKE_INS_THRESHOLD_MS 2500
-#define TAIKO_FAKE_REMOVAL_MIN_PERIOD_MS 50
-
-#define TAIKO_MBHC_BUTTON_MIN 0x8000
-
-#define TAIKO_MBHC_FAKE_INSERT_LOW 10
-#define TAIKO_MBHC_FAKE_INSERT_HIGH 80
-#define TAIKO_MBHC_FAKE_INS_HIGH_NO_GPIO 150
-
-#define TAIKO_MBHC_STATUS_REL_DETECTION 0x0C
-
-#define TAIKO_MBHC_GPIO_REL_DEBOUNCE_TIME_MS 200
-
-#define TAIKO_MBHC_FAKE_INS_DELTA_MV 200
-#define TAIKO_MBHC_FAKE_INS_DELTA_SCALED_MV 300
-
-#define TAIKO_HS_DETECT_PLUG_TIME_MS (5 * 1000)
-#define TAIKO_HS_DETECT_PLUG_INERVAL_MS 100
-
-#define TAIKO_GPIO_IRQ_DEBOUNCE_TIME_US 5000
-
-#define TAIKO_MBHC_GND_MIC_SWAP_THRESHOLD 2
-
-#define TAIKO_ACQUIRE_LOCK(x) do { mutex_lock(&x); } while (0)
-#define TAIKO_RELEASE_LOCK(x) do { mutex_unlock(&x); } while (0)
+#define TAIKO_COMP_DIGITAL_GAIN_OFFSET 3
 
 static const DECLARE_TLV_DB_SCALE(digital_gain, 0, 1, 0);
 static const DECLARE_TLV_DB_SCALE(line_gain, 0, 7, 1);
@@ -115,21 +80,6 @@
 static struct snd_soc_dai_driver taiko_dai[];
 static const DECLARE_TLV_DB_SCALE(aux_pga_gain, 0, 2, 0);
 
-enum taiko_bandgap_type {
-	TAIKO_BANDGAP_OFF = 0,
-	TAIKO_BANDGAP_AUDIO_MODE,
-	TAIKO_BANDGAP_MBHC_MODE,
-};
-
-struct mbhc_micbias_regs {
-	u16 cfilt_val;
-	u16 cfilt_ctl;
-	u16 mbhc_reg;
-	u16 int_rbias;
-	u16 ctl_reg;
-	u8 cfilt_sel;
-};
-
 /* Codec supports 2 IIR filters */
 enum {
 	IIR1 = 0,
@@ -162,72 +112,12 @@
 	COMPANDER_FS_MAX,
 };
 
-/* Flags to track of PA and DAC state.
- * PA and DAC should be tracked separately as AUXPGA loopback requires
- * only PA to be turned on without DAC being on. */
-enum taiko_priv_ack_flags {
-	TAIKO_HPHL_PA_OFF_ACK = 0,
-	TAIKO_HPHR_PA_OFF_ACK,
-	TAIKO_HPHL_DAC_OFF_ACK,
-	TAIKO_HPHR_DAC_OFF_ACK
-};
-
-
 struct comp_sample_dependent_params {
 	u32 peak_det_timeout;
 	u32 rms_meter_div_fact;
 	u32 rms_meter_resamp_fact;
 };
 
-/* Data used by MBHC */
-struct mbhc_internal_cal_data {
-	u16 dce_z;
-	u16 dce_mb;
-	u16 sta_z;
-	u16 sta_mb;
-	u32 t_sta_dce;
-	u32 t_dce;
-	u32 t_sta;
-	u32 micb_mv;
-	u16 v_ins_hu;
-	u16 v_ins_h;
-	u16 v_b1_hu;
-	u16 v_b1_h;
-	u16 v_b1_huc;
-	u16 v_brh;
-	u16 v_brl;
-	u16 v_no_mic;
-	u8 npoll;
-	u8 nbounce_wait;
-	s16 adj_v_hs_max;
-	u16 adj_v_ins_hu;
-	u16 adj_v_ins_h;
-	s16 v_inval_ins_low;
-	s16 v_inval_ins_high;
-};
-
-struct taiko_reg_address {
-	u16 micb_4_ctl;
-	u16 micb_4_int_rbias;
-	u16 micb_4_mbhc;
-};
-
-enum taiko_mbhc_plug_type {
-	PLUG_TYPE_INVALID = -1,
-	PLUG_TYPE_NONE,
-	PLUG_TYPE_HEADSET,
-	PLUG_TYPE_HEADPHONE,
-	PLUG_TYPE_HIGH_HPH,
-	PLUG_TYPE_GND_MIC_SWAP,
-};
-
-enum taiko_mbhc_state {
-	MBHC_STATE_NONE = -1,
-	MBHC_STATE_POTENTIAL,
-	MBHC_STATE_POTENTIAL_RECOVERY,
-	MBHC_STATE_RELEASE,
-};
-
 struct hpf_work {
 	struct taiko_priv *taiko;
 	u32 decimator;
@@ -237,62 +127,65 @@
 
 static struct hpf_work tx_hpf_work[NUM_DECIMATORS];
 
+static const struct wcd9xxx_ch taiko_rx_chs[TAIKO_RX_MAX] = {
+	WCD9XXX_CH(16, 0),
+	WCD9XXX_CH(17, 1),
+	WCD9XXX_CH(18, 2),
+	WCD9XXX_CH(19, 3),
+	WCD9XXX_CH(20, 4),
+	WCD9XXX_CH(21, 5),
+	WCD9XXX_CH(22, 6),
+	WCD9XXX_CH(23, 7),
+	WCD9XXX_CH(24, 8),
+	WCD9XXX_CH(25, 9),
+	WCD9XXX_CH(26, 10),
+	WCD9XXX_CH(27, 11),
+	WCD9XXX_CH(28, 12),
+};
+
+static const struct wcd9xxx_ch taiko_tx_chs[TAIKO_TX_MAX] = {
+	WCD9XXX_CH(0, 0),
+	WCD9XXX_CH(1, 1),
+	WCD9XXX_CH(2, 2),
+	WCD9XXX_CH(3, 3),
+	WCD9XXX_CH(4, 4),
+	WCD9XXX_CH(5, 5),
+	WCD9XXX_CH(6, 6),
+	WCD9XXX_CH(7, 7),
+	WCD9XXX_CH(8, 8),
+	WCD9XXX_CH(9, 9),
+	WCD9XXX_CH(10, 10),
+	WCD9XXX_CH(11, 11),
+	WCD9XXX_CH(12, 12),
+	WCD9XXX_CH(13, 13),
+	WCD9XXX_CH(14, 14),
+	WCD9XXX_CH(15, 15),
+};
+
+static const u32 vport_check_table[NUM_CODEC_DAIS] = {
+	0,					/* AIF1_PB */
+	(1 << AIF2_CAP) | (1 << AIF3_CAP),	/* AIF1_CAP */
+	0,					/* AIF2_PB */
+	(1 << AIF1_CAP) | (1 << AIF3_CAP),	/* AIF2_CAP */
+	0,					/* AIF2_PB */
+	(1 << AIF1_CAP) | (1 << AIF2_CAP),	/* AIF2_CAP */
+};
+
 struct taiko_priv {
 	struct snd_soc_codec *codec;
-	struct taiko_reg_address reg_addr;
 	u32 adc_count;
-	u32 cfilt1_cnt;
-	u32 cfilt2_cnt;
-	u32 cfilt3_cnt;
 	u32 rx_bias_count;
 	s32 dmic_1_2_clk_cnt;
 	s32 dmic_3_4_clk_cnt;
 	s32 dmic_5_6_clk_cnt;
 
-	enum taiko_bandgap_type bandgap_type;
-	bool mclk_enabled;
-	bool clock_active;
-	bool config_mode_active;
-	bool mbhc_polling_active;
-	unsigned long mbhc_fake_ins_start;
-	int buttons_pressed;
-	enum taiko_mbhc_state mbhc_state;
-	struct taiko_mbhc_config mbhc_cfg;
-	struct mbhc_internal_cal_data mbhc_data;
-
-	struct wcd9xxx_pdata *pdata;
 	u32 anc_slot;
 
-	bool no_mic_headset_override;
-	/* Delayed work to report long button press */
-	struct delayed_work mbhc_btn_dwork;
-
-	struct mbhc_micbias_regs mbhc_bias_regs;
-	bool mbhc_micbias_switched;
-
-	/* track PA/DAC state */
-	unsigned long hph_pa_dac_state;
-
 	/*track taiko interface type*/
 	u8 intf_type;
 
-	u32 hph_status; /* track headhpone status */
-	/* define separate work for left and right headphone OCP to avoid
-	 * additional checking on which OCP event to report so no locking
-	 * to ensure synchronization is required
-	 */
-	struct work_struct hphlocp_work; /* reporting left hph ocp off */
-	struct work_struct hphrocp_work; /* reporting right hph ocp off */
-
-	u8 hphlocp_cnt; /* headphone left ocp retry */
-	u8 hphrocp_cnt; /* headphone right ocp retry */
-
-	/* Work to perform MBHC Firmware Read */
-	struct delayed_work mbhc_firmware_dwork;
-	const struct firmware *mbhc_fw;
-
 	/* num of slim ports required */
-	struct taiko_codec_dai_data dai[NUM_CODEC_DAIS];
+	struct wcd9xxx_codec_dai_data  dai[NUM_CODEC_DAIS];
 
 	/*compander*/
 	int comp_enabled[COMPANDER_MAX];
@@ -303,29 +196,12 @@
 	u8 aux_l_gain;
 	u8 aux_r_gain;
 
-	struct delayed_work mbhc_insert_dwork;
-	unsigned long mbhc_last_resume; /* in jiffies */
-
-	u8 current_plug;
-	struct work_struct hs_correct_plug_work;
-	bool hs_detect_work_stop;
-	bool hs_polling_irq_prepared;
-	bool lpi_enabled; /* low power insertion detection */
-	bool in_gpio_handler;
-	/* Currently, only used for mbhc purpose, to protect
-	 * concurrent execution of mbhc threaded irq handlers and
-	 * kill race between DAPM and MBHC.But can serve as a
-	 * general lock to protect codec resource
-	 */
-	struct mutex codec_resource_lock;
-
-#ifdef CONFIG_DEBUG_FS
-	struct dentry *debugfs_poke;
-	struct dentry *debugfs_mbhc;
-#endif
+	/* resmgr module */
+	struct wcd9xxx_resmgr resmgr;
+	/* mbhc module */
+	struct wcd9xxx_mbhc mbhc;
 };
 
-
 static const u32 comp_shift[] = {
 	0,
 	2,
@@ -421,6 +297,7 @@
 		snd_soc_update_bits(codec, TAIKO_A_BUCK_MODE_5, 0x02, 0x02);
 		snd_soc_update_bits(codec, TAIKO_A_BUCK_MODE_4, 0xFF, 0xFF);
 		snd_soc_update_bits(codec, TAIKO_A_BUCK_MODE_1, 0x04, 0x04);
+		snd_soc_update_bits(codec, TAIKO_A_BUCK_MODE_1, 0x04, 0x00);
 		snd_soc_update_bits(codec, TAIKO_A_BUCK_MODE_3, 0x04, 0x00);
 		snd_soc_update_bits(codec, TAIKO_A_BUCK_MODE_3, 0x08, 0x00);
 		snd_soc_update_bits(codec, TAIKO_A_BUCK_MODE_1, 0x80, 0x80);
@@ -433,12 +310,26 @@
 static int taiko_codec_enable_charge_pump(struct snd_soc_dapm_widget *w,
 		struct snd_kcontrol *kcontrol, int event)
 {
+	struct snd_soc_codec *codec = w->codec;
+
 	pr_debug("%s %s %d\n", __func__, w->name, event);
 
 	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		snd_soc_update_bits(codec, w->reg, 0x01, 0x01);
+		snd_soc_update_bits(codec, w->reg, 0x40, 0x00);
+		snd_soc_update_bits(codec, TAIKO_A_NCP_STATIC, 0x0f, 0x01);
+		break;
+
 	case SND_SOC_DAPM_POST_PMU:
 		usleep_range(1000, 1000);
 		break;
+
+	case SND_SOC_DAPM_PRE_PMD:
+	    snd_soc_update_bits(codec, w->reg, 0x01, 0x00);
+		snd_soc_update_bits(codec, w->reg, 0x40, 0x40);
+		snd_soc_update_bits(codec, TAIKO_A_NCP_STATIC, 0x0f, 0x08);
+		break;
 	}
 	return 0;
 }
@@ -778,6 +669,9 @@
 	struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
 	u32 rate = taiko->comp_fs[w->shift];
 
+	pr_debug("%s: %s event %d enabled = %d", __func__, w->name,
+		event, taiko->comp_enabled[w->shift]);
+
 	switch (event) {
 	case SND_SOC_DAPM_PRE_PMU:
 		if (taiko->comp_enabled[w->shift] != 0) {
@@ -1084,10 +978,15 @@
 	"ZERO", "SRC1", "SRC2", "IIR1", "IIR2"
 };
 
-static const char * const rx_dsm_text[] = {
-	"CIC_OUT", "DSM_INV"
+static const char * const rx_rdac5_text[] = {
+	"DEM4", "DEM3_INV"
 };
 
+static const char * const rx_rdac7_text[] = {
+	"DEM6", "DEM5_INV"
+};
+
+
 static const char * const sb_tx1_mux_text[] = {
 	"ZERO", "RMIX1", "RMIX2", "RMIX3", "RMIX4", "RMIX5", "RMIX6", "RMIX7",
 		"DEC1"
@@ -1241,11 +1140,11 @@
 static const struct soc_enum rx7_mix2_inp2_chain_enum =
 	SOC_ENUM_SINGLE(TAIKO_A_CDC_CONN_RX7_B3_CTL, 3, 5, rx_mix2_text);
 
-static const struct soc_enum rx4_dsm_enum =
-	SOC_ENUM_SINGLE(TAIKO_A_CDC_RX4_B6_CTL, 4, 2, rx_dsm_text);
+static const struct soc_enum rx_rdac5_enum =
+	SOC_ENUM_SINGLE(TAIKO_A_CDC_CONN_MISC, 2, 2, rx_rdac5_text);
 
-static const struct soc_enum rx6_dsm_enum =
-	SOC_ENUM_SINGLE(TAIKO_A_CDC_RX6_B6_CTL, 4, 2, rx_dsm_text);
+static const struct soc_enum rx_rdac7_enum =
+	SOC_ENUM_SINGLE(TAIKO_A_CDC_CONN_MISC, 1, 2, rx_rdac7_text);
 
 static const struct soc_enum sb_tx1_mux_enum =
 	SOC_ENUM_SINGLE(TAIKO_A_CDC_CONN_TX_SB_B1_CTL, 0, 9, sb_tx1_mux_text);
@@ -1386,11 +1285,11 @@
 static const struct snd_kcontrol_new rx7_mix2_inp2_mux =
 	SOC_DAPM_ENUM("RX7 MIX2 INP2 Mux", rx7_mix2_inp2_chain_enum);
 
-static const struct snd_kcontrol_new rx4_dsm_mux =
-	SOC_DAPM_ENUM("RX4 DSM MUX Mux", rx4_dsm_enum);
+static const struct snd_kcontrol_new rx_dac5_mux =
+	SOC_DAPM_ENUM("RDAC5 MUX Mux", rx_rdac5_enum);
 
-static const struct snd_kcontrol_new rx6_dsm_mux =
-	SOC_DAPM_ENUM("RX6 DSM MUX Mux", rx6_dsm_enum);
+static const struct snd_kcontrol_new rx_dac7_mux =
+	SOC_DAPM_ENUM("RDAC7 MUX Mux", rx_rdac7_enum);
 
 static const struct snd_kcontrol_new sb_tx1_mux =
 	SOC_DAPM_ENUM("SLIM TX1 MUX Mux", sb_tx1_mux_enum);
@@ -1601,6 +1500,222 @@
 static const struct snd_kcontrol_new lineout4_ground_switch =
 	SOC_DAPM_SINGLE("Switch", TAIKO_A_RX_LINE_4_DAC_CTL, 6, 1, 0);
 
+/* virtual port entries */
+static int slim_tx_mixer_get(struct snd_kcontrol *kcontrol,
+			     struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+	struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+
+	ucontrol->value.integer.value[0] = widget->value;
+	return 0;
+}
+
+static int slim_tx_mixer_put(struct snd_kcontrol *kcontrol,
+			     struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+	struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+	struct snd_soc_codec *codec = widget->codec;
+	struct taiko_priv *taiko_p = snd_soc_codec_get_drvdata(codec);
+	struct wcd9xxx *core = dev_get_drvdata(codec->dev->parent);
+	struct soc_multi_mixer_control *mixer =
+		((struct soc_multi_mixer_control *)kcontrol->private_value);
+	u32 dai_id = widget->shift;
+	u32 port_id = mixer->shift;
+	u32 enable = ucontrol->value.integer.value[0];
+
+
+	pr_debug("%s: wname %s cname %s value %u shift %d item %ld\n", __func__,
+		widget->name, ucontrol->id.name, widget->value, widget->shift,
+		ucontrol->value.integer.value[0]);
+
+	mutex_lock(&codec->mutex);
+
+	if (taiko_p->intf_type != WCD9XXX_INTERFACE_TYPE_SLIMBUS) {
+		if (dai_id != AIF1_CAP) {
+			dev_err(codec->dev, "%s: invalid AIF for I2C mode\n",
+				__func__);
+			mutex_unlock(&codec->mutex);
+			return -EINVAL;
+		}
+	}
+	switch (dai_id) {
+	case AIF1_CAP:
+	case AIF2_CAP:
+	case AIF3_CAP:
+		/* only add to the list if value not set
+		 */
+		if (enable && !(widget->value & 1 << port_id)) {
+			if (wcd9xxx_tx_vport_validation(
+						vport_check_table[dai_id],
+						port_id,
+						taiko_p->dai)) {
+				pr_info("%s: TX%u is used by other virtual port\n",
+					__func__, port_id + 1);
+				mutex_unlock(&codec->mutex);
+				return -EINVAL;
+			}
+			widget->value |= 1 << port_id;
+			list_add_tail(&core->tx_chs[port_id].list,
+				      &taiko_p->dai[dai_id].wcd9xxx_ch_list
+				      );
+		} else if (!enable && (widget->value & 1 << port_id)) {
+			widget->value &= ~(1 << port_id);
+			list_del_init(&core->tx_chs[port_id].list);
+		} else {
+			if (enable)
+				pr_info("%s: TX%u port is used by this virtual port\n",
+					__func__, port_id + 1);
+			else
+				pr_info("%s: TX%u port is not used by this virtual port\n",
+					__func__, port_id + 1);
+			/* avoid update power function */
+			mutex_unlock(&codec->mutex);
+			return 0;
+		}
+		break;
+	default:
+		pr_err("Unknown AIF %d\n", dai_id);
+		mutex_unlock(&codec->mutex);
+		return -EINVAL;
+	}
+	pr_debug("%s: name %s sname %s updated value %u shift %d\n", __func__,
+		widget->name, widget->sname, widget->value, widget->shift);
+
+	snd_soc_dapm_mixer_update_power(widget, kcontrol, enable);
+
+	mutex_unlock(&codec->mutex);
+	return 0;
+}
+
+static int slim_rx_mux_get(struct snd_kcontrol *kcontrol,
+			   struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+	struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+
+	ucontrol->value.enumerated.item[0] = widget->value;
+	return 0;
+}
+
+static const char *const slim_rx_mux_text[] = {
+	"ZERO", "AIF1_PB", "AIF2_PB", "AIF3_PB"
+};
+
+static int slim_rx_mux_put(struct snd_kcontrol *kcontrol,
+			   struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+	struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+	struct snd_soc_codec *codec = widget->codec;
+	struct taiko_priv *taiko_p = snd_soc_codec_get_drvdata(codec);
+	struct wcd9xxx *core = dev_get_drvdata(codec->dev->parent);
+	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+	u32 port_id = widget->shift;
+
+	pr_debug("%s: wname %s cname %s value %u shift %d item %ld\n", __func__,
+		widget->name, ucontrol->id.name, widget->value, widget->shift,
+		ucontrol->value.integer.value[0]);
+
+	widget->value = ucontrol->value.enumerated.item[0];
+
+	mutex_lock(&codec->mutex);
+
+	if (taiko_p->intf_type != WCD9XXX_INTERFACE_TYPE_SLIMBUS) {
+		if (widget->value > 1) {
+			dev_err(codec->dev, "%s: invalid AIF for I2C mode\n",
+				__func__);
+			goto err;
+		}
+	}
+	/* value need to match the Virtual port and AIF number
+	 */
+	switch (widget->value) {
+	case 0:
+		list_del_init(&core->rx_chs[port_id].list);
+	break;
+	case 1:
+		if (wcd9xxx_rx_vport_validation(port_id + core->num_tx_port,
+			&taiko_p->dai[AIF1_PB].wcd9xxx_ch_list))
+			goto pr_err;
+		list_add_tail(&core->rx_chs[port_id].list,
+			      &taiko_p->dai[AIF1_PB].wcd9xxx_ch_list);
+	break;
+	case 2:
+		if (wcd9xxx_rx_vport_validation(port_id + core->num_tx_port,
+			&taiko_p->dai[AIF1_PB].wcd9xxx_ch_list))
+			goto pr_err;
+		list_add_tail(&core->rx_chs[port_id].list,
+			      &taiko_p->dai[AIF2_PB].wcd9xxx_ch_list);
+	break;
+	case 3:
+		if (wcd9xxx_rx_vport_validation(port_id + core->num_tx_port,
+			&taiko_p->dai[AIF1_PB].wcd9xxx_ch_list))
+			goto pr_err;
+		list_add_tail(&core->rx_chs[port_id].list,
+			      &taiko_p->dai[AIF3_PB].wcd9xxx_ch_list);
+	break;
+	default:
+		pr_err("Unknown AIF %d\n", widget->value);
+		goto err;
+	}
+
+	snd_soc_dapm_mux_update_power(widget, kcontrol, 1, widget->value, e);
+
+	mutex_unlock(&codec->mutex);
+	return 0;
+pr_err:
+	pr_err("%s: RX%u is used by current requesting AIF_PB itself\n",
+		__func__, port_id + 1);
+err:
+	mutex_unlock(&codec->mutex);
+	return -EINVAL;
+}
+
+static const struct soc_enum slim_rx_mux_enum =
+	SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(slim_rx_mux_text), slim_rx_mux_text);
+
+static const struct snd_kcontrol_new slim_rx_mux[TAIKO_RX_MAX] = {
+	SOC_DAPM_ENUM_EXT("SLIM RX1 Mux", slim_rx_mux_enum,
+			  slim_rx_mux_get, slim_rx_mux_put),
+	SOC_DAPM_ENUM_EXT("SLIM RX2 Mux", slim_rx_mux_enum,
+			  slim_rx_mux_get, slim_rx_mux_put),
+	SOC_DAPM_ENUM_EXT("SLIM RX3 Mux", slim_rx_mux_enum,
+			  slim_rx_mux_get, slim_rx_mux_put),
+	SOC_DAPM_ENUM_EXT("SLIM RX4 Mux", slim_rx_mux_enum,
+			  slim_rx_mux_get, slim_rx_mux_put),
+	SOC_DAPM_ENUM_EXT("SLIM RX5 Mux", slim_rx_mux_enum,
+			  slim_rx_mux_get, slim_rx_mux_put),
+	SOC_DAPM_ENUM_EXT("SLIM RX6 Mux", slim_rx_mux_enum,
+			  slim_rx_mux_get, slim_rx_mux_put),
+	SOC_DAPM_ENUM_EXT("SLIM RX7 Mux", slim_rx_mux_enum,
+			  slim_rx_mux_get, slim_rx_mux_put),
+};
+
+static const struct snd_kcontrol_new aif_cap_mixer[] = {
+	SOC_SINGLE_EXT("SLIM TX1", SND_SOC_NOPM, TAIKO_TX1, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX2", SND_SOC_NOPM, TAIKO_TX2, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX3", SND_SOC_NOPM, TAIKO_TX3, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX4", SND_SOC_NOPM, TAIKO_TX4, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX5", SND_SOC_NOPM, TAIKO_TX5, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX6", SND_SOC_NOPM, TAIKO_TX6, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX7", SND_SOC_NOPM, TAIKO_TX7, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX8", SND_SOC_NOPM, TAIKO_TX8, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX9", SND_SOC_NOPM, TAIKO_TX9, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX10", SND_SOC_NOPM, TAIKO_TX10, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+};
+
 static void taiko_codec_enable_adc_block(struct snd_soc_codec *codec,
 					 int enable)
 {
@@ -1667,173 +1782,6 @@
 	return 0;
 }
 
-static void taiko_codec_enable_audio_mode_bandgap(struct snd_soc_codec *codec)
-{
-	snd_soc_update_bits(codec, TAIKO_A_BIAS_CENTRAL_BG_CTL, 0x80,
-		0x80);
-	snd_soc_update_bits(codec, TAIKO_A_BIAS_CENTRAL_BG_CTL, 0x04,
-		0x04);
-	snd_soc_update_bits(codec, TAIKO_A_BIAS_CENTRAL_BG_CTL, 0x01,
-		0x01);
-	usleep_range(1000, 1000);
-	snd_soc_update_bits(codec, TAIKO_A_BIAS_CENTRAL_BG_CTL, 0x80,
-		0x00);
-}
-
-static void taiko_codec_enable_bandgap(struct snd_soc_codec *codec,
-	enum taiko_bandgap_type choice)
-{
-	struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
-
-	/* TODO lock resources accessed by audio streams and threaded
-	 * interrupt handlers
-	 */
-
-	pr_debug("%s, choice is %d, current is %d\n", __func__, choice,
-		taiko->bandgap_type);
-
-	if (taiko->bandgap_type == choice)
-		return;
-
-	if ((taiko->bandgap_type == TAIKO_BANDGAP_OFF) &&
-		(choice == TAIKO_BANDGAP_AUDIO_MODE)) {
-		taiko_codec_enable_audio_mode_bandgap(codec);
-	} else if (choice == TAIKO_BANDGAP_MBHC_MODE) {
-		/* bandgap mode becomes fast,
-		 * mclk should be off or clk buff source souldn't be VBG
-		 * Let's turn off mclk always */
-		WARN_ON(snd_soc_read(codec, TAIKO_A_CLK_BUFF_EN2) & (1 << 2));
-		snd_soc_update_bits(codec, TAIKO_A_BIAS_CENTRAL_BG_CTL, 0x2,
-			0x2);
-		snd_soc_update_bits(codec, TAIKO_A_BIAS_CENTRAL_BG_CTL, 0x80,
-			0x80);
-		snd_soc_update_bits(codec, TAIKO_A_BIAS_CENTRAL_BG_CTL, 0x4,
-			0x4);
-		snd_soc_update_bits(codec, TAIKO_A_BIAS_CENTRAL_BG_CTL, 0x01,
-			0x01);
-		usleep_range(1000, 1000);
-		snd_soc_update_bits(codec, TAIKO_A_BIAS_CENTRAL_BG_CTL, 0x80,
-			0x00);
-	} else if ((taiko->bandgap_type == TAIKO_BANDGAP_MBHC_MODE) &&
-		(choice == TAIKO_BANDGAP_AUDIO_MODE)) {
-		snd_soc_write(codec, TAIKO_A_BIAS_CENTRAL_BG_CTL, 0x00);
-		usleep_range(100, 100);
-		taiko_codec_enable_audio_mode_bandgap(codec);
-	} else if (choice == TAIKO_BANDGAP_OFF) {
-		snd_soc_write(codec, TAIKO_A_BIAS_CENTRAL_BG_CTL, 0x50);
-	} else {
-		pr_err("%s: Error, Invalid bandgap settings\n", __func__);
-	}
-	taiko->bandgap_type = choice;
-}
-
-static void taiko_codec_disable_clock_block(struct snd_soc_codec *codec)
-{
-	struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
-	pr_debug("%s\n", __func__);
-	snd_soc_update_bits(codec, TAIKO_A_CLK_BUFF_EN2, 0x04, 0x00);
-	usleep_range(50, 50);
-	snd_soc_update_bits(codec, TAIKO_A_CLK_BUFF_EN2, 0x02, 0x02);
-	snd_soc_update_bits(codec, TAIKO_A_CLK_BUFF_EN1, 0x01, 0x00);
-	usleep_range(50, 50);
-	taiko->clock_active = false;
-}
-
-static int taiko_codec_mclk_index(const struct taiko_priv *taiko)
-{
-	if (taiko->mbhc_cfg.mclk_rate == TAIKO_MCLK_RATE_12288KHZ)
-		return 0;
-	else if (taiko->mbhc_cfg.mclk_rate == TAIKO_MCLK_RATE_9600KHZ)
-		return 1;
-	else {
-		BUG_ON(1);
-		return -EINVAL;
-	}
-}
-
-static void taiko_enable_rx_bias(struct snd_soc_codec *codec, u32  enable)
-{
-	struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
-
-	if (enable) {
-		taiko->rx_bias_count++;
-		if (taiko->rx_bias_count == 1)
-			snd_soc_update_bits(codec, TAIKO_A_RX_COM_BIAS,
-				0x80, 0x80);
-	} else {
-		taiko->rx_bias_count--;
-		if (!taiko->rx_bias_count)
-			snd_soc_update_bits(codec, TAIKO_A_RX_COM_BIAS,
-				0x80, 0x00);
-	}
-}
-
-static int taiko_codec_enable_config_mode(struct snd_soc_codec *codec,
-	int enable)
-{
-	struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
-
-	pr_debug("%s: enable = %d\n", __func__, enable);
-	if (enable) {
-
-		snd_soc_update_bits(codec, TAIKO_A_RC_OSC_FREQ, 0x10, 0);
-		/* bandgap mode to fast */
-		snd_soc_write(codec, TAIKO_A_BIAS_OSC_BG_CTL, 0x17);
-		usleep_range(5, 5);
-		snd_soc_update_bits(codec, TAIKO_A_RC_OSC_FREQ, 0x80,
-				    0x80);
-		snd_soc_update_bits(codec, TAIKO_A_RC_OSC_TEST, 0x80,
-				    0x80);
-		usleep_range(10, 10);
-		snd_soc_update_bits(codec, TAIKO_A_RC_OSC_TEST, 0x80, 0);
-		usleep_range(10000, 10000);
-		snd_soc_update_bits(codec, TAIKO_A_CLK_BUFF_EN1, 0x08, 0x08);
-
-	} else {
-		snd_soc_update_bits(codec, TAIKO_A_BIAS_OSC_BG_CTL, 0x1,
-				    0);
-		snd_soc_update_bits(codec, TAIKO_A_RC_OSC_FREQ, 0x80, 0);
-		/* clk source to ext clk and clk buff ref to VBG */
-		snd_soc_update_bits(codec, TAIKO_A_CLK_BUFF_EN1, 0x0C, 0x04);
-	}
-	taiko->config_mode_active = enable ? true : false;
-
-	return 0;
-}
-
-static int taiko_codec_enable_clock_block(struct snd_soc_codec *codec,
-					  int config_mode)
-{
-	struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
-
-	pr_debug("%s: config_mode = %d\n", __func__, config_mode);
-
-	/* transit to RCO requires mclk off */
-	WARN_ON(snd_soc_read(codec, TAIKO_A_CLK_BUFF_EN2) & (1 << 2));
-	if (config_mode) {
-		/* enable RCO and switch to it */
-		taiko_codec_enable_config_mode(codec, 1);
-		snd_soc_write(codec, TAIKO_A_CLK_BUFF_EN2, 0x02);
-		usleep_range(1000, 1000);
-	} else {
-		/* switch to MCLK */
-		snd_soc_update_bits(codec, TAIKO_A_CLK_BUFF_EN1, 0x08, 0x00);
-
-		if (taiko->mbhc_polling_active)
-			snd_soc_write(codec, TAIKO_A_CLK_BUFF_EN2, 0x02);
-		taiko_codec_enable_config_mode(codec, 0);
-	}
-
-	snd_soc_update_bits(codec, TAIKO_A_CLK_BUFF_EN1, 0x01, 0x01);
-	snd_soc_update_bits(codec, TAIKO_A_CLK_BUFF_EN2, 0x02, 0x00);
-	/* on MCLK */
-	snd_soc_update_bits(codec, TAIKO_A_CLK_BUFF_EN2, 0x04, 0x04);
-	snd_soc_update_bits(codec, TAIKO_A_CDC_CLK_MCLK_CTL, 0x01, 0x01);
-	usleep_range(50, 50);
-	taiko->clock_active = true;
-	return 0;
-}
-
 static int taiko_codec_enable_aux_pga(struct snd_soc_dapm_widget *w,
 	struct snd_kcontrol *kcontrol, int event)
 {
@@ -1844,32 +1792,22 @@
 
 	switch (event) {
 	case SND_SOC_DAPM_PRE_PMU:
-		taiko_codec_enable_bandgap(codec, TAIKO_BANDGAP_AUDIO_MODE);
-		taiko_enable_rx_bias(codec, 1);
-
-		if (taiko->aux_pga_cnt++ == 1
-			&& !taiko->mclk_enabled) {
-			taiko_codec_enable_clock_block(codec, 1);
-			pr_debug("AUX PGA enabled RC osc\n");
-		}
+		WCD9XXX_BCL_LOCK(&taiko->resmgr);
+		wcd9xxx_resmgr_get_bandgap(&taiko->resmgr,
+					   WCD9XXX_BANDGAP_AUDIO_MODE);
+		/* AUX PGA requires RCO or MCLK */
+		wcd9xxx_resmgr_get_clk_block(&taiko->resmgr, WCD9XXX_CLK_RCO);
+		wcd9xxx_resmgr_enable_rx_bias(&taiko->resmgr, 1);
+		WCD9XXX_BCL_UNLOCK(&taiko->resmgr);
 		break;
 
 	case SND_SOC_DAPM_POST_PMD:
-		taiko_enable_rx_bias(codec, 0);
-
-		if (taiko->aux_pga_cnt-- == 0) {
-			if (taiko->mbhc_polling_active)
-				taiko_codec_enable_bandgap(codec,
-					TAIKO_BANDGAP_MBHC_MODE);
-			else
-				taiko_codec_enable_bandgap(codec,
-					TAIKO_BANDGAP_OFF);
-
-			if (!taiko->mclk_enabled &&
-				!taiko->mbhc_polling_active) {
-				taiko_codec_enable_clock_block(codec, 0);
-			}
-		}
+		WCD9XXX_BCL_LOCK(&taiko->resmgr);
+		wcd9xxx_resmgr_enable_rx_bias(&taiko->resmgr, 0);
+		wcd9xxx_resmgr_put_bandgap(&taiko->resmgr,
+					   WCD9XXX_BANDGAP_AUDIO_MODE);
+		wcd9xxx_resmgr_put_clk_block(&taiko->resmgr, WCD9XXX_CLK_RCO);
+		WCD9XXX_BCL_UNLOCK(&taiko->resmgr);
 		break;
 	}
 	return 0;
@@ -2014,7 +1952,7 @@
 	int anc_size_remaining;
 	u32 *anc_ptr;
 	u16 reg;
-	u8 mask, val, old_val;
+	u8 mask, val;
 
 	pr_debug("%s %d\n", __func__, event);
 	switch (event) {
@@ -2081,9 +2019,7 @@
 		for (i = 0; i < anc_writes_size; i++) {
 			TAIKO_CODEC_UNPACK_ENTRY(anc_ptr[i], reg,
 				mask, val);
-			old_val = snd_soc_read(codec, reg);
-			snd_soc_write(codec, reg, (old_val & ~mask) |
-				(val & mask));
+			snd_soc_write(codec, reg, val);
 		}
 		release_firmware(fw);
 
@@ -2096,358 +2032,47 @@
 	return 0;
 }
 
-/* called under codec_resource_lock acquisition */
-static void taiko_codec_start_hs_polling(struct snd_soc_codec *codec)
-{
-	struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
-	int mbhc_state = taiko->mbhc_state;
-
-	pr_debug("%s: enter\n", __func__);
-	if (!taiko->mbhc_polling_active) {
-		pr_debug("Polling is not active, do not start polling\n");
-		return;
-	}
-	snd_soc_write(codec, TAIKO_A_MBHC_SCALING_MUX_1, 0x84);
-
-	if (!taiko->no_mic_headset_override) {
-		if (mbhc_state == MBHC_STATE_POTENTIAL) {
-			pr_debug("%s recovering MBHC state macine\n", __func__);
-			taiko->mbhc_state = MBHC_STATE_POTENTIAL_RECOVERY;
-			/* set to max button press threshold */
-			snd_soc_write(codec, TAIKO_A_CDC_MBHC_VOLT_B2_CTL,
-				      0x7F);
-			snd_soc_write(codec, TAIKO_A_CDC_MBHC_VOLT_B1_CTL,
-				      0xFF);
-			snd_soc_write(codec, TAIKO_A_CDC_MBHC_VOLT_B4_CTL,
-				       0x7F);
-			snd_soc_write(codec, TAIKO_A_CDC_MBHC_VOLT_B3_CTL,
-				      0xFF);
-			/* set to max */
-			snd_soc_write(codec, TAIKO_A_CDC_MBHC_VOLT_B6_CTL,
-				      0x7F);
-			snd_soc_write(codec, TAIKO_A_CDC_MBHC_VOLT_B5_CTL,
-				      0xFF);
-		}
-	}
-
-	snd_soc_write(codec, TAIKO_A_CDC_MBHC_EN_CTL, 0x1);
-	snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_CLK_CTL, 0x8, 0x0);
-	snd_soc_write(codec, TAIKO_A_CDC_MBHC_EN_CTL, 0x1);
-	pr_debug("%s: leave\n", __func__);
-}
-
-/* called under codec_resource_lock acquisition */
-static void taiko_codec_pause_hs_polling(struct snd_soc_codec *codec)
-{
-	struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
-
-	pr_debug("%s: enter\n", __func__);
-	if (!taiko->mbhc_polling_active) {
-		pr_debug("polling not active, nothing to pause\n");
-		return;
-	}
-
-	snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_CLK_CTL, 0x8, 0x8);
-	pr_debug("%s: leave\n", __func__);
-}
-
-static void taiko_codec_switch_cfilt_mode(struct snd_soc_codec *codec, int mode)
-{
-	struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
-	u8 reg_mode_val, cur_mode_val;
-	bool mbhc_was_polling = false;
-
-	if (mode)
-		reg_mode_val = TAIKO_CFILT_FAST_MODE;
-	else
-		reg_mode_val = TAIKO_CFILT_SLOW_MODE;
-
-	cur_mode_val = snd_soc_read(codec,
-					taiko->mbhc_bias_regs.cfilt_ctl) & 0x40;
-
-	if (cur_mode_val != reg_mode_val) {
-		TAIKO_ACQUIRE_LOCK(taiko->codec_resource_lock);
-		if (taiko->mbhc_polling_active) {
-			taiko_codec_pause_hs_polling(codec);
-			mbhc_was_polling = true;
-		}
-		snd_soc_update_bits(codec,
-			taiko->mbhc_bias_regs.cfilt_ctl, 0x40, reg_mode_val);
-		if (mbhc_was_polling)
-			taiko_codec_start_hs_polling(codec);
-		TAIKO_RELEASE_LOCK(taiko->codec_resource_lock);
-		pr_debug("%s: CFILT mode change (%x to %x)\n", __func__,
-			cur_mode_val, reg_mode_val);
-	} else {
-		pr_debug("%s: CFILT Value is already %x\n",
-			__func__, cur_mode_val);
-	}
-}
-
-static void taiko_codec_update_cfilt_usage(struct snd_soc_codec *codec,
-					   u8 cfilt_sel, int inc)
-{
-	struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
-	u32 *cfilt_cnt_ptr = NULL;
-	u16 micb_cfilt_reg;
-
-	switch (cfilt_sel) {
-	case TAIKO_CFILT1_SEL:
-		cfilt_cnt_ptr = &taiko->cfilt1_cnt;
-		micb_cfilt_reg = TAIKO_A_MICB_CFILT_1_CTL;
-		break;
-	case TAIKO_CFILT2_SEL:
-		cfilt_cnt_ptr = &taiko->cfilt2_cnt;
-		micb_cfilt_reg = TAIKO_A_MICB_CFILT_2_CTL;
-		break;
-	case TAIKO_CFILT3_SEL:
-		cfilt_cnt_ptr = &taiko->cfilt3_cnt;
-		micb_cfilt_reg = TAIKO_A_MICB_CFILT_3_CTL;
-		break;
-	default:
-		return; /* should not happen */
-	}
-
-	if (inc) {
-		if (!(*cfilt_cnt_ptr)++) {
-			/* Switch CFILT to slow mode if MBHC CFILT being used */
-			if (cfilt_sel == taiko->mbhc_bias_regs.cfilt_sel)
-				taiko_codec_switch_cfilt_mode(codec, 0);
-
-			snd_soc_update_bits(codec, micb_cfilt_reg, 0x80, 0x80);
-		}
-	} else {
-		/* check if count not zero, decrement
-		 * then check if zero, go ahead disable cfilter
-		 */
-		if ((*cfilt_cnt_ptr) && !--(*cfilt_cnt_ptr)) {
-			snd_soc_update_bits(codec, micb_cfilt_reg, 0x80, 0);
-
-			/* Switch CFILT to fast mode if MBHC CFILT being used */
-			if (cfilt_sel == taiko->mbhc_bias_regs.cfilt_sel)
-				taiko_codec_switch_cfilt_mode(codec, 1);
-		}
-	}
-}
-
-static int taiko_find_k_value(unsigned int ldoh_v, unsigned int cfilt_mv)
-{
-	int rc = -EINVAL;
-	unsigned min_mv, max_mv;
-
-	switch (ldoh_v) {
-	case TAIKO_LDOH_1P95_V:
-		min_mv = 160;
-		max_mv = 1800;
-		break;
-	case TAIKO_LDOH_2P35_V:
-		min_mv = 200;
-		max_mv = 2200;
-		break;
-	case TAIKO_LDOH_2P75_V:
-		min_mv = 240;
-		max_mv = 2600;
-		break;
-	case TAIKO_LDOH_2P85_V:
-		min_mv = 250;
-		max_mv = 2700;
-		break;
-	default:
-		goto done;
-	}
-
-	if (cfilt_mv < min_mv || cfilt_mv > max_mv)
-		goto done;
-
-	for (rc = 4; rc <= 44; rc++) {
-		min_mv = max_mv * (rc) / 44;
-		if (min_mv >= cfilt_mv) {
-			rc -= 4;
-			break;
-		}
-	}
-done:
-	return rc;
-}
-
-static bool taiko_is_hph_pa_on(struct snd_soc_codec *codec)
-{
-	u8 hph_reg_val = 0;
-	hph_reg_val = snd_soc_read(codec, TAIKO_A_RX_HPH_CNP_EN);
-
-	return (hph_reg_val & 0x30) ? true : false;
-}
-
-static bool taiko_is_hph_dac_on(struct snd_soc_codec *codec, int left)
-{
-	u8 hph_reg_val = 0;
-	if (left)
-		hph_reg_val = snd_soc_read(codec,
-					   TAIKO_A_RX_HPH_L_DAC_CTL);
-	else
-		hph_reg_val = snd_soc_read(codec,
-					   TAIKO_A_RX_HPH_R_DAC_CTL);
-
-	return (hph_reg_val & 0xC0) ? true : false;
-}
-
-static void taiko_turn_onoff_override(struct snd_soc_codec *codec, bool on)
-{
-	snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_B1_CTL, 0x04, on << 2);
-}
-
-/* called under codec_resource_lock acquisition */
-static void taiko_codec_drive_v_to_micbias(struct snd_soc_codec *codec,
-					   int usec)
-{
-	int cfilt_k_val;
-	bool set = true;
-	struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
-
-	if (taiko->mbhc_data.micb_mv != VDDIO_MICBIAS_MV &&
-	    taiko->mbhc_micbias_switched) {
-		pr_debug("%s: set mic V to micbias V\n", __func__);
-		snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_CLK_CTL, 0x2, 0x2);
-		taiko_turn_onoff_override(codec, true);
-		while (1) {
-			cfilt_k_val = taiko_find_k_value(
-						taiko->pdata->micbias.ldoh_v,
-						set ? taiko->mbhc_data.micb_mv :
-						      VDDIO_MICBIAS_MV);
-			snd_soc_update_bits(codec,
-					    taiko->mbhc_bias_regs.cfilt_val,
-					    0xFC, (cfilt_k_val << 2));
-			if (!set)
-				break;
-			usleep_range(usec, usec);
-			set = false;
-		}
-		taiko_turn_onoff_override(codec, false);
-	}
-}
-
-/* called under codec_resource_lock acquisition */
-static void __taiko_codec_switch_micbias(struct snd_soc_codec *codec,
-					 int vddio_switch, bool restartpolling,
-					 bool checkpolling)
-{
-	int cfilt_k_val;
-	bool override;
-	struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
-
-	if (vddio_switch && !taiko->mbhc_micbias_switched &&
-	    (!checkpolling || taiko->mbhc_polling_active)) {
-		if (restartpolling)
-			taiko_codec_pause_hs_polling(codec);
-		override = snd_soc_read(codec, TAIKO_A_CDC_MBHC_B1_CTL) & 0x04;
-		if (!override)
-			taiko_turn_onoff_override(codec, true);
-		/* Adjust threshold if Mic Bias voltage changes */
-		if (taiko->mbhc_data.micb_mv != VDDIO_MICBIAS_MV) {
-			cfilt_k_val = taiko_find_k_value(
-						   taiko->pdata->micbias.ldoh_v,
-						   VDDIO_MICBIAS_MV);
-			usleep_range(10000, 10000);
-			snd_soc_update_bits(codec,
-					    taiko->mbhc_bias_regs.cfilt_val,
-					    0xFC, (cfilt_k_val << 2));
-			usleep_range(10000, 10000);
-			snd_soc_write(codec, TAIKO_A_CDC_MBHC_VOLT_B1_CTL,
-				      taiko->mbhc_data.adj_v_ins_hu & 0xFF);
-			snd_soc_write(codec, TAIKO_A_CDC_MBHC_VOLT_B2_CTL,
-				      (taiko->mbhc_data.adj_v_ins_hu >> 8) &
-				       0xFF);
-			pr_debug("%s: Programmed MBHC thresholds to VDDIO\n",
-				 __func__);
-		}
-
-		/* enable MIC BIAS Switch to VDDIO */
-		snd_soc_update_bits(codec, taiko->mbhc_bias_regs.mbhc_reg,
-				    0x80, 0x80);
-		snd_soc_update_bits(codec, taiko->mbhc_bias_regs.mbhc_reg,
-				    0x10, 0x00);
-		if (!override)
-			taiko_turn_onoff_override(codec, false);
-		if (restartpolling)
-			taiko_codec_start_hs_polling(codec);
-
-		taiko->mbhc_micbias_switched = true;
-		pr_debug("%s: VDDIO switch enabled\n", __func__);
-	} else if (!vddio_switch && taiko->mbhc_micbias_switched) {
-		if ((!checkpolling || taiko->mbhc_polling_active) &&
-		    restartpolling)
-			taiko_codec_pause_hs_polling(codec);
-		/* Reprogram thresholds */
-		if (taiko->mbhc_data.micb_mv != VDDIO_MICBIAS_MV) {
-			cfilt_k_val = taiko_find_k_value(
-						   taiko->pdata->micbias.ldoh_v,
-						   taiko->mbhc_data.micb_mv);
-			snd_soc_update_bits(codec,
-					    taiko->mbhc_bias_regs.cfilt_val,
-					    0xFC, (cfilt_k_val << 2));
-			usleep_range(10000, 10000);
-			snd_soc_write(codec, TAIKO_A_CDC_MBHC_VOLT_B1_CTL,
-				      taiko->mbhc_data.v_ins_hu & 0xFF);
-			snd_soc_write(codec, TAIKO_A_CDC_MBHC_VOLT_B2_CTL,
-				      (taiko->mbhc_data.v_ins_hu >> 8) & 0xFF);
-			pr_debug("%s: Programmed MBHC thresholds to MICBIAS\n",
-				 __func__);
-		}
-
-		/* Disable MIC BIAS Switch to VDDIO */
-		snd_soc_update_bits(codec, taiko->mbhc_bias_regs.mbhc_reg,
-				    0x80, 0x00);
-		snd_soc_update_bits(codec, taiko->mbhc_bias_regs.mbhc_reg,
-				    0x10, 0x00);
-
-		if ((!checkpolling || taiko->mbhc_polling_active) &&
-		    restartpolling)
-			taiko_codec_start_hs_polling(codec);
-
-		taiko->mbhc_micbias_switched = false;
-		pr_debug("%s: VDDIO switch disabled\n", __func__);
-	}
-}
-
-static void taiko_codec_switch_micbias(struct snd_soc_codec *codec,
-				       int vddio_switch)
-{
-	return __taiko_codec_switch_micbias(codec, vddio_switch, true, true);
-}
-
 static int taiko_codec_enable_micbias(struct snd_soc_dapm_widget *w,
 	struct snd_kcontrol *kcontrol, int event)
 {
 	struct snd_soc_codec *codec = w->codec;
 	struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
 	u16 micb_int_reg;
-	int micb_line;
 	u8 cfilt_sel_val = 0;
 	char *internal1_text = "Internal1";
 	char *internal2_text = "Internal2";
 	char *internal3_text = "Internal3";
+	enum wcd9xxx_notify_event e_post_off, e_pre_on, e_post_on;
 
 	pr_debug("%s %d\n", __func__, event);
 	switch (w->reg) {
 	case TAIKO_A_MICB_1_CTL:
 		micb_int_reg = TAIKO_A_MICB_1_INT_RBIAS;
-		cfilt_sel_val = taiko->pdata->micbias.bias1_cfilt_sel;
-		micb_line = TAIKO_MICBIAS1;
+		cfilt_sel_val = taiko->resmgr.pdata->micbias.bias1_cfilt_sel;
+		e_pre_on = WCD9XXX_EVENT_PRE_MICBIAS_1_ON;
+		e_post_on = WCD9XXX_EVENT_POST_MICBIAS_1_ON;
+		e_post_off = WCD9XXX_EVENT_POST_MICBIAS_1_OFF;
 		break;
 	case TAIKO_A_MICB_2_CTL:
 		micb_int_reg = TAIKO_A_MICB_2_INT_RBIAS;
-		cfilt_sel_val = taiko->pdata->micbias.bias2_cfilt_sel;
-		micb_line = TAIKO_MICBIAS2;
+		cfilt_sel_val = taiko->resmgr.pdata->micbias.bias2_cfilt_sel;
+		e_pre_on = WCD9XXX_EVENT_PRE_MICBIAS_2_ON;
+		e_post_on = WCD9XXX_EVENT_POST_MICBIAS_2_ON;
+		e_post_off = WCD9XXX_EVENT_POST_MICBIAS_2_OFF;
 		break;
 	case TAIKO_A_MICB_3_CTL:
 		micb_int_reg = TAIKO_A_MICB_3_INT_RBIAS;
-		cfilt_sel_val = taiko->pdata->micbias.bias3_cfilt_sel;
-		micb_line = TAIKO_MICBIAS3;
+		cfilt_sel_val = taiko->resmgr.pdata->micbias.bias3_cfilt_sel;
+		e_pre_on = WCD9XXX_EVENT_PRE_MICBIAS_3_ON;
+		e_post_on = WCD9XXX_EVENT_POST_MICBIAS_3_ON;
+		e_post_off = WCD9XXX_EVENT_POST_MICBIAS_3_OFF;
 		break;
 	case TAIKO_A_MICB_4_CTL:
-		micb_int_reg = taiko->reg_addr.micb_4_int_rbias;
-		cfilt_sel_val = taiko->pdata->micbias.bias4_cfilt_sel;
-		micb_line = TAIKO_MICBIAS4;
+		micb_int_reg = taiko->resmgr.reg_addr->micb_4_int_rbias;
+		cfilt_sel_val = taiko->resmgr.pdata->micbias.bias4_cfilt_sel;
+		e_pre_on = WCD9XXX_EVENT_PRE_MICBIAS_4_ON;
+		e_post_on = WCD9XXX_EVENT_POST_MICBIAS_4_ON;
+		e_post_off = WCD9XXX_EVENT_POST_MICBIAS_4_OFF;
 		break;
 	default:
 		pr_err("%s: Error, invalid micbias register\n", __func__);
@@ -2456,15 +2081,12 @@
 
 	switch (event) {
 	case SND_SOC_DAPM_PRE_PMU:
-		/* Decide whether to switch the micbias for MBHC */
-		if (w->reg == taiko->mbhc_bias_regs.ctl_reg) {
-			TAIKO_ACQUIRE_LOCK(taiko->codec_resource_lock);
-			taiko_codec_switch_micbias(codec, 0);
-			TAIKO_RELEASE_LOCK(taiko->codec_resource_lock);
-		}
+		/* Let MBHC module know so micbias switch to be off */
+		wcd9xxx_resmgr_notifier_call(&taiko->resmgr, e_pre_on);
 
 		snd_soc_update_bits(codec, w->reg, 0x0E, 0x0A);
-		taiko_codec_update_cfilt_usage(codec, cfilt_sel_val, 1);
+		/* Get cfilt */
+		wcd9xxx_resmgr_cfilt_get(&taiko->resmgr, cfilt_sel_val);
 
 		if (strnstr(w->name, internal1_text, 30))
 			snd_soc_update_bits(codec, micb_int_reg, 0xE0, 0xE0);
@@ -2475,25 +2097,13 @@
 
 		break;
 	case SND_SOC_DAPM_POST_PMU:
-
 		usleep_range(20000, 20000);
-
-		if (taiko->mbhc_polling_active &&
-		    taiko->mbhc_cfg.micbias == micb_line) {
-			TAIKO_ACQUIRE_LOCK(taiko->codec_resource_lock);
-			taiko_codec_pause_hs_polling(codec);
-			taiko_codec_start_hs_polling(codec);
-			TAIKO_RELEASE_LOCK(taiko->codec_resource_lock);
-		}
+		/* Let MBHC module know so micbias is on */
+		wcd9xxx_resmgr_notifier_call(&taiko->resmgr, e_post_on);
 		break;
-
 	case SND_SOC_DAPM_POST_PMD:
-		if ((w->reg == taiko->mbhc_bias_regs.ctl_reg) &&
-		    taiko_is_hph_pa_on(codec)) {
-			TAIKO_ACQUIRE_LOCK(taiko->codec_resource_lock);
-			taiko_codec_switch_micbias(codec, 1);
-			TAIKO_RELEASE_LOCK(taiko->codec_resource_lock);
-		}
+		/* Let MBHC module know so micbias switch to be off */
+		wcd9xxx_resmgr_notifier_call(&taiko->resmgr, e_post_off);
 
 		if (strnstr(w->name, internal1_text, 30))
 			snd_soc_update_bits(codec, micb_int_reg, 0x80, 0x00);
@@ -2502,7 +2112,8 @@
 		else if (strnstr(w->name, internal3_text, 30))
 			snd_soc_update_bits(codec, micb_int_reg, 0x2, 0x0);
 
-		taiko_codec_update_cfilt_usage(codec, cfilt_sel_val, 0);
+		/* Put cfilt */
+		wcd9xxx_resmgr_cfilt_put(&taiko->resmgr, cfilt_sel_val);
 		break;
 	}
 
@@ -2704,15 +2315,16 @@
 	struct snd_kcontrol *kcontrol, int event)
 {
 	struct snd_soc_codec *codec = w->codec;
+	struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
 
 	pr_debug("%s %d\n", __func__, event);
 
 	switch (event) {
 	case SND_SOC_DAPM_PRE_PMU:
-		taiko_enable_rx_bias(codec, 1);
+		wcd9xxx_resmgr_enable_rx_bias(&taiko->resmgr, 1);
 		break;
 	case SND_SOC_DAPM_POST_PMD:
-		taiko_enable_rx_bias(codec, 0);
+		wcd9xxx_resmgr_enable_rx_bias(&taiko->resmgr, 0);
 		break;
 	}
 	return 0;
@@ -2735,81 +2347,32 @@
 	return 0;
 }
 
-static void taiko_snd_soc_jack_report(struct taiko_priv *taiko,
-				      struct snd_soc_jack *jack, int status,
-				      int mask)
-{
-	/* XXX: wake_lock_timeout()? */
-	snd_soc_jack_report_no_dapm(jack, status, mask);
-}
-
-static void hphocp_off_report(struct taiko_priv *taiko,
-	u32 jack_status, int irq)
-{
-	struct snd_soc_codec *codec;
-	if (!taiko) {
-		pr_err("%s: Bad taiko private data\n", __func__);
-		return;
-	}
-
-	pr_debug("%s: clear ocp status %x\n", __func__, jack_status);
-	codec = taiko->codec;
-	if (taiko->hph_status & jack_status) {
-		taiko->hph_status &= ~jack_status;
-		if (taiko->mbhc_cfg.headset_jack)
-			taiko_snd_soc_jack_report(taiko,
-						  taiko->mbhc_cfg.headset_jack,
-						  taiko->hph_status,
-						  TAIKO_JACK_MASK);
-		snd_soc_update_bits(codec, TAIKO_A_RX_HPH_OCP_CTL, 0x10, 0x00);
-		snd_soc_update_bits(codec, TAIKO_A_RX_HPH_OCP_CTL, 0x10, 0x10);
-		/* reset retry counter as PA is turned off signifying
-		 * start of new OCP detection session
-		 */
-		if (TAIKO_IRQ_HPH_PA_OCPL_FAULT)
-			taiko->hphlocp_cnt = 0;
-		else
-			taiko->hphrocp_cnt = 0;
-		wcd9xxx_enable_irq(codec->control_data, irq);
-	}
-}
-
-static void hphlocp_off_report(struct work_struct *work)
-{
-	struct taiko_priv *taiko = container_of(work, struct taiko_priv,
-		hphlocp_work);
-	hphocp_off_report(taiko, SND_JACK_OC_HPHL, TAIKO_IRQ_HPH_PA_OCPL_FAULT);
-}
-
-static void hphrocp_off_report(struct work_struct *work)
-{
-	struct taiko_priv *taiko = container_of(work, struct taiko_priv,
-		hphrocp_work);
-	hphocp_off_report(taiko, SND_JACK_OC_HPHR, TAIKO_IRQ_HPH_PA_OCPR_FAULT);
-}
-
 static int taiko_hph_pa_event(struct snd_soc_dapm_widget *w,
-	struct snd_kcontrol *kcontrol, int event)
+			      struct snd_kcontrol *kcontrol, int event)
 {
 	struct snd_soc_codec *codec = w->codec;
 	struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
-	u8 mbhc_micb_ctl_val;
+	enum wcd9xxx_notify_event e_pre_on, e_post_off;
+
 	pr_debug("%s: %s event = %d\n", __func__, w->name, event);
+	if (w->shift == 5) {
+		e_pre_on = WCD9XXX_EVENT_PRE_HPHR_PA_ON;
+		e_post_off = WCD9XXX_EVENT_POST_HPHR_PA_OFF;
+	} else if (w->shift == 4) {
+		e_pre_on = WCD9XXX_EVENT_PRE_HPHL_PA_ON;
+		e_post_off = WCD9XXX_EVENT_POST_HPHL_PA_OFF;
+	} else {
+		pr_err("%s: Invalid w->shift %d\n", __func__, w->shift);
+		return -EINVAL;
+	}
 
 	switch (event) {
 	case SND_SOC_DAPM_PRE_PMU:
-		mbhc_micb_ctl_val = snd_soc_read(codec,
-				taiko->mbhc_bias_regs.ctl_reg);
-
-		if (!(mbhc_micb_ctl_val & 0x80)) {
-			TAIKO_ACQUIRE_LOCK(taiko->codec_resource_lock);
-			taiko_codec_switch_micbias(codec, 1);
-			TAIKO_RELEASE_LOCK(taiko->codec_resource_lock);
-		}
+		/* Let MBHC module know PA is turning on */
+		wcd9xxx_resmgr_notifier_call(&taiko->resmgr, e_pre_on);
 		break;
 
 	case SND_SOC_DAPM_POST_PMU:
-
 		usleep_range(10000, 10000);
 
 		snd_soc_update_bits(codec, TAIKO_A_BUCK_MODE_5, 0x02, 0x00);
@@ -2818,100 +2381,26 @@
 		snd_soc_update_bits(codec, TAIKO_A_BUCK_MODE_3, 0x08, 0x00);
 
 		usleep_range(10, 10);
-
 		break;
 
 	case SND_SOC_DAPM_POST_PMD:
-		/* schedule work is required because at the time HPH PA DAPM
+		/* Let MBHC module know PA turned off */
+		wcd9xxx_resmgr_notifier_call(&taiko->resmgr, e_post_off);
+
+		/*
+		 * schedule work is required because at the time HPH PA DAPM
 		 * event callback is called by DAPM framework, CODEC dapm mutex
 		 * would have been locked while snd_soc_jack_report also
 		 * attempts to acquire same lock.
 		 */
-		if (w->shift == 5) {
-			clear_bit(TAIKO_HPHL_PA_OFF_ACK,
-				  &taiko->hph_pa_dac_state);
-			clear_bit(TAIKO_HPHL_DAC_OFF_ACK,
-				  &taiko->hph_pa_dac_state);
-			if (taiko->hph_status & SND_JACK_OC_HPHL)
-				schedule_work(&taiko->hphlocp_work);
-		} else if (w->shift == 4) {
-			clear_bit(TAIKO_HPHR_PA_OFF_ACK,
-				  &taiko->hph_pa_dac_state);
-			clear_bit(TAIKO_HPHR_DAC_OFF_ACK,
-				  &taiko->hph_pa_dac_state);
-			if (taiko->hph_status & SND_JACK_OC_HPHR)
-				schedule_work(&taiko->hphrocp_work);
-		}
-
-		TAIKO_ACQUIRE_LOCK(taiko->codec_resource_lock);
-		taiko_codec_switch_micbias(codec, 0);
-		TAIKO_RELEASE_LOCK(taiko->codec_resource_lock);
-
 		pr_debug("%s: sleep 10 ms after %s PA disable.\n", __func__,
-				w->name);
+			 w->name);
 		usleep_range(10000, 10000);
 		break;
 	}
 	return 0;
 }
 
-static void taiko_get_mbhc_micbias_regs(struct snd_soc_codec *codec,
-					struct mbhc_micbias_regs *micbias_regs)
-{
-	struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
-	unsigned int cfilt;
-
-	switch (taiko->mbhc_cfg.micbias) {
-	case TAIKO_MICBIAS1:
-		cfilt = taiko->pdata->micbias.bias1_cfilt_sel;
-		micbias_regs->mbhc_reg = TAIKO_A_MICB_1_MBHC;
-		micbias_regs->int_rbias = TAIKO_A_MICB_1_INT_RBIAS;
-		micbias_regs->ctl_reg = TAIKO_A_MICB_1_CTL;
-		break;
-	case TAIKO_MICBIAS2:
-		cfilt = taiko->pdata->micbias.bias2_cfilt_sel;
-		micbias_regs->mbhc_reg = TAIKO_A_MICB_2_MBHC;
-		micbias_regs->int_rbias = TAIKO_A_MICB_2_INT_RBIAS;
-		micbias_regs->ctl_reg = TAIKO_A_MICB_2_CTL;
-		break;
-	case TAIKO_MICBIAS3:
-		cfilt = taiko->pdata->micbias.bias3_cfilt_sel;
-		micbias_regs->mbhc_reg = TAIKO_A_MICB_3_MBHC;
-		micbias_regs->int_rbias = TAIKO_A_MICB_3_INT_RBIAS;
-		micbias_regs->ctl_reg = TAIKO_A_MICB_3_CTL;
-		break;
-	case TAIKO_MICBIAS4:
-		cfilt = taiko->pdata->micbias.bias4_cfilt_sel;
-		micbias_regs->mbhc_reg = taiko->reg_addr.micb_4_mbhc;
-		micbias_regs->int_rbias = taiko->reg_addr.micb_4_int_rbias;
-		micbias_regs->ctl_reg = taiko->reg_addr.micb_4_ctl;
-		break;
-	default:
-		/* Should never reach here */
-		pr_err("%s: Invalid MIC BIAS for MBHC\n", __func__);
-		return;
-	}
-
-	micbias_regs->cfilt_sel = cfilt;
-
-	switch (cfilt) {
-	case TAIKO_CFILT1_SEL:
-		micbias_regs->cfilt_val = TAIKO_A_MICB_CFILT_1_VAL;
-		micbias_regs->cfilt_ctl = TAIKO_A_MICB_CFILT_1_CTL;
-		taiko->mbhc_data.micb_mv = taiko->pdata->micbias.cfilt1_mv;
-		break;
-	case TAIKO_CFILT2_SEL:
-		micbias_regs->cfilt_val = TAIKO_A_MICB_CFILT_2_VAL;
-		micbias_regs->cfilt_ctl = TAIKO_A_MICB_CFILT_2_CTL;
-		taiko->mbhc_data.micb_mv = taiko->pdata->micbias.cfilt2_mv;
-		break;
-	case TAIKO_CFILT3_SEL:
-		micbias_regs->cfilt_val = TAIKO_A_MICB_CFILT_3_VAL;
-		micbias_regs->cfilt_ctl = TAIKO_A_MICB_CFILT_3_CTL;
-		taiko->mbhc_data.micb_mv = taiko->pdata->micbias.cfilt3_mv;
-		break;
-	}
-}
 static const struct snd_soc_dapm_widget taiko_dapm_i2s_widgets[] = {
 	SND_SOC_DAPM_SUPPLY("RX_I2S_CLK", TAIKO_A_CDC_CLK_RX_I2S_CTL,
 	4, 0, NULL, 0),
@@ -2960,14 +2449,48 @@
 
 static const struct snd_soc_dapm_route audio_map[] = {
 	/* SLIMBUS Connections */
+	{"AIF1 CAP", NULL, "AIF1_CAP Mixer"},
+	{"AIF2 CAP", NULL, "AIF2_CAP Mixer"},
+	{"AIF3 CAP", NULL, "AIF3_CAP Mixer"},
 
-	{"SLIM TX1", NULL, "SLIM TX1 MUX"},
+	/* SLIM_MIXER("AIF1_CAP Mixer"),*/
+	{"AIF1_CAP Mixer", "SLIM TX1", "SLIM TX1 MUX"},
+	{"AIF1_CAP Mixer", "SLIM TX2", "SLIM TX2 MUX"},
+	{"AIF1_CAP Mixer", "SLIM TX3", "SLIM TX3 MUX"},
+	{"AIF1_CAP Mixer", "SLIM TX4", "SLIM TX4 MUX"},
+	{"AIF1_CAP Mixer", "SLIM TX5", "SLIM TX5 MUX"},
+	{"AIF1_CAP Mixer", "SLIM TX6", "SLIM TX6 MUX"},
+	{"AIF1_CAP Mixer", "SLIM TX7", "SLIM TX7 MUX"},
+	{"AIF1_CAP Mixer", "SLIM TX8", "SLIM TX8 MUX"},
+	{"AIF1_CAP Mixer", "SLIM TX9", "SLIM TX9 MUX"},
+	{"AIF1_CAP Mixer", "SLIM TX10", "SLIM TX10 MUX"},
+	/* SLIM_MIXER("AIF2_CAP Mixer"),*/
+	{"AIF2_CAP Mixer", "SLIM TX1", "SLIM TX1 MUX"},
+	{"AIF2_CAP Mixer", "SLIM TX2", "SLIM TX2 MUX"},
+	{"AIF2_CAP Mixer", "SLIM TX3", "SLIM TX3 MUX"},
+	{"AIF2_CAP Mixer", "SLIM TX4", "SLIM TX4 MUX"},
+	{"AIF2_CAP Mixer", "SLIM TX5", "SLIM TX5 MUX"},
+	{"AIF2_CAP Mixer", "SLIM TX6", "SLIM TX6 MUX"},
+	{"AIF2_CAP Mixer", "SLIM TX7", "SLIM TX7 MUX"},
+	{"AIF2_CAP Mixer", "SLIM TX8", "SLIM TX8 MUX"},
+	{"AIF2_CAP Mixer", "SLIM TX9", "SLIM TX9 MUX"},
+	{"AIF2_CAP Mixer", "SLIM TX10", "SLIM TX10 MUX"},
+	/* SLIM_MIXER("AIF3_CAP Mixer"),*/
+	{"AIF3_CAP Mixer", "SLIM TX1", "SLIM TX1 MUX"},
+	{"AIF3_CAP Mixer", "SLIM TX2", "SLIM TX2 MUX"},
+	{"AIF3_CAP Mixer", "SLIM TX3", "SLIM TX3 MUX"},
+	{"AIF3_CAP Mixer", "SLIM TX4", "SLIM TX4 MUX"},
+	{"AIF3_CAP Mixer", "SLIM TX5", "SLIM TX5 MUX"},
+	{"AIF3_CAP Mixer", "SLIM TX6", "SLIM TX6 MUX"},
+	{"AIF3_CAP Mixer", "SLIM TX7", "SLIM TX7 MUX"},
+	{"AIF3_CAP Mixer", "SLIM TX8", "SLIM TX8 MUX"},
+	{"AIF3_CAP Mixer", "SLIM TX9", "SLIM TX9 MUX"},
+	{"AIF3_CAP Mixer", "SLIM TX10", "SLIM TX10 MUX"},
+
 	{"SLIM TX1 MUX", "DEC1", "DEC1 MUX"},
 
-	{"SLIM TX2", NULL, "SLIM TX2 MUX"},
 	{"SLIM TX2 MUX", "DEC2", "DEC2 MUX"},
 
-	{"SLIM TX3", NULL, "SLIM TX3 MUX"},
 	{"SLIM TX3 MUX", "DEC3", "DEC3 MUX"},
 	{"SLIM TX3 MUX", "RMIX1", "RX1 MIX1"},
 	{"SLIM TX3 MUX", "RMIX2", "RX2 MIX1"},
@@ -2977,10 +2500,8 @@
 	{"SLIM TX3 MUX", "RMIX6", "RX6 MIX1"},
 	{"SLIM TX3 MUX", "RMIX7", "RX7 MIX1"},
 
-	{"SLIM TX4", NULL, "SLIM TX4 MUX"},
 	{"SLIM TX4 MUX", "DEC4", "DEC4 MUX"},
 
-	{"SLIM TX5", NULL, "SLIM TX5 MUX"},
 	{"SLIM TX5 MUX", "DEC5", "DEC5 MUX"},
 	{"SLIM TX5 MUX", "RMIX1", "RX1 MIX1"},
 	{"SLIM TX5 MUX", "RMIX2", "RX2 MIX1"},
@@ -2990,10 +2511,8 @@
 	{"SLIM TX5 MUX", "RMIX6", "RX6 MIX1"},
 	{"SLIM TX5 MUX", "RMIX7", "RX7 MIX1"},
 
-	{"SLIM TX6", NULL, "SLIM TX6 MUX"},
 	{"SLIM TX6 MUX", "DEC6", "DEC6 MUX"},
 
-	{"SLIM TX7", NULL, "SLIM TX7 MUX"},
 	{"SLIM TX7 MUX", "DEC1", "DEC1 MUX"},
 	{"SLIM TX7 MUX", "DEC2", "DEC2 MUX"},
 	{"SLIM TX7 MUX", "DEC3", "DEC3 MUX"},
@@ -3012,7 +2531,6 @@
 	{"SLIM TX7 MUX", "RMIX6", "RX6 MIX1"},
 	{"SLIM TX7 MUX", "RMIX7", "RX7 MIX1"},
 
-	{"SLIM TX8", NULL, "SLIM TX8 MUX"},
 	{"SLIM TX8 MUX", "DEC1", "DEC1 MUX"},
 	{"SLIM TX8 MUX", "DEC2", "DEC2 MUX"},
 	{"SLIM TX8 MUX", "DEC3", "DEC3 MUX"},
@@ -3024,7 +2542,6 @@
 	{"SLIM TX8 MUX", "DEC9", "DEC9 MUX"},
 	{"SLIM TX8 MUX", "DEC10", "DEC10 MUX"},
 
-	{"SLIM TX9", NULL, "SLIM TX9 MUX"},
 	{"SLIM TX9 MUX", "DEC1", "DEC1 MUX"},
 	{"SLIM TX9 MUX", "DEC2", "DEC2 MUX"},
 	{"SLIM TX9 MUX", "DEC3", "DEC3 MUX"},
@@ -3036,7 +2553,6 @@
 	{"SLIM TX9 MUX", "DEC9", "DEC9 MUX"},
 	{"SLIM TX9 MUX", "DEC10", "DEC10 MUX"},
 
-	{"SLIM TX10", NULL, "SLIM TX10 MUX"},
 	{"SLIM TX10 MUX", "DEC1", "DEC1 MUX"},
 	{"SLIM TX10 MUX", "DEC2", "DEC2 MUX"},
 	{"SLIM TX10 MUX", "DEC3", "DEC3 MUX"},
@@ -3101,29 +2617,44 @@
 	{"LINEOUT4", NULL, "LINEOUT4 PA"},
 	{"SPK_OUT", NULL, "SPK PA"},
 
+	{"LINEOUT1 PA", NULL, "CP"},
 	{"LINEOUT1 PA", NULL, "LINEOUT1_PA_MIXER"},
 	{"LINEOUT1_PA_MIXER", NULL, "LINEOUT1 DAC"},
+
+	{"LINEOUT2 PA", NULL, "CP"},
 	{"LINEOUT2 PA", NULL, "LINEOUT2_PA_MIXER"},
 	{"LINEOUT2_PA_MIXER", NULL, "LINEOUT2 DAC"},
+
+	{"LINEOUT3 PA", NULL, "CP"},
 	{"LINEOUT3 PA", NULL, "LINEOUT3_PA_MIXER"},
 	{"LINEOUT3_PA_MIXER", NULL, "LINEOUT3 DAC"},
+
+	{"LINEOUT4 PA", NULL, "CP"},
 	{"LINEOUT4 PA", NULL, "LINEOUT4_PA_MIXER"},
 	{"LINEOUT4_PA_MIXER", NULL, "LINEOUT4 DAC"},
 
+	{"CP", NULL, "CLASS_H_LINEOUTS_PA"},
+	{"CLASS_H_LINEOUTS_PA", NULL, "CLASS_H_CLK"},
+
+
+
 	{"LINEOUT1 DAC", NULL, "RX3 MIX1"},
 
-	{"RX4 DSM MUX", "DSM_INV", "RX3 MIX1"},
-	{"RX4 DSM MUX", "CIC_OUT", "RX4 MIX1"},
-	{"LINEOUT3 DAC", NULL, "RX4 DSM MUX"},
+
+	{"RDAC5 MUX", "DEM3_INV", "RX3 MIX1"},
+	{"RDAC5 MUX", "DEM4", "RX4 MIX1"},
+
+	{"LINEOUT3 DAC", NULL, "RDAC5 MUX"},
 
 	{"LINEOUT2 DAC", NULL, "RX5 MIX1"},
 
-	{"RX6 DSM MUX", "DSM_INV", "RX5 MIX1"},
-	{"RX6 DSM MUX", "CIC_OUT", "RX6 MIX1"},
-	{"LINEOUT4 DAC", NULL, "RX6 DSM MUX"},
+	{"RDAC7 MUX", "DEM5_INV", "RX5 MIX1"},
+	{"RDAC7 MUX", "DEM6", "RX6 MIX1"},
+
+	{"LINEOUT4 DAC", NULL, "RDAC7 MUX"},
 
 	{"SPK PA", NULL, "SPK DAC"},
-	{"SPK DAC", NULL, "RX7 MIX1"},
+	{"SPK DAC", NULL, "RX7 MIX2"},
 
 	{"RX1 CHAIN", NULL, "RX1 MIX2"},
 	{"RX2 CHAIN", NULL, "RX2 MIX2"},
@@ -3167,6 +2698,39 @@
 	{"RX7 MIX2", NULL, "RX7 MIX2 INP1"},
 	{"RX7 MIX2", NULL, "RX7 MIX2 INP2"},
 
+	/* SLIM_MUX("AIF1_PB", "AIF1 PB"),*/
+	{"SLIM RX1 MUX", "AIF1_PB", "AIF1 PB"},
+	{"SLIM RX2 MUX", "AIF1_PB", "AIF1 PB"},
+	{"SLIM RX3 MUX", "AIF1_PB", "AIF1 PB"},
+	{"SLIM RX4 MUX", "AIF1_PB", "AIF1 PB"},
+	{"SLIM RX5 MUX", "AIF1_PB", "AIF1 PB"},
+	{"SLIM RX6 MUX", "AIF1_PB", "AIF1 PB"},
+	{"SLIM RX7 MUX", "AIF1_PB", "AIF1 PB"},
+	/* SLIM_MUX("AIF2_PB", "AIF2 PB"),*/
+	{"SLIM RX1 MUX", "AIF2_PB", "AIF2 PB"},
+	{"SLIM RX2 MUX", "AIF2_PB", "AIF2 PB"},
+	{"SLIM RX3 MUX", "AIF2_PB", "AIF2 PB"},
+	{"SLIM RX4 MUX", "AIF2_PB", "AIF2 PB"},
+	{"SLIM RX5 MUX", "AIF2_PB", "AIF2 PB"},
+	{"SLIM RX6 MUX", "AIF2_PB", "AIF2 PB"},
+	{"SLIM RX7 MUX", "AIF2_PB", "AIF2 PB"},
+	/* SLIM_MUX("AIF3_PB", "AIF3 PB"),*/
+	{"SLIM RX1 MUX", "AIF3_PB", "AIF3 PB"},
+	{"SLIM RX2 MUX", "AIF3_PB", "AIF3 PB"},
+	{"SLIM RX3 MUX", "AIF3_PB", "AIF3 PB"},
+	{"SLIM RX4 MUX", "AIF3_PB", "AIF3 PB"},
+	{"SLIM RX5 MUX", "AIF3_PB", "AIF3 PB"},
+	{"SLIM RX6 MUX", "AIF3_PB", "AIF3 PB"},
+	{"SLIM RX7 MUX", "AIF3_PB", "AIF3 PB"},
+
+	{"SLIM RX1", NULL, "SLIM RX1 MUX"},
+	{"SLIM RX2", NULL, "SLIM RX2 MUX"},
+	{"SLIM RX3", NULL, "SLIM RX3 MUX"},
+	{"SLIM RX4", NULL, "SLIM RX4 MUX"},
+	{"SLIM RX5", NULL, "SLIM RX5 MUX"},
+	{"SLIM RX6", NULL, "SLIM RX6 MUX"},
+	{"SLIM RX7", NULL, "SLIM RX7 MUX"},
+
 	{"RX1 MIX1 INP1", "RX1", "SLIM RX1"},
 	{"RX1 MIX1 INP1", "RX2", "SLIM RX2"},
 	{"RX1 MIX1 INP1", "RX3", "SLIM RX3"},
@@ -3435,6 +2999,9 @@
 	if (reg == TAIKO_A_RX_HPH_L_STATUS || reg == TAIKO_A_RX_HPH_R_STATUS)
 		return 1;
 
+	if (reg == TAIKO_A_MBHC_INSERT_DET_STATUS)
+		return 1;
+
 	return 0;
 }
 
@@ -3443,6 +3010,10 @@
 	unsigned int value)
 {
 	int ret;
+
+	if (reg == SND_SOC_NOPM)
+		return 0;
+
 	BUG_ON(reg > TAIKO_MAX_REGISTER);
 
 	if (!taiko_volatile(codec, reg)) {
@@ -3460,6 +3031,9 @@
 	unsigned int val;
 	int ret;
 
+	if (reg == SND_SOC_NOPM)
+		return 0;
+
 	BUG_ON(reg > TAIKO_MAX_REGISTER);
 
 	if (!taiko_volatile(codec, reg) && taiko_readable(codec, reg) &&
@@ -3476,79 +3050,6 @@
 	return val;
 }
 
-static s16 taiko_get_current_v_ins(struct taiko_priv *taiko, bool hu)
-{
-	s16 v_ins;
-	if ((taiko->mbhc_data.micb_mv != VDDIO_MICBIAS_MV) &&
-	    taiko->mbhc_micbias_switched)
-		v_ins = hu ? (s16)taiko->mbhc_data.adj_v_ins_hu :
-			     (s16)taiko->mbhc_data.adj_v_ins_h;
-	else
-		v_ins = hu ? (s16)taiko->mbhc_data.v_ins_hu :
-			     (s16)taiko->mbhc_data.v_ins_h;
-	return v_ins;
-}
-
-static s16 taiko_get_current_v_hs_max(struct taiko_priv *taiko)
-{
-	s16 v_hs_max;
-	struct taiko_mbhc_plug_type_cfg *plug_type;
-
-	plug_type = TAIKO_MBHC_CAL_PLUG_TYPE_PTR(taiko->mbhc_cfg.calibration);
-	if ((taiko->mbhc_data.micb_mv != VDDIO_MICBIAS_MV) &&
-	    taiko->mbhc_micbias_switched)
-		v_hs_max = taiko->mbhc_data.adj_v_hs_max;
-	else
-		v_hs_max = plug_type->v_hs_max;
-	return v_hs_max;
-}
-
-static void taiko_codec_calibrate_hs_polling(struct snd_soc_codec *codec)
-{
-	u8 *n_ready, *n_cic;
-	struct taiko_mbhc_btn_detect_cfg *btn_det;
-	struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
-	const s16 v_ins_hu = taiko_get_current_v_ins(taiko, true);
-
-	btn_det = TAIKO_MBHC_CAL_BTN_DET_PTR(taiko->mbhc_cfg.calibration);
-
-	snd_soc_write(codec, TAIKO_A_CDC_MBHC_VOLT_B1_CTL,
-		      v_ins_hu & 0xFF);
-	snd_soc_write(codec, TAIKO_A_CDC_MBHC_VOLT_B2_CTL,
-		      (v_ins_hu >> 8) & 0xFF);
-
-	snd_soc_write(codec, TAIKO_A_CDC_MBHC_VOLT_B3_CTL,
-		      taiko->mbhc_data.v_b1_hu & 0xFF);
-	snd_soc_write(codec, TAIKO_A_CDC_MBHC_VOLT_B4_CTL,
-		      (taiko->mbhc_data.v_b1_hu >> 8) & 0xFF);
-
-	snd_soc_write(codec, TAIKO_A_CDC_MBHC_VOLT_B5_CTL,
-		      taiko->mbhc_data.v_b1_h & 0xFF);
-	snd_soc_write(codec, TAIKO_A_CDC_MBHC_VOLT_B6_CTL,
-		      (taiko->mbhc_data.v_b1_h >> 8) & 0xFF);
-
-	snd_soc_write(codec, TAIKO_A_CDC_MBHC_VOLT_B9_CTL,
-		      taiko->mbhc_data.v_brh & 0xFF);
-	snd_soc_write(codec, TAIKO_A_CDC_MBHC_VOLT_B10_CTL,
-		      (taiko->mbhc_data.v_brh >> 8) & 0xFF);
-
-	snd_soc_write(codec, TAIKO_A_CDC_MBHC_VOLT_B11_CTL,
-		      taiko->mbhc_data.v_brl & 0xFF);
-	snd_soc_write(codec, TAIKO_A_CDC_MBHC_VOLT_B12_CTL,
-		      (taiko->mbhc_data.v_brl >> 8) & 0xFF);
-
-	n_ready = taiko_mbhc_cal_btn_det_mp(btn_det, TAIKO_BTN_DET_N_READY);
-	snd_soc_write(codec, TAIKO_A_CDC_MBHC_TIMER_B1_CTL,
-		      n_ready[taiko_codec_mclk_index(taiko)]);
-	snd_soc_write(codec, TAIKO_A_CDC_MBHC_TIMER_B2_CTL,
-		      taiko->mbhc_data.npoll);
-	snd_soc_write(codec, TAIKO_A_CDC_MBHC_TIMER_B3_CTL,
-		      taiko->mbhc_data.nbounce_wait);
-	n_cic = taiko_mbhc_cal_btn_det_mp(btn_det, TAIKO_BTN_DET_N_CIC);
-	snd_soc_write(codec, TAIKO_A_CDC_MBHC_TIMER_B6_CTL,
-		      n_cic[taiko_codec_mclk_index(taiko)]);
-}
-
 static int taiko_startup(struct snd_pcm_substream *substream,
 		struct snd_soc_dai *dai)
 {
@@ -3583,54 +3084,20 @@
 
 	pr_debug("%s: mclk_enable = %u, dapm = %d\n", __func__, mclk_enable,
 		 dapm);
-	if (dapm)
-		TAIKO_ACQUIRE_LOCK(taiko->codec_resource_lock);
+
+	WCD9XXX_BCL_LOCK(&taiko->resmgr);
 	if (mclk_enable) {
-		taiko->mclk_enabled = true;
-
-		if (taiko->mbhc_polling_active) {
-			taiko_codec_pause_hs_polling(codec);
-			taiko_codec_disable_clock_block(codec);
-			taiko_codec_enable_bandgap(codec,
-						   TAIKO_BANDGAP_AUDIO_MODE);
-			taiko_codec_enable_clock_block(codec, 0);
-			taiko_codec_calibrate_hs_polling(codec);
-			taiko_codec_start_hs_polling(codec);
-		} else {
-			taiko_codec_disable_clock_block(codec);
-			taiko_codec_enable_bandgap(codec,
-						   TAIKO_BANDGAP_AUDIO_MODE);
-			taiko_codec_enable_clock_block(codec, 0);
-		}
+		wcd9xxx_resmgr_get_bandgap(&taiko->resmgr,
+					   WCD9XXX_BANDGAP_AUDIO_MODE);
+		wcd9xxx_resmgr_get_clk_block(&taiko->resmgr, WCD9XXX_CLK_MCLK);
 	} else {
-
-		if (!taiko->mclk_enabled) {
-			if (dapm)
-				TAIKO_RELEASE_LOCK(taiko->codec_resource_lock);
-			pr_err("Error, MCLK already diabled\n");
-			return -EINVAL;
-		}
-		taiko->mclk_enabled = false;
-
-		if (taiko->mbhc_polling_active) {
-			taiko_codec_pause_hs_polling(codec);
-			taiko_codec_disable_clock_block(codec);
-			taiko_codec_enable_bandgap(codec,
-						   TAIKO_BANDGAP_MBHC_MODE);
-			taiko_enable_rx_bias(codec, 1);
-			taiko_codec_enable_clock_block(codec, 1);
-			taiko_codec_calibrate_hs_polling(codec);
-			taiko_codec_start_hs_polling(codec);
-			snd_soc_update_bits(codec, TAIKO_A_CLK_BUFF_EN1,
-					0x05, 0x01);
-		} else {
-			taiko_codec_disable_clock_block(codec);
-			taiko_codec_enable_bandgap(codec,
-						   TAIKO_BANDGAP_OFF);
-		}
+		/* Put clock and BG */
+		wcd9xxx_resmgr_put_clk_block(&taiko->resmgr, WCD9XXX_CLK_MCLK);
+		wcd9xxx_resmgr_put_bandgap(&taiko->resmgr,
+					   WCD9XXX_BANDGAP_AUDIO_MODE);
 	}
-	if (dapm)
-		TAIKO_RELEASE_LOCK(taiko->codec_resource_lock);
+	WCD9XXX_BCL_UNLOCK(&taiko->resmgr);
+
 	return 0;
 }
 
@@ -3685,90 +3152,221 @@
 
 {
 	struct taiko_priv *taiko = snd_soc_codec_get_drvdata(dai->codec);
-	u32 i = 0;
+	struct wcd9xxx *core = dev_get_drvdata(dai->codec->dev->parent);
 	if (!tx_slot && !rx_slot) {
 		pr_err("%s: Invalid\n", __func__);
 		return -EINVAL;
 	}
-	pr_debug("%s(): dai_name = %s DAI-ID %x tx_ch %d rx_ch %d\n",
-			__func__, dai->name, dai->id, tx_num, rx_num);
+	pr_debug("%s(): dai_name = %s DAI-ID %x tx_ch %d rx_ch %d\n"
+		 "taiko->intf_type %d\n",
+		 __func__, dai->name, dai->id, tx_num, rx_num,
+		 taiko->intf_type);
 
-	if (dai->id == AIF1_PB || dai->id == AIF2_PB || dai->id == AIF3_PB) {
-		for (i = 0; i < rx_num; i++) {
-			taiko->dai[dai->id - 1].ch_num[i]  = rx_slot[i];
-			taiko->dai[dai->id - 1].ch_act = 0;
-			taiko->dai[dai->id - 1].ch_tot = rx_num;
+	if (taiko->intf_type == WCD9XXX_INTERFACE_TYPE_SLIMBUS)
+		wcd9xxx_init_slimslave(core, core->slim->laddr,
+				       tx_num, tx_slot, rx_num, rx_slot);
+	return 0;
+}
+
+static int taiko_get_channel_map(struct snd_soc_dai *dai,
+				 unsigned int *tx_num, unsigned int *tx_slot,
+				 unsigned int *rx_num, unsigned int *rx_slot)
+
+{
+	struct taiko_priv *taiko_p = snd_soc_codec_get_drvdata(dai->codec);
+	u32 i = 0;
+	struct wcd9xxx_ch *ch;
+
+	switch (dai->id) {
+	case AIF1_PB:
+	case AIF2_PB:
+	case AIF3_PB:
+		if (!rx_slot || !rx_num) {
+			pr_err("%s: Invalid rx_slot %d or rx_num %d\n",
+				 __func__, (u32) rx_slot, (u32) rx_num);
+			return -EINVAL;
 		}
-	} else if (dai->id == AIF1_CAP || dai->id == AIF2_CAP ||
-		   dai->id == AIF3_CAP) {
-		for (i = 0; i < tx_num; i++) {
-			taiko->dai[dai->id - 1].ch_num[i]  = tx_slot[i];
-			taiko->dai[dai->id - 1].ch_act = 0;
-			taiko->dai[dai->id - 1].ch_tot = tx_num;
+		list_for_each_entry(ch, &taiko_p->dai[dai->id].wcd9xxx_ch_list,
+				    list) {
+			pr_debug("%s: rx_slot[%d] %d, ch->ch_num %d\n",
+				 __func__, i, rx_slot[i], ch->ch_num);
+			rx_slot[i++] = ch->ch_num;
+		}
+		pr_debug("%s: rx_num %d\n", __func__, i);
+		*rx_num = i;
+		break;
+	case AIF1_CAP:
+	case AIF2_CAP:
+	case AIF3_CAP:
+		if (!tx_slot || !tx_num) {
+			pr_err("%s: Invalid tx_slot %d or tx_num %d\n",
+				 __func__, (u32) tx_slot, (u32) tx_num);
+			return -EINVAL;
+		}
+		list_for_each_entry(ch, &taiko_p->dai[dai->id].wcd9xxx_ch_list,
+				    list) {
+			pr_debug("%s: tx_slot[%d] %d, ch->ch_num %d\n",
+				 __func__, i, tx_slot[i], ch->ch_num);
+			tx_slot[i++] = ch->ch_num;
+		}
+		pr_debug("%s: tx_num %d\n", __func__, i);
+		*tx_num = i;
+		break;
+
+	default:
+		pr_err("%s: Invalid DAI ID %x\n", __func__, dai->id);
+		break;
+	}
+
+	return 0;
+}
+
+static int taiko_set_interpolator_rate(struct snd_soc_dai *dai,
+	u8 rx_fs_rate_reg_val, u32 compander_fs, u32 sample_rate)
+{
+	u32 j;
+	u8 rx_mix1_inp;
+	u16 rx_mix_1_reg_1, rx_mix_1_reg_2;
+	u16 rx_fs_reg;
+	u8 rx_mix_1_reg_1_val, rx_mix_1_reg_2_val;
+	struct snd_soc_codec *codec = dai->codec;
+	struct wcd9xxx_ch *ch;
+	struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
+
+	list_for_each_entry(ch, &taiko->dai[dai->id].wcd9xxx_ch_list, list) {
+		/* for RX port starting from 16 instead of 10 like tabla */
+		rx_mix1_inp = ch->port + RX_MIX1_INP_SEL_RX1 -
+			      TAIKO_TX_PORT_NUMBER;
+		if ((rx_mix1_inp < RX_MIX1_INP_SEL_RX1) ||
+			(rx_mix1_inp > RX_MIX1_INP_SEL_RX7)) {
+			pr_err("%s: Invalid TAIKO_RX%u port. Dai ID is %d\n",
+				__func__,  rx_mix1_inp - 5 , dai->id);
+			return -EINVAL;
+		}
+
+		rx_mix_1_reg_1 = TAIKO_A_CDC_CONN_RX1_B1_CTL;
+
+		for (j = 0; j < NUM_INTERPOLATORS; j++) {
+			rx_mix_1_reg_2 = rx_mix_1_reg_1 + 1;
+
+			rx_mix_1_reg_1_val = snd_soc_read(codec,
+							  rx_mix_1_reg_1);
+			rx_mix_1_reg_2_val = snd_soc_read(codec,
+							  rx_mix_1_reg_2);
+
+			if (((rx_mix_1_reg_1_val & 0x0F) == rx_mix1_inp) ||
+			    (((rx_mix_1_reg_1_val >> 4) & 0x0F)
+				== rx_mix1_inp) ||
+			    ((rx_mix_1_reg_2_val & 0x0F) == rx_mix1_inp)) {
+
+				rx_fs_reg = TAIKO_A_CDC_RX1_B5_CTL + 8 * j;
+
+				pr_debug("%s: AIF_PB DAI(%d) connected to RX%u\n",
+					__func__, dai->id, j + 1);
+
+				pr_debug("%s: set RX%u sample rate to %u\n",
+					__func__, j + 1, sample_rate);
+
+				snd_soc_update_bits(codec, rx_fs_reg,
+						0xE0, rx_fs_rate_reg_val);
+
+				if (comp_rx_path[j] < COMPANDER_MAX)
+					taiko->comp_fs[comp_rx_path[j]]
+					= compander_fs;
+			}
+			if (j <= 2)
+				rx_mix_1_reg_1 += 3;
+			else
+				rx_mix_1_reg_1 += 2;
 		}
 	}
 	return 0;
 }
 
-static int taiko_get_channel_map(struct snd_soc_dai *dai,
-				unsigned int *tx_num, unsigned int *tx_slot,
-				unsigned int *rx_num, unsigned int *rx_slot)
-
+static int taiko_set_decimator_rate(struct snd_soc_dai *dai,
+	u8 tx_fs_rate_reg_val, u32 sample_rate)
 {
-	struct wcd9xxx *taiko = dev_get_drvdata(dai->codec->control_data);
+	struct snd_soc_codec *codec = dai->codec;
+	struct wcd9xxx_ch *ch;
+	struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
+	u32 tx_port;
+	u16 tx_port_reg, tx_fs_reg;
+	u8 tx_port_reg_val;
+	s8 decimator;
 
-	u32 cnt = 0;
-	u32 tx_ch[SLIM_MAX_TX_PORTS];
-	u32 rx_ch[SLIM_MAX_RX_PORTS];
+	list_for_each_entry(ch, &taiko->dai[dai->id].wcd9xxx_ch_list, list) {
 
-	if (!rx_slot && !tx_slot) {
-		pr_err("%s: Invalid\n", __func__);
-		return -EINVAL;
+		tx_port = ch->port + 1;
+		pr_debug("%s: dai->id = %d, tx_port = %d",
+			__func__, dai->id, tx_port);
+
+		if ((tx_port < 1) || (tx_port > NUM_DECIMATORS)) {
+			pr_err("%s: Invalid SLIM TX%u port. DAI ID is %d\n",
+				__func__, tx_port, dai->id);
+			return -EINVAL;
+		}
+
+		tx_port_reg = TAIKO_A_CDC_CONN_TX_SB_B1_CTL + (tx_port - 1);
+		tx_port_reg_val =  snd_soc_read(codec, tx_port_reg);
+
+		decimator = 0;
+
+		if ((tx_port >= 1) && (tx_port <= 6)) {
+
+			tx_port_reg_val =  tx_port_reg_val & 0x0F;
+			if (tx_port_reg_val == 0x8)
+				decimator = tx_port;
+
+		} else if ((tx_port >= 7) && (tx_port <= NUM_DECIMATORS)) {
+
+			tx_port_reg_val =  tx_port_reg_val & 0x1F;
+
+			if ((tx_port_reg_val >= 0x8) &&
+			    (tx_port_reg_val <= 0x11)) {
+
+				decimator = (tx_port_reg_val - 0x8) + 1;
+			}
+		}
+
+		if (decimator) { /* SLIM_TX port has a DEC as input */
+
+			tx_fs_reg = TAIKO_A_CDC_TX1_CLK_FS_CTL +
+				    8 * (decimator - 1);
+
+			pr_debug("%s: set DEC%u (-> SLIM_TX%u) rate to %u\n",
+				__func__, decimator, tx_port, sample_rate);
+
+			snd_soc_update_bits(codec, tx_fs_reg, 0x07,
+					    tx_fs_rate_reg_val);
+
+		} else {
+			if ((tx_port_reg_val >= 0x1) &&
+			    (tx_port_reg_val <= 0x7)) {
+
+				pr_debug("%s: RMIX%u going to SLIM TX%u\n",
+					__func__, tx_port_reg_val, tx_port);
+
+			} else if  ((tx_port_reg_val >= 0x8) &&
+				    (tx_port_reg_val <= 0x11)) {
+
+				pr_err("%s: ERROR: Should not be here\n",
+				       __func__);
+				pr_err("%s: ERROR: DEC connected to SLIM TX%u\n",
+					__func__, tx_port);
+				return -EINVAL;
+
+			} else if (tx_port_reg_val == 0) {
+				pr_debug("%s: no signal to SLIM TX%u\n",
+					__func__, tx_port);
+			} else {
+				pr_err("%s: ERROR: wrong signal to SLIM TX%u\n",
+					__func__, tx_port);
+				pr_err("%s: ERROR: wrong signal = %u\n",
+					__func__, tx_port_reg_val);
+				return -EINVAL;
+			}
+		}
 	}
-
-	/* for virtual port, codec driver needs to do
-	 * housekeeping, for now should be ok
-	 */
-	wcd9xxx_get_channel(taiko, rx_ch, tx_ch);
-	if (dai->id == AIF1_PB) {
-		*rx_num = taiko_dai[dai->id - 1].playback.channels_max;
-		while (cnt < *rx_num) {
-			rx_slot[cnt] = rx_ch[cnt];
-			cnt++;
-		}
-	} else if (dai->id == AIF1_CAP) {
-		*tx_num = taiko_dai[dai->id - 1].capture.channels_max;
-		while (cnt < *tx_num) {
-			tx_slot[cnt] = tx_ch[6 + cnt];
-			cnt++;
-		}
-	} else if (dai->id == AIF2_PB) {
-		*rx_num = taiko_dai[dai->id - 1].playback.channels_max;
-		while (cnt < *rx_num) {
-			rx_slot[cnt] = rx_ch[5 + cnt];
-			cnt++;
-		}
-	} else if (dai->id == AIF2_CAP) {
-		*tx_num = taiko_dai[dai->id - 1].capture.channels_max;
-		tx_slot[0] = tx_ch[cnt];
-		tx_slot[1] = tx_ch[1 + cnt];
-		tx_slot[2] = tx_ch[5 + cnt];
-		tx_slot[3] = tx_ch[3 + cnt];
-
-	} else if (dai->id == AIF3_PB) {
-		*rx_num = taiko_dai[dai->id - 1].playback.channels_max;
-		rx_slot[0] = rx_ch[3];
-		rx_slot[1] = rx_ch[4];
-
-	} else if (dai->id == AIF3_CAP) {
-		*tx_num = taiko_dai[dai->id - 1].capture.channels_max;
-		tx_slot[cnt] = tx_ch[2 + cnt];
-		tx_slot[cnt + 1] = tx_ch[4 + cnt];
-	}
-	pr_debug("%s(): dai_name = %s DAI-ID %x tx_ch %d rx_ch %d\n",
-			__func__, dai->name, dai->id, *tx_num, *rx_num);
-
-
 	return 0;
 }
 
@@ -3778,10 +3376,9 @@
 {
 	struct snd_soc_codec *codec = dai->codec;
 	struct taiko_priv *taiko = snd_soc_codec_get_drvdata(dai->codec);
-	u8 path, shift;
-	u16 tx_fs_reg, rx_fs_reg;
-	u8 tx_fs_rate, rx_fs_rate, rx_state, tx_state;
+	u8 tx_fs_rate, rx_fs_rate;
 	u32 compander_fs;
+	int ret;
 
 	pr_debug("%s: dai_name = %s DAI-ID %x rate %d num_ch %d\n", __func__,
 		 dai->name, dai->id, params_rate(params),
@@ -3820,37 +3417,20 @@
 		break;
 	default:
 		pr_err("%s: Invalid sampling rate %d\n", __func__,
-				params_rate(params));
+			params_rate(params));
 		return -EINVAL;
 	}
 
-
-	/**
-	 * If current dai is a tx dai, set sample rate to
-	 * all the txfe paths that are currently not active
-	 */
-	if ((dai->id == AIF1_CAP) || (dai->id == AIF2_CAP) ||
-	    (dai->id == AIF3_CAP)) {
-
-		tx_state = snd_soc_read(codec,
-				TAIKO_A_CDC_CLK_TX_CLK_EN_B1_CTL);
-
-		for (path = 1, shift = 0;
-				path <= NUM_DECIMATORS; path++, shift++) {
-
-			if (path == BITS_PER_REG + 1) {
-				shift = 0;
-				tx_state = snd_soc_read(codec,
-					TAIKO_A_CDC_CLK_TX_CLK_EN_B2_CTL);
-			}
-
-			if (!(tx_state & (1 << shift))) {
-				tx_fs_reg = TAIKO_A_CDC_TX1_CLK_FS_CTL
-						+ (BITS_PER_REG*(path-1));
-				snd_soc_update_bits(codec, tx_fs_reg,
-							0x07, tx_fs_rate);
-			}
+	switch (substream->stream) {
+	case SNDRV_PCM_STREAM_CAPTURE:
+		ret = taiko_set_decimator_rate(dai, tx_fs_rate,
+					       params_rate(params));
+		if (ret < 0) {
+			pr_err("%s: set decimator rate failed %d\n", __func__,
+				ret);
+			return ret;
 		}
+
 		if (taiko->intf_type == WCD9XXX_INTERFACE_TYPE_I2C) {
 			switch (params_format(params)) {
 			case SNDRV_PCM_FORMAT_S16_LE:
@@ -3868,37 +3448,20 @@
 				break;
 			}
 			snd_soc_update_bits(codec, TAIKO_A_CDC_CLK_TX_I2S_CTL,
-						0x07, tx_fs_rate);
+					    0x07, tx_fs_rate);
 		} else {
-			taiko->dai[dai->id - 1].rate   = params_rate(params);
+			taiko->dai[dai->id].rate   = params_rate(params);
 		}
-	}
-	/**
-	 * TODO: Need to handle case where same RX chain takes 2 or more inputs
-	 * with varying sample rates
-	 */
+		break;
 
-	/**
-	 * If current dai is a rx dai, set sample rate to
-	 * all the rx paths that are currently not active
-	 */
-	if (dai->id == AIF1_PB || dai->id == AIF2_PB || dai->id == AIF3_PB) {
-
-		rx_state = snd_soc_read(codec,
-			TAIKO_A_CDC_CLK_RX_B1_CTL);
-
-		for (path = 1, shift = 0;
-				path <= NUM_INTERPOLATORS; path++, shift++) {
-
-			if (!(rx_state & (1 << shift))) {
-				rx_fs_reg = TAIKO_A_CDC_RX1_B5_CTL
-						+ (BITS_PER_REG*(path-1));
-				snd_soc_update_bits(codec, rx_fs_reg,
-						0xE0, rx_fs_rate);
-				if (comp_rx_path[shift] < COMPANDER_MAX)
-					taiko->comp_fs[comp_rx_path[shift]]
-					= compander_fs;
-			}
+	case SNDRV_PCM_STREAM_PLAYBACK:
+		ret = taiko_set_interpolator_rate(dai, rx_fs_rate,
+						  compander_fs,
+						  params_rate(params));
+		if (ret < 0) {
+			pr_err("%s: set decimator rate failed %d\n", __func__,
+				ret);
+			return ret;
 		}
 		if (taiko->intf_type == WCD9XXX_INTERFACE_TYPE_I2C) {
 			switch (params_format(params)) {
@@ -3917,10 +3480,15 @@
 				break;
 			}
 			snd_soc_update_bits(codec, TAIKO_A_CDC_CLK_RX_I2S_CTL,
-						0x03, (rx_fs_rate >> 0x05));
+					    0x03, (rx_fs_rate >> 0x05));
 		} else {
-			taiko->dai[dai->id - 1].rate   = params_rate(params);
+			taiko->dai[dai->id].rate   = params_rate(params);
 		}
+		break;
+	default:
+		pr_err("%s: Invalid stream type %d\n", __func__,
+			substream->stream);
+		return -EINVAL;
 	}
 
 	return 0;
@@ -4026,7 +3594,7 @@
 static struct snd_soc_dai_driver taiko_i2s_dai[] = {
 	{
 		.name = "taiko_i2s_rx1",
-		.id = 1,
+		.id = AIF1_PB,
 		.playback = {
 			.stream_name = "AIF1 Playback",
 			.rates = WCD9320_RATES,
@@ -4040,7 +3608,7 @@
 	},
 	{
 		.name = "taiko_i2s_tx1",
-		.id = 2,
+		.id = AIF1_CAP,
 		.capture = {
 			.stream_name = "AIF1 Capture",
 			.rates = WCD9320_RATES,
@@ -4055,125 +3623,77 @@
 };
 
 static int taiko_codec_enable_slimrx(struct snd_soc_dapm_widget *w,
-	struct snd_kcontrol *kcontrol, int event)
+				     struct snd_kcontrol *kcontrol,
+				     int event)
 {
-	struct wcd9xxx *taiko;
+	struct wcd9xxx *core;
 	struct snd_soc_codec *codec = w->codec;
 	struct taiko_priv *taiko_p = snd_soc_codec_get_drvdata(codec);
-	u32  j = 0;
 	u32  ret = 0;
-	codec->control_data = dev_get_drvdata(codec->dev->parent);
-	taiko = codec->control_data;
+	struct wcd9xxx_codec_dai_data *dai;
+
+	core = dev_get_drvdata(codec->dev->parent);
+
+	pr_debug("%s: event called! codec name %s num_dai %d\n"
+		"stream name %s event %d\n",
+		__func__, w->codec->name, w->codec->num_dai, w->sname, event);
+
 	/* Execute the callback only if interface type is slimbus */
 	if (taiko_p->intf_type != WCD9XXX_INTERFACE_TYPE_SLIMBUS)
 		return 0;
 
-	pr_debug("%s: %s %d\n", __func__, w->name, event);
+	dai = &taiko_p->dai[w->shift];
+	pr_debug("%s: w->name %s w->shift %d event %d\n",
+		 __func__, w->name, w->shift, event);
 
 	switch (event) {
 	case SND_SOC_DAPM_POST_PMU:
-		for (j = 0; j < ARRAY_SIZE(taiko_dai); j++) {
-			if ((taiko_dai[j].id == AIF1_CAP) ||
-			    (taiko_dai[j].id == AIF2_CAP) ||
-			    (taiko_dai[j].id == AIF3_CAP))
-				continue;
-			if (!strncmp(w->sname,
-				taiko_dai[j].playback.stream_name, 13)) {
-				++taiko_p->dai[j].ch_act;
-				break;
-			}
-		}
-		if (taiko_p->dai[j].ch_act == taiko_p->dai[j].ch_tot)
-			ret = wcd9xxx_cfg_slim_sch_rx(taiko,
-					taiko_p->dai[j].ch_num,
-					taiko_p->dai[j].ch_tot,
-					taiko_p->dai[j].rate);
+		ret = wcd9xxx_cfg_slim_sch_rx(core, &dai->wcd9xxx_ch_list,
+					      dai->rate, dai->bit_width,
+					      &dai->grph);
 		break;
 	case SND_SOC_DAPM_POST_PMD:
-		for (j = 0; j < ARRAY_SIZE(taiko_dai); j++) {
-			if ((taiko_dai[j].id == AIF1_CAP) ||
-			    (taiko_dai[j].id == AIF2_CAP) ||
-			    (taiko_dai[j].id == AIF3_CAP))
-				continue;
-			if (!strncmp(w->sname,
-				taiko_dai[j].playback.stream_name, 13)) {
-				--taiko_p->dai[j].ch_act;
-				break;
-			}
-		}
-		if (!taiko_p->dai[j].ch_act) {
-			ret = wcd9xxx_close_slim_sch_rx(taiko,
-						taiko_p->dai[j].ch_num,
-						taiko_p->dai[j].ch_tot);
-			usleep_range(15000, 15000);
-			taiko_p->dai[j].rate = 0;
-			memset(taiko_p->dai[j].ch_num, 0, (sizeof(u32)*
-					taiko_p->dai[j].ch_tot));
-			taiko_p->dai[j].ch_tot = 0;
-		}
+		ret = wcd9xxx_close_slim_sch_rx(core, &dai->wcd9xxx_ch_list,
+						dai->grph);
+		usleep_range(15000, 15000);
+		break;
 	}
 	return ret;
 }
 
 static int taiko_codec_enable_slimtx(struct snd_soc_dapm_widget *w,
-	struct snd_kcontrol *kcontrol, int event)
+				     struct snd_kcontrol *kcontrol,
+				     int event)
 {
-	struct wcd9xxx *taiko;
+	struct wcd9xxx *core;
 	struct snd_soc_codec *codec = w->codec;
 	struct taiko_priv *taiko_p = snd_soc_codec_get_drvdata(codec);
-	/* index to the DAI ID, for now hardcoding */
-	u32  j = 0;
 	u32  ret = 0;
+	struct wcd9xxx_codec_dai_data *dai;
 
-	codec->control_data = dev_get_drvdata(codec->dev->parent);
-	taiko = codec->control_data;
+	core = dev_get_drvdata(codec->dev->parent);
+
+	pr_debug("%s: event called! codec name %s num_dai %d stream name %s\n",
+		__func__, w->codec->name, w->codec->num_dai, w->sname);
 
 	/* Execute the callback only if interface type is slimbus */
 	if (taiko_p->intf_type != WCD9XXX_INTERFACE_TYPE_SLIMBUS)
 		return 0;
 
-	pr_debug("%s(): %s %d\n", __func__, w->name, event);
+	pr_debug("%s(): w->name %s event %d w->shift %d\n",
+		__func__, w->name, event, w->shift);
 
+	dai = &taiko_p->dai[w->shift];
 	switch (event) {
 	case SND_SOC_DAPM_POST_PMU:
-		for (j = 0; j < ARRAY_SIZE(taiko_dai); j++) {
-			if (taiko_dai[j].id == AIF1_PB ||
-				taiko_dai[j].id == AIF2_PB ||
-				taiko_dai[j].id == AIF3_PB)
-				continue;
-			if (!strncmp(w->sname,
-				taiko_dai[j].capture.stream_name, 13)) {
-				++taiko_p->dai[j].ch_act;
-				break;
-			}
-		}
-		if (taiko_p->dai[j].ch_act == taiko_p->dai[j].ch_tot)
-			ret = wcd9xxx_cfg_slim_sch_tx(taiko,
-						taiko_p->dai[j].ch_num,
-						taiko_p->dai[j].ch_tot,
-						taiko_p->dai[j].rate);
+		ret = wcd9xxx_cfg_slim_sch_tx(core, &dai->wcd9xxx_ch_list,
+					      dai->rate, dai->bit_width,
+					      &dai->grph);
 		break;
 	case SND_SOC_DAPM_POST_PMD:
-		for (j = 0; j < ARRAY_SIZE(taiko_dai); j++) {
-			if (taiko_dai[j].id == AIF1_PB ||
-				taiko_dai[j].id == AIF2_PB ||
-				taiko_dai[j].id == AIF3_PB)
-				continue;
-			if (!strncmp(w->sname,
-				taiko_dai[j].capture.stream_name, 13)) {
-				--taiko_p->dai[j].ch_act;
-				break;
-			}
-		}
-		if (!taiko_p->dai[j].ch_act) {
-			ret = wcd9xxx_close_slim_sch_tx(taiko,
-						taiko_p->dai[j].ch_num,
-						taiko_p->dai[j].ch_tot);
-			taiko_p->dai[j].rate = 0;
-			memset(taiko_p->dai[j].ch_num, 0, (sizeof(u32)*
-					taiko_p->dai[j].ch_tot));
-			taiko_p->dai[j].ch_tot = 0;
-		}
+		ret = wcd9xxx_close_slim_sch_tx(core, &dai->wcd9xxx_ch_list,
+						dai->grph);
+		break;
 	}
 	return ret;
 }
@@ -4213,29 +3733,38 @@
 	SND_SOC_DAPM_MIXER("DAC1", TAIKO_A_RX_EAR_EN, 6, 0, dac1_switch,
 		ARRAY_SIZE(dac1_switch)),
 
-	SND_SOC_DAPM_AIF_IN_E("SLIM RX1", "AIF1 Playback", 0, SND_SOC_NOPM, 0,
-				0, taiko_codec_enable_slimrx,
+	SND_SOC_DAPM_AIF_IN_E("AIF1 PB", "AIF1 Playback", 0, SND_SOC_NOPM,
+				AIF1_PB, 0, taiko_codec_enable_slimrx,
 				SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
-	SND_SOC_DAPM_AIF_IN_E("SLIM RX2", "AIF1 Playback", 0, SND_SOC_NOPM, 0,
-				0, taiko_codec_enable_slimrx,
+	SND_SOC_DAPM_AIF_IN_E("AIF2 PB", "AIF2 Playback", 0, SND_SOC_NOPM,
+				AIF2_PB, 0, taiko_codec_enable_slimrx,
 				SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
-	SND_SOC_DAPM_AIF_IN_E("SLIM RX3", "AIF1 Playback", 0, SND_SOC_NOPM, 0,
-				0, taiko_codec_enable_slimrx,
+	SND_SOC_DAPM_AIF_IN_E("AIF3 PB", "AIF3 Playback", 0, SND_SOC_NOPM,
+				AIF3_PB, 0, taiko_codec_enable_slimrx,
 				SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
 
-	SND_SOC_DAPM_AIF_IN_E("SLIM RX4", "AIF3 Playback", 0, SND_SOC_NOPM, 0,
-				0, taiko_codec_enable_slimrx,
-				SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
-	SND_SOC_DAPM_AIF_IN_E("SLIM RX5", "AIF3 Playback", 0, SND_SOC_NOPM, 0,
-				0, taiko_codec_enable_slimrx,
-				SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_MUX("SLIM RX1 MUX", SND_SOC_NOPM, TAIKO_RX1, 0,
+				&slim_rx_mux[TAIKO_RX1]),
+	SND_SOC_DAPM_MUX("SLIM RX2 MUX", SND_SOC_NOPM, TAIKO_RX2, 0,
+				&slim_rx_mux[TAIKO_RX2]),
+	SND_SOC_DAPM_MUX("SLIM RX3 MUX", SND_SOC_NOPM, TAIKO_RX3, 0,
+				&slim_rx_mux[TAIKO_RX3]),
+	SND_SOC_DAPM_MUX("SLIM RX4 MUX", SND_SOC_NOPM, TAIKO_RX4, 0,
+				&slim_rx_mux[TAIKO_RX4]),
+	SND_SOC_DAPM_MUX("SLIM RX5 MUX", SND_SOC_NOPM, TAIKO_RX5, 0,
+				&slim_rx_mux[TAIKO_RX5]),
+	SND_SOC_DAPM_MUX("SLIM RX6 MUX", SND_SOC_NOPM, TAIKO_RX6, 0,
+				&slim_rx_mux[TAIKO_RX6]),
+	SND_SOC_DAPM_MUX("SLIM RX7 MUX", SND_SOC_NOPM, TAIKO_RX7, 0,
+				&slim_rx_mux[TAIKO_RX7]),
 
-	SND_SOC_DAPM_AIF_IN_E("SLIM RX6", "AIF2 Playback", 0, SND_SOC_NOPM, 0,
-				0, taiko_codec_enable_slimrx,
-				SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
-	SND_SOC_DAPM_AIF_IN_E("SLIM RX7", "AIF2 Playback", 0, SND_SOC_NOPM, 0,
-				0, taiko_codec_enable_slimrx,
-				SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_MIXER("SLIM RX1", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("SLIM RX2", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("SLIM RX3", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("SLIM RX4", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("SLIM RX5", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("SLIM RX6", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("SLIM RX7", SND_SOC_NOPM, 0, 0, NULL, 0),
 
 	/* Headphone */
 	SND_SOC_DAPM_OUTPUT("HEADPHONE"),
@@ -4297,13 +3826,17 @@
 			   taiko_spk_dac_event,
 			   SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
 
+	SND_SOC_DAPM_MIXER("RX1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("RX2 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("RX7 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0),
+
 	SND_SOC_DAPM_MIXER_E("RX1 MIX2", TAIKO_A_CDC_CLK_RX_B1_CTL, 0, 0, NULL,
 		0, taiko_codec_enable_interpolator, SND_SOC_DAPM_PRE_PMU |
 		SND_SOC_DAPM_POST_PMU),
 	SND_SOC_DAPM_MIXER_E("RX2 MIX2", TAIKO_A_CDC_CLK_RX_B1_CTL, 1, 0, NULL,
 		0, taiko_codec_enable_interpolator, SND_SOC_DAPM_PRE_PMU |
 		SND_SOC_DAPM_POST_PMU),
-	SND_SOC_DAPM_MIXER_E("RX7 MIX2", TAIKO_A_CDC_CLK_RX_B1_CTL, 2, 0, NULL,
+	SND_SOC_DAPM_MIXER_E("RX3 MIX1", TAIKO_A_CDC_CLK_RX_B1_CTL, 2, 0, NULL,
 		0, taiko_codec_enable_interpolator, SND_SOC_DAPM_PRE_PMU |
 		SND_SOC_DAPM_POST_PMU),
 	SND_SOC_DAPM_MIXER_E("RX4 MIX1", TAIKO_A_CDC_CLK_RX_B1_CTL, 3, 0, NULL,
@@ -4315,21 +3848,10 @@
 	SND_SOC_DAPM_MIXER_E("RX6 MIX1", TAIKO_A_CDC_CLK_RX_B1_CTL, 5, 0, NULL,
 		0, taiko_codec_enable_interpolator, SND_SOC_DAPM_PRE_PMU |
 		SND_SOC_DAPM_POST_PMU),
-	SND_SOC_DAPM_MIXER_E("RX7 MIX1", TAIKO_A_CDC_CLK_RX_B1_CTL, 6, 0, NULL,
+	SND_SOC_DAPM_MIXER_E("RX7 MIX2", TAIKO_A_CDC_CLK_RX_B1_CTL, 6, 0, NULL,
 		0, taiko_codec_enable_interpolator, SND_SOC_DAPM_PRE_PMU |
 		SND_SOC_DAPM_POST_PMU),
 
-	SND_SOC_DAPM_MIXER("RX1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0),
-	SND_SOC_DAPM_MIXER("RX2 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0),
-	SND_SOC_DAPM_MIXER("RX3 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0),
-
-	SND_SOC_DAPM_MUX_E("RX4 DSM MUX", TAIKO_A_CDC_CLK_RX_B1_CTL, 3, 0,
-		&rx4_dsm_mux, taiko_codec_enable_interpolator,
-		SND_SOC_DAPM_PRE_PMU),
-
-	SND_SOC_DAPM_MUX_E("RX6 DSM MUX", TAIKO_A_CDC_CLK_RX_B1_CTL, 5, 0,
-		&rx6_dsm_mux, taiko_codec_enable_interpolator,
-		SND_SOC_DAPM_PRE_PMU),
 
 	SND_SOC_DAPM_MIXER("RX1 CHAIN", TAIKO_A_CDC_RX1_B6_CTL, 5, 0, NULL, 0),
 	SND_SOC_DAPM_MIXER("RX2 CHAIN", TAIKO_A_CDC_RX2_B6_CTL, 5, 0, NULL, 0),
@@ -4377,6 +3899,11 @@
 	SND_SOC_DAPM_MUX("RX7 MIX2 INP2", SND_SOC_NOPM, 0, 0,
 		&rx7_mix2_inp2_mux),
 
+	SND_SOC_DAPM_MUX("RDAC5 MUX", SND_SOC_NOPM, 0, 0,
+		&rx_dac5_mux),
+	SND_SOC_DAPM_MUX("RDAC7 MUX", SND_SOC_NOPM, 0, 0,
+		&rx_dac7_mux),
+
 	SND_SOC_DAPM_SUPPLY("CLASS_H_CLK", TAIKO_A_CDC_CLK_OTHR_CTL, 0, 0,
 		taiko_codec_enable_class_h_clk, SND_SOC_DAPM_PRE_PMU |
 		SND_SOC_DAPM_PRE_PMD),
@@ -4390,6 +3917,9 @@
 	SND_SOC_DAPM_SUPPLY("CLASS_H_HPH_R", TAIKO_A_CDC_CLSH_B1_CTL, 2, 0,
 		taiko_codec_enable_class_h, SND_SOC_DAPM_POST_PMU),
 
+	SND_SOC_DAPM_SUPPLY("CLASS_H_LINEOUTS_PA", SND_SOC_NOPM, 0, 0,
+		taiko_codec_enable_class_h, SND_SOC_DAPM_POST_PMU),
+
 	SND_SOC_DAPM_SUPPLY("CP", TAIKO_A_NCP_EN, 0, 0,
 		taiko_codec_enable_charge_pump, SND_SOC_DAPM_PRE_PMU |
 		SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
@@ -4536,55 +4066,47 @@
 		taiko_codec_enable_adc, SND_SOC_DAPM_PRE_PMU |
 		SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
 
-	SND_SOC_DAPM_MUX("SLIM TX1 MUX", SND_SOC_NOPM, 0, 0, &sb_tx1_mux),
-	SND_SOC_DAPM_AIF_OUT_E("SLIM TX1", "AIF2 Capture", 0, SND_SOC_NOPM, 0,
-				0, taiko_codec_enable_slimtx,
-				SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_AIF_OUT_E("AIF1 CAP", "AIF1 Capture", 0, SND_SOC_NOPM,
+		AIF1_CAP, 0, taiko_codec_enable_slimtx,
+		SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
 
-	SND_SOC_DAPM_MUX("SLIM TX2 MUX", SND_SOC_NOPM, 0, 0, &sb_tx2_mux),
-	SND_SOC_DAPM_AIF_OUT_E("SLIM TX2", "AIF2 Capture", 0, SND_SOC_NOPM, 0,
-				0, taiko_codec_enable_slimtx,
-				SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_AIF_OUT_E("AIF2 CAP", "AIF2 Capture", 0, SND_SOC_NOPM,
+		AIF2_CAP, 0, taiko_codec_enable_slimtx,
+		SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
 
-	SND_SOC_DAPM_MUX("SLIM TX3 MUX", SND_SOC_NOPM, 0, 0, &sb_tx3_mux),
-	SND_SOC_DAPM_AIF_OUT_E("SLIM TX3", "AIF3 Capture", 0, SND_SOC_NOPM, 0,
-				0, taiko_codec_enable_slimtx,
-				SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_AIF_OUT_E("AIF3 CAP", "AIF3 Capture", 0, SND_SOC_NOPM,
+		AIF3_CAP, 0, taiko_codec_enable_slimtx,
+		SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
 
-	SND_SOC_DAPM_MUX("SLIM TX4 MUX", SND_SOC_NOPM, 0, 0, &sb_tx4_mux),
-	SND_SOC_DAPM_AIF_OUT_E("SLIM TX4", "AIF2 Capture", 0, SND_SOC_NOPM, 0,
-				0, taiko_codec_enable_slimtx,
-				SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_MIXER("AIF1_CAP Mixer", SND_SOC_NOPM, AIF1_CAP, 0,
+		aif_cap_mixer, ARRAY_SIZE(aif_cap_mixer)),
 
-	SND_SOC_DAPM_MUX("SLIM TX5 MUX", SND_SOC_NOPM, 0, 0, &sb_tx5_mux),
-	SND_SOC_DAPM_AIF_OUT_E("SLIM TX5", "AIF3 Capture", 0, SND_SOC_NOPM, 0,
-				0, taiko_codec_enable_slimtx,
-				SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_MIXER("AIF2_CAP Mixer", SND_SOC_NOPM, AIF2_CAP, 0,
+		aif_cap_mixer, ARRAY_SIZE(aif_cap_mixer)),
 
-	SND_SOC_DAPM_MUX("SLIM TX6 MUX", SND_SOC_NOPM, 0, 0, &sb_tx6_mux),
-	SND_SOC_DAPM_AIF_OUT_E("SLIM TX6", "AIF2 Capture", 0, SND_SOC_NOPM, 0,
-				0, taiko_codec_enable_slimtx,
-				SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_MIXER("AIF3_CAP Mixer", SND_SOC_NOPM, AIF3_CAP, 0,
+		aif_cap_mixer, ARRAY_SIZE(aif_cap_mixer)),
 
-	SND_SOC_DAPM_MUX("SLIM TX7 MUX", SND_SOC_NOPM, 0, 0, &sb_tx7_mux),
-	SND_SOC_DAPM_AIF_OUT_E("SLIM TX7", "AIF1 Capture", 0, SND_SOC_NOPM, 0,
-				0, taiko_codec_enable_slimtx,
-				SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
-
-	SND_SOC_DAPM_MUX("SLIM TX8 MUX", SND_SOC_NOPM, 0, 0, &sb_tx8_mux),
-	SND_SOC_DAPM_AIF_OUT_E("SLIM TX8", "AIF1 Capture", 0, SND_SOC_NOPM, 0,
-				0, taiko_codec_enable_slimtx,
-				SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
-
-	SND_SOC_DAPM_MUX("SLIM TX9 MUX", SND_SOC_NOPM, 0, 0, &sb_tx9_mux),
-	SND_SOC_DAPM_AIF_OUT_E("SLIM TX9", "AIF1 Capture", NULL, SND_SOC_NOPM,
-			0, 0, taiko_codec_enable_slimtx,
-			SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
-
-	SND_SOC_DAPM_MUX("SLIM TX10 MUX", SND_SOC_NOPM, 0, 0, &sb_tx10_mux),
-	SND_SOC_DAPM_AIF_OUT_E("SLIM TX10", "AIF1 Capture", NULL, SND_SOC_NOPM,
-			0, 0, taiko_codec_enable_slimtx,
-			SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_MUX("SLIM TX1 MUX", SND_SOC_NOPM, TAIKO_TX1, 0,
+		&sb_tx1_mux),
+	SND_SOC_DAPM_MUX("SLIM TX2 MUX", SND_SOC_NOPM, TAIKO_TX2, 0,
+		&sb_tx2_mux),
+	SND_SOC_DAPM_MUX("SLIM TX3 MUX", SND_SOC_NOPM, TAIKO_TX3, 0,
+		&sb_tx3_mux),
+	SND_SOC_DAPM_MUX("SLIM TX4 MUX", SND_SOC_NOPM, TAIKO_TX4, 0,
+		&sb_tx4_mux),
+	SND_SOC_DAPM_MUX("SLIM TX5 MUX", SND_SOC_NOPM, TAIKO_TX5, 0,
+		&sb_tx5_mux),
+	SND_SOC_DAPM_MUX("SLIM TX6 MUX", SND_SOC_NOPM, TAIKO_TX6, 0,
+		&sb_tx6_mux),
+	SND_SOC_DAPM_MUX("SLIM TX7 MUX", SND_SOC_NOPM, TAIKO_TX7, 0,
+		&sb_tx7_mux),
+	SND_SOC_DAPM_MUX("SLIM TX8 MUX", SND_SOC_NOPM, TAIKO_TX8, 0,
+		&sb_tx8_mux),
+	SND_SOC_DAPM_MUX("SLIM TX9 MUX", SND_SOC_NOPM, TAIKO_TX9, 0,
+		&sb_tx9_mux),
+	SND_SOC_DAPM_MUX("SLIM TX10 MUX", SND_SOC_NOPM, TAIKO_TX10, 0,
+		&sb_tx10_mux),
 
 	/* Digital Mic Inputs */
 	SND_SOC_DAPM_ADC_E("DMIC1", NULL, SND_SOC_NOPM, 0, 0,
@@ -4648,2200 +4170,6 @@
 
 };
 
-static short taiko_codec_read_sta_result(struct snd_soc_codec *codec)
-{
-	u8 bias_msb, bias_lsb;
-	short bias_value;
-
-	bias_msb = snd_soc_read(codec, TAIKO_A_CDC_MBHC_B3_STATUS);
-	bias_lsb = snd_soc_read(codec, TAIKO_A_CDC_MBHC_B2_STATUS);
-	bias_value = (bias_msb << 8) | bias_lsb;
-	return bias_value;
-}
-
-static short taiko_codec_read_dce_result(struct snd_soc_codec *codec)
-{
-	u8 bias_msb, bias_lsb;
-	short bias_value;
-
-	bias_msb = snd_soc_read(codec, TAIKO_A_CDC_MBHC_B5_STATUS);
-	bias_lsb = snd_soc_read(codec, TAIKO_A_CDC_MBHC_B4_STATUS);
-	bias_value = (bias_msb << 8) | bias_lsb;
-	return bias_value;
-}
-
-static void taiko_turn_onoff_rel_detection(struct snd_soc_codec *codec, bool on)
-{
-	snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_B1_CTL, 0x02, on << 1);
-}
-
-static short __taiko_codec_sta_dce(struct snd_soc_codec *codec, int dce,
-				   bool override_bypass, bool noreldetection)
-{
-	short bias_value;
-	struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
-
-	wcd9xxx_disable_irq(codec->control_data, TAIKO_IRQ_MBHC_POTENTIAL);
-	if (noreldetection)
-		taiko_turn_onoff_rel_detection(codec, false);
-
-	/* Turn on the override */
-	if (!override_bypass)
-		snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_B1_CTL, 0x4, 0x4);
-	if (dce) {
-		snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_CLK_CTL, 0x8, 0x8);
-		snd_soc_write(codec, TAIKO_A_CDC_MBHC_EN_CTL, 0x4);
-		snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_CLK_CTL, 0x8, 0x0);
-		usleep_range(taiko->mbhc_data.t_sta_dce,
-			     taiko->mbhc_data.t_sta_dce);
-		snd_soc_write(codec, TAIKO_A_CDC_MBHC_EN_CTL, 0x4);
-		usleep_range(taiko->mbhc_data.t_dce,
-			     taiko->mbhc_data.t_dce);
-		bias_value = taiko_codec_read_dce_result(codec);
-	} else {
-		snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_CLK_CTL, 0x8, 0x8);
-		snd_soc_write(codec, TAIKO_A_CDC_MBHC_EN_CTL, 0x2);
-		snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_CLK_CTL, 0x8, 0x0);
-		usleep_range(taiko->mbhc_data.t_sta_dce,
-			     taiko->mbhc_data.t_sta_dce);
-		snd_soc_write(codec, TAIKO_A_CDC_MBHC_EN_CTL, 0x2);
-		usleep_range(taiko->mbhc_data.t_sta,
-			     taiko->mbhc_data.t_sta);
-		bias_value = taiko_codec_read_sta_result(codec);
-		snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_CLK_CTL, 0x8, 0x8);
-		snd_soc_write(codec, TAIKO_A_CDC_MBHC_EN_CTL, 0x0);
-	}
-	/* Turn off the override after measuring mic voltage */
-	if (!override_bypass)
-		snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_B1_CTL, 0x04, 0x00);
-
-	if (noreldetection)
-		taiko_turn_onoff_rel_detection(codec, true);
-	wcd9xxx_enable_irq(codec->control_data, TAIKO_IRQ_MBHC_POTENTIAL);
-
-	return bias_value;
-}
-
-static short taiko_codec_sta_dce(struct snd_soc_codec *codec, int dce,
-				 bool norel)
-{
-	return __taiko_codec_sta_dce(codec, dce, false, norel);
-}
-
-/* called only from interrupt which is under codec_resource_lock acquisition */
-static short taiko_codec_setup_hs_polling(struct snd_soc_codec *codec)
-{
-	struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
-	short bias_value;
-	u8 cfilt_mode;
-
-	pr_debug("%s: enter, mclk_enabled %d\n", __func__, taiko->mclk_enabled);
-	if (!taiko->mbhc_cfg.calibration) {
-		pr_err("Error, no taiko calibration\n");
-		return -ENODEV;
-	}
-
-	if (!taiko->mclk_enabled) {
-		taiko_codec_disable_clock_block(codec);
-		taiko_codec_enable_bandgap(codec, TAIKO_BANDGAP_MBHC_MODE);
-		taiko_enable_rx_bias(codec, 1);
-		taiko_codec_enable_clock_block(codec, 1);
-	}
-
-	snd_soc_update_bits(codec, TAIKO_A_CLK_BUFF_EN1, 0x05, 0x01);
-
-	/* Make sure CFILT is in fast mode, save current mode */
-	cfilt_mode = snd_soc_read(codec, taiko->mbhc_bias_regs.cfilt_ctl);
-	snd_soc_update_bits(codec, taiko->mbhc_bias_regs.cfilt_ctl, 0x70, 0x00);
-
-	snd_soc_update_bits(codec, taiko->mbhc_bias_regs.ctl_reg, 0x1F, 0x16);
-
-	snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_CLK_CTL, 0x2, 0x2);
-	snd_soc_write(codec, TAIKO_A_MBHC_SCALING_MUX_1, 0x84);
-
-	snd_soc_update_bits(codec, TAIKO_A_TX_7_MBHC_EN, 0x80, 0x80);
-	snd_soc_update_bits(codec, TAIKO_A_TX_7_MBHC_EN, 0x1F, 0x1C);
-	snd_soc_update_bits(codec, TAIKO_A_TX_7_MBHC_TEST_CTL, 0x40, 0x40);
-
-	snd_soc_update_bits(codec, TAIKO_A_TX_7_MBHC_EN, 0x80, 0x00);
-	snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_CLK_CTL, 0x8, 0x8);
-	snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_CLK_CTL, 0x8, 0x00);
-
-	snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_B1_CTL, 0x2, 0x2);
-	snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_CLK_CTL, 0x8, 0x8);
-
-	taiko_codec_calibrate_hs_polling(codec);
-
-	/* don't flip override */
-	bias_value = __taiko_codec_sta_dce(codec, 1, true, true);
-	snd_soc_update_bits(codec, taiko->mbhc_bias_regs.cfilt_ctl, 0x40,
-			    cfilt_mode);
-	snd_soc_update_bits(codec, TAIKO_A_MBHC_HPH, 0x13, 0x00);
-
-	return bias_value;
-}
-
-static int taiko_cancel_btn_work(struct taiko_priv *taiko)
-{
-	int r = 0;
-	struct wcd9xxx *core = dev_get_drvdata(taiko->codec->dev->parent);
-
-	if (cancel_delayed_work_sync(&taiko->mbhc_btn_dwork)) {
-		/* if scheduled mbhc_btn_dwork is canceled from here,
-		* we have to unlock from here instead btn_work */
-		wcd9xxx_unlock_sleep(core);
-		r = 1;
-	}
-	return r;
-}
-
-/* called under codec_resource_lock acquisition */
-void taiko_set_and_turnoff_hph_padac(struct snd_soc_codec *codec)
-{
-	struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
-	u8 wg_time;
-
-	wg_time = snd_soc_read(codec, TAIKO_A_RX_HPH_CNP_WG_TIME) ;
-	wg_time += 1;
-
-	/* If headphone PA is on, check if userspace receives
-	 * removal event to sync-up PA's state */
-	if (taiko_is_hph_pa_on(codec)) {
-		pr_debug("%s PA is on, setting PA_OFF_ACK\n", __func__);
-		set_bit(TAIKO_HPHL_PA_OFF_ACK, &taiko->hph_pa_dac_state);
-		set_bit(TAIKO_HPHR_PA_OFF_ACK, &taiko->hph_pa_dac_state);
-	} else {
-		pr_debug("%s PA is off\n", __func__);
-	}
-
-	if (taiko_is_hph_dac_on(codec, 1))
-		set_bit(TAIKO_HPHL_DAC_OFF_ACK, &taiko->hph_pa_dac_state);
-	if (taiko_is_hph_dac_on(codec, 0))
-		set_bit(TAIKO_HPHR_DAC_OFF_ACK, &taiko->hph_pa_dac_state);
-
-	snd_soc_update_bits(codec, TAIKO_A_RX_HPH_CNP_EN, 0x30, 0x00);
-	snd_soc_update_bits(codec, TAIKO_A_RX_HPH_L_DAC_CTL,
-			    0xC0, 0x00);
-	snd_soc_update_bits(codec, TAIKO_A_RX_HPH_R_DAC_CTL,
-			    0xC0, 0x00);
-	usleep_range(wg_time * 1000, wg_time * 1000);
-}
-
-static void taiko_clr_and_turnon_hph_padac(struct taiko_priv *taiko)
-{
-	bool pa_turned_on = false;
-	struct snd_soc_codec *codec = taiko->codec;
-	u8 wg_time;
-
-	wg_time = snd_soc_read(codec, TAIKO_A_RX_HPH_CNP_WG_TIME) ;
-	wg_time += 1;
-
-	if (test_and_clear_bit(TAIKO_HPHR_DAC_OFF_ACK,
-			       &taiko->hph_pa_dac_state)) {
-		pr_debug("%s: HPHR clear flag and enable DAC\n", __func__);
-		snd_soc_update_bits(taiko->codec, TAIKO_A_RX_HPH_R_DAC_CTL,
-				    0xC0, 0xC0);
-	}
-	if (test_and_clear_bit(TAIKO_HPHL_DAC_OFF_ACK,
-			       &taiko->hph_pa_dac_state)) {
-		pr_debug("%s: HPHL clear flag and enable DAC\n", __func__);
-		snd_soc_update_bits(taiko->codec, TAIKO_A_RX_HPH_L_DAC_CTL,
-				    0xC0, 0xC0);
-	}
-
-	if (test_and_clear_bit(TAIKO_HPHR_PA_OFF_ACK,
-			       &taiko->hph_pa_dac_state)) {
-		pr_debug("%s: HPHR clear flag and enable PA\n", __func__);
-		snd_soc_update_bits(taiko->codec, TAIKO_A_RX_HPH_CNP_EN, 0x10,
-				    1 << 4);
-		pa_turned_on = true;
-	}
-	if (test_and_clear_bit(TAIKO_HPHL_PA_OFF_ACK,
-			       &taiko->hph_pa_dac_state)) {
-		pr_debug("%s: HPHL clear flag and enable PA\n", __func__);
-		snd_soc_update_bits(taiko->codec, TAIKO_A_RX_HPH_CNP_EN, 0x20,
-				    1 << 5);
-		pa_turned_on = true;
-	}
-
-	if (pa_turned_on) {
-		pr_debug("%s: PA was turned off by MBHC and not by DAPM\n",
-				__func__);
-		usleep_range(wg_time * 1000, wg_time * 1000);
-	}
-}
-
-/* called under codec_resource_lock acquisition */
-static void taiko_codec_report_plug(struct snd_soc_codec *codec, int insertion,
-				    enum snd_jack_types jack_type)
-{
-	struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
-
-	if (!insertion) {
-		/* Report removal */
-		taiko->hph_status &= ~jack_type;
-		if (taiko->mbhc_cfg.headset_jack) {
-			/* cancel possibly scheduled btn work and
-			* report release if we reported button press */
-			if (taiko_cancel_btn_work(taiko)) {
-				pr_debug("%s: button press is canceled\n",
-					__func__);
-			} else if (taiko->buttons_pressed) {
-				pr_debug("%s: release of button press%d\n",
-					  __func__, jack_type);
-				taiko_snd_soc_jack_report(taiko,
-						 taiko->mbhc_cfg.button_jack, 0,
-						 taiko->buttons_pressed);
-				taiko->buttons_pressed &=
-							~TAIKO_JACK_BUTTON_MASK;
-			}
-			pr_debug("%s: Reporting removal %d(%x)\n", __func__,
-				 jack_type, taiko->hph_status);
-			taiko_snd_soc_jack_report(taiko,
-						  taiko->mbhc_cfg.headset_jack,
-						  taiko->hph_status,
-						  TAIKO_JACK_MASK);
-		}
-		taiko_set_and_turnoff_hph_padac(codec);
-		hphocp_off_report(taiko, SND_JACK_OC_HPHR,
-				  TAIKO_IRQ_HPH_PA_OCPR_FAULT);
-		hphocp_off_report(taiko, SND_JACK_OC_HPHL,
-				  TAIKO_IRQ_HPH_PA_OCPL_FAULT);
-		taiko->current_plug = PLUG_TYPE_NONE;
-		taiko->mbhc_polling_active = false;
-	} else {
-		/* Report insertion */
-		taiko->hph_status |= jack_type;
-
-		if (jack_type == SND_JACK_HEADPHONE)
-			taiko->current_plug = PLUG_TYPE_HEADPHONE;
-		else if (jack_type == SND_JACK_UNSUPPORTED)
-			taiko->current_plug = PLUG_TYPE_GND_MIC_SWAP;
-		else if (jack_type == SND_JACK_HEADSET) {
-			taiko->mbhc_polling_active = true;
-			taiko->current_plug = PLUG_TYPE_HEADSET;
-		}
-		if (taiko->mbhc_cfg.headset_jack) {
-			pr_debug("%s: Reporting insertion %d(%x)\n", __func__,
-				 jack_type, taiko->hph_status);
-			taiko_snd_soc_jack_report(taiko,
-						  taiko->mbhc_cfg.headset_jack,
-						  taiko->hph_status,
-						  TAIKO_JACK_MASK);
-		}
-		taiko_clr_and_turnon_hph_padac(taiko);
-	}
-}
-
-static int taiko_codec_enable_hs_detect(struct snd_soc_codec *codec,
-					int insertion, int trigger,
-					bool padac_off)
-{
-	struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
-	int central_bias_enabled = 0;
-	const struct taiko_mbhc_general_cfg *generic =
-	    TAIKO_MBHC_CAL_GENERAL_PTR(taiko->mbhc_cfg.calibration);
-	const struct taiko_mbhc_plug_detect_cfg *plug_det =
-	    TAIKO_MBHC_CAL_PLUG_DET_PTR(taiko->mbhc_cfg.calibration);
-
-	if (!taiko->mbhc_cfg.calibration) {
-		pr_err("Error, no taiko calibration\n");
-		return -EINVAL;
-	}
-
-	snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_INT_CTL, 0x1, 0);
-
-	/* Make sure mic bias and Mic line schmitt trigger
-	 * are turned OFF
-	 */
-	snd_soc_update_bits(codec, taiko->mbhc_bias_regs.ctl_reg, 0x01, 0x01);
-	snd_soc_update_bits(codec, taiko->mbhc_bias_regs.mbhc_reg, 0x90, 0x00);
-
-	if (insertion) {
-		taiko_codec_switch_micbias(codec, 0);
-
-		/* DAPM can manipulate PA/DAC bits concurrently */
-		if (padac_off == true)
-			taiko_set_and_turnoff_hph_padac(codec);
-
-		if (trigger & MBHC_USE_HPHL_TRIGGER) {
-			/* Enable HPH Schmitt Trigger */
-			snd_soc_update_bits(codec, TAIKO_A_MBHC_HPH, 0x11,
-					    0x11);
-			snd_soc_update_bits(codec, TAIKO_A_MBHC_HPH, 0x0C,
-					    plug_det->hph_current << 2);
-			snd_soc_update_bits(codec, TAIKO_A_MBHC_HPH, 0x02,
-					    0x02);
-		}
-		if (trigger & MBHC_USE_MB_TRIGGER) {
-			/* enable the mic line schmitt trigger */
-			snd_soc_update_bits(codec,
-					    taiko->mbhc_bias_regs.mbhc_reg,
-					    0x60, plug_det->mic_current << 5);
-			snd_soc_update_bits(codec,
-					    taiko->mbhc_bias_regs.mbhc_reg,
-					    0x80, 0x80);
-			usleep_range(plug_det->t_mic_pid, plug_det->t_mic_pid);
-			snd_soc_update_bits(codec,
-					    taiko->mbhc_bias_regs.ctl_reg, 0x01,
-					    0x00);
-			snd_soc_update_bits(codec,
-					    taiko->mbhc_bias_regs.mbhc_reg,
-					    0x10, 0x10);
-		}
-
-		/* setup for insetion detection */
-		snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_INT_CTL, 0x2, 0);
-	} else {
-		pr_debug("setup for removal detection\n");
-		/* Make sure the HPH schmitt trigger is OFF */
-		snd_soc_update_bits(codec, TAIKO_A_MBHC_HPH, 0x12, 0x00);
-
-		/* enable the mic line schmitt trigger */
-		snd_soc_update_bits(codec, taiko->mbhc_bias_regs.ctl_reg,
-				    0x01, 0x00);
-		snd_soc_update_bits(codec, taiko->mbhc_bias_regs.mbhc_reg, 0x60,
-				    plug_det->mic_current << 5);
-		snd_soc_update_bits(codec, taiko->mbhc_bias_regs.mbhc_reg,
-			0x80, 0x80);
-		usleep_range(plug_det->t_mic_pid, plug_det->t_mic_pid);
-		snd_soc_update_bits(codec, taiko->mbhc_bias_regs.mbhc_reg,
-			0x10, 0x10);
-
-		/* Setup for low power removal detection */
-		snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_INT_CTL, 0x2, 0x2);
-	}
-
-	if (snd_soc_read(codec, TAIKO_A_CDC_MBHC_B1_CTL) & 0x4) {
-		/* called called by interrupt */
-		if (!(taiko->clock_active)) {
-			taiko_codec_enable_config_mode(codec, 1);
-			snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_B1_CTL,
-				0x06, 0);
-			usleep_range(generic->t_shutdown_plug_rem,
-				     generic->t_shutdown_plug_rem);
-			taiko_codec_enable_config_mode(codec, 0);
-		} else
-			snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_B1_CTL,
-				0x06, 0);
-	}
-
-	snd_soc_update_bits(codec, taiko->mbhc_bias_regs.int_rbias, 0x80, 0);
-
-	/* If central bandgap disabled */
-	if (!(snd_soc_read(codec, TAIKO_A_PIN_CTL_OE1) & 1)) {
-		snd_soc_update_bits(codec, TAIKO_A_PIN_CTL_OE1, 0x3, 0x3);
-		usleep_range(generic->t_bg_fast_settle,
-			     generic->t_bg_fast_settle);
-		central_bias_enabled = 1;
-	}
-
-	/* If LDO_H disabled */
-	if (snd_soc_read(codec, TAIKO_A_PIN_CTL_OE0) & 0x80) {
-		snd_soc_update_bits(codec, TAIKO_A_PIN_CTL_OE0, 0x10, 0);
-		snd_soc_update_bits(codec, TAIKO_A_PIN_CTL_OE0, 0x80, 0x80);
-		usleep_range(generic->t_ldoh, generic->t_ldoh);
-		snd_soc_update_bits(codec, TAIKO_A_PIN_CTL_OE0, 0x80, 0);
-
-		if (central_bias_enabled)
-			snd_soc_update_bits(codec, TAIKO_A_PIN_CTL_OE1, 0x1, 0);
-	}
-
-	snd_soc_update_bits(codec, taiko->reg_addr.micb_4_mbhc, 0x3,
-			    taiko->mbhc_cfg.micbias);
-
-	wcd9xxx_enable_irq(codec->control_data, TAIKO_IRQ_MBHC_INSERTION);
-	snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_INT_CTL, 0x1, 0x1);
-	return 0;
-}
-
-static u16 taiko_codec_v_sta_dce(struct snd_soc_codec *codec, bool dce,
-				 s16 vin_mv)
-{
-	struct taiko_priv *taiko;
-	s16 diff, zero;
-	u32 mb_mv, in;
-	u16 value;
-
-	taiko = snd_soc_codec_get_drvdata(codec);
-	mb_mv = taiko->mbhc_data.micb_mv;
-
-	if (mb_mv == 0) {
-		pr_err("%s: Mic Bias voltage is set to zero\n", __func__);
-		return -EINVAL;
-	}
-
-	if (dce) {
-		diff = (taiko->mbhc_data.dce_mb) - (taiko->mbhc_data.dce_z);
-		zero = (taiko->mbhc_data.dce_z);
-	} else {
-		diff = (taiko->mbhc_data.sta_mb) - (taiko->mbhc_data.sta_z);
-		zero = (taiko->mbhc_data.sta_z);
-	}
-	in = (u32) diff * vin_mv;
-
-	value = (u16) (in / mb_mv) + zero;
-	return value;
-}
-
-static s32 taiko_codec_sta_dce_v(struct snd_soc_codec *codec, s8 dce,
-				 u16 bias_value)
-{
-	struct taiko_priv *taiko;
-	s16 value, z, mb;
-	s32 mv;
-
-	taiko = snd_soc_codec_get_drvdata(codec);
-	value = bias_value;
-	if (dce) {
-		z = (taiko->mbhc_data.dce_z);
-		mb = (taiko->mbhc_data.dce_mb);
-		mv = (value - z) * (s32)taiko->mbhc_data.micb_mv / (mb - z);
-	} else {
-		z = (taiko->mbhc_data.sta_z);
-		mb = (taiko->mbhc_data.sta_mb);
-		mv = (value - z) * (s32)taiko->mbhc_data.micb_mv / (mb - z);
-	}
-
-	return mv;
-}
-
-static void btn_lpress_fn(struct work_struct *work)
-{
-	struct delayed_work *delayed_work;
-	struct taiko_priv *taiko;
-	short bias_value;
-	int dce_mv, sta_mv;
-	struct wcd9xxx *core;
-
-	pr_debug("%s:\n", __func__);
-
-	delayed_work = to_delayed_work(work);
-	taiko = container_of(delayed_work, struct taiko_priv, mbhc_btn_dwork);
-	core = dev_get_drvdata(taiko->codec->dev->parent);
-
-	if (taiko) {
-		if (taiko->mbhc_cfg.button_jack) {
-			bias_value = taiko_codec_read_sta_result(taiko->codec);
-			sta_mv = taiko_codec_sta_dce_v(taiko->codec, 0,
-						bias_value);
-			bias_value = taiko_codec_read_dce_result(taiko->codec);
-			dce_mv = taiko_codec_sta_dce_v(taiko->codec, 1,
-						bias_value);
-			pr_debug("%s: Reporting long button press event\n",
-				__func__);
-			pr_debug("%s: STA: %d, DCE: %d\n", __func__, sta_mv,
-					dce_mv);
-			taiko_snd_soc_jack_report(taiko,
-						  taiko->mbhc_cfg.button_jack,
-						  taiko->buttons_pressed,
-						  taiko->buttons_pressed);
-		}
-	} else {
-		pr_err("%s: Bad taiko private data\n", __func__);
-	}
-
-	pr_debug("%s: leave\n", __func__);
-	wcd9xxx_unlock_sleep(core);
-}
-
-void taiko_mbhc_cal(struct snd_soc_codec *codec)
-{
-	struct taiko_priv *taiko;
-	struct taiko_mbhc_btn_detect_cfg *btn_det;
-	u8 cfilt_mode, bg_mode;
-	u8 ncic, nmeas, navg;
-	u32 mclk_rate;
-	u32 dce_wait, sta_wait;
-	u8 *n_cic;
-	void *calibration;
-
-	taiko = snd_soc_codec_get_drvdata(codec);
-	calibration = taiko->mbhc_cfg.calibration;
-
-	wcd9xxx_disable_irq(codec->control_data, TAIKO_IRQ_MBHC_POTENTIAL);
-	taiko_turn_onoff_rel_detection(codec, false);
-
-	/* First compute the DCE / STA wait times
-	 * depending on tunable parameters.
-	 * The value is computed in microseconds
-	 */
-	btn_det = TAIKO_MBHC_CAL_BTN_DET_PTR(calibration);
-	n_cic = taiko_mbhc_cal_btn_det_mp(btn_det, TAIKO_BTN_DET_N_CIC);
-	ncic = n_cic[taiko_codec_mclk_index(taiko)];
-	nmeas = TAIKO_MBHC_CAL_BTN_DET_PTR(calibration)->n_meas;
-	navg = TAIKO_MBHC_CAL_GENERAL_PTR(calibration)->mbhc_navg;
-	mclk_rate = taiko->mbhc_cfg.mclk_rate;
-	dce_wait = (1000 * 512 * ncic * (nmeas + 1)) / (mclk_rate / 1000);
-	sta_wait = (1000 * 128 * (navg + 1)) / (mclk_rate / 1000);
-
-	taiko->mbhc_data.t_dce = dce_wait;
-	taiko->mbhc_data.t_sta = sta_wait;
-
-	/* LDOH and CFILT are already configured during pdata handling.
-	 * Only need to make sure CFILT and bandgap are in Fast mode.
-	 * Need to restore defaults once calculation is done.
-	 */
-	cfilt_mode = snd_soc_read(codec, taiko->mbhc_bias_regs.cfilt_ctl);
-	snd_soc_update_bits(codec, taiko->mbhc_bias_regs.cfilt_ctl, 0x40, 0x00);
-	bg_mode = snd_soc_update_bits(codec, TAIKO_A_BIAS_CENTRAL_BG_CTL, 0x02,
-				      0x02);
-
-	/* Micbias, CFILT, LDOH, MBHC MUX mode settings
-	 * to perform ADC calibration
-	 */
-	snd_soc_update_bits(codec, taiko->mbhc_bias_regs.ctl_reg, 0x60,
-			    taiko->mbhc_cfg.micbias << 5);
-	snd_soc_update_bits(codec, taiko->mbhc_bias_regs.ctl_reg, 0x01, 0x00);
-	snd_soc_update_bits(codec, TAIKO_A_LDO_H_MODE_1, 0x60, 0x60);
-	snd_soc_write(codec, TAIKO_A_TX_7_MBHC_TEST_CTL, 0x78);
-	snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_B1_CTL, 0x04, 0x04);
-
-	/* DCE measurement for 0 volts */
-	snd_soc_write(codec, TAIKO_A_CDC_MBHC_CLK_CTL, 0x0A);
-	snd_soc_write(codec, TAIKO_A_CDC_MBHC_EN_CTL, 0x04);
-	snd_soc_write(codec, TAIKO_A_CDC_MBHC_CLK_CTL, 0x02);
-	snd_soc_write(codec, TAIKO_A_MBHC_SCALING_MUX_1, 0x81);
-	usleep_range(100, 100);
-	snd_soc_write(codec, TAIKO_A_CDC_MBHC_EN_CTL, 0x04);
-	usleep_range(taiko->mbhc_data.t_dce, taiko->mbhc_data.t_dce);
-	taiko->mbhc_data.dce_z = taiko_codec_read_dce_result(codec);
-
-	/* DCE measurment for MB voltage */
-	snd_soc_write(codec, TAIKO_A_CDC_MBHC_CLK_CTL, 0x0A);
-	snd_soc_write(codec, TAIKO_A_CDC_MBHC_CLK_CTL, 0x02);
-	snd_soc_write(codec, TAIKO_A_MBHC_SCALING_MUX_1, 0x82);
-	usleep_range(100, 100);
-	snd_soc_write(codec, TAIKO_A_CDC_MBHC_EN_CTL, 0x04);
-	usleep_range(taiko->mbhc_data.t_dce, taiko->mbhc_data.t_dce);
-	taiko->mbhc_data.dce_mb = taiko_codec_read_dce_result(codec);
-
-	/* Sta measuremnt for 0 volts */
-	snd_soc_write(codec, TAIKO_A_CDC_MBHC_CLK_CTL, 0x0A);
-	snd_soc_write(codec, TAIKO_A_CDC_MBHC_EN_CTL, 0x02);
-	snd_soc_write(codec, TAIKO_A_CDC_MBHC_CLK_CTL, 0x02);
-	snd_soc_write(codec, TAIKO_A_MBHC_SCALING_MUX_1, 0x81);
-	usleep_range(100, 100);
-	snd_soc_write(codec, TAIKO_A_CDC_MBHC_EN_CTL, 0x02);
-	usleep_range(taiko->mbhc_data.t_sta, taiko->mbhc_data.t_sta);
-	taiko->mbhc_data.sta_z = taiko_codec_read_sta_result(codec);
-
-	/* STA Measurement for MB Voltage */
-	snd_soc_write(codec, TAIKO_A_MBHC_SCALING_MUX_1, 0x82);
-	usleep_range(100, 100);
-	snd_soc_write(codec, TAIKO_A_CDC_MBHC_EN_CTL, 0x02);
-	usleep_range(taiko->mbhc_data.t_sta, taiko->mbhc_data.t_sta);
-	taiko->mbhc_data.sta_mb = taiko_codec_read_sta_result(codec);
-
-	/* Restore default settings. */
-	snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_B1_CTL, 0x04, 0x00);
-	snd_soc_update_bits(codec, taiko->mbhc_bias_regs.cfilt_ctl, 0x40,
-			    cfilt_mode);
-	snd_soc_update_bits(codec, TAIKO_A_BIAS_CENTRAL_BG_CTL, 0x02, bg_mode);
-
-	snd_soc_write(codec, TAIKO_A_MBHC_SCALING_MUX_1, 0x84);
-	usleep_range(100, 100);
-
-	wcd9xxx_enable_irq(codec->control_data, TAIKO_IRQ_MBHC_POTENTIAL);
-	taiko_turn_onoff_rel_detection(codec, true);
-}
-
-void *taiko_mbhc_cal_btn_det_mp(const struct taiko_mbhc_btn_detect_cfg *btn_det,
-				const enum taiko_mbhc_btn_det_mem mem)
-{
-	void *ret = &btn_det->_v_btn_low;
-
-	switch (mem) {
-	case TAIKO_BTN_DET_GAIN:
-		ret += sizeof(btn_det->_n_cic);
-	case TAIKO_BTN_DET_N_CIC:
-		ret += sizeof(btn_det->_n_ready);
-	case TAIKO_BTN_DET_N_READY:
-		ret += sizeof(btn_det->_v_btn_high[0]) * btn_det->num_btn;
-	case TAIKO_BTN_DET_V_BTN_HIGH:
-		ret += sizeof(btn_det->_v_btn_low[0]) * btn_det->num_btn;
-	case TAIKO_BTN_DET_V_BTN_LOW:
-		/* do nothing */
-		break;
-	default:
-		ret = NULL;
-	}
-
-	return ret;
-}
-
-static s16 taiko_scale_v_micb_vddio(struct taiko_priv *taiko, int v,
-				    bool tovddio)
-{
-	int r;
-	int vddio_k, mb_k;
-	vddio_k = taiko_find_k_value(taiko->pdata->micbias.ldoh_v,
-				     VDDIO_MICBIAS_MV);
-	mb_k = taiko_find_k_value(taiko->pdata->micbias.ldoh_v,
-				  taiko->mbhc_data.micb_mv);
-	if (tovddio)
-		r = v * vddio_k / mb_k;
-	else
-		r = v * mb_k / vddio_k;
-	return r;
-}
-
-static void taiko_mbhc_calc_thres(struct snd_soc_codec *codec)
-{
-	struct taiko_priv *taiko;
-	s16 btn_mv = 0, btn_delta_mv;
-	struct taiko_mbhc_btn_detect_cfg *btn_det;
-	struct taiko_mbhc_plug_type_cfg *plug_type;
-	u16 *btn_high;
-	u8 *n_ready;
-	int i;
-
-	taiko = snd_soc_codec_get_drvdata(codec);
-	btn_det = TAIKO_MBHC_CAL_BTN_DET_PTR(taiko->mbhc_cfg.calibration);
-	plug_type = TAIKO_MBHC_CAL_PLUG_TYPE_PTR(taiko->mbhc_cfg.calibration);
-
-	n_ready = taiko_mbhc_cal_btn_det_mp(btn_det, TAIKO_BTN_DET_N_READY);
-	if (taiko->mbhc_cfg.mclk_rate == TAIKO_MCLK_RATE_12288KHZ) {
-		taiko->mbhc_data.npoll = 4;
-		taiko->mbhc_data.nbounce_wait = 30;
-	} else if (taiko->mbhc_cfg.mclk_rate == TAIKO_MCLK_RATE_9600KHZ) {
-		taiko->mbhc_data.npoll = 7;
-		taiko->mbhc_data.nbounce_wait = 23;
-	}
-
-	taiko->mbhc_data.t_sta_dce = ((1000 * 256) /
-				      (taiko->mbhc_cfg.mclk_rate / 1000) *
-				      n_ready[taiko_codec_mclk_index(taiko)]) +
-				     10;
-	taiko->mbhc_data.v_ins_hu =
-	    taiko_codec_v_sta_dce(codec, STA, plug_type->v_hs_max);
-	taiko->mbhc_data.v_ins_h =
-	    taiko_codec_v_sta_dce(codec, DCE, plug_type->v_hs_max);
-
-	taiko->mbhc_data.v_inval_ins_low = TAIKO_MBHC_FAKE_INSERT_LOW;
-	if (taiko->mbhc_cfg.gpio)
-		taiko->mbhc_data.v_inval_ins_high =
-		    TAIKO_MBHC_FAKE_INSERT_HIGH;
-	else
-		taiko->mbhc_data.v_inval_ins_high =
-		    TAIKO_MBHC_FAKE_INS_HIGH_NO_GPIO;
-
-	if (taiko->mbhc_data.micb_mv != VDDIO_MICBIAS_MV) {
-		taiko->mbhc_data.adj_v_hs_max =
-		    taiko_scale_v_micb_vddio(taiko, plug_type->v_hs_max, true);
-		taiko->mbhc_data.adj_v_ins_hu =
-		    taiko_codec_v_sta_dce(codec, STA,
-					  taiko->mbhc_data.adj_v_hs_max);
-		taiko->mbhc_data.adj_v_ins_h =
-		    taiko_codec_v_sta_dce(codec, DCE,
-					  taiko->mbhc_data.adj_v_hs_max);
-		taiko->mbhc_data.v_inval_ins_low =
-		    taiko_scale_v_micb_vddio(taiko,
-					     taiko->mbhc_data.v_inval_ins_low,
-					     false);
-		taiko->mbhc_data.v_inval_ins_high =
-		    taiko_scale_v_micb_vddio(taiko,
-					     taiko->mbhc_data.v_inval_ins_high,
-					     false);
-	}
-
-	btn_high = taiko_mbhc_cal_btn_det_mp(btn_det, TAIKO_BTN_DET_V_BTN_HIGH);
-	for (i = 0; i < btn_det->num_btn; i++)
-		btn_mv = btn_high[i] > btn_mv ? btn_high[i] : btn_mv;
-
-	taiko->mbhc_data.v_b1_h = taiko_codec_v_sta_dce(codec, DCE, btn_mv);
-	btn_delta_mv = btn_mv + btn_det->v_btn_press_delta_sta;
-	taiko->mbhc_data.v_b1_hu =
-	    taiko_codec_v_sta_dce(codec, STA, btn_delta_mv);
-
-	btn_delta_mv = btn_mv + btn_det->v_btn_press_delta_cic;
-
-	taiko->mbhc_data.v_b1_huc =
-	    taiko_codec_v_sta_dce(codec, DCE, btn_delta_mv);
-
-	taiko->mbhc_data.v_brh = taiko->mbhc_data.v_b1_h;
-	taiko->mbhc_data.v_brl = TAIKO_MBHC_BUTTON_MIN;
-
-	taiko->mbhc_data.v_no_mic =
-	    taiko_codec_v_sta_dce(codec, STA, plug_type->v_no_mic);
-}
-
-void taiko_mbhc_init(struct snd_soc_codec *codec)
-{
-	struct taiko_priv *taiko;
-	struct taiko_mbhc_general_cfg *generic;
-	struct taiko_mbhc_btn_detect_cfg *btn_det;
-	int n;
-	u8 *n_cic, *gain;
-
-	taiko = snd_soc_codec_get_drvdata(codec);
-	generic = TAIKO_MBHC_CAL_GENERAL_PTR(taiko->mbhc_cfg.calibration);
-	btn_det = TAIKO_MBHC_CAL_BTN_DET_PTR(taiko->mbhc_cfg.calibration);
-
-	for (n = 0; n < 8; n++) {
-			snd_soc_update_bits(codec,
-					    TAIKO_A_CDC_MBHC_FIR_B1_CFG,
-					    0x07, n);
-			snd_soc_write(codec, TAIKO_A_CDC_MBHC_FIR_B2_CFG,
-				      btn_det->c[n]);
-	}
-
-	snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_B2_CTL, 0x07,
-			    btn_det->nc);
-
-	n_cic = taiko_mbhc_cal_btn_det_mp(btn_det, TAIKO_BTN_DET_N_CIC);
-	snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_TIMER_B6_CTL, 0xFF,
-			    n_cic[taiko_codec_mclk_index(taiko)]);
-
-	gain = taiko_mbhc_cal_btn_det_mp(btn_det, TAIKO_BTN_DET_GAIN);
-	snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_B2_CTL, 0x78,
-			    gain[taiko_codec_mclk_index(taiko)] << 3);
-
-	snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_TIMER_B4_CTL, 0x70,
-			    generic->mbhc_nsa << 4);
-
-	snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_TIMER_B4_CTL, 0x0F,
-			    btn_det->n_meas);
-
-	snd_soc_write(codec, TAIKO_A_CDC_MBHC_TIMER_B5_CTL, generic->mbhc_navg);
-
-	snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_B1_CTL, 0x80, 0x80);
-
-	snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_B1_CTL, 0x78,
-			    btn_det->mbhc_nsc << 3);
-
-	snd_soc_update_bits(codec, taiko->reg_addr.micb_4_mbhc, 0x03,
-			    TAIKO_MICBIAS2);
-
-	snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_B1_CTL, 0x02, 0x02);
-
-	snd_soc_update_bits(codec, TAIKO_A_MBHC_SCALING_MUX_2, 0xF0, 0xF0);
-}
-
-static bool taiko_mbhc_fw_validate(const struct firmware *fw)
-{
-	u32 cfg_offset;
-	struct taiko_mbhc_imped_detect_cfg *imped_cfg;
-	struct taiko_mbhc_btn_detect_cfg *btn_cfg;
-
-	if (fw->size < TAIKO_MBHC_CAL_MIN_SIZE)
-		return false;
-
-	/* previous check guarantees that there is enough fw data up
-	 * to num_btn
-	 */
-	btn_cfg = TAIKO_MBHC_CAL_BTN_DET_PTR(fw->data);
-	cfg_offset = (u32) ((void *) btn_cfg - (void *) fw->data);
-	if (fw->size < (cfg_offset + TAIKO_MBHC_CAL_BTN_SZ(btn_cfg)))
-		return false;
-
-	/* previous check guarantees that there is enough fw data up
-	 * to start of impedance detection configuration
-	 */
-	imped_cfg = TAIKO_MBHC_CAL_IMPED_DET_PTR(fw->data);
-	cfg_offset = (u32) ((void *) imped_cfg - (void *) fw->data);
-
-	if (fw->size < (cfg_offset + TAIKO_MBHC_CAL_IMPED_MIN_SZ))
-		return false;
-
-	if (fw->size < (cfg_offset + TAIKO_MBHC_CAL_IMPED_SZ(imped_cfg)))
-		return false;
-
-	return true;
-}
-
-/* called under codec_resource_lock acquisition */
-static int taiko_determine_button(const struct taiko_priv *priv,
-				  const s32 micmv)
-{
-	s16 *v_btn_low, *v_btn_high;
-	struct taiko_mbhc_btn_detect_cfg *btn_det;
-	int i, btn = -1;
-
-	btn_det = TAIKO_MBHC_CAL_BTN_DET_PTR(priv->mbhc_cfg.calibration);
-	v_btn_low = taiko_mbhc_cal_btn_det_mp(btn_det, TAIKO_BTN_DET_V_BTN_LOW);
-	v_btn_high = taiko_mbhc_cal_btn_det_mp(btn_det,
-				TAIKO_BTN_DET_V_BTN_HIGH);
-
-	for (i = 0; i < btn_det->num_btn; i++) {
-		if ((v_btn_low[i] <= micmv) && (v_btn_high[i] >= micmv)) {
-			btn = i;
-			break;
-		}
-	}
-
-	if (btn == -1)
-		pr_debug("%s: couldn't find button number for mic mv %d\n",
-			 __func__, micmv);
-
-	return btn;
-}
-
-static int taiko_get_button_mask(const int btn)
-{
-	int mask = 0;
-	switch (btn) {
-	case 0:
-		mask = SND_JACK_BTN_0;
-		break;
-	case 1:
-		mask = SND_JACK_BTN_1;
-		break;
-	case 2:
-		mask = SND_JACK_BTN_2;
-		break;
-	case 3:
-		mask = SND_JACK_BTN_3;
-		break;
-	case 4:
-		mask = SND_JACK_BTN_4;
-		break;
-	case 5:
-		mask = SND_JACK_BTN_5;
-		break;
-	case 6:
-		mask = SND_JACK_BTN_6;
-		break;
-	case 7:
-		mask = SND_JACK_BTN_7;
-		break;
-	}
-	return mask;
-}
-
-static irqreturn_t taiko_dce_handler(int irq, void *data)
-{
-	int i, mask;
-	short dce, sta;
-	s32 mv, mv_s, stamv_s;
-	bool vddio;
-	int btn = -1, meas = 0;
-	struct taiko_priv *priv = data;
-	const struct taiko_mbhc_btn_detect_cfg *d =
-	    TAIKO_MBHC_CAL_BTN_DET_PTR(priv->mbhc_cfg.calibration);
-	short btnmeas[d->n_btn_meas + 1];
-	struct snd_soc_codec *codec = priv->codec;
-	struct wcd9xxx *core = dev_get_drvdata(priv->codec->dev->parent);
-	int n_btn_meas = d->n_btn_meas;
-	u8 mbhc_status = snd_soc_read(codec, TAIKO_A_CDC_MBHC_B1_STATUS) & 0x3E;
-
-	pr_debug("%s: enter\n", __func__);
-
-	TAIKO_ACQUIRE_LOCK(priv->codec_resource_lock);
-	if (priv->mbhc_state == MBHC_STATE_POTENTIAL_RECOVERY) {
-		pr_debug("%s: mbhc is being recovered, skip button press\n",
-			 __func__);
-		goto done;
-	}
-
-	priv->mbhc_state = MBHC_STATE_POTENTIAL;
-
-	if (!priv->mbhc_polling_active) {
-		pr_warn("%s: mbhc polling is not active, skip button press\n",
-			__func__);
-		goto done;
-	}
-
-	dce = taiko_codec_read_dce_result(codec);
-	mv = taiko_codec_sta_dce_v(codec, 1, dce);
-
-	/* If GPIO interrupt already kicked in, ignore button press */
-	if (priv->in_gpio_handler) {
-		pr_debug("%s: GPIO State Changed, ignore button press\n",
-			 __func__);
-		btn = -1;
-		goto done;
-	}
-
-	vddio = (priv->mbhc_data.micb_mv != VDDIO_MICBIAS_MV &&
-		 priv->mbhc_micbias_switched);
-	mv_s = vddio ? taiko_scale_v_micb_vddio(priv, mv, false) : mv;
-
-	if (mbhc_status != TAIKO_MBHC_STATUS_REL_DETECTION) {
-		if (priv->mbhc_last_resume &&
-		    !time_after(jiffies, priv->mbhc_last_resume + HZ)) {
-			pr_debug("%s: Button is already released shortly after resume\n",
-				__func__);
-			n_btn_meas = 0;
-		} else {
-			pr_debug("%s: Button is already released without resume",
-				__func__);
-			sta = taiko_codec_read_sta_result(codec);
-			stamv_s = taiko_codec_sta_dce_v(codec, 0, sta);
-			if (vddio)
-				stamv_s = taiko_scale_v_micb_vddio(priv,
-								   stamv_s,
-								   false);
-			btn = taiko_determine_button(priv, mv_s);
-			if (btn != taiko_determine_button(priv, stamv_s))
-				btn = -1;
-			goto done;
-		}
-	}
-
-	/* determine pressed button */
-	btnmeas[meas++] = taiko_determine_button(priv, mv_s);
-	pr_debug("%s: meas %d - DCE %d,%d,%d button %d\n", __func__,
-		 meas - 1, dce, mv, mv_s, btnmeas[meas - 1]);
-	if (n_btn_meas == 0)
-		btn = btnmeas[0];
-	for (; ((d->n_btn_meas) && (meas < (d->n_btn_meas + 1))); meas++) {
-		dce = taiko_codec_sta_dce(codec, 1, false);
-		mv = taiko_codec_sta_dce_v(codec, 1, dce);
-		mv_s = vddio ? taiko_scale_v_micb_vddio(priv, mv, false) : mv;
-
-		btnmeas[meas] = taiko_determine_button(priv, mv_s);
-		pr_debug("%s: meas %d - DCE %d,%d,%d button %d\n",
-			 __func__, meas, dce, mv, mv_s, btnmeas[meas]);
-		/* if large enough measurements are collected,
-		 * start to check if last all n_btn_con measurements were
-		 * in same button low/high range */
-		if (meas + 1 >= d->n_btn_con) {
-			for (i = 0; i < d->n_btn_con; i++)
-				if ((btnmeas[meas] < 0) ||
-				    (btnmeas[meas] != btnmeas[meas - i]))
-					break;
-			if (i == d->n_btn_con) {
-				/* button pressed */
-				btn = btnmeas[meas];
-				break;
-			} else if ((n_btn_meas - meas) < (d->n_btn_con - 1)) {
-				/* if left measurements are less than n_btn_con,
-				 * it's impossible to find button number */
-				break;
-			}
-		}
-	}
-
-	if (btn >= 0) {
-		if (priv->in_gpio_handler) {
-			pr_debug(
-			"%s: GPIO already triggered, ignore button press\n",
-			__func__);
-			goto done;
-		}
-		mask = taiko_get_button_mask(btn);
-		priv->buttons_pressed |= mask;
-		wcd9xxx_lock_sleep(core);
-		if (schedule_delayed_work(&priv->mbhc_btn_dwork,
-					  msecs_to_jiffies(400)) == 0) {
-			WARN(1, "Button pressed twice without release event\n");
-			wcd9xxx_unlock_sleep(core);
-		}
-	} else {
-		pr_debug("%s: bogus button press, too short press?\n",
-			 __func__);
-	}
-
- done:
-	pr_debug("%s: leave\n", __func__);
-	TAIKO_RELEASE_LOCK(priv->codec_resource_lock);
-	return IRQ_HANDLED;
-}
-
-static int taiko_is_fake_press(struct taiko_priv *priv)
-{
-	int i;
-	int r = 0;
-	struct snd_soc_codec *codec = priv->codec;
-	const int dces = MBHC_NUM_DCE_PLUG_DETECT;
-	s16 mb_v, v_ins_hu, v_ins_h;
-
-	v_ins_hu = taiko_get_current_v_ins(priv, true);
-	v_ins_h = taiko_get_current_v_ins(priv, false);
-
-	for (i = 0; i < dces; i++) {
-		usleep_range(10000, 10000);
-		if (i == 0) {
-			mb_v = taiko_codec_sta_dce(codec, 0, true);
-			pr_debug("%s: STA[0]: %d,%d\n", __func__, mb_v,
-				 taiko_codec_sta_dce_v(codec, 0, mb_v));
-			if (mb_v < (s16)priv->mbhc_data.v_b1_hu ||
-			    mb_v > v_ins_hu) {
-				r = 1;
-				break;
-			}
-		} else {
-			mb_v = taiko_codec_sta_dce(codec, 1, true);
-			pr_debug("%s: DCE[%d]: %d,%d\n", __func__, i, mb_v,
-				 taiko_codec_sta_dce_v(codec, 1, mb_v));
-			if (mb_v < (s16)priv->mbhc_data.v_b1_h ||
-			    mb_v > v_ins_h) {
-				r = 1;
-				break;
-			}
-		}
-	}
-
-	return r;
-}
-
-static irqreturn_t taiko_release_handler(int irq, void *data)
-{
-	int ret;
-	struct taiko_priv *priv = data;
-	struct snd_soc_codec *codec = priv->codec;
-
-	pr_debug("%s: enter\n", __func__);
-
-	TAIKO_ACQUIRE_LOCK(priv->codec_resource_lock);
-	priv->mbhc_state = MBHC_STATE_RELEASE;
-
-	taiko_codec_drive_v_to_micbias(codec, 10000);
-
-	if (priv->buttons_pressed & TAIKO_JACK_BUTTON_MASK) {
-		ret = taiko_cancel_btn_work(priv);
-		if (ret == 0) {
-			pr_debug("%s: Reporting long button release event\n",
-				 __func__);
-			if (priv->mbhc_cfg.button_jack)
-				taiko_snd_soc_jack_report(priv,
-						  priv->mbhc_cfg.button_jack, 0,
-						  priv->buttons_pressed);
-		} else {
-			if (taiko_is_fake_press(priv)) {
-				pr_debug("%s: Fake button press interrupt\n",
-					 __func__);
-			} else if (priv->mbhc_cfg.button_jack) {
-				if (priv->in_gpio_handler) {
-					pr_debug("%s: GPIO kicked in, ignore\n",
-						 __func__);
-				} else {
-					pr_debug(
-					"%s: Reporting short button press and release\n",
-					 __func__);
-					taiko_snd_soc_jack_report(priv,
-						     priv->mbhc_cfg.button_jack,
-						     priv->buttons_pressed,
-						     priv->buttons_pressed);
-					taiko_snd_soc_jack_report(priv,
-						  priv->mbhc_cfg.button_jack, 0,
-						  priv->buttons_pressed);
-				}
-			}
-		}
-
-		priv->buttons_pressed &= ~TAIKO_JACK_BUTTON_MASK;
-	}
-
-	taiko_codec_calibrate_hs_polling(codec);
-
-	if (priv->mbhc_cfg.gpio)
-		msleep(TAIKO_MBHC_GPIO_REL_DEBOUNCE_TIME_MS);
-
-	taiko_codec_start_hs_polling(codec);
-
-	pr_debug("%s: leave\n", __func__);
-	TAIKO_RELEASE_LOCK(priv->codec_resource_lock);
-	return IRQ_HANDLED;
-}
-
-static void taiko_codec_shutdown_hs_removal_detect(struct snd_soc_codec *codec)
-{
-	struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
-	const struct taiko_mbhc_general_cfg *generic =
-	    TAIKO_MBHC_CAL_GENERAL_PTR(taiko->mbhc_cfg.calibration);
-
-	if (!taiko->mclk_enabled && !taiko->mbhc_polling_active)
-		taiko_codec_enable_config_mode(codec, 1);
-
-	snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_CLK_CTL, 0x2, 0x2);
-	snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_B1_CTL, 0x6, 0x0);
-
-	snd_soc_update_bits(codec, taiko->mbhc_bias_regs.mbhc_reg, 0x80, 0x00);
-
-	usleep_range(generic->t_shutdown_plug_rem,
-		     generic->t_shutdown_plug_rem);
-
-	snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_CLK_CTL, 0xA, 0x8);
-	if (!taiko->mclk_enabled && !taiko->mbhc_polling_active)
-		taiko_codec_enable_config_mode(codec, 0);
-
-	snd_soc_write(codec, TAIKO_A_MBHC_SCALING_MUX_1, 0x00);
-}
-
-static void taiko_codec_cleanup_hs_polling(struct snd_soc_codec *codec)
-{
-	struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
-
-	taiko_codec_shutdown_hs_removal_detect(codec);
-
-	if (!taiko->mclk_enabled) {
-		taiko_codec_disable_clock_block(codec);
-		taiko_codec_enable_bandgap(codec, TAIKO_BANDGAP_OFF);
-	}
-
-	taiko->mbhc_polling_active = false;
-	taiko->mbhc_state = MBHC_STATE_NONE;
-}
-
-static irqreturn_t taiko_hphl_ocp_irq(int irq, void *data)
-{
-	struct taiko_priv *taiko = data;
-	struct snd_soc_codec *codec;
-
-	pr_info("%s: received HPHL OCP irq\n", __func__);
-
-	if (taiko) {
-		codec = taiko->codec;
-		if (taiko->hphlocp_cnt++ < TAIKO_OCP_ATTEMPT) {
-			pr_info("%s: retry\n", __func__);
-			snd_soc_update_bits(codec, TAIKO_A_RX_HPH_OCP_CTL, 0x10,
-					    0x00);
-			snd_soc_update_bits(codec, TAIKO_A_RX_HPH_OCP_CTL, 0x10,
-					    0x10);
-		} else {
-			wcd9xxx_disable_irq(codec->control_data,
-					  TAIKO_IRQ_HPH_PA_OCPL_FAULT);
-			taiko->hphlocp_cnt = 0;
-			taiko->hph_status |= SND_JACK_OC_HPHL;
-			if (taiko->mbhc_cfg.headset_jack)
-				taiko_snd_soc_jack_report(taiko,
-						   taiko->mbhc_cfg.headset_jack,
-						   taiko->hph_status,
-						   TAIKO_JACK_MASK);
-		}
-	} else {
-		pr_err("%s: Bad taiko private data\n", __func__);
-	}
-
-	return IRQ_HANDLED;
-}
-
-static irqreturn_t taiko_hphr_ocp_irq(int irq, void *data)
-{
-	struct taiko_priv *taiko = data;
-	struct snd_soc_codec *codec;
-
-	pr_info("%s: received HPHR OCP irq\n", __func__);
-
-	if (taiko) {
-		codec = taiko->codec;
-		if (taiko->hphrocp_cnt++ < TAIKO_OCP_ATTEMPT) {
-			pr_info("%s: retry\n", __func__);
-			snd_soc_update_bits(codec, TAIKO_A_RX_HPH_OCP_CTL, 0x10,
-					    0x00);
-			snd_soc_update_bits(codec, TAIKO_A_RX_HPH_OCP_CTL, 0x10,
-					    0x10);
-		} else {
-			wcd9xxx_disable_irq(codec->control_data,
-					  TAIKO_IRQ_HPH_PA_OCPR_FAULT);
-			taiko->hphrocp_cnt = 0;
-			taiko->hph_status |= SND_JACK_OC_HPHR;
-			if (taiko->mbhc_cfg.headset_jack)
-				taiko_snd_soc_jack_report(taiko,
-						   taiko->mbhc_cfg.headset_jack,
-						   taiko->hph_status,
-						   TAIKO_JACK_MASK);
-		}
-	} else {
-		pr_err("%s: Bad taiko private data\n", __func__);
-	}
-
-	return IRQ_HANDLED;
-}
-
-static bool taiko_is_inval_ins_range(struct snd_soc_codec *codec,
-				     s32 mic_volt, bool highhph, bool *highv)
-{
-	struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
-	bool invalid = false;
-	s16 v_hs_max;
-
-	/* Perform this check only when the high voltage headphone
-	 * needs to be considered as invalid
-	 */
-	v_hs_max = taiko_get_current_v_hs_max(taiko);
-	*highv = mic_volt > v_hs_max;
-	if (!highhph && *highv)
-		invalid = true;
-	else if (mic_volt < taiko->mbhc_data.v_inval_ins_high &&
-		 (mic_volt > taiko->mbhc_data.v_inval_ins_low))
-		invalid = true;
-
-	return invalid;
-}
-
-static bool taiko_is_inval_ins_delta(struct snd_soc_codec *codec,
-				     int mic_volt, int mic_volt_prev,
-				     int threshold)
-{
-	return abs(mic_volt - mic_volt_prev) > threshold;
-}
-
-/* called under codec_resource_lock acquisition */
-void taiko_find_plug_and_report(struct snd_soc_codec *codec,
-				enum taiko_mbhc_plug_type plug_type)
-{
-	struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
-
-	if (plug_type == PLUG_TYPE_HEADPHONE &&
-	    taiko->current_plug == PLUG_TYPE_NONE) {
-		/* Nothing was reported previously
-		 * report a headphone or unsupported
-		 */
-		taiko_codec_report_plug(codec, 1, SND_JACK_HEADPHONE);
-		taiko_codec_cleanup_hs_polling(codec);
-	} else if (plug_type == PLUG_TYPE_GND_MIC_SWAP) {
-		if (taiko->current_plug == PLUG_TYPE_HEADSET)
-			taiko_codec_report_plug(codec, 0, SND_JACK_HEADSET);
-		else if (taiko->current_plug == PLUG_TYPE_HEADPHONE)
-			taiko_codec_report_plug(codec, 0, SND_JACK_HEADPHONE);
-
-		taiko_codec_report_plug(codec, 1, SND_JACK_UNSUPPORTED);
-		taiko_codec_cleanup_hs_polling(codec);
-	} else if (plug_type == PLUG_TYPE_HEADSET) {
-		/* If Headphone was reported previously, this will
-		 * only report the mic line
-		 */
-		taiko_codec_report_plug(codec, 1, SND_JACK_HEADSET);
-		msleep(100);
-		taiko_codec_start_hs_polling(codec);
-	} else if (plug_type == PLUG_TYPE_HIGH_HPH) {
-		if (taiko->current_plug == PLUG_TYPE_NONE)
-			taiko_codec_report_plug(codec, 1, SND_JACK_HEADPHONE);
-		taiko_codec_cleanup_hs_polling(codec);
-		pr_debug("setup mic trigger for further detection\n");
-		taiko->lpi_enabled = true;
-		taiko_codec_enable_hs_detect(codec, 1,
-					     MBHC_USE_MB_TRIGGER |
-					     MBHC_USE_HPHL_TRIGGER,
-					     false);
-	} else {
-		WARN(1, "Unexpected current plug_type %d, plug_type %d\n",
-		     taiko->current_plug, plug_type);
-	}
-}
-
-/* should be called under interrupt context that hold suspend */
-static void taiko_schedule_hs_detect_plug(struct taiko_priv *taiko)
-{
-	pr_debug("%s: scheduling taiko_hs_correct_gpio_plug\n", __func__);
-	taiko->hs_detect_work_stop = false;
-	wcd9xxx_lock_sleep(taiko->codec->control_data);
-	schedule_work(&taiko->hs_correct_plug_work);
-}
-
-/* called under codec_resource_lock acquisition */
-static void taiko_cancel_hs_detect_plug(struct taiko_priv *taiko)
-{
-	pr_debug("%s: canceling hs_correct_plug_work\n", __func__);
-	taiko->hs_detect_work_stop = true;
-	wmb();
-	TAIKO_RELEASE_LOCK(taiko->codec_resource_lock);
-	if (cancel_work_sync(&taiko->hs_correct_plug_work)) {
-		pr_debug("%s: hs_correct_plug_work is canceled\n", __func__);
-		wcd9xxx_unlock_sleep(taiko->codec->control_data);
-	}
-	TAIKO_ACQUIRE_LOCK(taiko->codec_resource_lock);
-}
-
-static bool taiko_hs_gpio_level_remove(struct taiko_priv *taiko)
-{
-	return (gpio_get_value_cansleep(taiko->mbhc_cfg.gpio) !=
-		taiko->mbhc_cfg.gpio_level_insert);
-}
-
-/* called under codec_resource_lock acquisition */
-static void taiko_codec_hphr_gnd_switch(struct snd_soc_codec *codec, bool on)
-{
-	snd_soc_update_bits(codec, TAIKO_A_MBHC_HPH, 0x01, on);
-	if (on)
-		usleep_range(5000, 5000);
-}
-
-/* called under codec_resource_lock acquisition and mbhc override = 1 */
-static enum taiko_mbhc_plug_type
-taiko_codec_get_plug_type(struct snd_soc_codec *codec, bool highhph)
-{
-	int i;
-	bool gndswitch, vddioswitch;
-	int scaled;
-	struct taiko_mbhc_plug_type_cfg *plug_type_ptr;
-	struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
-	const bool vddio = (taiko->mbhc_data.micb_mv != VDDIO_MICBIAS_MV);
-	int num_det = (MBHC_NUM_DCE_PLUG_DETECT + vddio);
-	enum taiko_mbhc_plug_type plug_type[num_det];
-	s16 mb_v[num_det];
-	s32 mic_mv[num_det];
-	bool inval;
-	bool highdelta;
-	bool ahighv = false, highv;
-
-	/* make sure override is on */
-	WARN_ON(!(snd_soc_read(codec, TAIKO_A_CDC_MBHC_B1_CTL) & 0x04));
-
-	/* GND and MIC swap detection requires at least 2 rounds of DCE */
-	BUG_ON(num_det < 2);
-
-	plug_type_ptr =
-	    TAIKO_MBHC_CAL_PLUG_TYPE_PTR(taiko->mbhc_cfg.calibration);
-
-	plug_type[0] = PLUG_TYPE_INVALID;
-
-	/* performs DCEs for N times
-	 * 1st: check if voltage is in invalid range
-	 * 2nd - N-2nd: check voltage range and delta
-	 * N-1st: check voltage range, delta with HPHR GND switch
-	 * Nth: check voltage range with VDDIO switch if micbias V != vddio V*/
-	for (i = 0; i < num_det; i++) {
-		gndswitch = (i == (num_det - 1 - vddio));
-		vddioswitch = (vddio && ((i == num_det - 1) ||
-					 (i == num_det - 2)));
-		if (i == 0) {
-			mb_v[i] = taiko_codec_setup_hs_polling(codec);
-			mic_mv[i] = taiko_codec_sta_dce_v(codec, 1 , mb_v[i]);
-			inval = taiko_is_inval_ins_range(codec, mic_mv[i],
-							 highhph, &highv);
-			ahighv |= highv;
-			scaled = mic_mv[i];
-		} else {
-			if (vddioswitch)
-				__taiko_codec_switch_micbias(taiko->codec, 1,
-							     false, false);
-			if (gndswitch)
-				taiko_codec_hphr_gnd_switch(codec, true);
-			mb_v[i] = __taiko_codec_sta_dce(codec, 1, true, true);
-			mic_mv[i] = taiko_codec_sta_dce_v(codec, 1 , mb_v[i]);
-			if (vddioswitch)
-				scaled = taiko_scale_v_micb_vddio(taiko,
-								  mic_mv[i],
-								  false);
-			else
-				scaled = mic_mv[i];
-			/* !gndswitch & vddioswitch means the previous DCE
-			 * was done with gndswitch, don't compare with DCE
-			 * with gndswitch */
-			highdelta = taiko_is_inval_ins_delta(codec, scaled,
-					mic_mv[i - !gndswitch - vddioswitch],
-					TAIKO_MBHC_FAKE_INS_DELTA_SCALED_MV);
-			inval = (taiko_is_inval_ins_range(codec, mic_mv[i],
-							  highhph, &highv) ||
-				 highdelta);
-			ahighv |= highv;
-			if (gndswitch)
-				taiko_codec_hphr_gnd_switch(codec, false);
-			if (vddioswitch)
-				__taiko_codec_switch_micbias(taiko->codec, 0,
-							     false, false);
-			/* claim UNSUPPORTED plug insertion when
-			 * good headset is detected but HPHR GND switch makes
-			 * delta difference */
-			if (i == (num_det - 2) && highdelta && !ahighv)
-				plug_type[0] = PLUG_TYPE_GND_MIC_SWAP;
-			else if (i == (num_det - 1) && inval)
-				plug_type[0] = PLUG_TYPE_INVALID;
-		}
-		pr_debug("%s: DCE #%d, %04x, V %d, scaled V %d, GND %d, VDDIO %d, inval %d\n",
-			__func__, i + 1, mb_v[i] & 0xffff, mic_mv[i], scaled,
-			gndswitch, vddioswitch, inval);
-		/* don't need to run further DCEs */
-		if (ahighv && inval)
-			break;
-		mic_mv[i] = scaled;
-	}
-
-	for (i = 0; (plug_type[0] != PLUG_TYPE_GND_MIC_SWAP && !inval) &&
-		     i < num_det; i++) {
-		/*
-		 * If we are here, means none of the all
-		 * measurements are fake, continue plug type detection.
-		 * If all three measurements do not produce same
-		 * plug type, restart insertion detection
-		 */
-		if (mic_mv[i] < plug_type_ptr->v_no_mic) {
-			plug_type[i] = PLUG_TYPE_HEADPHONE;
-			pr_debug("%s: Detect attempt %d, detected Headphone\n",
-				 __func__, i);
-		} else if (highhph && (mic_mv[i] > plug_type_ptr->v_hs_max)) {
-			plug_type[i] = PLUG_TYPE_HIGH_HPH;
-			pr_debug(
-			"%s: Detect attempt %d, detected High Headphone\n",
-			__func__, i);
-		} else {
-			plug_type[i] = PLUG_TYPE_HEADSET;
-			pr_debug("%s: Detect attempt %d, detected Headset\n",
-				 __func__, i);
-		}
-
-		if (i > 0 && (plug_type[i - 1] != plug_type[i])) {
-			pr_err("%s: Detect attempt %d and %d are not same",
-			       __func__, i - 1, i);
-			plug_type[0] = PLUG_TYPE_INVALID;
-			inval = true;
-			break;
-		}
-	}
-
-	pr_debug("%s: Detected plug type %d\n", __func__, plug_type[0]);
-	return plug_type[0];
-}
-
-static void taiko_hs_correct_gpio_plug(struct work_struct *work)
-{
-	struct taiko_priv *taiko;
-	struct snd_soc_codec *codec;
-	int retry = 0, pt_gnd_mic_swap_cnt = 0;
-	bool correction = false;
-	enum taiko_mbhc_plug_type plug_type;
-	unsigned long timeout;
-
-	taiko = container_of(work, struct taiko_priv, hs_correct_plug_work);
-	codec = taiko->codec;
-
-	pr_debug("%s: enter\n", __func__);
-	taiko->mbhc_cfg.mclk_cb_fn(codec, 1, false);
-
-	/* Keep override on during entire plug type correction work.
-	 *
-	 * This is okay under the assumption that any GPIO irqs which use
-	 * MBHC block cancel and sync this work so override is off again
-	 * prior to GPIO interrupt handler's MBHC block usage.
-	 * Also while this correction work is running, we can guarantee
-	 * DAPM doesn't use any MBHC block as this work only runs with
-	 * headphone detection.
-	 */
-	taiko_turn_onoff_override(codec, true);
-
-	timeout = jiffies + msecs_to_jiffies(TAIKO_HS_DETECT_PLUG_TIME_MS);
-	while (!time_after(jiffies, timeout)) {
-		++retry;
-		rmb();
-		if (taiko->hs_detect_work_stop) {
-			pr_debug("%s: stop requested\n", __func__);
-			break;
-		}
-
-		msleep(TAIKO_HS_DETECT_PLUG_INERVAL_MS);
-		if (taiko_hs_gpio_level_remove(taiko)) {
-			pr_debug("%s: GPIO value is low\n", __func__);
-			break;
-		}
-
-		/* can race with removal interrupt */
-		TAIKO_ACQUIRE_LOCK(taiko->codec_resource_lock);
-		plug_type = taiko_codec_get_plug_type(codec, true);
-		TAIKO_RELEASE_LOCK(taiko->codec_resource_lock);
-
-		if (plug_type == PLUG_TYPE_INVALID) {
-			pr_debug("Invalid plug in attempt # %d\n", retry);
-			if (retry == NUM_ATTEMPTS_TO_REPORT &&
-			    taiko->current_plug == PLUG_TYPE_NONE) {
-				taiko_codec_report_plug(codec, 1,
-							SND_JACK_HEADPHONE);
-			}
-		} else if (plug_type == PLUG_TYPE_HEADPHONE) {
-			pr_debug("Good headphone detected, continue polling mic\n");
-			if (taiko->current_plug == PLUG_TYPE_NONE)
-				taiko_codec_report_plug(codec, 1,
-							SND_JACK_HEADPHONE);
-		} else {
-			if (plug_type == PLUG_TYPE_GND_MIC_SWAP) {
-				pt_gnd_mic_swap_cnt++;
-				if (pt_gnd_mic_swap_cnt <
-				    TAIKO_MBHC_GND_MIC_SWAP_THRESHOLD)
-					continue;
-				else if (pt_gnd_mic_swap_cnt >
-					 TAIKO_MBHC_GND_MIC_SWAP_THRESHOLD) {
-					/* This is due to GND/MIC switch didn't
-					 * work,  Report unsupported plug */
-				} else if (taiko->mbhc_cfg.swap_gnd_mic) {
-					/* if switch is toggled, check again,
-					 * otherwise report unsupported plug */
-					if (taiko->mbhc_cfg.swap_gnd_mic(codec))
-						continue;
-				}
-			} else
-				pt_gnd_mic_swap_cnt = 0;
-
-			TAIKO_ACQUIRE_LOCK(taiko->codec_resource_lock);
-			/* Turn off override */
-			taiko_turn_onoff_override(codec, false);
-			/* The valid plug also includes PLUG_TYPE_GND_MIC_SWAP
-			 */
-			taiko_find_plug_and_report(codec, plug_type);
-			TAIKO_RELEASE_LOCK(taiko->codec_resource_lock);
-			pr_debug("Attempt %d found correct plug %d\n", retry,
-				 plug_type);
-			correction = true;
-			break;
-		}
-	}
-
-	/* Turn off override */
-	if (!correction)
-		taiko_turn_onoff_override(codec, false);
-
-	taiko->mbhc_cfg.mclk_cb_fn(codec, 0, false);
-	pr_debug("%s: leave\n", __func__);
-	/* unlock sleep */
-	wcd9xxx_unlock_sleep(taiko->codec->control_data);
-}
-
-/* called under codec_resource_lock acquisition */
-static void taiko_codec_decide_gpio_plug(struct snd_soc_codec *codec)
-{
-	enum taiko_mbhc_plug_type plug_type;
-	struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
-
-	pr_debug("%s: enter\n", __func__);
-
-	taiko_turn_onoff_override(codec, true);
-	plug_type = taiko_codec_get_plug_type(codec, true);
-	taiko_turn_onoff_override(codec, false);
-
-	if (taiko_hs_gpio_level_remove(taiko)) {
-		pr_debug("%s: GPIO value is low when determining plug\n",
-			 __func__);
-		return;
-	}
-
-	if (plug_type == PLUG_TYPE_INVALID ||
-	    plug_type == PLUG_TYPE_GND_MIC_SWAP) {
-		taiko_schedule_hs_detect_plug(taiko);
-	} else if (plug_type == PLUG_TYPE_HEADPHONE) {
-		taiko_codec_report_plug(codec, 1, SND_JACK_HEADPHONE);
-
-		taiko_schedule_hs_detect_plug(taiko);
-	} else {
-		pr_debug("%s: Valid plug found, determine plug type %d\n",
-			 __func__, plug_type);
-		taiko_find_plug_and_report(codec, plug_type);
-	}
-}
-
-/* called under codec_resource_lock acquisition */
-static void taiko_codec_detect_plug_type(struct snd_soc_codec *codec)
-{
-	enum taiko_mbhc_plug_type plug_type;
-	struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
-	const struct taiko_mbhc_plug_detect_cfg *plug_det =
-	    TAIKO_MBHC_CAL_PLUG_DET_PTR(taiko->mbhc_cfg.calibration);
-
-	/* Turn on the override,
-	 * taiko_codec_setup_hs_polling requires override on */
-	taiko_turn_onoff_override(codec, true);
-
-	if (plug_det->t_ins_complete > 20)
-		msleep(plug_det->t_ins_complete);
-	else
-		usleep_range(plug_det->t_ins_complete * 1000,
-			     plug_det->t_ins_complete * 1000);
-
-	if (taiko->mbhc_cfg.gpio) {
-		/* Turn off the override */
-		taiko_turn_onoff_override(codec, false);
-		if (taiko_hs_gpio_level_remove(taiko))
-			pr_debug(
-			"%s: GPIO value is low when determining plug\n",
-			__func__);
-		else
-			taiko_codec_decide_gpio_plug(codec);
-		return;
-	}
-
-	plug_type = taiko_codec_get_plug_type(codec, false);
-	taiko_turn_onoff_override(codec, false);
-
-	if (plug_type == PLUG_TYPE_INVALID) {
-		pr_debug("%s: Invalid plug type detected\n", __func__);
-		snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_B1_CTL, 0x02, 0x02);
-		taiko_codec_cleanup_hs_polling(codec);
-		taiko_codec_enable_hs_detect(codec, 1,
-					     MBHC_USE_MB_TRIGGER |
-					     MBHC_USE_HPHL_TRIGGER, false);
-	} else if (plug_type == PLUG_TYPE_GND_MIC_SWAP) {
-		pr_debug("%s: GND-MIC swapped plug type detected\n", __func__);
-		taiko_codec_report_plug(codec, 1, SND_JACK_UNSUPPORTED);
-		taiko_codec_cleanup_hs_polling(codec);
-		taiko_codec_enable_hs_detect(codec, 0, 0, false);
-	} else if (plug_type == PLUG_TYPE_HEADPHONE) {
-		pr_debug("%s: Headphone Detected\n", __func__);
-		taiko_codec_report_plug(codec, 1, SND_JACK_HEADPHONE);
-		taiko_codec_cleanup_hs_polling(codec);
-		taiko_codec_enable_hs_detect(codec, 0, 0, false);
-	} else if (plug_type == PLUG_TYPE_HEADSET) {
-		pr_debug("%s: Headset detected\n", __func__);
-		taiko_codec_report_plug(codec, 1, SND_JACK_HEADSET);
-
-		/* avoid false button press detect */
-		msleep(50);
-		taiko_codec_start_hs_polling(codec);
-	}
-}
-
-/* called only from interrupt which is under codec_resource_lock acquisition */
-static void taiko_hs_insert_irq_gpio(struct taiko_priv *priv, bool is_removal)
-{
-	struct snd_soc_codec *codec = priv->codec;
-
-	if (!is_removal) {
-		pr_debug("%s: MIC trigger insertion interrupt\n", __func__);
-
-		rmb();
-		if (priv->lpi_enabled)
-			msleep(100);
-
-		rmb();
-		if (!priv->lpi_enabled) {
-			pr_debug("%s: lpi is disabled\n", __func__);
-		} else if (gpio_get_value_cansleep(priv->mbhc_cfg.gpio) ==
-			   priv->mbhc_cfg.gpio_level_insert) {
-			pr_debug(
-			"%s: Valid insertion, detect plug type\n", __func__);
-			taiko_codec_decide_gpio_plug(codec);
-		} else {
-			pr_debug(
-			"%s: Invalid insertion stop plug detection\n",
-			__func__);
-		}
-	} else {
-		pr_err("%s: GPIO used, invalid MBHC Removal\n", __func__);
-	}
-}
-
-/* called only from interrupt which is under codec_resource_lock acquisition */
-static void taiko_hs_insert_irq_nogpio(struct taiko_priv *priv, bool is_removal,
-				       bool is_mb_trigger)
-{
-	int ret;
-	struct snd_soc_codec *codec = priv->codec;
-	struct wcd9xxx *core = dev_get_drvdata(priv->codec->dev->parent);
-
-	if (is_removal) {
-		/* cancel possiblely running hs detect work */
-		taiko_cancel_hs_detect_plug(priv);
-
-		/*
-		 * If headphone is removed while playback is in progress,
-		 * it is possible that micbias will be switched to VDDIO.
-		 */
-		taiko_codec_switch_micbias(codec, 0);
-		if (priv->current_plug == PLUG_TYPE_HEADPHONE)
-			taiko_codec_report_plug(codec, 0, SND_JACK_HEADPHONE);
-		else if (priv->current_plug == PLUG_TYPE_GND_MIC_SWAP)
-			taiko_codec_report_plug(codec, 0, SND_JACK_UNSUPPORTED);
-		else
-			WARN(1, "%s: Unexpected current plug type %d\n",
-			     __func__, priv->current_plug);
-		taiko_codec_shutdown_hs_removal_detect(codec);
-		taiko_codec_enable_hs_detect(codec, 1,
-					     MBHC_USE_MB_TRIGGER |
-					     MBHC_USE_HPHL_TRIGGER,
-					     true);
-	} else if (is_mb_trigger && !is_removal) {
-		pr_debug("%s: Waiting for Headphone left trigger\n",
-			__func__);
-		wcd9xxx_lock_sleep(core);
-		if (schedule_delayed_work(&priv->mbhc_insert_dwork,
-					  usecs_to_jiffies(1000000)) == 0) {
-			pr_err("%s: mbhc_insert_dwork is already scheduled\n",
-			       __func__);
-			wcd9xxx_unlock_sleep(core);
-		}
-		taiko_codec_enable_hs_detect(codec, 1, MBHC_USE_HPHL_TRIGGER,
-					     false);
-	} else  {
-		ret = cancel_delayed_work(&priv->mbhc_insert_dwork);
-		if (ret != 0) {
-			pr_debug(
-			"%s: Complete plug insertion, Detecting plug type\n",
-			__func__);
-			taiko_codec_detect_plug_type(codec);
-			wcd9xxx_unlock_sleep(core);
-		} else {
-			wcd9xxx_enable_irq(codec->control_data,
-					   TAIKO_IRQ_MBHC_INSERTION);
-			pr_err("%s: Error detecting plug insertion\n",
-			       __func__);
-		}
-	}
-}
-
-static irqreturn_t taiko_hs_insert_irq(int irq, void *data)
-{
-	bool is_mb_trigger, is_removal;
-	struct taiko_priv *priv = data;
-	struct snd_soc_codec *codec = priv->codec;
-
-	pr_debug("%s: enter\n", __func__);
-	TAIKO_ACQUIRE_LOCK(priv->codec_resource_lock);
-	wcd9xxx_disable_irq(codec->control_data, TAIKO_IRQ_MBHC_INSERTION);
-
-	is_mb_trigger = !!(snd_soc_read(codec, priv->mbhc_bias_regs.mbhc_reg) &
-					0x10);
-	is_removal = !!(snd_soc_read(codec, TAIKO_A_CDC_MBHC_INT_CTL) & 0x02);
-	snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_INT_CTL, 0x03, 0x00);
-
-	/* Turn off both HPH and MIC line schmitt triggers */
-	snd_soc_update_bits(codec, priv->mbhc_bias_regs.mbhc_reg, 0x90, 0x00);
-	snd_soc_update_bits(codec, TAIKO_A_MBHC_HPH, 0x13, 0x00);
-	snd_soc_update_bits(codec, priv->mbhc_bias_regs.ctl_reg, 0x01, 0x00);
-
-	if (priv->mbhc_cfg.gpio)
-		taiko_hs_insert_irq_gpio(priv, is_removal);
-	else
-		taiko_hs_insert_irq_nogpio(priv, is_removal, is_mb_trigger);
-
-	TAIKO_RELEASE_LOCK(priv->codec_resource_lock);
-	return IRQ_HANDLED;
-}
-
-static bool is_valid_mic_voltage(struct snd_soc_codec *codec, s32 mic_mv)
-{
-	struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
-	const struct taiko_mbhc_plug_type_cfg *plug_type =
-	    TAIKO_MBHC_CAL_PLUG_TYPE_PTR(taiko->mbhc_cfg.calibration);
-	const s16 v_hs_max = taiko_get_current_v_hs_max(taiko);
-
-	return (!(mic_mv > 10 && mic_mv < 80) && (mic_mv > plug_type->v_no_mic)
-		&& (mic_mv < v_hs_max)) ? true : false;
-}
-
-/* called under codec_resource_lock acquisition
- * returns true if mic voltage range is back to normal insertion
- * returns false either if timedout or removed */
-static bool taiko_hs_remove_settle(struct snd_soc_codec *codec)
-{
-	int i;
-	bool timedout, settled = false;
-	s32 mic_mv[MBHC_NUM_DCE_PLUG_DETECT];
-	short mb_v[MBHC_NUM_DCE_PLUG_DETECT];
-	unsigned long retry = 0, timeout;
-	struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
-	const s16 v_hs_max = taiko_get_current_v_hs_max(taiko);
-
-	timeout = jiffies + msecs_to_jiffies(TAIKO_HS_DETECT_PLUG_TIME_MS);
-	while (!(timedout = time_after(jiffies, timeout))) {
-		retry++;
-		if (taiko->mbhc_cfg.gpio && taiko_hs_gpio_level_remove(taiko)) {
-			pr_debug("%s: GPIO indicates removal\n", __func__);
-			break;
-		}
-
-		if (taiko->mbhc_cfg.gpio) {
-			if (retry > 1)
-				msleep(250);
-			else
-				msleep(50);
-		}
-
-		if (taiko->mbhc_cfg.gpio && taiko_hs_gpio_level_remove(taiko)) {
-			pr_debug("%s: GPIO indicates removal\n", __func__);
-			break;
-		}
-
-		for (i = 0; i < MBHC_NUM_DCE_PLUG_DETECT; i++) {
-			mb_v[i] = taiko_codec_sta_dce(codec, 1,  true);
-			mic_mv[i] = taiko_codec_sta_dce_v(codec, 1 , mb_v[i]);
-			pr_debug("%s : DCE run %lu, mic_mv = %d(%x)\n",
-				 __func__, retry, mic_mv[i], mb_v[i]);
-		}
-
-		if (taiko->mbhc_cfg.gpio && taiko_hs_gpio_level_remove(taiko)) {
-			pr_debug("%s: GPIO indicates removal\n", __func__);
-			break;
-		}
-
-		if (taiko->current_plug == PLUG_TYPE_NONE) {
-			pr_debug("%s : headset/headphone is removed\n",
-				 __func__);
-			break;
-		}
-
-		for (i = 0; i < MBHC_NUM_DCE_PLUG_DETECT; i++)
-			if (!is_valid_mic_voltage(codec, mic_mv[i]))
-				break;
-
-		if (i == MBHC_NUM_DCE_PLUG_DETECT) {
-			pr_debug("%s: MIC voltage settled\n", __func__);
-			settled = true;
-			msleep(200);
-			break;
-		}
-
-		/* only for non-GPIO remove irq */
-		if (!taiko->mbhc_cfg.gpio) {
-			for (i = 0; i < MBHC_NUM_DCE_PLUG_DETECT; i++)
-				if (mic_mv[i] < v_hs_max)
-					break;
-			if (i == MBHC_NUM_DCE_PLUG_DETECT) {
-				pr_debug("%s: Headset is removed\n", __func__);
-				break;
-			}
-		}
-	}
-
-	if (timedout)
-		pr_debug("%s: Microphone did not settle in %d seconds\n",
-			 __func__, TAIKO_HS_DETECT_PLUG_TIME_MS);
-	return settled;
-}
-
-/* called only from interrupt which is under codec_resource_lock acquisition */
-static void taiko_hs_remove_irq_gpio(struct taiko_priv *priv)
-{
-	struct snd_soc_codec *codec = priv->codec;
-
-	if (taiko_hs_remove_settle(codec))
-		taiko_codec_start_hs_polling(codec);
-	pr_debug("%s: remove settle done\n", __func__);
-}
-
-/* called only from interrupt which is under codec_resource_lock acquisition */
-static void taiko_hs_remove_irq_nogpio(struct taiko_priv *priv)
-{
-	short bias_value;
-	bool removed = true;
-	struct snd_soc_codec *codec = priv->codec;
-	const struct taiko_mbhc_general_cfg *generic =
-	    TAIKO_MBHC_CAL_GENERAL_PTR(priv->mbhc_cfg.calibration);
-	int min_us = TAIKO_FAKE_REMOVAL_MIN_PERIOD_MS * 1000;
-
-	if (priv->current_plug != PLUG_TYPE_HEADSET) {
-		pr_debug("%s(): Headset is not inserted, ignore removal\n",
-			 __func__);
-		snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_CLK_CTL,
-				    0x08, 0x08);
-		return;
-	}
-
-	usleep_range(generic->t_shutdown_plug_rem,
-		     generic->t_shutdown_plug_rem);
-
-	do {
-		bias_value = taiko_codec_sta_dce(codec, 1,  true);
-		pr_debug("%s: DCE %d,%d, %d us left\n", __func__, bias_value,
-			 taiko_codec_sta_dce_v(codec, 1, bias_value), min_us);
-		if (bias_value < taiko_get_current_v_ins(priv, false)) {
-			pr_debug("%s: checking false removal\n", __func__);
-			msleep(500);
-			removed = !taiko_hs_remove_settle(codec);
-			pr_debug("%s: headset %sactually removed\n", __func__,
-				 removed ? "" : "not ");
-			break;
-		}
-		min_us -= priv->mbhc_data.t_dce;
-	} while (min_us > 0);
-
-	if (removed) {
-		/* cancel possiblely running hs detect work */
-		taiko_cancel_hs_detect_plug(priv);
-		/*
-		 * If this removal is not false, first check the micbias
-		 * switch status and switch it to LDOH if it is already
-		 * switched to VDDIO.
-		 */
-		taiko_codec_switch_micbias(codec, 0);
-
-		taiko_codec_report_plug(codec, 0, SND_JACK_HEADSET);
-		taiko_codec_cleanup_hs_polling(codec);
-		taiko_codec_enable_hs_detect(codec, 1,
-					     MBHC_USE_MB_TRIGGER |
-					     MBHC_USE_HPHL_TRIGGER,
-					     true);
-	} else {
-		taiko_codec_start_hs_polling(codec);
-	}
-}
-
-static irqreturn_t taiko_hs_remove_irq(int irq, void *data)
-{
-	struct taiko_priv *priv = data;
-	bool vddio;
-	pr_debug("%s: enter, removal interrupt\n", __func__);
-
-	TAIKO_ACQUIRE_LOCK(priv->codec_resource_lock);
-	vddio = (priv->mbhc_data.micb_mv != VDDIO_MICBIAS_MV &&
-		 priv->mbhc_micbias_switched);
-	if (vddio)
-		__taiko_codec_switch_micbias(priv->codec, 0, false, true);
-
-	if (priv->mbhc_cfg.gpio)
-		taiko_hs_remove_irq_gpio(priv);
-	else
-		taiko_hs_remove_irq_nogpio(priv);
-
-	/* if driver turned off vddio switch and headset is not removed,
-	 * turn on the vddio switch back, if headset is removed then vddio
-	 * switch is off by time now and shouldn't be turn on again from here */
-	if (vddio && priv->current_plug == PLUG_TYPE_HEADSET)
-		__taiko_codec_switch_micbias(priv->codec, 1, true, true);
-	TAIKO_RELEASE_LOCK(priv->codec_resource_lock);
-
-	return IRQ_HANDLED;
-}
-
-static void taiko_mbhc_insert_work(struct work_struct *work)
-{
-	struct delayed_work *dwork;
-	struct taiko_priv *taiko;
-	struct snd_soc_codec *codec;
-	struct wcd9xxx *taiko_core;
-
-	dwork = to_delayed_work(work);
-	taiko = container_of(dwork, struct taiko_priv, mbhc_insert_dwork);
-	codec = taiko->codec;
-	taiko_core = dev_get_drvdata(codec->dev->parent);
-
-	pr_debug("%s:\n", __func__);
-
-	/* Turn off both HPH and MIC line schmitt triggers */
-	snd_soc_update_bits(codec, taiko->mbhc_bias_regs.mbhc_reg, 0x90, 0x00);
-	snd_soc_update_bits(codec, TAIKO_A_MBHC_HPH, 0x13, 0x00);
-	snd_soc_update_bits(codec, taiko->mbhc_bias_regs.ctl_reg, 0x01, 0x00);
-	wcd9xxx_disable_irq_sync(codec->control_data, TAIKO_IRQ_MBHC_INSERTION);
-	taiko_codec_detect_plug_type(codec);
-	wcd9xxx_unlock_sleep(taiko_core);
-}
-
-static void taiko_hs_gpio_handler(struct snd_soc_codec *codec)
-{
-	bool insert;
-	struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
-	bool is_removed = false;
-
-	pr_debug("%s: enter\n", __func__);
-
-	taiko->in_gpio_handler = true;
-	/* Wait here for debounce time */
-	usleep_range(TAIKO_GPIO_IRQ_DEBOUNCE_TIME_US,
-		     TAIKO_GPIO_IRQ_DEBOUNCE_TIME_US);
-
-	TAIKO_ACQUIRE_LOCK(taiko->codec_resource_lock);
-
-	/* cancel pending button press */
-	if (taiko_cancel_btn_work(taiko))
-		pr_debug("%s: button press is canceled\n", __func__);
-
-	insert = (gpio_get_value_cansleep(taiko->mbhc_cfg.gpio) ==
-		  taiko->mbhc_cfg.gpio_level_insert);
-	if ((taiko->current_plug == PLUG_TYPE_NONE) && insert) {
-		taiko->lpi_enabled = false;
-		wmb();
-
-		/* cancel detect plug */
-		taiko_cancel_hs_detect_plug(taiko);
-
-		/* Disable Mic Bias pull down and HPH Switch to GND */
-		snd_soc_update_bits(codec, taiko->mbhc_bias_regs.ctl_reg, 0x01,
-				    0x00);
-		snd_soc_update_bits(codec, TAIKO_A_MBHC_HPH, 0x01, 0x00);
-		taiko_codec_detect_plug_type(codec);
-	} else if ((taiko->current_plug != PLUG_TYPE_NONE) && !insert) {
-		taiko->lpi_enabled = false;
-		wmb();
-
-		/* cancel detect plug */
-		taiko_cancel_hs_detect_plug(taiko);
-
-		if (taiko->current_plug == PLUG_TYPE_HEADPHONE) {
-			taiko_codec_report_plug(codec, 0, SND_JACK_HEADPHONE);
-			is_removed = true;
-		} else if (taiko->current_plug == PLUG_TYPE_GND_MIC_SWAP) {
-			taiko_codec_report_plug(codec, 0, SND_JACK_UNSUPPORTED);
-			is_removed = true;
-		} else if (taiko->current_plug == PLUG_TYPE_HEADSET) {
-			taiko_codec_pause_hs_polling(codec);
-			taiko_codec_cleanup_hs_polling(codec);
-			taiko_codec_report_plug(codec, 0, SND_JACK_HEADSET);
-			is_removed = true;
-		}
-
-		if (is_removed) {
-			/* Enable Mic Bias pull down and HPH Switch to GND */
-			snd_soc_update_bits(codec,
-					    taiko->mbhc_bias_regs.ctl_reg, 0x01,
-					    0x01);
-			snd_soc_update_bits(codec, TAIKO_A_MBHC_HPH, 0x01,
-					    0x01);
-			/* Make sure mic trigger is turned off */
-			snd_soc_update_bits(codec,
-					    taiko->mbhc_bias_regs.ctl_reg,
-					    0x01, 0x01);
-			snd_soc_update_bits(codec,
-					    taiko->mbhc_bias_regs.mbhc_reg,
-					    0x90, 0x00);
-			/* Reset MBHC State Machine */
-			snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_CLK_CTL,
-					    0x08, 0x08);
-			snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_CLK_CTL,
-					    0x08, 0x00);
-			/* Turn off override */
-			taiko_turn_onoff_override(codec, false);
-		}
-	}
-
-	taiko->in_gpio_handler = false;
-	TAIKO_RELEASE_LOCK(taiko->codec_resource_lock);
-	pr_debug("%s: leave\n", __func__);
-}
-
-static irqreturn_t taiko_mechanical_plug_detect_irq(int irq, void *data)
-{
-	int r = IRQ_HANDLED;
-	struct snd_soc_codec *codec = data;
-
-	if (unlikely(wcd9xxx_lock_sleep(codec->control_data) == false)) {
-		pr_warn("%s: failed to hold suspend\n", __func__);
-		r = IRQ_NONE;
-	} else {
-		taiko_hs_gpio_handler(codec);
-		wcd9xxx_unlock_sleep(codec->control_data);
-	}
-
-	return r;
-}
-
-static int taiko_mbhc_init_and_calibrate(struct taiko_priv *taiko)
-{
-	int ret = 0;
-	struct snd_soc_codec *codec = taiko->codec;
-
-	taiko->mbhc_cfg.mclk_cb_fn(codec, 1, false);
-	taiko_mbhc_init(codec);
-	taiko_mbhc_cal(codec);
-	taiko_mbhc_calc_thres(codec);
-	taiko->mbhc_cfg.mclk_cb_fn(codec, 0, false);
-	taiko_codec_calibrate_hs_polling(codec);
-	if (!taiko->mbhc_cfg.gpio) {
-		ret = taiko_codec_enable_hs_detect(codec, 1,
-						   MBHC_USE_MB_TRIGGER |
-						   MBHC_USE_HPHL_TRIGGER,
-						   false);
-
-		if (IS_ERR_VALUE(ret))
-			pr_err("%s: Failed to setup MBHC detection\n",
-			       __func__);
-	} else {
-		/* Enable Mic Bias pull down and HPH Switch to GND */
-		snd_soc_update_bits(codec, taiko->mbhc_bias_regs.ctl_reg,
-				    0x01, 0x01);
-		snd_soc_update_bits(codec, TAIKO_A_MBHC_HPH, 0x01, 0x01);
-		INIT_WORK(&taiko->hs_correct_plug_work,
-			  taiko_hs_correct_gpio_plug);
-	}
-
-	if (!IS_ERR_VALUE(ret)) {
-		snd_soc_update_bits(codec, TAIKO_A_RX_HPH_OCP_CTL, 0x10, 0x10);
-		wcd9xxx_enable_irq(codec->control_data,
-				 TAIKO_IRQ_HPH_PA_OCPL_FAULT);
-		wcd9xxx_enable_irq(codec->control_data,
-				 TAIKO_IRQ_HPH_PA_OCPR_FAULT);
-
-		if (taiko->mbhc_cfg.gpio) {
-			ret = request_threaded_irq(taiko->mbhc_cfg.gpio_irq,
-					       NULL,
-					       taiko_mechanical_plug_detect_irq,
-					       (IRQF_TRIGGER_RISING |
-						IRQF_TRIGGER_FALLING),
-					       "taiko-gpio", codec);
-			if (!IS_ERR_VALUE(ret)) {
-				ret = enable_irq_wake(taiko->mbhc_cfg.gpio_irq);
-				/* Bootup time detection */
-				taiko_hs_gpio_handler(codec);
-			}
-		}
-	}
-
-	return ret;
-}
-
-static void mbhc_fw_read(struct work_struct *work)
-{
-	struct delayed_work *dwork;
-	struct taiko_priv *taiko;
-	struct snd_soc_codec *codec;
-	const struct firmware *fw;
-	int ret = -1, retry = 0;
-
-	dwork = to_delayed_work(work);
-	taiko = container_of(dwork, struct taiko_priv, mbhc_firmware_dwork);
-	codec = taiko->codec;
-
-	while (retry < MBHC_FW_READ_ATTEMPTS) {
-		retry++;
-		pr_info("%s:Attempt %d to request MBHC firmware\n",
-			__func__, retry);
-		ret = request_firmware(&fw, "wcd9320/wcd9320_mbhc.bin",
-					codec->dev);
-
-		if (ret != 0) {
-			usleep_range(MBHC_FW_READ_TIMEOUT,
-				     MBHC_FW_READ_TIMEOUT);
-		} else {
-			pr_info("%s: MBHC Firmware read succesful\n", __func__);
-			break;
-		}
-	}
-
-	if (ret != 0) {
-		pr_err("%s: Cannot load MBHC firmware use default cal\n",
-			__func__);
-	} else if (taiko_mbhc_fw_validate(fw) == false) {
-		pr_err("%s: Invalid MBHC cal data size use default cal\n",
-			 __func__);
-		release_firmware(fw);
-	} else {
-		taiko->mbhc_cfg.calibration = (void *)fw->data;
-		taiko->mbhc_fw = fw;
-	}
-
-	(void) taiko_mbhc_init_and_calibrate(taiko);
-}
-
-int taiko_hs_detect(struct snd_soc_codec *codec,
-		    const struct taiko_mbhc_config *cfg)
-{
-	struct taiko_priv *taiko;
-	int rc = 0;
-
-	if (!codec) {
-		pr_err("%s: no codec\n", __func__);
-		return -EINVAL;
-	}
-
-	if (!cfg->calibration) {
-		pr_warn("%s: mbhc is not configured\n", __func__);
-		return 0;
-	}
-
-	if (cfg->mclk_rate != TAIKO_MCLK_RATE_12288KHZ) {
-		if (cfg->mclk_rate == TAIKO_MCLK_RATE_9600KHZ)
-			pr_err("Error: clock rate %dHz is not yet supported\n",
-			       cfg->mclk_rate);
-		else
-			pr_err("Error: unsupported clock rate %d\n",
-			       cfg->mclk_rate);
-		return -EINVAL;
-	}
-
-	taiko = snd_soc_codec_get_drvdata(codec);
-	taiko->mbhc_cfg = *cfg;
-	taiko->in_gpio_handler = false;
-	taiko->current_plug = PLUG_TYPE_NONE;
-	taiko->lpi_enabled = false;
-	taiko_get_mbhc_micbias_regs(codec, &taiko->mbhc_bias_regs);
-
-	/* Put CFILT in fast mode by default */
-	snd_soc_update_bits(codec, taiko->mbhc_bias_regs.cfilt_ctl,
-			    0x40, TAIKO_CFILT_FAST_MODE);
-	INIT_DELAYED_WORK(&taiko->mbhc_firmware_dwork, mbhc_fw_read);
-	INIT_DELAYED_WORK(&taiko->mbhc_btn_dwork, btn_lpress_fn);
-	INIT_WORK(&taiko->hphlocp_work, hphlocp_off_report);
-	INIT_WORK(&taiko->hphrocp_work, hphrocp_off_report);
-	INIT_DELAYED_WORK(&taiko->mbhc_insert_dwork, taiko_mbhc_insert_work);
-
-	if (!taiko->mbhc_cfg.read_fw_bin)
-		rc = taiko_mbhc_init_and_calibrate(taiko);
-	else
-		schedule_delayed_work(&taiko->mbhc_firmware_dwork,
-				      usecs_to_jiffies(MBHC_FW_READ_TIMEOUT));
-
-	return rc;
-}
-EXPORT_SYMBOL_GPL(taiko_hs_detect);
-
 static unsigned long slimbus_value;
 
 static irqreturn_t taiko_slimbus_irq(int irq, void *data)
@@ -6868,8 +4196,8 @@
 		}
 		wcd9xxx_interface_reg_write(codec->control_data,
 			TAIKO_SLIM_PGD_PORT_INT_CLR0 + i, 0xFF);
-	}
 
+	}
 	return IRQ_HANDLED;
 }
 
@@ -6900,7 +4228,6 @@
 	/** end of Ear PA load 32 */
 };
 
-
 static const struct taiko_reg_mask_val taiko_1_0_class_h_hph[] = {
 
 	/* CLASS-H HPH  IDLE_THRESHOLD Table */
@@ -6955,7 +4282,7 @@
 static int taiko_handle_pdata(struct taiko_priv *taiko)
 {
 	struct snd_soc_codec *codec = taiko->codec;
-	struct wcd9xxx_pdata *pdata = taiko->pdata;
+	struct wcd9xxx_pdata *pdata = taiko->resmgr.pdata;
 	int k1, k2, k3, rc = 0;
 	u8 leg_mode, txfe_bypass, txfe_buff, flag;
 	u8 i = 0, j = 0;
@@ -6973,46 +4300,38 @@
 	flag = pdata->amic_settings.use_pdata;
 
 	/* Make sure settings are correct */
-	if ((pdata->micbias.ldoh_v > TAIKO_LDOH_2P85_V) ||
-	    (pdata->micbias.bias1_cfilt_sel > TAIKO_CFILT3_SEL) ||
-	    (pdata->micbias.bias2_cfilt_sel > TAIKO_CFILT3_SEL) ||
-	    (pdata->micbias.bias3_cfilt_sel > TAIKO_CFILT3_SEL) ||
-	    (pdata->micbias.bias4_cfilt_sel > TAIKO_CFILT3_SEL)) {
+	if ((pdata->micbias.ldoh_v > WCD9XXX_LDOH_3P0_V) ||
+	    (pdata->micbias.bias1_cfilt_sel > WCD9XXX_CFILT3_SEL) ||
+	    (pdata->micbias.bias2_cfilt_sel > WCD9XXX_CFILT3_SEL) ||
+	    (pdata->micbias.bias3_cfilt_sel > WCD9XXX_CFILT3_SEL) ||
+	    (pdata->micbias.bias4_cfilt_sel > WCD9XXX_CFILT3_SEL)) {
 		rc = -EINVAL;
 		goto done;
 	}
-
 	/* figure out k value */
-	k1 = taiko_find_k_value(pdata->micbias.ldoh_v,
-		pdata->micbias.cfilt1_mv);
-	k2 = taiko_find_k_value(pdata->micbias.ldoh_v,
-		pdata->micbias.cfilt2_mv);
-	k3 = taiko_find_k_value(pdata->micbias.ldoh_v,
-		pdata->micbias.cfilt3_mv);
+	k1 = wcd9xxx_resmgr_get_k_val(&taiko->resmgr, pdata->micbias.cfilt1_mv);
+	k2 = wcd9xxx_resmgr_get_k_val(&taiko->resmgr, pdata->micbias.cfilt2_mv);
+	k3 = wcd9xxx_resmgr_get_k_val(&taiko->resmgr, pdata->micbias.cfilt3_mv);
 
 	if (IS_ERR_VALUE(k1) || IS_ERR_VALUE(k2) || IS_ERR_VALUE(k3)) {
 		rc = -EINVAL;
 		goto done;
 	}
-
 	/* Set voltage level and always use LDO */
 	snd_soc_update_bits(codec, TAIKO_A_LDO_H_MODE_1, 0x0C,
-		(pdata->micbias.ldoh_v << 2));
+			    (pdata->micbias.ldoh_v << 2));
 
-	snd_soc_update_bits(codec, TAIKO_A_MICB_CFILT_1_VAL, 0xFC,
-		(k1 << 2));
-	snd_soc_update_bits(codec, TAIKO_A_MICB_CFILT_2_VAL, 0xFC,
-		(k2 << 2));
-	snd_soc_update_bits(codec, TAIKO_A_MICB_CFILT_3_VAL, 0xFC,
-		(k3 << 2));
+	snd_soc_update_bits(codec, TAIKO_A_MICB_CFILT_1_VAL, 0xFC, (k1 << 2));
+	snd_soc_update_bits(codec, TAIKO_A_MICB_CFILT_2_VAL, 0xFC, (k2 << 2));
+	snd_soc_update_bits(codec, TAIKO_A_MICB_CFILT_3_VAL, 0xFC, (k3 << 2));
 
 	snd_soc_update_bits(codec, TAIKO_A_MICB_1_CTL, 0x60,
-		(pdata->micbias.bias1_cfilt_sel << 5));
+			    (pdata->micbias.bias1_cfilt_sel << 5));
 	snd_soc_update_bits(codec, TAIKO_A_MICB_2_CTL, 0x60,
-		(pdata->micbias.bias2_cfilt_sel << 5));
+			    (pdata->micbias.bias2_cfilt_sel << 5));
 	snd_soc_update_bits(codec, TAIKO_A_MICB_3_CTL, 0x60,
-		(pdata->micbias.bias3_cfilt_sel << 5));
-	snd_soc_update_bits(codec, taiko->reg_addr.micb_4_ctl, 0x60,
+			    (pdata->micbias.bias3_cfilt_sel << 5));
+	snd_soc_update_bits(codec, taiko->resmgr.reg_addr->micb_4_ctl, 0x60,
 			    (pdata->micbias.bias4_cfilt_sel << 5));
 
 	for (i = 0; i < 6; j++, i += 2) {
@@ -7084,6 +4403,20 @@
 		}
 	}
 
+	/* Set micbias capless mode with tail current */
+	value = (pdata->micbias.bias1_cap_mode == MICBIAS_EXT_BYP_CAP ?
+		 0x00 : 0x16);
+	snd_soc_update_bits(codec, TAIKO_A_MICB_1_CTL, 0x1E, value);
+	value = (pdata->micbias.bias2_cap_mode == MICBIAS_EXT_BYP_CAP ?
+		 0x00 : 0x16);
+	snd_soc_update_bits(codec, TAIKO_A_MICB_2_CTL, 0x1E, value);
+	value = (pdata->micbias.bias3_cap_mode == MICBIAS_EXT_BYP_CAP ?
+		 0x00 : 0x16);
+	snd_soc_update_bits(codec, TAIKO_A_MICB_3_CTL, 0x1E, value);
+	value = (pdata->micbias.bias4_cap_mode == MICBIAS_EXT_BYP_CAP ?
+		 0x00 : 0x16);
+	snd_soc_update_bits(codec, TAIKO_A_MICB_4_CTL, 0x1E, value);
+
 	taiko_config_ear_class_h(codec, 32);
 	taiko_config_hph_class_h(codec, 16);
 
@@ -7138,6 +4471,9 @@
 	TAIKO_REG_VAL(TAIKO_A_CDC_RX5_B6_CTL, 0x80),
 	TAIKO_REG_VAL(TAIKO_A_CDC_RX6_B6_CTL, 0x80),
 	TAIKO_REG_VAL(TAIKO_A_CDC_RX7_B6_CTL, 0x80),
+
+	/* TX VHIGH comparator */
+	TAIKO_REG_VAL(TAIKO_A_TX_SUP_SWITCH_CTRL_2, 0x90),
 };
 
 static const struct taiko_reg_mask_val taiko_1_0_reg_defaults[] = {
@@ -7255,227 +4591,52 @@
 				taiko_codec_reg_init_val[i].val);
 }
 
-static void taiko_update_reg_address(struct taiko_priv *priv)
-{
-	struct taiko_reg_address *reg_addr = &priv->reg_addr;
-	reg_addr->micb_4_mbhc = TAIKO_A_MICB_4_MBHC;
-	reg_addr->micb_4_int_rbias = TAIKO_A_MICB_4_INT_RBIAS;
-	reg_addr->micb_4_ctl = TAIKO_A_MICB_4_CTL;
-
-}
-
-#ifdef CONFIG_DEBUG_FS
-static int codec_debug_open(struct inode *inode, struct file *file)
-{
-	file->private_data = inode->i_private;
-	return 0;
-}
-
-static ssize_t codec_debug_write(struct file *filp,
-	const char __user *ubuf, size_t cnt, loff_t *ppos)
-{
-	char lbuf[32];
-	char *buf;
-	int rc;
-	struct taiko_priv *taiko = filp->private_data;
-
-	if (cnt > sizeof(lbuf) - 1)
-		return -EINVAL;
-
-	rc = copy_from_user(lbuf, ubuf, cnt);
-	if (rc)
-		return -EFAULT;
-
-	lbuf[cnt] = '\0';
-	buf = (char *)lbuf;
-	taiko->no_mic_headset_override = (*strsep(&buf, " ") == '0') ?
-					     false : true;
-	return rc;
-}
-
-static ssize_t codec_mbhc_debug_read(struct file *file, char __user *buf,
-				     size_t count, loff_t *pos)
-{
-	const int size = 768;
-	char buffer[size];
-	int n = 0;
-	struct taiko_priv *taiko = file->private_data;
-	struct snd_soc_codec *codec = taiko->codec;
-	const struct mbhc_internal_cal_data *p = &taiko->mbhc_data;
-	const s16 v_ins_hu_cur = taiko_get_current_v_ins(taiko, true);
-	const s16 v_ins_h_cur = taiko_get_current_v_ins(taiko, false);
-
-	n = scnprintf(buffer, size - n, "dce_z = %x(%dmv)\n",  p->dce_z,
-		     taiko_codec_sta_dce_v(codec, 1, p->dce_z));
-	n += scnprintf(buffer + n, size - n, "dce_mb = %x(%dmv)\n",
-		       p->dce_mb, taiko_codec_sta_dce_v(codec, 1, p->dce_mb));
-	n += scnprintf(buffer + n, size - n, "sta_z = %x(%dmv)\n",
-		       p->sta_z, taiko_codec_sta_dce_v(codec, 0, p->sta_z));
-	n += scnprintf(buffer + n, size - n, "sta_mb = %x(%dmv)\n",
-		       p->sta_mb, taiko_codec_sta_dce_v(codec, 0, p->sta_mb));
-	n += scnprintf(buffer + n, size - n, "t_dce = %x\n",  p->t_dce);
-	n += scnprintf(buffer + n, size - n, "t_sta = %x\n",  p->t_sta);
-	n += scnprintf(buffer + n, size - n, "micb_mv = %dmv\n",
-		       p->micb_mv);
-	n += scnprintf(buffer + n, size - n, "v_ins_hu = %x(%dmv)%s\n",
-		       p->v_ins_hu,
-		       taiko_codec_sta_dce_v(codec, 0, p->v_ins_hu),
-		       p->v_ins_hu == v_ins_hu_cur ? "*" : "");
-	n += scnprintf(buffer + n, size - n, "v_ins_h = %x(%dmv)%s\n",
-		       p->v_ins_h, taiko_codec_sta_dce_v(codec, 1, p->v_ins_h),
-		       p->v_ins_h == v_ins_h_cur ? "*" : "");
-	n += scnprintf(buffer + n, size - n, "adj_v_ins_hu = %x(%dmv)%s\n",
-		       p->adj_v_ins_hu,
-		       taiko_codec_sta_dce_v(codec, 0, p->adj_v_ins_hu),
-		       p->adj_v_ins_hu == v_ins_hu_cur ? "*" : "");
-	n += scnprintf(buffer + n, size - n, "adj_v_ins_h = %x(%dmv)%s\n",
-		       p->adj_v_ins_h,
-		       taiko_codec_sta_dce_v(codec, 1, p->adj_v_ins_h),
-		       p->adj_v_ins_h == v_ins_h_cur ? "*" : "");
-	n += scnprintf(buffer + n, size - n, "v_b1_hu = %x(%dmv)\n",
-		       p->v_b1_hu, taiko_codec_sta_dce_v(codec, 0, p->v_b1_hu));
-	n += scnprintf(buffer + n, size - n, "v_b1_h = %x(%dmv)\n",
-		       p->v_b1_h, taiko_codec_sta_dce_v(codec, 1, p->v_b1_h));
-	n += scnprintf(buffer + n, size - n, "v_b1_huc = %x(%dmv)\n",
-		       p->v_b1_huc,
-		       taiko_codec_sta_dce_v(codec, 1, p->v_b1_huc));
-	n += scnprintf(buffer + n, size - n, "v_brh = %x(%dmv)\n",
-		       p->v_brh, taiko_codec_sta_dce_v(codec, 1, p->v_brh));
-	n += scnprintf(buffer + n, size - n, "v_brl = %x(%dmv)\n",  p->v_brl,
-		       taiko_codec_sta_dce_v(codec, 0, p->v_brl));
-	n += scnprintf(buffer + n, size - n, "v_no_mic = %x(%dmv)\n",
-		       p->v_no_mic,
-		       taiko_codec_sta_dce_v(codec, 0, p->v_no_mic));
-	n += scnprintf(buffer + n, size - n, "npoll = %d\n",  p->npoll);
-	n += scnprintf(buffer + n, size - n, "nbounce_wait = %d\n",
-		       p->nbounce_wait);
-	n += scnprintf(buffer + n, size - n, "v_inval_ins_low = %d\n",
-		       p->v_inval_ins_low);
-	n += scnprintf(buffer + n, size - n, "v_inval_ins_high = %d\n",
-		       p->v_inval_ins_high);
-	if (taiko->mbhc_cfg.gpio)
-		n += scnprintf(buffer + n, size - n, "GPIO insert = %d\n",
-			       taiko_hs_gpio_level_remove(taiko));
-	buffer[n] = 0;
-
-	return simple_read_from_buffer(buf, count, pos, buffer, n);
-}
-
-static const struct file_operations codec_debug_ops = {
-	.open = codec_debug_open,
-	.write = codec_debug_write,
-};
-
-static const struct file_operations codec_mbhc_debug_ops = {
-	.open = codec_debug_open,
-	.read = codec_mbhc_debug_read,
-};
-#endif
-
 static int taiko_setup_irqs(struct taiko_priv *taiko)
 {
-	int ret;
 	int i;
+	int ret = 0;
 	struct snd_soc_codec *codec = taiko->codec;
 
-	ret = wcd9xxx_request_irq(codec->control_data, TAIKO_IRQ_MBHC_INSERTION,
-				  taiko_hs_insert_irq, "Headset insert detect",
-				  taiko);
-	if (ret) {
-		pr_err("%s: Failed to request irq %d\n", __func__,
-			TAIKO_IRQ_MBHC_INSERTION);
-		goto err_insert_irq;
-	}
-	wcd9xxx_disable_irq(codec->control_data, TAIKO_IRQ_MBHC_INSERTION);
-
-	ret = wcd9xxx_request_irq(codec->control_data, TAIKO_IRQ_MBHC_REMOVAL,
-				  taiko_hs_remove_irq, "Headset remove detect",
-				  taiko);
-	if (ret) {
-		pr_err("%s: Failed to request irq %d\n", __func__,
-			TAIKO_IRQ_MBHC_REMOVAL);
-		goto err_remove_irq;
-	}
-
-	ret = wcd9xxx_request_irq(codec->control_data, TAIKO_IRQ_MBHC_POTENTIAL,
-				  taiko_dce_handler, "DC Estimation detect",
-				  taiko);
-	if (ret) {
-		pr_err("%s: Failed to request irq %d\n", __func__,
-			TAIKO_IRQ_MBHC_POTENTIAL);
-		goto err_potential_irq;
-	}
-
-	ret = wcd9xxx_request_irq(codec->control_data, TAIKO_IRQ_MBHC_RELEASE,
-				 taiko_release_handler, "Button Release detect",
-				 taiko);
-	if (ret) {
-		pr_err("%s: Failed to request irq %d\n", __func__,
-			TAIKO_IRQ_MBHC_RELEASE);
-		goto err_release_irq;
-	}
-
-	ret = wcd9xxx_request_irq(codec->control_data, TAIKO_IRQ_SLIMBUS,
+	ret = wcd9xxx_request_irq(codec->control_data, WCD9XXX_IRQ_SLIMBUS,
 				  taiko_slimbus_irq, "SLIMBUS Slave", taiko);
 	if (ret) {
 		pr_err("%s: Failed to request irq %d\n", __func__,
-			TAIKO_IRQ_SLIMBUS);
-		goto err_slimbus_irq;
+		       WCD9XXX_IRQ_SLIMBUS);
+		goto exit;
 	}
 
 	for (i = 0; i < WCD9XXX_SLIM_NUM_PORT_REG; i++)
 		wcd9xxx_interface_reg_write(codec->control_data,
-					   TAIKO_SLIM_PGD_PORT_INT_EN0 + i,
-					   0xFF);
-
-	ret = wcd9xxx_request_irq(codec->control_data,
-				  TAIKO_IRQ_HPH_PA_OCPL_FAULT,
-				  taiko_hphl_ocp_irq,
-				  "HPH_L OCP detect", taiko);
-	if (ret) {
-		pr_err("%s: Failed to request irq %d\n", __func__,
-			TAIKO_IRQ_HPH_PA_OCPL_FAULT);
-		goto err_hphl_ocp_irq;
-	}
-	wcd9xxx_disable_irq(codec->control_data, TAIKO_IRQ_HPH_PA_OCPL_FAULT);
-
-	ret = wcd9xxx_request_irq(codec->control_data,
-				  TAIKO_IRQ_HPH_PA_OCPR_FAULT,
-				  taiko_hphr_ocp_irq,
-				  "HPH_R OCP detect", taiko);
-	if (ret) {
-		pr_err("%s: Failed to request irq %d\n", __func__,
-		       TAIKO_IRQ_HPH_PA_OCPR_FAULT);
-		goto err_hphr_ocp_irq;
-	}
-	wcd9xxx_disable_irq(codec->control_data, TAIKO_IRQ_HPH_PA_OCPR_FAULT);
-
-err_hphr_ocp_irq:
-	wcd9xxx_free_irq(codec->control_data, TAIKO_IRQ_HPH_PA_OCPL_FAULT,
-			 taiko);
-err_hphl_ocp_irq:
-	wcd9xxx_free_irq(codec->control_data, TAIKO_IRQ_SLIMBUS, taiko);
-err_slimbus_irq:
-	wcd9xxx_free_irq(codec->control_data, TAIKO_IRQ_MBHC_RELEASE, taiko);
-err_release_irq:
-	wcd9xxx_free_irq(codec->control_data, TAIKO_IRQ_MBHC_POTENTIAL, taiko);
-err_potential_irq:
-	wcd9xxx_free_irq(codec->control_data, TAIKO_IRQ_MBHC_REMOVAL, taiko);
-err_remove_irq:
-	wcd9xxx_free_irq(codec->control_data, TAIKO_IRQ_MBHC_INSERTION, taiko);
-err_insert_irq:
-
+					    TAIKO_SLIM_PGD_PORT_INT_EN0 + i,
+					    0xFF);
+exit:
 	return ret;
 }
 
+int taiko_hs_detect(struct snd_soc_codec *codec,
+		    struct wcd9xxx_mbhc_config *mbhc_cfg)
+{
+	struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
+	return wcd9xxx_mbhc_start(&taiko->mbhc, mbhc_cfg);
+}
+EXPORT_SYMBOL_GPL(taiko_hs_detect);
+
+static struct wcd9xxx_reg_address taiko_reg_address = {
+	.micb_4_mbhc = TAIKO_A_MICB_4_MBHC,
+	.micb_4_int_rbias = TAIKO_A_MICB_4_INT_RBIAS,
+	.micb_4_ctl = TAIKO_A_MICB_4_CTL,
+};
+
 static int taiko_codec_probe(struct snd_soc_codec *codec)
 {
 	struct wcd9xxx *control;
 	struct taiko_priv *taiko;
+	struct wcd9xxx_pdata *pdata;
+	struct wcd9xxx *wcd9xxx;
 	struct snd_soc_dapm_context *dapm = &codec->dapm;
 	int ret = 0;
 	int i;
-	int ch_cnt;
+	void *ptr = NULL;
 
 	codec->control_data = dev_get_drvdata(codec->dev->parent);
 	control = codec->control_data;
@@ -7494,41 +4655,34 @@
 			tx_hpf_corner_freq_callback);
 	}
 
-	/* Make sure mbhc micbias register addresses are zeroed out */
-	memset(&taiko->mbhc_bias_regs, 0,
-		sizeof(struct mbhc_micbias_regs));
-	taiko->mbhc_micbias_switched = false;
-
-	/* Make sure mbhc intenal calibration data is zeroed out */
-	memset(&taiko->mbhc_data, 0,
-		sizeof(struct mbhc_internal_cal_data));
-	taiko->mbhc_data.t_sta_dce = DEFAULT_DCE_STA_WAIT;
-	taiko->mbhc_data.t_dce = DEFAULT_DCE_WAIT;
-	taiko->mbhc_data.t_sta = DEFAULT_STA_WAIT;
 	snd_soc_codec_set_drvdata(codec, taiko);
 
-	taiko->mclk_enabled = false;
-	taiko->bandgap_type = TAIKO_BANDGAP_OFF;
-	taiko->clock_active = false;
-	taiko->config_mode_active = false;
-	taiko->mbhc_polling_active = false;
-	taiko->mbhc_fake_ins_start = 0;
-	taiko->no_mic_headset_override = false;
-	taiko->hs_polling_irq_prepared = false;
-	mutex_init(&taiko->codec_resource_lock);
+	/* codec resmgr module init */
+	wcd9xxx = codec->control_data;
+	pdata = dev_get_platdata(codec->dev->parent);
+	ret = wcd9xxx_resmgr_init(&taiko->resmgr, codec, wcd9xxx, pdata,
+				  &taiko_reg_address);
+	if (ret) {
+		pr_err("%s: wcd9xxx init failed %d\n", __func__, ret);
+		return ret;
+	}
+
+	/* init and start mbhc */
+	ret = wcd9xxx_mbhc_init(&taiko->mbhc, &taiko->resmgr, codec);
+	if (ret) {
+		pr_err("%s: mbhc init failed %d\n", __func__, ret);
+		return ret;
+	}
+
 	taiko->codec = codec;
-	taiko->mbhc_state = MBHC_STATE_NONE;
-	taiko->mbhc_last_resume = 0;
 	for (i = 0; i < COMPANDER_MAX; i++) {
 		taiko->comp_enabled[i] = 0;
 		taiko->comp_fs[i] = COMPANDER_FS_48KHZ;
 	}
-	taiko->pdata = dev_get_platdata(codec->dev->parent);
 	taiko->intf_type = wcd9xxx_get_intf_type();
 	taiko->aux_pga_cnt = 0;
 	taiko->aux_l_gain = 0x1F;
 	taiko->aux_r_gain = 0x1F;
-	taiko_update_reg_address(taiko);
 	taiko_update_reg_defaults(codec);
 	taiko_codec_init_reg(codec);
 	ret = taiko_handle_pdata(taiko);
@@ -7537,84 +4691,57 @@
 		goto err_pdata;
 	}
 
+	ptr = kmalloc((sizeof(taiko_rx_chs) +
+		       sizeof(taiko_tx_chs)), GFP_KERNEL);
+	if (!ptr) {
+		pr_err("%s: no mem for slim chan ctl data\n", __func__);
+		ret = -ENOMEM;
+		goto err_nomem_slimch;
+	}
+
 	if (taiko->intf_type == WCD9XXX_INTERFACE_TYPE_I2C) {
 		snd_soc_dapm_new_controls(dapm, taiko_dapm_i2s_widgets,
 			ARRAY_SIZE(taiko_dapm_i2s_widgets));
 		snd_soc_dapm_add_routes(dapm, audio_i2s_map,
 			ARRAY_SIZE(audio_i2s_map));
+		for (i = 0; i < ARRAY_SIZE(taiko_i2s_dai); i++)
+			INIT_LIST_HEAD(&taiko->dai[i].wcd9xxx_ch_list);
+	} else if (taiko->intf_type == WCD9XXX_INTERFACE_TYPE_SLIMBUS) {
+		for (i = 0; i < NUM_CODEC_DAIS; i++) {
+			INIT_LIST_HEAD(&taiko->dai[i].wcd9xxx_ch_list);
+			init_waitqueue_head(&taiko->dai[i].dai_wait);
+		}
 	}
 
+	control->num_rx_port = TAIKO_RX_MAX;
+	control->rx_chs = ptr;
+	memcpy(control->rx_chs, taiko_rx_chs, sizeof(taiko_rx_chs));
+	control->num_tx_port = TAIKO_TX_MAX;
+	control->tx_chs = ptr + sizeof(taiko_rx_chs);
+	memcpy(control->tx_chs, taiko_tx_chs, sizeof(taiko_tx_chs));
+
 	snd_soc_dapm_sync(dapm);
 
 	(void) taiko_setup_irqs(taiko);
 
-	for (i = 0; i < ARRAY_SIZE(taiko_dai); i++) {
-		switch (taiko_dai[i].id) {
-		case AIF1_PB:
-			ch_cnt = taiko_dai[i].playback.channels_max;
-			break;
-		case AIF1_CAP:
-			ch_cnt = taiko_dai[i].capture.channels_max;
-			break;
-		case AIF2_PB:
-			ch_cnt = taiko_dai[i].playback.channels_max;
-			break;
-		case AIF2_CAP:
-			ch_cnt = taiko_dai[i].capture.channels_max;
-			break;
-		case AIF3_PB:
-			ch_cnt = taiko_dai[i].playback.channels_max;
-			break;
-		case AIF3_CAP:
-			ch_cnt = taiko_dai[i].capture.channels_max;
-			break;
-		default:
-			continue;
-		}
-		taiko->dai[i].ch_num = kzalloc((sizeof(unsigned int)*
-					ch_cnt), GFP_KERNEL);
-	}
-
-#ifdef CONFIG_DEBUG_FS
-	if (ret == 0) {
-		taiko->debugfs_poke =
-		    debugfs_create_file("TRRS", S_IFREG | S_IRUGO, NULL, taiko,
-					&codec_debug_ops);
-		taiko->debugfs_mbhc =
-		    debugfs_create_file("taiko_mbhc", S_IFREG | S_IRUGO,
-					NULL, taiko, &codec_mbhc_debug_ops);
-	}
-#endif
 	codec->ignore_pmdown_time = 1;
 	return ret;
 
 err_pdata:
-	mutex_destroy(&taiko->codec_resource_lock);
+	kfree(ptr);
+err_nomem_slimch:
 	kfree(taiko);
 	return ret;
 }
 static int taiko_codec_remove(struct snd_soc_codec *codec)
 {
-	int i;
 	struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
-	wcd9xxx_free_irq(codec->control_data, TAIKO_IRQ_SLIMBUS, taiko);
-	wcd9xxx_free_irq(codec->control_data, TAIKO_IRQ_MBHC_RELEASE, taiko);
-	wcd9xxx_free_irq(codec->control_data, TAIKO_IRQ_MBHC_POTENTIAL, taiko);
-	wcd9xxx_free_irq(codec->control_data, TAIKO_IRQ_MBHC_REMOVAL, taiko);
-	wcd9xxx_free_irq(codec->control_data, TAIKO_IRQ_MBHC_INSERTION, taiko);
-	TAIKO_ACQUIRE_LOCK(taiko->codec_resource_lock);
-	taiko_codec_disable_clock_block(codec);
-	TAIKO_RELEASE_LOCK(taiko->codec_resource_lock);
-	taiko_codec_enable_bandgap(codec, TAIKO_BANDGAP_OFF);
-	if (taiko->mbhc_fw)
-		release_firmware(taiko->mbhc_fw);
-	for (i = 0; i < ARRAY_SIZE(taiko_dai); i++)
-		kfree(taiko->dai[i].ch_num);
-	mutex_destroy(&taiko->codec_resource_lock);
-#ifdef CONFIG_DEBUG_FS
-	debugfs_remove(taiko->debugfs_poke);
-	debugfs_remove(taiko->debugfs_mbhc);
-#endif
+
+	/* cleanup MBHC */
+	wcd9xxx_mbhc_deinit(&taiko->mbhc);
+	/* cleanup resmgr */
+	wcd9xxx_resmgr_deinit(&taiko->resmgr);
+
 	kfree(taiko);
 	return 0;
 }
@@ -7652,7 +4779,8 @@
 	struct platform_device *pdev = to_platform_device(dev);
 	struct taiko_priv *taiko = platform_get_drvdata(pdev);
 	dev_dbg(dev, "%s: system resume\n", __func__);
-	taiko->mbhc_last_resume = jiffies;
+	/* Notify */
+	wcd9xxx_resmgr_notifier_call(&taiko->resmgr, WCD9XXX_EVENT_POST_RESUME);
 	return 0;
 }
 
diff --git a/sound/soc/codecs/wcd9320.h b/sound/soc/codecs/wcd9320.h
index 7ca8ff0..7bc5a57 100644
--- a/sound/soc/codecs/wcd9320.h
+++ b/sound/soc/codecs/wcd9320.h
@@ -15,6 +15,8 @@
 #include <sound/soc.h>
 #include <sound/jack.h>
 #include <linux/mfd/wcd9xxx/wcd9xxx-slimslave.h>
+#include "wcd9xxx-mbhc.h"
+#include "wcd9xxx-resmgr.h"
 
 #define TAIKO_NUM_REGISTERS 0x400
 #define TAIKO_MAX_REGISTER (TAIKO_NUM_REGISTERS-1)
@@ -22,27 +24,13 @@
 
 #define TAIKO_REG_VAL(reg, val)		{reg, 0, val}
 
-#define DEFAULT_DCE_STA_WAIT 55
-#define DEFAULT_DCE_WAIT 60000
-#define DEFAULT_STA_WAIT 5000
-#define VDDIO_MICBIAS_MV 1800
-
-#define STA 0
-#define DCE 1
-
-#define TAIKO_JACK_BUTTON_MASK (SND_JACK_BTN_0 | SND_JACK_BTN_1 | \
-				SND_JACK_BTN_2 | SND_JACK_BTN_3 | \
-				SND_JACK_BTN_4 | SND_JACK_BTN_5 | \
-				SND_JACK_BTN_6 | SND_JACK_BTN_7)
-
 extern const u8 taiko_reg_readable[TAIKO_CACHE_SIZE];
 extern const u8 taiko_reset_reg_defaults[TAIKO_CACHE_SIZE];
-
-enum taiko_micbias_num {
-	TAIKO_MICBIAS1 = 0,
-	TAIKO_MICBIAS2,
-	TAIKO_MICBIAS3,
-	TAIKO_MICBIAS4,
+struct taiko_codec_dai_data {
+	u32 rate;
+	u32 *ch_num;
+	u32 ch_act;
+	u32 ch_tot;
 };
 
 enum taiko_pid_current {
@@ -58,130 +46,50 @@
 	u8	val;
 };
 
-enum taiko_mbhc_clk_freq {
-	TAIKO_MCLK_12P2MHZ = 0,
-	TAIKO_MCLK_9P6MHZ,
-	TAIKO_NUM_CLK_FREQS,
-};
-
 enum taiko_mbhc_analog_pwr_cfg {
 	TAIKO_ANALOG_PWR_COLLAPSED = 0,
 	TAIKO_ANALOG_PWR_ON,
 	TAIKO_NUM_ANALOG_PWR_CONFIGS,
 };
 
-enum taiko_mbhc_btn_det_mem {
-	TAIKO_BTN_DET_V_BTN_LOW,
-	TAIKO_BTN_DET_V_BTN_HIGH,
-	TAIKO_BTN_DET_N_READY,
-	TAIKO_BTN_DET_N_CIC,
-	TAIKO_BTN_DET_GAIN
+/* Number of input and output Slimbus port */
+enum {
+	TAIKO_RX1 = 0,
+	TAIKO_RX2,
+	TAIKO_RX3,
+	TAIKO_RX4,
+	TAIKO_RX5,
+	TAIKO_RX6,
+	TAIKO_RX7,
+	TAIKO_RX8,
+	TAIKO_RX9,
+	TAIKO_RX10,
+	TAIKO_RX11,
+	TAIKO_RX12,
+	TAIKO_RX13,
+	TAIKO_RX_MAX,
 };
 
-struct taiko_mbhc_general_cfg {
-	u8 t_ldoh;
-	u8 t_bg_fast_settle;
-	u8 t_shutdown_plug_rem;
-	u8 mbhc_nsa;
-	u8 mbhc_navg;
-	u8 v_micbias_l;
-	u8 v_micbias;
-	u8 mbhc_reserved;
-	u16 settle_wait;
-	u16 t_micbias_rampup;
-	u16 t_micbias_rampdown;
-	u16 t_supply_bringup;
-} __packed;
-
-struct taiko_mbhc_plug_detect_cfg {
-	u32 mic_current;
-	u32 hph_current;
-	u16 t_mic_pid;
-	u16 t_ins_complete;
-	u16 t_ins_retry;
-	u16 v_removal_delta;
-	u8 micbias_slow_ramp;
-	u8 reserved0;
-	u8 reserved1;
-	u8 reserved2;
-} __packed;
-
-struct taiko_mbhc_plug_type_cfg {
-	u8 av_detect;
-	u8 mono_detect;
-	u8 num_ins_tries;
-	u8 reserved0;
-	s16 v_no_mic;
-	s16 v_av_min;
-	s16 v_av_max;
-	s16 v_hs_min;
-	s16 v_hs_max;
-	u16 reserved1;
-} __packed;
-
-
-struct taiko_mbhc_btn_detect_cfg {
-	s8 c[8];
-	u8 nc;
-	u8 n_meas;
-	u8 mbhc_nsc;
-	u8 n_btn_meas;
-	u8 n_btn_con;
-	u8 num_btn;
-	u8 reserved0;
-	u8 reserved1;
-	u16 t_poll;
-	u16 t_bounce_wait;
-	u16 t_rel_timeout;
-	s16 v_btn_press_delta_sta;
-	s16 v_btn_press_delta_cic;
-	u16 t_btn0_timeout;
-	s16 _v_btn_low[0]; /* v_btn_low[num_btn] */
-	s16 _v_btn_high[0]; /* v_btn_high[num_btn] */
-	u8 _n_ready[TAIKO_NUM_CLK_FREQS];
-	u8 _n_cic[TAIKO_NUM_CLK_FREQS];
-	u8 _gain[TAIKO_NUM_CLK_FREQS];
-} __packed;
-
-struct taiko_mbhc_imped_detect_cfg {
-	u8 _hs_imped_detect;
-	u8 _n_rload;
-	u8 _hph_keep_on;
-	u8 _repeat_rload_calc;
-	u16 _t_dac_ramp_time;
-	u16 _rhph_high;
-	u16 _rhph_low;
-	u16 _rload[0]; /* rload[n_rload] */
-	u16 _alpha[0]; /* alpha[n_rload] */
-	u16 _beta[3];
-} __packed;
-
-struct taiko_mbhc_config {
-	struct snd_soc_jack *headset_jack;
-	struct snd_soc_jack *button_jack;
-	bool read_fw_bin;
-	/* void* calibration contains:
-	 *  struct taiko_mbhc_general_cfg generic;
-	 *  struct taiko_mbhc_plug_detect_cfg plug_det;
-	 *  struct taiko_mbhc_plug_type_cfg plug_type;
-	 *  struct taiko_mbhc_btn_detect_cfg btn_det;
-	 *  struct taiko_mbhc_imped_detect_cfg imped_det;
-	 * Note: various size depends on btn_det->num_btn
-	 */
-	void *calibration;
-	enum taiko_micbias_num micbias;
-	int (*mclk_cb_fn) (struct snd_soc_codec*, int, bool);
-	unsigned int mclk_rate;
-	unsigned int gpio;
-	unsigned int gpio_irq;
-	int gpio_level_insert;
-	/* swap_gnd_mic returns true if extern GND/MIC swap switch toggled */
-	bool (*swap_gnd_mic) (struct snd_soc_codec *);
+enum {
+	TAIKO_TX1 = 0,
+	TAIKO_TX2,
+	TAIKO_TX3,
+	TAIKO_TX4,
+	TAIKO_TX5,
+	TAIKO_TX6,
+	TAIKO_TX7,
+	TAIKO_TX8,
+	TAIKO_TX9,
+	TAIKO_TX10,
+	TAIKO_TX11,
+	TAIKO_TX12,
+	TAIKO_TX13,
+	TAIKO_TX14,
+	TAIKO_TX15,
+	TAIKO_TX16,
+	TAIKO_TX_MAX,
 };
 
-extern int taiko_hs_detect(struct snd_soc_codec *codec,
-			   const struct taiko_mbhc_config *cfg);
-
 struct anc_header {
 	u32 reserved[3];
 	u32 num_anc_slots;
@@ -189,64 +97,7 @@
 
 extern int taiko_mclk_enable(struct snd_soc_codec *codec, int mclk_enable,
 			     bool dapm);
-
-extern void *taiko_mbhc_cal_btn_det_mp(const struct taiko_mbhc_btn_detect_cfg
-				       *btn_det,
-				       const enum taiko_mbhc_btn_det_mem mem);
-
-#define TAIKO_MBHC_CAL_SIZE(buttons, rload) ( \
-	sizeof(enum taiko_micbias_num) + \
-	sizeof(struct taiko_mbhc_general_cfg) + \
-	sizeof(struct taiko_mbhc_plug_detect_cfg) + \
-	    ((sizeof(s16) + sizeof(s16)) * buttons) + \
-	sizeof(struct taiko_mbhc_plug_type_cfg) + \
-	sizeof(struct taiko_mbhc_btn_detect_cfg) + \
-	sizeof(struct taiko_mbhc_imped_detect_cfg) + \
-	    ((sizeof(u16) + sizeof(u16)) * rload) \
-	)
-
-#define TAIKO_MBHC_CAL_GENERAL_PTR(cali) ( \
-	    (struct taiko_mbhc_general_cfg *) cali)
-#define TAIKO_MBHC_CAL_PLUG_DET_PTR(cali) ( \
-	    (struct taiko_mbhc_plug_detect_cfg *) \
-	    &(TAIKO_MBHC_CAL_GENERAL_PTR(cali)[1]))
-#define TAIKO_MBHC_CAL_PLUG_TYPE_PTR(cali) ( \
-	    (struct taiko_mbhc_plug_type_cfg *) \
-	    &(TAIKO_MBHC_CAL_PLUG_DET_PTR(cali)[1]))
-#define TAIKO_MBHC_CAL_BTN_DET_PTR(cali) ( \
-	    (struct taiko_mbhc_btn_detect_cfg *) \
-	    &(TAIKO_MBHC_CAL_PLUG_TYPE_PTR(cali)[1]))
-#define TAIKO_MBHC_CAL_IMPED_DET_PTR(cali) ( \
-	    (struct taiko_mbhc_imped_detect_cfg *) \
-	    (((void *)&TAIKO_MBHC_CAL_BTN_DET_PTR(cali)[1]) + \
-	     (TAIKO_MBHC_CAL_BTN_DET_PTR(cali)->num_btn * \
-	      (sizeof(TAIKO_MBHC_CAL_BTN_DET_PTR(cali)->_v_btn_low[0]) + \
-	       sizeof(TAIKO_MBHC_CAL_BTN_DET_PTR(cali)->_v_btn_high[0])))) \
-	)
-
-/* minimum size of calibration data assuming there is only one button and
- * one rload.
- */
-#define TAIKO_MBHC_CAL_MIN_SIZE ( \
-	sizeof(struct taiko_mbhc_general_cfg) + \
-	sizeof(struct taiko_mbhc_plug_detect_cfg) + \
-	sizeof(struct taiko_mbhc_plug_type_cfg) + \
-	sizeof(struct taiko_mbhc_btn_detect_cfg) + \
-	sizeof(struct taiko_mbhc_imped_detect_cfg) + \
-	(sizeof(u16) * 2))
-
-#define TAIKO_MBHC_CAL_BTN_SZ(cfg_ptr) ( \
-	    sizeof(struct taiko_mbhc_btn_detect_cfg) + \
-	    (cfg_ptr->num_btn * (sizeof(cfg_ptr->_v_btn_low[0]) + \
-				 sizeof(cfg_ptr->_v_btn_high[0]))))
-
-#define TAIKO_MBHC_CAL_IMPED_MIN_SZ ( \
-	    sizeof(struct taiko_mbhc_imped_detect_cfg) + \
-	    sizeof(u16) * 2)
-
-#define TAIKO_MBHC_CAL_IMPED_SZ(cfg_ptr) ( \
-	    sizeof(struct taiko_mbhc_imped_detect_cfg) + \
-	    (cfg_ptr->_n_rload * (sizeof(cfg_ptr->_rload[0]) + \
-				 sizeof(cfg_ptr->_alpha[0]))))
+extern int taiko_hs_detect(struct snd_soc_codec *codec,
+			   struct wcd9xxx_mbhc_config *mbhc_cfg);
 
 #endif
diff --git a/sound/soc/codecs/wcd9xxx-mbhc.c b/sound/soc/codecs/wcd9xxx-mbhc.c
new file mode 100644
index 0000000..653effa
--- /dev/null
+++ b/sound/soc/codecs/wcd9xxx-mbhc.c
@@ -0,0 +1,3309 @@
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/firmware.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/device.h>
+#include <linux/printk.h>
+#include <linux/ratelimit.h>
+#include <linux/debugfs.h>
+#include <linux/mfd/wcd9xxx/core.h>
+#include <linux/mfd/wcd9xxx/wcd9xxx_registers.h>
+#include <linux/mfd/wcd9xxx/wcd9320_registers.h>
+#include <linux/mfd/wcd9xxx/pdata.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include <linux/pm_runtime.h>
+#include <linux/kernel.h>
+#include <linux/gpio.h>
+#include "wcd9320.h"
+#include "wcd9xxx-mbhc.h"
+#include "wcd9xxx-resmgr.h"
+
+#define WCD9XXX_JACK_MASK (SND_JACK_HEADSET | SND_JACK_OC_HPHL | \
+			   SND_JACK_OC_HPHR | SND_JACK_LINEOUT | \
+			   SND_JACK_UNSUPPORTED)
+#define WCD9XXX_JACK_BUTTON_MASK (SND_JACK_BTN_0 | SND_JACK_BTN_1 | \
+				  SND_JACK_BTN_2 | SND_JACK_BTN_3 | \
+				  SND_JACK_BTN_4 | SND_JACK_BTN_5 | \
+				  SND_JACK_BTN_6 | SND_JACK_BTN_7)
+
+#define NUM_DCE_PLUG_DETECT 3
+#define NUM_ATTEMPTS_INSERT_DETECT 25
+#define NUM_ATTEMPTS_TO_REPORT 5
+
+#define FAKE_INS_LOW 10
+#define FAKE_INS_HIGH 80
+#define FAKE_INS_HIGH_NO_SWCH 150
+#define FAKE_REMOVAL_MIN_PERIOD_MS 50
+#define FAKE_INS_DELTA_SCALED_MV 300
+
+#define BUTTON_MIN 0x8000
+#define STATUS_REL_DETECTION 0x0C
+
+#define HS_DETECT_PLUG_TIME_MS (5 * 1000)
+#define HS_DETECT_PLUG_INERVAL_MS 100
+#define SWCH_REL_DEBOUNCE_TIME_MS 50
+#define SWCH_IRQ_DEBOUNCE_TIME_US 5000
+
+#define GND_MIC_SWAP_THRESHOLD 2
+#define OCP_ATTEMPT 1
+
+#define FW_READ_ATTEMPTS 15
+#define FW_READ_TIMEOUT 2000000
+
+#define BUTTON_POLLING_SUPPORTED true
+
+#define MCLK_RATE_12288KHZ 12288000
+#define MCLK_RATE_9600KHZ 9600000
+#define WCD9XXX_RCO_CLK_RATE MCLK_RATE_12288KHZ
+
+#define DEFAULT_DCE_STA_WAIT 55
+#define DEFAULT_DCE_WAIT 60000
+#define DEFAULT_STA_WAIT 5000
+
+#define VDDIO_MICBIAS_MV 1800
+
+enum meas_type {
+	STA = 0,
+	DCE,
+};
+
+enum {
+	MBHC_USE_HPHL_TRIGGER = 1,
+	MBHC_USE_MB_TRIGGER = 2
+};
+
+/*
+ * Flags to track of PA and DAC state.
+ * PA and DAC should be tracked separately as AUXPGA loopback requires
+ * only PA to be turned on without DAC being on.
+ */
+enum pa_dac_ack_flags {
+	WCD9XXX_HPHL_PA_OFF_ACK = 0,
+	WCD9XXX_HPHR_PA_OFF_ACK,
+	WCD9XXX_HPHL_DAC_OFF_ACK,
+	WCD9XXX_HPHR_DAC_OFF_ACK
+};
+
+static bool wcd9xxx_mbhc_polling(struct wcd9xxx_mbhc *mbhc)
+{
+	return mbhc->polling_active;
+}
+
+static void wcd9xxx_turn_onoff_override(struct snd_soc_codec *codec, bool on)
+{
+	snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x04, on << 2);
+}
+
+/* called under codec_resource_lock acquisition */
+static void wcd9xxx_pause_hs_polling(struct wcd9xxx_mbhc *mbhc)
+{
+	struct snd_soc_codec *codec = mbhc->codec;
+
+	pr_debug("%s: enter\n", __func__);
+	if (!mbhc->polling_active) {
+		pr_debug("polling not active, nothing to pause\n");
+		return;
+	}
+
+	/* Soft reset MBHC block */
+	snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8, 0x8);
+	pr_debug("%s: leave\n", __func__);
+}
+
+/* called under codec_resource_lock acquisition */
+static void wcd9xxx_start_hs_polling(struct wcd9xxx_mbhc *mbhc)
+{
+	struct snd_soc_codec *codec = mbhc->codec;
+	int mbhc_state = mbhc->mbhc_state;
+
+	pr_debug("%s: enter\n", __func__);
+	if (!mbhc->polling_active) {
+		pr_debug("Polling is not active, do not start polling\n");
+		return;
+	}
+	snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x84);
+
+	if (!mbhc->no_mic_headset_override &&
+	    mbhc_state == MBHC_STATE_POTENTIAL) {
+		pr_debug("%s recovering MBHC state macine\n", __func__);
+		mbhc->mbhc_state = MBHC_STATE_POTENTIAL_RECOVERY;
+		/* set to max button press threshold */
+		snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B2_CTL, 0x7F);
+		snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B1_CTL, 0xFF);
+		snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B4_CTL, 0x7F);
+		snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B3_CTL, 0xFF);
+		/* set to max */
+		snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B6_CTL, 0x7F);
+		snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B5_CTL, 0xFF);
+	}
+
+	snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x1);
+	snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8, 0x0);
+	snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x1);
+	pr_debug("%s: leave\n", __func__);
+}
+
+/* called under codec_resource_lock acquisition */
+static void __wcd9xxx_switch_micbias(struct wcd9xxx_mbhc *mbhc,
+				     int vddio_switch, bool restartpolling,
+				     bool checkpolling)
+{
+	int cfilt_k_val;
+	bool override;
+	struct snd_soc_codec *codec;
+
+	codec = mbhc->codec;
+
+	if (vddio_switch && !mbhc->mbhc_micbias_switched &&
+	    (!checkpolling || mbhc->polling_active)) {
+		if (restartpolling)
+			wcd9xxx_pause_hs_polling(mbhc);
+		override = snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B1_CTL) &
+			   0x04;
+		if (!override)
+			wcd9xxx_turn_onoff_override(codec, true);
+		/* Adjust threshold if Mic Bias voltage changes */
+		if (mbhc->mbhc_data.micb_mv != VDDIO_MICBIAS_MV) {
+			cfilt_k_val = wcd9xxx_resmgr_get_k_val(mbhc->resmgr,
+							      VDDIO_MICBIAS_MV);
+			usleep_range(10000, 10000);
+			snd_soc_update_bits(codec,
+					mbhc->mbhc_bias_regs.cfilt_val,
+					0xFC, (cfilt_k_val << 2));
+			usleep_range(10000, 10000);
+			snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B1_CTL,
+				      mbhc->mbhc_data.adj_v_ins_hu & 0xFF);
+			snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B2_CTL,
+				      (mbhc->mbhc_data.adj_v_ins_hu >> 8) &
+				      0xFF);
+			pr_debug("%s: Programmed MBHC thresholds to VDDIO\n",
+				 __func__);
+		}
+
+		/* Enable MIC BIAS Switch to VDDIO */
+		snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg,
+				    0x80, 0x80);
+		snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg,
+				    0x10, 0x00);
+		if (!override)
+			wcd9xxx_turn_onoff_override(codec, false);
+		if (restartpolling)
+			wcd9xxx_start_hs_polling(mbhc);
+
+		mbhc->mbhc_micbias_switched = true;
+		pr_debug("%s: VDDIO switch enabled\n", __func__);
+	} else if (!vddio_switch && mbhc->mbhc_micbias_switched) {
+		if ((!checkpolling || mbhc->polling_active) &&
+		    restartpolling)
+			wcd9xxx_pause_hs_polling(mbhc);
+		/* Reprogram thresholds */
+		if (mbhc->mbhc_data.micb_mv != VDDIO_MICBIAS_MV) {
+			cfilt_k_val =
+			    wcd9xxx_resmgr_get_k_val(mbhc->resmgr,
+						     mbhc->mbhc_data.micb_mv);
+			snd_soc_update_bits(codec,
+					mbhc->mbhc_bias_regs.cfilt_val,
+					0xFC, (cfilt_k_val << 2));
+			usleep_range(10000, 10000);
+			snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B1_CTL,
+					mbhc->mbhc_data.v_ins_hu & 0xFF);
+			snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B2_CTL,
+					(mbhc->mbhc_data.v_ins_hu >> 8) & 0xFF);
+			pr_debug("%s: Programmed MBHC thresholds to MICBIAS\n",
+					__func__);
+		}
+
+		/* Disable MIC BIAS Switch to VDDIO */
+		snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg, 0x80,
+				    0x00);
+		snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg, 0x10,
+				    0x00);
+
+		if ((!checkpolling || mbhc->polling_active) && restartpolling)
+			wcd9xxx_start_hs_polling(mbhc);
+
+		mbhc->mbhc_micbias_switched = false;
+		pr_debug("%s: VDDIO switch disabled\n", __func__);
+	}
+}
+
+static void wcd9xxx_switch_micbias(struct wcd9xxx_mbhc *mbhc, int vddio_switch)
+{
+	return __wcd9xxx_switch_micbias(mbhc, vddio_switch, true, true);
+}
+
+static s16 wcd9xxx_get_current_v_ins(struct wcd9xxx_mbhc *mbhc, bool hu)
+{
+	s16 v_ins;
+	if ((mbhc->mbhc_data.micb_mv != VDDIO_MICBIAS_MV) &&
+	    mbhc->mbhc_micbias_switched)
+		v_ins = hu ? (s16)mbhc->mbhc_data.adj_v_ins_hu :
+			(s16)mbhc->mbhc_data.adj_v_ins_h;
+	else
+		v_ins = hu ? (s16)mbhc->mbhc_data.v_ins_hu :
+			(s16)mbhc->mbhc_data.v_ins_h;
+	return v_ins;
+}
+
+void *wcd9xxx_mbhc_cal_btn_det_mp(
+			    const struct wcd9xxx_mbhc_btn_detect_cfg *btn_det,
+			    const enum wcd9xxx_mbhc_btn_det_mem mem)
+{
+	void *ret = &btn_det->_v_btn_low;
+
+	switch (mem) {
+	case MBHC_BTN_DET_GAIN:
+		ret += sizeof(btn_det->_n_cic);
+	case MBHC_BTN_DET_N_CIC:
+		ret += sizeof(btn_det->_n_ready);
+	case MBHC_BTN_DET_N_READY:
+		ret += sizeof(btn_det->_v_btn_high[0]) * btn_det->num_btn;
+	case MBHC_BTN_DET_V_BTN_HIGH:
+		ret += sizeof(btn_det->_v_btn_low[0]) * btn_det->num_btn;
+	case MBHC_BTN_DET_V_BTN_LOW:
+		/* do nothing */
+		break;
+	default:
+		ret = NULL;
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(wcd9xxx_mbhc_cal_btn_det_mp);
+
+static void wcd9xxx_calibrate_hs_polling(struct wcd9xxx_mbhc *mbhc)
+{
+	struct snd_soc_codec *codec = mbhc->codec;
+	const s16 v_ins_hu = wcd9xxx_get_current_v_ins(mbhc, true);
+
+	snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B1_CTL, v_ins_hu & 0xFF);
+	snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B2_CTL,
+		      (v_ins_hu >> 8) & 0xFF);
+
+	snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B3_CTL,
+		      mbhc->mbhc_data.v_b1_hu & 0xFF);
+	snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B4_CTL,
+		      (mbhc->mbhc_data.v_b1_hu >> 8) & 0xFF);
+
+	snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B5_CTL,
+		      mbhc->mbhc_data.v_b1_h & 0xFF);
+	snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B6_CTL,
+		      (mbhc->mbhc_data.v_b1_h >> 8) & 0xFF);
+
+	snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B9_CTL,
+		      mbhc->mbhc_data.v_brh & 0xFF);
+	snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B10_CTL,
+		      (mbhc->mbhc_data.v_brh >> 8) & 0xFF);
+
+	snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B11_CTL,
+		      mbhc->mbhc_data.v_brl & 0xFF);
+	snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B12_CTL,
+		      (mbhc->mbhc_data.v_brl >> 8) & 0xFF);
+}
+
+static void wcd9xxx_codec_switch_cfilt_mode(struct wcd9xxx_mbhc *mbhc,
+					    bool fast)
+{
+	struct snd_soc_codec *codec = mbhc->codec;
+	u8 reg_mode_val, cur_mode_val;
+
+	if (fast)
+		reg_mode_val = WCD9XXX_CFILT_FAST_MODE;
+	else
+		reg_mode_val = WCD9XXX_CFILT_SLOW_MODE;
+
+	cur_mode_val =
+	    snd_soc_read(codec, mbhc->mbhc_bias_regs.cfilt_ctl) & 0x40;
+
+	if (cur_mode_val != reg_mode_val) {
+		if (mbhc->polling_active)
+			wcd9xxx_pause_hs_polling(mbhc);
+		snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.cfilt_ctl, 0x40,
+				    reg_mode_val);
+		if (mbhc->polling_active)
+			wcd9xxx_start_hs_polling(mbhc);
+		pr_debug("%s: CFILT mode change (%x to %x)\n", __func__,
+			cur_mode_val, reg_mode_val);
+	} else {
+		pr_debug("%s: CFILT Value is already %x\n",
+			__func__, cur_mode_val);
+	}
+}
+
+static void wcd9xxx_jack_report(struct snd_soc_jack *jack, int status, int mask)
+{
+	snd_soc_jack_report_no_dapm(jack, status, mask);
+}
+
+static void __hphocp_off_report(struct wcd9xxx_mbhc *mbhc, u32 jack_status,
+				int irq)
+{
+	struct snd_soc_codec *codec;
+
+	pr_debug("%s: clear ocp status %x\n", __func__, jack_status);
+	codec = mbhc->codec;
+	if (mbhc->hph_status & jack_status) {
+		mbhc->hph_status &= ~jack_status;
+		wcd9xxx_jack_report(&mbhc->headset_jack,
+				    mbhc->hph_status, WCD9XXX_JACK_MASK);
+		snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_OCP_CTL, 0x10,
+				    0x00);
+		snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_OCP_CTL, 0x10,
+				    0x10);
+		/*
+		 * reset retry counter as PA is turned off signifying
+		 * start of new OCP detection session
+		 */
+		if (WCD9XXX_IRQ_HPH_PA_OCPL_FAULT)
+			mbhc->hphlocp_cnt = 0;
+		else
+			mbhc->hphrocp_cnt = 0;
+		wcd9xxx_enable_irq(codec->control_data, irq);
+	}
+}
+
+static void hphrocp_off_report(struct wcd9xxx_mbhc *mbhc, u32 jack_status)
+{
+	__hphocp_off_report(mbhc, SND_JACK_OC_HPHR,
+			    WCD9XXX_IRQ_HPH_PA_OCPR_FAULT);
+}
+
+static void hphlocp_off_report(struct wcd9xxx_mbhc *mbhc, u32 jack_status)
+{
+	__hphocp_off_report(mbhc, SND_JACK_OC_HPHL,
+			    WCD9XXX_IRQ_HPH_PA_OCPL_FAULT);
+}
+
+static void wcd9xxx_get_mbhc_micbias_regs(struct wcd9xxx_mbhc *mbhc,
+					struct mbhc_micbias_regs *micbias_regs)
+{
+	unsigned int cfilt;
+	struct wcd9xxx_pdata *pdata = mbhc->resmgr->pdata;
+
+	switch (mbhc->mbhc_cfg->micbias) {
+	case MBHC_MICBIAS1:
+		cfilt = pdata->micbias.bias1_cfilt_sel;
+		micbias_regs->mbhc_reg = WCD9XXX_A_MICB_1_MBHC;
+		micbias_regs->int_rbias = WCD9XXX_A_MICB_1_INT_RBIAS;
+		micbias_regs->ctl_reg = WCD9XXX_A_MICB_1_CTL;
+		break;
+	case MBHC_MICBIAS2:
+		cfilt = pdata->micbias.bias2_cfilt_sel;
+		micbias_regs->mbhc_reg = WCD9XXX_A_MICB_2_MBHC;
+		micbias_regs->int_rbias = WCD9XXX_A_MICB_2_INT_RBIAS;
+		micbias_regs->ctl_reg = WCD9XXX_A_MICB_2_CTL;
+		break;
+	case MBHC_MICBIAS3:
+		cfilt = pdata->micbias.bias3_cfilt_sel;
+		micbias_regs->mbhc_reg = WCD9XXX_A_MICB_3_MBHC;
+		micbias_regs->int_rbias = WCD9XXX_A_MICB_3_INT_RBIAS;
+		micbias_regs->ctl_reg = WCD9XXX_A_MICB_3_CTL;
+		break;
+	case MBHC_MICBIAS4:
+		cfilt = pdata->micbias.bias4_cfilt_sel;
+		micbias_regs->mbhc_reg = mbhc->resmgr->reg_addr->micb_4_mbhc;
+		micbias_regs->int_rbias =
+		    mbhc->resmgr->reg_addr->micb_4_int_rbias;
+		micbias_regs->ctl_reg = mbhc->resmgr->reg_addr->micb_4_ctl;
+		break;
+	default:
+		/* Should never reach here */
+		pr_err("%s: Invalid MIC BIAS for MBHC\n", __func__);
+		return;
+	}
+
+	micbias_regs->cfilt_sel = cfilt;
+
+	switch (cfilt) {
+	case WCD9XXX_CFILT1_SEL:
+		micbias_regs->cfilt_val = WCD9XXX_A_MICB_CFILT_1_VAL;
+		micbias_regs->cfilt_ctl = WCD9XXX_A_MICB_CFILT_1_CTL;
+		mbhc->mbhc_data.micb_mv =
+		    mbhc->resmgr->pdata->micbias.cfilt1_mv;
+		break;
+	case WCD9XXX_CFILT2_SEL:
+		micbias_regs->cfilt_val = WCD9XXX_A_MICB_CFILT_2_VAL;
+		micbias_regs->cfilt_ctl = WCD9XXX_A_MICB_CFILT_2_CTL;
+		mbhc->mbhc_data.micb_mv =
+		    mbhc->resmgr->pdata->micbias.cfilt2_mv;
+		break;
+	case WCD9XXX_CFILT3_SEL:
+		micbias_regs->cfilt_val = WCD9XXX_A_MICB_CFILT_3_VAL;
+		micbias_regs->cfilt_ctl = WCD9XXX_A_MICB_CFILT_3_CTL;
+		mbhc->mbhc_data.micb_mv =
+		    mbhc->resmgr->pdata->micbias.cfilt3_mv;
+		break;
+	}
+}
+
+static void wcd9xxx_clr_and_turnon_hph_padac(struct wcd9xxx_mbhc *mbhc)
+{
+	bool pa_turned_on = false;
+	struct snd_soc_codec *codec = mbhc->codec;
+	u8 wg_time;
+
+	wg_time = snd_soc_read(codec, WCD9XXX_A_RX_HPH_CNP_WG_TIME) ;
+	wg_time += 1;
+
+	if (test_and_clear_bit(WCD9XXX_HPHR_DAC_OFF_ACK,
+			       &mbhc->hph_pa_dac_state)) {
+		pr_debug("%s: HPHR clear flag and enable DAC\n", __func__);
+		snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_R_DAC_CTL,
+				    0xC0, 0xC0);
+	}
+	if (test_and_clear_bit(WCD9XXX_HPHL_DAC_OFF_ACK,
+				&mbhc->hph_pa_dac_state)) {
+		pr_debug("%s: HPHL clear flag and enable DAC\n", __func__);
+		snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_L_DAC_CTL,
+				    0xC0, 0xC0);
+	}
+
+	if (test_and_clear_bit(WCD9XXX_HPHR_PA_OFF_ACK,
+			       &mbhc->hph_pa_dac_state)) {
+		pr_debug("%s: HPHR clear flag and enable PA\n", __func__);
+		snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_CNP_EN, 0x10,
+				    1 << 4);
+		pa_turned_on = true;
+	}
+	if (test_and_clear_bit(WCD9XXX_HPHL_PA_OFF_ACK,
+			       &mbhc->hph_pa_dac_state)) {
+		pr_debug("%s: HPHL clear flag and enable PA\n", __func__);
+		snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_CNP_EN, 0x20, 1
+				    << 5);
+		pa_turned_on = true;
+	}
+
+	if (pa_turned_on) {
+		pr_debug("%s: PA was turned off by MBHC and not by DAPM\n",
+			 __func__);
+		usleep_range(wg_time * 1000, wg_time * 1000);
+	}
+}
+
+static int wcd9xxx_cancel_btn_work(struct wcd9xxx_mbhc *mbhc)
+{
+	int r;
+	r = cancel_delayed_work_sync(&mbhc->mbhc_btn_dwork);
+	if (r)
+		/* if scheduled mbhc.mbhc_btn_dwork is canceled from here,
+		 * we have to unlock from here instead btn_work */
+		wcd9xxx_unlock_sleep(mbhc->resmgr->core);
+	return r;
+}
+
+static bool wcd9xxx_is_hph_dac_on(struct snd_soc_codec *codec, int left)
+{
+	u8 hph_reg_val = 0;
+	if (left)
+		hph_reg_val = snd_soc_read(codec, WCD9XXX_A_RX_HPH_L_DAC_CTL);
+	else
+		hph_reg_val = snd_soc_read(codec, WCD9XXX_A_RX_HPH_R_DAC_CTL);
+
+	return (hph_reg_val & 0xC0) ? true : false;
+}
+
+static bool wcd9xxx_is_hph_pa_on(struct snd_soc_codec *codec)
+{
+	u8 hph_reg_val = 0;
+	hph_reg_val = snd_soc_read(codec, WCD9XXX_A_RX_HPH_CNP_EN);
+
+	return (hph_reg_val & 0x30) ? true : false;
+}
+
+/* called under codec_resource_lock acquisition */
+static void wcd9xxx_set_and_turnoff_hph_padac(struct wcd9xxx_mbhc *mbhc)
+{
+	u8 wg_time;
+	struct snd_soc_codec *codec = mbhc->codec;
+
+	wg_time = snd_soc_read(codec, WCD9XXX_A_RX_HPH_CNP_WG_TIME);
+	wg_time += 1;
+
+	/* If headphone PA is on, check if userspace receives
+	 * removal event to sync-up PA's state */
+	if (wcd9xxx_is_hph_pa_on(codec)) {
+		pr_debug("%s PA is on, setting PA_OFF_ACK\n", __func__);
+		set_bit(WCD9XXX_HPHL_PA_OFF_ACK, &mbhc->hph_pa_dac_state);
+		set_bit(WCD9XXX_HPHR_PA_OFF_ACK, &mbhc->hph_pa_dac_state);
+	} else {
+		pr_debug("%s PA is off\n", __func__);
+	}
+
+	if (wcd9xxx_is_hph_dac_on(codec, 1))
+		set_bit(WCD9XXX_HPHL_DAC_OFF_ACK, &mbhc->hph_pa_dac_state);
+	if (wcd9xxx_is_hph_dac_on(codec, 0))
+		set_bit(WCD9XXX_HPHR_DAC_OFF_ACK, &mbhc->hph_pa_dac_state);
+
+	snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_CNP_EN, 0x30, 0x00);
+	snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_L_DAC_CTL, 0xC0, 0x00);
+	snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_R_DAC_CTL, 0xC0, 0x00);
+	usleep_range(wg_time * 1000, wg_time * 1000);
+}
+
+static void wcd9xxx_insert_detect_setup(struct wcd9xxx_mbhc *mbhc, bool ins)
+{
+	if (!mbhc->mbhc_cfg->insert_detect)
+		return;
+	pr_debug("%s: Setting up %s detection\n", __func__,
+		 ins ? "insert" : "removal");
+	/* Disable detection to avoid glitch */
+	snd_soc_update_bits(mbhc->codec, WCD9XXX_A_MBHC_INSERT_DETECT, 1, 0);
+	/* Override mbhc power switch to avoid false IRQs */
+	snd_soc_update_bits(mbhc->codec, WCD9XXX_A_MICB_1_MBHC, 1 << 2,
+			    !ins << 2);
+	snd_soc_update_bits(mbhc->codec, WCD9XXX_A_MICB_2_MBHC, 1 << 2,
+			    !ins << 2);
+	snd_soc_update_bits(mbhc->codec, WCD9XXX_A_MICB_3_MBHC, 1 << 2,
+			    !ins << 2);
+	snd_soc_update_bits(mbhc->codec, WCD9XXX_A_MICB_4_MBHC, 1 << 2,
+			    !ins << 2);
+	snd_soc_write(mbhc->codec, WCD9XXX_A_MBHC_INSERT_DETECT,
+		      (0x68 | (ins ? (1 << 1) : 0)));
+	/* Re-enable detection */
+	snd_soc_update_bits(mbhc->codec, WCD9XXX_A_MBHC_INSERT_DETECT, 1, 1);
+}
+
+/* called under codec_resource_lock acquisition */
+static void wcd9xxx_report_plug(struct wcd9xxx_mbhc *mbhc, int insertion,
+				enum snd_jack_types jack_type)
+{
+	WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
+
+	pr_debug("%s: enter insertion %d hph_status %x\n",
+		 __func__, insertion, mbhc->hph_status);
+	if (!insertion) {
+		/* Report removal */
+		mbhc->hph_status &= ~jack_type;
+		/*
+		 * cancel possibly scheduled btn work and
+		 * report release if we reported button press
+		 */
+		if (wcd9xxx_cancel_btn_work(mbhc))
+			pr_debug("%s: button press is canceled\n", __func__);
+		else if (mbhc->buttons_pressed) {
+			pr_debug("%s: release of button press%d\n",
+				 __func__, jack_type);
+			wcd9xxx_jack_report(&mbhc->button_jack, 0,
+					    mbhc->buttons_pressed);
+			mbhc->buttons_pressed &=
+				~WCD9XXX_JACK_BUTTON_MASK;
+		}
+		pr_debug("%s: Reporting removal %d(%x)\n", __func__,
+			 jack_type, mbhc->hph_status);
+		wcd9xxx_jack_report(&mbhc->headset_jack, mbhc->hph_status,
+				    WCD9XXX_JACK_MASK);
+		wcd9xxx_set_and_turnoff_hph_padac(mbhc);
+		hphrocp_off_report(mbhc, SND_JACK_OC_HPHR);
+		hphlocp_off_report(mbhc, SND_JACK_OC_HPHL);
+		mbhc->current_plug = PLUG_TYPE_NONE;
+		mbhc->polling_active = false;
+	} else {
+		if (mbhc->mbhc_cfg->detect_extn_cable) {
+			/* Report removal of current jack type */
+			if (mbhc->hph_status != jack_type) {
+				pr_debug("%s: Reporting removal (%x)\n",
+					 __func__, mbhc->hph_status);
+				wcd9xxx_jack_report(&mbhc->headset_jack,
+						    0, WCD9XXX_JACK_MASK);
+				mbhc->hph_status = 0;
+			}
+		}
+		/* Report insertion */
+		mbhc->hph_status |= jack_type;
+
+		if (jack_type == SND_JACK_HEADPHONE) {
+			mbhc->current_plug = PLUG_TYPE_HEADPHONE;
+		} else if (jack_type == SND_JACK_UNSUPPORTED) {
+			mbhc->current_plug = PLUG_TYPE_GND_MIC_SWAP;
+		} else if (jack_type == SND_JACK_HEADSET) {
+			mbhc->polling_active = BUTTON_POLLING_SUPPORTED;
+			mbhc->current_plug = PLUG_TYPE_HEADSET;
+		} else if (jack_type == SND_JACK_LINEOUT) {
+			mbhc->current_plug = PLUG_TYPE_HIGH_HPH;
+		}
+		pr_debug("%s: Reporting insertion %d(%x)\n", __func__,
+			 jack_type, mbhc->hph_status);
+		wcd9xxx_jack_report(&mbhc->headset_jack,
+				    mbhc->hph_status, WCD9XXX_JACK_MASK);
+		wcd9xxx_clr_and_turnon_hph_padac(mbhc);
+	}
+	/* Setup insert detect */
+	wcd9xxx_insert_detect_setup(mbhc, !insertion);
+
+	pr_debug("%s: leave hph_status %x\n", __func__, mbhc->hph_status);
+}
+
+/* should be called under interrupt context that hold suspend */
+static void wcd9xxx_schedule_hs_detect_plug(struct wcd9xxx_mbhc *mbhc,
+					    struct work_struct *work)
+{
+	pr_debug("%s: scheduling wcd9xxx_correct_swch_plug\n", __func__);
+	WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
+	mbhc->hs_detect_work_stop = false;
+	wcd9xxx_lock_sleep(mbhc->resmgr->core);
+	schedule_work(work);
+}
+
+/* called under codec_resource_lock acquisition */
+static void wcd9xxx_cancel_hs_detect_plug(struct wcd9xxx_mbhc *mbhc,
+					 struct work_struct *work)
+{
+	pr_debug("%s: Canceling correct_plug_swch\n", __func__);
+	WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
+	mbhc->hs_detect_work_stop = true;
+	wmb();
+	WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
+	if (cancel_work_sync(work)) {
+		pr_debug("%s: correct_plug_swch is canceled\n",
+			 __func__);
+		wcd9xxx_unlock_sleep(mbhc->resmgr->core);
+	}
+	WCD9XXX_BCL_LOCK(mbhc->resmgr);
+}
+
+static s16 wcd9xxx_get_current_v_hs_max(struct wcd9xxx_mbhc *mbhc)
+{
+	s16 v_hs_max;
+	struct wcd9xxx_mbhc_plug_type_cfg *plug_type;
+
+	plug_type = WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(mbhc->mbhc_cfg->calibration);
+	if ((mbhc->mbhc_data.micb_mv != VDDIO_MICBIAS_MV) &&
+	    mbhc->mbhc_micbias_switched)
+		v_hs_max = mbhc->mbhc_data.adj_v_hs_max;
+	else
+		v_hs_max = plug_type->v_hs_max;
+	return v_hs_max;
+}
+
+static bool wcd9xxx_is_inval_ins_range(struct wcd9xxx_mbhc *mbhc,
+				     s32 mic_volt, bool highhph, bool *highv)
+{
+	s16 v_hs_max;
+	bool invalid = false;
+
+	/* Perform this check only when the high voltage headphone
+	 * needs to be considered as invalid
+	 */
+	v_hs_max = wcd9xxx_get_current_v_hs_max(mbhc);
+	*highv = mic_volt > v_hs_max;
+	if (!highhph && *highv)
+		invalid = true;
+	else if (mic_volt < mbhc->mbhc_data.v_inval_ins_high &&
+		 (mic_volt > mbhc->mbhc_data.v_inval_ins_low))
+		invalid = true;
+
+	return invalid;
+}
+
+static short wcd9xxx_read_sta_result(struct snd_soc_codec *codec)
+{
+	u8 bias_msb, bias_lsb;
+	short bias_value;
+
+	bias_msb = snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B3_STATUS);
+	bias_lsb = snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B2_STATUS);
+	bias_value = (bias_msb << 8) | bias_lsb;
+	return bias_value;
+}
+
+static short wcd9xxx_read_dce_result(struct snd_soc_codec *codec)
+{
+	u8 bias_msb, bias_lsb;
+	short bias_value;
+
+	bias_msb = snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B5_STATUS);
+	bias_lsb = snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B4_STATUS);
+	bias_value = (bias_msb << 8) | bias_lsb;
+	return bias_value;
+}
+
+static void wcd9xxx_turn_onoff_rel_detection(struct snd_soc_codec *codec,
+					     bool on)
+{
+	snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x02, on << 1);
+}
+
+static short __wcd9xxx_codec_sta_dce(struct wcd9xxx_mbhc *mbhc, int dce,
+				     bool override_bypass, bool noreldetection)
+{
+	short bias_value;
+	struct snd_soc_codec *codec = mbhc->codec;
+
+	wcd9xxx_disable_irq(mbhc->resmgr->core, WCD9XXX_IRQ_MBHC_POTENTIAL);
+	if (noreldetection)
+		wcd9xxx_turn_onoff_rel_detection(codec, false);
+
+	/* Turn on the override */
+	if (!override_bypass)
+		snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x4, 0x4);
+	if (dce) {
+		snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8,
+				    0x8);
+		snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x4);
+		snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8,
+				    0x0);
+		usleep_range(mbhc->mbhc_data.t_sta_dce,
+			     mbhc->mbhc_data.t_sta_dce);
+		snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x4);
+		usleep_range(mbhc->mbhc_data.t_dce, mbhc->mbhc_data.t_dce);
+		bias_value = wcd9xxx_read_dce_result(codec);
+	} else {
+		snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8,
+				    0x8);
+		snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x2);
+		snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8,
+				    0x0);
+		usleep_range(mbhc->mbhc_data.t_sta_dce,
+			     mbhc->mbhc_data.t_sta_dce);
+		snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x2);
+		usleep_range(mbhc->mbhc_data.t_sta,
+			     mbhc->mbhc_data.t_sta);
+		bias_value = wcd9xxx_read_sta_result(codec);
+		snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8,
+				    0x8);
+		snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x0);
+	}
+	/* Turn off the override after measuring mic voltage */
+	if (!override_bypass)
+		snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x04,
+				    0x00);
+
+	if (noreldetection)
+		wcd9xxx_turn_onoff_rel_detection(codec, true);
+	wcd9xxx_enable_irq(mbhc->resmgr->core, WCD9XXX_IRQ_MBHC_POTENTIAL);
+
+	return bias_value;
+}
+
+static short wcd9xxx_codec_sta_dce(struct wcd9xxx_mbhc *mbhc, int dce,
+				   bool norel)
+{
+	return __wcd9xxx_codec_sta_dce(mbhc, dce, false, norel);
+}
+
+static s32 wcd9xxx_codec_sta_dce_v(struct wcd9xxx_mbhc *mbhc, s8 dce,
+				   u16 bias_value)
+{
+	s16 value, z, mb;
+	s32 mv;
+
+	value = bias_value;
+	if (dce) {
+		z = (mbhc->mbhc_data.dce_z);
+		mb = (mbhc->mbhc_data.dce_mb);
+		mv = (value - z) * (s32)mbhc->mbhc_data.micb_mv / (mb - z);
+	} else {
+		z = (mbhc->mbhc_data.sta_z);
+		mb = (mbhc->mbhc_data.sta_mb);
+		mv = (value - z) * (s32)mbhc->mbhc_data.micb_mv / (mb - z);
+	}
+
+	return mv;
+}
+
+/* called only from interrupt which is under codec_resource_lock acquisition */
+static short wcd9xxx_mbhc_setup_hs_polling(struct wcd9xxx_mbhc *mbhc)
+{
+	struct snd_soc_codec *codec = mbhc->codec;
+	short bias_value;
+	u8 cfilt_mode;
+
+	WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
+
+	pr_debug("%s: enter\n", __func__);
+	if (!mbhc->mbhc_cfg->calibration) {
+		pr_err("%s: Error, no calibration exists\n", __func__);
+		return -ENODEV;
+	}
+
+	/*
+	 * Request BG and clock.
+	 * These will be released by wcd9xxx_cleanup_hs_polling
+	 */
+	wcd9xxx_resmgr_get_bandgap(mbhc->resmgr, WCD9XXX_BANDGAP_MBHC_MODE);
+	wcd9xxx_resmgr_get_clk_block(mbhc->resmgr, WCD9XXX_CLK_RCO);
+
+	snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN1, 0x05, 0x01);
+
+	/* Make sure CFILT is in fast mode, save current mode */
+	cfilt_mode = snd_soc_read(codec, mbhc->mbhc_bias_regs.cfilt_ctl);
+	snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.cfilt_ctl, 0x70, 0x00);
+
+	snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x2, 0x2);
+	snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x84);
+
+	snd_soc_update_bits(codec, WCD9XXX_A_TX_7_MBHC_EN, 0x80, 0x80);
+	snd_soc_update_bits(codec, WCD9XXX_A_TX_7_MBHC_EN, 0x1F, 0x1C);
+	snd_soc_update_bits(codec, WCD9XXX_A_TX_7_MBHC_TEST_CTL, 0x40, 0x40);
+
+	snd_soc_update_bits(codec, WCD9XXX_A_TX_7_MBHC_EN, 0x80, 0x00);
+	snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8, 0x8);
+	snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8, 0x00);
+
+	snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x2, 0x2);
+	snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8, 0x8);
+
+	wcd9xxx_calibrate_hs_polling(mbhc);
+
+	/* don't flip override */
+	bias_value = __wcd9xxx_codec_sta_dce(mbhc, 1, true, true);
+	snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.cfilt_ctl, 0x40,
+			    cfilt_mode);
+	snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x13, 0x00);
+
+	return bias_value;
+}
+
+static void wcd9xxx_shutdown_hs_removal_detect(struct wcd9xxx_mbhc *mbhc)
+{
+	struct snd_soc_codec *codec = mbhc->codec;
+	const struct wcd9xxx_mbhc_general_cfg *generic =
+	    WCD9XXX_MBHC_CAL_GENERAL_PTR(mbhc->mbhc_cfg->calibration);
+
+	/* Need MBHC clock */
+	wcd9xxx_resmgr_get_clk_block(mbhc->resmgr, WCD9XXX_CLK_RCO);
+
+	snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x2, 0x2);
+	snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x6, 0x0);
+	snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg, 0x80, 0x00);
+
+	usleep_range(generic->t_shutdown_plug_rem,
+		     generic->t_shutdown_plug_rem);
+
+	snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0xA, 0x8);
+
+	/* Put requested CLK back */
+	wcd9xxx_resmgr_put_clk_block(mbhc->resmgr, WCD9XXX_CLK_RCO);
+
+	snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x00);
+}
+
+static void wcd9xxx_cleanup_hs_polling(struct wcd9xxx_mbhc *mbhc)
+{
+	WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
+
+	wcd9xxx_shutdown_hs_removal_detect(mbhc);
+
+	/* Release clock and BG requested by wcd9xxx_mbhc_setup_hs_polling */
+	wcd9xxx_resmgr_put_clk_block(mbhc->resmgr, WCD9XXX_CLK_RCO);
+	wcd9xxx_resmgr_put_bandgap(mbhc->resmgr, WCD9XXX_BANDGAP_MBHC_MODE);
+
+	mbhc->polling_active = false;
+	mbhc->mbhc_state = MBHC_STATE_NONE;
+}
+
+static s16 scale_v_micb_vddio(struct wcd9xxx_mbhc *mbhc, int v, bool tovddio)
+{
+	int r;
+	int vddio_k, mb_k;
+	vddio_k = wcd9xxx_resmgr_get_k_val(mbhc->resmgr, VDDIO_MICBIAS_MV);
+	mb_k = wcd9xxx_resmgr_get_k_val(mbhc->resmgr, mbhc->mbhc_data.micb_mv);
+	if (tovddio)
+		r = v * vddio_k / mb_k;
+	else
+		r = v * mb_k / vddio_k;
+	return r;
+}
+
+/* called under codec_resource_lock acquisition */
+static void wcd9xxx_codec_hphr_gnd_switch(struct snd_soc_codec *codec, bool on)
+{
+	snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x01, on);
+	if (on)
+		usleep_range(5000, 5000);
+}
+
+static bool wcd9xxx_is_inval_ins_delta(struct snd_soc_codec *codec,
+				       int mic_volt, int mic_volt_prev,
+				       int threshold)
+{
+	return abs(mic_volt - mic_volt_prev) > threshold;
+}
+
+/* called under codec_resource_lock acquisition and mbhc override = 1 */
+static enum wcd9xxx_mbhc_plug_type
+wcd9xxx_codec_get_plug_type(struct wcd9xxx_mbhc *mbhc, bool highhph)
+{
+	int i;
+	bool gndswitch, vddioswitch;
+	int scaled;
+	struct wcd9xxx_mbhc_plug_type_cfg *plug_type_ptr;
+	struct snd_soc_codec *codec = mbhc->codec;
+	const bool vddio = (mbhc->mbhc_data.micb_mv != VDDIO_MICBIAS_MV);
+	int num_det = (NUM_DCE_PLUG_DETECT + vddio);
+	enum wcd9xxx_mbhc_plug_type plug_type[num_det];
+	s16 mb_v[num_det];
+	s32 mic_mv[num_det];
+	bool inval;
+	bool highdelta;
+	bool ahighv = false, highv;
+
+	pr_debug("%s: enter\n", __func__);
+	WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
+
+	/* make sure override is on */
+	WARN_ON(!(snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B1_CTL) & 0x04));
+
+	/* GND and MIC swap detection requires at least 2 rounds of DCE */
+	BUG_ON(num_det < 2);
+
+	plug_type_ptr =
+		WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(mbhc->mbhc_cfg->calibration);
+
+	plug_type[0] = PLUG_TYPE_INVALID;
+
+	/* performs DCEs for N times
+	 * 1st: check if voltage is in invalid range
+	 * 2nd - N-2nd: check voltage range and delta
+	 * N-1st: check voltage range, delta with HPHR GND switch
+	 * Nth: check voltage range with VDDIO switch if micbias V != vddio V*/
+	for (i = 0; i < num_det; i++) {
+		gndswitch = (i == (num_det - 1 - vddio));
+		vddioswitch = (vddio && ((i == num_det - 1) ||
+					(i == num_det - 2)));
+		if (i == 0) {
+			mb_v[i] = wcd9xxx_mbhc_setup_hs_polling(mbhc);
+			mic_mv[i] = wcd9xxx_codec_sta_dce_v(mbhc, 1 , mb_v[i]);
+			inval = wcd9xxx_is_inval_ins_range(mbhc, mic_mv[i],
+					highhph, &highv);
+			ahighv |= highv;
+			scaled = mic_mv[i];
+		} else {
+			if (vddioswitch)
+				__wcd9xxx_switch_micbias(mbhc, 1,
+							     false, false);
+			if (gndswitch)
+				wcd9xxx_codec_hphr_gnd_switch(codec, true);
+			mb_v[i] = __wcd9xxx_codec_sta_dce(mbhc, 1, true, true);
+			mic_mv[i] = wcd9xxx_codec_sta_dce_v(mbhc, 1 , mb_v[i]);
+			if (vddioswitch)
+				scaled = scale_v_micb_vddio(mbhc, mic_mv[i],
+							    false);
+			else
+				scaled = mic_mv[i];
+			/* !gndswitch & vddioswitch means the previous DCE
+			 * was done with gndswitch, don't compare with DCE
+			 * with gndswitch */
+			highdelta = wcd9xxx_is_inval_ins_delta(codec, scaled,
+					mic_mv[i - !gndswitch - vddioswitch],
+					FAKE_INS_DELTA_SCALED_MV);
+			inval = (wcd9xxx_is_inval_ins_range(mbhc, mic_mv[i],
+						highhph, &highv) ||
+					highdelta);
+			ahighv |= highv;
+			if (gndswitch)
+				wcd9xxx_codec_hphr_gnd_switch(codec, false);
+			if (vddioswitch)
+				__wcd9xxx_switch_micbias(mbhc, 0,
+							     false, false);
+			/* claim UNSUPPORTED plug insertion when
+			 * good headset is detected but HPHR GND switch makes
+			 * delta difference */
+			if (i == (num_det - 2) && highdelta && !ahighv)
+				plug_type[0] = PLUG_TYPE_GND_MIC_SWAP;
+			else if (i == (num_det - 1) && inval)
+				plug_type[0] = PLUG_TYPE_INVALID;
+		}
+		pr_debug("%s: DCE #%d, %04x, V %d, scaled V %d, GND %d, VDDIO %d, inval %d\n",
+			 __func__, i + 1, mb_v[i] & 0xffff, mic_mv[i], scaled,
+			 gndswitch, vddioswitch, inval);
+		/* don't need to run further DCEs */
+		if (ahighv && inval)
+			break;
+		mic_mv[i] = scaled;
+	}
+
+	for (i = 0; (plug_type[0] != PLUG_TYPE_GND_MIC_SWAP && !inval) &&
+		    (i < num_det); i++) {
+		/*
+		 * If we are here, means none of the all
+		 * measurements are fake, continue plug type detection.
+		 * If all three measurements do not produce same
+		 * plug type, restart insertion detection
+		 */
+		if (mic_mv[i] < plug_type_ptr->v_no_mic) {
+			plug_type[i] = PLUG_TYPE_HEADPHONE;
+			pr_debug("%s: Detect attempt %d, detected Headphone\n",
+				 __func__, i);
+		} else if (highhph && (mic_mv[i] > plug_type_ptr->v_hs_max)) {
+			plug_type[i] = PLUG_TYPE_HIGH_HPH;
+			pr_debug("%s: Detect attempt %d, detected High Headphone\n",
+				 __func__, i);
+		} else {
+			plug_type[i] = PLUG_TYPE_HEADSET;
+			pr_debug("%s: Detect attempt %d, detected Headset\n",
+					__func__, i);
+		}
+
+		if (i > 0 && (plug_type[i - 1] != plug_type[i])) {
+			pr_err("%s: Detect attempt %d and %d are not same",
+			       __func__, i - 1, i);
+			plug_type[0] = PLUG_TYPE_INVALID;
+			inval = true;
+			break;
+		}
+	}
+
+	pr_debug("%s: Detected plug type %d\n", __func__, plug_type[0]);
+	pr_debug("%s: leave\n", __func__);
+	return plug_type[0];
+}
+
+static bool wcd9xxx_swch_level_remove(struct wcd9xxx_mbhc *mbhc)
+{
+	if (mbhc->mbhc_cfg->gpio)
+		return (gpio_get_value_cansleep(mbhc->mbhc_cfg->gpio) !=
+			mbhc->mbhc_cfg->gpio_level_insert);
+	else if (mbhc->mbhc_cfg->insert_detect)
+		return snd_soc_read(mbhc->codec,
+				    WCD9XXX_A_MBHC_INSERT_DET_STATUS) &
+				    (1 << 2);
+	else
+		WARN(1, "Invalid jack detection configuration\n");
+
+	return true;
+}
+
+static bool is_clk_active(struct snd_soc_codec *codec)
+{
+	return !!(snd_soc_read(codec, WCD9XXX_A_CDC_CLK_MCLK_CTL) & 0x05);
+}
+
+static int wcd9xxx_enable_hs_detect(struct wcd9xxx_mbhc *mbhc,
+				    int insertion, int trigger, bool padac_off)
+{
+	struct snd_soc_codec *codec = mbhc->codec;
+	int central_bias_enabled = 0;
+	const struct wcd9xxx_mbhc_general_cfg *generic =
+	    WCD9XXX_MBHC_CAL_GENERAL_PTR(mbhc->mbhc_cfg->calibration);
+	const struct wcd9xxx_mbhc_plug_detect_cfg *plug_det =
+	    WCD9XXX_MBHC_CAL_PLUG_DET_PTR(mbhc->mbhc_cfg->calibration);
+
+	pr_debug("%s: enter insertion(%d) trigger(0x%x)\n",
+		 __func__, insertion, trigger);
+
+	if (!mbhc->mbhc_cfg->calibration) {
+		pr_err("Error, no wcd9xxx calibration\n");
+		return -EINVAL;
+	}
+
+	snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_INT_CTL, 0x1, 0);
+
+	/*
+	 * Make sure mic bias and Mic line schmitt trigger
+	 * are turned OFF
+	 */
+	snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01, 0x01);
+	snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg, 0x90, 0x00);
+
+	if (insertion) {
+		wcd9xxx_switch_micbias(mbhc, 0);
+
+		/* DAPM can manipulate PA/DAC bits concurrently */
+		if (padac_off == true)
+			wcd9xxx_set_and_turnoff_hph_padac(mbhc);
+
+		if (trigger & MBHC_USE_HPHL_TRIGGER) {
+			/* Enable HPH Schmitt Trigger */
+			snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x11,
+					0x11);
+			snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x0C,
+					plug_det->hph_current << 2);
+			snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x02,
+					0x02);
+		}
+		if (trigger & MBHC_USE_MB_TRIGGER) {
+			/* enable the mic line schmitt trigger */
+			snd_soc_update_bits(codec,
+					mbhc->mbhc_bias_regs.mbhc_reg,
+					0x60, plug_det->mic_current << 5);
+			snd_soc_update_bits(codec,
+					mbhc->mbhc_bias_regs.mbhc_reg,
+					0x80, 0x80);
+			usleep_range(plug_det->t_mic_pid, plug_det->t_mic_pid);
+			snd_soc_update_bits(codec,
+					mbhc->mbhc_bias_regs.ctl_reg, 0x01,
+					0x00);
+			snd_soc_update_bits(codec,
+					mbhc->mbhc_bias_regs.mbhc_reg,
+					0x10, 0x10);
+		}
+
+		/* setup for insetion detection */
+		snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_INT_CTL, 0x2, 0);
+	} else {
+		pr_debug("setup for removal detection\n");
+		/* Make sure the HPH schmitt trigger is OFF */
+		snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x12, 0x00);
+
+		/* enable the mic line schmitt trigger */
+		snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg,
+				    0x01, 0x00);
+		snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg, 0x60,
+				    plug_det->mic_current << 5);
+		snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg,
+				    0x80, 0x80);
+		usleep_range(plug_det->t_mic_pid, plug_det->t_mic_pid);
+		snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg,
+				    0x10, 0x10);
+
+		/* Setup for low power removal detection */
+		snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_INT_CTL, 0x2,
+				    0x2);
+	}
+
+	if (snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B1_CTL) & 0x4) {
+		/* called by interrupt */
+		if (!is_clk_active(codec)) {
+			wcd9xxx_resmgr_enable_config_mode(codec, 1);
+			snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL,
+					0x06, 0);
+			usleep_range(generic->t_shutdown_plug_rem,
+					generic->t_shutdown_plug_rem);
+			wcd9xxx_resmgr_enable_config_mode(codec, 0);
+		} else
+			snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL,
+					0x06, 0);
+	}
+
+	snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.int_rbias, 0x80, 0);
+
+	/* If central bandgap disabled */
+	if (!(snd_soc_read(codec, WCD9XXX_A_PIN_CTL_OE1) & 1)) {
+		snd_soc_update_bits(codec, WCD9XXX_A_PIN_CTL_OE1, 0x3, 0x3);
+		usleep_range(generic->t_bg_fast_settle,
+			     generic->t_bg_fast_settle);
+		central_bias_enabled = 1;
+	}
+
+	/* If LDO_H disabled */
+	if (snd_soc_read(codec, WCD9XXX_A_PIN_CTL_OE0) & 0x80) {
+		snd_soc_update_bits(codec, WCD9XXX_A_PIN_CTL_OE0, 0x10, 0);
+		snd_soc_update_bits(codec, WCD9XXX_A_PIN_CTL_OE0, 0x80, 0x80);
+		usleep_range(generic->t_ldoh, generic->t_ldoh);
+		snd_soc_update_bits(codec, WCD9XXX_A_PIN_CTL_OE0, 0x80, 0);
+
+		if (central_bias_enabled)
+			snd_soc_update_bits(codec, WCD9XXX_A_PIN_CTL_OE1, 0x1,
+					    0);
+	}
+
+	snd_soc_update_bits(codec, mbhc->resmgr->reg_addr->micb_4_mbhc, 0x3,
+			    mbhc->mbhc_cfg->micbias);
+
+	wcd9xxx_enable_irq(mbhc->resmgr->core, WCD9XXX_IRQ_MBHC_INSERTION);
+	snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_INT_CTL, 0x1, 0x1);
+	pr_debug("%s: leave\n", __func__);
+
+	return 0;
+}
+
+/* called under codec_resource_lock acquisition */
+static void wcd9xxx_find_plug_and_report(struct wcd9xxx_mbhc *mbhc,
+					 enum wcd9xxx_mbhc_plug_type plug_type)
+{
+	pr_debug("%s: enter current_plug(%d) new_plug(%d)\n",
+		 __func__, mbhc->current_plug, plug_type);
+
+	WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
+
+	if (plug_type == PLUG_TYPE_HEADPHONE &&
+	    mbhc->current_plug == PLUG_TYPE_NONE) {
+		/*
+		 * Nothing was reported previously
+		 * report a headphone or unsupported
+		 */
+		wcd9xxx_report_plug(mbhc, 1, SND_JACK_HEADPHONE);
+		wcd9xxx_cleanup_hs_polling(mbhc);
+	} else if (plug_type == PLUG_TYPE_GND_MIC_SWAP) {
+		if (!mbhc->mbhc_cfg->detect_extn_cable) {
+			if (mbhc->current_plug == PLUG_TYPE_HEADSET)
+				wcd9xxx_report_plug(mbhc, 0,
+							 SND_JACK_HEADSET);
+			else if (mbhc->current_plug == PLUG_TYPE_HEADPHONE)
+				wcd9xxx_report_plug(mbhc, 0,
+							 SND_JACK_HEADPHONE);
+		}
+		wcd9xxx_report_plug(mbhc, 1, SND_JACK_UNSUPPORTED);
+		wcd9xxx_cleanup_hs_polling(mbhc);
+	} else if (plug_type == PLUG_TYPE_HEADSET) {
+		/*
+		 * If Headphone was reported previously, this will
+		 * only report the mic line
+		 */
+		wcd9xxx_report_plug(mbhc, 1, SND_JACK_HEADSET);
+		msleep(100);
+		wcd9xxx_start_hs_polling(mbhc);
+	} else if (plug_type == PLUG_TYPE_HIGH_HPH) {
+		if (mbhc->mbhc_cfg->detect_extn_cable) {
+			/* High impedance device found. Report as LINEOUT*/
+			wcd9xxx_report_plug(mbhc, 1, SND_JACK_LINEOUT);
+			wcd9xxx_cleanup_hs_polling(mbhc);
+			pr_debug("%s: setup mic trigger for further detection\n",
+				 __func__);
+			mbhc->lpi_enabled = true;
+			/*
+			 * Do not enable HPHL trigger. If playback is active,
+			 * it might lead to continuous false HPHL triggers
+			 */
+			wcd9xxx_enable_hs_detect(mbhc, 1, MBHC_USE_MB_TRIGGER,
+						 false);
+		} else {
+			if (mbhc->current_plug == PLUG_TYPE_NONE)
+				wcd9xxx_report_plug(mbhc, 1,
+							 SND_JACK_HEADPHONE);
+			wcd9xxx_cleanup_hs_polling(mbhc);
+			pr_debug("setup mic trigger for further detection\n");
+			mbhc->lpi_enabled = true;
+			wcd9xxx_enable_hs_detect(mbhc, 1, MBHC_USE_MB_TRIGGER |
+							  MBHC_USE_HPHL_TRIGGER,
+						 false);
+		}
+	} else {
+		WARN(1, "Unexpected current plug_type %d, plug_type %d\n",
+		     mbhc->current_plug, plug_type);
+	}
+	pr_debug("%s: leave\n", __func__);
+}
+
+/* called under codec_resource_lock acquisition */
+static void wcd9xxx_mbhc_decide_swch_plug(struct wcd9xxx_mbhc *mbhc)
+{
+	enum wcd9xxx_mbhc_plug_type plug_type;
+	struct snd_soc_codec *codec = mbhc->codec;
+
+	pr_debug("%s: enter\n", __func__);
+
+	WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
+
+	wcd9xxx_turn_onoff_override(codec, true);
+	plug_type = wcd9xxx_codec_get_plug_type(mbhc, true);
+	wcd9xxx_turn_onoff_override(codec, false);
+
+	if (wcd9xxx_swch_level_remove(mbhc)) {
+		pr_debug("%s: Switch level is low when determining plug\n",
+			 __func__);
+		return;
+	}
+
+	if (plug_type == PLUG_TYPE_INVALID ||
+	    plug_type == PLUG_TYPE_GND_MIC_SWAP) {
+		wcd9xxx_schedule_hs_detect_plug(mbhc,
+						&mbhc->correct_plug_swch);
+	} else if (plug_type == PLUG_TYPE_HEADPHONE) {
+		wcd9xxx_report_plug(mbhc, 1, SND_JACK_HEADPHONE);
+		wcd9xxx_schedule_hs_detect_plug(mbhc,
+						&mbhc->correct_plug_swch);
+	} else {
+		pr_debug("%s: Valid plug found, determine plug type %d\n",
+			 __func__, plug_type);
+		wcd9xxx_find_plug_and_report(mbhc, plug_type);
+	}
+	pr_debug("%s: leave\n", __func__);
+}
+
+/* called under codec_resource_lock acquisition */
+static void wcd9xxx_mbhc_detect_plug_type(struct wcd9xxx_mbhc *mbhc)
+{
+	struct snd_soc_codec *codec = mbhc->codec;
+	const struct wcd9xxx_mbhc_plug_detect_cfg *plug_det =
+		WCD9XXX_MBHC_CAL_PLUG_DET_PTR(mbhc->mbhc_cfg->calibration);
+
+	pr_debug("%s: enter\n", __func__);
+	WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
+
+	/*
+	 * Turn on the override,
+	 * wcd9xxx_mbhc_setup_hs_polling requires override on
+	 */
+	wcd9xxx_turn_onoff_override(codec, true);
+	if (plug_det->t_ins_complete > 20)
+		msleep(plug_det->t_ins_complete);
+	else
+		usleep_range(plug_det->t_ins_complete * 1000,
+			     plug_det->t_ins_complete * 1000);
+	/* Turn off the override */
+	wcd9xxx_turn_onoff_override(codec, false);
+
+	if (wcd9xxx_swch_level_remove(mbhc))
+		pr_debug("%s: Switch level low when determining plug\n",
+			 __func__);
+	else
+		wcd9xxx_mbhc_decide_swch_plug(mbhc);
+	pr_debug("%s: leave\n", __func__);
+}
+
+/* called only from interrupt which is under codec_resource_lock acquisition */
+static void wcd9xxx_hs_insert_irq_swch(struct wcd9xxx_mbhc *mbhc,
+				       bool is_removal)
+{
+	if (!is_removal) {
+		pr_debug("%s: MIC trigger insertion interrupt\n", __func__);
+
+		rmb();
+		if (mbhc->lpi_enabled)
+			msleep(100);
+
+		rmb();
+		if (!mbhc->lpi_enabled) {
+			pr_debug("%s: lpi is disabled\n", __func__);
+		} else if (!wcd9xxx_swch_level_remove(mbhc)) {
+			pr_debug("%s: Valid insertion, detect plug type\n",
+				 __func__);
+			wcd9xxx_mbhc_decide_swch_plug(mbhc);
+		} else {
+			pr_debug("%s: Invalid insertion stop plug detection\n",
+				 __func__);
+		}
+	} else if (mbhc->mbhc_cfg->detect_extn_cable) {
+		pr_debug("%s: Removal\n", __func__);
+		if (!wcd9xxx_swch_level_remove(mbhc)) {
+			/*
+			 * Switch indicates, something is still inserted.
+			 * This could be extension cable i.e. headset is
+			 * removed from extension cable.
+			 */
+			/* cancel detect plug */
+			wcd9xxx_cancel_hs_detect_plug(mbhc,
+						      &mbhc->correct_plug_swch);
+			wcd9xxx_mbhc_decide_swch_plug(mbhc);
+		}
+	} else {
+		pr_err("%s: Switch IRQ used, invalid MBHC Removal\n", __func__);
+	}
+}
+
+static bool is_valid_mic_voltage(struct wcd9xxx_mbhc *mbhc, s32 mic_mv)
+{
+	const struct wcd9xxx_mbhc_plug_type_cfg *plug_type =
+	    WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(mbhc->mbhc_cfg->calibration);
+	const s16 v_hs_max = wcd9xxx_get_current_v_hs_max(mbhc);
+
+	return (!(mic_mv > 10 && mic_mv < 80) && (mic_mv > plug_type->v_no_mic)
+		&& (mic_mv < v_hs_max)) ? true : false;
+}
+
+/*
+ * called under codec_resource_lock acquisition
+ * returns true if mic voltage range is back to normal insertion
+ * returns false either if timedout or removed
+ */
+static bool wcd9xxx_hs_remove_settle(struct wcd9xxx_mbhc *mbhc)
+{
+	int i;
+	bool timedout, settled = false;
+	s32 mic_mv[NUM_DCE_PLUG_DETECT];
+	short mb_v[NUM_DCE_PLUG_DETECT];
+	unsigned long retry = 0, timeout;
+
+	timeout = jiffies + msecs_to_jiffies(HS_DETECT_PLUG_TIME_MS);
+	while (!(timedout = time_after(jiffies, timeout))) {
+		retry++;
+		if (wcd9xxx_swch_level_remove(mbhc)) {
+			pr_debug("%s: Switch indicates removal\n", __func__);
+			break;
+		}
+
+		if (retry > 1)
+			msleep(250);
+		else
+			msleep(50);
+
+		if (wcd9xxx_swch_level_remove(mbhc)) {
+			pr_debug("%s: Switch indicates removal\n", __func__);
+			break;
+		}
+
+		for (i = 0; i < NUM_DCE_PLUG_DETECT; i++) {
+			mb_v[i] = wcd9xxx_codec_sta_dce(mbhc, 1,  true);
+			mic_mv[i] = wcd9xxx_codec_sta_dce_v(mbhc, 1 , mb_v[i]);
+			pr_debug("%s : DCE run %lu, mic_mv = %d(%x)\n",
+				 __func__, retry, mic_mv[i], mb_v[i]);
+		}
+
+		if (wcd9xxx_swch_level_remove(mbhc)) {
+			pr_debug("%s: Switcn indicates removal\n", __func__);
+			break;
+		}
+
+		if (mbhc->current_plug == PLUG_TYPE_NONE) {
+			pr_debug("%s : headset/headphone is removed\n",
+				 __func__);
+			break;
+		}
+
+		for (i = 0; i < NUM_DCE_PLUG_DETECT; i++)
+			if (!is_valid_mic_voltage(mbhc, mic_mv[i]))
+				break;
+
+		if (i == NUM_DCE_PLUG_DETECT) {
+			pr_debug("%s: MIC voltage settled\n", __func__);
+			settled = true;
+			msleep(200);
+			break;
+		}
+	}
+
+	if (timedout)
+		pr_debug("%s: Microphone did not settle in %d seconds\n",
+			 __func__, HS_DETECT_PLUG_TIME_MS);
+	return settled;
+}
+
+/* called only from interrupt which is under codec_resource_lock acquisition */
+static void wcd9xxx_hs_remove_irq_swch(struct wcd9xxx_mbhc *mbhc)
+{
+	pr_debug("%s: enter\n", __func__);
+	if (wcd9xxx_hs_remove_settle(mbhc))
+		wcd9xxx_start_hs_polling(mbhc);
+	pr_debug("%s: leave\n", __func__);
+}
+
+/* called only from interrupt which is under codec_resource_lock acquisition */
+static void wcd9xxx_hs_remove_irq_noswch(struct wcd9xxx_mbhc *mbhc)
+{
+	short bias_value;
+	bool removed = true;
+	struct snd_soc_codec *codec = mbhc->codec;
+	const struct wcd9xxx_mbhc_general_cfg *generic =
+		WCD9XXX_MBHC_CAL_GENERAL_PTR(mbhc->mbhc_cfg->calibration);
+	int min_us = FAKE_REMOVAL_MIN_PERIOD_MS * 1000;
+
+	pr_debug("%s: enter\n", __func__);
+	if (mbhc->current_plug != PLUG_TYPE_HEADSET) {
+		pr_debug("%s(): Headset is not inserted, ignore removal\n",
+			 __func__);
+		snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL,
+				0x08, 0x08);
+		return;
+	}
+
+	usleep_range(generic->t_shutdown_plug_rem,
+			generic->t_shutdown_plug_rem);
+
+	do {
+		bias_value = wcd9xxx_codec_sta_dce(mbhc, 1,  true);
+		pr_debug("%s: DCE %d,%d, %d us left\n", __func__, bias_value,
+			 wcd9xxx_codec_sta_dce_v(mbhc, 1, bias_value), min_us);
+		if (bias_value < wcd9xxx_get_current_v_ins(mbhc, false)) {
+			pr_debug("%s: checking false removal\n", __func__);
+			msleep(500);
+			removed = !wcd9xxx_hs_remove_settle(mbhc);
+			pr_debug("%s: headset %sactually removed\n", __func__,
+				 removed ? "" : "not ");
+			break;
+		}
+		min_us -= mbhc->mbhc_data.t_dce;
+	} while (min_us > 0);
+
+	if (removed) {
+		if (mbhc->mbhc_cfg->detect_extn_cable) {
+			if (!wcd9xxx_swch_level_remove(mbhc)) {
+				/*
+				 * extension cable is still plugged in
+				 * report it as LINEOUT device
+				 */
+				wcd9xxx_report_plug(mbhc, 1, SND_JACK_LINEOUT);
+				wcd9xxx_cleanup_hs_polling(mbhc);
+				wcd9xxx_enable_hs_detect(mbhc, 1,
+							 MBHC_USE_MB_TRIGGER,
+							 false);
+			}
+		} else {
+			/* Cancel possibly running hs_detect_work */
+			wcd9xxx_cancel_hs_detect_plug(mbhc,
+						    &mbhc->correct_plug_noswch);
+			/*
+			 * If this removal is not false, first check the micbias
+			 * switch status and switch it to LDOH if it is already
+			 * switched to VDDIO.
+			 */
+			wcd9xxx_switch_micbias(mbhc, 0);
+
+			wcd9xxx_report_plug(mbhc, 0, SND_JACK_HEADSET);
+			wcd9xxx_cleanup_hs_polling(mbhc);
+			wcd9xxx_enable_hs_detect(mbhc, 1, MBHC_USE_MB_TRIGGER |
+							  MBHC_USE_HPHL_TRIGGER,
+						 true);
+		}
+	} else {
+		wcd9xxx_start_hs_polling(mbhc);
+	}
+	pr_debug("%s: leave\n", __func__);
+}
+
+/* called only from interrupt which is under codec_resource_lock acquisition */
+static void wcd9xxx_hs_insert_irq_extn(struct wcd9xxx_mbhc *mbhc,
+				       bool is_mb_trigger)
+{
+	/* Cancel possibly running hs_detect_work */
+	wcd9xxx_cancel_hs_detect_plug(mbhc, &mbhc->correct_plug_swch);
+
+	if (is_mb_trigger) {
+		pr_debug("%s: Waiting for Headphone left trigger\n", __func__);
+		wcd9xxx_enable_hs_detect(mbhc, 1, MBHC_USE_HPHL_TRIGGER, false);
+	} else  {
+		pr_debug("%s: HPHL trigger received, detecting plug type\n",
+			 __func__);
+		wcd9xxx_mbhc_detect_plug_type(mbhc);
+	}
+}
+
+static irqreturn_t wcd9xxx_hs_remove_irq(int irq, void *data)
+{
+	bool vddio;
+	struct wcd9xxx_mbhc *mbhc = data;
+
+	pr_debug("%s: enter, removal interrupt\n", __func__);
+	WCD9XXX_BCL_LOCK(mbhc->resmgr);
+	vddio = (mbhc->mbhc_data.micb_mv != VDDIO_MICBIAS_MV &&
+		 mbhc->mbhc_micbias_switched);
+	if (vddio)
+		__wcd9xxx_switch_micbias(mbhc, 0, false, true);
+
+	if (mbhc->mbhc_cfg->detect_extn_cable &&
+	    !wcd9xxx_swch_level_remove(mbhc))
+		wcd9xxx_hs_remove_irq_noswch(mbhc);
+	else
+		wcd9xxx_hs_remove_irq_swch(mbhc);
+
+	/*
+	 * if driver turned off vddio switch and headset is not removed,
+	 * turn on the vddio switch back, if headset is removed then vddio
+	 * switch is off by time now and shouldn't be turn on again from here
+	 */
+	if (vddio && mbhc->current_plug == PLUG_TYPE_HEADSET)
+		__wcd9xxx_switch_micbias(mbhc, 1, true, true);
+	WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t wcd9xxx_hs_insert_irq(int irq, void *data)
+{
+	bool is_mb_trigger, is_removal;
+	struct wcd9xxx_mbhc *mbhc = data;
+	struct snd_soc_codec *codec = mbhc->codec;
+
+	pr_debug("%s: enter\n", __func__);
+	WCD9XXX_BCL_LOCK(mbhc->resmgr);
+	wcd9xxx_disable_irq(codec->control_data, WCD9XXX_IRQ_MBHC_INSERTION);
+
+	is_mb_trigger = !!(snd_soc_read(codec, mbhc->mbhc_bias_regs.mbhc_reg) &
+			   0x10);
+	is_removal = !!(snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_INT_CTL) & 0x02);
+	snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_INT_CTL, 0x03, 0x00);
+
+	/* Turn off both HPH and MIC line schmitt triggers */
+	snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg, 0x90, 0x00);
+	snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x13, 0x00);
+	snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01, 0x00);
+
+	if (mbhc->mbhc_cfg->detect_extn_cable &&
+	    mbhc->current_plug == PLUG_TYPE_HIGH_HPH)
+		wcd9xxx_hs_insert_irq_extn(mbhc, is_mb_trigger);
+	else
+		wcd9xxx_hs_insert_irq_swch(mbhc, is_removal);
+
+	WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
+	return IRQ_HANDLED;
+}
+
+static void wcd9xxx_btn_lpress_fn(struct work_struct *work)
+{
+	struct delayed_work *dwork;
+	short bias_value;
+	int dce_mv, sta_mv;
+	struct wcd9xxx_mbhc *mbhc;
+
+	pr_debug("%s:\n", __func__);
+
+	dwork = to_delayed_work(work);
+	mbhc = container_of(dwork, struct wcd9xxx_mbhc, mbhc_btn_dwork);
+
+	bias_value = wcd9xxx_read_sta_result(mbhc->codec);
+	sta_mv = wcd9xxx_codec_sta_dce_v(mbhc, 0, bias_value);
+
+	bias_value = wcd9xxx_read_dce_result(mbhc->codec);
+	dce_mv = wcd9xxx_codec_sta_dce_v(mbhc, 1, bias_value);
+	pr_debug("%s: STA: %d, DCE: %d\n", __func__, sta_mv, dce_mv);
+
+	pr_debug("%s: Reporting long button press event\n", __func__);
+	wcd9xxx_jack_report(&mbhc->button_jack, mbhc->buttons_pressed,
+			    mbhc->buttons_pressed);
+
+	pr_debug("%s: leave\n", __func__);
+	wcd9xxx_unlock_sleep(mbhc->resmgr->core);
+}
+
+static void wcd9xxx_mbhc_insert_work(struct work_struct *work)
+{
+	struct delayed_work *dwork;
+	struct wcd9xxx_mbhc *mbhc;
+	struct snd_soc_codec *codec;
+	struct wcd9xxx *core;
+
+	dwork = to_delayed_work(work);
+	mbhc = container_of(dwork, struct wcd9xxx_mbhc, mbhc_insert_dwork);
+	codec = mbhc->codec;
+	core = mbhc->resmgr->core;
+
+	pr_debug("%s:\n", __func__);
+
+	/* Turn off both HPH and MIC line schmitt triggers */
+	snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg, 0x90, 0x00);
+	snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x13, 0x00);
+	snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01, 0x00);
+	wcd9xxx_disable_irq_sync(core, WCD9XXX_IRQ_MBHC_INSERTION);
+	wcd9xxx_mbhc_detect_plug_type(mbhc);
+	wcd9xxx_unlock_sleep(core);
+}
+
+static bool wcd9xxx_mbhc_fw_validate(const struct firmware *fw)
+{
+	u32 cfg_offset;
+	struct wcd9xxx_mbhc_imped_detect_cfg *imped_cfg;
+	struct wcd9xxx_mbhc_btn_detect_cfg *btn_cfg;
+
+	if (fw->size < WCD9XXX_MBHC_CAL_MIN_SIZE)
+		return false;
+
+	/*
+	 * Previous check guarantees that there is enough fw data up
+	 * to num_btn
+	 */
+	btn_cfg = WCD9XXX_MBHC_CAL_BTN_DET_PTR(fw->data);
+	cfg_offset = (u32) ((void *) btn_cfg - (void *) fw->data);
+	if (fw->size < (cfg_offset + WCD9XXX_MBHC_CAL_BTN_SZ(btn_cfg)))
+		return false;
+
+	/*
+	 * Previous check guarantees that there is enough fw data up
+	 * to start of impedance detection configuration
+	 */
+	imped_cfg = WCD9XXX_MBHC_CAL_IMPED_DET_PTR(fw->data);
+	cfg_offset = (u32) ((void *) imped_cfg - (void *) fw->data);
+
+	if (fw->size < (cfg_offset + WCD9XXX_MBHC_CAL_IMPED_MIN_SZ))
+		return false;
+
+	if (fw->size < (cfg_offset + WCD9XXX_MBHC_CAL_IMPED_SZ(imped_cfg)))
+		return false;
+
+	return true;
+}
+
+static u16 wcd9xxx_codec_v_sta_dce(struct wcd9xxx_mbhc *mbhc,
+				   enum meas_type dce, s16 vin_mv)
+{
+	s16 diff, zero;
+	u32 mb_mv, in;
+	u16 value;
+
+	mb_mv = mbhc->mbhc_data.micb_mv;
+
+	if (mb_mv == 0) {
+		pr_err("%s: Mic Bias voltage is set to zero\n", __func__);
+		return -EINVAL;
+	}
+
+	if (dce) {
+		diff = (mbhc->mbhc_data.dce_mb) - (mbhc->mbhc_data.dce_z);
+		zero = (mbhc->mbhc_data.dce_z);
+	} else {
+		diff = (mbhc->mbhc_data.sta_mb) - (mbhc->mbhc_data.sta_z);
+		zero = (mbhc->mbhc_data.sta_z);
+	}
+	in = (u32) diff * vin_mv;
+
+	value = (u16) (in / mb_mv) + zero;
+	return value;
+}
+
+static void wcd9xxx_mbhc_calc_thres(struct wcd9xxx_mbhc *mbhc)
+{
+	struct snd_soc_codec *codec;
+	s16 btn_mv = 0, btn_delta_mv;
+	struct wcd9xxx_mbhc_btn_detect_cfg *btn_det;
+	struct wcd9xxx_mbhc_plug_type_cfg *plug_type;
+	u16 *btn_high;
+	int i;
+
+	pr_debug("%s: enter\n", __func__);
+	codec = mbhc->codec;
+	btn_det = WCD9XXX_MBHC_CAL_BTN_DET_PTR(mbhc->mbhc_cfg->calibration);
+	plug_type = WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(mbhc->mbhc_cfg->calibration);
+
+	mbhc->mbhc_data.v_ins_hu =
+	    wcd9xxx_codec_v_sta_dce(mbhc, STA, plug_type->v_hs_max);
+	mbhc->mbhc_data.v_ins_h =
+	    wcd9xxx_codec_v_sta_dce(mbhc, DCE, plug_type->v_hs_max);
+
+	mbhc->mbhc_data.v_inval_ins_low = FAKE_INS_LOW;
+	mbhc->mbhc_data.v_inval_ins_high = FAKE_INS_HIGH;
+
+	if (mbhc->mbhc_data.micb_mv != VDDIO_MICBIAS_MV) {
+		mbhc->mbhc_data.adj_v_hs_max =
+		    scale_v_micb_vddio(mbhc, plug_type->v_hs_max, true);
+		mbhc->mbhc_data.adj_v_ins_hu =
+		    wcd9xxx_codec_v_sta_dce(mbhc, STA,
+					    mbhc->mbhc_data.adj_v_hs_max);
+		mbhc->mbhc_data.adj_v_ins_h =
+		    wcd9xxx_codec_v_sta_dce(mbhc, DCE,
+					    mbhc->mbhc_data.adj_v_hs_max);
+		mbhc->mbhc_data.v_inval_ins_low =
+		    scale_v_micb_vddio(mbhc, mbhc->mbhc_data.v_inval_ins_low,
+				       false);
+		mbhc->mbhc_data.v_inval_ins_high =
+		    scale_v_micb_vddio(mbhc, mbhc->mbhc_data.v_inval_ins_high,
+				       false);
+	}
+
+	btn_high = wcd9xxx_mbhc_cal_btn_det_mp(btn_det,
+					       MBHC_BTN_DET_V_BTN_HIGH);
+	for (i = 0; i < btn_det->num_btn; i++)
+		btn_mv = btn_high[i] > btn_mv ? btn_high[i] : btn_mv;
+
+	mbhc->mbhc_data.v_b1_h = wcd9xxx_codec_v_sta_dce(mbhc, DCE, btn_mv);
+	btn_delta_mv = btn_mv + btn_det->v_btn_press_delta_sta;
+	mbhc->mbhc_data.v_b1_hu =
+	    wcd9xxx_codec_v_sta_dce(mbhc, STA, btn_delta_mv);
+
+	btn_delta_mv = btn_mv + btn_det->v_btn_press_delta_cic;
+
+	mbhc->mbhc_data.v_b1_huc =
+	    wcd9xxx_codec_v_sta_dce(mbhc, DCE, btn_delta_mv);
+
+	mbhc->mbhc_data.v_brh = mbhc->mbhc_data.v_b1_h;
+	mbhc->mbhc_data.v_brl = BUTTON_MIN;
+
+	mbhc->mbhc_data.v_no_mic =
+	    wcd9xxx_codec_v_sta_dce(mbhc, STA, plug_type->v_no_mic);
+	pr_debug("%s: leave\n", __func__);
+}
+
+static void wcd9xxx_onoff_ext_mclk(struct wcd9xxx_mbhc *mbhc, bool on)
+{
+	/*
+	 * XXX: {codec}_mclk_enable holds WCD9XXX_BCL_LOCK,
+	 * therefore wcd9xxx_onoff_ext_mclk caller SHOULDN'T hold
+	 * WCD9XXX_BCL_LOCK when it calls wcd9xxx_onoff_ext_mclk()
+	 */
+	 mbhc->mbhc_cfg->mclk_cb_fn(mbhc->codec, on, false);
+}
+
+static void wcd9xxx_correct_swch_plug(struct work_struct *work)
+{
+	struct wcd9xxx_mbhc *mbhc;
+	struct snd_soc_codec *codec;
+	enum wcd9xxx_mbhc_plug_type plug_type = PLUG_TYPE_INVALID;
+	unsigned long timeout;
+	int retry = 0, pt_gnd_mic_swap_cnt = 0;
+	bool correction = false;
+
+	pr_debug("%s: enter\n", __func__);
+
+	mbhc = container_of(work, struct wcd9xxx_mbhc, correct_plug_swch);
+	codec = mbhc->codec;
+
+	wcd9xxx_onoff_ext_mclk(mbhc, true);
+
+	/*
+	 * Keep override on during entire plug type correction work.
+	 *
+	 * This is okay under the assumption that any switch irqs which use
+	 * MBHC block cancel and sync this work so override is off again
+	 * prior to switch interrupt handler's MBHC block usage.
+	 * Also while this correction work is running, we can guarantee
+	 * DAPM doesn't use any MBHC block as this work only runs with
+	 * headphone detection.
+	 */
+	wcd9xxx_turn_onoff_override(codec, true);
+
+	timeout = jiffies + msecs_to_jiffies(HS_DETECT_PLUG_TIME_MS);
+	while (!time_after(jiffies, timeout)) {
+		++retry;
+		rmb();
+		if (mbhc->hs_detect_work_stop) {
+			pr_debug("%s: stop requested\n", __func__);
+			break;
+		}
+
+		msleep(HS_DETECT_PLUG_INERVAL_MS);
+		if (wcd9xxx_swch_level_remove(mbhc)) {
+			pr_debug("%s: Switch level is low\n", __func__);
+			break;
+		}
+
+		/* can race with removal interrupt */
+		WCD9XXX_BCL_LOCK(mbhc->resmgr);
+		plug_type = wcd9xxx_codec_get_plug_type(mbhc, true);
+		WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
+
+		pr_debug("%s: attempt(%d) current_plug(%d) new_plug(%d)\n",
+			 __func__, retry, mbhc->current_plug, plug_type);
+		if (plug_type == PLUG_TYPE_INVALID) {
+			pr_debug("Invalid plug in attempt # %d\n", retry);
+			if (!mbhc->mbhc_cfg->detect_extn_cable &&
+			    retry == NUM_ATTEMPTS_TO_REPORT &&
+			    mbhc->current_plug == PLUG_TYPE_NONE) {
+				wcd9xxx_report_plug(mbhc, 1,
+						    SND_JACK_HEADPHONE);
+			}
+		} else if (plug_type == PLUG_TYPE_HEADPHONE) {
+			pr_debug("Good headphone detected, continue polling\n");
+			if (mbhc->mbhc_cfg->detect_extn_cable) {
+				if (mbhc->current_plug != plug_type)
+					wcd9xxx_report_plug(mbhc, 1,
+							    SND_JACK_HEADPHONE);
+			} else if (mbhc->current_plug == PLUG_TYPE_NONE) {
+				wcd9xxx_report_plug(mbhc, 1,
+						    SND_JACK_HEADPHONE);
+			}
+		} else {
+			if (plug_type == PLUG_TYPE_GND_MIC_SWAP) {
+				pt_gnd_mic_swap_cnt++;
+				if (pt_gnd_mic_swap_cnt <
+				    GND_MIC_SWAP_THRESHOLD)
+					continue;
+				else if (pt_gnd_mic_swap_cnt >
+					 GND_MIC_SWAP_THRESHOLD) {
+					/*
+					 * This is due to GND/MIC switch didn't
+					 * work,  Report unsupported plug
+					 */
+				} else if (mbhc->mbhc_cfg->swap_gnd_mic) {
+					/*
+					 * if switch is toggled, check again,
+					 * otherwise report unsupported plug
+					 */
+					if (mbhc->mbhc_cfg->swap_gnd_mic(codec))
+						continue;
+				}
+			} else
+				pt_gnd_mic_swap_cnt = 0;
+
+			WCD9XXX_BCL_LOCK(mbhc->resmgr);
+			/* Turn off override */
+			wcd9xxx_turn_onoff_override(codec, false);
+			/*
+			 * The valid plug also includes PLUG_TYPE_GND_MIC_SWAP
+			 */
+			wcd9xxx_find_plug_and_report(mbhc, plug_type);
+			WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
+			pr_debug("Attempt %d found correct plug %d\n", retry,
+				 plug_type);
+			correction = true;
+			break;
+		}
+	}
+
+	/* Turn off override */
+	if (!correction)
+		wcd9xxx_turn_onoff_override(codec, false);
+
+	wcd9xxx_onoff_ext_mclk(mbhc, false);
+
+	if (mbhc->mbhc_cfg->detect_extn_cable) {
+		WCD9XXX_BCL_LOCK(mbhc->resmgr);
+		if (mbhc->current_plug == PLUG_TYPE_HEADPHONE ||
+		    mbhc->current_plug == PLUG_TYPE_GND_MIC_SWAP ||
+		    mbhc->current_plug == PLUG_TYPE_INVALID ||
+		    plug_type == PLUG_TYPE_INVALID) {
+			/* Enable removal detection */
+			wcd9xxx_cleanup_hs_polling(mbhc);
+			wcd9xxx_enable_hs_detect(mbhc, 0, 0, false);
+		}
+		WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
+	}
+	pr_debug("%s: leave current_plug(%d)\n", __func__, mbhc->current_plug);
+	/* unlock sleep */
+	wcd9xxx_unlock_sleep(mbhc->resmgr->core);
+}
+
+static void wcd9xxx_swch_irq_handler(struct wcd9xxx_mbhc *mbhc)
+{
+	bool insert;
+	bool is_removed = false;
+	struct snd_soc_codec *codec = mbhc->codec;
+
+	pr_debug("%s: enter\n", __func__);
+
+	mbhc->in_swch_irq_handler = true;
+	/* Wait here for debounce time */
+	usleep_range(SWCH_IRQ_DEBOUNCE_TIME_US, SWCH_IRQ_DEBOUNCE_TIME_US);
+
+	WCD9XXX_BCL_LOCK(mbhc->resmgr);
+
+	/* cancel pending button press */
+	if (wcd9xxx_cancel_btn_work(mbhc))
+		pr_debug("%s: button press is canceled\n", __func__);
+
+	insert = !wcd9xxx_swch_level_remove(mbhc);
+	pr_debug("%s: Current plug type %d, insert %d\n", __func__,
+		 mbhc->current_plug, insert);
+	if ((mbhc->current_plug == PLUG_TYPE_NONE) && insert) {
+		mbhc->lpi_enabled = false;
+		wmb();
+
+		/* cancel detect plug */
+		wcd9xxx_cancel_hs_detect_plug(mbhc,
+					      &mbhc->correct_plug_swch);
+
+		/* Disable Mic Bias pull down and HPH Switch to GND */
+		snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01,
+				    0x00);
+		snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x01, 0x00);
+		wcd9xxx_mbhc_detect_plug_type(mbhc);
+	} else if ((mbhc->current_plug != PLUG_TYPE_NONE) && !insert) {
+		mbhc->lpi_enabled = false;
+		wmb();
+
+		/* cancel detect plug */
+		wcd9xxx_cancel_hs_detect_plug(mbhc,
+					      &mbhc->correct_plug_swch);
+
+		if (mbhc->current_plug == PLUG_TYPE_HEADPHONE) {
+			wcd9xxx_report_plug(mbhc, 0, SND_JACK_HEADPHONE);
+			is_removed = true;
+		} else if (mbhc->current_plug == PLUG_TYPE_GND_MIC_SWAP) {
+			wcd9xxx_report_plug(mbhc, 0, SND_JACK_UNSUPPORTED);
+			is_removed = true;
+		} else if (mbhc->current_plug == PLUG_TYPE_HEADSET) {
+			wcd9xxx_pause_hs_polling(mbhc);
+			wcd9xxx_cleanup_hs_polling(mbhc);
+			wcd9xxx_report_plug(mbhc, 0, SND_JACK_HEADSET);
+			is_removed = true;
+		} else if (mbhc->current_plug == PLUG_TYPE_HIGH_HPH) {
+			wcd9xxx_report_plug(mbhc, 0, SND_JACK_LINEOUT);
+			is_removed = true;
+		}
+
+		if (is_removed) {
+			/* Enable Mic Bias pull down and HPH Switch to GND */
+			snd_soc_update_bits(codec,
+					mbhc->mbhc_bias_regs.ctl_reg, 0x01,
+					0x01);
+			snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x01,
+					0x01);
+			/* Make sure mic trigger is turned off */
+			snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg,
+					    0x01, 0x01);
+			snd_soc_update_bits(codec,
+					    mbhc->mbhc_bias_regs.mbhc_reg,
+					    0x90, 0x00);
+			/* Reset MBHC State Machine */
+			snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL,
+					    0x08, 0x08);
+			snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL,
+					    0x08, 0x00);
+			/* Turn off override */
+			wcd9xxx_turn_onoff_override(codec, false);
+		}
+	}
+
+	mbhc->in_swch_irq_handler = false;
+	WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
+	pr_debug("%s: leave\n", __func__);
+}
+
+static irqreturn_t wcd9xxx_mech_plug_detect_irq(int irq, void *data)
+{
+	int r = IRQ_HANDLED;
+	struct wcd9xxx_mbhc *mbhc = data;
+
+	pr_debug("%s: enter\n", __func__);
+	if (unlikely(wcd9xxx_lock_sleep(mbhc->resmgr->core) == false)) {
+		pr_warn("%s: failed to hold suspend\n", __func__);
+		r = IRQ_NONE;
+	} else {
+		/* Call handler */
+		wcd9xxx_swch_irq_handler(mbhc);
+		wcd9xxx_unlock_sleep(mbhc->resmgr->core);
+	}
+
+	pr_debug("%s: leave %d\n", __func__, r);
+	return r;
+}
+
+/* called under codec_resource_lock acquisition */
+static void wcd9xxx_codec_drive_v_to_micbias(struct wcd9xxx_mbhc *mbhc,
+					     int usec)
+{
+	int cfilt_k_val;
+	bool set = true;
+
+	if (mbhc->mbhc_data.micb_mv != VDDIO_MICBIAS_MV &&
+	    mbhc->mbhc_micbias_switched) {
+		pr_debug("%s: set mic V to micbias V\n", __func__);
+		snd_soc_update_bits(mbhc->codec, WCD9XXX_A_CDC_MBHC_CLK_CTL,
+				    0x2, 0x2);
+		wcd9xxx_turn_onoff_override(mbhc->codec, true);
+		while (1) {
+			cfilt_k_val =
+			    wcd9xxx_resmgr_get_k_val(mbhc->resmgr,
+						set ? mbhc->mbhc_data.micb_mv :
+						VDDIO_MICBIAS_MV);
+			snd_soc_update_bits(mbhc->codec,
+					    mbhc->mbhc_bias_regs.cfilt_val,
+					    0xFC, (cfilt_k_val << 2));
+			if (!set)
+				break;
+			usleep_range(usec, usec);
+			set = false;
+		}
+		wcd9xxx_turn_onoff_override(mbhc->codec, false);
+	}
+}
+
+static int wcd9xxx_is_fake_press(struct wcd9xxx_mbhc *mbhc)
+{
+	int i;
+	int r = 0;
+	const int dces = NUM_DCE_PLUG_DETECT;
+	s16 mb_v, v_ins_hu, v_ins_h;
+
+	v_ins_hu = wcd9xxx_get_current_v_ins(mbhc, true);
+	v_ins_h = wcd9xxx_get_current_v_ins(mbhc, false);
+
+	for (i = 0; i < dces; i++) {
+		usleep_range(10000, 10000);
+		if (i == 0) {
+			mb_v = wcd9xxx_codec_sta_dce(mbhc, 0, true);
+			pr_debug("%s: STA[0]: %d,%d\n", __func__, mb_v,
+				 wcd9xxx_codec_sta_dce_v(mbhc, 0, mb_v));
+			if (mb_v < (s16)mbhc->mbhc_data.v_b1_hu ||
+			    mb_v > v_ins_hu) {
+				r = 1;
+				break;
+			}
+		} else {
+			mb_v = wcd9xxx_codec_sta_dce(mbhc, 1, true);
+			pr_debug("%s: DCE[%d]: %d,%d\n", __func__, i, mb_v,
+				 wcd9xxx_codec_sta_dce_v(mbhc, 1, mb_v));
+			if (mb_v < (s16)mbhc->mbhc_data.v_b1_h ||
+			    mb_v > v_ins_h) {
+				r = 1;
+				break;
+			}
+		}
+	}
+
+	return r;
+}
+
+/* called under codec_resource_lock acquisition */
+static int wcd9xxx_determine_button(const struct wcd9xxx_mbhc *mbhc,
+				  const s32 micmv)
+{
+	s16 *v_btn_low, *v_btn_high;
+	struct wcd9xxx_mbhc_btn_detect_cfg *btn_det;
+	int i, btn = -1;
+
+	btn_det = WCD9XXX_MBHC_CAL_BTN_DET_PTR(mbhc->mbhc_cfg->calibration);
+	v_btn_low = wcd9xxx_mbhc_cal_btn_det_mp(btn_det,
+						MBHC_BTN_DET_V_BTN_LOW);
+	v_btn_high = wcd9xxx_mbhc_cal_btn_det_mp(btn_det,
+						 MBHC_BTN_DET_V_BTN_HIGH);
+
+	for (i = 0; i < btn_det->num_btn; i++) {
+		if ((v_btn_low[i] <= micmv) && (v_btn_high[i] >= micmv)) {
+			btn = i;
+			break;
+		}
+	}
+
+	if (btn == -1)
+		pr_debug("%s: couldn't find button number for mic mv %d\n",
+			 __func__, micmv);
+
+	return btn;
+}
+
+static int wcd9xxx_get_button_mask(const int btn)
+{
+	int mask = 0;
+	switch (btn) {
+	case 0:
+		mask = SND_JACK_BTN_0;
+		break;
+	case 1:
+		mask = SND_JACK_BTN_1;
+		break;
+	case 2:
+		mask = SND_JACK_BTN_2;
+		break;
+	case 3:
+		mask = SND_JACK_BTN_3;
+		break;
+	case 4:
+		mask = SND_JACK_BTN_4;
+		break;
+	case 5:
+		mask = SND_JACK_BTN_5;
+		break;
+	case 6:
+		mask = SND_JACK_BTN_6;
+		break;
+	case 7:
+		mask = SND_JACK_BTN_7;
+		break;
+	}
+	return mask;
+}
+
+irqreturn_t wcd9xxx_dce_handler(int irq, void *data)
+{
+	int i, mask;
+	short dce, sta;
+	s32 mv, mv_s, stamv_s;
+	bool vddio;
+	u8 mbhc_status;
+	int btn = -1, meas = 0;
+	struct wcd9xxx_mbhc *mbhc = data;
+	const struct wcd9xxx_mbhc_btn_detect_cfg *d =
+	    WCD9XXX_MBHC_CAL_BTN_DET_PTR(mbhc->mbhc_cfg->calibration);
+	short btnmeas[d->n_btn_meas + 1];
+	struct snd_soc_codec *codec = mbhc->codec;
+	struct wcd9xxx *core = mbhc->resmgr->core;
+	int n_btn_meas = d->n_btn_meas;
+
+	pr_debug("%s: enter\n", __func__);
+
+	WCD9XXX_BCL_LOCK(mbhc->resmgr);
+	mbhc_status = snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B1_STATUS) & 0x3E;
+
+	if (mbhc->mbhc_state == MBHC_STATE_POTENTIAL_RECOVERY) {
+		pr_debug("%s: mbhc is being recovered, skip button press\n",
+			 __func__);
+		goto done;
+	}
+
+	mbhc->mbhc_state = MBHC_STATE_POTENTIAL;
+
+	if (!mbhc->polling_active) {
+		pr_warn("%s: mbhc polling is not active, skip button press\n",
+			__func__);
+		goto done;
+	}
+
+	dce = wcd9xxx_read_dce_result(codec);
+	mv = wcd9xxx_codec_sta_dce_v(mbhc, 1, dce);
+
+	/* If switch nterrupt already kicked in, ignore button press */
+	if (mbhc->in_swch_irq_handler) {
+		pr_debug("%s: Swtich level changed, ignore button press\n",
+			 __func__);
+		btn = -1;
+		goto done;
+	}
+
+	/* Measure scaled HW DCE */
+	vddio = (mbhc->mbhc_data.micb_mv != VDDIO_MICBIAS_MV &&
+		 mbhc->mbhc_micbias_switched);
+	mv_s = vddio ? scale_v_micb_vddio(mbhc, mv, false) : mv;
+
+	/* Measure scaled HW STA */
+	sta = wcd9xxx_read_sta_result(codec);
+	stamv_s = wcd9xxx_codec_sta_dce_v(mbhc, 0, sta);
+	if (vddio)
+		stamv_s = scale_v_micb_vddio(mbhc, stamv_s, false);
+	if (mbhc_status != STATUS_REL_DETECTION) {
+		if (mbhc->mbhc_last_resume &&
+		    !time_after(jiffies, mbhc->mbhc_last_resume + HZ)) {
+			pr_debug("%s: Button is released after resume\n",
+				__func__);
+			n_btn_meas = 0;
+		} else {
+			pr_debug("%s: Button is released without resume",
+				 __func__);
+			btn = wcd9xxx_determine_button(mbhc, mv_s);
+			if (btn != wcd9xxx_determine_button(mbhc, stamv_s))
+				btn = -1;
+			goto done;
+		}
+	}
+
+	pr_debug("%s: Meas HW - STA 0x%x,%d,%d\n", __func__,
+		 sta & 0xFFFF, wcd9xxx_codec_sta_dce_v(mbhc, 0, sta), stamv_s);
+
+	/* determine pressed button */
+	btnmeas[meas++] = wcd9xxx_determine_button(mbhc, mv_s);
+	pr_debug("%s: Meas HW - DCE 0x%x,%d,%d button %d\n", __func__,
+		 dce & 0xFFFF, mv, mv_s, btnmeas[meas - 1]);
+	if (n_btn_meas == 0)
+		btn = btnmeas[0];
+	for (; ((d->n_btn_meas) && (meas < (d->n_btn_meas + 1))); meas++) {
+		dce = wcd9xxx_codec_sta_dce(mbhc, 1, false);
+		mv = wcd9xxx_codec_sta_dce_v(mbhc, 1, dce);
+		mv_s = vddio ? scale_v_micb_vddio(mbhc, mv, false) : mv;
+
+		btnmeas[meas] = wcd9xxx_determine_button(mbhc, mv_s);
+		pr_debug("%s: Meas %d - DCE 0x%x,%d,%d button %d\n",
+			 __func__, meas, dce & 0xFFFF, mv, mv_s, btnmeas[meas]);
+		/*
+		 * if large enough measurements are collected,
+		 * start to check if last all n_btn_con measurements were
+		 * in same button low/high range
+		 */
+		if (meas + 1 >= d->n_btn_con) {
+			for (i = 0; i < d->n_btn_con; i++)
+				if ((btnmeas[meas] < 0) ||
+				    (btnmeas[meas] != btnmeas[meas - i]))
+					break;
+			if (i == d->n_btn_con) {
+				/* button pressed */
+				btn = btnmeas[meas];
+				break;
+			} else if ((n_btn_meas - meas) < (d->n_btn_con - 1)) {
+				/*
+				 * if left measurements are less than n_btn_con,
+				 * it's impossible to find button number
+				 */
+				break;
+			}
+		}
+	}
+
+	if (btn >= 0) {
+		if (mbhc->in_swch_irq_handler) {
+			pr_debug(
+			"%s: Switch irq triggered, ignore button press\n",
+			__func__);
+			goto done;
+		}
+		mask = wcd9xxx_get_button_mask(btn);
+		mbhc->buttons_pressed |= mask;
+		wcd9xxx_lock_sleep(core);
+		if (schedule_delayed_work(&mbhc->mbhc_btn_dwork,
+					  msecs_to_jiffies(400)) == 0) {
+			WARN(1, "Button pressed twice without release event\n");
+			wcd9xxx_unlock_sleep(core);
+		}
+	} else {
+		pr_debug("%s: bogus button press, too short press?\n",
+			 __func__);
+	}
+
+ done:
+	pr_debug("%s: leave\n", __func__);
+	WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t wcd9xxx_release_handler(int irq, void *data)
+{
+	int ret;
+	struct wcd9xxx_mbhc *mbhc = data;
+
+	pr_debug("%s: enter\n", __func__);
+	WCD9XXX_BCL_LOCK(mbhc->resmgr);
+	mbhc->mbhc_state = MBHC_STATE_RELEASE;
+
+	wcd9xxx_codec_drive_v_to_micbias(mbhc, 10000);
+
+	if (mbhc->buttons_pressed & WCD9XXX_JACK_BUTTON_MASK) {
+		ret = wcd9xxx_cancel_btn_work(mbhc);
+		if (ret == 0) {
+			pr_debug("%s: Reporting long button release event\n",
+				 __func__);
+			wcd9xxx_jack_report(&mbhc->button_jack, 0,
+					    mbhc->buttons_pressed);
+		} else {
+			if (wcd9xxx_is_fake_press(mbhc)) {
+				pr_debug("%s: Fake button press interrupt\n",
+					 __func__);
+			} else {
+				if (mbhc->in_swch_irq_handler) {
+					pr_debug("%s: Switch irq kicked in, ignore\n",
+						 __func__);
+				} else {
+					pr_debug("%s: Reporting btn press\n",
+						 __func__);
+					wcd9xxx_jack_report(&mbhc->button_jack,
+							 mbhc->buttons_pressed,
+							 mbhc->buttons_pressed);
+					pr_debug("%s: Reporting btn release\n",
+						 __func__);
+					wcd9xxx_jack_report(&mbhc->button_jack,
+						      0, mbhc->buttons_pressed);
+				}
+			}
+		}
+
+		mbhc->buttons_pressed &= ~WCD9XXX_JACK_BUTTON_MASK;
+	}
+
+	wcd9xxx_calibrate_hs_polling(mbhc);
+
+	msleep(SWCH_REL_DEBOUNCE_TIME_MS);
+	wcd9xxx_start_hs_polling(mbhc);
+
+	pr_debug("%s: leave\n", __func__);
+	WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t wcd9xxx_hphl_ocp_irq(int irq, void *data)
+{
+	struct wcd9xxx_mbhc *mbhc = data;
+	struct snd_soc_codec *codec;
+
+	pr_info("%s: received HPHL OCP irq\n", __func__);
+
+	if (mbhc) {
+		codec = mbhc->codec;
+		if (mbhc->hphlocp_cnt++ < OCP_ATTEMPT) {
+			pr_info("%s: retry\n", __func__);
+			snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_OCP_CTL,
+					    0x10, 0x00);
+			snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_OCP_CTL,
+					    0x10, 0x10);
+		} else {
+			wcd9xxx_disable_irq(codec->control_data,
+					  WCD9XXX_IRQ_HPH_PA_OCPL_FAULT);
+			mbhc->hphlocp_cnt = 0;
+			mbhc->hph_status |= SND_JACK_OC_HPHL;
+			wcd9xxx_jack_report(&mbhc->headset_jack,
+					    mbhc->hph_status,
+					    WCD9XXX_JACK_MASK);
+		}
+	} else {
+		pr_err("%s: Bad wcd9xxx private data\n", __func__);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t wcd9xxx_hphr_ocp_irq(int irq, void *data)
+{
+	struct wcd9xxx_mbhc *mbhc = data;
+	struct snd_soc_codec *codec;
+
+	pr_info("%s: received HPHR OCP irq\n", __func__);
+	codec = mbhc->codec;
+	if (mbhc->hphrocp_cnt++ < OCP_ATTEMPT) {
+		pr_info("%s: retry\n", __func__);
+		snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_OCP_CTL, 0x10,
+				    0x00);
+		snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_OCP_CTL, 0x10,
+				    0x10);
+	} else {
+		wcd9xxx_disable_irq(mbhc->resmgr->core,
+				    WCD9XXX_IRQ_HPH_PA_OCPR_FAULT);
+		mbhc->hphrocp_cnt = 0;
+		mbhc->hph_status |= SND_JACK_OC_HPHR;
+		wcd9xxx_jack_report(&mbhc->headset_jack,
+				    mbhc->hph_status, WCD9XXX_JACK_MASK);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static int wcd9xxx_acdb_mclk_index(const int rate)
+{
+	if (rate == MCLK_RATE_12288KHZ)
+		return 0;
+	else if (rate == MCLK_RATE_9600KHZ)
+		return 1;
+	else {
+		BUG_ON(1);
+		return -EINVAL;
+	}
+}
+
+static void wcd9xxx_update_mbhc_clk_rate(struct wcd9xxx_mbhc *mbhc, u32 rate)
+{
+	u32 dce_wait, sta_wait;
+	u8 ncic, nmeas, navg;
+	void *calibration;
+	u8 *n_cic, *n_ready;
+	struct wcd9xxx_mbhc_btn_detect_cfg *btn_det;
+	u8 npoll = 4, nbounce_wait = 30;
+	struct snd_soc_codec *codec = mbhc->codec;
+	int idx = wcd9xxx_acdb_mclk_index(rate);
+	int idxmclk = wcd9xxx_acdb_mclk_index(mbhc->mbhc_cfg->mclk_rate);
+
+	pr_debug("%s: Updating clock rate dependents, rate = %u\n", __func__,
+		 rate);
+	calibration = mbhc->mbhc_cfg->calibration;
+
+	/*
+	 * First compute the DCE / STA wait times depending on tunable
+	 * parameters. The value is computed in microseconds
+	 */
+	btn_det = WCD9XXX_MBHC_CAL_BTN_DET_PTR(calibration);
+	n_ready = wcd9xxx_mbhc_cal_btn_det_mp(btn_det, MBHC_BTN_DET_N_READY);
+	n_cic = wcd9xxx_mbhc_cal_btn_det_mp(btn_det, MBHC_BTN_DET_N_CIC);
+	nmeas = WCD9XXX_MBHC_CAL_BTN_DET_PTR(calibration)->n_meas;
+	navg = WCD9XXX_MBHC_CAL_GENERAL_PTR(calibration)->mbhc_navg;
+
+	/* ncic stays with the same what we had during calibration */
+	ncic = n_cic[idxmclk];
+	dce_wait = (1000 * 512 * ncic * (nmeas + 1)) / (rate / 1000);
+	sta_wait = (1000 * 128 * (navg + 1)) / (rate / 1000);
+	mbhc->mbhc_data.t_dce = dce_wait;
+	mbhc->mbhc_data.t_sta = sta_wait;
+	mbhc->mbhc_data.t_sta_dce = ((1000 * 256) / (rate / 1000) *
+				     n_ready[idx]) + 10;
+
+	snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_TIMER_B1_CTL, n_ready[idx]);
+	snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_TIMER_B6_CTL, ncic);
+
+	if (rate == MCLK_RATE_12288KHZ) {
+		npoll = 4;
+		nbounce_wait = 30;
+	} else if (rate == MCLK_RATE_9600KHZ) {
+		npoll = 3;
+		nbounce_wait = 23;
+	}
+
+	snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_TIMER_B2_CTL, npoll);
+	snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_TIMER_B3_CTL, nbounce_wait);
+	pr_debug("%s: leave\n", __func__);
+}
+
+static void wcd9xxx_mbhc_cal(struct wcd9xxx_mbhc *mbhc)
+{
+	u8 cfilt_mode, bg_mode;
+	struct snd_soc_codec *codec = mbhc->codec;
+
+	pr_debug("%s: enter\n", __func__);
+	wcd9xxx_disable_irq(codec->control_data, WCD9XXX_IRQ_MBHC_POTENTIAL);
+	wcd9xxx_turn_onoff_rel_detection(codec, false);
+
+	/* t_dce and t_sta are updated by wcd9xxx_update_mbhc_clk_rate() */
+	WARN_ON(!mbhc->mbhc_data.t_dce);
+	WARN_ON(!mbhc->mbhc_data.t_sta);
+
+	/*
+	 * LDOH and CFILT are already configured during pdata handling.
+	 * Only need to make sure CFILT and bandgap are in Fast mode.
+	 * Need to restore defaults once calculation is done.
+	 */
+	cfilt_mode = snd_soc_read(codec, mbhc->mbhc_bias_regs.cfilt_ctl);
+	snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.cfilt_ctl, 0x40, 0x00);
+	bg_mode = snd_soc_update_bits(codec, WCD9XXX_A_BIAS_CENTRAL_BG_CTL,
+				      0x02, 0x02);
+
+	/*
+	 * Micbias, CFILT, LDOH, MBHC MUX mode settings
+	 * to perform ADC calibration
+	 */
+	snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x60,
+			    mbhc->mbhc_cfg->micbias << 5);
+	snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01, 0x00);
+	snd_soc_update_bits(codec, WCD9XXX_A_LDO_H_MODE_1, 0x60, 0x60);
+	snd_soc_write(codec, WCD9XXX_A_TX_7_MBHC_TEST_CTL, 0x78);
+	snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x04, 0x04);
+
+	/* DCE measurement for 0 volts */
+	snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x0A);
+	snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x04);
+	snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x02);
+	snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x81);
+	usleep_range(100, 100);
+	snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x04);
+	usleep_range(mbhc->mbhc_data.t_dce, mbhc->mbhc_data.t_dce);
+	mbhc->mbhc_data.dce_z = wcd9xxx_read_dce_result(codec);
+
+	/* DCE measurment for MB voltage */
+	snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x0A);
+	snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x02);
+	snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x82);
+	usleep_range(100, 100);
+	snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x04);
+	usleep_range(mbhc->mbhc_data.t_dce, mbhc->mbhc_data.t_dce);
+	mbhc->mbhc_data.dce_mb = wcd9xxx_read_dce_result(codec);
+
+	/* STA measuremnt for 0 volts */
+	snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x0A);
+	snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x02);
+	snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x02);
+	snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x81);
+	usleep_range(100, 100);
+	snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x02);
+	usleep_range(mbhc->mbhc_data.t_sta, mbhc->mbhc_data.t_sta);
+	mbhc->mbhc_data.sta_z = wcd9xxx_read_sta_result(codec);
+
+	/* STA Measurement for MB Voltage */
+	snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x82);
+	usleep_range(100, 100);
+	snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x02);
+	usleep_range(mbhc->mbhc_data.t_sta, mbhc->mbhc_data.t_sta);
+	mbhc->mbhc_data.sta_mb = wcd9xxx_read_sta_result(codec);
+
+	/* Restore default settings. */
+	snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x04, 0x00);
+	snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.cfilt_ctl, 0x40,
+			    cfilt_mode);
+	snd_soc_update_bits(codec, WCD9XXX_A_BIAS_CENTRAL_BG_CTL, 0x02,
+			    bg_mode);
+
+	snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x84);
+	usleep_range(100, 100);
+
+	wcd9xxx_enable_irq(codec->control_data, WCD9XXX_IRQ_MBHC_POTENTIAL);
+	wcd9xxx_turn_onoff_rel_detection(codec, true);
+	pr_debug("%s: leave\n", __func__);
+}
+
+static void wcd9xxx_mbhc_setup(struct wcd9xxx_mbhc *mbhc)
+{
+	int n;
+	u8 *gain;
+	struct wcd9xxx_mbhc_general_cfg *generic;
+	struct wcd9xxx_mbhc_btn_detect_cfg *btn_det;
+	struct snd_soc_codec *codec = mbhc->codec;
+	const int idx = wcd9xxx_acdb_mclk_index(mbhc->mbhc_cfg->mclk_rate);
+
+	pr_debug("%s: enter\n", __func__);
+	generic = WCD9XXX_MBHC_CAL_GENERAL_PTR(mbhc->mbhc_cfg->calibration);
+	btn_det = WCD9XXX_MBHC_CAL_BTN_DET_PTR(mbhc->mbhc_cfg->calibration);
+
+	for (n = 0; n < 8; n++) {
+		snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_FIR_B1_CFG,
+				    0x07, n);
+		snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_FIR_B2_CFG,
+			      btn_det->c[n]);
+	}
+
+	snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B2_CTL, 0x07,
+			    btn_det->nc);
+
+	snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_TIMER_B4_CTL, 0x70,
+			    generic->mbhc_nsa << 4);
+
+	snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_TIMER_B4_CTL, 0x0F,
+			    btn_det->n_meas);
+
+	snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_TIMER_B5_CTL,
+		      generic->mbhc_navg);
+
+	snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x80, 0x80);
+
+	snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x78,
+			    btn_det->mbhc_nsc << 3);
+
+	snd_soc_update_bits(codec, mbhc->resmgr->reg_addr->micb_4_mbhc, 0x03,
+			    MBHC_MICBIAS2);
+
+	snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x02, 0x02);
+
+	snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_2, 0xF0, 0xF0);
+
+	gain = wcd9xxx_mbhc_cal_btn_det_mp(btn_det, MBHC_BTN_DET_GAIN);
+	snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B2_CTL, 0x78,
+			    gain[idx] << 3);
+
+	pr_debug("%s: leave\n", __func__);
+}
+
+static int wcd9xxx_setup_jack_detect_irq(struct wcd9xxx_mbhc *mbhc)
+{
+	int ret = 0;
+	void *core = mbhc->resmgr->core;
+
+	if (mbhc->mbhc_cfg->gpio) {
+		ret = request_threaded_irq(mbhc->mbhc_cfg->gpio_irq, NULL,
+					   wcd9xxx_mech_plug_detect_irq,
+					   (IRQF_TRIGGER_RISING |
+					    IRQF_TRIGGER_FALLING |
+					    IRQF_DISABLED),
+					   "headset detect", mbhc);
+		if (ret) {
+			pr_err("%s: Failed to request gpio irq %d\n", __func__,
+			       mbhc->mbhc_cfg->gpio_irq);
+		} else {
+			ret = enable_irq_wake(mbhc->mbhc_cfg->gpio_irq);
+			if (ret)
+				pr_err("%s: Failed to enable wake up irq %d\n",
+				       __func__, mbhc->mbhc_cfg->gpio_irq);
+		}
+	} else if (mbhc->mbhc_cfg->insert_detect) {
+		/* Enable HPHL_10K_SW */
+		snd_soc_update_bits(mbhc->codec, WCD9XXX_A_RX_HPH_OCP_CTL,
+				    1 << 1, 1 << 1);
+		ret = wcd9xxx_request_irq(core, WCD9XXX_IRQ_MBHC_JACK_SWITCH,
+					  wcd9xxx_mech_plug_detect_irq,
+					  "Jack Detect",
+					  mbhc);
+		if (ret)
+			pr_err("%s: Failed to request insert detect irq %d\n",
+			       __func__, WCD9XXX_IRQ_MBHC_JACK_SWITCH);
+	}
+
+	return ret;
+}
+
+static int wcd9xxx_init_and_calibrate(struct wcd9xxx_mbhc *mbhc)
+{
+	int ret = 0;
+	struct snd_soc_codec *codec = mbhc->codec;
+
+	pr_debug("%s: enter\n", __func__);
+
+	/* Enable MCLK during calibration */
+	wcd9xxx_onoff_ext_mclk(mbhc, true);
+	wcd9xxx_mbhc_setup(mbhc);
+	wcd9xxx_mbhc_cal(mbhc);
+	wcd9xxx_mbhc_calc_thres(mbhc);
+	wcd9xxx_onoff_ext_mclk(mbhc, false);
+	wcd9xxx_calibrate_hs_polling(mbhc);
+
+	/* Enable Mic Bias pull down and HPH Switch to GND */
+	snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01, 0x01);
+	snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x01, 0x01);
+	INIT_WORK(&mbhc->correct_plug_swch, wcd9xxx_correct_swch_plug);
+
+	if (!IS_ERR_VALUE(ret)) {
+		snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_OCP_CTL, 0x10,
+				    0x10);
+		wcd9xxx_enable_irq(codec->control_data,
+				   WCD9XXX_IRQ_HPH_PA_OCPL_FAULT);
+		wcd9xxx_enable_irq(codec->control_data,
+				   WCD9XXX_IRQ_HPH_PA_OCPR_FAULT);
+
+		/* Initialize mechanical mbhc */
+		ret = wcd9xxx_setup_jack_detect_irq(mbhc);
+
+		if (!ret && mbhc->mbhc_cfg->gpio) {
+			/* Requested with IRQF_DISABLED */
+			enable_irq(mbhc->mbhc_cfg->gpio_irq);
+
+			/* Bootup time detection */
+			wcd9xxx_swch_irq_handler(mbhc);
+		} else if (!ret && mbhc->mbhc_cfg->insert_detect) {
+			pr_debug("%s: Setting up codec own insert detection\n",
+				 __func__);
+			/* Setup for insertion detection */
+			wcd9xxx_insert_detect_setup(mbhc, true);
+		}
+	}
+
+	pr_debug("%s: leave\n", __func__);
+
+	return ret;
+}
+
+static void wcd9xxx_mbhc_fw_read(struct work_struct *work)
+{
+	struct delayed_work *dwork;
+	struct wcd9xxx_mbhc *mbhc;
+	struct snd_soc_codec *codec;
+	const struct firmware *fw;
+	int ret = -1, retry = 0;
+
+	dwork = to_delayed_work(work);
+	mbhc = container_of(dwork, struct wcd9xxx_mbhc, mbhc_firmware_dwork);
+	codec = mbhc->codec;
+
+	while (retry < FW_READ_ATTEMPTS) {
+		retry++;
+		pr_info("%s:Attempt %d to request MBHC firmware\n",
+			__func__, retry);
+		ret = request_firmware(&fw, "wcd9320/wcd9320_mbhc.bin",
+				       codec->dev);
+
+		if (ret != 0) {
+			usleep_range(FW_READ_TIMEOUT, FW_READ_TIMEOUT);
+		} else {
+			pr_info("%s: MBHC Firmware read succesful\n", __func__);
+			break;
+		}
+	}
+
+	if (ret != 0) {
+		pr_err("%s: Cannot load MBHC firmware use default cal\n",
+		       __func__);
+	} else if (wcd9xxx_mbhc_fw_validate(fw) == false) {
+		pr_err("%s: Invalid MBHC cal data size use default cal\n",
+		       __func__);
+		release_firmware(fw);
+	} else {
+		mbhc->mbhc_cfg->calibration = (void *)fw->data;
+		mbhc->mbhc_fw = fw;
+	}
+
+	(void) wcd9xxx_init_and_calibrate(mbhc);
+}
+
+#ifdef CONFIG_DEBUG_FS
+ssize_t codec_mbhc_debug_read(struct file *file, char __user *buf,
+			      size_t count, loff_t *pos)
+{
+	const int size = 768;
+	char buffer[size];
+	int n = 0;
+	struct wcd9xxx_mbhc *mbhc = file->private_data;
+	const struct mbhc_internal_cal_data *p = &mbhc->mbhc_data;
+	const s16 v_ins_hu_cur = wcd9xxx_get_current_v_ins(mbhc, true);
+	const s16 v_ins_h_cur = wcd9xxx_get_current_v_ins(mbhc, false);
+
+	n = scnprintf(buffer, size - n, "dce_z = %x(%dmv)\n",  p->dce_z,
+		      wcd9xxx_codec_sta_dce_v(mbhc, 1, p->dce_z));
+	n += scnprintf(buffer + n, size - n, "dce_mb = %x(%dmv)\n",
+		       p->dce_mb, wcd9xxx_codec_sta_dce_v(mbhc, 1, p->dce_mb));
+	n += scnprintf(buffer + n, size - n, "sta_z = %x(%dmv)\n",
+		       p->sta_z, wcd9xxx_codec_sta_dce_v(mbhc, 0, p->sta_z));
+	n += scnprintf(buffer + n, size - n, "sta_mb = %x(%dmv)\n",
+		       p->sta_mb, wcd9xxx_codec_sta_dce_v(mbhc, 0, p->sta_mb));
+	n += scnprintf(buffer + n, size - n, "t_dce = %x\n",  p->t_dce);
+	n += scnprintf(buffer + n, size - n, "t_sta = %x\n",  p->t_sta);
+	n += scnprintf(buffer + n, size - n, "micb_mv = %dmv\n",
+		       p->micb_mv);
+	n += scnprintf(buffer + n, size - n, "v_ins_hu = %x(%dmv)%s\n",
+		       p->v_ins_hu,
+		       wcd9xxx_codec_sta_dce_v(mbhc, 0, p->v_ins_hu),
+		       p->v_ins_hu == v_ins_hu_cur ? "*" : "");
+	n += scnprintf(buffer + n, size - n, "v_ins_h = %x(%dmv)%s\n",
+		       p->v_ins_h, wcd9xxx_codec_sta_dce_v(mbhc, 1, p->v_ins_h),
+		       p->v_ins_h == v_ins_h_cur ? "*" : "");
+	n += scnprintf(buffer + n, size - n, "adj_v_ins_hu = %x(%dmv)%s\n",
+		       p->adj_v_ins_hu,
+		       wcd9xxx_codec_sta_dce_v(mbhc, 0, p->adj_v_ins_hu),
+		       p->adj_v_ins_hu == v_ins_hu_cur ? "*" : "");
+	n += scnprintf(buffer + n, size - n, "adj_v_ins_h = %x(%dmv)%s\n",
+		       p->adj_v_ins_h,
+		       wcd9xxx_codec_sta_dce_v(mbhc, 1, p->adj_v_ins_h),
+		       p->adj_v_ins_h == v_ins_h_cur ? "*" : "");
+	n += scnprintf(buffer + n, size - n, "v_b1_hu = %x(%dmv)\n",
+		       p->v_b1_hu,
+		       wcd9xxx_codec_sta_dce_v(mbhc, 0, p->v_b1_hu));
+	n += scnprintf(buffer + n, size - n, "v_b1_h = %x(%dmv)\n",
+		       p->v_b1_h, wcd9xxx_codec_sta_dce_v(mbhc, 1, p->v_b1_h));
+	n += scnprintf(buffer + n, size - n, "v_b1_huc = %x(%dmv)\n",
+		       p->v_b1_huc,
+		       wcd9xxx_codec_sta_dce_v(mbhc, 1, p->v_b1_huc));
+	n += scnprintf(buffer + n, size - n, "v_brh = %x(%dmv)\n",
+		       p->v_brh, wcd9xxx_codec_sta_dce_v(mbhc, 1, p->v_brh));
+	n += scnprintf(buffer + n, size - n, "v_brl = %x(%dmv)\n",  p->v_brl,
+		       wcd9xxx_codec_sta_dce_v(mbhc, 0, p->v_brl));
+	n += scnprintf(buffer + n, size - n, "v_no_mic = %x(%dmv)\n",
+		       p->v_no_mic,
+		       wcd9xxx_codec_sta_dce_v(mbhc, 0, p->v_no_mic));
+	n += scnprintf(buffer + n, size - n, "v_inval_ins_low = %d\n",
+		       p->v_inval_ins_low);
+	n += scnprintf(buffer + n, size - n, "v_inval_ins_high = %d\n",
+		       p->v_inval_ins_high);
+	n += scnprintf(buffer + n, size - n, "Insert detect insert = %d\n",
+		       !wcd9xxx_swch_level_remove(mbhc));
+	buffer[n] = 0;
+
+	return simple_read_from_buffer(buf, count, pos, buffer, n);
+}
+
+static int codec_debug_open(struct inode *inode, struct file *file)
+{
+	file->private_data = inode->i_private;
+	return 0;
+}
+
+static ssize_t codec_debug_write(struct file *filp,
+				 const char __user *ubuf, size_t cnt,
+				 loff_t *ppos)
+{
+	char lbuf[32];
+	char *buf;
+	int rc;
+	struct wcd9xxx_mbhc *mbhc = filp->private_data;
+
+	if (cnt > sizeof(lbuf) - 1)
+		return -EINVAL;
+
+	rc = copy_from_user(lbuf, ubuf, cnt);
+	if (rc)
+		return -EFAULT;
+
+	lbuf[cnt] = '\0';
+	buf = (char *)lbuf;
+	mbhc->no_mic_headset_override = (*strsep(&buf, " ") == '0') ?
+					     false : true;
+	return rc;
+}
+
+static const struct file_operations mbhc_trrs_debug_ops = {
+	.open = codec_debug_open,
+	.write = codec_debug_write,
+};
+
+static const struct file_operations mbhc_debug_ops = {
+	.open = codec_debug_open,
+	.read = codec_mbhc_debug_read,
+};
+
+static void wcd9xxx_init_debugfs(struct wcd9xxx_mbhc *mbhc)
+{
+	mbhc->debugfs_poke =
+	    debugfs_create_file("TRRS", S_IFREG | S_IRUGO, NULL, mbhc,
+				&mbhc_trrs_debug_ops);
+	mbhc->debugfs_mbhc =
+	    debugfs_create_file("wcd9xxx_mbhc", S_IFREG | S_IRUGO,
+				NULL, mbhc, &mbhc_debug_ops);
+}
+
+static void wcd9xxx_cleanup_debugfs(struct wcd9xxx_mbhc *mbhc)
+{
+	debugfs_remove(mbhc->debugfs_poke);
+	debugfs_remove(mbhc->debugfs_mbhc);
+}
+#else
+static void wcd9xxx_init_debugfs(struct wcd9xxx_mbhc *mbhc)
+{
+}
+
+static void wcd9xxx_cleanup_debugfs(struct wcd9xxx_mbhc *mbhc)
+{
+}
+#endif
+
+int wcd9xxx_mbhc_start(struct wcd9xxx_mbhc *mbhc,
+		       struct wcd9xxx_mbhc_config *mbhc_cfg)
+{
+	int rc = 0;
+	struct snd_soc_codec *codec = mbhc->codec;
+
+	pr_debug("%s: enter\n", __func__);
+
+	if (!codec) {
+		pr_err("%s: no codec\n", __func__);
+		return -EINVAL;
+	}
+
+	if (mbhc_cfg->mclk_rate != MCLK_RATE_12288KHZ &&
+	    mbhc_cfg->mclk_rate != MCLK_RATE_9600KHZ) {
+		pr_err("Error: unsupported clock rate %d\n",
+		       mbhc_cfg->mclk_rate);
+		return -EINVAL;
+	}
+
+	/* Save mbhc config */
+	mbhc->mbhc_cfg = mbhc_cfg;
+
+	/* Get HW specific mbhc registers' address */
+	wcd9xxx_get_mbhc_micbias_regs(mbhc, &mbhc->mbhc_bias_regs);
+
+	/* Put CFILT in fast mode by default */
+	snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.cfilt_ctl,
+			    0x40, WCD9XXX_CFILT_FAST_MODE);
+
+	if (!mbhc->mbhc_cfg->read_fw_bin)
+		rc = wcd9xxx_init_and_calibrate(mbhc);
+	else
+		schedule_delayed_work(&mbhc->mbhc_firmware_dwork,
+				      usecs_to_jiffies(FW_READ_TIMEOUT));
+
+	pr_debug("%s: leave %d\n", __func__, rc);
+	return rc;
+}
+EXPORT_SYMBOL_GPL(wcd9xxx_mbhc_start);
+
+static enum wcd9xxx_micbias_num
+wcd9xxx_event_to_micbias(const enum wcd9xxx_notify_event event)
+{
+	enum wcd9xxx_micbias_num ret;
+	switch (event) {
+	case WCD9XXX_EVENT_PRE_MICBIAS_1_ON:
+		ret = MBHC_MICBIAS1;
+	case WCD9XXX_EVENT_PRE_MICBIAS_2_ON:
+		ret = MBHC_MICBIAS2;
+	case WCD9XXX_EVENT_PRE_MICBIAS_3_ON:
+		ret = MBHC_MICBIAS3;
+	case WCD9XXX_EVENT_PRE_MICBIAS_4_ON:
+		ret = MBHC_MICBIAS4;
+	default:
+		ret = MBHC_MICBIAS_INVALID;
+	}
+	return ret;
+}
+
+static int wcd9xxx_event_to_cfilt(const enum wcd9xxx_notify_event event)
+{
+	int ret;
+	switch (event) {
+	case WCD9XXX_EVENT_PRE_CFILT_1_OFF:
+	case WCD9XXX_EVENT_POST_CFILT_1_OFF:
+	case WCD9XXX_EVENT_PRE_CFILT_1_ON:
+	case WCD9XXX_EVENT_POST_CFILT_1_ON:
+		ret = WCD9XXX_CFILT1_SEL;
+		break;
+	case WCD9XXX_EVENT_PRE_CFILT_2_OFF:
+	case WCD9XXX_EVENT_POST_CFILT_2_OFF:
+	case WCD9XXX_EVENT_PRE_CFILT_2_ON:
+	case WCD9XXX_EVENT_POST_CFILT_2_ON:
+		ret = WCD9XXX_CFILT2_SEL;
+		break;
+	case WCD9XXX_EVENT_PRE_CFILT_3_OFF:
+	case WCD9XXX_EVENT_POST_CFILT_3_OFF:
+	case WCD9XXX_EVENT_PRE_CFILT_3_ON:
+	case WCD9XXX_EVENT_POST_CFILT_3_ON:
+		ret = WCD9XXX_CFILT3_SEL;
+		break;
+	default:
+		ret = -1;
+	}
+	return ret;
+}
+
+static int wcd9xxx_get_mbhc_cfilt_sel(struct wcd9xxx_mbhc *mbhc)
+{
+	int cfilt;
+	const struct wcd9xxx_pdata *pdata = mbhc->resmgr->pdata;
+
+	switch (mbhc->mbhc_cfg->micbias) {
+	case MBHC_MICBIAS1:
+		cfilt = pdata->micbias.bias1_cfilt_sel;
+		break;
+	case MBHC_MICBIAS2:
+		cfilt = pdata->micbias.bias2_cfilt_sel;
+		break;
+	case MBHC_MICBIAS3:
+		cfilt = pdata->micbias.bias3_cfilt_sel;
+		break;
+	case MBHC_MICBIAS4:
+		cfilt = pdata->micbias.bias4_cfilt_sel;
+		break;
+	default:
+		cfilt = MBHC_MICBIAS_INVALID;
+		break;
+	}
+	return cfilt;
+}
+
+static int wcd9xxx_event_notify(struct notifier_block *self, unsigned long val,
+				void *data)
+{
+	int ret = 0;
+	struct wcd9xxx_mbhc *mbhc = ((struct wcd9xxx_resmgr *)data)->mbhc;
+	struct snd_soc_codec *codec = mbhc->codec;
+	enum wcd9xxx_notify_event event = (enum wcd9xxx_notify_event)val;
+
+	pr_debug("%s: enter event %s(%d)\n", __func__,
+		 wcd9xxx_get_event_string(event), event);
+
+	switch (event) {
+	/* MICBIAS usage change */
+	case WCD9XXX_EVENT_PRE_MICBIAS_1_ON:
+	case WCD9XXX_EVENT_PRE_MICBIAS_2_ON:
+	case WCD9XXX_EVENT_PRE_MICBIAS_3_ON:
+	case WCD9XXX_EVENT_PRE_MICBIAS_4_ON:
+		if (mbhc->mbhc_cfg->micbias == wcd9xxx_event_to_micbias(event))
+			wcd9xxx_switch_micbias(mbhc, 0);
+		break;
+	case WCD9XXX_EVENT_POST_MICBIAS_1_ON:
+	case WCD9XXX_EVENT_POST_MICBIAS_2_ON:
+	case WCD9XXX_EVENT_POST_MICBIAS_3_ON:
+	case WCD9XXX_EVENT_POST_MICBIAS_4_ON:
+		if (mbhc->mbhc_cfg->micbias ==
+		    wcd9xxx_event_to_micbias(event) &&
+		    wcd9xxx_mbhc_polling(mbhc)) {
+			/* if polling is on, restart it */
+			wcd9xxx_pause_hs_polling(mbhc);
+			wcd9xxx_start_hs_polling(mbhc);
+		}
+		break;
+	case WCD9XXX_EVENT_POST_MICBIAS_1_OFF:
+	case WCD9XXX_EVENT_POST_MICBIAS_2_OFF:
+	case WCD9XXX_EVENT_POST_MICBIAS_3_OFF:
+	case WCD9XXX_EVENT_POST_MICBIAS_4_OFF:
+		if (mbhc->mbhc_cfg->micbias ==
+		    wcd9xxx_event_to_micbias(event) &&
+		    wcd9xxx_is_hph_pa_on(codec))
+			wcd9xxx_switch_micbias(mbhc, 1);
+		break;
+	/* PA usage change */
+	case WCD9XXX_EVENT_PRE_HPHL_PA_ON:
+		if (!(snd_soc_read(codec, mbhc->mbhc_bias_regs.ctl_reg & 0x80)))
+			/* if micbias is enabled, switch to vddio */
+			wcd9xxx_switch_micbias(mbhc, 1);
+		break;
+	case WCD9XXX_EVENT_PRE_HPHR_PA_ON:
+		/* Not used now */
+		break;
+	case WCD9XXX_EVENT_POST_HPHL_PA_OFF:
+		/* if HPH PAs are off, report OCP and switch back to CFILT */
+		clear_bit(WCD9XXX_HPHL_PA_OFF_ACK, &mbhc->hph_pa_dac_state);
+		clear_bit(WCD9XXX_HPHL_DAC_OFF_ACK, &mbhc->hph_pa_dac_state);
+		if (mbhc->hph_status & SND_JACK_OC_HPHL)
+			hphlocp_off_report(mbhc, SND_JACK_OC_HPHL);
+		wcd9xxx_switch_micbias(mbhc, 0);
+		break;
+	case WCD9XXX_EVENT_POST_HPHR_PA_OFF:
+		/* if HPH PAs are off, report OCP and switch back to CFILT */
+		clear_bit(WCD9XXX_HPHR_PA_OFF_ACK, &mbhc->hph_pa_dac_state);
+		clear_bit(WCD9XXX_HPHR_DAC_OFF_ACK, &mbhc->hph_pa_dac_state);
+		if (mbhc->hph_status & SND_JACK_OC_HPHR)
+			hphrocp_off_report(mbhc, SND_JACK_OC_HPHL);
+		wcd9xxx_switch_micbias(mbhc, 0);
+		break;
+	/* Clock usage change */
+	case WCD9XXX_EVENT_PRE_MCLK_ON:
+		break;
+	case WCD9XXX_EVENT_POST_MCLK_ON:
+		/* Change to lower TxAAF frequency */
+		snd_soc_update_bits(codec, WCD9XXX_A_TX_COM_BIAS, 1 << 4,
+				    1 << 4);
+		/* Re-calibrate clock rate dependent values */
+		wcd9xxx_update_mbhc_clk_rate(mbhc, mbhc->mbhc_cfg->mclk_rate);
+		/* If clock source changes, stop and restart polling */
+		if (wcd9xxx_mbhc_polling(mbhc)) {
+			wcd9xxx_calibrate_hs_polling(mbhc);
+			wcd9xxx_start_hs_polling(mbhc);
+		}
+		break;
+	case WCD9XXX_EVENT_PRE_MCLK_OFF:
+		/* If clock source changes, stop and restart polling */
+		if (wcd9xxx_mbhc_polling(mbhc))
+			wcd9xxx_pause_hs_polling(mbhc);
+		break;
+	case WCD9XXX_EVENT_POST_MCLK_OFF:
+		break;
+	case WCD9XXX_EVENT_PRE_RCO_ON:
+		break;
+	case WCD9XXX_EVENT_POST_RCO_ON:
+		/* Change to higher TxAAF frequency */
+		snd_soc_update_bits(codec, WCD9XXX_A_TX_COM_BIAS, 1 << 4,
+				    0 << 4);
+		/* Re-calibrate clock rate dependent values */
+		wcd9xxx_update_mbhc_clk_rate(mbhc, WCD9XXX_RCO_CLK_RATE);
+		/* If clock source changes, stop and restart polling */
+		if (wcd9xxx_mbhc_polling(mbhc)) {
+			wcd9xxx_calibrate_hs_polling(mbhc);
+			wcd9xxx_start_hs_polling(mbhc);
+		}
+		break;
+	case WCD9XXX_EVENT_PRE_RCO_OFF:
+		/* If clock source changes, stop and restart polling */
+		if (wcd9xxx_mbhc_polling(mbhc))
+			wcd9xxx_pause_hs_polling(mbhc);
+		break;
+	case WCD9XXX_EVENT_POST_RCO_OFF:
+		break;
+	/* CFILT usage change */
+	case WCD9XXX_EVENT_PRE_CFILT_1_ON:
+	case WCD9XXX_EVENT_PRE_CFILT_2_ON:
+	case WCD9XXX_EVENT_PRE_CFILT_3_ON:
+		if (wcd9xxx_get_mbhc_cfilt_sel(mbhc) ==
+		    wcd9xxx_event_to_cfilt(event))
+			/*
+			 * Switch CFILT to slow mode if MBHC CFILT is being
+			 * used.
+			 */
+			wcd9xxx_codec_switch_cfilt_mode(mbhc, false);
+		break;
+	case WCD9XXX_EVENT_POST_CFILT_1_OFF:
+	case WCD9XXX_EVENT_POST_CFILT_2_OFF:
+	case WCD9XXX_EVENT_POST_CFILT_3_OFF:
+		if (wcd9xxx_get_mbhc_cfilt_sel(mbhc) ==
+		    wcd9xxx_event_to_cfilt(event))
+			/*
+			 * Switch CFILT to fast mode if MBHC CFILT is not
+			 * used anymore.
+			 */
+			wcd9xxx_codec_switch_cfilt_mode(mbhc, true);
+		break;
+	/* System resume */
+	case WCD9XXX_EVENT_POST_RESUME:
+		mbhc->mbhc_last_resume = jiffies;
+		break;
+	/* BG mode chage */
+	case WCD9XXX_EVENT_PRE_BG_OFF:
+	case WCD9XXX_EVENT_POST_BG_OFF:
+	case WCD9XXX_EVENT_PRE_BG_AUDIO_ON:
+	case WCD9XXX_EVENT_POST_BG_AUDIO_ON:
+	case WCD9XXX_EVENT_PRE_BG_MBHC_ON:
+	case WCD9XXX_EVENT_POST_BG_MBHC_ON:
+		/* Not used for now */
+		break;
+	default:
+		WARN(1, "Unknown event %d\n", event);
+		ret = -EINVAL;
+	}
+
+	pr_debug("%s: leave\n", __func__);
+
+	return 0;
+}
+
+/*
+ * wcd9xxx_mbhc_init : initialize MBHC internal structures.
+ *
+ * NOTE: mbhc->mbhc_cfg is not YET configure so shouldn't be used
+ */
+int wcd9xxx_mbhc_init(struct wcd9xxx_mbhc *mbhc, struct wcd9xxx_resmgr *resmgr,
+		      struct snd_soc_codec *codec)
+{
+	int ret;
+	void *core;
+
+	pr_debug("%s: enter\n", __func__);
+	memset(&mbhc->mbhc_bias_regs, 0, sizeof(struct mbhc_micbias_regs));
+	memset(&mbhc->mbhc_data, 0, sizeof(struct mbhc_internal_cal_data));
+
+	mbhc->mbhc_data.t_sta_dce = DEFAULT_DCE_STA_WAIT;
+	mbhc->mbhc_data.t_dce = DEFAULT_DCE_WAIT;
+	mbhc->mbhc_data.t_sta = DEFAULT_STA_WAIT;
+	mbhc->mbhc_micbias_switched = false;
+	mbhc->polling_active = false;
+	mbhc->mbhc_state = MBHC_STATE_NONE;
+	mbhc->in_swch_irq_handler = false;
+	mbhc->current_plug = PLUG_TYPE_NONE;
+	mbhc->lpi_enabled = false;
+	mbhc->no_mic_headset_override = false;
+	mbhc->mbhc_last_resume = 0;
+	mbhc->codec = codec;
+	mbhc->resmgr = resmgr;
+	mbhc->resmgr->mbhc = mbhc;
+
+	ret = snd_soc_jack_new(codec, "Headset Jack", WCD9XXX_JACK_MASK,
+			       &mbhc->headset_jack);
+	if (ret) {
+		pr_err("%s: Failed to create new jack\n", __func__);
+		return ret;
+	}
+
+	ret = snd_soc_jack_new(codec, "Button Jack", WCD9XXX_JACK_BUTTON_MASK,
+			       &mbhc->button_jack);
+	if (ret) {
+		pr_err("Failed to create new jack\n");
+		return ret;
+	}
+
+	mbhc->mbhc_cfg = kzalloc(sizeof(*mbhc->mbhc_cfg), GFP_KERNEL);
+	if (!mbhc->mbhc_cfg)
+		return -ENOMEM;
+
+	INIT_DELAYED_WORK(&mbhc->mbhc_firmware_dwork, wcd9xxx_mbhc_fw_read);
+	INIT_DELAYED_WORK(&mbhc->mbhc_btn_dwork, wcd9xxx_btn_lpress_fn);
+	INIT_DELAYED_WORK(&mbhc->mbhc_insert_dwork, wcd9xxx_mbhc_insert_work);
+
+	/* Register event notifier */
+	mbhc->nblock.notifier_call = wcd9xxx_event_notify;
+	ret = wcd9xxx_resmgr_register_notifier(mbhc->resmgr, &mbhc->nblock);
+	if (ret) {
+		pr_err("%s: Failed to register notifier %d\n", __func__, ret);
+		return ret;
+	}
+
+	wcd9xxx_init_debugfs(mbhc);
+
+	core = mbhc->resmgr->core;
+	ret = wcd9xxx_request_irq(core, WCD9XXX_IRQ_MBHC_INSERTION,
+				  wcd9xxx_hs_insert_irq,
+				  "Headset insert detect", mbhc);
+	if (ret) {
+		pr_err("%s: Failed to request irq %d\n", __func__,
+		       WCD9XXX_IRQ_MBHC_INSERTION);
+		goto err_insert_irq;
+	}
+	wcd9xxx_disable_irq(core, WCD9XXX_IRQ_MBHC_INSERTION);
+
+	ret = wcd9xxx_request_irq(core, WCD9XXX_IRQ_MBHC_REMOVAL,
+				  wcd9xxx_hs_remove_irq,
+				  "Headset remove detect", mbhc);
+	if (ret) {
+		pr_err("%s: Failed to request irq %d\n", __func__,
+			WCD9XXX_IRQ_MBHC_REMOVAL);
+		goto err_remove_irq;
+	}
+
+	ret = wcd9xxx_request_irq(core, WCD9XXX_IRQ_MBHC_POTENTIAL,
+				  wcd9xxx_dce_handler, "DC Estimation detect",
+				  mbhc);
+	if (ret) {
+		pr_err("%s: Failed to request irq %d\n", __func__,
+		       WCD9XXX_IRQ_MBHC_POTENTIAL);
+		goto err_potential_irq;
+	}
+
+	ret = wcd9xxx_request_irq(core, WCD9XXX_IRQ_MBHC_RELEASE,
+				  wcd9xxx_release_handler,
+				  "Button Release detect", mbhc);
+	if (ret) {
+		pr_err("%s: Failed to request irq %d\n", __func__,
+			WCD9XXX_IRQ_MBHC_RELEASE);
+		goto err_release_irq;
+	}
+
+	ret = wcd9xxx_request_irq(core, WCD9XXX_IRQ_HPH_PA_OCPL_FAULT,
+				  wcd9xxx_hphl_ocp_irq, "HPH_L OCP detect",
+				  mbhc);
+	if (ret) {
+		pr_err("%s: Failed to request irq %d\n", __func__,
+		       WCD9XXX_IRQ_HPH_PA_OCPL_FAULT);
+		goto err_hphl_ocp_irq;
+	}
+	wcd9xxx_disable_irq(core, WCD9XXX_IRQ_HPH_PA_OCPL_FAULT);
+
+	ret = wcd9xxx_request_irq(core, WCD9XXX_IRQ_HPH_PA_OCPR_FAULT,
+				  wcd9xxx_hphr_ocp_irq, "HPH_R OCP detect",
+				  mbhc);
+	if (ret) {
+		pr_err("%s: Failed to request irq %d\n", __func__,
+		       WCD9XXX_IRQ_HPH_PA_OCPR_FAULT);
+		goto err_hphr_ocp_irq;
+	}
+	wcd9xxx_disable_irq(codec->control_data, WCD9XXX_IRQ_HPH_PA_OCPR_FAULT);
+
+	pr_debug("%s: leave ret %d\n", __func__, ret);
+	return ret;
+
+err_hphr_ocp_irq:
+	wcd9xxx_free_irq(core, WCD9XXX_IRQ_HPH_PA_OCPL_FAULT, mbhc);
+err_hphl_ocp_irq:
+	wcd9xxx_free_irq(core, WCD9XXX_IRQ_MBHC_RELEASE, mbhc);
+err_release_irq:
+	wcd9xxx_free_irq(core, WCD9XXX_IRQ_MBHC_POTENTIAL, mbhc);
+err_potential_irq:
+	wcd9xxx_free_irq(core, WCD9XXX_IRQ_MBHC_REMOVAL, mbhc);
+err_remove_irq:
+	wcd9xxx_free_irq(core, WCD9XXX_IRQ_MBHC_INSERTION, mbhc);
+err_insert_irq:
+	wcd9xxx_resmgr_unregister_notifier(mbhc->resmgr, &mbhc->nblock);
+
+	pr_debug("%s: leave ret %d\n", __func__, ret);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(wcd9xxx_mbhc_init);
+
+void wcd9xxx_mbhc_deinit(struct wcd9xxx_mbhc *mbhc)
+{
+	void *cdata = mbhc->codec->control_data;
+
+	wcd9xxx_free_irq(cdata, WCD9XXX_IRQ_SLIMBUS, mbhc);
+	wcd9xxx_free_irq(cdata, WCD9XXX_IRQ_MBHC_RELEASE, mbhc);
+	wcd9xxx_free_irq(cdata, WCD9XXX_IRQ_MBHC_POTENTIAL, mbhc);
+	wcd9xxx_free_irq(cdata, WCD9XXX_IRQ_MBHC_REMOVAL, mbhc);
+	wcd9xxx_free_irq(cdata, WCD9XXX_IRQ_MBHC_INSERTION, mbhc);
+
+	if (mbhc->mbhc_fw)
+		release_firmware(mbhc->mbhc_fw);
+
+	wcd9xxx_resmgr_unregister_notifier(mbhc->resmgr, &mbhc->nblock);
+
+	wcd9xxx_cleanup_debugfs(mbhc);
+
+	kfree(mbhc->mbhc_cfg);
+}
+EXPORT_SYMBOL_GPL(wcd9xxx_mbhc_deinit);
+
+MODULE_DESCRIPTION("wcd9xxx MBHC module");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/wcd9xxx-mbhc.h b/sound/soc/codecs/wcd9xxx-mbhc.h
new file mode 100644
index 0000000..fb1dfdc
--- /dev/null
+++ b/sound/soc/codecs/wcd9xxx-mbhc.h
@@ -0,0 +1,315 @@
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#ifndef __WCD9XXX_MBHC_H__
+#define __WCD9XXX_MBHC_H__
+
+#include "wcd9xxx-resmgr.h"
+
+#define WCD9XXX_CFILT_FAST_MODE 0x00
+#define WCD9XXX_CFILT_SLOW_MODE 0x40
+
+struct mbhc_micbias_regs {
+	u16 cfilt_val;
+	u16 cfilt_ctl;
+	u16 mbhc_reg;
+	u16 int_rbias;
+	u16 ctl_reg;
+	u8 cfilt_sel;
+};
+
+/* Data used by MBHC */
+struct mbhc_internal_cal_data {
+	u16 dce_z;
+	u16 dce_mb;
+	u16 sta_z;
+	u16 sta_mb;
+	u32 t_sta_dce;
+	u32 t_dce;
+	u32 t_sta;
+	u32 micb_mv;
+	u16 v_ins_hu;
+	u16 v_ins_h;
+	u16 v_b1_hu;
+	u16 v_b1_h;
+	u16 v_b1_huc;
+	u16 v_brh;
+	u16 v_brl;
+	u16 v_no_mic;
+	s16 adj_v_hs_max;
+	u16 adj_v_ins_hu;
+	u16 adj_v_ins_h;
+	s16 v_inval_ins_low;
+	s16 v_inval_ins_high;
+};
+
+enum wcd9xxx_mbhc_plug_type {
+	PLUG_TYPE_INVALID = -1,
+	PLUG_TYPE_NONE,
+	PLUG_TYPE_HEADSET,
+	PLUG_TYPE_HEADPHONE,
+	PLUG_TYPE_HIGH_HPH,
+	PLUG_TYPE_GND_MIC_SWAP,
+};
+
+enum wcd9xxx_micbias_num {
+	MBHC_MICBIAS_INVALID = -1,
+	MBHC_MICBIAS1,
+	MBHC_MICBIAS2,
+	MBHC_MICBIAS3,
+	MBHC_MICBIAS4,
+};
+
+enum wcd9xxx_mbhc_state {
+	MBHC_STATE_NONE = -1,
+	MBHC_STATE_POTENTIAL,
+	MBHC_STATE_POTENTIAL_RECOVERY,
+	MBHC_STATE_RELEASE,
+};
+
+enum wcd9xxx_mbhc_btn_det_mem {
+	MBHC_BTN_DET_V_BTN_LOW,
+	MBHC_BTN_DET_V_BTN_HIGH,
+	MBHC_BTN_DET_N_READY,
+	MBHC_BTN_DET_N_CIC,
+	MBHC_BTN_DET_GAIN
+};
+
+enum wcd9xxx_mbhc_clk_freq {
+	TAIKO_MCLK_12P2MHZ = 0,
+	TAIKO_MCLK_9P6MHZ,
+	TAIKO_NUM_CLK_FREQS,
+};
+
+struct wcd9xxx_mbhc_general_cfg {
+	u8 t_ldoh;
+	u8 t_bg_fast_settle;
+	u8 t_shutdown_plug_rem;
+	u8 mbhc_nsa;
+	u8 mbhc_navg;
+	u8 v_micbias_l;
+	u8 v_micbias;
+	u8 mbhc_reserved;
+	u16 settle_wait;
+	u16 t_micbias_rampup;
+	u16 t_micbias_rampdown;
+	u16 t_supply_bringup;
+} __packed;
+
+struct wcd9xxx_mbhc_plug_detect_cfg {
+	u32 mic_current;
+	u32 hph_current;
+	u16 t_mic_pid;
+	u16 t_ins_complete;
+	u16 t_ins_retry;
+	u16 v_removal_delta;
+	u8 micbias_slow_ramp;
+	u8 reserved0;
+	u8 reserved1;
+	u8 reserved2;
+} __packed;
+
+struct wcd9xxx_mbhc_plug_type_cfg {
+	u8 av_detect;
+	u8 mono_detect;
+	u8 num_ins_tries;
+	u8 reserved0;
+	s16 v_no_mic;
+	s16 v_av_min;
+	s16 v_av_max;
+	s16 v_hs_min;
+	s16 v_hs_max;
+	u16 reserved1;
+} __packed;
+
+struct wcd9xxx_mbhc_btn_detect_cfg {
+	s8 c[8];
+	u8 nc;
+	u8 n_meas;
+	u8 mbhc_nsc;
+	u8 n_btn_meas;
+	u8 n_btn_con;
+	u8 num_btn;
+	u8 reserved0;
+	u8 reserved1;
+	u16 t_poll;
+	u16 t_bounce_wait;
+	u16 t_rel_timeout;
+	s16 v_btn_press_delta_sta;
+	s16 v_btn_press_delta_cic;
+	u16 t_btn0_timeout;
+	s16 _v_btn_low[0]; /* v_btn_low[num_btn] */
+	s16 _v_btn_high[0]; /* v_btn_high[num_btn] */
+	u8 _n_ready[TAIKO_NUM_CLK_FREQS];
+	u8 _n_cic[TAIKO_NUM_CLK_FREQS];
+	u8 _gain[TAIKO_NUM_CLK_FREQS];
+} __packed;
+
+struct wcd9xxx_mbhc_imped_detect_cfg {
+	u8 _hs_imped_detect;
+	u8 _n_rload;
+	u8 _hph_keep_on;
+	u8 _repeat_rload_calc;
+	u16 _t_dac_ramp_time;
+	u16 _rhph_high;
+	u16 _rhph_low;
+	u16 _rload[0]; /* rload[n_rload] */
+	u16 _alpha[0]; /* alpha[n_rload] */
+	u16 _beta[3];
+} __packed;
+
+struct wcd9xxx_mbhc_config {
+	bool read_fw_bin;
+	/*
+	 * void* calibration contains:
+	 *  struct wcd9xxx_mbhc_general_cfg generic;
+	 *  struct wcd9xxx_mbhc_plug_detect_cfg plug_det;
+	 *  struct wcd9xxx_mbhc_plug_type_cfg plug_type;
+	 *  struct wcd9xxx_mbhc_btn_detect_cfg btn_det;
+	 *  struct wcd9xxx_mbhc_imped_detect_cfg imped_det;
+	 * Note: various size depends on btn_det->num_btn
+	 */
+	void *calibration;
+	enum wcd9xxx_micbias_num micbias;
+	int (*mclk_cb_fn) (struct snd_soc_codec*, int, bool);
+	unsigned int mclk_rate;
+	unsigned int gpio;
+	unsigned int gpio_irq;
+	int gpio_level_insert;
+	bool insert_detect; /* codec has own MBHC_INSERT_DETECT */
+	bool detect_extn_cable;
+	/* swap_gnd_mic returns true if extern GND/MIC swap switch toggled */
+	bool (*swap_gnd_mic) (struct snd_soc_codec *);
+};
+
+struct wcd9xxx_mbhc {
+	bool polling_active;
+	/* Delayed work to report long button press */
+	struct delayed_work mbhc_btn_dwork;
+	int buttons_pressed;
+	enum wcd9xxx_mbhc_state mbhc_state;
+	struct wcd9xxx_mbhc_config *mbhc_cfg;
+
+	struct mbhc_internal_cal_data mbhc_data;
+
+	struct mbhc_micbias_regs mbhc_bias_regs;
+	bool mbhc_micbias_switched;
+
+	u32 hph_status; /* track headhpone status */
+	u8 hphlocp_cnt; /* headphone left ocp retry */
+	u8 hphrocp_cnt; /* headphone right ocp retry */
+
+	/* Work to perform MBHC Firmware Read */
+	struct delayed_work mbhc_firmware_dwork;
+	const struct firmware *mbhc_fw;
+
+	struct delayed_work mbhc_insert_dwork;
+
+	u8 current_plug;
+	struct work_struct correct_plug_swch;
+	/*
+	 * Work to perform polling on microphone voltage
+	 * in order to correct plug type once plug type
+	 * is detected as headphone
+	 */
+	struct work_struct correct_plug_noswch;
+	bool hs_detect_work_stop;
+
+	bool lpi_enabled; /* low power insertion detection */
+	bool in_swch_irq_handler;
+
+	struct wcd9xxx_resmgr *resmgr;
+	struct snd_soc_codec *codec;
+
+	bool no_mic_headset_override;
+
+	/* track PA/DAC state */
+	unsigned long hph_pa_dac_state;
+
+	unsigned long mbhc_last_resume; /* in jiffies */
+
+	bool insert_detect_level_insert;
+
+	struct snd_soc_jack headset_jack;
+	struct snd_soc_jack button_jack;
+
+	struct notifier_block nblock;
+
+#ifdef CONFIG_DEBUG_FS
+	struct dentry *debugfs_poke;
+	struct dentry *debugfs_mbhc;
+#endif
+};
+
+#define WCD9XXX_MBHC_CAL_SIZE(buttons, rload) ( \
+	sizeof(enum wcd9xxx_micbias_num) + \
+	sizeof(struct wcd9xxx_mbhc_general_cfg) + \
+	sizeof(struct wcd9xxx_mbhc_plug_detect_cfg) + \
+	    ((sizeof(s16) + sizeof(s16)) * buttons) + \
+	sizeof(struct wcd9xxx_mbhc_plug_type_cfg) + \
+	sizeof(struct wcd9xxx_mbhc_btn_detect_cfg) + \
+	sizeof(struct wcd9xxx_mbhc_imped_detect_cfg) + \
+	    ((sizeof(u16) + sizeof(u16)) * rload) \
+	)
+
+#define WCD9XXX_MBHC_CAL_GENERAL_PTR(cali) ( \
+	    (struct wcd9xxx_mbhc_general_cfg *) cali)
+#define WCD9XXX_MBHC_CAL_PLUG_DET_PTR(cali) ( \
+	    (struct wcd9xxx_mbhc_plug_detect_cfg *) \
+	    &(WCD9XXX_MBHC_CAL_GENERAL_PTR(cali)[1]))
+#define WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(cali) ( \
+	    (struct wcd9xxx_mbhc_plug_type_cfg *) \
+	    &(WCD9XXX_MBHC_CAL_PLUG_DET_PTR(cali)[1]))
+#define WCD9XXX_MBHC_CAL_BTN_DET_PTR(cali) ( \
+	    (struct wcd9xxx_mbhc_btn_detect_cfg *) \
+	    &(WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(cali)[1]))
+#define WCD9XXX_MBHC_CAL_IMPED_DET_PTR(cali) ( \
+	    (struct wcd9xxx_mbhc_imped_detect_cfg *) \
+	    (((void *)&WCD9XXX_MBHC_CAL_BTN_DET_PTR(cali)[1]) + \
+	     (WCD9XXX_MBHC_CAL_BTN_DET_PTR(cali)->num_btn * \
+	      (sizeof(WCD9XXX_MBHC_CAL_BTN_DET_PTR(cali)->_v_btn_low[0]) + \
+	       sizeof(WCD9XXX_MBHC_CAL_BTN_DET_PTR(cali)->_v_btn_high[0])))) \
+	)
+
+/* minimum size of calibration data assuming there is only one button and
+ * one rload.
+ */
+#define WCD9XXX_MBHC_CAL_MIN_SIZE ( \
+	    sizeof(struct wcd9xxx_mbhc_general_cfg) + \
+	    sizeof(struct wcd9xxx_mbhc_plug_detect_cfg) + \
+	    sizeof(struct wcd9xxx_mbhc_plug_type_cfg) + \
+	    sizeof(struct wcd9xxx_mbhc_btn_detect_cfg) + \
+	    sizeof(struct wcd9xxx_mbhc_imped_detect_cfg) + \
+	    (sizeof(u16) * 2) \
+	)
+
+#define WCD9XXX_MBHC_CAL_BTN_SZ(cfg_ptr) ( \
+	    sizeof(struct wcd9xxx_mbhc_btn_detect_cfg) + \
+	    (cfg_ptr->num_btn * (sizeof(cfg_ptr->_v_btn_low[0]) + \
+				 sizeof(cfg_ptr->_v_btn_high[0]))))
+
+#define WCD9XXX_MBHC_CAL_IMPED_MIN_SZ ( \
+	    sizeof(struct wcd9xxx_mbhc_imped_detect_cfg) + sizeof(u16) * 2)
+
+#define WCD9XXX_MBHC_CAL_IMPED_SZ(cfg_ptr) ( \
+	    sizeof(struct wcd9xxx_mbhc_imped_detect_cfg) + \
+	    (cfg_ptr->_n_rload * \
+	     (sizeof(cfg_ptr->_rload[0]) + sizeof(cfg_ptr->_alpha[0]))))
+
+int wcd9xxx_mbhc_start(struct wcd9xxx_mbhc *mbhc,
+		       struct wcd9xxx_mbhc_config *mbhc_cfg);
+int wcd9xxx_mbhc_init(struct wcd9xxx_mbhc *mbhc, struct wcd9xxx_resmgr *resmgr,
+		      struct snd_soc_codec *codec);
+void wcd9xxx_mbhc_deinit(struct wcd9xxx_mbhc *mbhc);
+void *wcd9xxx_mbhc_cal_btn_det_mp(
+			    const struct wcd9xxx_mbhc_btn_detect_cfg *btn_det,
+			    const enum wcd9xxx_mbhc_btn_det_mem mem);
+#endif /* __WCD9XXX_MBHC_H__ */
diff --git a/sound/soc/codecs/wcd9xxx-resmgr.c b/sound/soc/codecs/wcd9xxx-resmgr.c
new file mode 100644
index 0000000..5dfa41c
--- /dev/null
+++ b/sound/soc/codecs/wcd9xxx-resmgr.c
@@ -0,0 +1,654 @@
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/firmware.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/device.h>
+#include <linux/printk.h>
+#include <linux/ratelimit.h>
+#include <linux/debugfs.h>
+#include <linux/mfd/wcd9xxx/core.h>
+#include <linux/mfd/wcd9xxx/wcd9xxx_registers.h>
+#include <linux/mfd/wcd9xxx/wcd9320_registers.h>
+#include <linux/mfd/wcd9xxx/pdata.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include <linux/pm_runtime.h>
+#include <linux/kernel.h>
+#include <linux/gpio.h>
+#include "wcd9xxx-resmgr.h"
+
+static char wcd9xxx_event_string[][64] = {
+	"WCD9XXX_EVENT_INVALID",
+
+	"WCD9XXX_EVENT_PRE_RCO_ON",
+	"WCD9XXX_EVENT_POST_RCO_ON",
+	"WCD9XXX_EVENT_PRE_RCO_OFF",
+	"WCD9XXX_EVENT_POST_RCO_OFF",
+
+	"WCD9XXX_EVENT_PRE_MCLK_ON",
+	"WCD9XXX_EVENT_POST_MCLK_ON",
+	"WCD9XXX_EVENT_PRE_MCLK_OFF",
+	"WCD9XXX_EVENT_POST_MCLK_OFF",
+
+	"WCD9XXX_EVENT_PRE_BG_OFF",
+	"WCD9XXX_EVENT_POST_BG_OFF",
+	"WCD9XXX_EVENT_PRE_BG_AUDIO_ON",
+	"WCD9XXX_EVENT_POST_BG_AUDIO_ON",
+	"WCD9XXX_EVENT_PRE_BG_MBHC_ON",
+	"WCD9XXX_EVENT_POST_BG_MBHC_ON",
+
+	"WCD9XXX_EVENT_PRE_MICBIAS_1_OFF",
+	"WCD9XXX_EVENT_POST_MICBIAS_1_OFF",
+	"WCD9XXX_EVENT_PRE_MICBIAS_2_OFF",
+	"WCD9XXX_EVENT_POST_MICBIAS_2_OFF",
+	"WCD9XXX_EVENT_PRE_MICBIAS_3_OFF",
+	"WCD9XXX_EVENT_POST_MICBIAS_3_OFF",
+	"WCD9XXX_EVENT_PRE_MICBIAS_4_OFF",
+	"WCD9XXX_EVENT_POST_MICBIAS_4_OFF",
+	"WCD9XXX_EVENT_PRE_MICBIAS_1_ON",
+	"WCD9XXX_EVENT_POST_MICBIAS_1_ON",
+	"WCD9XXX_EVENT_PRE_MICBIAS_2_ON",
+	"WCD9XXX_EVENT_POST_MICBIAS_2_ON",
+	"WCD9XXX_EVENT_PRE_MICBIAS_3_ON",
+	"WCD9XXX_EVENT_POST_MICBIAS_3_ON",
+	"WCD9XXX_EVENT_PRE_MICBIAS_4_ON",
+	"WCD9XXX_EVENT_POST_MICBIAS_4_ON",
+
+	"WCD9XXX_EVENT_PRE_CFILT_1_OFF",
+	"WCD9XXX_EVENT_POST_CFILT_1_OFF",
+	"WCD9XXX_EVENT_PRE_CFILT_2_OFF",
+	"WCD9XXX_EVENT_POST_CFILT_2_OFF",
+	"WCD9XXX_EVENT_PRE_CFILT_3_OFF",
+	"WCD9XXX_EVENT_POST_CFILT_3_OFF",
+	"WCD9XXX_EVENT_PRE_CFILT_1_ON",
+	"WCD9XXX_EVENT_POST_CFILT_1_ON",
+	"WCD9XXX_EVENT_PRE_CFILT_2_ON",
+	"WCD9XXX_EVENT_POST_CFILT_2_ON",
+	"WCD9XXX_EVENT_PRE_CFILT_3_ON",
+	"WCD9XXX_EVENT_POST_CFILT_3_ON",
+
+	"WCD9XXX_EVENT_PRE_HPHL_PA_ON",
+	"WCD9XXX_EVENT_POST_HPHL_PA_OFF",
+	"WCD9XXX_EVENT_PRE_HPHR_PA_ON",
+	"WCD9XXX_EVENT_POST_HPHR_PA_OFF",
+
+	"WCD9XXX_EVENT_POST_RESUME",
+
+	"WCD9XXX_EVENT_LAST",
+};
+
+static enum wcd9xxx_clock_type wcd9xxx_save_clock(struct wcd9xxx_resmgr
+						  *resmgr);
+static void wcd9xxx_restore_clock(struct wcd9xxx_resmgr *resmgr,
+				  enum wcd9xxx_clock_type type);
+
+const char *wcd9xxx_get_event_string(enum wcd9xxx_notify_event type)
+{
+	return wcd9xxx_event_string[type];
+}
+
+void wcd9xxx_resmgr_notifier_call(struct wcd9xxx_resmgr *resmgr,
+				  const enum wcd9xxx_notify_event e)
+{
+	pr_debug("%s: notifier call event %d\n", __func__, e);
+	blocking_notifier_call_chain(&resmgr->notifier, e, resmgr);
+}
+
+static void wcd9xxx_codec_disable_bg(struct wcd9xxx_resmgr *resmgr)
+{
+	/* Notify bg mode change */
+	wcd9xxx_resmgr_notifier_call(resmgr, WCD9XXX_EVENT_PRE_BG_OFF);
+	/* Disable bg */
+	snd_soc_write(resmgr->codec, WCD9XXX_A_BIAS_CENTRAL_BG_CTL, 0x00);
+	usleep_range(100, 100);
+	/* Notify bg mode change */
+	wcd9xxx_resmgr_notifier_call(resmgr, WCD9XXX_EVENT_POST_BG_OFF);
+}
+
+static void wcd9xxx_codec_enable_bg_audio(struct wcd9xxx_resmgr *resmgr)
+{
+	struct snd_soc_codec *codec = resmgr->codec;
+
+	/* Notify bandgap mode change */
+	wcd9xxx_resmgr_notifier_call(resmgr, WCD9XXX_EVENT_PRE_BG_AUDIO_ON);
+	/* Enable bg */
+	snd_soc_update_bits(codec, WCD9XXX_A_BIAS_CENTRAL_BG_CTL, 0x80, 0x80);
+	snd_soc_update_bits(codec, WCD9XXX_A_BIAS_CENTRAL_BG_CTL, 0x04, 0x04);
+	snd_soc_update_bits(codec, WCD9XXX_A_BIAS_CENTRAL_BG_CTL, 0x01, 0x01);
+	usleep_range(1000, 1000);
+	snd_soc_update_bits(codec, WCD9XXX_A_BIAS_CENTRAL_BG_CTL, 0x80, 0x00);
+	/* Notify bandgap mode change */
+	wcd9xxx_resmgr_notifier_call(resmgr, WCD9XXX_EVENT_POST_BG_AUDIO_ON);
+}
+
+static void wcd9xxx_enable_bg_mbhc(struct wcd9xxx_resmgr *resmgr)
+{
+	struct snd_soc_codec *codec = resmgr->codec;
+
+	/* Notify bandgap mode change */
+	wcd9xxx_resmgr_notifier_call(resmgr, WCD9XXX_EVENT_PRE_BG_MBHC_ON);
+
+	/*
+	 * bandgap mode becomes fast,
+	 * mclk should be off or clk buff source souldn't be VBG
+	 * Let's turn off mclk always
+	 */
+	WARN_ON(snd_soc_read(codec, WCD9XXX_A_CLK_BUFF_EN2) & (1 << 2));
+	snd_soc_update_bits(codec, WCD9XXX_A_BIAS_CENTRAL_BG_CTL, 0x2, 0x2);
+	snd_soc_update_bits(codec, WCD9XXX_A_BIAS_CENTRAL_BG_CTL, 0x80, 0x80);
+	snd_soc_update_bits(codec, WCD9XXX_A_BIAS_CENTRAL_BG_CTL, 0x4, 0x4);
+	snd_soc_update_bits(codec, WCD9XXX_A_BIAS_CENTRAL_BG_CTL, 0x01, 0x01);
+	usleep_range(1000, 1000);
+	snd_soc_update_bits(codec, WCD9XXX_A_BIAS_CENTRAL_BG_CTL, 0x80, 0x00);
+
+	/* Notify bandgap mode change */
+	wcd9xxx_resmgr_notifier_call(resmgr, WCD9XXX_EVENT_POST_BG_MBHC_ON);
+}
+
+static void wcd9xxx_disable_bg(struct wcd9xxx_resmgr *resmgr)
+{
+	struct snd_soc_codec *codec = resmgr->codec;
+	snd_soc_write(codec, WCD9XXX_A_BIAS_CENTRAL_BG_CTL, 0x00);
+}
+
+static void wcd9xxx_disable_clock_block(struct wcd9xxx_resmgr *resmgr)
+{
+	struct snd_soc_codec *codec = resmgr->codec;
+
+	pr_debug("%s: enter\n", __func__);
+	WCD9XXX_BCL_ASSERT_LOCKED(resmgr);
+
+	/* Notify */
+	if (resmgr->clk_type == WCD9XXX_CLK_RCO)
+		wcd9xxx_resmgr_notifier_call(resmgr, WCD9XXX_EVENT_PRE_RCO_OFF);
+	else
+		wcd9xxx_resmgr_notifier_call(resmgr,
+					     WCD9XXX_EVENT_PRE_MCLK_OFF);
+	/* Disable clock */
+	snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN2, 0x04, 0x00);
+	usleep_range(50, 50);
+	snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN2, 0x02, 0x02);
+	snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN1, 0x05, 0x00);
+	usleep_range(50, 50);
+	/* Notify */
+	if (resmgr->clk_type == WCD9XXX_CLK_RCO)
+		wcd9xxx_resmgr_notifier_call(resmgr,
+					     WCD9XXX_EVENT_POST_RCO_OFF);
+	else
+		wcd9xxx_resmgr_notifier_call(resmgr,
+					     WCD9XXX_EVENT_POST_MCLK_OFF);
+	pr_debug("%s: leave\n", __func__);
+}
+
+/*
+ * wcd9xxx_resmgr_get_bandgap : Vote for bandgap ref
+ * choice : WCD9XXX_BANDGAP_AUDIO_MODE, WCD9XXX_BANDGAP_MBHC_MODE
+ */
+void wcd9xxx_resmgr_get_bandgap(struct wcd9xxx_resmgr *resmgr,
+				const enum wcd9xxx_bandgap_type choice)
+{
+	enum wcd9xxx_clock_type clock_save;
+
+	pr_debug("%s: enter, wants %d\n", __func__, choice);
+
+	WCD9XXX_BCL_ASSERT_LOCKED(resmgr);
+	switch (choice) {
+	case WCD9XXX_BANDGAP_AUDIO_MODE:
+		resmgr->bg_audio_users++;
+		if (resmgr->bg_audio_users == 1 && resmgr->bg_mbhc_users) {
+			/*
+			 * Current bg is MBHC mode, about to switch to
+			 * audio mode.
+			 */
+			WARN_ON(resmgr->bandgap_type !=
+				WCD9XXX_BANDGAP_MBHC_MODE);
+
+			/* BG mode can be changed only with clock off */
+			clock_save = wcd9xxx_save_clock(resmgr);
+			/* Swtich BG mode */
+			wcd9xxx_codec_disable_bg(resmgr);
+			wcd9xxx_codec_enable_bg_audio(resmgr);
+			/* restore clock */
+			wcd9xxx_restore_clock(resmgr, clock_save);
+		} else if (resmgr->bg_audio_users == 1) {
+			/* currently off, just enable it */
+			WARN_ON(resmgr->bandgap_type != WCD9XXX_BANDGAP_OFF);
+			wcd9xxx_codec_enable_bg_audio(resmgr);
+		}
+		resmgr->bandgap_type = WCD9XXX_BANDGAP_AUDIO_MODE;
+		break;
+	case WCD9XXX_BANDGAP_MBHC_MODE:
+		resmgr->bg_mbhc_users++;
+		if (resmgr->bandgap_type == WCD9XXX_BANDGAP_MBHC_MODE ||
+		    resmgr->bandgap_type == WCD9XXX_BANDGAP_AUDIO_MODE)
+			/* do nothing */
+			break;
+
+		/* bg mode can be changed only with clock off */
+		clock_save = wcd9xxx_save_clock(resmgr);
+		/* enable bg with MBHC mode */
+		wcd9xxx_enable_bg_mbhc(resmgr);
+		/* restore clock */
+		wcd9xxx_restore_clock(resmgr, clock_save);
+		/* save current mode */
+		resmgr->bandgap_type = WCD9XXX_BANDGAP_MBHC_MODE;
+		break;
+	default:
+		pr_err("%s: Error, Invalid bandgap settings\n", __func__);
+		break;
+	}
+
+	pr_debug("%s: bg users audio %d, mbhc %d\n", __func__,
+		 resmgr->bg_audio_users, resmgr->bg_mbhc_users);
+}
+
+/*
+ * wcd9xxx_resmgr_put_bandgap : Unvote bandgap ref that has been voted
+ * choice : WCD9XXX_BANDGAP_AUDIO_MODE, WCD9XXX_BANDGAP_MBHC_MODE
+ */
+void wcd9xxx_resmgr_put_bandgap(struct wcd9xxx_resmgr *resmgr,
+				enum wcd9xxx_bandgap_type choice)
+{
+	enum wcd9xxx_clock_type clock_save;
+
+	pr_debug("%s: enter choice %d\n", __func__, choice);
+
+	WCD9XXX_BCL_ASSERT_LOCKED(resmgr);
+	switch (choice) {
+	case WCD9XXX_BANDGAP_AUDIO_MODE:
+		if (--resmgr->bg_audio_users == 0) {
+			if (resmgr->bg_mbhc_users) {
+				/* bg mode can be changed only with clock off */
+				clock_save = wcd9xxx_save_clock(resmgr);
+				/* switch to MBHC mode */
+				wcd9xxx_enable_bg_mbhc(resmgr);
+				/* restore clock */
+				wcd9xxx_restore_clock(resmgr, clock_save);
+				resmgr->bandgap_type =
+				    WCD9XXX_BANDGAP_MBHC_MODE;
+			} else {
+				/* turn off */
+				wcd9xxx_disable_bg(resmgr);
+				resmgr->bandgap_type = WCD9XXX_BANDGAP_OFF;
+			}
+		}
+		break;
+	case WCD9XXX_BANDGAP_MBHC_MODE:
+		WARN(resmgr->bandgap_type == WCD9XXX_BANDGAP_OFF,
+		     "Unexpected bandgap type %d\n", resmgr->bandgap_type);
+		if (--resmgr->bg_mbhc_users == 0 &&
+		    resmgr->bandgap_type == WCD9XXX_BANDGAP_MBHC_MODE) {
+			wcd9xxx_disable_bg(resmgr);
+			resmgr->bandgap_type = WCD9XXX_BANDGAP_OFF;
+		}
+		break;
+	default:
+		pr_err("%s: Error, Invalid bandgap settings\n", __func__);
+		break;
+	}
+
+	pr_debug("%s: bg users audio %d, mbhc %d\n", __func__,
+		 resmgr->bg_audio_users, resmgr->bg_mbhc_users);
+}
+
+void wcd9xxx_resmgr_enable_rx_bias(struct wcd9xxx_resmgr *resmgr, u32 enable)
+{
+	struct snd_soc_codec *codec = resmgr->codec;
+
+	if (enable) {
+		resmgr->rx_bias_count++;
+		if (resmgr->rx_bias_count == 1)
+			snd_soc_update_bits(codec, WCD9XXX_A_RX_COM_BIAS,
+					    0x80, 0x80);
+	} else {
+		resmgr->rx_bias_count--;
+		if (!resmgr->rx_bias_count)
+			snd_soc_update_bits(codec, WCD9XXX_A_RX_COM_BIAS,
+					    0x80, 0x00);
+	}
+}
+
+int wcd9xxx_resmgr_enable_config_mode(struct snd_soc_codec *codec, int enable)
+{
+	pr_debug("%s: enable = %d\n", __func__, enable);
+	if (enable) {
+		snd_soc_update_bits(codec, WCD9XXX_A_RC_OSC_FREQ, 0x10, 0);
+		/* bandgap mode to fast */
+		snd_soc_write(codec, WCD9XXX_A_BIAS_OSC_BG_CTL, 0x17);
+		usleep_range(5, 5);
+		snd_soc_update_bits(codec, WCD9XXX_A_RC_OSC_FREQ, 0x80, 0x80);
+		snd_soc_update_bits(codec, WCD9XXX_A_RC_OSC_TEST, 0x80, 0x80);
+		usleep_range(10, 10);
+		snd_soc_update_bits(codec, WCD9XXX_A_RC_OSC_TEST, 0x80, 0);
+		usleep_range(10000, 10000);
+		snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN1, 0x08, 0x08);
+	} else {
+		snd_soc_update_bits(codec, WCD9XXX_A_BIAS_OSC_BG_CTL, 0x1, 0);
+		snd_soc_update_bits(codec, WCD9XXX_A_RC_OSC_FREQ, 0x80, 0);
+		/* clk source to ext clk and clk buff ref to VBG */
+		snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN1, 0x0C, 0x04);
+	}
+
+	return 0;
+}
+
+static void wcd9xxx_enable_clock_block(struct wcd9xxx_resmgr *resmgr,
+				       int config_mode)
+{
+	struct snd_soc_codec *codec = resmgr->codec;
+
+	pr_debug("%s: config_mode = %d\n", __func__, config_mode);
+	/* transit to RCO requires mclk off */
+	WARN_ON(snd_soc_read(codec, WCD9XXX_A_CLK_BUFF_EN2) & (1 << 2));
+	if (config_mode) {
+		/* Notify */
+		wcd9xxx_resmgr_notifier_call(resmgr, WCD9XXX_EVENT_PRE_RCO_ON);
+		/* enable RCO and switch to it */
+		wcd9xxx_resmgr_enable_config_mode(codec, 1);
+		snd_soc_write(codec, WCD9XXX_A_CLK_BUFF_EN2, 0x02);
+		usleep_range(1000, 1000);
+	} else {
+		/* Notify */
+		wcd9xxx_resmgr_notifier_call(resmgr, WCD9XXX_EVENT_PRE_MCLK_ON);
+		/* switch to MCLK */
+		snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN1, 0x08, 0x00);
+		/* if RCO is enabled, switch from it */
+		if (snd_soc_read(codec, WCD9XXX_A_RC_OSC_FREQ) & 0x80) {
+			snd_soc_write(codec, WCD9XXX_A_CLK_BUFF_EN2, 0x02);
+			wcd9xxx_resmgr_enable_config_mode(codec, 0);
+		}
+	}
+
+	snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN1, 0x01, 0x01);
+	snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN2, 0x02, 0x00);
+
+	/* on MCLK */
+	snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN2, 0x04, 0x04);
+	snd_soc_update_bits(codec, WCD9XXX_A_CDC_CLK_MCLK_CTL, 0x01, 0x01);
+	usleep_range(50, 50);
+
+	/* Notify */
+	if (config_mode)
+		wcd9xxx_resmgr_notifier_call(resmgr,
+					     WCD9XXX_EVENT_POST_RCO_ON);
+	else
+		wcd9xxx_resmgr_notifier_call(resmgr,
+					     WCD9XXX_EVENT_POST_MCLK_ON);
+}
+
+/*
+ * disable clock and return previous clock state
+ */
+static enum wcd9xxx_clock_type wcd9xxx_save_clock(struct wcd9xxx_resmgr *resmgr)
+{
+	WCD9XXX_BCL_ASSERT_LOCKED(resmgr);
+	if (resmgr->clk_type != WCD9XXX_CLK_OFF)
+		wcd9xxx_disable_clock_block(resmgr);
+	return resmgr->clk_type != WCD9XXX_CLK_OFF;
+}
+
+static void wcd9xxx_restore_clock(struct wcd9xxx_resmgr *resmgr,
+				  enum wcd9xxx_clock_type type)
+{
+	if (type != WCD9XXX_CLK_OFF)
+		wcd9xxx_enable_clock_block(resmgr, type == WCD9XXX_CLK_RCO);
+}
+
+void wcd9xxx_resmgr_get_clk_block(struct wcd9xxx_resmgr *resmgr,
+				  enum wcd9xxx_clock_type type)
+{
+	pr_debug("%s: current %d, requested %d, rco_users %d, mclk_users %d\n",
+		 __func__, resmgr->clk_type, type,
+		 resmgr->clk_rco_users, resmgr->clk_mclk_users);
+	WCD9XXX_BCL_ASSERT_LOCKED(resmgr);
+	switch (type) {
+	case WCD9XXX_CLK_RCO:
+		if (++resmgr->clk_rco_users == 1 &&
+		    resmgr->clk_type == WCD9XXX_CLK_OFF) {
+			/* enable RCO and switch to it */
+			wcd9xxx_enable_clock_block(resmgr, 1);
+			resmgr->clk_type = WCD9XXX_CLK_RCO;
+		}
+		break;
+	case WCD9XXX_CLK_MCLK:
+		if (++resmgr->clk_mclk_users == 1 &&
+		    resmgr->clk_type == WCD9XXX_CLK_OFF) {
+			/* switch to MCLK */
+			wcd9xxx_enable_clock_block(resmgr, 0);
+			resmgr->clk_type = WCD9XXX_CLK_MCLK;
+		} else if (resmgr->clk_mclk_users == 1 &&
+			   resmgr->clk_type == WCD9XXX_CLK_RCO) {
+			/* if RCO is enabled, switch from it */
+			WARN_ON(!(snd_soc_read(resmgr->codec,
+					       WCD9XXX_A_RC_OSC_FREQ) & 0x80));
+			/* disable clock block */
+			wcd9xxx_disable_clock_block(resmgr);
+			/* switch to RCO */
+			wcd9xxx_enable_clock_block(resmgr, 0);
+			resmgr->clk_type = WCD9XXX_CLK_MCLK;
+		}
+		break;
+	default:
+		pr_err("%s: Error, Invalid clock get request %d\n", __func__,
+		       type);
+		break;
+	}
+	pr_debug("%s: leave\n", __func__);
+}
+
+void wcd9xxx_resmgr_put_clk_block(struct wcd9xxx_resmgr *resmgr,
+				  enum wcd9xxx_clock_type type)
+{
+	pr_debug("%s: current %d, put %d\n", __func__, resmgr->clk_type, type);
+
+	WCD9XXX_BCL_ASSERT_LOCKED(resmgr);
+	switch (type) {
+	case WCD9XXX_CLK_RCO:
+		if (--resmgr->clk_rco_users == 0 &&
+		    resmgr->clk_type == WCD9XXX_CLK_RCO) {
+			wcd9xxx_disable_clock_block(resmgr);
+			resmgr->clk_type = WCD9XXX_CLK_OFF;
+		}
+		break;
+	case WCD9XXX_CLK_MCLK:
+		if (--resmgr->clk_mclk_users == 0 &&
+		    resmgr->clk_rco_users == 0) {
+			wcd9xxx_disable_clock_block(resmgr);
+			resmgr->clk_type = WCD9XXX_CLK_OFF;
+		} else if (resmgr->clk_mclk_users == 0 &&
+			   resmgr->clk_rco_users) {
+			/* disable clock */
+			wcd9xxx_disable_clock_block(resmgr);
+			/* switch to RCO */
+			wcd9xxx_enable_clock_block(resmgr, 1);
+			resmgr->clk_type = WCD9XXX_CLK_RCO;
+		}
+		break;
+	default:
+		pr_err("%s: Error, Invalid clock get request %d\n", __func__,
+		       type);
+		break;
+	}
+	WARN_ON(resmgr->clk_rco_users < 0);
+	WARN_ON(resmgr->clk_mclk_users < 0);
+
+	pr_debug("%s: new rco_users %d, mclk_users %d\n", __func__,
+		 resmgr->clk_rco_users, resmgr->clk_mclk_users);
+}
+
+static void wcd9xxx_resmgr_update_cfilt_usage(struct wcd9xxx_resmgr *resmgr,
+					      enum wcd9xxx_cfilt_sel cfilt_sel,
+					      bool inc)
+{
+	u16 micb_cfilt_reg;
+	enum wcd9xxx_notify_event e_pre_on, e_post_off;
+	struct snd_soc_codec *codec = resmgr->codec;
+
+	switch (cfilt_sel) {
+	case WCD9XXX_CFILT1_SEL:
+		micb_cfilt_reg = WCD9XXX_A_MICB_CFILT_1_CTL;
+		e_pre_on = WCD9XXX_EVENT_PRE_CFILT_1_ON;
+		e_post_off = WCD9XXX_EVENT_POST_CFILT_1_OFF;
+		break;
+	case WCD9XXX_CFILT2_SEL:
+		micb_cfilt_reg = WCD9XXX_A_MICB_CFILT_2_CTL;
+		e_pre_on = WCD9XXX_EVENT_PRE_CFILT_2_ON;
+		e_post_off = WCD9XXX_EVENT_POST_CFILT_2_OFF;
+		break;
+	case WCD9XXX_CFILT3_SEL:
+		micb_cfilt_reg = WCD9XXX_A_MICB_CFILT_3_CTL;
+		e_pre_on = WCD9XXX_EVENT_PRE_CFILT_3_ON;
+		e_post_off = WCD9XXX_EVENT_POST_CFILT_3_OFF;
+		break;
+	default:
+		WARN(1, "Invalid CFILT selection %d\n", cfilt_sel);
+		return; /* should not happen */
+	}
+
+	if (inc) {
+		if ((resmgr->cfilt_users[cfilt_sel]++) == 0) {
+			/* Notify */
+			wcd9xxx_resmgr_notifier_call(resmgr, e_pre_on);
+			/* Enable CFILT */
+			snd_soc_update_bits(codec, micb_cfilt_reg, 0x80, 0x80);
+		}
+	} else {
+		/*
+		 * Check if count not zero, decrease
+		 * then check if zero, go ahead disable cfilter
+		 */
+		WARN(resmgr->cfilt_users[cfilt_sel] == 0,
+		     "Invalid CFILT use count 0\n");
+		if ((--resmgr->cfilt_users[cfilt_sel]) == 0) {
+			/* Disable CFILT */
+			snd_soc_update_bits(codec, micb_cfilt_reg, 0x80, 0);
+			/* Notify MBHC so MBHC can switch CFILT to fast mode */
+			wcd9xxx_resmgr_notifier_call(resmgr, e_post_off);
+		}
+	}
+}
+
+void wcd9xxx_resmgr_cfilt_get(struct wcd9xxx_resmgr *resmgr,
+			      enum wcd9xxx_cfilt_sel cfilt_sel)
+{
+	return wcd9xxx_resmgr_update_cfilt_usage(resmgr, cfilt_sel, true);
+}
+
+void wcd9xxx_resmgr_cfilt_put(struct wcd9xxx_resmgr *resmgr,
+			      enum wcd9xxx_cfilt_sel cfilt_sel)
+{
+	return wcd9xxx_resmgr_update_cfilt_usage(resmgr, cfilt_sel, false);
+}
+
+int wcd9xxx_resmgr_get_k_val(struct wcd9xxx_resmgr *resmgr,
+			     unsigned int cfilt_mv)
+{
+	int rc = -EINVAL;
+	unsigned int ldoh_v = resmgr->pdata->micbias.ldoh_v;
+	unsigned min_mv, max_mv;
+
+	switch (ldoh_v) {
+	case WCD9XXX_LDOH_1P95_V:
+		min_mv = 160;
+		max_mv = 1800;
+		break;
+	case WCD9XXX_LDOH_2P35_V:
+		min_mv = 200;
+		max_mv = 2200;
+		break;
+	case WCD9XXX_LDOH_2P75_V:
+		min_mv = 240;
+		max_mv = 2600;
+		break;
+	case WCD9XXX_LDOH_3P0_V:
+		min_mv = 260;
+		max_mv = 2875;
+		break;
+	default:
+		goto done;
+	}
+
+	if (cfilt_mv < min_mv || cfilt_mv > max_mv)
+		goto done;
+
+	for (rc = 4; rc <= 44; rc++) {
+		min_mv = max_mv * (rc) / 44;
+		if (min_mv >= cfilt_mv) {
+			rc -= 4;
+			break;
+		}
+	}
+done:
+	return rc;
+}
+
+int wcd9xxx_resmgr_register_notifier(struct wcd9xxx_resmgr *resmgr,
+				     struct notifier_block *nblock)
+{
+	return blocking_notifier_chain_register(&resmgr->notifier, nblock);
+}
+
+int wcd9xxx_resmgr_unregister_notifier(struct wcd9xxx_resmgr *resmgr,
+				       struct notifier_block *nblock)
+{
+	return blocking_notifier_chain_unregister(&resmgr->notifier, nblock);
+}
+
+int wcd9xxx_resmgr_init(struct wcd9xxx_resmgr *resmgr,
+			struct snd_soc_codec *codec,
+			struct wcd9xxx *wcd9xxx,
+			struct wcd9xxx_pdata *pdata,
+			struct wcd9xxx_reg_address *reg_addr)
+{
+	WARN(ARRAY_SIZE(wcd9xxx_event_string) != WCD9XXX_EVENT_LAST + 1,
+	     "Event string table isn't up to date!, %d != %d\n",
+	     ARRAY_SIZE(wcd9xxx_event_string), WCD9XXX_EVENT_LAST + 1);
+
+	resmgr->bandgap_type = WCD9XXX_BANDGAP_OFF;
+	resmgr->codec = codec;
+	/* This gives access of core handle to lock/unlock suspend */
+	resmgr->core = wcd9xxx;
+	resmgr->pdata = pdata;
+	resmgr->reg_addr = reg_addr;
+
+	BLOCKING_INIT_NOTIFIER_HEAD(&resmgr->notifier);
+
+	mutex_init(&resmgr->codec_resource_lock);
+
+	return 0;
+}
+
+void wcd9xxx_resmgr_deinit(struct wcd9xxx_resmgr *resmgr)
+{
+	mutex_destroy(&resmgr->codec_resource_lock);
+}
+
+void wcd9xxx_resmgr_bcl_lock(struct wcd9xxx_resmgr *resmgr)
+{
+	mutex_lock(&resmgr->codec_resource_lock);
+}
+
+void wcd9xxx_resmgr_bcl_unlock(struct wcd9xxx_resmgr *resmgr)
+{
+	mutex_unlock(&resmgr->codec_resource_lock);
+}
+
+MODULE_DESCRIPTION("wcd9xxx resmgr module");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/wcd9xxx-resmgr.h b/sound/soc/codecs/wcd9xxx-resmgr.h
new file mode 100644
index 0000000..2d04102
--- /dev/null
+++ b/sound/soc/codecs/wcd9xxx-resmgr.h
@@ -0,0 +1,191 @@
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#ifndef __WCD9XXX_COMMON_H__
+#define __WCD9XXX_COMMON_H__
+
+#include <linux/notifier.h>
+#include <linux/mfd/wcd9xxx/wcd9xxx_registers.h>
+
+enum wcd9xxx_bandgap_type {
+	WCD9XXX_BANDGAP_OFF,
+	WCD9XXX_BANDGAP_AUDIO_MODE,
+	WCD9XXX_BANDGAP_MBHC_MODE,
+};
+
+enum wcd9xxx_clock_type {
+	WCD9XXX_CLK_OFF,
+	WCD9XXX_CLK_RCO,
+	WCD9XXX_CLK_MCLK,
+};
+
+enum wcd9xxx_cfilt_sel {
+	WCD9XXX_CFILT1_SEL,
+	WCD9XXX_CFILT2_SEL,
+	WCD9XXX_CFILT3_SEL,
+	WCD9XXX_NUM_OF_CFILT,
+};
+
+struct wcd9xxx_reg_address {
+	u16 micb_4_ctl;
+	u16 micb_4_int_rbias;
+	u16 micb_4_mbhc;
+};
+
+enum wcd9xxx_notify_event {
+	WCD9XXX_EVENT_INVALID,
+
+	WCD9XXX_EVENT_PRE_RCO_ON,
+	WCD9XXX_EVENT_POST_RCO_ON,
+	WCD9XXX_EVENT_PRE_RCO_OFF,
+	WCD9XXX_EVENT_POST_RCO_OFF,
+
+	WCD9XXX_EVENT_PRE_MCLK_ON,
+	WCD9XXX_EVENT_POST_MCLK_ON,
+	WCD9XXX_EVENT_PRE_MCLK_OFF,
+	WCD9XXX_EVENT_POST_MCLK_OFF,
+
+	WCD9XXX_EVENT_PRE_BG_OFF,
+	WCD9XXX_EVENT_POST_BG_OFF,
+	WCD9XXX_EVENT_PRE_BG_AUDIO_ON,
+	WCD9XXX_EVENT_POST_BG_AUDIO_ON,
+	WCD9XXX_EVENT_PRE_BG_MBHC_ON,
+	WCD9XXX_EVENT_POST_BG_MBHC_ON,
+
+	WCD9XXX_EVENT_PRE_MICBIAS_1_OFF,
+	WCD9XXX_EVENT_POST_MICBIAS_1_OFF,
+	WCD9XXX_EVENT_PRE_MICBIAS_2_OFF,
+	WCD9XXX_EVENT_POST_MICBIAS_2_OFF,
+	WCD9XXX_EVENT_PRE_MICBIAS_3_OFF,
+	WCD9XXX_EVENT_POST_MICBIAS_3_OFF,
+	WCD9XXX_EVENT_PRE_MICBIAS_4_OFF,
+	WCD9XXX_EVENT_POST_MICBIAS_4_OFF,
+	WCD9XXX_EVENT_PRE_MICBIAS_1_ON,
+	WCD9XXX_EVENT_POST_MICBIAS_1_ON,
+	WCD9XXX_EVENT_PRE_MICBIAS_2_ON,
+	WCD9XXX_EVENT_POST_MICBIAS_2_ON,
+	WCD9XXX_EVENT_PRE_MICBIAS_3_ON,
+	WCD9XXX_EVENT_POST_MICBIAS_3_ON,
+	WCD9XXX_EVENT_PRE_MICBIAS_4_ON,
+	WCD9XXX_EVENT_POST_MICBIAS_4_ON,
+
+	WCD9XXX_EVENT_PRE_CFILT_1_OFF,
+	WCD9XXX_EVENT_POST_CFILT_1_OFF,
+	WCD9XXX_EVENT_PRE_CFILT_2_OFF,
+	WCD9XXX_EVENT_POST_CFILT_2_OFF,
+	WCD9XXX_EVENT_PRE_CFILT_3_OFF,
+	WCD9XXX_EVENT_POST_CFILT_3_OFF,
+	WCD9XXX_EVENT_PRE_CFILT_1_ON,
+	WCD9XXX_EVENT_POST_CFILT_1_ON,
+	WCD9XXX_EVENT_PRE_CFILT_2_ON,
+	WCD9XXX_EVENT_POST_CFILT_2_ON,
+	WCD9XXX_EVENT_PRE_CFILT_3_ON,
+	WCD9XXX_EVENT_POST_CFILT_3_ON,
+
+	WCD9XXX_EVENT_PRE_HPHL_PA_ON,
+	WCD9XXX_EVENT_POST_HPHL_PA_OFF,
+	WCD9XXX_EVENT_PRE_HPHR_PA_ON,
+	WCD9XXX_EVENT_POST_HPHR_PA_OFF,
+
+	WCD9XXX_EVENT_POST_RESUME,
+
+	WCD9XXX_EVENT_LAST,
+};
+
+struct wcd9xxx_resmgr {
+	struct snd_soc_codec *codec;
+	struct wcd9xxx *core;
+
+	u32 rx_bias_count;
+
+	enum wcd9xxx_bandgap_type bandgap_type;
+	u16 bg_audio_users;
+	u16 bg_mbhc_users;
+
+	enum wcd9xxx_clock_type clk_type;
+	u16 clk_rco_users;
+	u16 clk_mclk_users;
+
+	/* cfilt users per cfilts */
+	u16 cfilt_users[WCD9XXX_NUM_OF_CFILT];
+
+	struct wcd9xxx_reg_address *reg_addr;
+
+	struct wcd9xxx_pdata *pdata;
+
+	struct blocking_notifier_head notifier;
+	/* Notifier needs mbhc pointer with resmgr */
+	struct wcd9xxx_mbhc *mbhc;
+
+	/*
+	 * Currently, only used for mbhc purpose, to protect
+	 * concurrent execution of mbhc threaded irq handlers and
+	 * kill race between DAPM and MBHC. But can serve as a
+	 * general lock to protect codec resource
+	 */
+	struct mutex codec_resource_lock;
+};
+
+int wcd9xxx_resmgr_init(struct wcd9xxx_resmgr *resmgr,
+			struct snd_soc_codec *codec,
+			struct wcd9xxx *wcd9xxx,
+			struct wcd9xxx_pdata *pdata,
+			struct wcd9xxx_reg_address *reg_addr);
+void wcd9xxx_resmgr_deinit(struct wcd9xxx_resmgr *resmgr);
+
+int wcd9xxx_resmgr_enable_config_mode(struct snd_soc_codec *codec, int enable);
+
+void wcd9xxx_resmgr_enable_rx_bias(struct wcd9xxx_resmgr *resmgr, u32 enable);
+void wcd9xxx_resmgr_get_clk_block(struct wcd9xxx_resmgr *resmgr,
+				  enum wcd9xxx_clock_type type);
+void wcd9xxx_resmgr_put_clk_block(struct wcd9xxx_resmgr *resmgr,
+				  enum wcd9xxx_clock_type type);
+void wcd9xxx_resmgr_get_bandgap(struct wcd9xxx_resmgr *resmgr,
+				const enum wcd9xxx_bandgap_type choice);
+void wcd9xxx_resmgr_put_bandgap(struct wcd9xxx_resmgr *resmgr,
+				enum wcd9xxx_bandgap_type choice);
+void wcd9xxx_resmgr_cfilt_get(struct wcd9xxx_resmgr *resmgr,
+			      enum wcd9xxx_cfilt_sel cfilt_sel);
+void wcd9xxx_resmgr_cfilt_put(struct wcd9xxx_resmgr *resmgr,
+			      enum wcd9xxx_cfilt_sel cfilt_sel);
+
+void wcd9xxx_resmgr_bcl_lock(struct wcd9xxx_resmgr *resmgr);
+#define WCD9XXX_BCL_LOCK(resmgr)			\
+{							\
+	pr_debug("%s: Acquiring BCL\n", __func__);	\
+	wcd9xxx_resmgr_bcl_lock(resmgr);			\
+	pr_debug("%s: Acquiring BCL done\n", __func__);	\
+}
+
+void wcd9xxx_resmgr_bcl_unlock(struct wcd9xxx_resmgr *resmgr);
+#define WCD9XXX_BCL_UNLOCK(resmgr)			\
+{							\
+	pr_debug("%s: Release BCL\n", __func__);	\
+	wcd9xxx_resmgr_bcl_unlock(resmgr);			\
+}
+
+#define WCD9XXX_BCL_ASSERT_LOCKED(resmgr)		\
+{							\
+	WARN_ONCE(!mutex_is_locked(&resmgr->codec_resource_lock), \
+		  "%s: BCL should have acquired\n", __func__); \
+}
+
+const char *wcd9xxx_get_event_string(enum wcd9xxx_notify_event type);
+int wcd9xxx_resmgr_get_k_val(struct wcd9xxx_resmgr *resmgr,
+			     unsigned int cfilt_mv);
+int wcd9xxx_resmgr_register_notifier(struct wcd9xxx_resmgr *resmgr,
+				     struct notifier_block *nblock);
+int wcd9xxx_resmgr_unregister_notifier(struct wcd9xxx_resmgr *resmgr,
+				       struct notifier_block *nblock);
+void wcd9xxx_resmgr_notifier_call(struct wcd9xxx_resmgr *resmgr,
+				  const enum wcd9xxx_notify_event e);
+
+#endif /* __WCD9XXX_COMMON_H__ */
diff --git a/sound/soc/msm/apq8064-i2s.c b/sound/soc/msm/apq8064-i2s.c
index e309370..795b421 100644
--- a/sound/soc/msm/apq8064-i2s.c
+++ b/sound/soc/msm/apq8064-i2s.c
@@ -2690,8 +2690,7 @@
 
 static void __exit msm_audio_exit(void)
 {
-	if (!(cpu_is_apq8064() || cpu_is_apq8064ab()) ||
-				 (socinfo_get_id() == 130)) {
+	if (!soc_class_is_apq8064() || socinfo_get_id() == 130) {
 		pr_err("%s: Not the right machine type\n", __func__);
 		return ;
 	}
diff --git a/sound/soc/msm/apq8064.c b/sound/soc/msm/apq8064.c
index b921df1..4fe002b 100644
--- a/sound/soc/msm/apq8064.c
+++ b/sound/soc/msm/apq8064.c
@@ -859,7 +859,6 @@
 	unsigned int rx_ch_cnt = 0, tx_ch_cnt = 0;
 	unsigned int num_tx_ch = 0;
 
-
 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
 
 		pr_debug("%s: rx_0_ch=%d\n", __func__, msm_slim_0_rx_ch);
@@ -877,25 +876,7 @@
 			pr_err("%s: failed to set cpu chan map\n", __func__);
 			goto end;
 		}
-		ret = snd_soc_dai_set_channel_map(codec_dai, 0, 0,
-				msm_slim_0_rx_ch, rx_ch);
-		if (ret < 0) {
-			pr_err("%s: failed to set codec channel map\n",
-								__func__);
-			goto end;
-		}
 	} else {
-
-		if (codec_dai->id  == 2)
-			num_tx_ch =  msm_slim_0_tx_ch;
-		else if (codec_dai->id == 5) {
-			/* DAI 5 is used for external EC reference from codec.
-			 * Since Rx is fed as reference for EC, the config of
-			 * this DAI is based on that of the Rx path.
-			 */
-			num_tx_ch =  msm_slim_0_rx_ch;
-		}
-
 		pr_debug("%s: %s_tx_dai_id_%d_ch=%d\n", __func__,
 			codec_dai->name, codec_dai->id, num_tx_ch);
 
@@ -905,6 +886,19 @@
 			pr_err("%s: failed to get codec chan map\n", __func__);
 			goto end;
 		}
+		/* For tabla_tx1 case */
+		if (codec_dai->id  == 1)
+			num_tx_ch =  msm_slim_0_tx_ch;
+		/* For tabla_tx3 case */
+		else if (codec_dai->id == 4) {
+			/* DAI 5 is used for external EC reference from codec.
+			 * Since Rx is fed as reference for EC, the config of
+			 * this DAI is based on that of the Rx path.
+			 */
+			num_tx_ch =  msm_slim_0_rx_ch;
+		} else {
+			num_tx_ch = tx_ch_cnt;
+		}
 
 		ret = snd_soc_dai_set_channel_map(cpu_dai,
 				num_tx_ch, tx_ch, 0 , 0);
@@ -912,15 +906,6 @@
 			pr_err("%s: failed to set cpu chan map\n", __func__);
 			goto end;
 		}
-		ret = snd_soc_dai_set_channel_map(codec_dai,
-				num_tx_ch, tx_ch, 0, 0);
-		if (ret < 0) {
-			pr_err("%s: failed to set codec channel map\n",
-								__func__);
-			goto end;
-		}
-
-
 	}
 end:
 	return ret;
@@ -965,13 +950,6 @@
 			pr_err("%s: failed to set cpu chan map\n", __func__);
 			goto end;
 		}
-		ret = snd_soc_dai_set_channel_map(codec_dai, 0, 0,
-				num_rx_ch, rx_ch);
-		if (ret < 0) {
-			pr_err("%s: failed to set codec channel map\n",
-								__func__);
-			goto end;
-		}
 	} else {
 
 		num_tx_ch =  params_channels(params);
@@ -992,13 +970,6 @@
 			pr_err("%s: failed to set cpu chan map\n", __func__);
 			goto end;
 		}
-		ret = snd_soc_dai_set_channel_map(codec_dai,
-				num_tx_ch, tx_ch, 0, 0);
-		if (ret < 0) {
-			pr_err("%s: failed to set codec channel map\n",
-								__func__);
-			goto end;
-		}
 	}
 end:
 	return ret;
@@ -1128,14 +1099,14 @@
 	struct snd_soc_codec *codec = rtd->codec;
 	struct snd_soc_dapm_context *dapm = &codec->dapm;
 	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+	struct snd_soc_dai *codec_dai = rtd->codec_dai;
+	unsigned int rx_ch[TABLA_RX_MAX] = {138, 139, 140, 141, 142, 143, 144};
+	unsigned int tx_ch[TABLA_TX_MAX] = {128, 129, 130, 131, 132, 133, 134,
+					    135, 136, 137};
+
 
 	pr_debug("%s(), dev_name%s\n", __func__, dev_name(cpu_dai->dev));
 
-	/*if (machine_is_msm_liquid()) {
-		top_spk_pamp_gpio = (PM8921_GPIO_PM_TO_SYS(19));
-		bottom_spk_pamp_gpio = (PM8921_GPIO_PM_TO_SYS(18));
-	}*/
-
 	snd_soc_dapm_new_controls(dapm, apq8064_dapm_widgets,
 				ARRAY_SIZE(apq8064_dapm_widgets));
 
@@ -1221,6 +1192,8 @@
 	mbhc_cfg.read_fw_bin = apq8064_hs_detect_use_firmware;
 
 	err = tabla_hs_detect(codec, &mbhc_cfg);
+	snd_soc_dai_set_channel_map(codec_dai, ARRAY_SIZE(tx_ch),
+				    tx_ch, ARRAY_SIZE(rx_ch), rx_ch);
 
 	return err;
 }
@@ -1353,6 +1326,8 @@
 	pr_debug("%s channels->min %u channels->max %u ()\n", __func__,
 			channels->min, channels->max);
 
+	if (channels->max < 2)
+		channels->min = channels->max = 2;
 	rate->min = rate->max = 48000;
 
 	return 0;
@@ -2101,7 +2076,7 @@
 {
 	int ret;
 	u32	version = socinfo_get_platform_version();
-	if (!(cpu_is_apq8064() || cpu_is_apq8064ab()) ||
+	if (!soc_class_is_apq8064() ||
 		(socinfo_get_id() == 130) ||
 		(machine_is_apq8064_mtp() &&
 		(SOCINFO_VERSION_MINOR(version) == 1))) {
@@ -2142,8 +2117,7 @@
 
 static void __exit msm_audio_exit(void)
 {
-	if (!(cpu_is_apq8064() || cpu_is_apq8064ab()) ||
-				 (socinfo_get_id() == 130)) {
+	if (!soc_class_is_apq8064() || socinfo_get_id() == 130) {
 		pr_err("%s: Not the right machine type\n", __func__);
 		return ;
 	}
diff --git a/sound/soc/msm/mdm9615.c b/sound/soc/msm/mdm9615.c
index 1000a8b..5a47efe 100644
--- a/sound/soc/msm/mdm9615.c
+++ b/sound/soc/msm/mdm9615.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -89,6 +89,20 @@
 	.drv = GPIOMUX_DRV_8MA,
 	.pull = GPIOMUX_PULL_NONE,
 };
+static struct gpiomux_setting audio_sec_i2s[] = {
+	/* Suspend state */
+	{
+		.func = GPIOMUX_FUNC_GPIO,
+		.drv  = GPIOMUX_DRV_2MA,
+		.pull = GPIOMUX_PULL_DOWN,
+	},
+	/* Active state */
+	{
+		.func = GPIOMUX_FUNC_2,
+		.drv  = GPIOMUX_DRV_8MA,
+		.pull = GPIOMUX_PULL_NONE,
+	}
+};
 
 static struct gpiomux_setting cdc_i2s_dout = {
 	.func = GPIOMUX_FUNC_1,
@@ -142,6 +156,42 @@
 	},
 };
 
+static struct msm_gpiomux_config msm9615_audio_sec_i2s_codec_configs[] = {
+	{
+		.gpio = GPIO_SPKR_I2S_MCLK,
+		.settings = {
+			[GPIOMUX_SUSPENDED] = &cdc_i2s_mclk,
+		},
+	},
+	{
+		.gpio = GPIO_SEC_I2S_SCK,
+		.settings = {
+			[GPIOMUX_SUSPENDED] = &audio_sec_i2s[0],
+			[GPIOMUX_ACTIVE] = &audio_sec_i2s[1],
+		},
+	},
+	{
+		.gpio = GPIO_SEC_I2S_DOUT,
+		.settings = {
+			[GPIOMUX_SUSPENDED] = &audio_sec_i2s[0],
+			[GPIOMUX_ACTIVE] = &audio_sec_i2s[1],
+		},
+	},
+	{
+		.gpio = GPIO_SEC_I2S_WS,
+		.settings = {
+			[GPIOMUX_SUSPENDED] = &audio_sec_i2s[0],
+			[GPIOMUX_ACTIVE] = &audio_sec_i2s[1],
+		},
+	},
+	{
+		.gpio = GPIO_SEC_I2S_DIN,
+		.settings = {
+			[GPIOMUX_SUSPENDED] = &audio_sec_i2s[0],
+			[GPIOMUX_ACTIVE] = &audio_sec_i2s[1],
+		},
+	},
+};
 /* Physical address for LPA CSR
  * LPA SIF mux registers. These are
  * ioremap( ) for Virtual address.
@@ -274,6 +324,9 @@
 static struct platform_device *mdm9615_snd_device_slim;
 static struct platform_device *mdm9615_snd_device_i2s;
 
+static u32 sif_reg_value   = 0x0000;
+static u32 spare_reg_value = 0x0000;
+
 static bool hs_detect_use_gpio;
 module_param(hs_detect_use_gpio, bool, 0444);
 MODULE_PARM_DESC(hs_detect_use_gpio, "Use GPIO for headset detection");
@@ -862,13 +915,6 @@
 			pr_err("%s: failed to set cpu chan map\n", __func__);
 			goto end;
 		}
-		ret = snd_soc_dai_set_channel_map(codec_dai, 0, 0,
-				mdm9615_slim_0_rx_ch, rx_ch);
-		if (ret < 0) {
-			pr_err("%s: failed to set codec channel map\n",
-								__func__);
-			goto end;
-		}
 	} else {
 		ret = snd_soc_dai_get_channel_map(codec_dai,
 				&tx_ch_cnt, tx_ch, &rx_ch_cnt , rx_ch);
@@ -882,13 +928,6 @@
 			pr_err("%s: failed to set cpu chan map\n", __func__);
 			goto end;
 		}
-		ret = snd_soc_dai_set_channel_map(codec_dai,
-				mdm9615_slim_0_tx_ch, tx_ch, 0, 0);
-		if (ret < 0) {
-			pr_err("%s: failed to set codec channel map\n",
-								__func__);
-			goto end;
-		}
 	}
 end:
 	return ret;
@@ -947,6 +986,10 @@
 		     msm9615_i2s_rx_ch_get, msm9615_i2s_rx_ch_put),
 	SOC_ENUM_EXT("PRI_TX Channels", mdm9615_enum[2],
 		     msm9615_i2s_tx_ch_get, msm9615_i2s_tx_ch_put),
+	SOC_ENUM_EXT("SEC_RX Channels", mdm9615_enum[3],
+			msm9615_i2s_rx_ch_get, msm9615_i2s_rx_ch_put),
+	SOC_ENUM_EXT("SEC_TX Channels", mdm9615_enum[4],
+			msm9615_i2s_tx_ch_get, msm9615_i2s_tx_ch_put),
 };
 
 static int msm9615_i2s_audrx_init(struct snd_soc_pcm_runtime *rtd)
@@ -1234,47 +1277,64 @@
 	return ret;
 }
 
-static void msm9615_config_i2s_sif_mux(u8 value)
+static void msm9615_config_i2s_sif_mux(u8 value, u8 i2s_intf)
 {
 	struct msm_i2s_ctl *pintf = &msm9x15_i2s_ctl;
-	u32 sif_shadow  = 0x0000;
-	/* Make this variable global if both secondary and
-	 * primary needs to be supported. This is required
-	 * to retain bits in interace and set only specific
-	 * bits in the register. Also set Sec Intf bits.
-	 * Secondary interface bits are 0,1.
-	 **/
-	sif_shadow = (sif_shadow & LPASS_SIF_MUX_CTL_PRI_MUX_SEL_BMSK) |
-		     (value << LPASS_SIF_MUX_CTL_PRI_MUX_SEL_SHFT);
+	u32 sif_shadow = 0x0000;
+
+	pr_debug("%s() Value = 0x%x intf = 0x%x\n", __func__, value, i2s_intf);
+	if (i2s_intf == MSM_INTF_PRIM) {
+		sif_shadow = (sif_shadow & LPASS_SIF_MUX_CTL_PRI_MUX_SEL_BMSK) |
+			     (value << LPASS_SIF_MUX_CTL_PRI_MUX_SEL_SHFT);
+		pr_debug("%s() Sif shadow = 0x%x\n", __func__, sif_shadow);
+		sif_reg_value =
+			((sif_reg_value & LPASS_SIF_MUX_CTL_SEC_MUX_SEL_BMSK) |
+			 sif_shadow);
+	}
+	if (i2s_intf == MSM_INTF_SECN) {
+		sif_shadow = (sif_shadow & LPASS_SIF_MUX_CTL_SEC_MUX_SEL_BMSK) |
+				(value << LPASS_SIF_MUX_CTL_SEC_MUX_SEL_SHFT);
+		pr_debug("%s() Sif shadow = 0x%x\n", __func__, sif_shadow);
+		sif_reg_value =
+			((sif_reg_value & LPASS_SIF_MUX_CTL_PRI_MUX_SEL_BMSK) |
+			sif_shadow);
+	}
 	if (pintf->sif_virt_addr != NULL)
-		iowrite32(sif_shadow, pintf->sif_virt_addr);
+		iowrite32(sif_reg_value, pintf->sif_virt_addr);
 	/* Dont read SIF register. Device crashes. */
-	pr_debug("%s() SIF Reg = 0x%x\n", __func__, sif_shadow);
+	pr_debug("%s() SIF Reg = 0x%x\n", __func__, sif_reg_value);
 }
 
 static void msm9615_config_i2s_spare_mux(u8 value, u8 i2s_intf)
 {
 	struct msm_i2s_ctl *pintf = &msm9x15_i2s_ctl;
 	u32 spare_shadow = 0x0000;
-	/* Make this variable global if both secondary and
-	 * primary needs to be supported. This is required
-	 * to retain bits in interace and set only specific
-	 * bits in the register. Also set Sec Intf bits.
-	 **/
+
+	pr_debug("%s() Value = 0x%x intf = 0x%x\n", __func__, value, i2s_intf);
 	if (i2s_intf == MSM_INTF_PRIM) {
 		/* Configure Primary SIF */
-	    spare_shadow = (spare_shadow & LPAIF_SPARE_MUX_CTL_PRI_MUX_SEL_BMSK
-			   ) | (value << LPAIF_SPARE_MUX_CTL_PRI_MUX_SEL_SHFT);
+		spare_shadow =
+			(spare_shadow & LPAIF_SPARE_MUX_CTL_PRI_MUX_SEL_BMSK) |
+			(value << LPAIF_SPARE_MUX_CTL_PRI_MUX_SEL_SHFT);
+		pr_debug("%s() Spare shadow = 0x%x\n", __func__, spare_shadow);
+		spare_reg_value =
+			((spare_shadow & LPAIF_SPARE_MUX_CTL_SEC_MUX_SEL_BMSK) |
+			spare_shadow);
 	}
 	if (i2s_intf == MSM_INTF_SECN) {
 		/*Secondary interface configuration*/
-	    spare_shadow = (spare_shadow & LPAIF_SPARE_MUX_CTL_SEC_MUX_SEL_BMSK
-			   ) | (value << LPAIF_SPARE_MUX_CTL_SEC_MUX_SEL_SHFT);
+		spare_shadow =
+			(spare_shadow & LPAIF_SPARE_MUX_CTL_SEC_MUX_SEL_BMSK) |
+			(value << LPAIF_SPARE_MUX_CTL_SEC_MUX_SEL_SHFT);
+		pr_debug("%s() Spare shadow = 0x%x\n", __func__, spare_shadow);
+		spare_reg_value =
+			((spare_shadow & LPAIF_SPARE_MUX_CTL_PRI_MUX_SEL_BMSK) |
+			spare_shadow);
 	}
 	if (pintf->spare_virt_addr != NULL)
-		iowrite32(spare_shadow, pintf->spare_virt_addr);
+		iowrite32(spare_reg_value, pintf->spare_virt_addr);
 	/* Dont read SPARE register. Device crashes. */
-	pr_debug("%s( ): SPARE Reg =0x%x\n", __func__, spare_shadow);
+	pr_debug("%s( ): SPARE Reg =0x%x\n", __func__, spare_reg_value);
 }
 
 static int msm9615_i2s_hw_params(struct snd_pcm_substream *substream,
@@ -1342,7 +1402,8 @@
 					return -EINVAL;
 				}
 				msm9615_config_i2s_sif_mux(
-				       pintf->mux_ctl[MSM_DIR_BOTH].sifconfig);
+				       pintf->mux_ctl[MSM_DIR_BOTH].sifconfig,
+					i2s_intf);
 				msm9615_config_i2s_spare_mux(
 				      pintf->mux_ctl[MSM_DIR_BOTH].spareconfig,
 				      i2s_intf);
@@ -1368,7 +1429,8 @@
 					return -EINVAL;
 				}
 				msm9615_config_i2s_sif_mux(
-					pintf->mux_ctl[MSM_DIR_TX].sifconfig);
+					pintf->mux_ctl[MSM_DIR_TX].sifconfig,
+					i2s_intf);
 				msm9615_config_i2s_spare_mux(
 					pintf->mux_ctl[MSM_DIR_TX].spareconfig,
 					i2s_intf);
@@ -1397,7 +1459,8 @@
 					return -EINVAL;
 				}
 				msm9615_config_i2s_sif_mux(
-					pintf->mux_ctl[MSM_DIR_RX].sifconfig);
+					pintf->mux_ctl[MSM_DIR_RX].sifconfig,
+					i2s_intf);
 				msm9615_config_i2s_spare_mux(
 					pintf->mux_ctl[MSM_DIR_RX].spareconfig,
 					i2s_intf);
@@ -1451,11 +1514,34 @@
 		 pintf->intf_status[i2s_intf][MSM_DIR_TX]);
 }
 
-static void  mdm9615_install_codec_i2s_gpio(void)
+void msm9615_config_port_select(void)
 {
-	msm_gpiomux_install(msm9615_audio_prim_i2s_codec_configs,
-			ARRAY_SIZE(msm9615_audio_prim_i2s_codec_configs));
+	iowrite32(SEC_PCM_PORT_SLC_VALUE, secpcm_portslc_virt_addr);
+	pr_debug("%s() port select after updating = 0x%x\n",
+		__func__, ioread32(secpcm_portslc_virt_addr));
 }
+static void  mdm9615_install_codec_i2s_gpio(struct snd_pcm_substream *substream)
+{
+	u8 i2s_intf, i2s_dir;
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+
+	if (!msm9615_i2s_intf_dir_sel(cpu_dai->name, &i2s_intf, &i2s_dir)) {
+		pr_debug("%s( ): cpu name = %s intf =%d dir = %d\n",
+			 __func__, cpu_dai->name, i2s_intf, i2s_dir);
+		if (i2s_intf == MSM_INTF_PRIM) {
+			msm_gpiomux_install(
+			msm9615_audio_prim_i2s_codec_configs,
+			ARRAY_SIZE(msm9615_audio_prim_i2s_codec_configs));
+		} else if (i2s_intf == MSM_INTF_SECN) {
+			msm_gpiomux_install(msm9615_audio_sec_i2s_codec_configs,
+			ARRAY_SIZE(msm9615_audio_sec_i2s_codec_configs));
+			msm9615_config_port_select();
+
+		}
+	}
+}
+
 static int msm9615_i2s_prepare(struct snd_pcm_substream *substream)
 {
 	u8 ret = 0;
@@ -1463,7 +1549,7 @@
 	if (wcd9xxx_get_intf_type() < 0)
 		ret = -ENODEV;
 	else if (wcd9xxx_get_intf_type() == WCD9XXX_INTERFACE_TYPE_I2C)
-		mdm9615_install_codec_i2s_gpio();
+		mdm9615_install_codec_i2s_gpio(substream);
 
 	return ret;
 }
@@ -1488,6 +1574,15 @@
 		.vin_sel = 2,
 		.inv_int_pol = 0,
 	};
+	struct snd_soc_dai *codec_dai = rtd->codec_dai;
+
+	/* Tabla SLIMBUS configuration
+	 * RX1, RX2, RX3, RX4, RX5, RX6, RX7
+	 * TX1, TX2, TX3, TX4, TX5, TX6, TX7, TX8, TX9, TX10
+	 */
+	unsigned int rx_ch[TABLA_RX_MAX] = {138, 139, 140, 141, 142, 143, 144};
+	unsigned int tx_ch[TABLA_TX_MAX]  = {128, 129, 130, 131, 132, 133, 134,
+					     135, 136, 137};
 
 	pr_debug("%s(), dev_name%s\n", __func__, dev_name(cpu_dai->dev));
 
@@ -1539,6 +1634,10 @@
 
 	err = tabla_hs_detect(codec, &mbhc_cfg);
 
+	snd_soc_dai_set_channel_map(codec_dai, ARRAY_SIZE(tx_ch),
+				    tx_ch, ARRAY_SIZE(rx_ch), rx_ch);
+
+
 	return err;
 }
 
@@ -1734,15 +1833,6 @@
 	pr_debug("%s() SIF Reg = 0x%x\n", __func__, sif_shadow);
 }
 
-void msm9615_config_port_select(void)
-{
-	pr_debug("%s() port select defualt = 0x%x\n",
-		 __func__, ioread32(secpcm_portslc_virt_addr));
-	iowrite32(SEC_PCM_PORT_SLC_VALUE, secpcm_portslc_virt_addr);
-	pr_debug("%s() port select after updating = 0x%x\n",
-		 __func__, ioread32(secpcm_portslc_virt_addr));
-}
-
 static int mdm9615_auxpcm_startup(struct snd_pcm_substream *substream)
 {
 	int ret = 0;
@@ -2049,6 +2139,30 @@
 		.be_hw_params_fixup = msm9615_i2s_tx_be_hw_params_fixup,
 		.ops = &msm9615_i2s_be_ops,
 	},
+	{
+		.name = LPASS_BE_SEC_I2S_RX,
+		.stream_name = "Secondary I2S Playback",
+		.cpu_dai_name = "msm-dai-q6.4",
+		.platform_name = "msm-pcm-routing",
+		.codec_name     = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-rx",
+		.no_pcm = 1,
+		.be_id = MSM_BACKEND_DAI_SEC_I2S_RX,
+		.be_hw_params_fixup = msm9615_i2s_rx_be_hw_params_fixup,
+		.ops = &msm9615_i2s_be_ops,
+	},
+	{
+		.name = LPASS_BE_SEC_I2S_TX,
+		.stream_name = "Secondary I2S Capture",
+		.cpu_dai_name = "msm-dai-q6.5",
+		.platform_name = "msm-pcm-routing",
+		.codec_name     = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-tx",
+		.no_pcm = 1,
+		.be_id = MSM_BACKEND_DAI_SEC_I2S_TX,
+		.be_hw_params_fixup = msm9615_i2s_tx_be_hw_params_fixup,
+		.ops = &msm9615_i2s_be_ops,
+	},
 };
 
 static struct snd_soc_dai_link mdm9615_dai_slimbus_tabla[] = {
@@ -2097,8 +2211,8 @@
 	},
 	[1] = {
 		.name = "mdm9615-tabla-snd-card-i2s",
-		.controls = tabla_mdm9615_controls,
-		.num_controls = ARRAY_SIZE(tabla_mdm9615_controls),
+		.controls = tabla_msm9615_i2s_controls,
+		.num_controls = ARRAY_SIZE(tabla_msm9615_i2s_controls),
 	},
 };
 
diff --git a/sound/soc/msm/mpq8064.c b/sound/soc/msm/mpq8064.c
index 3cb7c58..90c96b4 100644
--- a/sound/soc/msm/mpq8064.c
+++ b/sound/soc/msm/mpq8064.c
@@ -140,11 +140,10 @@
 static int msm_slim_0_tx_ch = 1;
 static int msm_hdmi_rx_ch = 8;
 static int mi2s_rate_variable;
+static int hdmi_rate_variable;
 static struct clk *codec_clk;
 static int clk_users;
 
-static int msm_headset_gpios_configured;
-
 static struct snd_soc_jack hs_jack;
 static struct snd_soc_jack button_jack;
 
@@ -523,6 +522,8 @@
 static const char * const hdmi_rx_ch_text[] = {"Two", "Three", "Four",
 					"Five", "Six", "Seven", "Eight"};
 static const char * const mi2s_rate[] = {"Default", "Variable"};
+static const char * const hdmi_rate[] = {"Default", "Variable"};
+
 
 
 static const struct soc_enum msm_enum[] = {
@@ -531,6 +532,7 @@
 	SOC_ENUM_SINGLE_EXT(4, slim0_tx_ch_text),
 	SOC_ENUM_SINGLE_EXT(7, hdmi_rx_ch_text),
 	SOC_ENUM_SINGLE_EXT(2, mi2s_rate),
+	SOC_ENUM_SINGLE_EXT(2, hdmi_rate),
 
 };
 
@@ -606,6 +608,21 @@
 	return 0;
 }
 
+static int msm_hdmi_rate_put(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	hdmi_rate_variable = ucontrol->value.integer.value[0];
+	pr_debug("%s: hdmi_rate_variable = %d\n", __func__, hdmi_rate_variable);
+	return 0;
+}
+
+static int msm_hdmi_rate_get(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	ucontrol->value.integer.value[0] = hdmi_rate_variable;
+	return 0;
+}
+
 static const struct snd_kcontrol_new tabla_msm_controls[] = {
 	SOC_ENUM_EXT("Speaker Function", msm_enum[0], msm_get_spk,
 		msm_set_spk),
@@ -618,6 +635,9 @@
 	SOC_ENUM_EXT("SEC RX Rate", msm_enum[4],
 					msm_mi2s_rate_get,
 					msm_mi2s_rate_put),
+	SOC_ENUM_EXT("HDMI RX Rate", msm_enum[5],
+					msm_hdmi_rate_get,
+					msm_hdmi_rate_put),
 
 };
 
@@ -724,13 +744,6 @@
 			pr_err("%s: failed to set cpu chan map\n", __func__);
 			goto end;
 		}
-		ret = snd_soc_dai_set_channel_map(codec_dai, 0, 0,
-				msm_slim_0_rx_ch, rx_ch);
-		if (ret < 0) {
-			pr_err("%s: failed to set codec channel map\n",
-								__func__);
-			goto end;
-		}
 	} else {
 		ret = snd_soc_dai_get_channel_map(codec_dai,
 				&tx_ch_cnt, tx_ch, &rx_ch_cnt , rx_ch);
@@ -744,14 +757,6 @@
 			pr_err("%s: failed to set cpu chan map\n", __func__);
 			goto end;
 		}
-		ret = snd_soc_dai_set_channel_map(codec_dai,
-				msm_slim_0_tx_ch, tx_ch, 0, 0);
-		if (ret < 0) {
-			pr_err("%s: failed to set codec channel map\n",
-								__func__);
-			goto end;
-		}
-
 
 	}
 end:
@@ -764,6 +769,10 @@
 	struct snd_soc_codec *codec = rtd->codec;
 	struct snd_soc_dapm_context *dapm = &codec->dapm;
 	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+	struct snd_soc_dai *codec_dai = rtd->codec_dai;
+	unsigned int rx_ch[TABLA_RX_MAX] = {138, 139, 140, 141, 142, 143, 144};
+	unsigned int tx_ch[TABLA_TX_MAX] = {128, 129, 130, 131, 132, 133, 134,
+					    135, 136, 137};
 
 	pr_debug("%s(), dev_name%s\n", __func__, dev_name(cpu_dai->dev));
 
@@ -798,6 +807,8 @@
 	codec_clk = clk_get(cpu_dai->dev, "osr_clk");
 
 	err = tabla_hs_detect(codec, &mbhc_cfg);
+	snd_soc_dai_set_channel_map(codec_dai, ARRAY_SIZE(tx_ch),
+				    tx_ch, ARRAY_SIZE(rx_ch), rx_ch);
 
 	return err;
 }
@@ -834,6 +845,21 @@
 	return 0;
 }
 
+static int mpq8064_proxy_be_params_fixup(struct snd_soc_pcm_runtime *rtd,
+			struct snd_pcm_hw_params *params)
+{
+	struct snd_interval *rate = hw_param_interval(params,
+					SNDRV_PCM_HW_PARAM_RATE);
+
+	struct snd_interval *channels = hw_param_interval(params,
+					SNDRV_PCM_HW_PARAM_CHANNELS);
+	pr_debug("%s ()\n", __func__);
+	rate->min = rate->max = 48000;
+	channels->min =  channels->max = 2;
+
+	return 0;
+}
+
 static int msm_be_i2s_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
 			struct snd_pcm_hw_params *params)
 {
@@ -876,7 +902,9 @@
 	pr_debug("%s channels->min %u channels->max %u ()\n", __func__,
 			channels->min, channels->max);
 
-	rate->min = rate->max = 48000;
+	/*Configure the sample rate as 48000 KHz for the LPCM playback*/
+	if (!hdmi_rate_variable)
+		rate->min = rate->max = 48000;
 	channels->min =  channels->max = msm_hdmi_rx_ch;
 
 	return 0;
@@ -1374,20 +1402,6 @@
 		.codec_dai_name = "snd-soc-dummy-dai",
 		.codec_name = "snd-soc-dummy",
 	},
-	{
-		.name = "AUXPCM Hostless",
-		.stream_name = "AUXPCM Hostless",
-		.cpu_dai_name	= "AUXPCM_HOSTLESS",
-		.platform_name	= "msm-pcm-hostless",
-		.dynamic = 1,
-		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
-				SND_SOC_DPCM_TRIGGER_POST},
-		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
-		.ignore_suspend = 1,
-		.ignore_pmdown_time = 1, /* dainlink has playback support */
-		.codec_dai_name = "snd-soc-dummy-dai",
-		.codec_name = "snd-soc-dummy",
-	},
 	/* Secondary I2S RX Hostless */
 	{
 		.name = "SEC_I2S_RX Hostless",
@@ -1459,6 +1473,34 @@
 		.ignore_pmdown_time = 1, /* this dailink has playback support */
 		.be_id = MSM_FRONTEND_DAI_MULTIMEDIA8,
 	},
+	{
+		.name = "AUXPCM Hostless",
+		.stream_name = "AUXPCM Hostless",
+		.cpu_dai_name	= "AUXPCM_HOSTLESS",
+		.platform_name	= "msm-pcm-hostless",
+		.dynamic = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+				SND_SOC_DPCM_TRIGGER_POST},
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1, /* dainlink has playback support */
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+
+	},
+	{
+		.name = "MSM8960 Pseudo",
+		.stream_name = "Pseudo",
+		.cpu_dai_name   = "Pseudo",
+		.dynamic = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+				SND_SOC_DPCM_TRIGGER_POST},
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		.be_id = MSM_FRONTEND_DAI_PSEUDO,
+	},
 	/* Backend DAI Links */
 	{
 		.name = LPASS_BE_SLIMBUS_0_RX,
@@ -1557,6 +1599,7 @@
 		.codec_dai_name = "msm-stub-rx",
 		.no_pcm = 1,
 		.be_id = MSM_BACKEND_DAI_AFE_PCM_RX,
+		.be_hw_params_fixup = mpq8064_proxy_be_params_fixup,
 		.ignore_pmdown_time = 1, /* this dainlink has playback support */
 	},
 	{
@@ -1567,6 +1610,7 @@
 		.codec_name = "msm-stub-codec.1",
 		.codec_dai_name = "msm-stub-tx",
 		.no_pcm = 1,
+		.be_hw_params_fixup = mpq8064_proxy_be_params_fixup,
 		.be_id = MSM_BACKEND_DAI_AFE_PCM_TX,
 	},
 	/* AUX PCM Backend DAI Links */
@@ -1594,6 +1638,18 @@
 		.be_id = MSM_BACKEND_DAI_AUXPCM_TX,
 		.be_hw_params_fixup = mpq8064_auxpcm_be_params_fixup,
 	},
+	{
+		.name = LPASS_BE_PSEUDO,
+		.stream_name = "PSEUDO Playback",
+		.cpu_dai_name = "msm-dai-q6.32769",
+		.platform_name = "msm-pcm-routing",
+		.codec_name     = "snd-soc-dummy",
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.no_pcm = 1,
+		.be_id = MSM_BACKEND_DAI_PSEUDO_PORT,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.ignore_pmdown_time = 1,
+	},
 };
 
 
@@ -1607,57 +1663,6 @@
 
 static struct platform_device *msm_snd_device;
 
-static int msm_configure_headset_mic_gpios(void)
-{
-	int ret;
-	struct pm_gpio param = {
-		.direction      = PM_GPIO_DIR_OUT,
-		.output_buffer  = PM_GPIO_OUT_BUF_CMOS,
-		.output_value   = 1,
-		.pull	   = PM_GPIO_PULL_NO,
-		.vin_sel	= PM_GPIO_VIN_S4,
-		.out_strength   = PM_GPIO_STRENGTH_MED,
-		.function       = PM_GPIO_FUNC_NORMAL,
-	};
-
-	ret = gpio_request(PM8921_GPIO_PM_TO_SYS(23), "AV_SWITCH");
-	if (ret) {
-		pr_err("%s: Failed to request gpio %d\n", __func__,
-			PM8921_GPIO_PM_TO_SYS(23));
-		return ret;
-	}
-
-	ret = pm8xxx_gpio_config(PM8921_GPIO_PM_TO_SYS(23), &param);
-	if (ret)
-		pr_err("%s: Failed to configure gpio %d\n", __func__,
-			PM8921_GPIO_PM_TO_SYS(23));
-	else
-		gpio_direction_output(PM8921_GPIO_PM_TO_SYS(23), 0);
-
-	ret = gpio_request(PM8921_GPIO_PM_TO_SYS(35), "US_EURO_SWITCH");
-	if (ret) {
-		pr_err("%s: Failed to request gpio %d\n", __func__,
-			PM8921_GPIO_PM_TO_SYS(35));
-		gpio_free(PM8921_GPIO_PM_TO_SYS(23));
-		return ret;
-	}
-	ret = pm8xxx_gpio_config(PM8921_GPIO_PM_TO_SYS(35), &param);
-	if (ret)
-		pr_err("%s: Failed to configure gpio %d\n", __func__,
-			PM8921_GPIO_PM_TO_SYS(35));
-	else
-		gpio_direction_output(PM8921_GPIO_PM_TO_SYS(35), 0);
-
-	return 0;
-}
-static void msm_free_headset_mic_gpios(void)
-{
-	if (msm_headset_gpios_configured) {
-		gpio_free(PM8921_GPIO_PM_TO_SYS(23));
-		gpio_free(PM8921_GPIO_PM_TO_SYS(35));
-	}
-}
-
 static int __init msm_audio_init(void)
 {
 	int ret;
@@ -1691,12 +1696,6 @@
 		return ret;
 	}
 
-	if (msm_configure_headset_mic_gpios()) {
-		pr_err("%s Fail to configure headset mic gpios\n", __func__);
-		msm_headset_gpios_configured = 0;
-	} else
-		msm_headset_gpios_configured = 1;
-
 	return ret;
 
 }
@@ -1708,7 +1707,6 @@
 		pr_err("%s: Not the right machine type\n", __func__);
 		return ;
 	}
-	msm_free_headset_mic_gpios();
 	platform_device_unregister(msm_snd_device);
 	kfree(mbhc_cfg.calibration);
 }
diff --git a/sound/soc/msm/msm-compr-q6.c b/sound/soc/msm/msm-compr-q6.c
index 0b9d54f..5b0eb9c 100644
--- a/sound/soc/msm/msm-compr-q6.c
+++ b/sound/soc/msm/msm-compr-q6.c
@@ -32,6 +32,7 @@
 #include <linux/android_pmem.h>
 #include <sound/timer.h>
 #include <mach/qdsp6v2/q6core.h>
+#include <sound/pcm.h>
 
 #include "msm-compr-q6.h"
 #include "msm-pcm-routing.h"
@@ -140,6 +141,10 @@
 			break;
 		} else
 			atomic_set(&prtd->pending_buffer, 0);
+		if (runtime->status->hw_ptr >= runtime->control->appl_ptr) {
+			runtime->render_flag |= SNDRV_RENDER_STOPPED;
+			break;
+		}
 		buf = prtd->audio_client->port[IN].buf;
 		pr_debug("%s:writing %d bytes of buffer[%d] to dsp 2\n",
 				__func__, prtd->pcm_count, prtd->out_head);
@@ -197,6 +202,7 @@
 			wake_up(&the_locks.eos_wait);
 			atomic_set(&prtd->eos, 0);
 		}
+		atomic_set(&prtd->pending_buffer, 1);
 		break;
 	case ASM_DATA_EVENT_READ_DONE: {
 		pr_debug("ASM_DATA_EVENT_READ_DONE\n");
@@ -376,6 +382,7 @@
 		break;
 	case SND_AUDIOCODEC_AC3_PASS_THROUGH:
 	case SND_AUDIOCODEC_DTS_PASS_THROUGH:
+	case SND_AUDIOCODEC_DTS_LBR_PASS_THROUGH:
 		pr_debug("compressd playback, no need to send decoder params");
 		pr_debug("decoder id: %d\n",
 			compr->info.codec_param.codec.id);
@@ -461,10 +468,29 @@
 			return ret;
 		}
 		break;
+	case SND_AUDIOCODEC_MP2:
+		pr_debug("%s: SND_AUDIOCODEC_MP2\n", __func__);
+		break;
 	default:
 		return -EINVAL;
 	}
-
+	if (compr->info.codec_param.codec.transcode_dts) {
+		msm_pcm_routing_reg_pseudo_stream(
+			MSM_FRONTEND_DAI_PSEUDO,
+			prtd->enc_audio_client->perf_mode,
+			prtd->enc_audio_client->session,
+			SNDRV_PCM_STREAM_CAPTURE,
+			48000, runtime->channels > 6 ?
+			6 : runtime->channels);
+		pr_debug("%s: cmd: DTS ENCDEC CFG BLK\n", __func__);
+		ret = q6asm_enc_cfg_blk_dts(prtd->enc_audio_client,
+				DTS_ENC_SAMPLE_RATE48k,
+				runtime->channels > 6 ?
+				6 : runtime->channels);
+		if (ret < 0)
+			pr_err("%s: CMD: DTS ENCDEC CFG BLK failed\n",
+				__func__);
+	}
 	prtd->enabled = 1;
 	prtd->cmd_ack = 0;
 
@@ -560,6 +586,70 @@
 	return ret;
 }
 
+static int msm_compr_restart(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct compr_audio *compr = runtime->private_data;
+	struct msm_audio *prtd = &compr->prtd;
+	struct audio_aio_write_param param;
+	struct audio_buffer *buf = NULL;
+	struct output_meta_data_st output_meta_data;
+	int time_stamp_flag = 0;
+	int buffer_length = 0;
+
+	pr_err("msm_compr_restart\n");
+	if (runtime->render_flag & SNDRV_RENDER_STOPPED) {
+		buf = prtd->audio_client->port[IN].buf;
+		pr_debug("%s:writing %d bytes of buffer[%d] to dsp 2\n",
+				__func__, prtd->pcm_count, prtd->out_head);
+		pr_debug("%s:writing buffer[%d] from 0x%08x\n",
+				__func__, prtd->out_head,
+				((unsigned int)buf[0].phys
+				+ (prtd->out_head * prtd->pcm_count)));
+
+		if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE)
+			time_stamp_flag = SET_TIMESTAMP;
+		else
+			time_stamp_flag = NO_TIMESTAMP;
+		memcpy(&output_meta_data, (char *)(buf->data +
+			prtd->out_head * prtd->pcm_count),
+			COMPRE_OUTPUT_METADATA_SIZE);
+
+		buffer_length = output_meta_data.frame_size;
+		pr_debug("meta_data_length: %d, frame_length: %d\n",
+			 output_meta_data.meta_data_length,
+			 output_meta_data.frame_size);
+		pr_debug("timestamp_msw: %d, timestamp_lsw: %d\n",
+			 output_meta_data.timestamp_msw,
+			 output_meta_data.timestamp_lsw);
+		 if (buffer_length == 0) {
+			pr_debug("Recieved a zero length buffer-break out");
+			return -EINVAL;
+		}
+		param.paddr = (unsigned long)buf[0].phys
+				+ (prtd->out_head * prtd->pcm_count)
+				+ output_meta_data.meta_data_length;
+		param.len = buffer_length;
+		param.msw_ts = output_meta_data.timestamp_msw;
+		param.lsw_ts = output_meta_data.timestamp_lsw;
+		param.flags = time_stamp_flag;
+		param.uid =  (unsigned long)buf[0].phys
+				+ (prtd->out_head * prtd->pcm_count
+				+ output_meta_data.meta_data_length);
+		if (q6asm_async_write(prtd->audio_client,
+					&param) < 0)
+			pr_err("%s:q6asm_async_write failed\n",
+				__func__);
+		else
+			prtd->out_head =
+				(prtd->out_head + 1) & (runtime->periods - 1);
+
+		runtime->render_flag &= ~SNDRV_RENDER_STOPPED;
+		return 0;
+	}
+	return 0;
+}
+
 static int msm_compr_trigger(struct snd_pcm_substream *substream, int cmd)
 {
 	int ret = 0;
@@ -576,16 +666,22 @@
 	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
 		pr_debug("%s: Trigger start\n", __func__);
 		q6asm_run_nowait(prtd->audio_client, 0, 0, 0);
+		if (prtd->enc_audio_client)
+			q6asm_run_nowait(prtd->enc_audio_client, 0, 0, 0);
 		atomic_set(&prtd->start, 1);
 		break;
 	case SNDRV_PCM_TRIGGER_STOP:
 		atomic_set(&prtd->start, 0);
+		runtime->render_flag &= ~SNDRV_RENDER_STOPPED;
 		break;
 	case SNDRV_PCM_TRIGGER_SUSPEND:
 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
 		pr_debug("SNDRV_PCM_TRIGGER_PAUSE\n");
 		q6asm_cmd_nowait(prtd->audio_client, CMD_PAUSE);
+		if (prtd->enc_audio_client)
+			q6asm_cmd_nowait(prtd->enc_audio_client, CMD_PAUSE);
 		atomic_set(&prtd->start, 0);
+		runtime->render_flag &= ~SNDRV_RENDER_STOPPED;
 		break;
 	default:
 		ret = -EINVAL;
@@ -600,7 +696,7 @@
 {
 	pr_debug("%s\n", __func__);
 	/* MP3 Block */
-	compr->info.compr_cap.num_codecs = 12;
+	compr->info.compr_cap.num_codecs = 14;
 	compr->info.compr_cap.min_fragment_size = runtime->hw.period_bytes_min;
 	compr->info.compr_cap.max_fragment_size = runtime->hw.period_bytes_max;
 	compr->info.compr_cap.min_fragments = runtime->hw.periods_min;
@@ -617,6 +713,8 @@
 	compr->info.compr_cap.codecs[9] = SND_AUDIOCODEC_AMRWBPLUS;
 	compr->info.compr_cap.codecs[10] = SND_AUDIOCODEC_PASS_THROUGH;
 	compr->info.compr_cap.codecs[11] = SND_AUDIOCODEC_PCM;
+	compr->info.compr_cap.codecs[12] = SND_AUDIOCODEC_MP2;
+	compr->info.compr_cap.codecs[13] = SND_AUDIOCODEC_DTS_LBR_PASS_THROUGH;
 	/* Add new codecs here and update num_codecs*/
 }
 
@@ -646,6 +744,7 @@
 	}
 	prtd = &compr->prtd;
 	prtd->substream = substream;
+	runtime->render_flag = SNDRV_DMA_MODE;
 	prtd->audio_client = q6asm_audio_client_alloc(
 				(app_cb)compr_event_handler, compr);
 	if (!prtd->audio_client) {
@@ -732,21 +831,30 @@
 	atomic_set(&prtd->pending_buffer, 0);
 	prtd->pcm_irq_pos = 0;
 	q6asm_cmd(prtd->audio_client, CMD_CLOSE);
+	if (prtd->enc_audio_client)
+		q6asm_cmd(prtd->enc_audio_client, CMD_CLOSE);
 	compressed_audio.prtd = NULL;
 	q6asm_audio_client_buf_free_contiguous(dir,
 				prtd->audio_client);
-	if ((compr->info.codec_param.codec.id !=
-			SND_AUDIOCODEC_AC3_PASS_THROUGH) &&
-			(compr->info.codec_param.codec.id !=
-			SND_AUDIOCODEC_DTS_PASS_THROUGH))
+	switch (compr->info.codec_param.codec.id) {
+	case SND_AUDIOCODEC_AC3_PASS_THROUGH:
+	case SND_AUDIOCODEC_DTS_PASS_THROUGH:
+	case SND_AUDIOCODEC_DTS_LBR_PASS_THROUGH:
+		msm_pcm_routing_reg_psthr_stream(
+			soc_prtd->dai_link->be_id,
+			prtd->session_id, substream->stream,
+			0);
+	default:
 		msm_pcm_routing_dereg_phy_stream(
 			soc_prtd->dai_link->be_id,
 			SNDRV_PCM_STREAM_PLAYBACK);
-	else
-		msm_pcm_routing_reg_psthr_stream(
-					soc_prtd->dai_link->be_id,
-					prtd->session_id, substream->stream,
-					0);
+	}
+	if (compr->info.codec_param.codec.transcode_dts) {
+		msm_pcm_routing_dereg_pseudo_stream(MSM_FRONTEND_DAI_PSEUDO,
+			prtd->enc_audio_client->session);
+	}
+	if (prtd->enc_audio_client)
+		q6asm_audio_client_free(prtd->enc_audio_client);
 	q6asm_audio_client_free(prtd->audio_client);
 	kfree(prtd);
 	return 0;
@@ -828,6 +936,7 @@
 
 	pr_debug("%s\n", __func__);
 	prtd->mmap_flag = 1;
+	runtime->render_flag = SNDRV_NON_DMA_MODE;
 	if (runtime->dma_addr && runtime->dma_bytes) {
 		vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
 		result = remap_pfn_range(vma, vma->vm_start,
@@ -863,6 +972,7 @@
 		switch (compr->info.codec_param.codec.id) {
 		case SND_AUDIOCODEC_AC3_PASS_THROUGH:
 		case SND_AUDIOCODEC_DTS_PASS_THROUGH:
+		case SND_AUDIOCODEC_DTS_LBR_PASS_THROUGH:
 			ret = q6asm_open_write_compressed(prtd->audio_client,
 					compr->codec);
 
@@ -886,6 +996,29 @@
 				prtd->session_id,
 				substream->stream);
 
+			if (compr->info.codec_param.codec.transcode_dts) {
+				prtd->enc_audio_client =
+					q6asm_audio_client_alloc(
+					(app_cb)compr_event_handler, compr);
+				if (!prtd->enc_audio_client) {
+					pr_err("%s: Could not allocate " \
+							"memory\n", __func__);
+					return -ENOMEM;
+				}
+				prtd->enc_audio_client->perf_mode = false;
+				pr_debug("%s Setting up loopback path\n",
+						__func__);
+				ret = q6asm_open_transcode_loopback(
+					prtd->enc_audio_client,
+					params_channels(params));
+				if (ret < 0) {
+					pr_err("%s: Session transcode " \
+						"loopback open failed\n",
+						__func__);
+					return -ENODEV;
+				}
+			}
+
 			break;
 		}
 	} else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
@@ -1035,6 +1168,22 @@
 			pr_err("%s: ERROR: copy from user\n", __func__);
 			return rc;
 		}
+		/*
+		* DTS Security needed for the transcode path
+		*/
+		if (compr->info.codec_param.codec.transcode_dts) {
+			char modelId[128];
+			struct snd_dec_dts opt_dts =
+				compr->info.codec_param.codec.dts;
+			int modelIdLength = opt_dts.modelIdLength;
+			if (copy_from_user(modelId, (void *)opt_dts.modelId,
+				modelIdLength))
+				pr_err("%s: ERROR: copy modelId\n", __func__);
+			modelId[modelIdLength] = '\0';
+			pr_debug("%s: Received modelId =%s,length=%d\n",
+				__func__, modelId, modelIdLength);
+			core_set_dts_model_id(modelIdLength, modelId);
+		}
 		switch (compr->info.codec_param.codec.id) {
 		case SND_AUDIOCODEC_MP3:
 			/* For MP3 we dont need any other parameter */
@@ -1061,10 +1210,14 @@
 			pr_debug("SND_AUDIOCODEC_DTS_PASS_THROUGH\n");
 			compr->codec = FORMAT_DTS;
 			break;
+		case SND_AUDIOCODEC_DTS_LBR_PASS_THROUGH:
+			pr_debug("SND_AUDIOCODEC_DTS_LBR_PASS_THROUGH\n");
+			compr->codec = FORMAT_DTS_LBR;
+			break;
 		case SND_AUDIOCODEC_DTS: {
 			char modelId[128];
 			struct snd_dec_dts opt_dts =
-				compr->info.codec_param.codec.options.dts;
+				compr->info.codec_param.codec.dts;
 			int modelIdLength = opt_dts.modelIdLength;
 			pr_debug("SND_AUDIOCODEC_DTS\n");
 			if (copy_from_user(modelId, (void *)opt_dts.modelId,
@@ -1080,7 +1233,7 @@
 		case SND_AUDIOCODEC_DTS_LBR:{
 			char modelId[128];
 			struct snd_dec_dts opt_dts =
-				compr->info.codec_param.codec.options.dts;
+				compr->info.codec_param.codec.dts;
 			int modelIdLength = opt_dts.modelIdLength;
 			pr_debug("SND_AUDIOCODEC_DTS_LBR\n");
 			if (copy_from_user(modelId, (void *)opt_dts.modelId,
@@ -1111,6 +1264,10 @@
 			pr_debug("msm_compr_ioctl SND_AUDIOCODEC_PCM\n");
 			compr->codec = FORMAT_MULTI_CHANNEL_LINEAR_PCM;
 			break;
+		case SND_AUDIOCODEC_MP2:
+			pr_debug("SND_AUDIOCODEC_MP2\n");
+			compr->codec = FORMAT_MP2;
+			break;
 		default:
 			pr_err("msm_compr_ioctl failed..unknown codec\n");
 			return -EFAULT;
@@ -1127,6 +1284,7 @@
 				prtd->cmd_ack = 1;
 				wake_up(&the_locks.eos_wait);
 				atomic_set(&prtd->eos, 0);
+				atomic_set(&prtd->pending_buffer, 1);
 			}
 
 			/* A unlikely race condition possible with FLUSH
@@ -1173,6 +1331,7 @@
 	.trigger	= msm_compr_trigger,
 	.pointer	= msm_compr_pointer,
 	.mmap		= msm_compr_mmap,
+	.restart	= msm_compr_restart,
 };
 
 static int msm_asoc_pcm_new(struct snd_soc_pcm_runtime *rtd)
diff --git a/sound/soc/msm/msm-dai-fe.c b/sound/soc/msm/msm-dai-fe.c
index 16a4aaa..e91ed86 100644
--- a/sound/soc/msm/msm-dai-fe.c
+++ b/sound/soc/msm/msm-dai-fe.c
@@ -270,9 +270,9 @@
 			.rates = SNDRV_PCM_RATE_8000_48000,
 			.formats = SNDRV_PCM_FMTBIT_S16_LE,
 			.channels_min = 1,
-			.channels_max = 2,
+			.channels_max = 8,
 			.rate_min =     8000,
-			.rate_max =     48000,
+			.rate_max =     192000,
 		},
 		.capture = {
 			.stream_name = "SLIMBUS0 Hostless Capture",
@@ -280,9 +280,9 @@
 			.rates = SNDRV_PCM_RATE_8000_48000,
 			.formats = SNDRV_PCM_FMTBIT_S16_LE,
 			.channels_min = 1,
-			.channels_max = 2,
+			.channels_max = 8,
 			.rate_min =     8000,
-			.rate_max =     48000,
+			.rate_max =     192000,
 		},
 		.ops = &msm_fe_dai_ops,
 		.name = "SLIMBUS0_HOSTLESS",
@@ -477,6 +477,32 @@
 		.ops = &msm_fe_dai_ops,
 		.name = "SGLTE",
 	},
+	{
+		.playback = {
+			.stream_name = "Pseudo Playback",
+			.aif_name = "MM_DL9",
+			.rates = (SNDRV_PCM_RATE_8000_48000 |
+					SNDRV_PCM_RATE_KNOT),
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min =	8000,
+			.rate_max = 48000,
+		},
+		.capture = {
+			.stream_name = "Pseudo Capture",
+			.aif_name = "MM_UL9",
+			.rates = (SNDRV_PCM_RATE_8000_48000|
+					SNDRV_PCM_RATE_KNOT),
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min =     8000,
+			.rate_max =	48000,
+		},
+		.ops = &msm_fe_Multimedia_dai_ops,
+		.name = "Pseudo",
+	},
 };
 
 static __devinit int msm_fe_dai_dev_probe(struct platform_device *pdev)
diff --git a/sound/soc/msm/msm-dai-q6-hdmi.c b/sound/soc/msm/msm-dai-q6-hdmi.c
index 2b3dd5f..1995f1a 100644
--- a/sound/soc/msm/msm-dai-q6-hdmi.c
+++ b/sound/soc/msm/msm-dai-q6-hdmi.c
@@ -91,11 +91,25 @@
 	u32 channel_allocation = 0;
 	u32 level_shift  = 0; /* 0dB */
 	bool down_mix = FALSE;
+	int sample_rate = 48000;
 
 	dai_data->channels = params_channels(params);
 	dai_data->rate = params_rate(params);
 	dai_data->port_config.hdmi_multi_ch.reserved = 0;
 
+	switch (dai_data->rate) {
+	case 48000:
+		sample_rate = HDMI_SAMPLE_RATE_48KHZ;
+		break;
+	case 44100:
+		sample_rate = HDMI_SAMPLE_RATE_44_1KHZ;
+		break;
+	case 32000:
+		sample_rate = HDMI_SAMPLE_RATE_32KHZ;
+		break;
+	}
+	hdmi_msm_audio_sample_rate_reset(sample_rate);
+
 	switch (dai_data->channels) {
 	case 2:
 		channel_allocation  = 0;
diff --git a/sound/soc/msm/msm-dai-q6.c b/sound/soc/msm/msm-dai-q6.c
index ee1ab79..8cc0eaa 100644
--- a/sound/soc/msm/msm-dai-q6.c
+++ b/sound/soc/msm/msm-dai-q6.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -660,6 +660,24 @@
 	return 0;
 }
 
+static int msm_dai_q6_pseudo_hw_params(struct snd_pcm_hw_params *params,
+				struct snd_soc_dai *dai)
+{
+	struct msm_dai_q6_dai_data *dai_data = dev_get_drvdata(dai->dev);
+
+	dai_data->rate = params_rate(params);
+	dai_data->channels = params_channels(params) > 6 ?
+				params_channels(params) : 6;
+
+	dai_data->port_config.pseudo.bit_width = 16;
+	dai_data->port_config.pseudo.num_channels =
+			dai_data->channels;
+	dai_data->port_config.pseudo.data_format = 0;
+	dai_data->port_config.pseudo.timing_mode = 1;
+	dai_data->port_config.pseudo.reserved = 16;
+	return 0;
+}
+
 /* Current implementation assumes hw_param is called once
  * This may not be the case but what to do when ADM and AFE
  * port are already opened and parameter changes
@@ -674,6 +692,7 @@
 	case PRIMARY_I2S_TX:
 	case PRIMARY_I2S_RX:
 	case SECONDARY_I2S_RX:
+	case SECONDARY_I2S_TX:
 		rc = msm_dai_q6_cdc_hw_params(params, dai, substream->stream);
 		break;
 
@@ -702,6 +721,9 @@
 	case RT_PROXY_DAI_002_RX:
 		rc = msm_dai_q6_afe_rtproxy_hw_params(params, dai);
 		break;
+	case PSEUDOPORT_01:
+		rc = msm_dai_q6_pseudo_hw_params(params, dai);
+		break;
 	case VOICE_PLAYBACK_TX:
 	case VOICE_RECORD_RX:
 	case VOICE_RECORD_TX:
@@ -1379,6 +1401,7 @@
 	case PRIMARY_I2S_TX:
 	case PRIMARY_I2S_RX:
 	case SECONDARY_I2S_RX:
+	case SECONDARY_I2S_TX:
 		rc = msm_dai_q6_cdc_set_fmt(dai, fmt);
 		break;
 	default:
@@ -1817,6 +1840,20 @@
 	.probe = msm_dai_q6_dai_probe,
 	.remove = msm_dai_q6_dai_remove,
 };
+static struct snd_soc_dai_driver msm_dai_q6_pseudo_dai = {
+	.playback = {
+		.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+		SNDRV_PCM_RATE_48000,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE,
+		.channels_min = 1,
+		.channels_max = 6,
+		.rate_min = 8000,
+		.rate_max = 48000,
+	},
+	.ops = &msm_dai_q6_ops,
+	.probe = msm_dai_q6_dai_probe,
+	.remove = msm_dai_q6_dai_remove,
+};
 
 /* To do: change to register DAIs as batch */
 static __devinit int msm_dai_q6_dev_probe(struct platform_device *pdev)
@@ -1831,6 +1868,7 @@
 		rc = snd_soc_register_dai(&pdev->dev, &msm_dai_q6_i2s_rx_dai);
 		break;
 	case PRIMARY_I2S_TX:
+	case SECONDARY_I2S_TX:
 		rc = snd_soc_register_dai(&pdev->dev, &msm_dai_q6_i2s_tx_dai);
 		break;
 	case PCM_RX:
@@ -1912,6 +1950,10 @@
 		rc = snd_soc_register_dai(&pdev->dev,
 						&msm_dai_q6_incall_record_dai);
 		break;
+	case PSEUDOPORT_01:
+		rc = snd_soc_register_dai(&pdev->dev,
+					&msm_dai_q6_pseudo_dai);
+		break;
 	default:
 		rc = -ENODEV;
 		break;
diff --git a/sound/soc/msm/msm-lowlatency-pcm-q6.c b/sound/soc/msm/msm-lowlatency-pcm-q6.c
index 129f69f..98c28aa 100644
--- a/sound/soc/msm/msm-lowlatency-pcm-q6.c
+++ b/sound/soc/msm/msm-lowlatency-pcm-q6.c
@@ -218,8 +218,27 @@
 	if (prtd->enabled)
 		return 0;
 
+	if (!prtd->set_channel_map) {
+		memset(prtd->channel_map, 0, PCM_FORMAT_MAX_NUM_CHANNEL);
+		if (prtd->channel_mode == 1) {
+			prtd->channel_map[0] = PCM_CHANNEL_FL;
+		} else if (prtd->channel_mode == 2) {
+			prtd->channel_map[0] = PCM_CHANNEL_FL;
+			prtd->channel_map[1] = PCM_CHANNEL_FR;
+		} else if (prtd->channel_mode == 6) {
+			prtd->channel_map[0] = PCM_CHANNEL_FC;
+			prtd->channel_map[1] = PCM_CHANNEL_FL;
+			prtd->channel_map[2] = PCM_CHANNEL_FR;
+			prtd->channel_map[3] = PCM_CHANNEL_LB;
+			prtd->channel_map[4] = PCM_CHANNEL_RB;
+			prtd->channel_map[5] = PCM_CHANNEL_LFE;
+		} else {
+			pr_err("%s: ERROR.unsupported num_ch = %u\n", __func__,
+				prtd->channel_mode);
+		}
+	}
 	ret = q6asm_media_format_block_multi_ch_pcm(prtd->audio_client,
-			runtime->rate, runtime->channels);
+			runtime->rate, runtime->channels, prtd->channel_map);
 	if (ret < 0)
 		pr_info("%s: CMD Format block failed\n", __func__);
 
@@ -389,6 +408,7 @@
 	}
 
 	prtd->dsp_cnt = 0;
+	prtd->set_channel_map = false;
 	runtime->private_data = prtd;
 	pr_debug("substream->pcm->device = %d\n", substream->pcm->device);
 	pr_debug("soc_prtd->dai_link->be_id = %d\n", soc_prtd->dai_link->be_id);
diff --git a/sound/soc/msm/msm-multi-ch-pcm-q6.c b/sound/soc/msm/msm-multi-ch-pcm-q6.c
index 5b0759c..6cad0af 100644
--- a/sound/soc/msm/msm-multi-ch-pcm-q6.c
+++ b/sound/soc/msm/msm-multi-ch-pcm-q6.c
@@ -236,6 +236,11 @@
 			}
 			atomic_set(&prtd->start, 1);
 			break;
+		case ASM_STREAM_CMD_FLUSH:
+			pr_debug("ASM_STREAM_CMD_FLUSH\n");
+			prtd->cmd_ack = 1;
+			wake_up(&the_locks.flush_wait);
+			break;
 		default:
 			break;
 		}
@@ -269,9 +274,29 @@
 	prtd->channel_mode = runtime->channels;
 	if (prtd->enabled)
 		return 0;
-
+	pr_debug("prtd->set_channel_map: %d", prtd->set_channel_map);
+	if (!prtd->set_channel_map) {
+		pr_debug("using default channel map");
+		memset(prtd->channel_map, 0, PCM_FORMAT_MAX_NUM_CHANNEL);
+		if (prtd->channel_mode == 1) {
+			prtd->channel_map[0] = PCM_CHANNEL_FL;
+		} else if (prtd->channel_mode == 2) {
+			prtd->channel_map[0] = PCM_CHANNEL_FL;
+			prtd->channel_map[1] = PCM_CHANNEL_FR;
+		} else if (prtd->channel_mode == 6) {
+			prtd->channel_map[0] = PCM_CHANNEL_FC;
+			prtd->channel_map[1] = PCM_CHANNEL_FL;
+			prtd->channel_map[2] = PCM_CHANNEL_FR;
+			prtd->channel_map[3] = PCM_CHANNEL_LB;
+			prtd->channel_map[4] = PCM_CHANNEL_RB;
+			prtd->channel_map[5] = PCM_CHANNEL_LFE;
+		} else {
+			pr_err("%s: ERROR.unsupported num_ch = %u\n", __func__,
+				prtd->channel_mode);
+		}
+	}
 	ret = q6asm_media_format_block_multi_ch_pcm(prtd->audio_client,
-			runtime->rate, runtime->channels);
+			runtime->rate, runtime->channels, prtd->channel_map);
 	if (ret < 0)
 		pr_info("%s: CMD Format block failed\n", __func__);
 
@@ -452,6 +477,7 @@
 	}
 
 	prtd->dsp_cnt = 0;
+	prtd->set_channel_map = false;
 	runtime->private_data = prtd;
 	pr_debug("substream->pcm->device = %d\n", substream->pcm->device);
 	pr_debug("soc_prtd->dai_link->be_id = %d\n", soc_prtd->dai_link->be_id);
@@ -492,7 +518,6 @@
 	return rc;
 }
 
-
 static int msm_pcm_playback_copy(struct snd_pcm_substream *substream, int a,
 	snd_pcm_uframes_t hwoff, void __user *buf, snd_pcm_uframes_t frames)
 {
@@ -780,26 +805,103 @@
 	snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
 	return 0;
 }
+static int msm_pcm_ioctl(struct snd_pcm_substream *substream,
+			unsigned int cmd, void *arg)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct msm_audio *prtd = runtime->private_data;
+	int ret = 0, rc;
+
+	pr_debug("%s\n", __func__);
+	ret = snd_pcm_lib_ioctl(substream, cmd, arg);
+	if (ret < 0) {
+		pr_err("%s, snd_pcm_lib_ioctl error\n", __func__);
+		return ret;
+	}
+
+	switch (cmd) {
+	case SNDRV_PCM_IOCTL1_RESET:
+		pr_debug("%s, SNDRV_PCM_IOCTL1_RESET\n", __func__);
+		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+			prtd->cmd_ack = 0;
+			rc = q6asm_cmd(prtd->audio_client, CMD_FLUSH);
+			if (rc < 0) {
+				pr_err("%s: flush cmd failed rc=%d\n",
+					 __func__, rc);
+				break;
+			}
+			rc = wait_event_timeout(the_locks.flush_wait,
+				 prtd->cmd_ack, 5 * HZ);
+			if (rc < 0)
+				pr_err("Flush cmd timeout\n");
+			prtd->pcm_irq_pos = 0;
+			atomic_set(&prtd->out_count, runtime->periods);
+		}
+		break;
+	default:
+		break;
+	}
+
+	return ret;
+}
 
 static struct snd_pcm_ops msm_pcm_ops = {
 	.open           = msm_pcm_open,
 	.copy		= msm_pcm_copy,
 	.hw_params	= msm_pcm_hw_params,
 	.close          = msm_pcm_close,
-	.ioctl          = snd_pcm_lib_ioctl,
+	.ioctl          = msm_pcm_ioctl,
 	.prepare        = msm_pcm_prepare,
 	.trigger        = msm_pcm_trigger,
 	.pointer        = msm_pcm_pointer,
 	.mmap		= msm_pcm_mmap,
 };
 
+
+static int pcm_chmap_ctl_put(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	int i;
+	char channel_mapping[PCM_FORMAT_MAX_NUM_CHANNEL];
+
+	pr_debug("%s", __func__);
+	for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL; i++)
+		channel_mapping[i] = (char)(ucontrol->value.integer.value[i]);
+	if (multi_ch_pcm_audio.prtd) {
+		multi_ch_pcm_audio.prtd->set_channel_map = true;
+		memcpy(multi_ch_pcm_audio.prtd->channel_map, channel_mapping,
+			 PCM_FORMAT_MAX_NUM_CHANNEL);
+	}
+	return 0;
+}
+
+
 static int msm_asoc_pcm_new(struct snd_soc_pcm_runtime *rtd)
 {
 	struct snd_card *card = rtd->card->snd_card;
-	int ret = 0;
+	struct snd_pcm *pcm = rtd->pcm->streams[0].pcm;
+	struct snd_pcm_chmap *chmap_info;
+	struct snd_kcontrol *kctl;
+	char device_num[3];
 
+	int i, ret = 0;
 	if (!card->dev->coherent_dma_mask)
 		card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
+
+	pr_debug("%s, Channel map cntrl add\n", __func__);
+	ret = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+				NULL, PCM_FORMAT_MAX_NUM_CHANNEL, 0,
+				&chmap_info);
+	if (ret < 0)
+		return ret;
+	kctl = chmap_info->kctl;
+	for (i = 0; i < kctl->count; i++)
+		kctl->vd[i].access |= SNDRV_CTL_ELEM_ACCESS_WRITE;
+	snprintf(device_num, sizeof(device_num), "%d", pcm->device);
+	strlcat(kctl->id.name, device_num, sizeof(kctl->id.name));
+	pr_debug("%s, Overwriting channel map control name to: %s",
+			 __func__, kctl->id.name);
+	kctl->put = pcm_chmap_ctl_put;
 	return ret;
 }
 
@@ -836,6 +938,7 @@
 	init_waitqueue_head(&the_locks.eos_wait);
 	init_waitqueue_head(&the_locks.write_wait);
 	init_waitqueue_head(&the_locks.read_wait);
+	init_waitqueue_head(&the_locks.flush_wait);
 
 	return platform_driver_register(&msm_pcm_driver);
 }
diff --git a/sound/soc/msm/msm-pcm-q6.h b/sound/soc/msm/msm-pcm-q6.h
index f1af99a..86e5c54 100644
--- a/sound/soc/msm/msm-pcm-q6.h
+++ b/sound/soc/msm/msm-pcm-q6.h
@@ -59,6 +59,7 @@
 	uint16_t source; /* Encoding source bit mask */
 
 	struct audio_client *audio_client;
+	struct audio_client *enc_audio_client;
 
 	uint16_t session_id;
 
@@ -81,6 +82,8 @@
 	int periods;
 	int mmap_flag;
 	atomic_t pending_buffer;
+	bool set_channel_map;
+	char channel_map[8];
 };
 
 struct output_meta_data_st {
diff --git a/sound/soc/msm/msm-pcm-routing.c b/sound/soc/msm/msm-pcm-routing.c
index 4d0caa3..378baf1 100644
--- a/sound/soc/msm/msm-pcm-routing.c
+++ b/sound/soc/msm/msm-pcm-routing.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -182,6 +182,7 @@
 	{ MI2S_RX, 0, 0, 0, 0, 0},
 	{ MI2S_TX, 0, 0, 0, 0},
 	{ SECONDARY_I2S_RX, 0, 0, 0, 0, 0},
+	{ SECONDARY_I2S_TX, 0, 0, 0, 0, 0},
 	{ SLIMBUS_1_RX, 0, 0, 0, 0, 0},
 	{ SLIMBUS_1_TX, 0, 0, 0, 0, 0},
 	{ SLIMBUS_4_RX, 0, 0, 0, 0, 0},
@@ -193,6 +194,7 @@
 	{ SLIMBUS_EXTPROC_RX, 0, 0, 0, 0, 0},
 	{ SECONDARY_PCM_RX, 0, 0, 0, 0, 0},
 	{ SECONDARY_PCM_TX, 0, 0, 0, 0, 0},
+	{ PSEUDOPORT_01, 0, 0, 0, 0, 0},
 };
 
 
@@ -214,7 +216,8 @@
 	{INVALID_SESSION, INVALID_SESSION},
 	/* MULTIMEDIA8 */
 	{INVALID_SESSION, INVALID_SESSION},
-
+	/* PSEUDO */
+	{INVALID_SESSION, INVALID_SESSION},
 };
 
 static uint8_t is_be_dai_extproc(int be_dai)
@@ -297,6 +300,59 @@
 	mutex_unlock(&routing_lock);
 }
 
+void msm_pcm_routing_reg_pseudo_stream(int fedai_id, bool perf_mode,
+					int dspst_id, int stream_type,
+					int sample_rate, int channels)
+{
+	int i, session_type, path_type, port_type, mode, ret;
+	struct route_payload payload;
+	pr_debug("%s:fedai_id = %d dspst_id = %d stream_type %d",
+				__func__, fedai_id, dspst_id, stream_type);
+
+	if (stream_type == SNDRV_PCM_STREAM_PLAYBACK) {
+		session_type = SESSION_TYPE_RX;
+		path_type = ADM_PATH_PLAYBACK;
+		port_type = MSM_AFE_PORT_TYPE_RX;
+	} else {
+		session_type = SESSION_TYPE_TX;
+		path_type = ADM_PATH_LIVE_REC;
+		port_type = MSM_AFE_PORT_TYPE_TX;
+	}
+
+	mutex_lock(&routing_lock);
+
+	payload.num_copps = 0;
+	adm_multi_ch_copp_pseudo_open_v3(PSEUDOPORT_01,
+					path_type, sample_rate, channels,
+					DEFAULT_COPP_TOPOLOGY);
+
+	payload.copp_ids[payload.num_copps++] = PSEUDOPORT_01;
+
+	for (i = 0; i < MSM_BACKEND_DAI_MAX; i++) {
+		if (test_bit(fedai_id, &msm_bedais[i].fe_sessions))
+			msm_bedais[i].perf_mode = perf_mode;
+		if (!is_be_dai_extproc(i) &&
+		   (msm_bedais[i].active) &&
+		   (test_bit(fedai_id, &msm_bedais[i].fe_sessions))) {
+
+			mode = afe_get_port_type(msm_bedais[i].port_id);
+			ret = adm_connect_afe_port_v2(mode, dspst_id,
+						msm_bedais[i].port_id,
+						msm_bedais[i].sample_rate,
+						msm_bedais[i].channel);
+
+			if (ret < 0)
+				pr_err("%s: adm_connect_afe_port_v2 failed\n",
+					__func__);
+		}
+	}
+	if (payload.num_copps)
+		adm_matrix_map(dspst_id, path_type,
+			payload.num_copps, payload.copp_ids, 0);
+
+	mutex_unlock(&routing_lock);
+}
+
 void msm_pcm_routing_reg_phy_stream(int fedai_id, bool perf_mode, int dspst_id,
 							int stream_type)
 {
@@ -374,6 +430,32 @@
 	mutex_unlock(&routing_lock);
 }
 
+void msm_pcm_routing_dereg_pseudo_stream(int fedai_id, int dspst_id)
+{
+	int i, mode, ret;
+	pr_debug("%s:fedai_id = %d dspst_id = %d",
+			__func__, fedai_id, dspst_id);
+
+	mutex_lock(&routing_lock);
+
+	adm_pseudo_close(PSEUDOPORT_01);
+	for (i = 0; i < MSM_BACKEND_DAI_MAX; i++) {
+		if (!is_be_dai_extproc(i) &&
+			(msm_bedais[i].active) &&
+			(test_bit(fedai_id, &msm_bedais[i].fe_sessions))) {
+
+			mode = afe_get_port_type(msm_bedais[i].port_id);
+			ret = adm_disconnect_afe_port(mode, dspst_id,
+					msm_bedais[i].port_id);
+			if (ret < 0)
+				pr_err("%s: adm_connect_afe_port_v2 failed\n",
+					__func__);
+		}
+	}
+
+	mutex_unlock(&routing_lock);
+
+}
 void msm_pcm_routing_dereg_phy_stream(int fedai_id, int stream_type)
 {
 	int i, port_type, session_type;
@@ -1209,6 +1291,9 @@
 	SOC_SINGLE_EXT("MultiMedia8", MSM_BACKEND_DAI_SEC_I2S_RX,
 	MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
 	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("Pseudo", MSM_BACKEND_DAI_SEC_I2S_RX,
+	MSM_FRONTEND_DAI_PSEUDO, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
 };
 
 static const struct snd_kcontrol_new slimbus_rx_mixer_controls[] = {
@@ -1290,6 +1375,17 @@
 	SOC_SINGLE_EXT("MultiMedia8", MSM_BACKEND_DAI_HDMI_RX,
 	MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
 	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("Pseudo", MSM_BACKEND_DAI_HDMI_RX,
+	MSM_FRONTEND_DAI_PSEUDO, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+};
+static const struct snd_kcontrol_new pseudo_mixer_controls[] = {
+	SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_PSEUDO_PORT,
+	MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_PSEUDO_PORT,
+	MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
 };
 	/* incall music delivery mixer */
 static const struct snd_kcontrol_new incall_music_delivery_mixer_controls[] = {
@@ -1395,6 +1491,9 @@
 	SOC_SINGLE_EXT("PRI_TX", MSM_BACKEND_DAI_PRI_I2S_TX,
 		MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
 		msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("SEC_TX", MSM_BACKEND_DAI_SEC_I2S_TX,
+		MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+		msm_routing_put_audio_mixer),
 	SOC_SINGLE_EXT("MI2S_TX", MSM_BACKEND_DAI_MI2S_TX,
 		MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
 		msm_routing_put_audio_mixer),
@@ -1464,7 +1563,13 @@
 	msm_routing_put_audio_mixer),
 	SOC_SINGLE_EXT("SLIM_0_TX", MSM_BACKEND_DAI_SLIMBUS_0_TX,
 	MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
-	msm_routing_put_audio_mixer)
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("VOC_REC_DL", MSM_BACKEND_DAI_INCALL_RECORD_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("VOC_REC_UL", MSM_BACKEND_DAI_INCALL_RECORD_TX,
+	MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
 };
 
 static const struct snd_kcontrol_new pri_rx_voice_mixer_controls[] = {
@@ -1639,6 +1744,9 @@
 	SOC_SINGLE_EXT("PRI_TX_Voice", MSM_BACKEND_DAI_PRI_I2S_TX,
 	MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
 	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("SEC_TX_Voice", MSM_BACKEND_DAI_SEC_I2S_TX,
+	MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
 	SOC_SINGLE_EXT("MI2S_TX_Voice", MSM_BACKEND_DAI_MI2S_TX,
 	MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
 	msm_routing_put_voice_mixer),
@@ -1663,6 +1771,9 @@
 	SOC_SINGLE_EXT("PRI_TX_VoLTE", MSM_BACKEND_DAI_PRI_I2S_TX,
 	MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer,
 	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("SEC_TX_VoLTE", MSM_BACKEND_DAI_SEC_I2S_TX,
+	MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
 	SOC_SINGLE_EXT("SLIM_0_TX_VoLTE", MSM_BACKEND_DAI_SLIMBUS_0_TX,
 	MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer,
 	msm_routing_put_voice_mixer),
@@ -1684,6 +1795,9 @@
 	SOC_SINGLE_EXT("PRI_TX_SGLTE", MSM_BACKEND_DAI_PRI_I2S_TX,
 	MSM_FRONTEND_DAI_SGLTE, 1, 0, msm_routing_get_voice_mixer,
 	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("SEC_TX_SGLTE", MSM_BACKEND_DAI_SEC_I2S_TX,
+	MSM_FRONTEND_DAI_SGLTE, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
 	SOC_SINGLE_EXT("MI2S_TX_SGLTE", MSM_BACKEND_DAI_MI2S_TX,
 	MSM_FRONTEND_DAI_SGLTE, 1, 0, msm_routing_get_voice_mixer,
 	msm_routing_put_voice_mixer),
@@ -1707,6 +1821,9 @@
 	SOC_SINGLE_EXT("PRI_TX_Voip", MSM_BACKEND_DAI_PRI_I2S_TX,
 	MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
 	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("SEC_TX_Voip", MSM_BACKEND_DAI_SEC_I2S_TX,
+	MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
 	SOC_SINGLE_EXT("MI2S_TX_Voip", MSM_BACKEND_DAI_MI2S_TX,
 	MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
 	msm_routing_put_voice_mixer),
@@ -1749,6 +1866,9 @@
 	SOC_SINGLE_EXT("PRIMARY_I2S_TX", MSM_BACKEND_DAI_PRI_I2S_TX,
 	MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
 	msm_routing_put_voice_stub_mixer),
+	SOC_SINGLE_EXT("SECONDARY_I2S_TX", MSM_BACKEND_DAI_SEC_I2S_TX,
+	MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+	msm_routing_put_voice_stub_mixer),
 	SOC_SINGLE_EXT("AFE_PCM_TX", MSM_BACKEND_DAI_AFE_PCM_TX,
 	MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
 	msm_routing_put_voice_stub_mixer),
@@ -2138,6 +2258,7 @@
 	SND_SOC_DAPM_AIF_IN("MM_DL6", "MultiMedia6 Playback", 0, 0, 0, 0),
 	SND_SOC_DAPM_AIF_IN("MM_DL7", "MultiMedia7 Playback", 0, 0, 0, 0),
 	SND_SOC_DAPM_AIF_IN("MM_DL8", "MultiMedia8 Playback", 0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("MM_DL9", "Pseudo Playback", 0, 0, 0, 0),
 	SND_SOC_DAPM_AIF_IN("VOIP_DL", "VoIP Playback", 0, 0, 0, 0),
 	SND_SOC_DAPM_AIF_OUT("MM_UL1", "MultiMedia1 Capture", 0, 0, 0, 0),
 	SND_SOC_DAPM_AIF_OUT("MM_UL2", "MultiMedia2 Capture", 0, 0, 0, 0),
@@ -2176,8 +2297,10 @@
 				0, 0, 0 , 0),
 	SND_SOC_DAPM_AIF_OUT("SLIMBUS_0_RX", "Slimbus Playback", 0, 0, 0, 0),
 	SND_SOC_DAPM_AIF_OUT("HDMI", "HDMI Playback", 0, 0, 0 , 0),
+	SND_SOC_DAPM_AIF_OUT("PSEUDO", "PSEUDO Playback", 0, 0, 0 , 0),
 	SND_SOC_DAPM_AIF_OUT("MI2S_RX", "MI2S Playback", 0, 0, 0, 0),
 	SND_SOC_DAPM_AIF_IN("PRI_I2S_TX", "Primary I2S Capture", 0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("SEC_I2S_TX", "Secondary I2S Capture", 0, 0, 0, 0),
 	SND_SOC_DAPM_AIF_IN("MI2S_TX", "MI2S Capture", 0, 0, 0, 0),
 	SND_SOC_DAPM_AIF_IN("SLIMBUS_0_TX", "Slimbus Capture", 0, 0, 0, 0),
 	SND_SOC_DAPM_AIF_OUT("INT_BT_SCO_RX", "Internal BT-SCO Playback",
@@ -2234,6 +2357,8 @@
 	slimbus_rx_mixer_controls, ARRAY_SIZE(slimbus_rx_mixer_controls)),
 	SND_SOC_DAPM_MIXER("HDMI Mixer", SND_SOC_NOPM, 0, 0,
 	hdmi_mixer_controls, ARRAY_SIZE(hdmi_mixer_controls)),
+	SND_SOC_DAPM_MIXER("PSEUDO Mixer", SND_SOC_NOPM, 0, 0,
+	pseudo_mixer_controls, ARRAY_SIZE(pseudo_mixer_controls)),
 	SND_SOC_DAPM_MIXER("MI2S_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
 	mi2s_rx_mixer_controls, ARRAY_SIZE(mi2s_rx_mixer_controls)),
 	SND_SOC_DAPM_MIXER("MultiMedia1 Mixer", SND_SOC_NOPM, 0, 0,
@@ -2372,6 +2497,7 @@
 	{"SEC_RX Audio Mixer", "MultiMedia6", "MM_DL6"},
 	{"SEC_RX Audio Mixer", "MultiMedia7", "MM_DL7"},
 	{"SEC_RX Audio Mixer", "MultiMedia8", "MM_DL8"},
+	{"SEC_RX Audio Mixer", "Pseudo", "MM_DL9"},
 	{"SEC_I2S_RX", NULL, "SEC_RX Audio Mixer"},
 
 	{"SLIMBUS_0_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
@@ -2392,8 +2518,12 @@
 	{"HDMI Mixer", "MultiMedia6", "MM_DL6"},
 	{"HDMI Mixer", "MultiMedia7", "MM_DL7"},
 	{"HDMI Mixer", "MultiMedia8", "MM_DL8"},
+	{"HDMI Mixer", "Pseudo", "MM_DL9"},
 	{"HDMI", NULL, "HDMI Mixer"},
 
+	{"PSEUDO Mixer", "MultiMedia4", "MM_DL4"},
+	{"PSEUDO", NULL, "PSEUDO Mixer"},
+
 		/* incall */
 	{"Incall_Music Audio Mixer", "MultiMedia1", "MM_DL1"},
 	{"Incall_Music Audio Mixer", "MultiMedia2", "MM_DL2"},
@@ -2404,6 +2534,8 @@
 
 	{"MultiMedia1 Mixer", "VOC_REC_UL", "INCALL_RECORD_TX"},
 	{"MultiMedia1 Mixer", "VOC_REC_DL", "INCALL_RECORD_RX"},
+	{"MultiMedia4 Mixer", "VOC_REC_UL", "INCALL_RECORD_TX"},
+	{"MultiMedia4 Mixer", "VOC_REC_DL", "INCALL_RECORD_RX"},
 	{"MultiMedia1 Mixer", "SLIM_4_TX", "SLIMBUS_4_TX"},
 	{"MultiMedia5 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
 	{"MI2S_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
@@ -2414,6 +2546,7 @@
 	{"MI2S_RX", NULL, "MI2S_RX Audio Mixer"},
 
 	{"MultiMedia1 Mixer", "PRI_TX", "PRI_I2S_TX"},
+	{"MultiMedia1 Mixer", "SEC_TX", "SEC_I2S_TX"},
 	{"MultiMedia1 Mixer", "MI2S_TX", "MI2S_TX"},
 	{"MultiMedia2 Mixer", "MI2S_TX", "MI2S_TX"},
 	{"MultiMedia4 Mixer", "MI2S_TX", "MI2S_TX"},
@@ -2519,6 +2652,7 @@
 	{"HDMI", NULL, "HDMI_DL_HL"},
 
 	{"Voice_Tx Mixer", "PRI_TX_Voice", "PRI_I2S_TX"},
+	{"Voice_Tx Mixer", "SEC_TX_Voice", "SEC_I2S_TX"},
 	{"Voice_Tx Mixer", "MI2S_TX_Voice", "MI2S_TX"},
 	{"Voice_Tx Mixer", "SLIM_0_TX_Voice", "SLIMBUS_0_TX"},
 	{"Voice_Tx Mixer", "INTERNAL_BT_SCO_TX_Voice", "INT_BT_SCO_TX"},
@@ -2527,6 +2661,7 @@
 	{"Voice_Tx Mixer", "SEC_AUX_PCM_TX_Voice", "SEC_AUX_PCM_TX"},
 	{"CS-VOICE_UL1", NULL, "Voice_Tx Mixer"},
 	{"VoLTE_Tx Mixer", "PRI_TX_VoLTE", "PRI_I2S_TX"},
+	{"VoLTE_Tx Mixer", "SEC_TX_VoLTE", "SEC_I2S_TX"},
 	{"VoLTE_Tx Mixer", "SLIM_0_TX_VoLTE", "SLIMBUS_0_TX"},
 	{"VoLTE_Tx Mixer", "INTERNAL_BT_SCO_TX_VoLTE", "INT_BT_SCO_TX"},
 	{"VoLTE_Tx Mixer", "AFE_PCM_TX_VoLTE", "PCM_TX"},
@@ -2534,6 +2669,7 @@
 	{"VoLTE_Tx Mixer", "SEC_AUX_PCM_TX_VoLTE", "SEC_AUX_PCM_TX"},
 	{"VoLTE_UL", NULL, "VoLTE_Tx Mixer"},
 	{"SGLTE_Tx Mixer", "PRI_TX_SGLTE", "PRI_I2S_TX"},
+	{"SGLTE_Tx Mixer", "SEC_TX_SGLTE", "SEC_I2S_TX"},
 	{"SGLTE_Tx Mixer", "MI2S_TX_SGLTE", "MI2S_TX"},
 	{"SGLTE_Tx Mixer", "SLIM_0_TX_SGLTE", "SLIMBUS_0_TX"},
 	{"SGLTE_Tx Mixer", "INTERNAL_BT_SCO_TX_SGLTE", "INT_BT_SCO_TX"},
@@ -2542,6 +2678,7 @@
 	{"SGLTE_Tx Mixer", "SEC_AUX_PCM_TX_SGLTE", "SEC_AUX_PCM_TX"},
 	{"SGLTE_UL", NULL, "SGLTE_Tx Mixer"},
 	{"Voip_Tx Mixer", "PRI_TX_Voip", "PRI_I2S_TX"},
+	{"Voip_Tx Mixer", "SEC_TX_Voip", "SEC_I2S_TX"},
 	{"Voip_Tx Mixer", "MI2S_TX_Voip", "MI2S_TX"},
 	{"Voip_Tx Mixer", "SLIM_0_TX_Voip", "SLIMBUS_0_TX"},
 	{"Voip_Tx Mixer", "INTERNAL_BT_SCO_TX_Voip", "INT_BT_SCO_TX"},
@@ -2581,6 +2718,7 @@
 	{"Voice Stub Tx Mixer", "MI2S_TX", "MI2S_TX"},
 	{"Voice Stub Tx Mixer", "SLIM_3_TX", "SLIMBUS_3_TX"},
 	{"Voice Stub Tx Mixer", "PRIMARY_I2S_TX", "PRI_I2S_TX"},
+	{"Voice Stub Tx Mixer", "SECONDARY_I2S_TX", "SEC_I2S_TX"},
 	{"Voice Stub Tx Mixer", "AFE_PCM_TX", "PCM_TX"},
 	{"VOICE_STUB_UL", NULL, "Voice Stub Tx Mixer"},
 
@@ -2631,8 +2769,10 @@
 	{"BE_OUT", NULL, "SLIMBUS_3_RX"},
 	{"BE_OUT", NULL, "SLIMBUS_4_RX"},
 	{"BE_OUT", NULL, "HDMI"},
+	{"BE_OUT", NULL, "PSEUDO"},
 	{"BE_OUT", NULL, "MI2S_RX"},
 	{"PRI_I2S_TX", NULL, "BE_IN"},
+	{"SEC_I2S_TX", NULL, "BE_IN"},
 	{"MI2S_TX", NULL, "BE_IN"},
 	{"SLIMBUS_0_TX", NULL, "BE_IN" },
 	{"SLIMBUS_1_TX", NULL, "BE_IN" },
@@ -2749,7 +2889,14 @@
 	for_each_set_bit(i, &bedai->fe_sessions, MSM_FRONTEND_DAI_MM_SIZE) {
 		if (fe_dai_map[i][session_type] != INVALID_SESSION) {
 			channels = bedai->channel;
-			if ((playback || capture)
+			if (bedai->port_id == PSEUDOPORT_01) {
+				adm_multi_ch_copp_pseudo_open_v3(bedai->port_id,
+							path_type,
+							bedai->sample_rate,
+							channels > 6 ? 6 :
+							channels,
+							DEFAULT_COPP_TOPOLOGY);
+			} else if ((playback || capture)
 				&& ((channels == 2) || (channels == 1)) &&
 				bedai->perf_mode) {
 				adm_multi_ch_copp_open(bedai->port_id,
@@ -2867,6 +3014,7 @@
 	snd_soc_add_platform_controls(platform,
 				ec_ref_rx_mixer_controls,
 			ARRAY_SIZE(ec_ref_rx_mixer_controls));
+
 	return 0;
 }
 
diff --git a/sound/soc/msm/msm-pcm-routing.h b/sound/soc/msm/msm-pcm-routing.h
index 6b87475..32ab063 100644
--- a/sound/soc/msm/msm-pcm-routing.h
+++ b/sound/soc/msm/msm-pcm-routing.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -18,6 +18,7 @@
 #define LPASS_BE_SLIMBUS_0_RX "SLIMBUS_0_RX"
 #define LPASS_BE_SLIMBUS_0_TX "SLIMBUS_0_TX"
 #define LPASS_BE_HDMI "HDMI"
+#define LPASS_BE_PSEUDO "PSEUDO"
 #define LPASS_BE_INT_BT_SCO_RX "INT_BT_SCO_RX"
 #define LPASS_BE_INT_BT_SCO_TX "INT_BT_SCO_TX"
 #define LPASS_BE_INT_FM_RX "INT_FM_RX"
@@ -32,6 +33,7 @@
 #define LPASS_BE_INCALL_RECORD_RX "INCALL_RECORD_TX"
 #define LPASS_BE_INCALL_RECORD_TX "INCALL_RECORD_RX"
 #define LPASS_BE_SEC_I2S_RX "SECONDARY_I2S_RX"
+#define LPASS_BE_SEC_I2S_TX "SECONDARY_I2S_TX"
 
 #define LPASS_BE_MI2S_RX "MI2S_RX"
 #define LPASS_BE_MI2S_TX "MI2S_TX"
@@ -60,6 +62,7 @@
 	MSM_FRONTEND_DAI_MULTIMEDIA6,
 	MSM_FRONTEND_DAI_MULTIMEDIA7,
 	MSM_FRONTEND_DAI_MULTIMEDIA8,
+	MSM_FRONTEND_DAI_PSEUDO,
 	MSM_FRONTEND_DAI_CS_VOICE,
 	MSM_FRONTEND_DAI_VOIP,
 	MSM_FRONTEND_DAI_AFE_RX,
@@ -70,8 +73,8 @@
 	MSM_FRONTEND_DAI_MAX,
 };
 
-#define MSM_FRONTEND_DAI_MM_SIZE (MSM_FRONTEND_DAI_MULTIMEDIA8 + 1)
-#define MSM_FRONTEND_DAI_MM_MAX_ID MSM_FRONTEND_DAI_MULTIMEDIA8
+#define MSM_FRONTEND_DAI_MM_SIZE (MSM_FRONTEND_DAI_PSEUDO + 1)
+#define MSM_FRONTEND_DAI_MM_MAX_ID MSM_FRONTEND_DAI_PSEUDO
 
 enum {
 	MSM_BACKEND_DAI_PRI_I2S_RX = 0,
@@ -93,6 +96,7 @@
 	MSM_BACKEND_DAI_MI2S_RX,
 	MSM_BACKEND_DAI_MI2S_TX,
 	MSM_BACKEND_DAI_SEC_I2S_RX,
+	MSM_BACKEND_DAI_SEC_I2S_TX,
 	MSM_BACKEND_DAI_SLIMBUS_1_RX,
 	MSM_BACKEND_DAI_SLIMBUS_1_TX,
 	MSM_BACKEND_DAI_SLIMBUS_4_RX,
@@ -104,6 +108,7 @@
 	MSM_BACKEND_DAI_EXTPROC_EC_TX,
 	MSM_BACKEND_DAI_SEC_AUXPCM_RX,
 	MSM_BACKEND_DAI_SEC_AUXPCM_TX,
+	MSM_BACKEND_DAI_PSEUDO_PORT,
 	MSM_BACKEND_DAI_MAX,
 };
 
@@ -116,6 +121,12 @@
 void msm_pcm_routing_reg_psthr_stream(int fedai_id, int dspst_id,
 		int stream_type, int enable);
 
+void msm_pcm_routing_reg_pseudo_stream(int fedai_id, bool perf_mode,
+				int dspst_id, int stream_type, int sample_rate,
+				int channels);
+
+void msm_pcm_routing_dereg_pseudo_stream(int fedai_id, int dspst_id);
+
 void msm_pcm_routing_dereg_phy_stream(int fedai_id, int stream_type);
 
 int lpa_set_volume(unsigned volume);
@@ -126,4 +137,6 @@
 
 int compressed_set_volume(unsigned volume);
 
+void multi_ch_pcm_set_channel_map(char *channel_mapping);
+
 #endif /*_MSM_PCM_H*/
diff --git a/sound/soc/msm/msm8930.c b/sound/soc/msm/msm8930.c
index 4725e8e..42699c9 100644
--- a/sound/soc/msm/msm8930.c
+++ b/sound/soc/msm/msm8930.c
@@ -183,9 +183,9 @@
 				pm8xxx_spk_enable(MSM8930_SPK_ON);
 			}
 
-			pr_debug("%s: slepping 4 ms after turning on external "
+			pr_debug("%s: sleeping 10 ms after turning on external "
 				" Left Speaker Ampl\n", __func__);
-			usleep_range(4000, 4000);
+			usleep_range(10000, 10000);
 		}
 
 	} else  {
@@ -218,9 +218,9 @@
 
 		pm8xxx_spk_enable(MSM8930_SPK_OFF);
 		msm8930_ext_spk_pamp = 0;
-		pr_debug("%s: slepping 4 ms after turning on external "
+		pr_debug("%s: slepping 10 ms after turning on external "
 			" Left Speaker Ampl\n", __func__);
-		usleep_range(4000, 4000);
+		usleep_range(10000, 10000);
 
 	} else  {
 
@@ -621,13 +621,6 @@
 			pr_err("%s: failed to set cpu chan map\n", __func__);
 			goto end;
 		}
-		ret = snd_soc_dai_set_channel_map(codec_dai, 0, 0,
-				msm8930_slim_0_rx_ch, rx_ch);
-		if (ret < 0) {
-			pr_err("%s: failed to set codec channel map\n",
-							       __func__);
-			goto end;
-		}
 	} else {
 		ret = snd_soc_dai_get_channel_map(codec_dai,
 				&tx_ch_cnt, tx_ch, &rx_ch_cnt , rx_ch);
@@ -641,14 +634,6 @@
 			pr_err("%s: failed to set cpu chan map\n", __func__);
 			goto end;
 		}
-		ret = snd_soc_dai_set_channel_map(codec_dai,
-				msm8930_slim_0_tx_ch, tx_ch, 0, 0);
-		if (ret < 0) {
-			pr_err("%s: failed to set codec channel map\n",
-							       __func__);
-			goto end;
-		}
-
 	}
 end:
 	return ret;
@@ -660,6 +645,14 @@
 	struct snd_soc_codec *codec = rtd->codec;
 	struct snd_soc_dapm_context *dapm = &codec->dapm;
 	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+	struct snd_soc_dai *codec_dai = rtd->codec_dai;
+
+	/* Tabla SLIMBUS configuration
+	 * RX1, RX2, RX3, RX4, RX5
+	 * TX1, TX2, TX3, TX4, TX5
+	 */
+	unsigned int rx_ch[SITAR_RX_MAX] = {138, 139, 140, 141, 142};
+	unsigned int tx_ch[SITAR_TX_MAX]  = {128, 129, 130, 131, 132};
 
 	pr_debug("%s()\n", __func__);
 
@@ -690,6 +683,9 @@
 	}
 	codec_clk = clk_get(cpu_dai->dev, "osr_clk");
 
+	snd_soc_dai_set_channel_map(codec_dai, ARRAY_SIZE(tx_ch),
+				    tx_ch, ARRAY_SIZE(rx_ch), rx_ch);
+
 	mbhc_cfg.gpio = 37;
 	mbhc_cfg.gpio_irq = gpio_to_irq(mbhc_cfg.gpio);
 	sitar_hs_detect(codec, &mbhc_cfg);
@@ -792,6 +788,18 @@
 	return 0;
 }
 
+static int msm8930_proxy_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+			struct snd_pcm_hw_params *params)
+{
+	struct snd_interval *rate = hw_param_interval(params,
+	SNDRV_PCM_HW_PARAM_RATE);
+
+	pr_debug("%s()\n", __func__);
+	rate->min = rate->max = 48000;
+
+	return 0;
+}
+
 static int msm8930_aux_pcm_get_gpios(void)
 {
 	int ret = 0;
@@ -1199,6 +1207,7 @@
 		.codec_dai_name = "msm-stub-rx",
 		.no_pcm = 1,
 		.be_id = MSM_BACKEND_DAI_AFE_PCM_RX,
+		.be_hw_params_fixup = msm8930_proxy_be_hw_params_fixup,
 		.ignore_pmdown_time = 1, /* this dainlink has playback support */
 	},
 	{
@@ -1210,6 +1219,7 @@
 		.codec_dai_name = "msm-stub-tx",
 		.no_pcm = 1,
 		.be_id = MSM_BACKEND_DAI_AFE_PCM_TX,
+		.be_hw_params_fixup = msm8930_proxy_be_hw_params_fixup,
 	},
 	/* AUX PCM Backend DAI Links */
 	{
@@ -1311,7 +1321,7 @@
 {
 	int ret;
 
-	if (!cpu_is_msm8930() && !cpu_is_msm8930aa() && !cpu_is_msm8627()) {
+	if (!soc_class_is_msm8930()) {
 		pr_err("%s: Not the right machine type\n", __func__);
 		return -ENODEV ;
 	}
@@ -1350,7 +1360,7 @@
 
 static void __exit msm8930_audio_exit(void)
 {
-	if (!cpu_is_msm8930() && !cpu_is_msm8930aa() && !cpu_is_msm8627()) {
+	if (!soc_class_is_msm8930()) {
 		pr_err("%s: Not the right machine type\n", __func__);
 		return ;
 	}
diff --git a/sound/soc/msm/msm8960.c b/sound/soc/msm/msm8960.c
index 59d118e..ad78255 100644
--- a/sound/soc/msm/msm8960.c
+++ b/sound/soc/msm/msm8960.c
@@ -777,13 +777,6 @@
 			pr_err("%s: failed to set cpu chan map\n", __func__);
 			goto end;
 		}
-		ret = snd_soc_dai_set_channel_map(codec_dai, 0, 0,
-				msm8960_slim_0_rx_ch, rx_ch);
-		if (ret < 0) {
-			pr_err("%s: failed to set codec channel map\n",
-								__func__);
-			goto end;
-		}
 	} else {
 
 		pr_debug("%s: %s  tx_dai_id = %d  num_ch = %d\n", __func__,
@@ -801,13 +794,6 @@
 			pr_err("%s: failed to set cpu chan map\n", __func__);
 			goto end;
 		}
-		ret = snd_soc_dai_set_channel_map(codec_dai,
-				msm8960_slim_0_tx_ch, tx_ch, 0, 0);
-		if (ret < 0) {
-			pr_err("%s: failed to set codec channel map\n",
-								__func__);
-			goto end;
-		}
 	}
 end:
 	return ret;
@@ -845,13 +831,6 @@
 			pr_err("%s: failed to set cpu chan map\n", __func__);
 			goto end;
 		}
-		ret = snd_soc_dai_set_channel_map(codec_dai, 0, 0,
-				num_rx_ch, rx_ch);
-		if (ret < 0) {
-			pr_err("%s: failed to set codec channel map\n",
-								__func__);
-			goto end;
-		}
 	} else {
 		num_tx_ch =  params_channels(params);
 
@@ -871,13 +850,6 @@
 			pr_err("%s: failed to set cpu chan map\n", __func__);
 			goto end;
 		}
-		ret = snd_soc_dai_set_channel_map(codec_dai,
-				num_tx_ch, tx_ch, 0, 0);
-		if (ret < 0) {
-			pr_err("%s: failed to set codec channel map\n",
-								__func__);
-			goto end;
-		}
 	}
 end:
 	return ret;
@@ -896,6 +868,15 @@
 		.vin_sel = 2,
 		.inv_int_pol = 0,
 	};
+	struct snd_soc_dai *codec_dai = rtd->codec_dai;
+
+	/* Tabla SLIMBUS configuration
+	 * RX1, RX2, RX3, RX4, RX5, RX6, RX7
+	 * TX1, TX2, TX3, TX4, TX5, TX6, TX7, TX8
+	 */
+	unsigned int rx_ch[TABLA_RX_MAX] = {138, 139, 140, 141, 142, 143, 144};
+	unsigned int tx_ch[TABLA_TX_MAX]  = {128, 129, 130, 131, 132, 133, 134,
+					     135, 136, 137};
 
 	pr_debug("%s(), dev_name%s\n", __func__, dev_name(cpu_dai->dev));
 
@@ -958,6 +939,8 @@
 	mbhc_cfg.read_fw_bin = hs_detect_use_firmware;
 
 	err = tabla_hs_detect(codec, &mbhc_cfg);
+	snd_soc_dai_set_channel_map(codec_dai, ARRAY_SIZE(tx_ch),
+				    tx_ch, ARRAY_SIZE(rx_ch), rx_ch);
 
 	return err;
 }
@@ -1018,6 +1001,8 @@
 	pr_debug("%s channels->min %u channels->max %u ()\n", __func__,
 			channels->min, channels->max);
 
+	if (channels->max < 2)
+		channels->min = channels->max = 2;
 	rate->min = rate->max = 48000;
 
 	return 0;
@@ -1760,7 +1745,7 @@
 {
 	int ret;
 
-	if (!cpu_is_msm8960() && !cpu_is_msm8960ab()) {
+	if (!soc_class_is_msm8960()) {
 		pr_debug("%s: Not the right machine type\n", __func__);
 		return -ENODEV ;
 	}
@@ -1833,7 +1818,7 @@
 
 static void __exit msm8960_audio_exit(void)
 {
-	if (!cpu_is_msm8960() && !cpu_is_msm8960ab()) {
+	if (!soc_class_is_msm8960()) {
 		pr_debug("%s: Not the right machine type\n", __func__);
 		return ;
 	}
diff --git a/sound/soc/msm/msm8974.c b/sound/soc/msm/msm8974.c
index f462299..e8ea058 100644
--- a/sound/soc/msm/msm8974.c
+++ b/sound/soc/msm/msm8974.c
@@ -52,10 +52,27 @@
 #define GPIO_AUX_PCM_SYNC 45
 #define GPIO_AUX_PCM_CLK 46
 
-#define TABLA_EXT_CLK_RATE 12288000
+#define WCD9XXX_MBHC_DEF_BUTTONS 8
+#define WCD9XXX_MBHC_DEF_RLOADS 5
+#define TAIKO_EXT_CLK_RATE 9600000
 
-#define TABLA_MBHC_DEF_BUTTONS 8
-#define TABLA_MBHC_DEF_RLOADS 5
+void *def_taiko_mbhc_cal(void);
+static int msm_snd_enable_codec_ext_clk(struct snd_soc_codec *codec, int enable,
+					bool dapm);
+
+static struct wcd9xxx_mbhc_config mbhc_cfg = {
+	.read_fw_bin = false,
+	.calibration = NULL,
+	.micbias = MBHC_MICBIAS2,
+	.mclk_cb_fn = msm_snd_enable_codec_ext_clk,
+	.mclk_rate = TAIKO_EXT_CLK_RATE,
+	.gpio = 0,
+	.gpio_irq = 0,
+	.gpio_level_insert = 1,
+	.detect_extn_cable = true,
+	.insert_detect = true,
+	.swap_gnd_mic = NULL,
+};
 
 struct msm8974_asoc_mach_data {
 	int mclk_gpio;
@@ -83,9 +100,6 @@
 static int msm_btsco_rate = BTSCO_RATE_8KHZ;
 static int msm_btsco_ch = 1;
 
-static struct snd_soc_jack hs_jack;
-static struct snd_soc_jack button_jack;
-
 static struct mutex cdc_mclk_mutex;
 static struct q_clkdiv *codec_clk;
 static int clk_users;
@@ -317,7 +331,7 @@
 	return 0;
 }
 
-static int msm8974_enable_codec_ext_clk(struct snd_soc_codec *codec, int enable,
+static int msm_snd_enable_codec_ext_clk(struct snd_soc_codec *codec, int enable,
 					bool dapm)
 {
 	int ret = 0;
@@ -366,9 +380,9 @@
 
 	switch (event) {
 	case SND_SOC_DAPM_PRE_PMU:
-		return msm8974_enable_codec_ext_clk(w->codec, 1, true);
+		return msm_snd_enable_codec_ext_clk(w->codec, 1, true);
 	case SND_SOC_DAPM_POST_PMD:
-		return msm8974_enable_codec_ext_clk(w->codec, 0, true);
+		return msm_snd_enable_codec_ext_clk(w->codec, 0, true);
 	}
 
 	return 0;
@@ -524,6 +538,23 @@
 	return 0;
 }
 
+static int msm8974_hdmi_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+					struct snd_pcm_hw_params *params)
+{
+	struct snd_interval *rate = hw_param_interval(params,
+					SNDRV_PCM_HW_PARAM_RATE);
+
+	struct snd_interval *channels = hw_param_interval(params,
+					SNDRV_PCM_HW_PARAM_CHANNELS);
+
+	pr_debug("%s channels->min %u channels->max %u ()\n", __func__,
+			channels->min, channels->max);
+
+	rate->min = rate->max = 48000;
+
+	return 0;
+}
+
 static int msm_aux_pcm_get_gpios(void)
 {
 	int ret = 0;
@@ -639,6 +670,18 @@
 	return 0;
 }
 
+static int msm_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+				struct snd_pcm_hw_params *params)
+{
+	struct snd_interval *rate = hw_param_interval(params,
+					SNDRV_PCM_HW_PARAM_RATE);
+
+	pr_debug("%s()\n", __func__);
+	rate->min = rate->max = 48000;
+
+	return 0;
+}
+
 static const struct soc_enum msm_snd_enum[] = {
 	SOC_ENUM_SINGLE_EXT(2, spk_function),
 	SOC_ENUM_SINGLE_EXT(2, slim0_rx_ch_text),
@@ -660,6 +703,19 @@
 	struct snd_soc_codec *codec = rtd->codec;
 	struct snd_soc_dapm_context *dapm = &codec->dapm;
 	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+	struct snd_soc_dai *codec_dai = rtd->codec_dai;
+
+	/* Taiko SLIMBUS configuration
+	 * RX1, RX2, RX3, RX4, RX5, RX6, RX7, RX8, RX9, RX10, RX11, RX12, RX13
+	 * TX1, TX2, TX3, TX4, TX5, TX6, TX7, TX8, TX9, TX10, TX11, TX12, TX13
+	 * TX14, TX15, TX16
+	 */
+	unsigned int rx_ch[TAIKO_RX_MAX] = {144, 145, 146, 147, 148, 149, 150,
+					    151, 152, 153, 154, 155, 156};
+	unsigned int tx_ch[TAIKO_TX_MAX]  = {128, 129, 130, 131, 132, 133,
+					     134, 135, 136, 137, 138, 139,
+					     140, 141, 142, 143};
+
 
 	pr_info("%s(), dev_name%s\n", __func__, dev_name(cpu_dai->dev));
 
@@ -685,21 +741,15 @@
 
 	snd_soc_dapm_sync(dapm);
 
-	err = snd_soc_jack_new(codec, "Headset Jack",
-			       (SND_JACK_HEADSET | SND_JACK_OC_HPHL |
-				SND_JACK_OC_HPHR | SND_JACK_UNSUPPORTED),
-			       &hs_jack);
-	if (err) {
-		pr_err("failed to create new jack\n");
-		return err;
-	}
+	snd_soc_dai_set_channel_map(codec_dai, ARRAY_SIZE(tx_ch),
+				    tx_ch, ARRAY_SIZE(rx_ch), rx_ch);
 
-	err = snd_soc_jack_new(codec, "Button Jack",
-			       TAIKO_JACK_BUTTON_MASK, &button_jack);
-	if (err) {
-		pr_err("failed to create new jack\n");
-		return err;
-	}
+	/* start mbhc */
+	mbhc_cfg.calibration = def_taiko_mbhc_cal();
+	if (mbhc_cfg.calibration)
+		err = taiko_hs_detect(codec, &mbhc_cfg);
+	else
+		err = -ENOMEM;
 
 	return err;
 }
@@ -711,6 +761,84 @@
 	return 0;
 }
 
+void *def_taiko_mbhc_cal(void)
+{
+	void *taiko_cal;
+	struct wcd9xxx_mbhc_btn_detect_cfg *btn_cfg;
+	u16 *btn_low, *btn_high;
+	u8 *n_ready, *n_cic, *gain;
+
+	taiko_cal = kzalloc(WCD9XXX_MBHC_CAL_SIZE(WCD9XXX_MBHC_DEF_BUTTONS,
+						WCD9XXX_MBHC_DEF_RLOADS),
+			    GFP_KERNEL);
+	if (!taiko_cal) {
+		pr_err("%s: out of memory\n", __func__);
+		return NULL;
+	}
+
+#define S(X, Y) ((WCD9XXX_MBHC_CAL_GENERAL_PTR(taiko_cal)->X) = (Y))
+	S(t_ldoh, 100);
+	S(t_bg_fast_settle, 100);
+	S(t_shutdown_plug_rem, 255);
+	S(mbhc_nsa, 4);
+	S(mbhc_navg, 4);
+#undef S
+#define S(X, Y) ((WCD9XXX_MBHC_CAL_PLUG_DET_PTR(taiko_cal)->X) = (Y))
+	S(mic_current, TAIKO_PID_MIC_5_UA);
+	S(hph_current, TAIKO_PID_MIC_5_UA);
+	S(t_mic_pid, 100);
+	S(t_ins_complete, 250);
+	S(t_ins_retry, 200);
+#undef S
+#define S(X, Y) ((WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(taiko_cal)->X) = (Y))
+	S(v_no_mic, 30);
+	S(v_hs_max, 2400);
+#undef S
+#define S(X, Y) ((WCD9XXX_MBHC_CAL_BTN_DET_PTR(taiko_cal)->X) = (Y))
+	S(c[0], 62);
+	S(c[1], 124);
+	S(nc, 1);
+	S(n_meas, 3);
+	S(mbhc_nsc, 11);
+	S(n_btn_meas, 1);
+	S(n_btn_con, 2);
+	S(num_btn, WCD9XXX_MBHC_DEF_BUTTONS);
+	S(v_btn_press_delta_sta, 100);
+	S(v_btn_press_delta_cic, 50);
+#undef S
+	btn_cfg = WCD9XXX_MBHC_CAL_BTN_DET_PTR(taiko_cal);
+	btn_low = wcd9xxx_mbhc_cal_btn_det_mp(btn_cfg, MBHC_BTN_DET_V_BTN_LOW);
+	btn_high = wcd9xxx_mbhc_cal_btn_det_mp(btn_cfg,
+					       MBHC_BTN_DET_V_BTN_HIGH);
+	btn_low[0] = -50;
+	btn_high[0] = 34;
+	btn_low[1] = 35;
+	btn_high[1] = 52;
+	btn_low[2] = 53;
+	btn_high[2] = 94;
+	btn_low[3] = 95;
+	btn_high[3] = 133;
+	btn_low[4] = 134;
+	btn_high[4] = 171;
+	btn_low[5] = 172;
+	btn_high[5] = 208;
+	btn_low[6] = 209;
+	btn_high[6] = 244;
+	btn_low[7] = 245;
+	btn_high[7] = 330;
+	n_ready = wcd9xxx_mbhc_cal_btn_det_mp(btn_cfg, MBHC_BTN_DET_N_READY);
+	n_ready[0] = 80;
+	n_ready[1] = 68;
+	n_cic = wcd9xxx_mbhc_cal_btn_det_mp(btn_cfg, MBHC_BTN_DET_N_CIC);
+	n_cic[0] = 60;
+	n_cic[1] = 47;
+	gain = wcd9xxx_mbhc_cal_btn_det_mp(btn_cfg, MBHC_BTN_DET_GAIN);
+	gain[0] = 11;
+	gain[1] = 9;
+
+	return taiko_cal;
+}
+
 static int msm_snd_hw_params(struct snd_pcm_substream *substream,
 			     struct snd_pcm_hw_params *params)
 {
@@ -737,20 +865,8 @@
 			pr_err("%s: failed to set cpu chan map\n", __func__);
 			goto end;
 		}
-		ret = snd_soc_dai_set_channel_map(codec_dai, 0, 0,
-						  msm_slim_0_rx_ch, rx_ch);
-		if (ret < 0) {
-			pr_err("%s: failed to set codec channel map\n",
-			       __func__);
-			goto end;
-		}
 	} else {
 
-		if (codec_dai->id == 2)
-			user_set_tx_ch = msm_slim_0_tx_ch;
-		else if (codec_dai->id == 4)
-			user_set_tx_ch = params_channels(params);
-
 		pr_debug("%s: %s_tx_dai_id_%d_ch=%d\n", __func__,
 			 codec_dai->name, codec_dai->id, user_set_tx_ch);
 
@@ -760,19 +876,24 @@
 			pr_err("%s: failed to get codec chan map\n", __func__);
 			goto end;
 		}
+		/* For tabla_tx1 case */
+		if (codec_dai->id == 1)
+			user_set_tx_ch = msm_slim_0_tx_ch;
+		/* For tabla_tx2 case */
+		else if (codec_dai->id == 3)
+			user_set_tx_ch = params_channels(params);
+		else
+			user_set_tx_ch = tx_ch_cnt;
+
+		pr_debug("%s: msm_slim_0_tx_ch(%d)user_set_tx_ch(%d)tx_ch_cnt(%d)\n",
+			 __func__, msm_slim_0_tx_ch, user_set_tx_ch, tx_ch_cnt);
+
 		ret = snd_soc_dai_set_channel_map(cpu_dai,
 						  user_set_tx_ch, tx_ch, 0 , 0);
 		if (ret < 0) {
 			pr_err("%s: failed to set cpu chan map\n", __func__);
 			goto end;
 		}
-		ret = snd_soc_dai_set_channel_map(codec_dai,
-						  user_set_tx_ch, tx_ch, 0, 0);
-		if (ret < 0) {
-			pr_err("%s: failed to set codec channel map\n",
-			       __func__);
-			goto end;
-		}
 	}
 end:
 	return ret;
@@ -974,6 +1095,30 @@
 		.be_id = MSM_BACKEND_DAI_INT_BT_SCO_TX,
 		.be_hw_params_fixup = msm_btsco_be_hw_params_fixup,
 	},
+	{
+		.name = LPASS_BE_INT_FM_RX,
+		.stream_name = "Internal FM Playback",
+		.cpu_dai_name = "msm-dai-q6-dev.12292",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-rx",
+		.no_pcm = 1,
+		.be_id = MSM_BACKEND_DAI_INT_FM_RX,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		/* this dainlink has playback support */
+		.ignore_pmdown_time = 1,
+	},
+	{
+		.name = LPASS_BE_INT_FM_TX,
+		.stream_name = "Internal FM Capture",
+		.cpu_dai_name = "msm-dai-q6-dev.12293",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-tx",
+		.no_pcm = 1,
+		.be_id = MSM_BACKEND_DAI_INT_FM_TX,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+	},
 	/* Backend AFE DAI Links */
 	{
 		.name = LPASS_BE_AFE_PCM_RX,
@@ -999,6 +1144,34 @@
 		.be_id = MSM_BACKEND_DAI_AFE_PCM_TX,
 		.be_hw_params_fixup = msm_proxy_be_hw_params_fixup,
 	},
+	/* HDMI Hostless */
+	{
+		.name = "HDMI_RX_HOSTLESS",
+		.stream_name = "HDMI_RX_HOSTLESS",
+		.cpu_dai_name = "HDMI_HOSTLESS",
+		.platform_name = "msm-pcm-hostless",
+		.dynamic = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			SND_SOC_DPCM_TRIGGER_POST},
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+	},
+	/* HDMI BACK END DAI Link */
+	{
+		.name = LPASS_BE_HDMI,
+		.stream_name = "HDMI Playback",
+		.cpu_dai_name = "msm-dai-q6-hdmi.8",
+		.platform_name = "msm-pcm-routing",
+		.codec_name     = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-rx",
+		.no_pcm = 1,
+		.be_id = MSM_BACKEND_DAI_HDMI_RX,
+		.be_hw_params_fixup = msm8974_hdmi_be_hw_params_fixup,
+		.ignore_pmdown_time = 1,
+	},
 	/* AUX PCM Backend DAI Links */
 	{
 		.name = LPASS_BE_AUXPCM_RX,
diff --git a/sound/soc/msm/qdsp6/q6adm.c b/sound/soc/msm/qdsp6/q6adm.c
index c6970f1..00394aa 100644
--- a/sound/soc/msm/qdsp6/q6adm.c
+++ b/sound/soc/msm/qdsp6/q6adm.c
@@ -45,7 +45,7 @@
 static struct acdb_cal_block mem_addr_audvol[MAX_AUDPROC_TYPES];
 
 static struct adm_ctl			this_adm;
-
+static int pseudo_copp[2];
 
 int srs_trumedia_open(int port_id, int srs_tech_id, void *srs_params)
 {
@@ -293,6 +293,8 @@
 			case ADM_CMD_MATRIX_MAP_ROUTINGS:
 			case ADM_CMD_CONNECT_AFE_PORT:
 			case ADM_CMD_DISCONNECT_AFE_PORT:
+			case ADM_CMD_CONNECT_AFE_PORT_V2:
+			case ADM_CMD_MULTI_CHANNEL_COPP_OPEN_V3:
 				atomic_set(&this_adm.copp_stat[index], 1);
 				wake_up(&this_adm.wait);
 				break;
@@ -305,9 +307,10 @@
 		}
 
 		switch (data->opcode) {
+
+		case ADM_CMDRSP_MULTI_CHANNEL_COPP_OPEN_V3:
 		case ADM_CMDRSP_COPP_OPEN:
-		case ADM_CMDRSP_MULTI_CHANNEL_COPP_OPEN:
-		case ADM_CMDRSP_MULTI_CHANNEL_COPP_OPEN_V3: {
+		case ADM_CMDRSP_MULTI_CHANNEL_COPP_OPEN: {
 			struct adm_copp_open_respond *open = data->payload;
 			if (open->copp_id == INVALID_COPP_ID) {
 				pr_err("%s: invalid coppid rxed %d\n",
@@ -316,6 +319,10 @@
 				wake_up(&this_adm.wait);
 				break;
 			}
+			if (index == IDX_PSEUDOPORT_01)
+				pseudo_copp[
+				atomic_read(&this_adm.copp_cnt[index])] =
+					open->copp_id;
 			atomic_set(&this_adm.copp_id[index], open->copp_id);
 			atomic_set(&this_adm.copp_stat[index], 1);
 			pr_debug("%s: coppid rxed=%d\n", __func__,
@@ -337,6 +344,79 @@
 	return 0;
 }
 
+int adm_connect_afe_port_v2(int mode, int session_id, int port_id,
+				int sample_rate, int channels)
+{
+	struct adm_cmd_connect_afe_port_v2 cmd;
+	int ret = 0;
+	int index;
+
+	pr_debug("%s: port %d session id:%d\n", __func__,
+				port_id, session_id);
+
+	port_id = afe_convert_virtual_to_portid(port_id);
+
+	if (afe_validate_port(port_id) < 0) {
+		pr_err("%s port idi[%d] is invalid\n", __func__, port_id);
+		return -ENODEV;
+	}
+	if (this_adm.apr == NULL) {
+		this_adm.apr = apr_register("ADSP", "ADM", adm_callback,
+						0xFFFFFFFF, &this_adm);
+		if (this_adm.apr == NULL) {
+			pr_err("%s: Unable to register ADM\n", __func__);
+			ret = -ENODEV;
+			return ret;
+		}
+		rtac_set_adm_handle(this_adm.apr);
+	}
+	index = afe_get_port_index(port_id);
+	pr_debug("%s: Port ID %d, index %d\n", __func__, port_id, index);
+
+	cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+			APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+	cmd.hdr.pkt_size = sizeof(cmd);
+	cmd.hdr.src_svc = APR_SVC_ADM;
+	cmd.hdr.src_domain = APR_DOMAIN_APPS;
+	cmd.hdr.src_port = port_id;
+	cmd.hdr.dest_svc = APR_SVC_ADM;
+	cmd.hdr.dest_domain = APR_DOMAIN_ADSP;
+	cmd.hdr.dest_port = port_id;
+	cmd.hdr.token = port_id;
+	cmd.hdr.opcode = ADM_CMD_CONNECT_AFE_PORT_V2;
+
+	cmd.mode = mode;
+	cmd.session_id = session_id;
+	cmd.afe_port_id = port_id;
+	cmd.num_channels = channels;
+	cmd.sampling_rate = sample_rate;
+
+	atomic_set(&this_adm.copp_stat[index], 0);
+	ret = apr_send_pkt(this_adm.apr, (uint32_t *)&cmd);
+	if (ret < 0) {
+		pr_err("%s:ADM enable for port %d failed\n",
+					__func__, port_id);
+		ret = -EINVAL;
+		goto fail_cmd;
+	}
+	ret = wait_event_timeout(this_adm.wait,
+		atomic_read(&this_adm.copp_stat[index]),
+		msecs_to_jiffies(TIMEOUT_MS));
+	if (!ret) {
+		pr_err("%s ADM connect AFE failed for port %d\n", __func__,
+							port_id);
+		ret = -EINVAL;
+		goto fail_cmd;
+	}
+	atomic_inc(&this_adm.copp_cnt[index]);
+	return 0;
+
+fail_cmd:
+
+	return ret;
+
+}
+
 static int send_adm_cal_block(int port_id, struct acdb_cal_block *aud_cal)
 {
 	s32				result = 0;
@@ -718,6 +798,138 @@
 	return ret;
 }
 
+int adm_multi_ch_copp_pseudo_open_v3(int port_id, int path,
+				int rate, int channel_mode,
+				int topology)
+{
+	struct adm_multi_channel_copp_open_v3	open;
+	int ret = 0;
+	int index;
+
+	pr_debug("%s: port %d path:%d rate:%d mode:%d\n", __func__,
+				port_id, path, rate, channel_mode);
+
+	port_id = afe_convert_virtual_to_portid(port_id);
+
+	if (afe_validate_port(port_id) < 0) {
+		pr_err("%s port idi[%d] is invalid\n", __func__, port_id);
+		return -ENODEV;
+	}
+
+	index = afe_get_port_index(port_id);
+	pr_debug("%s: Port ID %d, index %d\n", __func__, port_id, index);
+
+	if (this_adm.apr == NULL) {
+		this_adm.apr = apr_register("ADSP", "ADM", adm_callback,
+						0xFFFFFFFF, &this_adm);
+		if (this_adm.apr == NULL) {
+			pr_err("%s: Unable to register ADM\n", __func__);
+			ret = -ENODEV;
+			return ret;
+		}
+		rtac_set_adm_handle(this_adm.apr);
+	}
+
+
+	{
+		open.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+				APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+		open.hdr.pkt_size = sizeof(open);
+		open.hdr.src_svc = APR_SVC_ADM;
+		open.hdr.src_domain = APR_DOMAIN_APPS;
+		open.hdr.src_port = port_id;
+		open.hdr.dest_svc = APR_SVC_ADM;
+		open.hdr.dest_domain = APR_DOMAIN_ADSP;
+		open.hdr.dest_port = port_id;
+		open.hdr.token = port_id;
+		open.hdr.opcode = ADM_CMD_MULTI_CHANNEL_COPP_OPEN_V3;
+		memset(open.dev_channel_mapping, 0, 8);
+
+		if (channel_mode == 1)	{
+			open.dev_channel_mapping[0] = PCM_CHANNEL_FC;
+		} else if (channel_mode == 2) {
+			open.dev_channel_mapping[0] = PCM_CHANNEL_FL;
+			open.dev_channel_mapping[1] = PCM_CHANNEL_FR;
+		} else if (channel_mode == 4) {
+			open.dev_channel_mapping[0] = PCM_CHANNEL_FL;
+			open.dev_channel_mapping[1] = PCM_CHANNEL_FR;
+			open.dev_channel_mapping[2] = PCM_CHANNEL_RB;
+			open.dev_channel_mapping[3] = PCM_CHANNEL_LB;
+		} else if (channel_mode == 6) {
+			open.dev_channel_mapping[0] = PCM_CHANNEL_FL;
+			open.dev_channel_mapping[1] = PCM_CHANNEL_FR;
+			open.dev_channel_mapping[2] = PCM_CHANNEL_LFE;
+			open.dev_channel_mapping[3] = PCM_CHANNEL_FC;
+			open.dev_channel_mapping[4] = PCM_CHANNEL_LB;
+			open.dev_channel_mapping[5] = PCM_CHANNEL_RB;
+		} else if (channel_mode == 8) {
+			open.dev_channel_mapping[0] = PCM_CHANNEL_FL;
+			open.dev_channel_mapping[1] = PCM_CHANNEL_FR;
+			open.dev_channel_mapping[2] = PCM_CHANNEL_LFE;
+			open.dev_channel_mapping[3] = PCM_CHANNEL_FC;
+			open.dev_channel_mapping[4] = PCM_CHANNEL_LB;
+			open.dev_channel_mapping[5] = PCM_CHANNEL_RB;
+			open.dev_channel_mapping[6] = PCM_CHANNEL_FLC;
+			open.dev_channel_mapping[7] = PCM_CHANNEL_FRC;
+		} else {
+			pr_err("%s invalid num_chan %d\n", __func__,
+					channel_mode);
+			return -EINVAL;
+		}
+
+		open.mode = path;
+		open.endpoint_id1 = port_id;
+		open.endpoint_id2 = 0xFFFF;
+		open.bit_width = 16;
+
+		if (path == ADM_PATH_PLAYBACK)
+			open.topology_id = get_adm_rx_topology();
+		else {
+			open.topology_id = get_adm_tx_topology();
+			if ((open.topology_id ==
+				VPM_TX_SM_ECNS_COPP_TOPOLOGY) ||
+				(open.topology_id ==
+				VPM_TX_DM_FLUENCE_COPP_TOPOLOGY))
+				rate = 16000;
+		}
+
+		if (open.topology_id  == 0)
+			open.topology_id = topology;
+
+		open.channel_config = channel_mode & 0x00FF;
+		open.rate  = rate;
+		open.flags = 0;
+
+		pr_debug("%s: channel_config=%d port_id=%d rate=%d" \
+			"topology_id=0x%X\n", __func__, open.channel_config,\
+			open.endpoint_id1, open.rate,\
+			open.topology_id);
+
+		atomic_set(&this_adm.copp_stat[index], 0);
+		ret = apr_send_pkt(this_adm.apr, (uint32_t *)&open);
+		if (ret < 0) {
+			pr_err("%s:ADM enable for port %d failed\n",
+						__func__, port_id);
+			ret = -EINVAL;
+			goto fail_cmd;
+		}
+		ret = wait_event_timeout(this_adm.wait,
+			atomic_read(&this_adm.copp_stat[index]),
+			msecs_to_jiffies(TIMEOUT_MS));
+		if (!ret) {
+			pr_err("%s ADM open failed for port %d\n", __func__,
+								port_id);
+			ret = -EINVAL;
+			goto fail_cmd;
+		}
+	}
+	atomic_inc(&this_adm.copp_cnt[index]);
+	return 0;
+
+fail_cmd:
+	return ret;
+
+}
 
 int adm_multi_ch_copp_open(int port_id, int path, int rate, int channel_mode,
 				int topology, int perfmode)
@@ -1127,6 +1339,56 @@
 	pr_debug("%s ec_ref_rx:%d", __func__, this_adm.ec_ref_rx);
 }
 
+int adm_pseudo_close(int port_id)
+{
+	struct apr_hdr close;
+
+	int ret = 0, i = 0;
+	int index = 0;
+	int pseudo_copp_cnt;
+	index = afe_get_port_index(port_id);
+	if (afe_validate_port(port_id) < 0)
+		return -EINVAL;
+
+	pseudo_copp_cnt = atomic_read(&this_adm.copp_cnt[index]);
+	pr_debug("%s port_id=%d index %d copp_cnt %d\n", __func__, port_id,
+				index, pseudo_copp_cnt);
+
+	for (i = 0; i < pseudo_copp_cnt; i++) {
+		close.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+				APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+		close.pkt_size = sizeof(close);
+		close.src_svc = APR_SVC_ADM;
+		close.src_domain = APR_DOMAIN_APPS;
+		close.src_port = port_id;
+		close.dest_svc = APR_SVC_ADM;
+		close.dest_domain = APR_DOMAIN_ADSP;
+		close.dest_port = pseudo_copp[i];
+		close.token = port_id;
+		close.opcode = ADM_CMD_COPP_CLOSE;
+
+		atomic_set(&this_adm.copp_id[index], RESET_COPP_ID);
+		atomic_set(&this_adm.copp_stat[index], 0);
+
+
+		pr_debug("%s:coppid %d portid=%d index=%d coppcnt=%d\n",
+				__func__,
+		atomic_read(&this_adm.copp_id[index]),
+			port_id, index,
+			atomic_read(&this_adm.copp_cnt[index]));
+
+		ret = apr_send_pkt(this_adm.apr, (uint32_t *)&close);
+
+		ret = wait_event_timeout(this_adm.wait,
+				atomic_read(&this_adm.copp_stat[index]),
+		msecs_to_jiffies(TIMEOUT_MS));
+	}
+
+	atomic_set(&this_adm.copp_cnt[index], 0);
+	return ret;
+
+}
+
 int adm_close(int port_id)
 {
 	struct apr_hdr close;
diff --git a/sound/soc/msm/qdsp6/q6afe.c b/sound/soc/msm/qdsp6/q6afe.c
index a4f4b60..9c62a2e 100644
--- a/sound/soc/msm/qdsp6/q6afe.c
+++ b/sound/soc/msm/qdsp6/q6afe.c
@@ -156,6 +156,7 @@
 	case VOICE_PLAYBACK_TX:
 	case RT_PROXY_PORT_001_RX:
 	case SLIMBUS_4_RX:
+	case PSEUDOPORT_01:
 		ret = MSM_AFE_PORT_TYPE_RX;
 		break;
 
@@ -225,6 +226,7 @@
 	case RT_PROXY_PORT_001_TX:
 	case SLIMBUS_4_RX:
 	case SLIMBUS_4_TX:
+	case PSEUDOPORT_01:
 	{
 		ret = 0;
 		break;
@@ -295,6 +297,7 @@
 	case RT_PROXY_PORT_001_TX: return IDX_RT_PROXY_PORT_001_TX;
 	case SLIMBUS_4_RX: return IDX_SLIMBUS_4_RX;
 	case SLIMBUS_4_TX: return IDX_SLIMBUS_4_TX;
+	case PSEUDOPORT_01: return IDX_PSEUDOPORT_01;
 
 	default: return -EINVAL;
 	}
@@ -331,6 +334,9 @@
 	case RT_PROXY_PORT_001_TX:
 		ret_size = SIZEOF_CFG_CMD(afe_port_rtproxy_cfg);
 		break;
+	case PSEUDOPORT_01:
+		ret_size = SIZEOF_CFG_CMD(afe_port_pseudo_cfg);
+		break;
 	case PCM_RX:
 	case PCM_TX:
 	case SECONDARY_PCM_RX:
@@ -506,6 +512,11 @@
 			else
 				config.hdr.opcode = AFE_PORT_CMD_I2S_CONFIG;
 		break;
+		case PSEUDOPORT_01:
+			config.hdr.opcode = AFE_PORT_AUDIO_IF_CONFIG;
+			pr_debug("%s, config, opcode=%x\n", __func__,
+					config.hdr.opcode);
+		break;
 		default:
 			config.hdr.opcode = AFE_PORT_AUDIO_IF_CONFIG;
 		break;
diff --git a/sound/soc/msm/qdsp6/q6asm.c b/sound/soc/msm/qdsp6/q6asm.c
index 6865871..7b52956 100644
--- a/sound/soc/msm/qdsp6/q6asm.c
+++ b/sound/soc/msm/qdsp6/q6asm.c
@@ -35,7 +35,6 @@
 
 #include <mach/memory.h>
 #include <mach/debug_mm.h>
-#include <mach/peripheral-loader.h>
 #include <mach/qdsp6v2/audio_acdb.h>
 #include <mach/qdsp6v2/rtac.h>
 
@@ -894,12 +893,14 @@
 		case ASM_STREAM_CMD_SET_ENCDEC_PARAM:
 		case ASM_STREAM_CMD_OPEN_WRITE_COMPRESSED:
 		case ASM_STREAM_CMD_OPEN_READ_COMPRESSED:
+		case ASM_STREAM_CMD_OPEN_TRANSCODE_LOOPBACK:
 			if (atomic_read(&ac->cmd_state) && wakeup_flag) {
 				atomic_set(&ac->cmd_state, 0);
+				pr_debug("response payload[1]:%d",
+							payload[1]);
 				if (payload[1] == ADSP_EUNSUPPORTED ||
+					payload[1] == ADSP_EBADPARAM ||
 					payload[1] == ADSP_EFAILED) {
-					pr_debug("payload[1]:%d unsupported",
-								payload[1]);
 					atomic_set(&ac->cmd_response, 1);
 				}
 				else
@@ -1065,6 +1066,114 @@
 	return 0;
 }
 
+int q6asm_open_transcode_loopback(struct audio_client *ac, uint32_t channels)
+{
+	int rc = 0x00;
+	struct asm_stream_cmd_open_transcode_loopback open;
+
+	if ((ac == NULL) || (ac->apr == NULL)) {
+		pr_err("%s: APR handle NULL\n", __func__);
+		return -EINVAL;
+	}
+	pr_debug("%s: session[%d] channels = %d", __func__, ac->session,
+		channels);
+
+	q6asm_add_hdr(ac, &open.hdr, sizeof(open), TRUE);
+
+	open.hdr.opcode = ASM_STREAM_CMD_OPEN_TRANSCODE_LOOPBACK;
+
+	open.mode_flags = 0;
+
+	if (channels > 2)
+		open.src_format_id = MULTI_CHANNEL_PCM;
+	else
+		open.src_format_id = LINEAR_PCM;
+
+
+	open.sink_format_id = DTS;
+	open.audproc_topo_id = DEFAULT_POPP_TOPOLOGY;
+	open.src_endpoint_type = 0;
+	open.sink_endpoint_type = 0;
+	open.bits_per_sample = 16;
+	open.reserved = 0;
+
+	rc = apr_send_pkt(ac->apr, (uint32_t *) &open);
+	if (rc < 0) {
+		pr_err("%s: open failed op[0x%x]rc[%d]\n", \
+					__func__, open.hdr.opcode, rc);
+		goto fail_cmd;
+	}
+	rc = wait_event_timeout(ac->cmd_wait,
+			(atomic_read(&ac->cmd_state) == 0), 5*HZ);
+	if (!rc) {
+		pr_err("%s: timeout. waited for OPEN_WRITE rc[%d]\n", __func__,
+			rc);
+		goto fail_cmd;
+	}
+	return 0;
+fail_cmd:
+	return -EINVAL;
+}
+
+int q6asm_enc_cfg_blk_dts(struct audio_client *ac,
+			uint32_t sample_rate,
+			uint32_t channels)
+{
+	struct asm_stream_cmd_encdec_cfg_blk enc_cfg;
+	int rc = 0;
+
+	pr_debug("%s: sample_rate=%d,channels=%d\n", __func__,
+				sample_rate, channels);
+
+	q6asm_add_hdr(ac, &enc_cfg.hdr, sizeof(enc_cfg), TRUE);
+
+	enc_cfg.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM;
+	enc_cfg.param_id = ASM_ENCDEC_CFG_BLK_ID;
+	enc_cfg.param_size = sizeof(struct asm_encode_cfg_blk);
+	enc_cfg.enc_blk.frames_per_buf = 0;
+	enc_cfg.enc_blk.format_id = DTS;
+	enc_cfg.enc_blk.cfg_size  = sizeof(struct asm_dts_enc_cfg);
+	enc_cfg.enc_blk.cfg.dts.sample_rate = sample_rate;
+	enc_cfg.enc_blk.cfg.dts.num_channels = channels;
+	if (channels == 2) {
+		enc_cfg.enc_blk.cfg.dts.channel_mapping[0] = PCM_CHANNEL_FL;
+		enc_cfg.enc_blk.cfg.dts.channel_mapping[1] = PCM_CHANNEL_FR;
+		enc_cfg.enc_blk.cfg.dts.channel_mapping[2] = 0;
+		enc_cfg.enc_blk.cfg.dts.channel_mapping[3] = 0;
+		enc_cfg.enc_blk.cfg.dts.channel_mapping[4] = 0;
+		enc_cfg.enc_blk.cfg.dts.channel_mapping[5] = 0;
+	} else if (channels == 4) {
+		enc_cfg.enc_blk.cfg.dts.channel_mapping[0] = PCM_CHANNEL_FL;
+		enc_cfg.enc_blk.cfg.dts.channel_mapping[1] = PCM_CHANNEL_FR;
+		enc_cfg.enc_blk.cfg.dts.channel_mapping[2] = PCM_CHANNEL_LS;
+		enc_cfg.enc_blk.cfg.dts.channel_mapping[3] = PCM_CHANNEL_RS;
+		enc_cfg.enc_blk.cfg.dts.channel_mapping[4] = 0;
+		enc_cfg.enc_blk.cfg.dts.channel_mapping[5] = 0;
+	} else if (channels == 6) {
+		enc_cfg.enc_blk.cfg.dts.channel_mapping[0] = PCM_CHANNEL_FL;
+		enc_cfg.enc_blk.cfg.dts.channel_mapping[1] = PCM_CHANNEL_FR;
+		enc_cfg.enc_blk.cfg.dts.channel_mapping[2] = PCM_CHANNEL_LFE;
+		enc_cfg.enc_blk.cfg.dts.channel_mapping[3] = PCM_CHANNEL_LS;
+		enc_cfg.enc_blk.cfg.dts.channel_mapping[4] = PCM_CHANNEL_RS;
+		enc_cfg.enc_blk.cfg.dts.channel_mapping[5] = PCM_CHANNEL_FC;
+	}
+	rc = apr_send_pkt(ac->apr, (uint32_t *) &enc_cfg);
+	if (rc < 0) {
+		pr_err("Comamnd %d failed\n", ASM_STREAM_CMD_SET_ENCDEC_PARAM);
+		rc = -EINVAL;
+		goto fail_cmd;
+	}
+	rc = wait_event_timeout(ac->cmd_wait,
+			(atomic_read(&ac->cmd_state) == 0), 5*HZ);
+	if (!rc) {
+		pr_err("timeout. waited for FORMAT_UPDATE\n");
+		goto fail_cmd;
+	}
+	return 0;
+fail_cmd:
+	return -EINVAL;
+}
+
 void *q6asm_is_cpu_buf_avail(int dir, struct audio_client *ac, uint32_t *size,
 				uint32_t *index)
 {
@@ -1462,6 +1571,9 @@
 	case FORMAT_MAT:
 		open.format = MAT;
 		break;
+	case FORMAT_MP2:
+		open.format = MP2;
+		break;
 	default:
 		pr_err("%s: Invalid format[%d]\n", __func__, format);
 		goto fail_cmd;
@@ -1482,6 +1594,10 @@
 			rc);
 		goto fail_cmd;
 	}
+	if (atomic_read(&ac->cmd_response)) {
+		pr_err("%s: format = %x not supported\n", __func__, format);
+		goto fail_cmd;
+	}
 	return 0;
 fail_cmd:
 	return -EINVAL;
@@ -1555,6 +1671,9 @@
 		open.format = AMR_WB_PLUS;
 		pr_debug("q6asm_open_write FORMAT_AMR_WB_PLUS");
 		break;
+	case FORMAT_MP2:
+		open.format = MP2;
+		break;
 	default:
 		pr_err("%s: Invalid format[%d]\n", __func__, format);
 		goto fail_cmd;
@@ -1642,6 +1761,9 @@
 	case FORMAT_MP3:
 		open.write_format = MP3;
 		break;
+	case FORMAT_MP2:
+		open.write_format = MP2;
+		break;
 	default:
 		pr_err("Invalid format[%d]\n", wr_format);
 		goto fail_cmd;
@@ -2317,7 +2439,7 @@
 }
 
 int q6asm_media_format_block_multi_ch_pcm(struct audio_client *ac,
-				uint32_t rate, uint32_t channels)
+			uint32_t rate, uint32_t channels, char *channel_map)
 {
 	struct asm_stream_media_format_update fmt;
 	u8 *channel_mapping;
@@ -2340,39 +2462,7 @@
 	channel_mapping =
 		fmt.write_cfg.multi_ch_pcm_cfg.channel_mapping;
 
-	memset(channel_mapping, 0, PCM_FORMAT_MAX_NUM_CHANNEL);
-
-	if (channels == 1)  {
-		channel_mapping[0] = PCM_CHANNEL_FL;
-	} else if (channels == 2) {
-		channel_mapping[0] = PCM_CHANNEL_FL;
-		channel_mapping[1] = PCM_CHANNEL_FR;
-	} else if (channels == 4) {
-		channel_mapping[0] = PCM_CHANNEL_FL;
-		channel_mapping[1] = PCM_CHANNEL_FR;
-		channel_mapping[1] = PCM_CHANNEL_LB;
-		channel_mapping[1] = PCM_CHANNEL_RB;
-	} else if (channels == 6) {
-		channel_mapping[0] = PCM_CHANNEL_FC;
-		channel_mapping[1] = PCM_CHANNEL_FL;
-		channel_mapping[2] = PCM_CHANNEL_FR;
-		channel_mapping[3] = PCM_CHANNEL_LB;
-		channel_mapping[4] = PCM_CHANNEL_RB;
-		channel_mapping[5] = PCM_CHANNEL_LFE;
-	} else if (channels == 8) {
-		channel_mapping[0] = PCM_CHANNEL_FC;
-		channel_mapping[1] = PCM_CHANNEL_FL;
-		channel_mapping[2] = PCM_CHANNEL_FR;
-		channel_mapping[3] = PCM_CHANNEL_LB;
-		channel_mapping[4] = PCM_CHANNEL_RB;
-		channel_mapping[5] = PCM_CHANNEL_LFE;
-		channel_mapping[6] = PCM_CHANNEL_FLC;
-		channel_mapping[7] = PCM_CHANNEL_FRC;
-	} else {
-		pr_err("%s: ERROR.unsupported num_ch = %u\n", __func__,
-				channels);
-		return -EINVAL;
-	}
+	memcpy(channel_mapping, channel_map, PCM_FORMAT_MAX_NUM_CHANNEL);
 
 	rc = apr_send_pkt(ac->apr, (uint32_t *) &fmt);
 	if (rc < 0) {
@@ -2574,6 +2664,9 @@
 	case FORMAT_DTS_LBR:
 		fmt.format = DTS_LBR;
 		break;
+	case FORMAT_MP2:
+		fmt.format = MP2;
+		break;
 	default:
 		pr_err("Invalid format[%d]\n", format);
 		goto fail_cmd;
diff --git a/sound/soc/msm/qdsp6v2/Makefile b/sound/soc/msm/qdsp6v2/Makefile
index acb073d..1d11907 100644
--- a/sound/soc/msm/qdsp6v2/Makefile
+++ b/sound/soc/msm/qdsp6v2/Makefile
@@ -1,5 +1,5 @@
 snd-soc-qdsp6v2-objs += msm-dai-q6-v2.o msm-pcm-q6-v2.o msm-pcm-routing-v2.o msm-compr-q6-v2.o  msm-multi-ch-pcm-q6-v2.o
-snd-soc-qdsp6v2-objs += msm-pcm-lpa-v2.o msm-pcm-afe-v2.o msm-pcm-voip-v2.o msm-pcm-voice-v2.o
+snd-soc-qdsp6v2-objs += msm-pcm-lpa-v2.o msm-pcm-afe-v2.o msm-pcm-voip-v2.o msm-pcm-voice-v2.o msm-dai-q6-hdmi-v2.o
 obj-$(CONFIG_SND_SOC_QDSP6V2) += snd-soc-qdsp6v2.o
 obj-y += q6adm.o q6afe.o q6asm.o q6audio-v2.o q6voice.o q6core.o
 ocmem-audio-objs += audio_ocmem.o
diff --git a/sound/soc/msm/qdsp6v2/audio_ocmem.c b/sound/soc/msm/qdsp6v2/audio_ocmem.c
index 86a82e2..d38bcbb 100644
--- a/sound/soc/msm/qdsp6v2/audio_ocmem.c
+++ b/sound/soc/msm/qdsp6v2/audio_ocmem.c
@@ -31,6 +31,7 @@
 #define AUDIO_OCMEM_BUF_SIZE (512 * SZ_1K)
 
 enum {
+	OCMEM_STATE_DEFAULT = 0,
 	OCMEM_STATE_ALLOC = 1,
 	OCMEM_STATE_MAP_TRANSITION,
 	OCMEM_STATE_MAP_COMPL,
@@ -80,6 +81,8 @@
 {
 	int rc = NOTIFY_DONE;
 	unsigned long flags;
+	struct ocmem_buf *rbuf;
+	int vwait = 0;
 
 	pr_debug("%s: event[%ld] cur state[%x]\n", __func__,
 			event1, atomic_read(&audio_ocmem_lcl.audio_state));
@@ -105,10 +108,24 @@
 				OCMEM_STATE_UNMAP_FAIL);
 		break;
 	case OCMEM_ALLOC_GROW:
-		audio_ocmem_lcl.buf = data;
-		atomic_set(&audio_ocmem_lcl.audio_state, OCMEM_STATE_GROW);
+		rbuf = data;
+		if (rbuf->len == AUDIO_OCMEM_BUF_SIZE) {
+			audio_ocmem_lcl.buf = data;
+			pr_debug("%s: Alloc grow request received buf->addr: 0x%08lx\n",
+						__func__,
+						(audio_ocmem_lcl.buf)->addr);
+			atomic_set(&audio_ocmem_lcl.audio_state,
+							OCMEM_STATE_GROW);
+		} else {
+			pr_debug("%s: Alloc grow request with size: %ld",
+							__func__,
+							rbuf->len);
+			vwait = 1;
+		}
+
 		break;
 	case OCMEM_ALLOC_SHRINK:
+		pr_debug("%s: Alloc shrink request received\n", __func__);
 		atomic_set(&audio_ocmem_lcl.audio_state, OCMEM_STATE_SHRINK);
 		break;
 	default:
@@ -116,7 +133,7 @@
 		break;
 	}
 	spin_unlock_irqrestore(&audio_ocmem_lcl.audio_lock, flags);
-	if (atomic_read(&audio_ocmem_lcl.audio_cond)) {
+	if (!vwait && (atomic_read(&audio_ocmem_lcl.audio_cond))) {
 		atomic_set(&audio_ocmem_lcl.audio_cond, 0);
 		wake_up(&audio_ocmem_lcl.audio_wait);
 	}
@@ -150,11 +167,14 @@
 
 	audio_ocmem_lcl.buf = buf;
 	atomic_set(&audio_ocmem_lcl.audio_exit, 0);
+	atomic_set(&audio_ocmem_lcl.audio_cond, 1);
+	pr_debug("%s: buf->len: %ld\n", __func__, buf->len);
 	if (!buf->len) {
+		pr_debug("%s: buf.len is 0, waiting for ocmem region\n",
+								__func__);
 		wait_event_interruptible(audio_ocmem_lcl.audio_wait,
 			(atomic_read(&audio_ocmem_lcl.audio_cond) == 0)	||
 			(atomic_read(&audio_ocmem_lcl.audio_exit) == 1));
-
 		if (atomic_read(&audio_ocmem_lcl.audio_exit)) {
 			pr_err("%s: audio playback ended while waiting for ocmem\n",
 					__func__);
@@ -162,6 +182,7 @@
 			goto fail_cmd;
 		}
 	}
+	pr_debug("%s: buf->len: %ld\n", __func__, (audio_ocmem_lcl.buf)->len);
 	if (audio_ocmem_lcl.lp_memseg_ptr == NULL) {
 		/* Retrieve low power segments */
 		ret = core_get_low_power_segments(
@@ -181,7 +202,7 @@
 			lp_segptr->mem_segment[i].start_address_lsw;
 		audio_ocmem_lcl.mlist.chunks[j].size =
 			lp_segptr->mem_segment[i].size;
-		pr_debug("%s: ro:%d, ddr_paddr[%x], size[%x]\n", __func__,
+		pr_debug("%s: ro:%d, ddr_paddr[0x%08x], size[0x%x]\n", __func__,
 			audio_ocmem_lcl.mlist.chunks[j].ro,
 			(uint32_t)audio_ocmem_lcl.mlist.chunks[j].ddr_paddr,
 			(uint32_t)audio_ocmem_lcl.mlist.chunks[j].size);
@@ -190,19 +211,28 @@
 	/* vote for ocmem bus bandwidth */
 	ret = msm_bus_scale_client_update_request(
 				audio_ocmem_lcl.audio_ocmem_bus_client,
-				0);
+				1);
 	if (ret)
 		pr_err("%s: failed to vote for bus bandwidth\n", __func__);
 
 	atomic_set(&audio_ocmem_lcl.audio_state, OCMEM_STATE_MAP_TRANSITION);
 
+	pr_debug("%s: buf->addr: 0x%08lx, len: %ld, audio_state[0x%x]\n",
+				__func__,
+				audio_ocmem_lcl.buf->addr,
+				audio_ocmem_lcl.buf->len,
+				atomic_read(&audio_ocmem_lcl.audio_state));
+
+	atomic_set(&audio_ocmem_lcl.audio_cond, 1);
 	ret = ocmem_map(cid, audio_ocmem_lcl.buf, &audio_ocmem_lcl.mlist);
 	if (ret) {
 		pr_err("%s: ocmem_map failed\n", __func__);
-		goto fail_cmd;
+		atomic_set(&audio_ocmem_lcl.audio_state, OCMEM_STATE_MAP_FAIL);
 	}
 
-
+	pr_debug("%s: audio_cond[%d] audio_state[0x%x]\n", __func__,
+				atomic_read(&audio_ocmem_lcl.audio_cond),
+				atomic_read(&audio_ocmem_lcl.audio_state));
 	while ((atomic_read(&audio_ocmem_lcl.audio_state) !=
 						OCMEM_STATE_EXIT)) {
 
@@ -219,6 +249,8 @@
 			atomic_set(&audio_ocmem_lcl.audio_cond, 1);
 			break;
 		case OCMEM_STATE_SHRINK:
+			pr_debug("%s: ocmem shrink request process\n",
+							__func__);
 			atomic_set(&audio_ocmem_lcl.audio_cond, 1);
 			ret = ocmem_unmap(cid, audio_ocmem_lcl.buf,
 					&audio_ocmem_lcl.mlist);
@@ -242,9 +274,11 @@
 				atomic_read(&audio_ocmem_lcl.audio_state));
 				goto fail_cmd;
 			}
-
+			atomic_set(&audio_ocmem_lcl.audio_cond, 1);
 			break;
 		case OCMEM_STATE_GROW:
+			pr_debug("%s: ocmem grow request process\n",
+							__func__);
 			atomic_set(&audio_ocmem_lcl.audio_cond, 1);
 			ret = ocmem_map(cid, audio_ocmem_lcl.buf,
 						&audio_ocmem_lcl.mlist);
@@ -260,9 +294,11 @@
 				atomic_read(&audio_ocmem_lcl.audio_cond) == 0);
 			atomic_set(&audio_ocmem_lcl.audio_state,
 				OCMEM_STATE_MAP_COMPL);
+			atomic_set(&audio_ocmem_lcl.audio_cond, 1);
 			break;
 		}
 	}
+	ret = 0;
 fail_cmd:
 	pr_debug("%s: exit\n", __func__);
 	return ret;
@@ -279,13 +315,19 @@
 int audio_ocmem_disable(int cid)
 {
 	int ret;
+	int cur_state;
 
-	if (atomic_read(&audio_ocmem_lcl.audio_cond))
-		atomic_set(&audio_ocmem_lcl.audio_cond, 0);
+	pr_debug("%s: disable\n", __func__);
+	cur_state = atomic_read(&audio_ocmem_lcl.audio_state);
+	if (atomic_cmpxchg(&audio_ocmem_lcl.audio_cond, 1, 0)) {
+		atomic_set(&audio_ocmem_lcl.audio_state, OCMEM_STATE_EXIT);
+		wake_up(&audio_ocmem_lcl.audio_wait);
+	}
+
 	pr_debug("%s: audio_cond[0x%x], audio_state[0x%x]\n", __func__,
 			 atomic_read(&audio_ocmem_lcl.audio_cond),
 			 atomic_read(&audio_ocmem_lcl.audio_state));
-	switch (atomic_read(&audio_ocmem_lcl.audio_state)) {
+	switch (cur_state) {
 	case OCMEM_STATE_MAP_COMPL:
 		atomic_set(&audio_ocmem_lcl.audio_cond, 1);
 		ret = ocmem_unmap(cid, audio_ocmem_lcl.buf,
@@ -302,6 +344,9 @@
 		wait_event_interruptible(audio_ocmem_lcl.audio_wait,
 				atomic_read(&audio_ocmem_lcl.audio_cond) == 0);
 	case OCMEM_STATE_UNMAP_COMPL:
+	case OCMEM_STATE_MAP_FAIL:
+	case OCMEM_STATE_MAP_TRANSITION:
+	case OCMEM_STATE_ALLOC:
 		ret = ocmem_free(OCMEM_LP_AUDIO, audio_ocmem_lcl.buf);
 		if (ret) {
 			pr_err("%s: ocmem_free failed, state[%d]\n",
@@ -309,13 +354,21 @@
 				atomic_read(&audio_ocmem_lcl.audio_state));
 			goto fail_cmd;
 		}
-		pr_debug("%s: ocmem_free success\n", __func__);
-	default:
 		pr_debug("%s: state=%d", __func__,
 			atomic_read(&audio_ocmem_lcl.audio_state));
+		atomic_set(&audio_ocmem_lcl.audio_state, OCMEM_STATE_EXIT);
+		pr_debug("%s: ocmem_free success\n", __func__);
+		break;
+
+	default:
+		pr_debug("%s:error: state=%d", __func__,
+			atomic_read(&audio_ocmem_lcl.audio_state));
 		break;
 
 	}
+	msm_bus_scale_client_update_request(
+				audio_ocmem_lcl.audio_ocmem_bus_client,
+				0);
 	return 0;
 fail_cmd:
 	return ret;
@@ -345,6 +398,7 @@
 		rc = -EINVAL;
 	}
 
+	return;
 }
 /**
  * voice_ocmem_process_req() - disable/enable OCMEM during voice call
@@ -441,6 +495,7 @@
 		rc = -EINVAL;
 	}
 
+	return;
 }
 
 /**
@@ -484,86 +539,21 @@
 
 static int audio_ocmem_platform_data_populate(struct platform_device *pdev)
 {
-	int ret;
-	struct msm_bus_scale_pdata *audio_ocmem_bus_scale_pdata = NULL;
-	struct msm_bus_vectors *audio_ocmem_bus_vectors = NULL;
-	struct msm_bus_paths *ocmem_audio_bus_paths = NULL;
-	u32 val;
+	struct msm_bus_scale_pdata *audio_ocmem_adata = NULL;
 
 	if (!pdev->dev.of_node) {
 		pr_err("%s: device tree information missing\n", __func__);
 		return -ENODEV;
 	}
-
-	audio_ocmem_bus_vectors = kzalloc(sizeof(struct msm_bus_vectors),
-								GFP_KERNEL);
-	if (!audio_ocmem_bus_vectors) {
-		dev_err(&pdev->dev, "Failed to allocate memory for platform data\n");
-		return -ENOMEM;
+	audio_ocmem_adata = msm_bus_cl_get_pdata(pdev);
+	if (!audio_ocmem_adata) {
+		pr_err("%s: bus device tree allocation failed\n", __func__);
+		return -EINVAL;
 	}
 
-	ret = of_property_read_u32(pdev->dev.of_node,
-				"qcom,msm-ocmem-audio-src-id", &val);
-	if (ret) {
-		dev_err(&pdev->dev, "%s: qcom,msm-ocmem-audio-src-id missing in DT node\n",
-				__func__);
-		goto fail1;
-	}
-	audio_ocmem_bus_vectors->src = val;
-	ret = of_property_read_u32(pdev->dev.of_node,
-				"qcom,msm-ocmem-audio-dst-id", &val);
-	if (ret) {
-		dev_err(&pdev->dev, "%s: qcom,msm-ocmem-audio-dst-id missing in DT node\n",
-				__func__);
-		goto fail1;
-	}
-	audio_ocmem_bus_vectors->dst = val;
-	ret = of_property_read_u32(pdev->dev.of_node,
-				"qcom,msm-ocmem-audio-ab", &val);
-	if (ret) {
-		dev_err(&pdev->dev, "%s: qcom,msm-ocmem-audio-ab missing in DT node\n",
-					__func__);
-		goto fail1;
-	}
-	audio_ocmem_bus_vectors->ab = val;
-	ret = of_property_read_u32(pdev->dev.of_node,
-				"qcom,msm-ocmem-audio-ib", &val);
-	if (ret) {
-		dev_err(&pdev->dev, "%s: qcom,msm-ocmem-audio-ib missing in DT node\n",
-					__func__);
-		goto fail1;
-	}
-	audio_ocmem_bus_vectors->ib = val;
+	dev_set_drvdata(&pdev->dev, audio_ocmem_adata);
 
-	ocmem_audio_bus_paths = kzalloc(sizeof(struct msm_bus_paths),
-								GFP_KERNEL);
-	if (!ocmem_audio_bus_paths) {
-		dev_err(&pdev->dev, "Failed to allocate memory for platform data\n");
-		goto fail1;
-	}
-	ocmem_audio_bus_paths->num_paths = 1;
-	ocmem_audio_bus_paths->vectors = audio_ocmem_bus_vectors;
-
-	audio_ocmem_bus_scale_pdata =
-		kzalloc(sizeof(struct msm_bus_scale_pdata), GFP_KERNEL);
-
-	if (!audio_ocmem_bus_scale_pdata) {
-		dev_err(&pdev->dev, "Failed to allocate memory for platform data\n");
-		goto fail2;
-	}
-
-	audio_ocmem_bus_scale_pdata->usecase = ocmem_audio_bus_paths;
-	audio_ocmem_bus_scale_pdata->num_usecases = 1;
-	audio_ocmem_bus_scale_pdata->name = "audio-ocmem";
-
-	dev_set_drvdata(&pdev->dev, audio_ocmem_bus_scale_pdata);
-	return ret;
-
-fail2:
-	kfree(ocmem_audio_bus_paths);
-fail1:
-	kfree(audio_ocmem_bus_vectors);
-	return ret;
+	return 0;
 }
 static int ocmem_audio_client_probe(struct platform_device *pdev)
 {
@@ -591,6 +581,7 @@
 
 	init_waitqueue_head(&audio_ocmem_lcl.audio_wait);
 	atomic_set(&audio_ocmem_lcl.audio_cond, 1);
+	atomic_set(&audio_ocmem_lcl.audio_state, OCMEM_STATE_DEFAULT);
 	atomic_set(&audio_ocmem_lcl.audio_exit, 0);
 	spin_lock_init(&audio_ocmem_lcl.audio_lock);
 
@@ -628,9 +619,7 @@
 	audio_ocmem_bus_scale_pdata = (struct msm_bus_scale_pdata *)
 					dev_get_drvdata(&pdev->dev);
 
-	kfree(audio_ocmem_bus_scale_pdata->usecase->vectors);
-	kfree(audio_ocmem_bus_scale_pdata->usecase);
-	kfree(audio_ocmem_bus_scale_pdata);
+	msm_bus_cl_clear_pdata(audio_ocmem_bus_scale_pdata);
 	ocmem_notifier_unregister(audio_ocmem_lcl.audio_hdl,
 					&audio_ocmem_client_nb);
 	return 0;
diff --git a/sound/soc/msm/qdsp6v2/msm-compr-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-compr-q6-v2.c
index bbd43f7..7ba6514 100644
--- a/sound/soc/msm/qdsp6v2/msm-compr-q6-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-compr-q6-v2.c
@@ -34,6 +34,7 @@
 
 #include "msm-compr-q6-v2.h"
 #include "msm-pcm-routing-v2.h"
+#include "audio_ocmem.h"
 
 #define COMPRE_CAPTURE_NUM_PERIODS	16
 /* Allocate the worst case frame size for compressed audio */
@@ -47,6 +48,7 @@
 struct snd_msm {
 	struct msm_audio *prtd;
 	unsigned volume;
+	atomic_t audio_ocmem_req;
 };
 static struct snd_msm compressed_audio = {NULL, 0x2000} ;
 
@@ -147,17 +149,14 @@
 				((unsigned int)buf[0].phys
 				+ (prtd->out_head * prtd->pcm_count)));
 
-		if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) {
+		if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE)
 			time_stamp_flag = SET_TIMESTAMP;
-			memcpy(&output_meta_data, (char *)(buf->data +
+		else
+			time_stamp_flag = NO_TIMESTAMP;
+		memcpy(&output_meta_data, (char *)(buf->data +
 			prtd->out_head * prtd->pcm_count),
 			COMPRE_OUTPUT_METADATA_SIZE);
-		} else {
-			time_stamp_flag = NO_TIMESTAMP;
-			memset(&output_meta_data, 0,
-				 COMPRE_OUTPUT_METADATA_SIZE);
-			output_meta_data.frame_size = prtd->pcm_count;
-		}
+
 		buffer_length = output_meta_data.frame_size;
 		pr_debug("meta_data_length: %d, frame_length: %d\n",
 			 output_meta_data.meta_data_length,
@@ -256,17 +255,13 @@
 				__func__, prtd->out_head,
 				((unsigned int)buf[0].phys
 				+ (prtd->out_head * prtd->pcm_count)));
-			if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) {
+			if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE)
 				time_stamp_flag = SET_TIMESTAMP;
-				memcpy(&output_meta_data, (char *)(buf->data +
+			else
+				time_stamp_flag = NO_TIMESTAMP;
+			memcpy(&output_meta_data, (char *)(buf->data +
 				prtd->out_head * prtd->pcm_count),
 				COMPRE_OUTPUT_METADATA_SIZE);
-			} else {
-				time_stamp_flag = NO_TIMESTAMP;
-				memset(&output_meta_data, 0,
-				 COMPRE_OUTPUT_METADATA_SIZE);
-				output_meta_data.frame_size = prtd->pcm_count;
-			}
 			buffer_length = output_meta_data.frame_size;
 			pr_debug("meta_data_length: %d, frame_length: %d\n",
 				 output_meta_data.meta_data_length,
@@ -440,6 +435,12 @@
 	switch (cmd) {
 	case SNDRV_PCM_TRIGGER_START:
 		prtd->pcm_irq_pos = 0;
+		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+			if (!atomic_cmpxchg(&compressed_audio.audio_ocmem_req,
+									0, 1))
+				audio_ocmem_process_req(AUDIO, true);
+		}
+
 		if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
 			switch (compr->info.codec_param.codec.id) {
 			case SND_AUDIOCODEC_AMRWB:
@@ -565,6 +566,7 @@
 	populate_codec_list(compr, runtime);
 	runtime->private_data = compr;
 	atomic_set(&prtd->eos, 0);
+	atomic_set(&compressed_audio.audio_ocmem_req, 0);
 	compressed_audio.prtd =  &compr->prtd;
 	ret = compressed_set_volume(compressed_audio.volume);
 	if (ret < 0)
@@ -611,6 +613,8 @@
 
 	dir = IN;
 	atomic_set(&prtd->pending_buffer, 0);
+	if (atomic_cmpxchg(&compressed_audio.audio_ocmem_req, 1, 0))
+		audio_ocmem_process_req(AUDIO, false);
 	prtd->pcm_irq_pos = 0;
 	q6asm_cmd(prtd->audio_client, CMD_CLOSE);
 	compressed_audio.prtd = NULL;
@@ -767,7 +771,8 @@
 		}
 	}
 
-	ret = q6asm_set_io_mode(prtd->audio_client, ASYNC_IO_MODE);
+	ret = q6asm_set_io_mode(prtd->audio_client,
+					(COMPRESSED_IO | ASYNC_IO_MODE));
 	if (ret < 0) {
 		pr_err("%s: Set IO mode failed\n", __func__);
 		return -ENOMEM;
diff --git a/sound/soc/msm/qdsp6v2/msm-dai-q6-hdmi-v2.c b/sound/soc/msm/qdsp6v2/msm-dai-q6-hdmi-v2.c
new file mode 100644
index 0000000..f80b5891
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/msm-dai-q6-hdmi-v2.c
@@ -0,0 +1,320 @@
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/bitops.h>
+#include <linux/slab.h>
+#include <linux/of_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/apr_audio-v2.h>
+#include <sound/q6afe-v2.h>
+#include <sound/msm-dai-q6-v2.h>
+#include <mach/msm_hdmi_audio.h>
+
+
+enum {
+	STATUS_PORT_STARTED, /* track if AFE port has started */
+	STATUS_MAX
+};
+
+struct msm_dai_q6_hdmi_dai_data {
+	DECLARE_BITMAP(status_mask, STATUS_MAX);
+	u32 rate;
+	u32 channels;
+	union afe_port_config port_config;
+};
+
+static int msm_dai_q6_hdmi_format_put(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+
+	struct msm_dai_q6_hdmi_dai_data *dai_data = kcontrol->private_data;
+	int value = ucontrol->value.integer.value[0];
+	dai_data->port_config.hdmi_multi_ch.datatype = value;
+	pr_debug("%s: value = %d\n", __func__, value);
+	return 0;
+}
+
+static int msm_dai_q6_hdmi_format_get(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+
+	struct msm_dai_q6_hdmi_dai_data *dai_data = kcontrol->private_data;
+	ucontrol->value.integer.value[0] =
+		dai_data->port_config.hdmi_multi_ch.datatype;
+	return 0;
+}
+
+
+/* HDMI format field for AFE_PORT_MULTI_CHAN_HDMI_AUDIO_IF_CONFIG command
+ *  0: linear PCM
+ *  1: non-linear PCM
+ */
+static const char * const hdmi_format[] = {
+	"LPCM",
+	"Compr"
+};
+
+static const struct soc_enum hdmi_config_enum[] = {
+	SOC_ENUM_SINGLE_EXT(2, hdmi_format),
+};
+
+static const struct snd_kcontrol_new hdmi_config_controls[] = {
+	SOC_ENUM_EXT("HDMI RX Format", hdmi_config_enum[0],
+				 msm_dai_q6_hdmi_format_get,
+				 msm_dai_q6_hdmi_format_put),
+};
+
+/* Current implementation assumes hw_param is called once
+ * This may not be the case but what to do when ADM and AFE
+ * port are already opened and parameter changes
+ */
+static int msm_dai_q6_hdmi_hw_params(struct snd_pcm_substream *substream,
+				struct snd_pcm_hw_params *params,
+				struct snd_soc_dai *dai)
+{
+	struct msm_dai_q6_hdmi_dai_data *dai_data = dev_get_drvdata(dai->dev);
+	u32 channel_allocation = 0;
+	u32 level_shift  = 0; /* 0dB */
+	bool down_mix = FALSE;
+
+	dai_data->channels = params_channels(params);
+	dai_data->rate = params_rate(params);
+	dai_data->port_config.hdmi_multi_ch.reserved = 0;
+	dai_data->port_config.hdmi_multi_ch.hdmi_cfg_minor_version = 1;
+	dai_data->port_config.hdmi_multi_ch.sample_rate = dai_data->rate;
+	dai_data->port_config.hdmi_multi_ch.bit_width = 16;
+
+	switch (dai_data->channels) {
+	case 2:
+		channel_allocation  = 0;
+		hdmi_msm_audio_info_setup(1, MSM_HDMI_AUDIO_CHANNEL_2,
+				channel_allocation, level_shift, down_mix);
+		dai_data->port_config.hdmi_multi_ch.channel_allocation =
+			channel_allocation;
+		break;
+	case 6:
+		channel_allocation  = 0x0B;
+		hdmi_msm_audio_info_setup(1, MSM_HDMI_AUDIO_CHANNEL_6,
+				channel_allocation, level_shift, down_mix);
+		dai_data->port_config.hdmi_multi_ch.channel_allocation =
+				channel_allocation;
+		break;
+	case 8:
+		channel_allocation  = 0x1F;
+		hdmi_msm_audio_info_setup(1, MSM_HDMI_AUDIO_CHANNEL_8,
+				channel_allocation, level_shift, down_mix);
+		dai_data->port_config.hdmi_multi_ch.channel_allocation =
+				channel_allocation;
+		break;
+	default:
+		dev_err(dai->dev, "invalid Channels = %u\n",
+				dai_data->channels);
+		return -EINVAL;
+	}
+	dev_dbg(dai->dev, "%s() minor version: %u samplerate: %u bitwidth: %u num_ch = %u channel_allocation = %u datatype = %d\n",
+		 __func__,
+		dai_data->port_config.hdmi_multi_ch.hdmi_cfg_minor_version,
+		dai_data->port_config.hdmi_multi_ch.sample_rate,
+		dai_data->port_config.hdmi_multi_ch.bit_width,
+		dai_data->channels,
+		dai_data->port_config.hdmi_multi_ch.channel_allocation,
+		dai_data->port_config.hdmi_multi_ch.datatype);
+
+	return 0;
+}
+
+
+static void msm_dai_q6_hdmi_shutdown(struct snd_pcm_substream *substream,
+				struct snd_soc_dai *dai)
+{
+	struct msm_dai_q6_hdmi_dai_data *dai_data = dev_get_drvdata(dai->dev);
+	int rc = 0;
+
+	if (!test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) {
+		pr_info("%s:  afe port not started. dai_data->status_mask = %ld\n",
+		 __func__, *dai_data->status_mask);
+		return;
+	}
+
+	rc = afe_close(dai->id); /* can block */
+
+	if (IS_ERR_VALUE(rc))
+		dev_err(dai->dev, "fail to close AFE port\n");
+
+	pr_debug("%s: dai_data->status_mask = %ld\n", __func__,
+			*dai_data->status_mask);
+
+	clear_bit(STATUS_PORT_STARTED, dai_data->status_mask);
+}
+
+
+static int msm_dai_q6_hdmi_prepare(struct snd_pcm_substream *substream,
+		struct snd_soc_dai *dai)
+{
+	struct msm_dai_q6_hdmi_dai_data *dai_data = dev_get_drvdata(dai->dev);
+	int rc = 0;
+
+	if (!test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) {
+		rc = afe_port_start(dai->id, &dai_data->port_config,
+				    dai_data->rate);
+		if (IS_ERR_VALUE(rc))
+			dev_err(dai->dev, "fail to open AFE port %x\n",
+				dai->id);
+		else
+			set_bit(STATUS_PORT_STARTED,
+				dai_data->status_mask);
+	}
+
+	return rc;
+}
+
+static int msm_dai_q6_hdmi_dai_probe(struct snd_soc_dai *dai)
+{
+	struct msm_dai_q6_hdmi_dai_data *dai_data;
+	const struct snd_kcontrol_new *kcontrol;
+	int rc = 0;
+
+	dai_data = kzalloc(sizeof(struct msm_dai_q6_hdmi_dai_data),
+		GFP_KERNEL);
+
+	if (!dai_data) {
+		dev_err(dai->dev, "DAI-%d: fail to allocate dai data\n",
+		dai->id);
+		rc = -ENOMEM;
+	} else
+		dev_set_drvdata(dai->dev, dai_data);
+
+	kcontrol = &hdmi_config_controls[0];
+
+	rc = snd_ctl_add(dai->card->snd_card,
+					 snd_ctl_new1(kcontrol, dai_data));
+	return rc;
+}
+
+static int msm_dai_q6_hdmi_dai_remove(struct snd_soc_dai *dai)
+{
+	struct msm_dai_q6_hdmi_dai_data *dai_data;
+	int rc;
+
+	dai_data = dev_get_drvdata(dai->dev);
+
+	/* If AFE port is still up, close it */
+	if (test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) {
+		rc = afe_close(dai->id); /* can block */
+
+		if (IS_ERR_VALUE(rc))
+			dev_err(dai->dev, "fail to close AFE port\n");
+
+		clear_bit(STATUS_PORT_STARTED, dai_data->status_mask);
+	}
+	kfree(dai_data);
+	snd_soc_unregister_dai(dai->dev);
+
+	return 0;
+}
+
+static struct snd_soc_dai_ops msm_dai_q6_hdmi_ops = {
+	.prepare	= msm_dai_q6_hdmi_prepare,
+	.hw_params	= msm_dai_q6_hdmi_hw_params,
+	.shutdown	= msm_dai_q6_hdmi_shutdown,
+};
+
+static struct snd_soc_dai_driver msm_dai_q6_hdmi_hdmi_rx_dai = {
+	.playback = {
+		.rates = SNDRV_PCM_RATE_48000,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE,
+		.channels_min = 2,
+		.channels_max = 6,
+		.rate_max =     48000,
+		.rate_min =	48000,
+	},
+	.ops = &msm_dai_q6_hdmi_ops,
+	.probe = msm_dai_q6_hdmi_dai_probe,
+	.remove = msm_dai_q6_hdmi_dai_remove,
+};
+
+
+/* To do: change to register DAIs as batch */
+static __devinit int msm_dai_q6_hdmi_dev_probe(struct platform_device *pdev)
+{
+	int rc, id;
+	const char *q6_dev_id = "qcom,msm-dai-q6-dev-id";
+
+	rc = of_property_read_u32(pdev->dev.of_node, q6_dev_id, &id);
+	if (rc) {
+		dev_err(&pdev->dev,
+			"%s: missing %s in dt node\n", __func__, q6_dev_id);
+		return rc;
+	}
+
+	pdev->id = id;
+	dev_set_name(&pdev->dev, "%s.%d", "msm-dai-q6-hdmi", id);
+
+	pr_debug("%s: dev name %s, id:%d\n", __func__,
+			dev_name(&pdev->dev), pdev->id);
+
+	switch (pdev->id) {
+	case HDMI_RX:
+		rc = snd_soc_register_dai(&pdev->dev,
+				&msm_dai_q6_hdmi_hdmi_rx_dai);
+		break;
+	default:
+		dev_err(&pdev->dev, "invalid device ID %d\n", pdev->id);
+		rc = -ENODEV;
+		break;
+	}
+	return rc;
+}
+
+static __devexit int msm_dai_q6_hdmi_dev_remove(struct platform_device *pdev)
+{
+	snd_soc_unregister_dai(&pdev->dev);
+	return 0;
+}
+
+static const struct of_device_id msm_dai_q6_hdmi_dt_match[] = {
+	{.compatible = "qcom,msm-dai-q6-hdmi"},
+	{}
+};
+MODULE_DEVICE_TABLE(of, msm_dai_q6_hdmi_dt_match);
+
+static struct platform_driver msm_dai_q6_hdmi_driver = {
+	.probe  = msm_dai_q6_hdmi_dev_probe,
+	.remove = msm_dai_q6_hdmi_dev_remove,
+	.driver = {
+		.name = "msm-dai-q6-hdmi",
+		.owner = THIS_MODULE,
+		.of_match_table = msm_dai_q6_hdmi_dt_match,
+	},
+};
+
+static int __init msm_dai_q6_hdmi_init(void)
+{
+	return platform_driver_register(&msm_dai_q6_hdmi_driver);
+}
+module_init(msm_dai_q6_hdmi_init);
+
+static void __exit msm_dai_q6_hdmi_exit(void)
+{
+	platform_driver_unregister(&msm_dai_q6_hdmi_driver);
+}
+module_exit(msm_dai_q6_hdmi_exit);
+
+/* Module information */
+MODULE_DESCRIPTION("MSM DSP HDMI DAI driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c
index a6cdad2..354dece 100644
--- a/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c
@@ -899,6 +899,36 @@
 	.remove = msm_dai_q6_dai_remove,
 };
 
+static struct snd_soc_dai_driver msm_dai_q6_fm_rx_dai = {
+	.playback = {
+		.rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+		SNDRV_PCM_RATE_16000,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE,
+		.channels_min = 2,
+		.channels_max = 2,
+		.rate_max = 48000,
+		.rate_min = 8000,
+	},
+	.ops = &msm_dai_q6_ops,
+	.probe = msm_dai_q6_dai_probe,
+	.remove = msm_dai_q6_dai_remove,
+};
+
+static struct snd_soc_dai_driver msm_dai_q6_fm_tx_dai = {
+	.capture = {
+		.rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+		SNDRV_PCM_RATE_16000,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE,
+		.channels_min = 2,
+		.channels_max = 2,
+		.rate_max = 48000,
+		.rate_min = 8000,
+	},
+	.ops = &msm_dai_q6_ops,
+	.probe = msm_dai_q6_dai_probe,
+	.remove = msm_dai_q6_dai_remove,
+};
+
 static int __devinit msm_auxpcm_dev_probe(struct platform_device *pdev)
 {
 	int id;
@@ -1091,7 +1121,7 @@
 		SNDRV_PCM_RATE_16000,
 		.formats = SNDRV_PCM_FMTBIT_S16_LE,
 		.channels_min = 1,
-		.channels_max = 2,
+		.channels_max = 8,
 		.rate_min = 8000,
 		.rate_max = 48000,
 	},
@@ -1106,7 +1136,7 @@
 		SNDRV_PCM_RATE_16000,
 		.formats = SNDRV_PCM_FMTBIT_S16_LE,
 		.channels_min = 1,
-		.channels_max = 2,
+		.channels_max = 8,
 		.rate_min = 8000,
 		.rate_max = 48000,
 	},
@@ -1158,6 +1188,12 @@
 		rc = snd_soc_register_dai(&pdev->dev,
 					&msm_dai_q6_bt_sco_tx_dai);
 		break;
+	case INT_FM_RX:
+		rc = snd_soc_register_dai(&pdev->dev, &msm_dai_q6_fm_rx_dai);
+		break;
+	case INT_FM_TX:
+		rc = snd_soc_register_dai(&pdev->dev, &msm_dai_q6_fm_tx_dai);
+		break;
 	case RT_PROXY_DAI_001_RX:
 	case RT_PROXY_DAI_002_RX:
 		rc = snd_soc_register_dai(&pdev->dev, &msm_dai_q6_afe_rx_dai);
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-lpa-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-lpa-v2.c
index 19e0464..2f0a9d7 100644
--- a/sound/soc/msm/qdsp6v2/msm-pcm-lpa-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-lpa-v2.c
@@ -42,6 +42,7 @@
 struct snd_msm {
 	struct msm_audio *prtd;
 	unsigned volume;
+	atomic_t audio_ocmem_req;
 };
 static struct snd_msm lpa_audio;
 
@@ -227,7 +228,8 @@
 	switch (cmd) {
 	case SNDRV_PCM_TRIGGER_START:
 		prtd->pcm_irq_pos = 0;
-		audio_ocmem_process_req(AUDIO, true);
+		if (!atomic_cmpxchg(&lpa_audio.audio_ocmem_req, 0, 1))
+			audio_ocmem_process_req(AUDIO, true);
 	case SNDRV_PCM_TRIGGER_RESUME:
 	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
 		pr_debug("SNDRV_PCM_TRIGGER_START\n");
@@ -237,7 +239,6 @@
 		break;
 	case SNDRV_PCM_TRIGGER_STOP:
 		pr_debug("SNDRV_PCM_TRIGGER_STOP\n");
-		audio_ocmem_process_req(AUDIO, false);
 		atomic_set(&prtd->start, 0);
 		atomic_set(&prtd->stop, 1);
 		if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
@@ -328,6 +329,7 @@
 	prtd->dsp_cnt = 0;
 	atomic_set(&prtd->pending_buffer, 1);
 	atomic_set(&prtd->stop, 1);
+	atomic_set(&lpa_audio.audio_ocmem_req, 0);
 	runtime->private_data = prtd;
 	lpa_audio.prtd = prtd;
 	lpa_set_volume(lpa_audio.volume);
@@ -387,6 +389,9 @@
 
 	dir = IN;
 	atomic_set(&prtd->pending_buffer, 0);
+
+	if (atomic_cmpxchg(&lpa_audio.audio_ocmem_req, 1, 0))
+		audio_ocmem_process_req(AUDIO, false);
 	lpa_audio.prtd = NULL;
 	q6asm_cmd(prtd->audio_client, CMD_CLOSE);
 	q6asm_audio_client_buf_free_contiguous(dir,
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c
index 7483bb6..1e6fc04 100644
--- a/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c
@@ -56,7 +56,7 @@
 	.rate_min =             8000,
 	.rate_max =             48000,
 	.channels_min =         1,
-	.channels_max =         2,
+	.channels_max =         4,
 	.buffer_bytes_max =     CAPTURE_NUM_PERIODS * CAPTURE_PERIOD_SIZE,
 	.period_bytes_min =	CAPTURE_PERIOD_SIZE,
 	.period_bytes_max =     CAPTURE_PERIOD_SIZE,
@@ -324,28 +324,23 @@
 			kfree(prtd);
 			return -ENOMEM;
 		}
+
+		pr_debug("%s: session ID %d\n", __func__,
+			prtd->audio_client->session);
+		prtd->session_id = prtd->audio_client->session;
+		msm_pcm_routing_reg_phy_stream(soc_prtd->dai_link->be_id,
+			prtd->session_id, substream->stream);
+		prtd->cmd_ack = 1;
+
 	}
 	/* Capture path */
-	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+	else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
 		runtime->hw = msm_pcm_hardware_capture;
-		ret = q6asm_open_read(prtd->audio_client, FORMAT_LINEAR_PCM);
-		if (ret < 0) {
-			pr_err("%s: pcm in open failed\n", __func__);
-			q6asm_audio_client_free(prtd->audio_client);
-			kfree(prtd);
-			return -ENOMEM;
-		}
+	else {
+		pr_err("Invalid Stream type %d\n", substream->stream);
+		return -EINVAL;
 	}
 
-	pr_debug("%s: session ID %d\n", __func__, prtd->audio_client->session);
-
-	prtd->session_id = prtd->audio_client->session;
-	msm_pcm_routing_reg_phy_stream(soc_prtd->dai_link->be_id,
-			prtd->session_id, substream->stream);
-
-	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
-		prtd->cmd_ack = 1;
-
 	ret = snd_pcm_hw_constraint_list(runtime, 0,
 				SNDRV_PCM_HW_PARAM_RATE,
 				&constraints_sample_rates);
@@ -530,12 +525,15 @@
 	int dir = OUT;
 
 	pr_debug("%s\n", __func__);
-	q6asm_cmd(prtd->audio_client, CMD_CLOSE);
-	q6asm_audio_client_buf_free_contiguous(dir,
+	if (prtd->audio_client) {
+		q6asm_cmd(prtd->audio_client, CMD_CLOSE);
+		q6asm_audio_client_buf_free_contiguous(dir,
 				prtd->audio_client);
+		q6asm_audio_client_free(prtd->audio_client);
+	}
+
 	msm_pcm_routing_dereg_phy_stream(soc_prtd->dai_link->be_id,
-	SNDRV_PCM_STREAM_CAPTURE);
-	q6asm_audio_client_free(prtd->audio_client);
+		SNDRV_PCM_STREAM_CAPTURE);
 	kfree(prtd);
 
 	return 0;
@@ -617,14 +615,31 @@
 	struct snd_pcm_runtime *runtime = substream->runtime;
 	struct msm_audio *prtd = runtime->private_data;
 	struct snd_dma_buffer *dma_buf = &substream->dma_buffer;
+	struct snd_soc_pcm_runtime *soc_prtd = substream->private_data;
 	struct audio_buffer *buf;
 	int dir, ret;
 
 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
 		dir = IN;
-	else
+	else {
 		dir = OUT;
-	pr_debug("%s: before buf alloc\n", __func__);
+		pr_debug("%s Opening %d-ch PCM read stream\n",
+			__func__, params_channels(params));
+		ret = q6asm_open_read(prtd->audio_client, FORMAT_LINEAR_PCM);
+		if (ret < 0) {
+			pr_err("%s: q6asm_open_read failed\n", __func__);
+			q6asm_audio_client_free(prtd->audio_client);
+			prtd->audio_client = NULL;
+			return -ENOMEM;
+		}
+	}
+
+	pr_debug("%s: session ID %d\n", __func__, prtd->audio_client->session);
+	prtd->session_id = prtd->audio_client->session;
+	msm_pcm_routing_reg_phy_stream(soc_prtd->dai_link->be_id,
+			prtd->session_id, substream->stream);
+
+
 	ret = q6asm_audio_client_buf_alloc_contiguous(dir,
 			prtd->audio_client,
 			runtime->hw.period_bytes_min,
@@ -634,7 +649,6 @@
 							ret);
 		return -ENOMEM;
 	}
-	pr_debug("%s: after buf alloc\n", __func__);
 	buf = prtd->audio_client->port[dir].buf;
 	if (buf == NULL || buf[0].data == NULL)
 		return -ENOMEM;
diff --git a/sound/soc/msm/qdsp6v2/q6adm.c b/sound/soc/msm/qdsp6v2/q6adm.c
index 62257b4..6acc136 100644
--- a/sound/soc/msm/qdsp6v2/q6adm.c
+++ b/sound/soc/msm/qdsp6v2/q6adm.c
@@ -51,6 +51,24 @@
 
 static struct adm_ctl			this_adm;
 
+static void adm_callback_debug_print(struct apr_client_data *data)
+{
+	uint32_t *payload;
+	payload = data->payload;
+
+	if (data->payload_size >= 8)
+		pr_debug("%s: code = 0x%x PL#0[%x], PL#1[%x], size = %d\n",
+			__func__, data->opcode, payload[0], payload[1],
+			data->payload_size);
+	else if (data->payload_size >= 4)
+		pr_debug("%s: code = 0x%x PL#0[%x], size = %d\n",
+			__func__, data->opcode, payload[0],
+			data->payload_size);
+	else
+		pr_debug("%s: code = 0x%x, size = %d\n",
+			__func__, data->opcode, data->payload_size);
+}
+
 static int32_t adm_callback(struct apr_client_data *data, void *priv)
 {
 	uint32_t *payload;
@@ -86,10 +104,7 @@
 		return 0;
 	}
 
-	pr_debug("%s: code = 0x%x PL#0[%x], PL#1[%x], size = %d\n", __func__,
-			data->opcode, payload[0], payload[1],
-					data->payload_size);
-
+	adm_callback_debug_print(data);
 	if (data->payload_size) {
 		index = q6audio_get_port_index(data->token);
 		if (index < 0 || index >= Q6_AFE_MAX_PORTS) {
@@ -99,6 +114,10 @@
 		}
 		if (data->opcode == APR_BASIC_RSP_RESULT) {
 			pr_debug("APR_BASIC_RSP_RESULT id %x\n", payload[0]);
+			if (payload[1] != 0) {
+				pr_err("%s: cmd = 0x%x returned error = 0x%x\n",
+					__func__, payload[0], payload[1]);
+			}
 			switch (payload[0]) {
 			case ADM_CMD_SET_PP_PARAMS_V5:
 				if (rtac_make_adm_callback(
@@ -116,8 +135,18 @@
 				wake_up(&this_adm.wait[index]);
 				break;
 			case ADM_CMD_SHARED_MEM_MAP_REGIONS:
-				/* Block until memory handle comes back */
-				/* via ADM_CMDRSP_SHARED_MEM_MAP_REGIONS */
+				pr_debug("%s: ADM_CMD_SHARED_MEM_MAP_REGIONS\n",
+					__func__);
+				/* Should only come here if there is an APR */
+				/* error or malformed APR packet. Otherwise */
+				/* response will be returned as */
+				/* ADM_CMDRSP_SHARED_MEM_MAP_REGIONS */
+				if (payload[1] != 0) {
+					pr_err("%s: ADM map error, resuming\n",
+						__func__);
+					atomic_set(&this_adm.copp_stat[0], 1);
+					wake_up(&this_adm.wait[index]);
+				}
 				break;
 			default:
 				pr_err("%s: Unknown Cmd: 0x%x\n", __func__,
@@ -247,24 +276,27 @@
 	get_audproc_cal(acdb_path, &aud_cal);
 
 	/* map & cache buffers used */
+	atomic_set(&mem_map_index, acdb_path);
 	if (((mem_addr_audproc[acdb_path].cal_paddr != aud_cal.cal_paddr)  &&
 		(aud_cal.cal_size > 0)) ||
 		(aud_cal.cal_size > mem_addr_audproc[acdb_path].cal_size)) {
 
-		atomic_set(&mem_map_index, acdb_path);
 		if (mem_addr_audproc[acdb_path].cal_paddr != 0)
 			adm_memory_unmap_regions(port_id,
 				&mem_addr_audproc[acdb_path].cal_paddr,
 				&size, 1);
 
 		result = adm_memory_map_regions(port_id, &aud_cal.cal_paddr,
-						0, &aud_cal.cal_size, 1);
-		if (result < 0)
+						0, &size, 1);
+		if (result < 0) {
 			pr_err("ADM audproc mmap did not work! path = %d, addr = 0x%x, size = %d\n",
 				acdb_path, aud_cal.cal_paddr,
 				aud_cal.cal_size);
-		else
-			mem_addr_audproc[acdb_path] = aud_cal;
+		} else {
+			mem_addr_audproc[acdb_path].cal_paddr =
+							aud_cal.cal_paddr;
+			mem_addr_audproc[acdb_path].cal_size = size;
+		}
 	}
 
 	if (!send_adm_cal_block(port_id, &aud_cal))
@@ -278,24 +310,27 @@
 	get_audvol_cal(acdb_path, &aud_cal);
 
 	/* map & cache buffers used */
+	atomic_set(&mem_map_index, (acdb_path + MAX_AUDPROC_TYPES));
 	if (((mem_addr_audvol[acdb_path].cal_paddr != aud_cal.cal_paddr)  &&
 		(aud_cal.cal_size > 0))  ||
 		(aud_cal.cal_size > mem_addr_audvol[acdb_path].cal_size)) {
 
-		atomic_set(&mem_map_index, (acdb_path + MAX_AUDPROC_TYPES));
 		if (mem_addr_audvol[acdb_path].cal_paddr != 0)
 			adm_memory_unmap_regions(port_id,
 				&mem_addr_audvol[acdb_path].cal_paddr,
 				&size, 1);
 
 		result = adm_memory_map_regions(port_id, &aud_cal.cal_paddr,
-						0, &aud_cal.cal_size, 1);
-		if (result < 0)
+						0, &size, 1);
+		if (result < 0) {
 			pr_err("ADM audvol mmap did not work! path = %d, addr = 0x%x, size = %d\n",
 				acdb_path, aud_cal.cal_paddr,
 				aud_cal.cal_size);
-		else
-			mem_addr_audvol[acdb_path] = aud_cal;
+		} else {
+			mem_addr_audvol[acdb_path].cal_paddr =
+							aud_cal.cal_paddr;
+			mem_addr_audvol[acdb_path].cal_size = size;
+		}
 	}
 
 	if (!send_adm_cal_block(port_id, &aud_cal))
@@ -454,6 +489,21 @@
 		} else if (channel_mode == 2) {
 			open.dev_channel_mapping[0] = PCM_CHANNEL_FL;
 			open.dev_channel_mapping[1] = PCM_CHANNEL_FR;
+		} else if (channel_mode == 3)	{
+			open.dev_channel_mapping[0] = PCM_CHANNEL_FL;
+			open.dev_channel_mapping[0] = PCM_CHANNEL_FR;
+			open.dev_channel_mapping[1] = PCM_CHANNEL_FC;
+		} else if (channel_mode == 4) {
+			open.dev_channel_mapping[0] = PCM_CHANNEL_FL;
+			open.dev_channel_mapping[1] = PCM_CHANNEL_FR;
+			open.dev_channel_mapping[2] = PCM_CHANNEL_RB;
+			open.dev_channel_mapping[3] = PCM_CHANNEL_LB;
+		} else if (channel_mode == 5) {
+			open.dev_channel_mapping[0] = PCM_CHANNEL_FL;
+			open.dev_channel_mapping[1] = PCM_CHANNEL_FR;
+			open.dev_channel_mapping[2] = PCM_CHANNEL_FC;
+			open.dev_channel_mapping[3] = PCM_CHANNEL_LB;
+			open.dev_channel_mapping[4] = PCM_CHANNEL_RB;
 		} else if (channel_mode == 6) {
 			open.dev_channel_mapping[0] = PCM_CHANNEL_FL;
 			open.dev_channel_mapping[1] = PCM_CHANNEL_FR;
@@ -461,6 +511,15 @@
 			open.dev_channel_mapping[3] = PCM_CHANNEL_FC;
 			open.dev_channel_mapping[4] = PCM_CHANNEL_LB;
 			open.dev_channel_mapping[5] = PCM_CHANNEL_RB;
+		} else if (channel_mode == 8) {
+			open.dev_channel_mapping[0] = PCM_CHANNEL_FL;
+			open.dev_channel_mapping[1] = PCM_CHANNEL_FR;
+			open.dev_channel_mapping[2] = PCM_CHANNEL_LFE;
+			open.dev_channel_mapping[3] = PCM_CHANNEL_FC;
+			open.dev_channel_mapping[4] = PCM_CHANNEL_LB;
+			open.dev_channel_mapping[5] = PCM_CHANNEL_RB;
+			open.dev_channel_mapping[6] = PCM_CHANNEL_FLC;
+			open.dev_channel_mapping[7] = PCM_CHANNEL_FRC;
 		} else {
 			pr_err("%s invalid num_chan %d\n", __func__,
 					channel_mode);
@@ -664,8 +723,8 @@
 								APR_PKT_VER);
 	mmap_regions->hdr.pkt_size = cmd_size;
 	mmap_regions->hdr.src_port = 0;
-	mmap_regions->hdr.dest_port = 0;
-	mmap_regions->hdr.token = 0;
+	mmap_regions->hdr.dest_port = atomic_read(&this_adm.copp_id[index]);
+	mmap_regions->hdr.token = port_id;
 	mmap_regions->hdr.opcode = ADM_CMD_SHARED_MEM_MAP_REGIONS;
 	mmap_regions->mem_pool_id = ADSP_MEMORY_MAP_SHMEM8_4K_POOL & 0x00ff;
 	mmap_regions->num_regions = bufcnt & 0x00ff;
@@ -733,8 +792,8 @@
 							APR_PKT_VER);
 	unmap_regions.hdr.pkt_size = cmd_size;
 	unmap_regions.hdr.src_port = 0;
-	unmap_regions.hdr.dest_port = 0;
-	unmap_regions.hdr.token = 0;
+	unmap_regions.hdr.dest_port = atomic_read(&this_adm.copp_id[index]);
+	unmap_regions.hdr.token = port_id;
 	unmap_regions.hdr.opcode = ADM_CMD_SHARED_MEM_UNMAP_REGIONS;
 	unmap_regions.mem_map_handle = atomic_read(&mem_map_handles[
 						atomic_read(&mem_map_index)]);
diff --git a/sound/soc/msm/qdsp6v2/q6afe.c b/sound/soc/msm/qdsp6v2/q6afe.c
index f0465a5..4819e0a 100644
--- a/sound/soc/msm/qdsp6v2/q6afe.c
+++ b/sound/soc/msm/qdsp6v2/q6afe.c
@@ -344,6 +344,8 @@
 		break;
 	case INT_BT_SCO_RX:
 	case INT_BT_SCO_TX:
+	case INT_FM_RX:
+	case INT_FM_TX:
 		cfg_type = AFE_PARAM_ID_INTERNAL_BT_FM_CONFIG;
 		break;
 	default:
diff --git a/sound/soc/msm/qdsp6v2/q6asm.c b/sound/soc/msm/qdsp6v2/q6asm.c
index 0dd6faf..2d52c43 100644
--- a/sound/soc/msm/qdsp6v2/q6asm.c
+++ b/sound/soc/msm/qdsp6v2/q6asm.c
@@ -34,7 +34,6 @@
 
 #include <mach/memory.h>
 #include <mach/debug_mm.h>
-#include <mach/peripheral-loader.h>
 #include <mach/qdsp6v2/audio_acdb.h>
 #include <mach/qdsp6v2/rtac.h>
 #include <mach/msm_subsystem_map.h>
@@ -476,9 +475,13 @@
 	return;
 }
 
-int q6asm_set_io_mode(struct audio_client *ac, uint32_t mode)
+int q6asm_set_io_mode(struct audio_client *ac, uint32_t mode1)
 {
+	uint32_t mode;
+
 	ac->io_mode &= 0xFF00;
+	mode = (mode1 & 0xF);
+
 	pr_debug("%s ac->mode after anding with FF00:0x[%x],\n",
 		__func__, ac->io_mode);
 	if (ac == NULL) {
@@ -486,7 +489,7 @@
 		return -EINVAL;
 	}
 	if ((mode == ASYNC_IO_MODE) || (mode == SYNC_IO_MODE)) {
-		ac->io_mode |= mode;
+		ac->io_mode |= mode1;
 		pr_debug("%s:Set Mode to 0x[%x]\n", __func__, ac->io_mode);
 		return 0;
 	} else {
@@ -1693,7 +1696,7 @@
 	enc_cfg.bits_per_sample = 16;
 	enc_cfg.sample_rate = rate;
 	enc_cfg.is_signed = 1;
-	channel_mapping = enc_cfg.channel_mapping;  /* ??? PHANI */
+	channel_mapping = enc_cfg.channel_mapping;
 
 	memset(channel_mapping, 0, PCM_FORMAT_MAX_NUM_CHANNEL);
 
@@ -1743,7 +1746,8 @@
 	enc_cfg.bits_per_sample = 16;
 	enc_cfg.sample_rate = 0;/*rate;*/
 	enc_cfg.is_signed = 1;
-	channel_mapping = enc_cfg.channel_mapping;  /* ??? PHANI */
+	channel_mapping = enc_cfg.channel_mapping;
+
 
 	memset(channel_mapping, 0, PCM_FORMAT_MAX_NUM_CHANNEL);
 
@@ -1778,27 +1782,36 @@
 		lchannel_mapping[0] = PCM_CHANNEL_FL;
 		lchannel_mapping[1] = PCM_CHANNEL_FR;
 	} else if (channels == 3) {
-		lchannel_mapping[0] = PCM_CHANNEL_FC;
-		lchannel_mapping[1] = PCM_CHANNEL_FL;
-		lchannel_mapping[2] = PCM_CHANNEL_FR;
+		lchannel_mapping[0] = PCM_CHANNEL_FL;
+		lchannel_mapping[1] = PCM_CHANNEL_FR;
+		lchannel_mapping[2] = PCM_CHANNEL_FC;
 	} else if (channels == 4) {
-		lchannel_mapping[0] = PCM_CHANNEL_FC;
-		lchannel_mapping[1] = PCM_CHANNEL_FL;
-		lchannel_mapping[2] = PCM_CHANNEL_FR;
+		lchannel_mapping[0] = PCM_CHANNEL_FL;
+		lchannel_mapping[1] = PCM_CHANNEL_FR;
+		lchannel_mapping[2] = PCM_CHANNEL_RB;
 		lchannel_mapping[3] = PCM_CHANNEL_LB;
 	} else if (channels == 5) {
-		lchannel_mapping[0] = PCM_CHANNEL_FC;
-		lchannel_mapping[1] = PCM_CHANNEL_FL;
-		lchannel_mapping[2] = PCM_CHANNEL_FR;
+		lchannel_mapping[0] = PCM_CHANNEL_FL;
+		lchannel_mapping[1] = PCM_CHANNEL_FR;
+		lchannel_mapping[2] = PCM_CHANNEL_FC;
 		lchannel_mapping[3] = PCM_CHANNEL_LB;
 		lchannel_mapping[4] = PCM_CHANNEL_RB;
 	} else if (channels == 6) {
-		lchannel_mapping[0] = PCM_CHANNEL_FC;
-		lchannel_mapping[1] = PCM_CHANNEL_FL;
-		lchannel_mapping[2] = PCM_CHANNEL_FR;
-		lchannel_mapping[3] = PCM_CHANNEL_LB;
-		lchannel_mapping[4] = PCM_CHANNEL_RB;
-		lchannel_mapping[5] = PCM_CHANNEL_LFE;
+		lchannel_mapping[0] = PCM_CHANNEL_FL;
+		lchannel_mapping[1] = PCM_CHANNEL_FR;
+		lchannel_mapping[2] = PCM_CHANNEL_LFE;
+		lchannel_mapping[3] = PCM_CHANNEL_FC;
+		lchannel_mapping[4] = PCM_CHANNEL_LB;
+		lchannel_mapping[5] = PCM_CHANNEL_RB;
+	} else if (channels == 6) {
+		lchannel_mapping[0] = PCM_CHANNEL_FL;
+		lchannel_mapping[1] = PCM_CHANNEL_FR;
+		lchannel_mapping[2] = PCM_CHANNEL_LFE;
+		lchannel_mapping[3] = PCM_CHANNEL_FC;
+		lchannel_mapping[4] = PCM_CHANNEL_LB;
+		lchannel_mapping[5] = PCM_CHANNEL_RB;
+		lchannel_mapping[6] = PCM_CHANNEL_FLC;
+		lchannel_mapping[7] = PCM_CHANNEL_FRC;
 	} else {
 		pr_err("%s: ERROR.unsupported num_ch = %u\n",
 		 __func__, channels);
@@ -2951,6 +2964,7 @@
 	struct audio_port_data     *port;
 	u32 lbuf_addr_lsw;
 	u32 liomode;
+	u32 io_compressed;
 
 	if (!ac || ac->apr == NULL) {
 		pr_err("%s: APR handle NULL\n", __func__);
@@ -2971,9 +2985,12 @@
 	write.timestamp_msw = param->msw_ts;
 	write.timestamp_lsw = param->lsw_ts;
 	liomode = (ASYNC_IO_MODE | NT_MODE);
+	io_compressed = (ASYNC_IO_MODE | COMPRESSED_IO);
 
 	if (ac->io_mode == liomode)
 		lbuf_addr_lsw = (write.buf_addr_lsw - 32);
+	else if (ac->io_mode == io_compressed)
+		lbuf_addr_lsw = (write.buf_addr_lsw - 0x40);
 	else
 		lbuf_addr_lsw = write.buf_addr_lsw;
 
diff --git a/sound/soc/msm/qdsp6v2/q6voice.c b/sound/soc/msm/qdsp6v2/q6voice.c
index 1f6dbf1..338cfe3 100644
--- a/sound/soc/msm/qdsp6v2/q6voice.c
+++ b/sound/soc/msm/qdsp6v2/q6voice.c
@@ -42,6 +42,12 @@
 /* CVS CAL Size: 49152 = 48 * 1024 */
 #define CVS_CAL_SIZE 49152
 
+enum {
+	VOC_TOKEN_NONE,
+	VOIP_MEM_MAP_TOKEN,
+	VOC_CAL_MEM_MAP_TOKEN,
+};
+
 static struct common_data common;
 
 static int voice_send_enable_vocproc_cmd(struct voice_data *v);
@@ -50,7 +56,6 @@
 static int voice_send_set_device_cmd(struct voice_data *v);
 static int voice_send_disable_vocproc_cmd(struct voice_data *v);
 static int voice_send_vol_index_cmd(struct voice_data *v);
-static int voice_send_mvm_map_memory_physical_cmd(struct voice_data *v);
 static int voice_send_mvm_unmap_memory_physical_cmd(struct voice_data *v,
 						    unsigned int bufcnt);
 static int voice_send_mvm_cal_network_cmd(struct voice_data *v);
@@ -59,6 +64,16 @@
 static int voice_send_cvs_packet_exchange_config_cmd(struct voice_data *v);
 static int voice_set_packet_exchange_mode_and_config(uint16_t session_id,
 						     uint32_t mode);
+
+static int voice_send_cvs_register_cal_cmd(struct voice_data *v);
+static int voice_send_cvs_deregister_cal_cmd(struct voice_data *v);
+static int voice_send_cvp_register_dev_cfg_cmd(struct voice_data *v);
+static int voice_send_cvp_deregister_dev_cfg_cmd(struct voice_data *v);
+static int voice_send_cvp_register_cal_cmd(struct voice_data *v);
+static int voice_send_cvp_deregister_cal_cmd(struct voice_data *v);
+static int voice_send_cvp_register_vol_cal_cmd(struct voice_data *v);
+static int voice_send_cvp_deregister_vol_cal_cmd(struct voice_data *v);
+
 static int voice_cvs_stop_playback(struct voice_data *v);
 static int voice_cvs_start_playback(struct voice_data *v);
 static int voice_cvs_start_record(struct voice_data *v, uint32_t rec_mode);
@@ -68,6 +83,10 @@
 static int32_t qdsp_cvs_callback(struct apr_client_data *data, void *priv);
 static int32_t qdsp_cvp_callback(struct apr_client_data *data, void *priv);
 
+static int voice_send_set_widevoice_enable_cmd(struct voice_data *v);
+static int voice_send_set_pp_enable_cmd(struct voice_data *v,
+					uint32_t module_id, int enable);
+
 static u16 voice_get_mvm_handle(struct voice_data *v)
 {
 	if (v == NULL) {
@@ -749,6 +768,114 @@
 	return -EINVAL;
 }
 
+static int voice_send_set_widevoice_enable_cmd(struct voice_data *v)
+{
+	struct mvm_set_widevoice_enable_cmd mvm_set_wv_cmd;
+	int ret = 0;
+	void *apr_mvm;
+	u16 mvm_handle;
+
+	if (v == NULL) {
+		pr_err("%s: v is NULL\n", __func__);
+		return -EINVAL;
+	}
+	apr_mvm = common.apr_q6_mvm;
+
+	if (!apr_mvm) {
+		pr_err("%s: apr_mvm is NULL.\n", __func__);
+		return -EINVAL;
+	}
+	mvm_handle = voice_get_mvm_handle(v);
+
+	mvm_set_wv_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+						     APR_HDR_LEN(APR_HDR_SIZE),
+						     APR_PKT_VER);
+	mvm_set_wv_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+						   sizeof(mvm_set_wv_cmd) -
+						   APR_HDR_SIZE);
+	mvm_set_wv_cmd.hdr.src_port = v->session_id;
+	mvm_set_wv_cmd.hdr.dest_port = mvm_handle;
+	mvm_set_wv_cmd.hdr.token = 0;
+	mvm_set_wv_cmd.hdr.opcode = VSS_IWIDEVOICE_CMD_SET_WIDEVOICE;
+
+	mvm_set_wv_cmd.vss_set_wv.enable = v->wv_enable;
+
+	v->mvm_state = CMD_STATUS_FAIL;
+	ret = apr_send_pkt(apr_mvm, (uint32_t *) &mvm_set_wv_cmd);
+	if (ret < 0) {
+		pr_err("Fail: sending mvm set widevoice enable,\n");
+		goto fail;
+	}
+	ret = wait_event_timeout(v->mvm_wait,
+				 (v->mvm_state == CMD_STATUS_SUCCESS),
+				 msecs_to_jiffies(TIMEOUT_MS));
+	if (!ret) {
+		pr_err("%s: wait_event timeout\n", __func__);
+		goto fail;
+	}
+	return 0;
+fail:
+	return -EINVAL;
+}
+
+static int voice_send_set_pp_enable_cmd(struct voice_data *v,
+					uint32_t module_id, int enable)
+{
+	struct cvs_set_pp_enable_cmd cvs_set_pp_cmd;
+	int ret = 0;
+	void *apr_cvs;
+	u16 cvs_handle;
+
+	if (v == NULL) {
+		pr_err("%s: v is NULL\n", __func__);
+		return -EINVAL;
+	}
+	apr_cvs = common.apr_q6_cvs;
+
+	if (!apr_cvs) {
+		pr_err("%s: apr_cvs is NULL.\n", __func__);
+		return -EINVAL;
+	}
+	cvs_handle = voice_get_cvs_handle(v);
+
+	cvs_set_pp_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+						     APR_HDR_LEN(APR_HDR_SIZE),
+						     APR_PKT_VER);
+	cvs_set_pp_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+						   sizeof(cvs_set_pp_cmd) -
+						   APR_HDR_SIZE);
+	cvs_set_pp_cmd.hdr.src_port = v->session_id;
+	cvs_set_pp_cmd.hdr.dest_port = cvs_handle;
+	cvs_set_pp_cmd.hdr.token = 0;
+	cvs_set_pp_cmd.hdr.opcode = VSS_ICOMMON_CMD_SET_UI_PROPERTY;
+
+	cvs_set_pp_cmd.vss_set_pp.module_id = module_id;
+	cvs_set_pp_cmd.vss_set_pp.param_id = VOICE_PARAM_MOD_ENABLE;
+	cvs_set_pp_cmd.vss_set_pp.param_size = MOD_ENABLE_PARAM_LEN;
+	cvs_set_pp_cmd.vss_set_pp.reserved = 0;
+	cvs_set_pp_cmd.vss_set_pp.enable = enable;
+	cvs_set_pp_cmd.vss_set_pp.reserved_field = 0;
+	pr_debug("voice_send_set_pp_enable_cmd, module_id=%d, enable=%d\n",
+		module_id, enable);
+
+	v->cvs_state = CMD_STATUS_FAIL;
+	ret = apr_send_pkt(apr_cvs, (uint32_t *) &cvs_set_pp_cmd);
+	if (ret < 0) {
+		pr_err("Fail: sending cvs set pp enable,\n");
+		goto fail;
+	}
+	ret = wait_event_timeout(v->cvs_wait,
+				 (v->cvs_state == CMD_STATUS_SUCCESS),
+				 msecs_to_jiffies(TIMEOUT_MS));
+	if (!ret) {
+		pr_err("%s: wait_event timeout\n", __func__);
+		goto fail;
+	}
+	return 0;
+fail:
+	return -EINVAL;
+}
+
 static int voice_set_dtx(struct voice_data *v)
 {
 	int ret = 0;
@@ -1263,32 +1390,557 @@
 	return -EINVAL;
 }
 
-static int voice_send_mvm_map_memory_physical_cmd(struct voice_data *v)
+static int voice_send_cvs_register_cal_cmd(struct voice_data *v)
+{
+	struct cvs_register_cal_data_cmd cvs_reg_cal_cmd;
+	struct acdb_cal_block cal_block;
+	int ret = 0;
+	memset(&cvs_reg_cal_cmd, 0, sizeof(cvs_reg_cal_cmd));
+
+	if (v == NULL) {
+		pr_err("%s: v is NULL\n", __func__);
+
+		goto fail;
+	}
+
+	if (!common.apr_q6_cvs) {
+		pr_err("%s: apr_cvs is NULL\n", __func__);
+
+		goto fail;
+	}
+
+	if (!common.cal_mem_handle) {
+		pr_err("%s: Cal mem handle is NULL\n", __func__);
+
+		goto fail;
+	}
+
+	get_all_vocstrm_cal(&cal_block);
+	if (cal_block.cal_size == 0) {
+		pr_err("%s: CVS cal size is 0\n", __func__);
+
+		goto fail;
+	}
+
+	cvs_reg_cal_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+				APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+	cvs_reg_cal_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+				sizeof(cvs_reg_cal_cmd) - APR_HDR_SIZE);
+	cvs_reg_cal_cmd.hdr.src_port = v->session_id;
+	cvs_reg_cal_cmd.hdr.dest_port = voice_get_cvs_handle(v);
+	cvs_reg_cal_cmd.hdr.token = 0;
+	cvs_reg_cal_cmd.hdr.opcode =
+				VSS_ISTREAM_CMD_REGISTER_CALIBRATION_DATA_V2;
+
+	cvs_reg_cal_cmd.cvs_cal_data.cal_mem_handle = common.cal_mem_handle;
+	cvs_reg_cal_cmd.cvs_cal_data.cal_mem_address = cal_block.cal_paddr;
+	cvs_reg_cal_cmd.cvs_cal_data.cal_mem_size = cal_block.cal_size;
+
+	/* Get the column info corresponding to CVS cal from ACDB. */
+	get_voice_col_data(VOCSTRM_CAL, &cal_block);
+	memcpy(&cvs_reg_cal_cmd.cvs_cal_data.column_info[0],
+	       (void *) cal_block.cal_kvaddr,
+	       cal_block.cal_size);
+
+	v->cvs_state = CMD_STATUS_FAIL;
+	ret = apr_send_pkt(common.apr_q6_cvs, (uint32_t *) &cvs_reg_cal_cmd);
+	if (ret < 0) {
+		pr_err("%s: Error %d registering CVS cal\n", __func__, ret);
+
+		goto fail;
+	}
+	ret = wait_event_timeout(v->cvs_wait,
+				 (v->cvs_state == CMD_STATUS_SUCCESS),
+				 msecs_to_jiffies(TIMEOUT_MS));
+	if (!ret) {
+		pr_err("%s: Command timeout\n", __func__);
+
+		goto fail;
+	}
+
+	return 0;
+
+fail:
+	return -EINVAL;
+}
+
+static int voice_send_cvs_deregister_cal_cmd(struct voice_data *v)
+{
+	struct cvs_deregister_cal_data_cmd cvs_dereg_cal_cmd;
+	struct acdb_cal_block cal_block;
+	int ret = 0;
+	memset(&cvs_dereg_cal_cmd, 0, sizeof(cvs_dereg_cal_cmd));
+
+	if (v == NULL) {
+		pr_err("%s: v is NULL\n", __func__);
+
+		goto fail;
+	}
+
+	if (!common.apr_q6_cvs) {
+		pr_err("%s: apr_cvs is NULL\n", __func__);
+
+		goto fail;
+	}
+
+	get_all_vocstrm_cal(&cal_block);
+	if (cal_block.cal_size == 0)
+		return 0;
+
+	cvs_dereg_cal_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+				APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+	cvs_dereg_cal_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+				sizeof(cvs_dereg_cal_cmd) - APR_HDR_SIZE);
+	cvs_dereg_cal_cmd.hdr.src_port = v->session_id;
+	cvs_dereg_cal_cmd.hdr.dest_port = voice_get_cvs_handle(v);
+	cvs_dereg_cal_cmd.hdr.token = 0;
+	cvs_dereg_cal_cmd.hdr.opcode =
+				VSS_ISTREAM_CMD_DEREGISTER_CALIBRATION_DATA;
+
+	v->cvs_state = CMD_STATUS_FAIL;
+	ret = apr_send_pkt(common.apr_q6_cvs, (uint32_t *) &cvs_dereg_cal_cmd);
+	if (ret < 0) {
+		pr_err("%s: Error %d de-registering CVS cal\n", __func__, ret);
+
+		goto fail;
+	}
+	ret = wait_event_timeout(v->cvs_wait,
+				 (v->cvs_state == CMD_STATUS_SUCCESS),
+				 msecs_to_jiffies(TIMEOUT_MS));
+	if (!ret) {
+		pr_err("%s: Command  timeout\n", __func__);
+
+		goto fail;
+	}
+
+	return 0;
+
+fail:
+	return -EINVAL;
+
+}
+
+static int voice_send_cvp_register_dev_cfg_cmd(struct voice_data *v)
+{
+	struct cvp_register_dev_cfg_cmd cvp_reg_dev_cfg_cmd;
+	struct acdb_cal_block cal_block;
+	int ret = 0;
+	memset(&cvp_reg_dev_cfg_cmd, 0, sizeof(cvp_reg_dev_cfg_cmd));
+
+	if (v == NULL) {
+		pr_err("%s: v is NULL\n", __func__);
+
+		goto fail;
+	}
+
+	if (!common.apr_q6_cvp) {
+		pr_err("%s: apr_cvp is NULL\n", __func__);
+
+		goto fail;
+	}
+
+	if (!common.cal_mem_handle) {
+		pr_err("%s: Cal mem handle is NULL\n", __func__);
+
+		goto fail;
+	}
+
+	get_vocproc_dev_cfg_cal(&cal_block);
+	if (cal_block.cal_size == 0) {
+		pr_err("%s: CVP cal size is 0\n", __func__);
+
+		goto fail;
+	}
+
+	cvp_reg_dev_cfg_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+				APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+	cvp_reg_dev_cfg_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+				sizeof(cvp_reg_dev_cfg_cmd) - APR_HDR_SIZE);
+	cvp_reg_dev_cfg_cmd.hdr.src_port = v->session_id;
+	cvp_reg_dev_cfg_cmd.hdr.dest_port = voice_get_cvp_handle(v);
+	cvp_reg_dev_cfg_cmd.hdr.token = 0;
+	cvp_reg_dev_cfg_cmd.hdr.opcode =
+					VSS_IVOCPROC_CMD_REGISTER_DEVICE_CONFIG;
+
+	cvp_reg_dev_cfg_cmd.cvp_dev_cfg_data.mem_handle = common.cal_mem_handle;
+	cvp_reg_dev_cfg_cmd.cvp_dev_cfg_data.mem_address = cal_block.cal_paddr;
+	cvp_reg_dev_cfg_cmd.cvp_dev_cfg_data.mem_size = cal_block.cal_size;
+
+	v->cvp_state = CMD_STATUS_FAIL;
+	ret = apr_send_pkt(common.apr_q6_cvp,
+			   (uint32_t *) &cvp_reg_dev_cfg_cmd);
+	if (ret < 0) {
+		pr_err("%s: Error %d registering CVP dev cfg cal\n",
+		       __func__, ret);
+
+		goto fail;
+	}
+	ret = wait_event_timeout(v->cvp_wait,
+				 (v->cvp_state == CMD_STATUS_SUCCESS),
+				 msecs_to_jiffies(TIMEOUT_MS));
+	if (!ret) {
+		pr_err("%s: Command timeout\n", __func__);
+
+		goto fail;
+	}
+
+	return 0;
+
+fail:
+	return -EINVAL;
+}
+
+static int voice_send_cvp_deregister_dev_cfg_cmd(struct voice_data *v)
+{
+	struct cvp_deregister_dev_cfg_cmd cvp_dereg_dev_cfg_cmd;
+	struct acdb_cal_block cal_block;
+	int ret = 0;
+	memset(&cvp_dereg_dev_cfg_cmd, 0, sizeof(cvp_dereg_dev_cfg_cmd));
+
+	if (v == NULL) {
+		pr_err("%s: v is NULL\n", __func__);
+
+		goto fail;
+	}
+
+	if (!common.apr_q6_cvp) {
+		pr_err("%s: apr_cvp is NULL.\n", __func__);
+
+		goto fail;
+	}
+
+	get_vocproc_dev_cfg_cal(&cal_block);
+	if (cal_block.cal_size == 0)
+		return 0;
+
+	cvp_dereg_dev_cfg_cmd.hdr.hdr_field =
+				APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+					APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+	cvp_dereg_dev_cfg_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+				sizeof(cvp_dereg_dev_cfg_cmd) - APR_HDR_SIZE);
+	cvp_dereg_dev_cfg_cmd.hdr.src_port = v->session_id;
+	cvp_dereg_dev_cfg_cmd.hdr.dest_port = voice_get_cvp_handle(v);
+	cvp_dereg_dev_cfg_cmd.hdr.token = 0;
+	cvp_dereg_dev_cfg_cmd.hdr.opcode =
+				VSS_IVOCPROC_CMD_DEREGISTER_DEVICE_CONFIG;
+
+	v->cvp_state = CMD_STATUS_FAIL;
+	ret = apr_send_pkt(common.apr_q6_cvp,
+			   (uint32_t *) &cvp_dereg_dev_cfg_cmd);
+	if (ret < 0) {
+		pr_err("%s: Error %d de-registering CVP dev cfg cal\n",
+		       __func__, ret);
+
+		goto fail;
+	}
+	ret = wait_event_timeout(v->cvp_wait,
+				 (v->cvp_state == CMD_STATUS_SUCCESS),
+				 msecs_to_jiffies(TIMEOUT_MS));
+	if (!ret) {
+		pr_err("%s: Command timeout\n", __func__);
+
+		goto fail;
+	}
+
+	return 0;
+
+fail:
+	return -EINVAL;
+}
+
+static int voice_send_cvp_register_cal_cmd(struct voice_data *v)
+{
+	struct cvp_register_cal_data_cmd cvp_reg_cal_cmd;
+	struct acdb_cal_block cal_block;
+	int ret = 0;
+	memset(&cvp_reg_cal_cmd, 0, sizeof(cvp_reg_cal_cmd));
+
+	if (v == NULL) {
+		pr_err("%s: v is NULL\n", __func__);
+
+		goto fail;
+	}
+
+	if (!common.apr_q6_cvp) {
+		pr_err("%s: apr_cvp is NULL\n", __func__);
+
+		goto fail;
+	}
+
+	if (!common.cal_mem_handle) {
+		pr_err("%s: Cal mem handle is NULL\n", __func__);
+
+		goto fail;
+	}
+
+	get_all_vocproc_cal(&cal_block);
+	if (cal_block.cal_size == 0) {
+		pr_err("%s: CVP cal size is 0\n", __func__);
+
+		goto fail;
+	}
+
+	cvp_reg_cal_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+				APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+	cvp_reg_cal_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+				sizeof(cvp_reg_cal_cmd) - APR_HDR_SIZE);
+	cvp_reg_cal_cmd.hdr.src_port = v->session_id;
+	cvp_reg_cal_cmd.hdr.dest_port = voice_get_cvp_handle(v);
+	cvp_reg_cal_cmd.hdr.token = 0;
+	cvp_reg_cal_cmd.hdr.opcode =
+				VSS_IVOCPROC_CMD_REGISTER_CALIBRATION_DATA_V2;
+
+	cvp_reg_cal_cmd.cvp_cal_data.cal_mem_handle = common.cal_mem_handle;
+	cvp_reg_cal_cmd.cvp_cal_data.cal_mem_address = cal_block.cal_paddr;
+	cvp_reg_cal_cmd.cvp_cal_data.cal_mem_size = cal_block.cal_size;
+
+	/* Get the column info corresponding to CVP cal from ACDB. */
+	get_voice_col_data(VOCPROC_CAL, &cal_block);
+	memcpy(&cvp_reg_cal_cmd.cvp_cal_data.column_info[0],
+	       (void *) cal_block.cal_kvaddr,
+	       cal_block.cal_size);
+
+	v->cvp_state = CMD_STATUS_FAIL;
+	ret = apr_send_pkt(common.apr_q6_cvp, (uint32_t *) &cvp_reg_cal_cmd);
+	if (ret < 0) {
+		pr_err("%s: Error %d registering CVP cal\n", __func__, ret);
+
+		goto fail;
+	}
+	ret = wait_event_timeout(v->cvp_wait,
+				 (v->cvp_state == CMD_STATUS_SUCCESS),
+				 msecs_to_jiffies(TIMEOUT_MS));
+	if (!ret) {
+		pr_err("%s: Command timeout\n", __func__);
+
+		goto fail;
+	}
+
+	return 0;
+
+fail:
+	return -EINVAL;
+}
+
+static int voice_send_cvp_deregister_cal_cmd(struct voice_data *v)
+{
+	struct cvp_deregister_cal_data_cmd cvp_dereg_cal_cmd;
+	struct acdb_cal_block cal_block;
+	int ret = 0;
+	memset(&cvp_dereg_cal_cmd, 0, sizeof(cvp_dereg_cal_cmd));
+
+	if (v == NULL) {
+		pr_err("%s: v is NULL\n", __func__);
+
+		goto fail;
+	}
+
+	if (!common.apr_q6_cvp) {
+		pr_err("%s: apr_cvp is NULL.\n", __func__);
+
+		goto fail;
+	}
+
+	get_all_vocproc_cal(&cal_block);
+	if (cal_block.cal_size == 0)
+		return 0;
+
+	cvp_dereg_cal_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+				APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+	cvp_dereg_cal_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+				sizeof(cvp_dereg_cal_cmd) - APR_HDR_SIZE);
+	cvp_dereg_cal_cmd.hdr.src_port = v->session_id;
+	cvp_dereg_cal_cmd.hdr.dest_port = voice_get_cvp_handle(v);
+	cvp_dereg_cal_cmd.hdr.token = 0;
+	cvp_dereg_cal_cmd.hdr.opcode =
+				VSS_IVOCPROC_CMD_DEREGISTER_CALIBRATION_DATA;
+
+	v->cvp_state = CMD_STATUS_FAIL;
+	ret = apr_send_pkt(common.apr_q6_cvp, (uint32_t *) &cvp_dereg_cal_cmd);
+	if (ret < 0) {
+		pr_err("%s: Error %d de-registering CVP cal\n", __func__, ret);
+
+		goto fail;
+	}
+	ret = wait_event_timeout(v->cvp_wait,
+				 (v->cvp_state == CMD_STATUS_SUCCESS),
+				 msecs_to_jiffies(TIMEOUT_MS));
+	if (!ret) {
+		pr_err("%s: Command timeout\n", __func__);
+
+		goto fail;
+	}
+
+	return 0;
+
+fail:
+	return -EINVAL;
+}
+
+static int voice_send_cvp_register_vol_cal_cmd(struct voice_data *v)
+{
+	struct cvp_register_vol_cal_data_cmd cvp_reg_vol_cal_cmd;
+	struct acdb_cal_block cal_block;
+	int ret = 0;
+	memset(&cvp_reg_vol_cal_cmd, 0, sizeof(cvp_reg_vol_cal_cmd));
+
+	if (v == NULL) {
+		pr_err("%s: v is NULL\n", __func__);
+
+		goto fail;
+	}
+
+	if (!common.apr_q6_cvp) {
+		pr_err("%s: apr_cvp is NULL.\n", __func__);
+
+		goto fail;
+	}
+
+	if (!common.cal_mem_handle) {
+		pr_err("%s: Cal mem handle is NULL\n", __func__);
+
+		goto fail;
+	}
+
+	get_all_vocvol_cal(&cal_block);
+	if (cal_block.cal_size == 0) {
+		pr_err("%s: CVP vol cal size is 0\n", __func__);
+
+		goto fail;
+	}
+
+	cvp_reg_vol_cal_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+				APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+	cvp_reg_vol_cal_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+				sizeof(cvp_reg_vol_cal_cmd) - APR_HDR_SIZE);
+	cvp_reg_vol_cal_cmd.hdr.src_port = v->session_id;
+	cvp_reg_vol_cal_cmd.hdr.dest_port = voice_get_cvp_handle(v);
+	cvp_reg_vol_cal_cmd.hdr.token = 0;
+	cvp_reg_vol_cal_cmd.hdr.opcode =
+			VSS_IVOCPROC_CMD_REGISTER_VOL_CALIBRATION_DATA;
+
+	cvp_reg_vol_cal_cmd.cvp_vol_cal_data.cal_mem_handle =
+							common.cal_mem_handle;
+	cvp_reg_vol_cal_cmd.cvp_vol_cal_data.cal_mem_address =
+							cal_block.cal_paddr;
+	cvp_reg_vol_cal_cmd.cvp_vol_cal_data.cal_mem_size = cal_block.cal_size;
+
+	/* Get the column info corresponding to CVP volume cal from ACDB. */
+	get_voice_col_data(VOCVOL_CAL, &cal_block);
+	memcpy(&cvp_reg_vol_cal_cmd.cvp_vol_cal_data.column_info[0],
+	       (void *) cal_block.cal_kvaddr,
+	       cal_block.cal_size);
+
+	v->cvp_state = CMD_STATUS_FAIL;
+	ret = apr_send_pkt(common.apr_q6_cvp,
+			   (uint32_t *) &cvp_reg_vol_cal_cmd);
+	if (ret < 0) {
+		pr_err("%s: Error %d registering CVP vol cal\n", __func__, ret);
+
+		goto fail;
+	}
+	ret = wait_event_timeout(v->cvp_wait,
+				 (v->cvp_state == CMD_STATUS_SUCCESS),
+				 msecs_to_jiffies(TIMEOUT_MS));
+	if (!ret) {
+		pr_err("%s: Command timeout\n", __func__);
+
+		goto fail;
+	}
+
+	return 0;
+
+fail:
+	return -EINVAL;
+}
+
+static int voice_send_cvp_deregister_vol_cal_cmd(struct voice_data *v)
+{
+	struct cvp_deregister_vol_cal_data_cmd cvp_dereg_vol_cal_cmd;
+	struct acdb_cal_block cal_block;
+	int ret = 0;
+	memset(&cvp_dereg_vol_cal_cmd, 0, sizeof(cvp_dereg_vol_cal_cmd));
+
+	if (v == NULL) {
+		pr_err("%s: v is NULL\n", __func__);
+
+		goto fail;
+	}
+
+	if (!common.apr_q6_cvp) {
+		pr_err("%s: apr_cvp is NULL\n", __func__);
+
+		goto fail;
+	}
+
+	get_all_vocvol_cal(&cal_block);
+	if (cal_block.cal_size == 0)
+		return 0;
+
+	cvp_dereg_vol_cal_cmd.hdr.hdr_field =
+			APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+				APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+	cvp_dereg_vol_cal_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+				sizeof(cvp_dereg_vol_cal_cmd) - APR_HDR_SIZE);
+	cvp_dereg_vol_cal_cmd.hdr.src_port = v->session_id;
+	cvp_dereg_vol_cal_cmd.hdr.dest_port = voice_get_cvp_handle(v);
+	cvp_dereg_vol_cal_cmd.hdr.token = 0;
+	cvp_dereg_vol_cal_cmd.hdr.opcode =
+			VSS_IVOCPROC_CMD_DEREGISTER_VOL_CALIBRATION_DATA;
+
+	v->cvp_state = CMD_STATUS_FAIL;
+	ret = apr_send_pkt(common.apr_q6_cvp,
+			   (uint32_t *) &cvp_dereg_vol_cal_cmd);
+	if (ret < 0) {
+		pr_err("%s: Error %d de-registering CVP vol cal\n",
+		       __func__, ret);
+
+		goto fail;
+	}
+	ret = wait_event_timeout(v->cvp_wait,
+				 (v->cvp_state == CMD_STATUS_SUCCESS),
+				 msecs_to_jiffies(TIMEOUT_MS));
+	if (!ret) {
+		pr_err("%s: Command timeout\n", __func__);
+
+		goto fail;
+	}
+
+	return 0;
+
+fail:
+	return -EINVAL;
+}
+
+static int voice_map_memory_physical_cmd(struct voice_data *v,
+					 struct mem_map_table *table_info,
+					 dma_addr_t phys,
+					 uint32_t size,
+					 uint32_t token)
 {
 	struct vss_imemory_cmd_map_physical_t mvm_map_phys_cmd;
 	uint32_t *memtable;
 	int ret = 0;
-	void *apr_mvm;
-	u16 mvm_handle;
 
 	if (v == NULL) {
 		pr_err("%s: v is NULL\n", __func__);
-		return -EINVAL;
+
+		goto fail;
 	}
 
-	apr_mvm = common.apr_q6_mvm;
-
-	if (!apr_mvm) {
+	if (!common.apr_q6_mvm) {
 		pr_err("%s: apr_mvm is NULL.\n", __func__);
-		return -EINVAL;
+
+		goto fail;
 	}
 
-	if (!v->shmem_info.memtbl.data) {
-		pr_err("%s: shmem_info.memtbl.data is NULL.\n", __func__);
-		return -EINVAL;
+	if (!table_info->data) {
+		pr_err("%s: memory table is NULL.\n", __func__);
+
+		goto fail;
 	}
 
-	memtable = (uint32_t *)v->shmem_info.memtbl.data;
+	memtable = (uint32_t *) table_info->data;
 
 	/*
 	 * Store next table descriptor's address(64 bit) as NULL as there
@@ -1301,25 +1953,22 @@
 	memtable[2] = 0;
 
 	/* Store shared mem add */
-	memtable[3] = v->shmem_info.sh_buf.buf[0].phys;
+	memtable[3] = phys;
 	memtable[4] = 0;
 
 	/* Store shared memory size */
-	memtable[5] = v->shmem_info.sh_buf.buf[0].size * NUM_OF_BUFFERS;
-
-	mvm_handle = voice_get_mvm_handle(v);
+	memtable[5] = size;
 
 	mvm_map_phys_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
 				APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
 	mvm_map_phys_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
 				sizeof(mvm_map_phys_cmd) - APR_HDR_SIZE);
 	mvm_map_phys_cmd.hdr.src_port = v->session_id;
-	mvm_map_phys_cmd.hdr.dest_port = mvm_handle;
-	mvm_map_phys_cmd.hdr.token = 0;
+	mvm_map_phys_cmd.hdr.dest_port = voice_get_mvm_handle(v);
+	mvm_map_phys_cmd.hdr.token = token;
 	mvm_map_phys_cmd.hdr.opcode = VSS_IMEMORY_CMD_MAP_PHYSICAL;
 
-	mvm_map_phys_cmd.table_descriptor.mem_address =
-						v->shmem_info.memtbl.phys;
+	mvm_map_phys_cmd.table_descriptor.mem_address = table_info->phys;
 	mvm_map_phys_cmd.table_descriptor.mem_size =
 			sizeof(struct vss_imemory_block_t) +
 			sizeof(struct vss_imemory_table_descriptor_t);
@@ -1330,36 +1979,71 @@
 	mvm_map_phys_cmd.min_data_width = 8;
 	mvm_map_phys_cmd.max_data_width = 64;
 
-	pr_debug("%s: ntd->add: %lld, ntd->size: %d, table->add: 0x%x\n",
-		__func__,
-		*((uint64_t *)v->shmem_info.memtbl.data),
-		*(((uint32_t *)(v->shmem_info.memtbl.data)) + 2),
-		*(((uint32_t *)(v->shmem_info.memtbl.data)) + 3));
-	pr_debug("%s: table->size: %d, pkt_size: %d, mvm_handle: 0x%x\n",
-		__func__,
-		*(((uint32_t *)(v->shmem_info.memtbl.data)) + 5),
-		mvm_map_phys_cmd.hdr.pkt_size, mvm_handle);
+	pr_debug("%s: next table desc: add: %lld, size: %d\n",
+		 __func__, *((uint64_t *) memtable),
+		 *(((uint32_t *) memtable) + 2));
+	pr_debug("%s: phy add of of mem being mapped 0x%x, size: %d\n",
+		 __func__, *(((uint32_t *) memtable) + 3),
+		 *(((uint32_t *) memtable) + 5));
 
 	v->mvm_state = CMD_STATUS_FAIL;
-	ret = apr_send_pkt(apr_mvm, (uint32_t *) &mvm_map_phys_cmd);
+	ret = apr_send_pkt(common.apr_q6_mvm, (uint32_t *) &mvm_map_phys_cmd);
 	if (ret < 0) {
-		pr_err("Fail: sending mvm map phy cmd %d\n", ret);
+		pr_err("%s: Error %d sending mvm map phy cmd\n", __func__, ret);
+
 		goto fail;
 	}
 
 	ret = wait_event_timeout(v->mvm_wait,
-			(v->mvm_state == CMD_STATUS_SUCCESS),
-			msecs_to_jiffies(TIMEOUT_MS));
+				 (v->mvm_state == CMD_STATUS_SUCCESS),
+				 msecs_to_jiffies(TIMEOUT_MS));
 	if (!ret) {
-		pr_err("%s: wait_event timeout %d\n", __func__, ret);
+		pr_err("%s: Command timeout\n", __func__);
+
 		goto fail;
 	}
 
 	return 0;
+
 fail:
 	return -EINVAL;
 }
 
+static int voice_mem_map_cal_block(struct voice_data *v)
+{
+	int ret = 0;
+	struct acdb_cal_block cal_block;
+
+	if (v == NULL) {
+		pr_err("%s: v is NULL\n", __func__);
+
+		return -EINVAL;
+	}
+
+	if (common.cal_mem_handle != 0) {
+		pr_debug("%s: Cal block already mem mapped\n", __func__);
+
+		return ret;
+	}
+
+	/* Get the physical address of calibration memory block from ACDB. */
+	get_voice_cal_allocation(&cal_block);
+
+	if (!cal_block.cal_paddr) {
+		pr_err("%s: Cal block not allocated\n", __func__);
+
+		return -EINVAL;
+	}
+
+	ret = voice_map_memory_physical_cmd(v,
+					    &common.cal_mem_map_table,
+					    cal_block.cal_paddr,
+					    cal_block.cal_size,
+					    VOC_CAL_MEM_MAP_TOKEN);
+
+	return ret;
+}
+
 static int voice_setup_vocproc(struct voice_data *v)
 {
 	struct cvp_create_full_ctl_session_cmd cvp_session_cmd;
@@ -1437,6 +2121,12 @@
 		goto fail;
 	}
 
+	voice_send_cvs_register_cal_cmd(v);
+
+	voice_send_cvp_register_dev_cfg_cmd(v);
+	voice_send_cvp_register_cal_cmd(v);
+	voice_send_cvp_register_vol_cal_cmd(v);
+
 	/* enable vocproc */
 	ret = voice_send_enable_vocproc_cmd(v);
 	if (ret < 0)
@@ -1464,6 +2154,20 @@
 		voice_send_netid_timing_cmd(v);
 	}
 
+	/* enable widevoice if wv_enable is set */
+	if (v->wv_enable)
+		voice_send_set_widevoice_enable_cmd(v);
+
+	/* enable slowtalk if st_enable is set */
+	if (v->st_enable)
+		voice_send_set_pp_enable_cmd(v,
+					     MODULE_ID_VOICE_MODULE_ST,
+					     v->st_enable);
+
+	voice_send_set_pp_enable_cmd(v,
+				     MODULE_ID_VOICE_MODULE_FENS,
+				     v->fens_enable);
+
 	/* Start in-call music delivery if this feature is enabled */
 	if (v->music_info.play_enable)
 		voice_cvs_start_playback(v);
@@ -1789,6 +2493,11 @@
 		goto fail;
 	}
 
+	voice_send_cvp_deregister_vol_cal_cmd(v);
+	voice_send_cvp_deregister_cal_cmd(v);
+	voice_send_cvp_deregister_dev_cfg_cmd(v);
+
+	voice_send_cvs_deregister_cal_cmd(v);
 
 	/* destrop cvp session */
 	cvp_destroy_session_cmd.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
@@ -1997,20 +2706,18 @@
 {
 	struct cvs_set_mute_cmd cvs_mute_cmd;
 	int ret = 0;
-	void *apr_cvs;
-	u16 cvs_handle;
 
 	if (v == NULL) {
 		pr_err("%s: v is NULL\n", __func__);
-		return -EINVAL;
-	}
-	apr_cvs = common.apr_q6_cvs;
 
-	if (!apr_cvs) {
-		pr_err("%s: apr_cvs is NULL.\n", __func__);
-		return -EINVAL;
+		goto fail;
 	}
-	cvs_handle = voice_get_cvs_handle(v);
+
+	if (!common.apr_q6_cvs) {
+		pr_err("%s: apr_cvs is NULL.\n", __func__);
+
+		goto fail;
+	}
 
 	/* send mute/unmute to cvs */
 	cvs_mute_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
@@ -2019,25 +2726,31 @@
 	cvs_mute_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
 					sizeof(cvs_mute_cmd) - APR_HDR_SIZE);
 	cvs_mute_cmd.hdr.src_port = v->session_id;
-	cvs_mute_cmd.hdr.dest_port = cvs_handle;
+	cvs_mute_cmd.hdr.dest_port = voice_get_cvs_handle(v);
 	cvs_mute_cmd.hdr.token = 0;
-	cvs_mute_cmd.hdr.opcode = VSS_ISTREAM_CMD_SET_MUTE;
-	cvs_mute_cmd.cvs_set_mute.direction = 0; /*tx*/
+	cvs_mute_cmd.hdr.opcode = VSS_IVOLUME_CMD_MUTE_V2;
+	cvs_mute_cmd.cvs_set_mute.direction = VSS_IVOLUME_DIRECTION_TX;
 	cvs_mute_cmd.cvs_set_mute.mute_flag = v->dev_tx.mute;
+	cvs_mute_cmd.cvs_set_mute.ramp_duration_ms = DEFAULT_MUTE_RAMP_DURATION;
 
 	v->cvs_state = CMD_STATUS_FAIL;
-	ret = apr_send_pkt(apr_cvs, (uint32_t *) &cvs_mute_cmd);
+	ret = apr_send_pkt(common.apr_q6_cvs, (uint32_t *) &cvs_mute_cmd);
 	if (ret < 0) {
-		pr_err("Fail: send STREAM SET MUTE\n");
+		pr_err("%s: Error %d sending stream mute\n", __func__, ret);
+
 		goto fail;
 	}
 	ret = wait_event_timeout(v->cvs_wait,
 				 (v->cvs_state == CMD_STATUS_SUCCESS),
 				 msecs_to_jiffies(TIMEOUT_MS));
-	if (!ret)
-		pr_err("%s: wait_event timeout\n", __func__);
+	if (!ret) {
+		pr_err("%s: Command timeout\n", __func__);
+
+		goto fail;
+	}
 
 	return 0;
+
 fail:
 	return -EINVAL;
 }
@@ -2046,19 +2759,18 @@
 {
 	struct cvp_set_mute_cmd cvp_mute_cmd;
 	int ret = 0;
-	void *apr_cvp;
-	u16 cvp_handle;
+
 	if (v == NULL) {
 		pr_err("%s: v is NULL\n", __func__);
-		return -EINVAL;
-	}
-	apr_cvp = common.apr_q6_cvp;
 
-	if (!apr_cvp) {
-		pr_err("%s: apr_cvp is NULL.\n", __func__);
-		return -EINVAL;
+		goto fail;
 	}
-	cvp_handle = voice_get_cvp_handle(v);
+
+	if (!common.apr_q6_cvp) {
+		pr_err("%s: apr_cvp is NULL.\n", __func__);
+
+		goto fail;
+	}
 
 	cvp_mute_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
 						APR_HDR_LEN(APR_HDR_SIZE),
@@ -2066,25 +2778,32 @@
 	cvp_mute_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
 					sizeof(cvp_mute_cmd) - APR_HDR_SIZE);
 	cvp_mute_cmd.hdr.src_port = v->session_id;
-	cvp_mute_cmd.hdr.dest_port = cvp_handle;
+	cvp_mute_cmd.hdr.dest_port = voice_get_cvp_handle(v);
 	cvp_mute_cmd.hdr.token = 0;
-	cvp_mute_cmd.hdr.opcode = VSS_IVOCPROC_CMD_SET_MUTE;
-	cvp_mute_cmd.cvp_set_mute.direction = 1;
+	cvp_mute_cmd.hdr.opcode = VSS_IVOLUME_CMD_MUTE_V2;
+	cvp_mute_cmd.cvp_set_mute.direction = VSS_IVOLUME_DIRECTION_RX;
 	cvp_mute_cmd.cvp_set_mute.mute_flag = v->dev_rx.mute;
+
 	v->cvp_state = CMD_STATUS_FAIL;
-	ret = apr_send_pkt(apr_cvp, (uint32_t *) &cvp_mute_cmd);
+	ret = apr_send_pkt(common.apr_q6_cvp, (uint32_t *) &cvp_mute_cmd);
 	if (ret < 0) {
-		pr_err("Fail in sending RX device mute cmd\n");
-		return -EINVAL;
+		pr_err("%s: Error %d sending rx device cmd\n", __func__, ret);
+
+		goto fail;
 	}
 	ret = wait_event_timeout(v->cvp_wait,
 				 (v->cvp_state == CMD_STATUS_SUCCESS),
 				 msecs_to_jiffies(TIMEOUT_MS));
 	if (!ret) {
-		pr_err("%s: wait_event timeout\n", __func__);
-		return -EINVAL;
+		pr_err("%s: Command timeout\n", __func__);
+
+		goto fail;
 	}
+
 	return 0;
+
+fail:
+	return -EINVAL;
 }
 
 static int voice_send_vol_index_cmd(struct voice_data *v)
@@ -2569,6 +3288,9 @@
 			goto fail;
 		}
 
+		voice_send_cvp_deregister_vol_cal_cmd(v);
+		voice_send_cvp_deregister_cal_cmd(v);
+		voice_send_cvp_deregister_dev_cfg_cmd(v);
 
 		v->voc_state = VOC_CHANGE;
 	}
@@ -2598,6 +3320,10 @@
 			goto fail;
 		}
 
+		voice_send_cvp_register_dev_cfg_cmd(v);
+		voice_send_cvp_register_cal_cmd(v);
+		voice_send_cvp_register_vol_cal_cmd(v);
+
 	ret = voice_send_enable_vocproc_cmd(v);
 	if (ret < 0) {
 		pr_err("%s: enable vocproc failed %d\n", __func__, ret);
@@ -2607,6 +3333,22 @@
 	/* Send tty mode if tty device is used */
 	voice_send_tty_mode_cmd(v);
 
+	/* enable widevoice if wv_enable is set */
+	if (v->wv_enable)
+		voice_send_set_widevoice_enable_cmd(v);
+
+	/* enable slowtalk */
+	if (v->st_enable)
+		voice_send_set_pp_enable_cmd(v,
+					     MODULE_ID_VOICE_MODULE_ST,
+					     v->st_enable);
+
+	/* enable FENS */
+	if (v->fens_enable)
+		voice_send_set_pp_enable_cmd(v,
+					     MODULE_ID_VOICE_MODULE_FENS,
+					     v->fens_enable);
+
 	v->voc_state = VOC_RUN;
 	}
 
@@ -2770,7 +3512,8 @@
 	v->wv_enable = wv_enable;
 
 	mvm_handle = voice_get_mvm_handle(v);
-
+	if (mvm_handle != 0)
+		voice_send_set_widevoice_enable_cmd(v);
 
 	mutex_unlock(&v->lock);
 
@@ -2814,6 +3557,17 @@
 	else if (module_id == MODULE_ID_VOICE_MODULE_FENS)
 		v->fens_enable = enable;
 
+	if (v->voc_state == VOC_RUN) {
+		if (module_id == MODULE_ID_VOICE_MODULE_ST)
+			ret = voice_send_set_pp_enable_cmd(v,
+						MODULE_ID_VOICE_MODULE_ST,
+						enable);
+		else if (module_id == MODULE_ID_VOICE_MODULE_FENS)
+			ret = voice_send_set_pp_enable_cmd(v,
+						MODULE_ID_VOICE_MODULE_FENS,
+						enable);
+	}
+
 	mutex_unlock(&v->lock);
 
 	return ret;
@@ -2985,8 +3739,21 @@
 			pr_err("create mvm and cvs failed\n");
 			goto fail;
 		}
+
+		/* Memory map the calibration memory block. */
+		ret = voice_mem_map_cal_block(v);
+		if (ret < 0) {
+			pr_err("%s: Memory map of cal block failed %d\n",
+			       __func__, ret);
+			/* Allow call to continue, call quality will be bad. */
+		}
+
 		if (is_voip_session(session_id)) {
-			ret = voice_send_mvm_map_memory_physical_cmd(v);
+			ret = voice_map_memory_physical_cmd(v,
+			      &v->shmem_info.memtbl,
+			      v->shmem_info.sh_buf.buf[0].phys,
+			      v->shmem_info.sh_buf.buf[0].size * NUM_OF_BUFFERS,
+			      VOIP_MEM_MAP_TOKEN);
 			if (ret) {
 				pr_err("%s: mvm_map_memory_phy failed %d\n",
 					__func__, ret);
@@ -3132,7 +3899,8 @@
 		}
 	} else if (data->opcode == VSS_IMEMORY_RSP_MAP) {
 		pr_debug("%s, Revd VSS_IMEMORY_RSP_MAP response\n", __func__);
-		if (data->payload_size) {
+
+		if (data->payload_size && data->token == VOIP_MEM_MAP_TOKEN) {
 			ptr = data->payload;
 			if (ptr[0]) {
 				v->shmem_info.mem_handle = ptr[0];
@@ -3141,6 +3909,21 @@
 				v->mvm_state = CMD_STATUS_SUCCESS;
 				wake_up(&v->mvm_wait);
 			}
+		} else if (data->payload_size &&
+			   data->token == VOC_CAL_MEM_MAP_TOKEN) {
+			ptr = data->payload;
+			if (ptr[0]) {
+				c->cal_mem_handle = ptr[0];
+
+				pr_debug("%s: cal mem handle 0x%x\n",
+					 __func__, c->cal_mem_handle);
+
+				v->mvm_state = CMD_STATUS_SUCCESS;
+				wake_up(&v->mvm_wait);
+			}
+		} else {
+			pr_err("%s: Unknown mem map token %d\n",
+			       __func__, data->token);
 		}
 	}
 	return 0;
@@ -3204,14 +3987,14 @@
 				v->cvs_state = CMD_STATUS_SUCCESS;
 				wake_up(&v->cvs_wait);
 				break;
-			case VSS_ISTREAM_CMD_SET_MUTE:
+			case VSS_IVOLUME_CMD_MUTE_V2:
 			case VSS_ISTREAM_CMD_SET_MEDIA_TYPE:
 			case VSS_ISTREAM_CMD_VOC_AMR_SET_ENC_RATE:
 			case VSS_ISTREAM_CMD_VOC_AMRWB_SET_ENC_RATE:
 			case VSS_ISTREAM_CMD_SET_ENC_DTX_MODE:
 			case VSS_ISTREAM_CMD_CDMA_SET_ENC_MINMAX_RATE:
 			case APRV2_IBASIC_CMD_DESTROY_SESSION:
-			case VSS_ISTREAM_CMD_REGISTER_CALIBRATION_DATA:
+			case VSS_ISTREAM_CMD_REGISTER_CALIBRATION_DATA_V2:
 			case VSS_ISTREAM_CMD_DEREGISTER_CALIBRATION_DATA:
 			case VSS_ICOMMON_CMD_MAP_MEMORY:
 			case VSS_ICOMMON_CMD_UNMAP_MEMORY:
@@ -3410,13 +4193,15 @@
 			case VSS_IVOCPROC_CMD_ENABLE:
 			case VSS_IVOCPROC_CMD_DISABLE:
 			case APRV2_IBASIC_CMD_DESTROY_SESSION:
-			case VSS_IVOCPROC_CMD_REGISTER_VOLUME_CAL_TABLE:
-			case VSS_IVOCPROC_CMD_DEREGISTER_VOLUME_CAL_TABLE:
-			case VSS_IVOCPROC_CMD_REGISTER_CALIBRATION_DATA:
+			case VSS_IVOCPROC_CMD_REGISTER_VOL_CALIBRATION_DATA:
+			case VSS_IVOCPROC_CMD_DEREGISTER_VOL_CALIBRATION_DATA:
+			case VSS_IVOCPROC_CMD_REGISTER_CALIBRATION_DATA_V2:
 			case VSS_IVOCPROC_CMD_DEREGISTER_CALIBRATION_DATA:
+			case VSS_IVOCPROC_CMD_REGISTER_DEVICE_CONFIG:
+			case VSS_IVOCPROC_CMD_DEREGISTER_DEVICE_CONFIG:
 			case VSS_ICOMMON_CMD_MAP_MEMORY:
 			case VSS_ICOMMON_CMD_UNMAP_MEMORY:
-			case VSS_IVOCPROC_CMD_SET_MUTE:
+			case VSS_IVOLUME_CMD_MUTE_V2:
 				v->cvp_state = CMD_STATUS_SUCCESS;
 				wake_up(&v->cvp_wait);
 				break;
@@ -3564,6 +4349,74 @@
 	return -EINVAL;
 }
 
+static int voice_alloc_cal_mem_map_table(void)
+{
+	int ret = 0;
+	int len;
+
+	common.cal_mem_map_table.client = msm_ion_client_create(UINT_MAX,
+								"voc_client");
+
+	if (IS_ERR_OR_NULL((void *) common.cal_mem_map_table.client)) {
+		pr_err("%s: ION create client for cal mem map table failed\n",
+		       __func__);
+
+		goto err;
+	}
+
+	common.cal_mem_map_table.handle =
+				ion_alloc(common.cal_mem_map_table.client,
+					  sizeof(struct vss_imemory_table_t),
+					  SZ_4K, (0x1 << ION_AUDIO_HEAP_ID), 0);
+	if (IS_ERR_OR_NULL((void *) common.cal_mem_map_table.handle)) {
+		pr_err("%s: ION memory alloc for cal mem map table failed\n",
+		       __func__);
+
+		goto err_ion_client;
+	}
+
+	ret = ion_phys(common.cal_mem_map_table.client,
+		      common.cal_mem_map_table.handle,
+		      (ion_phys_addr_t *) &common.cal_mem_map_table.phys,
+		      (size_t *) &len);
+	if (ret) {
+		pr_err("%s: Phy addr for cal mem map table failed %d\n",
+		       __func__, ret);
+
+		goto err_ion_handle;
+	}
+
+	common.cal_mem_map_table.data =
+				ion_map_kernel(common.cal_mem_map_table.client,
+					       common.cal_mem_map_table.handle);
+	if (IS_ERR_OR_NULL((void *) common.cal_mem_map_table.data)) {
+		pr_err("%s: Virtual addr for cal memory map table failed\n",
+		       __func__);
+
+		goto err_ion_handle;
+	}
+
+	memset(common.cal_mem_map_table.data, 0,
+	       sizeof(struct vss_imemory_table_t));
+
+	common.cal_mem_map_table.size = sizeof(struct vss_imemory_table_t);
+
+	pr_debug("%s: data 0x%x phys 0x%x\n", __func__,
+		 (unsigned int) common.cal_mem_map_table.data,
+		 common.cal_mem_map_table.phys);
+
+	return 0;
+
+err_ion_handle:
+	ion_free(common.cal_mem_map_table.client,
+		 common.cal_mem_map_table.handle);
+err_ion_client:
+	ion_client_destroy(common.cal_mem_map_table.client);
+	memset(&common.cal_mem_map_table, 0, sizeof(common.cal_mem_map_table));
+err:
+	return -EINVAL;
+}
+
 static int __init voice_init(void)
 {
 	int rc = 0, i = 0;
@@ -3612,6 +4465,9 @@
 			pr_err("failed to alloc mem map talbe %d\n", rc);
 	}
 
+	/* Allocate memory for calibration memory map table. */
+	rc = voice_alloc_cal_mem_map_table();
+
 	return rc;
 }
 
diff --git a/sound/soc/msm/qdsp6v2/q6voice.h b/sound/soc/msm/qdsp6v2/q6voice.h
index df0cbec..9f82694 100644
--- a/sound/soc/msm/qdsp6v2/q6voice.h
+++ b/sound/soc/msm/qdsp6v2/q6voice.h
@@ -25,6 +25,8 @@
  */
 #define BUFFER_BLOCK_SIZE       4096
 
+#define MAX_COL_INFO_SIZE	324
+
 #define VOC_REC_UPLINK		0x00
 #define VOC_REC_DOWNLINK	0x01
 #define VOC_REC_BOTH		0x02
@@ -437,9 +439,13 @@
 
 #define APRV2_IBASIC_CMD_DESTROY_SESSION		0x0001003C
 
-#define VSS_ISTREAM_CMD_SET_MUTE			0x00011022
+/*
+ * This command changes the mute setting. The new mute setting will
+ * be applied over the specified ramp duration.
+ */
+#define VSS_IVOLUME_CMD_MUTE_V2				0x0001138B
 
-#define VSS_ISTREAM_CMD_REGISTER_CALIBRATION_DATA	0x00011279
+#define VSS_ISTREAM_CMD_REGISTER_CALIBRATION_DATA_V2    0x00011369
 
 #define VSS_ISTREAM_CMD_DEREGISTER_CALIBRATION_DATA     0x0001127A
 
@@ -541,22 +547,33 @@
 	*/
 } __packed;
 
-struct vss_istream_cmd_set_mute_t {
+#define VSS_IVOLUME_DIRECTION_TX	0
+#define VSS_IVOLUME_DIRECTION_RX	1
+
+#define VSS_IVOLUME_MUTE_OFF		0
+#define VSS_IVOLUME_MUTE_ON		1
+
+#define DEFAULT_MUTE_RAMP_DURATION	500
+
+struct vss_ivolume_cmd_mute_v2_t {
 	uint16_t direction;
-	/**<
-	* 0 : TX only
-	* 1 : RX only
-	* 2 : TX and Rx
-	*/
+	/*
+	 * The direction field sets the direction to apply the mute command.
+	 * The Supported values:
+	 * VSS_IVOLUME_DIRECTION_TX
+	 * VSS_IVOLUME_DIRECTION_RX
+	 */
 	uint16_t mute_flag;
-	/**<
-	* Mute, un-mute.
-	*
-	* 0 : Silence disable
-	* 1 : Silence enable
-	* 2 : CNG enable. Applicable to TX only. If set on RX behavior
-	*     will be the same as 1
-	*/
+	/*
+	 * Turn mute on or off. The Supported values:
+	 * VSS_IVOLUME_MUTE_OFF
+	 * VSS_IVOLUME_MUTE_ON
+	 */
+	uint16_t ramp_duration_ms;
+	/*
+	 * Mute change ramp duration in milliseconds.
+	 * The Supported values: 0 to 5000.
+	 */
 } __packed;
 
 struct vss_istream_cmd_create_full_control_session_t {
@@ -666,14 +683,21 @@
 	 */
 } __packed;
 
-struct vss_istream_cmd_register_calibration_data_t {
-	uint32_t phys_addr;
-	/* Phsical address to be registered with stream. The calibration data
-	 *  is stored at this address.
-	 */
-	uint32_t mem_size;
+struct vss_istream_cmd_register_calibration_data_v2_t {
+	uint32_t cal_mem_handle;
+	/* Handle to the shared memory that holds the calibration data. */
+	uint64_t cal_mem_address;
+	/* Location of calibration data. */
+	uint32_t cal_mem_size;
 	/* Size of the calibration data in bytes. */
-};
+	uint8_t column_info[MAX_COL_INFO_SIZE];
+	/*
+	 * Column info contains the number of columns and the array of columns
+	 * in the calibration table. The order in which the columns are provided
+	 * here must match the order in which they exist in the calibration
+	 * table provided.
+	 */
+} __packed;
 
 struct vss_icommon_cmd_set_ui_property_enable_t {
 	uint32_t module_id;
@@ -705,7 +729,7 @@
 
 struct cvs_set_mute_cmd {
 	struct apr_hdr hdr;
-	struct vss_istream_cmd_set_mute_t cvs_set_mute;
+	struct vss_ivolume_cmd_mute_v2_t cvs_set_mute;
 } __packed;
 
 struct cvs_set_media_type_cmd {
@@ -740,7 +764,7 @@
 
 struct cvs_register_cal_data_cmd {
 	struct apr_hdr hdr;
-	struct vss_istream_cmd_register_calibration_data_t cvs_cal_data;
+	struct vss_istream_cmd_register_calibration_data_v2_t cvs_cal_data;
 } __packed;
 
 struct cvs_deregister_cal_data_cmd {
@@ -797,11 +821,24 @@
 #define VSS_IVOCPROC_CMD_DISABLE			0x000110E1
 /**< No payload. Wait for APRV2_IBASIC_RSP_RESULT response. */
 
-#define VSS_IVOCPROC_CMD_REGISTER_CALIBRATION_DATA	0x00011275
-#define VSS_IVOCPROC_CMD_DEREGISTER_CALIBRATION_DATA    0x00011276
+/*
+ * Registers the memory that contains device specific configuration data with
+ * the vocproc. The client must register device configuration data with the
+ * vocproc that corresponds with the device being set on the vocproc.
+ */
+#define VSS_IVOCPROC_CMD_REGISTER_DEVICE_CONFIG		0x00011371
 
-#define VSS_IVOCPROC_CMD_REGISTER_VOLUME_CAL_TABLE      0x00011277
-#define VSS_IVOCPROC_CMD_DEREGISTER_VOLUME_CAL_TABLE    0x00011278
+/*
+ * Deregisters the memory that holds device configuration data from the
+  vocproc.
+*/
+#define VSS_IVOCPROC_CMD_DEREGISTER_DEVICE_CONFIG	0x00011372
+
+#define VSS_IVOCPROC_CMD_REGISTER_CALIBRATION_DATA_V2	0x00011373
+#define VSS_IVOCPROC_CMD_DEREGISTER_CALIBRATION_DATA	0x00011276
+
+#define VSS_IVOCPROC_CMD_REGISTER_VOL_CALIBRATION_DATA	0x00011374
+#define VSS_IVOCPROC_CMD_DEREGISTER_VOL_CALIBRATION_DATA	0x00011375
 
 #define VSS_IVOCPROC_TOPOLOGY_ID_NONE			0x00010F70
 #define VSS_IVOCPROC_TOPOLOGY_ID_TX_SM_ECNS		0x00010F71
@@ -847,8 +884,6 @@
 #define VSS_MEDIA_ID_4GV_WB_MODEM	0x00010FC4
 /*CDMA EVRC-WB vocoder modem format */
 
-#define VSS_IVOCPROC_CMD_SET_MUTE			0x000110EF
-
 #define VOICE_CMD_SET_PARAM				0x00011006
 #define VOICE_CMD_GET_PARAM				0x00011007
 #define VOICE_EVT_GET_PARAM_ACK				0x00011008
@@ -941,39 +976,54 @@
 	 */
 } __packed;
 
-struct vss_ivocproc_cmd_register_calibration_data_t {
-	uint32_t phys_addr;
-	/* Phsical address to be registered with vocproc. Calibration data
-	 *  is stored at this address.
+struct vss_ivocproc_cmd_register_device_config_t {
+	uint32_t mem_handle;
+	/*
+	 * Handle to the shared memory that holds the per-network calibration
+	 * data.
 	 */
+	uint64_t mem_address;
+	/* Location of calibration data. */
 	uint32_t mem_size;
 	/* Size of the calibration data in bytes. */
 } __packed;
 
-struct vss_ivocproc_cmd_register_volume_cal_table_t {
-	uint32_t phys_addr;
-	/* Phsical address to be registered with the vocproc. The volume
-	 *  calibration table is stored at this location.
+struct vss_ivocproc_cmd_register_calibration_data_v2_t {
+	uint32_t cal_mem_handle;
+	/*
+	 * Handle to the shared memory that holds the per-network calibration
+	 * data.
 	 */
-
-	uint32_t mem_size;
-	/* Size of the volume calibration table in bytes. */
+	uint64_t cal_mem_address;
+	/* Location of calibration data. */
+	uint32_t cal_mem_size;
+	/* Size of the calibration data in bytes. */
+	uint8_t column_info[MAX_COL_INFO_SIZE];
+	/*
+	 * Column info contains the number of columns and the array of columns
+	 * in the calibration table. The order in which the columns are provided
+	 * here must match the order in which they exist in the calibration
+	 * table provided.
+	 */
 } __packed;
 
-struct vss_ivocproc_cmd_set_mute_t {
-	uint16_t direction;
+struct vss_ivocproc_cmd_register_volume_cal_data_t {
+	uint32_t cal_mem_handle;
 	/*
-	* 0 : TX only.
-	* 1 : RX only.
-	* 2 : TX and Rx.
-	*/
-	uint16_t mute_flag;
+	 * Handle to the shared memory that holds the volume calibration
+	 * data.
+	 */
+	uint64_t cal_mem_address;
+	/* Location of volume calibration data. */
+	uint32_t cal_mem_size;
+	/* Size of the volume calibration data in bytes. */
+	uint8_t column_info[MAX_COL_INFO_SIZE];
 	/*
-	* Mute, un-mute.
-	*
-	* 0 : Disable.
-	* 1 : Enable.
-	*/
+	 * Column info contains the number of columns and the array of columns
+	 * in the calibration table. The order in which the columns are provided
+	 * here must match the order in which they exist in the calibration
+	 * table provided.
+	 */
 } __packed;
 
 struct cvp_create_full_ctl_session_cmd {
@@ -999,27 +1049,36 @@
 	struct vss_ivocproc_cmd_set_volume_index_t cvp_set_vol_idx;
 } __packed;
 
+struct cvp_register_dev_cfg_cmd {
+	struct apr_hdr hdr;
+	struct vss_ivocproc_cmd_register_device_config_t cvp_dev_cfg_data;
+} __packed;
+
+struct cvp_deregister_dev_cfg_cmd {
+	struct apr_hdr hdr;
+} __packed;
+
 struct cvp_register_cal_data_cmd {
 	struct apr_hdr hdr;
-	struct vss_ivocproc_cmd_register_calibration_data_t cvp_cal_data;
+	struct vss_ivocproc_cmd_register_calibration_data_v2_t cvp_cal_data;
 } __packed;
 
 struct cvp_deregister_cal_data_cmd {
 	struct apr_hdr hdr;
 } __packed;
 
-struct cvp_register_vol_cal_table_cmd {
+struct cvp_register_vol_cal_data_cmd {
 	struct apr_hdr hdr;
-	struct vss_ivocproc_cmd_register_volume_cal_table_t cvp_vol_cal_tbl;
+	struct vss_ivocproc_cmd_register_volume_cal_data_t cvp_vol_cal_data;
 } __packed;
 
-struct cvp_deregister_vol_cal_table_cmd {
+struct cvp_deregister_vol_cal_data_cmd {
 	struct apr_hdr hdr;
 } __packed;
 
 struct cvp_set_mute_cmd {
 	struct apr_hdr hdr;
-	struct vss_ivocproc_cmd_set_mute_t cvp_set_mute;
+	struct vss_ivolume_cmd_mute_v2_t cvp_set_mute;
 } __packed;
 
 /* CB for up-link packets. */
@@ -1130,7 +1189,8 @@
 	/* APR to CVP in the Q6 */
 	void *apr_q6_cvp;
 
-	struct ion_client *client;
+	struct mem_map_table cal_mem_map_table;
+	uint32_t cal_mem_handle;
 	struct cal_mem cvp_cal;
 	struct cal_mem cvs_cal;
 
diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c
index f989b17..915c3c2 100644
--- a/sound/soc/soc-pcm.c
+++ b/sound/soc/soc-pcm.c
@@ -1496,9 +1496,6 @@
 	struct snd_soc_dpcm_params *dpcm_params;
 	int ret = 0;
 
-	if ((cmd == SNDRV_PCM_TRIGGER_PAUSE_RELEASE) ||
-				(cmd == SNDRV_PCM_TRIGGER_PAUSE_PUSH))
-		return ret;
 
 	list_for_each_entry(dpcm_params, &fe->dpcm[stream].be_clients, list_be) {
 
@@ -1767,6 +1764,7 @@
 		if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_PARAMS) &&
 		    (be->dpcm[stream].state != SND_SOC_DPCM_STATE_PREPARE) &&
 			(be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_FREE) &&
+		    (be->dpcm[stream].state != SND_SOC_DPCM_STATE_PAUSED) &&
 		    (be->dpcm[stream].state != SND_SOC_DPCM_STATE_STOP))
 			continue;
 
@@ -2582,6 +2580,7 @@
 		rtd->ops.silence	= platform->driver->ops->silence;
 		rtd->ops.page		= platform->driver->ops->page;
 		rtd->ops.mmap		= platform->driver->ops->mmap;
+		rtd->ops.restart	= platform->driver->ops->restart;
 	}
 
 	if (playback)