Merge "Documentation: block: ROW I/O scheduler"
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/DMA-attributes.txt b/Documentation/DMA-attributes.txt
index 5c72eed..d62d0c6 100644
--- a/Documentation/DMA-attributes.txt
+++ b/Documentation/DMA-attributes.txt
@@ -49,3 +49,30 @@
 consistent or non-consistent memory as it sees fit.  By using this API,
 you are guaranteeing to the platform that you have all the correct and
 necessary sync points for this memory in the driver.
+
+DMA_ATTR_NO_KERNEL_MAPPING
+--------------------------
+
+DMA_ATTR_NO_KERNEL_MAPPING lets the platform to avoid creating a kernel
+virtual mapping for the allocated buffer. On some architectures creating
+such mapping is non-trivial task and consumes very limited resources
+(like kernel virtual address space or dma consistent address space).
+Buffers allocated with this attribute can be only passed to user space
+by calling dma_mmap_attrs(). By using this API, you are guaranteeing
+that you won't dereference the pointer returned by dma_alloc_attr(). You
+can threat it as a cookie that must be passed to dma_mmap_attrs() and
+dma_free_attrs(). Make sure that both of these also get this attribute
+set on each call.
+
+Since it is optional for platforms to implement
+DMA_ATTR_NO_KERNEL_MAPPING, those that do not will simply ignore the
+attribute and exhibit default behavior.
+
+DMA_ATTR_STRONGLY_ORDERED
+-------------------------
+
+DMA_ATTR_STRONGLY_ORDERED allocates memory with a very restrictive type
+of mapping (no unaligned accesses, no re-ordering, no write merging, no
+buffering, no pre-fetching). This has severe performance penalties and
+should not be used for general purpose DMA allocations. It should only
+be used if one of the restrictions on strongly ordered memory is required.
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/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/rpm-stats.txt b/Documentation/devicetree/bindings/arm/msm/rpm-stats.txt
new file mode 100644
index 0000000..3137e91
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/msm/rpm-stats.txt
@@ -0,0 +1,22 @@
+* RPM Sleep Stats
+
+RPM maintains sleep data in the RPM RAM. A device tree node is added
+that will hold the address of the RPM RAM from where sleep stats are read.
+Additionally a version number is added to distinguish the type of data
+structure being read from the RAM.
+
+The required properties for rpm-stats are:
+
+- compatible: "qcom,rpm-stats"
+- reg: The address on the RPM RAM from where stats are read.
+- reg-names: Name given the register holding address.
+- qcom,sleep-stats-version: Version number.
+
+Example:
+
+qcom,rpm-stats@fc19dbd0 {
+		compatible = "qcom,rpm-stats";
+		reg = <0xfc19dbd0 0x1000>;
+		reg-names = "phys_addr_base";
+		qcom,sleep-stats-version = <2>;
+};
diff --git a/Documentation/devicetree/bindings/arm/msm/smem.txt b/Documentation/devicetree/bindings/arm/msm/smem.txt
new file mode 100644
index 0000000..a38984c
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/msm/smem.txt
@@ -0,0 +1,107 @@
+Qualcomm Shared Memory
+
+[Root level node]
+Required properties:
+-compatible : should be "qcom,smem"
+-reg : the location and size of smem, the irq register base memory, and
+	optionally any auxiliary smem areas
+-reg-names : "smem" - string to identify the shared memory region
+	     "irq-reg-base" - string to identify the irq register region
+	     "aux-mem1", "aux-mem2", "aux-mem3", ... - optional strings to
+			identify any auxiliary shared memory regions
+
+[Second level nodes]
+
+qcom,smd
+Required properties:
+-compatible : should be "qcom,smd"
+-qcom,smd-edge : the smd edge
+-qcom,smd-irq-offset : the offset into the irq register base memory for sending
+	interrupts
+-qcom,smd-irq-bitmask : the sending irq bitmask
+-interrupts : the receiving interrupt line
+
+Optional properties:
+-qcom,pil-string : the name to use when loading this edge
+-qcom,irq-no-suspend: configure the incoming irq line as active during suspend
+
+qcom,smsm
+Required properties:
+-compatible : should be "qcom,smsm"
+-qcom,smsm-edge : the smsm edge
+-qcom,smsm-irq-offset : the offset into the irq register base memory for sending
+	interrupts
+-qcom,smsm-irq-bitmask : the sending irq bitmask
+-interrupts : the receiving interrupt line
+
+
+Example:
+
+	qcom,smem@fa00000 {
+		compatible = "qcom,smem";
+		reg = <0xfa00000 0x200000>,
+			<0xfa006000 0x1000>,
+			<0xfc428000 0x4000>;
+		reg-names = "smem", "irq-reg-base", "aux-mem1";
+
+		qcom,smd-modem {
+			compatible = "qcom,smd";
+			qcom,smd-edge = <0>;
+			qcom,smd-irq-offset = <0x8>;
+			qcom,smd-irq-bitmask = <0x1000>;
+			qcom,pil-string = "modem";
+			interrupts = <0 25 1>;
+		};
+
+		qcom,smsm-modem {
+			compatible = "qcom,smsm";
+			qcom,smsm-edge = <0>;
+			qcom,smsm-irq-offset = <0x8>;
+			qcom,smsm-irq-bitmask = <0x2000>;
+			interrupts = <0 26 1>;
+		};
+
+		qcom,smd-adsp {
+			compatible = "qcom,smd";
+			qcom,smd-edge = <1>;
+			qcom,smd-irq-offset = <0x8>;
+			qcom,smd-irq-bitmask = <0x100>;
+			qcom,pil-string = "adsp";
+			interrupts = <0 156 1>;
+		};
+
+		qcom,smsm-adsp {
+			compatible = "qcom,smsm";
+			qcom,smsm-edge = <1>;
+			qcom,smsm-irq-offset = <0x8>;
+			qcom,smsm-irq-bitmask = <0x200>;
+			interrupts = <0 157 1>;
+		};
+
+		qcom,smd-wcnss {
+			compatible = "qcom,smd";
+			qcom,smd-edge = <6>;
+			qcom,smd-irq-offset = <0x8>;
+			qcom,smd-irq-bitmask = <0x20000>;
+			qcom,pil-string = "wcnss";
+			interrupts = <0 142 1>;
+		};
+
+		qcom,smsm-wcnss {
+			compatible = "qcom,smsm";
+			qcom,smsm-edge = <6>;
+			qcom,smsm-irq-offset = <0x8>;
+			qcom,smsm-irq-bitmask = <0x80000>;
+			interrupts = <0 144 1>;
+		};
+
+		qcom,smd-rpm {
+			compatible = "qcom,smd";
+			qcom,smd-edge = <15>;
+			qcom,smd-irq-offset = <0x8>;
+			qcom,smd-irq-bitmask = <0x1>;
+			interrupts = <0 168 1>;
+			qcom,irq-no-syspend;
+		};
+	};
+
diff --git a/Documentation/devicetree/bindings/fb/mdss-edp.txt b/Documentation/devicetree/bindings/fb/mdss-edp.txt
index 4fedc72..3c4e1d3 100644
--- a/Documentation/devicetree/bindings/fb/mdss-edp.txt
+++ b/Documentation/devicetree/bindings/fb/mdss-edp.txt
@@ -4,15 +4,20 @@
 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 to backlight driver.
-- 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.
+- 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 {
@@ -22,6 +27,8 @@
 		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/fb/msm-hdmi-tx.txt b/Documentation/devicetree/bindings/fb/msm-hdmi-tx.txt
index 70fea73..e777094 100644
--- a/Documentation/devicetree/bindings/fb/msm-hdmi-tx.txt
+++ b/Documentation/devicetree/bindings/fb/msm-hdmi-tx.txt
@@ -6,22 +6,32 @@
 - reg: offset and length of the register regions(s) for the device.
 - reg-names: a list of strings that map in order to the list of regs.
 
-- <supply-name>-supply: phandle to the regulator device tree node.
-- <compatible-name>-supply-names: a list of strings that map in order
+- hpd-5v-supply: phandle to the 5V regulator device tree node.
+- core-vdda-supply: phandle to the HDMI vdda regulator device tree node.
+- core-vcc-supply: phandle to the HDMI vcc regulator device tree node.
+- qcom,hdmi-tx-supply-names: a list of strings that map in order
   to the list of supplies.
-- <<compatible-name>-supply-type: a type of supply(ies) mentioned above.
+- qcom,hdmi-tx-supply-type: a type of supply(ies) mentioned above.
     0 = supply with controlled output
     1 = supply without controlled output. i.e. voltage switch
-- <compatible-name>-min-voltage-level: specifies minimum voltage level
+- qcom,hdmi-tx-min-voltage-level: specifies minimum voltage level
   of supply(ies) mentioned above.
-- <compatible-name>-max-voltage-level: specifies maximum voltage level
+- qcom,hdmi-tx-max-voltage-level: specifies maximum voltage level
   of supply(ies) mentioned above.
-- <compatible-name>-op-mode: specifies optimum operating mode of
+- qcom,hdmi-tx-op-mode: specifies optimum operating mode of
   supply(ies) mentioned above.
 
-- gpios: specifies gpios assigned for the device.
-- <compatible-name>-gpio-names: a list of strings that map in order to
-  the list of gpios
+- qcom,hdmi-tx-cec: gpio for Consumer Electronics Control (cec) line.
+- qcom,hdmi-tx-ddc-clk: gpio for Display Data Channel (ddc) clock line.
+- qcom,hdmi-tx-ddc-data: gpio for ddc data line.
+- qcom,hdmi-tx-hpd: gpio required for HDMI hot-plug detect.
+
+Optional properties:
+- qcom,hdmi-tx-mux-sel: gpio required to toggle HDMI output between
+  docking station, type A, and liquid device, type D, ports. Required
+  property for liquid devices.
+- qcom,hdmi-tx-mux-en: gpio required to enable mux for HDMI output
+  on liquid devices. Required property for liquid devices.
 
 Example:
 	qcom,hdmi_tx@fd922100 {
@@ -41,10 +51,8 @@
 		qcom,hdmi-tx-max-voltage-level = <0 1800000 1800000>;
 		qcom,hdmi-tx-op-mode = <0 1800000 0>;
 
-		gpios = <&msmgpio 31 0>,
-			<&msmgpio 32 0>,
-			<&msmgpio 33 0>,
-			<&msmgpio 34 0>;
-		qcom,hdmi-tx-gpio-names =
-			"cec-pin", "hpd-ddc-clk", "hpd-ddc-data", "hpd-pin";
+		qcom,hdmi-tx-cec = <&msmgpio 31 0>;
+		qcom,hdmi-tx-ddc-clk = <&msmgpio 32 0>;
+		qcom,hdmi-tx-ddc-data = <&msmgpio 33 0>;
+		qcom,hdmi-tx-hpd = <&msmgpio 34 0>;
 	};
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 38b2721..9164647 100644
--- a/Documentation/devicetree/bindings/gpu/adreno.txt
+++ b/Documentation/devicetree/bindings/gpu/adreno.txt
@@ -5,7 +5,12 @@
 Required properties:
 - label:		A string used as a descriptive name for the device.
 - compatible:		Must be "qcom,kgsl-3d0" and "qcom,kgsl-3d"
-- reg:			Specifies the base address and address size for this device.
+- reg:			Specifies the register base address and size. The second interval
+			specifies the shader memory base address and size.
+- reg-names:		Resource names used for the physical address of device registers
+			and shader memory. "kgsl_3d0_reg_memory" gives the physical address
+			and length of device registers while "kgsl_3d0_shader_memory" gives
+			physical address and length of device shader memory.
 - interrupts:		Interrupt mapping for GPU IRQ.
 - interrupt-names:	String property to describe the name of the interrupt.
 - qcom,id:		An integer used as an identification number for the device.
@@ -70,8 +75,9 @@
 	qcom,kgsl-3d0@fdb00000 {
 		label = "kgsl-3d0";
 		compatible = "qcom,kgsl-3d0", "qcom,kgsl-3d";
-		reg = <0xfdb00000 0x20000>;
-		reg-names = "kgsl_3d0_reg_memory";
+		reg = <0xfdb00000 0x10000
+		       0xfdb20000 0x10000>;
+		reg-names = "kgsl_3d0_reg_memory", "kgsl_3d0_shader_memory";
 		interrupts = <0 33 0>;
 		interrupt-names = "kgsl_3d0_irq";
 		qcom,id = <0>;
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/hwmon/qpnp-adc-current.txt b/Documentation/devicetree/bindings/hwmon/qpnp-adc-current.txt
index 33d5cc1..e458ea0 100644
--- a/Documentation/devicetree/bindings/hwmon/qpnp-adc-current.txt
+++ b/Documentation/devicetree/bindings/hwmon/qpnp-adc-current.txt
@@ -11,6 +11,7 @@
 - compatible : should be "qcom,qpnp-iadc" for Current ADC driver.
 - reg : offset and length of the PMIC Aribter register map.
 - interrupts : The USR bank peripheral IADC interrupt.
+- interrupt-names : Should contain "eoc-int-en-set".
 - qcom,adc-bit-resolution : Bit resolution of the ADC.
 - qcom,adc-vdd-reference : Voltage reference used by the ADC.
 - qcom,rsense : Internal rsense resistor used for current measurements.
@@ -84,6 +85,7 @@
                         compatible = "qcom,qpnp-iadc";
                         reg = <0x3200 0x100>;
                         interrupts = <0 0x36 0>;
+			interrupt-names = "eoc-int-en-set";
                         qcom,adc-bit-resolution = <16>;
                         qcom,adc-vdd-reference = <1800>;
 			qcom,rsense = <1500>;
diff --git a/Documentation/devicetree/bindings/hwmon/qpnp-adc-voltage.txt b/Documentation/devicetree/bindings/hwmon/qpnp-adc-voltage.txt
index d7d3ec2..e23605c 100644
--- a/Documentation/devicetree/bindings/hwmon/qpnp-adc-voltage.txt
+++ b/Documentation/devicetree/bindings/hwmon/qpnp-adc-voltage.txt
@@ -11,6 +11,7 @@
 - compatible : should be "qcom,qpnp-vadc" for Voltage ADC driver.
 - reg : offset and length of the PMIC Aribter register map.
 - interrupts : The USR bank peripheral VADC interrupt.
+- interrupt-names : Should contain "eoc-int-en-set".
 - qcom,adc-bit-resolution : Bit resolution of the ADC.
 - qcom,adc-vdd-reference : Voltage reference used by the ADC.
 
@@ -82,6 +83,7 @@
                         compatible = "qcom,qpnp-vadc";
                         reg = <0x3100 0x100>;
                         interrupts = <0x0 0x31 0x0>;
+			interrupt-names = "eoc-int-en-set";
                         qcom,adc-bit-resolution = <15>;
                         qcom,adc-vdd-reference = <1800>;
 
diff --git a/Documentation/devicetree/bindings/i2c/sii8334-i2c.txt b/Documentation/devicetree/bindings/i2c/sii8334-i2c.txt
new file mode 100644
index 0000000..ed45192
--- /dev/null
+++ b/Documentation/devicetree/bindings/i2c/sii8334-i2c.txt
@@ -0,0 +1,26 @@
+* Silicon Image-8334 MHL Tx
+
+Required properties:
+- compatible: must be "qcom,mhl-sii8334"
+- reg: i2c slave address
+- mhl-intr-gpio: MHL interrupt gpio coming out of sii8334
+- mhl-pwr-gpio: MHL power gpio required for power rails
+- mhl-rst-gpio: MHL reset gpio going into sii8334 for toggling reset pin
+- <supply-name>-supply: phandle to the regulator device tree node.
+
+Example:
+	i2c@f9967000 {
+		sii8334@72 {
+			compatible = "qcom,mhl-sii8334";
+			reg = <0x72>;
+			interrupt-parent = <&msmgpio>;
+			interrupts = <82 0x8>;
+			mhl-intr-gpio = <&msmgpio 82 0>;
+			mhl-pwr-gpio = <&msmgpio 12 0>;
+			mhl-rst-gpio = <&pm8941_mpps 8 0>;
+			avcc_18-supply = <&pm8941_l24>;
+			avcc_12-supply = <&pm8941_l2>;
+			smps3a-supply = <&pm8941_s3>;
+			vdda-supply = <&pm8941_l12>;
+		};
+	};
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/leds/leds-qpnp.txt b/Documentation/devicetree/bindings/leds/leds-qpnp.txt
index 51bf9e6..10732cf 100644
--- a/Documentation/devicetree/bindings/leds/leds-qpnp.txt
+++ b/Documentation/devicetree/bindings/leds/leds-qpnp.txt
@@ -4,54 +4,141 @@
 controlling LEDs that are part of PMIC on Qualcomm reference
 platforms. The PMIC is connected to Host processor via
 SPMI bus. This driver supports various LED modules such as
-WLED (white LED), RGB LED and flash LED. The first version of
-the driver supports WLED and other features are added in the
-next versions.
+WLED (white LED), RGB LED and flash LED.
 
 Required Properties:
 - 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.
 
 Optional properties for WLED:
 - qcom,num-strings: number of wled strings supported
-- qcom,ovp_val: over voltage protection threshold,
+- qcom,ovp-val: over voltage protection threshold,
 		follows enum wled_ovp_threshold
-- qcom,boost_curr_lim: boot currnet limit, follows enum wled_current_bost_limit
-- qcom,ctrl_delay_us: delay in activation of led
-- qcom,dig_mod_gen_en: digital module generator
-- qcom,cs_out_en: current sink output enable
-- qcom,op_fdbck: selection of output as feedback for the boost
-- qcom,cp_select: high pole capacitance
+- qcom,boost-curr-lim: boot currnet limit, follows enum wled_current_bost_limit
+- qcom,ctrl-delay-us: delay in activation of led
+- qcom,dig-mod-gen-en: digital module generator
+- qcom,cs-out-en: current sink output enable
+- qcom,op-fdbck: selection of output as feedback for the boost
+- qcom,cp-select: high pole capacitance
+- 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"
+
+RGB Led is a tri-colored led, Red, Blue & Green.
+
+Required properties for RGB led:
+- qcom,mode: mode the led should operate in, options 0 = PWM, 1 = LPG
+- qcom,pwm-channel: pwm channel the led will operate on
+- qcom,pwm-us: time the pwm device will modulate at (us)
+
+Required properties for LPG mode only:
+- qcom,duty-ms: duty cycle time the led will operate at (ms)
+- qcom,duty-pcts: array of values for duty cycle to go through
+- qcom,start-idx: starting point duty-pcts array
+
+Optional properties for RGB led:
 - 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@d000 {
+		status = "okay";
+		qcom,rgb_pwm {
+			label = "rgb";
+			linux,name = "led:rgb_red";
+			qcom,mode = <0>;
+			qcom,pwm-channel = <6>;
+			qcom,pwm-us = <1000>;
+			qcom,duty-ms = <20>;
+			qcom,start-idx = <1>;
+			qcom,idx-len = <10>;
+			qcom,duty-pcts = [00 19 32 4B 64
+					 64 4B 32 19 00];
+			qcom,max-current = <12>;
+			qcom,default-state = "off";
+			qcom,id = <3>;
+			linux,default-trigger =
+				"battery-charging";
+		};
+
+		qcom,rgb_lpg {
+			label = "rgb";
+			linux,name = "led:rgb_blue";
+			qcom,mode = <1>;
+			qcom,pwm-channel = <4>;
+			qcom,pwm-us = <1000>;
+			qcom,duty-ms = <20>;
+			qcom,start-idx = <1>;
+			qcom,idx-len = <10>;
+			qcom,duty-pcts = [00 19 32 4B 64
+					 64 4B 32 19 00];
+			qcom,max-current = <12>;
+			qcom,default-state = "off";
+			qcom,id = <5>;
+			linux,default-trigger = "none";
+		};
+	};
+
+	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>;
+			};
+	};
+
 	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";
+			};
+	};
diff --git a/Documentation/devicetree/bindings/media/video/msm-camera-flash.txt b/Documentation/devicetree/bindings/media/video/msm-camera-flash.txt
new file mode 100644
index 0000000..72b32be
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/video/msm-camera-flash.txt
@@ -0,0 +1,23 @@
+* Qualcomm MSM CAMERA FLASH
+
+Required properties:
+- cell-index : Should contain flash source index to diffentiate
+    between different flash devices. These indexes represent flash devices
+    for multiple sensors.
+    - 0, 1, 2, 3
+- compatible :
+    - "qcom,camera-led-flash"
+- qcom,flash-type : Should contain type flash device
+    - 1 for LED flash
+    - 2 for strobe flash
+- qcom,flash-source : Should contain array of phandles to flash source nodes.
+    - pm8941_flash0 pm8941_flash1
+
+Example:
+
+qcom,camera-led-flash {
+	cell-index = <0>;
+	compatible = "qcom,camera-led-flash";
+	qcom,flash-type = <1>;
+	qcom,flash-source = <&pm8941_flash0 &pm8941_flash1>;
+};
diff --git a/Documentation/devicetree/bindings/media/video/msm-cci.txt b/Documentation/devicetree/bindings/media/video/msm-cci.txt
index 6b03fab..e256265 100644
--- a/Documentation/devicetree/bindings/media/video/msm-cci.txt
+++ b/Documentation/devicetree/bindings/media/video/msm-cci.txt
@@ -62,9 +62,9 @@
     - 1 -> yuv format
 
 Optional properties:
-- qcom,flash-type : should contain flash type if flash is supported for this
-    sensor
-    - 0 if flash is not supported, 1 if flash is supported
+- qcom,flash-src-index : should contain phandle to flash source node if flash
+    is supported for this sensor
+    - led_flash0, led_flash1
 - qcom,mount-angle : should contain the physical mount angle of the sensor on
     the target
     - 0, 90, 180, 360
diff --git a/Documentation/devicetree/bindings/misc/ti_drv2667.txt b/Documentation/devicetree/bindings/misc/ti_drv2667.txt
new file mode 100644
index 0000000..3a8f4b3
--- /dev/null
+++ b/Documentation/devicetree/bindings/misc/ti_drv2667.txt
@@ -0,0 +1,44 @@
+TI DRV2667 is a haptic controller chip. It can drive piezo haptics
+and can operate in two modes - analog and digital.
+
+Required properties:
+
+-compatible	: should be "ti,drv2667".
+-reg		: i2c address to be used.
+-vdd-supply	: regulator to power the chip.
+-vdd-i2c-supply	: regulator to power i2c bus.
+
+Optional properties:
+
+-ti,label		: Name to be registered with timedoutput class.
+-ti,mode		: Mode to be supported, 0 to 3 - FIFO, RAM, WAVE and ANALOG.
+-ti,wav-seq		: Wave Sequence composed of 11 bytes - wave form id,
+				Header size, start upper byte, start lower byte,
+				stop upper byte, stop lower byte, repeat count,
+				amplitude, frequency, duration and envelope
+-ti,gain		: Gain to be programmed for the chip.
+-ti,idle-timeout-ms	: Idle timeout in ms to be programmed for the chip to go into
+				low power mode after finishing its operation.
+-ti,max-runtime-ms	: Maximum time in ms for which chip can drive haptics.
+
+Example:
+	i2c@f9967000 {
+		ti-drv2667@59 {
+			compatible = "ti,drv2667";
+			reg = <0x59>;
+			vdd-supply = <&drv2667_vreg>;
+			vdd-i2c-supply = <&pm8941_s3>;
+			ti,label = "vibrator";
+			ti,gain = <3>;
+			ti,idle-timeout-ms = <20>;
+			ti,max-runtime-ms = <15000>;
+			ti,mode = <2>;
+			ti,wav-seq = [
+				/* wave form id */
+				01
+				/* header size, start and stop bytes */
+				05 80 06 00 09
+				/* repeat, amp, freq, duration, envelope */
+				01 ff 19 02 00];
+		};
+	};
diff --git a/Documentation/devicetree/bindings/mmc/msm_sdcc.txt b/Documentation/devicetree/bindings/mmc/msm_sdcc.txt
index 5f7651a..46173a0 100644
--- a/Documentation/devicetree/bindings/mmc/msm_sdcc.txt
+++ b/Documentation/devicetree/bindings/mmc/msm_sdcc.txt
@@ -10,42 +10,56 @@
   "reg-names" examples are "core_mem", "dml_mem" and "bam_mem"
   - interrupts : should contain SDCC core interrupt.
   - interrupt-names : indicates interrupts passed to driver (via interrupts property) by name.
-  "interrupt-names" examples are "core_irq", "bam_irq" and "status_irq"
-  - qcom,sdcc-clk-rates : specifies supported SDCC clock frequencies, Units - Hz.
-  - qcom,sdcc-sup-voltages: specifies supported voltage ranges for card. Should always be
+  "core_irq" is mandatory, "bam_irq" is mandatory only when BAM DMA engine is used,
+  "status_irq" and "sdiowakeup_irq" are optional.
+  - qcom,clk-rates : specifies supported SDCC clock frequencies, Units - Hz.
+  - qcom,sup-voltages: specifies supported voltage ranges for card. Should always be
 			specified in pairs (min, max), Units - mV.
   - <supply-name>-supply: phandle to the regulator device tree node
   "supply-name" examples are "vdd", "vdd-io".
 
 Optional Properties:
 	- cell-index - defines slot ID.
-	- qcom,sdcc-bus-width - defines the bus I/O width that controller supports.
+	- qcom,bus-width - defines the bus I/O width that controller supports.
 	- wp-gpios - specify GPIO for write protect switch detection.
 	- cd-gpios - specify GPIO for card detection.
-	- qcom,sdcc-nonremovable - specifies whether the card in slot is
+	- qcom,nonremovable - specifies whether the card in slot is
 				hot pluggable or hard wired.
-	- qcom,sdcc-disable_cmd23 - disable sending CMD23 to card when controller can't support it.
-	- qcom,sdcc-bus-speed-mode - specifies supported bus speed modes by host.
-	- qcom,sdcc-current-limit - specifies max. current the host can drive.
-	- qcom,sdcc-xpc - specifies if the host can supply more than 150mA for SDXC cards.
+	- qcom,disable-cmd23 - disable sending CMD23 to card when controller can't support it.
+	- qcom,bus-speed-mode - specifies supported bus speed modes by host.
+	- qcom,current-limit - specifies max. current the host can drive.
+	- qcom,xpc - specifies if the host can supply more than 150mA for SDXC cards.
+	- qcom,dat1-mpm-int - specifies MPM interrupt number corresponding to DAT1 line of SDCC
+					(used only if slot has dedicated DAT1 MSM pin (not GPIO))
 
 In the following, <supply> can be vdd (flash core voltage) or vdd-io (I/O voltage).
-	- qcom,sdcc-<supply>-always_on - specifies whether supply should be kept "on" always.
-	- qcom,sdcc-<supply>-lpm_sup - specifies whether supply can be kept in low power mode (lpm).
-	- qcom,sdcc-<supply>-voltage_level - specifies voltage levels for supply. Should be
+	- qcom,<supply>-always-on - specifies whether supply should be kept "on" always.
+	- qcom,<supply>-lpm-sup - specifies whether supply can be kept in low power mode (lpm).
+	- qcom,<supply>-voltage-level - specifies voltage levels for supply. Should be
 	specified in pairs (min, max), units uV.
-	- qcom,sdcc-<supply>-current_level - specifies load levels for supply in lpm or
+	- qcom,<supply>-current-level - specifies load levels for supply in lpm or
 	high power mode (hpm). Should be specified in pairs (lpm, hpm), units uA.
 
 	- gpios - specifies gpios assigned for sdcc slot.
-	- qcom,sdcc-gpio-names -  a list of strings that map in order to the list of gpios
+	- qcom,gpio-names -  a list of strings that map in order to the list of gpios
 	A slot has either gpios or dedicated tlmm pins as represented below.
-	- qcom,sdcc-pad-pull-on - Active pull configuration for sdc tlmm pins
-	- qcom,sdcc-pad-pull-off - Suspend pull configuration for sdc tlmm pins.
-	- qcom,sdcc-pad-drv-on - Active drive strength configuration for sdc tlmm pins.
-	- qcom,sdcc-pad-drv-off - Suspend drive strength configuration for sdc tlmm pins.
+	- qcom,pad-pull-on - Active pull configuration for sdc tlmm pins
+	- qcom,pad-pull-off - Suspend pull configuration for sdc tlmm pins.
+	- qcom,pad-drv-on - Active drive strength configuration for sdc tlmm pins.
+	- qcom,pad-drv-off - Suspend drive strength configuration for sdc tlmm pins.
 	Tlmm pins are specified as <clk cmd data>
 
+	- qcom,bus-bw-vectors-bps - specifies array of throughput values in Bytes/sec. The
+	values in the array are determined according to supported bus speed modes. For example,
+	if host supports SDR12 mode, value is 13631488 Bytes/sec.
+	- Refer to "Documentation/devicetree/bindings/arm/msm/msm_bus.txt" for
+	below optional properties:
+		- qcom,msm-bus,name
+		- qcom,msm-bus,num-cases
+		- qcom,msm-bus,active-only
+		- qcom,msm-bus,num-paths
+		- qcom,msm-bus,vectors-KBps
+
 Example:
 
 	qcom,sdcc@f9600000 {
@@ -57,8 +71,21 @@
 		0xf9602000 0x2000> // BAM register interface
 
 	interrupts = <123>;
-	qcom,sdcc-clk-rates = <400000 24000000 48000000>;
-	qcom,sdcc-sup-voltages = <2700 3300>;
-	qcom,sdcc-bus-width = <8>; //8-bit wide
-	qcom,sdcc-nonremovable;
+	qcom,clk-rates = <400000 24000000 48000000>;
+	qcom,sup-voltages = <2700 3300>;
+	qcom,bus-width = <8>; //8-bit wide
+	qcom,nonremovable;
+
+	qcom,msm-bus,name = "sdcc2";
+	qcom,msm-bus,num-cases = <7>;
+	qcom,msm-bus,active-only = <0>;
+	qcom,msm-bus,num-paths = <1>;
+	qcom,msm-bus,vectors-KBps = <81 512 0 0>, /* No vote */
+			<81 512 6656 13312>, /* 13 MB/s*/
+			<81 512 13312 26624>, /* 26 MB/s */
+			<81 512 26624 53248>, /* 52 MB/s */
+			<81 512 53248 106496>, /* 104 MB/s */
+			<81 512 106496 212992>, /* 208 MB/s */
+			<81 512 2147483647 4294967295>; /* Max. bandwidth */
+	qcom,bus-bw-vectors-bps = <0 13631488 27262976 54525952 109051904 218103808 4294967295>;
 };
diff --git a/Documentation/devicetree/bindings/net/wireless/ath/ath6kl.txt b/Documentation/devicetree/bindings/net/wireless/ath/ath6kl.txt
new file mode 100644
index 0000000..7b9feae
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/wireless/ath/ath6kl.txt
@@ -0,0 +1,24 @@
+* Qualcomm Atheros mobile chipsets
+
+Required properties:
+    - compatible: Can be "qca,ar6004-sdio" for SDIO device and
+    "qca,ar6004-hsic" for HSIC devcie.
+    - qca,chip-pwd-l-gpios: specify GPIO for CHIP_PWD_L.
+
+Optional Properties:
+    - cell-index: WLAN Hardware index.
+    - qca,pm-enable-gpios: Specify this GPIO if internal PMU needs to be used.
+    - qca,ar6004-vbatt-supply: Specify this if VBATT is provided through a
+    regulator.
+    - qca,ar6004-vdd-io-supply: Specify this if VDD-IO is provided through a
+    regulator.
+
+Example:
+
+	wlan0: qca,wlan {
+		cell-index = <0>;
+		compatible = "qca,ar6004-sdio";
+		qca,chip-pwd-l-gpios = <&msmgpio 62 0>;
+		qca,pm-enable-gpios = <&pm8019_gpios 3 0x0>;
+		qca,ar6004-vdd-io-supply = <&pm8019_l11>;
+	};
diff --git a/Documentation/devicetree/bindings/pil/pil-mba.txt b/Documentation/devicetree/bindings/pil/pil-mba.txt
deleted file mode 100644
index ce6bb8f..0000000
--- a/Documentation/devicetree/bindings/pil/pil-mba.txt
+++ /dev/null
@@ -1,32 +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.
-- interrupts:         The modem watchdog interrupt
-- 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";
-		interrupts = <0 56 1>;
-
-		qcom,firmware-name = "modem";
-		qcom,depends-on    = "mba";
-	};
diff --git a/Documentation/devicetree/bindings/pil/pil-q6v5-mss.txt b/Documentation/devicetree/bindings/pil/pil-q6v5-mss.txt
index 41ffd8a..802716c 100644
--- a/Documentation/devicetree/bindings/pil/pil-q6v5-mss.txt
+++ b/Documentation/devicetree/bindings/pil/pil-q6v5-mss.txt
@@ -10,13 +10,15 @@
 - 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
 		      images and self-authentication is not desired;
 		      <1> if the hardware requires self-authenticating images.
+- qcom,is-loadable:   if PIL is required to load the modem image
 
 Example:
 	qcom,mss@fc880000 {
@@ -25,11 +27,13 @@
 		      <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,is-loadable;
 		qcom,firmware-name = "mba";
 		qcom,pil-self-auth = <1>;
 	};
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/ipa.txt b/Documentation/devicetree/bindings/platform/msm/ipa.txt
new file mode 100644
index 0000000..86c60e8
--- /dev/null
+++ b/Documentation/devicetree/bindings/platform/msm/ipa.txt
@@ -0,0 +1,81 @@
+Qualcomm Internet Packet Accelerator
+
+Internet Packet Accelerator (IPA) is a programmable protocol
+processor HW block. It is designed to support generic HW processing
+of UL/DL IP packets for various use cases independent of radio technology.
+
+Required properties:
+
+IPA node:
+
+- compatible : "qcom,ipa"
+- reg: Specifies the base physical addresses and the sizes of the IPA
+       registers.
+- reg-names: "ipa-base" - string to identify the IPA CORE base registers.
+	     "bam-base" - string to identify the IPA BAM base registers.
+- interrupts: Specifies the interrupt associated with IPA.
+- interrupt-names: "ipa-irq" - string to identify the IPA core interrupt.
+                   "bam-irq" - string to identify the IPA BAM interrupt.
+
+IPA pipe sub nodes (A2 static pipes configurations):
+
+-label: two labels are supported, a2-to-ipa and ipa-to-a2 which
+supply static configuration for A2-IPA connection.
+-qcom,src-bam-physical-address: The physical address of the source BAM
+-qcom,ipa-bam-mem-type:The memory type:
+                       0(Pipe memory), 1(Private memory), 2(System memory)
+-qcom,src-bam-pipe-index: Source pipe index
+-qcom,dst-bam-physical-address: The physical address of the
+                                destination BAM
+-qcom,dst-bam-pipe-index: Destination pipe index
+-qcom,data-fifo-offset: Data fifo base offset
+-qcom,data-fifo-size:  Data fifo size (bytes)
+-qcom,descriptor-fifo-offset: Descriptor fifo base offset
+-qcom,descriptor-fifo-size: Descriptor fifo size (bytes)
+
+Optional properties:
+-qcom,ipa-pipe-mem: Specifies the base physical address and the
+                    size of the IPA pipe memory region.
+                    Pipe memory is a feature which may be supported by the
+                    target (HW platform). The Driver support using pipe
+                    memory instead of system memory. In case this property
+                    will not appear in the IPA DTS entry, the driver will
+                    use system memory.
+
+Example:
+
+qcom,ipa@fd4c0000 {
+	compatible = "qcom,ipa";
+	reg = <0xfd4c0000 0x26000>,
+	      <0xfd4c4000 0x14818>;
+	reg-names = "ipa-base", "bam-base";
+	interrupts = <0 252 0>,
+	             <0 253 0>;
+	interrupt-names = "ipa-irq", "bam-irq";
+
+	qcom,pipe1 {
+		label = "a2-to-ipa";
+		qcom,src-bam-physical-address = <0xfc834000>;
+		qcom,ipa-bam-mem-type = <0>;
+		qcom,src-bam-pipe-index = <1>;
+		qcom,dst-bam-physical-address = <0xfd4c0000>;
+		qcom,dst-bam-pipe-index = <6>;
+		qcom,data-fifo-offset = <0x1000>;
+		qcom,data-fifo-size = <0xd00>;
+		qcom,descriptor-fifo-offset = <0x1d00>;
+		qcom,descriptor-fifo-size = <0x300>;
+	};
+
+	qcom,pipe2 {
+		label = "ipa-to-a2";
+		qcom,src-bam-physical-address = <0xfd4c0000>;
+		qcom,ipa-bam-mem-type = <0>;
+		qcom,src-bam-pipe-index = <7>;
+		qcom,dst-bam-physical-address = <0xfc834000>;
+		qcom,dst-bam-pipe-index = <0>;
+		qcom,data-fifo-offset = <0x00>;
+		qcom,data-fifo-size = <0xd00>;
+		qcom,descriptor-fifo-offset = <0xd00>;
+		qcom,descriptor-fifo-size = <0x300>;
+	};
+};
diff --git a/Documentation/devicetree/bindings/power/bq28400-battery.txt b/Documentation/devicetree/bindings/power/bq28400-battery.txt
new file mode 100644
index 0000000..1460d70
--- /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" or "ti,bq30z55-battery"
+- reg:		I2C Address must be 0xb.
+
+Example:
+	i2c@f9967000 {
+		battery@b {
+			compatible = "ti,bq28400-battery", "ti,bq30z55-battery";
+			reg = <0xb>;
+		};
+	};
+
diff --git a/Documentation/devicetree/bindings/power/qpnp-bms.txt b/Documentation/devicetree/bindings/power/qpnp-bms.txt
index 1d3c2db..4d571eb 100644
--- a/Documentation/devicetree/bindings/power/qpnp-bms.txt
+++ b/Documentation/devicetree/bindings/power/qpnp-bms.txt
@@ -36,12 +36,34 @@
 - qcom,bms-adjust-soc-low-threshold : The low threshold for the "flat portion"
 			of the charging curve. The BMS will not adjust SoC
 			based on voltage during this time.
-- qcom,bms-adjust-soc-high-threshold :  The high threshold for the "flat
+- qcom,bms-adjust-soc-high-threshold : The high threshold for the "flat
 			portion" of the charging curve. The BMS will not
 			adjust SoC based on voltage during this time.
+- qcom,bms-low-soc-calculate-soc-threshold : The SoC threshold for when
+			the period calculate_soc work speeds up. This ensures
+			SoC is updated in userspace constantly when we are near
+			shutdown.
+- qcom,bms-low-soc-calculate-soc-ms : The time period between subsequent
+			SoC recalculations when the current SoC is below
+			qcom,bms-low-soc-calculate-soc-threshold.
+- qcom,bms-soc-calculate-soc-ms : The time period between subsequent SoC
+			recalculations when the current SoC is above or equal
+			qcom,bms-low-soc-calculate-soc-threshold.
 - qcom,bms-chg-term-ua : current in micro-amps when charging is considered done.
 			As soon as current passes this point, charging is
 			stopped.
+- qcom,bms-batt-type: Type of battery used. This is an integer that corresponds
+			to the enum defined in
+			include/linux/mfd/pm8xxx/batterydata-lib.h
+
+Optional properties:
+- qcom,bms-ignore-shutdown-soc: A boolean that controls whether BMS will
+			try to force the startup SoC to be the same as the
+			shutdown SoC. Defining it will make BMS ignore the
+			shutdown SoC.
+- qcom,bms-use-voltage-soc : A boolean that controls whether BMS will use
+			voltage-based SoC instead of a coulomb counter based
+			one. Voltage-based SoC will not guarantee linearity.
 
 Example:
 	bms@4000 {
@@ -76,5 +98,10 @@
 		qcom,bms-shutdown-soc-valid-limit = <20>;
 		qcom,bms-adjust-soc-low-threshold = <25>;
 		qcom,bms-adjust-soc-high-threshold = <45>;
+		qcom,bms-low-soc-calculate-soc-threshold = <15>;
+		qcom,bms-low-soc-calculate-soc-ms = <5000>;
+		qcom,bms-calculate-soc-ms = <20000>;
 		qcom,bms-chg-term-ua = <100000>;
+		qcom,bms-batt-type = <0>;
+		qcom,bms-ignore-shutdown-soc;
 	};
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/smb137c-charger.txt b/Documentation/devicetree/bindings/power/smb137c-charger.txt
new file mode 100644
index 0000000..c0c4333
--- /dev/null
+++ b/Documentation/devicetree/bindings/power/smb137c-charger.txt
@@ -0,0 +1,86 @@
+Summit SMB137C Battery Charger
+
+The SMB137C is controlled via an I2C bus.  Its 7-bit I2C slave address is
+programmed during manufacturing.
+
+Required properties:
+- compatible:		Must be "summit,smb137c".
+- reg:			The device's 7-bit I2C address.
+
+Optional properties:
+- summit,chg-current-ma		Maximum battery charging current in milliamps.
+				Supported values are: 500, 650, 750, 850, 950,
+				1100, 1300, and 1500.
+- summit,term-current-ma	Charging terminaton current in milliamps.
+				Supported values are: 0, 35, 50, 100, and 150.
+				A value of 0 means no termination current is
+				used.
+- summit,pre-chg-current-ma	Maximum battery pre-charging current in
+				milliamps.  This current limit is applied while
+				the battery voltage is below the pre-charge /
+				fast-charge threshold.  Supported values are:
+				50, 100, 150, and 200.
+- summit,float-voltage-mv	Battery voltage threshold in millivolts at which
+				point charging switches from constant current to
+				constant voltage.  Supported values are: 3460 up
+				through 4730 in 10 mV steps.
+- summit,thresh-voltage-mv	Threshold voltage in millivolts which is used to
+				switch between pre-charge and fast-charge
+				current limits.  Supported values are: 2400 up
+				to 3100 in 100 mV steps.
+- summit,recharge-thresh-mv	Specifies the minimum voltage drop in millivolts
+				below the float voltage that is required in
+				order to initiate a new charging cycle.
+				Supported values are: 75 and 120.
+- summit,system-voltage-mv	Regulated voltage output on the VOUTL pin in
+				millivolts.  Supported values are 4250 and 4460.
+- summit,charging-timeout	Maximum duration in minutes that a single charge
+				cycle may last.  Supported values are: 0, 382,
+				764, and 1527.  A value of 0 means that no
+				charge cycle timeout is used and charging can
+				continue indefinitely.
+- summit,pre-charge-timeout	Maximum time in minutes spent in the pre-charge
+				state in any given charge cycle.  Supports
+				values are: 0, 48, 95, and 191.  A value of 0
+				means that there is no limit to the amount of
+				time that may be spent in the pre-charge state.
+- summit,therm-current-ua	Thermistor current in microamps to be used for
+				battery temperature monitoring.  Supported
+				values are 10, 20, 40, and 100.  These values
+				correspond to 100, 50, 25, and 10 kohm NTC
+				thermistors respectively.
+- summit,temperature-min	Specifies the minimum temperature at which
+				charging is allowed.  Supported values are
+				0 to 7.  These values correspond to -20 C to
+				+15 C in 5 C increments for an NTC thermistor
+				with beta = 4400.
+- summit,temperature-max	Specifies the maximum temperature at which
+				charging is allowed.  Supported values are
+				0 to 7.  These values correspond to +30 C to
+				+65 C in 5 C increments for an NTC thermistor
+				with beta = 4400.
+
+Note: If an optional property is not specified, then the hardware default value
+will be used.
+
+Example:
+/ {
+	i2c@f9925000 {
+		charger@57 {
+			compatible = "summit,smb137c";
+			reg = <0x57>;
+			summit,chg-current-ma = <1500>;
+			summit,term-current-ma = <50>;
+			summit,pre-chg-current-ma = <100>;
+			summit,float-voltage-mv = <4200>;
+			summit,thresh-voltage-mv = <3000>;
+			summit,recharge-thresh-mv = <75>;
+			summit,system-voltage-mv = <4250>;
+			summit,charging-timeout = <382>;
+			summit,pre-charge-timeout = <48>;
+			summit,therm-current-ua = <10>;
+			summit,temperature-min = <4>; /*  0 C */
+			summit,temperature-max = <3>; /* 45 C */
+		};
+	};
+};
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/prng/msm-rng.txt b/Documentation/devicetree/bindings/prng/msm-rng.txt
index 3d55808..28dfe50 100644
--- a/Documentation/devicetree/bindings/prng/msm-rng.txt
+++ b/Documentation/devicetree/bindings/prng/msm-rng.txt
@@ -4,9 +4,13 @@
 - compatible : Should be "qcom,msm-rng"
 - reg        : Offset and length of the register set for the device
 
+Optional property:
+- qcom,msm-rng-iface-clk : If the device uses iface-clk.
+
 Example:
 
-        qcom,msm-rng@f9bff000 {
-                              compatible = "qcom,msm-rng";
-                              reg = <0xf9bff000 0x200>;
-        };
+	qcom,msm-rng@f9bff000 {
+		compatible = "qcom,msm-rng";
+		reg = <0xf9bff000 0x200>;
+		qcom,msm-rng-iface-clk;
+	};
diff --git a/Documentation/devicetree/bindings/qseecom/qseecom.txt b/Documentation/devicetree/bindings/qseecom/qseecom.txt
index 5e7c42a..43033a8 100644
--- a/Documentation/devicetree/bindings/qseecom/qseecom.txt
+++ b/Documentation/devicetree/bindings/qseecom/qseecom.txt
@@ -2,6 +2,7 @@
 
 Required properties:
 - compatible : Should be "qcom,qseecom"
+- reg : should contain memory region address reserved for loading secure apps.
 - 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
@@ -10,6 +11,8 @@
 Example:
 	qcom,qseecom@fe806000 {
 		compatible = "qcom,qseecom";
+		reg = <0x7f00000 0x500000>;
+		reg-names = "secapp-region";
 		qcom,msm_bus,name = "qseecom-noc";
 		qcom,msm_bus,num_cases = <4>;
 		qcom,msm_bus,active_only = <0>;
diff --git a/Documentation/devicetree/bindings/regulator/krait-regulator.txt b/Documentation/devicetree/bindings/regulator/krait-regulator.txt
new file mode 100644
index 0000000..fddae80
--- /dev/null
+++ b/Documentation/devicetree/bindings/regulator/krait-regulator.txt
@@ -0,0 +1,27 @@
+Krait Voltage regulators
+
+Required properties:
+- compatible:			Must be "qcom,krait-regulator"
+- reg:				Specifies the address and size for this regulator device
+- qcom,headroom-voltage:	The minimum required voltage drop between the input
+			 	voltage and the output voltage for the LDO to be
+			 	operational, in microvolts
+- qcom,retention-voltage:	The value for retention voltage in microvolts
+- qcom,ldo-default-voltage:	The default value for LDO voltage in microvolts
+- qcom,ldo-threshold-voltage:	The voltage value above which LDO is nonfunctional
+
+Any property defined as part of the core regulator
+binding, defined in regulator.txt, can also be used.
+
+Example:
+	krait0_vreg: regulator@f9088000 {
+		compatible = "qcom,krait-regulator";
+		regulator-name = "krait0";
+		reg = <0xf9088000 0x1000>;
+		regulator-min-microvolt = <500000>;
+		regulator-max-microvolt = <1100000>;
+		qcom,headroom-voltage = <150000>;
+		qcom,retention-voltage = <745000>;
+		qcom,ldo-default-voltage = <745000>;
+		qcom,ldo-threshold-voltage = <750000>;
+	};
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/slimbus/slim-msm-ctrl.txt b/Documentation/devicetree/bindings/slimbus/slim-msm-ctrl.txt
index ecac09d..6b090fa 100644
--- a/Documentation/devicetree/bindings/slimbus/slim-msm-ctrl.txt
+++ b/Documentation/devicetree/bindings/slimbus/slim-msm-ctrl.txt
@@ -1,4 +1,19 @@
 Qualcomm SLIMBUS controller
+Qualcomm implements 2 type of slimbus controllers:
+1. "qcom,slim-msm": This controller is used if applications processor
+	driver is controlling slimbus master component. This driver is
+	responsible for communicating with slave HW directly using
+	messaging interface, and doing data channel management. Driver
+	also communicates with satellite component (driver implemented
+	by other execution environment, such as ADSP) to get its
+	requirements for data channel and bandwidth requirements.
+2. "qcom,slim-ngd": This controller is used if applications processor
+	driver is controlling slimbus satellite component (also known as
+	Non-ported Generic Device, or NGD). This is light-weight slimbus
+	controller responsible for communicating with slave HW directly
+	over bus messaging interface, and communicating with master component
+	(driver residing on other execution environment, such as ADSP)
+	for bandwidth and data channel management.
 
 Required properties:
 
@@ -8,7 +23,8 @@
 	 "slimbus_physical": Physical adderss of controller register blocks
 	 "slimbus_bam_physical": Physical address of Bus Access Module (BAM)
 				 for this controller
- - compatible : should be "qcom,slim-msm"
+ - compatible : should be "qcom,slim-msm" if this is master component driver
+ - compatible : should be "qcom,slim-ngd" if this is satellite component driver
  - cell-index : SLIMBUS number used for this controller
  - interrupts : Interrupt numbers used by this controller
  - interrupt-names : Required interrupt resource entries are:
diff --git a/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt b/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt
index 9743d0d..c47d442 100644
--- a/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt
+++ b/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt
@@ -72,6 +72,11 @@
 
  - compatible : "msm-dai-q6"
 
+Optional properties:
+
+ - qcom,ext-spk-amp-supply : External speaker amplifier power supply.
+ - qcom,ext-spk-amp-gpio : External speaker amplifier enable signal.
+
 [Second Level Nodes]
 
 Required properties:
@@ -212,6 +217,36 @@
 			qcom,msm-dai-q6-dev-id = <16385>;
 		};
 
+		qcom,msm-dai-q6-sb-1-rx {
+			compatible = "qcom,msm-dai-q6-dev";
+			qcom,msm-dai-q6-dev-id = <16386>;
+		};
+
+		qcom,msm-dai-q6-sb-1-tx {
+			compatible = "qcom,msm-dai-q6-dev";
+			qcom,msm-dai-q6-dev-id = <16387>;
+		};
+
+		qcom,msm-dai-q6-sb-3-rx {
+			compatible = "qcom,msm-dai-q6-dev";
+			qcom,msm-dai-q6-dev-id = <16390>;
+		};
+
+		qcom,msm-dai-q6-sb-3-tx {
+			compatible = "qcom,msm-dai-q6-dev";
+			qcom,msm-dai-q6-dev-id = <16391>;
+		};
+
+		qcom,msm-dai-q6-sb-4-rx {
+			compatible = "qcom,msm-dai-q6-dev";
+			qcom,msm-dai-q6-dev-id = <16392>;
+		};
+
+		qcom,msm-dai-q6-sb-4-tx {
+			compatible = "qcom,msm-dai-q6-dev";
+			qcom,msm-dai-q6-dev-id = <16393>;
+		};
+
 		qcom,msm-dai-q6-bt-sco-rx {
 			compatible = "qcom,msm-dai-q6-dev";
 			qcom,msm-dai-q6-dev-id = <12288>;
@@ -351,3 +386,115 @@
 	taiko-mclk-clk = <&pm8941_clkdiv1>;
 	qcom,taiko-mclk-clk-freq = <9600000>;
 };
+
+* msm-dai-mi2s
+
+[First Level Nodes]
+
+Required properties:
+
+ - compatible : "msm-dai-mi2s"
+
+ [Second Level Nodes]
+
+Required properties:
+
+ - compatible : "qcom,msm-dai-q6-mi2s"
+ - qcom,msm-dai-q6-mi2s-dev-id: MSM or MDM can use Slimbus or I2S interface to transfer data
+								to (WCD9XXX) codec. If slimbus interface is used then
+								"msm-dai-q6" needs to be filled with correct data for slimbus
+								interface. The sections "msm-dai-mi2s" is used by MDM or MSM
+								to use I2S interface with codec. This section is used by CPU
+								driver in ASOC MSM to configure MI2S interface. MSM internally
+								has multiple MI2S namely Primary, Secondary, Tertiary and
+								Quaternary MI2S. They are represented with id 0, 1, 2, 3
+								respectively. The field "qcom,msm-dai-q6-mi2s-dev-id" represents
+								which of the MI2S block is used. These MI2S are connected to I2S
+								interface.
+
+ - qcom,msm-mi2s-rx-lines:		Each MI2S interface in MSM has one or more SD lines. These lines
+								are used for data transfer between codec and MSM. This element in
+								indicates which output RX lines are used in the MI2S interface.
+
+ - qcom,msm-mi2s-tx-lines:  	Each MI2S interface in MSM has one or more SD lines. These lines
+								are used for data transfer between codec and MSM. This element in
+								indicates which input TX lines are used in the MI2S interface.
+
+Example:
+
+qcom,msm-dai-mi2s {
+		compatible = "qcom,msm-dai-mi2s";
+		qcom,msm-dai-q6-mi2s-prim {
+			compatible = "qcom,msm-dai-q6-mi2s";
+			qcom,msm-dai-q6-mi2s-dev-id = <0>;
+			qcom,msm-mi2s-rx-lines = <2>;
+			qcom,msm-mi2s-tx-lines = <1>;
+		};
+};
+
+* MSM9625 ASoC Machine driver
+
+Required properties:
+- compatible : "qcom,mdm9625-audio-taiko"
+- qcom,model : The user-visible name of this sound card.
+- qcom,audio-routing : A list of the connections between audio components.
+  Each entry is a pair of strings, the first being the connection's sink,
+  the second being the connection's source.
+- qcom,taiko-mclk-clk-freq : Master clock value given to codec. Some WCD9XXX
+  codec can run at different mclk values. Mclk value can be 9.6MHz or 12.288MHz.
+  This element represents the value for MCLK provided to codec.
+
+Example:
+
+sound {
+		compatible = "qcom,mdm9625-audio-taiko";
+		qcom,model = "mdm9625-taiko-i2s-snd-card";
+
+		qcom,audio-routing =
+			"RX_BIAS", "MCLK",
+			"LDO_H", "MCLK",
+			"Ext Spk Bottom Pos", "LINEOUT1",
+			"Ext Spk Bottom Neg", "LINEOUT3",
+			"Ext Spk Top Pos", "LINEOUT2",
+			"Ext Spk Top Neg", "LINEOUT4",
+			"AMIC1", "MIC BIAS1 External",
+			"MIC BIAS1 External", "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",
+			"DMIC1", "MIC BIAS1 External",
+			"MIC BIAS1 External", "Digital Mic1",
+			"DMIC2", "MIC BIAS1 External",
+			"MIC BIAS1 External", "Digital Mic2",
+			"DMIC3", "MIC BIAS3 External",
+			"MIC BIAS3 External", "Digital Mic3",
+			"DMIC4", "MIC BIAS3 External",
+			"MIC BIAS3 External", "Digital Mic4",
+			"DMIC5", "MIC BIAS4 External",
+			"MIC BIAS4 External", "Digital Mic5",
+			"DMIC6", "MIC BIAS4 External",
+			"MIC BIAS4 External", "Digital Mic6";
+			qcom,taiko-mclk-clk-freq = <12288000>;
+};
+
+* msm-adsp-loader
+
+Required properties:
+ - compatible : "msm-adsp-loader"
+ - qcom,adsp-state:
+	It is possible that some MSM use PIL to load the ADSP image. While
+	other MSM may use SBL to load the ADSP image at boot. Audio APR needs
+	state of ADSP to register and enable APR to be used for sending commands
+	to ADSP. so adsp-state represents the state of ADSP to ADSP loader. Value
+	of 0 indicates ADSP loader needs to use PIL and value of 2 means ADSP
+	image is already loaded by SBL.
+
+Example:
+
+qcom,msm-adsp-loader {
+	compatible = "qcom,adsp-loader";
+	qcom,adsp-state = <2>;
+};
diff --git a/Documentation/devicetree/bindings/sound/taiko_codec.txt b/Documentation/devicetree/bindings/sound/taiko_codec.txt
index 9f3719b..090d8db 100644
--- a/Documentation/devicetree/bindings/sound/taiko_codec.txt
+++ b/Documentation/devicetree/bindings/sound/taiko_codec.txt
@@ -22,12 +22,18 @@
  - qcom,cdc-micbias-cfilt1-mv - cfilt1 output voltage in milli volts.
  - qcom,cdc-micbias-cfilt2-mv - cfilt2 output voltage in milli volts.
  - qcom,cdc-micbias-cfilt3-mv - cfilt3 output voltage in milli volts.
-   cfilt volatge can be set to max of qcom,cdc-micbias-ldoh-v - 0.15V.
+   cfilt voltage can be set to max of qcom,cdc-micbias-ldoh-v - 0.15V.
 
  - qcom,cdc-micbias1-cfilt-sel = cfilt to use for micbias1 (should be from 1 to 3).
  - 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).
+   This value represents the connected CFLIT to MIC Bias.
+
+ - 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,7 +82,117 @@
 	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];
 };
+
+Wcd9xxx audio CODEC in I2C mode
+
+  - compatible = "qcom,wcd9xxx-i2c-device";
+  - reg: represents the slave address provided to the I2C driver.
+  - qcom,cdc-reset-gpio: gpio used for codec SOC reset.
+  - <supply-name>-supply: phandle to the regulator device tree node.
+  - qcom,<supply-name>-voltage -  specifies voltage levels for supply. Should be
+       specified in pairs (min, max), units mV.
+  - qcom,<supply-name>-current - specifies max current in mA that can drawn
+       from the <supply-name>.
+
+    above three properties with "supply-name" set to  "qcom,cdc-vdd-buck", "qcom,cdc-vdd-tx-h",
+     "qcom,cdc-vdd-rx-h", "qcom,cdc-vddpx-1", "qcom,cdc-vdd-a-1p2v", "qcom,cdc-vddcx-1",
+     "qcom,cdc-vddcx-2" should be present.
+
+ - qcom,cdc-micbias-ldoh-v - LDOH output in volts ( should be 1.95 V and 3.00 V).
+
+ - qcom,cdc-micbias-cfilt1-mv - cfilt1 output voltage in milli volts.
+ - qcom,cdc-micbias-cfilt2-mv - cfilt2 output voltage in milli volts.
+ - qcom,cdc-micbias-cfilt3-mv - cfilt3 output voltage in milli volts.
+   cfilt voltage can be set to max of qcom,cdc-micbias-ldoh-v - 0.15V.
+
+ - qcom,cdc-micbias1-cfilt-sel = cfilt to use for micbias1 (should be from 1 to 3).
+ - 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).
+   This value represents the connected CFLIT to MIC Bias.
+
+ - 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.
+
+Example:
+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>;
+
+	wcd9xxx_codec@0d{
+		compatible = "qcom,wcd9xxx-i2c";
+		reg = <0x0d>;
+		qcom,cdc-reset-gpio = <&msmgpio 22 0>;
+		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>;
+
+		cdc-vdd-buck-supply = <&pm8019_l11>;
+		qcom,cdc-vdd-buck-voltage = <1800000 1800000>;
+		qcom,cdc-vdd-buck-current = <25000>;
+
+		cdc-vdd-tx-h-supply = <&pm8019_l11>;
+		qcom,cdc-vdd-tx-h-voltage = <1800000 1800000>;
+		qcom,cdc-vdd-tx-h-current = <25000>;
+
+		cdc-vdd-rx-h-supply = <&pm8019_l11>;
+		qcom,cdc-vdd-rx-h-voltage = <1800000 1800000>;
+		qcom,cdc-vdd-rx-h-current = <25000>;
+
+		cdc-vddpx-1-supply = <&pm8019_l11>;
+		qcom,cdc-vddpx-1-voltage = <1800000 1800000>;
+		qcom,cdc-vddpx-1-current = <10000>;
+
+		cdc-vdd-a-1p2v-supply = <&pm8019_l9>;
+		qcom,cdc-vdd-a-1p2v-voltage = <1200000 1200000>;
+		qcom,cdc-vdd-a-1p2v-current = <10000>;
+
+		cdc-vddcx-1-supply = <&pm8019_l9>;
+		qcom,cdc-vddcx-1-voltage = <1200000 1200000>;
+		qcom,cdc-vddcx-1-current = <10000>;
+
+		cdc-vddcx-2-supply = <&pm8019_l9>;
+		qcom,cdc-vddcx-2-voltage = <1200000 1200000>;
+		qcom,cdc-vddcx-2-current = <10000>;
+
+		qcom,cdc-micbias-ldoh-v = <0x3>;
+		qcom,cdc-micbias-cfilt1-mv = <1800>;
+		qcom,cdc-micbias-cfilt2-mv = <2700>;
+		qcom,cdc-micbias-cfilt3-mv = <1800>;
+		qcom,cdc-micbias1-cfilt-sel = <0x0>;
+		qcom,cdc-micbias2-cfilt-sel = <0x1>;
+		qcom,cdc-micbias3-cfilt-sel = <0x2>;
+		qcom,cdc-micbias4-cfilt-sel = <0x2>;
+	};
+
+	wcd9xxx_codec@77{
+		compatible = "qcom,wcd9xxx-i2c";
+		reg = <0x77>;
+	};
+
+	wcd9xxx_codec@66{
+		compatible = "qcom,wcd9xxx-i2c";
+		reg = <0x66>;
+	};
+	wcd9xxx_codec@55{
+		compatible = "qcom,wcd9xxx-i2c";
+		reg = <0x55>;
+	};
+};
diff --git a/Documentation/devicetree/bindings/tty/serial/msm_serial.txt b/Documentation/devicetree/bindings/tty/serial/msm_serial.txt
index 3b0426b..ae7d736 100644
--- a/Documentation/devicetree/bindings/tty/serial/msm_serial.txt
+++ b/Documentation/devicetree/bindings/tty/serial/msm_serial.txt
@@ -34,9 +34,19 @@
 - reg : offset and length of the register set for the device.
 - interrupts : should contain the uart interrupt.
 
-Example:
+Aliases:
+An alias may optionally be used to bind the serial device to a tty device
+(ttyHSLx) with a given line number. Aliases are of the form serial<n> where <n>
+is an integer representing the line number to use. On systems with multiple
+serial devices present it is recommended that an alias be defined for each such
+device.
 
-	serial@19c400000 {
+Example:
+	aliases {
+		serial0 = &uart0;       // This device will be called ttyHSL0
+	};
+
+	uart0: serial@19c400000 {
 		compatible = "qcom,msm-lsuart-v14"
 		reg = <0x19c40000 0x1000">;
 		interrupts = <195>;
diff --git a/Documentation/devicetree/bindings/usb/msm-ehci-hsic.txt b/Documentation/devicetree/bindings/usb/msm-ehci-hsic.txt
new file mode 100644
index 0000000..0e59f69
--- /dev/null
+++ b/Documentation/devicetree/bindings/usb/msm-ehci-hsic.txt
@@ -0,0 +1,20 @@
+MSM HSIC EHCI controller
+
+Required properties :
+- compatible : should be "qcom,hsic-host"
+- regs : offset and length of the register set in the memory map
+- interrupts: IRQ lines used by this controller
+- interrupt-names : Required interrupt resource entries are:
+            HSIC EHCI expects "core_irq" and optionally "async_irq".
+- <supply-name>-supply: handle to the regulator device tree node
+  Required "supply-name" is "HSIC_VDDCX" and optionally - "HSIC_GDSC".
+
+Example MSM HSIC EHCI controller device node :
+	hsic@f9a15000 {
+		compatible = "qcom,hsic-host";
+		reg = <0xf9a15000 0x400>;
+		interrupts = <0 136 0>;
+		interrupt-names = "core_irq";
+		HSIC_VDDCX-supply = <&pm8019_l12>;
+		HSIC_GDSC-supply = <&gdsc_usb_hsic>;
+	};
diff --git a/Documentation/devicetree/bindings/usb/msm-hsusb.txt b/Documentation/devicetree/bindings/usb/msm-hsusb.txt
index 186a58d..015822f 100644
--- a/Documentation/devicetree/bindings/usb/msm-hsusb.txt
+++ b/Documentation/devicetree/bindings/usb/msm-hsusb.txt
@@ -81,13 +81,16 @@
 - compatible: should be "qcom,android-usb"
 
 Optional properties :
-- reg  : offset and length of memory region that is used by driver to
+- reg  : offset and length of memory region that is used by device to
   update USB PID and serial numbers used by bootloader in DLOAD mode.
+- qcom,android-usb-swfi-latency : value to be used by device to vote
+  for DMA latency in microsecs.
 
 Example Android USB device node :
 	android_usb@fc42b0c8 {
 		compatible = "qcom,android-usb";
 		reg = <0xfc42b0c8 0xc8>;
+		qcom,android-usb-swfi-latency = <1>;
 	};
 
 
@@ -97,13 +100,24 @@
 - compatible: should be "qcom,usb-bam-msm"
 - reg  : pairs of physical base addresses and region sizes
             of all the memory mapped BAM devices present
-- reg-names : Register region name(s) referenced in reg above
-            SSUSB BAM expects "ssusb" and "hsusb" for HSSUB BAM.
-            Specify "qscratch_ram1_reg" to provide QSCRATCH's RAM1
-            register to control USB3 private memory for uses as BAM FIFOs.
+- reg-names : Register region name(s), in 1-1 correspondence with the
+	    registers in 'reg'. This list should contain at least as many names
+            as the number of unique values given in both 'usb-active-bam' and
+            all the subnodes' 'usb-bam-type' properties.
+
+            If SSUSB_BAM is used, "ssusb" should be present.
+            If HSUSB_BAM is used, "hsusb" should be present.
+            If HSIC_BAM is used, "hsic" should be present.
+
+            If a QSCRATCH RAM1 register is designated for providing USB3
+            private memory to use as a BAM FIFO, specify "qscratch_ram1_reg".
 - interrupts: IRQ lines for BAM devices
-- interrupt-names: BAM interrupt name(s) referenced in interrupts above
-            SSUSB BAM expects "ssusb" and "hsusb" for HSSUB BAM
+- interrupt-names: BAM interrupt name(s), in 1-1 correspondence with
+            'interrupts' above.
+
+            If SSUSB_BAM is used, "ssusb" should be present.
+            If HSUSB_BAM is used, "hsusb" should be present.
+            If HSIC_BAM is used, "hsic" should be present.
 - qcom,usb-active-bam: active BAM type. Can be one of
             0 - SSUSB_BAM
             1 - HSUSB_BAM
diff --git a/Documentation/devicetree/bindings/usb/msm-ssusb.txt b/Documentation/devicetree/bindings/usb/msm-ssusb.txt
index 9cc9e6e..d686523 100644
--- a/Documentation/devicetree/bindings/usb/msm-ssusb.txt
+++ b/Documentation/devicetree/bindings/usb/msm-ssusb.txt
@@ -10,12 +10,20 @@
 	"irq" : Interrupt for DWC3 core
 	"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".
+  Required "supply-name" examples are:
+	"SSUSB_lp8" : 1.8v supply for SSPHY
+	"HSUSB_1p8" : 1.8v supply for HSPHY
+	"HSUSB_3p3" : 3.3v supply for HSPHY
+	"vbus_dwc3" : vbus supply for host mode
+	"ssusb_vdd_dig" : vdd supply for SSPHY digital circuit operation
+	"hsusb_vdd_dig" : vdd supply for HSPHY digital circuit operation
 - 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
   USB3.0 controller)
+- qcom,vdd-voltage-level: This property must be a list of three integer
+  values (no, min, max) where each value represents either a voltage in
+  microvolts or a value corresponding to voltage corner
 
 Optional properties :
 - Refer to "Documentation/devicetree/bindings/arm/msm/msm_bus.txt" for
@@ -25,20 +33,29 @@
     - 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,otg-capability: If present then depend on PMIC for VBUS notifications,
+  otherwise depend on PHY.
+- qcom,charging-disabled: If present then battery charging using USB
+  is disabled.
 
 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";
-		SSUSB_VDDCX-supply = <&pm8841_s2>;
+		interrupts = <0 131 0>, <0 179 0>, <0 133 0>;
+		interrupt-names = "irq", "otg_irq", "hs_phy_irq";
+		ssusb_vdd_dig-supply = <&pm8841_s2_corner>;
 		SSUSB_1p8-supply = <&pm8941_l6>;
-		HSUSB_VDDCX-supply = <&pm8841_s2>;
+		hsusb_vdd_dig-supply = <&pm8841_s2_corner>;
 		HSUSB_1p8-supply = <&pm8941_l6>;
 		HSUSB_3p3-supply = <&pm8941_l24>;
+		vbus_dwc3-supply = <&pm8941_mvs1>;
 		qcom,dwc-usb3-msm-dbm-eps = <4>
+		qcom,vdd-voltage-level = <1 5 7>;
 
 		qcom,msm_bus,name = "usb3";
 		qcom,msm_bus,num_cases = <2>;
diff --git a/Documentation/dvb/qcom-mpq.txt b/Documentation/dvb/qcom-mpq.txt
index 28f5d39..1196da0 100644
--- a/Documentation/dvb/qcom-mpq.txt
+++ b/Documentation/dvb/qcom-mpq.txt
@@ -123,17 +123,15 @@
 
 Background Processing
 ---------------------
-When demux receives notifications from underlying HW drivers about new
-data, it schedules work to a single-threaded workqueue to process the
-notification.
+Demux allocates a kernel thread for each live-input to process
+the TS packets notified from the HW for specific input. There
+are two such inputs (TSIF0 and TSIF1), both can be processed in
+parallel by two seperate threads.
 
 The processing is the action of demuxing of the new data; it may sleep
 as it locks against the demux data-structure that may be accessed by
 user-space in the meanwhile.
 
-A single threaded workqueue exists for each live input (TSIF0 or TSIF1)
-to process the inputs in parallel.
-
 Dependencies
 ------------
 The demux driver depends on the following kernel drivers and subsystems:
diff --git a/Documentation/trace/events-msm-low-power.txt b/Documentation/trace/events-msm-low-power.txt
new file mode 100644
index 0000000..5414146
--- /dev/null
+++ b/Documentation/trace/events-msm-low-power.txt
@@ -0,0 +1,57 @@
+Subsystem Trace Points: msm_low_power
+
+The msm_low_power tracing system captures the events during the entry
+and exit of various low power modes like power collapse, standalone
+power collapse, retention and wfi. The tracing system adds the following
+events to capture the state of the low power mode.
+
+1) msm_pm_enter
+===================
+msm_pm_enter: cpu: %u latency: %uus sleep: %uus
+msm_pm_enter_pc: cpu: %u latency: %uus sleep: %uus wake_up: %u
+msm_pm_enter_ret: cpu: %u latency: %uus sleep: %uus wake_up: %u
+msm_pm_enter_spc: cpu: %u latency: %uus sleep: %uus wake_up: %u
+msm_pm_enter_wfi: cpu: %u latency: %uus sleep: %uus wake_up: %u
+
+The event captures various parameters during the entry into low power
+modes.
+
+The 'cpu' parameter represents the cpu on which the low power mode is
+chosen.
+
+The 'latency_us' parameter represents the system latency at the time of
+choosing the low power mode.
+
+The 'sleep_us' parameter tells the maximum amount of time the kernel can
+sleep in this low power mode.
+
+The 'wake_up' parameter tells if there was any immediate wakeup required
+before entering low power mode.
+
+2) msm_pm_exit
+=================
+msm_pm_exit: cpu:%u success:%d
+msm_pm_exit_pc: cpu:%u success:%d
+msm_pm_exit_ret: cpu:%u success:%d
+msm_pm_exit_spc: cpu:%u success:%d
+msm_pm_exit_wfi: cpu:%u success:%d
+
+The event captures parameters during the exit of the low power modes.
+
+The 'cpu' parameter represents the cpu on which the low power mode is chosen.
+
+The 'success' parameter shows the state of power collapse/standalone power
+collapse. It will be set if power collapse/standalone power collapse were
+successful. For the rest of the low power modes it is set to one.
+
+3) lpm_resources
+=================
+lpm_resources: name:%s sleep_value:%d
+
+This event captures parameters for each of the lpm resources.
+
+The 'name' parameter represents the name of the lpm resource and it can hold
+l2, pxo, vdd mem, vdd dig depending on the resource chosen during power
+collapse.
+
+The 'sleep_value' parameter corresponds to the sleep value set for the resource.
diff --git a/Documentation/trace/events-rpm-smd.txt b/Documentation/trace/events-rpm-smd.txt
new file mode 100644
index 0000000..f6c6bef
--- /dev/null
+++ b/Documentation/trace/events-rpm-smd.txt
@@ -0,0 +1,43 @@
+Subsystem Trace Points: rpm_smd
+
+The rpm-smd tracing system captures the events related to sending/receiving
+messages to/from RPM hardware. The tracing system adds the following events to
+capture the transactions with the RPM driver.
+
+1) RPM send message
+===================
+rpm_send_message: ctx:%s set:%s rsc_type:0%x(%s),rsc_id:i0x%x, id:%u
+
+The event captures the parameters of the message that was sent to the RPM.
+
+The 'ctx' parameters takes one of the following string constants to indicate
+if the request was made in atomic/non-atomic context.
+ . "sleep" - non-atomic context
+ . "noslp" - atomic context
+
+The 'set' parameter takes one of the following string values to indicate if
+the message affects active or sleep set value of the resource
+ . "act" - active set configuraion
+ . "slp" - sleep set configuration
+
+The 'rsc_type' and 'rsc_id' identify the resource whose parametes is being
+modified. The 4 bytes string equivalent of the resource type is also displayed
+for easier identification of resources.
+
+The 'id' parameter is the id that RPM uses to acknowledge the receipt of the
+message in its ACK message
+
+
+2) RPM ack message
+=================
+rpm_ack_recd: ctx:%s id:%u
+
+The event captures the acknowledgement messages received from the RPM after
+successfully send a message request.
+
+The 'ctx' parameter has the same string constants referred to the "RPM Send
+Message"
+
+The 'id' parameter is the id that RPM uses to acknowledge the receipt of the
+message in its ACK message
+
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 5d5f9de..89c7417 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -264,6 +264,29 @@
 	def_bool y
 	depends on BUG
 
+config GENERIC_TIME_VSYSCALL
+	bool "Enable gettimeofday updates"
+	depends on CPU_V7
+	help
+	  Enables updating the kernel user helper area with the xtime struct
+	  data for gettimeofday via kernel user helpers.
+
+config ARM_USE_USER_ACCESSIBLE_TIMERS
+	bool "Enables mapping a timer counter page to user space"
+	depends on USE_USER_ACCESSIBLE_TIMERS && GENERIC_TIME_VSYSCALL
+	help
+	 Enables ARM-specific user-accessible timers via a shared
+	 memory page containing the cycle counter.
+
+config ARM_USER_ACCESSIBLE_TIMER_BASE
+	hex "Base address of user-accessible timer counter page"
+	default 0xfffef000
+	depends on ARM_USE_USER_ACCESSIBLE_TIMERS
+	help
+	 Specify the base user-space virtual address where the user-accessible
+	 timer counter page should be mapped by the kernel.  User-space apps
+	 will read directly from the page at this address.
+
 source "init/Kconfig"
 
 source "kernel/Kconfig.freezer"
diff --git a/arch/arm/boot/dts/mpq8092-regulator.dtsi b/arch/arm/boot/dts/mpq8092-regulator.dtsi
index fbc9586..b724a3d 100644
--- a/arch/arm/boot/dts/mpq8092-regulator.dtsi
+++ b/arch/arm/boot/dts/mpq8092-regulator.dtsi
@@ -159,7 +159,7 @@
 		};
 
 		pm8644_l13: regulator@4c00 {
-			regulator-min-microvolt = <2950000>;
+			regulator-min-microvolt = <1800000>;
 			regulator-max-microvolt = <2950000>;
 			qcom,enable-time = <200>;
 			qcom,pull-down-enable = <1>;
diff --git a/arch/arm/boot/dts/mpq8092.dtsi b/arch/arm/boot/dts/mpq8092.dtsi
index 252b9f5..0decddc 100644
--- a/arch/arm/boot/dts/mpq8092.dtsi
+++ b/arch/arm/boot/dts/mpq8092.dtsi
@@ -38,7 +38,7 @@
 	};
 
 	timer {
-		compatible = "qcom,msm-qtimer", "arm,armv7-timer";
+		compatible = "arm,armv7-timer";
 		interrupts = <1 2 0>, <1 3 0>;
 		clock-frequency = <19200000>;
 	};
@@ -65,6 +65,7 @@
 		/* 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 */
@@ -228,6 +229,75 @@
 				 <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,pad-pull-on = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
+		qcom,pad-pull-off = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
+		qcom,pad-drv-on = <0x7 0x4 0x4>; /* 16mA, 10mA, 10mA */
+		qcom,pad-drv-off = <0x0 0x0 0x0>; /* 2mA, 2mA, 2mA */
+
+		qcom,clk-rates = <400000 25000000 50000000 100000000 200000000>;
+		qcom,sup-voltages = <2950 2950>;
+		qcom,bus-width = <8>;
+		qcom,nonremovable;
+		qcom,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,pad-pull-on = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
+		qcom,pad-pull-off = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
+		qcom,pad-drv-on = <0x7 0x4 0x4>; /* 16mA, 10mA, 10mA */
+		qcom,pad-drv-off = <0x0 0x0 0x0>; /* 2mA, 2mA, 2mA */
+
+		qcom,clk-rates = <400000 25000000 50000000 100000000 200000000>;
+		qcom,sup-voltages = <2950 2950>;
+		qcom,bus-width = <4>;
+		qcom,xpc;
+		qcom,bus-speed-mode = "SDR12", "SDR25", "SDR50", "DDR50", "SDR104";
+		qcom,current-limit = <800>;
+	};
+};
+
+&gdsc_venus {
+	status = "ok";
+};
+
+&gdsc_mdss {
+	status = "ok";
+};
+
+&gdsc_jpeg {
+	status = "ok";
+};
+
+&gdsc_vfe {
+	status = "ok";
+};
+
+&gdsc_oxili_gx {
+	status = "ok";
+};
+
+&gdsc_oxili_cx {
+	status = "ok";
+};
+
+&gdsc_usb_hsic {
+	status = "ok";
 };
 
 /include/ "msm-pm8644.dtsi"
diff --git a/arch/arm/boot/dts/msm-gdsc.dtsi b/arch/arm/boot/dts/msm-gdsc.dtsi
index f83fe76..f0570ba 100644
--- a/arch/arm/boot/dts/msm-gdsc.dtsi
+++ b/arch/arm/boot/dts/msm-gdsc.dtsi
@@ -18,41 +18,48 @@
 		compatible = "qcom,gdsc";
 		regulator-name = "gdsc_venus";
 		reg = <0xfd8c1024 0x4>;
+		status = "disabled";
 	};
 
 	gdsc_mdss: qcom,gdsc@fd8c2304 {
 		compatible = "qcom,gdsc";
 		regulator-name = "gdsc_mdss";
 		reg = <0xfd8c2304 0x4>;
+		status = "disabled";
 	};
 
 	gdsc_jpeg: qcom,gdsc@fd8c35a4 {
 		compatible = "qcom,gdsc";
 		regulator-name = "gdsc_jpeg";
 		reg = <0xfd8c35a4 0x4>;
+		status = "disabled";
 	};
 
 	gdsc_vfe: qcom,gdsc@fd8c36a4 {
 		compatible = "qcom,gdsc";
 		regulator-name = "gdsc_vfe";
 		reg = <0xfd8c36a4 0x4>;
+		status = "disabled";
 	};
 
 	gdsc_oxili_gx: qcom,gdsc@fd8c4024 {
 		compatible = "qcom,gdsc";
 		regulator-name = "gdsc_oxili_gx";
 		reg = <0xfd8c4024 0x4>;
+		status = "disabled";
 	};
 
 	gdsc_oxili_cx: qcom,gdsc@fd8c4034 {
 		compatible = "qcom,gdsc";
 		regulator-name = "gdsc_oxili_cx";
 		reg = <0xfd8c4034 0x4>;
+		status = "disabled";
 	};
 
 	gdsc_usb_hsic: qcom,gdsc@fc400404 {
 		compatible = "qcom,gdsc";
 		regulator-name = "gdsc_usb_hsic";
 		reg = <0xfc400404 0x4>;
+		status = "disabled";
 	};
 };
diff --git a/arch/arm/boot/dts/msm-pm8019.dtsi b/arch/arm/boot/dts/msm-pm8019.dtsi
index e70eb36..2105e8a 100755
--- a/arch/arm/boot/dts/msm-pm8019.dtsi
+++ b/arch/arm/boot/dts/msm-pm8019.dtsi
@@ -152,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-pm8941.dtsi b/arch/arm/boot/dts/msm-pm8941.dtsi
index 1d95407..6538db5 100644
--- a/arch/arm/boot/dts/msm-pm8941.dtsi
+++ b/arch/arm/boot/dts/msm-pm8941.dtsi
@@ -58,10 +58,10 @@
 			};
 		};
 
-		bms@4000 {
+		pm8941_bms: bms@4000 {
 			#address-cells = <1>;
 			#size-cells = <1>;
-
+			status = "disabled";
 			compatible = "qcom,qpnp-bms";
 			reg = <0x4000 0x100>;
 
@@ -90,7 +90,12 @@
 			qcom,bms-shutdown-soc-valid-limit = <20>;
 			qcom,bms-adjust-soc-low-threshold = <25>;
 			qcom,bms-adjust-soc-high-threshold = <45>;
+			qcom,bms-low-soc-calculate-soc-threshold = <15>;
+			qcom,bms-low-soc-calculate-soc-ms = <5000>;
+			qcom,bms-calculate-soc-ms = <20000>;
 			qcom,bms-chg-term-ua = <100000>;
+			qcom,bms-batt-type = <0>;
+			qcom,bms-use-voltage-soc;
 		};
 
 		clkdiv@5b00 {
@@ -136,14 +141,14 @@
 						<0x0 0x10 0x6>,
 						<0x0 0x10 0x7>;
 
-				interrupt-names =	"chg-done",
-							"chg-failed",
-							"fast-chg-on",
-							"trkl-chg-on",
-							"state-change",
-							"chgwdog",
+				interrupt-names =	"vbat-det-lo",
 							"vbat-det-hi",
-							"vbat-det-lo";
+							"chgwdog",
+							"state-change",
+							"trkl-chg-on",
+							"fast-chg-on",
+							"chg-failed",
+							"chg-done";
 			};
 
 			qcom,chg-buck@1100 {
@@ -157,13 +162,13 @@
 						<0x0 0x11 0x5>,
 						<0x0 0x11 0x6>;
 
-				interrupt-names =	"vdd-loop",
-							"ibat-loop",
-							"ichg-loop",
-							"vchg-loop",
+				interrupt-names =	"vbat-ov",
+							"vreg-ov",
 							"overtemp",
-							"vref-ov",
-							"vbat-ov";
+							"vchg-loop",
+							"ichg-loop",
+							"ibat-loop",
+							"vdd-loop";
 			};
 
 			qcom,chg-bat-if@1200 {
@@ -175,11 +180,12 @@
 						<0x0 0x12 0x3>,
 						<0x0 0x12 0x4>;
 
-				interrupt-names =	"psi",
-							"vcp-on",
-							"bat-fet-on",
+				interrupt-names =	"batt-pres",
 							"bat-temp-ok",
-							"batt-pres";
+							"bat-fet-on",
+							"vcp-on",
+							"psi";
+
 			};
 
 			qcom,chg-usb-chgpth@1300 {
@@ -200,8 +206,8 @@
 				interrupts =	<0x0 0x14 0x0>,
 						<0x0 0x14 0x1>;
 
-				interrupt-names =	"dcin-valid",
-							"coarse-det-dc";
+				interrupt-names =	"coarse-det-dc",
+							"dcin-valid";
 			};
 
 			qcom,chg-boost@1500 {
@@ -210,8 +216,8 @@
 				interrupts =	<0x0 0x15 0x0>,
 						<0x0 0x15 0x1>;
 
-				interrupt-names =	"limit-error",
-							"boost-pwr-ok";
+				interrupt-names =	"boost-pwr-ok",
+							"limit-error";
 			};
 
 			qcom,chg-misc@1600 {
@@ -481,6 +487,7 @@
 			compatible = "qcom,qpnp-vadc";
 			reg = <0x3100 0x100>;
 			interrupts = <0x0 0x31 0x0>;
+			interrupt-names = "eoc-int-en-set";
 			qcom,adc-bit-resolution = <15>;
 			qcom,adc-vdd-reference = <1800>;
 
@@ -692,12 +699,24 @@
 				qcom,hw-settle-time = <0>;
 				qcom,fast-avg-setup = <0>;
 			};
+
+			chan@185 {
+				label = "usb_id";
+				qcom,channel-num = <185>;
+				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>;
+			};
 		};
 
 		iadc@3600 {
 			compatible = "qcom,qpnp-iadc";
 			reg = <0x3600 0x100>;
 			interrupts = <0x0 0x36 0x0>;
+			interrupt-names = "eoc-int-en-set";
 			qcom,adc-bit-resolution = <16>;
 			qcom,adc-vdd-reference = <1800>;
 			qcom,rsense = <1500>;
@@ -993,64 +1012,112 @@
 			status = "disabled";
 		};
 
+		qcom,leds@d000 {
+			compatible = "qcom,leds-qpnp";
+			reg = <0xd000 0x100>;
+			label = "rgb";
+		};
+
+		qcom,leds@d100 {
+			compatible = "qcom,leds-qpnp";
+			reg = <0xd100 0x100>;
+			label = "rgb";
+		};
+
+		qcom,leds@d200 {
+			compatible = "qcom,leds-qpnp";
+			reg = <0xd200 0x100>;
+			label = "rgb";
+		};
+
+		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 {
diff --git a/arch/arm/boot/dts/msm8226-ion.dtsi b/arch/arm/boot/dts/msm8226-ion.dtsi
index beaffe5..b06ad42 100644
--- a/arch/arm/boot/dts/msm8226-ion.dtsi
+++ b/arch/arm/boot/dts/msm8226-ion.dtsi
@@ -19,5 +19,33 @@
 		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 = <0x3800000>;
+		};
+
+		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 = <0x780000>;
+		};
+
+		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 = <0x314000>;
+		};
 	};
 };
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 7c25680..41ac69d 100644
--- a/arch/arm/boot/dts/msm8226-sim.dts
+++ b/arch/arm/boot/dts/msm8226-sim.dts
@@ -12,7 +12,6 @@
 
 /dts-v1/;
 /include/ "msm8226.dtsi"
-/include/ "msm8226-ion.dtsi"
 /include/ "msm8226-camera.dtsi"
 
 / {
diff --git a/arch/arm/boot/dts/msm8226.dtsi b/arch/arm/boot/dts/msm8226.dtsi
index 6d2ffec..927ebcd 100644
--- a/arch/arm/boot/dts/msm8226.dtsi
+++ b/arch/arm/boot/dts/msm8226.dtsi
@@ -11,6 +11,8 @@
  */
 
 /include/ "skeleton.dtsi"
+/include/ "msm8226-ion.dtsi"
+/include/ "msm-gdsc.dtsi"
 
 / {
 	model = "Qualcomm MSM 8226";
@@ -34,7 +36,7 @@
 	};
 
 	timer {
-		compatible = "qcom,msm-qtimer", "arm,armv7-timer";
+		compatible = "arm,armv7-timer";
 		interrupts = <1 2 0 1 3 0>;
 		clock-frequency = <19200000>;
 	};
@@ -58,6 +60,9 @@
 		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>;
@@ -69,4 +74,107 @@
 		compatible = "qcom,android-usb";
 	};
 
+	qcom,wdt@f9017000 {
+		compatible = "qcom,msm-watchdog";
+		reg = <0xf9017000 0x1000>;
+		interrupts = <0 3 0>, <0 4 0>;
+		qcom,bark-time = <11000>;
+		qcom,pet-time = <10000>;
+		qcom,ipi-ping = <1>;
+	};
+
+	qcom,smem@fa00000 {
+		compatible = "qcom,smem";
+		reg = <0xfa00000 0x200000>,
+			<0xfa006000 0x1000>,
+			<0xfc428000 0x4000>;
+		reg-names = "smem", "irq-reg-base", "aux-mem1";
+
+		qcom,smd-modem {
+			compatible = "qcom,smd";
+			qcom,smd-edge = <0>;
+			qcom,smd-irq-offset = <0x8>;
+			qcom,smd-irq-bitmask = <0x1000>;
+			qcom,pil-string = "modem";
+			interrupts = <0 25 1>;
+		};
+
+		qcom,smsm-modem {
+			compatible = "qcom,smsm";
+			qcom,smsm-edge = <0>;
+			qcom,smsm-irq-offset = <0x8>;
+			qcom,smsm-irq-bitmask = <0x2000>;
+			interrupts = <0 26 1>;
+		};
+
+		qcom,smd-adsp {
+			compatible = "qcom,smd";
+			qcom,smd-edge = <1>;
+			qcom,smd-irq-offset = <0x8>;
+			qcom,smd-irq-bitmask = <0x100>;
+			qcom,pil-string = "adsp";
+			interrupts = <0 156 1>;
+		};
+
+		qcom,smsm-adsp {
+			compatible = "qcom,smsm";
+			qcom,smsm-edge = <1>;
+			qcom,smsm-irq-offset = <0x8>;
+			qcom,smsm-irq-bitmask = <0x200>;
+			interrupts = <0 157 1>;
+		};
+
+		qcom,smd-wcnss {
+			compatible = "qcom,smd";
+			qcom,smd-edge = <6>;
+			qcom,smd-irq-offset = <0x8>;
+			qcom,smd-irq-bitmask = <0x20000>;
+			qcom,pil-string = "wcnss";
+			interrupts = <0 142 1>;
+		};
+
+		qcom,smsm-wcnss {
+			compatible = "qcom,smsm";
+			qcom,smsm-edge = <6>;
+			qcom,smsm-irq-offset = <0x8>;
+			qcom,smsm-irq-bitmask = <0x80000>;
+			interrupts = <0 144 1>;
+		};
+
+		qcom,smd-rpm {
+			compatible = "qcom,smd";
+			qcom,smd-edge = <15>;
+			qcom,smd-irq-offset = <0x8>;
+			qcom,smd-irq-bitmask = <0x1>;
+			interrupts = <0 168 1>;
+			qcom,irq-no-suspend;
+		};
+	};
+
 };
+
+&gdsc_venus {
+	status = "ok";
+};
+
+&gdsc_mdss {
+	status = "ok";
+};
+
+&gdsc_jpeg {
+	status = "ok";
+};
+
+&gdsc_vfe {
+	status = "ok";
+};
+
+&gdsc_oxili_cx {
+	status = "ok";
+};
+
+&gdsc_usb_hsic {
+	status = "ok";
+};
+
+/include/ "msm8226-regulator.dtsi"
diff --git a/arch/arm/boot/dts/msm8910-ion.dtsi b/arch/arm/boot/dts/msm8910-ion.dtsi
new file mode 100644
index 0000000..88bb1ab
--- /dev/null
+++ b/arch/arm/boot/dts/msm8910-ion.dtsi
@@ -0,0 +1,62 @@
+/* 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 = <0x3800000>;
+		};
+
+		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 = <0x780000>;
+		};
+
+		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 = <0x314000>;
+		};
+
+		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>;
+		};
+
+	};
+};
+
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-rumi.dts b/arch/arm/boot/dts/msm8910-rumi.dts
new file mode 100644
index 0000000..0d944aa
--- /dev/null
+++ b/arch/arm/boot/dts/msm8910-rumi.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 Rumi";
+	compatible = "qcom,msm8910-rumi", "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
index 83dabfb..a5cac86 100644
--- a/arch/arm/boot/dts/msm8910.dtsi
+++ b/arch/arm/boot/dts/msm8910.dtsi
@@ -11,6 +11,7 @@
  */
 
 /include/ "skeleton.dtsi"
+/include/ "msm8910-ion.dtsi"
 
 / {
 	model = "Qualcomm MSM 8910";
@@ -34,7 +35,7 @@
 	};
 
 	timer {
-		compatible = "qcom,msm-qtimer", "arm,armv7-timer";
+		compatible = "arm,armv7-timer";
 		interrupts = <1 2 0 1 3 0>;
 		clock-frequency = <19200000>;
 	};
@@ -51,6 +52,9 @@
 		reg = <0xf9a55000 0x400>;
 		interrupts = <0 134 0>;
 		interrupt-names = "core_irq";
+		HSUSB_VDDCX-supply = <&pm8110_s1>;
+		HSUSB_1p8-supply = <&pm8110_l10>;
+		HSUSB_3p3-supply = <&pm8110_l20>;
 
 		qcom,hsusb-otg-phy-type = <2>;
 		qcom,hsusb-otg-mode = <1>;
@@ -62,4 +66,143 @@
 		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";
+
+		vdd-supply = <&pm8110_l17>;
+		qcom,vdd-always-on;
+		qcom,vdd-lpm-sup;
+		qcom,vdd-voltage-level = <2900000 2900000>;
+		qcom,vdd-current-level = <9000 400000>;
+
+		vdd-io-supply = <&pm8110_l6>;
+		qcom,vdd-io-always-on;
+		qcom,vdd-io-lpm-sup;
+		qcom,vdd-io-voltage-level = <1800000 1800000>;
+		qcom,vdd-io-current-level = <9000 60000>;
+
+		qcom,pad-pull-on = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
+		qcom,pad-pull-off = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
+		qcom,pad-drv-on = <0x7 0x4 0x4>; /* 16mA, 10mA, 10mA */
+		qcom,pad-drv-off = <0x0 0x0 0x0>; /* 2mA, 2mA, 2mA */
+
+		qcom,clk-rates = <400000 25000000 50000000 100000000 200000000>;
+		qcom,sup-voltages = <2900 2900>;
+		qcom,bus-width = <8>;
+		qcom,nonremovable;
+		qcom,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";
+
+		vdd-supply = <&pm8110_l18>;
+		qcom,vdd-voltage-level = <2950000 2950000>;
+		qcom,vdd-current-level = <9000 400000>;
+
+		vdd-io-supply = <&pm8110_l21>;
+		qcom,vdd-io-voltage-level = <1800000 2950000>;
+		qcom,vdd-io-current-level = <9000 50000>;
+
+		qcom,pad-pull-on = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
+		qcom,pad-pull-off = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
+		qcom,pad-drv-on = <0x7 0x4 0x4>; /* 16mA, 10mA, 10mA */
+		qcom,pad-drv-off = <0x0 0x0 0x0>; /* 2mA, 2mA, 2mA */
+
+		qcom,clk-rates = <400000 25000000 50000000 100000000 200000000>;
+		qcom,sup-voltages = <2950 2950>;
+		qcom,bus-width = <4>;
+		qcom,xpc;
+		qcom,bus-speed-mode = "SDR12", "SDR25", "SDR50", "DDR50", "SDR104";
+		qcom,current-limit = <800>;
+	};
+
+	qcom,smem@fa00000 {
+		compatible = "qcom,smem";
+		reg = <0xfa00000 0x200000>,
+			<0xfa006000 0x1000>,
+			<0xfc428000 0x4000>;
+		reg-names = "smem", "irq-reg-base", "aux-mem1";
+
+		qcom,smd-modem {
+			compatible = "qcom,smd";
+			qcom,smd-edge = <0>;
+			qcom,smd-irq-offset = <0x8>;
+			qcom,smd-irq-bitmask = <0x1000>;
+			qcom,pil-string = "modem";
+			interrupts = <0 25 1>;
+		};
+
+		qcom,smsm-modem {
+			compatible = "qcom,smsm";
+			qcom,smsm-edge = <0>;
+			qcom,smsm-irq-offset = <0x8>;
+			qcom,smsm-irq-bitmask = <0x2000>;
+			interrupts = <0 26 1>;
+		};
+
+		qcom,smd-adsp {
+			compatible = "qcom,smd";
+			qcom,smd-edge = <1>;
+			qcom,smd-irq-offset = <0x8>;
+			qcom,smd-irq-bitmask = <0x100>;
+			qcom,pil-string = "adsp";
+			interrupts = <0 156 1>;
+		};
+
+		qcom,smsm-adsp {
+			compatible = "qcom,smsm";
+			qcom,smsm-edge = <1>;
+			qcom,smsm-irq-offset = <0x8>;
+			qcom,smsm-irq-bitmask = <0x200>;
+			interrupts = <0 157 1>;
+		};
+
+		qcom,smd-wcnss {
+			compatible = "qcom,smd";
+			qcom,smd-edge = <6>;
+			qcom,smd-irq-offset = <0x8>;
+			qcom,smd-irq-bitmask = <0x20000>;
+			qcom,pil-string = "wcnss";
+			interrupts = <0 142 1>;
+		};
+
+		qcom,smsm-wcnss {
+			compatible = "qcom,smsm";
+			qcom,smsm-edge = <6>;
+			qcom,smsm-irq-offset = <0x8>;
+			qcom,smsm-irq-bitmask = <0x80000>;
+			interrupts = <0 144 1>;
+		};
+
+		qcom,smd-rpm {
+			compatible = "qcom,smd";
+			qcom,smd-edge = <15>;
+			qcom,smd-irq-offset = <0x8>;
+			qcom,smd-irq-bitmask = <0x1>;
+			interrupts = <0 168 1>;
+			qcom,irq-no-suspend;
+		};
+	};
+
+	qcom,wdt@f9017000 {
+		compatible = "qcom,msm-watchdog";
+		reg = <0xf9017000 0x1000>;
+		interrupts = <0 3 0>, <0 4 0>;
+		qcom,bark-time = <11000>;
+		qcom,pet-time = <10000>;
+		qcom,ipi-ping = <1>;
+	};
 };
+
+/include/ "msm8910-regulator.dtsi"
diff --git a/arch/arm/boot/dts/msm8974-camera-sensor-liquid.dtsi b/arch/arm/boot/dts/msm8974-camera-sensor-liquid.dtsi
new file mode 100644
index 0000000..c9b999f
--- /dev/null
+++ b/arch/arm/boot/dts/msm8974-camera-sensor-liquid.dtsi
@@ -0,0 +1,97 @@
+
+/*
+ * 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.
+ */
+
+&cci {
+
+	qcom,camera@6e {
+		compatible = "qcom,s5k3l1yx";
+		reg = <0x6e 0x0>;
+		qcom,csi-if = <1>;
+		qcom,csid-core = <0>;
+		qcom,flash-src-index = <&led_flash0>;
+		qcom,mount-angle = <0>;
+		qcom,sensor-name = "s5k3l1yx";
+		cam_vdig-supply = <&pm8941_l3>;
+		cam_vana-supply = <&pm8941_l17>;
+		cam_vio-supply = <&pm8941_lvs3>;
+		cam_vaf-supply = <&pm8941_l23>;
+		qcom,cam-vreg-name = "cam_vdig", "cam_vana", "cam_vio",
+				     "cam_vaf";
+		qcom,cam-vreg-type = <0 0 1 0>;
+		qcom,cam-vreg-min-voltage = <1225000 2850000 0 3000000>;
+		qcom,cam-vreg-max-voltage = <1225000 2850000 0 3000000>;
+		qcom,cam-vreg-op-mode = <105000 80000 0 100000>;
+		qcom,gpio-no-mux = <0>;
+		gpios = <&msmgpio 15 0>,
+			<&msmgpio 19 0>,
+			<&msmgpio 20 0>,
+			<&msmgpio 90 0>;
+		qcom,gpio-common-tbl-num = <0 1 2>;
+		qcom,gpio-common-tbl-flags = <1 1 1>;
+		qcom,gpio-common-tbl-label = "CAMIF_MCLK",
+					     "CAMIF_I2C_DATA",
+					     "CAMIF_I2C_CLK";
+		qcom,gpio-req-tbl-num = <3>;
+		qcom,gpio-req-tbl-flags = <0>;
+		qcom,gpio-req-tbl-label = "CAM_RESET1";
+		qcom,gpio-set-tbl-num = <3 3>;
+		qcom,gpio-set-tbl-flags = <0 2>;
+		qcom,gpio-set-tbl-delay = <1000 30000>;
+		qcom,csi-lane-assign = <0x4320>;
+		qcom,csi-lane-mask = <0x1F>;
+		qcom,csi-phy-sel = <0>;
+		qcom,camera-type = <0>;
+		qcom,sensor-type = <0>;
+		status = "ok";
+	};
+
+	qcom,camera@6c {
+		compatible = "qcom,ov2720";
+		reg = <0x6c 0x0>;
+		qcom,csi-if = <1>;
+		qcom,csid-core = <0>;
+		qcom,mount-angle = <180>;
+		qcom,sensor-name = "ov2720";
+		cam_vdig-supply = <&pm8941_l3>;
+		cam_vana-supply = <&pm8941_l17>;
+		cam_vio-supply = <&pm8941_lvs2>;
+		qcom,cam-vreg-name = "cam_vdig", "cam_vana", "cam_vio";
+		qcom,cam-vreg-type = <0 0 1>;
+		qcom,cam-vreg-min-voltage = <1225000 2850000 0>;
+		qcom,cam-vreg-max-voltage = <1225000 2850000 0>;
+		qcom,cam-vreg-op-mode = <105000 80000 0>;
+		qcom,gpio-no-mux = <0>;
+		gpios = <&msmgpio 17 0>,
+			<&msmgpio 19 0>,
+			<&msmgpio 20 0>,
+			<&msmgpio 18 0>;
+		qcom,gpio-common-tbl-num = <0 1 2>;
+		qcom,gpio-common-tbl-flags = <1 1 1>;
+		qcom,gpio-common-tbl-label = "CAMIF_MCLK",
+					     "CAMIF_I2C_DATA",
+					     "CAMIF_I2C_CLK";
+		qcom,gpio-req-tbl-num = <3>;
+		qcom,gpio-req-tbl-flags = <0>;
+		qcom,gpio-req-tbl-label = "CAM_RESET1";
+		qcom,gpio-set-tbl-num = <3 3>;
+		qcom,gpio-set-tbl-flags = <0 2>;
+		qcom,gpio-set-tbl-delay = <1000 4000>;
+		qcom,csi-lane-assign = <0x4320>;
+		qcom,csi-lane-mask = <0x7>;
+		qcom,csi-phy-sel = <2>;
+		qcom,camera-type = <1>;
+		qcom,sensor-type = <0>;
+		status = "ok";
+	};
+};
diff --git a/arch/arm/boot/dts/msm8974-camera-sensor.dtsi b/arch/arm/boot/dts/msm8974-camera-sensor.dtsi
new file mode 100644
index 0000000..68da844
--- /dev/null
+++ b/arch/arm/boot/dts/msm8974-camera-sensor.dtsi
@@ -0,0 +1,96 @@
+/*
+ * 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.
+ */
+
+&cci {
+
+	qcom,camera@6e {
+		compatible = "qcom,s5k3l1yx";
+		reg = <0x6e 0x0>;
+		qcom,csi-if = <1>;
+		qcom,csid-core = <0>;
+		qcom,flash-src-index = <&led_flash0>;
+		qcom,mount-angle = <90>;
+		qcom,sensor-name = "s5k3l1yx";
+		cam_vdig-supply = <&pm8941_l3>;
+		cam_vana-supply = <&pm8941_l17>;
+		cam_vio-supply = <&pm8941_lvs3>;
+		cam_vaf-supply = <&pm8941_l23>;
+		qcom,cam-vreg-name = "cam_vdig", "cam_vana", "cam_vio",
+				     "cam_vaf";
+		qcom,cam-vreg-type = <0 0 1 0>;
+		qcom,cam-vreg-min-voltage = <1225000 2850000 0 3000000>;
+		qcom,cam-vreg-max-voltage = <1225000 2850000 0 3000000>;
+		qcom,cam-vreg-op-mode = <105000 80000 0 100000>;
+		qcom,gpio-no-mux = <0>;
+		gpios = <&msmgpio 15 0>,
+			<&msmgpio 19 0>,
+			<&msmgpio 20 0>,
+			<&msmgpio 90 0>;
+		qcom,gpio-common-tbl-num = <0 1 2>;
+		qcom,gpio-common-tbl-flags = <1 1 1>;
+		qcom,gpio-common-tbl-label = "CAMIF_MCLK",
+					     "CAMIF_I2C_DATA",
+					     "CAMIF_I2C_CLK";
+		qcom,gpio-req-tbl-num = <3>;
+		qcom,gpio-req-tbl-flags = <0>;
+		qcom,gpio-req-tbl-label = "CAM_RESET1";
+		qcom,gpio-set-tbl-num = <3 3>;
+		qcom,gpio-set-tbl-flags = <0 2>;
+		qcom,gpio-set-tbl-delay = <1000 30000>;
+		qcom,csi-lane-assign = <0x4320>;
+		qcom,csi-lane-mask = <0x1F>;
+		qcom,csi-phy-sel = <0>;
+		qcom,camera-type = <0>;
+		qcom,sensor-type = <0>;
+		status = "ok";
+	};
+
+	qcom,camera@6c {
+		compatible = "qcom,ov2720";
+		reg = <0x6c 0x0>;
+		qcom,csi-if = <1>;
+		qcom,csid-core = <0>;
+		qcom,mount-angle = <180>;
+		qcom,sensor-name = "ov2720";
+		cam_vdig-supply = <&pm8941_l3>;
+		cam_vana-supply = <&pm8941_l17>;
+		cam_vio-supply = <&pm8941_lvs3>;
+		qcom,cam-vreg-name = "cam_vdig", "cam_vana", "cam_vio";
+		qcom,cam-vreg-type = <0 0 1>;
+		qcom,cam-vreg-min-voltage = <1225000 2850000 0>;
+		qcom,cam-vreg-max-voltage = <1225000 2850000 0>;
+		qcom,cam-vreg-op-mode = <105000 80000 0>;
+		qcom,gpio-no-mux = <0>;
+		gpios = <&msmgpio 17 0>,
+			<&msmgpio 19 0>,
+			<&msmgpio 20 0>,
+			<&msmgpio 18 0>;
+		qcom,gpio-common-tbl-num = <0 1 2>;
+		qcom,gpio-common-tbl-flags = <1 1 1>;
+		qcom,gpio-common-tbl-label = "CAMIF_MCLK",
+					     "CAMIF_I2C_DATA",
+					     "CAMIF_I2C_CLK";
+		qcom,gpio-req-tbl-num = <3>;
+		qcom,gpio-req-tbl-flags = <0>;
+		qcom,gpio-req-tbl-label = "CAM_RESET1";
+		qcom,gpio-set-tbl-num = <3 3>;
+		qcom,gpio-set-tbl-flags = <0 2>;
+		qcom,gpio-set-tbl-delay = <1000 4000>;
+		qcom,csi-lane-assign = <0x4320>;
+		qcom,csi-lane-mask = <0x7>;
+		qcom,csi-phy-sel = <2>;
+		qcom,camera-type = <1>;
+		qcom,sensor-type = <0>;
+		status = "ok";
+	};
+};
diff --git a/arch/arm/boot/dts/msm8974-camera.dtsi b/arch/arm/boot/dts/msm8974-camera.dtsi
index fd652a0..48dd4dc 100644
--- a/arch/arm/boot/dts/msm8974-camera.dtsi
+++ b/arch/arm/boot/dts/msm8974-camera.dtsi
@@ -165,7 +165,14 @@
 		vdd-supply = <&gdsc_vfe>;
 	};
 
-	qcom,cci@fda0C000 {
+	led_flash0: qcom,camera-led-flash {
+		cell-index = <0>;
+		compatible = "qcom,camera-led-flash";
+		qcom,flash-type = <1>;
+		qcom,flash-source = <&pm8941_flash0 &pm8941_flash1>;
+	};
+
+	cci: qcom,cci@fda0C000 {
 		cell-index = <0>;
 		compatible = "qcom,cci";
 		reg = <0xfda0C000 0x1000>;
@@ -177,83 +184,11 @@
 		interrupt-names = "cci";
 
 		qcom,camera@6e {
-			compatible = "qcom,s5k3l1yx";
-			reg = <0x6e 0x0>;
-			qcom,csi-if = <1>;
-			qcom,csid-core = <0>;
-			qcom,flash-type = <0>;
-			qcom,mount-angle = <90>;
-			qcom,sensor-name = "s5k3l1yx";
-			cam_vdig-supply = <&pm8941_l3>;
-			cam_vana-supply = <&pm8941_l17>;
-			cam_vio-supply = <&pm8941_lvs3>;
-			cam_vaf-supply = <&pm8941_l23>;
-			qcom,cam-vreg-name = "cam_vdig", "cam_vana", "cam_vio",
-					     "cam_vaf";
-			qcom,cam-vreg-type = <0 0 1 0>;
-			qcom,cam-vreg-min-voltage = <1225000 2850000 0 3000000>;
-			qcom,cam-vreg-max-voltage = <1225000 2850000 0 3000000>;
-			qcom,cam-vreg-op-mode = <105000 80000 0 100000>;
-			qcom,gpio-no-mux = <0>;
-			gpios = <&msmgpio 15 0>,
-				<&msmgpio 19 0>,
-				<&msmgpio 20 0>,
-				<&msmgpio 90 0>;
-			qcom,gpio-common-tbl-num = <0 1 2>;
-			qcom,gpio-common-tbl-flags = <1 1 1>;
-			qcom,gpio-common-tbl-label = "CAMIF_MCLK",
-						     "CAMIF_I2C_DATA",
-						     "CAMIF_I2C_CLK";
-			qcom,gpio-req-tbl-num = <3>;
-			qcom,gpio-req-tbl-flags = <0>;
-			qcom,gpio-req-tbl-label = "CAM_RESET1";
-			qcom,gpio-set-tbl-num = <3 3>;
-			qcom,gpio-set-tbl-flags = <0 2>;
-			qcom,gpio-set-tbl-delay = <1000 30000>;
-			qcom,csi-lane-assign = <0x4320>;
-			qcom,csi-lane-mask = <0x1F>;
-			qcom,csi-phy-sel = <0>;
-			qcom,camera-type = <0>;
-			qcom,sensor-type = <0>;
+			status = "disable";
 		};
 
 		qcom,camera@6c {
-			compatible = "qcom,ov2720";
-			reg = <0x6c 0x0>;
-			qcom,csi-if = <1>;
-			qcom,csid-core = <0>;
-			qcom,flash-type = <0>;
-			qcom,mount-angle = <0>;
-			qcom,sensor-name = "ov2720";
-			cam_vdig-supply = <&pm8941_l3>;
-			cam_vana-supply = <&pm8941_l17>;
-			cam_vio-supply = <&pm8941_lvs3>;
-			qcom,cam-vreg-name = "cam_vdig", "cam_vana", "cam_vio";
-			qcom,cam-vreg-type = <0 0 1>;
-			qcom,cam-vreg-min-voltage = <1225000 2850000 0>;
-			qcom,cam-vreg-max-voltage = <1225000 2850000 0>;
-			qcom,cam-vreg-op-mode = <105000 80000 0>;
-			qcom,gpio-no-mux = <0>;
-			gpios = <&msmgpio 16 0>,
-				<&msmgpio 19 0>,
-				<&msmgpio 20 0>,
-				<&msmgpio 92 0>;
-			qcom,gpio-common-tbl-num = <0 1 2>;
-			qcom,gpio-common-tbl-flags = <1 1 1>;
-			qcom,gpio-common-tbl-label = "CAMIF_MCLK",
-						     "CAMIF_I2C_DATA",
-						     "CAMIF_I2C_CLK";
-			qcom,gpio-req-tbl-num = <3>;
-			qcom,gpio-req-tbl-flags = <0>;
-			qcom,gpio-req-tbl-label = "CAM_RESET1";
-			qcom,gpio-set-tbl-num = <3 3>;
-			qcom,gpio-set-tbl-flags = <0 2>;
-			qcom,gpio-set-tbl-delay = <1000 4000>;
-			qcom,csi-lane-assign = <0x4320>;
-			qcom,csi-lane-mask = <0x7>;
-			qcom,csi-phy-sel = <1>;
-			qcom,camera-type = <1>;
-			qcom,sensor-type = <0>;
+			status = "disable";
 		};
 
 		qcom,camera@90 {
@@ -261,7 +196,6 @@
 			reg = <0x90 0x0>;
 			qcom,csi-if = <1>;
 			qcom,csid-core = <0>;
-			qcom,flash-type = <0>;
 			qcom,mount-angle = <0>;
 			qcom,sensor-name = "mt9m114";
 			cam_vdig-supply = <&pm8941_l3>;
diff --git a/arch/arm/boot/dts/msm8974-cdp.dts b/arch/arm/boot/dts/msm8974-cdp.dts
index 0e0f6cf..b8b3141 100644
--- a/arch/arm/boot/dts/msm8974-cdp.dts
+++ b/arch/arm/boot/dts/msm8974-cdp.dts
@@ -13,356 +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";
-		};
-	};
-
-	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 = <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";
-		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,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..00e9c7a
--- /dev/null
+++ b/arch/arm/boot/dts/msm8974-cdp.dtsi
@@ -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.
+ */
+
+/include/ "dsi-panel-toshiba-720p-video.dtsi"
+/include/ "msm8974-leds.dtsi"
+/include/ "msm8974-camera-sensor.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>;
+		};
+	};
+
+	sound {
+		qcom,model = "msm8974-taiko-cdp-snd-card";
+	};
+};
+
+&spmi_bus {
+	qcom,pm8941@1 {
+		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 {
+			status = "disabled";
+		};
+
+		qcom,leds@da00 {
+			status = "disabled";
+		};
+
+		qcom,leds@db00 {
+			status = "disabled";
+		};
+
+		qcom,leds@dc00 {
+			status = "disabled";
+		};
+
+		qcom,leds@dd00 {
+			status = "disabled";
+		};
+
+		qcom,leds@de00 {
+			status = "disabled";
+		};
+
+		qcom,leds@df00 {
+			status = "disabled";
+		};
+
+		qcom,leds@e000 {
+			status = "disabled";
+		};
+
+		qcom,leds@e100 {
+			status = "disabled";
+		};
+	};
+};
+
+&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 891379f..b014e14 100644
--- a/arch/arm/boot/dts/msm8974-fluid.dts
+++ b/arch/arm/boot/dts/msm8974-fluid.dts
@@ -13,359 +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";
-		};
-	};
-
-	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 = <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";
-		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,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..cac7d3c
--- /dev/null
+++ b/arch/arm/boot/dts/msm8974-fluid.dtsi
@@ -0,0 +1,460 @@
+/* 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"
+/include/ "msm8974-camera-sensor.dtsi"
+/include/ "msm8974-leds.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
+					];
+			};
+		};
+	};
+
+	i2c@f9967000 {
+		sii8334@72 {
+			compatible = "qcom,mhl-sii8334";
+			reg = <0x72>;
+			interrupt-parent = <&msmgpio>;
+			interrupts = <82 0x8>;
+			mhl-intr-gpio = <&msmgpio 82 0>;
+			mhl-pwr-gpio = <&msmgpio 12 0>;
+			mhl-rst-gpio = <&pm8941_mpps 8 0>;
+			avcc_18-supply = <&pm8941_l24>;
+			avcc_12-supply = <&pm8941_l2>;
+			smps3a-supply = <&pm8941_s3>;
+			vdda-supply = <&pm8941_l12>;
+		};
+	};
+
+	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>;
+		};
+	};
+
+	sound {
+		qcom,model = "msm8974-taiko-fluid-snd-card";
+	};
+};
+
+&spmi_bus {
+	qcom,pm8941@1 {
+		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 {
+			status = "disabled";
+		};
+
+		qcom,leds@da00 {
+			status = "disabled";
+		};
+
+		qcom,leds@db00 {
+			status = "disabled";
+		};
+
+		qcom,leds@dc00 {
+			status = "disabled";
+		};
+
+		qcom,leds@dd00 {
+			status = "disabled";
+		};
+
+		qcom,leds@de00 {
+			status = "disabled";
+		};
+
+		qcom,leds@df00 {
+			status = "disabled";
+		};
+
+		qcom,leds@e000 {
+			status = "disabled";
+		};
+
+		qcom,leds@e100 {
+			status = "disabled";
+		};
+	};
+};
+
+&sdcc1 {
+	qcom,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 */
+		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@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 */
+		qcom,mode = <1>; /* DIG_OUT */
+		qcom,output-type = <0>; /* CMOS */
+		qcom,pull-up = <0>;
+		qcom,vin-sel = <2>; /* PM8941_S3 1.8V > 1.6V */
+		qcom,src-select = <0>; /* CONSTANT */
+		qcom,master-en = <1>; /* ENABLE MPP */
+	};
+};
+
+&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 4fe0eda..6623568 100644
--- a/arch/arm/boot/dts/msm8974-gpu.dtsi
+++ b/arch/arm/boot/dts/msm8974-gpu.dtsi
@@ -13,8 +13,9 @@
 	qcom,kgsl-3d0@fdb00000 {
 		label = "kgsl-3d0";
 		compatible = "qcom,kgsl-3d0", "qcom,kgsl-3d";
-		reg = <0xfdb00000 0x20000>;
-		reg-names = "kgsl_3d0_reg_memory";
+		reg = <0xfdb00000 0x10000
+		       0xfdb20000 0x10000>;
+		reg-names = "kgsl_3d0_reg_memory" , "kgsl_3d0_shader_memory";
 		interrupts = <0 33 0>;
 		interrupt-names = "kgsl_3d0_irq";
 		qcom,id = <0>;
@@ -29,12 +30,15 @@
 		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>;
diff --git a/arch/arm/boot/dts/msm8974-ion.dtsi b/arch/arm/boot/dts/msm8974-ion.dtsi
index 9b5aaac..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 */
diff --git a/arch/arm/boot/dts/msm8974-leds.dtsi b/arch/arm/boot/dts/msm8974-leds.dtsi
new file mode 100644
index 0000000..8ba3470
--- /dev/null
+++ b/arch/arm/boot/dts/msm8974-leds.dtsi
@@ -0,0 +1,104 @@
+/*
+ * 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 {
+	qcom,pm8941@1 {
+		qcom,leds@d000 {
+			status = "okay";
+			qcom,rgb_0 {
+				label = "rgb";
+				linux,name = "led:rgb_red";
+				qcom,mode = <0>;
+				qcom,pwm-channel = <6>;
+				qcom,pwm-us = <1000>;
+				qcom,max-current = <12>;
+				qcom,default-state = "off";
+				qcom,id = <3>;
+				linux,default-trigger =
+					"battery-charging";
+			};
+
+			qcom,rgb_1 {
+				label = "rgb";
+				linux,name = "led:rgb_green";
+				qcom,mode = <0>;
+				qcom,pwm-channel = <5>;
+				qcom,pwm-us = <1000>;
+				qcom,max-current = <12>;
+				qcom,default-state = "off";
+				qcom,id = <4>;
+				linux,default-trigger = "battery-full";
+			};
+		};
+
+		qcom,leds@d100 {
+			status = "disabled";
+		};
+
+		qcom,leds@d200 {
+			status = "disabled";
+		};
+
+		qcom,leds@d300 {
+			status = "okay";
+			pm8941_flash0: qcom,flash_0 {
+				qcom,max-current = <1000>;
+				qcom,default-state = "off";
+				qcom,headroom = <0>;
+				qcom,duration = <1280>;
+				qcom,clamp-curr = <200>;
+				qcom,startup-dly = <1>;
+				qcom,safety-timer;
+				label = "flash";
+				linux,default-trigger =
+					"flash0_trigger";
+				qcom,id = <1>;
+				linux,name = "led:flash_0";
+				qcom,current = <625>;
+			};
+
+			pm8941_flash1: qcom,flash_1 {
+				qcom,max-current = <1000>;
+				qcom,default-state = "off";
+				qcom,headroom = <0>;
+				qcom,duration = <1280>;
+				qcom,clamp-curr = <200>;
+				qcom,startup-dly = <1>;
+				qcom,safety-timer;
+				linux,default-trigger =
+					"flash1_trigger";
+				label = "flash";
+				qcom,id = <2>;
+				linux,name = "led:flash_1";
+				qcom,current = <625>;
+			};
+		};
+
+		qcom,leds@d400 {
+			status = "disabled";
+		};
+
+		qcom,leds@d500 {
+			status = "disabled";
+		};
+
+		qcom,leds@d600 {
+			status = "disabled";
+		};
+
+		qcom,leds@d700 {
+			status = "disabled";
+		};
+	};
+};
+
diff --git a/arch/arm/boot/dts/msm8974-liquid.dts b/arch/arm/boot/dts/msm8974-liquid.dts
index 6ccd933..ef38036 100644
--- a/arch/arm/boot/dts/msm8974-liquid.dts
+++ b/arch/arm/boot/dts/msm8974-liquid.dts
@@ -13,357 +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";
-	};
-
-	qcom,mdss_edp@fd923400 {
-		status = "ok";
-	};
-
-	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 = <1>;
-			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
-					];
-			};
-		};
-	};
-};
-
-&pm8941_gpios {
-	gpio@c000 { /* GPIO 1 */
-		qcom,mode = <0>;
-		qcom,pull = <0>;
-		qcom,vin-sel = <2>;
-		qcom,select = <0>;
-	};
-
-	gpio@c100 { /* GPIO 2 */
-		qcom,mode = <0>;
-		qcom,pull = <0>;
-		qcom,vin-sel = <2>;
-		qcom,select = <0>;
-	};
-
-	gpio@c200 { /* GPIO 3 */
-	};
-
-	gpio@c300 { /* GPIO 4 */
-	};
-
-	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 { /* 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-select = <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-select = <0>; /* CONSTANT */
-		qcom,master-en = <1>; /* ENABLE MPP */
-	};
 };
diff --git a/arch/arm/boot/dts/msm8974-liquid.dtsi b/arch/arm/boot/dts/msm8974-liquid.dtsi
new file mode 100644
index 0000000..7da00d3
--- /dev/null
+++ b/arch/arm/boot/dts/msm8974-liquid.dtsi
@@ -0,0 +1,523 @@
+/* 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/ "msm8974-leds.dtsi"
+/include/ "msm8974-camera-sensor-liquid.dtsi"
+
+/ {
+	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";
+
+		qcom,hdmi-tx-mux-sel = <&pm8841_mpps 3 0>;
+		qcom,hdmi-tx-mux-en = <&pm8841_mpps 4 0>;
+	};
+
+	drv2667_vreg: drv2667_vdd_vreg {
+		compatible = "regulator-fixed";
+		regulator-name = "vdd_drv2667";
+	};
+
+	i2c@f9967000 {
+		ti-drv2667@59 {
+			compatible = "ti,drv2667";
+			reg = <0x59>;
+			vdd-supply = <&drv2667_vreg>;
+			vdd-i2c-supply = <&pm8941_s3>;
+			ti,label = "vibrator";
+			ti,gain = <2>;
+			ti,idle-timeout-ms = <20>;
+			ti,max-runtime-ms = <15000>;
+			ti,mode = <2>;
+			ti,wav-seq = [
+				/* wave form id */
+				01
+				/* header size, start and stop bytes */
+				05 80 06 00 09
+				/* repeat, amp, freq, duration, envelope */
+				01 ff 19 02 00];
+		};
+	};
+
+	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>;
+		startup-delay-us = <12000>;
+		enable-active-high;
+	};
+
+	sound {
+		qcom,model = "msm8974-taiko-liquid-snd-card";
+
+		qcom,audio-routing =
+			"RX_BIAS", "MCLK",
+			"LDO_H", "MCLK",
+			"Lineout_1 amp", "LINEOUT1",
+			"Lineout_3 amp", "LINEOUT3",
+			"Lineout_2 amp", "LINEOUT2",
+			"Lineout_4 amp", "LINEOUT4",
+			"AMIC1", "MIC BIAS1 Internal1",
+			"MIC BIAS1 Internal1", "Handset Mic",
+			"AMIC2", "MIC BIAS2 External",
+			"MIC BIAS2 External", "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",
+			"MIC BIAS1 External", "Digital Mic2",
+			"DMIC3", "MIC BIAS3 External",
+			"MIC BIAS3 External", "Digital Mic3",
+			"DMIC4", "MIC BIAS3 External",
+			"MIC BIAS3 External", "Digital Mic4",
+			"DMIC5", "MIC BIAS4 External",
+			"MIC BIAS4 External", "Digital Mic5",
+			"DMIC6", "MIC BIAS4 External",
+			"MIC BIAS4 External", "Digital Mic6";
+
+		qcom,ext-spk-amp-supply = <&ext_5v>;
+		qcom,ext-spk-amp-gpio = <&pm8841_mpps 1 0>;
+	};
+};
+
+&usb3 {
+	qcom,charging-disabled;
+};
+
+&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 */
+		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,invert = <1>;
+		qcom,master-en = <1>;
+	};
+
+	gpio@e100 { /* GPIO 34 */
+		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,invert = <0>;
+		qcom,master-en = <1>;
+	};
+
+	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 */
+		/* CLASS_D_EN speakers PA */
+		qcom,mode = <1>; /* DIG_OUT */
+		qcom,output-type = <0>;  /* PNP_PIN_OUT_BUF_CMOS */
+		qcom,vin-sel = <2>; /* S3A 1.8v */
+		qcom,src-select = <0>; /* CONSTANT */
+		qcom,master-en = <1>; /* ENABLE MPP */
+	};
+
+	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 e791348..b765611 100644
--- a/arch/arm/boot/dts/msm8974-mdss.dtsi
+++ b/arch/arm/boot/dts/msm8974-mdss.dtsi
@@ -48,11 +48,10 @@
 		qcom,hdmi-tx-max-voltage-level = <0 1800000 1800000>;
 		qcom,hdmi-tx-op-mode = <0 1800000 0>;
 
-		gpios = <&msmgpio 31 0>,
-			<&msmgpio 32 0>,
-			<&msmgpio 33 0>,
-			<&msmgpio 34 0>;
-		qcom,hdmi-tx-gpio-names = "cec-pin", "hpd-ddc-clk", "hpd-ddc-data", "hpd-pin";
+		qcom,hdmi-tx-cec = <&msmgpio 31 0>;
+		qcom,hdmi-tx-ddc-clk = <&msmgpio 32 0>;
+		qcom,hdmi-tx-ddc-data = <&msmgpio 33 0>;
+		qcom,hdmi-tx-hpd = <&msmgpio 34 0>;
 	};
 
 	qcom,mdss_wb_panel {
@@ -68,6 +67,9 @@
 		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 f75ebbe..9946cf0 100644
--- a/arch/arm/boot/dts/msm8974-mtp.dts
+++ b/arch/arm/boot/dts/msm8974-mtp.dts
@@ -13,391 +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";
-		};
-	};
-
-	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 = <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";
-		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>;
-};
-
-&pm8941_chg {
-	status = "ok";
-
-	qcom,chg-chgr@1000 {
-		status = "ok";
-	};
-
-	qcom,chg-buck@1100 {
-		status = "ok";
-	};
-
-	qcom,chg-bat-if@1200 {
-		status = "ok";
-	};
-
-	qcom,chg-usb-chgpth@1300 {
-		status = "ok";
-	};
-
-	qcom,chg-dc-chgpth@1400 {
-		status = "ok";
-	};
-
-	qcom,chg-boost@1500 {
-		status = "ok";
-	};
-
-	qcom,chg-misc@1600 {
-		status = "ok";
-	};
-};
-
-&pm8941_gpios {
-	gpio@c000 { /* GPIO 1 */
-	};
-
-	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..cdb1710
--- /dev/null
+++ b/arch/arm/boot/dts/msm8974-mtp.dtsi
@@ -0,0 +1,479 @@
+/* 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"
+/include/ "msm8974-camera-sensor.dtsi"
+/include/ "msm8974-leds.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>;
+		};
+	};
+
+	sound {
+		qcom,model = "msm8974-taiko-mtp-snd-card";
+	};
+};
+
+&spmi_bus {
+	qcom,pm8941@1 {
+		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 {
+			status = "disabled";
+		};
+
+		qcom,leds@da00 {
+			status = "disabled";
+		};
+
+		qcom,leds@db00 {
+			status = "disabled";
+		};
+
+		qcom,leds@dc00 {
+			status = "disabled";
+		};
+
+		qcom,leds@dd00 {
+			status = "disabled";
+		};
+
+		qcom,leds@de00 {
+			status = "disabled";
+		};
+
+		qcom,leds@df00 {
+			status = "disabled";
+		};
+
+		qcom,leds@e000 {
+			status = "disabled";
+		};
+
+		qcom,leds@e100 {
+			status = "disabled";
+		};
+	};
+};
+
+&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,otg-capability;
+};
+
+&pm8941_bms {
+	status = "ok";
+};
+
+&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 97%
rename from arch/arm/boot/dts/msm8974_pm.dtsi
rename to arch/arm/boot/dts/msm8974-pm.dtsi
index c6cbca3..52f2a41 100644
--- a/arch/arm/boot/dts/msm8974_pm.dtsi
+++ b/arch/arm/boot/dts/msm8974-pm.dtsi
@@ -125,7 +125,7 @@
 		qcom,vctl-port = <0x0>;
 		qcom,phase-port = <0x1>;
 		qcom,pfm-port = <0x2>;
-		qcom,saw2-spm-cmd-ret = [00 20 03 22 00 0f];
+		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];
@@ -422,4 +422,17 @@
 		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;
+	};
+
+	qcom,rpm-stats@0xfc19dbd0{
+		compatible = "qcom,rpm-stats";
+		reg = <0xfc19dbd0 0x1000>;
+		reg-names = "phys_addr_base";
+		qcom,sleep-stats-version = <2>;
+	};
 };
diff --git a/arch/arm/boot/dts/msm8974-regulator.dtsi b/arch/arm/boot/dts/msm8974-regulator.dtsi
index de9e98c..3f7e9de 100644
--- a/arch/arm/boot/dts/msm8974-regulator.dtsi
+++ b/arch/arm/boot/dts/msm8974-regulator.dtsi
@@ -71,7 +71,6 @@
 
 	rpm-regulator-smpb2 {
 		status = "okay";
-		qcom,allow-atomic = <1>;
 		pm8841_s2: regulator-s2 {
 			regulator-min-microvolt = <500000>;
 			regulator-max-microvolt = <1050000>;
@@ -131,7 +130,6 @@
 
 	rpm-regulator-smpa2 {
 		status = "okay";
-		qcom,allow-atomic = <1>;
 		pm8941_s2: regulator-s2 {
 			regulator-min-microvolt = <2150000>;
 			regulator-max-microvolt = <2150000>;
@@ -284,7 +282,6 @@
 
 	rpm-regulator-ldoa12 {
 		status = "okay";
-		qcom,allow-atomic = <1>;
 		pm8941_l12: regulator-l12 {
 			parent-supply = <&pm8941_s2>;
 			regulator-min-microvolt = <1800000>;
@@ -456,6 +453,10 @@
 		reg = <0xf9088000 0x1000>;
 		regulator-min-microvolt = <500000>;
 		regulator-max-microvolt = <1100000>;
+		qcom,headroom-voltage = <150000>;
+		qcom,retention-voltage = <745000>;
+		qcom,ldo-default-voltage = <745000>;
+		qcom,ldo-threshold-voltage = <750000>;
 	};
 
 	krait1_vreg: regulator@f9098000 {
@@ -464,6 +465,10 @@
 		reg = <0xf9098000 0x1000>;
 		regulator-min-microvolt = <500000>;
 		regulator-max-microvolt = <1100000>;
+		qcom,headroom-voltage = <150000>;
+		qcom,retention-voltage = <745000>;
+		qcom,ldo-default-voltage = <745000>;
+		qcom,ldo-threshold-voltage = <750000>;
 	};
 
 	krait2_vreg: regulator@f90a8000 {
@@ -472,6 +477,10 @@
 		reg = <0xf90a8000 0x1000>;
 		regulator-min-microvolt = <500000>;
 		regulator-max-microvolt = <1100000>;
+		qcom,headroom-voltage = <150000>;
+		qcom,retention-voltage = <745000>;
+		qcom,ldo-default-voltage = <745000>;
+		qcom,ldo-threshold-voltage = <750000>;
 	};
 
 	krait3_vreg: regulator@f90b8000 {
@@ -480,6 +489,10 @@
 		reg = <0xf90b8000 0x1000>;
 		regulator-min-microvolt = <500000>;
 		regulator-max-microvolt = <1100000>;
+		qcom,headroom-voltage = <150000>;
+		qcom,retention-voltage = <745000>;
+		qcom,ldo-default-voltage = <745000>;
+		qcom,ldo-threshold-voltage = <750000>;
 	};
 
 	spi_eth_vreg: spi_eth_phy_vreg {
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..4919391
--- /dev/null
+++ b/arch/arm/boot/dts/msm8974-rumi.dtsi
@@ -0,0 +1,142 @@
+/* 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/ "msm8974-leds.dtsi"
+/include/ "msm8974-camera-sensor.dtsi"
+
+/ {
+	timer {
+		clock-frequency = <5000000>;
+	};
+
+	serial@f995e000 {
+		status = "ok";
+	};
+
+	usb@f9a55000 {
+		status = "disable";
+	};
+
+	qcom,sdcc@f9824000 {
+                qcom,clk-rates = <400000 19200000>;
+        };
+
+        qcom,sdcc@f98a4000 {
+                qcom,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";
+	};
+
+        qcom,kgsl-3d0@fdb00000 {
+		status = "disabled";
+	};
+};
+
+&gdsc_venus {
+        status = "disabled";
+};
+
+&gdsc_mdss {
+        status = "disabled";
+};
+
+&gdsc_jpeg {
+        status = "disabled";
+};
+
+&gdsc_vfe {
+        status = "disabled";
+};
+
+&gdsc_oxili_gx {
+        status = "disabled";
+};
+
+&gdsc_oxili_cx {
+        status = "disabled";
+};
+
+&gdsc_usb_hsic {
+        status = "disabled";
+};
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..fb638f7
--- /dev/null
+++ b/arch/arm/boot/dts/msm8974-sim.dtsi
@@ -0,0 +1,93 @@
+/* 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"
+/include/ "msm8974-leds.dtsi"
+/include/ "msm8974-camera-sensor.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 8d54585..6d5f3cb 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"
@@ -52,7 +52,7 @@
 	};
 
 	timer {
-		compatible = "qcom,msm-qtimer", "arm,armv7-timer";
+		compatible = "arm,armv7-timer";
 		interrupts = <1 2 0 1 3 0>;
 		clock-frequency = <19200000>;
 	};
@@ -110,18 +110,19 @@
 		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 {
 		compatible = "qcom,android-usb";
 		reg = <0xfc42b0c8 0xc8>;
+		qcom,android-usb-swfi-latency = <1>;
 	};
 
 	sdcc1: qcom,sdcc@f9824000 {
@@ -136,25 +137,39 @@
 		vdd-supply = <&pm8941_l20>;
 		vdd-io-supply = <&pm8941_s3>;
 
-		qcom,sdcc-vdd-always_on;
-		qcom,sdcc-vdd-lpm_sup;
-		qcom,sdcc-vdd-voltage_level = <2950000 2950000>;
-		qcom,sdcc-vdd-current_level = <800 500000>;
+		qcom,vdd-always-on;
+		qcom,vdd-lpm-sup;
+		qcom,vdd-voltage-level = <2950000 2950000>;
+		qcom,vdd-current-level = <800 500000>;
 
-		qcom,sdcc-vdd-io-always_on;
-		qcom,sdcc-vdd-io-voltage_level = <1800000 1800000>;
-		qcom,sdcc-vdd-io-current_level = <250 154000>;
+		qcom,vdd-io-always-on;
+		qcom,vdd-io-voltage-level = <1800000 1800000>;
+		qcom,vdd-io-current-level = <250 154000>;
 
-		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,pad-pull-on = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
+		qcom,pad-pull-off = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
+		qcom,pad-drv-on = <0x7 0x4 0x4>; /* 16mA, 10mA, 10mA */
+		qcom,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";
+		qcom,clk-rates = <400000 25000000 50000000 100000000 200000000>;
+		qcom,sup-voltages = <2950 2950>;
+		qcom,bus-width = <8>;
+		qcom,nonremovable;
+		qcom,bus-speed-mode = "HS200_1p8v", "DDR_1p8v";
+
+		qcom,msm-bus,name = "sdcc1";
+		qcom,msm-bus,num-cases = <7>;
+		qcom,msm-bus,active-only = <0>;
+		qcom,msm-bus,num-paths = <1>;
+		qcom,msm-bus,vectors-KBps = <78 512 0 0>, /* No vote */
+				<78 512 6656 13312>, /* 13 MB/s*/
+				<78 512 13312 26624>, /* 26 MB/s */
+				<78 512 26624 53248>, /* 52 MB/s */
+				<78 512 53248 106496>, /* 104 MB/s */
+				<78 512 106496 212992>, /* 208 MB/s */
+				<78 512 2147483647 4294967295>; /* Max. bandwidth */
+		qcom,bus-bw-vectors-bps = <0 13631488 27262976 54525952 109051904 218103808 4294967295>;
+		qcom,dat1-mpm-int = <42>;
 	};
 
 	sdcc2: qcom,sdcc@f98a4000 {
@@ -169,25 +184,39 @@
 		vdd-supply = <&pm8941_l21>;
 		vdd-io-supply = <&pm8941_l13>;
 
-		qcom,sdcc-vdd-voltage_level = <2950000 2950000>;
-		qcom,sdcc-vdd-current_level = <9000 800000>;
+		qcom,vdd-voltage-level = <2950000 2950000>;
+		qcom,vdd-current-level = <9000 800000>;
 
-		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,vdd-io-always-on;
+		qcom,vdd-io-lpm-sup;
+		qcom,vdd-io-voltage-level = <1800000 2950000>;
+		qcom,vdd-io-current-level = <6 22000>;
 
-		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,pad-pull-on = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
+		qcom,pad-pull-off = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
+		qcom,pad-drv-on = <0x7 0x4 0x4>; /* 16mA, 10mA, 10mA */
+		qcom,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>;
+		qcom,clk-rates = <400000 25000000 50000000 100000000 200000000>;
+		qcom,sup-voltages = <2950 2950>;
+		qcom,bus-width = <4>;
+		qcom,xpc;
+		qcom,bus-speed-mode = "SDR12", "SDR25", "SDR50", "DDR50", "SDR104";
+		qcom,current-limit = <800>;
+
+		qcom,msm-bus,name = "sdcc2";
+		qcom,msm-bus,num-cases = <7>;
+		qcom,msm-bus,active-only = <0>;
+		qcom,msm-bus,num-paths = <1>;
+		qcom,msm-bus,vectors-KBps = <81 512 0 0>, /* No vote */
+				<81 512 6656 13312>, /* 13 MB/s*/
+				<81 512 13312 26624>, /* 26 MB/s */
+				<81 512 26624 53248>, /* 52 MB/s */
+				<81 512 53248 106496>, /* 104 MB/s */
+				<81 512 106496 212992>, /* 208 MB/s */
+				<81 512 2147483647 4294967295>; /* Max. bandwidth */
+		qcom,bus-bw-vectors-bps = <0 13631488 27262976 54525952 109051904 218103808 4294967295>;
+		qcom,dat1-mpm-int = <44>;
 	};
 
 	sdcc3: qcom,sdcc@f9864000 {
@@ -197,8 +226,15 @@
 			<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";
+		#address-cells = <0>;
+		interrupt-parent = <&sdcc3>;
+		interrupts = <0 1 2>;
+		#interrupt-cells = <1>;
+		interrupt-map-mask = <0xffffffff>;
+		interrupt-map = <0 &intc 0 127 0
+				1 &intc 0 223 0
+				2 &msmgpio 37 0x8>;
+		interrupt-names = "core_irq", "bam_irq", "sdiowakeup_irq";
 
 		gpios = <&msmgpio 40 0>, /* CLK */
 			<&msmgpio 39 0>, /* CMD */
@@ -206,12 +242,25 @@
 			<&msmgpio 37 0>, /* DATA1 */
 			<&msmgpio 36 0>, /* DATA2 */
 			<&msmgpio 35 0>; /* DATA3 */
-		qcom,sdcc-gpio-names = "CLK", "CMD", "DAT0", "DAT1", "DAT2", "DAT3";
+		qcom,gpio-names = "CLK", "CMD", "DAT0", "DAT1", "DAT2", "DAT3";
 
-		qcom,sdcc-clk-rates = <400000 25000000 50000000 100000000>;
-		qcom,sdcc-sup-voltages = <1800 1800>;
-		qcom,sdcc-bus-width = <4>;
-		qcom,sdcc-bus-speed-mode = "SDR12", "SDR25", "SDR50", "DDR50";
+		qcom,clk-rates = <400000 25000000 50000000 100000000>;
+		qcom,sup-voltages = <1800 1800>;
+		qcom,bus-width = <4>;
+		qcom,bus-speed-mode = "SDR12", "SDR25", "SDR50", "DDR50";
+
+		qcom,msm-bus,name = "sdcc3";
+		qcom,msm-bus,num-cases = <7>;
+		qcom,msm-bus,active-only = <0>;
+		qcom,msm-bus,num-paths = <1>;
+		qcom,msm-bus,vectors-KBps = <79 512 0 0>, /* No vote */
+				<79 512 6656 13312>, /* 13 MB/s*/
+				<79 512 13312 26624>, /* 26 MB/s */
+				<79 512 26624 53248>, /* 52 MB/s */
+				<79 512 53248 106496>, /* 104 MB/s */
+				<79 512 106496 212992>, /* 208 MB/s */
+				<79 512 2147483647 4294967295>; /* Max. bandwidth */
+		qcom,bus-bw-vectors-bps = <0 13631488 27262976 54525952 109051904 218103808 4294967295>;
 		status = "disable";
 	};
 
@@ -222,8 +271,15 @@
 			<0xf98e4800 0x100>,
 			<0xf98c4000 0x7000>;
 		reg-names = "core_mem", "dml_mem", "bam_mem";
-		interrupts = <0 129 0>, <0 226 0>;
-		interrupt-names = "core_irq", "bam_irq";
+		#address-cells = <0>;
+		interrupt-parent = <&sdcc4>;
+		interrupts = <0 1 2>;
+		#interrupt-cells = <1>;
+		interrupt-map-mask = <0xffffffff>;
+		interrupt-map = <0 &intc 0 129 0
+				1 &intc 0 226 0
+				2 &msmgpio 95 0x8>;
+		interrupt-names = "core_irq", "bam_irq", "sdiowakeup_irq";
 
 		gpios = <&msmgpio 93 0>, /* CLK */
 			<&msmgpio 91 0>, /* CMD */
@@ -231,12 +287,25 @@
 			<&msmgpio 95 0>, /* DATA1 */
 			<&msmgpio 94 0>, /* DATA2 */
 			<&msmgpio 92 0>; /* DATA3 */
-		qcom,sdcc-gpio-names = "CLK", "CMD", "DAT0", "DAT1", "DAT2", "DAT3";
+		qcom,gpio-names = "CLK", "CMD", "DAT0", "DAT1", "DAT2", "DAT3";
 
-		qcom,sdcc-clk-rates = <400000 25000000 50000000 100000000>;
-		qcom,sdcc-sup-voltages = <1800 1800>;
-		qcom,sdcc-bus-width = <4>;
-		qcom,sdcc-bus-speed-mode = "SDR12", "SDR25", "SDR50", "DDR50";
+		qcom,clk-rates = <400000 25000000 50000000 100000000>;
+		qcom,sup-voltages = <1800 1800>;
+		qcom,bus-width = <4>;
+		qcom,bus-speed-mode = "SDR12", "SDR25", "SDR50", "DDR50";
+
+		qcom,msm-bus,name = "sdcc4";
+		qcom,msm-bus,num-cases = <7>;
+		qcom,msm-bus,active-only = <0>;
+		qcom,msm-bus,num-paths = <1>;
+		qcom,msm-bus,vectors-KBps = <80 512 0 0>, /* No vote */
+				<80 512 6656 13312>, /* 13 MB/s*/
+				<80 512 13312 26624>, /* 26 MB/s */
+				<80 512 26624 53248>, /* 52 MB/s */
+				<80 512 53248 106496>, /* 104 MB/s */
+				<80 512 106496 212992>, /* 208 MB/s */
+				<80 512 2147483647 4294967295>; /* Max. bandwidth */
+		qcom,bus-bw-vectors-bps = <0 13631488 27262976 54525952 109051904 218103808 4294967295>;
 		status = "disable";
 	};
 
@@ -261,18 +330,33 @@
 			<&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";
+		compatible = "qcom,slim-ngd";
 		reg = <0xfe12f000 0x35000>,
 		      <0xfe104000 0x20000>;
 		reg-names = "slimbus_physical", "slimbus_bam_physical";
 		interrupts = <0 163 0 0 164 0>;
 		interrupt-names = "slimbus_irq", "slimbus_bam_irq";
-		qcom,min-clk-gear = <10>;
-		qcom,rxreg-access;
 
 		taiko_codec {
 			compatible = "qcom,taiko-slim-pgd";
@@ -332,18 +416,14 @@
 		qcom,audio-routing =
 			"RX_BIAS", "MCLK",
 			"LDO_H", "MCLK",
-			"Ext Spk Bottom Pos", "LINEOUT1",
-			"Ext Spk Bottom Neg", "LINEOUT3",
-			"Ext Spk Top Pos", "LINEOUT2",
-			"Ext Spk Top Neg", "LINEOUT4",
 			"AMIC1", "MIC BIAS1 Internal1",
 			"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",
@@ -508,66 +588,14 @@
 					 <0x1e50008a>, /* LPG_CHAN10 */
 					 <0x1e60008b>, /* LPG_CHAN11 */
 					 <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>;
-				status = "okay";
-			};
-
-			qcom,leds@d900 {
-				status = "disabled";
-			};
-
-			qcom,leds@da00 {
-				status = "disabled";
-			};
-
-			qcom,leds@db00 {
-				status = "disabled";
-			};
-
-			qcom,leds@dc00 {
-				status = "disabled";
-			};
-
-			qcom,leds@dd00 {
-				status = "disabled";
-			};
-
-			qcom,leds@de00 {
-				status = "disabled";
-			};
-
-			qcom,leds@df00 {
-				status = "disabled";
-			};
-
-			qcom,leds@e000 {
-				status = "disabled";
-			};
-
-			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";
@@ -628,26 +656,28 @@
 		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";
-		SSUSB_VDDCX-supply = <&pm8841_s2>;
+		interrupts = <0 131 0>, <0 179 0>, <0 133 0>;
+		interrupt-names = "irq", "otg_irq", "hs_phy_irq";
+		ssusb_vdd_dig-supply = <&pm8841_s2_corner>;
 		SSUSB_1p8-supply = <&pm8941_l6>;
-		HSUSB_VDDCX-supply = <&pm8841_s2>;
+		hsusb_vdd_dig-supply = <&pm8841_s2_corner>;
 		HSUSB_1p8-supply = <&pm8941_l6>;
 		HSUSB_3p3-supply = <&pm8941_l24>;
+		vbus_dwc3-supply = <&pm8941_mvs1>;
 		qcom,dwc-usb3-msm-dbm-eps = <4>;
+		qcom,vdd-voltage-level = <1 5 7>;
 
-		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 {
@@ -666,6 +696,7 @@
 
 	qcom,msm-adsp-loader {
 		compatible = "qcom,adsp-loader";
+		qcom,adsp-state = <0>;
 	};
 
 	qcom,msm-pcm {
@@ -721,6 +752,36 @@
 			qcom,msm-dai-q6-dev-id = <16385>;
 		};
 
+		qcom,msm-dai-q6-sb-1-rx {
+			compatible = "qcom,msm-dai-q6-dev";
+			qcom,msm-dai-q6-dev-id = <16386>;
+		};
+
+		qcom,msm-dai-q6-sb-1-tx {
+			compatible = "qcom,msm-dai-q6-dev";
+			qcom,msm-dai-q6-dev-id = <16387>;
+		};
+
+		qcom,msm-dai-q6-sb-3-rx {
+			compatible = "qcom,msm-dai-q6-dev";
+			qcom,msm-dai-q6-dev-id = <16390>;
+		};
+
+		qcom,msm-dai-q6-sb-3-tx {
+			compatible = "qcom,msm-dai-q6-dev";
+			qcom,msm-dai-q6-dev-id = <16391>;
+		};
+
+		qcom,msm-dai-q6-sb-4-rx {
+			compatible = "qcom,msm-dai-q6-dev";
+			qcom,msm-dai-q6-dev-id = <16392>;
+		};
+
+		qcom,msm-dai-q6-sb-4-tx {
+			compatible = "qcom,msm-dai-q6-dev";
+			qcom,msm-dai-q6-dev-id = <16393>;
+		};
+
 		qcom,msm-dai-q6-bt-sco-rx {
 			compatible = "qcom,msm-dai-q6-dev";
 			qcom,msm-dai-q6-dev-id = <12288>;
@@ -784,19 +845,29 @@
 		};
 	};
 
+	qcom,msm-dai-mi2s {
+		compatible = "qcom,msm-dai-mi2s";
+		qcom,msm-dai-q6-mi2s-quat {
+			compatible = "qcom,msm-dai-q6-mi2s";
+			qcom,msm-dai-q6-mi2s-dev-id = <3>;
+			qcom,msm-mi2s-rx-lines = <1>;
+			qcom,msm-mi2s-tx-lines = <2>;
+		};
+	};
+
 	qcom,msm-pcm-hostless {
 		compatible = "qcom,msm-pcm-hostless";
 	};
 
 	qcom,msm-ocmem-audio {
 		compatible = "qcom,msm-ocmem-audio";
-		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 =
+		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 32505856 32505856>;
+			<11 604 32506 32506>;
 	};
 
 	qcom,mss@fc880000 {
@@ -805,27 +876,18 @@
 		      <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,is-loadable;
 		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";
-		interrupts = <0 24 1>;
-
-		qcom,firmware-name = "modem";
-		qcom,depends-on    = "mba";
-	};
-
 	qcom,pronto@fb21b000 {
 		compatible = "qcom,pil-pronto";
 		reg = <0xfb21b000 0x3000>,
@@ -917,15 +979,17 @@
 
 	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 =
+		reg = <0x7f00000 0x500000>;
+		reg-names = "secapp-region";
+		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 3936000000 393600000>,
-				<55 512 3936000000 393600000>,
-				<55 512 3936000000 393600000>;
+				<55 512 3936000 393600>,
+				<55 512 3936000 393600>,
+				<55 512 3936000 393600>;
 	};
 
 	qcom,wdt@f9017000 {
@@ -950,8 +1014,6 @@
 		vdd-supply = <&gdsc_venus>;
 
 		qcom,firmware-name = "venus";
-		qcom,firmware-min-paddr = <0xF500000>;
-		qcom,firmware-max-paddr = <0xFA00000>;
 	};
 
 	qcom,cache_erp {
@@ -986,6 +1048,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>,
@@ -1041,7 +1109,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>;
 		};
@@ -1094,7 +1162,106 @@
                compatible = "qcom,msm-wdog-debug";
                reg = <0xfc401000 0x1000>;
         };
+        qcom,msm-mem-hole {
+                compatible = "qcom,msm-mem-hole";
+                qcom,memblock-remove = <0x7f00000 0x8000000>; /* Address and Size of Hole */
+        };
 
+	qcom,smem@fa00000 {
+		compatible = "qcom,smem";
+		reg = <0xfa00000 0x200000>,
+			<0xfa006000 0x1000>,
+			<0xfc428000 0x4000>;
+		reg-names = "smem", "irq-reg-base", "aux-mem1";
+
+		qcom,smd-modem {
+			compatible = "qcom,smd";
+			qcom,smd-edge = <0>;
+			qcom,smd-irq-offset = <0x8>;
+			qcom,smd-irq-bitmask = <0x1000>;
+			qcom,pil-string = "modem";
+			interrupts = <0 25 1>;
+		};
+
+		qcom,smsm-modem {
+			compatible = "qcom,smsm";
+			qcom,smsm-edge = <0>;
+			qcom,smsm-irq-offset = <0x8>;
+			qcom,smsm-irq-bitmask = <0x2000>;
+			interrupts = <0 26 1>;
+		};
+
+		qcom,smd-adsp {
+			compatible = "qcom,smd";
+			qcom,smd-edge = <1>;
+			qcom,smd-irq-offset = <0x8>;
+			qcom,smd-irq-bitmask = <0x100>;
+			qcom,pil-string = "adsp";
+			interrupts = <0 156 1>;
+		};
+
+		qcom,smsm-adsp {
+			compatible = "qcom,smsm";
+			qcom,smsm-edge = <1>;
+			qcom,smsm-irq-offset = <0x8>;
+			qcom,smsm-irq-bitmask = <0x200>;
+			interrupts = <0 157 1>;
+		};
+
+		qcom,smd-wcnss {
+			compatible = "qcom,smd";
+			qcom,smd-edge = <6>;
+			qcom,smd-irq-offset = <0x8>;
+			qcom,smd-irq-bitmask = <0x20000>;
+			qcom,pil-string = "wcnss";
+			interrupts = <0 142 1>;
+		};
+
+		qcom,smsm-wcnss {
+			compatible = "qcom,smsm";
+			qcom,smsm-edge = <6>;
+			qcom,smsm-irq-offset = <0x8>;
+			qcom,smsm-irq-bitmask = <0x80000>;
+			interrupts = <0 144 1>;
+		};
+
+		qcom,smd-rpm {
+			compatible = "qcom,smd";
+			qcom,smd-edge = <15>;
+			qcom,smd-irq-offset = <0x8>;
+			qcom,smd-irq-bitmask = <0x1>;
+			interrupts = <0 168 1>;
+			qcom,irq-no-suspend;
+		};
+	};
+};
+
+&gdsc_venus {
+	status = "ok";
+};
+
+&gdsc_mdss {
+	status = "ok";
+};
+
+&gdsc_jpeg {
+	status = "ok";
+};
+
+&gdsc_vfe {
+	status = "ok";
+};
+
+&gdsc_oxili_gx {
+	status = "ok";
+};
+
+&gdsc_oxili_cx {
+	status = "ok";
+};
+
+&gdsc_usb_hsic {
+	status = "ok";
 };
 
 /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 89c269e..232fba7 100644
--- a/arch/arm/boot/dts/msm9625-cdp.dts
+++ b/arch/arm/boot/dts/msm9625-cdp.dts
@@ -18,6 +18,33 @@
 	model = "Qualcomm MSM 9625 CDP";
 	compatible = "qcom,msm9625-cdp", "qcom,msm9625";
 	qcom,msm-id = <134 1 0>, <152 1 0>;
+
+	i2c@f9925000 {
+		charger@57 {
+			compatible = "summit,smb137c";
+			reg = <0x57>;
+			summit,chg-current-ma = <1500>;
+			summit,term-current-ma = <50>;
+			summit,pre-chg-current-ma = <100>;
+			summit,float-voltage-mv = <4200>;
+			summit,thresh-voltage-mv = <3000>;
+			summit,recharge-thresh-mv = <75>;
+			summit,system-voltage-mv = <4250>;
+			summit,charging-timeout = <382>;
+			summit,pre-charge-timeout = <48>;
+			summit,therm-current-ua = <10>;
+			summit,temperature-min = <4>; /*  0 C */
+			summit,temperature-max = <3>; /* 45 C */
+		};
+	};
+
+	wlan0: qca,wlan {
+		cell-index = <0>;
+		compatible = "qca,ar6004-sdio";
+		qca,chip-pwd-l-gpios = <&msmgpio 62 0>;
+		qca,pm-enable-gpios = <&pm8019_gpios 3 0x0>;
+		qca,ar6004-vdd-io-supply = <&pm8019_l11>;
+	};
 };
 
 /* PM8019 GPIO and MPP configuration */
@@ -38,7 +65,7 @@
 		qcom,invert = <0>; /* Output low */
 		qcom,out-strength = <1>; /* Low */
 		qcom,vin-sel = <2>; /* PM8019 L11 - 1.8V */
-		qcom,src-select = <0>; /* Constant */
+		qcom,src-sel = <0>; /* Constant */
 		qcom,master-en = <1>; /* Enable GPIO */
 	};
 
diff --git a/arch/arm/boot/dts/msm9625-coresight.dtsi b/arch/arm/boot/dts/msm9625-coresight.dtsi
new file mode 100644
index 0000000..f01fe63
--- /dev/null
+++ b/arch/arm/boot/dts/msm9625-coresight.dtsi
@@ -0,0 +1,118 @@
+/* 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.
+ */
+
+/ {
+	tmc_etr: tmc@fc322000 {
+		compatible = "arm,coresight-tmc";
+		reg = <0xfc322000 0x1000>,
+		      <0xfc37c000 0x3000>;
+
+		qcom,memory-reservation-type = "EBI1";
+		qcom,memory-reservation-size = <0x100000>; /* 1M EBI1 buffer */
+
+		coresight-id = <0>;
+		coresight-name = "coresight-tmc-etr";
+		coresight-nr-inports = <1>;
+	};
+
+	tpiu: tpiu@fc318000 {
+		compatible = "arm,coresight-tpiu";
+		reg = <0xfc318000 0x1000>;
+
+		coresight-id = <1>;
+		coresight-name = "coresight-tpiu";
+		coresight-nr-inports = <1>;
+	};
+
+	replicator: replicator@fc31c000 {
+		compatible = "qcom,coresight-replicator";
+		reg = <0xfc31c000 0x1000>;
+
+		coresight-id = <2>;
+		coresight-name = "coresight-replicator";
+		coresight-nr-inports = <1>;
+		coresight-outports = <0 1>;
+		coresight-child-list = <&tmc_etr &tpiu>;
+		coresight-child-ports = <0 0>;
+	};
+
+	tmc_etf: tmc@fc307000 {
+		compatible = "arm,coresight-tmc";
+		reg = <0xfc307000 0x1000>;
+
+		coresight-id = <3>;
+		coresight-name = "coresight-tmc-etf";
+		coresight-nr-inports = <1>;
+		coresight-outports = <0>;
+		coresight-child-list = <&replicator>;
+		coresight-child-ports = <0>;
+		coresight-default-sink;
+	};
+
+	funnel_merg: funnel@fc31b000 {
+		compatible = "arm,coresight-funnel";
+		reg = <0xfc31b000 0x1000>;
+
+		coresight-id = <4>;
+		coresight-name = "coresight-funnel-merg";
+		coresight-nr-inports = <2>;
+		coresight-outports = <0>;
+		coresight-child-list = <&tmc_etf>;
+		coresight-child-ports = <0>;
+	};
+
+	funnel_in0: funnel@fc319000 {
+		compatible = "arm,coresight-funnel";
+		reg = <0xfc319000 0x1000>;
+
+		coresight-id = <5>;
+		coresight-name = "coresight-funnel-in0";
+		coresight-nr-inports = <8>;
+		coresight-outports = <0>;
+		coresight-child-list = <&funnel_merg>;
+		coresight-child-ports = <0>;
+	};
+
+	funnel_in1: funnel@fc31a000 {
+		compatible = "arm,coresight-funnel";
+		reg = <0xfc31a000 0x1000>;
+
+		coresight-id = <6>;
+		coresight-name = "coresight-funnel-in1";
+		coresight-nr-inports = <8>;
+		coresight-outports = <0>;
+		coresight-child-list = <&funnel_merg>;
+		coresight-child-ports = <1>;
+	};
+
+	stm: stm@fc321000 {
+		compatible = "arm,coresight-stm";
+		reg = <0xfc321000 0x1000>,
+		      <0xfa280000 0x180000>;
+
+		coresight-id = <7>;
+		coresight-name = "coresight-stm";
+		coresight-nr-inports = <0>;
+		coresight-outports = <0>;
+		coresight-child-list = <&funnel_in1>;
+		coresight-child-ports = <7>;
+	};
+
+	csr: csr@fc302000 {
+		compatible = "qcom,coresight-csr";
+		reg = <0xfc302000 0x1000>;
+
+		coresight-id = <8>;
+		coresight-name = "coresight-csr";
+		coresight-nr-inports = <0>;
+	};
+};
diff --git a/arch/arm/boot/dts/msm9625-mtp.dts b/arch/arm/boot/dts/msm9625-mtp.dts
index a5673e5..faf86d4 100644
--- a/arch/arm/boot/dts/msm9625-mtp.dts
+++ b/arch/arm/boot/dts/msm9625-mtp.dts
@@ -18,6 +18,33 @@
 	model = "Qualcomm MSM 9625 MTP";
 	compatible = "qcom,msm9625-mtp", "qcom,msm9625";
 	qcom,msm-id = <134 7 0>, <152 7 0>;
+
+	i2c@f9925000 {
+		charger@57 {
+			compatible = "summit,smb137c";
+			reg = <0x57>;
+			summit,chg-current-ma = <1500>;
+			summit,term-current-ma = <50>;
+			summit,pre-chg-current-ma = <100>;
+			summit,float-voltage-mv = <4200>;
+			summit,thresh-voltage-mv = <3000>;
+			summit,recharge-thresh-mv = <75>;
+			summit,system-voltage-mv = <4250>;
+			summit,charging-timeout = <382>;
+			summit,pre-charge-timeout = <48>;
+			summit,therm-current-ua = <10>;
+			summit,temperature-min = <4>; /*  0 C */
+			summit,temperature-max = <3>; /* 45 C */
+		};
+	};
+
+	wlan0: qca,wlan {
+		cell-index = <0>;
+		compatible = "qca,ar6004-sdio";
+		qca,chip-pwd-l-gpios = <&msmgpio 62 0>;
+		qca,pm-enable-gpios = <&pm8019_gpios 3 0x0>;
+		qca,ar6004-vdd-io-supply = <&pm8019_l11>;
+	};
 };
 
 /* PM8019 GPIO and MPP configuration */
@@ -38,7 +65,7 @@
 		qcom,invert = <0>; /* Output low */
 		qcom,out-strength = <1>; /* Low */
 		qcom,vin-sel = <2>; /* PM8019 L11 - 1.8V */
-		qcom,src-select = <0>; /* Constant */
+		qcom,src-sel = <0>; /* Constant */
 		qcom,master-en = <1>; /* Enable GPIO */
 	};
 
diff --git a/arch/arm/boot/dts/msm9625-pm.dtsi b/arch/arm/boot/dts/msm9625-pm.dtsi
new file mode 100644
index 0000000..2839864
--- /dev/null
+++ b/arch/arm/boot/dts/msm9625-pm.dtsi
@@ -0,0 +1,229 @@
+/* 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 = <1>;          /* ON */
+			qcom,l2 = <0>;          /* GDHS */
+			qcom,vdd-mem-upper-bound = <1050000>; /* SUPER TURBO */
+			qcom,vdd-mem-lower-bound = <950000>; /* SVS SOC */
+			qcom,vdd-dig-upper-bound = <4>; /* NORMAL */
+			qcom,vdd-dig-lower-bound = <3>;  /* SVS SOC */
+			qcom,latency-us = <4500>;
+			qcom,ss-power = <5000>;
+			qcom,energy-overhead = <60350000>;
+			qcom,time-overhead = <7300>;
+		};
+
+		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 = <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@5 {
+			reg = <0x5>;
+			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;
+	};
+
+	qcom,rpm-stats@fc19dbd0 {
+		compatible = "qcom,rpm-stats";
+		reg = <0xfc19dbd0 0x1000>;
+		reg-names = "phys_addr_base";
+		qcom,sleep-stats-version = <2>;
+	};
+};
diff --git a/arch/arm/boot/dts/msm9625-regulator.dtsi b/arch/arm/boot/dts/msm9625-regulator.dtsi
index dccc723..24f616d 100644
--- a/arch/arm/boot/dts/msm9625-regulator.dtsi
+++ b/arch/arm/boot/dts/msm9625-regulator.dtsi
@@ -23,7 +23,6 @@
 
 	rpm-regulator-smpa2 {
 		status = "okay";
-		qcom,allow-atomic = <1>;
 		pm8019_s2: regulator-s2 {
 			regulator-min-microvolt = <1250000>;
 			regulator-max-microvolt = <1250000>;
@@ -215,7 +214,7 @@
 		status = "okay";
 		pm8019_l12: regulator-l12 {
 			parent-supply = <&pm8019_s3>;
-			regulator-min-microvolt = <750000>;
+			regulator-min-microvolt = <675000>;
 			regulator-max-microvolt = <1050000>;
 			status = "okay";
 		};
@@ -224,10 +223,20 @@
 			regulator-name = "8019_l12_ao";
 			qcom,set = <1>;
 			parent-supply = <&pm8019_s3_ao>;
-			regulator-min-microvolt = <750000>;
+			regulator-min-microvolt = <675000>;
 			regulator-max-microvolt = <1050000>;
 			status = "okay";
 		};
+		pm8019_l12_so: regulator-l12-so {
+			compatible = "qcom,rpm-regulator-smd";
+			regulator-name = "8019_l12_so";
+			qcom,set = <2>;
+			parent-supply = <&pm8019_s3>;
+			regulator-min-microvolt = <675000>;
+			regulator-max-microvolt = <1050000>;
+			qcom,init-voltage = <675000>;
+			status = "okay";
+		};
 	};
 
 	rpm-regulator-ldoa13 {
@@ -258,4 +267,9 @@
 		gpio = <&pm8019_gpios 4 0>;
 		enable-active-high;
 	};
+
+	usb_vbus: regulator-usb-vbus {
+		compatible = "regulator-fixed";
+		regulator-name = "usb_vbus";
+	};
 };
diff --git a/arch/arm/boot/dts/msm9625.dtsi b/arch/arm/boot/dts/msm9625.dtsi
index 90e1118..7462911 100644
--- a/arch/arm/boot/dts/msm9625.dtsi
+++ b/arch/arm/boot/dts/msm9625.dtsi
@@ -12,6 +12,8 @@
 
 /include/ "skeleton.dtsi"
 /include/ "msm9625-ion.dtsi"
+/include/ "msm9625-pm.dtsi"
+/include/ "msm9625-coresight.dtsi"
 
 / {
 	model = "Qualcomm MSM 9625";
@@ -43,7 +45,7 @@
 	};
 
 	timer: msm-qtimer@f9021000 {
-		compatible = "qcom,msm-qtimer", "arm,armv7-timer";
+		compatible = "arm,armv7-timer";
 		reg = <0xF9021000 0x1000>;
 		interrupts = <0 7 0>;
 		irq-is-not-percpu;
@@ -73,6 +75,7 @@
 		HSUSB_VDDCX-supply = <&pm8019_l12>;
 		HSUSB_1p8-supply = <&pm8019_l2>;
 		HSUSB_3p3-supply = <&pm8019_l4>;
+		vbus_otg-supply = <&usb_vbus>;
 
 		qcom,hsusb-otg-phy-type = <2>;
 		qcom,hsusb-otg-mode = <1>;
@@ -85,6 +88,47 @@
 		reg = <0xfc42b0c8 0xc8>;
 	};
 
+	hsic@f9a15000 {
+		compatible = "qcom,hsic-host";
+		reg = <0xf9a15000 0x400>;
+		interrupts = <0 136 0>;
+		interrupt-names = "core_irq";
+		HSIC_VDDCX-supply = <&pm8019_l12>;
+		HSIC_GDSC-supply = <&gdsc_usb_hsic>;
+	};
+
+	qcom,usbbam@f9a44000 {
+		compatible = "qcom,usb-bam-msm";
+		reg = <0xf9a44000 0x11000>;
+		reg-names = "hsusb";
+		interrupts = <0 135 0>;
+		interrupt-names = "hsusb";
+		qcom,usb-active-bam = <1>;
+		qcom,usb-total-bam-num = <3>;
+		qcom,usb-bam-num-pipes = <16>;
+		qcom,ignore-core-reset-ack;
+
+		qcom,pipe0 {
+			label = "usb-to-ipa";
+			qcom,usb-bam-type = <1>;
+			qcom,usb-bam-mem-type = <2>;
+			qcom,src-bam-physical-address = <0xf9a44000>;
+			qcom,src-bam-pipe-index = <1>;
+			qcom,data-fifo-size = <0x600>;
+			qcom,descriptor-fifo-size = <0x300>;
+		};
+
+		qcom,pipe1 {
+			label = "ipa-to-usb";
+			qcom,usb-bam-type = <1>;
+			qcom,usb-bam-mem-type = <2>;
+			qcom,dst-bam-physical-address = <0xf9a44000>;
+			qcom,dst-bam-pipe-index = <0>;
+			qcom,data-fifo-size = <0x600>;
+			qcom,descriptor-fifo-size = <0x100>;
+		};
+	};
+
 	qcom,nand@f9ac0000 {
 		compatible = "qcom,msm-nand";
 		reg = <0xf9ac0000 0x1000>,
@@ -95,25 +139,26 @@
 		interrupt-names = "bam_irq";
 	};
 
-	spi@f9928000 {
+	spi@f9924000 {
+		cell-index = <0>;
 		compatible = "qcom,spi-qup-v2";
-		reg = <0xf9928000 0x1000>;
-		interrupts = <0 100 0>;
-		spi-max-frequency = <24000000>;
+		reg = <0xf9924000 0x1000>;
+		interrupts = <0 96 0>;
+		spi-max-frequency = <25000000>;
 		#address-cells = <1>;
 		#size-cells = <0>;
-		gpios = <&msmgpio 23 0>, /* CLK  */
-			<&msmgpio 21 0>, /* MISO */
-			<&msmgpio 20 0>; /* MOSI */
+		gpios = <&msmgpio 7 0>, /* CLK  */
+			<&msmgpio 5 0>, /* MISO */
+			<&msmgpio 4 0>; /* MOSI */
 
-		cs-gpios = <&msmgpio 69 0>;
+		cs-gpios = <&msmgpio 6 0>;
 
 		ethernet-switch@0 {
 			compatible = "simtec,ks8851";
 			reg = <0>;
 			interrupt-parent = <&msmgpio>;
 			interrupts = <75 0>;
-			spi-max-frequency = <5000000>;
+			spi-max-frequency = <4800000>;
 		};
 	};
 
@@ -140,7 +185,6 @@
 		/* 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 = <0x02400000>, /* TEMP_ALARM */
@@ -183,22 +227,22 @@
 		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,vdd-io-always-on;
+		qcom,vdd-io-lpm-sup;
+		qcom,vdd-io-voltage-level = <1800000 2950000>;
+		qcom,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,pad-pull-on = <0x0 0x3 0x3>;
+		qcom,pad-pull-off = <0x0 0x3 0x3>;
+		qcom,pad-drv-on = <0x7 0x4 0x4>;
+		qcom,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>;
+		qcom,clk-rates = <400000 25000000 50000000 100000000 200000000>;
+		qcom,sup-voltages = <2950 2950>;
+		qcom,bus-width = <4>;
+		qcom,xpc;
+		qcom,bus-speed-mode = "SDR12", "SDR25", "SDR50", "DDR50", "SDR104";
+		qcom,current-limit = <800>;
 
 		interrupt-parent = <&sdcc2>;
 		#address-cells = <0>;
@@ -228,13 +272,12 @@
 			<&msmgpio 17 0>,
 			<&msmgpio 18 0>,
 			<&msmgpio 19 0>;
-		qcom,sdcc-gpio-names = "CLK", "CMD", "DAT0", "DAT1", "DAT2", "DAT3";
+		qcom,gpio-names = "CLK", "CMD", "DAT0", "DAT1", "DAT2", "DAT3";
 
-		qcom,sdcc-clk-rates = <400000 25000000 50000000 100000000>;
-		qcom,sdcc-sup-voltages = <2950 2950>;
-		qcom,sdcc-bus-width = <4>;
-		qcom,sdcc-bus-speed-mode = "SDR12", "SDR25", "SDR50", "DDR50";
-		status = "disable";
+		qcom,clk-rates = <400000 25000000 50000000 100000000>;
+		qcom,sup-voltages = <2950 2950>;
+		qcom,bus-width = <4>;
+		qcom,bus-speed-mode = "SDR12", "SDR25", "SDR50", "DDR50";
 	};
 
 	qcom,bam_dmux@fc834000 {
@@ -242,8 +285,303 @@
 		reg = <0xfc834000 0x7000>;
 		interrupts = <0 29 1>;
 	};
+
+	qcom,ipa@fd4c0000 {
+		compatible = "qcom,ipa";
+		reg = <0xfd4c0000 0x26000>,
+		      <0xfd4c4000 0x14818>;
+		reg-names = "ipa-base", "bam-base";
+		interrupts = <0 252 0>,
+		             <0 253 0>;
+		interrupt-names = "ipa-irq", "bam-irq";
+
+		qcom,pipe1 {
+			label = "a2-to-ipa";
+			qcom,src-bam-physical-address = <0xfc834000>;
+			qcom,ipa-bam-mem-type = <0>;
+			qcom,src-bam-pipe-index = <1>;
+			qcom,dst-bam-physical-address = <0xfd4c0000>;
+			qcom,dst-bam-pipe-index = <6>;
+			qcom,data-fifo-offset = <0x1000>;
+			qcom,data-fifo-size = <0xd00>;
+			qcom,descriptor-fifo-offset = <0x1d00>;
+			qcom,descriptor-fifo-size = <0x300>;
+		};
+
+		qcom,pipe2 {
+			label = "ipa-to-a2";
+			qcom,src-bam-physical-address = <0xfd4c0000>;
+			qcom,ipa-bam-mem-type = <0>;
+			qcom,src-bam-pipe-index = <7>;
+			qcom,dst-bam-physical-address = <0xfc834000>;
+			qcom,dst-bam-pipe-index = <0>;
+			qcom,data-fifo-offset = <0x00>;
+			qcom,data-fifo-size = <0xd00>;
+			qcom,descriptor-fifo-offset = <0xd00>;
+			qcom,descriptor-fifo-size = <0x300>;
+		};
+	};
+
+	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";
+	};
+
+	tsens@fc4a8000 {
+		compatible = "qcom,msm-tsens";
+		reg = <0xfc4a8000 0x2000>,
+		      <0xfc4b8000 0x1000>;
+		reg-names = "tsens_physical", "tsens_eeprom_physical";
+		interrupts = <0 184 0>;
+		qcom,sensors = <5>;
+		qcom,slope = <3200 3200 3200 3200 3200>;
+	};
+
+	qcom,msm-rng@f9bff000 {
+                compatible = "qcom,msm-rng";
+                reg = <0xf9bff000 0x200>;
+                qcom,msm-rng-iface-clk;
+	};
+
+	wcd9xxx_intc: wcd9xxx-irq {
+		compatible = "qcom,wcd9xxx-irq";
+		interrupt-controller;
+		#interrupt-cells = <1>;
+		interrupt-parent = <&msmgpio>;
+		interrupts = <20 0>;
+		interrupt-names = "cdc-int";
+	};
+
+	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>;
+
+		wcd9xxx_codec@0d{
+			compatible = "qcom,wcd9xxx-i2c";
+			reg = <0x0d>;
+			qcom,cdc-reset-gpio = <&msmgpio 22 0>;
+			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>;
+			cdc-vdd-buck-supply = <&pm8019_l11>;
+			qcom,cdc-vdd-buck-voltage = <1800000 1800000>;
+			qcom,cdc-vdd-buck-current = <25000>;
+
+			cdc-vdd-tx-h-supply = <&pm8019_l11>;
+			qcom,cdc-vdd-tx-h-voltage = <1800000 1800000>;
+			qcom,cdc-vdd-tx-h-current = <25000>;
+
+			cdc-vdd-rx-h-supply = <&pm8019_l11>;
+			qcom,cdc-vdd-rx-h-voltage = <1800000 1800000>;
+			qcom,cdc-vdd-rx-h-current = <25000>;
+
+			cdc-vddpx-1-supply = <&pm8019_l11>;
+			qcom,cdc-vddpx-1-voltage = <1800000 1800000>;
+			qcom,cdc-vddpx-1-current = <10000>;
+
+			cdc-vdd-a-1p2v-supply = <&pm8019_l9>;
+			qcom,cdc-vdd-a-1p2v-voltage = <1200000 1200000>;
+			qcom,cdc-vdd-a-1p2v-current = <10000>;
+
+			cdc-vddcx-1-supply = <&pm8019_l9>;
+			qcom,cdc-vddcx-1-voltage = <1200000 1200000>;
+			qcom,cdc-vddcx-1-current = <10000>;
+
+			cdc-vddcx-2-supply = <&pm8019_l9>;
+			qcom,cdc-vddcx-2-voltage = <1200000 1200000>;
+			qcom,cdc-vddcx-2-current = <10000>;
+
+			qcom,cdc-micbias-ldoh-v = <0x3>;
+			qcom,cdc-micbias-cfilt1-mv = <1800>;
+			qcom,cdc-micbias-cfilt2-mv = <2700>;
+			qcom,cdc-micbias-cfilt3-mv = <1800>;
+			qcom,cdc-micbias1-cfilt-sel = <0x0>;
+			qcom,cdc-micbias2-cfilt-sel = <0x1>;
+			qcom,cdc-micbias3-cfilt-sel = <0x2>;
+			qcom,cdc-micbias4-cfilt-sel = <0x2>;
+		};
+
+		wcd9xxx_codec@77{
+			compatible = "qcom,wcd9xxx-i2c";
+			reg = <0x77>;
+		};
+
+		wcd9xxx_codec@66{
+			compatible = "qcom,wcd9xxx-i2c";
+			reg = <0x66>;
+		};
+
+		wcd9xxx_codec@55{
+			compatible = "qcom,wcd9xxx-i2c";
+			reg = <0x55>;
+		};
+	};
+
+	sound {
+		compatible = "qcom,mdm9625-audio-taiko";
+		qcom,model = "mdm9625-taiko-i2s-snd-card";
+
+		qcom,audio-routing =
+			"RX_BIAS", "MCLK",
+			"LDO_H", "MCLK",
+			"Ext Spk Bottom Pos", "LINEOUT1",
+			"Ext Spk Bottom Neg", "LINEOUT3",
+			"Ext Spk Top Pos", "LINEOUT2",
+			"Ext Spk Top Neg", "LINEOUT4",
+			"AMIC1", "MIC BIAS1 External",
+			"MIC BIAS1 External", "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",
+			"DMIC1", "MIC BIAS1 External",
+			"MIC BIAS1 External", "Digital Mic1",
+			"DMIC2", "MIC BIAS1 External",
+			"MIC BIAS1 External", "Digital Mic2",
+			"DMIC3", "MIC BIAS3 External",
+			"MIC BIAS3 External", "Digital Mic3",
+			"DMIC4", "MIC BIAS3 External",
+			"MIC BIAS3 External", "Digital Mic4",
+			"DMIC5", "MIC BIAS4 External",
+			"MIC BIAS4 External", "Digital Mic5",
+			"DMIC6", "MIC BIAS4 External",
+			"MIC BIAS4 External", "Digital Mic6";
+			qcom,taiko-mclk-clk-freq = <12288000>;
+	};
+
+	qcom,msm-adsp-loader {
+		compatible = "qcom,adsp-loader";
+		qcom,adsp-state = <2>;
+	};
+
+	qcom,msm-pcm {
+		compatible = "qcom,msm-pcm-dsp";
+	};
+
+	qcom,msm-pcm-routing {
+		compatible = "qcom,msm-pcm-routing";
+	};
+
+	qcom,msm-compr-dsp {
+		compatible = "qcom,msm-compr-dsp";
+	};
+
+	qcom,msm-voip-dsp {
+		compatible = "qcom,msm-voip-dsp";
+	};
+
+	qcom,msm-pcm-voice {
+		compatible = "qcom,msm-pcm-voice";
+	};
+
+	qcom,msm-dai-fe {
+		compatible = "qcom,msm-dai-fe";
+	};
+
+	qcom,msm-pcm-afe {
+		compatible = "qcom,msm-pcm-afe";
+	};
+
+	qcom,msm-pcm-hostless {
+		compatible = "qcom,msm-pcm-hostless";
+	};
+
+	qcom,msm-dai-mi2s {
+		compatible = "qcom,msm-dai-mi2s";
+		qcom,msm-dai-q6-mi2s-prim {
+			compatible = "qcom,msm-dai-q6-mi2s";
+			qcom,msm-dai-q6-mi2s-dev-id = <0>;
+			qcom,msm-mi2s-rx-lines = <2>;
+			qcom,msm-mi2s-tx-lines = <1>;
+		};
+	};
+
+	qcom,msm-dai-q6 {
+		compatible = "qcom,msm-dai-q6";
+	};
+
+	qcom,mss {
+		compatible = "qcom,pil-q6v5-mss";
+		interrupts = <0 24 1>;
+	};
 };
 
 /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/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 76650e0..8e948c2 100644
--- a/arch/arm/configs/msm7627a-perf_defconfig
+++ b/arch/arm/configs/msm7627a-perf_defconfig
@@ -42,6 +42,7 @@
 # CONFIG_MSM_SMD_NMEA is not set
 # CONFIG_MSM_SMD_QMI is not set
 CONFIG_MSM_ONCRPCROUTER=y
+CONFIG_MSM_IPC_ROUTER=y
 # CONFIG_MSM_RPCSERVER_TIME_REMOTE is not set
 CONFIG_MSM_RMT_STORAGE_CLIENT=y
 # CONFIG_MSM_HW3D is not set
diff --git a/arch/arm/configs/msm7627a_defconfig b/arch/arm/configs/msm7627a_defconfig
index 8ab57de..d403cec 100644
--- a/arch/arm/configs/msm7627a_defconfig
+++ b/arch/arm/configs/msm7627a_defconfig
@@ -42,6 +42,7 @@
 # CONFIG_MSM_SMD_NMEA is not set
 # CONFIG_MSM_SMD_QMI is not set
 CONFIG_MSM_ONCRPCROUTER=y
+CONFIG_MSM_IPC_ROUTER=y
 # CONFIG_MSM_RPCSERVER_TIME_REMOTE is not set
 CONFIG_MSM_RMT_STORAGE_CLIENT=y
 # CONFIG_MSM_HW3D is not set
diff --git a/arch/arm/configs/msm8660-perf_defconfig b/arch/arm/configs/msm8660-perf_defconfig
index 2ee3f3b..828484a 100644
--- a/arch/arm/configs/msm8660-perf_defconfig
+++ b/arch/arm/configs/msm8660-perf_defconfig
@@ -295,7 +295,6 @@
 CONFIG_GPIO_SYSFS=y
 CONFIG_GPIO_SX150X=y
 CONFIG_POWER_SUPPLY=y
-# CONFIG_BATTERY_MSM is not set
 CONFIG_BATTERY_MSM8X60=y
 CONFIG_PM8058_CHARGER=y
 CONFIG_ISL9519_CHARGER=y
diff --git a/arch/arm/configs/msm8660_defconfig b/arch/arm/configs/msm8660_defconfig
index 25c5207..28d7b12 100644
--- a/arch/arm/configs/msm8660_defconfig
+++ b/arch/arm/configs/msm8660_defconfig
@@ -295,7 +295,6 @@
 CONFIG_GPIO_SYSFS=y
 CONFIG_GPIO_SX150X=y
 CONFIG_POWER_SUPPLY=y
-# CONFIG_BATTERY_MSM is not set
 CONFIG_BATTERY_MSM8X60=y
 CONFIG_PM8058_CHARGER=y
 CONFIG_ISL9519_CHARGER=y
diff --git a/arch/arm/configs/msm8910_defconfig b/arch/arm/configs/msm8910_defconfig
index a9dadee..de78559 100644
--- a/arch/arm/configs/msm8910_defconfig
+++ b/arch/arm/configs/msm8910_defconfig
@@ -10,7 +10,6 @@
 CONFIG_CGROUP_CPUACCT=y
 CONFIG_RESOURCE_COUNTERS=y
 CONFIG_CGROUP_SCHED=y
-# CONFIG_FAIR_GROUP_SCHED is not set
 CONFIG_RT_GROUP_SCHED=y
 CONFIG_NAMESPACES=y
 # CONFIG_UTS_NS is not set
@@ -21,52 +20,97 @@
 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_ARCH_MSM8226=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_SMD_PKG4=y
+# CONFIG_MSM_HW3D is not set
 CONFIG_MSM_DIRECT_SCLK_ACCESS=y
+CONFIG_MSM_WATCHDOG_V2=y
 CONFIG_NO_HZ=y
 CONFIG_HIGH_RES_TIMERS=y
+CONFIG_SMP=y
 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_ANDROID_PMEM is not set
-# CONFIG_INPUT_MOUSEDEV is not set
-# CONFIG_INPUT_KEYBOARD is not set
-# CONFIG_INPUT_MOUSE is not set
+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_SPMI=y
+CONFIG_SPMI_MSM_PMIC_ARB=y
+CONFIG_MSM_QPNP_INT=y
 CONFIG_DEBUG_GPIO=y
 CONFIG_GPIO_SYSFS=y
 # CONFIG_HWMON is not set
+CONFIG_REGULATOR=y
+CONFIG_REGULATOR_STUB=y
+CONFIG_ION=y
+CONFIG_ION_MSM=y
+CONFIG_FB=y
+CONFIG_FB_VIRTUAL=y
 CONFIG_SOUND=y
 CONFIG_SND=y
 CONFIG_SND_SOC=y
@@ -75,9 +119,32 @@
 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_SPS=y
+CONFIG_SPS_SUPPORT_NDP_BAM=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_MISC_FILESYSTEMS is not set
 CONFIG_NLS_CODEPAGE_437=y
 CONFIG_NLS_ASCII=y
 CONFIG_NLS_ISO8859_1=y
@@ -92,19 +159,11 @@
 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 53e6260..d5e15f1 100644
--- a/arch/arm/configs/msm8960-perf_defconfig
+++ b/arch/arm/configs/msm8960-perf_defconfig
@@ -315,7 +315,6 @@
 CONFIG_GPIO_SYSFS=y
 CONFIG_GPIO_SX150X=y
 CONFIG_POWER_SUPPLY=y
-# CONFIG_BATTERY_MSM is not set
 CONFIG_ISL9519_CHARGER=y
 CONFIG_SMB349_CHARGER=y
 CONFIG_PM8921_CHARGER=y
@@ -356,6 +355,7 @@
 CONFIG_MSM_CAMERA_FLASH_SC628A=y
 CONFIG_MSM_CAMERA_FLASH_TPS61310=y
 CONFIG_OV2720=y
+CONFIG_IMX135=y
 CONFIG_MSM_CAMERA_SENSOR=y
 CONFIG_MSM_ACTUATOR=y
 CONFIG_MSM_EEPROM=y
@@ -373,7 +373,6 @@
 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
@@ -415,7 +414,6 @@
 CONFIG_USB_EHCI_MSM_HOST4=y
 CONFIG_USB_ACM=y
 CONFIG_USB_STORAGE=y
-CONFIG_USB_STORAGE_DEBUG=y
 CONFIG_USB_STORAGE_DATAFAB=y
 CONFIG_USB_STORAGE_FREECOM=y
 CONFIG_USB_STORAGE_ISD200=y
diff --git a/arch/arm/configs/msm8960_defconfig b/arch/arm/configs/msm8960_defconfig
index 5770859..386f311 100644
--- a/arch/arm/configs/msm8960_defconfig
+++ b/arch/arm/configs/msm8960_defconfig
@@ -320,7 +320,6 @@
 CONFIG_GPIO_SYSFS=y
 CONFIG_GPIO_SX150X=y
 CONFIG_POWER_SUPPLY=y
-# CONFIG_BATTERY_MSM is not set
 CONFIG_ISL9519_CHARGER=y
 CONFIG_SMB349_CHARGER=y
 CONFIG_PM8921_CHARGER=y
@@ -360,6 +359,7 @@
 CONFIG_IMX074_ACT=y
 CONFIG_MSM_CAMERA_FLASH_SC628A=y
 CONFIG_OV2720=y
+CONFIG_IMX135=y
 CONFIG_MSM_CAMERA_SENSOR=y
 CONFIG_MSM_ACTUATOR=y
 CONFIG_MSM_EEPROM=y
@@ -377,7 +377,6 @@
 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
@@ -418,7 +417,6 @@
 CONFIG_USB_EHCI_MSM_HOST4=y
 CONFIG_USB_ACM=y
 CONFIG_USB_STORAGE=y
-CONFIG_USB_STORAGE_DEBUG=y
 CONFIG_USB_STORAGE_DATAFAB=y
 CONFIG_USB_STORAGE_FREECOM=y
 CONFIG_USB_STORAGE_ISD200=y
diff --git a/arch/arm/configs/msm8974-perf_defconfig b/arch/arm/configs/msm8974-perf_defconfig
index b2ee503..bf44665 100644
--- a/arch/arm/configs/msm8974-perf_defconfig
+++ b/arch/arm/configs/msm8974-perf_defconfig
@@ -37,7 +37,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
@@ -48,11 +47,13 @@
 CONFIG_MSM_BAM_DMUX=y
 CONFIG_MSM_IPC_ROUTER=y
 CONFIG_MSM_IPC_ROUTER_SMD_XPRT=y
+CONFIG_MSM_IPC_ROUTER_SECURITY=y
+CONFIG_MSM_QMI_INTERFACE=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_TZ_LOG=y
@@ -217,6 +218,7 @@
 CONFIG_GENLOCK_MISCDEVICE=y
 CONFIG_BLK_DEV_LOOP=y
 CONFIG_BLK_DEV_RAM=y
+CONFIG_TI_DRV2667=y
 CONFIG_QSEECOM=y
 CONFIG_SCSI=y
 CONFIG_SCSI_TGT=y
@@ -239,6 +241,7 @@
 CONFIG_SLIP=y
 CONFIG_SLIP_COMPRESSED=y
 CONFIG_SLIP_MODE_SLIP6=y
+CONFIG_USB_USBNET=y
 CONFIG_WCNSS_CORE=y
 CONFIG_WCNSS_CORE_PRONTO=y
 CONFIG_INPUT_EVDEV=y
@@ -263,17 +266,23 @@
 CONFIG_SPMI=y
 CONFIG_SPMI_MSM_PMIC_ARB=y
 CONFIG_MSM_QPNP_INT=y
-CONFIG_SLIMBUS_MSM_CTRL=y
+CONFIG_SLIMBUS_MSM_NGD=y
 CONFIG_DEBUG_GPIO=y
 CONFIG_GPIO_SYSFS=y
 CONFIG_GPIO_QPNP_PIN=y
 CONFIG_GPIO_QPNP_PIN_DEBUG=y
 CONFIG_POWER_SUPPLY=y
-# CONFIG_BATTERY_MSM is not set
+CONFIG_SMB350_CHARGER=y
+CONFIG_BATTERY_BQ28400=y
+CONFIG_QPNP_CHARGER=y
+CONFIG_BATTERY_BCL=y
+CONFIG_QPNP_BMS=y
+CONFIG_SENSORS_EPM_ADC=y
 CONFIG_SENSORS_QPNP_ADC_VOLTAGE=y
 CONFIG_SENSORS_QPNP_ADC_CURRENT=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
@@ -283,10 +292,13 @@
 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_FLASH=y
+CONFIG_MSM_CAMERA_LED_TRIGGER_FLASH=y
 CONFIG_MSM_CAMERA_SENSOR=y
 CONFIG_MSM_ACTUATOR=y
 CONFIG_MSM_JPEG=y
@@ -297,6 +309,7 @@
 CONFIG_MSM_CSI2_REGISTER=y
 CONFIG_MSM_ISPIF=y
 CONFIG_S5K3L1YX=y
+CONFIG_MSM_WFD=y
 CONFIG_RADIO_IRIS=y
 CONFIG_RADIO_IRIS_TRANSPORT=m
 CONFIG_ION=y
@@ -314,10 +327,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
@@ -333,7 +348,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
@@ -343,6 +358,8 @@
 CONFIG_MMC_BLOCK_MINORS=32
 # CONFIG_MMC_BLOCK_BOUNCE is not set
 CONFIG_MMC_TEST=m
+CONFIG_MMC_SDHCI=y
+CONFIG_MMC_SDHCI_PLTFM=y
 CONFIG_MMC_MSM=y
 CONFIG_MMC_MSM_SPS_SUPPORT=y
 CONFIG_LEDS_QPNP=y
@@ -365,6 +382,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
diff --git a/arch/arm/configs/msm8974_defconfig b/arch/arm/configs/msm8974_defconfig
index d8d2eae..f9dbc85 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
@@ -47,12 +46,13 @@
 CONFIG_MSM_BAM_DMUX=y
 CONFIG_MSM_IPC_ROUTER=y
 CONFIG_MSM_IPC_ROUTER_SMD_XPRT=y
+CONFIG_MSM_IPC_ROUTER_SECURITY=y
+CONFIG_MSM_QMI_INTERFACE=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_TZ_LOG=y
@@ -220,6 +220,7 @@
 CONFIG_GENLOCK_MISCDEVICE=y
 CONFIG_BLK_DEV_LOOP=y
 CONFIG_BLK_DEV_RAM=y
+CONFIG_TI_DRV2667=y
 CONFIG_QSEECOM=y
 CONFIG_SCSI=y
 CONFIG_SCSI_TGT=y
@@ -242,6 +243,7 @@
 CONFIG_SLIP=y
 CONFIG_SLIP_COMPRESSED=y
 CONFIG_SLIP_MODE_SLIP6=y
+CONFIG_USB_USBNET=y
 CONFIG_WCNSS_CORE=y
 CONFIG_WCNSS_CORE_PRONTO=y
 CONFIG_INPUT_EVDEV=y
@@ -266,15 +268,18 @@
 CONFIG_SPMI=y
 CONFIG_SPMI_MSM_PMIC_ARB=y
 CONFIG_MSM_QPNP_INT=y
-CONFIG_SLIMBUS_MSM_CTRL=y
+CONFIG_SLIMBUS_MSM_NGD=y
 CONFIG_DEBUG_GPIO=y
 CONFIG_GPIO_SYSFS=y
 CONFIG_GPIO_QPNP_PIN=y
 CONFIG_GPIO_QPNP_PIN_DEBUG=y
 CONFIG_POWER_SUPPLY=y
-# CONFIG_BATTERY_MSM is not set
+CONFIG_SMB350_CHARGER=y
+CONFIG_BATTERY_BQ28400=y
 CONFIG_QPNP_CHARGER=y
+CONFIG_BATTERY_BCL=y
 CONFIG_QPNP_BMS=y
+CONFIG_SENSORS_EPM_ADC=y
 CONFIG_SENSORS_QPNP_ADC_VOLTAGE=y
 CONFIG_SENSORS_QPNP_ADC_CURRENT=y
 CONFIG_THERMAL=y
@@ -284,16 +289,18 @@
 CONFIG_REGULATOR_FIXED_VOLTAGE=y
 CONFIG_REGULATOR_STUB=y
 CONFIG_REGULATOR_QPNP=y
-CONFIG_QPNP_PWM=y
 CONFIG_MEDIA_SUPPORT=y
 CONFIG_MEDIA_CONTROLLER=y
 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_FLASH=y
+CONFIG_MSM_CAMERA_LED_TRIGGER_FLASH=y
 CONFIG_MSM_CAMERA_SENSOR=y
 CONFIG_MSM_ACTUATOR=y
 CONFIG_MSM_JPEG=y
@@ -322,10 +329,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
@@ -341,7 +350,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
@@ -351,6 +360,8 @@
 CONFIG_MMC_BLOCK_MINORS=32
 # CONFIG_MMC_BLOCK_BOUNCE is not set
 CONFIG_MMC_TEST=m
+CONFIG_MMC_SDHCI=y
+CONFIG_MMC_SDHCI_PLTFM=y
 CONFIG_MMC_MSM=y
 CONFIG_MMC_MSM_SPS_SUPPORT=y
 CONFIG_LEDS_QPNP=y
@@ -373,6 +384,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
@@ -399,6 +411,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
diff --git a/arch/arm/configs/msm9615_defconfig b/arch/arm/configs/msm9615_defconfig
index 81b853d..a052609 100644
--- a/arch/arm/configs/msm9615_defconfig
+++ b/arch/arm/configs/msm9615_defconfig
@@ -199,7 +199,6 @@
 CONFIG_DEBUG_GPIO=y
 CONFIG_GPIO_SYSFS=y
 CONFIG_POWER_SUPPLY=y
-# CONFIG_BATTERY_MSM is not set
 CONFIG_SENSORS_PM8XXX_ADC=y
 CONFIG_THERMAL=y
 CONFIG_THERMAL_TSENS8960=y
diff --git a/arch/arm/configs/msm9625_defconfig b/arch/arm/configs/msm9625_defconfig
index 4e34ebd..80f16d4 100644
--- a/arch/arm/configs/msm9625_defconfig
+++ b/arch/arm/configs/msm9625_defconfig
@@ -21,6 +21,8 @@
 CONFIG_BLK_DEV_INITRD=y
 CONFIG_RD_BZIP2=y
 CONFIG_RD_LZMA=y
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
+CONFIG_PANIC_TIMEOUT=5
 CONFIG_KALLSYMS_ALL=y
 CONFIG_EMBEDDED=y
 CONFIG_PROFILING=y
@@ -37,9 +39,16 @@
 # 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_SUBSYSTEM_RESTART=y
+CONFIG_MSM_PIL=y
+CONFIG_MSM_PIL_MSS_QDSP6V5=y
 CONFIG_MSM_DIRECT_SCLK_ACCESS=y
 CONFIG_MSM_WATCHDOG_V2=y
+CONFIG_MSM_DLOAD_MODE=y
 CONFIG_NO_HZ=y
 CONFIG_HIGH_RES_TIMERS=y
 CONFIG_ARM_ARCH_TIMER=y
@@ -48,6 +57,11 @@
 CONFIG_HIGHMEM=y
 CONFIG_VMALLOC_RESERVE=0x19000000
 CONFIG_USE_OF=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_CPU_IDLE=y
 CONFIG_VFP=y
 CONFIG_NEON=y
@@ -58,7 +72,23 @@
 CONFIG_UNIX=y
 CONFIG_INET=y
 CONFIG_IPV6=y
-# CONFIG_WIRELESS is not set
+CONFIG_NETFILTER=y
+CONFIG_NETFILTER_DEBUG=y
+CONFIG_NETFILTER_NETLINK_QUEUE=y
+CONFIG_NETFILTER_NETLINK_LOG=y
+CONFIG_NF_CONNTRACK=y
+CONFIG_NF_CONNTRACK_EVENTS=y
+CONFIG_NF_CONNTRACK_TIMESTAMP=y
+CONFIG_NF_CONNTRACK_FTP=y
+CONFIG_NF_CONNTRACK_PPTP=y
+CONFIG_NF_CONNTRACK_SIP=y
+CONFIG_NF_CONNTRACK_TFTP=y
+CONFIG_NET_SCHED=y
+CONFIG_NET_SCH_HTB=y
+CONFIG_NET_SCH_PRIO=y
+CONFIG_NET_CLS_FW=y
+CONFIG_CFG80211=m
+CONFIG_NL80211_TESTMODE=y
 CONFIG_MTD=y
 CONFIG_MTD_CMDLINE_PARTS=y
 CONFIG_MTD_OF_PARTS=y
@@ -76,12 +106,14 @@
 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
 # CONFIG_NET_VENDOR_STMICRO is not set
-# CONFIG_WLAN is not set
+CONFIG_ATH6K_LEGACY_EXT=y
 # CONFIG_INPUT_MOUSEDEV is not set
+CONFIG_INPUT_EVDEV=y
 # CONFIG_INPUT_KEYBOARD is not set
 # CONFIG_INPUT_MOUSE is not set
 CONFIG_INPUT_MISC=y
@@ -93,22 +125,28 @@
 CONFIG_SERIAL_MSM_HSL_CONSOLE=y
 CONFIG_DIAG_CHAR=y
 CONFIG_HW_RANDOM=y
+CONFIG_HW_RANDOM_MSM=y
 CONFIG_I2C=y
 CONFIG_I2C_CHARDEV=y
 CONFIG_I2C_QUP=y
+CONFIG_MSM_BUS_SCALING=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_SMB137C_CHARGER=y
+CONFIG_SENSORS_QPNP_ADC_VOLTAGE=y
+CONFIG_THERMAL=y
+CONFIG_THERMAL_TSENS8974=y
 CONFIG_REGULATOR=y
+CONFIG_REGULATOR_FIXED_VOLTAGE=y
 CONFIG_REGULATOR_QPNP=y
 CONFIG_ION=y
 CONFIG_ION_MSM=y
@@ -131,6 +169,7 @@
 CONFIG_RTC_CLASS=y
 # CONFIG_RTC_DRV_MSM is not set
 CONFIG_RTC_DRV_QPNP=y
+CONFIG_IPA=y
 CONFIG_SPS=y
 CONFIG_USB_BAM=y
 CONFIG_SPS_SUPPORT_BAMDMA=y
@@ -169,3 +208,58 @@
 # CONFIG_CRYPTO_HW is not set
 CONFIG_CRC_CCITT=y
 CONFIG_LIBCRC32C=y
+CONFIG_ENABLE_DEFAULT_TRACERS=y
+CONFIG_MSM_QDSS=y
+CONFIG_IP_MULTICAST=y
+CONFIG_IP_ADVANCED_ROUTER=y
+CONFIG_IP_MULTIPLE_TABLES=y
+CONFIG_IP_MROUTE=y
+CONFIG_IP_PIMSM_V2=y
+CONFIG_IPV6_MULTIPLE_TABLES=y
+CONFIG_IPV6_SUBTREES=y
+CONFIG_NETFILTER_XT_MARK=y
+CONFIG_NETFILTER_XT_CONNMARK=y
+CONFIG_NETFILTER_XT_MATCH_ADDRTYPE=y
+CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y
+CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y
+CONFIG_NETFILTER_XT_MATCH_IPRANGE=y
+CONFIG_NETFILTER_XT_MATCH_MULTIPORT=y
+CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y
+CONFIG_IP_SET=y
+CONFIG_NF_CONNTRACK_IPV4=y
+CONFIG_IP_NF_IPTABLES=y
+CONFIG_IP_NF_MATCH_AH=y
+CONFIG_IP_NF_MATCH_ECN=y
+CONFIG_IP_NF_MATCH_TTL=y
+CONFIG_IP_NF_FILTER=y
+CONFIG_IP_NF_TARGET_REJECT=y
+CONFIG_IP_NF_TARGET_REJECT_SKERR=y
+CONFIG_IP_NF_TARGET_ULOG=y
+CONFIG_NF_NAT=y
+CONFIG_IP_NF_TARGET_MASQUERADE=y
+CONFIG_IP_NF_TARGET_NETMAP=y
+CONFIG_IP_NF_TARGET_REDIRECT=y
+CONFIG_IP_NF_MANGLE=y
+CONFIG_IP_NF_TARGET_ECN=y
+CONFIG_IP_NF_TARGET_TTL=y
+CONFIG_IP_NF_RAW=y
+CONFIG_NF_CONNTRACK_IPV6=y
+CONFIG_IP6_NF_IPTABLES=y
+CONFIG_IP6_NF_MATCH_AH=y
+CONFIG_IP6_NF_MATCH_FRAG=y
+CONFIG_IP6_NF_MATCH_OPTS=y
+CONFIG_IP6_NF_MATCH_HL=y
+CONFIG_IP6_NF_MATCH_IPV6HEADER=y
+CONFIG_IP6_NF_MATCH_MH=y
+CONFIG_IP6_NF_MATCH_RT=y
+CONFIG_IP6_NF_FILTER=y
+CONFIG_IP6_NF_TARGET_REJECT=y
+CONFIG_IP6_NF_TARGET_REJECT_SKERR=y
+CONFIG_IP6_NF_MANGLE=y
+CONFIG_IP6_NF_RAW=y
+CONFIG_WCD9320_CODEC=y
+CONFIG_SOUND=y
+CONFIG_SND=y
+CONFIG_SND_SOC=y
+CONFIG_SND_SOC_MDM9625=y
+CONFIG_MSM_ADSP_LOADER=m
diff --git a/arch/arm/include/asm/dma-mapping.h b/arch/arm/include/asm/dma-mapping.h
index 5f28327..abb222f 100644
--- a/arch/arm/include/asm/dma-mapping.h
+++ b/arch/arm/include/asm/dma-mapping.h
@@ -261,6 +261,58 @@
 	return dma_mmap_attrs(dev, vma, cpu_addr, dma_addr, size, &attrs);
 }
 
+static inline void *dma_alloc_stronglyordered(struct device *dev, size_t size,
+				       dma_addr_t *dma_handle, gfp_t flag)
+{
+	DEFINE_DMA_ATTRS(attrs);
+	dma_set_attr(DMA_ATTR_STRONGLY_ORDERED, &attrs);
+	return dma_alloc_attrs(dev, size, dma_handle, flag, &attrs);
+}
+
+static inline void dma_free_stronglyordered(struct device *dev, size_t size,
+				     void *cpu_addr, dma_addr_t dma_handle)
+{
+	DEFINE_DMA_ATTRS(attrs);
+	dma_set_attr(DMA_ATTR_STRONGLY_ORDERED, &attrs);
+	return dma_free_attrs(dev, size, cpu_addr, dma_handle, &attrs);
+}
+
+static inline int dma_mmap_stronglyordered(struct device *dev,
+		struct vm_area_struct *vma, void *cpu_addr,
+		dma_addr_t dma_addr, size_t size)
+{
+	DEFINE_DMA_ATTRS(attrs);
+	dma_set_attr(DMA_ATTR_STRONGLY_ORDERED, &attrs);
+	return dma_mmap_attrs(dev, vma, cpu_addr, dma_addr, size, &attrs);
+}
+
+static inline void *dma_alloc_nonconsistent(struct device *dev, size_t size,
+				       dma_addr_t *dma_handle, gfp_t flag)
+{
+	DEFINE_DMA_ATTRS(attrs);
+	dma_set_attr(DMA_ATTR_NON_CONSISTENT, &attrs);
+	return dma_alloc_attrs(dev, size, dma_handle, flag, &attrs);
+}
+
+static inline void dma_free_nonconsistent(struct device *dev, size_t size,
+				     void *cpu_addr, dma_addr_t dma_handle)
+{
+	DEFINE_DMA_ATTRS(attrs);
+	dma_set_attr(DMA_ATTR_NON_CONSISTENT, &attrs);
+	return dma_free_attrs(dev, size, cpu_addr, dma_handle, &attrs);
+}
+
+static inline int dma_mmap_nonconsistent(struct device *dev,
+		struct vm_area_struct *vma, void *cpu_addr,
+		dma_addr_t dma_addr, size_t size)
+{
+	DEFINE_DMA_ATTRS(attrs);
+	dma_set_attr(DMA_ATTR_NON_CONSISTENT, &attrs);
+	return dma_mmap_attrs(dev, vma, cpu_addr, dma_addr, size, &attrs);
+}
+
+
+
 /*
  * This can be called during boot to increase the size of the consistent
  * DMA region above it's default value of 2MB. It must be called before the
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/mach/map.h b/arch/arm/include/asm/mach/map.h
index cd5be28..f705388 100644
--- a/arch/arm/include/asm/mach/map.h
+++ b/arch/arm/include/asm/mach/map.h
@@ -37,6 +37,7 @@
 #define MT_MEMORY_RW		16
 #define MT_MEMORY_RX		17
 #define MT_MEMORY_DMA_READY	18
+#define MT_DEVICE_USER_ACCESSIBLE	19
 
 #ifdef CONFIG_MMU
 extern void iotable_init(struct map_desc *, int);
diff --git a/arch/arm/include/asm/mach/mmc.h b/arch/arm/include/asm/mach/mmc.h
index d341ea9..e7de62e 100644
--- a/arch/arm/include/asm/mach/mmc.h
+++ b/arch/arm/include/asm/mach/mmc.h
@@ -149,7 +149,7 @@
 	int status_gpio;
 	/* Indicates the polarity of the GPIO line when card is inserted */
 	bool is_status_gpio_active_low;
-        unsigned int sdiowakeup_irq;
+	int sdiowakeup_irq;
         unsigned long irq_flags;
         unsigned long mmc_bus_width;
         int (*wpswitch) (struct device *);
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/include/asm/pmu.h b/arch/arm/include/asm/pmu.h
index 88d0872..5188dbf 100644
--- a/arch/arm/include/asm/pmu.h
+++ b/arch/arm/include/asm/pmu.h
@@ -32,6 +32,10 @@
  *	interrupt and passed the address of the low level handler,
  *	and can be used to implement any platform specific handling
  *	before or after calling it.
+ * @request_pmu_irq: an optional handler in case the platform wants
+ *	to use a percpu IRQ API call. e.g. request_percpu_irq
+ * @free_pmu_irq: an optional handler in case the platform wants
+ *	to use a percpu IRQ API call. e.g. free_percpu_irq
  * @enable_irq: an optional handler which will be called after
  *	request_irq and be used to handle some platform specific
  *	irq enablement
@@ -42,6 +46,8 @@
 struct arm_pmu_platdata {
 	irqreturn_t (*handle_irq)(int irq, void *dev,
 				  irq_handler_t pmu_handler);
+	int	(*request_pmu_irq)(int irq, irq_handler_t *irq_h);
+	void	(*free_pmu_irq)(int irq);
 	void (*enable_irq)(int irq);
 	void (*disable_irq)(int irq);
 };
@@ -114,8 +120,8 @@
 	u64		max_period;
 	struct platform_device	*plat_device;
 	irqreturn_t	(*handle_irq)(int irq_num, void *dev);
-	int     	(*request_pmu_irq)(int irq, irq_handler_t *irq_h);
-	void    	(*free_pmu_irq)(int irq);
+	int		(*request_pmu_irq)(int irq, irq_handler_t *irq_h);
+	void		(*free_pmu_irq)(int irq);
 	void		(*enable)(struct hw_perf_event *evt, int idx, int cpu);
 	void		(*disable)(struct hw_perf_event *evt, int idx);
 	int		(*get_event_idx)(struct pmu_hw_events *hw_events,
diff --git a/arch/arm/include/asm/user_accessible_timer.h b/arch/arm/include/asm/user_accessible_timer.h
new file mode 100644
index 0000000..c6d7bd4
--- /dev/null
+++ b/arch/arm/include/asm/user_accessible_timer.h
@@ -0,0 +1,49 @@
+/* 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 _ARM_KERNEL_USER_ACCESSIBLE_TIMER_H_
+#define _ARM_KERNEL_USER_ACCESSIBLE_TIMER_H_
+
+#define ARM_USER_ACCESSIBLE_TIMERS_INVALID_PAGE -1
+
+extern unsigned long zero_pfn;
+
+#ifdef CONFIG_ARM_USE_USER_ACCESSIBLE_TIMERS
+#ifndef CONFIG_ARM_USER_ACCESSIBLE_TIMER_BASE
+#define CONFIG_ARM_USER_ACCESSIBLE_TIMER_BASE 0xfffef000
+#endif
+extern void setup_user_timer_offset(unsigned long addr);
+extern int get_timer_page_address(void);
+static inline int get_user_accessible_timers_base(void)
+{
+	return CONFIG_ARM_USER_ACCESSIBLE_TIMER_BASE;
+}
+extern void set_user_accessible_timer_flag(bool flag);
+#else
+#define CONFIG_ARM_USER_ACCESSIBLE_TIMER_BASE 0
+static inline void setup_user_timer_offset(unsigned long addr)
+{
+}
+static inline int get_timer_page_address(void)
+{
+	return ARM_USER_ACCESSIBLE_TIMERS_INVALID_PAGE;
+}
+static inline int get_user_accessible_timers_base(void)
+{
+	return 0;
+}
+static inline void set_user_accessible_timer_flag(bool flag)
+{
+}
+#endif
+
+#endif
diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile
index 22b0f1e..fc00a23 100644
--- a/arch/arm/kernel/Makefile
+++ b/arch/arm/kernel/Makefile
@@ -63,6 +63,7 @@
 obj-$(CONFIG_SWP_EMULATE)	+= swp_emulate.o
 CFLAGS_swp_emulate.o		:= -Wa,-march=armv7-a
 obj-$(CONFIG_HAVE_HW_BREAKPOINT)	+= hw_breakpoint.o
+obj-$(CONFIG_GENERIC_TIME_VSYSCALL)	+= update_vsyscall_arm.o
 
 obj-$(CONFIG_CPU_XSCALE)	+= xscale-cp0.o
 obj-$(CONFIG_CPU_XSC3)		+= xscale-cp0.o
@@ -73,6 +74,7 @@
 obj-$(CONFIG_HW_PERF_EVENTS)	+= perf_event.o
 AFLAGS_iwmmxt.o			:= -Wa,-mcpu=iwmmxt
 obj-$(CONFIG_ARM_CPU_TOPOLOGY)  += topology.o
+obj-$(CONFIG_ARM_USE_USER_ACCESSIBLE_TIMERS)	+= user_accessible_timer.o
 
 ifneq ($(CONFIG_ARCH_EBSA110),y)
   obj-y		+= io.o
diff --git a/arch/arm/kernel/entry-armv.S b/arch/arm/kernel/entry-armv.S
index 7a8c2d6..ddd421c 100644
--- a/arch/arm/kernel/entry-armv.S
+++ b/arch/arm/kernel/entry-armv.S
@@ -764,6 +764,97 @@
 	.align	5
 	.globl	__kuser_helper_start
 __kuser_helper_start:
+#ifdef GENERIC_TIME_VSYSCALL
+/*
+ * Reference declaration:
+ *
+ *      extern struct timezone __kernel_helper_gtod_timezone
+ *      extern unsigned int __kernel_helper_gtod_seqnum
+ *
+ *  Definition and user space usage example:
+ *
+ *      #define __kernel_helper_gtod_timezone (*(unsigned int*)0xffff0f20)
+ *      #define __kernel_helper_gtod_seqnum   (*(unsigned int*)0xffff0f28)
+ *
+ *      unsigned int prelock, postlock ;
+ *      do {
+ *          prelock = __kernel_helper_gtod_seqnum;
+ *          memcpy(&tz, (void*)&(__kernel_helper_gtod_timezone),
+ *                     sizeof(struct timezone)) ;
+ *          postlock = __kernel_helper_gtod_seqnum;
+ *      } while (prelock != postlock);
+ *
+ * 0xffff0f20-3: tz_minuteswest
+ * 0xffff0f24-7: tz_dsttime
+ * 0xffff0f28-b: sequence #.
+ * 0xffff0f30-3: offset into CONFIG_USER_ACCESSIBLE_TIMER_BASE to get the timer.
+ * 0xffff0f34-7: Feature flag
+ * 0xffff0f38-b: wall-to-mononic: tv_sec
+ * 0xffff0f3c-f: wall-to-mononic: tv_nsec
+ */
+	.globl  __kuser_gtod_timezone
+__kuser_gtod_timezone: @0xffff0f20
+	.word	0
+	.word	0
+	.word	0
+	.word	0
+	.word	0
+	/* This offset is where the flag to enable the
+	 * user accessible timers is located.
+	 */
+	.word	0
+	.word	0
+	.word	0
+	.align	5
+
+/*
+ * Reference declaration:
+ *
+ *      extern struct timeval __kernel_helper_gtod_timeval
+ *      extern unsigned int __kernel_helper_gtod_seqnum
+ *
+ *  Definition and user space usage example:
+ *
+ *      #define __kernel_helper_gtod_timeval (*(unsigned int*)0xffff0f40)
+ *      #define __kernel_helper_gtod_seqnum   (*(unsigned int*)0xffff0f48)
+ *
+ *      unsigned int prelock, postlock ;
+ *      struct gtod {
+ *          uint64_t  cycle_last;
+ *          uint64_t  mask;
+ *          uint32_t  mult;
+ *          uint32_t  shift;
+ *          uint32_t  tv_sec;
+ *          uint32_t  tv_nsec;
+ *      };
+ *      struct gtod gdtod;
+ *
+ *      do {
+ *          prelock = __kernel_helper_gtod_seqnum;
+ *          memcpy(&gdtod, (void*)&(__kernel_helper_gtod_timeval),
+ *                     sizeof(struct gtod)) ;
+ *          postlock = __kernel_helper_gtod_seqnum;
+ *      } while (prelock != postlock);
+ *
+ * 0xffff0f40-7: cycle_last
+ * 0xffff0f48-f: mask
+ * 0xffff0f50-3: mult
+ * 0xffff0f54-7: shift
+ * 0xffff0f58-b: tv_sec
+ * 0xffff0f5c-f: tv_nsec
+ */
+	.globl  __kuser_gtod_timeval
+__kuser_gtod_timeval:  @0xffff0f40
+	.word	0
+	.word	0
+	.word	0
+	.word	0
+	.word	0
+	.word	0
+	.word	0
+	.word	0
+	.align	5
+#endif
 
 /*
  * Due to the length of some sequences, __kuser_cmpxchg64 spans 2 regular
diff --git a/arch/arm/kernel/perf_event.c b/arch/arm/kernel/perf_event.c
index 3f6a6d3..3a52ddc 100644
--- a/arch/arm/kernel/perf_event.c
+++ b/arch/arm/kernel/perf_event.c
@@ -437,6 +437,16 @@
 	else
 		handle_irq = armpmu->handle_irq;
 
+	if (plat && plat->request_pmu_irq)
+		armpmu->request_pmu_irq = plat->request_pmu_irq;
+	else if (!armpmu->request_pmu_irq)
+		armpmu->request_pmu_irq = armpmu_generic_request_irq;
+
+	if (plat && plat->free_pmu_irq)
+		armpmu->free_pmu_irq = plat->free_pmu_irq;
+	else if (!armpmu->request_pmu_irq)
+		armpmu->free_pmu_irq = armpmu_generic_free_irq;
+
 	irqs = min(pmu_device->num_resources, num_possible_cpus());
 	if (irqs < 1) {
 		pr_err("no irqs for PMUs defined\n");
diff --git a/arch/arm/kernel/perf_event_msm.c b/arch/arm/kernel/perf_event_msm.c
index 8f58adf..92dc7c7 100644
--- a/arch/arm/kernel/perf_event_msm.c
+++ b/arch/arm/kernel/perf_event_msm.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
@@ -729,9 +729,6 @@
 	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;
 }
@@ -742,8 +739,6 @@
 	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 eec614b..5708d74 100644
--- a/arch/arm/kernel/perf_event_msm_krait.c
+++ b/arch/arm/kernel/perf_event_msm_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
@@ -520,53 +520,6 @@
 	armv7_pmnc_write(ARMV7_PMNC_P | ARMV7_PMNC_C);
 }
 
-static void enable_irq_callback(void *info)
-{
-        int irq = *(unsigned int *)info;
-
-        enable_percpu_irq(irq, IRQ_TYPE_EDGE_RISING);
-}
-
-static void disable_irq_callback(void *info)
-{
-        int irq = *(unsigned int *)info;
-
-        disable_percpu_irq(irq);
-}
-
-static int
-msm_request_irq(int irq, irq_handler_t *handle_irq)
-{
-        int err = 0;
-        int cpu;
-
-	err = request_percpu_irq(irq, *handle_irq, "l1-armpmu",
-			&cpu_hw_events);
-
-        if (!err) {
-                for_each_cpu(cpu, cpu_online_mask) {
-                        smp_call_function_single(cpu,
-                                        enable_irq_callback, &irq, 1);
-                }
-        }
-
-        return err;
-}
-
-static void
-msm_free_irq(int irq)
-{
-        int cpu;
-
-        if (irq >= 0) {
-                for_each_cpu(cpu, cpu_online_mask) {
-                        smp_call_function_single(cpu,
-                                        disable_irq_callback, &irq, 1);
-                }
-                free_percpu_irq(irq, &cpu_hw_events);
-        }
-}
-
 /*
  * We check for column exclusion constraints here.
  * Two events cant have same reg and same group.
@@ -621,8 +574,6 @@
 
 static struct arm_pmu krait_pmu = {
 	.handle_irq		= armv7pmu_handle_irq,
-	.request_pmu_irq	= msm_request_irq,
-	.free_pmu_irq		= msm_free_irq,
 	.enable			= krait_pmu_enable_event,
 	.disable		= krait_pmu_disable_event,
 	.read_counter		= armv7pmu_read_counter,
diff --git a/arch/arm/kernel/process.c b/arch/arm/kernel/process.c
index af21496..e7a9237 100644
--- a/arch/arm/kernel/process.c
+++ b/arch/arm/kernel/process.c
@@ -252,11 +252,6 @@
 		tick_nohz_idle_enter();
 		rcu_idle_enter();
 		while (!need_resched()) {
-#ifdef CONFIG_HOTPLUG_CPU
-			if (cpu_is_offline(smp_processor_id()))
-				cpu_die();
-#endif
-
 			/*
 			 * We need to disable interrupts here
 			 * to ensure we don't miss a wakeup call.
@@ -285,6 +280,10 @@
 		tick_nohz_idle_exit();
 		idle_notifier_call_chain(IDLE_END);
 		schedule_preempt_disabled();
+#ifdef CONFIG_HOTPLUG_CPU
+		if (cpu_is_offline(smp_processor_id()))
+			cpu_die();
+#endif
 	}
 }
 
@@ -680,6 +679,11 @@
 
 const char *arch_vma_name(struct vm_area_struct *vma)
 {
-	return (vma == &gate_vma) ? "[vectors]" : NULL;
+	if (vma == &gate_vma)
+		return "[vectors]";
+	else if (vma == get_user_timers_vma(NULL))
+		return "[timers]";
+	else
+		return NULL;
 }
 #endif
diff --git a/arch/arm/kernel/update_vsyscall_arm.c b/arch/arm/kernel/update_vsyscall_arm.c
new file mode 100644
index 0000000..51f47ae
--- /dev/null
+++ b/arch/arm/kernel/update_vsyscall_arm.c
@@ -0,0 +1,100 @@
+/* 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/export.h>
+#include <linux/clocksource.h>
+#include <linux/time.h>
+#include "update_vsyscall_arm.h"
+/*
+ * See entry-armv.S for the offsets into the kernel user helper for
+ * these fields.
+ */
+#define ARM_VSYSCALL_TIMER_TZ			0xf20
+#define ARM_VSYSCALL_TIMER_SEQ			0xf28
+#define ARM_VSYSCALL_TIMER_OFFSET		0xf30
+#define ARM_VSYSCALL_TIMER_WTM_TV_SEC		0xf38
+#define ARM_VSYSCALL_TIMER_WTM_TV_NSEC		0xf3c
+#define ARM_VSYSCALL_TIMER_CYCLE_LAST		0xf40
+#define ARM_VSYSCALL_TIMER_MASK			0xf48
+#define ARM_VSYSCALL_TIMER_MULT			0xf50
+#define ARM_VSYSCALL_TIMER_SHIFT		0xf54
+#define ARM_VSYSCALL_TIMER_TV_SEC		0xf58
+#define ARM_VSYSCALL_TIMER_TV_NSEC		0xf5c
+
+struct kernel_gtod_t {
+	u64  cycle_last;
+	u64  mask;
+	u32  mult;
+	u32  shift;
+	u32  tv_sec;
+	u32  tv_nsec;
+};
+
+struct kernel_tz_t {
+	u32  tz_minuteswest;
+	u32  tz_dsttime;
+};
+
+struct kernel_wtm_t {
+	u32  tv_sec;
+	u32  tv_nsec;
+};
+
+/*
+ * Updates the kernel user helper area with the current timespec
+ * data, as well as additional fields needed to calculate
+ * gettimeofday, clock_gettime, etc.
+ */
+void
+update_vsyscall(struct timespec *ts, struct timespec *wtm,
+	struct clocksource *c, u32 mult)
+{
+	unsigned long vectors = (unsigned long)vectors_page;
+	unsigned long flags;
+	unsigned *seqnum = (unsigned *)(vectors + ARM_VSYSCALL_TIMER_SEQ);
+	struct kernel_gtod_t *dgtod = (struct kernel_gtod_t *)(vectors +
+		ARM_VSYSCALL_TIMER_CYCLE_LAST);
+	struct kernel_wtm_t *dgwtm = (struct kernel_wtm_t *)(vectors +
+		ARM_VSYSCALL_TIMER_WTM_TV_SEC);
+
+	write_seqlock_irqsave(&kuh_time_lock, flags);
+	*seqnum = kuh_time_lock.sequence;
+	dgtod->cycle_last = c->cycle_last;
+	dgtod->mask = c->mask;
+	dgtod->mult = c->mult;
+	dgtod->shift = c->shift;
+	dgtod->tv_sec = ts->tv_sec;
+	dgtod->tv_nsec = ts->tv_nsec;
+	dgwtm->tv_sec = wtm->tv_sec;
+	dgwtm->tv_nsec = wtm->tv_nsec;
+	*seqnum = kuh_time_lock.sequence + 1;
+	write_sequnlock_irqrestore(&kuh_time_lock, flags);
+}
+EXPORT_SYMBOL(update_vsyscall);
+
+void
+update_vsyscall_tz(void)
+{
+	unsigned long vectors = (unsigned long)vectors_page;
+	unsigned long flags;
+	unsigned *seqnum = (unsigned *)(vectors + ARM_VSYSCALL_TIMER_SEQ);
+	struct kernel_tz_t *dgtod = (struct kernel_tz_t *)(vectors +
+		ARM_VSYSCALL_TIMER_TZ);
+
+	write_seqlock_irqsave(&kuh_time_lock, flags);
+	*seqnum = kuh_time_lock.sequence;
+	dgtod->tz_minuteswest = sys_tz.tz_minuteswest;
+	dgtod->tz_dsttime = sys_tz.tz_dsttime;
+	*seqnum = kuh_time_lock.sequence + 1;
+	write_sequnlock_irqrestore(&kuh_time_lock, flags);
+}
+EXPORT_SYMBOL(update_vsyscall_tz);
diff --git a/arch/arm/kernel/update_vsyscall_arm.h b/arch/arm/kernel/update_vsyscall_arm.h
new file mode 100644
index 0000000..d06ca56
--- /dev/null
+++ b/arch/arm/kernel/update_vsyscall_arm.h
@@ -0,0 +1,23 @@
+/* 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/export.h>
+#include <linux/clocksource.h>
+#include <linux/time.h>
+
+extern void *vectors_page;
+extern struct timezone sys_tz;
+
+/*
+ * This read-write spinlock protects us from races in SMP while
+ * updating the kernel user helper-embedded time.
+ */
+__cacheline_aligned_in_smp DEFINE_SEQLOCK(kuh_time_lock);
diff --git a/arch/arm/kernel/user_accessible_timer.c b/arch/arm/kernel/user_accessible_timer.c
new file mode 100644
index 0000000..c550c03
--- /dev/null
+++ b/arch/arm/kernel/user_accessible_timer.c
@@ -0,0 +1,132 @@
+/* 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/export.h>
+#include <linux/mm.h>
+#include <linux/sched.h>
+#include <asm/user_accessible_timer.h>
+#include <asm/traps.h>
+
+#define USER_ACCESS_TIMER_OFFSET	0xf30
+#define USER_ACCESS_FEATURE_OFFSET	0xf34
+#define USER_ACCESS_FEATURE_FLAG	0xffff0f20
+
+static struct vm_area_struct user_timers_vma;
+static int __init user_timers_vma_init(void)
+{
+	user_timers_vma.vm_start        = CONFIG_ARM_USER_ACCESSIBLE_TIMER_BASE;
+	user_timers_vma.vm_end          = CONFIG_ARM_USER_ACCESSIBLE_TIMER_BASE
+						+ PAGE_SIZE;
+	user_timers_vma.vm_page_prot    = PAGE_READONLY;
+	user_timers_vma.vm_flags        = VM_READ | VM_MAYREAD;
+	return 0;
+}
+arch_initcall(user_timers_vma_init);
+
+int in_user_timers_area(struct mm_struct *mm, unsigned long addr)
+{
+	return (addr >= user_timers_vma.vm_start) &&
+		(addr < user_timers_vma.vm_end);
+}
+EXPORT_SYMBOL(in_user_timers_area);
+
+struct vm_area_struct *get_user_timers_vma(struct mm_struct *mm)
+{
+	return &user_timers_vma;
+}
+EXPORT_SYMBOL(get_user_timers_vma);
+
+int get_user_timer_page(struct vm_area_struct *vma,
+	struct mm_struct *mm, unsigned long start, unsigned int gup_flags,
+	struct page **pages, int idx, int *goto_next_page)
+{
+	/* Replicates the earlier work done in mm/memory.c */
+	unsigned long pg = start & PAGE_MASK;
+	pgd_t *pgd;
+	pud_t *pud;
+	pmd_t *pmd;
+	pte_t *pte;
+
+	/* Unset this flag -- this only gets activated if the
+	 * caller should go straight to the next_page label on
+	 * return.
+	 */
+	*goto_next_page = 0;
+
+	/* user gate pages are read-only */
+	if (gup_flags & FOLL_WRITE)
+		return idx ? : -EFAULT;
+	if (pg > TASK_SIZE)
+		pgd = pgd_offset_k(pg);
+	else
+		pgd = pgd_offset_gate(mm, pg);
+	BUG_ON(pgd_none(*pgd));
+	pud = pud_offset(pgd, pg);
+	BUG_ON(pud_none(*pud));
+	pmd = pmd_offset(pud, pg);
+	if (pmd_none(*pmd))
+		return idx ? : -EFAULT;
+	VM_BUG_ON(pmd_trans_huge(*pmd));
+	pte = pte_offset_map(pmd, pg);
+	if (pte_none(*pte)) {
+		pte_unmap(pte);
+		return idx ? : -EFAULT;
+	}
+	vma = get_user_timers_vma(mm);
+	if (pages) {
+		struct page *page;
+
+		page = vm_normal_page(vma, start, *pte);
+		if (!page) {
+			if (!(gup_flags & FOLL_DUMP) &&
+				zero_pfn == pte_pfn(*pte))
+				page = pte_page(*pte);
+			else {
+				pte_unmap(pte);
+				return idx ? : -EFAULT;
+			}
+		}
+		pages[idx] = page;
+		get_page(page);
+	}
+	pte_unmap(pte);
+	/* In this case, set the next page */
+	*goto_next_page = 1;
+	return 0;
+}
+EXPORT_SYMBOL(get_user_timer_page);
+
+void setup_user_timer_offset(unsigned long addr)
+{
+#if defined(CONFIG_CPU_USE_DOMAINS)
+	unsigned long vectors = CONFIG_VECTORS_BASE;
+#else
+	unsigned long vectors = (unsigned long)vectors_page;
+#endif
+	unsigned long *timer_offset = (unsigned long *)(vectors +
+		USER_ACCESS_TIMER_OFFSET);
+	*timer_offset = addr;
+}
+EXPORT_SYMBOL(setup_user_timer_offset);
+
+void set_user_accessible_timer_flag(bool flag)
+{
+#if defined(CONFIG_CPU_USE_DOMAINS)
+	unsigned long vectors = CONFIG_VECTORS_BASE;
+#else
+	unsigned long vectors = (unsigned long)vectors_page;
+#endif
+	unsigned long *timer_offset = (unsigned long *)(vectors +
+		USER_ACCESS_FEATURE_OFFSET);
+	*timer_offset = (flag ? USER_ACCESS_FEATURE_FLAG : 0);
+}
+EXPORT_SYMBOL(set_user_accessible_timer_flag);
diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig
index 10c7089..90aed03 100644
--- a/arch/arm/mach-msm/Kconfig
+++ b/arch/arm/mach-msm/Kconfig
@@ -67,6 +67,7 @@
 	select MSM_PM2 if PM
 	select HOLES_IN_ZONE if SPARSEMEM
 	select MSM_MODEM_RESTART
+	select ARM_HAS_SG_CHAIN
 
 config ARCH_QSD8X50
 	bool "QSD8X50"
@@ -177,6 +178,11 @@
 	select ARM_HAS_SG_CHAIN
 	select MSM_KRAIT_WFE_FIXUP
 	select MSM_ULTRASOUND_A
+	select MSM_IOMMU_GPU_SYNC
+	select GENERIC_TIME_VSYSCALL
+	select USE_USER_ACCESSIBLE_TIMERS
+	select ARM_USE_USER_ACCESSIBLE_TIMERS
+	select MSM_USE_USER_ACCESSIBLE_TIMERS
 
 config ARCH_MSM8930
 	bool "MSM8930"
@@ -209,6 +215,11 @@
 	select HOLES_IN_ZONE if SPARSEMEM
 	select ARM_HAS_SG_CHAIN
 	select MSM_KRAIT_WFE_FIXUP
+	select MSM_IOMMU_GPU_SYNC
+	select GENERIC_TIME_VSYSCALL
+	select USE_USER_ACCESSIBLE_TIMERS
+	select ARM_USE_USER_ACCESSIBLE_TIMERS
+	select MSM_USE_USER_ACCESSIBLE_TIMERS
 
 config ARCH_APQ8064
 	bool "APQ8064"
@@ -237,6 +248,11 @@
 	select ARM_HAS_SG_CHAIN
 	select MSM_KRAIT_WFE_FIXUP
 	select MSM_ULTRASOUND_A
+	select MSM_IOMMU_GPU_SYNC
+	select GENERIC_TIME_VSYSCALL
+	select USE_USER_ACCESSIBLE_TIMERS
+	select ARM_USE_USER_ACCESSIBLE_TIMERS
+	select MSM_USE_USER_ACCESSIBLE_TIMERS
 
 config ARCH_MSM8974
 	bool "MSM8974"
@@ -264,6 +280,10 @@
 	select MSM_RPM_REGULATOR_SMD
 	select ARM_HAS_SG_CHAIN
 	select MSM_RUN_QUEUE_STATS
+	select MEMORY_HOLE_CARVEOUT
+	select MSM_RPM_STATS_LOG
+	select QMI_ENCDEC
+	select DONT_MAP_HOLE_AFTER_MEMBANK0
 
 config ARCH_MPQ8092
 	bool "MPQ8092"
@@ -277,30 +297,6 @@
 	select SPARSE_IRQ
 	select MSM_NOPM
 
-config ARCH_MSM8226
-	bool "MSM8226"
-	select ARCH_MSM_KRAITMP
-	select GPIO_MSM_V3
-	select ARM_GIC
-	select CPU_V7
-	select MSM_SCM if SMP
-	select MSM_GPIOMUX
-	select MULTI_IRQ_HANDLER
-	select MSM_MULTIMEDIA_USE_ION
-	select MSM_PIL
-	select MSM_SPM_V2
-	select MSM_L2_SPM
-	select MSM_PM8X60 if PM
-	select MAY_HAVE_SPARSE_IRQ
-	select SPARSE_IRQ
-	select MSM_RPM_SMD
-	select REGULATOR
-	select MSM_QDSP6_APRV2
-	select MSM_QDSP6V2_CODECS
-	select MSM_AUDIO_QDSP6V2 if SND_SOC
-	select MSM_RPM_REGULATOR_SMD
-	select ARM_HAS_SG_CHAIN
-
 config ARCH_FSM9XXX
 	bool "FSM9XXX"
 	select ARCH_MSM_SCORPION
@@ -351,7 +347,6 @@
 config ARCH_MSM9625
 	bool "MSM9625"
 	select ARM_GIC
-	select GIC_SECURE
 	select MIGHT_HAVE_CACHE_L2X0
 	select ARCH_MSM_CORTEX_A5
 	select SMP
@@ -370,12 +365,15 @@
 	select MAY_HAVE_SPARSE_IRQ
 	select SPARSE_IRQ
 	select MSM_MULTIMEDIA_USE_ION
+	select MSM_RPM_STATS_LOG
+	select MSM_QDSP6_APRV2
+	select MSM_QDSP6V2_CODECS
+	select MSM_AUDIO_QDSP6V2 if SND_SOC
 
 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
@@ -384,6 +382,23 @@
 	select MULTI_IRQ_HANDLER
 	select GPIO_MSM_V3
 	select MSM_GPIOMUX
+	select MSM_NATIVE_RESTART
+	select MSM_RESTART_V2
+
+config ARCH_MSM8226
+	bool "MSM8226"
+	select ARM_GIC
+	select GIC_SECURE
+	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
@@ -963,7 +978,7 @@
 	default "0x00000000" if ARCH_MSM8974
 	default "0x00000000" if ARCH_MPQ8092
 	default "0x00000000" if ARCH_MSM8226
-	default "0x80200000" if ARCH_MSM8910
+	default "0x00000000" if ARCH_MSM8910
 	default "0x10000000" if ARCH_FSM9XXX
 	default "0x00200000" if ARCH_MSM9625
 	default "0x00200000" if !MSM_STACKED_MEMORY
@@ -1523,6 +1538,27 @@
 	help
 	  SMD Transport Layer for IPC Router
 
+config MSM_IPC_ROUTER_SECURITY
+	depends on MSM_IPC_ROUTER
+	bool "MSM IPC Router Security support"
+	help
+	  This feature of IPC Router will enforce security rules
+	  configured by a security script from the user-space. IPC Router
+	  once configured with the security rules will ensure that the
+	  sender of the message to a service belongs to the relevant
+	  Linux group as configured by the security script.
+
+config MSM_QMI_INTERFACE
+	depends on MSM_IPC_ROUTER
+	depends on QMI_ENCDEC
+	default n
+	bool "MSM QMI Interface Library"
+	help
+	  Library to send and receive QMI messages over IPC Router.
+	  This library provides interface functions to the kernel drivers
+	  to perform QMI message marshaling and transport them over IPC
+	  Router.
+
 config MSM_ONCRPCROUTER_DEBUG
 	depends on MSM_ONCRPCROUTER
 	default y
@@ -1982,18 +2018,14 @@
 	  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 && MSM_SUBSYSTEM_RESTART
+	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"
@@ -2005,7 +2037,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.
 
@@ -2024,13 +2056,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.
@@ -2099,7 +2131,7 @@
 config MSM_RPM_STATS_LOG
 	tristate "MSM Resource Power Manager Stat Driver"
 	depends on DEBUG_FS
-	depends on MSM_RPM
+	depends on MSM_RPM || MSM_RPM_SMD
 	default n
 	  help
 	  This option enables a driver which reads RPM messages from a shared
@@ -2191,7 +2223,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
@@ -2659,4 +2691,12 @@
 	  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.
+
+config MSM_USE_USER_ACCESSIBLE_TIMERS
+	bool "Enables mapping an MSM timer counter page to user space."
+	depends on ARM_USE_USER_ACCESSIBLE_TIMERS
+	help
+	  Enables MSM-specific user accessible timers via a shared
+	  memory page containing the cycle counter.
+
 endif
diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile
index 6a62f8c..548f40e 100644
--- a/arch/arm/mach-msm/Makefile
+++ b/arch/arm/mach-msm/Makefile
@@ -46,15 +46,15 @@
 endif
 
 obj-$(CONFIG_SMP) += headsmp.o
+ifdef CONFIG_ARCH_MSM_CORTEXMP
 ifdef CONFIG_ARCH_MSM8625
 	obj-$(CONFIG_SMP) += platsmp-8625.o
 else
-ifdef CONFIG_ARCH_MSM8910
 	obj-$(CONFIG_SMP) += platsmp-8910.o
+endif
 else
 	obj-$(CONFIG_SMP) += platsmp.o
 endif
-endif
 obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o
 
 obj-$(CONFIG_MSM_CPU_AVS) += avs.o
@@ -84,7 +84,6 @@
 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
@@ -139,6 +138,8 @@
 obj-$(CONFIG_MSM_ONCRPCROUTER) += smd_rpcrouter_device.o
 obj-$(CONFIG_MSM_IPC_ROUTER) += ipc_router.o
 obj-$(CONFIG_MSM_IPC_ROUTER)+= ipc_socket.o
+obj-$(CONFIG_MSM_IPC_ROUTER_SECURITY)+= msm_ipc_router_security.o
+obj-$(CONFIG_MSM_QMI_INTERFACE) += msm_qmi_interface.o
 obj-$(CONFIG_DEBUG_FS) += smd_rpc_sym.o
 obj-$(CONFIG_MSM_ONCRPCROUTER) += smd_rpcrouter_servers.o
 obj-$(CONFIG_MSM_ONCRPCROUTER) += smd_rpcrouter_clients.o
@@ -282,6 +283,7 @@
 obj-$(CONFIG_MACH_MSM8930_MTP) += board-8930-all.o board-8930-regulator-pm8038.o board-8930-regulator-pm8917.o
 obj-$(CONFIG_MACH_MSM8930_FLUID) += board-8930-all.o board-8930-regulator-pm8038.o board-8930-regulator-pm8917.o
 obj-$(CONFIG_PM8921_BMS) += bms-batterydata.o bms-batterydata-desay.o batterydata-lib.o
+obj-$(CONFIG_QPNP_BMS) += bms-batterydata.o bms-batterydata-desay.o batterydata-lib.o
 obj-$(CONFIG_MACH_APQ8064_CDP) += board-8064-all.o board-8064-regulator.o
 obj-$(CONFIG_MACH_APQ8064_MTP) += board-8064-all.o board-8064-regulator.o
 obj-$(CONFIG_MACH_APQ8064_LIQUID) += board-8064-all.o board-8064-regulator.o
@@ -293,13 +295,15 @@
 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_MSM8226) += 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
-obj-$(CONFIG_ARCH_MSM8930) += acpuclock-8930.o acpuclock-8627.o acpuclock-8930aa.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 acpuclock-8930ab.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_ARCH_MSM8910) += board-8910.o board-8910-gpiomux.o
 
 obj-$(CONFIG_MACH_SAPPHIRE) += board-sapphire.o board-sapphire-gpio.o
 obj-$(CONFIG_MACH_SAPPHIRE) += board-sapphire-keypad.o board-sapphire-panel.o
@@ -330,7 +334,7 @@
 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
@@ -396,8 +400,12 @@
 
 obj-$(CONFIG_MSM_ENABLE_WDOG_DEBUG_CONTROL) += wdog_debug.o
 
+obj-$(CONFIG_MSM_USE_USER_ACCESSIBLE_TIMERS) += timer_page.o
+
 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 fa9ee54..cf1f401 100644
--- a/arch/arm/mach-msm/Makefile.boot
+++ b/arch/arm/mach-msm/Makefile.boot
@@ -66,4 +66,4 @@
    zreladdr-$(CONFIG_ARCH_MPQ8092)	:= 0x00008000
 
 # MSM8910
-   zreladdr-$(CONFIG_ARCH_MSM8910)	:= 0x80208000
+   zreladdr-$(CONFIG_ARCH_MSM8910)	:= 0x00008000
diff --git a/arch/arm/mach-msm/acpuclock-8064.c b/arch/arm/mach-msm/acpuclock-8064.c
index cda952f..8174370 100644
--- a/arch/arm/mach-msm/acpuclock-8064.c
+++ b/arch/arm/mach-msm/acpuclock-8064.c
@@ -213,60 +213,193 @@
 	{ 0, { 0 } }
 };
 
+static struct acpu_level tbl_faster[] __initdata = {
+	{ 1, {   384000, PLL_8, 0, 0x00 }, L2(0),   850000 },
+	{ 0, {   432000, HFPLL, 2, 0x20 }, L2(6),   875000 },
+	{ 1, {   486000, HFPLL, 2, 0x24 }, L2(6),   875000 },
+	{ 0, {   540000, HFPLL, 2, 0x28 }, L2(6),   900000 },
+	{ 1, {   594000, HFPLL, 1, 0x16 }, L2(6),   900000 },
+	{ 0, {   648000, HFPLL, 1, 0x18 }, L2(6),   925000 },
+	{ 1, {   702000, HFPLL, 1, 0x1A }, L2(6),   925000 },
+	{ 0, {   756000, HFPLL, 1, 0x1C }, L2(6),   962500 },
+	{ 1, {   810000, HFPLL, 1, 0x1E }, L2(6),   962500 },
+	{ 0, {   864000, HFPLL, 1, 0x20 }, L2(6),   975000 },
+	{ 1, {   918000, HFPLL, 1, 0x22 }, L2(6),   975000 },
+	{ 0, {   972000, HFPLL, 1, 0x24 }, L2(6),  1000000 },
+	{ 1, {  1026000, HFPLL, 1, 0x26 }, L2(6),  1000000 },
+	{ 0, {  1080000, HFPLL, 1, 0x28 }, L2(15), 1050000 },
+	{ 1, {  1134000, HFPLL, 1, 0x2A }, L2(15), 1050000 },
+	{ 0, {  1188000, HFPLL, 1, 0x2C }, L2(15), 1075000 },
+	{ 1, {  1242000, HFPLL, 1, 0x2E }, L2(15), 1075000 },
+	{ 0, {  1296000, HFPLL, 1, 0x30 }, L2(15), 1100000 },
+	{ 1, {  1350000, HFPLL, 1, 0x32 }, L2(15), 1100000 },
+	{ 0, {  1404000, HFPLL, 1, 0x34 }, L2(15), 1112500 },
+	{ 1, {  1458000, HFPLL, 1, 0x36 }, L2(15), 1112500 },
+	{ 1, {  1512000, HFPLL, 1, 0x38 }, L2(15), 1125000 },
+	{ 0, { 0 } }
+};
+
 static struct acpu_level tbl_PVS0_1700MHz[] __initdata = {
 	{ 1, {   384000, PLL_8, 0, 0x00 }, L2(0),   950000 },
-	{ 1, {   486000, HFPLL, 2, 0x24 }, L2(6),   975000 },
-	{ 1, {   594000, HFPLL, 1, 0x16 }, L2(6),  1000000 },
-	{ 1, {   702000, HFPLL, 1, 0x1A }, L2(6),  1025000 },
-	{ 1, {   810000, HFPLL, 1, 0x1E }, L2(6),  1075000 },
-	{ 1, {   918000, HFPLL, 1, 0x22 }, L2(6),  1100000 },
-	{ 1, {  1026000, HFPLL, 1, 0x26 }, L2(6),  1125000 },
-	{ 1, {  1134000, HFPLL, 1, 0x2A }, L2(15), 1175000 },
-	{ 1, {  1242000, HFPLL, 1, 0x2E }, L2(15), 1200000 },
-	{ 1, {  1350000, HFPLL, 1, 0x32 }, L2(15), 1225000 },
-	{ 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, {   486000, HFPLL, 2, 0x24 }, L2(6),   950000 },
+	{ 1, {   594000, HFPLL, 1, 0x16 }, L2(6),   950000 },
+	{ 1, {   702000, HFPLL, 1, 0x1A }, L2(6),   962500 },
+	{ 1, {   810000, HFPLL, 1, 0x1E }, L2(6),  1000000 },
+	{ 1, {   918000, HFPLL, 1, 0x22 }, L2(6),  1025000 },
+	{ 1, {  1026000, HFPLL, 1, 0x26 }, L2(6),  1037500 },
+	{ 1, {  1134000, HFPLL, 1, 0x2A }, L2(15), 1075000 },
+	{ 1, {  1242000, HFPLL, 1, 0x2E }, L2(15), 1087500 },
+	{ 1, {  1350000, HFPLL, 1, 0x32 }, L2(15), 1125000 },
+	{ 1, {  1458000, HFPLL, 1, 0x36 }, L2(15), 1150000 },
+	{ 1, {  1566000, HFPLL, 1, 0x3A }, L2(15), 1175000 },
+	{ 1, {  1674000, HFPLL, 1, 0x3E }, L2(15), 1225000 },
+	{ 1, {  1728000, HFPLL, 1, 0x40 }, L2(15), 1250000 },
 	{ 0, { 0 } }
 };
 
-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 },
+static struct acpu_level tbl_PVS1_1700MHz[] __initdata = {
+	{ 1, {   384000, PLL_8, 0, 0x00 }, L2(0),   950000 },
+	{ 1, {   486000, HFPLL, 2, 0x24 }, L2(6),   950000 },
+	{ 1, {   594000, HFPLL, 1, 0x16 }, L2(6),   950000 },
+	{ 1, {   702000, HFPLL, 1, 0x1A }, L2(6),   962500 },
+	{ 1, {   810000, HFPLL, 1, 0x1E }, L2(6),   975000 },
+	{ 1, {   918000, HFPLL, 1, 0x22 }, L2(6),  1000000 },
+	{ 1, {  1026000, HFPLL, 1, 0x26 }, L2(6),  1012500 },
+	{ 1, {  1134000, HFPLL, 1, 0x2A }, L2(15), 1037500 },
+	{ 1, {  1242000, HFPLL, 1, 0x2E }, L2(15), 1050000 },
+	{ 1, {  1350000, HFPLL, 1, 0x32 }, L2(15), 1087500 },
 	{ 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 },
+	{ 1, {  1674000, HFPLL, 1, 0x3E }, L2(15), 1187500 },
+	{ 1, {  1728000, HFPLL, 1, 0x40 }, L2(15), 1200000 },
 	{ 0, { 0 } }
 };
 
-static struct acpu_level tbl_PVS1_2000MHz[] __initdata = {
+static struct acpu_level tbl_PVS2_1700MHz[] __initdata = {
+	{ 1, {   384000, PLL_8, 0, 0x00 }, L2(0),   925000 },
+	{ 1, {   486000, HFPLL, 2, 0x24 }, L2(6),   925000 },
+	{ 1, {   594000, HFPLL, 1, 0x16 }, L2(6),   925000 },
+	{ 1, {   702000, HFPLL, 1, 0x1A }, L2(6),   925000 },
+	{ 1, {   810000, HFPLL, 1, 0x1E }, L2(6),   937500 },
+	{ 1, {   918000, HFPLL, 1, 0x22 }, L2(6),   950000 },
+	{ 1, {  1026000, HFPLL, 1, 0x26 }, L2(6),   975000 },
+	{ 1, {  1134000, HFPLL, 1, 0x2A }, L2(15), 1000000 },
+	{ 1, {  1242000, HFPLL, 1, 0x2E }, L2(15), 1012500 },
+	{ 1, {  1350000, HFPLL, 1, 0x32 }, L2(15), 1037500 },
+	{ 1, {  1458000, HFPLL, 1, 0x36 }, L2(15), 1075000 },
+	{ 1, {  1566000, HFPLL, 1, 0x3A }, L2(15), 1100000 },
+	{ 1, {  1674000, HFPLL, 1, 0x3E }, L2(15), 1137500 },
+	{ 1, {  1728000, HFPLL, 1, 0x40 }, L2(15), 1162500 },
+	{ 0, { 0 } }
+};
+
+static struct acpu_level tbl_PVS3_1700MHz[] __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, {   918000, HFPLL, 1, 0x22 }, L2(6),   925000 },
+	{ 1, {  1026000, HFPLL, 1, 0x26 }, L2(6),   950000 },
+	{ 1, {  1134000, HFPLL, 1, 0x2A }, L2(15),  975000 },
+	{ 1, {  1242000, HFPLL, 1, 0x2E }, L2(15),  987500 },
+	{ 1, {  1350000, HFPLL, 1, 0x32 }, L2(15), 1000000 },
+	{ 1, {  1458000, HFPLL, 1, 0x36 }, L2(15), 1037500 },
+	{ 1, {  1566000, HFPLL, 1, 0x3A }, L2(15), 1062500 },
+	{ 1, {  1674000, HFPLL, 1, 0x3E }, L2(15), 1100000 },
+	{ 1, {  1728000, HFPLL, 1, 0x40 }, L2(15), 1125000 },
+	{ 0, { 0 } }
+};
+
+static struct acpu_level tbl_PVS4_1700MHz[] __initdata = {
+	{ 1, {   384000, PLL_8, 0, 0x00 }, L2(0),   875000 },
+	{ 1, {   486000, HFPLL, 2, 0x24 }, L2(6),   875000 },
+	{ 1, {   594000, HFPLL, 1, 0x16 }, L2(6),   875000 },
+	{ 1, {   702000, HFPLL, 1, 0x1A }, L2(6),   875000 },
+	{ 1, {   810000, HFPLL, 1, 0x1E }, L2(6),   887500 },
+	{ 1, {   918000, HFPLL, 1, 0x22 }, L2(6),   900000 },
+	{ 1, {  1026000, HFPLL, 1, 0x26 }, L2(6),   925000 },
+	{ 1, {  1134000, HFPLL, 1, 0x2A }, L2(15),  950000 },
+	{ 1, {  1242000, HFPLL, 1, 0x2E }, L2(15),  962500 },
+	{ 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), 1075000 },
+	{ 1, {  1728000, HFPLL, 1, 0x40 }, L2(15), 1100000 },
+	{ 0, { 0 } }
+};
+
+static struct acpu_level tbl_PVS5_1700MHz[] __initdata = {
+	{ 1, {   384000, PLL_8, 0, 0x00 }, L2(0),   875000 },
+	{ 1, {   486000, HFPLL, 2, 0x24 }, L2(6),   875000 },
+	{ 1, {   594000, HFPLL, 1, 0x16 }, L2(6),   875000 },
+	{ 1, {   702000, HFPLL, 1, 0x1A }, L2(6),   875000 },
+	{ 1, {   810000, HFPLL, 1, 0x1E }, L2(6),   887500 },
+	{ 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), 1050000 },
+	{ 1, {  1728000, HFPLL, 1, 0x40 }, L2(15), 1075000 },
+	{ 0, { 0 } }
+};
+
+static struct acpu_level tbl_PVS6_1700MHz[] __initdata = {
+	{ 1, {   384000, PLL_8, 0, 0x00 }, L2(0),   875000 },
+	{ 1, {   486000, HFPLL, 2, 0x24 }, L2(6),   875000 },
+	{ 1, {   594000, HFPLL, 1, 0x16 }, L2(6),   875000 },
+	{ 1, {   702000, HFPLL, 1, 0x1A }, L2(6),   875000 },
+	{ 1, {   810000, HFPLL, 1, 0x1E }, L2(6),   887500 },
+	{ 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, {  1728000, HFPLL, 1, 0x40 }, L2(15), 1050000 },
+	{ 0, { 0 } }
+};
+
+static struct acpu_level tbl_PVS0_2000MHz[] __initdata = {
+	{ 1, {   384000, PLL_8, 0, 0x00 }, L2(0),   950000 },
+	{ 1, {   486000, HFPLL, 2, 0x24 }, L2(6),   950000 },
+	{ 1, {   594000, HFPLL, 1, 0x16 }, L2(6),   950000 },
+	{ 1, {   702000, HFPLL, 1, 0x1A }, L2(6),   950000 },
+	{ 1, {   810000, HFPLL, 1, 0x1E }, L2(6),   962500 },
+	{ 1, {   918000, HFPLL, 1, 0x22 }, L2(6),   975000 },
+	{ 1, {  1026000, HFPLL, 1, 0x26 }, L2(6),  1000000 },
+	{ 1, {  1134000, HFPLL, 1, 0x2A }, L2(15), 1025000 },
+	{ 1, {  1242000, HFPLL, 1, 0x2E }, L2(15), 1037500 },
+	{ 1, {  1350000, HFPLL, 1, 0x32 }, L2(15), 1062500 },
+	{ 1, {  1458000, HFPLL, 1, 0x36 }, L2(15), 1100000 },
+	{ 1, {  1566000, HFPLL, 1, 0x3A }, L2(15), 1125000 },
+	{ 1, {  1674000, HFPLL, 1, 0x3E }, L2(15), 1175000 },
+	{ 1, {  1782000, HFPLL, 1, 0x42 }, L2(15), 1225000 },
+	{ 1, {  1890000, HFPLL, 1, 0x46 }, L2(15), 1287500 },
+	{ 0, { 0 } }
+};
+
+static struct acpu_level tbl_PVS1_2000MHz[] __initdata = {
+	{ 1, {   384000, PLL_8, 0, 0x00 }, L2(0),   925000 },
+	{ 1, {   486000, HFPLL, 2, 0x24 }, L2(6),   925000 },
+	{ 1, {   594000, HFPLL, 1, 0x16 }, L2(6),   925000 },
+	{ 1, {   702000, HFPLL, 1, 0x1A }, L2(6),   925000 },
+	{ 1, {   810000, HFPLL, 1, 0x1E }, L2(6),   937500 },
+	{ 1, {   918000, HFPLL, 1, 0x22 }, L2(6),   950000 },
+	{ 1, {  1026000, HFPLL, 1, 0x26 }, L2(6),   975000 },
 	{ 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 },
+	{ 1, {  1350000, HFPLL, 1, 0x32 }, L2(15), 1037500 },
+	{ 1, {  1458000, HFPLL, 1, 0x36 }, L2(15), 1075000 },
+	{ 1, {  1566000, HFPLL, 1, 0x3A }, L2(15), 1100000 },
+	{ 1, {  1674000, HFPLL, 1, 0x3E }, L2(15), 1137500 },
+	{ 1, {  1782000, HFPLL, 1, 0x42 }, L2(15), 1187500 },
+	{ 1, {  1890000, HFPLL, 1, 0x46 }, L2(15), 1250000 },
 	{ 0, { 0 } }
 };
 
@@ -275,17 +408,17 @@
 	{ 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, {   810000, HFPLL, 1, 0x1E }, L2(6),   912500 },
+	{ 1, {   918000, HFPLL, 1, 0x22 }, L2(6),   925000 },
+	{ 1, {  1026000, HFPLL, 1, 0x26 }, L2(6),   950000 },
+	{ 1, {  1134000, HFPLL, 1, 0x2A }, L2(15),  975000 },
+	{ 1, {  1242000, HFPLL, 1, 0x2E }, L2(15),  987500 },
+	{ 1, {  1350000, HFPLL, 1, 0x32 }, L2(15), 1012500 },
+	{ 1, {  1458000, HFPLL, 1, 0x36 }, L2(15), 1050000 },
+	{ 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), 1212500 },
 	{ 0, { 0 } }
 };
 
@@ -295,44 +428,44 @@
 	{ 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, {   918000, HFPLL, 1, 0x22 }, L2(6),   912500 },
+	{ 1, {  1026000, HFPLL, 1, 0x26 }, L2(6),   937500 },
 	{ 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 },
+	{ 1, {  1350000, HFPLL, 1, 0x32 }, L2(15), 1000000 },
+	{ 1, {  1458000, HFPLL, 1, 0x36 }, L2(15), 1025000 },
+	{ 1, {  1566000, HFPLL, 1, 0x3A }, L2(15), 1050000 },
+	{ 1, {  1674000, HFPLL, 1, 0x3E }, L2(15), 1087500 },
+	{ 1, {  1782000, HFPLL, 1, 0x42 }, L2(15), 1137500 },
+	{ 1, {  1890000, HFPLL, 1, 0x46 }, L2(15), 1175000 },
 	{ 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, {   384000, PLL_8, 0, 0x00 }, L2(0),   875000 },
+	{ 1, {   486000, HFPLL, 2, 0x24 }, L2(6),   875000 },
+	{ 1, {   594000, HFPLL, 1, 0x16 }, L2(6),   875000 },
+	{ 1, {   702000, HFPLL, 1, 0x1A }, L2(6),   875000 },
+	{ 1, {   810000, HFPLL, 1, 0x1E }, L2(6),   887500 },
 	{ 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, {  1134000, HFPLL, 1, 0x2A }, L2(15),  950000 },
+	{ 1, {  1242000, HFPLL, 1, 0x2E }, L2(15),  962500 },
 	{ 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, {  1674000, HFPLL, 1, 0x3E }, L2(15), 1075000 },
 	{ 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, {   384000, PLL_8, 0, 0x00 }, L2(0),   875000 },
+	{ 1, {   486000, HFPLL, 2, 0x24 }, L2(6),   875000 },
+	{ 1, {   594000, HFPLL, 1, 0x16 }, L2(6),   875000 },
+	{ 1, {   702000, HFPLL, 1, 0x1A }, L2(6),   875000 },
+	{ 1, {   810000, HFPLL, 1, 0x1E }, L2(6),   887500 },
 	{ 1, {   918000, HFPLL, 1, 0x22 }, L2(6),   900000 },
 	{ 1, {  1026000, HFPLL, 1, 0x26 }, L2(6),   925000 },
 	{ 1, {  1134000, HFPLL, 1, 0x2A }, L2(15),  937500 },
@@ -340,18 +473,18 @@
 	{ 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, {  1674000, HFPLL, 1, 0x3E }, L2(15), 1050000 },
 	{ 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, {   384000, PLL_8, 0, 0x00 }, L2(0),   875000 },
+	{ 1, {   486000, HFPLL, 2, 0x24 }, L2(6),   875000 },
+	{ 1, {   594000, HFPLL, 1, 0x16 }, L2(6),   875000 },
+	{ 1, {   702000, HFPLL, 1, 0x1A }, L2(6),   875000 },
+	{ 1, {   810000, HFPLL, 1, 0x1E }, L2(6),   887500 },
 	{ 1, {   918000, HFPLL, 1, 0x22 }, L2(6),   900000 },
 	{ 1, {  1026000, HFPLL, 1, 0x26 }, L2(6),   925000 },
 	{ 1, {  1134000, HFPLL, 1, 0x2A }, L2(15),  937500 },
@@ -369,15 +502,15 @@
 	[0][PVS_SLOW]    = {tbl_slow, sizeof(tbl_slow),     0 },
 	[0][PVS_NOMINAL] = {tbl_nom,  sizeof(tbl_nom),  25000 },
 	[0][PVS_FAST]    = {tbl_fast, sizeof(tbl_fast), 25000 },
-	[0][PVS_FASTER]  = {tbl_fast, sizeof(tbl_fast), 25000 },
+	[0][PVS_FASTER]  = {tbl_faster, sizeof(tbl_faster), 25000 },
 
 	[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 },
+	[1][1] = { tbl_PVS1_1700MHz, sizeof(tbl_PVS1_1700MHz),     0 },
+	[1][2] = { tbl_PVS2_1700MHz, sizeof(tbl_PVS2_1700MHz),     0 },
+	[1][3] = { tbl_PVS3_1700MHz, sizeof(tbl_PVS3_1700MHz),     0 },
+	[1][4] = { tbl_PVS4_1700MHz, sizeof(tbl_PVS4_1700MHz),     0 },
+	[1][5] = { tbl_PVS5_1700MHz, sizeof(tbl_PVS5_1700MHz),     0 },
+	[1][6] = { tbl_PVS6_1700MHz, sizeof(tbl_PVS6_1700MHz),     0 },
 
 	[2][0] = { tbl_PVS0_2000MHz, sizeof(tbl_PVS0_2000MHz),     0 },
 	[2][1] = { tbl_PVS1_2000MHz, sizeof(tbl_PVS1_2000MHz),     0 },
diff --git a/arch/arm/mach-msm/acpuclock-8930.c b/arch/arm/mach-msm/acpuclock-8930.c
index e46599a..948ecdd 100644
--- a/arch/arm/mach-msm/acpuclock-8930.c
+++ b/arch/arm/mach-msm/acpuclock-8930.c
@@ -155,19 +155,19 @@
 
 static struct acpu_level acpu_freq_tbl_slow[] __initdata = {
 	{ 1, {   384000, PLL_8, 0, 0x00 }, L2(0),   950000 },
-	{ 1, {   432000, HFPLL, 2, 0x20 }, L2(5),   975000 },
+	{ 0, {   432000, HFPLL, 2, 0x20 }, L2(5),   975000 },
 	{ 1, {   486000, HFPLL, 2, 0x24 }, L2(5),   975000 },
-	{ 1, {   540000, HFPLL, 2, 0x28 }, L2(5),  1000000 },
+	{ 0, {   540000, HFPLL, 2, 0x28 }, L2(5),  1000000 },
 	{ 1, {   594000, HFPLL, 1, 0x16 }, L2(5),  1000000 },
-	{ 1, {   648000, HFPLL, 1, 0x18 }, L2(5),  1025000 },
+	{ 0, {   648000, HFPLL, 1, 0x18 }, L2(5),  1025000 },
 	{ 1, {   702000, HFPLL, 1, 0x1A }, L2(5),  1025000 },
-	{ 1, {   756000, HFPLL, 1, 0x1C }, L2(10), 1075000 },
+	{ 0, {   756000, HFPLL, 1, 0x1C }, L2(10), 1075000 },
 	{ 1, {   810000, HFPLL, 1, 0x1E }, L2(10), 1075000 },
-	{ 1, {   864000, HFPLL, 1, 0x20 }, L2(10), 1100000 },
+	{ 0, {   864000, HFPLL, 1, 0x20 }, L2(10), 1100000 },
 	{ 1, {   918000, HFPLL, 1, 0x22 }, L2(10), 1100000 },
-	{ 1, {   972000, HFPLL, 1, 0x24 }, L2(10), 1125000 },
+	{ 0, {   972000, HFPLL, 1, 0x24 }, L2(10), 1125000 },
 	{ 1, {  1026000, HFPLL, 1, 0x26 }, L2(10), 1125000 },
-	{ 1, {  1080000, HFPLL, 1, 0x28 }, L2(15), 1175000 },
+	{ 0, {  1080000, HFPLL, 1, 0x28 }, L2(15), 1175000 },
 	{ 1, {  1134000, HFPLL, 1, 0x2A }, L2(15), 1175000 },
 	{ 1, {  1188000, HFPLL, 1, 0x2C }, L2(15), 1200000 },
 	{ 0, { 0 } }
@@ -175,19 +175,19 @@
 
 static struct acpu_level acpu_freq_tbl_nom[] __initdata = {
 	{ 1, {   384000, PLL_8, 0, 0x00 }, L2(0),   925000 },
-	{ 1, {   432000, HFPLL, 2, 0x20 }, L2(5),   950000 },
+	{ 0, {   432000, HFPLL, 2, 0x20 }, L2(5),   950000 },
 	{ 1, {   486000, HFPLL, 2, 0x24 }, L2(5),   950000 },
-	{ 1, {   540000, HFPLL, 2, 0x28 }, L2(5),   975000 },
+	{ 0, {   540000, HFPLL, 2, 0x28 }, L2(5),   975000 },
 	{ 1, {   594000, HFPLL, 1, 0x16 }, L2(5),   975000 },
-	{ 1, {   648000, HFPLL, 1, 0x18 }, L2(5),  1000000 },
+	{ 0, {   648000, HFPLL, 1, 0x18 }, L2(5),  1000000 },
 	{ 1, {   702000, HFPLL, 1, 0x1A }, L2(5),  1000000 },
-	{ 1, {   756000, HFPLL, 1, 0x1C }, L2(10), 1050000 },
+	{ 0, {   756000, HFPLL, 1, 0x1C }, L2(10), 1050000 },
 	{ 1, {   810000, HFPLL, 1, 0x1E }, L2(10), 1050000 },
-	{ 1, {   864000, HFPLL, 1, 0x20 }, L2(10), 1075000 },
+	{ 0, {   864000, HFPLL, 1, 0x20 }, L2(10), 1075000 },
 	{ 1, {   918000, HFPLL, 1, 0x22 }, L2(10), 1075000 },
-	{ 1, {   972000, HFPLL, 1, 0x24 }, L2(10), 1100000 },
+	{ 0, {   972000, HFPLL, 1, 0x24 }, L2(10), 1100000 },
 	{ 1, {  1026000, HFPLL, 1, 0x26 }, L2(10), 1100000 },
-	{ 1, {  1080000, HFPLL, 1, 0x28 }, L2(15), 1150000 },
+	{ 0, {  1080000, HFPLL, 1, 0x28 }, L2(15), 1150000 },
 	{ 1, {  1134000, HFPLL, 1, 0x2A }, L2(15), 1150000 },
 	{ 1, {  1188000, HFPLL, 1, 0x2C }, L2(15), 1175000 },
 	{ 0, { 0 } }
@@ -195,19 +195,19 @@
 
 static struct acpu_level acpu_freq_tbl_fast[] __initdata = {
 	{ 1, {   384000, PLL_8, 0, 0x00 }, L2(0),   900000 },
-	{ 1, {   432000, HFPLL, 2, 0x20 }, L2(5),   900000 },
+	{ 0, {   432000, HFPLL, 2, 0x20 }, L2(5),   900000 },
 	{ 1, {   486000, HFPLL, 2, 0x24 }, L2(5),   900000 },
-	{ 1, {   540000, HFPLL, 2, 0x28 }, L2(5),   925000 },
+	{ 0, {   540000, HFPLL, 2, 0x28 }, L2(5),   925000 },
 	{ 1, {   594000, HFPLL, 1, 0x16 }, L2(5),   925000 },
-	{ 1, {   648000, HFPLL, 1, 0x18 }, L2(5),   950000 },
+	{ 0, {   648000, HFPLL, 1, 0x18 }, L2(5),   950000 },
 	{ 1, {   702000, HFPLL, 1, 0x1A }, L2(5),   950000 },
-	{ 1, {   756000, HFPLL, 1, 0x1C }, L2(10), 1000000 },
+	{ 0, {   756000, HFPLL, 1, 0x1C }, L2(10), 1000000 },
 	{ 1, {   810000, HFPLL, 1, 0x1E }, L2(10), 1000000 },
-	{ 1, {   864000, HFPLL, 1, 0x20 }, L2(10), 1025000 },
+	{ 0, {   864000, HFPLL, 1, 0x20 }, L2(10), 1025000 },
 	{ 1, {   918000, HFPLL, 1, 0x22 }, L2(10), 1025000 },
-	{ 1, {   972000, HFPLL, 1, 0x24 }, L2(10), 1050000 },
+	{ 0, {   972000, HFPLL, 1, 0x24 }, L2(10), 1050000 },
 	{ 1, {  1026000, HFPLL, 1, 0x26 }, L2(10), 1050000 },
-	{ 1, {  1080000, HFPLL, 1, 0x28 }, L2(15), 1100000 },
+	{ 0, {  1080000, HFPLL, 1, 0x28 }, L2(15), 1100000 },
 	{ 1, {  1134000, HFPLL, 1, 0x2A }, L2(15), 1100000 },
 	{ 1, {  1188000, HFPLL, 1, 0x2C }, L2(15), 1125000 },
 	{ 0, { 0 } }
diff --git a/arch/arm/mach-msm/acpuclock-8930aa.c b/arch/arm/mach-msm/acpuclock-8930aa.c
index 9d2b6fc..8d48b54 100644
--- a/arch/arm/mach-msm/acpuclock-8930aa.c
+++ b/arch/arm/mach-msm/acpuclock-8930aa.c
@@ -119,23 +119,23 @@
 
 static struct acpu_level acpu_freq_tbl_slow[] __initdata = {
 	{ 1, {   384000, PLL_8, 0, 0x00 }, L2(0),   950000 },
-	{ 1, {   432000, HFPLL, 2, 0x20 }, L2(5),   975000 },
+	{ 0, {   432000, HFPLL, 2, 0x20 }, L2(5),   975000 },
 	{ 1, {   486000, HFPLL, 2, 0x24 }, L2(5),   975000 },
-	{ 1, {   540000, HFPLL, 2, 0x28 }, L2(5),  1000000 },
+	{ 0, {   540000, HFPLL, 2, 0x28 }, L2(5),  1000000 },
 	{ 1, {   594000, HFPLL, 1, 0x16 }, L2(5),  1000000 },
-	{ 1, {   648000, HFPLL, 1, 0x18 }, L2(5),  1025000 },
+	{ 0, {   648000, HFPLL, 1, 0x18 }, L2(5),  1025000 },
 	{ 1, {   702000, HFPLL, 1, 0x1A }, L2(5),  1025000 },
-	{ 1, {   756000, HFPLL, 1, 0x1C }, L2(10), 1075000 },
+	{ 0, {   756000, HFPLL, 1, 0x1C }, L2(10), 1075000 },
 	{ 1, {   810000, HFPLL, 1, 0x1E }, L2(10), 1075000 },
-	{ 1, {   864000, HFPLL, 1, 0x20 }, L2(10), 1100000 },
+	{ 0, {   864000, HFPLL, 1, 0x20 }, L2(10), 1100000 },
 	{ 1, {   918000, HFPLL, 1, 0x22 }, L2(10), 1100000 },
-	{ 1, {   972000, HFPLL, 1, 0x24 }, L2(10), 1125000 },
+	{ 0, {   972000, HFPLL, 1, 0x24 }, L2(10), 1125000 },
 	{ 1, {  1026000, HFPLL, 1, 0x26 }, L2(10), 1125000 },
-	{ 1, {  1080000, HFPLL, 1, 0x28 }, L2(15), 1175000 },
+	{ 0, {  1080000, HFPLL, 1, 0x28 }, L2(15), 1175000 },
 	{ 1, {  1134000, HFPLL, 1, 0x2A }, L2(15), 1175000 },
-	{ 1, {  1188000, HFPLL, 1, 0x2C }, L2(15), 1200000 },
+	{ 0, {  1188000, HFPLL, 1, 0x2C }, L2(15), 1200000 },
 	{ 1, {  1242000, HFPLL, 1, 0x2E }, L2(15), 1200000 },
-	{ 1, {  1296000, HFPLL, 1, 0x30 }, L2(15), 1225000 },
+	{ 0, {  1296000, HFPLL, 1, 0x30 }, L2(15), 1225000 },
 	{ 1, {  1350000, HFPLL, 1, 0x32 }, L2(15), 1225000 },
 	{ 1, {  1404000, HFPLL, 1, 0x34 }, L2(15), 1237500 },
 	{ 0, { 0 } }
@@ -143,23 +143,23 @@
 
 static struct acpu_level acpu_freq_tbl_nom[] __initdata = {
 	{ 1, {   384000, PLL_8, 0, 0x00 }, L2(0),   925000 },
-	{ 1, {   432000, HFPLL, 2, 0x20 }, L2(5),   950000 },
+	{ 0, {   432000, HFPLL, 2, 0x20 }, L2(5),   950000 },
 	{ 1, {   486000, HFPLL, 2, 0x24 }, L2(5),   950000 },
-	{ 1, {   540000, HFPLL, 2, 0x28 }, L2(5),   975000 },
+	{ 0, {   540000, HFPLL, 2, 0x28 }, L2(5),   975000 },
 	{ 1, {   594000, HFPLL, 1, 0x16 }, L2(5),   975000 },
-	{ 1, {   648000, HFPLL, 1, 0x18 }, L2(5),  1000000 },
+	{ 0, {   648000, HFPLL, 1, 0x18 }, L2(5),  1000000 },
 	{ 1, {   702000, HFPLL, 1, 0x1A }, L2(5),  1000000 },
-	{ 1, {   756000, HFPLL, 1, 0x1C }, L2(10), 1050000 },
+	{ 0, {   756000, HFPLL, 1, 0x1C }, L2(10), 1050000 },
 	{ 1, {   810000, HFPLL, 1, 0x1E }, L2(10), 1050000 },
-	{ 1, {   864000, HFPLL, 1, 0x20 }, L2(10), 1075000 },
+	{ 0, {   864000, HFPLL, 1, 0x20 }, L2(10), 1075000 },
 	{ 1, {   918000, HFPLL, 1, 0x22 }, L2(10), 1075000 },
-	{ 1, {   972000, HFPLL, 1, 0x24 }, L2(10), 1100000 },
+	{ 0, {   972000, HFPLL, 1, 0x24 }, L2(10), 1100000 },
 	{ 1, {  1026000, HFPLL, 1, 0x26 }, L2(10), 1100000 },
-	{ 1, {  1080000, HFPLL, 1, 0x28 }, L2(15), 1150000 },
+	{ 0, {  1080000, HFPLL, 1, 0x28 }, L2(15), 1150000 },
 	{ 1, {  1134000, HFPLL, 1, 0x2A }, L2(15), 1150000 },
-	{ 1, {  1188000, HFPLL, 1, 0x2C }, L2(15), 1175000 },
+	{ 0, {  1188000, HFPLL, 1, 0x2C }, L2(15), 1175000 },
 	{ 1, {  1242000, HFPLL, 1, 0x2E }, L2(15), 1175000 },
-	{ 1, {  1296000, HFPLL, 1, 0x30 }, L2(15), 1200000 },
+	{ 0, {  1296000, HFPLL, 1, 0x30 }, L2(15), 1200000 },
 	{ 1, {  1350000, HFPLL, 1, 0x32 }, L2(15), 1200000 },
 	{ 1, {  1404000, HFPLL, 1, 0x34 }, L2(15), 1212500 },
 	{ 0, { 0 } }
@@ -167,23 +167,23 @@
 
 static struct acpu_level acpu_freq_tbl_fast[] __initdata = {
 	{ 1, {   384000, PLL_8, 0, 0x00 }, L2(0),   900000 },
-	{ 1, {   432000, HFPLL, 2, 0x20 }, L2(5),   900000 },
+	{ 0, {   432000, HFPLL, 2, 0x20 }, L2(5),   900000 },
 	{ 1, {   486000, HFPLL, 2, 0x24 }, L2(5),   900000 },
-	{ 1, {   540000, HFPLL, 2, 0x28 }, L2(5),   925000 },
+	{ 0, {   540000, HFPLL, 2, 0x28 }, L2(5),   925000 },
 	{ 1, {   594000, HFPLL, 1, 0x16 }, L2(5),   925000 },
-	{ 1, {   648000, HFPLL, 1, 0x18 }, L2(5),   950000 },
+	{ 0, {   648000, HFPLL, 1, 0x18 }, L2(5),   950000 },
 	{ 1, {   702000, HFPLL, 1, 0x1A }, L2(5),   950000 },
-	{ 1, {   756000, HFPLL, 1, 0x1C }, L2(10), 1000000 },
+	{ 0, {   756000, HFPLL, 1, 0x1C }, L2(10), 1000000 },
 	{ 1, {   810000, HFPLL, 1, 0x1E }, L2(10), 1000000 },
-	{ 1, {   864000, HFPLL, 1, 0x20 }, L2(10), 1025000 },
+	{ 0, {   864000, HFPLL, 1, 0x20 }, L2(10), 1025000 },
 	{ 1, {   918000, HFPLL, 1, 0x22 }, L2(10), 1025000 },
-	{ 1, {   972000, HFPLL, 1, 0x24 }, L2(10), 1050000 },
+	{ 0, {   972000, HFPLL, 1, 0x24 }, L2(10), 1050000 },
 	{ 1, {  1026000, HFPLL, 1, 0x26 }, L2(10), 1050000 },
-	{ 1, {  1080000, HFPLL, 1, 0x28 }, L2(15), 1100000 },
+	{ 0, {  1080000, HFPLL, 1, 0x28 }, L2(15), 1100000 },
 	{ 1, {  1134000, HFPLL, 1, 0x2A }, L2(15), 1100000 },
-	{ 1, {  1188000, HFPLL, 1, 0x2C }, L2(15), 1125000 },
+	{ 0, {  1188000, HFPLL, 1, 0x2C }, L2(15), 1125000 },
 	{ 1, {  1242000, HFPLL, 1, 0x2E }, L2(15), 1125000 },
-	{ 1, {  1296000, HFPLL, 1, 0x30 }, L2(15), 1150000 },
+	{ 0, {  1296000, HFPLL, 1, 0x30 }, L2(15), 1150000 },
 	{ 1, {  1350000, HFPLL, 1, 0x32 }, L2(15), 1150000 },
 	{ 1, {  1404000, HFPLL, 1, 0x34 }, L2(15), 1162500 },
 	{ 0, { 0 } }
diff --git a/arch/arm/mach-msm/acpuclock-8930ab.c b/arch/arm/mach-msm/acpuclock-8930ab.c
new file mode 100644
index 0000000..96029b4
--- /dev/null
+++ b/arch/arm/mach-msm/acpuclock-8930ab.c
@@ -0,0 +1,284 @@
+/*
+ * 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <mach/rpm-regulator.h>
+#include <mach/msm_bus_board.h>
+#include <mach/msm_bus.h>
+
+#include "acpuclock.h"
+#include "acpuclock-krait.h"
+
+/* Corner type vreg VDD values */
+#define LVL_NONE	RPM_VREG_CORNER_NONE
+#define LVL_LOW		RPM_VREG_CORNER_LOW
+#define LVL_NOM		RPM_VREG_CORNER_NOMINAL
+#define LVL_HIGH	RPM_VREG_CORNER_HIGH
+
+static struct hfpll_data hfpll_data __initdata = {
+	.mode_offset = 0x00,
+	.l_offset = 0x08,
+	.m_offset = 0x0C,
+	.n_offset = 0x10,
+	.config_offset = 0x04,
+	.config_val = 0x7845C665,
+	.has_droop_ctl = true,
+	.droop_offset = 0x14,
+	.droop_val = 0x0108C000,
+	.low_vdd_l_max = 37,
+	.nom_vdd_l_max = 74,
+	.vdd[HFPLL_VDD_NONE] = LVL_NONE,
+	.vdd[HFPLL_VDD_LOW]  = LVL_LOW,
+	.vdd[HFPLL_VDD_NOM]  = LVL_NOM,
+	.vdd[HFPLL_VDD_HIGH] = LVL_HIGH,
+};
+
+static struct scalable scalable_pm8917[] __initdata = {
+	[CPU0] = {
+		.hfpll_phys_base = 0x00903200,
+		.aux_clk_sel_phys = 0x02088014,
+		.aux_clk_sel = 3,
+		.sec_clk_sel = 2,
+		.l2cpmr_iaddr = 0x4501,
+		.vreg[VREG_CORE] = { "krait0", 1300000 },
+		.vreg[VREG_MEM]  = { "krait0_mem", 1150000 },
+		.vreg[VREG_DIG]  = { "krait0_dig", 1150000 },
+		.vreg[VREG_HFPLL_A] = { "krait0_s8", 2050000 },
+		.vreg[VREG_HFPLL_B] = { "krait0_l23", 1800000 },
+	},
+	[CPU1] = {
+		.hfpll_phys_base = 0x00903300,
+		.aux_clk_sel_phys = 0x02098014,
+		.aux_clk_sel = 3,
+		.sec_clk_sel = 2,
+		.l2cpmr_iaddr = 0x5501,
+		.vreg[VREG_CORE] = { "krait1", 1300000 },
+		.vreg[VREG_MEM]  = { "krait1_mem", 1150000 },
+		.vreg[VREG_DIG]  = { "krait1_dig", 1150000 },
+		.vreg[VREG_HFPLL_A] = { "krait1_s8", 2050000 },
+		.vreg[VREG_HFPLL_B] = { "krait1_l23", 1800000 },
+	},
+	[L2] = {
+		.hfpll_phys_base = 0x00903400,
+		.aux_clk_sel_phys = 0x02011028,
+		.aux_clk_sel = 3,
+		.sec_clk_sel = 2,
+		.l2cpmr_iaddr = 0x0500,
+		.vreg[VREG_HFPLL_A] = { "l2_s8", 2050000 },
+		.vreg[VREG_HFPLL_B] = { "l2_l23", 1800000 },
+	},
+};
+
+static struct scalable scalable[] __initdata = {
+	[CPU0] = {
+		.hfpll_phys_base = 0x00903200,
+		.aux_clk_sel_phys = 0x02088014,
+		.aux_clk_sel = 3,
+		.sec_clk_sel = 2,
+		.l2cpmr_iaddr = 0x4501,
+		.vreg[VREG_CORE] = { "krait0", 1300000 },
+		.vreg[VREG_MEM]  = { "krait0_mem", 1150000 },
+		.vreg[VREG_DIG]  = { "krait0_dig", 1150000 },
+		.vreg[VREG_HFPLL_A] = { "krait0_hfpll", 1800000 },
+	},
+	[CPU1] = {
+		.hfpll_phys_base = 0x00903300,
+		.aux_clk_sel_phys = 0x02098014,
+		.aux_clk_sel = 3,
+		.sec_clk_sel = 2,
+		.l2cpmr_iaddr = 0x5501,
+		.vreg[VREG_CORE] = { "krait1", 1300000 },
+		.vreg[VREG_MEM]  = { "krait1_mem", 1150000 },
+		.vreg[VREG_DIG]  = { "krait1_dig", 1150000 },
+		.vreg[VREG_HFPLL_A] = { "krait1_hfpll", 1800000 },
+	},
+	[L2] = {
+		.hfpll_phys_base = 0x00903400,
+		.aux_clk_sel_phys = 0x02011028,
+		.aux_clk_sel = 3,
+		.sec_clk_sel = 2,
+		.l2cpmr_iaddr = 0x0500,
+		.vreg[VREG_HFPLL_A] = { "l2_hfpll", 1800000 },
+	},
+};
+
+static struct msm_bus_paths bw_level_tbl[] __initdata = {
+	[0] =  BW_MBPS(640), /* At least  80 MHz on bus. */
+	[1] = BW_MBPS(1064), /* At least 133 MHz on bus. */
+	[2] = BW_MBPS(1600), /* At least 200 MHz on bus. */
+	[3] = BW_MBPS(2128), /* At least 266 MHz on bus. */
+	[4] = BW_MBPS(3200), /* At least 400 MHz on bus. */
+	[5] = BW_MBPS(4800), /* At least 600 MHz on bus. */
+};
+
+static struct msm_bus_scale_pdata bus_scale_data __initdata = {
+	.usecase = bw_level_tbl,
+	.num_usecases = ARRAY_SIZE(bw_level_tbl),
+	.active_only = 1,
+	.name = "acpuclk-8930ab",
+};
+
+/* TODO: Update new L2 freqs once they are available */
+static struct l2_level l2_freq_tbl[] __initdata = {
+	[0]  = { {  384000, PLL_8, 0, 0x00 },  LVL_NOM, 1050000, 1 },
+	[1]  = { {  432000, HFPLL, 2, 0x20 },  LVL_NOM, 1050000, 2 },
+	[2]  = { {  486000, HFPLL, 2, 0x24 },  LVL_NOM, 1050000, 2 },
+	[3]  = { {  540000, HFPLL, 2, 0x28 },  LVL_NOM, 1050000, 2 },
+	[4]  = { {  594000, HFPLL, 1, 0x16 },  LVL_NOM, 1050000, 2 },
+	[5]  = { {  648000, HFPLL, 1, 0x18 },  LVL_NOM, 1050000, 4 },
+	[6]  = { {  702000, HFPLL, 1, 0x1A },  LVL_NOM, 1050000, 4 },
+	[7]  = { {  756000, HFPLL, 1, 0x1C }, LVL_HIGH, 1150000, 4 },
+	[8]  = { {  810000, HFPLL, 1, 0x1E }, LVL_HIGH, 1150000, 4 },
+	[9]  = { {  864000, HFPLL, 1, 0x20 }, LVL_HIGH, 1150000, 4 },
+	[10] = { {  918000, HFPLL, 1, 0x22 }, LVL_HIGH, 1150000, 5 },
+	[11] = { {  972000, HFPLL, 1, 0x24 }, LVL_HIGH, 1150000, 5 },
+	[12] = { { 1026000, HFPLL, 1, 0x26 }, LVL_HIGH, 1150000, 5 },
+	[13] = { { 1080000, HFPLL, 1, 0x28 }, LVL_HIGH, 1150000, 5 },
+	[14] = { { 1134000, HFPLL, 1, 0x2A }, LVL_HIGH, 1150000, 5 },
+	[15] = { { 1188000, HFPLL, 1, 0x2C }, LVL_HIGH, 1150000, 5 },
+	{ }
+};
+
+static struct acpu_level acpu_freq_tbl_slow[] __initdata = {
+	{ 1, {   384000, PLL_8, 0, 0x00 }, L2(0),   950000 },
+	{ 0, {   432000, HFPLL, 2, 0x20 }, L2(5),   975000 },
+	{ 1, {   486000, HFPLL, 2, 0x24 }, L2(5),   975000 },
+	{ 0, {   540000, HFPLL, 2, 0x28 }, L2(5),  1000000 },
+	{ 1, {   594000, HFPLL, 1, 0x16 }, L2(5),  1000000 },
+	{ 0, {   648000, HFPLL, 1, 0x18 }, L2(5),  1025000 },
+	{ 1, {   702000, HFPLL, 1, 0x1A }, L2(5),  1025000 },
+	{ 0, {   756000, HFPLL, 1, 0x1C }, L2(10), 1075000 },
+	{ 1, {   810000, HFPLL, 1, 0x1E }, L2(10), 1075000 },
+	{ 0, {   864000, HFPLL, 1, 0x20 }, L2(10), 1100000 },
+	{ 1, {   918000, HFPLL, 1, 0x22 }, L2(10), 1100000 },
+	{ 0, {   972000, HFPLL, 1, 0x24 }, L2(10), 1125000 },
+	{ 1, {  1026000, HFPLL, 1, 0x26 }, L2(10), 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 },
+	{ 0, {  1512000, HFPLL, 1, 0x38 }, L2(15), 1250000 },
+	{ 1, {  1566000, HFPLL, 1, 0x3A }, L2(15), 1250000 },
+	{ 0, {  1620000, HFPLL, 1, 0x3C }, L2(15), 1262500 },
+	{ 1, {  1674000, HFPLL, 1, 0x3E }, L2(15), 1262500 },
+	{ 1, {  1728000, HFPLL, 1, 0x40 }, L2(15), 1287500 },
+	{ 0, { 0 } }
+};
+
+static struct acpu_level acpu_freq_tbl_nom[] __initdata = {
+	{ 1, {   384000, PLL_8, 0, 0x00 }, L2(0),   950000 },
+	{ 0, {   432000, HFPLL, 2, 0x20 }, L2(5),   975000 },
+	{ 1, {   486000, HFPLL, 2, 0x24 }, L2(5),   975000 },
+	{ 0, {   540000, HFPLL, 2, 0x28 }, L2(5),  1000000 },
+	{ 1, {   594000, HFPLL, 1, 0x16 }, L2(5),  1000000 },
+	{ 0, {   648000, HFPLL, 1, 0x18 }, L2(5),  1025000 },
+	{ 1, {   702000, HFPLL, 1, 0x1A }, L2(5),  1025000 },
+	{ 0, {   756000, HFPLL, 1, 0x1C }, L2(10), 1075000 },
+	{ 1, {   810000, HFPLL, 1, 0x1E }, L2(10), 1075000 },
+	{ 0, {   864000, HFPLL, 1, 0x20 }, L2(10), 1100000 },
+	{ 1, {   918000, HFPLL, 1, 0x22 }, L2(10), 1100000 },
+	{ 0, {   972000, HFPLL, 1, 0x24 }, L2(10), 1125000 },
+	{ 1, {  1026000, HFPLL, 1, 0x26 }, L2(10), 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 },
+	{ 0, {  1512000, HFPLL, 1, 0x38 }, L2(15), 1250000 },
+	{ 1, {  1566000, HFPLL, 1, 0x3A }, L2(15), 1250000 },
+	{ 0, {  1620000, HFPLL, 1, 0x3C }, L2(15), 1262500 },
+	{ 1, {  1674000, HFPLL, 1, 0x3E }, L2(15), 1262500 },
+	{ 1, {  1728000, HFPLL, 1, 0x40 }, L2(15), 1287500 },
+	{ 0, { 0 } }
+};
+
+static struct acpu_level acpu_freq_tbl_fast[] __initdata = {
+	{ 1, {   384000, PLL_8, 0, 0x00 }, L2(0),   950000 },
+	{ 0, {   432000, HFPLL, 2, 0x20 }, L2(5),   975000 },
+	{ 1, {   486000, HFPLL, 2, 0x24 }, L2(5),   975000 },
+	{ 0, {   540000, HFPLL, 2, 0x28 }, L2(5),  1000000 },
+	{ 1, {   594000, HFPLL, 1, 0x16 }, L2(5),  1000000 },
+	{ 0, {   648000, HFPLL, 1, 0x18 }, L2(5),  1025000 },
+	{ 1, {   702000, HFPLL, 1, 0x1A }, L2(5),  1025000 },
+	{ 0, {   756000, HFPLL, 1, 0x1C }, L2(10), 1075000 },
+	{ 1, {   810000, HFPLL, 1, 0x1E }, L2(10), 1075000 },
+	{ 0, {   864000, HFPLL, 1, 0x20 }, L2(10), 1100000 },
+	{ 1, {   918000, HFPLL, 1, 0x22 }, L2(10), 1100000 },
+	{ 0, {   972000, HFPLL, 1, 0x24 }, L2(10), 1125000 },
+	{ 1, {  1026000, HFPLL, 1, 0x26 }, L2(10), 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 },
+	{ 0, {  1512000, HFPLL, 1, 0x38 }, L2(15), 1250000 },
+	{ 1, {  1566000, HFPLL, 1, 0x3A }, L2(15), 1250000 },
+	{ 0, {  1620000, HFPLL, 1, 0x3C }, L2(15), 1262500 },
+	{ 1, {  1674000, HFPLL, 1, 0x3E }, L2(15), 1262500 },
+	{ 1, {  1728000, HFPLL, 1, 0x40 }, L2(15), 1287500 },
+	{ 0, { 0 } }
+};
+
+/* TODO: Update boost voltage once the pvs data is available */
+static struct pvs_table pvs_tables[NUM_SPEED_BINS][NUM_PVS] __initdata = {
+[0][PVS_SLOW]    = { acpu_freq_tbl_slow, sizeof(acpu_freq_tbl_slow),     0 },
+[0][PVS_NOMINAL] = { acpu_freq_tbl_nom,  sizeof(acpu_freq_tbl_nom),      0 },
+[0][PVS_FAST]    = { acpu_freq_tbl_fast, sizeof(acpu_freq_tbl_fast),     0 },
+};
+
+static struct acpuclk_krait_params acpuclk_8930ab_params __initdata = {
+	.scalable = scalable,
+	.scalable_size = sizeof(scalable),
+	.hfpll_data = &hfpll_data,
+	.pvs_tables = pvs_tables,
+	.l2_freq_tbl = l2_freq_tbl,
+	.l2_freq_tbl_size = sizeof(l2_freq_tbl),
+	.bus_scale = &bus_scale_data,
+	.pte_efuse_phys = 0x007000C0,
+	.stby_khz = 384000,
+};
+
+static int __init acpuclk_8930ab_probe(struct platform_device *pdev)
+{
+	struct acpuclk_platform_data *pdata = pdev->dev.platform_data;
+	if (pdata && pdata->uses_pm8917)
+		acpuclk_8930ab_params.scalable = scalable_pm8917;
+
+	return acpuclk_krait_init(&pdev->dev, &acpuclk_8930ab_params);
+}
+
+static struct platform_driver acpuclk_8930ab_driver = {
+	.driver = {
+		.name = "acpuclk-8930ab",
+		.owner = THIS_MODULE,
+	},
+};
+
+static int __init acpuclk_8930ab_init(void)
+{
+	return platform_driver_probe(&acpuclk_8930ab_driver,
+				     acpuclk_8930ab_probe);
+}
+device_initcall(acpuclk_8930ab_init);
diff --git a/arch/arm/mach-msm/acpuclock-8960ab.c b/arch/arm/mach-msm/acpuclock-8960ab.c
index ae1cd7b..03a2004 100644
--- a/arch/arm/mach-msm/acpuclock-8960ab.c
+++ b/arch/arm/mach-msm/acpuclock-8960ab.c
@@ -105,40 +105,140 @@
 	{ }
 };
 
-static struct acpu_level acpu_freq_tbl_slow[] __initdata = {
+static struct acpu_level freq_tbl_PVS0[] __initdata = {
 	{ 1, {   384000, PLL_8, 0, 0x00 }, L2(0),   950000 },
-	{ 0, {   432000, HFPLL, 2, 0x20 }, L2(3),   975000 },
-	{ 1, {   486000, HFPLL, 2, 0x24 }, L2(3),   975000 },
-	{ 0, {   540000, HFPLL, 2, 0x28 }, L2(3),  1000000 },
-	{ 1, {   594000, HFPLL, 1, 0x16 }, L2(3),  1000000 },
-	{ 0, {   648000, HFPLL, 1, 0x18 }, L2(3),  1025000 },
-	{ 1, {   702000, HFPLL, 1, 0x1A }, L2(3),  1025000 },
-	{ 0, {   756000, HFPLL, 1, 0x1C }, L2(3),  1075000 },
-	{ 1, {   810000, HFPLL, 1, 0x1E }, L2(3),  1075000 },
-	{ 0, {   864000, HFPLL, 1, 0x20 }, L2(3),  1100000 },
-	{ 1, {   918000, HFPLL, 1, 0x22 }, L2(3),  1100000 },
-	{ 0, {   972000, HFPLL, 1, 0x24 }, L2(3),  1125000 },
-	{ 1, {  1026000, HFPLL, 1, 0x26 }, L2(3),  1125000 },
-	{ 0, {  1080000, HFPLL, 1, 0x28 }, L2(9),  1175000 },
-	{ 1, {  1134000, HFPLL, 1, 0x2A }, L2(9),  1175000 },
-	{ 0, {  1188000, HFPLL, 1, 0x2C }, L2(9),  1200000 },
-	{ 1, {  1242000, HFPLL, 1, 0x2E }, L2(9),  1200000 },
-	{ 0, {  1296000, HFPLL, 1, 0x30 }, L2(9),  1225000 },
-	{ 1, {  1350000, HFPLL, 1, 0x32 }, L2(9),  1225000 },
-	{ 0, {  1404000, HFPLL, 1, 0x34 }, L2(9),  1237500 },
-	{ 1, {  1458000, HFPLL, 1, 0x36 }, L2(9),  1237500 },
-	{ 1, {  1512000, HFPLL, 1, 0x38 }, L2(9),  1250000 },
-	{ 1, {  1566000, HFPLL, 1, 0x3A }, L2(9),  1250000 },
-	{ 1, {  1620000, HFPLL, 1, 0x3C }, L2(9),  1250000 },
-	{ 1, {  1674000, HFPLL, 1, 0x3E }, L2(9),  1250000 },
+	{ 1, {   486000, HFPLL, 2, 0x24 }, L2(3),   950000 },
+	{ 1, {   594000, HFPLL, 1, 0x16 }, L2(3),   975000 },
+	{ 1, {   702000, HFPLL, 1, 0x1A }, L2(3),  1000000 },
+	{ 1, {   810000, HFPLL, 1, 0x1E }, L2(3),  1025000 },
+	{ 1, {   918000, HFPLL, 1, 0x22 }, L2(3),  1050000 },
+	{ 1, {  1026000, HFPLL, 1, 0x26 }, L2(3),  1075000 },
+	{ 1, {  1134000, HFPLL, 1, 0x2A }, L2(9),  1100000 },
+	{ 1, {  1242000, HFPLL, 1, 0x2E }, L2(9),  1125000 },
+	{ 1, {  1350000, HFPLL, 1, 0x32 }, L2(9),  1150000 },
+	{ 1, {  1458000, HFPLL, 1, 0x36 }, L2(9),  1175000 },
+	{ 1, {  1566000, HFPLL, 1, 0x3A }, L2(9),  1200000 },
+	{ 1, {  1674000, HFPLL, 1, 0x3E }, L2(9),  1225000 },
 	{ 1, {  1728000, HFPLL, 1, 0x40 }, L2(9),  1250000 },
 	{ 0, { 0 } }
 };
 
+static struct acpu_level freq_tbl_PVS1[] __initdata = {
+	{ 1, {   384000, PLL_8, 0, 0x00 }, L2(0),   925000 },
+	{ 1, {   486000, HFPLL, 2, 0x24 }, L2(3),   925000 },
+	{ 1, {   594000, HFPLL, 1, 0x16 }, L2(3),   950000 },
+	{ 1, {   702000, HFPLL, 1, 0x1A }, L2(3),   975000 },
+	{ 1, {   810000, HFPLL, 1, 0x1E }, L2(3),  1000000 },
+	{ 1, {   918000, HFPLL, 1, 0x22 }, L2(3),  1025000 },
+	{ 1, {  1026000, HFPLL, 1, 0x26 }, L2(3),  1050000 },
+	{ 1, {  1134000, HFPLL, 1, 0x2A }, L2(9),  1075000 },
+	{ 1, {  1242000, HFPLL, 1, 0x2E }, L2(9),  1100000 },
+	{ 1, {  1350000, HFPLL, 1, 0x32 }, L2(9),  1125000 },
+	{ 1, {  1458000, HFPLL, 1, 0x36 }, L2(9),  1150000 },
+	{ 1, {  1566000, HFPLL, 1, 0x3A }, L2(9),  1175000 },
+	{ 1, {  1674000, HFPLL, 1, 0x3E }, L2(9),  1200000 },
+	{ 1, {  1728000, HFPLL, 1, 0x40 }, L2(9),  1225000 },
+	{ 0, { 0 } }
+};
+
+static struct acpu_level freq_tbl_PVS2[] __initdata = {
+	{ 1, {   384000, PLL_8, 0, 0x00 }, L2(0),   900000 },
+	{ 1, {   486000, HFPLL, 2, 0x24 }, L2(3),   900000 },
+	{ 1, {   594000, HFPLL, 1, 0x16 }, L2(3),   925000 },
+	{ 1, {   702000, HFPLL, 1, 0x1A }, L2(3),   950000 },
+	{ 1, {   810000, HFPLL, 1, 0x1E }, L2(3),   975000 },
+	{ 1, {   918000, HFPLL, 1, 0x22 }, L2(3),  1000000 },
+	{ 1, {  1026000, HFPLL, 1, 0x26 }, L2(3),  1025000 },
+	{ 1, {  1134000, HFPLL, 1, 0x2A }, L2(9),  1050000 },
+	{ 1, {  1242000, HFPLL, 1, 0x2E }, L2(9),  1075000 },
+	{ 1, {  1350000, HFPLL, 1, 0x32 }, L2(9),  1100000 },
+	{ 1, {  1458000, HFPLL, 1, 0x36 }, L2(9),  1125000 },
+	{ 1, {  1566000, HFPLL, 1, 0x3A }, L2(9),  1150000 },
+	{ 1, {  1674000, HFPLL, 1, 0x3E }, L2(9),  1175000 },
+	{ 1, {  1728000, HFPLL, 1, 0x40 }, L2(9),  1200000 },
+	{ 0, { 0 } }
+};
+
+static struct acpu_level freq_tbl_PVS3[] __initdata = {
+	{ 1, {   384000, PLL_8, 0, 0x00 }, L2(0),   900000 },
+	{ 1, {   486000, HFPLL, 2, 0x24 }, L2(3),   900000 },
+	{ 1, {   594000, HFPLL, 1, 0x16 }, L2(3),   900000 },
+	{ 1, {   702000, HFPLL, 1, 0x1A }, L2(3),   925000 },
+	{ 1, {   810000, HFPLL, 1, 0x1E }, L2(3),   950000 },
+	{ 1, {   918000, HFPLL, 1, 0x22 }, L2(3),   975000 },
+	{ 1, {  1026000, HFPLL, 1, 0x26 }, L2(3),  1000000 },
+	{ 1, {  1134000, HFPLL, 1, 0x2A }, L2(9),  1025000 },
+	{ 1, {  1242000, HFPLL, 1, 0x2E }, L2(9),  1050000 },
+	{ 1, {  1350000, HFPLL, 1, 0x32 }, L2(9),  1075000 },
+	{ 1, {  1458000, HFPLL, 1, 0x36 }, L2(9),  1100000 },
+	{ 1, {  1566000, HFPLL, 1, 0x3A }, L2(9),  1125000 },
+	{ 1, {  1674000, HFPLL, 1, 0x3E }, L2(9),  1150000 },
+	{ 1, {  1728000, HFPLL, 1, 0x40 }, L2(9),  1175000 },
+	{ 0, { 0 } }
+};
+
+static struct acpu_level freq_tbl_PVS4[] __initdata = {
+	{ 1, {   384000, PLL_8, 0, 0x00 }, L2(0),   875000 },
+	{ 1, {   486000, HFPLL, 2, 0x24 }, L2(3),   875000 },
+	{ 1, {   594000, HFPLL, 1, 0x16 }, L2(3),   875000 },
+	{ 1, {   702000, HFPLL, 1, 0x1A }, L2(3),   900000 },
+	{ 1, {   810000, HFPLL, 1, 0x1E }, L2(3),   925000 },
+	{ 1, {   918000, HFPLL, 1, 0x22 }, L2(3),   950000 },
+	{ 1, {  1026000, HFPLL, 1, 0x26 }, L2(3),   975000 },
+	{ 1, {  1134000, HFPLL, 1, 0x2A }, L2(9),  1000000 },
+	{ 1, {  1242000, HFPLL, 1, 0x2E }, L2(9),  1025000 },
+	{ 1, {  1350000, HFPLL, 1, 0x32 }, L2(9),  1050000 },
+	{ 1, {  1458000, HFPLL, 1, 0x36 }, L2(9),  1075000 },
+	{ 1, {  1566000, HFPLL, 1, 0x3A }, L2(9),  1100000 },
+	{ 1, {  1674000, HFPLL, 1, 0x3E }, L2(9),  1125000 },
+	{ 1, {  1728000, HFPLL, 1, 0x40 }, L2(9),  1150000 },
+	{ 0, { 0 } }
+};
+
+static struct acpu_level freq_tbl_PVS5[] __initdata = {
+	{ 1, {   384000, PLL_8, 0, 0x00 }, L2(0),   875000 },
+	{ 1, {   486000, HFPLL, 2, 0x24 }, L2(3),   875000 },
+	{ 1, {   594000, HFPLL, 1, 0x16 }, L2(3),   875000 },
+	{ 1, {   702000, HFPLL, 1, 0x1A }, L2(3),   875000 },
+	{ 1, {   810000, HFPLL, 1, 0x1E }, L2(3),   900000 },
+	{ 1, {   918000, HFPLL, 1, 0x22 }, L2(3),   925000 },
+	{ 1, {  1026000, HFPLL, 1, 0x26 }, L2(3),   950000 },
+	{ 1, {  1134000, HFPLL, 1, 0x2A }, L2(9),   975000 },
+	{ 1, {  1242000, HFPLL, 1, 0x2E }, L2(9),  1000000 },
+	{ 1, {  1350000, HFPLL, 1, 0x32 }, L2(9),  1025000 },
+	{ 1, {  1458000, HFPLL, 1, 0x36 }, L2(9),  1050000 },
+	{ 1, {  1566000, HFPLL, 1, 0x3A }, L2(9),  1075000 },
+	{ 1, {  1674000, HFPLL, 1, 0x3E }, L2(9),  1100000 },
+	{ 1, {  1728000, HFPLL, 1, 0x40 }, L2(9),  1125000 },
+	{ 0, { 0 } }
+};
+
+static struct acpu_level freq_tbl_PVS6[] __initdata = {
+	{ 1, {   384000, PLL_8, 0, 0x00 }, L2(0),   850000 },
+	{ 1, {   486000, HFPLL, 2, 0x24 }, L2(3),   850000 },
+	{ 1, {   594000, HFPLL, 1, 0x16 }, L2(3),   850000 },
+	{ 1, {   702000, HFPLL, 1, 0x1A }, L2(3),   850000 },
+	{ 1, {   810000, HFPLL, 1, 0x1E }, L2(3),   875000 },
+	{ 1, {   918000, HFPLL, 1, 0x22 }, L2(3),   900000 },
+	{ 1, {  1026000, HFPLL, 1, 0x26 }, L2(3),   925000 },
+	{ 1, {  1134000, HFPLL, 1, 0x2A }, L2(9),   950000 },
+	{ 1, {  1242000, HFPLL, 1, 0x2E }, L2(9),   975000 },
+	{ 1, {  1350000, HFPLL, 1, 0x32 }, L2(9),  1000000 },
+	{ 1, {  1458000, HFPLL, 1, 0x36 }, L2(9),  1025000 },
+	{ 1, {  1566000, HFPLL, 1, 0x3A }, L2(9),  1050000 },
+	{ 1, {  1674000, HFPLL, 1, 0x3E }, L2(9),  1075000 },
+	{ 1, {  1728000, HFPLL, 1, 0x40 }, L2(9),  1100000 },
+	{ 0, { 0 } }
+};
+
 static struct pvs_table pvs_tables[NUM_SPEED_BINS][NUM_PVS] __initdata = {
-[0][PVS_SLOW]    = { acpu_freq_tbl_slow, sizeof(acpu_freq_tbl_slow),  0 },
-[0][PVS_NOMINAL] = { acpu_freq_tbl_slow, sizeof(acpu_freq_tbl_slow),  0 },
-[0][PVS_FAST]    = { acpu_freq_tbl_slow, sizeof(acpu_freq_tbl_slow),  0 },
+[0][0] = { freq_tbl_PVS0, sizeof(freq_tbl_PVS0),  0 },
+[0][1] = { freq_tbl_PVS1, sizeof(freq_tbl_PVS1),  0 },
+[0][2] = { freq_tbl_PVS2, sizeof(freq_tbl_PVS2),  0 },
+[0][3] = { freq_tbl_PVS3, sizeof(freq_tbl_PVS3),  0 },
+[0][4] = { freq_tbl_PVS4, sizeof(freq_tbl_PVS4),  0 },
+[0][5] = { freq_tbl_PVS5, sizeof(freq_tbl_PVS5),  0 },
+[0][6] = { freq_tbl_PVS6, sizeof(freq_tbl_PVS6),  0 },
 };
 
 static struct acpuclk_krait_params acpuclk_8960ab_params __initdata = {
diff --git a/arch/arm/mach-msm/acpuclock-8974.c b/arch/arm/mach-msm/acpuclock-8974.c
index 098f854..0fbd6dc 100644
--- a/arch/arm/mach-msm/acpuclock-8974.c
+++ b/arch/arm/mach-msm/acpuclock-8974.c
@@ -113,14 +113,14 @@
 };
 
 static struct l2_level l2_freq_tbl[] __initdata = {
-	[0]  = { {  300000, PLL_0, 0,   0 }, LVL_LOW,   950000, 0 },
-	[1]  = { {  384000, HFPLL, 2,  40 }, LVL_NOM,   950000, 1 },
-	[2]  = { {  460800, HFPLL, 2,  48 }, LVL_NOM,   950000, 1 },
-	[3]  = { {  537600, HFPLL, 1,  28 }, LVL_NOM,   950000, 2 },
-	[4]  = { {  576000, HFPLL, 1,  30 }, LVL_NOM,   950000, 2 },
-	[5]  = { {  652800, HFPLL, 1,  34 }, LVL_NOM,   950000, 2 },
-	[6]  = { {  729600, HFPLL, 1,  38 }, LVL_NOM,   950000, 2 },
-	[7]  = { {  806400, HFPLL, 1,  42 }, LVL_NOM,   950000, 2 },
+	[0]  = { {  300000, PLL_0, 0,   0 }, LVL_LOW,  1050000, 0 },
+	[1]  = { {  345600, HFPLL, 2,  36 }, LVL_NOM,  1050000, 1 },
+	[2]  = { {  422400, HFPLL, 2,  44 }, LVL_NOM,  1050000, 1 },
+	[3]  = { {  499200, HFPLL, 2,  52 }, LVL_NOM,  1050000, 2 },
+	[4]  = { {  576000, HFPLL, 1,  30 }, LVL_NOM,  1050000, 2 },
+	[5]  = { {  652800, HFPLL, 1,  34 }, LVL_NOM,  1050000, 2 },
+	[6]  = { {  729600, HFPLL, 1,  38 }, LVL_NOM,  1050000, 2 },
+	[7]  = { {  806400, HFPLL, 1,  42 }, LVL_NOM,  1050000, 2 },
 	[8]  = { {  883200, HFPLL, 1,  46 }, LVL_HIGH, 1050000, 2 },
 	[9]  = { {  960000, HFPLL, 1,  50 }, LVL_HIGH, 1050000, 2 },
 	[10] = { { 1036800, HFPLL, 1,  54 }, LVL_HIGH, 1050000, 3 },
@@ -143,30 +143,30 @@
 };
 
 static struct acpu_level acpu_freq_tbl[] __initdata = {
-	{ 1, {  300000, PLL_0, 0,   0 }, L2(0),   950000, 3200000 },
-	{ 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 },
-	{ 1, {  576000, HFPLL, 1,  30 }, L2(5),   950000, 3200000 },
-	{ 1, {  652800, HFPLL, 1,  34 }, L2(5),   950000, 3200000 },
-	{ 1, {  729600, HFPLL, 1,  38 }, L2(5),   950000, 3200000 },
-	{ 1, {  806400, HFPLL, 1,  42 }, L2(7),   950000, 3200000 },
-	{ 1, {  883200, HFPLL, 1,  46 }, L2(7),   950000, 3200000 },
-	{ 1, {  960000, HFPLL, 1,  50 }, L2(7),   950000, 3200000 },
-	{ 1, { 1036800, HFPLL, 1,  54 }, L2(7),   950000, 3200000 },
-	{ 1, { 1113600, HFPLL, 1,  58 }, L2(12), 1050000, 3200000 },
-	{ 1, { 1190400, HFPLL, 1,  62 }, L2(12), 1050000, 3200000 },
-	{ 1, { 1267200, HFPLL, 1,  66 }, L2(12), 1050000, 3200000 },
-	{ 1, { 1344000, HFPLL, 1,  70 }, L2(15), 1050000, 3200000 },
-	{ 1, { 1420800, HFPLL, 1,  74 }, L2(15), 1050000, 3200000 },
-	{ 1, { 1497600, HFPLL, 1,  78 }, L2(16), 1050000, 3200000 },
-	{ 0, { 1574400, HFPLL, 1,  82 }, L2(20), 1050000, 3200000 },
-	{ 0, { 1651200, HFPLL, 1,  86 }, L2(20), 1050000, 3200000 },
-	{ 0, { 1728000, HFPLL, 1,  90 }, L2(20), 1050000, 3200000 },
-	{ 0, { 1804800, HFPLL, 1,  94 }, L2(25), 1050000, 3200000 },
-	{ 0, { 1881600, HFPLL, 1,  98 }, L2(25), 1050000, 3200000 },
-	{ 0, { 1958400, HFPLL, 1, 102 }, L2(25), 1050000, 3200000 },
-	{ 0, { 1996800, HFPLL, 1, 104 }, L2(25), 1050000, 3200000 },
+	{ 1, {  300000, PLL_0, 0,   0 }, L2(0),   850000,  100000 },
+	{ 0, {  345600, HFPLL, 2,  36 }, L2(0),   850000, 3200000 },
+	{ 1, {  422400, HFPLL, 2,  44 }, L2(0),   850000, 3200000 },
+	{ 0, {  499200, HFPLL, 2,  52 }, L2(0),   850000, 3200000 },
+	{ 1, {  576000, HFPLL, 1,  30 }, L2(0),   850000, 3200000 },
+	{ 1, {  652800, HFPLL, 1,  34 }, L2(16),  850000, 3200000 },
+	{ 0, {  729600, HFPLL, 1,  38 }, L2(16),  850000, 3200000 },
+	{ 1, {  806400, HFPLL, 1,  42 }, L2(16),  850000, 3200000 },
+	{ 1, {  883200, HFPLL, 1,  46 }, L2(16),  870000, 3200000 },
+	{ 1, {  960000, HFPLL, 1,  50 }, L2(16),  880000, 3200000 },
+	{ 1, { 1036800, HFPLL, 1,  54 }, L2(16),  900000, 3200000 },
+	{ 1, { 1113600, HFPLL, 1,  58 }, L2(16),  915000, 3200000 },
+	{ 1, { 1190400, HFPLL, 1,  62 }, L2(16),  935000, 3200000 },
+	{ 1, { 1267200, HFPLL, 1,  66 }, L2(16),  950000, 3200000 },
+	{ 1, { 1344000, HFPLL, 1,  70 }, L2(16),  970000, 3200000 },
+	{ 1, { 1420800, HFPLL, 1,  74 }, L2(16),  985000, 3200000 },
+	{ 1, { 1497600, HFPLL, 1,  78 }, L2(16), 1000000, 3200000 },
+	{ 1, { 1574400, HFPLL, 1,  82 }, L2(16), 1015000, 3200000 },
+	{ 1, { 1651200, HFPLL, 1,  86 }, L2(16), 1030000, 3200000 },
+	{ 1, { 1728000, HFPLL, 1,  90 }, L2(16), 1050000, 3200000 },
+	{ 0, { 1804800, HFPLL, 1,  94 }, L2(16), 1050000, 3200000 },
+	{ 0, { 1881600, HFPLL, 1,  98 }, L2(16), 1050000, 3200000 },
+	{ 0, { 1958400, HFPLL, 1, 102 }, L2(16), 1050000, 3200000 },
+	{ 0, { 1996800, HFPLL, 1, 104 }, L2(16), 1050000, 3200000 },
 	{ 0, { 0 } }
 };
 
diff --git a/arch/arm/mach-msm/acpuclock-9625.c b/arch/arm/mach-msm/acpuclock-9625.c
new file mode 100644
index 0000000..b0556c3
--- /dev/null
+++ b/arch/arm/mach-msm/acpuclock-9625.c
@@ -0,0 +1,506 @@
+/*
+ * 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, rc;
+	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;
+
+	/* Initialize regulators */
+	rc = increase_vdd(acpu_freq_tbl[i].vdd_cpu, acpu_freq_tbl[i].vdd_mem);
+	if (rc)
+		goto err_vdd;
+
+	rc = regulator_enable(drv_data.vdd_mem);
+	if (rc) {
+		dev_err(&pdev->dev, "regulator_enable for a5_mem failed\n");
+		goto err_vdd;
+	}
+
+	rc = regulator_enable(drv_data.vdd_cpu);
+	if (rc) {
+		dev_err(&pdev->dev, "regulator_enable for a5_cpu failed\n");
+		goto err_vdd_cpu;
+	}
+
+	acpuclk_9625_set_rate(smp_processor_id(), max_cpu_khz, SETRATE_INIT);
+
+	acpuclk_register(&acpuclk_9625_data);
+	cpufreq_table_init();
+
+	return 0;
+
+err_vdd_cpu:
+	regulator_disable(drv_data.vdd_mem);
+err_vdd:
+	regulator_put(drv_data.vdd_mem);
+	regulator_put(drv_data.vdd_cpu);
+
+	for (i = 0; i < NUM_SRC; i++) {
+		if (!src_clocks[i].name)
+			continue;
+		clk_unprepare(src_clocks[i].clk);
+		clk_put(src_clocks[i].clk);
+	}
+	return rc;
+}
+
+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
index 0ab70b4..a29735e 100644
--- a/arch/arm/mach-msm/acpuclock-krait-debug.c
+++ b/arch/arm/mach-msm/acpuclock-krait-debug.c
@@ -35,7 +35,7 @@
 	bool set;
 	bool enable;
 };
-static int l2_acg_en_val[MAX_SCALABLES];
+static int l2_acg_en_val;
 static struct dentry *base_dir;
 static struct dentry *sc_dir[MAX_SCALABLES];
 
@@ -51,8 +51,8 @@
 			val &= ~BIT(0);
 		else
 			val |= BIT(0);
-		asm volatile ("mcr p15, 7, %[l2cpdr], c15, c0, 5\n\t"
-				: : [l2cpdr]"r" (val));
+		asm volatile ("mcr p15, 7, %[cpmr0], c15, c0, 5\n\t"
+				: : [cpmr0]"r" (val));
 	} else {
 		action->enable = !(val & BIT(0));
 	}
@@ -65,7 +65,7 @@
 
 	if (sc_id == L2) {
 		regval = get_l2_indirect_reg(drv->scalable[sc_id].l2cpmr_iaddr);
-		l2_acg_en_val[sc_id] = regval & (0x3 << 10);
+		l2_acg_en_val = regval & (0x3 << 10);
 		regval |= (0x3 << 10);
 		set_l2_indirect_reg(drv->scalable[sc_id].l2cpmr_iaddr, regval);
 	} else {
@@ -82,7 +82,7 @@
 	if (sc_id == L2) {
 		regval = get_l2_indirect_reg(drv->scalable[sc_id].l2cpmr_iaddr);
 		regval &= ~(0x3 << 10);
-		regval |= l2_acg_en_val[sc_id];
+		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 };
diff --git a/arch/arm/mach-msm/acpuclock-krait.c b/arch/arm/mach-msm/acpuclock-krait.c
index a386e78..10c4d6c 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
@@ -33,6 +33,7 @@
 #include <mach/rpm-regulator.h>
 #include <mach/rpm-regulator-smd.h>
 #include <mach/msm_bus.h>
+#include <mach/msm_dcvs.h>
 
 #include "acpuclock.h"
 #include "acpuclock-krait.h"
@@ -209,7 +210,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;
 
@@ -232,10 +234,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);
 	}
 
@@ -426,6 +428,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)
@@ -433,8 +476,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())
@@ -478,13 +522,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
@@ -493,15 +555,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);
 
@@ -663,6 +733,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:
@@ -874,6 +952,17 @@
 static void __init cpufreq_table_init(void) {}
 #endif
 
+static void __init dcvs_freq_init(void)
+{
+	int i;
+
+	for (i = 0; drv.acpu_freq_tbl[i].speed.khz != 0; i++)
+		if (drv.acpu_freq_tbl[i].use_for_scaling)
+			msm_dcvs_register_cpu_freq(
+				drv.acpu_freq_tbl[i].speed.khz,
+				drv.acpu_freq_tbl[i].vdd_core / 1000);
+}
+
 static int __cpuinit acpuclk_cpu_callback(struct notifier_block *nfb,
 					    unsigned long action, void *hcpu)
 {
@@ -888,7 +977,11 @@
 		/* Fall through. */
 	case CPU_UP_CANCELED:
 		acpuclk_krait_set_rate(cpu, hot_unplug_khz, SETRATE_HOTPLUG);
+
+		regulator_disable(sc->vreg[VREG_CORE].reg);
 		regulator_set_optimum_mode(sc->vreg[VREG_CORE].reg, 0);
+		regulator_set_voltage(sc->vreg[VREG_CORE].reg, 0,
+						sc->vreg[VREG_CORE].max_vdd);
 		break;
 	case CPU_UP_PREPARE:
 		if (!sc->initialized) {
@@ -899,10 +992,20 @@
 		}
 		if (WARN_ON(!prev_khz[cpu]))
 			return NOTIFY_BAD;
+
+		rc = regulator_set_voltage(sc->vreg[VREG_CORE].reg,
+					sc->vreg[VREG_CORE].cur_vdd,
+					sc->vreg[VREG_CORE].max_vdd);
+		if (rc < 0)
+			return NOTIFY_BAD;
 		rc = regulator_set_optimum_mode(sc->vreg[VREG_CORE].reg,
 						sc->vreg[VREG_CORE].cur_ua);
 		if (rc < 0)
 			return NOTIFY_BAD;
+		rc = regulator_enable(sc->vreg[VREG_CORE].reg);
+		if (rc < 0)
+			return NOTIFY_BAD;
+
 		acpuclk_krait_set_rate(cpu, prev_khz[cpu], SETRATE_HOTPLUG);
 		break;
 	default:
@@ -1079,6 +1182,7 @@
 	hw_init();
 
 	cpufreq_table_init();
+	dcvs_freq_init();
 	acpuclk_register(&acpuclk_krait_data);
 	register_hotcpu_notifier(&acpuclk_cpu_notifier);
 
diff --git a/arch/arm/mach-msm/acpuclock-krait.h b/arch/arm/mach-msm/acpuclock-krait.h
index 245f276..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, \
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 0a95e51..76d2844 100644
--- a/arch/arm/mach-msm/board-8064-camera.c
+++ b/arch/arm/mach-msm/board-8064-camera.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
@@ -187,8 +187,22 @@
 #define VFE_CAMIF_TIMER1_GPIO 3
 #define VFE_CAMIF_TIMER2_GPIO 1
 
+static struct gpio flash_init_gpio[] = {
+	{VFE_CAMIF_TIMER1_GPIO, GPIOF_OUT_INIT_LOW, "CAMIF_TIMER1"},
+	{VFE_CAMIF_TIMER2_GPIO, GPIOF_OUT_INIT_LOW, "CAMIF_TIMER2"},
+};
+
+static struct msm_gpio_set_tbl flash_set_gpio[] = {
+	{VFE_CAMIF_TIMER1_GPIO, GPIOF_OUT_INIT_HIGH, 2000},
+	{VFE_CAMIF_TIMER2_GPIO, GPIOF_OUT_INIT_HIGH, 2000},
+};
+
 static struct msm_camera_sensor_flash_src msm_flash_src = {
 	.flash_sr_type = MSM_CAMERA_FLASH_SRC_EXT,
+	.init_gpio_tbl = flash_init_gpio,
+	.init_gpio_tbl_size = ARRAY_SIZE(flash_init_gpio),
+	.set_gpio_tbl = flash_set_gpio,
+	.set_gpio_tbl_size = ARRAY_SIZE(flash_set_gpio),
 	._fsrc.ext_driver_src.led_en = VFE_CAMIF_TIMER1_GPIO,
 	._fsrc.ext_driver_src.led_flash_en = VFE_CAMIF_TIMER2_GPIO,
 	._fsrc.ext_driver_src.flash_id = MAM_CAMERA_EXT_LED_FLASH_SC628A,
@@ -531,9 +545,15 @@
 	.sensor_type = BAYER_SENSOR,
 };
 
+static struct i2c_board_info sc628a_flash_i2c_info = {
+	I2C_BOARD_INFO("sc628a", 0x6E),
+};
+
 static struct msm_camera_sensor_flash_data flash_imx074 = {
 	.flash_type	= MSM_CAMERA_FLASH_LED,
-	.flash_src	= &msm_flash_src
+	.flash_src	= &msm_flash_src,
+	.board_info     = &sc628a_flash_i2c_info,
+	.bus_id         = APQ_8064_GSBI4_QUP_I2C_BUS_ID,
 };
 
 static struct msm_camera_csi_lane_params imx074_csi_lane_params = {
@@ -740,9 +760,6 @@
 	.platform_data = &msm_camera_sensor_ov2720_data,
 	},
 	{
-	I2C_BOARD_INFO("sc628a", 0x6E),
-	},
-	{
 	I2C_BOARD_INFO("imx091", 0x34),
 	.platform_data = &msm_camera_sensor_imx091_data,
 	},
diff --git a/arch/arm/mach-msm/board-8064-gpiomux.c b/arch/arm/mach-msm/board-8064-gpiomux.c
index cb03d4b..cd3e636 100644
--- a/arch/arm/mach-msm/board-8064-gpiomux.c
+++ b/arch/arm/mach-msm/board-8064-gpiomux.c
@@ -479,6 +479,18 @@
 	.pull = GPIOMUX_PULL_NONE,
 };
 
+static struct gpiomux_setting gsbi5_uart_cfg = {
+	.func = GPIOMUX_FUNC_2,
+	.drv = GPIOMUX_DRV_8MA,
+	.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 +1526,48 @@
 	},
 };
 
+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,
+		},
+	},
+};
+
+static struct msm_gpiomux_config mpq8064_gsbi5_uart_configs[] __initdata = {
+	{
+		.gpio      = 51,        /* GSBI5 UART TX */
+		.settings = {
+			[GPIOMUX_SUSPENDED] = &gsbi5_uart_cfg,
+		},
+	},
+	{
+		.gpio      = 52,        /* GSBI5 UART RX */
+		.settings = {
+			[GPIOMUX_SUSPENDED] = &gsbi5_uart_cfg,
+		},
+	},
+};
+
 void __init apq8064_init_gpiomux(void)
 {
 	int rc;
@@ -1532,6 +1586,8 @@
 		 machine_is_mpq8064_dtv()) {
 		msm_gpiomux_install(mpq8064_gsbi5_i2c_configs,
 				ARRAY_SIZE(mpq8064_gsbi5_i2c_configs));
+		msm_gpiomux_install(mpq8064_gsbi5_uart_configs,
+				ARRAY_SIZE(mpq8064_gsbi5_uart_configs));
 #ifdef CONFIG_MSM_VCAP
 		msm_gpiomux_install(vcap_configs,
 				ARRAY_SIZE(vcap_configs));
@@ -1630,10 +1686,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 fad7092..38ac83e 100644
--- a/arch/arm/mach-msm/board-8064-gpu.c
+++ b/arch/arm/mach-msm/board-8064-gpu.c
@@ -182,6 +182,12 @@
 	{
 		.name = KGSL_3D0_REG_MEMORY,
 		.start = 0x04300000, /* GFX3D address */
+		.end = 0x0430ffff,
+		.flags = IORESOURCE_MEM,
+	},
+	{
+		.name = KGSL_3D0_SHADER_MEMORY,
+		.start = 0x04310000, /* Shader Mem Address */
 		.end = 0x0431ffff,
 		.flags = IORESOURCE_MEM,
 	},
@@ -226,7 +232,7 @@
 			.io_fraction = 0,
 		},
 		{
-			.gpu_freq = 325000000,
+			.gpu_freq = 320000000,
 			.bus_freq = 3,
 			.io_fraction = 33,
 		},
@@ -281,8 +287,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 e64a672..f6dd2ea 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 = {
@@ -394,7 +395,6 @@
 #define CHG_TERM_MA		100
 static struct pm8921_charger_platform_data
 apq8064_pm8921_chg_pdata __devinitdata = {
-	.safety_time		= 180,
 	.update_time		= 60000,
 	.max_voltage		= MAX_VOLTAGE_MV,
 	.min_voltage		= 3200,
@@ -405,7 +405,7 @@
 	.resume_charge_percent	= 99,
 	.term_current		= CHG_TERM_MA,
 	.cool_temp		= 10,
-	.warm_temp		= 40,
+	.warm_temp		= 45,
 	.temp_check_period	= 1,
 	.max_bat_chg_current	= 1100,
 	.cool_bat_chg_current	= 350,
@@ -419,20 +419,22 @@
 
 static struct pm8xxx_ccadc_platform_data
 apq8064_pm8xxx_ccadc_pdata = {
-	.r_sense		= 10,
+	.r_sense_uohm		= 10000,
 	.calib_delay_ms		= 600000,
 };
 
 static struct pm8921_bms_platform_data
 apq8064_pm8921_bms_pdata __devinitdata = {
 	.battery_type			= BATT_UNKNOWN,
-	.r_sense			= 10,
+	.r_sense_uohm			= 10000,
 	.v_cutoff			= 3400,
 	.max_voltage_uv			= MAX_VOLTAGE_MV * 1000,
 	.rconn_mohm			= 18,
 	.shutdown_soc_valid_limit	= 20,
 	.adjust_soc_low_threshold	= 25,
 	.chg_term_ua			= CHG_TERM_MA * 1000,
+	.normal_voltage_calc_ms		= 20000,
+	.low_voltage_calc_ms		= 1000,
 };
 
 static struct pm8921_platform_data
@@ -512,4 +514,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 851f7d9..a66495d 100644
--- a/arch/arm/mach-msm/board-8064-regulator.c
+++ b/arch/arm/mach-msm/board-8064-regulator.c
@@ -455,7 +455,8 @@
 	{ \
 		.constraints = { \
 			.name		= _name, \
-			.valid_ops_mask	= REGULATOR_CHANGE_VOLTAGE, \
+			.valid_ops_mask	= REGULATOR_CHANGE_VOLTAGE | \
+					  REGULATOR_CHANGE_STATUS, \
 			.min_uV		= _min_uV, \
 			.max_uV		= _max_uV, \
 		}, \
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 9e5e4f5..b3e682f 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>
@@ -329,9 +330,7 @@
  * to each other.
  * Don't swap the order unless you know what you are doing!
  */
-static struct ion_platform_data apq8064_ion_pdata = {
-	.nr = MSM_ION_HEAP_NUM,
-	.heaps = {
+struct ion_platform_heap apq8064_heaps[] = {
 		{
 			.id	= ION_SYSTEM_HEAP_ID,
 			.type	= ION_HEAP_TYPE_SYSTEM,
@@ -394,7 +393,11 @@
 			.extra_data = (void *) &co_apq8064_ion_pdata,
 		},
 #endif
-	}
+};
+
+static struct ion_platform_data apq8064_ion_pdata = {
+	.nr = MSM_ION_HEAP_NUM,
+	.heaps = apq8064_heaps,
 };
 
 static struct platform_device apq8064_ion_dev = {
@@ -692,12 +695,6 @@
 static struct msm_bus_vectors hsic_init_vectors[] = {
 	{
 		.src = MSM_BUS_MASTER_SPS,
-		.dst = MSM_BUS_SLAVE_EBI_CH0,
-		.ab = 0,
-		.ib = 0,
-	},
-	{
-		.src = MSM_BUS_MASTER_SPS,
 		.dst = MSM_BUS_SLAVE_SPS,
 		.ab = 0,
 		.ib = 0,
@@ -708,15 +705,9 @@
 static struct msm_bus_vectors hsic_max_vectors[] = {
 	{
 		.src = MSM_BUS_MASTER_SPS,
-		.dst = MSM_BUS_SLAVE_EBI_CH0,
-		.ab = 60000000,		/* At least 480Mbps on bus. */
-		.ib = 960000000,	/* MAX bursts rate */
-	},
-	{
-		.src = MSM_BUS_MASTER_SPS,
 		.dst = MSM_BUS_SLAVE_SPS,
 		.ab = 0,
-		.ib = 512000000, /*vote for 64Mhz dfab clk rate*/
+		.ib = 256000000, /*vote for 32Mhz dfab clk rate*/
 	},
 };
 
@@ -1256,29 +1247,20 @@
  * clock is running at 100KHz and voltage levels are at 3.3
  * and 5 volts
  */
-static int enable_100KHz_ls(int enable)
+static int enable_100KHz_ls(int enable, int gpio)
 {
-	int ret = 0;
-	if (enable) {
-		ret = gpio_request(SX150X_GPIO(1, 10),
-					"cs8427_100KHZ_ENABLE");
-		if (ret) {
-			pr_err("%s: Failed to request gpio %d\n", __func__,
-				SX150X_GPIO(1, 10));
-			return ret;
-		}
-		gpio_direction_output(SX150X_GPIO(1, 10), 1);
-	} else {
-		gpio_direction_output(SX150X_GPIO(1, 10), 0);
-		gpio_free(SX150X_GPIO(1, 10));
-	}
-	return ret;
+	if (enable)
+		gpio_direction_output(gpio, 1);
+	else
+		gpio_direction_output(gpio, 0);
+	return 0;
 }
 
 static struct cs8427_platform_data cs8427_i2c_platform_data = {
 	.irq = SX150X_GPIO(1, 4),
 	.reset_gpio = SX150X_GPIO(1, 6),
 	.enable = enable_100KHz_ls,
+	.ls_gpio = SX150X_GPIO(1, 10),
 };
 
 static struct i2c_board_info cs8427_device_info[] __initdata = {
@@ -1396,7 +1378,7 @@
 	/* T6 Object */
 	0, 0, 0, 0, 0, 0,
 	/* T38 Object */
-	14, 3, 0, 5, 7, 12, 0, 0, 0, 0,
+	14, 4, 0, 5, 11, 12, 0, 0, 0, 0,
 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
@@ -1404,7 +1386,7 @@
 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 	0, 0, 0, 0,
 	/* T7 Object */
-	32, 10, 50,
+	32, 8, 50,
 	/* T8 Object */
 	25, 0, 20, 20, 0, 0, 0, 0, 0, 0,
 	/* T9 Object */
@@ -2131,6 +2113,21 @@
 	0x24, 0x30, 0x0f,
 };
 
+/* 8064AB has a different command to assert apc_pdn */
+static uint8_t spm_power_collapse_without_rpm_krait_v3[] __initdata = {
+	0x00, 0x24, 0x84, 0x10,
+	0x09, 0x03, 0x01,
+	0x10, 0x84, 0x30, 0x0C,
+	0x24, 0x30, 0x0f,
+};
+
+static uint8_t spm_power_collapse_with_rpm_krait_v3[] __initdata = {
+	0x00, 0x24, 0x84, 0x10,
+	0x09, 0x07, 0x01, 0x0B,
+	0x10, 0x84, 0x30, 0x0C,
+	0x24, 0x30, 0x0f,
+};
+
 static struct msm_spm_seq_entry msm_spm_boot_cpu_seq_list[] __initdata = {
 	[0] = {
 		.mode = MSM_SPM_MODE_CLOCK_GATING,
@@ -2283,6 +2280,27 @@
 	},
 };
 
+static void __init apq8064ab_update_krait_spm(void)
+{
+	int i;
+
+	/* Update the SPM sequences for SPC and PC */
+	for (i = 0; i < ARRAY_SIZE(msm_spm_data); i++) {
+		int j;
+		struct msm_spm_platform_data *pdata = &msm_spm_data[i];
+		for (j = 0; j < pdata->num_modes; j++) {
+			if (pdata->modes[j].cmd ==
+					spm_power_collapse_without_rpm)
+				pdata->modes[j].cmd =
+				spm_power_collapse_without_rpm_krait_v3;
+			else if (pdata->modes[j].cmd ==
+					spm_power_collapse_with_rpm)
+				pdata->modes[j].cmd =
+				spm_power_collapse_with_rpm_krait_v3;
+		}
+	}
+}
+
 static void __init apq8064_init_buses(void)
 {
 	msm_bus_rpm_set_mt_mask();
@@ -2401,6 +2419,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 = {
@@ -2522,6 +2541,7 @@
 	&msm_pil_vidc,
 	&msm_gss,
 	&apq8064_rtb_device,
+	&apq8064_dcvs_device,
 	&apq8064_msm_gov_device,
 	&apq8064_device_cache_erp,
 	&msm8960_device_ebi1_ch0_erp,
@@ -2678,6 +2698,7 @@
 #endif
 
 static struct platform_device *mpq_devices[] __initdata = {
+	&mpq8064_device_uart_gsbi5,
 	&msm_device_sps_apq8064,
 	&mpq8064_device_qup_i2c_gsbi5,
 #ifdef CONFIG_MSM_ROTATOR
@@ -2693,12 +2714,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 = 10800000,
+};
+
+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 = {
@@ -2716,6 +2747,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,
 	},
 };
 
@@ -2753,10 +2795,21 @@
 
 #define GSBI_DUAL_MODE_CODE 0x60
 #define MSM_GSBI1_PHYS		0x12440000
+#define MSM_GSBI5_PHYS		0x1A200000
 static void __init apq8064_i2c_init(void)
 {
 	void __iomem *gsbi_mem;
-
+	if (machine_is_mpq8064_cdp() || machine_is_mpq8064_hrd() ||
+			machine_is_mpq8064_dtv()) {
+		gsbi_mem = ioremap_nocache(MSM_GSBI5_PHYS, 4);
+		writel_relaxed(GSBI_DUAL_MODE_CODE, gsbi_mem);
+		/* Ensure protocol code is written before proceeding */
+		wmb();
+		iounmap(gsbi_mem);
+		mpq8064_i2c_qup_gsbi5_pdata.use_gsbi_shared_mode = 1;
+		mpq8064_device_qup_i2c_gsbi5.dev.platform_data =
+					&mpq8064_i2c_qup_gsbi5_pdata;
+	}
 	apq8064_device_qup_i2c_gsbi1.dev.platform_data =
 					&apq8064_i2c_qup_gsbi1_pdata;
 	gsbi_mem = ioremap_nocache(MSM_GSBI1_PHYS, 4);
@@ -2945,6 +2998,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,
@@ -3279,6 +3409,7 @@
 static void __init apq8064_common_init(void)
 {
 	u32 platform_version = socinfo_get_platform_version();
+	struct msm_rpmrs_level rpmrs_level;
 
 	if (socinfo_get_pmic_model() == PMIC_MODEL_PM8917)
 		apq8064_pm8917_pdata_fixup();
@@ -3300,6 +3431,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 =
@@ -3346,8 +3486,12 @@
 	}
 
 	enable_ddr3_regulator();
-	msm_hsic_pdata.swfi_latency =
-		msm_rpmrs_levels[0].latency_us;
+	rpmrs_level =
+		msm_rpmrs_levels[MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT];
+	msm_hsic_pdata.swfi_latency = rpmrs_level.latency_us;
+	rpmrs_level =
+		msm_rpmrs_levels[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE];
+	msm_hsic_pdata.standalone_latency = rpmrs_level.latency_us;
 	if (machine_is_apq8064_mtp()) {
 		msm_hsic_pdata.log2_irq_thresh = 5,
 		apq8064_device_hsic_host.dev.platform_data = &msm_hsic_pdata;
@@ -3378,6 +3522,8 @@
 		apq8064_init_dsps();
 		platform_device_register(&msm_8960_riva);
 	}
+	if (cpu_is_apq8064ab())
+		apq8064ab_update_krait_spm();
 	msm_spm_init(msm_spm_data, ARRAY_SIZE(msm_spm_data));
 	msm_spm_l2_init(msm_spm_l2_data);
 	BUG_ON(msm_pm_boot_init(&msm_pm_boot_pdata));
@@ -3404,8 +3550,14 @@
 					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();
@@ -3446,7 +3598,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 bd1762d..3e31f68 100644
--- a/arch/arm/mach-msm/board-8092.c
+++ b/arch/arm/mach-msm/board-8092.c
@@ -18,7 +18,6 @@
 #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>
@@ -30,15 +29,9 @@
 #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, },
-	{ .compatible = "qcom,spmi-pmic-arb", .data = qpnpint_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),
@@ -81,26 +74,7 @@
 	msm_reserve();
 }
 
-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 void __init mpq8092_dt_init_irq(void)
-{
-	mpq8092_init_irq();
-}
-
-static void __init mpq8092_dt_map_io(void)
+static void __init mpq8092_map_io(void)
 {
 	msm_map_mpq8092_io();
 	if (socinfo_init() < 0)
@@ -113,21 +87,19 @@
 			"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);
 }
 
@@ -136,12 +108,12 @@
 	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,
diff --git a/arch/arm/mach-msm/board-8226.c b/arch/arm/mach-msm/board-8226.c
index b27382f..3e90489 100644
--- a/arch/arm/mach-msm/board-8226.c
+++ b/arch/arm/mach-msm/board-8226.c
@@ -28,12 +28,12 @@
 #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>
 #include <mach/gpiomux.h>
 #include <mach/msm_iomap.h>
+#include <mach/restart.h>
 #ifdef CONFIG_ION_MSM
 #include <mach/ion.h>
 #endif
@@ -41,58 +41,81 @@
 #include <mach/socinfo.h>
 #include <mach/board.h>
 #include <mach/clk-provider.h>
+#include "board-dt.h"
 #include "clock.h"
 
+static struct memtype_reserve msm8226_reserve_table[] __initdata = {
+	[MEMTYPE_SMI] = {
+	},
+	[MEMTYPE_EBI0] = {
+		.flags	=	MEMTYPE_FLAGS_1M_ALIGN,
+	},
+	[MEMTYPE_EBI1] = {
+		.flags	=	MEMTYPE_FLAGS_1M_ALIGN,
+	},
+};
+
+static int msm8226_paddr_to_memtype(unsigned int paddr)
+{
+	return MEMTYPE_EBI1;
+}
 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 = {
+static struct clock_init_data msm_dummy_clock_init_data __initdata = {
 	.table = msm_clocks_dummy,
 	.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-sdcc", 0xF9824000, \
+			"msm_sdcc.1", NULL),
+	OF_DEV_AUXDATA("qcom,msm-sdcc", 0xF98A4000, \
+			"msm_sdcc.2", 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
+static struct reserve_info msm8226_reserve_info __initdata = {
+	.memtype_reserve_table = msm8226_reserve_table,
+	.paddr_to_memtype = msm8226_paddr_to_memtype,
 };
 
-void __init msm8226_init_irq(void)
+static void __init msm8226_early_memory(void)
 {
-	of_irq_init(irq_match);
+	reserve_info = &msm8226_reserve_info;
+	of_scan_flat_dt(dt_scan_for_memory_reserve, msm8226_reserve_table);
 }
 
+static void __init msm8226_reserve(void)
+{
+	msm_reserve();
+}
+
+
 void __init msm8226_init(void)
 {
+	struct of_dev_auxdata *adata = msm8226_auxdata_lookup;
+
 	msm8226_init_gpiomux();
 
 	msm_clock_init(&msm_dummy_clock_init_data);
-}
-
-void __init msm8226_dt_init(void)
-{
-	msm8226_init();
 
 	if (socinfo_init() < 0)
 		pr_err("%s: socinfo_init() failed\n", __func__);
 
-	of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
+	of_platform_populate(NULL, of_default_bus_match_table, adata, NULL);
 }
 
-
 static const char *msm8226_dt_match[] __initconst = {
 	"qcom,msm8226",
 	NULL
@@ -100,9 +123,12 @@
 
 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,
+	.reserve = msm8226_reserve,
+	.init_very_early = msm8226_early_memory,
+	.restart = msm_restart,
 MACHINE_END
diff --git a/arch/arm/mach-msm/board-8910-gpiomux.c b/arch/arm/mach-msm/board-8910-gpiomux.c
new file mode 100644
index 0000000..a67f916
--- /dev/null
+++ b/arch/arm/mach-msm/board-8910-gpiomux.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.
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <mach/board.h>
+#include <mach/gpio.h>
+#include <mach/gpiomux.h>
+
+void __init msm8910_init_gpiomux(void)
+{
+	int rc;
+
+	rc = msm_gpiomux_init(NR_GPIO_IRQS);
+	if (rc) {
+		pr_err(KERN_ERR "msm_8910_init_gpiomux failed %d\n", rc);
+		return;
+	}
+}
diff --git a/arch/arm/mach-msm/board-8910.c b/arch/arm/mach-msm/board-8910.c
index 1c92494f..b4a95ee 100644
--- a/arch/arm/mach-msm/board-8910.c
+++ b/arch/arm/mach-msm/board-8910.c
@@ -31,54 +31,87 @@
 #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/msm_memtypes.h>
 #include <mach/socinfo.h>
 #include <mach/board.h>
 #include <mach/clk-provider.h>
+#include "board-dt.h"
 #include "clock.h"
 
+static struct memtype_reserve msm8910_reserve_table[] __initdata = {
+	[MEMTYPE_SMI] = {
+	},
+	[MEMTYPE_EBI0] = {
+		.flags	=	MEMTYPE_FLAGS_1M_ALIGN,
+	},
+	[MEMTYPE_EBI1] = {
+		.flags	=	MEMTYPE_FLAGS_1M_ALIGN,
+	},
+};
+
+static int msm8910_paddr_to_memtype(unsigned int paddr)
+{
+	return MEMTYPE_EBI1;
+}
+
 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),
+	CLK_DUMMY("dfab_clk",	DFAB_CLK,	"msm_sps", OFF),
 };
 
-struct clock_init_data msm_dummy_clock_init_data __initdata = {
+static struct clock_init_data msm_dummy_clock_init_data __initdata = {
 	.table = msm_clocks_dummy,
 	.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 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),
+	{}
 };
 
-static void __init msm8910_dt_timer_init(void)
+static struct reserve_info msm8910_reserve_info __initdata = {
+	.memtype_reserve_table = msm8910_reserve_table,
+	.paddr_to_memtype = msm8910_paddr_to_memtype,
+};
+
+static void __init msm8910_early_memory(void)
 {
-	arch_timer_of_register();
+	reserve_info = &msm8910_reserve_info;
+	of_scan_flat_dt(dt_scan_for_memory_reserve, msm8910_reserve_table);
 }
 
-static struct sys_timer msm8910_dt_timer = {
-	.init = msm8910_dt_timer_init
-};
-
-void __init msm8910_init_irq(void)
+static void __init msm8910_reserve(void)
 {
-	of_irq_init(irq_match);
+	msm_reserve();
 }
 
 void __init msm8910_init(void)
 {
+	struct of_dev_auxdata *adata = msm8910_auxdata_lookup;
+
+	msm8910_init_gpiomux();
 	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, NULL, NULL);
+	of_platform_populate(NULL, of_default_bus_match_table, adata, NULL);
 }
 
 static const char *msm8910_dt_match[] __initconst = {
@@ -88,9 +121,12 @@
 
 DT_MACHINE_START(MSM8910_DT, "Qualcomm MSM 8910 (Flattened Device Tree)")
 	.map_io = msm_map_msm8910_io,
-	.init_irq = msm8910_init_irq,
+	.init_irq = msm_dt_init_irq_nompm,
 	.init_machine = msm8910_init,
 	.handle_irq = gic_handle_irq,
-	.timer = &msm8910_dt_timer,
+	.timer = &msm_dt_timer,
 	.dt_compat = msm8910_dt_match,
+	.restart = msm_restart,
+	.reserve = msm8910_reserve,
+	.init_very_early = msm8910_early_memory
 MACHINE_END
diff --git a/arch/arm/mach-msm/board-8930-camera.c b/arch/arm/mach-msm/board-8930-camera.c
index eb00f34..be55031 100644
--- a/arch/arm/mach-msm/board-8930-camera.c
+++ b/arch/arm/mach-msm/board-8930-camera.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
@@ -188,6 +188,17 @@
 #define VFE_CAMIF_TIMER1_GPIO 2
 #define VFE_CAMIF_TIMER2_GPIO 3
 #define VFE_CAMIF_TIMER3_GPIO_INT 4
+
+static struct gpio flash_init_gpio[] = {
+	{VFE_CAMIF_TIMER1_GPIO, GPIOF_OUT_INIT_LOW, "CAMIF_TIMER1"},
+	{VFE_CAMIF_TIMER2_GPIO, GPIOF_OUT_INIT_LOW, "CAMIF_TIMER2"},
+};
+
+static struct msm_gpio_set_tbl flash_set_gpio[] = {
+	{VFE_CAMIF_TIMER1_GPIO, GPIOF_OUT_INIT_HIGH, 2000},
+	{VFE_CAMIF_TIMER2_GPIO, GPIOF_OUT_INIT_HIGH, 2000},
+};
+
 static struct msm_camera_sensor_strobe_flash_data strobe_flash_xenon = {
 	.flash_trigger = VFE_CAMIF_TIMER2_GPIO,
 	.flash_charge = VFE_CAMIF_TIMER1_GPIO,
@@ -196,14 +207,16 @@
 	.irq = MSM_GPIO_TO_INT(VFE_CAMIF_TIMER3_GPIO_INT),
 };
 
-#ifdef CONFIG_MSM_CAMERA_FLASH
 static struct msm_camera_sensor_flash_src msm_flash_src = {
 	.flash_sr_type = MSM_CAMERA_FLASH_SRC_EXT,
+	.init_gpio_tbl = flash_init_gpio,
+	.init_gpio_tbl_size = ARRAY_SIZE(flash_init_gpio),
+	.set_gpio_tbl = flash_set_gpio,
+	.set_gpio_tbl_size = ARRAY_SIZE(flash_set_gpio),
 	._fsrc.ext_driver_src.led_en = VFE_CAMIF_TIMER1_GPIO,
 	._fsrc.ext_driver_src.led_flash_en = VFE_CAMIF_TIMER2_GPIO,
 	._fsrc.ext_driver_src.flash_id = MAM_CAMERA_EXT_LED_FLASH_TPS61310,
 };
-#endif
 
 static struct msm_bus_vectors cam_init_vectors[] = {
 	{
@@ -231,7 +244,7 @@
 		.src = MSM_BUS_MASTER_VFE,
 		.dst = MSM_BUS_SLAVE_EBI_CH0,
 		.ab  = 27648000,
-		.ib  = 110592000,
+		.ib  = 2656000000UL,
 	},
 	{
 		.src = MSM_BUS_MASTER_VPE,
@@ -252,7 +265,7 @@
 		.src = MSM_BUS_MASTER_VFE,
 		.dst = MSM_BUS_SLAVE_EBI_CH0,
 		.ab  = 274406400,
-		.ib  = 561807360,
+		.ib  = 2656000000UL,
 	},
 	{
 		.src = MSM_BUS_MASTER_VPE,
@@ -272,8 +285,8 @@
 	{
 		.src = MSM_BUS_MASTER_VFE,
 		.dst = MSM_BUS_SLAVE_EBI_CH0,
-		.ab  = 274423680,
-		.ib  = 1097694720,
+		.ab  = 600000000,
+		.ib  = 2656000000UL,
 	},
 	{
 		.src = MSM_BUS_MASTER_VPE,
@@ -293,8 +306,8 @@
 	{
 		.src = MSM_BUS_MASTER_VFE,
 		.dst = MSM_BUS_SLAVE_EBI_CH0,
-		.ab  = 302071680,
-		.ib  = 1208286720,
+		.ab  = 600000000,
+		.ib  = 2656000000UL,
 	},
 	{
 		.src = MSM_BUS_MASTER_VPE,
@@ -314,8 +327,8 @@
 	{
 		.src = MSM_BUS_MASTER_VFE,
 		.dst = MSM_BUS_SLAVE_EBI_CH0,
-		.ab  = 348192000,
-		.ib  = 617103360,
+		.ab  = 600000000,
+		.ib  = 4264000000UL,
 	},
 	{
 		.src = MSM_BUS_MASTER_VPE,
@@ -336,7 +349,7 @@
 		.src = MSM_BUS_MASTER_VFE,
 		.dst = MSM_BUS_SLAVE_EBI_CH0,
 		.ab  = 302071680,
-		.ib  = 1208286720,
+		.ib  = 2656000000UL,
 	},
 	{
 		.src = MSM_BUS_MASTER_VPE,
@@ -352,6 +365,27 @@
 	},
 };
 
+static struct msm_bus_vectors cam_adv_video_vectors[] = {
+	{
+		.src = MSM_BUS_MASTER_VFE,
+		.dst = MSM_BUS_SLAVE_EBI_CH0,
+		.ab  = 274406400,
+		.ib  = 2656000000UL,
+	},
+	{
+		.src = MSM_BUS_MASTER_VPE,
+		.dst = MSM_BUS_SLAVE_EBI_CH0,
+		.ab  = 206807040,
+		.ib  = 488816640,
+	},
+	{
+		.src = MSM_BUS_MASTER_JPEG_ENC,
+		.dst = MSM_BUS_SLAVE_EBI_CH0,
+		.ab  = 0,
+		.ib  = 0,
+	},
+};
+
 
 static struct msm_bus_paths cam_bus_client_config[] = {
 	{
@@ -382,6 +416,11 @@
 		ARRAY_SIZE(cam_dual_vectors),
 		cam_dual_vectors,
 	},
+	{
+		ARRAY_SIZE(cam_adv_video_vectors),
+		cam_adv_video_vectors,
+	},
+
 };
 
 static struct msm_bus_scale_pdata cam_bus_client_pdata = {
@@ -476,9 +515,7 @@
 
 static struct msm_camera_sensor_flash_data flash_imx074 = {
 	.flash_type	= MSM_CAMERA_FLASH_LED,
-#ifdef CONFIG_MSM_CAMERA_FLASH
 	.flash_src	= &msm_flash_src
-#endif
 };
 
 static struct msm_camera_csi_lane_params imx074_csi_lane_params = {
@@ -560,9 +597,15 @@
 	.sensor_type = BAYER_SENSOR,
 };
 
+static struct i2c_board_info tps61310_flash_i2c_info = {
+	I2C_BOARD_INFO("tps61310", 0x66),
+};
+
 static struct msm_camera_sensor_flash_data flash_s5k3l1yx = {
 	.flash_type = MSM_CAMERA_FLASH_LED,
-	.flash_src = &msm_flash_src
+	.flash_src = &msm_flash_src,
+	.board_info = &tps61310_flash_i2c_info,
+	.bus_id = MSM_8930_GSBI4_QUP_I2C_BUS_ID,
 };
 
 static struct msm_camera_csi_lane_params s5k3l1yx_csi_lane_params = {
@@ -650,9 +693,6 @@
 	I2C_BOARD_INFO("s5k3l1yx", 0x20),
 	.platform_data = &msm_camera_sensor_s5k3l1yx_data,
 	},
-	{
-	I2C_BOARD_INFO("tps61310", 0x66),
-	},
 };
 
 struct msm_camera_board_info msm8930_camera_board_info = {
diff --git a/arch/arm/mach-msm/board-8930-display.c b/arch/arm/mach-msm/board-8930-display.c
index a0bfabf..4506ea7 100644
--- a/arch/arm/mach-msm/board-8930-display.c
+++ b/arch/arm/mach-msm/board-8930-display.c
@@ -132,6 +132,15 @@
 };
 
 static bool dsi_power_on;
+static struct mipi_dsi_panel_platform_data novatek_pdata;
+static void pm8917_gpio_set_backlight(int bl_level)
+{
+	int gpio24 = PM8917_GPIO_PM_TO_SYS(24);
+	if (bl_level > 0)
+		gpio_set_value_cansleep(gpio24, 1);
+	else
+		gpio_set_value_cansleep(gpio24, 0);
+}
 
 /*
  * TODO: When physical 8930/PM8038 hardware becomes
@@ -214,9 +223,13 @@
 					rc);
 				return -ENODEV;
 			}
+			gpio_set_value_cansleep(gpio24, 0);
+			novatek_pdata.gpio_set_backlight =
+				pm8917_gpio_set_backlight;
 		}
 		dsi_power_on = true;
 	}
+
 	if (on) {
 		rc = regulator_set_optimum_mode(reg_l8, 100000);
 		if (rc < 0) {
@@ -256,8 +269,6 @@
 		gpio_set_value(DISP_RST_GPIO, 1);
 		gpio_set_value(DISP_3D_2D_MODE, 1);
 		usleep(20);
-		if (socinfo_get_pmic_model() == PMIC_MODEL_PM8917)
-			gpio_set_value_cansleep(gpio24, 1);
 	} else {
 
 		gpio_set_value(DISP_RST_GPIO, 0);
@@ -294,8 +305,6 @@
 		}
 		gpio_set_value(DISP_3D_2D_MODE, 0);
 		usleep(20);
-		if (socinfo_get_pmic_model() == PMIC_MODEL_PM8917)
-			gpio_set_value_cansleep(gpio24, 0);
 	}
 	return 0;
 }
@@ -441,7 +450,7 @@
 #ifdef CONFIG_MSM_BUS_SCALING
 	.mdp_bus_scale_table = &mdp_bus_scale_pdata,
 #endif
-	.mdp_rev = MDP_REV_42,
+	.mdp_rev = MDP_REV_43,
 #ifdef CONFIG_MSM_MULTIMEDIA_USE_ION
 	.mem_hid = BIT(ION_CP_MM_HEAP_ID),
 #else
@@ -740,11 +749,29 @@
 		return 0;
 
 	if (on) {
+		if (!(hdmi_msm_data.is_mhl_enabled)) {
+			rc = gpio_request(HDMI_MHL_MUX_GPIO, "MHL_HDMI_MUX");
+			if (rc < 0) {
+				pr_err("gpio hdmi_mhl mux req failed:%d\n",
+					rc);
+				return rc;
+			}
+			rc = gpio_direction_output(HDMI_MHL_MUX_GPIO, 1);
+			if (rc < 0) {
+				pr_err("set gpio hdmi_mhl dir failed:%d\n",
+					rc);
+				goto error0;
+			}
+			gpio_set_value(HDMI_MHL_MUX_GPIO, 1);
+			pr_debug("set gpio hdmi mhl mux %d to 1\n",
+				HDMI_MHL_MUX_GPIO);
+		}
+
 		rc = gpio_request(100, "HDMI_DDC_CLK");
 		if (rc) {
 			pr_err("'%s'(%d) gpio_request failed, rc=%d\n",
 				"HDMI_DDC_CLK", 100, rc);
-			return rc;
+			goto error0;
 		}
 		rc = gpio_request(101, "HDMI_DDC_DATA");
 		if (rc) {
@@ -760,6 +787,8 @@
 		}
 		pr_debug("%s(on): success\n", __func__);
 	} else {
+		if (!(hdmi_msm_data.is_mhl_enabled))
+			gpio_free(HDMI_MHL_MUX_GPIO);
 		gpio_free(100);
 		gpio_free(101);
 		gpio_free(102);
@@ -773,6 +802,9 @@
 	gpio_free(101);
 error1:
 	gpio_free(100);
+error0:
+	if (!(hdmi_msm_data.is_mhl_enabled))
+		gpio_free(HDMI_MHL_MUX_GPIO);
 	return rc;
 }
 
diff --git a/arch/arm/mach-msm/board-8930-gpu.c b/arch/arm/mach-msm/board-8930-gpu.c
index 99a5a34..3eb7d8a 100644
--- a/arch/arm/mach-msm/board-8930-gpu.c
+++ b/arch/arm/mach-msm/board-8930-gpu.c
@@ -88,6 +88,12 @@
 	{
 		.name = KGSL_3D0_REG_MEMORY,
 		.start = 0x04300000, /* GFX3D address */
+		.end = 0x0430ffff,
+		.flags = IORESOURCE_MEM,
+	},
+	{
+		.name = KGSL_3D0_SHADER_MEMORY,
+		.start = 0x04310000,
 		.end = 0x0431ffff,
 		.flags = IORESOURCE_MEM,
 	},
@@ -163,10 +169,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 8687b2a..a5fded4 100644
--- a/arch/arm/mach-msm/board-8930-pmic.c
+++ b/arch/arm/mach-msm/board-8930-pmic.c
@@ -315,7 +315,6 @@
 #define MAX_VOLTAGE_MV		4200
 #define CHG_TERM_MA		100
 static struct pm8921_charger_platform_data pm8921_chg_pdata __devinitdata = {
-	.safety_time		= 180,
 	.update_time		= 60000,
 	.max_voltage		= MAX_VOLTAGE_MV,
 	.min_voltage		= 3200,
@@ -326,7 +325,7 @@
 	.resume_charge_percent	= 99,
 	.term_current		= CHG_TERM_MA,
 	.cool_temp		= 10,
-	.warm_temp		= 40,
+	.warm_temp		= 45,
 	.temp_check_period	= 1,
 	.max_bat_chg_current	= 1100,
 	.cool_bat_chg_current	= 350,
@@ -438,7 +437,7 @@
 };
 
 static struct pm8xxx_ccadc_platform_data pm8xxx_ccadc_pdata = {
-	.r_sense		= 10,
+	.r_sense_uohm		= 10000,
 	.calib_delay_ms		= 600000,
 };
 
@@ -466,13 +465,15 @@
 
 static struct pm8921_bms_platform_data pm8921_bms_pdata __devinitdata = {
 	.battery_type			= BATT_UNKNOWN,
-	.r_sense			= 10,
+	.r_sense_uohm			= 10000,
 	.v_cutoff			= 3400,
 	.max_voltage_uv			= MAX_VOLTAGE_MV * 1000,
 	.shutdown_soc_valid_limit	= 20,
 	.adjust_soc_low_threshold	= 25,
 	.chg_term_ua			= CHG_TERM_MA * 1000,
 	.rconn_mohm			= 18,
+	.normal_voltage_calc_ms		= 20000,
+	.low_voltage_calc_ms		= 1000,
 };
 
 static struct pm8038_platform_data pm8038_platform_data __devinitdata = {
@@ -596,4 +597,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 16a82b4..eaebea0 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),
@@ -187,12 +189,14 @@
 	REGULATOR_SUPPLY("krait0",		"acpuclk-8627"),
 	REGULATOR_SUPPLY("krait0",		"acpuclk-8930"),
 	REGULATOR_SUPPLY("krait0",		"acpuclk-8930aa"),
+	REGULATOR_SUPPLY("krait0",		"acpuclk-8930ab"),
 };
 VREG_CONSUMERS(S6) = {
 	REGULATOR_SUPPLY("8038_s6",		NULL),
 	REGULATOR_SUPPLY("krait1",		"acpuclk-8627"),
 	REGULATOR_SUPPLY("krait1",		"acpuclk-8930"),
 	REGULATOR_SUPPLY("krait1",		"acpuclk-8930aa"),
+	REGULATOR_SUPPLY("krait1",		"acpuclk-8930ab"),
 };
 VREG_CONSUMERS(LVS1) = {
 	REGULATOR_SUPPLY("8038_lvs1",		NULL),
@@ -445,7 +449,8 @@
 	{ \
 		.constraints = { \
 			.name		= _name, \
-			.valid_ops_mask	= REGULATOR_CHANGE_VOLTAGE, \
+			.valid_ops_mask	= REGULATOR_CHANGE_VOLTAGE | \
+					  REGULATOR_CHANGE_STATUS, \
 			.min_uV		= _min_uV, \
 			.max_uV		= _max_uV, \
 		}, \
@@ -488,7 +493,7 @@
 	/*	ID a_on pd ss min_uV   max_uV  supply sys_uA  freq  fm  ss_fm */
 	RPM_SMPS(S1, 0, 1, 1,  500000, 1150000, NULL, 100000, 4p80, AUTO, LPM),
 	RPM_SMPS(S2, 1, 1, 1, 1400000, 1400000, NULL, 100000, 1p60, AUTO, LPM),
-	RPM_SMPS(S3, 0, 1, 1, 1150000, 1150000, NULL, 100000, 3p20, AUTO, LPM),
+	RPM_SMPS(S3, 0, 1, 1, 1150000, 1150000, NULL, 100000, 3p20, AUTO, AUTO),
 	RPM_SMPS(S4, 1, 1, 1, 1950000, 2200000, NULL, 100000, 1p60, AUTO, LPM),
 
 	/*	ID     a_on pd ss min_uV   max_uV  supply  sys_uA init_ip */
@@ -562,6 +567,14 @@
 	RPM_REG_MAP(L24,            0, 2, "krait1_mem",   "acpuclk-8930aa"),
 	RPM_REG_MAP(VDD_DIG_CORNER, 0, 1, "krait0_dig",   "acpuclk-8930aa"),
 	RPM_REG_MAP(VDD_DIG_CORNER, 0, 2, "krait1_dig",   "acpuclk-8930aa"),
+
+	RPM_REG_MAP(L23,            0, 1, "krait0_hfpll", "acpuclk-8930ab"),
+	RPM_REG_MAP(L23,            0, 2, "krait1_hfpll", "acpuclk-8930ab"),
+	RPM_REG_MAP(L23,            0, 6, "l2_hfpll",     "acpuclk-8930ab"),
+	RPM_REG_MAP(L24,            0, 1, "krait0_mem",   "acpuclk-8930ab"),
+	RPM_REG_MAP(L24,            0, 2, "krait1_mem",   "acpuclk-8930ab"),
+	RPM_REG_MAP(VDD_DIG_CORNER, 0, 1, "krait0_dig",   "acpuclk-8930ab"),
+	RPM_REG_MAP(VDD_DIG_CORNER, 0, 2, "krait1_dig",   "acpuclk-8930ab"),
 };
 
 struct rpm_regulator_platform_data
diff --git a/arch/arm/mach-msm/board-8930-regulator-pm8917.c b/arch/arm/mach-msm/board-8930-regulator-pm8917.c
index 8898b50..9a2967a 100644
--- a/arch/arm/mach-msm/board-8930-regulator-pm8917.c
+++ b/arch/arm/mach-msm/board-8930-regulator-pm8917.c
@@ -34,6 +34,7 @@
 	REGULATOR_SUPPLY("8917_l2",		NULL),
 	REGULATOR_SUPPLY("iris_vdddig",		"wcnss_wlan.0"),
 	REGULATOR_SUPPLY("dsi_vdda",		"mipi_dsi.1"),
+	REGULATOR_SUPPLY("dsi_pll_vdda",	"mdp.0"),
 	REGULATOR_SUPPLY("mipi_csi_vdd",	"msm_csid.0"),
 	REGULATOR_SUPPLY("mipi_csi_vdd",	"msm_csid.1"),
 	REGULATOR_SUPPLY("mipi_csi_vdd",	"msm_csid.2"),
@@ -117,6 +118,7 @@
 VREG_CONSUMERS(L23) = {
 	REGULATOR_SUPPLY("8917_l23",		NULL),
 	REGULATOR_SUPPLY("dsi_vddio",		"mipi_dsi.1"),
+	REGULATOR_SUPPLY("dsi_pll_vddio",	"mdp.0"),
 	REGULATOR_SUPPLY("hdmi_avdd",		"hdmi_msm.0"),
 	REGULATOR_SUPPLY("hdmi_vcc",		"hdmi_msm.0"),
 	REGULATOR_SUPPLY("pll_vdd",		"pil_riva"),
@@ -196,18 +198,22 @@
 	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),
 	REGULATOR_SUPPLY("krait0",		"acpuclk-8627"),
 	REGULATOR_SUPPLY("krait0",		"acpuclk-8930"),
 	REGULATOR_SUPPLY("krait0",		"acpuclk-8930aa"),
+	REGULATOR_SUPPLY("krait0",		"acpuclk-8930ab"),
 };
 VREG_CONSUMERS(S6) = {
 	REGULATOR_SUPPLY("8917_s6",		NULL),
 	REGULATOR_SUPPLY("krait1",		"acpuclk-8627"),
 	REGULATOR_SUPPLY("krait1",		"acpuclk-8930"),
 	REGULATOR_SUPPLY("krait1",		"acpuclk-8930aa"),
+	REGULATOR_SUPPLY("krait1",		"acpuclk-8930ab"),
 };
 VREG_CONSUMERS(S7) = {
 	REGULATOR_SUPPLY("8917_s7",		NULL),
@@ -481,7 +487,8 @@
 	{ \
 		.constraints = { \
 			.name		= _name, \
-			.valid_ops_mask	= REGULATOR_CHANGE_VOLTAGE, \
+			.valid_ops_mask	= REGULATOR_CHANGE_VOLTAGE | \
+					  REGULATOR_CHANGE_STATUS, \
 			.min_uV		= _min_uV, \
 			.max_uV		= _max_uV, \
 		}, \
@@ -627,6 +634,18 @@
 	RPM_REG_MAP(L24,            0, 2, "krait1_mem",   "acpuclk-8930aa"),
 	RPM_REG_MAP(VDD_DIG_CORNER, 0, 1, "krait0_dig",   "acpuclk-8930aa"),
 	RPM_REG_MAP(VDD_DIG_CORNER, 0, 2, "krait1_dig",   "acpuclk-8930aa"),
+
+	RPM_REG_MAP(L23,            0, 1, "krait0_l23",   "acpuclk-8930ab"),
+	RPM_REG_MAP(S8,             0, 1, "krait0_s8",    "acpuclk-8930ab"),
+	RPM_REG_MAP(L23,            0, 2, "krait1_l23",   "acpuclk-8930ab"),
+	RPM_REG_MAP(S8,             0, 2, "krait1_s8",    "acpuclk-8930ab"),
+	RPM_REG_MAP(L23,            0, 6, "l2_l23",       "acpuclk-8930ab"),
+	RPM_REG_MAP(S8,             0, 6, "l2_s8",        "acpuclk-8930ab"),
+	RPM_REG_MAP(L24,            0, 1, "krait0_mem",   "acpuclk-8930ab"),
+	RPM_REG_MAP(L24,            0, 2, "krait1_mem",   "acpuclk-8930ab"),
+	RPM_REG_MAP(VDD_DIG_CORNER, 0, 1, "krait0_dig",   "acpuclk-8930ab"),
+	RPM_REG_MAP(VDD_DIG_CORNER, 0, 2, "krait1_dig",   "acpuclk-8930ab"),
+
 };
 
 struct rpm_regulator_platform_data
diff --git a/arch/arm/mach-msm/board-8930.c b/arch/arm/mach-msm/board-8930.c
index 05d2fe1..9464816 100644
--- a/arch/arm/mach-msm/board-8930.c
+++ b/arch/arm/mach-msm/board-8930.c
@@ -112,7 +112,6 @@
 #define KS8851_IRQ_GPIO		90
 #define HAP_SHIFT_LVL_OE_GPIO	47
 
-#define HDMI_MHL_MUX_GPIO       73
 #define MHL_GPIO_INT            72
 #define MHL_GPIO_RESET          71
 #define MHL_GPIO_PWR_EN         5
@@ -133,7 +132,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
@@ -377,9 +376,7 @@
  * to each other.
  * Don't swap the order unless you know what you are doing!
  */
-static struct ion_platform_data msm8930_ion_pdata = {
-	.nr = MSM_ION_HEAP_NUM,
-	.heaps = {
+struct ion_platform_heap msm8930_heaps[] = {
 		{
 			.id	= ION_SYSTEM_HEAP_ID,
 			.type	= ION_HEAP_TYPE_SYSTEM,
@@ -442,7 +439,12 @@
 			.extra_data = (void *) &co_msm8930_ion_pdata,
 		},
 #endif
-	}
+};
+
+static struct ion_platform_data msm8930_ion_pdata = {
+	.nr = MSM_ION_HEAP_NUM,
+	.heaps = msm8930_heaps,
+
 };
 
 static struct platform_device msm8930_ion_dev = {
@@ -762,7 +764,7 @@
 	.regulator = {
 	{
 		.name = "CDC_VDD_CP",
-		.min_uV = 1800000,
+		.min_uV = 2200000,
 		.max_uV = 2200000,
 		.optimum_uA = WCD9XXX_CDC_VDDA_CP_CUR_MAX,
 	},
@@ -828,7 +830,7 @@
 	.regulator = {
 	{
 		.name = "CDC_VDD_CP",
-		.min_uV = 1800000,
+		.min_uV = 2200000,
 		.max_uV = 2200000,
 		.optimum_uA = WCD9XXX_CDC_VDDA_CP_CUR_MAX,
 	},
@@ -2295,7 +2297,6 @@
 
 static struct platform_device *common_devices[] __initdata = {
 	&msm_8960_q6_lpass,
-	&msm_8960_q6_mss,
 	&msm_8960_riva,
 	&msm_pil_tzapps,
 	&msm_pil_vidc,
@@ -2714,11 +2715,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;
@@ -2734,6 +2758,9 @@
 
 	pdata = msm8930_device_acpuclk.dev.platform_data;
 	pdata->uses_pm8917 = true;
+
+	pdata = msm8930ab_device_acpuclk.dev.platform_data;
+	pdata->uses_pm8917 = true;
 }
 
 static void __init msm8930_cdp_init(void)
@@ -2790,18 +2817,25 @@
 	msm_spm_init(msm_spm_data, ARRAY_SIZE(msm_spm_data));
 	msm_spm_l2_init(msm_spm_l2_data);
 	msm8930_init_buses();
-	if (cpu_is_msm8627())
+	if (cpu_is_msm8627()) {
 		platform_add_devices(msm8627_footswitch,
 				msm8627_num_footswitch);
-	else
-		platform_add_devices(msm8930_footswitch,
-				msm8930_num_footswitch);
+	} else {
+		if (socinfo_get_pmic_model() == PMIC_MODEL_PM8917)
+			platform_add_devices(msm8930_pm8917_footswitch,
+					msm8930_pm8917_num_footswitch);
+		else
+			platform_add_devices(msm8930_footswitch,
+					msm8930_num_footswitch);
+	}
 	if (cpu_is_msm8627())
 		platform_device_register(&msm8627_device_acpuclk);
 	else if (cpu_is_msm8930())
 		platform_device_register(&msm8930_device_acpuclk);
 	else if (cpu_is_msm8930aa())
 		platform_device_register(&msm8930aa_device_acpuclk);
+	else if (cpu_is_msm8930ab())
+		platform_device_register(&msm8930ab_device_acpuclk);
 	platform_add_devices(early_common_devices,
 				ARRAY_SIZE(early_common_devices));
 	if (socinfo_get_pmic_model() != PMIC_MODEL_PM8917)
@@ -2825,6 +2859,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-8930.h b/arch/arm/mach-msm/board-8930.h
index 055576f..dbcfa9d 100644
--- a/arch/arm/mach-msm/board-8930.h
+++ b/arch/arm/mach-msm/board-8930.h
@@ -164,5 +164,7 @@
 #define MSM_8930_GSBI10_QUP_I2C_BUS_ID 10
 #define MSM_8930_GSBI12_QUP_I2C_BUS_ID 12
 
+#define HDMI_MHL_MUX_GPIO       73
+
 extern struct msm_rtb_platform_data msm8930_rtb_pdata;
 extern struct msm_cache_dump_platform_data msm8930_cache_dump_pdata;
diff --git a/arch/arm/mach-msm/board-8960-camera.c b/arch/arm/mach-msm/board-8960-camera.c
index 88fd527..7a2e9e1 100644
--- a/arch/arm/mach-msm/board-8960-camera.c
+++ b/arch/arm/mach-msm/board-8960-camera.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
@@ -185,6 +185,17 @@
 #define VFE_CAMIF_TIMER1_GPIO 2
 #define VFE_CAMIF_TIMER2_GPIO 3
 #define VFE_CAMIF_TIMER3_GPIO_INT 4
+
+static struct gpio flash_init_gpio[] = {
+	{VFE_CAMIF_TIMER1_GPIO, GPIOF_OUT_INIT_LOW, "CAMIF_TIMER1"},
+	{VFE_CAMIF_TIMER2_GPIO, GPIOF_OUT_INIT_LOW, "CAMIF_TIMER2"},
+};
+
+static struct msm_gpio_set_tbl flash_set_gpio[] = {
+	{VFE_CAMIF_TIMER1_GPIO, GPIOF_OUT_INIT_HIGH, 2000},
+	{VFE_CAMIF_TIMER2_GPIO, GPIOF_OUT_INIT_HIGH, 2000},
+};
+
 static struct msm_camera_sensor_strobe_flash_data strobe_flash_xenon = {
 	.flash_trigger = VFE_CAMIF_TIMER2_GPIO,
 	.flash_charge = VFE_CAMIF_TIMER1_GPIO,
@@ -193,14 +204,16 @@
 	.irq = MSM_GPIO_TO_INT(VFE_CAMIF_TIMER3_GPIO_INT),
 };
 
-#ifdef CONFIG_MSM_CAMERA_FLASH
 static struct msm_camera_sensor_flash_src msm_flash_src = {
 	.flash_sr_type = MSM_CAMERA_FLASH_SRC_EXT,
+	.init_gpio_tbl = flash_init_gpio,
+	.init_gpio_tbl_size = ARRAY_SIZE(flash_init_gpio),
+	.set_gpio_tbl = flash_set_gpio,
+	.set_gpio_tbl_size = ARRAY_SIZE(flash_set_gpio),
 	._fsrc.ext_driver_src.led_en = VFE_CAMIF_TIMER1_GPIO,
 	._fsrc.ext_driver_src.led_flash_en = VFE_CAMIF_TIMER2_GPIO,
 	._fsrc.ext_driver_src.flash_id = MAM_CAMERA_EXT_LED_FLASH_SC628A,
 };
-#endif
 
 static struct msm_bus_vectors cam_init_vectors[] = {
 	{
@@ -570,11 +583,15 @@
 	.vcm_enable     = 0,
 };
 
+static struct i2c_board_info sc628a_flash_i2c_info = {
+	I2C_BOARD_INFO("sc628a", 0x6E),
+};
+
 static struct msm_camera_sensor_flash_data flash_imx074 = {
 	.flash_type	= MSM_CAMERA_FLASH_LED,
-#ifdef CONFIG_MSM_CAMERA_FLASH
-	.flash_src	= &msm_flash_src
-#endif
+	.flash_src	= &msm_flash_src,
+	.board_info     = &sc628a_flash_i2c_info,
+	.bus_id         = MSM_8960_GSBI4_QUP_I2C_BUS_ID,
 };
 
 static struct msm_camera_csi_lane_params imx074_csi_lane_params = {
@@ -715,9 +732,9 @@
 
 static struct msm_camera_sensor_flash_data flash_imx091 = {
 	.flash_type	= MSM_CAMERA_FLASH_LED,
-#ifdef CONFIG_MSM_CAMERA_FLASH
-	.flash_src	= &msm_flash_src
-#endif
+	.flash_src	= &msm_flash_src,
+	.board_info     = &sc628a_flash_i2c_info,
+	.bus_id         = MSM_8960_GSBI4_QUP_I2C_BUS_ID,
 };
 
 static struct msm_camera_sensor_platform_info sensor_board_info_imx091 = {
@@ -876,11 +893,6 @@
 	I2C_BOARD_INFO("s5k3l1yx", 0x20),
 	.platform_data = &msm_camera_sensor_s5k3l1yx_data,
 	},
-#ifdef CONFIG_MSM_CAMERA_FLASH_SC628A
-	{
-	I2C_BOARD_INFO("sc628a", 0x6E),
-	},
-#endif
 	{
 	I2C_BOARD_INFO("imx091", 0x34),
 	.platform_data = &msm_camera_sensor_imx091_data,
diff --git a/arch/arm/mach-msm/board-8960-display.c b/arch/arm/mach-msm/board-8960-display.c
index 3052902..ecf5ec6 100644
--- a/arch/arm/mach-msm/board-8960-display.c
+++ b/arch/arm/mach-msm/board-8960-display.c
@@ -988,6 +988,12 @@
 
 void __init msm8960_init_fb(void)
 {
+	uint32_t soc_platform_version = socinfo_get_version();
+
+
+	if (SOCINFO_VERSION_MAJOR(soc_platform_version) >= 3)
+		mdp_pdata.mdp_rev = MDP_REV_43;
+
 	if (cpu_is_msm8960ab())
 		mdp_pdata.mdp_rev = MDP_REV_44;
 
diff --git a/arch/arm/mach-msm/board-8960-pmic.c b/arch/arm/mach-msm/board-8960-pmic.c
index d8a260b..f0ba1c9 100644
--- a/arch/arm/mach-msm/board-8960-pmic.c
+++ b/arch/arm/mach-msm/board-8960-pmic.c
@@ -396,7 +396,6 @@
 #define MAX_VOLTAGE_MV		4200
 #define CHG_TERM_MA		100
 static struct pm8921_charger_platform_data pm8921_chg_pdata __devinitdata = {
-	.safety_time		= 180,
 	.update_time		= 60000,
 	.max_voltage		= MAX_VOLTAGE_MV,
 	.min_voltage		= 3200,
@@ -407,7 +406,7 @@
 	.resume_charge_percent	= 99,
 	.term_current		= CHG_TERM_MA,
 	.cool_temp		= 10,
-	.warm_temp		= 40,
+	.warm_temp		= 45,
 	.temp_check_period	= 1,
 	.max_bat_chg_current	= 1100,
 	.cool_bat_chg_current	= 350,
@@ -425,13 +424,15 @@
 
 static struct pm8921_bms_platform_data pm8921_bms_pdata __devinitdata = {
 	.battery_type			= BATT_UNKNOWN,
-	.r_sense			= 10,
+	.r_sense_uohm			= 10000,
 	.v_cutoff			= 3400,
 	.max_voltage_uv			= MAX_VOLTAGE_MV * 1000,
 	.rconn_mohm			= 18,
 	.shutdown_soc_valid_limit	= 20,
 	.adjust_soc_low_threshold	= 25,
 	.chg_term_ua			= CHG_TERM_MA * 1000,
+	.normal_voltage_calc_ms		= 20000,
+	.low_voltage_calc_ms		= 1000,
 };
 
 #define	PM8921_LC_LED_MAX_CURRENT	4	/* I = 4mA */
@@ -553,7 +554,7 @@
 };
 
 static struct pm8xxx_ccadc_platform_data pm8xxx_ccadc_pdata = {
-	.r_sense		= 10,
+	.r_sense_uohm		= 10000,
 	.calib_delay_ms		= 600000,
 };
 
@@ -612,4 +613,8 @@
 
 	if (machine_is_msm8960_fluid())
 		pm8921_bms_pdata.rconn_mohm = 20;
+
+	if (!machine_is_msm8960_fluid() && !machine_is_msm8960_liquid()
+			&& !machine_is_msm8960_mtp())
+		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 f9e2c8e..397411d 100644
--- a/arch/arm/mach-msm/board-8960-regulator.c
+++ b/arch/arm/mach-msm/board-8960-regulator.c
@@ -382,7 +382,8 @@
 	{ \
 		.constraints = { \
 			.name		= _name, \
-			.valid_ops_mask	= REGULATOR_CHANGE_VOLTAGE, \
+			.valid_ops_mask	= REGULATOR_CHANGE_VOLTAGE | \
+					  REGULATOR_CHANGE_STATUS, \
 			.min_uV		= _min_uV, \
 			.max_uV		= _max_uV, \
 		}, \
diff --git a/arch/arm/mach-msm/board-8960.c b/arch/arm/mach-msm/board-8960.c
index 4a78e31..32ab870 100644
--- a/arch/arm/mach-msm/board-8960.c
+++ b/arch/arm/mach-msm/board-8960.c
@@ -398,9 +398,7 @@
  * to each other.
  * Don't swap the order unless you know what you are doing!
  */
-static struct ion_platform_data msm8960_ion_pdata = {
-	.nr = MSM_ION_HEAP_NUM,
-	.heaps = {
+struct ion_platform_heap msm8960_heaps[] = {
 		{
 			.id	= ION_SYSTEM_HEAP_ID,
 			.type	= ION_HEAP_TYPE_SYSTEM,
@@ -463,7 +461,11 @@
 			.extra_data = (void *) &co_msm8960_ion_pdata,
 		},
 #endif
-	}
+};
+
+static struct ion_platform_data msm8960_ion_pdata = {
+	.nr = MSM_ION_HEAP_NUM,
+	.heaps = msm8960_heaps,
 };
 
 static struct platform_device msm8960_ion_dev = {
@@ -1593,26 +1595,41 @@
 };
 
 static uint8_t spm_wfi_cmd_sequence[] __initdata = {
-			0x03, 0x0f,
+	0x03, 0x0f,
 };
 
 static uint8_t spm_retention_cmd_sequence[] __initdata = {
-			0x00, 0x05, 0x03, 0x0D,
-			0x0B, 0x00, 0x0f,
+	0x00, 0x05, 0x03, 0x0D,
+	0x0B, 0x00, 0x0f,
 };
 
 static uint8_t spm_power_collapse_without_rpm[] __initdata = {
-			0x00, 0x24, 0x54, 0x10,
-			0x09, 0x03, 0x01,
-			0x10, 0x54, 0x30, 0x0C,
-			0x24, 0x30, 0x0f,
+	0x00, 0x24, 0x54, 0x10,
+	0x09, 0x03, 0x01,
+	0x10, 0x54, 0x30, 0x0C,
+	0x24, 0x30, 0x0f,
 };
 
 static uint8_t spm_power_collapse_with_rpm[] __initdata = {
-			0x00, 0x24, 0x54, 0x10,
-			0x09, 0x07, 0x01, 0x0B,
-			0x10, 0x54, 0x30, 0x0C,
-			0x24, 0x30, 0x0f,
+	0x00, 0x24, 0x54, 0x10,
+	0x09, 0x07, 0x01, 0x0B,
+	0x10, 0x54, 0x30, 0x0C,
+	0x24, 0x30, 0x0f,
+};
+
+/* 8960AB has a different command to assert apc_pdn */
+static uint8_t spm_power_collapse_without_rpm_krait_v3[] __initdata = {
+	0x00, 0x24, 0x84, 0x10,
+	0x09, 0x03, 0x01,
+	0x10, 0x84, 0x30, 0x0C,
+	0x24, 0x30, 0x0f,
+};
+
+static uint8_t spm_power_collapse_with_rpm_krait_v3[] __initdata = {
+	0x00, 0x24, 0x84, 0x10,
+	0x09, 0x07, 0x01, 0x0B,
+	0x10, 0x84, 0x30, 0x0C,
+	0x24, 0x30, 0x0f,
 };
 
 static struct msm_spm_seq_entry msm_spm_boot_cpu_seq_list[] __initdata = {
@@ -1621,13 +1638,11 @@
 		.notify_rpm = false,
 		.cmd = spm_wfi_cmd_sequence,
 	},
-
 	[1] = {
 		.mode = MSM_SPM_MODE_POWER_RETENTION,
 		.notify_rpm = false,
 		.cmd = spm_retention_cmd_sequence,
 	},
-
 	[2] = {
 		.mode = MSM_SPM_MODE_POWER_COLLAPSE,
 		.notify_rpm = false,
@@ -1663,7 +1678,7 @@
 		.reg_base_addr = MSM_SAW0_BASE,
 		.reg_init_values[MSM_SPM_REG_SAW2_CFG] = 0x1F,
 #if defined(CONFIG_MSM_AVS_HW)
-		.reg_init_values[MSM_SPM_REG_SAW2_AVS_CTL] = 0x50589464,
+		.reg_init_values[MSM_SPM_REG_SAW2_AVS_CTL] = 0x58589464,
 		.reg_init_values[MSM_SPM_REG_SAW2_AVS_HYSTERESIS] = 0x00020000,
 #endif
 		.reg_init_values[MSM_SPM_REG_SAW2_SPM_CTL] = 0x01,
@@ -1678,7 +1693,7 @@
 		.reg_base_addr = MSM_SAW1_BASE,
 		.reg_init_values[MSM_SPM_REG_SAW2_CFG] = 0x1F,
 #if defined(CONFIG_MSM_AVS_HW)
-		.reg_init_values[MSM_SPM_REG_SAW2_AVS_CTL] = 0x50589464,
+		.reg_init_values[MSM_SPM_REG_SAW2_AVS_CTL] = 0x58589464,
 		.reg_init_values[MSM_SPM_REG_SAW2_AVS_HYSTERESIS] = 0x00020000,
 #endif
 		.reg_init_values[MSM_SPM_REG_SAW2_SPM_CTL] = 0x01,
@@ -2875,10 +2890,20 @@
 	/* 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;
+
+		/*
+		 * If this an A320 GPU device (MSM8960AB), then
+		 * switch the resource table to 8960AB, to reflect the
+		 * separate register and shader memory mapping used in A320.
+		 */
+
+		msm_kgsl_3d0.num_resources = kgsl_num_resources_8960ab;
+		msm_kgsl_3d0.resource = kgsl_3d0_resources_8960ab;
 	} else {
 		kgsl_3d0_pdata->iommu_count = 1;
+
 		if (SOCINFO_VERSION_MAJOR(soc_platform_version) == 1) {
 			kgsl_3d0_pdata->pwrlevel[0].gpu_freq = 320000000;
 			kgsl_3d0_pdata->pwrlevel[1].gpu_freq = 266667000;
@@ -3216,6 +3241,34 @@
 	msm_tsens_early_init(&msm_tsens_pdata);
 }
 
+static void __init msm8960ab_update_krait_spm(void)
+{
+	int i;
+
+	/* Reset the AVS registers until we have support for AVS */
+	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;
+	}
+
+	/* Update the SPM sequences for SPC and PC */
+	for (i = 0; i < ARRAY_SIZE(msm_spm_data); i++) {
+		int j;
+		struct msm_spm_platform_data *pdata = &msm_spm_data[i];
+		for (j = 0; j < pdata->num_modes; j++) {
+			if (pdata->modes[j].cmd ==
+					spm_power_collapse_without_rpm)
+				pdata->modes[j].cmd =
+				spm_power_collapse_without_rpm_krait_v3;
+			else if (pdata->modes[j].cmd ==
+					spm_power_collapse_with_rpm)
+				pdata->modes[j].cmd =
+				spm_power_collapse_with_rpm_krait_v3;
+		}
+	}
+}
+
 static void __init msm8960_cdp_init(void)
 {
 	if (meminfo_init(SYS_MEMORY, SZ_256M) < 0)
@@ -3265,14 +3318,25 @@
 
 	msm8960_init_pmic();
 	if (machine_is_msm8960_liquid() || (machine_is_msm8960_mtp() &&
-		(socinfo_get_platform_subtype() == PLATFORM_SUBTYPE_SGLTE)))
+		(socinfo_get_platform_subtype() == PLATFORM_SUBTYPE_SGLTE ||
+			cpu_is_msm8960ab())))
 		msm_isa1200_board_info[0].platform_data = &isa1200_1_pdata;
 	msm8960_i2c_init();
 	msm8960_gfx_init();
+
+	if (cpu_is_msm8960ab())
+		msm8960ab_update_krait_spm();
 	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())
diff --git a/arch/arm/mach-msm/board-8974-gpiomux.c b/arch/arm/mach-msm/board-8974-gpiomux.c
index 50b59e1..ad74182 100644
--- a/arch/arm/mach-msm/board-8974-gpiomux.c
+++ b/arch/arm/mach-msm/board-8974-gpiomux.c
@@ -16,6 +16,7 @@
 #include <mach/board.h>
 #include <mach/gpio.h>
 #include <mach/gpiomux.h>
+#include <mach/socinfo.h>
 
 #define KS8851_IRQ_GPIO 94
 
@@ -152,6 +153,26 @@
 
 };
 
+static struct gpiomux_setting mhl_suspend_config = {
+	.func = GPIOMUX_FUNC_GPIO,
+	.drv = GPIOMUX_DRV_2MA,
+	.pull = GPIOMUX_PULL_DOWN,
+};
+
+static struct gpiomux_setting mhl_active_1_cfg = {
+	.func = GPIOMUX_FUNC_1,
+	.drv = GPIOMUX_DRV_2MA,
+	.pull = GPIOMUX_PULL_UP,
+	.dir = GPIOMUX_OUT_HIGH,
+};
+
+static struct gpiomux_setting mhl_active_2_cfg = {
+	.func = GPIOMUX_FUNC_1,
+	.drv = GPIOMUX_DRV_2MA,
+	.pull = GPIOMUX_PULL_UP,
+};
+
+
 static struct gpiomux_setting hdmi_suspend_cfg = {
 	.func = GPIOMUX_FUNC_GPIO,
 	.drv = GPIOMUX_DRV_2MA,
@@ -170,6 +191,34 @@
 	.pull = GPIOMUX_PULL_DOWN,
 };
 
+static struct msm_gpiomux_config msm_mhl_configs[] __initdata = {
+	{
+		/* mhl-sii8334 pwr */
+		.gpio = 12,
+		.settings = {
+			[GPIOMUX_SUSPENDED] = &mhl_suspend_config,
+			[GPIOMUX_ACTIVE]    = &mhl_active_1_cfg,
+		},
+	},
+	{
+		/* mhl-sii8334 intr */
+		.gpio = 82,
+		.settings = {
+			[GPIOMUX_SUSPENDED] = &mhl_suspend_config,
+			[GPIOMUX_ACTIVE]    = &mhl_active_1_cfg,
+		},
+	},
+	{
+		/* mhl-sii8334 reset */
+		.gpio = 8,
+		.settings = {
+			[GPIOMUX_SUSPENDED] = &mhl_suspend_config,
+			[GPIOMUX_ACTIVE]    = &mhl_active_2_cfg,
+		},
+	},
+};
+
+
 static struct msm_gpiomux_config msm_hdmi_configs[] __initdata = {
 	{
 		.gpio = 31,
@@ -201,6 +250,21 @@
 	},
 };
 
+static struct msm_gpiomux_config msm_rumi_blsp_configs[] __initdata = {
+	{
+		.gpio      = 45,	/* BLSP2 UART8 TX */
+		.settings = {
+			[GPIOMUX_SUSPENDED] = &gpio_uart_config,
+		},
+	},
+	{
+		.gpio      = 46,	/* BLSP2 UART8 RX */
+		.settings = {
+			[GPIOMUX_SUSPENDED] = &gpio_uart_config,
+		},
+	},
+};
+
 static struct msm_gpiomux_config msm_blsp_configs[] __initdata = {
 #if defined(CONFIG_KS8851) || defined(CONFIG_KS8851_MODULE)
 	{
@@ -575,4 +639,9 @@
 	msm_gpiomux_install(msm_taiko_config, ARRAY_SIZE(msm_taiko_config));
 
 	msm_gpiomux_install(msm_hdmi_configs, ARRAY_SIZE(msm_hdmi_configs));
+	msm_gpiomux_install(msm_mhl_configs, ARRAY_SIZE(msm_mhl_configs));
+
+	if (machine_is_msm8974_rumi())
+		msm_gpiomux_install(msm_rumi_blsp_configs,
+				    ARRAY_SIZE(msm_rumi_blsp_configs));
 }
diff --git a/arch/arm/mach-msm/board-8974.c b/arch/arm/mach-msm/board-8974.c
index 7480437..ca8f68a 100644
--- a/arch/arm/mach-msm/board-8974.c
+++ b/arch/arm/mach-msm/board-8974.c
@@ -49,17 +49,6 @@
 #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 msm8974_reserve_table[] __initdata = {
 	[MEMTYPE_SMI] = {
@@ -77,190 +66,22 @@
 	return MEMTYPE_EBI1;
 }
 
-static void __init reserve_ebi_memory(void)
-{
-#ifdef CONFIG_KERNEL_PMEM_EBI_REGION
-	msm8974_reserve_table[MEMTYPE_EBI1].size += kernel_ebi1_mem_size;
-#endif
-}
-
-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	= "wcnss_smd_in",
-		.start	= 32 + 142,		/* WcnssAppsSmdMedIrq  */
-		.flags	= IORESOURCE_IRQ,
-	},
-	{
-		.name	= "wcnss_smsm_in",
-		.start	= 32 + 144,		/* RivaAppsWlanSmsmIrq  */
-		.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_WCNSS,
-		.subsys_name = "wcnss",
-		.edge = SMD_APPS_WCNSS,
-
-		.smd_int.irq_name = "wcnss_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 << 17,
-		.smd_int.out_base = (void __iomem *)MSM_APCS_GCC_BASE,
-		.smd_int.out_offset = 0x8,
-
-		.smsm_int.irq_name = "wcnss_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 << 19,
-		.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_8974 = {
-	.name	= "msm_smd",
-	.id	= -1,
-	.resource = smd_resource,
-	.num_resources = ARRAY_SIZE(smd_resource),
-	.dev = {
-		.platform_data = &smd_platform_data,
-	}
-};
-
-static void __init msm8974_calculate_reserve_sizes(void)
-{
-	reserve_ebi_memory();
-}
-
 static struct reserve_info msm8974_reserve_info __initdata = {
 	.memtype_reserve_table = msm8974_reserve_table,
-	.calculate_reserve_sizes = msm8974_calculate_reserve_sizes,
 	.paddr_to_memtype = msm8974_paddr_to_memtype,
 };
 
+void __init msm_8974_reserve(void)
+{
+	reserve_info = &msm8974_reserve_info;
+	of_scan_flat_dt(dt_scan_for_memory_reserve, msm8974_reserve_table);
+	msm_reserve();
+}
+
 static void __init msm8974_early_memory(void)
 {
 	reserve_info = &msm8974_reserve_info;
-	of_scan_flat_dt(dt_scan_for_memory_reserve, msm8974_reserve_table);
+	of_scan_flat_dt(dt_scan_for_memory_hole, msm8974_reserve_table);
 }
 
 #define BIMC_BASE	0xfc380000
@@ -450,11 +271,6 @@
 				ARRAY_SIZE(msm_bus_8974_devices));
 };
 
-void __init msm8974_add_devices(void)
-{
-	platform_device_register(&msm_device_smd_8974);
-}
-
 /*
  * Used to satisfy dependencies for devices that need to be
  * run early or in a particular order. Most likely your device doesn't fall
@@ -488,8 +304,6 @@
 			"usb_bam", NULL),
 	OF_DEV_AUXDATA("qcom,spi-qup-v2", 0xF9924000, \
 			"spi_qsd.1", 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, \
@@ -501,7 +315,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, \
@@ -561,7 +374,6 @@
 	regulator_has_full_constraints();
 	of_platform_populate(NULL, of_default_bus_match_table, adata, NULL);
 
-	msm8974_add_devices();
 	msm8974_add_drivers();
 }
 
@@ -582,7 +394,7 @@
 	.handle_irq = gic_handle_irq,
 	.timer = &msm_dt_timer,
 	.dt_compat = msm8974_dt_match,
-	.reserve = msm_reserve,
+	.reserve = msm_8974_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..e5b7678 100644
--- a/arch/arm/mach-msm/board-9615-gpiomux.c
+++ b/arch/arm/mach-msm/board-9615-gpiomux.c
@@ -37,7 +37,7 @@
 
 static struct gpiomux_setting gsbi5 = {
 	.func = GPIOMUX_FUNC_1,
-	.drv = GPIOMUX_DRV_8MA,
+	.drv = GPIOMUX_DRV_2MA,
 	.pull = GPIOMUX_PULL_NONE,
 };
 
@@ -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 b9da615..1dcd54f 100644
--- a/arch/arm/mach-msm/board-9615.c
+++ b/arch/arm/mach-msm/board-9615.c
@@ -81,9 +81,7 @@
 	.align = PAGE_SIZE,
 };
 
-static struct ion_platform_data ion_pdata = {
-	.nr = MSM_ION_HEAP_NUM,
-	.heaps = {
+static struct ion_platform_heap msm9615_heaps[] = {
 		{
 			.id	= ION_SYSTEM_HEAP_ID,
 			.type	= ION_HEAP_TYPE_SYSTEM,
@@ -102,7 +100,11 @@
 			.memory_type = ION_EBI_TYPE,
 			.extra_data = (void *) &co_ion_pdata,
 		},
-	}
+};
+
+static struct ion_platform_data ion_pdata = {
+	.nr = MSM_ION_HEAP_NUM,
+	.heaps = msm9615_heaps,
 };
 
 static struct platform_device ion_dev = {
@@ -621,7 +623,7 @@
 #define USB_BAM_PHY_BASE	0x12502000
 #define HSIC_BAM_PHY_BASE	0x12542000
 #define A2_BAM_PHY_BASE		0x124C2000
-static struct usb_bam_pipe_connect msm_usb_bam_connections[MAX_BAMS][4][2] = {
+static struct usb_bam_pipe_connect msm_usb_bam_connections[MAX_BAMS][8][2] = {
 	[HSUSB_BAM][0][USB_TO_PEER_PERIPHERAL] = {
 		.src_phy_addr = USB_BAM_PHY_BASE,
 		.src_pipe_index = 11,
@@ -762,6 +764,7 @@
 	.disable_reset_on_disconnect	= true,
 	.enable_lpm_on_dev_suspend	= true,
 	.core_clk_always_on_workaround = true,
+	.delay_lpm_on_disconnect = true,
 };
 
 
@@ -907,6 +910,7 @@
 	&msm_cpu_fe,
 	&msm_stub_codec,
 	&msm_voice,
+	&msm_dtmf,
 	&msm_voip,
 	&msm_i2s_cpudai0,
 	&msm_i2s_cpudai1,
@@ -922,6 +926,10 @@
 	&msm_cpudai_auxpcm_tx,
 	&msm_cpudai_sec_auxpcm_rx,
 	&msm_cpudai_sec_auxpcm_tx,
+	&msm_cpudai_stub,
+	&msm_cpudai_incall_music_rx,
+	&msm_cpudai_incall_record_rx,
+	&msm_cpudai_incall_record_tx,
 
 #if defined(CONFIG_CRYPTO_DEV_QCRYPTO) || \
 		defined(CONFIG_CRYPTO_DEV_QCRYPTO_MODULE)
@@ -939,6 +947,7 @@
 	&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 c4e174b..686bb41 100644
--- a/arch/arm/mach-msm/board-9625-gpiomux.c
+++ b/arch/arm/mach-msm/board-9625-gpiomux.c
@@ -25,19 +25,49 @@
 };
 
 static struct gpiomux_setting gpio_spi_cs_config = {
-	.func = GPIOMUX_FUNC_9,
+	.func = GPIOMUX_FUNC_1,
 	.drv = GPIOMUX_DRV_12MA,
 	.pull = GPIOMUX_PULL_NONE,
 };
 
 static struct gpiomux_setting gpio_spi_config = {
-	.func = GPIOMUX_FUNC_2,
+	.func = GPIOMUX_FUNC_1,
 	.drv = GPIOMUX_DRV_12MA,
 	.pull = GPIOMUX_PULL_NONE,
 };
 
+static struct gpiomux_setting gpio_i2c_config = {
+	.func = GPIOMUX_FUNC_3,
+	.drv = GPIOMUX_DRV_2MA,
+	.pull = GPIOMUX_PULL_NONE,
+};
+
 static struct msm_gpiomux_config msm_blsp_configs[] __initdata = {
 	{
+		.gpio      = 4,		/* BLSP1 QUP2 SPI_DATA_MOSI */
+		.settings = {
+			[GPIOMUX_SUSPENDED] = &gpio_spi_config,
+		},
+	},
+	{
+		.gpio      = 5,		/* BLSP1 QUP2 SPI_DATA_MISO */
+		.settings = {
+			[GPIOMUX_SUSPENDED] = &gpio_spi_config,
+		},
+	},
+	{
+		.gpio      = 6,		/* BLSP1 QUP2 SPI_CS_N */
+		.settings = {
+			[GPIOMUX_SUSPENDED] = &gpio_spi_cs_config,
+		},
+	},
+	{
+		.gpio      = 7,		/* BLSP1 QUP2 SPI_CLK */
+		.settings = {
+			[GPIOMUX_SUSPENDED] = &gpio_spi_config,
+		},
+	},
+	{
 		.gpio      = 8,	       /* BLSP1 UART TX */
 		.settings = {
 			[GPIOMUX_SUSPENDED] = &gpio_uart_config,
@@ -50,30 +80,83 @@
 		},
 	},
 	{
-		.gpio      = 69,		/* BLSP6 QUP SPI_CS_N */
+		.gpio      = 10,		/* BLSP1 QUP3 I2C_DAT */
 		.settings = {
-			[GPIOMUX_SUSPENDED] = &gpio_spi_cs_config,
+			[GPIOMUX_SUSPENDED] = &gpio_i2c_config,
 		},
 	},
 	{
-		.gpio      = 20,		/* BLSP6 QUP SPI_DATA_MOSI */
+		.gpio      = 11,		/* BLSP1 QUP3 I2C_CLK */
 		.settings = {
-			[GPIOMUX_SUSPENDED] = &gpio_spi_config,
+			[GPIOMUX_SUSPENDED] = &gpio_i2c_config,
 		},
 	},
-	{
-		.gpio      = 21,		/* BLSP6 QUP SPI_DATA_MISO */
-		.settings = {
-			[GPIOMUX_SUSPENDED] = &gpio_spi_config,
-		},
-	},
-	{
-		.gpio      = 23,		/* BLSP6 QUP SPI_CLK */
-		.settings = {
-			[GPIOMUX_SUSPENDED] = &gpio_spi_config,
-		},
-	},
+};
 
+static struct gpiomux_setting  mi2s_active_cfg = {
+	.func = GPIOMUX_FUNC_1,
+	.drv = GPIOMUX_DRV_8MA,
+	.pull = GPIOMUX_PULL_NONE,
+};
+
+static struct gpiomux_setting  mi2s_suspend_cfg = {
+	.func = GPIOMUX_FUNC_GPIO,
+	.drv = GPIOMUX_DRV_2MA,
+	.pull = GPIOMUX_PULL_DOWN,
+};
+
+static struct gpiomux_setting codec_reset = {
+	.func = GPIOMUX_FUNC_GPIO,
+	.drv = GPIOMUX_DRV_6MA,
+	.pull = GPIOMUX_PULL_NONE,
+	.dir = GPIOMUX_OUT_LOW,
+};
+
+static struct msm_gpiomux_config mdm9625_mi2s_configs[] __initdata = {
+	{
+		.gpio	= 12,		/* mi2s ws */
+		.settings = {
+			[GPIOMUX_SUSPENDED] = &mi2s_suspend_cfg,
+			[GPIOMUX_ACTIVE] = &mi2s_active_cfg,
+		},
+	},
+	{
+		.gpio	= 15,		/* mi2s sclk */
+		.settings = {
+			[GPIOMUX_SUSPENDED] = &mi2s_suspend_cfg,
+			[GPIOMUX_ACTIVE] = &mi2s_active_cfg,
+		},
+	},
+	{
+		.gpio	= 14,		/* mi2s dout */
+		.settings = {
+			[GPIOMUX_SUSPENDED] = &mi2s_suspend_cfg,
+			[GPIOMUX_ACTIVE] = &mi2s_active_cfg,
+		},
+	},
+	{
+		.gpio	= 13,		/* mi2s din */
+		.settings = {
+			[GPIOMUX_SUSPENDED] = &mi2s_suspend_cfg,
+			[GPIOMUX_ACTIVE] = &mi2s_active_cfg,
+		},
+	},
+	{
+		.gpio	= 71,		/* mi2s mclk */
+		.settings = {
+			[GPIOMUX_SUSPENDED] = &mi2s_suspend_cfg,
+			[GPIOMUX_ACTIVE] = &mi2s_active_cfg,
+		},
+	},
+};
+
+static struct msm_gpiomux_config mdm9625_cdc_reset_config[] __initdata = {
+	{
+		.gpio   = 22,           /* SYS_RST_N */
+		.settings = {
+			[GPIOMUX_SUSPENDED] = &codec_reset,
+		},
+	}
 };
 
 static struct gpiomux_setting sdc3_clk_active_cfg = {
@@ -152,6 +235,47 @@
 	},
 };
 
+static struct gpiomux_setting wlan_ath6kl_active_config = {
+	.func = GPIOMUX_FUNC_GPIO,
+	.drv = GPIOMUX_DRV_2MA,
+	.pull = GPIOMUX_PULL_NONE,
+	.dir = GPIOMUX_OUT_LOW,
+};
+
+static struct gpiomux_setting wlan_ath6kl_suspend_config = {
+	.func = GPIOMUX_FUNC_GPIO,
+	.drv = GPIOMUX_DRV_2MA,
+	.pull = GPIOMUX_PULL_NONE,
+	.dir = GPIOMUX_IN,
+};
+
+static struct msm_gpiomux_config wlan_ath6kl_configs[] __initdata = {
+	{
+		.gpio      = 62,/* CHIP_PWD_L */
+		.settings = {
+			[GPIOMUX_ACTIVE] = &wlan_ath6kl_active_config,
+			[GPIOMUX_SUSPENDED] = &wlan_ath6kl_suspend_config,
+		},
+	},
+};
+
+static struct gpiomux_setting sdc2_card_det_cfg = {
+	.func = GPIOMUX_FUNC_GPIO,
+	.drv = GPIOMUX_DRV_2MA,
+	.pull = GPIOMUX_PULL_DOWN,
+	.dir = GPIOMUX_IN,
+};
+
+struct msm_gpiomux_config sdc2_card_det_config[] __initdata = {
+	{
+		.gpio = 66,
+		.settings = {
+			[GPIOMUX_ACTIVE]    = &sdc2_card_det_cfg,
+			[GPIOMUX_SUSPENDED] = &sdc2_card_det_cfg,
+		},
+	},
+};
+
 void __init msm9625_init_gpiomux(void)
 {
 	int rc;
@@ -164,4 +288,12 @@
 
 	msm_gpiomux_install(msm_blsp_configs, ARRAY_SIZE(msm_blsp_configs));
 	msm_gpiomux_install(sdc3_configs, ARRAY_SIZE(sdc3_configs));
+	msm_gpiomux_install(wlan_ath6kl_configs,
+		ARRAY_SIZE(wlan_ath6kl_configs));
+	msm_gpiomux_install(mdm9625_mi2s_configs,
+			ARRAY_SIZE(mdm9625_mi2s_configs));
+	msm_gpiomux_install(mdm9625_cdc_reset_config,
+			ARRAY_SIZE(mdm9625_cdc_reset_config));
+	msm_gpiomux_install(sdc2_card_det_config,
+		ARRAY_SIZE(sdc2_card_det_config));
 }
diff --git a/arch/arm/mach-msm/board-9625.c b/arch/arm/mach-msm/board-9625.c
index 49f2561..42f3f41 100644
--- a/arch/arm/mach-msm/board-9625.c
+++ b/arch/arm/mach-msm/board-9625.c
@@ -21,11 +21,8 @@
 #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>
@@ -35,19 +32,16 @@
 #include <mach/msm_memtypes.h>
 #include <mach/msm_iomap.h>
 #include <mach/msm_smd.h>
-#include <mach/scm.h>
 #include <mach/rpm-smd.h>
 #include <mach/rpm-regulator-smd.h>
-#include <mach/mpm.h>
+#include "board-dt.h"
+#include <mach/msm_bus_board.h>
 #include "clock.h"
 #include "modem_notifier.h"
 #include "lpm_resources.h"
 #include "spm.h"
 
 #define MSM_KERNEL_EBI_SIZE	0x51000
-#define SCM_SVC_L2CC_PL310	16
-#define L2CC_PL310_CTRL_ID	1
-#define L2CC_PL310_ON		1
 
 static struct memtype_reserve msm9625_reserve_table[] __initdata = {
 	[MEMTYPE_SMI] = {
@@ -76,10 +70,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),
@@ -103,13 +93,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
@@ -118,45 +101,19 @@
 static struct of_dev_auxdata msm9625_auxdata_lookup[] __initdata = {
 	OF_DEV_AUXDATA("qcom,msm-lsuart-v14", 0xF991F000, \
 			"msm_serial_hsl.0", NULL),
-	OF_DEV_AUXDATA("qcom,spi-qup-v2", 0xF9928000, \
-			"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),
+	OF_DEV_AUXDATA("qcom,msm-tsens", 0xFC4A8000, \
+			"msm-tsens", NULL),
+	OF_DEV_AUXDATA("qcom,usb-bam-msm", 0xF9A44000, \
+			"usb_bam", NULL),
 	{}
 };
 
-static struct of_device_id mpm_match[] __initdata = {
-	{.compatible = "qcom,mpm-v2", },
-	{},
-};
-
-void __init msm9625_init_irq(void)
-{
-	struct device_node *node;
-	scm_call_atomic1(SCM_SVC_L2CC_PL310, L2CC_PL310_CTRL_ID, L2CC_PL310_ON);
-	l2x0_of_init(0, ~0UL);
-	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_timer_init(void)
-{
-	arch_timer_of_register();
-}
-
-static struct sys_timer msm_dt_timer = {
-	.init = msm_dt_timer_init
-};
-
 static void __init msm9625_early_memory(void)
 {
 	reserve_info = &msm9625_reserve_info;
@@ -297,6 +254,97 @@
 	}
 };
 
+#define BIMC_BASE	0xfc380000
+#define BIMC_SIZE	0x0006A000
+#define SYS_NOC_BASE	0xfc460000
+#define PERIPH_NOC_BASE 0xFC468000
+#define CONFIG_NOC_BASE	0xfc480000
+#define NOC_SIZE	0x00004000
+
+static struct resource bimc_res[] = {
+	{
+		.start = BIMC_BASE,
+		.end = BIMC_BASE + BIMC_SIZE,
+		.flags = IORESOURCE_MEM,
+		.name = "bimc_mem",
+	},
+};
+
+static struct resource sys_noc_res[] = {
+	{
+		.start = SYS_NOC_BASE,
+		.end = SYS_NOC_BASE + NOC_SIZE,
+		.flags = IORESOURCE_MEM,
+		.name = "sys_noc_mem",
+	},
+};
+
+static struct resource config_noc_res[] = {
+	{
+		.start = CONFIG_NOC_BASE,
+		.end = CONFIG_NOC_BASE + NOC_SIZE,
+		.flags = IORESOURCE_MEM,
+		.name = "config_noc_mem",
+	},
+};
+
+static struct resource periph_noc_res[] = {
+	{
+		.start = PERIPH_NOC_BASE,
+		.end = PERIPH_NOC_BASE + NOC_SIZE,
+		.flags = IORESOURCE_MEM,
+		.name = "periph_noc_mem",
+	},
+};
+
+static struct platform_device msm_bus_sys_noc = {
+	.name  = "msm_bus_fabric",
+	.id    =  MSM_BUS_FAB_SYS_NOC,
+	.num_resources = ARRAY_SIZE(sys_noc_res),
+	.resource = sys_noc_res,
+};
+
+static struct platform_device msm_bus_bimc = {
+	.name  = "msm_bus_fabric",
+	.id    = MSM_BUS_FAB_BIMC,
+	.num_resources = ARRAY_SIZE(bimc_res),
+	.resource = bimc_res,
+};
+
+static struct platform_device msm_bus_periph_noc = {
+	.name  = "msm_bus_fabric",
+	.id    = MSM_BUS_FAB_PERIPH_NOC,
+	.num_resources = ARRAY_SIZE(periph_noc_res),
+	.resource = periph_noc_res,
+};
+
+static struct platform_device msm_bus_config_noc = {
+	.name  = "msm_bus_fabric",
+	.id    = MSM_BUS_FAB_CONFIG_NOC,
+	.num_resources = ARRAY_SIZE(config_noc_res),
+	.resource = config_noc_res,
+};
+
+static struct platform_device *msm_bus_9625_devices[] = {
+	&msm_bus_sys_noc,
+	&msm_bus_bimc,
+	&msm_bus_periph_noc,
+	&msm_bus_config_noc,
+};
+
+static void __init msm9625_init_buses(void)
+{
+#ifdef CONFIG_MSM_BUS_SCALING
+	msm_bus_sys_noc.dev.platform_data =
+		&msm_bus_9625_sys_noc_pdata;
+	msm_bus_bimc.dev.platform_data = &msm_bus_9625_bimc_pdata;
+	msm_bus_periph_noc.dev.platform_data = &msm_bus_9625_periph_noc_pdata;
+	msm_bus_config_noc.dev.platform_data = &msm_bus_9625_config_noc_pdata;
+#endif
+	platform_add_devices(msm_bus_9625_devices,
+				ARRAY_SIZE(msm_bus_9625_devices));
+}
+
 void __init msm9625_add_devices(void)
 {
 	platform_device_register(&msm_device_smd_9625);
@@ -317,6 +365,7 @@
 	rpm_regulator_smd_driver_init();
 	msm_spm_device_init();
 	msm_clock_init(&msm9625_clock_init_data);
+	msm9625_init_buses();
 }
 
 void __init msm9625_init(void)
@@ -331,9 +380,9 @@
 	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,
diff --git a/arch/arm/mach-msm/board-dt.c b/arch/arm/mach-msm/board-dt.c
index 15a544a..74b0d0d 100644
--- a/arch/arm/mach-msm/board-dt.c
+++ b/arch/arm/mach-msm/board-dt.c
@@ -17,12 +17,18 @@
 #include <linux/mfd/wcd9xxx/core.h>
 #include <asm/arch_timer.h>
 #include <asm/mach/time.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();
@@ -57,3 +63,15 @@
 	if (node)
 		of_mpm_init(node);
 }
+
+void __init msm_dt_init_irq_nompm(void)
+{
+	of_irq_init(irq_match);
+}
+
+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
index 31143a5..cc3e92c 100644
--- a/arch/arm/mach-msm/board-dt.h
+++ b/arch/arm/mach-msm/board-dt.h
@@ -12,3 +12,5 @@
 
 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 bcc9645..1c2d8a2 100644
--- a/arch/arm/mach-msm/board-msm7627a-bt.c
+++ b/arch/arm/mach-msm/board-msm7627a-bt.c
@@ -103,11 +103,12 @@
 	if (machine_is_msm7627a_qrd1())
 		gpio_bt_sys_rest_en = 114;
 	if (machine_is_msm7627a_evb() || machine_is_msm8625_evb()
-				|| machine_is_msm8625_evt()
-				|| machine_is_qrd_skud_prime())
+				|| machine_is_msm8625_evt())
 		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;
@@ -976,9 +977,6 @@
 	int i, rc = 0;
 	struct device *dev;
 
-	if (machine_is_qrd_skud_prime())
-		return;
-
 	gpio_bt_config();
 
 	rc = i2c_register_board_info(MSM_GSBI1_QUP_I2C_BUS_ID,
diff --git a/arch/arm/mach-msm/board-msm7627a-io.c b/arch/arm/mach-msm/board-msm7627a-io.c
index 6e3d10a..8c52456 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,
@@ -699,6 +727,15 @@
 	[KP_INDEX_SKU3(1, 1)] = KEY_CAMERA,
 };
 
+static unsigned int kp_row_gpios_skud[] = {31, 32};
+static unsigned int kp_col_gpios_skud[] = {37};
+
+static const unsigned short keymap_skud[] = {
+	[KP_INDEX_SKU3(0, 0)] = KEY_VOLUMEUP,
+	[KP_INDEX_SKU3(0, 1)] = KEY_VOLUMEDOWN,
+};
+
+
 static struct gpio_event_matrix_info kp_matrix_info_sku3 = {
 	.info.func      = gpio_event_matrix_func,
 	.keymap         = keymap_sku3,
@@ -845,7 +882,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();
 	}
 
@@ -858,13 +896,23 @@
 #endif
 
 	/* keypad */
+
+	if (machine_is_qrd_skud_prime()) {
+		kp_matrix_info_sku3.keymap = keymap_skud;
+		kp_matrix_info_sku3.output_gpios = kp_row_gpios_skud;
+		kp_matrix_info_sku3.input_gpios = kp_col_gpios_skud;
+		kp_matrix_info_sku3.noutputs = ARRAY_SIZE(kp_row_gpios_skud);
+		kp_matrix_info_sku3.ninputs = ARRAY_SIZE(kp_col_gpios_skud);
+	}
+
 	if (machine_is_msm8625_evt())
 		kp_matrix_info_8625.keymap = keymap_8625_evt;
 
 	if (machine_is_msm7627a_evb() || machine_is_msm8625_evb() ||
 			machine_is_msm8625_evt())
 		platform_device_register(&kp_pdev_8625);
-	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())
 		platform_device_register(&kp_pdev_sku3);
 
 	/* leds */
diff --git a/arch/arm/mach-msm/board-msm7x27a.c b/arch/arm/mach-msm/board-msm7x27a.c
index 13c4be2..9fd5218 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 = {
@@ -728,10 +767,7 @@
  * These heaps are listed in the order they will be allocated.
  * Don't swap the order unless you know what you are doing!
  */
-static struct ion_platform_data ion_pdata = {
-	.nr = MSM_ION_HEAP_NUM,
-	.has_outer_cache = 1,
-	.heaps = {
+struct ion_platform_heap msm7627a_heaps[] = {
 		{
 			.id	= ION_SYSTEM_HEAP_ID,
 			.type	= ION_HEAP_TYPE_SYSTEM,
@@ -763,7 +799,12 @@
 			.extra_data = (void *)&co_ion_pdata,
 		},
 #endif
-	}
+};
+
+static struct ion_platform_data ion_pdata = {
+	.nr = MSM_ION_HEAP_NUM,
+	.has_outer_cache = 1,
+	.heaps = msm7627a_heaps,
 };
 
 static struct platform_device ion_dev = {
diff --git a/arch/arm/mach-msm/board-msm7x30.c b/arch/arm/mach-msm/board-msm7x30.c
index ee13e04..9822aa9 100644
--- a/arch/arm/mach-msm/board-msm7x30.c
+++ b/arch/arm/mach-msm/board-msm7x30.c
@@ -7161,9 +7161,7 @@
  * These heaps are listed in the order they will be allocated.
  * Don't swap the order unless you know what you are doing!
  */
-static struct ion_platform_data ion_pdata = {
-	.nr = MSM_ION_HEAP_NUM,
-	.heaps = {
+struct ion_platform_heap msm7x30_heaps[] = {
 		{
 			.id	= ION_SYSTEM_HEAP_ID,
 			.type	= ION_HEAP_TYPE_SYSTEM,
@@ -7195,7 +7193,11 @@
 			.extra_data = (void *)&co_ion_pdata,
 		},
 #endif
-	}
+};
+
+static struct ion_platform_data ion_pdata = {
+	.nr = MSM_ION_HEAP_NUM,
+	.heaps = msm7x30_heaps,
 };
 
 static struct platform_device ion_dev = {
diff --git a/arch/arm/mach-msm/board-msm8x60.c b/arch/arm/mach-msm/board-msm8x60.c
index d831ad2..08e6a0d 100644
--- a/arch/arm/mach-msm/board-msm8x60.c
+++ b/arch/arm/mach-msm/board-msm8x60.c
@@ -5347,9 +5347,7 @@
  * to each other.
  * Don't swap the order unless you know what you are doing!
  */
-static struct ion_platform_data ion_pdata = {
-	.nr = MSM_ION_HEAP_NUM,
-	.heaps = {
+struct ion_platform_heap msm8x60_heaps [] = {
 		{
 			.id	= ION_SYSTEM_HEAP_ID,
 			.type	= ION_HEAP_TYPE_SYSTEM,
@@ -5424,7 +5422,11 @@
 			.extra_data = (void *)&co_ion_pdata,
 		},
 #endif
-	}
+};
+
+static struct ion_platform_data ion_pdata = {
+	.nr = MSM_ION_HEAP_NUM,
+	.heaps = msm8x60_heaps,
 };
 
 static struct platform_device ion_dev = {
diff --git a/arch/arm/mach-msm/board-qrd7627a.c b/arch/arm/mach-msm/board-qrd7627a.c
index fd322e9..47a3120 100644
--- a/arch/arm/mach-msm/board-qrd7627a.c
+++ b/arch/arm/mach-msm/board-qrd7627a.c
@@ -46,6 +46,7 @@
 #include <mach/usbdiag.h>
 #include <mach/msm_memtypes.h>
 #include <mach/msm_serial_hs.h>
+#include <mach/msm_serial_pdata.h>
 #include <mach/pmic.h>
 #include <mach/socinfo.h>
 #include <mach/vreg.h>
@@ -82,6 +83,10 @@
 	.id             = -1,
 };
 
+static struct msm_serial_platform_data msm_8625_uart1_pdata = {
+	.userid		= 10,
+};
+
 static struct msm_gpio qup_i2c_gpios_io[] = {
 	{ GPIO_CFG(60, 0, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_8MA),
 		"qup_scl" },
@@ -349,7 +354,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,
@@ -407,6 +413,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 = {
@@ -758,10 +802,7 @@
  * These heaps are listed in the order they will be allocated.
  * Don't swap the order unless you know what you are doing!
  */
-static struct ion_platform_data ion_pdata = {
-	.nr = MSM_ION_HEAP_NUM,
-	.has_outer_cache = 1,
-	.heaps = {
+struct ion_platform_heap qrd7627a_heaps[] = {
 		{
 			.id	= ION_SYSTEM_HEAP_ID,
 			.type	= ION_HEAP_TYPE_SYSTEM,
@@ -793,7 +834,12 @@
 			.extra_data = (void *)&co_ion_pdata,
 		},
 #endif
-	}
+};
+
+static struct ion_platform_data ion_pdata = {
+	.nr = MSM_ION_HEAP_NUM,
+	.has_outer_cache = 1,
+	.heaps = qrd7627a_heaps,
 };
 
 static struct platform_device ion_dev = {
@@ -955,6 +1001,7 @@
 	if (machine_is_msm8625_evb() || machine_is_msm8625_qrd7()
 				|| machine_is_msm8625_evt()
 				|| machine_is_qrd_skud_prime()) {
+		msm8625_device_uart1.dev.platform_data = &msm_8625_uart1_pdata;
 		platform_add_devices(msm8625_evb_devices,
 				ARRAY_SIZE(msm8625_evb_devices));
 		platform_add_devices(qrd3_devices,
diff --git a/arch/arm/mach-msm/clock-7x30.c b/arch/arm/mach-msm/clock-7x30.c
index e42fe65..3f59035 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
@@ -272,8 +279,8 @@
 	.en_mask = BIT(1),
 	.status_reg = PLL1_STATUS_BASE_REG,
 	.status_mask = BIT(16),
-	.parent = &tcxo_clk.c,
 	.c = {
+		.parent = &tcxo_clk.c,
 		.dbg_name = "pll1_clk",
 		.rate = 768000000,
 		.ops = &clk_ops_pll_vote,
@@ -286,8 +293,8 @@
 	.en_mask = BIT(2),
 	.status_reg = PLL2_STATUS_BASE_REG,
 	.status_mask = BIT(16),
-	.parent = &tcxo_clk.c,
 	.c = {
+		.parent = &tcxo_clk.c,
 		.dbg_name = "pll2_clk",
 		.rate = 806400000, /* TODO: Support scaling */
 		.ops = &clk_ops_pll_vote,
@@ -300,8 +307,8 @@
 	.en_mask = BIT(3),
 	.status_reg = PLL3_STATUS_BASE_REG,
 	.status_mask = BIT(16),
-	.parent = &lpxo_clk.c,
 	.c = {
+		.parent = &lpxo_clk.c,
 		.dbg_name = "pll3_clk",
 		.rate = 737280000,
 		.ops = &clk_ops_pll_vote,
@@ -314,8 +321,8 @@
 	.en_mask = BIT(4),
 	.status_reg = PLL4_STATUS_BASE_REG,
 	.status_mask = BIT(16),
-	.parent = &lpxo_clk.c,
 	.c = {
+		.parent = &lpxo_clk.c,
 		.dbg_name = "pll4_clk",
 		.rate = 891000000,
 		.ops = &clk_ops_pll_vote,
@@ -356,8 +363,8 @@
 		.halt_check = HALT_VOTED,
 		.halt_bit = 2,
 	},
-	.parent = &glbl_root_clk.c,
 	.c = {
+		.parent = &glbl_root_clk.c,
 		.dbg_name = "axi_li_apps_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(axi_li_apps_clk.c),
@@ -372,8 +379,8 @@
 		.halt_check = HALT_VOTED,
 		.halt_bit = 14,
 	},
-	.parent = &axi_li_apps_clk.c,
 	.c = {
+		.parent = &axi_li_apps_clk.c,
 		.dbg_name = "axi_li_adsp_a_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(axi_li_adsp_a_clk.c),
@@ -388,8 +395,8 @@
 		.halt_check = HALT_VOTED,
 		.halt_bit = 19,
 	},
-	.parent = &axi_li_apps_clk.c,
 	.c = {
+		.parent = &axi_li_apps_clk.c,
 		.dbg_name = "axi_li_jpeg_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(axi_li_jpeg_clk.c),
@@ -404,8 +411,8 @@
 		.halt_check = HALT_VOTED,
 		.halt_bit = 23,
 	},
-	.parent = &axi_li_apps_clk.c,
 	.c = {
+		.parent = &axi_li_apps_clk.c,
 		.dbg_name = "axi_li_vfe_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(axi_li_vfe_clk.c),
@@ -420,8 +427,8 @@
 		.halt_check = HALT_VOTED,
 		.halt_bit = 29,
 	},
-	.parent = &axi_li_apps_clk.c,
 	.c = {
+		.parent = &axi_li_apps_clk.c,
 		.dbg_name = "axi_mdp_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(axi_mdp_clk.c),
@@ -436,8 +443,8 @@
 		.halt_check = HALT_VOTED,
 		.halt_bit = 3,
 	},
-	.parent = &glbl_root_clk.c,
 	.c = {
+		.parent = &glbl_root_clk.c,
 		.dbg_name = "axi_li_vg_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(axi_li_vg_clk.c),
@@ -452,8 +459,8 @@
 		.halt_check = HALT_VOTED,
 		.halt_bit = 21,
 	},
-	.parent = &axi_li_vg_clk.c,
 	.c = {
+		.parent = &axi_li_vg_clk.c,
 		.dbg_name = "axi_grp_2d_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(axi_grp_2d_clk.c),
@@ -468,8 +475,8 @@
 		.halt_check = HALT_VOTED,
 		.halt_bit = 22,
 	},
-	.parent = &axi_li_vg_clk.c,
 	.c = {
+		.parent = &axi_li_vg_clk.c,
 		.dbg_name = "axi_li_grp_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(axi_li_grp_clk.c),
@@ -484,8 +491,8 @@
 		.halt_check = HALT_VOTED,
 		.halt_bit = 20,
 	},
-	.parent = &axi_li_vg_clk.c,
 	.c = {
+		.parent = &axi_li_vg_clk.c,
 		.dbg_name = "axi_mfc_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(axi_mfc_clk.c),
@@ -501,8 +508,8 @@
 		.halt_bit = 22,
 		.reset_mask = P_AXI_ROTATOR_CLK,
 	},
-	.parent = &axi_li_vg_clk.c,
 	.c = {
+		.parent = &axi_li_vg_clk.c,
 		.dbg_name = "axi_rotator_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(axi_rotator_clk.c),
@@ -517,8 +524,8 @@
 		.halt_check = HALT_VOTED,
 		.halt_bit = 21,
 	},
-	.parent = &axi_li_vg_clk.c,
 	.c = {
+		.parent = &axi_li_vg_clk.c,
 		.dbg_name = "axi_vpe_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(axi_vpe_clk.c),
@@ -535,8 +542,8 @@
 		.halt_bit = 5,
 		.reset_mask = P_ADM_CLK,
 	},
-	.parent = &axi_li_apps_clk.c,
 	.c = {
+		.parent = &axi_li_apps_clk.c,
 		.dbg_name = "adm_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(adm_clk.c),
@@ -551,8 +558,8 @@
 		.halt_check = HALT_VOTED,
 		.halt_bit = 15,
 	},
-	.parent = &glbl_root_clk.c,
 	.c = {
+		.parent = &glbl_root_clk.c,
 		.dbg_name = "adm_p_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(adm_p_clk.c),
@@ -568,8 +575,8 @@
 		.halt_bit = 6,
 		.reset_mask = P_CE_CLK,
 	},
-	.parent = &glbl_root_clk.c,
 	.c = {
+		.parent = &glbl_root_clk.c,
 		.dbg_name = "ce_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(ce_clk.c),
@@ -585,8 +592,8 @@
 		.halt_bit = 9,
 		.reset_mask = P_CAMIF_PAD_P_CLK,
 	},
-	.parent = &glbl_root_clk.c,
 	.c = {
+		.parent = &glbl_root_clk.c,
 		.dbg_name = "camif_pad_p_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(camif_pad_p_clk.c),
@@ -602,8 +609,8 @@
 		.halt_bit = 30,
 		.reset_mask = P_CSI0_P_CLK,
 	},
-	.parent = &glbl_root_clk.c,
 	.c = {
+		.parent = &glbl_root_clk.c,
 		.dbg_name = "csi0_p_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(csi0_p_clk.c),
@@ -619,8 +626,8 @@
 		.halt_bit = 3,
 		.reset_mask = P_EMDH_P_CLK,
 	},
-	.parent = &glbl_root_clk.c,
 	.c = {
+		.parent = &glbl_root_clk.c,
 		.dbg_name = "emdh_p_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(emdh_p_clk.c),
@@ -636,8 +643,8 @@
 		.halt_bit = 24,
 		.reset_mask = P_GRP_2D_P_CLK,
 	},
-	.parent = &glbl_root_clk.c,
 	.c = {
+		.parent = &glbl_root_clk.c,
 		.dbg_name = "grp_2d_p_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(grp_2d_p_clk.c),
@@ -653,8 +660,8 @@
 		.halt_bit = 17,
 		.reset_mask = P_GRP_3D_P_CLK,
 	},
-	.parent = &glbl_root_clk.c,
 	.c = {
+		.parent = &glbl_root_clk.c,
 		.dbg_name = "grp_3d_p_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(grp_3d_p_clk.c),
@@ -670,8 +677,8 @@
 		.halt_bit = 24,
 		.reset_mask = P_JPEG_P_CLK,
 	},
-	.parent = &glbl_root_clk.c,
 	.c = {
+		.parent = &glbl_root_clk.c,
 		.dbg_name = "jpeg_p_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(jpeg_p_clk.c),
@@ -687,8 +694,8 @@
 		.halt_bit = 7,
 		.reset_mask = P_LPA_P_CLK,
 	},
-	.parent = &glbl_root_clk.c,
 	.c = {
+		.parent = &glbl_root_clk.c,
 		.dbg_name = "lpa_p_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(lpa_p_clk.c),
@@ -704,8 +711,8 @@
 		.halt_bit = 6,
 		.reset_mask = P_MDP_P_CLK,
 	},
-	.parent = &glbl_root_clk.c,
 	.c = {
+		.parent = &glbl_root_clk.c,
 		.dbg_name = "mdp_p_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(mdp_p_clk.c),
@@ -721,8 +728,8 @@
 		.halt_bit = 26,
 		.reset_mask = P_MFC_P_CLK,
 	},
-	.parent = &glbl_root_clk.c,
 	.c = {
+		.parent = &glbl_root_clk.c,
 		.dbg_name = "mfc_p_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(mfc_p_clk.c),
@@ -738,8 +745,8 @@
 		.halt_bit = 4,
 		.reset_mask = P_PMDH_P_CLK,
 	},
-	.parent = &glbl_root_clk.c,
 	.c = {
+		.parent = &glbl_root_clk.c,
 		.dbg_name = "pmdh_p_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(pmdh_p_clk.c),
@@ -755,8 +762,8 @@
 		.halt_bit = 23,
 		.reset_mask = P_ROTATOR_IMEM_CLK,
 	},
-	.parent = &glbl_root_clk.c,
 	.c = {
+		.parent = &glbl_root_clk.c,
 		.dbg_name = "rotator_imem_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(rotator_imem_clk.c),
@@ -772,8 +779,8 @@
 		.halt_bit = 25,
 		.reset_mask = P_ROTATOR_P_CLK,
 	},
-	.parent = &glbl_root_clk.c,
 	.c = {
+		.parent = &glbl_root_clk.c,
 		.dbg_name = "rotator_p_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(rotator_p_clk.c),
@@ -789,8 +796,8 @@
 		.halt_bit = 7,
 		.reset_mask = P_SDC1_P_CLK,
 	},
-	.parent = &glbl_root_clk.c,
 	.c = {
+		.parent = &glbl_root_clk.c,
 		.dbg_name = "sdc1_p_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(sdc1_p_clk.c),
@@ -806,8 +813,8 @@
 		.halt_bit = 8,
 		.reset_mask = P_SDC2_P_CLK,
 	},
-	.parent = &glbl_root_clk.c,
 	.c = {
+		.parent = &glbl_root_clk.c,
 		.dbg_name = "sdc2_p_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(sdc2_p_clk.c),
@@ -823,8 +830,8 @@
 		.halt_bit = 27,
 		.reset_mask = P_SDC3_P_CLK,
 	},
-	.parent = &glbl_root_clk.c,
 	.c = {
+		.parent = &glbl_root_clk.c,
 		.dbg_name = "sdc3_p_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(sdc3_p_clk.c),
@@ -840,8 +847,8 @@
 		.halt_bit = 28,
 		.reset_mask = P_SDC4_P_CLK,
 	},
-	.parent = &glbl_root_clk.c,
 	.c = {
+		.parent = &glbl_root_clk.c,
 		.dbg_name = "sdc4_p_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(sdc4_p_clk.c),
@@ -857,8 +864,8 @@
 		.halt_bit = 10,
 		.reset_mask = P_SPI_P_CLK,
 	},
-	.parent = &glbl_root_clk.c,
 	.c = {
+		.parent = &glbl_root_clk.c,
 		.dbg_name = "spi_p_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(spi_p_clk.c),
@@ -874,8 +881,8 @@
 		.halt_bit = 18,
 		.reset_mask = P_TSIF_P_CLK,
 	},
-	.parent = &glbl_root_clk.c,
 	.c = {
+		.parent = &glbl_root_clk.c,
 		.dbg_name = "tsif_p_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(tsif_p_clk.c),
@@ -890,8 +897,8 @@
 		.halt_check = HALT_VOTED,
 		.halt_bit = 17,
 	},
-	.parent = &glbl_root_clk.c,
 	.c = {
+		.parent = &glbl_root_clk.c,
 		.dbg_name = "uart1dm_p_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(uart1dm_p_clk.c),
@@ -906,8 +913,8 @@
 		.halt_check = HALT_VOTED,
 		.halt_bit = 26,
 	},
-	.parent = &glbl_root_clk.c,
 	.c = {
+		.parent = &glbl_root_clk.c,
 		.dbg_name = "uart2dm_p_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(uart2dm_p_clk.c),
@@ -923,8 +930,8 @@
 		.halt_bit = 8,
 		.reset_mask = P_USB_HS2_P_CLK,
 	},
-	.parent = &glbl_root_clk.c,
 	.c = {
+		.parent = &glbl_root_clk.c,
 		.dbg_name = "usb_hs2_p_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(usb_hs2_p_clk.c),
@@ -940,8 +947,8 @@
 		.halt_bit = 9,
 		.reset_mask = P_USB_HS3_P_CLK,
 	},
-	.parent = &glbl_root_clk.c,
 	.c = {
+		.parent = &glbl_root_clk.c,
 		.dbg_name = "usb_hs3_p_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(usb_hs3_p_clk.c),
@@ -957,8 +964,8 @@
 		.halt_bit = 25,
 		.reset_mask = P_USB_HS_P_CLK,
 	},
-	.parent = &glbl_root_clk.c,
 	.c = {
+		.parent = &glbl_root_clk.c,
 		.dbg_name = "usb_hs_p_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(usb_hs_p_clk.c),
@@ -974,8 +981,8 @@
 		.halt_bit = 27,
 		.reset_mask = P_VFE_P_CLK,
 	},
-	.parent = &glbl_root_clk.c,
 	.c = {
+		.parent = &glbl_root_clk.c,
 		.dbg_name = "vfe_p_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(vfe_p_clk.c),
@@ -1313,8 +1320,8 @@
 		.halt_bit = 18,
 		.reset_mask = P_GRP_3D_CLK,
 	},
-	.parent = &grp_3d_src_clk.c,
 	.c = {
+		.parent = &grp_3d_src_clk.c,
 		.dbg_name = "grp_3d_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(grp_3d_clk.c),
@@ -1329,8 +1336,8 @@
 		.halt_bit = 19,
 		.reset_mask = P_IMEM_CLK,
 	},
-	.parent = &grp_3d_src_clk.c,
 	.c = {
+		.parent = &grp_3d_src_clk.c,
 		.dbg_name = "imem_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(imem_clk.c),
@@ -1535,8 +1542,8 @@
 		.halt_bit = 29,
 		.reset_mask = P_MDP_LCDC_PAD_PCLK_CLK,
 	},
-	.parent = &mdp_lcdc_pclk_clk.c,
 	.c = {
+		.parent = &mdp_lcdc_pclk_clk.c,
 		.dbg_name = "mdp_lcdc_pad_pclk_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(mdp_lcdc_pad_pclk_clk.c),
@@ -1609,8 +1616,8 @@
 		.halt_bit = 13,
 		.reset_mask = P_MI2S_CODEC_RX_S_CLK,
 	},
-	.parent = &mi2s_codec_rx_m_clk.c,
 	.c = {
+		.parent = &mi2s_codec_rx_m_clk.c,
 		.dbg_name = "mi2s_codec_rx_s_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(mi2s_codec_rx_s_clk.c),
@@ -1649,8 +1656,8 @@
 		.halt_bit = 11,
 		.reset_mask = P_MI2S_CODEC_TX_S_CLK,
 	},
-	.parent = &mi2s_codec_tx_m_clk.c,
 	.c = {
+		.parent = &mi2s_codec_tx_m_clk.c,
 		.dbg_name = "mi2s_codec_tx_s_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(mi2s_codec_tx_s_clk.c),
@@ -1695,8 +1702,8 @@
 		.halt_bit = 3,
 		.reset_mask = P_MI2S_S_CLK,
 	},
-	.parent = &mi2s_m_clk.c,
 	.c = {
+		.parent = &mi2s_m_clk.c,
 		.dbg_name = "mi2s_s_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(mi2s_s_clk.c),
@@ -1756,8 +1763,8 @@
 		.halt_bit = 17,
 		.reset_mask = P_SDAC_M_CLK,
 	},
-	.parent = &sdac_clk.c,
 	.c = {
+		.parent = &sdac_clk.c,
 		.dbg_name = "sdac_m_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(sdac_m_clk.c),
@@ -1800,8 +1807,8 @@
 		.halt_bit = 7,
 		.reset_mask = P_HDMI_CLK,
 	},
-	.parent = &tv_clk.c,
 	.c = {
+		.parent = &tv_clk.c,
 		.dbg_name = "hdmi_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(hdmi_clk.c),
@@ -1816,8 +1823,8 @@
 		.halt_bit = 27,
 		.reset_mask = P_TV_DAC_CLK,
 	},
-	.parent = &tv_clk.c,
 	.c = {
+		.parent = &tv_clk.c,
 		.dbg_name = "tv_dac_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(tv_dac_clk.c),
@@ -1832,8 +1839,8 @@
 		.halt_bit = 10,
 		.reset_mask = P_TV_ENC_CLK,
 	},
-	.parent = &tv_clk.c,
 	.c = {
+		.parent = &tv_clk.c,
 		.dbg_name = "tv_enc_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(tv_enc_clk.c),
@@ -1849,8 +1856,8 @@
 		.halt_bit = 11,
 		.reset_mask = P_TSIF_REF_CLK,
 	},
-	.parent = &tv_clk.c,
 	.c = {
+		.parent = &tv_clk.c,
 		.dbg_name = "tsif_ref_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(tsif_ref_clk.c),
@@ -1908,8 +1915,8 @@
 		.halt_bit = 27,
 		.reset_mask = P_USB_HS_CORE_CLK,
 	},
-	.parent = &usb_hs_src_clk.c,
 	.c = {
+		.parent = &usb_hs_src_clk.c,
 		.dbg_name = "usb_hs_core_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(usb_hs_core_clk.c),
@@ -1924,8 +1931,8 @@
 		.halt_bit = 3,
 		.reset_mask = P_USB_HS2_CLK,
 	},
-	.parent = &usb_hs_src_clk.c,
 	.c = {
+		.parent = &usb_hs_src_clk.c,
 		.dbg_name = "usb_hs2_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(usb_hs2_clk.c),
@@ -1940,8 +1947,8 @@
 		.halt_bit = 28,
 		.reset_mask = P_USB_HS2_CORE_CLK,
 	},
-	.parent = &usb_hs_src_clk.c,
 	.c = {
+		.parent = &usb_hs_src_clk.c,
 		.dbg_name = "usb_hs2_core_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(usb_hs2_core_clk.c),
@@ -1956,8 +1963,8 @@
 		.halt_bit = 2,
 		.reset_mask = P_USB_HS3_CLK,
 	},
-	.parent = &usb_hs_src_clk.c,
 	.c = {
+		.parent = &usb_hs_src_clk.c,
 		.dbg_name = "usb_hs3_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(usb_hs3_clk.c),
@@ -1972,8 +1979,8 @@
 		.halt_bit = 29,
 		.reset_mask = P_USB_HS3_CORE_CLK,
 	},
-	.parent = &usb_hs_src_clk.c,
 	.c = {
+		.parent = &usb_hs_src_clk.c,
 		.dbg_name = "usb_hs3_core_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(usb_hs3_core_clk.c),
@@ -2055,8 +2062,8 @@
 		.halt_bit = 9,
 		.reset_mask = P_VFE_MDC_CLK,
 	},
-	.parent = &vfe_clk.c,
 	.c = {
+		.parent = &vfe_clk.c,
 		.dbg_name = "vfe_mdc_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(vfe_mdc_clk.c),
@@ -2071,8 +2078,8 @@
 		.halt_bit = 13,
 		.reset_mask = P_VFE_CAMIF_CLK,
 	},
-	.parent = &vfe_clk.c,
 	.c = {
+		.parent = &vfe_clk.c,
 		.dbg_name = "vfe_camif_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(vfe_camif_clk.c),
@@ -2087,8 +2094,8 @@
 		.halt_bit = 16,
 		.reset_mask = P_CSI0_VFE_CLK,
 	},
-	.parent = &vfe_clk.c,
 	.c = {
+		.parent = &vfe_clk.c,
 		.dbg_name = "csi0_vfe_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(csi0_vfe_clk.c),
@@ -2212,8 +2219,8 @@
 		.halt_bit = 11,
 		.reset_mask = P_MFC_DIV2_CLK,
 	},
-	.parent = &mfc_clk.c,
 	.c = {
+		.parent = &mfc_clk.c,
 		.dbg_name = "mfc_div2_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(mfc_div2_clk.c),
diff --git a/arch/arm/mach-msm/clock-8960.c b/arch/arm/mach-msm/clock-8960.c
index adf1733..f6386b4 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,
@@ -513,8 +525,8 @@
 
 static struct pll_clk pll2_clk = {
 	.mode_reg = MM_PLL1_MODE_REG,
-	.parent = &pxo_clk.c,
 	.c = {
+		.parent = &pxo_clk.c,
 		.dbg_name = "pll2_clk",
 		.rate = 800000000,
 		.ops = &clk_ops_local_pll,
@@ -524,13 +536,16 @@
 
 static struct pll_clk pll3_clk = {
 	.mode_reg = BB_MMCC_PLL2_MODE_REG,
-	.parent = &pxo_clk.c,
 	.c = {
+		.parent = &pxo_clk.c,
 		.dbg_name = "pll3_clk",
 		.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),
 	},
 };
@@ -540,8 +555,8 @@
 	.en_mask = BIT(4),
 	.status_reg = LCC_PLL0_STATUS_REG,
 	.status_mask = BIT(16),
-	.parent = &pxo_clk.c,
 	.c = {
+		.parent = &pxo_clk.c,
 		.dbg_name = "pll4_clk",
 		.rate = 393216000,
 		.ops = &clk_ops_pll_vote,
@@ -554,8 +569,8 @@
 	.en_mask = BIT(8),
 	.status_reg = BB_PLL8_STATUS_REG,
 	.status_mask = BIT(16),
-	.parent = &pxo_clk.c,
 	.c = {
+		.parent = &pxo_clk.c,
 		.dbg_name = "pll8_clk",
 		.rate = 384000000,
 		.ops = &clk_ops_pll_vote,
@@ -568,8 +583,8 @@
 	.en_mask = BIT(14),
 	.status_reg = BB_PLL14_STATUS_REG,
 	.status_mask = BIT(16),
-	.parent = &pxo_clk.c,
 	.c = {
+		.parent = &pxo_clk.c,
 		.dbg_name = "pll14_clk",
 		.rate = 480000000,
 		.ops = &clk_ops_pll_vote,
@@ -579,8 +594,8 @@
 
 static struct pll_clk pll15_clk = {
 	.mode_reg = MM_PLL3_MODE_REG,
-	.parent = &pxo_clk.c,
 	.c = {
+		.parent = &pxo_clk.c,
 		.dbg_name = "pll15_clk",
 		.rate = 975000000,
 		.ops = &clk_ops_local_pll,
@@ -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,
 };
@@ -1688,8 +1701,8 @@
 		.halt_reg = CLK_HALT_CFPB_STATEA_REG,
 		.halt_bit = 24,
 	},
-	.parent = &usb_hsic_xcvr_fs_clk.c,
 	.c = {
+		.parent = &usb_hsic_xcvr_fs_clk.c,
 		.dbg_name = "usb_hsic_system_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(usb_hsic_system_clk.c),
@@ -1730,8 +1743,8 @@
 		.halt_reg = CLK_HALT_CFPB_STATEA_REG,
 		.halt_bit = 19,
 	},
-	.parent = &usb_hsic_hsic_src_clk.c,
 	.c = {
+		.parent = &usb_hsic_hsic_src_clk.c,
 		.dbg_name = "usb_hsic_hsic_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(usb_hsic_hsic_clk.c),
@@ -1810,8 +1823,8 @@
 		.halt_reg = CLK_HALT_CFPB_STATEA_REG,
 		.halt_bit = 15,
 	},
-	.parent = &usb_fs1_src_clk.c,
 	.c = {
+		.parent = &usb_fs1_src_clk.c,
 		.dbg_name = "usb_fs1_xcvr_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(usb_fs1_xcvr_clk.c),
@@ -1827,8 +1840,8 @@
 		.halt_reg = CLK_HALT_CFPB_STATEA_REG,
 		.halt_bit = 16,
 	},
-	.parent = &usb_fs1_src_clk.c,
 	.c = {
+		.parent = &usb_fs1_src_clk.c,
 		.dbg_name = "usb_fs1_sys_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(usb_fs1_sys_clk.c),
@@ -1845,8 +1858,8 @@
 		.halt_reg = CLK_HALT_CFPB_STATEA_REG,
 		.halt_bit = 12,
 	},
-	.parent = &usb_fs2_src_clk.c,
 	.c = {
+		.parent = &usb_fs2_src_clk.c,
 		.dbg_name = "usb_fs2_xcvr_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(usb_fs2_xcvr_clk.c),
@@ -1862,8 +1875,8 @@
 		.halt_reg = CLK_HALT_CFPB_STATEA_REG,
 		.halt_bit = 13,
 	},
-	.parent = &usb_fs2_src_clk.c,
 	.c = {
+		.parent = &usb_fs2_src_clk.c,
 		.dbg_name = "usb_fs2_sys_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(usb_fs2_sys_clk.c),
@@ -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,
 };
@@ -1949,8 +1962,8 @@
 		.halt_reg = CLK_HALT_MSS_SMPSS_MISC_STATE_REG,
 		.halt_bit = 5,
 	},
-	.parent = &ce3_src_clk.c,
 	.c = {
+		.parent = &ce3_src_clk.c,
 		.dbg_name = "ce3_core_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(ce3_core_clk.c),
@@ -1966,8 +1979,8 @@
 		.halt_reg = CLK_HALT_AFAB_SFAB_STATEB_REG,
 		.halt_bit = 16,
 	},
-	.parent = &ce3_src_clk.c,
 	.c = {
+		.parent = &ce3_src_clk.c,
 		.dbg_name = "ce3_p_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(ce3_p_clk.c),
@@ -2014,8 +2027,8 @@
 		.halt_reg = CLK_HALT_MSS_SMPSS_MISC_STATE_REG,
 		.halt_bit = 26,
 	},
-	.parent = &sata_src_clk.c,
 	.c = {
+		.parent = &sata_src_clk.c,
 		.dbg_name = "sata_rxoob_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(sata_rxoob_clk.c),
@@ -2029,8 +2042,8 @@
 		.halt_reg = CLK_HALT_MSS_SMPSS_MISC_STATE_REG,
 		.halt_bit = 25,
 	},
-	.parent = &sata_src_clk.c,
 	.c = {
+		.parent = &sata_src_clk.c,
 		.dbg_name = "sata_pmalive_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(sata_pmalive_clk.c),
@@ -2044,8 +2057,8 @@
 		.halt_reg = CLK_HALT_MSS_SMPSS_MISC_STATE_REG,
 		.halt_bit = 24,
 	},
-	.parent = &pxo_clk.c,
 	.c = {
+		.parent = &pxo_clk.c,
 		.dbg_name = "sata_phy_ref_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(sata_phy_ref_clk.c),
@@ -2737,8 +2750,8 @@
 		.halt_reg = DBG_BUS_VEC_B_REG,
 		.halt_bit = 13,
 	},
-	.parent = &csi0_src_clk.c,
 	.c = {
+		.parent = &csi0_src_clk.c,
 		.dbg_name = "csi0_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(csi0_clk.c),
@@ -2754,8 +2767,8 @@
 		.halt_reg = DBG_BUS_VEC_I_REG,
 		.halt_bit = 9,
 	},
-	.parent = &csi0_src_clk.c,
 	.c = {
+		.parent = &csi0_src_clk.c,
 		.dbg_name = "csi0_phy_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(csi0_phy_clk.c),
@@ -2793,8 +2806,8 @@
 		.halt_reg = DBG_BUS_VEC_B_REG,
 		.halt_bit = 14,
 	},
-	.parent = &csi1_src_clk.c,
 	.c = {
+		.parent = &csi1_src_clk.c,
 		.dbg_name = "csi1_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(csi1_clk.c),
@@ -2810,8 +2823,8 @@
 		.halt_reg = DBG_BUS_VEC_I_REG,
 		.halt_bit = 10,
 	},
-	.parent = &csi1_src_clk.c,
 	.c = {
+		.parent = &csi1_src_clk.c,
 		.dbg_name = "csi1_phy_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(csi1_phy_clk.c),
@@ -2849,8 +2862,8 @@
 		.halt_reg = DBG_BUS_VEC_B_REG,
 		.halt_bit = 29,
 	},
-	.parent = &csi2_src_clk.c,
 	.c = {
+		.parent = &csi2_src_clk.c,
 		.dbg_name = "csi2_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(csi2_clk.c),
@@ -2866,8 +2879,8 @@
 		.halt_reg = DBG_BUS_VEC_I_REG,
 		.halt_bit = 29,
 	},
-	.parent = &csi2_src_clk.c,
 	.c = {
+		.parent = &csi2_src_clk.c,
 		.dbg_name = "csi2_phy_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(csi2_phy_clk.c),
@@ -2964,6 +2977,7 @@
 	mb();
 	udelay(1);
 	rdi->cur_rate = rate;
+	c->parent = mux_map[rate];
 	spin_unlock(&local_clock_reg_lock);
 
 	if (rdi->enabled)
@@ -3025,11 +3039,6 @@
 	return branch_reset(&to_pix_rdi_clk(c)->b, action);
 }
 
-static struct clk *pix_rdi_clk_get_parent(struct clk *c)
-{
-	return pix_rdi_mux_map[to_pix_rdi_clk(c)->cur_rate];
-}
-
 static int pix_rdi_clk_list_rate(struct clk *c, unsigned n)
 {
 	if (pix_rdi_mux_map[n])
@@ -3051,6 +3060,7 @@
 	rdi->cur_rate = reg & rdi->s_mask ? 1 : 0;
 	reg = readl_relaxed(rdi->s2_reg);
 	rdi->cur_rate = reg & rdi->s2_mask ? 2 : rdi->cur_rate;
+	c->parent = pix_rdi_mux_map[rdi->cur_rate];
 
 	return HANDOFF_ENABLED_CLK;
 }
@@ -3065,7 +3075,6 @@
 	.get_rate = pix_rdi_clk_get_rate,
 	.list_rate = pix_rdi_clk_list_rate,
 	.reset = pix_rdi_clk_reset,
-	.get_parent = pix_rdi_clk_get_parent,
 };
 
 static struct pix_rdi_clk csi_pix_clk = {
@@ -3207,8 +3216,8 @@
 		.halt_reg = DBG_BUS_VEC_I_REG,
 		.halt_bit = 17,
 	},
-	.parent = &csiphy_timer_src_clk.c,
 	.c = {
+		.parent = &csiphy_timer_src_clk.c,
 		.dbg_name = "csi0phy_timer_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(csi0phy_timer_clk.c),
@@ -3222,8 +3231,8 @@
 		.halt_reg = DBG_BUS_VEC_I_REG,
 		.halt_bit = 18,
 	},
-	.parent = &csiphy_timer_src_clk.c,
 	.c = {
+		.parent = &csiphy_timer_src_clk.c,
 		.dbg_name = "csi1phy_timer_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(csi1phy_timer_clk.c),
@@ -3237,8 +3246,8 @@
 		.halt_reg = DBG_BUS_VEC_I_REG,
 		.halt_bit = 30,
 	},
-	.parent = &csiphy_timer_src_clk.c,
 	.c = {
+		.parent = &csiphy_timer_src_clk.c,
 		.dbg_name = "csi2phy_timer_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(csi2phy_timer_clk.c),
@@ -3497,24 +3506,26 @@
 		.ctl_val = CC_BANKED(9, 6, n), \
 	}
 
-static struct clk_freq_tbl clk_tbl_gfx3d_8960ab[] = {
-	F_GFX3D(        0, gnd,  0,  0),
-	F_GFX3D( 27000000, pxo,  0,  0),
-	F_GFX3D( 48000000, pll8, 1,  8),
-	F_GFX3D( 54857000, pll8, 1,  7),
-	F_GFX3D( 64000000, pll8, 1,  6),
-	F_GFX3D( 76800000, pll8, 1,  5),
-	F_GFX3D( 96000000, pll8, 1,  4),
-	F_GFX3D(128000000, pll8, 1,  3),
-	F_GFX3D(145455000, pll2, 2, 11),
-	F_GFX3D(160000000, pll2, 1,  5),
-	F_GFX3D(177778000, pll2, 2,  9),
-	F_GFX3D(200000000, pll2, 1,  4),
-	F_GFX3D(228571000, pll2, 2,  7),
-	F_GFX3D(266667000, pll2, 1,  3),
-	F_GFX3D(320000000, pll2, 2,  5),
-	F_GFX3D(325000000, pll3, 1,  2),
-	F_GFX3D(400000000, pll2, 1,  2),
+/*Shared by 8064, 8930, and 8960ab*/
+static struct clk_freq_tbl clk_tbl_gfx3d[] = {
+	F_GFX3D(        0, gnd,   0,  0),
+	F_GFX3D( 27000000, pxo,   0,  0),
+	F_GFX3D( 48000000, pll8,  1,  8),
+	F_GFX3D( 54857000, pll8,  1,  7),
+	F_GFX3D( 64000000, pll8,  1,  6),
+	F_GFX3D( 76800000, pll8,  1,  5),
+	F_GFX3D( 96000000, pll8,  1,  4),
+	F_GFX3D(128000000, pll8,  1,  3),
+	F_GFX3D(145455000, pll2,  2, 11),
+	F_GFX3D(160000000, pll2,  1,  5),
+	F_GFX3D(177778000, pll2,  2,  9),
+	F_GFX3D(192000000, pll8,  1,  2),
+	F_GFX3D(200000000, pll2,  1,  4),
+	F_GFX3D(228571000, pll2,  2,  7),
+	F_GFX3D(266667000, pll2,  1,  3),
+	F_GFX3D(320000000, pll2,  2,  5),
+	F_GFX3D(400000000, pll2,  1,  2),
+	F_GFX3D(450000000, pll15, 1,  2),
 	F_END
 };
 
@@ -3539,28 +3550,7 @@
 	F_END
 };
 
-static struct clk_freq_tbl clk_tbl_gfx3d_8064[] = {
-	F_GFX3D(        0, gnd,   0,  0),
-	F_GFX3D( 27000000, pxo,   0,  0),
-	F_GFX3D( 48000000, pll8,  1,  8),
-	F_GFX3D( 54857000, pll8,  1,  7),
-	F_GFX3D( 64000000, pll8,  1,  6),
-	F_GFX3D( 76800000, pll8,  1,  5),
-	F_GFX3D( 96000000, pll8,  1,  4),
-	F_GFX3D(128000000, pll8,  1,  3),
-	F_GFX3D(145455000, pll2,  2, 11),
-	F_GFX3D(160000000, pll2,  1,  5),
-	F_GFX3D(177778000, pll2,  2,  9),
-	F_GFX3D(192000000, pll8,  1,  2),
-	F_GFX3D(200000000, pll2,  1,  4),
-	F_GFX3D(228571000, pll2,  2,  7),
-	F_GFX3D(266667000, pll2,  1,  3),
-	F_GFX3D(400000000, pll2,  1,  2),
-	F_GFX3D(450000000, pll15, 1,  2),
-	F_END
-};
-
-static struct clk_freq_tbl clk_tbl_gfx3d_8930[] = {
+static struct clk_freq_tbl clk_tbl_gfx3d_8930ab[] = {
 	F_GFX3D(        0, gnd,   0,  0),
 	F_GFX3D( 27000000, pxo,   0,  0),
 	F_GFX3D( 48000000, pll8,  1,  8),
@@ -3578,34 +3568,40 @@
 	F_GFX3D(266667000, pll2,  1,  3),
 	F_GFX3D(320000000, pll2,  2,  5),
 	F_GFX3D(400000000, pll2,  1,  2),
-	F_GFX3D(450000000, pll15, 1,  2),
+	F_GFX3D(500000000, pll15, 1,  2),
 	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
 };
 
+static unsigned long fmax_gfx3d_8930ab[VDD_DIG_NUM] = {
+	[VDD_DIG_LOW]     = 192000000,
+	[VDD_DIG_NOMINAL] = 320000000,
+	[VDD_DIG_HIGH]    = 500000000
+};
+
 static struct bank_masks bmnd_info_gfx3d = {
 	.bank_sel_mask =		BIT(11),
 	.bank0_mask = {
@@ -3638,7 +3634,7 @@
 	.ns_reg = GFX3D_NS_REG,
 	.root_en_mask = BIT(2),
 	.set_rate = set_rate_mnd_banked,
-	.freq_tbl = clk_tbl_gfx3d_8960,
+	.freq_tbl = clk_tbl_gfx3d,
 	.bank_info = &bmnd_info_gfx3d,
 	.current_freq = &rcg_dummy_freq,
 	.c = {
@@ -3719,8 +3715,8 @@
 		.halt_reg = DBG_BUS_VEC_J_REG,
 		.halt_bit = 25,
 	},
-	.parent = &vcap_clk.c,
 	.c = {
+		.parent = &vcap_clk.c,
 		.dbg_name = "vcap_npl_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(vcap_npl_clk.c),
@@ -3752,7 +3748,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 +3875,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
 };
@@ -3937,8 +3933,8 @@
 		.retain_reg = MDP_LUT_CC_REG,
 		.retain_mask = BIT(31),
 	},
-	.parent = &mdp_clk.c,
 	.c = {
+		.parent = &mdp_clk.c,
 		.dbg_name = "lut_mdp_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(lut_mdp_clk.c),
@@ -4058,22 +4054,20 @@
 	spin_unlock_irqrestore(&local_clock_reg_lock, flags);
 }
 
-static struct clk *hdmi_pll_clk_get_parent(struct clk *c)
-{
-	return &pxo_clk.c;
-}
-
 static struct clk_ops clk_ops_hdmi_pll = {
 	.enable = hdmi_pll_clk_enable,
 	.disable = hdmi_pll_clk_disable,
-	.get_parent = hdmi_pll_clk_get_parent,
 };
 
 static struct clk hdmi_pll_clk = {
+	.parent = &pxo_clk.c,
 	.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 +4099,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
 };
@@ -4173,8 +4167,8 @@
 		.halt_reg = DBG_BUS_VEC_D_REG,
 		.halt_bit = 9,
 	},
-	.parent = &tv_src_clk.c,
 	.c = {
+		.parent = &tv_src_clk.c,
 		.dbg_name = "tv_enc_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(tv_enc_clk.c),
@@ -4188,8 +4182,8 @@
 		.halt_reg = DBG_BUS_VEC_D_REG,
 		.halt_bit = 10,
 	},
-	.parent = &tv_src_clk.c,
 	.c = {
+		.parent = &tv_src_clk.c,
 		.dbg_name = "tv_dac_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(tv_dac_clk.c),
@@ -4207,8 +4201,8 @@
 		.retain_reg = TV_CC2_REG,
 		.retain_mask = BIT(10),
 	},
-	.parent = &tv_src_clk.c,
 	.c = {
+		.parent = &tv_src_clk.c,
 		.dbg_name = "mdp_tv_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(mdp_tv_clk.c),
@@ -4224,8 +4218,8 @@
 		.halt_reg = DBG_BUS_VEC_D_REG,
 		.halt_bit = 11,
 	},
-	.parent = &tv_src_clk.c,
 	.c = {
+		.parent = &tv_src_clk.c,
 		.dbg_name = "hdmi_tv_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(hdmi_tv_clk.c),
@@ -4239,8 +4233,8 @@
 		.halt_reg = DBG_BUS_VEC_J_REG,
 		.halt_bit = 27,
 	},
-	.parent = &tv_src_clk.c,
 	.c = {
+		.parent = &tv_src_clk.c,
 		.dbg_name = "rgb_tv_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(rgb_tv_clk.c),
@@ -4254,8 +4248,8 @@
 		.halt_reg = DBG_BUS_VEC_J_REG,
 		.halt_bit = 26,
 	},
-	.parent = &tv_src_clk.c,
 	.c = {
+		.parent = &tv_src_clk.c,
 		.dbg_name = "npl_tv_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(npl_tv_clk.c),
@@ -4344,12 +4338,18 @@
 	},
 };
 
-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,
 };
 
+static unsigned long fmax_vcodec_8930ab[VDD_DIG_NUM] = {
+	[VDD_DIG_LOW]     = 100000000,
+	[VDD_DIG_NOMINAL] = 200000000,
+	[VDD_DIG_HIGH]    = 266670000
+};
+
 #define F_VPE(f, s, d) \
 	{ \
 		.freq_hz = f, \
@@ -4426,7 +4426,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
@@ -4471,8 +4471,8 @@
 		.halt_reg = DBG_BUS_VEC_B_REG,
 		.halt_bit = 8,
 	},
-	.parent = &vfe_clk.c,
 	.c = {
+		.parent = &vfe_clk.c,
 		.dbg_name = "csi_vfe_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(csi_vfe_clk.c),
@@ -4744,8 +4744,8 @@
 		.halt_check = ENABLE,
 		.halt_bit = 1,
 	},
-	.parent = &audio_slimbus_clk.c,
 	.c = {
+		.parent = &audio_slimbus_clk.c,
 		.dbg_name = "sps_slimbus_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(sps_slimbus_clk.c),
@@ -4759,8 +4759,8 @@
 		.halt_reg = CLK_HALT_DFAB_STATE_REG,
 		.halt_bit = 28,
 	},
-	.parent = &sps_slimbus_clk.c,
 	.c = {
+		.parent = &sps_slimbus_clk.c,
 		.dbg_name = "slimbus_xo_src_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(slimbus_xo_src_clk.c),
@@ -5282,7 +5282,7 @@
 	CLK_LOOKUP("core_clk",		gsbi2_uart_clk.c,	""),
 	CLK_LOOKUP("core_clk",		gsbi3_uart_clk.c,	""),
 	CLK_LOOKUP("core_clk",		gsbi4_uart_clk.c,	""),
-	CLK_LOOKUP("core_clk",		gsbi5_uart_clk.c,	""),
+	CLK_LOOKUP("core_clk",		gsbi5_uart_clk.c, "msm_serial_hsl.2"),
 	CLK_LOOKUP("core_clk",		gsbi6_uart_clk.c, "msm_serial_hs.0"),
 	CLK_LOOKUP("core_clk",		gsbi7_uart_clk.c, "msm_serial_hsl.0"),
 	CLK_LOOKUP("core_clk",		gsbi1_qup_clk.c,	"qup_i2c.0"),
@@ -5291,7 +5291,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"),
@@ -5331,9 +5331,11 @@
 	CLK_LOOKUP("iface_clk",		gsbi2_p_clk.c,		""),
 	CLK_LOOKUP("iface_clk",		gsbi3_p_clk.c,		"qup_i2c.3"),
 	CLK_LOOKUP("iface_clk",		gsbi4_p_clk.c,		"qup_i2c.4"),
+	CLK_LOOKUP("iface_clk",		gsbi5_p_clk.c,	"msm_serial_hsl.2"),
 	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"),
@@ -6318,12 +6320,12 @@
 	 */
 	/*
 	 * Initialize MM AHB registers: Enable the FPB clock and disable HW
-	 * gating on 8627 and 8960 for all clocks. Also set VFE_AHB's
+	 * gating on 8627 and 8930ab for all clocks. Also set VFE_AHB's
 	 * FORCE_CORE_ON bit to prevent its memory from being collapsed when
 	 * the clock is halted. The sleep and wake-up delays are set to safe
 	 * values.
 	 */
-	if (cpu_is_msm8627() || cpu_is_msm8960ab()) {
+	if (cpu_is_msm8627() || cpu_is_msm8930ab()) {
 		rmwreg(0x00000003, AHB_EN_REG,  0x6C000103);
 		writel_relaxed(0x000007F9, AHB_EN2_REG);
 	} else {
@@ -6332,7 +6334,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);
@@ -6341,9 +6343,9 @@
 	/* Initialize MM AXI registers: Enable HW gating for all clocks that
 	 * support it. Also set FORCE_CORE_ON bits, and any sleep and wake-up
 	 * delays to safe values. */
-	if (cpu_is_msm8960ab() || (cpu_is_msm8960() &&
+	if ((cpu_is_msm8960() &&
 			SOCINFO_VERSION_MAJOR(socinfo_get_version()) < 3) ||
-			cpu_is_msm8627()) {
+			cpu_is_msm8627() || cpu_is_msm8930ab()) {
 		rmwreg(0x000007F9, MAXI_EN_REG,  0x0803FFFF);
 		rmwreg(0x3027FCFF, MAXI_EN2_REG, 0x3A3FFFFF);
 	} else {
@@ -6356,15 +6358,14 @@
 
 	if (cpu_is_apq8064() || cpu_is_apq8064ab())
 		rmwreg(0x019FECFF, MAXI_EN5_REG, 0x01FFEFFF);
-	if (cpu_is_msm8930() || cpu_is_msm8930aa() || cpu_is_msm8627())
+	if (cpu_is_msm8930() || cpu_is_msm8930aa() || cpu_is_msm8627() ||
+	    cpu_is_msm8930ab())
 		rmwreg(0x000004FF, MAXI_EN5_REG, 0x00000FFF);
 	if (cpu_is_msm8960ab())
 		rmwreg(0x009FE000, MAXI_EN5_REG, 0x01FFE000);
 
-	if (cpu_is_msm8627())
+	if (cpu_is_msm8627() || cpu_is_msm8930ab())
 		rmwreg(0x000003C7, SAXI_EN_REG,  0x00003FFF);
-	else if (cpu_is_msm8960ab())
-		rmwreg(0x000001C6, SAXI_EN_REG,  0x00001DF6);
 	else
 		rmwreg(0x00003C38, SAXI_EN_REG,  0x00003FFF);
 
@@ -6402,7 +6403,7 @@
 		rmwreg(0x00000001, DSI2_PIXEL_CC2_REG, 0x00000001);
 
 	if (cpu_is_msm8960() || cpu_is_msm8930() || cpu_is_msm8930aa() ||
-	    cpu_is_msm8627())
+	    cpu_is_msm8627() || cpu_is_msm8930ab())
 		rmwreg(0x80FF0000, TV_CC_REG,        0xE1FFC010);
 	if (cpu_is_msm8960ab())
 		rmwreg(0x00000000, TV_CC_REG,        0x00004010);
@@ -6502,8 +6503,7 @@
 	}
 
 	/*
-	 * Program PLL15 to 900MHz with ref clk = 27MHz and
-	 * only enable PLL main output.
+	 * Change PLL15 configuration based on the SoC we're running on.
 	 */
 	if (cpu_is_msm8930() || cpu_is_msm8930aa() || cpu_is_msm8627()) {
 		pll15_config.l = 0x21 | BVAL(31, 7, 0x600);
@@ -6512,6 +6512,13 @@
 		configure_sr_pll(&pll15_config, &pll15_regs, 0);
 		/* Disable AUX and BIST outputs */
 		writel_relaxed(0, MM_PLL3_TEST_CTL_REG);
+	} else if (cpu_is_msm8930ab()) {
+		pll15_config.l = 0x25 | BVAL(31, 7, 0x600);
+		pll15_config.m = 0x25;
+		pll15_config.n = 0x3E7;
+		configure_sr_pll(&pll15_config, &pll15_regs, 0);
+		/* Disable AUX and BIST outputs */
+		writel_relaxed(0, MM_PLL3_TEST_CTL_REG);
 	}
 }
 
@@ -6541,7 +6548,6 @@
 			sizeof(msm_clocks_8960_common));
 	if (cpu_is_msm8960ab()) {
 		pll3_clk.c.rate = 650000000;
-		gfx3d_clk.freq_tbl = clk_tbl_gfx3d_8960ab;
 		gfx3d_clk.c.fmax[VDD_DIG_LOW] = 192000000;
 		gfx3d_clk.c.fmax[VDD_DIG_NOMINAL] = 325000000;
 		gfx3d_clk.c.fmax[VDD_DIG_HIGH] = 400000000;
@@ -6556,6 +6562,7 @@
 
 		gmem_axi_clk.c.depends = &gfx3d_axi_clk.c;
 	} else if (cpu_is_msm8960()) {
+		gfx3d_clk.freq_tbl = clk_tbl_gfx3d_8960;
 		memcpy(msm_clocks_8960 + ARRAY_SIZE(msm_clocks_8960_common),
 			 msm_clocks_8960_only, sizeof(msm_clocks_8960_only));
 		msm8960_clock_init_data.size -=
@@ -6566,38 +6573,24 @@
 	 * clocks which differ between chips.
 	 */
 	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;
 	}
 
@@ -6605,17 +6598,19 @@
 	 * Change the freq tables and voltage requirements for
 	 * 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));
-	} else if (cpu_is_msm8930aa()) {
-		memcpy(gfx3d_clk.c.fmax, fmax_gfx3d_8930aa,
-		       sizeof(gfx3d_clk.c.fmax));
-	}
+	if (cpu_is_msm8930() || cpu_is_msm8627())
+		gfx3d_clk.c.fmax = fmax_gfx3d_8930;
+	else if (cpu_is_msm8930aa())
+		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;
 		pll15_clk.c.rate = 900000000;
 		gmem_axi_clk.c.depends = &gfx3d_axi_clk_8930.c;
+	} else if (cpu_is_msm8930ab()) {
+		gfx3d_clk.freq_tbl = clk_tbl_gfx3d_8930ab;
+		pll15_clk.c.rate = 1000000000;
+		gfx3d_clk.c.fmax = fmax_gfx3d_8930ab;
+		gmem_axi_clk.c.depends = &gfx3d_axi_clk_8930.c;
+		vcodec_clk.c.fmax = fmax_vcodec_8930ab;
 	}
 	if ((readl_relaxed(PRNG_CLK_NS_REG) & 0x7F) == 0x2B)
 		prng_clk.freq_tbl = clk_tbl_prng_64;
@@ -6670,7 +6665,7 @@
 	}
 	clk_set_rate(&usb_fs1_src_clk.c, 60000000);
 	if (cpu_is_msm8960ab() || cpu_is_msm8960() || cpu_is_msm8930() ||
-		cpu_is_msm8930aa() || cpu_is_msm8627())
+		cpu_is_msm8930aa() || cpu_is_msm8627() || cpu_is_msm8930ab())
 		clk_set_rate(&usb_fs2_src_clk.c, 60000000);
 	clk_set_rate(&usb_hsic_xcvr_fs_clk.c, 60000000);
 	clk_set_rate(&usb_hsic_hsic_src_clk.c, 480000000);
diff --git a/arch/arm/mach-msm/clock-8974.c b/arch/arm/mach-msm/clock-8974.c
index 246ceaf..706b036 100644
--- a/arch/arm/mach-msm/clock-8974.c
+++ b/arch/arm/mach-msm/clock-8974.c
@@ -522,6 +522,14 @@
 #define dsipll0_pixel_mm_source_val 1
 #define hdmipll_mm_source_val 3
 
+#define F_GCC_GND \
+	{ \
+		.freq_hz = 0, \
+		.m_val = 0, \
+		.n_val  = 0, \
+		.div_src_val = BVAL(4, 0, 1) | BVAL(10, 8, gnd_source_val), \
+	}
+
 #define F(f, s, div, m, n) \
 	{ \
 		.freq_hz = (f), \
@@ -588,23 +596,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 +640,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
@@ -684,9 +702,9 @@
 	.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,
 	.base = &virt_bases[GCC_BASE],
 	.c = {
+		.parent = &cxo_clk_src.c,
 		.rate = 600000000,
 		.dbg_name = "gpll0_clk_src",
 		.ops = &clk_ops_pll_vote,
@@ -699,9 +717,9 @@
 	.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 = {
+		.parent = &cxo_clk_src.c,
 		.rate = 480000000,
 		.dbg_name = "gpll1_clk_src",
 		.ops = &clk_ops_pll_vote,
@@ -714,9 +732,9 @@
 	.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 = {
+		.parent = &cxo_clk_src.c,
 		.rate = 491520000,
 		.dbg_name = "lpapll0_clk_src",
 		.ops = &clk_ops_pll_vote,
@@ -729,9 +747,9 @@
 	.en_mask = BIT(0),
 	.status_reg = (void __iomem *)MMPLL0_STATUS_REG,
 	.status_mask = BIT(17),
-	.parent = &cxo_clk_src.c,
 	.base = &virt_bases[MMSS_BASE],
 	.c = {
+		.parent = &cxo_clk_src.c,
 		.dbg_name = "mmpll0_clk_src",
 		.rate = 800000000,
 		.ops = &clk_ops_pll_vote,
@@ -744,9 +762,9 @@
 	.en_mask = BIT(1),
 	.status_reg = (void __iomem *)MMPLL1_STATUS_REG,
 	.status_mask = BIT(17),
-	.parent = &cxo_clk_src.c,
 	.base = &virt_bases[MMSS_BASE],
 	.c = {
+		.parent = &cxo_clk_src.c,
 		.dbg_name = "mmpll1_clk_src",
 		.rate = 846000000,
 		.ops = &clk_ops_pll_vote,
@@ -757,9 +775,9 @@
 static struct pll_clk mmpll3_clk_src = {
 	.mode_reg = (void __iomem *)MMPLL3_MODE_REG,
 	.status_reg = (void __iomem *)MMPLL3_STATUS_REG,
-	.parent = &cxo_clk_src.c,
 	.base = &virt_bases[MMSS_BASE],
 	.c = {
+		.parent = &cxo_clk_src.c,
 		.dbg_name = "mmpll3_clk_src",
 		.rate = 1000000000,
 		.ops = &clk_ops_local_pll,
@@ -904,6 +922,7 @@
 };
 
 static struct clk_freq_tbl ftbl_gcc_blsp1_2_uart1_6_apps_clk[] = {
+	F_GCC_GND,
 	F( 3686400,  gpll0,    1,  96,  15625),
 	F( 7372800,  gpll0,    1, 192,  15625),
 	F(14745600,  gpll0,    1, 384,  15625),
@@ -1502,10 +1521,10 @@
 
 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 = {
+		.parent = &cxo_clk_src.c,
 		.dbg_name = "gcc_blsp1_qup1_i2c_apps_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(gcc_blsp1_qup1_i2c_apps_clk.c),
@@ -1514,9 +1533,9 @@
 
 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,
 	.base = &virt_bases[GCC_BASE],
 	.c = {
+		.parent = &blsp1_qup1_spi_apps_clk_src.c,
 		.dbg_name = "gcc_blsp1_qup1_spi_apps_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(gcc_blsp1_qup1_spi_apps_clk.c),
@@ -1525,10 +1544,10 @@
 
 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 = {
+		.parent = &cxo_clk_src.c,
 		.dbg_name = "gcc_blsp1_qup2_i2c_apps_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(gcc_blsp1_qup2_i2c_apps_clk.c),
@@ -1537,9 +1556,9 @@
 
 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,
 	.base = &virt_bases[GCC_BASE],
 	.c = {
+		.parent = &blsp1_qup2_spi_apps_clk_src.c,
 		.dbg_name = "gcc_blsp1_qup2_spi_apps_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(gcc_blsp1_qup2_spi_apps_clk.c),
@@ -1548,10 +1567,10 @@
 
 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 = {
+		.parent = &cxo_clk_src.c,
 		.dbg_name = "gcc_blsp1_qup3_i2c_apps_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(gcc_blsp1_qup3_i2c_apps_clk.c),
@@ -1560,9 +1579,9 @@
 
 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,
 	.base = &virt_bases[GCC_BASE],
 	.c = {
+		.parent = &blsp1_qup3_spi_apps_clk_src.c,
 		.dbg_name = "gcc_blsp1_qup3_spi_apps_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(gcc_blsp1_qup3_spi_apps_clk.c),
@@ -1571,10 +1590,10 @@
 
 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 = {
+		.parent = &cxo_clk_src.c,
 		.dbg_name = "gcc_blsp1_qup4_i2c_apps_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(gcc_blsp1_qup4_i2c_apps_clk.c),
@@ -1583,9 +1602,9 @@
 
 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,
 	.base = &virt_bases[GCC_BASE],
 	.c = {
+		.parent = &blsp1_qup4_spi_apps_clk_src.c,
 		.dbg_name = "gcc_blsp1_qup4_spi_apps_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(gcc_blsp1_qup4_spi_apps_clk.c),
@@ -1594,10 +1613,10 @@
 
 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 = {
+		.parent = &cxo_clk_src.c,
 		.dbg_name = "gcc_blsp1_qup5_i2c_apps_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(gcc_blsp1_qup5_i2c_apps_clk.c),
@@ -1606,9 +1625,9 @@
 
 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,
 	.base = &virt_bases[GCC_BASE],
 	.c = {
+		.parent = &blsp1_qup5_spi_apps_clk_src.c,
 		.dbg_name = "gcc_blsp1_qup5_spi_apps_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(gcc_blsp1_qup5_spi_apps_clk.c),
@@ -1617,10 +1636,10 @@
 
 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 = {
+		.parent = &cxo_clk_src.c,
 		.dbg_name = "gcc_blsp1_qup6_i2c_apps_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(gcc_blsp1_qup6_i2c_apps_clk.c),
@@ -1629,9 +1648,9 @@
 
 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,
 	.base = &virt_bases[GCC_BASE],
 	.c = {
+		.parent = &blsp1_qup6_spi_apps_clk_src.c,
 		.dbg_name = "gcc_blsp1_qup6_spi_apps_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(gcc_blsp1_qup6_spi_apps_clk.c),
@@ -1640,9 +1659,9 @@
 
 static struct branch_clk gcc_blsp1_uart1_apps_clk = {
 	.cbcr_reg = BLSP1_UART1_APPS_CBCR,
-	.parent = &blsp1_uart1_apps_clk_src.c,
 	.base = &virt_bases[GCC_BASE],
 	.c = {
+		.parent = &blsp1_uart1_apps_clk_src.c,
 		.dbg_name = "gcc_blsp1_uart1_apps_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(gcc_blsp1_uart1_apps_clk.c),
@@ -1651,9 +1670,9 @@
 
 static struct branch_clk gcc_blsp1_uart2_apps_clk = {
 	.cbcr_reg = BLSP1_UART2_APPS_CBCR,
-	.parent = &blsp1_uart2_apps_clk_src.c,
 	.base = &virt_bases[GCC_BASE],
 	.c = {
+		.parent = &blsp1_uart2_apps_clk_src.c,
 		.dbg_name = "gcc_blsp1_uart2_apps_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(gcc_blsp1_uart2_apps_clk.c),
@@ -1662,9 +1681,9 @@
 
 static struct branch_clk gcc_blsp1_uart3_apps_clk = {
 	.cbcr_reg = BLSP1_UART3_APPS_CBCR,
-	.parent = &blsp1_uart3_apps_clk_src.c,
 	.base = &virt_bases[GCC_BASE],
 	.c = {
+		.parent = &blsp1_uart3_apps_clk_src.c,
 		.dbg_name = "gcc_blsp1_uart3_apps_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(gcc_blsp1_uart3_apps_clk.c),
@@ -1673,9 +1692,9 @@
 
 static struct branch_clk gcc_blsp1_uart4_apps_clk = {
 	.cbcr_reg = BLSP1_UART4_APPS_CBCR,
-	.parent = &blsp1_uart4_apps_clk_src.c,
 	.base = &virt_bases[GCC_BASE],
 	.c = {
+		.parent = &blsp1_uart4_apps_clk_src.c,
 		.dbg_name = "gcc_blsp1_uart4_apps_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(gcc_blsp1_uart4_apps_clk.c),
@@ -1684,9 +1703,9 @@
 
 static struct branch_clk gcc_blsp1_uart5_apps_clk = {
 	.cbcr_reg = BLSP1_UART5_APPS_CBCR,
-	.parent = &blsp1_uart5_apps_clk_src.c,
 	.base = &virt_bases[GCC_BASE],
 	.c = {
+		.parent = &blsp1_uart5_apps_clk_src.c,
 		.dbg_name = "gcc_blsp1_uart5_apps_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(gcc_blsp1_uart5_apps_clk.c),
@@ -1695,9 +1714,9 @@
 
 static struct branch_clk gcc_blsp1_uart6_apps_clk = {
 	.cbcr_reg = BLSP1_UART6_APPS_CBCR,
-	.parent = &blsp1_uart6_apps_clk_src.c,
 	.base = &virt_bases[GCC_BASE],
 	.c = {
+		.parent = &blsp1_uart6_apps_clk_src.c,
 		.dbg_name = "gcc_blsp1_uart6_apps_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(gcc_blsp1_uart6_apps_clk.c),
@@ -1730,10 +1749,10 @@
 
 static struct branch_clk gcc_blsp2_qup1_i2c_apps_clk = {
 	.cbcr_reg = BLSP2_QUP1_I2C_APPS_CBCR,
-	.parent = &cxo_clk_src.c,
 	.has_sibling = 1,
 	.base = &virt_bases[GCC_BASE],
 	.c = {
+		.parent = &cxo_clk_src.c,
 		.dbg_name = "gcc_blsp2_qup1_i2c_apps_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(gcc_blsp2_qup1_i2c_apps_clk.c),
@@ -1742,9 +1761,9 @@
 
 static struct branch_clk gcc_blsp2_qup1_spi_apps_clk = {
 	.cbcr_reg = BLSP2_QUP1_SPI_APPS_CBCR,
-	.parent = &blsp2_qup1_spi_apps_clk_src.c,
 	.base = &virt_bases[GCC_BASE],
 	.c = {
+		.parent = &blsp2_qup1_spi_apps_clk_src.c,
 		.dbg_name = "gcc_blsp2_qup1_spi_apps_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(gcc_blsp2_qup1_spi_apps_clk.c),
@@ -1753,10 +1772,10 @@
 
 static struct branch_clk gcc_blsp2_qup2_i2c_apps_clk = {
 	.cbcr_reg = BLSP2_QUP2_I2C_APPS_CBCR,
-	.parent = &cxo_clk_src.c,
 	.has_sibling = 1,
 	.base = &virt_bases[GCC_BASE],
 	.c = {
+		.parent = &cxo_clk_src.c,
 		.dbg_name = "gcc_blsp2_qup2_i2c_apps_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(gcc_blsp2_qup2_i2c_apps_clk.c),
@@ -1765,9 +1784,9 @@
 
 static struct branch_clk gcc_blsp2_qup2_spi_apps_clk = {
 	.cbcr_reg = BLSP2_QUP2_SPI_APPS_CBCR,
-	.parent = &blsp2_qup2_spi_apps_clk_src.c,
 	.base = &virt_bases[GCC_BASE],
 	.c = {
+		.parent = &blsp2_qup2_spi_apps_clk_src.c,
 		.dbg_name = "gcc_blsp2_qup2_spi_apps_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(gcc_blsp2_qup2_spi_apps_clk.c),
@@ -1776,10 +1795,10 @@
 
 static struct branch_clk gcc_blsp2_qup3_i2c_apps_clk = {
 	.cbcr_reg = BLSP2_QUP3_I2C_APPS_CBCR,
-	.parent = &cxo_clk_src.c,
 	.has_sibling = 1,
 	.base = &virt_bases[GCC_BASE],
 	.c = {
+		.parent = &cxo_clk_src.c,
 		.dbg_name = "gcc_blsp2_qup3_i2c_apps_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(gcc_blsp2_qup3_i2c_apps_clk.c),
@@ -1788,9 +1807,9 @@
 
 static struct branch_clk gcc_blsp2_qup3_spi_apps_clk = {
 	.cbcr_reg = BLSP2_QUP3_SPI_APPS_CBCR,
-	.parent = &blsp2_qup3_spi_apps_clk_src.c,
 	.base = &virt_bases[GCC_BASE],
 	.c = {
+		.parent = &blsp2_qup3_spi_apps_clk_src.c,
 		.dbg_name = "gcc_blsp2_qup3_spi_apps_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(gcc_blsp2_qup3_spi_apps_clk.c),
@@ -1799,10 +1818,10 @@
 
 static struct branch_clk gcc_blsp2_qup4_i2c_apps_clk = {
 	.cbcr_reg = BLSP2_QUP4_I2C_APPS_CBCR,
-	.parent = &cxo_clk_src.c,
 	.has_sibling = 1,
 	.base = &virt_bases[GCC_BASE],
 	.c = {
+		.parent = &cxo_clk_src.c,
 		.dbg_name = "gcc_blsp2_qup4_i2c_apps_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(gcc_blsp2_qup4_i2c_apps_clk.c),
@@ -1811,9 +1830,9 @@
 
 static struct branch_clk gcc_blsp2_qup4_spi_apps_clk = {
 	.cbcr_reg = BLSP2_QUP4_SPI_APPS_CBCR,
-	.parent = &blsp2_qup4_spi_apps_clk_src.c,
 	.base = &virt_bases[GCC_BASE],
 	.c = {
+		.parent = &blsp2_qup4_spi_apps_clk_src.c,
 		.dbg_name = "gcc_blsp2_qup4_spi_apps_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(gcc_blsp2_qup4_spi_apps_clk.c),
@@ -1822,10 +1841,10 @@
 
 static struct branch_clk gcc_blsp2_qup5_i2c_apps_clk = {
 	.cbcr_reg = BLSP2_QUP5_I2C_APPS_CBCR,
-	.parent = &cxo_clk_src.c,
 	.has_sibling = 1,
 	.base = &virt_bases[GCC_BASE],
 	.c = {
+		.parent = &cxo_clk_src.c,
 		.dbg_name = "gcc_blsp2_qup5_i2c_apps_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(gcc_blsp2_qup5_i2c_apps_clk.c),
@@ -1834,9 +1853,9 @@
 
 static struct branch_clk gcc_blsp2_qup5_spi_apps_clk = {
 	.cbcr_reg = BLSP2_QUP5_SPI_APPS_CBCR,
-	.parent = &blsp2_qup5_spi_apps_clk_src.c,
 	.base = &virt_bases[GCC_BASE],
 	.c = {
+		.parent = &blsp2_qup5_spi_apps_clk_src.c,
 		.dbg_name = "gcc_blsp2_qup5_spi_apps_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(gcc_blsp2_qup5_spi_apps_clk.c),
@@ -1845,10 +1864,10 @@
 
 static struct branch_clk gcc_blsp2_qup6_i2c_apps_clk = {
 	.cbcr_reg = BLSP2_QUP6_I2C_APPS_CBCR,
-	.parent = &cxo_clk_src.c,
 	.has_sibling = 1,
 	.base = &virt_bases[GCC_BASE],
 	.c = {
+		.parent = &cxo_clk_src.c,
 		.dbg_name = "gcc_blsp2_qup6_i2c_apps_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(gcc_blsp2_qup6_i2c_apps_clk.c),
@@ -1857,9 +1876,9 @@
 
 static struct branch_clk gcc_blsp2_qup6_spi_apps_clk = {
 	.cbcr_reg = BLSP2_QUP6_SPI_APPS_CBCR,
-	.parent = &blsp2_qup6_spi_apps_clk_src.c,
 	.base = &virt_bases[GCC_BASE],
 	.c = {
+		.parent = &blsp2_qup6_spi_apps_clk_src.c,
 		.dbg_name = "gcc_blsp2_qup6_spi_apps_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(gcc_blsp2_qup6_spi_apps_clk.c),
@@ -1868,9 +1887,9 @@
 
 static struct branch_clk gcc_blsp2_uart1_apps_clk = {
 	.cbcr_reg = BLSP2_UART1_APPS_CBCR,
-	.parent = &blsp2_uart1_apps_clk_src.c,
 	.base = &virt_bases[GCC_BASE],
 	.c = {
+		.parent = &blsp2_uart1_apps_clk_src.c,
 		.dbg_name = "gcc_blsp2_uart1_apps_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(gcc_blsp2_uart1_apps_clk.c),
@@ -1879,9 +1898,9 @@
 
 static struct branch_clk gcc_blsp2_uart2_apps_clk = {
 	.cbcr_reg = BLSP2_UART2_APPS_CBCR,
-	.parent = &blsp2_uart2_apps_clk_src.c,
 	.base = &virt_bases[GCC_BASE],
 	.c = {
+		.parent = &blsp2_uart2_apps_clk_src.c,
 		.dbg_name = "gcc_blsp2_uart2_apps_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(gcc_blsp2_uart2_apps_clk.c),
@@ -1890,9 +1909,9 @@
 
 static struct branch_clk gcc_blsp2_uart3_apps_clk = {
 	.cbcr_reg = BLSP2_UART3_APPS_CBCR,
-	.parent = &blsp2_uart3_apps_clk_src.c,
 	.base = &virt_bases[GCC_BASE],
 	.c = {
+		.parent = &blsp2_uart3_apps_clk_src.c,
 		.dbg_name = "gcc_blsp2_uart3_apps_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(gcc_blsp2_uart3_apps_clk.c),
@@ -1901,9 +1920,9 @@
 
 static struct branch_clk gcc_blsp2_uart4_apps_clk = {
 	.cbcr_reg = BLSP2_UART4_APPS_CBCR,
-	.parent = &blsp2_uart4_apps_clk_src.c,
 	.base = &virt_bases[GCC_BASE],
 	.c = {
+		.parent = &blsp2_uart4_apps_clk_src.c,
 		.dbg_name = "gcc_blsp2_uart4_apps_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(gcc_blsp2_uart4_apps_clk.c),
@@ -1912,9 +1931,9 @@
 
 static struct branch_clk gcc_blsp2_uart5_apps_clk = {
 	.cbcr_reg = BLSP2_UART5_APPS_CBCR,
-	.parent = &blsp2_uart5_apps_clk_src.c,
 	.base = &virt_bases[GCC_BASE],
 	.c = {
+		.parent = &blsp2_uart5_apps_clk_src.c,
 		.dbg_name = "gcc_blsp2_uart5_apps_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(gcc_blsp2_uart5_apps_clk.c),
@@ -1923,9 +1942,9 @@
 
 static struct branch_clk gcc_blsp2_uart6_apps_clk = {
 	.cbcr_reg = BLSP2_UART6_APPS_CBCR,
-	.parent = &blsp2_uart6_apps_clk_src.c,
 	.base = &virt_bases[GCC_BASE],
 	.c = {
+		.parent = &blsp2_uart6_apps_clk_src.c,
 		.dbg_name = "gcc_blsp2_uart6_apps_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(gcc_blsp2_uart6_apps_clk.c),
@@ -2006,9 +2025,9 @@
 
 static struct branch_clk gcc_gp1_clk = {
 	.cbcr_reg = GP1_CBCR,
-	.parent = &gp1_clk_src.c,
 	.base = &virt_bases[GCC_BASE],
 	.c = {
+		.parent = &gp1_clk_src.c,
 		.dbg_name = "gcc_gp1_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(gcc_gp1_clk.c),
@@ -2017,9 +2036,9 @@
 
 static struct branch_clk gcc_gp2_clk = {
 	.cbcr_reg = GP2_CBCR,
-	.parent = &gp2_clk_src.c,
 	.base = &virt_bases[GCC_BASE],
 	.c = {
+		.parent = &gp2_clk_src.c,
 		.dbg_name = "gcc_gp2_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(gcc_gp2_clk.c),
@@ -2028,9 +2047,9 @@
 
 static struct branch_clk gcc_gp3_clk = {
 	.cbcr_reg = GP3_CBCR,
-	.parent = &gp3_clk_src.c,
 	.base = &virt_bases[GCC_BASE],
 	.c = {
+		.parent = &gp3_clk_src.c,
 		.dbg_name = "gcc_gp3_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(gcc_gp3_clk.c),
@@ -2039,9 +2058,9 @@
 
 static struct branch_clk gcc_pdm2_clk = {
 	.cbcr_reg = PDM2_CBCR,
-	.parent = &pdm2_clk_src.c,
 	.base = &virt_bases[GCC_BASE],
 	.c = {
+		.parent = &pdm2_clk_src.c,
 		.dbg_name = "gcc_pdm2_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(gcc_pdm2_clk.c),
@@ -2084,9 +2103,9 @@
 
 static struct branch_clk gcc_sdcc1_apps_clk = {
 	.cbcr_reg = SDCC1_APPS_CBCR,
-	.parent = &sdcc1_apps_clk_src.c,
 	.base = &virt_bases[GCC_BASE],
 	.c = {
+		.parent = &sdcc1_apps_clk_src.c,
 		.dbg_name = "gcc_sdcc1_apps_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(gcc_sdcc1_apps_clk.c),
@@ -2106,9 +2125,9 @@
 
 static struct branch_clk gcc_sdcc2_apps_clk = {
 	.cbcr_reg = SDCC2_APPS_CBCR,
-	.parent = &sdcc2_apps_clk_src.c,
 	.base = &virt_bases[GCC_BASE],
 	.c = {
+		.parent = &sdcc2_apps_clk_src.c,
 		.dbg_name = "gcc_sdcc2_apps_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(gcc_sdcc2_apps_clk.c),
@@ -2128,9 +2147,9 @@
 
 static struct branch_clk gcc_sdcc3_apps_clk = {
 	.cbcr_reg = SDCC3_APPS_CBCR,
-	.parent = &sdcc3_apps_clk_src.c,
 	.base = &virt_bases[GCC_BASE],
 	.c = {
+		.parent = &sdcc3_apps_clk_src.c,
 		.dbg_name = "gcc_sdcc3_apps_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(gcc_sdcc3_apps_clk.c),
@@ -2150,9 +2169,9 @@
 
 static struct branch_clk gcc_sdcc4_apps_clk = {
 	.cbcr_reg = SDCC4_APPS_CBCR,
-	.parent = &sdcc4_apps_clk_src.c,
 	.base = &virt_bases[GCC_BASE],
 	.c = {
+		.parent = &sdcc4_apps_clk_src.c,
 		.dbg_name = "gcc_sdcc4_apps_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(gcc_sdcc4_apps_clk.c),
@@ -2172,9 +2191,9 @@
 
 static struct branch_clk gcc_tsif_ref_clk = {
 	.cbcr_reg = TSIF_REF_CBCR,
-	.parent = &tsif_ref_clk_src.c,
 	.base = &virt_bases[GCC_BASE],
 	.c = {
+		.parent = &tsif_ref_clk_src.c,
 		.dbg_name = "gcc_tsif_ref_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(gcc_tsif_ref_clk.c),
@@ -2183,10 +2202,10 @@
 
 struct branch_clk gcc_sys_noc_usb3_axi_clk = {
 	.cbcr_reg = SYS_NOC_USB3_AXI_CBCR,
-	.parent = &usb30_master_clk_src.c,
 	.has_sibling = 1,
 	.base = &virt_bases[GCC_BASE],
 	.c = {
+		.parent = &usb30_master_clk_src.c,
 		.dbg_name = "gcc_sys_noc_usb3_axi_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(gcc_sys_noc_usb3_axi_clk.c),
@@ -2196,10 +2215,10 @@
 static struct branch_clk gcc_usb30_master_clk = {
 	.cbcr_reg = USB30_MASTER_CBCR,
 	.bcr_reg = USB_30_BCR,
-	.parent = &usb30_master_clk_src.c,
 	.has_sibling = 1,
 	.base = &virt_bases[GCC_BASE],
 	.c = {
+		.parent = &usb30_master_clk_src.c,
 		.dbg_name = "gcc_usb30_master_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(gcc_usb30_master_clk.c),
@@ -2209,9 +2228,9 @@
 
 static struct branch_clk gcc_usb30_mock_utmi_clk = {
 	.cbcr_reg = USB30_MOCK_UTMI_CBCR,
-	.parent = &usb30_mock_utmi_clk_src.c,
 	.base = &virt_bases[GCC_BASE],
 	.c = {
+		.parent = &usb30_mock_utmi_clk_src.c,
 		.dbg_name = "gcc_usb30_mock_utmi_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(gcc_usb30_mock_utmi_clk.c),
@@ -2265,9 +2284,9 @@
 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,
 	.base = &virt_bases[GCC_BASE],
 	.c = {
+		.parent = &usb_hs_system_clk_src.c,
 		.dbg_name = "gcc_usb_hs_system_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(gcc_usb_hs_system_clk.c),
@@ -2288,9 +2307,9 @@
 static struct branch_clk gcc_usb_hsic_clk = {
 	.cbcr_reg = USB_HSIC_CBCR,
 	.bcr_reg = USB_HS_HSIC_BCR,
-	.parent = &usb_hsic_clk_src.c,
 	.base = &virt_bases[GCC_BASE],
 	.c = {
+		.parent = &usb_hsic_clk_src.c,
 		.dbg_name = "gcc_usb_hsic_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(gcc_usb_hsic_clk.c),
@@ -2299,9 +2318,9 @@
 
 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,
 	.base = &virt_bases[GCC_BASE],
 	.c = {
+		.parent = &usb_hsic_io_cal_clk_src.c,
 		.dbg_name = "gcc_usb_hsic_io_cal_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(gcc_usb_hsic_io_cal_clk.c),
@@ -2310,9 +2329,9 @@
 
 static struct branch_clk gcc_usb_hsic_system_clk = {
 	.cbcr_reg = USB_HSIC_SYSTEM_CBCR,
-	.parent = &usb_hsic_system_clk_src.c,
 	.base = &virt_bases[GCC_BASE],
 	.c = {
+		.parent = &usb_hsic_system_clk_src.c,
 		.dbg_name = "gcc_usb_hsic_system_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(gcc_usb_hsic_system_clk.c),
@@ -2797,18 +2816,15 @@
 	},
 };
 
-static struct clk *dsi_pll_clk_get_parent(struct clk *c)
-{
-	return &cxo_clk_src.c;
-}
-
 static struct clk dsipll0_byte_clk_src = {
+	.parent = &cxo_clk_src.c,
 	.dbg_name = "dsipll0_byte_clk_src",
 	.ops = &clk_ops_dsi_byte_pll,
 	CLK_INIT(dsipll0_byte_clk_src),
 };
 
 static struct clk dsipll0_pixel_clk_src = {
+	.parent = &cxo_clk_src.c,
 	.dbg_name = "dsipll0_pixel_clk_src",
 	.ops = &clk_ops_dsi_pixel_pll,
 	CLK_INIT(dsipll0_pixel_clk_src),
@@ -2819,8 +2835,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;
@@ -2890,6 +2906,7 @@
 	.current_freq = &byte_freq,
 	.base = &virt_bases[MMSS_BASE],
 	.c = {
+		.parent = &dsipll0_byte_clk_src,
 		.dbg_name = "byte0_clk_src",
 		.ops = &clk_ops_byte,
 		VDD_DIG_FMAX_MAP3(LOW, 93800000, NOMINAL, 187500000,
@@ -2903,6 +2920,7 @@
 	.current_freq = &byte_freq,
 	.base = &virt_bases[MMSS_BASE],
 	.c = {
+		.parent = &dsipll0_byte_clk_src,
 		.dbg_name = "byte1_clk_src",
 		.ops = &clk_ops_byte,
 		VDD_DIG_FMAX_MAP3(LOW, 93800000, NOMINAL, 187500000,
@@ -2951,7 +2969,7 @@
 };
 
 static struct clk_freq_tbl ftbl_mdss_edppixel_clk[] = {
-	F_MDSS(148500000, edppll_350,   2,   0,   0),
+	F_MDSS(138500000, edppll_350,   2,   0,   0),
 	F_MDSS(350000000, edppll_350,  11,   0,   0),
 	F_END
 };
@@ -3035,19 +3053,14 @@
 	return rc;
 }
 
-static struct clk *hdmi_pll_clk_get_parent(struct clk *c)
-{
-	return &cxo_clk_src.c;
-}
-
 static struct clk_ops clk_ops_hdmi_pll = {
 	.enable = hdmi_pll_clk_enable,
 	.disable = hdmi_pll_clk_disable,
 	.set_rate = hdmi_pll_clk_set_rate,
-	.get_parent = hdmi_pll_clk_get_parent,
 };
 
 static struct clk hdmipll_clk_src = {
+	.parent = &cxo_clk_src.c,
 	.dbg_name = "hdmipll_clk_src",
 	.ops = &clk_ops_hdmi_pll,
 	CLK_INIT(hdmipll_clk_src),
@@ -3117,6 +3130,7 @@
 	.current_freq = &pixel_freq,
 	.base = &virt_bases[MMSS_BASE],
 	.c = {
+		.parent = &dsipll0_pixel_clk_src,
 		.dbg_name = "pclk0_clk_src",
 		.ops = &clk_ops_pixel,
 		VDD_DIG_FMAX_MAP2(LOW, 125000000, NOMINAL, 250000000),
@@ -3129,6 +3143,7 @@
 	.current_freq = &pixel_freq,
 	.base = &virt_bases[MMSS_BASE],
 	.c = {
+		.parent = &dsipll0_pixel_clk_src,
 		.dbg_name = "pclk1_clk_src",
 		.ops = &clk_ops_pixel,
 		VDD_DIG_FMAX_MAP2(LOW, 125000000, NOMINAL, 250000000),
@@ -3193,10 +3208,10 @@
 
 static struct branch_clk camss_cci_cci_clk = {
 	.cbcr_reg = CAMSS_CCI_CCI_CBCR,
-	.parent = &cci_clk_src.c,
 	.has_sibling = 0,
 	.base = &virt_bases[MMSS_BASE],
 	.c = {
+		.parent = &cci_clk_src.c,
 		.dbg_name = "camss_cci_cci_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(camss_cci_cci_clk.c),
@@ -3216,10 +3231,10 @@
 
 static struct branch_clk camss_csi0_clk = {
 	.cbcr_reg = CAMSS_CSI0_CBCR,
-	.parent = &csi0_clk_src.c,
 	.has_sibling = 1,
 	.base = &virt_bases[MMSS_BASE],
 	.c = {
+		.parent = &csi0_clk_src.c,
 		.dbg_name = "camss_csi0_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(camss_csi0_clk.c),
@@ -3228,10 +3243,10 @@
 
 static struct branch_clk camss_csi0phy_clk = {
 	.cbcr_reg = CAMSS_CSI0PHY_CBCR,
-	.parent = &csi0_clk_src.c,
 	.has_sibling = 1,
 	.base = &virt_bases[MMSS_BASE],
 	.c = {
+		.parent = &csi0_clk_src.c,
 		.dbg_name = "camss_csi0phy_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(camss_csi0phy_clk.c),
@@ -3240,10 +3255,10 @@
 
 static struct branch_clk camss_csi0pix_clk = {
 	.cbcr_reg = CAMSS_CSI0PIX_CBCR,
-	.parent = &csi0_clk_src.c,
 	.has_sibling = 1,
 	.base = &virt_bases[MMSS_BASE],
 	.c = {
+		.parent = &csi0_clk_src.c,
 		.dbg_name = "camss_csi0pix_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(camss_csi0pix_clk.c),
@@ -3252,10 +3267,10 @@
 
 static struct branch_clk camss_csi0rdi_clk = {
 	.cbcr_reg = CAMSS_CSI0RDI_CBCR,
-	.parent = &csi0_clk_src.c,
 	.has_sibling = 1,
 	.base = &virt_bases[MMSS_BASE],
 	.c = {
+		.parent = &csi0_clk_src.c,
 		.dbg_name = "camss_csi0rdi_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(camss_csi0rdi_clk.c),
@@ -3275,10 +3290,10 @@
 
 static struct branch_clk camss_csi1_clk = {
 	.cbcr_reg = CAMSS_CSI1_CBCR,
-	.parent = &csi1_clk_src.c,
 	.has_sibling = 1,
 	.base = &virt_bases[MMSS_BASE],
 	.c = {
+		.parent = &csi1_clk_src.c,
 		.dbg_name = "camss_csi1_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(camss_csi1_clk.c),
@@ -3287,10 +3302,10 @@
 
 static struct branch_clk camss_csi1phy_clk = {
 	.cbcr_reg = CAMSS_CSI1PHY_CBCR,
-	.parent = &csi1_clk_src.c,
 	.has_sibling = 1,
 	.base = &virt_bases[MMSS_BASE],
 	.c = {
+		.parent = &csi1_clk_src.c,
 		.dbg_name = "camss_csi1phy_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(camss_csi1phy_clk.c),
@@ -3299,10 +3314,10 @@
 
 static struct branch_clk camss_csi1pix_clk = {
 	.cbcr_reg = CAMSS_CSI1PIX_CBCR,
-	.parent = &csi1_clk_src.c,
 	.has_sibling = 1,
 	.base = &virt_bases[MMSS_BASE],
 	.c = {
+		.parent = &csi1_clk_src.c,
 		.dbg_name = "camss_csi1pix_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(camss_csi1pix_clk.c),
@@ -3311,10 +3326,10 @@
 
 static struct branch_clk camss_csi1rdi_clk = {
 	.cbcr_reg = CAMSS_CSI1RDI_CBCR,
-	.parent = &csi1_clk_src.c,
 	.has_sibling = 1,
 	.base = &virt_bases[MMSS_BASE],
 	.c = {
+		.parent = &csi1_clk_src.c,
 		.dbg_name = "camss_csi1rdi_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(camss_csi1rdi_clk.c),
@@ -3334,10 +3349,10 @@
 
 static struct branch_clk camss_csi2_clk = {
 	.cbcr_reg = CAMSS_CSI2_CBCR,
-	.parent = &csi2_clk_src.c,
 	.has_sibling = 1,
 	.base = &virt_bases[MMSS_BASE],
 	.c = {
+		.parent = &csi2_clk_src.c,
 		.dbg_name = "camss_csi2_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(camss_csi2_clk.c),
@@ -3346,10 +3361,10 @@
 
 static struct branch_clk camss_csi2phy_clk = {
 	.cbcr_reg = CAMSS_CSI2PHY_CBCR,
-	.parent = &csi2_clk_src.c,
 	.has_sibling = 1,
 	.base = &virt_bases[MMSS_BASE],
 	.c = {
+		.parent = &csi2_clk_src.c,
 		.dbg_name = "camss_csi2phy_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(camss_csi2phy_clk.c),
@@ -3358,10 +3373,10 @@
 
 static struct branch_clk camss_csi2pix_clk = {
 	.cbcr_reg = CAMSS_CSI2PIX_CBCR,
-	.parent = &csi2_clk_src.c,
 	.has_sibling = 1,
 	.base = &virt_bases[MMSS_BASE],
 	.c = {
+		.parent = &csi2_clk_src.c,
 		.dbg_name = "camss_csi2pix_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(camss_csi2pix_clk.c),
@@ -3370,10 +3385,10 @@
 
 static struct branch_clk camss_csi2rdi_clk = {
 	.cbcr_reg = CAMSS_CSI2RDI_CBCR,
-	.parent = &csi2_clk_src.c,
 	.has_sibling = 1,
 	.base = &virt_bases[MMSS_BASE],
 	.c = {
+		.parent = &csi2_clk_src.c,
 		.dbg_name = "camss_csi2rdi_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(camss_csi2rdi_clk.c),
@@ -3393,10 +3408,10 @@
 
 static struct branch_clk camss_csi3_clk = {
 	.cbcr_reg = CAMSS_CSI3_CBCR,
-	.parent = &csi3_clk_src.c,
 	.has_sibling = 1,
 	.base = &virt_bases[MMSS_BASE],
 	.c = {
+		.parent = &csi3_clk_src.c,
 		.dbg_name = "camss_csi3_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(camss_csi3_clk.c),
@@ -3405,10 +3420,10 @@
 
 static struct branch_clk camss_csi3phy_clk = {
 	.cbcr_reg = CAMSS_CSI3PHY_CBCR,
-	.parent = &csi3_clk_src.c,
 	.has_sibling = 1,
 	.base = &virt_bases[MMSS_BASE],
 	.c = {
+		.parent = &csi3_clk_src.c,
 		.dbg_name = "camss_csi3phy_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(camss_csi3phy_clk.c),
@@ -3417,10 +3432,10 @@
 
 static struct branch_clk camss_csi3pix_clk = {
 	.cbcr_reg = CAMSS_CSI3PIX_CBCR,
-	.parent = &csi3_clk_src.c,
 	.has_sibling = 1,
 	.base = &virt_bases[MMSS_BASE],
 	.c = {
+		.parent = &csi3_clk_src.c,
 		.dbg_name = "camss_csi3pix_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(camss_csi3pix_clk.c),
@@ -3429,10 +3444,10 @@
 
 static struct branch_clk camss_csi3rdi_clk = {
 	.cbcr_reg = CAMSS_CSI3RDI_CBCR,
-	.parent = &csi3_clk_src.c,
 	.has_sibling = 1,
 	.base = &virt_bases[MMSS_BASE],
 	.c = {
+		.parent = &csi3_clk_src.c,
 		.dbg_name = "camss_csi3rdi_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(camss_csi3rdi_clk.c),
@@ -3441,10 +3456,10 @@
 
 static struct branch_clk camss_csi_vfe0_clk = {
 	.cbcr_reg = CAMSS_CSI_VFE0_CBCR,
-	.parent = &vfe0_clk_src.c,
 	.has_sibling = 1,
 	.base = &virt_bases[MMSS_BASE],
 	.c = {
+		.parent = &vfe0_clk_src.c,
 		.dbg_name = "camss_csi_vfe0_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(camss_csi_vfe0_clk.c),
@@ -3453,10 +3468,10 @@
 
 static struct branch_clk camss_csi_vfe1_clk = {
 	.cbcr_reg = CAMSS_CSI_VFE1_CBCR,
-	.parent = &vfe1_clk_src.c,
 	.has_sibling = 1,
 	.base = &virt_bases[MMSS_BASE],
 	.c = {
+		.parent = &vfe1_clk_src.c,
 		.dbg_name = "camss_csi_vfe1_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(camss_csi_vfe1_clk.c),
@@ -3465,10 +3480,10 @@
 
 static struct branch_clk camss_gp0_clk = {
 	.cbcr_reg = CAMSS_GP0_CBCR,
-	.parent = &mmss_gp0_clk_src.c,
 	.has_sibling = 0,
 	.base = &virt_bases[MMSS_BASE],
 	.c = {
+		.parent = &mmss_gp0_clk_src.c,
 		.dbg_name = "camss_gp0_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(camss_gp0_clk.c),
@@ -3477,10 +3492,10 @@
 
 static struct branch_clk camss_gp1_clk = {
 	.cbcr_reg = CAMSS_GP1_CBCR,
-	.parent = &mmss_gp1_clk_src.c,
 	.has_sibling = 0,
 	.base = &virt_bases[MMSS_BASE],
 	.c = {
+		.parent = &mmss_gp1_clk_src.c,
 		.dbg_name = "camss_gp1_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(camss_gp1_clk.c),
@@ -3500,10 +3515,10 @@
 
 static struct branch_clk camss_jpeg_jpeg0_clk = {
 	.cbcr_reg = CAMSS_JPEG_JPEG0_CBCR,
-	.parent = &jpeg0_clk_src.c,
 	.has_sibling = 0,
 	.base = &virt_bases[MMSS_BASE],
 	.c = {
+		.parent = &jpeg0_clk_src.c,
 		.dbg_name = "camss_jpeg_jpeg0_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(camss_jpeg_jpeg0_clk.c),
@@ -3512,10 +3527,10 @@
 
 static struct branch_clk camss_jpeg_jpeg1_clk = {
 	.cbcr_reg = CAMSS_JPEG_JPEG1_CBCR,
-	.parent = &jpeg1_clk_src.c,
 	.has_sibling = 0,
 	.base = &virt_bases[MMSS_BASE],
 	.c = {
+		.parent = &jpeg1_clk_src.c,
 		.dbg_name = "camss_jpeg_jpeg1_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(camss_jpeg_jpeg1_clk.c),
@@ -3524,10 +3539,10 @@
 
 static struct branch_clk camss_jpeg_jpeg2_clk = {
 	.cbcr_reg = CAMSS_JPEG_JPEG2_CBCR,
-	.parent = &jpeg2_clk_src.c,
 	.has_sibling = 0,
 	.base = &virt_bases[MMSS_BASE],
 	.c = {
+		.parent = &jpeg2_clk_src.c,
 		.dbg_name = "camss_jpeg_jpeg2_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(camss_jpeg_jpeg2_clk.c),
@@ -3547,10 +3562,10 @@
 
 static struct branch_clk camss_jpeg_jpeg_axi_clk = {
 	.cbcr_reg = CAMSS_JPEG_JPEG_AXI_CBCR,
-	.parent = &axi_clk_src.c,
 	.has_sibling = 1,
 	.base = &virt_bases[MMSS_BASE],
 	.c = {
+		.parent = &axi_clk_src.c,
 		.dbg_name = "camss_jpeg_jpeg_axi_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(camss_jpeg_jpeg_axi_clk.c),
@@ -3559,10 +3574,10 @@
 
 static struct branch_clk camss_jpeg_jpeg_ocmemnoc_clk = {
 	.cbcr_reg = CAMSS_JPEG_JPEG_OCMEMNOC_CBCR,
-	.parent = &ocmemnoc_clk_src.c,
 	.has_sibling = 1,
 	.base = &virt_bases[MMSS_BASE],
 	.c = {
+		.parent = &ocmemnoc_clk_src.c,
 		.dbg_name = "camss_jpeg_jpeg_ocmemnoc_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(camss_jpeg_jpeg_ocmemnoc_clk.c),
@@ -3571,10 +3586,10 @@
 
 static struct branch_clk camss_mclk0_clk = {
 	.cbcr_reg = CAMSS_MCLK0_CBCR,
-	.parent = &mclk0_clk_src.c,
 	.has_sibling = 0,
 	.base = &virt_bases[MMSS_BASE],
 	.c = {
+		.parent = &mclk0_clk_src.c,
 		.dbg_name = "camss_mclk0_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(camss_mclk0_clk.c),
@@ -3583,10 +3598,10 @@
 
 static struct branch_clk camss_mclk1_clk = {
 	.cbcr_reg = CAMSS_MCLK1_CBCR,
-	.parent = &mclk1_clk_src.c,
 	.has_sibling = 0,
 	.base = &virt_bases[MMSS_BASE],
 	.c = {
+		.parent = &mclk1_clk_src.c,
 		.dbg_name = "camss_mclk1_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(camss_mclk1_clk.c),
@@ -3595,10 +3610,10 @@
 
 static struct branch_clk camss_mclk2_clk = {
 	.cbcr_reg = CAMSS_MCLK2_CBCR,
-	.parent = &mclk2_clk_src.c,
 	.has_sibling = 0,
 	.base = &virt_bases[MMSS_BASE],
 	.c = {
+		.parent = &mclk2_clk_src.c,
 		.dbg_name = "camss_mclk2_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(camss_mclk2_clk.c),
@@ -3607,10 +3622,10 @@
 
 static struct branch_clk camss_mclk3_clk = {
 	.cbcr_reg = CAMSS_MCLK3_CBCR,
-	.parent = &mclk3_clk_src.c,
 	.has_sibling = 0,
 	.base = &virt_bases[MMSS_BASE],
 	.c = {
+		.parent = &mclk3_clk_src.c,
 		.dbg_name = "camss_mclk3_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(camss_mclk3_clk.c),
@@ -3630,10 +3645,10 @@
 
 static struct branch_clk camss_phy0_csi0phytimer_clk = {
 	.cbcr_reg = CAMSS_PHY0_CSI0PHYTIMER_CBCR,
-	.parent = &csi0phytimer_clk_src.c,
 	.has_sibling = 0,
 	.base = &virt_bases[MMSS_BASE],
 	.c = {
+		.parent = &csi0phytimer_clk_src.c,
 		.dbg_name = "camss_phy0_csi0phytimer_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(camss_phy0_csi0phytimer_clk.c),
@@ -3642,10 +3657,10 @@
 
 static struct branch_clk camss_phy1_csi1phytimer_clk = {
 	.cbcr_reg = CAMSS_PHY1_CSI1PHYTIMER_CBCR,
-	.parent = &csi1phytimer_clk_src.c,
 	.has_sibling = 0,
 	.base = &virt_bases[MMSS_BASE],
 	.c = {
+		.parent = &csi1phytimer_clk_src.c,
 		.dbg_name = "camss_phy1_csi1phytimer_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(camss_phy1_csi1phytimer_clk.c),
@@ -3654,10 +3669,10 @@
 
 static struct branch_clk camss_phy2_csi2phytimer_clk = {
 	.cbcr_reg = CAMSS_PHY2_CSI2PHYTIMER_CBCR,
-	.parent = &csi2phytimer_clk_src.c,
 	.has_sibling = 0,
 	.base = &virt_bases[MMSS_BASE],
 	.c = {
+		.parent = &csi2phytimer_clk_src.c,
 		.dbg_name = "camss_phy2_csi2phytimer_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(camss_phy2_csi2phytimer_clk.c),
@@ -3688,10 +3703,10 @@
 
 static struct branch_clk camss_vfe_cpp_clk = {
 	.cbcr_reg = CAMSS_VFE_CPP_CBCR,
-	.parent = &cpp_clk_src.c,
 	.has_sibling = 0,
 	.base = &virt_bases[MMSS_BASE],
 	.c = {
+		.parent = &cpp_clk_src.c,
 		.dbg_name = "camss_vfe_cpp_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(camss_vfe_cpp_clk.c),
@@ -3700,10 +3715,10 @@
 
 static struct branch_clk camss_vfe_vfe0_clk = {
 	.cbcr_reg = CAMSS_VFE_VFE0_CBCR,
-	.parent = &vfe0_clk_src.c,
 	.has_sibling = 1,
 	.base = &virt_bases[MMSS_BASE],
 	.c = {
+		.parent = &vfe0_clk_src.c,
 		.dbg_name = "camss_vfe_vfe0_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(camss_vfe_vfe0_clk.c),
@@ -3712,10 +3727,10 @@
 
 static struct branch_clk camss_vfe_vfe1_clk = {
 	.cbcr_reg = CAMSS_VFE_VFE1_CBCR,
-	.parent = &vfe1_clk_src.c,
 	.has_sibling = 1,
 	.base = &virt_bases[MMSS_BASE],
 	.c = {
+		.parent = &vfe1_clk_src.c,
 		.dbg_name = "camss_vfe_vfe1_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(camss_vfe_vfe1_clk.c),
@@ -3735,10 +3750,10 @@
 
 static struct branch_clk camss_vfe_vfe_axi_clk = {
 	.cbcr_reg = CAMSS_VFE_VFE_AXI_CBCR,
-	.parent = &axi_clk_src.c,
 	.has_sibling = 1,
 	.base = &virt_bases[MMSS_BASE],
 	.c = {
+		.parent = &axi_clk_src.c,
 		.dbg_name = "camss_vfe_vfe_axi_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(camss_vfe_vfe_axi_clk.c),
@@ -3747,10 +3762,10 @@
 
 static struct branch_clk camss_vfe_vfe_ocmemnoc_clk = {
 	.cbcr_reg = CAMSS_VFE_VFE_OCMEMNOC_CBCR,
-	.parent = &ocmemnoc_clk_src.c,
 	.has_sibling = 1,
 	.base = &virt_bases[MMSS_BASE],
 	.c = {
+		.parent = &ocmemnoc_clk_src.c,
 		.dbg_name = "camss_vfe_vfe_ocmemnoc_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(camss_vfe_vfe_ocmemnoc_clk.c),
@@ -3770,10 +3785,10 @@
 
 static struct branch_clk mdss_axi_clk = {
 	.cbcr_reg = MDSS_AXI_CBCR,
-	.parent = &axi_clk_src.c,
 	.has_sibling = 1,
 	.base = &virt_bases[MMSS_BASE],
 	.c = {
+		.parent = &axi_clk_src.c,
 		.dbg_name = "mdss_axi_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(mdss_axi_clk.c),
@@ -3782,10 +3797,10 @@
 
 static struct branch_clk mdss_byte0_clk = {
 	.cbcr_reg = MDSS_BYTE0_CBCR,
-	.parent = &byte0_clk_src.c,
 	.has_sibling = 0,
 	.base = &virt_bases[MMSS_BASE],
 	.c = {
+		.parent = &byte0_clk_src.c,
 		.dbg_name = "mdss_byte0_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(mdss_byte0_clk.c),
@@ -3794,10 +3809,10 @@
 
 static struct branch_clk mdss_byte1_clk = {
 	.cbcr_reg = MDSS_BYTE1_CBCR,
-	.parent = &byte1_clk_src.c,
 	.has_sibling = 0,
 	.base = &virt_bases[MMSS_BASE],
 	.c = {
+		.parent = &byte1_clk_src.c,
 		.dbg_name = "mdss_byte1_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(mdss_byte1_clk.c),
@@ -3806,10 +3821,10 @@
 
 static struct branch_clk mdss_edpaux_clk = {
 	.cbcr_reg = MDSS_EDPAUX_CBCR,
-	.parent = &edpaux_clk_src.c,
 	.has_sibling = 0,
 	.base = &virt_bases[MMSS_BASE],
 	.c = {
+		.parent = &edpaux_clk_src.c,
 		.dbg_name = "mdss_edpaux_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(mdss_edpaux_clk.c),
@@ -3818,10 +3833,10 @@
 
 static struct branch_clk mdss_edplink_clk = {
 	.cbcr_reg = MDSS_EDPLINK_CBCR,
-	.parent = &edplink_clk_src.c,
 	.has_sibling = 0,
 	.base = &virt_bases[MMSS_BASE],
 	.c = {
+		.parent = &edplink_clk_src.c,
 		.dbg_name = "mdss_edplink_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(mdss_edplink_clk.c),
@@ -3830,10 +3845,10 @@
 
 static struct branch_clk mdss_edppixel_clk = {
 	.cbcr_reg = MDSS_EDPPIXEL_CBCR,
-	.parent = &edppixel_clk_src.c,
 	.has_sibling = 0,
 	.base = &virt_bases[MMSS_BASE],
 	.c = {
+		.parent = &edppixel_clk_src.c,
 		.dbg_name = "mdss_edppixel_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(mdss_edppixel_clk.c),
@@ -3842,10 +3857,10 @@
 
 static struct branch_clk mdss_esc0_clk = {
 	.cbcr_reg = MDSS_ESC0_CBCR,
-	.parent = &esc0_clk_src.c,
 	.has_sibling = 0,
 	.base = &virt_bases[MMSS_BASE],
 	.c = {
+		.parent = &esc0_clk_src.c,
 		.dbg_name = "mdss_esc0_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(mdss_esc0_clk.c),
@@ -3854,10 +3869,10 @@
 
 static struct branch_clk mdss_esc1_clk = {
 	.cbcr_reg = MDSS_ESC1_CBCR,
-	.parent = &esc1_clk_src.c,
 	.has_sibling = 0,
 	.base = &virt_bases[MMSS_BASE],
 	.c = {
+		.parent = &esc1_clk_src.c,
 		.dbg_name = "mdss_esc1_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(mdss_esc1_clk.c),
@@ -3866,10 +3881,10 @@
 
 static struct branch_clk mdss_extpclk_clk = {
 	.cbcr_reg = MDSS_EXTPCLK_CBCR,
-	.parent = &extpclk_clk_src.c,
 	.has_sibling = 0,
 	.base = &virt_bases[MMSS_BASE],
 	.c = {
+		.parent = &extpclk_clk_src.c,
 		.dbg_name = "mdss_extpclk_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(mdss_extpclk_clk.c),
@@ -3889,10 +3904,10 @@
 
 static struct branch_clk mdss_hdmi_clk = {
 	.cbcr_reg = MDSS_HDMI_CBCR,
-	.parent = &hdmi_clk_src.c,
 	.has_sibling = 0,
 	.base = &virt_bases[MMSS_BASE],
 	.c = {
+		.parent = &hdmi_clk_src.c,
 		.dbg_name = "mdss_hdmi_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(mdss_hdmi_clk.c),
@@ -3901,10 +3916,10 @@
 
 static struct branch_clk mdss_mdp_clk = {
 	.cbcr_reg = MDSS_MDP_CBCR,
-	.parent = &mdp_clk_src.c,
 	.has_sibling = 1,
 	.base = &virt_bases[MMSS_BASE],
 	.c = {
+		.parent = &mdp_clk_src.c,
 		.dbg_name = "mdss_mdp_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(mdss_mdp_clk.c),
@@ -3913,10 +3928,10 @@
 
 static struct branch_clk mdss_mdp_lut_clk = {
 	.cbcr_reg = MDSS_MDP_LUT_CBCR,
-	.parent = &mdp_clk_src.c,
 	.has_sibling = 1,
 	.base = &virt_bases[MMSS_BASE],
 	.c = {
+		.parent = &mdp_clk_src.c,
 		.dbg_name = "mdss_mdp_lut_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(mdss_mdp_lut_clk.c),
@@ -3925,10 +3940,10 @@
 
 static struct branch_clk mdss_pclk0_clk = {
 	.cbcr_reg = MDSS_PCLK0_CBCR,
-	.parent = &pclk0_clk_src.c,
 	.has_sibling = 0,
 	.base = &virt_bases[MMSS_BASE],
 	.c = {
+		.parent = &pclk0_clk_src.c,
 		.dbg_name = "mdss_pclk0_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(mdss_pclk0_clk.c),
@@ -3937,10 +3952,10 @@
 
 static struct branch_clk mdss_pclk1_clk = {
 	.cbcr_reg = MDSS_PCLK1_CBCR,
-	.parent = &pclk1_clk_src.c,
 	.has_sibling = 0,
 	.base = &virt_bases[MMSS_BASE],
 	.c = {
+		.parent = &pclk1_clk_src.c,
 		.dbg_name = "mdss_pclk1_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(mdss_pclk1_clk.c),
@@ -3949,10 +3964,10 @@
 
 static struct branch_clk mdss_vsync_clk = {
 	.cbcr_reg = MDSS_VSYNC_CBCR,
-	.parent = &vsync_clk_src.c,
 	.has_sibling = 0,
 	.base = &virt_bases[MMSS_BASE],
 	.c = {
+		.parent = &vsync_clk_src.c,
 		.dbg_name = "mdss_vsync_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(mdss_vsync_clk.c),
@@ -3983,10 +3998,10 @@
 
 static struct branch_clk mmss_mmssnoc_axi_clk = {
 	.cbcr_reg = MMSS_MMSSNOC_AXI_CBCR,
-	.parent = &axi_clk_src.c,
 	.has_sibling = 1,
 	.base = &virt_bases[MMSS_BASE],
 	.c = {
+		.parent = &axi_clk_src.c,
 		.dbg_name = "mmss_mmssnoc_axi_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(mmss_mmssnoc_axi_clk.c),
@@ -3995,11 +4010,11 @@
 
 static struct branch_clk mmss_s0_axi_clk = {
 	.cbcr_reg = MMSS_S0_AXI_CBCR,
-	.parent = &axi_clk_src.c,
 	/* The bus driver needs set_rate to go through to the parent */
 	.has_sibling = 0,
 	.base = &virt_bases[MMSS_BASE],
 	.c = {
+		.parent = &axi_clk_src.c,
 		.dbg_name = "mmss_s0_axi_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(mmss_s0_axi_clk.c),
@@ -4009,11 +4024,11 @@
 
 struct branch_clk ocmemnoc_clk = {
 	.cbcr_reg = OCMEMNOC_CBCR,
-	.parent = &ocmemnoc_clk_src.c,
 	.has_sibling = 0,
 	.bcr_reg = 0x50b0,
 	.base = &virt_bases[MMSS_BASE],
 	.c = {
+		.parent = &ocmemnoc_clk_src.c,
 		.dbg_name = "ocmemnoc_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(ocmemnoc_clk.c),
@@ -4022,10 +4037,10 @@
 
 struct branch_clk ocmemcx_ocmemnoc_clk = {
 	.cbcr_reg = OCMEMCX_OCMEMNOC_CBCR,
-	.parent = &ocmemnoc_clk_src.c,
 	.has_sibling = 1,
 	.base = &virt_bases[MMSS_BASE],
 	.c = {
+		.parent = &ocmemnoc_clk_src.c,
 		.dbg_name = "ocmemcx_ocmemnoc_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(ocmemcx_ocmemnoc_clk.c),
@@ -4045,10 +4060,10 @@
 
 static struct branch_clk venus0_axi_clk = {
 	.cbcr_reg = VENUS0_AXI_CBCR,
-	.parent = &axi_clk_src.c,
 	.has_sibling = 1,
 	.base = &virt_bases[MMSS_BASE],
 	.c = {
+		.parent = &axi_clk_src.c,
 		.dbg_name = "venus0_axi_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(venus0_axi_clk.c),
@@ -4057,10 +4072,10 @@
 
 static struct branch_clk venus0_ocmemnoc_clk = {
 	.cbcr_reg = VENUS0_OCMEMNOC_CBCR,
-	.parent = &ocmemnoc_clk_src.c,
 	.has_sibling = 1,
 	.base = &virt_bases[MMSS_BASE],
 	.c = {
+		.parent = &ocmemnoc_clk_src.c,
 		.dbg_name = "venus0_ocmemnoc_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(venus0_ocmemnoc_clk.c),
@@ -4069,10 +4084,10 @@
 
 static struct branch_clk venus0_vcodec0_clk = {
 	.cbcr_reg = VENUS0_VCODEC0_CBCR,
-	.parent = &vcodec0_clk_src.c,
 	.has_sibling = 0,
 	.base = &virt_bases[MMSS_BASE],
 	.c = {
+		.parent = &vcodec0_clk_src.c,
 		.dbg_name = "venus0_vcodec0_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(venus0_vcodec0_clk.c),
@@ -4081,10 +4096,10 @@
 
 static struct branch_clk oxilicx_axi_clk = {
 	.cbcr_reg = OXILICX_AXI_CBCR,
-	.parent = &axi_clk_src.c,
 	.has_sibling = 1,
 	.base = &virt_bases[MMSS_BASE],
 	.c = {
+		.parent = &axi_clk_src.c,
 		.dbg_name = "oxilicx_axi_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(oxilicx_axi_clk.c),
@@ -4093,9 +4108,9 @@
 
 static struct branch_clk oxili_gfx3d_clk = {
 	.cbcr_reg = OXILI_GFX3D_CBCR,
-	.parent = &oxili_gfx3d_clk_src.c,
 	.base = &virt_bases[MMSS_BASE],
 	.c = {
+		.parent = &oxili_gfx3d_clk_src.c,
 		.dbg_name = "oxili_gfx3d_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(oxili_gfx3d_clk.c),
@@ -4135,9 +4150,9 @@
 
 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 = {
+		.parent = &audio_core_slimbus_core_clk_src.c,
 		.dbg_name = "audio_core_slimbus_core_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(audio_core_slimbus_core_clk.c),
@@ -4283,10 +4298,10 @@
 
 static struct branch_clk audio_core_lpaif_codec_spkr_osr_clk = {
 	.cbcr_reg = AUDIO_CORE_LPAIF_CODEC_SPKR_OSR_CBCR,
-	.parent = &audio_core_lpaif_codec_spkr_clk_src.c,
 	.has_sibling = 1,
 	.base = &virt_bases[LPASS_BASE],
 	.c = {
+		.parent = &audio_core_lpaif_codec_spkr_clk_src.c,
 		.dbg_name = "audio_core_lpaif_codec_spkr_osr_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(audio_core_lpaif_codec_spkr_osr_clk.c),
@@ -4306,11 +4321,11 @@
 
 static struct branch_clk audio_core_lpaif_codec_spkr_ibit_clk = {
 	.cbcr_reg = AUDIO_CORE_LPAIF_CODEC_SPKR_IBIT_CBCR,
-	.parent = &audio_core_lpaif_codec_spkr_clk_src.c,
 	.has_sibling = 1,
 	.max_div = 15,
 	.base = &virt_bases[LPASS_BASE],
 	.c = {
+		.parent = &audio_core_lpaif_codec_spkr_clk_src.c,
 		.dbg_name = "audio_core_lpaif_codec_spkr_ibit_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(audio_core_lpaif_codec_spkr_ibit_clk.c),
@@ -4319,10 +4334,10 @@
 
 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 = {
+		.parent = &audio_core_lpaif_pri_clk_src.c,
 		.dbg_name = "audio_core_lpaif_pri_osr_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(audio_core_lpaif_pri_osr_clk.c),
@@ -4342,11 +4357,11 @@
 
 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 = {
+		.parent = &audio_core_lpaif_pri_clk_src.c,
 		.dbg_name = "audio_core_lpaif_pri_ibit_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(audio_core_lpaif_pri_ibit_clk.c),
@@ -4355,10 +4370,10 @@
 
 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 = {
+		.parent = &audio_core_lpaif_sec_clk_src.c,
 		.dbg_name = "audio_core_lpaif_sec_osr_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(audio_core_lpaif_sec_osr_clk.c),
@@ -4378,11 +4393,11 @@
 
 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 = {
+		.parent = &audio_core_lpaif_sec_clk_src.c,
 		.dbg_name = "audio_core_lpaif_sec_ibit_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(audio_core_lpaif_sec_ibit_clk.c),
@@ -4391,10 +4406,10 @@
 
 static struct branch_clk audio_core_lpaif_ter_osr_clk = {
 	.cbcr_reg = AUDIO_CORE_LPAIF_TER_OSR_CBCR,
-	.parent = &audio_core_lpaif_ter_clk_src.c,
 	.has_sibling = 1,
 	.base = &virt_bases[LPASS_BASE],
 	.c = {
+		.parent = &audio_core_lpaif_ter_clk_src.c,
 		.dbg_name = "audio_core_lpaif_ter_osr_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(audio_core_lpaif_ter_osr_clk.c),
@@ -4414,11 +4429,11 @@
 
 static struct branch_clk audio_core_lpaif_ter_ibit_clk = {
 	.cbcr_reg = AUDIO_CORE_LPAIF_TER_IBIT_CBCR,
-	.parent = &audio_core_lpaif_ter_clk_src.c,
 	.has_sibling = 1,
 	.max_div = 15,
 	.base = &virt_bases[LPASS_BASE],
 	.c = {
+		.parent = &audio_core_lpaif_ter_clk_src.c,
 		.dbg_name = "audio_core_lpaif_ter_ibit_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(audio_core_lpaif_ter_ibit_clk.c),
@@ -4427,10 +4442,10 @@
 
 static struct branch_clk audio_core_lpaif_quad_osr_clk = {
 	.cbcr_reg = AUDIO_CORE_LPAIF_QUAD_OSR_CBCR,
-	.parent = &audio_core_lpaif_quad_clk_src.c,
 	.has_sibling = 1,
 	.base = &virt_bases[LPASS_BASE],
 	.c = {
+		.parent = &audio_core_lpaif_quad_clk_src.c,
 		.dbg_name = "audio_core_lpaif_quad_osr_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(audio_core_lpaif_quad_osr_clk.c),
@@ -4450,11 +4465,11 @@
 
 static struct branch_clk audio_core_lpaif_quad_ibit_clk = {
 	.cbcr_reg = AUDIO_CORE_LPAIF_QUAD_IBIT_CBCR,
-	.parent = &audio_core_lpaif_quad_clk_src.c,
 	.has_sibling = 1,
 	.max_div = 15,
 	.base = &virt_bases[LPASS_BASE],
 	.c = {
+		.parent = &audio_core_lpaif_quad_clk_src.c,
 		.dbg_name = "audio_core_lpaif_quad_ibit_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(audio_core_lpaif_quad_ibit_clk.c),
@@ -4474,10 +4489,10 @@
 
 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 = 1,
 	.base = &virt_bases[LPASS_BASE],
 	.c = {
+		.parent = &audio_core_lpaif_pcm0_clk_src.c,
 		.dbg_name = "audio_core_lpaif_pcm0_ibit_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(audio_core_lpaif_pcm0_ibit_clk.c),
@@ -4486,10 +4501,10 @@
 
 static struct branch_clk audio_core_lpaif_pcm1_ebit_clk = {
 	.cbcr_reg = AUDIO_CORE_LPAIF_PCM1_EBIT_CBCR,
-	.parent = &audio_core_lpaif_pcm1_clk_src.c,
 	.has_sibling = 1,
 	.base = &virt_bases[LPASS_BASE],
 	.c = {
+		.parent = &audio_core_lpaif_pcm1_clk_src.c,
 		.dbg_name = "audio_core_lpaif_pcm1_ebit_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(audio_core_lpaif_pcm1_ebit_clk.c),
@@ -4498,10 +4513,10 @@
 
 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 = 1,
 	.base = &virt_bases[LPASS_BASE],
 	.c = {
+		.parent = &audio_core_lpaif_pcm1_clk_src.c,
 		.dbg_name = "audio_core_lpaif_pcm1_ibit_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(audio_core_lpaif_pcm1_ibit_clk.c),
@@ -4510,9 +4525,9 @@
 
 struct branch_clk audio_core_lpaif_pcmoe_clk = {
 	.cbcr_reg = AUDIO_CORE_LPAIF_PCM_DATA_OE_CBCR,
-	.parent = &audio_core_lpaif_pcmoe_clk_src.c,
 	.base = &virt_bases[LPASS_BASE],
 	.c = {
+		.parent = &audio_core_lpaif_pcmoe_clk_src.c,
 		.dbg_name = "audio_core_lpaif_pcmoe_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(audio_core_lpaif_pcmoe_clk.c),
@@ -5012,7 +5027,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"),
@@ -5115,6 +5129,7 @@
 	CLK_LOOKUP("core_clk", gcc_usb30_master_clk.c,    "msm_dwc3"),
 	CLK_LOOKUP("utmi_clk", gcc_usb30_mock_utmi_clk.c, "msm_dwc3"),
 	CLK_LOOKUP("iface_clk", gcc_sys_noc_usb3_axi_clk.c, "msm_dwc3"),
+	CLK_LOOKUP("iface_clk", gcc_sys_noc_usb3_axi_clk.c, "msm_usb3"),
 	CLK_LOOKUP("sleep_clk", gcc_usb30_sleep_clk.c, "msm_dwc3"),
 	CLK_LOOKUP("sleep_a_clk", gcc_usb2a_phy_sleep_clk.c, "msm_dwc3"),
 	CLK_LOOKUP("sleep_b_clk", gcc_usb2b_phy_sleep_clk.c, "msm_dwc3"),
@@ -5150,10 +5165,10 @@
 
 	/* MM sensor clocks */
 	CLK_LOOKUP("cam_src_clk", mclk0_clk_src.c, "6e.qcom,camera"),
-	CLK_LOOKUP("cam_src_clk", mclk1_clk_src.c, "6c.qcom,camera"),
+	CLK_LOOKUP("cam_src_clk", mclk2_clk_src.c, "6c.qcom,camera"),
 	CLK_LOOKUP("cam_src_clk", mclk1_clk_src.c, "90.qcom,camera"),
 	CLK_LOOKUP("cam_clk", camss_mclk0_clk.c, "6e.qcom,camera"),
-	CLK_LOOKUP("cam_clk", camss_mclk1_clk.c, "6c.qcom,camera"),
+	CLK_LOOKUP("cam_clk", camss_mclk2_clk.c, "6c.qcom,camera"),
 	CLK_LOOKUP("cam_clk", camss_mclk1_clk.c, "90.qcom,camera"),
 	CLK_LOOKUP("cam_clk", camss_mclk1_clk.c, ""),
 	CLK_LOOKUP("cam_clk", camss_mclk2_clk.c, ""),
@@ -5340,10 +5355,14 @@
 	CLK_LOOKUP("osr_clk", audio_core_lpaif_ter_osr_clk.c, ""),
 	CLK_LOOKUP("ebit_clk", audio_core_lpaif_ter_ebit_clk.c, ""),
 	CLK_LOOKUP("ibit_clk", audio_core_lpaif_ter_ibit_clk.c, ""),
-	CLK_LOOKUP("core_clk", audio_core_lpaif_quad_clk_src.c, ""),
-	CLK_LOOKUP("osr_clk", audio_core_lpaif_quad_osr_clk.c, ""),
-	CLK_LOOKUP("ebit_clk", audio_core_lpaif_quad_ebit_clk.c, ""),
-	CLK_LOOKUP("ibit_clk", audio_core_lpaif_quad_ibit_clk.c, ""),
+	CLK_LOOKUP("core_clk", audio_core_lpaif_quad_clk_src.c,
+			"msm-dai-q6-mi2s.3"),
+	CLK_LOOKUP("osr_clk", audio_core_lpaif_quad_osr_clk.c,
+			"msm-dai-q6-mi2s.3"),
+	CLK_LOOKUP("ebit_clk", audio_core_lpaif_quad_ebit_clk.c,
+			"msm-dai-q6-mi2s.3"),
+	CLK_LOOKUP("ibit_clk", audio_core_lpaif_quad_ibit_clk.c,
+			"msm-dai-q6-mi2s.3"),
 	CLK_LOOKUP("pcm_clk", audio_core_lpaif_pcm0_clk_src.c,
 						"msm-dai-q6.4106"),
 	CLK_LOOKUP("ebit_clk", audio_core_lpaif_pcm0_ebit_clk.c, ""),
@@ -5672,11 +5691,9 @@
 {
 	clk_ops_byte = clk_ops_rcg_mnd;
 	clk_ops_byte.set_rate = set_rate_byte;
-	clk_ops_dsi_byte_pll.get_parent = dsi_pll_clk_get_parent;
 
 	clk_ops_pixel = clk_ops_rcg;
 	clk_ops_pixel.set_rate = set_rate_pixel;
-	clk_ops_dsi_pixel_pll.get_parent = dsi_pll_clk_get_parent;
 
 	mdss_clk_ctrl_init();
 }
diff --git a/arch/arm/mach-msm/clock-8x60.c b/arch/arm/mach-msm/clock-8x60.c
index bc4bb2e..2ad425d 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);
@@ -307,8 +317,8 @@
 	.en_mask = BIT(8),
 	.status_reg = BB_PLL8_STATUS_REG,
 	.status_mask = BIT(16),
-	.parent = &pxo_clk.c,
 	.c = {
+		.parent = &pxo_clk.c,
 		.dbg_name = "pll8_clk",
 		.rate = 384000000,
 		.ops = &clk_ops_pll_vote,
@@ -318,8 +328,8 @@
 
 static struct pll_clk pll2_clk = {
 	.mode_reg = MM_PLL1_MODE_REG,
-	.parent = &pxo_clk.c,
 	.c = {
+		.parent = &pxo_clk.c,
 		.dbg_name = "pll2_clk",
 		.rate = 800000000,
 		.ops = &clk_ops_local_pll,
@@ -329,8 +339,8 @@
 
 static struct pll_clk pll3_clk = {
 	.mode_reg = MM_PLL2_MODE_REG,
-	.parent = &pxo_clk.c,
 	.c = {
+		.parent = &pxo_clk.c,
 		.dbg_name = "pll3_clk",
 		.rate = 0, /* TODO: Detect rate dynamically */
 		.ops = &clk_ops_local_pll,
@@ -350,11 +360,6 @@
 	msm_rpm_set_noirq(MSM_RPM_CTX_SET_0, &iv, 1);
 }
 
-static struct clk *pll4_clk_get_parent(struct clk *c)
-{
-	return &pxo_clk.c;
-}
-
 static bool pll4_clk_is_local(struct clk *c)
 {
 	return false;
@@ -373,13 +378,13 @@
 static struct clk_ops clk_ops_pll4 = {
 	.enable = pll4_clk_enable,
 	.disable = pll4_clk_disable,
-	.get_parent = pll4_clk_get_parent,
 	.is_local = pll4_clk_is_local,
 	.handoff = pll4_clk_handoff,
 };
 
 static struct fixed_clk pll4_clk = {
 	.c = {
+		.parent = &pxo_clk.c,
 		.dbg_name = "pll4_clk",
 		.rate = 540672000,
 		.ops = &clk_ops_pll4,
@@ -597,7 +602,7 @@
 	},
 	.c = {
 		.dbg_name = "smi_2x_axi_clk",
-		.ops = &clk_ops_branch,
+		.ops = &clk_ops_smi_2x,
 		CLK_INIT(smi_2x_axi_clk.c),
 	},
 };
@@ -1376,8 +1381,8 @@
 		.halt_reg = CLK_HALT_CFPB_STATEA_REG,
 		.halt_bit = 15,
 	},
-	.parent = &usb_fs1_src_clk.c,
 	.c = {
+		.parent = &usb_fs1_src_clk.c,
 		.dbg_name = "usb_fs1_xcvr_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(usb_fs1_xcvr_clk.c),
@@ -1393,8 +1398,8 @@
 		.halt_reg = CLK_HALT_CFPB_STATEA_REG,
 		.halt_bit = 16,
 	},
-	.parent = &usb_fs1_src_clk.c,
 	.c = {
+		.parent = &usb_fs1_src_clk.c,
 		.dbg_name = "usb_fs1_sys_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(usb_fs1_sys_clk.c),
@@ -1411,8 +1416,8 @@
 		.halt_reg = CLK_HALT_CFPB_STATEA_REG,
 		.halt_bit = 12,
 	},
-	.parent = &usb_fs2_src_clk.c,
 	.c = {
+		.parent = &usb_fs2_src_clk.c,
 		.dbg_name = "usb_fs2_xcvr_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(usb_fs2_xcvr_clk.c),
@@ -1428,8 +1433,8 @@
 		.halt_reg = CLK_HALT_CFPB_STATEA_REG,
 		.halt_bit = 13,
 	},
-	.parent = &usb_fs2_src_clk.c,
 	.c = {
+		.parent = &usb_fs2_src_clk.c,
 		.dbg_name = "usb_fs2_sys_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(usb_fs2_sys_clk.c),
@@ -1444,8 +1449,8 @@
 		.halt_reg = CLK_HALT_CFPB_STATEC_REG,
 		.halt_bit = 0,
 	},
-	.parent = &pxo_clk.c,
 	.c = {
+		.parent = &pxo_clk.c,
 		.dbg_name = "ce2_p_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(ce2_p_clk.c),
@@ -1798,8 +1803,8 @@
 		.halt_check = HALT_VOTED,
 		.halt_bit = 14,
 	},
-	.parent = &pxo_clk.c,
 	.c = {
+		.parent = &pxo_clk.c,
 		.dbg_name = "adm0_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(adm0_clk.c),
@@ -1829,8 +1834,8 @@
 		.halt_check = HALT_VOTED,
 		.halt_bit = 12,
 	},
-	.parent = &pxo_clk.c,
 	.c = {
+		.parent = &pxo_clk.c,
 		.dbg_name = "adm1_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(adm1_clk.c),
@@ -2034,8 +2039,8 @@
 		.halt_reg = DBG_BUS_VEC_B_REG,
 		.halt_bit = 13,
 	},
-	.parent = &csi_src_clk.c,
 	.c = {
+		.parent = &csi_src_clk.c,
 		.dbg_name = "csi0_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(csi0_clk.c),
@@ -2051,8 +2056,8 @@
 		.halt_reg = DBG_BUS_VEC_B_REG,
 		.halt_bit = 14,
 	},
-	.parent = &csi_src_clk.c,
 	.c = {
+		.parent = &csi_src_clk.c,
 		.dbg_name = "csi1_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(csi1_clk.c),
@@ -2556,8 +2561,8 @@
 		.halt_reg = DBG_BUS_VEC_C_REG,
 		.halt_bit = 21,
 	},
-	.parent = &pixel_mdp_clk.c,
 	.c = {
+		.parent = &pixel_mdp_clk.c,
 		.dbg_name = "pixel_lcdc_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(pixel_lcdc_clk.c),
@@ -2685,8 +2690,8 @@
 		.halt_reg = DBG_BUS_VEC_D_REG,
 		.halt_bit = 8,
 	},
-	.parent = &tv_src_clk.c,
 	.c = {
+		.parent = &tv_src_clk.c,
 		.dbg_name = "tv_enc_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(tv_enc_clk.c),
@@ -2700,8 +2705,8 @@
 		.halt_reg = DBG_BUS_VEC_D_REG,
 		.halt_bit = 9,
 	},
-	.parent = &tv_src_clk.c,
 	.c = {
+		.parent = &tv_src_clk.c,
 		.dbg_name = "tv_dac_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(tv_dac_clk.c),
@@ -2717,8 +2722,8 @@
 		.halt_reg = DBG_BUS_VEC_D_REG,
 		.halt_bit = 11,
 	},
-	.parent = &tv_src_clk.c,
 	.c = {
+		.parent = &tv_src_clk.c,
 		.dbg_name = "mdp_tv_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(mdp_tv_clk.c),
@@ -2734,8 +2739,8 @@
 		.halt_reg = DBG_BUS_VEC_D_REG,
 		.halt_bit = 10,
 	},
-	.parent = &tv_src_clk.c,
 	.c = {
+		.parent = &tv_src_clk.c,
 		.dbg_name = "hdmi_tv_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(hdmi_tv_clk.c),
@@ -2924,8 +2929,8 @@
 		.halt_reg = DBG_BUS_VEC_B_REG,
 		.halt_bit = 7,
 	},
-	.parent = &vfe_clk.c,
 	.c = {
+		.parent = &vfe_clk.c,
 		.dbg_name = "csi0_vfe_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(csi0_vfe_clk.c),
@@ -2941,8 +2946,8 @@
 		.halt_reg = DBG_BUS_VEC_B_REG,
 		.halt_bit = 8,
 	},
-	.parent = &vfe_clk.c,
 	.c = {
+		.parent = &vfe_clk.c,
 		.dbg_name = "csi1_vfe_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(csi1_vfe_clk.c),
@@ -3091,7 +3096,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 035ef5c..fffbcea 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
@@ -221,10 +228,10 @@
 	.en_mask = BIT(0),
 	.status_reg = BB_PLL0_STATUS_REG,
 	.status_mask = BIT(16),
-	.parent = &cxo_clk.c,
 	.soft_vote = &soft_vote_pll0,
 	.soft_vote_mask = PLL_SOFT_VOTE_PRIMARY,
 	.c = {
+		.parent = &cxo_clk.c,
 		.dbg_name = "pll0_clk",
 		.rate = 276000000,
 		.ops = &clk_ops_pll_acpu_vote,
@@ -252,8 +259,8 @@
 	.en_mask = BIT(4),
 	.status_reg = LCC_PLL0_STATUS_REG,
 	.status_mask = BIT(16),
-	.parent = &cxo_clk.c,
 	.c = {
+		.parent = &cxo_clk.c,
 		.dbg_name = "pll4_clk",
 		.rate = 393216000,
 		.ops = &clk_ops_pll_vote,
@@ -268,10 +275,10 @@
 	.en_mask = BIT(8),
 	.status_reg = BB_PLL8_STATUS_REG,
 	.status_mask = BIT(16),
-	.parent = &cxo_clk.c,
 	.soft_vote = &soft_vote_pll8,
 	.soft_vote_mask = PLL_SOFT_VOTE_PRIMARY,
 	.c = {
+		.parent = &cxo_clk.c,
 		.dbg_name = "pll8_clk",
 		.rate = 384000000,
 		.ops = &clk_ops_pll_acpu_vote,
@@ -309,8 +316,8 @@
 	.en_mask = BIT(11),
 	.status_reg = BB_PLL14_STATUS_REG,
 	.status_mask = BIT(16),
-	.parent = &cxo_clk.c,
 	.c = {
+		.parent = &cxo_clk.c,
 		.dbg_name = "pll14_clk",
 		.rate = 480000000,
 		.ops = &clk_ops_pll_vote,
@@ -760,8 +767,8 @@
 		.halt_reg = CLK_HALT_DFAB_STATE_REG,
 		.halt_bit = 8,
 	},
-	.parent = &cxo_clk.c,
 	.c = {
+		.parent = &cxo_clk.c,
 		.dbg_name = "usb_hsic_hsio_cal_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(usb_hsic_hsio_cal_clk.c),
@@ -1288,8 +1295,8 @@
 		.halt_check = ENABLE,
 		.halt_bit = 1,
 	},
-	.parent = &audio_slimbus_clk.c,
 	.c = {
+		.parent = &audio_slimbus_clk.c,
 		.dbg_name = "sps_slimbus_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(sps_slimbus_clk.c),
@@ -1303,8 +1310,8 @@
 		.halt_reg = CLK_HALT_DFAB_STATE_REG,
 		.halt_bit = 28,
 	},
-	.parent = &sps_slimbus_clk.c,
 	.c = {
+		.parent = &sps_slimbus_clk.c,
 		.dbg_name = "slimbus_xo_src_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(slimbus_xo_src_clk.c),
diff --git a/arch/arm/mach-msm/clock-9625.c b/arch/arm/mach-msm/clock-9625.c
index fb4f32a..d3a4bba 100644
--- a/arch/arm/mach-msm/clock-9625.c
+++ b/arch/arm/mach-msm/clock-9625.c
@@ -246,6 +246,14 @@
 #define lpapll0_lpass_source_val 1
 #define gpll0_lpass_source_val 5
 
+#define F_GCC_GND \
+	{ \
+		.freq_hz = 0, \
+		.m_val = 0, \
+		.n_val  = 0, \
+		.div_src_val = BVAL(4, 0, 1) | BVAL(10, 8, gnd_source_val), \
+	}
+
 #define F(f, s, div, m, n) \
 	{ \
 		.freq_hz = (f), \
@@ -291,23 +299,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[] = {
@@ -325,7 +343,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);
 
 /* TODO: Needs to confirm the below values */
 #define RPM_MISC_CLK_TYPE	0x306b6c63
@@ -378,11 +396,11 @@
 	.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 = {
+		.parent = &cxo_clk_src.c,
 		.rate = 600000000,
 		.dbg_name = "gpll0_clk_src",
 		.ops = &clk_ops_pll_acpu_vote,
@@ -410,9 +428,9 @@
 	.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 = {
+		.parent = &cxo_clk_src.c,
 		.rate = 393216000,
 		.dbg_name = "lpapll0_clk_src",
 		.ops = &clk_ops_pll_vote,
@@ -425,9 +443,9 @@
 	.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 = {
+		.parent = &cxo_clk_src.c,
 		.rate = 480000000,
 		.dbg_name = "gpll1_clk_src",
 		.ops = &clk_ops_pll_vote,
@@ -601,6 +619,7 @@
 };
 
 static struct clk_freq_tbl ftbl_gcc_blsp1_uart1_6_apps_clk[] = {
+	F_GCC_GND,
 	F( 3686400,    gpll0,    1,    96,   15625),
 	F( 7372800,    gpll0,    1,   192,   15625),
 	F(14745600,    gpll0,    1,   384,   15625),
@@ -978,10 +997,10 @@
 
 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 = {
+		.parent = &cxo_clk_src.c,
 		.dbg_name = "gcc_blsp1_qup1_i2c_apps_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(gcc_blsp1_qup1_i2c_apps_clk.c),
@@ -990,10 +1009,10 @@
 
 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 = {
+		.parent = &blsp1_qup1_spi_apps_clk_src.c,
 		.dbg_name = "gcc_blsp1_qup1_spi_apps_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(gcc_blsp1_qup1_spi_apps_clk.c),
@@ -1002,10 +1021,10 @@
 
 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 = {
+		.parent = &cxo_clk_src.c,
 		.dbg_name = "gcc_blsp1_qup2_i2c_apps_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(gcc_blsp1_qup2_i2c_apps_clk.c),
@@ -1014,10 +1033,10 @@
 
 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 = {
+		.parent = &blsp1_qup2_spi_apps_clk_src.c,
 		.dbg_name = "gcc_blsp1_qup2_spi_apps_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(gcc_blsp1_qup2_spi_apps_clk.c),
@@ -1026,10 +1045,10 @@
 
 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 = {
+		.parent = &cxo_clk_src.c,
 		.dbg_name = "gcc_blsp1_qup3_i2c_apps_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(gcc_blsp1_qup3_i2c_apps_clk.c),
@@ -1038,10 +1057,10 @@
 
 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 = {
+		.parent = &blsp1_qup3_spi_apps_clk_src.c,
 		.dbg_name = "gcc_blsp1_qup3_spi_apps_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(gcc_blsp1_qup3_spi_apps_clk.c),
@@ -1050,10 +1069,10 @@
 
 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 = {
+		.parent = &cxo_clk_src.c,
 		.dbg_name = "gcc_blsp1_qup4_i2c_apps_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(gcc_blsp1_qup4_i2c_apps_clk.c),
@@ -1062,10 +1081,10 @@
 
 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 = {
+		.parent = &blsp1_qup4_spi_apps_clk_src.c,
 		.dbg_name = "gcc_blsp1_qup4_spi_apps_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(gcc_blsp1_qup4_spi_apps_clk.c),
@@ -1074,10 +1093,10 @@
 
 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 = {
+		.parent = &cxo_clk_src.c,
 		.dbg_name = "gcc_blsp1_qup5_i2c_apps_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(gcc_blsp1_qup5_i2c_apps_clk.c),
@@ -1086,10 +1105,10 @@
 
 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 = {
+		.parent = &blsp1_qup5_spi_apps_clk_src.c,
 		.dbg_name = "gcc_blsp1_qup5_spi_apps_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(gcc_blsp1_qup5_spi_apps_clk.c),
@@ -1098,10 +1117,10 @@
 
 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 = {
+		.parent = &cxo_clk_src.c,
 		.dbg_name = "gcc_blsp1_qup6_i2c_apps_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(gcc_blsp1_qup6_i2c_apps_clk.c),
@@ -1110,10 +1129,10 @@
 
 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 = {
+		.parent = &blsp1_qup6_spi_apps_clk_src.c,
 		.dbg_name = "gcc_blsp1_qup6_spi_apps_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(gcc_blsp1_qup6_spi_apps_clk.c),
@@ -1122,10 +1141,10 @@
 
 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 = {
+		.parent = &blsp1_uart1_apps_clk_src.c,
 		.dbg_name = "gcc_blsp1_uart1_apps_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(gcc_blsp1_uart1_apps_clk.c),
@@ -1134,10 +1153,10 @@
 
 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 = {
+		.parent = &blsp1_uart2_apps_clk_src.c,
 		.dbg_name = "gcc_blsp1_uart2_apps_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(gcc_blsp1_uart2_apps_clk.c),
@@ -1146,10 +1165,10 @@
 
 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 = {
+		.parent = &blsp1_uart3_apps_clk_src.c,
 		.dbg_name = "gcc_blsp1_uart3_apps_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(gcc_blsp1_uart3_apps_clk.c),
@@ -1158,10 +1177,10 @@
 
 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 = {
+		.parent = &blsp1_uart4_apps_clk_src.c,
 		.dbg_name = "gcc_blsp1_uart4_apps_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(gcc_blsp1_uart4_apps_clk.c),
@@ -1170,10 +1189,10 @@
 
 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 = {
+		.parent = &blsp1_uart5_apps_clk_src.c,
 		.dbg_name = "gcc_blsp1_uart5_apps_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(gcc_blsp1_uart5_apps_clk.c),
@@ -1182,10 +1201,10 @@
 
 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 = {
+		.parent = &blsp1_uart6_apps_clk_src.c,
 		.dbg_name = "gcc_blsp1_uart6_apps_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(gcc_blsp1_uart6_apps_clk.c),
@@ -1242,10 +1261,10 @@
 
 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 = {
+		.parent = &gp1_clk_src.c,
 		.dbg_name = "gcc_gp1_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(gcc_gp1_clk.c),
@@ -1254,10 +1273,10 @@
 
 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 = {
+		.parent = &gp2_clk_src.c,
 		.dbg_name = "gcc_gp2_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(gcc_gp2_clk.c),
@@ -1266,10 +1285,10 @@
 
 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 = {
+		.parent = &gp3_clk_src.c,
 		.dbg_name = "gcc_gp3_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(gcc_gp3_clk.c),
@@ -1278,10 +1297,10 @@
 
 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 = {
+		.parent = &ipa_clk_src.c,
 		.dbg_name = "gcc_ipa_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(gcc_ipa_clk.c),
@@ -1301,10 +1320,10 @@
 
 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 = {
+		.parent = &pdm2_clk_src.c,
 		.dbg_name = "gcc_pdm2_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(gcc_pdm2_clk.c),
@@ -1347,10 +1366,10 @@
 
 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 = {
+		.parent = &qpic_clk_src.c,
 		.dbg_name = "gcc_qpic_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(gcc_qpic_clk.c),
@@ -1370,10 +1389,10 @@
 
 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 = {
+		.parent = &sdcc2_apps_clk_src.c,
 		.dbg_name = "gcc_sdcc2_apps_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(gcc_sdcc2_apps_clk.c),
@@ -1393,10 +1412,10 @@
 
 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 = {
+		.parent = &sdcc3_apps_clk_src.c,
 		.dbg_name = "gcc_sdcc3_apps_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(gcc_sdcc3_apps_clk.c),
@@ -1405,10 +1424,10 @@
 
 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 = {
+		.parent = &ipa_clk_src.c,
 		.dbg_name = "gcc_sys_noc_ipa_axi_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(gcc_sys_noc_ipa_axi_clk.c),
@@ -1429,10 +1448,10 @@
 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 = {
+		.parent = &usb_hs_system_clk_src.c,
 		.dbg_name = "gcc_usb_hs_system_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(gcc_usb_hs_system_clk.c),
@@ -1452,10 +1471,10 @@
 
 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 = {
+		.parent = &usb_hsic_clk_src.c,
 		.dbg_name = "gcc_usb_hsic_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(gcc_usb_hsic_clk.c),
@@ -1464,10 +1483,10 @@
 
 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 = {
+		.parent = &usb_hsic_io_cal_clk_src.c,
 		.dbg_name = "gcc_usb_hsic_io_cal_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(gcc_usb_hsic_io_cal_clk.c),
@@ -1477,10 +1496,10 @@
 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 = {
+		.parent = &usb_hsic_system_clk_src.c,
 		.dbg_name = "gcc_usb_hsic_system_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(gcc_usb_hsic_system_clk.c),
@@ -1489,10 +1508,10 @@
 
 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 = {
+		.parent = &usb_hsic_xcvr_fs_clk_src.c,
 		.dbg_name = "gcc_usb_hsic_xcvr_fs_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(gcc_usb_hsic_xcvr_fs_clk.c),
@@ -1629,9 +1648,9 @@
 
 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 = {
+		.parent = &audio_core_lpaif_pcmoe_clk_src.c,
 		.dbg_name = "audio_core_lpaif_pcm_data_oe_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(audio_core_lpaif_pcm_data_oe_clk.c),
@@ -1640,9 +1659,9 @@
 
 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 = {
+		.parent = &audio_core_slimbus_core_clk_src.c,
 		.dbg_name = "audio_core_slimbus_core_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(audio_core_slimbus_core_clk.c),
@@ -1662,11 +1681,11 @@
 
 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 = {
+		.parent = &audio_core_lpaif_pri_clk_src.c,
 		.dbg_name = "audio_core_lpaif_pri_ibit_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(audio_core_lpaif_pri_ibit_clk.c),
@@ -1675,10 +1694,10 @@
 
 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 = {
+		.parent = &audio_core_lpaif_pri_clk_src.c,
 		.dbg_name = "audio_core_lpaif_pri_osr_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(audio_core_lpaif_pri_osr_clk.c),
@@ -1698,10 +1717,10 @@
 
 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 = {
+		.parent = &audio_core_lpaif_pcm0_clk_src.c,
 		.dbg_name = "audio_core_lpaif_pcm0_ibit_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(audio_core_lpaif_pcm0_ibit_clk.c),
@@ -1721,11 +1740,11 @@
 
 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 = {
+		.parent = &audio_core_lpaif_sec_clk_src.c,
 		.dbg_name = "audio_core_lpaif_sec_ibit_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(audio_core_lpaif_sec_ibit_clk.c),
@@ -1734,10 +1753,10 @@
 
 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 = {
+		.parent = &audio_core_lpaif_sec_clk_src.c,
 		.dbg_name = "audio_core_lpaif_sec_osr_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(audio_core_lpaif_sec_osr_clk.c),
@@ -1757,10 +1776,10 @@
 
 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 = {
+		.parent = &audio_core_lpaif_pcm1_clk_src.c,
 		.dbg_name = "audio_core_lpaif_pcm1_ibit_clk",
 		.ops = &clk_ops_branch,
 		CLK_INIT(audio_core_lpaif_pcm1_ibit_clk.c),
@@ -2017,13 +2036,13 @@
 
 	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, "f9924000.spi"),
 	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_qup1_spi_apps_clk.c, ""),
 	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_qup2_spi_apps_clk.c, "f9924000.spi"),
 	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, ""),
@@ -2048,6 +2067,7 @@
 	CLK_LOOKUP("core_clk", gcc_gp2_clk.c, ""),
 	CLK_LOOKUP("core_clk", gcc_gp3_clk.c, ""),
 
+	CLK_LOOKUP("iface_clk", gcc_prng_ahb_clk.c, "f9bff000.qcom,msm-rng"),
 	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"),
@@ -2075,10 +2095,15 @@
 	/* 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_pri_clk_src.c,
+		   "msm-dai-q6-mi2s.0"),
+	CLK_LOOKUP("osr_clk", audio_core_lpaif_pri_osr_clk.c,
+		   "msm-dai-q6-mi2s.0"),
+	CLK_LOOKUP("ebit_clk", audio_core_lpaif_pri_ebit_clk.c,
+		   "msm-dai-q6-mi2s.0"),
+	CLK_LOOKUP("ibit_clk", audio_core_lpaif_pri_ibit_clk.c,
+		   "msm-dai-q6-mi2s.0"),
 	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, ""),
@@ -2114,6 +2139,26 @@
 	CLK_LOOKUP("dfab_clk", pnoc_sps_clk.c, "msm_sps"),
 
 	CLK_LOOKUP("a5_m_clk", a5_m_clk, ""),
+
+	/* Coresight QDSS clocks */
+	CLK_LOOKUP("core_clk", qdss_clk.c, "fc322000.tmc"),
+	CLK_LOOKUP("core_clk", qdss_clk.c, "fc318000.tpiu"),
+	CLK_LOOKUP("core_clk", qdss_clk.c, "fc31c000.replicator"),
+	CLK_LOOKUP("core_clk", qdss_clk.c, "fc307000.tmc"),
+	CLK_LOOKUP("core_clk", qdss_clk.c, "fc31b000.funnel"),
+	CLK_LOOKUP("core_clk", qdss_clk.c, "fc319000.funnel"),
+	CLK_LOOKUP("core_clk", qdss_clk.c, "fc31a000.funnel"),
+	CLK_LOOKUP("core_clk", qdss_clk.c, "fc321000.stm"),
+
+	CLK_LOOKUP("core_a_clk", qdss_clk.c, "fc322000.tmc"),
+	CLK_LOOKUP("core_a_clk", qdss_clk.c, "fc318000.tpiu"),
+	CLK_LOOKUP("core_a_clk", qdss_clk.c, "fc31c000.replicator"),
+	CLK_LOOKUP("core_a_clk", qdss_clk.c, "fc307000.tmc"),
+	CLK_LOOKUP("core_a_clk", qdss_clk.c, "fc31b000.funnel"),
+	CLK_LOOKUP("core_a_clk", qdss_clk.c, "fc319000.funnel"),
+	CLK_LOOKUP("core_a_clk", qdss_clk.c, "fc31a000.funnel"),
+	CLK_LOOKUP("core_a_clk", qdss_clk.c, "fc321000.stm"),
+
 };
 
 static struct pll_config_regs gpll0_regs __initdata = {
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 d2260cb..4432795 100644
--- a/arch/arm/mach-msm/clock-local.c
+++ b/arch/arm/mach-msm/clock-local.c
@@ -527,6 +527,7 @@
 	 * is called to make sure the MNCNTR_EN bit is set correctly.
 	 */
 	rcg->current_freq = nf;
+	c->parent = nf->src_clk;
 
 	/* Enable any clocks that were disabled. */
 	if (!rcg->bank_info) {
@@ -583,11 +584,6 @@
 	return (rcg->freq_tbl + n)->freq_hz;
 }
 
-static struct clk *rcg_clk_get_parent(struct clk *c)
-{
-	return to_rcg_clk(c)->current_freq->src_clk;
-}
-
 /* Disable hw clock gating if not set at boot */
 enum handoff branch_handoff(struct branch *b, struct clk *c)
 {
@@ -644,6 +640,7 @@
 		return HANDOFF_UNKNOWN_RATE;
 
 	rcg->current_freq = freq;
+	c->parent = freq->src_clk;
 	c->rate = freq->freq_hz;
 
 	return HANDOFF_ENABLED_CLK;
@@ -683,11 +680,6 @@
 	spin_unlock_irqrestore(&local_clock_reg_lock, flags);
 }
 
-static struct clk *branch_clk_get_parent(struct clk *c)
-{
-	return to_branch_clk(c)->parent;
-}
-
 static int branch_clk_is_enabled(struct clk *c)
 {
 	return to_branch_clk(c)->enabled;
@@ -834,11 +826,17 @@
 	.in_hwcg_mode = branch_clk_in_hwcg_mode,
 	.is_enabled = branch_clk_is_enabled,
 	.reset = branch_clk_reset,
-	.get_parent = branch_clk_get_parent,
 	.handoff = branch_clk_handoff,
 	.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,
+	.handoff = branch_clk_handoff,
+};
+
 struct clk_ops clk_ops_reset = {
 	.reset = branch_clk_reset,
 };
@@ -862,7 +860,6 @@
 	.is_enabled = rcg_clk_is_enabled,
 	.round_rate = rcg_clk_round_rate,
 	.reset = rcg_clk_reset,
-	.get_parent = rcg_clk_get_parent,
 	.set_flags = rcg_clk_set_flags,
 };
 
diff --git a/arch/arm/mach-msm/clock-local.h b/arch/arm/mach-msm/clock-local.h
index 1873343..ff6dc69 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);
@@ -234,7 +235,6 @@
  * struct branch_clk - branch
  * @enabled: true if clock is on, false otherwise
  * @b: branch
- * @parent: clock source
  * @c: clock
  *
  * An on/off switch with a rate derived from the parent.
@@ -242,7 +242,6 @@
 struct branch_clk {
 	bool enabled;
 	struct branch b;
-	struct clk *parent;
 	struct clk c;
 };
 
diff --git a/arch/arm/mach-msm/clock-local2.c b/arch/arm/mach-msm/clock-local2.c
index b9c3036..5923951 100644
--- a/arch/arm/mach-msm/clock-local2.c
+++ b/arch/arm/mach-msm/clock-local2.c
@@ -206,6 +206,7 @@
 		clk_unprepare(cf->src_clk);
 
 	rcg->current_freq = nf;
+	c->parent = nf->src_clk;
 
 	return 0;
 }
@@ -234,11 +235,6 @@
 	return (rcg->freq_tbl + n)->freq_hz;
 }
 
-static struct clk *rcg_clk_get_parent(struct clk *c)
-{
-	return to_rcg_clk(c)->current_freq->src_clk;
-}
-
 static enum handoff _rcg_clk_handoff(struct rcg_clk *rcg, int has_mnd)
 {
 	u32 n_regval = 0, m_regval = 0, d_regval = 0;
@@ -306,6 +302,7 @@
 		return HANDOFF_UNKNOWN_RATE;
 
 	rcg->current_freq = freq;
+	rcg->c.parent = freq->src_clk;
 	rcg->c.rate = freq->freq_hz;
 
 	return HANDOFF_ENABLED_CLK;
@@ -431,7 +428,7 @@
 		return branch_cdiv_set_rate(branch, rate);
 
 	if (!branch->has_sibling)
-		return clk_set_rate(branch->parent, rate);
+		return clk_set_rate(c->parent, rate);
 
 	return -EPERM;
 }
@@ -444,7 +441,7 @@
 		return rate <= (branch->max_div) ? rate : -EPERM;
 
 	if (!branch->has_sibling)
-		return clk_round_rate(branch->parent, rate);
+		return clk_round_rate(c->parent, rate);
 
 	return -EPERM;
 }
@@ -457,16 +454,11 @@
 		return branch->c.rate;
 
 	if (!branch->has_sibling)
-		return clk_get_rate(branch->parent);
+		return clk_get_rate(c->parent);
 
 	return 0;
 }
 
-static struct clk *branch_clk_get_parent(struct clk *c)
-{
-	return to_branch_clk(c)->parent;
-}
-
 static int branch_clk_list_rate(struct clk *c, unsigned n)
 {
 	struct branch_clk *branch = to_branch_clk(c);
@@ -474,8 +466,8 @@
 	if (branch->has_sibling == 1)
 		return -ENXIO;
 
-	if (branch->parent && branch->parent->ops->list_rate)
-		return branch->parent->ops->list_rate(branch->parent, n);
+	if (c->parent && c->parent->ops->list_rate)
+		return c->parent->ops->list_rate(c->parent, n);
 	else
 		return -ENXIO;
 }
@@ -489,9 +481,9 @@
 	if ((cbcr_regval & CBCR_BRANCH_OFF_BIT))
 		return HANDOFF_DISABLED_CLK;
 
-	if (branch->parent) {
-		if (branch->parent->ops->handoff)
-			return branch->parent->ops->handoff(branch->parent);
+	if (c->parent) {
+		if (c->parent->ops->handoff)
+			return c->parent->ops->handoff(c->parent);
 	}
 
 	return HANDOFF_ENABLED_CLK;
@@ -603,7 +595,6 @@
 	.set_rate = rcg_clk_set_rate,
 	.list_rate = rcg_clk_list_rate,
 	.round_rate = rcg_clk_round_rate,
-	.get_parent = rcg_clk_get_parent,
 	.handoff = rcg_clk_handoff,
 };
 
@@ -612,7 +603,6 @@
 	.set_rate = rcg_clk_set_rate,
 	.list_rate = rcg_clk_list_rate,
 	.round_rate = rcg_clk_round_rate,
-	.get_parent = rcg_clk_get_parent,
 	.handoff = rcg_mnd_clk_handoff,
 };
 
@@ -624,7 +614,6 @@
 	.list_rate = branch_clk_list_rate,
 	.round_rate = branch_clk_round_rate,
 	.reset = branch_clk_reset,
-	.get_parent = branch_clk_get_parent,
 	.handoff = branch_clk_handoff,
 };
 
diff --git a/arch/arm/mach-msm/clock-local2.h b/arch/arm/mach-msm/clock-local2.h
index 101dc2d..a6d2ed6 100644
--- a/arch/arm/mach-msm/clock-local2.h
+++ b/arch/arm/mach-msm/clock-local2.h
@@ -87,7 +87,6 @@
 /**
  * struct branch_clk - branch clock
  * @set_rate: Set the frequency of this branch clock.
- * @parent: clock source
  * @c: clk
  * @cbcr_reg: branch control register
  * @bcr_reg: block reset register
@@ -99,7 +98,6 @@
  */
 struct branch_clk {
 	void   (*set_rate)(struct branch_clk *, struct clk_freq_tbl *);
-	struct clk *parent;
 	struct clk c;
 	const u32 cbcr_reg;
 	const u32 bcr_reg;
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 240f4e4..8e11d37 100644
--- a/arch/arm/mach-msm/clock-pll.c
+++ b/arch/arm/mach-msm/clock-pll.c
@@ -98,11 +98,6 @@
 	spin_unlock_irqrestore(&pll_reg_lock, flags);
 }
 
-static struct clk *pll_vote_clk_get_parent(struct clk *c)
-{
-	return to_pll_vote_clk(c)->parent;
-}
-
 static int pll_vote_clk_is_enabled(struct clk *c)
 {
 	struct pll_vote_clk *pllv = to_pll_vote_clk(c);
@@ -122,7 +117,6 @@
 	.enable = pll_vote_clk_enable,
 	.disable = pll_vote_clk_disable,
 	.is_enabled = pll_vote_clk_is_enabled,
-	.get_parent = pll_vote_clk_get_parent,
 	.handoff = pll_vote_clk_handoff,
 };
 
@@ -229,11 +223,6 @@
 	return HANDOFF_DISABLED_CLK;
 }
 
-static struct clk *local_pll_clk_get_parent(struct clk *c)
-{
-	return to_pll_clk(c)->parent;
-}
-
 static int local_pll_clk_set_rate(struct clk *c, unsigned long rate)
 {
 	struct pll_freq_tbl *nf;
@@ -346,7 +335,6 @@
 	.disable = local_pll_clk_disable,
 	.set_rate = local_pll_clk_set_rate,
 	.handoff = local_pll_clk_handoff,
-	.get_parent = local_pll_clk_get_parent,
 };
 
 struct pll_rate {
@@ -537,7 +525,6 @@
 	.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,
diff --git a/arch/arm/mach-msm/clock-pll.h b/arch/arm/mach-msm/clock-pll.h
index 33b35a8..cb334d7 100644
--- a/arch/arm/mach-msm/clock-pll.h
+++ b/arch/arm/mach-msm/clock-pll.h
@@ -35,7 +35,6 @@
  * any HW voting
  * @id: PLL ID
  * @mode_reg: enable register
- * @parent: clock source
  * @c: clock
  */
 struct pll_shared_clk {
@@ -104,7 +103,6 @@
  * @en_mask: ORed with @en_reg to enable the clock
  * @status_mask: ANDed with @status_reg to determine if PLL is active.
  * @status_reg: status register
- * @parent: clock source
  * @c: clock
  */
 struct pll_vote_clk {
@@ -115,7 +113,6 @@
 	void __iomem *const status_reg;
 	const u32 status_mask;
 
-	struct clk *parent;
 	struct clk c;
 	void *const __iomem *base;
 };
@@ -144,7 +141,6 @@
  * @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.
  */
@@ -159,7 +155,6 @@
 	struct pll_config_masks masks;
 	struct pll_freq_tbl *freq_tbl;
 
-	struct clk *parent;
 	struct clk c;
 	void *const __iomem *base;
 };
diff --git a/arch/arm/mach-msm/clock-rpm.c b/arch/arm/mach-msm/clock-rpm.c
index daf83e2..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;
 }
@@ -319,8 +303,8 @@
 }
 
 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,
@@ -330,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-voter.c b/arch/arm/mach-msm/clock-voter.c
index fa170bf4..7421ba6 100644
--- a/arch/arm/mach-msm/clock-voter.c
+++ b/arch/arm/mach-msm/clock-voter.c
@@ -43,7 +43,7 @@
 	mutex_lock(&voter_clk_lock);
 
 	if (v->enabled) {
-		struct clk *parent = v->parent;
+		struct clk *parent = clk->parent;
 
 		/*
 		 * Get the aggregate rate without this clock's vote and update
@@ -79,7 +79,7 @@
 	struct clk_voter *v = to_clk_voter(clk);
 
 	mutex_lock(&voter_clk_lock);
-	parent = v->parent;
+	parent = clk->parent;
 
 	/*
 	 * Increase the rate if this clock is voting for a higher rate
@@ -105,7 +105,7 @@
 	struct clk_voter *v = to_clk_voter(clk);
 
 	mutex_lock(&voter_clk_lock);
-	parent = v->parent;
+	parent = clk->parent;
 
 	/*
 	 * Decrease the rate if this clock was the only one voting for
@@ -129,14 +129,7 @@
 
 static long voter_clk_round_rate(struct clk *clk, unsigned long rate)
 {
-	struct clk_voter *v = to_clk_voter(clk);
-	return clk_round_rate(v->parent, rate);
-}
-
-static struct clk *voter_clk_get_parent(struct clk *clk)
-{
-	struct clk_voter *v = to_clk_voter(clk);
-	return v->parent;
+	return clk_round_rate(clk->parent, rate);
 }
 
 static bool voter_clk_is_local(struct clk *clk)
@@ -159,7 +152,6 @@
 	.set_rate = voter_clk_set_rate,
 	.is_enabled = voter_clk_is_enabled,
 	.round_rate = voter_clk_round_rate,
-	.get_parent = voter_clk_get_parent,
 	.is_local = voter_clk_is_local,
 	.handoff = voter_clk_handoff,
 };
diff --git a/arch/arm/mach-msm/clock-voter.h b/arch/arm/mach-msm/clock-voter.h
index 82c071b..eb55a12 100644
--- a/arch/arm/mach-msm/clock-voter.h
+++ b/arch/arm/mach-msm/clock-voter.h
@@ -21,7 +21,6 @@
 
 struct clk_voter {
 	bool enabled;
-	struct clk *parent;
 	struct clk c;
 };
 
@@ -32,8 +31,8 @@
 
 #define DEFINE_CLK_VOTER(clk_name, _parent, _default_rate) \
 	struct clk_voter clk_name = { \
-		.parent = _parent, \
 		.c = { \
+			.parent = _parent, \
 			.dbg_name = #clk_name, \
 			.ops = &clk_ops_voter, \
 			.rate = _default_rate, \
diff --git a/arch/arm/mach-msm/clock.c b/arch/arm/mach-msm/clock.c
index 5100980..c2bf5ba 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",
@@ -157,7 +163,7 @@
 
 	mutex_lock(&clk->prepare_lock);
 	if (clk->prepare_count == 0) {
-		parent = clk_get_parent(clk);
+		parent = clk->parent;
 
 		ret = clk_prepare(parent);
 		if (ret)
@@ -207,7 +213,7 @@
 	WARN(!clk->prepare_count,
 			"%s: Don't call enable on unprepared clocks\n", name);
 	if (clk->count == 0) {
-		parent = clk_get_parent(clk);
+		parent = clk->parent;
 
 		ret = clk_enable(parent);
 		if (ret)
@@ -252,7 +258,7 @@
 	if (WARN(clk->count == 0, "%s is unbalanced", name))
 		goto out;
 	if (clk->count == 1) {
-		struct clk *parent = clk_get_parent(clk);
+		struct clk *parent = clk->parent;
 
 		trace_clock_disable(name, 0, smp_processor_id());
 		if (clk->ops->disable)
@@ -277,7 +283,7 @@
 	if (WARN(!clk->prepare_count, "%s is unbalanced (prepare)", name))
 		goto out;
 	if (clk->prepare_count == 1) {
-		struct clk *parent = clk_get_parent(clk);
+		struct clk *parent = clk->parent;
 
 		WARN(clk->count,
 			"%s: Don't call unprepare when the clock is enabled\n",
@@ -405,10 +411,7 @@
 	if (IS_ERR_OR_NULL(clk))
 		return NULL;
 
-	if (!clk->ops->get_parent)
-		return NULL;
-
-	return clk->ops->get_parent(clk);
+	return clk->parent;
 }
 EXPORT_SYMBOL(clk_get_parent);
 
@@ -432,7 +435,7 @@
 
 	for (n = 0; n < num_clocks; n++) {
 		clk = clock_tbl[n].clk;
-		parent = clk_get_parent(clk);
+		parent = clk->parent;
 		if (parent && list_empty(&clk->siblings))
 			list_add(&clk->siblings, &parent->children);
 	}
@@ -486,11 +489,11 @@
 
 	/*
 	 * Handoff functions for children must be called before their parents'
-	 * so that the correct parent is returned by the clk_get_parent() below.
+	 * so that the correct parent is available below.
 	 */
 	ret = clk->ops->handoff(clk);
 	if (ret == HANDOFF_ENABLED_CLK) {
-		ret = __handoff_clk(clk_get_parent(clk));
+		ret = __handoff_clk(clk->parent);
 		if (ret == HANDOFF_ENABLED_CLK) {
 			h = kmalloc(sizeof(*h), GFP_KERNEL);
 			if (!h) {
diff --git a/arch/arm/mach-msm/cpufreq.c b/arch/arm/mach-msm/cpufreq.c
index 6c9b413..e0d98b7 100644
--- a/arch/arm/mach-msm/cpufreq.c
+++ b/arch/arm/mach-msm/cpufreq.c
@@ -52,8 +52,11 @@
 static int set_cpu_freq(struct cpufreq_policy *policy, unsigned int new_freq)
 {
 	int ret = 0;
+	int saved_sched_policy = -EINVAL;
+	int saved_sched_rt_prio = -EINVAL;
 	struct cpufreq_freqs freqs;
 	struct cpu_freq *limit = &per_cpu(cpu_freq_info, policy->cpu);
+	struct sched_param param = { .sched_priority = MAX_RT_PRIO-1 };
 
 	if (limit->limits_init) {
 		if (new_freq > limit->allowed_max) {
@@ -70,11 +73,29 @@
 	freqs.old = policy->cur;
 	freqs.new = new_freq;
 	freqs.cpu = policy->cpu;
+
+	/*
+	 * Put the caller into SCHED_FIFO priority to avoid cpu starvation
+	 * in the acpuclk_set_rate path while increasing frequencies
+	 */
+
+	if (freqs.new > freqs.old && current->policy != SCHED_FIFO) {
+		saved_sched_policy = current->policy;
+		saved_sched_rt_prio = current->rt_priority;
+		sched_setscheduler_nocheck(current, SCHED_FIFO, &param);
+	}
+
 	cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
+
 	ret = acpuclk_set_rate(policy->cpu, new_freq, SETRATE_CPUFREQ);
 	if (!ret)
 		cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
 
+	/* Restore priority after clock ramp-up */
+	if (freqs.new > freqs.old && saved_sched_policy >= 0) {
+		param.sched_priority = saved_sched_rt_prio;
+		sched_setscheduler_nocheck(current, saved_sched_policy, &param);
+	}
 	return ret;
 }
 
diff --git a/arch/arm/mach-msm/devices-8064.c b/arch/arm/mach-msm/devices-8064.c
index d727c54..8d10d6a 100644
--- a/arch/arm/mach-msm/devices-8064.c
+++ b/arch/arm/mach-msm/devices-8064.c
@@ -59,6 +59,7 @@
 /* GSBI UART devices */
 #define MSM_UART1DM_PHYS	(MSM_GSBI1_PHYS + 0x10000)
 #define MSM_UART3DM_PHYS	(MSM_GSBI3_PHYS + 0x40000)
+#define MSM_UART5DM_PHYS	(MSM_GSBI5_PHYS + 0x40000)
 #define MSM_UART6DM_PHYS	(MSM_GSBI6_PHYS + 0x40000)
 #define MSM_UART7DM_PHYS	(MSM_GSBI7_PHYS + 0x40000)
 
@@ -373,6 +374,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",
@@ -413,6 +466,33 @@
 	.resource	= resources_qup_i2c_gsbi5,
 };
 
+static struct resource resources_uart_gsbi5[] = {
+	{
+		.start  = GSBI5_UARTDM_IRQ,
+		.end    = GSBI5_UARTDM_IRQ,
+		.flags  = IORESOURCE_IRQ,
+	},
+	{
+		.start  = MSM_UART5DM_PHYS,
+		.end    = MSM_UART5DM_PHYS + PAGE_SIZE - 1,
+		.name   = "uartdm_resource",
+		.flags  = IORESOURCE_MEM,
+	},
+	{
+		.start  = MSM_GSBI5_PHYS,
+		.end    = MSM_GSBI5_PHYS + PAGE_SIZE - 1,
+		.name   = "gsbi_resource",
+		.flags  = IORESOURCE_MEM,
+	},
+};
+
+struct platform_device mpq8064_device_uart_gsbi5 = {
+	.name	= "msm_serial_hsl",
+	.id	= 2,
+	.num_resources	= ARRAY_SIZE(resources_uart_gsbi5),
+	.resource	= resources_uart_gsbi5,
+};
+
 /* GSBI 6 used into UARTDM Mode */
 static struct resource msm_uart_dm6_resources[] = {
 	{
@@ -542,6 +622,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)
@@ -1047,10 +1131,10 @@
 		.flags	= IORESOURCE_IRQ,
 	},
 	{
-		.start	= 47,
-		.end	= 47,
+		.start	= MSM_GPIO_TO_INT(47),
+		.end	= MSM_GPIO_TO_INT(47),
 		.name	= "wakeup",
-		.flags	= IORESOURCE_IO,
+		.flags	= IORESOURCE_IRQ,
 	},
 };
 
@@ -1818,7 +1902,7 @@
 	},
 	{
 		.irq_config_id = SMD_Q6,
-		.subsys_name = "q6",
+		.subsys_name = "adsp",
 		.edge = SMD_APPS_QDSP,
 
 		.smd_int.irq_name = "adsp_a11",
@@ -1964,6 +2048,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,
@@ -2340,16 +2429,28 @@
 };
 
 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[] = {
@@ -2653,18 +2754,27 @@
 	.resource	= i2s_mdm_resources,
 };
 
-static struct msm_dcvs_freq_entry apq8064_freq[] = {
-	{ 384000, 900,  0, 0, 0},
-	{ 594000, 950,  0, 0, 0},
-	{ 702000, 975,  0, 0, 0},
-	{1026000, 1075, 0, 0, 0},
-	{1242000, 1150, 0, 100, 100},
-	{1458000, 1188, 0, 100, 100},
-	{1512000, 1200, 1, 100, 100},
+static struct msm_dcvs_sync_rule apq8064_dcvs_sync_rules[] = {
+	{1026000,	400000},
+	{384000,	200000},
+	{0,		128000},
+};
+
+static struct msm_dcvs_platform_data apq8064_dcvs_data = {
+	.sync_rules	= apq8064_dcvs_sync_rules,
+	.num_sync_rules = ARRAY_SIZE(apq8064_dcvs_sync_rules),
+	.gpu_max_nom_khz = 320000,
+};
+
+struct platform_device apq8064_dcvs_device = {
+	.name		= "dcvs",
+	.id		= -1,
+	.dev		= {
+		.platform_data = &apq8064_dcvs_data,
+	},
 };
 
 static struct msm_dcvs_core_info apq8064_core_info = {
-	.freq_tbl		= &apq8064_freq[0],
 	.num_cores		= 4,
 	.sensors		= (int[]){7, 8, 9, 10},
 	.thermal_poll_ms	= 60000,
@@ -2699,7 +2809,7 @@
 	},
 	.power_param		= {
 		.current_temp	= 25,
-		.num_freq	= ARRAY_SIZE(apq8064_freq),
+		.num_freq	= 0, /* set at runtime */
 	}
 };
 
diff --git a/arch/arm/mach-msm/devices-8930.c b/arch/arm/mach-msm/devices-8930.c
index 30a99cd..aad512e 100644
--- a/arch/arm/mach-msm/devices-8930.c
+++ b/arch/arm/mach-msm/devices-8930.c
@@ -143,12 +143,12 @@
 		MSM_RPM_MAP(8930, PM8038_CLK2_0, PM8038_CLK2, 2),
 		MSM_RPM_MAP(8930, PM8038_LVS1, PM8038_LVS1, 1),
 		MSM_RPM_MAP(8930, PM8038_LVS2, PM8038_LVS2, 1),
-		MSM_RPM_MAP(8930, NCP_0, NCP, 2),
-		MSM_RPM_MAP(8930, CXO_BUFFERS, CXO_BUFFERS, 1),
-		MSM_RPM_MAP(8930, USB_OTG_SWITCH, USB_OTG_SWITCH, 1),
-		MSM_RPM_MAP(8930, HDMI_SWITCH, HDMI_SWITCH, 1),
-		MSM_RPM_MAP(8930, QDSS_CLK, QDSS_CLK, 1),
-		MSM_RPM_MAP(8930, VOLTAGE_CORNER, VOLTAGE_CORNER, 1),
+		MSM_RPM_MAP_PMIC(8930, 8038, NCP_0, NCP, 2),
+		MSM_RPM_MAP_PMIC(8930, 8038, CXO_BUFFERS, CXO_BUFFERS, 1),
+		MSM_RPM_MAP_PMIC(8930, 8038, USB_OTG_SWITCH, USB_OTG_SWITCH, 1),
+		MSM_RPM_MAP_PMIC(8930, 8038, HDMI_SWITCH, HDMI_SWITCH, 1),
+		MSM_RPM_MAP_PMIC(8930, 8038, QDSS_CLK, QDSS_CLK, 1),
+		MSM_RPM_MAP_PMIC(8930, 8038, VOLTAGE_CORNER, VOLTAGE_CORNER, 1),
 	},
 	.target_status = {
 		MSM_RPM_STATUS_ID_MAP(8930, VERSION_MAJOR),
@@ -369,12 +369,12 @@
 		MSM_RPM_MAP(8930, PM8917_LVS5, PM8917_LVS5, 1),
 		MSM_RPM_MAP(8930, PM8917_LVS6, PM8917_LVS6, 1),
 		MSM_RPM_MAP(8930, PM8917_LVS7, PM8917_LVS7, 1),
-		MSM_RPM_MAP(8930, NCP_0, NCP, 2),
-		MSM_RPM_MAP(8930, CXO_BUFFERS, CXO_BUFFERS, 1),
-		MSM_RPM_MAP(8930, USB_OTG_SWITCH, USB_OTG_SWITCH, 1),
-		MSM_RPM_MAP(8930, HDMI_SWITCH, HDMI_SWITCH, 1),
-		MSM_RPM_MAP(8930, QDSS_CLK, QDSS_CLK, 1),
-		MSM_RPM_MAP(8930, VOLTAGE_CORNER, VOLTAGE_CORNER, 1),
+		MSM_RPM_MAP_PMIC(8930, 8917, NCP_0, NCP, 2),
+		MSM_RPM_MAP_PMIC(8930, 8917, CXO_BUFFERS, CXO_BUFFERS, 1),
+		MSM_RPM_MAP_PMIC(8930, 8917, USB_OTG_SWITCH, USB_OTG_SWITCH, 1),
+		MSM_RPM_MAP_PMIC(8930, 8917, HDMI_SWITCH, HDMI_SWITCH, 1),
+		MSM_RPM_MAP_PMIC(8930, 8917, QDSS_CLK, QDSS_CLK, 1),
+		MSM_RPM_MAP_PMIC(8930, 8917, VOLTAGE_CORNER, VOLTAGE_CORNER, 1),
 	},
 	.target_status = {
 		MSM_RPM_STATUS_ID_MAP(8930, VERSION_MAJOR),
@@ -547,16 +547,28 @@
 };
 
 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[] = {
@@ -657,6 +669,18 @@
 	.id		= -1,
 };
 
+static struct acpuclk_platform_data acpuclk_8930ab_pdata = {
+	.uses_pm8917 = false,
+};
+
+struct platform_device msm8930ab_device_acpuclk = {
+	.name		= "acpuclk-8930ab",
+	.id		= -1,
+	.dev = {
+		.platform_data = &acpuclk_8930ab_pdata,
+	},
+};
+
 static struct fs_driver_data gfx3d_fs_data = {
 	.clks = (struct fs_clk_data[]){
 		{ .name = "core_clk", .reset_rate = 27000000 },
@@ -693,6 +717,20 @@
 	.bus_port1 = MSM_BUS_MASTER_MDP_PORT1,
 };
 
+static struct fs_driver_data mdp_fs_data_8930_pm8917 = {
+	.clks = (struct fs_clk_data[]){
+		{ .name = "core_clk" },
+		{ .name = "iface_clk" },
+		{ .name = "bus_clk" },
+		{ .name = "vsync_clk" },
+		{ .name = "lut_clk" },
+		{ .name = "reset1_clk" },
+		{ 0 }
+	},
+	.bus_port0 = MSM_BUS_MASTER_MDP_PORT0,
+	.bus_port1 = MSM_BUS_MASTER_MDP_PORT1,
+};
+
 static struct fs_driver_data mdp_fs_data_8627 = {
 	.clks = (struct fs_clk_data[]){
 		{ .name = "core_clk" },
@@ -759,6 +797,18 @@
 };
 unsigned msm8930_num_footswitch __initdata = ARRAY_SIZE(msm8930_footswitch);
 
+struct platform_device *msm8930_pm8917_footswitch[] __initdata = {
+	FS_8X60(FS_MDP,    "vdd",	"mdp.0",      &mdp_fs_data_8930_pm8917),
+	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 msm8930_pm8917_num_footswitch __initdata =
+		ARRAY_SIZE(msm8930_pm8917_footswitch);
+
 struct platform_device *msm8627_footswitch[] __initdata = {
 	FS_8X60(FS_MDP,    "vdd",	"mdp.0",	&mdp_fs_data_8627),
 	FS_8X60(FS_ROT,    "vdd",	"msm_rotator.0", &rot_fs_data),
@@ -1083,6 +1133,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 0f71bc4..c59461a 100644
--- a/arch/arm/mach-msm/devices-8960.c
+++ b/arch/arm/mach-msm/devices-8960.c
@@ -1373,6 +1373,11 @@
 		.flags  = IORESOURCE_MEM,
 	},
 	{
+		.start  = 0x00900000,
+		.end    = 0x00900000 + SZ_16K - 1,
+		.flags  = IORESOURCE_MEM,
+	},
+	{
 		.start  = 0x08B00000,
 		.end    = 0x08B00000 + SZ_256 - 1,
 		.flags  = IORESOURCE_MEM,
@@ -1412,7 +1417,6 @@
 		.aclk_reg = MSM_CLK_CTL_BASE + 0x2C6C,
 		.jtag_clk_reg = MSM_CLK_CTL_BASE + 0x2044,
 		.name = "modem_fw",
-		.depends = "q6",
 		.pas_id = PAS_MODEM_FW,
 		.bus_port = MSM_BUS_MASTER_MSS_FW_PROC,
 	},
@@ -1423,7 +1427,6 @@
 		.aclk_reg = MSM_CLK_CTL_BASE + 0x2040,
 		.jtag_clk_reg = MSM_CLK_CTL_BASE + 0x2C68,
 		.name = "modem",
-		.depends = "modem_fw",
 		.pas_id = PAS_MODEM_SW,
 		.bus_port = MSM_BUS_MASTER_MSS_SW_PROC,
 	}
@@ -1444,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,
@@ -1464,6 +1472,11 @@
 
 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,
@@ -1557,7 +1570,7 @@
 	},
 	{
 		.irq_config_id = SMD_Q6,
-		.subsys_name = "q6",
+		.subsys_name = "adsp",
 		.edge = SMD_APPS_QDSP,
 
 		.smd_int.irq_name = "adsp_a11",
@@ -2520,6 +2533,17 @@
 	.bus_port1 = MSM_BUS_MASTER_HD_CODEC_PORT1,
 };
 
+static struct fs_driver_data ved_fs_data_8960ab = {
+	.clks = (struct fs_clk_data[]){
+		{ .name = "core_clk" },
+		{ .name = "iface_clk" },
+		{ .name = "bus_clk" },
+		{ 0 }
+	},
+	.bus_port0 = MSM_BUS_MASTER_VIDEO_DEC,
+	.bus_port1 = MSM_BUS_MASTER_VIDEO_ENC,
+};
+
 static struct fs_driver_data vfe_fs_data = {
 	.clks = (struct fs_clk_data[]){
 		{ .name = "core_clk" },
@@ -2553,6 +2577,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_8960ab),
+};
+unsigned msm8960ab_num_footswitch __initdata = ARRAY_SIZE(msm8960ab_footswitch);
+
 #ifdef CONFIG_MSM_ROTATOR
 static struct msm_bus_vectors rotator_init_vectors[] = {
 	{
@@ -3211,7 +3246,30 @@
 };
 #endif
 
-static struct resource kgsl_3d0_resources[] = {
+struct resource kgsl_3d0_resources_8960ab[] = {
+	{
+		.name = KGSL_3D0_REG_MEMORY,
+		.start = 0x04300000, /* GFX3D address */
+		.end = 0x0430ffff,
+		.flags = IORESOURCE_MEM,
+	},
+	{
+		.name = KGSL_3D0_SHADER_MEMORY,
+		.start = 0x04310000, /* Shader Mem Address (8960AB) */
+		.end = 0x0431ffff,
+		.flags = IORESOURCE_MEM,
+	},
+	{
+		.name = KGSL_3D0_IRQ,
+		.start = GFX3D_IRQ,
+		.end = GFX3D_IRQ,
+		.flags = IORESOURCE_IRQ,
+	},
+};
+
+int kgsl_num_resources_8960ab = ARRAY_SIZE(kgsl_3d0_resources_8960ab);
+
+static struct resource kgsl_3d0_resources_8960[] = {
 	{
 		.name = KGSL_3D0_REG_MEMORY,
 		.start = 0x04300000, /* GFX3D address */
@@ -3295,8 +3353,8 @@
 struct platform_device msm_kgsl_3d0 = {
 	.name = "kgsl-3d0",
 	.id = 0,
-	.num_resources = ARRAY_SIZE(kgsl_3d0_resources),
-	.resource = kgsl_3d0_resources,
+	.num_resources = ARRAY_SIZE(kgsl_3d0_resources_8960),
+	.resource = kgsl_3d0_resources_8960,
 	.dev = {
 		.platform_data = &kgsl_3d0_pdata,
 	},
@@ -3749,16 +3807,28 @@
 };
 
 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[] = {
diff --git a/arch/arm/mach-msm/devices-9615.c b/arch/arm/mach-msm/devices-9615.c
index fe3a4d5..e55e9a7 100644
--- a/arch/arm/mach-msm/devices-9615.c
+++ b/arch/arm/mach-msm/devices-9615.c
@@ -562,6 +562,21 @@
 	.id	= -1,
 };
 
+struct platform_device msm_cpudai_incall_music_rx = {
+	.name   = "msm-dai-q6",
+	.id     = 0x8005,
+};
+
+struct platform_device msm_cpudai_incall_record_rx = {
+	.name   = "msm-dai-q6",
+	.id     = 0x8004,
+};
+
+struct platform_device msm_cpudai_incall_record_tx = {
+	.name   = "msm-dai-q6",
+	.id     = 0x8003,
+};
+
 struct platform_device msm_i2s_cpudai0 = {
 	.name   = "msm-dai-q6",
 	.id     = PRIMARY_I2S_RX,
@@ -584,6 +599,14 @@
 	.name	= "msm-voip-dsp",
 	.id	= -1,
 };
+struct platform_device msm_cpudai_stub = {
+	.name = "msm-dai-stub",
+	.id = -1,
+};
+struct platform_device msm_dtmf = {
+	.name	= "msm-pcm-dtmf",
+	.id	= -1,
+};
 
 struct platform_device msm_compr_dsp = {
 	.name	= "msm-compr-dsp",
@@ -1361,16 +1384,29 @@
 };
 
 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[] = {
@@ -1421,6 +1457,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 63c1dbd..983b13e 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,
@@ -365,25 +360,25 @@
 static struct msm_iommu_dev gfx3d_iommu = {
 	.name = "gfx3d",
 	.ncb = 3,
-	.ttbr_split = 1,
+	.ttbr_split = 0,
 };
 
 static struct msm_iommu_dev gfx3d1_iommu = {
 	.name = "gfx3d1",
 	.ncb = 3,
-	.ttbr_split = 1,
+	.ttbr_split = 0,
 };
 
 static struct msm_iommu_dev gfx2d0_iommu = {
 	.name = "gfx2d0",
 	.ncb = 2,
-	.ttbr_split = 1,
+	.ttbr_split = 0,
 };
 
 static struct msm_iommu_dev gfx2d1_iommu = {
 	.name = "gfx2d1",
 	.ncb = 2,
-	.ttbr_split = 1,
+	.ttbr_split = 0,
 };
 
 static struct msm_iommu_dev vcap_iommu = {
@@ -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));
@@ -1051,9 +1026,6 @@
 			ARRAY_SIZE(msm_iommu_vcap_ctx_devs));
 
 	return 0;
-
-failure:
-	return ret;
 }
 
 static void __exit iommu_exit(void)
@@ -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 9943812..8fc5020 100644
--- a/arch/arm/mach-msm/devices-msm7x27a.c
+++ b/arch/arm/mach-msm/devices-msm7x27a.c
@@ -501,6 +501,18 @@
 	.exit_sleep3 = msm_gic_irq_exit_sleep3,
 };
 
+void msm_clk_dump_debug_info(void)
+{
+	pr_info("%s: GLBL_CLK_ENA: 0x%08X\n", __func__,
+		readl_relaxed(MSM_CLK_CTL_BASE + 0x0));
+	pr_info("%s: GLBL_CLK_STATE: 0x%08X\n", __func__,
+		readl_relaxed(MSM_CLK_CTL_BASE + 0x4));
+	pr_info("%s: GRP_NS_REG: 0x%08X\n", __func__,
+		readl_relaxed(MSM_CLK_CTL_BASE + 0x84));
+	pr_info("%s: CLK_HALT_STATEB: 0x%08X\n", __func__,
+		readl_relaxed(MSM_CLK_CTL_BASE + 0x10C));
+}
+
 void __init msm_pm_register_irqs(void)
 {
 	if (cpu_is_msm8625())
@@ -780,6 +792,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 +819,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)
@@ -1643,6 +1679,7 @@
 	case 0x771:
 	case 0x77C:
 	case 0x780:
+	case 0x785: /* Edge-only MSM8125-0 */
 	case 0x8D0:
 		cpu = MSM8625;
 		break;
@@ -1770,7 +1807,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,
@@ -1778,12 +1815,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,
@@ -1816,6 +1854,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)
@@ -1848,11 +1887,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;
 	}
@@ -1890,8 +1929,6 @@
 	else if (msm8625_cpu_id() == MSM8625)
 		msm_cpr_pdata.max_freq = 1008000;
 
-	msm_cpr_clk_enable();
-
 	platform_device_register(&msm8625_vp_device);
 	platform_device_register(&msm8625_device_cpr);
 }
@@ -1963,6 +2000,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()) {
@@ -1994,6 +2066,9 @@
 
 	platform_device_register(&pl310_erp_device);
 
+	if (msm_gpio_config_gps() < 0)
+		pr_err("Error for gpio config for GPS gpio\n");
+
 	return 0;
 }
 
@@ -2072,6 +2147,7 @@
 static int msm7627a_panic_handler(struct notifier_block *this,
 		unsigned long event, void *ptr)
 {
+	msm_clk_dump_debug_info();
 	flush_cache_all();
 	outer_flush_all();
 	return NOTIFY_DONE;
diff --git a/arch/arm/mach-msm/devices-msm8x60.c b/arch/arm/mach-msm/devices-msm8x60.c
index 5554eb8..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",
 };
 
diff --git a/arch/arm/mach-msm/devices.h b/arch/arm/mach-msm/devices.h
index 8f02050..c0d73c2 100644
--- a/arch/arm/mach-msm/devices.h
+++ b/arch/arm/mach-msm/devices.h
@@ -56,6 +56,7 @@
 extern struct platform_device msm_device_uart_dm8;
 extern struct platform_device msm_device_uart_dm9;
 extern struct platform_device mpq8064_device_uartdm_gsbi6;
+extern struct platform_device mpq8064_device_uart_gsbi5;
 
 extern struct platform_device msm8960_device_uart_gsbi2;
 extern struct platform_device msm8960_device_uart_gsbi5;
@@ -109,6 +110,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 +219,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;
@@ -231,6 +235,7 @@
 extern struct platform_device msm_stub_codec;
 extern struct platform_device msm_voice;
 extern struct platform_device msm_voip;
+extern struct platform_device msm_dtmf;
 extern struct platform_device msm_lpa_pcm;
 extern struct platform_device msm_pcm_hostless;
 extern struct platform_device msm_cpudai_afe_01_rx;
@@ -246,6 +251,8 @@
 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_cpudai_stub;
+
 extern struct platform_device msm_pil_q6v3;
 extern struct platform_device msm_pil_modem;
 extern struct platform_device msm_pil_tzapps;
@@ -300,10 +307,14 @@
 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[];
 extern unsigned msm8930_num_footswitch;
+extern struct platform_device *msm8930_pm8917_footswitch[];
+extern unsigned msm8930_pm8917_num_footswitch;
 extern struct platform_device *msm8627_footswitch[];
 extern unsigned msm8627_num_footswitch;
 
@@ -317,6 +328,9 @@
 extern struct platform_device msm_kgsl_2d0;
 extern struct platform_device msm_kgsl_2d1;
 
+extern struct resource kgsl_3d0_resources_8960ab[];
+extern int kgsl_num_resources_8960ab;
+
 extern struct platform_device msm_mipi_dsi1_device;
 extern struct platform_device mipi_dsi_device;
 extern struct platform_device msm_lcdc_device;
@@ -400,6 +414,7 @@
 extern struct platform_device *msm_8974_stub_regulator_devices[];
 extern int msm_8974_stub_regulator_devices_len;
 
+extern struct platform_device apq8064_dcvs_device;
 extern struct platform_device apq8064_msm_gov_device;
 
 extern struct platform_device msm_bus_8930_apps_fabric;
@@ -413,6 +428,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;
@@ -443,6 +459,7 @@
 extern struct platform_device msm8x60_device_acpuclk;
 extern struct platform_device msm8930_device_acpuclk;
 extern struct platform_device msm8930aa_device_acpuclk;
+extern struct platform_device msm8930ab_device_acpuclk;
 extern struct platform_device msm8960_device_acpuclk;
 extern struct platform_device msm8960ab_device_acpuclk;
 extern struct platform_device msm9615_device_acpuclk;
diff --git a/arch/arm/mach-msm/idle-v7.S b/arch/arm/mach-msm/idle-v7.S
index 1d8f313..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
@@ -377,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 7a939ab..ee3209c 100644
--- a/arch/arm/mach-msm/idle.h
+++ b/arch/arm/mach-msm/idle.h
@@ -33,6 +33,9 @@
 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
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 ff4776a..8b5c70f 100644
--- a/arch/arm/mach-msm/include/mach/board.h
+++ b/arch/arm/mach-msm/include/mach/board.h
@@ -129,6 +129,10 @@
 
 struct msm_camera_sensor_flash_src {
 	int flash_sr_type;
+	struct gpio *init_gpio_tbl;
+	uint8_t init_gpio_tbl_size;
+	struct msm_gpio_set_tbl *set_gpio_tbl;
+	uint8_t set_gpio_tbl_size;
 
 	union {
 		struct msm_camera_sensor_flash_pmic pmic_src;
@@ -144,6 +148,9 @@
 struct msm_camera_sensor_flash_data {
 	int flash_type;
 	struct msm_camera_sensor_flash_src *flash_src;
+	struct i2c_board_info const *board_info;
+	int bus_id;
+	uint8_t flash_src_index;
 };
 
 struct msm_camera_sensor_strobe_flash_data {
@@ -458,6 +465,7 @@
 	char dlane_swap;
 	void (*dsi_pwm_cfg)(void);
 	char enable_wled_bl_ctrl;
+	void (*gpio_set_backlight)(int bl_level);
 };
 
 struct lvds_panel_platform_data {
@@ -587,9 +595,17 @@
 void msm_map_msm8226_io(void);
 void msm8226_init_irq(void);
 void msm8226_init_gpiomux(void);
+void msm8910_init_gpiomux(void);
 void msm_map_msm8910_io(void);
 void msm8910_init_irq(void);
 
+/* Dump debug info (states, rate, etc) of clocks */
+#if defined(CONFIG_ARCH_MSM7X27)
+void msm_clk_dump_debug_info(void);
+#else
+static inline void msm_clk_dump_debug_info(void) {}
+#endif
+
 struct mmc_platform_data;
 int msm_add_sdcc(unsigned int controller,
 		struct mmc_platform_data *plat);
diff --git a/arch/arm/mach-msm/include/mach/camera.h b/arch/arm/mach-msm/include/mach/camera.h
index f5a158f..3dfa659c 100644
--- a/arch/arm/mach-msm/include/mach/camera.h
+++ b/arch/arm/mach-msm/include/mach/camera.h
@@ -1,4 +1,4 @@
-/* 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 and
@@ -508,35 +508,6 @@
 	struct msm_pmem_region *region;
 };
 
-#ifdef CONFIG_MSM_CAMERA_FLASH
-int msm_camera_flash_set_led_state(
-	struct msm_camera_sensor_flash_data *fdata,
-	unsigned led_state);
-int msm_strobe_flash_init(struct msm_sync *sync, uint32_t sftype);
-int msm_flash_ctrl(struct msm_camera_sensor_info *sdata,
-			struct flash_ctrl_data *flash_info);
-#else
-static inline int msm_camera_flash_set_led_state(
-	struct msm_camera_sensor_flash_data *fdata,
-	unsigned led_state)
-{
-	return -ENOTSUPP;
-}
-static inline int msm_strobe_flash_init(
-	struct msm_sync *sync, uint32_t sftype)
-{
-	return -ENOTSUPP;
-}
-static inline int msm_flash_ctrl(
-		struct msm_camera_sensor_info *sdata,
-		struct flash_ctrl_data *flash_info)
-{
-	return -ENOTSUPP;
-}
-#endif
-
-
-
 void msm_camvfe_init(void);
 int msm_camvfe_check(void *);
 void msm_camvfe_fn_init(struct msm_camvfe_fn *, void *);
@@ -634,6 +605,7 @@
 	S_DEFAULT,
 	S_LIVESHOT,
 	S_DUAL,
+	S_ADV_VIDEO,
 	S_EXIT
 };
 
@@ -704,4 +676,10 @@
 	(struct msm_camera_sensor_info *sinfo, int gpio_en);
 void msm_camera_bus_scale_cfg(uint32_t bus_perf_client,
 		enum msm_bus_perf_setting perf_setting);
+
+int msm_camera_init_gpio_table(struct gpio *gpio_tbl, uint8_t gpio_tbl_size,
+	int gpio_en);
+
+int msm_camera_set_gpio_table(struct msm_gpio_set_tbl *gpio_tbl,
+	uint8_t gpio_tbl_size, int gpio_en);
 #endif
diff --git a/arch/arm/mach-msm/include/mach/clk-provider.h b/arch/arm/mach-msm/include/mach/clk-provider.h
index 770713d..0f2feaa 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) \
 	}
 
@@ -102,6 +103,7 @@
  * @depends: non-direct parent of clock to enable when this clock is enabled
  * @vdd_class: voltage scaling requirement class
  * @fmax: maximum frequency in Hz supported at each voltage level
+ * @parent: the current source of this clock
  */
 struct clk {
 	uint32_t flags;
@@ -109,8 +111,10 @@
 	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 clk *parent;
 
 	struct list_head children;
 	struct list_head siblings;
diff --git a/arch/arm/mach-msm/include/mach/dma.h b/arch/arm/mach-msm/include/mach/dma.h
index 988b249..5fcf579 100644
--- a/arch/arm/mach-msm/include/mach/dma.h
+++ b/arch/arm/mach-msm/include/mach/dma.h
@@ -269,7 +269,7 @@
 #define DMOV8064_CE_OUT_CHAN       1
 #define DMOV8064_CE_OUT_CRCI       15
 
-#define DMOV8064_TSIF_CHAN         2
+#define DMOV8064_TSIF_CHAN         4
 #define DMOV8064_TSIF_CRCI         1
 
 /* channels for MPQ8064 */
diff --git a/arch/arm/mach-msm/include/mach/iommu.h b/arch/arm/mach-msm/include/mach/iommu.h
index 69ddeb9..ea3fb64 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,11 +15,11 @@
 
 #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 */
@@ -91,6 +91,8 @@
  * @name:	Human-readable name of this IOMMU device
  * @gdsc:	Regulator needed to power 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.
@@ -106,8 +108,13 @@
 	struct regulator *gdsc;
 	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
@@ -140,6 +147,59 @@
 irqreturn_t msm_iommu_fault_handler(int irq, void *dev_id);
 irqreturn_t msm_iommu_fault_handler_v2(int irq, void *dev_id);
 
+enum {
+	PROC_APPS,
+	PROC_GPU,
+	PROC_MAX
+};
+
+/* Expose structure to allow kgsl iommu driver to use the same structure to
+ * communicate to GPU the addresses of the flag and turn variables.
+ */
+struct remote_iommu_petersons_spinlock {
+	uint32_t flag[PROC_MAX];
+	uint32_t turn;
+};
+
+#ifdef CONFIG_MSM_IOMMU
+void *msm_iommu_lock_initialize(void);
+void msm_iommu_mutex_lock(void);
+void msm_iommu_mutex_unlock(void);
+#else
+static inline void *msm_iommu_lock_initialize(void)
+{
+	return NULL;
+}
+static inline void msm_iommu_mutex_lock(void) { }
+static inline void msm_iommu_mutex_unlock(void) { }
+#endif
+
+#ifdef CONFIG_MSM_IOMMU_GPU_SYNC
+void msm_iommu_remote_p0_spin_lock(void);
+void msm_iommu_remote_p0_spin_unlock(void);
+
+#define msm_iommu_remote_lock_init() _msm_iommu_remote_spin_lock_init()
+#define msm_iommu_remote_spin_lock() msm_iommu_remote_p0_spin_lock()
+#define msm_iommu_remote_spin_unlock() msm_iommu_remote_p0_spin_unlock()
+#else
+#define msm_iommu_remote_lock_init()
+#define msm_iommu_remote_spin_lock()
+#define msm_iommu_remote_spin_unlock()
+#endif
+
+/* Allows kgsl iommu driver to acquire lock */
+#define msm_iommu_lock() \
+	do { \
+		msm_iommu_mutex_lock(); \
+		msm_iommu_remote_spin_lock(); \
+	} while (0)
+
+#define msm_iommu_unlock() \
+	do { \
+		msm_iommu_remote_spin_unlock(); \
+		msm_iommu_mutex_unlock(); \
+	} while (0)
+
 #ifdef CONFIG_MSM_IOMMU
 /*
  * Look up an IOMMU context device by its context name. NULL if none found.
@@ -154,7 +214,12 @@
 }
 #endif
 
-#endif
+/*
+ * Function to program the global registers of an IOMMU securely.
+ * This should only be called on IOMMUs for which kernel programming
+ * of global registers is not possible
+ */
+int msm_iommu_sec_program_iommu(int sec_id);
 
 static inline int msm_soc_version_supports_iommu_v1(void)
 {
@@ -178,3 +243,4 @@
 	}
 	return 1;
 }
+#endif
diff --git a/arch/arm/mach-msm/include/mach/ipa.h b/arch/arm/mach-msm/include/mach/ipa.h
new file mode 100644
index 0000000..dae6d3b
--- /dev/null
+++ b/arch/arm/mach-msm/include/mach/ipa.h
@@ -0,0 +1,732 @@
+/* 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 _IPA_H_
+#define _IPA_H_
+
+#include <linux/msm_ipa.h>
+#include <linux/skbuff.h>
+#include <linux/types.h>
+#include <mach/sps.h>
+
+/**
+ * enum ipa_nat_en_type - NAT setting type in IPA end-point
+ */
+enum ipa_nat_en_type {
+	IPA_BYPASS_NAT,
+	IPA_SRC_NAT,
+	IPA_DST_NAT,
+};
+
+/**
+ * enum ipa_mode_type - mode setting type in IPA end-point
+ * @BASIC: basic mode
+ * @ENABLE_FRAMING_HDLC: not currently supported
+ * @ENABLE_DEFRAMING_HDLC: not currently supported
+ */
+enum ipa_mode_type {
+	IPA_BASIC,
+	IPA_ENABLE_FRAMING_HDLC,
+	IPA_ENABLE_DEFRAMING_HDLC,
+	IPA_DMA,
+};
+
+/**
+ *  enum ipa_aggr_en_type - aggregation setting type in IPA
+ *  end-point
+ */
+enum ipa_aggr_en_type {
+	IPA_BYPASS_AGGR,
+	IPA_ENABLE_AGGR,
+	IPA_ENABLE_DEAGGR,
+};
+
+/**
+ *  enum ipa_aggr_type - type of aggregation in IPA end-point
+ */
+enum ipa_aggr_type {
+	IPA_MBIM_16,
+	IPA_MBIM_32,
+	IPA_TLP,
+};
+
+/**
+ * enum ipa_aggr_mode - global aggregation mode
+ */
+enum ipa_aggr_mode {
+	IPA_MBIM,
+	IPA_QCNCM,
+};
+
+/**
+ * enum ipa_dp_evt_type - type of event client callback is
+ * invoked for on data path
+ * @IPA_RECEIVE: data is struct sk_buff
+ * @IPA_WRITE_DONE: data is struct sk_buff
+ */
+enum ipa_dp_evt_type {
+	IPA_RECEIVE,
+	IPA_WRITE_DONE,
+};
+
+/**
+ * struct ipa_ep_cfg_nat - NAT configuration in IPA end-point
+ * @nat_en:	This defines the default NAT mode for the pipe: in case of
+ *		filter miss - the default NAT mode defines the NATing operation
+ *		on the packet. Valid for Input Pipes only (IPA consumer)
+ */
+struct ipa_ep_cfg_nat {
+	enum ipa_nat_en_type nat_en;
+};
+
+/**
+ * struct ipa_ep_cfg_hdr - header configuration in IPA end-point
+ * @hdr_len:	Header length in bytes to be added/removed. Assuming header len
+ *		is constant per endpoint. Valid for both Input and Output Pipes
+ * @hdr_ofst_metadata_valid:	0: Metadata_Ofst  value is invalid, i.e., no
+ *				metadata within header.
+ *				1: Metadata_Ofst  value is valid, i.e., metadata
+ *				within header is in offset Metadata_Ofst Valid
+ *				for Input Pipes only (IPA Consumer) (for output
+ *				pipes, metadata already set within the header)
+ * @hdr_ofst_metadata:	Offset within header in which metadata resides
+ *			Size of metadata - 4bytes
+ *			Example -  Stream ID/SSID/mux ID.
+ *			Valid for  Input Pipes only (IPA Consumer) (for output
+ *			pipes, metadata already set within the header)
+ * @hdr_additional_const_len:	Defines the constant length that should be added
+ *				to the payload length in order for IPA to update
+ *				correctly the length field within the header
+ *				(valid only in case Hdr_Ofst_Pkt_Size_Valid=1)
+ *				Valid for Output Pipes (IPA Producer)
+ * @hdr_ofst_pkt_size_valid:	0: Hdr_Ofst_Pkt_Size  value is invalid, i.e., no
+ *				length field within the inserted header
+ *				1: Hdr_Ofst_Pkt_Size  value is valid, i.e., a
+ *				packet length field resides within the header
+ *				Valid for Output Pipes (IPA Producer)
+ * @hdr_ofst_pkt_size:	Offset within header in which packet size reside. Upon
+ *			Header Insertion, IPA will update this field within the
+ *			header with the packet length . Assumption is that
+ *			header length field size is constant and is 2Bytes
+ *			Valid for Output Pipes (IPA Producer)
+ * @hdr_a5_mux:	Determines whether A5 Mux header should be added to the packet.
+ *		This bit is valid only when Hdr_En=01(Header Insertion)
+ *		SW should set this bit for IPA-to-A5 pipes.
+ *		0: Do not insert A5 Mux Header
+ *		1: Insert A5 Mux Header
+ *		Valid for Output Pipes (IPA Producer)
+ */
+struct ipa_ep_cfg_hdr {
+	u32 hdr_len;
+	u32 hdr_ofst_metadata_valid;
+	u32 hdr_ofst_metadata;
+	u32 hdr_additional_const_len;
+	u32 hdr_ofst_pkt_size_valid;
+	u32 hdr_ofst_pkt_size;
+	u32 hdr_a5_mux;
+};
+
+/**
+ * struct ipa_ep_cfg_mode - mode configuration in IPA end-point
+ * @mode:	Valid for Input Pipes only (IPA Consumer)
+ * @dst:	This parameter specifies the output pipe to which the packets
+ *		will be routed to.
+ *		This parameter is valid for Mode=DMA and not valid for
+ *		Mode=Basic
+ *		Valid for Input Pipes only (IPA Consumer)
+ */
+struct ipa_ep_cfg_mode {
+	enum ipa_mode_type mode;
+	enum ipa_client_type dst;
+};
+
+/**
+ * struct ipa_ep_cfg_aggr - aggregation configuration in IPA end-point
+ * @aggr_en:	Valid for both Input and Output Pipes
+ * @aggr:	Valid for both Input and Output Pipes
+ * @aggr_byte_limit:	Limit of aggregated packet size in KB (<=32KB) When set
+ *			to 0, there is no size limitation on the aggregation.
+ *			When both, Aggr_Byte_Limit and Aggr_Time_Limit are set
+ *			to 0, there is no aggregation, every packet is sent
+ *			independently according to the aggregation structure
+ *			Valid for Output Pipes only (IPA Producer )
+ * @aggr_time_limit:	Timer to close aggregated packet (<=32ms) When set to 0,
+ *			there is no time limitation on the aggregation.  When
+ *			both, Aggr_Byte_Limit and Aggr_Time_Limit are set to 0,
+ *			there is no aggregation, every packet is sent
+ *			independently according to the aggregation structure
+ *			Valid for Output Pipes only (IPA Producer)
+ */
+struct ipa_ep_cfg_aggr {
+	enum ipa_aggr_en_type aggr_en;
+	enum ipa_aggr_type aggr;
+	u32 aggr_byte_limit;
+	u32 aggr_time_limit;
+};
+
+/**
+ * struct ipa_ep_cfg_route - route configuration in IPA end-point
+ * @rt_tbl_hdl:	Defines the default routing table index to be used in case there
+ *		is no filter rule matching, valid for Input Pipes only (IPA
+ *		Consumer). Clients should set this to 0 which will cause default
+ *		v4 and v6 routes setup internally by IPA driver to be used for
+ *		this end-point
+ */
+struct ipa_ep_cfg_route {
+	u32 rt_tbl_hdl;
+};
+
+/**
+ * struct ipa_ep_cfg - configuration of IPA end-point
+ * @nat:	NAT parmeters
+ * @hdr:	Header parameters
+ * @mode:	Mode parameters
+ * @aggr:	Aggregation parameters
+ * @route:	Routing parameters
+ */
+struct ipa_ep_cfg {
+	struct ipa_ep_cfg_nat nat;
+	struct ipa_ep_cfg_hdr hdr;
+	struct ipa_ep_cfg_mode mode;
+	struct ipa_ep_cfg_aggr aggr;
+	struct ipa_ep_cfg_route route;
+};
+
+/**
+ * struct ipa_connect_params - low-level client connect input parameters. Either
+ * client allocates the data and desc FIFO and specifies that in data+desc OR
+ * specifies sizes and pipe_mem pref and IPA does the allocation.
+ *
+ * @ipa_ep_cfg:	IPA EP configuration
+ * @client:	type of "client"
+ * @client_bam_hdl:	 client SPS handle
+ * @client_ep_idx:	 client PER EP index
+ * @priv:	callback cookie
+ * @notify:	callback
+ *		priv - callback cookie evt - type of event data - data relevant
+ *		to event.  May not be valid. See event_type enum for valid
+ *		cases.
+ * @desc_fifo_sz:	size of desc FIFO
+ * @data_fifo_sz:	size of data FIFO
+ * @pipe_mem_preferred:	if true, try to alloc the FIFOs in pipe mem, fallback
+ *			to sys mem if pipe mem alloc fails
+ * @desc:	desc FIFO meta-data when client has allocated it
+ * @data:	data FIFO meta-data when client has allocated it
+ */
+struct ipa_connect_params {
+	struct ipa_ep_cfg ipa_ep_cfg;
+	enum ipa_client_type client;
+	u32 client_bam_hdl;
+	u32 client_ep_idx;
+	void *priv;
+	void (*notify)(void *priv, enum ipa_dp_evt_type evt,
+			unsigned long data);
+	u32 desc_fifo_sz;
+	u32 data_fifo_sz;
+	bool pipe_mem_preferred;
+	struct sps_mem_buffer desc;
+	struct sps_mem_buffer data;
+};
+
+/**
+ *  struct ipa_sps_params - SPS related output parameters resulting from
+ *  low/high level client connect
+ *  @ipa_bam_hdl:	IPA SPS handle
+ *  @ipa_ep_idx:	IPA PER EP index
+ *  @desc:	desc FIFO meta-data
+ *  @data:	data FIFO meta-data
+ */
+struct ipa_sps_params {
+	u32 ipa_bam_hdl;
+	u32 ipa_ep_idx;
+	struct sps_mem_buffer desc;
+	struct sps_mem_buffer data;
+};
+
+/**
+ * struct ipa_tx_intf - interface tx properties
+ * @num_props:	number of tx properties
+ * @prop:	the tx properties array
+ */
+struct ipa_tx_intf {
+	u32 num_props;
+	struct ipa_ioc_tx_intf_prop *prop;
+};
+
+/**
+ * struct ipa_rx_intf - interface rx properties
+ * @num_props:	number of rx properties
+ * @prop:	the rx properties array
+ */
+struct ipa_rx_intf {
+	u32 num_props;
+	struct ipa_ioc_rx_intf_prop *prop;
+};
+
+/**
+ * struct ipa_sys_connect_params - information needed to setup an IPA end-point
+ * in system-BAM mode
+ * @ipa_ep_cfg:	IPA EP configuration
+ * @client:	the type of client who "owns" the EP
+ * @desc_fifo_sz:	size of desc FIFO
+ * @priv:	callback cookie
+ * @notify:	callback
+ *		priv - callback cookie
+ *		evt - type of event
+ *		data - data relevant to event.  May not be valid. See event_type
+ *		enum for valid cases.
+ */
+struct ipa_sys_connect_params {
+	struct ipa_ep_cfg ipa_ep_cfg;
+	enum ipa_client_type client;
+	u32 desc_fifo_sz;
+	void *priv;
+	void (*notify)(void *priv,
+			enum ipa_dp_evt_type evt,
+			unsigned long data);
+};
+
+/**
+ * struct ipa_msg_meta_wrapper - message meta-data wrapper
+ * @meta:	the meta-data itself
+ * @link:	opaque to client
+ * @meta_wrapper_free:	function to free the metadata wrapper when IPA driver
+ *			is done with it
+ */
+struct ipa_msg_meta_wrapper {
+	struct ipa_msg_meta meta;
+	struct list_head link;
+	void (*meta_wrapper_free)(struct ipa_msg_meta_wrapper *buff);
+};
+
+/**
+ * struct ipa_tx_meta - meta-data for the TX packet
+ * @mbim_stream_id:	the stream ID used in NDP signature
+ * @mbim_stream_id_valid:	 is above field valid?
+ */
+struct ipa_tx_meta {
+	u8 mbim_stream_id;
+	bool mbim_stream_id_valid;
+};
+
+/**
+ * struct ipa_msg_wrapper - message wrapper
+ * @msg:	the message buffer itself, MUST exist after call returns, will
+ *		be freed by IPA driver when it is done with it
+ * @link:	opaque to client
+ * @msg_free:	function to free the message when IPA driver is done with it
+ * @msg_wrapper_free:	function to free the message wrapper when IPA driver is
+ *			done with it
+ */
+struct ipa_msg_wrapper {
+	void *msg;
+	struct list_head link;
+	void (*msg_free)(void *msg);
+	void (*msg_wrapper_free)(struct ipa_msg_wrapper *buff);
+};
+
+/**
+ * typedef ipa_pull_fn - callback function
+ * @buf - [in] the buffer to populate the message into
+ * @sz - [in] the size of the buffer
+ *
+ * callback function registered by kernel client with IPA driver for IPA driver
+ * to be able to pull messages from the kernel client asynchronously.
+ *
+ * Returns how many bytes were copied into the buffer, negative on failure.
+ */
+typedef int (*ipa_pull_fn)(void *buf, uint16_t sz);
+
+#ifdef CONFIG_IPA
+
+/*
+ * Connect / Disconnect
+ */
+int ipa_connect(const struct ipa_connect_params *in, struct ipa_sps_params *sps,
+		u32 *clnt_hdl);
+int ipa_disconnect(u32 clnt_hdl);
+
+/*
+ * Configuration
+ */
+int ipa_cfg_ep(u32 clnt_hdl, const struct ipa_ep_cfg *ipa_ep_cfg);
+
+int ipa_cfg_ep_nat(u32 clnt_hdl, const struct ipa_ep_cfg_nat *ipa_ep_cfg);
+
+int ipa_cfg_ep_hdr(u32 clnt_hdl, const struct ipa_ep_cfg_hdr *ipa_ep_cfg);
+
+int ipa_cfg_ep_mode(u32 clnt_hdl, const struct ipa_ep_cfg_mode *ipa_ep_cfg);
+
+int ipa_cfg_ep_aggr(u32 clnt_hdl, const struct ipa_ep_cfg_aggr *ipa_ep_cfg);
+
+int ipa_cfg_ep_route(u32 clnt_hdl, const struct ipa_ep_cfg_route *ipa_ep_cfg);
+
+/*
+ * Header removal / addition
+ */
+int ipa_add_hdr(struct ipa_ioc_add_hdr *hdrs);
+
+int ipa_del_hdr(struct ipa_ioc_del_hdr *hdls);
+
+int ipa_commit_hdr(void);
+
+int ipa_reset_hdr(void);
+
+int ipa_get_hdr(struct ipa_ioc_get_hdr *lookup);
+
+int ipa_put_hdr(u32 hdr_hdl);
+
+int ipa_copy_hdr(struct ipa_ioc_copy_hdr *copy);
+
+/*
+ * Routing
+ */
+int ipa_add_rt_rule(struct ipa_ioc_add_rt_rule *rules);
+
+int ipa_del_rt_rule(struct ipa_ioc_del_rt_rule *hdls);
+
+int ipa_commit_rt(enum ipa_ip_type ip);
+
+int ipa_reset_rt(enum ipa_ip_type ip);
+
+int ipa_get_rt_tbl(struct ipa_ioc_get_rt_tbl *lookup);
+
+int ipa_put_rt_tbl(u32 rt_tbl_hdl);
+
+/*
+ * Filtering
+ */
+int ipa_add_flt_rule(struct ipa_ioc_add_flt_rule *rules);
+
+int ipa_del_flt_rule(struct ipa_ioc_del_flt_rule *hdls);
+
+int ipa_commit_flt(enum ipa_ip_type ip);
+
+int ipa_reset_flt(enum ipa_ip_type ip);
+
+/*
+ * NAT
+ */
+int allocate_nat_device(struct ipa_ioc_nat_alloc_mem *mem);
+
+int ipa_nat_init_cmd(struct ipa_ioc_v4_nat_init *init);
+
+int ipa_nat_dma_cmd(struct ipa_ioc_nat_dma_cmd *dma);
+
+int ipa_nat_del_cmd(struct ipa_ioc_v4_nat_del *del);
+
+/*
+ * Aggregation
+ */
+int ipa_set_aggr_mode(enum ipa_aggr_mode mode);
+
+int ipa_set_qcncm_ndp_sig(char sig[3]);
+
+int ipa_set_single_ndp_per_mbim(bool enable);
+
+/*
+ * rmnet bridge
+ */
+int rmnet_bridge_init(void);
+
+int rmnet_bridge_disconnect(void);
+
+int rmnet_bridge_connect(u32 producer_hdl,
+			 u32 consumer_hdl,
+			 int wwan_logical_channel_id);
+
+/*
+ * Data path
+ */
+int ipa_tx_dp(enum ipa_client_type dst, struct sk_buff *skb,
+		struct ipa_tx_meta *metadata);
+
+/*
+ * System pipes
+ */
+int ipa_setup_sys_pipe(struct ipa_sys_connect_params *sys_in, u32 *clnt_hdl);
+
+int ipa_teardown_sys_pipe(u32 clnt_hdl);
+
+#else
+
+/*
+ * Connect / Disconnect
+ */
+static inline int ipa_connect(const struct ipa_connect_params *in,
+		struct ipa_sps_params *sps,	u32 *clnt_hdl)
+{
+	return -EPERM;
+}
+
+static inline int ipa_disconnect(u32 clnt_hdl)
+{
+	return -EPERM;
+}
+
+
+/*
+ * Configuration
+ */
+static inline int ipa_cfg_ep(u32 clnt_hdl,
+		const struct ipa_ep_cfg *ipa_ep_cfg)
+{
+	return -EPERM;
+}
+
+
+static inline int ipa_cfg_ep_nat(u32 clnt_hdl,
+		const struct ipa_ep_cfg_nat *ipa_ep_cfg)
+{
+	return -EPERM;
+}
+
+
+static inline int ipa_cfg_ep_hdr(u32 clnt_hdl,
+		const struct ipa_ep_cfg_hdr *ipa_ep_cfg)
+{
+	return -EPERM;
+}
+
+
+static inline int ipa_cfg_ep_mode(u32 clnt_hdl,
+		const struct ipa_ep_cfg_mode *ipa_ep_cfg)
+{
+	return -EPERM;
+}
+
+
+static inline int ipa_cfg_ep_aggr(u32 clnt_hdl,
+		const struct ipa_ep_cfg_aggr *ipa_ep_cfg)
+{
+	return -EPERM;
+}
+
+
+static inline int ipa_cfg_ep_route(u32 clnt_hdl,
+		const struct ipa_ep_cfg_route *ipa_ep_cfg)
+{
+	return -EPERM;
+}
+
+
+/*
+ * Header removal / addition
+ */
+static inline int ipa_add_hdr(struct ipa_ioc_add_hdr *hdrs)
+{
+	return -EPERM;
+}
+
+
+static inline int ipa_del_hdr(struct ipa_ioc_del_hdr *hdls)
+{
+	return -EPERM;
+}
+
+
+static inline int ipa_commit_hdr(void)
+{
+	return -EPERM;
+}
+
+
+static inline int ipa_reset_hdr(void)
+{
+	return -EPERM;
+}
+
+
+static inline int ipa_get_hdr(struct ipa_ioc_get_hdr *lookup)
+{
+	return -EPERM;
+}
+
+
+static inline int ipa_put_hdr(u32 hdr_hdl)
+{
+	return -EPERM;
+}
+
+
+static inline int ipa_copy_hdr(struct ipa_ioc_copy_hdr *copy)
+{
+	return -EPERM;
+}
+
+
+/*
+ * Routing
+ */
+static inline int ipa_add_rt_rule(struct ipa_ioc_add_rt_rule *rules)
+{
+	return -EPERM;
+}
+
+
+static inline int ipa_del_rt_rule(struct ipa_ioc_del_rt_rule *hdls)
+{
+	return -EPERM;
+}
+
+
+static inline int ipa_commit_rt(enum ipa_ip_type ip)
+{
+	return -EPERM;
+}
+
+
+static inline int ipa_reset_rt(enum ipa_ip_type ip)
+{
+	return -EPERM;
+}
+
+
+static inline int ipa_get_rt_tbl(struct ipa_ioc_get_rt_tbl *lookup)
+{
+	return -EPERM;
+}
+
+
+static inline int ipa_put_rt_tbl(u32 rt_tbl_hdl)
+{
+	return -EPERM;
+}
+
+
+/*
+ * Filtering
+ */
+static inline int ipa_add_flt_rule(struct ipa_ioc_add_flt_rule *rules)
+{
+	return -EPERM;
+}
+
+
+static inline int ipa_del_flt_rule(struct ipa_ioc_del_flt_rule *hdls)
+{
+	return -EPERM;
+}
+
+
+static inline int ipa_commit_flt(enum ipa_ip_type ip)
+{
+	return -EPERM;
+}
+
+
+static inline int ipa_reset_flt(enum ipa_ip_type ip)
+{
+	return -EPERM;
+}
+
+
+/*
+ * NAT
+ */
+static inline int allocate_nat_device(struct ipa_ioc_nat_alloc_mem *mem)
+{
+	return -EPERM;
+}
+
+
+static inline int ipa_nat_init_cmd(struct ipa_ioc_v4_nat_init *init)
+{
+	return -EPERM;
+}
+
+
+static inline int ipa_nat_dma_cmd(struct ipa_ioc_nat_dma_cmd *dma)
+{
+	return -EPERM;
+}
+
+
+static inline int ipa_nat_del_cmd(struct ipa_ioc_v4_nat_del *del)
+{
+	return -EPERM;
+}
+
+
+/*
+ * Aggregation
+ */
+static inline int ipa_set_aggr_mode(enum ipa_aggr_mode mode)
+{
+	return -EPERM;
+}
+
+
+static inline int ipa_set_qcncm_ndp_sig(char sig[3])
+{
+	return -EPERM;
+}
+
+
+static inline int ipa_set_single_ndp_per_mbim(bool enable)
+{
+	return -EPERM;
+}
+
+
+/*
+ * rmnet bridge
+ */
+static inline int rmnet_bridge_init(void)
+{
+	return -EPERM;
+}
+
+
+static inline int rmnet_bridge_disconnect(void)
+{
+	return -EPERM;
+}
+
+
+static inline int rmnet_bridge_connect(u32 producer_hdl,
+			 u32 consumer_hdl,
+			 int wwan_logical_channel_id)
+{
+	return -EPERM;
+}
+
+
+/*
+ * Data path
+ */
+static inline int ipa_tx_dp(enum ipa_client_type dst, struct sk_buff *skb,
+		struct ipa_tx_meta *metadata)
+{
+	return -EPERM;
+}
+
+
+/*
+ * System pipes
+ */
+static inline int ipa_setup_sys_pipe(struct ipa_sys_connect_params *sys_in,
+		u32 *clnt_hdl)
+{
+	return -EPERM;
+}
+
+
+static inline int ipa_teardown_sys_pipe(u32 clnt_hdl)
+{
+	return -EPERM;
+}
+
+
+#endif /* CONFIG_IPA*/
+
+#endif /* _IPA_H_ */
diff --git a/arch/arm/mach-msm/include/mach/irqs-8092.h b/arch/arm/mach-msm/include/mach/irqs-8092.h
index ae9634e..dfe21c2 100644
--- a/arch/arm/mach-msm/include/mach/irqs-8092.h
+++ b/arch/arm/mach-msm/include/mach/irqs-8092.h
@@ -34,12 +34,5 @@
 #define TLMM_MSM_SUMMARY_IRQ		(GIC_SPI_START + 208)
 #define SPS_BAM_DMA_IRQ			(GIC_SPI_START + 105)
 
-#define NR_MSM_IRQS 1020 /* Should be 256 - but higher due to bug in sim */
-#define NR_GPIO_IRQS 146
-#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-8226.h b/arch/arm/mach-msm/include/mach/irqs-8226.h
index fad7b90..abc62d2 100644
--- a/arch/arm/mach-msm/include/mach/irqs-8226.h
+++ b/arch/arm/mach-msm/include/mach/irqs-8226.h
@@ -24,21 +24,11 @@
 #define GIC_PPI_START 16
 #define GIC_SPI_START 32
 
-#define AVS_SVICINT				(GIC_PPI_START + 6)
-#define AVS_SVICINTSWDONE			(GIC_PPI_START + 7)
 #define INT_ARMQC_PERFMON			(GIC_PPI_START + 10)
 /* PPI 15 is unused */
 
 #define APCC_QGICL2PERFMONIRPTREQ	(GIC_SPI_START + 1)
 #define SC_SICL2PERFMONIRPTREQ		APCC_QGICL2PERFMONIRPTREQ
 #define TLMM_MSM_SUMMARY_IRQ		(GIC_SPI_START + 208)
-#define SPS_BAM_DMA_IRQ			(GIC_SPI_START + 105)
-
-#define NR_MSM_IRQS 256
-#define NR_GPIO_IRQS 146
-#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-8625.h b/arch/arm/mach-msm/include/mach/irqs-8625.h
index 2a61118..f591a9e 100644
--- a/arch/arm/mach-msm/include/mach/irqs-8625.h
+++ b/arch/arm/mach-msm/include/mach/irqs-8625.h
@@ -21,7 +21,7 @@
 #endif
 
 /* As per QGIC2 PPI 16 aka 0 is reserved */
-#define MSM8625_INT_A5_PMU_IRQ		(GIC_PPI_START + 1)
+#define MSM8625_INT_ARMQC_PERFMON	(GIC_PPI_START + 1)
 #define MSM8625_INT_DEBUG_TIMER_EXP	(GIC_PPI_START + 2)
 #define MSM8625_INT_GP_TIMER_EXP	(GIC_PPI_START + 3)
 #define MSM8625_INT_COMMRX		(GIC_PPI_START + 4)
diff --git a/arch/arm/mach-msm/include/mach/irqs-8910.h b/arch/arm/mach-msm/include/mach/irqs-8910.h
index 22fdc16..635c044 100644
--- a/arch/arm/mach-msm/include/mach/irqs-8910.h
+++ b/arch/arm/mach-msm/include/mach/irqs-8910.h
@@ -30,11 +30,4 @@
 #define SC_SICL2PERFMONIRPTREQ		APCC_QGICL2PERFMONIRPTREQ
 #define TLMM_MSM_SUMMARY_IRQ		(GIC_SPI_START + 208)
 
-#define NR_MSM_IRQS 256
-#define NR_GPIO_IRQS 146
-#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-8974.h b/arch/arm/mach-msm/include/mach/irqs-8974.h
index 8152eca..150b2ee 100644
--- a/arch/arm/mach-msm/include/mach/irqs-8974.h
+++ b/arch/arm/mach-msm/include/mach/irqs-8974.h
@@ -32,12 +32,5 @@
 #define TLMM_MSM_SUMMARY_IRQ		(GIC_SPI_START + 208)
 #define SPS_BAM_DMA_IRQ			(GIC_SPI_START + 105)
 
-#define NR_MSM_IRQS 1020 /* Should be 256 - but higher due to bug in sim */
-#define NR_GPIO_IRQS 146
-#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-9625.h b/arch/arm/mach-msm/include/mach/irqs-9625.h
index f50606d..b1f65d1 100644
--- a/arch/arm/mach-msm/include/mach/irqs-9625.h
+++ b/arch/arm/mach-msm/include/mach/irqs-9625.h
@@ -21,6 +21,10 @@
  * 32+:   SPI (shared peripheral interrupts)
  */
 
+#define GIC_PPI_START 16
+
+#define INT_ARMQC_PERFMON		(GIC_PPI_START + 7)
+
 #define GIC_SPI_START 32
 
 #define APCC_QGICL2PERFMONIRPTREQ	(GIC_SPI_START + 1)
@@ -28,7 +32,7 @@
 #define TLMM_MSM_SUMMARY_IRQ		(GIC_SPI_START + 208)
 
 #define NR_MSM_IRQS 288
-#define NR_GPIO_IRQS 88
+#define NR_GPIO_IRQS 76
 #define NR_BOARD_IRQS 0
 #define NR_TLMM_MSM_DIR_CONN_IRQ 8 /*Need to Verify this Count*/
 #define NR_MSM_GPIOS NR_GPIO_IRQS
diff --git a/arch/arm/mach-msm/include/mach/irqs.h b/arch/arm/mach-msm/include/mach/irqs.h
index 7aff770..7837c79 100644
--- a/arch/arm/mach-msm/include/mach/irqs.h
+++ b/arch/arm/mach-msm/include/mach/irqs.h
@@ -19,9 +19,40 @@
 
 #define MSM_IRQ_BIT(irq)     (1 << ((irq) & 31))
 
-#include "irqs-8625.h"
+#if defined(CONFIG_ARCH_MSM8974) || defined(CONFIG_ARCH_MPQ8092)
 
-#if defined(CONFIG_ARCH_MSM8960) || defined(CONFIG_ARCH_APQ8064) || \
+#ifdef CONFIG_ARCH_MSM8974
+#include "irqs-8974.h"
+#endif
+
+#ifdef CONFIG_ARCH_MPQ8092
+#include "irqs-8092.h"
+#endif
+
+#define NR_MSM_IRQS 1020 /* Should be 256 - but higher due to bug in sim */
+#define NR_GPIO_IRQS 146
+#define NR_QPNP_IRQS 32768
+#define NR_BOARD_IRQS NR_QPNP_IRQS
+#define NR_TLMM_MSM_DIR_CONN_IRQ 8
+#define NR_MSM_GPIOS NR_GPIO_IRQS
+
+#elif defined(CONFIG_ARCH_MSM8910) || defined(CONFIG_ARCH_MSM8226)
+#ifdef CONFIG_ARCH_MSM8910
+#include "irqs-8910.h"
+#endif
+
+#ifdef CONFIG_ARCH_MSM8226
+#include "irqs-8226.h"
+#endif
+
+#define NR_MSM_IRQS 256
+#define NR_GPIO_IRQS 117
+#define NR_QPNP_IRQS 32768
+#define NR_BOARD_IRQS NR_QPNP_IRQS
+#define NR_TLMM_MSM_DIR_CONN_IRQ 8
+#define NR_MSM_GPIOS NR_GPIO_IRQS
+
+#elif defined(CONFIG_ARCH_MSM8960) || defined(CONFIG_ARCH_APQ8064) || \
 	defined(CONFIG_ARCH_MSM8930)
 
 #ifdef CONFIG_ARCH_MSM8960
@@ -58,18 +89,10 @@
 
 #else
 
-#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)
+#if defined(CONFIG_ARCH_MSM9615)
 #include "irqs-9615.h"
 #elif defined(CONFIG_ARCH_MSM9625)
 #include "irqs-9625.h"
-#elif defined(CONFIG_ARCH_MSM8226)
-#include "irqs-8226.h"
 #elif defined(CONFIG_ARCH_MSM7X30)
 #include "irqs-7x30.h"
 #elif defined(CONFIG_ARCH_QSD8X50)
@@ -78,7 +101,8 @@
 #elif defined(CONFIG_ARCH_MSM8X60)
 #include "irqs-8x60.h"
 #elif defined(CONFIG_ARCH_MSM7X01A) || defined(CONFIG_ARCH_MSM7X25) \
-	|| defined(CONFIG_ARCH_MSM7X27)
+	|| defined(CONFIG_ARCH_MSM7X27) || defined(CONFIG_ARCH_MSM8625)
+#include "irqs-8625.h"
 #include "irqs-7xxx.h"
 
 #define NR_GPIO_IRQS 133
diff --git a/arch/arm/mach-msm/include/mach/kgsl.h b/arch/arm/mach-msm/include/mach/kgsl.h
index a51cc46..f07a9e8 100644
--- a/arch/arm/mach-msm/include/mach/kgsl.h
+++ b/arch/arm/mach-msm/include/mach/kgsl.h
@@ -27,6 +27,7 @@
 	(val*1000*1000U)
 
 #define KGSL_3D0_REG_MEMORY	"kgsl_3d0_reg_memory"
+#define KGSL_3D0_SHADER_MEMORY	"kgsl_3d0_shader_memory"
 #define KGSL_3D0_IRQ		"kgsl_3d0_irq"
 #define KGSL_2D0_REG_MEMORY	"kgsl_2d0_reg_memory"
 #define KGSL_2D0_IRQ		"kgsl_2d0_irq"
diff --git a/arch/arm/mach-msm/include/mach/memory.h b/arch/arm/mach-msm/include/mach/memory.h
index acfbe4a..d089924 100644
--- a/arch/arm/mach-msm/include/mach/memory.h
+++ b/arch/arm/mach-msm/include/mach/memory.h
@@ -98,26 +98,25 @@
 #define finish_arch_switch(prev)	do { store_ttbr0(); } while (0)
 #endif
 
+#define MAX_HOLE_ADDRESS    (PHYS_OFFSET + 0x10000000)
+extern unsigned long memory_hole_offset;
+extern unsigned long memory_hole_start;
+extern unsigned long memory_hole_end;
 #ifdef CONFIG_DONT_MAP_HOLE_AFTER_MEMBANK0
-extern unsigned long membank0_size;
-extern unsigned long membank1_start;
-void find_membank0_hole(void);
+void find_memory_hole(void);
 
-#define MEMBANK0_PHYS_OFFSET PHYS_OFFSET
-#define MEMBANK0_PAGE_OFFSET PAGE_OFFSET
-
-#define MEMBANK1_PHYS_OFFSET (membank1_start)
-#define MEMBANK1_PAGE_OFFSET (MEMBANK0_PAGE_OFFSET + (membank0_size))
+#define MEM_HOLE_END_PHYS_OFFSET (memory_hole_end)
+#define MEM_HOLE_PAGE_OFFSET (PAGE_OFFSET + memory_hole_offset)
 
 #define __phys_to_virt(phys)				\
-	((MEMBANK1_PHYS_OFFSET && ((phys) >= MEMBANK1_PHYS_OFFSET)) ?	\
-	(phys) - MEMBANK1_PHYS_OFFSET + MEMBANK1_PAGE_OFFSET :	\
-	(phys) - MEMBANK0_PHYS_OFFSET + MEMBANK0_PAGE_OFFSET)
+	((MEM_HOLE_END_PHYS_OFFSET && ((phys) >= MEM_HOLE_END_PHYS_OFFSET)) ? \
+	(phys) - MEM_HOLE_END_PHYS_OFFSET + MEM_HOLE_PAGE_OFFSET :	\
+	(phys) - PHYS_OFFSET + PAGE_OFFSET)
 
 #define __virt_to_phys(virt)				\
-	((MEMBANK1_PHYS_OFFSET && ((virt) >= MEMBANK1_PAGE_OFFSET)) ?	\
-	(virt) - MEMBANK1_PAGE_OFFSET + MEMBANK1_PHYS_OFFSET :	\
-	(virt) - MEMBANK0_PAGE_OFFSET + MEMBANK0_PHYS_OFFSET)
+	((MEM_HOLE_END_PHYS_OFFSET && ((virt) >= MEM_HOLE_PAGE_OFFSET)) ? \
+	(virt) - MEM_HOLE_PAGE_OFFSET + MEM_HOLE_END_PHYS_OFFSET :	\
+	(virt) - PAGE_OFFSET + PHYS_OFFSET)
 #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_bus_board.h b/arch/arm/mach-msm/include/mach/msm_bus_board.h
index 0a53b46..ab0e72f 100644
--- a/arch/arm/mach-msm/include/mach/msm_bus_board.h
+++ b/arch/arm/mach-msm/include/mach/msm_bus_board.h
@@ -29,6 +29,7 @@
 	unsigned int len;
 	int ahb;
 	const char *fabclk[NUM_CTX];
+	const char *iface_clk;
 	unsigned int offset;
 	unsigned int haltid;
 	unsigned int rpm_enabled;
@@ -92,6 +93,11 @@
 extern struct msm_bus_fabric_registration msm_bus_8974_config_noc_pdata;
 extern struct msm_bus_fabric_registration msm_bus_8974_ocmem_vnoc_pdata;
 
+extern struct msm_bus_fabric_registration msm_bus_9625_sys_noc_pdata;
+extern struct msm_bus_fabric_registration msm_bus_9625_bimc_pdata;
+extern struct msm_bus_fabric_registration msm_bus_9625_periph_noc_pdata;
+extern struct msm_bus_fabric_registration msm_bus_9625_config_noc_pdata;
+
 void msm_bus_rpm_set_mt_mask(void);
 int msm_bus_board_rpm_get_il_ids(uint16_t *id);
 int msm_bus_board_get_iid(int id);
@@ -291,8 +297,10 @@
 	MSM_BUS_MASTER_USB_HS,
 	MSM_BUS_MASTER_PNOC_CFG,
 	MSM_BUS_MASTER_V_OCMEM_GFX3D,
+	MSM_BUS_MASTER_IPA,
+	MSM_BUS_MASTER_QPIC,
 
-	MSM_BUS_MASTER_LAST = MSM_BUS_MASTER_V_OCMEM_GFX3D,
+	MSM_BUS_MASTER_LAST = MSM_BUS_MASTER_QPIC,
 
 	MSM_BUS_SYSTEM_FPB_MASTER_SYSTEM =
 		MSM_BUS_SYSTEM_MASTER_SYSTEM_FPB,
@@ -446,8 +454,10 @@
 	MSM_BUS_SLAVE_PHY_APU_CFG,
 	MSM_BUS_SLAVE_EBI1_PHY_CFG,
 	MSM_BUS_SLAVE_SERVICE_CNOC,
+	MSM_BUS_SLAVE_IPS_CFG,
+	MSM_BUS_SLAVE_QPIC,
 
-	MSM_BUS_SLAVE_LAST = MSM_BUS_SLAVE_SERVICE_CNOC,
+	MSM_BUS_SLAVE_LAST = MSM_BUS_SLAVE_QPIC,
 
 	MSM_BUS_SYSTEM_FPB_SLAVE_SYSTEM =
 		MSM_BUS_SYSTEM_SLAVE_SYSTEM_FPB,
diff --git a/arch/arm/mach-msm/include/mach/msm_dcvs.h b/arch/arm/mach-msm/include/mach/msm_dcvs.h
index e81cee4..2ad7d22 100644
--- a/arch/arm/mach-msm/include/mach/msm_dcvs.h
+++ b/arch/arm/mach-msm/include/mach/msm_dcvs.h
@@ -36,12 +36,37 @@
 	MSM_DCVS_DISABLE_HIGH_LATENCY_MODES,
 };
 
+struct msm_dcvs_sync_rule {
+	unsigned long cpu_khz;
+	unsigned long gpu_floor_khz;
+};
+
+struct msm_dcvs_platform_data {
+	struct msm_dcvs_sync_rule *sync_rules;
+	unsigned num_sync_rules;
+	unsigned long gpu_max_nom_khz;
+};
+
 struct msm_gov_platform_data {
 	struct msm_dcvs_core_info *info;
 	int latency;
 };
 
 /**
+ * msm_dcvs_register_cpu_freq
+ * @freq: the frequency value to register
+ * @voltage: the operating voltage (in mV) associated with the above frequency
+ *
+ * Register a cpu frequency and its operating voltage with dcvs.
+ */
+#ifdef CONFIG_MSM_DCVS
+void msm_dcvs_register_cpu_freq(uint32_t freq, uint32_t voltage);
+#else
+static inline void msm_dcvs_register_cpu_freq(uint32_t freq, uint32_t voltage)
+{}
+#endif
+
+/**
  * msm_dcvs_idle
  * @dcvs_core_id: The id returned by msm_dcvs_register_core
  * @state: The enter/exit idle state the core is in
@@ -98,6 +123,7 @@
 	unsigned int (*get_frequency)(int type_core_num),
 	int (*idle_enable)(int type_core_num,
 				enum msm_core_control_event event),
+	int (*set_floor_frequency)(int type_core_num, unsigned int freq),
 	int sensor);
 
 /**
@@ -129,4 +155,23 @@
  * Update the frequency known to dcvs when the limits are changed.
  */
 extern void msm_dcvs_update_limits(int dcvs_core_id);
+
+/**
+ * msm_dcvs_apply_gpu_floor
+ * @cpu_freq: CPU frequency to compare to GPU sync rules
+ *
+ * Apply a GPU floor frequency if the corresponding CPU frequency,
+ * or the number of CPUs online, requires it.
+ */
+extern void msm_dcvs_apply_gpu_floor(unsigned long cpu_freq);
+
+/**
+ * msm_dcvs_update_algo_params
+ * @return:
+ *      0 on success, < 0 on error
+ *
+ * Updates the DCVS algorithm with parameters depending on the
+ * number of CPUs online.
+ */
+extern int msm_dcvs_update_algo_params(void);
 #endif
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_iomap-8226.h b/arch/arm/mach-msm/include/mach/msm_iomap-8226.h
index 08bc981..ab43a8a 100644
--- a/arch/arm/mach-msm/include/mach/msm_iomap-8226.h
+++ b/arch/arm/mach-msm/include/mach/msm_iomap-8226.h
@@ -37,15 +37,15 @@
 #define MSM8226_TLMM_PHYS	0xFD510000
 #define MSM8226_TLMM_SIZE	SZ_16K
 
-#define MSM8226_IMEM_PHYS	0xFC42B000
+#define MSM8226_IMEM_PHYS	0xFE805000
 #define MSM8226_IMEM_SIZE	SZ_4K
 
+#define MSM8226_MPM2_PSHOLD_PHYS	0xFC4AB000
+#define MSM8226_MPM2_PSHOLD_SIZE	SZ_4K
+
 #ifdef CONFIG_DEBUG_MSM8226_UART
 #define MSM_DEBUG_UART_BASE	IOMEM(0xFA71E000)
 #define MSM_DEBUG_UART_PHYS	0xF991E000
 #endif
 
-#define MSM8226_DBG_IMEM_PHYS	0xFE805000
-#define MSM8226_DBG_IMEM_SIZE	SZ_4K
-
 #endif
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
index e4cd312..08f21b6 100644
--- a/arch/arm/mach-msm/include/mach/msm_iomap-8910.h
+++ b/arch/arm/mach-msm/include/mach/msm_iomap-8910.h
@@ -30,9 +30,12 @@
 #define MSM8910_TLMM_PHYS	0xFD510000
 #define MSM8910_TLMM_SIZE	SZ_16K
 
-#define MSM8910_IMEM_PHYS	0xFC42B000
+#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
diff --git a/arch/arm/mach-msm/include/mach/msm_ipc_router.h b/arch/arm/mach-msm/include/mach/msm_ipc_router.h
new file mode 100644
index 0000000..c68c783
--- /dev/null
+++ b/arch/arm/mach-msm/include/mach/msm_ipc_router.h
@@ -0,0 +1,182 @@
+/* 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_IPC_ROUTER_H
+#define _MSM_IPC_ROUTER_H
+
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/errno.h>
+#include <linux/mm.h>
+#include <linux/list.h>
+#include <linux/wakelock.h>
+#include <linux/msm_ipc.h>
+
+#define MAX_WAKELOCK_NAME_SZ 32
+
+/**
+ * enum msm_ipc_router_event - Events that will be generated by IPC Router
+ */
+enum msm_ipc_router_event {
+	MSM_IPC_ROUTER_READ_CB = 0,
+	MSM_IPC_ROUTER_WRITE_DONE,
+};
+
+struct msm_ipc_port {
+	struct list_head list;
+
+	struct msm_ipc_port_addr this_port;
+	struct msm_ipc_port_name port_name;
+	uint32_t type;
+	unsigned flags;
+	spinlock_t port_lock;
+
+	struct list_head incomplete;
+	struct mutex incomplete_lock;
+
+	struct list_head port_rx_q;
+	struct mutex port_rx_q_lock;
+	char rx_wakelock_name[MAX_WAKELOCK_NAME_SZ];
+	struct wake_lock port_rx_wake_lock;
+	wait_queue_head_t port_rx_wait_q;
+
+	int restart_state;
+	spinlock_t restart_lock;
+	wait_queue_head_t restart_wait;
+
+	void *endpoint;
+	void (*notify)(unsigned event, void *priv);
+	int (*check_send_permissions)(void *data);
+
+	uint32_t num_tx;
+	uint32_t num_rx;
+	unsigned long num_tx_bytes;
+	unsigned long num_rx_bytes;
+	void *priv;
+};
+
+#ifdef CONFIG_MSM_IPC_ROUTER
+/**
+ * msm_ipc_router_create_port() - Create a IPC Router port/endpoint
+ * @notify: Callback function to notify any event on the port.
+ * @priv: Private info to be passed while the notification is generated.
+ *
+ * @return: Pointer to the port on success, NULL on error.
+ */
+struct msm_ipc_port *msm_ipc_router_create_port(
+	void (*notify)(unsigned event, void *priv),
+	void *priv);
+
+/**
+ * msm_ipc_router_lookup_server_name() - Resolve server address
+ * @srv_name: Name<service:instance> of the server to be resolved.
+ * @srv_info: Buffer to hold the resolved address.
+ * @num_entries_in_array: Number of server info the buffer can hold.
+ * @lookup_mask: Mask to specify the range of instances to be resolved.
+ *
+ * @return: Number of server addresses resolved on success, < 0 on error.
+ */
+int msm_ipc_router_lookup_server_name(struct msm_ipc_port_name *srv_name,
+				      struct msm_ipc_server_info *srv_info,
+				      int num_entries_in_array,
+				      uint32_t lookup_mask);
+
+/**
+ * msm_ipc_router_send_msg() - Send a message/packet
+ * @src: Sender's address/port.
+ * @dest: Destination address.
+ * @data: Pointer to the data to be sent.
+ * @data_len: Length of the data to be sent.
+ *
+ * @return: 0 on success, < 0 on error.
+ */
+int msm_ipc_router_send_msg(struct msm_ipc_port *src,
+			    struct msm_ipc_addr *dest,
+			    void *data, unsigned int data_len);
+
+/**
+ * msm_ipc_router_get_curr_pkt_size() - Get the packet size of the first
+ *                                      packet in the rx queue
+ * @port_ptr: Port which owns the rx queue.
+ *
+ * @return: Returns the size of the first packet, if available.
+ *          0 if no packets available, < 0 on error.
+ */
+int msm_ipc_router_get_curr_pkt_size(struct msm_ipc_port *port_ptr);
+
+/**
+ * msm_ipc_router_read_msg() - Read a message/packet
+ * @port_ptr: Receiver's port/address.
+ * @data: Pointer containing the address of the received data.
+ * @src: Address of the sender/source.
+ * @len: Length of the data being read.
+ *
+ * @return: 0 on success, < 0 on error.
+ */
+int msm_ipc_router_read_msg(struct msm_ipc_port *port_ptr,
+			    struct msm_ipc_addr *src,
+			    unsigned char **data,
+			    unsigned int *len);
+
+/**
+ * msm_ipc_router_close_port() - Close the port
+ * @port_ptr: Pointer to the port to be closed.
+ *
+ * @return: 0 on success, < 0 on error.
+ */
+int msm_ipc_router_close_port(struct msm_ipc_port *port_ptr);
+
+#else
+
+struct msm_ipc_port *msm_ipc_router_create_port(
+	void (*notify)(unsigned event, void *priv),
+	void *priv)
+{
+	return NULL;
+}
+
+int msm_ipc_router_lookup_server_name(struct msm_ipc_port_name *srv_name,
+				      struct msm_ipc_server_info *srv_info,
+				      int num_entries_in_array,
+				      uint32_t lookup_mask)
+{
+	return -ENODEV;
+}
+
+int msm_ipc_router_send_msg(struct msm_ipc_port *src,
+			    struct msm_ipc_addr *dest,
+			    void *data, unsigned int data_len)
+{
+	return -ENODEV;
+}
+
+int msm_ipc_router_get_curr_pkt_size(struct msm_ipc_port *port_ptr)
+{
+	return -ENODEV;
+}
+
+int msm_ipc_router_read_msg(struct msm_ipc_port *port_ptr,
+			    struct msm_ipc_addr *src,
+			    unsigned char **data,
+			    unsigned int *len)
+{
+	return -ENODEV;
+}
+
+int msm_ipc_router_close_port(struct msm_ipc_port *port_ptr)
+{
+	return -ENODEV;
+}
+
+#endif
+
+#endif
diff --git a/arch/arm/mach-msm/include/mach/msm_memtypes.h b/arch/arm/mach-msm/include/mach/msm_memtypes.h
index 5ca5861..80e454a 100644
--- a/arch/arm/mach-msm/include/mach/msm_memtypes.h
+++ b/arch/arm/mach-msm/include/mach/msm_memtypes.h
@@ -68,6 +68,8 @@
 
 int __init dt_scan_for_memory_reserve(unsigned long node, const char *uname,
 					int depth, void *data);
-
+int __init dt_scan_for_memory_hole(unsigned long node, const char *uname,
+					int depth, void *data);
+void adjust_meminfo(unsigned long start, unsigned long size);
 unsigned long __init reserve_memory_for_fmem(unsigned long, unsigned long);
 #endif
diff --git a/arch/arm/mach-msm/include/mach/msm_qmi_interface.h b/arch/arm/mach-msm/include/mach/msm_qmi_interface.h
new file mode 100644
index 0000000..11867f3
--- /dev/null
+++ b/arch/arm/mach-msm/include/mach/msm_qmi_interface.h
@@ -0,0 +1,280 @@
+/* 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_QMI_INTERFACE_H_
+#define _MSM_QMI_INTERFACE_H_
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/mm.h>
+#include <linux/list.h>
+#include <linux/socket.h>
+#include <linux/gfp.h>
+#include <linux/qmi_encdec.h>
+
+#define QMI_COMMON_TLV_TYPE 0
+
+enum qmi_event_type {
+	QMI_RECV_MSG = 1,
+	QMI_SERVER_ARRIVE,
+	QMI_SERVER_EXIT,
+};
+
+struct qmi_handle {
+	void *src_port;
+	void *dest_info;
+	uint16_t next_txn_id;
+	struct list_head txn_list;
+	struct mutex handle_lock;
+	spinlock_t notify_lock;
+	void (*notify)(struct qmi_handle *handle, enum qmi_event_type event,
+			void *notify_priv);
+	void *notify_priv;
+	void (*ind_cb)(struct qmi_handle *handle,
+			unsigned int msg_id, void *msg,
+			unsigned int msg_len, void *ind_cb_priv);
+	void *ind_cb_priv;
+	int handle_reset;
+	wait_queue_head_t reset_waitq;
+};
+
+enum qmi_result_type_v01 {
+	/* To force a 32 bit signed enum. Do not change or use*/
+	QMI_RESULT_TYPE_MIN_ENUM_VAL_V01 = INT_MIN,
+	QMI_RESULT_SUCCESS_V01 = 0,
+	QMI_RESULT_FAILURE_V01 = 1,
+	QMI_RESULT_TYPE_MAX_ENUM_VAL_V01 = INT_MAX,
+};
+
+enum qmi_error_type_v01 {
+	/* To force a 32 bit signed enum. Do not change or use*/
+	QMI_ERROR_TYPE_MIN_ENUM_VAL_V01 = INT_MIN,
+	QMI_ERR_NONE_V01 = 0x0000,
+	QMI_ERROR_MALFORMED_MSG_V01 = 0x0001,
+	QMI_ERR_NO_MEMORY_V01 = 0x0002,
+	QMI_ERR_INTERNAL_V01 = 0x0003,
+	QMI_ERR_INVALID_ID_V01 = 0x0029,
+	QMI_ERR_INCOMPATIBLE_STATE_V01 = 0x005A,
+	QMI_ERROR_TYPE_MAX_ENUM_VAL_V01 = INT_MAX,
+};
+
+struct qmi_response_type_v01 {
+	enum qmi_result_type_v01 result;
+	enum qmi_error_type_v01 error;
+};
+
+#ifdef CONFIG_MSM_QMI_INTERFACE
+
+/* Element info array describing common qmi response structure */
+extern struct elem_info qmi_response_type_v01_ei[];
+#define get_qmi_response_type_v01_ei() qmi_response_type_v01_ei
+
+/**
+ * qmi_handle_create() - Create a QMI handle
+ * @notify: Callback to notify events on the handle created.
+ * @notify_priv: Private information to be passed along with the notification.
+ *
+ * @return: Valid QMI handle on success, NULL on error.
+ */
+struct qmi_handle *qmi_handle_create(
+	void (*notify)(struct qmi_handle *handle,
+		       enum qmi_event_type event, void *notify_priv),
+	void *notify_priv);
+
+/**
+ * qmi_handle_destroy() - Destroy the QMI handle
+ * @handle: QMI handle to be destroyed.
+ *
+ * @return: 0 on success, < 0 on error.
+ */
+int qmi_handle_destroy(struct qmi_handle *handle);
+
+/**
+ * qmi_register_ind_cb() - Register the indication callback function
+ * @handle: QMI handle with which the function is registered.
+ * @ind_cb: Callback function to be registered.
+ * @ind_cb_priv: Private data to be passed with the indication callback.
+ *
+ * @return: 0 on success, < 0 on error.
+ */
+int qmi_register_ind_cb(struct qmi_handle *handle,
+	void (*ind_cb)(struct qmi_handle *handle,
+		       unsigned int msg_id, void *msg,
+		       unsigned int msg_len, void *ind_cb_priv),
+	void *ind_cb_priv);
+
+/**
+ * qmi_send_req_wait() - Send a synchronous QMI request
+ * @handle: QMI handle through which the QMI request is sent.
+ * @request_desc: Structure describing the request data structure.
+ * @req: Buffer containing the request data structure.
+ * @req_len: Length of the request data structure.
+ * @resp_desc: Structure describing the response data structure.
+ * @resp: Buffer to hold the response data structure.
+ * @resp_len: Length of the response data structure.
+ * @timeout_ms: Timeout before a response is received.
+ *
+ * @return: 0 on success, < 0 on error.
+ */
+int qmi_send_req_wait(struct qmi_handle *handle,
+		      struct msg_desc *req_desc,
+		      void *req, unsigned int req_len,
+		      struct msg_desc *resp_desc,
+		      void *resp, unsigned int resp_len,
+		      unsigned long timeout_ms);
+
+/**
+ * qmi_send_req_nowait() - Send an asynchronous QMI request
+ * @handle: QMI handle through which the QMI request is sent.
+ * @request_desc: Structure describing the request data structure.
+ * @req: Buffer containing the request data structure.
+ * @req_len: Length of the request data structure.
+ * @resp_desc: Structure describing the response data structure.
+ * @resp: Buffer to hold the response data structure.
+ * @resp_len: Length of the response data structure.
+ * @resp_cb: Callback function to be invoked when the response arrives.
+ * @resp_cb_data: Private information to be passed along with the callback.
+ *
+ * @return: 0 on success, < 0 on error.
+ */
+int qmi_send_req_nowait(struct qmi_handle *handle,
+			struct msg_desc *req_desc,
+			void *req, unsigned int req_len,
+			struct msg_desc *resp_desc,
+			void *resp, unsigned int resp_len,
+			void (*resp_cb)(struct qmi_handle *handle,
+					unsigned int msg_id, void *msg,
+					void *resp_cb_data),
+			void *resp_cb_data);
+
+/**
+ * qmi_recv_msg() - Receive the QMI message
+ * @handle: Handle for which the QMI message has to be received.
+ *
+ * @return: 0 on success, < 0 on error.
+ */
+int qmi_recv_msg(struct qmi_handle *handle);
+
+/**
+ * qmi_connect_to_service() - Connect the QMI handle with a QMI service
+ * @handle: QMI handle to be connected with the QMI service.
+ * @service_id: Service id to identify the QMI service.
+ * @instance_id: Instance id to identify the instance of the QMI service.
+ *
+ * @return: 0 on success, < 0 on error.
+ */
+int qmi_connect_to_service(struct qmi_handle *handle,
+			   uint32_t service_id, uint32_t instance_id);
+
+/**
+ * qmi_svc_event_notifier_register() - Register a notifier block to receive
+ *                                     events regarding a QMI service
+ * @service_id: Service ID to identify the QMI service.
+ * @instance_id: Instance ID to identify the instance of the QMI service.
+ * @nb: Notifier block used to receive the event.
+ *
+ * @return: 0 if successfully registered, < 0 on error.
+ */
+int qmi_svc_event_notifier_register(uint32_t service_id,
+				    uint32_t instance_id,
+				    struct notifier_block *nb);
+
+/**
+ * qmi_svc_event_notifier_unregister() - Unregister service event
+ *                                       notifier block
+ * @service_id: Service ID to identify the QMI service.
+ * @instance_id: Instance ID to identify the instance of the QMI service.
+ * @nb: Notifier block registered to receive the events.
+ *
+ * @return: 0 if successfully registered, < 0 on error.
+ */
+int qmi_svc_event_notifier_unregister(uint32_t service_id,
+				      uint32_t instance_id,
+				      struct notifier_block *nb);
+#else
+
+#define get_qmi_response_type_v01_ei() NULL
+
+static inline struct qmi_handle *qmi_handle_create(
+	void (*notify)(struct qmi_handle *handle,
+		       enum qmi_event_type event, void *notify_priv),
+	void *notify_priv)
+{
+	return NULL;
+}
+
+static inline int qmi_handle_destroy(struct qmi_handle *handle)
+{
+	return -ENODEV;
+}
+
+static inline int qmi_register_ind_cb(struct qmi_handle *handle,
+	void (*ind_cb)(struct qmi_handle *handle,
+		       unsigned int msg_id, void *msg,
+		       unsigned int msg_len, void *ind_cb_priv),
+	void *ind_cb_priv)
+{
+	return -ENODEV;
+}
+
+static inline int qmi_send_req_wait(struct qmi_handle *handle,
+				    struct msg_desc *req_desc,
+				    void *req, unsigned int req_len,
+				    struct msg_desc *resp_desc,
+				    void *resp, unsigned int resp_len,
+				    unsigned long timeout_ms)
+{
+	return -ENODEV;
+}
+
+static inline int qmi_send_req_nowait(struct qmi_handle *handle,
+				struct msg_desc *req_desc,
+				void *req, unsigned int req_len,
+				struct msg_desc *resp_desc,
+				void *resp, unsigned int resp_len,
+				void (*resp_cb)(struct qmi_handle *handle,
+						unsigned int msg_id, void *msg,
+						void *resp_cb_data),
+				void *resp_cb_data)
+{
+	return -ENODEV;
+}
+
+static inline int qmi_recv_msg(struct qmi_handle *handle)
+{
+	return -ENODEV;
+}
+
+static inline int qmi_connect_to_service(struct qmi_handle *handle,
+					 uint32_t service_id,
+					 uint32_t instance_id)
+{
+	return -ENODEV;
+}
+
+static inline int qmi_svc_event_notifier_register(uint32_t service_id,
+						  uint32_t instance_id,
+						  struct notifier_block *nb)
+{
+	return -ENODEV;
+}
+
+static inline int qmi_svc_event_notifier_unregister(uint32_t service_id,
+						    uint32_t instance_id,
+						    struct notifier_block *nb)
+{
+	return -ENODEV;
+}
+
+#endif
+
+#endif
diff --git a/arch/arm/mach-msm/include/mach/msm_serial_pdata.h b/arch/arm/mach-msm/include/mach/msm_serial_pdata.h
index 4153cb2..40bdc9d 100644
--- a/arch/arm/mach-msm/include/mach/msm_serial_pdata.h
+++ b/arch/arm/mach-msm/include/mach/msm_serial_pdata.h
@@ -10,8 +10,8 @@
  * GNU General Public License for more details.
 */
 
-#ifndef __ASM_ARCH_MSM_SERIAL_HS_H
-#define __ASM_ARCH_MSM_SERIAL_HS_H
+#ifndef __ASM_ARCH_MSM_SERIAL_H
+#define __ASM_ARCH_MSM_SERIAL_H
 
 #include <linux/serial_core.h>
 
@@ -22,6 +22,7 @@
 	/* bool: inject char into rx tty on wakeup */
 	unsigned char inject_rx_on_wakeup;
 	char rx_to_inject;
+	int userid;
 };
 
 #endif
diff --git a/arch/arm/mach-msm/include/mach/msm_smsm.h b/arch/arm/mach-msm/include/mach/msm_smsm.h
index 44b52b6..c77c181 100644
--- a/arch/arm/mach-msm/include/mach/msm_smsm.h
+++ b/arch/arm/mach-msm/include/mach/msm_smsm.h
@@ -83,7 +83,7 @@
 #define SMSM_WKUP_REASON_TIMER	0x00000008
 #define SMSM_WKUP_REASON_ALARM	0x00000010
 #define SMSM_WKUP_REASON_RESET	0x00000020
-#define SMSM_A2_FORCE_SHUTDOWN 0x00002000
+#define SMSM_USB_PLUG_UNPLUG    0x00002000
 #define SMSM_A2_RESET_BAM      0x00004000
 
 #define SMSM_VENDOR             0x00020000
diff --git a/arch/arm/mach-msm/include/mach/msm_spi.h b/arch/arm/mach-msm/include/mach/msm_spi.h
index 11d3014..ab5271f 100644
--- a/arch/arm/mach-msm/include/mach/msm_spi.h
+++ b/arch/arm/mach-msm/include/mach/msm_spi.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2008-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 and
@@ -20,6 +20,10 @@
 	void (*gpio_release)(void);
 	int (*dma_config)(void);
 	const char *rsl_id;
-	uint32_t pm_lat;
-	uint32_t infinite_mode;
+	u32  pm_lat;
+	u32  infinite_mode;
+	bool ver_reg_exists;
+	bool use_bam;
+	u32  bam_consumer_pipe_index;
+	u32  bam_producer_pipe_index;
 };
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 cb8aae0..5355215 100644
--- a/arch/arm/mach-msm/include/mach/ocmem.h
+++ b/arch/arm/mach-msm/include/mach/ocmem.h
@@ -16,13 +16,14 @@
 #include <asm/page.h>
 #include <linux/module.h>
 #include <linux/notifier.h>
+#include <linux/err.h>
 
 #define OCMEM_MIN_ALLOC SZ_64K
 #define OCMEM_MIN_ALIGN SZ_64K
 
 /* Maximum number of slots in DM */
 #define OCMEM_MAX_CHUNKS 32
-#define MIN_CHUNK_SIZE 128
+#define MIN_CHUNK_SIZE SZ_4K
 
 struct ocmem_notifier;
 
@@ -99,6 +100,7 @@
 };
 
 /* APIS */
+#ifdef CONFIG_MSM_OCMEM
 /* Notification APIs */
 struct ocmem_notifier *ocmem_notifier_register(int client_id,
 						struct notifier_block *nb);
@@ -151,4 +153,111 @@
 
 struct ocmem_vectors *ocmem_get_vectors(int client_id,
 						struct ocmem_buf *buf);
+
+#else
+/* Notification APIs */
+static inline struct ocmem_notifier *ocmem_notifier_register
+				(int client_id, struct notifier_block *nb)
+{
+	return ERR_PTR(-ENODEV);
+}
+
+static inline int ocmem_notifier_unregister(struct ocmem_notifier *notif_hndl,
+				struct notifier_block *nb)
+{
+	return -ENODEV;
+}
+
+/* Obtain the maximum quota for the client */
+static inline unsigned long get_max_quota(int client_id)
+{
+	return 0;
+}
+
+/* Allocation APIs */
+static inline struct ocmem_buf *ocmem_allocate(int client_id,
+						unsigned long size)
+{
+	return ERR_PTR(-ENODEV);
+}
+
+static inline struct ocmem_buf *ocmem_allocate_nowait(int client_id,
+							unsigned long size)
+{
+	return ERR_PTR(-ENODEV);
+}
+
+static inline struct ocmem_buf *ocmem_allocate_nb(int client_id,
+							unsigned long size)
+{
+	return ERR_PTR(-ENODEV);
+}
+
+static inline struct ocmem_buf *ocmem_allocate_range(int client_id,
+		unsigned long min, unsigned long goal, unsigned long step)
+{
+	return ERR_PTR(-ENODEV);
+}
+
+/* Free APIs */
+static inline int ocmem_free(int client_id, struct ocmem_buf *buf)
+{
+	return -ENODEV;
+}
+
+/* Dynamic Resize APIs */
+static inline int ocmem_shrink(int client_id, struct ocmem_buf *buf,
+			unsigned long new_size)
+{
+	return -ENODEV;
+}
+
+/* Transfer APIs */
+static inline int ocmem_map(int client_id, struct ocmem_buf *buffer,
+			struct ocmem_map_list *list)
+{
+	return -ENODEV;
+}
+
+static inline int ocmem_unmap(int client_id, struct ocmem_buf *buffer,
+			struct ocmem_map_list *list)
+{
+	return -ENODEV;
+}
+
+static inline int ocmem_dump(int client_id, struct ocmem_buf *buffer,
+				unsigned long dst_phys_addr)
+{
+	return -ENODEV;
+}
+
+/* Priority Enforcement APIs */
+static inline int ocmem_evict(int client_id)
+{
+	return -ENODEV;
+}
+
+static inline int ocmem_restore(int client_id)
+{
+	return -ENODEV;
+}
+
+/* Power Control APIs */
+static inline int ocmem_set_power_state(int client_id,
+		struct ocmem_buf *buf, enum ocmem_power_state new_state)
+{
+	return -ENODEV;
+}
+
+static inline enum ocmem_power_state ocmem_get_power_state(int client_id,
+				struct ocmem_buf *buf)
+{
+	return -ENODEV;
+}
+static inline struct ocmem_vectors *ocmem_get_vectors(int client_id,
+						struct ocmem_buf *buf)
+{
+	return ERR_PTR(-ENODEV);
+}
+#endif
 #endif
diff --git a/arch/arm/mach-msm/include/mach/ocmem_priv.h b/arch/arm/mach-msm/include/mach/ocmem_priv.h
index 0b30c26..380fde1 100644
--- a/arch/arm/mach-msm/include/mach/ocmem_priv.h
+++ b/arch/arm/mach-msm/include/mach/ocmem_priv.h
@@ -127,7 +127,7 @@
 	struct list_head req_list;
 	struct work_struct work;
 	int prio;
-	int pending;
+	atomic_t pending;
 	bool passive;
 };
 
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/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/audio_acdb.h b/arch/arm/mach-msm/include/mach/qdsp6v2/audio_acdb.h
index d34536d..88cb94a 100644
--- a/arch/arm/mach-msm/include/mach/qdsp6v2/audio_acdb.h
+++ b/arch/arm/mach-msm/include/mach/qdsp6v2/audio_acdb.h
@@ -14,7 +14,7 @@
 #define _AUDIO_ACDB_H
 
 #include <linux/msm_audio_acdb.h>
-#ifdef CONFIG_ARCH_MSM8974
+#if defined CONFIG_ARCH_MSM8974 || defined CONFIG_ARCH_MSM9625
 #include <sound/q6adm-v2.h>
 #else
 #include <sound/q6adm.h>
diff --git a/arch/arm/mach-msm/include/mach/rpm-8930.h b/arch/arm/mach-msm/include/mach/rpm-8930.h
index 4ac9192..bc1b918 100644
--- a/arch/arm/mach-msm/include/mach/rpm-8930.h
+++ b/arch/arm/mach-msm/include/mach/rpm-8930.h
@@ -219,182 +219,190 @@
 		MSM_RPM_8930_ID_MM_FABRIC_ARB_0	+ 10,
 
 	/* PMIC 8038 */
-	MSM_RPM_8930_ID_PM8038_S1_0	= 90,
-	MSM_RPM_8930_ID_PM8038_S1_1	= 91,
-	MSM_RPM_8930_ID_PM8038_S2_0	= 92,
-	MSM_RPM_8930_ID_PM8038_S2_1	= 93,
-	MSM_RPM_8930_ID_PM8038_S3_0	= 94,
-	MSM_RPM_8930_ID_PM8038_S3_1	= 95,
-	MSM_RPM_8930_ID_PM8038_S4_0	= 96,
-	MSM_RPM_8930_ID_PM8038_S4_1	= 97,
-	MSM_RPM_8930_ID_PM8038_S5_0	= 98,
-	MSM_RPM_8930_ID_PM8038_S5_1	= 99,
-	MSM_RPM_8930_ID_PM8038_S6_0	= 100,
-	MSM_RPM_8930_ID_PM8038_S6_1	= 101,
-	MSM_RPM_8930_ID_PM8038_L1_0	= 102,
-	MSM_RPM_8930_ID_PM8038_L1_1	= 103,
-	MSM_RPM_8930_ID_PM8038_L2_0	= 104,
-	MSM_RPM_8930_ID_PM8038_L2_1	= 105,
-	MSM_RPM_8930_ID_PM8038_L3_0	= 106,
-	MSM_RPM_8930_ID_PM8038_L3_1	= 107,
-	MSM_RPM_8930_ID_PM8038_L4_0	= 108,
-	MSM_RPM_8930_ID_PM8038_L4_1	= 109,
-	MSM_RPM_8930_ID_PM8038_L5_0	= 110,
-	MSM_RPM_8930_ID_PM8038_L5_1	= 111,
-	MSM_RPM_8930_ID_PM8038_L6_0	= 112,
-	MSM_RPM_8930_ID_PM8038_L6_1	= 113,
-	MSM_RPM_8930_ID_PM8038_L7_0	= 114,
-	MSM_RPM_8930_ID_PM8038_L7_1	= 115,
-	MSM_RPM_8930_ID_PM8038_L8_0	= 116,
-	MSM_RPM_8930_ID_PM8038_L8_1	= 117,
-	MSM_RPM_8930_ID_PM8038_L9_0	= 118,
-	MSM_RPM_8930_ID_PM8038_L9_1	= 119,
-	MSM_RPM_8930_ID_PM8038_L10_0	= 120,
-	MSM_RPM_8930_ID_PM8038_L10_1	= 121,
-	MSM_RPM_8930_ID_PM8038_L11_0	= 122,
-	MSM_RPM_8930_ID_PM8038_L11_1	= 123,
-	MSM_RPM_8930_ID_PM8038_L12_0	= 124,
-	MSM_RPM_8930_ID_PM8038_L12_1	= 125,
-	MSM_RPM_8930_ID_PM8038_L13_0	= 126,
-	MSM_RPM_8930_ID_PM8038_L13_1	= 127,
-	MSM_RPM_8930_ID_PM8038_L14_0	= 128,
-	MSM_RPM_8930_ID_PM8038_L14_1	= 129,
-	MSM_RPM_8930_ID_PM8038_L15_0	= 130,
-	MSM_RPM_8930_ID_PM8038_L15_1	= 131,
-	MSM_RPM_8930_ID_PM8038_L16_0	= 132,
-	MSM_RPM_8930_ID_PM8038_L16_1	= 133,
-	MSM_RPM_8930_ID_PM8038_L17_0	= 134,
-	MSM_RPM_8930_ID_PM8038_L17_1	= 135,
-	MSM_RPM_8930_ID_PM8038_L18_0	= 136,
-	MSM_RPM_8930_ID_PM8038_L18_1	= 137,
-	MSM_RPM_8930_ID_PM8038_L19_0	= 138,
-	MSM_RPM_8930_ID_PM8038_L19_1	= 139,
-	MSM_RPM_8930_ID_PM8038_L20_0	= 140,
-	MSM_RPM_8930_ID_PM8038_L20_1	= 141,
-	MSM_RPM_8930_ID_PM8038_L21_0	= 142,
-	MSM_RPM_8930_ID_PM8038_L21_1	= 143,
-	MSM_RPM_8930_ID_PM8038_L22_0	= 144,
-	MSM_RPM_8930_ID_PM8038_L22_1	= 145,
-	MSM_RPM_8930_ID_PM8038_L23_0	= 146,
-	MSM_RPM_8930_ID_PM8038_L23_1	= 147,
-	MSM_RPM_8930_ID_PM8038_L24_0	= 148,
-	MSM_RPM_8930_ID_PM8038_L24_1	= 149,
-	MSM_RPM_8930_ID_PM8038_L25_0	= 150,
-	MSM_RPM_8930_ID_PM8038_L25_1	= 151,
-	MSM_RPM_8930_ID_PM8038_L26_0	= 152,
-	MSM_RPM_8930_ID_PM8038_L26_1	= 153,
-	MSM_RPM_8930_ID_PM8038_L27_0	= 154,
-	MSM_RPM_8930_ID_PM8038_L27_1	= 155,
-	MSM_RPM_8930_ID_PM8038_CLK1_0	= 156,
-	MSM_RPM_8930_ID_PM8038_CLK1_1	= 157,
-	MSM_RPM_8930_ID_PM8038_CLK2_0	= 158,
-	MSM_RPM_8930_ID_PM8038_CLK2_1	= 159,
-	MSM_RPM_8930_ID_PM8038_LVS1	= 160,
-	MSM_RPM_8930_ID_PM8038_LVS2	= 161,
+	MSM_RPM_8930_ID_PM8038_S1_0		= 90,
+	MSM_RPM_8930_ID_PM8038_S1_1		= 91,
+	MSM_RPM_8930_ID_PM8038_S2_0		= 92,
+	MSM_RPM_8930_ID_PM8038_S2_1		= 93,
+	MSM_RPM_8930_ID_PM8038_S3_0		= 94,
+	MSM_RPM_8930_ID_PM8038_S3_1		= 95,
+	MSM_RPM_8930_ID_PM8038_S4_0		= 96,
+	MSM_RPM_8930_ID_PM8038_S4_1		= 97,
+	MSM_RPM_8930_ID_PM8038_S5_0		= 98,
+	MSM_RPM_8930_ID_PM8038_S5_1		= 99,
+	MSM_RPM_8930_ID_PM8038_S6_0		= 100,
+	MSM_RPM_8930_ID_PM8038_S6_1		= 101,
+	MSM_RPM_8930_ID_PM8038_L1_0		= 102,
+	MSM_RPM_8930_ID_PM8038_L1_1		= 103,
+	MSM_RPM_8930_ID_PM8038_L2_0		= 104,
+	MSM_RPM_8930_ID_PM8038_L2_1		= 105,
+	MSM_RPM_8930_ID_PM8038_L3_0		= 106,
+	MSM_RPM_8930_ID_PM8038_L3_1		= 107,
+	MSM_RPM_8930_ID_PM8038_L4_0		= 108,
+	MSM_RPM_8930_ID_PM8038_L4_1		= 109,
+	MSM_RPM_8930_ID_PM8038_L5_0		= 110,
+	MSM_RPM_8930_ID_PM8038_L5_1		= 111,
+	MSM_RPM_8930_ID_PM8038_L6_0		= 112,
+	MSM_RPM_8930_ID_PM8038_L6_1		= 113,
+	MSM_RPM_8930_ID_PM8038_L7_0		= 114,
+	MSM_RPM_8930_ID_PM8038_L7_1		= 115,
+	MSM_RPM_8930_ID_PM8038_L8_0		= 116,
+	MSM_RPM_8930_ID_PM8038_L8_1		= 117,
+	MSM_RPM_8930_ID_PM8038_L9_0		= 118,
+	MSM_RPM_8930_ID_PM8038_L9_1		= 119,
+	MSM_RPM_8930_ID_PM8038_L10_0		= 120,
+	MSM_RPM_8930_ID_PM8038_L10_1		= 121,
+	MSM_RPM_8930_ID_PM8038_L11_0		= 122,
+	MSM_RPM_8930_ID_PM8038_L11_1		= 123,
+	MSM_RPM_8930_ID_PM8038_L12_0		= 124,
+	MSM_RPM_8930_ID_PM8038_L12_1		= 125,
+	MSM_RPM_8930_ID_PM8038_L13_0		= 126,
+	MSM_RPM_8930_ID_PM8038_L13_1		= 127,
+	MSM_RPM_8930_ID_PM8038_L14_0		= 128,
+	MSM_RPM_8930_ID_PM8038_L14_1		= 129,
+	MSM_RPM_8930_ID_PM8038_L15_0		= 130,
+	MSM_RPM_8930_ID_PM8038_L15_1		= 131,
+	MSM_RPM_8930_ID_PM8038_L16_0		= 132,
+	MSM_RPM_8930_ID_PM8038_L16_1		= 133,
+	MSM_RPM_8930_ID_PM8038_L17_0		= 134,
+	MSM_RPM_8930_ID_PM8038_L17_1		= 135,
+	MSM_RPM_8930_ID_PM8038_L18_0		= 136,
+	MSM_RPM_8930_ID_PM8038_L18_1		= 137,
+	MSM_RPM_8930_ID_PM8038_L19_0		= 138,
+	MSM_RPM_8930_ID_PM8038_L19_1		= 139,
+	MSM_RPM_8930_ID_PM8038_L20_0		= 140,
+	MSM_RPM_8930_ID_PM8038_L20_1		= 141,
+	MSM_RPM_8930_ID_PM8038_L21_0		= 142,
+	MSM_RPM_8930_ID_PM8038_L21_1		= 143,
+	MSM_RPM_8930_ID_PM8038_L22_0		= 144,
+	MSM_RPM_8930_ID_PM8038_L22_1		= 145,
+	MSM_RPM_8930_ID_PM8038_L23_0		= 146,
+	MSM_RPM_8930_ID_PM8038_L23_1		= 147,
+	MSM_RPM_8930_ID_PM8038_L24_0		= 148,
+	MSM_RPM_8930_ID_PM8038_L24_1		= 149,
+	MSM_RPM_8930_ID_PM8038_L25_0		= 150,
+	MSM_RPM_8930_ID_PM8038_L25_1		= 151,
+	MSM_RPM_8930_ID_PM8038_L26_0		= 152,
+	MSM_RPM_8930_ID_PM8038_L26_1		= 153,
+	MSM_RPM_8930_ID_PM8038_L27_0		= 154,
+	MSM_RPM_8930_ID_PM8038_L27_1		= 155,
+	MSM_RPM_8930_ID_PM8038_CLK1_0		= 156,
+	MSM_RPM_8930_ID_PM8038_CLK1_1		= 157,
+	MSM_RPM_8930_ID_PM8038_CLK2_0		= 158,
+	MSM_RPM_8930_ID_PM8038_CLK2_1		= 159,
+	MSM_RPM_8930_ID_PM8038_LVS1		= 160,
+	MSM_RPM_8930_ID_PM8038_LVS2		= 161,
+	MSM_RPM_8930_ID_PM8038_NCP_0		= 162,
+	MSM_RPM_8930_ID_PM8038_NCP_1		= 163,
+	MSM_RPM_8930_ID_PM8038_CXO_BUFFERS	= 164,
+	MSM_RPM_8930_ID_PM8038_USB_OTG_SWITCH	= 165,
+	MSM_RPM_8930_ID_PM8038_HDMI_SWITCH	= 166,
+	MSM_RPM_8930_ID_PM8038_QDSS_CLK		= 167,
+	MSM_RPM_8930_ID_PM8038_VOLTAGE_CORNER	= 168,
+	MSM_RPM_8930_ID_PM8038_LAST = MSM_RPM_8930_ID_PM8038_VOLTAGE_CORNER,
 
 	/* PMIC 8917 */
-	MSM_RPM_8930_ID_PM8917_S1_0	= 90,
-	MSM_RPM_8930_ID_PM8917_S1_1	= 91,
-	MSM_RPM_8930_ID_PM8917_S2_0	= 92,
-	MSM_RPM_8930_ID_PM8917_S2_1	= 93,
-	MSM_RPM_8930_ID_PM8917_S3_0	= 94,
-	MSM_RPM_8930_ID_PM8917_S3_1	= 95,
-	MSM_RPM_8930_ID_PM8917_S4_0	= 96,
-	MSM_RPM_8930_ID_PM8917_S4_1	= 97,
-	MSM_RPM_8930_ID_PM8917_S5_0	= 98,
-	MSM_RPM_8930_ID_PM8917_S5_1	= 99,
-	MSM_RPM_8930_ID_PM8917_S6_0	= 100,
-	MSM_RPM_8930_ID_PM8917_S6_1	= 101,
-	MSM_RPM_8930_ID_PM8917_S7_0	= 102,
-	MSM_RPM_8930_ID_PM8917_S7_1	= 103,
-	MSM_RPM_8930_ID_PM8917_S8_0	= 104,
-	MSM_RPM_8930_ID_PM8917_S8_1	= 105,
-	MSM_RPM_8930_ID_PM8917_L1_0	= 106,
-	MSM_RPM_8930_ID_PM8917_L1_1	= 107,
-	MSM_RPM_8930_ID_PM8917_L2_0	= 108,
-	MSM_RPM_8930_ID_PM8917_L2_1	= 109,
-	MSM_RPM_8930_ID_PM8917_L3_0	= 110,
-	MSM_RPM_8930_ID_PM8917_L3_1	= 111,
-	MSM_RPM_8930_ID_PM8917_L4_0	= 112,
-	MSM_RPM_8930_ID_PM8917_L4_1	= 113,
-	MSM_RPM_8930_ID_PM8917_L5_0	= 114,
-	MSM_RPM_8930_ID_PM8917_L5_1	= 115,
-	MSM_RPM_8930_ID_PM8917_L6_0	= 116,
-	MSM_RPM_8930_ID_PM8917_L6_1	= 117,
-	MSM_RPM_8930_ID_PM8917_L7_0	= 118,
-	MSM_RPM_8930_ID_PM8917_L7_1	= 119,
-	MSM_RPM_8930_ID_PM8917_L8_0	= 120,
-	MSM_RPM_8930_ID_PM8917_L8_1	= 121,
-	MSM_RPM_8930_ID_PM8917_L9_0	= 122,
-	MSM_RPM_8930_ID_PM8917_L9_1	= 123,
-	MSM_RPM_8930_ID_PM8917_L10_0	= 124,
-	MSM_RPM_8930_ID_PM8917_L10_1	= 125,
-	MSM_RPM_8930_ID_PM8917_L11_0	= 126,
-	MSM_RPM_8930_ID_PM8917_L11_1	= 127,
-	MSM_RPM_8930_ID_PM8917_L12_0	= 128,
-	MSM_RPM_8930_ID_PM8917_L12_1	= 129,
-	MSM_RPM_8930_ID_PM8917_L14_0	= 130,
-	MSM_RPM_8930_ID_PM8917_L14_1	= 131,
-	MSM_RPM_8930_ID_PM8917_L15_0	= 132,
-	MSM_RPM_8930_ID_PM8917_L15_1	= 133,
-	MSM_RPM_8930_ID_PM8917_L16_0	= 134,
-	MSM_RPM_8930_ID_PM8917_L16_1	= 135,
-	MSM_RPM_8930_ID_PM8917_L17_0	= 136,
-	MSM_RPM_8930_ID_PM8917_L17_1	= 137,
-	MSM_RPM_8930_ID_PM8917_L18_0	= 138,
-	MSM_RPM_8930_ID_PM8917_L18_1	= 139,
-	MSM_RPM_8930_ID_PM8917_L21_0	= 140,
-	MSM_RPM_8930_ID_PM8917_L21_1	= 141,
-	MSM_RPM_8930_ID_PM8917_L22_0	= 142,
-	MSM_RPM_8930_ID_PM8917_L22_1	= 143,
-	MSM_RPM_8930_ID_PM8917_L23_0	= 144,
-	MSM_RPM_8930_ID_PM8917_L23_1	= 145,
-	MSM_RPM_8930_ID_PM8917_L24_0	= 146,
-	MSM_RPM_8930_ID_PM8917_L24_1	= 147,
-	MSM_RPM_8930_ID_PM8917_L25_0	= 148,
-	MSM_RPM_8930_ID_PM8917_L25_1	= 149,
-	MSM_RPM_8930_ID_PM8917_L26_0	= 150,
-	MSM_RPM_8930_ID_PM8917_L26_1	= 151,
-	MSM_RPM_8930_ID_PM8917_L27_0	= 152,
-	MSM_RPM_8930_ID_PM8917_L27_1	= 153,
-	MSM_RPM_8930_ID_PM8917_L28_0	= 154,
-	MSM_RPM_8930_ID_PM8917_L28_1	= 155,
-	MSM_RPM_8930_ID_PM8917_L29_0	= 156,
-	MSM_RPM_8930_ID_PM8917_L29_1	= 157,
-	MSM_RPM_8930_ID_PM8917_L30_0	= 158,
-	MSM_RPM_8930_ID_PM8917_L30_1	= 159,
-	MSM_RPM_8930_ID_PM8917_L31_0	= 160,
-	MSM_RPM_8930_ID_PM8917_L31_1	= 161,
-	MSM_RPM_8930_ID_PM8917_L32_0	= 162,
-	MSM_RPM_8930_ID_PM8917_L32_1	= 163,
-	MSM_RPM_8930_ID_PM8917_L33_0	= 164,
-	MSM_RPM_8930_ID_PM8917_L33_1	= 165,
-	MSM_RPM_8930_ID_PM8917_L34_0	= 166,
-	MSM_RPM_8930_ID_PM8917_L34_1	= 167,
-	MSM_RPM_8930_ID_PM8917_L35_0	= 168,
-	MSM_RPM_8930_ID_PM8917_L35_1	= 169,
-	MSM_RPM_8930_ID_PM8917_L36_0	= 170,
-	MSM_RPM_8930_ID_PM8917_L36_1	= 171,
-	MSM_RPM_8930_ID_PM8917_CLK1_0	= 172,
-	MSM_RPM_8930_ID_PM8917_CLK1_1	= 173,
-	MSM_RPM_8930_ID_PM8917_CLK2_0	= 174,
-	MSM_RPM_8930_ID_PM8917_CLK2_1	= 175,
-	MSM_RPM_8930_ID_PM8917_LVS1	= 176,
-	MSM_RPM_8930_ID_PM8917_LVS3	= 177,
-	MSM_RPM_8930_ID_PM8917_LVS4	= 178,
-	MSM_RPM_8930_ID_PM8917_LVS5	= 179,
-	MSM_RPM_8930_ID_PM8917_LVS6	= 180,
-	MSM_RPM_8930_ID_PM8917_LVS7	= 181,
+	MSM_RPM_8930_ID_PM8917_S1_0		= 90,
+	MSM_RPM_8930_ID_PM8917_S1_1		= 91,
+	MSM_RPM_8930_ID_PM8917_S2_0		= 92,
+	MSM_RPM_8930_ID_PM8917_S2_1		= 93,
+	MSM_RPM_8930_ID_PM8917_S3_0		= 94,
+	MSM_RPM_8930_ID_PM8917_S3_1		= 95,
+	MSM_RPM_8930_ID_PM8917_S4_0		= 96,
+	MSM_RPM_8930_ID_PM8917_S4_1		= 97,
+	MSM_RPM_8930_ID_PM8917_S5_0		= 98,
+	MSM_RPM_8930_ID_PM8917_S5_1		= 99,
+	MSM_RPM_8930_ID_PM8917_S6_0		= 100,
+	MSM_RPM_8930_ID_PM8917_S6_1		= 101,
+	MSM_RPM_8930_ID_PM8917_S7_0		= 102,
+	MSM_RPM_8930_ID_PM8917_S7_1		= 103,
+	MSM_RPM_8930_ID_PM8917_S8_0		= 104,
+	MSM_RPM_8930_ID_PM8917_S8_1		= 105,
+	MSM_RPM_8930_ID_PM8917_L1_0		= 106,
+	MSM_RPM_8930_ID_PM8917_L1_1		= 107,
+	MSM_RPM_8930_ID_PM8917_L2_0		= 108,
+	MSM_RPM_8930_ID_PM8917_L2_1		= 109,
+	MSM_RPM_8930_ID_PM8917_L3_0		= 110,
+	MSM_RPM_8930_ID_PM8917_L3_1		= 111,
+	MSM_RPM_8930_ID_PM8917_L4_0		= 112,
+	MSM_RPM_8930_ID_PM8917_L4_1		= 113,
+	MSM_RPM_8930_ID_PM8917_L5_0		= 114,
+	MSM_RPM_8930_ID_PM8917_L5_1		= 115,
+	MSM_RPM_8930_ID_PM8917_L6_0		= 116,
+	MSM_RPM_8930_ID_PM8917_L6_1		= 117,
+	MSM_RPM_8930_ID_PM8917_L7_0		= 118,
+	MSM_RPM_8930_ID_PM8917_L7_1		= 119,
+	MSM_RPM_8930_ID_PM8917_L8_0		= 120,
+	MSM_RPM_8930_ID_PM8917_L8_1		= 121,
+	MSM_RPM_8930_ID_PM8917_L9_0		= 122,
+	MSM_RPM_8930_ID_PM8917_L9_1		= 123,
+	MSM_RPM_8930_ID_PM8917_L10_0		= 124,
+	MSM_RPM_8930_ID_PM8917_L10_1		= 125,
+	MSM_RPM_8930_ID_PM8917_L11_0		= 126,
+	MSM_RPM_8930_ID_PM8917_L11_1		= 127,
+	MSM_RPM_8930_ID_PM8917_L12_0		= 128,
+	MSM_RPM_8930_ID_PM8917_L12_1		= 129,
+	MSM_RPM_8930_ID_PM8917_L14_0		= 130,
+	MSM_RPM_8930_ID_PM8917_L14_1		= 131,
+	MSM_RPM_8930_ID_PM8917_L15_0		= 132,
+	MSM_RPM_8930_ID_PM8917_L15_1		= 133,
+	MSM_RPM_8930_ID_PM8917_L16_0		= 134,
+	MSM_RPM_8930_ID_PM8917_L16_1		= 135,
+	MSM_RPM_8930_ID_PM8917_L17_0		= 136,
+	MSM_RPM_8930_ID_PM8917_L17_1		= 137,
+	MSM_RPM_8930_ID_PM8917_L18_0		= 138,
+	MSM_RPM_8930_ID_PM8917_L18_1		= 139,
+	MSM_RPM_8930_ID_PM8917_L21_0		= 140,
+	MSM_RPM_8930_ID_PM8917_L21_1		= 141,
+	MSM_RPM_8930_ID_PM8917_L22_0		= 142,
+	MSM_RPM_8930_ID_PM8917_L22_1		= 143,
+	MSM_RPM_8930_ID_PM8917_L23_0		= 144,
+	MSM_RPM_8930_ID_PM8917_L23_1		= 145,
+	MSM_RPM_8930_ID_PM8917_L24_0		= 146,
+	MSM_RPM_8930_ID_PM8917_L24_1		= 147,
+	MSM_RPM_8930_ID_PM8917_L25_0		= 148,
+	MSM_RPM_8930_ID_PM8917_L25_1		= 149,
+	MSM_RPM_8930_ID_PM8917_L26_0		= 150,
+	MSM_RPM_8930_ID_PM8917_L26_1		= 151,
+	MSM_RPM_8930_ID_PM8917_L27_0		= 152,
+	MSM_RPM_8930_ID_PM8917_L27_1		= 153,
+	MSM_RPM_8930_ID_PM8917_L28_0		= 154,
+	MSM_RPM_8930_ID_PM8917_L28_1		= 155,
+	MSM_RPM_8930_ID_PM8917_L29_0		= 156,
+	MSM_RPM_8930_ID_PM8917_L29_1		= 157,
+	MSM_RPM_8930_ID_PM8917_L30_0		= 158,
+	MSM_RPM_8930_ID_PM8917_L30_1		= 159,
+	MSM_RPM_8930_ID_PM8917_L31_0		= 160,
+	MSM_RPM_8930_ID_PM8917_L31_1		= 161,
+	MSM_RPM_8930_ID_PM8917_L32_0		= 162,
+	MSM_RPM_8930_ID_PM8917_L32_1		= 163,
+	MSM_RPM_8930_ID_PM8917_L33_0		= 164,
+	MSM_RPM_8930_ID_PM8917_L33_1		= 165,
+	MSM_RPM_8930_ID_PM8917_L34_0		= 166,
+	MSM_RPM_8930_ID_PM8917_L34_1		= 167,
+	MSM_RPM_8930_ID_PM8917_L35_0		= 168,
+	MSM_RPM_8930_ID_PM8917_L35_1		= 169,
+	MSM_RPM_8930_ID_PM8917_L36_0		= 170,
+	MSM_RPM_8930_ID_PM8917_L36_1		= 171,
+	MSM_RPM_8930_ID_PM8917_CLK1_0		= 172,
+	MSM_RPM_8930_ID_PM8917_CLK1_1		= 173,
+	MSM_RPM_8930_ID_PM8917_CLK2_0		= 174,
+	MSM_RPM_8930_ID_PM8917_CLK2_1		= 175,
+	MSM_RPM_8930_ID_PM8917_LVS1		= 176,
+	MSM_RPM_8930_ID_PM8917_LVS3		= 177,
+	MSM_RPM_8930_ID_PM8917_LVS4		= 178,
+	MSM_RPM_8930_ID_PM8917_LVS5		= 179,
+	MSM_RPM_8930_ID_PM8917_LVS6		= 180,
+	MSM_RPM_8930_ID_PM8917_LVS7		= 181,
+	MSM_RPM_8930_ID_PM8917_NCP_0		= 182,
+	MSM_RPM_8930_ID_PM8917_NCP_1		= 183,
+	MSM_RPM_8930_ID_PM8917_CXO_BUFFERS	= 184,
+	MSM_RPM_8930_ID_PM8917_USB_OTG_SWITCH	= 185,
+	MSM_RPM_8930_ID_PM8917_HDMI_SWITCH	= 186,
+	MSM_RPM_8930_ID_PM8917_QDSS_CLK		= 187,
+	MSM_RPM_8930_ID_PM8917_VOLTAGE_CORNER	= 188,
 
-	MSM_RPM_8930_ID_NCP_0		= 182,
-	MSM_RPM_8930_ID_NCP_1		= 183,
-	MSM_RPM_8930_ID_CXO_BUFFERS	= 184,
-	MSM_RPM_8930_ID_USB_OTG_SWITCH	= 185,
-	MSM_RPM_8930_ID_HDMI_SWITCH	= 186,
-	MSM_RPM_8930_ID_QDSS_CLK	= 187,
-	MSM_RPM_8930_ID_VOLTAGE_CORNER	= 188,
-
-	MSM_RPM_8930_ID_LAST = MSM_RPM_8930_ID_VOLTAGE_CORNER,
+	MSM_RPM_8930_ID_LAST = MSM_RPM_8930_ID_PM8917_VOLTAGE_CORNER,
+	MSM_RPM_8930_ID_PM8917_LAST = MSM_RPM_8930_ID_LAST,
 };
 
 /* RPM status ID enum */
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/rpm.h b/arch/arm/mach-msm/include/mach/rpm.h
index 4ee1997..200a8cf 100644
--- a/arch/arm/mach-msm/include/mach/rpm.h
+++ b/arch/arm/mach-msm/include/mach/rpm.h
@@ -1086,6 +1086,14 @@
 		.count = c, \
 	}
 
+#define MSM_RPM_MAP_PMIC(_target, _pmic, _id, _select, _count) \
+	[MSM_RPM_ID_##_id] = \
+	{\
+		.id = MSM_RPM_##_target##_ID_PM##_pmic##_##_id, \
+		.sel = MSM_RPM_##_target##_SEL_##_select, \
+		.count = _count, \
+	}
+
 #define MSM_RPM_STATUS_ID_VALID BIT(31)
 
 #define MSM_RPM_STATUS_ID_MAP(t, i) \
diff --git a/arch/arm/mach-msm/include/mach/socinfo.h b/arch/arm/mach-msm/include/mach/socinfo.h
index 5c88101..0499a7a 100644
--- a/arch/arm/mach-msm/include/mach/socinfo.h
+++ b/arch/arm/mach-msm/include/mach/socinfo.h
@@ -101,6 +101,7 @@
 	MSM_CPU_8064AB,
 	MSM_CPU_8930,
 	MSM_CPU_8930AA,
+	MSM_CPU_8930AB,
 	MSM_CPU_7X27AA,
 	MSM_CPU_9615,
 	MSM_CPU_8974,
@@ -110,6 +111,7 @@
 	MSM_CPU_8092,
 	MSM_CPU_8226,
 	MSM_CPU_8910,
+	MSM_CPU_8625Q,
 };
 
 enum pmic_model {
@@ -342,6 +344,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 */
@@ -437,6 +448,18 @@
 #endif
 }
 
+static inline int cpu_is_msm8625q(void)
+{
+#ifdef CONFIG_ARCH_MSM8625
+	enum msm_cpu cpu = socinfo_get_msm_cpu();
+
+	BUG_ON(cpu == MSM_CPU_UNKNOWN);
+	return cpu == MSM_CPU_8625Q;
+#else
+	return 0;
+#endif
+}
+
 static inline int soc_class_is_msm8960(void)
 {
 	return cpu_is_msm8960() || cpu_is_msm8960ab();
@@ -449,7 +472,8 @@
 
 static inline int soc_class_is_msm8930(void)
 {
-	return cpu_is_msm8930() || cpu_is_msm8930aa() || cpu_is_msm8627();
+	return cpu_is_msm8930() || cpu_is_msm8930aa() || cpu_is_msm8930ab() ||
+	       cpu_is_msm8627();
 }
 
 #endif
diff --git a/arch/arm/mach-msm/include/mach/sps.h b/arch/arm/mach-msm/include/mach/sps.h
index 5333c2e..a000c3e 100644
--- a/arch/arm/mach-msm/include/mach/sps.h
+++ b/arch/arm/mach-msm/include/mach/sps.h
@@ -160,6 +160,8 @@
 	SPS_O_AUTO_ENABLE = 0x20000000,
 	/* DISABLE endpoint synchronization for config/enable/disable */
 	SPS_O_NO_EP_SYNC = 0x40000000,
+	/* Allow partial polling duing IRQ mode */
+	SPS_O_HYBRID = 0x80000000,
 };
 
 /**
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/include/mach/usb_bam.h b/arch/arm/mach-msm/include/mach/usb_bam.h
index 47313a7..5e1ef6f 100644
--- a/arch/arm/mach-msm/include/mach/usb_bam.h
+++ b/arch/arm/mach-msm/include/mach/usb_bam.h
@@ -13,6 +13,7 @@
 #ifndef _USB_BAM_H_
 #define _USB_BAM_H_
 #include "sps.h"
+#include <mach/ipa.h>
 
 /**
  * SPS Pipes direction.
@@ -27,6 +28,22 @@
 	PEER_PERIPHERAL_TO_USB,
 };
 
+struct usb_bam_connect_ipa_params {
+	u8 idx;
+	u32 *src_pipe;
+	u32 *dst_pipe;
+	enum usb_bam_pipe_dir dir;
+	/* client handle assigned by IPA to client */
+	u32 prod_clnt_hdl;
+	u32 cons_clnt_hdl;
+	/* params assigned by the CD */
+	enum ipa_client_type client;
+	struct ipa_ep_cfg ipa_ep_cfg;
+	void *priv;
+	void (*notify)(void *priv, enum ipa_dp_evt_type evt,
+			unsigned long data);
+};
+
 #ifdef CONFIG_USB_BAM
 /**
  * Connect USB-to-Periperal SPS connection.
@@ -47,6 +64,31 @@
 int usb_bam_connect(u8 idx, u32 *src_pipe_idx, u32 *dst_pipe_idx);
 
 /**
+ * Connect USB-to-IPA SPS connection.
+ *
+ * This function returns the allocated pipes number adn clnt handles.
+ *
+ * @ipa_params - in/out parameters
+ *
+ * @return 0 on success, negative value on error
+ *
+ */
+int usb_bam_connect_ipa(struct usb_bam_connect_ipa_params *ipa_params);
+
+/**
+ * Disconnect USB-to-IPA SPS connection.
+ *
+ * @idx - Connection index.
+ *
+ * @ipa_params - in/out parameters
+ *
+ * @return 0 on success, negative value on error
+ *
+ */
+int usb_bam_disconnect_ipa(u8 idx,
+		struct usb_bam_connect_ipa_params *ipa_params);
+
+/**
  * Register a wakeup callback from peer BAM.
  *
  * @idx - Connection index.
@@ -96,6 +138,18 @@
 	return -ENODEV;
 }
 
+static inline int usb_bam_connect_ipa(
+			struct usb_bam_connect_ipa_params *ipa_params)
+{
+	return -ENODEV;
+}
+
+static inline int usb_bam_disconnect_ipa(u8 idx,
+			struct usb_bam_connect_ipa_params *ipa_params)
+{
+	return -ENODEV;
+}
+
 static inline int usb_bam_register_wake_cb(u8 idx,
 	int (*callback)(void *), void* param)
 {
diff --git a/arch/arm/mach-msm/include/mach/usb_gadget_xport.h b/arch/arm/mach-msm/include/mach/usb_gadget_xport.h
index be11989..41dac62 100644
--- a/arch/arm/mach-msm/include/mach/usb_gadget_xport.h
+++ b/arch/arm/mach-msm/include/mach/usb_gadget_xport.h
@@ -21,12 +21,13 @@
 	USB_GADGET_XPORT_SMD,
 	USB_GADGET_XPORT_BAM,
 	USB_GADGET_XPORT_BAM2BAM,
+	USB_GADGET_XPORT_BAM2BAM_IPA,
 	USB_GADGET_XPORT_HSIC,
 	USB_GADGET_XPORT_HSUART,
 	USB_GADGET_XPORT_NONE,
 };
 
-#define XPORT_STR_LEN	10
+#define XPORT_STR_LEN	12
 
 static char *xport_to_str(enum transport_type t)
 {
@@ -41,6 +42,8 @@
 		return "BAM";
 	case USB_GADGET_XPORT_BAM2BAM:
 		return "BAM2BAM";
+	case USB_GADGET_XPORT_BAM2BAM_IPA:
+		return "BAM2BAM_IPA";
 	case USB_GADGET_XPORT_HSIC:
 		return "HSIC";
 	case USB_GADGET_XPORT_HSUART:
@@ -64,6 +67,8 @@
 		return USB_GADGET_XPORT_BAM;
 	if (!strncasecmp("BAM2BAM", name, XPORT_STR_LEN))
 		return USB_GADGET_XPORT_BAM2BAM;
+	if (!strncasecmp("BAM2BAM_IPA", name, XPORT_STR_LEN))
+		return USB_GADGET_XPORT_BAM2BAM_IPA;
 	if (!strncasecmp("HSIC", name, XPORT_STR_LEN))
 		return USB_GADGET_XPORT_HSIC;
 	if (!strncasecmp("HSUART", name, XPORT_STR_LEN))
diff --git a/arch/arm/mach-msm/include/mach/usbdiag.h b/arch/arm/mach-msm/include/mach/usbdiag.h
index d1e3605..d9320c3 100644
--- a/arch/arm/mach-msm/include/mach/usbdiag.h
+++ b/arch/arm/mach-msm/include/mach/usbdiag.h
@@ -1,6 +1,6 @@
 /* include/asm-arm/arch-msm/usbdiag.h
  *
- * Copyright (c) 2008-2010, Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2008-2010, 2012, The Linux Foundation. All rights reserved.
  *
  * All source code in this file is licensed under the following license except
  * where indicated.
@@ -21,8 +21,11 @@
 #ifndef _DRIVERS_USB_DIAG_H_
 #define _DRIVERS_USB_DIAG_H_
 
+#include <linux/err.h>
+
 #define DIAG_LEGACY		"diag"
 #define DIAG_MDM		"diag_mdm"
+#define DIAG_QSC		"diag_qsc"
 
 #define USB_DIAG_CONNECT	0
 #define USB_DIAG_DISCONNECT	1
@@ -45,6 +48,7 @@
 	void *priv_usb;
 };
 
+#ifdef CONFIG_USB_G_ANDROID
 struct usb_diag_ch *usb_diag_open(const char *name, void *priv,
 		void (*notify)(void *, unsigned, struct diag_request *));
 void usb_diag_close(struct usb_diag_ch *ch);
@@ -52,7 +56,32 @@
 void usb_diag_free_req(struct usb_diag_ch *ch);
 int usb_diag_read(struct usb_diag_ch *ch, struct diag_request *d_req);
 int usb_diag_write(struct usb_diag_ch *ch, struct diag_request *d_req);
-
-int diag_read_from_cb(unsigned char * , int);
-
+#else
+static inline struct usb_diag_ch *usb_diag_open(const char *name, void *priv,
+		void (*notify)(void *, unsigned, struct diag_request *))
+{
+	return ERR_PTR(-ENODEV);
+}
+static inline void usb_diag_close(struct usb_diag_ch *ch)
+{
+}
+static inline
+int usb_diag_alloc_req(struct usb_diag_ch *ch, int n_write, int n_read)
+{
+	return -ENODEV;
+}
+static inline void usb_diag_free_req(struct usb_diag_ch *ch)
+{
+}
+static inline
+int usb_diag_read(struct usb_diag_ch *ch, struct diag_request *d_req)
+{
+	return -ENODEV;
+}
+static inline
+int usb_diag_write(struct usb_diag_ch *ch, struct diag_request *d_req)
+{
+	return -ENODEV;
+}
+#endif /* CONFIG_USB_G_ANDROID */
 #endif /* _DRIVERS_USB_DIAG_H_ */
diff --git a/arch/arm/mach-msm/io.c b/arch/arm/mach-msm/io.c
index c2c9233..cd70ae9 100644
--- a/arch/arm/mach-msm/io.c
+++ b/arch/arm/mach-msm/io.c
@@ -430,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) || \
@@ -517,7 +519,7 @@
 	MSM_CHIP_DEVICE(APCS_GCC, MSM8226),
 	MSM_CHIP_DEVICE(TLMM, MSM8226),
 	MSM_CHIP_DEVICE(IMEM, MSM8226),
-	MSM_CHIP_DEVICE(DBG_IMEM, MSM8226),
+	MSM_CHIP_DEVICE(MPM2_PSHOLD, MSM8226),
 	{
 		.virtual =  (unsigned long) MSM_SHARED_RAM_BASE,
 		.length =   MSM_SHARED_RAM_SIZE,
@@ -540,6 +542,7 @@
 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,
diff --git a/arch/arm/mach-msm/ipc_router.c b/arch/arm/mach-msm/ipc_router.c
index c82eac1..fde43b0 100644
--- a/arch/arm/mach-msm/ipc_router.c
+++ b/arch/arm/mach-msm/ipc_router.c
@@ -33,9 +33,11 @@
 
 #include <mach/smem_log.h>
 #include <mach/subsystem_notif.h>
+#include <mach/msm_ipc_router.h>
 
 #include "ipc_router.h"
 #include "modem_notifier.h"
+#include "msm_ipc_router_security.h"
 
 enum {
 	SMEM_LOG = 1U << 0,
@@ -96,6 +98,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);
@@ -104,11 +113,15 @@
 struct msm_ipc_server {
 	struct list_head list;
 	struct msm_ipc_port_name name;
+	char pdev_name[32];
+	int next_pdev_id;
+	int synced_sec_rule;
 	struct list_head server_port_list;
 };
 
 struct msm_ipc_server_port {
 	struct list_head list;
+	struct platform_device pdev;
 	struct msm_ipc_port_addr server_addr;
 	struct msm_ipc_router_xprt_info *xprt_info;
 };
@@ -122,6 +135,7 @@
 	wait_queue_head_t quota_wait;
 	uint32_t tx_quota_cnt;
 	struct mutex quota_lock;
+	void *sec_rule;
 };
 
 struct msm_ipc_router_xprt_info {
@@ -174,7 +188,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)
 
@@ -360,6 +373,102 @@
 	return;
 }
 
+static struct sk_buff_head *msm_ipc_router_buf_to_skb(void *buf,
+						unsigned int buf_len)
+{
+	struct sk_buff_head *skb_head;
+	struct sk_buff *skb;
+	int first = 1, offset = 0;
+	int skb_size, data_size;
+	void *data;
+
+	skb_head = kmalloc(sizeof(struct sk_buff_head), GFP_KERNEL);
+	if (!skb_head) {
+		pr_err("%s: Couldnot allocate skb_head\n", __func__);
+		return NULL;
+	}
+	skb_queue_head_init(skb_head);
+
+	data_size = buf_len;
+	while (offset != buf_len) {
+		skb_size = data_size;
+		if (first)
+			skb_size += IPC_ROUTER_HDR_SIZE;
+
+		skb = alloc_skb(skb_size, GFP_KERNEL);
+		if (!skb) {
+			if (skb_size <= (PAGE_SIZE/2)) {
+				pr_err("%s: cannot allocate skb\n", __func__);
+				goto buf_to_skb_error;
+			}
+			data_size = data_size / 2;
+			continue;
+		}
+
+		if (first) {
+			skb_reserve(skb, IPC_ROUTER_HDR_SIZE);
+			first = 0;
+		}
+
+		data = skb_put(skb, data_size);
+		memcpy(skb->data, buf + offset, data_size);
+		skb_queue_tail(skb_head, skb);
+		offset += data_size;
+		data_size = buf_len - offset;
+	}
+	return skb_head;
+
+buf_to_skb_error:
+	while (!skb_queue_empty(skb_head)) {
+		skb = skb_dequeue(skb_head);
+		kfree_skb(skb);
+	}
+	kfree(skb_head);
+	return NULL;
+}
+
+static void *msm_ipc_router_skb_to_buf(struct sk_buff_head *skb_head,
+				       unsigned int len)
+{
+	struct sk_buff *temp;
+	int offset = 0, buf_len = 0, copy_len;
+	void *buf;
+
+	if (!skb_head) {
+		pr_err("%s: NULL skb_head\n", __func__);
+		return NULL;
+	}
+
+	temp = skb_peek(skb_head);
+	buf_len = len;
+	buf = kmalloc(buf_len, GFP_KERNEL);
+	if (!buf) {
+		pr_err("%s: cannot allocate buf\n", __func__);
+		return NULL;
+	}
+	skb_queue_walk(skb_head, temp) {
+		copy_len = buf_len < temp->len ? buf_len : temp->len;
+		memcpy(buf + offset, temp->data, copy_len);
+		offset += copy_len;
+		buf_len -= copy_len;
+	}
+	return buf;
+}
+
+static void msm_ipc_router_free_skb(struct sk_buff_head *skb_head)
+{
+	struct sk_buff *temp_skb;
+
+	if (!skb_head)
+		return;
+
+	while (!skb_queue_empty(skb_head)) {
+		temp_skb = skb_dequeue(skb_head);
+		kfree_skb(temp_skb);
+	}
+	kfree(skb_head);
+}
+
 static int post_control_ports(struct rr_packet *pkt)
 {
 	struct msm_ipc_port *port_ptr;
@@ -431,8 +540,7 @@
 }
 
 struct msm_ipc_port *msm_ipc_router_create_raw_port(void *endpoint,
-		void (*notify)(unsigned event, void *data,
-			       void *addr, void *priv),
+		void (*notify)(unsigned event, void *priv),
 		void *priv)
 {
 	struct msm_ipc_port *port_ptr;
@@ -546,6 +654,7 @@
 	rport_ptr->port_id = port_id;
 	rport_ptr->node_id = node_id;
 	rport_ptr->restart_state = RESTART_NORMAL;
+	rport_ptr->sec_rule = NULL;
 	rport_ptr->tx_quota_cnt = 0;
 	init_waitqueue_head(&rport_ptr->quota_wait);
 	mutex_init(&rport_ptr->quota_lock);
@@ -582,6 +691,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 +713,43 @@
 {
 	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;
 }
 
+static void dummy_release(struct device *dev)
+{
+}
+
+/**
+ * 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,34 +759,35 @@
 {
 	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))
 			goto create_srv_port;
 	}
 
-	server = kmalloc(sizeof(struct msm_ipc_server), GFP_KERNEL);
+	server = kzalloc(sizeof(struct msm_ipc_server), GFP_KERNEL);
 	if (!server) {
-		mutex_unlock(&server_list_lock);
 		pr_err("%s: Server allocation failed\n", __func__);
 		return NULL;
 	}
 	server->name.service = service;
 	server->name.instance = instance;
+	server->synced_sec_rule = 0;
 	INIT_LIST_HEAD(&server->server_port_list);
 	list_add_tail(&server->list, &server_list[key]);
+	scnprintf(server->pdev_name, sizeof(server->pdev_name),
+		  "QMI%08x:%08x", service, instance);
+	server->next_pdev_id = 1;
 
 create_srv_port:
-	server_port = kmalloc(sizeof(struct msm_ipc_server_port), GFP_KERNEL);
+	server_port = kzalloc(sizeof(struct msm_ipc_server_port), GFP_KERNEL);
 	if (!server_port) {
 		if (list_empty(&server->server_port_list)) {
 			list_del(&server->list);
 			kfree(server);
 		}
-		mutex_unlock(&server_list_lock);
 		pr_err("%s: Server Port allocation failed\n", __func__);
 		return NULL;
 	}
@@ -658,11 +795,27 @@
 	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);
+
+	server_port->pdev.name = server->pdev_name;
+	server_port->pdev.id = server->next_pdev_id++;
+	server_port->pdev.dev.release = dummy_release;
+	platform_device_register(&server_port->pdev);
 
 	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,13 +824,13 @@
 	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))
 			break;
 	}
 	if (server_port) {
+		platform_device_unregister(&server_port->pdev);
 		list_del(&server_port->list);
 		kfree(server_port);
 	}
@@ -685,7 +838,6 @@
 		list_del(&server->list);
 		kfree(server);
 	}
-	mutex_unlock(&server_list_lock);
 	return;
 }
 
@@ -765,7 +917,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 +932,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 +951,6 @@
 			}
 		}
 	}
-	mutex_unlock(&server_list_lock);
 
 	return 0;
 }
@@ -1046,6 +1196,7 @@
 				ctl.srv.port_id = svr_port->server_addr.port_id;
 				relay_ctl_msg(xprt_info, &ctl);
 				broadcast_ctl_msg_locally(&ctl);
+				platform_device_unregister(&svr_port->pdev);
 				list_del(&svr_port->list);
 				kfree(svr_port);
 			}
@@ -1166,14 +1317,178 @@
 	msm_ipc_cleanup_routing_table(xprt_info);
 }
 
+/**
+ * sync_sec_rule() - Synchrnoize the security rule into the server structure
+ * @server: Server structure where the rule has to be synchronized.
+ * @rule: Security tule to be synchronized.
+ *
+ * This function is used to update the server structure with the security
+ * rule configured for the <service:instance> corresponding to that server.
+ */
+static void sync_sec_rule(struct msm_ipc_server *server, void *rule)
+{
+	struct msm_ipc_server_port *server_port;
+	struct msm_ipc_router_remote_port *rport_ptr = NULL;
+
+	list_for_each_entry(server_port, &server->server_port_list, list) {
+		rport_ptr = msm_ipc_router_lookup_remote_port(
+				server_port->server_addr.node_id,
+				server_port->server_addr.port_id);
+		if (!rport_ptr)
+			continue;
+		rport_ptr->sec_rule = rule;
+	}
+	server->synced_sec_rule = 1;
+}
+
+/**
+ * msm_ipc_sync_sec_rule() - Sync the security rule to the service
+ * @service: Service for which the rule has to be synchronized.
+ * @instance: Instance for which the rule has to be synchronized.
+ * @rule: Security rule to be synchronized.
+ *
+ * This function is used to syncrhonize the security rule with the server
+ * hash table, if the user-space script configures the rule after the service
+ * has come up. This function is used to synchronize the security rule to a
+ * specific service and optionally a specific instance.
+ */
+void msm_ipc_sync_sec_rule(uint32_t service, uint32_t instance, void *rule)
+{
+	int key = (service & (SRV_HASH_SIZE - 1));
+	struct msm_ipc_server *server;
+
+	mutex_lock(&server_list_lock);
+	list_for_each_entry(server, &server_list[key], list) {
+		if (server->name.service != service)
+			continue;
+
+		if (server->name.instance != instance &&
+		    instance != ALL_INSTANCE)
+			continue;
+
+		/*
+		 * If the rule applies to all instances and if the specific
+		 * instance of a service has a rule synchronized already,
+		 * do not apply the rule for that specific instance.
+		 */
+		if (instance == ALL_INSTANCE && server->synced_sec_rule)
+			continue;
+
+		sync_sec_rule(server, rule);
+	}
+	mutex_unlock(&server_list_lock);
+}
+
+/**
+ * msm_ipc_sync_default_sec_rule() - Default security rule to all services
+ * @rule: Security rule to be synchronized.
+ *
+ * This function is used to syncrhonize the security rule with the server
+ * hash table, if the user-space script configures the rule after the service
+ * has come up. This function is used to synchronize the security rule that
+ * applies to all services, if the concerned service do not have any rule
+ * defined.
+ */
+void msm_ipc_sync_default_sec_rule(void *rule)
+{
+	int key;
+	struct msm_ipc_server *server;
+
+	mutex_lock(&server_list_lock);
+	for (key = 0; key < SRV_HASH_SIZE; key++) {
+		list_for_each_entry(server, &server_list[key], list) {
+			if (server->synced_sec_rule)
+				continue;
+
+			sync_sec_rule(server, rule);
+		}
+	}
+	mutex_unlock(&server_list_lock);
+}
+
+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 +1514,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 +1563,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 +1573,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;
 			}
@@ -1302,9 +1585,13 @@
 				if (!rport_ptr)
 					pr_err("%s: Remote port create "
 					       "failed\n", __func__);
+				rport_ptr->sec_rule =
+					msm_ipc_get_security_rule(
+					msg->srv.service, msg->srv.instance);
 			}
 			wake_up(&newserver_wait);
 		}
+		mutex_unlock(&server_list_lock);
 
 		relay_msg(xprt_info, pkt);
 		post_control_ports(pkt);
@@ -1312,6 +1599,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 +1611,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",
@@ -1353,7 +1642,6 @@
 	struct rr_packet *pkt = NULL;
 	struct msm_ipc_port *port_ptr;
 	struct sk_buff *head_skb;
-	struct msm_ipc_port_addr *src_addr;
 	struct msm_ipc_router_remote_port *rport_ptr;
 	uint32_t resume_tx, resume_tx_node_id, resume_tx_port_id;
 
@@ -1448,30 +1736,15 @@
 			}
 		}
 
-		if (!port_ptr->notify) {
-			mutex_lock(&port_ptr->port_rx_q_lock);
-			wake_lock(&port_ptr->port_rx_wake_lock);
-			list_add_tail(&pkt->list, &port_ptr->port_rx_q);
-			wake_up(&port_ptr->port_rx_wait_q);
-			mutex_unlock(&port_ptr->port_rx_q_lock);
-			mutex_unlock(&local_ports_lock);
-		} else {
-			mutex_lock(&port_ptr->port_rx_q_lock);
-			src_addr = kmalloc(sizeof(struct msm_ipc_port_addr),
-					   GFP_KERNEL);
-			if (src_addr) {
-				src_addr->node_id = hdr->src_node_id;
-				src_addr->port_id = hdr->src_port_id;
-			}
-			skb_pull(head_skb, IPC_ROUTER_HDR_SIZE);
-			mutex_unlock(&local_ports_lock);
+		mutex_lock(&port_ptr->port_rx_q_lock);
+		wake_lock(&port_ptr->port_rx_wake_lock);
+		list_add_tail(&pkt->list, &port_ptr->port_rx_q);
+		wake_up(&port_ptr->port_rx_wait_q);
+		if (port_ptr->notify)
 			port_ptr->notify(MSM_IPC_ROUTER_READ_CB,
-				pkt->pkt_fragment_q, src_addr, port_ptr->priv);
-			mutex_unlock(&port_ptr->port_rx_q_lock);
-			pkt->pkt_fragment_q = NULL;
-			src_addr = NULL;
-			release_pkt(pkt);
-		}
+					 port_ptr->priv);
+		mutex_unlock(&port_ptr->port_rx_q_lock);
+		mutex_unlock(&local_ports_lock);
 
 process_done:
 		if (resume_tx) {
@@ -1507,11 +1780,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 +1797,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 +1807,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 +1838,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 +1854,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 +2056,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);
@@ -1805,6 +2086,15 @@
 		return -ENOMEM;
 	}
 
+	if (src->check_send_permissions) {
+		ret = src->check_send_permissions(rport_ptr->sec_rule);
+		if (ret <= 0) {
+			pr_err("%s: permission failure for %s\n",
+				__func__, current->comm);
+			return -EPERM;
+		}
+	}
+
 	pkt = create_pkt(data);
 	if (!pkt) {
 		pr_err("%s: Pkt creation failed\n", __func__);
@@ -1817,6 +2107,28 @@
 	return ret;
 }
 
+int msm_ipc_router_send_msg(struct msm_ipc_port *src,
+			    struct msm_ipc_addr *dest,
+			    void *data, unsigned int data_len)
+{
+	struct sk_buff_head *out_skb_head;
+	int ret;
+
+	out_skb_head = msm_ipc_router_buf_to_skb(data, data_len);
+	if (!out_skb_head) {
+		pr_err("%s: SKB conversion failed\n", __func__);
+		return -EFAULT;
+	}
+
+	ret = msm_ipc_router_send_to(src, out_skb_head, dest);
+	if (ret < 0) {
+		pr_err("%s: msm_ipc_router_send_to failed - ret: %d\n",
+			__func__, ret);
+		msm_ipc_router_free_skb(out_skb_head);
+	}
+	return 0;
+}
+
 int msm_ipc_router_read(struct msm_ipc_port *port_ptr,
 			struct sk_buff_head **data,
 			size_t buf_len)
@@ -1852,7 +2164,7 @@
 int msm_ipc_router_recv_from(struct msm_ipc_port *port_ptr,
 			     struct sk_buff_head **data,
 			     struct msm_ipc_addr *src,
-			     unsigned long timeout)
+			     long timeout)
 {
 	int ret, data_len, align_size;
 	struct sk_buff *temp_skb;
@@ -1909,11 +2221,42 @@
 	return data_len;
 }
 
+int msm_ipc_router_read_msg(struct msm_ipc_port *port_ptr,
+			    struct msm_ipc_addr *src,
+			    unsigned char **data,
+			    unsigned int *len)
+{
+	struct sk_buff_head *in_skb_head;
+	int ret;
+
+	ret = msm_ipc_router_recv_from(port_ptr, &in_skb_head, src, -1);
+	if (ret < 0) {
+		pr_err("%s: msm_ipc_router_recv_from failed - ret: %d\n",
+			__func__, ret);
+		return ret;
+	}
+
+	*data = msm_ipc_router_skb_to_buf(in_skb_head, ret);
+	if (!(*data))
+		pr_err("%s: Buf conversion failed\n", __func__);
+
+	*len = ret;
+	msm_ipc_router_free_skb(in_skb_head);
+	return 0;
+}
+
 struct msm_ipc_port *msm_ipc_router_create_port(
-	void (*notify)(unsigned event, void *data, void *addr, void *priv),
+	void (*notify)(unsigned event, void *priv),
 	void *priv)
 {
 	struct msm_ipc_port *port_ptr;
+	int ret;
+
+	ret = wait_for_completion_interruptible(&msm_ipc_local_router_up);
+	if (ret < 0) {
+		pr_err("%s: Error waiting for local router\n", __func__);
+		return NULL;
+	}
 
 	port_ptr = msm_ipc_router_create_raw_port(NULL, notify, priv);
 	if (!port_ptr)
@@ -1968,6 +2311,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 +2321,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 +2386,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);
@@ -2545,6 +2887,10 @@
 	if (ret < 0)
 		pr_err("%s: Init sockets failed\n", __func__);
 
+	ret = msm_ipc_router_security_init();
+	if (ret < 0)
+		pr_err("%s: Security Init failed\n", __func__);
+
 	complete_all(&msm_ipc_local_router_up);
 	return ret;
 }
diff --git a/arch/arm/mach-msm/ipc_router.h b/arch/arm/mach-msm/ipc_router.h
index 07bc5e0..39bde30 100644
--- a/arch/arm/mach-msm/ipc_router.h
+++ b/arch/arm/mach-msm/ipc_router.h
@@ -18,22 +18,18 @@
 #include <linux/errno.h>
 #include <linux/mm.h>
 #include <linux/list.h>
-#include <linux/cdev.h>
 #include <linux/platform_device.h>
-#include <linux/wakelock.h>
 #include <linux/msm_ipc.h>
 
 #include <net/sock.h>
 
 /* definitions for the R2R wire protcol */
 #define IPC_ROUTER_VERSION			1
-#define IPC_ROUTER_PROCESSORS_MAX		4
 
 #define IPC_ROUTER_CLIENT_BCAST_ID		0xffffffff
 #define IPC_ROUTER_ADDRESS			0xfffffffe
 
 #define IPC_ROUTER_NID_LOCAL			1
-#define IPC_ROUTER_NID_REMOTE			0
 
 #define IPC_ROUTER_CTRL_CMD_DATA		1
 #define IPC_ROUTER_CTRL_CMD_HELLO		2
@@ -51,17 +47,13 @@
 #define IPC_ROUTER_XPRT_EVENT_OPEN  2
 #define IPC_ROUTER_XPRT_EVENT_CLOSE 3
 
-#define NUM_NODES 2
-
 #define IPC_ROUTER_INFINITY -1
 #define DEFAULT_RCV_TIMEO IPC_ROUTER_INFINITY
 
 #define ALIGN_SIZE(x) ((4 - ((x) & 3)) & 3)
 
-enum {
-	MSM_IPC_ROUTER_READ_CB = 0,
-	MSM_IPC_ROUTER_WRITE_DONE,
-};
+#define ALL_SERVICE 0xFFFFFFFF
+#define ALL_INSTANCE 0xFFFFFFFF
 
 union rr_control_msg {
 	uint32_t cmd;
@@ -92,10 +84,6 @@
 
 #define IPC_ROUTER_HDR_SIZE sizeof(struct rr_header)
 #define MAX_IPC_PKT_SIZE 66000
-/* internals */
-
-#define IPC_ROUTER_MAX_REMOTE_SERVERS		100
-#define MAX_WAKELOCK_NAME_SZ 32
 
 struct rr_packet {
 	struct list_head list;
@@ -103,50 +91,12 @@
 	uint32_t length;
 };
 
-struct msm_ipc_port {
-	struct list_head list;
-
-	struct msm_ipc_port_addr this_port;
-	struct msm_ipc_port_name port_name;
-	uint32_t type;
-	unsigned flags;
-	spinlock_t port_lock;
-
-	struct list_head incomplete;
-	struct mutex incomplete_lock;
-
-	struct list_head port_rx_q;
-	struct mutex port_rx_q_lock;
-	char rx_wakelock_name[MAX_WAKELOCK_NAME_SZ];
-	struct wake_lock port_rx_wake_lock;
-	wait_queue_head_t port_rx_wait_q;
-
-	int restart_state;
-	spinlock_t restart_lock;
-	wait_queue_head_t restart_wait;
-
-	void *endpoint;
-	void (*notify)(unsigned event, void *data, void *addr, void *priv);
-
-	uint32_t num_tx;
-	uint32_t num_rx;
-	unsigned long num_tx_bytes;
-	unsigned long num_rx_bytes;
-	void *priv;
-};
-
 struct msm_ipc_sock {
 	struct sock sk;
 	struct msm_ipc_port *port;
 	void *default_pil;
 };
 
-enum write_data_type {
-	HEADER = 1,
-	PACKMARK,
-	PAYLOAD,
-};
-
 struct msm_ipc_router_xprt {
 	char *name;
 	uint32_t link_id;
@@ -161,8 +111,6 @@
 	int (*close)(struct msm_ipc_router_xprt *xprt);
 };
 
-extern struct completion msm_ipc_remote_router_up;
-
 void msm_ipc_router_xprt_notify(struct msm_ipc_router_xprt *xprt,
 				unsigned event,
 				void *data);
@@ -173,8 +121,7 @@
 
 
 struct msm_ipc_port *msm_ipc_router_create_raw_port(void *endpoint,
-		void (*notify)(unsigned event, void *data,
-			       void *addr, void *priv),
+		void (*notify)(unsigned event, void *priv),
 		void *priv);
 int msm_ipc_router_send_to(struct msm_ipc_port *src,
 			   struct sk_buff_head *data,
@@ -182,30 +129,23 @@
 int msm_ipc_router_read(struct msm_ipc_port *port_ptr,
 			struct sk_buff_head **data,
 			size_t buf_len);
-int msm_ipc_router_get_curr_pkt_size(struct msm_ipc_port *port_ptr);
 int msm_ipc_router_bind_control_port(struct msm_ipc_port *port_ptr);
-int msm_ipc_router_lookup_server_name(struct msm_ipc_port_name *srv_name,
-				      struct msm_ipc_server_info *srv_info,
-				      int num_entries_in_array,
-				      uint32_t lookup_mask);
-int msm_ipc_router_close_port(struct msm_ipc_port *port_ptr);
 
-struct msm_ipc_port *msm_ipc_router_create_port(
-	void (*notify)(unsigned event, void *data,
-		       void *addr, void *priv),
-	void *priv);
 int msm_ipc_router_recv_from(struct msm_ipc_port *port_ptr,
 		      struct sk_buff_head **data,
 		      struct msm_ipc_addr *src_addr,
-		      unsigned long timeout);
+		      long timeout);
 int msm_ipc_router_register_server(struct msm_ipc_port *server_port,
 			    struct msm_ipc_addr *name);
 int msm_ipc_router_unregister_server(struct msm_ipc_port *server_port);
 
-
 int msm_ipc_router_init_sockets(void);
 void msm_ipc_router_exit_sockets(void);
 
+void msm_ipc_sync_sec_rule(uint32_t service, uint32_t instance, void *rule);
+
+void msm_ipc_sync_default_sec_rule(void *rule);
+
 #if defined CONFIG_MSM_IPC_ROUTER_SMD_XPRT
 extern void *msm_ipc_load_default_node(void);
 
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/ipc_socket.c b/arch/arm/mach-msm/ipc_socket.c
index d3917f1..5d21fa5 100644
--- a/arch/arm/mach-msm/ipc_socket.c
+++ b/arch/arm/mach-msm/ipc_socket.c
@@ -21,16 +21,15 @@
 #include <linux/gfp.h>
 #include <linux/msm_ipc.h>
 
-#ifdef CONFIG_ANDROID_PARANOID_NETWORK
-#include <linux/android_aid.h>
-#endif
-
 #include <asm/string.h>
 #include <asm/atomic.h>
 
 #include <net/sock.h>
 
+#include <mach/msm_ipc_router.h>
+
 #include "ipc_router.h"
+#include "msm_ipc_router_security.h"
 
 #define msm_ipc_sk(sk) ((struct msm_ipc_sock *)(sk))
 #define msm_ipc_sk_port(sk) ((struct msm_ipc_port *)(msm_ipc_sk(sk)->port))
@@ -39,21 +38,6 @@
 static struct proto msm_ipc_proto;
 static const struct proto_ops msm_ipc_proto_ops;
 
-#ifdef CONFIG_ANDROID_PARANOID_NETWORK
-static inline int check_permissions(void)
-{
-	int rc = 0;
-	if (!current_euid() || in_egroup_p(AID_NET_RAW))
-		rc = 1;
-	return rc;
-}
-# else
-static inline int check_permissions(void)
-{
-	return 1;
-}
-#endif
-
 static struct sk_buff_head *msm_ipc_router_build_msg(unsigned int num_sect,
 					  struct iovec const *msg_sect,
 					  size_t total_len)
@@ -191,7 +175,8 @@
 	void *pil;
 
 	if (!check_permissions()) {
-		pr_err("%s: Do not have permissions\n", __func__);
+		pr_err("%s: %s Do not have permissions\n",
+			__func__, current->comm);
 		return -EPERM;
 	}
 
@@ -221,6 +206,7 @@
 		return -ENOMEM;
 	}
 
+	port_ptr->check_send_permissions = msm_ipc_check_send_permissions;
 	sock->ops = &msm_ipc_proto_ops;
 	sock_init_data(sock, sk);
 	sk->sk_rcvtimeo = DEFAULT_RCV_TIMEO;
@@ -443,6 +429,10 @@
 		ret = msm_ipc_router_bind_control_port(port_ptr);
 		break;
 
+	case IPC_ROUTER_IOCTL_CONFIG_SEC_RULES:
+		ret = msm_ipc_config_sec_rules((void *)arg);
+		break;
+
 	default:
 		ret = -EINVAL;
 	}
diff --git a/arch/arm/mach-msm/krait-regulator.c b/arch/arm/mach-msm/krait-regulator.c
index 96c4809..f7b2b1e 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"
 
@@ -55,16 +56,10 @@
  *           |_________________|
  */
 
-#define V_RETENTION			600000
-#define V_LDO_HEADROOM			150000
-
 #define PMIC_VOLTAGE_MIN		350000
 #define PMIC_VOLTAGE_MAX		1355000
 #define LV_RANGE_STEP			5000
 
-/* use LDO for core voltage below LDO_THRESH */
-#define CORE_VOLTAGE_LDO_THRESH		750000
-
 #define LOAD_PER_PHASE			3200000
 
 #define CORE_VOLTAGE_MIN		900000
@@ -87,6 +82,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 +135,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;
@@ -154,8 +155,15 @@
 	int				load_uA;
 	enum krait_supply_mode		mode;
 	void __iomem			*reg_base;
+	int				ldo_default_uV;
+	int				retention_uV;
+	int				headroom_uV;
+	int				ldo_threshold_uV;
+	bool				online;
 };
 
+static u32 version;
+
 static void krait_masked_write(struct krait_power_vreg *kvreg,
 					int reg, uint32_t mask, uint32_t val)
 {
@@ -191,11 +199,22 @@
 	return uV;
 }
 
-static int set_krait_ldo_uv(struct krait_power_vreg *kvreg)
+static int set_krait_retention_uv(struct krait_power_vreg *kvreg, int uV)
 {
 	uint32_t reg_val;
 
-	reg_val = kvreg->uV - KRAIT_LDO_VOLTAGE_OFFSET / KRAIT_LDO_STEP;
+	reg_val = DIV_ROUND_UP(uV - KRAIT_LDO_VOLTAGE_OFFSET, KRAIT_LDO_STEP);
+	krait_masked_write(kvreg, APC_LDO_VREF_SET, VREF_RET_MASK,
+						reg_val << VREF_RET_POS);
+
+	return 0;
+}
+
+static int set_krait_ldo_uv(struct krait_power_vreg *kvreg, int uV)
+{
+	uint32_t reg_val;
+
+	reg_val = DIV_ROUND_UP(uV - KRAIT_LDO_VOLTAGE_OFFSET, KRAIT_LDO_STEP);
 	krait_masked_write(kvreg, APC_LDO_VREF_SET, VREF_LDO_MASK,
 						reg_val << VREF_LDO_BIT_POS);
 
@@ -243,7 +262,7 @@
 	if (kvreg->mode == LDO_MODE)
 		switch_to_using_hs(kvreg);
 
-	set_krait_ldo_uv(kvreg);
+	set_krait_ldo_uv(kvreg, kvreg->uV);
 
 	/*
 	 * enable ldo - note that both LDO and BHS are are supplying voltage to
@@ -291,6 +310,7 @@
 	}
 
 	setpoint = DIV_ROUND_UP(uV, LV_RANGE_STEP);
+
 	return msm_spm_apcs_set_vdd(setpoint);
 }
 
@@ -301,8 +321,10 @@
 	int rc = 0;
 
 	list_for_each_entry(kvreg, &pvreg->krait_power_vregs, link) {
-		if (kvreg->uV > CORE_VOLTAGE_LDO_THRESH
-			 || kvreg->uV > vmax - V_LDO_HEADROOM) {
+		if (!kvreg->online)
+			continue;
+		if (kvreg->uV > kvreg->ldo_threshold_uV
+			 || kvreg->uV > vmax - kvreg->headroom_uV) {
 			rc = switch_to_using_hs(kvreg);
 			if (rc < 0) {
 				pr_err("could not switch %s to hs rc = %d\n",
@@ -425,7 +447,7 @@
 	return rc;
 }
 
-static int __init pvreg_init(struct platform_device *pdev)
+static int __devinit pvreg_init(struct platform_device *pdev)
 {
 	struct pmic_gang_vreg *pvreg;
 
@@ -464,6 +486,9 @@
 	struct pmic_gang_vreg *pvreg = from->pvreg;
 
 	list_for_each_entry(kvreg, &pvreg->krait_power_vregs, link) {
+		if (!kvreg->online)
+			continue;
+
 		v = kvreg->uV;
 
 		if (kvreg == from)
@@ -472,6 +497,7 @@
 		if (vmax < v)
 			vmax = v;
 	}
+
 	return vmax;
 }
 
@@ -481,14 +507,17 @@
 	struct krait_power_vreg *kvreg;
 	struct pmic_gang_vreg *pvreg = from->pvreg;
 
-	list_for_each_entry(kvreg, &pvreg->krait_power_vregs, link)
+	list_for_each_entry(kvreg, &pvreg->krait_power_vregs, link) {
+		if (!kvreg->online)
+			continue;
 		load_total += kvreg->load_uA;
+	}
 
 	return load_total;
 }
 
 #define ROUND_UP_VOLTAGE(v, res) (DIV_ROUND_UP(v, res) * res)
-static int krait_power_set_voltage(struct regulator_dev *rdev,
+static int _set_voltage(struct regulator_dev *rdev,
 			int min_uV, int max_uV, unsigned *selector)
 {
 	struct krait_power_vreg *kvreg = rdev_get_drvdata(rdev);
@@ -496,30 +525,11 @@
 	int rc;
 	int vmax;
 
-	/*
-	 * if the voltage requested is below LDO_THRESHOLD this cpu could
-	 * switch to LDO mode. Hence round the voltage as per the LDO
-	 * resolution
-	 */
-	if (min_uV < CORE_VOLTAGE_LDO_THRESH) {
-		if (min_uV < KRAIT_LDO_VOLTAGE_MIN)
-			min_uV = KRAIT_LDO_VOLTAGE_MIN;
-		min_uV = ROUND_UP_VOLTAGE(min_uV, KRAIT_LDO_STEP);
-	}
-
-	mutex_lock(&pvreg->krait_power_vregs_lock);
-
 	vmax = get_vmax(kvreg, min_uV);
 
 	/* round up the pmic voltage as per its resolution */
 	vmax = ROUND_UP_VOLTAGE(vmax, LV_RANGE_STEP);
 
-	/*
-	 * Assign the voltage before updating the gang voltage as we iterate
-	 * over all the core voltages and choose HS or LDO for each of them
-	 */
-	kvreg->uV = min_uV;
-
 	rc = pmic_gang_set_voltage(kvreg, vmax);
 	if (rc < 0) {
 		dev_err(&rdev->dev, "%s failed set voltage (%d, %d) rc = %d\n",
@@ -530,25 +540,81 @@
 	pvreg->pmic_vmax_uV = vmax;
 
 out:
-	mutex_unlock(&pvreg->krait_power_vregs_lock);
 	return rc;
 }
 
-static unsigned int krait_power_get_optimum_mode(struct regulator_dev *rdev,
+static int krait_power_set_voltage(struct regulator_dev *rdev,
+			int min_uV, int max_uV, unsigned *selector)
+{
+	struct krait_power_vreg *kvreg = rdev_get_drvdata(rdev);
+	struct pmic_gang_vreg *pvreg = kvreg->pvreg;
+	int rc;
+
+	/*
+	 * if the voltage requested is below LDO_THRESHOLD this cpu could
+	 * switch to LDO mode. Hence round the voltage as per the LDO
+	 * resolution
+	 */
+	if (min_uV < kvreg->ldo_threshold_uV) {
+		if (min_uV < KRAIT_LDO_VOLTAGE_MIN)
+			min_uV = KRAIT_LDO_VOLTAGE_MIN;
+		min_uV = ROUND_UP_VOLTAGE(min_uV, KRAIT_LDO_STEP);
+	}
+
+	mutex_lock(&pvreg->krait_power_vregs_lock);
+	kvreg->uV = min_uV;
+
+	if (!kvreg->online) {
+		mutex_unlock(&pvreg->krait_power_vregs_lock);
+		return 0;
+	}
+
+	rc = _set_voltage(rdev, min_uV, max_uV, selector);
+	mutex_unlock(&pvreg->krait_power_vregs_lock);
+
+	return rc;
+}
+
+#define PMIC_FTS_MODE_PFM	0x00
+#define PMIC_FTS_MODE_PWM	0x80
+#define PFM_LOAD_UA		500000
+static unsigned int _get_optimum_mode(struct regulator_dev *rdev,
 			int input_uV, int output_uV, int load_uA)
 {
 	struct krait_power_vreg *kvreg = rdev_get_drvdata(rdev);
 	struct pmic_gang_vreg *pvreg = kvreg->pvreg;
 	int rc;
 	int load_total_uA;
-	int reg_mode = -EINVAL;
-
-	mutex_lock(&pvreg->krait_power_vregs_lock);
-
-	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;
+			}
+		}
+		return kvreg->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,10 +622,28 @@
 		goto out;
 	}
 
-	reg_mode = kvreg->mode;
 out:
+	return kvreg->mode;
+}
+
+static unsigned int krait_power_get_optimum_mode(struct regulator_dev *rdev,
+			int input_uV, int output_uV, int load_uA)
+{
+	struct krait_power_vreg *kvreg = rdev_get_drvdata(rdev);
+	struct pmic_gang_vreg *pvreg = kvreg->pvreg;
+	int rc;
+
+	mutex_lock(&pvreg->krait_power_vregs_lock);
+	kvreg->load_uA = load_uA;
+	if (!kvreg->online) {
+		mutex_unlock(&pvreg->krait_power_vregs_lock);
+		return kvreg->mode;
+	}
+
+	rc = _get_optimum_mode(rdev, input_uV, output_uV, load_uA);
 	mutex_unlock(&pvreg->krait_power_vregs_lock);
-	return reg_mode;
+
+	return rc;
 }
 
 static int krait_power_set_mode(struct regulator_dev *rdev, unsigned int mode)
@@ -574,12 +658,62 @@
 	return kvreg->mode;
 }
 
+static int krait_power_is_enabled(struct regulator_dev *rdev)
+{
+	struct krait_power_vreg *kvreg = rdev_get_drvdata(rdev);
+
+	return kvreg->online;
+}
+
+static int krait_power_enable(struct regulator_dev *rdev)
+{
+	struct krait_power_vreg *kvreg = rdev_get_drvdata(rdev);
+	struct pmic_gang_vreg *pvreg = kvreg->pvreg;
+	int rc;
+
+	mutex_lock(&pvreg->krait_power_vregs_lock);
+	kvreg->online = true;
+	rc = _get_optimum_mode(rdev, kvreg->uV, kvreg->uV,
+							kvreg->load_uA);
+	if (rc < 0)
+		goto en_err;
+	rc = _set_voltage(rdev, kvreg->uV,
+					rdev->constraints->max_uV, NULL);
+en_err:
+	mutex_unlock(&pvreg->krait_power_vregs_lock);
+	return rc;
+}
+
+static int krait_power_disable(struct regulator_dev *rdev)
+{
+	struct krait_power_vreg *kvreg = rdev_get_drvdata(rdev);
+	struct pmic_gang_vreg *pvreg = kvreg->pvreg;
+	int rc;
+
+	mutex_lock(&pvreg->krait_power_vregs_lock);
+	kvreg->online = false;
+
+	rc = _get_optimum_mode(rdev, kvreg->uV, kvreg->uV,
+							kvreg->load_uA);
+	if (rc < 0)
+		goto dis_err;
+
+	rc = _set_voltage(rdev, kvreg->uV,
+					rdev->constraints->max_uV, NULL);
+dis_err:
+	mutex_unlock(&pvreg->krait_power_vregs_lock);
+	return rc;
+}
+
 static struct regulator_ops krait_power_ops = {
 	.get_voltage		= krait_power_get_voltage,
 	.set_voltage		= krait_power_set_voltage,
 	.get_optimum_mode	= krait_power_get_optimum_mode,
 	.set_mode		= krait_power_set_mode,
 	.get_mode		= krait_power_get_mode,
+	.enable			= krait_power_enable,
+	.disable		= krait_power_disable,
+	.is_enabled		= krait_power_is_enabled,
 };
 
 static void kvreg_hw_init(struct krait_power_vreg *kvreg)
@@ -597,14 +731,45 @@
 	/* BHS has six different segments, turn them all on */
 	krait_masked_write(kvreg, APC_PWR_GATE_CTL,
 		BHS_SEG_EN_MASK, BHS_SEG_EN_DEFAULT << BHS_SEG_EN_BIT_POS);
+
+	set_krait_retention_uv(kvreg, kvreg->retention_uV);
+	set_krait_ldo_uv(kvreg, kvreg->ldo_default_uV);
 }
 
+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 is_between(int left, int right, int value)
+{
+	if (left >= right && left >= value && value >= right)
+		return 1;
+	if (left <= right && left <= value && value <= right)
+		return 1;
+	return 0;
+}
+
+#define LDO_HDROOM_MIN		50000
+#define LDO_HDROOM_MAX		250000
+
+#define LDO_UV_MIN		465000
+#define LDO_UV_MAX		750000
+
+#define LDO_TH_MIN		600000
+#define LDO_TH_MAX		800000
+
 static int __devinit krait_power_probe(struct platform_device *pdev)
 {
 	struct krait_power_vreg *kvreg;
 	struct resource *res;
 	struct regulator_init_data *init_data = pdev->dev.platform_data;
 	int rc = 0;
+	int headroom_uV, retention_uV, ldo_default_uV, ldo_threshold_uV;
 
 	/* Initialize the pmic gang if it hasn't been initialized already */
 	if (the_gang == NULL) {
@@ -614,6 +779,8 @@
 				"failed to init pmic gang rc = %d\n", rc);
 			return rc;
 		}
+		/* global initializtion */
+		glb_init(pdev);
 	}
 
 	if (pdev->dev.of_node) {
@@ -627,6 +794,57 @@
 			|= REGULATOR_MODE_NORMAL | REGULATOR_MODE_IDLE
 			| REGULATOR_MODE_FAST;
 		init_data->constraints.input_uV = init_data->constraints.max_uV;
+		rc = of_property_read_u32(pdev->dev.of_node,
+					"qcom,headroom-voltage",
+					&headroom_uV);
+		if (rc < 0) {
+			pr_err("headroom-voltage missing rc=%d\n", rc);
+			return rc;
+		}
+		if (!is_between(LDO_HDROOM_MIN, LDO_HDROOM_MAX, headroom_uV)) {
+			pr_err("bad headroom-voltage = %d specified\n",
+					headroom_uV);
+			return -EINVAL;
+		}
+
+		rc = of_property_read_u32(pdev->dev.of_node,
+					"qcom,retention-voltage",
+					&retention_uV);
+		if (rc < 0) {
+			pr_err("retention-voltage missing rc=%d\n", rc);
+			return rc;
+		}
+		if (!is_between(LDO_UV_MIN, LDO_UV_MAX, retention_uV)) {
+			pr_err("bad retention-voltage = %d specified\n",
+					retention_uV);
+			return -EINVAL;
+		}
+
+		rc = of_property_read_u32(pdev->dev.of_node,
+					"qcom,ldo-default-voltage",
+					&ldo_default_uV);
+		if (rc < 0) {
+			pr_err("ldo-default-voltage missing rc=%d\n", rc);
+			return rc;
+		}
+		if (!is_between(LDO_UV_MIN, LDO_UV_MAX, ldo_default_uV)) {
+			pr_err("bad ldo-default-voltage = %d specified\n",
+					ldo_default_uV);
+			return -EINVAL;
+		}
+
+		rc = of_property_read_u32(pdev->dev.of_node,
+					"qcom,ldo-threshold-voltage",
+					&ldo_threshold_uV);
+		if (rc < 0) {
+			pr_err("ldo-threshold-voltage missing rc=%d\n", rc);
+			return rc;
+		}
+		if (!is_between(LDO_TH_MIN, LDO_TH_MAX, ldo_threshold_uV)) {
+			pr_err("bad ldo-threshold-voltage = %d specified\n",
+					ldo_threshold_uV);
+			return -EINVAL;
+		}
 	}
 
 	if (!init_data) {
@@ -656,15 +874,19 @@
 	kvreg->reg_base = devm_ioremap(&pdev->dev,
 				res->start, resource_size(res));
 
-	kvreg->pvreg	  = the_gang;
-	kvreg->name	  = init_data->constraints.name;
-	kvreg->desc.name  = kvreg->name;
-	kvreg->desc.ops   = &krait_power_ops;
-	kvreg->desc.type  = REGULATOR_VOLTAGE;
-	kvreg->desc.owner = THIS_MODULE;
-	kvreg->uV	  = CORE_VOLTAGE_MIN;
-	kvreg->mode	  = HS_MODE;
-	kvreg->desc.ops   = &krait_power_ops;
+	kvreg->pvreg		= the_gang;
+	kvreg->name		= init_data->constraints.name;
+	kvreg->desc.name	= kvreg->name;
+	kvreg->desc.ops		= &krait_power_ops;
+	kvreg->desc.type	= REGULATOR_VOLTAGE;
+	kvreg->desc.owner	= THIS_MODULE;
+	kvreg->uV		= CORE_VOLTAGE_MIN;
+	kvreg->mode		= HS_MODE;
+	kvreg->desc.ops		= &krait_power_ops;
+	kvreg->headroom_uV	= headroom_uV;
+	kvreg->retention_uV	= retention_uV;
+	kvreg->ldo_default_uV	= ldo_default_uV;
+	kvreg->ldo_threshold_uV = ldo_threshold_uV;
 
 	platform_set_drvdata(pdev, kvreg);
 
@@ -738,8 +960,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/lpm_levels.c b/arch/arm/mach-msm/lpm_levels.c
index 4854fc48..61c2aa8 100644
--- a/arch/arm/mach-msm/lpm_levels.c
+++ b/arch/arm/mach-msm/lpm_levels.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
@@ -145,6 +145,11 @@
 		if (time_param->latency_us < level->latency_us)
 			continue;
 
+		if ((MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE == sleep_mode)
+			|| (MSM_PM_SLEEP_MODE_POWER_COLLAPSE == sleep_mode))
+			if (!cpu && msm_rpm_waiting_for_ack())
+					break;
+
 		if (time_param->sleep_us <= 1) {
 			pwr = level->energy_overhead;
 		} else if (time_param->sleep_us <= level->time_overhead_us) {
diff --git a/arch/arm/mach-msm/lpm_resources.c b/arch/arm/mach-msm/lpm_resources.c
index 255cd46..c21ea33 100644
--- a/arch/arm/mach-msm/lpm_resources.c
+++ b/arch/arm/mach-msm/lpm_resources.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
@@ -27,6 +27,7 @@
 #include "lpm_resources.h"
 #include "rpm-notifier.h"
 #include "idle.h"
+#include "trace_msm_low_power.h"
 
 /*Debug Definitions*/
 enum {
@@ -417,6 +418,7 @@
 
 	if (rs->valid)
 		rs->sleep_value = limits->l2_cache;
+	trace_lpm_resources(rs->sleep_value, rs->name);
 }
 
 static void msm_lpm_flush_l2(int notify_rpm)
@@ -497,6 +499,7 @@
 		else
 			rs->sleep_value = vdd_buf;
 	}
+	trace_lpm_resources(rs->sleep_value, rs->name);
 }
 
 static void msm_lpm_flush_vdd_dig(int notify_rpm)
@@ -551,6 +554,7 @@
 		else
 			rs->sleep_value = vdd_buf;
 	}
+	trace_lpm_resources(rs->sleep_value, rs->name);
 }
 
 static void msm_lpm_flush_vdd_mem(int notify_rpm)
@@ -608,6 +612,7 @@
 			pr_info("%s: pxo buf %d sleep value %d\n",
 					__func__, pxo_buf, rs->sleep_value);
 	}
+	trace_lpm_resources(rs->sleep_value, rs->name);
 }
 
 static void msm_lpm_flush_pxo(int notify_rpm)
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..aef4ac9 100644
--- a/arch/arm/mach-msm/mdm_common.c
+++ b/arch/arm/mach-msm/mdm_common.c
@@ -300,6 +300,7 @@
 		if (ret)
 			pr_err("%s: Graceful shutdown of the external modem failed, ret = %d\n",
 				   __func__, ret);
+		put_user(ret, (unsigned long __user *) arg);
 		break;
 	default:
 		pr_err("%s: invalid ioctl cmd = %d\n", __func__, _IOC_NR(cmd));
@@ -681,6 +682,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 7d7380b..9cc2a9d 100644
--- a/arch/arm/mach-msm/memory.c
+++ b/arch/arm/mach-msm/memory.c
@@ -27,6 +27,7 @@
 #include <asm/setup.h>
 #include <asm/mach-types.h>
 #include <mach/msm_memtypes.h>
+#include <mach/memory.h>
 #include <linux/hardirq.h>
 #if defined(CONFIG_MSM_NPA_REMOTE)
 #include "npa_remote.h"
@@ -365,7 +366,7 @@
 	return ret;
 }
 
-static int check_for_compat(unsigned long node)
+static int __init check_for_compat(unsigned long node)
 {
 	char **start = __compat_exports_start;
 
@@ -454,6 +455,79 @@
 	return 0;
 }
 
+/* This function scans the device tree to populate the memory hole table */
+int __init dt_scan_for_memory_hole(unsigned long node, const char *uname,
+		int depth, void *data)
+{
+	unsigned int *memory_remove_prop;
+	unsigned long memory_remove_prop_length;
+	unsigned long hole_start;
+	unsigned long hole_size;
+
+	memory_remove_prop = of_get_flat_dt_prop(node,
+						"qcom,memblock-remove",
+						&memory_remove_prop_length);
+
+	if (memory_remove_prop) {
+		if (!check_for_compat(node))
+			goto out;
+	} else {
+		goto out;
+	}
+
+	if (memory_remove_prop) {
+		if (memory_remove_prop_length != (2*sizeof(unsigned int))) {
+			WARN(1, "Memory remove malformed\n");
+			goto out;
+		}
+
+		hole_start = be32_to_cpu(memory_remove_prop[0]);
+		hole_size = be32_to_cpu(memory_remove_prop[1]);
+
+		if (hole_start + hole_size <= MAX_HOLE_ADDRESS) {
+			if (memory_hole_start == 0 && memory_hole_end == 0) {
+				memory_hole_start = hole_start;
+				memory_hole_end = hole_start + hole_size;
+			} else if ((memory_hole_end - memory_hole_start)
+							<= hole_size) {
+				memory_hole_start = hole_start;
+				memory_hole_end = hole_start + hole_size;
+			}
+		}
+		adjust_meminfo(hole_start, hole_size);
+	}
+
+out:
+	return 0;
+}
+
+/*
+ * Split the memory bank to reflect the hole, if present,
+ * using the start and end of the memory hole.
+ */
+void adjust_meminfo(unsigned long start, unsigned long size)
+{
+	int i, j;
+
+	for (i = 0, j = 0; i < meminfo.nr_banks; i++) {
+		struct membank *bank = &meminfo.bank[j];
+		*bank = meminfo.bank[i];
+
+		if (((start + size) <= (bank->start + bank->size)) &&
+			(start >= bank->start)) {
+			memmove(bank + 1, bank,
+				(meminfo.nr_banks - i) * sizeof(*bank));
+			meminfo.nr_banks++;
+			i++;
+			bank[1].size -= (start + size);
+			bank[1].start = (start + size);
+			bank[1].highmem = 0;
+			j++;
+			bank->size = start - bank->start;
+		}
+		j++;
+	}
+}
 unsigned long get_ddr_size(void)
 {
 	unsigned int i;
@@ -464,3 +538,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/mpm-8625.c b/arch/arm/mach-msm/mpm-8625.c
index c70ff5c..aaac476 100644
--- a/arch/arm/mach-msm/mpm-8625.c
+++ b/arch/arm/mach-msm/mpm-8625.c
@@ -101,6 +101,7 @@
 
 static uint16_t msm_bypassed_apps_irqs[] = {
 	MSM8625_INT_CPR_IRQ0,
+	MSM8625_INT_L2CC_INTR,
 };
 
 /* Check IRQ falls into bypassed list are not */
diff --git a/arch/arm/mach-msm/msm_bus/Makefile b/arch/arm/mach-msm/msm_bus/Makefile
index bdc6fac..dde25ab 100644
--- a/arch/arm/mach-msm/msm_bus/Makefile
+++ b/arch/arm/mach-msm/msm_bus/Makefile
@@ -11,4 +11,5 @@
 obj-$(CONFIG_ARCH_APQ8064) += msm_bus_board_8064.o
 obj-$(CONFIG_ARCH_MSM8930) += msm_bus_board_8930.o
 obj-$(CONFIG_ARCH_MSM8974) += msm_bus_board_8974.o
+obj-$(CONFIG_ARCH_MSM9625) += msm_bus_board_9625.o
 obj-$(CONFIG_DEBUG_FS) += msm_bus_dbg.o
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 97299a0..ea17efe 100644
--- a/arch/arm/mach-msm/msm_bus/msm_bus_bimc.c
+++ b/arch/arm/mach-msm/msm_bus/msm_bus_bimc.c
@@ -1807,7 +1807,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;
@@ -1817,7 +1817,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 cfd84eb..dbfa5ec 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
@@ -642,6 +642,8 @@
 		.qport = qports_crypto_c0,
 		.mas_hw_id = MAS_CRYPTO_CORE0,
 		.hw_sel = MSM_BUS_NOC,
+		.prio_rd = 1,
+		.prio_wr = 1,
 	},
 	{
 		.id = MSM_BUS_MASTER_CRYPTO_CORE1,
@@ -653,6 +655,8 @@
 		.qport = qports_crypto_c1,
 		.mas_hw_id = MAS_CRYPTO_CORE1,
 		.hw_sel = MSM_BUS_NOC,
+		.prio_rd = 1,
+		.prio_wr = 1,
 	},
 	{
 		.id = MSM_BUS_MASTER_LPASS_PROC,
@@ -722,6 +726,7 @@
 		.prio_rd = 2,
 		.prio_wr = 2,
 		.hw_sel = MSM_BUS_NOC,
+		.iface_clk_node = "msm_usb3",
 	},
 	{
 		.id = MSM_BUS_SLAVE_AMPSS,
@@ -806,7 +811,7 @@
 		.tier = tier2,
 		.num_tiers = ARRAY_SIZE(tier2),
 		.hw_sel = MSM_BUS_NOC,
-		.perm_mode = NOC_QOS_MODES_ALL_PERM,
+		.perm_mode = NOC_QOS_PERM_MODE_BYPASS,
 		.mode = NOC_QOS_MODE_BYPASS,
 		.ws = 10000,
 		.qport = qports_oxili,
@@ -819,7 +824,7 @@
 		.tier = tier2,
 		.num_tiers = ARRAY_SIZE(tier2),
 		.hw_sel = MSM_BUS_NOC,
-		.perm_mode = NOC_QOS_MODES_ALL_PERM,
+		.perm_mode = NOC_QOS_PERM_MODE_BYPASS,
 		.mode = NOC_QOS_MODE_BYPASS,
 		.qport = qports_gemini,
 		.ws = 10000,
@@ -832,7 +837,7 @@
 		.tier = tier2,
 		.num_tiers = ARRAY_SIZE(tier2),
 		.hw_sel = MSM_BUS_NOC,
-		.perm_mode = NOC_QOS_MODES_ALL_PERM,
+		.perm_mode = NOC_QOS_PERM_MODE_BYPASS,
 		.mode = NOC_QOS_MODE_BYPASS,
 		.qport = qports_mdp,
 		.ws = 10000,
@@ -845,7 +850,7 @@
 		.tier = tier2,
 		.num_tiers = ARRAY_SIZE(tier2),
 		.hw_sel = MSM_BUS_NOC,
-		.perm_mode = NOC_QOS_MODES_ALL_PERM,
+		.perm_mode = NOC_QOS_PERM_MODE_BYPASS,
 		.mode = NOC_QOS_MODE_BYPASS,
 		.ws = 10000,
 		.qport = qports_venus_p0,
@@ -858,7 +863,7 @@
 		.tier = tier2,
 		.num_tiers = ARRAY_SIZE(tier2),
 		.hw_sel = MSM_BUS_NOC,
-		.perm_mode = NOC_QOS_MODES_ALL_PERM,
+		.perm_mode = NOC_QOS_PERM_MODE_BYPASS,
 		.mode = NOC_QOS_MODE_BYPASS,
 		.ws = 10000,
 		.qport = qports_venus_p1,
@@ -871,7 +876,7 @@
 		.tier = tier2,
 		.num_tiers = ARRAY_SIZE(tier2),
 		.hw_sel = MSM_BUS_NOC,
-		.perm_mode = NOC_QOS_MODES_ALL_PERM,
+		.perm_mode = NOC_QOS_PERM_MODE_BYPASS,
 		.mode = NOC_QOS_MODE_BYPASS,
 		.ws = 10000,
 		.qport = qports_vfe,
diff --git a/arch/arm/mach-msm/msm_bus/msm_bus_board_9625.c b/arch/arm/mach-msm/msm_bus/msm_bus_board_9625.c
new file mode 100644
index 0000000..92cd255
--- /dev/null
+++ b/arch/arm/mach-msm/msm_bus/msm_bus_board_9625.c
@@ -0,0 +1,1303 @@
+/* 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/device.h>
+#include <linux/module.h>
+#include <mach/msm_bus.h>
+#include <mach/msm_bus_board.h>
+#include <mach/board.h>
+#include <mach/rpm.h>
+#include "msm_bus_core.h"
+#include "msm_bus_noc.h"
+#include "msm_bus_bimc.h"
+
+#define NMASTERS 120
+#define NSLAVES 150
+#define NFAB_9625 4
+
+enum msm_bus_9625_master_ports_type {
+	/* System NOC Masters */
+	MASTER_PORT_LPASS_AHB = 0,
+	MASTER_PORT_QDSS_BAM,
+	MASTER_PORT_SNOC_CFG,
+	MASTER_PORT_GW_BIMC_SNOC,
+	MASTER_PORT_GW_CNOC_SNOC,
+	MASTER_PORT_CRYPTO_CORE0,
+	MASTER_PORT_LPASS_PROC,
+	MASTER_PORT_MSS,
+	MASTER_PORT_MSS_NAV,
+	MASTER_PORT_IPA,
+	MASTER_PORT_GW_PNOC_SNOC,
+	MASTER_PORT_QDSS_ETR,
+
+	/* BIMC Masters */
+	MASTER_PORT_KMPSS_M0 = 0,
+	MASTER_PORT_MSS_PROC,
+	MASTER_PORT_GW_SNOC_BIMC_0,
+
+	/* Peripheral NOC Masters */
+	MASTER_PORT_QPIC = 0,
+	MASTER_PORT_SDCC_1,
+	MASTER_PORT_SDCC_3,
+	MASTER_PORT_SDCC_2,
+	MASTER_PORT_SDCC_4,
+	MASTER_PORT_TSIF,
+	MASTER_PORT_BAM_DMA,
+	MASTER_PORT_BLSP_2,
+	MASTER_PORT_USB_HSIC,
+	MASTER_PORT_BLSP_1,
+	MASTER_PORT_USB_HS1,
+	MASTER_PORT_USB_HS2,
+	MASTER_PORT_PNOC_CFG,
+	MASTER_PORT_GW_SNOC_PNOC,
+
+	/* Config NOC Masters */
+	MASTER_PORT_RPM_INST = 0,
+	MASTER_PORT_RPM_DATA,
+	MASTER_PORT_RPM_SYS,
+	MASTER_PORT_DEHR,
+	MASTER_PORT_QDSS_DAP,
+	MASTER_PORT_SPDM,
+	MASTER_PORT_TIC,
+	MASTER_PORT_GW_SNOC_CNOC,
+};
+
+enum msm_bus_9625_slave_ports_type {
+	/* System NOC Slaves */
+	SLAVE_PORT_KMPSS = 1,
+	SLAVE_PORT_LPASS,
+	SLAVE_PORT_GW_SNOC_BIMC_P0,
+	SLAVE_PORT_GW_SNOC_CNOC,
+	SLAVE_PORT_OCIMEM,
+	SLAVE_PORT_GW_SNOC_PNOC,
+	SLAVE_PORT_SERVICE_SNOC,
+	SLAVE_PORT_QDSS_STM,
+
+	/* BIMC Slaves */
+	SLAVE_PORT_EBI1_CH0 = 0,
+	SLAVE_PORT_GW_BIMC_SNOC,
+
+	/*Peripheral NOC Slaves */
+	SLAVE_PORT_QPIC = 0,
+	SLAVE_PORT_SDCC_1,
+	SLAVE_PORT_SDCC_3,
+	SLAVE_PORT_SDCC_2,
+	SLAVE_PORT_SDCC_4,
+	SLAVE_PORT_TSIF,
+	SLAVE_PORT_BAM_DMA,
+	SLAVE_PORT_BLSP_2,
+	SLAVE_PORT_USB_HSIC,
+	SLAVE_PORT_BLSP_1,
+	SLAVE_PORT_USB_HS1,
+	SLAVE_PORT_USB_HS2,
+	SLAVE_PORT_PDM,
+	SLAVE_PORT_PERIPH_APU_CFG,
+	SLAVE_PORT_PNOC_MPU_CFG,
+	SLAVE_PORT_PRNG,
+	SLAVE_PORT_GW_PNOC_SNOC,
+	SLAVE_PORT_SERVICE_PNOC,
+
+	/* Config NOC slaves */
+	SLAVE_PORT_CLK_CTL = 0,
+	SLAVE_PORT_CNOC_MSS,
+	SLAVE_PORT_SECURITY,
+	SLAVE_PORT_TCSR,
+	SLAVE_PORT_TLMM,
+	SLAVE_PORT_CRYPTO_0_CFG,
+	SLAVE_PORT_IMEM_CFG,
+	SLAVE_PORT_IPS_CFG,
+	SLAVE_PORT_MESSAGE_RAM,
+	SLAVE_PORT_BIMC_CFG,
+	SLAVE_PORT_BOOT_ROM,
+	SLAVE_PORT_PMIC_ARB,
+	SLAVE_PORT_SPDM_WRAPPER,
+	SLAVE_PORT_DEHR_CFG,
+	SLAVE_PORT_MPM,
+	SLAVE_PORT_QDSS_CFG,
+	SLAVE_PORT_RBCPR_CFG,
+	SLAVE_PORT_RBCPR_QDSS_APU_CFG,
+	SLAVE_PORT_SNOC_MPU_CFG,
+	SLAVE_PORT_PNOC_CFG,
+	SLAVE_PORT_SNOC_CFG,
+	SLAVE_PORT_PHY_APU_CFG,
+	SLAVE_PORT_EBI1_PHY_CFG,
+	SLAVE_PORT_RPM,
+	SLAVE_PORT_GW_CNOC_SNOC,
+	SLAVE_PORT_SERVICE_CNOC,
+};
+
+/* Hardware IDs for RPM */
+enum msm_bus_9625_mas_hw_id {
+	MAS_APPSS_PROC = 0,
+	MAS_AMSS_PROC,
+	MAS_MNOC_BIMC,
+	MAS_SNOC_BIMC,
+	MAS_CNOC_MNOC_MMSS_CFG,
+	MAS_CNOC_MNOC_CFG,
+	MAS_GFX3D,
+	MAS_JPEG,
+	MAS_MDP,
+	MAS_VIDEO_P0,
+	MAS_VIDEO_P1,
+	MAS_VFE,
+	MAS_CNOC_ONOC_CFG,
+	MAS_JPEG_OCMEM,
+	MAS_MDP_OCMEM,
+	MAS_VIDEO_P0_OCMEM,
+	MAS_VIDEO_P1_OCMEM,
+	MAS_VFE_OCMEM,
+	MAS_LPASS_AHB,
+	MAS_QDSS_BAM,
+	MAS_SNOC_CFG,
+	MAS_BIMC_SNOC,
+	MAS_CNOC_SNOC,
+	MAS_CRYPTO_CORE0,
+	MAS_CRYPTO_CORE1,
+	MAS_LPASS_PROC,
+	MAS_MSS,
+	MAS_MSS_NAV,
+	MAS_OCMEM_DMA,
+	MAS_PNOC_SNOC,
+	MAS_WCSS,
+	MAS_QDSS_ETR,
+	MAS_USB3,
+	MAS_SDCC_1,
+	MAS_SDCC_3,
+	MAS_SDCC_2,
+	MAS_SDCC_4,
+	MAS_TSIF,
+	MAS_BAM_DMA,
+	MAS_BLSP_2,
+	MAS_USB_HSIC,
+	MAS_BLSP_1,
+	MAS_USB_HS,
+	MAS_PNOC_CFG,
+	MAS_SNOC_PNOC,
+	MAS_RPM_INST,
+	MAS_RPM_DATA,
+	MAS_RPM_SYS,
+	MAS_DEHR,
+	MAS_QDSS_DAP,
+	MAS_SPDM,
+	MAS_TIC,
+	MAS_SNOC_CNOC,
+	MAS_OVNOC_SNOC,
+	MAS_OVNOC_ONOC,
+	MAS_V_OCMEM_GFX3D,
+	MAS_ONOC_OVNOC,
+	MAS_SNOC_OVNOC,
+	MAS_QPIC,
+	MAS_IPA,
+};
+
+enum msm_bus_9625_slv_hw_id {
+	SLV_EBI = 0,
+	SLV_APSS_L2,
+	SLV_BIMC_SNOC,
+	SLV_CAMERA_CFG,
+	SLV_DISPLAY_CFG,
+	SLV_OCMEM_CFG,
+	SLV_CPR_CFG,
+	SLV_CPR_XPU_CFG,
+	SLV_MISC_CFG,
+	SLV_MISC_XPU_CFG,
+	SLV_VENUS_CFG,
+	SLV_GFX3D_CFG,
+	SLV_MMSS_CLK_CFG,
+	SLV_MMSS_CLK_XPU_CFG,
+	SLV_MNOC_MPU_CFG,
+	SLV_ONOC_MPU_CFG,
+	SLV_MMSS_BIMC,
+	SLV_SERVICE_MNOC,
+	SLV_OCMEM,
+	SLV_SERVICE_ONOC,
+	SLV_APPSS,
+	SLV_LPASS,
+	SLV_USB3,
+	SLV_WCSS,
+	SLV_SNOC_BIMC,
+	SLV_SNOC_CNOC,
+	SLV_OCIMEM,
+	SLV_SNOC_OCMEM,
+	SLV_SNOC_PNOC,
+	SLV_SERVICE_SNOC,
+	SLV_QDSS_STM,
+	SLV_SDCC_1,
+	SLV_SDCC_3,
+	SLV_SDCC_2,
+	SLV_SDCC_4,
+	SLV_TSIF,
+	SLV_BAM_DMA,
+	SLV_BLSP_2,
+	SLV_USB_HSIC,
+	SLV_BLSP_1,
+	SLV_USB_HS,
+	SLV_PDM,
+	SLV_PERIPH_APU_CFG,
+	SLV_MPU_CFG,
+	SLV_PRNG,
+	SLV_PNOC_SNOC,
+	SLV_SERVICE_PNOC,
+	SLV_CLK_CTL,
+	SLV_CNOC_MSS,
+	SLV_SECURITY,
+	SLV_TCSR,
+	SLV_TLMM,
+	SLV_CRYPTO_0_CFG,
+	SLV_CRYPTO_1_CFG,
+	SLV_IMEM_CFG,
+	SLV_MESSAGE_RAM,
+	SLV_BIMC_CFG,
+	SLV_BOOT_ROM,
+	SLV_CNOC_MNOC_MMSS_CFG,
+	SLV_PMIC_ARB,
+	SLV_SPDM_WRAPPER,
+	SLV_DEHR_CFG,
+	SLV_MPM,
+	SLV_QDSS_CFG,
+	SLV_RBCPR_CFG,
+	SLV_RBCPR_QDSS_APU_CFG,
+	SLV_CNOC_MNOC_CFG,
+	SLV_SNOC_MPU_CFG,
+	SLV_CNOC_ONOC_CFG,
+	SLV_PNOC_CFG,
+	SLV_SNOC_CFG,
+	SLV_EBI1_DLL_CFG,
+	SLV_PHY_APU_CFG,
+	SLV_EBI1_PHY_CFG,
+	SLV_RPM,
+	SLV_CNOC_SNOC,
+	SLV_SERVICE_CNOC,
+	SLV_SNOC_OVNOC,
+	SLV_ONOC_OVNOC,
+	SLV_USB_HS2,
+	SLV_QPIC,
+	SLV_IPS_CFG,
+};
+
+static uint32_t master_iids[NMASTERS];
+static uint32_t slave_iids[NSLAVES];
+
+/* System NOC nodes */
+static int mport_lpass_ahb[] = {MASTER_PORT_LPASS_AHB,};
+static int mport_qdss_bam[] = {MASTER_PORT_QDSS_BAM,};
+static int mport_snoc_cfg[] = {MASTER_PORT_SNOC_CFG,};
+static int mport_gw_bimc_snoc[] = {MASTER_PORT_GW_BIMC_SNOC,};
+static int mport_gw_cnoc_snoc[] = {MASTER_PORT_GW_CNOC_SNOC,};
+static int mport_crypto_core0[] = {MASTER_PORT_CRYPTO_CORE0,};
+static int mport_lpass_proc[] = {MASTER_PORT_LPASS_PROC};
+static int mport_mss[] = {MASTER_PORT_MSS};
+static int mport_mss_nav[] = {MASTER_PORT_MSS_NAV};
+static int mport_ipa[] = {MASTER_PORT_IPA};
+static int mport_gw_pnoc_snoc[] = {MASTER_PORT_GW_PNOC_SNOC};
+static int mport_qdss_etr[] = {MASTER_PORT_QDSS_ETR};
+
+static int sport_kmpss[] = {SLAVE_PORT_KMPSS};
+static int sport_lpass[] = {SLAVE_PORT_LPASS};
+static int sport_gw_snoc_bimc[] = {SLAVE_PORT_GW_SNOC_BIMC_P0};
+static int sport_gw_snoc_cnoc[] = {SLAVE_PORT_GW_SNOC_CNOC};
+static int sport_ocimem[] = {SLAVE_PORT_OCIMEM};
+static int sport_gw_snoc_pnoc[] = {SLAVE_PORT_GW_SNOC_PNOC};
+static int sport_service_snoc[] = {SLAVE_PORT_SERVICE_SNOC};
+static int sport_qdss_stm[] = {SLAVE_PORT_QDSS_STM};
+
+/* BIMC Nodes */
+
+static int mport_kmpss_m0[] = {MASTER_PORT_KMPSS_M0,};
+static int mport_mss_proc[] = {MASTER_PORT_MSS_PROC};
+static int mport_gw_snoc_bimc[] = {MASTER_PORT_GW_SNOC_BIMC_0};
+
+static int sport_ebi1[] = {SLAVE_PORT_EBI1_CH0};
+static int sport_gw_bimc_snoc[] = {SLAVE_PORT_GW_BIMC_SNOC,};
+
+/* Peripheral NOC Nodes */
+static int mport_sdcc_1[] = {MASTER_PORT_SDCC_1,};
+static int mport_sdcc_3[] = {MASTER_PORT_SDCC_3,};
+static int mport_sdcc_2[] = {MASTER_PORT_SDCC_2,};
+static int mport_sdcc_4[] = {MASTER_PORT_SDCC_4,};
+static int mport_tsif[] = {MASTER_PORT_TSIF,};
+static int mport_bam_dma[] = {MASTER_PORT_BAM_DMA,};
+static int mport_blsp_2[] = {MASTER_PORT_BLSP_2,};
+static int mport_usb_hsic[] = {MASTER_PORT_USB_HSIC,};
+static int mport_blsp_1[] = {MASTER_PORT_BLSP_1,};
+static int mport_pnoc_cfg[] = {MASTER_PORT_PNOC_CFG,};
+static int mport_qpic[] = {MASTER_PORT_QPIC,};
+static int mport_gw_snoc_pnoc[] = {MASTER_PORT_GW_SNOC_PNOC,};
+
+static int sport_sdcc_1[] = {SLAVE_PORT_SDCC_1,};
+static int sport_sdcc_3[] = {SLAVE_PORT_SDCC_3,};
+static int sport_sdcc_2[] = {SLAVE_PORT_SDCC_2,};
+static int sport_sdcc_4[] = {SLAVE_PORT_SDCC_4,};
+static int sport_tsif[] = {SLAVE_PORT_TSIF,};
+static int sport_qpic[] = {SLAVE_PORT_QPIC,};
+static int sport_bam_dma[] = {SLAVE_PORT_BAM_DMA,};
+static int sport_blsp_2[] = {SLAVE_PORT_BLSP_2,};
+static int sport_usb_hsic[] = {SLAVE_PORT_USB_HSIC,};
+static int sport_blsp_1[] = {SLAVE_PORT_BLSP_1,};
+static int sport_pdm[] = {SLAVE_PORT_PDM,};
+static int sport_periph_apu_cfg[] = {
+	SLAVE_PORT_PERIPH_APU_CFG,
+};
+static int sport_pnoc_mpu_cfg[] = {SLAVE_PORT_PNOC_MPU_CFG,};
+static int sport_prng[] = {SLAVE_PORT_PRNG,};
+static int sport_gw_pnoc_snoc[] = {SLAVE_PORT_GW_PNOC_SNOC,};
+static int sport_service_pnoc[] = {SLAVE_PORT_SERVICE_PNOC,};
+
+/* Config NOC Nodes */
+static int mport_rpm_inst[] = {MASTER_PORT_RPM_INST,};
+static int mport_rpm_data[] = {MASTER_PORT_RPM_DATA,};
+static int mport_rpm_sys[] = {MASTER_PORT_RPM_SYS,};
+static int mport_dehr[] = {MASTER_PORT_DEHR,};
+static int mport_qdss_dap[] = {MASTER_PORT_QDSS_DAP,};
+static int mport_spdm[] = {MASTER_PORT_SPDM,};
+static int mport_tic[] = {MASTER_PORT_TIC,};
+static int mport_gw_snoc_cnoc[] = {MASTER_PORT_GW_SNOC_CNOC,};
+
+static int sport_clk_ctl[] = {SLAVE_PORT_CLK_CTL,};
+static int sport_cnoc_mss[] = {SLAVE_PORT_CNOC_MSS,};
+static int sport_security[] = {SLAVE_PORT_SECURITY,};
+static int sport_tcsr[] = {SLAVE_PORT_TCSR,};
+static int sport_tlmm[] = {SLAVE_PORT_TLMM,};
+static int sport_crypto_0_cfg[] = {SLAVE_PORT_CRYPTO_0_CFG,};
+static int sport_imem_cfg[] = {SLAVE_PORT_IMEM_CFG,};
+static int sport_ips_cfg[] = {SLAVE_PORT_IPS_CFG,};
+static int sport_message_ram[] = {SLAVE_PORT_MESSAGE_RAM,};
+static int sport_bimc_cfg[] = {SLAVE_PORT_BIMC_CFG,};
+static int sport_boot_rom[] = {SLAVE_PORT_BOOT_ROM,};
+static int sport_pmic_arb[] = {SLAVE_PORT_PMIC_ARB,};
+static int sport_spdm_wrapper[] = {SLAVE_PORT_SPDM_WRAPPER,};
+static int sport_dehr_cfg[] = {SLAVE_PORT_DEHR_CFG,};
+static int sport_mpm[] = {SLAVE_PORT_MPM,};
+static int sport_qdss_cfg[] = {SLAVE_PORT_QDSS_CFG,};
+static int sport_rbcpr_cfg[] = {SLAVE_PORT_RBCPR_CFG,};
+static int sport_rbcpr_qdss_apu_cfg[] = {SLAVE_PORT_RBCPR_QDSS_APU_CFG,};
+static int sport_snoc_mpu_cfg[] = {SLAVE_PORT_SNOC_MPU_CFG,};
+static int sport_pnoc_cfg[] = {SLAVE_PORT_PNOC_CFG,};
+static int sport_snoc_cfg[] = {SLAVE_PORT_SNOC_CFG,};
+static int sport_phy_apu_cfg[] = {SLAVE_PORT_PHY_APU_CFG,};
+static int sport_ebi1_phy_cfg[] = {SLAVE_PORT_EBI1_PHY_CFG,};
+static int sport_rpm[] = {SLAVE_PORT_RPM,};
+static int sport_gw_cnoc_snoc[] = {SLAVE_PORT_GW_CNOC_SNOC,};
+static int sport_service_cnoc[] = {SLAVE_PORT_SERVICE_CNOC,};
+
+static int tier2[] = {MSM_BUS_BW_TIER2,};
+
+/*
+ * QOS Ports defined only when qos ports are different than
+ * master ports
+ **/
+static int qports_crypto_c0[] = {2};
+static int qports_lpass_proc[] = {4};
+static int qports_gw_snoc_bimc[] = {2};
+static int qports_kmpss[] = {0};
+static int qports_lpass_ahb[] = {0};
+static int qports_qdss_bam[] = {1};
+static int qports_gw_pnoc_snoc[] = {8};
+static int qports_ipa[] = {7};
+static int qports_qdss_etr[] = {10};
+
+static struct msm_bus_node_info sys_noc_info[] = {
+	{
+		.id = MSM_BUS_MASTER_LPASS_AHB,
+		.masterp = mport_lpass_ahb,
+		.num_mports = ARRAY_SIZE(mport_lpass_ahb),
+		.tier = tier2,
+		.num_tiers = ARRAY_SIZE(tier2),
+		.qport = qports_lpass_ahb,
+		.mas_hw_id = MAS_LPASS_AHB,
+		.mode = NOC_QOS_MODE_FIXED,
+		.prio_rd = 2,
+		.prio_wr = 2,
+	},
+	{
+		.id = MSM_BUS_MASTER_QDSS_BAM,
+		.masterp = mport_qdss_bam,
+		.num_mports = ARRAY_SIZE(mport_qdss_bam),
+		.tier = tier2,
+		.num_tiers = ARRAY_SIZE(tier2),
+		.mode = NOC_QOS_MODE_FIXED,
+		.qport = qports_qdss_bam,
+		.mas_hw_id = MAS_QDSS_BAM,
+	},
+	{
+		.id = MSM_BUS_MASTER_SNOC_CFG,
+		.masterp = mport_snoc_cfg,
+		.num_mports = ARRAY_SIZE(mport_snoc_cfg),
+		.tier = tier2,
+		.num_tiers = ARRAY_SIZE(tier2),
+		.mas_hw_id = MAS_SNOC_CFG,
+	},
+	{
+		.id = MSM_BUS_FAB_BIMC,
+		.gateway = 1,
+		.slavep = sport_gw_snoc_bimc,
+		.num_sports = ARRAY_SIZE(sport_gw_snoc_bimc),
+		.masterp = mport_gw_bimc_snoc,
+		.num_mports = ARRAY_SIZE(mport_gw_bimc_snoc),
+		.buswidth = 8,
+		.mas_hw_id = MAS_BIMC_SNOC,
+		.slv_hw_id = SLV_SNOC_BIMC,
+	},
+	{
+		.id = MSM_BUS_FAB_CONFIG_NOC,
+		.gateway = 1,
+		.slavep = sport_gw_snoc_cnoc,
+		.num_sports = ARRAY_SIZE(sport_gw_snoc_cnoc),
+		.masterp = mport_gw_cnoc_snoc,
+		.num_mports = ARRAY_SIZE(mport_gw_cnoc_snoc),
+		.buswidth = 8,
+		.mas_hw_id = MAS_CNOC_SNOC,
+		.slv_hw_id = SLV_SNOC_CNOC,
+	},
+	{
+		.id = MSM_BUS_FAB_PERIPH_NOC,
+		.gateway = 1,
+		.slavep = sport_gw_snoc_pnoc,
+		.num_sports = ARRAY_SIZE(sport_gw_snoc_pnoc),
+		.masterp = mport_gw_pnoc_snoc,
+		.num_mports = ARRAY_SIZE(mport_gw_pnoc_snoc),
+		.buswidth = 8,
+		.qport = qports_gw_pnoc_snoc,
+		.mas_hw_id = MAS_PNOC_SNOC,
+		.slv_hw_id = SLV_SNOC_PNOC,
+		.mode = NOC_QOS_MODE_FIXED,
+		.prio_rd = 2,
+		.prio_wr = 2,
+	},
+	{
+		.id = MSM_BUS_MASTER_CRYPTO_CORE0,
+		.masterp = mport_crypto_core0,
+		.num_mports = ARRAY_SIZE(mport_crypto_core0),
+		.tier = tier2,
+		.num_tiers = ARRAY_SIZE(tier2),
+		.mode = NOC_QOS_MODE_FIXED,
+		.qport = qports_crypto_c0,
+		.mas_hw_id = MAS_CRYPTO_CORE0,
+		.hw_sel = MSM_BUS_NOC,
+	},
+	{
+		.id = MSM_BUS_MASTER_LPASS_PROC,
+		.masterp = mport_lpass_proc,
+		.num_mports = ARRAY_SIZE(mport_lpass_proc),
+		.tier = tier2,
+		.num_tiers = ARRAY_SIZE(tier2),
+		.qport = qports_lpass_proc,
+		.mas_hw_id = MAS_LPASS_PROC,
+		.mode = NOC_QOS_MODE_FIXED,
+		.prio_rd = 2,
+		.prio_wr = 2,
+	},
+	{
+		.id = MSM_BUS_MASTER_MSS,
+		.masterp = mport_mss,
+		.num_mports = ARRAY_SIZE(mport_mss),
+		.tier = tier2,
+		.num_tiers = ARRAY_SIZE(tier2),
+		.mas_hw_id = MAS_MSS,
+	},
+	{
+		.id = MSM_BUS_MASTER_MSS_NAV,
+		.masterp = mport_mss_nav,
+		.num_mports = ARRAY_SIZE(mport_mss_nav),
+		.tier = tier2,
+		.num_tiers = ARRAY_SIZE(tier2),
+		.mas_hw_id = MAS_MSS_NAV,
+	},
+	{
+		.id = MSM_BUS_MASTER_IPA,
+		.masterp = mport_ipa,
+		.num_mports = ARRAY_SIZE(mport_ipa),
+		.tier = tier2,
+		.num_tiers = ARRAY_SIZE(tier2),
+		.mode = NOC_QOS_MODE_FIXED,
+		.qport = qports_ipa,
+		.mas_hw_id = MAS_IPA,
+	},
+	{
+		.id = MSM_BUS_MASTER_QDSS_ETR,
+		.masterp = mport_qdss_etr,
+		.num_mports = ARRAY_SIZE(mport_qdss_etr),
+		.tier = tier2,
+		.num_tiers = ARRAY_SIZE(tier2),
+		.qport = qports_qdss_etr,
+		.mode = NOC_QOS_MODE_FIXED,
+		.mas_hw_id = MAS_QDSS_ETR,
+	},
+	{
+		.id = MSM_BUS_SLAVE_AMPSS,
+		.slavep = sport_kmpss,
+		.num_sports = ARRAY_SIZE(sport_kmpss),
+		.tier = tier2,
+		.num_tiers = ARRAY_SIZE(tier2),
+		.buswidth = 8,
+		.slv_hw_id = SLV_APPSS,
+	},
+	{
+		.id = MSM_BUS_SLAVE_LPASS,
+		.slavep = sport_lpass,
+		.num_sports = ARRAY_SIZE(sport_lpass),
+		.tier = tier2,
+		.num_tiers = ARRAY_SIZE(tier2),
+		.buswidth = 8,
+		.slv_hw_id = SLV_LPASS,
+	},
+	{
+		.id = MSM_BUS_SLAVE_OCIMEM,
+		.slavep = sport_ocimem,
+		.num_sports = ARRAY_SIZE(sport_ocimem),
+		.tier = tier2,
+		.num_tiers = ARRAY_SIZE(tier2),
+		.buswidth = 8,
+		.slv_hw_id = SLV_OCIMEM,
+	},
+	{
+		.id = MSM_BUS_SLAVE_SERVICE_SNOC,
+		.slavep = sport_service_snoc,
+		.num_sports = ARRAY_SIZE(sport_service_snoc),
+		.tier = tier2,
+		.num_tiers = ARRAY_SIZE(tier2),
+		.buswidth = 8,
+		.slv_hw_id = SLV_SERVICE_SNOC,
+	},
+	{
+		.id = MSM_BUS_SLAVE_QDSS_STM,
+		.slavep = sport_qdss_stm,
+		.num_sports = ARRAY_SIZE(sport_qdss_stm),
+		.tier = tier2,
+		.num_tiers = ARRAY_SIZE(tier2),
+		.buswidth = 8,
+		.slv_hw_id = SLV_QDSS_STM,
+	},
+};
+
+static struct msm_bus_node_info bimc_info[]  = {
+	{
+		.id = MSM_BUS_MASTER_AMPSS_M0,
+		.masterp = mport_kmpss_m0,
+		.num_mports = ARRAY_SIZE(mport_kmpss_m0),
+		.tier = tier2,
+		.num_tiers = ARRAY_SIZE(tier2),
+		.hw_sel = MSM_BUS_BIMC,
+		.mode = NOC_QOS_MODE_FIXED,
+		.qport = qports_kmpss,
+		.ws = 10000,
+		.mas_hw_id = MAS_APPSS_PROC,
+		.prio_lvl = 0,
+		.prio_rd = 2,
+		.prio_wr = 2,
+	},
+	{
+		.id = MSM_BUS_MASTER_MSS_PROC,
+		.masterp = mport_mss_proc,
+		.num_mports = ARRAY_SIZE(mport_mss_proc),
+		.tier = tier2,
+		.num_tiers = ARRAY_SIZE(tier2),
+		.hw_sel = MSM_BUS_RPM,
+		.mas_hw_id = MAS_AMSS_PROC,
+	},
+	{
+		.id = MSM_BUS_FAB_SYS_NOC,
+		.gateway = 1,
+		.slavep = sport_gw_bimc_snoc,
+		.num_sports = ARRAY_SIZE(sport_gw_bimc_snoc),
+		.masterp = mport_gw_snoc_bimc,
+		.num_mports = ARRAY_SIZE(mport_gw_snoc_bimc),
+		.qport = qports_gw_snoc_bimc,
+		.buswidth = 8,
+		.ws = 10000,
+		.mas_hw_id = MAS_SNOC_BIMC,
+		.slv_hw_id = SLV_BIMC_SNOC,
+		.mode = NOC_QOS_MODE_BYPASS,
+	},
+	{
+		.id = MSM_BUS_SLAVE_EBI_CH0,
+		.slavep = sport_ebi1,
+		.num_sports = ARRAY_SIZE(sport_ebi1),
+		.tier = tier2,
+		.num_tiers = ARRAY_SIZE(tier2),
+		.buswidth = 8,
+		.slv_hw_id = SLV_EBI,
+		.mode = NOC_QOS_MODE_BYPASS,
+	},
+};
+
+static struct msm_bus_node_info periph_noc_info[] = {
+	{
+		.id = MSM_BUS_MASTER_PNOC_CFG,
+		.masterp = mport_pnoc_cfg,
+		.num_mports = ARRAY_SIZE(mport_pnoc_cfg),
+		.tier = tier2,
+		.num_tiers = ARRAY_SIZE(tier2),
+		.buswidth = 8,
+		.mas_hw_id = MAS_PNOC_CFG,
+	},
+	{
+		.id = MSM_BUS_MASTER_QPIC,
+		.masterp = mport_qpic,
+		.num_mports = ARRAY_SIZE(mport_qpic),
+		.tier = tier2,
+		.num_tiers = ARRAY_SIZE(tier2),
+		.buswidth = 8,
+		.mas_hw_id = MAS_QPIC,
+	},
+	{
+		.id = MSM_BUS_MASTER_SDCC_1,
+		.masterp = mport_sdcc_1,
+		.num_mports = ARRAY_SIZE(mport_sdcc_1),
+		.tier = tier2,
+		.num_tiers = ARRAY_SIZE(tier2),
+		.buswidth = 8,
+		.mas_hw_id = MAS_SDCC_1,
+	},
+	{
+		.id = MSM_BUS_MASTER_SDCC_3,
+		.masterp = mport_sdcc_3,
+		.num_mports = ARRAY_SIZE(mport_sdcc_3),
+		.tier = tier2,
+		.num_tiers = ARRAY_SIZE(tier2),
+		.buswidth = 8,
+		.mas_hw_id = MAS_SDCC_3,
+	},
+	{
+		.id = MSM_BUS_MASTER_SDCC_4,
+		.masterp = mport_sdcc_4,
+		.num_mports = ARRAY_SIZE(mport_sdcc_4),
+		.tier = tier2,
+		.num_tiers = ARRAY_SIZE(tier2),
+		.buswidth = 8,
+		.mas_hw_id = MAS_SDCC_4,
+	},
+	{
+		.id = MSM_BUS_MASTER_SDCC_2,
+		.masterp = mport_sdcc_2,
+		.num_mports = ARRAY_SIZE(mport_sdcc_2),
+		.tier = tier2,
+		.num_tiers = ARRAY_SIZE(tier2),
+		.buswidth = 8,
+		.mas_hw_id = MAS_SDCC_2,
+	},
+	{
+		.id = MSM_BUS_MASTER_TSIF,
+		.masterp = mport_tsif,
+		.num_mports = ARRAY_SIZE(mport_tsif),
+		.tier = tier2,
+		.num_tiers = ARRAY_SIZE(tier2),
+		.buswidth = 8,
+		.mas_hw_id = MAS_TSIF,
+	},
+	{
+		.id = MSM_BUS_MASTER_BAM_DMA,
+		.masterp = mport_bam_dma,
+		.num_mports = ARRAY_SIZE(mport_bam_dma),
+		.tier = tier2,
+		.num_tiers = ARRAY_SIZE(tier2),
+		.buswidth = 8,
+		.mas_hw_id = MAS_BAM_DMA,
+	},
+	{
+		.id = MSM_BUS_MASTER_BLSP_2,
+		.masterp = mport_blsp_2,
+		.num_mports = ARRAY_SIZE(mport_blsp_2),
+		.tier = tier2,
+		.num_tiers = ARRAY_SIZE(tier2),
+		.buswidth = 8,
+		.mas_hw_id = MAS_BLSP_2,
+	},
+	{
+		.id = MSM_BUS_MASTER_USB_HSIC,
+		.masterp = mport_usb_hsic,
+		.num_mports = ARRAY_SIZE(mport_usb_hsic),
+		.tier = tier2,
+		.num_tiers = ARRAY_SIZE(tier2),
+		.buswidth = 8,
+		.mas_hw_id = MAS_USB_HSIC,
+	},
+	{
+		.id = MSM_BUS_MASTER_BLSP_1,
+		.masterp = mport_blsp_1,
+		.num_mports = ARRAY_SIZE(mport_blsp_1),
+		.tier = tier2,
+		.num_tiers = ARRAY_SIZE(tier2),
+		.buswidth = 8,
+		.mas_hw_id = MAS_BLSP_1,
+	},
+	{
+		.id = MSM_BUS_FAB_SYS_NOC,
+		.gateway = 1,
+		.slavep = sport_gw_pnoc_snoc,
+		.num_sports = ARRAY_SIZE(sport_gw_pnoc_snoc),
+		.masterp = mport_gw_snoc_pnoc,
+		.num_mports = ARRAY_SIZE(mport_gw_snoc_pnoc),
+		.tier = tier2,
+		.num_tiers = ARRAY_SIZE(tier2),
+		.buswidth = 8,
+		.slv_hw_id = SLV_PNOC_SNOC,
+		.mas_hw_id = MAS_SNOC_PNOC,
+	},
+	{
+		.id = MSM_BUS_SLAVE_SDCC_1,
+		.slavep = sport_sdcc_1,
+		.num_sports = ARRAY_SIZE(sport_sdcc_1),
+		.tier = tier2,
+		.num_tiers = ARRAY_SIZE(tier2),
+		.buswidth = 8,
+		.slv_hw_id = SLV_SDCC_1,
+	},
+	{
+		.id = MSM_BUS_SLAVE_SDCC_3,
+		.slavep = sport_sdcc_3,
+		.num_sports = ARRAY_SIZE(sport_sdcc_3),
+		.tier = tier2,
+		.num_tiers = ARRAY_SIZE(tier2),
+		.buswidth = 8,
+		.slv_hw_id = SLV_SDCC_3,
+	},
+	{
+		.id = MSM_BUS_SLAVE_SDCC_2,
+		.slavep = sport_sdcc_2,
+		.num_sports = ARRAY_SIZE(sport_sdcc_2),
+		.tier = tier2,
+		.num_tiers = ARRAY_SIZE(tier2),
+		.buswidth = 8,
+		.slv_hw_id = SLV_SDCC_2,
+	},
+	{
+		.id = MSM_BUS_SLAVE_SDCC_4,
+		.slavep = sport_sdcc_4,
+		.num_sports = ARRAY_SIZE(sport_sdcc_4),
+		.tier = tier2,
+		.num_tiers = ARRAY_SIZE(tier2),
+		.buswidth = 8,
+		.slv_hw_id = SLV_SDCC_4,
+	},
+	{
+		.id = MSM_BUS_SLAVE_TSIF,
+		.slavep = sport_tsif,
+		.num_sports = ARRAY_SIZE(sport_tsif),
+		.tier = tier2,
+		.num_tiers = ARRAY_SIZE(tier2),
+		.buswidth = 8,
+		.slv_hw_id = SLV_TSIF,
+	},
+	{
+		.id = MSM_BUS_SLAVE_BAM_DMA,
+		.slavep = sport_bam_dma,
+		.num_sports = ARRAY_SIZE(sport_bam_dma),
+		.tier = tier2,
+		.num_tiers = ARRAY_SIZE(tier2),
+		.buswidth = 8,
+		.slv_hw_id = SLV_BAM_DMA,
+	},
+	{
+		.id = MSM_BUS_SLAVE_QPIC,
+		.masterp = sport_qpic,
+		.num_mports = ARRAY_SIZE(sport_qpic),
+		.tier = tier2,
+		.num_tiers = ARRAY_SIZE(tier2),
+		.buswidth = 8,
+		.mas_hw_id = SLV_QPIC,
+	},
+	{
+		.id = MSM_BUS_SLAVE_BLSP_2,
+		.slavep = sport_blsp_2,
+		.num_sports = ARRAY_SIZE(sport_blsp_2),
+		.tier = tier2,
+		.num_tiers = ARRAY_SIZE(tier2),
+		.buswidth = 8,
+		.slv_hw_id = SLV_BLSP_2,
+	},
+	{
+		.id = MSM_BUS_SLAVE_USB_HSIC,
+		.slavep = sport_usb_hsic,
+		.num_sports = ARRAY_SIZE(sport_usb_hsic),
+		.tier = tier2,
+		.num_tiers = ARRAY_SIZE(tier2),
+		.buswidth = 8,
+		.slv_hw_id = SLV_USB_HSIC,
+	},
+	{
+		.id = MSM_BUS_SLAVE_BLSP_1,
+		.slavep = sport_blsp_1,
+		.num_sports = ARRAY_SIZE(sport_blsp_1),
+		.tier = tier2,
+		.num_tiers = ARRAY_SIZE(tier2),
+		.buswidth = 8,
+		.slv_hw_id = SLV_BLSP_1,
+	},
+	{
+		.id = MSM_BUS_SLAVE_PDM,
+		.slavep = sport_pdm,
+		.num_sports = ARRAY_SIZE(sport_pdm),
+		.tier = tier2,
+		.num_tiers = ARRAY_SIZE(tier2),
+		.buswidth = 8,
+		.slv_hw_id = SLV_PDM,
+	},
+	{
+		.id = MSM_BUS_SLAVE_PERIPH_APU_CFG,
+		.slavep = sport_periph_apu_cfg,
+		.num_sports = ARRAY_SIZE(sport_periph_apu_cfg),
+		.tier = tier2,
+		.num_tiers = ARRAY_SIZE(tier2),
+		.buswidth = 8,
+		.slv_hw_id = SLV_PERIPH_APU_CFG,
+	},
+	{
+		.id = MSM_BUS_SLAVE_PNOC_MPU_CFG,
+		.slavep = sport_pnoc_mpu_cfg,
+		.num_sports = ARRAY_SIZE(sport_pnoc_mpu_cfg),
+		.tier = tier2,
+		.num_tiers = ARRAY_SIZE(tier2),
+		.buswidth = 8,
+		.slv_hw_id = SLV_MPU_CFG,
+	},
+	{
+		.id = MSM_BUS_SLAVE_PRNG,
+		.slavep = sport_prng,
+		.num_sports = ARRAY_SIZE(sport_prng),
+		.tier = tier2,
+		.num_tiers = ARRAY_SIZE(tier2),
+		.buswidth = 8,
+		.slv_hw_id = SLV_PRNG,
+	},
+	{
+		.id = MSM_BUS_SLAVE_SERVICE_PNOC,
+		.slavep = sport_service_pnoc,
+		.num_sports = ARRAY_SIZE(sport_service_pnoc),
+		.tier = tier2,
+		.num_tiers = ARRAY_SIZE(tier2),
+		.buswidth = 8,
+		.slv_hw_id = SLV_SERVICE_PNOC,
+	},
+};
+
+static struct msm_bus_node_info config_noc_info[] = {
+	{
+		.id = MSM_BUS_MASTER_RPM_INST,
+		.masterp = mport_rpm_inst,
+		.num_mports = ARRAY_SIZE(mport_rpm_inst),
+		.tier = tier2,
+		.num_tiers = ARRAY_SIZE(tier2),
+		.buswidth = 8,
+		.mas_hw_id = MAS_RPM_INST,
+	},
+	{
+		.id = MSM_BUS_MASTER_RPM_DATA,
+		.masterp = mport_rpm_data,
+		.num_mports = ARRAY_SIZE(mport_rpm_data),
+		.tier = tier2,
+		.num_tiers = ARRAY_SIZE(tier2),
+		.buswidth = 8,
+		.mas_hw_id = MAS_RPM_DATA,
+	},
+	{
+		.id = MSM_BUS_MASTER_RPM_SYS,
+		.masterp = mport_rpm_sys,
+		.num_mports = ARRAY_SIZE(mport_rpm_sys),
+		.tier = tier2,
+		.num_tiers = ARRAY_SIZE(tier2),
+		.buswidth = 8,
+		.mas_hw_id = MAS_RPM_SYS,
+	},
+	{
+		.id = MSM_BUS_MASTER_DEHR,
+		.masterp = mport_dehr,
+		.num_mports = ARRAY_SIZE(mport_dehr),
+		.tier = tier2,
+		.num_tiers = ARRAY_SIZE(tier2),
+		.buswidth = 8,
+		.mas_hw_id = MAS_DEHR,
+	},
+	{
+		.id = MSM_BUS_MASTER_QDSS_DAP,
+		.masterp = mport_qdss_dap,
+		.num_mports = ARRAY_SIZE(mport_qdss_dap),
+		.tier = tier2,
+		.num_tiers = ARRAY_SIZE(tier2),
+		.buswidth = 8,
+		.mas_hw_id = MAS_QDSS_DAP,
+	},
+	{
+		.id = MSM_BUS_MASTER_SPDM,
+		.masterp = mport_spdm,
+		.num_mports = ARRAY_SIZE(mport_spdm),
+		.tier = tier2,
+		.num_tiers = ARRAY_SIZE(tier2),
+		.buswidth = 8,
+		.mas_hw_id = MAS_SPDM,
+	},
+	{
+		.id = MSM_BUS_MASTER_TIC,
+		.masterp = mport_tic,
+		.num_mports = ARRAY_SIZE(mport_tic),
+		.tier = tier2,
+		.num_tiers = ARRAY_SIZE(tier2),
+		.buswidth = 8,
+		.mas_hw_id = MAS_TIC,
+	},
+	{
+		.id = MSM_BUS_SLAVE_CLK_CTL,
+		.slavep = sport_clk_ctl,
+		.num_sports = ARRAY_SIZE(sport_clk_ctl),
+		.tier = tier2,
+		.num_tiers = ARRAY_SIZE(tier2),
+		.buswidth = 8,
+		.slv_hw_id = SLV_CLK_CTL,
+	},
+	{
+		.id = MSM_BUS_SLAVE_CNOC_MSS,
+		.slavep = sport_cnoc_mss,
+		.num_sports = ARRAY_SIZE(sport_cnoc_mss),
+		.tier = tier2,
+		.num_tiers = ARRAY_SIZE(tier2),
+		.buswidth = 8,
+		.slv_hw_id = SLV_CNOC_MSS,
+	},
+	{
+		.id = MSM_BUS_SLAVE_SECURITY,
+		.slavep = sport_security,
+		.num_sports = ARRAY_SIZE(sport_security),
+		.tier = tier2,
+		.num_tiers = ARRAY_SIZE(tier2),
+		.buswidth = 8,
+		.slv_hw_id = SLV_SECURITY,
+	},
+	{
+		.id = MSM_BUS_SLAVE_TCSR,
+		.slavep = sport_tcsr,
+		.num_sports = ARRAY_SIZE(sport_tcsr),
+		.tier = tier2,
+		.num_tiers = ARRAY_SIZE(tier2),
+		.buswidth = 8,
+		.slv_hw_id = SLV_TCSR,
+	},
+	{
+		.id = MSM_BUS_SLAVE_TLMM,
+		.slavep = sport_tlmm,
+		.num_sports = ARRAY_SIZE(sport_tlmm),
+		.tier = tier2,
+		.num_tiers = ARRAY_SIZE(tier2),
+		.buswidth = 8,
+		.slv_hw_id = SLV_TLMM,
+	},
+	{
+		.id = MSM_BUS_SLAVE_CRYPTO_0_CFG,
+		.slavep = sport_crypto_0_cfg,
+		.num_sports = ARRAY_SIZE(sport_crypto_0_cfg),
+		.tier = tier2,
+		.num_tiers = ARRAY_SIZE(tier2),
+		.buswidth = 8,
+		.slv_hw_id = SLV_CRYPTO_0_CFG,
+	},
+	{
+		.id = MSM_BUS_SLAVE_IMEM_CFG,
+		.slavep = sport_imem_cfg,
+		.num_sports = ARRAY_SIZE(sport_imem_cfg),
+		.tier = tier2,
+		.num_tiers = ARRAY_SIZE(tier2),
+		.buswidth = 8,
+		.slv_hw_id = SLV_IMEM_CFG,
+	},
+	{
+		.id = MSM_BUS_SLAVE_IPS_CFG,
+		.slavep = sport_ips_cfg,
+		.num_sports = ARRAY_SIZE(sport_ips_cfg),
+		.tier = tier2,
+		.num_tiers = ARRAY_SIZE(tier2),
+		.buswidth = 8,
+		.slv_hw_id = SLV_IPS_CFG,
+	},
+	{
+		.id = MSM_BUS_SLAVE_MESSAGE_RAM,
+		.slavep = sport_message_ram,
+		.num_sports = ARRAY_SIZE(sport_message_ram),
+		.tier = tier2,
+		.num_tiers = ARRAY_SIZE(tier2),
+		.buswidth = 8,
+		.slv_hw_id = SLV_MESSAGE_RAM,
+	},
+	{
+		.id = MSM_BUS_SLAVE_BIMC_CFG,
+		.slavep = sport_bimc_cfg,
+		.num_sports = ARRAY_SIZE(sport_bimc_cfg),
+		.tier = tier2,
+		.num_tiers = ARRAY_SIZE(tier2),
+		.buswidth = 8,
+		.slv_hw_id = SLV_BIMC_CFG,
+	},
+	{
+		.id = MSM_BUS_SLAVE_BOOT_ROM,
+		.slavep = sport_boot_rom,
+		.num_sports = ARRAY_SIZE(sport_boot_rom),
+		.tier = tier2,
+		.num_tiers = ARRAY_SIZE(tier2),
+		.buswidth = 8,
+		.slv_hw_id = SLV_BOOT_ROM,
+	},
+	{
+		.id = MSM_BUS_SLAVE_PMIC_ARB,
+		.slavep = sport_pmic_arb,
+		.num_sports = ARRAY_SIZE(sport_pmic_arb),
+		.tier = tier2,
+		.num_tiers = ARRAY_SIZE(tier2),
+		.buswidth = 8,
+		.slv_hw_id = SLV_PMIC_ARB,
+	},
+	{
+		.id = MSM_BUS_SLAVE_SPDM_WRAPPER,
+		.slavep = sport_spdm_wrapper,
+		.num_sports = ARRAY_SIZE(sport_spdm_wrapper),
+		.tier = tier2,
+		.num_tiers = ARRAY_SIZE(tier2),
+		.buswidth = 8,
+		.slv_hw_id = SLV_SPDM_WRAPPER,
+	},
+	{
+		.id = MSM_BUS_SLAVE_DEHR_CFG,
+		.slavep = sport_dehr_cfg,
+		.num_sports = ARRAY_SIZE(sport_dehr_cfg),
+		.tier = tier2,
+		.num_tiers = ARRAY_SIZE(tier2),
+		.buswidth = 8,
+		.slv_hw_id = SLV_DEHR_CFG,
+	},
+	{
+		.id = MSM_BUS_SLAVE_MPM,
+		.slavep = sport_mpm,
+		.num_sports = ARRAY_SIZE(sport_mpm),
+		.tier = tier2,
+		.num_tiers = ARRAY_SIZE(tier2),
+		.buswidth = 8,
+		.slv_hw_id = SLV_MPM,
+	},
+	{
+		.id = MSM_BUS_SLAVE_QDSS_CFG,
+		.slavep = sport_qdss_cfg,
+		.num_sports = ARRAY_SIZE(sport_qdss_cfg),
+		.tier = tier2,
+		.num_tiers = ARRAY_SIZE(tier2),
+		.buswidth = 8,
+		.slv_hw_id = SLV_QDSS_CFG,
+	},
+	{
+		.id = MSM_BUS_SLAVE_RBCPR_CFG,
+		.slavep = sport_rbcpr_cfg,
+		.num_sports = ARRAY_SIZE(sport_rbcpr_cfg),
+		.tier = tier2,
+		.num_tiers = ARRAY_SIZE(tier2),
+		.buswidth = 8,
+		.slv_hw_id = SLV_RBCPR_CFG,
+	},
+	{
+		.id = MSM_BUS_SLAVE_RBCPR_QDSS_APU_CFG,
+		.slavep = sport_rbcpr_qdss_apu_cfg,
+		.num_sports = ARRAY_SIZE(sport_rbcpr_qdss_apu_cfg),
+		.tier = tier2,
+		.num_tiers = ARRAY_SIZE(tier2),
+		.buswidth = 8,
+		.slv_hw_id = SLV_RBCPR_QDSS_APU_CFG,
+	},
+	{
+		.id = MSM_BUS_FAB_SYS_NOC,
+		.gateway = 1,
+		.slavep = sport_gw_cnoc_snoc,
+		.num_sports = ARRAY_SIZE(sport_gw_cnoc_snoc),
+		.masterp = mport_gw_snoc_cnoc,
+		.num_mports = ARRAY_SIZE(mport_gw_snoc_cnoc),
+		.tier = tier2,
+		.num_tiers = ARRAY_SIZE(tier2),
+		.buswidth = 8,
+		.mas_hw_id = MAS_SNOC_CNOC,
+		.slv_hw_id = SLV_CNOC_SNOC,
+	},
+	{
+		.id = MSM_BUS_SLAVE_PNOC_CFG,
+		.slavep = sport_pnoc_cfg,
+		.num_sports = ARRAY_SIZE(sport_pnoc_cfg),
+		.tier = tier2,
+		.num_tiers = ARRAY_SIZE(tier2),
+		.buswidth = 8,
+		.slv_hw_id = SLV_PNOC_CFG,
+	},
+	{
+		.id = MSM_BUS_SLAVE_SNOC_MPU_CFG,
+		.slavep = sport_snoc_mpu_cfg,
+		.num_sports = ARRAY_SIZE(sport_snoc_mpu_cfg),
+		.tier = tier2,
+		.num_tiers = ARRAY_SIZE(tier2),
+		.buswidth = 8,
+		.slv_hw_id = SLV_SNOC_MPU_CFG,
+	},
+	{
+		.id = MSM_BUS_SLAVE_SNOC_CFG,
+		.slavep = sport_snoc_cfg,
+		.num_sports = ARRAY_SIZE(sport_snoc_cfg),
+		.tier = tier2,
+		.num_tiers = ARRAY_SIZE(tier2),
+		.buswidth = 8,
+		.slv_hw_id = SLV_SNOC_CFG,
+	},
+	{
+		.id = MSM_BUS_SLAVE_PHY_APU_CFG,
+		.slavep = sport_phy_apu_cfg,
+		.num_sports = ARRAY_SIZE(sport_phy_apu_cfg),
+		.tier = tier2,
+		.num_tiers = ARRAY_SIZE(tier2),
+		.buswidth = 8,
+		.slv_hw_id = SLV_PHY_APU_CFG,
+	},
+	{
+		.id = MSM_BUS_SLAVE_EBI1_PHY_CFG,
+		.slavep = sport_ebi1_phy_cfg,
+		.num_sports = ARRAY_SIZE(sport_ebi1_phy_cfg),
+		.tier = tier2,
+		.num_tiers = ARRAY_SIZE(tier2),
+		.buswidth = 8,
+		.slv_hw_id = SLV_EBI1_PHY_CFG,
+	},
+	{
+		.id = MSM_BUS_SLAVE_RPM,
+		.slavep = sport_rpm,
+		.num_sports = ARRAY_SIZE(sport_rpm),
+		.tier = tier2,
+		.num_tiers = ARRAY_SIZE(tier2),
+		.buswidth = 8,
+		.slv_hw_id = SLV_RPM,
+	},
+	{
+		.id = MSM_BUS_SLAVE_SERVICE_CNOC,
+		.slavep = sport_service_cnoc,
+		.num_sports = ARRAY_SIZE(sport_service_cnoc),
+		.tier = tier2,
+		.num_tiers = ARRAY_SIZE(tier2),
+		.buswidth = 8,
+		.slv_hw_id = SLV_SERVICE_CNOC,
+	},
+};
+
+static void msm_bus_board_assign_iids(struct msm_bus_fabric_registration
+	*fabreg, int fabid)
+{
+	int i;
+	for (i = 0; i < fabreg->len; i++) {
+		if (!fabreg->info[i].gateway) {
+			fabreg->info[i].priv_id = fabid + fabreg->info[i].id;
+			if (fabreg->info[i].id < SLAVE_ID_KEY) {
+				WARN(fabreg->info[i].id >= NMASTERS,
+					"id %d exceeds array size!\n",
+					fabreg->info[i].id);
+				master_iids[fabreg->info[i].id] =
+					fabreg->info[i].priv_id;
+			} else {
+				WARN((fabreg->info[i].id - SLAVE_ID_KEY) >=
+					NSLAVES, "id %d exceeds array size!\n",
+					fabreg->info[i].id);
+				slave_iids[fabreg->info[i].id - (SLAVE_ID_KEY)]
+					= fabreg->info[i].priv_id;
+			}
+		} else {
+			fabreg->info[i].priv_id = fabreg->info[i].id;
+		}
+	}
+}
+
+static int msm_bus_board_9625_get_iid(int id)
+{
+	if ((id < SLAVE_ID_KEY && id >= NMASTERS) ||
+		id >= (SLAVE_ID_KEY + NSLAVES)) {
+		MSM_BUS_ERR("Cannot get iid. Invalid id %d passed\n", id);
+		return -EINVAL;
+	}
+
+	return CHECK_ID(((id < SLAVE_ID_KEY) ? master_iids[id] :
+		slave_iids[id - SLAVE_ID_KEY]), id);
+}
+
+int msm_bus_board_rpm_get_il_ids(uint16_t *id)
+{
+	return -ENXIO;
+}
+
+static struct msm_bus_board_algorithm msm_bus_board_algo = {
+	.board_nfab = NFAB_9625,
+	.get_iid = msm_bus_board_9625_get_iid,
+	.assign_iids = msm_bus_board_assign_iids,
+};
+
+struct msm_bus_fabric_registration msm_bus_9625_sys_noc_pdata = {
+	.id = MSM_BUS_FAB_SYS_NOC,
+	.name = "msm_sys_noc",
+	.info = sys_noc_info,
+	.len = ARRAY_SIZE(sys_noc_info),
+	.ahb = 0,
+	.fabclk[DUAL_CTX] = "bus_clk",
+	.fabclk[ACTIVE_CTX] = "bus_a_clk",
+	.nmasters = 15,
+	.nslaves = 12,
+	.ntieredslaves = 0,
+	.board_algo = &msm_bus_board_algo,
+	.qos_freq = 4800,
+	.hw_sel = MSM_BUS_NOC,
+	.rpm_enabled = 1,
+};
+
+struct msm_bus_fabric_registration msm_bus_9625_bimc_pdata = {
+	.id = MSM_BUS_FAB_BIMC,
+	.name = "msm_bimc",
+	.info = bimc_info,
+	.len = ARRAY_SIZE(bimc_info),
+	.ahb = 0,
+	.fabclk[DUAL_CTX] = "mem_clk",
+	.fabclk[ACTIVE_CTX] = "mem_a_clk",
+	.nmasters = 7,
+	.nslaves = 4,
+	.ntieredslaves = 0,
+	.board_algo = &msm_bus_board_algo,
+	.qos_freq = 4800,
+	.hw_sel = MSM_BUS_BIMC,
+	.rpm_enabled = 1,
+};
+
+struct msm_bus_fabric_registration msm_bus_9625_periph_noc_pdata = {
+	.id = MSM_BUS_FAB_PERIPH_NOC,
+	.name = "msm_periph_noc",
+	.info = periph_noc_info,
+	.len = ARRAY_SIZE(periph_noc_info),
+	.ahb = 0,
+	.fabclk[DUAL_CTX] = "bus_clk",
+	.fabclk[ACTIVE_CTX] = "bus_a_clk",
+	.nmasters = 14,
+	.nslaves = 15,
+	.ntieredslaves = 0,
+	.board_algo = &msm_bus_board_algo,
+	.hw_sel = MSM_BUS_NOC,
+	.rpm_enabled = 1,
+};
+
+struct msm_bus_fabric_registration msm_bus_9625_config_noc_pdata = {
+	.id = MSM_BUS_FAB_CONFIG_NOC,
+	.name = "msm_config_noc",
+	.info = config_noc_info,
+	.len = ARRAY_SIZE(config_noc_info),
+	.ahb = 0,
+	.fabclk[DUAL_CTX] = "bus_clk",
+	.fabclk[ACTIVE_CTX] = "bus_a_clk",
+	.nmasters = 8,
+	.nslaves = 30,
+	.ntieredslaves = 0,
+	.board_algo = &msm_bus_board_algo,
+	.hw_sel = MSM_BUS_NOC,
+	.rpm_enabled = 1,
+};
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..2c6efb8 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)
 
@@ -71,6 +71,7 @@
 	int hw_sel;
 	const char *slaveclk[NUM_CTX];
 	const char *memclk[NUM_CTX];
+	const char *iface_clk_node;
 	unsigned int buswidth;
 	unsigned int ws;
 	unsigned int mode;
@@ -83,40 +84,41 @@
 };
 
 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;
 	int commit_index;
 	struct nodeclk nodeclk[NUM_CTX];
 	struct nodeclk memclk[NUM_CTX];
+	struct nodeclk iface_clk;
 	void *hw_data;
 };
 
@@ -137,7 +139,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 +164,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 +177,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 +204,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..b6870c6 100644
--- a/arch/arm/mach-msm/msm_bus/msm_bus_fabric.c
+++ b/arch/arm/mach-msm/msm_bus/msm_bus_fabric.c
@@ -175,6 +175,15 @@
 			}
 		}
 
+		if (info->node_info->iface_clk_node) {
+			info->iface_clk.clk = clk_get_sys(info->node_info->
+				iface_clk_node, "iface_clk");
+			if (IS_ERR(info->iface_clk.clk)) {
+				MSM_BUS_ERR("ERR: Couldn't get clk %s\n",
+					info->node_info->iface_clk_node);
+			}
+		}
+
 		ret = info->node_info->gateway ?
 			msm_bus_fabric_add_fab(fabric, info) :
 			msm_bus_fabric_add_node(fabric, info);
@@ -187,6 +196,12 @@
 		if (fabric->fabdev.hw_algo.node_init == NULL)
 			continue;
 
+		if (info->iface_clk.clk) {
+			MSM_BUS_DBG("Enabled iface clock for node init: %d\n",
+				info->node_info->priv_id);
+			clk_prepare_enable(info->iface_clk.clk);
+		}
+
 		for (j = 0; j < NUM_CTX; j++)
 			clk_prepare_enable(fabric->info.nodeclk[j].clk);
 
@@ -198,6 +213,14 @@
 
 		for (j = 0; j < NUM_CTX; j++)
 			clk_disable_unprepare(fabric->info.nodeclk[j].clk);
+
+		if (info->iface_clk.clk) {
+			MSM_BUS_DBG("Disable iface_clk after node init: %d\n",
+				info->node_info->priv_id);
+			clk_disable_unprepare(info->iface_clk.clk);
+		}
+
+
 	}
 
 	MSM_BUS_DBG("Fabric: %d nmasters: %d nslaves: %d\n"
@@ -227,13 +250,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 +289,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 +306,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 +324,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 +336,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 +360,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;
@@ -355,14 +378,35 @@
 		return;
 	}
 
+	/* Enable clocks before accessing QoS registers */
 	for (i = 0; i < NUM_CTX; i++)
 		clk_prepare_enable(fabric->info.nodeclk[i].clk);
 
+	if (info->iface_clk.clk)
+		clk_prepare_enable(info->iface_clk.clk);
+
+	if (hop->iface_clk.clk)
+		clk_prepare_enable(hop->iface_clk.clk);
+
 	fabdev->hw_algo.update_bw(hop, info, fabric->pdata, sel_cdata,
 		master_tiers, add_bw);
+
+	/* Disable clocks after accessing QoS registers */
 	for (i = 0; i < NUM_CTX; i++)
 		clk_disable_unprepare(fabric->info.nodeclk[i].clk);
 
+	if (info->iface_clk.clk) {
+		MSM_BUS_DBG("Commented: Will disable clock for info: %d\n",
+			info->node_info->priv_id);
+		clk_disable_unprepare(info->iface_clk.clk);
+	}
+
+	if (hop->iface_clk.clk) {
+		MSM_BUS_DBG("Commented Will disable clock for hop: %d\n",
+			hop->node_info->priv_id);
+		clk_disable_unprepare(hop->iface_clk.clk);
+	}
+
 	fabric->arb_dirty = true;
 }
 
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..9e89256 100644
--- a/arch/arm/mach-msm/msm_bus/msm_bus_noc.c
+++ b/arch/arm/mach-msm/msm_bus/msm_bus_noc.c
@@ -362,16 +362,18 @@
 	}
 
 	for (i = 0; i < info->node_info->num_mports; i++) {
-		if (info->node_info->mode != NOC_QOS_MODE_BYPASS)
+		if (info->node_info->mode != NOC_QOS_MODE_BYPASS) {
 			noc_set_qos_priority(ninfo, info->node_info->qport[i],
 				prio);
 
-		if (info->node_info->mode != NOC_QOS_MODE_FIXED) {
-			struct msm_bus_noc_qos_bw qbw;
-			qbw.ws = info->node_info->ws;
-			qbw.bw = 0;
-			msm_bus_noc_set_qos_bw(ninfo, info->node_info->qport[i],
-				info->node_info->perm_mode, &qbw);
+			if (info->node_info->mode != NOC_QOS_MODE_FIXED) {
+				struct msm_bus_noc_qos_bw qbw;
+				qbw.ws = info->node_info->ws;
+				qbw.bw = 0;
+				msm_bus_noc_set_qos_bw(ninfo, info->node_info->
+					qport[i], info->node_info->perm_mode,
+					&qbw);
+			}
 		}
 
 		noc_set_qos_mode(ninfo, info->node_info->qport[i], info->
@@ -504,7 +506,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 +529,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..c7a8b98 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,12 +894,26 @@
 		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;
 		return -ENOMEM;
 	}
 
+	/* enable clk for cpr */
+	if (!pdata->clk_enable) {
+		pr_err("CPR: Invalid clk_enable hook\n");
+		return -EFAULT;
+	}
+
+	pdata->clk_enable();
+
 	/* Initialize platform_data */
 	cpr->config = pdata;
 
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 310197e..3b9656e 100644
--- a/arch/arm/mach-msm/msm_dcvs.c
+++ b/arch/arm/mach-msm/msm_dcvs.c
@@ -23,6 +23,7 @@
 #include <linux/stringify.h>
 #include <linux/debugfs.h>
 #include <linux/msm_tsens.h>
+#include <linux/platform_device.h>
 #include <asm/atomic.h>
 #include <asm/page.h>
 #include <mach/msm_dcvs.h>
@@ -33,6 +34,8 @@
 #define __info(f, ...) pr_info("MSM_DCVS: %s: " f, __func__, __VA_ARGS__)
 #define MAX_PENDING	(5)
 
+#define CORE_FLAG_TEMP_UPDATE		0x1
+
 struct core_attribs {
 	struct kobj_attribute freq_change_us;
 
@@ -61,6 +64,9 @@
 
 	struct kobj_attribute thermal_poll_ms;
 
+	struct kobj_attribute freq_tbl;
+	struct kobj_attribute offset_tbl;
+
 	struct attribute_group attrib_group;
 };
 
@@ -121,12 +127,14 @@
 	unsigned int (*get_frequency)(int type_core_num);
 	int (*idle_enable)(int type_core_num,
 			enum msm_core_control_event event);
+	int (*set_floor_frequency)(int type_core_num, unsigned int freq);
 
 	spinlock_t	pending_freq_lock;
 	int pending_freq;
 
 	struct hrtimer	slack_timer;
 	struct delayed_work	temperature_work;
+	int flags;
 };
 
 static int msm_dcvs_enabled = 1;
@@ -138,6 +146,13 @@
 
 static struct kobject *cores_kobj;
 
+#define DCVS_MAX_NUM_FREQS 15
+static struct msm_dcvs_freq_entry cpu_freq_tbl[DCVS_MAX_NUM_FREQS];
+static unsigned num_cpu_freqs;
+static struct msm_dcvs_platform_data *dcvs_pdata;
+
+static DEFINE_MUTEX(gpu_floor_mutex);
+
 static void force_stop_slack_timer(struct dcvs_core *core)
 {
 	unsigned long flags;
@@ -244,6 +259,56 @@
 	spin_unlock_irqrestore(&core->idle_state_change_lock, flags2);
 }
 
+void msm_dcvs_apply_gpu_floor(unsigned long cpu_freq)
+{
+	static unsigned long curr_cpu0_freq;
+	unsigned long gpu_floor_freq = 0;
+	struct dcvs_core *gpu;
+	int i;
+
+	if (!dcvs_pdata)
+		return;
+
+	mutex_lock(&gpu_floor_mutex);
+
+	if (cpu_freq)
+		curr_cpu0_freq = cpu_freq;
+
+	for (i = 0; i < dcvs_pdata->num_sync_rules; i++)
+		if (curr_cpu0_freq > dcvs_pdata->sync_rules[i].cpu_khz) {
+			gpu_floor_freq =
+				dcvs_pdata->sync_rules[i].gpu_floor_khz;
+			break;
+		}
+
+	if (num_online_cpus() > 1)
+		gpu_floor_freq = max(gpu_floor_freq,
+				     dcvs_pdata->gpu_max_nom_khz);
+
+	if (!gpu_floor_freq) {
+		mutex_unlock(&gpu_floor_mutex);
+		return;
+	}
+
+	for (i = GPU_OFFSET; i < CORES_MAX; i++) {
+		gpu = &core_list[i];
+		if (gpu->dcvs_core_id == -1)
+			continue;
+
+		if (gpu->pending_freq != STOP_FREQ_CHANGE &&
+		    gpu->set_floor_frequency) {
+			gpu->set_floor_frequency(gpu->type_core_num,
+						 gpu_floor_freq);
+			/* TZ will know about a freq change (if any)
+			 * at next idle exit. */
+			gpu->actual_freq =
+				gpu->get_frequency(gpu->type_core_num);
+		}
+	}
+
+	mutex_unlock(&gpu_floor_mutex);
+}
+
 static int __msm_dcvs_change_freq(struct dcvs_core *core)
 {
 	int ret = 0;
@@ -254,27 +319,26 @@
 	uint32_t ret1 = 0;
 
 	spin_lock_irqsave(&core->pending_freq_lock, flags);
+	if (core->pending_freq == STOP_FREQ_CHANGE)
+		goto out;
 repeat:
 	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 = 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;
-	}
+	core->pending_freq = NO_OUTSTANDING_FREQ_CHANGE;
 
 	if (requested_freq == core->actual_freq)
 		goto out;
 
 	spin_unlock_irqrestore(&core->pending_freq_lock, flags);
 
+	if (core->type == MSM_DCVS_CORE_TYPE_CPU &&
+	    core->type_core_num == 0)
+		msm_dcvs_apply_gpu_floor(requested_freq);
+
 	/**
 	 * Call the frequency sink driver to change the frequency
 	 * We will need to get back the actual frequency in KHz and
@@ -351,6 +415,9 @@
 	unsigned long temp = 0;
 	int interval_ms;
 
+	if (!(core->flags & CORE_FLAG_TEMP_UPDATE))
+		return;
+
 	tsens_dev.sensor_num = core->sensor;
 	ret = tsens_get_temp(&tsens_dev, &temp);
 	if (!temp) {
@@ -382,9 +449,6 @@
 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);
 
 	while (!kthread_should_stop()) {
 		wait_event(core->wait_q, !(core->pending_freq == 0 ||
@@ -413,10 +477,7 @@
 	}
 
 	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;
+		core->pending_freq = STOP_FREQ_CHANGE;
 		return;
 	}
 
@@ -492,6 +553,36 @@
 	return HRTIMER_NORESTART;
 }
 
+int msm_dcvs_update_algo_params(void)
+{
+	static struct msm_dcvs_algo_param curr_params;
+	static DEFINE_MUTEX(param_update_mutex);
+	struct msm_dcvs_algo_param *new_params;
+	int cpu, ret = 0;
+
+	mutex_lock(&param_update_mutex);
+	new_params = &core_list[CPU_OFFSET + num_online_cpus() - 1].algo_param;
+
+	if (memcmp(&curr_params, new_params,
+		   sizeof(struct msm_dcvs_algo_param))) {
+		for_each_possible_cpu(cpu) {
+			ret = msm_dcvs_scm_set_algo_params(CPU_OFFSET + cpu,
+							   new_params);
+			if (ret) {
+				pr_err("scm set algo params failed on cpu %d, ret %d\n",
+				       cpu, ret);
+				mutex_unlock(&param_update_mutex);
+				return ret;
+			}
+		}
+		memcpy(&curr_params, new_params,
+		       sizeof(struct msm_dcvs_algo_param));
+	}
+
+	mutex_unlock(&param_update_mutex);
+	return ret;
+}
+
 /* Helper functions and macros for sysfs nodes for a core */
 #define CORE_FROM_ATTRIBS(attr, name) \
 	container_of(container_of(attr, struct core_attribs, name), \
@@ -546,12 +637,9 @@
 	} else { \
 		uint32_t old_val = core->algo_param._name; \
 		core->algo_param._name = val; \
-		ret = msm_dcvs_scm_set_algo_params(core->dcvs_core_id, \
-				&core->algo_param); \
+		ret = msm_dcvs_update_algo_params(); \
 		if (ret) { \
 			core->algo_param._name = old_val; \
-			__err("Error(%d) in setting %d for algo param %s\n",\
-					ret, val, __stringify(_name)); \
 		} \
 	} \
 	return count; \
@@ -633,11 +721,154 @@
 
 DCVS_PARAM_STORE(thermal_poll_ms)
 
+static ssize_t msm_dcvs_attr_offset_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, offset_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++) {
+		len = snprintf(buf_idx, 30, "%7d %7d %7d\n",
+			       freq_tbl[i].freq,
+			       freq_tbl[i].active_energy_offset,
+			       freq_tbl[i].leakage_energy_offset);
+		/* buf_idx always points at terminating null */
+		buf_idx += len;
+	}
+	return buf_idx - buf;
+}
+
+static ssize_t msm_dcvs_attr_offset_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, active_energy_offset, leakage_energy_offset;
+	int i, ret;
+	struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, offset_tbl);
+
+	freq_tbl = core->info->freq_tbl;
+
+	ret = sscanf(buf, "%u %u %u",
+		     &freq, &active_energy_offset, &leakage_energy_offset);
+	if (ret != 3) {
+		__err("Invalid input %s for offset_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].active_energy_offset =
+				active_energy_offset;
+			freq_tbl[i].leakage_energy_offset =
+				leakage_energy_offset;
+			break;
+		}
+
+	if (i >= core->info->power_param.num_freq) {
+		__err("Invalid frequency for offset_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)
+		__err("Error %d in updating active/leakage energy\n", ret);
+
+	return count;
+}
+
+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 = 24;
+	const int attr_count = 26;
 
 	BUG_ON(!cores_kobj);
 
@@ -675,7 +906,10 @@
 	DCVS_RW_ATTRIB(21, leakage_coeff_d);
 	DCVS_RW_ATTRIB(22, thermal_poll_ms);
 
-	core->attrib.attrib_group.attrs[23] = NULL;
+	DCVS_RW_ATTRIB(23, freq_tbl);
+	DCVS_RW_ATTRIB(24, offset_tbl);
+
+	core->attrib.attrib_group.attrs[25] = NULL;
 
 	core_kobj = kobject_create_and_add(core->core_name, cores_kobj);
 	if (!core_kobj) {
@@ -752,6 +986,16 @@
 	return &core_list[offset];
 }
 
+void msm_dcvs_register_cpu_freq(uint32_t freq, uint32_t voltage)
+{
+	BUG_ON(freq == 0 || voltage == 0 ||
+	       num_cpu_freqs == DCVS_MAX_NUM_FREQS);
+
+	cpu_freq_tbl[num_cpu_freqs].freq = freq;
+	cpu_freq_tbl[num_cpu_freqs].voltage = voltage;
+
+	num_cpu_freqs++;
+}
 
 int msm_dcvs_register_core(
 	enum msm_dcvs_core_type type,
@@ -761,6 +1005,7 @@
 	unsigned int (*get_frequency)(int type_core_num),
 	int (*idle_enable)(int type_core_num,
 					enum msm_core_control_event event),
+	int (*set_floor_frequency)(int type_core_num, unsigned int freq),
 	int sensor)
 {
 	int ret = -EINVAL;
@@ -784,9 +1029,15 @@
 	core->set_frequency = set_frequency;
 	core->get_frequency = get_frequency;
 	core->idle_enable = idle_enable;
-	core->pending_freq = STOP_FREQ_CHANGE;
+	core->set_floor_frequency = set_floor_frequency;
 
 	core->info = info;
+	if (type == MSM_DCVS_CORE_TYPE_CPU) {
+		BUG_ON(num_cpu_freqs == 0);
+		info->freq_tbl = cpu_freq_tbl;
+		info->power_param.num_freq = num_cpu_freqs;
+	}
+
 	memcpy(&core->algo_param, &info->algo_param,
 			sizeof(struct msm_dcvs_algo_param));
 
@@ -840,10 +1091,6 @@
 	core->task = kthread_run(msm_dcvs_do_freq, (void *)core,
 			"msm_dcvs/%d", core->dcvs_core_id);
 	ret = core->dcvs_core_id;
-
-	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;
@@ -912,6 +1159,10 @@
 	}
 	force_start_slack_timer(core, timer_interval_us);
 
+	core->flags |= CORE_FLAG_TEMP_UPDATE;
+	INIT_DELAYED_WORK(&core->temperature_work, msm_dcvs_report_temp_work);
+	schedule_delayed_work(&core->temperature_work,
+			      msecs_to_jiffies(core->info->thermal_poll_ms));
 
 	core->idle_enable(core->type_core_num, MSM_DCVS_ENABLE_IDLE_PULSE);
 	return 0;
@@ -938,16 +1189,27 @@
 		return ret;
 	}
 
+	core->flags &= ~CORE_FLAG_TEMP_UPDATE;
+	cancel_delayed_work(&core->temperature_work);
+
 	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);
+
+	if (core->type == MSM_DCVS_CORE_TYPE_GPU)
+		mutex_lock(&gpu_floor_mutex);
+
 	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);
+
+	if (core->type == MSM_DCVS_CORE_TYPE_GPU)
+		mutex_unlock(&gpu_floor_mutex);
+
 	force_stop_slack_timer(core);
 
 	return 0;
@@ -1036,11 +1298,29 @@
 }
 late_initcall(msm_dcvs_late_init);
 
+static int __devinit dcvs_probe(struct platform_device *pdev)
+{
+	if (pdev->dev.platform_data)
+		dcvs_pdata = pdev->dev.platform_data;
+
+	return 0;
+}
+
+static struct platform_driver dcvs_driver = {
+	.probe = dcvs_probe,
+	.driver = {
+		.name = "dcvs",
+		.owner = THIS_MODULE,
+	},
+};
+
 static int __init msm_dcvs_early_init(void)
 {
 	int ret = 0;
 	int i;
 
+	platform_driver_register(&dcvs_driver);
+
 	if (!msm_dcvs_enabled) {
 		__info("Not enabled (%d)\n", msm_dcvs_enabled);
 		return 0;
@@ -1054,8 +1334,10 @@
 		goto done;
 	}
 
-	for (i = 0; i < CORES_MAX; i++)
+	for (i = 0; i < CORES_MAX; i++) {
 		core_list[i].dcvs_core_id = -1;
+		core_list[i].pending_freq = STOP_FREQ_CHANGE;
+	}
 done:
 	return ret;
 }
diff --git a/arch/arm/mach-msm/msm_dsps.c b/arch/arm/mach-msm/msm_dsps.c
index b85c812..0551130 100644
--- a/arch/arm/mach-msm/msm_dsps.c
+++ b/arch/arm/mach-msm/msm_dsps.c
@@ -32,7 +32,6 @@
 #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>
@@ -90,14 +89,13 @@
 /**
  *  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);
@@ -111,7 +109,7 @@
 {
 	pr_debug("%s.\n", __func__);
 
-	pil_put(drv->pil);
+	subsystem_put(drv->pil);
 }
 
 /**
@@ -531,7 +529,7 @@
 		if (ret)
 			return ret;
 
-		ret = dsps_load(drv->pdata->pil_name);
+		ret = dsps_load();
 
 		if (ret) {
 			dsps_power_off_handler();
@@ -587,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.
  */
@@ -679,6 +677,8 @@
 		goto cdev_add_err;
 	}
 
+	return 0;
+
 cdev_add_err:
 	kfree(drv->cdev);
 cdev_alloc_err:
diff --git a/arch/arm/mach-msm/msm_ipc_router_security.c b/arch/arm/mach-msm/msm_ipc_router_security.c
new file mode 100644
index 0000000..27cf524
--- /dev/null
+++ b/arch/arm/mach-msm/msm_ipc_router_security.c
@@ -0,0 +1,271 @@
+/* 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/types.h>
+#include <linux/net.h>
+#include <linux/socket.h>
+#include <linux/errno.h>
+#include <linux/mm.h>
+#include <linux/poll.h>
+#include <linux/fcntl.h>
+#include <linux/gfp.h>
+#include <linux/uaccess.h>
+#include <linux/kernel.h>
+#include <linux/msm_ipc.h>
+
+#include <asm/uaccess.h>
+
+#include <net/sock.h>
+#include "ipc_router.h"
+#include "msm_ipc_router_security.h"
+
+#define SEC_RULES_HASH_SZ 32
+struct security_rule {
+	struct list_head list;
+	uint32_t service_id;
+	uint32_t instance_id;
+	unsigned reserved;
+	int num_group_info;
+	int *group_id;
+};
+
+static DEFINE_MUTEX(security_rules_lock);
+static struct list_head security_rules[SEC_RULES_HASH_SZ];
+
+/**
+ * check_permisions() - Check whether the process has permissions to
+ *                      create an interface handle with IPC Router
+ *
+ * @return: true if the process has permissions, else false.
+ */
+int check_permissions(void)
+{
+	int rc = 0;
+	if (!current_euid() || in_egroup_p(AID_NET_RAW))
+		rc = 1;
+	return rc;
+}
+EXPORT_SYMBOL(check_permissions);
+
+/**
+ * msm_ipc_config_sec_rules() - Add a security rule to the database
+ * @arg: Pointer to the buffer containing the rule.
+ *
+ * @return: 0 if successfully added, < 0 for error.
+ *
+ * A security rule is defined using <Service_ID: Group_ID> tuple. The rule
+ * implies that a user-space process in order to send a QMI message to
+ * service Service_ID should belong to the Linux group Group_ID.
+ */
+int msm_ipc_config_sec_rules(void *arg)
+{
+	struct config_sec_rules_args sec_rules_arg;
+	struct security_rule *rule, *temp_rule;
+	int key;
+	int ret;
+
+	if (current_euid())
+		return -EPERM;
+
+	ret = copy_from_user(&sec_rules_arg, (void *)arg,
+			     sizeof(sec_rules_arg));
+	if (ret)
+		return -EFAULT;
+
+	if (sec_rules_arg.num_group_info <= 0)
+		return -EINVAL;
+
+	rule = kzalloc(sizeof(struct security_rule), GFP_KERNEL);
+	if (!rule) {
+		pr_err("%s: security_rule alloc failed\n", __func__);
+		return -ENOMEM;
+	}
+
+	rule->group_id = kzalloc((sec_rules_arg.num_group_info * sizeof(int)),
+				 GFP_KERNEL);
+	if (!rule->group_id) {
+		pr_err("%s: group_id alloc failed\n", __func__);
+		kfree(rule);
+		return -ENOMEM;
+	}
+
+	rule->service_id = sec_rules_arg.service_id;
+	rule->instance_id = sec_rules_arg.instance_id;
+	rule->reserved = sec_rules_arg.reserved;
+	rule->num_group_info = sec_rules_arg.num_group_info;
+	ret = copy_from_user(rule->group_id,
+			     ((void *)(arg + sizeof(sec_rules_arg))),
+			     (rule->num_group_info * sizeof(uint32_t)));
+	if (ret) {
+		kfree(rule->group_id);
+		kfree(rule);
+		return -EFAULT;
+	}
+
+	key = rule->service_id & (SEC_RULES_HASH_SZ - 1);
+	mutex_lock(&security_rules_lock);
+	if (rule->service_id == ALL_SERVICE) {
+		temp_rule = list_first_entry(&security_rules[key],
+					     struct security_rule, list);
+		list_del(&temp_rule->list);
+		kfree(temp_rule->group_id);
+		kfree(temp_rule);
+	}
+	list_add_tail(&rule->list, &security_rules[key]);
+	mutex_unlock(&security_rules_lock);
+
+	if (rule->service_id == ALL_SERVICE)
+		msm_ipc_sync_default_sec_rule((void *)rule);
+	else
+		msm_ipc_sync_sec_rule(rule->service_id, rule->instance_id,
+				      (void *)rule);
+
+	return 0;
+}
+EXPORT_SYMBOL(msm_ipc_config_sec_rules);
+
+/**
+ * msm_ipc_add_default_rule() - Add default security rule
+ *
+ * @return: 0 on success, < 0 on error/
+ *
+ * This function is used to ensure the basic security, if there is no
+ * security rule defined for a service. It can be overwritten by the
+ * default security rule from user-space script.
+ */
+static int msm_ipc_add_default_rule(void)
+{
+	struct security_rule *rule;
+	int key;
+
+	rule = kzalloc(sizeof(struct security_rule), GFP_KERNEL);
+	if (!rule) {
+		pr_err("%s: security_rule alloc failed\n", __func__);
+		return -ENOMEM;
+	}
+
+	rule->group_id = kzalloc(sizeof(int), GFP_KERNEL);
+	if (!rule->group_id) {
+		pr_err("%s: group_id alloc failed\n", __func__);
+		kfree(rule);
+		return -ENOMEM;
+	}
+
+	rule->service_id = ALL_SERVICE;
+	rule->instance_id = ALL_INSTANCE;
+	rule->num_group_info = 1;
+	*(rule->group_id) = AID_NET_RAW;
+	mutex_lock(&security_rules_lock);
+	key = (ALL_SERVICE & (SEC_RULES_HASH_SZ - 1));
+	list_add_tail(&rule->list, &security_rules[key]);
+	mutex_unlock(&security_rules_lock);
+	return 0;
+}
+
+/**
+ * msm_ipc_get_security_rule() - Get the security rule corresponding to a
+ *                               service
+ * @service_id: Service ID for which the rule has to be got.
+ * @instance_id: Instance ID for which the rule has to be got.
+ *
+ * @return: Returns the rule info on success, NULL on error.
+ *
+ * This function is used when the service comes up and gets registered with
+ * the IPC Router.
+ */
+void *msm_ipc_get_security_rule(uint32_t service_id, uint32_t instance_id)
+{
+	int key;
+	struct security_rule *rule;
+
+	key = (service_id & (SEC_RULES_HASH_SZ - 1));
+	mutex_lock(&security_rules_lock);
+	/* Return the rule for a specific <service:instance>, if found. */
+	list_for_each_entry(rule, &security_rules[key], list) {
+		if ((rule->service_id == service_id) &&
+		    (rule->instance_id == instance_id)) {
+			mutex_unlock(&security_rules_lock);
+			return (void *)rule;
+		}
+	}
+
+	/* Return the rule for a specific service, if found. */
+	list_for_each_entry(rule, &security_rules[key], list) {
+		if ((rule->service_id == service_id) &&
+		    (rule->instance_id == ALL_INSTANCE)) {
+			mutex_unlock(&security_rules_lock);
+			return (void *)rule;
+		}
+	}
+
+	/* Return the default rule, if no rule defined for a service. */
+	key = (ALL_SERVICE & (SEC_RULES_HASH_SZ - 1));
+	list_for_each_entry(rule, &security_rules[key], list) {
+		if ((rule->service_id == ALL_SERVICE) &&
+		    (rule->instance_id == ALL_INSTANCE)) {
+			mutex_unlock(&security_rules_lock);
+			return (void *)rule;
+		}
+	}
+	return NULL;
+}
+EXPORT_SYMBOL(msm_ipc_get_security_rule);
+
+/**
+ * msm_ipc_check_send_permissions() - Check if the sendng process has
+ *                                    permissions specified as per the rule
+ * @data: Security rule to be checked.
+ *
+ * @return: true if the process has permissions, else false.
+ *
+ * This function is used to check if the current executing process has
+ * permissions to send message to the remote entity. The security rule
+ * corresponding to the remote entity is specified by "data" parameter
+ */
+int msm_ipc_check_send_permissions(void *data)
+{
+	int i;
+	struct security_rule *rule = (struct security_rule *)data;
+
+	/* Source/Sender is Root user */
+	if (!current_euid())
+		return 1;
+
+	/* Destination has no rules defined, possibly a client. */
+	if (!rule)
+		return 1;
+
+	for (i = 0; i < rule->num_group_info; i++) {
+		if (in_egroup_p(rule->group_id[i]))
+			return 1;
+	}
+	return 0;
+}
+EXPORT_SYMBOL(msm_ipc_check_send_permissions);
+
+/**
+ * msm_ipc_router_security_init() - Initialize the security rule database
+ *
+ * @return: 0 if successful, < 0 for error.
+ */
+int msm_ipc_router_security_init(void)
+{
+	int i;
+
+	for (i = 0; i < SEC_RULES_HASH_SZ; i++)
+		INIT_LIST_HEAD(&security_rules[i]);
+
+	msm_ipc_add_default_rule();
+	return 0;
+}
+EXPORT_SYMBOL(msm_ipc_router_security_init);
diff --git a/arch/arm/mach-msm/msm_ipc_router_security.h b/arch/arm/mach-msm/msm_ipc_router_security.h
new file mode 100644
index 0000000..8701343
--- /dev/null
+++ b/arch/arm/mach-msm/msm_ipc_router_security.h
@@ -0,0 +1,104 @@
+/* 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_IPC_ROUTER_SECURITY_H
+#define _MSM_IPC_ROUTER_SECURITY_H
+
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/errno.h>
+
+#ifdef CONFIG_MSM_IPC_ROUTER_SECURITY
+#include <linux/android_aid.h>
+
+/**
+ * check_permisions() - Check whether the process has permissions to
+ *                      create an interface handle with IPC Router
+ *
+ * @return: true if the process has permissions, else false.
+ */
+int check_permissions(void);
+
+/**
+ * msm_ipc_config_sec_rules() - Add a security rule to the database
+ * @arg: Pointer to the buffer containing the rule.
+ *
+ * @return: 0 if successfully added, < 0 for error.
+ *
+ * A security rule is defined using <Service_ID: Group_ID> tuple. The rule
+ * implies that a user-space process in order to send a QMI message to
+ * service Service_ID should belong to the Linux group Group_ID.
+ */
+int msm_ipc_config_sec_rules(void *arg);
+
+/**
+ * msm_ipc_get_security_rule() - Get the security rule corresponding to a
+ *                               service
+ * @service_id: Service ID for which the rule has to be got.
+ * @instance_id: Instance ID for which the rule has to be got.
+ *
+ * @return: Returns the rule info on success, NULL on error.
+ *
+ * This function is used when the service comes up and gets registered with
+ * the IPC Router.
+ */
+void *msm_ipc_get_security_rule(uint32_t service_id, uint32_t instance_id);
+
+/**
+ * msm_ipc_check_send_permissions() - Check if the sendng process has
+ *                                    permissions specified as per the rule
+ * @data: Security rule to be checked.
+ *
+ * @return: true if the process has permissions, else false.
+ *
+ * This function is used to check if the current executing process has
+ * permissions to send message to the remote entity. The security rule
+ * corresponding to the remote entity is specified by "data" parameter
+ */
+int msm_ipc_check_send_permissions(void *data);
+
+/**
+ * msm_ipc_router_security_init() - Initialize the security rule database
+ *
+ * @return: 0 if successful, < 0 for error.
+ */
+int msm_ipc_router_security_init(void);
+
+#else
+
+static inline int check_permissions(void)
+{
+	return 1;
+}
+
+static inline int msm_ipc_config_sec_rules(void *arg)
+{
+	return -ENODEV;
+}
+
+static inline void *msm_ipc_get_security_rule(uint32_t service_id,
+					      uint32_t instance_id)
+{
+	return NULL;
+}
+
+static inline int msm_ipc_check_send_permissions(void *data)
+{
+	return 1;
+}
+
+static inline int msm_ipc_router_security_init(void)
+{
+	return 0;
+}
+#endif
+#endif
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 9f6cc11..94b546a 100644
--- a/arch/arm/mach-msm/msm_mpdecision.c
+++ b/arch/arm/mach-msm/msm_mpdecision.c
@@ -123,6 +123,7 @@
 
 static unsigned long last_nr;
 static int num_present_hundreds;
+static ktime_t last_down_time;
 
 #define RQ_AVG_INSIGNIFICANT_BITS	3
 static bool ok_to_update_tz(int nr, int last_nr)
@@ -356,11 +357,8 @@
 static int __cpuinit msm_mpd_do_hotplug(void *data)
 {
 	int *event = (int *)data;
-	static struct sched_param param = {.sched_priority = MAX_RT_PRIO - 1};
 	int cpu;
 
-	sched_setscheduler(current, SCHED_FIFO, &param);
-
 	while (1) {
 		wait_event(msm_mpd.wait_hpq, *event || kthread_should_stop());
 		if (kthread_should_stop())
@@ -383,15 +381,18 @@
 			}
 		}
 
-		for_each_possible_cpu(cpu) {
-			if (!(atomic_read(&msm_mpd.algo_cpu_mask) & (1 << cpu))
-				&& cpu_online(cpu)) {
-				bring_down_cpu(cpu);
-				if (!cpu_online(cpu))
-					goto restart;
-			}
-		}
+		if (ktime_to_ns(ktime_sub(ktime_get(), last_down_time)) >
+		    100 * NSEC_PER_MSEC)
+			for_each_possible_cpu(cpu)
+				if (!(atomic_read(&msm_mpd.algo_cpu_mask) &
+				      (1 << cpu)) && cpu_online(cpu)) {
+					bring_down_cpu(cpu);
+					last_down_time = ktime_get();
+					break;
+				}
 		msm_mpd.hpupdate = HPUPDATE_WAITING;
+		msm_dcvs_apply_gpu_floor(0);
+		msm_dcvs_update_algo_params();
 	}
 
 	return 0;
@@ -400,13 +401,10 @@
 static int msm_mpd_do_update_scm(void *data)
 {
 	struct msm_mpd_scm_data *scm_data = (struct msm_mpd_scm_data *)data;
-	static struct sched_param param = {.sched_priority = MAX_RT_PRIO - 1};
 	unsigned long flags;
 	enum msm_dcvs_scm_event event;
 	int nr;
 
-	sched_setscheduler(current, SCHED_FIFO, &param);
-
 	while (1) {
 		wait_event(msm_mpd.wait_q,
 			msm_mpd.data.event == MSM_DCVS_SCM_MPD_QOS_TIMER_EXPIRED
diff --git a/arch/arm/mach-msm/msm_qmi_interface.c b/arch/arm/mach-msm/msm_qmi_interface.c
new file mode 100644
index 0000000..4c4635a
--- /dev/null
+++ b/arch/arm/mach-msm/msm_qmi_interface.c
@@ -0,0 +1,694 @@
+/* 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/slab.h>
+#include <linux/uaccess.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/io.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/list.h>
+#include <linux/socket.h>
+#include <linux/gfp.h>
+#include <linux/qmi_encdec.h>
+
+#include <mach/msm_qmi_interface.h>
+#include <mach/msm_ipc_router.h>
+
+#include "msm_qmi_interface_priv.h"
+
+static LIST_HEAD(svc_event_nb_list);
+static DEFINE_MUTEX(svc_event_nb_list_lock);
+
+struct elem_info qmi_response_type_v01_ei[] = {
+	{
+		.data_type	= QMI_SIGNED_2_BYTE_ENUM,
+		.elem_len	= 1,
+		.elem_size	= sizeof(uint16_t),
+		.is_array	= NO_ARRAY,
+		.tlv_type	= QMI_COMMON_TLV_TYPE,
+		.offset		= offsetof(struct qmi_response_type_v01,
+					   result),
+		.ei_array	= NULL,
+	},
+	{
+		.data_type      = QMI_SIGNED_2_BYTE_ENUM,
+		.elem_len       = 1,
+		.elem_size      = sizeof(uint16_t),
+		.is_array       = NO_ARRAY,
+		.tlv_type       = QMI_COMMON_TLV_TYPE,
+		.offset         = offsetof(struct qmi_response_type_v01,
+					   error),
+		.ei_array       = NULL,
+	},
+	{
+		.data_type	= QMI_EOTI,
+		.elem_len	= 0,
+		.elem_size	= 0,
+		.is_array	= NO_ARRAY,
+		.tlv_type	= QMI_COMMON_TLV_TYPE,
+		.offset		= 0,
+		.ei_array	= NULL,
+	},
+};
+
+static void qmi_event_notify(unsigned event, void *priv)
+{
+	struct qmi_handle *handle = (struct qmi_handle *)priv;
+	unsigned long flags;
+
+	if (!handle)
+		return;
+
+	mutex_lock(&handle->handle_lock);
+	if (handle->handle_reset) {
+		mutex_unlock(&handle->handle_lock);
+		return;
+	}
+
+	switch (event) {
+	case MSM_IPC_ROUTER_READ_CB:
+		spin_lock_irqsave(&handle->notify_lock, flags);
+		handle->notify(handle, QMI_RECV_MSG, handle->notify_priv);
+		spin_unlock_irqrestore(&handle->notify_lock, flags);
+		break;
+
+	default:
+		break;
+	}
+	mutex_unlock(&handle->handle_lock);
+}
+
+struct qmi_handle *qmi_handle_create(
+	void (*notify)(struct qmi_handle *handle,
+		       enum qmi_event_type event, void *notify_priv),
+	void *notify_priv)
+{
+	struct qmi_handle *temp_handle;
+	struct msm_ipc_port *port_ptr;
+
+	temp_handle = kzalloc(sizeof(struct qmi_handle), GFP_KERNEL);
+	if (!temp_handle) {
+		pr_err("%s: Failure allocating client handle\n", __func__);
+		return NULL;
+	}
+
+	port_ptr = msm_ipc_router_create_port(qmi_event_notify,
+					      (void *)temp_handle);
+	if (!port_ptr) {
+		pr_err("%s: IPC router port creation failed\n", __func__);
+		kfree(temp_handle);
+		return NULL;
+	}
+
+	temp_handle->src_port = port_ptr;
+	temp_handle->next_txn_id = 1;
+	INIT_LIST_HEAD(&temp_handle->txn_list);
+	mutex_init(&temp_handle->handle_lock);
+	spin_lock_init(&temp_handle->notify_lock);
+	temp_handle->notify = notify;
+	temp_handle->notify_priv = notify_priv;
+	temp_handle->handle_reset = 0;
+	init_waitqueue_head(&temp_handle->reset_waitq);
+	return temp_handle;
+}
+EXPORT_SYMBOL(qmi_handle_create);
+
+static void clean_txn_info(struct qmi_handle *handle)
+{
+	struct qmi_txn *txn_handle, *temp_txn_handle;
+
+	list_for_each_entry_safe(txn_handle, temp_txn_handle,
+				 &handle->txn_list, list) {
+		if (txn_handle->type == QMI_ASYNC_TXN) {
+			list_del(&txn_handle->list);
+			kfree(txn_handle);
+		} else if (txn_handle->type == QMI_SYNC_TXN) {
+			wake_up(&txn_handle->wait_q);
+		}
+	}
+}
+
+int qmi_handle_destroy(struct qmi_handle *handle)
+{
+	int rc;
+
+	if (!handle)
+		return -EINVAL;
+
+	mutex_lock(&handle->handle_lock);
+	handle->handle_reset = 1;
+	clean_txn_info(handle);
+	mutex_unlock(&handle->handle_lock);
+
+	rc = wait_event_interruptible(handle->reset_waitq,
+				      list_empty(&handle->txn_list));
+
+	/* TODO: Destroy client owned transaction */
+	msm_ipc_router_close_port((struct msm_ipc_port *)(handle->src_port));
+	kfree(handle->dest_info);
+	kfree(handle);
+	return 0;
+}
+EXPORT_SYMBOL(qmi_handle_destroy);
+
+int qmi_register_ind_cb(struct qmi_handle *handle,
+	void (*ind_cb)(struct qmi_handle *handle,
+		       unsigned int msg_id, void *msg,
+		       unsigned int msg_len, void *ind_cb_priv),
+	void *ind_cb_priv)
+{
+	if (!handle)
+		return -EINVAL;
+
+	mutex_lock(&handle->handle_lock);
+	if (handle->handle_reset) {
+		mutex_unlock(&handle->handle_lock);
+		return -ENETRESET;
+	}
+
+	handle->ind_cb = ind_cb;
+	handle->ind_cb_priv = ind_cb_priv;
+	mutex_unlock(&handle->handle_lock);
+	return 0;
+}
+EXPORT_SYMBOL(qmi_register_ind_cb);
+
+static int qmi_encode_and_send_req(struct qmi_txn **ret_txn_handle,
+	struct qmi_handle *handle, enum txn_type type,
+	struct msg_desc *req_desc, void *req, unsigned int req_len,
+	struct msg_desc *resp_desc, void *resp, unsigned int resp_len,
+	void (*resp_cb)(struct qmi_handle *handle,
+			unsigned int msg_id, void *msg,
+			void *resp_cb_data),
+	void *resp_cb_data)
+{
+	struct qmi_txn *txn_handle;
+	int rc, encoded_req_len;
+	void *encoded_req;
+
+	if (!handle || !handle->dest_info ||
+	    !req_desc || !req || !resp_desc || !resp)
+		return -EINVAL;
+
+	mutex_lock(&handle->handle_lock);
+	if (handle->handle_reset) {
+		mutex_unlock(&handle->handle_lock);
+		return -ENETRESET;
+	}
+
+	/* Allocate Transaction Info */
+	txn_handle = kzalloc(sizeof(struct qmi_txn), GFP_KERNEL);
+	if (!txn_handle) {
+		pr_err("%s: Failed to allocate txn handle\n", __func__);
+		mutex_unlock(&handle->handle_lock);
+		return -ENOMEM;
+	}
+	txn_handle->type = type;
+	INIT_LIST_HEAD(&txn_handle->list);
+	init_waitqueue_head(&txn_handle->wait_q);
+
+	/* Cache the parameters passed & mark it as sync*/
+	txn_handle->handle = handle;
+	txn_handle->resp_desc = resp_desc;
+	txn_handle->resp = resp;
+	txn_handle->resp_len = resp_len;
+	txn_handle->resp_received = 0;
+	txn_handle->resp_cb = resp_cb;
+	txn_handle->resp_cb_data = resp_cb_data;
+
+	/* Encode the request msg */
+	encoded_req_len = req_desc->max_msg_len + QMI_HEADER_SIZE;
+	encoded_req = kmalloc(encoded_req_len, GFP_KERNEL);
+	if (!encoded_req) {
+		pr_err("%s: Failed to allocate req_msg_buf\n", __func__);
+		rc = -ENOMEM;
+		goto encode_and_send_req_err1;
+	}
+	rc = qmi_kernel_encode(req_desc,
+		(void *)(encoded_req + QMI_HEADER_SIZE),
+		req_desc->max_msg_len, req);
+	if (rc < 0) {
+		pr_err("%s: Encode Failure %d\n", __func__, rc);
+		goto encode_and_send_req_err2;
+	}
+	encoded_req_len = rc;
+
+	/* Encode the header & Add to the txn_list */
+	if (!handle->next_txn_id)
+		handle->next_txn_id++;
+	txn_handle->txn_id = handle->next_txn_id++;
+	encode_qmi_header(encoded_req, QMI_REQUEST_CONTROL_FLAG,
+			  txn_handle->txn_id, req_desc->msg_id,
+			  encoded_req_len);
+	encoded_req_len += QMI_HEADER_SIZE;
+	list_add_tail(&txn_handle->list, &handle->txn_list);
+
+	/* Send the request */
+	rc = msm_ipc_router_send_msg((struct msm_ipc_port *)(handle->src_port),
+		(struct msm_ipc_addr *)handle->dest_info,
+		encoded_req, encoded_req_len);
+	if (rc < 0) {
+		pr_err("%s: send_msg failed %d\n", __func__, rc);
+		goto encode_and_send_req_err3;
+	}
+	mutex_unlock(&handle->handle_lock);
+
+	kfree(encoded_req);
+	if (ret_txn_handle)
+		*ret_txn_handle = txn_handle;
+	return 0;
+
+encode_and_send_req_err3:
+	list_del(&txn_handle->list);
+encode_and_send_req_err2:
+	kfree(encoded_req);
+encode_and_send_req_err1:
+	kfree(txn_handle);
+	mutex_unlock(&handle->handle_lock);
+	return rc;
+}
+
+int qmi_send_req_wait(struct qmi_handle *handle,
+		      struct msg_desc *req_desc,
+		      void *req, unsigned int req_len,
+		      struct msg_desc *resp_desc,
+		      void *resp, unsigned int resp_len,
+		      unsigned long timeout_ms)
+{
+	struct qmi_txn *txn_handle = NULL;
+	int rc;
+
+	/* Encode and send the request */
+	rc = qmi_encode_and_send_req(&txn_handle, handle, QMI_SYNC_TXN,
+				     req_desc, req, req_len,
+				     resp_desc, resp, resp_len,
+				     NULL, NULL);
+	if (rc < 0) {
+		pr_err("%s: Error encode & send req: %d\n", __func__, rc);
+		return rc;
+	}
+
+	/* Wait for the response */
+	if (!timeout_ms) {
+		rc = wait_event_interruptible(txn_handle->wait_q,
+					(txn_handle->resp_received ||
+					 handle->handle_reset));
+	} else {
+		rc = wait_event_interruptible_timeout(txn_handle->wait_q,
+					(txn_handle->resp_received ||
+					 handle->handle_reset),
+					msecs_to_jiffies(timeout_ms));
+		if (rc == 0)
+			rc = -ETIMEDOUT;
+	}
+
+	mutex_lock(&handle->handle_lock);
+	if (!txn_handle->resp_received) {
+		pr_err("%s: Response Wait Error %d\n", __func__, rc);
+		if (handle->handle_reset)
+			rc = -ENETRESET;
+		if (rc >= 0)
+			rc = -EFAULT;
+		goto send_req_wait_err;
+	}
+	rc = 0;
+
+send_req_wait_err:
+	list_del(&txn_handle->list);
+	kfree(txn_handle);
+	mutex_unlock(&handle->handle_lock);
+	wake_up(&handle->reset_waitq);
+	return rc;
+}
+EXPORT_SYMBOL(qmi_send_req_wait);
+
+int qmi_send_req_nowait(struct qmi_handle *handle,
+			struct msg_desc *req_desc,
+			void *req, unsigned int req_len,
+			struct msg_desc *resp_desc,
+			void *resp, unsigned int resp_len,
+			void (*resp_cb)(struct qmi_handle *handle,
+					unsigned int msg_id, void *msg,
+					void *resp_cb_data),
+			void *resp_cb_data)
+{
+	return qmi_encode_and_send_req(NULL, handle, QMI_ASYNC_TXN,
+				       req_desc, req, req_len,
+				       resp_desc, resp, resp_len,
+				       resp_cb, resp_cb_data);
+}
+EXPORT_SYMBOL(qmi_send_req_nowait);
+
+static struct qmi_txn *find_txn_handle(struct qmi_handle *handle,
+				       uint16_t txn_id)
+{
+	struct qmi_txn *txn_handle;
+
+	list_for_each_entry(txn_handle, &handle->txn_list, list) {
+		if (txn_handle->txn_id == txn_id)
+			return txn_handle;
+	}
+	return NULL;
+}
+
+static int handle_qmi_response(struct qmi_handle *handle,
+			       unsigned char *resp_msg, uint16_t txn_id,
+			       uint16_t msg_id, uint16_t msg_len)
+{
+	struct qmi_txn *txn_handle;
+	int rc;
+
+	/* Find the transaction handle */
+	txn_handle = find_txn_handle(handle, txn_id);
+	if (!txn_handle) {
+		pr_err("%s Response received for non-existent txn_id %d\n",
+			__func__, txn_id);
+		return -EINVAL;
+	}
+
+	/* Decode the message */
+	rc = qmi_kernel_decode(txn_handle->resp_desc, txn_handle->resp,
+			       (void *)(resp_msg + QMI_HEADER_SIZE), msg_len);
+	if (rc < 0) {
+		pr_err("%s: Response Decode Failure <%d: %d: %d> rc: %d\n",
+			__func__, txn_id, msg_id, msg_len, rc);
+		wake_up(&txn_handle->wait_q);
+		if (txn_handle->type == QMI_ASYNC_TXN) {
+			list_del(&txn_handle->list);
+			kfree(txn_handle);
+		}
+		return rc;
+	}
+
+	/* Handle async or sync resp */
+	switch (txn_handle->type) {
+	case QMI_SYNC_TXN:
+		txn_handle->resp_received = 1;
+		wake_up(&txn_handle->wait_q);
+		rc = 0;
+		break;
+
+	case QMI_ASYNC_TXN:
+		if (txn_handle->resp_cb)
+			txn_handle->resp_cb(txn_handle->handle, msg_id,
+					    txn_handle->resp,
+					    txn_handle->resp_cb_data);
+		list_del(&txn_handle->list);
+		kfree(txn_handle);
+		rc = 0;
+		break;
+
+	default:
+		pr_err("%s: Unrecognized transaction type\n", __func__);
+		return -EFAULT;
+	}
+	return rc;
+}
+
+static int handle_qmi_indication(struct qmi_handle *handle, void *msg,
+				 unsigned int msg_id, unsigned int msg_len)
+{
+	if (handle->ind_cb)
+		handle->ind_cb(handle, msg_id, msg,
+				msg_len, handle->ind_cb_priv);
+	return 0;
+}
+
+int qmi_recv_msg(struct qmi_handle *handle)
+{
+	unsigned int recv_msg_len;
+	unsigned char *recv_msg = NULL;
+	struct msm_ipc_addr src_addr;
+	unsigned char cntl_flag;
+	uint16_t txn_id, msg_id, msg_len;
+	int rc;
+
+	if (!handle)
+		return -EINVAL;
+
+	mutex_lock(&handle->handle_lock);
+	if (handle->handle_reset) {
+		mutex_unlock(&handle->handle_lock);
+		return -ENETRESET;
+	}
+
+	/* Read the messages */
+	rc = msm_ipc_router_read_msg((struct msm_ipc_port *)(handle->src_port),
+				     &src_addr, &recv_msg, &recv_msg_len);
+	if (rc < 0) {
+		pr_err("%s: Read failed %d\n", __func__, rc);
+		mutex_unlock(&handle->handle_lock);
+		return rc;
+	}
+
+	/* Decode the header & Handle the req, resp, indication message */
+	decode_qmi_header(recv_msg, &cntl_flag, &txn_id, &msg_id, &msg_len);
+
+	switch (cntl_flag) {
+	case QMI_RESPONSE_CONTROL_FLAG:
+		rc = handle_qmi_response(handle, recv_msg,
+					 txn_id, msg_id, msg_len);
+		break;
+
+	case QMI_INDICATION_CONTROL_FLAG:
+		rc = handle_qmi_indication(handle, recv_msg, msg_id, msg_len);
+		break;
+
+	default:
+		rc = -EFAULT;
+		pr_err("%s: Unsupported message type %d\n",
+			__func__, cntl_flag);
+		break;
+	}
+	kfree(recv_msg);
+	mutex_unlock(&handle->handle_lock);
+	return rc;
+}
+EXPORT_SYMBOL(qmi_recv_msg);
+
+int qmi_connect_to_service(struct qmi_handle *handle,
+			   uint32_t service_id, uint32_t instance_id)
+{
+	struct msm_ipc_port_name svc_name;
+	struct msm_ipc_server_info svc_info;
+	struct msm_ipc_addr *svc_dest_addr;
+	int rc;
+
+	if (!handle)
+		return -EINVAL;
+
+	svc_dest_addr = kzalloc(sizeof(struct msm_ipc_addr),
+				GFP_KERNEL);
+	if (!svc_dest_addr) {
+		pr_err("%s: Failure allocating memory\n", __func__);
+		return -ENOMEM;
+	}
+
+	svc_name.service = service_id;
+	svc_name.instance = instance_id;
+
+	rc = msm_ipc_router_lookup_server_name(&svc_name, &svc_info, 1, 0xFF);
+	if (rc <= 0) {
+		pr_err("%s: Server not found\n", __func__);
+		return -ENODEV;
+	}
+	svc_dest_addr->addrtype = MSM_IPC_ADDR_ID;
+	svc_dest_addr->addr.port_addr.node_id = svc_info.node_id;
+	svc_dest_addr->addr.port_addr.port_id = svc_info.port_id;
+	mutex_lock(&handle->handle_lock);
+	if (handle->handle_reset) {
+		mutex_unlock(&handle->handle_lock);
+		return -ENETRESET;
+	}
+	handle->dest_info = svc_dest_addr;
+	mutex_unlock(&handle->handle_lock);
+
+	return 0;
+}
+EXPORT_SYMBOL(qmi_connect_to_service);
+
+static struct svc_event_nb *find_svc_event_nb_by_name(const char *name)
+{
+	struct svc_event_nb *temp;
+
+	list_for_each_entry(temp, &svc_event_nb_list, list) {
+		if (!strncmp(name, temp->pdriver_name,
+			     sizeof(temp->pdriver_name)))
+			return temp;
+	}
+	return NULL;
+}
+
+static int qmi_svc_event_probe(struct platform_device *pdev)
+{
+	struct svc_event_nb *temp;
+	unsigned long flags;
+
+	mutex_lock(&svc_event_nb_list_lock);
+	temp = find_svc_event_nb_by_name(pdev->name);
+	if (!temp) {
+		mutex_unlock(&svc_event_nb_list_lock);
+		return -EINVAL;
+	}
+
+	spin_lock_irqsave(&temp->nb_lock, flags);
+	temp->svc_avail = 1;
+	raw_notifier_call_chain(&temp->svc_event_rcvr_list,
+				QMI_SERVER_ARRIVE, NULL);
+	spin_unlock_irqrestore(&temp->nb_lock, flags);
+	mutex_unlock(&svc_event_nb_list_lock);
+	return 0;
+}
+
+static int qmi_svc_event_remove(struct platform_device *pdev)
+{
+	struct svc_event_nb *temp;
+	unsigned long flags;
+
+	mutex_lock(&svc_event_nb_list_lock);
+	temp = find_svc_event_nb_by_name(pdev->name);
+	if (!temp) {
+		mutex_unlock(&svc_event_nb_list_lock);
+		return -EINVAL;
+	}
+
+	spin_lock_irqsave(&temp->nb_lock, flags);
+	temp->svc_avail = 0;
+	raw_notifier_call_chain(&temp->svc_event_rcvr_list,
+				QMI_SERVER_EXIT, NULL);
+	spin_unlock_irqrestore(&temp->nb_lock, flags);
+	mutex_unlock(&svc_event_nb_list_lock);
+	return 0;
+}
+
+static struct svc_event_nb *find_svc_event_nb(uint32_t service_id,
+					      uint32_t instance_id)
+{
+	struct svc_event_nb *temp;
+
+	list_for_each_entry(temp, &svc_event_nb_list, list) {
+		if (temp->service_id == service_id &&
+		    temp->instance_id == instance_id)
+			return temp;
+	}
+	return NULL;
+}
+
+static struct svc_event_nb *find_and_add_svc_event_nb(uint32_t service_id,
+						      uint32_t instance_id)
+{
+	struct svc_event_nb *temp;
+	int ret;
+
+	mutex_lock(&svc_event_nb_list_lock);
+	temp = find_svc_event_nb(service_id, instance_id);
+	if (temp) {
+		mutex_unlock(&svc_event_nb_list_lock);
+		return temp;
+	}
+
+	temp = kzalloc(sizeof(struct svc_event_nb), GFP_KERNEL);
+	if (!temp) {
+		mutex_unlock(&svc_event_nb_list_lock);
+		pr_err("%s: Failed to alloc notifier block\n", __func__);
+		return temp;
+	}
+
+	spin_lock_init(&temp->nb_lock);
+	temp->service_id = service_id;
+	temp->instance_id = instance_id;
+	INIT_LIST_HEAD(&temp->list);
+	temp->svc_driver.probe = qmi_svc_event_probe;
+	temp->svc_driver.remove = qmi_svc_event_remove;
+	scnprintf(temp->pdriver_name, sizeof(temp->pdriver_name),
+		  "QMI%08x:%08x", service_id, instance_id);
+	temp->svc_driver.driver.name = temp->pdriver_name;
+	RAW_INIT_NOTIFIER_HEAD(&temp->svc_event_rcvr_list);
+
+	list_add_tail(&temp->list, &svc_event_nb_list);
+	mutex_unlock(&svc_event_nb_list_lock);
+
+	ret = platform_driver_register(&temp->svc_driver);
+	if (ret < 0) {
+		pr_err("%s: Failed pdriver register\n", __func__);
+		mutex_lock(&svc_event_nb_list_lock);
+		list_del(&temp->list);
+		mutex_unlock(&svc_event_nb_list_lock);
+		kfree(temp);
+		temp = NULL;
+	}
+
+	return temp;
+}
+
+int qmi_svc_event_notifier_register(uint32_t service_id,
+				    uint32_t instance_id,
+				    struct notifier_block *nb)
+{
+	struct svc_event_nb *temp;
+	unsigned long flags;
+	int ret;
+
+	temp = find_and_add_svc_event_nb(service_id, instance_id);
+	if (!temp)
+		return -EFAULT;
+
+	mutex_lock(&svc_event_nb_list_lock);
+	temp = find_svc_event_nb(service_id, instance_id);
+	if (!temp) {
+		mutex_unlock(&svc_event_nb_list_lock);
+		return -EFAULT;
+	}
+	spin_lock_irqsave(&temp->nb_lock, flags);
+	if (temp->svc_avail)
+		nb->notifier_call(nb, QMI_SERVER_ARRIVE, NULL);
+
+	ret = raw_notifier_chain_register(&temp->svc_event_rcvr_list, nb);
+	spin_unlock_irqrestore(&temp->nb_lock, flags);
+	mutex_unlock(&svc_event_nb_list_lock);
+
+	return ret;
+}
+EXPORT_SYMBOL(qmi_svc_event_notifier_register);
+
+int qmi_svc_event_notifier_unregister(uint32_t service_id,
+				      uint32_t instance_id,
+				      struct notifier_block *nb)
+{
+	int ret;
+	struct svc_event_nb *temp;
+	unsigned long flags;
+
+	mutex_lock(&svc_event_nb_list_lock);
+	temp = find_svc_event_nb(service_id, instance_id);
+	if (!temp) {
+		mutex_unlock(&svc_event_nb_list_lock);
+		return -EINVAL;
+	}
+
+	spin_lock_irqsave(&temp->nb_lock, flags);
+	ret = raw_notifier_chain_unregister(&temp->svc_event_rcvr_list, nb);
+	spin_unlock_irqrestore(&temp->nb_lock, flags);
+	mutex_unlock(&svc_event_nb_list_lock);
+
+	return ret;
+}
+EXPORT_SYMBOL(qmi_svc_event_notifier_unregister);
+
+MODULE_DESCRIPTION("MSM QMI Interface");
+MODULE_LICENSE("GPL v2");
diff --git a/arch/arm/mach-msm/msm_qmi_interface_priv.h b/arch/arm/mach-msm/msm_qmi_interface_priv.h
new file mode 100644
index 0000000..58f1ce3
--- /dev/null
+++ b/arch/arm/mach-msm/msm_qmi_interface_priv.h
@@ -0,0 +1,58 @@
+/* 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_QMI_INTERFACE_PRIV_H_
+#define _MSM_QMI_INTERFACE_PRIV_H_
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/mm.h>
+#include <linux/list.h>
+#include <linux/socket.h>
+#include <linux/gfp.h>
+#include <linux/platform_device.h>
+#include <linux/qmi_encdec.h>
+
+#include <mach/msm_qmi_interface.h>
+
+enum txn_type {
+	QMI_SYNC_TXN = 1,
+	QMI_ASYNC_TXN,
+};
+
+struct qmi_txn {
+	struct list_head list;
+	uint16_t txn_id;
+	enum txn_type type;
+	struct qmi_handle *handle;
+	struct msg_desc *resp_desc;
+	void *resp;
+	unsigned int resp_len;
+	int resp_received;
+	void (*resp_cb)(struct qmi_handle *handle, unsigned int msg_id,
+			void *msg, void *resp_cb_data);
+	void *resp_cb_data;
+	wait_queue_head_t wait_q;
+};
+
+struct svc_event_nb {
+	spinlock_t nb_lock;
+	uint32_t service_id;
+	uint32_t instance_id;
+	char pdriver_name[32];
+	int svc_avail;
+	struct platform_driver svc_driver;
+	struct raw_notifier_head svc_event_rcvr_list;
+	struct list_head list;
+};
+
+#endif
diff --git a/arch/arm/mach-msm/msm_smem_iface.c b/arch/arm/mach-msm/msm_smem_iface.c
index b35467b..5ae5772 100644
--- a/arch/arm/mach-msm/msm_smem_iface.c
+++ b/arch/arm/mach-msm/msm_smem_iface.c
@@ -42,4 +42,5 @@
 	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 a6d6714..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
@@ -39,6 +38,7 @@
 	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 {
@@ -49,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[24];
+	uint8_t PAD[23];
 };
 
 void msm_smem_get_cpr_info(struct cpr_info_type *cpr_info);
diff --git a/arch/arm/mach-msm/no-pm.c b/arch/arm/mach-msm/no-pm.c
index d38b416..d460c70 100644
--- a/arch/arm/mach-msm/no-pm.c
+++ b/arch/arm/mach-msm/no-pm.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2010-2011, 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
@@ -12,13 +12,15 @@
  */
 
 #include <linux/module.h>
-
+#include <asm/proc-fns.h>
 #include <mach/cpuidle.h>
 #include "idle.h"
 #include "pm.h"
 
 void arch_idle(void)
-{ }
+{
+	cpu_do_idle();
+}
 
 void msm_pm_set_platform_data(struct msm_pm_platform_data *data, int count)
 { }
diff --git a/arch/arm/mach-msm/ocmem.c b/arch/arm/mach-msm/ocmem.c
index 7829d8d..34fd8d2 100644
--- a/arch/arm/mach-msm/ocmem.c
+++ b/arch/arm/mach-msm/ocmem.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
@@ -395,7 +395,8 @@
 	pr_debug("ocmem: Disabled br clock\n");
 }
 
-static struct ocmem_plat_data *parse_dt_config(struct platform_device *pdev)
+static struct ocmem_plat_data * __devinit parse_dt_config
+						(struct platform_device *pdev)
 {
 	struct device   *dev = &pdev->dev;
 	struct device_node *node = pdev->dev.of_node;
diff --git a/arch/arm/mach-msm/ocmem_api.c b/arch/arm/mach-msm/ocmem_api.c
index 689e015..8b56775 100644
--- a/arch/arm/mach-msm/ocmem_api.c
+++ b/arch/arm/mach-msm/ocmem_api.c
@@ -304,6 +304,7 @@
 
 	for (i = 0; i < list->num_chunks; i++) {
 		if (!chunks[i].ddr_paddr ||
+			!IS_ALIGNED(chunks[i].ddr_paddr, MIN_CHUNK_SIZE) ||
 			chunks[i].size < MIN_CHUNK_SIZE ||
 			!IS_ALIGNED(chunks[i].size, MIN_CHUNK_SIZE)) {
 			pr_err("Invalid ocmem chunk at index %d (p: %lx, size %lx)\n",
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 c380c54..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,
 };
 
@@ -74,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;
@@ -117,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},
@@ -128,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 {
@@ -154,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;
@@ -220,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;
@@ -241,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;
@@ -307,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;
 }
 
@@ -584,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);
 
@@ -602,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:
@@ -630,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:
@@ -1009,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:
 
@@ -1036,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;
@@ -1065,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;
@@ -1109,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;
@@ -1134,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;
@@ -1219,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);
 
@@ -1281,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;
 }
 
@@ -1298,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);
@@ -1346,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) {
@@ -1365,10 +1456,6 @@
 
 	}
 
-	rc = do_free(req);
-	if (rc < 0)
-		return -EINVAL;
-
 	inc_ocmem_stat(zone_of(req), NR_FREES);
 
 	ocmem_destroy_req(req);
@@ -1437,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) {
@@ -1457,6 +1536,7 @@
 		goto transfer_out_error;
 	}
 
+	inc_ocmem_stat(zone_of(req), NR_TRANSFERS_TO_DDR);
 	return 0;
 
 transfer_out_error:
@@ -1474,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) {
@@ -1525,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;
@@ -1541,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);
 	}
 
@@ -1567,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;
@@ -1667,6 +1984,7 @@
 	up_write(&req->rw_sem);
 	return 0;
 err_allocate_fail:
+	mutex_unlock(&allocation_mutex);
 	up_write(&req->rw_sem);
 	return -EINVAL;
 }
@@ -1699,33 +2017,6 @@
 	return -EINVAL;
 }
 
-int process_restore(int id)
-{
-	struct ocmem_req *req = NULL;
-	struct ocmem_req *next = NULL;
-	struct ocmem_eviction_data *edata = evictions[id];
-
-	if (!edata)
-		return 0;
-
-	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();
-	return 0;
-}
-
 int process_allocate(int id, struct ocmem_handle *handle,
 			unsigned long min, unsigned long max,
 			unsigned long step, bool can_block, bool can_wait)
@@ -1774,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);
 
@@ -1795,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);
@@ -1819,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);
 
@@ -1849,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);
@@ -1965,6 +2259,7 @@
 
 	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;
diff --git a/arch/arm/mach-msm/peripheral-loader.c b/arch/arm/mach-msm/peripheral-loader.c
index 4ff34bf..e3a3563 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,377 @@
 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);
+	priv->region = NULL;
+	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 +427,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 +440,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 +448,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 +459,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 +470,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 +664,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 dc80a3a..519e1c9 100644
--- a/arch/arm/mach-msm/pil-dsps.c
+++ b/arch/arm/mach-msm/pil-dsps.c
@@ -13,34 +13,31 @@
 #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 <mach/peripheral-loader.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 {
-	struct pil_device *pil;
+	void __iomem *base;
 	struct pil_desc desc;
 	struct subsys_device *subsys;
 	struct subsys_desc subsys_desc;
@@ -58,33 +55,41 @@
 };
 
 #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;
 }
 
@@ -163,6 +168,19 @@
 	}
 }
 
+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);
@@ -171,7 +189,7 @@
 		writel_relaxed(0, drv->ppss_base + PPSS_WDOG_UNMASKED_INT_EN);
 		mb(); /* Make sure wdog is disabled before shutting down */
 	}
-	pil_force_shutdown(drv->desc.name);
+	pil_shutdown(&drv->desc);
 	return 0;
 }
 
@@ -179,7 +197,7 @@
 {
 	struct dsps_data *drv = desc_to_drv(desc);
 
-	pil_force_boot(drv->desc.name);
+	pil_boot(&drv->desc);
 	atomic_set(&drv->crash_in_progress, 0);
 	enable_irq(drv->wdog_irq);
 
@@ -236,8 +254,8 @@
 {
 	struct dsps_data *drv;
 	struct pil_desc *desc;
-	int ret;
 	struct resource *res;
+	int ret;
 
 	drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL);
 	if (!drv)
@@ -245,6 +263,13 @@
 	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));
@@ -256,6 +281,7 @@
 	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");
@@ -263,9 +289,9 @@
 		desc->ops = &pil_dsps_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->fw_ramdump_segments[0].address = 0x12000000;
 	drv->fw_ramdump_segments[0].size = 0x28000;
@@ -275,7 +301,7 @@
 	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");
+	drv->ramdump_dev = create_ramdump_device("dsps", &pdev->dev);
 	if (!drv->ramdump_dev) {
 		ret = -ENOMEM;
 		goto err_ramdump;
@@ -283,13 +309,17 @@
 
 	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");
+	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,
@@ -329,7 +359,7 @@
 err_smem_ramdump:
 	destroy_ramdump_device(drv->ramdump_dev);
 err_ramdump:
-	msm_pil_unregister(drv->pil);
+	pil_desc_release(desc);
 	return ret;
 }
 
@@ -341,7 +371,7 @@
 	subsys_unregister(drv->subsys);
 	destroy_ramdump_device(drv->smem_ramdump_dev);
 	destroy_ramdump_device(drv->ramdump_dev);
-	msm_pil_unregister(drv->pil);
+	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 8432328..0000000
--- a/arch/arm/mach-msm/pil-mba.c
+++ /dev/null
@@ -1,453 +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 <linux/interrupt.h>
-
-#include <mach/subsystem_restart.h>
-#include <mach/msm_smsm.h>
-#include <mach/peripheral-loader.h>
-
-#include "peripheral-loader.h"
-#include "ramdump.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
-
-#define MAX_SSR_REASON_LEN 81U
-
-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 subsys_device *subsys;
-	struct subsys_desc subsys_desc;
-	struct clk *xo;
-	void *ramdump_dev;
-	void *smem_ramdump_dev;
-	bool crash_shutdown;
-	bool ignore_errors;
-	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,
-};
-
-#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)
-{
-	pil_force_shutdown("modem");
-	pil_force_shutdown("mba");
-	return 0;
-}
-
-static int modem_powerup(const struct subsys_desc *subsys)
-{
-	struct mba_data *drv = subsys_to_drv(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.
-	 */
-	drv->ignore_errors = 0;
-	pil_force_boot("mba");
-	pil_force_boot("modem");
-	return 0;
-}
-
-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;
-
-	pil_force_boot("mba");
-
-	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_force_shutdown("mba");
-	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 __devinit pil_mba_driver_probe(struct platform_device *pdev)
-{
-	struct mba_data *drv;
-	struct resource *res;
-	struct pil_desc *desc;
-	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;
-
-	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);
-
-	drv->subsys_desc.name = desc->name;
-	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->ramdump_dev = create_ramdump_device("modem");
-	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");
-	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)) {
-		goto err_subsys;
-		ret = PTR_ERR(drv->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:
-	msm_pil_unregister(drv->pil);
-	return ret;
-}
-
-static int __devexit pil_mba_driver_exit(struct platform_device *pdev)
-{
-	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);
-	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 5685787..162a7f7 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>
@@ -28,11 +27,11 @@
 #include <linux/wcnss_wlan.h>
 
 #include <mach/subsystem_restart.h>
-#include <mach/peripheral-loader.h>
 #include <mach/msm_smsm.h>
 
 #include "peripheral-loader.h"
 #include "scm-pas.h"
+#include "ramdump.h"
 
 #define PRONTO_PMU_COMMON_GDSCR				0x24
 #define PRONTO_PMU_COMMON_GDSCR_SW_COLLAPSE		BIT(0)
@@ -73,8 +72,8 @@
 	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;
@@ -83,6 +82,7 @@
 	bool crash;
 	struct delayed_work cancel_vote_work;
 	int irq;
+	struct ramdump_device *ramdump_dev;
 };
 
 static int pil_pronto_make_proxy_vote(struct pil_desc *pil)
@@ -114,22 +114,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);
@@ -232,15 +223,50 @@
 }
 
 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,
 };
 
+static int pil_pronto_init_image_trusted(struct pil_desc *pil,
+			const u8 *metadata, size_t size)
+{
+	return pas_init_image(PAS_WCNSS, metadata, size);
+}
+
+static int pil_pronto_reset_trusted(struct pil_desc *pil)
+{
+	return pas_auth_and_reset(PAS_WCNSS);
+}
+
+static int pil_pronto_shutdown_trusted(struct pil_desc *pil)
+{
+	return pas_shutdown(PAS_WCNSS);
+}
+
+static struct pil_reset_ops pil_pronto_ops_trusted = {
+	.init_image = pil_pronto_init_image_trusted,
+	.auth_and_reset = pil_pronto_reset_trusted,
+	.shutdown = pil_pronto_shutdown_trusted,
+	.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;
@@ -301,6 +327,7 @@
 		return IRQ_HANDLED;
 	}
 
+	disable_irq_nosync(drv->irq);
 	drv->restart_inprogress = true;
 	restart_wcnss(drv);
 
@@ -319,10 +346,9 @@
 {
 	struct pronto_data *drv = subsys_to_drv(subsys);
 
-	pil_force_shutdown("wcnss");
+	pil_shutdown(&drv->desc);
 	flush_delayed_work(&drv->cancel_vote_work);
 	wcnss_flush_delayed_boot_votes();
-	disable_irq_nosync(drv->irq);
 
 	return 0;
 }
@@ -339,7 +365,9 @@
 					WCNSS_WLAN_SWITCH_ON);
 	if (!ret) {
 		msleep(1000);
-		pil_force_boot("wcnss");
+		ret = pil_boot(&drv->desc);
+		if (ret)
+			return ret;
 	}
 	drv->restart_inprogress = false;
 	enable_irq(drv->irq);
@@ -357,9 +385,19 @@
 		smsm_change_state(SMSM_APPS_STATE, SMSM_RESET, SMSM_RESET);
 }
 
-static int wcnss_ramdump(int enable, const struct subsys_desc *crashed_subsys)
+static struct ramdump_segment pronto_segments[] = {
+	{ 0x0D200000, 0x0D980000 - 0x0D200000 }
+};
+
+static int wcnss_ramdump(int enable, const struct subsys_desc *subsys)
 {
-	return 0;
+	struct pronto_data *drv = subsys_to_drv(subsys);
+
+	if (enable)
+		return do_ramdump(drv->ramdump_dev, pronto_segments,
+				ARRAY_SIZE(pronto_segments));
+	else
+		return 0;
 }
 
 static int __devinit pil_pronto_probe(struct platform_device *pdev)
@@ -370,10 +408,6 @@
 	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;
@@ -383,28 +417,22 @@
 	if (drv->irq < 0)
 		return drv->irq;
 
-	drv->base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
+	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)
@@ -414,9 +442,13 @@
 	desc->owner = THIS_MODULE;
 	desc->proxy_timeout = 10000;
 
-	/* TODO: need to add secure boot when the support is available */
-	desc->ops = &pil_pronto_ops;
-	dev_info(&pdev->dev, "using non-secure boot\n");
+	if (pas_supported(PAS_WCNSS) > 0) {
+		desc->ops = &pil_pronto_ops_trusted;
+		dev_info(&pdev->dev, "using secure boot\n");
+	} else {
+		desc->ops = &pil_pronto_ops;
+		dev_info(&pdev->dev, "using non-secure boot\n");
+	}
 
 	drv->vreg = devm_regulator_get(&pdev->dev, "vdd_pronto_pll");
 	if (IS_ERR(drv->vreg)) {
@@ -440,9 +472,9 @@
 	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);
@@ -456,6 +488,8 @@
 	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);
 
@@ -470,6 +504,12 @@
 	if (ret < 0)
 		goto err_irq;
 
+	drv->ramdump_dev = create_ramdump_device("pronto", &pdev->dev);
+	if (!drv->ramdump_dev) {
+		ret = -ENOMEM;
+		goto err_irq;
+	}
+
 	/* Initialize common_ss GDSCR to wait 4 cycles between states */
 	regval = readl_relaxed(drv->base + PRONTO_PMU_COMMON_GDSCR)
 		& PRONTO_PMU_COMMON_GDSCR_SW_COLLAPSE;
@@ -484,7 +524,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;
 }
 
@@ -494,7 +534,8 @@
 	subsys_unregister(drv->subsys);
 	smsm_state_cb_deregister(SMSM_WCNSS_STATE, SMSM_RESET,
 					smsm_state_cb_hdlr, drv);
-	msm_pil_unregister(drv->pil);
+	pil_desc_release(&drv->desc);
+	destroy_ramdump_device(drv->ramdump_dev);
 	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
index a0432550..1e6c1f6 100644
--- a/arch/arm/mach-msm/pil-q6v4-lpass.c
+++ b/arch/arm/mach-msm/pil-q6v4-lpass.c
@@ -22,7 +22,6 @@
 #include <linux/delay.h>
 
 #include <mach/scm.h>
-#include <mach/peripheral-loader.h>
 #include <mach/subsystem_restart.h>
 #include <mach/subsystem_notif.h>
 
@@ -70,7 +69,6 @@
 }
 
 static struct pil_reset_ops pil_q6v4_lpass_ops = {
-	.init_image = pil_q6v4_init_image,
 	.auth_and_reset = pil_q6v4_lpass_boot,
 	.shutdown = pil_q6v4_lpass_shutdown,
 	.proxy_vote = pil_q6v4_make_proxy_votes,
@@ -192,13 +190,30 @@
 
 #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_force_shutdown("q6");
+		pil_shutdown(&drv->q6.desc);
 	disable_irq_nosync(drv->q6.wdog_irq);
 
 	return 0;
@@ -210,7 +225,7 @@
 	int ret = 0;
 
 	if (drv->loadable)
-		ret = pil_force_boot("q6");
+		ret = pil_boot(&drv->q6.desc);
 	enable_irq(drv->q6.wdog_irq);
 
 	return ret;
@@ -271,10 +286,7 @@
 	drv->loadable = !!pdata; /* No pdata = don't use PIL */
 	if (drv->loadable) {
 		res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-		if (!res)
-			return -EINVAL;
-		q6->base = devm_ioremap(&pdev->dev, res->start,
-				resource_size(res));
+		q6->base = devm_request_and_ioremap(&pdev->dev, res);
 		if (!q6->base)
 			return -ENOMEM;
 
@@ -300,12 +312,16 @@
 			dev_info(&pdev->dev, "using non-secure boot\n");
 		}
 
-		q6->pil = msm_pil_register(desc);
-		if (IS_ERR(q6->pil))
-			return PTR_ERR(q6->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_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;
@@ -313,7 +329,7 @@
 
 	INIT_WORK(&drv->work, lpass_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;
@@ -324,6 +340,8 @@
 		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);
@@ -359,7 +377,7 @@
 	destroy_ramdump_device(drv->ramdump_dev);
 err_ramdump:
 	if (drv->loadable)
-		msm_pil_unregister(q6->pil);
+		pil_desc_release(desc);
 	return ret;
 }
 
@@ -373,7 +391,7 @@
 	subsys_unregister(drv->subsys);
 	destroy_ramdump_device(drv->ramdump_dev);
 	if (drv->loadable)
-		msm_pil_unregister(drv->q6.pil);
+		pil_desc_release(&drv->q6.desc);
 	return 0;
 }
 
diff --git a/arch/arm/mach-msm/pil-q6v4-mss.c b/arch/arm/mach-msm/pil-q6v4-mss.c
index 61b5536..ee01f04 100644
--- a/arch/arm/mach-msm/pil-q6v4-mss.c
+++ b/arch/arm/mach-msm/pil-q6v4-mss.c
@@ -20,10 +20,8 @@
 #include <linux/clk.h>
 #include <linux/interrupt.h>
 
-#include <mach/msm_iomap.h>
 #include <mach/subsystem_restart.h>
 #include <mach/msm_smsm.h>
-#include <mach/peripheral-loader.h>
 
 #include "smd_private.h"
 #include "ramdump.h"
@@ -31,16 +29,17 @@
 #include "pil-q6v4.h"
 #include "scm-pas.h"
 
-#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 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;
@@ -48,27 +47,29 @@
 	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 *jtag_clk)
+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, 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);
+		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, MSS_RESET);
+		writel_relaxed(0x0, cbase + MSS_RESET);
 		mb();
 		udelay(10);
 		/* Enable MSS */
@@ -84,13 +85,13 @@
 }
 
 /* Put modem subsystem back into reset */
-static void pil_q6v4_shutdown_modem(void)
+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, MSS_RESET);
+		writel_relaxed(0x1, mdm->cbase + MSS_RESET);
 	mutex_unlock(&pil_q6v4_modem_lock);
 }
 
@@ -104,25 +105,25 @@
 	if (err)
 		return err;
 
-	pil_q6v4_init_modem(mdm->modem_base, drv->jtag_clk_reg);
+	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();
+	pil_q6v4_shutdown_modem(mdm);
 	pil_q6v4_power_down(drv);
 	return 0;
 }
 
 static struct pil_reset_ops pil_q6v4_modem_ops = {
-	.init_image = pil_q6v4_init_image,
 	.auth_and_reset = pil_q6v4_modem_boot,
 	.shutdown = pil_q6v4_modem_shutdown,
 	.proxy_vote = pil_q6v4_make_proxy_votes,
@@ -169,6 +170,31 @@
 
 #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);
@@ -179,8 +205,8 @@
 	mb();
 
 	if (drv->loadable) {
-		pil_force_shutdown("modem");
-		pil_force_shutdown("modem_fw");
+		pil_shutdown(&drv->q6_sw.desc);
+		pil_shutdown(&drv->q6_fw.desc);
 	}
 
 	disable_irq_nosync(drv->q6_fw.wdog_irq);
@@ -192,10 +218,17 @@
 static int modem_powerup(const struct subsys_desc *subsys)
 {
 	struct q6v4_modem *drv = desc_to_modem(subsys);
+	int ret;
 
 	if (drv->loadable) {
-		pil_force_boot("modem_fw");
-		pil_force_boot("modem");
+		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);
@@ -279,20 +312,13 @@
 	struct pil_desc *desc;
 	struct resource *res;
 
-	res = platform_get_resource(pdev, IORESOURCE_MEM, 1 + (i * 2));
-	if (!res)
-		return -EINVAL;
-
-	drv->base = devm_ioremap(&pdev->dev, res->start, resource_size(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, 2 + (i * 2));
-	if (!res)
-		return -EINVAL;
-
-	drv->wdog_base = devm_ioremap(&pdev->dev, res->start,
-			resource_size(res));
+	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;
 
@@ -307,7 +333,6 @@
 
 	desc = &drv->desc;
 	desc->name = pdata->name;
-	desc->depends_on = pdata->depends;
 	desc->dev = &pdev->dev;
 	desc->owner = THIS_MODULE;
 	desc->proxy_timeout = 10000;
@@ -376,44 +401,51 @@
 		}
 
 		res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-		if (!res)
-			return -EINVAL;
-
-		drv->modem_base = devm_ioremap(&pdev->dev, res->start,
-				resource_size(res));
+		drv->modem_base = devm_request_and_ioremap(&pdev->dev, res);
 		if (!drv->modem_base)
 			return -ENOMEM;
 
-		drv_fw->pil = msm_pil_register(&drv_fw->desc);
-		if (IS_ERR(drv_fw->pil))
-			return PTR_ERR(drv_fw->pil);
+		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;
 
-		drv_sw->pil = msm_pil_register(&drv_sw->desc);
-		if (IS_ERR(drv_sw->pil)) {
-			ret = PTR_ERR(drv_sw->pil);
+		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");
+	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");
+	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");
+	drv->smem_ramdump_dev = create_ramdump_device("smem-modem", &pdev->dev);
 	if (!drv->smem_ramdump_dev) {
 		ret = -ENOMEM;
 		goto err_smem_ramdump;
@@ -424,6 +456,8 @@
 		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,
@@ -453,9 +487,9 @@
 	destroy_ramdump_device(drv->fw_ramdump_dev);
 err_fw_ramdump:
 	if (drv->loadable)
-		msm_pil_unregister(drv_sw->pil);
+		pil_desc_release(&drv_sw->desc);
 err_pil_sw:
-	msm_pil_unregister(drv_fw->pil);
+	pil_desc_release(&drv_fw->desc);
 	return ret;
 }
 
@@ -470,8 +504,8 @@
 	destroy_ramdump_device(drv->sw_ramdump_dev);
 	destroy_ramdump_device(drv->fw_ramdump_dev);
 	if (drv->loadable) {
-		msm_pil_unregister(drv->q6_sw.pil);
-		msm_pil_unregister(drv->q6_fw.pil);
+		pil_desc_release(&drv->q6_sw.desc);
+		pil_desc_release(&drv->q6_fw.desc);
 	}
 	return 0;
 }
diff --git a/arch/arm/mach-msm/pil-q6v4.c b/arch/arm/mach-msm/pil-q6v4.c
index 47033fc..7f04c64 100644
--- a/arch/arm/mach-msm/pil-q6v4.c
+++ b/arch/arm/mach-msm/pil-q6v4.c
@@ -16,7 +16,6 @@
 #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>
@@ -52,16 +51,6 @@
 #define Q6SS_CLK_ENA		BIT(1)
 #define Q6SS_SRC_SWITCH_CLK_OVR	BIT(8)
 
-int pil_q6v4_init_image(struct pil_desc *pil, const u8 *metadata,
-		size_t size)
-{
-	const struct elf32_hdr *ehdr = (struct elf32_hdr *)metadata;
-	struct q6v4_data *drv = pil_to_q6v4_data(pil);
-	drv->start_addr = ehdr->e_entry;
-	return 0;
-}
-EXPORT_SYMBOL(pil_q6v4_init_image);
-
 int pil_q6v4_make_proxy_votes(struct pil_desc *pil)
 {
 	const struct q6v4_data *drv = pil_to_q6v4_data(pil);
@@ -141,6 +130,7 @@
 {
 	u32 reg, err;
 	const struct q6v4_data *drv = pil_to_q6v4_data(pil);
+	unsigned long start_addr = pil_get_entry_addr(pil);
 
 	/* Enable Q6 ACLK */
 	writel_relaxed(0x10, drv->aclk_reg);
@@ -156,7 +146,7 @@
 	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 */
diff --git a/arch/arm/mach-msm/pil-q6v4.h b/arch/arm/mach-msm/pil-q6v4.h
index 0395bed..86e55ea 100644
--- a/arch/arm/mach-msm/pil-q6v4.h
+++ b/arch/arm/mach-msm/pil-q6v4.h
@@ -21,7 +21,6 @@
 	void __iomem *aclk_reg;
 	void __iomem *jtag_clk_reg;
 	const char *name;
-	const char *depends;
 	const unsigned pas_id;
 	int bus_port;
 };
@@ -36,7 +35,6 @@
 struct q6v4_data {
 	void __iomem *base;
 	void __iomem *wdog_base;
-	unsigned long start_addr;
 	unsigned long strap_tcm_base;
 	unsigned long strap_ahb_upper;
 	unsigned long strap_ahb_lower;
@@ -51,14 +49,11 @@
 	bool vreg_enabled;
 	struct clk *xo;
 
-	struct pil_device *pil;
 	struct pil_desc desc;
 };
 
 #define pil_to_q6v4_data(p) container_of(p, struct q6v4_data, desc)
 
-extern int pil_q6v4_init_image(struct pil_desc *pil, const u8 *metadata,
-		size_t size);
 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);
diff --git a/arch/arm/mach-msm/pil-q6v5-lpass.c b/arch/arm/mach-msm/pil-q6v5-lpass.c
index c48ea02..662377d 100644
--- a/arch/arm/mach-msm/pil-q6v5-lpass.c
+++ b/arch/arm/mach-msm/pil-q6v5-lpass.c
@@ -26,7 +26,6 @@
 #include <mach/subsystem_restart.h>
 #include <mach/subsystem_notif.h>
 #include <mach/scm.h>
-#include <mach/peripheral-loader.h>
 
 #include "peripheral-loader.h"
 #include "pil-q6v5.h"
@@ -119,6 +118,7 @@
 static int pil_lpass_reset(struct pil_desc *pil)
 {
 	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);
@@ -126,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);
@@ -141,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,
@@ -286,7 +285,7 @@
 	send_q6_nmi();
 	/* The write needs to go through before the q6 is shutdown. */
 	mb();
-	pil_force_shutdown("adsp");
+	pil_shutdown(&drv->q6->desc);
 	disable_irq_nosync(drv->wdog_irq);
 
 	return 0;
@@ -302,7 +301,7 @@
 		msleep(10000);
 	}
 
-	ret = pil_force_boot("adsp");
+	ret = pil_boot(&drv->q6->desc);
 	enable_irq(drv->wdog_irq);
 
 	return ret;
@@ -337,6 +336,19 @@
 	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 lpass_data *drv;
@@ -386,9 +398,9 @@
 		dev_info(&pdev->dev, "using non-secure boot\n");
 	}
 
-	drv->q6->pil = msm_pil_register(desc);
-	if (IS_ERR(drv->q6->pil))
-		return PTR_ERR(drv->q6->pil);
+	ret = pil_desc_init(desc);
+	if (ret)
+		return ret;
 
 	drv->subsys_desc.name = desc->name;
 	drv->subsys_desc.owner = THIS_MODULE;
@@ -397,10 +409,12 @@
 	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");
+	drv->ramdump_dev = create_ramdump_device("adsp", &pdev->dev);
 	if (!drv->ramdump_dev) {
 		ret = -ENOMEM;
 		goto err_ramdump;
@@ -445,7 +459,7 @@
 err_subsys:
 	destroy_ramdump_device(drv->ramdump_dev);
 err_ramdump:
-	msm_pil_unregister(drv->q6->pil);
+	pil_desc_release(desc);
 	return 0;
 }
 
@@ -458,7 +472,7 @@
 			adsp_smsm_state_cb, drv);
 	subsys_unregister(drv->subsys);
 	destroy_ramdump_device(drv->ramdump_dev);
-	msm_pil_unregister(drv->q6->pil);
+	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 f3c731f..07cbe19 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,22 @@
 #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"
+#include "sysmon.h"
 
 /* Q6 Register Offsets */
 #define QDSP6SS_RST_EVB			0x010
@@ -38,9 +42,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,14 +50,48 @@
 /* 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;
+	void *adsp_state_notifier;
+	u32 img_length;
+	struct q6v5_data *q6;
+	int self_auth;
+	void *ramdump_dev;
+	void *smem_ramdump_dev;
+	bool crash_shutdown;
+	bool ignore_errors;
+	int is_loadable;
+};
+
 static int pbl_mba_boot_timeout_ms = 100;
 module_param(pbl_mba_boot_timeout_ms, int, S_IRUGO | S_IWUSR);
 
+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;
@@ -108,11 +143,12 @@
 static int wait_for_mba_ready(struct q6v5_data *drv)
 {
 	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");
@@ -124,7 +160,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");
@@ -171,6 +207,9 @@
 static int pil_mss_reset(struct pil_desc *pil)
 {
 	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,24 +230,21 @@
 		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) {
+	if (mba->self_auth) {
 		ret = wait_for_mba_ready(drv);
 		if (ret)
 			goto err_auth;
@@ -229,90 +265,494 @@
 }
 
 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;
+	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;
+
+	/* Copy metadata to assigned shared buffer location */
+	memcpy(drv->metadata_base, metadata, size);
+
+	/* 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);
+
+	if (!drv->is_loadable)
+		return 0;
+	/* 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;
+
+	if (!drv->is_loadable)
+		return 0;
+	/*
+	 * 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 int adsp_state_notifier_fn(struct notifier_block *this,
+				unsigned long code, void *ss_handle)
+{
+	int ret;
+	ret = sysmon_send_event(SYSMON_SS_MODEM, "adsp", code);
+	if (ret < 0)
+		pr_err("%s: sysmon_send_event failed (%d).", __func__, ret);
+	return NOTIFY_DONE;
+}
+
+static struct notifier_block adsp_state_notifier_block = {
+	.notifier_call = adsp_state_notifier_fn,
+};
+
+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);
+
+	if (!drv->is_loadable)
+		return 0;
+
+	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);
+
+	if (!drv->is_loadable)
+		return;
+
+	/* MBA doesn't support shutdown */
+	pil_shutdown(&drv->q6->desc);
+}
+
+static int __devinit pil_subsys_init(struct mba_data *drv,
+					struct platform_device *pdev)
+{
+	int irq, ret;
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0)
+		return irq;
+
+	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->subsys = subsys_register(&drv->subsys_desc);
+	if (IS_ERR(drv->subsys)) {
+		ret = PTR_ERR(drv->subsys);
+		goto err_subsys;
+	}
+
+	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;
+	}
+
+	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;
+	}
+
+	drv->adsp_state_notifier = subsys_notif_register_notifier("adsp",
+						&adsp_state_notifier_block);
+	if (IS_ERR(drv->adsp_state_notifier)) {
+		ret = PTR_ERR(drv->adsp_state_notifier);
+		dev_err(&pdev->dev, "%s: Registration with the SSR notification driver failed (%d)",
+			__func__, ret);
+		goto err_smsm;
+	}
+
+	return 0;
+
+err_smsm:
+	smsm_state_cb_deregister(SMSM_MODEM_STATE, SMSM_RESET, smsm_state_cb,
+			drv);
+err_irq:
+	destroy_ramdump_device(drv->smem_ramdump_dev);
+err_ramdump_smem:
+	destroy_ramdump_device(drv->ramdump_dev);
+err_ramdump:
+	subsys_unregister(drv->subsys);
+err_subsys:
+	return ret;
+}
+
+static int __devinit pil_mss_loadable_init(struct mba_data *drv,
+					struct platform_device *pdev)
+{
+	struct q6v5_data *q6;
+	struct pil_desc *q6_desc, *mba_desc;
 	struct resource *res;
 	int ret;
 
-	drv = pil_q6v5_init(pdev);
-	if (IS_ERR(drv))
-		return PTR_ERR(drv);
-	platform_set_drvdata(pdev, drv);
+	q6 = pil_q6v5_init(pdev);
+	if (IS_ERR(q6))
+		return PTR_ERR(q6);
+	drv->q6 = q6;
 
-	desc = &drv->desc;
-	desc->ops = &pil_mss_ops;
-	desc->owner = THIS_MODULE;
-	desc->proxy_timeout = PROXY_TIMEOUT_MS;
+	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;
 
 	return 0;
+
+err_mba_desc:
+	pil_desc_release(q6_desc);
+	return ret;
+
+}
+
+static int __devinit pil_mss_driver_probe(struct platform_device *pdev)
+{
+	struct mba_data *drv;
+	int ret;
+
+	drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL);
+	if (!drv)
+		return -ENOMEM;
+	platform_set_drvdata(pdev, drv);
+
+	drv->is_loadable = of_property_read_bool(pdev->dev.of_node,
+							"qcom,is-loadable");
+	if (drv->is_loadable) {
+		ret = pil_mss_loadable_init(drv, pdev);
+		if (ret)
+			return ret;
+	}
+
+	return pil_subsys_init(drv, pdev);
 }
 
 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);
+
+	subsys_notif_unregister_notifier(drv->adsp_state_notifier,
+						&adsp_state_notifier_block);
+	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 70a12de..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>
@@ -100,16 +99,6 @@
 }
 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 = container_of(pil, struct q6v5_data, desc);
-	drv->start_addr = ehdr->e_entry;
-	return 0;
-}
-EXPORT_SYMBOL(pil_q6v5_init_image);
-
 void pil_q6v5_shutdown(struct pil_desc *pil)
 {
 	u32 val;
@@ -205,12 +194,10 @@
 		return ERR_PTR(-ENOMEM);
 
 	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));
diff --git a/arch/arm/mach-msm/pil-q6v5.h b/arch/arm/mach-msm/pil-q6v5.h
index f176d2d..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
@@ -29,22 +29,15 @@
 	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 q6v5_data *pil_q6v5_init(struct platform_device *pdev);
diff --git a/arch/arm/mach-msm/pil-riva.c b/arch/arm/mach-msm/pil-riva.c
index dbb4408..74fae98 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;
@@ -535,7 +542,7 @@
 	}
 
 	ret = devm_request_irq(&pdev->dev, drv->irq, riva_wdog_bite_irq_hdlr,
-			IRQF_TRIGGER_HIGH, "riva_wdog", drv);
+			IRQF_TRIGGER_RISING, "riva_wdog", drv);
 	if (ret < 0)
 		goto err;
 
@@ -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..103fd9f 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,9 @@
 
 #include <mach/iommu.h>
 #include <mach/iommu_domains.h>
+#include <mach/subsystem_restart.h>
+#include <mach/msm_bus_board.h>
+#include <mach/msm_bus.h>
 
 #include "peripheral-loader.h"
 #include "scm-pas.h"
@@ -65,9 +67,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;
@@ -76,8 +79,11 @@
 	u32 fw_sz;
 	u32 fw_min_paddr;
 	u32 fw_max_paddr;
+	u32 bus_perf_client;
 };
 
+#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 = {
@@ -144,6 +150,41 @@
 		clk_disable_unprepare(drv->clks[i]);
 }
 
+static struct msm_bus_vectors pil_venus_unvote_bw_vector[] = {
+	{
+		.src = MSM_BUS_MASTER_VIDEO_P0,
+		.dst = MSM_BUS_SLAVE_EBI_CH0,
+		.ab = 0,
+		.ib = 0,
+	},
+};
+
+static struct msm_bus_vectors pil_venus_vote_bw_vector[] = {
+	{
+		.src = MSM_BUS_MASTER_VIDEO_P0,
+		.dst = MSM_BUS_SLAVE_EBI_CH0,
+		.ab = 0,
+		.ib = 16 * 19 * 1000000UL, /* At least 19.2MHz on bus. */
+	},
+};
+
+static struct msm_bus_paths pil_venus_bw_tbl[] = {
+	{
+		.num_paths = ARRAY_SIZE(pil_venus_unvote_bw_vector),
+		.vectors = pil_venus_unvote_bw_vector,
+	},
+	{
+		.num_paths = ARRAY_SIZE(pil_venus_vote_bw_vector),
+		.vectors = pil_venus_vote_bw_vector,
+	},
+};
+
+static struct msm_bus_scale_pdata pil_venus_client_pdata = {
+	.usecase = pil_venus_bw_tbl,
+	.num_usecases = ARRAY_SIZE(pil_venus_bw_tbl),
+	.name = "pil-venus",
+};
+
 static int pil_venus_make_proxy_vote(struct pil_desc *pil)
 {
 	struct venus_data *drv = dev_get_drvdata(pil->dev);
@@ -158,13 +199,28 @@
 	rc = regulator_enable(drv->gdsc);
 	if (rc) {
 		dev_err(pil->dev, "GDSC enable failed\n");
-		return rc;
+		goto err_regulator;
 	}
 
 	rc = venus_clock_prepare_enable(pil->dev);
-	if (rc)
-		regulator_disable(drv->gdsc);
+	if (rc) {
+		dev_err(pil->dev, "clock prepare and enable failed\n");
+		goto err_clock;
+	}
 
+	rc = msm_bus_scale_client_update_request(drv->bus_perf_client, 1);
+	if (rc) {
+		dev_err(pil->dev, "bandwith request failed\n");
+		goto err_bw;
+	}
+
+	return 0;
+
+err_bw:
+	venus_clock_disable_unprepare(pil->dev);
+err_clock:
+	regulator_disable(drv->gdsc);
+err_regulator:
 	return rc;
 }
 
@@ -172,28 +228,37 @@
 {
 	struct venus_data *drv = dev_get_drvdata(pil->dev);
 
+	msm_bus_scale_client_update_request(drv->bus_perf_client, 0);
+
 	venus_clock_disable_unprepare(pil->dev);
 
 	/* Disable GDSC */
 	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 +267,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 +386,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 +446,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 +466,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;
 
@@ -422,51 +493,20 @@
 	if (rc)
 		return rc;
 
+	drv->bus_perf_client =
+			msm_bus_scale_register_client(&pil_venus_client_pdata);
+	if (!drv->bus_perf_client) {
+		dev_err(&pdev->dev, "Failed to register bus client\n");
+		return -EINVAL;
+	}
+
 	drv->iommu_fw_ctx  = msm_iommu_get_ctx("venus_fw");
 	if (!drv->iommu_fw_ctx) {
 		dev_err(&pdev->dev, "No iommu fw context found\n");
 		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 +524,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 +546,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 3b31b9f..0e75cae 100644
--- a/arch/arm/mach-msm/platsmp-8625.c
+++ b/arch/arm/mach-msm/platsmp-8625.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
@@ -121,10 +121,10 @@
 	write_pen_release(-1);
 
 	/* clear the IPC pending SPI */
-	if (power_collapsed) {
+	if (per_cpu(power_collapsed, cpu)) {
 		raise_clear_spi(cpu, false);
 		clear_pending_spi(cpu_data[cpu].ipc_irq);
-		power_collapsed = 0;
+		per_cpu(power_collapsed, cpu) = 0;
 	}
 
 	/*
@@ -216,7 +216,7 @@
 	 * GDFS which needs to be brought out by raising an SPI.
 	 */
 
-	if (power_collapsed) {
+	if (per_cpu(power_collapsed, cpu)) {
 		gic_configure_and_raise(cpu_data[cpu].ipc_irq, cpu);
 		raise_clear_spi(cpu, true);
 	} else {
diff --git a/arch/arm/mach-msm/platsmp-8910.c b/arch/arm/mach-msm/platsmp-8910.c
index 5d055bd..af2f496 100644
--- a/arch/arm/mach-msm/platsmp-8910.c
+++ b/arch/arm/mach-msm/platsmp-8910.c
@@ -32,7 +32,7 @@
  * control for which core is the next to come out of the secondary
  * boot "holding pen"
  */
-volatile int __cpuinitdata pen_release = -1;
+volatile int pen_release = -1;
 
 /*
  * Write pen_release in a way that is guaranteed to be visible to all
diff --git a/arch/arm/mach-msm/platsmp.c b/arch/arm/mach-msm/platsmp.c
index 5f05f98..b1d2464 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);
 	}
@@ -176,11 +175,13 @@
 {
 	BUG_ON(cpu >= get_core_count());
 
+	if (machine_is_msm8974_rumi())
+		return 0;
+
 	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 (soc_class_is_msm8960() || soc_class_is_msm8930() ||
diff --git a/arch/arm/mach-msm/pm-8x60.c b/arch/arm/mach-msm/pm-8x60.c
index f55d509..550bb56 100644
--- a/arch/arm/mach-msm/pm-8x60.c
+++ b/arch/arm/mach-msm/pm-8x60.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
@@ -35,7 +35,7 @@
 #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
@@ -51,7 +51,8 @@
 #include "timer.h"
 #include "pm-boot.h"
 #include <mach/event_timer.h>
-
+#define CREATE_TRACE_POINTS
+#include "trace_msm_low_power.h"
 /******************************************************************************
  * Debug Definitions
  *****************************************************************************/
@@ -114,6 +115,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;
 /*
@@ -473,7 +475,6 @@
 }
 
 static void *msm_pm_idle_rs_limits;
-static bool msm_pm_use_qtimer;
 
 static void msm_pm_swfi(void)
 {
@@ -499,24 +500,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)
 {
@@ -547,7 +530,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);
 
@@ -576,11 +559,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;
 }
@@ -590,6 +575,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)
@@ -601,7 +587,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();
@@ -643,6 +629,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)
@@ -657,14 +644,11 @@
 {
 	if (cpu_is_apq8064())
 		msm_pm_save_cp15 = true;
-
-	if (cpu_is_msm8974())
-		msm_pm_use_qtimer = 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();
@@ -672,7 +656,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);
@@ -682,7 +666,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);
@@ -694,7 +678,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) {
@@ -741,6 +725,51 @@
 	return;
 }
 
+static inline void msm_pm_ftrace_lpm_enter(unsigned int cpu,
+		uint32_t latency, uint32_t sleep_us,
+		uint32_t wake_up,
+		enum msm_pm_sleep_mode mode)
+{
+	switch (mode) {
+	case MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT:
+		trace_msm_pm_enter_wfi(cpu, latency, sleep_us, wake_up);
+		break;
+	case MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE:
+		trace_msm_pm_enter_spc(cpu, latency, sleep_us, wake_up);
+		break;
+	case MSM_PM_SLEEP_MODE_POWER_COLLAPSE:
+		trace_msm_pm_enter_pc(cpu, latency, sleep_us, wake_up);
+		break;
+	case MSM_PM_SLEEP_MODE_RETENTION:
+		trace_msm_pm_enter_ret(cpu, latency, sleep_us, wake_up);
+		break;
+	default:
+		break;
+	}
+}
+
+static inline void msm_pm_ftrace_lpm_exit(unsigned int cpu,
+		enum msm_pm_sleep_mode mode,
+		bool success)
+{
+	switch (mode) {
+	case MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT:
+		trace_msm_pm_exit_wfi(cpu, success);
+		break;
+	case MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE:
+		trace_msm_pm_exit_spc(cpu, success);
+		break;
+	case MSM_PM_SLEEP_MODE_POWER_COLLAPSE:
+		trace_msm_pm_exit_pc(cpu, success);
+		break;
+	case MSM_PM_SLEEP_MODE_RETENTION:
+		trace_msm_pm_exit_ret(cpu, success);
+		break;
+	default:
+		break;
+	}
+}
+
 int msm_pm_idle_prepare(struct cpuidle_device *dev,
 		struct cpuidle_driver *drv, int index)
 {
@@ -845,6 +874,11 @@
 
 	if (modified_time_us && !dev->cpu)
 		msm_pm_set_timer(modified_time_us);
+
+	msm_pm_ftrace_lpm_enter(dev->cpu, time_param.latency_us,
+			time_param.sleep_us, time_param.next_event_us,
+			ret);
+
 	return ret;
 }
 
@@ -852,6 +886,7 @@
 {
 	int64_t time;
 	int exit_stat;
+	bool collapsed = 1;
 
 	if (MSM_PM_DEBUG_IDLE & msm_pm_debug_mask)
 		pr_info("CPU%u: %s: mode %d\n",
@@ -871,7 +906,7 @@
 		break;
 
 	case MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE:
-		msm_pm_power_collapse_standalone(true);
+		collapsed = msm_pm_power_collapse_standalone(true);
 		exit_stat = MSM_PM_STAT_IDLE_STANDALONE_POWER_COLLAPSE;
 		break;
 
@@ -882,8 +917,6 @@
 		int ret = -ENODEV;
 		int notify_rpm =
 			(sleep_mode == MSM_PM_SLEEP_MODE_POWER_COLLAPSE);
-		int collapsed;
-
 		timer_expiration = msm_pm_timer_enter_idle();
 
 		sleep_delay = (uint32_t) msm_pm_convert_and_cap_time(
@@ -918,6 +951,8 @@
 
 	time = ktime_to_ns(ktime_get()) - time;
 	msm_pm_add_stat(exit_stat, time);
+	msm_pm_ftrace_lpm_exit(smp_processor_id(), sleep_mode,
+				collapsed);
 
 	do_div(time, 1000);
 	return (int) time;
@@ -1165,3 +1200,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 7bc4fe0..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;
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..bd61feb 100644
--- a/arch/arm/mach-msm/pm.h
+++ b/arch/arm/mach-msm/pm.h
@@ -1,7 +1,7 @@
 /* arch/arm/mach-msm/pm.h
  *
  * Copyright (C) 2007 Google, Inc.
- * Copyright (c) 2009-2012, Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2009-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
@@ -27,7 +27,7 @@
 #define msm_secondary_startup NULL
 #endif
 
-extern int power_collapsed;
+DECLARE_PER_CPU(int,  power_collapsed);
 
 struct msm_pm_irq_calls {
 	unsigned int (*irq_pending)(void);
@@ -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 427e39f..96c1218 100644
--- a/arch/arm/mach-msm/pm2.c
+++ b/arch/arm/mach-msm/pm2.c
@@ -3,7 +3,7 @@
  * MSM Power Management Routines
  *
  * Copyright (C) 2007 Google, Inc.
- * Copyright (c) 2008-2012 Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2008-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
@@ -75,8 +75,9 @@
 	MSM_PM_DEBUG_HOTPLUG = BIT(7),
 };
 
+DEFINE_PER_CPU(int, power_collapsed);
+
 static int msm_pm_debug_mask;
-int power_collapsed;
 module_param_named(
 	debug_mask, msm_pm_debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP
 );
@@ -482,66 +483,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 = core_reset_base(1);
-	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();
+	}
 }
 
 /*
@@ -560,7 +566,7 @@
 		__raw_writel(0, APPS_PWRDOWN);
 		mb();
 
-		if (power_collapsed) {
+		if (per_cpu(power_collapsed, 1)) {
 			/*
 			 * enable the SCU while coming out of power
 			 * collapse.
@@ -569,7 +575,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);
@@ -978,20 +984,50 @@
 	 * path by reading the MPA5_GDFS_CNT_VAL register.
 	 */
 	if (cpu_is_msm8625()) {
+		int cpu;
 		/*
 		 * 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) {
+				for_each_possible_cpu(cpu) {
+					if (!cpu)
+						continue;
+					per_cpu(power_collapsed, cpu) = 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) {
+				for_each_possible_cpu(cpu) {
+					if (!cpu)
+						continue;
+					per_cpu(power_collapsed, cpu) = 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 +1720,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/pmu.c b/arch/arm/mach-msm/pmu.c
index 5e339da..f0b83f9 100644
--- a/arch/arm/mach-msm/pmu.c
+++ b/arch/arm/mach-msm/pmu.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
@@ -11,8 +11,77 @@
  */
 
 #include <linux/platform_device.h>
+#include <linux/irq.h>
 #include <asm/pmu.h>
 #include <mach/irqs.h>
+#include <mach/socinfo.h>
+
+/*
+ * If a GIC is present, then all IRQ's < 32 are PPI's and can only be
+ * requested and free'd using the percpu IRQ API.
+ * If a VIC is present, then only the traditional request, free API works.
+ *
+ * All MPCore's have GIC's. The Cortex A5 however may or may not be MPcore, but
+ * it still has a GIC. Except, the 7x27a, which is an A5 and yet has a VIC.
+ * So if the chip is A5 but does not have a GIC, default to the traditional
+ * IRQ {request, free}_irq API.
+ */
+
+#if defined(CONFIG_ARCH_MSM_KRAITMP) || defined(CONFIG_ARCH_MSM_SCORPIONMP) \
+	|| defined(CONFIG_ARCH_MSM8625) || \
+	(defined(CONFIG_ARCH_MSM_CORTEX_A5) && !defined(CONFIG_MSM_VIC))
+static DEFINE_PER_CPU(u32, pmu_irq_cookie);
+
+static void enable_irq_callback(void *info)
+{
+	int irq = *(unsigned int *)info;
+	enable_percpu_irq(irq, IRQ_TYPE_EDGE_RISING);
+}
+
+static void disable_irq_callback(void *info)
+{
+	int irq = *(unsigned int *)info;
+	disable_percpu_irq(irq);
+}
+
+static int
+multicore_request_irq(int irq, irq_handler_t *handle_irq)
+{
+	int err = 0;
+	int cpu;
+
+	err = request_percpu_irq(irq, *handle_irq, "l1-armpmu",
+			&pmu_irq_cookie);
+
+	if (!err) {
+		for_each_cpu(cpu, cpu_online_mask) {
+			smp_call_function_single(cpu,
+					enable_irq_callback, &irq, 1);
+		}
+	}
+
+	return err;
+}
+
+static void
+multicore_free_irq(int irq)
+{
+	int cpu;
+
+	if (irq >= 0) {
+		for_each_cpu(cpu, cpu_online_mask) {
+			smp_call_function_single(cpu,
+					disable_irq_callback, &irq, 1);
+		}
+		free_percpu_irq(irq, &pmu_irq_cookie);
+	}
+}
+
+static struct arm_pmu_platdata multicore_data = {
+	.request_pmu_irq = multicore_request_irq,
+	.free_pmu_irq = multicore_free_irq,
+};
+#endif
 
 static struct resource cpu_pmu_resource[] = {
 	{
@@ -47,6 +116,29 @@
 	.num_resources	= ARRAY_SIZE(cpu_pmu_resource),
 };
 
+/*
+ * The 8625 is a special case. Due to the requirement of a single
+ * kernel image for the 7x27a and 8625 (which share IRQ headers),
+ * this target breaks the uniformity of IRQ names.
+ * See the file - arch/arm/mach-msm/include/mach/irqs-8625.h
+ */
+#ifdef CONFIG_ARCH_MSM8625
+static struct resource msm8625_cpu_pmu_resource[] = {
+	{
+		.start = MSM8625_INT_ARMQC_PERFMON,
+		.end = MSM8625_INT_ARMQC_PERFMON,
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct platform_device msm8625_cpu_pmu_device = {
+	.name		= "cpu-arm-pmu",
+	.id		= ARM_PMU_DEVICE_CPU,
+	.resource	= msm8625_cpu_pmu_resource,
+	.num_resources	= ARRAY_SIZE(msm8625_cpu_pmu_resource),
+};
+#endif
+
 static struct platform_device *pmu_devices[] = {
 	&cpu_pmu_device,
 #ifdef CONFIG_CPU_HAS_L2_PMU
@@ -56,6 +148,30 @@
 
 static int __init msm_pmu_init(void)
 {
+	/*
+	 * For the targets we know are multicore's set the request/free IRQ
+	 * handlers to call the percpu API.
+	 * Defaults to unicore API {request,free}_irq().
+	 * See arch/arm/kernel/perf_event.c
+	 * See Comment above on the A5 and MSM_VIC.
+	 */
+#if defined(CONFIG_ARCH_MSM_KRAITMP) || defined(CONFIG_ARCH_MSM_SCORPIONMP) \
+	|| (defined(CONFIG_ARCH_MSM_CORTEX_A5) && !defined(CONFIG_MSM_VIC))
+	cpu_pmu_device.dev.platform_data = &multicore_data;
+#endif
+
+	/*
+	 * The 7x27a and 8625 require a single kernel image.
+	 * So we need to check if we're on an 8625 at runtime
+	 * and point to the appropriate 'struct resource'.
+	 */
+#ifdef CONFIG_ARCH_MSM8625
+	if (cpu_is_msm8625()) {
+		pmu_devices[0] = &msm8625_cpu_pmu_device;
+		msm8625_cpu_pmu_device.dev.platform_data = &multicore_data;
+	}
+#endif
+
 	return platform_add_devices(pmu_devices, ARRAY_SIZE(pmu_devices));
 }
 
diff --git a/arch/arm/mach-msm/qdsp5/audio_acdb.c b/arch/arm/mach-msm/qdsp5/audio_acdb.c
index 16f23f4..d7a4607 100644
--- a/arch/arm/mach-msm/qdsp5/audio_acdb.c
+++ b/arch/arm/mach-msm/qdsp5/audio_acdb.c
@@ -2575,6 +2575,7 @@
 
 	memset(&acdb_data, 0, sizeof(acdb_data));
 	spin_lock_init(&acdb_data.dsp_lock);
+	init_waitqueue_head(&acdb_data.wait);
 	acdb_data.cb_thread_task = kthread_run(acdb_calibrate_device,
 		NULL, "acdb_cb_thread");
 
@@ -2590,7 +2591,6 @@
 		MM_ERR("RTC ACDB=>INIT Failure\n");
 
 #endif
-	init_waitqueue_head(&acdb_data.wait);
 
 	return misc_register(&acdb_misc);
 err:
diff --git a/arch/arm/mach-msm/qdsp6v2/Makefile b/arch/arm/mach-msm/qdsp6v2/Makefile
index 3731722..08a6de6 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
@@ -19,11 +20,10 @@
 obj-$(CONFIG_MSM_QDSP6_CODECS) += aac_in.o qcelp_in.o evrc_in.o amrnb_in.o audio_utils.o
 obj-$(CONFIG_MSM_QDSP6_CODECS) += audio_wma.o audio_wmapro.o audio_aac.o audio_multi_aac.o audio_utils_aio.o
 obj-$(CONFIG_MSM_QDSP6_CODECS) += rtac.o q6audio_v1.o q6audio_v1_aio.o
-obj-$(CONFIG_MSM_QDSP6_CODECS) += audio_mp3.o audio_amrnb.o audio_amrwb.o audio_evrc.o audio_qcelp.o amrwb_in.o
+obj-$(CONFIG_MSM_QDSP6_CODECS) += audio_mp3.o audio_amrnb.o audio_amrwb.o audio_amrwbplus.o audio_evrc.o audio_qcelp.o amrwb_in.o
 obj-$(CONFIG_MSM_QDSP6V2_CODECS) += aac_in.o qcelp_in.o evrc_in.o amrnb_in.o audio_utils.o
 obj-$(CONFIG_MSM_QDSP6V2_CODECS) += audio_wma.o audio_wmapro.o audio_aac.o audio_multi_aac.o audio_utils_aio.o
 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_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_A) += ultrasound/version_a/
-obj-m += adsprpc.o
diff --git a/arch/arm/mach-msm/qdsp6v2/adsp-loader.c b/arch/arm/mach-msm/qdsp6v2/adsp-loader.c
index 9924b52..02dbece 100644
--- a/arch/arm/mach-msm/qdsp6v2/adsp-loader.c
+++ b/arch/arm/mach-msm/qdsp6v2/adsp-loader.c
@@ -17,8 +17,9 @@
 #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>
+#include <linux/of_device.h>
 
 #define Q6_PIL_GET_DELAY_MS 100
 
@@ -30,25 +31,41 @@
 {
 	struct adsp_loader_private *priv;
 	int rc = 0;
+	const char *adsp_dt = "qcom,adsp-state";
+	u32 adsp_state;
 
-	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
-	if (!priv)
-		return -ENOMEM;
-
-	platform_set_drvdata(pdev, priv);
-
-	priv->pil_h = pil_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);
-		goto fail;
+	rc = of_property_read_u32(pdev->dev.of_node, adsp_dt, &adsp_state);
+	if (rc) {
+		dev_err(&pdev->dev,
+			"%s: ADSP state = %x\n", __func__, adsp_state);
+		return rc;
 	}
 
-	/* Query the DSP to check if resources are available */
-	msleep(Q6_PIL_GET_DELAY_MS);
+	if (adsp_state == APR_SUBSYS_DOWN) {
+		priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+		if (!priv)
+			return -ENOMEM;
 
-	/* Set the state of the ADSP in APR driver */
-	apr_set_q6_state(APR_SUBSYS_LOADED);
+		platform_set_drvdata(pdev, priv);
+
+		priv->pil_h = subsystem_get("adsp");
+		if (IS_ERR(priv->pil_h)) {
+			pr_err("%s: pil get adsp failed, error:%d\n",
+				__func__, rc);
+			devm_kfree(&pdev->dev, priv);
+			goto fail;
+		}
+
+		/* Query the DSP to check if resources are available */
+		msleep(Q6_PIL_GET_DELAY_MS);
+
+		/* Set the state of the ADSP in APR driver */
+		apr_set_q6_state(APR_SUBSYS_LOADED);
+	} else if (adsp_state == APR_SUBSYS_LOADED) {
+		dev_dbg(&pdev->dev,
+			"%s:MDM9x25 ADSP state = %x\n", __func__, adsp_state);
+		apr_set_q6_state(APR_SUBSYS_LOADED);
+	}
 
 	/* Query for MMPM API */
 
@@ -62,7 +79,8 @@
 	struct adsp_loader_private *priv;
 
 	priv = platform_get_drvdata(pdev);
-	pil_put(priv->pil_h);
+	if (priv != NULL)
+		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 a24b9ec..cad845f 100644
--- a/arch/arm/mach-msm/qdsp6v2/audio_acdb.c
+++ b/arch/arm/mach-msm/qdsp6v2/audio_acdb.c
@@ -730,12 +730,19 @@
 
 static int deregister_memory(void)
 {
+	int i;
+
 	if (atomic64_read(&acdb_data.mem_len)) {
 		mutex_lock(&acdb_data.acdb_mutex);
+		atomic64_set(&acdb_data.mem_len, 0);
 		atomic_set(&acdb_data.vocstrm_total_cal_size, 0);
 		atomic_set(&acdb_data.vocproc_total_cal_size, 0);
 		atomic_set(&acdb_data.vocvol_total_cal_size, 0);
-		atomic64_set(&acdb_data.mem_len, 0);
+
+		for (i = 0; i < MAX_VOCPROC_TYPES; i++) {
+			kfree(acdb_data.col_data[i]);
+			acdb_data.col_data[i] = NULL;
+		}
 		ion_unmap_kernel(acdb_data.ion_client, acdb_data.ion_handle);
 		ion_free(acdb_data.ion_client, acdb_data.ion_handle);
 		ion_client_destroy(acdb_data.ion_client);
@@ -747,12 +754,19 @@
 static int register_memory(void)
 {
 	int			result;
+	int			i;
 	unsigned long		paddr;
 	void                    *kvptr;
 	unsigned long		kvaddr;
 	unsigned long		mem_len;
 
 	mutex_lock(&acdb_data.acdb_mutex);
+	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]);
+	}
+
 	acdb_data.ion_client =
 		msm_ion_client_create(UINT_MAX, "audio_acdb_client");
 	if (IS_ERR_OR_NULL(acdb_data.ion_client)) {
@@ -1029,7 +1043,6 @@
 
 static int acdb_release(struct inode *inode, struct file *f)
 {
-	int i;
 	s32 result = 0;
 
 	atomic_dec(&usage_count);
@@ -1038,11 +1051,6 @@
 	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
@@ -1067,16 +1075,10 @@
 
 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_amrwbplus.c b/arch/arm/mach-msm/qdsp6v2/audio_amrwbplus.c
new file mode 100644
index 0000000..2889c14
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp6v2/audio_amrwbplus.c
@@ -0,0 +1,234 @@
+/* amr-wbplus audio output device
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Copyright (C) 2008 HTC Corporation
+ * Copyright (c) 2010-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.
+ *
+ */
+#include <linux/msm_audio_amrwbplus.h>
+#include "audio_utils_aio.h"
+
+#ifdef CONFIG_DEBUG_FS
+static const struct file_operations audio_amrwbplus_debug_fops = {
+	.read = audio_aio_debug_read,
+	.open = audio_aio_debug_open,
+};
+static void config_debug_fs(struct q6audio_aio *audio)
+{
+	if (audio != NULL) {
+		char name[sizeof("msm_amrwbplus_") + 5];
+		snprintf(name, sizeof(name), "msm_amrwbplus_%04x",
+			audio->ac->session);
+		audio->dentry = debugfs_create_file(name, S_IFREG | S_IRUGO,
+						NULL, (void *)audio,
+						&audio_amrwbplus_debug_fops);
+		if (IS_ERR(audio->dentry))
+			pr_debug("debugfs_create_file failed\n");
+	}
+}
+#else
+static void config_debug_fs(struct q6audio_aio *)
+{
+}
+#endif
+
+static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+	struct asm_amrwbplus_cfg q6_amrwbplus_cfg;
+	struct msm_audio_amrwbplus_config_v2 *amrwbplus_drv_config;
+	struct q6audio_aio *audio = file->private_data;
+	int rc = 0;
+
+	switch (cmd) {
+	case AUDIO_START: {
+		pr_err("%s[%p]: AUDIO_START session_id[%d]\n", __func__,
+			audio, audio->ac->session);
+		if (audio->feedback == NON_TUNNEL_MODE) {
+			/* Configure PCM output block */
+			rc = q6asm_enc_cfg_blk_pcm(audio->ac,
+			audio->pcm_cfg.sample_rate,
+			audio->pcm_cfg.channel_count);
+			if (rc < 0) {
+				pr_err("pcm output block config failed\n");
+				break;
+			}
+		}
+		amrwbplus_drv_config =
+		(struct msm_audio_amrwbplus_config_v2 *)audio->codec_cfg;
+
+		q6_amrwbplus_cfg.size_bytes     =
+			amrwbplus_drv_config->size_bytes;
+		q6_amrwbplus_cfg.version        =
+			amrwbplus_drv_config->version;
+		q6_amrwbplus_cfg.num_channels   =
+			amrwbplus_drv_config->num_channels;
+		q6_amrwbplus_cfg.amr_band_mode  =
+			amrwbplus_drv_config->amr_band_mode;
+		q6_amrwbplus_cfg.amr_dtx_mode   =
+			amrwbplus_drv_config->amr_dtx_mode;
+		q6_amrwbplus_cfg.amr_frame_fmt  =
+			amrwbplus_drv_config->amr_frame_fmt;
+		q6_amrwbplus_cfg.amr_lsf_idx    =
+			amrwbplus_drv_config->amr_lsf_idx;
+
+		rc = q6asm_media_format_block_amrwbplus(audio->ac,
+							&q6_amrwbplus_cfg);
+		if (rc < 0) {
+			pr_err("q6asm_media_format_block_amrwb+ failed...\n");
+			break;
+		}
+		rc = audio_aio_enable(audio);
+		audio->eos_rsp = 0;
+		audio->eos_flag = 0;
+		if (!rc) {
+			audio->enabled = 1;
+		} else {
+			audio->enabled = 0;
+			pr_err("Audio Start procedure failed rc=%d\n", rc);
+			break;
+		}
+		pr_debug("%s:AUDIO_START sessionid[%d]enable[%d]\n", __func__,
+			audio->ac->session,
+			audio->enabled);
+		if (audio->stopped == 1)
+			audio->stopped = 0;
+			break;
+		}
+	case AUDIO_GET_AMRWBPLUS_CONFIG_V2: {
+		if ((audio) && (arg) && (audio->codec_cfg)) {
+			if (copy_to_user((void *)arg, audio->codec_cfg,
+				sizeof(struct msm_audio_amrwbplus_config_v2))) {
+				rc = -EFAULT;
+				pr_err("wb+ config get copy_to_user failed");
+				break;
+			}
+			} else {
+				pr_err("wb+ config v2 invalid parameters..");
+				rc = -EFAULT;
+				break;
+			}
+		break;
+	}
+	case AUDIO_SET_AMRWBPLUS_CONFIG_V2: {
+		if ((audio) && (arg) && (audio->codec_cfg)) {
+			if (copy_from_user(audio->codec_cfg, (void *)arg,
+			sizeof(struct msm_audio_amrwbplus_config_v2))) {
+				rc = -EFAULT;
+				pr_err("wb+ config set copy_to_user_failed");
+				break;
+			}
+			} else {
+				pr_err("wb+ config invalid parameters..");
+				rc = -EFAULT;
+				break;
+			}
+		break;
+	}
+	default:
+		pr_debug("%s[%p]: Calling utils ioctl\n", __func__, audio);
+		rc = audio->codec_ioctl(file, cmd, arg);
+	}
+	return rc;
+}
+
+static int audio_open(struct inode *inode, struct file *file)
+{
+	struct q6audio_aio *audio = NULL;
+	int rc = 0;
+
+	audio = kzalloc(sizeof(struct q6audio_aio), GFP_KERNEL);
+
+	if (audio == NULL) {
+		pr_err("kzalloc failed for amrwb+ decode driver\n");
+		return -ENOMEM;
+	}
+	audio->codec_cfg =
+	kzalloc(sizeof(struct msm_audio_amrwbplus_config_v2), GFP_KERNEL);
+	if (audio->codec_cfg == NULL) {
+		pr_err("%s:failed kzalloc for amrwb+ config structure",
+			__func__);
+		kfree(audio);
+		return -ENOMEM;
+	}
+	audio->pcm_cfg.buffer_size = PCM_BUFSZ_MIN;
+
+	audio->ac =
+	q6asm_audio_client_alloc((app_cb) q6_audio_cb, (void *)audio);
+
+	if (!audio->ac) {
+		pr_err("Could not allocate memory for audio client\n");
+		kfree(audio->codec_cfg);
+		kfree(audio);
+		return -ENOMEM;
+	}
+
+	/* open in T/NT mode */
+	if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) {
+		rc = q6asm_open_read_write(audio->ac, FORMAT_LINEAR_PCM,
+					FORMAT_AMR_WB_PLUS);
+		if (rc < 0) {
+			pr_err("amrwbplus NT mode Open failed rc=%d\n", rc);
+			rc = -ENODEV;
+			goto fail;
+		}
+		audio->feedback = NON_TUNNEL_MODE;
+		audio->buf_cfg.frames_per_buf = 0x01;
+		audio->buf_cfg.meta_info_enable = 0x01;
+	} else if ((file->f_mode & FMODE_WRITE) &&
+			!(file->f_mode & FMODE_READ)) {
+			rc = q6asm_open_write(audio->ac, FORMAT_AMR_WB_PLUS);
+			if (rc < 0) {
+				pr_err("wb+ T mode Open failed rc=%d\n", rc);
+				rc = -ENODEV;
+				goto fail;
+			}
+		audio->feedback = TUNNEL_MODE;
+		audio->buf_cfg.meta_info_enable = 0x00;
+	} else {
+		pr_err("audio_amrwbplus Not supported mode\n");
+		rc = -EACCES;
+		goto fail;
+	}
+	rc = audio_aio_open(audio, file);
+
+	config_debug_fs(audio);
+	pr_debug("%s: AMRWBPLUS dec success mode[%d]session[%d]\n", __func__,
+		audio->feedback,
+		audio->ac->session);
+	return 0;
+fail:
+	q6asm_audio_client_free(audio->ac);
+	kfree(audio->codec_cfg);
+	kfree(audio);
+	return rc;
+}
+
+static const struct file_operations audio_amrwbplus_fops = {
+	.owner = THIS_MODULE,
+	.open = audio_open,
+	.release = audio_aio_release,
+	.unlocked_ioctl = audio_ioctl,
+	.fsync = audio_aio_fsync,
+};
+
+struct miscdevice audio_amrwbplus_misc = {
+	.minor = MISC_DYNAMIC_MINOR,
+	.name = "msm_amrwbplus",
+	.fops = &audio_amrwbplus_fops,
+};
+
+static int __init audio_amrwbplus_init(void)
+{
+	return misc_register(&audio_amrwbplus_misc);
+}
+
+device_initcall(audio_amrwbplus_init);
diff --git a/arch/arm/mach-msm/qdsp6v2/audio_utils_aio.c b/arch/arm/mach-msm/qdsp6v2/audio_utils_aio.c
index d6abdda..a4a6b906 100644
--- a/arch/arm/mach-msm/qdsp6v2/audio_utils_aio.c
+++ b/arch/arm/mach-msm/qdsp6v2/audio_utils_aio.c
@@ -779,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 */
@@ -798,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;
@@ -1135,6 +1142,11 @@
 			mutex_unlock(&audio->lock);
 			break;
 		}
+		if (audio->drv_status & ADRV_STATUS_FSYNC) {
+			pr_debug("%s[%p] Waking up the audio_aio_fsync\n",
+					__func__, audio);
+			wake_up(&audio->write_wait);
+		}
 		mutex_unlock(&audio->lock);
 		break;
 	}
@@ -1171,6 +1183,11 @@
 		mutex_lock(&audio->lock);
 		audio->rflush = 1;
 		audio->wflush = 1;
+		if (audio->drv_status & ADRV_STATUS_FSYNC) {
+			pr_debug("%s[%p] Waking up the audio_aio_fsync\n",
+				__func__, audio);
+			wake_up(&audio->write_wait);
+		}
 		/* Flush DSP */
 		rc = audio_aio_flush(audio);
 		/* Flush input / Output buffer in software*/
diff --git a/arch/arm/mach-msm/qdsp6v2/audio_utils_aio.h b/arch/arm/mach-msm/qdsp6v2/audio_utils_aio.h
index b2829c3..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) ({                                  \
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_common.h b/arch/arm/mach-msm/qdsp6v2/q6audio_common.h
index e4291e7..3bc8454 100644
--- a/arch/arm/mach-msm/qdsp6v2/q6audio_common.h
+++ b/arch/arm/mach-msm/qdsp6v2/q6audio_common.h
@@ -15,7 +15,7 @@
 #ifndef __Q6_AUDIO_COMMON_H__
 #define __Q6_AUDIO_COMMON_H__
 
-#ifdef CONFIG_ARCH_MSM8974
+#if defined(CONFIG_ARCH_MSM8974) || defined(CONFIG_ARCH_MSM9625)
 #include <sound/apr_audio-v2.h>
 #include <sound/q6asm-v2.h>
 #else
diff --git a/arch/arm/mach-msm/qdsp6v2/rtac_v2.c b/arch/arm/mach-msm/qdsp6v2/rtac_v2.c
index 2d0607c..409d796 100644
--- a/arch/arm/mach-msm/qdsp6v2/rtac_v2.c
+++ b/arch/arm/mach-msm/qdsp6v2/rtac_v2.c
@@ -24,6 +24,7 @@
 #include <mach/qdsp6v2/rtac.h>
 #include "q6audio_common.h"
 #include <sound/q6afe-v2.h>
+#include <sound/apr_audio-v2.h>
 
 #ifndef CONFIG_RTAC
 
@@ -45,10 +46,6 @@
 
 #else
 
-#define VOICE_CMD_SET_PARAM		0x00011006
-#define VOICE_CMD_GET_PARAM		0x00011007
-#define VOICE_EVT_GET_PARAM_ACK		0x00011008
-
 /* Max size of payload (buf size - apr header) */
 #define MAX_PAYLOAD_SIZE		4076
 #define RTAC_MAX_ACTIVE_DEVICES		4
@@ -353,7 +350,7 @@
 	return;
 }
 
-static int get_voice_index(u32 cvs_handle)
+static int get_voice_index_cvs(u32 cvs_handle)
 {
 	u32 i;
 
@@ -367,6 +364,32 @@
 	return 0;
 }
 
+static int get_voice_index_cvp(u32 cvp_handle)
+{
+	u32 i;
+
+	for (i = 0; i < rtac_voice_data.num_of_voice_combos; i++) {
+		if (rtac_voice_data.voice[i].cvp_handle == cvp_handle)
+			return i;
+	}
+
+	pr_err("%s: No voice index for CVP handle %d found returning 0\n",
+	       __func__, cvp_handle);
+	return 0;
+}
+
+static int get_voice_index(u32 mode, u32 handle)
+{
+	if (mode == RTAC_CVP)
+		return get_voice_index_cvp(handle);
+	if (mode == RTAC_CVS)
+		return get_voice_index_cvs(handle);
+
+	pr_err("%s: Invalid mode %d, returning 0\n",
+	       __func__, mode);
+	return 0;
+}
+
 
 /* ADM APR */
 void rtac_set_adm_handle(void *handle)
@@ -402,6 +425,7 @@
 		if (payload_size > rtac_adm_user_buf_size) {
 			pr_err("%s: Buffer set not big enough for returned data, buf size = %d, ret data = %d\n",
 			 __func__, rtac_adm_user_buf_size, payload_size);
+			rtac_adm_payload_size = 0;
 			goto done;
 		}
 		memcpy(rtac_adm_buffer + sizeof(u32), payload, payload_size);
@@ -470,6 +494,7 @@
 
 	/* Set globals for copy of returned payload */
 	rtac_adm_user_buf_size = count;
+
 	/* Copy buffer to in-band payload */
 	if (copy_from_user(rtac_adm_buffer + sizeof(adm_params),
 			buf + 3 * sizeof(u32), payload_size)) {
@@ -572,6 +597,7 @@
 		if (payload_size > rtac_asm_user_buf_size) {
 			pr_err("%s: Buffer set not big enough for returned data, buf size = %d, ret data = %d\n",
 			 __func__, rtac_asm_user_buf_size, payload_size);
+			rtac_asm_payload_size = 0;
 			goto done;
 		}
 		memcpy(rtac_asm_buffer + sizeof(u32), payload, payload_size);
@@ -619,6 +645,7 @@
 			__func__);
 		goto done;
 	}
+
 	if (session_id > (SESSION_MAX + 1)) {
 		pr_err("%s: Invalid Session = %d\n", __func__, session_id);
 		goto done;
@@ -739,6 +766,7 @@
 		if (payload_size > rtac_voice_user_buf_size) {
 			pr_err("%s: Buffer set not big enough for returned data, buf size = %d, ret data = %d\n",
 			 __func__, rtac_voice_user_buf_size, payload_size);
+			rtac_voice_payload_size = 0;
 			goto done;
 		}
 		memcpy(rtac_voice_buffer + sizeof(u32), payload, payload_size);
@@ -753,7 +781,7 @@
 	u32	count = 0;
 	u32	bytes_returned = 0;
 	u32	payload_size;
-	u16	dest_port;
+	u32	dest_port;
 	struct apr_hdr		voice_params;
 	pr_debug("%s\n", __func__);
 
@@ -818,10 +846,10 @@
 	voice_params.src_svc = 0;
 	voice_params.src_domain = APR_DOMAIN_APPS;
 	voice_params.src_port = voice_session_id[
-					get_voice_index(dest_port)];
+					get_voice_index(mode, dest_port)];
 	voice_params.dest_svc = 0;
 	voice_params.dest_domain = APR_DOMAIN_MODEM;
-	voice_params.dest_port = dest_port;
+	voice_params.dest_port = (u16)dest_port;
 	voice_params.token = 0;
 	voice_params.opcode = opcode;
 
diff --git a/arch/arm/mach-msm/qdsp6v2/ultrasound/usfcdev.c b/arch/arm/mach-msm/qdsp6v2/ultrasound/usfcdev.c
index f566e82..94192cf 100644
--- a/arch/arm/mach-msm/qdsp6v2/ultrasound/usfcdev.c
+++ b/arch/arm/mach-msm/qdsp6v2/ultrasound/usfcdev.c
@@ -16,15 +16,38 @@
 #include <linux/miscdevice.h>
 #include <linux/module.h>
 #include <linux/init.h>
+#include <linux/input/mt.h>
+#include <linux/syscalls.h>
 #include "usfcdev.h"
 
+#define UNDEF_ID    0xffffffff
+#define SLOT_CMD_ID 0
+#define MAX_RETRIES 10
+
+
+
+enum usdev_event_status {
+	USFCDEV_EVENT_ENABLED,
+	USFCDEV_EVENT_DISABLING,
+	USFCDEV_EVENT_DISABLED,
+};
+
 struct usfcdev_event {
 	bool (*match_cb)(uint16_t, struct input_dev *dev);
 	bool registered_event;
-	bool filter;
+	bool interleaved;
+	enum usdev_event_status event_status;
 };
 static struct usfcdev_event s_usfcdev_events[MAX_EVENT_TYPE_NUM];
 
+struct usfcdev_input_command {
+	unsigned int type;
+	unsigned int code;
+	unsigned int value;
+};
+
+static long  s_usf_pid;
+
 static bool usfcdev_filter(struct input_handle *handle,
 			 unsigned int type, unsigned int code, int value);
 static bool usfcdev_match(struct input_handler *handler,
@@ -83,6 +106,22 @@
 	},
 };
 
+static struct usfcdev_input_command initial_clear_cmds[] = {
+	{EV_ABS, ABS_PRESSURE,               0},
+	{EV_KEY, BTN_TOUCH,                  0},
+};
+
+static struct usfcdev_input_command slot_clear_cmds[] = {
+	{EV_ABS, ABS_MT_SLOT,               0},
+	{EV_ABS, ABS_MT_TRACKING_ID, UNDEF_ID},
+};
+
+static struct usfcdev_input_command no_filter_cmds[] = {
+	{EV_ABS, ABS_MT_SLOT,               0},
+	{EV_ABS, ABS_MT_TRACKING_ID, UNDEF_ID},
+	{EV_SYN, SYN_REPORT,                0},
+};
+
 static bool usfcdev_match(struct input_handler *handler, struct input_dev *dev)
 {
 	bool rc = false;
@@ -91,7 +130,7 @@
 	pr_debug("%s: name=[%s]; ind=%d\n", __func__, dev->name, ind);
 
 	if (s_usfcdev_events[ind].registered_event &&
-			s_usfcdev_events[ind].match_cb) {
+		s_usfcdev_events[ind].match_cb) {
 		rc = (*s_usfcdev_events[ind].match_cb)((uint16_t)ind, dev);
 		pr_debug("%s: [%s]; rc=%d\n", __func__, dev->name, rc);
 	}
@@ -139,16 +178,39 @@
 static bool usfcdev_filter(struct input_handle *handle,
 			unsigned int type, unsigned int code, int value)
 {
+	uint16_t i = 0;
 	uint16_t ind = (uint16_t)handle->handler->minor;
+	bool rc = (s_usfcdev_events[ind].event_status != USFCDEV_EVENT_ENABLED);
 
-	pr_debug("%s: event_type=%d; filter=%d; abs_xy=%ld; abs_y_mt[]=%ld\n",
-		__func__,
-		ind,
-		s_usfcdev_events[ind].filter,
-		 usfc_tsc_ids[0].absbit[0],
-		 usfc_tsc_ids[1].absbit[1]);
+	if (s_usf_pid == sys_getpid()) {
+		/* Pass events from usfcdev driver */
+		rc = false;
+		pr_debug("%s: event_type=%d; type=%d; code=%d; val=%d",
+			__func__,
+			ind,
+			type,
+			code,
+			value);
+	} else if (s_usfcdev_events[ind].event_status ==
+						USFCDEV_EVENT_DISABLING) {
+		uint32_t u_value = value;
+		s_usfcdev_events[ind].interleaved = true;
+		/* Pass events for freeing slots from TSC driver */
+		for (i = 0; i < ARRAY_SIZE(no_filter_cmds); ++i) {
+			if ((no_filter_cmds[i].type == type) &&
+			    (no_filter_cmds[i].code == code) &&
+			    (no_filter_cmds[i].value <= u_value)) {
+				rc = false;
+				pr_debug("%s: no_filter_cmds[%d]; %d",
+					__func__,
+					i,
+					no_filter_cmds[i].value);
+				break;
+			}
+		}
+	}
 
-	return s_usfcdev_events[ind].filter;
+	return rc;
 }
 
 bool usfcdev_register(
@@ -175,7 +237,7 @@
 
 	s_usfcdev_events[event_type_ind].registered_event = true;
 	s_usfcdev_events[event_type_ind].match_cb = match_cb;
-	s_usfcdev_events[event_type_ind].filter = false;
+	s_usfcdev_events[event_type_ind].event_status = USFCDEV_EVENT_ENABLED;
 	ret = input_register_handler(&s_usfc_handlers[event_type_ind]);
 	if (!ret) {
 		rc = true;
@@ -209,7 +271,64 @@
 			event_type_ind);
 		s_usfcdev_events[event_type_ind].registered_event = false;
 		s_usfcdev_events[event_type_ind].match_cb = NULL;
-		s_usfcdev_events[event_type_ind].filter = false;
+		s_usfcdev_events[event_type_ind].event_status =
+							USFCDEV_EVENT_ENABLED;
+
+	}
+}
+
+static inline void usfcdev_send_cmd(
+	struct input_dev *dev,
+	struct usfcdev_input_command cmd)
+{
+	input_event(dev, cmd.type, cmd.code, cmd.value);
+}
+
+static void usfcdev_clean_dev(uint16_t event_type_ind)
+{
+	struct input_dev *dev = NULL;
+	int i;
+	int j;
+	int retries = 0;
+
+	if (event_type_ind >= MAX_EVENT_TYPE_NUM) {
+		pr_err("%s: wrong input: event_type_ind=%d\n",
+			__func__,
+			event_type_ind);
+		return;
+	}
+
+	dev = s_usfc_handles[event_type_ind].dev;
+
+	for (i = 0; i < ARRAY_SIZE(initial_clear_cmds); i++)
+		usfcdev_send_cmd(dev, initial_clear_cmds[i]);
+	input_sync(dev);
+
+	/* Send commands to free all slots */
+	for (i = 0; i < dev->mtsize; i++) {
+		s_usfcdev_events[event_type_ind].interleaved = false;
+		if (input_mt_get_value(&(dev->mt[i]), ABS_MT_TRACKING_ID) < 0) {
+			pr_debug("%s: skipping slot %d",
+				__func__, i);
+			continue;
+		}
+		slot_clear_cmds[SLOT_CMD_ID].value = i;
+		for (j = 0; j < ARRAY_SIZE(slot_clear_cmds); j++)
+			usfcdev_send_cmd(dev, slot_clear_cmds[j]);
+
+		if (s_usfcdev_events[event_type_ind].interleaved) {
+			pr_debug("%s: interleaved(%d): slot(%d)",
+				__func__, i, dev->slot);
+			if (retries++ < MAX_RETRIES) {
+				--i;
+				continue;
+			}
+			pr_warning("%s: index(%d) reached max retires",
+				__func__, i);
+		}
+
+		retries = 0;
+		input_sync(dev);
 	}
 }
 
@@ -225,12 +344,22 @@
 	}
 
 	if (s_usfcdev_events[event_type_ind].registered_event) {
-		s_usfcdev_events[event_type_ind].filter = filter;
+
 		pr_debug("%s: event_type[%d]; filter=%d\n",
 			__func__,
 			event_type_ind,
 			filter
 			);
+		if (filter) {
+			s_usfcdev_events[event_type_ind].event_status =
+						USFCDEV_EVENT_DISABLING;
+			s_usf_pid = sys_getpid();
+			usfcdev_clean_dev(event_type_ind);
+			s_usfcdev_events[event_type_ind].event_status =
+						USFCDEV_EVENT_DISABLED;
+		} else
+			s_usfcdev_events[event_type_ind].event_status =
+						USFCDEV_EVENT_ENABLED;
 	} else {
 		pr_err("%s: event_type[%d] isn't registered\n",
 			__func__,
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/rpm-notifier.h b/arch/arm/mach-msm/rpm-notifier.h
index 33086c6..b9815a5 100644
--- a/arch/arm/mach-msm/rpm-notifier.h
+++ b/arch/arm/mach-msm/rpm-notifier.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
@@ -47,4 +47,12 @@
  * msm_rpm_exit_sleep - Notify RPM driver about resuming from power collapse
  */
 void msm_rpm_exit_sleep(void);
+
+/**
+ * msm_rpm_waiting_for_ack - Indicate if there is RPM message
+ *				pending acknowledgement.
+ * returns true for pending messages and false otherwise
+ */
+bool msm_rpm_waiting_for_ack(void);
+
 #endif /*__ARCH_ARM_MACH_MSM_RPM_NOTIF_H */
diff --git a/arch/arm/mach-msm/rpm-regulator-smd.c b/arch/arm/mach-msm/rpm-regulator-smd.c
index d1c61fe..bb33283 100644
--- a/arch/arm/mach-msm/rpm-regulator-smd.c
+++ b/arch/arm/mach-msm/rpm-regulator-smd.c
@@ -659,19 +659,6 @@
 	return uV;
 }
 
-static int rpm_vreg_list_voltage(struct regulator_dev *rdev, unsigned selector)
-{
-	struct rpm_regulator *reg = rdev_get_drvdata(rdev);
-	int uV = 0;
-
-	if (selector == 0)
-		uV = reg->min_uV;
-	else if (selector == 1)
-		uV = reg->max_uV;
-
-	return uV;
-}
-
 static int rpm_vreg_set_voltage_corner(struct regulator_dev *rdev, int min_uV,
 				int max_uV, unsigned *selector)
 {
@@ -1030,7 +1017,6 @@
 	.is_enabled		= rpm_vreg_is_enabled,
 	.set_voltage		= rpm_vreg_set_voltage,
 	.get_voltage		= rpm_vreg_get_voltage,
-	.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,
@@ -1043,7 +1029,6 @@
 	.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,
@@ -1056,7 +1041,6 @@
 	.is_enabled		= rpm_vreg_is_enabled,
 	.set_voltage		= rpm_vreg_set_voltage,
 	.get_voltage		= rpm_vreg_get_voltage,
-	.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,
@@ -1069,7 +1053,6 @@
 	.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,
@@ -1089,7 +1072,6 @@
 	.is_enabled		= rpm_vreg_is_enabled,
 	.set_voltage		= rpm_vreg_set_voltage,
 	.get_voltage		= rpm_vreg_get_voltage,
-	.list_voltage		= rpm_vreg_list_voltage,
 	.enable_time		= rpm_vreg_enable_time,
 };
 
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 f97eb9a..1db3d34 100644
--- a/arch/arm/mach-msm/rpm-smd.c
+++ b/arch/arm/mach-msm/rpm-smd.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
@@ -36,7 +36,8 @@
 #include <mach/msm_smd.h>
 #include <mach/rpm-smd.h>
 #include "rpm-notifier.h"
-
+#define CREATE_TRACE_POINTS
+#include "trace_rpm_smd.h"
 /* Debug Definitions */
 
 enum {
@@ -151,6 +152,8 @@
 
 LIST_HEAD(msm_rpm_ack_list);
 
+static DECLARE_COMPLETION(data_ready);
+
 static void msm_rpm_notify_sleep_chain(struct rpm_message_header *hdr,
 		struct msm_rpm_kvp_data *kvp)
 {
@@ -225,9 +228,6 @@
 	memcpy(handle->kvp[i].value, data, size);
 	handle->kvp[i].valid = true;
 
-	if (handle->msg_hdr.set == MSM_RPM_CTX_SLEEP_SET)
-		msm_rpm_notify_sleep_chain(&handle->msg_hdr, &handle->kvp[i]);
-
 	return 0;
 
 }
@@ -339,7 +339,7 @@
 
 	switch (event) {
 	case SMD_EVENT_DATA:
-		queue_work(msm_rpm_smd_wq, &pdata->work);
+		complete(&data_ready);
 		break;
 	case SMD_EVENT_OPEN:
 		complete(&pdata->smd_open);
@@ -354,6 +354,18 @@
 	}
 }
 
+bool msm_rpm_waiting_for_ack(void)
+{
+	bool ret;
+	unsigned long flags;
+
+	spin_lock_irqsave(&msm_rpm_list_lock, flags);
+	ret = list_empty(&msm_rpm_wait_list);
+	spin_unlock_irqrestore(&msm_rpm_list_lock, flags);
+
+	return !ret;
+}
+
 static struct msm_rpm_wait_data *msm_rpm_get_entry_from_msg_id(uint32_t msg_id)
 {
 	struct list_head *ptr;
@@ -517,17 +529,19 @@
 	int errno;
 	char buf[MAX_ERR_BUFFER_SIZE] = {0};
 
-	if (!spin_trylock(&msm_rpm_data.smd_lock_read))
-		return;
-	while (smd_is_pkt_avail(msm_rpm_data.ch_info)) {
-		if (msm_rpm_read_smd_data(buf)) {
-			break;
+	while (1) {
+		wait_for_completion(&data_ready);
+
+		spin_lock(&msm_rpm_data.smd_lock_read);
+		while (smd_is_pkt_avail(msm_rpm_data.ch_info)) {
+			if (msm_rpm_read_smd_data(buf))
+				break;
+			msg_id = msm_rpm_get_msg_id_from_ack(buf);
+			errno = msm_rpm_get_error_from_ack(buf);
+			msm_rpm_process_ack(msg_id, errno);
 		}
-		msg_id = msm_rpm_get_msg_id_from_ack(buf);
-		errno = msm_rpm_get_error_from_ack(buf);
-		msm_rpm_process_ack(msg_id, errno);
+		spin_unlock(&msm_rpm_data.smd_lock_read);
 	}
-	spin_unlock(&msm_rpm_data.smd_lock_read);
 }
 
 #define DEBUG_PRINT_BUFFER_SIZE 512
@@ -719,6 +733,11 @@
 
 		memcpy(tmpbuff, cdata->kvp[i].value, cdata->kvp[i].nbytes);
 		tmpbuff += cdata->kvp[i].nbytes;
+
+		if (cdata->msg_hdr.set == MSM_RPM_CTX_SLEEP_SET)
+			msm_rpm_notify_sleep_chain(&cdata->msg_hdr,
+					&cdata->kvp[i]);
+
 	}
 
 	if (msm_rpm_debug_mask
@@ -761,6 +780,10 @@
 	spin_unlock_irqrestore(&msm_rpm_data.smd_lock_write, flags);
 
 	if (ret == msg_size) {
+		trace_rpm_send_message(noirq, cdata->msg_hdr.set,
+				cdata->msg_hdr.resource_type,
+				cdata->msg_hdr.resource_id,
+				cdata->msg_hdr.msg_id);
 		for (i = 0; (i < cdata->write_idx); i++)
 			cdata->kvp[i].valid = false;
 		cdata->msg_hdr.data_len = 0;
@@ -816,6 +839,8 @@
 		return 0;
 
 	wait_for_completion(&elem->ack);
+	trace_rpm_ack_recd(0, msg_id);
+
 	msm_rpm_free_list_entry(elem);
 	return elem->errno;
 }
@@ -868,9 +893,14 @@
 	}
 
 	rc = elem->errno;
+	trace_rpm_ack_recd(1, msg_id);
+
 	msm_rpm_free_list_entry(elem);
 wait_ack_cleanup:
 	spin_unlock_irqrestore(&msm_rpm_data.smd_lock_read, flags);
+
+	if (smd_is_pkt_avail(msm_rpm_data.ch_info))
+		complete(&data_ready);
 	return rc;
 }
 EXPORT_SYMBOL(msm_rpm_wait_for_ack_noirq);
@@ -943,7 +973,7 @@
 
 static bool msm_rpm_set_standalone(void)
 {
-	if (machine_is_msm9625()) {
+	if (machine_is_msm9625() || machine_is_msm8974_rumi()) {
 		pr_warn("%s(): Running in standalone mode, requests "
 				"will not be sent to RPM\n", __func__);
 		standalone = true;
@@ -992,6 +1022,7 @@
 		msm_rpm_smd_wq = create_singlethread_workqueue("rpm-smd");
 		if (!msm_rpm_smd_wq)
 			return -EINVAL;
+		queue_work(msm_rpm_smd_wq, &msm_rpm_data.work);
 	}
 
 	of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev);
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_stats.c b/arch/arm/mach-msm/rpm_stats.c
index a831bd5..9a8b8ec 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,11 +22,14 @@
 #include <linux/slab.h>
 #include <linux/types.h>
 #include <linux/mm.h>
+#include <linux/types.h>
+#include <linux/of.h>
 #include <asm/uaccess.h>
-
+#include <asm/arch_timer.h>
 #include <mach/msm_iomap.h>
 #include "rpm_stats.h"
 
+
 enum {
 	ID_COUNTER,
 	ID_ACCUM_TIME_SCLK,
@@ -39,21 +42,111 @@
 };
 
 #define SCLK_HZ 32768
+#define MSM_ARCH_TIMER_FREQ 19200000
+
 struct msm_rpmstats_record{
 	char		name[32];
 	uint32_t	id;
 	uint32_t	val;
 };
-
 struct msm_rpmstats_private_data{
 	void __iomem *reg_base;
 	u32 num_records;
 	u32 read_idx;
 	u32 len;
-	char buf[128];
+	char buf[256];
 	struct msm_rpmstats_platform_data *platform_data;
 };
 
+struct msm_rpm_stats_data_v2 {
+	u32 stat_type;
+	u32 count;
+	u64 last_entered_at;
+	u64 last_exited_at;
+};
+
+static inline u64 get_time_in_sec(u64 counter)
+{
+	do_div(counter, MSM_ARCH_TIMER_FREQ);
+	return counter;
+}
+
+static inline u64 get_time_in_msec(u64 counter)
+{
+	do_div(counter, MSM_ARCH_TIMER_FREQ);
+	counter *= MSEC_PER_SEC;
+	return counter;
+}
+
+static inline int msm_rpmstats_append_data_to_buf(char *buf,
+		struct msm_rpm_stats_data_v2 *data, int buflength)
+{
+	char stat_type[5];
+	u64 time_in_last_mode;
+	u64 time_since_last_mode;
+
+	stat_type[4] = 0;
+	memcpy(stat_type, &data->stat_type, sizeof(u32));
+
+	time_in_last_mode = data->last_exited_at - data->last_entered_at;
+	time_in_last_mode = get_time_in_msec(time_in_last_mode);
+	time_since_last_mode = arch_counter_get_cntpct() - data->last_exited_at;
+	time_since_last_mode = get_time_in_sec(time_since_last_mode);
+
+	return  snprintf(buf , buflength,
+		"RPM Mode:%s\n\t count:%d\n time in last mode(msec):%llu\n"
+		"time since last mode(sec):%llu\n",
+		stat_type, data->count, time_in_last_mode,
+		time_since_last_mode);
+}
+
+static inline u32 msm_rpmstats_read_long_register_v2(void __iomem *regbase,
+		int index, int offset)
+{
+	return readl_relaxed(regbase + offset +
+			index * sizeof(struct msm_rpm_stats_data_v2));
+}
+
+static inline u64 msm_rpmstats_read_quad_register_v2(void __iomem *regbase,
+		int index, int offset)
+{
+	u64 dst;
+	memcpy_fromio(&dst,
+		regbase + offset + index * sizeof(struct msm_rpm_stats_data_v2),
+		8);
+	return dst;
+}
+
+static inline int msm_rpmstats_copy_stats_v2(
+			struct msm_rpmstats_private_data *prvdata)
+{
+	void __iomem *reg;
+	struct msm_rpm_stats_data_v2 data;
+	int i, length;
+
+	reg = prvdata->reg_base;
+
+	for (i = 0, length = 0; i < prvdata->num_records; i++) {
+
+		data.stat_type = msm_rpmstats_read_long_register_v2(reg, i,
+				offsetof(struct msm_rpm_stats_data_v2,
+					stat_type));
+		data.count = msm_rpmstats_read_long_register_v2(reg, i,
+				offsetof(struct msm_rpm_stats_data_v2, count));
+		data.last_entered_at = msm_rpmstats_read_quad_register_v2(reg,
+				i, offsetof(struct msm_rpm_stats_data_v2,
+					last_entered_at));
+		data.last_exited_at = msm_rpmstats_read_quad_register_v2(reg,
+				i, offsetof(struct msm_rpm_stats_data_v2,
+					last_exited_at));
+
+		length += msm_rpmstats_append_data_to_buf(prvdata->buf + length,
+				&data, sizeof(prvdata->buf) - length);
+		prvdata->read_idx++;
+	}
+	return length;
+}
+
 static inline unsigned long  msm_rpmstats_read_register(void __iomem *regbase,
 		int index, int offset)
 {
@@ -133,13 +226,19 @@
 	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);
+			else if (prvdata->platform_data->version == 2)
+				prvdata->len = msm_rpmstats_copy_stats_v2(
+						prvdata);
+			*ppos = 0;
 	}
 
 	return simple_read_from_buffer(bufu, count, ppos,
@@ -160,7 +259,8 @@
 		return -ENOMEM;
 	prvdata = file->private_data;
 
-	prvdata->reg_base = ioremap(pdata->phys_addr_base, pdata->phys_size);
+	prvdata->reg_base = ioremap_nocache(pdata->phys_addr_base,
+					pdata->phys_size);
 	if (!prvdata->reg_base) {
 		kfree(file->private_data);
 		prvdata = NULL;
@@ -172,6 +272,9 @@
 
 	prvdata->read_idx = prvdata->num_records =  prvdata->len = 0;
 	prvdata->platform_data = pdata;
+	if (pdata->version == 2)
+		prvdata->num_records = 2;
+
 	return 0;
 }
 
@@ -196,18 +299,53 @@
 
 static  int __devinit msm_rpmstats_probe(struct platform_device *pdev)
 {
-	struct dentry *dent;
+	struct dentry *dent = NULL;
 	struct msm_rpmstats_platform_data *pdata;
+	struct msm_rpmstats_platform_data *pd;
+	struct resource *res = NULL;
+	struct device_node *node = NULL;
+	int ret = 0;
 
-	pdata = pdev->dev.platform_data;
-	if (!pdata)
+	if (!pdev)
 		return -EINVAL;
-	dent = debugfs_create_file("rpm_stats", S_IRUGO, NULL,
-			pdev->dev.platform_data, &msm_rpmstats_fops);
 
-	if (!dent) {
-		pr_err("%s: ERROR debugfs_create_file failed\n", __func__);
+	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);
+	node = pdev->dev.of_node;
+	if (pdev->dev.platform_data) {
+		pd = pdev->dev.platform_data;
+		pdata->version = pd->version;
+
+	} else if (node)
+		ret = of_property_read_u32(node,
+			"qcom,sleep-stats-version", &pdata->version);
+
+	if (!ret) {
+
+		dent = debugfs_create_file("rpm_stats", S_IRUGO, NULL,
+				pdata, &msm_rpmstats_fops);
+
+		if (!dent) {
+			pr_err("%s: ERROR debugfs_create_file failed\n",
+					__func__);
+			kfree(pdata);
+			return -ENOMEM;
+		}
+
+	} else {
+		kfree(pdata);
+		return -EINVAL;
 	}
 	platform_set_drvdata(pdev, dent);
 	return 0;
@@ -222,12 +360,19 @@
 	platform_set_drvdata(pdev, NULL);
 	return 0;
 }
+
+static struct of_device_id rpm_stats_table[] = {
+	       {.compatible = "qcom,rpm-stats"},
+	       {},
+};
+
 static struct platform_driver msm_rpmstats_driver = {
 	.probe	= msm_rpmstats_probe,
 	.remove = __devexit_p(msm_rpmstats_remove),
 	.driver = {
 		.name = "msm_rpm_stat",
 		.owner = THIS_MODULE,
+		.of_match_table = rpm_stats_table,
 	},
 };
 static int __init msm_rpmstats_init(void)
diff --git a/arch/arm/mach-msm/rpm_stats.h b/arch/arm/mach-msm/rpm_stats.h
index a3beaa4..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,6 +19,7 @@
 struct msm_rpmstats_platform_data {
 	phys_addr_t phys_addr_base;
 	u32 phys_size;
+	u32 version;
 };
 
 struct msm_rpm_master_stats_platform_data {
diff --git a/arch/arm/mach-msm/saw-regulator.c b/arch/arm/mach-msm/saw-regulator.c
index 6762648..0a81a33 100644
--- a/arch/arm/mach-msm/saw-regulator.c
+++ b/arch/arm/mach-msm/saw-regulator.c
@@ -54,11 +54,17 @@
 	struct regulator_dev		*rdev;
 	char				*name;
 	int				uV;
+	int				last_set_uV;
+	unsigned			vlevel;
+	bool				online;
 };
 
 /* Minimum core operating voltage */
 #define MIN_CORE_VOLTAGE		950000
 
+/* Specifies an uninitialized voltage */
+#define INVALID_VOLTAGE			-1
+
 /* Specifies the PMIC internal slew rate in uV/us. */
 #define REGULATOR_SLEW_RATE		1250
 
@@ -69,12 +75,32 @@
 	return vreg->uV;
 }
 
+static int _set_voltage(struct regulator_dev *rdev)
+{
+	struct saw_vreg *vreg = rdev_get_drvdata(rdev);
+	int rc;
+
+	rc = msm_spm_set_vdd(rdev_get_id(rdev), vreg->vlevel);
+	if (!rc) {
+		if (vreg->uV > vreg->last_set_uV) {
+			/* Wait for voltage to stabalize. */
+			udelay((vreg->uV - vreg->last_set_uV) /
+						REGULATOR_SLEW_RATE);
+		}
+		vreg->last_set_uV = vreg->uV;
+	} else {
+		pr_err("%s: msm_spm_set_vdd failed %d\n", vreg->name, rc);
+		vreg->uV = vreg->last_set_uV;
+	}
+
+	return rc;
+}
+
 static int saw_set_voltage(struct regulator_dev *rdev, int min_uV, int max_uV,
 			   unsigned *selector)
 {
 	struct saw_vreg *vreg = rdev_get_drvdata(rdev);
 	int uV = min_uV;
-	int rc;
 	u8 vprog, band;
 
 	if (uV < FTSMPS_BAND1_UV_MIN && max_uV >= FTSMPS_BAND1_UV_MIN)
@@ -119,23 +145,51 @@
 		return -EINVAL;
 	}
 
-	rc = msm_spm_set_vdd(rdev_get_id(rdev), band | vprog);
-	if (!rc) {
-		if (uV > vreg->uV) {
-			/* Wait for voltage to stabalize. */
-			udelay((uV - vreg->uV) / REGULATOR_SLEW_RATE);
-		}
-		vreg->uV = uV;
-	} else {
-		pr_err("%s: msm_spm_set_vdd failed %d\n", vreg->name, rc);
-	}
+	vreg->vlevel = band | vprog;
+	vreg->uV = uV;
+
+	if (!vreg->online)
+		return 0;
+
+	return _set_voltage(rdev);
+}
+
+static int saw_enable(struct regulator_dev *rdev)
+{
+	struct saw_vreg *vreg = rdev_get_drvdata(rdev);
+	int rc = 0;
+
+	if (vreg->uV != vreg->last_set_uV)
+		rc = _set_voltage(rdev);
+
+	if (!rc)
+		vreg->online = true;
 
 	return rc;
 }
 
+static int saw_disable(struct regulator_dev *rdev)
+{
+	struct saw_vreg *vreg = rdev_get_drvdata(rdev);
+
+	vreg->online = false;
+
+	return 0;
+}
+
+static int saw_is_enabled(struct regulator_dev *rdev)
+{
+	struct saw_vreg *vreg = rdev_get_drvdata(rdev);
+
+	return vreg->online;
+}
+
 static struct regulator_ops saw_ops = {
 	.get_voltage = saw_get_voltage,
 	.set_voltage = saw_set_voltage,
+	.enable	     = saw_enable,
+	.disable     = saw_disable,
+	.is_enabled  = saw_is_enabled,
 };
 
 static int __devinit saw_probe(struct platform_device *pdev)
@@ -168,12 +222,13 @@
 		goto free_vreg;
 	}
 
-	vreg->desc.name  = vreg->name;
-	vreg->desc.id    = pdev->id;
-	vreg->desc.ops   = &saw_ops;
-	vreg->desc.type  = REGULATOR_VOLTAGE;
-	vreg->desc.owner = THIS_MODULE;
-	vreg->uV	 = MIN_CORE_VOLTAGE;
+	vreg->desc.name	  = vreg->name;
+	vreg->desc.id	  = pdev->id;
+	vreg->desc.ops	  = &saw_ops;
+	vreg->desc.type	  = REGULATOR_VOLTAGE;
+	vreg->desc.owner  = THIS_MODULE;
+	vreg->uV	  = INVALID_VOLTAGE;
+	vreg->last_set_uV = MIN_CORE_VOLTAGE;
 
 	vreg->rdev = regulator_register(&vreg->desc, &pdev->dev,
 							init_data, vreg, NULL);
@@ -233,5 +288,4 @@
 
 MODULE_LICENSE("GPL v2");
 MODULE_DESCRIPTION("SAW regulator driver");
-MODULE_VERSION("1.0");
 MODULE_ALIAS("platform:saw-regulator");
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..f4dae89 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);
@@ -203,12 +204,16 @@
 	return ret;
 }
 
-static u32 cacheline_size;
-
 static void scm_inv_range(unsigned long start, unsigned long end)
 {
+	u32 cacheline_size, ctr;
+
+	asm volatile("mrc p15, 0, %0, c0, c0, 1" : "=r" (ctr));
+	cacheline_size = 4 << ((ctr >> 16) & 0xf);
+
 	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");
@@ -442,13 +447,3 @@
 }
 EXPORT_SYMBOL(scm_get_feat_version);
 
-static int scm_init(void)
-{
-	u32 ctr;
-
-	asm volatile("mrc p15, 0, %0, c0, c0, 1" : "=r" (ctr));
-	cacheline_size =  4 << ((ctr >> 16) & 0xf);
-
-	return 0;
-}
-early_initcall(scm_init);
diff --git a/arch/arm/mach-msm/smd.c b/arch/arm/mach-msm/smd.c
index 71fb87c..7427899 100644
--- a/arch/arm/mach-msm/smd.c
+++ b/arch/arm/mach-msm/smd.c
@@ -36,6 +36,8 @@
 #include <linux/notifier.h>
 #include <linux/sort.h>
 #include <linux/suspend.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
 #include <mach/msm_smd.h>
 #include <mach/msm_iomap.h>
 #include <mach/system.h>
@@ -706,7 +708,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 +873,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 +884,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;
+			}
 		}
 	}
 
@@ -1012,6 +1018,7 @@
 	notify_dsp_smd();
 	notify_dsps_smd();
 	notify_wcnss_smd();
+	notify_rpm_smd();
 
 	/* change all remote states to CLOSED */
 	mutex_lock(&smd_probe_lock);
@@ -1027,6 +1034,7 @@
 	notify_dsp_smd();
 	notify_dsps_smd();
 	notify_wcnss_smd();
+	notify_rpm_smd();
 
 	SMD_DBG("%s: finished reset\n", __func__);
 }
@@ -3496,6 +3504,295 @@
 	return err_ret;
 }
 
+static int __devinit parse_smd_devicetree(struct device_node *node,
+						void *irq_out_base)
+{
+	uint32_t edge;
+	char *key;
+	int ret;
+	uint32_t irq_offset;
+	uint32_t irq_bitmask;
+	uint32_t irq_line;
+	unsigned long irq_flags = IRQF_TRIGGER_RISING;
+	const char *pilstr;
+	struct interrupt_config_item *private_irq;
+
+	key = "qcom,smd-edge";
+	ret = of_property_read_u32(node, key, &edge);
+	if (ret)
+		goto missing_key;
+	SMD_DBG("%s: %s = %d", __func__, key, edge);
+
+	key = "qcom,smd-irq-offset";
+	ret = of_property_read_u32(node, key, &irq_offset);
+	if (ret)
+		goto missing_key;
+	SMD_DBG("%s: %s = %x", __func__, key, irq_offset);
+
+	key = "qcom,smd-irq-bitmask";
+	ret = of_property_read_u32(node, key, &irq_bitmask);
+	if (ret)
+		goto missing_key;
+	SMD_DBG("%s: %s = %x", __func__, key, irq_bitmask);
+
+	key = "interrupts";
+	irq_line = irq_of_parse_and_map(node, 0);
+	if (!irq_line)
+		goto missing_key;
+	SMD_DBG("%s: %s = %d", __func__, key, irq_line);
+
+	key = "qcom,pil-string";
+	pilstr = of_get_property(node, key, NULL);
+	if (pilstr)
+		SMD_DBG("%s: %s = %s", __func__, key, pilstr);
+
+	key = "qcom,irq-no-suspend";
+	ret = of_property_read_bool(node, key);
+	if (ret)
+		irq_flags |= IRQF_NO_SUSPEND;
+
+	private_irq = &private_intr_config[edge_to_pids[edge].remote_pid].smd;
+	private_irq->out_bit_pos = irq_bitmask;
+	private_irq->out_offset = irq_offset;
+	private_irq->out_base = irq_out_base;
+	private_irq->irq_id = irq_line;
+
+	ret = request_irq(irq_line,
+				private_irq->irq_handler,
+				irq_flags,
+				"smd_dev",
+				NULL);
+	if (ret < 0) {
+		pr_err("%s: request_irq() failed on %d\n", __func__, irq_line);
+		return ret;
+	} else {
+		ret = enable_irq_wake(irq_line);
+		if (ret < 0)
+			pr_err("%s: enable_irq_wake() failed on %d\n", __func__,
+					irq_line);
+	}
+
+	if (pilstr)
+		strlcpy(edge_to_pids[edge].subsys_name, pilstr,
+						SMD_MAX_CH_NAME_LEN);
+
+	return 0;
+
+missing_key:
+	pr_err("%s: missing key: %s", __func__, key);
+	return -ENODEV;
+}
+
+static int __devinit parse_smsm_devicetree(struct device_node *node,
+						void *irq_out_base)
+{
+	uint32_t edge;
+	char *key;
+	int ret;
+	uint32_t irq_offset;
+	uint32_t irq_bitmask;
+	uint32_t irq_line;
+	struct interrupt_config_item *private_irq;
+
+	key = "qcom,smsm-edge";
+	ret = of_property_read_u32(node, key, &edge);
+	if (ret)
+		goto missing_key;
+	SMD_DBG("%s: %s = %d", __func__, key, edge);
+
+	key = "qcom,smsm-irq-offset";
+	ret = of_property_read_u32(node, key, &irq_offset);
+	if (ret)
+		goto missing_key;
+	SMD_DBG("%s: %s = %x", __func__, key, irq_offset);
+
+	key = "qcom,smsm-irq-bitmask";
+	ret = of_property_read_u32(node, key, &irq_bitmask);
+	if (ret)
+		goto missing_key;
+	SMD_DBG("%s: %s = %x", __func__, key, irq_bitmask);
+
+	key = "interrupts";
+	irq_line = irq_of_parse_and_map(node, 0);
+	if (!irq_line)
+		goto missing_key;
+	SMD_DBG("%s: %s = %d", __func__, key, irq_line);
+
+	private_irq = &private_intr_config[edge_to_pids[edge].remote_pid].smsm;
+	private_irq->out_bit_pos = irq_bitmask;
+	private_irq->out_offset = irq_offset;
+	private_irq->out_base = irq_out_base;
+	private_irq->irq_id = irq_line;
+
+	ret = request_irq(irq_line,
+				private_irq->irq_handler,
+				IRQF_TRIGGER_RISING,
+				"smsm_dev",
+				NULL);
+	if (ret < 0) {
+		pr_err("%s: request_irq() failed on %d\n", __func__, irq_line);
+		return ret;
+	} else {
+		ret = enable_irq_wake(irq_line);
+		if (ret < 0)
+			pr_err("%s: enable_irq_wake() failed on %d\n", __func__,
+					irq_line);
+	}
+
+	return 0;
+
+missing_key:
+	pr_err("%s: missing key: %s", __func__, key);
+	return -ENODEV;
+}
+
+static void __devinit unparse_smd_devicetree(struct device_node *node)
+{
+	uint32_t irq_line;
+
+	irq_line = irq_of_parse_and_map(node, 0);
+
+	free_irq(irq_line, NULL);
+}
+
+static void __devinit unparse_smsm_devicetree(struct device_node *node)
+{
+	uint32_t irq_line;
+
+	irq_line = irq_of_parse_and_map(node, 0);
+
+	free_irq(irq_line, NULL);
+}
+
+static int __devinit smd_core_devicetree_init(struct platform_device *pdev)
+{
+	char *key;
+	struct resource *r;
+	void *irq_out_base;
+	void *aux_mem_base;
+	uint32_t aux_mem_size;
+	int temp_string_size = 11; /* max 3 digit count */
+	char temp_string[temp_string_size];
+	int count;
+	struct device_node *node;
+	int ret;
+	const char *compatible;
+	int subnode_num = 0;
+
+	disable_smsm_reset_handshake = 1;
+
+	key = "irq-reg-base";
+	r = platform_get_resource_byname(pdev, IORESOURCE_MEM, key);
+	if (!r) {
+		pr_err("%s: missing '%s'\n", __func__, key);
+		return -ENODEV;
+	}
+	irq_out_base = (void *)(r->start);
+	SMD_DBG("%s: %s = %p", __func__, key, irq_out_base);
+
+	count = 1;
+	while (1) {
+		scnprintf(temp_string, temp_string_size, "aux-mem%d", count);
+		r = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+								temp_string);
+		if (!r)
+			break;
+
+		++num_smem_areas;
+		++count;
+		if (count > 999) {
+			pr_err("%s: max num aux mem regions reached\n",
+								__func__);
+			break;
+		}
+	}
+
+	if (num_smem_areas) {
+		smem_areas = kmalloc(sizeof(struct smem_area) * num_smem_areas,
+					GFP_KERNEL);
+		if (!smem_areas) {
+			pr_err("%s: smem areas kmalloc failed\n", __func__);
+			num_smem_areas = 0;
+			return -ENOMEM;
+		}
+		count = 1;
+		while (1) {
+			scnprintf(temp_string, temp_string_size, "aux-mem%d",
+									count);
+			r = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+								temp_string);
+			if (!r)
+				break;
+			aux_mem_base = (void *)(r->start);
+			aux_mem_size = (uint32_t)(resource_size(r));
+			SMD_DBG("%s: %s = %p %x", __func__, temp_string,
+					aux_mem_base, aux_mem_size);
+			smem_areas[count - 1].phys_addr = aux_mem_base;
+			smem_areas[count - 1].size = aux_mem_size;
+			smem_areas[count - 1].virt_addr = ioremap_nocache(
+				(unsigned long)(smem_areas[count-1].phys_addr),
+				smem_areas[count - 1].size);
+			if (!smem_areas[count - 1].virt_addr) {
+				pr_err("%s: ioremap_nocache() of addr:%p size: %x\n",
+					__func__,
+					smem_areas[count - 1].phys_addr,
+					smem_areas[count - 1].size);
+				ret = -ENOMEM;
+				goto free_smem_areas;
+			}
+
+			++count;
+			if (count > 999) {
+				pr_err("%s: max num aux mem regions reached\n",
+								__func__);
+				break;
+			}
+		}
+		sort(smem_areas, num_smem_areas,
+				sizeof(struct smem_area),
+				sort_cmp_func, NULL);
+	}
+
+	for_each_child_of_node(pdev->dev.of_node, node) {
+		compatible = of_get_property(node, "compatible", NULL);
+		if (!strcmp(compatible, "qcom,smd")) {
+			ret = parse_smd_devicetree(node, irq_out_base);
+			if (ret)
+				goto rollback_subnodes;
+		} else if (!strcmp(compatible, "qcom,smsm")) {
+			ret = parse_smsm_devicetree(node, irq_out_base);
+			if (ret)
+				goto rollback_subnodes;
+		} else {
+			pr_err("%s: invalid child node named: %s\n", __func__,
+							compatible);
+			ret = -ENODEV;
+			goto rollback_subnodes;
+		}
+		++subnode_num;
+	}
+
+	return 0;
+
+rollback_subnodes:
+	count = 0;
+	for_each_child_of_node(pdev->dev.of_node, node) {
+		if (count >= subnode_num)
+			break;
+		++count;
+		compatible = of_get_property(node, "compatible", NULL);
+		if (!strcmp(compatible, "qcom,smd"))
+			unparse_smd_devicetree(node);
+		else
+			unparse_smsm_devicetree(node);
+	}
+free_smem_areas:
+	num_smem_areas = 0;
+	kfree(smem_areas);
+	smem_areas = NULL;
+	return ret;
+}
+
 static int __devinit msm_smd_probe(struct platform_device *pdev)
 {
 	int ret;
@@ -3516,8 +3813,12 @@
 
 	if (pdev) {
 		if (pdev->dev.of_node) {
-			pr_err("SMD: Device tree not currently supported\n");
-			return -ENODEV;
+			ret = smd_core_devicetree_init(pdev);
+			if (ret) {
+				pr_err("%s: device tree init failed\n",
+								__func__);
+				return ret;
+			}
 		} else if (pdev->dev.platform_data) {
 			ret = smd_core_platform_init(pdev);
 			if (ret) {
@@ -3594,11 +3895,17 @@
 }
 late_initcall(modem_restart_late_init);
 
+static struct of_device_id msm_smem_match_table[] = {
+	{ .compatible = "qcom,smem" },
+	{},
+};
+
 static struct platform_driver msm_smd_driver = {
 	.probe = msm_smd_probe,
 	.driver = {
 		.name = MODULE_NAME,
 		.owner = THIS_MODULE,
+		.of_match_table = msm_smem_match_table,
 	},
 };
 
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_rpcrouter.c b/arch/arm/mach-msm/smd_rpcrouter.c
index 80a639c..ff68d81 100644
--- a/arch/arm/mach-msm/smd_rpcrouter.c
+++ b/arch/arm/mach-msm/smd_rpcrouter.c
@@ -306,6 +306,10 @@
 	struct msm_rpc_reply *reply, *reply_tmp;
 	unsigned long flags;
 
+	if (!xprt_info) {
+		pr_err("%s: Invalid xprt_info\n", __func__);
+		return;
+	}
 	spin_lock_irqsave(&local_endpoints_lock, flags);
 	/* remove all partial packets received */
 	list_for_each_entry(ept, &local_endpoints, list) {
@@ -541,12 +545,13 @@
 			D("%s: registering device %x\n",
 			  __func__, board_info->dev->prog);
 			list_del(&board_info->list);
+			spin_unlock_irqrestore(&rpc_board_dev_list_lock, flags);
 			rc = platform_device_register(&board_info->dev->pdev);
 			if (rc)
 				pr_err("%s: board dev register failed %d\n",
 				       __func__, rc);
 			kfree(board_info);
-			break;
+			return;
 		}
 	}
 	spin_unlock_irqrestore(&rpc_board_dev_list_lock, flags);
@@ -2154,6 +2159,7 @@
 	while (!list_empty(&xprt_info_list)) {
 		xprt_info = list_first_entry(&xprt_info_list,
 					struct rpcrouter_xprt_info, list);
+		modem_reset_cleanup(xprt_info);
 		xprt_info->abort_data_read = 1;
 		wake_up(&xprt_info->read_wait);
 		rpcrouter_send_control_msg(xprt_info, &ctl);
@@ -2164,6 +2170,8 @@
 		flush_workqueue(xprt_info->workqueue);
 		destroy_workqueue(xprt_info->workqueue);
 		wake_lock_destroy(&xprt_info->wakelock);
+		/*free memory*/
+		xprt_info->xprt->priv = 0;
 		kfree(xprt_info);
 
 		mutex_lock(&xprt_info_list_lock);
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 0beb952..2743547 100644
--- a/arch/arm/mach-msm/socinfo.c
+++ b/arch/arm/mach-msm/socinfo.c
@@ -260,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,
@@ -272,6 +273,7 @@
 
 	/* 9625 IDs */
 	[134] = MSM_CPU_9625,
+	[152] = MSM_CPU_9625,
 
 	/* 8960AB IDs */
 	[138] = MSM_CPU_8960AB,
@@ -283,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,
@@ -296,6 +299,18 @@
 	/* 8064AB IDs */
 	[153] = MSM_CPU_8064AB,
 
+	/* 8930AB IDs */
+	[154] = MSM_CPU_8930AB,
+	[155] = MSM_CPU_8930AB,
+	[156] = MSM_CPU_8930AB,
+	[157] = MSM_CPU_8930AB,
+
+	/* 8625Q IDs */
+	[168] = MSM_CPU_8625Q,
+	[169] = MSM_CPU_8625Q,
+	[170] = MSM_CPU_8625Q,
+
+
 	/* Uninitialized IDs are not known to run Linux.
 	   MSM_CPU_UNKNOWN is set to 0 to ensure these IDs are
 	   considered as unknown CPU. */
diff --git a/arch/arm/mach-msm/spm-v2.c b/arch/arm/mach-msm/spm-v2.c
index 17997d2..620aa1d 100644
--- a/arch/arm/mach-msm/spm-v2.c
+++ b/arch/arm/mach-msm/spm-v2.c
@@ -309,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);
@@ -333,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) { }
 
@@ -345,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;
@@ -355,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;
@@ -384,14 +399,18 @@
 			__func__, timeout_us);
 
 	/* Set AVS min/max */
-	msm_spm_drv_set_avs_vlevel(dev, vlevel);
-	msm_spm_drv_enable_avs(dev);
+	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);
-	pr_err("%s: failed %#x, remaining timeout %u us, vlevel %#x\n",
+	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;
 }
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 4cdfcf8..a353ce0 100644
--- a/arch/arm/mach-msm/spm.h
+++ b/arch/arm/mach-msm/spm.h
@@ -127,93 +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);
-
-/** 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);
 
 /* 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
diff --git a/arch/arm/mach-msm/spm_devices.c b/arch/arm/mach-msm/spm_devices.c
index 3fe3bd7..b378d3b 100644
--- a/arch/arm/mach-msm/spm_devices.c
+++ b/arch/arm/mach-msm/spm_devices.c
@@ -57,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;
@@ -91,6 +96,11 @@
 }
 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;
@@ -166,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;
@@ -208,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);
@@ -215,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;
@@ -238,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(
@@ -251,12 +280,20 @@
 }
 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_pmic_data(&msm_spm_l2_device.reg_data,
@@ -264,6 +301,10 @@
 }
 EXPORT_SYMBOL(msm_spm_apcs_set_phase);
 
+/** 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,
@@ -271,7 +312,10 @@
 }
 EXPORT_SYMBOL(msm_spm_enable_fts_lpm);
 
-/* Board file init function */
+/**
+ * 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);
@@ -453,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/subsystem_restart.c b/arch/arm/mach-msm/subsystem_restart.c
index a58f2ab..ea9337d 100644
--- a/arch/arm/mach-msm/subsystem_restart.c
+++ b/arch/arm/mach-msm/subsystem_restart.c
@@ -33,7 +33,6 @@
 
 #include <asm/current.h>
 
-#include <mach/peripheral-loader.h>
 #include <mach/socinfo.h>
 #include <mach/subsystem_notif.h>
 #include <mach/subsystem_restart.h>
@@ -139,7 +138,7 @@
 	struct module *owner;
 	int count;
 	int id;
-	void *restart_order;
+	struct subsys_soc_restart_order *restart_order;
 #ifdef CONFIG_DEBUG_FS
 	struct dentry *dentry;
 #endif
@@ -178,6 +177,20 @@
 	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),
@@ -214,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);
 
@@ -444,6 +457,127 @@
 	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,
@@ -513,17 +647,12 @@
 {
 	struct subsys_desc *desc = dev->desc;
 	const char *name = dev->desc->name;
-	struct subsys_soc_restart_order *order = dev->restart_order;
 	struct subsys_tracking *track;
 	unsigned long flags;
 
-	if (restart_level != RESET_SUBSYS_INDEPENDENT && order)
-		track = &order->track;
-	else
-		track = &dev->track;
-
 	pr_debug("Restarting %s [level=%d]!\n", desc->name, restart_level);
 
+	track = subsys_get_track(dev);
 	/*
 	 * Allow drivers to call subsystem_restart{_dev}() as many times as
 	 * they want up until the point where the subsystem is shutdown.
@@ -555,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);
 
@@ -620,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;
 	}
@@ -692,7 +838,6 @@
 	subsys->dev.parent = desc->dev;
 	subsys->dev.bus = &subsys_bus_type;
 	subsys->dev.release = subsys_device_release;
-	subsys->track.state = SUBSYS_ONLINE; /* Until proper refcounting */
 
 	subsys->notify = subsys_notif_add_subsys(desc->name);
 	subsys->restart_order = update_restart_order(subsys);
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 b61604a..212ad77 100644
--- a/arch/arm/mach-msm/timer.c
+++ b/arch/arm/mach-msm/timer.c
@@ -23,12 +23,14 @@
 #include <linux/delay.h>
 #include <linux/io.h>
 #include <linux/percpu.h>
+#include <linux/mm.h>
 
 #include <asm/localtimer.h>
 #include <asm/mach/time.h>
 #include <asm/hardware/gic.h>
 #include <asm/sched_clock.h>
 #include <asm/smp_plat.h>
+#include <asm/user_accessible_timer.h>
 #include <mach/msm_iomap.h>
 #include <mach/irqs.h>
 #include <mach/socinfo.h>
@@ -1010,6 +1012,19 @@
 };
 #endif /* CONFIG_LOCAL_TIMERS */
 
+#ifdef CONFIG_ARCH_MSM8625
+static void fixup_msm8625_timer(void)
+{
+	struct msm_clock *dgt = &msm_clocks[MSM_CLOCK_DGT];
+	struct msm_clock *gpt = &msm_clocks[MSM_CLOCK_GPT];
+	dgt->irq = MSM8625_INT_DEBUG_TIMER_EXP;
+	gpt->irq = MSM8625_INT_GP_TIMER_EXP;
+	global_timer_offset =  MSM_TMR0_BASE - MSM_TMR_BASE;
+}
+#else
+static inline void fixup_msm8625_timer(void) { };
+#endif
+
 static void __init msm_timer_init(void)
 {
 	int i;
@@ -1030,11 +1045,8 @@
 		gpt->flags |= MSM_CLOCK_FLAGS_UNSTABLE_COUNT
 			   |  MSM_CLOCK_FLAGS_ODD_MATCH_WRITE
 			   |  MSM_CLOCK_FLAGS_DELAYED_WRITE_POST;
-		if (cpu_is_msm8625()) {
-			dgt->irq = MSM8625_INT_DEBUG_TIMER_EXP;
-			gpt->irq = MSM8625_INT_GP_TIMER_EXP;
-			global_timer_offset =  MSM_TMR0_BASE - MSM_TMR_BASE;
-		}
+		if (cpu_is_msm8625())
+			fixup_msm8625_timer();
 	} else if (cpu_is_qsd8x50()) {
 		dgt->freq = 4800000;
 		gpt->regbase = MSM_TMR_BASE;
@@ -1161,6 +1173,16 @@
 	}
 	msm_sched_clock_init();
 
+	if (use_user_accessible_timers()) {
+		if (cpu_is_msm8960() || cpu_is_msm8930() || cpu_is_apq8064()) {
+			struct msm_clock *gtclock = &msm_clocks[MSM_CLOCK_GPT];
+			void __iomem *addr = gtclock->regbase +
+				TIMER_COUNT_VAL + global_timer_offset;
+			setup_user_timer_offset(virt_to_phys(addr)&0xfff);
+			set_user_accessible_timer_flag(true);
+		}
+	}
+
 #ifdef ARCH_HAS_READ_CURRENT_TIMER
 	if (is_smp()) {
 		__raw_writel(1,
diff --git a/arch/arm/mach-msm/timer_page.c b/arch/arm/mach-msm/timer_page.c
new file mode 100644
index 0000000..24d2a35
--- /dev/null
+++ b/arch/arm/mach-msm/timer_page.c
@@ -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.
+ */
+
+#include <linux/mm.h>
+#include <linux/export.h>
+#include <asm/user_accessible_timer.h>
+#include "mach/socinfo.h"
+#include "mach/msm_iomap.h"
+
+#include "timer.h"
+
+inline int get_timer_page_address(void)
+{
+	if (!use_user_accessible_timers())
+		return ARM_USER_ACCESSIBLE_TIMERS_INVALID_PAGE;
+
+	if (cpu_is_msm8960())
+		return MSM8960_TMR0_PHYS;
+	else if (cpu_is_msm8930())
+		return MSM8930_TMR0_PHYS;
+	else if (cpu_is_apq8064())
+		return APQ8064_TMR0_PHYS;
+	else
+		return ARM_USER_ACCESSIBLE_TIMERS_INVALID_PAGE;
+}
+EXPORT_SYMBOL(get_timer_page_address);
+
diff --git a/arch/arm/mach-msm/trace_msm_low_power.h b/arch/arm/mach-msm/trace_msm_low_power.h
new file mode 100644
index 0000000..4e9da85
--- /dev/null
+++ b/arch/arm/mach-msm/trace_msm_low_power.h
@@ -0,0 +1,154 @@
+/* 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.
+ */
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM msm_low_power
+
+#if !defined(_TRACE_MSM_LOW_POWER_H_) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_MSM_LOW_POWER_H_
+
+#include <linux/tracepoint.h>
+
+DECLARE_EVENT_CLASS(msm_pm_enter,
+
+	TP_PROTO(unsigned int cpu, uint32_t latency,
+		uint32_t sleep_us, uint32_t wake_up),
+
+	TP_ARGS(cpu, latency, sleep_us, wake_up),
+
+	TP_STRUCT__entry(
+		__field(unsigned int, cpu)
+		__field(uint32_t, latency)
+		__field(uint32_t, sleep_us)
+		__field(uint32_t, wake_up)
+	),
+
+	TP_fast_assign(
+		__entry->cpu = cpu;
+		__entry->latency = latency;
+		__entry->sleep_us = sleep_us;
+		__entry->wake_up = wake_up;
+	),
+
+	TP_printk("cpu: %u latency: %uus sleep: %uus wake_up: %u",
+		__entry->cpu,
+		__entry->latency,
+		__entry->sleep_us,
+		__entry->wake_up)
+);
+
+DEFINE_EVENT(msm_pm_enter, msm_pm_enter_pc,
+
+	TP_PROTO(unsigned int cpu, uint32_t latency,
+		uint32_t sleep_us, uint32_t wake_up),
+
+	TP_ARGS(cpu, latency, sleep_us, wake_up)
+);
+
+DEFINE_EVENT(msm_pm_enter, msm_pm_enter_ret,
+
+	TP_PROTO(unsigned int cpu, uint32_t latency,
+		uint32_t sleep_us, uint32_t wake_up),
+
+	TP_ARGS(cpu, latency, sleep_us, wake_up)
+);
+
+DEFINE_EVENT(msm_pm_enter, msm_pm_enter_spc,
+
+	TP_PROTO(unsigned int cpu, uint32_t latency,
+		uint32_t sleep_us, uint32_t wake_up),
+
+	TP_ARGS(cpu, latency, sleep_us, wake_up)
+);
+
+DEFINE_EVENT(msm_pm_enter, msm_pm_enter_wfi,
+
+	TP_PROTO(unsigned int cpu, uint32_t latency,
+		uint32_t sleep_us, uint32_t wake_up),
+
+	TP_ARGS(cpu, latency, sleep_us, wake_up)
+);
+
+DECLARE_EVENT_CLASS(msm_pm_exit,
+
+	TP_PROTO(unsigned int cpu, bool success),
+
+	TP_ARGS(cpu, success),
+
+	TP_STRUCT__entry(
+		__field(unsigned int , cpu)
+		__field(int, success)
+	),
+
+	TP_fast_assign(
+		__entry->cpu = cpu;
+		__entry->success = success;
+	),
+
+	TP_printk("cpu:%u success:%d",
+			__entry->cpu,
+			__entry->success)
+);
+
+DEFINE_EVENT(msm_pm_exit, msm_pm_exit_pc,
+
+	TP_PROTO(unsigned int cpu, bool success),
+
+	TP_ARGS(cpu, success)
+);
+
+DEFINE_EVENT(msm_pm_exit, msm_pm_exit_ret,
+
+	TP_PROTO(unsigned int cpu, bool success),
+
+	TP_ARGS(cpu, success)
+);
+
+DEFINE_EVENT(msm_pm_exit, msm_pm_exit_spc,
+
+	TP_PROTO(unsigned int cpu, bool success),
+
+	TP_ARGS(cpu, success)
+);
+
+DEFINE_EVENT(msm_pm_exit, msm_pm_exit_wfi,
+
+	TP_PROTO(unsigned int cpu, bool success),
+
+	TP_ARGS(cpu, success)
+);
+
+TRACE_EVENT(lpm_resources,
+
+	TP_PROTO(uint32_t sleep_value , char *name),
+
+	TP_ARGS(sleep_value, name),
+
+	TP_STRUCT__entry(
+		__field(uint32_t , sleep_value)
+		__string(name, name)
+	),
+
+	TP_fast_assign(
+		__entry->sleep_value = sleep_value;
+		__assign_str(name, name);
+	),
+
+	TP_printk("name:%s sleep_value:%d",
+			 __get_str(name),
+			__entry->sleep_value)
+);
+#endif
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH .
+#define TRACE_INCLUDE_FILE trace_msm_low_power
+#include <trace/define_trace.h>
diff --git a/arch/arm/mach-msm/trace_rpm_smd.h b/arch/arm/mach-msm/trace_rpm_smd.h
new file mode 100644
index 0000000..eff4860
--- /dev/null
+++ b/arch/arm/mach-msm/trace_rpm_smd.h
@@ -0,0 +1,79 @@
+/* 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.
+ */
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM rpm_smd
+
+#if !defined(_TRACE_RPM_SMD_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_RPM_SMD_H
+
+#include <linux/tracepoint.h>
+
+TRACE_EVENT(rpm_ack_recd,
+
+	TP_PROTO(unsigned int irq, unsigned int msg_id),
+
+	TP_ARGS(irq, msg_id),
+
+	TP_STRUCT__entry(
+		__field(int, irq)
+		__field(int, msg_id)
+	),
+
+	TP_fast_assign(
+		__entry->irq = irq;
+		__entry->msg_id = msg_id;
+	),
+
+	TP_printk("ctx:%s id:%d",
+		__entry->irq ? "noslp" : "sleep",
+		__entry->msg_id)
+);
+
+TRACE_EVENT(rpm_send_message,
+
+	TP_PROTO(unsigned int irq, unsigned int set, unsigned int rsc_type,
+		unsigned int rsc_id, unsigned int msg_id),
+
+	TP_ARGS(irq, set, rsc_type, rsc_id, msg_id),
+
+	TP_STRUCT__entry(
+		__field(u32, irq)
+		__field(u32, set)
+		__field(u32, rsc_type)
+		__field(u32, rsc_id)
+		__field(u32, msg_id)
+		__array(char, name, 5)
+	),
+
+	TP_fast_assign(
+		__entry->irq	= irq;
+		__entry->name[4] = 0;
+		__entry->set = set;
+		__entry->rsc_type = rsc_type;
+		__entry->rsc_id = rsc_id;
+		__entry->msg_id = msg_id;
+		memcpy(__entry->name, &rsc_type, sizeof(uint32_t));
+
+	),
+
+	TP_printk("ctx:%s set:%s rsc_type:0x%08x(%s), rsc_id:0x%08x, id:%d",
+			__entry->irq ? "noslp" : "sleep",
+			__entry->set ? "slp" : "act",
+			__entry->rsc_type, __entry->name,
+			__entry->rsc_id, __entry->msg_id)
+);
+#endif
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH .
+#define TRACE_INCLUDE_FILE trace_rpm_smd
+#include <trace/define_trace.h>
diff --git a/arch/arm/mach-msm/wdog_debug.c b/arch/arm/mach-msm/wdog_debug.c
index 08dd9ce..8b39d26 100644
--- a/arch/arm/mach-msm/wdog_debug.c
+++ b/arch/arm/mach-msm/wdog_debug.c
@@ -43,6 +43,11 @@
 	value = readl_relaxed(wdog_data->base + GCC_WDOG_DEBUG_OFFSET);
 	value &= ~BIT(WDOG_DEBUG_EN);
 	writel_relaxed(value, wdog_data->base + GCC_WDOG_DEBUG_OFFSET);
+
+	/* Ensure the WDOG_DEBUG_EN status has changed */
+	while (readl_relaxed(wdog_data->base + GCC_WDOG_DEBUG_OFFSET) &
+		BIT(WDOG_DEBUG_EN))
+			;
 }
 EXPORT_SYMBOL(msm_disable_wdog_debug);
 
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
index ad75143..191060f 100644
--- a/arch/arm/mm/cache-pl310-erp.c
+++ b/arch/arm/mm/cache-pl310-erp.c
@@ -34,6 +34,7 @@
 	unsigned int slverr;
 	unsigned int decerr;
 	void __iomem *base;
+	unsigned int intr_mask_reg;
 };
 
 #define ECNTR	BIT(0)
@@ -128,19 +129,20 @@
 
 static void pl310_mask_int(struct pl310_drv_data *p, bool enable)
 {
-	uint16_t mask;
-
+	/* L2CC register contents needs to be saved
+	 * as it's power rail will be removed during suspend
+	 */
 	if (enable)
-		mask = 0x1FF;
+		p->intr_mask_reg = 0x1FF;
 	else
-		mask = 0x0;
+		p->intr_mask_reg = 0x0;
 
-	writel_relaxed(mask, p->base + L2X0_INTR_MASK);
+	writel_relaxed(p->intr_mask_reg, p->base + L2X0_INTR_MASK);
 
 	/* Make sure Mask is updated */
 	mb();
 
-	pr_debug("Mask interrupt %x\n",
+	pr_debug("Mask interrupt 0%x\n",
 			readl_relaxed(p->base + L2X0_INTR_MASK));
 }
 
@@ -258,12 +260,42 @@
 	return 0;
 }
 
+#ifdef CONFIG_PM
+static int pl310_suspend(struct device *dev)
+{
+	struct pl310_drv_data *p = dev_get_drvdata(dev);
+
+	disable_irq(p->irq);
+
+	return 0;
+}
+
+static int pl310_resume_early(struct device *dev)
+{
+	struct pl310_drv_data *p = dev_get_drvdata(dev);
+
+	pl310_mask_int(p, true);
+
+	enable_irq(p->irq);
+
+	return 0;
+}
+
+static const struct dev_pm_ops pl310_cache_pm_ops = {
+	.suspend = pl310_suspend,
+	.resume_early = pl310_resume_early,
+};
+#endif
+
 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,
+#ifdef CONFIG_PM
+		.pm = &pl310_cache_pm_ops,
+#endif
 	},
 };
 
diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c
index 8404601..afaa39d 100644
--- a/arch/arm/mm/dma-mapping.c
+++ b/arch/arm/mm/dma-mapping.c
@@ -314,7 +314,8 @@
 core_initcall(consistent_init);
 
 static void *__alloc_from_contiguous(struct device *dev, size_t size,
-				     pgprot_t prot, struct page **ret_page);
+				     pgprot_t prot, struct page **ret_page,
+				     bool no_kernel_mapping);
 
 static struct arm_vmregion_head coherent_head = {
 	.vm_lock	= __SPIN_LOCK_UNLOCKED(&coherent_head.vm_lock),
@@ -343,7 +344,7 @@
 	if (!IS_ENABLED(CONFIG_CMA))
 		return 0;
 
-	ptr = __alloc_from_contiguous(NULL, size, prot, &page);
+	ptr = __alloc_from_contiguous(NULL, size, prot, &page, false);
 	if (ptr) {
 		coherent_head.vm_start = (unsigned long) ptr;
 		coherent_head.vm_end = (unsigned long) ptr + size;
@@ -522,12 +523,27 @@
 	return 0;
 }
 
-static void __dma_remap(struct page *page, size_t size, pgprot_t prot)
+static int __dma_clear_pte(pte_t *pte, pgtable_t token, unsigned long addr,
+			    void *data)
+{
+	pte_clear(&init_mm, addr, pte);
+	return 0;
+}
+
+static void __dma_remap(struct page *page, size_t size, pgprot_t prot,
+			bool no_kernel_map)
 {
 	unsigned long start = (unsigned long) page_address(page);
 	unsigned end = start + size;
+	int (*func)(pte_t *pte, pgtable_t token, unsigned long addr,
+			    void *data);
 
-	apply_to_page_range(&init_mm, start, size, __dma_update_pte, &prot);
+	if (no_kernel_map)
+		func = __dma_clear_pte;
+	else
+		func = __dma_update_pte;
+
+	apply_to_page_range(&init_mm, start, size, func, &prot);
 	dsb();
 	flush_tlb_kernel_range(start, end);
 }
@@ -604,7 +620,8 @@
 }
 
 static void *__alloc_from_contiguous(struct device *dev, size_t size,
-				     pgprot_t prot, struct page **ret_page)
+				     pgprot_t prot, struct page **ret_page,
+				     bool no_kernel_mapping)
 {
 	unsigned long order = get_order(size);
 	size_t count = size >> PAGE_SHIFT;
@@ -615,7 +632,7 @@
 		return NULL;
 
 	__dma_clear_buffer(page, size);
-	__dma_remap(page, size, prot);
+	__dma_remap(page, size, prot, no_kernel_mapping);
 
 	*ret_page = page;
 	return page_address(page);
@@ -624,15 +641,20 @@
 static void __free_from_contiguous(struct device *dev, struct page *page,
 				   size_t size)
 {
-	__dma_remap(page, size, pgprot_kernel);
+	__dma_remap(page, size, pgprot_kernel, false);
 	dma_release_from_contiguous(dev, page, size >> PAGE_SHIFT);
 }
 
 static inline pgprot_t __get_dma_pgprot(struct dma_attrs *attrs, pgprot_t prot)
 {
-	prot = dma_get_attr(DMA_ATTR_WRITE_COMBINE, attrs) ?
-			    pgprot_writecombine(prot) :
-			    pgprot_dmacoherent(prot);
+	if (dma_get_attr(DMA_ATTR_WRITE_COMBINE, attrs))
+		prot = pgprot_writecombine(prot);
+	else if (dma_get_attr(DMA_ATTR_STRONGLY_ORDERED, attrs))
+		prot = pgprot_stronglyordered(prot);
+	/* if non-consistent just pass back what was given */
+	else if (!dma_get_attr(DMA_ATTR_NON_CONSISTENT, attrs))
+		prot = pgprot_dmacoherent(prot);
+
 	return prot;
 }
 
@@ -644,7 +666,7 @@
 
 #define __alloc_remap_buffer(dev, size, gfp, prot, ret, c)	NULL
 #define __alloc_from_pool(dev, size, ret_page, c)		NULL
-#define __alloc_from_contiguous(dev, size, prot, ret)		NULL
+#define __alloc_from_contiguous(dev, size, prot, ret, w)	NULL
 #define __free_from_pool(cpu_addr, size)			0
 #define __free_from_contiguous(dev, page, size)			do { } while (0)
 #define __dma_free_remap(cpu_addr, size)			do { } while (0)
@@ -667,7 +689,8 @@
 
 
 static void *__dma_alloc(struct device *dev, size_t size, dma_addr_t *handle,
-			 gfp_t gfp, pgprot_t prot, const void *caller)
+			 gfp_t gfp, pgprot_t prot, const void *caller,
+			 bool no_kernel_mapping)
 {
 	u64 mask = get_coherent_dma_mask(dev);
 	struct page *page;
@@ -707,7 +730,8 @@
 	else if (gfp & GFP_ATOMIC)
 		addr = __alloc_from_pool(dev, size, &page, caller);
 	else
-		addr = __alloc_from_contiguous(dev, size, prot, &page);
+		addr = __alloc_from_contiguous(dev, size, prot, &page,
+						no_kernel_mapping);
 
 	if (addr)
 		*handle = pfn_to_dma(dev, page_to_pfn(page));
@@ -724,12 +748,14 @@
 {
 	pgprot_t prot = __get_dma_pgprot(attrs, pgprot_kernel);
 	void *memory;
+	bool no_kernel_mapping = dma_get_attr(DMA_ATTR_NO_KERNEL_MAPPING,
+					attrs);
 
 	if (dma_alloc_from_coherent(dev, size, handle, &memory))
 		return memory;
 
 	return __dma_alloc(dev, size, handle, gfp, prot,
-			   __builtin_return_address(0));
+			   __builtin_return_address(0), no_kernel_mapping);
 }
 
 /*
diff --git a/arch/arm/mm/init.c b/arch/arm/mm/init.c
index 57f41ca..0ebc2b9 100644
--- a/arch/arm/mm/init.c
+++ b/arch/arm/mm/init.c
@@ -375,29 +375,61 @@
 	return cmp < 0 ? -1 : cmp > 0 ? 1 : 0;
 }
 
+unsigned long memory_hole_offset;
+EXPORT_SYMBOL(memory_hole_offset);
+unsigned long memory_hole_start;
+EXPORT_SYMBOL(memory_hole_start);
+unsigned long memory_hole_end;
+EXPORT_SYMBOL(memory_hole_end);
+
 #ifdef CONFIG_DONT_MAP_HOLE_AFTER_MEMBANK0
-unsigned long membank0_size;
-EXPORT_SYMBOL(membank0_size);
-unsigned long membank1_start;
-EXPORT_SYMBOL(membank1_start);
-
-void __init find_membank0_hole(void)
+void find_memory_hole(void)
 {
-	sort(&meminfo.bank, meminfo.nr_banks,
-		sizeof(meminfo.bank[0]), meminfo_cmp, NULL);
+	int i;
+	unsigned long hole_start;
+	unsigned long hole_size;
 
-	membank0_size = meminfo.bank[0].size;
-	membank1_start = meminfo.bank[1].start;
+	/*
+	 * Find the start and end of the hole, using meminfo
+	 * if it hasnt been found already.
+	 */
+	if (memory_hole_start == 0 && memory_hole_end == 0) {
+		for (i = 0; i < (meminfo.nr_banks - 1); i++) {
+			if ((meminfo.bank[i].start + meminfo.bank[i].size) !=
+						meminfo.bank[i+1].start) {
+				if (meminfo.bank[i].start + meminfo.bank[i].size
+							<= MAX_HOLE_ADDRESS) {
+
+					hole_start = meminfo.bank[i].start +
+							meminfo.bank[i].size;
+					hole_size = meminfo.bank[i+1].start -
+								hole_start;
+
+					if (memory_hole_start == 0 &&
+							memory_hole_end == 0) {
+						memory_hole_start = hole_start;
+						memory_hole_end = hole_start +
+								hole_size;
+					} else if ((memory_hole_end -
+					memory_hole_start) <= hole_size) {
+						memory_hole_start = hole_start;
+						memory_hole_end = hole_start +
+								hole_size;
+					}
+				}
+			}
+		}
+	}
+	memory_hole_offset = memory_hole_start - PHYS_OFFSET;
 }
+
 #endif
 
 void __init arm_memblock_init(struct meminfo *mi, struct machine_desc *mdesc)
 {
 	int i;
 
-#ifndef CONFIG_DONT_MAP_HOLE_AFTER_MEMBANK0
 	sort(&meminfo.bank, meminfo.nr_banks, sizeof(meminfo.bank[0]), meminfo_cmp, NULL);
-#endif
 
 	for (i = 0; i < mi->nr_banks; i++)
 		memblock_add(mi->bank[i].start, mi->bank[i].size);
@@ -771,6 +803,9 @@
 
 	printk(KERN_NOTICE "Virtual kernel memory layout:\n"
 			"    vector  : 0x%08lx - 0x%08lx   (%4ld kB)\n"
+#ifdef CONFIG_ARM_USE_USER_ACCESSIBLE_TIMERS
+			"    timers  : 0x%08lx - 0x%08lx   (%4ld kB)\n"
+#endif
 #ifdef CONFIG_HAVE_TCM
 			"    DTCM    : 0x%08lx - 0x%08lx   (%4ld kB)\n"
 			"    ITCM    : 0x%08lx - 0x%08lx   (%4ld kB)\n"
@@ -791,6 +826,11 @@
 
 			MLK(UL(CONFIG_VECTORS_BASE), UL(CONFIG_VECTORS_BASE) +
 				(PAGE_SIZE)),
+#ifdef CONFIG_ARM_USE_USER_ACCESSIBLE_TIMERS
+			MLK(UL(CONFIG_ARM_USER_ACCESSIBLE_TIMER_BASE),
+				UL(CONFIG_ARM_USER_ACCESSIBLE_TIMER_BASE)
+					+ (PAGE_SIZE)),
+#endif
 #ifdef CONFIG_HAVE_TCM
 			MLK(DTCM_OFFSET, (unsigned long) dtcm_end),
 			MLK(ITCM_OFFSET, (unsigned long) itcm_end),
diff --git a/arch/arm/mm/mmu.c b/arch/arm/mm/mmu.c
index bae23b0..8575f78 100644
--- a/arch/arm/mm/mmu.c
+++ b/arch/arm/mm/mmu.c
@@ -33,6 +33,8 @@
 #include <asm/mach/arch.h>
 #include <asm/mach/map.h>
 
+#include <asm/user_accessible_timer.h>
+
 #include "mm.h"
 
 /*
@@ -309,6 +311,13 @@
 		.prot_l1   = PMD_TYPE_TABLE,
 		.domain    = DOMAIN_KERNEL,
 	},
+	[MT_DEVICE_USER_ACCESSIBLE] = {
+		.prot_pte  = PROT_PTE_DEVICE | L_PTE_MT_DEV_SHARED |
+				L_PTE_SHARED | L_PTE_USER | L_PTE_RDONLY,
+		.prot_l1   = PMD_TYPE_TABLE,
+		.prot_sect = PROT_SECT_DEVICE | PMD_SECT_S,
+		.domain    = DOMAIN_IO,
+	},
 };
 
 const struct mem_type *get_mem_type(unsigned int type)
@@ -764,7 +773,9 @@
 	const struct mem_type *type;
 	pgd_t *pgd;
 
-	if (md->virtual != vectors_base() && md->virtual < TASK_SIZE) {
+	if ((md->virtual != vectors_base() &&
+		md->virtual != get_user_accessible_timers_base()) &&
+			md->virtual < TASK_SIZE) {
 		printk(KERN_WARNING "BUG: not creating mapping for 0x%08llx"
 		       " at 0x%08lx in user region\n",
 		       (long long)__pfn_to_phys((u64)md->pfn), md->virtual);
@@ -950,7 +961,7 @@
 	int i, j, highmem = 0;
 
 #ifdef CONFIG_DONT_MAP_HOLE_AFTER_MEMBANK0
-	find_membank0_hole();
+	find_memory_hole();
 #endif
 
 #if (defined CONFIG_HIGHMEM) && (defined CONFIG_FIX_MOVABLE_ZONE)
@@ -1203,6 +1214,20 @@
 		mdesc->map_io();
 	fill_pmd_gaps();
 
+	if (use_user_accessible_timers()) {
+		/*
+		 * Generate a mapping for the timer page.
+		 */
+		int page_addr = get_timer_page_address();
+		if (page_addr != ARM_USER_ACCESSIBLE_TIMERS_INVALID_PAGE) {
+			map.pfn = __phys_to_pfn(page_addr);
+			map.virtual = CONFIG_ARM_USER_ACCESSIBLE_TIMER_BASE;
+			map.length = PAGE_SIZE;
+			map.type = MT_DEVICE_USER_ACCESSIBLE;
+			create_mapping(&map, false);
+		}
+	}
+
 	/*
 	 * Finally flush the caches and tlb to ensure that we're in a
 	 * consistent state wrt the writebuffer.  This also ensures that
diff --git a/block/Kconfig.iosched b/block/Kconfig.iosched
index 5fd98ea..5751d28 100644
--- a/block/Kconfig.iosched
+++ b/block/Kconfig.iosched
@@ -15,7 +15,7 @@
 config IOSCHED_TEST
 	tristate "Test I/O scheduler"
 	depends on DEBUG_FS
-	default m
+	default y
 	---help---
 	  The test I/O scheduler is a duplicate of the noop scheduler with
 	  addition of test utlity.
diff --git a/block/test-iosched.c b/block/test-iosched.c
index 0a033dc..71e8669 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;
@@ -660,7 +663,7 @@
 			test_name = ptd->test_info.get_test_case_str_fn(ptd);
 		else
 			test_name = "Unknown testcase";
-		test_pr_info("%s: Starting test %s\n", __func__, test_name);
+		test_pr_info("%s: Starting test %s", __func__, test_name);
 
 		ret = prepare_test(ptd);
 		if (ret) {
@@ -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/base/power/main.c b/drivers/base/power/main.c
index b5d38d5..93b8ef1 100644
--- a/drivers/base/power/main.c
+++ b/drivers/base/power/main.c
@@ -571,7 +571,6 @@
 	pm_callback_t callback = NULL;
 	char *info = NULL;
 	int error = 0;
-	bool put = false;
 
 	TRACE_DEVICE(dev);
 	TRACE_RESUME(0);
@@ -589,7 +588,6 @@
 		goto Unlock;
 
 	pm_runtime_enable(dev);
-	put = true;
 
 	if (dev->pm_domain) {
 		info = "power domain ";
@@ -642,9 +640,6 @@
 
 	TRACE_RESUME(error);
 
-	if (put)
-		pm_runtime_put_sync(dev);
-
 	return error;
 }
 
@@ -779,6 +774,8 @@
 	}
 
 	device_unlock(dev);
+
+	pm_runtime_put_sync(dev);
 }
 
 /**
@@ -1062,16 +1059,20 @@
 	dpm_wait_for_children(dev, async);
 
 	if (async_error)
-		return 0;
+		goto Complete;
 
-	pm_runtime_get_noresume(dev);
+	/*
+	 * If a device configured to wake up the system from sleep states
+	 * has been suspended at run time and there's a resume request pending
+	 * for it, this is equivalent to the device signaling wakeup, so the
+	 * system suspend operation should be aborted.
+	 */
 	if (pm_runtime_barrier(dev) && device_may_wakeup(dev))
 		pm_wakeup_event(dev, 0);
 
 	if (pm_wakeup_pending()) {
-		pm_runtime_put_sync(dev);
 		async_error = -EBUSY;
-		return 0;
+		goto Complete;
 	}
 
 	data.dev = dev;
@@ -1140,14 +1141,13 @@
 	del_timer_sync(&timer);
 	destroy_timer_on_stack(&timer);
 
+ Complete:
 	complete_all(&dev->power.completion);
 
-	if (error) {
-		pm_runtime_put_sync(dev);
+	if (error)
 		async_error = error;
-	} else if (dev->power.is_suspended) {
+	else if (dev->power.is_suspended)
 		__pm_runtime_disable(dev, false);
-	}
 
 	return error;
 }
@@ -1240,6 +1240,14 @@
 	char *info = NULL;
 	int error = 0;
 
+	/*
+	 * If a device's parent goes into runtime suspend at the wrong time,
+	 * it won't be possible to resume the device.  To prevent this we
+	 * block runtime suspend here, during the prepare phase, and allow
+	 * it again during the complete phase.
+	 */
+	pm_runtime_get_noresume(dev);
+
 	device_lock(dev);
 
 	dev->power.wakeup_path = device_may_wakeup(dev);
diff --git a/drivers/bluetooth/hci_ath.c b/drivers/bluetooth/hci_ath.c
index 50b3362..b5531d6 100644
--- a/drivers/bluetooth/hci_ath.c
+++ b/drivers/bluetooth/hci_ath.c
@@ -43,6 +43,9 @@
 #include <net/bluetooth/hci_core.h>
 
 #include "hci_uart.h"
+#ifdef CONFIG_SERIAL_MSM_HS
+#include <mach/msm_serial_hs.h>
+#endif
 
 unsigned int enableuartsleep = 1;
 module_param(enableuartsleep, uint, 0644);
@@ -52,8 +55,8 @@
 /** Global state flags */
 static unsigned long flags;
 
-/** Tasklet to respond to change in hostwake line */
-static struct tasklet_struct hostwake_task;
+/** Workqueue to respond to change in hostwake line */
+static void wakeup_host_work(struct work_struct *work);
 
 /** Transmission timer */
 static void bluesleep_tx_timer_expire(unsigned long data);
@@ -89,11 +92,23 @@
 
 	struct sk_buff_head txq;
 	struct work_struct ctxtsw;
+	struct work_struct ws_sleep;
 };
 
-static void hostwake_interrupt(unsigned long data)
+static void hsuart_serial_clock_on(struct tty_struct *tty)
 {
-	BT_INFO(" wakeup host\n");
+	struct uart_state *state = tty->driver_data;
+	struct uart_port *port = state->uart_port;
+	BT_DBG("");
+	msm_hs_request_clock_on(port);
+}
+
+static void hsuart_serial_clock_off(struct tty_struct *tty)
+{
+	struct uart_state *state = tty->driver_data;
+	struct uart_port *port = state->uart_port;
+	BT_DBG("");
+	msm_hs_request_clock_off(port);
 }
 
 static void modify_timer_task(void)
@@ -109,6 +124,7 @@
 {
 	int status = 0;
 	if (test_bit(BT_TXEXPIRED, &flags)) {
+		hsuart_serial_clock_on(tty);
 		BT_INFO("wakeup device\n");
 		gpio_set_value(bsi->ext_wake, 0);
 		msleep(20);
@@ -118,6 +134,19 @@
 	return status;
 }
 
+static void wakeup_host_work(struct work_struct *work)
+{
+	struct ath_struct *ath =
+		container_of(work, struct ath_struct, ws_sleep);
+
+	BT_INFO("wake up host");
+	if (test_bit(BT_SLEEPENABLE, &flags)) {
+		if (test_bit(BT_TXEXPIRED, &flags))
+			hsuart_serial_clock_on(ath->hu->tty);
+	}
+	modify_timer_task();
+}
+
 static void ath_hci_uart_work(struct work_struct *work)
 {
 	int status;
@@ -141,11 +170,14 @@
 static irqreturn_t bluesleep_hostwake_isr(int irq, void *dev_id)
 {
 	/* schedule a tasklet to handle the change in the host wake line */
-	tasklet_schedule(&hostwake_task);
+	struct ath_struct *ath = (struct ath_struct *)dev_id;
+
+	schedule_work(&ath->ws_sleep);
+
 	return IRQ_HANDLED;
 }
 
-static int ath_bluesleep_gpio_config(int on)
+static int ath_bluesleep_gpio_config(struct ath_struct *ath, int on)
 {
 	int ret = 0;
 
@@ -193,19 +225,16 @@
 	/* Initialize timer */
 	init_timer(&tx_timer);
 	tx_timer.function = bluesleep_tx_timer_expire;
-	tx_timer.data = 0;
-
-	/* initialize host wake tasklet */
-	tasklet_init(&hostwake_task, hostwake_interrupt, 0);
+	tx_timer.data = (u_long)ath->hu;
 
 	if (bsi->irq_polarity == POLARITY_LOW) {
 		ret = request_irq(bsi->host_wake_irq, bluesleep_hostwake_isr,
 				IRQF_DISABLED | IRQF_TRIGGER_FALLING,
-				"bluetooth hostwake", NULL);
+				"bluetooth hostwake", (void *)ath);
 	} else  {
 		ret = request_irq(bsi->host_wake_irq, bluesleep_hostwake_isr,
 				IRQF_DISABLED | IRQF_TRIGGER_RISING,
-				"bluetooth hostwake", NULL);
+				"bluetooth hostwake", (void *)ath);
 	}
 	if (ret  < 0) {
 		BT_ERR("Couldn't acquire BT_HOST_WAKE IRQ");
@@ -221,7 +250,7 @@
 	return 0;
 
 free_host_wake_irq:
-	free_irq(bsi->host_wake_irq, NULL);
+	free_irq(bsi->host_wake_irq, (void *)ath);
 delete_timer:
 	del_timer(&tx_timer);
 gpio_ext_wake:
@@ -242,11 +271,6 @@
 	if (!bsi)
 		return -EIO;
 
-	if (ath_bluesleep_gpio_config(1) < 0) {
-		BT_ERR("HCIATH3K GPIO Config failed");
-		return -EIO;
-	}
-
 	ath = kzalloc(sizeof(*ath), GFP_ATOMIC);
 	if (!ath)
 		return -ENOMEM;
@@ -256,13 +280,20 @@
 	hu->priv = ath;
 	ath->hu = hu;
 
+	if (ath_bluesleep_gpio_config(ath, 1) < 0) {
+		BT_ERR("HCIATH3K GPIO Config failed");
+		hu->priv = NULL;
+		kfree(ath);
+		return -EIO;
+	}
+
 	ath->cur_sleep = enableuartsleep;
 	if (ath->cur_sleep == 1) {
 		set_bit(BT_SLEEPENABLE, &flags);
 		modify_timer_task();
 	}
 	INIT_WORK(&ath->ctxtsw, ath_hci_uart_work);
-
+	INIT_WORK(&ath->ws_sleep, wakeup_host_work);
 	return 0;
 }
 
@@ -289,11 +320,13 @@
 
 	cancel_work_sync(&ath->ctxtsw);
 
-	hu->priv = NULL;
-	kfree(ath);
+	cancel_work_sync(&ath->ws_sleep);
 
 	if (bsi)
-		ath_bluesleep_gpio_config(0);
+		ath_bluesleep_gpio_config(ath, 0);
+
+	hu->priv = NULL;
+	kfree(ath);
 
 	return 0;
 }
@@ -383,11 +416,14 @@
 
 static void bluesleep_tx_timer_expire(unsigned long data)
 {
+	struct hci_uart *hu = (struct hci_uart *) data;
+
 	if (!test_bit(BT_SLEEPENABLE, &flags))
 		return;
 	BT_INFO("Tx timer expired\n");
 
 	set_bit(BT_TXEXPIRED, &flags);
+	hsuart_serial_clock_off(hu->tty);
 }
 
 static struct hci_uart_proto athp = {
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/Kconfig b/drivers/char/diag/Kconfig
index 8f8707f..91fcdfc 100644
--- a/drivers/char/diag/Kconfig
+++ b/drivers/char/diag/Kconfig
@@ -30,9 +30,9 @@
 	 SDIO Transport Layer for DIAG Router
 endmenu
 
-menu "HSIC support for DIAG"
+menu "HSIC/SMUX support for DIAG"
 
-config DIAG_BRIDGE_CODE
+config DIAGFWD_BRIDGE_CODE
 	depends on USB_QCOM_DIAG_BRIDGE
 	default y
 	bool "Enable QSC/9K DIAG traffic over SMUX/HSIC"
diff --git a/drivers/char/diag/Makefile b/drivers/char/diag/Makefile
index 6ecc970..c9204ea 100644
--- a/drivers/char/diag/Makefile
+++ b/drivers/char/diag/Makefile
@@ -1,5 +1,6 @@
 obj-$(CONFIG_DIAG_CHAR) := diagchar.o
 obj-$(CONFIG_DIAG_SDIO_PIPE) += diagfwd_sdio.o
-obj-$(CONFIG_DIAG_BRIDGE_CODE) += diagfwd_hsic.o
-obj-$(CONFIG_DIAG_BRIDGE_CODE) += diagfwd_smux.o
+obj-$(CONFIG_DIAGFWD_BRIDGE_CODE) += diagfwd_bridge.o
+obj-$(CONFIG_DIAGFWD_BRIDGE_CODE) += diagfwd_hsic.o
+obj-$(CONFIG_DIAGFWD_BRIDGE_CODE) += diagfwd_smux.o
 diagchar-objs := diagchar_core.o diagchar_hdlc.o diagfwd.o diagmem.o diagfwd_cntl.o diag_dci.o diag_masks.o diag_debugfs.o
diff --git a/drivers/char/diag/diag_dci.c b/drivers/char/diag/diag_dci.c
index 7c0c0b9..24fc99a 100644
--- a/drivers/char/diag/diag_dci.c
+++ b/drivers/char/diag/diag_dci.c
@@ -35,6 +35,10 @@
 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;
+
+smd_channel_t *ch_dci_temp;
 
 #define DCI_CHK_CAPACITY(entry, new_data_len)				\
 ((entry->data_len + new_data_len > entry->total_capacity) ? 1 : 0)	\
@@ -91,14 +95,15 @@
 			read_bytes += 5 + dci_pkt_len;
 			buf += 5 + dci_pkt_len; /* advance to next DCI pkt */
 		}
-		driver->in_busy_dci = 1;
 		/* 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->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);
+			}
 	}
 }
 
@@ -131,33 +136,37 @@
 		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;
+		if (driver->dci_client_tbl[i].client != NULL) {
+			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) =
+				*(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;
+				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;
+			}
 		}
 	}
 }
@@ -224,6 +233,8 @@
 							dropped_events++;
 						return;
 					}
+					driver->dci_client_tbl[i].
+							received_events++;
 					*(int *)(entry->dci_data+
 					entry->data_len) = DCI_EVENT_TYPE;
 					memcpy(entry->dci_data+
@@ -281,6 +292,7 @@
 								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,
@@ -296,28 +308,100 @@
 	diag_smd_dci_send_req(MODEM_PROC);
 }
 
-static void diag_smd_dci_notify(void *ctxt, unsigned event)
+void diag_update_smd_dci_work_fn(struct work_struct *work)
 {
-	queue_work(driver->diag_dci_wq, &(driver->diag_read_smd_dci_work));
+	int i, j;
+	char dirty_bits[16];
+	uint8_t *client_log_mask_ptr;
+	uint8_t *log_mask_ptr;
+	int ret;
+
+	/* Update the peripheral(s) with the dci log and event masks */
+
+	/* If the cntl channel is not up, we can't update logs and events */
+	if (!driver->ch_cntl)
+		return;
+
+	memset(dirty_bits, 0, 16 * sizeof(uint8_t));
+
+	/*
+	 * From each log entry used by each client, determine
+	 * which log entries in the cumulative logs that need
+	 * to be updated on the peripheral.
+	 */
+	for (i = 0; i < MAX_DCI_CLIENTS; i++) {
+		if (driver->dci_client_tbl[i].client) {
+			client_log_mask_ptr =
+				driver->dci_client_tbl[i].dci_log_mask;
+			for (j = 0; j < 16; j++) {
+				if (*(client_log_mask_ptr+1))
+					dirty_bits[j] = 1;
+				client_log_mask_ptr += 514;
+			}
+		}
+	}
+
+	mutex_lock(&dci_log_mask_mutex);
+	/* Update the appropriate dirty bits in the cumulative mask */
+	log_mask_ptr = dci_cumulative_log_mask;
+	for (i = 0; i < 16; i++) {
+		if (dirty_bits[i])
+			*(log_mask_ptr+1) = dirty_bits[i];
+
+		log_mask_ptr += 514;
+	}
+	mutex_unlock(&dci_log_mask_mutex);
+
+	ret = diag_send_dci_log_mask(driver->ch_cntl);
+
+	ret = diag_send_dci_event_mask(driver->ch_cntl);
 }
 
-void diag_dci_notify_client(int peripheral_mask)
+void diag_dci_notify_client(int peripheral_mask, int data)
 {
 	int i, stat;
+	struct siginfo info;
+	memset(&info, 0, sizeof(struct siginfo));
+	info.si_code = SI_QUEUE;
+	info.si_int = (peripheral_mask | data);
 
 	/* Notify the DCI process that the peripheral DCI Channel is up */
 	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);
+			info.si_signo = driver->dci_client_tbl[i].signal_type;
+			stat = send_sig_info(
+				driver->dci_client_tbl[i].signal_type,
+				&info, driver->dci_client_tbl[i].client);
 			if (stat)
-				pr_err("diag: Err send sig stat: %d\n", stat);
+				pr_err("diag: Err sending dci signal to client, signal data: 0x%x, stat: %d\n",
+				info.si_int, stat);
 			break;
 		}
 	} /* end of loop for all DCI clients */
 }
 
+static void diag_smd_dci_notify(void *ctxt, unsigned event)
+{
+	if (event == SMD_EVENT_CLOSE) {
+		driver->ch_dci = 0;
+		/* Notify the clients of the close */
+		diag_dci_notify_client(DIAG_CON_MPSS, DIAG_STATUS_CLOSED);
+		return;
+	} else if (event == SMD_EVENT_OPEN) {
+
+		if (ch_dci_temp)
+			driver->ch_dci = ch_dci_temp;
+
+		queue_work(driver->diag_dci_wq,
+			&(driver->diag_update_smd_dci_work));
+
+		/* Notify the clients of the open */
+		diag_dci_notify_client(DIAG_CON_MPSS, DIAG_STATUS_OPEN);
+	}
+
+	queue_work(driver->diag_dci_wq, &(driver->diag_read_smd_dci_work));
+}
+
 static int diag_dci_probe(struct platform_device *pdev)
 {
 	int err = 0;
@@ -329,12 +413,11 @@
 			pr_err("diag: cannot open DCI port, Id = %d, err ="
 				" %d\n", pdev->id, err);
 		else
-			diag_dci_notify_client(DIAG_CON_MPSS);
+			ch_dci_temp = driver->ch_dci;
 	}
 	return err;
 }
 
-
 int diag_send_dci_pkt(struct diag_master_table entry, unsigned char *buf,
 					 int len, int index)
 {
@@ -378,14 +461,6 @@
 		}
 	}
 	mutex_lock(&driver->dci_mutex);
-	if (new_dci_client)
-		driver->num_dci_client++;
-	if (driver->num_dci_client > MAX_DCI_CLIENTS) {
-		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++) {
@@ -410,6 +485,12 @@
 	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;
+	int offset = 0;
+
+	if (!driver->ch_dci) {
+		pr_err("diag: ch_dci not valid for dci updates\n");
+		return DIAG_DCI_SEND_DATA_FAIL;
+	}
 
 	/* This is Pkt request/response transaction */
 	if (*(int *)temp > 0) {
@@ -465,10 +546,12 @@
 	} 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 (driver->dci_client_tbl[i].client != NULL) {
+				if (driver->dci_client_tbl[i].client->tgid ==
+							current->tgid) {
+					found = 1;
+					break;
+				}
 			}
 		}
 		if (!found) {
@@ -483,7 +566,7 @@
 		temp += 4;
 
 		head_log_mask_ptr = driver->dci_client_tbl[i].dci_log_mask;
-		pr_info("diag: head of dci log mask %p\n", head_log_mask_ptr);
+		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;
@@ -497,16 +580,18 @@
 			 */
 			log_mask_ptr = head_log_mask_ptr;
 			found = 0;
+			offset = 0;
 			while (log_mask_ptr) {
 				if (*log_mask_ptr == equip_id) {
 					found = 1;
-					pr_info("diag: find equip id = %x at %p\n",
+					pr_debug("diag: find equip id = %x at %p\n",
 						 equip_id, log_mask_ptr);
 					break;
 				} else {
-					pr_info("diag: did not find equip id = %x at %p\n",
+					pr_debug("diag: did not find equip id = %x at %p\n",
 						 equip_id, log_mask_ptr);
 					log_mask_ptr += 514;
+					offset += 514;
 				}
 			}
 			if (!found) {
@@ -519,21 +604,25 @@
 				*log_mask_ptr |= byte_mask;
 			else
 				*log_mask_ptr &= ~byte_mask;
+			/* add to cumulative mask */
+			update_dci_cumulative_log_mask(
+				offset, byte_index,
+				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);
+		ret = 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 (driver->dci_client_tbl[i].client != NULL) {
+				if (driver->dci_client_tbl[i].client->tgid ==
+							current->tgid) {
+					found = 1;
+					break;
+				}
 			}
 		}
 		if (!found) {
@@ -563,36 +652,52 @@
 				*(event_mask_ptr + byte_index) |= byte_mask;
 			else
 				*(event_mask_ptr + byte_index) &= ~byte_mask;
+			/* add to cumulative mask */
+			update_dci_cumulative_event_mask(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);
+		ret = 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)
+void update_dci_cumulative_event_mask(int offset, uint8_t byte_mask)
 {
 	int i;
-	uint8_t *update_ptr = dci_cumulative_event_mask;
 	uint8_t *event_mask_ptr;
+	uint8_t *update_ptr = dci_cumulative_event_mask;
+	bool is_set = false;
 
-	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_lock(&dci_event_mask_mutex);
+	update_ptr += offset;
+	for (i = 0; i < MAX_DCI_CLIENTS; i++) {
+		event_mask_ptr =
+			driver->dci_client_tbl[i].dci_event_mask;
+		event_mask_ptr += offset;
+		if ((*event_mask_ptr & byte_mask) == byte_mask) {
+			is_set = true;
+			/* break even if one client has the event mask set */
+			break;
+		}
+	}
+	if (is_set == false)
+		*update_ptr &= ~byte_mask;
+	else
+		*update_ptr |= byte_mask;
+	mutex_unlock(&dci_event_mask_mutex);
 }
 
-void diag_send_dci_event_mask(smd_channel_t *ch)
+int 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;
+	int ret = DIAG_DCI_NO_ERROR;
 
 	mutex_lock(&driver->diag_cntl_mutex);
 	/* send event mask update */
@@ -616,42 +721,68 @@
 				break;
 			}
 		}
-		if (wr_size != header_size + DCI_EVENT_MASK_SIZE)
+		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;
-
-	*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++;
+			ret = DIAG_DCI_SEND_DATA_FAIL;
 		}
-		*update_ptr = i+1;
-		update_ptr++;
-		log_mask_ptr++;
+	} else {
+		pr_err("diag: ch not valid for dci event mask update\n");
+		ret = DIAG_DCI_SEND_DATA_FAIL;
 	}
+	mutex_unlock(&driver->diag_cntl_mutex);
+
+	return ret;
 }
 
-void diag_send_dci_log_mask(smd_channel_t *ch)
+void update_dci_cumulative_log_mask(int offset, int byte_index,
+						uint8_t byte_mask)
+{
+	int i;
+	uint8_t *update_ptr = dci_cumulative_log_mask;
+	uint8_t *log_mask_ptr;
+	bool is_set = false;
+
+	mutex_lock(&dci_log_mask_mutex);
+	*update_ptr = 0;
+	/* set the equipment IDs */
+	for (i = 0; i < 16; i++)
+		*(update_ptr + (i*514)) = i;
+
+	update_ptr += offset;
+	/* update the dirty bit */
+	*(update_ptr+1) = 1;
+	update_ptr = update_ptr + byte_index;
+	for (i = 0; i < MAX_DCI_CLIENTS; i++) {
+		log_mask_ptr =
+			(driver->dci_client_tbl[i].dci_log_mask);
+		log_mask_ptr = log_mask_ptr + offset + byte_index;
+		if ((*log_mask_ptr & byte_mask) == byte_mask) {
+			is_set = true;
+			/* break even if one client has the log mask set */
+			break;
+		}
+	}
+
+	if (is_set == false)
+		*update_ptr &= ~byte_mask;
+	else
+		*update_ptr |= byte_mask;
+	mutex_unlock(&dci_log_mask_mutex);
+}
+
+int 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;
+	int ret = DIAG_DCI_NO_ERROR;
+
+	if (!ch) {
+		pr_err("diag: ch not valid for dci log mask update\n");
+		return DIAG_DCI_SEND_DATA_FAIL;
+	}
 
 	mutex_lock(&driver->diag_cntl_mutex);
 	for (i = 0; i < 16; i++) {
@@ -675,10 +806,12 @@
 				} else
 					break;
 			}
-			if (wr_size != header_size + 512)
+			if (wr_size != header_size + 512) {
 				pr_err("diag: dci log mask update failed %d, tried %d",
 					 wr_size, header_size + 512);
-			else {
+				ret = DIAG_DCI_SEND_DATA_FAIL;
+
+			} else {
 				*(log_mask_ptr+1) = 0; /* clear dirty byte */
 				pr_debug("diag: updated dci log equip ID %d\n",
 						 *log_mask_ptr);
@@ -687,6 +820,8 @@
 		log_mask_ptr += 514;
 	}
 	mutex_unlock(&driver->diag_cntl_mutex);
+
+	return ret;
 }
 
 void create_dci_log_mask_tbl(unsigned char *tbl_buf)
@@ -738,11 +873,15 @@
 {
 	int success = 0;
 
+	ch_dci_temp = NULL;
+
 	driver->dci_tag = 0;
 	driver->dci_client_id = 0;
 	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)
diff --git a/drivers/char/diag/diag_dci.h b/drivers/char/diag/diag_dci.h
index 97a285c..3f62e5e 100644
--- a/drivers/char/diag/diag_dci.h
+++ b/drivers/char/diag/diag_dci.h
@@ -53,6 +53,17 @@
 	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 {
@@ -68,18 +79,20 @@
 int diag_dci_init(void);
 void diag_dci_exit(void);
 void diag_read_smd_dci_work_fn(struct work_struct *);
+void diag_update_smd_dci_work_fn(struct work_struct *);
 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 update_dci_cumulative_log_mask(int offset, int byte_index,
+						uint8_t byte_mask);
+int 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 update_dci_cumulative_event_mask(int offset, uint8_t byte_mask);
+int 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/diag_debugfs.c b/drivers/char/diag/diag_debugfs.c
index ed0f08e..c404229 100644
--- a/drivers/char/diag/diag_debugfs.c
+++ b/drivers/char/diag/diag_debugfs.c
@@ -16,6 +16,7 @@
 #include <linux/debugfs.h>
 #include "diagchar.h"
 #include "diagfwd.h"
+#include "diagfwd_bridge.h"
 
 #define DEBUG_BUF_SIZE	4096
 static struct dentry *diag_dbgfs_dent;
@@ -112,7 +113,8 @@
 		"diag_modem_mask_update_work: %d\n"
 		"diag_lpass_mask_update_work: %d\n"
 		"diag_wcnss_mask_update_work: %d\n"
-		"diag_read_smd_dci_work: %d\n",
+		"diag_read_smd_dci_work: %d\n"
+		"diag_update_smd_dci_work: %d\n",
 		work_pending(&(driver->diag_drain_work)),
 		work_pending(&(driver->diag_read_smd_work)),
 		work_pending(&(driver->diag_read_smd_cntl_work)),
@@ -123,7 +125,8 @@
 		work_pending(&(driver->diag_modem_mask_update_work)),
 		work_pending(&(driver->diag_lpass_mask_update_work)),
 		work_pending(&(driver->diag_wcnss_mask_update_work)),
-		work_pending(&(driver->diag_read_smd_dci_work)));
+		work_pending(&(driver->diag_read_smd_dci_work)),
+		work_pending(&(driver->diag_update_smd_dci_work)));
 
 #ifdef CONFIG_DIAG_OVER_USB
 	ret += scnprintf(buf+ret, DEBUG_BUF_SIZE,
@@ -195,8 +198,8 @@
 	return ret;
 }
 
-#ifdef CONFIG_DIAG_BRIDGE_CODE
-static ssize_t diag_dbgfs_read_hsic(struct file *file, char __user *ubuf,
+#ifdef CONFIG_DIAGFWD_BRIDGE_CODE
+static ssize_t diag_dbgfs_read_bridge(struct file *file, char __user *ubuf,
 				    size_t count, loff_t *ppos)
 {
 	char *buf;
@@ -220,13 +223,17 @@
 		"count_hsic_write_pool: %d\n"
 		"diag_hsic_pool: %x\n"
 		"diag_hsic_write_pool: %x\n"
-		"write_len_mdm: %d\n"
+		"HSIC write_len: %d\n"
 		"num_hsic_buf_tbl_entries: %d\n"
-		"usb_mdm_connected: %d\n"
-		"diag_read_mdm_work: %d\n"
+		"HSIC usb_connected: %d\n"
+		"HSIC diag_read_work: %d\n"
 		"diag_read_hsic_work: %d\n"
 		"diag_disconnect_work: %d\n"
-		"diag_usb_read_complete_work: %d\n",
+		"diag_usb_read_complete_work: %d\n"
+		"smux ch: %d"
+		"smux enabled %d"
+		"smux in busy %d"
+		"smux connected %d",
 		driver->hsic_ch,
 		driver->hsic_inited,
 		driver->hsic_device_enabled,
@@ -238,13 +245,17 @@
 		driver->count_hsic_write_pool,
 		(unsigned int)driver->diag_hsic_pool,
 		(unsigned int)driver->diag_hsic_write_pool,
-		driver->write_len_mdm,
+			diag_bridge[HSIC].write_len,
 		driver->num_hsic_buf_tbl_entries,
-		driver->usb_mdm_connected,
-		work_pending(&(driver->diag_read_mdm_work)),
+			diag_bridge[HSIC].usb_connected,
+			work_pending(&(diag_bridge[HSIC].diag_read_work)),
 		work_pending(&(driver->diag_read_hsic_work)),
 		work_pending(&(driver->diag_disconnect_work)),
-		work_pending(&(driver->diag_usb_read_complete_work)));
+		work_pending(&(diag_bridge[HSIC].usb_read_complete_work)),
+		driver->lcid,
+		driver->diag_smux_enabled,
+		driver->in_busy_smux,
+		driver->smux_connected);
 
 	ret = simple_read_from_buffer(ubuf, count, ppos, buf, ret);
 
@@ -252,8 +263,8 @@
 	return ret;
 }
 
-const struct file_operations diag_dbgfs_hsic_ops = {
-	.read = diag_dbgfs_read_hsic,
+const struct file_operations diag_dbgfs_bridge_ops = {
+	.read = diag_dbgfs_read_bridge,
 };
 #endif
 
@@ -284,9 +295,9 @@
 	debugfs_create_file("work_pending", 0444, diag_dbgfs_dent, 0,
 		&diag_dbgfs_workpending_ops);
 
-#ifdef CONFIG_DIAG_BRIDGE_CODE
-	debugfs_create_file("hsic", 0444, diag_dbgfs_dent, 0,
-		&diag_dbgfs_hsic_ops);
+#ifdef CONFIG_DIAGFWD_BRIDGE_CODE
+	debugfs_create_file("bridge", 0444, diag_dbgfs_dent, 0,
+		&diag_dbgfs_bridge_ops);
 #endif
 
 	diag_dbgfs_table_index = 0;
diff --git a/drivers/char/diag/diagchar.h b/drivers/char/diag/diagchar.h
index a37260b..ec3bb81 100644
--- a/drivers/char/diag/diagchar.h
+++ b/drivers/char/diag/diagchar.h
@@ -29,6 +29,7 @@
 #define IN_BUF_SIZE		16384
 #define MAX_IN_BUF_SIZE	32768
 #define MAX_SYNC_OBJ_NAME_SIZE	32
+#define UINT32_MAX	UINT_MAX
 /* Size of the buffer used for deframing a packet
   reveived from the PC tool*/
 #define HDLC_MAX 4096
@@ -66,6 +67,15 @@
 #define DIAG_CON_LPASS (0x0004)	/* Bit mask for LPASS */
 #define DIAG_CON_WCNSS (0x0008)	/* Bit mask for WCNSS */
 
+/*
+ * The status bit masks when received in a signal handler are to be
+ * used in conjunction with the peripheral list bit mask to determine the
+ * status for a peripheral. For instance, 0x00010002 would denote an open
+ * status on the MPSS
+ */
+#define DIAG_STATUS_OPEN (0x00010000)	/* DCI channel open status mask   */
+#define DIAG_STATUS_CLOSED (0x00020000)	/* DCI channel closed status mask */
+
 /* Maximum number of pkt reg supported at initialization*/
 extern unsigned int diag_max_reg;
 extern unsigned int diag_threshold_reg;
@@ -234,6 +244,7 @@
 	struct work_struct diag_lpass_mask_update_work;
 	struct work_struct diag_wcnss_mask_update_work;
 	struct work_struct diag_read_smd_dci_work;
+	struct work_struct diag_update_smd_dci_work;
 	struct work_struct diag_clean_modem_reg_work;
 	struct work_struct diag_clean_lpass_reg_work;
 	struct work_struct diag_clean_wcnss_reg_work;
@@ -257,6 +268,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;
@@ -271,7 +283,9 @@
 	struct diag_request *usb_read_mdm_ptr;
 	struct diag_request *write_ptr_mdm;
 #endif
-#ifdef CONFIG_DIAG_BRIDGE_CODE
+#ifdef CONFIG_DIAGFWD_BRIDGE_CODE
+	/* common for all bridges */
+	struct work_struct diag_disconnect_work;
 	/* SGLTE variables */
 	int lcid;
 	unsigned char *buf_in_smux;
@@ -288,18 +302,6 @@
 	int in_busy_hsic_read_on_device;
 	int in_busy_hsic_write;
 	struct work_struct diag_read_hsic_work;
-	struct mutex bridge_mutex;
-	/* USB MDM channel variables */
-	int usb_mdm_connected;
-	int read_len_mdm;
-	int write_len_mdm;
-	unsigned char *usb_buf_mdm_out;
-	struct usb_diag_ch *mdm_ch;
-	struct workqueue_struct *diag_bridge_wq;
-	struct work_struct diag_read_mdm_work;
-	struct work_struct diag_disconnect_work;
-	struct work_struct diag_usb_read_complete_work;
-	struct diag_request *usb_read_mdm_ptr;
 	int count_hsic_pool;
 	int count_hsic_write_pool;
 	unsigned int poolsize_hsic;
@@ -314,5 +316,6 @@
 #endif
 };
 
+extern struct diag_bridge_dev *diag_bridge;
 extern struct diagchar_dev *driver;
 #endif
diff --git a/drivers/char/diag/diagchar_core.c b/drivers/char/diag/diagchar_core.c
index c29a1d3f..34ff345 100644
--- a/drivers/char/diag/diagchar_core.c
+++ b/drivers/char/diag/diagchar_core.c
@@ -32,13 +32,14 @@
 #ifdef CONFIG_DIAG_SDIO_PIPE
 #include "diagfwd_sdio.h"
 #endif
-#ifdef CONFIG_DIAG_BRIDGE_CODE
+#ifdef CONFIG_DIAGFWD_BRIDGE_CODE
 #include "diagfwd_hsic.h"
 #include "diagfwd_smux.h"
 #endif
 #include <linux/timer.h>
 #include "diag_debugfs.h"
 #include "diag_masks.h"
+#include "diagfwd_bridge.h"
 
 MODULE_DESCRIPTION("Diag Char Driver");
 MODULE_LICENSE("GPL v2");
@@ -127,7 +128,7 @@
 	mutex_unlock(&driver->diagchar_mutex);
 }
 
-#ifdef CONFIG_DIAG_BRIDGE_CODE
+#ifdef CONFIG_DIAGFWD_BRIDGE_CODE
 void diag_clear_hsic_tbl(void)
 {
 	int i;
@@ -246,29 +247,39 @@
 	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 */
 	if (driver->logging_process_id == current->tgid) {
 		driver->logging_mode = USB_MODE;
 		diagfwd_connect();
-#ifdef CONFIG_DIAG_BRIDGE_CODE
+#ifdef CONFIG_DIAGFWD_BRIDGE_CODE
 		diag_clear_hsic_tbl();
 		diagfwd_cancel_hsic();
 		diagfwd_connect_bridge(0);
@@ -348,7 +359,7 @@
 }
 
 void diag_add_reg(int j, struct bindpkt_params *params,
-					  int *success, int *count_entries)
+				  int *success, unsigned int *count_entries)
 {
 	*success = 1;
 	driver->table[j].cmd_code = params->cmd_code;
@@ -370,80 +381,172 @@
 	(*count_entries)++;
 }
 
+#ifdef CONFIG_DIAG_BRIDGE_CODE
+uint16_t diag_get_remote_device_mask(void)
+{
+	uint16_t remote_dev = 0;
+
+	if (driver->hsic_inited)
+		remote_dev |= (1 << 0);
+	if (driver->diag_smux_enabled)
+		remote_dev |= (1 << 1);
+
+	return remote_dev;
+}
+#else
+inline uint16_t diag_get_remote_device_mask(void) { return 0; }
+#endif
+
 long diagchar_ioctl(struct file *filp,
 			   unsigned int iocmd, unsigned long ioarg)
 {
-	int i, j, count_entries = 0, temp;
-	int success = -1;
+	int i, j, temp, success = -1, status;
+	unsigned int count_entries = 0, interim_count = 0;
 	void *temp_buf;
 	uint16_t support_list = 0;
-	struct diag_dci_client_tbl *notify_params;
-	int status;
+	struct diag_dci_client_tbl *dci_params;
+	struct diag_dci_health_stats stats;
 
 	if (iocmd == DIAG_IOCTL_COMMAND_REG) {
-		struct bindpkt_params_per_process *pkt_params =
-			 (struct bindpkt_params_per_process *) ioarg;
+		struct bindpkt_params_per_process pkt_params;
+		struct bindpkt_params *params;
+		struct bindpkt_params *head_params;
+		if (copy_from_user(&pkt_params, (void *)ioarg,
+			   sizeof(struct bindpkt_params_per_process))) {
+			return -EFAULT;
+		}
+		if ((UINT32_MAX/sizeof(struct bindpkt_params)) <
+							 pkt_params.count) {
+			pr_warning("diag: integer overflow while multiply\n");
+			return -EFAULT;
+		}
+		params = kzalloc(pkt_params.count*sizeof(
+			struct bindpkt_params), GFP_KERNEL);
+		if (!params) {
+			pr_err("diag: unable to alloc memory\n");
+			return -ENOMEM;
+		} else
+			head_params = params;
+
+		if (copy_from_user(params, pkt_params.params,
+			   pkt_params.count*sizeof(struct bindpkt_params))) {
+			kfree(head_params);
+			return -EFAULT;
+		}
 		mutex_lock(&driver->diagchar_mutex);
 		for (i = 0; i < diag_max_reg; i++) {
 			if (driver->table[i].process_id == 0) {
-				diag_add_reg(i, pkt_params->params,
-						&success, &count_entries);
-				if (pkt_params->count > count_entries) {
-					pkt_params->params++;
+				diag_add_reg(i, params, &success,
+							 &count_entries);
+				if (pkt_params.count > count_entries) {
+					params++;
 				} else {
 					mutex_unlock(&driver->diagchar_mutex);
+					kfree(head_params);
 					return success;
 				}
 			}
 		}
 		if (i < diag_threshold_reg) {
 			/* Increase table size by amount required */
-			diag_max_reg += pkt_params->count -
+			if (pkt_params.count >= count_entries) {
+				interim_count = pkt_params.count -
 							 count_entries;
+			} else {
+				pr_warning("diag: error in params count\n");
+				kfree(head_params);
+				mutex_unlock(&driver->diagchar_mutex);
+				return -EFAULT;
+			}
+			if (UINT32_MAX - diag_max_reg >=
+							interim_count) {
+				diag_max_reg += interim_count;
+			} else {
+				pr_warning("diag: Integer overflow\n");
+				kfree(head_params);
+				mutex_unlock(&driver->diagchar_mutex);
+				return -EFAULT;
+			}
 			/* Make sure size doesnt go beyond threshold */
 			if (diag_max_reg > diag_threshold_reg) {
 				diag_max_reg = diag_threshold_reg;
 				pr_info("diag: best case memory allocation\n");
 			}
+			if (UINT32_MAX/sizeof(struct diag_master_table) <
+								 diag_max_reg) {
+				pr_warning("diag: integer overflow\n");
+				kfree(head_params);
+				mutex_unlock(&driver->diagchar_mutex);
+				return -EFAULT;
+			}
 			temp_buf = krealloc(driver->table,
 					 diag_max_reg*sizeof(struct
 					 diag_master_table), GFP_KERNEL);
 			if (!temp_buf) {
-				diag_max_reg -= pkt_params->count -
-							 count_entries;
-				pr_alert("diag: Insufficient memory for reg.");
+				pr_alert("diag: Insufficient memory for reg.\n");
 				mutex_unlock(&driver->diagchar_mutex);
+
+				if (pkt_params.count >= count_entries) {
+					interim_count = pkt_params.count -
+								 count_entries;
+				} else {
+					pr_warning("diag: params count error\n");
+					mutex_unlock(&driver->diagchar_mutex);
+					kfree(head_params);
+					return -EFAULT;
+				}
+				if (diag_max_reg >= interim_count) {
+					diag_max_reg -= interim_count;
+				} else {
+					pr_warning("diag: Integer underflow\n");
+					mutex_unlock(&driver->diagchar_mutex);
+					kfree(head_params);
+					return -EFAULT;
+				}
+				kfree(head_params);
 				return 0;
 			} else {
 				driver->table = temp_buf;
 			}
 			for (j = i; j < diag_max_reg; j++) {
-				diag_add_reg(j, pkt_params->params,
-						&success, &count_entries);
-				if (pkt_params->count > count_entries) {
-					pkt_params->params++;
+				diag_add_reg(j, params, &success,
+							 &count_entries);
+				if (pkt_params.count > count_entries) {
+					params++;
 				} else {
 					mutex_unlock(&driver->diagchar_mutex);
+					kfree(head_params);
 					return success;
 				}
 			}
+			kfree(head_params);
 			mutex_unlock(&driver->diagchar_mutex);
 		} else {
 			mutex_unlock(&driver->diagchar_mutex);
+			kfree(head_params);
 			pr_err("Max size reached, Pkt Registration failed for"
 						" Process %d", current->tgid);
 		}
 		success = 0;
 	} else if (iocmd == DIAG_IOCTL_GET_DELAYED_RSP_ID) {
-		struct diagpkt_delay_params *delay_params =
-					(struct diagpkt_delay_params *) ioarg;
-
-		if ((delay_params->rsp_ptr) &&
-		 (delay_params->size == sizeof(delayed_rsp_id)) &&
-				 (delay_params->num_bytes_ptr)) {
-			*((uint16_t *)delay_params->rsp_ptr) =
-				DIAGPKT_NEXT_DELAYED_RSP_ID(delayed_rsp_id);
-			*(delay_params->num_bytes_ptr) = sizeof(delayed_rsp_id);
+		struct diagpkt_delay_params delay_params;
+		uint16_t interim_rsp_id;
+		int interim_size;
+		if (copy_from_user(&delay_params, (void *)ioarg,
+					   sizeof(struct diagpkt_delay_params)))
+			return -EFAULT;
+		if ((delay_params.rsp_ptr) &&
+		 (delay_params.size == sizeof(delayed_rsp_id)) &&
+				 (delay_params.num_bytes_ptr)) {
+			interim_rsp_id = DIAGPKT_NEXT_DELAYED_RSP_ID(
+							delayed_rsp_id);
+			if (copy_to_user((void *)delay_params.rsp_ptr,
+					 &interim_rsp_id, sizeof(uint16_t)))
+				return -EFAULT;
+			interim_size = sizeof(delayed_rsp_id);
+			if (copy_to_user((void *)delay_params.num_bytes_ptr,
+						 &interim_size, sizeof(int)))
+				return -EFAULT;
 			success = 0;
 		}
 	} else if (iocmd == DIAG_IOCTL_DCI_REG) {
@@ -451,8 +554,18 @@
 			return DIAG_DCI_NO_REG;
 		if (driver->num_dci_client >= MAX_DCI_CLIENTS)
 			return DIAG_DCI_NO_REG;
-		notify_params = (struct diag_dci_client_tbl *) ioarg;
+		dci_params = kzalloc(sizeof(struct diag_dci_client_tbl),
+								 GFP_KERNEL);
+		if (dci_params == NULL) {
+			pr_err("diag: unable to alloc memory\n");
+			return -ENOMEM;
+		}
+		if (copy_from_user(dci_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++;
@@ -460,9 +573,9 @@
 			if (driver->dci_client_tbl[i].client == NULL) {
 				driver->dci_client_tbl[i].client = current;
 				driver->dci_client_tbl[i].list =
-							 notify_params->list;
+							 dci_params->list;
 				driver->dci_client_tbl[i].signal_type =
-					 notify_params->signal_type;
+					 dci_params->signal_type;
 				create_dci_log_mask_tbl(driver->
 					dci_client_tbl[i].dci_log_mask);
 				create_dci_event_mask_tbl(driver->
@@ -474,46 +587,73 @@
 								 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;
 			}
 		}
 		mutex_unlock(&driver->dci_mutex);
+		kfree(dci_params);
 		return driver->dci_client_id;
 	} else if (iocmd == DIAG_IOCTL_DCI_DEINIT) {
 		success = -1;
 		/* Delete this process from DCI table */
 		mutex_lock(&driver->dci_mutex);
-		for (i = 0; i < dci_max_reg; i++) {
-			if (driver->req_tracking_tbl[i].pid == current->tgid) {
-				pr_debug("diag: delete %d\n", current->tgid);
+		for (i = 0; i < dci_max_reg; i++)
+			if (driver->req_tracking_tbl[i].pid == current->tgid)
 				driver->req_tracking_tbl[i].pid = 0;
-				success = i;
-			}
-		}
 		for (i = 0; i < MAX_DCI_CLIENTS; i++) {
-			if (driver->dci_client_tbl[i].client == current) {
+			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;
 				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);
-		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;
+		if (copy_to_user((void *)ioarg, &support_list,
+							 sizeof(uint16_t)))
+			return -EFAULT;
+		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++) {
+			dci_params = &(driver->dci_client_tbl[i]);
+			if (dci_params->client &&
+				dci_params->client->tgid == current->tgid) {
+				stats.dropped_logs = dci_params->dropped_logs;
+				stats.dropped_events =
+						 dci_params->dropped_events;
+				stats.received_logs = dci_params->received_logs;
+				stats.received_events =
+						 dci_params->received_events;
+				if (stats.reset_status) {
+					dci_params->dropped_logs = 0;
+					dci_params->dropped_events = 0;
+					dci_params->received_logs = 0;
+					dci_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)
 				break;
-		if (i == -1)
+		if (i == driver->num_clients)
 			return -EINVAL;
 		driver->data_ready[i] |= DEINIT_TYPE;
 		wake_up_interruptible(&driver->wait_q);
@@ -522,6 +662,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;
@@ -540,14 +685,14 @@
 				}
 			}
 		}
-		if (driver->logging_mode == UART_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;
+		if (driver->logging_mode == CALLBACK_MODE)
+			driver->callback_process = current;
+		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;
 		}
@@ -564,7 +709,7 @@
 #ifdef CONFIG_DIAG_SDIO_PIPE
 			driver->in_busy_sdio = 1;
 #endif
-#ifdef CONFIG_DIAG_BRIDGE_CODE
+#ifdef CONFIG_DIAGFWD_BRIDGE_CODE
 			diagfwd_disconnect_bridge(0);
 			diag_clear_hsic_tbl();
 #endif
@@ -593,7 +738,7 @@
 				queue_work(driver->diag_sdio_wq,
 					&(driver->diag_read_sdio_work));
 #endif
-#ifdef CONFIG_DIAG_BRIDGE_CODE
+#ifdef CONFIG_DIAGFWD_BRIDGE_CODE
 			diagfwd_connect_bridge(0);
 #endif
 		}
@@ -601,13 +746,13 @@
 		else if (temp == USB_MODE && driver->logging_mode
 							 == NO_LOGGING_MODE) {
 			diagfwd_disconnect();
-#ifdef CONFIG_DIAG_BRIDGE_CODE
+#ifdef CONFIG_DIAGFWD_BRIDGE_CODE
 			diagfwd_disconnect_bridge(0);
 #endif
 		} else if (temp == NO_LOGGING_MODE && driver->logging_mode
 								== USB_MODE) {
 			diagfwd_connect();
-#ifdef CONFIG_DIAG_BRIDGE_CODE
+#ifdef CONFIG_DIAGFWD_BRIDGE_CODE
 			diagfwd_connect_bridge(0);
 #endif
 		} else if (temp == USB_MODE && driver->logging_mode
@@ -637,14 +782,14 @@
 				queue_work(driver->diag_sdio_wq,
 					&(driver->diag_read_sdio_work));
 #endif
-#ifdef CONFIG_DIAG_BRIDGE_CODE
+#ifdef CONFIG_DIAGFWD_BRIDGE_CODE
 			diagfwd_cancel_hsic();
 			diagfwd_connect_bridge(0);
 #endif
 		} else if (temp == MEMORY_DEVICE_MODE &&
 				 driver->logging_mode == USB_MODE) {
 			diagfwd_connect();
-#ifdef CONFIG_DIAG_BRIDGE_CODE
+#ifdef CONFIG_DIAGFWD_BRIDGE_CODE
 			diag_clear_hsic_tbl();
 			diagfwd_cancel_hsic();
 			diagfwd_connect_bridge(0);
@@ -652,6 +797,13 @@
 		}
 #endif /* DIAG over USB */
 		success = 1;
+	} else if (iocmd == DIAG_IOCTL_REMOTE_DEV) {
+		uint16_t remote_dev = diag_get_remote_device_mask();
+
+		if (copy_to_user((void *)ioarg, &remote_dev, sizeof(uint16_t)))
+			success = -EFAULT;
+		else
+			success = 1;
 	}
 
 	return success;
@@ -663,7 +815,7 @@
 	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)
+#if defined(CONFIG_DIAG_SDIO_PIPE) || defined(CONFIG_DIAGFWD_BRIDGE_CODE)
 	int mdm_token = MDM_TOKEN;
 #endif
 
@@ -680,16 +832,16 @@
 				  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
+#ifdef CONFIG_DIAGFWD_BRIDGE_CODE
 		unsigned long spin_lock_flags;
 		struct diag_write_device hsic_buf_tbl[NUM_HSIC_BUF_TBL_ENTRIES];
 #endif
 
 		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;
@@ -818,7 +970,7 @@
 			driver->in_busy_sdio = 0;
 		}
 #endif
-#ifdef CONFIG_DIAG_BRIDGE_CODE
+#ifdef CONFIG_DIAGFWD_BRIDGE_CODE
 		spin_lock_irqsave(&driver->hsic_spinlock, spin_lock_flags);
 		for (i = 0; i < driver->poolsize_hsic_write; i++) {
 			hsic_buf_tbl[i].buf = driver->hsic_buf_tbl[i].buf;
@@ -892,7 +1044,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));
@@ -909,10 +1061,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) {
@@ -969,14 +1121,17 @@
 		COPY_USER_SPACE_OR_EXIT(buf, data_type, 4);
 		/* 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;
+			if (driver->dci_client_tbl[i].client != NULL) {
+				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;
@@ -994,15 +1149,15 @@
 static int diagchar_write(struct file *file, const char __user *buf,
 				size_t count, loff_t *ppos)
 {
-	int err, ret = 0, pkt_type;
-	bool mdm_mask = false;
+	int err, ret = 0, pkt_type, token_offset = 0;
+	bool remote_data = false;
 #ifdef DIAG_DEBUG
 	int length = 0, i;
 #endif
 	struct diag_send_desc_type send = { NULL, NULL, DIAG_STATE_START, 0 };
 	struct diag_hdlc_dest_type enc = { NULL, NULL, 0 };
 	void *buf_copy = NULL;
-	int payload_size;
+	unsigned int payload_size;
 #ifdef CONFIG_DIAG_OVER_USB
 	if (((driver->logging_mode == USB_MODE) && (!driver->usb_connected)) ||
 				(driver->logging_mode == NO_LOGGING_MODE)) {
@@ -1013,8 +1168,17 @@
 	/* Get the packet type F3/log/event/Pkt response */
 	err = copy_from_user((&pkt_type), buf, 4);
 	/* First 4 bytes indicate the type of payload - ignore these */
+	if (count < 4) {
+		pr_err("diag: Client sending short data\n");
+		return -EBADMSG;
+	}
 	payload_size = count - 4;
-
+	if (payload_size > USER_SPACE_DATA) {
+		pr_err("diag: Dropping packet, packet payload size crosses 8KB limit. Current payload size %d\n",
+				payload_size);
+		driver->dropped_count++;
+		return -EBADMSG;
+	}
 	if (pkt_type == DCI_DATA_TYPE) {
 		err = copy_from_user(driver->user_space_data, buf + 4,
 							 payload_size);
@@ -1026,18 +1190,21 @@
 							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 for proc_type */
+		if (*(int *)driver->user_space_data == MDM_TOKEN) {
+			remote_data = true;
+			token_offset = 4;
+			payload_size -= 4;
+			buf += 4;
+		}
+
 		/* Check masks for On-Device logging */
 		if (driver->mask_check) {
-			/* Check if mask is for MDM or MSM */
-			if (*(int *)driver->user_space_data == MDM_TOKEN) {
-				mdm_mask = true;
-				driver->user_space_data += 4;
-				buf += 4;
-			}
-			if (!mask_request_validate(driver->user_space_data)) {
+			if (!mask_request_validate(driver->user_space_data +
+							 token_offset)) {
 				pr_alert("diag: mask request Invalid\n");
 				return -EFAULT;
 			}
@@ -1046,31 +1213,34 @@
 #ifdef DIAG_DEBUG
 		pr_debug("diag: user space data %d\n", payload_size);
 		for (i = 0; i < payload_size; i++)
-			pr_debug("\t %x", *((driver->user_space_data)+i));
+			pr_debug("\t %x", *((driver->user_space_data
+						+ token_offset)+i));
 #endif
 #ifdef CONFIG_DIAG_SDIO_PIPE
 		/* send masks to 9k too */
-		if (driver->sdio_ch && mdm_mask) {
+		if (driver->sdio_ch && remote_data) {
 			wait_event_interruptible(driver->wait_q,
 				 (sdio_write_avail(driver->sdio_ch) >=
 					 payload_size));
 			if (driver->sdio_ch && (payload_size > 0)) {
 				sdio_write(driver->sdio_ch, (void *)
-				   (driver->user_space_data), payload_size);
+				   (driver->user_space_data + token_offset),
+				   payload_size);
 			}
 		}
 #endif
-#ifdef CONFIG_DIAG_BRIDGE_CODE
+#ifdef CONFIG_DIAGFWD_BRIDGE_CODE
 		/* send masks to 9k too */
-		if (driver->hsic_ch && (payload_size > 0) && mdm_mask) {
+		if (driver->hsic_ch && (payload_size > 0) && remote_data) {
 			/* wait sending mask updates if HSIC ch not ready */
 			if (driver->in_busy_hsic_write)
 				wait_event_interruptible(driver->wait_q,
 					(driver->in_busy_hsic_write != 1));
 			driver->in_busy_hsic_write = 1;
 			driver->in_busy_hsic_read_on_device = 0;
-			err = diag_bridge_write(driver->user_space_data,
-							 payload_size);
+			err = diag_bridge_write(
+					driver->user_space_data + token_offset,
+					payload_size);
 			if (err) {
 				pr_err("diag: err sending mask to MDM: %d\n",
 									 err);
@@ -1084,11 +1254,12 @@
 					driver->in_busy_hsic_write = 0;
 			}
 		}
-		if (driver->diag_smux_enabled && mdm_mask && driver->lcid) {
+		if (driver->diag_smux_enabled && remote_data
+						&& driver->lcid) {
 			if (payload_size > 0) {
 				err = msm_smux_write(driver->lcid, NULL,
-						driver->user_space_data,
-						payload_size);
+					driver->user_space_data + token_offset,
+					payload_size);
 				if (err) {
 					pr_err("diag:send mask to MDM err %d",
 							err);
@@ -1098,9 +1269,10 @@
 		}
 #endif
 		/* send masks to 8k now */
-		if (!mdm_mask)
-			diag_process_hdlc((void *)(driver->user_space_data),
-							 payload_size);
+		if (!remote_data)
+			diag_process_hdlc((void *)
+				(driver->user_space_data + token_offset),
+				 payload_size);
 		return 0;
 	}
 
@@ -1156,8 +1328,9 @@
 		if (err) {
 			/*Free the buffer right away if write failed */
 			diagmem_free(driver, buf_hdlc, POOL_TYPE_HDLC);
-			diagmem_free(driver, (unsigned char *)driver->
-				 write_ptr_svc, POOL_TYPE_WRITE_STRUCT);
+			if (driver->logging_mode == USB_MODE)
+				diagmem_free(driver, (unsigned char *)driver->
+					write_ptr_svc, POOL_TYPE_WRITE_STRUCT);
 			ret = -EIO;
 			goto fail_free_hdlc;
 		}
@@ -1184,8 +1357,9 @@
 		if (err) {
 			/*Free the buffer right away if write failed */
 			diagmem_free(driver, buf_hdlc, POOL_TYPE_HDLC);
-			diagmem_free(driver, (unsigned char *)driver->
-				 write_ptr_svc, POOL_TYPE_WRITE_STRUCT);
+			if (driver->logging_mode == USB_MODE)
+				diagmem_free(driver, (unsigned char *)driver->
+					write_ptr_svc, POOL_TYPE_WRITE_STRUCT);
 			ret = -EIO;
 			goto fail_free_hdlc;
 		}
@@ -1209,8 +1383,9 @@
 		if (err) {
 			/*Free the buffer right away if write failed */
 			diagmem_free(driver, buf_hdlc, POOL_TYPE_HDLC);
-			diagmem_free(driver, (unsigned char *)driver->
-				 write_ptr_svc, POOL_TYPE_WRITE_STRUCT);
+			if (driver->logging_mode == USB_MODE)
+				diagmem_free(driver, (unsigned char *)driver->
+					write_ptr_svc, POOL_TYPE_WRITE_STRUCT);
 			ret = -EIO;
 			goto fail_free_hdlc;
 		}
@@ -1360,6 +1535,13 @@
 	return 0;
 }
 
+#ifdef CONFIG_DIAGFWD_BRIDGE_CODE
+static void diag_disconnect_work_fn(struct work_struct *w)
+{
+	diagfwd_disconnect_bridge(1);
+}
+#endif
+
 #ifdef CONFIG_DIAG_SDIO_PIPE
 void diag_sdio_fn(int type)
 {
@@ -1374,16 +1556,14 @@
 inline void diag_sdio_fn(int type) {}
 #endif
 
-#ifdef CONFIG_DIAG_BRIDGE_CODE
-void diag_bridge_fn(int type)
+#ifdef CONFIG_DIAGFWD_BRIDGE_CODE
+void diagfwd_bridge_fn(int type)
 {
-	if (type == INIT)
-		diagfwd_bridge_init();
-	else if (type == EXIT)
+	if (type == EXIT)
 		diagfwd_bridge_exit();
 }
 #else
-inline void diag_bridge_fn(int type) {}
+inline void diagfwd_bridge_fn(int type) { }
 #endif
 
 static int __init diagchar_init(void)
@@ -1393,6 +1573,12 @@
 
 	pr_debug("diagfwd initializing ..\n");
 	driver = kzalloc(sizeof(struct diagchar_dev) + 5, GFP_KERNEL);
+#ifdef CONFIG_DIAGFWD_BRIDGE_CODE
+	diag_bridge = kzalloc(MAX_BRIDGES * sizeof(struct diag_bridge_dev),
+								 GFP_KERNEL);
+	if (!diag_bridge)
+		pr_warning("diag: could not allocate memory for bridge\n");
+#endif
 
 	if (driver) {
 		driver->used = 0;
@@ -1409,6 +1595,7 @@
 		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);
@@ -1426,7 +1613,9 @@
 		INIT_WORK(&(driver->diag_read_smd_wcnss_cntl_work),
 			diag_read_smd_wcnss_cntl_work_fn);
 		INIT_WORK(&(driver->diag_read_smd_dci_work),
-						 diag_read_smd_dci_work_fn);
+						diag_read_smd_dci_work_fn);
+		INIT_WORK(&(driver->diag_update_smd_dci_work),
+						diag_update_smd_dci_work_fn);
 		INIT_WORK(&(driver->diag_clean_modem_reg_work),
 						 diag_clean_modem_reg_fn);
 		INIT_WORK(&(driver->diag_clean_lpass_reg_work),
@@ -1436,10 +1625,16 @@
 		diag_debugfs_init();
 		diag_masks_init();
 		diagfwd_init();
+#ifdef CONFIG_DIAGFWD_BRIDGE_CODE
+		diagfwd_bridge_init(HSIC);
+		diagfwd_bridge_init(SMUX);
+		INIT_WORK(&(driver->diag_disconnect_work),
+						 diag_disconnect_work_fn);
+#endif
 		diagfwd_cntl_init();
 		driver->dci_state = diag_dci_init();
 		diag_sdio_fn(INIT);
-		diag_bridge_fn(INIT);
+
 		pr_debug("diagchar initializing ..\n");
 		driver->num = 1;
 		driver->name = ((void *)driver) + sizeof(struct diagchar_dev);
@@ -1474,7 +1669,7 @@
 	diagfwd_cntl_exit();
 	diag_masks_exit();
 	diag_sdio_fn(EXIT);
-	diag_bridge_fn(EXIT);
+	diagfwd_bridge_fn(EXIT);
 	return -1;
 }
 
@@ -1488,7 +1683,7 @@
 	diagfwd_cntl_exit();
 	diag_masks_exit();
 	diag_sdio_fn(EXIT);
-	diag_bridge_fn(EXIT);
+	diagfwd_bridge_fn(EXIT);
 	diag_debugfs_cleanup();
 	diagchar_cleanup();
 	printk(KERN_INFO "done diagchar exit\n");
diff --git a/drivers/char/diag/diagfwd.c b/drivers/char/diag/diagfwd.c
index 0bce4e9..cee4c96 100644
--- a/drivers/char/diag/diagfwd.c
+++ b/drivers/char/diag/diagfwd.c
@@ -40,6 +40,7 @@
 #endif
 #include "diag_dci.h"
 #include "diag_masks.h"
+#include "diagfwd_bridge.h"
 
 #define MODE_CMD		41
 #define RESET_ID		2
@@ -115,6 +116,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 +144,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:
@@ -210,8 +213,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);
 		}
@@ -325,7 +328,7 @@
 				}
 		}
 
-#ifdef CONFIG_DIAG_BRIDGE_CODE
+#ifdef CONFIG_DIAGFWD_BRIDGE_CODE
 		else if (proc_num == HSIC_DATA) {
 			unsigned long flags;
 			int foundIndex = -1;
@@ -335,7 +338,7 @@
 				if (driver->hsic_buf_tbl[i].length == 0) {
 					driver->hsic_buf_tbl[i].buf = buf;
 					driver->hsic_buf_tbl[i].length =
-							driver->write_len_mdm;
+						diag_bridge[HSIC].write_len;
 					driver->num_hsic_buf_tbl_entries++;
 					foundIndex = i;
 					break;
@@ -347,7 +350,7 @@
 			else
 				pr_debug("diag: ENQUEUE HSIC buf ptr and length is %x , %d\n",
 					(unsigned int)buf,
-					driver->write_len_mdm);
+					 diag_bridge[HSIC].write_len);
 		}
 #endif
 		for (i = 0; i < driver->num_clients; i++)
@@ -355,7 +358,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
@@ -384,10 +387,10 @@
 				&(driver->diag_read_sdio_work));
 		}
 #endif
-#ifdef CONFIG_DIAG_BRIDGE_CODE
+#ifdef CONFIG_DIAGFWD_BRIDGE_CODE
 		else if (proc_num == HSIC_DATA) {
 			if (driver->hsic_ch)
-				queue_work(driver->diag_bridge_wq,
+				queue_work(diag_bridge[HSIC].wq,
 					&(driver->diag_read_hsic_work));
 		}
 #endif
@@ -434,7 +437,7 @@
 						"while USB write\n");
 		}
 #endif
-#ifdef CONFIG_DIAG_BRIDGE_CODE
+#ifdef CONFIG_DIAGFWD_BRIDGE_CODE
 		else if (proc_num == HSIC_DATA) {
 			if (driver->hsic_device_enabled) {
 				struct diag_request *write_ptr_mdm;
@@ -445,9 +448,10 @@
 				if (write_ptr_mdm) {
 					write_ptr_mdm->buf = buf;
 					write_ptr_mdm->length =
-						driver->write_len_mdm;
-					err = usb_diag_write(driver->mdm_ch,
-								write_ptr_mdm);
+					   diag_bridge[HSIC].write_len;
+					write_ptr_mdm->context = (void *)HSIC;
+					err = usb_diag_write(
+					diag_bridge[HSIC].ch, write_ptr_mdm);
 					/* Return to the pool immediately */
 					if (err) {
 						diagmem_free(driver,
@@ -461,14 +465,16 @@
 					err = -1;
 				}
 			} else {
-				pr_err("diag: Incorrect hsic data "
+				pr_err("diag: Incorrect HSIC data "
 						"while USB write\n");
 				err = -1;
 			}
 		} else if (proc_num == SMUX_DATA) {
 				write_ptr->buf = buf;
+				write_ptr->context = (void *)SMUX;
 				pr_debug("diag: writing SMUX data\n");
-				err = usb_diag_write(driver->mdm_ch, write_ptr);
+				err = usb_diag_write(diag_bridge[SMUX].ch,
+								 write_ptr);
 		}
 #endif
 		APPEND_DEBUG('d');
diff --git a/drivers/char/diag/diagfwd_bridge.c b/drivers/char/diag/diagfwd_bridge.c
new file mode 100644
index 0000000..75fdeb4
--- /dev/null
+++ b/drivers/char/diag/diagfwd_bridge.c
@@ -0,0 +1,355 @@
+/* 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/slab.h>
+#include <linux/delay.h>
+#include <linux/diagchar.h>
+#include <linux/kmemleak.h>
+#include <linux/err.h>
+#include <linux/workqueue.h>
+#include <linux/ratelimit.h>
+#include <linux/platform_device.h>
+#include <linux/smux.h>
+#ifdef CONFIG_DIAG_OVER_USB
+#include <mach/usbdiag.h>
+#endif
+#include "diagchar.h"
+#include "diagmem.h"
+#include "diagfwd_cntl.h"
+#include "diagfwd_smux.h"
+#include "diagfwd_hsic.h"
+#include "diag_masks.h"
+#include "diagfwd_bridge.h"
+
+struct diag_bridge_dev *diag_bridge;
+
+/* diagfwd_connect_bridge is called when the USB mdm channel is connected */
+int diagfwd_connect_bridge(int process_cable)
+{
+	int i;
+
+	pr_debug("diag: in %s\n", __func__);
+
+	for (i = 0; i < MAX_BRIDGES; i++)
+		if (diag_bridge[i].enabled)
+			connect_bridge(process_cable, i);
+	return 0;
+}
+
+void connect_bridge(int process_cable, int index)
+{
+	int err;
+
+	mutex_lock(&diag_bridge[index].bridge_mutex);
+	/* If the usb cable is being connected */
+	if (process_cable) {
+		err = usb_diag_alloc_req(diag_bridge[index].ch, N_MDM_WRITE,
+			       N_MDM_READ);
+		if (err)
+			pr_err("diag: unable to alloc USB req on mdm ch err:%d\n",
+							 err);
+
+		diag_bridge[index].usb_connected = 1;
+	}
+
+	if (index == SMUX && driver->diag_smux_enabled) {
+		driver->in_busy_smux = 0;
+		diagfwd_connect_smux();
+	} else if (index == HSIC && driver->hsic_device_enabled) {
+		driver->in_busy_hsic_read_on_device = 0;
+		driver->in_busy_hsic_write = 0;
+		/* If the HSIC (diag_bridge) platform device is not open */
+		if (!driver->hsic_device_opened) {
+			err = diag_bridge_open(&hsic_diag_bridge_ops);
+			if (err) {
+				pr_err("diag: HSIC channel open error: %d\n",
+					  err);
+			} else {
+				pr_debug("diag: opened HSIC channel\n");
+				driver->hsic_device_opened = 1;
+			}
+		} else {
+			pr_debug("diag: HSIC channel already open\n");
+		}
+		/*
+		 * Turn on communication over usb mdm and HSIC, if the HSIC
+		 * device driver is enabled and opened
+		 */
+		if (driver->hsic_device_opened) {
+			driver->hsic_ch = 1;
+			/* Poll USB mdm channel to check for data */
+			if (driver->logging_mode == USB_MODE)
+				queue_work(diag_bridge[HSIC].wq,
+					  &diag_bridge[HSIC].diag_read_work);
+			/* Poll HSIC channel to check for data */
+			queue_work(diag_bridge[HSIC].wq,
+				   &driver->diag_read_hsic_work);
+		}
+	}
+	mutex_unlock(&diag_bridge[index].bridge_mutex);
+}
+
+/*
+ * diagfwd_disconnect_bridge is called when the USB mdm channel
+ * is disconnected. So disconnect should happen for all bridges
+ */
+int diagfwd_disconnect_bridge(int process_cable)
+{
+	int i;
+	pr_debug("diag: In %s, process_cable: %d\n", __func__, process_cable);
+
+	for (i = 0; i < MAX_BRIDGES; i++) {
+		if (diag_bridge[i].enabled) {
+			mutex_lock(&diag_bridge[i].bridge_mutex);
+			/* If the usb cable is being disconnected */
+			if (process_cable) {
+				diag_bridge[i].usb_connected = 0;
+				usb_diag_free_req(diag_bridge[i].ch);
+			}
+
+			if (i == HSIC && driver->hsic_device_enabled &&
+				 driver->logging_mode != MEMORY_DEVICE_MODE) {
+				driver->in_busy_hsic_read_on_device = 1;
+				driver->in_busy_hsic_write = 1;
+				/* Turn off communication over usb and HSIC */
+				diag_hsic_close();
+			} else if (i == SMUX && driver->diag_smux_enabled &&
+					driver->logging_mode == USB_MODE) {
+				driver->in_busy_smux = 1;
+				driver->lcid = LCID_INVALID;
+				driver->smux_connected = 0;
+				/* Turn off communication over usb and smux */
+				msm_smux_close(LCID_VALID);
+			}
+			mutex_unlock(&diag_bridge[i].bridge_mutex);
+		}
+	}
+	return 0;
+}
+
+/* Called after the asychronous usb_diag_read() on mdm channel is complete */
+int diagfwd_read_complete_bridge(struct diag_request *diag_read_ptr)
+{
+	 int index = (int)(diag_read_ptr->context);
+
+	/* The read of the usb on the mdm (not HSIC/SMUX) has completed */
+	diag_bridge[index].read_len = diag_read_ptr->actual;
+
+	if (index == SMUX) {
+		if (driver->diag_smux_enabled) {
+			diagfwd_read_complete_smux();
+			return 0;
+		} else {
+			pr_warning("diag: incorrect callback for smux\n");
+		}
+	}
+
+	/* If SMUX not enabled, check for HSIC */
+	driver->in_busy_hsic_read_on_device = 0;
+	if (!driver->hsic_ch) {
+		pr_err("DIAG in %s: driver->hsic_ch == 0\n", __func__);
+		return 0;
+	}
+
+	/*
+	 * The read of the usb driver on the mdm channel has completed.
+	 * If there is no write on the HSIC in progress, check if the
+	 * read has data to pass on to the HSIC. If so, pass the usb
+	 * mdm data on to the HSIC.
+	 */
+	if (!driver->in_busy_hsic_write && diag_bridge[HSIC].usb_buf_out &&
+		(diag_bridge[HSIC].read_len > 0)) {
+
+		/*
+		 * Initiate the HSIC write. The HSIC write is
+		 * asynchronous. When complete the write
+		 * complete callback function will be called
+		 */
+		int err;
+		driver->in_busy_hsic_write = 1;
+		err = diag_bridge_write(diag_bridge[HSIC].usb_buf_out,
+					diag_bridge[HSIC].read_len);
+		if (err) {
+			pr_err_ratelimited("diag: mdm data on HSIC write err: %d\n",
+					err);
+			/*
+			 * If the error is recoverable, then clear
+			 * the write flag, so we will resubmit a
+			 * write on the next frame.  Otherwise, don't
+			 * resubmit a write on the next frame.
+			 */
+			if ((-ENODEV) != err)
+				driver->in_busy_hsic_write = 0;
+		}
+	}
+
+	/*
+	 * If there is no write of the usb mdm data on the
+	 * HSIC channel
+	 */
+	if (!driver->in_busy_hsic_write)
+		queue_work(diag_bridge[HSIC].wq,
+			 &diag_bridge[HSIC].diag_read_work);
+
+	return 0;
+}
+
+static void diagfwd_bridge_notifier(void *priv, unsigned event,
+					struct diag_request *d_req)
+{
+	int index;
+
+	switch (event) {
+	case USB_DIAG_CONNECT:
+		diagfwd_connect_bridge(1);
+		break;
+	case USB_DIAG_DISCONNECT:
+		queue_work(driver->diag_wq,
+			 &driver->diag_disconnect_work);
+		break;
+	case USB_DIAG_READ_DONE:
+		index = (int)(d_req->context);
+		queue_work(diag_bridge[index].wq,
+		&diag_bridge[index].usb_read_complete_work);
+		break;
+	case USB_DIAG_WRITE_DONE:
+		index = (int)(d_req->context);
+		if (index == HSIC &&  driver->hsic_device_enabled)
+			diagfwd_write_complete_hsic(d_req);
+		else if (index == SMUX && driver->diag_smux_enabled)
+			diagfwd_write_complete_smux();
+		break;
+	default:
+		pr_err("diag: in %s: Unknown event from USB diag:%u\n",
+			__func__, event);
+		break;
+	}
+}
+
+void diagfwd_bridge_init(int index)
+{
+	int ret;
+	unsigned char name[20];
+
+	if (index == HSIC)
+		strlcpy(name, "hsic", sizeof(name));
+	else
+		strlcpy(name, "smux", sizeof(name));
+
+	strlcpy(diag_bridge[index].name, name, sizeof(diag_bridge[index].name));
+	strlcat(name, "_diag_wq", sizeof(diag_bridge[index].name));
+	diag_bridge[index].enabled = 1;
+	diag_bridge[index].wq = create_singlethread_workqueue(name);
+	diag_bridge[index].read_len = 0;
+	diag_bridge[index].write_len = 0;
+	if (diag_bridge[index].usb_buf_out == NULL)
+		diag_bridge[index].usb_buf_out =
+				 kzalloc(USB_MAX_OUT_BUF, GFP_KERNEL);
+	if (diag_bridge[index].usb_buf_out == NULL)
+		goto err;
+	if (diag_bridge[index].usb_read_ptr == NULL)
+		diag_bridge[index].usb_read_ptr =
+			 kzalloc(sizeof(struct diag_request), GFP_KERNEL);
+	if (diag_bridge[index].usb_read_ptr == NULL)
+		goto err;
+	if (diag_bridge[index].usb_read_ptr->context == NULL)
+		diag_bridge[index].usb_read_ptr->context =
+					 kzalloc(sizeof(int), GFP_KERNEL);
+	if (diag_bridge[index].usb_read_ptr->context == NULL)
+		goto err;
+	mutex_init(&diag_bridge[index].bridge_mutex);
+
+	if (index == HSIC) {
+		INIT_WORK(&(diag_bridge[index].usb_read_complete_work),
+				 diag_usb_read_complete_hsic_fn);
+#ifdef CONFIG_DIAG_OVER_USB
+		INIT_WORK(&(diag_bridge[index].diag_read_work),
+		      diag_read_usb_hsic_work_fn);
+		diag_bridge[index].ch = usb_diag_open(DIAG_MDM, (void *)index,
+						  diagfwd_bridge_notifier);
+		if (IS_ERR(diag_bridge[index].ch)) {
+			pr_err("diag: Unable to open USB diag MDM channel\n");
+			goto err;
+		}
+#endif
+		/* register HSIC device */
+		ret = platform_driver_register(&msm_hsic_ch_driver);
+		if (ret)
+			pr_err("diag: could not register HSIC device, ret: %d\n",
+									 ret);
+	} else if (index == SMUX) {
+		INIT_WORK(&(diag_bridge[index].usb_read_complete_work),
+					 diag_usb_read_complete_smux_fn);
+#ifdef CONFIG_DIAG_OVER_USB
+		INIT_WORK(&(diag_bridge[index].diag_read_work),
+					 diag_read_usb_smux_work_fn);
+		diag_bridge[index].ch = usb_diag_open(DIAG_QSC, (void *)index,
+					     diagfwd_bridge_notifier);
+		if (IS_ERR(diag_bridge[index].ch)) {
+			pr_err("diag: Unable to open USB diag QSC channel\n");
+			goto err;
+		}
+#endif
+		ret = platform_driver_register(&msm_diagfwd_smux_driver);
+		if (ret)
+			pr_err("diag: could not register SMUX device, ret: %d\n",
+									 ret);
+	}
+	 return;
+err:
+	pr_err("diag: Could not initialize for bridge forwarding\n");
+	kfree(diag_bridge[index].usb_buf_out);
+	kfree(driver->hsic_buf_tbl);
+	kfree(driver->write_ptr_mdm);
+	kfree(diag_bridge[index].usb_read_ptr);
+	if (diag_bridge[index].wq)
+		destroy_workqueue(diag_bridge[index].wq);
+	return;
+}
+
+void diagfwd_bridge_exit(void)
+{
+	int i;
+	pr_debug("diag: in %s\n", __func__);
+
+	if (driver->hsic_device_enabled) {
+		diag_hsic_close();
+		driver->hsic_device_enabled = 0;
+		diag_bridge[HSIC].enabled = 0;
+	}
+	driver->hsic_inited = 0;
+	diagmem_exit(driver, POOL_TYPE_ALL);
+	if (driver->diag_smux_enabled) {
+		driver->lcid = LCID_INVALID;
+		kfree(driver->buf_in_smux);
+		driver->diag_smux_enabled = 0;
+		diag_bridge[SMUX].enabled = 0;
+	}
+	platform_driver_unregister(&msm_hsic_ch_driver);
+	platform_driver_unregister(&msm_diagfwd_smux_driver);
+	/* destroy USB MDM specific variables */
+	for (i = 0; i < MAX_BRIDGES; i++) {
+		if (diag_bridge[i].enabled) {
+#ifdef CONFIG_DIAG_OVER_USB
+			if (diag_bridge[i].usb_connected)
+				usb_diag_free_req(diag_bridge[i].ch);
+			usb_diag_close(diag_bridge[i].ch);
+#endif
+			kfree(diag_bridge[i].usb_buf_out);
+			kfree(diag_bridge[i].usb_read_ptr);
+			destroy_workqueue(diag_bridge[i].wq);
+			diag_bridge[i].enabled = 0;
+		}
+	}
+	kfree(driver->hsic_buf_tbl);
+	kfree(driver->write_ptr_mdm);
+}
diff --git a/drivers/char/diag/diagfwd_bridge.h b/drivers/char/diag/diagfwd_bridge.h
new file mode 100644
index 0000000..06e6a96
--- /dev/null
+++ b/drivers/char/diag/diagfwd_bridge.h
@@ -0,0 +1,47 @@
+/* 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 DIAGFWD_BRIDGE_H
+#define DIAGFWD_BRIDGE_H
+
+#include "diagfwd.h"
+
+#define MAX_BRIDGES	5
+#define HSIC	0
+#define SMUX	1
+
+int diagfwd_connect_bridge(int);
+void connect_bridge(int, int);
+int diagfwd_disconnect_bridge(int);
+void diagfwd_bridge_init(int index);
+void diagfwd_bridge_exit(void);
+int diagfwd_read_complete_bridge(struct diag_request *diag_read_ptr);
+
+/* Diag-Bridge structure, n bridges can be used at same time
+ * for instance SMUX, HSIC working at same time
+ */
+struct diag_bridge_dev {
+	char name[20];
+	int enabled;
+	struct mutex bridge_mutex;
+	int usb_connected;
+	int read_len;
+	int write_len;
+	unsigned char *usb_buf_out;
+	struct usb_diag_ch *ch;
+	struct workqueue_struct *wq;
+	struct work_struct diag_read_work;
+	struct diag_request *usb_read_ptr;
+	struct work_struct usb_read_complete_work;
+};
+
+#endif
diff --git a/drivers/char/diag/diagfwd_hsic.c b/drivers/char/diag/diagfwd_hsic.c
index 56f2fae..3d5eea5 100644
--- a/drivers/char/diag/diagfwd_hsic.c
+++ b/drivers/char/diag/diagfwd_hsic.c
@@ -31,6 +31,7 @@
 #include "diagfwd.h"
 #include "diagfwd_hsic.h"
 #include "diagfwd_smux.h"
+#include "diagfwd_bridge.h"
 
 #define READ_HSIC_BUF_SIZE 2048
 
@@ -72,7 +73,7 @@
 		write_ptrs_available--;
 
 		/*
-		 * No sense queuing a read if the hsic bridge was
+		 * No sense queuing a read if the HSIC bridge was
 		 * closed in another thread
 		 */
 		if (!driver->hsic_ch)
@@ -82,7 +83,7 @@
 							POOL_TYPE_HSIC);
 		if (buf_in_hsic) {
 			/*
-			 * Initiate the read from the hsic.  The hsic read is
+			 * Initiate the read from the HSIC.  The HSIC read is
 			 * asynchronous.  Once the read is complete the read
 			 * callback function will be called.
 			 */
@@ -116,7 +117,7 @@
 	if ((driver->count_hsic_pool < driver->poolsize_hsic) &&
 		(num_reads_submitted == 0) && (err != -ENODEV) &&
 		(driver->hsic_ch != 0))
-		queue_work(driver->diag_bridge_wq,
+		queue_work(diag_bridge[HSIC].wq,
 				 &driver->diag_read_hsic_work);
 }
 
@@ -127,7 +128,7 @@
 
 	if (!driver->hsic_ch) {
 		/*
-		 * The hsic channel is closed. Return the buffer to
+		 * The HSIC channel is closed. Return the buffer to
 		 * the pool.  Do not send it on.
 		 */
 		diagmem_free(driver, buf, POOL_TYPE_HSIC);
@@ -149,7 +150,7 @@
 			 * Send data in buf to be written on the
 			 * appropriate device, e.g. USB MDM channel
 			 */
-			driver->write_len_mdm = actual_size;
+			diag_bridge[HSIC].write_len = actual_size;
 			err = diag_device_write((void *)buf, HSIC_DATA, NULL);
 			/* If an error, return buffer to the pool */
 			if (err) {
@@ -170,13 +171,13 @@
 	}
 
 	/*
-	 * If for some reason there was no hsic data to write to the
+	 * If for some reason there was no HSIC data to write to the
 	 * mdm channel, set up another read
 	 */
 	if (err &&
 		((driver->logging_mode == MEMORY_DEVICE_MODE) ||
-		(driver->usb_mdm_connected && !driver->hsic_suspend))) {
-		queue_work(driver->diag_bridge_wq,
+		(diag_bridge[HSIC].usb_connected && !driver->hsic_suspend))) {
+		queue_work(diag_bridge[HSIC].wq,
 				 &driver->diag_read_hsic_work);
 	}
 }
@@ -195,8 +196,10 @@
 	if (actual_size < 0)
 		pr_err("DIAG in %s: actual_size: %d\n", __func__, actual_size);
 
-	if (driver->usb_mdm_connected)
-		queue_work(driver->diag_bridge_wq, &driver->diag_read_mdm_work);
+	if (diag_bridge[HSIC].usb_connected &&
+				 (driver->logging_mode == USB_MODE))
+		queue_work(diag_bridge[HSIC].wq,
+				 &diag_bridge[HSIC].diag_read_work);
 }
 
 static int diag_hsic_suspend(void *ctxt)
@@ -223,12 +226,12 @@
 
 	if ((driver->count_hsic_pool < driver->poolsize_hsic) &&
 		((driver->logging_mode == MEMORY_DEVICE_MODE) ||
-				(driver->usb_mdm_connected)))
-		queue_work(driver->diag_bridge_wq,
+				(diag_bridge[HSIC].usb_connected)))
+		queue_work(diag_bridge[HSIC].wq,
 			 &driver->diag_read_hsic_work);
 }
 
-static struct diag_bridge_ops hsic_diag_bridge_ops = {
+struct diag_bridge_ops hsic_diag_bridge_ops = {
 	.ctxt = NULL,
 	.read_complete_cb = diag_hsic_read_complete_callback,
 	.write_complete_cb = diag_hsic_write_complete_callback,
@@ -236,7 +239,7 @@
 	.resume = diag_hsic_resume,
 };
 
-static void diag_hsic_close(void)
+void diag_hsic_close(void)
 {
 	if (driver->hsic_device_enabled) {
 		driver->hsic_ch = 0;
@@ -257,7 +260,7 @@
 {
 	int err;
 
-	mutex_lock(&driver->bridge_mutex);
+	mutex_lock(&diag_bridge[HSIC].bridge_mutex);
 	if (driver->hsic_device_enabled) {
 		if (driver->hsic_device_opened) {
 			driver->hsic_ch = 0;
@@ -274,112 +277,7 @@
 			}
 		}
 	}
-
-	mutex_unlock(&driver->bridge_mutex);
-	return 0;
-}
-
-/* diagfwd_connect_bridge is called when the USB mdm channel is connected */
-int diagfwd_connect_bridge(int process_cable)
-{
-	int err;
-
-	pr_debug("diag: in %s\n", __func__);
-
-	mutex_lock(&driver->bridge_mutex);
-	/* If the usb cable is being connected */
-	if (process_cable) {
-		err = usb_diag_alloc_req(driver->mdm_ch, N_MDM_WRITE,
-			N_MDM_READ);
-		if (err)
-			pr_err("diag: unable to alloc USB req on mdm"
-				" ch err:%d\n", err);
-
-		driver->usb_mdm_connected = 1;
-	}
-
-	if (driver->hsic_device_enabled) {
-		driver->in_busy_hsic_read_on_device = 0;
-		driver->in_busy_hsic_write = 0;
-	} else if (driver->diag_smux_enabled) {
-		driver->in_busy_smux = 0;
-		diagfwd_connect_smux();
-		mutex_unlock(&driver->bridge_mutex);
-		return 0;
-	}
-
-	/* If the hsic (diag_bridge) platform device is not open */
-	if (driver->hsic_device_enabled) {
-		if (!driver->hsic_device_opened) {
-			err = diag_bridge_open(&hsic_diag_bridge_ops);
-			if (err) {
-				pr_err("diag: HSIC channel open error: %d\n",
-					err);
-			} else {
-				pr_debug("diag: opened HSIC channel\n");
-				driver->hsic_device_opened = 1;
-			}
-		} else {
-			pr_debug("diag: HSIC channel already open\n");
-		}
-
-		/*
-		 * Turn on communication over usb mdm and hsic, if the hsic
-		 * device driver is enabled and opened
-		 */
-		if (driver->hsic_device_opened) {
-			driver->hsic_ch = 1;
-
-			/* Poll USB mdm channel to check for data */
-			if (driver->logging_mode == USB_MODE)
-				queue_work(driver->diag_bridge_wq,
-						&driver->diag_read_mdm_work);
-
-			/* Poll HSIC channel to check for data */
-			queue_work(driver->diag_bridge_wq,
-					 &driver->diag_read_hsic_work);
-		}
-	} else {
-		/* The hsic device driver has not yet been enabled */
-		pr_info("diag: HSIC channel not yet enabled\n");
-	}
-
-	mutex_unlock(&driver->bridge_mutex);
-	return 0;
-}
-
-/*
- * diagfwd_disconnect_bridge is called when the USB mdm channel
- * is disconnected
- */
-int diagfwd_disconnect_bridge(int process_cable)
-{
-	pr_debug("diag: In %s, process_cable: %d\n", __func__, process_cable);
-
-	mutex_lock(&driver->bridge_mutex);
-
-	/* If the usb cable is being disconnected */
-	if (process_cable) {
-		driver->usb_mdm_connected = 0;
-		usb_diag_free_req(driver->mdm_ch);
-	}
-
-	if (driver->hsic_device_enabled &&
-		driver->logging_mode != MEMORY_DEVICE_MODE) {
-		driver->in_busy_hsic_read_on_device = 1;
-		driver->in_busy_hsic_write = 1;
-		/* Turn off communication over usb mdm and hsic */
-		diag_hsic_close();
-	} else if (driver->diag_smux_enabled &&
-		driver->logging_mode == USB_MODE) {
-		driver->in_busy_smux = 1;
-		driver->lcid = LCID_INVALID;
-		driver->smux_connected = 0;
-		/* Turn off communication over usb mdm and smux */
-		msm_smux_close(LCID_VALID);
-	}
-
-	mutex_unlock(&driver->bridge_mutex);
+	mutex_unlock(&diag_bridge[HSIC].bridge_mutex);
 	return 0;
 }
 
@@ -403,225 +301,128 @@
 		return 0;
 	}
 
-	/* Read data from the hsic */
-	queue_work(driver->diag_bridge_wq, &driver->diag_read_hsic_work);
+	/* Read data from the HSIC */
+	queue_work(diag_bridge[HSIC].wq, &driver->diag_read_hsic_work);
 
 	return 0;
 }
 
-/* Called after the asychronous usb_diag_read() on mdm channel is complete */
-static int diagfwd_read_complete_bridge(struct diag_request *diag_read_ptr)
+void diag_usb_read_complete_hsic_fn(struct work_struct *w)
 {
-	/* The read of the usb driver on the mdm (not hsic) has completed */
-	driver->in_busy_hsic_read_on_device = 0;
-	driver->read_len_mdm = diag_read_ptr->actual;
+	diagfwd_read_complete_bridge(diag_bridge[HSIC].usb_read_ptr);
+}
 
-	if (driver->diag_smux_enabled) {
-		diagfwd_read_complete_smux();
-		return 0;
-	}
-	/* If SMUX not enabled, check for HSIC */
+
+void diag_read_usb_hsic_work_fn(struct work_struct *work)
+{
 	if (!driver->hsic_ch) {
-		pr_err("DIAG in %s: driver->hsic_ch == 0\n", __func__);
-		return 0;
-	}
-
-	/*
-	 * The read of the usb driver on the mdm channel has completed.
-	 * If there is no write on the hsic in progress, check if the
-	 * read has data to pass on to the hsic. If so, pass the usb
-	 * mdm data on to the hsic.
-	 */
-	if (!driver->in_busy_hsic_write && driver->usb_buf_mdm_out &&
-		(driver->read_len_mdm > 0)) {
-
-		/*
-		 * Initiate the hsic write. The hsic write is
-		 * asynchronous. When complete the write
-		 * complete callback function will be called
-		 */
-		int err;
-		driver->in_busy_hsic_write = 1;
-		err = diag_bridge_write(driver->usb_buf_mdm_out,
-					driver->read_len_mdm);
-		if (err) {
-			pr_err_ratelimited("diag: mdm data on hsic write err: %d\n",
-					err);
-			/*
-			 * If the error is recoverable, then clear
-			 * the write flag, so we will resubmit a
-			 * write on the next frame.  Otherwise, don't
-			 * resubmit a write on the next frame.
-			 */
-			if ((-ENODEV) != err)
-				driver->in_busy_hsic_write = 0;
-		}
-	}
-
-	/*
-	 * If there is no write of the usb mdm data on the
-	 * hsic channel
-	 */
-	if (!driver->in_busy_hsic_write)
-		queue_work(driver->diag_bridge_wq, &driver->diag_read_mdm_work);
-
-	return 0;
-}
-
-static void diagfwd_bridge_notifier(void *priv, unsigned event,
-					struct diag_request *d_req)
-{
-	switch (event) {
-	case USB_DIAG_CONNECT:
-		diagfwd_connect_bridge(1);
-		break;
-	case USB_DIAG_DISCONNECT:
-		queue_work(driver->diag_bridge_wq,
-			 &driver->diag_disconnect_work);
-		break;
-	case USB_DIAG_READ_DONE:
-		queue_work(driver->diag_bridge_wq,
-				&driver->diag_usb_read_complete_work);
-		break;
-	case USB_DIAG_WRITE_DONE:
-		if (driver->hsic_device_enabled)
-			diagfwd_write_complete_hsic(d_req);
-		else if (driver->diag_smux_enabled)
-			diagfwd_write_complete_smux();
-		break;
-	default:
-		pr_err("diag: in %s: Unknown event from USB diag:%u\n",
-			__func__, event);
-		break;
-	}
-}
-
-static void diag_usb_read_complete_fn(struct work_struct *w)
-{
-	diagfwd_read_complete_bridge(driver->usb_read_mdm_ptr);
-}
-
-static void diag_disconnect_work_fn(struct work_struct *w)
-{
-	diagfwd_disconnect_bridge(1);
-}
-
-static void diag_read_mdm_work_fn(struct work_struct *work)
-{
-	int ret;
-	if (driver->diag_smux_enabled) {
-		if (driver->lcid && driver->usb_buf_mdm_out &&
-				(driver->read_len_mdm > 0) &&
-				driver->smux_connected) {
-			ret = msm_smux_write(driver->lcid,  NULL,
-		 driver->usb_buf_mdm_out, driver->read_len_mdm);
-			if (ret)
-				pr_err("diag: writing to SMUX ch, r = %d,"
-					"lcid = %d\n", ret, driver->lcid);
-		}
-		driver->usb_read_mdm_ptr->buf = driver->usb_buf_mdm_out;
-		driver->usb_read_mdm_ptr->length = USB_MAX_OUT_BUF;
-		usb_diag_read(driver->mdm_ch, driver->usb_read_mdm_ptr);
+		pr_err("diag: in %s: driver->hsic_ch == 0\n", __func__);
 		return;
 	}
-
-	/* if SMUX not enabled, check for HSIC */
-	if (!driver->hsic_ch) {
-		pr_err("DIAG in %s: driver->hsic_ch == 0\n", __func__);
-		return;
-	}
-
 	/*
 	 * If there is no data being read from the usb mdm channel
 	 * and there is no mdm channel data currently being written
-	 * to the hsic
+	 * to the HSIC
 	 */
 	if (!driver->in_busy_hsic_read_on_device &&
-				 !driver->in_busy_hsic_write) {
+	     !driver->in_busy_hsic_write) {
 		APPEND_DEBUG('x');
-
 		/* Setup the next read from usb mdm channel */
 		driver->in_busy_hsic_read_on_device = 1;
-		driver->usb_read_mdm_ptr->buf = driver->usb_buf_mdm_out;
-		driver->usb_read_mdm_ptr->length = USB_MAX_OUT_BUF;
-		usb_diag_read(driver->mdm_ch, driver->usb_read_mdm_ptr);
+		diag_bridge[HSIC].usb_read_ptr->buf =
+				 diag_bridge[HSIC].usb_buf_out;
+		diag_bridge[HSIC].usb_read_ptr->length = USB_MAX_OUT_BUF;
+		diag_bridge[HSIC].usb_read_ptr->context = (void *)HSIC;
+		usb_diag_read(diag_bridge[HSIC].ch,
+				 diag_bridge[HSIC].usb_read_ptr);
 		APPEND_DEBUG('y');
 	}
-
-	/*
-	 * If for some reason there was no mdm channel read initiated,
+	/* If for some reason there was no mdm channel read initiated,
 	 * queue up the reading of data from the mdm channel
 	 */
-	if (!driver->in_busy_hsic_read_on_device)
-		queue_work(driver->diag_bridge_wq,
-			 &driver->diag_read_mdm_work);
+
+	if (!driver->in_busy_hsic_read_on_device &&
+		(driver->logging_mode == USB_MODE))
+		queue_work(diag_bridge[HSIC].wq,
+			 &(diag_bridge[HSIC].diag_read_work));
 }
 
 static int diag_hsic_probe(struct platform_device *pdev)
 {
 	int err = 0;
+
 	pr_debug("diag: in %s\n", __func__);
+	mutex_lock(&diag_bridge[HSIC].bridge_mutex);
 	if (!driver->hsic_inited) {
+		spin_lock_init(&driver->hsic_spinlock);
+		driver->num_hsic_buf_tbl_entries = 0;
+		if (driver->hsic_buf_tbl == NULL)
+			driver->hsic_buf_tbl = kzalloc(NUM_HSIC_BUF_TBL_ENTRIES
+				* sizeof(struct diag_write_device), GFP_KERNEL);
+		if (driver->hsic_buf_tbl == NULL) {
+			mutex_unlock(&diag_bridge[HSIC].bridge_mutex);
+			return -ENOMEM;
+		}
+		driver->count_hsic_pool = 0;
+		driver->count_hsic_write_pool = 0;
+		driver->itemsize_hsic = READ_HSIC_BUF_SIZE;
+		driver->poolsize_hsic = N_MDM_WRITE;
+		driver->itemsize_hsic_write = sizeof(struct diag_request);
+		driver->poolsize_hsic_write = N_MDM_WRITE;
 		diagmem_hsic_init(driver);
 		INIT_WORK(&(driver->diag_read_hsic_work),
-					 diag_read_hsic_work_fn);
+			    diag_read_hsic_work_fn);
 		driver->hsic_inited = 1;
 	}
-
-	mutex_lock(&driver->bridge_mutex);
-
 	/*
 	 * The probe function was called after the usb was connected
 	 * on the legacy channel OR ODL is turned on. Communication over usb
-	 * mdm and hsic needs to be turned on.
+	 * mdm and HSIC needs to be turned on.
 	 */
-	if (driver->usb_mdm_connected || (driver->logging_mode ==
-							 MEMORY_DEVICE_MODE)) {
+	if (diag_bridge[HSIC].usb_connected || (driver->logging_mode ==
+						   MEMORY_DEVICE_MODE)) {
 		if (driver->hsic_device_opened) {
 			/* should not happen. close it before re-opening */
 			pr_warn("diag: HSIC channel already opened in probe\n");
 			diag_bridge_close();
 		}
-
 		err = diag_bridge_open(&hsic_diag_bridge_ops);
 		if (err) {
 			pr_err("diag: could not open HSIC, err: %d\n", err);
 			driver->hsic_device_opened = 0;
-			mutex_unlock(&driver->bridge_mutex);
+			mutex_unlock(&diag_bridge[HSIC].bridge_mutex);
 			return err;
 		}
 
 		pr_info("diag: opened HSIC channel\n");
 		driver->hsic_device_opened = 1;
 		driver->hsic_ch = 1;
-
 		driver->in_busy_hsic_read_on_device = 0;
 		driver->in_busy_hsic_write = 0;
 
-		if (driver->usb_mdm_connected) {
+		if (diag_bridge[HSIC].usb_connected) {
 			/* Poll USB mdm channel to check for data */
-			queue_work(driver->diag_bridge_wq,
-					 &driver->diag_read_mdm_work);
+			queue_work(diag_bridge[HSIC].wq,
+			     &diag_bridge[HSIC].diag_read_work);
 		}
-
 		/* Poll HSIC channel to check for data */
-		queue_work(driver->diag_bridge_wq,
-				 &driver->diag_read_hsic_work);
+		queue_work(diag_bridge[HSIC].wq,
+			  &driver->diag_read_hsic_work);
 	}
-
-	/* The hsic (diag_bridge) platform device driver is enabled */
+	/* The HSIC (diag_bridge) platform device driver is enabled */
 	driver->hsic_device_enabled = 1;
-	mutex_unlock(&driver->bridge_mutex);
+	mutex_unlock(&diag_bridge[HSIC].bridge_mutex);
 	return err;
 }
 
 static int diag_hsic_remove(struct platform_device *pdev)
 {
 	pr_debug("diag: %s called\n", __func__);
-	mutex_lock(&driver->bridge_mutex);
+	mutex_lock(&diag_bridge[HSIC].bridge_mutex);
 	diag_hsic_close();
 	driver->hsic_device_enabled = 0;
-	mutex_unlock(&driver->bridge_mutex);
+	mutex_unlock(&diag_bridge[HSIC].bridge_mutex);
+
 	return 0;
 }
 
@@ -642,7 +443,7 @@
 	.runtime_resume = diagfwd_hsic_runtime_resume,
 };
 
-static struct platform_driver msm_hsic_ch_driver = {
+struct platform_driver msm_hsic_ch_driver = {
 	.probe = diag_hsic_probe,
 	.remove = diag_hsic_remove,
 	.driver = {
@@ -651,112 +452,3 @@
 		   .pm   = &diagfwd_hsic_dev_pm_ops,
 		   },
 };
-
-void diagfwd_bridge_init(void)
-{
-	int ret;
-
-	pr_debug("diag: in %s\n", __func__);
-	driver->diag_bridge_wq = create_singlethread_workqueue(
-							"diag_bridge_wq");
-	driver->read_len_mdm = 0;
-	driver->write_len_mdm = 0;
-	driver->num_hsic_buf_tbl_entries = 0;
-	spin_lock_init(&driver->hsic_spinlock);
-	if (driver->usb_buf_mdm_out  == NULL)
-		driver->usb_buf_mdm_out = kzalloc(USB_MAX_OUT_BUF,
-							 GFP_KERNEL);
-	if (driver->usb_buf_mdm_out == NULL)
-		goto err;
-	/* Only used by smux move to smux probe function */
-	if (driver->write_ptr_mdm == NULL)
-		driver->write_ptr_mdm = kzalloc(
-		sizeof(struct diag_request), GFP_KERNEL);
-	if (driver->write_ptr_mdm == NULL)
-		goto err;
-	if (driver->usb_read_mdm_ptr == NULL)
-		driver->usb_read_mdm_ptr = kzalloc(
-		sizeof(struct diag_request), GFP_KERNEL);
-	if (driver->usb_read_mdm_ptr == NULL)
-		goto err;
-
-	if (driver->hsic_buf_tbl == NULL)
-		driver->hsic_buf_tbl = kzalloc(NUM_HSIC_BUF_TBL_ENTRIES *
-				sizeof(struct diag_write_device), GFP_KERNEL);
-	if (driver->hsic_buf_tbl == NULL)
-		goto err;
-
-	driver->count_hsic_pool = 0;
-	driver->count_hsic_write_pool = 0;
-
-	driver->itemsize_hsic = READ_HSIC_BUF_SIZE;
-	driver->poolsize_hsic = N_MDM_WRITE;
-	driver->itemsize_hsic_write = sizeof(struct diag_request);
-	driver->poolsize_hsic_write = N_MDM_WRITE;
-
-	mutex_init(&driver->bridge_mutex);
-#ifdef CONFIG_DIAG_OVER_USB
-	INIT_WORK(&(driver->diag_read_mdm_work), diag_read_mdm_work_fn);
-#endif
-	INIT_WORK(&(driver->diag_disconnect_work), diag_disconnect_work_fn);
-	INIT_WORK(&(driver->diag_usb_read_complete_work),
-			diag_usb_read_complete_fn);
-#ifdef CONFIG_DIAG_OVER_USB
-	driver->mdm_ch = usb_diag_open(DIAG_MDM, driver,
-						 diagfwd_bridge_notifier);
-	if (IS_ERR(driver->mdm_ch)) {
-		pr_err("diag: Unable to open USB diag MDM channel\n");
-		goto err;
-	}
-#endif
-	/* register HSIC device */
-	ret = platform_driver_register(&msm_hsic_ch_driver);
-	if (ret)
-		pr_err("diag: could not register HSIC device, ret: %d\n", ret);
-	/* register SMUX device */
-	ret = platform_driver_register(&msm_diagfwd_smux_driver);
-	if (ret)
-		pr_err("diag: could not register SMUX device, ret: %d\n", ret);
-
-	return;
-err:
-	pr_err("diag: Could not initialize for bridge forwarding\n");
-	kfree(driver->usb_buf_mdm_out);
-	kfree(driver->hsic_buf_tbl);
-	kfree(driver->write_ptr_mdm);
-	kfree(driver->usb_read_mdm_ptr);
-	if (driver->diag_bridge_wq)
-		destroy_workqueue(driver->diag_bridge_wq);
-
-	return;
-}
-
-void diagfwd_bridge_exit(void)
-{
-	pr_debug("diag: in %s\n", __func__);
-
-	if (driver->hsic_device_enabled) {
-		diag_hsic_close();
-		driver->hsic_device_enabled = 0;
-	}
-	driver->hsic_inited = 0;
-	diagmem_exit(driver, POOL_TYPE_ALL);
-	if (driver->diag_smux_enabled) {
-		driver->lcid = LCID_INVALID;
-		kfree(driver->buf_in_smux);
-		driver->diag_smux_enabled = 0;
-	}
-	platform_driver_unregister(&msm_hsic_ch_driver);
-	platform_driver_unregister(&msm_diagfwd_smux_driver);
-	/* destroy USB MDM specific variables */
-#ifdef CONFIG_DIAG_OVER_USB
-	if (driver->usb_mdm_connected)
-		usb_diag_free_req(driver->mdm_ch);
-	usb_diag_close(driver->mdm_ch);
-#endif
-	kfree(driver->usb_buf_mdm_out);
-	kfree(driver->hsic_buf_tbl);
-	kfree(driver->write_ptr_mdm);
-	kfree(driver->usb_read_mdm_ptr);
-	destroy_workqueue(driver->diag_bridge_wq);
-}
diff --git a/drivers/char/diag/diagfwd_hsic.h b/drivers/char/diag/diagfwd_hsic.h
index 19ed3c7..2190fff 100644
--- a/drivers/char/diag/diagfwd_hsic.h
+++ b/drivers/char/diag/diagfwd_hsic.h
@@ -17,14 +17,14 @@
 
 #define N_MDM_WRITE	8
 #define N_MDM_READ	1
-
 #define NUM_HSIC_BUF_TBL_ENTRIES N_MDM_WRITE
 
-int diagfwd_connect_bridge(int);
-int diagfwd_disconnect_bridge(int);
 int diagfwd_write_complete_hsic(struct diag_request *);
 int diagfwd_cancel_hsic(void);
-void diagfwd_bridge_init(void);
-void diagfwd_bridge_exit(void);
+void diag_read_usb_hsic_work_fn(struct work_struct *work);
+void diag_usb_read_complete_hsic_fn(struct work_struct *w);
+extern struct diag_bridge_ops hsic_diag_bridge_ops;
+extern struct platform_driver msm_hsic_ch_driver;
+void diag_hsic_close(void);
 
 #endif
diff --git a/drivers/char/diag/diagfwd_smux.c b/drivers/char/diag/diagfwd_smux.c
index ae90686..0a97baf 100644
--- a/drivers/char/diag/diagfwd_smux.c
+++ b/drivers/char/diag/diagfwd_smux.c
@@ -18,6 +18,8 @@
 #include "diagchar.h"
 #include "diagfwd.h"
 #include "diagfwd_smux.h"
+#include "diagfwd_hsic.h"
+#include "diagfwd_bridge.h"
 
 void diag_smux_event(void *priv, int event_type, const void *metadata)
 {
@@ -30,8 +32,8 @@
 		driver->smux_connected = 1;
 		driver->in_busy_smux = 0;
 		/* read data from USB MDM channel & Initiate first write */
-		queue_work(driver->diag_bridge_wq,
-				 &(driver->diag_read_mdm_work));
+		queue_work(diag_bridge[SMUX].wq,
+			   &diag_bridge[SMUX].diag_read_work);
 		break;
 	case SMUX_DISCONNECTED:
 		driver->smux_connected = 0;
@@ -67,7 +69,7 @@
 
 int diagfwd_read_complete_smux(void)
 {
-	queue_work(driver->diag_bridge_wq, &(driver->diag_read_mdm_work));
+	queue_work(diag_bridge[SMUX].wq, &diag_bridge[SMUX].diag_read_work);
 	return 0;
 }
 
@@ -85,6 +87,36 @@
 	return 0;
 }
 
+void diag_usb_read_complete_smux_fn(struct work_struct *w)
+{
+	diagfwd_read_complete_bridge(diag_bridge[SMUX].usb_read_ptr);
+}
+
+void diag_read_usb_smux_work_fn(struct work_struct *work)
+{
+	int ret;
+
+	if (driver->diag_smux_enabled) {
+		if (driver->lcid && diag_bridge[SMUX].usb_buf_out &&
+			(diag_bridge[SMUX].read_len > 0) &&
+				driver->smux_connected) {
+			ret = msm_smux_write(driver->lcid, NULL,
+			      diag_bridge[SMUX].usb_buf_out,
+				 diag_bridge[SMUX].read_len);
+			if (ret)
+				pr_err("diag: writing to SMUX ch, r = %d, lcid = %d\n",
+						 ret, driver->lcid);
+		}
+		diag_bridge[SMUX].usb_read_ptr->buf =
+					 diag_bridge[SMUX].usb_buf_out;
+		diag_bridge[SMUX].usb_read_ptr->length = USB_MAX_OUT_BUF;
+		diag_bridge[SMUX].usb_read_ptr->context = (void *)SMUX;
+		usb_diag_read(diag_bridge[SMUX].ch,
+					 diag_bridge[SMUX].usb_read_ptr);
+		return;
+	}
+}
+
 static int diagfwd_smux_runtime_suspend(struct device *dev)
 {
 	dev_dbg(dev, "pm_runtime: suspending...\n");
@@ -120,7 +152,7 @@
 		}
 	}
 	/* Poll USB channel to check for data*/
-	queue_work(driver->diag_bridge_wq, &(driver->diag_read_mdm_work));
+	queue_work(diag_bridge[SMUX].wq, &(diag_bridge[SMUX].diag_read_work));
 	return ret;
 }
 
@@ -142,6 +174,11 @@
 	 * if (ret)
 	 *	pr_err("diag: error setting SMUX ch option, r = %d\n", ret);
 	 */
+	if (driver->write_ptr_mdm == NULL)
+		driver->write_ptr_mdm = kzalloc(sizeof(struct diag_request),
+								 GFP_KERNEL);
+	if (driver->write_ptr_mdm == NULL)
+		goto err;
 	ret = diagfwd_connect_smux();
 	return ret;
 
diff --git a/drivers/char/diag/diagfwd_smux.h b/drivers/char/diag/diagfwd_smux.h
index e78b7ed..b45fd5d 100644
--- a/drivers/char/diag/diagfwd_smux.h
+++ b/drivers/char/diag/diagfwd_smux.h
@@ -20,6 +20,8 @@
 int diagfwd_read_complete_smux(void);
 int diagfwd_write_complete_smux(void);
 int diagfwd_connect_smux(void);
+void diag_usb_read_complete_smux_fn(struct work_struct *w);
+void diag_read_usb_smux_work_fn(struct work_struct *work);
 extern struct platform_driver msm_diagfwd_smux_driver;
 
 #endif
diff --git a/drivers/char/diag/diagmem.c b/drivers/char/diag/diagmem.c
index 1a522d5..ab1aa75 100644
--- a/drivers/char/diag/diagmem.c
+++ b/drivers/char/diag/diagmem.c
@@ -51,7 +51,7 @@
 				driver->diag_write_struct_pool, GFP_ATOMIC);
 			}
 		}
-#ifdef CONFIG_DIAG_BRIDGE_CODE
+#ifdef CONFIG_DIAGFWD_BRIDGE_CODE
 	} else if (pool_type == POOL_TYPE_HSIC) {
 		if (driver->diag_hsic_pool) {
 			if (driver->count_hsic_pool < driver->poolsize_hsic) {
@@ -105,7 +105,7 @@
 		} else if (driver->ref_count == 0 && pool_type == POOL_TYPE_ALL)
 			printk(KERN_ALERT "Unable to destroy STRUCT mempool");
 	}
-#ifdef CONFIG_DIAG_BRIDGE_CODE
+#ifdef CONFIG_DIAGFWD_BRIDGE_CODE
 	if (driver->diag_hsic_pool && (driver->hsic_inited == 0)) {
 		if (driver->count_hsic_pool == 0) {
 			mempool_destroy(driver->diag_hdlc_pool);
@@ -156,7 +156,7 @@
 			pr_err("diag: Attempt to free up DIAG driver "
 			   "USB structure mempool which is already free %d ",
 				    driver->count_write_struct_pool);
-#ifdef CONFIG_DIAG_BRIDGE_CODE
+#ifdef CONFIG_DIAGFWD_BRIDGE_CODE
 	} else if (pool_type == POOL_TYPE_HSIC) {
 		if (driver->diag_hsic_pool != NULL &&
 			driver->count_hsic_pool > 0) {
@@ -210,7 +210,7 @@
 		printk(KERN_INFO "Cannot allocate diag USB struct mempool\n");
 }
 
-#ifdef CONFIG_DIAG_BRIDGE_CODE
+#ifdef CONFIG_DIAGFWD_BRIDGE_CODE
 void diagmem_hsic_init(struct diagchar_dev *driver)
 {
 	if (driver->count_hsic_pool == 0)
diff --git a/drivers/char/diag/diagmem.h b/drivers/char/diag/diagmem.h
index 8665c75..36def72f 100644
--- a/drivers/char/diag/diagmem.h
+++ b/drivers/char/diag/diagmem.h
@@ -18,7 +18,7 @@
 void diagmem_free(struct diagchar_dev *driver, void *buf, int pool_type);
 void diagmem_init(struct diagchar_dev *driver);
 void diagmem_exit(struct diagchar_dev *driver, int pool_type);
-#ifdef CONFIG_DIAG_BRIDGE_CODE
+#ifdef CONFIG_DIAGFWD_BRIDGE_CODE
 void diagmem_hsic_init(struct diagchar_dev *driver);
 #endif
 #endif
diff --git a/drivers/char/hw_random/msm_rng.c b/drivers/char/hw_random/msm_rng.c
index 60ca44f..f2e5439 100644
--- a/drivers/char/hw_random/msm_rng.c
+++ b/drivers/char/hw_random/msm_rng.c
@@ -181,7 +181,12 @@
 	msm_rng_dev->base = base;
 
 	/* create a handle for clock control */
-	msm_rng_dev->prng_clk = clk_get(&pdev->dev, "core_clk");
+	if ((pdev->dev.of_node) && (of_property_read_bool(pdev->dev.of_node,
+					"qcom,msm-rng-iface-clk")))
+		msm_rng_dev->prng_clk = clk_get(&pdev->dev,
+							"iface_clk");
+	else
+		msm_rng_dev->prng_clk = clk_get(&pdev->dev, "core_clk");
 	if (IS_ERR(msm_rng_dev->prng_clk)) {
 		dev_err(&pdev->dev, "failed to register clock source\n");
 		error = -EPERM;
diff --git a/drivers/char/msm_rotator.c b/drivers/char/msm_rotator.c
index e1e3ff5..b3843fa 100644
--- a/drivers/char/msm_rotator.c
+++ b/drivers/char/msm_rotator.c
@@ -422,6 +422,7 @@
 	case MDP_Y_CRCB_H2V1:
 	case MDP_Y_CBCR_H2V1:
 	case MDP_Y_CRCB_H1V2:
+	case MDP_Y_CBCR_H1V2:
 		p->num_planes = 2;
 		p->plane_size[0] = w * h;
 		p->plane_size[1] = w * h;
@@ -470,8 +471,24 @@
 				  unsigned int out_chroma_paddr)
 {
 	int bpp;
-
-	if (info->src.format != info->dst.format)
+	uint32_t dst_format;
+	switch (info->src.format) {
+	case MDP_Y_CRCB_H2V1:
+		if (info->rotations & MDP_ROT_90)
+			dst_format = MDP_Y_CRCB_H1V2;
+		else
+			dst_format = info->src.format;
+		break;
+	case MDP_Y_CBCR_H2V1:
+		if (info->rotations & MDP_ROT_90)
+			dst_format = MDP_Y_CBCR_H1V2;
+		else
+			dst_format = info->src.format;
+		break;
+	default:
+		return -EINVAL;
+	}
+	if (info->dst.format != dst_format)
 		return -EINVAL;
 
 	bpp = get_bpp(info->src.format);
@@ -1281,10 +1298,18 @@
 		is_rgb = 1;
 		info.dst.format = info.src.format;
 		break;
+	case MDP_Y_CBCR_H2V1:
+	if (info.rotations & MDP_ROT_90) {
+		info.dst.format = MDP_Y_CBCR_H1V2;
+		break;
+	}
+	case MDP_Y_CRCB_H2V1:
+	if (info.rotations & MDP_ROT_90) {
+		info.dst.format = MDP_Y_CRCB_H1V2;
+		break;
+	}
 	case MDP_Y_CBCR_H2V2:
 	case MDP_Y_CRCB_H2V2:
-	case MDP_Y_CBCR_H2V1:
-	case MDP_Y_CRCB_H2V1:
 	case MDP_YCBCR_H1V1:
 	case MDP_YCRCB_H1V1:
 		info.dst.format = info.src.format;
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..9f96b19 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,24 @@
 	uint32_t			ctxid_mask;
 	uint32_t			sync_freq;
 	uint32_t			timestamp_event;
-	uint8_t				pdcr_pwrup;
 	bool				pcsave_impl;
 	bool				pcsave_enable;
+	bool				pcsave_sticky_enable;
+	bool				pcsave_boot_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 +353,19 @@
 	     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_clr_pwrup(drvdata);
 
 	ETM_LOCK(drvdata);
 }
@@ -391,14 +376,8 @@
 
 	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_LOCK(drvdata);
 }
@@ -416,12 +395,13 @@
 	 * 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_clr_pwrup(drvdata);
 	etm_set_prog(drvdata);
 
 	etmcr = etm_readl(drvdata, ETMCR);
@@ -463,7 +443,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 +459,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 +492,13 @@
 	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_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,
@@ -1525,28 +1518,55 @@
 	return scnprintf(buf, PAGE_SIZE, "%#lx\n", val);
 }
 
-static int __etm_store_pcsave(struct etm_drvdata *drvdata, unsigned long val)
+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);
+	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;
+		drvdata->pcsave_sticky_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);
 
 	clk_disable_unprepare(drvdata->clk);
-	return 0;
+	return ret;
+}
+
+static int __etm_store_pcsave(struct etm_drvdata *drvdata, unsigned long val)
+{
+	int ret;
+
+	get_online_cpus();
+	ret = ____etm_store_pcsave(drvdata, val);
+	put_online_cpus();
+
+	return ret;
 }
 
 static ssize_t etm_store_pcsave(struct device *dev,
@@ -1613,15 +1633,49 @@
 	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_ONLINE:
+		if (etmdrvdata[cpu] && etmdrvdata[cpu]->pcsave_boot_enable &&
+		    !etmdrvdata[cpu]->pcsave_sticky_enable) {
+			____etm_store_pcsave(etmdrvdata[cpu], 1);
+		}
+		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 +1687,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;
@@ -1660,6 +1705,7 @@
 	 * certain registers might be ignored.
 	 */
 	etm_clr_pwrdwn(drvdata);
+	etm_clr_pwrup(drvdata);
 	/* Set prog bit. It will be set from reset but this is included to
 	 * ensure it is set
 	 */
@@ -1677,19 +1723,17 @@
 	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 +1818,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 +1831,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 +1874,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 +1883,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 +1895,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)
@@ -1860,15 +1913,23 @@
 	if (boot_enable)
 		coresight_enable(drvdata->csdev);
 
-	if (drvdata->pcsave_impl && boot_pcsave_enable)
-		__etm_store_pcsave(drvdata, true);
+	if (drvdata->pcsave_impl && boot_pcsave_enable) {
+		__etm_store_pcsave(drvdata, 1);
+		drvdata->pcsave_boot_enable = 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 +1939,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 6ddbf4e..8f086aa 100644
--- a/drivers/cpufreq/cpufreq_gov_msm.c
+++ b/drivers/cpufreq/cpufreq_gov_msm.c
@@ -253,6 +253,7 @@
 						msm_dcvs_freq_set,
 						msm_dcvs_freq_get,
 						msm_dcvs_idle_notifier,
+						NULL,
 						sensor);
 		if (gov->dcvs_core_id < 0) {
 			pr_err("Unable to register core for %d\n", cpu);
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 78161b6..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();
 
@@ -869,11 +878,7 @@
 static void dbs_input_event(struct input_handle *handle, unsigned int type,
 		unsigned int code, int value)
 {
-	int i, j;
-	struct cpumask cpus_scheduled;
-	struct cpu_dbs_info_s *dbs_info;
-	cpumask_clear(&cpus_scheduled);
-
+	int i;
 
 	if ((dbs_tuners_ins.powersave_bias == POWERSAVE_BIAS_MAXLEVEL) ||
 		(dbs_tuners_ins.powersave_bias == POWERSAVE_BIAS_MINLEVEL)) {
@@ -881,25 +886,8 @@
 		return;
 	}
 
-	for_each_online_cpu(i) {
-		dbs_info = &per_cpu(od_cpu_dbs_info, i);
-
-		if (!dbs_info->cur_policy) {
-			pr_err("Dbs policy is NULL\n");
-			continue;
-		}
-
-		for_each_cpu(j, &cpus_scheduled) {
-			if (cpumask_test_cpu(j, dbs_info->cur_policy->cpus))
-				goto skip_schedule;
-		}
-		cpumask_set_cpu(i, &cpus_scheduled);
-		queue_work_on(i, input_wq, &per_cpu(dbs_refresh_work, i));
-
-		/* This CPU is already running at new frequency */
-skip_schedule:
-		;
-	}
+	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,
@@ -1092,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/qce40.c b/drivers/crypto/msm/qce40.c
index 7a229a5..de060cc 100644
--- a/drivers/crypto/msm/qce40.c
+++ b/drivers/crypto/msm/qce40.c
@@ -1844,6 +1844,8 @@
 	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->ce_data_in);
 	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->get_status_wait);
 	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->get_status_wait);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->get_status_wait);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->get_status_wait);
 	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->get_auth_byte_count);
 	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->get_auth_result_20);
 	*cmd_ptr_vaddr++ = QCE_SET_LAST_CMD_PTR(cmdlist->get_status_ocu);
@@ -1860,6 +1862,8 @@
 	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->ce_data_in);
 	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->get_status_wait);
 	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->get_status_wait);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->get_status_wait);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->get_status_wait);
 	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->get_auth_byte_count);
 	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->get_auth_result_32);
 	*cmd_ptr_vaddr++ = QCE_SET_LAST_CMD_PTR(cmdlist->get_status_ocu);
@@ -1877,6 +1881,8 @@
 	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->ce_data_in);
 	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->get_status_wait);
 	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->get_status_wait);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->get_status_wait);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->get_status_wait);
 	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->get_auth_byte_count);
 	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->get_auth_result_20);
 	*cmd_ptr_vaddr++ = QCE_SET_LAST_CMD_PTR(cmdlist->get_status_ocu);
@@ -1894,6 +1900,8 @@
 	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->ce_data_in);
 	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->get_status_wait);
 	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->get_status_wait);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->get_status_wait);
+	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->get_status_wait);
 	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->get_auth_byte_count);
 	*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->get_auth_result_32);
 	*cmd_ptr_vaddr++ = QCE_SET_LAST_CMD_PTR(cmdlist->get_status_ocu);
diff --git a/drivers/crypto/msm/qce50.c b/drivers/crypto/msm/qce50.c
index 8a9567b..d605a61 100644
--- a/drivers/crypto/msm/qce50.c
+++ b/drivers/crypto/msm/qce50.c
@@ -46,7 +46,7 @@
 	uint32_t cnt;
 };
 static struct bam_registration_info bam_registry;
-
+static bool ce_bam_registered;
 /*
  * CE HW device structure.
  * Each engine has an instance of the structure.
@@ -250,7 +250,7 @@
 
 		pce = cmdlistinfo->go_proc;
 		if (i == authk_size_in_word) {
-			pce->addr = (uint32_t)(CRYPTO_GOPROC_OEM_KEY_REG +
+			pce->addr = (uint32_t)(CRYPTO_GOPROC_QC_KEY_REG +
 							pce_dev->phy_iobase);
 		} else {
 			pce->addr = (uint32_t)(CRYPTO_GOPROC_REG +
@@ -382,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;
@@ -434,7 +434,7 @@
 	pce = cmdlistinfo->go_proc;
 	if (i == enck_size_in_word) {
 		use_hw_key = true;
-		pce->addr = (uint32_t)(CRYPTO_GOPROC_OEM_KEY_REG +
+		pce->addr = (uint32_t)(CRYPTO_GOPROC_QC_KEY_REG +
 						pce_dev->phy_iobase);
 	} else {
 		pce->addr = (uint32_t)(CRYPTO_GOPROC_REG +
@@ -487,7 +487,10 @@
 		pce->data = auth_cfg;
 
 		pce = cmdlistinfo->auth_seg_size;
-		pce->data = totallen_in;
+		if (creq->dir == QCE_ENCRYPT)
+			pce->data = totallen_in;
+		else
+			pce->data = totallen_in - creq->authsize;
 		pce = cmdlistinfo->auth_seg_start;
 		pce->data = 0;
 	}
@@ -503,7 +506,8 @@
 		encr_cfg |= (CRYPTO_ENCR_MODE_XTS << CRYPTO_ENCR_MODE);
 		break;
 	case QCE_MODE_CCM:
-		encr_cfg |= (CRYPTO_ENCR_MODE_CCM << CRYPTO_ENCR_MODE);
+		encr_cfg |= (CRYPTO_ENCR_MODE_CCM << CRYPTO_ENCR_MODE) |
+				(CRYPTO_LAST_CCM_XFR << CRYPTO_LAST_CCM);
 		break;
 	case QCE_MODE_CTR:
 	default:
@@ -767,15 +771,30 @@
 			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,
@@ -1138,15 +1157,21 @@
 	pr_debug("bam virtual base=0x%x\n", (u32)bam.virt_addr);
 
 	mutex_lock(&bam_register_cnt);
+	if (ce_bam_registered == false) {
+		bam_registry.handle = 0;
+		bam_registry.cnt = 0;
+	}
 	if ((bam_registry.handle == 0) && (bam_registry.cnt == 0)) {
 		/* Register CE Peripheral BAM device to SPS driver */
 		rc = sps_register_bam_device(&bam, &bam_registry.handle);
 		if (rc) {
+			mutex_unlock(&bam_register_cnt);
 			pr_err("sps_register_bam_device() failed! err=%d", rc);
 			return -EIO;
 		}
 		bam_registry.cnt++;
 		register_bam = true;
+		ce_bam_registered = true;
 	} else {
 		   bam_registry.cnt++;
 	}
@@ -1172,9 +1197,14 @@
 sps_connect_consumer_err:
 	qce_sps_exit_ep_conn(pce_dev, &pce_dev->ce_sps.producer);
 sps_connect_producer_err:
-	if (register_bam)
+	if (register_bam) {
+		mutex_lock(&bam_register_cnt);
 		sps_deregister_bam_device(pce_dev->ce_sps.bam_handle);
-
+		ce_bam_registered = false;
+		bam_registry.handle = 0;
+		bam_registry.cnt = 0;
+		mutex_unlock(&bam_register_cnt);
+	}
 	return rc;
 }
 
@@ -1456,10 +1486,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,
@@ -1624,13 +1655,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),
@@ -1921,7 +1945,9 @@
 		auth_cfg &= ~(1 << CRYPTO_USE_HW_KEY_AUTH);
 		encr_cfg = (CRYPTO_ENCR_KEY_SZ_AES256 << CRYPTO_ENCR_KEY_SZ) |
 			(CRYPTO_ENCR_ALG_AES << CRYPTO_ENCR_ALG) |
-			((CRYPTO_ENCR_MODE_CCM << CRYPTO_ENCR_MODE));
+			(CRYPTO_ENCR_MODE_CCM << CRYPTO_ENCR_MODE) |
+			(CRYPTO_LAST_CCM_XFR << CRYPTO_LAST_CCM);
+
 		key_reg = 8;
 	}
 	qce_add_cmd_element(pdev, &ce_vaddr, CRYPTO_CONFIG_REG,
@@ -2152,8 +2178,10 @@
 	struct qce_cmdlist_info *cmdlistinfo = NULL;
 	struct qce_cmdlist_info *auth_cmdlistinfo = NULL;
 
-	if (q_req->mode != QCE_MODE_CCM)
+	if (q_req->mode != QCE_MODE_CCM) {
 		ivsize = crypto_aead_ivsize(aead);
+		auth_cmdlistinfo = &pce_dev->ce_sps.cmdlistptr.aead_sha1_hmac;
+	}
 
 	ce_burst_size = pce_dev->ce_sps.ce_burst_size;
 	if (q_req->dir == QCE_ENCRYPT) {
@@ -2761,32 +2789,14 @@
 	ce_support->aes_xts = true;
 	ce_support->ota = false;
 	ce_support->bam = true;
-	if (pce_dev->ce_sps.minor_version) {
+	ce_support->aes_ccm = true;
+	if (pce_dev->ce_sps.minor_version)
 		ce_support->aligned_only = false;
-		ce_support->aes_ccm = true;
-	} else {
+	else
 		ce_support->aligned_only = true;
-		ce_support->aes_ccm = false;
-	}
 	return 0;
 }
 EXPORT_SYMBOL(qce_hw_support);
 
-static int __init qce_init(void)
-{
-	bam_registry.handle = 0;
-	bam_registry.cnt = 0;
-	return 0;
-}
-
-static void __exit qce_exit(void)
-{
-	bam_registry.handle = 0;
-	bam_registry.cnt = 0;
-}
-
-module_init(qce_init);
-module_exit(qce_exit);
-
 MODULE_LICENSE("GPL v2");
 MODULE_DESCRIPTION("Crypto Engine driver");
diff --git a/drivers/crypto/msm/qcrypto.c b/drivers/crypto/msm/qcrypto.c
index 7fc5cab..10f83f3 100644
--- a/drivers/crypto/msm/qcrypto.c
+++ b/drivers/crypto/msm/qcrypto.c
@@ -919,7 +919,7 @@
 
 			for (sg = areq->dst; bytes != nbytes; sg++) {
 				memcpy(sg_virt(sg),
-				((char *)rctx->data + rctx->assoclen + bytes),
+				((char *)rctx->data + areq->assoclen + bytes),
 								sg->length);
 				bytes += sg->length;
 			}
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_cma_heap.c b/drivers/gpu/ion/ion_cma_heap.c
new file mode 100644
index 0000000..6c8dd6d
--- /dev/null
+++ b/drivers/gpu/ion/ion_cma_heap.c
@@ -0,0 +1,179 @@
+/*
+ * drivers/gpu/ion/ion_cma_heap.c
+ *
+ * Copyright (C) Linaro 2012
+ * Author: <benjamin.gaignard@linaro.org> for ST-Ericsson.
+ *
+ * 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/ion.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/err.h>
+#include <linux/dma-mapping.h>
+
+/* for ion_heap_ops structure */
+#include "ion_priv.h"
+
+#define ION_CMA_ALLOCATE_FAILED -1
+
+struct ion_cma_buffer_info {
+	void *cpu_addr;
+	dma_addr_t handle;
+	struct sg_table *table;
+};
+
+/*
+ * Create scatter-list for the already allocated DMA buffer.
+ * This function could be replace by dma_common_get_sgtable
+ * as soon as it will avalaible.
+ */
+int ion_cma_get_sgtable(struct device *dev, struct sg_table *sgt,
+			void *cpu_addr, dma_addr_t handle, size_t size)
+{
+	struct page *page = virt_to_page(cpu_addr);
+	int ret;
+
+	ret = sg_alloc_table(sgt, 1, GFP_KERNEL);
+	if (unlikely(ret))
+		return ret;
+
+	sg_set_page(sgt->sgl, page, PAGE_ALIGN(size), 0);
+	return 0;
+}
+
+/* ION CMA heap operations functions */
+static int ion_cma_allocate(struct ion_heap *heap, struct ion_buffer *buffer,
+			    unsigned long len, unsigned long align,
+			    unsigned long flags)
+{
+	struct device *dev = heap->priv;
+	struct ion_cma_buffer_info *info;
+
+	dev_dbg(dev, "Request buffer allocation len %ld\n", len);
+
+	info = kzalloc(sizeof(struct ion_cma_buffer_info), GFP_KERNEL);
+	if (!info) {
+		dev_err(dev, "Can't allocate buffer info\n");
+		return ION_CMA_ALLOCATE_FAILED;
+	}
+
+	info->cpu_addr = dma_alloc_coherent(dev, len, &(info->handle), 0);
+
+	if (!info->cpu_addr) {
+		dev_err(dev, "Fail to allocate buffer\n");
+		goto err;
+	}
+
+	info->table = kmalloc(sizeof(struct sg_table), GFP_KERNEL);
+	if (!info->table) {
+		dev_err(dev, "Fail to allocate sg table\n");
+		goto err;
+	}
+
+	ion_cma_get_sgtable(dev,
+			info->table, info->cpu_addr, info->handle, len);
+
+	/* keep this for memory release */
+	buffer->priv_virt = info;
+	dev_dbg(dev, "Allocate buffer %p\n", buffer);
+	return 0;
+
+err:
+	kfree(info);
+	return ION_CMA_ALLOCATE_FAILED;
+}
+
+static void ion_cma_free(struct ion_buffer *buffer)
+{
+	struct device *dev = buffer->heap->priv;
+	struct ion_cma_buffer_info *info = buffer->priv_virt;
+
+	dev_dbg(dev, "Release buffer %p\n", buffer);
+	/* release memory */
+	dma_free_coherent(dev, buffer->size, info->cpu_addr, info->handle);
+	/* release sg table */
+	kfree(info->table);
+	kfree(info);
+}
+
+/* return physical address in addr */
+static int ion_cma_phys(struct ion_heap *heap, struct ion_buffer *buffer,
+			ion_phys_addr_t *addr, size_t *len)
+{
+	struct device *dev = heap->priv;
+	struct ion_cma_buffer_info *info = buffer->priv_virt;
+
+	dev_dbg(dev, "Return buffer %p physical address 0x%x\n", buffer,
+		virt_to_phys(info->cpu_addr));
+
+	*addr = virt_to_phys(info->cpu_addr);
+	*len = buffer->size;
+
+	return 0;
+}
+
+struct sg_table *ion_cma_heap_map_dma(struct ion_heap *heap,
+					 struct ion_buffer *buffer)
+{
+	struct ion_cma_buffer_info *info = buffer->priv_virt;
+
+	return info->table;
+}
+
+void ion_cma_heap_unmap_dma(struct ion_heap *heap,
+			       struct ion_buffer *buffer)
+{
+	return;
+}
+
+static int ion_cma_mmap(struct ion_heap *mapper, struct ion_buffer *buffer,
+			struct vm_area_struct *vma)
+{
+	struct device *dev = buffer->heap->priv;
+	struct ion_cma_buffer_info *info = buffer->priv_virt;
+
+	return dma_mmap_coherent(dev, vma, info->cpu_addr, info->handle,
+				 buffer->size);
+}
+
+static struct ion_heap_ops ion_cma_ops = {
+	.allocate = ion_cma_allocate,
+	.free = ion_cma_free,
+	.map_dma = ion_cma_heap_map_dma,
+	.unmap_dma = ion_cma_heap_unmap_dma,
+	.phys = ion_cma_phys,
+	.map_user = ion_cma_mmap,
+};
+
+struct ion_heap *ion_cma_heap_create(struct ion_platform_heap *data)
+{
+	struct ion_heap *heap;
+
+	heap = kzalloc(sizeof(struct ion_heap), GFP_KERNEL);
+
+	if (!heap)
+		return ERR_PTR(-ENOMEM);
+
+	heap->ops = &ion_cma_ops;
+	/* set device as private heaps data, later it will be
+	 * used to make the link with reserved CMA memory */
+	heap->priv = data->priv;
+	heap->type = ION_HEAP_TYPE_DMA;
+	return heap;
+}
+
+void ion_cma_heap_destroy(struct ion_heap *heap)
+{
+	kfree(heap);
+}
diff --git a/drivers/gpu/ion/ion_cp_heap.c b/drivers/gpu/ion/ion_cp_heap.c
index f9a9212..aa3469c 100644
--- a/drivers/gpu/ion/ion_cp_heap.c
+++ b/drivers/gpu/ion/ion_cp_heap.c
@@ -399,14 +399,16 @@
 		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);
 	}
 
 	/*
@@ -569,18 +571,11 @@
 	if (!table)
 		return ERR_PTR(-ENOMEM);
 
-	if (buf->is_secure) {
+	if (buf->is_secure && IS_ALIGNED(buffer->size, SZ_1M)) {
 		int n_chunks;
 		int i;
 		struct scatterlist *sg;
 
-		if (!IS_ALIGNED(buffer->size, SZ_1M)) {
-			pr_err("%s: buffer is marked as secure but buffer size %x is not aligned to 1MB\n",
-				__func__, buffer->size);
-
-			return ERR_PTR(-EINVAL);
-		}
-
 		/* Count number of 1MB chunks. Alignment is already checked. */
 		n_chunks = buffer->size >> 20;
 
diff --git a/drivers/gpu/ion/ion_heap.c b/drivers/gpu/ion/ion_heap.c
index 4c83d75..0670468 100644
--- a/drivers/gpu/ion/ion_heap.c
+++ b/drivers/gpu/ion/ion_heap.c
@@ -40,6 +40,11 @@
 	case ION_HEAP_TYPE_CP:
 		heap = ion_cp_heap_create(heap_data);
 		break;
+#ifdef CONFIG_CMA
+	case ION_HEAP_TYPE_DMA:
+		heap = ion_cma_heap_create(heap_data);
+		break;
+#endif
 	default:
 		pr_err("%s: Invalid heap type %d\n", __func__,
 		       heap_data->type);
@@ -55,6 +60,7 @@
 
 	heap->name = heap_data->name;
 	heap->id = heap_data->id;
+	heap->priv = heap_data->priv;
 	return heap;
 }
 
@@ -79,6 +85,11 @@
 	case ION_HEAP_TYPE_CP:
 		ion_cp_heap_destroy(heap);
 		break;
+#ifdef CONFIG_CMA
+	case ION_HEAP_TYPE_DMA:
+		ion_cma_heap_destroy(heap);
+		break;
+#endif
 	default:
 		pr_err("%s: Invalid heap type %d\n", __func__,
 		       heap->type);
diff --git a/drivers/gpu/ion/ion_priv.h b/drivers/gpu/ion/ion_priv.h
index 991a310..7713875 100644
--- a/drivers/gpu/ion/ion_priv.h
+++ b/drivers/gpu/ion/ion_priv.h
@@ -162,6 +162,7 @@
  *			allocating.  These are specified by platform data and
  *			MUST be unique
  * @name:		used for debugging
+ * @priv:		private heap data
  *
  * Represents a pool of memory from which buffers can be made.  In some
  * systems the only heap is regular system memory allocated via vmalloc.
@@ -175,6 +176,7 @@
 	struct ion_heap_ops *ops;
 	int id;
 	const char *name;
+	void *priv;
 };
 
 /**
@@ -257,6 +259,10 @@
 void ion_carveout_free(struct ion_heap *heap, ion_phys_addr_t addr,
 		       unsigned long size);
 
+#ifdef CONFIG_CMA
+struct ion_heap *ion_cma_heap_create(struct ion_platform_heap *);
+void ion_cma_heap_destroy(struct ion_heap *);
+#endif
 
 struct ion_heap *msm_get_contiguous_heap(void);
 /**
diff --git a/drivers/gpu/ion/msm/msm_ion.c b/drivers/gpu/ion/msm/msm_ion.c
index 7fe47ee..e422fd26 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,
@@ -413,6 +424,7 @@
 	unsigned int i;
 	for (i = 0; i < pdata->nr; ++i)
 		kfree(pdata->heaps[i].extra_data);
+	kfree(pdata->heaps);
 	kfree(pdata);
 }
 
@@ -458,6 +470,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);
@@ -481,12 +494,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)
@@ -524,6 +554,7 @@
 					const struct device_node *dt_node)
 {
 	struct ion_platform_data *pdata = 0;
+	struct ion_platform_heap *heaps = NULL;
 	struct device_node *node;
 	uint32_t val = 0;
 	int ret = 0;
@@ -536,11 +567,17 @@
 	if (!num_heaps)
 		return ERR_PTR(-EINVAL);
 
-	pdata = kzalloc(sizeof(struct ion_platform_data) +
-			num_heaps*sizeof(struct ion_platform_heap), GFP_KERNEL);
+	pdata = kzalloc(sizeof(struct ion_platform_data), GFP_KERNEL);
 	if (!pdata)
 		return ERR_PTR(-ENOMEM);
 
+	heaps = kzalloc(sizeof(struct ion_platform_heap)*num_heaps, GFP_KERNEL);
+	if (!heaps) {
+		kfree(pdata);
+		return ERR_PTR(-ENOMEM);
+	}
+
+	pdata->heaps = heaps;
 	pdata->nr = num_heaps;
 
 	for_each_child_of_node(dt_node, node) {
@@ -560,6 +597,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]);
@@ -586,18 +624,15 @@
 	if (end < start)
 		goto out;
 
-	down_read(&mm->mmap_sem);
 	vma = find_vma(mm, start);
 	if (vma && vma->vm_start < end) {
 		if (start < vma->vm_start)
-			goto out_up;
+			goto out;
 		if (end > vma->vm_end)
-			goto out_up;
+			goto out;
 		ret = 0;
 	}
 
-out_up:
-	up_read(&mm->mmap_sem);
 out:
 	return ret;
 }
@@ -615,20 +650,12 @@
 		unsigned long start, end;
 		struct ion_handle *handle = NULL;
 		int ret;
+		struct mm_struct *mm = current->active_mm;
 
 		if (copy_from_user(&data, (void __user *)arg,
 					sizeof(struct ion_flush_data)))
 			return -EFAULT;
 
-		start = (unsigned long) data.vaddr;
-		end = (unsigned long) data.vaddr + data.length;
-
-		if (check_vaddr_bounds(start, end)) {
-			pr_err("%s: virtual address %p is out of bounds\n",
-				__func__, data.vaddr);
-			return -EINVAL;
-		}
-
 		if (!data.handle) {
 			handle = ion_import_dma_buf(client, data.fd);
 			if (IS_ERR(handle)) {
@@ -638,11 +665,27 @@
 			}
 		}
 
+		down_read(&mm->mmap_sem);
+
+		start = (unsigned long) data.vaddr;
+		end = (unsigned long) data.vaddr + data.length;
+
+		if (check_vaddr_bounds(start, end)) {
+			up_read(&mm->mmap_sem);
+			pr_err("%s: virtual address %p is out of bounds\n",
+				__func__, data.vaddr);
+			if (!data.handle)
+				ion_free(client, handle);
+			return -EINVAL;
+		}
+
 		ret = ion_do_cache_op(client,
 				data.handle ? data.handle : handle,
 				data.vaddr, data.offset, data.length,
 				cmd);
 
+		up_read(&mm->mmap_sem);
+
 		if (!data.handle)
 			ion_free(client, handle);
 
@@ -734,6 +777,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/adreno.c b/drivers/gpu/msm/adreno.c
index 930d233..060e89a 100644
--- a/drivers/gpu/msm/adreno.c
+++ b/drivers/gpu/msm/adreno.c
@@ -98,6 +98,7 @@
 			.irq_name = KGSL_3D0_IRQ,
 		},
 		.iomemname = KGSL_3D0_REG_MEMORY,
+		.shadermemname = KGSL_3D0_SHADER_MEMORY,
 		.ftbl = &adreno_functable,
 #ifdef CONFIG_HAS_EARLYSUSPEND
 		.display_off = {
@@ -253,6 +254,13 @@
 	if (result)
 		goto unmap_memstore_desc;
 
+	/*
+	 * Set the mpu end to the last "normal" global memory we use.
+	 * For the IOMMU, this will be used to restrict access to the
+	 * mapped registers.
+	 */
+	device->mh.mpu_range = device->mmu.setstate_memory.gpuaddr +
+				device->mmu.setstate_memory.size;
 	return result;
 
 unmap_memstore_desc:
@@ -385,13 +393,6 @@
 		*cmds++ = cp_type3_packet(CP_INVALIDATE_STATE, 1);
 		*cmds++ = 0x7fff;
 		sizedwords += 2;
-		/*
-		 * add an interrupt at the end of commands so that the smmu
-		 * disable clock off function will get called
-		 */
-		*cmds++ = cp_type3_packet(CP_INTERRUPT, 1);
-		*cmds++ = CP_INT_CNTL__RB_INT_MASK;
-		sizedwords += 2;
 		/* This returns the per context timestamp but we need to
 		 * use the global timestamp for iommu clock disablement */
 		adreno_ringbuffer_issuecmds(device, adreno_ctx,
@@ -731,101 +732,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)
 {
@@ -1121,7 +1027,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;
@@ -1142,7 +1048,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);
@@ -1496,6 +1401,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
@@ -1860,12 +1770,6 @@
 	return status;
 }
 
-static inline void adreno_poke(struct kgsl_device *device)
-{
-	struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
-	adreno_regwrite(device, REG_CP_RB_WPTR, adreno_dev->ringbuffer.wptr);
-}
-
 static int adreno_ringbuffer_drain(struct kgsl_device *device,
 	unsigned int *regs)
 {
@@ -1886,12 +1790,8 @@
 
 	wait = jiffies + msecs_to_jiffies(100);
 
-	adreno_poke(device);
-
 	do {
 		if (time_after(jiffies, wait)) {
-			adreno_poke(device);
-
 			/* Check to see if the core is hung */
 			if (adreno_hang_detect(device, regs))
 				return -ETIMEDOUT;
@@ -1931,7 +1831,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)) {
@@ -1960,18 +1860,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. */
@@ -1980,17 +1908,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;
@@ -2087,12 +2005,23 @@
 	return memdesc ? kgsl_gpuaddr_to_vaddr(memdesc, gpuaddr) : NULL;
 }
 
-void adreno_regread(struct kgsl_device *device, unsigned int offsetwords,
-				unsigned int *value)
+/**
+ * adreno_read - General read function to read adreno device memory
+ * @device - Pointer to the GPU device struct (for adreno device)
+ * @base - Base address (kernel virtual) where the device memory is mapped
+ * @offsetwords - Offset in words from the base address, of the memory that
+ * is to be read
+ * @value - Value read from the device memory
+ * @mem_len - Length of the device memory mapped to the kernel
+ */
+static void adreno_read(struct kgsl_device *device, void *base,
+		unsigned int offsetwords, unsigned int *value,
+		unsigned int mem_len)
 {
+
 	unsigned int *reg;
-	BUG_ON(offsetwords*sizeof(uint32_t) >= device->reg_len);
-	reg = (unsigned int *)(device->reg_virt + (offsetwords << 2));
+	BUG_ON(offsetwords*sizeof(uint32_t) >= mem_len);
+	reg = (unsigned int *)(base + (offsetwords << 2));
 
 	if (!in_interrupt())
 		kgsl_pre_hwaccess(device);
@@ -2103,6 +2032,31 @@
 	rmb();
 }
 
+/**
+ * adreno_regread - Used to read adreno device registers
+ * @offsetwords - Word (4 Bytes) offset to the register to be read
+ * @value - Value read from device register
+ */
+void adreno_regread(struct kgsl_device *device, unsigned int offsetwords,
+	unsigned int *value)
+{
+	adreno_read(device, device->reg_virt, offsetwords, value,
+						device->reg_len);
+}
+
+/**
+ * adreno_shadermem_regread - Used to read GPU (adreno) shader memory
+ * @device - GPU device whose shader memory is to be read
+ * @offsetwords - Offset in words, of the shader memory address to be read
+ * @value - Pointer to where the read shader mem value is to be stored
+ */
+void adreno_shadermem_regread(struct kgsl_device *device,
+	unsigned int offsetwords, unsigned int *value)
+{
+	adreno_read(device, device->shader_mem_virt, offsetwords, value,
+					device->shader_mem_len);
+}
+
 void adreno_regwrite(struct kgsl_device *device, unsigned int offsetwords,
 				unsigned int value)
 {
@@ -2136,6 +2090,67 @@
 	return context_id;
 }
 
+static void adreno_next_event(struct kgsl_device *device,
+		struct kgsl_event *event)
+{
+	int status;
+	unsigned int ref_ts, enableflag;
+	unsigned int context_id = _get_context_id(event->context);
+	struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
+
+	status = kgsl_check_timestamp(device, event->context, event->timestamp);
+	if (!status) {
+		kgsl_sharedmem_readl(&device->memstore, &enableflag,
+			KGSL_MEMSTORE_OFFSET(context_id, ts_cmp_enable));
+		/*
+		 * Barrier is needed here to make sure the read from memstore
+		 * has posted
+		 */
+
+		mb();
+
+		if (enableflag) {
+			kgsl_sharedmem_readl(&device->memstore, &ref_ts,
+				KGSL_MEMSTORE_OFFSET(context_id,
+					ref_wait_ts));
+
+			/* Make sure the memstore read has posted */
+			mb();
+			if (timestamp_cmp(ref_ts, event->timestamp) >= 0) {
+				kgsl_sharedmem_writel(&device->memstore,
+				KGSL_MEMSTORE_OFFSET(context_id,
+					ref_wait_ts), event->timestamp);
+				/* Make sure the memstore write is posted */
+				wmb();
+			}
+		} else {
+			unsigned int cmds[2];
+			kgsl_sharedmem_writel(&device->memstore,
+				KGSL_MEMSTORE_OFFSET(context_id,
+					ref_wait_ts), event->timestamp);
+			enableflag = 1;
+			kgsl_sharedmem_writel(&device->memstore,
+				KGSL_MEMSTORE_OFFSET(context_id,
+					ts_cmp_enable), enableflag);
+
+			/* Make sure the memstore write gets posted */
+			wmb();
+
+			/*
+			 * submit a dummy packet so that even if all
+			 * commands upto timestamp get executed we will still
+			 * get an interrupt
+			 */
+			cmds[0] = cp_type3_packet(CP_NOP, 1);
+			cmds[1] = 0;
+
+			if (adreno_dev->drawctxt_active)
+				adreno_ringbuffer_issuecmds_intr(device,
+						event->context, &cmds[0], 2);
+		}
+	}
+}
+
 static int kgsl_check_interrupt_timestamp(struct kgsl_device *device,
 		struct kgsl_context *context, unsigned int timestamp)
 {
@@ -2233,8 +2248,24 @@
 	if (!adreno_dev->fast_hang_detect)
 		return 0;
 
-	if (device->ftbl->isidle(device))
+	if (is_adreno_rbbm_status_idle(device)) {
+
+		/*
+		 * On A2XX if the RPTR != WPTR and the device is idle, then
+		 * the last write to WPTR probably failed to latch so write it
+		 * again
+		 */
+
+		if (adreno_is_a2xx(adreno_dev)) {
+			unsigned int rptr;
+			adreno_regread(device, REG_CP_RB_RPTR, &rptr);
+			if (rptr != adreno_dev->ringbuffer.wptr)
+				adreno_regwrite(device, REG_CP_RB_WPTR,
+					adreno_dev->ringbuffer.wptr);
+		}
+
 		return 0;
+	}
 
 	for (i = 0; i < hang_detect_regs_count; i++) {
 
@@ -2252,162 +2283,225 @@
 	return hang_detected;
 }
 
-
-/* MUST be called with the device mutex held */
-static int adreno_waittimestamp(struct kgsl_device *device,
-				struct kgsl_context *context,
-				unsigned int timestamp,
-				unsigned int msecs)
+/**
+ * adreno_handle_hang - Process a hang detected in adreno_waittimestamp
+ * @device - pointer to a KGSL device structure
+ * @context - pointer to the active KGSL context
+ * @timestamp - the timestamp that the process was waiting for
+ *
+ * Process a possible GPU hang and try to recover from it cleanly
+ */
+static int adreno_handle_hang(struct kgsl_device *device,
+	struct kgsl_context *context, unsigned int timestamp)
 {
-	long status = 0;
-	uint io = 1;
-	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;
+	unsigned int ts_issued;
 
-	memset(prev_reg_val, 0, sizeof(prev_reg_val));
+	/* Do one last check to see if we somehow made it through */
+	if (kgsl_check_timestamp(device, context, timestamp))
+		return 0;
 
 	ts_issued = adreno_dev->ringbuffer.timestamp[context_id];
 
-	/* Don't wait forever, set a max value for now */
-	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) {
-		if (adreno_ctx == NULL ||
-			!(adreno_ctx->flags & CTXT_FLAGS_USER_GENERATED_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);
-			status = -EINVAL;
-			goto done;
-		} else
-			retry_ts_cmp = 1;
-	}
-
-	/*
-	 * Make the first timeout interval 100 msecs and then try to kick the
-	 * wptr again.  This helps to ensure the wptr is updated properly.  If
-	 * the requested timeout is less than 100 msecs, then wait 20msecs which
-	 * is the minimum amount of time we can safely wait at 100HZ
-	 */
-
-	if (msecs == 0 || msecs >= 100)
-		wait = 100;
-	else
-		wait = 20;
-
-	do {
-		/*
-		 * If the context ID is invalid, we are in a race with
-		 * the context being destroyed by userspace so bail.
-		 */
-		if (context_id == KGSL_CONTEXT_INVALID) {
-			KGSL_DRV_WARN(device, "context was detached");
-			status = -EINVAL;
-			goto done;
-		}
-		if (kgsl_check_timestamp(device, context, timestamp)) {
-			/* if the timestamp happens while we're not
-			 * waiting, there's a chance that an interrupt
-			 * will not be generated and thus the timestamp
-			 * work needs to be queued.
-			 */
-			queue_work(device->work_queue, &device->ts_expired_ws);
-			status = 0;
-			goto done;
-		}
-		adreno_poke(device);
-		io_cnt = (io_cnt + 1) % 100;
-		if (io_cnt <
-		    pwr->pwrlevels[pwr->active_pwrlevel].io_fraction)
-			io = 0;
-
-		if ((retries > 0) &&
-			(adreno_hang_detect(device, prev_reg_val)))
-			goto hang_dump;
-
-		mutex_unlock(&device->mutex);
-		/* We need to make sure that the process is
-		 * placed in wait-q before its condition is called
-		 */
-		status = kgsl_wait_event_interruptible_timeout(
-				device->wait_queue,
-				kgsl_check_interrupt_timestamp(device,
-					context, timestamp),
-				msecs_to_jiffies(wait), io);
-
-		mutex_lock(&device->mutex);
-
-		if (status > 0) {
-			/*completed before the wait finished */
-			status = 0;
-			goto done;
-		} else if (status < 0) {
-			/*an error occurred*/
-			goto done;
-		}
-		/*this wait timed out*/
-
-		time_elapsed += wait;
-		wait = KGSL_TIMEOUT_PART;
-
-		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) {
-				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);
-				status = -EINVAL;
-				goto done;
-			}
-			retry_ts_cmp = 0;
-		}
-
-	} while (!msecs || time_elapsed < msecs);
-
-hang_dump:
-	/*
-	 * Check if timestamp has retired here because we may have hit
-	 * recovery which can take some time and cause waiting threads
-	 * to timeout
-	 */
-	if (kgsl_check_timestamp(device, context, timestamp))
-		goto done;
-	status = -ETIMEDOUT;
 	KGSL_DRV_ERR(device,
 		     "Device hang detected while waiting for timestamp: "
 		     "<%d:0x%x>, last submitted timestamp: <%d:0x%x>, "
 		     "wptr: 0x%x\n",
 		      context_id, timestamp, context_id, ts_issued,
 		      adreno_dev->ringbuffer.wptr);
-	if (!adreno_dump_and_recover(device)) {
-		/* The timestamp that this process wanted
-		 * to wait on may be invalid or expired now
-		 * after successful recovery */
-			status = 0;
+
+	/* Return 0 after a successful recovery */
+	if (!adreno_dump_and_recover(device))
+		return 0;
+
+	return -ETIMEDOUT;
+}
+
+static int _check_pending_timestamp(struct kgsl_device *device,
+		struct kgsl_context *context, unsigned int timestamp)
+{
+	struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
+	unsigned int context_id = _get_context_id(context);
+	unsigned int ts_issued;
+
+	if (context_id == KGSL_CONTEXT_INVALID)
+		return -EINVAL;
+
+	ts_issued = adreno_dev->ringbuffer.timestamp[context_id];
+
+	if (timestamp_cmp(timestamp, ts_issued) <= 0)
+		return 0;
+
+	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);
+
+			/* Only print this message once */
+			context->wait_on_invalid_ts = true;
 	}
-done:
-	return (int)status;
+
+	return -EINVAL;
+}
+
+/**
+ * adreno_waittimestamp - sleep while waiting for the specified timestamp
+ * @device - pointer to a KGSL device structure
+ * @context - pointer to the active kgsl context
+ * @timestamp - GPU timestamp to wait for
+ * @msecs - amount of time to wait (in milliseconds)
+ *
+ * Wait 'msecs' milliseconds for the specified timestamp to expire. Wake up
+ * every KGSL_TIMEOUT_PART milliseconds to check for a device hang and process
+ * one if it happened.  Otherwise, spend most of our time in an interruptible
+ * wait for the timestamp interrupt to be processed.  This function must be
+ * called with the mutex already held.
+ */
+static int adreno_waittimestamp(struct kgsl_device *device,
+				struct kgsl_context *context,
+				unsigned int timestamp,
+				unsigned int msecs)
+{
+	static unsigned int io_cnt;
+	struct adreno_context *adreno_ctx = context ? context->devctxt : NULL;
+	struct kgsl_pwrctrl *pwr = &device->pwrctrl;
+	unsigned int context_id = _get_context_id(context);
+	unsigned int prev_reg_val[hang_detect_regs_count];
+	unsigned int time_elapsed = 0;
+	unsigned int wait;
+	int ts_compare = 1;
+	int io, ret = -ETIMEDOUT;
+
+	/* Get out early if the context has already been destroyed */
+
+	if (context_id == KGSL_CONTEXT_INVALID) {
+		KGSL_DRV_WARN(device, "context was detached");
+		return -EINVAL;
+	}
+
+	/*
+	 * Check to see if the requested timestamp is "newer" then the last
+	 * timestamp issued. If it is complain once and return error.  Only
+	 * print the message once per context so that badly behaving
+	 * applications don't spam the logs
+	 */
+
+	if (adreno_ctx && !(adreno_ctx->flags & CTXT_FLAGS_USER_GENERATED_TS)) {
+		if (_check_pending_timestamp(device, context, timestamp))
+			return -EINVAL;
+
+		/* Reset the invalid timestamp flag on a valid wait */
+		context->wait_on_invalid_ts = false;
+	}
+
+
+	/* Clear the registers used for hang detection */
+	memset(prev_reg_val, 0, sizeof(prev_reg_val));
+
+	/*
+	 * On the first time through the loop only wait 100ms.
+	 * this gives enough time for the engine to start moving and oddly
+	 * provides better hang detection results than just going the full
+	 * KGSL_TIMEOUT_PART right off the bat. The exception to this rule
+	 * is if msecs happens to be < 100ms then just use the full timeout
+	 */
+
+	wait = 100;
+
+	do {
+		long status;
+
+		if (wait > (msecs - time_elapsed))
+			wait = msecs - time_elapsed;
+
+		/*
+		 * if the timestamp happens while we're not
+		 * waiting, there's a chance that an interrupt
+		 * will not be generated and thus the timestamp
+		 * work needs to be queued.
+		 */
+
+		if (kgsl_check_timestamp(device, context, timestamp)) {
+			queue_work(device->work_queue, &device->ts_expired_ws);
+			ret = 0;
+			break;
+		}
+
+		/* Check to see if the GPU is hung */
+		if (adreno_hang_detect(device, prev_reg_val)) {
+			ret = adreno_handle_hang(device, context, timestamp);
+			break;
+		}
+
+		/*
+		 * For proper power accounting sometimes we need to call
+		 * io_wait_interruptible_timeout and sometimes we need to call
+		 * plain old wait_interruptible_timeout. We call the regular
+		 * timeout N times out of 100, where N is a number specified by
+		 * the current power level
+		 */
+
+		io_cnt = (io_cnt + 1) % 100;
+		io = (io_cnt < pwr->pwrlevels[pwr->active_pwrlevel].io_fraction)
+			? 0 : 1;
+
+		mutex_unlock(&device->mutex);
+
+		/* Wait for a timestamp event */
+		status = kgsl_wait_event_interruptible_timeout(
+			device->wait_queue,
+			kgsl_check_interrupt_timestamp(device, context,
+				timestamp), msecs_to_jiffies(wait), io);
+
+		mutex_lock(&device->mutex);
+
+		/*
+		 * If status is non zero then either the condition was satisfied
+		 * or there was an error.  In either event, this is the end of
+		 * the line for us
+		 */
+
+		if (status != 0) {
+			ret = (status > 0) ? 0 : (int) status;
+			break;
+		}
+
+		time_elapsed += wait;
+
+		/* If user specified timestamps are being used, wait at least
+		 * KGSL_SYNCOBJ_SERVER_TIMEOUT msecs for the user driver to
+		 * issue a IB for a timestamp before checking to see if the
+		 * current timestamp we are waiting for is valid or not
+		 */
+
+		if (ts_compare && (adreno_ctx &&
+			(adreno_ctx->flags & CTXT_FLAGS_USER_GENERATED_TS))) {
+			if (time_elapsed > KGSL_SYNCOBJ_SERVER_TIMEOUT) {
+				ret = _check_pending_timestamp(device, context,
+					timestamp);
+				if (ret)
+					break;
+
+				/* Don't do this check again */
+				ts_compare = 0;
+
+				/*
+				 * Reset the invalid timestamp flag on a valid
+				 * wait
+				 */
+				context->wait_on_invalid_ts = false;
+			}
+		}
+
+		/*
+		 * all subsequent trips through the loop wait the full
+		 * KGSL_TIMEOUT_PART interval
+		 */
+		wait = KGSL_TIMEOUT_PART;
+
+	} while (!msecs || time_elapsed < msecs);
+
+	return ret;
 }
 
 static unsigned int adreno_readtimestamp(struct kgsl_device *device,
@@ -2566,6 +2660,7 @@
 	.drawctxt_destroy = adreno_drawctxt_destroy,
 	.setproperty = adreno_setproperty,
 	.postmortem_dump = adreno_dump,
+	.next_event = adreno_next_event,
 };
 
 static struct platform_driver adreno_platform_driver = {
diff --git a/drivers/gpu/msm/adreno.h b/drivers/gpu/msm/adreno.h
index bec19e2..61378fe 100644
--- a/drivers/gpu/msm/adreno.h
+++ b/drivers/gpu/msm/adreno.h
@@ -38,6 +38,7 @@
 /* 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
 
@@ -180,6 +181,10 @@
 void adreno_regwrite(struct kgsl_device *device, unsigned int offsetwords,
 				unsigned int value);
 
+void adreno_shadermem_regread(struct kgsl_device *device,
+						unsigned int offsetwords,
+						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);
diff --git a/drivers/gpu/msm/adreno_a2xx.c b/drivers/gpu/msm/adreno_a2xx.c
index 8de2c70..4e4843b 100644
--- a/drivers/gpu/msm/adreno_a2xx.c
+++ b/drivers/gpu/msm/adreno_a2xx.c
@@ -1718,9 +1718,15 @@
 					eoptimestamp));
 
 		if (context_id < KGSL_MEMSTORE_MAX) {
-			kgsl_sharedmem_writel(&rb->device->memstore,
+			/* reset per context ts_cmp_enable */
+			kgsl_sharedmem_writel(&device->memstore,
 					KGSL_MEMSTORE_OFFSET(context_id,
 						ts_cmp_enable), 0);
+			/* Always reset global timestamp ts_cmp_enable */
+			kgsl_sharedmem_writel(&device->memstore,
+					KGSL_MEMSTORE_OFFSET(
+						KGSL_MEMSTORE_GLOBAL,
+						ts_cmp_enable), 0);
 			wmb();
 		}
 
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 4c7534c..feced43 100644
--- a/drivers/gpu/msm/adreno_a3xx.c
+++ b/drivers/gpu/msm/adreno_a3xx.c
@@ -2586,9 +2586,15 @@
 					eoptimestamp));
 
 		if (context_id < KGSL_MEMSTORE_MAX) {
+			/* reset per context ts_cmp_enable */
 			kgsl_sharedmem_writel(&device->memstore,
 					KGSL_MEMSTORE_OFFSET(context_id,
 						ts_cmp_enable), 0);
+			/* Always reset global timestamp ts_cmp_enable */
+			kgsl_sharedmem_writel(&device->memstore,
+					KGSL_MEMSTORE_OFFSET(
+						KGSL_MEMSTORE_GLOBAL,
+						ts_cmp_enable), 0);
 			wmb();
 		}
 
@@ -2766,12 +2772,12 @@
 static struct a3xx_vbif_data a330_vbif[] = {
 	/* Set up 16 deep read/write request queues */
 	{ A3XX_VBIF_IN_RD_LIM_CONF0, 0x18181818 },
-	{ A3XX_VBIF_IN_RD_LIM_CONF1, 0x18181818 },
-	{ A3XX_VBIF_OUT_RD_LIM_CONF0, 0x18181818 },
-	{ A3XX_VBIF_OUT_WR_LIM_CONF0, 0x18181818 },
+	{ A3XX_VBIF_IN_RD_LIM_CONF1, 0x00001818 },
+	{ A3XX_VBIF_OUT_RD_LIM_CONF0, 0x00001818 },
+	{ A3XX_VBIF_OUT_WR_LIM_CONF0, 0x00001818 },
 	{ A3XX_VBIF_DDR_OUT_MAX_BURST, 0x0000303 },
 	{ A3XX_VBIF_IN_WR_LIM_CONF0, 0x18181818 },
-	{ A3XX_VBIF_IN_WR_LIM_CONF1, 0x18181818 },
+	{ A3XX_VBIF_IN_WR_LIM_CONF1, 0x00001818 },
 	/* Enable WR-REQ */
 	{ A3XX_VBIF_GATE_OFF_WRREQ_EN, 0x00003F },
 	/* Set up round robin arbitration between both AXI ports */
@@ -2779,10 +2785,10 @@
 	/* Set up VBIF_ROUND_ROBIN_QOS_ARB */
 	{ A3XX_VBIF_ROUND_ROBIN_QOS_ARB, 0x0001 },
 	/* Set up AOOO */
-	{ A3XX_VBIF_OUT_AXI_AOOO_EN, 0x0000FFFF },
-	{ A3XX_VBIF_OUT_AXI_AOOO, 0xFFFFFFFF },
+	{ A3XX_VBIF_OUT_AXI_AOOO_EN, 0x0000003F },
+	{ A3XX_VBIF_OUT_AXI_AOOO, 0x003F003F },
 	/* Enable 1K sort */
-	{ A3XX_VBIF_ABIT_SORT, 0x1FFFF },
+	{ A3XX_VBIF_ABIT_SORT, 0x0001003F },
 	{ A3XX_VBIF_ABIT_SORT_CONF, 0x000000A4 },
 	/* Disable VBIF clock gating. This is to enable AXI running
 	 * higher frequency than GPU.
diff --git a/drivers/gpu/msm/adreno_a3xx_snapshot.c b/drivers/gpu/msm/adreno_a3xx_snapshot.c
index a410445..1243dd0 100644
--- a/drivers/gpu/msm/adreno_a3xx_snapshot.c
+++ b/drivers/gpu/msm/adreno_a3xx_snapshot.c
@@ -11,6 +11,7 @@
  *
  */
 
+#include <linux/io.h>
 #include "kgsl.h"
 #include "adreno.h"
 #include "kgsl_snapshot.h"
@@ -19,14 +20,27 @@
 #define DEBUG_SECTION_SZ(_dwords) (((_dwords) * sizeof(unsigned int)) \
 		+ sizeof(struct kgsl_snapshot_debug))
 
+/* Shader memory size in words */
 #define SHADER_MEMORY_SIZE 0x4000
 
+/**
+ * a3xx_snapshot_shader_memory - Helper function to dump the GPU shader
+ * memory to the snapshot buffer.
+ * @device - GPU device whose shader memory is to be dumped
+ * @snapshot - Pointer to binary snapshot data blob being made
+ * @remain - Number of remaining bytes in the snapshot blob
+ * @priv - Unused parameter
+ */
 static int a3xx_snapshot_shader_memory(struct kgsl_device *device,
 	void *snapshot, int remain, void *priv)
 {
 	struct kgsl_snapshot_debug *header = snapshot;
+	unsigned int i;
 	unsigned int *data = snapshot + sizeof(*header);
-	int i;
+	unsigned int shader_read_len = SHADER_MEMORY_SIZE;
+
+	if (SHADER_MEMORY_SIZE > (device->shader_mem_len >> 2))
+		shader_read_len = (device->shader_mem_len >> 2);
 
 	if (remain < DEBUG_SECTION_SZ(SHADER_MEMORY_SIZE)) {
 		SNAPSHOT_ERR_NOMEM(device, "SHADER MEMORY");
@@ -36,8 +50,22 @@
 	header->type = SNAPSHOT_DEBUG_SHADER_MEMORY;
 	header->size = SHADER_MEMORY_SIZE;
 
-	for (i = 0; i < SHADER_MEMORY_SIZE; i++)
-		adreno_regread(device, 0x4000 + i, &data[i]);
+	/* Map shader memory to kernel, for dumping */
+	if (device->shader_mem_virt == NULL)
+		device->shader_mem_virt = devm_ioremap(device->dev,
+					device->shader_mem_phys,
+					device->shader_mem_len);
+
+	if (device->shader_mem_virt == NULL) {
+		KGSL_DRV_ERR(device,
+		"Unable to map shader memory region\n");
+		return 0;
+	}
+
+	/* Now, dump shader memory to snapshot */
+	for (i = 0; i < shader_read_len; i++)
+		adreno_shadermem_regread(device, i, &data[i]);
+
 
 	return DEBUG_SECTION_SZ(SHADER_MEMORY_SIZE);
 }
@@ -220,30 +248,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 +296,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 +335,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 +351,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 +371,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,
diff --git a/drivers/gpu/msm/adreno_postmortem.c b/drivers/gpu/msm/adreno_postmortem.c
index daa78ed..6e0d6ad 100644
--- a/drivers/gpu/msm/adreno_postmortem.c
+++ b/drivers/gpu/msm/adreno_postmortem.c
@@ -12,6 +12,7 @@
  */
 
 #include <linux/vmalloc.h>
+#include <mach/board.h>
 
 #include "kgsl.h"
 #include "kgsl_sharedmem.h"
@@ -70,6 +71,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 +138,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 +276,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) {
@@ -702,6 +738,8 @@
 
 	mb();
 
+	msm_clk_dump_debug_info();
+
 	if (adreno_is_a2xx(adreno_dev))
 		adreno_dump_a2xx(device);
 	else if (adreno_is_a3xx(adreno_dev))
diff --git a/drivers/gpu/msm/adreno_ringbuffer.c b/drivers/gpu/msm/adreno_ringbuffer.c
index da9daf7..27343c5 100644
--- a/drivers/gpu/msm/adreno_ringbuffer.c
+++ b/drivers/gpu/msm/adreno_ringbuffer.c
@@ -28,6 +28,14 @@
 
 #define GSL_RB_NOP_SIZEDWORDS				2
 
+/*
+ * CP DEBUG settings for all cores:
+ * DYNAMIC_CLK_DISABLE [27] - turn off the dynamic clock control
+ * PROG_END_PTR_ENABLE [25] - Allow 128 bit writes to the VBIF
+ */
+
+#define CP_DEBUG_DEFAULT ((1 << 27) | (1 << 25))
+
 void adreno_ringbuffer_submit(struct adreno_ringbuffer *rb)
 {
 	BUG_ON(rb->wptr == 0);
@@ -231,7 +239,7 @@
 	KGSL_DRV_INFO(device, "loading pm4 ucode version: %d\n",
 		adreno_dev->pm4_fw[0]);
 
-	adreno_regwrite(device, REG_CP_DEBUG, 0x02000000);
+	adreno_regwrite(device, REG_CP_DEBUG, CP_DEBUG_DEFAULT);
 	adreno_regwrite(device, REG_CP_ME_RAM_WADDR, 0);
 	for (i = 1; i < adreno_dev->pm4_fw_size; i++)
 		adreno_regwrite(device, REG_CP_ME_RAM_DATA,
@@ -524,16 +532,18 @@
 	total_sizedwords += flags & KGSL_CMD_FLAGS_PMODE ? 4 : 0;
 	/* 2 dwords to store the start of command sequence */
 	total_sizedwords += 2;
-	/*
-	 * 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;
+	/* 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 */
+	total_sizedwords += context ? 7 : 0;
 
 	if (adreno_is_a3xx(adreno_dev))
 		total_sizedwords += 7;
 
+	if (adreno_is_a2xx(adreno_dev))
+		total_sizedwords += 2; /* CP_WAIT_FOR_IDLE */
+
 	total_sizedwords += 2; /* scratchpad ts for recovery */
 	if (context && context->flags & CTXT_FLAGS_PER_CONTEXT_TS &&
 			!(flags & KGSL_CMD_FLAGS_INTERNAL_ISSUE)) {
@@ -545,6 +555,9 @@
 		total_sizedwords += 4; /* global timestamp for recovery*/
 	}
 
+	if (adreno_is_a20x(adreno_dev))
+		total_sizedwords += 2; /* CACHE_FLUSH */
+
 	ringcmds = adreno_ringbuffer_allocspace(rb, context, total_sizedwords);
 	if (!ringcmds) {
 		/*
@@ -560,6 +573,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,
@@ -594,6 +612,16 @@
 	}
 	timestamp = rb->timestamp[context_id];
 
+	/* HW Workaround for MMU Page fault
+	* due to memory getting free early before
+	* GPU completes it.
+	*/
+	if (adreno_is_a2xx(adreno_dev)) {
+		GSL_RB_WRITE(ringcmds, rcmd_gpu,
+			cp_type3_packet(CP_WAIT_FOR_IDLE, 1));
+		GSL_RB_WRITE(ringcmds, rcmd_gpu, 0x00);
+	}
+
 	/* scratchpad ts for recovery */
 	GSL_RB_WRITE(ringcmds, rcmd_gpu, cp_type0_packet(REG_CP_TIMESTAMP, 1));
 	GSL_RB_WRITE(ringcmds, rcmd_gpu, rb->timestamp[KGSL_MEMSTORE_GLOBAL]);
@@ -647,7 +675,13 @@
 				rb->timestamp[KGSL_MEMSTORE_GLOBAL]);
 	}
 
-	if (context && !(flags & KGSL_CMD_FLAGS_INTERNAL_ISSUE)) {
+	if (adreno_is_a20x(adreno_dev)) {
+		GSL_RB_WRITE(ringcmds, rcmd_gpu,
+			cp_type3_packet(CP_EVENT_WRITE, 1));
+		GSL_RB_WRITE(ringcmds, rcmd_gpu, CACHE_FLUSH);
+	}
+
+	if (context) {
 		/* Conditional execution based on memory values */
 		GSL_RB_WRITE(ringcmds, rcmd_gpu,
 			cp_type3_packet(CP_COND_EXEC, 4));
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 afe384b..deafa7a 100644
--- a/drivers/gpu/msm/kgsl.c
+++ b/drivers/gpu/msm/kgsl.c
@@ -28,6 +28,7 @@
 #include <linux/msm_ion.h>
 #include <linux/io.h>
 #include <mach/socinfo.h>
+#include <linux/mman.h>
 
 #include "kgsl.h"
 #include "kgsl_debugfs.h"
@@ -193,6 +194,52 @@
 }
 EXPORT_SYMBOL(kgsl_cancel_events);
 
+int kgsl_memfree_hist_init(void)
+{
+	void *base;
+
+	base = kzalloc(KGSL_MEMFREE_HIST_SIZE, GFP_KERNEL);
+	kgsl_driver.memfree_hist.base_hist_rb = base;
+	if (base == NULL)
+		return -ENOMEM;
+	kgsl_driver.memfree_hist.size = KGSL_MEMFREE_HIST_SIZE;
+	kgsl_driver.memfree_hist.wptr = base;
+	return 0;
+}
+
+void kgsl_memfree_hist_exit(void)
+{
+	kfree(kgsl_driver.memfree_hist.base_hist_rb);
+	kgsl_driver.memfree_hist.base_hist_rb = NULL;
+}
+
+void kgsl_memfree_hist_set_event(unsigned int pid, unsigned int gpuaddr,
+			unsigned int size, int flags)
+{
+	struct kgsl_memfree_hist_elem *p;
+
+	void *base = kgsl_driver.memfree_hist.base_hist_rb;
+	int rbsize = kgsl_driver.memfree_hist.size;
+
+	if (base == NULL)
+		return;
+
+	mutex_lock(&kgsl_driver.memfree_hist_mutex);
+	p = kgsl_driver.memfree_hist.wptr;
+	p->pid = pid;
+	p->gpuaddr = gpuaddr;
+	p->size = size;
+	p->flags = flags;
+
+	kgsl_driver.memfree_hist.wptr++;
+	if ((void *)kgsl_driver.memfree_hist.wptr >= base+rbsize) {
+		kgsl_driver.memfree_hist.wptr =
+			(struct kgsl_memfree_hist_elem *)base;
+	}
+	mutex_unlock(&kgsl_driver.memfree_hist_mutex);
+}
+
+
 /* kgsl_get_mem_entry - get the mem_entry structure for the specified object
  * @device - Pointer to the device structure
  * @ptbase - the pagetable base of the object
@@ -277,9 +324,19 @@
 }
 EXPORT_SYMBOL(kgsl_mem_entry_destroy);
 
-static
-void kgsl_mem_entry_attach_process(struct kgsl_mem_entry *entry,
-				   struct kgsl_process_private *process)
+/**
+ * kgsl_mem_entry_track_gpuaddr - Insert a mem_entry in the address tree
+ * @process: the process that owns the memory
+ * @entry: the memory entry
+ *
+ * Insert a kgsl_mem_entry in to the rb_tree for searching by GPU address.
+ * Not all mem_entries will have gpu addresses when first created, so this
+ * function may be called after creation when the GPU address is finally
+ * assigned.
+ */
+static void
+kgsl_mem_entry_track_gpuaddr(struct kgsl_process_private *process,
+				struct kgsl_mem_entry *entry)
 {
 	struct rb_node **node;
 	struct rb_node *parent = NULL;
@@ -304,8 +361,48 @@
 	rb_insert_color(&entry->node, &process->mem_rb);
 
 	spin_unlock(&process->mem_lock);
+}
 
+/**
+ * kgsl_mem_entry_attach_process - Attach a mem_entry to its owner process
+ * @entry: the memory entry
+ * @process: the owner process
+ *
+ * Attach a newly created mem_entry to its owner process so that
+ * it can be found later. The mem_entry will be added to mem_idr and have
+ * its 'id' field assigned. If the GPU address has been set, the entry
+ * will also be added to the mem_rb tree.
+ *
+ * @returns - 0 on success or error code on failure.
+ */
+static int
+kgsl_mem_entry_attach_process(struct kgsl_mem_entry *entry,
+				   struct kgsl_process_private *process)
+{
+	int ret;
+
+	while (1) {
+		if (idr_pre_get(&process->mem_idr, GFP_KERNEL) == 0) {
+			ret = -ENOMEM;
+			goto err;
+		}
+
+		spin_lock(&process->mem_lock);
+		ret = idr_get_new_above(&process->mem_idr, entry, 1,
+					&entry->id);
+		spin_unlock(&process->mem_lock);
+
+		if (ret == 0)
+			break;
+		else if (ret != -EAGAIN)
+			goto err;
+	}
 	entry->priv = process;
+
+	if (entry->memdesc.gpuaddr != 0)
+		kgsl_mem_entry_track_gpuaddr(process, entry);
+err:
+	return ret;
 }
 
 /* Detach a memory entry from a process and unmap it from the MMU */
@@ -315,6 +412,17 @@
 	if (entry == NULL)
 		return;
 
+	spin_lock(&entry->priv->mem_lock);
+
+	if (entry->id != 0)
+		idr_remove(&entry->priv->mem_idr, entry->id);
+	entry->id = 0;
+
+	if (entry->memdesc.gpuaddr != 0)
+		rb_erase(&entry->node, &entry->priv->mem_rb);
+
+	spin_unlock(&entry->priv->mem_lock);
+
 	entry->priv->stats[entry->memtype].cur -= entry->memdesc.size;
 	entry->priv = NULL;
 
@@ -333,14 +441,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 +463,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 +472,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;
@@ -449,6 +566,22 @@
 		kfree(event);
 	}
 
+	/* Send the next pending event for each context to the device */
+	if (device->ftbl->next_event) {
+		unsigned int id = KGSL_MEMSTORE_GLOBAL;
+
+		list_for_each_entry(event, &device->events, list) {
+
+			if (!event->context)
+				continue;
+
+			if (event->context->id != id) {
+				device->ftbl->next_event(device, event);
+				id = event->context->id;
+			}
+		}
+	}
+
 	mutex_unlock(&device->mutex);
 }
 EXPORT_SYMBOL(kgsl_timestamp_expired);
@@ -706,6 +839,8 @@
 	private->pid = task_tgid_nr(current);
 	private->mem_rb = RB_ROOT;
 
+	idr_init(&private->mem_idr);
+
 	if (kgsl_mmu_enabled())
 	{
 		unsigned long pt_name;
@@ -734,7 +869,7 @@
 			 struct kgsl_process_private *private)
 {
 	struct kgsl_mem_entry *entry = NULL;
-	struct rb_node *node;
+	int next = 0;
 
 	if (!private)
 		return;
@@ -749,14 +884,22 @@
 
 	list_del(&private->list);
 
-	for (node = rb_first(&private->mem_rb); node; ) {
-		entry = rb_entry(node, struct kgsl_mem_entry, node);
-		node = rb_next(&entry->node);
-
-		rb_erase(&entry->node, &private->mem_rb);
+	while (1) {
+		rcu_read_lock();
+		entry = idr_get_next(&private->mem_idr, &next);
+		rcu_read_unlock();
+		if (entry == NULL)
+			break;
 		kgsl_mem_entry_detach_process(entry);
+		/*
+		 * Always start back at the beginning, to
+		 * ensure all entries are removed,
+		 * like list_for_each_entry_safe.
+		 */
+		next = 0;
 	}
 	kgsl_mmu_putpagetable(private->pagetable);
+	idr_destroy(&private->mem_idr);
 	kfree(private);
 unlock:
 	mutex_unlock(&kgsl_driver.process_mutex);
@@ -844,13 +987,6 @@
 	dev_priv->device = device;
 	filep->private_data = dev_priv;
 
-	/* Get file (per process) private struct */
-	dev_priv->process_priv = kgsl_get_process_private(dev_priv);
-	if (dev_priv->process_priv ==  NULL) {
-		result = -ENOMEM;
-		goto err_freedevpriv;
-	}
-
 	mutex_lock(&device->mutex);
 	kgsl_check_suspended(device);
 
@@ -862,21 +998,38 @@
 
 		if (result) {
 			mutex_unlock(&device->mutex);
-			goto err_putprocess;
+			goto err_freedevpriv;
 		}
 		kgsl_pwrctrl_set_state(device, KGSL_STATE_ACTIVE);
 	}
 	device->open_count++;
 	mutex_unlock(&device->mutex);
 
+	/*
+	 * Get file (per process) private struct. This must be done
+	 * after the first start so that the global pagetable mappings
+	 * are set up before we create the per-process pagetable.
+	 */
+	dev_priv->process_priv = kgsl_get_process_private(dev_priv);
+	if (dev_priv->process_priv ==  NULL) {
+		result = -ENOMEM;
+		goto err_stop;
+	}
+
 	KGSL_DRV_INFO(device, "Initialized %s: mmu=%s pagetable_count=%d\n",
 		device->name, kgsl_mmu_enabled() ? "on" : "off",
 		kgsl_pagetable_count);
 
 	return result;
 
-err_putprocess:
-	kgsl_put_process_private(device, dev_priv->process_priv);
+err_stop:
+	mutex_lock(&device->mutex);
+	device->open_count--;
+	if (device->open_count == 0) {
+		result = device->ftbl->stop(device);
+		kgsl_pwrctrl_set_state(device, KGSL_STATE_INIT);
+	}
+	mutex_unlock(&device->mutex);
 err_freedevpriv:
 	filep->private_data = NULL;
 	kfree(dev_priv);
@@ -925,6 +1078,77 @@
 	return kgsl_sharedmem_find_region(private, gpuaddr, 1);
 }
 
+/**
+ * kgsl_sharedmem_region_empty - Check if an addression region is empty
+ *
+ * @private: private data for the process to check.
+ * @gpuaddr: start address of the region
+ * @size: length of the region.
+ *
+ * Checks that there are no existing allocations within an address
+ * region. Note that unlike other kgsl_sharedmem* search functions,
+ * this one manages locking on its own.
+ */
+int
+kgsl_sharedmem_region_empty(struct kgsl_process_private *private,
+	unsigned int gpuaddr, size_t size)
+{
+	int result = 1;
+	unsigned int gpuaddr_end = gpuaddr + size;
+
+	struct rb_node *node = private->mem_rb.rb_node;
+
+	if (!kgsl_mmu_gpuaddr_in_range(gpuaddr))
+		return 0;
+
+	/* don't overflow */
+	if (gpuaddr_end < gpuaddr)
+		return 0;
+
+	spin_lock(&private->mem_lock);
+	node = private->mem_rb.rb_node;
+	while (node != NULL) {
+		struct kgsl_mem_entry *entry;
+		unsigned int memdesc_start, memdesc_end;
+
+		entry = rb_entry(node, struct kgsl_mem_entry, node);
+
+		memdesc_start = entry->memdesc.gpuaddr;
+		memdesc_end = memdesc_start
+				+ kgsl_memdesc_mmapsize(&entry->memdesc);
+
+		if (gpuaddr_end <= memdesc_start)
+			node = node->rb_left;
+		else if (memdesc_end <= gpuaddr)
+			node = node->rb_right;
+		else {
+			result = 0;
+			break;
+		}
+	}
+	spin_unlock(&private->mem_lock);
+	return result;
+}
+
+/**
+ * kgsl_sharedmem_find_id - find a memory entry by id
+ * @process: the owning process
+ * @id: id to find
+ *
+ * @returns - the mem_entry or NULL
+ */
+static inline struct kgsl_mem_entry *
+kgsl_sharedmem_find_id(struct kgsl_process_private *process, unsigned int id)
+{
+	struct kgsl_mem_entry *entry;
+
+	rcu_read_lock();
+	entry = idr_find(&process->mem_idr, id);
+	rcu_read_unlock();
+
+	return entry;
+}
+
 /*call all ioctl sub functions with driver locked*/
 static long kgsl_ioctl_device_getproperty(struct kgsl_device_private *dev_priv,
 					  unsigned int cmd, void *data)
@@ -1212,9 +1436,6 @@
 	void *priv, u32 id, u32 timestamp)
 {
 	struct kgsl_mem_entry *entry = priv;
-	spin_lock(&entry->priv->mem_lock);
-	rb_erase(&entry->node, &entry->priv->mem_rb);
-	spin_unlock(&entry->priv->mem_lock);
 	trace_kgsl_mem_timestamp_free(device, entry, id, timestamp, 0);
 	kgsl_mem_entry_detach_process(entry);
 }
@@ -1283,8 +1504,8 @@
 
 	context = kgsl_create_context(dev_priv);
 
-	if (context == NULL) {
-		result = -ENOMEM;
+	if (IS_ERR(context)) {
+		result = PTR_ERR(context);
 		goto done;
 	}
 
@@ -1298,7 +1519,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;
@@ -1326,27 +1547,52 @@
 static long kgsl_ioctl_sharedmem_free(struct kgsl_device_private *dev_priv,
 					unsigned int cmd, void *data)
 {
-	int result = 0;
 	struct kgsl_sharedmem_free *param = data;
 	struct kgsl_process_private *private = dev_priv->process_priv;
 	struct kgsl_mem_entry *entry = NULL;
 
 	spin_lock(&private->mem_lock);
 	entry = kgsl_sharedmem_find(private, param->gpuaddr);
-	if (entry)
-		rb_erase(&entry->node, &private->mem_rb);
-
 	spin_unlock(&private->mem_lock);
 
-	if (entry) {
-		trace_kgsl_mem_free(entry);
-		kgsl_mem_entry_detach_process(entry);
-	} else {
-		KGSL_CORE_ERR("invalid gpuaddr %08x\n", param->gpuaddr);
-		result = -EINVAL;
+	if (!entry) {
+		KGSL_MEM_INFO(dev_priv->device, "invalid gpuaddr %08x\n",
+				param->gpuaddr);
+		return -EINVAL;
 	}
+	trace_kgsl_mem_free(entry);
 
-	return result;
+	kgsl_memfree_hist_set_event(entry->priv->pid,
+				    entry->memdesc.gpuaddr,
+				    entry->memdesc.size,
+				    entry->memdesc.flags);
+
+	kgsl_mem_entry_detach_process(entry);
+	return 0;
+}
+
+static long kgsl_ioctl_gpumem_free_id(struct kgsl_device_private *dev_priv,
+					unsigned int cmd, void *data)
+{
+	struct kgsl_gpumem_free_id *param = data;
+	struct kgsl_process_private *private = dev_priv->process_priv;
+	struct kgsl_mem_entry *entry = NULL;
+
+	entry = kgsl_sharedmem_find_id(private, param->id);
+
+	if (!entry) {
+		KGSL_MEM_INFO(dev_priv->device, "invalid id %d\n", param->id);
+		return -EINVAL;
+	}
+	trace_kgsl_mem_free(entry);
+
+	kgsl_memfree_hist_set_event(entry->priv->pid,
+				    entry->memdesc.gpuaddr,
+				    entry->memdesc.size,
+				    entry->memdesc.flags);
+
+	kgsl_mem_entry_detach_process(entry);
+	return 0;
 }
 
 static struct vm_area_struct *kgsl_get_vma_from_start_addr(unsigned int addr)
@@ -1455,6 +1701,8 @@
 	entry->memdesc.size = size;
 	entry->memdesc.physaddr = phys + offset;
 	entry->memdesc.hostptr = (void *) (virt + offset);
+	/* USE_CPU_MAP is not impemented for PMEM. */
+	entry->memdesc.flags &= ~KGSL_MEMFLAGS_USE_CPU_MAP;
 
 	ret = memdesc_sg_phys(&entry->memdesc, phys + offset, size);
 	if (ret)
@@ -1469,11 +1717,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);
 
@@ -1524,34 +1771,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;
 	}
 
@@ -1572,28 +1818,29 @@
 
 	entry->memdesc.pagetable = pagetable;
 	entry->memdesc.size = size;
-	entry->memdesc.hostptr = hostptr + (offset & PAGE_MASK);
+	entry->memdesc.useraddr = useraddr + (offset & PAGE_MASK);
+	if (kgsl_memdesc_use_cpu_map(&entry->memdesc))
+		entry->memdesc.gpuaddr = entry->memdesc.useraddr;
 
-	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;
 	}
@@ -1604,8 +1851,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;
 	}
 
@@ -1625,9 +1872,11 @@
 	entry->priv_data = filep;
 	entry->memdesc.pagetable = pagetable;
 	entry->memdesc.size = ALIGN(size, PAGE_SIZE);
-	entry->memdesc.hostptr = hostptr;
+	entry->memdesc.useraddr = useraddr;
+	if (kgsl_memdesc_use_cpu_map(&entry->memdesc))
+		entry->memdesc.gpuaddr = entry->memdesc.useraddr;
 
-	ret = memdesc_sg_virt(&entry->memdesc, hostptr, size);
+	ret = memdesc_sg_virt(&entry->memdesc, useraddr, size);
 	if (ret)
 		goto err;
 
@@ -1640,7 +1889,7 @@
 #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;
 }
@@ -1671,6 +1920,8 @@
 	entry->priv_data = handle;
 	entry->memdesc.pagetable = pagetable;
 	entry->memdesc.size = 0;
+	/* USE_CPU_MAP is not impemented for ION. */
+	entry->memdesc.flags &= ~KGSL_MEMFLAGS_USE_CPU_MAP;
 
 	sg_table = ion_sg_table(kgsl_ion_client, handle);
 
@@ -1694,6 +1945,13 @@
 	return -ENOMEM;
 }
 
+static inline int
+can_use_cpu_map(void)
+{
+	return (kgsl_mmu_get_mmutype() == KGSL_MMU_TYPE_IOMMU
+		&& kgsl_mmu_is_perprocess());
+}
+
 static long kgsl_ioctl_map_user_mem(struct kgsl_device_private *dev_priv,
 				     unsigned int cmd, void *data)
 {
@@ -1713,6 +1971,21 @@
 	else
 		memtype = param->memtype;
 
+	/*
+	 * Mask off unknown flags from userspace. This way the caller can
+	 * check if a flag is supported by looking at the returned flags.
+	 * Note: CACHEMODE is ignored for this call. Caching should be
+	 * determined by type of allocation being mapped.
+	 */
+	param->flags &= KGSL_MEMFLAGS_GPUREADONLY
+			| KGSL_MEMTYPE_MASK
+			| KGSL_MEMALIGN_MASK
+			| KGSL_MEMFLAGS_USE_CPU_MAP;
+
+	entry->memdesc.flags = param->flags;
+	if (!can_use_cpu_map())
+		entry->memdesc.flags &= ~KGSL_MEMFLAGS_USE_CPU_MAP;
+
 	switch (memtype) {
 	case KGSL_USER_MEM_TYPE_PMEM:
 		if (param->fd == 0 || param->len == 0)
@@ -1737,8 +2010,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;
@@ -1755,7 +2028,7 @@
 			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;
@@ -1771,12 +2044,10 @@
 	if (result)
 		goto error;
 
-	entry->memdesc.priv |= param->flags & KGSL_MEMTYPE_MASK;
-
 	if (entry->memdesc.size >= SZ_1M)
-		entry->memdesc.priv |= ilog2(SZ_1M) << KGSL_MEMALIGN_SHIFT;
+		kgsl_memdesc_set_align(&entry->memdesc, ilog2(SZ_1M));
 	else if (entry->memdesc.size >= SZ_64K)
-		entry->memdesc.priv |= ilog2(SZ_64K) << KGSL_MEMALIGN_SHIFT;
+		kgsl_memdesc_set_align(&entry->memdesc, ilog2(SZ_64));
 
 	result = kgsl_mmu_map(private->pagetable,
 			      &entry->memdesc,
@@ -1787,18 +2058,25 @@
 
 	/* Adjust the returned value for a non 4k aligned offset */
 	param->gpuaddr = entry->memdesc.gpuaddr + (param->offset & ~PAGE_MASK);
+	/* echo back flags */
+	param->flags = entry->memdesc.flags;
+
+	result = kgsl_mem_entry_attach_process(entry, private);
+	if (result)
+		goto error_unmap;
 
 	KGSL_STATS_ADD(param->len, kgsl_driver.stats.mapped,
 		kgsl_driver.stats.mapped_max);
 
 	kgsl_process_add_stats(private, entry->memtype, param->len);
 
-	kgsl_mem_entry_attach_process(entry, private);
 	trace_kgsl_mem_map(entry, param->fd);
 
 	kgsl_check_idle(dev_priv->device);
 	return result;
 
+error_unmap:
+	kgsl_mmu_unmap(private->pagetable, &entry->memdesc);
 error_put_file_ptr:
 	switch (entry->memtype) {
 	case KGSL_MEM_ENTRY_PMEM:
@@ -1818,33 +2096,134 @@
 	return result;
 }
 
-/*This function flushes a graphics memory allocation from CPU cache
- *when caching is enabled with MMU*/
+static int _kgsl_gpumem_sync_cache(struct kgsl_mem_entry *entry, int op)
+{
+	int ret = 0;
+	int cacheop;
+	int mode;
+
+	/*
+	 * Flush is defined as (clean | invalidate).  If both bits are set, then
+	 * do a flush, otherwise check for the individual bits and clean or inv
+	 * as requested
+	 */
+
+	if ((op & KGSL_GPUMEM_CACHE_FLUSH) == KGSL_GPUMEM_CACHE_FLUSH)
+		cacheop = KGSL_CACHE_OP_FLUSH;
+	else if (op & KGSL_GPUMEM_CACHE_CLEAN)
+		cacheop = KGSL_CACHE_OP_CLEAN;
+	else if (op & KGSL_GPUMEM_CACHE_INV)
+		cacheop = KGSL_CACHE_OP_INV;
+	else {
+		ret = -EINVAL;
+		goto done;
+	}
+
+	mode = kgsl_memdesc_get_cachemode(&entry->memdesc);
+	if (mode != KGSL_CACHEMODE_UNCACHED
+		&& mode != KGSL_CACHEMODE_WRITECOMBINE)
+		kgsl_cache_range_op(&entry->memdesc, cacheop);
+
+done:
+	return ret;
+}
+
+/* New cache sync function - supports both directions (clean and invalidate) */
+
+static long
+kgsl_ioctl_gpumem_sync_cache(struct kgsl_device_private *dev_priv,
+	unsigned int cmd, void *data)
+{
+	struct kgsl_gpumem_sync_cache *param = data;
+	struct kgsl_process_private *private = dev_priv->process_priv;
+	struct kgsl_mem_entry *entry = NULL;
+
+	if (param->id != 0) {
+		entry = kgsl_sharedmem_find_id(private, param->id);
+		if (entry == NULL) {
+			KGSL_MEM_INFO(dev_priv->device, "can't find id %d\n",
+					param->id);
+			return -EINVAL;
+		}
+	} else if (param->gpuaddr != 0) {
+		spin_lock(&private->mem_lock);
+		entry = kgsl_sharedmem_find(private, param->gpuaddr);
+		spin_unlock(&private->mem_lock);
+		if (entry == NULL) {
+			KGSL_MEM_INFO(dev_priv->device,
+					"can't find gpuaddr %x\n",
+					param->gpuaddr);
+			return -EINVAL;
+		}
+	} else {
+		return -EINVAL;
+	}
+
+	return _kgsl_gpumem_sync_cache(entry, param->op);
+}
+
+/* Legacy cache function, does a flush (clean  + invalidate) */
+
 static long
 kgsl_ioctl_sharedmem_flush_cache(struct kgsl_device_private *dev_priv,
 				 unsigned int cmd, void *data)
 {
-	int result = 0;
-	struct kgsl_mem_entry *entry;
 	struct kgsl_sharedmem_free *param = data;
 	struct kgsl_process_private *private = dev_priv->process_priv;
+	struct kgsl_mem_entry *entry = NULL;
 
 	spin_lock(&private->mem_lock);
 	entry = kgsl_sharedmem_find(private, param->gpuaddr);
-	if (!entry) {
-		KGSL_CORE_ERR("invalid gpuaddr %08x\n", param->gpuaddr);
-		result = -EINVAL;
-		goto done;
-	}
-	if (!entry->memdesc.hostptr) {
-		KGSL_CORE_ERR("invalid hostptr with gpuaddr %08x\n",
-			param->gpuaddr);
-			goto done;
+	spin_unlock(&private->mem_lock);
+	if (entry == NULL) {
+		KGSL_MEM_INFO(dev_priv->device,
+				"can't find gpuaddr %x\n",
+				param->gpuaddr);
+		return -EINVAL;
 	}
 
-	kgsl_cache_range_op(&entry->memdesc, KGSL_CACHE_OP_CLEAN);
-done:
-	spin_unlock(&private->mem_lock);
+	return _kgsl_gpumem_sync_cache(entry, KGSL_GPUMEM_CACHE_FLUSH);
+}
+
+/*
+ * The common parts of kgsl_ioctl_gpumem_alloc and kgsl_ioctl_gpumem_alloc_id.
+ */
+int
+_gpumem_alloc(struct kgsl_device_private *dev_priv,
+		struct kgsl_mem_entry **ret_entry,
+		unsigned int size, unsigned int flags)
+{
+	int result;
+	struct kgsl_process_private *private = dev_priv->process_priv;
+	struct kgsl_mem_entry *entry;
+
+	/*
+	 * Mask off unknown flags from userspace. This way the caller can
+	 * check if a flag is supported by looking at the returned flags.
+	 */
+	flags &= KGSL_MEMFLAGS_GPUREADONLY
+		| KGSL_CACHEMODE_MASK
+		| KGSL_MEMTYPE_MASK
+		| KGSL_MEMALIGN_MASK
+		| KGSL_MEMFLAGS_USE_CPU_MAP;
+
+	entry = kgsl_mem_entry_create();
+	if (entry == NULL)
+		return -ENOMEM;
+
+	result = kgsl_allocate_user(&entry->memdesc, private->pagetable, size,
+				    flags);
+	if (result != 0)
+		goto err;
+
+	entry->memtype = KGSL_MEM_ENTRY_KERNEL;
+
+	kgsl_check_idle(dev_priv->device);
+	*ret_entry = entry;
+	return result;
+err:
+	kfree(entry);
+	*ret_entry = NULL;
 	return result;
 }
 
@@ -1854,29 +2233,117 @@
 {
 	struct kgsl_process_private *private = dev_priv->process_priv;
 	struct kgsl_gpumem_alloc *param = data;
-	struct kgsl_mem_entry *entry;
+	struct kgsl_mem_entry *entry = NULL;
 	int result;
 
-	entry = kgsl_mem_entry_create();
-	if (entry == NULL)
-		return -ENOMEM;
+	param->flags &= ~KGSL_MEMFLAGS_USE_CPU_MAP;
+	result = _gpumem_alloc(dev_priv, &entry, param->size, param->flags);
+	if (result)
+		return result;
 
-	result = kgsl_allocate_user(&entry->memdesc, private->pagetable,
-		param->size, param->flags);
+	result = kgsl_mmu_map(private->pagetable, &entry->memdesc,
+				kgsl_memdesc_protflags(&entry->memdesc));
+	if (result)
+		goto err;
 
-	if (result == 0) {
-		entry->memtype = KGSL_MEM_ENTRY_KERNEL;
-		kgsl_mem_entry_attach_process(entry, private);
-		param->gpuaddr = entry->memdesc.gpuaddr;
+	result = kgsl_mem_entry_attach_process(entry, private);
+	if (result != 0)
+		goto err;
 
-		kgsl_process_add_stats(private, entry->memtype, param->size);
-		trace_kgsl_mem_alloc(entry);
-	} else
-		kfree(entry);
+	kgsl_process_add_stats(private, entry->memtype, param->size);
+	trace_kgsl_mem_alloc(entry);
 
-	kgsl_check_idle(dev_priv->device);
+	param->gpuaddr = entry->memdesc.gpuaddr;
+	param->size = entry->memdesc.size;
+	param->flags = entry->memdesc.flags;
+	return result;
+err:
+	kgsl_sharedmem_free(&entry->memdesc);
+	kfree(entry);
 	return result;
 }
+
+static long
+kgsl_ioctl_gpumem_alloc_id(struct kgsl_device_private *dev_priv,
+			unsigned int cmd, void *data)
+{
+	struct kgsl_process_private *private = dev_priv->process_priv;
+	struct kgsl_gpumem_alloc_id *param = data;
+	struct kgsl_mem_entry *entry = NULL;
+	int result;
+
+	if (!can_use_cpu_map())
+		param->flags &= ~KGSL_MEMFLAGS_USE_CPU_MAP;
+
+	result = _gpumem_alloc(dev_priv, &entry, param->size, param->flags);
+	if (result != 0)
+		goto err;
+
+	if (!kgsl_memdesc_use_cpu_map(&entry->memdesc)) {
+		result = kgsl_mmu_map(private->pagetable, &entry->memdesc,
+				kgsl_memdesc_protflags(&entry->memdesc));
+		if (result)
+			goto err;
+	}
+
+	result = kgsl_mem_entry_attach_process(entry, private);
+	if (result != 0)
+		goto err;
+
+	kgsl_process_add_stats(private, entry->memtype, param->size);
+	trace_kgsl_mem_alloc(entry);
+
+	param->id = entry->id;
+	param->flags = entry->memdesc.flags;
+	param->size = entry->memdesc.size;
+	param->mmapsize = kgsl_memdesc_mmapsize(&entry->memdesc);
+	param->gpuaddr = entry->memdesc.gpuaddr;
+	return result;
+err:
+	if (entry)
+		kgsl_sharedmem_free(&entry->memdesc);
+	kfree(entry);
+	return result;
+}
+
+static long
+kgsl_ioctl_gpumem_get_info(struct kgsl_device_private *dev_priv,
+			unsigned int cmd, void *data)
+{
+	struct kgsl_process_private *private = dev_priv->process_priv;
+	struct kgsl_gpumem_get_info *param = data;
+	struct kgsl_mem_entry *entry = NULL;
+	int result = 0;
+
+	if (param->id != 0) {
+		entry = kgsl_sharedmem_find_id(private, param->id);
+		if (entry == NULL) {
+			KGSL_MEM_INFO(dev_priv->device, "can't find id %d\n",
+					param->id);
+			return -EINVAL;
+		}
+	} else if (param->gpuaddr != 0) {
+		spin_lock(&private->mem_lock);
+		entry = kgsl_sharedmem_find(private, param->gpuaddr);
+		spin_unlock(&private->mem_lock);
+		if (entry == NULL) {
+			KGSL_MEM_INFO(dev_priv->device,
+					"can't find gpuaddr %lx\n",
+					param->gpuaddr);
+			return -EINVAL;
+		}
+	} else {
+		return -EINVAL;
+	}
+	param->gpuaddr = entry->memdesc.gpuaddr;
+	param->id = entry->id;
+	param->flags = entry->memdesc.flags;
+	param->size = entry->memdesc.size;
+	param->mmapsize = kgsl_memdesc_mmapsize(&entry->memdesc);
+	param->useraddr = entry->memdesc.useraddr;
+	return result;
+}
+
 static long kgsl_ioctl_cff_syncmem(struct kgsl_device_private *dev_priv,
 					unsigned int cmd, void *data)
 {
@@ -2076,6 +2543,14 @@
 			kgsl_ioctl_timestamp_event, 1),
 	KGSL_IOCTL_FUNC(IOCTL_KGSL_SETPROPERTY,
 			kgsl_ioctl_device_setproperty, 1),
+	KGSL_IOCTL_FUNC(IOCTL_KGSL_GPUMEM_ALLOC_ID,
+			kgsl_ioctl_gpumem_alloc_id, 0),
+	KGSL_IOCTL_FUNC(IOCTL_KGSL_GPUMEM_FREE_ID,
+			kgsl_ioctl_gpumem_free_id, 0),
+	KGSL_IOCTL_FUNC(IOCTL_KGSL_GPUMEM_GET_INFO,
+			kgsl_ioctl_gpumem_get_info, 0),
+	KGSL_IOCTL_FUNC(IOCTL_KGSL_GPUMEM_SYNC_CACHE,
+			kgsl_ioctl_gpumem_sync_cache, 0),
 };
 
 static long kgsl_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
@@ -2232,6 +2707,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);
 }
 
@@ -2241,8 +2718,145 @@
 	.close = kgsl_gpumem_vm_close,
 };
 
+static int
+get_mmap_entry(struct kgsl_process_private *private,
+		struct kgsl_mem_entry **out_entry, unsigned long pgoff,
+		unsigned long len)
+{
+	int ret = -EINVAL;
+	struct kgsl_mem_entry *entry;
+
+	entry = kgsl_sharedmem_find_id(private, pgoff);
+	if (entry == NULL) {
+		spin_lock(&private->mem_lock);
+		entry = kgsl_sharedmem_find(private, pgoff << PAGE_SHIFT);
+		spin_unlock(&private->mem_lock);
+	}
+
+	if (!entry)
+		return -EINVAL;
+
+	kgsl_mem_entry_get(entry);
+
+	if (!entry->memdesc.ops ||
+		!entry->memdesc.ops->vmflags ||
+		!entry->memdesc.ops->vmfault) {
+		ret = -EINVAL;
+		goto err_put;
+	}
+
+	if (entry->memdesc.useraddr != 0) {
+		ret = -EBUSY;
+		goto err_put;
+	}
+
+	if (len != kgsl_memdesc_mmapsize(&entry->memdesc)) {
+		ret = -ERANGE;
+		goto err_put;
+	}
+
+	*out_entry = entry;
+	return 0;
+err_put:
+	kgsl_mem_entry_put(entry);
+	return ret;
+}
+
+static unsigned long
+kgsl_get_unmapped_area(struct file *file, unsigned long addr,
+			unsigned long len, unsigned long pgoff,
+			unsigned long flags)
+{
+	unsigned long ret = 0;
+	unsigned long vma_offset = pgoff << PAGE_SHIFT;
+	struct kgsl_device_private *dev_priv = file->private_data;
+	struct kgsl_process_private *private = dev_priv->process_priv;
+	struct kgsl_device *device = dev_priv->device;
+	struct kgsl_mem_entry *entry = NULL;
+	unsigned int align;
+	unsigned int retry = 0;
+
+	if (vma_offset == device->memstore.gpuaddr)
+		return get_unmapped_area(NULL, addr, len, pgoff, flags);
+
+	ret = get_mmap_entry(private, &entry, pgoff, len);
+	if (ret)
+		return ret;
+
+	if (!kgsl_memdesc_use_cpu_map(&entry->memdesc) || (flags & MAP_FIXED)) {
+		/*
+		 * If we're not going to use the same mapping on the gpu,
+		 * any address is fine.
+		 * For MAP_FIXED, hopefully the caller knows what they're doing,
+		 * but we may fail in mmap() if there is already something
+		 * at the virtual address chosen.
+		 */
+		ret = get_unmapped_area(NULL, addr, len, pgoff, flags);
+		goto put;
+	}
+	if (entry->memdesc.gpuaddr != 0) {
+		KGSL_MEM_INFO(device,
+				"pgoff %lx already mapped to gpuaddr %x\n",
+				pgoff, entry->memdesc.gpuaddr);
+		ret = -EBUSY;
+		goto put;
+	}
+
+	align = kgsl_memdesc_get_align(&entry->memdesc);
+	if (align >= ilog2(SZ_1M))
+		align = ilog2(SZ_1M);
+	else if (align >= ilog2(SZ_64K))
+		align = ilog2(SZ_64K);
+	else if (align <= PAGE_SHIFT)
+		align = 0;
+
+	if (align)
+		len += 1 << align;
+	do {
+		ret = get_unmapped_area(NULL, addr, len, pgoff, flags);
+		if (IS_ERR_VALUE(ret))
+			break;
+		if (align)
+			ret = ALIGN(ret, (1 << align));
+
+		/*make sure there isn't a GPU only mapping at this address */
+		if (kgsl_sharedmem_region_empty(private, ret, len))
+			break;
+
+		trace_kgsl_mem_unmapped_area_collision(entry, addr, len, ret);
+
+		/*
+		 * If we collided, bump the hint address so that
+		 * get_umapped_area knows to look somewhere else.
+		 */
+		addr = (addr == 0) ? ret + len : addr + len;
+
+		/*
+		 * The addr hint can be set by userspace to be near
+		 * the end of the address space. Make sure we search
+		 * the whole address space at least once by wrapping
+		 * back around once.
+		 */
+		if (!retry && (addr + len >= TASK_SIZE)) {
+			addr = 0;
+			retry = 1;
+		} else {
+			ret = -EBUSY;
+		}
+	} while (addr + len < TASK_SIZE);
+
+	if (IS_ERR_VALUE(ret))
+		KGSL_MEM_INFO(device,
+				"pid %d pgoff %lx len %ld failed error %ld\n",
+				private->pid, pgoff, len, ret);
+put:
+	kgsl_mem_entry_put(entry);
+	return ret;
+}
+
 static int kgsl_mmap(struct file *file, struct vm_area_struct *vma)
 {
+	unsigned int ret, cache;
 	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;
@@ -2254,31 +2868,54 @@
 	if (vma_offset == device->memstore.gpuaddr)
 		return kgsl_mmap_memstore(device, vma);
 
-	/* Find a chunk of GPU memory */
+	ret = get_mmap_entry(private, &entry, vma->vm_pgoff,
+				vma->vm_end - vma->vm_start);
+	if (ret)
+		return ret;
 
-	spin_lock(&private->mem_lock);
-	entry = kgsl_sharedmem_find(private, vma_offset);
+	if (kgsl_memdesc_use_cpu_map(&entry->memdesc)) {
+		entry->memdesc.gpuaddr = vma->vm_start;
 
-	if (entry)
-		kgsl_mem_entry_get(entry);
-
-	spin_unlock(&private->mem_lock);
-
-	if (entry == NULL)
-		return -EINVAL;
-
-	if (!entry->memdesc.ops ||
-		!entry->memdesc.ops->vmflags ||
-		!entry->memdesc.ops->vmfault)
-		return -EINVAL;
+		ret = kgsl_mmu_map(private->pagetable, &entry->memdesc,
+				   kgsl_memdesc_protflags(&entry->memdesc));
+		if (ret) {
+			kgsl_mem_entry_put(entry);
+			return ret;
+		}
+		kgsl_mem_entry_track_gpuaddr(private, entry);
+	}
 
 	vma->vm_flags |= entry->memdesc.ops->vmflags(&entry->memdesc);
 
 	vma->vm_private_data = entry;
-	vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
+
+	/* Determine user-side caching policy */
+
+	cache = kgsl_memdesc_get_cachemode(&entry->memdesc);
+
+	switch (cache) {
+	case KGSL_CACHEMODE_UNCACHED:
+		vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+		break;
+	case KGSL_CACHEMODE_WRITETHROUGH:
+		vma->vm_page_prot = pgprot_writethroughcache(vma->vm_page_prot);
+		break;
+	case KGSL_CACHEMODE_WRITEBACK:
+		vma->vm_page_prot = pgprot_writebackcache(vma->vm_page_prot);
+		break;
+	case KGSL_CACHEMODE_WRITECOMBINE:
+	default:
+		vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
+		break;
+	}
+
 	vma->vm_ops = &kgsl_gpumem_vm_ops;
 	vma->vm_file = file;
 
+	entry->memdesc.useraddr = vma->vm_start;
+
+	trace_kgsl_mem_mmap(entry);
+
 	return 0;
 }
 
@@ -2295,6 +2932,7 @@
 	.release = kgsl_release,
 	.open = kgsl_open,
 	.mmap = kgsl_mmap,
+	.get_unmapped_area = kgsl_get_unmapped_area,
 	.unlocked_ioctl = kgsl_ioctl,
 };
 
@@ -2302,6 +2940,8 @@
 	.process_mutex = __MUTEX_INITIALIZER(kgsl_driver.process_mutex),
 	.ptlock = __SPIN_LOCK_UNLOCKED(kgsl_driver.ptlock),
 	.devlock = __MUTEX_INITIALIZER(kgsl_driver.devlock),
+	.memfree_hist_mutex =
+		__MUTEX_INITIALIZER(kgsl_driver.memfree_hist_mutex),
 };
 EXPORT_SYMBOL(kgsl_driver);
 
@@ -2384,6 +3024,7 @@
 
 	kgsl_ion_client = msm_ion_client_create(UINT_MAX, KGSL_NAME);
 
+	/* Get starting physical address of device registers */
 	res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
 					   device->iomemname);
 	if (res == NULL) {
@@ -2401,6 +3042,33 @@
 	device->reg_phys = res->start;
 	device->reg_len = resource_size(res);
 
+	/*
+	 * Check if a shadermemname is defined, and then get shader memory
+	 * details including shader memory starting physical address
+	 * and shader memory length
+	 */
+	if (device->shadermemname != NULL) {
+		res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+						device->shadermemname);
+
+		if (res == NULL) {
+			KGSL_DRV_ERR(device,
+			"Shader memory: platform_get_resource_byname failed\n");
+		}
+
+		else {
+			device->shader_mem_phys = res->start;
+			device->shader_mem_len = resource_size(res);
+		}
+
+		if (!devm_request_mem_region(device->dev,
+					device->shader_mem_phys,
+					device->shader_mem_len,
+						device->name)) {
+			KGSL_DRV_ERR(device, "request_mem_region_failed\n");
+		}
+	}
+
 	if (!devm_request_mem_region(device->dev, device->reg_phys,
 				device->reg_len, device->name)) {
 		KGSL_DRV_ERR(device, "request_mem_region failed\n");
@@ -2521,6 +3189,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]));
 
@@ -2629,6 +3300,7 @@
 		kgsl_driver.class = NULL;
 	}
 
+	kgsl_memfree_hist_exit();
 	unregister_chrdev_region(kgsl_driver.major, KGSL_DEVICE_MAX);
 }
 
@@ -2700,6 +3372,9 @@
 			goto err;
 	}
 
+	if (kgsl_memfree_hist_init())
+		KGSL_CORE_ERR("failed to init memfree_hist");
+
 	return 0;
 
 err:
diff --git a/drivers/gpu/msm/kgsl.h b/drivers/gpu/msm/kgsl.h
index 2861117..72e7776 100644
--- a/drivers/gpu/msm/kgsl.h
+++ b/drivers/gpu/msm/kgsl.h
@@ -71,6 +71,23 @@
 #define KGSL_STATS_ADD(_size, _stat, _max) \
 	do { _stat += (_size); if (_stat > _max) _max = _stat; } while (0)
 
+
+#define KGSL_MEMFREE_HIST_SIZE	((int)(PAGE_SIZE * 2))
+
+struct kgsl_memfree_hist_elem {
+	unsigned int pid;
+	unsigned int gpuaddr;
+	unsigned int size;
+	unsigned int flags;
+};
+
+struct kgsl_memfree_hist {
+	void *base_hist_rb;
+	unsigned int size;
+	struct kgsl_memfree_hist_elem *wptr;
+};
+
+
 struct kgsl_device;
 
 struct kgsl_driver {
@@ -98,6 +115,9 @@
 
 	void *ptpool;
 
+	struct mutex memfree_hist_mutex;
+	struct kgsl_memfree_hist memfree_hist;
+
 	struct {
 		unsigned int vmalloc;
 		unsigned int vmalloc_max;
@@ -124,21 +144,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; /* 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 */
@@ -161,6 +185,7 @@
 	int flags;
 	void *priv_data;
 	struct rb_node node;
+	unsigned int id;
 	unsigned int context_id;
 	/* back pointer to private structure under whose context this
 	* allocation is made */
@@ -217,6 +242,10 @@
 static inline int kgsl_gpuaddr_in_memdesc(const struct kgsl_memdesc *memdesc,
 				unsigned int gpuaddr, unsigned int size)
 {
+	/* don't overflow */
+	if ((gpuaddr + size) < gpuaddr)
+		return 0;
+
 	if (gpuaddr >= memdesc->gpuaddr &&
 	    ((gpuaddr + size) <= (memdesc->gpuaddr + memdesc->size))) {
 		return 1;
diff --git a/drivers/gpu/msm/kgsl_debugfs.c b/drivers/gpu/msm/kgsl_debugfs.c
index b49c260..3bc107f 100644
--- a/drivers/gpu/msm/kgsl_debugfs.c
+++ b/drivers/gpu/msm/kgsl_debugfs.c
@@ -106,6 +106,52 @@
 KGSL_DEBUGFS_LOG(mem_log);
 KGSL_DEBUGFS_LOG(pwr_log);
 
+static int memfree_hist_print(struct seq_file *s, void *unused)
+{
+	void *base = kgsl_driver.memfree_hist.base_hist_rb;
+
+	struct kgsl_memfree_hist_elem *wptr = kgsl_driver.memfree_hist.wptr;
+	struct kgsl_memfree_hist_elem *p;
+	char str[16];
+
+	seq_printf(s, "%8s %8s %8s %11s\n",
+			"pid", "gpuaddr", "size", "flags");
+
+	mutex_lock(&kgsl_driver.memfree_hist_mutex);
+	p = wptr;
+	for (;;) {
+		kgsl_get_memory_usage(str, sizeof(str), p->flags);
+		/*
+		 * if the ring buffer is not filled up yet
+		 * all its empty elems have size==0
+		 * just skip them ...
+		*/
+		if (p->size)
+			seq_printf(s, "%8d %08x %8d %11s\n",
+				p->pid, p->gpuaddr, p->size, str);
+		p++;
+		if ((void *)p >= base + kgsl_driver.memfree_hist.size)
+			p = (struct kgsl_memfree_hist_elem *) base;
+
+		if (p == kgsl_driver.memfree_hist.wptr)
+			break;
+	}
+	mutex_unlock(&kgsl_driver.memfree_hist_mutex);
+	return 0;
+}
+
+static int memfree_hist_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, memfree_hist_print, inode->i_private);
+}
+
+static const struct file_operations memfree_hist_fops = {
+	.open = memfree_hist_open,
+	.read = seq_read,
+	.llseek = seq_lseek,
+	.release = single_release,
+};
+
 void kgsl_device_debugfs_init(struct kgsl_device *device)
 {
 	if (kgsl_debugfs_dir && !IS_ERR(kgsl_debugfs_dir))
@@ -131,6 +177,8 @@
 				&mem_log_fops);
 	debugfs_create_file("log_level_pwr", 0644, device->d_debugfs, device,
 				&pwr_log_fops);
+	debugfs_create_file("memfree_history", 0444, device->d_debugfs, device,
+				&memfree_hist_fops);
 
 	/* Create postmortem dump control files */
 
@@ -163,44 +211,80 @@
 	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 char get_cacheflag(const struct kgsl_memdesc *m)
+{
+	static const char table[] = {
+		[KGSL_CACHEMODE_WRITECOMBINE] = '-',
+		[KGSL_CACHEMODE_UNCACHED] = 'u',
+		[KGSL_CACHEMODE_WRITEBACK] = 'b',
+		[KGSL_CACHEMODE_WRITETHROUGH] = 't',
+	};
+	return table[kgsl_memdesc_get_cachemode(m)];
+}
+
+static void print_mem_entry(struct seq_file *s, struct kgsl_mem_entry *entry)
+{
+	char flags[6];
+	char usage[16];
+	struct kgsl_memdesc *m = &entry->memdesc;
+
+	flags[0] = kgsl_memdesc_is_global(m) ?  'g' : '-';
+	flags[1] = m->flags & KGSL_MEMFLAGS_GPUREADONLY ? 'r' : '-';
+	flags[2] = get_alignflag(m);
+	flags[3] = get_cacheflag(m);
+	flags[4] = kgsl_memdesc_use_cpu_map(m) ? 'p' : '-';
+	flags[5] = '\0';
+
+	kgsl_get_memory_usage(usage, sizeof(usage), m->flags);
+
+	seq_printf(s, "%08x %8d %5d %5s %10s %16s %5d\n",
+			m->gpuaddr, m->size, entry->id, flags,
+			memtype_str(entry->memtype), usage, m->sglen);
+}
+
 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];
-	unsigned int align;
+	int next = 0;
 
+	seq_printf(s, "%8s %8s %5s %5s %10s %16s %5s\n",
+		   "gpuaddr", "size", "id", "flags", "type", "usage", "sglen");
+
+	/* print all entries with a GPU address */
 	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_MEMFLAGS_GLOBAL ?  'g' : '-';
-		flags[1] = m->priv & KGSL_MEMFLAGS_GPUREADONLY ? 'r' : '-';
-
-		align = (m->priv & KGSL_MEMALIGN_MASK) >> KGSL_MEMALIGN_SHIFT;
-		if (align >= ilog2(SZ_1M))
-			flags[2] = 'L';
-		else if (align >= ilog2(SZ_64K))
-			flags[2] = 'l';
-		else
-			flags[2] = '-';
-
-		flags[3] = '\0';
-
-		kgsl_get_memory_usage(usage, sizeof(usage), m->priv);
-
-		seq_printf(s, "%08x %8d %5s %10s %16s %5d\n",
-			   m->gpuaddr, m->size, flags,
-			   memtype_str(entry->memtype), usage, m->sglen);
+		print_mem_entry(s, entry);
 	}
+
 	spin_unlock(&private->mem_lock);
+
+	/* now print all the unbound entries */
+	while (1) {
+		rcu_read_lock();
+		entry = idr_get_next(&private->mem_idr, &next);
+		rcu_read_unlock();
+
+		if (entry == NULL)
+			break;
+		if (entry->memdesc.gpuaddr == 0)
+			print_mem_entry(s, entry);
+		next++;
+	}
+
 	return 0;
 }
 
diff --git a/drivers/gpu/msm/kgsl_device.h b/drivers/gpu/msm/kgsl_device.h
index 4394118..ce3820c 100644
--- a/drivers/gpu/msm/kgsl_device.h
+++ b/drivers/gpu/msm/kgsl_device.h
@@ -58,6 +58,7 @@
 struct kgsl_device_private;
 struct kgsl_context;
 struct kgsl_power_stats;
+struct kgsl_event;
 
 struct kgsl_functable {
 	/* Mandatory functions - these functions must be implemented
@@ -112,6 +113,8 @@
 		enum kgsl_property_type type, void *value,
 		unsigned int sizebytes);
 	int (*postmortem_dump) (struct kgsl_device *device, int manual);
+	void (*next_event)(struct kgsl_device *device,
+		struct kgsl_event *event);
 };
 
 /* MH register values */
@@ -140,11 +143,27 @@
 	unsigned int ver_minor;
 	uint32_t flags;
 	enum kgsl_deviceid id;
+
+	/* Starting physical address for GPU registers */
 	unsigned long reg_phys;
+
+	/* Starting Kernel virtual address for GPU registers */
 	void *reg_virt;
+
+	/* Total memory size for all GPU registers */
 	unsigned int reg_len;
+
+	/* Kernel virtual address for GPU shader memory */
+	void *shader_mem_virt;
+
+	/* Starting physical address for GPU shader memory */
+	unsigned long shader_mem_phys;
+
+	/* GPU shader memory size */
+	unsigned int shader_mem_len;
 	struct kgsl_memdesc memstore;
 	const char *iomemname;
+	const char *shadermemname;
 
 	struct kgsl_mh mh;
 	struct kgsl_mmu mmu;
@@ -235,7 +254,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.
@@ -248,6 +268,7 @@
 	pid_t pid;
 	spinlock_t mem_lock;
 	struct rb_root mem_rb;
+	struct idr mem_idr;
 	struct kgsl_pagetable *pagetable;
 	struct list_head list;
 	struct kobject kobj;
diff --git a/drivers/gpu/msm/kgsl_drm.c b/drivers/gpu/msm/kgsl_drm.c
index 870a7d7..98c7434 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"
@@ -27,7 +28,7 @@
 #define DRIVER_AUTHOR           "Qualcomm"
 #define DRIVER_NAME             "kgsl"
 #define DRIVER_DESC             "KGSL DRM"
-#define DRIVER_DATE             "20100127"
+#define DRIVER_DATE             "20121107"
 
 #define DRIVER_MAJOR            2
 #define DRIVER_MINOR            1
@@ -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;
@@ -585,6 +635,43 @@
 }
 
 int
+kgsl_gem_get_ion_fd_ioctl(struct drm_device *dev, void *data,
+			struct drm_file *file_priv)
+{
+	struct drm_kgsl_gem_get_ion_fd *args = data;
+	struct drm_gem_object *obj;
+	struct drm_kgsl_gem_object *priv;
+	int ret = 0;
+
+	obj = drm_gem_object_lookup(dev, file_priv, args->handle);
+
+	if (obj == NULL) {
+		DRM_ERROR("Invalid GEM handle %x\n", args->handle);
+		return -EBADF;
+	}
+
+	mutex_lock(&dev->struct_mutex);
+	priv = obj->driver_private;
+
+	if (TYPE_IS_FD(priv->type))
+		ret = -EINVAL;
+	else {
+		if (priv->ion_handle) {
+			args->ion_fd = ion_share_dma_buf(
+			kgsl_drm_ion_phys_client, priv->ion_handle);
+		} else {
+			DRM_ERROR("GEM object has no ion memory allocated.\n");
+			ret = -EINVAL;
+		}
+	}
+
+	drm_gem_object_unreference(obj);
+	mutex_unlock(&dev->struct_mutex);
+
+	return ret;
+}
+
+int
 kgsl_gem_setmemtype_ioctl(struct drm_device *dev, void *data,
 			struct drm_file *file_priv)
 {
@@ -1432,6 +1519,7 @@
 	DRM_IOCTL_DEF_DRV(KGSL_GEM_ALLOC, kgsl_gem_alloc_ioctl, 0),
 	DRM_IOCTL_DEF_DRV(KGSL_GEM_MMAP, kgsl_gem_mmap_ioctl, 0),
 	DRM_IOCTL_DEF_DRV(KGSL_GEM_GET_BUFINFO, kgsl_gem_get_bufinfo_ioctl, 0),
+	DRM_IOCTL_DEF_DRV(KGSL_GEM_GET_ION_FD, kgsl_gem_get_ion_fd_ioctl, 0),
 	DRM_IOCTL_DEF_DRV(KGSL_GEM_SET_BUFCOUNT,
 		      kgsl_gem_set_bufcount_ioctl, 0),
 	DRM_IOCTL_DEF_DRV(KGSL_GEM_SET_ACTIVE, kgsl_gem_set_active_ioctl, 0),
@@ -1445,6 +1533,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 +1554,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,11 +1583,24 @@
 		gem_buf_fence[i].fence_id = ENTRY_EMPTY;
 	}
 
+	/* Create ION Client */
+	kgsl_drm_ion_phys_client = msm_ion_client_create(
+			ION_HEAP_CARVEOUT_MASK, "kgsl_drm");
+	if (!kgsl_drm_ion_phys_client) {
+		DRM_ERROR("Unable to create ION client\n");
+		return -ENOMEM;
+	}
+
 	return drm_platform_init(&driver, dev);
 }
 
 void kgsl_drm_exit(void)
 {
 	kgsl_drm_inited = DRM_KGSL_NOT_INITED;
+
+	if (kgsl_drm_ion_phys_client)
+		ion_client_destroy(kgsl_drm_ion_phys_client);
+	kgsl_drm_ion_phys_client = NULL;
+
 	drm_platform_exit(&driver, driver.kdriver.platform_device);
 }
diff --git a/drivers/gpu/msm/kgsl_iommu.c b/drivers/gpu/msm/kgsl_iommu.c
index 62108f2..1bccd4d 100644
--- a/drivers/gpu/msm/kgsl_iommu.c
+++ b/drivers/gpu/msm/kgsl_iommu.c
@@ -702,6 +702,70 @@
 	}
 }
 
+/*
+ * kgsl_iommu_setup_regs - map iommu registers into a pagetable
+ * @mmu: Pointer to mmu structure
+ * @pt: the pagetable
+ *
+ * To do pagetable switches from the GPU command stream, the IOMMU
+ * registers need to be mapped into the GPU's pagetable. This function
+ * is used differently on different targets. On 8960, the registers
+ * are mapped into every pagetable during kgsl_setup_pt(). On
+ * all other targets, the registers are mapped only into the second
+ * context bank.
+ *
+ * Return - 0 on success else error code
+ */
+static int kgsl_iommu_setup_regs(struct kgsl_mmu *mmu,
+				    struct kgsl_pagetable *pt)
+{
+	int status;
+	int i = 0;
+	struct kgsl_iommu *iommu = mmu->priv;
+
+	if (!msm_soc_version_supports_iommu_v1())
+		return 0;
+
+	for (i = 0; i < iommu->unit_count; i++) {
+		iommu->iommu_units[i].reg_map.priv |= KGSL_MEMDESC_GLOBAL;
+		status = kgsl_mmu_map(pt,
+				&(iommu->iommu_units[i].reg_map),
+				GSL_PT_PAGE_RV | GSL_PT_PAGE_WV);
+		if (status) {
+			iommu->iommu_units[i].reg_map.priv &=
+				~KGSL_MEMDESC_GLOBAL;
+			goto err;
+		}
+	}
+	return 0;
+err:
+	for (i--; i >= 0; i--) {
+		kgsl_mmu_unmap(pt,
+				&(iommu->iommu_units[i].reg_map));
+		iommu->iommu_units[i].reg_map.priv &= ~KGSL_MEMDESC_GLOBAL;
+	}
+	return status;
+}
+
+/*
+ * kgsl_iommu_cleanup_regs - unmap iommu registers from a pagetable
+ * @mmu: Pointer to mmu structure
+ * @pt: the pagetable
+ *
+ * Removes mappings created by kgsl_iommu_setup_regs().
+ *
+ * Return - 0 on success else error code
+ */
+static void kgsl_iommu_cleanup_regs(struct kgsl_mmu *mmu,
+					struct kgsl_pagetable *pt)
+{
+	struct kgsl_iommu *iommu = mmu->priv;
+	int i;
+	for (i = 0; i < iommu->unit_count; i++)
+		kgsl_mmu_unmap(pt, &(iommu->iommu_units[i].reg_map));
+}
+
+
 static int kgsl_iommu_init(struct kgsl_mmu *mmu)
 {
 	/*
@@ -744,6 +808,15 @@
 				KGSL_IOMMU_SETSTATE_NOP_OFFSET,
 				cp_nop_packet(1));
 
+	if (cpu_is_msm8960()) {
+		/*
+		 * 8960 doesn't have a second context bank, so the IOMMU
+		 * registers must be mapped into every pagetable.
+		 */
+		iommu_ops.mmu_setup_pt = kgsl_iommu_setup_regs;
+		iommu_ops.mmu_cleanup_pt = kgsl_iommu_cleanup_regs;
+	}
+
 	dev_info(mmu->device->dev, "|%s| MMU type set for device is IOMMU\n",
 			__func__);
 done:
@@ -766,9 +839,6 @@
 static int kgsl_iommu_setup_defaultpagetable(struct kgsl_mmu *mmu)
 {
 	int status = 0;
-	int i = 0;
-	struct kgsl_iommu *iommu = mmu->priv;
-	struct kgsl_pagetable *pagetable = NULL;
 
 	/* If chip is not 8960 then we use the 2nd context bank for pagetable
 	 * switching on the 3D side for which a separate table is allocated */
@@ -779,6 +849,9 @@
 			status = -ENOMEM;
 			goto err;
 		}
+		status = kgsl_iommu_setup_regs(mmu, mmu->priv_bank_table);
+		if (status)
+			goto err;
 	}
 	mmu->defaultpagetable = kgsl_mmu_getpagetable(KGSL_MMU_GLOBAL_PT);
 	/* Return error if the default pagetable doesn't exist */
@@ -786,31 +859,10 @@
 		status = -ENOMEM;
 		goto err;
 	}
-	pagetable = mmu->priv_bank_table ? mmu->priv_bank_table :
-				mmu->defaultpagetable;
-	/* Map the IOMMU regsiters to only defaultpagetable */
-	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;
-			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;
-				goto err;
-			}
-		}
-	}
 	return status;
 err:
-	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;
-	}
 	if (mmu->priv_bank_table) {
+		kgsl_iommu_cleanup_regs(mmu, mmu->priv_bank_table);
 		kgsl_mmu_putpagetable(mmu->priv_bank_table);
 		mmu->priv_bank_table = NULL;
 	}
@@ -839,10 +891,12 @@
 	 * a225, hence we still keep the MMU active on 8960 */
 	if (cpu_is_msm8960()) {
 		struct kgsl_mh *mh = &(mmu->device->mh);
+		BUG_ON(iommu->iommu_units[0].reg_map.gpuaddr != 0 &&
+			mh->mpu_base > iommu->iommu_units[0].reg_map.gpuaddr);
 		kgsl_regwrite(mmu->device, MH_MMU_CONFIG, 0x00000001);
+
 		kgsl_regwrite(mmu->device, MH_MMU_MPU_END,
-			mh->mpu_base +
-			iommu->iommu_units[0].reg_map.gpuaddr);
+			mh->mpu_base + mh->mpu_range);
 	} else {
 		kgsl_regwrite(mmu->device, MH_MMU_CONFIG, 0x00000000);
 	}
@@ -915,14 +969,12 @@
 			"with err: %d\n", iommu_pt->domain, gpuaddr,
 			range, ret);
 
-#ifdef CONFIG_KGSL_PER_PROCESS_PAGE_TABLE
 	/*
 	 * Flushing only required if per process pagetables are used. With
 	 * global case, flushing will happen inside iommu_map function
 	 */
-	if (!ret && msm_soc_version_supports_iommu_v1())
+	if (!ret && kgsl_mmu_is_perprocess())
 		*tlb_flags = UINT_MAX;
-#endif
 	return 0;
 }
 
@@ -1003,22 +1055,23 @@
 {
 	struct kgsl_iommu *iommu = mmu->priv;
 	int i;
-	for (i = 0; i < iommu->unit_count; i++) {
-		struct kgsl_pagetable *pagetable = (mmu->priv_bank_table ?
-			mmu->priv_bank_table : mmu->defaultpagetable);
-		if (iommu->iommu_units[i].reg_map.gpuaddr)
-			kgsl_mmu_unmap(pagetable,
-			&(iommu->iommu_units[i].reg_map));
-		if (iommu->iommu_units[i].reg_map.hostptr)
-			iounmap(iommu->iommu_units[i].reg_map.hostptr);
-		kgsl_sg_free(iommu->iommu_units[i].reg_map.sg,
-				iommu->iommu_units[i].reg_map.sglen);
+
+	if (mmu->priv_bank_table != NULL) {
+		kgsl_iommu_cleanup_regs(mmu, mmu->priv_bank_table);
+		kgsl_mmu_putpagetable(mmu->priv_bank_table);
 	}
 
-	if (mmu->priv_bank_table)
-		kgsl_mmu_putpagetable(mmu->priv_bank_table);
-	if (mmu->defaultpagetable)
+	if (mmu->defaultpagetable != NULL)
 		kgsl_mmu_putpagetable(mmu->defaultpagetable);
+
+	for (i = 0; i < iommu->unit_count; i++) {
+		struct kgsl_memdesc *reg_map = &iommu->iommu_units[i].reg_map;
+
+		if (reg_map->hostptr)
+			iounmap(reg_map->hostptr);
+		kgsl_sg_free(reg_map->sg, reg_map->sglen);
+	}
+
 	kfree(iommu);
 
 	return 0;
@@ -1149,6 +1202,9 @@
 	.mmu_get_num_iommu_units = kgsl_iommu_get_num_iommu_units,
 	.mmu_pt_equal = kgsl_iommu_pt_equal,
 	.mmu_get_pt_base_addr = kgsl_iommu_get_pt_base_addr,
+	/* These callbacks will be set on some chipsets */
+	.mmu_setup_pt = NULL,
+	.mmu_cleanup_pt = NULL,
 };
 
 struct kgsl_mmu_pt_ops iommu_pt_ops = {
diff --git a/drivers/gpu/msm/kgsl_mmu.c b/drivers/gpu/msm/kgsl_mmu.c
index dbb88ee..533f02f 100644
--- a/drivers/gpu/msm/kgsl_mmu.c
+++ b/drivers/gpu/msm/kgsl_mmu.c
@@ -25,9 +25,7 @@
 #include "kgsl_mmu.h"
 #include "kgsl_device.h"
 #include "kgsl_sharedmem.h"
-
-#define KGSL_MMU_ALIGN_SHIFT    13
-#define KGSL_MMU_ALIGN_MASK     (~((1 << KGSL_MMU_ALIGN_SHIFT) - 1))
+#include "adreno.h"
 
 static enum kgsl_mmutype kgsl_mmu_type;
 
@@ -36,17 +34,18 @@
 static int kgsl_cleanup_pt(struct kgsl_pagetable *pt)
 {
 	int i;
-	/* For IOMMU only unmap the global structures to global pt */
-	if ((KGSL_MMU_TYPE_NONE != kgsl_mmu_type) &&
-		(KGSL_MMU_TYPE_IOMMU == kgsl_mmu_type) &&
-		(KGSL_MMU_GLOBAL_PT !=  pt->name) &&
-		(KGSL_MMU_PRIV_BANK_TABLE_NAME !=  pt->name))
-		return 0;
+	struct kgsl_device *device;
+
 	for (i = 0; i < KGSL_DEVICE_MAX; i++) {
-		struct kgsl_device *device = kgsl_driver.devp[i];
+		device = kgsl_driver.devp[i];
 		if (device)
 			device->ftbl->cleanup_pt(device, pt);
 	}
+	/* Only the 3d device needs mmu specific pt entries */
+	device = kgsl_driver.devp[KGSL_DEVICE_3D0];
+	if (device->mmu.mmu_ops->mmu_cleanup_pt != NULL)
+		device->mmu.mmu_ops->mmu_cleanup_pt(&device->mmu, pt);
+
 	return 0;
 }
 
@@ -55,21 +54,23 @@
 {
 	int i = 0;
 	int status = 0;
+	struct kgsl_device *device;
 
-	/* For IOMMU only map the global structures to global pt */
-	if ((KGSL_MMU_TYPE_NONE != kgsl_mmu_type) &&
-		(KGSL_MMU_TYPE_IOMMU == kgsl_mmu_type) &&
-		(KGSL_MMU_GLOBAL_PT !=  pt->name) &&
-		(KGSL_MMU_PRIV_BANK_TABLE_NAME !=  pt->name))
-		return 0;
 	for (i = 0; i < KGSL_DEVICE_MAX; i++) {
-		struct kgsl_device *device = kgsl_driver.devp[i];
+		device = kgsl_driver.devp[i];
 		if (device) {
 			status = device->ftbl->setup_pt(device, pt);
 			if (status)
 				goto error_pt;
 		}
 	}
+	/* Only the 3d device needs mmu specific pt entries */
+	device = kgsl_driver.devp[KGSL_DEVICE_3D0];
+	if (device->mmu.mmu_ops->mmu_setup_pt != NULL) {
+		status = device->mmu.mmu_ops->mmu_setup_pt(&device->mmu, pt);
+		if (status)
+			goto error_pt;
+	}
 	return status;
 error_pt:
 	while (i >= 0) {
@@ -309,22 +310,6 @@
 	return ret;
 }
 
-unsigned int kgsl_mmu_get_ptsize(void)
-{
-	/*
-	 * For IOMMU, we could do up to 4G virtual range if we wanted to, but
-	 * it makes more sense to return a smaller range and leave the rest of
-	 * the virtual range for future improvements
-	 */
-
-	if (KGSL_MMU_TYPE_GPU == kgsl_mmu_type)
-		return CONFIG_MSM_KGSL_PAGE_TABLE_SIZE;
-	else if (KGSL_MMU_TYPE_IOMMU == kgsl_mmu_type)
-		return SZ_2G - KGSL_PAGETABLE_BASE;
-	else
-		return 0;
-}
-
 int
 kgsl_mmu_get_ptname_from_ptbase(struct kgsl_mmu *mmu, unsigned int pt_base)
 {
@@ -459,10 +444,10 @@
 	if ((KGSL_MMU_TYPE_IOMMU == kgsl_mmu_get_mmutype()) &&
 		((KGSL_MMU_GLOBAL_PT == name) ||
 		(KGSL_MMU_PRIV_BANK_TABLE_NAME == name))) {
-		pagetable->kgsl_pool = gen_pool_create(PAGE_SHIFT, -1);
+		pagetable->kgsl_pool = gen_pool_create(ilog2(SZ_8K), -1);
 		if (pagetable->kgsl_pool == NULL) {
 			KGSL_CORE_ERR("gen_pool_create(%d) failed\n",
-					KGSL_MMU_ALIGN_SHIFT);
+					ilog2(SZ_8K));
 			goto err_alloc;
 		}
 		if (gen_pool_add(pagetable->kgsl_pool,
@@ -473,14 +458,14 @@
 		}
 	}
 
-	pagetable->pool = gen_pool_create(KGSL_MMU_ALIGN_SHIFT, -1);
+	pagetable->pool = gen_pool_create(PAGE_SHIFT, -1);
 	if (pagetable->pool == NULL) {
 		KGSL_CORE_ERR("gen_pool_create(%d) failed\n",
-			      KGSL_MMU_ALIGN_SHIFT);
+			      PAGE_SHIFT);
 		goto err_kgsl_pool;
 	}
 
-	if (gen_pool_add(pagetable->pool, KGSL_PAGETABLE_BASE,
+	if (gen_pool_add(pagetable->pool, kgsl_mmu_get_base_addr(),
 				ptsize, -1)) {
 		KGSL_CORE_ERR("gen_pool_add failed\n");
 		goto err_pool;
@@ -528,11 +513,7 @@
 	if (KGSL_MMU_TYPE_NONE == kgsl_mmu_type)
 		return (void *)(-1);
 
-#ifndef CONFIG_KGSL_PER_PROCESS_PAGE_TABLE
-	name = KGSL_MMU_GLOBAL_PT;
-#endif
-	/* We presently do not support per-process for IOMMU-v2 */
-	if (!msm_soc_version_supports_iommu_v1())
+	if (!kgsl_mmu_is_perprocess())
 		name = KGSL_MMU_GLOBAL_PT;
 
 	pt = kgsl_get_pagetable(name);
@@ -553,6 +534,12 @@
 			uint32_t flags)
 {
 	struct kgsl_device *device = mmu->device;
+	struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
+
+	if (!(flags & (KGSL_MMUFLAGS_TLBFLUSH | KGSL_MMUFLAGS_PTUPDATE))
+		&& !adreno_is_a2xx(adreno_dev))
+		return;
+
 	if (KGSL_MMU_TYPE_NONE == kgsl_mmu_type)
 		return;
 	else if (device->ftbl->setstate)
@@ -589,22 +576,13 @@
 	 */
 }
 
-static inline struct gen_pool *
-_get_pool(struct kgsl_pagetable *pagetable, unsigned int flags)
-{
-	if (pagetable->kgsl_pool &&
-		(KGSL_MEMFLAGS_GLOBAL & flags))
-		return pagetable->kgsl_pool;
-	return pagetable->pool;
-}
-
 int
 kgsl_mmu_map(struct kgsl_pagetable *pagetable,
 				struct kgsl_memdesc *memdesc,
 				unsigned int protflags)
 {
 	int ret;
-	struct gen_pool *pool;
+	struct gen_pool *pool = NULL;
 	int size;
 	int page_align = ilog2(PAGE_SIZE);
 
@@ -628,29 +606,52 @@
 
 	size = kgsl_sg_size(memdesc->sg, memdesc->sglen);
 
-	/* Allocate from kgsl pool if it exists for global mappings */
-	pool = _get_pool(pagetable, memdesc->priv);
+	pool = pagetable->pool;
 
-	/* 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() &&
-	    (memdesc->priv & KGSL_MEMALIGN_MASK)) {
-		page_align = (memdesc->priv & KGSL_MEMALIGN_MASK)
-				>> KGSL_MEMALIGN_SHIFT;
+	if (KGSL_MMU_TYPE_IOMMU == kgsl_mmu_get_mmutype()) {
+		/* 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_memdesc_get_align(memdesc) > 0)
+			page_align = kgsl_memdesc_get_align(memdesc);
+		if (kgsl_memdesc_is_global(memdesc)) {
+			/*
+			 * Only the default pagetable has a kgsl_pool, and
+			 * it is responsible for creating the mapping for
+			 * each global buffer. The mapping will be reused
+			 * in all other pagetables and it must already exist
+			 * when we're creating other pagetables which do not
+			 * have a kgsl_pool.
+			 */
+			pool = pagetable->kgsl_pool;
+			if (pool == NULL && memdesc->gpuaddr == 0) {
+				KGSL_CORE_ERR(
+				  "No address for global mapping into pt %d\n",
+				  pagetable->name);
+				return -EINVAL;
+			}
+		} else if (kgsl_memdesc_use_cpu_map(memdesc)) {
+			if (memdesc->gpuaddr == 0)
+				return -EINVAL;
+			pool = NULL;
+		}
 	}
-	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,
-			(pool == pagetable->kgsl_pool) ?
-			"kgsl_pool" : "general_pool");
-		KGSL_CORE_ERR(" [%d] allocated=%d, entries=%d\n",
-				pagetable->name, pagetable->stats.mapped,
-				pagetable->stats.entries);
-		return -ENOMEM;
+	if (pool) {
+		memdesc->gpuaddr = gen_pool_alloc_aligned(pool, size,
+							  page_align);
+		if (memdesc->gpuaddr == 0) {
+			KGSL_CORE_ERR("gen_pool_alloc(%d) failed, pool: %s\n",
+					size,
+					(pool == pagetable->kgsl_pool) ?
+					"kgsl_pool" : "general_pool");
+			KGSL_CORE_ERR(" [%d] allocated=%d, entries=%d\n",
+					pagetable->name,
+					pagetable->stats.mapped,
+					pagetable->stats.entries);
+			return -ENOMEM;
+		}
 	}
 
 	if (KGSL_MMU_TYPE_IOMMU != kgsl_mmu_get_mmutype())
@@ -677,7 +678,8 @@
 
 err_free_gpuaddr:
 	spin_unlock(&pagetable->lock);
-	gen_pool_free(pool, memdesc->gpuaddr, size);
+	if (pool)
+		gen_pool_free(pool, memdesc->gpuaddr, size);
 	memdesc->gpuaddr = 0;
 	return ret;
 }
@@ -712,14 +714,22 @@
 
 	spin_unlock(&pagetable->lock);
 
-	pool = _get_pool(pagetable, memdesc->priv);
-	gen_pool_free(pool, memdesc->gpuaddr, size);
+	pool = pagetable->pool;
+
+	if (KGSL_MMU_TYPE_IOMMU == kgsl_mmu_get_mmutype()) {
+		if (kgsl_memdesc_is_global(memdesc))
+			pool = pagetable->kgsl_pool;
+		else if (kgsl_memdesc_use_cpu_map(memdesc))
+			pool = NULL;
+	}
+	if (pool)
+		gen_pool_free(pool, memdesc->gpuaddr, size);
 
 	/*
 	 * Don't clear the gpuaddr on global mappings because they
 	 * may be in use by other pagetables
 	 */
-	if (!(memdesc->priv & KGSL_MEMFLAGS_GLOBAL))
+	if (!kgsl_memdesc_is_global(memdesc))
 		memdesc->gpuaddr = 0;
 	return 0;
 }
@@ -740,8 +750,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)
@@ -848,8 +857,13 @@
 {
 	if (KGSL_MMU_TYPE_NONE == kgsl_mmu_type)
 		return 1;
-	return ((gpuaddr >= KGSL_PAGETABLE_BASE) &&
-		(gpuaddr < (KGSL_PAGETABLE_BASE + kgsl_mmu_get_ptsize())));
+	if (gpuaddr >= kgsl_mmu_get_base_addr() &&
+		gpuaddr < kgsl_mmu_get_base_addr() + kgsl_mmu_get_ptsize())
+		return 1;
+	if (kgsl_mmu_get_mmutype() == KGSL_MMU_TYPE_IOMMU
+		&& kgsl_mmu_is_perprocess())
+		return (gpuaddr > 0 && gpuaddr < TASK_SIZE);
+	return 0;
 }
 EXPORT_SYMBOL(kgsl_mmu_gpuaddr_in_range);
 
diff --git a/drivers/gpu/msm/kgsl_mmu.h b/drivers/gpu/msm/kgsl_mmu.h
index b8b9149..c8d637e 100644
--- a/drivers/gpu/msm/kgsl_mmu.h
+++ b/drivers/gpu/msm/kgsl_mmu.h
@@ -13,16 +13,16 @@
 #ifndef __KGSL_MMU_H
 #define __KGSL_MMU_H
 
-/*
- * These defines control the split between ttbr1 and ttbr0 pagetables of IOMMU
- * and what ranges of memory we map to them
- */
-#define KGSL_IOMMU_GLOBAL_MEM_BASE	0xC0000000
-#define KGSL_IOMMU_GLOBAL_MEM_SIZE	SZ_4M
-#define KGSL_IOMMU_TTBR1_SPLIT		2
+#include <mach/iommu.h>
 
-#define KGSL_MMU_ALIGN_SHIFT    13
-#define KGSL_MMU_ALIGN_MASK     (~((1 << KGSL_MMU_ALIGN_SHIFT) - 1))
+/*
+ * These defines control the address range for allocations that
+ * are mapped into all pagetables.
+ */
+#define KGSL_IOMMU_GLOBAL_MEM_BASE	0xf8000000
+#define KGSL_IOMMU_GLOBAL_MEM_SIZE	SZ_4M
+
+#define KGSL_MMU_ALIGN_MASK     (~((1 << PAGE_SHIFT) - 1))
 
 /* Identifier for the global page table */
 /* Per process page tables will probably pass in the thread group
@@ -148,6 +148,10 @@
 	unsigned int (*mmu_get_pt_base_addr)
 			(struct kgsl_mmu *mmu,
 			struct kgsl_pagetable *pt);
+	int (*mmu_setup_pt) (struct kgsl_mmu *mmu,
+			struct kgsl_pagetable *pt);
+	void (*mmu_cleanup_pt) (struct kgsl_mmu *mmu,
+			struct kgsl_pagetable *pt);
 };
 
 struct kgsl_mmu_pt_ops {
@@ -209,7 +213,6 @@
 int kgsl_mmu_enabled(void);
 void kgsl_mmu_set_mmutype(char *mmutype);
 enum kgsl_mmutype kgsl_mmu_get_mmutype(void);
-unsigned int kgsl_mmu_get_ptsize(void);
 int kgsl_mmu_gpuaddr_in_range(unsigned int gpuaddr);
 
 /*
@@ -321,4 +324,78 @@
 		return 0;
 }
 
+/*
+ * kgsl_mmu_is_perprocess() - Runtime check for per-process
+ * pagetables.
+ *
+ * Returns non-zero if per-process pagetables are enabled,
+ * 0 if not.
+ */
+#ifdef CONFIG_KGSL_PER_PROCESS_PAGE_TABLE
+static inline int kgsl_mmu_is_perprocess(void)
+{
+
+	/* We presently do not support per-process for IOMMU-v2 */
+	return (kgsl_mmu_get_mmutype() != KGSL_MMU_TYPE_IOMMU)
+		|| msm_soc_version_supports_iommu_v1();
+}
+#else
+static inline int kgsl_mmu_is_perprocess(void)
+{
+	return 0;
+}
+#endif
+
+/*
+ * kgsl_mmu_base_addr() - Get gpu virtual address base.
+ *
+ * Returns the start address of the allocatable gpu
+ * virtual address space. Other mappings that mirror
+ * the CPU address space are possible outside this range.
+ */
+static inline unsigned int kgsl_mmu_get_base_addr(void)
+{
+	if (KGSL_MMU_TYPE_GPU == kgsl_mmu_get_mmutype()
+		|| !kgsl_mmu_is_perprocess())
+		return KGSL_PAGETABLE_BASE;
+	/*
+	 * This is the start of the kernel address
+	 * space, so allocations from this range will
+	 * never conflict with userpace addresses
+	 */
+	return PAGE_OFFSET;
+}
+
+/*
+ * kgsl_mmu_get_ptsize() - Get gpu pagetable size
+ *
+ * Returns the usable size of the gpu allocatable
+ * address space.
+ */
+static inline unsigned int kgsl_mmu_get_ptsize(void)
+{
+	/*
+	 * For IOMMU per-process pagetables, the allocatable range
+	 * and the kernel global range must both be outside
+	 * the userspace address range. There is a 1Mb gap
+	 * between these address ranges to make overrun
+	 * detection easier.
+	 * For the shared pagetable case use 2GB and because
+	 * mirroring the CPU address space is not possible and
+	 * we're better off with extra room.
+	 */
+	enum kgsl_mmutype mmu_type = kgsl_mmu_get_mmutype();
+
+	if (KGSL_MMU_TYPE_GPU == mmu_type)
+		return CONFIG_MSM_KGSL_PAGE_TABLE_SIZE;
+	else if (KGSL_MMU_TYPE_IOMMU == mmu_type) {
+		if (kgsl_mmu_is_perprocess())
+			return KGSL_IOMMU_GLOBAL_MEM_BASE
+				- kgsl_mmu_get_base_addr() - SZ_1M;
+		else
+			return SZ_2G;
+	}
+	return 0;
+}
+
 #endif /* __KGSL_MMU_H */
diff --git a/drivers/gpu/msm/kgsl_pwrctrl.c b/drivers/gpu/msm/kgsl_pwrctrl.c
index 739dcb5..df44166 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
@@ -85,63 +86,296 @@
 	clkstats->start = ktime_get();
 }
 
+/*
+ * Given a requested power level do bounds checking on the constraints and
+ * return the nearest possible level
+ */
+
+static inline int _adjust_pwrlevel(struct kgsl_pwrctrl *pwr, int level)
+{
+	unsigned int max_pwrlevel = max_t(int, pwr->thermal_pwrlevel,
+		pwr->max_pwrlevel);
+
+	unsigned int min_pwrlevel = max_t(int, pwr->thermal_pwrlevel,
+		pwr->min_pwrlevel);
+
+	if (level < max_pwrlevel)
+		return max_pwrlevel;
+	if (level > min_pwrlevel)
+		return min_pwrlevel;
+
+	return level;
+}
+
 void kgsl_pwrctrl_pwrlevel_change(struct kgsl_device *device,
 				unsigned int new_level)
 {
 	struct kgsl_pwrctrl *pwr = &device->pwrctrl;
-	if (new_level < (pwr->num_pwrlevels - 1) &&
-		new_level >= pwr->thermal_pwrlevel &&
-		new_level != pwr->active_pwrlevel) {
-		struct kgsl_pwrlevel *pwrlevel = &pwr->pwrlevels[new_level];
-		int diff = new_level - pwr->active_pwrlevel;
-		int d = (diff > 0) ? 1 : -1;
-		int level = pwr->active_pwrlevel;
-		/* Update the clock stats */
-		update_clk_statistics(device, true);
-		/* Finally set active level */
-		pwr->active_pwrlevel = new_level;
-		if ((test_bit(KGSL_PWRFLAGS_CLK_ON, &pwr->power_flags)) ||
-			(device->state == KGSL_STATE_NAP)) {
-			/*
-			 * On some platforms, instability is caused on
-			 * changing clock freq when the core is busy.
-			 * Idle the gpu core before changing the clock freq.
-			 */
-			if (pwr->idle_needed == true)
-				device->ftbl->idle(device);
+	struct kgsl_pwrlevel *pwrlevel;
+	int delta;
 
-			/* Don't shift by more than one level at a time to
-			 * avoid glitches.
-			 */
-			while (level != new_level) {
-				level += d;
-				clk_set_rate(pwr->grp_clks[0],
-						pwr->pwrlevels[level].gpu_freq);
-			}
+	/* Adjust the power level to the current constraints */
+	new_level = _adjust_pwrlevel(pwr, new_level);
+
+	if (new_level == pwr->active_pwrlevel)
+		return;
+
+	delta = new_level < pwr->active_pwrlevel ? -1 : 1;
+
+	update_clk_statistics(device, true);
+
+	if (test_bit(KGSL_PWRFLAGS_CLK_ON, &pwr->power_flags) ||
+		(device->state == KGSL_STATE_NAP)) {
+
+		/*
+		 * On some platforms, instability is caused on
+		 * changing clock freq when the core is busy.
+		 * Idle the gpu core before changing the clock freq.
+		 */
+
+		if (pwr->idle_needed == true)
+			device->ftbl->idle(device);
+
+		/*
+		 * Don't shift by more than one level at a time to
+		 * avoid glitches.
+		 */
+
+		while (pwr->active_pwrlevel != new_level) {
+			pwr->active_pwrlevel += delta;
+
+			clk_set_rate(pwr->grp_clks[0],
+				pwr->pwrlevels[pwr->active_pwrlevel].gpu_freq);
 		}
-		if (test_bit(KGSL_PWRFLAGS_AXI_ON, &pwr->power_flags)) {
-			if (pwr->pcl)
-				msm_bus_scale_client_update_request(pwr->pcl,
-					pwrlevel->bus_freq);
-			else if (pwr->ebi1_clk)
-				clk_set_rate(pwr->ebi1_clk, pwrlevel->bus_freq);
-		}
-		trace_kgsl_pwrlevel(device, pwr->active_pwrlevel,
-				    pwrlevel->gpu_freq);
 	}
+
+	pwrlevel = &pwr->pwrlevels[pwr->active_pwrlevel];
+
+	if (test_bit(KGSL_PWRFLAGS_AXI_ON, &pwr->power_flags)) {
+
+		if (pwr->pcl)
+			msm_bus_scale_client_update_request(pwr->pcl,
+				pwrlevel->bus_freq);
+		else if (pwr->ebi1_clk)
+			clk_set_rate(pwr->ebi1_clk, pwrlevel->bus_freq);
+	}
+
+	trace_kgsl_pwrlevel(device, pwr->active_pwrlevel, pwrlevel->gpu_freq);
 }
+
 EXPORT_SYMBOL(kgsl_pwrctrl_pwrlevel_change);
 
-static int __gpuclk_store(int max, struct device *dev,
-						  struct device_attribute *attr,
-						  const char *buf, size_t count)
-{	int ret, i, delta = 5000000;
-	unsigned long val;
+static int kgsl_pwrctrl_thermal_pwrlevel_store(struct device *dev,
+					 struct device_attribute *attr,
+					 const char *buf, size_t count)
+{
 	struct kgsl_device *device = kgsl_device_from_dev(dev);
 	struct kgsl_pwrctrl *pwr;
+	int ret, level;
 
 	if (device == NULL)
 		return 0;
+
+	pwr = &device->pwrctrl;
+
+	ret = sscanf(buf, "%d", &level);
+	if (ret != 1)
+		return count;
+
+	if (level < 0)
+		return count;
+
+	mutex_lock(&device->mutex);
+
+	if (level > pwr->num_pwrlevels - 2)
+		level = pwr->num_pwrlevels - 2;
+
+	pwr->thermal_pwrlevel = level;
+
+	/*
+	 * If there is no power policy set the clock to the requested thermal
+	 * level - if thermal now happens to be higher than max, then that will
+	 * be limited by the pwrlevel change function.  Otherwise if there is
+	 * a policy only change the active clock if it is higher then the new
+	 * thermal level
+	 */
+
+	if (device->pwrscale.policy == NULL ||
+		pwr->thermal_pwrlevel > pwr->active_pwrlevel)
+		kgsl_pwrctrl_pwrlevel_change(device, pwr->thermal_pwrlevel);
+
+	mutex_unlock(&device->mutex);
+
+	return count;
+}
+
+static int kgsl_pwrctrl_thermal_pwrlevel_show(struct device *dev,
+					struct device_attribute *attr,
+					char *buf)
+{
+
+	struct kgsl_device *device = kgsl_device_from_dev(dev);
+	struct kgsl_pwrctrl *pwr;
+	if (device == NULL)
+		return 0;
+	pwr = &device->pwrctrl;
+	return snprintf(buf, PAGE_SIZE, "%d\n", pwr->thermal_pwrlevel);
+}
+
+static int kgsl_pwrctrl_max_pwrlevel_store(struct device *dev,
+					 struct device_attribute *attr,
+					 const char *buf, size_t count)
+{
+	struct kgsl_device *device = kgsl_device_from_dev(dev);
+	struct kgsl_pwrctrl *pwr;
+	int ret, level, max_level;
+
+	if (device == NULL)
+		return 0;
+
+	pwr = &device->pwrctrl;
+
+	ret = sscanf(buf, "%d", &level);
+	if (ret != 1)
+		return count;
+
+	/* If the use specifies a negative number, then don't change anything */
+	if (level < 0)
+		return count;
+
+	mutex_lock(&device->mutex);
+
+	/* You can't set a maximum power level lower than the minimum */
+	if (level > pwr->min_pwrlevel)
+		level = pwr->min_pwrlevel;
+
+	pwr->max_pwrlevel = level;
+
+
+	max_level = max_t(int, pwr->thermal_pwrlevel, pwr->max_pwrlevel);
+
+	/*
+	 * If there is no policy then move to max by default.  Otherwise only
+	 * move max if the current level happens to be higher then the new max
+	 */
+
+	if (device->pwrscale.policy == NULL ||
+		(max_level > pwr->active_pwrlevel))
+		kgsl_pwrctrl_pwrlevel_change(device, max_level);
+
+	mutex_unlock(&device->mutex);
+
+	return count;
+}
+
+static int kgsl_pwrctrl_max_pwrlevel_show(struct device *dev,
+					struct device_attribute *attr,
+					char *buf)
+{
+
+	struct kgsl_device *device = kgsl_device_from_dev(dev);
+	struct kgsl_pwrctrl *pwr;
+	if (device == NULL)
+		return 0;
+	pwr = &device->pwrctrl;
+	return snprintf(buf, PAGE_SIZE, "%d\n", pwr->max_pwrlevel);
+}
+
+static int kgsl_pwrctrl_min_pwrlevel_store(struct device *dev,
+					 struct device_attribute *attr,
+					 const char *buf, size_t count)
+{	struct kgsl_device *device = kgsl_device_from_dev(dev);
+	struct kgsl_pwrctrl *pwr;
+	int ret, level, min_level;
+
+	if (device == NULL)
+		return 0;
+
+	pwr = &device->pwrctrl;
+
+	ret = sscanf(buf, "%d", &level);
+	if (ret != 1)
+		return count;
+
+	/* Don't do anything on obviously incorrect values */
+	if (level < 0)
+		return count;
+
+	mutex_lock(&device->mutex);
+	if (level > pwr->num_pwrlevels - 2)
+		level = pwr->num_pwrlevels - 2;
+
+	/* You can't set a minimum power level lower than the maximum */
+	if (level < pwr->max_pwrlevel)
+		level = pwr->max_pwrlevel;
+
+	pwr->min_pwrlevel = level;
+
+	min_level = max_t(int, pwr->thermal_pwrlevel, pwr->min_pwrlevel);
+
+	/* Only move the power level higher if minimum is higher then the
+	 * current level
+	 */
+
+	if (min_level < pwr->active_pwrlevel)
+		kgsl_pwrctrl_pwrlevel_change(device, min_level);
+
+	mutex_unlock(&device->mutex);
+
+	return count;
+}
+
+static int kgsl_pwrctrl_min_pwrlevel_show(struct device *dev,
+					struct device_attribute *attr,
+					char *buf)
+{
+	struct kgsl_device *device = kgsl_device_from_dev(dev);
+	struct kgsl_pwrctrl *pwr;
+	if (device == NULL)
+		return 0;
+	pwr = &device->pwrctrl;
+	return snprintf(buf, PAGE_SIZE, "%d\n", pwr->min_pwrlevel);
+}
+
+static int kgsl_pwrctrl_num_pwrlevels_show(struct device *dev,
+					struct device_attribute *attr,
+					char *buf)
+{
+
+	struct kgsl_device *device = kgsl_device_from_dev(dev);
+	struct kgsl_pwrctrl *pwr;
+	if (device == NULL)
+		return 0;
+	pwr = &device->pwrctrl;
+	return snprintf(buf, PAGE_SIZE, "%d\n", pwr->num_pwrlevels - 1);
+}
+
+/* Given a GPU clock value, return the nearest powerlevel */
+
+static int _get_nearest_pwrlevel(struct kgsl_pwrctrl *pwr, unsigned int clock)
+{
+	int i;
+
+	for (i = 0; i < pwr->num_pwrlevels - 1; i++) {
+		if (abs(pwr->pwrlevels[i].gpu_freq - clock) < 5000000)
+			return i;
+	}
+
+	return -ERANGE;
+}
+
+static int kgsl_pwrctrl_max_gpuclk_store(struct device *dev,
+					 struct device_attribute *attr,
+					 const char *buf, size_t count)
+{
+	struct kgsl_device *device = kgsl_device_from_dev(dev);
+	struct kgsl_pwrctrl *pwr;
+	unsigned long val;
+	int ret, level;
+
+	if (device == NULL)
+		return 0;
+
 	pwr = &device->pwrctrl;
 
 	ret = sscanf(buf, "%ld", &val);
@@ -149,44 +383,30 @@
 		return count;
 
 	mutex_lock(&device->mutex);
-	for (i = 0; i < pwr->num_pwrlevels - 1; i++) {
-		if (abs(pwr->pwrlevels[i].gpu_freq - val) < delta) {
-			if (max)
-				pwr->thermal_pwrlevel = i;
-			break;
-		}
-	}
-
-	if (i == (pwr->num_pwrlevels - 1))
+	level = _get_nearest_pwrlevel(pwr, val);
+	if (level < 0)
 		goto done;
 
+	pwr->thermal_pwrlevel = level;
+
 	/*
-	 * If the current or requested clock speed is greater than the
-	 * thermal limit, bump down immediately.
+	 * if the thermal limit is lower than the current setting,
+	 * move the speed down immediately
 	 */
 
-	if (pwr->pwrlevels[pwr->active_pwrlevel].gpu_freq >
-	    pwr->pwrlevels[pwr->thermal_pwrlevel].gpu_freq)
+	if (pwr->thermal_pwrlevel > pwr->active_pwrlevel)
 		kgsl_pwrctrl_pwrlevel_change(device, pwr->thermal_pwrlevel);
-	else if (!max)
-		kgsl_pwrctrl_pwrlevel_change(device, i);
 
 done:
 	mutex_unlock(&device->mutex);
 	return count;
 }
 
-static int kgsl_pwrctrl_max_gpuclk_store(struct device *dev,
-					 struct device_attribute *attr,
-					 const char *buf, size_t count)
-{
-	return __gpuclk_store(1, dev, attr, buf, count);
-}
-
 static int kgsl_pwrctrl_max_gpuclk_show(struct device *dev,
 					struct device_attribute *attr,
 					char *buf)
 {
+
 	struct kgsl_device *device = kgsl_device_from_dev(dev);
 	struct kgsl_pwrctrl *pwr;
 	if (device == NULL)
@@ -200,7 +420,27 @@
 				     struct device_attribute *attr,
 				     const char *buf, size_t count)
 {
-	return __gpuclk_store(0, dev, attr, buf, count);
+	struct kgsl_device *device = kgsl_device_from_dev(dev);
+	struct kgsl_pwrctrl *pwr;
+	unsigned long val;
+	int ret, level;
+
+	if (device == NULL)
+		return 0;
+
+	pwr = &device->pwrctrl;
+
+	ret = sscanf(buf, "%ld", &val);
+	if (ret != 1)
+		return count;
+
+	mutex_lock(&device->mutex);
+	level = _get_nearest_pwrlevel(pwr, val);
+	if (level >= 0)
+		kgsl_pwrctrl_pwrlevel_change(device, level);
+
+	mutex_unlock(&device->mutex);
+	return count;
 }
 
 static int kgsl_pwrctrl_gpuclk_show(struct device *dev,
@@ -381,6 +621,18 @@
 DEVICE_ATTR(gpu_available_frequencies, 0444,
 	kgsl_pwrctrl_gpu_available_frequencies_show,
 	NULL);
+DEVICE_ATTR(max_pwrlevel, 0644,
+	kgsl_pwrctrl_max_pwrlevel_show,
+	kgsl_pwrctrl_max_pwrlevel_store);
+DEVICE_ATTR(min_pwrlevel, 0644,
+	kgsl_pwrctrl_min_pwrlevel_show,
+	kgsl_pwrctrl_min_pwrlevel_store);
+DEVICE_ATTR(thermal_pwrlevel, 0644,
+	kgsl_pwrctrl_thermal_pwrlevel_show,
+	kgsl_pwrctrl_thermal_pwrlevel_store);
+DEVICE_ATTR(num_pwrlevels, 0444,
+	kgsl_pwrctrl_num_pwrlevels_show,
+	NULL);
 
 static const struct device_attribute *pwrctrl_attr_list[] = {
 	&dev_attr_gpuclk,
@@ -390,6 +642,10 @@
 	&dev_attr_gpubusy,
 	&dev_attr_gputop,
 	&dev_attr_gpu_available_frequencies,
+	&dev_attr_max_pwrlevel,
+	&dev_attr_min_pwrlevel,
+	&dev_attr_thermal_pwrlevel,
+	&dev_attr_num_pwrlevels,
 	NULL
 };
 
@@ -622,6 +878,13 @@
 		goto done;
 	}
 	pwr->num_pwrlevels = pdata->num_levels;
+
+	/* Initialize the user and thermal clock constraints */
+
+	pwr->max_pwrlevel = 0;
+	pwr->min_pwrlevel = pdata->num_levels - 2;
+	pwr->thermal_pwrlevel = 0;
+
 	pwr->active_pwrlevel = pdata->init_level;
 	pwr->default_pwrlevel = pdata->init_level;
 	for (i = 0; i < pdata->num_levels; i++) {
@@ -884,6 +1147,9 @@
 				kgsl_pwrstate_to_str(device->state));
 		break;
 	}
+
+	kgsl_mmu_disable_clk_on_ts(&device->mmu, 0, false);
+
 	return 0;
 }
 
@@ -951,6 +1217,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 +1235,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 */
@@ -977,6 +1259,7 @@
 		pm_qos_update_request(&device->pm_qos_req_dma,
 					GPU_SWFI_LATENCY);
 	case KGSL_STATE_ACTIVE:
+		kgsl_pwrctrl_request_state(device, KGSL_STATE_NONE);
 		break;
 	default:
 		KGSL_PWR_WARN(device, "unhandled state %s\n",
diff --git a/drivers/gpu/msm/kgsl_pwrctrl.h b/drivers/gpu/msm/kgsl_pwrctrl.h
index c02a9fc..e51ec54 100644
--- a/drivers/gpu/msm/kgsl_pwrctrl.h
+++ b/drivers/gpu/msm/kgsl_pwrctrl.h
@@ -38,6 +38,30 @@
 	unsigned int elapsed_old;
 };
 
+/**
+ * struct kgsl_pwrctrl - Power control settings for a KGSL device
+ * @interrupt_num - The interrupt number for the device
+ * @ebi1_clk - Pointer to the EBI clock structure
+ * @grp_clks - Array of clocks structures that we control
+ * @power_flags - Control flags for power
+ * @pwrlevels - List of supported power levels
+ * @active_pwrlevel - The currently active power level
+ * @thermal_pwrlevel - maximum powerlevel constraint from thermal
+ * @max_pwrlevel - maximum allowable powerlevel per the user
+ * @min_pwrlevel - minimum allowable powerlevel per the user
+ * @num_pwrlevels - number of available power levels
+ * @interval_timeout - timeout in jiffies to be idle before a power event
+ * @strtstp_sleepwake - true if the device supports low latency GPU start/stop
+ * @gpu_reg - pointer to the regulator structure for gpu_reg
+ * @gpu_cx - pointer to the regulator structure for gpu_cx
+ * @pcl - bus scale identifier
+ * @nap_allowed - true if the device supports naps
+ * @idle_needed - true if the device needs a idle before clock change
+ * @irq_name - resource name for the IRQ
+ * @restore_slumber - Flag to indicate that we are in a suspend/restore sequence
+ * @clk_stats - structure of clock statistics
+ */
+
 struct kgsl_pwrctrl {
 	int interrupt_num;
 	struct clk *ebi1_clk;
@@ -47,6 +71,8 @@
 	unsigned int active_pwrlevel;
 	int thermal_pwrlevel;
 	unsigned int default_pwrlevel;
+	unsigned int max_pwrlevel;
+	unsigned int min_pwrlevel;
 	unsigned int num_pwrlevels;
 	unsigned int interval_timeout;
 	bool strtstp_sleepwake;
diff --git a/drivers/gpu/msm/kgsl_pwrscale.c b/drivers/gpu/msm/kgsl_pwrscale.c
index f6277b3..aad1a8d 100644
--- a/drivers/gpu/msm/kgsl_pwrscale.c
+++ b/drivers/gpu/msm/kgsl_pwrscale.c
@@ -299,8 +299,14 @@
 {
 	if (device->pwrscale.policy != NULL) {
 		device->pwrscale.policy->close(device, &device->pwrscale);
+
+		/*
+		 * Try to set max pwrlevel which will be limited to thermal by
+		 * kgsl_pwrctrl_pwrlevel_change if thermal is indeed lower
+		 */
+
 		kgsl_pwrctrl_pwrlevel_change(device,
-				device->pwrctrl.thermal_pwrlevel);
+				device->pwrctrl.max_pwrlevel);
 	}
 	device->pwrscale.policy = NULL;
 }
diff --git a/drivers/gpu/msm/kgsl_pwrscale_msm.c b/drivers/gpu/msm/kgsl_pwrscale_msm.c
index b302bee..c680d57 100644
--- a/drivers/gpu/msm/kgsl_pwrscale_msm.c
+++ b/drivers/gpu/msm/kgsl_pwrscale_msm.c
@@ -23,6 +23,8 @@
 	struct kgsl_device		*device;
 	int				enabled;
 	unsigned int			cur_freq;
+	unsigned int			req_level;
+	int				floor_level;
 	struct msm_dcvs_core_info	*core_info;
 	int				gpu_busy;
 	int				dcvs_core_id;
@@ -69,7 +71,39 @@
 		return 0;
 
 	mutex_lock(&device->mutex);
-	kgsl_pwrctrl_pwrlevel_change(device, i);
+	priv->req_level = i;
+	if (priv->req_level <= priv->floor_level) {
+		kgsl_pwrctrl_pwrlevel_change(device, priv->req_level);
+		priv->cur_freq = pwr->pwrlevels[pwr->active_pwrlevel].gpu_freq;
+	}
+	mutex_unlock(&device->mutex);
+
+	/* return current frequency in kHz */
+	return priv->cur_freq / 1000;
+}
+
+static int msm_set_min_freq(int core_num, unsigned int freq)
+{
+	int i, delta = 5000000;
+	struct msm_priv *priv = the_msm_priv;
+	struct kgsl_device *device = priv->device;
+	struct kgsl_pwrctrl *pwr = &device->pwrctrl;
+
+	/* msm_dcvs manager uses frequencies in kHz */
+	freq *= 1000;
+	for (i = 0; i < pwr->num_pwrlevels; i++)
+		if (abs(pwr->pwrlevels[i].gpu_freq - freq) < delta)
+			break;
+	if (i == pwr->num_pwrlevels)
+		return 0;
+
+	mutex_lock(&device->mutex);
+	priv->floor_level = i;
+	if (priv->floor_level <= priv->req_level)
+		kgsl_pwrctrl_pwrlevel_change(device, priv->floor_level);
+	else if (priv->floor_level > priv->req_level)
+		kgsl_pwrctrl_pwrlevel_change(device, priv->req_level);
+
 	priv->cur_freq = pwr->pwrlevels[pwr->active_pwrlevel].gpu_freq;
 	mutex_unlock(&device->mutex);
 
@@ -170,6 +204,7 @@
 
 		priv->core_info = pdata->core_info;
 		tbl = priv->core_info->freq_tbl;
+		priv->floor_level = pwr->num_pwrlevels - 1;
 		/* 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++)
@@ -180,6 +215,7 @@
 				0,
 				priv->core_info,
 				msm_set_freq, msm_get_freq, msm_idle_enable,
+				msm_set_min_freq,
 				priv->core_info->sensors[0]);
 		if (priv->dcvs_core_id < 0) {
 			KGSL_PWR_ERR(device, "msm_dcvs_register_core failed");
diff --git a/drivers/gpu/msm/kgsl_pwrscale_trustzone.c b/drivers/gpu/msm/kgsl_pwrscale_trustzone.c
index 7c2514b..e01932b 100644
--- a/drivers/gpu/msm/kgsl_pwrscale_trustzone.c
+++ b/drivers/gpu/msm/kgsl_pwrscale_trustzone.c
@@ -93,7 +93,7 @@
 		priv->governor = TZ_GOVERNOR_PERFORMANCE;
 
 	if (priv->governor == TZ_GOVERNOR_PERFORMANCE)
-		kgsl_pwrctrl_pwrlevel_change(device, pwr->thermal_pwrlevel);
+		kgsl_pwrctrl_pwrlevel_change(device, pwr->max_pwrlevel);
 
 	mutex_unlock(&device->mutex);
 	return count;
diff --git a/drivers/gpu/msm/kgsl_sharedmem.c b/drivers/gpu/msm/kgsl_sharedmem.c
index 77617ba..08353ee 100644
--- a/drivers/gpu/msm/kgsl_sharedmem.c
+++ b/drivers/gpu/msm/kgsl_sharedmem.c
@@ -367,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;
@@ -405,7 +405,7 @@
 		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 */
@@ -511,21 +511,29 @@
 
 void kgsl_cache_range_op(struct kgsl_memdesc *memdesc, int op)
 {
-	void *addr = memdesc->hostptr;
+	/*
+	 * If the buffer is mapped in the kernel operate on that address
+	 * otherwise use the user address
+	 */
+
+	void *addr = (memdesc->hostptr) ?
+		memdesc->hostptr : (void *) memdesc->useraddr;
+
 	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);
@@ -533,38 +541,22 @@
 static int
 _kgsl_sharedmem_page_alloc(struct kgsl_memdesc *memdesc,
 			struct kgsl_pagetable *pagetable,
-			size_t size, unsigned int flags, unsigned int protflags)
+			size_t 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;
 
-	/*
-	 * Get the current memory information to be used in deciding if we
-	 * should go ahead with this allocation
-	 */
-
-	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;
-
-	align = (flags & KGSL_MEMALIGN_MASK) >> KGSL_MEMALIGN_SHIFT;
+	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 */
+	if (page_size != PAGE_SIZE)
+		kgsl_memdesc_set_align(memdesc, ilog2(page_size));
 
 	/*
 	 * There needs to be enough room in the sg structure to be able to
@@ -583,7 +575,6 @@
 
 	memdesc->size = size;
 	memdesc->pagetable = pagetable;
-	memdesc->priv |= (flags & KGSL_MEMALIGN_MASK);
 	memdesc->ops = &kgsl_page_alloc_ops;
 
 	memdesc->sg = kgsl_sg_alloc(sglen_alloc);
@@ -621,7 +612,7 @@
 	while (len > 0) {
 		struct page *page;
 		unsigned int gfp_mask = GFP_KERNEL | __GFP_HIGHMEM |
-			__GFP_NOWARN;
+			__GFP_NOWARN | __GFP_NORETRY;
 		int j;
 
 		/* don't waste space at the end of the allocation*/
@@ -638,6 +629,13 @@
 				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;
+			goto done;
 		}
 
 		for (j = 0; j < page_size >> PAGE_SHIFT; j++)
@@ -663,7 +661,7 @@
 		if (kgsl_guard_page != NULL) {
 			sg_set_page(&memdesc->sg[sglen++], kgsl_guard_page,
 				PAGE_SIZE, 0);
-			memdesc->flags |= KGSL_MEMDESC_GUARD_PAGE;
+			memdesc->priv |= KGSL_MEMDESC_GUARD_PAGE;
 		}
 	}
 
@@ -708,11 +706,6 @@
 	outer_cache_range_op_sg(memdesc->sg, memdesc->sglen,
 				KGSL_CACHE_OP_FLUSH);
 
-	ret = kgsl_mmu_map(pagetable, memdesc, protflags);
-
-	if (ret)
-		goto done;
-
 	KGSL_STATS_ADD(size, kgsl_driver.stats.page_alloc,
 		kgsl_driver.stats.page_alloc_max);
 
@@ -739,8 +732,7 @@
 
 	size = ALIGN(size, PAGE_SIZE * 2);
 
-	ret =  _kgsl_sharedmem_page_alloc(memdesc, pagetable, size,
-		0, GSL_PT_PAGE_RV | GSL_PT_PAGE_WV);
+	ret =  _kgsl_sharedmem_page_alloc(memdesc, pagetable, size);
 	if (!ret)
 		ret = kgsl_page_alloc_map_kernel(memdesc);
 	if (ret)
@@ -752,19 +744,9 @@
 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;
-
-	if (size == 0)
-		return -EINVAL;
-
-	protflags = GSL_PT_PAGE_RV;
-	if (!(flags & KGSL_MEMFLAGS_GPUREADONLY))
-		protflags |= GSL_PT_PAGE_WV;
-
-	return _kgsl_sharedmem_page_alloc(memdesc, pagetable, size,
-		flags, protflags);
+	return _kgsl_sharedmem_page_alloc(memdesc, pagetable, PAGE_ALIGN(size));
 }
 EXPORT_SYMBOL(kgsl_sharedmem_page_alloc_user);
 
@@ -842,12 +824,6 @@
 	if (result)
 		goto err;
 
-	result = kgsl_mmu_map(pagetable, memdesc,
-		GSL_PT_PAGE_RV | GSL_PT_PAGE_WV);
-
-	if (result)
-		goto err;
-
 	KGSL_STATS_ADD(size, kgsl_driver.stats.coherent,
 		kgsl_driver.stats.coherent_max);
 
@@ -861,7 +837,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);
diff --git a/drivers/gpu/msm/kgsl_sharedmem.h b/drivers/gpu/msm/kgsl_sharedmem.h
index 5a6c4c2..d937699 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,48 @@
 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_get_cachemode - Get cache mode of a memdesc
+ * @memdesc: the memdesc
+ *
+ * Returns a KGSL_CACHEMODE* value.
+ */
+static inline int
+kgsl_memdesc_get_cachemode(const struct kgsl_memdesc *memdesc)
+{
+	return (memdesc->flags & KGSL_CACHEMODE_MASK) >> KGSL_CACHEMODE_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)
 {
 	/*
@@ -128,14 +169,90 @@
 	return 0;
 }
 
+/*
+ * kgsl_memdesc_is_global - is this a globally mapped buffer?
+ * @memdesc: the memdesc
+ *
+ * Returns nonzero if this is a global mapping, 0 otherwise
+ */
+static inline int kgsl_memdesc_is_global(const struct kgsl_memdesc *memdesc)
+{
+	return (memdesc->priv & KGSL_MEMDESC_GLOBAL) != 0;
+}
+
+/*
+ * kgsl_memdesc_has_guard_page - is the last page a guard page?
+ * @memdesc - the memdesc
+ *
+ * Returns nonzero if there is a guard page, 0 otherwise
+ */
+static inline int
+kgsl_memdesc_has_guard_page(const struct kgsl_memdesc *memdesc)
+{
+	return (memdesc->priv & KGSL_MEMDESC_GUARD_PAGE) != 0;
+}
+
+/*
+ * kgsl_memdesc_protflags - get mmu protection flags
+ * @memdesc - the memdesc
+ * Returns a mask of GSL_PT_PAGE* values based on the
+ * memdesc flags.
+ */
+static inline unsigned int
+kgsl_memdesc_protflags(const struct kgsl_memdesc *memdesc)
+{
+	unsigned int protflags = GSL_PT_PAGE_RV;
+	if (!(memdesc->flags & KGSL_MEMFLAGS_GPUREADONLY))
+		protflags |= GSL_PT_PAGE_WV;
+	return protflags;
+}
+
+/*
+ * kgsl_memdesc_use_cpu_map - use the same virtual mapping on CPU and GPU?
+ * @memdesc - the memdesc
+ */
+static inline int
+kgsl_memdesc_use_cpu_map(const struct kgsl_memdesc *memdesc)
+{
+	return (memdesc->flags & KGSL_MEMFLAGS_USE_CPU_MAP) != 0;
+}
+
+/*
+ * kgsl_memdesc_mmapsize - get the size of the mmap region
+ * @memdesc - the memdesc
+ *
+ * The entire memdesc must be mapped. Additionally if the
+ * CPU mapping is going to be mirrored, there must be room
+ * for the guard page to be mapped so that the address spaces
+ * match up.
+ */
+static inline unsigned int
+kgsl_memdesc_mmapsize(const struct kgsl_memdesc *memdesc)
+{
+	unsigned int size = memdesc->size;
+	if (kgsl_memdesc_use_cpu_map(memdesc) &&
+		kgsl_memdesc_has_guard_page(memdesc))
+		size += SZ_4K;
+	return size;
+}
+
 static inline int
 kgsl_allocate(struct kgsl_memdesc *memdesc,
 		struct kgsl_pagetable *pagetable, size_t size)
 {
+	int ret;
+	memdesc->priv |= (KGSL_MEMTYPE_KERNEL << KGSL_MEMTYPE_SHIFT);
 	if (kgsl_mmu_get_mmutype() == KGSL_MMU_TYPE_NONE)
 		return kgsl_sharedmem_ebimem(memdesc, pagetable, size);
-	memdesc->priv |= (KGSL_MEMTYPE_KERNEL << KGSL_MEMTYPE_SHIFT);
-	return kgsl_sharedmem_page_alloc(memdesc, pagetable, size);
+
+	ret = kgsl_sharedmem_page_alloc(memdesc, pagetable, size);
+	if (ret)
+		return ret;
+	ret = kgsl_mmu_map(pagetable, memdesc,
+			   kgsl_memdesc_protflags(memdesc));
+	if (ret)
+		kgsl_sharedmem_free(memdesc);
+	return ret;
 }
 
 static inline int
@@ -144,15 +261,17 @@
 		size_t size, unsigned int flags)
 {
 	int ret;
-	unsigned int mask = (KGSL_MEMTYPE_MASK | KGSL_MEMFLAGS_GPUREADONLY);
+
+	if (size == 0)
+		return -EINVAL;
+
+	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 +281,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 81cb34f..9662fce 100644
--- a/drivers/gpu/msm/kgsl_trace.h
+++ b/drivers/gpu/msm/kgsl_trace.h
@@ -317,6 +317,8 @@
 		__field(unsigned int, size)
 		__field(unsigned int, tgid)
 		__array(char, usage, 16)
+		__field(unsigned int, id)
+		__field(unsigned int, flags)
 	),
 
 	TP_fast_assign(
@@ -324,13 +326,77 @@
 		__entry->size = mem_entry->memdesc.size;
 		__entry->tgid = mem_entry->priv->pid;
 		kgsl_get_memory_usage(__entry->usage, sizeof(__entry->usage),
-				     mem_entry->memdesc.priv);
+				     mem_entry->memdesc.flags);
+		__entry->id = mem_entry->id;
+		__entry->flags = mem_entry->memdesc.flags;
 	),
 
 	TP_printk(
-		"gpuaddr=0x%08x size=%d tgid=%d usage=%s",
+		"gpuaddr=0x%08x size=%d tgid=%d usage=%s id=%d flags=0x%08x",
 		__entry->gpuaddr, __entry->size, __entry->tgid,
-		__entry->usage
+		__entry->usage, __entry->id, __entry->flags
+	)
+);
+
+TRACE_EVENT(kgsl_mem_mmap,
+
+	TP_PROTO(struct kgsl_mem_entry *mem_entry),
+
+	TP_ARGS(mem_entry),
+
+	TP_STRUCT__entry(
+		__field(unsigned long, useraddr)
+		__field(unsigned int, gpuaddr)
+		__field(unsigned int, size)
+		__array(char, usage, 16)
+		__field(unsigned int, id)
+		__field(unsigned int, flags)
+	),
+
+	TP_fast_assign(
+		__entry->useraddr = mem_entry->memdesc.useraddr;
+		__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->id = mem_entry->id;
+		__entry->flags = mem_entry->memdesc.flags;
+	),
+
+	TP_printk(
+		"useraddr=%lx gpuaddr=0x%08x size=%d usage=%s id=%d"
+		" flags=0x%08x",
+		__entry->useraddr, __entry->gpuaddr, __entry->size,
+		__entry->usage, __entry->id, __entry->flags
+	)
+);
+
+TRACE_EVENT(kgsl_mem_unmapped_area_collision,
+
+	TP_PROTO(struct kgsl_mem_entry *mem_entry,
+		 unsigned long hint,
+		 unsigned long len,
+		 unsigned long addr),
+
+	TP_ARGS(mem_entry, hint, len, addr),
+
+	TP_STRUCT__entry(
+		__field(unsigned int, id)
+		__field(unsigned long, hint)
+		__field(unsigned long, len)
+		__field(unsigned long, addr)
+	),
+
+	TP_fast_assign(
+		__entry->id = mem_entry->id;
+		__entry->hint  = hint;
+		__entry->len = len;
+		__entry->addr = addr;
+	),
+
+	TP_printk(
+		"id=%d hint=0x%lx len=%ld addr=0x%lx",
+		__entry->id, __entry->hint, __entry->len, __entry->addr
 	)
 );
 
@@ -347,6 +413,7 @@
 		__field(int, type)
 		__field(unsigned int, tgid)
 		__array(char, usage, 16)
+		__field(unsigned int, id)
 	),
 
 	TP_fast_assign(
@@ -356,14 +423,15 @@
 		__entry->type = mem_entry->memtype;
 		__entry->tgid = mem_entry->priv->pid;
 		kgsl_get_memory_usage(__entry->usage, sizeof(__entry->usage),
-				     mem_entry->memdesc.priv);
+				     mem_entry->memdesc.flags);
+		__entry->id = mem_entry->id;
 	),
 
 	TP_printk(
-		"gpuaddr=0x%08x size=%d type=%d fd=%d tgid=%d usage %s",
+		"gpuaddr=0x%08x size=%d type=%d fd=%d tgid=%d usage=%s id=%d",
 		__entry->gpuaddr, __entry->size,
 		__entry->type, __entry->fd, __entry->tgid,
-		__entry->usage
+		__entry->usage, __entry->id
 	)
 );
 
@@ -380,6 +448,7 @@
 		__field(int, fd)
 		__field(unsigned int, tgid)
 		__array(char, usage, 16)
+		__field(unsigned int, id)
 	),
 
 	TP_fast_assign(
@@ -388,13 +457,14 @@
 		__entry->type = mem_entry->memtype;
 		__entry->tgid = mem_entry->priv->pid;
 		kgsl_get_memory_usage(__entry->usage, sizeof(__entry->usage),
-				     mem_entry->memdesc.priv);
+				     mem_entry->memdesc.flags);
+		__entry->id = mem_entry->id;
 	),
 
 	TP_printk(
-		"gpuaddr=0x%08x size=%d type=%d tgid=%d usage=%s",
+		"gpuaddr=0x%08x size=%d type=%d tgid=%d usage=%s id=%d",
 		__entry->gpuaddr, __entry->size, __entry->type,
-		__entry->tgid, __entry->usage
+		__entry->tgid, __entry->usage, __entry->id
 	)
 );
 
@@ -411,6 +481,7 @@
 		__field(unsigned int, size)
 		__field(int, type)
 		__array(char, usage, 16)
+		__field(unsigned int, id)
 		__field(unsigned int, drawctxt_id)
 		__field(unsigned int, curr_ts)
 		__field(unsigned int, free_ts)
@@ -421,7 +492,8 @@
 		__entry->gpuaddr = mem_entry->memdesc.gpuaddr;
 		__entry->size = mem_entry->memdesc.size;
 		kgsl_get_memory_usage(__entry->usage, sizeof(__entry->usage),
-				     mem_entry->memdesc.priv);
+				     mem_entry->memdesc.flags);
+		__entry->id = mem_entry->id;
 		__entry->drawctxt_id = id;
 		__entry->type = mem_entry->memtype;
 		__entry->curr_ts = curr_ts;
@@ -429,13 +501,14 @@
 	),
 
 	TP_printk(
-		"d_name=%s gpuaddr=0x%08x size=%d type=%d usage=%s ctx=%u"
+		"d_name=%s gpuaddr=0x%08x size=%d type=%d usage=%s id=%d ctx=%u"
 		" curr_ts=0x%x free_ts=0x%x",
 		__get_str(device_name),
 		__entry->gpuaddr,
 		__entry->size,
 		__entry->type,
 		__entry->usage,
+		__entry->id,
 		__entry->drawctxt_id,
 		__entry->curr_ts,
 		__entry->free_ts
diff --git a/drivers/gpu/msm/z180.c b/drivers/gpu/msm/z180.c
index 712bc60..258dcfa 100644
--- a/drivers/gpu/msm/z180.c
+++ b/drivers/gpu/msm/z180.c
@@ -260,6 +260,13 @@
 				     GSL_PT_PAGE_RV);
 	if (result)
 		goto error_unmap_memstore;
+	/*
+	 * Set the mpu end to the last "normal" global memory we use.
+	 * For the IOMMU, this will be used to restrict access to the
+	 * mapped registers.
+	 */
+	device->mh.mpu_range = z180_dev->ringbuffer.cmdbufdesc.gpuaddr +
+				z180_dev->ringbuffer.cmdbufdesc.size;
 	return result;
 
 error_unmap_dummy:
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-common.c b/drivers/hwmon/qpnp-adc-common.c
index a384103..5fb041d 100644
--- a/drivers/hwmon/qpnp-adc-common.c
+++ b/drivers/hwmon/qpnp-adc-common.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
@@ -299,81 +299,83 @@
 	{419,		128000}
 };
 
+/* Voltage to temperature */
 static const struct qpnp_vadc_map_pt adcmap_100k_104ef_104fb[] = {
-	{-40,	1758},
-	{-35,	1742},
-	{-30,	1719},
-	{-25,	1691},
-	{-20,	1654},
-	{-15,	1608},
-	{-10,	1551},
-	{-5,	1483},
-	{0,	1404},
-	{5,	1315},
-	{10,	1218},
-	{15,	1114},
-	{20,	1007},
-	{25,	900},
-	{30,	795},
-	{35,	696},
-	{40,	605},
-	{45,	522},
-	{50,	448},
-	{55,	383},
-	{60,	327},
-	{65,	278},
-	{70,	237},
-	{75,	202},
-	{80,	172},
-	{85,	146},
-	{90,	125},
-	{95,	107},
-	{100,	92},
-	{105,	79},
-	{110,	68},
-	{115,	59},
-	{120,	51},
-	{125,	44}
+	{1758,	-40},
+	{1742,	-35},
+	{1719,	-30},
+	{1691,	-25},
+	{1654,	-20},
+	{1608,	-15},
+	{1551,	-10},
+	{1483,	-5},
+	{1404,	0},
+	{1315,	5},
+	{1218,	10},
+	{1114,	15},
+	{1007,	20},
+	{900,	25},
+	{795,	30},
+	{696,	35},
+	{605,	40},
+	{522,	45},
+	{448,	50},
+	{383,	55},
+	{327,	60},
+	{278,	65},
+	{237,	70},
+	{202,	75},
+	{172,	80},
+	{146,	85},
+	{125,	90},
+	{107,	95},
+	{92,	100},
+	{79,	105},
+	{68,	110},
+	{59,	115},
+	{51,	120},
+	{44,	125}
 };
 
+/* Voltage to temperature */
 static const struct qpnp_vadc_map_pt adcmap_150k_104ef_104fb[] = {
-	{-40,	1738},
-	{-35,	1714},
-	{-30,	1682},
-	{-25,	1641},
-	{-20,	1589},
-	{-15,	1526},
-	{-10,	1451},
-	{-5,	1363},
-	{0,	1266},
-	{5,	1159},
-	{10,	1048},
-	{15,	936},
-	{20,	825},
-	{25,	720},
-	{30,	622},
-	{35,	533},
-	{40,	454},
-	{45,	385},
-	{50,	326},
-	{55,	275},
-	{60,	232},
-	{65,	195},
-	{70,	165},
-	{75,	139},
-	{80,	118},
-	{85,	100},
-	{90,	85},
-	{95,	73},
-	{100,	62},
-	{105,	53},
-	{110,	46},
-	{115,	40},
-	{120,	34},
-	{125,	30}
+	{1738,	-40},
+	{1714,	-35},
+	{1682,	-30},
+	{1641,	-25},
+	{1589,	-20},
+	{1526,	-15},
+	{1451,	-10},
+	{1363,	-5},
+	{1266,	0},
+	{1159,	5},
+	{1048,	10},
+	{936,	15},
+	{825,	20},
+	{720,	25},
+	{622,	30},
+	{533,	35},
+	{454,	40},
+	{385,	45},
+	{326,	50},
+	{275,	55},
+	{232,	60},
+	{195,	65},
+	{165,	70},
+	{139,	75},
+	{118,	80},
+	{100,	85},
+	{85,	90},
+	{73,	95},
+	{62,	100},
+	{53,	105},
+	{46,	110},
+	{40,	115},
+	{34,	120},
+	{30,	125}
 };
 
-static int32_t qpnp_adc_map_linear(const struct qpnp_vadc_map_pt *pts,
+static int32_t qpnp_adc_map_voltage_temp(const struct qpnp_vadc_map_pt *pts,
 		uint32_t tablesize, int32_t input, int64_t *output)
 {
 	bool descending = 1;
@@ -419,7 +421,7 @@
 	return 0;
 }
 
-static int32_t qpnp_adc_map_batt_therm(const struct qpnp_vadc_map_pt *pts,
+static int32_t qpnp_adc_map_temp_voltage(const struct qpnp_vadc_map_pt *pts,
 		uint32_t tablesize, int32_t input, int64_t *output)
 {
 	bool descending = 1;
@@ -552,7 +554,7 @@
 	xo_thm = qpnp_adc_scale_ratiometric_calib(adc_code,
 			adc_properties, chan_properties);
 	xo_thm <<= 4;
-	qpnp_adc_map_linear(adcmap_ntcg_104ef_104fb,
+	qpnp_adc_map_voltage_temp(adcmap_ntcg_104ef_104fb,
 		ARRAY_SIZE(adcmap_ntcg_104ef_104fb),
 		xo_thm, &adc_chan_result->physical);
 
@@ -570,7 +572,7 @@
 	bat_voltage = qpnp_adc_scale_ratiometric_calib(adc_code,
 			adc_properties, chan_properties);
 
-	return qpnp_adc_map_batt_therm(
+	return qpnp_adc_map_temp_voltage(
 			adcmap_btm_threshold,
 			ARRAY_SIZE(adcmap_btm_threshold),
 			bat_voltage,
@@ -588,7 +590,7 @@
 	therm_voltage = qpnp_adc_scale_ratiometric_calib(adc_code,
 			adc_properties, chan_properties);
 
-	qpnp_adc_map_linear(adcmap_150k_104ef_104fb,
+	qpnp_adc_map_voltage_temp(adcmap_150k_104ef_104fb,
 		ARRAY_SIZE(adcmap_150k_104ef_104fb),
 		therm_voltage, &adc_chan_result->physical);
 
@@ -606,7 +608,7 @@
 	therm_voltage = qpnp_adc_scale_ratiometric_calib(adc_code,
 			adc_properties, chan_properties);
 
-	qpnp_adc_map_linear(adcmap_100k_104ef_104fb,
+	qpnp_adc_map_voltage_temp(adcmap_100k_104ef_104fb,
 		ARRAY_SIZE(adcmap_100k_104ef_104fb),
 		therm_voltage, &adc_chan_result->physical);
 
@@ -614,6 +616,63 @@
 }
 EXPORT_SYMBOL_GPL(qpnp_adc_scale_therm_pu2);
 
+int32_t qpnp_adc_tm_scale_voltage_therm_pu2(uint32_t reg, int64_t *result)
+{
+	int64_t adc_voltage = 0;
+	struct qpnp_vadc_linear_graph param1;
+	int negative_offset;
+
+	qpnp_get_vadc_gain_and_offset(&param1, CALIB_RATIOMETRIC);
+
+	adc_voltage = (reg - param1.adc_gnd) * param1.adc_vref;
+	if (adc_voltage < 0) {
+		negative_offset = 1;
+		adc_voltage = -adc_voltage;
+	}
+
+	do_div(adc_voltage, param1.dy);
+
+	qpnp_adc_map_temp_voltage(adcmap_100k_104ef_104fb,
+		ARRAY_SIZE(adcmap_100k_104ef_104fb),
+		adc_voltage, result);
+	if (negative_offset)
+		adc_voltage = -adc_voltage;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(qpnp_adc_tm_scale_voltage_therm_pu2);
+
+int32_t qpnp_adc_tm_scale_therm_voltage_pu2(struct qpnp_adc_tm_config *param)
+{
+	struct qpnp_vadc_linear_graph param1;
+	int rc;
+
+	qpnp_get_vadc_gain_and_offset(&param1, CALIB_RATIOMETRIC);
+
+	rc = qpnp_adc_map_voltage_temp(adcmap_100k_104ef_104fb,
+		ARRAY_SIZE(adcmap_100k_104ef_104fb),
+		param->low_thr_temp, &param->low_thr_voltage);
+	if (rc)
+		return rc;
+
+	param->low_thr_voltage *= param1.dy;
+	do_div(param->low_thr_voltage, param1.adc_vref);
+	param->low_thr_voltage += param1.adc_gnd;
+
+	rc = qpnp_adc_map_voltage_temp(adcmap_100k_104ef_104fb,
+		ARRAY_SIZE(adcmap_100k_104ef_104fb),
+		param->high_thr_temp, &param->high_thr_voltage);
+	if (rc)
+		return rc;
+
+	param->high_thr_voltage *= param1.dy;
+	do_div(param->high_thr_voltage, param1.adc_vref);
+	param->high_thr_voltage += param1.adc_gnd;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(qpnp_adc_tm_scale_therm_voltage_pu2);
+
 int32_t qpnp_adc_scale_batt_id(int32_t adc_code,
 		const struct qpnp_adc_properties *adc_properties,
 		const struct qpnp_vadc_chan_properties *chan_properties,
@@ -687,6 +746,65 @@
 }
 EXPORT_SYMBOL_GPL(qpnp_adc_scale_default);
 
+int32_t qpnp_adc_usb_scaler(struct qpnp_adc_tm_usbid_param *param,
+		uint32_t *low_threshold, uint32_t *high_threshold)
+{
+	struct qpnp_vadc_linear_graph usb_param;
+
+	qpnp_get_vadc_gain_and_offset(&usb_param, CALIB_ABSOLUTE);
+
+	*low_threshold = param->low_thr * usb_param.dy;
+	do_div(*low_threshold, usb_param.adc_vref);
+	*low_threshold += usb_param.adc_gnd;
+
+	*high_threshold = param->high_thr * usb_param.dy;
+	do_div(*high_threshold, usb_param.adc_vref);
+	*high_threshold += usb_param.adc_gnd;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(qpnp_adc_usb_scaler);
+
+int32_t qpnp_adc_btm_scaler(struct qpnp_adc_tm_btm_param *param,
+		uint32_t *low_threshold, uint32_t *high_threshold)
+{
+	struct qpnp_vadc_linear_graph btm_param;
+	int64_t *low_output = 0, *high_output = 0;
+	int rc = 0;
+
+	qpnp_get_vadc_gain_and_offset(&btm_param, CALIB_RATIOMETRIC);
+
+	rc = qpnp_adc_map_temp_voltage(
+		adcmap_btm_threshold,
+		ARRAY_SIZE(adcmap_btm_threshold),
+		(param->low_temp),
+		low_output);
+	if (rc)
+		return rc;
+
+	*low_output *= btm_param.dy;
+	do_div(*low_output, btm_param.adc_vref);
+	*low_output += btm_param.adc_gnd;
+
+	rc = qpnp_adc_map_temp_voltage(
+		adcmap_btm_threshold,
+		ARRAY_SIZE(adcmap_btm_threshold),
+		(param->high_temp),
+		high_output);
+	if (rc)
+		return rc;
+
+	*high_output *= btm_param.dy;
+	do_div(*high_output, btm_param.adc_vref);
+	*high_output += btm_param.adc_gnd;
+
+	low_threshold = (uint32_t *) low_output;
+	high_threshold = (uint32_t *) high_output;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(qpnp_adc_btm_scaler);
+
 int32_t qpnp_vadc_check_result(int32_t *data)
 {
 	if (*data < QPNP_VADC_MIN_ADC_CODE)
@@ -729,7 +847,7 @@
 		return -ENOMEM;
 	}
 	adc_channel_list = devm_kzalloc(&spmi->dev,
-		sizeof(struct qpnp_vadc_amux) * count_adc_channel_list,
+		((sizeof(struct qpnp_vadc_amux)) * count_adc_channel_list),
 				GFP_KERNEL);
 	if (!adc_channel_list) {
 		dev_err(&spmi->dev, "Unable to allocate memory\n");
@@ -842,8 +960,9 @@
 	adc_qpnp->offset = res->start;
 
 	/* Register the ADC peripheral interrupt */
-	adc_qpnp->adc_irq = spmi_get_irq(spmi, 0, 0);
-	if (adc_qpnp->adc_irq < 0) {
+	adc_qpnp->adc_irq_eoc = spmi_get_irq_byname(spmi, NULL,
+						"eoc-int-en-set");
+	if (adc_qpnp->adc_irq_eoc < 0) {
 		pr_err("Invalid irq\n");
 		return -ENXIO;
 	}
diff --git a/drivers/hwmon/qpnp-adc-current.c b/drivers/hwmon/qpnp-adc-current.c
index aa375d7..b5ee104 100644
--- a/drivers/hwmon/qpnp-adc-current.c
+++ b/drivers/hwmon/qpnp-adc-current.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
@@ -54,28 +54,6 @@
 #define QPNP_STATUS2_CONV_SEQ_TIMEOUT_STS		BIT(0)
 #define QPNP_CONV_TIMEOUT_ERR				2
 
-#define QPNP_INT_RT_ST					0x10
-#define QPNP_INT_SET_TYPE				0x11
-#define QPNP_INT_SET_TYPE_LOW_THR_INT_SET		BIT(4)
-#define QPNP_INT_SET_TYPE_HIGH_THR_INT_SET		BIT(3)
-#define QPNP_INT_SET_TYPE_CONV_SEQ_TIMEOUT_INT_SET	BIT(2)
-#define QPNP_INT_SET_TYPE_FIFO_NOT_EMPTY_INT_SET	BIT(1)
-#define QPNP_INT_SET_TYPE_EOC_SET_INT_TYPE		BIT(0)
-#define QPNP_INT_POLARITY_HIGH				0x12
-#define QPNP_INT_POLARITY_LOW				0x13
-#define QPNP_INT_EN_SET					0x15
-#define QPNP_INT_EN_SET_LOW_THR_INT_EN_SET		BIT(4)
-#define QPNP_INT_EN_SET_HIGH_THR_INT_EN_SET		BIT(3)
-#define QPNP_INT_EN_SET_CONV_SEQ_TIMEOUT_INT_EN		BIT(2)
-#define QPNP_INT_EN_SET_FIFO_NOT_EMPTY_INT_EN		BIT(1)
-#define QPNP_INT_EN_SET_EOC_INT_EN_SET			BIT(0)
-#define QPNP_INT_CLR					0x16
-#define QPNP_INT_CLR_LOW_THR_INT_EN_CLR			BIT(4)
-#define QPNP_INT_CLR_HIGH_THR_INT_EN_CLKR		BIT(3)
-#define QPNP_INT_CLR_CONV_SEQ_TIMEOUT_INT_EN		BIT(2)
-#define QPNP_INT_CLR_FIFO_NOT_EMPTY_INT_EN		BIT(1)
-#define QPNP_INT_CLR_EOC_INT_EN_CLR			BIT(0)
-#define QPNP_INT_CLR_MASK				0x1f
 #define QPNP_IADC_MODE_CTL				0x40
 #define QPNP_OP_MODE_SHIFT				4
 #define QPNP_USE_BMS_DATA				BIT(4)
@@ -111,6 +89,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 +112,28 @@
 #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
+#define QPNP_ADC_COMPLETION_TIMEOUT			HZ
 
 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];
 };
 
@@ -173,47 +171,9 @@
 	return 0;
 }
 
-static int32_t qpnp_iadc_configure_interrupt(void)
-{
-	int rc = 0;
-	u8 data = 0;
-
-	/* Configure interrupt as an Edge trigger */
-	rc = qpnp_iadc_write_reg(QPNP_INT_SET_TYPE,
-					QPNP_INT_CLR_MASK);
-	if (rc < 0) {
-		pr_err("%s Interrupt configure failed\n", __func__);
-		return rc;
-	}
-
-	/* Configure interrupt for rising edge trigger */
-	rc = qpnp_iadc_write_reg(QPNP_INT_POLARITY_HIGH,
-					QPNP_INT_CLR_MASK);
-	if (rc < 0) {
-		pr_err("%s Rising edge trigger configure failed\n", __func__);
-		return rc;
-	}
-
-	/* Disable low level interrupt triggering */
-	data = QPNP_INT_CLR_MASK;
-	rc = qpnp_iadc_write_reg(QPNP_INT_POLARITY_LOW,
-					(~data & QPNP_INT_CLR_MASK));
-	if (rc < 0) {
-		pr_err("%s Setting level low to disable failed\n", __func__);
-		return rc;
-	}
-
-	return 0;
-}
-
 static void trigger_iadc_completion(struct work_struct *work)
 {
 	struct qpnp_iadc_drv *iadc = qpnp_iadc;
-	int rc;
-
-	rc = qpnp_iadc_write_reg(QPNP_INT_CLR, QPNP_INT_CLR_MASK);
-	if (rc < 0)
-		pr_err("qpnp iadc interrupt mask failed with %d\n", rc);
 
 	complete(&iadc->adc->adc_rslt_completion);
 
@@ -253,9 +213,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 +231,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;
@@ -293,13 +256,6 @@
 
 	qpnp_iadc_conv_req = QPNP_IADC_CONV_REQ;
 
-	rc = qpnp_iadc_write_reg(QPNP_INT_EN_SET,
-					QPNP_INT_EN_SET_EOC_INT_EN_SET);
-	if (rc < 0) {
-		pr_err("qpnp adc configure error for interrupt setup\n");
-		return rc;
-	}
-
 	rc = qpnp_iadc_write_reg(QPNP_IADC_MODE_CTL, qpnp_iadc_mode_reg);
 	if (rc) {
 		pr_err("qpnp adc read adc failed with %d\n", rc);
@@ -344,9 +300,24 @@
 		return rc;
 	}
 
-	wait_for_completion(&iadc->adc->adc_rslt_completion);
+	rc = wait_for_completion_timeout(&iadc->adc->adc_rslt_completion,
+				QPNP_ADC_COMPLETION_TIMEOUT);
+	if (!rc) {
+		u8 status1 = 0;
+		rc = qpnp_iadc_read_reg(QPNP_STATUS1, &status1);
+		if (rc < 0)
+			return rc;
+		status1 &= (QPNP_STATUS1_REQ_STS | QPNP_STATUS1_EOC);
+		if (status1 == QPNP_STATUS1_EOC)
+			pr_debug("End of conversion status set\n");
+		else {
+			pr_err("EOC interrupt not received\n");
+			return -EINVAL;
+		}
+	}
 
-	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 +326,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 +459,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 +562,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 +603,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 +621,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) {
@@ -570,14 +695,14 @@
 		return -EINVAL;
 	}
 
-	rc = devm_request_irq(&spmi->dev, iadc->adc->adc_irq,
+	rc = devm_request_irq(&spmi->dev, iadc->adc->adc_irq_eoc,
 				qpnp_iadc_isr,
 	IRQF_TRIGGER_RISING, "qpnp_iadc_interrupt", iadc);
 	if (rc) {
 		dev_err(&spmi->dev, "failed to request adc irq\n");
 		return rc;
 	} else
-		enable_irq_wake(iadc->adc->adc_irq);
+		enable_irq_wake(iadc->adc->adc_irq_eoc);
 
 	iadc->iadc_init_calib = false;
 	dev_set_drvdata(&spmi->dev, iadc);
@@ -590,12 +715,24 @@
 	}
 	iadc->iadc_hwmon = hwmon_device_register(&iadc->adc->spmi->dev);
 
-	rc = qpnp_iadc_configure_interrupt();
+	rc = qpnp_iadc_version_check();
 	if (rc) {
-		dev_err(&spmi->dev, "failed to configure interrupt");
+		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..b71c998 100644
--- a/drivers/hwmon/qpnp-adc-voltage.c
+++ b/drivers/hwmon/qpnp-adc-voltage.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
@@ -54,18 +54,6 @@
 #define QPNP_VADC_STATUS2_CONV_SEQ_STATE_SHIFT			4
 #define QPNP_VADC_CONV_TIMEOUT_ERR				2
 
-#define QPNP_VADC_INT_SET_TYPE					0x11
-#define QPNP_VADC_INT_POLARITY_HIGH				0x12
-#define QPNP_VADC_INT_POLARITY_LOW				0x13
-#define QPNP_VADC_INT_LATCHED_CLR				0x14
-#define QPNP_VADC_INT_EN_SET					0x15
-#define QPNP_VADC_INT_CLR					0x16
-#define QPNP_VADC_INT_LOW_THR_BIT				BIT(4)
-#define QPNP_VADC_INT_HIGH_THR_BIT				BIT(3)
-#define QPNP_VADC_INT_CONV_SEQ_TIMEOUT_BIT			BIT(2)
-#define QPNP_VADC_INT_FIFO_NOT_EMPTY_BIT			BIT(1)
-#define QPNP_VADC_INT_EOC_BIT					BIT(0)
-#define QPNP_VADC_INT_CLR_MASK					0x1f
 #define QPNP_VADC_MODE_CTL					0x40
 #define QPNP_VADC_OP_MODE_SHIFT					4
 #define QPNP_VADC_VREF_XO_THM_FORCE				BIT(2)
@@ -101,6 +89,7 @@
 #define QPNP_VADC_CONV_TIMEOUT_ERR				2
 #define QPNP_VADC_CONV_TIME_MIN					2000
 #define QPNP_VADC_CONV_TIME_MAX					2100
+#define QPNP_ADC_COMPLETION_TIMEOUT				HZ
 
 struct qpnp_vadc_drv {
 	struct qpnp_adc_drv		*adc;
@@ -156,39 +145,6 @@
 	return 0;
 }
 
-static int32_t qpnp_vadc_configure_interrupt(void)
-{
-	int rc = 0;
-	u8 data = 0;
-
-	/* Configure interrupt as an Edge trigger */
-	rc = qpnp_vadc_write_reg(QPNP_VADC_INT_SET_TYPE,
-					QPNP_VADC_INT_CLR_MASK);
-	if (rc < 0) {
-		pr_err("%s Interrupt configure failed\n", __func__);
-		return rc;
-	}
-
-	/* Configure interrupt for rising edge trigger */
-	rc = qpnp_vadc_write_reg(QPNP_VADC_INT_POLARITY_HIGH,
-					QPNP_VADC_INT_CLR_MASK);
-	if (rc < 0) {
-		pr_err("%s Rising edge trigger configure failed\n", __func__);
-		return rc;
-	}
-
-	/* Disable low level interrupt triggering */
-	data = QPNP_VADC_INT_CLR_MASK;
-	rc = qpnp_vadc_write_reg(QPNP_VADC_INT_POLARITY_LOW,
-					(~data & QPNP_VADC_INT_CLR_MASK));
-	if (rc < 0) {
-		pr_err("%s Setting level low to disable failed\n", __func__);
-		return rc;
-	}
-
-	return 0;
-}
-
 static int32_t qpnp_vadc_enable(bool state)
 {
 	int rc = 0;
@@ -222,13 +178,6 @@
 	u8 mode_ctrl = 0;
 	int rc = 0;
 
-	rc = qpnp_vadc_write_reg(QPNP_VADC_INT_EN_SET,
-			QPNP_VADC_INT_EOC_BIT);
-	if (rc < 0) {
-		pr_err("Configure error for interrupt setup\n");
-		return rc;
-	}
-
 	/* Mode selection */
 	mode_ctrl = chan_prop->mode_sel << QPNP_VADC_OP_MODE_SHIFT;
 	rc = qpnp_vadc_write_reg(QPNP_VADC_MODE_CTL, mode_ctrl);
@@ -389,11 +338,6 @@
 static void qpnp_vadc_work(struct work_struct *work)
 {
 	struct qpnp_vadc_drv *vadc = qpnp_vadc;
-	int rc;
-
-	rc = qpnp_vadc_write_reg(QPNP_VADC_INT_CLR, QPNP_VADC_INT_EOC_BIT);
-	if (rc)
-		pr_err("qpnp_vadc clear mask interrupt failed with %d\n", rc);
 
 	complete(&vadc->adc->adc_rslt_completion);
 
@@ -565,6 +509,39 @@
 	return rc;
 }
 
+int32_t qpnp_get_vadc_gain_and_offset(struct qpnp_vadc_linear_graph *param,
+				enum qpnp_adc_calib_type calib_type)
+{
+
+	struct qpnp_vadc_drv *vadc = qpnp_vadc;
+
+	switch (calib_type) {
+	case CALIB_RATIOMETRIC:
+	param->dy =
+	vadc->adc->amux_prop->chan_prop->adc_graph[CALIB_RATIOMETRIC].dy;
+	param->dx =
+	vadc->adc->amux_prop->chan_prop->adc_graph[CALIB_RATIOMETRIC].dx;
+	param->adc_vref = vadc->adc->adc_prop->adc_vdd_reference;
+	param->adc_gnd =
+	vadc->adc->amux_prop->chan_prop->adc_graph[CALIB_RATIOMETRIC].adc_gnd;
+	break;
+	case CALIB_ABSOLUTE:
+	param->dy =
+	vadc->adc->amux_prop->chan_prop->adc_graph[CALIB_ABSOLUTE].dy;
+	param->dx =
+	vadc->adc->amux_prop->chan_prop->adc_graph[CALIB_ABSOLUTE].dx;
+	param->adc_vref = vadc->adc->adc_prop->adc_vdd_reference;
+	param->adc_gnd =
+	vadc->adc->amux_prop->chan_prop->adc_graph[CALIB_ABSOLUTE].adc_gnd;
+	break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(qpnp_get_vadc_gain_and_offset);
+
 int32_t qpnp_vadc_is_ready(void)
 {
 	struct qpnp_vadc_drv *vadc = qpnp_vadc;
@@ -606,8 +583,11 @@
 			!= channel || dt_index > vadc->max_channels_available)
 		dt_index++;
 
-	if (dt_index > vadc->max_channels_available)
+	if (dt_index > vadc->max_channels_available) {
+		pr_err("not a valid VADC channel\n");
+		rc = -EINVAL;
 		goto fail_unlock;
+	}
 
 	vadc->adc->amux_prop->decimation =
 			vadc->adc->adc_channels[dt_index].adc_decimation;
@@ -635,7 +615,22 @@
 		goto fail_unlock;
 	}
 
-	wait_for_completion(&vadc->adc->adc_rslt_completion);
+	rc = wait_for_completion_timeout(&vadc->adc->adc_rslt_completion,
+					QPNP_ADC_COMPLETION_TIMEOUT);
+	if (!rc) {
+		u8 status1 = 0;
+		rc = qpnp_vadc_read_reg(QPNP_VADC_STATUS1, &status1);
+		if (rc < 0)
+			goto fail_unlock;
+		status1 &= (QPNP_VADC_STATUS1_REQ_STS | QPNP_VADC_STATUS1_EOC);
+		if (status1 == QPNP_VADC_STATUS1_EOC)
+			pr_debug("End of conversion status set\n");
+		else {
+			pr_err("EOC interrupt not received\n");
+			rc = -EINVAL;
+			goto fail_unlock;
+		}
+	}
 
 	if (trigger_channel < ADC_SEQ_NONE) {
 		rc = qpnp_vadc_read_status(vadc->adc->amux_prop->mode_sel);
@@ -714,9 +709,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) {
@@ -781,7 +776,7 @@
 		return rc;
 	}
 
-	rc = devm_request_irq(&spmi->dev, vadc->adc->adc_irq,
+	rc = devm_request_irq(&spmi->dev, vadc->adc->adc_irq_eoc,
 				qpnp_vadc_isr, IRQF_TRIGGER_RISING,
 				"qpnp_vadc_interrupt", vadc);
 	if (rc) {
@@ -789,7 +784,7 @@
 			"failed to request adc irq with error %d\n", rc);
 		return rc;
 	} else {
-		enable_irq_wake(vadc->adc->adc_irq);
+		enable_irq_wake(vadc->adc->adc_irq_eoc);
 	}
 
 	qpnp_vadc = vadc;
@@ -797,25 +792,14 @@
 	rc = qpnp_vadc_init_hwmon(spmi);
 	if (rc) {
 		dev_err(&spmi->dev, "failed to initialize qpnp hwmon adc\n");
-		goto fail_free_irq;
+		return rc;
 	}
 	vadc->vadc_hwmon = hwmon_device_register(&vadc->adc->spmi->dev);
 	vadc->vadc_init_calib = false;
-	vadc->vadc_initialized = true;
 	vadc->max_channels_available = count_adc_channel_list;
-
-	rc = qpnp_vadc_configure_interrupt();
-	if (rc) {
-		dev_err(&spmi->dev, "failed to configure interrupt");
-		goto fail_free_irq;
-	}
+	vadc->vadc_initialized = true;
 
 	return 0;
-
-fail_free_irq:
-	free_irq(vadc->adc->adc_irq, vadc);
-
-	return rc;
 }
 
 static int __devexit qpnp_vadc_remove(struct spmi_device *spmi)
@@ -830,7 +814,6 @@
 			&vadc->sens_attr[i].dev_attr);
 		i++;
 	}
-	free_irq(vadc->adc->adc_irq, vadc);
 	vadc->vadc_initialized = false;
 	dev_set_drvdata(&spmi->dev, NULL);
 
diff --git a/drivers/input/misc/lis3dh_acc.c b/drivers/input/misc/lis3dh_acc.c
index af96d3f..cc4ee9f 100644
--- a/drivers/input/misc/lis3dh_acc.c
+++ b/drivers/input/misc/lis3dh_acc.c
@@ -1086,26 +1086,26 @@
 
 static struct device_attribute attributes[] = {
 
-	__ATTR(pollrate_ms, 0666, attr_get_polling_rate,
+	__ATTR(pollrate_ms, 0664, attr_get_polling_rate,
 			attr_set_polling_rate),
-	__ATTR(range, 0666, attr_get_range, attr_set_range),
-	__ATTR(enable, 0666, attr_get_enable, attr_set_enable),
-	__ATTR(int1_config, 0666, attr_get_intconfig1, attr_set_intconfig1),
-	__ATTR(int1_duration, 0666, attr_get_duration1, attr_set_duration1),
-	__ATTR(int1_threshold, 0666, attr_get_thresh1, attr_set_thresh1),
+	__ATTR(range, 0664, attr_get_range, attr_set_range),
+	__ATTR(enable, 0664, attr_get_enable, attr_set_enable),
+	__ATTR(int1_config, 0664, attr_get_intconfig1, attr_set_intconfig1),
+	__ATTR(int1_duration, 0664, attr_get_duration1, attr_set_duration1),
+	__ATTR(int1_threshold, 0664, attr_get_thresh1, attr_set_thresh1),
 	__ATTR(int1_source, 0444, attr_get_source1, NULL),
-	__ATTR(click_config, 0666, attr_get_click_cfg, attr_set_click_cfg),
+	__ATTR(click_config, 0664, attr_get_click_cfg, attr_set_click_cfg),
 	__ATTR(click_source, 0444, attr_get_click_source, NULL),
-	__ATTR(click_threshold, 0666, attr_get_click_ths, attr_set_click_ths),
-	__ATTR(click_timelimit, 0666, attr_get_click_tlim,
+	__ATTR(click_threshold, 0664, attr_get_click_ths, attr_set_click_ths),
+	__ATTR(click_timelimit, 0664, attr_get_click_tlim,
 			attr_set_click_tlim),
-	__ATTR(click_timelatency, 0666, attr_get_click_tlat,
+	__ATTR(click_timelatency, 0664, attr_get_click_tlat,
 							attr_set_click_tlat),
-	__ATTR(click_timewindow, 0666, attr_get_click_tw, attr_set_click_tw),
+	__ATTR(click_timewindow, 0664, attr_get_click_tw, attr_set_click_tw),
 
 #ifdef DEBUG
-	__ATTR(reg_value, 0666, attr_reg_get, attr_reg_set),
-	__ATTR(reg_addr, 0222, NULL, attr_addr_set),
+	__ATTR(reg_value, 0664, attr_reg_get, attr_reg_set),
+	__ATTR(reg_addr, 0220, NULL, attr_addr_set),
 #endif
 };
 
diff --git a/drivers/input/misc/mpu3050.c b/drivers/input/misc/mpu3050.c
index 04a7598..db6f93c 100644
--- a/drivers/input/misc/mpu3050.c
+++ b/drivers/input/misc/mpu3050.c
@@ -288,7 +288,7 @@
 
 static struct device_attribute attributes[] = {
 
-	__ATTR(pollrate_ms, 0666,
+	__ATTR(pollrate_ms, 0664,
 		mpu3050_attr_get_polling_rate,
 		mpu3050_attr_set_polling_rate),
 };
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/iommu/Kconfig b/drivers/iommu/Kconfig
index ec3429b..332138c 100644
--- a/drivers/iommu/Kconfig
+++ b/drivers/iommu/Kconfig
@@ -25,6 +25,17 @@
 
 	  If unsure, say N here.
 
+# MSM IOMMU CPU-GPU sync programming support
+config MSM_IOMMU_GPU_SYNC
+	bool "MSM IOMMU CPU-GPU Sync Support"
+	depends on (ARCH_MSM8X60 || ARCH_MSM8960 || ARCH_APQ8064 || ARCH_MSM8930) && MSM_IOMMU && MSM_REMOTE_SPINLOCK_SFPB
+	help
+	  Say Y here if you want to synchronize access to IOMMU configuration
+	  port between CPU and GPU. CPU will grab a remote spinlock before
+	  accessing IOMMU configuration registers and GPU will do the same.
+
+	  If unsure, say N here.
+
 config IOMMU_PGTABLES_L2
 	bool "Allow SMMU page tables in the L2 cache (Experimental)"
 	depends on MSM_IOMMU && MMU && SMP && CPU_DCACHE_DISABLE=n
diff --git a/drivers/iommu/msm_iommu-v2.c b/drivers/iommu/msm_iommu-v2.c
index 9d88fdd..425eb8a 100644
--- a/drivers/iommu/msm_iommu-v2.c
+++ b/drivers/iommu/msm_iommu-v2.c
@@ -148,6 +148,9 @@
 	return ret;
 }
 
+/*
+ * May only be called for non-secure iommus
+ */
 static void __reset_iommu(void __iomem *base)
 {
 	int i, smt_size;
@@ -170,6 +173,9 @@
 	mb();
 }
 
+/*
+ * May only be called for non-secure iommus
+ */
 static void __program_iommu(void __iomem *base,
 			    struct msm_iommu_bfb_settings *bfb_settings)
 {
@@ -223,14 +229,14 @@
 
 static void __program_context(void __iomem *base, int ctx, int ncb,
 				phys_addr_t pgtable, int redirect,
-				u32 *sids, int len)
+				u32 *sids, int len, bool is_secure)
 {
 	unsigned int prrr, nmrr;
 	unsigned int pn;
 	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);
@@ -266,41 +272,44 @@
 		SET_CB_TTBR0_RGN(base, ctx, 1);   /* WB, WA */
 	}
 
-	/* Program the M2V tables for this context */
-	for (i = 0; i < len / sizeof(*sids); i++) {
-		for (; num < smt_size; num++)
-			if (GET_SMR_VALID(base, num) == 0)
-				break;
-		BUG_ON(num >= smt_size);
+	if (!is_secure) {
+		smt_size = GET_IDR0_NUMSMRG(base);
+		/* Program the M2V tables for this context */
+		for (i = 0; i < len / sizeof(*sids); i++) {
+			for (; num < smt_size; num++)
+				if (GET_SMR_VALID(base, num) == 0)
+					break;
+			BUG_ON(num >= smt_size);
 
-		SET_SMR_VALID(base, num, 1);
-		SET_SMR_MASK(base, num, 0);
-		SET_SMR_ID(base, num, sids[i]);
+			SET_SMR_VALID(base, num, 1);
+			SET_SMR_MASK(base, num, 0);
+			SET_SMR_ID(base, num, sids[i]);
 
-		SET_S2CR_N(base, num, 0);
-		SET_S2CR_CBNDX(base, num, ctx);
-		SET_S2CR_MEMATTR(base, num, 0x0A);
-		/* Set security bit override to be Non-secure */
-		SET_S2CR_NSCFG(base, num, 3);
+			SET_S2CR_N(base, num, 0);
+			SET_S2CR_CBNDX(base, num, ctx);
+			SET_S2CR_MEMATTR(base, num, 0x0A);
+			/* Set security bit override to be Non-secure */
+			SET_S2CR_NSCFG(base, num, 3);
+		}
+		SET_CBAR_N(base, ctx, 0);
+
+		/* Stage 1 Context with Stage 2 bypass */
+		SET_CBAR_TYPE(base, ctx, 1);
+
+		/* Route page faults to the non-secure interrupt */
+		SET_CBAR_IRPTNDX(base, ctx, 1);
+
+		/* Set VMID to non-secure HLOS */
+		SET_CBAR_VMID(base, ctx, 3);
+
+		/* Bypass is treated as inner-shareable */
+		SET_CBAR_BPSHCFG(base, ctx, 2);
+
+		/* Do not downgrade memory attributes */
+		SET_CBAR_MEMATTR(base, ctx, 0x0A);
+
 	}
 
-	SET_CBAR_N(base, ctx, 0);
-
-	/* Stage 1 Context with Stage 2 bypass */
-	SET_CBAR_TYPE(base, ctx, 1);
-
-	/* Route page faults to the non-secure interrupt */
-	SET_CBAR_IRPTNDX(base, ctx, 1);
-
-	/* Set VMID to non-secure HLOS */
-	SET_CBAR_VMID(base, ctx, 3);
-
-	/* Bypass is treated as inner-shareable */
-	SET_CBAR_BPSHCFG(base, ctx, 2);
-
-	/* Do not downgrade memory attributes */
-	SET_CBAR_MEMATTR(base, ctx, 0x0A);
-
        /* Find if this page table is used elsewhere, and re-use ASID */
 	found = 0;
 	for (i = 0; i < ncb; i++)
@@ -399,6 +408,7 @@
 	struct msm_iommu_ctx_drvdata *ctx_drvdata;
 	struct msm_iommu_ctx_drvdata *tmp_drvdata;
 	int ret;
+	int is_secure;
 
 	mutex_lock(&msm_iommu_lock);
 
@@ -426,6 +436,8 @@
 			goto fail;
 		}
 
+	is_secure = iommu_drvdata->sec_id != -1;
+
 	ret = regulator_enable(iommu_drvdata->gdsc);
 	if (ret)
 		goto fail;
@@ -436,13 +448,25 @@
 		goto fail;
 	}
 
-	if (!msm_iommu_ctx_attached(dev->parent))
-		__program_iommu(iommu_drvdata->base,
+	if (!msm_iommu_ctx_attached(dev->parent)) {
+		if (!is_secure) {
+			__program_iommu(iommu_drvdata->base,
 				iommu_drvdata->bfb_settings);
+		} else {
+			ret = msm_iommu_sec_program_iommu(
+				iommu_drvdata->sec_id);
+			if (ret) {
+				regulator_disable(iommu_drvdata->gdsc);
+				__disable_clocks(iommu_drvdata);
+				goto fail;
+			}
+		}
+	}
 
 	__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);
+		priv->pt.redirect, ctx_drvdata->sids, ctx_drvdata->nsid,
+		is_secure);
 	__disable_clocks(iommu_drvdata);
 
 	list_add(&(ctx_drvdata->attached_elm), &priv->list_attached);
@@ -460,6 +484,7 @@
 	struct msm_iommu_drvdata *iommu_drvdata;
 	struct msm_iommu_ctx_drvdata *ctx_drvdata;
 	int ret;
+	int is_secure;
 
 	mutex_lock(&msm_iommu_lock);
 	priv = domain->priv;
@@ -475,11 +500,14 @@
 	if (ret)
 		goto fail;
 
+	is_secure = iommu_drvdata->sec_id != -1;
+
 	SET_TLBIASID(iommu_drvdata->base, ctx_drvdata->num,
 		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);
+	if (!is_secure)
+		__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 bf173b3..4c72df7 100644
--- a/drivers/iommu/msm_iommu.c
+++ b/drivers/iommu/msm_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
@@ -29,6 +29,7 @@
 
 #include <mach/iommu_hw-8xxx.h>
 #include <mach/iommu.h>
+#include <mach/msm_smsm.h>
 
 #define MRC(reg, processor, op1, crn, crm, op2)				\
 __asm__ __volatile__ (							\
@@ -66,6 +67,69 @@
 
 DEFINE_MUTEX(msm_iommu_lock);
 
+/**
+ * Remote spinlock implementation based on Peterson's algorithm to be used
+ * to synchronize IOMMU config port access between CPU and GPU.
+ * This implements Process 0 of the spin lock algorithm. GPU implements
+ * Process 1. Flag and turn is stored in shared memory to allow GPU to
+ * access these.
+ */
+struct msm_iommu_remote_lock {
+	int initialized;
+	struct remote_iommu_petersons_spinlock *lock;
+};
+
+static struct msm_iommu_remote_lock msm_iommu_remote_lock;
+
+#ifdef CONFIG_MSM_IOMMU_GPU_SYNC
+static void _msm_iommu_remote_spin_lock_init(void)
+{
+	msm_iommu_remote_lock.lock = smem_alloc(SMEM_SPINLOCK_ARRAY, 32);
+	memset(msm_iommu_remote_lock.lock, 0,
+			sizeof(*msm_iommu_remote_lock.lock));
+}
+
+void msm_iommu_remote_p0_spin_lock(void)
+{
+	msm_iommu_remote_lock.lock->flag[PROC_APPS] = 1;
+	msm_iommu_remote_lock.lock->turn = 1;
+
+	smp_mb();
+
+	while (msm_iommu_remote_lock.lock->flag[PROC_GPU] == 1 &&
+	       msm_iommu_remote_lock.lock->turn == 1)
+		cpu_relax();
+}
+
+void msm_iommu_remote_p0_spin_unlock(void)
+{
+	smp_mb();
+
+	msm_iommu_remote_lock.lock->flag[PROC_APPS] = 0;
+}
+#endif
+
+inline void msm_iommu_mutex_lock(void)
+{
+	mutex_lock(&msm_iommu_lock);
+}
+
+inline void msm_iommu_mutex_unlock(void)
+{
+	mutex_unlock(&msm_iommu_lock);
+}
+
+void *msm_iommu_lock_initialize(void)
+{
+	mutex_lock(&msm_iommu_lock);
+	if (!msm_iommu_remote_lock.initialized) {
+		msm_iommu_remote_lock_init();
+		msm_iommu_remote_lock.initialized = 1;
+	}
+	mutex_unlock(&msm_iommu_lock);
+	return msm_iommu_remote_lock.lock;
+}
+
 struct msm_priv {
 	unsigned long *pgtable;
 	int redirect;
@@ -116,12 +180,17 @@
 		if (ret)
 			goto fail;
 
+		msm_iommu_remote_spin_lock();
+
 		asid = GET_CONTEXTIDR_ASID(iommu_drvdata->base,
 					   ctx_drvdata->num);
 
 		SET_TLBIVA(iommu_drvdata->base, ctx_drvdata->num,
 			   asid | (va & TLBIVA_VA));
 		mb();
+
+		msm_iommu_remote_spin_unlock();
+
 		__disable_clocks(iommu_drvdata);
 	}
 fail:
@@ -148,11 +217,16 @@
 		if (ret)
 			goto fail;
 
+		msm_iommu_remote_spin_lock();
+
 		asid = GET_CONTEXTIDR_ASID(iommu_drvdata->base,
 					   ctx_drvdata->num);
 
 		SET_TLBIASID(iommu_drvdata->base, ctx_drvdata->num, asid);
 		mb();
+
+		msm_iommu_remote_spin_unlock();
+
 		__disable_clocks(iommu_drvdata);
 	}
 fail:
@@ -189,6 +263,9 @@
 {
 	unsigned int prrr, nmrr;
 	int i, j, found;
+
+	msm_iommu_remote_spin_lock();
+
 	__reset_context(base, ctx);
 
 	/* Set up HTW mode */
@@ -278,6 +355,8 @@
 	/* Enable the MMU */
 	SET_M(base, ctx, 1);
 	mb();
+
+	msm_iommu_remote_spin_unlock();
 }
 
 static int msm_iommu_domain_init(struct iommu_domain *domain, int flags)
@@ -417,10 +496,15 @@
 	if (ret)
 		goto fail;
 
+	msm_iommu_remote_spin_lock();
+
 	SET_TLBIASID(iommu_drvdata->base, ctx_dev->num,
 		     GET_CONTEXTIDR_ASID(iommu_drvdata->base, ctx_dev->num));
 
 	__reset_context(iommu_drvdata->base, ctx_dev->num);
+
+	msm_iommu_remote_spin_unlock();
+
 	__disable_clocks(iommu_drvdata);
 	list_del_init(&ctx_drvdata->attached_elm);
 	ctx_drvdata->attached_domain = NULL;
@@ -772,6 +856,55 @@
 		&& (len >= align);
 }
 
+static int check_range(unsigned long *fl_table, unsigned int va,
+				 unsigned int len)
+{
+	unsigned int offset = 0;
+	unsigned long *fl_pte;
+	unsigned long fl_offset;
+	unsigned long *sl_table;
+	unsigned long sl_start, sl_end;
+	int i;
+
+	fl_offset = FL_OFFSET(va);	/* Upper 12 bits */
+	fl_pte = fl_table + fl_offset;	/* int pointers, 4 bytes */
+
+	while (offset < len) {
+		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;
+
+			for (i = sl_start; i < sl_end; i++) {
+				if (sl_table[i] != 0) {
+					pr_err("%08x - %08x already mapped\n",
+						va, va + SZ_4K);
+					return -EBUSY;
+				}
+				offset += SZ_4K;
+				va += SZ_4K;
+			}
+
+
+			sl_start = 0;
+		} else {
+			if (*fl_pte != 0) {
+				pr_err("%08x - %08x already mapped\n",
+				       va, va + SZ_1M);
+				return -EBUSY;
+			}
+			va += SZ_1M;
+			offset += SZ_1M;
+			sl_start = 0;
+		}
+		fl_pte++;
+	}
+	return 0;
+}
+
 static int msm_iommu_map_range(struct iommu_domain *domain, unsigned int va,
 			       struct scatterlist *sg, unsigned int len,
 			       int prot)
@@ -804,6 +937,9 @@
 		ret = -EINVAL;
 		goto fail;
 	}
+	ret = check_range(fl_table, va, len);
+	if (ret)
+		goto fail;
 
 	fl_offset = FL_OFFSET(va);	/* Upper 12 bits */
 	fl_pte = fl_table + fl_offset;	/* int pointers, 4 bytes */
@@ -1031,6 +1167,8 @@
 	if (ret)
 		goto fail;
 
+	msm_iommu_remote_spin_lock();
+
 	SET_V2PPR(base, ctx, va & V2Pxx_VA);
 
 	mb();
@@ -1045,6 +1183,8 @@
 	if (GET_FAULT(base, ctx))
 		ret = 0;
 
+	msm_iommu_remote_spin_unlock();
+
 	__disable_clocks(iommu_drvdata);
 fail:
 	mutex_unlock(&msm_iommu_lock);
@@ -1105,6 +1245,8 @@
 	if (ret)
 		goto fail;
 
+	msm_iommu_remote_spin_lock();
+
 	fsr = GET_FSR(base, num);
 
 	if (fsr) {
@@ -1136,6 +1278,8 @@
 	} else
 		ret = IRQ_NONE;
 
+	msm_iommu_remote_spin_unlock();
+
 	__disable_clocks(drvdata);
 fail:
 	mutex_unlock(&msm_iommu_lock);
@@ -1206,6 +1350,8 @@
 	if (!msm_soc_version_supports_iommu_v1())
 		return -ENODEV;
 
+	msm_iommu_lock_initialize();
+
 	setup_iommu_tex_classes();
 	bus_set_iommu(&platform_bus_type, &msm_iommu_ops);
 	return 0;
diff --git a/drivers/iommu/msm_iommu_dev-v2.c b/drivers/iommu/msm_iommu_dev-v2.c
index ea6c87c..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>
@@ -95,9 +94,8 @@
 	struct device_node *child;
 	int ret = 0;
 
-	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)
@@ -118,20 +116,12 @@
 	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;
@@ -192,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);
@@ -315,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");
@@ -354,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
index a89c4a8..72ec4a6 100644
--- a/drivers/iommu/msm_iommu_sec.c
+++ b/drivers/iommu/msm_iommu_sec.c
@@ -128,7 +128,7 @@
 	return ret;
 }
 
-static int msm_iommu_sec_program_iommu(int sec_id)
+int msm_iommu_sec_program_iommu(int sec_id)
 {
 	struct msm_scm_sec_cfg {
 		unsigned int id;
diff --git a/drivers/leds/leds-pm8xxx.c b/drivers/leds/leds-pm8xxx.c
index 255920e..a641ce9 100644
--- a/drivers/leds/leds-pm8xxx.c
+++ b/drivers/leds/leds-pm8xxx.c
@@ -46,8 +46,8 @@
 /* wled control registers */
 #define WLED_MOD_CTRL_REG		SSBI_REG_ADDR_WLED_CTRL(1)
 #define WLED_MAX_CURR_CFG_REG(n)	SSBI_REG_ADDR_WLED_CTRL(n + 2)
-#define WLED_BRIGHTNESS_CNTL_REG1(n)	SSBI_REG_ADDR_WLED_CTRL(n + 5)
-#define WLED_BRIGHTNESS_CNTL_REG2(n)	SSBI_REG_ADDR_WLED_CTRL(n + 6)
+#define WLED_BRIGHTNESS_CNTL_REG1(n)	SSBI_REG_ADDR_WLED_CTRL((2 * n) + 5)
+#define WLED_BRIGHTNESS_CNTL_REG2(n)	SSBI_REG_ADDR_WLED_CTRL((2 * n) + 6)
 #define WLED_SYNC_REG			SSBI_REG_ADDR_WLED_CTRL(11)
 #define WLED_OVP_CFG_REG		SSBI_REG_ADDR_WLED_CTRL(13)
 #define WLED_BOOST_CFG_REG		SSBI_REG_ADDR_WLED_CTRL(14)
@@ -640,7 +640,7 @@
 	/* program activation delay and maximum current */
 	for (i = 0; i < num_wled_strings; i++) {
 		rc = pm8xxx_readb(led->dev->parent,
-				WLED_MAX_CURR_CFG_REG(i + 2), &val);
+				WLED_MAX_CURR_CFG_REG(i), &val);
 		if (rc) {
 			dev_err(led->dev->parent, "can't read wled max current"
 				" config register rc=%d\n", rc);
@@ -665,7 +665,7 @@
 		val = (val & ~WLED_MAX_CURR_MASK) | led->max_current;
 
 		rc = pm8xxx_writeb(led->dev->parent,
-				WLED_MAX_CURR_CFG_REG(i + 2), val);
+				WLED_MAX_CURR_CFG_REG(i), val);
 		if (rc) {
 			dev_err(led->dev->parent, "can't write wled max current"
 				" config register rc=%d\n", rc);
diff --git a/drivers/leds/leds-qpnp.c b/drivers/leds/leds-qpnp.c
index 13493b8..d658217 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
@@ -21,6 +21,7 @@
 #include <linux/of_platform.h>
 #include <linux/of_device.h>
 #include <linux/spmi.h>
+#include <linux/qpnp/pwm.h>
 
 #define WLED_MOD_EN_REG(base, n)	(base + 0x60 + n*0x10)
 #define WLED_IDAC_DLY_REG(base, n)	(WLED_MOD_EN_REG(base, n) + 0x01)
@@ -68,8 +69,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 +76,85 @@
 #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"
+
+#define RGB_LED_SRC_SEL(base)		(base + 0x45)
+#define RGB_LED_EN_CTL(base)		(base + 0x46)
+#define RGB_LED_ATC_CTL(base)		(base + 0x47)
+
+#define RGB_MAX_LEVEL			LED_FULL
+#define RGB_LED_ENABLE_RED		0x80
+#define RGB_LED_ENABLE_GREEN		0x40
+#define RGB_LED_ENABLE_BLUE		0x20
+#define RGB_LED_SOURCE_VPH_PWR		0x01
+#define RGB_LED_ENABLE_MASK		0xE0
+#define RGB_LED_SRC_MASK		0x03
+#define QPNP_LED_PWM_FLAGS	(PM_PWM_LUT_LOOP | PM_PWM_LUT_RAMP_UP)
+#define	PWM_LUT_MAX_SIZE		63
+#define RGB_LED_DISABLE			0x00
+
 /**
  * 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_RGB_RED,
+	QPNP_ID_RGB_GREEN,
+	QPNP_ID_RGB_BLUE,
+	QPNP_ID_MAX,
 };
 
 /* current boost limit */
@@ -113,18 +185,45 @@
 	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,
+};
+
+enum rgb_mode {
+	RGB_MODE_PWM = 0,
+	RGB_MODE_LPG,
+};
+
 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,
+};
+
+static u8 rgb_pwm_debug_regs[] = {
+	0x45, 0x46, 0x47,
+};
 /**
  *  wled_config_data - wled configuration data
  *  @num_strings - number of wled strings supported
@@ -149,12 +248,57 @@
 };
 
 /**
+ *  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;
+};
+
+/**
+ *  rgb_config_data - rgb configuration data
+ *  @lut_params - lut parameters to be used by pwm driver
+ *  @pwm_device - pwm device
+ *  @pwm_channel - pwm channel to be configured for led
+ *  @pwm_period_us - period for pwm, in us
+ *  @mode - mode the led operates in
+ */
+struct rgb_config_data {
+	struct lut_params	lut_params;
+	struct pwm_device	*pwm_dev;
+	int			pwm_channel;
+	u32			pwm_period_us;
+	struct pwm_duty_cycles	*duty_cycles;
+	u8	mode;
+	u8	enable;
+};
+
+/**
  * 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 +308,11 @@
 	int			id;
 	u16			base;
 	u8			reg;
+	u8			num_leds;
 	spinlock_t		lock;
 	struct wled_config_data *wled_cfg;
+	struct flash_config_data	*flash_cfg;
+	struct rgb_config_data	*rgb_cfg;
 	int			max_current;
 	bool			default_on;
 };
@@ -194,6 +341,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 +434,121 @@
 	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");
+
+	qpnp_dump_regs(led, flash_debug_regs, ARRAY_SIZE(flash_debug_regs));
+
+	return 0;
+}
+
+static int qpnp_rgb_set(struct qpnp_led_data *led)
+{
+	int duty_us;
+	int rc;
+
+	if (led->cdev.brightness) {
+		if (led->rgb_cfg->mode == RGB_MODE_PWM) {
+			duty_us = (led->rgb_cfg->pwm_period_us *
+				led->cdev.brightness) / LED_FULL;
+			rc = pwm_config(led->rgb_cfg->pwm_dev, duty_us,
+					led->rgb_cfg->pwm_period_us);
+			if (rc < 0) {
+				dev_err(&led->spmi_dev->dev, "Failed to " \
+					"configure pwm for new values\n");
+				return rc;
+			}
+		}
+		rc = qpnp_led_masked_write(led,
+			RGB_LED_EN_CTL(led->base),
+			led->rgb_cfg->enable, led->rgb_cfg->enable);
+		if (rc) {
+			dev_err(&led->spmi_dev->dev,
+				"Failed to write led enable reg\n");
+			return rc;
+		}
+		rc = pwm_enable(led->rgb_cfg->pwm_dev);
+	} else {
+		pwm_disable(led->rgb_cfg->pwm_dev);
+		rc = qpnp_led_masked_write(led,
+			RGB_LED_EN_CTL(led->base),
+			led->rgb_cfg->enable, RGB_LED_DISABLE);
+		if (rc) {
+			dev_err(&led->spmi_dev->dev,
+				"Failed to write led enable reg\n");
+			return rc;
+		}
+	}
+
+	qpnp_dump_regs(led, rgb_pwm_debug_regs, ARRAY_SIZE(rgb_pwm_debug_regs));
+
+	return 0;
 }
 
 static void qpnp_led_set(struct led_classdev *led_cdev,
@@ -294,9 +558,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 +570,26 @@
 	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;
+	case QPNP_ID_RGB_RED:
+	case QPNP_ID_RGB_GREEN:
+	case QPNP_ID_RGB_BLUE:
+		rc = qpnp_rgb_set(led);
+		if (rc < 0)
+			dev_err(&led->spmi_dev->dev,
+				"RGB 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 +601,17 @@
 	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;
+	case QPNP_ID_RGB_RED:
+	case QPNP_ID_RGB_GREEN:
+	case QPNP_ID_RGB_BLUE:
+		led->cdev.max_brightness = RGB_MAX_LEVEL;
+		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 +752,190 @@
 	}
 
 	/* 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;
+}
+
+static int __devinit qpnp_rgb_init(struct qpnp_led_data *led)
+{
+	int rc, start_idx, idx_len;
+
+	rc = qpnp_led_masked_write(led, RGB_LED_SRC_SEL(led->base),
+		RGB_LED_SRC_MASK, RGB_LED_SOURCE_VPH_PWR);
+	if (rc) {
+		dev_err(&led->spmi_dev->dev,
+			"Failed to write led source select register\n");
+		return rc;
+	}
+
+	if (led->rgb_cfg->pwm_channel != -1) {
+		led->rgb_cfg->pwm_dev =
+			pwm_request(led->rgb_cfg->pwm_channel,
+						led->cdev.name);
+
+		if (IS_ERR_OR_NULL(led->rgb_cfg->pwm_dev)) {
+			dev_err(&led->spmi_dev->dev,
+				"could not acquire PWM Channel %d, " \
+				"error %ld\n",
+				led->rgb_cfg->pwm_channel,
+				PTR_ERR(led->rgb_cfg->pwm_dev));
+			led->rgb_cfg->pwm_dev = NULL;
+			return -ENODEV;
+		}
+
+		if (led->rgb_cfg->mode == RGB_MODE_LPG) {
+			start_idx =
+			led->rgb_cfg->duty_cycles->start_idx;
+			idx_len =
+			led->rgb_cfg->duty_cycles->num_duty_pcts;
+
+			if (idx_len >= PWM_LUT_MAX_SIZE &&
+					start_idx) {
+				dev_err(&led->spmi_dev->dev,
+					"Wrong LUT size or index\n");
+				return -EINVAL;
+			}
+			if ((start_idx + idx_len) >
+					PWM_LUT_MAX_SIZE) {
+				dev_err(&led->spmi_dev->dev,
+					"Exceed LUT limit\n");
+				return -EINVAL;
+			}
+			rc = pwm_lut_config(led->rgb_cfg->pwm_dev,
+				led->rgb_cfg->pwm_period_us,
+				led->rgb_cfg->duty_cycles->duty_pcts,
+				led->rgb_cfg->lut_params);
+			if (rc < 0) {
+				dev_err(&led->spmi_dev->dev, "Failed to " \
+					"configure pwm LUT\n");
+				return rc;
+			}
+		}
+	} else {
+		dev_err(&led->spmi_dev->dev,
+			"Invalid PWM channel\n");
+		return -EINVAL;
+	}
+
+	/* Initialize led for use in auto trickle charging mode */
+	rc = qpnp_led_masked_write(led, RGB_LED_ATC_CTL(led->base),
+		led->rgb_cfg->enable, led->rgb_cfg->enable);
 
 	return 0;
 }
@@ -478,15 +948,56 @@
 	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;
+	case QPNP_ID_RGB_RED:
+	case QPNP_ID_RGB_GREEN:
+	case QPNP_ID_RGB_BLUE:
+		rc = qpnp_rgb_init(led);
+		if (rc)
+			dev_err(&led->spmi_dev->dev,
+				"RGB initialize failed(%d)\n", rc);
+		break;
 	default:
-		dev_err(led->cdev.dev, "Invalid LED(%d)\n", led->id);
-		rc = -EINVAL;
+		dev_err(&led->spmi_dev->dev, "Invalid LED(%d)\n", led->id);
+		return -EINVAL;
 	}
 
-	return rc;
+	return 0;
+}
+
+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;
 }
 
 /*
@@ -497,9 +1008,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 +1016,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 +1070,342 @@
 	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_get_config_rgb(struct qpnp_led_data *led,
+				struct device_node *node)
+{
+	struct property *prop;
+	int rc, i;
+	u32 val;
+	u8 *temp_cfg;
+
+	led->rgb_cfg = devm_kzalloc(&led->spmi_dev->dev,
+				sizeof(struct rgb_config_data), GFP_KERNEL);
+	if (!led->rgb_cfg) {
+		dev_err(&led->spmi_dev->dev, "Unable to allocate memory\n");
+		return -ENOMEM;
+	}
+
+	if (led->id == QPNP_ID_RGB_RED)
+		led->rgb_cfg->enable = RGB_LED_ENABLE_RED;
+	else if (led->id == QPNP_ID_RGB_GREEN)
+		led->rgb_cfg->enable = RGB_LED_ENABLE_GREEN;
+	else if (led->id == QPNP_ID_RGB_BLUE)
+		led->rgb_cfg->enable = RGB_LED_ENABLE_BLUE;
+	else
+		return -EINVAL;
+
+	rc = of_property_read_u32(node, "qcom,mode", &val);
+	if (!rc)
+		led->rgb_cfg->mode = (u8) val;
+	else
+		return rc;
+
+	rc = of_property_read_u32(node, "qcom,pwm-channel", &val);
+	if (!rc)
+		led->rgb_cfg->pwm_channel = (u8) val;
+	else
+		return rc;
+
+	rc = of_property_read_u32(node, "qcom,pwm-us", &val);
+	if (!rc)
+		led->rgb_cfg->pwm_period_us = val;
+	else
+		return rc;
+
+	if (led->rgb_cfg->mode == RGB_MODE_LPG) {
+		led->rgb_cfg->duty_cycles =
+			devm_kzalloc(&led->spmi_dev->dev,
+			sizeof(struct pwm_duty_cycles), GFP_KERNEL);
+		if (!led->rgb_cfg->duty_cycles) {
+			dev_err(&led->spmi_dev->dev,
+				"Unable to allocate memory\n");
+			return -ENOMEM;
+		}
+
+		rc = of_property_read_u32(node, "qcom,duty-ms", &val);
+		if (!rc)
+			led->rgb_cfg->duty_cycles->duty_ms = (u8) val;
+		else
+			return rc;
+
+		prop = of_find_property(node, "qcom,duty-pcts",
+			&led->rgb_cfg->duty_cycles->num_duty_pcts);
+		if (!prop) {
+			dev_err(&led->spmi_dev->dev, "Looking up property " \
+				"node qcom,duty-pcts failed\n");
+			return -ENODEV;
+		} else if (!led->rgb_cfg->duty_cycles->num_duty_pcts) {
+			dev_err(&led->spmi_dev->dev, "Invalid length of " \
+				"duty pcts\n");
+			return -EINVAL;
+		}
+
+		led->rgb_cfg->duty_cycles->duty_pcts =
+			devm_kzalloc(&led->spmi_dev->dev,
+			sizeof(int) * led->rgb_cfg->duty_cycles->num_duty_pcts,
+			GFP_KERNEL);
+		if (!led->rgb_cfg->duty_cycles->duty_pcts) {
+			dev_err(&led->spmi_dev->dev,
+				"Unable to allocate memory\n");
+			return -ENOMEM;
+		}
+
+		temp_cfg = devm_kzalloc(&led->spmi_dev->dev,
+				led->rgb_cfg->duty_cycles->num_duty_pcts *
+				sizeof(u8), GFP_KERNEL);
+		if (!temp_cfg) {
+			dev_err(&led->spmi_dev->dev, "Failed to allocate " \
+				"memory for duty pcts\n");
+			return -ENOMEM;
+		}
+
+		memcpy(temp_cfg, prop->value,
+			led->rgb_cfg->duty_cycles->num_duty_pcts);
+
+		for (i = 0; i < led->rgb_cfg->duty_cycles->num_duty_pcts; i++)
+			led->rgb_cfg->duty_cycles->duty_pcts[i] =
+				(int) temp_cfg[i];
+
+		rc = of_property_read_u32(node, "qcom,start-idx", &val);
+		if (!rc) {
+			led->rgb_cfg->lut_params.start_idx = (u8) val;
+			led->rgb_cfg->duty_cycles->start_idx = (u8) val;
+		} else
+			return rc;
+
+		led->rgb_cfg->lut_params.idx_len =
+			led->rgb_cfg->duty_cycles->num_duty_pcts;
+		led->rgb_cfg->lut_params.lut_pause_hi = 0;
+		led->rgb_cfg->lut_params.lut_pause_lo = 0;
+		led->rgb_cfg->lut_params.ramp_step_ms = 255;
+		led->rgb_cfg->lut_params.flags = QPNP_LED_PWM_FLAGS;
+	}
+
+	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 if (strncmp(led_label, "rgb", sizeof("rgb")) == 0) {
+			rc = qpnp_get_config_rgb(led, temp);
+			if (rc < 0) {
+				dev_err(&led->spmi_dev->dev,
+					"Unable to read rgb 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 +1438,4 @@
 MODULE_DESCRIPTION("QPNP LEDs driver");
 MODULE_LICENSE("GPL v2");
 MODULE_ALIAS("leds:leds-qpnp");
+
diff --git a/drivers/media/dvb/dvb-core/demux.h b/drivers/media/dvb/dvb-core/demux.h
index 7190af8..f802a38 100644
--- a/drivers/media/dvb/dvb-core/demux.h
+++ b/drivers/media/dvb/dvb-core/demux.h
@@ -205,6 +205,8 @@
 			dmx_ts_data_ready_cb callback);
 	int (*notify_data_read)(struct dmx_ts_feed *feed,
 			u32 bytes_num);
+	int (*set_tsp_out_format) (struct dmx_ts_feed *feed,
+				enum dmx_tsp_format_t tsp_format);
 };
 
 /*--------------------------------------------------------------------------*/
@@ -336,6 +338,8 @@
 	void* priv;                  /* Pointer to private data of the API client */
 	struct data_buffer dvr_input; /* DVR input buffer */
 
+	struct dentry *debugfs_demux_dir; /* debugfs dir */
+
 	int (*open) (struct dmx_demux* demux);
 	int (*close) (struct dmx_demux* demux);
 	int (*write) (struct dmx_demux *demux, const char *buf, size_t count);
@@ -367,9 +371,6 @@
 	int (*set_tsp_format) (struct dmx_demux *demux,
 				enum dmx_tsp_format_t tsp_format);
 
-	int (*set_tsp_out_format) (struct dmx_demux *demux,
-				enum dmx_tsp_format_t tsp_format);
-
 	int (*set_playback_mode) (struct dmx_demux *demux,
 				 enum dmx_playback_mode_t mode,
 				 dmx_ts_fullness ts_fullness_callback,
diff --git a/drivers/media/dvb/dvb-core/dmxdev.c b/drivers/media/dvb/dvb-core/dmxdev.c
index d9f62ad..507c014 100644
--- a/drivers/media/dvb/dvb-core/dmxdev.c
+++ b/drivers/media/dvb/dvb-core/dmxdev.c
@@ -32,6 +32,8 @@
 #include <linux/wait.h>
 #include <linux/mm.h>
 #include <asm/uaccess.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
 #include "dmxdev.h"
 
 static int debug;
@@ -98,6 +100,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;
@@ -451,6 +465,90 @@
 	return NULL;
 }
 
+static int dvr_input_thread_entry(void *arg)
+{
+	struct dmxdev *dmxdev = arg;
+	struct dvb_ringbuffer *src = &dmxdev->dvr_input_buffer;
+	int ret;
+	size_t todo;
+	size_t split;
+
+	while (1) {
+		/* wait for input */
+		ret = wait_event_interruptible(src->queue,
+						   (!src->data) ||
+					       (dvb_ringbuffer_avail(src)) ||
+					       (src->error != 0) ||
+					       (dmxdev->dvr_in_exit) ||
+					       kthread_should_stop());
+
+		if ((ret < 0) || kthread_should_stop())
+			break;
+
+		spin_lock(&dmxdev->dvr_in_lock);
+
+		if (!src->data || dmxdev->exit || dmxdev->dvr_in_exit) {
+			spin_unlock(&dmxdev->dvr_in_lock);
+			break;
+		}
+
+		if (src->error) {
+			spin_unlock(&dmxdev->dvr_in_lock);
+			wake_up_all(&src->queue);
+			break;
+		}
+
+		dmxdev->dvr_processing_input = 1;
+
+		ret = dvb_ringbuffer_avail(src);
+		todo = ret;
+
+		split = (src->pread + ret > src->size) ?
+				src->size - src->pread :
+				0;
+
+		/*
+		 * In DVR PULL mode, write might block.
+		 * Lock on DVR buffer is released before calling to
+		 * write, if DVR was released meanwhile, dvr_in_exit is
+		 * prompted. Lock is aquired when updating the read pointer
+		 * again to preserve read/write pointers consistancy
+		 */
+		if (split > 0) {
+			spin_unlock(&dmxdev->dvr_in_lock);
+			dmxdev->demux->write(dmxdev->demux,
+						src->data + src->pread,
+						split);
+
+			if (dmxdev->dvr_in_exit)
+				break;
+
+			spin_lock(&dmxdev->dvr_in_lock);
+
+			todo -= split;
+			DVB_RINGBUFFER_SKIP(src, split);
+		}
+
+		spin_unlock(&dmxdev->dvr_in_lock);
+		dmxdev->demux->write(dmxdev->demux,
+					src->data + src->pread, todo);
+
+		if (dmxdev->dvr_in_exit)
+			break;
+
+		spin_lock(&dmxdev->dvr_in_lock);
+
+		DVB_RINGBUFFER_SKIP(src, todo);
+		dmxdev->dvr_processing_input = 0;
+		spin_unlock(&dmxdev->dvr_in_lock);
+
+		wake_up_all(&src->queue);
+	}
+
+	return 0;
+}
+
+
 static int dvb_dvr_open(struct inode *inode, struct file *file)
 {
 	struct dvb_device *dvbdev = file->private_data;
@@ -528,6 +626,17 @@
 		dmxdev->demux->dvr_input.priv_handle = NULL;
 		dmxdev->demux->dvr_input.ringbuff = &dmxdev->dvr_input_buffer;
 		dvbdev->writers--;
+
+		dmxdev->dvr_input_thread =
+			kthread_run(
+				dvr_input_thread_entry,
+				(void *)dmxdev,
+				"dvr_input");
+
+		if (IS_ERR(dmxdev->dvr_input_thread)) {
+			mutex_unlock(&dmxdev->mutex);
+			return -ENOMEM;
+		}
 	}
 
 	dvbdev->users++;
@@ -587,11 +696,11 @@
 			dmxdev->demux->write_cancel(dmxdev->demux);
 
 		/*
-		 * Now flush dvr-in workqueue so that no one
+		 * Now stop dvr-input thread so that no one
 		 * would process data from dvr input buffer any more
 		 * before it gets freed.
 		 */
-		flush_workqueue(dmxdev->dvr_input_workqueue);
+		kthread_stop(dmxdev->dvr_input_thread);
 
 		dvbdev->writers++;
 		dmxdev->demux->disconnect_frontend(dmxdev->demux);
@@ -605,7 +714,8 @@
 			dmxdev->dvr_input_buffer.data = NULL;
 			spin_unlock_irq(&dmxdev->dvr_in_lock);
 
-			if (dmxdev->dvr_buffer_mode == DMX_BUFFER_MODE_INTERNAL)
+			if (dmxdev->dvr_input_buffer_mode ==
+				DMX_BUFFER_MODE_INTERNAL)
 				vfree(mem);
 		}
 
@@ -758,12 +868,7 @@
 		buf += ret;
 
 		mutex_unlock(&dmxdev->mutex);
-
 		wake_up_all(&src->queue);
-
-		if (!work_pending(&dmxdev->dvr_input_work))
-			queue_work(dmxdev->dvr_input_workqueue,
-						&dmxdev->dvr_input_work);
 	}
 
 	return (count - todo) ? (count - todo) : ret;
@@ -788,6 +893,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
@@ -805,87 +917,6 @@
 	return res;
 }
 
-static void dvr_input_work_func(struct work_struct *worker)
-{
-	struct dmxdev *dmxdev =
-		container_of(worker, struct dmxdev, dvr_input_work);
-	struct dvb_ringbuffer *src = &dmxdev->dvr_input_buffer;
-	int ret;
-	size_t todo;
-	size_t split;
-
-	while (1) {
-		/* wait for input */
-		ret = wait_event_interruptible(src->queue,
-						   (!src->data) ||
-					       (dvb_ringbuffer_avail(src)) ||
-					       (src->error != 0) ||
-					       (dmxdev->dvr_in_exit));
-
-		if (ret < 0)
-			break;
-
-		spin_lock(&dmxdev->dvr_in_lock);
-
-		if (!src->data || dmxdev->exit || dmxdev->dvr_in_exit) {
-			spin_unlock(&dmxdev->dvr_in_lock);
-			break;
-		}
-
-		if (src->error) {
-			spin_unlock(&dmxdev->dvr_in_lock);
-			wake_up_all(&src->queue);
-			break;
-		}
-
-		dmxdev->dvr_processing_input = 1;
-
-		ret = dvb_ringbuffer_avail(src);
-		todo = ret;
-
-		split = (src->pread + ret > src->size) ?
-				src->size - src->pread :
-				0;
-
-		/*
-		 * In DVR PULL mode, write might block.
-		 * Lock on DVR buffer is released before calling to
-		 * write, if DVR was released meanwhile, dvr_in_exit is
-		 * prompted. Lock is aquired when updating the read pointer
-		 * again to preserve read/write pointers consistancy
-		 */
-		if (split > 0) {
-			spin_unlock(&dmxdev->dvr_in_lock);
-			dmxdev->demux->write(dmxdev->demux,
-						src->data + src->pread,
-						split);
-
-			if (dmxdev->dvr_in_exit)
-				break;
-
-			spin_lock(&dmxdev->dvr_in_lock);
-
-			todo -= split;
-			DVB_RINGBUFFER_SKIP(src, split);
-		}
-
-		spin_unlock(&dmxdev->dvr_in_lock);
-		dmxdev->demux->write(dmxdev->demux,
-					src->data + src->pread, todo);
-
-		if (dmxdev->dvr_in_exit)
-			break;
-
-		spin_lock(&dmxdev->dvr_in_lock);
-
-		DVB_RINGBUFFER_SKIP(src, todo);
-		dmxdev->dvr_processing_input = 0;
-		spin_unlock(&dmxdev->dvr_in_lock);
-
-		wake_up_all(&src->queue);
-	}
-}
-
 static int dvb_dvr_set_buffer_size(struct dmxdev *dmxdev,
 						unsigned int f_flags,
 						unsigned long size)
@@ -944,6 +975,12 @@
 static int dvb_dvr_set_buffer_mode(struct dmxdev *dmxdev,
 			unsigned int f_flags, enum dmx_buffer_mode mode)
 {
+	struct dvb_ringbuffer *buf;
+	spinlock_t *lock;
+	enum dmx_buffer_mode *buffer_mode;
+	void **buff_handle;
+	void *oldmem;
+
 	if ((mode != DMX_BUFFER_MODE_INTERNAL) &&
 		(mode != DMX_BUFFER_MODE_EXTERNAL))
 		return -EINVAL;
@@ -956,10 +993,42 @@
 		(!dmxdev->demux->map_buffer || !dmxdev->demux->unmap_buffer))
 		return -EINVAL;
 
-	if ((f_flags & O_ACCMODE) == O_RDONLY)
-		dmxdev->dvr_buffer_mode = mode;
-	else
-		dmxdev->dvr_input_buffer_mode = mode;
+	if ((f_flags & O_ACCMODE) == O_RDONLY) {
+		buf = &dmxdev->dvr_buffer;
+		lock = &dmxdev->lock;
+		buffer_mode = &dmxdev->dvr_buffer_mode;
+		buff_handle = &dmxdev->dvr_priv_buff_handle;
+	} else {
+		buf = &dmxdev->dvr_input_buffer;
+		lock = &dmxdev->dvr_in_lock;
+		buffer_mode = &dmxdev->dvr_input_buffer_mode;
+		buff_handle = &dmxdev->demux->dvr_input.priv_handle;
+	}
+
+	if (mode == *buffer_mode)
+		return 0;
+
+	oldmem = buf->data;
+	spin_lock_irq(lock);
+	buf->data = NULL;
+	spin_unlock_irq(lock);
+
+	*buffer_mode = mode;
+
+	if (mode == DMX_BUFFER_MODE_INTERNAL) {
+		/* switched from external to internal */
+		if (*buff_handle) {
+			dmxdev->demux->unmap_buffer(dmxdev->demux,
+				*buff_handle);
+			*buff_handle = NULL;
+		}
+
+		/* set default internal buffer */
+		dvb_dvr_set_buffer_size(dmxdev, f_flags, DVR_BUFFER_SIZE);
+	} else if (oldmem) {
+		/* switched from internal to external */
+		vfree(oldmem);
+	}
 
 	return 0;
 }
@@ -1037,6 +1106,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;
 }
 
@@ -1138,10 +1214,6 @@
 
 	wake_up_all(&buffer->queue);
 
-	if (!work_pending(&dmxdev->dvr_input_work))
-		queue_work(dmxdev->dvr_input_workqueue,
-					&dmxdev->dvr_input_work);
-
 	return 0;
 }
 
@@ -1264,6 +1336,21 @@
 	return 0;
 }
 
+static int dvb_dmxdev_set_tsp_out_format(struct dmxdev_filter *dmxdevfilter,
+				enum dmx_tsp_format_t dmx_tsp_format)
+{
+	if (dmxdevfilter->state >= DMXDEV_STATE_GO)
+		return -EBUSY;
+
+	if ((dmx_tsp_format > DMX_TSP_FORMAT_192_HEAD) ||
+		(dmx_tsp_format < DMX_TSP_FORMAT_188))
+		return -EINVAL;
+
+	dmxdevfilter->dmx_tsp_format = dmx_tsp_format;
+
+	return 0;
+}
+
 static int dvb_dmxdev_set_pes_buffer_size(struct dmxdev_filter *dmxdevfilter,
 					unsigned long size)
 {
@@ -1303,13 +1390,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 +1421,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 +1431,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 +1448,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 +1470,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 +1480,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 +1633,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;
 
 }
@@ -2172,6 +2275,9 @@
 		return ret;
 	}
 
+	if (tsfeed->set_tsp_out_format)
+		tsfeed->set_tsp_out_format(tsfeed, filter->dmx_tsp_format);
+
 	/* Support indexing for video PES */
 	if ((para->pes_type == DMX_PES_VIDEO0) ||
 	    (para->pes_type == DMX_PES_VIDEO1) ||
@@ -2382,6 +2488,8 @@
 
 	dmxdevfilter->pes_buffer_size = 32768;
 
+	dmxdevfilter->dmx_tsp_format = DMX_TSP_FORMAT_188;
+
 	dvbdev->users++;
 
 	mutex_unlock(&dmxdev->mutex);
@@ -2608,6 +2716,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
@@ -2764,19 +2879,15 @@
 		break;
 
 	case DMX_SET_TS_OUT_FORMAT:
-		if (!dmxdev->demux->set_tsp_out_format) {
-			ret = -EINVAL;
-			break;
+		if (mutex_lock_interruptible(&dmxdevfilter->mutex)) {
+			mutex_unlock(&dmxdev->mutex);
+			return -ERESTARTSYS;
 		}
 
-		if (dmxdevfilter->state >= DMXDEV_STATE_GO) {
-			ret = -EBUSY;
-			break;
-		}
-
-		ret = dmxdev->demux->set_tsp_out_format(
-				dmxdev->demux,
+		ret = dvb_dmxdev_set_tsp_out_format(dmxdevfilter,
 				*(enum dmx_tsp_format_t *)parg);
+
+		mutex_unlock(&dmxdevfilter->mutex);
 		break;
 
 	case DMX_SET_DECODER_BUFFER_SIZE:
@@ -3076,6 +3187,73 @@
 	.fops = &dvb_dvr_fops
 };
 
+
+/**
+ * debugfs service to print active filters information.
+ */
+static int dvb_dmxdev_dbgfs_print(struct seq_file *s, void *p)
+{
+	int i;
+	struct dmxdev *dmxdev = s->private;
+	struct dmxdev_filter *filter;
+	int active_count = 0;
+	struct dmx_buffer_status buffer_status;
+	const char *pes_feeds[] = {"DEC", "PES", "DVR", "REC"};
+
+	if (!dmxdev)
+		return 0;
+
+	for (i = 0; i < dmxdev->filternum; i++) {
+		filter = &dmxdev->filter[i];
+		if (filter->state >= DMXDEV_STATE_GO) {
+			active_count++;
+
+			seq_printf(s, "filter_%02d - ", i);
+
+		    if (filter->type == DMXDEV_TYPE_SEC) {
+				seq_printf(s, "type: SEC, ");
+				seq_printf(s, "PID %04d ",
+						filter->params.sec.pid);
+		    } else {
+				seq_printf(s, "type: %s, ",
+					pes_feeds[filter->params.pes.output]);
+				seq_printf(s, "PID: %04d ",
+						filter->params.pes.pid);
+		    }
+
+			if (0 == dvb_dmxdev_get_buffer_status(
+						filter, &buffer_status)) {
+				seq_printf(s, "buffer size: %08d, ",
+					buffer_status.size);
+				seq_printf(s, "buffer fullness: %08d\n",
+					buffer_status.fullness);
+				seq_printf(s, "buffer error: %08d\n",
+					buffer_status.error);
+			}
+		}
+	}
+
+	if (!active_count)
+		seq_printf(s, "No active filters\n");
+
+	seq_printf(s, "\n");
+
+	return 0;
+}
+
+static int dvb_dmxdev_dbgfs_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, dvb_dmxdev_dbgfs_print, inode->i_private);
+}
+
+static const struct file_operations dbgfs_filters_fops = {
+	.open = dvb_dmxdev_dbgfs_open,
+	.read = seq_read,
+	.llseek = seq_lseek,
+	.release = single_release,
+	.owner = THIS_MODULE,
+};
+
 int dvb_dmxdev_init(struct dmxdev *dmxdev, struct dvb_adapter *dvb_adapter)
 {
 	int i;
@@ -3087,14 +3265,6 @@
 	if (!dmxdev->filter)
 		return -ENOMEM;
 
-	dmxdev->dvr_input_workqueue =
-		create_singlethread_workqueue("dvr_workqueue");
-
-	if (dmxdev->dvr_input_workqueue == NULL) {
-		vfree(dmxdev->filter);
-		return -ENOMEM;
-	}
-
 	dmxdev->playback_mode = DMX_PB_MODE_PUSH;
 
 	mutex_init(&dmxdev->mutex);
@@ -3115,8 +3285,10 @@
 	dvb_ringbuffer_init(&dmxdev->dvr_buffer, NULL, 8192);
 	dvb_ringbuffer_init(&dmxdev->dvr_input_buffer, NULL, 8192);
 
-	INIT_WORK(&dmxdev->dvr_input_work,
-			  dvr_input_work_func);
+	if (dmxdev->demux->debugfs_demux_dir)
+		debugfs_create_file("filters", S_IRUGO,
+			dmxdev->demux->debugfs_demux_dir, dmxdev,
+			&dbgfs_filters_fops);
 
 	return 0;
 }
@@ -3135,9 +3307,6 @@
 				dmxdev->dvr_dvbdev->users==1);
 	}
 
-	flush_workqueue(dmxdev->dvr_input_workqueue);
-	destroy_workqueue(dmxdev->dvr_input_workqueue);
-
 	dvb_unregister_device(dmxdev->dvbdev);
 	dvb_unregister_device(dmxdev->dvr_dvbdev);
 
diff --git a/drivers/media/dvb/dvb-core/dmxdev.h b/drivers/media/dvb/dvb-core/dmxdev.h
index e30c2c3..d1c1cc3 100644
--- a/drivers/media/dvb/dvb-core/dmxdev.h
+++ b/drivers/media/dvb/dvb-core/dmxdev.h
@@ -34,7 +34,7 @@
 #include <linux/string.h>
 #include <linux/mutex.h>
 #include <linux/slab.h>
-#include <linux/workqueue.h>
+#include <linux/kthread.h>
 #include <linux/dvb/dmx.h>
 
 #include "dvbdev.h"
@@ -122,6 +122,8 @@
 	/* relevent for decoder PES */
 	unsigned long pes_buffer_size;
 
+	/* for recording output */
+	enum dmx_tsp_format_t dmx_tsp_format;
 	u32 rec_chunk_size;
 
 	/* only for sections */
@@ -131,8 +133,6 @@
 };
 
 struct dmxdev {
-	struct work_struct dvr_input_work;
-
 	struct dvb_device *dvbdev;
 	struct dvb_device *dvr_dvbdev;
 
@@ -165,7 +165,7 @@
 
 	struct dvb_ringbuffer dvr_input_buffer;
 	enum dmx_buffer_mode dvr_input_buffer_mode;
-	struct workqueue_struct *dvr_input_workqueue;
+	struct task_struct *dvr_input_thread;
 
 #define DVR_BUFFER_SIZE (10*188*1024)
 
diff --git a/drivers/media/dvb/dvb-core/dvb_demux.c b/drivers/media/dvb/dvb-core/dvb_demux.c
index de7b28d..978cb38d 100644
--- a/drivers/media/dvb/dvb-core/dvb_demux.c
+++ b/drivers/media/dvb/dvb-core/dvb_demux.c
@@ -417,17 +417,16 @@
 
 static inline void dvb_dmx_swfilter_output_packet(
 	struct dvb_demux_feed *feed,
-	const u8 *buf)
+	const u8 *buf,
+	const u8 timestamp[TIMESTAMP_LEN])
 {
-	u8 time_stamp[4] = {0};
-	struct dvb_demux *demux = feed->demux;
-
 	/*
 	 * if we output 192 packet with timestamp at head of packet,
 	 * output the timestamp now before the 188 TS packet
 	 */
-	if (demux->tsp_out_format == DMX_TSP_FORMAT_192_HEAD)
-		feed->cb.ts(time_stamp, 4, NULL, 0, &feed->feed.ts, DMX_OK);
+	if (feed->tsp_out_format == DMX_TSP_FORMAT_192_HEAD)
+		feed->cb.ts(timestamp, TIMESTAMP_LEN, NULL,
+			0, &feed->feed.ts, DMX_OK);
 
 	feed->cb.ts(buf, 188, NULL, 0, &feed->feed.ts, DMX_OK);
 
@@ -435,8 +434,9 @@
 	 * if we output 192 packet with timestamp at tail of packet,
 	 * output the timestamp now after the 188 TS packet
 	 */
-	if (demux->tsp_out_format == DMX_TSP_FORMAT_192_TAIL)
-		feed->cb.ts(time_stamp, 4, NULL, 0, &feed->feed.ts, DMX_OK);
+	if (feed->tsp_out_format == DMX_TSP_FORMAT_192_TAIL)
+		feed->cb.ts(timestamp, TIMESTAMP_LEN, NULL,
+			0, &feed->feed.ts, DMX_OK);
 }
 
 static inline void dvb_dmx_configure_decoder_fullness(
@@ -571,7 +571,7 @@
 }
 
 static inline void dvb_dmx_swfilter_packet_type(struct dvb_demux_feed *feed,
-						const u8 *buf)
+			const u8 *buf, const u8 timestamp[TIMESTAMP_LEN])
 {
 	switch (feed->type) {
 	case DMX_TYPE_TS:
@@ -581,7 +581,8 @@
 			if (feed->ts_type & TS_PAYLOAD_ONLY)
 				dvb_dmx_swfilter_payload(feed, buf);
 			else
-				dvb_dmx_swfilter_output_packet(feed, buf);
+				dvb_dmx_swfilter_output_packet(feed,
+						buf, timestamp);
 		}
 		if (feed->ts_type & TS_DECODER)
 			if (feed->demux->write_to_decoder)
@@ -605,7 +606,8 @@
 	((f)->feed.ts.is_filtering) &&					\
 	(((f)->ts_type & (TS_PACKET | TS_DEMUX)) == TS_PACKET))
 
-static void dvb_dmx_swfilter_packet(struct dvb_demux *demux, const u8 *buf)
+void dvb_dmx_swfilter_packet(struct dvb_demux *demux, const u8 *buf,
+				const u8 timestamp[TIMESTAMP_LEN])
 {
 	struct dvb_demux_feed *feed;
 	u16 pid = ts_pid(buf);
@@ -678,12 +680,13 @@
 			continue;
 
 		if (feed->pid == pid)
-			dvb_dmx_swfilter_packet_type(feed, buf);
+			dvb_dmx_swfilter_packet_type(feed, buf, timestamp);
 		else if ((feed->pid == 0x2000) &&
 			     (feed->feed.ts.is_filtering))
-			dvb_dmx_swfilter_output_packet(feed, buf);
+			dvb_dmx_swfilter_output_packet(feed, buf, timestamp);
 	}
 }
+EXPORT_SYMBOL(dvb_dmx_swfilter_packet);
 
 void dvb_dmx_swfilter_section_packets(struct dvb_demux *demux, const u8 *buf,
 			      size_t count)
@@ -735,6 +738,7 @@
 			      size_t count)
 {
 	struct timespec pre_time;
+	u8 timestamp[TIMESTAMP_LEN] = {0};
 
 	if (dvb_demux_performancecheck)
 		pre_time = current_kernel_time();
@@ -746,7 +750,7 @@
 
 	while (count--) {
 		if (buf[0] == 0x47)
-			dvb_dmx_swfilter_packet(demux, buf);
+			dvb_dmx_swfilter_packet(demux, buf, timestamp);
 		buf += 188;
 	}
 
@@ -794,6 +798,7 @@
 	int p = 0, i, j;
 	const u8 *q;
 	struct timespec pre_time;
+	u8 timestamp[TIMESTAMP_LEN];
 
 	if (dvb_demux_performancecheck)
 		pre_time = current_kernel_time();
@@ -812,12 +817,23 @@
 			goto bailout;
 		}
 		memcpy(&demux->tsbuf[i], buf, j);
+
+		if (pktsize == 192) {
+			if (leadingbytes)
+				memcpy(timestamp, &buf[p], TIMESTAMP_LEN);
+			else
+				memcpy(timestamp, &buf[188], TIMESTAMP_LEN);
+		} else {
+			memset(timestamp, 0, TIMESTAMP_LEN);
+		}
+
 		if (pktsize == 192 &&
 			leadingbytes &&
 			demux->tsbuf[leadingbytes] == 0x47)  /* double check */
-			dvb_dmx_swfilter_packet(demux, demux->tsbuf+4);
+			dvb_dmx_swfilter_packet(demux,
+				demux->tsbuf + TIMESTAMP_LEN, timestamp);
 		else if (demux->tsbuf[0] == 0x47) /* double check */
-			dvb_dmx_swfilter_packet(demux, demux->tsbuf);
+			dvb_dmx_swfilter_packet(demux, demux->tsbuf, timestamp);
 		demux->tsbufp = 0;
 		p += j;
 	}
@@ -841,10 +857,18 @@
 			q = demux->tsbuf;
 		}
 
-		if (pktsize == 192 && leadingbytes)
-			q = &buf[p+leadingbytes];
+		if (pktsize == 192) {
+			if (leadingbytes) {
+				q = &buf[p+leadingbytes];
+				memcpy(timestamp, &buf[p], TIMESTAMP_LEN);
+			} else {
+				memcpy(timestamp, &buf[188], TIMESTAMP_LEN);
+			}
+		} else {
+			memset(timestamp, 0, TIMESTAMP_LEN);
+		}
 
-		dvb_dmx_swfilter_packet(demux, q);
+		dvb_dmx_swfilter_packet(demux, q, timestamp);
 		p += pktsize;
 	}
 
@@ -891,7 +915,7 @@
 		break;
 
 	case DMX_TSP_FORMAT_192_HEAD:
-		_dvb_dmx_swfilter(demux, buf, count, 192, 4);
+		_dvb_dmx_swfilter(demux, buf, count, 192, TIMESTAMP_LEN);
 		break;
 
 	case DMX_TSP_FORMAT_204:
@@ -1154,6 +1178,25 @@
 	return 0;
 }
 
+static int dmx_ts_set_tsp_out_format(
+	struct dmx_ts_feed *ts_feed,
+	enum dmx_tsp_format_t tsp_format)
+{
+	struct dvb_demux_feed *feed = (struct dvb_demux_feed *)ts_feed;
+	struct dvb_demux *dvbdmx = feed->demux;
+
+	mutex_lock(&dvbdmx->mutex);
+
+	if (feed->state == DMX_STATE_GO) {
+		mutex_unlock(&dvbdmx->mutex);
+		return -EINVAL;
+	}
+
+	feed->tsp_out_format = tsp_format;
+	mutex_unlock(&dvbdmx->mutex);
+	return 0;
+}
+
 static int dvbdmx_allocate_ts_feed(struct dmx_demux *dmx,
 				   struct dmx_ts_feed **ts_feed,
 				   dmx_ts_cb callback)
@@ -1175,6 +1218,7 @@
 	feed->pid = 0xffff;
 	feed->peslen = 0;
 	feed->buffer = NULL;
+	feed->tsp_out_format = DMX_TSP_FORMAT_188;
 	memset(&feed->indexing_params, 0,
 			sizeof(struct dmx_indexing_video_params));
 
@@ -1193,6 +1237,7 @@
 	(*ts_feed)->stop_filtering = dmx_ts_feed_stop_filtering;
 	(*ts_feed)->set = dmx_ts_feed_set;
 	(*ts_feed)->set_indexing_params = dmx_ts_set_indexing_params;
+	(*ts_feed)->set_tsp_out_format = dmx_ts_set_tsp_out_format;
 	(*ts_feed)->get_decoder_buff_status = dmx_ts_feed_decoder_buff_status;
 	(*ts_feed)->data_ready_cb = dmx_ts_feed_data_ready_cb;
 	(*ts_feed)->notify_data_read = NULL;
@@ -1689,23 +1734,6 @@
 	return 0;
 }
 
-static int dvbdmx_set_tsp_out_format(
-	struct dmx_demux *demux,
-	enum dmx_tsp_format_t tsp_format)
-{
-	struct dvb_demux *dvbdemux = (struct dvb_demux *)demux;
-
-	if ((tsp_format > DMX_TSP_FORMAT_192_HEAD) ||
-		(tsp_format < DMX_TSP_FORMAT_188))
-		return -EINVAL;
-
-	mutex_lock(&dvbdemux->mutex);
-
-	dvbdemux->tsp_out_format = tsp_format;
-	mutex_unlock(&dvbdemux->mutex);
-	return 0;
-}
-
 int dvb_dmx_init(struct dvb_demux *dvbdemux)
 {
 	int i;
@@ -1732,19 +1760,20 @@
 			"demux%d",
 			dvb_demux_index++);
 
-	dvbdemux->debugfs_demux_dir = debugfs_create_dir(dvbdemux->alias, NULL);
+	dvbdemux->dmx.debugfs_demux_dir =
+		debugfs_create_dir(dvbdemux->alias, NULL);
 
-	if (dvbdemux->debugfs_demux_dir != NULL) {
+	if (dvbdemux->dmx.debugfs_demux_dir != NULL) {
 		debugfs_create_u32(
 			"total_processing_time",
 			S_IRUGO|S_IWUGO,
-			dvbdemux->debugfs_demux_dir,
+			dvbdemux->dmx.debugfs_demux_dir,
 			&dvbdemux->total_process_time);
 
 		debugfs_create_u32(
 			"total_crc_time",
 			S_IRUGO|S_IWUGO,
-			dvbdemux->debugfs_demux_dir,
+			dvbdemux->dmx.debugfs_demux_dir,
 			&dvbdemux->total_crc_time);
 	}
 
@@ -1775,7 +1804,6 @@
 	dvbdemux->tsbufp = 0;
 
 	dvbdemux->tsp_format = DMX_TSP_FORMAT_188;
-	dvbdemux->tsp_out_format = DMX_TSP_FORMAT_188;
 
 	if (!dvbdemux->check_crc32)
 		dvbdemux->check_crc32 = dvb_dmx_crc32;
@@ -1805,7 +1833,6 @@
 	dmx->get_pes_pids = dvbdmx_get_pes_pids;
 
 	dmx->set_tsp_format = dvbdmx_set_tsp_format;
-	dmx->set_tsp_out_format = dvbdmx_set_tsp_out_format;
 
 	mutex_init(&dvbdemux->mutex);
 	spin_lock_init(&dvbdemux->lock);
@@ -1817,9 +1844,10 @@
 
 void dvb_dmx_release(struct dvb_demux *dvbdemux)
 {
-	if (dvbdemux->debugfs_demux_dir != NULL)
-		debugfs_remove_recursive(dvbdemux->debugfs_demux_dir);
+	if (dvbdemux->dmx.debugfs_demux_dir != NULL)
+		debugfs_remove_recursive(dvbdemux->dmx.debugfs_demux_dir);
 
+	dvb_demux_index--;
 	vfree(dvbdemux->cnt_storage);
 	vfree(dvbdemux->filter);
 	vfree(dvbdemux->feed);
diff --git a/drivers/media/dvb/dvb-core/dvb_demux.h b/drivers/media/dvb/dvb-core/dvb_demux.h
index 5a32363..f89367b 100644
--- a/drivers/media/dvb/dvb-core/dvb_demux.h
+++ b/drivers/media/dvb/dvb-core/dvb_demux.h
@@ -47,6 +47,8 @@
 
 #define MAX_PID 0x1fff
 
+#define TIMESTAMP_LEN	4
+
 #define SPEED_PKTS_INTERVAL 50000
 
 struct dvb_demux_filter {
@@ -90,6 +92,7 @@
 	u16 pid;
 	u8 *buffer;
 	int buffer_size;
+	enum dmx_tsp_format_t tsp_out_format;
 
 	struct timespec timeout;
 	struct dvb_demux_filter *filter;
@@ -155,7 +158,6 @@
 	uint32_t speed_pkts_cnt; /* for TS speed check */
 
 	enum dmx_tsp_format_t tsp_format;
-	enum dmx_tsp_format_t tsp_out_format;
 
 	enum dmx_playback_mode_t playback_mode;
 	int sw_filter_abort;
@@ -174,7 +176,6 @@
 
 	u32 total_process_time;
 	u32 total_crc_time;
-	struct dentry *debugfs_demux_dir;
 };
 
 int dvb_dmx_init(struct dvb_demux *dvbdemux);
@@ -190,6 +191,8 @@
 			struct dvb_demux *demux, const u8 *buf,
 			size_t count,
 			enum dmx_tsp_format_t tsp_format);
+void dvb_dmx_swfilter_packet(struct dvb_demux *demux, const u8 *buf,
+				const u8 timestamp[TIMESTAMP_LEN]);
 
 
 #endif /* _DVB_DEMUX_H_ */
diff --git a/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_common.c b/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_common.c
index 18c3767..e2ef6a0 100644
--- a/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_common.c
+++ b/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_common.c
@@ -473,33 +473,46 @@
 	 * when dvb-demux is terminated.
 	 */
 	mpq_demux->hw_notification_count = 0;
-	mpq_demux->hw_notification_rate = 0;
+	mpq_demux->hw_notification_interval = 0;
 	mpq_demux->hw_notification_size = 0;
 	mpq_demux->decoder_tsp_drop_count = 0;
+	mpq_demux->hw_notification_min_size = 0xFFFFFFFF;
 
-	if (mpq_demux->demux.debugfs_demux_dir != NULL) {
+	if (mpq_demux->demux.dmx.debugfs_demux_dir != NULL) {
 		debugfs_create_u32(
-			"hw_notification_rate",
+			"hw_notification_interval",
 			S_IRUGO|S_IWUGO,
-			mpq_demux->demux.debugfs_demux_dir,
-			&mpq_demux->hw_notification_rate);
+			mpq_demux->demux.dmx.debugfs_demux_dir,
+			&mpq_demux->hw_notification_interval);
+
+		debugfs_create_u32(
+			"hw_notification_min_interval",
+			S_IRUGO|S_IWUGO,
+			mpq_demux->demux.dmx.debugfs_demux_dir,
+			&mpq_demux->hw_notification_min_interval);
 
 		debugfs_create_u32(
 			"hw_notification_count",
 			S_IRUGO|S_IWUGO,
-			mpq_demux->demux.debugfs_demux_dir,
+			mpq_demux->demux.dmx.debugfs_demux_dir,
 			&mpq_demux->hw_notification_count);
 
 		debugfs_create_u32(
 			"hw_notification_size",
 			S_IRUGO|S_IWUGO,
-			mpq_demux->demux.debugfs_demux_dir,
+			mpq_demux->demux.dmx.debugfs_demux_dir,
 			&mpq_demux->hw_notification_size);
 
 		debugfs_create_u32(
+			"hw_notification_min_size",
+			S_IRUGO|S_IWUGO,
+			mpq_demux->demux.dmx.debugfs_demux_dir,
+			&mpq_demux->hw_notification_min_size);
+
+		debugfs_create_u32(
 			"decoder_tsp_drop_count",
 			S_IRUGO|S_IWUGO,
-			mpq_demux->demux.debugfs_demux_dir,
+			mpq_demux->demux.dmx.debugfs_demux_dir,
 			&mpq_demux->decoder_tsp_drop_count);
 	}
 }
@@ -520,10 +533,17 @@
 					curr_time,
 					mpq_demux->last_notification_time);
 
-		delta_time_ms = (u64)timespec_to_ns(&delta_time);
-		delta_time_ms = div64_u64(delta_time_ms, 1000000); /* ns->ms */
+		delta_time_ms = ((u64)delta_time.tv_sec * MSEC_PER_SEC) +
+					delta_time.tv_nsec / NSEC_PER_MSEC;
 
-		mpq_demux->hw_notification_rate = delta_time_ms;
+		mpq_demux->hw_notification_interval = delta_time_ms;
+
+		if ((mpq_demux->hw_notification_count == 1) ||
+			(mpq_demux->hw_notification_interval &&
+			 mpq_demux->hw_notification_interval <
+				mpq_demux->hw_notification_min_interval))
+			mpq_demux->hw_notification_min_interval =
+				mpq_demux->hw_notification_interval;
 	}
 
 	mpq_demux->hw_notification_count++;
diff --git a/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_common.h b/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_common.h
index 3500eda..69305b6 100644
--- a/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_common.h
+++ b/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_common.h
@@ -30,7 +30,7 @@
 /**
  * TSIF alias name length
  */
-#define TSIF_NAME_LENGTH				10
+#define TSIF_NAME_LENGTH				20
 
 #define MPQ_MAX_FOUND_PATTERNS				5
 
@@ -44,9 +44,14 @@
  *                  initialized or not.
  * @ion_client: ION demux client used to allocate memory from ION.
  * @feed_lock: Lock used to protect against private feed data
- * @hw_notification_rate: Notification rate in msec, exposed in debugfs.
+ * @hw_notification_interval: Notification interval in msec,
+ *                            exposed in debugfs.
+ * @hw_notification_min_interval: Minimum notification internal in msec,
+ * exposed in debugfs.
  * @hw_notification_count: Notification count, exposed in debugfs.
  * @hw_notification_size: Notification size in bytes, exposed in debugfs.
+ * @hw_notification_min_size: Minimum notification size in bytes,
+ *                            exposed in debugfs.
  * @decoder_tsp_drop_count: Counter of number of dropped TS packets
  * due to decoder buffer fullness, exposed in debugfs.
  * @last_notification_time: Time of last HW notification.
@@ -61,9 +66,11 @@
 	spinlock_t feed_lock;
 
 	/* debug-fs */
-	u32 hw_notification_rate;
+	u32 hw_notification_interval;
+	u32 hw_notification_min_interval;
 	u32 hw_notification_count;
 	u32 hw_notification_size;
+	u32 hw_notification_min_size;
 	u32 decoder_tsp_drop_count;
 	struct timespec last_notification_time;
 };
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..bbf9d0a 100644
--- a/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_tsif.c
+++ b/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_tsif.c
@@ -13,7 +13,7 @@
 #include <linux/init.h>
 #include <linux/module.h>
 #include <linux/tsif_api.h>
-#include <linux/workqueue.h>
+#include <linux/kthread.h>
 #include <linux/moduleparam.h>
 #include "mpq_dvb_debug.h"
 #include "mpq_dmx_plugin_common.h"
@@ -30,24 +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);
-
-/*
- * Work scheduled each time TSIF notifies dmx
- * of new TS packet
- */
-struct tsif_work {
-	struct work_struct work;
-	int tsif_id;
-};
+module_param(tsif_mode, int, S_IRUGO | S_IWUSR);
+module_param(clock_inv, int, S_IRUGO | S_IWUSR);
 
 
 /*
@@ -77,11 +69,12 @@
 {
 	/* Information for each TSIF input processing */
 	struct {
-		/* work used to submit to workqueue for processing */
-		struct tsif_work work;
+		/* thread processing TS packets from TSIF */
+		struct task_struct *thread;
+		wait_queue_head_t wait_queue;
 
-		/* workqueue that processes TS packets from specific TSIF */
-		struct workqueue_struct *workqueue;
+		/* Counter for data notifications from TSIF */
+		atomic_t data_cnt;
 
 		/* TSIF alias */
 		char name[TSIF_NAME_LENGTH];
@@ -102,94 +95,72 @@
 
 
 /**
- * Worker function that processes the TS packets notified by the TSIF driver.
+ * Demux thread function handling data from specific TSIF.
  *
- * @worker: the executed work
+ * @arg: TSIF number
  */
-static void mpq_dmx_tsif_work(struct work_struct *worker)
+static int mpq_dmx_tsif_thread(void *arg)
 {
-	struct tsif_work *tsif_work =
-		container_of(worker, struct tsif_work, work);
 	struct mpq_demux *mpq_demux;
 	struct tsif_driver_info *tsif_driver;
 	size_t packets = 0;
-	int tsif = tsif_work->tsif_id;
+	int tsif = (int)arg;
+	int ret;
 
-	mpq_demux = mpq_dmx_tsif_info.tsif[tsif].mpq_demux;
-	tsif_driver = &(mpq_dmx_tsif_info.tsif[tsif].tsif_driver);
+	do {
+		ret = wait_event_interruptible(
+			mpq_dmx_tsif_info.tsif[tsif].wait_queue,
+			(atomic_read(
+				&mpq_dmx_tsif_info.tsif[tsif].data_cnt) != 0) ||
+			kthread_should_stop());
 
-	MPQ_DVB_DBG_PRINT(
-		"%s executed, tsif = %d\n",
-		__func__,
-		tsif);
+		if ((ret < 0) || kthread_should_stop()) {
+			MPQ_DVB_DBG_PRINT("%s: exit\n", __func__);
+			break;
+		}
 
-	if (mutex_lock_interruptible(&mpq_dmx_tsif_info.tsif[tsif].mutex))
-		return;
+		if (mutex_lock_interruptible(
+			&mpq_dmx_tsif_info.tsif[tsif].mutex))
+			return -ERESTARTSYS;
 
-	/* Check if driver handler is still valid */
-	if (tsif_driver->tsif_handler == NULL) {
-		mutex_unlock(&mpq_dmx_tsif_info.tsif[tsif].mutex);
-		MPQ_DVB_ERR_PRINT("%s: tsif_driver->tsif_handler is NULL!\n",
+		tsif_driver = &(mpq_dmx_tsif_info.tsif[tsif].tsif_driver);
+		mpq_demux = mpq_dmx_tsif_info.tsif[tsif].mpq_demux;
+
+		/* Check if driver handler is still valid */
+		if (tsif_driver->tsif_handler == NULL) {
+			mutex_unlock(&mpq_dmx_tsif_info.tsif[tsif].mutex);
+			MPQ_DVB_DBG_PRINT(
+				"%s: tsif was detached\n",
 				__func__);
-		return;
-	}
+			continue;
+		}
 
-	tsif_get_state(tsif_driver->tsif_handler, &(tsif_driver->ri),
-				&(tsif_driver->wi), &(tsif_driver->state));
+		tsif_get_state(
+			tsif_driver->tsif_handler, &(tsif_driver->ri),
+			&(tsif_driver->wi), &(tsif_driver->state));
 
-	if ((tsif_driver->wi == tsif_driver->ri) ||
-		(tsif_driver->state == tsif_state_stopped) ||
-		(tsif_driver->state == tsif_state_error)) {
+		if ((tsif_driver->wi == tsif_driver->ri) ||
+			(tsif_driver->state == tsif_state_stopped) ||
+			(tsif_driver->state == tsif_state_error)) {
 
-		mpq_demux->hw_notification_size = 0;
+			mpq_demux->hw_notification_size = 0;
 
-		mutex_unlock(&mpq_dmx_tsif_info.tsif[tsif].mutex);
+			mutex_unlock(&mpq_dmx_tsif_info.tsif[tsif].mutex);
 
-		MPQ_DVB_ERR_PRINT(
-			"%s: invalid TSIF state (%d), wi = (%d), ri = (%d)\n",
-			__func__,
-			tsif_driver->state, tsif_driver->wi, tsif_driver->ri);
-		return;
-	}
+			MPQ_DVB_DBG_PRINT(
+				"%s: TSIF invalid state %d, %d, %d\n",
+				__func__,
+				tsif_driver->state,
+				tsif_driver->wi,
+				tsif_driver->ri);
+			continue;
+		}
 
-	if (tsif_driver->wi > tsif_driver->ri) {
-		packets = (tsif_driver->wi - tsif_driver->ri);
-		mpq_demux->hw_notification_size = packets;
+		atomic_dec(&mpq_dmx_tsif_info.tsif[tsif].data_cnt);
 
-		dvb_dmx_swfilter_format(
-			&mpq_demux->demux,
-			(tsif_driver->data_buffer +
-			(tsif_driver->ri * TSIF_PKT_SIZE)),
-			(packets * TSIF_PKT_SIZE),
-			DMX_TSP_FORMAT_192_TAIL);
-
-		tsif_driver->ri =
-			(tsif_driver->ri + packets) % tsif_driver->buffer_size;
-
-		tsif_reclaim_packets(tsif_driver->tsif_handler,
-					tsif_driver->ri);
-	} else {
-		/*
-		 * wi < ri, means wraparound on cyclic buffer.
-		 * Handle in two stages.
-		 */
-		packets = (tsif_driver->buffer_size - tsif_driver->ri);
-		mpq_demux->hw_notification_size = packets;
-
-		dvb_dmx_swfilter_format(
-			&mpq_demux->demux,
-			(tsif_driver->data_buffer +
-			(tsif_driver->ri * TSIF_PKT_SIZE)),
-			(packets * TSIF_PKT_SIZE),
-			DMX_TSP_FORMAT_192_TAIL);
-
-		/* tsif_driver->ri should be 0 after this */
-		tsif_driver->ri =
-			(tsif_driver->ri + packets) % tsif_driver->buffer_size;
-
-		packets = tsif_driver->wi;
-		if (packets > 0) {
-			mpq_demux->hw_notification_size += packets;
+		if (tsif_driver->wi > tsif_driver->ri) {
+			packets = (tsif_driver->wi - tsif_driver->ri);
+			mpq_demux->hw_notification_size = packets;
 
 			dvb_dmx_swfilter_format(
 				&mpq_demux->demux,
@@ -201,13 +172,55 @@
 			tsif_driver->ri =
 				(tsif_driver->ri + packets) %
 				tsif_driver->buffer_size;
+
+			tsif_reclaim_packets(
+				tsif_driver->tsif_handler,
+					tsif_driver->ri);
+		} else {
+			/*
+			 * wi < ri, means wraparound on cyclic buffer.
+			 * Handle in two stages.
+			 */
+			packets = (tsif_driver->buffer_size - tsif_driver->ri);
+			mpq_demux->hw_notification_size = packets;
+
+			dvb_dmx_swfilter_format(
+				&mpq_demux->demux,
+				(tsif_driver->data_buffer +
+				(tsif_driver->ri * TSIF_PKT_SIZE)),
+				(packets * TSIF_PKT_SIZE),
+				DMX_TSP_FORMAT_192_TAIL);
+
+			/* tsif_driver->ri should be 0 after this */
+			tsif_driver->ri =
+				(tsif_driver->ri + packets) %
+				tsif_driver->buffer_size;
+
+			packets = tsif_driver->wi;
+			if (packets > 0) {
+				mpq_demux->hw_notification_size += packets;
+
+				dvb_dmx_swfilter_format(
+					&mpq_demux->demux,
+					(tsif_driver->data_buffer +
+					(tsif_driver->ri * TSIF_PKT_SIZE)),
+					(packets * TSIF_PKT_SIZE),
+					DMX_TSP_FORMAT_192_TAIL);
+
+				tsif_driver->ri =
+					(tsif_driver->ri + packets) %
+					tsif_driver->buffer_size;
+			}
+
+			tsif_reclaim_packets(
+				tsif_driver->tsif_handler,
+				tsif_driver->ri);
 		}
 
-		tsif_reclaim_packets(tsif_driver->tsif_handler,
-					tsif_driver->ri);
-	}
+		mutex_unlock(&mpq_dmx_tsif_info.tsif[tsif].mutex);
+	} while (1);
 
-	mutex_unlock(&mpq_dmx_tsif_info.tsif[tsif].mutex);
+	return 0;
 }
 
 
@@ -219,7 +232,6 @@
 static void mpq_tsif_callback(void *user)
 {
 	int tsif = (int)user;
-	struct work_struct *work;
 	struct mpq_demux *mpq_demux;
 
 	MPQ_DVB_DBG_PRINT("%s executed, tsif = %d\n", __func__,	tsif);
@@ -228,11 +240,8 @@
 	mpq_demux = mpq_dmx_tsif_info.tsif[tsif].mpq_demux;
 	mpq_dmx_update_hw_statistics(mpq_demux);
 
-	work = &mpq_dmx_tsif_info.tsif[tsif].work.work;
-
-	/* Scheudle a new work to demux workqueue */
-	if (!work_pending(work))
-		queue_work(mpq_dmx_tsif_info.tsif[tsif].workqueue, work);
+	atomic_inc(&mpq_dmx_tsif_info.tsif[tsif].data_cnt);
+	wake_up(&mpq_dmx_tsif_info.tsif[tsif].wait_queue);
 }
 
 
@@ -273,7 +282,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 +292,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 +319,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) {
@@ -381,20 +384,10 @@
 		tsif_driver = &(mpq_dmx_tsif_info.tsif[tsif].tsif_driver);
 		tsif_stop(tsif_driver->tsif_handler);
 		tsif_detach(tsif_driver->tsif_handler);
-		/*
-		 * temporarily release mutex and flush the work queue
-		 * before setting tsif_handler to NULL
-		 */
-		mutex_unlock(&mpq_dmx_tsif_info.tsif[tsif].mutex);
-		flush_workqueue(mpq_dmx_tsif_info.tsif[tsif].workqueue);
-		/* re-acquire mutex */
-		if (mutex_lock_interruptible(
-				&mpq_dmx_tsif_info.tsif[tsif].mutex))
-			return -ERESTARTSYS;
-
 		tsif_driver->tsif_handler = NULL;
 		tsif_driver->data_buffer = NULL;
 		tsif_driver->buffer_size = 0;
+		atomic_set(&mpq_dmx_tsif_info.tsif[tsif].data_cnt, 0);
 		mpq_dmx_tsif_info.tsif[tsif].mpq_demux = NULL;
 	}
 
@@ -705,39 +698,36 @@
 			__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++) {
-		mpq_dmx_tsif_info.tsif[i].work.tsif_id = i;
-
-		INIT_WORK(&mpq_dmx_tsif_info.tsif[i].work.work,
-				  mpq_dmx_tsif_work);
-
 		snprintf(mpq_dmx_tsif_info.tsif[i].name,
 				TSIF_NAME_LENGTH,
-				"tsif_%d",
+				"dmx_tsif%d",
 				i);
 
-		mpq_dmx_tsif_info.tsif[i].workqueue =
-			create_singlethread_workqueue(
+		atomic_set(&mpq_dmx_tsif_info.tsif[i].data_cnt, 0);
+		init_waitqueue_head(&mpq_dmx_tsif_info.tsif[i].wait_queue);
+		mpq_dmx_tsif_info.tsif[i].thread =
+			kthread_run(
+				mpq_dmx_tsif_thread, (void *)i,
 				mpq_dmx_tsif_info.tsif[i].name);
 
-		if (mpq_dmx_tsif_info.tsif[i].workqueue == NULL) {
+		if (IS_ERR(mpq_dmx_tsif_info.tsif[i].thread)) {
 			int j;
 
 			for (j = 0; j < i; j++) {
-				destroy_workqueue(
-					mpq_dmx_tsif_info.tsif[j].workqueue);
+				kthread_stop(mpq_dmx_tsif_info.tsif[j].thread);
 				mutex_destroy(&mpq_dmx_tsif_info.tsif[j].mutex);
 			}
 
 			MPQ_DVB_ERR_PRINT(
-				"%s: create_singlethread_workqueue failed\n",
+				"%s: kthread_run failed\n",
 				__func__);
 
 			return -ENOMEM;
@@ -758,7 +748,7 @@
 			ret);
 
 		for (i = 0; i < TSIF_COUNT; i++) {
-			destroy_workqueue(mpq_dmx_tsif_info.tsif[i].workqueue);
+			kthread_stop(mpq_dmx_tsif_info.tsif[i].thread);
 			mutex_destroy(&mpq_dmx_tsif_info.tsif[i].mutex);
 		}
 	}
@@ -786,16 +776,13 @@
 			if (tsif_driver->tsif_handler)
 				tsif_stop(tsif_driver->tsif_handler);
 		}
+
 		/* Detach from TSIF driver to avoid further notifications. */
 		if (tsif_driver->tsif_handler)
 			tsif_detach(tsif_driver->tsif_handler);
 
-		/* release mutex to allow work queue to finish scheduled work */
 		mutex_unlock(&mpq_dmx_tsif_info.tsif[i].mutex);
-		/* flush the work queue and destroy it */
-		flush_workqueue(mpq_dmx_tsif_info.tsif[i].workqueue);
-		destroy_workqueue(mpq_dmx_tsif_info.tsif[i].workqueue);
-
+		kthread_stop(mpq_dmx_tsif_info.tsif[i].thread);
 		mutex_destroy(&mpq_dmx_tsif_info.tsif[i].mutex);
 	}
 
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..ac03e43 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
@@ -12,22 +12,22 @@
 
 #include <linux/init.h>
 #include <linux/module.h>
-#include <linux/workqueue.h>
+#include <linux/kthread.h>
 #include <mach/msm_tspp.h>
 #include "mpq_dvb_debug.h"
 #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
+#define TSPP_CHANNEL_COUNT			2
 
 /* the channel_id set to TSPP driver based on TSIF number and channel type */
 #define TSPP_CHANNEL_ID(tsif, ch)		((tsif << 1) + ch)
@@ -35,50 +35,57 @@
 #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
+#define TSPP_PASS_THROUGH_PID		0x2000
 
-/* TODO - NEED TO SET THESE PROPERLY
- * once TSPP driver is ready, reduce TSPP_BUFFER_SIZE
- * to single packet and set TSPP_BUFFER_COUNT accordingly
- */
+#define TSPP_RAW_TTS_SIZE		192
+#define TSPP_RAW_SIZE			188
 
-#define TSPP_RAW_TTS_SIZE				192
+#define MAX_BAM_DESCRIPTOR_SIZE	(32*1024 - 1)
 
-/* Size of single descriptor. Using max descriptor size (170 packets).
+#define TSPP_BUFFER_SIZE		(500 * 1024) /* 500KB */
+
+#define TSPP_PES_BUFFER_SIZE		(TSPP_RAW_TTS_SIZE)
+
+#define TSPP_SECTION_BUFFER_SIZE	(TSPP_RAW_TTS_SIZE)
+
+#define TSPP_BUFFER_COUNT(buffer_size)	\
+	((buffer_size) / TSPP_RAW_TTS_SIZE)
+
+/* When TSPP notifies demux that new packets are received.
+ * Using max descriptor size (170 packets).
  * Assuming 20MBit/sec stream, with 170 packets
  * per descriptor there would be about 82 descriptors,
  * 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)
-
-/* Number of descriptors, total size: TSPP_BUFFER_SIZE*TSPP_BUFFER_COUNT */
-#define TSPP_BUFFER_COUNT				(32)
-
-/* When TSPP notifies demux that new packets are received */
-#define TSPP_NOTIFICATION_SIZE			1
+#define TSPP_NOTIFICATION_SIZE(desc_size)		\
+	(MAX_BAM_DESCRIPTOR_SIZE / (desc_size))
 
 /* Channel timeout in msec */
-#define TSPP_CHANNEL_TIMEOUT			16
+#define TSPP_CHANNEL_TIMEOUT			100
+
+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;
+static int tspp_out_buffer_size = TSPP_BUFFER_SIZE;
+static int tspp_notification_size = TSPP_NOTIFICATION_SIZE(TSPP_RAW_TTS_SIZE);
+static int tspp_channel_timeout = TSPP_CHANNEL_TIMEOUT;
 
-/*
- * Work scheduled each time TSPP notifies dmx
- * of new TS packet in some channel
- */
-struct tspp_work {
-	struct work_struct work;
-	int channel_id;
-};
+module_param(tsif_mode, int, S_IRUGO | S_IWUSR);
+module_param(clock_inv, int, S_IRUGO | S_IWUSR);
+module_param(allocation_mode, int, S_IRUGO);
+module_param(tspp_out_buffer_size, int, S_IRUGO);
+module_param(tspp_notification_size, int, S_IRUGO | S_IWUSR);
+module_param(tspp_channel_timeout, int, S_IRUGO | S_IWUSR);
+
 
 /* The following structure hold singelton information
  * required for dmx implementation on top of TSPP.
@@ -94,8 +101,17 @@
 		 */
 		int pes_channel_ref;
 
-		/* work used to submit to workqueue to process pes channel */
-		struct tspp_work pes_work;
+		/* Counter for data notifications on PES pipe */
+		atomic_t pes_data_cnt;
+
+		/* 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.
@@ -104,8 +120,19 @@
 		 */
 		int section_channel_ref;
 
-		/* work used to submit to workqueue to process pes channel */
-		struct tspp_work section_work;
+		/* Counter for data notifications on section pipe */
+		atomic_t section_data_cnt;
+
+		/* 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;
+
+		u32 buffer_count;
 
 		/*
 		 * Holds PIDs of allocated TSPP filters along with
@@ -116,8 +143,9 @@
 			int ref_count;
 		} filters[TSPP_MAX_PID_FILTER_NUM];
 
-		/* workqueue that processes TS packets from specific TSIF */
-		struct workqueue_struct *workqueue;
+		/* thread processing TS packets from TSPP */
+		struct task_struct *thread;
+		wait_queue_head_t wait_queue;
 
 		/* TSIF alias */
 		char name[TSIF_NAME_LENGTH];
@@ -128,8 +156,66 @@
 		/* 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 ==
+			mpq_dmx_tspp_info.tsif[i].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 ==
+			mpq_dmx_tspp_info.tsif[i].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.
@@ -182,55 +268,105 @@
 }
 
 /**
- * Worker function that processes the TS packets notified by TSPP.
+ * Demux thread function handling data from specific TSIF.
  *
- * @worker: the executed work
+ * @arg: TSIF number
  */
-static void mpq_dmx_tspp_work(struct work_struct *worker)
+static int mpq_dmx_tspp_thread(void *arg)
 {
-	struct tspp_work *tspp_work =
-		container_of(worker, struct tspp_work, work);
+	int tsif = (int)arg;
 	struct mpq_demux *mpq_demux;
-	int channel_id = tspp_work->channel_id;
-	int tsif = TSPP_GET_TSIF_NUM(channel_id);
 	const struct tspp_data_descriptor *tspp_data_desc;
+	atomic_t *data_cnt;
+	u32 notif_size;
 	int ref_count;
+	int ret;
+	int i;
+	int j;
 
-	mpq_demux = mpq_dmx_tspp_info.tsif[tsif].mpq_demux;
+	do {
+		ret = wait_event_interruptible(
+			mpq_dmx_tspp_info.tsif[tsif].wait_queue,
+			(atomic_read(
+			 &mpq_dmx_tspp_info.tsif[tsif].pes_data_cnt)) ||
+			(atomic_read(
+			 &mpq_dmx_tspp_info.tsif[tsif].section_data_cnt)) ||
+			kthread_should_stop());
 
-	/* Lock against the TSPP filters data-structure */
-	if (mutex_lock_interruptible(&mpq_dmx_tspp_info.tsif[tsif].mutex))
-		return;
+		if ((ret < 0) || kthread_should_stop()) {
+			MPQ_DVB_ERR_PRINT("%s: exit\n", __func__);
+			break;
+		}
 
-	/* Make sure channel is still active */
-	if (TSPP_IS_PES_CHANNEL(channel_id))
-		ref_count = mpq_dmx_tspp_info.tsif[tsif].pes_channel_ref;
-	else
-		ref_count = mpq_dmx_tspp_info.tsif[tsif].section_channel_ref;
+		/* Lock against the TSPP filters data-structure */
+		if (mutex_lock_interruptible(
+			&mpq_dmx_tspp_info.tsif[tsif].mutex))
+			return -ERESTARTSYS;
 
-	if (ref_count == 0) {
+		for (i = 0; i < TSPP_CHANNEL_COUNT; i++) {
+			int channel_id = TSPP_CHANNEL_ID(tsif, i);
+
+			if (TSPP_IS_PES_CHANNEL(channel_id)) {
+				ref_count =
+				 mpq_dmx_tspp_info.tsif[tsif].pes_channel_ref;
+				data_cnt =
+				 &mpq_dmx_tspp_info.tsif[tsif].pes_data_cnt;
+			} else {
+				ref_count =
+				 mpq_dmx_tspp_info.tsif[tsif].
+					section_channel_ref;
+				data_cnt =
+				 &mpq_dmx_tspp_info.tsif[tsif].section_data_cnt;
+			}
+
+			/* Make sure channel is still active */
+			if (ref_count == 0)
+				continue;
+
+			atomic_dec(data_cnt);
+
+			mpq_demux = mpq_dmx_tspp_info.tsif[tsif].mpq_demux;
+			mpq_demux->hw_notification_size = 0;
+
+			/*
+			 * Go through all filled descriptors
+			 * and perform demuxing on them
+			 */
+			while ((tspp_data_desc =
+				tspp_get_buffer(0, channel_id)) != NULL) {
+				notif_size =
+					tspp_data_desc->size /
+					TSPP_RAW_TTS_SIZE;
+
+				mpq_demux->hw_notification_size +=
+					notif_size;
+
+				for (j = 0; j < notif_size; j++)
+					dvb_dmx_swfilter_packet(
+					 &mpq_demux->demux,
+					 ((u8 *)tspp_data_desc->virt_base) +
+					 j * TSPP_RAW_TTS_SIZE,
+					 ((u8 *)tspp_data_desc->virt_base) +
+					 j * TSPP_RAW_TTS_SIZE + TSPP_RAW_SIZE);
+				/*
+				 * Notify TSPP that the buffer
+				 * is no longer needed
+				 */
+				tspp_release_buffer(0,
+					channel_id, tspp_data_desc->id);
+			}
+
+			if (mpq_demux->hw_notification_size &&
+				(mpq_demux->hw_notification_size <
+				mpq_demux->hw_notification_min_size))
+				mpq_demux->hw_notification_min_size =
+					mpq_demux->hw_notification_size;
+		}
+
 		mutex_unlock(&mpq_dmx_tspp_info.tsif[tsif].mutex);
-		return;
-	}
+	} while (1);
 
-	mpq_demux->hw_notification_size = 0;
-
-	/* Go through all filled descriptors and perform demuxing on them */
-	while ((tspp_data_desc = tspp_get_buffer(0, channel_id)) != NULL) {
-		mpq_demux->hw_notification_size +=
-			(tspp_data_desc->size / TSPP_RAW_TTS_SIZE);
-
-		dvb_dmx_swfilter_format(
-				&mpq_demux->demux,
-				tspp_data_desc->virt_base,
-				tspp_data_desc->size,
-				DMX_TSP_FORMAT_192_TAIL);
-
-		/* Notify TSPP that the buffer is no longer needed */
-		tspp_release_buffer(0, channel_id, tspp_data_desc->id);
-	}
-
-	mutex_unlock(&mpq_dmx_tspp_info.tsif[tsif].mutex);
+	return 0;
 }
 
 /**
@@ -242,7 +378,6 @@
 static void mpq_tspp_callback(int channel_id, void *user)
 {
 	int tsif = (int)user;
-	struct work_struct *work;
 	struct mpq_demux *mpq_demux;
 
 	/* Save statistics on TSPP notifications */
@@ -250,13 +385,11 @@
 	mpq_dmx_update_hw_statistics(mpq_demux);
 
 	if (TSPP_IS_PES_CHANNEL(channel_id))
-		work = &mpq_dmx_tspp_info.tsif[tsif].pes_work.work;
+		atomic_inc(&mpq_dmx_tspp_info.tsif[tsif].pes_data_cnt);
 	else
-		work = &mpq_dmx_tspp_info.tsif[tsif].section_work.work;
+		atomic_inc(&mpq_dmx_tspp_info.tsif[tsif].section_data_cnt);
 
-	/* Scheudle a new work to demux workqueue */
-	if (!work_pending(work))
-		queue_work(mpq_dmx_tspp_info.tsif[tsif].workqueue, work);
+	wake_up(&mpq_dmx_tspp_info.tsif[tsif].wait_queue);
 }
 
 /**
@@ -272,23 +405,38 @@
 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;
+	u32 buffer_size;
+
+	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(
@@ -320,16 +468,17 @@
 		channel_id = TSPP_CHANNEL_ID(tsif, TSPP_PES_CHANNEL);
 		channel_ref_count =
 			&mpq_dmx_tspp_info.tsif[tsif].pes_channel_ref;
+		buffer_size = TSPP_PES_BUFFER_SIZE;
 	} else {
 		channel_id = TSPP_CHANNEL_ID(tsif, TSPP_SECTION_CHANNEL);
 		channel_ref_count =
 			&mpq_dmx_tspp_info.tsif[tsif].section_channel_ref;
+		buffer_size = TSPP_SECTION_BUFFER_SIZE;
 	}
 
 	/* check if required TSPP pipe is already allocated or not */
 	if (*channel_ref_count == 0) {
 		ret = tspp_open_channel(0, channel_id);
-
 		if (ret < 0) {
 			MPQ_DVB_ERR_PRINT(
 				"%s: tspp_open_channel(%d) failed (%d)\n",
@@ -341,13 +490,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;
@@ -358,22 +507,28 @@
 					   channel_id,
 					   mpq_tspp_callback,
 					   (void *)tsif,
-					   TSPP_CHANNEL_TIMEOUT);
+					   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,
+				   mpq_dmx_tspp_info.tsif[tsif].buffer_count,
+				   buffer_size, tspp_notification_size,
+				   tspp_mem_allocator, tspp_mem_free, NULL);
+		} else {
+			ret = tspp_allocate_buffers(0, channel_id,
+				   mpq_dmx_tspp_info.tsif[tsif].buffer_count,
+				   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 +573,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) {
@@ -463,6 +618,7 @@
 	int tsif;
 	int ret;
 	int channel_id;
+	atomic_t *data_cnt;
 	int *channel_ref_count;
 	struct tspp_filter tspp_filter;
 	struct mpq_demux *mpq_demux = feed->demux->priv;
@@ -490,10 +646,12 @@
 		channel_id = TSPP_CHANNEL_ID(tsif, TSPP_PES_CHANNEL);
 		channel_ref_count =
 			&mpq_dmx_tspp_info.tsif[tsif].pes_channel_ref;
+		data_cnt = &mpq_dmx_tspp_info.tsif[tsif].pes_data_cnt;
 	} else {
 		channel_id = TSPP_CHANNEL_ID(tsif, TSPP_SECTION_CHANNEL);
 		channel_ref_count =
 			&mpq_dmx_tspp_info.tsif[tsif].section_channel_ref;
+		data_cnt = &mpq_dmx_tspp_info.tsif[tsif].section_data_cnt;
 	}
 
 	/* check if required TSPP pipe is already allocated or not */
@@ -553,6 +711,8 @@
 		/* 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);
+		atomic_set(data_cnt, 0);
 	}
 
 	mutex_unlock(&mpq_dmx_tspp_info.tsif[tsif].mutex);
@@ -611,7 +771,7 @@
 		ret = mpq_dmx_init_video_feed(feed);
 
 		if (ret < 0) {
-			MPQ_DVB_DBG_PRINT(
+			MPQ_DVB_ERR_PRINT(
 				"%s: mpq_dmx_init_video_feed failed(%d)\n",
 				__func__,
 				ret);
@@ -730,20 +890,154 @@
 	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,
+				 (mpq_dmx_tspp_info.tsif[i].buffer_count *
+				  TSPP_PES_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,
+				 (mpq_dmx_tspp_info.tsif[i].buffer_count *
+				  TSPP_SECTION_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 +1097,7 @@
 init_failed_dmx_release:
 	dvb_dmx_release(&mpq_demux->demux);
 init_failed:
+	mpq_dmx_tspp_ion_cleanup();
 	return result;
 }
 
@@ -815,21 +1110,22 @@
 	MPQ_DVB_DBG_PRINT("%s executed\n", __func__);
 
 	for (i = 0; i < TSIF_COUNT; i++) {
+		mpq_dmx_tspp_info.tsif[i].buffer_count =
+				TSPP_BUFFER_COUNT(tspp_out_buffer_size);
+
 		mpq_dmx_tspp_info.tsif[i].pes_channel_ref = 0;
-
-		mpq_dmx_tspp_info.tsif[i].pes_work.channel_id =
-			TSPP_CHANNEL_ID(i, TSPP_PES_CHANNEL);
-
-		INIT_WORK(&mpq_dmx_tspp_info.tsif[i].pes_work.work,
-				  mpq_dmx_tspp_work);
+		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;
+		atomic_set(&mpq_dmx_tspp_info.tsif[i].pes_data_cnt, 0);
 
 		mpq_dmx_tspp_info.tsif[i].section_channel_ref = 0;
-
-		mpq_dmx_tspp_info.tsif[i].section_work.channel_id =
-			TSPP_CHANNEL_ID(i, TSPP_SECTION_CHANNEL);
-
-		INIT_WORK(&mpq_dmx_tspp_info.tsif[i].section_work.work,
-				  mpq_dmx_tspp_work);
+		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;
+		atomic_set(&mpq_dmx_tspp_info.tsif[i].section_data_cnt, 0);
 
 		for (j = 0; j < TSPP_MAX_PID_FILTER_NUM; j++) {
 			mpq_dmx_tspp_info.tsif[i].filters[j].pid = -1;
@@ -838,24 +1134,23 @@
 
 		snprintf(mpq_dmx_tspp_info.tsif[i].name,
 				TSIF_NAME_LENGTH,
-				"tsif_%d",
+				"dmx_tsif%d",
 				i);
 
-		mpq_dmx_tspp_info.tsif[i].workqueue =
-			create_singlethread_workqueue(
+		init_waitqueue_head(&mpq_dmx_tspp_info.tsif[i].wait_queue);
+		mpq_dmx_tspp_info.tsif[i].thread =
+			kthread_run(
+				mpq_dmx_tspp_thread, (void *)i,
 				mpq_dmx_tspp_info.tsif[i].name);
 
-		if (mpq_dmx_tspp_info.tsif[i].workqueue == NULL) {
-
+		if (IS_ERR(mpq_dmx_tspp_info.tsif[i].thread)) {
 			for (j = 0; j < i; j++) {
-				destroy_workqueue(
-					mpq_dmx_tspp_info.tsif[j].workqueue);
-
+				kthread_stop(mpq_dmx_tspp_info.tsif[j].thread);
 				mutex_destroy(&mpq_dmx_tspp_info.tsif[j].mutex);
 			}
 
 			MPQ_DVB_ERR_PRINT(
-				"%s: create_singlethread_workqueue failed\n",
+				"%s: kthread_run failed\n",
 				__func__);
 
 			return -ENOMEM;
@@ -873,7 +1168,7 @@
 			ret);
 
 		for (i = 0; i < TSIF_COUNT; i++) {
-			destroy_workqueue(mpq_dmx_tspp_info.tsif[i].workqueue);
+			kthread_stop(mpq_dmx_tspp_info.tsif[i].thread);
 			mutex_destroy(&mpq_dmx_tspp_info.tsif[i].mutex);
 		}
 	}
@@ -890,6 +1185,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,13 +1202,14 @@
 				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);
-		destroy_workqueue(mpq_dmx_tspp_info.tsif[i].workqueue);
+		kthread_stop(mpq_dmx_tspp_info.tsif[i].thread);
 		mutex_destroy(&mpq_dmx_tspp_info.tsif[i].mutex);
 	}
 
diff --git a/drivers/media/dvb/mpq/video/mpq_dvb_video.c b/drivers/media/dvb/mpq/video/mpq_dvb_video.c
index 4628ba7..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,
@@ -2085,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));
 
@@ -2120,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");
@@ -2161,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");
@@ -2176,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..363b541 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);
@@ -2271,7 +2535,6 @@
 		case SCAN_FOR_WEAK:
 			radio->srch_st_list.srch_list_dir = dir;
 			radio->srch_st_list.srch_list_mode = srch;
-			radio->srch_st_list.srch_list_max = 0;
 			retval = hci_fm_search_station_list(
 				&radio->srch_st_list, radio->fm_hdev);
 			break;
@@ -2906,6 +3169,7 @@
 		radio->srch_rds.srch_pi = ctrl->value;
 		break;
 	case V4L2_CID_PRIVATE_IRIS_SRCH_CNT:
+		radio->srch_st_list.srch_list_max = ctrl->value;
 		break;
 	case V4L2_CID_PRIVATE_IRIS_SPACING:
 		if (radio->mode == FM_RECV) {
@@ -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/video/msm/Kconfig b/drivers/media/video/msm/Kconfig
index be9c43c..24f6556 100644
--- a/drivers/media/video/msm/Kconfig
+++ b/drivers/media/video/msm/Kconfig
@@ -186,6 +186,17 @@
 	  It is a Texas Instruments multiple LED Flash
 	  for camera flash and video light applications.
 
+config MSM_CAMERA_LED_TRIGGER_FLASH
+	bool "Qualcomm MSM LED trigger flash support"
+	depends on MSM_CAMERA
+	default n
+	---help---
+	  Enable support for LED flash for msm camera.
+	  It creates LED trigger client, reads LED flash
+	  hardware properties provided in board file /
+	  device tree and uses these information to configure
+	  LED flash using LED trigger event function.
+
 config IMX072
 	bool "Sensor imx072 (Sony 5M)"
 	default n
diff --git a/drivers/media/video/msm/Makefile b/drivers/media/video/msm/Makefile
index 5921632..56d3e0c 100644
--- a/drivers/media/video/msm/Makefile
+++ b/drivers/media/video/msm/Makefile
@@ -10,18 +10,19 @@
   EXTRA_CFLAGS += -Idrivers/media/video/msm/sensors
   EXTRA_CFLAGS += -Idrivers/media/video/msm/actuators
   EXTRA_CFLAGS += -Idrivers/media/video/msm/server
+  EXTRA_CFLAGS += -Idrivers/media/video/msm/flash
   obj-$(CONFIG_MSM_CAMERA) += msm_isp.o msm.o msm_mem.o msm_mctl.o msm_mctl_buf.o msm_mctl_pp.o
   obj-$(CONFIG_MSM_CAMERA) += server/
   obj-$(CONFIG_MSM_CAM_IRQ_ROUTER) += msm_camirq_router.o
   obj-$(CONFIG_MSM_CAMERA) += cci/ eeprom/ sensors/ actuators/ csi/
   obj-$(CONFIG_MSM_CPP) += cpp/
   obj-$(CONFIG_MSM_CAMERA) += msm_gesture.o
+  obj-$(CONFIG_MSM_CAMERA) += flash/
 else
   obj-$(CONFIG_MSM_CAMERA) += msm_camera.o
 endif
 obj-$(CONFIG_MSM_CAMERA) += vfe/
 obj-$(CONFIG_MSM_CAMERA) += msm_axi_qos.o gemini/ mercury/ jpeg_10/
-obj-$(CONFIG_MSM_CAMERA_FLASH) += flash.o
 ifeq ($(CONFIG_MSM_CAMERA_V4L2),y)
   obj-$(CONFIG_ARCH_MSM8X60) += msm_vpe.o
   obj-$(CONFIG_ARCH_MSM7X30) += msm_vpe.o msm_axi_qos.o
diff --git a/drivers/media/video/msm/flash.c b/drivers/media/video/msm/flash.c
deleted file mode 100644
index 54c59f8..0000000
--- a/drivers/media/video/msm/flash.c
+++ /dev/null
@@ -1,763 +0,0 @@
-
-/* Copyright (c) 2009-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/errno.h>
-#include <linux/leds-pmic8058.h>
-#include <linux/pwm.h>
-#include <linux/pmic8058-pwm.h>
-#include <linux/hrtimer.h>
-#include <linux/export.h>
-#include <mach/pmic.h>
-#include <mach/camera.h>
-#include <mach/gpio.h>
-#include "msm_camera_i2c.h"
-
-struct i2c_client *sx150x_client;
-struct timer_list timer_flash;
-static struct msm_camera_sensor_info *sensor_data;
-static struct msm_camera_i2c_client i2c_client;
-enum msm_cam_flash_stat{
-	MSM_CAM_FLASH_OFF,
-	MSM_CAM_FLASH_ON,
-};
-
-static struct i2c_client *sc628a_client;
-
-static const struct i2c_device_id sc628a_i2c_id[] = {
-	{"sc628a", 0},
-	{ }
-};
-
-static int sc628a_i2c_probe(struct i2c_client *client,
-		const struct i2c_device_id *id)
-{
-	int rc = 0;
-	CDBG("sc628a_probe called!\n");
-
-	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
-		pr_err("i2c_check_functionality failed\n");
-		goto probe_failure;
-	}
-
-	sc628a_client = client;
-
-	CDBG("sc628a_probe success rc = %d\n", rc);
-	return 0;
-
-probe_failure:
-	pr_err("sc628a_probe failed! rc = %d\n", rc);
-	return rc;
-}
-
-static struct i2c_driver sc628a_i2c_driver = {
-	.id_table = sc628a_i2c_id,
-	.probe  = sc628a_i2c_probe,
-	.remove = __exit_p(sc628a_i2c_remove),
-	.driver = {
-		.name = "sc628a",
-	},
-};
-
-static struct i2c_client *tps61310_client;
-
-static const struct i2c_device_id tps61310_i2c_id[] = {
-	{"tps61310", 0},
-	{ }
-};
-
-static int tps61310_i2c_probe(struct i2c_client *client,
-		const struct i2c_device_id *id)
-{
-	int rc = 0;
-	CDBG("%s enter\n", __func__);
-
-	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
-		pr_err("i2c_check_functionality failed\n");
-		goto probe_failure;
-	}
-
-	tps61310_client = client;
-	i2c_client.client = tps61310_client;
-	i2c_client.addr_type = MSM_CAMERA_I2C_BYTE_ADDR;
-	rc = msm_camera_i2c_write(&i2c_client, 0x01, 0x00,
-		MSM_CAMERA_I2C_BYTE_DATA);
-	if (rc < 0) {
-		tps61310_client = NULL;
-		goto probe_failure;
-	}
-
-	CDBG("%s success! rc = %d\n", __func__, rc);
-	return 0;
-
-probe_failure:
-	pr_err("%s failed! rc = %d\n", __func__, rc);
-	return rc;
-}
-
-static struct i2c_driver tps61310_i2c_driver = {
-	.id_table = tps61310_i2c_id,
-	.probe  = tps61310_i2c_probe,
-	.remove = __exit_p(tps61310_i2c_remove),
-	.driver = {
-		.name = "tps61310",
-	},
-};
-
-static int config_flash_gpio_table(enum msm_cam_flash_stat stat,
-			struct msm_camera_sensor_strobe_flash_data *sfdata)
-{
-	int rc = 0, i = 0;
-	int msm_cam_flash_gpio_tbl[][2] = {
-		{sfdata->flash_trigger, 1},
-		{sfdata->flash_charge, 1},
-		{sfdata->flash_charge_done, 0}
-	};
-
-	if (stat == MSM_CAM_FLASH_ON) {
-		for (i = 0; i < ARRAY_SIZE(msm_cam_flash_gpio_tbl); i++) {
-			rc = gpio_request(msm_cam_flash_gpio_tbl[i][0],
-							  "CAM_FLASH_GPIO");
-			if (unlikely(rc < 0)) {
-				pr_err("%s not able to get gpio\n", __func__);
-				for (i--; i >= 0; i--)
-					gpio_free(msm_cam_flash_gpio_tbl[i][0]);
-				break;
-			}
-			if (msm_cam_flash_gpio_tbl[i][1])
-				gpio_direction_output(
-					msm_cam_flash_gpio_tbl[i][0], 0);
-			else
-				gpio_direction_input(
-					msm_cam_flash_gpio_tbl[i][0]);
-		}
-	} else {
-		for (i = 0; i < ARRAY_SIZE(msm_cam_flash_gpio_tbl); i++) {
-			gpio_direction_input(msm_cam_flash_gpio_tbl[i][0]);
-			gpio_free(msm_cam_flash_gpio_tbl[i][0]);
-		}
-	}
-	return rc;
-}
-
-int msm_camera_flash_current_driver(
-	struct msm_camera_sensor_flash_current_driver *current_driver,
-	unsigned led_state)
-{
-	int rc = 0;
-#if defined CONFIG_LEDS_PMIC8058
-	int idx;
-	const struct pmic8058_leds_platform_data *driver_channel =
-		current_driver->driver_channel;
-	int num_leds = driver_channel->num_leds;
-
-	CDBG("%s: led_state = %d\n", __func__, led_state);
-
-	/* Evenly distribute current across all channels */
-	switch (led_state) {
-	case MSM_CAMERA_LED_OFF:
-		for (idx = 0; idx < num_leds; ++idx) {
-			rc = pm8058_set_led_current(
-				driver_channel->leds[idx].id, 0);
-			if (rc < 0)
-				pr_err(
-					"%s: FAIL name = %s, rc = %d\n",
-					__func__,
-					driver_channel->leds[idx].name,
-					rc);
-		}
-		break;
-
-	case MSM_CAMERA_LED_LOW:
-		for (idx = 0; idx < num_leds; ++idx) {
-			rc = pm8058_set_led_current(
-				driver_channel->leds[idx].id,
-				current_driver->low_current/num_leds);
-			if (rc < 0)
-				pr_err(
-					"%s: FAIL name = %s, rc = %d\n",
-					__func__,
-					driver_channel->leds[idx].name,
-					rc);
-		}
-		break;
-
-	case MSM_CAMERA_LED_HIGH:
-		for (idx = 0; idx < num_leds; ++idx) {
-			rc = pm8058_set_led_current(
-				driver_channel->leds[idx].id,
-				current_driver->high_current/num_leds);
-			if (rc < 0)
-				pr_err(
-					"%s: FAIL name = %s, rc = %d\n",
-					__func__,
-					driver_channel->leds[idx].name,
-					rc);
-		}
-		break;
-	case MSM_CAMERA_LED_INIT:
-	case MSM_CAMERA_LED_RELEASE:
-		break;
-
-	default:
-		rc = -EFAULT;
-		break;
-	}
-	CDBG("msm_camera_flash_led_pmic8058: return %d\n", rc);
-#endif /* CONFIG_LEDS_PMIC8058 */
-	return rc;
-}
-
-int msm_camera_flash_led(
-		struct msm_camera_sensor_flash_external *external,
-		unsigned led_state)
-{
-	int rc = 0;
-
-	CDBG("msm_camera_flash_led: %d\n", led_state);
-	switch (led_state) {
-	case MSM_CAMERA_LED_INIT:
-		rc = gpio_request(external->led_en, "sgm3141");
-		CDBG("MSM_CAMERA_LED_INIT: gpio_req: %d %d\n",
-				external->led_en, rc);
-		if (!rc)
-			gpio_direction_output(external->led_en, 0);
-		else
-			return 0;
-
-		rc = gpio_request(external->led_flash_en, "sgm3141");
-		CDBG("MSM_CAMERA_LED_INIT: gpio_req: %d %d\n",
-				external->led_flash_en, rc);
-		if (!rc)
-			gpio_direction_output(external->led_flash_en, 0);
-
-			break;
-
-	case MSM_CAMERA_LED_RELEASE:
-		CDBG("MSM_CAMERA_LED_RELEASE\n");
-		gpio_set_value_cansleep(external->led_en, 0);
-		gpio_free(external->led_en);
-		gpio_set_value_cansleep(external->led_flash_en, 0);
-		gpio_free(external->led_flash_en);
-		break;
-
-	case MSM_CAMERA_LED_OFF:
-		CDBG("MSM_CAMERA_LED_OFF\n");
-		gpio_set_value_cansleep(external->led_en, 0);
-		gpio_set_value_cansleep(external->led_flash_en, 0);
-		break;
-
-	case MSM_CAMERA_LED_LOW:
-		CDBG("MSM_CAMERA_LED_LOW\n");
-		gpio_set_value_cansleep(external->led_en, 1);
-		gpio_set_value_cansleep(external->led_flash_en, 1);
-		break;
-
-	case MSM_CAMERA_LED_HIGH:
-		CDBG("MSM_CAMERA_LED_HIGH\n");
-		gpio_set_value_cansleep(external->led_en, 1);
-		gpio_set_value_cansleep(external->led_flash_en, 1);
-		break;
-
-	default:
-		rc = -EFAULT;
-		break;
-	}
-
-	return rc;
-}
-
-int msm_camera_flash_external(
-	struct msm_camera_sensor_flash_external *external,
-	unsigned led_state)
-{
-	int rc = 0;
-
-	switch (led_state) {
-
-	case MSM_CAMERA_LED_INIT:
-		if (external->flash_id == MAM_CAMERA_EXT_LED_FLASH_SC628A) {
-			if (!sc628a_client) {
-				rc = i2c_add_driver(&sc628a_i2c_driver);
-				if (rc < 0 || sc628a_client == NULL) {
-					pr_err("sc628a_i2c_driver add failed\n");
-					rc = -ENOTSUPP;
-					return rc;
-				}
-			}
-		} else if (external->flash_id ==
-			MAM_CAMERA_EXT_LED_FLASH_TPS61310) {
-			if (!tps61310_client) {
-				rc = i2c_add_driver(&tps61310_i2c_driver);
-				if (rc < 0 || tps61310_client == NULL) {
-					pr_err("tps61310_i2c_driver add failed\n");
-					rc = -ENOTSUPP;
-					return rc;
-				}
-			}
-		} else {
-			pr_err("Flash id not supported\n");
-			rc = -ENOTSUPP;
-			return rc;
-		}
-
-#if defined(CONFIG_GPIO_SX150X) || defined(CONFIG_GPIO_SX150X_MODULE)
-		if (external->expander_info && !sx150x_client) {
-			struct i2c_adapter *adapter =
-			i2c_get_adapter(external->expander_info->bus_id);
-			if (adapter)
-				sx150x_client = i2c_new_device(adapter,
-					external->expander_info->board_info);
-			if (!sx150x_client || !adapter) {
-				pr_err("sx150x_client is not available\n");
-				rc = -ENOTSUPP;
-				if (sc628a_client) {
-					i2c_del_driver(&sc628a_i2c_driver);
-					sc628a_client = NULL;
-				}
-				if (tps61310_client) {
-					i2c_del_driver(&tps61310_i2c_driver);
-					tps61310_client = NULL;
-				}
-				return rc;
-			}
-			i2c_put_adapter(adapter);
-		}
-#endif
-		if (sc628a_client)
-			rc = gpio_request(external->led_en, "sc628a");
-		if (tps61310_client)
-			rc = gpio_request(external->led_en, "tps61310");
-
-		if (!rc) {
-			gpio_direction_output(external->led_en, 0);
-		} else {
-			goto error;
-		}
-
-		if (sc628a_client)
-			rc = gpio_request(external->led_flash_en, "sc628a");
-		if (tps61310_client)
-			rc = gpio_request(external->led_flash_en, "tps61310");
-
-		if (!rc) {
-			gpio_direction_output(external->led_flash_en, 0);
-			break;
-		}
-
-		gpio_set_value_cansleep(external->led_en, 0);
-		gpio_free(external->led_en);
-error:
-		pr_err("%s gpio request failed\n", __func__);
-		if (sc628a_client) {
-			i2c_del_driver(&sc628a_i2c_driver);
-			sc628a_client = NULL;
-		}
-		if (tps61310_client) {
-			i2c_del_driver(&tps61310_i2c_driver);
-			tps61310_client = NULL;
-		}
-		break;
-
-	case MSM_CAMERA_LED_RELEASE:
-		if (sc628a_client || tps61310_client) {
-			gpio_set_value_cansleep(external->led_en, 0);
-			gpio_free(external->led_en);
-			gpio_set_value_cansleep(external->led_flash_en, 0);
-			gpio_free(external->led_flash_en);
-			if (sc628a_client) {
-				i2c_del_driver(&sc628a_i2c_driver);
-				sc628a_client = NULL;
-			}
-			if (tps61310_client) {
-				i2c_del_driver(&tps61310_i2c_driver);
-				tps61310_client = NULL;
-			}
-		}
-#if defined(CONFIG_GPIO_SX150X) || defined(CONFIG_GPIO_SX150X_MODULE)
-		if (external->expander_info && sx150x_client) {
-			i2c_unregister_device(sx150x_client);
-			sx150x_client = NULL;
-		}
-#endif
-		break;
-
-	case MSM_CAMERA_LED_OFF:
-		if (sc628a_client) {
-			i2c_client.client = sc628a_client;
-			i2c_client.addr_type = MSM_CAMERA_I2C_BYTE_ADDR;
-			rc = msm_camera_i2c_write(&i2c_client, 0x02, 0x00,
-				MSM_CAMERA_I2C_BYTE_DATA);
-		}
-		if (tps61310_client) {
-			i2c_client.client = tps61310_client;
-			i2c_client.addr_type = MSM_CAMERA_I2C_BYTE_ADDR;
-			rc = msm_camera_i2c_write(&i2c_client, 0x01, 0x00,
-				MSM_CAMERA_I2C_BYTE_DATA);
-		}
-		gpio_set_value_cansleep(external->led_en, 0);
-		gpio_set_value_cansleep(external->led_flash_en, 0);
-		break;
-
-	case MSM_CAMERA_LED_LOW:
-		gpio_set_value_cansleep(external->led_en, 1);
-		gpio_set_value_cansleep(external->led_flash_en, 1);
-		usleep_range(2000, 3000);
-		if (sc628a_client) {
-			i2c_client.client = sc628a_client;
-			i2c_client.addr_type = MSM_CAMERA_I2C_BYTE_ADDR;
-			rc = msm_camera_i2c_write(&i2c_client, 0x02, 0x06,
-				MSM_CAMERA_I2C_BYTE_DATA);
-		}
-		if (tps61310_client) {
-			i2c_client.client = tps61310_client;
-			i2c_client.addr_type = MSM_CAMERA_I2C_BYTE_ADDR;
-			rc = msm_camera_i2c_write(&i2c_client, 0x01, 0x86,
-				MSM_CAMERA_I2C_BYTE_DATA);
-		}
-		break;
-
-	case MSM_CAMERA_LED_HIGH:
-		gpio_set_value_cansleep(external->led_en, 1);
-		gpio_set_value_cansleep(external->led_flash_en, 1);
-		usleep_range(2000, 3000);
-		if (sc628a_client) {
-			i2c_client.client = sc628a_client;
-			i2c_client.addr_type = MSM_CAMERA_I2C_BYTE_ADDR;
-			rc = msm_camera_i2c_write(&i2c_client, 0x02, 0x49,
-				MSM_CAMERA_I2C_BYTE_DATA);
-		}
-		if (tps61310_client) {
-			i2c_client.client = tps61310_client;
-			i2c_client.addr_type = MSM_CAMERA_I2C_BYTE_ADDR;
-			rc = msm_camera_i2c_write(&i2c_client, 0x01, 0x8B,
-				MSM_CAMERA_I2C_BYTE_DATA);
-		}
-		break;
-
-	default:
-		rc = -EFAULT;
-		break;
-	}
-	return rc;
-}
-
-static int msm_camera_flash_pwm(
-	struct msm_camera_sensor_flash_pwm *pwm,
-	unsigned led_state)
-{
-	int rc = 0;
-	int PWM_PERIOD = USEC_PER_SEC / pwm->freq;
-
-	static struct pwm_device *flash_pwm;
-
-	if (!flash_pwm) {
-		flash_pwm = pwm_request(pwm->channel, "camera-flash");
-		if (flash_pwm == NULL || IS_ERR(flash_pwm)) {
-			pr_err("%s: FAIL pwm_request(): flash_pwm=%p\n",
-			       __func__, flash_pwm);
-			flash_pwm = NULL;
-			return -ENXIO;
-		}
-	}
-
-	switch (led_state) {
-	case MSM_CAMERA_LED_LOW:
-		rc = pwm_config(flash_pwm,
-			(PWM_PERIOD/pwm->max_load)*pwm->low_load,
-			PWM_PERIOD);
-		if (rc >= 0)
-			rc = pwm_enable(flash_pwm);
-		break;
-
-	case MSM_CAMERA_LED_HIGH:
-		rc = pwm_config(flash_pwm,
-			(PWM_PERIOD/pwm->max_load)*pwm->high_load,
-			PWM_PERIOD);
-		if (rc >= 0)
-			rc = pwm_enable(flash_pwm);
-		break;
-
-	case MSM_CAMERA_LED_OFF:
-		pwm_disable(flash_pwm);
-		break;
-	case MSM_CAMERA_LED_INIT:
-	case MSM_CAMERA_LED_RELEASE:
-		break;
-
-	default:
-		rc = -EFAULT;
-		break;
-	}
-	return rc;
-}
-
-int msm_camera_flash_pmic(
-	struct msm_camera_sensor_flash_pmic *pmic,
-	unsigned led_state)
-{
-	int rc = 0;
-
-	switch (led_state) {
-	case MSM_CAMERA_LED_OFF:
-		rc = pmic->pmic_set_current(pmic->led_src_1, 0);
-		if (pmic->num_of_src > 1)
-			rc = pmic->pmic_set_current(pmic->led_src_2, 0);
-		break;
-
-	case MSM_CAMERA_LED_LOW:
-		rc = pmic->pmic_set_current(pmic->led_src_1,
-				pmic->low_current);
-		if (pmic->num_of_src > 1)
-			rc = pmic->pmic_set_current(pmic->led_src_2, 0);
-		break;
-
-	case MSM_CAMERA_LED_HIGH:
-		rc = pmic->pmic_set_current(pmic->led_src_1,
-			pmic->high_current);
-		if (pmic->num_of_src > 1)
-			rc = pmic->pmic_set_current(pmic->led_src_2,
-				pmic->high_current);
-		break;
-
-	case MSM_CAMERA_LED_INIT:
-	case MSM_CAMERA_LED_RELEASE:
-		 break;
-
-	default:
-		rc = -EFAULT;
-		break;
-	}
-	CDBG("flash_set_led_state: return %d\n", rc);
-
-	return rc;
-}
-
-int32_t msm_camera_flash_set_led_state(
-	struct msm_camera_sensor_flash_data *fdata, unsigned led_state)
-{
-	int32_t rc;
-
-	if (fdata->flash_type != MSM_CAMERA_FLASH_LED ||
-		fdata->flash_src == NULL)
-		return -ENODEV;
-
-	switch (fdata->flash_src->flash_sr_type) {
-	case MSM_CAMERA_FLASH_SRC_PMIC:
-		rc = msm_camera_flash_pmic(&fdata->flash_src->_fsrc.pmic_src,
-			led_state);
-		break;
-
-	case MSM_CAMERA_FLASH_SRC_PWM:
-		rc = msm_camera_flash_pwm(&fdata->flash_src->_fsrc.pwm_src,
-			led_state);
-		break;
-
-	case MSM_CAMERA_FLASH_SRC_CURRENT_DRIVER:
-		rc = msm_camera_flash_current_driver(
-			&fdata->flash_src->_fsrc.current_driver_src,
-			led_state);
-		break;
-
-	case MSM_CAMERA_FLASH_SRC_EXT:
-		rc = msm_camera_flash_external(
-			&fdata->flash_src->_fsrc.ext_driver_src,
-			led_state);
-		break;
-
-	case MSM_CAMERA_FLASH_SRC_LED1:
-		rc = msm_camera_flash_led(
-				&fdata->flash_src->_fsrc.ext_driver_src,
-				led_state);
-		break;
-
-	default:
-		rc = -ENODEV;
-		break;
-	}
-
-	return rc;
-}
-
-static int msm_strobe_flash_xenon_charge(int32_t flash_charge,
-		int32_t charge_enable, uint32_t flash_recharge_duration)
-{
-	gpio_set_value_cansleep(flash_charge, charge_enable);
-	if (charge_enable) {
-		timer_flash.expires = jiffies +
-			msecs_to_jiffies(flash_recharge_duration);
-		/* add timer for the recharge */
-		if (!timer_pending(&timer_flash))
-			add_timer(&timer_flash);
-	} else
-		del_timer_sync(&timer_flash);
-	return 0;
-}
-
-static void strobe_flash_xenon_recharge_handler(unsigned long data)
-{
-	unsigned long flags;
-	struct msm_camera_sensor_strobe_flash_data *sfdata =
-		(struct msm_camera_sensor_strobe_flash_data *)data;
-
-	spin_lock_irqsave(&sfdata->timer_lock, flags);
-	msm_strobe_flash_xenon_charge(sfdata->flash_charge, 1,
-		sfdata->flash_recharge_duration);
-	spin_unlock_irqrestore(&sfdata->timer_lock, flags);
-
-	return;
-}
-
-static irqreturn_t strobe_flash_charge_ready_irq(int irq_num, void *data)
-{
-	struct msm_camera_sensor_strobe_flash_data *sfdata =
-		(struct msm_camera_sensor_strobe_flash_data *)data;
-
-	/* put the charge signal to low */
-	gpio_set_value_cansleep(sfdata->flash_charge, 0);
-
-	return IRQ_HANDLED;
-}
-
-static int msm_strobe_flash_xenon_init(
-	struct msm_camera_sensor_strobe_flash_data *sfdata)
-{
-	unsigned long flags;
-	int rc = 0;
-
-	spin_lock_irqsave(&sfdata->spin_lock, flags);
-	if (!sfdata->state) {
-
-		rc = config_flash_gpio_table(MSM_CAM_FLASH_ON, sfdata);
-		if (rc < 0) {
-			pr_err("%s: gpio_request failed\n", __func__);
-			goto go_out;
-		}
-		rc = request_irq(sfdata->irq, strobe_flash_charge_ready_irq,
-			IRQF_TRIGGER_RISING, "charge_ready", sfdata);
-		if (rc < 0) {
-			pr_err("%s: request_irq failed %d\n", __func__, rc);
-			goto go_out;
-		}
-
-		spin_lock_init(&sfdata->timer_lock);
-		/* setup timer */
-		init_timer(&timer_flash);
-		timer_flash.function = strobe_flash_xenon_recharge_handler;
-		timer_flash.data = (unsigned long)sfdata;
-	}
-	sfdata->state++;
-go_out:
-	spin_unlock_irqrestore(&sfdata->spin_lock, flags);
-
-	return rc;
-}
-
-static int msm_strobe_flash_xenon_release
-(struct msm_camera_sensor_strobe_flash_data *sfdata, int32_t final_release)
-{
-	unsigned long flags;
-
-	spin_lock_irqsave(&sfdata->spin_lock, flags);
-	if (sfdata->state > 0) {
-		if (final_release)
-			sfdata->state = 0;
-		else
-			sfdata->state--;
-
-		if (!sfdata->state) {
-			free_irq(sfdata->irq, sfdata);
-			config_flash_gpio_table(MSM_CAM_FLASH_OFF, sfdata);
-			if (timer_pending(&timer_flash))
-				del_timer_sync(&timer_flash);
-		}
-	}
-	spin_unlock_irqrestore(&sfdata->spin_lock, flags);
-	return 0;
-}
-
-static void msm_strobe_flash_xenon_fn_init
-	(struct msm_strobe_flash_ctrl *strobe_flash_ptr)
-{
-	strobe_flash_ptr->strobe_flash_init =
-				msm_strobe_flash_xenon_init;
-	strobe_flash_ptr->strobe_flash_charge =
-				msm_strobe_flash_xenon_charge;
-	strobe_flash_ptr->strobe_flash_release =
-				msm_strobe_flash_xenon_release;
-}
-
-int msm_strobe_flash_init(struct msm_sync *sync, uint32_t sftype)
-{
-	int rc = 0;
-	switch (sftype) {
-	case MSM_CAMERA_STROBE_FLASH_XENON:
-		if (sync->sdata->strobe_flash_data) {
-			msm_strobe_flash_xenon_fn_init(&sync->sfctrl);
-			rc = sync->sfctrl.strobe_flash_init(
-			sync->sdata->strobe_flash_data);
-		} else
-			return -ENODEV;
-		break;
-	default:
-		rc = -ENODEV;
-	}
-	return rc;
-}
-
-int msm_strobe_flash_ctrl(struct msm_camera_sensor_strobe_flash_data *sfdata,
-	struct strobe_flash_ctrl_data *strobe_ctrl)
-{
-	int rc = 0;
-	switch (strobe_ctrl->type) {
-	case STROBE_FLASH_CTRL_INIT:
-		if (!sfdata)
-			return -ENODEV;
-		rc = msm_strobe_flash_xenon_init(sfdata);
-		break;
-	case STROBE_FLASH_CTRL_CHARGE:
-		rc = msm_strobe_flash_xenon_charge(sfdata->flash_charge,
-			strobe_ctrl->charge_en,
-			sfdata->flash_recharge_duration);
-		break;
-	case STROBE_FLASH_CTRL_RELEASE:
-		if (sfdata)
-			rc = msm_strobe_flash_xenon_release(sfdata, 0);
-		break;
-	default:
-		pr_err("Invalid Strobe Flash State\n");
-		rc = -EINVAL;
-	}
-	return rc;
-}
-
-int msm_flash_ctrl(struct msm_camera_sensor_info *sdata,
-	struct flash_ctrl_data *flash_info)
-{
-	int rc = 0;
-	sensor_data = sdata;
-	switch (flash_info->flashtype) {
-	case LED_FLASH:
-		rc = msm_camera_flash_set_led_state(sdata->flash_data,
-			flash_info->ctrl_data.led_state);
-			break;
-	case STROBE_FLASH:
-		rc = msm_strobe_flash_ctrl(sdata->strobe_flash_data,
-			&(flash_info->ctrl_data.strobe_ctrl));
-		break;
-	default:
-		pr_err("Invalid Flash MODE\n");
-		rc = -EINVAL;
-	}
-	return rc;
-}
diff --git a/drivers/media/video/msm/flash/Makefile b/drivers/media/video/msm/flash/Makefile
new file mode 100644
index 0000000..8d0812b
--- /dev/null
+++ b/drivers/media/video/msm/flash/Makefile
@@ -0,0 +1,10 @@
+GCC_VERSION      := $(shell $(CONFIG_SHELL) $(PWD)/scripts/gcc-version.sh $(CROSS_COMPILE)gcc)
+ccflags-y += -Idrivers/media/video/msm
+ccflags-y += -Idrivers/media/video/msm/io
+obj-$(CONFIG_MSM_CAMERA_FLASH) += msm_flash.o
+obj-$(CONFIG_MSM_CAMERA_FLASH_SC628A) += sc628a.o
+obj-$(CONFIG_MSM_CAMERA_FLASH_TPS61310) += tps61310.o
+obj-$(CONFIG_MSM_CAMERA_FLASH_PMIC_FLASH) += pmic8058_flash.o
+obj-$(CONFIG_MSM_CAMERA_FLASH_SGM3141) += sgm3141.o
+obj-$(CONFIG_MSM_CAMERA_FLASH_PMIC8058_PWM) += pmic8058_pwm.o
+obj-$(CONFIG_MSM_CAMERA_LED_TRIGGER_FLASH) += led_trigger_flash.o
diff --git a/drivers/media/video/msm/flash/led_trigger_flash.c b/drivers/media/video/msm/flash/led_trigger_flash.c
new file mode 100644
index 0000000..cd34cde
--- /dev/null
+++ b/drivers/media/video/msm/flash/led_trigger_flash.c
@@ -0,0 +1,162 @@
+/* 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/export.h>
+#include "msm_flash.h"
+
+#define FLASH_NAME "camera-led-flash"
+static struct msm_flash_ctrl_t fctrl;
+
+static int msm_camera_led_trigger_flash(struct msm_flash_ctrl_t *fctrl,
+	uint8_t led_state)
+{
+	int rc = 0;
+	CDBG("%s:%d called led_state %d\n", __func__, __LINE__, led_state);
+
+	if (!fctrl->led_trigger[0]) {
+		pr_err("%s:%d failed\n", __func__, __LINE__);
+		return -EINVAL;
+	}
+	switch (led_state) {
+	case MSM_CAMERA_LED_OFF:
+		led_trigger_event(fctrl->led_trigger[0], 0);
+		break;
+
+	case MSM_CAMERA_LED_LOW:
+		led_trigger_event(fctrl->led_trigger[0],
+			fctrl->max_current[0] / 2);
+		break;
+
+	case MSM_CAMERA_LED_HIGH:
+		led_trigger_event(fctrl->led_trigger[0], fctrl->max_current[0]);
+		break;
+
+	case MSM_CAMERA_LED_INIT:
+	case MSM_CAMERA_LED_RELEASE:
+		led_trigger_event(fctrl->led_trigger[0], 0);
+		break;
+
+	default:
+		rc = -EFAULT;
+		break;
+	}
+	CDBG("flash_set_led_state: return %d\n", rc);
+	return rc;
+}
+
+static const struct of_device_id msm_camera_flash_dt_match[] = {
+	{.compatible = "qcom,camera-led-flash"},
+	{}
+};
+
+MODULE_DEVICE_TABLE(of, msm_camera_flash_dt_match);
+
+static struct platform_driver msm_led_trigger_flash_driver = {
+	.driver = {
+		.name = FLASH_NAME,
+		.owner = THIS_MODULE,
+		.of_match_table = msm_camera_flash_dt_match,
+	},
+};
+
+static int32_t msm_led_trigger_flash_probe(struct platform_device *pdev)
+{
+	int32_t rc = 0, i = 0;
+	struct device_node *of_node = pdev->dev.of_node;
+	struct device_node *flash_src_node = NULL;
+	uint32_t count = 0;
+
+	CDBG("%s called\n", __func__);
+
+	if (!of_node) {
+		pr_err("%s of_node NULL\n", __func__);
+		return -EINVAL;
+	}
+
+	fctrl.pdev = pdev;
+
+	rc = of_property_read_u32(of_node, "cell-index", &pdev->id);
+	if (rc < 0) {
+		pr_err("%s:%d failed\n", __func__, __LINE__);
+		return -EINVAL;
+	}
+	CDBG("%s:%d pdev id %d\n", __func__, __LINE__, pdev->id);
+
+	if (of_get_property(of_node, "qcom,flash-source", &count)) {
+		count /= sizeof(uint32_t);
+		CDBG("%s count %d\n", __func__, count);
+		if (count > MAX_LED_TRIGGERS) {
+			pr_err("%s:%d failed\n", __func__, __LINE__);
+			return -EINVAL;
+		}
+		for (i = 0; i < count; i++) {
+			flash_src_node = of_parse_phandle(of_node,
+				"qcom,flash-source", i);
+			if (!flash_src_node) {
+				pr_err("%s:%d flash_src_node NULL\n", __func__,
+					__LINE__);
+				continue;
+			}
+
+			rc = of_property_read_string(flash_src_node,
+				"linux,default-trigger",
+				&fctrl.led_trigger_name[i]);
+			if (rc < 0) {
+				pr_err("%s:%d failed\n", __func__, __LINE__);
+				of_node_put(flash_src_node);
+				continue;
+			}
+
+			CDBG("%s default trigger %s\n", __func__,
+				fctrl.led_trigger_name[i]);
+
+			rc = of_property_read_u32(flash_src_node,
+				"qcom,max-current", &fctrl.max_current[i]);
+			if (rc < 0) {
+				pr_err("%s:%d failed rc %d\n", __func__,
+					__LINE__, rc);
+				of_node_put(flash_src_node);
+				continue;
+			}
+
+			of_node_put(flash_src_node);
+
+			CDBG("%s max_current[%d] %d\n", __func__, i,
+				fctrl.max_current[i]);
+
+			led_trigger_register_simple(fctrl.led_trigger_name[i],
+				&fctrl.led_trigger[i]);
+		}
+	}
+	rc = msm_flash_platform_probe(pdev, &fctrl);
+	return rc;
+}
+
+static int __init msm_flash_add_driver(void)
+{
+	CDBG("%s called\n", __func__);
+	return platform_driver_probe(&msm_led_trigger_flash_driver,
+		msm_led_trigger_flash_probe);
+}
+
+static struct msm_flash_fn_t msm_led_trigger_flash_func_tbl = {
+	.flash_led_config = msm_camera_led_trigger_flash,
+};
+
+static struct msm_flash_ctrl_t fctrl = {
+	.func_tbl = &msm_led_trigger_flash_func_tbl,
+};
+
+module_init(msm_flash_add_driver);
+MODULE_DESCRIPTION("LED TRIGGER FLASH");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/msm/flash/msm_flash.c b/drivers/media/video/msm/flash/msm_flash.c
new file mode 100644
index 0000000..6639a4b
--- /dev/null
+++ b/drivers/media/video/msm/flash/msm_flash.c
@@ -0,0 +1,514 @@
+/* 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 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/hrtimer.h>
+#include <linux/export.h>
+#include <linux/of.h>
+#include <mach/pmic.h>
+#include <mach/camera.h>
+#include <mach/gpio.h>
+#include "msm_flash.h"
+#include "msm.h"
+
+static struct timer_list timer_flash;
+
+enum msm_cam_flash_stat {
+	MSM_CAM_FLASH_OFF,
+	MSM_CAM_FLASH_ON,
+};
+
+static int config_flash_gpio_table(enum msm_cam_flash_stat stat,
+			struct msm_camera_sensor_strobe_flash_data *sfdata)
+{
+	int rc = 0, i = 0;
+	int msm_cam_flash_gpio_tbl[][2] = {
+		{sfdata->flash_trigger, 1},
+		{sfdata->flash_charge, 1},
+		{sfdata->flash_charge_done, 0}
+	};
+
+	if (stat == MSM_CAM_FLASH_ON) {
+		for (i = 0; i < ARRAY_SIZE(msm_cam_flash_gpio_tbl); i++) {
+			rc = gpio_request(msm_cam_flash_gpio_tbl[i][0],
+							  "CAM_FLASH_GPIO");
+			if (unlikely(rc < 0)) {
+				pr_err("%s not able to get gpio\n", __func__);
+				for (i--; i >= 0; i--)
+					gpio_free(msm_cam_flash_gpio_tbl[i][0]);
+				break;
+			}
+			if (msm_cam_flash_gpio_tbl[i][1])
+				gpio_direction_output(
+					msm_cam_flash_gpio_tbl[i][0], 0);
+			else
+				gpio_direction_input(
+					msm_cam_flash_gpio_tbl[i][0]);
+		}
+	} else {
+		for (i = 0; i < ARRAY_SIZE(msm_cam_flash_gpio_tbl); i++) {
+			gpio_direction_input(msm_cam_flash_gpio_tbl[i][0]);
+			gpio_free(msm_cam_flash_gpio_tbl[i][0]);
+		}
+	}
+	return rc;
+}
+
+static int msm_strobe_flash_xenon_charge(int32_t flash_charge,
+		int32_t charge_enable, uint32_t flash_recharge_duration)
+{
+	gpio_set_value_cansleep(flash_charge, charge_enable);
+	if (charge_enable) {
+		timer_flash.expires = jiffies +
+			msecs_to_jiffies(flash_recharge_duration);
+		/* add timer for the recharge */
+		if (!timer_pending(&timer_flash))
+			add_timer(&timer_flash);
+	} else
+		del_timer_sync(&timer_flash);
+	return 0;
+}
+
+static void strobe_flash_xenon_recharge_handler(unsigned long data)
+{
+	unsigned long flags;
+	struct msm_camera_sensor_strobe_flash_data *sfdata =
+		(struct msm_camera_sensor_strobe_flash_data *)data;
+
+	spin_lock_irqsave(&sfdata->timer_lock, flags);
+	msm_strobe_flash_xenon_charge(sfdata->flash_charge, 1,
+		sfdata->flash_recharge_duration);
+	spin_unlock_irqrestore(&sfdata->timer_lock, flags);
+
+	return;
+}
+
+static irqreturn_t strobe_flash_charge_ready_irq(int irq_num, void *data)
+{
+	struct msm_camera_sensor_strobe_flash_data *sfdata =
+		(struct msm_camera_sensor_strobe_flash_data *)data;
+
+	/* put the charge signal to low */
+	gpio_set_value_cansleep(sfdata->flash_charge, 0);
+
+	return IRQ_HANDLED;
+}
+
+static int msm_strobe_flash_xenon_init(
+	struct msm_camera_sensor_strobe_flash_data *sfdata)
+{
+	unsigned long flags;
+	int rc = 0;
+
+	spin_lock_irqsave(&sfdata->spin_lock, flags);
+	if (!sfdata->state) {
+
+		rc = config_flash_gpio_table(MSM_CAM_FLASH_ON, sfdata);
+		if (rc < 0) {
+			pr_err("%s: gpio_request failed\n", __func__);
+			goto go_out;
+		}
+		rc = request_irq(sfdata->irq, strobe_flash_charge_ready_irq,
+			IRQF_TRIGGER_RISING, "charge_ready", sfdata);
+		if (rc < 0) {
+			pr_err("%s: request_irq failed %d\n", __func__, rc);
+			goto go_out;
+		}
+
+		spin_lock_init(&sfdata->timer_lock);
+		/* setup timer */
+		init_timer(&timer_flash);
+		timer_flash.function = strobe_flash_xenon_recharge_handler;
+		timer_flash.data = (unsigned long)sfdata;
+	}
+	sfdata->state++;
+go_out:
+	spin_unlock_irqrestore(&sfdata->spin_lock, flags);
+
+	return rc;
+}
+
+static int msm_strobe_flash_xenon_release
+(struct msm_camera_sensor_strobe_flash_data *sfdata, int32_t final_release)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&sfdata->spin_lock, flags);
+	if (sfdata->state > 0) {
+		if (final_release)
+			sfdata->state = 0;
+		else
+			sfdata->state--;
+
+		if (!sfdata->state) {
+			free_irq(sfdata->irq, sfdata);
+			config_flash_gpio_table(MSM_CAM_FLASH_OFF, sfdata);
+			if (timer_pending(&timer_flash))
+				del_timer_sync(&timer_flash);
+		}
+	}
+	spin_unlock_irqrestore(&sfdata->spin_lock, flags);
+	return 0;
+}
+
+static int msm_strobe_flash_ctrl(
+	struct msm_camera_sensor_strobe_flash_data *sfdata,
+	struct strobe_flash_ctrl_data *strobe_ctrl)
+{
+	int rc = 0;
+	switch (strobe_ctrl->type) {
+	case STROBE_FLASH_CTRL_INIT:
+		if (!sfdata)
+			return -ENODEV;
+		rc = msm_strobe_flash_xenon_init(sfdata);
+		break;
+	case STROBE_FLASH_CTRL_CHARGE:
+		rc = msm_strobe_flash_xenon_charge(sfdata->flash_charge,
+			strobe_ctrl->charge_en,
+			sfdata->flash_recharge_duration);
+		break;
+	case STROBE_FLASH_CTRL_RELEASE:
+		if (sfdata)
+			rc = msm_strobe_flash_xenon_release(sfdata, 0);
+		break;
+	default:
+		pr_err("Invalid Strobe Flash State\n");
+		rc = -EINVAL;
+	}
+	return rc;
+}
+
+int msm_flash_led_init(struct msm_flash_ctrl_t *fctrl)
+{
+	int rc = 0;
+	struct msm_camera_sensor_flash_external *external = NULL;
+	CDBG("%s:%d called\n", __func__, __LINE__);
+	if (!fctrl) {
+		pr_err("%s:%d fctrl NULL\n", __func__, __LINE__);
+		return -EINVAL;
+	}
+	external = &fctrl->flash_data->flash_src->_fsrc.ext_driver_src;
+	if (external->expander_info && !fctrl->expander_client) {
+		struct i2c_adapter *adapter =
+		i2c_get_adapter(external->expander_info->bus_id);
+		if (adapter)
+			fctrl->expander_client = i2c_new_device(adapter,
+				external->expander_info->board_info);
+		if (!fctrl->expander_client || !adapter) {
+			pr_err("fctrl->expander_client is not available\n");
+			rc = -ENOTSUPP;
+			return rc;
+		}
+		i2c_put_adapter(adapter);
+	}
+	rc = msm_camera_init_gpio_table(
+		fctrl->flash_data->flash_src->init_gpio_tbl,
+		fctrl->flash_data->flash_src->init_gpio_tbl_size, 1);
+	if (rc < 0)
+		pr_err("%s:%d failed\n", __func__, __LINE__);
+	return rc;
+}
+
+int msm_flash_led_release(struct msm_flash_ctrl_t *fctrl)
+{
+	struct msm_camera_sensor_flash_external *external = NULL;
+	CDBG("%s:%d called\n", __func__, __LINE__);
+	if (!fctrl) {
+		pr_err("%s:%d fctrl NULL\n", __func__, __LINE__);
+		return -EINVAL;
+	}
+	external = &fctrl->flash_data->flash_src->_fsrc.ext_driver_src;
+	msm_camera_set_gpio_table(
+		fctrl->flash_data->flash_src->set_gpio_tbl,
+		fctrl->flash_data->flash_src->set_gpio_tbl_size, 0);
+	msm_camera_init_gpio_table(
+		fctrl->flash_data->flash_src->init_gpio_tbl,
+		fctrl->flash_data->flash_src->init_gpio_tbl_size, 0);
+	if (external->expander_info && fctrl->expander_client) {
+		i2c_unregister_device(fctrl->expander_client);
+		fctrl->expander_client = NULL;
+	}
+	return 0;
+}
+
+int msm_flash_led_off(struct msm_flash_ctrl_t *fctrl)
+{
+	int rc = 0;
+	struct msm_camera_sensor_flash_external *external = NULL;
+	CDBG("%s:%d called\n", __func__, __LINE__);
+	if (!fctrl) {
+		pr_err("%s:%d fctrl NULL\n", __func__, __LINE__);
+		return -EINVAL;
+	}
+	external = &fctrl->flash_data->flash_src->_fsrc.ext_driver_src;
+	if (fctrl->flash_i2c_client && fctrl->reg_setting) {
+		rc = msm_camera_i2c_write_tbl(
+			fctrl->flash_i2c_client,
+			fctrl->reg_setting->off_setting,
+			fctrl->reg_setting->off_setting_size,
+			fctrl->reg_setting->default_data_type);
+		if (rc < 0)
+			pr_err("%s:%d failed\n", __func__, __LINE__);
+	}
+	msm_camera_set_gpio_table(
+		fctrl->flash_data->flash_src->set_gpio_tbl,
+		fctrl->flash_data->flash_src->set_gpio_tbl_size, 0);
+
+	return rc;
+}
+
+int msm_flash_led_low(struct msm_flash_ctrl_t *fctrl)
+{
+	int rc = 0;
+	struct msm_camera_sensor_flash_external *external = NULL;
+	CDBG("%s:%d called\n", __func__, __LINE__);
+	if (!fctrl) {
+		pr_err("%s:%d fctrl NULL\n", __func__, __LINE__);
+		return -EINVAL;
+	}
+	external = &fctrl->flash_data->flash_src->_fsrc.ext_driver_src;
+	msm_camera_set_gpio_table(
+		fctrl->flash_data->flash_src->set_gpio_tbl,
+		fctrl->flash_data->flash_src->set_gpio_tbl_size, 1);
+	if (fctrl->flash_i2c_client && fctrl->reg_setting) {
+		rc = msm_camera_i2c_write_tbl(
+			fctrl->flash_i2c_client,
+			fctrl->reg_setting->low_setting,
+			fctrl->reg_setting->low_setting_size,
+			fctrl->reg_setting->default_data_type);
+		if (rc < 0)
+			pr_err("%s:%d failed\n", __func__, __LINE__);
+	}
+	return rc;
+}
+
+int msm_flash_led_high(struct msm_flash_ctrl_t *fctrl)
+{
+	int rc = 0;
+	struct msm_camera_sensor_flash_external *external = NULL;
+	CDBG("%s:%d called\n", __func__, __LINE__);
+	if (!fctrl) {
+		pr_err("%s:%d fctrl NULL\n", __func__, __LINE__);
+		return -EINVAL;
+	}
+	external = &fctrl->flash_data->flash_src->_fsrc.ext_driver_src;
+	msm_camera_set_gpio_table(
+		fctrl->flash_data->flash_src->set_gpio_tbl,
+		fctrl->flash_data->flash_src->set_gpio_tbl_size, 1);
+	if (fctrl->flash_i2c_client && fctrl->reg_setting) {
+		rc = msm_camera_i2c_write_tbl(
+			fctrl->flash_i2c_client,
+			fctrl->reg_setting->high_setting,
+			fctrl->reg_setting->high_setting_size,
+			fctrl->reg_setting->default_data_type);
+		if (rc < 0)
+			pr_err("%s:%d failed\n", __func__, __LINE__);
+	}
+	return rc;
+}
+
+int msm_camera_flash_led_config(struct msm_flash_ctrl_t *fctrl,
+	uint8_t led_state)
+{
+	int rc = 0;
+
+	CDBG("%s:%d called\n", __func__, __LINE__);
+	if (!fctrl->func_tbl) {
+		pr_err("%s flash func tbl NULL\n", __func__);
+		return 0;
+	}
+	switch (led_state) {
+	case MSM_CAMERA_LED_INIT:
+		if (fctrl->func_tbl->flash_led_init)
+			rc = fctrl->func_tbl->flash_led_init(fctrl);
+		break;
+
+	case MSM_CAMERA_LED_RELEASE:
+		if (fctrl->func_tbl->flash_led_release)
+			rc = fctrl->func_tbl->
+				flash_led_release(fctrl);
+		break;
+
+	case MSM_CAMERA_LED_OFF:
+		if (fctrl->func_tbl->flash_led_off)
+			rc = fctrl->func_tbl->flash_led_off(fctrl);
+		break;
+
+	case MSM_CAMERA_LED_LOW:
+		if (fctrl->func_tbl->flash_led_low)
+			rc = fctrl->func_tbl->flash_led_low(fctrl);
+		break;
+
+	case MSM_CAMERA_LED_HIGH:
+		if (fctrl->func_tbl->flash_led_high)
+			rc = fctrl->func_tbl->flash_led_high(fctrl);
+		break;
+
+	default:
+		rc = -EFAULT;
+		break;
+	}
+	return rc;
+}
+
+static struct msm_flash_ctrl_t *get_fctrl(struct v4l2_subdev *sd)
+{
+	return container_of(sd, struct msm_flash_ctrl_t, v4l2_sdev);
+}
+
+static long msm_flash_config(struct msm_flash_ctrl_t *fctrl, void __user *argp)
+{
+	long rc = 0;
+	struct flash_ctrl_data flash_info;
+	if (!argp) {
+		pr_err("%s argp NULL\n", __func__);
+		return -EINVAL;
+	}
+	if (copy_from_user(&flash_info, argp, sizeof(flash_info))) {
+		pr_err("%s:%d failed\n", __func__, __LINE__);
+		return -EFAULT;
+	}
+	switch (flash_info.flashtype) {
+	case LED_FLASH:
+		if (fctrl->func_tbl->flash_led_config)
+			rc = fctrl->func_tbl->flash_led_config(fctrl,
+				flash_info.ctrl_data.led_state);
+		if (rc < 0)
+			pr_err("%s:%d failed\n", __func__, __LINE__);
+		break;
+	case STROBE_FLASH:
+		rc = msm_strobe_flash_ctrl(fctrl->strobe_flash_data,
+			&(flash_info.ctrl_data.strobe_ctrl));
+		break;
+	default:
+		pr_err("Invalid Flash MODE\n");
+		rc = -EINVAL;
+	}
+	return rc;
+}
+
+static long msm_flash_subdev_ioctl(struct v4l2_subdev *sd,
+			unsigned int cmd, void *arg)
+{
+	struct msm_flash_ctrl_t *fctrl = NULL;
+	void __user *argp = (void __user *)arg;
+	if (!sd) {
+		pr_err("%s:%d sd NULL\n", __func__, __LINE__);
+		return -EINVAL;
+	}
+	fctrl = get_fctrl(sd);
+	if (!fctrl) {
+		pr_err("%s:%d fctrl NULL\n", __func__, __LINE__);
+		return -EINVAL;
+	}
+	switch (cmd) {
+	case VIDIOC_MSM_FLASH_LED_DATA_CFG:
+		fctrl->flash_data = (struct msm_camera_sensor_flash_data *)argp;
+		return 0;
+	case VIDIOC_MSM_FLASH_STROBE_DATA_CFG:
+		fctrl->strobe_flash_data =
+			(struct msm_camera_sensor_strobe_flash_data *)argp;
+		return 0;
+	case VIDIOC_MSM_FLASH_CFG:
+		return msm_flash_config(fctrl, argp);
+	default:
+		return -ENOIOCTLCMD;
+	}
+}
+
+static struct v4l2_subdev_core_ops msm_flash_subdev_core_ops = {
+	.ioctl = msm_flash_subdev_ioctl,
+};
+
+static struct v4l2_subdev_ops msm_flash_subdev_ops = {
+	.core = &msm_flash_subdev_core_ops,
+};
+
+int msm_flash_i2c_probe(struct i2c_client *client,
+		const struct i2c_device_id *id)
+{
+	int rc = 0;
+	struct msm_flash_ctrl_t *fctrl = NULL;
+	CDBG("%s:%d called\n", __func__, __LINE__);
+
+	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+		pr_err("i2c_check_functionality failed\n");
+		goto probe_failure;
+	}
+
+	fctrl = (struct msm_flash_ctrl_t *)(id->driver_data);
+	if (fctrl->flash_i2c_client)
+		fctrl->flash_i2c_client->client = client;
+
+	/* Assign name for sub device */
+	snprintf(fctrl->v4l2_sdev.name, sizeof(fctrl->v4l2_sdev.name),
+		"%s", id->name);
+
+	/* Initialize sub device */
+	v4l2_i2c_subdev_init(&fctrl->v4l2_sdev, client, &msm_flash_subdev_ops);
+
+	CDBG("%s:%d probe success\n", __func__, __LINE__);
+	return 0;
+
+probe_failure:
+	CDBG("%s:%d probe failed\n", __func__, __LINE__);
+	return rc;
+}
+
+int msm_flash_platform_probe(struct platform_device *pdev, void *data)
+{
+	struct msm_flash_ctrl_t *fctrl = (struct msm_flash_ctrl_t *)data;
+	struct msm_cam_subdev_info sd_info;
+	CDBG("%s:%d called\n", __func__, __LINE__);
+
+	if (!fctrl) {
+		pr_err("%s fctrl NULL\n", __func__);
+		return -EINVAL;
+	}
+
+	/* Initialize sub device */
+	v4l2_subdev_init(&fctrl->v4l2_sdev, &msm_flash_subdev_ops);
+
+	/* Assign name for sub device */
+	snprintf(fctrl->v4l2_sdev.name, sizeof(fctrl->v4l2_sdev.name),
+		"%s", "msm_flash");
+
+	fctrl->pdev = pdev;
+	sd_info.sdev_type = FLASH_DEV;
+	sd_info.sd_index = pdev->id;
+	msm_cam_register_subdev_node(&fctrl->v4l2_sdev, &sd_info);
+
+	CDBG("%s:%d probe success\n", __func__, __LINE__);
+	return 0;
+}
+
+int msm_flash_create_v4l2_subdev(void *data, uint8_t sd_index)
+{
+	struct msm_flash_ctrl_t *fctrl = (struct msm_flash_ctrl_t *)data;
+	struct msm_cam_subdev_info sd_info;
+	CDBG("%s:%d called\n", __func__, __LINE__);
+
+	/* Initialize sub device */
+	v4l2_subdev_init(&fctrl->v4l2_sdev, &msm_flash_subdev_ops);
+
+	/* Assign name for sub device */
+	snprintf(fctrl->v4l2_sdev.name, sizeof(fctrl->v4l2_sdev.name),
+		"%s", "msm_flash");
+
+	sd_info.sdev_type = FLASH_DEV;
+	sd_info.sd_index = sd_index;
+	msm_cam_register_subdev_node(&fctrl->v4l2_sdev, &sd_info);
+
+	CDBG("%s:%d probe success\n", __func__, __LINE__);
+	return 0;
+}
diff --git a/drivers/media/video/msm/flash/msm_flash.h b/drivers/media/video/msm/flash/msm_flash.h
new file mode 100644
index 0000000..2513995
--- /dev/null
+++ b/drivers/media/video/msm/flash/msm_flash.h
@@ -0,0 +1,91 @@
+/* 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 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_FLASH_H
+#define MSM_FLASH_H
+
+#include <linux/i2c.h>
+#include <linux/leds.h>
+#include <media/v4l2-subdev.h>
+#include <mach/board.h>
+#include "msm_camera_i2c.h"
+
+#define MAX_LED_TRIGGERS 2
+
+struct msm_flash_ctrl_t;
+
+struct msm_flash_reg_t {
+	enum msm_camera_i2c_data_type default_data_type;
+	struct msm_camera_i2c_reg_conf *init_setting;
+	uint8_t init_setting_size;
+	struct msm_camera_i2c_reg_conf *off_setting;
+	uint8_t off_setting_size;
+	struct msm_camera_i2c_reg_conf *low_setting;
+	uint8_t low_setting_size;
+	struct msm_camera_i2c_reg_conf *high_setting;
+	uint8_t high_setting_size;
+};
+
+struct msm_flash_fn_t {
+	int (*flash_led_config)(struct msm_flash_ctrl_t *, uint8_t);
+	int (*flash_led_init)(struct msm_flash_ctrl_t *);
+	int (*flash_led_release)(struct msm_flash_ctrl_t *);
+	int (*flash_led_off)(struct msm_flash_ctrl_t *);
+	int (*flash_led_low)(struct msm_flash_ctrl_t *);
+	int (*flash_led_high)(struct msm_flash_ctrl_t *);
+};
+
+struct msm_flash_ctrl_t {
+	struct msm_camera_i2c_client *flash_i2c_client;
+	struct platform_device *pdev;
+	struct i2c_client *expander_client;
+	struct v4l2_subdev v4l2_sdev;
+	struct msm_camera_sensor_flash_data *flash_data;
+	struct msm_camera_sensor_strobe_flash_data *strobe_flash_data;
+	struct msm_flash_fn_t *func_tbl;
+	struct msm_flash_reg_t *reg_setting;
+	const char *led_trigger_name[MAX_LED_TRIGGERS];
+	struct led_trigger *led_trigger[MAX_LED_TRIGGERS];
+	uint32_t max_current[MAX_LED_TRIGGERS];
+	void *data;
+};
+
+int msm_flash_i2c_probe(struct i2c_client *client,
+	const struct i2c_device_id *id);
+
+int msm_flash_platform_probe(struct platform_device *pdev, void *data);
+
+int msm_flash_create_v4l2_subdev(void *data, uint8_t sd_index);
+
+int msm_camera_flash_led_config(struct msm_flash_ctrl_t *fctrl,
+	uint8_t led_state);
+
+int msm_flash_led_init(struct msm_flash_ctrl_t *fctrl);
+
+int msm_flash_led_release(struct msm_flash_ctrl_t *fctrl);
+
+int msm_flash_led_off(struct msm_flash_ctrl_t *fctrl);
+
+int msm_flash_led_low(struct msm_flash_ctrl_t *fctrl);
+
+int msm_flash_led_high(struct msm_flash_ctrl_t *fctrl);
+
+#define VIDIOC_MSM_FLASH_LED_DATA_CFG \
+	_IOWR('V', BASE_VIDIOC_PRIVATE + 20, void __user *)
+
+#define VIDIOC_MSM_FLASH_STROBE_DATA_CFG \
+	_IOWR('V', BASE_VIDIOC_PRIVATE + 21, void __user *)
+
+#define VIDIOC_MSM_FLASH_CFG \
+	_IOWR('V', BASE_VIDIOC_PRIVATE + 22, void __user *)
+
+#endif
diff --git a/drivers/media/video/msm/flash/pmic8058_flash.c b/drivers/media/video/msm/flash/pmic8058_flash.c
new file mode 100644
index 0000000..2017bcb
--- /dev/null
+++ b/drivers/media/video/msm/flash/pmic8058_flash.c
@@ -0,0 +1,79 @@
+/* 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 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/export.h>
+#include "msm_flash.h"
+
+#define SD_INDEX 0
+
+static struct msm_flash_ctrl_t fctrl;
+
+static int msm_camera_pmic_flash(struct msm_flash_ctrl_t *fctrl,
+	uint8_t led_state)
+{
+	int rc = 0;
+	struct msm_camera_sensor_flash_pmic *pmic =
+		&fctrl->flash_data->flash_src->_fsrc.pmic_src;
+
+	switch (led_state) {
+	case MSM_CAMERA_LED_OFF:
+		rc = pmic->pmic_set_current(pmic->led_src_1, 0);
+		if (pmic->num_of_src > 1)
+			rc = pmic->pmic_set_current(pmic->led_src_2, 0);
+		break;
+
+	case MSM_CAMERA_LED_LOW:
+		rc = pmic->pmic_set_current(pmic->led_src_1,
+				pmic->low_current);
+		if (pmic->num_of_src > 1)
+			rc = pmic->pmic_set_current(pmic->led_src_2, 0);
+		break;
+
+	case MSM_CAMERA_LED_HIGH:
+		rc = pmic->pmic_set_current(pmic->led_src_1,
+			pmic->high_current);
+		if (pmic->num_of_src > 1)
+			rc = pmic->pmic_set_current(pmic->led_src_2,
+				pmic->high_current);
+		break;
+
+	case MSM_CAMERA_LED_INIT:
+	case MSM_CAMERA_LED_RELEASE:
+		 break;
+
+	default:
+		rc = -EFAULT;
+		break;
+	}
+	CDBG("flash_set_led_state: return %d\n", rc);
+
+	return rc;
+}
+
+static int __init msm_flash_i2c_add_driver(void)
+{
+	CDBG("%s called\n", __func__);
+	return msm_flash_create_v4l2_subdev(&fctrl, SD_INDEX);
+}
+
+static struct msm_flash_fn_t pmic_flash_func_tbl = {
+	.flash_led_config = msm_camera_pmic_flash,
+};
+
+static struct msm_flash_ctrl_t fctrl = {
+	.func_tbl = &pmic_flash_func_tbl,
+};
+
+module_init(msm_flash_i2c_add_driver);
+MODULE_DESCRIPTION("PMIC FLASH");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/msm/flash/pmic8058_pwm.c b/drivers/media/video/msm/flash/pmic8058_pwm.c
new file mode 100644
index 0000000..2215340
--- /dev/null
+++ b/drivers/media/video/msm/flash/pmic8058_pwm.c
@@ -0,0 +1,89 @@
+/* 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 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/export.h>
+#include <linux/pwm.h>
+
+#include "msm_flash.h"
+#define SD_INDEX 0
+
+static struct msm_flash_ctrl_t fctrl;
+
+static int msm_camera_flash_pwm(struct msm_flash_ctrl_t *fctrl,
+	uint8_t led_state)
+{
+	int rc = 0;
+	struct msm_camera_sensor_flash_pwm *pwm =
+		&fctrl->flash_data->flash_src->_fsrc.pwm_src;
+	int PWM_PERIOD = USEC_PER_SEC / pwm->freq;
+
+	struct pwm_device *flash_pwm = (struct pwm_device *)fctrl->data;
+
+	if (!flash_pwm) {
+		flash_pwm = pwm_request(pwm->channel, "camera-flash");
+		if (flash_pwm == NULL || IS_ERR(flash_pwm)) {
+			pr_err("%s: FAIL pwm_request(): flash_pwm=%p\n",
+			       __func__, flash_pwm);
+			flash_pwm = NULL;
+			return -ENXIO;
+		}
+	}
+
+	switch (led_state) {
+	case MSM_CAMERA_LED_LOW:
+		rc = pwm_config(flash_pwm,
+			(PWM_PERIOD/pwm->max_load)*pwm->low_load,
+			PWM_PERIOD);
+		if (rc >= 0)
+			rc = pwm_enable(flash_pwm);
+		break;
+
+	case MSM_CAMERA_LED_HIGH:
+		rc = pwm_config(flash_pwm,
+			(PWM_PERIOD/pwm->max_load)*pwm->high_load,
+			PWM_PERIOD);
+		if (rc >= 0)
+			rc = pwm_enable(flash_pwm);
+		break;
+
+	case MSM_CAMERA_LED_OFF:
+		pwm_disable(flash_pwm);
+		break;
+	case MSM_CAMERA_LED_INIT:
+	case MSM_CAMERA_LED_RELEASE:
+		break;
+
+	default:
+		rc = -EFAULT;
+		break;
+	}
+	return rc;
+}
+
+static int __init msm_flash_i2c_add_driver(void)
+{
+	CDBG("%s called\n", __func__);
+	return msm_flash_create_v4l2_subdev(&fctrl, SD_INDEX);
+}
+
+static struct msm_flash_fn_t pmic8058_pwm_func_tbl = {
+	.flash_led_config = msm_camera_flash_pwm,
+};
+
+static struct msm_flash_ctrl_t fctrl = {
+	.func_tbl = &pmic8058_pwm_func_tbl,
+};
+
+module_init(msm_flash_i2c_add_driver);
+MODULE_DESCRIPTION("PMIC FLASH");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/msm/flash/sc628a.c b/drivers/media/video/msm/flash/sc628a.c
new file mode 100644
index 0000000..58824e1
--- /dev/null
+++ b/drivers/media/video/msm/flash/sc628a.c
@@ -0,0 +1,91 @@
+/* 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 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/export.h>
+#include "msm_flash.h"
+
+#define FLASH_NAME "sc628a"
+
+static struct msm_flash_ctrl_t fctrl;
+static struct i2c_driver sc628a_i2c_driver;
+
+static struct msm_camera_i2c_reg_conf sc628a_off_setting[] = {
+	{0x02, 0x00},
+};
+
+static struct msm_camera_i2c_reg_conf sc628a_low_setting[] = {
+	{0x02, 0x06},
+};
+
+static struct msm_camera_i2c_reg_conf sc628a_high_setting[] = {
+	{0x02, 0x49},
+};
+
+static int __exit msm_flash_i2c_remove(struct i2c_client *client)
+{
+	i2c_del_driver(&sc628a_i2c_driver);
+	return 0;
+}
+
+static const struct i2c_device_id sc628a_i2c_id[] = {
+	{FLASH_NAME, (kernel_ulong_t)&fctrl},
+	{ }
+};
+
+static struct i2c_driver sc628a_i2c_driver = {
+	.id_table = sc628a_i2c_id,
+	.probe  = msm_flash_i2c_probe,
+	.remove = __exit_p(msm_flash_i2c_remove),
+	.driver = {
+		.name = FLASH_NAME,
+	},
+};
+
+static int __init msm_flash_i2c_add_driver(void)
+{
+	CDBG("%s called\n", __func__);
+	return i2c_add_driver(&sc628a_i2c_driver);
+}
+
+static struct msm_camera_i2c_client sc628a_i2c_client = {
+	.addr_type = MSM_CAMERA_I2C_BYTE_ADDR,
+};
+
+static struct msm_flash_reg_t sc628a_regs = {
+	.default_data_type = MSM_CAMERA_I2C_BYTE_DATA,
+	.off_setting = sc628a_off_setting,
+	.off_setting_size = ARRAY_SIZE(sc628a_off_setting),
+	.low_setting = sc628a_low_setting,
+	.low_setting_size = ARRAY_SIZE(sc628a_low_setting),
+	.high_setting = sc628a_high_setting,
+	.high_setting_size = ARRAY_SIZE(sc628a_high_setting),
+};
+
+static struct msm_flash_fn_t sc628a_func_tbl = {
+	.flash_led_config = msm_camera_flash_led_config,
+	.flash_led_init = msm_flash_led_init,
+	.flash_led_release = msm_flash_led_release,
+	.flash_led_off = msm_flash_led_off,
+	.flash_led_low = msm_flash_led_low,
+	.flash_led_high = msm_flash_led_high,
+};
+
+static struct msm_flash_ctrl_t fctrl = {
+	.flash_i2c_client = &sc628a_i2c_client,
+	.reg_setting = &sc628a_regs,
+	.func_tbl = &sc628a_func_tbl,
+};
+
+subsys_initcall(msm_flash_i2c_add_driver);
+MODULE_DESCRIPTION("SC628A FLASH");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/msm/flash/sgm3141.c b/drivers/media/video/msm/flash/sgm3141.c
new file mode 100644
index 0000000..a8f8ca0
--- /dev/null
+++ b/drivers/media/video/msm/flash/sgm3141.c
@@ -0,0 +1,98 @@
+/* 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 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/export.h>
+#include <mach/gpio.h>
+#include "msm_flash.h"
+
+#define SD_INDEX 0
+
+static struct msm_flash_ctrl_t fctrl;
+
+static int msm_camera_flash_led(struct msm_flash_ctrl_t *fctrl,
+	uint8_t led_state)
+{
+	int rc = 0;
+	struct msm_camera_sensor_flash_external *external =
+		&fctrl->flash_data->flash_src->_fsrc.ext_driver_src;
+
+	CDBG("msm_camera_flash_led: %d\n", led_state);
+	switch (led_state) {
+	case MSM_CAMERA_LED_INIT:
+		rc = gpio_request(external->led_en, "sgm3141");
+		CDBG("MSM_CAMERA_LED_INIT: gpio_req: %d %d\n",
+				external->led_en, rc);
+		if (!rc)
+			gpio_direction_output(external->led_en, 0);
+		else
+			return 0;
+
+		rc = gpio_request(external->led_flash_en, "sgm3141");
+		CDBG("MSM_CAMERA_LED_INIT: gpio_req: %d %d\n",
+				external->led_flash_en, rc);
+		if (!rc)
+			gpio_direction_output(external->led_flash_en, 0);
+
+			break;
+
+	case MSM_CAMERA_LED_RELEASE:
+		CDBG("MSM_CAMERA_LED_RELEASE\n");
+		gpio_set_value_cansleep(external->led_en, 0);
+		gpio_free(external->led_en);
+		gpio_set_value_cansleep(external->led_flash_en, 0);
+		gpio_free(external->led_flash_en);
+		break;
+
+	case MSM_CAMERA_LED_OFF:
+		CDBG("MSM_CAMERA_LED_OFF\n");
+		gpio_set_value_cansleep(external->led_en, 0);
+		gpio_set_value_cansleep(external->led_flash_en, 0);
+		break;
+
+	case MSM_CAMERA_LED_LOW:
+		CDBG("MSM_CAMERA_LED_LOW\n");
+		gpio_set_value_cansleep(external->led_en, 1);
+		gpio_set_value_cansleep(external->led_flash_en, 1);
+		break;
+
+	case MSM_CAMERA_LED_HIGH:
+		CDBG("MSM_CAMERA_LED_HIGH\n");
+		gpio_set_value_cansleep(external->led_en, 1);
+		gpio_set_value_cansleep(external->led_flash_en, 1);
+		break;
+
+	default:
+		rc = -EFAULT;
+		break;
+	}
+
+	return rc;
+}
+
+static int __init msm_flash_i2c_add_driver(void)
+{
+	CDBG("%s called\n", __func__);
+	return msm_flash_create_v4l2_subdev(&fctrl, SD_INDEX);
+}
+
+static struct msm_flash_fn_t sgm3141_func_tbl = {
+	.flash_led_config = msm_camera_flash_led,
+};
+
+static struct msm_flash_ctrl_t fctrl = {
+	.func_tbl = &sgm3141_func_tbl,
+};
+
+module_init(msm_flash_i2c_add_driver);
+MODULE_DESCRIPTION("SGM3141 FLASH");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/msm/flash/tps61310.c b/drivers/media/video/msm/flash/tps61310.c
new file mode 100644
index 0000000..63e6955
--- /dev/null
+++ b/drivers/media/video/msm/flash/tps61310.c
@@ -0,0 +1,97 @@
+/* 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 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/export.h>
+#include "msm_flash.h"
+
+#define FLASH_NAME "tps61310"
+
+static struct msm_flash_ctrl_t fctrl;
+static struct i2c_driver tps61310_i2c_driver;
+
+static struct msm_camera_i2c_reg_conf tps61310_init_setting[] = {
+	{0x01, 0x00},
+};
+
+static struct msm_camera_i2c_reg_conf tps61310_off_setting[] = {
+	{0x01, 0x00},
+};
+
+static struct msm_camera_i2c_reg_conf tps61310_low_setting[] = {
+	{0x01, 0x86},
+};
+
+static struct msm_camera_i2c_reg_conf tps61310_high_setting[] = {
+	{0x01, 0x8B},
+};
+
+static int __exit msm_flash_i2c_remove(struct i2c_client *client)
+{
+	i2c_del_driver(&tps61310_i2c_driver);
+	return 0;
+}
+
+static const struct i2c_device_id tps61310_i2c_id[] = {
+	{FLASH_NAME, (kernel_ulong_t)&fctrl},
+	{ }
+};
+
+static struct i2c_driver tps61310_i2c_driver = {
+	.id_table = tps61310_i2c_id,
+	.probe  = msm_flash_i2c_probe,
+	.remove = __exit_p(msm_flash_i2c_remove),
+	.driver = {
+		.name = FLASH_NAME,
+	},
+};
+
+static int __init msm_flash_i2c_add_driver(void)
+{
+	CDBG("%s called\n", __func__);
+	return i2c_add_driver(&tps61310_i2c_driver);
+}
+
+static struct msm_camera_i2c_client tps61310_i2c_client = {
+	.addr_type = MSM_CAMERA_I2C_BYTE_ADDR,
+};
+
+static struct msm_flash_reg_t tps61310_regs = {
+	.default_data_type = MSM_CAMERA_I2C_BYTE_DATA,
+	.init_setting = tps61310_init_setting,
+	.init_setting_size = ARRAY_SIZE(tps61310_init_setting),
+	.off_setting = tps61310_off_setting,
+	.off_setting_size = ARRAY_SIZE(tps61310_off_setting),
+	.low_setting = tps61310_low_setting,
+	.low_setting_size = ARRAY_SIZE(tps61310_low_setting),
+	.high_setting = tps61310_high_setting,
+	.high_setting_size = ARRAY_SIZE(tps61310_high_setting),
+};
+
+static struct msm_flash_fn_t tps61310_func_tbl = {
+	.flash_led_config = msm_camera_flash_led_config,
+	.flash_led_init = msm_flash_led_init,
+	.flash_led_release = msm_flash_led_release,
+	.flash_led_off = msm_flash_led_off,
+	.flash_led_low = msm_flash_led_low,
+	.flash_led_high = msm_flash_led_high,
+};
+
+static struct msm_flash_ctrl_t fctrl = {
+	.flash_i2c_client = &tps61310_i2c_client,
+	.reg_setting = &tps61310_regs,
+	.func_tbl = &tps61310_func_tbl,
+};
+
+subsys_initcall(msm_flash_i2c_add_driver);
+MODULE_DESCRIPTION("TPS61310 FLASH");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/msm/io/msm_camera_io_util.c b/drivers/media/video/msm/io/msm_camera_io_util.c
index 613850b..1e0a013 100644
--- a/drivers/media/video/msm/io/msm_camera_io_util.c
+++ b/drivers/media/video/msm/io/msm_camera_io_util.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2011-2012, The Linux Foundataion. 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
@@ -494,3 +494,42 @@
 		pr_warning("%s: INVALID CASE\n", __func__);
 	}
 }
+
+int msm_camera_init_gpio_table(struct gpio *gpio_tbl, uint8_t gpio_tbl_size,
+	int gpio_en)
+{
+	int rc = 0;
+
+	if (gpio_en) {
+		rc = gpio_request_array(gpio_tbl, gpio_tbl_size);
+		if (rc < 0) {
+			pr_err("%s:%d failed\n" , __func__, __LINE__);
+			return rc;
+		}
+	} else {
+		gpio_free_array(gpio_tbl, gpio_tbl_size);
+	}
+	return rc;
+}
+
+int msm_camera_set_gpio_table(struct msm_gpio_set_tbl *gpio_tbl,
+	uint8_t gpio_tbl_size, int gpio_en)
+{
+	int rc = 0, i;
+
+	if (gpio_en) {
+		for (i = 0; i < gpio_tbl_size; i++) {
+			gpio_set_value_cansleep(gpio_tbl[i].gpio,
+				gpio_tbl[i].flags);
+			usleep_range(gpio_tbl[i].delay,
+				gpio_tbl[i].delay + 1000);
+		}
+	} else {
+		for (i = gpio_tbl_size - 1; i >= 0; i--) {
+			if (gpio_tbl[i].flags)
+				gpio_set_value_cansleep(gpio_tbl[i].gpio,
+					GPIOF_OUT_INIT_LOW);
+		}
+	}
+	return rc;
+}
diff --git a/drivers/media/video/msm/io/msm_io_8960.c b/drivers/media/video/msm/io/msm_io_8960.c
index 808cc32..1b56578 100644
--- a/drivers/media/video/msm/io/msm_io_8960.c
+++ b/drivers/media/video/msm/io/msm_io_8960.c
@@ -103,6 +103,14 @@
 		} else
 			CDBG("%s: Bus Client NOT Registered!!!\n", __func__);
 		break;
+	case S_ADV_VIDEO:
+		if (bus_perf_client) {
+			rc = msm_bus_scale_client_update_request(
+				bus_perf_client, 7);
+			CDBG("%s: S_ADV_VIDEO rc = %d\n", __func__, rc);
+		} else
+			CDBG("%s: Bus Client NOT Registered!!!\n", __func__);
+		break;
 	case S_DEFAULT:
 		break;
 	default:
diff --git a/drivers/media/video/msm/jpeg_10/msm_jpeg_dev.c b/drivers/media/video/msm/jpeg_10/msm_jpeg_dev.c
index 0662f54..3e6e0d5 100644
--- a/drivers/media/video/msm/jpeg_10/msm_jpeg_dev.c
+++ b/drivers/media/video/msm/jpeg_10/msm_jpeg_dev.c
@@ -265,6 +265,7 @@
 
 static const struct of_device_id msm_jpeg_dt_match[] = {
 			{.compatible = "qcom,jpeg"},
+			{},
 };
 
 MODULE_DEVICE_TABLE(of, msm_jpeg_dt_match);
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 ff99aa3..31286dd 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
@@ -126,5 +126,11 @@
 #define JPEG_VBIF_OUT_WR_LIM_CONF0             0xD4
 #define JPEG_VBIF_DDR_OUT_MAX_BURST            0xD8
 #define JPEG_VBIF_OCMEM_OUT_MAX_BURST          0xDC
+#define JPEG_VBIF_ARB_CTL                      0xF0
+#define JPEG_VBIF_OUT_AXI_AOOO_EN              0x178
+#define JPEG_VBIF_OUT_AXI_AOOO                 0x17c
+#define JPEG_VBIF_ROUND_ROBIN_QOS_ARB          0x124
+#define JPEG_VBIF_OUT_AXI_AMEMTYPE_CONF0       0x160
+#define JPEG_VBIF_OUT_AXI_AMEMTYPE_CONF1       0x164
 
 #endif /* MSM_JPEG_HW_REG_H */
diff --git a/drivers/media/video/msm/jpeg_10/msm_jpeg_platform.c b/drivers/media/video/msm/jpeg_10/msm_jpeg_platform.c
index 06135ec..38a0ffb 100644
--- a/drivers/media/video/msm/jpeg_10/msm_jpeg_platform.c
+++ b/drivers/media/video/msm/jpeg_10/msm_jpeg_platform.c
@@ -108,12 +108,26 @@
 		jpeg_vbif_base + JPEG_VBIF_IN_WR_LIM_CONF2);
 	writel_relaxed(0x00001010,
 		jpeg_vbif_base + JPEG_VBIF_OUT_RD_LIM_CONF0);
-	writel_relaxed(0x00001010,
+	writel_relaxed(0x00000110,
 		jpeg_vbif_base + JPEG_VBIF_OUT_WR_LIM_CONF0);
 	writel_relaxed(0x00000707,
 		jpeg_vbif_base + JPEG_VBIF_DDR_OUT_MAX_BURST);
-	writel_relaxed(0x00000707,
+	writel_relaxed(0x7,
 		jpeg_vbif_base + JPEG_VBIF_OCMEM_OUT_MAX_BURST);
+	writel_relaxed(0x00000030,
+		jpeg_vbif_base + JPEG_VBIF_ARB_CTL);
+	writel_relaxed(0x00000FFF,
+		jpeg_vbif_base + JPEG_VBIF_OUT_AXI_AOOO_EN);
+	writel_relaxed(0x0FFF0FFF,
+		jpeg_vbif_base + JPEG_VBIF_OUT_AXI_AOOO);
+	/*FE and WE QOS configuration need to be set when
+	QOS RR arbitration is enabled*/
+	writel_relaxed(0x00000001,
+		jpeg_vbif_base + JPEG_VBIF_ROUND_ROBIN_QOS_ARB);
+	writel_relaxed(0x22222222,
+		jpeg_vbif_base + JPEG_VBIF_OUT_AXI_AMEMTYPE_CONF0);
+	writel_relaxed(0x2222,
+		jpeg_vbif_base + JPEG_VBIF_OUT_AXI_AMEMTYPE_CONF1);
 }
 
 
diff --git a/drivers/media/video/msm/msm.c b/drivers/media/video/msm/msm.c
index 50a9776..ef1edae 100644
--- a/drivers/media/video/msm/msm.c
+++ b/drivers/media/video/msm/msm.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
@@ -1396,6 +1396,7 @@
 		}
 	}
 
+	i2c_put_adapter(adapter);
 	return act_sdev;
 
 client_fail:
@@ -1431,6 +1432,7 @@
 	if (eeprom_sdev == NULL)
 		goto client_fail;
 
+	i2c_put_adapter(adapter);
 	return eeprom_sdev;
 client_fail:
 	pr_err("%s client_fail\n", __func__);
@@ -1444,6 +1446,45 @@
 	return NULL;
 }
 
+static struct v4l2_subdev *msm_flash_probe(
+	struct msm_camera_sensor_flash_data *flash_info)
+{
+	struct v4l2_subdev *flash_sdev = NULL;
+	struct i2c_adapter *adapter = NULL;
+	void *flash_client = NULL;
+
+	D("%s called\n", __func__);
+
+	if (!flash_info || !flash_info->board_info)
+		goto probe_fail;
+
+	adapter = i2c_get_adapter(flash_info->bus_id);
+	if (!adapter)
+		goto probe_fail;
+
+	flash_client = i2c_new_device(adapter, flash_info->board_info);
+	if (!flash_client)
+		goto device_fail;
+
+	flash_sdev = (struct v4l2_subdev *)i2c_get_clientdata(flash_client);
+	if (flash_sdev == NULL)
+		goto client_fail;
+
+	i2c_put_adapter(adapter);
+	return flash_sdev;
+
+client_fail:
+	pr_err("%s client_fail\n", __func__);
+	i2c_unregister_device(flash_client);
+device_fail:
+	pr_err("%s device_fail\n", __func__);
+	i2c_put_adapter(adapter);
+	adapter = NULL;
+probe_fail:
+	pr_err("%s probe_fail\n", __func__);
+	return NULL;
+}
+
 /* register a msm sensor into the msm device, which will probe the
  * sensor HW. if the HW exist then create a video device (/dev/videoX/)
  * to represent this sensor */
@@ -1471,6 +1512,7 @@
 
 	pcam->act_sdev = msm_actuator_probe(sdata->actuator_info);
 	pcam->eeprom_sdev = msm_eeprom_probe(sdata->eeprom_info);
+	pcam->flash_sdev = msm_flash_probe(sdata->flash_data);
 
 	D("%s: pcam =0x%p\n", __func__, pcam);
 
@@ -1528,6 +1570,15 @@
 		}
 	}
 
+	if (pcam->flash_sdev) {
+		rc = v4l2_device_register_subdev(&pcam->v4l2_dev,
+			pcam->flash_sdev);
+		if (rc < 0) {
+			D("%s flash sub device register failed\n", __func__);
+			goto failure;
+		}
+	}
+
 	pcam->vnode_id = vnode_count++;
 	return rc;
 
diff --git a/drivers/media/video/msm/msm.h b/drivers/media/video/msm/msm.h
index 17303dd..1198f17 100644
--- a/drivers/media/video/msm/msm.h
+++ b/drivers/media/video/msm/msm.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
@@ -75,6 +75,7 @@
 #define MAX_NUM_JPEG_DEV 3
 #define MAX_NUM_CPP_DEV 1
 #define MAX_NUM_CCI_DEV 1
+#define MAX_NUM_FLASH_DEV 4
 
 /* msm queue management APIs*/
 
@@ -293,6 +294,7 @@
 	struct v4l2_subdev *vfe_sdev; /* vfe sub device */
 	struct v4l2_subdev *eeprom_sdev; /* eeprom sub device */
 	struct v4l2_subdev *cpp_sdev;/*cpp sub device*/
+	struct v4l2_subdev *flash_sdev;/*flash sub device*/
 
 	struct msm_cam_config_dev *config_device;
 
@@ -398,6 +400,7 @@
 	struct v4l2_subdev *sensor_sdev; /* sensor sub device */
 	struct v4l2_subdev *act_sdev; /* actuator sub device */
 	struct v4l2_subdev *eeprom_sdev; /* actuator sub device */
+	struct v4l2_subdev *flash_sdev; /* flash sub device */
 	struct msm_camera_sensor_info *sdata;
 
 	struct msm_device_queue eventData_q; /*payload for events sent to app*/
@@ -580,6 +583,7 @@
 	struct v4l2_subdev *cpp_device[MAX_NUM_CPP_DEV];
 	struct v4l2_subdev *irqr_device;
 	struct v4l2_subdev *cci_device;
+	struct v4l2_subdev *flash_device[MAX_NUM_FLASH_DEV];
 
 	spinlock_t  intr_table_lock;
 	struct irqmgr_intr_lkup_table irq_lkup_table;
@@ -723,6 +727,8 @@
 	struct msm_cam_subdev_info *sd_info);
 int msm_mctl_find_sensor_subdevs(struct msm_cam_media_controller *p_mctl,
 	uint8_t csiphy_core_index, uint8_t csid_core_index);
+int msm_mctl_find_flash_subdev(struct msm_cam_media_controller *p_mctl,
+	uint8_t index);
 int msm_server_open_client(int *p_qidx);
 int msm_server_send_ctrl(struct msm_ctrl_cmd *out, int ctrl_id);
 int msm_server_close_client(int idx);
diff --git a/drivers/media/video/msm/msm_camera.c b/drivers/media/video/msm/msm_camera.c
index dce3630..c40711d 100644
--- a/drivers/media/video/msm/msm_camera.c
+++ b/drivers/media/video/msm/msm_camera.c
@@ -2855,61 +2855,6 @@
 		rc = pmsm->sync->sctrl.s_config(argp);
 		break;
 
-	case MSM_CAM_IOCTL_FLASH_LED_CFG: {
-		uint32_t led_state;
-		if (copy_from_user(&led_state, argp, sizeof(led_state))) {
-			ERR_COPY_FROM_USER();
-			rc = -EFAULT;
-		} else
-			rc = msm_camera_flash_set_led_state(pmsm->sync->
-					sdata->flash_data, led_state);
-		break;
-	}
-
-	case MSM_CAM_IOCTL_STROBE_FLASH_CFG: {
-		uint32_t flash_type;
-		if (copy_from_user(&flash_type, argp, sizeof(flash_type))) {
-			pr_err("msm_strobe_flash_init failed");
-			ERR_COPY_FROM_USER();
-			rc = -EFAULT;
-		} else {
-			CDBG("msm_strobe_flash_init enter");
-			rc = msm_strobe_flash_init(pmsm->sync, flash_type);
-		}
-		break;
-	}
-
-	case MSM_CAM_IOCTL_STROBE_FLASH_RELEASE:
-		if (pmsm->sync->sdata->strobe_flash_data) {
-			rc = pmsm->sync->sfctrl.strobe_flash_release(
-				pmsm->sync->sdata->strobe_flash_data, 0);
-		}
-		break;
-
-	case MSM_CAM_IOCTL_STROBE_FLASH_CHARGE: {
-		uint32_t charge_en;
-		if (copy_from_user(&charge_en, argp, sizeof(charge_en))) {
-			ERR_COPY_FROM_USER();
-			rc = -EFAULT;
-		} else
-			rc = pmsm->sync->sfctrl.strobe_flash_charge(
-			pmsm->sync->sdata->strobe_flash_data->flash_charge,
-			charge_en, pmsm->sync->sdata->strobe_flash_data->
-				flash_recharge_duration);
-		break;
-	}
-
-	case MSM_CAM_IOCTL_FLASH_CTRL: {
-		struct flash_ctrl_data flash_info;
-		if (copy_from_user(&flash_info, argp, sizeof(flash_info))) {
-			ERR_COPY_FROM_USER();
-			rc = -EFAULT;
-		} else
-			rc = msm_flash_ctrl(pmsm->sync->sdata, &flash_info);
-
-		break;
-	}
-
 	case MSM_CAM_IOCTL_ERROR_CONFIG:
 		rc = msm_error_config(pmsm->sync, argp);
 		break;
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 36fb849..0210d23 100644
--- a/drivers/media/video/msm/msm_mctl.c
+++ b/drivers/media/video/msm/msm_mctl.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2011-2012, The Linux Foundataion. 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
@@ -40,6 +40,7 @@
 #include "msm_vfe32.h"
 #include "msm_camera_eeprom.h"
 #include "msm_csi_register.h"
+#include "msm_flash.h"
 
 #ifdef CONFIG_MSM_CAMERA_DEBUG
 #define D(fmt, args...) pr_debug("msm_mctl: " fmt, ##args)
@@ -414,14 +415,9 @@
 	}
 
 	case MSM_CAM_IOCTL_FLASH_CTRL: {
-		struct flash_ctrl_data flash_info;
-		if (copy_from_user(&flash_info, argp, sizeof(flash_info))) {
-			ERR_COPY_FROM_USER();
-			rc = -EFAULT;
-		} else {
-			if (msm_sensor_state_check(p_mctl))
-				rc = msm_flash_ctrl(p_mctl->sdata, &flash_info);
-		}
+		if (p_mctl->flash_sdev && msm_sensor_state_check(p_mctl))
+			rc = v4l2_subdev_call(p_mctl->flash_sdev,
+				core, ioctl, VIDIOC_MSM_FLASH_CFG, argp);
 		break;
 	}
 	case MSM_CAM_IOCTL_PICT_PP:
@@ -520,6 +516,7 @@
 	struct msm_camera_sensor_info *sinfo =
 		(struct msm_camera_sensor_info *) s_ctrl->sensordata;
 	struct msm_camera_device_platform_data *camdev = sinfo->pdata;
+	struct msm_camera_sensor_flash_data *flash_data = sinfo->flash_data;
 	uint8_t csid_core;
 	D("%s\n", __func__);
 	if (!p_mctl) {
@@ -569,6 +566,35 @@
 			goto msm_csi_version;
 		}
 
+		if (!p_mctl->flash_sdev && flash_data) {
+			if ((flash_data->flash_type == MSM_CAMERA_FLASH_LED) &&
+				(flash_data->flash_src_index >= 0))
+				msm_mctl_find_flash_subdev(p_mctl,
+					flash_data->flash_src_index);
+		}
+
+		if (p_mctl->flash_sdev && p_mctl->sdata->flash_data &&
+			p_mctl->sdata->flash_data->flash_type !=
+			MSM_CAMERA_FLASH_NONE) {
+			rc = v4l2_subdev_call(p_mctl->flash_sdev, core, ioctl,
+					VIDIOC_MSM_FLASH_LED_DATA_CFG,
+					p_mctl->sdata->flash_data);
+			if (rc < 0) {
+				pr_err("%s: set flash led failed %d\n",
+				__func__, rc);
+			}
+		}
+
+		if (p_mctl->flash_sdev && p_mctl->sdata->strobe_flash_data) {
+			rc = v4l2_subdev_call(p_mctl->flash_sdev, core, ioctl,
+					VIDIOC_MSM_FLASH_STROBE_DATA_CFG,
+					p_mctl->sdata->strobe_flash_data);
+			if (rc < 0) {
+				pr_err("%s: set strobe flash led failed %d\n",
+				__func__, rc);
+			}
+		}
+
 		pm_qos_add_request(&p_mctl->pm_qos_req_list,
 			PM_QOS_CPU_DMA_LATENCY, PM_QOS_DEFAULT_VALUE);
 		pm_qos_update_request(&p_mctl->pm_qos_req_list,
@@ -739,6 +765,7 @@
 	pmctl->vfe_output_mode = 0;
 	spin_lock_init(&pmctl->pp_info.lock);
 
+	pmctl->flash_sdev = pcam->flash_sdev;
 	pmctl->act_sdev = pcam->act_sdev;
 	pmctl->eeprom_sdev = pcam->eeprom_sdev;
 	pmctl->sensor_sdev = pcam->sensor_sdev;
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/msm_sensor.c b/drivers/media/video/msm/sensors/msm_sensor.c
index 63cf38e..907523c 100644
--- a/drivers/media/video/msm/sensors/msm_sensor.c
+++ b/drivers/media/video/msm/sensors/msm_sensor.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
@@ -603,6 +603,8 @@
 {
 	int32_t rc = 0;
 	uint32_t val = 0;
+	struct msm_camera_sensor_flash_data *flash_data = NULL;
+	struct device_node *flash_src_node = NULL;
 
 	sensordata->flash_data = kzalloc(sizeof(
 		struct msm_camera_sensor_flash_data), GFP_KERNEL);
@@ -611,16 +613,42 @@
 		return -ENOMEM;
 	}
 
-	rc = of_property_read_u32(of_node, "qcom,flash-type", &val);
+	if (!of_get_property(of_node, "qcom,flash-src-index", &val)) {
+		CDBG("%s flash not available\n", __func__);
+		return rc;
+	}
+	flash_data = sensordata->flash_data;
+
+	flash_src_node = of_parse_phandle(of_node, "qcom,flash-src-index", 0);
+	if (!flash_src_node) {
+		pr_err("%s:%d flash_src_node NULL\n", __func__,
+			__LINE__);
+		goto ERROR1;
+	}
+
+	rc = of_property_read_u32(flash_src_node, "qcom,flash-type", &val);
 	CDBG("%s qcom,flash-type %d, rc %d\n", __func__, val, rc);
 	if (rc < 0) {
 		pr_err("%s failed %d\n", __func__, __LINE__);
-		goto ERROR;
+		goto ERROR2;
 	}
-	sensordata->flash_data->flash_type = val;
+	flash_data->flash_type = val;
+
+	rc = of_property_read_u32(flash_src_node, "cell-index", &val);
+	CDBG("%s qcom,flash-src-index %d, rc %d\n", __func__, val, rc);
+	if (rc < 0) {
+		pr_err("%s failed %d\n", __func__, __LINE__);
+		goto ERROR2;
+	}
+	flash_data->flash_src_index = val;
+
+	of_node_put(flash_src_node);
+
 	return rc;
-ERROR:
-	kfree(sensordata->flash_data);
+ERROR2:
+	of_node_put(flash_src_node);
+ERROR1:
+	flash_data->flash_type = MSM_CAMERA_FLASH_NONE;
 	return rc;
 }
 
diff --git a/drivers/media/video/msm/server/msm_cam_server.c b/drivers/media/video/msm/server/msm_cam_server.c
index b8b1d51..f61b74f 100644
--- a/drivers/media/video/msm/server/msm_cam_server.c
+++ b/drivers/media/video/msm/server/msm_cam_server.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
@@ -473,16 +473,15 @@
 	kfree(ctrlcmd);
 	free_qcmd(rcmd);
 	D("%s: rc %d\n", __func__, rc);
-	/* rc is the time elapsed. */
-	if (rc >= 0) {
-		/* TODO: Refactor msm_ctrl_cmd::status field */
-		if (out->status == 0)
-			rc = -1;
-		else if (out->status == 1 || out->status == 4)
-			rc = 0;
-		else
-			rc = -EINVAL;
-	}
+	/* rc is the time elapsed.
+	 * This means that the communication with the daemon itself was
+	 * successful(irrespective of the handling of the ctrlcmd).
+	 * So, just reset the rc to 0 to indicate success.
+	 * Its upto the caller to parse the ctrlcmd to check the status. We
+	 * dont need to parse it here. */
+	if (rc >= 0)
+		rc = 0;
+
 	return rc;
 
 ctrlcmd_alloc_fail:
@@ -846,9 +845,9 @@
 			rc = -EINVAL;
 			goto end;
 		}
-
+		tmp_cmd.status = cmd_ptr->status = ctrlcmd.status;
 		if (copy_to_user((void __user *)ioctl_ptr->ioctl_ptr,
-			(void *)&tmp_cmd, cmd_len)) {
+			(void *)cmd_ptr, cmd_len)) {
 			pr_err("%s: copy_to_user failed in cpy, size=%d\n",
 				__func__, cmd_len);
 			rc = -EINVAL;
@@ -1645,6 +1644,7 @@
 
 static const struct v4l2_ioctl_ops msm_ioctl_ops_server = {
 	.vidioc_subscribe_event = msm_server_v4l2_subscribe_event,
+	.vidioc_unsubscribe_event = msm_server_v4l2_unsubscribe_event,
 	.vidioc_default = msm_ioctl_server,
 };
 
@@ -1853,6 +1853,14 @@
 	return rc;
 }
 
+int msm_mctl_find_flash_subdev(struct msm_cam_media_controller *p_mctl,
+	uint8_t index)
+{
+	if (index < MAX_NUM_FLASH_DEV)
+		p_mctl->flash_sdev = g_server_dev.flash_device[index];
+	return 0;
+}
+
 static irqreturn_t msm_camera_server_parse_irq(int irq_num, void *data)
 {
 	unsigned long flags;
@@ -2327,6 +2335,16 @@
 				sd_info->irq_num);
 		}
 		break;
+
+	case FLASH_DEV:
+		if (index >= MAX_NUM_FLASH_DEV) {
+			pr_err("%s Invalid flash idx %d", __func__, index);
+			err = -EINVAL;
+			break;
+		}
+		g_server_dev.flash_device[index] = sd;
+		break;
+
 	default:
 		break;
 	}
diff --git a/drivers/media/video/msm/server/msm_cam_server.h b/drivers/media/video/msm/server/msm_cam_server.h
index 5e39d25..387c254 100644
--- a/drivers/media/video/msm/server/msm_cam_server.h
+++ b/drivers/media/video/msm/server/msm_cam_server.h
@@ -17,7 +17,7 @@
 #include <linux/proc_fs.h>
 #include <linux/ioctl.h>
 #include <mach/camera.h>
-#include "msm.h"
+#include "../msm.h"
 
 uint32_t msm_cam_server_get_mctl_handle(void);
 struct iommu_domain *msm_cam_server_get_domain(void);
diff --git a/drivers/media/video/msm/vfe/msm_vfe32.c b/drivers/media/video/msm/vfe/msm_vfe32.c
index db0db36..a382d53 100644
--- a/drivers/media/video/msm/vfe/msm_vfe32.c
+++ b/drivers/media/video/msm/vfe/msm_vfe32.c
@@ -1464,8 +1464,10 @@
 	CDBG("VFE opertaion mode = 0x%x, output mode = 0x%x\n",
 		vfe32_ctrl->share_ctrl->operation_mode,
 		vfe32_ctrl->share_ctrl->outpath.output_mode);
-		msm_camera_io_w_mb(1, vfe32_ctrl->share_ctrl->vfebase +
-			VFE_CAMIF_COMMAND);
+	msm_camera_io_w_mb(1, vfe32_ctrl->share_ctrl->vfebase +
+		VFE_CAMIF_COMMAND);
+	msm_camera_io_w_mb(VFE_AXI_CFG_MASK,
+		vfe32_ctrl->share_ctrl->vfebase + VFE_AXI_CFG);
 }
 
 static int vfe32_start_recording(
@@ -5789,7 +5791,7 @@
 void axi_start(struct msm_cam_media_controller *pmctl,
 	struct axi_ctrl_t *axi_ctrl, struct msm_camera_vfe_params_t vfe_params)
 {
-	int rc = 0;
+	int rc = 0, bus_vector_idx = 0;
 	uint32_t reg_update = 0;
 	uint32_t vfe_mode =
 		(axi_ctrl->share_ctrl->current_mode &
@@ -5811,9 +5813,22 @@
 			pmctl->sdata->pdata->cam_bus_scale_table, S_CAPTURE);
 		break;
 	case AXI_CMD_RECORD:
+		if (cpu_is_msm8930() || cpu_is_msm8930aa() ||
+			cpu_is_msm8930ab()) {
+			if (axi_ctrl->share_ctrl->current_mode &
+				VFE_OUTPUTS_PREVIEW_AND_VIDEO
+			|| axi_ctrl->share_ctrl->current_mode &
+				VFE_OUTPUTS_VIDEO_AND_PREVIEW)
+				bus_vector_idx = S_VIDEO;
+			else
+				bus_vector_idx = S_ADV_VIDEO;
+		} else {
+			bus_vector_idx = S_VIDEO;
+		}
 		if (!axi_ctrl->share_ctrl->dual_enabled)
 			msm_camio_bus_scale_cfg(
-			pmctl->sdata->pdata->cam_bus_scale_table, S_VIDEO);
+			pmctl->sdata->pdata->cam_bus_scale_table,
+			bus_vector_idx);
 		return;
 	case AXI_CMD_ZSL:
 		if (!axi_ctrl->share_ctrl->dual_enabled)
@@ -6046,6 +6061,8 @@
 	uint32_t vfe_mode =
 	axi_ctrl->share_ctrl->current_mode & ~(VFE_OUTPUTS_RDI0|
 		VFE_OUTPUTS_RDI1);
+	int bus_vector_idx = 0;
+
 	switch (vfe_params.cmd_type) {
 	case AXI_CMD_PREVIEW:
 	case AXI_CMD_CAPTURE:
@@ -6059,9 +6076,17 @@
 			pmctl->sdata->pdata->cam_bus_scale_table, S_PREVIEW);
 		return;
 	case AXI_CMD_LIVESHOT:
-		if (!axi_ctrl->share_ctrl->dual_enabled)
+		if (!axi_ctrl->share_ctrl->dual_enabled) {
+			bus_vector_idx = S_VIDEO;
+
+			if (cpu_is_msm8930() || cpu_is_msm8930aa() ||
+				cpu_is_msm8930ab())
+				bus_vector_idx = S_ADV_VIDEO;
+
 			msm_camio_bus_scale_cfg(
-			pmctl->sdata->pdata->cam_bus_scale_table, S_VIDEO);
+			pmctl->sdata->pdata->cam_bus_scale_table,
+			bus_vector_idx);
+		}
 		return;
 	default:
 		return;
diff --git a/drivers/media/video/msm/vfe/msm_vfe32.h b/drivers/media/video/msm/vfe/msm_vfe32.h
index f985221..169c34e 100644
--- a/drivers/media/video/msm/vfe/msm_vfe32.h
+++ b/drivers/media/video/msm/vfe/msm_vfe32.h
@@ -913,6 +913,7 @@
 #define VFE_DMI_ADDR                    0x0000059C
 #define VFE_DMI_DATA_HI                 0x000005A0
 #define VFE_DMI_DATA_LO                 0x000005A4
+#define VFE_AXI_CFG                     0x00000600
 #define VFE_BUS_IO_FORMAT_CFG           0x000006F8
 #define VFE_PIXEL_IF_CFG                0x000006FC
 #define VFE_RDI0_CFG                    0x00000734
@@ -923,6 +924,8 @@
 #define VFE33_DMI_DATA_HI               0x000005A0
 #define VFE33_DMI_DATA_LO               0x000005A4
 
+#define VFE_AXI_CFG_MASK                0xFFFFFFFF
+
 #define VFE32_OUTPUT_MODE_PT			BIT(0)
 #define VFE32_OUTPUT_MODE_S			BIT(1)
 #define VFE32_OUTPUT_MODE_V			BIT(2)
diff --git a/drivers/media/video/msm/vfe/msm_vfe40.c b/drivers/media/video/msm/vfe/msm_vfe40.c
index a084a6d..81a6dd2 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)
@@ -822,29 +820,34 @@
 	CDBG("%s: Use bayer stats = %d\n", __func__,
 		 vfe40_use_bayer_stats(vfe40_ctrl));
 
-	msm_camera_io_w(0x8350001F,
-	vfe40_ctrl->share_ctrl->vfebase +
-			VFE_BUS_STATS_HIST_WR_UB_CFG);
-	msm_camera_io_w(0x8370002F,
-		vfe40_ctrl->share_ctrl->vfebase +
-			VFE_BUS_STATS_BG_WR_UB_CFG);
-	msm_camera_io_w(0x83A0002F,
-		vfe40_ctrl->share_ctrl->vfebase +
-			VFE_BUS_STATS_BF_WR_UB_CFG);
-	msm_camera_io_w(0x83D0000F,
+	msm_camera_io_w(0x82F80007,
 		vfe40_ctrl->share_ctrl->vfebase +
 			VFE_BUS_STATS_RS_WR_UB_CFG);
-	msm_camera_io_w(0x83E00007,
+	msm_camera_io_w(0x8300000F,
 		vfe40_ctrl->share_ctrl->vfebase +
 			VFE_BUS_STATS_CS_WR_UB_CFG);
-	msm_camera_io_w(0x83E80007,
+
+	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 +
@@ -854,6 +857,9 @@
 			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 +
@@ -877,6 +883,9 @@
 			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 +
@@ -898,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,
@@ -1090,7 +1102,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_AWB);
 	spin_unlock_irqrestore(&vfe40_ctrl->stats_bufq_lock, flags);
@@ -1115,7 +1126,40 @@
 	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 = 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 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;
@@ -1148,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;
@@ -1193,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);
@@ -1860,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__);
@@ -1894,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__);
@@ -2034,15 +2077,25 @@
 		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: {
 		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) {
+
+		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 BG",
+					__func__);
+				goto proc_general_done;
+			}
+		} else if (VFE_CMD_STATS_BG_START == cmd->id) {
 			module_val |= BG_ENABLE_MASK;
-			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 BG",
 					__func__);
@@ -2050,7 +2103,7 @@
 			}
 		} 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 BF",
 					__func__);
@@ -2683,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;
@@ -2695,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;
@@ -2749,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 */
@@ -2765,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;
 
@@ -3338,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:
@@ -4010,6 +4063,16 @@
 				vfe40_ctrl->stats_ops.client);
 		}
 		break;
+	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;
@@ -4096,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;
@@ -4113,6 +4176,29 @@
 				&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;
@@ -4123,16 +4209,16 @@
 	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_ctrl->bgStatsControl.bufToRender =
 			vfe40_process_stats_irq_common(vfe40_ctrl, statsBgNum,
 			addr);
 
 		vfe_send_stats_msg(vfe40_ctrl,
-			vfe40_ctrl->aecbgStatsControl.bufToRender, statsBgNum);
+			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);
 	}
 }
 
@@ -4167,16 +4253,16 @@
 	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_ctrl->bfStatsControl.bufToRender =
 			vfe40_process_stats_irq_common(vfe40_ctrl, statsBfNum,
 			addr);
 
 		vfe_send_stats_msg(vfe40_ctrl,
-			vfe40_ctrl->afbfStatsControl.bufToRender, statsBfNum);
+			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);
 	}
 }
 
@@ -4277,22 +4363,39 @@
 
 	CDBG("%s, stats = 0x%x\n", __func__, status_bits);
 	spin_lock_irqsave(&vfe40_ctrl->stats_bufq_lock, flags);
-	stats_type = 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, 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) {
@@ -4317,17 +4420,17 @@
 		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, 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) {
@@ -4436,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);
@@ -4517,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) |
@@ -4605,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,
@@ -4874,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 &&
@@ -4917,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;
@@ -4940,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:
@@ -5052,39 +5170,44 @@
 
 static void msm_vfe40_init_vbif_parms(void __iomem *vfe_vbif_base)
 {
-	msm_camera_io_w_mb(0x1,
+	msm_camera_io_w(0x1,
 		vfe_vbif_base + VFE40_VBIF_CLKON);
-	msm_camera_io_w_mb(0x1,
-		vfe_vbif_base + VFE40_VBIF_ROUND_ROBIN_QOS_ARB);
-	msm_camera_io_w_mb(0xFFFF,
-		vfe_vbif_base + VFE40_VBIF_OUT_AXI_AOOO_EN);
-	msm_camera_io_w_mb(0xFFFFFFFF,
-		vfe_vbif_base + VFE40_VBIF_OUT_AXI_AOOO);
-
-	msm_camera_io_w_mb(0x10101010,
+	msm_camera_io_w(0x01010101,
 		vfe_vbif_base + VFE40_VBIF_IN_RD_LIM_CONF0);
-	msm_camera_io_w_mb(0x10101010,
+	msm_camera_io_w(0x01010101,
 		vfe_vbif_base + VFE40_VBIF_IN_RD_LIM_CONF1);
-	msm_camera_io_w_mb(0x10101010,
+	msm_camera_io_w(0x10010110,
 		vfe_vbif_base + VFE40_VBIF_IN_RD_LIM_CONF2);
-	msm_camera_io_w_mb(0x10101010,
+	msm_camera_io_w(0x10101010,
 		vfe_vbif_base + VFE40_VBIF_IN_WR_LIM_CONF0);
-	msm_camera_io_w_mb(0x10101010,
+	msm_camera_io_w(0x10101010,
 		vfe_vbif_base + VFE40_VBIF_IN_WR_LIM_CONF1);
-	msm_camera_io_w_mb(0x10101010,
+	msm_camera_io_w(0x10101010,
 		vfe_vbif_base + VFE40_VBIF_IN_WR_LIM_CONF2);
-	msm_camera_io_w_mb(0x00001010,
+	msm_camera_io_w(0x00001010,
 		vfe_vbif_base + VFE40_VBIF_OUT_RD_LIM_CONF0);
-	msm_camera_io_w_mb(0x00001010,
+	msm_camera_io_w(0x00001010,
 		vfe_vbif_base + VFE40_VBIF_OUT_WR_LIM_CONF0);
-	msm_camera_io_w_mb(0x00000707,
+	msm_camera_io_w(0x00000707,
 		vfe_vbif_base + VFE40_VBIF_DDR_OUT_MAX_BURST);
-	msm_camera_io_w_mb(0x00000030,
+	msm_camera_io_w(0x00000707,
+		vfe_vbif_base + VFE40_VBIF_OCMEM_OUT_MAX_BURST);
+	msm_camera_io_w(0x00000030,
 		vfe_vbif_base + VFE40_VBIF_ARB_CTL);
-	msm_camera_io_w_mb(0x04210842,
+	msm_camera_io_w(0x04210842,
 		vfe_vbif_base + VFE40_VBIF_DDR_ARB_CONF0);
-	msm_camera_io_w_mb(0x04210842,
+	msm_camera_io_w(0x04210842,
 		vfe_vbif_base + VFE40_VBIF_DDR_ARB_CONF1);
+	msm_camera_io_w(0x00000001,
+		vfe_vbif_base + VFE40_VBIF_ROUND_ROBIN_QOS_ARB);
+	msm_camera_io_w(0x22222222,
+		vfe_vbif_base + VFE40_VBIF_OUT_AXI_AMEMTYPE_CONF0);
+	msm_camera_io_w(0x00002222,
+		vfe_vbif_base + VFE40_VBIF_OUT_AXI_AMEMTYPE_CONF1);
+	msm_camera_io_w(0x00000FFF,
+		vfe_vbif_base + VFE40_VBIF_OUT_AXI_AOOO_EN);
+	msm_camera_io_w(0x0FFF0FFF,
+		vfe_vbif_base + VFE40_VBIF_OUT_AXI_AOOO);
 }
 
 int msm_axi_subdev_init(struct v4l2_subdev *sd)
diff --git a/drivers/media/video/msm/vfe/msm_vfe40.h b/drivers/media/video/msm/vfe/msm_vfe40.h
index 6363bfb..a94c428 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
 
@@ -901,22 +901,25 @@
 #define VFE40_OUTPUT_MODE_TERTIARY1		BIT(10)
 #define VFE40_OUTPUT_MODE_TERTIARY2		BIT(11)
 
-#define VFE40_VBIF_CLKON				0x4
-#define VFE40_VBIF_IN_RD_LIM_CONF0		0xB0
-#define VFE40_VBIF_IN_RD_LIM_CONF1		0xB4
-#define VFE40_VBIF_IN_RD_LIM_CONF2		0xB8
-#define VFE40_VBIF_IN_WR_LIM_CONF0		0xC0
-#define VFE40_VBIF_IN_WR_LIM_CONF1		0xC4
-#define VFE40_VBIF_IN_WR_LIM_CONF2		0xC8
-#define VFE40_VBIF_OUT_RD_LIM_CONF0		0xD0
-#define VFE40_VBIF_OUT_WR_LIM_CONF0		0xD4
-#define VFE40_VBIF_DDR_OUT_MAX_BURST	0xD8
-#define VFE40_VBIF_ARB_CTL				0xF0
-#define VFE40_VBIF_DDR_ARB_CONF0		0xF4
-#define VFE40_VBIF_DDR_ARB_CONF1		0xF8
-#define VFE40_VBIF_ROUND_ROBIN_QOS_ARB	0x124
-#define VFE40_VBIF_OUT_AXI_AOOO_EN		0x178
-#define VFE40_VBIF_OUT_AXI_AOOO			0x17C
+#define VFE40_VBIF_CLKON					0x4
+#define VFE40_VBIF_IN_RD_LIM_CONF0			0xB0
+#define VFE40_VBIF_IN_RD_LIM_CONF1			0xB4
+#define VFE40_VBIF_IN_RD_LIM_CONF2			0xB8
+#define VFE40_VBIF_IN_WR_LIM_CONF0			0xC0
+#define VFE40_VBIF_IN_WR_LIM_CONF1			0xC4
+#define VFE40_VBIF_IN_WR_LIM_CONF2			0xC8
+#define VFE40_VBIF_OUT_RD_LIM_CONF0			0xD0
+#define VFE40_VBIF_OUT_WR_LIM_CONF0			0xD4
+#define VFE40_VBIF_DDR_OUT_MAX_BURST		0xD8
+#define VFE40_VBIF_OCMEM_OUT_MAX_BURST		0xDC
+#define VFE40_VBIF_ARB_CTL					0xF0
+#define VFE40_VBIF_DDR_ARB_CONF0			0xF4
+#define VFE40_VBIF_DDR_ARB_CONF1			0xF8
+#define VFE40_VBIF_ROUND_ROBIN_QOS_ARB		0x124
+#define VFE40_VBIF_OUT_AXI_AMEMTYPE_CONF0	0x160
+#define VFE40_VBIF_OUT_AXI_AMEMTYPE_CONF1	0x164
+#define VFE40_VBIF_OUT_AXI_AOOO_EN			0x178
+#define VFE40_VBIF_OUT_AXI_AOOO				0x17C
 
 struct vfe_stats_control {
 	uint32_t droppedStatsFrameCount;
@@ -1014,9 +1017,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;
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_smem.c b/drivers/media/video/msm_vidc/msm_smem.c
index 3dd2193..e1b73ef 100644
--- a/drivers/media/video/msm_vidc/msm_smem.c
+++ b/drivers/media/video/msm_vidc/msm_smem.c
@@ -24,7 +24,7 @@
 static int get_device_address(struct ion_client *clnt,
 		struct ion_handle *hndl, int domain_num, int partition_num,
 		unsigned long align, unsigned long *iova,
-		unsigned long *buffer_size)
+		unsigned long *buffer_size, int flags)
 {
 	int rc;
 	if (!iova || !buffer_size || !hndl || !clnt) {
@@ -32,34 +32,47 @@
 				clnt, hndl, iova, buffer_size);
 		return -EINVAL;
 	}
-	if (align < 4096)
-		align = 4096;
 	dprintk(VIDC_DBG, "domain: %d, partition: %d\n",
 		domain_num, partition_num);
+	if (flags & SMEM_SECURE) {
+		if (flags & SMEM_INPUT)
+			rc = msm_ion_secure_buffer(clnt, hndl, 0x1, 0);
+		else
+			rc = msm_ion_secure_buffer(clnt, hndl, 0x2, 0);
+		if (rc) {
+			dprintk(VIDC_ERR, "Failed to secure memory\n");
+			goto mem_secure_failed;
+		}
+	}
 	rc = ion_map_iommu(clnt, hndl, domain_num, partition_num, align,
 			0, iova, buffer_size, 0, 0);
 	if (rc)
 		dprintk(VIDC_ERR,
 		"ion_map_iommu failed(%d).domain: %d,partition: %d\n",
 		rc, domain_num, partition_num);
-
+mem_secure_failed:
 	return rc;
 }
 
 static void put_device_address(struct ion_client *clnt,
-		struct ion_handle *hndl, int domain_num, int partition_num)
+	struct ion_handle *hndl, int domain_num, int partition_num, int flags)
 {
 	ion_unmap_iommu(clnt, hndl, domain_num, partition_num);
+	if (flags & SMEM_SECURE) {
+		if (msm_ion_unsecure_buffer(clnt, hndl))
+			dprintk(VIDC_ERR, "Failed to unsecure memory\n");
+	}
 }
 
 static int ion_user_to_kernel(struct smem_client *client,
 			int fd, u32 offset, int domain, int partition,
-			struct msm_smem *mem)
+			struct msm_smem *mem, int flags)
 {
 	struct ion_handle *hndl;
 	unsigned long iova = 0;
 	unsigned long buffer_size = 0;
 	int rc = 0;
+	int align = SZ_4K;
 	hndl = ion_import_dma_buf(client->clnt, fd);
 	if (IS_ERR_OR_NULL(hndl)) {
 		dprintk(VIDC_ERR, "Failed to get handle: %p, %d, %d, %p\n",
@@ -70,8 +83,13 @@
 	mem->kvaddr = NULL;
 	mem->domain = domain;
 	mem->partition_num = partition;
+	mem->flags = flags;
+
+	if (flags & SMEM_SECURE)
+		align = ALIGN(align, SZ_1M);
+
 	rc = get_device_address(client->clnt, hndl, mem->domain,
-		mem->partition_num, 4096, &iova, &buffer_size);
+		mem->partition_num, align, &iova, &buffer_size, flags);
 	if (rc) {
 		dprintk(VIDC_ERR, "Failed to get device address: %d\n", rc);
 		goto fail_device_address;
@@ -101,15 +119,21 @@
 	unsigned long ionflags = 0;
 	unsigned long heap_mask = 0;
 	int rc = 0;
-	if (flags == SMEM_CACHED)
+	if (flags & SMEM_CACHED)
 		ionflags = ION_SET_CACHED(ionflags);
 	else
 		ionflags = ION_SET_UNCACHED(ionflags);
 
+	align = ALIGN(align, SZ_4K);
+	size = ALIGN(size, SZ_4K);
+
+	if (flags & SMEM_SECURE) {
+		ionflags |= ION_SECURE;
+		size = ALIGN(size, SZ_1M);
+		align = ALIGN(align, SZ_1M);
+	}
+
 	heap_mask = ION_HEAP(ION_CP_MM_HEAP_ID);
-	if (align < 4096)
-		align = 4096;
-	size = (size + 4095) & (~4095);
 	dprintk(VIDC_DBG, "domain: %d, partition: %d\n",
 		domain, partition);
 	hndl = ion_alloc(client->clnt, size, align, heap_mask, ionflags);
@@ -124,6 +148,7 @@
 	mem->smem_priv = hndl;
 	mem->domain = domain;
 	mem->partition_num = partition;
+	mem->flags = flags;
 	if (map_kernel) {
 		mem->kvaddr = ion_map_kernel(client->clnt, hndl);
 		if (!mem->kvaddr) {
@@ -136,7 +161,7 @@
 		mem->kvaddr = NULL;
 
 	rc = get_device_address(client->clnt, hndl, mem->domain,
-		mem->partition_num, align, &iova, &buffer_size);
+		mem->partition_num, align, &iova, &buffer_size, flags);
 	if (rc) {
 		dprintk(VIDC_ERR, "Failed to get device address: %d\n",
 			rc);
@@ -160,7 +185,8 @@
 {
 	if (mem->device_addr)
 		put_device_address(client->clnt,
-			mem->smem_priv, mem->domain, mem->partition_num);
+			mem->smem_priv, mem->domain,
+			mem->partition_num, mem->flags);
 	if (mem->kvaddr)
 		ion_unmap_kernel(client->clnt, mem->smem_priv);
 	if (mem->smem_priv)
@@ -182,7 +208,7 @@
 }
 
 struct msm_smem *msm_smem_user_to_kernel(void *clt, int fd, u32 offset,
-	int domain, int partition)
+	int domain, int partition, int flags)
 {
 	struct smem_client *client = clt;
 	int rc = 0;
@@ -199,7 +225,7 @@
 	switch (client->mem_type) {
 	case SMEM_ION:
 		rc = ion_user_to_kernel(clt, fd, offset,
-			domain, partition, mem);
+			domain, partition, mem, flags);
 		break;
 	default:
 		dprintk(VIDC_ERR, "Mem type not supported\n");
diff --git a/drivers/media/video/msm_vidc/msm_smem.h b/drivers/media/video/msm_vidc/msm_smem.h
index c109abd..8241fdd 100644
--- a/drivers/media/video/msm_vidc/msm_smem.h
+++ b/drivers/media/video/msm_vidc/msm_smem.h
@@ -20,9 +20,10 @@
 	SMEM_ION,
 };
 
-enum smem_cache_prop {
-	SMEM_CACHED,
-	SMEM_UNCACHED,
+enum smem_prop {
+	SMEM_CACHED = 0x1,
+	SMEM_SECURE = 0x2,
+	SMEM_INPUT = 0x4,
 };
 
 struct msm_smem {
@@ -32,6 +33,7 @@
 	unsigned long device_addr;
 	int domain;
 	int partition_num;
+	int flags;
 	void *smem_priv;
 };
 
@@ -41,6 +43,6 @@
 void msm_smem_free(void *clt, struct msm_smem *mem);
 void msm_smem_delete_client(void *clt);
 struct msm_smem *msm_smem_user_to_kernel(void *clt, int fd, u32 offset, int
-		domain, int partition);
+		domain, int partition, int flags);
 int msm_smem_clean_invalidate(void *clt, struct msm_smem *mem);
 #endif
diff --git a/drivers/media/video/msm_vidc/msm_v4l2_vidc.c b/drivers/media/video/msm_vidc/msm_v4l2_vidc.c
index a608e1ab..38e8de1 100644
--- a/drivers/media/video/msm_vidc/msm_v4l2_vidc.c
+++ b/drivers/media/video/msm_vidc/msm_v4l2_vidc.c
@@ -441,12 +441,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 +472,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_INFO,
-				"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 +510,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_INFO, "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)
@@ -542,7 +591,7 @@
 	}
 
 	v4l2_inst->vidc_inst = msm_vidc_open(core->id, vid_dev->type);
-	if (rc) {
+	if (!v4l2_inst->vidc_inst) {
 		dprintk(VIDC_ERR,
 		"Failed to create video instance, core: %d, type = %d\n",
 		core->id, vid_dev->type);
@@ -566,25 +615,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 +645,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 +663,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,7 +754,10 @@
 	struct buffer_info *temp;
 	struct msm_vidc_inst *vidc_inst;
 	struct msm_v4l2_vid_inst *v4l2_inst;
+	int plane = 0;
 	int i, rc = 0;
+	int smem_flags = 0;
+	int domain;
 	vidc_inst = get_vidc_inst(file, fh);
 	v4l2_inst = get_v4l2_inst(file, fh);
 	if (!v4l2_inst->mem_client) {
@@ -703,40 +765,66 @@
 		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,
+		smem_flags = 0;
+		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_INFO,
+				b->m.planes[i].length, &plane);
+		if (temp) {
+			dprintk(VIDC_DBG,
 				"This memory region has already been prepared\n");
 			rc = -EINVAL;
+			kfree(binfo);
 			goto exit;
 		}
-		binfo = kzalloc(sizeof(*binfo), GFP_KERNEL);
-		if (!binfo) {
-			dprintk(VIDC_ERR, "Out of memory\n");
-			rc = -ENOMEM;
-			goto exit;
-		}
+		if ((vidc_inst->mode == VIDC_SECURE)
+				&& (!EXTRADATA_IDX(b->length)
+					|| (i != EXTRADATA_IDX(b->length)))) {
+			smem_flags |= SMEM_SECURE;
+			domain =
+			vidc_inst->core->resources.io_map[CP_MAP].domain;
+		} else
+			domain =
+			vidc_inst->core->resources.io_map[NS_MAP].domain;
+
+		if (b->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+			smem_flags |= SMEM_INPUT;
+
 		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],
 			b->m.planes[i].reserved[1],
-			vidc_inst->core->resources.io_map[NS_MAP].domain,
-			0);
+			domain,	0, smem_flags);
 			if (!handle) {
 				dprintk(VIDC_ERR,
 					"Failed to get device buffer address\n");
@@ -744,21 +832,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 +859,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 +889,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 +910,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 +989,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 +1038,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 = {
@@ -980,9 +1135,11 @@
 					- SHARED_QSIZE;
 			partition[1].size = SHARED_QSIZE;
 			layout.npartitions = 2;
+			layout.is_secure = 0;
 		} else {
 			partition[0].size = io_map[i].addr_range[1];
 			layout.npartitions = 1;
+			layout.is_secure = 1;
 		}
 		layout.partitions = &partition[0];
 		layout.client_name = io_map[i].name;
diff --git a/drivers/media/video/msm_vidc/msm_vdec.c b/drivers/media/video/msm_vidc/msm_vdec.c
index 49b28ae..e0a341a 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
@@ -48,6 +47,27 @@
 	"Decode Order",
 	NULL
 };
+static const char *const mpeg_video_vidc_extradata[] = {
+	"Extradata none",
+	"Extradata MB Quantization",
+	"Extradata Interlace Video",
+	"Extradata VC1 Framedisp",
+	"Extradata VC1 Seqdisp",
+	"Extradata timestamp",
+	"Extradata S3D Frame Packing",
+	"Extradata Frame Rate",
+	"Extradata Panscan Window",
+	"Extradata Recovery point SEI",
+	"Extradata Closed Caption UD",
+	"Extradata AFD UD",
+	"Extradata Multislice info",
+	"Extradata number of concealed MB",
+	"Extradata metadata filler",
+	"Extradata input crop",
+	"Extradata digital zoom",
+	"Extradata aspect ratio",
+};
+
 static const struct msm_vidc_ctrl msm_vdec_ctrls[] = {
 	{
 		.id = V4L2_CID_MPEG_VIDC_VIDEO_STREAM_FORMAT,
@@ -150,6 +170,55 @@
 		.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,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDC_VIDEO_SECURE,
+		.name = "Secure mode",
+		.type = V4L2_CTRL_TYPE_BUTTON,
+		.minimum = 0,
+		.maximum = 0,
+		.default_value = 0,
+		.step = 0,
+		.menu_skip_mask = 0,
+		.qmenu = NULL,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDC_VIDEO_EXTRADATA,
+		.name = "Extradata Type",
+		.type = V4L2_CTRL_TYPE_MENU,
+		.minimum = V4L2_MPEG_VIDC_EXTRADATA_NONE,
+		.maximum = V4L2_MPEG_VIDC_INDEX_EXTRADATA_ASPECT_RATIO,
+		.default_value = V4L2_MPEG_VIDC_EXTRADATA_NONE,
+		.menu_skip_mask = ~(
+			(1 << V4L2_MPEG_VIDC_EXTRADATA_NONE) |
+			(1 << V4L2_MPEG_VIDC_EXTRADATA_MB_QUANTIZATION) |
+			(1 << V4L2_MPEG_VIDC_EXTRADATA_INTERLACE_VIDEO) |
+			(1 << V4L2_MPEG_VIDC_EXTRADATA_VC1_FRAMEDISP) |
+			(1 << V4L2_MPEG_VIDC_EXTRADATA_VC1_SEQDISP) |
+			(1 << V4L2_MPEG_VIDC_EXTRADATA_TIMESTAMP) |
+			(1 << V4L2_MPEG_VIDC_EXTRADATA_S3D_FRAME_PACKING) |
+			(1 << V4L2_MPEG_VIDC_EXTRADATA_FRAME_RATE) |
+			(1 << V4L2_MPEG_VIDC_EXTRADATA_PANSCAN_WINDOW) |
+			(1 << V4L2_MPEG_VIDC_EXTRADATA_RECOVERY_POINT_SEI) |
+			(1 << V4L2_MPEG_VIDC_EXTRADATA_CLOSED_CAPTION_UD) |
+			(1 << V4L2_MPEG_VIDC_EXTRADATA_AFD_UD) |
+			(1 << V4L2_MPEG_VIDC_EXTRADATA_MULTISLICE_INFO) |
+			(1 << V4L2_MPEG_VIDC_EXTRADATA_NUM_CONCEALED_MB) |
+			(1 << V4L2_MPEG_VIDC_EXTRADATA_METADATA_FILLER) |
+			(1 << V4L2_MPEG_VIDC_INDEX_EXTRADATA_INPUT_CROP) |
+			(1 << V4L2_MPEG_VIDC_INDEX_EXTRADATA_DIGITAL_ZOOM) |
+			(1 << V4L2_MPEG_VIDC_INDEX_EXTRADATA_ASPECT_RATIO)
+			),
+		.qmenu = mpeg_video_vidc_extradata,
+		.step = 0,
+	},
 };
 
 #define NUM_CTRLS ARRAY_SIZE(msm_vdec_ctrls)
@@ -157,28 +226,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 +240,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 +301,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 +361,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 extradata 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 +419,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;
+			buffer_info.response_required = false;
 			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;
 }
 
@@ -467,7 +539,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 +556,93 @@
 
 	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);
+				inst->bufq[OUTPUT_PORT].
+					vb2_bufq.plane_sizes[i] =
+					f->fmt.pix_mp.plane_fmt[i].sizeimage;
+			}
+		} 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;
+			}
+			for (i = 0; i < fmt->num_planes; ++i)
+				inst->bufq[CAPTURE_PORT].
+					vb2_bufq.plane_sizes[i] =
+					f->fmt.pix_mp.plane_fmt[i].sizeimage;
+
 		}
 	} 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) {
+		msm_comm_scale_clocks_and_bus(inst);
+	}
+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 +655,75 @@
 		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);
+				"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;
-
+		inst->fmts[fmt->type] = 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);
-		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;
+		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;
+			}
+		}
+		f->fmt.pix_mp.num_planes = fmt->num_planes;
+		for (i = 0; i < fmt->num_planes; ++i) {
+			inst->bufq[CAPTURE_PORT].vb2_bufq.plane_sizes[i] =
+				f->fmt.pix_mp.plane_fmt[i].sizeimage;
 		}
 	} 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) {
+				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);
+			"Format: %d not supported on OUTPUT port\n",
+			f->fmt.pix_mp.pixelformat);
 			rc = -EINVAL;
 			goto err_invalid_fmt;
 		}
-	}
-
-	if (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;
 		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->bufq[OUTPUT_PORT].vb2_bufq.plane_sizes[i] =
+				f->fmt.pix_mp.plane_fmt[i].sizeimage;
 		}
-		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;
-			}
-		}
-	} else {
-		dprintk(VIDC_ERR,
-			"Buf type not recognized, type = %d\n", f->type);
-		rc = -EINVAL;
 	}
 err_invalid_fmt:
 	return rc;
@@ -638,14 +787,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 +808,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 +820,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 +844,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);
@@ -724,11 +878,7 @@
 			"Failed to set persist buffers: %d\n", rc);
 		goto fail_start;
 	}
-	if (msm_comm_scale_clocks(inst->core, inst->session_type)) {
-		dprintk(VIDC_WARN,
-			"Failed to scale clocks. Performance might be impacted\n");
-	}
-
+	msm_comm_scale_clocks_and_bus(inst);
 	rc = msm_comm_try_state(inst, MSM_VIDC_START_DONE);
 	if (rc) {
 		dprintk(VIDC_ERR,
@@ -819,10 +969,7 @@
 		rc = -EINVAL;
 		break;
 	}
-	if (msm_comm_scale_clocks(inst->core, inst->session_type)) {
-		dprintk(VIDC_WARN,
-			"Failed to scale clocks. Power might be impacted\n");
-	}
+	msm_comm_scale_clocks_and_bus(inst);
 
 	if (rc)
 		dprintk(VIDC_ERR,
@@ -842,6 +989,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);
@@ -849,10 +998,20 @@
 	case V4L2_DEC_CMD_STOP:
 		rc = msm_comm_release_scratch_buffers(inst);
 		if (rc)
-			pr_err("Failed to release scratch buffers: %d\n", rc);
+			dprintk(VIDC_ERR,
+				"Failed to release scratch buffers: %d\n", rc);
 		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:
@@ -909,17 +1068,8 @@
 	void *pdata;
 	struct msm_vidc_inst *inst = container_of(ctrl->handler,
 				struct msm_vidc_inst, ctrl_handler);
-	rc = msm_comm_try_state(inst, MSM_VIDC_OPEN_DONE);
-
-	if (rc) {
-		dprintk(VIDC_ERR,
-			"Failed to move inst: %p to start done state\n", inst);
-		goto failed_open_done;
-	}
-
 	control.id = ctrl->id;
 	control.value = ctrl->val;
-
 	switch (control.id) {
 	case V4L2_CID_MPEG_VIDC_VIDEO_STREAM_FORMAT:
 		property_id =
@@ -968,10 +1118,35 @@
 		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;
+	case V4L2_CID_MPEG_VIDC_VIDEO_SECURE:
+		inst->mode = VIDC_SECURE;
+		dprintk(VIDC_DBG, "Setting secure mode to :%d\n", inst->mode);
+		break;
+	case V4L2_CID_MPEG_VIDC_VIDEO_EXTRADATA:
+	{
+		struct hal_extradata_enable extra;
+		property_id = HAL_PARAM_INDEX_EXTRADATA;
+		extra.index = msm_comm_get_hal_extradata_index(control.value);
+		extra.enable = 1;
+		pdata = &extra;
+		break;
+	}
 	default:
 		break;
-		}
+	}
 	if (property_id) {
+		rc = msm_comm_try_state(inst, MSM_VIDC_OPEN_DONE);
+		if (rc) {
+			dprintk(VIDC_ERR,
+			"Failed to move inst: %p to start done state\n", inst);
+			goto failed_open_done;
+		}
 		dprintk(VIDC_DBG,
 			"Control: HAL property=%d,ctrl_id=%d,ctrl_value=%d\n",
 			property_id,
@@ -983,11 +1158,10 @@
 		}
 	if (rc)
 		dprintk(VIDC_ERR, "Failed to set hal property for framesize\n");
-
 failed_open_done:
-
 	return rc;
 }
+
 static int msm_vdec_op_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
 {
 	return 0;
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 4573018..f436cf3 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
@@ -136,11 +136,11 @@
 	{
 		.id = V4L2_CID_MPEG_VIDC_VIDEO_REQUEST_IFRAME,
 		.name = "Request I Frame",
-		.type = V4L2_CTRL_TYPE_BOOLEAN,
+		.type = V4L2_CTRL_TYPE_BUTTON,
 		.minimum = 0,
-		.maximum = 1,
+		.maximum = 0,
 		.default_value = 0,
-		.step = 1,
+		.step = 0,
 		.menu_skip_mask = 0,
 		.qmenu = NULL,
 	},
@@ -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,6 +558,8 @@
 {
 	int i, rc = 0;
 	struct msm_vidc_inst *inst;
+	struct hal_buffer_count_actual new_buf_count;
+	enum hal_property property_id;
 	unsigned long flags;
 	if (!q || !q->drv_priv) {
 		dprintk(VIDC_ERR, "Invalid input, q = %p\n", q);
@@ -607,8 +593,13 @@
 		spin_lock_irqsave(&inst->lock, flags);
 		*num_buffers = inst->buff_req.buffer[0].buffer_count_actual =
 			max(*num_buffers, inst->buff_req.buffer[0].
-			buffer_count_actual);
+				buffer_count_actual);
 		spin_unlock_irqrestore(&inst->lock, flags);
+		property_id = HAL_PARAM_BUFFER_COUNT_ACTUAL;
+		new_buf_count.buffer_type = HAL_BUFFER_INPUT;
+		new_buf_count.buffer_count_actual = *num_buffers;
+		rc = vidc_hal_session_set_property(inst->session,
+					property_id, &new_buf_count);
 		dprintk(VIDC_DBG, "size = %d, alignment = %d, count = %d\n",
 				inst->buff_req.buffer[0].buffer_size,
 				inst->buff_req.buffer[0].buffer_alignment,
@@ -648,10 +639,7 @@
 		dprintk(VIDC_ERR, "Failed to set persist buffers: %d\n", rc);
 		goto fail_start;
 	}
-	if (msm_comm_scale_clocks(inst->core, inst->session_type)) {
-		dprintk(VIDC_WARN,
-			"Failed to scale clocks. Performance might be impacted\n");
-	}
+	msm_comm_scale_clocks_and_bus(inst);
 
 	rc = msm_comm_try_state(inst, MSM_VIDC_START_DONE);
 	if (rc) {
@@ -727,10 +715,7 @@
 		rc = -EINVAL;
 		break;
 	}
-	if (msm_comm_scale_clocks(inst->core, inst->session_type)) {
-		dprintk(VIDC_WARN,
-			"Failed to scale clocks. Power might be impacted\n");
-	}
+	msm_comm_scale_clocks_and_bus(inst);
 
 	if (rc)
 		dprintk(VIDC_ERR,
@@ -824,7 +809,7 @@
 	case V4L2_CID_MPEG_VIDC_VIDEO_REQUEST_IFRAME:
 		property_id =
 			HAL_CONFIG_VENC_REQUEST_IFRAME;
-		request_iframe.enable = control.value;
+		request_iframe.enable = true;
 		pdata = &request_iframe;
 		break;
 	case V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL:
@@ -1260,11 +1245,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;
 	}
@@ -1322,6 +1324,52 @@
 	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);
+		}
+		msm_comm_scale_clocks_and_bus(inst);
+	}
+exit:
+	return rc;
+}
 int msm_venc_s_fmt(struct msm_vidc_inst *inst, struct v4l2_format *f)
 {
 	const struct msm_vidc_format *fmt = NULL;
@@ -1498,6 +1546,44 @@
 	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;
+			buffer_info.response_required = false;
+			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..64897c7 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;
 }
 
@@ -398,11 +413,13 @@
 
 	inst = kzalloc(sizeof(*inst), GFP_KERNEL);
 	if (!inst) {
-		pr_err("Failed to allocate memory\n")	;
+		dprintk(VIDC_ERR, "Failed to allocate memory\n");
 		rc = -ENOMEM;
 		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 4ff28d62..46a88c2 100644
--- a/drivers/media/video/msm_vidc/msm_vidc_common.c
+++ b/drivers/media/video/msm_vidc/msm_vidc_common.c
@@ -17,12 +17,14 @@
 #include <asm/div64.h>
 #include <mach/iommu.h>
 #include <mach/iommu_domains.h>
-#include <mach/peripheral-loader.h>
+#include <mach/subsystem_restart.h>
+#include <mach/scm.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)
 
@@ -49,8 +51,19 @@
 	__mbs;\
 })
 
+#define TZBSP_MEM_PROTECT_VIDEO_VAR 0x8
+struct tzbsp_memprot {
+	u32 cp_start;
+	u32 cp_size;
+	u32 cp_nonpixel_start;
+	u32 cp_nonpixel_size;
+};
+
+struct tzbsp_resp {
+	int ret;
+};
+
 static const u32 bus_table[] = {
-	0,
 	36000,
 	110400,
 	244800,
@@ -63,12 +76,11 @@
 {
 	int num_rows = sizeof(bus_table)/(sizeof(u32));
 	int i;
-	if (!load)
-		return 0;
 	for (i = 0; i < num_rows; i++) {
 		if (load <= bus_table[i])
 			break;
 	}
+	i++;
 	dprintk(VIDC_DBG, "Required bus = %d\n", i);
 	return i;
 }
@@ -108,34 +120,82 @@
 			break;
 		ret = table[i].freq;
 	}
-	dprintk(VIDC_INFO, "Required clock rate = %lu\n", ret);
+	dprintk(VIDC_DBG, "Required clock rate = %lu\n", ret);
 	return ret;
 }
 
-int msm_comm_scale_bus(struct msm_vidc_core *core, enum session_type type)
+static int msm_comm_scale_bus(struct msm_vidc_core *core,
+	enum session_type type, enum mem_type mtype)
 {
 	int load;
 	int rc = 0;
+	u32 handle = 0;
 	if (!core || type >= MSM_VIDC_MAX_DEVICES) {
 		dprintk(VIDC_ERR, "Invalid args: %p, %d\n", core, type);
 		return -EINVAL;
 	}
 	load = msm_comm_get_load(core, type);
-	rc = msm_bus_scale_client_update_request(
-			core->resources.bus_info.ddr_handle[type],
-			get_bus_vector(load));
-	if (rc) {
-		dprintk(VIDC_ERR, "Failed to scale bus: %d\n", rc);
-		goto fail_scale_bus;
+	if (mtype & DDR_MEM)
+		handle = core->resources.bus_info.ddr_handle[type];
+	if (mtype & OCMEM_MEM)
+		handle = core->resources.bus_info.ocmem_handle[type];
+	if (handle) {
+		rc = msm_bus_scale_client_update_request(
+				handle, get_bus_vector(load));
+		if (rc)
+			dprintk(VIDC_ERR, "Failed to scale bus: %d\n", rc);
+	} else {
+		dprintk(VIDC_ERR, "Failed to scale bus, mtype: %d\n",
+				mtype);
+		rc = -EINVAL;
 	}
-	rc = msm_bus_scale_client_update_request(
-			core->resources.bus_info.ocmem_handle[type],
-			get_bus_vector(load));
-	if (rc) {
-		dprintk(VIDC_ERR, "Failed to scale bus: %d\n", rc);
-		goto fail_scale_bus;
+	return rc;
+}
+
+static void msm_comm_unvote_buses(struct msm_vidc_core *core,
+	enum mem_type mtype)
+{
+	int i;
+	for (i = 0; i < MSM_VIDC_MAX_DEVICES; i++) {
+		if ((mtype & DDR_MEM) &&
+			msm_bus_scale_client_update_request(
+				core->resources.bus_info.ddr_handle[i],
+				0)) {
+			dprintk(VIDC_WARN,
+				"Failed to unvote for DDR accesses\n");
+		}
+		if ((mtype & OCMEM_MEM) &&
+			msm_bus_scale_client_update_request(
+				core->resources.bus_info.ocmem_handle[i],
+				0)) {
+			dprintk(VIDC_WARN,
+				"Failed to unvote for OCMEM accesses\n");
+		}
 	}
-fail_scale_bus:
+}
+
+static int protect_cp_mem(struct msm_vidc_core *core)
+{
+	struct tzbsp_memprot memprot;
+	unsigned int resp = 0;
+	int rc = 0;
+	struct msm_vidc_iommu_info *io_map = core->resources.io_map;
+	if (!io_map) {
+		dprintk(VIDC_ERR, "invalid params: %p\n", io_map);
+		return -EINVAL;
+	}
+	memprot.cp_start = 0x0;
+	memprot.cp_size = io_map[CP_MAP].addr_range[0] +
+			io_map[CP_MAP].addr_range[1];
+	memprot.cp_nonpixel_start = 0;
+	memprot.cp_nonpixel_size = 0;
+
+	rc = scm_call(SCM_SVC_CP, TZBSP_MEM_PROTECT_VIDEO_VAR, &memprot,
+			sizeof(memprot), &resp, sizeof(resp));
+	if (rc)
+		dprintk(VIDC_ERR,
+		"Failed to protect memory , rc is :%d, response : %d\n",
+		rc, resp);
 	return rc;
 }
 
@@ -294,6 +354,48 @@
 	}
 }
 
+static void handle_session_release_buf_done(enum command_response cmd,
+	void *data)
+{
+	struct msm_vidc_cb_cmd_done *response = data;
+	struct msm_vidc_inst *inst;
+	struct internal_buf *buf;
+	struct list_head *ptr, *next;
+	struct hal_buffer_info *buffer;
+	u32 address, buf_found = false;
+
+	if (!response || !response->data) {
+		dprintk(VIDC_ERR, "Invalid release_buf_done response\n");
+		return;
+	}
+
+	inst = (struct msm_vidc_inst *)response->session_id;
+	buffer = (struct hal_buffer_info *) response->data;
+	address = (u32) buffer->buffer_addr;
+
+	list_for_each_safe(ptr, next, &inst->internalbufs) {
+		buf = list_entry(ptr, struct internal_buf, list);
+		if (address == buf->handle->device_addr) {
+			dprintk(VIDC_DBG, "releasing scratch: 0x%x",
+					(u32) buf->handle->device_addr);
+					buf_found = true;
+		}
+	}
+
+	list_for_each_safe(ptr, next, &inst->persistbufs) {
+		buf = list_entry(ptr, struct internal_buf, list);
+		if (address == (u32) buf->handle->device_addr) {
+			dprintk(VIDC_DBG, "releasing persist: 0x%x",
+					(u32) buf->handle->device_addr);
+			buf_found = true;
+		}
+	}
+
+	if (!buf_found)
+		dprintk(VIDC_ERR, "invalid buffer received from firmware");
+	complete(&inst->completions[SESSION_MSG_INDEX(cmd)]);
+}
+
 static void handle_sys_release_res_done(
 	enum command_response cmd, void *data)
 {
@@ -312,7 +414,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 +489,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 +499,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 +621,72 @@
 	}
 }
 
+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) {
+		core = get_vidc_core(response->device_id);
+		dprintk(VIDC_WARN, "SYS_ERROR received for core %p\n", 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) {
+				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,
+				"Got SYS_ERR but unable to identify core");
+		}
+	} 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)
+{
+	dprintk(VIDC_ERR,
+		"msm_vidc: Sub System Restart initiated\n");
+}
+
 
 static void handle_session_close(enum command_response cmd, void *data)
 {
@@ -520,6 +699,7 @@
 		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 {
@@ -592,7 +772,6 @@
 		(u32)fill_buf_done->packet_buffer1);
 	if (vb) {
 		vb->v4l2_planes[0].bytesused = fill_buf_done->filled_len1;
-
 		if (!(fill_buf_done->flags1 &
 			HAL_BUFFERFLAG_TIMESTAMPINVALID)) {
 			int64_t time_usec = fill_buf_done->timestamp_hi;
@@ -630,6 +809,10 @@
 		default:
 			break;
 		}
+		inst->count.fbd++;
+		if (fill_buf_done->filled_len1)
+			msm_vidc_debugfs_update(inst,
+				MSM_VIDC_DEBUGFS_EVENT_FBD);
 
 		dprintk(VIDC_DBG, "Filled length = %d; flags %x\n",
 				vb->v4l2_planes[0].bytesused,
@@ -638,7 +821,6 @@
 		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:
@@ -677,7 +859,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;
@@ -743,13 +925,25 @@
 	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;
+	case SESSION_RELEASE_BUFFER_DONE:
+		handle_session_release_buf_done(cmd, data);
+		break;
 	default:
 		dprintk(VIDC_ERR, "response unhandled\n");
 		break;
 	}
 }
 
-int msm_comm_scale_clocks(struct msm_vidc_core *core, enum session_type type)
+static int msm_comm_scale_clocks(struct msm_vidc_core *core)
 {
 	int num_mbs_per_sec;
 	int rc = 0;
@@ -763,14 +957,8 @@
 	rc = clk_set_rate(core->resources.clock[VCODEC_CLK].clk,
 			get_clock_rate(&core->resources.clock[VCODEC_CLK],
 				num_mbs_per_sec));
-	if (rc) {
-		dprintk(VIDC_ERR, "Failed to set clock rate: %d\n", rc);
-		goto fail_clk_set_rate;
-	}
-	rc = msm_comm_scale_bus(core, type);
 	if (rc)
-		dprintk(VIDC_ERR, "Failed to scale bus bandwidth\n");
-fail_clk_set_rate:
+		dprintk(VIDC_ERR, "Failed to set clock rate: %d\n", rc);
 	return rc;
 }
 
@@ -816,6 +1004,28 @@
 	}
 }
 
+void msm_comm_scale_clocks_and_bus(struct msm_vidc_inst *inst)
+{
+	struct msm_vidc_core *core = inst->core;
+	if (!inst) {
+		dprintk(VIDC_WARN, "Invalid params\n");
+		return;
+	}
+	if (msm_comm_scale_clocks(core)) {
+		dprintk(VIDC_WARN,
+		"Failed to scale clocks. Performance might be impacted\n");
+	}
+	if (msm_comm_scale_bus(core, inst->session_type, DDR_MEM)) {
+		dprintk(VIDC_WARN,
+		"Failed to scale DDR bus. Performance might be impacted\n");
+	}
+	if (core->resources.ocmem.buf) {
+		if (msm_comm_scale_bus(core, inst->session_type, OCMEM_MEM))
+			dprintk(VIDC_WARN,
+			"Failed to scale OCMEM bus. Performance might be impacted\n");
+	}
+}
+
 static int msm_comm_load_fw(struct msm_vidc_core *core)
 {
 	int rc = 0;
@@ -823,22 +1033,28 @@
 		dprintk(VIDC_ERR, "Invalid paramter: %p\n", core);
 		return -EINVAL;
 	}
-
 	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_load_fw;
 	}
-
+	/*Clocks can be enabled only after pil_get since
+	 * gdsc is turned-on in pil_get*/
 	rc = msm_comm_enable_clks(core);
 	if (rc) {
 		dprintk(VIDC_ERR, "Failed to enable clocks: %d\n", rc);
 		goto fail_enable_clks;
 	}
 
+	rc = protect_cp_mem(core);
+	if (rc) {
+		dprintk(VIDC_ERR, "Failed to protect memory\n");
+		goto fail_iommu_attach;
+	}
+
 	rc = msm_comm_iommu_attach(core);
 	if (rc) {
 		dprintk(VIDC_ERR, "Failed to attach iommu");
@@ -848,23 +1064,23 @@
 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_load_fw:
 	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);
-		core->resources.fw.cookie = NULL;
 		msm_comm_iommu_detach(core);
 		msm_comm_disable_clks(core);
+		subsystem_put(core->resources.fw.cookie);
+		core->resources.fw.cookie = NULL;
 	}
 }
 
@@ -901,7 +1117,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;
@@ -967,7 +1183,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) {
@@ -1053,16 +1269,24 @@
 				core->id, core->state);
 		goto core_already_inited;
 	}
-	rc = msm_comm_scale_clocks(core, inst->session_type);
+
+	rc = msm_comm_scale_bus(core, inst->session_type, DDR_MEM);
 	if (rc) {
-		dprintk(VIDC_ERR, "Failed to set clock rate: %d\n", rc);
-		goto fail_load_fw;
+		dprintk(VIDC_ERR, "Failed to scale DDR bus: %d\n", rc);
+		goto fail_scale_bus;
 	}
+
 	rc = msm_comm_load_fw(core);
 	if (rc) {
 		dprintk(VIDC_ERR, "Failed to load video firmware\n");
 		goto fail_load_fw;
 	}
+	rc = msm_comm_scale_clocks(core);
+	if (rc) {
+		dprintk(VIDC_ERR, "Failed to scale clocks: %d\n", rc);
+		goto fail_core_init;
+	}
+
 	init_completion(&core->completions[SYS_MSG_INDEX(SYS_INIT_DONE)]);
 	rc = vidc_hal_core_init(core->device,
 		core->resources.io_map[NS_MAP].domain);
@@ -1080,6 +1304,8 @@
 fail_core_init:
 	msm_comm_unload_fw(core);
 fail_load_fw:
+	msm_comm_unvote_buses(core, DDR_MEM);
+fail_scale_bus:
 	mutex_unlock(&core->sync_lock);
 	return rc;
 }
@@ -1095,10 +1321,7 @@
 				core->id, core->state);
 		goto core_already_uninited;
 	}
-	if (msm_comm_scale_clocks(core, inst->session_type)) {
-		dprintk(VIDC_WARN, "Failed to scale clocks while closing\n");
-		dprintk(VIDC_INFO, "Power might be impacted\n");
-	}
+	msm_comm_scale_clocks_and_bus(inst);
 	if (list_empty(&core->instances)) {
 		msm_comm_unset_ocmem(core);
 		msm_comm_free_ocmem(core);
@@ -1113,6 +1336,7 @@
 		core->state = VIDC_CORE_UNINIT;
 		spin_unlock_irqrestore(&core->lock, flags);
 		msm_comm_unload_fw(core);
+		msm_comm_unvote_buses(core, DDR_MEM|OCMEM_MEM);
 	}
 core_already_uninited:
 	change_inst_state(inst, MSM_VIDC_CORE_UNINIT);
@@ -1231,11 +1455,18 @@
 		goto exit;
 	}
 	ocmem_sz = get_ocmem_requirement(inst->prop.height, inst->prop.width);
-	rc = msm_comm_alloc_ocmem(inst->core, ocmem_sz);
-	if (rc)
-		dprintk(VIDC_WARN,
+	rc = msm_comm_scale_bus(inst->core, inst->session_type, OCMEM_MEM);
+	if (!rc) {
+		rc = msm_comm_alloc_ocmem(inst->core, ocmem_sz);
+		if (rc) {
+			dprintk(VIDC_WARN,
 			"Failed to allocate OCMEM. Performance will be impacted\n");
-
+			msm_comm_unvote_buses(inst->core, OCMEM_MEM);
+		}
+	} else {
+		dprintk(VIDC_WARN,
+		"Failed to vote for OCMEM BW. Performance will be impacted\n");
+	}
 	rc = vidc_hal_session_load_res((void *) inst->session);
 	if (rc) {
 		dprintk(VIDC_ERR,
@@ -1339,113 +1570,134 @@
 	return rc;
 }
 
-int msm_comm_try_state(struct msm_vidc_inst *inst, int state)
+static int get_flipped_state(int present_state,
+	int desired_state)
 {
-	int rc = 0;
-	int flipped_state;
-	if (!inst) {
-		dprintk(VIDC_ERR,
-			"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);
-	mutex_lock(&inst->sync_lock);
-	flipped_state = inst->state;
+	int flipped_state = present_state;
 	if (flipped_state < MSM_VIDC_STOP
-		&& state > MSM_VIDC_STOP) {
+			&& desired_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) {
+			&& desired_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;
 	}
+	return flipped_state;
+}
+
+int msm_comm_try_state(struct msm_vidc_inst *inst, int state)
+{
+	int rc = 0;
+	int flipped_state;
+	struct msm_vidc_core *core;
+	if (!inst) {
+		dprintk(VIDC_ERR,
+				"Invalid instance pointer = %p\n", inst);
+		return -EINVAL;
+	}
 	dprintk(VIDC_DBG,
-		"flipped_state = 0x%x\n", flipped_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 = get_flipped_state(inst->state, state);
+	dprintk(VIDC_DBG,
+			"flipped_state = 0x%x\n", flipped_state);
 	switch (flipped_state) {
 	case MSM_VIDC_CORE_UNINIT_DONE:
 	case MSM_VIDC_CORE_INIT:
 		rc = msm_comm_init_core(inst);
-		if (rc || state <= inst->state)
+		if (rc || state <= get_flipped_state(inst->state, state))
 			break;
 	case MSM_VIDC_CORE_INIT_DONE:
 		rc = msm_comm_init_core_done(inst);
-		if (rc || state <= inst->state)
+		if (rc || state <= get_flipped_state(inst->state, state))
 			break;
 	case MSM_VIDC_OPEN:
 		rc = msm_comm_session_init(flipped_state, inst);
-		if (rc || state <= inst->state)
+		if (rc || state <= get_flipped_state(inst->state, state))
 			break;
 	case MSM_VIDC_OPEN_DONE:
 		rc = wait_for_state(inst, flipped_state, MSM_VIDC_OPEN_DONE,
 			SESSION_INIT_DONE);
-		if (rc || state <= inst->state)
+		if (rc || state <= get_flipped_state(inst->state, state))
 			break;
 	case MSM_VIDC_LOAD_RESOURCES:
 		rc = msm_vidc_load_resources(flipped_state, inst);
-		if (rc || state <= inst->state)
+		if (rc || state <= get_flipped_state(inst->state, state))
 			break;
 	case MSM_VIDC_LOAD_RESOURCES_DONE:
 	case MSM_VIDC_START:
 		rc = msm_vidc_start(flipped_state, inst);
-		if (rc || state <= inst->state)
+		if (rc || state <= get_flipped_state(inst->state, state))
 			break;
 	case MSM_VIDC_START_DONE:
 		rc = wait_for_state(inst, flipped_state, MSM_VIDC_START_DONE,
 				SESSION_START_DONE);
-		if (rc || state <= inst->state)
+		if (rc || state <= get_flipped_state(inst->state, state))
 			break;
 	case MSM_VIDC_STOP:
 		rc = msm_vidc_stop(flipped_state, inst);
-		if (rc || state <= inst->state)
+		if (rc || state <= get_flipped_state(inst->state, state))
 			break;
 	case MSM_VIDC_STOP_DONE:
 		rc = wait_for_state(inst, flipped_state, MSM_VIDC_STOP_DONE,
 				SESSION_STOP_DONE);
-		if (rc || state <= inst->state)
+		if (rc || state <= get_flipped_state(inst->state, state))
 			break;
 		dprintk(VIDC_DBG, "Moving to Stop Done state\n");
 	case MSM_VIDC_RELEASE_RESOURCES:
 		rc = msm_vidc_release_res(flipped_state, inst);
-		if (rc || state <= inst->state)
+		if (rc || state <= get_flipped_state(inst->state, state))
 			break;
 	case MSM_VIDC_RELEASE_RESOURCES_DONE:
 		rc = wait_for_state(inst, flipped_state,
 			MSM_VIDC_RELEASE_RESOURCES_DONE,
 			SESSION_RELEASE_RESOURCE_DONE);
-		if (rc || state <= inst->state)
+		if (rc || state <= get_flipped_state(inst->state, 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)
+		if (rc || state <= get_flipped_state(inst->state, state))
 			break;
 	case MSM_VIDC_CLOSE_DONE:
 		rc = wait_for_state(inst, flipped_state, MSM_VIDC_CLOSE_DONE,
-			SESSION_END_DONE);
-		if (rc || state <= inst->state)
+				SESSION_END_DONE);
+		if (rc || state <= get_flipped_state(inst->state, state))
 			break;
 	case MSM_VIDC_CORE_UNINIT:
 		dprintk(VIDC_DBG, "Sending core uninit\n");
 		rc = msm_vidc_deinit_core(inst);
-		if (rc || state == inst->state)
+		if (rc || state == get_flipped_state(inst->state, state))
 			break;
 	default:
 		dprintk(VIDC_ERR, "State not recognized\n");
 		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;
 }
 
@@ -1456,13 +1708,25 @@
 	struct msm_vidc_inst *inst;
 	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) {
@@ -1508,14 +1772,17 @@
 			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.offset = 0;
+			frame_data.alloc_len = vb->v4l2_planes[0].length;
 			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);
@@ -1559,6 +1826,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);
@@ -1580,7 +1853,6 @@
 	mutex_unlock(&inst->sync_lock);
 	return rc;
 }
-
 int msm_comm_release_scratch_buffers(struct msm_vidc_inst *inst)
 {
 	struct msm_smem *handle;
@@ -1589,6 +1861,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) {
@@ -1599,13 +1883,25 @@
 			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) {
+				buffer_info.response_required = true;
+				init_completion(
+				   &inst->completions[SESSION_MSG_INDEX
+				   (SESSION_RELEASE_BUFFER_DONE)]);
+				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);
+				spin_unlock_irqrestore(&inst->lock, flags);
+				rc = wait_for_sess_signal_receipt(inst,
+					SESSION_RELEASE_BUFFER_DONE);
+				spin_lock_irqsave(&inst->lock, flags);
+			}
 			list_del(&buf->list);
 			spin_unlock_irqrestore(&inst->lock, flags);
 			msm_smem_free(inst->mem_client, buf->handle);
@@ -1625,23 +1921,47 @@
 	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) {
+				buffer_info.response_required = true;
+				init_completion(
+				   &inst->completions[SESSION_MSG_INDEX
+				   (SESSION_RELEASE_BUFFER_DONE)]);
+				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);
+				spin_unlock_irqrestore(&inst->lock, flags);
+				rc = wait_for_sess_signal_receipt(inst,
+					SESSION_RELEASE_BUFFER_DONE);
+				spin_lock_irqsave(&inst->lock, flags);
+			}
 			list_del(&buf->list);
 			spin_unlock_irqrestore(&inst->lock, flags);
 			msm_smem_free(inst->mem_client, buf->handle);
@@ -1653,6 +1973,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;
@@ -1660,6 +2003,8 @@
 	struct internal_buf *binfo;
 	struct vidc_buffer_addr_info buffer_info;
 	unsigned long flags;
+	int domain;
+	unsigned long smem_flags = 0;
 	struct hal_buffer_requirements *scratch_buf =
 		&inst->buff_req.buffer[HAL_BUFFER_INTERNAL_SCRATCH];
 	int i;
@@ -1669,14 +2014,18 @@
 		scratch_buf->buffer_size);
 	if (msm_comm_release_scratch_buffers(inst))
 		dprintk(VIDC_WARN, "Failed to release scratch buffers\n");
+	if (inst->mode == VIDC_SECURE) {
+		domain = inst->core->resources.io_map[CP_MAP].domain;
+		smem_flags |= SMEM_SECURE;
+	} else
+		domain = inst->core->resources.io_map[NS_MAP].domain;
 
 	if (scratch_buf->buffer_size) {
 		for (i = 0; i < scratch_buf->buffer_count_actual;
 				i++) {
 			handle = msm_smem_alloc(inst->mem_client,
-				scratch_buf->buffer_size, 1, SMEM_UNCACHED,
-				inst->core->resources.io_map[NS_MAP].domain,
-				0, 0);
+				scratch_buf->buffer_size, 1, smem_flags,
+				domain, 0, 0);
 			if (!handle) {
 				dprintk(VIDC_ERR,
 					"Failed to allocate scratch memory\n");
@@ -1694,6 +2043,8 @@
 			buffer_info.buffer_type = HAL_BUFFER_INTERNAL_SCRATCH;
 			buffer_info.num_buffers = 1;
 			buffer_info.align_device_addr = handle->device_addr;
+			dprintk(VIDC_DBG, "Scratch buffer address: %x",
+					buffer_info.align_device_addr);
 			rc = vidc_hal_session_set_buffers(
 					(void *) inst->session,	&buffer_info);
 			if (rc) {
@@ -1722,6 +2073,8 @@
 	struct internal_buf *binfo;
 	struct vidc_buffer_addr_info buffer_info;
 	unsigned long flags;
+	unsigned long smem_flags = 0;
+	int domain;
 	struct hal_buffer_requirements *persist_buf =
 		&inst->buff_req.buffer[HAL_BUFFER_INTERNAL_PERSIST];
 	int i;
@@ -1735,12 +2088,17 @@
 		return rc;
 	}
 
+	if (inst->mode == VIDC_SECURE) {
+		domain = inst->core->resources.io_map[CP_MAP].domain;
+		flags |= SMEM_SECURE;
+	} else
+		domain = inst->core->resources.io_map[NS_MAP].domain;
+
 	if (persist_buf->buffer_size) {
 		for (i = 0;	i <	persist_buf->buffer_count_actual; i++) {
 			handle = msm_smem_alloc(inst->mem_client,
-				persist_buf->buffer_size, 1, SMEM_UNCACHED,
-				inst->core->resources.io_map[NS_MAP].domain,
-				0, 0);
+				persist_buf->buffer_size, 1, smem_flags,
+				domain, 0, 0);
 			if (!handle) {
 				dprintk(VIDC_ERR,
 					"Failed to allocate persist memory\n");
@@ -1758,6 +2116,8 @@
 			buffer_info.buffer_type = HAL_BUFFER_INTERNAL_PERSIST;
 			buffer_info.num_buffers = 1;
 			buffer_info.align_device_addr = handle->device_addr;
+			dprintk(VIDC_DBG, "Persist buffer address: %x",
+					buffer_info.align_device_addr);
 			rc = vidc_hal_session_set_buffers(
 				(void *) inst->session, &buffer_info);
 			if (rc) {
@@ -1779,6 +2139,50 @@
 	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;
@@ -1787,12 +2191,34 @@
 	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;
+
 	if (ip_flush && !op_flush) {
 		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)) {
@@ -1830,3 +2256,61 @@
 	mutex_unlock(&inst->sync_lock);
 	return rc;
 }
+
+
+enum hal_extradata_id msm_comm_get_hal_extradata_index(
+	enum v4l2_mpeg_vidc_extradata index)
+{
+	int ret = 0;
+	switch (index) {
+	case V4L2_MPEG_VIDC_EXTRADATA_NONE:
+		ret = HAL_EXTRADATA_NONE;
+		break;
+	case V4L2_MPEG_VIDC_EXTRADATA_MB_QUANTIZATION:
+		ret = HAL_EXTRADATA_MB_QUANTIZATION;
+		break;
+	case V4L2_MPEG_VIDC_EXTRADATA_INTERLACE_VIDEO:
+		ret = HAL_EXTRADATA_INTERLACE_VIDEO;
+		break;
+	case V4L2_MPEG_VIDC_EXTRADATA_VC1_FRAMEDISP:
+		ret = HAL_EXTRADATA_VC1_FRAMEDISP;
+		break;
+	case V4L2_MPEG_VIDC_EXTRADATA_VC1_SEQDISP:
+		ret = HAL_EXTRADATA_VC1_SEQDISP;
+		break;
+	case V4L2_MPEG_VIDC_EXTRADATA_TIMESTAMP:
+		ret = HAL_EXTRADATA_TIMESTAMP;
+		break;
+	case V4L2_MPEG_VIDC_EXTRADATA_S3D_FRAME_PACKING:
+		ret = HAL_EXTRADATA_S3D_FRAME_PACKING;
+		break;
+	case V4L2_MPEG_VIDC_EXTRADATA_FRAME_RATE:
+		ret = HAL_EXTRADATA_FRAME_RATE;
+		break;
+	case V4L2_MPEG_VIDC_EXTRADATA_PANSCAN_WINDOW:
+		ret = HAL_EXTRADATA_PANSCAN_WINDOW;
+		break;
+	case V4L2_MPEG_VIDC_EXTRADATA_RECOVERY_POINT_SEI:
+		ret = HAL_EXTRADATA_RECOVERY_POINT_SEI;
+		break;
+	case V4L2_MPEG_VIDC_EXTRADATA_CLOSED_CAPTION_UD:
+		ret = HAL_EXTRADATA_CLOSED_CAPTION_UD;
+		break;
+	case V4L2_MPEG_VIDC_EXTRADATA_AFD_UD:
+		ret = HAL_EXTRADATA_AFD_UD;
+		break;
+	case V4L2_MPEG_VIDC_EXTRADATA_MULTISLICE_INFO:
+		ret = HAL_EXTRADATA_MULTISLICE_INFO;
+		break;
+	case V4L2_MPEG_VIDC_EXTRADATA_NUM_CONCEALED_MB:
+		ret = HAL_EXTRADATA_NUM_CONCEALED_MB;
+		break;
+	case V4L2_MPEG_VIDC_EXTRADATA_METADATA_FILLER:
+		ret = HAL_EXTRADATA_METADATA_FILLER;
+		break;
+	default:
+		dprintk(VIDC_WARN, "Extradata not found: %d\n", index);
+		break;
+	}
+	return ret;
+};
diff --git a/drivers/media/video/msm_vidc/msm_vidc_common.h b/drivers/media/video/msm_vidc/msm_vidc_common.h
index 0708724..916a3ca 100644
--- a/drivers/media/video/msm_vidc/msm_vidc_common.h
+++ b/drivers/media/video/msm_vidc/msm_vidc_common.h
@@ -27,13 +27,22 @@
 		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);
-int msm_comm_scale_clocks(struct msm_vidc_core *core, enum session_type type);
+void msm_comm_scale_clocks_and_bus(struct msm_vidc_inst *inst);
 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);
+enum hal_extradata_id msm_comm_get_hal_extradata_index(
+	enum v4l2_mpeg_vidc_extradata index);
 #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 914c422..f91d0dd 100644
--- a/drivers/media/video/msm_vidc/msm_vidc_debug.c
+++ b/drivers/media/video/msm_vidc/msm_vidc_debug.c
@@ -202,28 +202,27 @@
 	switch (e) {
 	case MSM_VIDC_DEBUGFS_EVENT_ETB:
 		inst->count.etb++;
-		if (inst->count.ftb > inst->count.fbd) {
+		if (inst->count.ebd && 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)
+		if (inst->count.ebd && 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) {
+		if (inst->count.ebd && 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)
+		inst->debug.samples++;
+		if (inst->count.ebd && inst->count.fbd == inst->count.ftb)
 			toc(inst, FRAME_PROCESSING);
 		break;
 	default:
diff --git a/drivers/media/video/msm_vidc/msm_vidc_debug.h b/drivers/media/video/msm_vidc/msm_vidc_debug.h
index 1a51173..995daf0 100644
--- a/drivers/media/video/msm_vidc/msm_vidc_debug.h
+++ b/drivers/media/video/msm_vidc/msm_vidc_debug.h
@@ -81,23 +81,8 @@
 		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].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;
 	}
 }
@@ -110,9 +95,11 @@
 			(msm_vidc_debug & VIDC_PROF)) {
 			dprintk(VIDC_PROF, "%s averaged %d ms/sample\n",
 				i->debug.pdata[x].name,
-				i->debug.pdata[x].average);
+				i->debug.pdata[x].cumulative /
+					i->debug.samples);
 			dprintk(VIDC_PROF, "%s Samples: %d",
-					i->debug.pdata[x].name, i->count.fbd);
+					i->debug.pdata[x].name,
+					i->debug.samples);
 		}
 	}
 }
diff --git a/drivers/media/video/msm_vidc/msm_vidc_internal.h b/drivers/media/video/msm_vidc/msm_vidc_internal.h
index 9806d771..b274d13 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 {
@@ -155,6 +134,11 @@
 	u32 freq;
 };
 
+enum mem_type {
+	DDR_MEM = 0x1,
+	OCMEM_MEM = 0x2,
+};
+
 struct core_clock {
 	char name[MAX_NAME_LENGTH];
 	struct clk *clk;
@@ -221,7 +205,19 @@
 struct msm_vidc_debug {
 	struct profile_data pdata[MAX_PROFILING_POINTS];
 	int profile;
-	int counter;
+	int samples;
+};
+
+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;
+};
+
+enum msm_vidc_mode {
+	VIDC_NON_SECURE,
+	VIDC_SECURE,
 };
 
 struct msm_vidc_core {
@@ -241,6 +237,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 {
@@ -273,6 +270,7 @@
 	void *priv;
 	struct msm_vidc_debug debug;
 	struct buf_count count;
+	enum msm_vidc_mode mode;
 };
 
 extern struct msm_vidc_drv *vidc_driver;
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 89f0273..207bfe4 100644
--- a/drivers/media/video/msm_vidc/vidc_hal.c
+++ b/drivers/media/video/msm_vidc/vidc_hal.c
@@ -501,18 +501,27 @@
 static void vidc_hal_interface_queues_release(struct hal_device *device)
 {
 	int i;
+
+	vidc_hal_free(device->hal_client, device->mem_addr.mem_data);
+
 	for (i = 0; i < VIDC_IFACEQ_NUMQ; i++) {
-		vidc_hal_free(device->hal_client,
-			device->iface_queues[i].q_array.mem_data);
 		device->iface_queues[i].q_hdr = NULL;
 		device->iface_queues[i].q_array.mem_data = NULL;
 		device->iface_queues[i].q_array.align_virtual_addr = NULL;
 		device->iface_queues[i].q_array.align_device_addr = NULL;
 	}
-	vidc_hal_free(device->hal_client,
-				device->iface_q_table.mem_data);
 	device->iface_q_table.align_virtual_addr = NULL;
 	device->iface_q_table.align_device_addr = NULL;
+
+	device->qdss.align_virtual_addr = NULL;
+	device->qdss.align_device_addr = NULL;
+
+	device->sfr.align_virtual_addr = NULL;
+	device->sfr.align_device_addr = NULL;
+
+	device->mem_addr.align_virtual_addr = NULL;
+	device->mem_addr.align_device_addr = NULL;
+
 	msm_smem_delete_client(device->hal_client);
 	device->hal_client = NULL;
 }
@@ -524,14 +533,51 @@
 	u8 i;
 	int rc = 0;
 	struct vidc_iface_q_info *iface_q;
-
-	rc = vidc_hal_alloc((void *) &dev->iface_q_table,
-					dev->hal_client,
-			VIDC_IFACEQ_TABLE_SIZE, 1, SMEM_UNCACHED, domain);
+	struct hfi_sfr_struct *vsfr;
+	struct vidc_mem_addr *mem_addr;
+	int offset = 0;
+	int size_1m = 1024 * 1024;
+	int uc_size = (UC_SIZE + size_1m - 1) & (~(size_1m - 1));
+	mem_addr = &dev->mem_addr;
+	rc = vidc_hal_alloc((void *) mem_addr,
+			dev->hal_client, uc_size, 1,
+			0, domain);
 	if (rc) {
 		dprintk(VIDC_ERR, "iface_q_table_alloc_fail");
 		return -ENOMEM;
 	}
+	dev->iface_q_table.align_virtual_addr = mem_addr->align_virtual_addr;
+	dev->iface_q_table.align_device_addr = mem_addr->align_device_addr;
+	dev->iface_q_table.mem_size = VIDC_IFACEQ_TABLE_SIZE;
+	dev->iface_q_table.mem_data = NULL;
+	offset += dev->iface_q_table.mem_size;
+
+	for (i = 0; i < VIDC_IFACEQ_NUMQ; i++) {
+		iface_q = &dev->iface_queues[i];
+		iface_q->q_array.align_device_addr =
+			mem_addr->align_device_addr + offset;
+		iface_q->q_array.align_virtual_addr =
+			mem_addr->align_virtual_addr + offset;
+		iface_q->q_array.mem_size = VIDC_IFACEQ_QUEUE_SIZE;
+		iface_q->q_array.mem_data = NULL;
+		offset += iface_q->q_array.mem_size;
+		iface_q->q_hdr = VIDC_IFACEQ_GET_QHDR_START_ADDR(
+				dev->iface_q_table.align_virtual_addr, i);
+		vidc_hal_set_queue_hdr_defaults(iface_q->q_hdr);
+	}
+
+	dev->qdss.align_device_addr = mem_addr->align_device_addr + offset;
+	dev->qdss.align_virtual_addr = mem_addr->align_virtual_addr + offset;
+	dev->qdss.mem_size = QDSS_SIZE;
+	dev->qdss.mem_data = NULL;
+	offset += dev->qdss.mem_size;
+
+	dev->sfr.align_device_addr = mem_addr->align_device_addr + offset;
+	dev->sfr.align_virtual_addr = mem_addr->align_virtual_addr + offset;
+	dev->sfr.mem_size = SFR_SIZE;
+	dev->sfr.mem_data = NULL;
+	offset += dev->sfr.mem_size;
+
 	q_tbl_hdr = (struct hfi_queue_table_header *)
 			dev->iface_q_table.align_virtual_addr;
 	q_tbl_hdr->qtbl_version = 0;
@@ -543,23 +589,6 @@
 	q_tbl_hdr->qtbl_num_q = VIDC_IFACEQ_NUMQ;
 	q_tbl_hdr->qtbl_num_active_q = VIDC_IFACEQ_NUMQ;
 
-	for (i = 0; i < VIDC_IFACEQ_NUMQ; i++) {
-		iface_q = &dev->iface_queues[i];
-		rc = vidc_hal_alloc((void *) &iface_q->q_array,
-				dev->hal_client, VIDC_IFACEQ_QUEUE_SIZE,
-				1, SMEM_UNCACHED, domain);
-		if (rc) {
-			dprintk(VIDC_ERR, "iface_q_table_alloc[%d]_fail", i);
-			vidc_hal_interface_queues_release(dev);
-			return -ENOMEM;
-		} else {
-			iface_q->q_hdr =
-				VIDC_IFACEQ_GET_QHDR_START_ADDR(
-			dev->iface_q_table.align_virtual_addr, i);
-			vidc_hal_set_queue_hdr_defaults(iface_q->q_hdr);
-		}
-	}
-
 	iface_q = &dev->iface_queues[VIDC_IFACEQ_CMDQ_IDX];
 	q_hdr = iface_q->q_hdr;
 	q_hdr->qhdr_start_addr = (u32)
@@ -577,6 +606,12 @@
 	q_hdr->qhdr_start_addr = (u32)
 		iface_q->q_array.align_device_addr;
 	q_hdr->qhdr_type |= HFI_Q_ID_CTRL_TO_HOST_DEBUG_Q;
+
+	write_register(dev->hal_data->register_base_addr,
+			VIDC_UC_REGION_ADDR,
+			(u32) mem_addr->align_device_addr, 0);
+	write_register(dev->hal_data->register_base_addr,
+			VIDC_UC_REGION_SIZE, mem_addr->mem_size, 0);
 	write_register(dev->hal_data->register_base_addr,
 		VIDC_CPU_CS_SCIACMDARG2,
 		(u32) dev->iface_q_table.align_device_addr,
@@ -584,6 +619,15 @@
 	write_register(dev->hal_data->register_base_addr,
 		VIDC_CPU_CS_SCIACMDARG1, 0x01,
 		dev->iface_q_table.align_virtual_addr);
+	write_register(dev->hal_data->register_base_addr,
+			VIDC_MMAP_ADDR,
+			(u32) dev->qdss.align_device_addr, 0);
+
+	vsfr = (struct hfi_sfr_struct *) dev->sfr.align_virtual_addr;
+	vsfr->bufSize = SFR_SIZE;
+
+	write_register(dev->hal_data->register_base_addr,
+			VIDC_SFR_ADDR, (u32)dev->sfr.align_device_addr , 0);
 	return 0;
 }
 
@@ -595,10 +639,15 @@
 			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) {
 		ctrl_status = read_register(
 		device->hal_data->register_base_addr,
 		VIDC_CPU_CS_SCIACMDARG0);
+		if ((ctrl_status & 0xFE) == 0x4) {
+			dprintk(VIDC_ERR, "invalid setting for UC_REGION\n");
+			break;
+		}
 		usleep_range(500, 1000);
 		count++;
 	}
@@ -613,11 +662,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 +688,15 @@
 	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)
@@ -679,6 +734,7 @@
 	spin_lock_init(&dev->read_lock);
 	spin_lock_init(&dev->write_lock);
 	set_vbif_registers(dev);
+
 	if (!dev->hal_client) {
 		dev->hal_client = msm_smem_new_client(SMEM_ION);
 		if (dev->hal_client == NULL) {
@@ -687,8 +743,7 @@
 			goto err_no_mem;
 		}
 
-		dprintk(VIDC_DBG, "Device_Virt_Address : 0x%x,"
-		"Register_Virt_Addr: 0x%x",
+		dprintk(VIDC_DBG, "Dev_Virt: 0x%x, Reg_Virt: 0x%x",
 		dev->hal_data->device_base_addr,
 		(u32) dev->hal_data->register_base_addr);
 
@@ -703,12 +758,15 @@
 		rc = -EEXIST;
 		goto err_no_mem;
 	}
+	write_register(dev->hal_data->register_base_addr,
+		VIDC_CTRL_INIT, 0x1, 0);
 	rc = vidc_hal_core_start_cpu(dev);
 	if (rc) {
 		dprintk(VIDC_ERR, "Failed to start core");
 		rc = -ENODEV;
 		goto err_no_dev;
 	}
+
 	pkt.size = sizeof(struct hfi_cmd_sys_init_packet);
 	pkt.packet_type = HFI_CMD_SYS_INIT;
 	pkt.arch_type = HFI_ARCH_OX_OFFSET;
@@ -914,6 +972,61 @@
 	}
 	return buffer;
 }
+
+
+static int get_hfi_extradata_index(enum hal_extradata_id index)
+{
+	int ret = 0;
+	switch (index) {
+	case HAL_EXTRADATA_MB_QUANTIZATION:
+		ret = HFI_PROPERTY_PARAM_VDEC_MB_QUANTIZATION;
+		break;
+	case HAL_EXTRADATA_INTERLACE_VIDEO:
+		ret = HFI_PROPERTY_PARAM_VDEC_INTERLACE_VIDEO_EXTRADATA;
+		break;
+	case HAL_EXTRADATA_VC1_FRAMEDISP:
+		ret = HFI_PROPERTY_PARAM_VDEC_VC1_FRAMEDISP_EXTRADATA;
+		break;
+	case HAL_EXTRADATA_VC1_SEQDISP:
+		ret = HFI_PROPERTY_PARAM_VDEC_VC1_SEQDISP_EXTRADATA;
+		break;
+	case HAL_EXTRADATA_TIMESTAMP:
+		ret = HFI_PROPERTY_PARAM_VDEC_TIMESTAMP_EXTRADATA;
+		break;
+	case HAL_EXTRADATA_S3D_FRAME_PACKING:
+		ret = HFI_PROPERTY_PARAM_S3D_FRAME_PACKING_EXTRADATA;
+		break;
+	case HAL_EXTRADATA_FRAME_RATE:
+		ret = HFI_PROPERTY_PARAM_VDEC_FRAME_RATE_EXTRADATA;
+		break;
+	case HAL_EXTRADATA_PANSCAN_WINDOW:
+		ret = HFI_PROPERTY_PARAM_VDEC_PANSCAN_WNDW_EXTRADATA;
+		break;
+	case HAL_EXTRADATA_RECOVERY_POINT_SEI:
+		ret = HFI_PROPERTY_PARAM_VDEC_RECOVERY_POINT_SEI_EXTRADATA;
+		break;
+	case HAL_EXTRADATA_CLOSED_CAPTION_UD:
+		ret = HFI_PROPERTY_PARAM_VDEC_CLOSED_CAPTION_EXTRADATA;
+		break;
+	case HAL_EXTRADATA_AFD_UD:
+		ret = HFI_PROPERTY_PARAM_VDEC_AFD_EXTRADATA;
+		break;
+	case HAL_EXTRADATA_MULTISLICE_INFO:
+		ret = HFI_PROPERTY_PARAM_VENC_MULTI_SLICE_INFO;
+		break;
+	case HAL_EXTRADATA_NUM_CONCEALED_MB:
+		ret = HFI_PROPERTY_PARAM_VDEC_NUM_CONCEALED_MB;
+		break;
+	case HAL_EXTRADATA_INDEX:
+		ret = HFI_PROPERTY_PARAM_EXTRA_DATA_HEADER_CONFIG;
+		break;
+	default:
+		dprintk(VIDC_WARN, "Extradata index not found: %d\n", index);
+		break;
+	}
+	return ret;
+}
+
 int vidc_hal_session_set_property(void *sess,
 	enum hal_property ptype, void *pdata)
 {
@@ -921,6 +1034,7 @@
 	struct hfi_cmd_session_set_property_packet *pkt =
 		(struct hfi_cmd_session_set_property_packet *) &packet;
 	struct hal_session *session;
+	int rc = 0;
 
 	if (!sess || !pdata) {
 		dprintk(VIDC_ERR, "Invalid Params");
@@ -1188,11 +1302,21 @@
 		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;
 		pkt->rg_property_data[0] =
-			HFI_PROPERTY_PARAM_VENC_SYNC_FRAME_SEQUENCE_HEADER;
+			HFI_PROPERTY_CONFIG_VENC_SYNC_FRAME_SEQUENCE_HEADER;
 		hfi = (struct hfi_enable *) &pkt->rg_property_data[1];
 		hfi->enable = ((struct hfi_enable *) pdata)->enable;
 		pkt->size += sizeof(u32) * 2;
@@ -1460,6 +1584,30 @@
 					hfi_multi_slice_control);
 		break;
 	}
+	case HAL_PARAM_INDEX_EXTRADATA:
+	{
+		struct hfi_index_extradata_config *hfi;
+		struct hal_extradata_enable *extra = pdata;
+		int index = 0;
+		pkt->rg_property_data[0] =
+			get_hfi_extradata_index(extra->index);
+		hfi =
+			(struct hfi_index_extradata_config *)
+			&pkt->rg_property_data[1];
+		hfi->enable = extra->enable;
+		index = get_hfi_extradata_index(extra->index);
+		if (index)
+			hfi->index_extra_data_id = index;
+		else {
+			dprintk(VIDC_WARN,
+				"Failed to find extradata index: %d\n",
+				index);
+			rc = -EINVAL;
+		}
+		pkt->size += sizeof(u32) +
+			sizeof(struct hfi_index_extradata_config);
+		break;
+	}
 	case HAL_CONFIG_VPE_DEINTERLACE:
 		break;
 	/* FOLLOWING PROPERTIES ARE NOT IMPLEMENTED IN CORE YET */
@@ -1494,9 +1642,10 @@
 		dprintk(VIDC_INFO, "DEFAULT: Calling 0x%x", ptype);
 		break;
 	}
-	if (vidc_hal_iface_cmdq_write(session->device, pkt))
-		return -ENOTEMPTY;
-	return 0;
+	if (!rc)
+		rc = vidc_hal_iface_cmdq_write(session->device, pkt);
+
+	return rc;
 }
 
 int vidc_hal_session_get_property(void *sess,
@@ -1718,7 +1867,6 @@
 		((buffer_info->num_buffers - 1) * sizeof(u32));
 	pkt->packet_type = HFI_CMD_SESSION_SET_BUFFERS;
 	pkt->session_id = (u32) session;
-	pkt->buffer_mode = HFI_BUFFER_MODE_STATIC;
 	pkt->buffer_size = buffer_info->buffer_size;
 	pkt->min_buffer_size = buffer_info->buffer_size;
 	pkt->num_buffers = buffer_info->num_buffers;
@@ -1806,6 +1954,7 @@
 		pkt->size = sizeof(struct hfi_cmd_session_set_buffers_packet) +
 			((buffer_info->num_buffers - 1) * sizeof(u32));
 	}
+	pkt->response_req = buffer_info->response_required;
 	buffer = get_hfi_buffer(buffer_info->buffer_type);
 	if (buffer)
 		pkt->buffer_type = buffer;
@@ -1933,8 +2082,11 @@
 	pkt.packet_buffer = (u8 *) output_frame->device_addr;
 	pkt.extra_data_buffer =
 		(u8 *) output_frame->extradata_addr;
-
-	dprintk(VIDC_INFO, "### Q OUTPUT BUFFER ###");
+	pkt.alloc_len = output_frame->alloc_len;
+	pkt.filled_len = output_frame->filled_len;
+	pkt.offset = output_frame->offset;
+	dprintk(VIDC_DBG, "### Q OUTPUT BUFFER ###: %d, %d, %d\n",
+			pkt.alloc_len, pkt.filled_len, pkt.offset);
 	if (vidc_hal_iface_cmdq_write(session->device, &pkt))
 		rc = -ENOTEMPTY;
 	return rc;
@@ -2111,8 +2263,8 @@
 
 	dprintk(VIDC_INFO, " GOT INTERRUPT () ");
 	if (!device->callback) {
-		dprintk(VIDC_ERR, "No callback function	"
-					  "to process interrupt: %p\n", device);
+		dprintk(VIDC_ERR, "No interrupt callback function: %p\n",
+				device);
 		return;
 	}
 	vidc_hal_core_clear_interrupt(device);
@@ -2220,7 +2372,7 @@
 		list_for_each_entry(close, &hal_ctxt.dev_head, list) {
 			if (close->hal_data->irq == dev->hal_data->irq) {
 				hal_ctxt.dev_count--;
-				free_irq(dev->hal_data->irq, NULL);
+				free_irq(dev->hal_data->irq, close);
 				list_del(&close->list);
 				destroy_workqueue(close->vidc_workq);
 				kfree(close->hal_data);
diff --git a/drivers/media/video/msm_vidc/vidc_hal.h b/drivers/media/video/msm_vidc/vidc_hal.h
index c586172..c8a7d43 100644
--- a/drivers/media/video/msm_vidc/vidc_hal.h
+++ b/drivers/media/video/msm_vidc/vidc_hal.h
@@ -81,6 +81,12 @@
 	(void *)((((u32)ptr) + sizeof(struct hfi_queue_table_header)) + \
 		(i * sizeof(struct hfi_queue_header)))
 
+#define QDSS_SIZE 4096
+#define SFR_SIZE 4096
+
+#define UC_SIZE (VIDC_IFACEQ_TABLE_SIZE + \
+	(VIDC_IFACEQ_QUEUE_SIZE * VIDC_IFACEQ_NUMQ) + QDSS_SIZE + SFR_SIZE)
+
 enum vidc_hw_reg {
 	VIDC_HWREG_CTRL_STATUS =  0x1,
 	VIDC_HWREG_QTBL_INFO =  0x2,
@@ -113,6 +119,9 @@
 #define HFI_BUFFERFLAG_TIMESTAMPINVALID	0x00000100
 #define HFI_BUFFERFLAG_READONLY			0x00000200
 #define HFI_BUFFERFLAG_ENDOFSUBFRAME	0x00000400
+#define HFI_BUFFERFLAG_EOSEQ			0x00200000
+#define HFI_BUFFERFLAG_DISCONTINUITY	0x80000000
+#define HFI_BUFFERFLAG_TEI				0x40000000
 
 #define HFI_ERR_SESSION_EMPTY_BUFFER_DONE_OUTPUT_PENDING	\
 	(HFI_OX_BASE + 0x1001)
@@ -120,6 +129,8 @@
 	(HFI_OX_BASE + 0x1002)
 #define HFI_ERR_SESSION_SYNC_FRAME_NOT_DETECTED		\
 	(HFI_OX_BASE + 0x1003)
+#define  HFI_ERR_SESSION_START_CODE_NOT_FOUND		\
+	(HFI_OX_BASE + 0x1004)
 
 #define HFI_BUFFER_INTERNAL_SCRATCH (HFI_OX_BASE + 0x1)
 #define HFI_BUFFER_EXTRADATA_INPUT (HFI_OX_BASE + 0x2)
@@ -129,6 +140,11 @@
 #define HFI_BUFFER_MODE_STATIC (HFI_OX_BASE + 0x1)
 #define HFI_BUFFER_MODE_RING (HFI_OX_BASE + 0x2)
 
+struct hfi_buffer_alloc_mode {
+	u32 buffer_type;
+	u32 buffer_mode;
+};
+
 #define HFI_FLUSH_INPUT (HFI_OX_BASE + 0x1)
 #define HFI_FLUSH_OUTPUT (HFI_OX_BASE + 0x2)
 #define HFI_FLUSH_OUTPUT2 (HFI_OX_BASE + 0x3)
@@ -141,7 +157,11 @@
 #define HFI_EXTRADATA_VC1_SEQDISP			0x00000004
 #define HFI_EXTRADATA_TIMESTAMP				0x00000005
 #define HFI_EXTRADATA_S3D_FRAME_PACKING		0x00000006
-#define  HFI_EXTRADATA_EOSNAL_DETECTED      0x00000007
+#define HFI_EXTRADATA_FRAME_RATE			0x00000007
+#define HFI_EXTRADATA_PANSCAN_WINDOW		0x00000008
+#define HFI_EXTRADATA_RECOVERY_POINT_SEI	0x00000009
+#define HFI_EXTRADATA_CLOSED_CAPTION_UD		0x0000000A
+#define HFI_EXTRADATA_AFD_UD				0x0000000B
 #define HFI_EXTRADATA_MULTISLICE_INFO		0x7F100000
 #define HFI_EXTRADATA_NUM_CONCEALED_MB		0x7F100001
 #define HFI_EXTRADATA_INDEX					0x7F100002
@@ -151,7 +171,7 @@
 #define HFI_INDEX_EXTRADATA_DIGITAL_ZOOM	0x07000010
 #define HFI_INDEX_EXTRADATA_ASPECT_RATIO	0x7F100003
 
-struct HFI_INDEX_EXTRADATA_CONFIG_TYPE {
+struct hfi_index_extradata_config {
 	int enable;
 	u32 index_extra_data_id;
 };
@@ -188,10 +208,14 @@
 (HFI_PROPERTY_PARAM_OX_START + 0x004)
 #define HFI_PROPERTY_PARAM_EXTRA_DATA_HEADER_CONFIG		\
 	(HFI_PROPERTY_PARAM_OX_START + 0x005)
-#define  HFI_PROPERTY_PARAM_INDEX_EXTRADATA             \
+#define HFI_PROPERTY_PARAM_INDEX_EXTRADATA             \
 	(HFI_PROPERTY_PARAM_OX_START + 0x006)
 #define HFI_PROPERTY_PARAM_DIVX_FORMAT					\
 	(HFI_PROPERTY_PARAM_OX_START + 0x007)
+#define HFI_PROPERTY_PARAM_BUFFER_ALLOC_MODE			\
+	(HFI_PROPERTY_PARAM_OX_START + 0x008)
+#define HFI_PROPERTY_PARAM_S3D_FRAME_PACKING_EXTRADATA	\
+	(HFI_PROPERTY_PARAM_OX_START + 0x009)
 
 #define HFI_PROPERTY_CONFIG_OX_START					\
 	(HFI_DOMAIN_BASE_COMMON + HFI_ARCH_OX_OFFSET + 0x02000)
@@ -224,6 +248,31 @@
 	(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_PARAM_VDEC_FRAME_ASSEMBLY		\
+	(HFI_PROPERTY_PARAM_VDEC_OX_START + 0x00E)
+#define HFI_PROPERTY_PARAM_VDEC_CLOSED_CAPTION_EXTRADATA	\
+	(HFI_PROPERTY_PARAM_VDEC_OX_START + 0x00F)
+#define HFI_PROPERTY_PARAM_VDEC_AFD_EXTRADATA		\
+	(HFI_PROPERTY_PARAM_VDEC_OX_START + 0x010)
+#define HFI_PROPERTY_PARAM_VDEC_VC1_FRAMEDISP_EXTRADATA		\
+	(HFI_PROPERTY_PARAM_VDEC_OX_START + 0x011)
+#define HFI_PROPERTY_PARAM_VDEC_VC1_SEQDISP_EXTRADATA		\
+	(HFI_PROPERTY_PARAM_VDEC_OX_START + 0x012)
+#define HFI_PROPERTY_PARAM_VDEC_TIMESTAMP_EXTRADATA			\
+	(HFI_PROPERTY_PARAM_VDEC_OX_START + 0x013)
+#define HFI_PROPERTY_PARAM_VDEC_INTERLACE_VIDEO_EXTRADATA	\
+	(HFI_PROPERTY_PARAM_VDEC_OX_START + 0x014)
+#define HFI_PROPERTY_PARAM_VDEC_AVC_SESSION_SELECT \
+	(HFI_PROPERTY_PARAM_VDEC_OX_START + 0x015)
 
 #define HFI_PROPERTY_CONFIG_VDEC_OX_START				\
 	(HFI_DOMAIN_BASE_VDEC + HFI_ARCH_OX_OFFSET + 0x0000)
@@ -240,8 +289,11 @@
 	(HFI_PROPERTY_PARAM_VENC_OX_START + 0x001)
 #define  HFI_PROPERTY_PARAM_VENC_H264_IDR_S3D_FRAME_PACKING_NAL \
 	(HFI_PROPERTY_PARAM_VENC_OX_START + 0x002)
+
 #define HFI_PROPERTY_CONFIG_VENC_OX_START				\
 	(HFI_DOMAIN_BASE_VENC + HFI_ARCH_OX_OFFSET + 0x6000)
+#define  HFI_PROPERTY_CONFIG_VENC_FRAME_QP				\
+	(HFI_PROPERTY_CONFIG_VENC_OX_START + 0x001)
 
 #define HFI_PROPERTY_PARAM_VPE_OX_START					\
 	(HFI_DOMAIN_BASE_VPE + HFI_ARCH_OX_OFFSET + 0x7000)
@@ -337,12 +389,12 @@
 };
 
 #define HFI_CMD_SYS_OX_START		\
-	(HFI_DOMAIN_BASE_COMMON + HFI_ARCH_OX_OFFSET + 0x0000)
+(HFI_DOMAIN_BASE_COMMON + HFI_ARCH_OX_OFFSET + HFI_CMD_START_OFFSET + 0x0000)
 #define HFI_CMD_SYS_SESSION_ABORT	(HFI_CMD_SYS_OX_START + 0x001)
 #define HFI_CMD_SYS_PING		(HFI_CMD_SYS_OX_START + 0x002)
 
 #define HFI_CMD_SESSION_OX_START	\
-	(HFI_DOMAIN_BASE_COMMON + HFI_ARCH_OX_OFFSET + 0x1000)
+(HFI_DOMAIN_BASE_COMMON + HFI_ARCH_OX_OFFSET + HFI_CMD_START_OFFSET + 0x1000)
 #define HFI_CMD_SESSION_LOAD_RESOURCES	(HFI_CMD_SESSION_OX_START + 0x001)
 #define HFI_CMD_SESSION_START		(HFI_CMD_SESSION_OX_START + 0x002)
 #define HFI_CMD_SESSION_STOP		(HFI_CMD_SESSION_OX_START + 0x003)
@@ -360,14 +412,14 @@
 	(HFI_CMD_SESSION_OX_START + 0x00C)
 
 #define HFI_MSG_SYS_OX_START			\
-	(HFI_DOMAIN_BASE_COMMON + HFI_ARCH_OX_OFFSET + 0x0000)
+(HFI_DOMAIN_BASE_COMMON + HFI_ARCH_OX_OFFSET + HFI_MSG_START_OFFSET + 0x0000)
 #define HFI_MSG_SYS_IDLE		(HFI_MSG_SYS_OX_START + 0x1)
 #define HFI_MSG_SYS_PING_ACK	(HFI_MSG_SYS_OX_START + 0x2)
 #define HFI_MSG_SYS_PROPERTY_INFO	(HFI_MSG_SYS_OX_START + 0x3)
 #define HFI_MSG_SYS_SESSION_ABORT_DONE	(HFI_MSG_SYS_OX_START + 0x4)
 
 #define HFI_MSG_SESSION_OX_START		\
-	(HFI_DOMAIN_BASE_COMMON + HFI_ARCH_OX_OFFSET + 0x1000)
+(HFI_DOMAIN_BASE_COMMON + HFI_ARCH_OX_OFFSET + HFI_MSG_START_OFFSET + 0x1000)
 #define HFI_MSG_SESSION_LOAD_RESOURCES_DONE	(HFI_MSG_SESSION_OX_START + 0x1)
 #define HFI_MSG_SESSION_START_DONE		(HFI_MSG_SESSION_OX_START + 0x2)
 #define HFI_MSG_SESSION_STOP_DONE		(HFI_MSG_SESSION_OX_START + 0x3)
@@ -377,9 +429,12 @@
 #define HFI_MSG_SESSION_EMPTY_BUFFER_DONE	(HFI_MSG_SESSION_OX_START + 0x7)
 #define HFI_MSG_SESSION_FILL_BUFFER_DONE	(HFI_MSG_SESSION_OX_START + 0x8)
 #define HFI_MSG_SESSION_PROPERTY_INFO		(HFI_MSG_SESSION_OX_START + 0x9)
-#define HFI_MSG_SESSION_RELEASE_RESOURCES_DONE	(HFI_MSG_SESSION_OX_START + 0xA)
+#define HFI_MSG_SESSION_RELEASE_RESOURCES_DONE	\
+	(HFI_MSG_SESSION_OX_START + 0xA)
 #define HFI_MSG_SESSION_PARSE_SEQUENCE_HEADER_DONE		\
 	(HFI_MSG_SESSION_OX_START + 0xB)
+#define  HFI_MSG_SESSION_RELEASE_BUFFERS_DONE			\
+	(HFI_MSG_SESSION_OX_START + 0xC)
 
 struct hfi_cmd_sys_session_abort_packet {
 	u32 size;
@@ -426,6 +481,7 @@
 	u32 input_tag;
 	u8 *packet_buffer;
 	u8 *extra_data_buffer;
+	u32 rgData[0];
 };
 
 struct hfi_cmd_session_empty_buffer_uncompressed_plane0_packet {
@@ -444,6 +500,7 @@
 	u32 input_tag;
 	u8 *packet_buffer;
 	u8 *extra_data_buffer;
+	u32 rgData[0];
 };
 
 struct hfi_cmd_session_empty_buffer_uncompressed_plane1_packet {
@@ -452,6 +509,7 @@
 	u32 filled_len;
 	u32 offset;
 	u8 *packet_buffer2;
+	u32 rgData[0];
 };
 
 struct hfi_cmd_session_empty_buffer_uncompressed_plane2_packet {
@@ -460,6 +518,7 @@
 	u32 filled_len;
 	u32 offset;
 	u8 *packet_buffer3;
+	u32 rgData[0];
 };
 
 struct hfi_cmd_session_fill_buffer_packet {
@@ -467,9 +526,13 @@
 	u32 packet_type;
 	u32 session_id;
 	u32 stream_id;
+	u32 offset;
+	u32 alloc_len;
+	u32 filled_len;
 	u32 output_tag;
 	u8 *packet_buffer;
 	u8 *extra_data_buffer;
+	u32 rgData[0];
 };
 
 struct hfi_cmd_session_flush_packet {
@@ -506,6 +569,7 @@
 	u32 buffer_type;
 	u32 buffer_size;
 	u32 extra_data_size;
+	int response_req;
 	u32 num_buffers;
 	u32 rg_buffer_info[1];
 };
@@ -602,6 +666,7 @@
 	u32 input_tag;
 	u8 *packet_buffer;
 	u8 *extra_data_buffer;
+	u32 rgData[0];
 };
 
 struct hfi_msg_session_fill_buffer_done_compressed_packet {
@@ -623,6 +688,7 @@
 	u32 picture_type;
 	u8 *packet_buffer;
 	u8 *extra_data_buffer;
+	u32 rgData[0];
 };
 
 struct hfi_msg_session_fbd_uncompressed_plane0_packet {
@@ -651,6 +717,7 @@
 	u32 picture_type;
 	u8 *packet_buffer;
 	u8 *extra_data_buffer;
+	u32 rgData[0];
 };
 
 struct hfi_msg_session_fill_buffer_done_uncompressed_plane1_packet {
@@ -659,6 +726,7 @@
 	u32 filled_len;
 	u32 offset;
 	u8 *packet_buffer2;
+	u32 rgData[0];
 };
 
 struct hfi_msg_session_fill_buffer_done_uncompressed_plane2_packet {
@@ -667,6 +735,7 @@
 	u32 filled_len;
 	u32 offset;
 	u8 *packet_buffer3;
+	u32 rgData[0];
 };
 
 struct hfi_msg_session_parse_sequence_header_done_packet {
@@ -693,6 +762,15 @@
 	u32 error_type;
 };
 
+struct hfi_msg_session_release_buffers_done_packet {
+	u32 size;
+	u32 packet_type;
+	u32 session_id;
+	u32 error_type;
+	u32 num_buffers;
+	u32 rg_buffer_info[1];
+};
+
 struct hfi_extradata_mb_quantization_payload {
 	u8 rg_mb_qp[1];
 };
@@ -731,41 +809,26 @@
 	u32 time_stamp_high;
 };
 
-enum HFI_S3D_FP_LAYOUT {
-	HFI_S3D_FP_LAYOUT_NONE,
-	HFI_S3D_FP_LAYOUT_INTRLV_CHECKERBOARD,
-	HFI_S3D_FP_LAYOUT_INTRLV_COLUMN,
-	HFI_S3D_FP_LAYOUT_INTRLV_ROW,
-	HFI_S3D_FP_LAYOUT_SIDEBYSIDE,
-	HFI_S3D_FP_LAYOUT_TOPBOTTOM,
-	HFI_S3D_FP_LAYOUT_UNUSED = 0x10000000
-};
-
-enum HFI_S3D_FP_VIEW_ORDER {
-	HFI_S3D_FP_LEFTVIEW_FIRST,
-	HFI_S3D_FP_RIGHTVIEW_FIRST,
-	HFI_S3D_FP_UNKNOWN,
-	HFI_S3D_FP_VIEWORDER_UNUSED = 0x10000000
-};
-
-enum HFI_S3D_FP_FLIP {
-	HFI_S3D_FP_FLIP_NONE,
-	HFI_S3D_FP_FLIP_LEFT_HORIZ,
-	HFI_S3D_FP_FLIP_LEFT_VERT,
-	HFI_S3D_FP_FLIP_RIGHT_HORIZ,
-	HFI_S3D_FP_FLIP_RIGHT_VERT,
-	HFI_S3D_FP_FLIP_UNUSED = 0x10000000
-};
 
 struct hfi_extradata_s3d_frame_packing_payload {
-	enum HFI_S3D_FP_LAYOUT layout;
-	enum HFI_S3D_FP_VIEW_ORDER order;
-	enum HFI_S3D_FP_FLIP flip;
-	int quin_cunx;
-	u32 left_view_luma_site_x;
-	u32 left_view_luma_site_y;
-	u32 right_view_luma_site_x;
-	u32 right_view_luma_site_y;
+	u32 fpa_id;
+	int cancel_flag;
+	u32 fpa_type;
+	int quin_cunx_flag;
+	u32 content_interprtation_type;
+	int spatial_flipping_flag;
+	int frame0_flipped_flag;
+	int field_views_flag;
+	int current_frame_isFrame0_flag;
+	int frame0_self_contained_flag;
+	int frame1_self_contained_flag;
+	u32 frame0_graid_pos_x;
+	u32 frame0_graid_pos_y;
+	u32 frame1_graid_pos_x;
+	u32 frame1_graid_pos_y;
+	u32 fpa_reserved_byte;
+	u32 fpa_repetition_period;
+	int fpa_extension_flag;
 };
 
 struct hfi_extradata_interlace_video_payload {
@@ -804,6 +867,26 @@
 	int height;
 };
 
+struct hfi_index_extradata_aspect_ratio_payload {
+	u32 size;
+	u32 version;
+	u32 port_index;
+	u32 aspect_width;
+	u32 aspect_height;
+};
+struct hfi_extradata_panscan_wndw_payload {
+	u32 num_window;
+	struct hfi_extradata_vc1_pswnd wnd[1];
+};
+
+struct hfi_extradata_frame_type_payload {
+	u32 frame_rate;
+};
+
+struct hfi_extradata_recovery_point_sei_payload {
+	u32 flag;
+};
+
 struct vidc_mem_addr {
 	u8 *align_device_addr;
 	u8 *align_virtual_addr;
@@ -833,6 +916,9 @@
 	spinlock_t write_lock;
 	void (*callback) (u32 response, void *callback);
 	struct vidc_mem_addr iface_q_table;
+	struct vidc_mem_addr qdss;
+	struct vidc_mem_addr sfr;
+	struct vidc_mem_addr mem_addr;
 	struct vidc_iface_q_info iface_queues[VIDC_IFACEQ_NUMQ];
 	struct smem_client *hal_client;
 	struct hal_data *hal_data;
@@ -853,14 +939,6 @@
 	int dev_count;
 };
 
-struct hfi_index_extradata_aspect_ratio_payload {
-	u32 size;
-	u32 version;
-	u32 port_index;
-	u32 saspect_width;
-	u32  saspect_height;
-};
-
 extern struct hal_device_data hal_ctxt;
 
 int vidc_hal_iface_msgq_read(struct hal_device *device, void *pkt);
diff --git a/drivers/media/video/msm_vidc/vidc_hal_api.h b/drivers/media/video/msm_vidc/vidc_hal_api.h
index 879418d..3b83424 100644
--- a/drivers/media/video/msm_vidc/vidc_hal_api.h
+++ b/drivers/media/video/msm_vidc/vidc_hal_api.h
@@ -72,12 +72,32 @@
 	VIDC_ERR_UNUSED = 0x10000000
 };
 
+enum hal_extradata_id {
+	HAL_EXTRADATA_NONE,
+	HAL_EXTRADATA_MB_QUANTIZATION,
+	HAL_EXTRADATA_INTERLACE_VIDEO,
+	HAL_EXTRADATA_VC1_FRAMEDISP,
+	HAL_EXTRADATA_VC1_SEQDISP,
+	HAL_EXTRADATA_TIMESTAMP,
+	HAL_EXTRADATA_S3D_FRAME_PACKING,
+	HAL_EXTRADATA_FRAME_RATE,
+	HAL_EXTRADATA_PANSCAN_WINDOW,
+	HAL_EXTRADATA_RECOVERY_POINT_SEI,
+	HAL_EXTRADATA_CLOSED_CAPTION_UD,
+	HAL_EXTRADATA_AFD_UD,
+	HAL_EXTRADATA_MULTISLICE_INFO,
+	HAL_EXTRADATA_INDEX,
+	HAL_EXTRADATA_NUM_CONCEALED_MB,
+	HAL_EXTRADATA_METADATA_FILLER,
+};
+
 enum hal_property {
 	HAL_CONFIG_FRAME_RATE = 0x04000001,
 	HAL_PARAM_UNCOMPRESSED_FORMAT_SELECT,
 	HAL_PARAM_UNCOMPRESSED_PLANE_ACTUAL_CONSTRAINTS_INFO,
 	HAL_PARAM_UNCOMPRESSED_PLANE_ACTUAL_INFO,
 	HAL_PARAM_EXTRA_DATA_HEADER_CONFIG,
+	HAL_PARAM_INDEX_EXTRADATA,
 	HAL_PARAM_FRAME_SIZE,
 	HAL_CONFIG_REALTIME,
 	HAL_PARAM_BUFFER_COUNT_ACTUAL,
@@ -138,6 +158,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 {
@@ -358,7 +379,7 @@
 	HAL_BUFFER_EXTRADATA_OUTPUT2,
 	HAL_BUFFER_INTERNAL_SCRATCH,
 	HAL_BUFFER_INTERNAL_PERSIST,
-	HAL_UNUSED_BUFFER = 0x10000000,
+	HAL_BUFFER_MAX
 };
 
 struct hal_frame_rate {
@@ -459,6 +480,11 @@
 	HAL_UNUSED_PICT = 0x10000000,
 };
 
+struct hal_extradata_enable {
+	u32 enable;
+	enum hal_extradata_id index;
+};
+
 struct hal_enable_picture {
 	u32 picture_type;
 };
@@ -751,6 +777,12 @@
 	u32 align_device_addr;
 	u32 extradata_size;
 	u32 extradata_addr;
+	u32 response_required;
+};
+
+struct hal_buffer_info {
+	u32 buffer_addr;
+	u32 extra_data_addr;
 };
 
 struct vidc_frame_plane_config {
@@ -812,6 +844,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 +865,7 @@
 	SESSION_RELEASE_BUFFER_DONE,
 	SESSION_RELEASE_RESOURCE_DONE,
 	SESSION_PROPERTY_INFO,
+	SESSION_ERROR,
 	RESPONSE_UNUSED = 0x10000000,
 };
 
@@ -940,7 +975,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_helper.h b/drivers/media/video/msm_vidc/vidc_hal_helper.h
index 43995eb..9f805d7 100644
--- a/drivers/media/video/msm_vidc/vidc_hal_helper.h
+++ b/drivers/media/video/msm_vidc/vidc_hal_helper.h
@@ -14,55 +14,6 @@
 #ifndef __H_VIDC_HAL_HELPER_H__
 #define __H_VIDC_HAL_HELPER_H__
 
-#define HFI_NV12_IL_CALC_Y_STRIDE(stride, frame_width, stride_multiple) \
-	{ stride = (frame_width + stride_multiple - 1) & \
-	(0xffffffff - (stride_multiple - 1))}
-
-#define HFI_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 HFI_NV12_IL_CALC_UV_STRIDE(stride, frame_width, stride_multiple) \
-	{ stride = ((((frame_width + 1) >> 1) + stride_multiple - 1) & \
-	(0xffffffff - (stride_multiple - 1))) << 1 }
-
-#define HFI_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 HFI_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 HFI_YUYV_CALC_STRIDE(stride, frame_width, stride_multiple) \
-	{ stride = ((frame_width << 1) + stride_multiple - 1) & \
-	(0xffffffff - (stride_multiple - 1)) }
-
-#define HFI_YUYV_CALC_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 HFI_YUYV_CALC_BUF_SIZE(buf_size, stride, buf_height) \
-	{ buf_size = stride * buf_height }
-
-#define HFI_RGB888_CALC_STRIDE(stride, frame_width, stride_multiple) \
-	{ stride = ((frame_width * 3) + stride_multiple - 1) & \
-	(0xffffffff - (stride_multiple - 1)) }
-
-#define HFI_RGB888_CALC_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 HFI_RGB888_CALC_BUF_SIZE(buf_size, stride, buf_height) \
-	{ buf_size = (stride * buf_height) }
-
 #define HFI_COMMON_BASE				(0)
 #define HFI_OX_BASE					(0x01000000)
 
@@ -81,6 +32,9 @@
 #define HFI_ARCH_COMMON_OFFSET		(0)
 #define HFI_ARCH_OX_OFFSET			(0x00200000)
 
+#define  HFI_CMD_START_OFFSET		(0x00010000)
+#define  HFI_MSG_START_OFFSET		(0x00020000)
+
 #define HFI_ERR_NONE						HFI_COMMON_BASE
 #define HFI_ERR_SYS_FATAL				(HFI_COMMON_BASE + 0x1)
 #define HFI_ERR_SYS_INVALID_PARAMETER		(HFI_COMMON_BASE + 0x2)
@@ -110,6 +64,7 @@
 
 #define HFI_ERR_SESSION_STREAM_CORRUPT		(HFI_COMMON_BASE + 0x100B)
 #define HFI_ERR_SESSION_ENC_OVERFLOW		(HFI_COMMON_BASE + 0x100C)
+#define  HFI_ERR_SESSION_UNSUPPORTED_STREAM	(HFI_COMMON_BASE + 0x100D)
 
 #define HFI_EVENT_SYS_ERROR				(HFI_COMMON_BASE + 0x1)
 #define HFI_EVENT_SESSION_ERROR			(HFI_COMMON_BASE + 0x2)
@@ -130,8 +85,8 @@
 #define HFI_H264_PROFILE_HIGH				0x00000004
 #define HFI_H264_PROFILE_STEREO_HIGH		0x00000008
 #define HFI_H264_PROFILE_MULTIVIEW_HIGH		0x00000010
-#define HFI_H264_PROFILE_CONSTRAINED_HIGH	0x00000020
-#define  HFI_H264_PROFILE_CONSTRAINED_BASE  0x00000040
+#define HFI_H264_PROFILE_CONSTRAINED_BASE	0x00000020
+#define HFI_H264_PROFILE_CONSTRAINED_HIGH	0x00000040
 
 #define HFI_H264_LEVEL_1					0x00000001
 #define HFI_H264_LEVEL_1b					0x00000002
@@ -235,8 +190,11 @@
 #define HFI_PROPERTY_SYS_DEBUG_CONFIG		\
 	(HFI_PROPERTY_SYS_COMMON_START + 0x001)
 #define HFI_PROPERTY_SYS_RESOURCE_OCMEM_REQUIREMENT_INFO	\
-(HFI_PROPERTY_SYS_COMMON_START + 0x002)
-#define HFI_PROPERTY_PARAM_COMMON_START		\
+	(HFI_PROPERTY_SYS_COMMON_START + 0x002)
+#define HFI_PROPERTY_SYS_CONFIG_VCODEC_CLKFREQ				\
+	(HFI_PROPERTY_SYS_COMMON_START + 0x003)
+
+#define HFI_PROPERTY_PARAM_COMMON_START	\
 	(HFI_DOMAIN_BASE_COMMON + HFI_ARCH_COMMON_OFFSET + 0x1000)
 #define HFI_PROPERTY_PARAM_FRAME_SIZE		\
 	(HFI_PROPERTY_PARAM_COMMON_START + 0x001)
@@ -266,6 +224,8 @@
 	(HFI_PROPERTY_PARAM_COMMON_START + 0x00D)
 #define  HFI_PROPERTY_PARAM_CODEC_MASK_SUPPORTED            \
 	(HFI_PROPERTY_PARAM_COMMON_START + 0x00E)
+#define HFI_PROPERTY_PARAM_MVC_BUFFER_LAYOUT \
+	(HFI_PROPERTY_PARAM_COMMON_START + 0x00F)
 
 #define HFI_PROPERTY_CONFIG_COMMON_START				\
 	(HFI_DOMAIN_BASE_COMMON + HFI_ARCH_COMMON_OFFSET + 0x2000)
@@ -316,16 +276,25 @@
 	(HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x00F)
 #define  HFI_PROPERTY_PARAM_VENC_QUALITY_VS_SPEED           \
 	(HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x010)
-#define HFI_PROPERTY_PARAM_VENC_MPEG4_QPEL				\
-	(HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x011)
 #define HFI_PROPERTY_PARAM_VENC_ADVANCED				\
 	(HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x012)
-#define HFI_PROPERTY_PARAM_VENC_SYNC_FRAME_SEQUENCE_HEADER	\
-	(HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x013)
 #define  HFI_PROPERTY_PARAM_VENC_H264_SPS_ID                \
 	(HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x014)
 #define  HFI_PROPERTY_PARAM_VENC_H264_PPS_ID               \
 	(HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x015)
+#define HFI_PROPERTY_PARAM_VENC_H264_GENERATE_AUDNAL	\
+	(HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x016)
+#define HFI_PROPERTY_PARAM_VENC_ASPECT_RATIO			\
+	(HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x017)
+#define HFI_PROPERTY_PARAM_VENC_NUMREF					\
+	(HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x018)
+#define HFI_PROPERTY_PARAM_VENC_MULTIREF_P				\
+	(HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x019)
+#define HFI_PROPERTY_PARAM_VENC_HIER_P_NUM_ENH_LAYER	\
+	(HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x01A)
+
+#define HFI_PROPERTY_PARAM_VENC_H264_NAL_SVC_EXT		\
+	(HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x01B)
 
 #define HFI_PROPERTY_CONFIG_VENC_COMMON_START				\
 	(HFI_DOMAIN_BASE_VENC + HFI_ARCH_COMMON_OFFSET + 0x6000)
@@ -339,13 +308,13 @@
 	(HFI_PROPERTY_CONFIG_VENC_COMMON_START + 0x004)
 #define  HFI_PROPERTY_CONFIG_VENC_SLICE_SIZE                \
 	(HFI_PROPERTY_CONFIG_VENC_COMMON_START + 0x005)
-#define HFI_PROPERTY_CONFIG_VENC_FRAME_QP				\
-	(HFI_PROPERTY_CONFIG_VENC_COMMON_START + 0x006)
 #define HFI_PROPERTY_CONFIG_VENC_MAX_BITRATE				\
 	(HFI_PROPERTY_CONFIG_VENC_COMMON_START + 0x007)
 
 #define HFI_PROPERTY_PARAM_VPE_COMMON_START				\
 	(HFI_DOMAIN_BASE_VPE + HFI_ARCH_COMMON_OFFSET + 0x7000)
+#define  HFI_PROPERTY_CONFIG_VENC_SYNC_FRAME_SEQUENCE_HEADER	\
+	(HFI_PROPERTY_CONFIG_VENC_COMMON_START + 0x008)
 
 #define HFI_PROPERTY_CONFIG_VPE_COMMON_START				\
 	(HFI_DOMAIN_BASE_VPE + HFI_ARCH_COMMON_OFFSET + 0x8000)
@@ -356,6 +325,7 @@
 
 struct hfi_bitrate {
 	u32 bit_rate;
+	u32 layer_id;
 };
 
 #define HFI_CAPABILITY_FRAME_WIDTH			(HFI_COMMON_BASE + 0x1)
@@ -367,7 +337,7 @@
 #define HFI_CAPABILITY_SCALE_Y				(HFI_COMMON_BASE + 0x7)
 #define HFI_CAPABILITY_BITRATE				(HFI_COMMON_BASE + 0x8)
 #define  HFI_CAPABILITY_BFRAME				(HFI_COMMON_BASE + 0x9)
-#define  HFI_CAPABILITY_HIERARCHICAL_P_LAYERS	(HFI_COMMON_BASE + 0x10)
+#define  HFI_CAPABILITY_HIER_P_NUM_ENH_LAYERS   (HFI_COMMON_BASE + 0x10)
 
 struct hfi_capability_supported {
 	u32 capability_type;
@@ -386,9 +356,14 @@
 #define HFI_DEBUG_MSG_HIGH					0x00000004
 #define HFI_DEBUG_MSG_ERROR					0x00000008
 #define HFI_DEBUG_MSG_FATAL					0x00000010
+#define HFI_DEBUG_MSG_PERF					0x00000020
+
+#define HFI_DEBUG_MODE_QUEUE				0x00000001
+#define HFI_DEBUG_MODE_QDSS					0x00000002
 
 struct hfi_debug_config {
 	u32 debug_config;
+	u32 debug_mode;
 };
 
 struct hfi_enable {
@@ -396,8 +371,9 @@
 };
 
 #define HFI_H264_DB_MODE_DISABLE			(HFI_COMMON_BASE + 0x1)
-#define HFI_H264_DB_MODE_SKIP_SLICE_BOUNDARY	(HFI_COMMON_BASE + 0x2)
-#define HFI_H264_DB_MODE_ALL_BOUNDARY			(HFI_COMMON_BASE + 0x3)
+#define HFI_H264_DB_MODE_SKIP_SLICE_BOUNDARY	\
+	(HFI_COMMON_BASE + 0x2)
+#define HFI_H264_DB_MODE_ALL_BOUNDARY		(HFI_COMMON_BASE + 0x3)
 
 struct hfi_h264_db_control {
 	u32 mode;
@@ -465,8 +441,8 @@
 };
 
 #define HFI_MULTI_SLICE_OFF				(HFI_COMMON_BASE + 0x1)
-#define HFI_MULTI_SLICE_BY_MB_COUNT			(HFI_COMMON_BASE + 0x2)
-#define HFI_MULTI_SLICE_BY_BYTE_COUNT		(HFI_COMMON_BASE + 0x3)
+#define HFI_MULTI_SLICE_BY_MB_COUNT		(HFI_COMMON_BASE + 0x2)
+#define HFI_MULTI_SLICE_BY_BYTE_COUNT	(HFI_COMMON_BASE + 0x3)
 #define HFI_MULTI_SLICE_GOB				(HFI_COMMON_BASE + 0x4)
 
 struct hfi_multi_slice_control {
@@ -654,9 +630,23 @@
 struct hfi_seq_header_info {
 	u32 max_hader_len;
 };
+struct hfi_aspect_ratio {
+	u32 aspect_width;
+	u32 aspect_height;
+};
+#define HFI_MVC_BUFFER_LAYOUT_TOP_BOTTOM  (0)
+#define HFI_MVC_BUFFER_LAYOUT_SIDEBYSIDE  (1)
+#define HFI_MVC_BUFFER_LAYOUT_SEQ         (2)
+struct hfi_mvc_buffer_lauout_descp_type {
+	u32    layout_type;
+	u32    bright_view_first;
+	u32    ngap;
+};
+
 
 #define HFI_CMD_SYS_COMMON_START			\
-	(HFI_DOMAIN_BASE_COMMON + HFI_ARCH_COMMON_OFFSET + 0x0000)
+(HFI_DOMAIN_BASE_COMMON + HFI_ARCH_COMMON_OFFSET + HFI_CMD_START_OFFSET \
+	+ 0x0000)
 #define HFI_CMD_SYS_INIT		(HFI_CMD_SYS_COMMON_START + 0x001)
 #define HFI_CMD_SYS_PC_PREP		(HFI_CMD_SYS_COMMON_START + 0x002)
 #define HFI_CMD_SYS_SET_RESOURCE	(HFI_CMD_SYS_COMMON_START + 0x003)
@@ -666,9 +656,11 @@
 #define HFI_CMD_SYS_SESSION_INIT	(HFI_CMD_SYS_COMMON_START + 0x007)
 #define HFI_CMD_SYS_SESSION_END		(HFI_CMD_SYS_COMMON_START + 0x008)
 #define HFI_CMD_SYS_SET_BUFFERS		(HFI_CMD_SYS_COMMON_START + 0x009)
+#define HFI_CMD_SYS_TEST_START		(HFI_CMD_SYS_COMMON_START + 0x100)
 
 #define HFI_CMD_SESSION_COMMON_START		\
-	(HFI_DOMAIN_BASE_COMMON + HFI_ARCH_COMMON_OFFSET + 0x1000)
+	(HFI_DOMAIN_BASE_COMMON + HFI_ARCH_COMMON_OFFSET +	\
+	HFI_CMD_START_OFFSET + 0x1000)
 #define HFI_CMD_SESSION_SET_PROPERTY		\
 	(HFI_CMD_SESSION_COMMON_START + 0x001)
 #define HFI_CMD_SESSION_SET_BUFFERS			\
@@ -677,7 +669,8 @@
 	(HFI_CMD_SESSION_COMMON_START + 0x003)
 
 #define HFI_MSG_SYS_COMMON_START			\
-	(HFI_DOMAIN_BASE_COMMON + HFI_ARCH_COMMON_OFFSET + 0x0000)
+	(HFI_DOMAIN_BASE_COMMON + HFI_ARCH_COMMON_OFFSET +	\
+	HFI_MSG_START_OFFSET + 0x0000)
 #define HFI_MSG_SYS_INIT_DONE			(HFI_MSG_SYS_COMMON_START + 0x1)
 #define HFI_MSG_SYS_PC_PREP_DONE		(HFI_MSG_SYS_COMMON_START + 0x2)
 #define HFI_MSG_SYS_RELEASE_RESOURCE	(HFI_MSG_SYS_COMMON_START + 0x3)
@@ -686,7 +679,8 @@
 #define HFI_MSG_SYS_SESSION_END_DONE	(HFI_MSG_SYS_COMMON_START + 0x7)
 
 #define HFI_MSG_SESSION_COMMON_START		\
-	(HFI_DOMAIN_BASE_COMMON + HFI_ARCH_COMMON_OFFSET + 0x1000)
+	(HFI_DOMAIN_BASE_COMMON + HFI_ARCH_COMMON_OFFSET +	\
+	HFI_MSG_START_OFFSET + 0x1000)
 #define HFI_MSG_EVENT_NOTIFY	(HFI_MSG_SESSION_COMMON_START + 0x1)
 #define HFI_MSG_SESSION_GET_SEQUENCE_HEADER_DONE	\
 	(HFI_MSG_SESSION_COMMON_START + 0x2)
@@ -778,7 +772,6 @@
 	u32 packet_type;
 	u32 session_id;
 	u32 buffer_type;
-	u32 buffer_mode;
 	u32 buffer_size;
 	u32 extra_data_size;
 	u32 min_buffer_size;
@@ -860,4 +853,22 @@
 	u8 rg_msg_data[1];
 };
 
+enum HFI_VENUS_QTBL_STATUS {
+	HFI_VENUS_QTBL_DISABLED = 0x00,
+	HFI_VENUS_QTBL_ENABLED = 0x01,
+	HFI_VENUS_QTBL_INITIALIZING = 0x02,
+	HFI_VENUS_QTBL_DEINITIALIZING = 0x03
+};
+
+enum HFI_VENUS_CTRL_INIT_STATUS {
+	HFI_VENUS_CTRL_NOT_INIT = 0x0,
+	HFI_VENUS_CTRL_READY = 0x1,
+	HFI_VENUS_CTRL_ERROR_FATAL = 0x2
+};
+
+struct hfi_sfr_struct {
+	u32 bufSize;
+	u8 rg_data[1];
+};
+
 #endif
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 043ed73..3bedb92 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,32 @@
 	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 hfi_msg_event_notify_packet *pkt)
+{
+	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;
+	cmd_done.session_id = ((struct hal_session *) pkt->session_id)->
+		session_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 +178,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, pkt);
 		break;
 	case HFI_EVENT_SESSION_SEQUENCE_CHANGED:
 		dprintk(VIDC_INFO, "HFI_EVENT_SESSION_SEQUENCE_CHANGED");
@@ -167,7 +196,6 @@
 		break;
 	}
 }
-
 static void hal_process_sys_init_done(struct hal_device *device,
 		struct hfi_msg_sys_init_done_packet *pkt)
 {
@@ -290,16 +318,22 @@
 {
 	struct hfi_buffer_requirements *hfi_buf_req;
 	u32 req_bytes;
-	enum vidc_status rc = VIDC_ERR_NONE;
 
 	dprintk(VIDC_DBG, "Entered ");
+	if (!prop) {
+		dprintk(VIDC_ERR,
+			"hal_process_sess_get_prop_buf_req:bad_prop: %p",
+			prop);
+		return;
+	}
 	req_bytes = prop->size - sizeof(
 	struct hfi_msg_session_property_info_packet);
 
-	if (req_bytes == 0 || (req_bytes % sizeof(
-		struct hfi_buffer_requirements))) {
+	if (!req_bytes || (req_bytes % sizeof(
+		struct hfi_buffer_requirements)) ||
+		(!prop->rg_property_data[1])) {
 		dprintk(VIDC_ERR,
-			"hal_process_sess_get_prop_buf_req:bad_pkt_size: %d",
+			"hal_process_sess_get_prop_buf_req:bad_pkt: %d",
 			req_bytes);
 		return;
 	}
@@ -307,15 +341,14 @@
 	hfi_buf_req = (struct hfi_buffer_requirements *)
 		&prop->rg_property_data[1];
 
-	while (req_bytes != 0) {
-		if ((hfi_buf_req->buffer_count_min > hfi_buf_req->
-			buffer_count_actual)
-			|| (hfi_buf_req->buffer_alignment == 0)
-			|| (hfi_buf_req->buffer_size == 0)) {
-			dprintk(VIDC_ERR, "hal_process_sess_get_prop_buf_req:"
-						"bad_buf_req");
-			rc = VIDC_ERR_FAIL;
-		}
+	while (req_bytes) {
+		if ((hfi_buf_req->buffer_size) &&
+			((hfi_buf_req->buffer_count_min > hfi_buf_req->
+			buffer_count_actual)))
+				dprintk(VIDC_WARN,
+					"hal_process_sess_get_prop_buf_req:"
+					"bad_buf_req");
+
 		dprintk(VIDC_DBG, "got buffer requirements for: %d",
 					hfi_buf_req->buffer_type);
 		switch (hfi_buf_req->buffer_type) {
@@ -500,7 +533,7 @@
 
 	dprintk(VIDC_DBG, "RECEIVED:SESSION_ETB_DONE");
 
-	if (!pkt || pkt->size !=
+	if (!pkt || pkt->size <
 		sizeof(struct hfi_msg_session_empty_buffer_done_packet)) {
 		dprintk(VIDC_ERR, "hal_process_session_etb_done:bad_pkt_size");
 		return;
@@ -546,7 +579,7 @@
 		msg_hdr;
 		if (sizeof(struct
 			hfi_msg_session_fill_buffer_done_compressed_packet)
-			!= pkt->size) {
+			> pkt->size) {
 			dprintk(VIDC_ERR,
 				"hal_process_session_ftb_done: bad_pkt_size");
 			return;
@@ -699,6 +732,31 @@
 	device->callback(SESSION_RELEASE_RESOURCE_DONE, &cmd_done);
 }
 
+static void hal_process_session_rel_buf_done(struct hal_device *device,
+	struct hfi_msg_session_release_buffers_done_packet *pkt)
+{
+	struct msm_vidc_cb_cmd_done cmd_done;
+	if (!pkt || pkt->size !=
+		sizeof(struct
+			   hfi_msg_session_release_buffers_done_packet)) {
+		dprintk(VIDC_ERR, "bad packet/packet size: %d", pkt->size);
+		return;
+	}
+	memset(&cmd_done, 0, sizeof(struct msm_vidc_cb_cmd_done));
+	cmd_done.device_id = device->device_id;
+	cmd_done.size = sizeof(struct msm_vidc_cb_cmd_done);
+	cmd_done.session_id =
+		((struct hal_session *) pkt->session_id)->session_id;
+	cmd_done.status = vidc_map_hal_err_status((u32)pkt->error_type);
+	if (pkt->rg_buffer_info) {
+		cmd_done.data = (void *) &pkt->rg_buffer_info;
+		cmd_done.size = sizeof(struct hfi_buffer_info);
+	} else {
+		dprintk(VIDC_ERR, "invalid payload in rel_buff_done\n");
+	}
+	device->callback(SESSION_RELEASE_BUFFER_DONE, &cmd_done);
+}
+
 static void hal_process_session_end_done(struct hal_device *device,
 	struct hfi_msg_sys_session_end_done_packet *pkt)
 {
@@ -764,6 +822,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,
@@ -832,6 +895,11 @@
 			hfi_msg_session_get_sequence_header_done_packet
 			 *) msg_hdr);
 		break;
+	case HFI_MSG_SESSION_RELEASE_BUFFERS_DONE:
+		hal_process_session_rel_buf_done(device, (struct
+			hfi_msg_session_release_buffers_done_packet
+			*) msg_hdr);
+		break;
 	default:
 		dprintk(VIDC_ERR, "UNKNOWN_MSG_TYPE : %d", msg_hdr->packet);
 		break;
diff --git a/drivers/media/video/msm_vidc/vidc_hal_io.h b/drivers/media/video/msm_vidc/vidc_hal_io.h
index b85e015..9888adb 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,42 @@
 	(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)
+
+#define VIDC_CTRL_INIT 0x000D2048
+#define VIDC_CTRL_INIT_RESERVED_BITS31_1__M 0xFFFFFFFE
+#define VIDC_CTRL_INIT_RESERVED_BITS31_1__S 1
+#define VIDC_CTRL_INIT_CTRL__M 0x00000001
+#define VIDC_CTRL_INIT_CTRL__S 0
+
+#define VIDC_CTRL_STATUS 0x000D204C
+#define VIDC_CTRL_STATUS_RESERVED_BITS31_8__M 0xFFFFFF00
+#define VIDC_CTRL_STATUS_RESERVED_BITS31_8__S 8
+#define VIDC_CTRL_ERROR_STATUS__M             0x000000FE
+#define VIDC_CTRL_ERROR_STATUS__S             1
+#define VIDC_CTRL_INIT_STATUS__M              0x00000001
+#define VIDC_CTRL_INIT_STATUS__S              0
+
+#define VIDC_QTBL_INFO 0x000D2050
+#define VIDC_QTBL_HOSTID__M 0xFF000000
+#define VIDC_QTBL_HOSTID__S 24
+#define VIDC_QTBL_INFO_RESERVED_BITS23_8__M 0x00FFFF00
+#define VIDC_QTBL_INFO_RESERVED_BITS23_8__S 8
+#define VIDC_QTBL_STATUS__M 0x000000FF
+#define VIDC_QTBL_STATUS__S 0
+
+#define VIDC_QTBL_ADDR 0x000D2054
+
+#define VIDC_VERSION_INFO 0x000D2058
+#define VIDC_VERSION_INFO_MAJOR__M  0xF0000000
+#define VIDC_VERSION_INFO_MAJOR__S  28
+#define VIDC_VERSION_INFO_MINOR__M  0x0FFFFFE0
+#define VIDC_VERSION_INFO_MINOR__S  5
+#define VIDC_VERSION_INFO_BRANCH__M 0x0000001F
+#define VIDC_VERSION_INFO_BRANCH__S 0
+
+#define VIDC_SFR_ADDR 0x000D205C
+#define VIDC_MMAP_ADDR 0x000D2060
+#define VIDC_UC_REGION_ADDR 0x000D2064
+#define VIDC_UC_REGION_SIZE 0x000D2068
 
 #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..21fc719 100644
--- a/drivers/media/video/msm_wfd/enc-mfc-subdev.c
+++ b/drivers/media/video/msm_wfd/enc-mfc-subdev.c
@@ -23,6 +23,8 @@
 
 #define VID_ENC_MAX_ENCODER_CLIENTS 1
 #define MAX_NUM_CTRLS 20
+#define V4L2_FRAME_FLAGS (V4L2_BUF_FLAG_KEYFRAME | V4L2_BUF_FLAG_PFRAME | \
+		V4L2_BUF_FLAG_BFRAME | V4L2_QCOM_BUF_FLAG_CODECCONFIG)
 
 static long venc_fill_outbuf(struct v4l2_subdev *sd, void *arg);
 
@@ -38,6 +40,7 @@
 	int secure;
 	struct mem_region unqueued_op_bufs;
 	bool streaming;
+	enum venc_framerate_modes framerate_mode;
 };
 
 struct venc {
@@ -179,6 +182,7 @@
 		vbuf->v4l2_planes[0].bytesused =
 			frame_data->data_len;
 
+		vbuf->v4l2_buf.flags &= ~(V4L2_FRAME_FLAGS);
 		switch (frame_data->frame) {
 		case VCD_FRAME_I:
 		case VCD_FRAME_IDR:
@@ -194,6 +198,9 @@
 			break;
 		}
 
+		if (frame_data->flags & VCD_FRAME_FLAG_CODECCONFIG)
+			vbuf->v4l2_buf.flags |= V4L2_QCOM_BUF_FLAG_CODECCONFIG;
+
 		vbuf->v4l2_buf.timestamp =
 			ns_to_timeval(frame_data->time_stamp * NSEC_PER_USEC);
 
@@ -295,6 +302,8 @@
 	inst->cbdata = vmops->cbdata;
 	inst->secure = vmops->secure;
 	inst->streaming = false;
+	inst->framerate_mode = VENC_MODE_VFR;
+
 	if (vmops->secure) {
 		WFD_MSG_ERR("OPENING SECURE SESSION\n");
 		flags |= VCD_CP_SESSION;
@@ -1117,6 +1126,14 @@
 	return rc;
 }
 
+static long venc_set_framerate_mode(struct v4l2_subdev *sd,
+				void *arg)
+{
+	struct venc_inst *inst = sd->dev_priv;
+	inst->framerate_mode = *(enum venc_framerate_modes *)arg;
+	return 0;
+}
+
 static long venc_set_qp_value(struct video_client_ctx *client_ctx,
 		__s32 frametype, __s32 qp)
 {
@@ -1394,6 +1411,51 @@
 	return rc;
 }
 
+static long venc_set_vui_timing_info(struct video_client_ctx *client_ctx,
+			struct venc_inst *inst, __s32 flag)
+{
+	struct vcd_property_hdr vcd_property_hdr;
+	struct vcd_property_vui_timing_info_enable vui_timing_info_enable;
+
+	if (!client_ctx)
+		return -EINVAL;
+	if (inst->framerate_mode == VENC_MODE_VFR) {
+		WFD_MSG_ERR("VUI timing info not suported in VFR mode ");
+		return -EINVAL;
+	}
+	vcd_property_hdr.prop_id = VCD_I_ENABLE_VUI_TIMING_INFO;
+	vcd_property_hdr.sz =
+			sizeof(struct vcd_property_vui_timing_info_enable);
+	vui_timing_info_enable.vui_timing_info = flag;
+	return vcd_set_property(client_ctx->vcd_handle,
+				&vcd_property_hdr, &vui_timing_info_enable);
+}
+
+static long venc_get_vui_timing_info(struct video_client_ctx *client_ctx,
+			__s32 *flag)
+{
+	struct vcd_property_hdr vcd_property_hdr;
+	struct vcd_property_vui_timing_info_enable vui_timing_info_enable;
+	int rc = 0;
+
+	if (!client_ctx || !flag)
+		return -EINVAL;
+
+	vcd_property_hdr.prop_id = VCD_I_ENABLE_VUI_TIMING_INFO;
+	vcd_property_hdr.sz =
+			sizeof(struct vcd_property_vui_timing_info_enable);
+	rc = vcd_get_property(client_ctx->vcd_handle,
+				&vcd_property_hdr, &vui_timing_info_enable);
+
+	if (rc < 0) {
+		WFD_MSG_ERR("Failed getting property for VUI timing info");
+		return rc;
+	}
+
+	*flag = vui_timing_info_enable.vui_timing_info;
+	return rc;
+}
+
 static long venc_set_header_mode(struct video_client_ctx *client_ctx,
 		__s32 mode)
 {
@@ -1975,6 +2037,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 +2051,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 +2063,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,
@@ -2277,6 +2341,9 @@
 	case V4L2_CID_MPEG_VIDC_VIDEO_H264_AU_DELIMITER:
 		rc = venc_set_avc_delimiter(client_ctx, ctrl->value);
 		break;
+	case V4L2_CID_MPEG_VIDC_VIDEO_H264_VUI_TIMING_INFO:
+		rc = venc_set_vui_timing_info(client_ctx, inst, ctrl->value);
+		break;
 	case V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE:
 		rc = venc_set_entropy_mode(client_ctx, ctrl->value);
 		break;
@@ -2351,6 +2418,9 @@
 	case V4L2_CID_MPEG_VIDC_VIDEO_H264_AU_DELIMITER:
 		rc = venc_get_avc_delimiter(client_ctx, &ctrl->value);
 		break;
+	case V4L2_CID_MPEG_VIDC_VIDEO_H264_VUI_TIMING_INFO:
+		rc = venc_get_vui_timing_info(client_ctx, &ctrl->value);
+		break;
 	default:
 		WFD_MSG_ERR("Get property not suported: %d\n", ctrl->id);
 		rc = -ENOTSUPP;
@@ -2495,6 +2565,9 @@
 	case ENC_MUNMAP:
 		rc = venc_munmap(sd, arg);
 		break;
+	case SET_FRAMERATE_MODE:
+		rc = venc_set_framerate_mode(sd, arg);
+		break;
 	default:
 		rc = -1;
 		break;
diff --git a/drivers/media/video/msm_wfd/enc-subdev.h b/drivers/media/video/msm_wfd/enc-subdev.h
index c6c854e..25373e4 100644
--- a/drivers/media/video/msm_wfd/enc-subdev.h
+++ b/drivers/media/video/msm_wfd/enc-subdev.h
@@ -20,6 +20,11 @@
 #include <media/videobuf2-core.h>
 #define VENC_MAGIC_IOCTL 'V'
 
+enum venc_framerate_modes {
+	VENC_MODE_CFR,
+	VENC_MODE_VFR,
+};
+
 struct mem_region {
 	struct list_head list;
 	u8 *kvaddr;
@@ -101,6 +106,7 @@
 #define ENCODE_FLUSH _IO('V', 24)
 #define ENC_MMAP _IOWR('V', 25, struct mem_region_map *)
 #define ENC_MUNMAP _IOWR('V', 26, struct mem_region_map *)
+#define SET_FRAMERATE_MODE _IO('V', 27)
 
 extern int venc_init(struct v4l2_subdev *sd, u32 val);
 extern int venc_load_fw(struct v4l2_subdev *sd);
diff --git a/drivers/media/video/msm_wfd/enc-venus-subdev.c b/drivers/media/video/msm_wfd/enc-venus-subdev.c
index 89ad6c7..150c667 100644
--- a/drivers/media/video/msm_wfd/enc-venus-subdev.c
+++ b/drivers/media/video/msm_wfd/enc-venus-subdev.c
@@ -178,7 +178,7 @@
 			rc = msm_vidc_dqbuf(inst->vidc_context, &buffer);
 
 			if (rc) {
-				WFD_MSG_ERR("Error dequeuing buffer" \
+				WFD_MSG_ERR("Error dequeuing buffer " \
 						"from vidc: %d", rc);
 				goto abort_dequeue;
 			}
@@ -1010,7 +1010,8 @@
 	inst = (struct venc_inst *)sd->dev_priv;
 
 	enc_cmd.cmd = V4L2_ENC_QCOM_CMD_FLUSH;
-	enc_cmd.flags = BUF_TYPE_INPUT | BUF_TYPE_OUTPUT;
+	enc_cmd.flags = V4L2_QCOM_CMD_FLUSH_OUTPUT |
+		V4L2_QCOM_CMD_FLUSH_CAPTURE;
 	msm_vidc_encoder_cmd(inst->vidc_context, &enc_cmd);
 
 	wait_for_completion(&inst->cmd_complete);
diff --git a/drivers/media/video/msm_wfd/wfd-ioctl.c b/drivers/media/video/msm_wfd/wfd-ioctl.c
index d8080dd..99dc0d0 100644
--- a/drivers/media/video/msm_wfd/wfd-ioctl.c
+++ b/drivers/media/video/msm_wfd/wfd-ioctl.c
@@ -155,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");
@@ -238,6 +240,7 @@
 	int rc;
 	unsigned long flags;
 	struct mdp_buf_info mdp_buf = {0};
+	struct mem_region_map mmap_context = {0};
 	spin_lock_irqsave(&inst->inst_lock, flags);
 	if (inst->input_bufs_allocated) {
 		spin_unlock_irqrestore(&inst->inst_lock, flags);
@@ -247,7 +250,6 @@
 	spin_unlock_irqrestore(&inst->inst_lock, flags);
 
 	for (i = 0; i < VENC_INPUT_BUFFERS; ++i) {
-		struct mem_region_map mmap_context = {0};
 		mpair = kzalloc(sizeof(*mpair), GFP_KERNEL);
 		enc_mregion = kzalloc(sizeof(*enc_mregion), GFP_KERNEL);
 		mdp_mregion = kzalloc(sizeof(*enc_mregion), GFP_KERNEL);
@@ -269,12 +271,17 @@
 			goto alloc_fail;
 		}
 
-		WFD_MSG_ERR("NOTE: enc paddr = %p, kvaddr = %p\n",
-				enc_mregion->paddr,
+		WFD_MSG_DBG("NOTE: enc paddr = [%p->%p], kvaddr = %p\n",
+				enc_mregion->paddr, (int8_t *)
+				enc_mregion->paddr + enc_mregion->size,
 				enc_mregion->kvaddr);
 
 		rc = v4l2_subdev_call(&wfd_dev->enc_sdev, core, ioctl,
 				SET_INPUT_BUFFER, (void *)enc_mregion);
+		if (rc) {
+			WFD_MSG_ERR("Setting enc input buffer failed\n");
+			goto set_input_fail;
+		}
 
 		/* map the buffer from encoder to mdp */
 		mdp_mregion->kvaddr = enc_mregion->kvaddr;
@@ -298,7 +305,7 @@
 			mdp_mregion->kvaddr = NULL;
 			mdp_mregion->paddr = NULL;
 			mdp_mregion->ion_handle = NULL;
-			goto alloc_fail;
+			goto mdp_mmap_fail;
 		}
 
 		mdp_buf.inst = inst->mdp_inst;
@@ -311,34 +318,58 @@
 				((int)mdp_mregion->paddr + mdp_mregion->size),
 				mdp_mregion->kvaddr);
 
-		INIT_LIST_HEAD(&mpair->list);
-		mpair->enc = enc_mregion;
-		mpair->mdp = mdp_mregion;
-		list_add_tail(&mpair->list, &inst->input_mem_list);
-
 		rc = v4l2_subdev_call(&wfd_dev->mdp_sdev, core, ioctl,
 				MDP_Q_BUFFER, (void *)&mdp_buf);
 		if (rc) {
 			WFD_MSG_ERR("Unable to queue the"
 					" buffer to mdp\n");
-			break;
+			goto mdp_q_fail;
 		} else {
 			wfd_stats_update(&inst->stats,
 					WFD_STAT_EVENT_MDP_QUEUE);
 		}
+
+		INIT_LIST_HEAD(&mpair->list);
+		mpair->enc = enc_mregion;
+		mpair->mdp = mdp_mregion;
+		list_add_tail(&mpair->list, &inst->input_mem_list);
+
 	}
+
 	rc = v4l2_subdev_call(&wfd_dev->enc_sdev, core, ioctl,
 			ALLOC_RECON_BUFFERS, NULL);
 	if (rc) {
 		WFD_MSG_ERR("Failed to allocate recon buffers\n");
-		goto alloc_fail;
+		goto recon_alloc_fail;
 	}
 	return rc;
 
+	/*
+	 * Clean up only the buffer that we failed in setting up.
+	 * Caller will clean up the rest by calling free_input_buffers()
+	 */
+mdp_q_fail:
+	memset(&mmap_context, 0, sizeof(mmap_context));
+	mmap_context.mregion = mdp_mregion;
+	mmap_context.ion_client = wfd_dev->ion_client;
+	mmap_context.cookie = inst->mdp_inst;
+	v4l2_subdev_call(&wfd_dev->mdp_sdev, core, ioctl,
+			MDP_MUNMAP, (void *)&mmap_context);
+mdp_mmap_fail:
+	v4l2_subdev_call(&wfd_dev->enc_sdev,
+			core, ioctl, FREE_INPUT_BUFFER,
+			(void *)enc_mregion);
+set_input_fail:
+	memset(&mmap_context, 0, sizeof(mmap_context));
+	mmap_context.ion_client = wfd_dev->ion_client;
+	mmap_context.mregion = enc_mregion;
+	v4l2_subdev_call(&wfd_dev->enc_sdev, core, ioctl,
+			ENC_MUNMAP, &mmap_context);
 alloc_fail:
 	kfree(mpair);
 	kfree(enc_mregion);
 	kfree(mdp_mregion);
+recon_alloc_fail:
 	return rc;
 }
 void wfd_free_input_buffers(struct wfd_device *wfd_dev,
@@ -1044,7 +1075,8 @@
 	struct v4l2_qcom_frameskip frameskip;
 	int64_t frame_interval, max_frame_interval;
 	void *extendedmode = NULL;
-	enum vsg_modes mode = VSG_MODE_VFR;
+	enum vsg_modes vsg_mode = VSG_MODE_VFR;
+	enum venc_framerate_modes venc_mode = VENC_MODE_VFR;
 
 
 	if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
@@ -1066,6 +1098,7 @@
 			rc = -EINVAL;
 			goto set_parm_fail;
 		}
+		venc_mode = VENC_MODE_CFR;
 		frame_interval =
 			a->parm.capture.timeperframe.numerator * NSEC_PER_SEC /
 			a->parm.capture.timeperframe.denominator;
@@ -1094,7 +1127,7 @@
 			goto set_parm_fail;
 
 		max_frame_interval = (int64_t)frameskip.maxframeinterval;
-		mode = VSG_MODE_VFR;
+		vsg_mode = VSG_MODE_VFR;
 
 		rc = v4l2_subdev_call(&wfd_dev->vsg_sdev, core,
 				ioctl, VSG_SET_MAX_FRAME_INTERVAL,
@@ -1104,19 +1137,23 @@
 			goto set_parm_fail;
 
 		rc = v4l2_subdev_call(&wfd_dev->vsg_sdev, core,
-				ioctl, VSG_SET_MODE, &mode);
+				ioctl, VSG_SET_MODE, &vsg_mode);
 
 		if (rc)
 			goto set_parm_fail;
 	} else {
-		mode = VSG_MODE_CFR;
+		vsg_mode = VSG_MODE_CFR;
 		rc = v4l2_subdev_call(&wfd_dev->vsg_sdev, core,
-				ioctl, VSG_SET_MODE, &mode);
+				ioctl, VSG_SET_MODE, &vsg_mode);
 
 		if (rc)
 			goto set_parm_fail;
 	}
 
+	rc = v4l2_subdev_call(&wfd_dev->enc_sdev, core,
+			ioctl, SET_FRAMERATE_MODE,
+			&venc_mode);
+
 set_parm_fail:
 	return rc;
 }
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 7757b5c..5b76c35 100644
--- a/drivers/media/video/vcap_v4l2.c
+++ b/drivers/media/video/vcap_v4l2.c
@@ -82,7 +82,7 @@
 
 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));
@@ -501,7 +501,7 @@
 	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;
@@ -521,23 +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) {
+
+	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_unmap_iommu(dev->ion_client, buf->ion_handle, dev->domain_num, 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;
@@ -555,11 +564,33 @@
 	if (NULL == vb)
 		return -EINVAL;
 
-	free_ion_handle_work(dev, vb);
+	free_ion_handle_work(c_data, vb);
 	return 0;
 }
 
+void free_ion_on_q_bufs(struct vb2_queue *vq)
+{
+	struct vcap_client_data *c_data = vb2_get_drv_priv(vq);
+	struct vb2_buffer *vb;
+
+	if (!vq->streaming) {
+		list_for_each_entry(vb, &vq->queued_list, queued_entry)
+			free_ion_handle_work(c_data, vb);
+	}
+}
+
 /* VC Videobuf operations */
+static void wait_prepare(struct vb2_queue *q)
+{
+	struct vcap_client_data *c_data = vb2_get_drv_priv(q);
+	mutex_unlock(&c_data->mutex);
+}
+
+static void wait_finish(struct vb2_queue *q)
+{
+	struct vcap_client_data *c_data = vb2_get_drv_priv(q);
+	mutex_lock(&c_data->mutex);
+}
 
 static int capture_queue_setup(struct vb2_queue *vq,
 			       const struct v4l2_format *fmt,
@@ -627,7 +658,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;
 }
 
@@ -642,6 +673,8 @@
 
 static struct vb2_ops capture_video_qops = {
 	.queue_setup		= capture_queue_setup,
+	.wait_finish		= wait_finish,
+	.wait_prepare		= wait_prepare,
 	.buf_init			= capture_buffer_init,
 	.buf_prepare		= capture_buffer_prepare,
 	.buf_queue			= capture_buffer_queue,
@@ -725,7 +758,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;
 }
 
@@ -740,6 +773,8 @@
 
 static struct vb2_ops vp_in_video_qops = {
 	.queue_setup		= vp_in_queue_setup,
+	.wait_finish		= wait_finish,
+	.wait_prepare		= wait_prepare,
 	.buf_init			= vp_in_buffer_init,
 	.buf_prepare		= vp_in_buffer_prepare,
 	.buf_queue			= vp_in_buffer_queue,
@@ -823,7 +858,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 +873,8 @@
 
 static struct vb2_ops vp_out_video_qops = {
 	.queue_setup		= vp_out_queue_setup,
+	.wait_finish		= wait_finish,
+	.wait_prepare		= wait_prepare,
 	.buf_init			= vp_out_buffer_init,
 	.buf_prepare		= vp_out_buffer_prepare,
 	.buf_queue			= vp_out_buffer_queue,
@@ -896,10 +933,15 @@
 	case VC_TYPE:
 		vc_format = (struct v4l2_format_vc_ext *) &priv_fmt->u.timing;
 		c_data->vc_format = *vc_format;
+		c_data->stride = priv_fmt->stride;
 
 		size = (c_data->vc_format.hactive_end -
 			c_data->vc_format.hactive_start);
-		size = VCAP_STRIDE_CALC(size);
+		if (c_data->stride == VC_STRIDE_32)
+			size = VCAP_STRIDE_CALC(size, VCAP_STRIDE_ALIGN_32);
+		else
+			size = VCAP_STRIDE_CALC(size, VCAP_STRIDE_ALIGN_16);
+
 
 		if (c_data->vc_format.color_space)
 			size *= 3;
@@ -1056,16 +1098,18 @@
 				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;
 		}
 		rc = get_phys_addr(c_data->dev, &c_data->vc_vidq, p);
 		if (rc < 0)
 			return rc;
+		mutex_lock(&c_data->mutex);
 		rc = vb2_qbuf(&c_data->vc_vidq, p);
+		mutex_unlock(&c_data->mutex);
 		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)
@@ -1073,17 +1117,21 @@
 		rc = get_phys_addr(c_data->dev, &c_data->vp_in_vidq, p);
 		if (rc < 0)
 			return rc;
+		mutex_lock(&c_data->mutex);
 		rc = vb2_qbuf(&c_data->vp_in_vidq, p);
+		mutex_unlock(&c_data->mutex);
 		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);
 		if (rc < 0)
 			return rc;
+		mutex_lock(&c_data->mutex);
 		rc = vb2_qbuf(&c_data->vp_out_vidq, p);
+		mutex_unlock(&c_data->mutex);
 		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__);
@@ -1097,29 +1145,38 @@
 	struct vcap_client_data *c_data = to_client_data(file->private_data);
 	int rc;
 
+	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)
 			return -EINVAL;
+		mutex_lock(&c_data->mutex);
 		rc = vb2_dqbuf(&c_data->vc_vidq, p, file->f_flags & O_NONBLOCK);
+		mutex_unlock(&c_data->mutex);
 		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;
+		mutex_lock(&c_data->mutex);
 		rc = vb2_dqbuf(&c_data->vp_in_vidq, p, file->f_flags &
 				O_NONBLOCK);
+		mutex_unlock(&c_data->mutex);
 		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:
+		mutex_lock(&c_data->mutex);
 		rc = vb2_dqbuf(&c_data->vp_out_vidq, p, file->f_flags &
 				O_NONBLOCK);
+		mutex_unlock(&c_data->mutex);
 		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;
@@ -1476,8 +1533,10 @@
 		dev->vc_resource = 0;
 		mutex_unlock(&dev->dev_mutex);
 		c_data->streaming = 0;
+		mutex_lock(&c_data->mutex);
 		rc = vb2_streamoff(&c_data->vc_vidq,
 				V4L2_BUF_TYPE_VIDEO_CAPTURE);
+		mutex_unlock(&c_data->mutex);
 		if (rc >= 0)
 			atomic_set(&c_data->dev->vc_enabled, 0);
 		return rc;
@@ -1503,14 +1562,18 @@
 			return rc;
 		c_data->streaming = 0;
 
+		mutex_unlock(&dev->dev_mutex);
 		/* These stream on calls should not fail */
 		rc = vb2_streamoff(&c_data->vp_in_vidq,
 				V4L2_BUF_TYPE_INTERLACED_IN_DECODER);
-		if (rc < 0)
+		if (rc < 0) {
+			mutex_unlock(&c_data->mutex);
 			return rc;
+		}
 
 		rc = vb2_streamoff(&c_data->vp_out_vidq,
 				V4L2_BUF_TYPE_VIDEO_OUTPUT);
+		mutex_unlock(&c_data->mutex);
 		if (rc < 0)
 			return rc;
 
@@ -1545,20 +1608,26 @@
 		if (rc < 0)
 			return rc;
 
-		/* These stream on calls should not fail */
 		c_data->streaming = 0;
+		mutex_lock(&c_data->mutex);
+		/* These stream on calls should not fail */
 		rc = vb2_streamoff(&c_data->vc_vidq,
 				V4L2_BUF_TYPE_VIDEO_CAPTURE);
-		if (rc < 0)
+		if (rc < 0) {
+			mutex_unlock(&c_data->mutex);
 			return rc;
+		}
 
 		rc = vb2_streamoff(&c_data->vp_in_vidq,
 				V4L2_BUF_TYPE_INTERLACED_IN_DECODER);
-		if (rc < 0)
+		if (rc < 0) {
+			mutex_unlock(&c_data->mutex);
 			return rc;
+		}
 
 		rc = vb2_streamoff(&c_data->vp_out_vidq,
 				V4L2_BUF_TYPE_VIDEO_OUTPUT);
+		mutex_unlock(&c_data->mutex);
 		if (rc < 0)
 			return rc;
 
@@ -1704,6 +1773,7 @@
 	c_data->dev = dev;
 
 	spin_lock_init(&c_data->cap_slock);
+	mutex_init(&c_data->mutex);
 
 	/* initialize vc queue */
 	q = &c_data->vc_vidq;
@@ -1787,6 +1857,7 @@
 vp_in_q_failed:
 	vb2_queue_release(&c_data->vc_vidq);
 vc_q_failed:
+	mutex_destroy(&c_data->mutex);
 	kfree(c_data);
 	return ret;
 }
@@ -1814,6 +1885,10 @@
 	}
 	v4l2_fh_del(&c_data->vfh);
 	v4l2_fh_exit(&c_data->vfh);
+	free_ion_on_q_bufs(&c_data->vp_out_vidq);
+	free_ion_on_q_bufs(&c_data->vp_in_vidq);
+	free_ion_on_q_bufs(&c_data->vc_vidq);
+
 	vb2_queue_release(&c_data->vp_out_vidq);
 	vb2_queue_release(&c_data->vp_in_vidq);
 	vb2_queue_release(&c_data->vc_vidq);
@@ -1821,6 +1896,7 @@
 		c_data->dev->vc_client = NULL;
 	if (c_data->dev->vp_client == c_data)
 		c_data->dev->vp_client = NULL;
+	mutex_destroy(&c_data->mutex);
 	kfree(c_data);
 	return 0;
 }
@@ -1860,6 +1936,9 @@
 	struct vb2_queue *q;
 	unsigned int mask = 0;
 
+	if (c_data->streaming == 0)
+		return 0;
+
 	pr_debug("%s: Enter slect/poll\n", __func__);
 
 	switch (c_data->op_mode) {
@@ -2252,7 +2331,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;
diff --git a/drivers/media/video/vcap_vc.c b/drivers/media/video/vcap_vc.c
index 92b205e..642074f 100644
--- a/drivers/media/video/vcap_vc.c
+++ b/drivers/media/video/vcap_vc.c
@@ -37,7 +37,10 @@
 	} else {
 		int size = (c_data->vc_format.hactive_end -
 				c_data->vc_format.hactive_start);
-		size = VCAP_STRIDE_CALC(size);
+		if (c_data->stride == VC_STRIDE_32)
+			size = VCAP_STRIDE_CALC(size, VCAP_STRIDE_ALIGN_32);
+		else
+			size = VCAP_STRIDE_CALC(size, VCAP_STRIDE_ALIGN_16);
 		size *= (c_data->vc_format.vactive_end -
 				c_data->vc_format.vactive_start);
 		writel_relaxed(buf->paddr, y_addr);
@@ -126,28 +129,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);
-
-	pr_debug("%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 +
@@ -166,6 +150,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 +
@@ -178,33 +168,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 +
@@ -213,12 +196,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 +=
@@ -234,51 +222,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;
-
-			atomic_inc(&dev->dbg_p.vc_drop_count);
+			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;
 }
 
@@ -348,6 +427,8 @@
 	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);
@@ -425,11 +506,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) |
@@ -450,7 +530,10 @@
 	writel_iowmb(0x000033FF, VCAP_VC_BUF_CTRL);
 
 	rc = vc_format->hactive_end - vc_format->hactive_start;
-	rc = VCAP_STRIDE_CALC(rc);
+	if (c_data->stride == VC_STRIDE_32)
+		rc = VCAP_STRIDE_CALC(rc, VCAP_STRIDE_ALIGN_32);
+	else
+		rc = VCAP_STRIDE_CALC(rc, VCAP_STRIDE_ALIGN_16);
 	if (vc_format->color_space)
 		rc *= 3;
 
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 57813f5..5161b7b 100644
--- a/drivers/media/video/vcap_vp.c
+++ b/drivers/media/video/vcap_vp.c
@@ -267,7 +267,7 @@
 	}
 
 	/* 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);
@@ -396,6 +396,7 @@
 		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);
+			atomic_set(&dev->vp_enabled, 0);
 			pr_err("%s: VP Timeout and VP still running\n",
 				__func__);
 		}
@@ -832,7 +833,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);
@@ -875,7 +876,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 */
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 48bc92d..4996279 100644
--- a/drivers/mfd/pm8038-core.c
+++ b/drivers/mfd/pm8038-core.c
@@ -373,6 +373,11 @@
 	.num_resources	= ARRAY_SIZE(ccadc_cell_resources),
 };
 
+static struct mfd_cell vibrator_cell __devinitdata = {
+	.name           = PM8XXX_VIBRATOR_DEV_NAME,
+	.id             = -1,
+};
+
 static struct pm8xxx_vreg regulator_data[] = {
 	/*   name	     pc_name	    ctrl   test   hpm_min */
 	NLDO1200("8038_l1",		    0x0AE, 0x0AF, LDO_1200),
@@ -609,6 +614,17 @@
 		}
 	}
 
+	if (pdata->vibrator_pdata) {
+		vibrator_cell.platform_data = pdata->vibrator_pdata;
+		vibrator_cell.pdata_size =
+			sizeof(struct pm8xxx_vibrator_platform_data);
+		ret = mfd_add_devices(pmic->dev, 0, &vibrator_cell, 1, NULL, 0);
+		if (ret) {
+			pr_err("Failed to add vibrator ret=%d\n", ret);
+			goto bail;
+		}
+	}
+
 	if (pdata->spk_pdata) {
 		spk_cell.platform_data = pdata->spk_pdata;
 		spk_cell.pdata_size = sizeof(struct pm8xxx_spk_platform_data);
diff --git a/drivers/mfd/pm8821-core.c b/drivers/mfd/pm8821-core.c
index 86bd5ec..fe3e67e 100644
--- a/drivers/mfd/pm8821-core.c
+++ b/drivers/mfd/pm8821-core.c
@@ -312,11 +312,11 @@
 	drvdata = platform_get_drvdata(pdev);
 	if (drvdata)
 		pmic = drvdata->pm_chip_data;
-	if (pmic)
-		mfd_remove_devices(pmic->dev);
-	if (pmic->irq_chip) {
-		pm8821_irq_exit(pmic->irq_chip);
-		pmic->irq_chip = NULL;
+	if (pmic) {
+		if (pmic->dev)
+			mfd_remove_devices(pmic->dev);
+		if (pmic->irq_chip)
+			pm8821_irq_exit(pmic->irq_chip);
 	}
 	platform_set_drvdata(pdev, NULL);
 	kfree(pmic);
diff --git a/drivers/mfd/pm8xxx-misc.c b/drivers/mfd/pm8xxx-misc.c
index 6bb1441..fb57bd0 100644
--- a/drivers/mfd/pm8xxx-misc.c
+++ b/drivers/mfd/pm8xxx-misc.c
@@ -671,8 +671,7 @@
 	voltage = chg_config->voltage;
 	resistor = chg_config->resistor;
 
-	if (resistor < PM8XXX_COINCELL_RESISTOR_2100_OHMS ||
-			resistor > PM8XXX_COINCELL_RESISTOR_800_OHMS) {
+	if (resistor > PM8XXX_COINCELL_RESISTOR_800_OHMS) {
 		pr_err("Invalid resistor value provided\n");
 		return -EINVAL;
 	}
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 62f1a93..fa7c116 100644
--- a/drivers/mfd/wcd9xxx-core.c
+++ b/drivers/mfd/wcd9xxx-core.c
@@ -35,6 +35,8 @@
 #define MAX_WCD9XXX_DEVICE	4
 #define TABLA_I2C_MODE	0x03
 #define SITAR_I2C_MODE	0x01
+#define CODEC_DT_MAX_PROP_SIZE   40
+#define WCD9XXX_I2C_GSBI_SLAVE_ID "3-000d"
 
 struct wcd9xxx_i2c {
 	struct i2c_client *client;
@@ -43,6 +45,17 @@
 	int mod_id;
 };
 
+static char *taiko_supplies[] = {
+	"cdc-vdd-buck", "cdc-vdd-tx-h", "cdc-vdd-rx-h", "cdc-vddpx-1",
+	"cdc-vdd-a-1p2v", "cdc-vddcx-1", "cdc-vddcx-2",
+};
+
+static int wcd9xxx_dt_parse_vreg_info(struct device *dev,
+	struct wcd9xxx_regulator *vreg, const char *vreg_name);
+static int wcd9xxx_dt_parse_micbias_info(struct device *dev,
+	struct wcd9xxx_micbias_setting *micbias);
+static struct wcd9xxx_pdata *wcd9xxx_populate_dt_pdata(struct device *dev);
+
 struct wcd9xxx_i2c wcd9xxx_modules[MAX_WCD9XXX_DEVICE];
 static int wcd9xxx_intf = -1;
 
@@ -550,6 +563,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;
@@ -757,19 +777,31 @@
 	int ret = 0;
 	int i2c_mode = 0;
 	static int device_id;
+	struct device *dev;
 
 	pr_info("%s\n", __func__);
 	if (wcd9xxx_intf == WCD9XXX_INTERFACE_TYPE_SLIMBUS) {
-		pr_info("tabla card is already detected in slimbus mode\n");
+		dev_dbg(&client->dev, "%s:Codec is detected in slimbus mode\n",
+			 __func__);
 		return -ENODEV;
 	}
-	pdata = client->dev.platform_data;
 	if (device_id > 0) {
 		wcd9xxx_modules[device_id++].client = client;
-		pr_info("probe for other slaves devices of tabla\n");
+		dev_dbg(&client->dev, "%s:probe for other slaves\n"
+			"devices of codec\n", __func__);
 		return ret;
 	}
-
+	dev = &client->dev;
+	if (client->dev.of_node) {
+		dev_dbg(&client->dev, "%s:Platform data from device tree\n",
+			__func__);
+		pdata = wcd9xxx_populate_dt_pdata(&client->dev);
+		client->dev.platform_data = pdata;
+	} else {
+		dev_dbg(&client->dev, "%s:Platform data from board file\n",
+			__func__);
+		pdata = client->dev.platform_data;
+	}
 	wcd9xxx = kzalloc(sizeof(struct wcd9xxx), GFP_KERNEL);
 	if (wcd9xxx == NULL) {
 		pr_err("%s: error, allocation failed\n", __func__);
@@ -851,7 +883,6 @@
 	return 0;
 }
 
-#define CODEC_DT_MAX_PROP_SIZE   40
 static int wcd9xxx_dt_parse_vreg_info(struct device *dev,
 	struct wcd9xxx_regulator *vreg, const char *vreg_name)
 {
@@ -988,6 +1019,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);
@@ -998,6 +1043,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;
 }
 
@@ -1031,11 +1081,6 @@
 	return 0;
 }
 
-static char *taiko_supplies[] = {
-	"cdc-vdd-buck", "cdc-vdd-tx-h", "cdc-vdd-rx-h", "cdc-vddpx-1",
-	"cdc-vdd-a-1p2v", "cdc-vddcx-1", "cdc-vddcx-2",
-};
-
 static struct wcd9xxx_pdata *wcd9xxx_populate_dt_pdata(struct device *dev)
 {
 	struct wcd9xxx_pdata *pdata;
@@ -1045,12 +1090,11 @@
 
 	pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
 	if (!pdata) {
-		dev_err(dev,
-			"could not allocate memory for platform data\n");
+		dev_err(dev, "could not allocate memory for platform data\n");
 		return NULL;
 	}
-
-	if (!strcmp(dev_name(dev), "taiko-slim-pgd")) {
+	if (!strcmp(dev_name(dev), "taiko-slim-pgd") ||
+		(!strcmp(dev_name(dev), WCD9XXX_I2C_GSBI_SLAVE_ID))) {
 		codec_supplies = taiko_supplies;
 		num_of_supplies = ARRAY_SIZE(taiko_supplies);
 	} else {
@@ -1085,11 +1129,7 @@
 			pdata->reset_gpio);
 		goto err;
 	}
-
-	ret = wcd9xxx_dt_parse_slim_interface_dev_info(dev,
-			&pdata->slimbus_slave_device);
-	if (ret)
-		goto err;
+	dev_dbg(dev, "%s: reset gpio %d", __func__, pdata->reset_gpio);
 	return pdata;
 err:
 	devm_kfree(dev, pdata);
@@ -1125,6 +1165,14 @@
 	if (slim->dev.of_node) {
 		dev_info(&slim->dev, "Platform data from device tree\n");
 		pdata = wcd9xxx_populate_dt_pdata(&slim->dev);
+		ret = wcd9xxx_dt_parse_slim_interface_dev_info(&slim->dev,
+				&pdata->slimbus_slave_device);
+		if (ret) {
+			dev_err(&slim->dev, "Error, parsing slim interface\n");
+			devm_kfree(&slim->dev, pdata);
+			ret = -EINVAL;
+			goto err;
+		}
 		slim->dev.platform_data = pdata;
 
 	} else {
@@ -1434,6 +1482,14 @@
 #define WCD9XXX_I2C_DIGITAL_1	2
 #define WCD9XXX_I2C_DIGITAL_2	3
 
+static struct i2c_device_id wcd9xxx_id_table[] = {
+	{"wcd9xxx-i2c", WCD9XXX_I2C_TOP_LEVEL},
+	{"wcd9xxx-i2c", WCD9XXX_I2C_ANALOG},
+	{"wcd9xxx-i2c", WCD9XXX_I2C_DIGITAL_1},
+	{"wcd9xxx-i2c", WCD9XXX_I2C_DIGITAL_2},
+	{}
+};
+
 static struct i2c_device_id tabla_id_table[] = {
 	{"tabla top level", WCD9XXX_I2C_TOP_LEVEL},
 	{"tabla analog", WCD9XXX_I2C_ANALOG},
@@ -1455,9 +1511,22 @@
 	.suspend = wcd9xxx_i2c_suspend,
 };
 
+static struct i2c_driver wcd9xxx_i2c_driver = {
+	.driver                 = {
+		.owner          =       THIS_MODULE,
+		.name           =       "wcd9xxx-i2c-core",
+	},
+	.id_table               =       wcd9xxx_id_table,
+	.probe                  =       wcd9xxx_i2c_probe,
+	.remove                 =       __devexit_p(wcd9xxx_i2c_remove),
+	.resume	= wcd9xxx_i2c_resume,
+	.suspend = wcd9xxx_i2c_suspend,
+};
+
+
 static int __init wcd9xxx_init(void)
 {
-	int ret1, ret2, ret3, ret4, ret5, ret6;
+	int ret1, ret2, ret3, ret4, ret5, ret6, ret7;
 
 	ret1 = slim_driver_register(&tabla_slim_driver);
 	if (ret1 != 0)
@@ -1469,7 +1538,7 @@
 
 	ret3 = i2c_add_driver(&tabla_i2c_driver);
 	if (ret3 != 0)
-		pr_err("failed to add the I2C driver\n");
+		pr_err("failed to add the tabla2x I2C driver\n");
 
 	ret4 = slim_driver_register(&sitar_slim_driver);
 	if (ret4 != 0)
@@ -1483,7 +1552,11 @@
 	if (ret6 != 0)
 		pr_err("Failed to register taiko SB driver: %d\n", ret6);
 
-	return (ret1 && ret2 && ret3 && ret4 && ret5 && ret6) ? -1 : 0;
+	ret7 = i2c_add_driver(&wcd9xxx_i2c_driver);
+	if (ret7 != 0)
+		pr_err("failed to add the wcd9xxx I2C driver\n");
+
+	return (ret1 && ret2 && ret3 && ret4 && ret5 && ret6 && ret7) ? -1 : 0;
 }
 module_init(wcd9xxx_init);
 
diff --git a/drivers/mfd/wcd9xxx-irq.c b/drivers/mfd/wcd9xxx-irq.c
index 103c1a3..23e0fcc 100644
--- a/drivers/mfd/wcd9xxx-irq.c
+++ b/drivers/mfd/wcd9xxx-irq.c
@@ -56,6 +56,12 @@
 	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.
@@ -218,6 +224,9 @@
 	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;
 	}
diff --git a/drivers/mfd/wcd9xxx-slimslave.c b/drivers/mfd/wcd9xxx-slimslave.c
index 6e6de37..948cb6e 100644
--- a/drivers/mfd/wcd9xxx-slimslave.c
+++ b/drivers/mfd/wcd9xxx-slimslave.c
@@ -482,3 +482,56 @@
 	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..93a3237 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
@@ -653,6 +664,17 @@
 	  This adds support for connecting devices like mouse in HSIC
 	  Host mode.
 
+config TI_DRV2667
+	tristate "TI's DRV2667 haptic controller support"
+	depends on I2C
+	help
+	  The DRV2667 is a piezo haptic controller chip. It can drive
+	  piezo haptics either in digital mode or analog mode. This chip
+	  can be used in variety of devices to provide haptic support.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called ti_drv2667.
+
 source "drivers/misc/c2port/Kconfig"
 source "drivers/misc/eeprom/Kconfig"
 source "drivers/misc/cb710/Kconfig"
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index be3d0a0..8395ef4 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
@@ -71,3 +72,4 @@
 obj-$(CONFIG_PMIC8058_XOADC) += pmic8058-xoadc.o
 obj-$(CONFIG_QSEECOM) += qseecom.o
 obj-$(CONFIG_QFP_FUSE) += qfp_fuse.o
+obj-$(CONFIG_TI_DRV2667) += ti_drv2667.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 12f896e..3715417 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,19 +32,33 @@
 #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 <linux/scatterlist.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 QSEE_VERSION_01			0x401000
+#define QSEE_VERSION_02			0x402000
+
+
+#define QSEOS_CHECK_VERSION_CMD		0x00001803
+
+#define QSEE_CE_CLK_100MHZ		100000000
+#define QSEE_CE_CLK_50MHZ		50000000
+
+#define QSEECOM_MAX_SG_ENTRY	10
 
 enum qseecom_command_scm_resp_type {
 	QSEOS_APP_ID = 0xEE01,
@@ -61,6 +75,10 @@
 	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_APP_REGION_NOTIFICATION,
 	QSEOS_CMD_MAX     = 0xEFFFFFFF
 };
 
@@ -75,6 +93,12 @@
 	CLK_SFPB,
 };
 
+__packed  struct qsee_apps_region_info_ireq {
+	uint32_t qsee_cmd_id;
+	uint32_t addr;
+	uint32_t size;
+};
+
 __packed struct qseecom_check_app_ireq {
 	uint32_t qsee_cmd_id;
 	char     app_name[MAX_APP_NAME_SIZE];
@@ -93,6 +117,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;
@@ -168,6 +203,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;
@@ -176,11 +216,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 {
@@ -215,11 +260,14 @@
 struct clk *ce_core_src_clk;
 struct clk *ce_bus_clk;
 
+struct qseecom_sg_entry {
+	uint32_t phys_addr;
+	uint32_t len;
+};
+
 /* 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)
@@ -656,90 +704,112 @@
 	ret = qsee_vote_for_clock(CLK_SFPB);
 	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",
+	ret = __qseecom_check_app_exists(req);
+	if (ret < 0)
+		return ret;
+	else
+		app_id = ret;
+
+	if (app_id) {
+		pr_warn("App id %d (%s) already exists\n", app_id,
 			(char *)(req.app_name));
-	/* Get the handle of the shared fd */
-	ihandle = ion_import_dma_buf(qseecom.ion_clnt,
+		spin_lock_irqsave(&qseecom.registered_app_list_lock, flags);
+		list_for_each_entry(entry,
+		&qseecom.registered_app_list_head, list){
+			if (entry->app_id == app_id) {
+				entry->ref_cnt++;
+				break;
+			}
+		}
+		spin_unlock_irqrestore(
+		&qseecom.registered_app_list_lock, flags);
+	} else {
+		pr_warn("App (%s) does'nt exist, loading apps for first time\n",
+			(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);
-	if (IS_ERR_OR_NULL(ihandle)) {
-		pr_err("Ion client could not retrieve the handle\n");
-		qsee_disable_clock_vote(CLK_SFPB);
-		return -ENOMEM;
-	}
+		if (IS_ERR_OR_NULL(ihandle)) {
+			pr_err("Ion client could not retrieve the handle\n");
+			qsee_disable_clock_vote(CLK_SFPB);
+			return -ENOMEM;
+		}
 
-	/* Get the physical address of the ION BUF */
-	ret = ion_phys(qseecom.ion_clnt, ihandle, &pa, &len);
+		/* Get the physical address of the ION BUF */
+		ret = ion_phys(qseecom.ion_clnt, ihandle, &pa, &len);
 
-	/* Populate the structure for sending scm call to load image */
-	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;
-	load_req.phy_addr = pa;
+		/* 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;
+		load_req.phy_addr = pa;
 
-	/*  SCM_CALL  to load the app and get the app_id back */
-	ret = scm_call(SCM_SVC_TZSCHEDULER, 1,  &load_req,
+		/*  SCM_CALL  to load the app and get the app_id back */
+		ret = scm_call(SCM_SVC_TZSCHEDULER, 1,  &load_req,
 			sizeof(struct qseecom_load_app_ireq),
 			&resp, sizeof(resp));
-	if (ret) {
-		pr_err("scm_call to load app failed\n");
-		return -EINVAL;
-	}
-
-	if (resp.result == QSEOS_RESULT_FAILURE) {
-		pr_err("scm_call rsp.result is QSEOS_RESULT_FAILURE\n");
-		if (!IS_ERR_OR_NULL(ihandle))
-			ion_free(qseecom.ion_clnt, ihandle);
-		qsee_disable_clock_vote(CLK_SFPB);
-		return -EFAULT;
-	}
-
-	if (resp.result == QSEOS_RESULT_INCOMPLETE) {
-		ret = __qseecom_process_incomplete_cmd(data, &resp);
 		if (ret) {
-			pr_err("process_incomplete_cmd failed err: %d\n",
-					ret);
+			pr_err("scm_call to load app failed\n");
+			return -EINVAL;
+		}
+
+		if (resp.result == QSEOS_RESULT_FAILURE) {
+			pr_err("scm_call rsp.result is QSEOS_RESULT_FAILURE\n");
 			if (!IS_ERR_OR_NULL(ihandle))
 				ion_free(qseecom.ion_clnt, ihandle);
 			qsee_disable_clock_vote(CLK_SFPB);
-			return ret;
+			return -EFAULT;
 		}
-	}
 
-	if (resp.result != QSEOS_RESULT_SUCCESS) {
-		pr_err("scm_call failed resp.result unknown, %d\n",
+		if (resp.result == QSEOS_RESULT_INCOMPLETE) {
+			ret = __qseecom_process_incomplete_cmd(data, &resp);
+			if (ret) {
+				pr_err("process_incomplete_cmd failed err: %d\n",
+					ret);
+				if (!IS_ERR_OR_NULL(ihandle))
+					ion_free(qseecom.ion_clnt, ihandle);
+				qsee_disable_clock_vote(CLK_SFPB);
+				return ret;
+			}
+		}
+
+		if (resp.result != QSEOS_RESULT_SUCCESS) {
+			pr_err("scm_call failed resp.result unknown, %d\n",
 				resp.result);
+			if (!IS_ERR_OR_NULL(ihandle))
+				ion_free(qseecom.ion_clnt, ihandle);
+			qsee_disable_clock_vote(CLK_SFPB);
+			return -EFAULT;
+		}
+
+		app_id = resp.data;
+
+		entry = kmalloc(sizeof(*entry), GFP_KERNEL);
+		if (!entry) {
+			pr_err("kmalloc failed\n");
+			qsee_disable_clock_vote(CLK_SFPB);
+			return -ENOMEM;
+		}
+		entry->app_id = app_id;
+		entry->ref_cnt = 1;
+
+		/* Deallocate the handle */
 		if (!IS_ERR_OR_NULL(ihandle))
 			ion_free(qseecom.ion_clnt, ihandle);
-		qsee_disable_clock_vote(CLK_SFPB);
-		return -EFAULT;
+
+		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);
+
+		pr_warn("App with id %d (%s) now loaded\n", app_id,
+		(char *)(load_img_req.img_name));
 	}
-
-	app_id = resp.data;
-
-	entry = kmalloc(sizeof(*entry), GFP_KERNEL);
-	if (!entry) {
-		pr_err("kmalloc failed\n");
-		qsee_disable_clock_vote(CLK_SFPB);
-		return -ENOMEM;
-	}
-	entry->app_id = app_id;
-	entry->ref_cnt = 1;
-
-	/* Deallocate the handle */
-	if (!IS_ERR_OR_NULL(ihandle))
-		ion_free(qseecom.ion_clnt, ihandle);
-
-	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);
-
-	pr_warn("App with id %d (%s) now loaded\n", app_id,
-		(char *)(req.app_name));
-
 	data->client.app_id = app_id;
 	load_img_req.app_id = app_id;
 	if (copy_to_user(argp, &load_img_req, sizeof(load_img_req))) {
@@ -1066,13 +1136,11 @@
 {
 	struct ion_handle *ihandle;
 	char *field;
-	uint32_t *update;
-	ion_phys_addr_t pa;
 	int ret = 0;
 	int i = 0;
-	uint32_t length;
 
 	for (i = 0; i < MAX_ION_FD; i++) {
+		struct sg_table *sg_ptr = NULL;
 		if (req->ifd_data[i].fd > 0) {
 			/* Get the handle of the shared fd */
 			ihandle = ion_import_dma_buf(qseecom.ion_clnt,
@@ -1083,20 +1151,51 @@
 			}
 			field = (char *) req->cmd_req_buf +
 						req->ifd_data[i].cmd_buf_offset;
-			update = (uint32_t *) field;
 
 			/* Populate the cmd data structure with the phys_addr */
-			ret = ion_phys(qseecom.ion_clnt, ihandle, &pa, &length);
-			if (ret)
-				return -ENOMEM;
-
-			*update = (uint32_t)pa;
+			sg_ptr = ion_sg_table(qseecom.ion_clnt, ihandle);
+			if (sg_ptr == NULL) {
+				pr_err("IOn client could not retrieve sg table\n");
+				goto err;
+			}
+			if (sg_ptr->nents == 0) {
+				pr_err("Num of scattered entries is 0\n");
+				goto err;
+			}
+			if (sg_ptr->nents > QSEECOM_MAX_SG_ENTRY) {
+				pr_err("Num of scattered entries");
+				pr_err(" (%d) is greater than max supported %d\n",
+					sg_ptr->nents, QSEECOM_MAX_SG_ENTRY);
+				goto err;
+			}
+			if (sg_ptr->nents == 1) {
+				uint32_t *update;
+				update = (uint32_t *) field;
+				*update = (uint32_t)sg_dma_address(sg_ptr->sgl);
+			} else {
+				struct qseecom_sg_entry *update;
+				struct scatterlist *sg;
+				int j = 0;
+				update = (struct qseecom_sg_entry *) field;
+				sg = sg_ptr->sgl;
+				for (j = 0; j < sg_ptr->nents; j++) {
+					update->phys_addr = (uint32_t)
+						sg_dma_address(sg);
+					update->len = (uint32_t)sg->length;
+					update++;
+					sg = sg_next(sg);
+				}
+			}
 			/* Deallocate the handle */
 			if (!IS_ERR_OR_NULL(ihandle))
 				ion_free(qseecom.ion_clnt, ihandle);
 		}
 	}
 	return ret;
+err:
+	if (!IS_ERR_OR_NULL(ihandle))
+		ion_free(qseecom.ion_clnt, ihandle);
+	return -ENOMEM;
 }
 
 static int qseecom_send_modfd_cmd(struct qseecom_dev_handle *data,
@@ -1171,6 +1270,514 @@
 	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);
+	ret = qsee_vote_for_clock(CLK_SFPB);
+	if (ret) {
+		kzfree(img_data);
+		pr_warning("Unable to vote for SFPB clock");
+		return -EIO;
+	}
+
+	/* 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);
+		qsee_disable_clock_vote(CLK_SFPB);
+		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;
+	}
+	qsee_disable_clock_vote(CLK_SFPB);
+
+	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;
+
+	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;
+
+	*handle = kzalloc(sizeof(struct qseecom_handle), GFP_KERNEL);
+	if (!(*handle)) {
+		pr_err("failed to allocate memory for kernel client handle\n");
+		return -ENOMEM;
+	}
+
+	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);
+
+int qseecom_set_bandwidth(struct qseecom_handle *handle, bool high)
+{
+	if ((handle == NULL) || (handle->dev == NULL)) {
+		pr_err("No valid kernel client\n");
+		return -EINVAL;
+	}
+	if (high)
+		return qsee_vote_for_clock(CLK_DFAB);
+	else {
+		qsee_disable_clock_vote(CLK_DFAB);
+		return 0;
+	}
+}
+EXPORT_SYMBOL(qseecom_set_bandwidth);
+
 static int qseecom_send_resp(void)
 {
 	qseecom.send_resp_flag = 1;
@@ -1209,9 +1816,13 @@
 			if (qsee_sfpb_bw_count > 0)
 				ret = msm_bus_scale_client_update_request(
 						qsee_perf_client, 3);
-			else
+			else {
+				if (ce_core_src_clk != NULL)
+					clk_set_rate(ce_core_src_clk,
+							QSEE_CE_CLK_100MHZ);
 				ret = msm_bus_scale_client_update_request(
 						qsee_perf_client, 1);
+			}
 			if (ret)
 				pr_err("DFAB Bandwidth req failed (%d)\n",
 								ret);
@@ -1228,9 +1839,13 @@
 			if (qsee_bw_count > 0)
 				ret = msm_bus_scale_client_update_request(
 						qsee_perf_client, 3);
-			else
+			else {
+				if (ce_core_src_clk != NULL)
+					clk_set_rate(ce_core_src_clk,
+							QSEE_CE_CLK_100MHZ);
 				ret = msm_bus_scale_client_update_request(
 						qsee_perf_client, 2);
+			}
 
 			if (ret)
 				pr_err("SFPB Bandwidth req failed (%d)\n",
@@ -1269,9 +1884,13 @@
 			if (qsee_sfpb_bw_count > 0)
 				ret = msm_bus_scale_client_update_request(
 						qsee_perf_client, 2);
-			else
+			else {
 				ret = msm_bus_scale_client_update_request(
 						qsee_perf_client, 0);
+				if (ce_core_src_clk != NULL)
+					clk_set_rate(ce_core_src_clk,
+							QSEE_CE_CLK_50MHZ);
+			}
 			if (ret)
 				pr_err("SFPB Bandwidth req fail (%d)\n",
 								ret);
@@ -1289,9 +1908,13 @@
 			if (qsee_bw_count > 0)
 				ret = msm_bus_scale_client_update_request(
 						qsee_perf_client, 1);
-			else
+			else {
 				ret = msm_bus_scale_client_update_request(
 						qsee_perf_client, 0);
+				if (ce_core_src_clk != NULL)
+					clk_set_rate(ce_core_src_clk,
+							QSEE_CE_CLK_50MHZ);
+			}
 			if (ret)
 				pr_err("SFPB Bandwidth req fail (%d)\n",
 								ret);
@@ -1588,7 +2211,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)
@@ -1693,7 +2324,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);
@@ -1728,7 +2359,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);
 	}
@@ -1744,7 +2375,47 @@
 		.release = qseecom_release
 };
 
-static int __qseecom_init_clk()
+static int __qseecom_enable_clk(void)
+{
+	int rc = 0;
+
+	/* Enable CE core clk */
+	rc = clk_prepare_enable(ce_core_clk);
+	if (rc) {
+		pr_err("Unable to enable/prepare CE core clk\n");
+		return -EIO;
+	} 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);
+			return -EIO;
+		} 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);
+				return -EIO;
+			}
+		}
+	}
+	return rc;
+}
+
+static void __qseecom_disable_clk(void)
+{
+	if (ce_clk != NULL)
+		clk_disable_unprepare(ce_clk);
+	if (ce_core_clk != NULL)
+		clk_disable_unprepare(ce_core_clk);
+	if (ce_bus_clk != NULL)
+		clk_disable_unprepare(ce_bus_clk);
+}
+
+static int __qseecom_init_clk(void)
 {
 	int rc = 0;
 	struct device *pdev;
@@ -1753,14 +2424,12 @@
 	/* 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);
+		/* Set the core src clk @50Mhz */
+		rc = clk_set_rate(ce_core_src_clk, QSEE_CE_CLK_50MHZ);
 		if (rc) {
 			clk_put(ce_core_src_clk);
 			pr_err("Unable to set the core src clk @100Mhz.\n");
-			goto err_clk;
+			return -EIO;
 		}
 	} else {
 		pr_warn("Unable to get CE core src clk, set to NULL\n");
@@ -1774,7 +2443,7 @@
 		pr_err("Unable to get CE core clk\n");
 		if (ce_core_src_clk != NULL)
 			clk_put(ce_core_src_clk);
-		goto err_clk;
+		return -EIO;
 	}
 
 	/* Get CE Interface clk */
@@ -1785,7 +2454,7 @@
 		if (ce_core_src_clk != NULL)
 			clk_put(ce_core_src_clk);
 		clk_put(ce_core_clk);
-		goto err_clk;
+		return -EIO;
 	}
 
 	/* Get CE AXI clk */
@@ -1797,85 +2466,48 @@
 			clk_put(ce_core_src_clk);
 		clk_put(ce_core_clk);
 		clk_put(ce_clk);
-		goto err_clk;
+		return -EIO;
 	}
-
-	/* 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()
+static void __qseecom_deinit_clk(void)
 {
-	clk_disable_unprepare(ce_clk);
-	clk_disable_unprepare(ce_core_clk);
-	clk_disable_unprepare(ce_bus_clk);
-	if (ce_core_src_clk != NULL)
+	if (ce_clk != NULL) {
+		clk_put(ce_clk);
+		ce_clk = NULL;
+	}
+	if (ce_core_clk != NULL) {
+		clk_put(ce_core_clk);
+		ce_clk = NULL;
+	}
+	if (ce_bus_clk != NULL) {
+		clk_put(ce_bus_clk);
+		ce_clk = NULL;
+	}
+	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);
+		ce_core_src_clk = NULL;
+	}
 }
 
 static int __devinit qseecom_probe(struct platform_device *pdev)
 {
 	int rc;
-	int ret;
+	int ret = 0;
 	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;
 	qsee_perf_client = 0;
+	qsee_sfpb_bw_count = 0;
+
+	ce_core_clk = NULL;
+	ce_clk = NULL;
+	ce_core_src_clk = NULL;
+	ce_bus_clk = NULL;
 
 	rc = alloc_chrdev_region(&qseecom_device_no, 0, 1, QSEECOM_DEV);
 	if (rc < 0) {
@@ -1911,26 +2543,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;
@@ -1942,8 +2586,39 @@
 		ret = __qseecom_init_clk();
 		if (ret)
 			goto err;
+		ret = __qseecom_enable_clk();
+		if (ret) {
+			__qseecom_deinit_clk();
+			goto err;
+		}
 		qseecom_platform_support = (struct msm_bus_scale_pdata *)
 						msm_bus_cl_get_pdata(pdev);
+		if (qseecom.qsee_version >= (QSEE_VERSION_02)) {
+			struct resource *resource = NULL;
+			struct qsee_apps_region_info_ireq req;
+			struct qseecom_command_scm_resp resp;
+
+			resource = platform_get_resource_byname(pdev,
+					IORESOURCE_MEM, "secapp-region");
+			if (resource) {
+				req.qsee_cmd_id = QSEOS_APP_REGION_NOTIFICATION;
+				req.addr = resource->start;
+				req.size = resource_size(resource);
+				pr_warn("secure app region addr=0x%x size=0x%x",
+							req.addr, req.size);
+			} else {
+				pr_err("Fail to get secure app region info\n");
+				rc = -EINVAL;
+				goto err;
+			}
+			rc = scm_call(SCM_SVC_TZSCHEDULER, 1, &req, sizeof(req),
+							&resp, sizeof(resp));
+			if (rc) {
+				pr_err("Failed to send secapp region info %d\n",
+									rc);
+				goto err;
+			}
+		}
 	} else {
 		qseecom_platform_support = (struct msm_bus_scale_pdata *)
 						pdev->dev.platform_data;
@@ -1966,9 +2641,70 @@
 
 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();
+
+	if (qsee_perf_client)
+		msm_bus_scale_client_update_request(qsee_perf_client, 0);
+	/* register client for bus scaling */
+	if (pdev->dev.of_node) {
+		__qseecom_disable_clk();
+		__qseecom_deinit_clk();
+	}
+	return ret;
 };
 
 static struct of_device_id qseecom_match[] = {
@@ -1995,9 +2731,6 @@
 
 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..0c93ef2
--- /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);
+int qseecom_set_bandwidth(struct qseecom_handle *handle, bool high);
+
+#endif /* __QSEECOM_KERNEL_H_ */
diff --git a/drivers/misc/ti_drv2667.c b/drivers/misc/ti_drv2667.c
new file mode 100644
index 0000000..554799c
--- /dev/null
+++ b/drivers/misc/ti_drv2667.c
@@ -0,0 +1,679 @@
+/* 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/gpio.h>
+#include <linux/i2c.h>
+#include <linux/pm.h>
+#include <linux/err.h>
+#include <linux/of.h>
+#include <linux/regulator/consumer.h>
+#include <linux/i2c/ti_drv2667.h>
+#include "../staging/android/timed_output.h"
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+#include <linux/earlysuspend.h>
+#define DRV2667_SUS_LEVEL	1
+#endif
+
+#define DRV2667_STATUS_REG	0x00
+#define DRV2667_CNTL1_REG	0x01
+#define DRV2667_CNTL2_REG	0x02
+#define DRV2667_WAV_SEQ3_REG	0x03
+#define DRV2667_FIFO_REG	0x0B
+#define DRV2667_PAGE_REG	0xFF
+
+#define DRV2667_STANDBY_MASK	0xBF
+#define DRV2667_INPUT_MUX_MASK	0x04
+#define DRV2667_GAIN_MASK	0xFC
+#define DRV2667_GAIN_SHIFT	0
+#define DRV2667_TIMEOUT_MASK	0xF3
+#define DRV2667_TIMEOUT_SHIFT	2
+#define DRV2667_GO_MASK		0x01
+#define DRV2667_FIFO_SIZE	100
+#define DRV2667_VIB_START_VAL	0x7F
+#define DRV2667_REG_PAGE_ID	0x00
+#define DRV2667_FIFO_CHUNK_MS	10
+#define DRV2667_BYTES_PER_MS	8
+
+#define DRV2667_WAV_SEQ_ID_IDX		1
+#define DRV2667_WAV_SEQ_REP_IDX		6
+#define DRV2667_WAV_SEQ_FREQ_IDX	8
+#define DRV2667_WAV_SEQ_FREQ_MIN	8
+#define DRV2667_WAV_SEQ_DUR_IDX		9
+
+#define DRV2667_MIN_IDLE_TIMEOUT_MS	5
+#define DRV2667_MAX_IDLE_TIMEOUT_MS	20
+
+#define DRV2667_VTG_MIN_UV	3000000
+#define DRV2667_VTG_MAX_UV	5500000
+#define DRV2667_VTG_CURR_UA	24000
+#define DRV2667_I2C_VTG_MIN_UV	1800000
+#define DRV2667_I2C_VTG_MAX_UV	1800000
+#define DRV2667_I2C_CURR_UA	9630
+
+/* supports 3 modes in digital - fifo, ram and wave */
+enum drv2667_modes {
+	FIFO_MODE = 0,
+	RAM_SEQ_MODE,
+	WAV_SEQ_MODE,
+	ANALOG_MODE,
+};
+
+struct drv2667_data {
+	struct i2c_client *client;
+	struct timed_output_dev dev;
+	struct hrtimer timer;
+	struct work_struct work;
+	struct mutex lock;
+	struct regulator *vdd;
+	struct regulator *vdd_i2c;
+	u32 max_runtime_ms;
+	u32 runtime_left;
+	u8 buf[DRV2667_FIFO_SIZE + 1];
+	u8 cntl2_val;
+	enum drv2667_modes mode;
+	u32 time_chunk_ms;
+#ifdef CONFIG_HAS_EARLYSUSPEND
+	struct early_suspend es;
+#endif
+};
+
+static int drv2667_read_reg(struct i2c_client *client, u32 reg)
+{
+	int rc;
+
+	rc = i2c_smbus_read_byte_data(client, reg);
+	if (rc < 0)
+		dev_err(&client->dev, "i2c reg read for 0x%x failed\n", reg);
+	return rc;
+}
+
+static int drv2667_write_reg(struct i2c_client *client, u32 reg, u8 val)
+{
+	int rc;
+
+	rc = i2c_smbus_write_byte_data(client, reg, val);
+	if (rc < 0)
+		dev_err(&client->dev, "i2c reg write for 0x%xfailed\n", reg);
+
+	return rc;
+}
+
+static void drv2667_dump_regs(struct drv2667_data *data, char *label)
+{
+	dev_dbg(&data->client->dev,
+		"%s: reg0x00 = 0x%x, reg0x01 = 0x%x reg0x02 = 0x%x", label,
+		drv2667_read_reg(data->client, DRV2667_STATUS_REG),
+		drv2667_read_reg(data->client, DRV2667_CNTL1_REG),
+		drv2667_read_reg(data->client, DRV2667_CNTL2_REG));
+}
+
+static void drv2667_worker(struct work_struct *work)
+{
+	struct drv2667_data *data;
+	int rc = 0;
+	u8 val;
+
+	data = container_of(work, struct drv2667_data, work);
+
+	if (data->mode == WAV_SEQ_MODE) {
+		if (data->runtime_left)
+			val = data->cntl2_val | DRV2667_GO_MASK;
+		else
+			val = data->cntl2_val & ~DRV2667_GO_MASK;
+		rc = drv2667_write_reg(data->client, DRV2667_CNTL2_REG, val);
+	} else if (data->mode == FIFO_MODE) {
+		/* data is played at 8khz */
+		if (data->runtime_left < data->time_chunk_ms)
+			val = data->runtime_left * DRV2667_BYTES_PER_MS;
+		else
+			val = data->time_chunk_ms * DRV2667_BYTES_PER_MS;
+
+		rc = i2c_master_send(data->client, data->buf, val + 1);
+	}
+
+	if (rc < 0)
+		dev_err(&data->client->dev, "i2c send message failed\n");
+}
+
+static void drv2667_enable(struct timed_output_dev *dev, int runtime)
+{
+	struct drv2667_data *data = container_of(dev, struct drv2667_data, dev);
+	unsigned long time_ms;
+
+	if (runtime > data->max_runtime_ms) {
+		dev_dbg(&data->client->dev, "Invalid runtime\n");
+		runtime = data->max_runtime_ms;
+	}
+
+	mutex_lock(&data->lock);
+	hrtimer_cancel(&data->timer);
+	data->runtime_left = runtime;
+	if (data->runtime_left < data->time_chunk_ms)
+		time_ms = runtime * NSEC_PER_MSEC;
+	else
+		time_ms = data->time_chunk_ms * NSEC_PER_MSEC;
+	hrtimer_start(&data->timer, ktime_set(0, time_ms), HRTIMER_MODE_REL);
+	schedule_work(&data->work);
+	mutex_unlock(&data->lock);
+}
+
+static int drv2667_get_time(struct timed_output_dev *dev)
+{
+	struct drv2667_data *data = container_of(dev, struct drv2667_data, dev);
+
+	if (hrtimer_active(&data->timer))
+		return	data->runtime_left +
+			ktime_to_ms(hrtimer_get_remaining(&data->timer));
+	return 0;
+}
+
+static enum hrtimer_restart drv2667_timer(struct hrtimer *timer)
+{
+	struct drv2667_data *data;
+	int time_ms;
+
+	data = container_of(timer, struct drv2667_data, timer);
+	if (data->runtime_left <= data->time_chunk_ms) {
+		data->runtime_left = 0;
+		schedule_work(&data->work);
+		return HRTIMER_NORESTART;
+	}
+
+	data->runtime_left -= data->time_chunk_ms;
+	if (data->runtime_left < data->time_chunk_ms)
+		time_ms = data->runtime_left * NSEC_PER_MSEC;
+	else
+		time_ms = data->time_chunk_ms * NSEC_PER_MSEC;
+
+	hrtimer_forward_now(&data->timer, ktime_set(0, time_ms));
+	schedule_work(&data->work);
+	return HRTIMER_RESTART;
+}
+
+static int drv2667_vreg_config(struct drv2667_data *data, bool on)
+{
+	int rc = 0;
+
+	if (!on)
+		goto deconfig_vreg;
+
+	data->vdd = regulator_get(&data->client->dev, "vdd");
+	if (IS_ERR(data->vdd)) {
+		rc = PTR_ERR(data->vdd);
+		dev_err(&data->client->dev, "unable to request vdd\n");
+		return rc;
+	}
+
+	if (regulator_count_voltages(data->vdd) > 0) {
+		rc = regulator_set_voltage(data->vdd,
+				DRV2667_VTG_MIN_UV, DRV2667_VTG_MAX_UV);
+		if (rc < 0) {
+			dev_err(&data->client->dev,
+				"vdd set voltage failed(%d)\n", rc);
+			goto put_vdd;
+		}
+	}
+
+	data->vdd_i2c = regulator_get(&data->client->dev, "vdd-i2c");
+	if (IS_ERR(data->vdd_i2c)) {
+		rc = PTR_ERR(data->vdd_i2c);
+		dev_err(&data->client->dev, "unable to request vdd for i2c\n");
+		goto reset_vdd_volt;
+	}
+
+	if (regulator_count_voltages(data->vdd_i2c) > 0) {
+		rc = regulator_set_voltage(data->vdd_i2c,
+			DRV2667_I2C_VTG_MIN_UV, DRV2667_I2C_VTG_MAX_UV);
+		if (rc < 0) {
+			dev_err(&data->client->dev,
+				"vdd_i2c set voltage failed(%d)\n", rc);
+			goto put_vdd_i2c;
+		}
+	}
+
+	return rc;
+
+deconfig_vreg:
+	if (regulator_count_voltages(data->vdd_i2c) > 0)
+		regulator_set_voltage(data->vdd_i2c, 0, DRV2667_I2C_VTG_MAX_UV);
+put_vdd_i2c:
+	regulator_put(data->vdd_i2c);
+reset_vdd_volt:
+	if (regulator_count_voltages(data->vdd) > 0)
+		regulator_set_voltage(data->vdd, 0, DRV2667_VTG_MAX_UV);
+put_vdd:
+	regulator_put(data->vdd);
+	return rc;
+}
+
+static int reg_set_optimum_mode_check(struct regulator *reg, int load_uA)
+{
+	return (regulator_count_voltages(reg) > 0) ?
+		regulator_set_optimum_mode(reg, load_uA) : 0;
+}
+
+
+static int drv2667_vreg_on(struct drv2667_data *data, bool on)
+{
+	int rc = 0;
+
+	if (!on)
+		goto vreg_off;
+
+	rc = reg_set_optimum_mode_check(data->vdd, DRV2667_VTG_CURR_UA);
+	if (rc < 0) {
+		dev_err(&data->client->dev,
+			"Regulator vdd set_opt failed rc=%d\n", rc);
+		return rc;
+	}
+
+	rc = regulator_enable(data->vdd);
+	if (rc < 0) {
+		dev_err(&data->client->dev, "enable vdd failed\n");
+		return rc;
+	}
+
+	rc = reg_set_optimum_mode_check(data->vdd_i2c, DRV2667_I2C_CURR_UA);
+	if (rc < 0) {
+		dev_err(&data->client->dev,
+			"Regulator vdd_i2c set_opt failed rc=%d\n", rc);
+		return rc;
+	}
+
+	rc = regulator_enable(data->vdd_i2c);
+	if (rc < 0) {
+		dev_err(&data->client->dev, "enable vdd_i2c failed\n");
+		goto disable_vdd;
+	}
+
+	return rc;
+vreg_off:
+	regulator_disable(data->vdd_i2c);
+disable_vdd:
+	regulator_disable(data->vdd);
+	return rc;
+}
+
+#ifdef CONFIG_PM
+static int drv2667_suspend(struct device *dev)
+{
+	struct drv2667_data *data = dev_get_drvdata(dev);
+	u8 val;
+	int rc;
+
+	hrtimer_cancel(&data->timer);
+	cancel_work_sync(&data->work);
+
+	/* set standby */
+	val = data->cntl2_val | ~DRV2667_STANDBY_MASK;
+	rc = drv2667_write_reg(data->client, DRV2667_CNTL2_REG, val);
+	if (rc < 0)
+		dev_err(dev, "unable to set standby\n");
+
+	/* turn regulators off */
+	drv2667_vreg_on(data, false);
+	return 0;
+}
+
+static int drv2667_resume(struct device *dev)
+{
+	struct drv2667_data *data = dev_get_drvdata(dev);
+	int rc;
+
+	/* turn regulators on */
+	rc = drv2667_vreg_on(data, true);
+	if (rc < 0) {
+		dev_err(dev, "unable to turn regulators on\n");
+		return rc;
+	}
+
+	/* clear standby */
+	rc = drv2667_write_reg(data->client,
+			DRV2667_CNTL2_REG, data->cntl2_val);
+	if (rc < 0) {
+		dev_err(dev, "unable to clear standby\n");
+		goto vreg_off;
+	}
+
+	return 0;
+vreg_off:
+	drv2667_vreg_on(data, false);
+	return rc;
+}
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void drv2667_early_suspend(struct early_suspend *es)
+{
+	struct drv2667_data *data = container_of(es, struct drv2667_data, es);
+
+	drv2667_suspend(&data->client->dev);
+}
+
+static void drv2667_late_resume(struct early_suspend *es)
+{
+	struct drv2667_data *data = container_of(es, struct drv2667_data, es);
+
+	drv2667_resume(&data->client->dev);
+}
+#endif
+
+static const struct dev_pm_ops drv2667_pm_ops = {
+#ifndef CONFIG_HAS_EARLYSUSPEND
+	.suspend = drv2667_suspend,
+	.resume = drv2667_resume,
+#endif
+};
+#endif
+
+#ifdef CONFIG_OF
+static int drv2667_parse_dt(struct device *dev, struct drv2667_pdata *pdata)
+{
+	struct property *prop;
+	int rc;
+	u32 temp;
+
+	rc = of_property_read_string(dev->of_node, "ti,label", &pdata->name);
+	/* set vibrator as default name */
+	if (rc < 0)
+		pdata->name = "vibrator";
+
+	rc = of_property_read_u32(dev->of_node, "ti,gain", &temp);
+	/* set gain as 0 */
+	if (rc < 0)
+		pdata->gain = 0;
+	else
+		pdata->gain = (u8) temp;
+
+	rc = of_property_read_u32(dev->of_node, "ti,mode", &temp);
+	/* set FIFO mode as default */
+	if (rc < 0)
+		pdata->mode = FIFO_MODE;
+	else
+		pdata->mode = (u8) temp;
+
+	/* read wave sequence */
+	if (pdata->mode == WAV_SEQ_MODE) {
+		prop = of_find_property(dev->of_node, "ti,wav-seq", &temp);
+		if (!prop) {
+			dev_err(dev, "wav seq data not found");
+			return -ENODEV;
+		} else if (temp != DRV2667_WAV_SEQ_LEN) {
+			dev_err(dev, "Invalid length of wav seq data\n");
+			return -EINVAL;
+		}
+		memcpy(pdata->wav_seq, prop->value, DRV2667_WAV_SEQ_LEN);
+	}
+
+	rc = of_property_read_u32(dev->of_node, "ti,idle-timeout-ms", &temp);
+	/* configure minimum idle timeout */
+	if (rc < 0)
+		pdata->idle_timeout_ms = DRV2667_MIN_IDLE_TIMEOUT_MS;
+	else
+		pdata->idle_timeout_ms = (u8) temp;
+
+	rc = of_property_read_u32(dev->of_node, "ti,max-runtime-ms",
+						&pdata->max_runtime_ms);
+	/* configure one sec as default time */
+	if (rc < 0)
+		pdata->max_runtime_ms = MSEC_PER_SEC;
+
+	return 0;
+}
+#else
+static int drv2667_parse_dt(struct device *dev, struct drv2667_pdata *pdata)
+{
+	return -ENODEV;
+}
+#endif
+
+static int __devinit drv2667_probe(struct i2c_client *client,
+				const struct i2c_device_id *id)
+{
+	struct drv2667_data *data;
+	struct drv2667_pdata *pdata;
+	int rc, i;
+	u8 val, fifo_seq_val, reg;
+
+	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+		dev_err(&client->dev, "i2c is not supported\n");
+		return -EIO;
+	}
+
+	if (client->dev.of_node) {
+		pdata = devm_kzalloc(&client->dev,
+			sizeof(struct drv2667_pdata), GFP_KERNEL);
+		if (!pdata) {
+			dev_err(&client->dev, "unable to allocate pdata\n");
+			return -ENOMEM;
+		}
+		/* parse DT */
+		rc = drv2667_parse_dt(&client->dev, pdata);
+		if (rc) {
+			dev_err(&client->dev, "DT parsing failed\n");
+			return rc;
+		}
+	} else {
+		pdata = client->dev.platform_data;
+		if (!pdata) {
+			dev_err(&client->dev, "invalid pdata\n");
+			return -EINVAL;
+		}
+	}
+
+	data = devm_kzalloc(&client->dev, sizeof(struct drv2667_data),
+					GFP_KERNEL);
+	if (!data) {
+		dev_err(&client->dev, "unable to allocate memory\n");
+		return -ENOMEM;
+	}
+
+	i2c_set_clientdata(client, data);
+
+	data->client = client;
+	data->max_runtime_ms = pdata->max_runtime_ms;
+	mutex_init(&data->lock);
+	INIT_WORK(&data->work, drv2667_worker);
+	hrtimer_init(&data->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+	data->timer.function = drv2667_timer;
+	data->mode = pdata->mode;
+
+	/* configure voltage regulators */
+	rc = drv2667_vreg_config(data, true);
+	if (rc) {
+		dev_err(&client->dev, "unable to configure regulators\n");
+		goto destroy_mutex;
+	}
+
+	/* turn on voltage regulators */
+	rc = drv2667_vreg_on(data, true);
+	if (rc) {
+		dev_err(&client->dev, "unable to turn on regulators\n");
+		goto deconfig_vreg;
+	}
+
+	rc = drv2667_read_reg(client, DRV2667_CNTL2_REG);
+	if (rc < 0)
+		goto vreg_off;
+
+	/* set timeout, clear standby */
+	val = (u8) rc;
+
+	if (pdata->idle_timeout_ms < DRV2667_MIN_IDLE_TIMEOUT_MS ||
+		pdata->idle_timeout_ms > DRV2667_MAX_IDLE_TIMEOUT_MS ||
+		(pdata->idle_timeout_ms % DRV2667_MIN_IDLE_TIMEOUT_MS)) {
+		dev_err(&client->dev, "Invalid idle timeout\n");
+		goto vreg_off;
+	}
+
+	val = (val & DRV2667_TIMEOUT_MASK) |
+		((pdata->idle_timeout_ms / DRV2667_MIN_IDLE_TIMEOUT_MS - 1) <<
+		DRV2667_TIMEOUT_SHIFT);
+
+	val &= DRV2667_STANDBY_MASK;
+
+	rc = drv2667_write_reg(client, DRV2667_CNTL2_REG, val);
+	if (rc < 0)
+		goto vreg_off;
+
+	/* cache control2 val */
+	data->cntl2_val = val;
+
+	/* program drv2667 registers */
+	rc = drv2667_read_reg(client, DRV2667_CNTL1_REG);
+	if (rc < 0)
+		goto vreg_off;
+
+	/* gain and input mode */
+	val = (u8) rc;
+
+	/* remove this check after adding support for these modes */
+	if (data->mode == ANALOG_MODE || data->mode == RAM_SEQ_MODE) {
+		dev_err(&data->client->dev, "Mode not supported\n");
+		goto vreg_off;
+	} else
+		val &= ~DRV2667_INPUT_MUX_MASK; /* set digital mode */
+
+	val = (val & DRV2667_GAIN_MASK) | (pdata->gain << DRV2667_GAIN_SHIFT);
+
+	rc = drv2667_write_reg(client, DRV2667_CNTL1_REG, val);
+	if (rc < 0)
+		goto vreg_off;
+
+	if (data->mode == FIFO_MODE) {
+		/* Load a predefined pattern for FIFO mode */
+		data->buf[0] = DRV2667_FIFO_REG;
+		fifo_seq_val = DRV2667_VIB_START_VAL;
+
+		for (i = 1; i < DRV2667_FIFO_SIZE - 1; i++, fifo_seq_val++)
+			data->buf[i] = fifo_seq_val;
+
+		data->time_chunk_ms = DRV2667_FIFO_CHUNK_MS;
+	} else if (data->mode == WAV_SEQ_MODE) {
+		u8 freq, rep, dur;
+
+		/* program wave sequence from pdata */
+		/* id to wave sequence 3, set page */
+		rc = drv2667_write_reg(client, DRV2667_WAV_SEQ3_REG,
+				pdata->wav_seq[DRV2667_WAV_SEQ_ID_IDX]);
+		if (rc < 0)
+			goto vreg_off;
+
+		/* set page to wave form sequence */
+		rc = drv2667_write_reg(client, DRV2667_PAGE_REG,
+				pdata->wav_seq[DRV2667_WAV_SEQ_ID_IDX]);
+		if (rc < 0)
+			goto vreg_off;
+
+		/* program waveform sequence */
+		for (reg = 0, i = 1; i < DRV2667_WAV_SEQ_LEN - 1; i++, reg++) {
+			rc = drv2667_write_reg(client, reg, pdata->wav_seq[i]);
+			if (rc < 0)
+				goto vreg_off;
+		}
+
+		/* set page back to normal register space */
+		rc = drv2667_write_reg(client, DRV2667_PAGE_REG,
+					DRV2667_REG_PAGE_ID);
+		if (rc < 0)
+			goto vreg_off;
+
+		freq = pdata->wav_seq[DRV2667_WAV_SEQ_FREQ_IDX];
+		rep = pdata->wav_seq[DRV2667_WAV_SEQ_REP_IDX];
+		dur = pdata->wav_seq[DRV2667_WAV_SEQ_DUR_IDX];
+
+		data->time_chunk_ms = (rep * dur * MSEC_PER_SEC) /
+				(freq *	DRV2667_WAV_SEQ_FREQ_MIN);
+	}
+
+	drv2667_dump_regs(data, "new");
+
+	/* register with timed output class */
+	data->dev.name = pdata->name;
+	data->dev.get_time = drv2667_get_time;
+	data->dev.enable = drv2667_enable;
+
+	rc = timed_output_dev_register(&data->dev);
+	if (rc) {
+		dev_err(&client->dev, "unable to register with timed_output\n");
+		goto vreg_off;
+	}
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+	data->es.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + DRV2667_SUS_LEVEL;
+	data->es.suspend = drv2667_early_suspend;
+	data->es.resume = drv2667_late_resume;
+	register_early_suspend(&data->es);
+#endif
+	return 0;
+
+vreg_off:
+	drv2667_vreg_on(data, false);
+deconfig_vreg:
+	drv2667_vreg_config(data, false);
+destroy_mutex:
+	mutex_destroy(&data->lock);
+	return rc;
+}
+
+static int __devexit drv2667_remove(struct i2c_client *client)
+{
+	struct drv2667_data *data = i2c_get_clientdata(client);
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+	unregister_early_suspend(&data->es);
+#endif
+	mutex_destroy(&data->lock);
+	timed_output_dev_unregister(&data->dev);
+	hrtimer_cancel(&data->timer);
+	cancel_work_sync(&data->work);
+	drv2667_vreg_on(data, false);
+	drv2667_vreg_config(data, false);
+
+	return 0;
+}
+
+static const struct i2c_device_id drv2667_id_table[] = {
+	{"drv2667", 0},
+	{ },
+};
+MODULE_DEVICE_TABLE(i2c, drv2667_id_table);
+
+#ifdef CONFIG_OF
+static const struct of_device_id drv2667_of_id_table[] = {
+	{.compatible = "ti, drv2667"},
+	{ },
+};
+#else
+#define drv2667_of_id_table NULL
+#endif
+
+static struct i2c_driver drv2667_i2c_driver = {
+	.driver = {
+		.name = "drv2667",
+		.owner = THIS_MODULE,
+		.of_match_table = drv2667_of_id_table,
+#ifdef CONFIG_PM
+		.pm = &drv2667_pm_ops,
+#endif
+	},
+	.probe = drv2667_probe,
+	.remove = __devexit_p(drv2667_remove),
+	.id_table = drv2667_id_table,
+};
+
+module_i2c_driver(drv2667_i2c_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("TI DRV2667 chip driver");
diff --git a/drivers/misc/tspp.c b/drivers/misc/tspp.c
index 1792104..eebaab9 100644
--- a/drivers/misc/tspp.c
+++ b/drivers/misc/tspp.c
@@ -35,6 +35,8 @@
 #include <mach/sps.h>            /* BAM stuff */
 #include <mach/gpio.h>
 #include <linux/wakelock.h>      /* Locking functions */
+#include <linux/timer.h>         /* Timer services */
+#include <linux/jiffies.h>       /* Jiffies counter */
 #include <mach/dma.h>
 #include <mach/msm_tspp.h>
 #include <linux/debugfs.h>
@@ -49,11 +51,24 @@
 #define TSPP_NUM_PRIORITIES            16
 #define TSPP_NUM_KEYS                  8
 #define INVALID_CHANNEL                0xFFFFFFFF
-#define TSPP_SPS_DESCRIPTOR_COUNT      128
+
+/*
+ * BAM descriptor FIFO size (in number of descriptors).
+ * Max number of descriptors allowed by SPS which is 8K-1.
+ * Restrict it to half of this to save DMA memory.
+ */
+#define TSPP_SPS_DESCRIPTOR_COUNT      (4 * 1024 - 1)
 #define TSPP_PACKET_LENGTH             188
 #define TSPP_MIN_BUFFER_SIZE           (TSPP_PACKET_LENGTH)
-#define TSPP_MAX_BUFFER_SIZE           (32 * 1024)
-#define TSPP_NUM_BUFFERS               64
+
+/* Max descriptor buffer size allowed by SPS */
+#define TSPP_MAX_BUFFER_SIZE           (32 * 1024 - 1)
+
+/*
+ * Max allowed TSPP buffers/descriptors.
+ * If SPS desc FIFO holds X descriptors, we can queue up to X-1 descriptors.
+ */
+#define TSPP_NUM_BUFFERS               (TSPP_SPS_DESCRIPTOR_COUNT - 1)
 #define TSPP_TSIF_DEFAULT_TIME_LIMIT   60
 #define SPS_DESCRIPTOR_SIZE            8
 #define MIN_ACCEPTABLE_BUFFER_COUNT    2
@@ -103,29 +118,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 +187,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
@@ -307,6 +322,8 @@
 #define CONTEXT_UNSPEC_LENGTH					BIT(11)
 #define CONTEXT_GET_CONT_COUNT(_a)			((_a >> 12) & 0xF)
 
+#define MSEC_TO_JIFFIES(msec)			((msec) * HZ / 1000)
+
 struct tspp_pipe_performance_regs {
 	u32 tsp_total;
 	u32 ps_duplicate_tsp;
@@ -323,10 +340,19 @@
 	u32 time_limit;
 	u32 ref_count;
 	enum tspp_tsif_mode mode;
+	int clock_inverse;
+	int data_inverse;
+	int sync_inverse;
+	int enable_inverse;
+	u32 tsif_irq;
 
 	/* debugfs */
 	struct dentry *dent_tsif;
 	struct dentry *debugfs_tsif_regs[ARRAY_SIZE(debugfs_tsif_regs)];
+	u32 stat_rx;
+	u32 stat_overflow;
+	u32 stat_lost_sync;
+	u32 stat_timeout;
 };
 
 enum tspp_buf_state {
@@ -370,7 +396,10 @@
 	enum tspp_mode mode;
 	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 */
+	u32 expiration_period_ms; /* notification on partially filled buffers */
+	struct timer_list expiration_timer;
+	tspp_memfree *memfree;   /* user defined memory free function */
+	void *user_info; /* user cookie passed to memory alloc/free function */
 };
 
 struct tspp_pid_filter_table {
@@ -474,6 +503,49 @@
 		dev_info(&device->pdev->dev, "broken pipe %i", status & 0xffff);
 
 	writel_relaxed(status, device->base + TSPP_IRQ_CLEAR);
+
+	/*
+	 * Before returning IRQ_HANDLED to the generic interrupt handling
+	 * framework need to make sure all operations including clearing of
+	 * interrupt status registers in the hardware is performed.
+	 * Thus a barrier after clearing the interrupt status register
+	 * is required to guarantee that the interrupt status register has
+	 * really been cleared by the time we return from this handler.
+	 */
+	wmb();
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t tsif_isr(int irq, void *dev)
+{
+	struct tspp_tsif_device *tsif_device = dev;
+	u32 sts_ctl = ioread32(tsif_device->base + TSIF_STS_CTL_OFF);
+
+	if (!(sts_ctl & (TSIF_STS_CTL_PACK_AVAIL |
+			 TSIF_STS_CTL_OVERFLOW |
+			 TSIF_STS_CTL_LOST_SYNC |
+			 TSIF_STS_CTL_TIMEOUT)))
+		return IRQ_NONE;
+
+	if (sts_ctl & TSIF_STS_CTL_OVERFLOW)
+		tsif_device->stat_overflow++;
+
+	if (sts_ctl & TSIF_STS_CTL_LOST_SYNC)
+		tsif_device->stat_lost_sync++;
+
+	if (sts_ctl & TSIF_STS_CTL_TIMEOUT)
+		tsif_device->stat_timeout++;
+
+	iowrite32(sts_ctl, tsif_device->base + TSIF_STS_CTL_OFF);
+
+	/*
+	 * Before returning IRQ_HANDLED to the generic interrupt handling
+	 * framework need to make sure all operations including clearing of
+	 * interrupt status registers in the hardware is performed.
+	 * Thus a barrier after clearing the interrupt status register
+	 * is required to guarantee that the interrupt status register has
+	 * really been cleared by the time we return from this handler.
+	 */
 	wmb();
 	return IRQ_HANDLED;
 }
@@ -485,6 +557,14 @@
 	tasklet_schedule(&pdev->tlet);
 }
 
+static void tspp_expiration_timer(unsigned long data)
+{
+	struct tspp_device *pdev = (struct tspp_device *)data;
+
+	if (pdev)
+		tasklet_schedule(&pdev->tlet);
+}
+
 /*** tasklet ***/
 static void tspp_sps_complete_tlet(unsigned long data)
 {
@@ -499,9 +579,14 @@
 	for (i = 0; i < TSPP_NUM_CHANNELS; i++) {
 		complete = 0;
 		channel = &device->channels[i];
+
 		if (!channel->used || !channel->waiting)
 			continue;
 
+		/* stop the expiration timer */
+		if (channel->expiration_period_ms)
+			del_timer(&channel->expiration_timer);
+
 		/* get completions */
 		while (channel->waiting->state == TSPP_BUF_STATE_WAITING) {
 			if (sps_get_iovec(channel->pipe, &iovec) != 0) {
@@ -521,6 +606,11 @@
 			channel->waiting->filled = iovec.size;
 			channel->waiting->read_index = 0;
 
+			if (channel->src == TSPP_SOURCE_TSIF0)
+				device->tsif[0].stat_rx++;
+			else if (channel->src == TSPP_SOURCE_TSIF1)
+				device->tsif[1].stat_rx++;
+
 			/* update the pointers */
 			channel->waiting = channel->waiting->next;
 		}
@@ -534,6 +624,13 @@
 				channel->notifier(channel->id,
 					channel->notify_data);
 		}
+
+		/* restart expiration timer */
+		if (channel->expiration_period_ms)
+			mod_timer(&channel->expiration_timer,
+				jiffies +
+				MSEC_TO_JIFFIES(
+					channel->expiration_period_ms));
 	}
 
 	spin_unlock_irqrestore(&device->spinlock, flags);
@@ -580,8 +677,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 +700,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 +793,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 +911,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;
@@ -827,7 +935,7 @@
 
 	/* generate interrupt according to requested frequency */
 	if (buffer->desc.id % channel->int_freq == channel->int_freq-1)
-		flags = SPS_IOVEC_FLAG_INT | SPS_IOVEC_FLAG_EOB;
+		flags = SPS_IOVEC_FLAG_INT;
 
 	/* start the transfer */
 	rc = sps_transfer_one(channel->pipe,
@@ -852,6 +960,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 +1025,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 +1068,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->expiration_period_ms = 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 +1097,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 +1132,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 +1278,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 +1310,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 +1319,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 +1370,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;
@@ -1167,10 +1432,11 @@
 		SPS_O_AUTO_ENABLE | /* connection is auto-enabled */
 		SPS_O_STREAMING | /* streaming mode */
 		SPS_O_DESC_DONE | /* interrupt on end of descriptor */
-		SPS_O_ACK_TRANSFERS; /* must use sps_get_iovec() */
+		SPS_O_ACK_TRANSFERS | /* must use sps_get_iovec() */
+		SPS_O_HYBRID; /* Read actual descriptors in sps_get_iovec() */
 	config->src_pipe_index = channel->id;
 	config->desc.size =
-		(TSPP_SPS_DESCRIPTOR_COUNT + 1) * SPS_DESCRIPTOR_SIZE;
+		TSPP_SPS_DESCRIPTOR_COUNT * SPS_DESCRIPTOR_SIZE;
 	config->desc.base = dma_alloc_coherent(NULL,
 						config->desc.size,
 						&config->desc.phys_base,
@@ -1201,6 +1467,11 @@
 		goto err_event;
 	}
 
+	init_timer(&channel->expiration_timer);
+	channel->expiration_timer.function = tspp_expiration_timer;
+	channel->expiration_timer.data = (unsigned long)pdev;
+	channel->expiration_timer.expires = 0xffffffffL;
+
 	rc = pm_runtime_get(&pdev->pdev->dev);
 	if (rc < 0) {
 		dev_err(&pdev->pdev->dev,
@@ -1221,6 +1492,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 +1510,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");
@@ -1247,9 +1526,12 @@
 	if (!channel->used)
 		return 0;
 
+	if (channel->expiration_period_ms)
+		del_timer(&channel->expiration_timer);
+
 	channel->notifier = NULL;
 	channel->notify_data = NULL;
-	channel->notify_timer = 0;
+	channel->expiration_period_ms = 0;
 
 	config = &channel->config;
 	pdev = channel->pdev;
@@ -1285,21 +1567,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 +1588,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 +1632,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 +1698,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 +1728,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 +1790,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 +1850,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)
 {
@@ -1561,11 +1880,21 @@
 	channel = &pdev->channels[channel_id];
 	channel->notifier = pNotify;
 	channel->notify_data = userdata;
-	channel->notify_timer = timer_ms;
+	channel->expiration_period_ms = timer_ms;
+
 	return 0;
 }
 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 +1916,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 +1965,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 +2026,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 +2055,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 +2119,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 +2143,31 @@
 
 		/* 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;
+
+	/* Now that buffers are scheduled to HW, kick data expiration timer */
+	if (channel->expiration_period_ms)
+		mod_timer(&channel->expiration_timer,
+			jiffies +
+			MSEC_TO_JIFFIES(
+				channel->expiration_period_ms));
+
 	return 0;
 }
 EXPORT_SYMBOL(tspp_allocate_buffers);
@@ -1894,8 +2286,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 +2388,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;
 
@@ -2038,6 +2434,31 @@
 				base + debugfs_tsif_regs[i].offset,
 				&fops_iomem_x32);
 		}
+
+		debugfs_create_u32(
+			"stat_rx_chunks",
+			S_IRUGO|S_IWUGO,
+			tsif_device->dent_tsif,
+			&tsif_device->stat_rx);
+
+		debugfs_create_u32(
+			"stat_overflow",
+			S_IRUGO|S_IWUGO,
+			tsif_device->dent_tsif,
+			&tsif_device->stat_overflow);
+
+		debugfs_create_u32(
+			"stat_lost_sync",
+			S_IRUGO|S_IWUGO,
+			tsif_device->dent_tsif,
+			&tsif_device->stat_lost_sync);
+
+		debugfs_create_u32(
+			"stat_timeout",
+			S_IRUGO|S_IWUGO,
+			tsif_device->dent_tsif,
+			&tsif_device->stat_timeout);
+
 	}
 }
 
@@ -2088,13 +2509,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;
@@ -2215,6 +2637,21 @@
 		goto err_irq;
 	}
 
+	/* map TSIF IRQs */
+	device->tsif[0].tsif_irq = TSIF1_IRQ;
+	device->tsif[1].tsif_irq = TSIF2_IRQ;
+
+	for (i = 0; i < TSPP_TSIF_INSTANCES; i++) {
+		rc = request_irq(device->tsif[i].tsif_irq,
+				tsif_isr, IRQF_SHARED,
+				dev_name(&pdev->dev), &device->tsif[i]);
+		if (rc) {
+			dev_warn(&pdev->dev, "failed to request TSIF%d IRQ: %d",
+				i, rc);
+			device->tsif[i].tsif_irq = 0;
+		}
+	}
+
 	/* BAM IRQ */
 	device->bam_irq = TSIF_BAM_IRQ;
 
@@ -2290,6 +2727,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:
@@ -2340,8 +2783,11 @@
 
 	sps_deregister_bam_device(device->bam_handle);
 
-	for (i = 0; i < TSPP_TSIF_INSTANCES; i++)
+	for (i = 0; i < TSPP_TSIF_INSTANCES; i++) {
 		tsif_debugfs_exit(&device->tsif[i]);
+		if (device->tsif[i].tsif_irq)
+			free_irq(device->tsif[i].tsif_irq,  &device->tsif[i]);
+	}
 
 	wake_lock_destroy(&device->wake_lock);
 	free_irq(device->tspp_irq, device);
diff --git a/drivers/mmc/card/Kconfig b/drivers/mmc/card/Kconfig
index 33f0600..a1bea00 100644
--- a/drivers/mmc/card/Kconfig
+++ b/drivers/mmc/card/Kconfig
@@ -80,6 +80,7 @@
 config MMC_BLOCK_TEST
 	tristate "MMC block test"
 	depends on MMC_BLOCK && IOSCHED_TEST
+	default y
 	help
 	  MMC block test can be used with test iosched to test the MMC block
 	  device.
diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
index 254672f..ae68060 100644
--- a/drivers/mmc/card/block.c
+++ b/drivers/mmc/card/block.c
@@ -298,13 +298,33 @@
 {
 	int value;
 	struct mmc_blk_data *md = mmc_blk_get(dev_to_disk(dev));
+	struct mmc_card *card = md->queue.card;
+	int ret = count;
+
+	if (!card) {
+		ret = -EINVAL;
+		goto exit;
+	}
 
 	sscanf(buf, "%d", &value);
-	if (value >= 0)
-		md->queue.num_wr_reqs_to_start_packing = value;
 
+	if (value >= 0) {
+		md->queue.num_wr_reqs_to_start_packing =
+		    min_t(int, value, (int)card->ext_csd.max_packed_writes);
+
+		pr_debug("%s: trigger to pack: new value = %d",
+			mmc_hostname(card->host),
+			md->queue.num_wr_reqs_to_start_packing);
+	} else {
+		pr_err("%s: value %d is not valid. old value remains = %d",
+			mmc_hostname(card->host), value,
+			md->queue.num_wr_reqs_to_start_packing);
+		ret = -EINVAL;
+	}
+
+exit:
 	mmc_blk_put(md);
-	return count;
+	return ret;
 }
 
 static ssize_t
@@ -317,13 +337,13 @@
 	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);
+		ret = -EINVAL;
+	else {
+	    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;
@@ -1409,6 +1429,10 @@
 	if (!(host->caps2 & MMC_CAP2_PACKED_WR))
 		return;
 
+	/* Support for the write packing on eMMC 4.5 or later */
+	if (mq->card->ext_csd.rev <= 5)
+		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
@@ -1472,64 +1496,6 @@
 }
 EXPORT_SYMBOL(mmc_blk_init_packed_statistics);
 
-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);
-}
-EXPORT_SYMBOL(print_mmc_packing_stats);
-
 static u8 mmc_blk_prep_packed_list(struct mmc_queue *mq, struct request *req)
 {
 	struct request_queue *q = mq->queue;
@@ -1737,6 +1703,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;
@@ -2058,7 +2025,8 @@
 		/* complete ongoing async transfer before issuing discard */
 		if (card->host->areq)
 			mmc_blk_issue_rw_rq(mq, NULL);
-		if (req->cmd_flags & REQ_SECURE)
+		if (req->cmd_flags & REQ_SECURE &&
+			!(card->quirks & MMC_QUIRK_SEC_ERASE_TRIM_BROKEN))
 			ret = mmc_blk_issue_secdiscard_rq(mq, req);
 		else
 			ret = mmc_blk_issue_discard_rq(mq, req);
@@ -2360,7 +2328,7 @@
 	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;
@@ -2373,14 +2341,19 @@
 	ret = device_create_file(disk_to_dev(md->disk),
 				 &md->min_sectors_to_check_bkops_status);
 	if (ret)
-		goto power_ro_lock_fail;
+		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;
 }
@@ -2388,6 +2361,7 @@
 #define CID_MANFID_SANDISK	0x2
 #define CID_MANFID_TOSHIBA	0x11
 #define CID_MANFID_MICRON	0x13
+#define CID_MANFID_SAMSUNG	0x15
 
 static const struct mmc_fixup blk_fixups[] =
 {
@@ -2428,6 +2402,28 @@
 	MMC_FIXUP("SEM04G", 0x45, CID_OEMID_ANY, add_quirk_mmc,
 		  MMC_QUIRK_INAND_DATA_TIMEOUT),
 
+	/*
+	 * On these Samsung MoviNAND parts, performing secure erase or
+	 * secure trim can result in unrecoverable corruption due to a
+	 * firmware bug.
+	 */
+	MMC_FIXUP("M8G2FA", CID_MANFID_SAMSUNG, CID_OEMID_ANY, add_quirk_mmc,
+		  MMC_QUIRK_SEC_ERASE_TRIM_BROKEN),
+	MMC_FIXUP("MAG4FA", CID_MANFID_SAMSUNG, CID_OEMID_ANY, add_quirk_mmc,
+		  MMC_QUIRK_SEC_ERASE_TRIM_BROKEN),
+	MMC_FIXUP("MBG8FA", CID_MANFID_SAMSUNG, CID_OEMID_ANY, add_quirk_mmc,
+		  MMC_QUIRK_SEC_ERASE_TRIM_BROKEN),
+	MMC_FIXUP("MCGAFA", CID_MANFID_SAMSUNG, CID_OEMID_ANY, add_quirk_mmc,
+		  MMC_QUIRK_SEC_ERASE_TRIM_BROKEN),
+	MMC_FIXUP("VAL00M", CID_MANFID_SAMSUNG, CID_OEMID_ANY, add_quirk_mmc,
+		  MMC_QUIRK_SEC_ERASE_TRIM_BROKEN),
+	MMC_FIXUP("VYL00M", CID_MANFID_SAMSUNG, CID_OEMID_ANY, add_quirk_mmc,
+		  MMC_QUIRK_SEC_ERASE_TRIM_BROKEN),
+	MMC_FIXUP("KYL00M", CID_MANFID_SAMSUNG, CID_OEMID_ANY, add_quirk_mmc,
+		  MMC_QUIRK_SEC_ERASE_TRIM_BROKEN),
+	MMC_FIXUP("VZL00M", CID_MANFID_SAMSUNG, CID_OEMID_ANY, add_quirk_mmc,
+		  MMC_QUIRK_SEC_ERASE_TRIM_BROKEN),
+
 	END_FIXUP
 };
 
diff --git a/drivers/mmc/card/mmc_block_test.c b/drivers/mmc/card/mmc_block_test.c
index 7d3ac83..610a822 100644
--- a/drivers/mmc/card/mmc_block_test.c
+++ b/drivers/mmc/card/mmc_block_test.c
@@ -20,6 +20,7 @@
 #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>
 
@@ -36,6 +37,28 @@
 #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)
@@ -127,6 +150,9 @@
 	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 {
@@ -154,6 +180,8 @@
 	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 {
@@ -193,6 +221,63 @@
 
 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.
@@ -469,101 +554,105 @@
 		return NULL;
 	}
 
-	switch (td->test_info.testcase) {
+switch (td->test_info.testcase) {
 	case TEST_STOP_DUE_TO_FLUSH:
-		return "Test stop due to flush";
+		return "\"stop due to flush\"";
 	case TEST_STOP_DUE_TO_FLUSH_AFTER_MAX_REQS:
-		return "Test stop due to flush after max-1 reqs";
+		return "\"stop due to flush after max-1 reqs\"";
 	case TEST_STOP_DUE_TO_READ:
-		return "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";
+		return "\"stop due to read after max-1 reqs\"";
 	case TEST_STOP_DUE_TO_EMPTY_QUEUE:
-		return "Test stop due to empty queue";
+		return "\"stop due to empty queue\"";
 	case TEST_STOP_DUE_TO_MAX_REQ_NUM:
-		return "Test stop due to max req num";
+		return "\"stop due to max req num\"";
 	case TEST_STOP_DUE_TO_THRESHOLD:
-		return "Test stop due to exceeding threshold";
+		return "\"stop due to exceeding threshold\"";
 	case TEST_RET_ABORT:
-		return "Test err_check return abort";
+		return "\"err_check return abort\"";
 	case TEST_RET_PARTIAL_FOLLOWED_BY_SUCCESS:
-		return "Test err_check return partial followed by success";
+		return "\"err_check return partial followed by success\"";
 	case TEST_RET_PARTIAL_FOLLOWED_BY_ABORT:
-		return "Test err_check return partial followed by abort";
+		return "\"err_check return partial followed by abort\"";
 	case TEST_RET_PARTIAL_MULTIPLE_UNTIL_SUCCESS:
-		return "Test err_check return partial multiple until success";
+		return "\"err_check return partial multiple until success\"";
 	case TEST_RET_PARTIAL_MAX_FAIL_IDX:
-		return "Test err_check return partial max fail index";
+		return "\"err_check return partial max fail index\"";
 	case TEST_RET_RETRY:
-		return "Test err_check return retry";
+		return "\"err_check return retry\"";
 	case TEST_RET_CMD_ERR:
-		return "Test err_check return cmd error";
+		return "\"err_check return cmd error\"";
 	case TEST_RET_DATA_ERR:
-		return "Test err_check return data error";
+		return "\"err_check return data error\"";
 	case TEST_HDR_INVALID_VERSION:
-		return "Test invalid - wrong header version";
+		return "\"invalid - wrong header version\"";
 	case TEST_HDR_WRONG_WRITE_CODE:
-		return "Test invalid - wrong write code";
+		return "\"invalid - wrong write code\"";
 	case TEST_HDR_INVALID_RW_CODE:
-		return "Test invalid - wrong R/W code";
+		return "\"invalid - wrong R/W code\"";
 	case TEST_HDR_DIFFERENT_ADDRESSES:
-		return "Test invalid - header different addresses";
+		return "\"invalid - header different addresses\"";
 	case TEST_HDR_REQ_NUM_SMALLER_THAN_ACTUAL:
-		return "Test invalid - header req num smaller than actual";
+		return "\"invalid - header req num smaller than actual\"";
 	case TEST_HDR_REQ_NUM_LARGER_THAN_ACTUAL:
-		return "Test invalid - header req num larger than actual";
+		return "\"invalid - header req num larger than actual\"";
 	case TEST_HDR_CMD23_PACKED_BIT_SET:
-		return "Test invalid - header cmd23 packed bit set";
+		return "\"invalid - header cmd23 packed bit set\"";
 	case TEST_CMD23_MAX_PACKED_WRITES:
-		return "Test invalid - cmd23 max packed writes";
+		return "\"invalid - cmd23 max packed writes\"";
 	case TEST_CMD23_ZERO_PACKED_WRITES:
-		return "Test invalid - cmd23 zero packed writes";
+		return "\"invalid - cmd23 zero packed writes\"";
 	case TEST_CMD23_PACKED_BIT_UNSET:
-		return "Test invalid - cmd23 packed bit unset";
+		return "\"invalid - cmd23 packed bit unset\"";
 	case TEST_CMD23_REL_WR_BIT_SET:
-		return "Test invalid - cmd23 rel wr bit set";
+		return "\"invalid - cmd23 rel wr bit set\"";
 	case TEST_CMD23_BITS_16TO29_SET:
-		return "Test invalid - cmd23 bits [16-29] set";
+		return "\"invalid - cmd23 bits [16-29] set\"";
 	case TEST_CMD23_HDR_BLK_NOT_IN_COUNT:
-		return "Test invalid - cmd23 header block not in count";
+		return "\"invalid - cmd23 header block not in count\"";
 	case TEST_PACKING_EXP_N_OVER_TRIGGER:
-		return "\nTest packing control - pack n";
+		return "\"packing control - pack n\"";
 	case TEST_PACKING_EXP_N_OVER_TRIGGER_FB_READ:
-		return "\nTest packing control - pack n followed by read";
+		return "\"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";
+		return "\"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";
+		return "\"packing control - pack one followed by read\"";
 	case TEST_PACKING_EXP_THRESHOLD_OVER_TRIGGER:
-		return "\nTest packing control - pack threshold";
+		return "\"packing control - pack threshold\"";
 	case TEST_PACKING_NOT_EXP_LESS_THAN_TRIGGER_REQUESTS:
-		return "\nTest packing control - no packing";
+		return "\"packing control - no packing\"";
 	case TEST_PACKING_NOT_EXP_TRIGGER_REQUESTS:
-		return "\nTest packing control - no packing, trigger requests";
+		return "\"packing control - no packing, trigger requests\"";
 	case TEST_PACKING_NOT_EXP_TRIGGER_READ_TRIGGER:
-		return "\nTest packing control - no pack, trigger-read-trigger";
+		return "\"packing control - no pack, trigger-read-trigger\"";
 	case TEST_PACKING_NOT_EXP_TRIGGER_FLUSH_TRIGGER:
-		return "\nTest packing control- no pack, trigger-flush-trigger";
+		return "\"packing control- no pack, trigger-flush-trigger\"";
 	case TEST_PACK_MIX_PACKED_NO_PACKED_PACKED:
-		return "\nTest packing control - mix: pack -> no pack -> pack";
+		return "\"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";
+		return "\"packing control - mix: no pack->pack->no pack\"";
 	case TEST_WRITE_DISCARD_SANITIZE_READ:
-		return "\nTest write, discard, sanitize";
+		return "\"write, discard, sanitize\"";
 	case BKOPS_DELAYED_WORK_LEVEL_1:
-		return "\nTest delayed work BKOPS level 1";
+		return "\"delayed work BKOPS level 1\"";
 	case BKOPS_DELAYED_WORK_LEVEL_1_HPI:
-		return "\nTest delayed work BKOPS level 1 with HPI";
+		return "\"delayed work BKOPS level 1 with HPI\"";
 	case BKOPS_CANCEL_DELAYED_WORK:
-		return "\nTest cancel delayed BKOPS work";
+		return "\"cancel delayed BKOPS work\"";
 	case BKOPS_URGENT_LEVEL_2:
-		return "\nTest urgent BKOPS level 2";
+		return "\"urgent BKOPS level 2\"";
 	case BKOPS_URGENT_LEVEL_2_TWO_REQS:
-		return "\nTest urgent BKOPS level 2, followed by a request";
+		return "\"urgent BKOPS level 2, followed by a request\"";
 	case BKOPS_URGENT_LEVEL_3:
-		return "\nTest urgent BKOPS level 3";
+		return "\"urgent BKOPS level 3\"";
+	case TEST_LONG_SEQUENTIAL_READ:
+		return "\"long sequential read\"";
+	case TEST_LONG_SEQUENTIAL_WRITE:
+		return "\"long sequential write\"";
 	default:
-		 return "Unknown testcase";
+		return " Unknown testcase";
 	}
 
 	return NULL;
@@ -818,8 +907,10 @@
 	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 + 4096 * td->num_of_write_bios;
+	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
@@ -1139,7 +1230,8 @@
 		if (i > (num_requests / 2))
 			is_err_expected = 1;
 
-		start_address = td->start_sector + 4096 * td->num_of_write_bios;
+		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);
@@ -1243,6 +1335,48 @@
 	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
@@ -1351,9 +1485,15 @@
 		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__);
-		return -EINVAL;
+		ret = -EINVAL;
 	}
 
 	return ret;
@@ -1562,6 +1702,12 @@
 		    (bkops_stat->suspend == 0) &&
 		    (bkops_stat->hpi == 1))
 			goto exit;
+		/* this might happen due to timing issues */
+		else if
+		   ((bkops_stat->bkops_level[BKOPS_SEVERITY_1_INDEX] == 0) &&
+		    (bkops_stat->suspend == 0) &&
+		    (bkops_stat->hpi == 0))
+			goto ignore;
 		else
 			goto fail;
 		break;
@@ -1595,6 +1741,9 @@
 
 exit:
 	return 0;
+ignore:
+	test_iosched_set_ignore_round(true);
+	return 0;
 fail:
 	if (td->fs_wr_reqs_during_test) {
 		test_pr_info("%s: wr reqs during test, cancel the round",
@@ -2430,6 +2579,185 @@
 	.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);
@@ -2439,6 +2767,8 @@
 	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)
@@ -2521,6 +2851,26 @@
 	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:
diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c
index cc91646..8eb787d 100644
--- a/drivers/mmc/card/queue.c
+++ b/drivers/mmc/card/queue.c
@@ -201,15 +201,18 @@
 	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;
+	mq->num_wr_reqs_to_start_packing =
+		min_t(int, (int)card->ext_csd.max_packed_writes,
+		     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/core/core.c b/drivers/mmc/core/core.c
index 3aa38d2..f91ba89 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -29,6 +29,7 @@
 #include <linux/wakelock.h>
 #include <linux/pm.h>
 #include <linux/slab.h>
+#include <linux/jiffies.h>
 
 #include <linux/mmc/card.h>
 #include <linux/mmc/host.h>
@@ -44,6 +45,8 @@
 #include "sd_ops.h"
 #include "sdio_ops.h"
 
+static void mmc_clk_scaling(struct mmc_host *host, bool from_wq);
+
 /*
  * Background operations can take a long time, depending on the housekeeping
  * operations the card has to perform.
@@ -171,6 +174,10 @@
 #ifdef CONFIG_MMC_PERF_PROFILING
 	ktime_t diff;
 #endif
+	if (host->card && host->clk_scaling.enable)
+		host->clk_scaling.busy_time_us +=
+			ktime_to_us(ktime_sub(ktime_get(),
+					host->clk_scaling.start_busy));
 
 	if (err && cmd->retries && mmc_host_is_spi(host)) {
 		if (cmd->resp[0] & R1_SPI_ILLEGAL_COMMAND)
@@ -300,6 +307,19 @@
 	}
 	mmc_host_clk_hold(host);
 	led_trigger_event(host->led, LED_FULL);
+
+	if (host->card && host->clk_scaling.enable) {
+		/*
+		 * Check if we need to scale the clocks. Clocks
+		 * will be scaled up immediately if necessary
+		 * conditions are satisfied. Scaling down the
+		 * frequency will be done after current thread
+		 * releases host.
+		 */
+		mmc_clk_scaling(host, false);
+		host->clk_scaling.start_busy = ktime_get();
+	}
+
 	host->ops->request(host, mrq);
 }
 
@@ -2286,6 +2306,289 @@
 }
 EXPORT_SYMBOL(mmc_hw_reset_check);
 
+/**
+ * mmc_reset_clk_scale_stats() - reset clock scaling statistics
+ * @host: pointer to mmc host structure
+ */
+void mmc_reset_clk_scale_stats(struct mmc_host *host)
+{
+	host->clk_scaling.busy_time_us = 0;
+	host->clk_scaling.window_time = jiffies;
+}
+EXPORT_SYMBOL_GPL(mmc_reset_clk_scale_stats);
+
+/**
+ * mmc_get_max_frequency() - get max. frequency supported
+ * @host: pointer to mmc host structure
+ *
+ * Returns max. frequency supported by card/host. If the
+ * timing mode is SDR50/SDR104/HS200/DDR50 return appropriate
+ * max. frequency in these modes else, use the current frequency.
+ * Also, allow host drivers to overwrite the frequency in case
+ * they support "get_max_frequency" host ops.
+ */
+unsigned long mmc_get_max_frequency(struct mmc_host *host)
+{
+	unsigned long freq;
+
+	if (host->ops && host->ops->get_max_frequency) {
+		freq = host->ops->get_max_frequency(host);
+		goto out;
+	}
+
+	switch (host->ios.timing) {
+	case MMC_TIMING_UHS_SDR50:
+		freq = UHS_SDR50_MAX_DTR;
+		break;
+	case MMC_TIMING_UHS_SDR104:
+		freq = UHS_SDR104_MAX_DTR;
+		break;
+	case MMC_TIMING_MMC_HS200:
+		freq = MMC_HS200_MAX_DTR;
+		break;
+	case MMC_TIMING_UHS_DDR50:
+		freq = UHS_DDR50_MAX_DTR;
+		break;
+	default:
+		mmc_host_clk_hold(host);
+		freq = host->ios.clock;
+		mmc_host_clk_release(host);
+		break;
+	}
+
+out:
+	return freq;
+}
+EXPORT_SYMBOL_GPL(mmc_get_max_frequency);
+
+/**
+ * mmc_get_min_frequency() - get min. frequency supported
+ * @host: pointer to mmc host structure
+ *
+ * Returns min. frequency supported by card/host which doesn't impair
+ * performance for most usecases. If the timing mode is SDR50/SDR104/HS200
+ * return 50MHz value. If timing mode is DDR50 return 25MHz so that
+ * throughput would be equivalent to SDR50/SDR104 in 50MHz. Also, allow
+ * host drivers to overwrite the frequency in case they support
+ * "get_min_frequency" host ops.
+ */
+static unsigned long mmc_get_min_frequency(struct mmc_host *host)
+{
+	unsigned long freq;
+
+	if (host->ops && host->ops->get_min_frequency) {
+		freq = host->ops->get_min_frequency(host);
+		goto out;
+	}
+
+	switch (host->ios.timing) {
+	case MMC_TIMING_UHS_SDR50:
+	case MMC_TIMING_UHS_SDR104:
+		freq = UHS_SDR25_MAX_DTR;
+		break;
+	case MMC_TIMING_MMC_HS200:
+		freq = MMC_HIGH_52_MAX_DTR;
+		break;
+	case MMC_TIMING_UHS_DDR50:
+		freq = UHS_DDR50_MAX_DTR / 2;
+		break;
+	default:
+		mmc_host_clk_hold(host);
+		freq = host->ios.clock;
+		mmc_host_clk_release(host);
+		break;
+	}
+
+out:
+	return freq;
+}
+
+/*
+ * Scale down clocks to minimum frequency supported.
+ * The delayed work re-arms itself in case it cannot
+ * claim the host.
+ */
+static void mmc_clk_scale_work(struct work_struct *work)
+{
+	struct mmc_host *host = container_of(work, struct mmc_host,
+					      clk_scaling.work.work);
+
+	if (!host->card || !host->bus_ops ||
+			!host->bus_ops->change_bus_speed ||
+			!host->clk_scaling.enable || !host->ios.clock)
+		goto out;
+
+	if (!mmc_try_claim_host(host)) {
+		/* retry after a timer tick */
+		queue_delayed_work(system_nrt_wq, &host->clk_scaling.work, 1);
+		goto out;
+	}
+
+	mmc_clk_scaling(host, true);
+	mmc_release_host(host);
+out:
+	return;
+}
+
+
+/**
+ * mmc_clk_scaling() - clock scaling decision algorithm
+ * @host:	pointer to mmc host structure
+ * @from_wq:	variable that specifies the context in which
+ *		mmc_clk_scaling() is called.
+ *
+ * Calculate load percentage based on host busy time
+ * and total sampling interval and decide clock scaling
+ * based on scale up/down thresholds.
+ * If load is greater than up threshold increase the
+ * frequency to maximum as supported by host. Else,
+ * if load is less than down threshold, scale down the
+ * frequency to minimum supported by the host. Otherwise,
+ * retain current frequency and do nothing.
+ */
+static void mmc_clk_scaling(struct mmc_host *host, bool from_wq)
+{
+	int err = 0;
+	struct mmc_card *card = host->card;
+	unsigned long total_time_ms = 0;
+	unsigned long busy_time_ms = 0;
+	unsigned long freq;
+	unsigned int up_threshold = host->clk_scaling.up_threshold;
+	unsigned int down_threshold = host->clk_scaling.down_threshold;
+	bool queue_scale_down_work = false;
+
+	if (!card || !host->bus_ops || !host->bus_ops->change_bus_speed) {
+		pr_err("%s: %s: invalid entry\n", mmc_hostname(host), __func__);
+		goto out;
+	}
+
+	/* Check if the clocks are already gated. */
+	if (!host->ios.clock)
+		goto out;
+
+	if (time_is_after_jiffies(host->clk_scaling.window_time +
+			msecs_to_jiffies(host->clk_scaling.polling_delay_ms)))
+		goto out;
+
+	/* handle time wrap */
+	total_time_ms = jiffies_to_msecs((long)jiffies -
+			(long)host->clk_scaling.window_time);
+
+	/* Check if we re-enter during clock switching */
+	if (unlikely(host->clk_scaling.in_progress))
+		goto out;
+
+	host->clk_scaling.in_progress = true;
+
+	busy_time_ms = host->clk_scaling.busy_time_us / USEC_PER_MSEC;
+
+	freq = host->clk_scaling.curr_freq;
+
+	/*
+	 * Note that the max. and min. frequency should be based
+	 * on the timing modes that the card and host handshake
+	 * during initialization.
+	 */
+	if ((busy_time_ms * 100 > total_time_ms * up_threshold)) {
+		freq = mmc_get_max_frequency(host);
+	} else if ((busy_time_ms * 100 < total_time_ms * down_threshold)) {
+		if (!from_wq)
+			queue_scale_down_work = true;
+		freq = mmc_get_min_frequency(host);
+	}
+
+	if (freq != host->clk_scaling.curr_freq) {
+		if (!queue_scale_down_work) {
+			if (!from_wq)
+				cancel_delayed_work_sync(
+						&host->clk_scaling.work);
+			err = host->bus_ops->change_bus_speed(host, &freq);
+			if (!err)
+				host->clk_scaling.curr_freq = freq;
+			else
+				pr_err("%s: %s: failed (%d) at freq=%lu\n",
+					mmc_hostname(host), __func__, err,
+					freq);
+		} else {
+			/*
+			 * We hold claim host while queueing the scale down
+			 * work, so delay atleast one timer tick to release
+			 * host and re-claim while scaling down the clocks.
+			 */
+			queue_delayed_work(system_nrt_wq,
+					&host->clk_scaling.work, 1);
+			host->clk_scaling.in_progress = false;
+			goto out;
+		}
+	}
+
+	mmc_reset_clk_scale_stats(host);
+	host->clk_scaling.in_progress = false;
+out:
+	return;
+}
+
+/**
+ * mmc_disable_clk_scaling() - Disable clock scaling
+ * @host: pointer to mmc host structure
+ *
+ * Disables clock scaling temporarily by setting enable
+ * property to false. To disable completely, one also
+ * need to set 'initialized' variable to false.
+ */
+void mmc_disable_clk_scaling(struct mmc_host *host)
+{
+	cancel_delayed_work_sync(&host->clk_scaling.work);
+	host->clk_scaling.enable = false;
+}
+EXPORT_SYMBOL_GPL(mmc_disable_clk_scaling);
+
+/**
+ * mmc_can_scale_clk() - Check if clock scaling is initialized
+ * @host: pointer to mmc host structure
+ */
+bool mmc_can_scale_clk(struct mmc_host *host)
+{
+	return host->clk_scaling.initialized;
+}
+EXPORT_SYMBOL_GPL(mmc_can_scale_clk);
+
+/**
+ * mmc_init_clk_scaling() - Initialize clock scaling
+ * @host: pointer to mmc host structure
+ *
+ * Initialize clock scaling for supported hosts.
+ * It is assumed that the caller ensure clock is
+ * running at maximum possible frequency before
+ * calling this function.
+ */
+void mmc_init_clk_scaling(struct mmc_host *host)
+{
+	if (!host->card || !(host->caps2 & MMC_CAP2_CLK_SCALE))
+		return;
+
+	INIT_DELAYED_WORK(&host->clk_scaling.work, mmc_clk_scale_work);
+	host->clk_scaling.curr_freq = mmc_get_max_frequency(host);
+	mmc_reset_clk_scale_stats(host);
+	host->clk_scaling.enable = true;
+	host->clk_scaling.initialized = true;
+	pr_debug("%s: clk scaling enabled\n", mmc_hostname(host));
+}
+EXPORT_SYMBOL_GPL(mmc_init_clk_scaling);
+
+/**
+ * mmc_exit_clk_scaling() - Disable clock scaling
+ * @host: pointer to mmc host structure
+ *
+ * Disable clock scaling permanently.
+ */
+void mmc_exit_clk_scaling(struct mmc_host *host)
+{
+	cancel_delayed_work_sync(&host->clk_scaling.work);
+	memset(&host->clk_scaling, 0, sizeof(host->clk_scaling));
+}
+EXPORT_SYMBOL_GPL(mmc_exit_clk_scaling);
+
 static int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq)
 {
 	host->f_init = freq;
@@ -2639,7 +2942,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;
diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h
index 85d2737..c85f5aa 100644
--- a/drivers/mmc/core/core.h
+++ b/drivers/mmc/core/core.h
@@ -25,6 +25,7 @@
 	int (*power_save)(struct mmc_host *);
 	int (*power_restore)(struct mmc_host *);
 	int (*alive)(struct mmc_host *);
+	int (*change_bus_speed)(struct mmc_host *, unsigned long *);
 };
 
 void mmc_attach_bus(struct mmc_host *host, const struct mmc_bus_ops *ops);
@@ -80,5 +81,11 @@
 void mmc_add_card_debugfs(struct mmc_card *card);
 void mmc_remove_card_debugfs(struct mmc_card *card);
 
+extern void mmc_disable_clk_scaling(struct mmc_host *host);
+extern bool mmc_can_scale_clk(struct mmc_host *host);
+extern void mmc_init_clk_scaling(struct mmc_host *host);
+extern void mmc_exit_clk_scaling(struct mmc_host *host);
+extern void mmc_reset_clk_scale_stats(struct mmc_host *host);
+extern unsigned long mmc_get_max_frequency(struct mmc_host *host);
 #endif
 
diff --git a/drivers/mmc/core/debugfs.c b/drivers/mmc/core/debugfs.c
index ddb562e..84a26a1 100644
--- a/drivers/mmc/core/debugfs.c
+++ b/drivers/mmc/core/debugfs.c
@@ -186,6 +186,46 @@
 DEFINE_SIMPLE_ATTRIBUTE(mmc_clock_fops, mmc_clock_opt_get, mmc_clock_opt_set,
 	"%llu\n");
 
+static int mmc_max_clock_get(void *data, u64 *val)
+{
+	struct mmc_host *host = data;
+
+	if (!host)
+		return -EINVAL;
+
+	*val = host->f_max;
+
+	return 0;
+}
+
+static int mmc_max_clock_set(void *data, u64 val)
+{
+	struct mmc_host *host = data;
+	int err = -EINVAL;
+	unsigned long freq = val;
+	unsigned int old_freq;
+
+	if (!host || (val < host->f_min))
+		goto out;
+
+	mmc_claim_host(host);
+	if (host->bus_ops && host->bus_ops->change_bus_speed) {
+		old_freq = host->f_max;
+		host->f_max = freq;
+
+		err = host->bus_ops->change_bus_speed(host, &freq);
+
+		if (err)
+			host->f_max = old_freq;
+	}
+	mmc_release_host(host);
+out:
+	return err;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(mmc_max_clock_fops, mmc_max_clock_get,
+		mmc_max_clock_set, "%llu\n");
+
 void mmc_add_host_debugfs(struct mmc_host *host)
 {
 	struct dentry *root;
@@ -208,6 +248,10 @@
 			&mmc_clock_fops))
 		goto err_node;
 
+	if (!debugfs_create_file("max_clock", S_IRUSR | S_IWUSR, root, host,
+		&mmc_max_clock_fops))
+		goto err_node;
+
 #ifdef CONFIG_MMC_CLKGATE
 	if (!debugfs_create_u32("clk_delay", (S_IRUSR | S_IWUSR),
 				root, &host->clk_delay))
diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c
index 850872d..d30f10f 100644
--- a/drivers/mmc/core/host.c
+++ b/drivers/mmc/core/host.c
@@ -163,6 +163,9 @@
 	if (host->clk_gated) {
 		spin_unlock_irqrestore(&host->clk_lock, flags);
 		mmc_ungate_clock(host);
+
+		/* Reset clock scaling stats as host is out of idle */
+		mmc_reset_clk_scale_stats(host);
 		spin_lock_irqsave(&host->clk_lock, flags);
 		pr_debug("%s: ungated MCI clock\n", mmc_hostname(host));
 	}
@@ -446,6 +449,10 @@
 #endif
 	mmc_host_clk_sysfs_init(host);
 
+	host->clk_scaling.up_threshold = 35;
+	host->clk_scaling.down_threshold = 5;
+	host->clk_scaling.polling_delay_ms = 100;
+
 	err = sysfs_create_group(&host->parent->kobj, &dev_attr_grp);
 	if (err)
 		pr_err("%s: failed to create sysfs group with err %d\n",
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
index 47fd9b9..d3dc133 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -479,11 +479,11 @@
 				else
 					card->ext_csd.bkops_en = 1;
 			}
-			if (!card->ext_csd.bkops_en)
-				pr_info("%s: BKOPS_EN bit is not set\n",
-					mmc_hostname(card->host));
 		}
 
+		pr_info("%s: BKOPS_EN bit = %d\n",
+			mmc_hostname(card->host), card->ext_csd.bkops_en);
+
 		/* check whether the eMMC card supports HPI */
 		if (ext_csd[EXT_CSD_HPI_FEATURES] & 0x1) {
 			card->ext_csd.hpi = 1;
@@ -821,6 +821,69 @@
 	return err;
 }
 
+/**
+ * mmc_change_bus_speed() - Change MMC card bus frequency at runtime
+ * @host: pointer to mmc host structure
+ * @freq: pointer to desired frequency to be set
+ *
+ * Change the MMC card bus frequency at runtime after the card is
+ * initialized. Callers are expected to make sure of the card's
+ * state (DATA/RCV/TRANSFER) beforing changing the frequency at runtime.
+ *
+ * If the frequency to change is greater than max. supported by card,
+ * *freq is changed to max. supported by card and if it is less than min.
+ * supported by host, *freq is changed to min. supported by host.
+ */
+static int mmc_change_bus_speed(struct mmc_host *host, unsigned long *freq)
+{
+	int err = 0;
+	struct mmc_card *card;
+
+	mmc_claim_host(host);
+	/*
+	 * Assign card pointer after claiming host to avoid race
+	 * conditions that may arise during removal of the card.
+	 */
+	card = host->card;
+
+	if (!card || !freq) {
+		err = -EINVAL;
+		goto out;
+	}
+
+	if (mmc_card_highspeed(card) || mmc_card_hs200(card)
+			|| mmc_card_ddr_mode(card)) {
+		if (*freq > card->ext_csd.hs_max_dtr)
+			*freq = card->ext_csd.hs_max_dtr;
+	} else if (*freq > card->csd.max_dtr) {
+		*freq = card->csd.max_dtr;
+	}
+
+	if (*freq < host->f_min)
+		*freq = host->f_min;
+
+	mmc_set_clock(host, (unsigned int) (*freq));
+
+	if (mmc_card_hs200(card) && card->host->ops->execute_tuning) {
+		/*
+		 * We try to probe host driver for tuning for any
+		 * frequency, it is host driver responsibility to
+		 * perform actual tuning only when required.
+		 */
+		mmc_host_clk_hold(card->host);
+		err = card->host->ops->execute_tuning(card->host,
+				MMC_SEND_TUNING_BLOCK_HS200);
+		mmc_host_clk_release(card->host);
+
+		if (err)
+			pr_warn("%s: %s: tuning execution failed %d\n",
+				   mmc_hostname(card->host), __func__, err);
+	}
+out:
+	mmc_release_host(host);
+	return err;
+}
+
 /*
  * Handle the detection and initialisation of a card.
  *
@@ -1397,6 +1460,7 @@
 
 	mmc_claim_host(host);
 	host->card = NULL;
+	mmc_exit_clk_scaling(host);
 	mmc_release_host(host);
 }
 
@@ -1447,6 +1511,12 @@
 	BUG_ON(!host);
 	BUG_ON(!host->card);
 
+	/*
+	 * Disable clock scaling before suspend and enable it after resume so
+	 * as to avoid clock scaling decisions kicking in during this window.
+	 */
+	mmc_disable_clk_scaling(host);
+
 	mmc_claim_host(host);
 	if (mmc_can_poweroff_notify(host->card))
 		err = mmc_poweroff_notify(host->card, EXT_CSD_POWER_OFF_SHORT);
@@ -1477,6 +1547,13 @@
 	err = mmc_init_card(host, host->ocr, host->card);
 	mmc_release_host(host);
 
+	/*
+	 * We have done full initialization of the card,
+	 * reset the clk scale stats and current frequency.
+	 */
+	if (mmc_can_scale_clk(host))
+		mmc_init_clk_scaling(host);
+
 	return err;
 }
 
@@ -1484,11 +1561,17 @@
 {
 	int ret;
 
+	/* Disable clk scaling to avoid switching frequencies intermittently */
+	mmc_disable_clk_scaling(host);
+
 	host->card->state &= ~(MMC_STATE_HIGHSPEED | MMC_STATE_HIGHSPEED_200);
 	mmc_claim_host(host);
 	ret = mmc_init_card(host, host->ocr, host->card);
 	mmc_release_host(host);
 
+	if (mmc_can_scale_clk(host))
+		mmc_init_clk_scaling(host);
+
 	return ret;
 }
 
@@ -1531,6 +1614,7 @@
 	.resume = NULL,
 	.power_restore = mmc_power_restore,
 	.alive = mmc_alive,
+	.change_bus_speed = mmc_change_bus_speed,
 };
 
 static const struct mmc_bus_ops mmc_ops_unsafe = {
@@ -1542,6 +1626,7 @@
 	.resume = mmc_resume,
 	.power_restore = mmc_power_restore,
 	.alive = mmc_alive,
+	.change_bus_speed = mmc_change_bus_speed,
 };
 
 static void mmc_attach_bus_ops(struct mmc_host *host)
@@ -1621,6 +1706,10 @@
 	if (err)
 		goto remove_card;
 
+	/* Initialize clock scaling only for high frequency modes */
+	if (mmc_card_hs200(host->card) || mmc_card_ddr_mode(host->card))
+		mmc_init_clk_scaling(host);
+
 	return 0;
 
 remove_card:
diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
index ff5821b..318d590 100644
--- a/drivers/mmc/core/sd.c
+++ b/drivers/mmc/core/sd.c
@@ -609,6 +609,72 @@
 	return 0;
 }
 
+/**
+ * mmc_sd_change_bus_speed() - Change SD card bus frequency at runtime
+ * @host: pointer to mmc host structure
+ * @freq: pointer to desired frequency to be set
+ *
+ * Change the SD card bus frequency at runtime after the card is
+ * initialized. Callers are expected to make sure of the card's
+ * state (DATA/RCV/TRANSFER) beforing changing the frequency at runtime.
+ *
+ * If the frequency to change is greater than max. supported by card,
+ * *freq is changed to max. supported by card and if it is less than min.
+ * supported by host, *freq is changed to min. supported by host.
+ */
+static int mmc_sd_change_bus_speed(struct mmc_host *host, unsigned long *freq)
+{
+	int err = 0;
+	struct mmc_card *card;
+
+	mmc_claim_host(host);
+	/*
+	 * Assign card pointer after claiming host to avoid race
+	 * conditions that may arise during removal of the card.
+	 */
+	card = host->card;
+
+	/* sanity checks */
+	if (!card || !freq) {
+		err = -EINVAL;
+		goto out;
+	}
+
+	if (mmc_card_uhs(card)) {
+		if (*freq > card->sw_caps.uhs_max_dtr)
+			*freq = card->sw_caps.uhs_max_dtr;
+	} else {
+		if (*freq > mmc_sd_get_max_clock(card))
+			*freq = mmc_sd_get_max_clock(card);
+	}
+
+	if (*freq < host->f_min)
+		*freq = host->f_min;
+
+	mmc_set_clock(host, (unsigned int) (*freq));
+
+	if (!mmc_host_is_spi(card->host) && mmc_sd_card_uhs(card)
+			&& card->host->ops->execute_tuning) {
+		/*
+		 * We try to probe host driver for tuning for any
+		 * frequency, it is host driver responsibility to
+		 * perform actual tuning only when required.
+		 */
+		mmc_host_clk_hold(card->host);
+		err = card->host->ops->execute_tuning(card->host,
+				MMC_SEND_TUNING_BLOCK);
+		mmc_host_clk_release(card->host);
+
+		if (err)
+			pr_warn("%s: %s: tuning execution failed %d\n",
+				   mmc_hostname(card->host), __func__, err);
+	}
+
+out:
+	mmc_release_host(host);
+	return err;
+}
+
 /*
  * UHS-I specific initialization procedure
  */
@@ -1055,6 +1121,7 @@
 
 	mmc_claim_host(host);
 	host->card = NULL;
+	mmc_exit_clk_scaling(host);
 	mmc_release_host(host);
 }
 
@@ -1121,6 +1188,12 @@
 	BUG_ON(!host);
 	BUG_ON(!host->card);
 
+	/*
+	 * Disable clock scaling before suspend and enable it after resume so
+	 * as to avoid clock scaling decisions kicking in during this window.
+	 */
+	mmc_disable_clk_scaling(host);
+
 	mmc_claim_host(host);
 	if (!mmc_host_is_spi(host))
 		mmc_deselect_cards(host);
@@ -1169,6 +1242,13 @@
 #endif
 	mmc_release_host(host);
 
+	/*
+	 * We have done full initialization of the card,
+	 * reset the clk scale stats and current frequency.
+	 */
+	if (mmc_can_scale_clk(host))
+		mmc_init_clk_scaling(host);
+
 	return err;
 }
 
@@ -1176,11 +1256,17 @@
 {
 	int ret;
 
+	/* Disable clk scaling to avoid switching frequencies intermittently */
+	mmc_disable_clk_scaling(host);
+
 	host->card->state &= ~MMC_STATE_HIGHSPEED;
 	mmc_claim_host(host);
 	ret = mmc_sd_init_card(host, host->ocr, host->card);
 	mmc_release_host(host);
 
+	if (mmc_can_scale_clk(host))
+		mmc_init_clk_scaling(host);
+
 	return ret;
 }
 
@@ -1191,6 +1277,7 @@
 	.resume = NULL,
 	.power_restore = mmc_sd_power_restore,
 	.alive = mmc_sd_alive,
+	.change_bus_speed = mmc_sd_change_bus_speed,
 };
 
 static const struct mmc_bus_ops mmc_sd_ops_unsafe = {
@@ -1200,6 +1287,7 @@
 	.resume = mmc_sd_resume,
 	.power_restore = mmc_sd_power_restore,
 	.alive = mmc_sd_alive,
+	.change_bus_speed = mmc_sd_change_bus_speed,
 };
 
 static void mmc_sd_attach_bus_ops(struct mmc_host *host)
@@ -1317,6 +1405,10 @@
 	if (err)
 		goto remove_card;
 
+	/* Initialize clock scaling only for high frequency modes */
+	if (mmc_card_uhs(host->card))
+		mmc_init_clk_scaling(host);
+
 	return 0;
 
 remove_card:
diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c
index 81a4ba0..4e76f61 100644
--- a/drivers/mmc/core/sdio.c
+++ b/drivers/mmc/core/sdio.c
@@ -162,10 +162,7 @@
 			if (ret)
 				goto out;
 
-			if (card->host->caps &
-				(MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 |
-				 MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR104 |
-				 MMC_CAP_UHS_DDR50)) {
+			if (mmc_host_uhs(card->host)) {
 				if (data & SDIO_UHS_DDR50)
 					card->sw_caps.sd3_bus_mode
 						|= SD_MODE_UHS_DDR50;
@@ -480,8 +477,7 @@
 	 * If the host doesn't support any of the UHS-I modes, fallback on
 	 * default speed.
 	 */
-	if (!(card->host->caps & (MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 |
-	    MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR104 | MMC_CAP_UHS_DDR50)))
+	if (!mmc_host_uhs(card->host))
 		return 0;
 
 	bus_speed = SDIO_SPEED_SDR12;
@@ -491,23 +487,27 @@
 			bus_speed = SDIO_SPEED_SDR104;
 			timing = MMC_TIMING_UHS_SDR104;
 			card->sw_caps.uhs_max_dtr = UHS_SDR104_MAX_DTR;
+			card->sd_bus_speed = UHS_SDR104_BUS_SPEED;
 	} else if ((card->host->caps & MMC_CAP_UHS_DDR50) &&
 		   (card->sw_caps.sd3_bus_mode & SD_MODE_UHS_DDR50)) {
 			bus_speed = SDIO_SPEED_DDR50;
 			timing = MMC_TIMING_UHS_DDR50;
 			card->sw_caps.uhs_max_dtr = UHS_DDR50_MAX_DTR;
+			card->sd_bus_speed = UHS_DDR50_BUS_SPEED;
 	} else if ((card->host->caps & (MMC_CAP_UHS_SDR104 |
 		    MMC_CAP_UHS_SDR50)) && (card->sw_caps.sd3_bus_mode &
 		    SD_MODE_UHS_SDR50)) {
 			bus_speed = SDIO_SPEED_SDR50;
 			timing = MMC_TIMING_UHS_SDR50;
 			card->sw_caps.uhs_max_dtr = UHS_SDR50_MAX_DTR;
+			card->sd_bus_speed = UHS_SDR50_BUS_SPEED;
 	} else if ((card->host->caps & (MMC_CAP_UHS_SDR104 |
 		    MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR25)) &&
 		   (card->sw_caps.sd3_bus_mode & SD_MODE_UHS_SDR25)) {
 			bus_speed = SDIO_SPEED_SDR25;
 			timing = MMC_TIMING_UHS_SDR25;
 			card->sw_caps.uhs_max_dtr = UHS_SDR25_MAX_DTR;
+			card->sd_bus_speed = UHS_SDR25_BUS_SPEED;
 	} else if ((card->host->caps & (MMC_CAP_UHS_SDR104 |
 		    MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR25 |
 		    MMC_CAP_UHS_SDR12)) && (card->sw_caps.sd3_bus_mode &
@@ -515,6 +515,7 @@
 			bus_speed = SDIO_SPEED_SDR12;
 			timing = MMC_TIMING_UHS_SDR12;
 			card->sw_caps.uhs_max_dtr = UHS_SDR12_MAX_DTR;
+			card->sd_bus_speed = UHS_SDR12_BUS_SPEED;
 	}
 
 	err = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_SPEED, 0, &speed);
@@ -653,11 +654,7 @@
 	 * systems that claim 1.8v signalling in fact do not support
 	 * it.
 	 */
-	if ((ocr & R4_18V_PRESENT) &&
-		(host->caps &
-			(MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 |
-			 MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR104 |
-			 MMC_CAP_UHS_DDR50))) {
+	if ((ocr & R4_18V_PRESENT) && mmc_host_uhs(host)) {
 		err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180,
 				true);
 		if (err) {
@@ -964,10 +961,12 @@
 	mmc_claim_host(host);
 
 	/* No need to reinitialize powered-resumed nonremovable cards */
-	if (mmc_card_is_removable(host) || !mmc_card_keep_power(host))
+	if (mmc_card_is_removable(host) || !mmc_card_keep_power(host)) {
+		sdio_reset(host);
+		mmc_go_idle(host);
 		err = mmc_sdio_init_card(host, host->ocr, host->card,
 					mmc_card_keep_power(host));
-	else if (mmc_card_keep_power(host) && mmc_card_wake_sdio_irq(host)) {
+	} else if (mmc_card_keep_power(host) && mmc_card_wake_sdio_irq(host)) {
 		/* We may have switched to 1-bit mode during suspend */
 		err = sdio_enable_4bit_bus(host->card);
 		if (err > 0) {
@@ -1054,6 +1053,10 @@
 		goto out;
 	}
 
+	if (mmc_host_uhs(host))
+		/* to query card if 1.8V signalling is supported */
+		host->ocr |= R4_18V_PRESENT;
+
 	ret = mmc_sdio_init_card(host, host->ocr, host->card,
 				mmc_card_keep_power(host));
 	if (!ret && host->sdio_irqs)
@@ -1119,6 +1122,10 @@
 	/*
 	 * Detect and init the card.
 	 */
+	if (mmc_host_uhs(host))
+		/* to query card if 1.8V signalling is supported */
+		host->ocr |= R4_18V_PRESENT;
+
 	err = mmc_sdio_init_card(host, host->ocr, NULL, 0);
 	if (err) {
 		if (err == -EAGAIN) {
@@ -1236,79 +1243,6 @@
 
 int sdio_reset_comm(struct mmc_card *card)
 {
-	struct mmc_host *host = card->host;
-	u32 ocr;
-	int err;
-
-	printk("%s():\n", __func__);
-	mmc_claim_host(host);
-
-	mmc_go_idle(host);
-
-	mmc_set_clock(host, host->f_min);
-
-	err = mmc_send_io_op_cond(host, 0, &ocr);
-	if (err)
-		goto err;
-
-	host->ocr = mmc_select_voltage(host, ocr);
-	if (!host->ocr) {
-		err = -EINVAL;
-		goto err;
-	}
-
-	err = mmc_send_io_op_cond(host, host->ocr, &ocr);
-	if (err)
-		goto err;
-
-	if (mmc_host_is_spi(host)) {
-		err = mmc_spi_set_crc(host, use_spi_crc);
-		if (err)
-			goto err;
-	}
-
-	if (!mmc_host_is_spi(host)) {
-		err = mmc_send_relative_addr(host, &card->rca);
-		if (err)
-			goto err;
-		mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL);
-	}
-	if (!mmc_host_is_spi(host)) {
-		err = mmc_select_card(card);
-		if (err)
-			goto err;
-	}
-
-	/*
-	 * Switch to high-speed (if supported).
-	 */
-	err = sdio_enable_hs(card);
-	if (err > 0)
-		mmc_sd_go_highspeed(card);
-	else if (err)
-		goto err;
-
-	/*
-	 * Change to the card's maximum speed.
-	 */
-	mmc_set_clock(host, mmc_sdio_get_max_clock(card));
-
-	err = sdio_enable_4bit_bus(card);
-	if (err > 0) {
-		if (host->caps & MMC_CAP_8_BIT_DATA)
-			mmc_set_bus_width(host, MMC_BUS_WIDTH_8);
-		else if (host->caps & MMC_CAP_4_BIT_DATA)
-			mmc_set_bus_width(host, MMC_BUS_WIDTH_4);
-	}
-	else if (err)
-		goto err;
-
-	mmc_release_host(host);
-	return 0;
-err:
-	printk("%s: Error resetting SDIO communications (%d)\n",
-	       mmc_hostname(host), err);
-	mmc_release_host(host);
-	return err;
+	return mmc_power_restore_host(card->host);
 }
 EXPORT_SYMBOL(sdio_reset_comm);
diff --git a/drivers/mmc/host/msm_sdcc.c b/drivers/mmc/host/msm_sdcc.c
index 29413ab..c8b47b9 100644
--- a/drivers/mmc/host/msm_sdcc.c
+++ b/drivers/mmc/host/msm_sdcc.c
@@ -46,6 +46,7 @@
 #include <linux/regulator/consumer.h>
 #include <linux/slab.h>
 #include <linux/pm_qos.h>
+#include <linux/iopoll.h>
 
 #include <asm/cacheflush.h>
 #include <asm/div64.h>
@@ -76,6 +77,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 *);
@@ -158,6 +160,9 @@
 static void msmsdcc_sg_start(struct msmsdcc_host *host);
 static int msmsdcc_vreg_reset(struct msmsdcc_host *host);
 static int msmsdcc_runtime_resume(struct device *dev);
+static int msmsdcc_dt_get_array(struct device *dev, const char *prop_name,
+		u32 **out_array, int *len, int size);
+static int msmsdcc_execute_tuning(struct mmc_host *mmc, u32 opcode);
 
 static inline unsigned short msmsdcc_get_nr_sg(struct msmsdcc_host *host)
 {
@@ -213,9 +218,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.
@@ -223,43 +228,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.
@@ -277,6 +303,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);
@@ -296,6 +327,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);
@@ -314,23 +350,23 @@
 	 * SDCC controller itself can support hard reset.
 	 */
 	if (is_sw_hard_reset(host)) {
-		ktime_t start;
+		u32 pwr;
 
 		writel_relaxed(readl_relaxed(host->base + MMCIPOWER)
 				| MCI_SW_RST, host->base + MMCIPOWER);
 		msmsdcc_sync_reg_wr(host);
 
-		start = ktime_get();
-		while (readl_relaxed(host->base + MMCIPOWER) & MCI_SW_RST) {
-			/*
-			 * See comment in msmsdcc_soft_reset() on choosing 1ms
-			 * poll timeout.
-			 */
-			if (ktime_to_us(ktime_sub(ktime_get(), start)) > 1000) {
-				pr_err("%s: %s failed\n",
-					mmc_hostname(host->mmc), __func__);
-				BUG();
-			}
+		/*
+		 * See comment in msmsdcc_soft_reset() on choosing 1ms
+		 * poll timeout.
+		 */
+		ret = readl_poll_timeout_noirq(host->base + MMCIPOWER,
+				pwr, !(pwr & MCI_SW_RST), 100, 10);
+
+		if (ret) {
+			pr_err("%s: %s failed (%d)\n",
+			mmc_hostname(host->mmc), __func__, ret);
+			BUG();
 		}
 	} else {
 		ret = clk_reset(host->clk, CLK_RESET_ASSERT);
@@ -354,25 +390,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;
@@ -436,7 +472,9 @@
 					readl_relaxed(host->base + MMCISTATUS)
 					& MCI_TXACTIVE ? "TX" : "RX");
 				msmsdcc_dump_sdcc_state(host);
-				BUG();
+				msmsdcc_reset_and_restore(host);
+				host->pending_dpsm_reset = false;
+				goto out;
 			}
 		}
 	}
@@ -913,7 +951,7 @@
 	if ((host->dma.channel == -1) || (host->dma.crci == -1))
 		return -ENOENT;
 
-	BUG_ON((host->pdev_id < 1) || (host->pdev_id > 5));
+	BUG_ON((host->pdev->id < 1) || (host->pdev->id > 5));
 
 	host->dma.sg = data->sg;
 	host->dma.num_ents = data->sg_len;
@@ -1164,8 +1202,9 @@
 		*c |= MCI_CSPM_DATCMD;
 
 	/* Check if AUTO CMD19/CMD21 is required or not? */
-	if (host->tuning_needed &&
-		(host->en_auto_cmd19 || host->en_auto_cmd21)) {
+	if (host->tuning_needed && (cmd->mrq->data &&
+	    (cmd->mrq->data->flags & MMC_DATA_READ)) &&
+	    (host->en_auto_cmd19 || host->en_auto_cmd21)) {
 		/*
 		 * For open ended block read operation (without CMD23),
 		 * AUTO_CMD19/AUTO_CMD21 bit should be set while sending
@@ -1179,7 +1218,8 @@
 				MMC_READ_MULTIPLE_BLOCK) ||
 			(!host->curr.mrq->sbc &&
 			(cmd->opcode == MMC_READ_SINGLE_BLOCK ||
-			cmd->opcode == MMC_READ_MULTIPLE_BLOCK))) {
+			cmd->opcode == MMC_READ_MULTIPLE_BLOCK ||
+			cmd->opcode == SD_IO_RW_EXTENDED))) {
 			msmsdcc_enable_cdr_cm_sdc4_dll(host);
 			if (host->en_auto_cmd19 &&
 			    host->mmc->ios.timing == MMC_TIMING_UHS_SDR104)
@@ -1348,6 +1388,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));
@@ -1376,6 +1419,10 @@
 			else
 				data->error = -ETIMEDOUT;
 		}
+		/* In case of DATA CRC/timeout error, execute tuning again */
+		if (host->tuning_needed && !host->tuning_in_progress)
+			host->tuning_done = false;
+
 	} else if (status & MCI_RXOVERRUN) {
 		pr_err("%s: RX overrun\n", mmc_hostname(host->mmc));
 		data->error = -EIO;
@@ -1726,6 +1773,10 @@
 		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;
+		if (host->tuning_needed)
+			host->tuning_done = false;
 		cmd->error = -EILSEQ;
 	}
 
@@ -1822,9 +1873,10 @@
 				 */
 				wake_lock(&host->sdio_wlock);
 			} else {
-				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));
+				if (!mmc->card || (mmc->card &&
+				    !mmc_card_sdio(mmc->card))) {
+					pr_warning("%s: SDCC core interrupt received for non-SDIO cards when SDCC clocks are off\n",
+					   mmc_hostname(mmc));
 					ret = 1;
 					break;
 				}
@@ -1856,9 +1908,10 @@
 #endif
 
 		if (status & MCI_SDIOINTROPE) {
-			if (mmc->card && !mmc_card_sdio(mmc->card)) {
-				WARN(1, "%s: SDIO interrupt received for non-SDIO card\n",
-					mmc_hostname(mmc));
+			if (!mmc->card || (mmc->card &&
+			    !mmc_card_sdio(mmc->card))) {
+				pr_warning("%s: SDIO interrupt (SDIOINTROPE) received for non-SDIO card\n",
+					   mmc_hostname(mmc));
 				ret = 1;
 				break;
 			}
@@ -2086,6 +2139,7 @@
 {
 	struct msmsdcc_host *host = mmc_priv(mmc);
 	unsigned long		flags;
+	int retries = 5;
 
 	/*
 	 * Get the SDIO AL client out of LPM.
@@ -2094,10 +2148,30 @@
 	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);
+		}
+	}
+
+	/*
+	 * Check if DLL retuning is required? If yes, perform it here before
+	 * starting new request.
+	 */
+	if (host->tuning_needed && !host->tuning_in_progress &&
+	    !host->tuning_done) {
+		pr_debug("%s: %s: execute_tuning for timing mode = %d\n",
+			 mmc_hostname(mmc), __func__, host->mmc->ios.timing);
+		if (host->mmc->ios.timing == MMC_TIMING_UHS_SDR104)
+			msmsdcc_execute_tuning(mmc,
+					       MMC_SEND_TUNING_BLOCK);
+		else if (host->mmc->ios.timing == MMC_TIMING_MMC_HS200)
+			msmsdcc_execute_tuning(mmc,
+					       MMC_SEND_TUNING_BLOCK_HS200);
 	}
 
 	spin_lock_irqsave(&host->lock, flags);
@@ -2118,8 +2192,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);
@@ -2178,7 +2253,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;
 	}
 
@@ -2310,7 +2387,7 @@
 		rc = msmsdcc_vreg_reset(host);
 		if (rc)
 			pr_err("msmsdcc.%d vreg reset failed (%d)\n",
-			       host->pdev_id, rc);
+			       host->pdev->id, rc);
 		goto out;
 	} else {
 		/* Deregister all regulators from regulator framework */
@@ -2940,6 +3017,27 @@
 	int rc = 0;
 	struct msm_bus_scale_pdata *use_cases;
 
+	if (host->pdev->dev.of_node) {
+		struct msm_mmc_bus_voting_data *data;
+		struct device *dev = &host->pdev->dev;
+
+		data = devm_kzalloc(dev,
+			sizeof(struct msm_mmc_bus_voting_data), GFP_KERNEL);
+		if (!data) {
+			dev_err(&host->pdev->dev,
+				"%s: failed to allocate memory\n", __func__);
+			rc = -ENOMEM;
+			goto out;
+		}
+
+		rc = msmsdcc_dt_get_array(dev, "qcom,bus-bw-vectors-bps",
+				&data->bw_vecs, &data->bw_vecs_size, 0);
+		if (!rc) {
+			data->use_cases = msm_bus_cl_get_pdata(host->pdev);
+			host->plat->msm_bus_voting_data = data;
+		}
+	}
+
 	if (host->plat->msm_bus_voting_data &&
 	    host->plat->msm_bus_voting_data->use_cases &&
 	    host->plat->msm_bus_voting_data->bw_vecs &&
@@ -2962,7 +3060,7 @@
 		host->msm_bus_vote.max_bw_vote =
 				msmsdcc_msm_bus_get_vote_for_bw(host, UINT_MAX);
 	}
-
+out:
 	return rc;
 }
 
@@ -3197,8 +3295,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
@@ -3259,10 +3370,24 @@
 		/* Card clock frequency must be > 100MHz to enable tuning */
 		clk |= (4 << 14);
 		host->tuning_needed = 1;
-	} else if (ios->timing == MMC_TIMING_UHS_DDR50) {
-		clk |= (3 << 14);
 	} else {
-		clk |= (2 << 14); /* feedback clock */
+		if (ios->timing == MMC_TIMING_UHS_DDR50)
+			clk |= (3 << 14);
+		else
+			clk |= (2 << 14); /* feedback clock */
+
+		host->tuning_done = false;
+		if (atomic_read(&host->clks_on)) {
+			/* Write 1 to DLL_RST bit of MCI_DLL_CONFIG register */
+			writel_relaxed((readl_relaxed(host->base +
+					MCI_DLL_CONFIG) | MCI_DLL_RST),
+					host->base + MCI_DLL_CONFIG);
+
+			/* Write 1 to DLL_PDN bit of MCI_DLL_CONFIG register */
+			writel_relaxed((readl_relaxed(host->base +
+					MCI_DLL_CONFIG) | MCI_DLL_PDN),
+					host->base + MCI_DLL_CONFIG);
+		}
 	}
 
 	/* Select free running MCLK as input clock of cm_dll_sdc4 */
@@ -3980,6 +4105,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__);
 
@@ -4013,7 +4139,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};
@@ -4045,9 +4177,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);
 
@@ -4066,6 +4205,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 {
@@ -4081,6 +4222,8 @@
 out:
 	spin_lock_irqsave(&host->lock, flags);
 	host->tuning_in_progress = 0;
+	if (!rc)
+		host->tuning_done = true;
 	spin_unlock_irqrestore(&host->lock, flags);
 exit:
 	pr_debug("%s: Exit %s\n", mmc_hostname(mmc), __func__);
@@ -4586,11 +4729,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));
@@ -5006,6 +5146,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),
@@ -5173,7 +5317,7 @@
 	pull_data->on = pull;
 	pull_data->off = pull + pull_data->size;
 
-	ret = msmsdcc_dt_get_array(dev, "qcom,sdcc-pad-pull-on",
+	ret = msmsdcc_dt_get_array(dev, "qcom,pad-pull-on",
 			&tmp, &len, pull_data->size);
 	if (!ret) {
 		for (i = 0; i < len; i++) {
@@ -5186,7 +5330,7 @@
 		goto err;
 	}
 
-	ret = msmsdcc_dt_get_array(dev, "qcom,sdcc-pad-pull-off",
+	ret = msmsdcc_dt_get_array(dev, "qcom,pad-pull-off",
 			&tmp, &len, pull_data->size);
 	if (!ret) {
 		for (i = 0; i < len; i++) {
@@ -5251,7 +5395,7 @@
 	drv_data->on = drv;
 	drv_data->off = drv + drv_data->size;
 
-	ret = msmsdcc_dt_get_array(dev, "qcom,sdcc-pad-drv-on",
+	ret = msmsdcc_dt_get_array(dev, "qcom,pad-drv-on",
 			&tmp, &len, drv_data->size);
 	if (!ret) {
 		for (i = 0; i < len; i++) {
@@ -5264,7 +5408,7 @@
 		goto err;
 	}
 
-	ret = msmsdcc_dt_get_array(dev, "qcom,sdcc-pad-drv-off",
+	ret = msmsdcc_dt_get_array(dev, "qcom,pad-drv-off",
 			&tmp, &len, drv_data->size);
 	if (!ret) {
 		for (i = 0; i < len; i++) {
@@ -5344,7 +5488,7 @@
 			char result[32];
 			pin_data->gpio_data->gpio[i].no = of_get_gpio(np, i);
 			of_property_read_string_index(np,
-					"qcom,sdcc-gpio-names", i, &name);
+					"qcom,gpio-names", i, &name);
 
 			snprintf(result, 32, "%s-%s",
 					dev_name(dev), name ? name : "?");
@@ -5403,17 +5547,17 @@
 		vreg->name = vreg_name;
 
 		snprintf(prop_name, MAX_PROP_SIZE,
-				"qcom,sdcc-%s-always_on", vreg_name);
+				"qcom,%s-always-on", vreg_name);
 		if (of_get_property(np, prop_name, NULL))
 			vreg->always_on = true;
 
 		snprintf(prop_name, MAX_PROP_SIZE,
-				"qcom,sdcc-%s-lpm_sup", vreg_name);
+				"qcom,%s-lpm-sup", vreg_name);
 		if (of_get_property(np, prop_name, NULL))
 			vreg->lpm_sup = true;
 
 		snprintf(prop_name, MAX_PROP_SIZE,
-				"qcom,sdcc-%s-voltage_level", vreg_name);
+				"qcom,%s-voltage-level", vreg_name);
 		prop = of_get_property(np, prop_name, &len);
 		if (!prop || (len != (2 * sizeof(__be32)))) {
 			dev_warn(dev, "%s %s property\n",
@@ -5424,7 +5568,7 @@
 		}
 
 		snprintf(prop_name, MAX_PROP_SIZE,
-				"qcom,sdcc-%s-current_level", vreg_name);
+				"qcom,%s-current-level", vreg_name);
 		prop = of_get_property(np, prop_name, &len);
 		if (!prop || (len != (2 * sizeof(__be32)))) {
 			dev_warn(dev, "%s %s property\n",
@@ -5460,7 +5604,7 @@
 		goto err;
 	}
 
-	of_property_read_u32(np, "qcom,sdcc-bus-width", &bus_width);
+	of_property_read_u32(np, "qcom,bus-width", &bus_width);
 	if (bus_width == 8) {
 		pdata->mmc_bus_width = MMC_CAP_8_BIT_DATA;
 	} else if (bus_width == 4) {
@@ -5470,7 +5614,7 @@
 		pdata->mmc_bus_width = 0;
 	}
 
-	ret = msmsdcc_dt_get_array(dev, "qcom,sdcc-sup-voltages",
+	ret = msmsdcc_dt_get_array(dev, "qcom,sup-voltages",
 			&sup_voltages, &sup_volt_len, 0);
 	if (!ret) {
 		for (i = 0; i < sup_volt_len; i += 2) {
@@ -5485,7 +5629,7 @@
 		dev_dbg(dev, "OCR mask=0x%x\n", pdata->ocr_mask);
 	}
 
-	ret = msmsdcc_dt_get_array(dev, "qcom,sdcc-clk-rates",
+	ret = msmsdcc_dt_get_array(dev, "qcom,clk-rates",
 			&clk_table, &clk_table_len, 0);
 	if (!ret) {
 		pdata->sup_clk_table = clk_table;
@@ -5510,13 +5654,13 @@
 	if (msmsdcc_dt_parse_gpio_info(dev, pdata))
 		goto err;
 
-	len = of_property_count_strings(np, "qcom,sdcc-bus-speed-mode");
+	len = of_property_count_strings(np, "qcom,bus-speed-mode");
 
 	for (i = 0; i < len; i++) {
 		const char *name = NULL;
 
 		of_property_read_string_index(np,
-			"qcom,sdcc-bus-speed-mode", i, &name);
+			"qcom,bus-speed-mode", i, &name);
 		if (!name)
 			continue;
 
@@ -5542,7 +5686,7 @@
 						| MMC_CAP_UHS_DDR50;
 	}
 
-	of_property_read_u32(np, "qcom,sdcc-current-limit", &current_limit);
+	of_property_read_u32(np, "qcom,current-limit", &current_limit);
 	if (current_limit == 800)
 		pdata->uhs_caps |= MMC_CAP_MAX_CURRENT_800;
 	else if (current_limit == 600)
@@ -5552,12 +5696,14 @@
 	else if (current_limit == 200)
 		pdata->uhs_caps |= MMC_CAP_MAX_CURRENT_200;
 
-	if (of_get_property(np, "qcom,sdcc-xpc", NULL))
+	if (of_get_property(np, "qcom,xpc", NULL))
 		pdata->xpc_cap = true;
-	if (of_get_property(np, "qcom,sdcc-nonremovable", NULL))
+	if (of_get_property(np, "qcom,nonremovable", NULL))
 		pdata->nonremovable = true;
-	if (of_get_property(np, "qcom,sdcc-disable_cmd23", NULL))
+	if (of_get_property(np, "qcom,disable-cmd23", NULL))
 		pdata->disable_cmd23 = true;
+	of_property_read_u32(np, "qcom,dat1-mpm-int",
+					&pdata->mpm_sdiowakeup_int);
 
 	return pdata;
 err:
@@ -5649,7 +5795,7 @@
 	}
 
 	host = mmc_priv(mmc);
-	host->pdev_id = pdev->id;
+	host->pdev = pdev;
 	host->plat = plat;
 	host->mmc = mmc;
 	host->curr.cmd = NULL;
@@ -5750,6 +5896,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
@@ -5841,6 +5988,7 @@
 	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;
@@ -5885,6 +6033,14 @@
 	disable_irq(core_irqres->start);
 	host->sdcc_irq_disabled = 1;
 
+	if (!plat->sdiowakeup_irq) {
+		/* Check if registered as IORESOURCE_IRQ */
+		plat->sdiowakeup_irq =
+			platform_get_irq_byname(pdev, "sdiowakeup_irq");
+		if (plat->sdiowakeup_irq < 0)
+			plat->sdiowakeup_irq = 0;
+	}
+
 	if (plat->sdiowakeup_irq) {
 		wake_lock_init(&host->sdio_wlock, WAKE_LOCK_SUSPEND,
 				mmc_hostname(mmc));
@@ -6131,7 +6287,7 @@
  vreg_deinit:
 	msmsdcc_vreg_init(host, false);
  clk_disable:
-	clk_disable(host->clk);
+	clk_disable_unprepare(host->clk);
 	msmsdcc_msm_bus_unregister(host);
  pm_qos_remove:
 	if (host->cpu_dma_latency)
diff --git a/drivers/mmc/host/msm_sdcc.h b/drivers/mmc/host/msm_sdcc.h
index c66d1a5..500b5fb 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;
 };
 
@@ -347,7 +346,7 @@
 	void __iomem		*dml_base;
 	void __iomem		*bam_base;
 
-	int			pdev_id;
+	struct platform_device	*pdev;
 
 	struct msmsdcc_curr_req	curr;
 
@@ -402,6 +401,7 @@
 	bool io_pad_pwr_switch;
 	bool tuning_in_progress;
 	bool tuning_needed;
+	bool tuning_done;
 	bool en_auto_cmd19;
 	bool en_auto_cmd21;
 	bool sdio_gpio_lpm;
@@ -428,6 +428,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 +447,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 +462,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 +496,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-pci.c b/drivers/mmc/host/sdhci-pci.c
index b34b069..e7a3741 100644
--- a/drivers/mmc/host/sdhci-pci.c
+++ b/drivers/mmc/host/sdhci-pci.c
@@ -23,8 +23,9 @@
 #include <linux/scatterlist.h>
 #include <linux/io.h>
 #include <linux/gpio.h>
-#include <linux/pm_runtime.h>
 #include <linux/mmc/sdhci-pci-data.h>
+#include <linux/sfi.h>
+#include <linux/pm_runtime.h>
 
 #include "sdhci.h"
 
@@ -1451,6 +1452,8 @@
 	int i;
 	struct sdhci_pci_chip *chip;
 
+	sdhci_pci_runtime_pm_forbid(&pdev->dev);
+
 	chip = pci_get_drvdata(pdev);
 
 	if (chip) {
diff --git a/drivers/mmc/host/sdhci-s3c.c b/drivers/mmc/host/sdhci-s3c.c
index 55a164f..425d092 100644
--- a/drivers/mmc/host/sdhci-s3c.c
+++ b/drivers/mmc/host/sdhci-s3c.c
@@ -672,6 +672,7 @@
 }
 
 #ifdef CONFIG_PM_SLEEP
+
 static int sdhci_s3c_suspend(struct device *dev)
 {
 	struct sdhci_host *host = dev_get_drvdata(dev);
@@ -712,6 +713,13 @@
 
 #define SDHCI_S3C_PMOPS (&sdhci_s3c_pmops)
 
+static const struct dev_pm_ops sdhci_s3c_pmops = {
+	.suspend	= sdhci_s3c_suspend,
+	.resume		= sdhci_s3c_resume,
+};
+
+#define SDHCI_S3C_PMOPS (&sdhci_s3c_pmops)
+
 #else
 #define SDHCI_S3C_PMOPS NULL
 #endif
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 43f7e77..6451d62 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -21,6 +21,7 @@
 #include <linux/slab.h>
 #include <linux/scatterlist.h>
 #include <linux/regulator/consumer.h>
+#include <linux/pm_runtime.h>
 
 #include <linux/leds.h>
 
@@ -42,14 +43,29 @@
 #define MAX_TUNING_LOOP 40
 
 static unsigned int debug_quirks = 0;
+static unsigned int debug_quirks2;
 
 static void sdhci_finish_data(struct sdhci_host *);
 
 static void sdhci_send_command(struct sdhci_host *, struct mmc_command *);
 static void sdhci_finish_command(struct sdhci_host *);
-static int sdhci_execute_tuning(struct mmc_host *mmc);
+static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode);
 static void sdhci_tuning_timer(unsigned long data);
 
+#ifdef CONFIG_PM_RUNTIME
+static int sdhci_runtime_pm_get(struct sdhci_host *host);
+static int sdhci_runtime_pm_put(struct sdhci_host *host);
+#else
+static inline int sdhci_runtime_pm_get(struct sdhci_host *host)
+{
+	return 0;
+}
+static inline int sdhci_runtime_pm_put(struct sdhci_host *host)
+{
+	return 0;
+}
+#endif
+
 static void sdhci_dumpregs(struct sdhci_host *host)
 {
 	printk(KERN_DEBUG DRIVER_NAME ": =========== REGISTER DUMP (%s)===========\n",
@@ -134,6 +150,9 @@
 	    (host->mmc->caps & MMC_CAP_NONREMOVABLE))
 		return;
 
+	if (host->quirks2 & SDHCI_QUIRK2_OWN_CARD_DETECTION)
+		return;
+
 	if (enable)
 		sdhci_unmask_irqs(host, irqs);
 	else
@@ -249,11 +268,14 @@
 
 	spin_lock_irqsave(&host->lock, flags);
 
+	if (host->runtime_suspended)
+		goto out;
+
 	if (brightness == LED_OFF)
 		sdhci_deactivate_led(host);
 	else
 		sdhci_activate_led(host);
-
+out:
 	spin_unlock_irqrestore(&host->lock, flags);
 }
 #endif
@@ -398,12 +420,12 @@
 static char *sdhci_kmap_atomic(struct scatterlist *sg, unsigned long *flags)
 {
 	local_irq_save(*flags);
-	return kmap_atomic(sg_page(sg), KM_BIO_SRC_IRQ) + sg->offset;
+	return kmap_atomic(sg_page(sg)) + sg->offset;
 }
 
 static void sdhci_kunmap_atomic(void *buffer, unsigned long *flags)
 {
-	kunmap_atomic(buffer, KM_BIO_SRC_IRQ);
+	kunmap_atomic(buffer);
 	local_irq_restore(*flags);
 }
 
@@ -653,9 +675,7 @@
 			break;
 	}
 
-	if (count >= 0xF) {
-		printk(KERN_WARNING "%s: Too large timeout requested for CMD%d!\n",
-		       mmc_hostname(host->mmc), cmd->opcode);
+	if (count >= 0xF)
 		count = 0xE;
 
 	return count;
@@ -992,7 +1012,8 @@
 		flags |= SDHCI_CMD_INDEX;
 
 	/* CMD19 is special in that the Data Present Select should be set */
-	if (cmd->data || (cmd->opcode == MMC_SEND_TUNING_BLOCK))
+	if (cmd->data || cmd->opcode == MMC_SEND_TUNING_BLOCK ||
+	    cmd->opcode == MMC_SEND_TUNING_BLOCK_HS200)
 		flags |= SDHCI_CMD_DATA;
 
 	sdhci_writew(host, SDHCI_MAKE_CMD(cmd->opcode, flags), SDHCI_COMMAND);
@@ -1208,6 +1229,8 @@
 
 	host = mmc_priv(mmc);
 
+	sdhci_runtime_pm_get(host);
+
 	spin_lock_irqsave(&host->lock, flags);
 
 	WARN_ON(host->mrq != NULL);
@@ -1251,7 +1274,7 @@
 		if ((host->flags & SDHCI_NEEDS_RETUNING) &&
 		    !(present_state & (SDHCI_DOING_WRITE | SDHCI_DOING_READ))) {
 			spin_unlock_irqrestore(&host->lock, flags);
-			sdhci_execute_tuning(mmc);
+			sdhci_execute_tuning(mmc, mrq->cmd->opcode);
 			spin_lock_irqsave(&host->lock, flags);
 
 			/* Restore original mmc_request structure */
@@ -1268,14 +1291,11 @@
 	spin_unlock_irqrestore(&host->lock, flags);
 }
 
-static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios)
 {
-	struct sdhci_host *host;
 	unsigned long flags;
 	u8 ctrl;
 
-	host = mmc_priv(mmc);
-
 	spin_lock_irqsave(&host->lock, flags);
 
 	if (host->flags & SDHCI_DEVICE_DEAD)
@@ -1338,7 +1358,8 @@
 		unsigned int clock;
 
 		/* In case of UHS-I modes, set High Speed Enable */
-		if ((ios->timing == MMC_TIMING_UHS_SDR50) ||
+		if ((ios->timing == MMC_TIMING_MMC_HS200) ||
+		    (ios->timing == MMC_TIMING_UHS_SDR50) ||
 		    (ios->timing == MMC_TIMING_UHS_SDR104) ||
 		    (ios->timing == MMC_TIMING_UHS_DDR50) ||
 		    (ios->timing == MMC_TIMING_UHS_SDR25))
@@ -1391,7 +1412,9 @@
 			ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
 			/* Select Bus Speed Mode for host */
 			ctrl_2 &= ~SDHCI_CTRL_UHS_MASK;
-			if (ios->timing == MMC_TIMING_UHS_SDR12)
+			if (ios->timing == MMC_TIMING_MMC_HS200)
+				ctrl_2 |= SDHCI_CTRL_HS_SDR200;
+			else if (ios->timing == MMC_TIMING_UHS_SDR12)
 				ctrl_2 |= SDHCI_CTRL_UHS_SDR12;
 			else if (ios->timing == MMC_TIMING_UHS_SDR25)
 				ctrl_2 |= SDHCI_CTRL_UHS_SDR25;
@@ -1424,7 +1447,16 @@
 	spin_unlock_irqrestore(&host->lock, flags);
 }
 
-static int check_ro(struct sdhci_host *host)
+static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+	struct sdhci_host *host = mmc_priv(mmc);
+
+	sdhci_runtime_pm_get(host);
+	sdhci_do_set_ios(host, ios);
+	sdhci_runtime_pm_put(host);
+}
+
+static int sdhci_check_ro(struct sdhci_host *host)
 {
 	unsigned long flags;
 	int is_readonly;
@@ -1448,19 +1480,16 @@
 
 #define SAMPLE_COUNT	5
 
-static int sdhci_get_ro(struct mmc_host *mmc)
+static int sdhci_do_get_ro(struct sdhci_host *host)
 {
-	struct sdhci_host *host;
 	int i, ro_count;
 
-	host = mmc_priv(mmc);
-
 	if (!(host->quirks & SDHCI_QUIRK_UNSTABLE_RO_DETECT))
-		return check_ro(host);
+		return sdhci_check_ro(host);
 
 	ro_count = 0;
 	for (i = 0; i < SAMPLE_COUNT; i++) {
-		if (check_ro(host)) {
+		if (sdhci_check_ro(host)) {
 			if (++ro_count > SAMPLE_COUNT / 2)
 				return 1;
 		}
@@ -1469,38 +1498,64 @@
 	return 0;
 }
 
-static void sdhci_enable_sdio_irq(struct mmc_host *mmc, int enable)
+static void sdhci_hw_reset(struct mmc_host *mmc)
 {
-	struct sdhci_host *host;
-	unsigned long flags;
+	struct sdhci_host *host = mmc_priv(mmc);
 
-	host = mmc_priv(mmc);
+	if (host->ops && host->ops->hw_reset)
+		host->ops->hw_reset(host);
+}
 
-	spin_lock_irqsave(&host->lock, flags);
+static int sdhci_get_ro(struct mmc_host *mmc)
+{
+	struct sdhci_host *host = mmc_priv(mmc);
+	int ret;
 
+	sdhci_runtime_pm_get(host);
+	ret = sdhci_do_get_ro(host);
+	sdhci_runtime_pm_put(host);
+	return ret;
+}
+
+static void sdhci_enable_sdio_irq_nolock(struct sdhci_host *host, int enable)
+{
 	if (host->flags & SDHCI_DEVICE_DEAD)
 		goto out;
 
 	if (enable)
+		host->flags |= SDHCI_SDIO_IRQ_ENABLED;
+	else
+		host->flags &= ~SDHCI_SDIO_IRQ_ENABLED;
+
+	/* SDIO IRQ will be enabled as appropriate in runtime resume */
+	if (host->runtime_suspended)
+		goto out;
+
+	if (enable)
 		sdhci_unmask_irqs(host, SDHCI_INT_CARD_INT);
 	else
 		sdhci_mask_irqs(host, SDHCI_INT_CARD_INT);
 out:
 	mmiowb();
+}
 
+static void sdhci_enable_sdio_irq(struct mmc_host *mmc, int enable)
+{
+	struct sdhci_host *host = mmc_priv(mmc);
+	unsigned long flags;
+
+	spin_lock_irqsave(&host->lock, flags);
+	sdhci_enable_sdio_irq_nolock(host, enable);
 	spin_unlock_irqrestore(&host->lock, flags);
 }
 
-static int sdhci_start_signal_voltage_switch(struct mmc_host *mmc,
-	struct mmc_ios *ios)
+static int sdhci_do_start_signal_voltage_switch(struct sdhci_host *host,
+						struct mmc_ios *ios)
 {
-	struct sdhci_host *host;
 	u8 pwr;
 	u16 clk, ctrl;
 	u32 present_state;
 
-	host = mmc_priv(mmc);
-
 	/*
 	 * Signal Voltage Switching is only applicable for Host Controllers
 	 * v3.00 and above.
@@ -1593,7 +1648,21 @@
 		return 0;
 }
 
-static int sdhci_execute_tuning(struct mmc_host *mmc)
+static int sdhci_start_signal_voltage_switch(struct mmc_host *mmc,
+	struct mmc_ios *ios)
+{
+	struct sdhci_host *host = mmc_priv(mmc);
+	int err;
+
+	if (host->version < SDHCI_SPEC_300)
+		return 0;
+	sdhci_runtime_pm_get(host);
+	err = sdhci_do_start_signal_voltage_switch(host, ios);
+	sdhci_runtime_pm_put(host);
+	return err;
+}
+
+static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode)
 {
 	struct sdhci_host *host;
 	u16 ctrl;
@@ -1601,26 +1670,35 @@
 	int tuning_loop_counter = MAX_TUNING_LOOP;
 	unsigned long timeout;
 	int err = 0;
+	bool requires_tuning_nonuhs = false;
 
 	host = mmc_priv(mmc);
 
+	sdhci_runtime_pm_get(host);
 	disable_irq(host->irq);
 	spin_lock(&host->lock);
 
 	ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
 
 	/*
-	 * Host Controller needs tuning only in case of SDR104 mode
-	 * and for SDR50 mode when Use Tuning for SDR50 is set in
+	 * The Host Controller needs tuning only in case of SDR104 mode
+	 * and for SDR50 mode when Use Tuning for SDR50 is set in the
 	 * Capabilities register.
+	 * If the Host Controller supports the HS200 mode then the
+	 * tuning function has to be executed.
 	 */
+	if (((ctrl & SDHCI_CTRL_UHS_MASK) == SDHCI_CTRL_UHS_SDR50) &&
+	    (host->flags & SDHCI_SDR50_NEEDS_TUNING ||
+	     host->flags & SDHCI_HS200_NEEDS_TUNING))
+		requires_tuning_nonuhs = true;
+
 	if (((ctrl & SDHCI_CTRL_UHS_MASK) == SDHCI_CTRL_UHS_SDR104) ||
-	    (((ctrl & SDHCI_CTRL_UHS_MASK) == SDHCI_CTRL_UHS_SDR50) &&
-	    (host->flags & SDHCI_SDR50_NEEDS_TUNING)))
+	    requires_tuning_nonuhs)
 		ctrl |= SDHCI_CTRL_EXEC_TUNING;
 	else {
 		spin_unlock(&host->lock);
 		enable_irq(host->irq);
+		sdhci_runtime_pm_put(host);
 		return 0;
 	}
 
@@ -1646,12 +1724,12 @@
 	timeout = 150;
 	do {
 		struct mmc_command cmd = {0};
-		struct mmc_request mrq = {0};
+		struct mmc_request mrq = {NULL};
 
 		if (!tuning_loop_counter && !timeout)
 			break;
 
-		cmd.opcode = MMC_SEND_TUNING_BLOCK;
+		cmd.opcode = opcode;
 		cmd.arg = 0;
 		cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
 		cmd.retries = 0;
@@ -1666,7 +1744,17 @@
 		 * block to the Host Controller. So we set the block size
 		 * to 64 here.
 		 */
-		sdhci_writew(host, SDHCI_MAKE_BLKSZ(7, 64), SDHCI_BLOCK_SIZE);
+		if (cmd.opcode == MMC_SEND_TUNING_BLOCK_HS200) {
+			if (mmc->ios.bus_width == MMC_BUS_WIDTH_8)
+				sdhci_writew(host, SDHCI_MAKE_BLKSZ(7, 128),
+					     SDHCI_BLOCK_SIZE);
+			else if (mmc->ios.bus_width == MMC_BUS_WIDTH_4)
+				sdhci_writew(host, SDHCI_MAKE_BLKSZ(7, 64),
+					     SDHCI_BLOCK_SIZE);
+		} else {
+			sdhci_writew(host, SDHCI_MAKE_BLKSZ(7, 64),
+				     SDHCI_BLOCK_SIZE);
+		}
 
 		/*
 		 * The tuning block is sent by the card to the host controller.
@@ -1764,18 +1852,16 @@
 	sdhci_clear_set_irqs(host, SDHCI_INT_DATA_AVAIL, ier);
 	spin_unlock(&host->lock);
 	enable_irq(host->irq);
+	sdhci_runtime_pm_put(host);
 
 	return err;
 }
 
-static void sdhci_enable_preset_value(struct mmc_host *mmc, bool enable)
+static void sdhci_do_enable_preset_value(struct sdhci_host *host, bool enable)
 {
-	struct sdhci_host *host;
 	u16 ctrl;
 	unsigned long flags;
 
-	host = mmc_priv(mmc);
-
 	/* Host Controller v3.00 defines preset value registers */
 	if (host->version < SDHCI_SPEC_300)
 		return;
@@ -1791,18 +1877,30 @@
 	if (enable && !(ctrl & SDHCI_CTRL_PRESET_VAL_ENABLE)) {
 		ctrl |= SDHCI_CTRL_PRESET_VAL_ENABLE;
 		sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
+		host->flags |= SDHCI_PV_ENABLED;
 	} else if (!enable && (ctrl & SDHCI_CTRL_PRESET_VAL_ENABLE)) {
 		ctrl &= ~SDHCI_CTRL_PRESET_VAL_ENABLE;
 		sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
+		host->flags &= ~SDHCI_PV_ENABLED;
 	}
 
 	spin_unlock_irqrestore(&host->lock, flags);
 }
 
+static void sdhci_enable_preset_value(struct mmc_host *mmc, bool enable)
+{
+	struct sdhci_host *host = mmc_priv(mmc);
+
+	sdhci_runtime_pm_get(host);
+	sdhci_do_enable_preset_value(host, enable);
+	sdhci_runtime_pm_put(host);
+}
+
 static const struct mmc_host_ops sdhci_ops = {
 	.request	= sdhci_request,
 	.set_ios	= sdhci_set_ios,
 	.get_ro		= sdhci_get_ro,
+	.hw_reset	= sdhci_hw_reset,
 	.enable_sdio_irq = sdhci_enable_sdio_irq,
 	.start_signal_voltage_switch	= sdhci_start_signal_voltage_switch,
 	.execute_tuning			= sdhci_execute_tuning,
@@ -1824,19 +1922,19 @@
 
 	spin_lock_irqsave(&host->lock, flags);
 
-	if (!(sdhci_readl(host, SDHCI_PRESENT_STATE) & SDHCI_CARD_PRESENT)) {
-		if (host->mrq) {
-			printk(KERN_ERR "%s: Card removed during transfer!\n",
-				mmc_hostname(host->mmc));
-			printk(KERN_ERR "%s: Resetting controller.\n",
-				mmc_hostname(host->mmc));
+	/* Check host->mrq first in case we are runtime suspended */
+	if (host->mrq &&
+	    !(sdhci_readl(host, SDHCI_PRESENT_STATE) & SDHCI_CARD_PRESENT)) {
+		printk(KERN_ERR "%s: Card removed during transfer!\n",
+			mmc_hostname(host->mmc));
+		printk(KERN_ERR "%s: Resetting controller.\n",
+			mmc_hostname(host->mmc));
 
-			sdhci_reset(host, SDHCI_RESET_CMD);
-			sdhci_reset(host, SDHCI_RESET_DATA);
+		sdhci_reset(host, SDHCI_RESET_CMD);
+		sdhci_reset(host, SDHCI_RESET_DATA);
 
-			host->mrq->cmd->error = -ENOMEDIUM;
-			tasklet_schedule(&host->finish_tasklet);
-		}
+		host->mrq->cmd->error = -ENOMEDIUM;
+		tasklet_schedule(&host->finish_tasklet);
 	}
 
 	spin_unlock_irqrestore(&host->lock, flags);
@@ -1852,14 +1950,16 @@
 
 	host = (struct sdhci_host*)param;
 
+	spin_lock_irqsave(&host->lock, flags);
+
         /*
          * If this tasklet gets rescheduled while running, it will
          * be run again afterwards but without any active request.
          */
-	if (!host->mrq)
+	if (!host->mrq) {
+		spin_unlock_irqrestore(&host->lock, flags);
 		return;
-
-	spin_lock_irqsave(&host->lock, flags);
+	}
 
 	del_timer(&host->timer);
 
@@ -1903,6 +2003,7 @@
 	spin_unlock_irqrestore(&host->lock, flags);
 
 	mmc_request_done(host->mmc, mrq);
+	sdhci_runtime_pm_put(host);
 }
 
 static void sdhci_timeout_timer(unsigned long data)
@@ -2036,12 +2137,14 @@
 
 static void sdhci_data_irq(struct sdhci_host *host, u32 intmask)
 {
+	u32 command;
 	BUG_ON(intmask == 0);
 
 	/* CMD19 generates _only_ Buffer Read Ready interrupt */
 	if (intmask & SDHCI_INT_DATA_AVAIL) {
-		if (SDHCI_GET_CMD(sdhci_readw(host, SDHCI_COMMAND)) ==
-		    MMC_SEND_TUNING_BLOCK) {
+		command = SDHCI_GET_CMD(sdhci_readw(host, SDHCI_COMMAND));
+		if (command == MMC_SEND_TUNING_BLOCK ||
+		    command == MMC_SEND_TUNING_BLOCK_HS200) {
 			host->tuning_done = 1;
 			wake_up(&host->buf_ready_int);
 			return;
@@ -2140,6 +2243,13 @@
 
 	spin_lock(&host->lock);
 
+	if (host->runtime_suspended) {
+		spin_unlock(&host->lock);
+		printk(KERN_WARNING "%s: got irq while runtime suspended\n",
+		       mmc_hostname(host->mmc));
+		return IRQ_HANDLED;
+	}
+
 	intmask = sdhci_readl(host, SDHCI_INT_STATUS);
 
 	if (!intmask || intmask == 0xffffffff) {
@@ -2226,7 +2336,7 @@
 
 #ifdef CONFIG_PM
 
-int sdhci_suspend_host(struct sdhci_host *host, pm_message_t state)
+int sdhci_suspend_host(struct sdhci_host *host)
 {
 	int ret;
 
@@ -2266,7 +2376,6 @@
 			return ret;
 	}
 
-
 	if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) {
 		if (host->ops->enable_dma)
 			host->ops->enable_dma(host);
@@ -2317,6 +2426,90 @@
 
 #endif /* CONFIG_PM */
 
+#ifdef CONFIG_PM_RUNTIME
+
+static int sdhci_runtime_pm_get(struct sdhci_host *host)
+{
+	return pm_runtime_get_sync(host->mmc->parent);
+}
+
+static int sdhci_runtime_pm_put(struct sdhci_host *host)
+{
+	pm_runtime_mark_last_busy(host->mmc->parent);
+	return pm_runtime_put_autosuspend(host->mmc->parent);
+}
+
+int sdhci_runtime_suspend_host(struct sdhci_host *host)
+{
+	unsigned long flags;
+	int ret = 0;
+
+	/* Disable tuning since we are suspending */
+	if (host->version >= SDHCI_SPEC_300 &&
+	    host->tuning_mode == SDHCI_TUNING_MODE_1) {
+		del_timer_sync(&host->tuning_timer);
+		host->flags &= ~SDHCI_NEEDS_RETUNING;
+	}
+
+	spin_lock_irqsave(&host->lock, flags);
+	sdhci_mask_irqs(host, SDHCI_INT_ALL_MASK);
+	spin_unlock_irqrestore(&host->lock, flags);
+
+	synchronize_irq(host->irq);
+
+	spin_lock_irqsave(&host->lock, flags);
+	host->runtime_suspended = true;
+	spin_unlock_irqrestore(&host->lock, flags);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(sdhci_runtime_suspend_host);
+
+int sdhci_runtime_resume_host(struct sdhci_host *host)
+{
+	unsigned long flags;
+	int ret = 0, host_flags = host->flags;
+
+	if (host_flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) {
+		if (host->ops->enable_dma)
+			host->ops->enable_dma(host);
+	}
+
+	sdhci_init(host, 0);
+
+	/* Force clock and power re-program */
+	host->pwr = 0;
+	host->clock = 0;
+	sdhci_do_set_ios(host, &host->mmc->ios);
+
+	sdhci_do_start_signal_voltage_switch(host, &host->mmc->ios);
+	if (host_flags & SDHCI_PV_ENABLED)
+		sdhci_do_enable_preset_value(host, true);
+
+	/* Set the re-tuning expiration flag */
+	if ((host->version >= SDHCI_SPEC_300) && host->tuning_count &&
+	    (host->tuning_mode == SDHCI_TUNING_MODE_1))
+		host->flags |= SDHCI_NEEDS_RETUNING;
+
+	spin_lock_irqsave(&host->lock, flags);
+
+	host->runtime_suspended = false;
+
+	/* Enable SDIO IRQ */
+	if ((host->flags & SDHCI_SDIO_IRQ_ENABLED))
+		sdhci_enable_sdio_irq_nolock(host, true);
+
+	/* Enable Card Detection */
+	sdhci_enable_card_detection(host);
+
+	spin_unlock_irqrestore(&host->lock, flags);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(sdhci_runtime_resume_host);
+
+#endif
+
 /*****************************************************************************\
  *                                                                           *
  * Device allocation/registration                                            *
@@ -2359,6 +2552,8 @@
 
 	if (debug_quirks)
 		host->quirks = debug_quirks;
+	if (debug_quirks2)
+		host->quirks2 = debug_quirks2;
 
 	sdhci_reset(host, SDHCI_RESET_ALL);
 
@@ -2569,10 +2764,14 @@
 	if (caps[1] & SDHCI_SUPPORT_DDR50)
 		mmc->caps |= MMC_CAP_UHS_DDR50;
 
-	/* Does the host needs tuning for SDR50? */
+	/* Does the host need tuning for SDR50? */
 	if (caps[1] & SDHCI_USE_SDR50_TUNING)
 		host->flags |= SDHCI_SDR50_NEEDS_TUNING;
 
+	/* Does the host need tuning for HS200? */
+	if (mmc->caps2 & MMC_CAP2_HS200)
+		host->flags |= SDHCI_HS200_NEEDS_TUNING;
+
 	/* Driver Type(s) (A, C, D) supported by the host */
 	if (caps[1] & SDHCI_DRIVER_TYPE_A)
 		mmc->caps |= MMC_CAP_DRIVER_TYPE_A;
@@ -2893,9 +3092,11 @@
 module_exit(sdhci_drv_exit);
 
 module_param(debug_quirks, uint, 0444);
+module_param(debug_quirks2, uint, 0444);
 
 MODULE_AUTHOR("Pierre Ossman <pierre@ossman.eu>");
 MODULE_DESCRIPTION("Secure Digital Host Controller Interface core driver");
 MODULE_LICENSE("GPL");
 
 MODULE_PARM_DESC(debug_quirks, "Force certain quirks.");
+MODULE_PARM_DESC(debug_quirks2, "Force certain other quirks.");
diff --git a/drivers/mtd/devices/msm_nand.c b/drivers/mtd/devices/msm_nand.c
index c43abca..5cace6b 100644
--- a/drivers/mtd/devices/msm_nand.c
+++ b/drivers/mtd/devices/msm_nand.c
@@ -6757,6 +6757,23 @@
 	return 0;
 }
 
+static const unsigned int bch_sup_cntrl[] = {
+	0x307, /* MSM7x2xA */
+	0x4030, /* MDM 9x15 */
+};
+
+static inline bool msm_nand_has_bch_ecc_engine(unsigned int hw_id)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(bch_sup_cntrl); i++) {
+		if (hw_id == bch_sup_cntrl[i])
+			return true;
+	}
+
+	return false;
+}
+
 /**
  * msm_nand_scan - [msm_nand Interface] Scan for the msm_nand device
  * @param mtd		MTD device structure
@@ -6778,6 +6795,7 @@
 	uint32_t devcfg;
 	struct nand_flash_dev *flashdev = NULL;
 	struct nand_manufacturers  *flashman = NULL;
+	unsigned int hw_id;
 
 	/* Probe the Flash device for ONFI compliance */
 	if (!flash_onfi_probe(chip)) {
@@ -6845,7 +6863,8 @@
 			mtd_writesize = mtd->writesize >> 1;
 
 		/* Check whether controller and NAND device support 8bit ECC*/
-		if ((flash_rd_reg(chip, MSM_NAND_HW_INFO) == 0x307)
+		hw_id = flash_rd_reg(chip, MSM_NAND_HW_INFO);
+		if (msm_nand_has_bch_ecc_engine(hw_id)
 				&& (supported_flash.ecc_correctability >= 8)) {
 			pr_info("Found supported NAND device for %dbit ECC\n",
 					supported_flash.ecc_correctability);
@@ -6853,7 +6872,8 @@
 		} else {
 			pr_info("Found a supported NAND device\n");
 		}
-		pr_info("NAND Id  : 0x%x\n", supported_flash.flash_id);
+		pr_info("NAND Controller ID : 0x%x\n", hw_id);
+		pr_info("NAND Device ID  : 0x%x\n", supported_flash.flash_id);
 		pr_info("Buswidth : %d Bits\n", (wide_bus) ? 16 : 8);
 		pr_info("Density  : %lld MByte\n", (mtd->size>>20));
 		pr_info("Pagesize : %d Bytes\n", mtd->writesize);
diff --git a/drivers/mtd/devices/msm_qpic_nand.c b/drivers/mtd/devices/msm_qpic_nand.c
index d709e17..9a6cc80 100644
--- a/drivers/mtd/devices/msm_qpic_nand.c
+++ b/drivers/mtd/devices/msm_qpic_nand.c
@@ -2351,7 +2351,7 @@
 {
 	struct msm_nand_info *info;
 	struct resource *res;
-	int err, n_parts;
+	int err;
 	struct device_node *pnode;
 	struct mtd_part_parser_data parser_data;
 
@@ -2443,10 +2443,10 @@
 		goto free_bam;
 	}
 	parser_data.of_node = pnode;
-	n_parts = mtd_device_parse_register(&info->mtd, NULL, &parser_data,
+	err = mtd_device_parse_register(&info->mtd, NULL, &parser_data,
 					NULL, 0);
-	if (n_parts < 0) {
-		pr_err("Unable to register MTD partitions %d\n", n_parts);
+	if (err < 0) {
+		pr_err("Unable to register MTD partitions %d\n", err);
 		goto free_bam;
 	}
 	dev_set_drvdata(&pdev->dev, info);
@@ -2455,7 +2455,6 @@
 			info->nand_phys, info->bam_phys, info->bam_irq);
 	pr_info("Allocated DMA buffer at virt_addr 0x%p, phys_addr 0x%x\n",
 		info->nand_chip.dma_virt_addr, info->nand_chip.dma_phys_addr);
-	pr_info("Found %d MTD partitions\n", n_parts);
 	goto out;
 free_bam:
 	msm_nand_bam_free(info);
diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c
index 0c634ca..4ee18c75 100644
--- a/drivers/mtd/mtdpart.c
+++ b/drivers/mtd/mtdpart.c
@@ -536,15 +536,6 @@
 #ifndef CONFIG_MTD_LAZYECCSTATS
 	part_fill_badblockstats(&(slave->mtd));
 #endif
-	if (master->_block_isbad) {
-		uint64_t offs = 0;
-
-		while (offs < slave->mtd.size) {
-			if (mtd_block_isbad(master, offs + slave->offset))
-				slave->mtd.ecc_stats.badblocks++;
-			offs += slave->mtd.erasesize;
-		}
-	}
 
 out_register:
 	return slave;
diff --git a/drivers/net/ethernet/msm/msm_rmnet.c b/drivers/net/ethernet/msm/msm_rmnet.c
index 41ad8af..cc9ed74 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;
@@ -94,6 +94,7 @@
 	struct sk_buff *skb;
 	spinlock_t lock;
 	struct tasklet_struct tsklt;
+	struct tasklet_struct rx_tasklet;
 	u32 operation_mode;    /* IOCTL specified mode (protocol, QoS header) */
 	struct platform_driver pdrv;
 	struct completion complete;
@@ -257,7 +258,6 @@
 }
 
 static void smd_net_data_handler(unsigned long arg);
-static DECLARE_TASKLET(smd_net_data_tasklet, smd_net_data_handler, 0);
 
 /* Called in soft-irq context */
 static void smd_net_data_handler(unsigned long arg)
@@ -280,8 +280,8 @@
 			pr_err("[%s] rmnet_recv() cannot allocate skb\n",
 			       dev->name);
 			/* out of memory, reschedule a later attempt */
-			smd_net_data_tasklet.data = (unsigned long)dev;
-			tasklet_schedule(&smd_net_data_tasklet);
+			p->rx_tasklet.data = (unsigned long)dev;
+			tasklet_schedule(&p->rx_tasklet);
 			break;
 		} else {
 			skb->dev = dev;
@@ -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__);
@@ -449,8 +449,8 @@
 
 		if (smd_read_avail(p->ch) &&
 			(smd_read_avail(p->ch) >= smd_cur_packet_size(p->ch))) {
-			smd_net_data_tasklet.data = (unsigned long) _dev;
-			tasklet_schedule(&smd_net_data_tasklet);
+			p->rx_tasklet.data = (unsigned long) _dev;
+			tasklet_schedule(&p->rx_tasklet);
 		}
 		break;
 
@@ -788,6 +788,8 @@
 		spin_lock_init(&p->lock);
 		tasklet_init(&p->tsklt, _rmnet_resume_flow,
 				(unsigned long)dev);
+		tasklet_init(&p->rx_tasklet, smd_net_data_handler,
+				(unsigned long)dev);
 		wake_lock_init(&p->wake_lock, WAKE_LOCK_SUSPEND, ch_name[n]);
 #ifdef CONFIG_MSM_RMNET_DEBUG
 		p->timeout_us = timeout_us;
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_ctrl.c b/drivers/net/usb/rmnet_usb_ctrl.c
index 7ed8ffa..f87b3b9 100644
--- a/drivers/net/usb/rmnet_usb_ctrl.c
+++ b/drivers/net/usb/rmnet_usb_ctrl.c
@@ -339,8 +339,10 @@
 
 int rmnet_usb_ctrl_suspend(struct rmnet_ctrl_dev *dev)
 {
-	if (!flush_work_sync(&dev->get_encap_work))
-		usb_kill_anchored_urbs(&dev->rx_submitted);
+	if (work_busy(&dev->get_encap_work))
+		return -EBUSY;
+
+	usb_kill_anchored_urbs(&dev->rx_submitted);
 
 	return 0;
 }
diff --git a/drivers/net/wireless/wcnss/wcnss_wlan.c b/drivers/net/wireless/wcnss/wcnss_wlan.c
index 2bf857c..71a9860 100644
--- a/drivers/net/wireless/wcnss/wcnss_wlan.c
+++ b/drivers/net/wireless/wcnss/wcnss_wlan.c
@@ -25,9 +25,12 @@
 #include <linux/delay.h>
 #include <linux/of.h>
 #include <linux/of_gpio.h>
-#include <mach/peripheral-loader.h>
+#include <linux/clk.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
@@ -43,6 +46,21 @@
 module_param(has_48mhz_xo, int, S_IWUSR | S_IRUGO);
 MODULE_PARM_DESC(has_48mhz_xo, "Is an external 48 MHz XO present");
 
+static DEFINE_SPINLOCK(reg_spinlock);
+
+#define MSM_RIVA_PHYS			0x03204000
+#define MSM_PRONTO_PHYS			0xfb21b000
+
+#define RIVA_SPARE_OFFSET		0x0b4
+#define RIVA_SUSPEND_BIT		BIT(24)
+
+#define MSM_RIVA_CCU_BASE			0x03200800
+
+#define CCU_INVALID_ADDR_OFFSET		0x100
+#define CCU_LAST_ADDR0_OFFSET		0x104
+#define CCU_LAST_ADDR1_OFFSET		0x108
+#define CCU_LAST_ADDR2_OFFSET		0x10c
+
 #define WCNSS_CTRL_CHANNEL			"WCNSS_CTRL"
 #define WCNSS_MAX_FRAME_SIZE		500
 #define WCNSS_VERSION_LEN			30
@@ -89,6 +107,7 @@
 	struct work_struct wcnssctrl_version_work;
 	struct work_struct wcnssctrl_rx_work;
 	struct wake_lock wcnss_wake_lock;
+	void __iomem *msm_wcnss_base;
 } *penv = NULL;
 
 static ssize_t wcnss_serial_number_show(struct device *dev,
@@ -160,15 +179,50 @@
 static DEVICE_ATTR(wcnss_version, S_IRUSR,
 		wcnss_version_show, NULL);
 
+/* wcnss_reset_intr() is invoked when host drivers fails to
+ * communicate with WCNSS over SMD; so logging these registers
+ * helps to know WCNSS failure reason */
+static void wcnss_log_ccpu_regs(void)
+{
+	void __iomem *ccu_base;
+	void __iomem *ccu_reg;
+	u32 reg = 0;
+
+	ccu_base = ioremap(MSM_RIVA_CCU_BASE, SZ_512);
+	if (!ccu_base) {
+		pr_err("%s: ioremap WCNSS CCU reg failed\n", __func__);
+		return;
+	}
+
+	ccu_reg = ccu_base + CCU_INVALID_ADDR_OFFSET;
+	reg = readl_relaxed(ccu_reg);
+	pr_info("%s: CCU_CCPU_INVALID_ADDR %08x\n", __func__, reg);
+
+	ccu_reg = ccu_base + CCU_LAST_ADDR0_OFFSET;
+	reg = readl_relaxed(ccu_reg);
+	pr_info("%s: CCU_CCPU_LAST_ADDR0 %08x\n", __func__, reg);
+
+	ccu_reg = ccu_base + CCU_LAST_ADDR1_OFFSET;
+	reg = readl_relaxed(ccu_reg);
+	pr_info("%s: CCU_CCPU_LAST_ADDR1 %08x\n", __func__, reg);
+
+	ccu_reg = ccu_base + CCU_LAST_ADDR2_OFFSET;
+	reg = readl_relaxed(ccu_reg);
+	pr_info("%s: CCU_CCPU_LAST_ADDR2 %08x\n", __func__, reg);
+
+	iounmap(ccu_base);
+}
+
 /* interface to reset Riva by sending the reset interrupt */
 void wcnss_reset_intr(void)
 {
-	if (wcnss_hardware_type() == WCNSS_RIVA_HW) {
-		wmb();
-		__raw_writel(1 << 24, MSM_APCS_GCC_BASE + 0x8);
-	} else {
+	if (wcnss_hardware_type() != WCNSS_RIVA_HW) {
 		pr_err("%s: reset interrupt not supported\n", __func__);
+		return;
 	}
+	wcnss_log_ccpu_regs();
+	wmb();
+	__raw_writel(1 << 24, MSM_APCS_GCC_BASE + 0x8);
 }
 EXPORT_SYMBOL(wcnss_reset_intr);
 
@@ -487,6 +541,71 @@
 }
 EXPORT_SYMBOL(wcnss_get_serial_number);
 
+static int enable_wcnss_suspend_notify;
+
+static int enable_wcnss_suspend_notify_set(const char *val,
+				struct kernel_param *kp)
+{
+	int ret;
+
+	ret = param_set_int(val, kp);
+	if (ret)
+		return ret;
+
+	if (enable_wcnss_suspend_notify)
+		pr_debug("Suspend notification activated for wcnss\n");
+
+	return 0;
+}
+module_param_call(enable_wcnss_suspend_notify, enable_wcnss_suspend_notify_set,
+		param_get_int, &enable_wcnss_suspend_notify, S_IRUGO | S_IWUSR);
+
+
+void wcnss_suspend_notify(void)
+{
+	void __iomem *pmu_spare_reg;
+	u32 reg = 0;
+	unsigned long flags;
+
+	if (!enable_wcnss_suspend_notify)
+		return;
+
+	if (wcnss_hardware_type() == WCNSS_PRONTO_HW)
+		return;
+
+	/* For Riva */
+	pmu_spare_reg = penv->msm_wcnss_base + RIVA_SPARE_OFFSET;
+	spin_lock_irqsave(&reg_spinlock, flags);
+	reg = readl_relaxed(pmu_spare_reg);
+	reg |= RIVA_SUSPEND_BIT;
+	writel_relaxed(reg, pmu_spare_reg);
+	spin_unlock_irqrestore(&reg_spinlock, flags);
+}
+EXPORT_SYMBOL(wcnss_suspend_notify);
+
+void wcnss_resume_notify(void)
+{
+	void __iomem *pmu_spare_reg;
+	u32 reg = 0;
+	unsigned long flags;
+
+	if (!enable_wcnss_suspend_notify)
+		return;
+
+	if (wcnss_hardware_type() == WCNSS_PRONTO_HW)
+		return;
+
+	/* For Riva */
+	pmu_spare_reg = penv->msm_wcnss_base + RIVA_SPARE_OFFSET;
+
+	spin_lock_irqsave(&reg_spinlock, flags);
+	reg = readl_relaxed(pmu_spare_reg);
+	reg &= ~RIVA_SUSPEND_BIT;
+	writel_relaxed(reg, pmu_spare_reg);
+	spin_unlock_irqrestore(&reg_spinlock, flags);
+}
+EXPORT_SYMBOL(wcnss_resume_notify);
+
 static int wcnss_wlan_suspend(struct device *dev)
 {
 	if (penv && dev && (dev == &penv->pdev->dev) &&
@@ -610,6 +729,8 @@
 {
 	int ret;
 	struct qcom_wcnss_opts *pdata;
+	unsigned long wcnss_phys_addr;
+	int size = 0;
 	int has_pronto_hw = of_property_read_bool(pdev->dev.of_node,
 									"qcom,has_pronto_hw");
 
@@ -664,7 +785,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);
@@ -690,21 +811,37 @@
 
 	wake_lock_init(&penv->wcnss_wake_lock, WAKE_LOCK_SUSPEND, "wcnss");
 
+	if (wcnss_hardware_type() == WCNSS_PRONTO_HW) {
+		size = 0x3000;
+		wcnss_phys_addr = MSM_PRONTO_PHYS;
+	} else {
+		wcnss_phys_addr = MSM_RIVA_PHYS;
+		size = SZ_256;
+	}
+
+	penv->msm_wcnss_base = ioremap(wcnss_phys_addr, size);
+	if (!penv->msm_wcnss_base) {
+		ret = -ENOMEM;
+		pr_err("%s: ioremap wcnss physical failed\n", __func__);
+		goto fail_wake;
+	}
+
 	return 0;
 
+fail_wake:
+	wake_lock_destroy(&penv->wcnss_wake_lock);
 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);
 fail_power:
 	if (has_pronto_hw)
-		ret = wcnss_pronto_gpios_config(&pdev->dev, false);
+		wcnss_pronto_gpios_config(&pdev->dev, false);
 	else
 		wcnss_gpios_config(penv->gpios_5wire, false);
 fail_gpio_res:
-	kfree(penv);
 	penv = NULL;
 	return ret;
 }
@@ -745,7 +882,7 @@
 	}
 
 	/* create an environment to track the device */
-	penv = kzalloc(sizeof(*penv), GFP_KERNEL);
+	penv = devm_kzalloc(&pdev->dev, sizeof(*penv), GFP_KERNEL);
 	if (!penv) {
 		dev_err(&pdev->dev, "cannot allocate device memory.\n");
 		return -ENOMEM;
@@ -754,8 +891,10 @@
 
 	/* register sysfs entries */
 	ret = wcnss_create_sysfs(&pdev->dev);
-	if (ret)
+	if (ret) {
+		penv = NULL;
 		return -ENOENT;
+	}
 
 
 #ifdef MODULE
@@ -790,6 +929,7 @@
 wcnss_wlan_remove(struct platform_device *pdev)
 {
 	wcnss_remove_sysfs(&pdev->dev);
+	penv = NULL;
 	return 0;
 }
 
@@ -840,10 +980,9 @@
 {
 	if (penv) {
 		if (penv->pil)
-			pil_put(penv->pil);
+			subsystem_put(penv->pil);
 
 
-		kfree(penv);
 		penv = NULL;
 	}
 
diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c
index 6b54b23..9a18a97 100644
--- a/drivers/pci/pci-driver.c
+++ b/drivers/pci/pci-driver.c
@@ -617,21 +617,6 @@
 	int error = 0;
 
 	/*
-	 * If a PCI device configured to wake up the system from sleep states
-	 * has been suspended at run time and there's a resume request pending
-	 * for it, this is equivalent to the device signaling wakeup, so the
-	 * system suspend operation should be aborted.
-	 */
-	pm_runtime_get_noresume(dev);
-	if (pm_runtime_barrier(dev) && device_may_wakeup(dev))
-		pm_wakeup_event(dev, 0);
-
-	if (pm_wakeup_pending()) {
-		pm_runtime_put_sync(dev);
-		return -EBUSY;
-	}
-
-	/*
 	 * PCI devices suspended at run time need to be resumed at this
 	 * point, because in general it is necessary to reconfigure them for
 	 * system suspend.  Namely, if the device is supposed to wake up the
@@ -654,8 +639,6 @@
 
 	if (drv && drv->pm && drv->pm->complete)
 		drv->pm->complete(dev);
-
-	pm_runtime_put_sync(dev);
 }
 
 #else /* !CONFIG_PM_SLEEP */
diff --git a/drivers/platform/msm/Kconfig b/drivers/platform/msm/Kconfig
index 34e1d40..872a9b5 100644
--- a/drivers/platform/msm/Kconfig
+++ b/drivers/platform/msm/Kconfig
@@ -12,11 +12,7 @@
 
 config SPS
 	bool "SPS support"
-	depends on (HAS_IOMEM && (ARCH_MSM8960 || ARCH_MSM8X60 \
-			|| ARCH_APQ8064 || ARCH_MSM9615 \
-			|| ARCH_MSM9625 || ARCH_MSM8974))
 	select GENERIC_ALLOCATOR
-	default n
 	help
 	  The SPS (Smart Peripheral Switch) is a DMA engine.
 	  It can move data in the following modes:
@@ -76,4 +72,18 @@
 	  PNP PMIC. It configures the frequency of clkdiv outputs on the
 	  PMIC. These clocks are typically wired through alternate functions
 	  on gpio pins.
+
+config IPA
+	tristate "IPA support"
+	depends on SPS
+	help
+	  This driver supports the Internet Packet Accelerator (IPA) core.
+	  IPA is a programmable protocol processor HW block.
+	  It is designed to support generic HW processing of UL/DL IP packets
+	  for various use cases independent of radio technology.
+	  The driver support client connection and configuration
+	  for the IPA core.
+	  Kernel and user-space processes can call the IPA driver
+	  to configure IPA core.
+
 endmenu
diff --git a/drivers/platform/msm/Makefile b/drivers/platform/msm/Makefile
index 35efd91..0a755d3 100644
--- a/drivers/platform/msm/Makefile
+++ b/drivers/platform/msm/Makefile
@@ -3,6 +3,7 @@
 #
 obj-$(CONFIG_MSM_SSBI) += ssbi.o
 obj-$(CONFIG_USB_BAM) += usb_bam.o
+obj-$(CONFIG_IPA) += ipa/
 obj-$(CONFIG_SPS) += sps/
 obj-$(CONFIG_QPNP_PWM) += qpnp-pwm.o
 obj-$(CONFIG_QPNP_POWER_ON) += qpnp-power-on.o
diff --git a/drivers/platform/msm/ipa/Makefile b/drivers/platform/msm/ipa/Makefile
new file mode 100644
index 0000000..ded5b50
--- /dev/null
+++ b/drivers/platform/msm/ipa/Makefile
@@ -0,0 +1,3 @@
+obj-$(CONFIG_IPA) += ipat.o
+ipat-y := ipa.o ipa_debugfs.o ipa_hdr.o ipa_flt.o ipa_rt.o ipa_dp.o ipa_client.o \
+	ipa_utils.o ipa_nat.o rmnet_bridge.o a2_service.o ipa_bridge.o
diff --git a/drivers/platform/msm/ipa/a2_service.c b/drivers/platform/msm/ipa/a2_service.c
new file mode 100644
index 0000000..0ae2552
--- /dev/null
+++ b/drivers/platform/msm/ipa/a2_service.c
@@ -0,0 +1,276 @@
+/* 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/types.h>
+#include <mach/bam_dmux.h>
+#include <mach/ipa.h>
+#include <mach/sps.h>
+#include "ipa_i.h"
+
+static struct a2_service_cb_type {
+	void *tx_complete_cb;
+	void *rx_cb;
+	u32 producer_handle;
+	u32 consumer_handle;
+} a2_service_cb;
+
+static struct sps_mem_buffer data_mem_buf[2];
+static struct sps_mem_buffer desc_mem_buf[2];
+
+static int connect_pipe_ipa(enum a2_mux_pipe_direction pipe_dir,
+			u8 *usb_pipe_idx,
+			u32 *clnt_hdl,
+			struct sps_pipe *pipe);
+
+static int a2_ipa_connect_pipe(struct ipa_connect_params *in_params,
+		struct ipa_sps_params *out_params, u32 *clnt_hdl);
+
+/**
+ * a2_mux_initialize() - initialize A2 MUX module
+ *
+ * Return codes:
+ * 0: success
+ */
+int a2_mux_initialize(void)
+{
+	(void) msm_bam_dmux_ul_power_vote();
+
+	return 0;
+}
+
+/**
+ * a2_mux_close() - close A2 MUX module
+ *
+ * Return codes:
+ * 0: success
+ * -EINVAL: invalid parameters
+ */
+int a2_mux_close(void)
+{
+	int ret = 0;
+
+	(void) msm_bam_dmux_ul_power_unvote();
+
+	ret = ipa_disconnect(a2_service_cb.consumer_handle);
+	if (0 != ret) {
+		pr_err("%s: ipa_disconnect failure\n", __func__);
+		goto bail;
+	}
+
+	ret = ipa_disconnect(a2_service_cb.producer_handle);
+	if (0 != ret) {
+		pr_err("%s: ipa_disconnect failure\n", __func__);
+		goto bail;
+	}
+
+	ret = 0;
+
+bail:
+
+	return ret;
+}
+
+/**
+ * a2_mux_open_port() - open connection to A2
+ * @wwan_logical_channel_id:	 WWAN logical channel ID
+ * @rx_cb:	Rx callback
+ * @tx_complete_cb:	Tx completed callback
+ *
+ * Return codes:
+ * 0: success
+ * -EINVAL: invalid parameters
+ */
+int a2_mux_open_port(int wwan_logical_channel_id, void *rx_cb,
+		void *tx_complete_cb)
+{
+	int ret = 0;
+	u8 src_pipe = 0;
+	u8 dst_pipe = 0;
+	struct sps_pipe *a2_to_ipa_pipe = NULL;
+	struct sps_pipe *ipa_to_a2_pipe = NULL;
+
+	(void) wwan_logical_channel_id;
+
+	a2_service_cb.rx_cb = rx_cb;
+	a2_service_cb.tx_complete_cb = tx_complete_cb;
+
+	ret = connect_pipe_ipa(A2_TO_IPA,
+			&src_pipe,
+			&(a2_service_cb.consumer_handle),
+			a2_to_ipa_pipe);
+	if (ret) {
+		pr_err("%s: A2 to IPA pipe connection failure\n", __func__);
+		goto bail;
+	}
+
+	ret = connect_pipe_ipa(IPA_TO_A2,
+			&dst_pipe,
+			&(a2_service_cb.producer_handle),
+			ipa_to_a2_pipe);
+	if (ret) {
+		pr_err("%s: IPA to A2 pipe connection failure\n", __func__);
+		sps_disconnect(a2_to_ipa_pipe);
+		sps_free_endpoint(a2_to_ipa_pipe);
+		(void) ipa_disconnect(a2_service_cb.consumer_handle);
+		goto bail;
+	}
+
+	ret = 0;
+
+bail:
+
+	return ret;
+}
+
+static int connect_pipe_ipa(enum a2_mux_pipe_direction pipe_dir,
+			u8 *usb_pipe_idx,
+			u32 *clnt_hdl,
+			struct sps_pipe *pipe)
+{
+	int ret;
+	struct sps_connect connection = {0, };
+	u32 a2_handle = 0;
+	u32 a2_phy_addr = 0;
+	struct a2_mux_pipe_connection pipe_connection = { 0, };
+	struct ipa_connect_params ipa_in_params;
+	struct ipa_sps_params sps_out_params;
+
+	memset(&ipa_in_params, 0, sizeof(ipa_in_params));
+	memset(&sps_out_params, 0, sizeof(sps_out_params));
+
+	if (!usb_pipe_idx || !clnt_hdl) {
+		pr_err("connect_pipe_ipa :: null arguments\n");
+		ret = -EINVAL;
+		goto bail;
+	}
+
+	ret = ipa_get_a2_mux_pipe_info(pipe_dir, &pipe_connection);
+	if (ret) {
+		pr_err("ipa_get_a2_mux_pipe_info failed\n");
+		goto bail;
+	}
+
+	if (pipe_dir == A2_TO_IPA) {
+		a2_phy_addr = pipe_connection.src_phy_addr;
+		ipa_in_params.client = IPA_CLIENT_A2_TETHERED_PROD;
+		ipa_in_params.ipa_ep_cfg.mode.mode = IPA_DMA;
+		ipa_in_params.ipa_ep_cfg.mode.dst = IPA_CLIENT_USB_CONS;
+		pr_err("-*&- pipe_connection->src_pipe_index = %d\n",
+				pipe_connection.src_pipe_index);
+		ipa_in_params.client_ep_idx = pipe_connection.src_pipe_index;
+	} else {
+		a2_phy_addr = pipe_connection.dst_phy_addr;
+		ipa_in_params.client = IPA_CLIENT_A2_TETHERED_CONS;
+		ipa_in_params.client_ep_idx = pipe_connection.dst_pipe_index;
+	}
+
+	ret = sps_phy2h(a2_phy_addr, &a2_handle);
+	if (ret) {
+		pr_err("%s: sps_phy2h failed (A2 BAM) %d\n", __func__, ret);
+		goto bail;
+	}
+
+	ipa_in_params.client_bam_hdl = a2_handle;
+	ipa_in_params.desc_fifo_sz = pipe_connection.desc_fifo_size;
+	ipa_in_params.data_fifo_sz = pipe_connection.data_fifo_size;
+
+	if (pipe_connection.mem_type == IPA_SPS_PIPE_MEM) {
+		pr_debug("%s: A2 BAM using SPS pipe memory\n", __func__);
+		ret = sps_setup_bam2bam_fifo(&data_mem_buf[pipe_dir],
+				pipe_connection.data_fifo_base_offset,
+				pipe_connection.data_fifo_size, 1);
+		if (ret) {
+			pr_err("%s: data fifo setup failure %d\n",
+					__func__, ret);
+			goto bail;
+		}
+
+		ret = sps_setup_bam2bam_fifo(&desc_mem_buf[pipe_dir],
+				pipe_connection.desc_fifo_base_offset,
+				pipe_connection.desc_fifo_size, 1);
+		if (ret) {
+			pr_err("%s: desc. fifo setup failure %d\n",
+					__func__, ret);
+			goto bail;
+		}
+
+		ipa_in_params.data = data_mem_buf[pipe_dir];
+		ipa_in_params.desc = desc_mem_buf[pipe_dir];
+	}
+
+	ret = a2_ipa_connect_pipe(&ipa_in_params,
+			&sps_out_params,
+			clnt_hdl);
+	if (ret) {
+		pr_err("-**- USB-IPA info: ipa_connect failed\n");
+		pr_err("%s: usb_ipa_connect_pipe failed\n", __func__);
+		goto bail;
+	}
+
+	pipe = sps_alloc_endpoint();
+	if (pipe == NULL) {
+		pr_err("%s: sps_alloc_endpoint failed\n", __func__);
+		ret = -ENOMEM;
+		goto a2_ipa_connect_pipe_failed;
+	}
+
+	ret = sps_get_config(pipe, &connection);
+	if (ret) {
+		pr_err("%s: tx get config failed %d\n", __func__, ret);
+		goto get_config_failed;
+	}
+
+	if (pipe_dir == A2_TO_IPA) {
+		connection.mode = SPS_MODE_SRC;
+		*usb_pipe_idx = connection.src_pipe_index;
+		connection.source = a2_handle;
+		connection.destination = sps_out_params.ipa_bam_hdl;
+		connection.src_pipe_index = pipe_connection.src_pipe_index;
+		connection.dest_pipe_index = sps_out_params.ipa_ep_idx;
+	} else {
+		connection.mode = SPS_MODE_DEST;
+		*usb_pipe_idx = connection.dest_pipe_index;
+		connection.source = sps_out_params.ipa_bam_hdl;
+		connection.destination = a2_handle;
+		connection.src_pipe_index = sps_out_params.ipa_ep_idx;
+		connection.dest_pipe_index = pipe_connection.dst_pipe_index;
+	}
+
+	connection.event_thresh = 16;
+	connection.data = sps_out_params.data;
+	connection.desc = sps_out_params.desc;
+
+	ret = sps_connect(pipe, &connection);
+	if (ret < 0) {
+		pr_err("%s: tx connect error %d\n", __func__, ret);
+		goto error;
+	}
+
+	ret = 0;
+	goto bail;
+error:
+	sps_disconnect(pipe);
+get_config_failed:
+	sps_free_endpoint(pipe);
+a2_ipa_connect_pipe_failed:
+	(void) ipa_disconnect(*clnt_hdl);
+bail:
+	return ret;
+}
+
+static int a2_ipa_connect_pipe(struct ipa_connect_params *in_params,
+		struct ipa_sps_params *out_params, u32 *clnt_hdl)
+{
+	return ipa_connect(in_params, out_params, clnt_hdl);
+}
+
diff --git a/drivers/platform/msm/ipa/a2_service.h b/drivers/platform/msm/ipa/a2_service.h
new file mode 100644
index 0000000..80885da
--- /dev/null
+++ b/drivers/platform/msm/ipa/a2_service.h
@@ -0,0 +1,24 @@
+/* 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 _A2_SERVICE_H_
+#define _A2_SERVICE_H_
+
+int a2_mux_initialize(void);
+
+int a2_mux_close(void);
+
+int a2_mux_open_port(int wwan_logical_channel_id, void *rx_cb,
+		void *tx_complete_cb);
+
+#endif /* _A2_SERVICE_H_ */
+
diff --git a/drivers/platform/msm/ipa/ipa.c b/drivers/platform/msm/ipa/ipa.c
new file mode 100644
index 0000000..8f68ef5
--- /dev/null
+++ b/drivers/platform/msm/ipa/ipa.c
@@ -0,0 +1,1790 @@
+/* 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/clk.h>
+#include <linux/device.h>
+#include <linux/dmapool.h>
+#include <linux/fs.h>
+#include <linux/genalloc.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/rbtree.h>
+#include <linux/uaccess.h>
+#include "ipa_i.h"
+
+#define IPA_SUMMING_THRESHOLD (0x10)
+#define IPA_PIPE_MEM_START_OFST (0x0)
+#define IPA_PIPE_MEM_SIZE (0x0)
+#define IPA_READ_MAX (16)
+#define IPA_MOBILE_AP_MODE(x) (x == IPA_MODE_MOBILE_AP_ETH || \
+			       x == IPA_MODE_MOBILE_AP_WAN || \
+			       x == IPA_MODE_MOBILE_AP_WLAN)
+#define IPA_CNOC_CLK_RATE (75 * 1000 * 1000UL)
+#define IPA_V1_CLK_RATE (92.31 * 1000 * 1000UL)
+#define IPA_DMA_POOL_SIZE (512)
+#define IPA_DMA_POOL_ALIGNMENT (4)
+#define IPA_DMA_POOL_BOUNDARY (1024)
+#define WLAN_AMPDU_TX_EP (15)
+#define IPA_ROUTING_RULE_BYTE_SIZE (4)
+#define IPA_BAM_CNFG_BITS_VAL (0x7FFFE004)
+
+#define IPA_AGGR_MAX_STR_LENGTH (10)
+
+#define IPA_AGGR_STR_IN_BYTES(str) \
+	(strnlen((str), IPA_AGGR_MAX_STR_LENGTH - 1) + 1)
+
+struct ipa_plat_drv_res {
+	u32 ipa_mem_base;
+	u32 ipa_mem_size;
+	u32 bam_mem_base;
+	u32 bam_mem_size;
+	u32 ipa_irq;
+	u32 bam_irq;
+	u32 ipa_pipe_mem_start_ofst;
+	u32 ipa_pipe_mem_size;
+	struct a2_mux_pipe_connection a2_to_ipa_pipe;
+	struct a2_mux_pipe_connection ipa_to_a2_pipe;
+};
+
+static struct ipa_plat_drv_res ipa_res = {0, };
+static struct of_device_id ipa_plat_drv_match[] = {
+	{
+		.compatible = "qcom,ipa",
+	},
+
+	{
+	}
+};
+
+static struct clk *ipa_clk_src;
+static struct clk *ipa_clk;
+static struct clk *sys_noc_ipa_axi_clk;
+static struct clk *ipa_cnoc_clk;
+static struct device *ipa_dev;
+
+struct ipa_context *ipa_ctx;
+
+static bool polling_mode;
+module_param(polling_mode, bool, 0644);
+MODULE_PARM_DESC(polling_mode,
+		"1 - pure polling mode; 0 - interrupt+polling mode");
+static uint polling_delay_ms = 50;
+module_param(polling_delay_ms, uint, 0644);
+MODULE_PARM_DESC(polling_delay_ms, "set to desired delay between polls");
+static bool hdr_tbl_lcl = 1;
+module_param(hdr_tbl_lcl, bool, 0644);
+MODULE_PARM_DESC(hdr_tbl_lcl, "where hdr tbl resides 1-local; 0-system");
+static bool ip4_rt_tbl_lcl = 1;
+module_param(ip4_rt_tbl_lcl, bool, 0644);
+MODULE_PARM_DESC(ip4_rt_tbl_lcl,
+		"where ip4 rt tables reside 1-local; 0-system");
+static bool ip6_rt_tbl_lcl = 1;
+module_param(ip6_rt_tbl_lcl, bool, 0644);
+MODULE_PARM_DESC(ip6_rt_tbl_lcl,
+		"where ip6 rt tables reside 1-local; 0-system");
+static bool ip4_flt_tbl_lcl = 1;
+module_param(ip4_flt_tbl_lcl, bool, 0644);
+MODULE_PARM_DESC(ip4_flt_tbl_lcl,
+		"where ip4 flt tables reside 1-local; 0-system");
+static bool ip6_flt_tbl_lcl = 1;
+module_param(ip6_flt_tbl_lcl, bool, 0644);
+MODULE_PARM_DESC(ip6_flt_tbl_lcl,
+		"where ip6 flt tables reside 1-local; 0-system");
+
+static int ipa_load_pipe_connection(struct platform_device *pdev,
+				    enum a2_mux_pipe_direction pipe_dir,
+				    struct a2_mux_pipe_connection     *pdata);
+
+static int ipa_update_connections_info(struct device_node *node,
+			struct a2_mux_pipe_connection *pipe_connection);
+
+static void ipa_set_aggregation_params(void);
+
+static ssize_t ipa_read(struct file *filp, char __user *buf, size_t count,
+		loff_t *f_pos)
+{
+	u32 reg_val = 0xfeedface;
+	char str[IPA_READ_MAX];
+	int result;
+	static int read_cnt;
+
+	if (read_cnt) {
+		IPAERR("only supports one call to read\n");
+		return 0;
+	}
+
+	reg_val = ipa_read_reg(ipa_ctx->mmio, IPA_COMP_HW_VERSION_OFST);
+	result = scnprintf(str, IPA_READ_MAX, "%x\n", reg_val);
+	if (copy_to_user(buf, str, result))
+		return -EFAULT;
+	read_cnt = 1;
+
+	return result;
+}
+
+static int ipa_open(struct inode *inode, struct file *filp)
+{
+	struct ipa_context *ctx = NULL;
+
+	IPADBG("ENTER\n");
+	ctx = container_of(inode->i_cdev, struct ipa_context, cdev);
+	filp->private_data = ctx;
+
+	return 0;
+}
+
+static long ipa_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+{
+	int retval = 0;
+	u32 pyld_sz;
+	u8 header[128] = { 0 };
+	u8 *param = NULL;
+	struct ipa_ioc_nat_alloc_mem nat_mem;
+	struct ipa_ioc_v4_nat_init nat_init;
+	struct ipa_ioc_v4_nat_del nat_del;
+
+	IPADBG("cmd=%x nr=%d\n", cmd, _IOC_NR(cmd));
+
+	if (_IOC_TYPE(cmd) != IPA_IOC_MAGIC)
+		return -ENOTTY;
+	if (_IOC_NR(cmd) >= IPA_IOCTL_MAX)
+		return -ENOTTY;
+
+	switch (cmd) {
+	case IPA_IOC_ALLOC_NAT_MEM:
+		if (copy_from_user((u8 *)&nat_mem, (u8 *)arg,
+					sizeof(struct ipa_ioc_nat_alloc_mem))) {
+			retval = -EFAULT;
+			break;
+		}
+
+		if (allocate_nat_device(&nat_mem)) {
+			retval = -EFAULT;
+			break;
+		}
+		if (copy_to_user((u8 *)arg, (u8 *)&nat_mem,
+					sizeof(struct ipa_ioc_nat_alloc_mem))) {
+			retval = -EFAULT;
+			break;
+		}
+		break;
+	case IPA_IOC_V4_INIT_NAT:
+		if (copy_from_user((u8 *)&nat_init, (u8 *)arg,
+					sizeof(struct ipa_ioc_v4_nat_init))) {
+			retval = -EFAULT;
+			break;
+		}
+		if (ipa_nat_init_cmd(&nat_init)) {
+			retval = -EFAULT;
+			break;
+		}
+		break;
+
+	case IPA_IOC_NAT_DMA:
+		if (copy_from_user(header, (u8 *)arg,
+					sizeof(struct ipa_ioc_nat_dma_cmd))) {
+			retval = -EFAULT;
+			break;
+		}
+
+		pyld_sz =
+		   sizeof(struct ipa_ioc_nat_dma_cmd) +
+		   ((struct ipa_ioc_nat_dma_cmd *)header)->entries *
+		   sizeof(struct ipa_ioc_nat_dma_one);
+		param = kzalloc(pyld_sz, GFP_KERNEL);
+		if (!param) {
+			retval = -ENOMEM;
+			break;
+		}
+
+		if (copy_from_user(param, (u8 *)arg, pyld_sz)) {
+			retval = -EFAULT;
+			break;
+		}
+
+		if (ipa_nat_dma_cmd((struct ipa_ioc_nat_dma_cmd *)param)) {
+			retval = -EFAULT;
+			break;
+		}
+		break;
+
+	case IPA_IOC_V4_DEL_NAT:
+		if (copy_from_user((u8 *)&nat_del, (u8 *)arg,
+					sizeof(struct ipa_ioc_v4_nat_del))) {
+			retval = -EFAULT;
+			break;
+		}
+		if (ipa_nat_del_cmd(&nat_del)) {
+			retval = -EFAULT;
+			break;
+		}
+		break;
+
+	case IPA_IOC_ADD_HDR:
+		if (copy_from_user(header, (u8 *)arg,
+					sizeof(struct ipa_ioc_add_hdr))) {
+			retval = -EFAULT;
+			break;
+		}
+		pyld_sz =
+		   sizeof(struct ipa_ioc_add_hdr) +
+		   ((struct ipa_ioc_add_hdr *)header)->num_hdrs *
+		   sizeof(struct ipa_hdr_add);
+		param = kzalloc(pyld_sz, GFP_KERNEL);
+		if (!param) {
+			retval = -ENOMEM;
+			break;
+		}
+		if (copy_from_user(param, (u8 *)arg, pyld_sz)) {
+			retval = -EFAULT;
+			break;
+		}
+		if (ipa_add_hdr((struct ipa_ioc_add_hdr *)param)) {
+			retval = -EFAULT;
+			break;
+		}
+		if (copy_to_user((u8 *)arg, param, pyld_sz)) {
+			retval = -EFAULT;
+			break;
+		}
+		break;
+
+	case IPA_IOC_DEL_HDR:
+		if (copy_from_user(header, (u8 *)arg,
+					sizeof(struct ipa_ioc_del_hdr))) {
+			retval = -EFAULT;
+			break;
+		}
+		pyld_sz =
+		   sizeof(struct ipa_ioc_del_hdr) +
+		   ((struct ipa_ioc_del_hdr *)header)->num_hdls *
+		   sizeof(struct ipa_hdr_del);
+		param = kzalloc(pyld_sz, GFP_KERNEL);
+		if (!param) {
+			retval = -ENOMEM;
+			break;
+		}
+		if (copy_from_user(param, (u8 *)arg, pyld_sz)) {
+			retval = -EFAULT;
+			break;
+		}
+		if (ipa_del_hdr((struct ipa_ioc_del_hdr *)param)) {
+			retval = -EFAULT;
+			break;
+		}
+		if (copy_to_user((u8 *)arg, param, pyld_sz)) {
+			retval = -EFAULT;
+			break;
+		}
+		break;
+
+	case IPA_IOC_ADD_RT_RULE:
+		if (copy_from_user(header, (u8 *)arg,
+					sizeof(struct ipa_ioc_add_rt_rule))) {
+			retval = -EFAULT;
+			break;
+		}
+		pyld_sz =
+		   sizeof(struct ipa_ioc_add_rt_rule) +
+		   ((struct ipa_ioc_add_rt_rule *)header)->num_rules *
+		   sizeof(struct ipa_rt_rule_add);
+		param = kzalloc(pyld_sz, GFP_KERNEL);
+		if (!param) {
+			retval = -ENOMEM;
+			break;
+		}
+		if (copy_from_user(param, (u8 *)arg, pyld_sz)) {
+			retval = -EFAULT;
+			break;
+		}
+		if (ipa_add_rt_rule((struct ipa_ioc_add_rt_rule *)param)) {
+			retval = -EFAULT;
+			break;
+		}
+		if (copy_to_user((u8 *)arg, param, pyld_sz)) {
+			retval = -EFAULT;
+			break;
+		}
+		break;
+
+	case IPA_IOC_DEL_RT_RULE:
+		if (copy_from_user(header, (u8 *)arg,
+					sizeof(struct ipa_ioc_del_rt_rule))) {
+			retval = -EFAULT;
+			break;
+		}
+		pyld_sz =
+		   sizeof(struct ipa_ioc_del_rt_rule) +
+		   ((struct ipa_ioc_del_rt_rule *)header)->num_hdls *
+		   sizeof(struct ipa_rt_rule_del);
+		param = kzalloc(pyld_sz, GFP_KERNEL);
+		if (!param) {
+			retval = -ENOMEM;
+			break;
+		}
+		if (copy_from_user(param, (u8 *)arg, pyld_sz)) {
+			retval = -EFAULT;
+			break;
+		}
+		if (ipa_del_rt_rule((struct ipa_ioc_del_rt_rule *)param)) {
+			retval = -EFAULT;
+			break;
+		}
+		if (copy_to_user((u8 *)arg, param, pyld_sz)) {
+			retval = -EFAULT;
+			break;
+		}
+		break;
+
+	case IPA_IOC_ADD_FLT_RULE:
+		if (copy_from_user(header, (u8 *)arg,
+					sizeof(struct ipa_ioc_add_flt_rule))) {
+			retval = -EFAULT;
+			break;
+		}
+		pyld_sz =
+		   sizeof(struct ipa_ioc_add_flt_rule) +
+		   ((struct ipa_ioc_add_flt_rule *)header)->num_rules *
+		   sizeof(struct ipa_flt_rule_add);
+		param = kzalloc(pyld_sz, GFP_KERNEL);
+		if (!param) {
+			retval = -ENOMEM;
+			break;
+		}
+		if (copy_from_user(param, (u8 *)arg, pyld_sz)) {
+			retval = -EFAULT;
+			break;
+		}
+		if (ipa_add_flt_rule((struct ipa_ioc_add_flt_rule *)param)) {
+			retval = -EFAULT;
+			break;
+		}
+		if (copy_to_user((u8 *)arg, param, pyld_sz)) {
+			retval = -EFAULT;
+			break;
+		}
+		break;
+
+	case IPA_IOC_DEL_FLT_RULE:
+		if (copy_from_user(header, (u8 *)arg,
+					sizeof(struct ipa_ioc_del_flt_rule))) {
+			retval = -EFAULT;
+			break;
+		}
+		pyld_sz =
+		   sizeof(struct ipa_ioc_del_flt_rule) +
+		   ((struct ipa_ioc_del_flt_rule *)header)->num_hdls *
+		   sizeof(struct ipa_flt_rule_del);
+		param = kzalloc(pyld_sz, GFP_KERNEL);
+		if (!param) {
+			retval = -ENOMEM;
+			break;
+		}
+		if (copy_from_user(param, (u8 *)arg, pyld_sz)) {
+			retval = -EFAULT;
+			break;
+		}
+		if (ipa_del_flt_rule((struct ipa_ioc_del_flt_rule *)param)) {
+			retval = -EFAULT;
+			break;
+		}
+		if (copy_to_user((u8 *)arg, param, pyld_sz)) {
+			retval = -EFAULT;
+			break;
+		}
+		break;
+
+	case IPA_IOC_COMMIT_HDR:
+		retval = ipa_commit_hdr();
+		break;
+	case IPA_IOC_RESET_HDR:
+		retval = ipa_reset_hdr();
+		break;
+	case IPA_IOC_COMMIT_RT:
+		retval = ipa_commit_rt(arg);
+		break;
+	case IPA_IOC_RESET_RT:
+		retval = ipa_reset_rt(arg);
+		break;
+	case IPA_IOC_COMMIT_FLT:
+		retval = ipa_commit_flt(arg);
+		break;
+	case IPA_IOC_RESET_FLT:
+		retval = ipa_reset_flt(arg);
+		break;
+	case IPA_IOC_DUMP:
+		ipa_dump();
+		break;
+	case IPA_IOC_GET_RT_TBL:
+		if (copy_from_user(header, (u8 *)arg,
+					sizeof(struct ipa_ioc_get_rt_tbl))) {
+			retval = -EFAULT;
+			break;
+		}
+		if (ipa_get_rt_tbl((struct ipa_ioc_get_rt_tbl *)header)) {
+			retval = -EFAULT;
+			break;
+		}
+		if (copy_to_user((u8 *)arg, header,
+					sizeof(struct ipa_ioc_get_rt_tbl))) {
+			retval = -EFAULT;
+			break;
+		}
+		break;
+	case IPA_IOC_PUT_RT_TBL:
+		retval = ipa_put_rt_tbl(arg);
+		break;
+	case IPA_IOC_GET_HDR:
+		if (copy_from_user(header, (u8 *)arg,
+					sizeof(struct ipa_ioc_get_hdr))) {
+			retval = -EFAULT;
+			break;
+		}
+		if (ipa_get_hdr((struct ipa_ioc_get_hdr *)header)) {
+			retval = -EFAULT;
+			break;
+		}
+		if (copy_to_user((u8 *)arg, header,
+					sizeof(struct ipa_ioc_get_hdr))) {
+			retval = -EFAULT;
+			break;
+		}
+		break;
+	case IPA_IOC_PUT_HDR:
+		retval = ipa_put_hdr(arg);
+		break;
+	case IPA_IOC_SET_FLT:
+		retval = ipa_cfg_filter(arg);
+		break;
+	case IPA_IOC_COPY_HDR:
+		if (copy_from_user(header, (u8 *)arg,
+					sizeof(struct ipa_ioc_copy_hdr))) {
+			retval = -EFAULT;
+			break;
+		}
+		if (ipa_copy_hdr((struct ipa_ioc_copy_hdr *)header)) {
+			retval = -EFAULT;
+			break;
+		}
+		if (copy_to_user((u8 *)arg, header,
+					sizeof(struct ipa_ioc_copy_hdr))) {
+			retval = -EFAULT;
+			break;
+		}
+		break;
+	default:        /* redundant, as cmd was checked against MAXNR */
+		return -ENOTTY;
+	}
+	kfree(param);
+
+	return retval;
+}
+
+/**
+* ipa_setup_dflt_rt_tables() - Setup default routing tables
+*
+* Return codes:
+* 0: success
+* -ENOMEM: failed to allocate memory
+* -EPERM: failed to add the tables
+*/
+int ipa_setup_dflt_rt_tables(void)
+{
+	struct ipa_ioc_add_rt_rule *rt_rule;
+	struct ipa_rt_rule_add *rt_rule_entry;
+
+	rt_rule =
+	   kzalloc(sizeof(struct ipa_ioc_add_rt_rule) + 1 *
+			   sizeof(struct ipa_rt_rule_add), GFP_KERNEL);
+	if (!rt_rule) {
+		IPAERR("fail to alloc mem\n");
+		return -ENOMEM;
+	}
+	/* setup a default v4 route to point to A5 */
+	rt_rule->num_rules = 1;
+	rt_rule->commit = 1;
+	rt_rule->ip = IPA_IP_v4;
+	strlcpy(rt_rule->rt_tbl_name, IPA_DFLT_RT_TBL_NAME,
+			IPA_RESOURCE_NAME_MAX);
+
+	rt_rule_entry = &rt_rule->rules[0];
+	rt_rule_entry->at_rear = 1;
+	rt_rule_entry->rule.dst = IPA_CLIENT_A5_LAN_WAN_CONS;
+	rt_rule_entry->rule.hdr_hdl = ipa_ctx->excp_hdr_hdl;
+
+	if (ipa_add_rt_rule(rt_rule)) {
+		IPAERR("fail to add dflt v4 rule\n");
+		kfree(rt_rule);
+		return -EPERM;
+	}
+	IPADBG("dflt v4 rt rule hdl=%x\n", rt_rule_entry->rt_rule_hdl);
+	ipa_ctx->dflt_v4_rt_rule_hdl = rt_rule_entry->rt_rule_hdl;
+
+	/* setup a default v6 route to point to A5 */
+	rt_rule->ip = IPA_IP_v6;
+	if (ipa_add_rt_rule(rt_rule)) {
+		IPAERR("fail to add dflt v6 rule\n");
+		kfree(rt_rule);
+		return -EPERM;
+	}
+	IPADBG("dflt v6 rt rule hdl=%x\n", rt_rule_entry->rt_rule_hdl);
+	ipa_ctx->dflt_v6_rt_rule_hdl = rt_rule_entry->rt_rule_hdl;
+
+	/*
+	 * because these tables are the very first to be added, they will both
+	 * have the same index (0) which is essential for programming the
+	 * "route" end-point config
+	 */
+
+	kfree(rt_rule);
+
+	return 0;
+}
+
+static int ipa_setup_exception_path(void)
+{
+	struct ipa_ioc_add_hdr *hdr;
+	struct ipa_hdr_add *hdr_entry;
+	struct ipa_route route = { 0 };
+	int ret;
+
+	/* install the basic exception header */
+	hdr = kzalloc(sizeof(struct ipa_ioc_add_hdr) + 1 *
+		      sizeof(struct ipa_hdr_add), GFP_KERNEL);
+	if (!hdr) {
+		IPAERR("fail to alloc exception hdr\n");
+		return -ENOMEM;
+	}
+	hdr->num_hdrs = 1;
+	hdr->commit = 1;
+	hdr_entry = &hdr->hdr[0];
+	strlcpy(hdr_entry->name, IPA_DFLT_HDR_NAME, IPA_RESOURCE_NAME_MAX);
+
+	/*
+	 * only single stream for MBIM supported and no exception packets
+	 * expected so set default header to zero
+	 */
+	hdr_entry->hdr_len = 1;
+	hdr_entry->hdr[0] = 0;
+
+	/*
+	 * SW does not know anything about default exception header so
+	 * we don't set it. IPA HW will use it as a template
+	 */
+	if (ipa_add_hdr(hdr)) {
+		IPAERR("fail to add exception hdr\n");
+		ret = -EPERM;
+		goto bail;
+	}
+
+	if (hdr_entry->status) {
+		IPAERR("fail to add exception hdr\n");
+		ret = -EPERM;
+		goto bail;
+	}
+
+	ipa_ctx->excp_hdr_hdl = hdr_entry->hdr_hdl;
+
+	/* exception packets goto LAN-WAN pipe from IPA to A5 */
+	route.route_def_pipe = IPA_A5_LAN_WAN_IN;
+	route.route_def_hdr_table = !ipa_ctx->hdr_tbl_lcl;
+
+	if (ipa_cfg_route(&route)) {
+		IPAERR("fail to add exception hdr\n");
+		ret = -EPERM;
+		goto bail;
+	}
+
+	ret = 0;
+bail:
+	kfree(hdr);
+	return ret;
+}
+
+static void ipa_handle_tx_poll_for_pipe(struct ipa_sys_context *sys)
+{
+	struct ipa_tx_pkt_wrapper *tx_pkt, *t;
+	struct sps_iovec iov;
+	unsigned long irq_flags;
+	int ret;
+
+	while (1) {
+		iov.addr = 0;
+		ret = sps_get_iovec(sys->ep->ep_hdl, &iov);
+		if (ret) {
+			pr_err("%s: sps_get_iovec failed %d\n", __func__, ret);
+			break;
+		}
+		if (!iov.addr)
+			break;
+		spin_lock_irqsave(&sys->spinlock, irq_flags);
+		tx_pkt = list_first_entry(&sys->head_desc_list,
+					  struct ipa_tx_pkt_wrapper, link);
+		spin_unlock_irqrestore(&sys->spinlock, irq_flags);
+
+		switch (tx_pkt->cnt) {
+		case 1:
+			ipa_write_done(&tx_pkt->work);
+			break;
+		case 0xFFFF:
+			/* reached end of set */
+			spin_lock_irqsave(&sys->spinlock, irq_flags);
+			list_for_each_entry_safe(tx_pkt, t,
+						 &sys->wait_desc_list, link) {
+				list_del(&tx_pkt->link);
+				list_add(&tx_pkt->link, &sys->head_desc_list);
+			}
+			tx_pkt =
+			   list_first_entry(&sys->head_desc_list,
+					    struct ipa_tx_pkt_wrapper, link);
+			spin_unlock_irqrestore(&sys->spinlock, irq_flags);
+			ipa_write_done(&tx_pkt->work);
+			break;
+		default:
+			/* keep looping till reach the end of the set */
+			spin_lock_irqsave(&sys->spinlock,
+					  irq_flags);
+			list_del(&tx_pkt->link);
+			list_add_tail(&tx_pkt->link,
+				      &sys->wait_desc_list);
+			spin_unlock_irqrestore(&sys->spinlock,
+					       irq_flags);
+			break;
+		}
+	}
+}
+
+static void ipa_poll_function(struct work_struct *work)
+{
+	int ret;
+	int tx_pipes[] = { IPA_A5_CMD, IPA_A5_LAN_WAN_OUT,
+		IPA_A5_WLAN_AMPDU_OUT };
+	int i;
+	int num_tx_pipes;
+
+	/* check all the system pipes for tx completions and rx available */
+	if (ipa_ctx->sys[IPA_A5_LAN_WAN_IN].ep->valid)
+		ipa_handle_rx_core();
+
+	num_tx_pipes = sizeof(tx_pipes) / sizeof(tx_pipes[0]);
+
+	if (!IPA_MOBILE_AP_MODE(ipa_ctx->mode))
+		num_tx_pipes--;
+
+	for (i = 0; i < num_tx_pipes; i++)
+		if (ipa_ctx->sys[tx_pipes[i]].ep->valid)
+			ipa_handle_tx_poll_for_pipe(&ipa_ctx->sys[tx_pipes[i]]);
+
+	/* re-post the poll work */
+	INIT_DELAYED_WORK(&ipa_ctx->poll_work, ipa_poll_function);
+	ret = schedule_delayed_work_on(smp_processor_id(), &ipa_ctx->poll_work,
+			msecs_to_jiffies(polling_delay_ms));
+
+	return;
+}
+
+static int ipa_setup_a5_pipes(void)
+{
+	struct ipa_sys_connect_params sys_in;
+	int result = 0;
+
+	/* CMD OUT (A5->IPA) */
+	memset(&sys_in, 0, sizeof(struct ipa_sys_connect_params));
+	sys_in.client = IPA_CLIENT_A5_CMD_PROD;
+	sys_in.desc_fifo_sz = IPA_SYS_DESC_FIFO_SZ;
+	sys_in.ipa_ep_cfg.mode.mode = IPA_DMA;
+	sys_in.ipa_ep_cfg.mode.dst = IPA_CLIENT_A5_LAN_WAN_CONS;
+	if (ipa_setup_sys_pipe(&sys_in, &ipa_ctx->clnt_hdl_cmd)) {
+		IPAERR(":setup sys pipe failed.\n");
+		result = -EPERM;
+		goto fail;
+	}
+
+	if (ipa_setup_exception_path()) {
+		IPAERR(":fail to setup excp path\n");
+		result = -EPERM;
+		goto fail_cmd;
+	}
+
+	/* LAN-WAN IN (IPA->A5) */
+	memset(&sys_in, 0, sizeof(struct ipa_sys_connect_params));
+	sys_in.client = IPA_CLIENT_A5_LAN_WAN_CONS;
+	sys_in.desc_fifo_sz = IPA_SYS_DESC_FIFO_SZ;
+	sys_in.ipa_ep_cfg.hdr.hdr_a5_mux = 1;
+	sys_in.ipa_ep_cfg.hdr.hdr_len = 8;  /* size of A5 exception hdr */
+	if (ipa_setup_sys_pipe(&sys_in, &ipa_ctx->clnt_hdl_data_in)) {
+		IPAERR(":setup sys pipe failed.\n");
+		result = -EPERM;
+		goto fail_cmd;
+	}
+	/* LAN-WAN OUT (A5->IPA) */
+	memset(&sys_in, 0, sizeof(struct ipa_sys_connect_params));
+	sys_in.client = IPA_CLIENT_A5_LAN_WAN_PROD;
+	sys_in.desc_fifo_sz = IPA_SYS_DESC_FIFO_SZ;
+	sys_in.ipa_ep_cfg.mode.mode = IPA_BASIC;
+	sys_in.ipa_ep_cfg.mode.dst = IPA_CLIENT_A5_LAN_WAN_CONS;
+	if (ipa_setup_sys_pipe(&sys_in, &ipa_ctx->clnt_hdl_data_out)) {
+		IPAERR(":setup sys pipe failed.\n");
+		result = -EPERM;
+		goto fail_data_out;
+	}
+	if (ipa_ctx->polling_mode) {
+		INIT_DELAYED_WORK(&ipa_ctx->poll_work, ipa_poll_function);
+		result =
+		   schedule_delayed_work_on(smp_processor_id(),
+					&ipa_ctx->poll_work,
+					msecs_to_jiffies(polling_delay_ms));
+		if (!result) {
+			IPAERR(":schedule delayed work failed.\n");
+			goto fail_schedule_delayed_work;
+		}
+	}
+
+	return 0;
+
+fail_schedule_delayed_work:
+	ipa_teardown_sys_pipe(ipa_ctx->clnt_hdl_data_out);
+fail_data_out:
+	ipa_teardown_sys_pipe(ipa_ctx->clnt_hdl_data_in);
+fail_cmd:
+	ipa_teardown_sys_pipe(ipa_ctx->clnt_hdl_cmd);
+fail:
+	return result;
+}
+
+static void ipa_teardown_a5_pipes(void)
+{
+	cancel_delayed_work(&ipa_ctx->poll_work);
+	ipa_teardown_sys_pipe(ipa_ctx->clnt_hdl_data_out);
+	ipa_teardown_sys_pipe(ipa_ctx->clnt_hdl_data_in);
+	ipa_teardown_sys_pipe(ipa_ctx->clnt_hdl_cmd);
+}
+
+static int ipa_load_pipe_connection(struct platform_device *pdev,
+				    enum a2_mux_pipe_direction  pipe_dir,
+				    struct a2_mux_pipe_connection *pdata)
+{
+	struct device_node *node = pdev->dev.of_node;
+	int rc = 0;
+
+	if (!pdata || !pdev)
+		goto err;
+
+	/* retrieve device tree parameters */
+	for_each_child_of_node(pdev->dev.of_node, node)
+	{
+		const char *str;
+
+		rc = of_property_read_string(node, "label", &str);
+		if (rc) {
+			IPAERR("Cannot read string\n");
+			goto err;
+		}
+
+		/* Check if connection type is supported */
+		if (strncmp(str, "a2-to-ipa", 10)
+			&& strncmp(str, "ipa-to-a2", 10))
+			goto err;
+
+		if (strnstr(str, "a2-to-ipa", strnlen("a2-to-ipa", 10))
+				&& IPA_TO_A2 == pipe_dir)
+			continue; /* skip to the next pipe */
+		else if (strnstr(str, "ipa-to-a2", strnlen("ipa-to-a2", 10))
+				&& A2_TO_IPA == pipe_dir)
+			continue; /* skip to the next pipe */
+
+
+		rc = ipa_update_connections_info(node, pdata);
+		if (rc)
+			goto err;
+	}
+
+	return 0;
+err:
+	IPAERR("%s: failed\n", __func__);
+
+	return rc;
+}
+
+static int ipa_update_connections_info(struct device_node *node,
+		struct a2_mux_pipe_connection     *pipe_connection)
+{
+	u32      rc;
+	char     *key;
+	uint32_t val;
+	enum ipa_pipe_mem_type mem_type;
+
+	if (!pipe_connection || !node)
+		goto err;
+
+	key = "qcom,src-bam-physical-address";
+	rc = of_property_read_u32(node, key, &val);
+	if (rc)
+		goto err;
+	pipe_connection->src_phy_addr = val;
+
+	key = "qcom,ipa-bam-mem-type";
+	rc = of_property_read_u32(node, key, &mem_type);
+	if (rc)
+		goto err;
+	pipe_connection->mem_type = mem_type;
+
+	key = "qcom,src-bam-pipe-index";
+	rc = of_property_read_u32(node, key, &val);
+	if (rc)
+		goto err;
+	pipe_connection->src_pipe_index = val;
+
+	key = "qcom,dst-bam-physical-address";
+	rc = of_property_read_u32(node, key, &val);
+	if (rc)
+		goto err;
+	pipe_connection->dst_phy_addr = val;
+
+	key = "qcom,dst-bam-pipe-index";
+	rc = of_property_read_u32(node, key, &val);
+	if (rc)
+		goto err;
+	pipe_connection->dst_pipe_index = val;
+
+	key = "qcom,data-fifo-offset";
+	rc = of_property_read_u32(node, key, &val);
+	if (rc)
+		goto err;
+	pipe_connection->data_fifo_base_offset = val;
+
+	key = "qcom,data-fifo-size";
+	rc = of_property_read_u32(node, key, &val);
+	if (rc)
+		goto err;
+	pipe_connection->data_fifo_size = val;
+
+	key = "qcom,descriptor-fifo-offset";
+	rc = of_property_read_u32(node, key, &val);
+	if (rc)
+		goto err;
+	pipe_connection->desc_fifo_base_offset = val;
+
+	key = "qcom,descriptor-fifo-size";
+	rc = of_property_read_u32(node, key, &val);
+	if (rc)
+		goto err;
+
+	pipe_connection->desc_fifo_size = val;
+
+	return 0;
+err:
+	IPAERR("%s: Error in name %s key %s\n", __func__, node->full_name, key);
+
+	return rc;
+}
+
+/**
+* ipa_get_a2_mux_pipe_info() - Exposes A2 parameters fetched from DTS
+*
+* @pipe_dir: pipe direction
+* @pipe_connect: connect structure containing the parameters fetched from DTS
+*
+* Return codes:
+* 0: success
+* -EFAULT: invalid parameters
+*/
+int ipa_get_a2_mux_pipe_info(enum a2_mux_pipe_direction  pipe_dir,
+			     struct a2_mux_pipe_connection *pipe_connect)
+{
+	if (!pipe_connect) {
+		IPAERR("ipa_get_a2_mux_pipe_info switch null args\n");
+		return -EFAULT;
+	}
+
+	switch (pipe_dir) {
+	case A2_TO_IPA:
+		*pipe_connect = ipa_res.a2_to_ipa_pipe;
+		break;
+	case IPA_TO_A2:
+		*pipe_connect = ipa_res.ipa_to_a2_pipe;
+		break;
+	default:
+		IPAERR("ipa_get_a2_mux_pipe_info switch in default\n");
+		return -EFAULT;
+	}
+
+	return 0;
+}
+
+static void ipa_set_aggregation_params(void)
+{
+	struct ipa_ep_cfg_aggr agg_params;
+	u32 producer_hdl = 0;
+	u32 consumer_hdl = 0;
+
+	rmnet_bridge_get_client_handles(&producer_hdl, &consumer_hdl);
+
+	agg_params.aggr = ipa_ctx->aggregation_type;
+	agg_params.aggr_byte_limit = ipa_ctx->aggregation_byte_limit;
+	agg_params.aggr_time_limit = ipa_ctx->aggregation_time_limit;
+
+	/* configure aggregation on producer */
+	agg_params.aggr_en = IPA_ENABLE_AGGR;
+	ipa_cfg_ep_aggr(producer_hdl, &agg_params);
+
+	/* configure deaggregation on consumer */
+	agg_params.aggr_en = IPA_ENABLE_DEAGGR;
+	ipa_cfg_ep_aggr(consumer_hdl, &agg_params);
+
+}
+
+/*
+ * The following device attributes are for configuring the aggregation
+ * attributes when the driver is already running.
+ * The attributes are for configuring the aggregation type
+ * (MBIM_16/MBIM_32/TLP), the aggregation byte limit and the aggregation
+ * time limit.
+ */
+static ssize_t ipa_show_aggregation_type(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	ssize_t ret_val;
+	char str[IPA_AGGR_MAX_STR_LENGTH];
+
+	if (!buf) {
+		IPAERR("buffer for ipa_show_aggregation_type is NULL\n");
+		return -EINVAL;
+	}
+
+	memset(str, 0, sizeof(str));
+
+	switch (ipa_ctx->aggregation_type) {
+	case IPA_MBIM_16:
+		strlcpy(str, "MBIM_16", IPA_AGGR_STR_IN_BYTES("MBIM_16"));
+		break;
+	case IPA_MBIM_32:
+		strlcpy(str, "MBIM_32", IPA_AGGR_STR_IN_BYTES("MBIM_32"));
+		break;
+	case IPA_TLP:
+		strlcpy(str, "TLP", IPA_AGGR_STR_IN_BYTES("TLP"));
+		break;
+	default:
+		strlcpy(str, "NONE", IPA_AGGR_STR_IN_BYTES("NONE"));
+		break;
+	}
+
+	ret_val = scnprintf(buf, PAGE_SIZE, "%s\n", str);
+
+	return ret_val;
+}
+
+static ssize_t ipa_store_aggregation_type(struct device *dev,
+				     struct device_attribute *attr,
+				     const char *buf, size_t count)
+{
+	char str[IPA_AGGR_MAX_STR_LENGTH], *pstr;
+
+	if (!buf) {
+		IPAERR("buffer for ipa_store_aggregation_type is NULL\n");
+		return -EINVAL;
+	}
+
+	strlcpy(str, buf, sizeof(str));
+	pstr = strim(str);
+
+	if (!strncmp(pstr, "MBIM_16", IPA_AGGR_STR_IN_BYTES("MBIM_16")))
+		ipa_ctx->aggregation_type = IPA_MBIM_16;
+	else if (!strncmp(pstr, "MBIM_32", IPA_AGGR_STR_IN_BYTES("MBIM_32")))
+		ipa_ctx->aggregation_type = IPA_MBIM_32;
+	else if (!strncmp(pstr, "TLP", IPA_AGGR_STR_IN_BYTES("TLP")))
+		ipa_ctx->aggregation_type = IPA_TLP;
+	else {
+		IPAERR("ipa_store_aggregation_type wrong input\n");
+		return -EINVAL;
+	}
+
+	ipa_set_aggregation_params();
+
+	return count;
+}
+
+static DEVICE_ATTR(aggregation_type, S_IWUSR | S_IRUSR,
+		ipa_show_aggregation_type,
+		ipa_store_aggregation_type);
+
+static ssize_t ipa_show_aggregation_byte_limit(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	ssize_t ret_val;
+
+	if (!buf) {
+		IPAERR("buffer for ipa_show_aggregation_byte_limit is NULL\n");
+		return -EINVAL;
+	}
+
+	ret_val = scnprintf(buf, PAGE_SIZE, "%u\n",
+			    ipa_ctx->aggregation_byte_limit);
+
+	return ret_val;
+}
+
+static ssize_t ipa_store_aggregation_byte_limit(struct device *dev,
+				     struct device_attribute *attr,
+				     const char *buf, size_t count)
+{
+	char str[IPA_AGGR_MAX_STR_LENGTH];
+	char *pstr;
+	u32 ret = 0;
+
+	if (!buf) {
+		IPAERR("buffer for ipa_store_aggregation_byte_limit is NULL\n");
+		return -EINVAL;
+	}
+
+	strlcpy(str, buf, sizeof(str));
+	pstr = strim(str);
+
+	if (kstrtouint(pstr, IPA_AGGR_MAX_STR_LENGTH, &ret)) {
+		IPAERR("ipa_store_aggregation_byte_limit wrong input\n");
+		return -EINVAL;
+	}
+
+	ipa_ctx->aggregation_byte_limit = ret;
+
+	ipa_set_aggregation_params();
+
+	return count;
+}
+
+static DEVICE_ATTR(aggregation_byte_limit, S_IWUSR | S_IRUSR,
+		ipa_show_aggregation_byte_limit,
+		ipa_store_aggregation_byte_limit);
+
+static ssize_t ipa_show_aggregation_time_limit(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	ssize_t ret_val;
+
+	if (!buf) {
+		IPAERR("buffer for ipa_show_aggregation_time_limit is NULL\n");
+		return -EINVAL;
+	}
+
+	ret_val = scnprintf(buf,
+			    PAGE_SIZE,
+			    "%u\n",
+			    ipa_ctx->aggregation_time_limit);
+
+	return ret_val;
+}
+
+static ssize_t ipa_store_aggregation_time_limit(struct device *dev,
+				     struct device_attribute *attr,
+				     const char *buf, size_t count)
+{
+	char str[IPA_AGGR_MAX_STR_LENGTH], *pstr;
+	u32 ret = 0;
+
+	if (!buf) {
+		IPAERR("buffer for ipa_store_aggregation_time_limit is NULL\n");
+		return -EINVAL;
+	}
+
+	strlcpy(str, buf, sizeof(str));
+	pstr = strim(str);
+
+	if (kstrtouint(pstr, IPA_AGGR_MAX_STR_LENGTH, &ret)) {
+		IPAERR("ipa_store_aggregation_time_limit wrong input\n");
+		return -EINVAL;
+	}
+
+	ipa_ctx->aggregation_time_limit = ret;
+
+	ipa_set_aggregation_params();
+
+	return count;
+}
+
+static DEVICE_ATTR(aggregation_time_limit, S_IWUSR | S_IRUSR,
+		ipa_show_aggregation_time_limit,
+		ipa_store_aggregation_time_limit);
+
+static const struct file_operations ipa_drv_fops = {
+	.owner = THIS_MODULE,
+	.open = ipa_open,
+	.read = ipa_read,
+	.unlocked_ioctl = ipa_ioctl,
+};
+
+static int ipa_get_clks(struct device *dev)
+{
+	ipa_cnoc_clk = clk_get(dev, "iface_clk");
+	if (IS_ERR(ipa_cnoc_clk)) {
+		ipa_cnoc_clk = NULL;
+		IPAERR("fail to get cnoc clk\n");
+		return -ENODEV;
+	}
+
+	ipa_clk_src = clk_get(dev, "core_src_clk");
+	if (IS_ERR(ipa_clk_src)) {
+		ipa_clk_src = NULL;
+		IPAERR("fail to get ipa clk src\n");
+		return -ENODEV;
+	}
+
+	ipa_clk = clk_get(dev, "core_clk");
+	if (IS_ERR(ipa_clk)) {
+		ipa_clk = NULL;
+		IPAERR("fail to get ipa clk\n");
+		return -ENODEV;
+	}
+
+	sys_noc_ipa_axi_clk = clk_get(dev, "bus_clk");
+	if (IS_ERR(sys_noc_ipa_axi_clk)) {
+		sys_noc_ipa_axi_clk = NULL;
+		IPAERR("fail to get sys_noc_ipa_axi clk\n");
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+/**
+* ipa_enable_clks() - Turn on IPA clocks
+*
+* Return codes:
+* None
+*/
+void ipa_enable_clks(void)
+{
+	if (ipa_cnoc_clk) {
+		clk_prepare(ipa_cnoc_clk);
+		clk_enable(ipa_cnoc_clk);
+		clk_set_rate(ipa_cnoc_clk, IPA_CNOC_CLK_RATE);
+	} else {
+		WARN_ON(1);
+	}
+
+	if (ipa_clk_src)
+		clk_set_rate(ipa_clk_src, IPA_V1_CLK_RATE);
+	else
+		WARN_ON(1);
+
+	if (ipa_clk)
+		clk_prepare(ipa_clk);
+	else
+		WARN_ON(1);
+
+	if (sys_noc_ipa_axi_clk)
+		clk_prepare(sys_noc_ipa_axi_clk);
+	else
+		WARN_ON(1);
+
+	if (ipa_clk)
+		clk_enable(ipa_clk);
+	else
+		WARN_ON(1);
+
+	if (sys_noc_ipa_axi_clk)
+		clk_enable(sys_noc_ipa_axi_clk);
+	else
+		WARN_ON(1);
+}
+
+/**
+* ipa_disable_clks() - Turn off IPA clocks
+*
+* Return codes:
+* None
+*/
+void ipa_disable_clks(void)
+{
+	if (sys_noc_ipa_axi_clk)
+		clk_disable_unprepare(sys_noc_ipa_axi_clk);
+	else
+		WARN_ON(1);
+
+	if (ipa_clk)
+		clk_disable_unprepare(ipa_clk);
+	else
+		WARN_ON(1);
+
+	if (ipa_cnoc_clk)
+		clk_disable_unprepare(ipa_cnoc_clk);
+	else
+		WARN_ON(1);
+}
+
+static int ipa_setup_bam_cfg(const struct ipa_plat_drv_res *res)
+{
+	void *bam_cnfg_bits;
+
+	bam_cnfg_bits = ioremap(res->ipa_mem_base + IPA_BAM_REG_BASE_OFST,
+				IPA_BAM_REMAP_SIZE);
+	if (!bam_cnfg_bits)
+		return -ENOMEM;
+	ipa_write_reg(bam_cnfg_bits, IPA_BAM_CNFG_BITS_OFST,
+		      IPA_BAM_CNFG_BITS_VAL);
+	iounmap(bam_cnfg_bits);
+
+	return 0;
+}
+/**
+* ipa_init() - Initialize the IPA Driver
+*@resource_p:	contain platform specific values from DST file
+*
+* Function initialization process:
+* - Allocate memory for the driver context data struct
+* - Initializing the ipa_ctx with:
+*    1)parsed values from the dts file
+*    2)parameters passed to the module initialization
+*    3)read HW values(such as core memory size)
+* - Map IPA core registers to CPU memory
+* - Restart IPA core(HW reset)
+* - Register IPA BAM to SPS driver and get a BAM handler
+* - Set configuration for IPA BAM via BAM_CNFG_BITS
+* - Initialize the look-aside caches(kmem_cache/slab) for filter,
+*   routing and IPA-tree
+* - Create memory pool with 4 objects for DMA operations(each object
+*   is 512Bytes long), this object will be use for tx(A5->IPA)
+* - Initialize lists head(routing,filter,hdr,system pipes)
+* - Initialize mutexes (for ipa_ctx and NAT memory mutexes)
+* - Initialize spinlocks (for list related to A5<->IPA pipes)
+* - Initialize 2 single-threaded work-queue named "ipa rx wq" and "ipa tx wq"
+* - Initialize Red-Black-Tree(s) for handles of header,routing rule,
+*   routing table ,filtering rule
+* - Setup all A5<->IPA pipes by calling to ipa_setup_a5_pipes
+* - Preparing the descriptors for System pipes
+* - Initialize the filter block by committing IPV4 and IPV6 default rules
+* - Create empty routing table in system memory(no committing)
+* - Initialize pipes memory pool with ipa_pipe_mem_init for supported platforms
+* - Create a char-device for IPA
+*/
+static int ipa_init(const struct ipa_plat_drv_res *resource_p)
+{
+	int result = 0;
+	int i;
+	struct sps_bam_props bam_props = { 0 };
+	struct ipa_flt_tbl *flt_tbl;
+	struct ipa_rt_tbl_set *rset;
+
+	IPADBG("IPA init\n");
+
+	ipa_ctx = kzalloc(sizeof(*ipa_ctx), GFP_KERNEL);
+	if (!ipa_ctx) {
+		IPAERR(":kzalloc err.\n");
+		result = -ENOMEM;
+		goto fail_mem;
+	}
+
+	IPADBG("polling_mode=%u delay_ms=%u\n", polling_mode, polling_delay_ms);
+	ipa_ctx->polling_mode = polling_mode;
+	IPADBG("hdr_lcl=%u ip4_rt=%u ip6_rt=%u ip4_flt=%u ip6_flt=%u\n",
+	       hdr_tbl_lcl, ip4_rt_tbl_lcl, ip6_rt_tbl_lcl, ip4_flt_tbl_lcl,
+	       ip6_flt_tbl_lcl);
+	ipa_ctx->hdr_tbl_lcl = hdr_tbl_lcl;
+	ipa_ctx->ip4_rt_tbl_lcl = ip4_rt_tbl_lcl;
+	ipa_ctx->ip6_rt_tbl_lcl = ip6_rt_tbl_lcl;
+	ipa_ctx->ip4_flt_tbl_lcl = ip4_flt_tbl_lcl;
+	ipa_ctx->ip6_flt_tbl_lcl = ip6_flt_tbl_lcl;
+
+	ipa_ctx->ipa_wrapper_base = resource_p->ipa_mem_base;
+
+	/* setup IPA register access */
+	ipa_ctx->mmio = ioremap(resource_p->ipa_mem_base + IPA_REG_BASE_OFST,
+			resource_p->ipa_mem_size);
+	if (!ipa_ctx->mmio) {
+		IPAERR(":ipa-base ioremap err.\n");
+		result = -EFAULT;
+		goto fail_remap;
+	}
+	/* do POR programming to setup HW */
+	result = ipa_init_hw();
+	if (result) {
+		IPAERR(":error initializing driver.\n");
+		result = -ENODEV;
+		goto fail_init_hw;
+	}
+	/* read how much SRAM is available for SW use */
+	ipa_ctx->smem_sz = ipa_read_reg(ipa_ctx->mmio,
+			IPA_SHARED_MEM_SIZE_OFST);
+
+	if (IPA_RAM_END_OFST > ipa_ctx->smem_sz) {
+		IPAERR("SW expect more core memory, needed %d, avail %d\n",
+				IPA_RAM_END_OFST, ipa_ctx->smem_sz);
+		result = -ENOMEM;
+		goto fail_init_hw;
+	}
+	/* register IPA with SPS driver */
+	bam_props.phys_addr = resource_p->bam_mem_base;
+	bam_props.virt_addr = ioremap(resource_p->bam_mem_base,
+			resource_p->bam_mem_size);
+	if (!bam_props.virt_addr) {
+		IPAERR(":bam-base ioremap err.\n");
+		result = -EFAULT;
+		goto fail_bam_remap;
+	}
+	bam_props.virt_size = resource_p->bam_mem_size;
+	bam_props.irq = resource_p->bam_irq;
+	bam_props.num_pipes = IPA_NUM_PIPES;
+	bam_props.summing_threshold = IPA_SUMMING_THRESHOLD;
+	bam_props.event_threshold = IPA_EVENT_THRESHOLD;
+
+	result = sps_register_bam_device(&bam_props, &ipa_ctx->bam_handle);
+	if (result) {
+		IPAERR(":bam register err.\n");
+		result = -ENODEV;
+		goto fail_bam_register;
+	}
+
+	if (ipa_setup_bam_cfg(resource_p)) {
+		IPAERR(":bam cfg err.\n");
+		result = -ENODEV;
+		goto fail_flt_rule_cache;
+	}
+
+	/* set up the default op mode */
+	ipa_ctx->mode = IPA_MODE_USB_DONGLE;
+
+	/* init the lookaside cache */
+	ipa_ctx->flt_rule_cache = kmem_cache_create("IPA FLT",
+			sizeof(struct ipa_flt_entry), 0, 0, NULL);
+	if (!ipa_ctx->flt_rule_cache) {
+		IPAERR(":ipa flt cache create failed\n");
+		result = -ENOMEM;
+		goto fail_flt_rule_cache;
+	}
+	ipa_ctx->rt_rule_cache = kmem_cache_create("IPA RT",
+			sizeof(struct ipa_rt_entry), 0, 0, NULL);
+	if (!ipa_ctx->rt_rule_cache) {
+		IPAERR(":ipa rt cache create failed\n");
+		result = -ENOMEM;
+		goto fail_rt_rule_cache;
+	}
+	ipa_ctx->hdr_cache = kmem_cache_create("IPA HDR",
+			sizeof(struct ipa_hdr_entry), 0, 0, NULL);
+	if (!ipa_ctx->hdr_cache) {
+		IPAERR(":ipa hdr cache create failed\n");
+		result = -ENOMEM;
+		goto fail_hdr_cache;
+	}
+	ipa_ctx->hdr_offset_cache =
+	   kmem_cache_create("IPA HDR OFF", sizeof(struct ipa_hdr_offset_entry),
+			   0, 0, NULL);
+	if (!ipa_ctx->hdr_offset_cache) {
+		IPAERR(":ipa hdr off cache create failed\n");
+		result = -ENOMEM;
+		goto fail_hdr_offset_cache;
+	}
+	ipa_ctx->rt_tbl_cache = kmem_cache_create("IPA RT TBL",
+			sizeof(struct ipa_rt_tbl), 0, 0, NULL);
+	if (!ipa_ctx->rt_tbl_cache) {
+		IPAERR(":ipa rt tbl cache create failed\n");
+		result = -ENOMEM;
+		goto fail_rt_tbl_cache;
+	}
+	ipa_ctx->tx_pkt_wrapper_cache =
+	   kmem_cache_create("IPA TX PKT WRAPPER",
+			   sizeof(struct ipa_tx_pkt_wrapper), 0, 0, NULL);
+	if (!ipa_ctx->tx_pkt_wrapper_cache) {
+		IPAERR(":ipa tx pkt wrapper cache create failed\n");
+		result = -ENOMEM;
+		goto fail_tx_pkt_wrapper_cache;
+	}
+	ipa_ctx->rx_pkt_wrapper_cache =
+	   kmem_cache_create("IPA RX PKT WRAPPER",
+			   sizeof(struct ipa_rx_pkt_wrapper), 0, 0, NULL);
+	if (!ipa_ctx->rx_pkt_wrapper_cache) {
+		IPAERR(":ipa rx pkt wrapper cache create failed\n");
+		result = -ENOMEM;
+		goto fail_rx_pkt_wrapper_cache;
+	}
+	ipa_ctx->tree_node_cache =
+	   kmem_cache_create("IPA TREE", sizeof(struct ipa_tree_node), 0, 0,
+			   NULL);
+	if (!ipa_ctx->tree_node_cache) {
+		IPAERR(":ipa tree node cache create failed\n");
+		result = -ENOMEM;
+		goto fail_tree_node_cache;
+	}
+
+	/*
+	 * setup DMA pool 4 byte aligned, don't cross 1k boundaries, nominal
+	 * size 512 bytes
+	 */
+	ipa_ctx->one_kb_no_straddle_pool = dma_pool_create("ipa_1k", NULL,
+			IPA_DMA_POOL_SIZE, IPA_DMA_POOL_ALIGNMENT,
+			IPA_DMA_POOL_BOUNDARY);
+	if (!ipa_ctx->one_kb_no_straddle_pool) {
+		IPAERR("cannot setup 1kb alloc DMA pool.\n");
+		result = -ENOMEM;
+		goto fail_dma_pool;
+	}
+
+	ipa_ctx->glob_flt_tbl[IPA_IP_v4].in_sys = !ipa_ctx->ip4_flt_tbl_lcl;
+	ipa_ctx->glob_flt_tbl[IPA_IP_v6].in_sys = !ipa_ctx->ip6_flt_tbl_lcl;
+
+	/* init the various list heads */
+	INIT_LIST_HEAD(&ipa_ctx->glob_flt_tbl[IPA_IP_v4].head_flt_rule_list);
+	INIT_LIST_HEAD(&ipa_ctx->glob_flt_tbl[IPA_IP_v6].head_flt_rule_list);
+	INIT_LIST_HEAD(&ipa_ctx->hdr_tbl.head_hdr_entry_list);
+	for (i = 0; i < IPA_HDR_BIN_MAX; i++) {
+		INIT_LIST_HEAD(&ipa_ctx->hdr_tbl.head_offset_list[i]);
+		INIT_LIST_HEAD(&ipa_ctx->hdr_tbl.head_free_offset_list[i]);
+	}
+	INIT_LIST_HEAD(&ipa_ctx->rt_tbl_set[IPA_IP_v4].head_rt_tbl_list);
+	INIT_LIST_HEAD(&ipa_ctx->rt_tbl_set[IPA_IP_v6].head_rt_tbl_list);
+	for (i = 0; i < IPA_NUM_PIPES; i++) {
+		flt_tbl = &ipa_ctx->flt_tbl[i][IPA_IP_v4];
+		INIT_LIST_HEAD(&flt_tbl->head_flt_rule_list);
+		flt_tbl->in_sys = !ipa_ctx->ip4_flt_tbl_lcl;
+
+		flt_tbl = &ipa_ctx->flt_tbl[i][IPA_IP_v6];
+		INIT_LIST_HEAD(&flt_tbl->head_flt_rule_list);
+		flt_tbl->in_sys = !ipa_ctx->ip6_flt_tbl_lcl;
+	}
+
+	rset = &ipa_ctx->reap_rt_tbl_set[IPA_IP_v4];
+	INIT_LIST_HEAD(&rset->head_rt_tbl_list);
+	rset = &ipa_ctx->reap_rt_tbl_set[IPA_IP_v6];
+	INIT_LIST_HEAD(&rset->head_rt_tbl_list);
+
+	mutex_init(&ipa_ctx->lock);
+	mutex_init(&ipa_ctx->nat_mem.lock);
+
+	for (i = 0; i < IPA_A5_SYS_MAX; i++) {
+		INIT_LIST_HEAD(&ipa_ctx->sys[i].head_desc_list);
+		spin_lock_init(&ipa_ctx->sys[i].spinlock);
+		if (i != IPA_A5_WLAN_AMPDU_OUT)
+			ipa_ctx->sys[i].ep = &ipa_ctx->ep[i];
+		else
+			ipa_ctx->sys[i].ep = &ipa_ctx->ep[WLAN_AMPDU_TX_EP];
+		INIT_LIST_HEAD(&ipa_ctx->sys[i].wait_desc_list);
+	}
+
+	ipa_ctx->rx_wq = create_singlethread_workqueue("ipa rx wq");
+	if (!ipa_ctx->rx_wq) {
+		IPAERR(":fail to create rx wq\n");
+		result = -ENOMEM;
+		goto fail_rx_wq;
+	}
+
+	ipa_ctx->tx_wq = create_singlethread_workqueue("ipa tx wq");
+	if (!ipa_ctx->tx_wq) {
+		IPAERR(":fail to create tx wq\n");
+		result = -ENOMEM;
+		goto fail_tx_wq;
+	}
+
+	ipa_ctx->hdr_hdl_tree = RB_ROOT;
+	ipa_ctx->rt_rule_hdl_tree = RB_ROOT;
+	ipa_ctx->rt_tbl_hdl_tree = RB_ROOT;
+	ipa_ctx->flt_rule_hdl_tree = RB_ROOT;
+
+	atomic_set(&ipa_ctx->ipa_active_clients, 0);
+
+	result = ipa_bridge_init();
+	if (result) {
+		IPAERR("ipa bridge init err.\n");
+		result = -ENODEV;
+		goto fail_bridge_init;
+	}
+
+	/* setup the A5-IPA pipes */
+	if (ipa_setup_a5_pipes()) {
+		IPAERR(":failed to setup IPA-A5 pipes.\n");
+		result = -ENODEV;
+		goto fail_a5_pipes;
+	}
+
+	ipa_replenish_rx_cache();
+
+	/* init the filtering block */
+	ipa_commit_flt(IPA_IP_v4);
+	ipa_commit_flt(IPA_IP_v6);
+
+	/*
+	 * setup an empty routing table in system memory, this will be used
+	 * to delete a routing table cleanly and safely
+	 */
+	ipa_ctx->empty_rt_tbl_mem.size = IPA_ROUTING_RULE_BYTE_SIZE;
+
+	ipa_ctx->empty_rt_tbl_mem.base =
+		dma_alloc_coherent(NULL, ipa_ctx->empty_rt_tbl_mem.size,
+				    &ipa_ctx->empty_rt_tbl_mem.phys_base,
+				    GFP_KERNEL);
+	if (!ipa_ctx->empty_rt_tbl_mem.base) {
+		IPAERR("DMA buff alloc fail %d bytes for empty routing tbl\n",
+				ipa_ctx->empty_rt_tbl_mem.size);
+		result = -ENOMEM;
+		goto fail_empty_rt_tbl;
+	}
+	memset(ipa_ctx->empty_rt_tbl_mem.base, 0,
+			ipa_ctx->empty_rt_tbl_mem.size);
+
+	/* setup the IPA pipe mem pool */
+	ipa_pipe_mem_init(resource_p->ipa_pipe_mem_start_ofst,
+			resource_p->ipa_pipe_mem_size);
+
+	ipa_ctx->class = class_create(THIS_MODULE, DRV_NAME);
+
+	result = alloc_chrdev_region(&ipa_ctx->dev_num, 0, 1, DRV_NAME);
+	if (result) {
+		IPAERR("alloc_chrdev_region err.\n");
+		result = -ENODEV;
+		goto fail_alloc_chrdev_region;
+	}
+
+	ipa_ctx->dev = device_create(ipa_ctx->class, NULL, ipa_ctx->dev_num,
+			ipa_ctx, DRV_NAME);
+	if (IS_ERR(ipa_ctx->dev)) {
+		IPAERR(":device_create err.\n");
+		result = -ENODEV;
+		goto fail_device_create;
+	}
+
+	cdev_init(&ipa_ctx->cdev, &ipa_drv_fops);
+	ipa_ctx->cdev.owner = THIS_MODULE;
+	ipa_ctx->cdev.ops = &ipa_drv_fops;  /* from LDD3 */
+
+	result = cdev_add(&ipa_ctx->cdev, ipa_ctx->dev_num, 1);
+	if (result) {
+		IPAERR(":cdev_add err=%d\n", -result);
+		result = -ENODEV;
+		goto fail_cdev_add;
+	}
+
+	/* default aggregation parameters */
+	ipa_ctx->aggregation_type = IPA_MBIM_16;
+	ipa_ctx->aggregation_byte_limit = 1;
+	ipa_ctx->aggregation_time_limit = 0;
+	IPADBG(":IPA driver init OK.\n");
+
+	/* gate IPA clocks */
+	ipa_disable_clks();
+
+	return 0;
+
+fail_cdev_add:
+	device_destroy(ipa_ctx->class, ipa_ctx->dev_num);
+fail_device_create:
+	unregister_chrdev_region(ipa_ctx->dev_num, 1);
+fail_alloc_chrdev_region:
+	if (ipa_ctx->pipe_mem_pool)
+		gen_pool_destroy(ipa_ctx->pipe_mem_pool);
+	dma_free_coherent(NULL,
+			  ipa_ctx->empty_rt_tbl_mem.size,
+			  ipa_ctx->empty_rt_tbl_mem.base,
+			  ipa_ctx->empty_rt_tbl_mem.phys_base);
+fail_empty_rt_tbl:
+	ipa_cleanup_rx();
+	ipa_teardown_a5_pipes();
+fail_a5_pipes:
+	ipa_bridge_cleanup();
+fail_bridge_init:
+	destroy_workqueue(ipa_ctx->tx_wq);
+fail_tx_wq:
+	destroy_workqueue(ipa_ctx->rx_wq);
+fail_rx_wq:
+	dma_pool_destroy(ipa_ctx->one_kb_no_straddle_pool);
+fail_dma_pool:
+	kmem_cache_destroy(ipa_ctx->tree_node_cache);
+fail_tree_node_cache:
+	kmem_cache_destroy(ipa_ctx->rx_pkt_wrapper_cache);
+fail_rx_pkt_wrapper_cache:
+	kmem_cache_destroy(ipa_ctx->tx_pkt_wrapper_cache);
+fail_tx_pkt_wrapper_cache:
+	kmem_cache_destroy(ipa_ctx->rt_tbl_cache);
+fail_rt_tbl_cache:
+	kmem_cache_destroy(ipa_ctx->hdr_offset_cache);
+fail_hdr_offset_cache:
+	kmem_cache_destroy(ipa_ctx->hdr_cache);
+fail_hdr_cache:
+	kmem_cache_destroy(ipa_ctx->rt_rule_cache);
+fail_rt_rule_cache:
+	kmem_cache_destroy(ipa_ctx->flt_rule_cache);
+fail_flt_rule_cache:
+	sps_deregister_bam_device(ipa_ctx->bam_handle);
+fail_bam_register:
+	iounmap(bam_props.virt_addr);
+fail_bam_remap:
+fail_init_hw:
+	iounmap(ipa_ctx->mmio);
+fail_remap:
+	kfree(ipa_ctx);
+	ipa_ctx = NULL;
+fail_mem:
+	/* gate IPA clocks */
+	ipa_disable_clks();
+	return result;
+}
+
+static int ipa_plat_drv_probe(struct platform_device *pdev_p)
+{
+	int result = 0;
+	struct resource *resource_p;
+	IPADBG("IPA plat drv probe\n");
+
+	/* initialize ipa_res */
+	ipa_res.ipa_pipe_mem_start_ofst = IPA_PIPE_MEM_START_OFST;
+	ipa_res.ipa_pipe_mem_size = IPA_PIPE_MEM_SIZE;
+
+	result = ipa_load_pipe_connection(pdev_p,
+					A2_TO_IPA,
+					&ipa_res.a2_to_ipa_pipe);
+	if (0 != result)
+		IPAERR(":ipa_load_pipe_connection failed!\n");
+
+	result = ipa_load_pipe_connection(pdev_p, IPA_TO_A2,
+					  &ipa_res.ipa_to_a2_pipe);
+	if (0 != result)
+		IPAERR(":ipa_load_pipe_connection failed!\n");
+
+	/* Get IPA wrapper address */
+	resource_p = platform_get_resource_byname(pdev_p, IORESOURCE_MEM,
+			"ipa-base");
+
+	if (!resource_p) {
+		IPAERR(":get resource failed for ipa-base!\n");
+		return -ENODEV;
+	} else {
+		ipa_res.ipa_mem_base = resource_p->start;
+		ipa_res.ipa_mem_size = resource_size(resource_p);
+	}
+
+	/* Get IPA BAM address */
+	resource_p = platform_get_resource_byname(pdev_p, IORESOURCE_MEM,
+			"bam-base");
+
+	if (!resource_p) {
+		IPAERR(":get resource failed for bam-base!\n");
+		return -ENODEV;
+	} else {
+		ipa_res.bam_mem_base = resource_p->start;
+		ipa_res.bam_mem_size = resource_size(resource_p);
+	}
+
+	/* Get IPA pipe mem start ofst */
+	resource_p = platform_get_resource_byname(pdev_p, IORESOURCE_MEM,
+			"ipa-pipe-mem");
+
+	if (!resource_p) {
+		IPADBG(":get resource failed for ipa-pipe-mem\n");
+	} else {
+		ipa_res.ipa_pipe_mem_start_ofst = resource_p->start;
+		ipa_res.ipa_pipe_mem_size = resource_size(resource_p);
+	}
+
+	/* Get IPA IRQ number */
+	resource_p = platform_get_resource_byname(pdev_p, IORESOURCE_IRQ,
+			"ipa-irq");
+
+	if (!resource_p) {
+		IPAERR(":get resource failed for ipa-irq!\n");
+		return -ENODEV;
+	} else {
+		ipa_res.ipa_irq = resource_p->start;
+	}
+
+	/* Get IPA BAM IRQ number */
+	resource_p = platform_get_resource_byname(pdev_p, IORESOURCE_IRQ,
+			"bam-irq");
+
+	if (!resource_p) {
+		IPAERR(":get resource failed for bam-irq!\n");
+		return -ENODEV;
+	} else {
+		ipa_res.bam_irq = resource_p->start;
+	}
+
+	IPADBG(":ipa_mem_base = 0x%x, ipa_mem_size = 0x%x\n",
+	       ipa_res.ipa_mem_base, ipa_res.ipa_mem_size);
+	IPADBG(":bam_mem_base = 0x%x, bam_mem_size = 0x%x\n",
+	       ipa_res.bam_mem_base, ipa_res.bam_mem_size);
+	IPADBG(":pipe_mem_start_ofst = 0x%x, pipe_mem_size = 0x%x\n",
+	       ipa_res.ipa_pipe_mem_start_ofst, ipa_res.ipa_pipe_mem_size);
+
+	IPADBG(":ipa_irq = %d\n", ipa_res.ipa_irq);
+	IPADBG(":bam_irq = %d\n", ipa_res.bam_irq);
+
+	/* stash the IPA dev ptr */
+	ipa_dev = &pdev_p->dev;
+
+	/* get IPA clocks */
+	if (ipa_get_clks(ipa_dev) != 0)
+		return -ENODEV;
+
+	/* enable IPA clocks */
+	ipa_enable_clks();
+
+	/* Proceed to real initialization */
+	result = ipa_init(&ipa_res);
+	if (result)
+		IPAERR("ipa_init failed\n");
+
+	result = device_create_file(&pdev_p->dev,
+			&dev_attr_aggregation_type);
+	if (result)
+		IPAERR("failed to create device file\n");
+
+	result = device_create_file(&pdev_p->dev,
+			&dev_attr_aggregation_byte_limit);
+	if (result)
+		IPAERR("failed to create device file\n");
+
+	result = device_create_file(&pdev_p->dev,
+			&dev_attr_aggregation_time_limit);
+	if (result)
+		IPAERR("failed to create device file\n");
+
+	return result;
+}
+
+static struct platform_driver ipa_plat_drv = {
+	.probe = ipa_plat_drv_probe,
+	.driver = {
+		.name = DRV_NAME,
+		.owner = THIS_MODULE,
+		.of_match_table = ipa_plat_drv_match,
+	},
+};
+
+static int ipa_plat_drv_init(void)
+{
+	return platform_driver_register(&ipa_plat_drv);
+}
+
+struct ipa_context *ipa_get_ctx(void)
+{
+	return ipa_ctx;
+}
+
+static int __init ipa_module_init(void)
+{
+	int result = 0;
+
+	IPADBG("IPA module init\n");
+	ipa_debugfs_init();
+	/* Register as a platform device driver */
+	result = ipa_plat_drv_init();
+
+	return result;
+}
+
+late_initcall(ipa_module_init);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("IPA HW device driver");
+
diff --git a/drivers/platform/msm/ipa/ipa_bridge.c b/drivers/platform/msm/ipa/ipa_bridge.c
new file mode 100644
index 0000000..cf51ab6
--- /dev/null
+++ b/drivers/platform/msm/ipa/ipa_bridge.c
@@ -0,0 +1,789 @@
+/* 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/delay.h>
+#include <linux/ratelimit.h>
+#include "ipa_i.h"
+
+enum ipa_bridge_id {
+	IPA_DL_FROM_A2,
+	IPA_DL_TO_IPA,
+	IPA_UL_FROM_IPA,
+	IPA_UL_TO_A2,
+	IPA_BRIDGE_ID_MAX
+};
+
+static int polling_min_sleep[IPA_DIR_MAX] = { 950, 950 };
+static int polling_max_sleep[IPA_DIR_MAX] = { 1050, 1050 };
+static int polling_inactivity[IPA_DIR_MAX] = { 20, 20 };
+
+struct ipa_pkt_info {
+	void *buffer;
+	dma_addr_t dma_address;
+	uint32_t len;
+	struct list_head list_node;
+};
+
+struct ipa_bridge_pipe_context {
+	struct list_head head_desc_list;
+	struct sps_pipe *pipe;
+	struct sps_connect connection;
+	struct sps_mem_buffer desc_mem_buf;
+	struct sps_register_event register_event;
+	spinlock_t spinlock;
+	u32 len;
+	u32 free_len;
+	struct list_head free_desc_list;
+};
+
+static struct ipa_bridge_pipe_context bridge[IPA_BRIDGE_ID_MAX];
+
+static struct workqueue_struct *ipa_ul_workqueue;
+static struct workqueue_struct *ipa_dl_workqueue;
+static void ipa_do_bridge_work(enum ipa_bridge_dir dir);
+
+static u32 alloc_cnt[IPA_DIR_MAX];
+
+static void ul_work_func(struct work_struct *work)
+{
+	ipa_do_bridge_work(IPA_UL);
+}
+
+static void dl_work_func(struct work_struct *work)
+{
+	ipa_do_bridge_work(IPA_DL);
+}
+
+static DECLARE_WORK(ul_work, ul_work_func);
+static DECLARE_WORK(dl_work, dl_work_func);
+
+static int ipa_switch_to_intr_mode(enum ipa_bridge_dir dir)
+{
+	int ret;
+	struct ipa_bridge_pipe_context *sys = &bridge[2 * dir];
+
+	ret = sps_get_config(sys->pipe, &sys->connection);
+	if (ret) {
+		IPAERR("sps_get_config() failed %d\n", ret);
+		goto fail;
+	}
+	sys->register_event.options = SPS_O_EOT;
+	ret = sps_register_event(sys->pipe, &sys->register_event);
+	if (ret) {
+		IPAERR("sps_register_event() failed %d\n", ret);
+		goto fail;
+	}
+	sys->connection.options =
+	   SPS_O_AUTO_ENABLE | SPS_O_ACK_TRANSFERS | SPS_O_EOT;
+	ret = sps_set_config(sys->pipe, &sys->connection);
+	if (ret) {
+		IPAERR("sps_set_config() failed %d\n", ret);
+		goto fail;
+	}
+	ret = 0;
+fail:
+	return ret;
+}
+
+static int ipa_switch_to_poll_mode(enum ipa_bridge_dir dir)
+{
+	int ret;
+	struct ipa_bridge_pipe_context *sys = &bridge[2 * dir];
+
+	ret = sps_get_config(sys->pipe, &sys->connection);
+	if (ret) {
+		IPAERR("sps_get_config() failed %d\n", ret);
+		goto fail;
+	}
+	sys->connection.options =
+	   SPS_O_AUTO_ENABLE | SPS_O_ACK_TRANSFERS | SPS_O_POLL;
+	ret = sps_set_config(sys->pipe, &sys->connection);
+	if (ret) {
+		IPAERR("sps_set_config() failed %d\n", ret);
+		goto fail;
+	}
+	ret = 0;
+fail:
+	return ret;
+}
+
+static int queue_rx_single(enum ipa_bridge_dir dir)
+{
+	struct ipa_bridge_pipe_context *sys_rx = &bridge[2 * dir];
+	struct ipa_pkt_info *info;
+	int ret;
+
+	info = kmalloc(sizeof(struct ipa_pkt_info), GFP_KERNEL);
+	if (!info) {
+		IPAERR("unable to alloc rx_pkt_info\n");
+		goto fail_pkt;
+	}
+
+	info->buffer = kmalloc(IPA_RX_SKB_SIZE, GFP_KERNEL | GFP_DMA);
+	if (!info->buffer) {
+		IPAERR("unable to alloc rx_pkt_buffer\n");
+		goto fail_buffer;
+	}
+
+	info->dma_address = dma_map_single(NULL, info->buffer, IPA_RX_SKB_SIZE,
+					   DMA_BIDIRECTIONAL);
+	if (info->dma_address == 0 || info->dma_address == ~0) {
+		IPAERR("dma_map_single failure %p for %p\n",
+				(void *)info->dma_address, info->buffer);
+		goto fail_dma;
+	}
+
+	info->len = ~0;
+
+	list_add_tail(&info->list_node, &sys_rx->head_desc_list);
+	ret = sps_transfer_one(sys_rx->pipe, info->dma_address,
+			       IPA_RX_SKB_SIZE, info,
+			       SPS_IOVEC_FLAG_INT | SPS_IOVEC_FLAG_EOT);
+	if (ret) {
+		list_del(&info->list_node);
+		dma_unmap_single(NULL, info->dma_address, IPA_RX_SKB_SIZE,
+				 DMA_BIDIRECTIONAL);
+		IPAERR("sps_transfer_one failed %d\n", ret);
+		goto fail_dma;
+	}
+	sys_rx->len++;
+	return 0;
+
+fail_dma:
+	kfree(info->buffer);
+fail_buffer:
+	kfree(info);
+fail_pkt:
+	IPAERR("failed\n");
+	return -ENOMEM;
+}
+
+static void ipa_do_bridge_work(enum ipa_bridge_dir dir)
+{
+	struct ipa_bridge_pipe_context *sys_rx = &bridge[2 * dir];
+	struct ipa_bridge_pipe_context *sys_tx = &bridge[2 * dir + 1];
+	struct ipa_pkt_info *tx_pkt;
+	struct ipa_pkt_info *rx_pkt;
+	struct ipa_pkt_info *tmp_pkt;
+	struct sps_iovec iov;
+	int ret;
+	int inactive_cycles = 0;
+
+	while (1) {
+		++inactive_cycles;
+		iov.addr = 0;
+		ret = sps_get_iovec(sys_tx->pipe, &iov);
+		if (ret || iov.addr == 0) {
+			/* no-op */
+		} else {
+			inactive_cycles = 0;
+
+			tx_pkt = list_first_entry(&sys_tx->head_desc_list,
+						  struct ipa_pkt_info,
+						  list_node);
+			list_move_tail(&tx_pkt->list_node,
+					&sys_tx->free_desc_list);
+			sys_tx->len--;
+			sys_tx->free_len++;
+			tx_pkt->len = ~0;
+		}
+
+		iov.addr = 0;
+		ret = sps_get_iovec(sys_rx->pipe, &iov);
+		if (ret || iov.addr == 0) {
+			/* no-op */
+		} else {
+			inactive_cycles = 0;
+
+			rx_pkt = list_first_entry(&sys_rx->head_desc_list,
+						  struct ipa_pkt_info,
+						  list_node);
+			list_del(&rx_pkt->list_node);
+			sys_rx->len--;
+			rx_pkt->len = iov.size;
+
+retry_alloc_tx:
+			if (list_empty(&sys_tx->free_desc_list)) {
+				tmp_pkt = kmalloc(sizeof(struct ipa_pkt_info),
+						GFP_KERNEL);
+				if (!tmp_pkt) {
+					pr_err_ratelimited("%s: unable to alloc tx_pkt_info\n",
+					       __func__);
+					usleep_range(polling_min_sleep[dir],
+							polling_max_sleep[dir]);
+					goto retry_alloc_tx;
+				}
+
+				tmp_pkt->buffer = kmalloc(IPA_RX_SKB_SIZE,
+						GFP_KERNEL | GFP_DMA);
+				if (!tmp_pkt->buffer) {
+					pr_err_ratelimited("%s: unable to alloc tx_pkt_buffer\n",
+					       __func__);
+					kfree(tmp_pkt);
+					usleep_range(polling_min_sleep[dir],
+							polling_max_sleep[dir]);
+					goto retry_alloc_tx;
+				}
+
+				tmp_pkt->dma_address = dma_map_single(NULL,
+						tmp_pkt->buffer,
+						IPA_RX_SKB_SIZE,
+						DMA_BIDIRECTIONAL);
+				if (tmp_pkt->dma_address == 0 ||
+						tmp_pkt->dma_address == ~0) {
+					pr_err_ratelimited("%s: dma_map_single failure %p for %p\n",
+					       __func__,
+					       (void *)tmp_pkt->dma_address,
+					       tmp_pkt->buffer);
+				}
+
+				list_add_tail(&tmp_pkt->list_node,
+						&sys_tx->free_desc_list);
+				sys_tx->free_len++;
+				alloc_cnt[dir]++;
+
+				tmp_pkt->len = ~0;
+			}
+
+			tx_pkt = list_first_entry(&sys_tx->free_desc_list,
+						  struct ipa_pkt_info,
+						  list_node);
+			list_del(&tx_pkt->list_node);
+			sys_tx->free_len--;
+
+retry_add_rx:
+			list_add_tail(&tx_pkt->list_node,
+					&sys_rx->head_desc_list);
+			ret = sps_transfer_one(sys_rx->pipe,
+					tx_pkt->dma_address,
+					IPA_RX_SKB_SIZE,
+					tx_pkt,
+					SPS_IOVEC_FLAG_INT |
+					SPS_IOVEC_FLAG_EOT);
+			if (ret) {
+				list_del(&tx_pkt->list_node);
+				pr_err_ratelimited("%s: sps_transfer_one failed %d\n",
+						__func__, ret);
+				usleep_range(polling_min_sleep[dir],
+						polling_max_sleep[dir]);
+				goto retry_add_rx;
+			}
+			sys_rx->len++;
+
+retry_add_tx:
+			list_add_tail(&rx_pkt->list_node,
+					&sys_tx->head_desc_list);
+			ret = sps_transfer_one(sys_tx->pipe,
+					       rx_pkt->dma_address,
+					       iov.size,
+					       rx_pkt,
+					       SPS_IOVEC_FLAG_INT |
+					       SPS_IOVEC_FLAG_EOT);
+			if (ret) {
+				pr_err_ratelimited("%s: fail to add to TX dir=%d\n",
+						__func__, dir);
+				list_del(&rx_pkt->list_node);
+				usleep_range(polling_min_sleep[dir],
+						polling_max_sleep[dir]);
+				goto retry_add_tx;
+			}
+			sys_tx->len++;
+		}
+
+		if (inactive_cycles >= polling_inactivity[dir]) {
+			ipa_switch_to_intr_mode(dir);
+			break;
+		}
+	}
+}
+
+static void ipa_rx_notify(struct sps_event_notify *notify)
+{
+	switch (notify->event_id) {
+	case SPS_EVENT_EOT:
+		ipa_switch_to_poll_mode(IPA_UL);
+		queue_work(ipa_ul_workqueue, &ul_work);
+		break;
+	default:
+		IPAERR("recieved unexpected event id %d\n", notify->event_id);
+	}
+}
+
+static int setup_bridge_to_ipa(enum ipa_bridge_dir dir)
+{
+	struct ipa_bridge_pipe_context *sys;
+	struct ipa_ep_cfg_mode mode;
+	dma_addr_t dma_addr;
+	int ipa_ep_idx;
+	int ret;
+	int i;
+
+	if (dir == IPA_DL) {
+		ipa_ep_idx = ipa_get_ep_mapping(ipa_ctx->mode,
+				IPA_CLIENT_A2_TETHERED_PROD);
+		if (ipa_ep_idx == -1) {
+			IPAERR("Invalid client.\n");
+			ret = -EINVAL;
+			goto tx_alloc_endpoint_failed;
+		}
+
+		sys = &bridge[IPA_DL_TO_IPA];
+		sys->pipe = sps_alloc_endpoint();
+		if (sys->pipe == NULL) {
+			IPAERR("tx alloc endpoint failed\n");
+			ret = -ENOMEM;
+			goto tx_alloc_endpoint_failed;
+		}
+		ret = sps_get_config(sys->pipe, &sys->connection);
+		if (ret) {
+			IPAERR("tx get config failed %d\n", ret);
+			goto tx_get_config_failed;
+		}
+
+		sys->connection.source = SPS_DEV_HANDLE_MEM;
+		sys->connection.src_pipe_index = ipa_ctx->a5_pipe_index++;
+		sys->connection.destination = ipa_ctx->bam_handle;
+		sys->connection.dest_pipe_index = ipa_ep_idx;
+		sys->connection.mode = SPS_MODE_DEST;
+		sys->connection.options =
+		   SPS_O_AUTO_ENABLE | SPS_O_ACK_TRANSFERS | SPS_O_POLL;
+		sys->desc_mem_buf.size = IPA_SYS_DESC_FIFO_SZ; /* 2k */
+		sys->desc_mem_buf.base = dma_alloc_coherent(NULL,
+				sys->desc_mem_buf.size,
+				&dma_addr,
+				0);
+		if (sys->desc_mem_buf.base == NULL) {
+			IPAERR("tx memory alloc failed\n");
+			ret = -ENOMEM;
+			goto tx_get_config_failed;
+		}
+		sys->desc_mem_buf.phys_base = dma_addr;
+		memset(sys->desc_mem_buf.base, 0x0, sys->desc_mem_buf.size);
+		sys->connection.desc = sys->desc_mem_buf;
+		sys->connection.event_thresh = IPA_EVENT_THRESHOLD;
+
+		ret = sps_connect(sys->pipe, &sys->connection);
+		if (ret < 0) {
+			IPAERR("tx connect error %d\n", ret);
+			goto tx_connect_failed;
+		}
+
+		INIT_LIST_HEAD(&sys->head_desc_list);
+		INIT_LIST_HEAD(&sys->free_desc_list);
+		spin_lock_init(&sys->spinlock);
+
+		ipa_ctx->ep[ipa_ep_idx].valid = 1;
+
+		mode.mode = IPA_DMA;
+		mode.dst = IPA_CLIENT_USB_CONS;
+		ret = ipa_cfg_ep_mode(ipa_ep_idx, &mode);
+		if (ret < 0) {
+			IPAERR("DMA mode set error %d\n", ret);
+			goto tx_mode_set_failed;
+		}
+
+		return 0;
+
+tx_mode_set_failed:
+		sps_disconnect(sys->pipe);
+tx_connect_failed:
+		dma_free_coherent(NULL, sys->desc_mem_buf.size,
+				sys->desc_mem_buf.base,
+				sys->desc_mem_buf.phys_base);
+tx_get_config_failed:
+		sps_free_endpoint(sys->pipe);
+tx_alloc_endpoint_failed:
+		return ret;
+	} else {
+
+		ipa_ep_idx = ipa_get_ep_mapping(ipa_ctx->mode,
+				IPA_CLIENT_A2_TETHERED_CONS);
+		if (ipa_ep_idx == -1) {
+			IPAERR("Invalid client.\n");
+			ret = -EINVAL;
+			goto rx_alloc_endpoint_failed;
+		}
+
+		sys = &bridge[IPA_UL_FROM_IPA];
+		sys->pipe = sps_alloc_endpoint();
+		if (sys->pipe == NULL) {
+			IPAERR("rx alloc endpoint failed\n");
+			ret = -ENOMEM;
+			goto rx_alloc_endpoint_failed;
+		}
+		ret = sps_get_config(sys->pipe, &sys->connection);
+		if (ret) {
+			IPAERR("rx get config failed %d\n", ret);
+			goto rx_get_config_failed;
+		}
+
+		sys->connection.source = ipa_ctx->bam_handle;
+		sys->connection.src_pipe_index = 7;
+		sys->connection.destination = SPS_DEV_HANDLE_MEM;
+		sys->connection.dest_pipe_index = ipa_ctx->a5_pipe_index++;
+		sys->connection.mode = SPS_MODE_SRC;
+		sys->connection.options = SPS_O_AUTO_ENABLE | SPS_O_EOT |
+		      SPS_O_ACK_TRANSFERS;
+		sys->desc_mem_buf.size = IPA_SYS_DESC_FIFO_SZ; /* 2k */
+		sys->desc_mem_buf.base = dma_alloc_coherent(NULL,
+				sys->desc_mem_buf.size,
+				&dma_addr,
+				0);
+		if (sys->desc_mem_buf.base == NULL) {
+			IPAERR("rx memory alloc failed\n");
+			ret = -ENOMEM;
+			goto rx_get_config_failed;
+		}
+		sys->desc_mem_buf.phys_base = dma_addr;
+		memset(sys->desc_mem_buf.base, 0x0, sys->desc_mem_buf.size);
+		sys->connection.desc = sys->desc_mem_buf;
+		sys->connection.event_thresh = IPA_EVENT_THRESHOLD;
+
+		ret = sps_connect(sys->pipe, &sys->connection);
+		if (ret < 0) {
+			IPAERR("rx connect error %d\n", ret);
+			goto rx_connect_failed;
+		}
+
+		sys->register_event.options = SPS_O_EOT;
+		sys->register_event.mode = SPS_TRIGGER_CALLBACK;
+		sys->register_event.xfer_done = NULL;
+		sys->register_event.callback = ipa_rx_notify;
+		sys->register_event.user = NULL;
+		ret = sps_register_event(sys->pipe, &sys->register_event);
+		if (ret < 0) {
+			IPAERR("tx register event error %d\n", ret);
+			goto rx_event_reg_failed;
+		}
+
+		INIT_LIST_HEAD(&sys->head_desc_list);
+		INIT_LIST_HEAD(&sys->free_desc_list);
+		spin_lock_init(&sys->spinlock);
+
+		for (i = 0; i < IPA_RX_POOL_CEIL; i++) {
+			ret = queue_rx_single(dir);
+			if (ret < 0)
+				IPAERR("queue fail %d %d\n", dir, i);
+		}
+
+		return 0;
+
+rx_event_reg_failed:
+		sps_disconnect(sys->pipe);
+rx_connect_failed:
+		dma_free_coherent(NULL,
+				sys->desc_mem_buf.size,
+				sys->desc_mem_buf.base,
+				sys->desc_mem_buf.phys_base);
+rx_get_config_failed:
+		sps_free_endpoint(sys->pipe);
+rx_alloc_endpoint_failed:
+		return ret;
+	}
+}
+
+static void bam_mux_rx_notify(struct sps_event_notify *notify)
+{
+	switch (notify->event_id) {
+	case SPS_EVENT_EOT:
+		ipa_switch_to_poll_mode(IPA_DL);
+		queue_work(ipa_dl_workqueue, &dl_work);
+		break;
+	default:
+		IPAERR("recieved unexpected event id %d\n", notify->event_id);
+	}
+}
+
+static int setup_bridge_to_a2(enum ipa_bridge_dir dir)
+{
+	struct ipa_bridge_pipe_context *sys;
+	struct a2_mux_pipe_connection pipe_conn = { 0, };
+	dma_addr_t dma_addr;
+	u32 a2_handle;
+	int ret;
+	int i;
+
+	if (dir == IPA_UL) {
+		ret = ipa_get_a2_mux_pipe_info(IPA_TO_A2, &pipe_conn);
+		if (ret) {
+			IPAERR("ipa_get_a2_mux_pipe_info failed IPA_TO_A2\n");
+			goto tx_alloc_endpoint_failed;
+		}
+
+		ret = sps_phy2h(pipe_conn.dst_phy_addr, &a2_handle);
+		if (ret) {
+			IPAERR("sps_phy2h failed (A2 BAM) %d\n", ret);
+			goto tx_alloc_endpoint_failed;
+		}
+
+		sys = &bridge[IPA_UL_TO_A2];
+		sys->pipe = sps_alloc_endpoint();
+		if (sys->pipe == NULL) {
+			IPAERR("tx alloc endpoint failed\n");
+			ret = -ENOMEM;
+			goto tx_alloc_endpoint_failed;
+		}
+		ret = sps_get_config(sys->pipe, &sys->connection);
+		if (ret) {
+			IPAERR("tx get config failed %d\n", ret);
+			goto tx_get_config_failed;
+		}
+
+		sys->connection.source = SPS_DEV_HANDLE_MEM;
+		sys->connection.src_pipe_index = ipa_ctx->a5_pipe_index++;
+		sys->connection.destination = a2_handle;
+		sys->connection.dest_pipe_index = pipe_conn.dst_pipe_index;
+		sys->connection.mode = SPS_MODE_DEST;
+		sys->connection.options =
+		   SPS_O_AUTO_ENABLE | SPS_O_ACK_TRANSFERS | SPS_O_POLL;
+		sys->desc_mem_buf.size = IPA_SYS_DESC_FIFO_SZ; /* 2k */
+		sys->desc_mem_buf.base = dma_alloc_coherent(NULL,
+				sys->desc_mem_buf.size,
+				&dma_addr,
+				0);
+		if (sys->desc_mem_buf.base == NULL) {
+			IPAERR("tx memory alloc failed\n");
+			ret = -ENOMEM;
+			goto tx_get_config_failed;
+		}
+		sys->desc_mem_buf.phys_base = dma_addr;
+		memset(sys->desc_mem_buf.base, 0x0, sys->desc_mem_buf.size);
+		sys->connection.desc = sys->desc_mem_buf;
+		sys->connection.event_thresh = IPA_EVENT_THRESHOLD;
+
+		ret = sps_connect(sys->pipe, &sys->connection);
+		if (ret < 0) {
+			IPAERR("tx connect error %d\n", ret);
+			goto tx_connect_failed;
+		}
+
+		INIT_LIST_HEAD(&sys->head_desc_list);
+		INIT_LIST_HEAD(&sys->free_desc_list);
+		spin_lock_init(&sys->spinlock);
+
+		return 0;
+
+tx_connect_failed:
+		dma_free_coherent(NULL,
+				sys->desc_mem_buf.size,
+				sys->desc_mem_buf.base,
+				sys->desc_mem_buf.phys_base);
+tx_get_config_failed:
+		sps_free_endpoint(sys->pipe);
+tx_alloc_endpoint_failed:
+		return ret;
+	} else { /* dir == IPA_UL */
+
+		ret = ipa_get_a2_mux_pipe_info(A2_TO_IPA, &pipe_conn);
+		if (ret) {
+			IPAERR("ipa_get_a2_mux_pipe_info failed A2_TO_IPA\n");
+			goto rx_alloc_endpoint_failed;
+		}
+
+		ret = sps_phy2h(pipe_conn.src_phy_addr, &a2_handle);
+		if (ret) {
+			IPAERR("sps_phy2h failed (A2 BAM) %d\n", ret);
+			goto rx_alloc_endpoint_failed;
+		}
+
+		sys = &bridge[IPA_DL_FROM_A2];
+		sys->pipe = sps_alloc_endpoint();
+		if (sys->pipe == NULL) {
+			IPAERR("rx alloc endpoint failed\n");
+			ret = -ENOMEM;
+			goto rx_alloc_endpoint_failed;
+		}
+		ret = sps_get_config(sys->pipe, &sys->connection);
+		if (ret) {
+			IPAERR("rx get config failed %d\n", ret);
+			goto rx_get_config_failed;
+		}
+
+		sys->connection.source = a2_handle;
+		sys->connection.src_pipe_index = pipe_conn.src_pipe_index;
+		sys->connection.destination = SPS_DEV_HANDLE_MEM;
+		sys->connection.dest_pipe_index = ipa_ctx->a5_pipe_index++;
+		sys->connection.mode = SPS_MODE_SRC;
+		sys->connection.options = SPS_O_AUTO_ENABLE | SPS_O_EOT |
+		      SPS_O_ACK_TRANSFERS;
+		sys->desc_mem_buf.size = IPA_SYS_DESC_FIFO_SZ; /* 2k */
+		sys->desc_mem_buf.base = dma_alloc_coherent(NULL,
+				sys->desc_mem_buf.size,
+				&dma_addr,
+				0);
+		if (sys->desc_mem_buf.base == NULL) {
+			IPAERR("rx memory alloc failed\n");
+			ret = -ENOMEM;
+			goto rx_get_config_failed;
+		}
+		sys->desc_mem_buf.phys_base = dma_addr;
+		memset(sys->desc_mem_buf.base, 0x0, sys->desc_mem_buf.size);
+		sys->connection.desc = sys->desc_mem_buf;
+		sys->connection.event_thresh = IPA_EVENT_THRESHOLD;
+
+		ret = sps_connect(sys->pipe, &sys->connection);
+		if (ret < 0) {
+			IPAERR("rx connect error %d\n", ret);
+			goto rx_connect_failed;
+		}
+
+		sys->register_event.options = SPS_O_EOT;
+		sys->register_event.mode = SPS_TRIGGER_CALLBACK;
+		sys->register_event.xfer_done = NULL;
+		sys->register_event.callback = bam_mux_rx_notify;
+		sys->register_event.user = NULL;
+		ret = sps_register_event(sys->pipe, &sys->register_event);
+		if (ret < 0) {
+			IPAERR("tx register event error %d\n", ret);
+			goto rx_event_reg_failed;
+		}
+
+		INIT_LIST_HEAD(&sys->head_desc_list);
+		INIT_LIST_HEAD(&sys->free_desc_list);
+		spin_lock_init(&sys->spinlock);
+
+
+		for (i = 0; i < IPA_RX_POOL_CEIL; i++) {
+			ret = queue_rx_single(dir);
+			if (ret < 0)
+				IPAERR("queue fail %d %d\n", dir, i);
+		}
+
+		return 0;
+
+rx_event_reg_failed:
+		sps_disconnect(sys->pipe);
+rx_connect_failed:
+		dma_free_coherent(NULL,
+				sys->desc_mem_buf.size,
+				sys->desc_mem_buf.base,
+				sys->desc_mem_buf.phys_base);
+rx_get_config_failed:
+		sps_free_endpoint(sys->pipe);
+rx_alloc_endpoint_failed:
+		return ret;
+	}
+}
+
+/**
+ * ipa_bridge_init() - initialize the tethered bridge, allocate UL and DL
+ * workqueues
+ *
+ * Return codes: 0: success, -ENOMEM: failure
+ */
+int ipa_bridge_init(void)
+{
+	int ret;
+
+	ipa_ul_workqueue = alloc_workqueue("ipa_ul",
+			WQ_MEM_RECLAIM | WQ_CPU_INTENSIVE, 1);
+	if (!ipa_ul_workqueue) {
+		IPAERR("ipa ul wq alloc failed\n");
+		ret = -ENOMEM;
+		goto fail_ul;
+	}
+
+	ipa_dl_workqueue = alloc_workqueue("ipa_dl",
+			WQ_MEM_RECLAIM | WQ_CPU_INTENSIVE, 1);
+	if (!ipa_dl_workqueue) {
+		IPAERR("ipa dl wq alloc failed\n");
+		ret = -ENOMEM;
+		goto fail_dl;
+	}
+
+	return 0;
+fail_dl:
+	destroy_workqueue(ipa_ul_workqueue);
+fail_ul:
+	return ret;
+}
+
+/**
+ * ipa_bridge_setup() - setup tethered SW bridge in specified direction
+ * @dir: downlink or uplink (from air interface perspective)
+ *
+ * Return codes:
+ * 0: success
+ * various negative error codes on errors
+ */
+int ipa_bridge_setup(enum ipa_bridge_dir dir)
+{
+	int ret;
+
+	if (atomic_inc_return(&ipa_ctx->ipa_active_clients) == 1)
+		ipa_enable_clks();
+
+	if (setup_bridge_to_a2(dir)) {
+		IPAERR("fail to setup SYS pipe to A2 %d\n", dir);
+		ret = -EINVAL;
+		goto bail_a2;
+	}
+
+	if (setup_bridge_to_ipa(dir)) {
+		IPAERR("fail to setup SYS pipe to IPA %d\n", dir);
+		ret = -EINVAL;
+		goto bail_ipa;
+	}
+
+	return 0;
+
+bail_ipa:
+	if (dir == IPA_UL)
+		sps_disconnect(bridge[IPA_UL_TO_A2].pipe);
+	else
+		sps_disconnect(bridge[IPA_DL_FROM_A2].pipe);
+bail_a2:
+	if (atomic_dec_return(&ipa_ctx->ipa_active_clients) == 0)
+		ipa_disable_clks();
+	return ret;
+}
+
+/**
+ * ipa_bridge_teardown() - teardown the tethered bridge in the specified dir
+ * @dir: downlink or uplink (from air interface perspective)
+ *
+ * Return codes:
+ * 0: always
+ */
+int ipa_bridge_teardown(enum ipa_bridge_dir dir)
+{
+	struct ipa_bridge_pipe_context *sys;
+
+	if (dir == IPA_UL) {
+		sys = &bridge[IPA_UL_TO_A2];
+		sps_disconnect(sys->pipe);
+		sys = &bridge[IPA_UL_FROM_IPA];
+		sps_disconnect(sys->pipe);
+	} else {
+		sys = &bridge[IPA_DL_FROM_A2];
+		sps_disconnect(sys->pipe);
+		sys = &bridge[IPA_DL_TO_IPA];
+		sps_disconnect(sys->pipe);
+	}
+
+	if (atomic_dec_return(&ipa_ctx->ipa_active_clients) == 0)
+		ipa_disable_clks();
+
+	return 0;
+}
+
+/**
+ * ipa_bridge_cleanup() - de-initialize the tethered bridge
+ *
+ * Return codes:
+ * None
+ */
+void ipa_bridge_cleanup(void)
+{
+	destroy_workqueue(ipa_dl_workqueue);
+	destroy_workqueue(ipa_ul_workqueue);
+}
diff --git a/drivers/platform/msm/ipa/ipa_client.c b/drivers/platform/msm/ipa/ipa_client.c
new file mode 100644
index 0000000..823b17d
--- /dev/null
+++ b/drivers/platform/msm/ipa/ipa_client.c
@@ -0,0 +1,325 @@
+/* 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 "ipa_i.h"
+
+static int ipa_connect_configure_sps(const struct ipa_connect_params *in,
+				     struct ipa_ep_context *ep, int ipa_ep_idx)
+{
+	int result = -EFAULT;
+
+	/* Default Config */
+	ep->ep_hdl = sps_alloc_endpoint();
+
+	if (ep->ep_hdl == NULL) {
+		IPAERR("SPS EP alloc failed EP.\n");
+		return -EFAULT;
+	}
+
+	result = sps_get_config(ep->ep_hdl,
+		&ep->connect);
+	if (result) {
+		IPAERR("fail to get config.\n");
+		return -EFAULT;
+	}
+
+	/* Specific Config */
+	if (IPA_CLIENT_IS_CONS(in->client)) {
+		ep->connect.mode = SPS_MODE_SRC;
+		ep->connect.destination =
+			in->client_bam_hdl;
+		ep->connect.source = ipa_ctx->bam_handle;
+		ep->connect.dest_pipe_index =
+			in->client_ep_idx;
+		ep->connect.src_pipe_index = ipa_ep_idx;
+	} else {
+		ep->connect.mode = SPS_MODE_DEST;
+		ep->connect.source = in->client_bam_hdl;
+		ep->connect.destination = ipa_ctx->bam_handle;
+		ep->connect.src_pipe_index = in->client_ep_idx;
+		ep->connect.dest_pipe_index = ipa_ep_idx;
+	}
+
+	return 0;
+}
+
+static int ipa_connect_allocate_fifo(const struct ipa_connect_params *in,
+				     struct sps_mem_buffer *mem_buff_ptr,
+				     bool *fifo_in_pipe_mem_ptr,
+				     u32 *fifo_pipe_mem_ofst_ptr,
+				     u32 fifo_size, int ipa_ep_idx)
+{
+	dma_addr_t dma_addr;
+	u32 ofst;
+	int result = -EFAULT;
+
+	mem_buff_ptr->size = fifo_size;
+	if (in->pipe_mem_preferred) {
+		if (ipa_pipe_mem_alloc(&ofst, fifo_size)) {
+			IPAERR("FIFO pipe mem alloc fail ep %u\n",
+				ipa_ep_idx);
+			mem_buff_ptr->base =
+				dma_alloc_coherent(NULL,
+				mem_buff_ptr->size,
+				&dma_addr, GFP_KERNEL);
+		} else {
+			memset(mem_buff_ptr, 0, sizeof(struct sps_mem_buffer));
+			result = sps_setup_bam2bam_fifo(mem_buff_ptr, ofst,
+				fifo_size, 1);
+			WARN_ON(result);
+			*fifo_in_pipe_mem_ptr = 1;
+			dma_addr = mem_buff_ptr->phys_base;
+			*fifo_pipe_mem_ofst_ptr = ofst;
+		}
+	} else {
+		mem_buff_ptr->base =
+			dma_alloc_coherent(NULL, mem_buff_ptr->size,
+			&dma_addr, GFP_KERNEL);
+	}
+	mem_buff_ptr->phys_base = dma_addr;
+	if (mem_buff_ptr->base == NULL) {
+		IPAERR("fail to get DMA memory.\n");
+		return -EFAULT;
+	}
+
+	return 0;
+}
+
+
+/**
+ * ipa_connect() - low-level IPA client connect
+ * @in:	[in] input parameters from client
+ * @sps:	[out] sps output from IPA needed by client for sps_connect
+ * @clnt_hdl:	[out] opaque client handle assigned by IPA to client
+ *
+ * Should be called by the driver of the peripheral that wants to connect to
+ * IPA in BAM-BAM mode. these peripherals are A2, USB and HSIC. this api
+ * expects caller to take responsibility to add any needed headers, routing
+ * and filtering tables and rules as needed.
+ *
+ * Returns:	0 on success, negative on failure
+ *
+ * Note:	Should not be called from atomic context
+ */
+int ipa_connect(const struct ipa_connect_params *in, struct ipa_sps_params *sps,
+		u32 *clnt_hdl)
+{
+	int ipa_ep_idx;
+	int ipa_ep_idx_dst;
+	int result = -EFAULT;
+	struct ipa_ep_context *ep;
+
+	if (atomic_inc_return(&ipa_ctx->ipa_active_clients) == 1)
+		ipa_enable_clks();
+
+	if (in == NULL || sps == NULL || clnt_hdl == NULL ||
+	    in->client >= IPA_CLIENT_MAX ||
+	    in->ipa_ep_cfg.mode.dst >= IPA_CLIENT_MAX ||
+	    in->desc_fifo_sz == 0 || in->data_fifo_sz == 0) {
+		IPAERR("bad parm.\n");
+		result = -EINVAL;
+		goto fail;
+	}
+
+	ipa_ep_idx = ipa_get_ep_mapping(ipa_ctx->mode, in->client);
+	if (ipa_ep_idx == -1) {
+		IPAERR("fail to alloc EP.\n");
+		goto fail;
+	}
+
+	ep = &ipa_ctx->ep[ipa_ep_idx];
+
+	if (ep->valid) {
+		IPAERR("EP already allocated.\n");
+		goto fail;
+	}
+
+	if (IPA_CLIENT_IS_PROD(in->client) &&
+			(in->ipa_ep_cfg.mode.mode == IPA_DMA)) {
+		ipa_ep_idx_dst = ipa_get_ep_mapping(ipa_ctx->mode,
+				in->ipa_ep_cfg.mode.dst);
+		if ((ipa_ep_idx_dst == -1) ||
+				(ipa_ctx->ep[ipa_ep_idx_dst].valid)) {
+			IPADBG("dst EP for IPA input pipe doesn't yet exist\n");
+		}
+	}
+
+	memset(&ipa_ctx->ep[ipa_ep_idx], 0, sizeof(struct ipa_ep_context));
+
+	ep->valid = 1;
+	ep->client = in->client;
+	ep->notify = in->notify;
+	ep->priv = in->priv;
+
+	if (ipa_cfg_ep(ipa_ep_idx, &in->ipa_ep_cfg)) {
+		IPAERR("fail to configure EP.\n");
+		goto ipa_cfg_ep_fail;
+	}
+
+	result = ipa_connect_configure_sps(in, ep, ipa_ep_idx);
+	if (result) {
+		IPAERR("fail to configure SPS.\n");
+		goto ipa_cfg_ep_fail;
+	}
+
+	if (in->desc.base == NULL) {
+		result = ipa_connect_allocate_fifo(in, &ep->connect.desc,
+						  &ep->desc_fifo_in_pipe_mem,
+						  &ep->desc_fifo_pipe_mem_ofst,
+						  in->desc_fifo_sz, ipa_ep_idx);
+		if (result) {
+			IPAERR("fail to allocate DESC FIFO.\n");
+			goto desc_mem_alloc_fail;
+		}
+	} else {
+		IPADBG("client allocated DESC FIFO\n");
+		ep->connect.desc = in->desc;
+		ep->desc_fifo_client_allocated = 1;
+	}
+	IPADBG("Descriptor FIFO pa=0x%x, size=%d\n", ep->connect.desc.phys_base,
+	       ep->connect.desc.size);
+
+	if (in->data.base == NULL) {
+		result = ipa_connect_allocate_fifo(in, &ep->connect.data,
+						&ep->data_fifo_in_pipe_mem,
+						&ep->data_fifo_pipe_mem_ofst,
+						in->data_fifo_sz, ipa_ep_idx);
+		if (result) {
+			IPAERR("fail to allocate DATA FIFO.\n");
+			goto data_mem_alloc_fail;
+		}
+	} else {
+		IPADBG("client allocated DATA FIFO\n");
+		ep->connect.data = in->data;
+		ep->data_fifo_client_allocated = 1;
+	}
+	IPADBG("Data FIFO pa=0x%x, size=%d\n", ep->connect.data.phys_base,
+	       ep->connect.data.size);
+
+	ep->connect.event_thresh = IPA_EVENT_THRESHOLD;
+	ep->connect.options = SPS_O_AUTO_ENABLE;    /* BAM-to-BAM */
+
+	result = sps_connect(ep->ep_hdl, &ep->connect);
+	if (result) {
+		IPAERR("sps_connect fails.\n");
+		goto sps_connect_fail;
+	}
+
+	sps->ipa_bam_hdl = ipa_ctx->bam_handle;
+	sps->ipa_ep_idx = ipa_ep_idx;
+	*clnt_hdl = ipa_ep_idx;
+	memcpy(&sps->desc, &ep->connect.desc, sizeof(struct sps_mem_buffer));
+	memcpy(&sps->data, &ep->connect.data, sizeof(struct sps_mem_buffer));
+
+	return 0;
+
+sps_connect_fail:
+	if (!ep->data_fifo_in_pipe_mem)
+		dma_free_coherent(NULL,
+				  ep->connect.data.size,
+				  ep->connect.data.base,
+				  ep->connect.data.phys_base);
+	else
+		ipa_pipe_mem_free(ep->data_fifo_pipe_mem_ofst,
+				  ep->connect.data.size);
+
+data_mem_alloc_fail:
+	if (!ep->desc_fifo_in_pipe_mem)
+		dma_free_coherent(NULL,
+				  ep->connect.desc.size,
+				  ep->connect.desc.base,
+				  ep->connect.desc.phys_base);
+	else
+		ipa_pipe_mem_free(ep->desc_fifo_pipe_mem_ofst,
+				  ep->connect.desc.size);
+
+desc_mem_alloc_fail:
+	sps_free_endpoint(ep->ep_hdl);
+ipa_cfg_ep_fail:
+	memset(&ipa_ctx->ep[ipa_ep_idx], 0, sizeof(struct ipa_ep_context));
+fail:
+	if (atomic_dec_return(&ipa_ctx->ipa_active_clients) == 0)
+		ipa_disable_clks();
+
+	return result;
+}
+EXPORT_SYMBOL(ipa_connect);
+
+/**
+ * ipa_disconnect() - low-level IPA client disconnect
+ * @clnt_hdl:	[in] opaque client handle assigned by IPA to client
+ *
+ * Should be called by the driver of the peripheral that wants to disconnect
+ * from IPA in BAM-BAM mode. this api expects caller to take responsibility to
+ * free any needed headers, routing and filtering tables and rules as needed.
+ *
+ * Returns:	0 on success, negative on failure
+ *
+ * Note:	Should not be called from atomic context
+ */
+int ipa_disconnect(u32 clnt_hdl)
+{
+	int result;
+	struct ipa_ep_context *ep;
+
+	if (clnt_hdl >= IPA_NUM_PIPES || ipa_ctx->ep[clnt_hdl].valid == 0) {
+		IPAERR("bad parm.\n");
+		return -EINVAL;
+	}
+
+	ep = &ipa_ctx->ep[clnt_hdl];
+
+	result = sps_disconnect(ep->ep_hdl);
+	if (result) {
+		IPAERR("SPS disconnect failed.\n");
+		return -EPERM;
+	}
+
+	if (!ep->desc_fifo_client_allocated &&
+	     ep->connect.desc.base) {
+		if (!ep->desc_fifo_in_pipe_mem)
+			dma_free_coherent(NULL,
+					  ep->connect.desc.size,
+					  ep->connect.desc.base,
+					  ep->connect.desc.phys_base);
+		else
+			ipa_pipe_mem_free(ep->desc_fifo_pipe_mem_ofst,
+					  ep->connect.desc.size);
+	}
+
+	if (!ep->data_fifo_client_allocated &&
+	     ep->connect.data.base) {
+		if (!ep->data_fifo_in_pipe_mem)
+			dma_free_coherent(NULL,
+					  ep->connect.data.size,
+					  ep->connect.data.base,
+					  ep->connect.data.phys_base);
+		else
+			ipa_pipe_mem_free(ep->data_fifo_pipe_mem_ofst,
+					  ep->connect.data.size);
+	}
+
+	result = sps_free_endpoint(ep->ep_hdl);
+	if (result) {
+		IPAERR("SPS de-alloc EP failed.\n");
+		return -EPERM;
+	}
+
+	memset(&ipa_ctx->ep[clnt_hdl], 0, sizeof(struct ipa_ep_context));
+
+	if (atomic_dec_return(&ipa_ctx->ipa_active_clients) == 0)
+		ipa_disable_clks();
+
+	return 0;
+}
+EXPORT_SYMBOL(ipa_disconnect);
+
diff --git a/drivers/platform/msm/ipa/ipa_debugfs.c b/drivers/platform/msm/ipa/ipa_debugfs.c
new file mode 100644
index 0000000..43b0178d
--- /dev/null
+++ b/drivers/platform/msm/ipa/ipa_debugfs.c
@@ -0,0 +1,507 @@
+/* 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.
+ */
+
+#ifdef CONFIG_DEBUG_FS
+
+#include <linux/debugfs.h>
+#include "ipa_i.h"
+
+
+#define IPA_MAX_MSG_LEN 1024
+static struct dentry *dent;
+static struct dentry *dfile_gen_reg;
+static struct dentry *dfile_ep_reg;
+static struct dentry *dfile_hdr;
+static struct dentry *dfile_ip4_rt;
+static struct dentry *dfile_ip6_rt;
+static struct dentry *dfile_ip4_flt;
+static struct dentry *dfile_ip6_flt;
+static char dbg_buff[IPA_MAX_MSG_LEN];
+static s8 ep_reg_idx;
+
+static ssize_t ipa_read_gen_reg(struct file *file, char __user *ubuf,
+		size_t count, loff_t *ppos)
+{
+	int nbytes;
+
+	nbytes = scnprintf(dbg_buff, IPA_MAX_MSG_LEN,
+			"IPA_VERSION=0x%x\n"
+			"IPA_COMP_HW_VERSION=0x%x\n"
+			"IPA_ROUTE=0x%x\n"
+			"IPA_FILTER=0x%x\n"
+			"IPA_SHARED_MEM_SIZE=0x%x\n"
+			"IPA_HEAD_OF_LINE_BLOCK_EN=0x%x\n",
+			ipa_read_reg(ipa_ctx->mmio, IPA_VERSION_OFST),
+			ipa_read_reg(ipa_ctx->mmio, IPA_COMP_HW_VERSION_OFST),
+			ipa_read_reg(ipa_ctx->mmio, IPA_ROUTE_OFST),
+			ipa_read_reg(ipa_ctx->mmio, IPA_FILTER_OFST),
+			ipa_read_reg(ipa_ctx->mmio, IPA_SHARED_MEM_SIZE_OFST),
+			ipa_read_reg(ipa_ctx->mmio,
+				IPA_HEAD_OF_LINE_BLOCK_EN_OFST));
+
+	return simple_read_from_buffer(ubuf, count, ppos, dbg_buff, nbytes);
+}
+
+static ssize_t ipa_write_ep_reg(struct file *file, const char __user *buf,
+		size_t count, loff_t *ppos)
+{
+	unsigned long missing;
+	s8 option = 0;
+
+	if (sizeof(dbg_buff) < count + 1)
+		return -EFAULT;
+
+	missing = copy_from_user(dbg_buff, buf, count);
+	if (missing)
+		return -EFAULT;
+
+	dbg_buff[count] = '\0';
+	if (kstrtos8(dbg_buff, 0, &option))
+		return -EFAULT;
+
+	if (option >= IPA_NUM_PIPES) {
+		IPAERR("bad pipe specified %u\n", option);
+		return count;
+	}
+
+	ep_reg_idx = option;
+
+	return count;
+}
+
+static ssize_t ipa_read_ep_reg(struct file *file, char __user *ubuf,
+		size_t count, loff_t *ppos)
+{
+	int nbytes;
+	int i;
+	int start_idx;
+	int end_idx;
+	int size = 0;
+	int ret;
+	loff_t pos;
+
+	/* negative ep_reg_idx means all registers */
+	if (ep_reg_idx < 0) {
+		start_idx = 0;
+		end_idx = IPA_NUM_PIPES;
+	} else {
+		start_idx = ep_reg_idx;
+		end_idx = start_idx + 1;
+	}
+	pos = *ppos;
+	for (i = start_idx; i < end_idx; i++) {
+
+		nbytes = scnprintf(dbg_buff, IPA_MAX_MSG_LEN,
+				"IPA_ENDP_INIT_NAT_%u=0x%x\n"
+				"IPA_ENDP_INIT_HDR_%u=0x%x\n"
+				"IPA_ENDP_INIT_MODE_%u=0x%x\n"
+				"IPA_ENDP_INIT_AGGR_%u=0x%x\n"
+				"IPA_ENDP_INIT_ROUTE_%u=0x%x\n",
+				i, ipa_read_reg(ipa_ctx->mmio,
+					IPA_ENDP_INIT_NAT_n_OFST(i)),
+				i, ipa_read_reg(ipa_ctx->mmio,
+					IPA_ENDP_INIT_HDR_n_OFST(i)),
+				i, ipa_read_reg(ipa_ctx->mmio,
+					IPA_ENDP_INIT_MODE_n_OFST(i)),
+				i, ipa_read_reg(ipa_ctx->mmio,
+					IPA_ENDP_INIT_AGGR_n_OFST(i)),
+				i, ipa_read_reg(ipa_ctx->mmio,
+					IPA_ENDP_INIT_ROUTE_n_OFST(i)));
+		*ppos = pos;
+		ret = simple_read_from_buffer(ubuf, count, ppos, dbg_buff,
+					      nbytes);
+		if (ret < 0)
+			return ret;
+
+		size += ret;
+		ubuf += nbytes;
+		count -= nbytes;
+	}
+
+	*ppos = pos + size;
+	return size;
+}
+
+static ssize_t ipa_read_hdr(struct file *file, char __user *ubuf, size_t count,
+		loff_t *ppos)
+{
+	int nbytes = 0;
+	int cnt = 0;
+	int i = 0;
+	struct ipa_hdr_entry *entry;
+
+	mutex_lock(&ipa_ctx->lock);
+	list_for_each_entry(entry, &ipa_ctx->hdr_tbl.head_hdr_entry_list,
+			link) {
+		nbytes = scnprintf(dbg_buff + cnt, IPA_MAX_MSG_LEN - cnt,
+				   "name:%s len=%d ref=%d partial=%d lcl=%d ofst=%u ",
+				   entry->name,
+				   entry->hdr_len, entry->ref_cnt,
+				   entry->is_partial,
+				   ipa_ctx->hdr_tbl_lcl,
+				   entry->offset_entry->offset >> 2);
+		for (i = 0; i < entry->hdr_len; i++) {
+			scnprintf(dbg_buff + cnt + nbytes + i * 2,
+				  IPA_MAX_MSG_LEN - cnt - nbytes - i * 2,
+				  "%02x", entry->hdr[i]);
+		}
+		scnprintf(dbg_buff + cnt + nbytes + entry->hdr_len * 2,
+			  IPA_MAX_MSG_LEN - cnt - nbytes - entry->hdr_len * 2,
+			  "\n");
+		cnt += nbytes + entry->hdr_len * 2 + 1;
+	}
+	mutex_unlock(&ipa_ctx->lock);
+
+	return simple_read_from_buffer(ubuf, count, ppos, dbg_buff, cnt);
+}
+
+static int ipa_attrib_dump(char *buff, size_t sz,
+		struct ipa_rule_attrib *attrib, enum ipa_ip_type ip)
+{
+	int nbytes = 0;
+	int cnt = 0;
+	uint32_t addr[4];
+	uint32_t mask[4];
+	int i;
+
+	if (attrib->attrib_mask & IPA_FLT_TOS) {
+		nbytes = scnprintf(buff + cnt, sz - cnt, "tos:%d ",
+				attrib->u.v4.tos);
+		cnt += nbytes;
+	}
+	if (attrib->attrib_mask & IPA_FLT_PROTOCOL) {
+		nbytes = scnprintf(buff + cnt, sz - cnt, "protocol:%d ",
+				attrib->u.v4.protocol);
+		cnt += nbytes;
+	}
+	if (attrib->attrib_mask & IPA_FLT_SRC_ADDR) {
+		if (ip == IPA_IP_v4) {
+			addr[0] = htonl(attrib->u.v4.src_addr);
+			mask[0] = htonl(attrib->u.v4.src_addr_mask);
+			nbytes = scnprintf(buff + cnt, sz - cnt,
+					"src_addr:%pI4 src_addr_mask:%pI4 ",
+					addr + 0, mask + 0);
+			cnt += nbytes;
+		} else if (ip == IPA_IP_v6) {
+			for (i = 0; i < 4; i++) {
+				addr[i] = htonl(attrib->u.v6.src_addr[i]);
+				mask[i] = htonl(attrib->u.v6.src_addr_mask[i]);
+			}
+			nbytes =
+			   scnprintf(buff + cnt, sz - cnt,
+					   "src_addr:%pI6 src_addr_mask:%pI6 ",
+					   addr + 0, mask + 0);
+			cnt += nbytes;
+		} else {
+			WARN_ON(1);
+		}
+	}
+	if (attrib->attrib_mask & IPA_FLT_DST_ADDR) {
+		if (ip == IPA_IP_v4) {
+			addr[0] = htonl(attrib->u.v4.dst_addr);
+			mask[0] = htonl(attrib->u.v4.dst_addr_mask);
+			nbytes =
+			   scnprintf(buff + cnt, sz - cnt,
+					   "dst_addr:%pI4 dst_addr_mask:%pI4 ",
+					   addr + 0, mask + 0);
+			cnt += nbytes;
+		} else if (ip == IPA_IP_v6) {
+			for (i = 0; i < 4; i++) {
+				addr[i] = htonl(attrib->u.v6.dst_addr[i]);
+				mask[i] = htonl(attrib->u.v6.dst_addr_mask[i]);
+			}
+			nbytes =
+			   scnprintf(buff + cnt, sz - cnt,
+					   "dst_addr:%pI6 dst_addr_mask:%pI6 ",
+					   addr + 0, mask + 0);
+			cnt += nbytes;
+		} else {
+			WARN_ON(1);
+		}
+	}
+	if (attrib->attrib_mask & IPA_FLT_SRC_PORT_RANGE) {
+		nbytes =
+		   scnprintf(buff + cnt, sz - cnt, "src_port_range:%u %u ",
+				   attrib->src_port_lo,
+			     attrib->src_port_hi);
+		cnt += nbytes;
+	}
+	if (attrib->attrib_mask & IPA_FLT_DST_PORT_RANGE) {
+		nbytes =
+		   scnprintf(buff + cnt, sz - cnt, "dst_port_range:%u %u ",
+				   attrib->dst_port_lo,
+			     attrib->dst_port_hi);
+		cnt += nbytes;
+	}
+	if (attrib->attrib_mask & IPA_FLT_TYPE) {
+		nbytes = scnprintf(buff + cnt, sz - cnt, "type:%d ",
+				attrib->type);
+		cnt += nbytes;
+	}
+	if (attrib->attrib_mask & IPA_FLT_CODE) {
+		nbytes = scnprintf(buff + cnt, sz - cnt, "code:%d ",
+				attrib->code);
+		cnt += nbytes;
+	}
+	if (attrib->attrib_mask & IPA_FLT_SPI) {
+		nbytes = scnprintf(buff + cnt, sz - cnt, "spi:%x ",
+				attrib->spi);
+		cnt += nbytes;
+	}
+	if (attrib->attrib_mask & IPA_FLT_SRC_PORT) {
+		nbytes = scnprintf(buff + cnt, sz - cnt, "src_port:%u ",
+				attrib->src_port);
+		cnt += nbytes;
+	}
+	if (attrib->attrib_mask & IPA_FLT_DST_PORT) {
+		nbytes = scnprintf(buff + cnt, sz - cnt, "dst_port:%u ",
+				attrib->dst_port);
+		cnt += nbytes;
+	}
+	if (attrib->attrib_mask & IPA_FLT_TC) {
+		nbytes = scnprintf(buff + cnt, sz - cnt, "tc:%d ",
+				attrib->u.v6.tc);
+		cnt += nbytes;
+	}
+	if (attrib->attrib_mask & IPA_FLT_FLOW_LABEL) {
+		nbytes = scnprintf(buff + cnt, sz - cnt, "flow_label:%x ",
+				attrib->u.v6.flow_label);
+		cnt += nbytes;
+	}
+	if (attrib->attrib_mask & IPA_FLT_NEXT_HDR) {
+		nbytes = scnprintf(buff + cnt, sz - cnt, "next_hdr:%d ",
+				attrib->u.v6.next_hdr);
+		cnt += nbytes;
+	}
+	if (attrib->attrib_mask & IPA_FLT_META_DATA) {
+		nbytes =
+		   scnprintf(buff + cnt, sz - cnt,
+				   "metadata:%x metadata_mask:%x",
+				   attrib->meta_data, attrib->meta_data_mask);
+		cnt += nbytes;
+	}
+	if (attrib->attrib_mask & IPA_FLT_FRAGMENT) {
+		nbytes = scnprintf(buff + cnt, sz - cnt, "frg ");
+		cnt += nbytes;
+	}
+	nbytes = scnprintf(buff + cnt, sz - cnt, "\n");
+	cnt += nbytes;
+
+	return cnt;
+}
+
+static int ipa_open_dbg(struct inode *inode, struct file *file)
+{
+	file->private_data = inode->i_private;
+	return 0;
+}
+
+static ssize_t ipa_read_rt(struct file *file, char __user *ubuf, size_t count,
+		loff_t *ppos)
+{
+	int nbytes = 0;
+	int cnt = 0;
+	int i = 0;
+	struct ipa_rt_tbl *tbl;
+	struct ipa_rt_entry *entry;
+	struct ipa_rt_tbl_set *set;
+	enum ipa_ip_type ip = (enum ipa_ip_type)file->private_data;
+	u32 hdr_ofst;
+
+	set = &ipa_ctx->rt_tbl_set[ip];
+
+	mutex_lock(&ipa_ctx->lock);
+	list_for_each_entry(tbl, &set->head_rt_tbl_list, link) {
+		i = 0;
+		list_for_each_entry(entry, &tbl->head_rt_rule_list, link) {
+			if (entry->hdr)
+				hdr_ofst = entry->hdr->offset_entry->offset;
+			else
+				hdr_ofst = 0;
+			nbytes = scnprintf(dbg_buff + cnt,
+					IPA_MAX_MSG_LEN - cnt,
+					"tbl_idx:%d tbl_name:%s tbl_ref:%u rule_idx:%d dst:%d ep:%d S:%u hdr_ofst[words]:%u attrib_mask:%08x ",
+					entry->tbl->idx, entry->tbl->name,
+					entry->tbl->ref_cnt, i, entry->rule.dst,
+					ipa_get_ep_mapping(ipa_ctx->mode,
+						entry->rule.dst),
+					   !ipa_ctx->hdr_tbl_lcl,
+					   hdr_ofst >> 2,
+					   entry->rule.attrib.attrib_mask);
+			cnt += nbytes;
+			cnt += ipa_attrib_dump(dbg_buff + cnt,
+					   IPA_MAX_MSG_LEN - cnt,
+					   &entry->rule.attrib,
+					   ip);
+			i++;
+		}
+	}
+	mutex_unlock(&ipa_ctx->lock);
+
+	return simple_read_from_buffer(ubuf, count, ppos, dbg_buff, cnt);
+}
+
+static ssize_t ipa_read_flt(struct file *file, char __user *ubuf, size_t count,
+		loff_t *ppos)
+{
+	int nbytes = 0;
+	int cnt = 0;
+	int i;
+	int j;
+	struct ipa_flt_tbl *tbl;
+	struct ipa_flt_entry *entry;
+	enum ipa_ip_type ip = (enum ipa_ip_type)file->private_data;
+	struct ipa_rt_tbl *rt_tbl;
+
+	tbl = &ipa_ctx->glob_flt_tbl[ip];
+	mutex_lock(&ipa_ctx->lock);
+	i = 0;
+	list_for_each_entry(entry, &tbl->head_flt_rule_list, link) {
+		rt_tbl = (struct ipa_rt_tbl *)entry->rule.rt_tbl_hdl;
+		nbytes = scnprintf(dbg_buff + cnt, IPA_MAX_MSG_LEN - cnt,
+				   "ep_idx:global rule_idx:%d act:%d rt_tbl_idx:%d attrib_mask:%08x ",
+				   i, entry->rule.action, rt_tbl->idx,
+				   entry->rule.attrib.attrib_mask);
+		cnt += nbytes;
+		cnt += ipa_attrib_dump(dbg_buff + cnt, IPA_MAX_MSG_LEN - cnt,
+				&entry->rule.attrib, ip);
+		i++;
+	}
+
+	for (j = 0; j < IPA_NUM_PIPES; j++) {
+		tbl = &ipa_ctx->flt_tbl[j][ip];
+		i = 0;
+		list_for_each_entry(entry, &tbl->head_flt_rule_list, link) {
+			rt_tbl = (struct ipa_rt_tbl *)entry->rule.rt_tbl_hdl;
+			nbytes = scnprintf(dbg_buff + cnt,
+					IPA_MAX_MSG_LEN - cnt,
+					"ep_idx:%d rule_idx:%d act:%d rt_tbl_idx:%d attrib_mask:%08x ",
+					j, i, entry->rule.action, rt_tbl->idx,
+					entry->rule.attrib.attrib_mask);
+			cnt += nbytes;
+			cnt +=
+			   ipa_attrib_dump(dbg_buff + cnt,
+					   IPA_MAX_MSG_LEN - cnt,
+					   &entry->rule.attrib,
+					   ip);
+			i++;
+		}
+	}
+	mutex_unlock(&ipa_ctx->lock);
+
+	return simple_read_from_buffer(ubuf, count, ppos, dbg_buff, cnt);
+}
+
+const struct file_operations ipa_gen_reg_ops = {
+	.read = ipa_read_gen_reg,
+};
+
+const struct file_operations ipa_ep_reg_ops = {
+	.read = ipa_read_ep_reg,
+	.write = ipa_write_ep_reg,
+};
+
+const struct file_operations ipa_hdr_ops = {
+	.read = ipa_read_hdr,
+};
+
+const struct file_operations ipa_rt_ops = {
+	.read = ipa_read_rt,
+	.open = ipa_open_dbg,
+};
+
+const struct file_operations ipa_flt_ops = {
+	.read = ipa_read_flt,
+	.open = ipa_open_dbg,
+};
+
+void ipa_debugfs_init(void)
+{
+	const mode_t read_only_mode = S_IRUSR | S_IRGRP | S_IROTH;
+	const mode_t read_write_mode = S_IRUSR | S_IRGRP | S_IROTH |
+			S_IWUSR | S_IWGRP | S_IWOTH;
+
+	dent = debugfs_create_dir("ipa", 0);
+	if (IS_ERR(dent)) {
+		IPAERR("fail to create folder in debug_fs.\n");
+		return;
+	}
+
+	dfile_gen_reg = debugfs_create_file("gen_reg", read_only_mode, dent, 0,
+			&ipa_gen_reg_ops);
+	if (!dfile_gen_reg || IS_ERR(dfile_gen_reg)) {
+		IPAERR("fail to create file for debug_fs gen_reg\n");
+		goto fail;
+	}
+
+	dfile_ep_reg = debugfs_create_file("ep_reg", read_write_mode, dent, 0,
+			&ipa_ep_reg_ops);
+	if (!dfile_ep_reg || IS_ERR(dfile_ep_reg)) {
+		IPAERR("fail to create file for debug_fs ep_reg\n");
+		goto fail;
+	}
+
+	dfile_hdr = debugfs_create_file("hdr", read_only_mode, dent, 0,
+			&ipa_hdr_ops);
+	if (!dfile_hdr || IS_ERR(dfile_hdr)) {
+		IPAERR("fail to create file for debug_fs hdr\n");
+		goto fail;
+	}
+
+	dfile_ip4_rt = debugfs_create_file("ip4_rt", read_only_mode, dent,
+			(void *)IPA_IP_v4, &ipa_rt_ops);
+	if (!dfile_ip4_rt || IS_ERR(dfile_ip4_rt)) {
+		IPAERR("fail to create file for debug_fs ip4 rt\n");
+		goto fail;
+	}
+
+	dfile_ip6_rt = debugfs_create_file("ip6_rt", read_only_mode, dent,
+			(void *)IPA_IP_v6, &ipa_rt_ops);
+	if (!dfile_ip6_rt || IS_ERR(dfile_ip6_rt)) {
+		IPAERR("fail to create file for debug_fs ip6:w" " rt\n");
+		goto fail;
+	}
+
+	dfile_ip4_flt = debugfs_create_file("ip4_flt", read_only_mode, dent,
+			(void *)IPA_IP_v4, &ipa_flt_ops);
+	if (!dfile_ip4_flt || IS_ERR(dfile_ip4_flt)) {
+		IPAERR("fail to create file for debug_fs ip4 flt\n");
+		goto fail;
+	}
+
+	dfile_ip6_flt = debugfs_create_file("ip6_flt", read_only_mode, dent,
+			(void *)IPA_IP_v6, &ipa_flt_ops);
+	if (!dfile_ip6_flt || IS_ERR(dfile_ip6_flt)) {
+		IPAERR("fail to create file for debug_fs ip6 flt\n");
+		goto fail;
+	}
+
+	return;
+
+fail:
+	debugfs_remove_recursive(dent);
+}
+
+void ipa_debugfs_remove(void)
+{
+	if (IS_ERR(dent)) {
+		IPAERR("ipa_debugfs_remove: folder was not created.\n");
+		return;
+	}
+	debugfs_remove_recursive(dent);
+}
+
+#else /* !CONFIG_DEBUG_FS */
+void ipa_debugfs_init(void) {}
+void ipa_debugfs_remove(void) {}
+#endif
+
diff --git a/drivers/platform/msm/ipa/ipa_dp.c b/drivers/platform/msm/ipa/ipa_dp.c
new file mode 100644
index 0000000..c677a6e
--- /dev/null
+++ b/drivers/platform/msm/ipa/ipa_dp.c
@@ -0,0 +1,1038 @@
+/* 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/device.h>
+#include <linux/dmapool.h>
+#include <linux/list.h>
+#include <linux/netdevice.h>
+#include "ipa_i.h"
+
+#define list_next_entry(pos, member) \
+	list_entry(pos->member.next, typeof(*pos), member)
+/**
+ * ipa_write_done - this function will be (enevtually) called when a Tx
+ * operation is complete
+ * @work:	work_struct used by the work queue
+ */
+void ipa_write_done(struct work_struct *work)
+{
+	struct ipa_tx_pkt_wrapper *tx_pkt;
+	struct ipa_tx_pkt_wrapper *next_pkt;
+	struct ipa_tx_pkt_wrapper *tx_pkt_expected;
+	unsigned long irq_flags;
+	struct ipa_mem_buffer mult = { 0 };
+	int i;
+	u16 cnt;
+
+	tx_pkt = container_of(work, struct ipa_tx_pkt_wrapper, work);
+	cnt = tx_pkt->cnt;
+	IPADBG("cnt=%d\n", cnt);
+
+	if (unlikely(cnt == 0))
+		WARN_ON(1);
+
+	if (cnt > 1 && cnt != 0xFFFF)
+		mult = tx_pkt->mult;
+
+	for (i = 0; i < cnt; i++) {
+		if (unlikely(tx_pkt == NULL))
+			WARN_ON(1);
+		spin_lock_irqsave(&tx_pkt->sys->spinlock, irq_flags);
+		tx_pkt_expected = list_first_entry(&tx_pkt->sys->head_desc_list,
+						   struct ipa_tx_pkt_wrapper,
+						   link);
+		if (unlikely(tx_pkt != tx_pkt_expected)) {
+			spin_unlock_irqrestore(&tx_pkt->sys->spinlock,
+					irq_flags);
+			WARN_ON(1);
+		}
+		next_pkt = list_next_entry(tx_pkt, link);
+		list_del(&tx_pkt->link);
+		tx_pkt->sys->len--;
+		spin_unlock_irqrestore(&tx_pkt->sys->spinlock, irq_flags);
+		dma_pool_free(ipa_ctx->one_kb_no_straddle_pool, tx_pkt->bounce,
+				tx_pkt->mem.phys_base);
+		if (tx_pkt->callback)
+			tx_pkt->callback(tx_pkt->user1, tx_pkt->user2);
+
+		kmem_cache_free(ipa_ctx->tx_pkt_wrapper_cache, tx_pkt);
+		tx_pkt = next_pkt;
+	}
+
+	if (mult.phys_base)
+		dma_free_coherent(NULL, mult.size, mult.base, mult.phys_base);
+}
+
+/**
+ * ipa_send_one() - Send a single descriptor
+ * @sys:	system pipe context
+ * @desc:	descriptor to send
+ *
+ * Return codes: 0: success, -EFAULT: failure
+ */
+int ipa_send_one(struct ipa_sys_context *sys, struct ipa_desc *desc)
+{
+	struct ipa_tx_pkt_wrapper *tx_pkt;
+	unsigned long irq_flags;
+	int result;
+	u16 sps_flags = SPS_IOVEC_FLAG_EOT | SPS_IOVEC_FLAG_INT;
+	dma_addr_t dma_address;
+	u16 len;
+
+	tx_pkt = kmem_cache_zalloc(ipa_ctx->tx_pkt_wrapper_cache, GFP_KERNEL);
+	if (!tx_pkt) {
+		IPAERR("failed to alloc tx wrapper\n");
+		goto fail_mem_alloc;
+	}
+
+	WARN_ON(desc->len > 512);
+
+	/*
+	 * Due to a HW limitation, we need to make sure that the packet does not
+	 * cross a 1KB boundary
+	 */
+	tx_pkt->bounce = dma_pool_alloc(ipa_ctx->one_kb_no_straddle_pool,
+			GFP_KERNEL, &dma_address);
+	if (!tx_pkt->bounce) {
+		dma_address = 0;
+	} else {
+		WARN_ON(!ipa_straddle_boundary
+		       ((u32)dma_address, (u32)dma_address + desc->len - 1,
+			1024));
+		memcpy(tx_pkt->bounce, desc->pyld, desc->len);
+	}
+
+	if (!dma_address) {
+		IPAERR("failed to DMA wrap\n");
+		goto fail_dma_map;
+	}
+
+	INIT_LIST_HEAD(&tx_pkt->link);
+	INIT_WORK(&tx_pkt->work, ipa_write_done);
+	tx_pkt->type = desc->type;
+	tx_pkt->cnt = 1;    /* only 1 desc in this "set" */
+
+	tx_pkt->mem.phys_base = dma_address;
+	tx_pkt->mem.base = desc->pyld;
+	tx_pkt->mem.size = desc->len;
+	tx_pkt->sys = sys;
+	tx_pkt->callback = desc->callback;
+	tx_pkt->user1 = desc->user1;
+	tx_pkt->user2 = desc->user2;
+
+	/*
+	 * Special treatment for immediate commands, where the structure of the
+	 * descriptor is different
+	 */
+	if (desc->type == IPA_IMM_CMD_DESC) {
+		sps_flags |= SPS_IOVEC_FLAG_IMME;
+		len = desc->opcode;
+	} else {
+		len = desc->len;
+	}
+
+	if (desc->type == IPA_IMM_CMD_DESC) {
+		IPADBG("sending cmd=%d pyld_len=%d sps_flags=%x\n",
+				desc->opcode, desc->len, sps_flags);
+		IPA_DUMP_BUFF(desc->pyld, dma_address, desc->len);
+	}
+
+	spin_lock_irqsave(&sys->spinlock, irq_flags);
+	list_add_tail(&tx_pkt->link, &sys->head_desc_list);
+	sys->len++;
+	result = sps_transfer_one(sys->ep->ep_hdl, dma_address, len, tx_pkt,
+			sps_flags);
+	if (result) {
+		IPAERR("sps_transfer_one failed rc=%d\n", result);
+		goto fail_sps_send;
+	}
+
+	spin_unlock_irqrestore(&sys->spinlock, irq_flags);
+
+	return 0;
+
+fail_sps_send:
+	list_del(&tx_pkt->link);
+	spin_unlock_irqrestore(&sys->spinlock, irq_flags);
+	dma_pool_free(ipa_ctx->one_kb_no_straddle_pool, tx_pkt->bounce,
+			dma_address);
+fail_dma_map:
+	kmem_cache_free(ipa_ctx->tx_pkt_wrapper_cache, tx_pkt);
+fail_mem_alloc:
+	return -EFAULT;
+}
+
+/**
+ * ipa_send() - Send multiple descriptors in one HW transaction
+ * @sys: system pipe context
+ * @num_desc: number of packets
+ * @desc: packets to send
+ *
+ * Return codes: 0: success, -EFAULT: failure
+ */
+int ipa_send(struct ipa_sys_context *sys, u16 num_desc, struct ipa_desc *desc)
+{
+	struct ipa_tx_pkt_wrapper *tx_pkt;
+	struct ipa_tx_pkt_wrapper *next_pkt;
+	struct sps_transfer transfer = { 0 };
+	struct sps_iovec *iovec;
+	unsigned long irq_flags;
+	dma_addr_t dma_addr;
+	int i;
+	int j;
+	int result;
+	int fail_dma_wrap;
+	uint size = num_desc * sizeof(struct sps_iovec);
+
+	for (i = 0; i < num_desc; i++) {
+		fail_dma_wrap = 0;
+		tx_pkt = kmem_cache_zalloc(ipa_ctx->tx_pkt_wrapper_cache,
+					   GFP_KERNEL);
+		if (!tx_pkt) {
+			IPAERR("failed to alloc tx wrapper\n");
+			goto failure;
+		}
+		/*
+		 * first desc of set is "special" as it holds the count and
+		 * other info
+		 */
+		if (i == 0) {
+			transfer.user = tx_pkt;
+			transfer.iovec =
+				dma_alloc_coherent(NULL, size, &dma_addr, 0);
+			transfer.iovec_phys = dma_addr;
+			transfer.iovec_count = num_desc;
+			if (!transfer.iovec) {
+				IPAERR("fail alloc DMA mem for sps xfr buff\n");
+				goto failure;
+			}
+
+			tx_pkt->mult.phys_base = dma_addr;
+			tx_pkt->mult.base = transfer.iovec;
+			tx_pkt->mult.size = size;
+			tx_pkt->cnt = num_desc;
+		}
+
+		iovec = &transfer.iovec[i];
+		iovec->flags = 0;
+
+		INIT_LIST_HEAD(&tx_pkt->link);
+		INIT_WORK(&tx_pkt->work, ipa_write_done);
+		tx_pkt->type = desc[i].type;
+
+		tx_pkt->mem.base = desc[i].pyld;
+		tx_pkt->mem.size = desc[i].len;
+
+		WARN_ON(tx_pkt->mem.size > 512);
+
+		/*
+		 * Due to a HW limitation, we need to make sure that the
+		 * packet does not cross a 1KB boundary
+		 */
+		tx_pkt->bounce =
+		   dma_pool_alloc(ipa_ctx->one_kb_no_straddle_pool, GFP_KERNEL,
+				   &tx_pkt->mem.phys_base);
+		if (!tx_pkt->bounce) {
+			tx_pkt->mem.phys_base = 0;
+		} else {
+			WARN_ON(!ipa_straddle_boundary(
+						(u32)tx_pkt->mem.phys_base,
+						(u32)tx_pkt->mem.phys_base +
+						tx_pkt->mem.size - 1, 1024));
+			memcpy(tx_pkt->bounce, tx_pkt->mem.base,
+					tx_pkt->mem.size);
+		}
+
+		if (!tx_pkt->mem.phys_base) {
+			IPAERR("failed to alloc tx wrapper\n");
+			fail_dma_wrap = 1;
+			goto failure;
+		}
+
+		tx_pkt->sys = sys;
+		tx_pkt->callback = desc[i].callback;
+		tx_pkt->user1 = desc[i].user1;
+		tx_pkt->user2 = desc[i].user2;
+
+		iovec->addr = tx_pkt->mem.phys_base;
+		spin_lock_irqsave(&sys->spinlock, irq_flags);
+		list_add_tail(&tx_pkt->link, &sys->head_desc_list);
+		sys->len++;
+		spin_unlock_irqrestore(&sys->spinlock, irq_flags);
+
+		/*
+		 * Special treatment for immediate commands, where the structure
+		 * of the descriptor is different
+		 */
+		if (desc[i].type == IPA_IMM_CMD_DESC) {
+			iovec->size = desc[i].opcode;
+			iovec->flags |= SPS_IOVEC_FLAG_IMME;
+		} else {
+			iovec->size = desc[i].len;
+		}
+
+		if (i == (num_desc - 1)) {
+			iovec->flags |= (SPS_IOVEC_FLAG_EOT |
+					SPS_IOVEC_FLAG_INT);
+			/* "mark" the last desc */
+			tx_pkt->cnt = 0xFFFF;
+		}
+	}
+
+	result = sps_transfer(sys->ep->ep_hdl, &transfer);
+	if (result) {
+		IPAERR("sps_transfer failed rc=%d\n", result);
+		goto failure;
+	}
+
+	return 0;
+
+failure:
+	tx_pkt = transfer.user;
+	for (j = 0; j < i; j++) {
+		spin_lock_irqsave(&sys->spinlock, irq_flags);
+		next_pkt = list_next_entry(tx_pkt, link);
+		list_del(&tx_pkt->link);
+		spin_unlock_irqrestore(&sys->spinlock, irq_flags);
+		dma_pool_free(ipa_ctx->one_kb_no_straddle_pool, tx_pkt->bounce,
+				tx_pkt->mem.phys_base);
+		kmem_cache_free(ipa_ctx->tx_pkt_wrapper_cache, tx_pkt);
+		tx_pkt = next_pkt;
+	}
+	if (i < num_desc)
+		/* last desc failed */
+		if (fail_dma_wrap)
+			kmem_cache_free(ipa_ctx->tx_pkt_wrapper_cache, tx_pkt);
+	if (transfer.iovec_phys)
+		dma_free_coherent(NULL, size, transfer.iovec,
+				  transfer.iovec_phys);
+
+	return -EFAULT;
+}
+
+/**
+ * ipa_cmd_ack - callback function which will be called by SPS driver after an
+ * immediate command is complete.
+ * @user1:	pointer to the descriptor of the transfer
+ * @user2:
+ *
+ * Complete the immediate commands completion object, this will release the
+ * thread which waits on this completion object (ipa_send_cmd())
+ */
+static void ipa_cmd_ack(void *user1, void *user2)
+{
+	struct ipa_desc *desc = (struct ipa_desc *)user1;
+
+	if (!desc)
+		WARN_ON(1);
+	IPADBG("got ack for cmd=%d\n", desc->opcode);
+	complete(&desc->xfer_done);
+}
+
+/**
+ * ipa_send_cmd - send immediate commands
+ * @num_desc:	number of descriptors within the descr struct
+ * @descr:	descriptor structure
+ *
+ * Function will block till command gets ACK from IPA HW, caller needs
+ * to free any resources it allocated after function returns
+ */
+int ipa_send_cmd(u16 num_desc, struct ipa_desc *descr)
+{
+	struct ipa_desc *desc;
+
+	if (num_desc == 1) {
+		init_completion(&descr->xfer_done);
+
+		/* client should not set these */
+		if (descr->callback || descr->user1)
+			WARN_ON(1);
+
+		descr->callback = ipa_cmd_ack;
+		descr->user1 = descr;
+		if (ipa_send_one(&ipa_ctx->sys[IPA_A5_CMD], descr)) {
+			IPAERR("fail to send immediate command\n");
+			return -EFAULT;
+		}
+		wait_for_completion(&descr->xfer_done);
+	} else {
+		desc = &descr[num_desc - 1];
+		init_completion(&desc->xfer_done);
+
+		/* client should not set these */
+		if (desc->callback || desc->user1)
+			WARN_ON(1);
+
+		desc->callback = ipa_cmd_ack;
+		desc->user1 = desc;
+		if (ipa_send(&ipa_ctx->sys[IPA_A5_CMD], num_desc, descr)) {
+			IPAERR("fail to send multiple immediate command set\n");
+			return -EFAULT;
+		}
+		wait_for_completion(&desc->xfer_done);
+	}
+
+	return 0;
+}
+
+/**
+ * ipa_tx_notify() - Callback function which will be called by the SPS driver
+ * after a Tx operation is complete. Called in an interrupt context.
+ * @notify:	SPS driver supplied notification struct
+ */
+static void ipa_tx_notify(struct sps_event_notify *notify)
+{
+	struct ipa_tx_pkt_wrapper *tx_pkt;
+
+	IPADBG("event %d notified\n", notify->event_id);
+
+	switch (notify->event_id) {
+	case SPS_EVENT_EOT:
+		tx_pkt = notify->data.transfer.user;
+		queue_work(ipa_ctx->tx_wq, &tx_pkt->work);
+		break;
+	default:
+		IPAERR("recieved unexpected event id %d\n", notify->event_id);
+	}
+}
+
+/**
+ * ipa_handle_rx_core() - The core functionality of packet reception. This
+ * function is read from multiple code paths.
+ *
+ * All the packets on the Rx data path are received on the IPA_A5_LAN_WAN_IN
+ * endpoint. The function runs as long as there are packets in the pipe.
+ * For each packet:
+ *  - Disconnect the packet from the system pipe linked list
+ *  - Unmap the packets skb, make it non DMAable
+ *  - Free the packet from the cache
+ *  - Prepare a proper skb
+ *  - Call the endpoints notify function, passing the skb in the parameters
+ *  - Replenish the rx cache
+ */
+void ipa_handle_rx_core(void)
+{
+	struct ipa_a5_mux_hdr *mux_hdr;
+	struct ipa_rx_pkt_wrapper *rx_pkt;
+	struct sk_buff *rx_skb;
+	struct sps_iovec iov;
+	unsigned long irq_flags;
+	u16 pull_len;
+	u16 padding;
+	int ret;
+	struct ipa_sys_context *sys = &ipa_ctx->sys[IPA_A5_LAN_WAN_IN];
+	struct ipa_ep_context *ep;
+
+	do {
+		ret = sps_get_iovec(sys->ep->ep_hdl, &iov);
+		if (ret) {
+			IPAERR("sps_get_iovec failed %d\n", ret);
+			break;
+		}
+
+		/* Break the loop when there are no more packets to receive */
+		if (iov.addr == 0)
+			break;
+
+		spin_lock_irqsave(&sys->spinlock, irq_flags);
+		if (list_empty(&sys->head_desc_list))
+			WARN_ON(1);
+		rx_pkt = list_first_entry(&sys->head_desc_list,
+					  struct ipa_rx_pkt_wrapper, link);
+		if (!rx_pkt)
+			WARN_ON(1);
+		rx_pkt->len = iov.size;
+		sys->len--;
+		list_del(&rx_pkt->link);
+		spin_unlock_irqrestore(&sys->spinlock, irq_flags);
+
+		IPADBG("--curr_cnt=%d\n", sys->len);
+
+		rx_skb = rx_pkt->skb;
+		dma_unmap_single(NULL, rx_pkt->dma_address, IPA_RX_SKB_SIZE,
+				 DMA_FROM_DEVICE);
+		kmem_cache_free(ipa_ctx->rx_pkt_wrapper_cache, rx_pkt);
+
+		/*
+		 * make it look like a real skb, "data" was already set at
+		 * alloc time
+		 */
+		rx_skb->tail = rx_skb->data + rx_pkt->len;
+		rx_skb->len = rx_pkt->len;
+		rx_skb->truesize = rx_pkt->len + sizeof(struct sk_buff);
+
+		mux_hdr = (struct ipa_a5_mux_hdr *)rx_skb->data;
+
+		IPADBG("RX pkt len=%d IID=0x%x src=%d, flags=0x%x, meta=0x%x\n",
+		       rx_skb->len, ntohs(mux_hdr->interface_id),
+		       mux_hdr->src_pipe_index,
+		       mux_hdr->flags, ntohl(mux_hdr->metadata));
+
+		IPA_DUMP_BUFF(rx_skb->data, 0, rx_skb->len);
+
+		if (mux_hdr->src_pipe_index >= IPA_NUM_PIPES ||
+		    !ipa_ctx->ep[mux_hdr->src_pipe_index].valid ||
+		    !ipa_ctx->ep[mux_hdr->src_pipe_index].notify) {
+			IPAERR("drop pipe=%d ep_valid=%d notify=%p\n",
+			       mux_hdr->src_pipe_index,
+			       ipa_ctx->ep[mux_hdr->src_pipe_index].valid,
+			       ipa_ctx->ep[mux_hdr->src_pipe_index].notify);
+			dev_kfree_skb_any(rx_skb);
+			ipa_replenish_rx_cache();
+			continue;
+		}
+
+		ep = &ipa_ctx->ep[mux_hdr->src_pipe_index];
+		pull_len = sizeof(struct ipa_a5_mux_hdr);
+
+		/*
+		 * IP packet starts on word boundary
+		 * remove the MUX header and any padding and pass the frame to
+		 * the client which registered a rx callback on the "src pipe"
+		 */
+		padding = ep->cfg.hdr.hdr_len & 0x3;
+		if (padding)
+			pull_len += 4 - padding;
+
+		IPADBG("pulling %d bytes from skb\n", pull_len);
+		skb_pull(rx_skb, pull_len);
+		ep->notify(ep->priv, IPA_RECEIVE, (unsigned long)(rx_skb));
+		ipa_replenish_rx_cache();
+	} while (1);
+}
+
+/**
+ * ipa_rx_switch_to_intr_mode() - Operate the Rx data path in interrupt mode
+ */
+static void ipa_rx_switch_to_intr_mode(void)
+{
+	int ret;
+	struct ipa_sys_context *sys;
+
+	IPADBG("Enter");
+	if (!ipa_ctx->curr_polling_state) {
+		IPAERR("already in intr mode\n");
+		return;
+	}
+
+	sys = &ipa_ctx->sys[IPA_A5_LAN_WAN_IN];
+
+	ret = sps_get_config(sys->ep->ep_hdl, &sys->ep->connect);
+	if (ret) {
+		IPAERR("sps_get_config() failed %d\n", ret);
+		return;
+	}
+	sys->event.options = SPS_O_EOT;
+	ret = sps_register_event(sys->ep->ep_hdl, &sys->event);
+	if (ret) {
+		IPAERR("sps_register_event() failed %d\n", ret);
+		return;
+	}
+	sys->ep->connect.options =
+		SPS_O_AUTO_ENABLE | SPS_O_ACK_TRANSFERS | SPS_O_EOT;
+	ret = sps_set_config(sys->ep->ep_hdl, &sys->ep->connect);
+	if (ret) {
+		IPAERR("sps_set_config() failed %d\n", ret);
+		return;
+	}
+	ipa_handle_rx_core();
+	ipa_ctx->curr_polling_state = 0;
+}
+
+/**
+ * ipa_rx_switch_to_poll_mode() - Operate the Rx data path in polling mode
+ */
+static void ipa_rx_switch_to_poll_mode(void)
+{
+	int ret;
+	struct ipa_ep_context *ep;
+
+	IPADBG("Enter");
+	ep = ipa_ctx->sys[IPA_A5_LAN_WAN_IN].ep;
+
+	ret = sps_get_config(ep->ep_hdl, &ep->connect);
+	if (ret) {
+		IPAERR("sps_get_config() failed %d\n", ret);
+		return;
+	}
+	ep->connect.options =
+		SPS_O_AUTO_ENABLE | SPS_O_ACK_TRANSFERS | SPS_O_POLL;
+	ret = sps_set_config(ep->ep_hdl, &ep->connect);
+	if (ret) {
+		IPAERR("sps_set_config() failed %d\n", ret);
+		return;
+	}
+	ipa_ctx->curr_polling_state = 1;
+}
+
+/**
+ * ipa_rx_notify() - Callback function which is called by the SPS driver when a
+ * a packet is received
+ * @notify:	SPS driver supplied notification information
+ *
+ * Called in an interrupt context, therefore the majority of the work is
+ * deffered using a work queue.
+ *
+ * After receiving a packet, the driver goes to polling mode and keeps pulling
+ * packets until the rx buffer is empty, then it goes back to interrupt mode.
+ * This comes to prevent the CPU from handling too many interrupts when the
+ * throughput is high.
+ */
+static void ipa_rx_notify(struct sps_event_notify *notify)
+{
+	struct ipa_rx_pkt_wrapper *rx_pkt;
+
+	IPADBG("event %d notified\n", notify->event_id);
+
+	switch (notify->event_id) {
+	case SPS_EVENT_EOT:
+		if (!ipa_ctx->curr_polling_state) {
+			ipa_rx_switch_to_poll_mode();
+			rx_pkt = notify->data.transfer.user;
+			queue_work(ipa_ctx->rx_wq, &rx_pkt->work);
+		}
+		break;
+	default:
+		IPAERR("recieved unexpected event id %d\n", notify->event_id);
+	}
+}
+
+/**
+ * ipa_setup_sys_pipe() - Setup an IPA end-point in system-BAM mode and perform
+ * IPA EP configuration
+ * @sys_in:	[in] input needed to setup BAM pipe and config EP
+ * @clnt_hdl:	[out] client handle
+ *
+ * Returns:	0 on success, negative on failure
+ */
+int ipa_setup_sys_pipe(struct ipa_sys_connect_params *sys_in, u32 *clnt_hdl)
+{
+	int ipa_ep_idx;
+	int sys_idx = -1;
+	int result = -EFAULT;
+	dma_addr_t dma_addr;
+
+	if (sys_in == NULL || clnt_hdl == NULL ||
+	    sys_in->client >= IPA_CLIENT_MAX || sys_in->desc_fifo_sz == 0) {
+		IPAERR("bad parm.\n");
+		result = -EINVAL;
+		goto fail_bad_param;
+	}
+
+	ipa_ep_idx = ipa_get_ep_mapping(ipa_ctx->mode, sys_in->client);
+	if (ipa_ep_idx == -1) {
+		IPAERR("Invalid client.\n");
+		goto fail_bad_param;
+	}
+
+	if (ipa_ctx->ep[ipa_ep_idx].valid == 1) {
+		IPAERR("EP already allocated.\n");
+		goto fail_bad_param;
+	}
+
+	memset(&ipa_ctx->ep[ipa_ep_idx], 0, sizeof(struct ipa_ep_context));
+
+	ipa_ctx->ep[ipa_ep_idx].valid = 1;
+	ipa_ctx->ep[ipa_ep_idx].client = sys_in->client;
+
+	if (ipa_cfg_ep(ipa_ep_idx, &sys_in->ipa_ep_cfg)) {
+		IPAERR("fail to configure EP.\n");
+		goto fail_sps_api;
+	}
+
+	/* Default Config */
+	ipa_ctx->ep[ipa_ep_idx].ep_hdl = sps_alloc_endpoint();
+
+	if (ipa_ctx->ep[ipa_ep_idx].ep_hdl == NULL) {
+		IPAERR("SPS EP allocation failed.\n");
+		goto fail_sps_api;
+	}
+
+	result = sps_get_config(ipa_ctx->ep[ipa_ep_idx].ep_hdl,
+			&ipa_ctx->ep[ipa_ep_idx].connect);
+	if (result) {
+		IPAERR("fail to get config.\n");
+		goto fail_mem_alloc;
+	}
+
+	/* Specific Config */
+	if (IPA_CLIENT_IS_CONS(sys_in->client)) {
+		ipa_ctx->ep[ipa_ep_idx].connect.mode = SPS_MODE_SRC;
+		ipa_ctx->ep[ipa_ep_idx].connect.destination =
+			SPS_DEV_HANDLE_MEM;
+		ipa_ctx->ep[ipa_ep_idx].connect.source = ipa_ctx->bam_handle;
+		ipa_ctx->ep[ipa_ep_idx].connect.dest_pipe_index =
+			ipa_ctx->a5_pipe_index++;
+		ipa_ctx->ep[ipa_ep_idx].connect.src_pipe_index = ipa_ep_idx;
+		ipa_ctx->ep[ipa_ep_idx].connect.options =
+			SPS_O_AUTO_ENABLE | SPS_O_EOT | SPS_O_ACK_TRANSFERS;
+		if (ipa_ctx->polling_mode)
+			ipa_ctx->ep[ipa_ep_idx].connect.options |= SPS_O_POLL;
+	} else {
+		ipa_ctx->ep[ipa_ep_idx].connect.mode = SPS_MODE_DEST;
+		ipa_ctx->ep[ipa_ep_idx].connect.source = SPS_DEV_HANDLE_MEM;
+		ipa_ctx->ep[ipa_ep_idx].connect.destination =
+			ipa_ctx->bam_handle;
+		ipa_ctx->ep[ipa_ep_idx].connect.src_pipe_index =
+			ipa_ctx->a5_pipe_index++;
+		ipa_ctx->ep[ipa_ep_idx].connect.dest_pipe_index = ipa_ep_idx;
+		ipa_ctx->ep[ipa_ep_idx].connect.options =
+			SPS_O_AUTO_ENABLE | SPS_O_EOT;
+		if (ipa_ctx->polling_mode)
+			ipa_ctx->ep[ipa_ep_idx].connect.options |=
+				SPS_O_ACK_TRANSFERS | SPS_O_POLL;
+	}
+
+	ipa_ctx->ep[ipa_ep_idx].connect.desc.size = sys_in->desc_fifo_sz;
+	ipa_ctx->ep[ipa_ep_idx].connect.desc.base =
+	   dma_alloc_coherent(NULL, ipa_ctx->ep[ipa_ep_idx].connect.desc.size,
+			   &dma_addr, 0);
+	ipa_ctx->ep[ipa_ep_idx].connect.desc.phys_base = dma_addr;
+	if (ipa_ctx->ep[ipa_ep_idx].connect.desc.base == NULL) {
+		IPAERR("fail to get DMA desc memory.\n");
+		goto fail_mem_alloc;
+	}
+
+	ipa_ctx->ep[ipa_ep_idx].connect.event_thresh = IPA_EVENT_THRESHOLD;
+
+	result = sps_connect(ipa_ctx->ep[ipa_ep_idx].ep_hdl,
+			&ipa_ctx->ep[ipa_ep_idx].connect);
+	if (result) {
+		IPAERR("sps_connect fails.\n");
+		goto fail_sps_connect;
+	}
+
+	switch (ipa_ep_idx) {
+	case 1:
+		/* fall through */
+	case 2:
+		/* fall through */
+	case 3:
+		sys_idx = ipa_ep_idx;
+		break;
+	case 15:
+		sys_idx = IPA_A5_WLAN_AMPDU_OUT;
+		break;
+	default:
+		IPAERR("Invalid EP index.\n");
+		result = -EFAULT;
+		goto fail_register_event;
+	}
+
+	if (!ipa_ctx->polling_mode) {
+		if (IPA_CLIENT_IS_CONS(sys_in->client)) {
+			ipa_ctx->sys[sys_idx].event.options = SPS_O_EOT;
+			ipa_ctx->sys[sys_idx].event.mode = SPS_TRIGGER_CALLBACK;
+			ipa_ctx->sys[sys_idx].event.xfer_done = NULL;
+			ipa_ctx->sys[sys_idx].event.callback = ipa_rx_notify;
+			ipa_ctx->sys[sys_idx].event.user =
+				&ipa_ctx->sys[sys_idx];
+			result =
+			   sps_register_event(ipa_ctx->ep[ipa_ep_idx].ep_hdl,
+					      &ipa_ctx->sys[sys_idx].event);
+			if (result < 0) {
+				IPAERR("rx register event error %d\n", result);
+				goto fail_register_event;
+			}
+		} else {
+			ipa_ctx->sys[sys_idx].event.options = SPS_O_EOT;
+			ipa_ctx->sys[sys_idx].event.mode = SPS_TRIGGER_CALLBACK;
+			ipa_ctx->sys[sys_idx].event.xfer_done = NULL;
+			ipa_ctx->sys[sys_idx].event.callback = ipa_tx_notify;
+			ipa_ctx->sys[sys_idx].event.user =
+				&ipa_ctx->sys[sys_idx];
+			result =
+			   sps_register_event(ipa_ctx->ep[ipa_ep_idx].ep_hdl,
+					      &ipa_ctx->sys[sys_idx].event);
+			if (result < 0) {
+				IPAERR("tx register event error %d\n", result);
+				goto fail_register_event;
+			}
+		}
+	}
+
+	return 0;
+
+fail_register_event:
+	sps_disconnect(ipa_ctx->ep[ipa_ep_idx].ep_hdl);
+fail_sps_connect:
+	dma_free_coherent(NULL, ipa_ctx->ep[ipa_ep_idx].connect.desc.size,
+			  ipa_ctx->ep[ipa_ep_idx].connect.desc.base,
+			  ipa_ctx->ep[ipa_ep_idx].connect.desc.phys_base);
+fail_mem_alloc:
+	sps_free_endpoint(ipa_ctx->ep[ipa_ep_idx].ep_hdl);
+fail_sps_api:
+	memset(&ipa_ctx->ep[ipa_ep_idx], 0, sizeof(struct ipa_ep_context));
+fail_bad_param:
+	return result;
+}
+EXPORT_SYMBOL(ipa_setup_sys_pipe);
+
+/**
+ * ipa_teardown_sys_pipe() - Teardown the system-BAM pipe and cleanup IPA EP
+ * @clnt_hdl:	[in] the handle obtained from ipa_setup_sys_pipe
+ *
+ * Returns:	0 on success, negative on failure
+ */
+int ipa_teardown_sys_pipe(u32 clnt_hdl)
+{
+	if (clnt_hdl >= IPA_NUM_PIPES || ipa_ctx->ep[clnt_hdl].valid == 0) {
+		IPAERR("bad parm.\n");
+		return -EINVAL;
+	}
+
+	sps_disconnect(ipa_ctx->ep[clnt_hdl].ep_hdl);
+	dma_free_coherent(NULL, ipa_ctx->ep[clnt_hdl].connect.desc.size,
+			  ipa_ctx->ep[clnt_hdl].connect.desc.base,
+			  ipa_ctx->ep[clnt_hdl].connect.desc.phys_base);
+	sps_free_endpoint(ipa_ctx->ep[clnt_hdl].ep_hdl);
+	memset(&ipa_ctx->ep[clnt_hdl], 0, sizeof(struct ipa_ep_context));
+	return 0;
+}
+EXPORT_SYMBOL(ipa_teardown_sys_pipe);
+
+/**
+ * ipa_tx_comp() - Callback function which will call the user supplied callback
+ * function to release the skb, or release it on its own if no callback function
+ * was supplied.
+ * @user1
+ * @user2
+ */
+static void ipa_tx_comp(void *user1, void *user2)
+{
+	struct sk_buff *skb = (struct sk_buff *)user1;
+	u32 ep_idx = (u32)user2;
+
+	IPADBG("skb=%p ep=%d\n", skb, ep_idx);
+
+	if (ipa_ctx->ep[ep_idx].notify)
+		ipa_ctx->ep[ep_idx].notify(ipa_ctx->ep[ep_idx].priv,
+				IPA_WRITE_DONE, (unsigned long)skb);
+	else
+		dev_kfree_skb_any(skb);
+}
+
+/**
+ * ipa_tx_dp() - Data-path tx handler
+ * @dst:	[in] which IPA destination to route tx packets to
+ * @skb:	[in] the packet to send
+ * @metadata:	[in] TX packet meta-data
+ *
+ * Data-path tx handler, this is used for both SW data-path which by-passes most
+ * IPA HW blocks AND the regular HW data-path for WLAN AMPDU traffic only. If
+ * dst is a "valid" CONS type, then SW data-path is used. If dst is the
+ * WLAN_AMPDU PROD type, then HW data-path for WLAN AMPDU is used. Anything else
+ * is an error. For errors, client needs to free the skb as needed. For success,
+ * IPA driver will later invoke client calback if one was supplied. That
+ * callback should free the skb. If no callback supplied, IPA driver will free
+ * the skb internally
+ *
+ * Returns:	0 on success, negative on failure
+ */
+int ipa_tx_dp(enum ipa_client_type dst, struct sk_buff *skb,
+		struct ipa_tx_meta *meta)
+{
+	struct ipa_desc desc[2];
+	int ipa_ep_idx;
+	struct ipa_ip_packet_init *cmd;
+
+	memset(&desc, 0, 2 * sizeof(struct ipa_desc));
+
+	ipa_ep_idx = ipa_get_ep_mapping(ipa_ctx->mode, dst);
+	if (ipa_ep_idx == -1) {
+		IPAERR("dest EP does not exist.\n");
+		goto fail_gen;
+	}
+
+	if (ipa_ctx->ep[ipa_ep_idx].valid == 0) {
+		IPAERR("dest EP not valid.\n");
+		goto fail_gen;
+	}
+
+	if (IPA_CLIENT_IS_CONS(dst)) {
+		cmd = kzalloc(sizeof(struct ipa_ip_packet_init), GFP_KERNEL);
+		if (!cmd) {
+			IPAERR("failed to alloc immediate command object\n");
+			goto fail_mem_alloc;
+		}
+
+		cmd->destination_pipe_index = ipa_ep_idx;
+		if (meta && meta->mbim_stream_id_valid)
+			cmd->metadata = meta->mbim_stream_id;
+		desc[0].opcode = IPA_IP_PACKET_INIT;
+		desc[0].pyld = cmd;
+		desc[0].len = sizeof(struct ipa_ip_packet_init);
+		desc[0].type = IPA_IMM_CMD_DESC;
+		desc[1].pyld = skb->data;
+		desc[1].len = skb->len;
+		desc[1].type = IPA_DATA_DESC_SKB;
+		desc[1].callback = ipa_tx_comp;
+		desc[1].user1 = skb;
+		desc[1].user2 = (void *)ipa_ep_idx;
+
+		if (ipa_send(&ipa_ctx->sys[IPA_A5_LAN_WAN_OUT], 2, desc)) {
+			IPAERR("fail to send immediate command\n");
+			goto fail_send;
+		}
+	} else if (dst == IPA_CLIENT_A5_WLAN_AMPDU_PROD) {
+		desc[0].pyld = skb->data;
+		desc[0].len = skb->len;
+		desc[0].type = IPA_DATA_DESC_SKB;
+		desc[0].callback = ipa_tx_comp;
+		desc[0].user1 = skb;
+		desc[0].user2 = (void *)ipa_ep_idx;
+
+		if (ipa_send_one(&ipa_ctx->sys[IPA_A5_WLAN_AMPDU_OUT],
+					&desc[0])) {
+			IPAERR("fail to send skb\n");
+			goto fail_gen;
+		}
+	} else {
+		IPAERR("%d PROD is not supported.\n", dst);
+		goto fail_gen;
+	}
+
+	return 0;
+
+fail_send:
+	kfree(cmd);
+fail_mem_alloc:
+fail_gen:
+	return -EFAULT;
+}
+EXPORT_SYMBOL(ipa_tx_dp);
+
+/**
+ * ipa_handle_rx() - handle packet reception. This function is executed in the
+ * context of a work queue.
+ * @work: work struct needed by the work queue
+ *
+ * ipa_handle_rx_core() is run in polling mode. After all packets has been
+ * received, the driver switches back to interrupt mode.
+ */
+void ipa_handle_rx(struct work_struct *work)
+{
+	ipa_handle_rx_core();
+	ipa_rx_switch_to_intr_mode();
+}
+
+/**
+ * ipa_replenish_rx_cache() - Replenish the Rx packets cache.
+ *
+ * The function allocates buffers in the rx_pkt_wrapper_cache cache until there
+ * are IPA_RX_POOL_CEIL buffers in the cache.
+ *   - Allocate a buffer in the cache
+ *   - Initialized the packets link
+ *   - Initialize the packets work struct
+ *   - Allocate the packets socket buffer (skb)
+ *   - Fill the packets skb with data
+ *   - Make the packet DMAable
+ *   - Add the packet to the system pipe linked list
+ *   - Initiate a SPS transfer so that SPS driver will use this packet later.
+ */
+void ipa_replenish_rx_cache(void)
+{
+	void *ptr;
+	struct ipa_rx_pkt_wrapper *rx_pkt;
+	int ret;
+	int rx_len_cached;
+	unsigned long irq_flags;
+	struct ipa_sys_context *sys = &ipa_ctx->sys[IPA_A5_LAN_WAN_IN];
+
+	spin_lock_irqsave(&sys->spinlock, irq_flags);
+	rx_len_cached = sys->len;
+	spin_unlock_irqrestore(&sys->spinlock, irq_flags);
+
+	/* true RX data path is not currently exercised so drop the ceil */
+	while (rx_len_cached < (IPA_RX_POOL_CEIL >> 3)) {
+		rx_pkt = kmem_cache_zalloc(ipa_ctx->rx_pkt_wrapper_cache,
+					   GFP_KERNEL);
+		if (!rx_pkt) {
+			IPAERR("failed to alloc rx wrapper\n");
+			return;
+		}
+
+		INIT_LIST_HEAD(&rx_pkt->link);
+		INIT_WORK(&rx_pkt->work, ipa_handle_rx);
+
+		rx_pkt->skb = __dev_alloc_skb(IPA_RX_SKB_SIZE, GFP_KERNEL);
+		if (rx_pkt->skb == NULL) {
+			IPAERR("failed to alloc skb\n");
+			goto fail_skb_alloc;
+		}
+		ptr = skb_put(rx_pkt->skb, IPA_RX_SKB_SIZE);
+		rx_pkt->dma_address = dma_map_single(NULL, ptr,
+						     IPA_RX_SKB_SIZE,
+						     DMA_FROM_DEVICE);
+		if (rx_pkt->dma_address == 0 || rx_pkt->dma_address == ~0) {
+			IPAERR("dma_map_single failure %p for %p\n",
+			       (void *)rx_pkt->dma_address, ptr);
+			goto fail_dma_mapping;
+		}
+
+		spin_lock_irqsave(&sys->spinlock, irq_flags);
+		list_add_tail(&rx_pkt->link, &sys->head_desc_list);
+		rx_len_cached = ++sys->len;
+		spin_unlock_irqrestore(&sys->spinlock, irq_flags);
+
+		ret = sps_transfer_one(sys->ep->ep_hdl, rx_pkt->dma_address,
+				       IPA_RX_SKB_SIZE, rx_pkt,
+				       SPS_IOVEC_FLAG_INT);
+
+		if (ret) {
+			IPAERR("sps_transfer_one failed %d\n", ret);
+			goto fail_sps_transfer;
+		}
+
+		IPADBG("++curr_cnt=%d\n", sys->len);
+	}
+
+	return;
+
+fail_sps_transfer:
+	spin_lock_irqsave(&sys->spinlock, irq_flags);
+	list_del(&rx_pkt->link);
+	--sys->len;
+	spin_unlock_irqrestore(&sys->spinlock, irq_flags);
+	dma_unmap_single(NULL, rx_pkt->dma_address, IPA_RX_SKB_SIZE,
+			 DMA_FROM_DEVICE);
+fail_dma_mapping:
+	dev_kfree_skb_any(rx_pkt->skb);
+fail_skb_alloc:
+	kmem_cache_free(ipa_ctx->rx_pkt_wrapper_cache, rx_pkt);
+
+	return;
+}
+
+/**
+ * ipa_cleanup_rx() - release RX queue resources
+ *
+ */
+void ipa_cleanup_rx(void)
+{
+	struct ipa_rx_pkt_wrapper *rx_pkt;
+	struct ipa_rx_pkt_wrapper *r;
+	unsigned long irq_flags;
+	struct ipa_sys_context *sys = &ipa_ctx->sys[IPA_A5_LAN_WAN_IN];
+
+	spin_lock_irqsave(&sys->spinlock, irq_flags);
+	list_for_each_entry_safe(rx_pkt, r,
+				 &sys->head_desc_list, link) {
+		list_del(&rx_pkt->link);
+		dma_unmap_single(NULL, rx_pkt->dma_address, IPA_RX_SKB_SIZE,
+				 DMA_FROM_DEVICE);
+		dev_kfree_skb_any(rx_pkt->skb);
+		kmem_cache_free(ipa_ctx->rx_pkt_wrapper_cache, rx_pkt);
+	}
+	spin_unlock_irqrestore(&sys->spinlock, irq_flags);
+}
+
diff --git a/drivers/platform/msm/ipa/ipa_flt.c b/drivers/platform/msm/ipa/ipa_flt.c
new file mode 100644
index 0000000..81f3a80
--- /dev/null
+++ b/drivers/platform/msm/ipa/ipa_flt.c
@@ -0,0 +1,811 @@
+/* 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 "ipa_i.h"
+
+#define IPA_FLT_TABLE_WORD_SIZE			(4)
+#define IPA_FLT_ENTRY_MEMORY_ALLIGNMENT		(0x3)
+#define IPA_FLT_BIT_MASK			(0x1)
+#define IPA_FLT_TABLE_INDEX_NOT_FOUND		(-1)
+#define IPA_FLT_STATUS_OF_ADD_FAILED		(-1)
+#define IPA_FLT_STATUS_OF_DEL_FAILED		(-1)
+
+/**
+ * ipa_generate_flt_hw_rule() - generates the filtering hardware rule
+ * @ip: the ip address family type
+ * @entry: routing entry
+ * @buf: output buffer, buf == NULL means
+ *		caller wants to know the size of the rule as seen
+ *		by HW so they did not pass a valid buffer, we will use a
+ *		scratch buffer instead.
+ *		With this scheme we are going to
+ *		generate the rule twice, once to know size using scratch
+ *		buffer and second to write the rule to the actual caller
+ *		supplied buffer which is of required size
+ *
+ * Returns:	0 on success, negative on failure
+ *
+ * caller needs to hold any needed locks to ensure integrity
+ *
+ */
+static int ipa_generate_flt_hw_rule(enum ipa_ip_type ip,
+		struct ipa_flt_entry *entry, u8 *buf)
+{
+	struct ipa_flt_rule_hw_hdr *hdr;
+	const struct ipa_flt_rule *rule =
+		(const struct ipa_flt_rule *)&entry->rule;
+	u16 en_rule = 0;
+	u8 tmp[IPA_RT_FLT_HW_RULE_BUF_SIZE];
+	u8 *start;
+
+	memset(tmp, 0, IPA_RT_FLT_HW_RULE_BUF_SIZE);
+	if (buf == NULL)
+		buf = tmp;
+
+	start = buf;
+	hdr = (struct ipa_flt_rule_hw_hdr *)buf;
+	hdr->u.hdr.action = entry->rule.action;
+	hdr->u.hdr.rt_tbl_idx = entry->rt_tbl->idx;
+	hdr->u.hdr.rsvd = 0;
+	buf += sizeof(struct ipa_flt_rule_hw_hdr);
+
+	if (ipa_generate_hw_rule(ip, &rule->attrib, &buf, &en_rule)) {
+		IPAERR("fail to generate hw rule\n");
+		return -EPERM;
+	}
+
+	IPADBG("en_rule %x\n", en_rule);
+
+	hdr->u.hdr.en_rule = en_rule;
+	ipa_write_32(hdr->u.word, (u8 *)hdr);
+
+	if (entry->hw_len == 0) {
+		entry->hw_len = buf - start;
+	} else if (entry->hw_len != (buf - start)) {
+		IPAERR("hw_len differs b/w passes passed=%x calc=%x\n",
+		       entry->hw_len, (buf - start));
+		return -EPERM;
+	}
+
+	return 0;
+}
+
+/**
+ * ipa_get_flt_hw_tbl_size() - returns the size of HW filtering table
+ * @ip: the ip address family type
+ * @hdr_sz: header size
+ *
+ * Returns:	0 on success, negative on failure
+ *
+ * caller needs to hold any needed locks to ensure integrity
+ *
+ */
+static int ipa_get_flt_hw_tbl_size(enum ipa_ip_type ip, u32 *hdr_sz)
+{
+	struct ipa_flt_tbl *tbl;
+	struct ipa_flt_entry *entry;
+	u32 total_sz = 0;
+	u32 rule_set_sz;
+	int i;
+
+	*hdr_sz = 0;
+	tbl = &ipa_ctx->glob_flt_tbl[ip];
+	rule_set_sz = 0;
+	list_for_each_entry(entry, &tbl->head_flt_rule_list, link) {
+		if (ipa_generate_flt_hw_rule(ip, entry, NULL)) {
+			IPAERR("failed to find HW FLT rule size\n");
+			return -EPERM;
+		}
+		IPADBG("glob ip %d len %d\n", ip, entry->hw_len);
+		rule_set_sz += entry->hw_len;
+	}
+
+	if (rule_set_sz) {
+		tbl->sz = rule_set_sz + IPA_FLT_TABLE_WORD_SIZE;
+		/* this rule-set uses a word in header block */
+		*hdr_sz += IPA_FLT_TABLE_WORD_SIZE;
+		if (!tbl->in_sys) {
+			/* add the terminator */
+			total_sz += (rule_set_sz + IPA_FLT_TABLE_WORD_SIZE);
+			total_sz = (total_sz +
+					IPA_FLT_ENTRY_MEMORY_ALLIGNMENT) &
+					~IPA_FLT_ENTRY_MEMORY_ALLIGNMENT;
+		}
+	}
+
+	for (i = 0; i < IPA_NUM_PIPES; i++) {
+		tbl = &ipa_ctx->flt_tbl[i][ip];
+		rule_set_sz = 0;
+		list_for_each_entry(entry, &tbl->head_flt_rule_list, link) {
+			if (ipa_generate_flt_hw_rule(ip, entry, NULL)) {
+				IPAERR("failed to find HW FLT rule size\n");
+				return -EPERM;
+			}
+			IPADBG("pipe %d len %d\n", i, entry->hw_len);
+			rule_set_sz += entry->hw_len;
+		}
+
+		if (rule_set_sz) {
+			tbl->sz = rule_set_sz + IPA_FLT_TABLE_WORD_SIZE;
+			/* this rule-set uses a word in header block */
+			*hdr_sz += IPA_FLT_TABLE_WORD_SIZE;
+			if (!tbl->in_sys) {
+				/* add the terminator */
+				total_sz += (rule_set_sz +
+					    IPA_FLT_TABLE_WORD_SIZE);
+				total_sz = (total_sz +
+					IPA_FLT_ENTRY_MEMORY_ALLIGNMENT) &
+					~IPA_FLT_ENTRY_MEMORY_ALLIGNMENT;
+			}
+		}
+	}
+
+	*hdr_sz += IPA_FLT_TABLE_WORD_SIZE;
+	total_sz += *hdr_sz;
+	IPADBG("FLT HW TBL SZ %d HDR SZ %d IP %d\n", total_sz, *hdr_sz, ip);
+
+	return total_sz;
+}
+
+/**
+ * ipa_generate_flt_hw_tbl() - generates the filtering hardware table
+ * @ip:	[in] the ip address family type
+ * @mem:	[out] buffer to put the filtering table
+ *
+ * Returns:	0 on success, negative on failure
+ */
+int ipa_generate_flt_hw_tbl(enum ipa_ip_type ip, struct ipa_mem_buffer *mem)
+{
+	struct ipa_flt_tbl *tbl;
+	struct ipa_flt_entry *entry;
+	u32 hdr_top = 0;
+	int i;
+	u32 hdr_sz;
+	u32 offset;
+	u8 *hdr;
+	u8 *body;
+	u8 *base;
+	struct ipa_mem_buffer flt_tbl_mem;
+	u8 *ftbl_membody;
+
+	mem->size = ipa_get_flt_hw_tbl_size(ip, &hdr_sz);
+	mem->size = IPA_HW_TABLE_ALIGNMENT(mem->size);
+
+	if (mem->size == 0) {
+		IPAERR("flt tbl empty ip=%d\n", ip);
+		goto error;
+	}
+	mem->base = dma_alloc_coherent(NULL, mem->size, &mem->phys_base,
+			GFP_KERNEL);
+	if (!mem->base) {
+		IPAERR("fail to alloc DMA buff of size %d\n", mem->size);
+		goto error;
+	}
+
+	memset(mem->base, 0, mem->size);
+
+	/* build the flt tbl in the DMA buffer to submit to IPA HW */
+	base = hdr = (u8 *)mem->base;
+	body = base + hdr_sz;
+
+	/* write a dummy header to move cursor */
+	hdr = ipa_write_32(hdr_top, hdr);
+
+	tbl = &ipa_ctx->glob_flt_tbl[ip];
+
+	if (!list_empty(&tbl->head_flt_rule_list)) {
+		hdr_top |= IPA_FLT_BIT_MASK;
+		if (!tbl->in_sys) {
+			offset = body - base;
+			if (offset & IPA_FLT_ENTRY_MEMORY_ALLIGNMENT) {
+				IPAERR("offset is not word multiple %d\n",
+						offset);
+				goto proc_err;
+			}
+
+			offset &= ~IPA_FLT_ENTRY_MEMORY_ALLIGNMENT;
+			/* rule is at an offset from base */
+			offset |= IPA_FLT_BIT_MASK;
+			hdr = ipa_write_32(offset, hdr);
+
+			/* generate the rule-set */
+			list_for_each_entry(entry, &tbl->head_flt_rule_list,
+					link) {
+				if (ipa_generate_flt_hw_rule(ip, entry, body)) {
+					IPAERR("failed to gen HW FLT rule\n");
+					goto proc_err;
+				}
+				body += entry->hw_len;
+			}
+
+			/* write the rule-set terminator */
+			body = ipa_write_32(0, body);
+			if ((u32)body & IPA_FLT_ENTRY_MEMORY_ALLIGNMENT)
+				/* advance body to next word boundary */
+				body = body + (IPA_FLT_TABLE_WORD_SIZE -
+					((u32)body &
+					IPA_FLT_ENTRY_MEMORY_ALLIGNMENT));
+		} else {
+			WARN_ON(tbl->sz == 0);
+			/* allocate memory for the flt tbl */
+			flt_tbl_mem.size = tbl->sz;
+			flt_tbl_mem.base =
+			   dma_alloc_coherent(NULL, flt_tbl_mem.size,
+					   &flt_tbl_mem.phys_base, GFP_KERNEL);
+			if (!flt_tbl_mem.base) {
+				IPAERR("fail to alloc DMA buff of size %d\n",
+						flt_tbl_mem.size);
+				WARN_ON(1);
+				goto proc_err;
+			}
+
+			WARN_ON(flt_tbl_mem.phys_base &
+				IPA_FLT_ENTRY_MEMORY_ALLIGNMENT);
+			ftbl_membody = flt_tbl_mem.base;
+			memset(flt_tbl_mem.base, 0, flt_tbl_mem.size);
+			hdr = ipa_write_32(flt_tbl_mem.phys_base, hdr);
+
+			/* generate the rule-set */
+			list_for_each_entry(entry, &tbl->head_flt_rule_list,
+					link) {
+				if (ipa_generate_flt_hw_rule(ip, entry,
+							ftbl_membody)) {
+					IPAERR("failed to gen HW FLT rule\n");
+					WARN_ON(1);
+				}
+				ftbl_membody += entry->hw_len;
+			}
+
+			/* write the rule-set terminator */
+			ftbl_membody = ipa_write_32(0, ftbl_membody);
+			if (tbl->curr_mem.phys_base) {
+				WARN_ON(tbl->prev_mem.phys_base);
+				tbl->prev_mem = tbl->curr_mem;
+			}
+			tbl->curr_mem = flt_tbl_mem;
+		}
+	}
+
+	for (i = 0; i < IPA_NUM_PIPES; i++) {
+		tbl = &ipa_ctx->flt_tbl[i][ip];
+		if (!list_empty(&tbl->head_flt_rule_list)) {
+			/* pipe "i" is at bit "i+1" */
+			hdr_top |= (1 << (i + 1));
+			if (!tbl->in_sys) {
+				offset = body - base;
+				if (offset & IPA_FLT_ENTRY_MEMORY_ALLIGNMENT) {
+					IPAERR("ofst is not word multiple %d\n",
+					       offset);
+					goto proc_err;
+				}
+				offset &= ~IPA_FLT_ENTRY_MEMORY_ALLIGNMENT;
+				/* rule is at an offset from base */
+				offset |= IPA_FLT_BIT_MASK;
+				hdr = ipa_write_32(offset, hdr);
+
+				/* generate the rule-set */
+				list_for_each_entry(entry,
+						&tbl->head_flt_rule_list,
+						link) {
+					if (ipa_generate_flt_hw_rule(ip, entry,
+								body)) {
+						IPAERR("fail gen FLT rule\n");
+						goto proc_err;
+					}
+					body += entry->hw_len;
+				}
+
+				/* write the rule-set terminator */
+				body = ipa_write_32(0, body);
+				if ((u32)body & IPA_FLT_ENTRY_MEMORY_ALLIGNMENT)
+					/* advance body to next word boundary */
+					body = body + (IPA_FLT_TABLE_WORD_SIZE -
+						((u32)body &
+					IPA_FLT_ENTRY_MEMORY_ALLIGNMENT));
+			} else {
+				WARN_ON(tbl->sz == 0);
+				/* allocate memory for the flt tbl */
+				flt_tbl_mem.size = tbl->sz;
+				flt_tbl_mem.base =
+				   dma_alloc_coherent(NULL, flt_tbl_mem.size,
+						   &flt_tbl_mem.phys_base,
+						   GFP_KERNEL);
+				if (!flt_tbl_mem.base) {
+					IPAERR("fail alloc DMA buff size %d\n",
+							flt_tbl_mem.size);
+					WARN_ON(1);
+					goto proc_err;
+				}
+
+				WARN_ON(flt_tbl_mem.phys_base &
+				IPA_FLT_ENTRY_MEMORY_ALLIGNMENT);
+
+				ftbl_membody = flt_tbl_mem.base;
+				memset(flt_tbl_mem.base, 0, flt_tbl_mem.size);
+				hdr = ipa_write_32(flt_tbl_mem.phys_base, hdr);
+
+				/* generate the rule-set */
+				list_for_each_entry(entry,
+						&tbl->head_flt_rule_list,
+						link) {
+					if (ipa_generate_flt_hw_rule(ip, entry,
+							ftbl_membody)) {
+						IPAERR("fail gen FLT rule\n");
+						WARN_ON(1);
+					}
+					ftbl_membody += entry->hw_len;
+				}
+
+				/* write the rule-set terminator */
+				ftbl_membody =
+					ipa_write_32(0, ftbl_membody);
+				if (tbl->curr_mem.phys_base) {
+					WARN_ON(tbl->prev_mem.phys_base);
+					tbl->prev_mem = tbl->curr_mem;
+				}
+				tbl->curr_mem = flt_tbl_mem;
+			}
+		}
+	}
+
+	/* now write the hdr_top */
+	ipa_write_32(hdr_top, base);
+
+	return 0;
+proc_err:
+	dma_free_coherent(NULL, mem->size, mem->base, mem->phys_base);
+error:
+
+	return -EPERM;
+}
+
+static void __ipa_reap_sys_flt_tbls(enum ipa_ip_type ip)
+{
+	struct ipa_flt_tbl *tbl;
+	int i;
+
+	tbl = &ipa_ctx->glob_flt_tbl[ip];
+	if (tbl->prev_mem.phys_base) {
+		IPADBG("reaping glob flt tbl (prev) ip=%d\n", ip);
+		dma_free_coherent(NULL, tbl->prev_mem.size, tbl->prev_mem.base,
+				tbl->prev_mem.phys_base);
+		memset(&tbl->prev_mem, 0, sizeof(tbl->prev_mem));
+	}
+
+	if (list_empty(&tbl->head_flt_rule_list)) {
+		if (tbl->curr_mem.phys_base) {
+			IPADBG("reaping glob flt tbl (curr) ip=%d\n", ip);
+			dma_free_coherent(NULL, tbl->curr_mem.size,
+					tbl->curr_mem.base,
+					tbl->curr_mem.phys_base);
+			memset(&tbl->curr_mem, 0, sizeof(tbl->curr_mem));
+		}
+	}
+
+	for (i = 0; i < IPA_NUM_PIPES; i++) {
+		tbl = &ipa_ctx->flt_tbl[i][ip];
+		if (tbl->prev_mem.phys_base) {
+			IPADBG("reaping flt tbl (prev) pipe=%d ip=%d\n", i, ip);
+			dma_free_coherent(NULL, tbl->prev_mem.size,
+					tbl->prev_mem.base,
+					tbl->prev_mem.phys_base);
+			memset(&tbl->prev_mem, 0, sizeof(tbl->prev_mem));
+		}
+
+		if (list_empty(&tbl->head_flt_rule_list)) {
+			if (tbl->curr_mem.phys_base) {
+				IPADBG("reaping flt tbl (curr) pipe=%d ip=%d\n",
+						i, ip);
+				dma_free_coherent(NULL, tbl->curr_mem.size,
+						tbl->curr_mem.base,
+						tbl->curr_mem.phys_base);
+				memset(&tbl->curr_mem, 0,
+						sizeof(tbl->curr_mem));
+			}
+		}
+	}
+}
+
+static int __ipa_commit_flt(enum ipa_ip_type ip)
+{
+	struct ipa_desc desc = { 0 };
+	struct ipa_mem_buffer *mem;
+	void *cmd;
+	struct ipa_ip_v4_filter_init *v4;
+	struct ipa_ip_v6_filter_init *v6;
+	u16 avail;
+	u16 size;
+
+	mem = kmalloc(sizeof(struct ipa_mem_buffer), GFP_KERNEL);
+	if (!mem) {
+		IPAERR("failed to alloc memory object\n");
+		goto fail_alloc_mem;
+	}
+
+	if (ip == IPA_IP_v4) {
+		avail = IPA_RAM_V4_FLT_SIZE;
+		size = sizeof(struct ipa_ip_v4_filter_init);
+	} else {
+		avail = IPA_RAM_V6_FLT_SIZE;
+		size = sizeof(struct ipa_ip_v6_filter_init);
+	}
+	cmd = kmalloc(size, GFP_KERNEL);
+	if (!cmd) {
+		IPAERR("failed to alloc immediate command object\n");
+		goto fail_alloc_cmd;
+	}
+
+	if (ipa_generate_flt_hw_tbl(ip, mem)) {
+		IPAERR("fail to generate FLT HW TBL ip %d\n", ip);
+		goto fail_hw_tbl_gen;
+	}
+
+	if (mem->size > avail) {
+		IPAERR("tbl too big, needed %d avail %d\n", mem->size, avail);
+		goto fail_hw_tbl_gen;
+	}
+
+	if (ip == IPA_IP_v4) {
+		v4 = (struct ipa_ip_v4_filter_init *)cmd;
+		desc.opcode = IPA_IP_V4_FILTER_INIT;
+		v4->ipv4_rules_addr = mem->phys_base;
+		v4->size_ipv4_rules = mem->size;
+		v4->ipv4_addr = IPA_RAM_V4_FLT_OFST;
+	} else {
+		v6 = (struct ipa_ip_v6_filter_init *)cmd;
+		desc.opcode = IPA_IP_V6_FILTER_INIT;
+		v6->ipv6_rules_addr = mem->phys_base;
+		v6->size_ipv6_rules = mem->size;
+		v6->ipv6_addr = IPA_RAM_V6_FLT_OFST;
+	}
+
+	desc.pyld = cmd;
+	desc.len = size;
+	desc.type = IPA_IMM_CMD_DESC;
+	IPA_DUMP_BUFF(mem->base, mem->phys_base, mem->size);
+
+	if (ipa_send_cmd(1, &desc)) {
+		IPAERR("fail to send immediate command\n");
+		goto fail_send_cmd;
+	}
+
+	__ipa_reap_sys_flt_tbls(ip);
+	dma_free_coherent(NULL, mem->size, mem->base, mem->phys_base);
+	kfree(cmd);
+	kfree(mem);
+
+	return 0;
+
+fail_send_cmd:
+	if (mem->phys_base)
+		dma_free_coherent(NULL, mem->size, mem->base, mem->phys_base);
+fail_hw_tbl_gen:
+	kfree(cmd);
+fail_alloc_cmd:
+	kfree(mem);
+fail_alloc_mem:
+
+	return -EPERM;
+}
+
+static int __ipa_add_flt_rule(struct ipa_flt_tbl *tbl, enum ipa_ip_type ip,
+			      const struct ipa_flt_rule *rule, u8 add_rear,
+			      u32 *rule_hdl)
+{
+	struct ipa_flt_entry *entry;
+	struct ipa_tree_node *node;
+
+	if (!rule->rt_tbl_hdl) {
+		IPAERR("flt rule does not point to valid RT tbl\n");
+		goto error;
+	}
+
+	if (ipa_search(&ipa_ctx->rt_tbl_hdl_tree, rule->rt_tbl_hdl) == NULL) {
+		IPAERR("RT tbl not found\n");
+		goto error;
+	}
+
+	if (((struct ipa_rt_tbl *)rule->rt_tbl_hdl)->cookie != IPA_COOKIE) {
+		IPAERR("flt rule cookie is invalid\n");
+		goto error;
+	}
+
+	node = kmem_cache_zalloc(ipa_ctx->tree_node_cache, GFP_KERNEL);
+	if (!node) {
+		IPAERR("failed to alloc tree node object\n");
+		goto error;
+	}
+
+	entry = kmem_cache_zalloc(ipa_ctx->flt_rule_cache, GFP_KERNEL);
+	if (!entry) {
+		IPAERR("failed to alloc FLT rule object\n");
+		goto mem_alloc_fail;
+	}
+	INIT_LIST_HEAD(&entry->link);
+	entry->rule = *rule;
+	entry->cookie = IPA_COOKIE;
+	entry->rt_tbl = (struct ipa_rt_tbl *)rule->rt_tbl_hdl;
+	entry->tbl = tbl;
+	if (add_rear)
+		list_add_tail(&entry->link, &tbl->head_flt_rule_list);
+	else
+		list_add(&entry->link, &tbl->head_flt_rule_list);
+	tbl->rule_cnt++;
+	entry->rt_tbl->ref_cnt++;
+	*rule_hdl = (u32)entry;
+	IPADBG("add flt rule rule_cnt=%d\n", tbl->rule_cnt);
+
+	node->hdl = *rule_hdl;
+	if (ipa_insert(&ipa_ctx->flt_rule_hdl_tree, node)) {
+		IPAERR("failed to add to tree\n");
+		WARN_ON(1);
+	}
+
+	return 0;
+
+mem_alloc_fail:
+	kmem_cache_free(ipa_ctx->tree_node_cache, node);
+error:
+
+	return -EPERM;
+}
+
+static int __ipa_del_flt_rule(u32 rule_hdl)
+{
+	struct ipa_flt_entry *entry = (struct ipa_flt_entry *)rule_hdl;
+	struct ipa_tree_node *node;
+
+	if (entry == NULL || (entry->cookie != IPA_COOKIE)) {
+		IPAERR("bad params\n");
+
+		return -EINVAL;
+	}
+	node = ipa_search(&ipa_ctx->flt_rule_hdl_tree, rule_hdl);
+	if (node == NULL) {
+		IPAERR("lookup failed\n");
+
+		return -EPERM;
+	}
+	list_del(&entry->link);
+	entry->tbl->rule_cnt--;
+	entry->rt_tbl->ref_cnt--;
+	IPADBG("del flt rule rule_cnt=%d\n", entry->tbl->rule_cnt);
+	entry->cookie = 0;
+	kmem_cache_free(ipa_ctx->flt_rule_cache, entry);
+
+	/* remove the handle from the database */
+	rb_erase(&node->node, &ipa_ctx->flt_rule_hdl_tree);
+	kmem_cache_free(ipa_ctx->tree_node_cache, node);
+
+	return 0;
+}
+
+static int __ipa_add_global_flt_rule(enum ipa_ip_type ip,
+		const struct ipa_flt_rule *rule, u8 add_rear, u32 *rule_hdl)
+{
+	struct ipa_flt_tbl *tbl;
+
+	tbl = &ipa_ctx->glob_flt_tbl[ip];
+	IPADBG("add global flt rule ip=%d\n", ip);
+
+	return __ipa_add_flt_rule(tbl, ip, rule, add_rear, rule_hdl);
+}
+
+static int __ipa_add_ep_flt_rule(enum ipa_ip_type ip, enum ipa_client_type ep,
+				 const struct ipa_flt_rule *rule, u8 add_rear,
+				 u32 *rule_hdl)
+{
+	struct ipa_flt_tbl *tbl;
+	int ipa_ep_idx;
+
+	if (ip >= IPA_IP_MAX || rule == NULL || rule_hdl == NULL ||
+			ep >= IPA_CLIENT_MAX) {
+		IPAERR("bad parms\n");
+
+		return -EINVAL;
+	}
+	ipa_ep_idx = ipa_get_ep_mapping(ipa_ctx->mode, ep);
+	if (ipa_ep_idx == IPA_FLT_TABLE_INDEX_NOT_FOUND ||
+				ipa_ctx->ep[ipa_ep_idx].valid == 0) {
+		IPAERR("bad parms\n");
+
+		return -EINVAL;
+	}
+	tbl = &ipa_ctx->flt_tbl[ipa_ep_idx][ip];
+	IPADBG("add ep flt rule ip=%d ep=%d\n", ip, ep);
+
+	return __ipa_add_flt_rule(tbl, ip, rule, add_rear, rule_hdl);
+}
+
+/**
+ * ipa_add_flt_rule() - Add the specified filtering rules to SW and optionally
+ * commit to IPA HW
+ *
+ * Returns:	0 on success, negative on failure
+ *
+ * Note:	Should not be called from atomic context
+ */
+int ipa_add_flt_rule(struct ipa_ioc_add_flt_rule *rules)
+{
+	int i;
+	int result;
+
+	if (rules == NULL || rules->num_rules == 0 ||
+			rules->ip >= IPA_IP_MAX) {
+		IPAERR("bad parm\n");
+
+		return -EINVAL;
+	}
+
+	mutex_lock(&ipa_ctx->lock);
+	for (i = 0; i < rules->num_rules; i++) {
+		if (rules->global)
+			result = __ipa_add_global_flt_rule(rules->ip,
+					&rules->rules[i].rule,
+					rules->rules[i].at_rear,
+					&rules->rules[i].flt_rule_hdl);
+		else
+			result = __ipa_add_ep_flt_rule(rules->ip, rules->ep,
+					&rules->rules[i].rule,
+					rules->rules[i].at_rear,
+					&rules->rules[i].flt_rule_hdl);
+		if (result) {
+			IPAERR("failed to add flt rule %d\n", i);
+			rules->rules[i].status = IPA_FLT_STATUS_OF_ADD_FAILED;
+		} else {
+			rules->rules[i].status = 0;
+		}
+	}
+
+	if (rules->commit)
+		if (__ipa_commit_flt(rules->ip)) {
+			result = -EPERM;
+			goto bail;
+		}
+	result = 0;
+bail:
+	mutex_unlock(&ipa_ctx->lock);
+
+	return result;
+}
+EXPORT_SYMBOL(ipa_add_flt_rule);
+
+/**
+ * ipa_del_flt_rule() - Remove the specified filtering rules from SW and
+ * optionally commit to IPA HW
+ *
+ * Returns:	0 on success, negative on failure
+ *
+ * Note:	Should not be called from atomic context
+ */
+int ipa_del_flt_rule(struct ipa_ioc_del_flt_rule *hdls)
+{
+	int i;
+	int result;
+
+	if (hdls == NULL || hdls->num_hdls == 0 || hdls->ip >= IPA_IP_MAX) {
+		IPAERR("bad parm\n");
+
+		return -EINVAL;
+	}
+
+	mutex_lock(&ipa_ctx->lock);
+	for (i = 0; i < hdls->num_hdls; i++) {
+		if (__ipa_del_flt_rule(hdls->hdl[i].hdl)) {
+			IPAERR("failed to del rt rule %i\n", i);
+			hdls->hdl[i].status = IPA_FLT_STATUS_OF_DEL_FAILED;
+		} else {
+			hdls->hdl[i].status = 0;
+		}
+	}
+
+	if (hdls->commit)
+		if (__ipa_commit_flt(hdls->ip)) {
+			mutex_unlock(&ipa_ctx->lock);
+			result = -EPERM;
+			goto bail;
+		}
+	result = 0;
+bail:
+	mutex_unlock(&ipa_ctx->lock);
+
+	return result;
+}
+EXPORT_SYMBOL(ipa_del_flt_rule);
+
+/**
+ * ipa_commit_flt() - Commit the current SW filtering table of specified type to
+ * IPA HW
+ * @ip:	[in] the family of routing tables
+ *
+ * Returns:	0 on success, negative on failure
+ *
+ * Note:	Should not be called from atomic context
+ */
+int ipa_commit_flt(enum ipa_ip_type ip)
+{
+	int result;
+
+	mutex_lock(&ipa_ctx->lock);
+
+	if (__ipa_commit_flt(ip)) {
+		result = -EPERM;
+		goto bail;
+	}
+	result = 0;
+
+bail:
+	mutex_unlock(&ipa_ctx->lock);
+
+	return result;
+}
+EXPORT_SYMBOL(ipa_commit_flt);
+
+/**
+ * ipa_reset_flt() - Reset the current SW filtering table of specified type
+ * (does not commit to HW)
+ * @ip:	[in] the family of routing tables
+ *
+ * Returns:	0 on success, negative on failure
+ *
+ * Note:	Should not be called from atomic context
+ */
+int ipa_reset_flt(enum ipa_ip_type ip)
+{
+	struct ipa_flt_tbl *tbl;
+	struct ipa_flt_entry *entry;
+	struct ipa_flt_entry *next;
+	struct ipa_tree_node *node;
+	int i;
+
+	tbl = &ipa_ctx->glob_flt_tbl[ip];
+	mutex_lock(&ipa_ctx->lock);
+	IPADBG("reset flt ip=%d\n", ip);
+	list_for_each_entry_safe(entry, next, &tbl->head_flt_rule_list, link) {
+		node = ipa_search(&ipa_ctx->flt_rule_hdl_tree, (u32)entry);
+		if (node == NULL)
+			WARN_ON(1);
+		list_del(&entry->link);
+		entry->tbl->rule_cnt--;
+		entry->rt_tbl->ref_cnt--;
+		entry->cookie = 0;
+		kmem_cache_free(ipa_ctx->flt_rule_cache, entry);
+
+		/* remove the handle from the database */
+		rb_erase(&node->node, &ipa_ctx->flt_rule_hdl_tree);
+		kmem_cache_free(ipa_ctx->tree_node_cache, node);
+	}
+
+	for (i = 0; i < IPA_NUM_PIPES; i++) {
+		tbl = &ipa_ctx->flt_tbl[i][ip];
+		list_for_each_entry_safe(entry, next, &tbl->head_flt_rule_list,
+				link) {
+			node = ipa_search(&ipa_ctx->flt_rule_hdl_tree,
+					(u32)entry);
+			if (node == NULL)
+				WARN_ON(1);
+			list_del(&entry->link);
+			entry->tbl->rule_cnt--;
+			entry->rt_tbl->ref_cnt--;
+			entry->cookie = 0;
+			kmem_cache_free(ipa_ctx->flt_rule_cache, entry);
+
+			/* remove the handle from the database */
+			rb_erase(&node->node, &ipa_ctx->flt_rule_hdl_tree);
+			kmem_cache_free(ipa_ctx->tree_node_cache, node);
+		}
+	}
+	mutex_unlock(&ipa_ctx->lock);
+
+	return 0;
+}
+EXPORT_SYMBOL(ipa_reset_flt);
diff --git a/drivers/platform/msm/ipa/ipa_hdr.c b/drivers/platform/msm/ipa/ipa_hdr.c
new file mode 100644
index 0000000..4b9a500
--- /dev/null
+++ b/drivers/platform/msm/ipa/ipa_hdr.c
@@ -0,0 +1,614 @@
+/* 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 "ipa_i.h"
+
+static const u32 ipa_hdr_bin_sz[IPA_HDR_BIN_MAX] = { 8, 16, 32, 64 };
+
+/**
+ * ipa_generate_hdr_hw_tbl() - generates the headers table
+ * @mem:	[out] buffer to put the header table
+ *
+ * Returns:	0 on success, negative on failure
+ */
+int ipa_generate_hdr_hw_tbl(struct ipa_mem_buffer *mem)
+{
+	struct ipa_hdr_entry *entry;
+
+	mem->size = ipa_ctx->hdr_tbl.end;
+
+	if (mem->size == 0) {
+		IPAERR("hdr tbl empty\n");
+		return -EPERM;
+	}
+	IPADBG("tbl_sz=%d\n", ipa_ctx->hdr_tbl.end);
+
+	mem->base = dma_alloc_coherent(NULL, mem->size, &mem->phys_base,
+			GFP_KERNEL);
+	if (!mem->base) {
+		IPAERR("fail to alloc DMA buff of size %d\n", mem->size);
+		return -ENOMEM;
+	}
+
+	memset(mem->base, 0, mem->size);
+	list_for_each_entry(entry, &ipa_ctx->hdr_tbl.head_hdr_entry_list,
+			link) {
+		IPADBG("hdr of len %d ofst=%d\n", entry->hdr_len,
+				entry->offset_entry->offset);
+		memcpy(mem->base + entry->offset_entry->offset, entry->hdr,
+				entry->hdr_len);
+	}
+
+	return 0;
+}
+
+/*
+ * __ipa_commit_hdr() commits hdr to hardware
+ * This function needs to be called with a locked mutex.
+ */
+static int __ipa_commit_hdr(void)
+{
+	struct ipa_desc desc = { 0 };
+	struct ipa_mem_buffer *mem;
+	struct ipa_hdr_init_local *cmd;
+	u16 len;
+
+	mem = kmalloc(sizeof(struct ipa_mem_buffer), GFP_KERNEL);
+	if (!mem) {
+		IPAERR("failed to alloc memory object\n");
+		goto fail_alloc_mem;
+	}
+
+	/* the immediate command param size is same for both local and system */
+	len = sizeof(struct ipa_hdr_init_local);
+
+	/*
+	 * we can use init_local ptr for init_system due to layout of the
+	 * struct
+	 */
+	cmd = kmalloc(len, GFP_KERNEL);
+	if (!cmd) {
+		IPAERR("failed to alloc immediate command object\n");
+		goto fail_alloc_cmd;
+	}
+
+	if (ipa_generate_hdr_hw_tbl(mem)) {
+		IPAERR("fail to generate HDR HW TBL\n");
+		goto fail_hw_tbl_gen;
+	}
+
+	if (ipa_ctx->hdr_tbl_lcl && mem->size > IPA_RAM_HDR_SIZE) {
+		IPAERR("tbl too big, needed %d avail %d\n", mem->size,
+				IPA_RAM_HDR_SIZE);
+		goto fail_hw_tbl_gen;
+	}
+
+	cmd->hdr_table_addr = mem->phys_base;
+	if (ipa_ctx->hdr_tbl_lcl) {
+		cmd->size_hdr_table = mem->size;
+		cmd->hdr_addr = IPA_RAM_HDR_OFST;
+		desc.opcode = IPA_HDR_INIT_LOCAL;
+	} else {
+		desc.opcode = IPA_HDR_INIT_SYSTEM;
+	}
+	desc.pyld = cmd;
+	desc.len = sizeof(struct ipa_hdr_init_local);
+	desc.type = IPA_IMM_CMD_DESC;
+	IPA_DUMP_BUFF(mem->base, mem->phys_base, mem->size);
+
+	if (ipa_send_cmd(1, &desc)) {
+		IPAERR("fail to send immediate command\n");
+		goto fail_send_cmd;
+	}
+
+	if (ipa_ctx->hdr_tbl_lcl) {
+		dma_free_coherent(NULL, mem->size, mem->base, mem->phys_base);
+	} else {
+		if (ipa_ctx->hdr_mem.phys_base) {
+			dma_free_coherent(NULL, ipa_ctx->hdr_mem.size,
+					  ipa_ctx->hdr_mem.base,
+					  ipa_ctx->hdr_mem.phys_base);
+		}
+		ipa_ctx->hdr_mem = *mem;
+	}
+	kfree(cmd);
+	kfree(mem);
+
+	return 0;
+
+fail_send_cmd:
+	if (mem->phys_base)
+		dma_free_coherent(NULL, mem->size, mem->base, mem->phys_base);
+fail_hw_tbl_gen:
+	kfree(cmd);
+fail_alloc_cmd:
+	kfree(mem);
+fail_alloc_mem:
+
+	return -EPERM;
+}
+
+static int __ipa_add_hdr(struct ipa_hdr_add *hdr)
+{
+	struct ipa_hdr_entry *entry;
+	struct ipa_hdr_offset_entry *offset;
+	struct ipa_tree_node *node;
+	u32 bin;
+	struct ipa_hdr_tbl *htbl = &ipa_ctx->hdr_tbl;
+
+	if (hdr->hdr_len == 0) {
+		IPAERR("bad parm\n");
+		goto error;
+	}
+
+	node = kmem_cache_zalloc(ipa_ctx->tree_node_cache, GFP_KERNEL);
+	if (!node) {
+		IPAERR("failed to alloc tree node object\n");
+		goto error;
+	}
+
+	entry = kmem_cache_zalloc(ipa_ctx->hdr_cache, GFP_KERNEL);
+	if (!entry) {
+		IPAERR("failed to alloc hdr object\n");
+		goto hdr_alloc_fail;
+	}
+
+	INIT_LIST_HEAD(&entry->link);
+
+	memcpy(entry->hdr, hdr->hdr, hdr->hdr_len);
+	entry->hdr_len = hdr->hdr_len;
+	strlcpy(entry->name, hdr->name, IPA_RESOURCE_NAME_MAX);
+	entry->is_partial = hdr->is_partial;
+	entry->cookie = IPA_COOKIE;
+
+	if (hdr->hdr_len <= ipa_hdr_bin_sz[IPA_HDR_BIN0])
+		bin = IPA_HDR_BIN0;
+	else if (hdr->hdr_len <= ipa_hdr_bin_sz[IPA_HDR_BIN1])
+		bin = IPA_HDR_BIN1;
+	else if (hdr->hdr_len <= ipa_hdr_bin_sz[IPA_HDR_BIN2])
+		bin = IPA_HDR_BIN2;
+	else if (hdr->hdr_len <= ipa_hdr_bin_sz[IPA_HDR_BIN3])
+		bin = IPA_HDR_BIN3;
+	else {
+		IPAERR("unexpected hdr len %d\n", hdr->hdr_len);
+		goto bad_hdr_len;
+	}
+
+	if (list_empty(&htbl->head_free_offset_list[bin])) {
+		offset = kmem_cache_zalloc(ipa_ctx->hdr_offset_cache,
+					   GFP_KERNEL);
+		if (!offset) {
+			IPAERR("failed to alloc hdr offset object\n");
+			goto ofst_alloc_fail;
+		}
+		INIT_LIST_HEAD(&offset->link);
+		/*
+		 * for a first item grow, set the bin and offset which are set
+		 * in stone
+		 */
+		offset->offset = htbl->end;
+		offset->bin = bin;
+		htbl->end += ipa_hdr_bin_sz[bin];
+		list_add(&offset->link,
+				&htbl->head_offset_list[bin]);
+	} else {
+		/* get the first free slot */
+		offset =
+		    list_first_entry(&htbl->head_free_offset_list[bin],
+				     struct ipa_hdr_offset_entry, link);
+		list_move(&offset->link, &htbl->head_offset_list[bin]);
+	}
+
+	entry->offset_entry = offset;
+	list_add(&entry->link, &htbl->head_hdr_entry_list);
+	htbl->hdr_cnt++;
+	IPADBG("add hdr of sz=%d hdr_cnt=%d ofst=%d\n", hdr->hdr_len,
+			htbl->hdr_cnt, offset->offset);
+
+	hdr->hdr_hdl = (u32) entry;
+	node->hdl = hdr->hdr_hdl;
+	if (ipa_insert(&ipa_ctx->hdr_hdl_tree, node)) {
+		IPAERR("failed to add to tree\n");
+		WARN_ON(1);
+	}
+
+	return 0;
+
+ofst_alloc_fail:
+	kmem_cache_free(ipa_ctx->hdr_offset_cache, offset);
+bad_hdr_len:
+	entry->cookie = 0;
+	kmem_cache_free(ipa_ctx->hdr_cache, entry);
+hdr_alloc_fail:
+	kmem_cache_free(ipa_ctx->tree_node_cache, node);
+error:
+	return -EPERM;
+}
+
+static int __ipa_del_hdr(u32 hdr_hdl)
+{
+	struct ipa_hdr_entry *entry = (struct ipa_hdr_entry *)hdr_hdl;
+	struct ipa_tree_node *node;
+	struct ipa_hdr_tbl *htbl = &ipa_ctx->hdr_tbl;
+
+	if (!entry || (entry->cookie != IPA_COOKIE) || (entry->ref_cnt != 0)) {
+		IPAERR("bad parm\n");
+		return -EINVAL;
+	}
+	node = ipa_search(&ipa_ctx->hdr_hdl_tree, hdr_hdl);
+	if (node == NULL) {
+		IPAERR("lookup failed\n");
+		return -EPERM;
+	}
+
+	IPADBG("del hdr of sz=%d hdr_cnt=%d ofst=%d\n", entry->hdr_len,
+			htbl->hdr_cnt, entry->offset_entry->offset);
+
+	/* move the offset entry to appropriate free list */
+	list_move(&entry->offset_entry->link,
+		  &htbl->head_free_offset_list[entry->offset_entry->bin]);
+	list_del(&entry->link);
+	htbl->hdr_cnt--;
+	entry->cookie = 0;
+	kmem_cache_free(ipa_ctx->hdr_cache, entry);
+
+	/* remove the handle from the database */
+	rb_erase(&node->node, &ipa_ctx->hdr_hdl_tree);
+	kmem_cache_free(ipa_ctx->tree_node_cache, node);
+
+	return 0;
+}
+
+/**
+ * ipa_add_hdr() - add the specified headers to SW and optionally commit them to
+ * IPA HW
+ * @hdrs:	[inout] set of headers to add
+ *
+ * Returns:	0 on success, negative on failure
+ *
+ * Note:	Should not be called from atomic context
+ */
+int ipa_add_hdr(struct ipa_ioc_add_hdr *hdrs)
+{
+	int i;
+	int result = -EFAULT;
+
+	if (hdrs == NULL || hdrs->num_hdrs == 0) {
+		IPAERR("bad parm\n");
+		return -EINVAL;
+	}
+
+	mutex_lock(&ipa_ctx->lock);
+	for (i = 0; i < hdrs->num_hdrs; i++) {
+		if (__ipa_add_hdr(&hdrs->hdr[i])) {
+			IPAERR("failed to add hdr %d\n", i);
+			hdrs->hdr[i].status = -1;
+		} else {
+			hdrs->hdr[i].status = 0;
+		}
+	}
+
+	if (hdrs->commit) {
+		if (__ipa_commit_hdr()) {
+			result = -EPERM;
+			goto bail;
+		}
+	}
+	result = 0;
+bail:
+	mutex_unlock(&ipa_ctx->lock);
+	return result;
+}
+EXPORT_SYMBOL(ipa_add_hdr);
+
+/**
+ * ipa_del_hdr() - Remove the specified headers from SW and optionally commit them
+ * to IPA HW
+ * @hdls:	[inout] set of headers to delete
+ *
+ * Returns:	0 on success, negative on failure
+ *
+ * Note:	Should not be called from atomic context
+ */
+int ipa_del_hdr(struct ipa_ioc_del_hdr *hdls)
+{
+	int i;
+	int result = -EFAULT;
+
+	if (hdls == NULL || hdls->num_hdls == 0) {
+		IPAERR("bad parm\n");
+		return -EINVAL;
+	}
+
+	mutex_lock(&ipa_ctx->lock);
+	for (i = 0; i < hdls->num_hdls; i++) {
+		if (__ipa_del_hdr(hdls->hdl[i].hdl)) {
+			IPAERR("failed to del hdr %i\n", i);
+			hdls->hdl[i].status = -1;
+		} else {
+			hdls->hdl[i].status = 0;
+		}
+	}
+
+	if (hdls->commit) {
+		if (__ipa_commit_hdr()) {
+			result = -EPERM;
+			goto bail;
+		}
+	}
+	result = 0;
+bail:
+	mutex_unlock(&ipa_ctx->lock);
+	return result;
+}
+EXPORT_SYMBOL(ipa_del_hdr);
+
+/**
+ * ipa_dump_hdr() - prints all the headers in the header table in SW
+ *
+ * Note:	Should not be called from atomic context
+ */
+void ipa_dump_hdr(void)
+{
+	struct ipa_hdr_entry *entry;
+
+	IPADBG("START\n");
+	mutex_lock(&ipa_ctx->lock);
+	list_for_each_entry(entry, &ipa_ctx->hdr_tbl.head_hdr_entry_list,
+			link) {
+		IPADBG("hdr_len=%4d off=%4d bin=%4d\n", entry->hdr_len,
+				entry->offset_entry->offset,
+				entry->offset_entry->bin);
+	}
+	mutex_unlock(&ipa_ctx->lock);
+	IPADBG("END\n");
+}
+
+/**
+ * ipa_commit_hdr() - commit to IPA HW the current header table in SW
+ *
+ * Returns:	0 on success, negative on failure
+ *
+ * Note:	Should not be called from atomic context
+ */
+int ipa_commit_hdr(void)
+{
+	int result = -EFAULT;
+
+	/*
+	 * issue a commit on the routing module since routing rules point to
+	 * header table entries
+	 */
+	if (ipa_commit_rt(IPA_IP_v4))
+		return -EPERM;
+	if (ipa_commit_rt(IPA_IP_v6))
+		return -EPERM;
+
+	mutex_lock(&ipa_ctx->lock);
+	if (__ipa_commit_hdr()) {
+		result = -EPERM;
+		goto bail;
+	}
+	result = 0;
+bail:
+	mutex_unlock(&ipa_ctx->lock);
+	return result;
+}
+EXPORT_SYMBOL(ipa_commit_hdr);
+
+/**
+ * ipa_reset_hdr() - reset the current header table in SW (does not commit to
+ * HW)
+ *
+ * Returns:	0 on success, negative on failure
+ *
+ * Note:	Should not be called from atomic context
+ */
+int ipa_reset_hdr(void)
+{
+	struct ipa_hdr_entry *entry;
+	struct ipa_hdr_entry *next;
+	struct ipa_hdr_offset_entry *off_entry;
+	struct ipa_hdr_offset_entry *off_next;
+	struct ipa_tree_node *node;
+	int i;
+
+	/*
+	 * issue a reset on the routing module since routing rules point to
+	 * header table entries
+	 */
+	if (ipa_reset_rt(IPA_IP_v4))
+		IPAERR("fail to reset v4 rt\n");
+	if (ipa_reset_rt(IPA_IP_v6))
+		IPAERR("fail to reset v4 rt\n");
+
+	mutex_lock(&ipa_ctx->lock);
+	IPADBG("reset hdr\n");
+	list_for_each_entry_safe(entry, next,
+			&ipa_ctx->hdr_tbl.head_hdr_entry_list, link) {
+
+		/* do not remove the default exception header */
+		if (!strncmp(entry->name, IPA_DFLT_HDR_NAME,
+					IPA_RESOURCE_NAME_MAX))
+			continue;
+
+		node = ipa_search(&ipa_ctx->hdr_hdl_tree, (u32) entry);
+		if (node == NULL)
+			WARN_ON(1);
+		list_del(&entry->link);
+		entry->cookie = 0;
+		kmem_cache_free(ipa_ctx->hdr_cache, entry);
+
+		/* remove the handle from the database */
+		rb_erase(&node->node, &ipa_ctx->hdr_hdl_tree);
+		kmem_cache_free(ipa_ctx->tree_node_cache, node);
+
+	}
+	for (i = 0; i < IPA_HDR_BIN_MAX; i++) {
+		list_for_each_entry_safe(off_entry, off_next,
+					 &ipa_ctx->hdr_tbl.head_offset_list[i],
+					 link) {
+
+			/*
+			 * do not remove the default exception header which is
+			 * at offset 0
+			 */
+			if (off_entry->offset == 0)
+				continue;
+
+			list_del(&off_entry->link);
+			kmem_cache_free(ipa_ctx->hdr_offset_cache, off_entry);
+		}
+		list_for_each_entry_safe(off_entry, off_next,
+				&ipa_ctx->hdr_tbl.head_free_offset_list[i],
+				link) {
+			list_del(&off_entry->link);
+			kmem_cache_free(ipa_ctx->hdr_offset_cache, off_entry);
+		}
+	}
+	/* there is one header of size 8 */
+	ipa_ctx->hdr_tbl.end = 8;
+	ipa_ctx->hdr_tbl.hdr_cnt = 1;
+	mutex_unlock(&ipa_ctx->lock);
+
+	return 0;
+}
+EXPORT_SYMBOL(ipa_reset_hdr);
+
+static struct ipa_hdr_entry *__ipa_find_hdr(const char *name)
+{
+	struct ipa_hdr_entry *entry;
+
+	list_for_each_entry(entry, &ipa_ctx->hdr_tbl.head_hdr_entry_list,
+			link) {
+		if (!strncmp(name, entry->name, IPA_RESOURCE_NAME_MAX))
+			return entry;
+	}
+
+	return NULL;
+}
+
+/**
+ * ipa_get_hdr() - Lookup the specified header resource
+ * @lookup:	[inout] header to lookup and its handle
+ *
+ * lookup the specified header resource and return handle if it exists, if
+ * lookup succeeds the header entry ref cnt is increased
+ *
+ * Returns:	0 on success, negative on failure
+ *
+ * Note:	Should not be called from atomic context
+ *		Caller should call ipa_put_hdr later if this function succeeds
+ */
+int ipa_get_hdr(struct ipa_ioc_get_hdr *lookup)
+{
+	struct ipa_hdr_entry *entry;
+	int result = -1;
+
+	if (lookup == NULL) {
+		IPAERR("bad parm\n");
+		return -EINVAL;
+	}
+	mutex_lock(&ipa_ctx->lock);
+	entry = __ipa_find_hdr(lookup->name);
+	if (entry) {
+		entry->ref_cnt++;
+		lookup->hdl = (uint32_t) entry;
+		result = 0;
+	}
+	mutex_unlock(&ipa_ctx->lock);
+
+	return result;
+}
+EXPORT_SYMBOL(ipa_get_hdr);
+
+/**
+ * ipa_put_hdr() - Release the specified header handle
+ * @hdr_hdl:	[in] the header handle to release
+ *
+ * Returns:	0 on success, negative on failure
+ *
+ * Note:	Should not be called from atomic context
+ */
+int ipa_put_hdr(u32 hdr_hdl)
+{
+	struct ipa_hdr_entry *entry = (struct ipa_hdr_entry *)hdr_hdl;
+	struct ipa_tree_node *node;
+	int result = -EFAULT;
+
+	if (entry == NULL || entry->cookie != IPA_COOKIE ||
+			entry->ref_cnt == 0) {
+		IPAERR("bad params\n");
+		return -EINVAL;
+	}
+	node = ipa_search(&ipa_ctx->hdr_hdl_tree, hdr_hdl);
+	if (node == NULL) {
+		IPAERR("lookup failed\n");
+		return -EPERM;
+	}
+	mutex_lock(&ipa_ctx->lock);
+	entry->ref_cnt--;
+	if (entry->ref_cnt == 0) {
+		if (__ipa_del_hdr(hdr_hdl)) {
+			IPAERR("fail to del hdr\n");
+			result = -EFAULT;
+			goto bail;
+		}
+		/* commit for put */
+		if (__ipa_commit_hdr()) {
+			IPAERR("fail to commit hdr\n");
+			result = -EFAULT;
+			goto bail;
+		}
+	}
+	result = 0;
+bail:
+	mutex_unlock(&ipa_ctx->lock);
+	return result;
+}
+EXPORT_SYMBOL(ipa_put_hdr);
+
+/**
+ * ipa_copy_hdr() - Lookup the specified header resource and return a copy of it
+ * @copy:	[inout] header to lookup and its copy
+ *
+ * lookup the specified header resource and return a copy of it (along with its
+ * attributes) if it exists, this would be called for partial headers
+ *
+ * Returns:	0 on success, negative on failure
+ *
+ * Note:	Should not be called from atomic context
+ */
+int ipa_copy_hdr(struct ipa_ioc_copy_hdr *copy)
+{
+	struct ipa_hdr_entry *entry;
+	int result = -EFAULT;
+
+	if (copy == NULL) {
+		IPAERR("bad parm\n");
+		return -EINVAL;
+	}
+	mutex_lock(&ipa_ctx->lock);
+	entry = __ipa_find_hdr(copy->name);
+	if (entry) {
+		memcpy(copy->hdr, entry->hdr, entry->hdr_len);
+		copy->hdr_len = entry->hdr_len;
+		copy->is_partial = entry->is_partial;
+		result = 0;
+	}
+	mutex_unlock(&ipa_ctx->lock);
+
+	return result;
+}
+EXPORT_SYMBOL(ipa_copy_hdr);
+
+
diff --git a/drivers/platform/msm/ipa/ipa_hw_defs.h b/drivers/platform/msm/ipa/ipa_hw_defs.h
new file mode 100644
index 0000000..3131a84
--- /dev/null
+++ b/drivers/platform/msm/ipa/ipa_hw_defs.h
@@ -0,0 +1,258 @@
+/* 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 _IPA_HW_DEFS_H
+#define _IPA_HW_DEFS_H
+#include <linux/bitops.h>
+
+/* This header defines various HW related data types */
+
+/* immediate command op-codes */
+#define IPA_DECIPH_INIT        (1)
+#define IPA_PPP_FRM_INIT       (2)
+#define IPA_IP_V4_FILTER_INIT  (3)
+#define IPA_IP_V6_FILTER_INIT  (4)
+#define IPA_IP_V4_NAT_INIT     (5)
+#define IPA_IP_V6_NAT_INIT     (6)
+#define IPA_IP_V4_ROUTING_INIT (7)
+#define IPA_IP_V6_ROUTING_INIT (8)
+#define IPA_HDR_INIT_LOCAL     (9)
+#define IPA_HDR_INIT_SYSTEM   (10)
+#define IPA_DECIPH_SETUP      (11)
+#define IPA_INSERT_NAT_RULE   (12)
+#define IPA_DELETE_NAT_RULE   (13)
+#define IPA_NAT_DMA           (14)
+#define IPA_IP_PACKET_TAG     (15)
+#define IPA_IP_PACKET_INIT    (16)
+
+#define IPA_INTERFACE_ID_EXCEPTION         (0)
+#define IPA_INTERFACE_ID_A2_WWAN        (0x10)
+#define IPA_INTERFACE_ID_HSUSB_RMNET1   (0x21)
+#define IPA_INTERFACE_ID_HSUSB_RMNET2   (0x22)
+#define IPA_INTERFACE_ID_HSUSB_RMNET3   (0x23)
+#define IPA_INTERFACE_ID_HSIC_WLAN_WAN  (0x31)
+#define IPA_INTERFACE_ID_HSIC_WLAN_LAN1 (0x32)
+#define IPA_INTERFACE_ID_HSIC_WLAN_LAN2 (0x33)
+#define IPA_INTERFACE_ID_HSIC_RMNET1    (0x41)
+#define IPA_INTERFACE_ID_HSIC_RMNET2    (0x42)
+#define IPA_INTERFACE_ID_HSIC_RMNET3    (0x43)
+#define IPA_INTERFACE_ID_HSIC_RMNET4    (0x44)
+#define IPA_INTERFACE_ID_HSIC_RMNET5    (0x45)
+
+/**
+ * struct ipa_flt_rule_hw_hdr - HW header of IPA filter rule
+ * @word: filtering rule properties
+ * @en_rule: enable rule
+ * @action: post routing action
+ * @rt_tbl_idx: index in routing table
+ * @rsvd: reserved
+ */
+struct ipa_flt_rule_hw_hdr {
+	union {
+		u32 word;
+		struct {
+			u32 en_rule:16;
+			u32 action:5;
+			u32 rt_tbl_idx:5;
+			u32 rsvd:6;
+		} hdr;
+	} u;
+};
+
+/**
+ * struct ipa_rt_rule_hw_hdr - HW header of IPA routing rule
+ * @word: filtering rule properties
+ * @en_rule: enable rule
+ * @pipe_dest_idx: destination pipe index
+ * @system: changed from local to system due to HW change
+ * @hdr_offset: header offset
+ */
+struct ipa_rt_rule_hw_hdr {
+	union {
+		u32 word;
+		struct {
+			u32 en_rule:16;
+			u32 pipe_dest_idx:5;
+			u32 system:1;
+			u32 hdr_offset:10;
+		} hdr;
+	} u;
+};
+
+/**
+ * struct ipa_ip_v4_filter_init - IPA_IP_V4_FILTER_INIT command payload
+ * @ipv4_rules_addr: address of ipv4 rules
+ * @size_ipv4_rules: size of the above
+ * @ipv4_addr: ipv4 address
+ * @rsvd: reserved
+ */
+struct ipa_ip_v4_filter_init {
+	u64 ipv4_rules_addr:32;
+	u64 size_ipv4_rules:12;
+	u64 ipv4_addr:16;
+	u64 rsvd:4;
+};
+
+/**
+ * struct ipa_ip_v6_filter_init - IPA_IP_V6_FILTER_INIT command payload
+ * @ipv6_rules_addr: address of ipv6 rules
+ * @size_ipv6_rules: size of the above
+ * @ipv6_addr: ipv6 address
+ */
+struct ipa_ip_v6_filter_init {
+	u64 ipv6_rules_addr:32;
+	u64 size_ipv6_rules:16;
+	u64 ipv6_addr:16;
+};
+
+/**
+ * struct ipa_ip_v4_routing_init - IPA_IP_V4_ROUTING_INIT command payload
+ * @ipv4_rules_addr: address of ipv4 rules
+ * @size_ipv4_rules: size of the above
+ * @ipv4_addr: ipv4 address
+ * @rsvd: reserved
+ */
+struct ipa_ip_v4_routing_init {
+	u64 ipv4_rules_addr:32;
+	u64 size_ipv4_rules:12;
+	u64 ipv4_addr:16;
+	u64 rsvd:4;
+};
+
+/**
+ * struct ipa_ip_v6_routing_init - IPA_IP_V6_ROUTING_INIT command payload
+ * @ipv6_rules_addr: address of ipv6 rules
+ * @size_ipv6_rules: size of the above
+ * @ipv6_addr: ipv6 address
+ */
+struct ipa_ip_v6_routing_init {
+	u64 ipv6_rules_addr:32;
+	u64 size_ipv6_rules:16;
+	u64 ipv6_addr:16;
+};
+
+/**
+ * struct ipa_hdr_init_local - IPA_HDR_INIT_LOCAL command payload
+ * @hdr_table_addr: address of header table
+ * @size_hdr_table: size of the above
+ * @hdr_addr: header address
+ * @rsvd: reserved
+ */
+struct ipa_hdr_init_local {
+	u64 hdr_table_addr:32;
+	u64 size_hdr_table:12;
+	u64 hdr_addr:16;
+	u64 rsvd:4;
+};
+
+/**
+ * struct ipa_hdr_init_system - IPA_HDR_INIT_SYSTEM command payload
+ * @hdr_table_addr: address of header table
+ * @rsvd: reserved
+ */
+struct ipa_hdr_init_system {
+	u64 hdr_table_addr:32;
+	u64 rsvd:32;
+};
+
+#define IPA_A5_MUX_HDR_EXCP_FLAG_IP		BIT(0)
+#define IPA_A5_MUX_HDR_EXCP_FLAG_NAT		BIT(1)
+#define IPA_A5_MUX_HDR_EXCP_FLAG_SW_FLT	BIT(2)
+#define IPA_A5_MUX_HDR_EXCP_FLAG_TAG		BIT(3)
+#define IPA_A5_MUX_HDR_EXCP_FLAG_REPLICATED	BIT(4)
+#define IPA_A5_MUX_HDR_EXCP_FLAG_IHL		BIT(5)
+
+/**
+ * struct ipa_a5_mux_hdr - A5 MUX header definition
+ * @interface_id: interface ID
+ * @src_pipe_index: source pipe index
+ * @flags: flags
+ * @metadata: metadata
+ *
+ * A5 MUX header is in BE, A5 runs in LE. This struct definition
+ * allows A5 SW to correctly parse the header
+ */
+struct ipa_a5_mux_hdr {
+	u16 interface_id;
+	u8 src_pipe_index;
+	u8 flags;
+	u32 metadata;
+};
+
+/**
+ * struct ipa_nat_dma - IPA_NAT_DMA command payload
+ * @table_index: NAT table index
+ * @rsvd1: reserved
+ * @base_addr: base address
+ * @rsvd2: reserved
+ * @offset: offset
+ * @data: metadata
+ * @rsvd3: reserved
+ */
+struct ipa_nat_dma {
+	u64 table_index:3;
+	u64 rsvd1:1;
+	u64 base_addr:2;
+	u64 rsvd2:2;
+	u64 offset:32;
+	u64 data:16;
+	u64 rsvd3:8;
+};
+
+/**
+ * struct ipa_nat_dma - IPA_IP_PACKET_INIT command payload
+ * @destination_pipe_index: destination pipe index
+ * @rsvd1: reserved
+ * @metadata: metadata
+ * @rsvd2: reserved
+ */
+struct ipa_ip_packet_init {
+	u64 destination_pipe_index:5;
+	u64 rsvd1:3;
+	u64 metadata:32;
+	u64 rsvd2:24;
+};
+
+/**
+ * struct ipa_nat_dma - IPA_IP_V4_NAT_INIT command payload
+ * @ipv4_rules_addr: ipv4 rules address
+ * @ipv4_expansion_rules_addr: ipv4 expansion rules address
+ * @index_table_addr: index tables address
+ * @index_table_expansion_addr: index expansion table address
+ * @table_index: index in table
+ * @ipv4_rules_addr_type: ipv4 address type
+ * @ipv4_expansion_rules_addr_type: ipv4 expansion address type
+ * @index_table_addr_type: index table address type
+ * @index_table_expansion_addr_type: index expansion table type
+ * @size_base_tables: size of base tables
+ * @size_expansion_tables: size of expansion tables
+ * @rsvd2: reserved
+ * @public_ip_addr: public IP address
+ */
+struct ipa_ip_v4_nat_init {
+	u64 ipv4_rules_addr:32;
+	u64 ipv4_expansion_rules_addr:32;
+	u64 index_table_addr:32;
+	u64 index_table_expansion_addr:32;
+	u64 table_index:3;
+	u64 rsvd1:1;
+	u64 ipv4_rules_addr_type:1;
+	u64 ipv4_expansion_rules_addr_type:1;
+	u64 index_table_addr_type:1;
+	u64 index_table_expansion_addr_type:1;
+	u64 size_base_tables:12;
+	u64 size_expansion_tables:10;
+	u64 rsvd2:2;
+	u64 public_ip_addr:32;
+};
+
+#endif /* _IPA_HW_DEFS_H */
diff --git a/drivers/platform/msm/ipa/ipa_i.h b/drivers/platform/msm/ipa/ipa_i.h
new file mode 100644
index 0000000..63ef5fb
--- /dev/null
+++ b/drivers/platform/msm/ipa/ipa_i.h
@@ -0,0 +1,727 @@
+/* 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 _IPA_I_H_
+#define _IPA_I_H_
+
+#include <linux/bitops.h>
+#include <linux/cdev.h>
+#include <linux/export.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/skbuff.h>
+#include <linux/slab.h>
+#include <mach/ipa.h>
+#include <mach/sps.h>
+#include "ipa_hw_defs.h"
+#include "ipa_ram_mmap.h"
+#include "ipa_reg.h"
+
+#define DRV_NAME "ipa"
+#define IPA_COOKIE 0xfacefeed
+
+#define IPA_NUM_PIPES 0x14
+#define IPA_SYS_DESC_FIFO_SZ (0x800)
+
+#ifdef IPA_DEBUG
+#define IPADBG(fmt, args...) \
+	pr_debug(DRV_NAME " %s:%d " fmt, __func__, __LINE__, ## args)
+#else
+#define IPADBG(fmt, args...)
+#endif
+
+#define IPAERR(fmt, args...) \
+	pr_err(DRV_NAME " %s:%d " fmt, __func__, __LINE__, ## args)
+
+#define IPA_TOS_EQ			BIT(0)
+#define IPA_PROTOCOL_EQ		BIT(1)
+#define IPA_OFFSET_MEQ32_0		BIT(2)
+#define IPA_OFFSET_MEQ32_1		BIT(3)
+#define IPA_IHL_OFFSET_RANGE16_0	BIT(4)
+#define IPA_IHL_OFFSET_RANGE16_1	BIT(5)
+#define IPA_IHL_OFFSET_EQ_16		BIT(6)
+#define IPA_IHL_OFFSET_EQ_32		BIT(7)
+#define IPA_IHL_OFFSET_MEQ32_0		BIT(8)
+#define IPA_OFFSET_MEQ128_0		BIT(9)
+#define IPA_OFFSET_MEQ128_1		BIT(10)
+#define IPA_TC_EQ			BIT(11)
+#define IPA_FL_EQ			BIT(12)
+#define IPA_IHL_OFFSET_MEQ32_1		BIT(13)
+#define IPA_METADATA_COMPARE		BIT(14)
+#define IPA_IPV4_IS_FRAG		BIT(15)
+
+#define IPA_HDR_BIN0 0
+#define IPA_HDR_BIN1 1
+#define IPA_HDR_BIN2 2
+#define IPA_HDR_BIN3 3
+#define IPA_HDR_BIN_MAX 4
+
+#define IPA_EVENT_THRESHOLD 0x10
+
+#define IPA_RX_POOL_CEIL 24
+#define IPA_RX_SKB_SIZE 2048
+
+#define IPA_DFLT_HDR_NAME "ipa_excp_hdr"
+
+#define IPA_CLIENT_IS_PROD(x) (x >= IPA_CLIENT_PROD && x < IPA_CLIENT_CONS)
+#define IPA_CLIENT_IS_CONS(x) (x >= IPA_CLIENT_CONS && x < IPA_CLIENT_MAX)
+#define IPA_SETFIELD(val, shift, mask) (((val) << (shift)) & (mask))
+
+#define IPA_HW_TABLE_ALIGNMENT(start_ofst) \
+	(((start_ofst) + 127) & ~127)
+#define IPA_RT_FLT_HW_RULE_BUF_SIZE	(128)
+
+/**
+ * enum ipa_sys_pipe - 5 A5-IPA pipes
+ *
+ * 5 A5-IPA pipes (all system mode)
+ */
+enum ipa_sys_pipe {
+	IPA_A5_UNUSED,
+	IPA_A5_CMD,
+	IPA_A5_LAN_WAN_OUT,
+	IPA_A5_LAN_WAN_IN,
+	IPA_A5_WLAN_AMPDU_OUT,
+	IPA_A5_SYS_MAX
+};
+
+/**
+ * enum ipa_operating_mode - IPA operating mode
+ *
+ * IPA operating mode
+ */
+enum ipa_operating_mode {
+	IPA_MODE_USB_DONGLE,
+	IPA_MODE_MSM,
+	IPA_MODE_EXT_APPS,
+	IPA_MODE_MOBILE_AP_WAN,
+	IPA_MODE_MOBILE_AP_WLAN,
+	IPA_MODE_MOBILE_AP_ETH,
+	IPA_MODE_MAX
+};
+
+/**
+ * enum ipa_bridge_dir - direction of the bridge from air interface perspective
+ *
+ * IPA bridge direction
+ */
+enum ipa_bridge_dir {
+	IPA_DL,
+	IPA_UL,
+	IPA_DIR_MAX
+};
+
+/**
+ * struct ipa_mem_buffer - IPA memory buffer
+ * @base: base
+ * @phys_base: physical base address
+ * @size: size of memory buffer
+ */
+struct ipa_mem_buffer {
+	void *base;
+	dma_addr_t phys_base;
+	u32 size;
+};
+
+/**
+ * struct ipa_flt_entry - IPA filtering table entry
+ * @link: entry's link in global filtering enrties list
+ * @rule: filter rule
+ * @cookie: cookie used for validity check
+ * @tbl: filter table
+ * @rt_tbl: routing table
+ * @hw_len: entry's size
+ */
+struct ipa_flt_entry {
+	struct list_head link;
+	struct ipa_flt_rule rule;
+	u32 cookie;
+	struct ipa_flt_tbl *tbl;
+	struct ipa_rt_tbl *rt_tbl;
+	u32 hw_len;
+};
+
+/**
+ * struct ipa_rt_tbl - IPA routing table
+ * @link: table's link in global routing tables list
+ * @head_rt_rule_list: head of routing rules list
+ * @name: routing table name
+ * @idx: routing table index
+ * @rule_cnt: number of rules in routing table
+ * @ref_cnt: reference counter of raouting table
+ * @set: collection of routing tables
+ * @cookie: cookie used for validity check
+ * @in_sys: flag indicating if the table is located in system memory
+ * @sz: the size of the routing table
+ * @curr_mem: current routing tables block in sys memory
+ * @prev_mem: previous routing table block in sys memory
+ */
+struct ipa_rt_tbl {
+	struct list_head link;
+	struct list_head head_rt_rule_list;
+	char name[IPA_RESOURCE_NAME_MAX];
+	u32 idx;
+	u32 rule_cnt;
+	u32 ref_cnt;
+	struct ipa_rt_tbl_set *set;
+	u32 cookie;
+	bool in_sys;
+	u32 sz;
+	struct ipa_mem_buffer curr_mem;
+	struct ipa_mem_buffer prev_mem;
+};
+
+/**
+ * struct ipa_hdr_entry - IPA header table entry
+ * @link: entry's link in global header table entries list
+ * @hdr: the header
+ * @hdr_len: header length
+ * @name: name of header table entry
+ * @is_partial: flag indicating if header table entry is partial
+ * @offset_entry: entry's offset
+ * @cookie: cookie used for validity check
+ * @ref_cnt: reference counter of raouting table
+ */
+struct ipa_hdr_entry {
+	struct list_head link;
+	u8 hdr[IPA_HDR_MAX_SIZE];
+	u32 hdr_len;
+	char name[IPA_RESOURCE_NAME_MAX];
+	u8 is_partial;
+	struct ipa_hdr_offset_entry *offset_entry;
+	u32 cookie;
+	u32 ref_cnt;
+};
+
+/**
+ * struct ipa_hdr_offset_entry - IPA header offset entry
+ * @link: entry's link in global header offset entries list
+ * @offset: the offset
+ * @bin: bin
+ */
+struct ipa_hdr_offset_entry {
+	struct list_head link;
+	u32 offset;
+	u32 bin;
+};
+
+/**
+ * struct ipa_hdr_tbl - IPA header table
+ * @head_hdr_entry_list: header entries list
+ * @head_offset_list: header offset list
+ * @head_free_offset_list: header free offset list
+ * @hdr_cnt: number of headers
+ * @end: the last header index
+ */
+struct ipa_hdr_tbl {
+	struct list_head head_hdr_entry_list;
+	struct list_head head_offset_list[IPA_HDR_BIN_MAX];
+	struct list_head head_free_offset_list[IPA_HDR_BIN_MAX];
+	u32 hdr_cnt;
+	u32 end;
+};
+
+/**
+ * struct ipa_flt_tbl - IPA filter table
+ * @head_flt_rule_list: filter rules list
+ * @rule_cnt: number of filter rules
+ * @in_sys: flag indicating if filter table is located in system memory
+ * @sz: the size of the filter table
+ * @end: the last header index
+ * @curr_mem: current filter tables block in sys memory
+ * @prev_mem: previous filter table block in sys memory
+ */
+struct ipa_flt_tbl {
+	struct list_head head_flt_rule_list;
+	u32 rule_cnt;
+	bool in_sys;
+	u32 sz;
+	struct ipa_mem_buffer curr_mem;
+	struct ipa_mem_buffer prev_mem;
+};
+
+/**
+ * struct ipa_rt_entry - IPA routing table entry
+ * @link: entry's link in global routing table entries list
+ * @rule: routing rule
+ * @cookie: cookie used for validity check
+ * @tbl: routing table
+ * @hdr: header table
+ * @hw_len: the length of the table
+ */
+struct ipa_rt_entry {
+	struct list_head link;
+	struct ipa_rt_rule rule;
+	u32 cookie;
+	struct ipa_rt_tbl *tbl;
+	struct ipa_hdr_entry *hdr;
+	u32 hw_len;
+};
+
+/**
+ * struct ipa_rt_tbl_set - collection of routing tables
+ * @head_rt_tbl_list: collection of routing tables
+ * @tbl_cnt: number of routing tables
+ */
+struct ipa_rt_tbl_set {
+	struct list_head head_rt_tbl_list;
+	u32 tbl_cnt;
+};
+
+/**
+ * struct ipa_tree_node - handle database entry
+ * @node: RB node
+ * @hdl: handle
+ */
+struct ipa_tree_node {
+	struct rb_node node;
+	u32 hdl;
+};
+
+/**
+ * struct ipa_ep_context - IPA end point context
+ * @valid: flag indicating id EP context is valid
+ * @client: EP client type
+ * @ep_hdl: EP's client SPS handle
+ * @cfg: EP cionfiguration
+ * @dst_pipe_index: destination pipe index
+ * @rt_tbl_idx: routing table index
+ * @connect: SPS connect
+ * @priv: user provided information
+ * @notify: user provided CB for EP events notification
+ * @desc_fifo_in_pipe_mem: flag indicating if descriptors FIFO uses pipe memory
+ * @data_fifo_in_pipe_mem: flag indicating if data FIFO uses pipe memory
+ * @desc_fifo_pipe_mem_ofst: descriptors FIFO pipe memory offset
+ * @data_fifo_pipe_mem_ofst: data FIFO pipe memory offset
+ * @desc_fifo_client_allocated: if descriptors FIFO was allocated by a client
+ * @data_fifo_client_allocated: if data FIFO was allocated by a client
+ */
+struct ipa_ep_context {
+	int valid;
+	enum ipa_client_type client;
+	struct sps_pipe *ep_hdl;
+	struct ipa_ep_cfg cfg;
+	u32 dst_pipe_index;
+	u32 rt_tbl_idx;
+	struct sps_connect connect;
+	void *priv;
+	void (*notify)(void *priv, enum ipa_dp_evt_type evt,
+		       unsigned long data);
+	bool desc_fifo_in_pipe_mem;
+	bool data_fifo_in_pipe_mem;
+	u32 desc_fifo_pipe_mem_ofst;
+	u32 data_fifo_pipe_mem_ofst;
+	bool desc_fifo_client_allocated;
+	bool data_fifo_client_allocated;
+};
+
+/**
+ * struct ipa_sys_context - IPA endpoint context for system to BAM pipes
+ * @head_desc_list: header descriptors list
+ * @len: the size of the above list
+ * @spinlock: protects the list and its size
+ * @event: used to request CALLBACK mode from SPS driver
+ * @ep: IPA EP context
+ * @wait_desc_list: used to hold completed Tx packets
+ *
+ * IPA context specific to the system-bam pipes a.k.a LAN IN/OUT and WAN
+ */
+struct ipa_sys_context {
+	struct list_head head_desc_list;
+	u32 len;
+	spinlock_t spinlock;
+	struct sps_register_event event;
+	struct ipa_ep_context *ep;
+	struct list_head wait_desc_list;
+};
+
+/**
+ * enum ipa_desc_type - IPA decriptors type
+ *
+ * IPA decriptors type, IPA supports DD and ICD but no CD
+ */
+enum ipa_desc_type {
+	IPA_DATA_DESC,
+	IPA_DATA_DESC_SKB,
+	IPA_IMM_CMD_DESC
+};
+
+/**
+ * struct ipa_tx_pkt_wrapper - IPA Tx packet wrapper
+ * @type: info for the skb or immediate command param
+ * @mem: memory buffer used by this Tx packet
+ * @work: work struct for current Tx packet
+ * @link: linked to the wrappers on that pipe
+ * @callback: IPA client provided callback
+ * @user1: cookie1 for above callback
+ * @user2: cookie2 for above callback
+ * @sys: corresponding IPA sys context
+ * @mult: valid only for first of a "multiple" transfer,
+ * holds info for the "sps_transfer" buffer
+ * @cnt: 1 for single transfers,
+ * >1 and <0xFFFF for first of a "multiple" tranfer,
+ * 0xFFFF for last desc, 0 for rest of "multiple' transfer
+ * @bounce: va of bounce buffer
+ */
+struct ipa_tx_pkt_wrapper {
+	enum ipa_desc_type type;
+	struct ipa_mem_buffer mem;
+	struct work_struct work;
+	struct list_head link;
+	void (*callback)(void *user1, void *user2);
+	void *user1;
+	void *user2;
+	struct ipa_sys_context *sys;
+	struct ipa_mem_buffer mult;
+	u16 cnt;
+	void *bounce;
+};
+
+/**
+ * struct ipa_desc - IPA descriptor
+ * @type: skb or immediate command or plain old data
+ * @pyld: points to skb
+ * or kmalloc'ed immediate command parameters/plain old data
+ * @len: length of the pyld
+ * @opcode: for immediate commands
+ * @callback: IPA client provided completion callback
+ * @user1: cookie1 for above callback
+ * @user2: cookie2 for above callback
+ * @xfer_done: completion object for sync completion
+ */
+struct ipa_desc {
+	enum ipa_desc_type type;
+	void *pyld;
+	u16 len;
+	u16 opcode;
+	void (*callback)(void *user1, void *user2);
+	void *user1;
+	void *user2;
+	struct completion xfer_done;
+};
+
+/**
+ * struct ipa_rx_pkt_wrapper - IPA Rx packet wrapper
+ * @skb: skb
+ * @dma_address: DMA address of this Rx packet
+ * @work: work struct for current Rx packet
+ * @link: linked to the Rx packets on that pipe
+ * @len: how many bytes are copied into skb's flat buffer
+ */
+struct ipa_rx_pkt_wrapper {
+	struct sk_buff *skb;
+	dma_addr_t dma_address;
+	struct work_struct work;
+	struct list_head link;
+	u16 len;
+};
+
+/**
+ * struct ipa_nat_mem - IPA NAT memory description
+ * @class: pointer to the struct class
+ * @dev: the dev_t of the device
+ * @cdev: cdev of the device
+ * @dev_num: device number
+ * @vaddr: virtual address
+ * @dma_handle: DMA handle
+ * @size: NAT memory size
+ * @is_mapped: flag indicating if NAT memory is mapped
+ * @is_sys_mem: flag indicating if NAT memory is sys memory
+ * @is_dev_init: flag indicating if NAT device is initialized
+ * @lock: NAT memory mutex
+ */
+struct ipa_nat_mem {
+	struct class *class;
+	struct device *dev;
+	struct cdev cdev;
+	dev_t dev_num;
+	void *vaddr;
+	dma_addr_t dma_handle;
+	size_t size;
+	bool is_mapped;
+	bool is_sys_mem;
+	bool is_dev_init;
+	struct mutex lock;
+};
+
+/**
+ * struct ipa_context - IPA context
+ * @class: pointer to the struct class
+ * @dev_num: device number
+ * @dev: the dev_t of the device
+ * @cdev: cdev of the device
+ * @bam_handle: IPA driver's BAM handle
+ * @ep: list of all end points
+ * @flt_tbl: list of all IPA filter tables
+ * @mode: IPA operating mode
+ * @mmio: iomem
+ * @ipa_wrapper_base: IPA wrapper base address
+ * @glob_flt_tbl: global filter table
+ * @hdr_tbl: IPA header table
+ * @rt_tbl_set: list of routing tables each of which is a list of rules
+ * @reap_rt_tbl_set: list of sys mem routing tables waiting to be reaped
+ * @flt_rule_cache: filter rule cache
+ * @rt_rule_cache: routing rule cache
+ * @hdr_cache: header cache
+ * @hdr_offset_cache: header offset cache
+ * @rt_tbl_cache: routing table cache
+ * @tx_pkt_wrapper_cache: Tx packets cache
+ * @rx_pkt_wrapper_cache: Rx packets cache
+ * @tree_node_cache: tree nodes cache
+ * @rt_idx_bitmap: routing table index bitmap
+ * @lock: this does NOT protect the linked lists within ipa_sys_context
+ * @sys: IPA sys context for system-bam pipes
+ * @rx_wq: Rx packets work queue
+ * @tx_wq: Tx packets work queue
+ * @smem_sz: shared memory size
+ * @hdr_hdl_tree: header handles tree
+ * @rt_rule_hdl_tree: routing rule handles tree
+ * @rt_tbl_hdl_tree: routing table handles tree
+ * @flt_rule_hdl_tree: filtering rule handles tree
+ * @nat_mem: NAT memory
+ * @excp_hdr_hdl: exception header handle
+ * @dflt_v4_rt_rule_hdl: default v4 routing rule handle
+ * @dflt_v6_rt_rule_hdl: default v6 routing rule handle
+ * @polling_mode: 1 - pure polling mode; 0 - interrupt+polling mode
+ * @aggregation_type: aggregation type used on USB client endpoint
+ * @aggregation_byte_limit: aggregation byte limit used on USB client endpoint
+ * @aggregation_time_limit: aggregation time limit used on USB client endpoint
+ * @curr_polling_state: current polling state
+ * @poll_work: polling work
+ * @hdr_tbl_lcl: where hdr tbl resides 1-local, 0-system
+ * @hdr_mem: header memory
+ * @ip4_rt_tbl_lcl: where ip4 rt tables reside 1-local; 0-system
+ * @ip6_rt_tbl_lcl: where ip6 rt tables reside 1-local; 0-system
+ * @ip4_flt_tbl_lcl: where ip4 flt tables reside 1-local; 0-system
+ * @ip6_flt_tbl_lcl: where ip6 flt tables reside 1-local; 0-system
+ * @empty_rt_tbl_mem: empty routing tables memory
+ * @pipe_mem_pool: pipe memory pool
+ * @one_kb_no_straddle_pool: one kb no straddle pool
+ *
+ * IPA context - holds all relevant info about IPA driver and its state
+ */
+struct ipa_context {
+	struct class *class;
+	dev_t dev_num;
+	struct device *dev;
+	struct cdev cdev;
+	u32 bam_handle;
+	struct ipa_ep_context ep[IPA_NUM_PIPES];
+	struct ipa_flt_tbl flt_tbl[IPA_NUM_PIPES][IPA_IP_MAX];
+	enum ipa_operating_mode mode;
+	void __iomem *mmio;
+	u32 ipa_wrapper_base;
+	struct ipa_flt_tbl glob_flt_tbl[IPA_IP_MAX];
+	struct ipa_hdr_tbl hdr_tbl;
+	struct ipa_rt_tbl_set rt_tbl_set[IPA_IP_MAX];
+	struct ipa_rt_tbl_set reap_rt_tbl_set[IPA_IP_MAX];
+	struct kmem_cache *flt_rule_cache;
+	struct kmem_cache *rt_rule_cache;
+	struct kmem_cache *hdr_cache;
+	struct kmem_cache *hdr_offset_cache;
+	struct kmem_cache *rt_tbl_cache;
+	struct kmem_cache *tx_pkt_wrapper_cache;
+	struct kmem_cache *rx_pkt_wrapper_cache;
+	struct kmem_cache *tree_node_cache;
+	unsigned long rt_idx_bitmap[IPA_IP_MAX];
+	struct mutex lock;
+	struct ipa_sys_context sys[IPA_A5_SYS_MAX];
+	struct workqueue_struct *rx_wq;
+	struct workqueue_struct *tx_wq;
+	u16 smem_sz;
+	struct rb_root hdr_hdl_tree;
+	struct rb_root rt_rule_hdl_tree;
+	struct rb_root rt_tbl_hdl_tree;
+	struct rb_root flt_rule_hdl_tree;
+	struct ipa_nat_mem nat_mem;
+	u32 excp_hdr_hdl;
+	u32 dflt_v4_rt_rule_hdl;
+	u32 dflt_v6_rt_rule_hdl;
+	bool polling_mode;
+	uint aggregation_type;
+	uint aggregation_byte_limit;
+	uint aggregation_time_limit;
+	uint curr_polling_state;
+	struct delayed_work poll_work;
+	bool hdr_tbl_lcl;
+	struct ipa_mem_buffer hdr_mem;
+	bool ip4_rt_tbl_lcl;
+	bool ip6_rt_tbl_lcl;
+	bool ip4_flt_tbl_lcl;
+	bool ip6_flt_tbl_lcl;
+	struct ipa_mem_buffer empty_rt_tbl_mem;
+	struct gen_pool *pipe_mem_pool;
+	struct dma_pool *one_kb_no_straddle_pool;
+	atomic_t ipa_active_clients;
+	u32 clnt_hdl_cmd;
+	u32 clnt_hdl_data_in;
+	u32 clnt_hdl_data_out;
+	u8 a5_pipe_index;
+};
+
+/**
+ * struct ipa_route - IPA route
+ * @route_dis: route disable
+ * @route_def_pipe: route default pipe
+ * @route_def_hdr_table: route default header table
+ * @route_def_hdr_ofst: route default header offset table
+ */
+struct ipa_route {
+	u32 route_dis;
+	u32 route_def_pipe;
+	u32 route_def_hdr_table;
+	u32 route_def_hdr_ofst;
+};
+
+/**
+ * enum ipa_pipe_mem_type - IPA pipe memory type
+ * @IPA_SPS_PIPE_MEM: Default, SPS dedicated pipe memory
+ * @IPA_PRIVATE_MEM: IPA's private memory
+ * @IPA_SYSTEM_MEM: System RAM, requires allocation
+ */
+enum ipa_pipe_mem_type {
+	IPA_SPS_PIPE_MEM = 0,
+	IPA_PRIVATE_MEM  = 1,
+	IPA_SYSTEM_MEM   = 2,
+};
+
+/**
+ * enum a2_mux_pipe_direction - IPA-A2 pipe direction
+ */
+enum a2_mux_pipe_direction {
+	A2_TO_IPA = 0,
+	IPA_TO_A2 = 1
+};
+
+/**
+ * struct a2_mux_pipe_connection - A2 MUX pipe connection
+ * @src_phy_addr: source physical address
+ * @src_pipe_index: source pipe index
+ * @dst_phy_addr: destination physical address
+ * @dst_pipe_index: destination pipe index
+ * @mem_type: pipe memory type
+ * @data_fifo_base_offset: data FIFO base offset
+ * @data_fifo_size: data FIFO size
+ * @desc_fifo_base_offset: descriptors FIFO base offset
+ * @desc_fifo_size: descriptors FIFO size
+ */
+struct a2_mux_pipe_connection {
+	int			src_phy_addr;
+	int			src_pipe_index;
+	int			dst_phy_addr;
+	int			dst_pipe_index;
+	enum ipa_pipe_mem_type	mem_type;
+	int			data_fifo_base_offset;
+	int			data_fifo_size;
+	int			desc_fifo_base_offset;
+	int			desc_fifo_size;
+};
+
+extern struct ipa_context *ipa_ctx;
+
+int ipa_get_a2_mux_pipe_info(enum a2_mux_pipe_direction pipe_dir,
+				struct a2_mux_pipe_connection *pipe_connect);
+void rmnet_bridge_get_client_handles(u32 *producer_handle,
+		u32 *consumer_handle);
+int ipa_send_one(struct ipa_sys_context *sys, struct ipa_desc *desc);
+int ipa_send(struct ipa_sys_context *sys, u16 num_desc, struct ipa_desc *desc);
+int ipa_get_ep_mapping(enum ipa_operating_mode mode,
+		       enum ipa_client_type client);
+int ipa_generate_hw_rule(enum ipa_ip_type ip,
+			 const struct ipa_rule_attrib *attrib,
+			 u8 **buf,
+			 u16 *en_rule);
+u8 *ipa_write_32(u32 w, u8 *dest);
+u8 *ipa_write_16(u16 hw, u8 *dest);
+u8 *ipa_write_8(u8 b, u8 *dest);
+u8 *ipa_pad_to_32(u8 *dest);
+int ipa_init_hw(void);
+struct ipa_rt_tbl *__ipa_find_rt_tbl(enum ipa_ip_type ip, const char *name);
+void ipa_dump(void);
+int ipa_generate_hdr_hw_tbl(struct ipa_mem_buffer *mem);
+int ipa_generate_rt_hw_tbl(enum ipa_ip_type ip, struct ipa_mem_buffer *mem);
+int ipa_generate_flt_hw_tbl(enum ipa_ip_type ip, struct ipa_mem_buffer *mem);
+void ipa_debugfs_init(void);
+void ipa_debugfs_remove(void);
+
+/*
+ * below functions read from/write to IPA local memory a.k.a. device memory.
+ * the order of the arguments is deliberately different from the ipa_write*
+ * functions which operate on system memory
+ */
+void ipa_write_dev_8(u8 val, u16 ofst_ipa_sram);
+void ipa_write_dev_16(u16 val, u16 ofst_ipa_sram);
+void ipa_write_dev_32(u32 val, u16 ofst_ipa_sram);
+unsigned int ipa_read_dev_8(u16 ofst_ipa_sram);
+unsigned int ipa_read_dev_16(u16 ofst_ipa_sram);
+unsigned int ipa_read_dev_32(u16 ofst_ipa_sram);
+void ipa_write_dev_8rep(u16 ofst_ipa_sram, const void *buf,
+		unsigned long count);
+void ipa_write_dev_16rep(u16 ofst_ipa_sram, const void *buf,
+		unsigned long count);
+void ipa_write_dev_32rep(u16 ofst_ipa_sram, const void *buf,
+		unsigned long count);
+void ipa_read_dev_8rep(u16 ofst_ipa_sram, void *buf, unsigned long count);
+void ipa_read_dev_16rep(u16 ofst_ipa_sram, void *buf, unsigned long count);
+void ipa_read_dev_32rep(u16 ofst_ipa_sram, void *buf, unsigned long count);
+void ipa_memset_dev(u16 ofst_ipa_sram, u8 value, unsigned int count);
+void ipa_memcpy_from_dev(void *dest, u16 ofst_ipa_sram, unsigned int count);
+void ipa_memcpy_to_dev(u16 ofst_ipa_sram, void *source, unsigned int count);
+
+int ipa_insert(struct rb_root *root, struct ipa_tree_node *data);
+struct ipa_tree_node *ipa_search(struct rb_root *root, u32 hdl);
+void ipa_dump_buff_internal(void *base, dma_addr_t phy_base, u32 size);
+
+#ifdef IPA_DEBUG
+#define IPA_DUMP_BUFF(base, phy_base, size) \
+	ipa_dump_buff_internal(base, phy_base, size)
+#else
+#define IPA_DUMP_BUFF(base, phy_base, size)
+#endif
+
+int ipa_cfg_route(struct ipa_route *route);
+int ipa_send_cmd(u16 num_desc, struct ipa_desc *descr);
+void ipa_replenish_rx_cache(void);
+void ipa_cleanup_rx(void);
+int ipa_cfg_filter(u32 disable);
+void ipa_write_done(struct work_struct *work);
+void ipa_handle_rx(struct work_struct *work);
+void ipa_handle_rx_core(void);
+int ipa_pipe_mem_init(u32 start_ofst, u32 size);
+int ipa_pipe_mem_alloc(u32 *ofst, u32 size);
+int ipa_pipe_mem_free(u32 ofst, u32 size);
+int ipa_straddle_boundary(u32 start, u32 end, u32 boundary);
+struct ipa_context *ipa_get_ctx(void);
+void ipa_enable_clks(void);
+void ipa_disable_clks(void);
+
+static inline u32 ipa_read_reg(void *base, u32 offset)
+{
+	u32 val = ioread32(base + offset);
+	IPADBG("0x%x(va) read reg 0x%x r_val 0x%x.\n",
+		(u32)base, offset, val);
+	return val;
+}
+
+static inline void ipa_write_reg(void *base, u32 offset, u32 val)
+{
+	iowrite32(val, base + offset);
+	IPADBG("0x%x(va) write reg 0x%x w_val 0x%x.\n",
+		(u32)base, offset, val);
+}
+
+int ipa_bridge_init(void);
+void ipa_bridge_cleanup(void);
+int ipa_bridge_setup(enum ipa_bridge_dir dir);
+int ipa_bridge_teardown(enum ipa_bridge_dir dir);
+
+#endif /* _IPA_I_H_ */
diff --git a/drivers/platform/msm/ipa/ipa_nat.c b/drivers/platform/msm/ipa/ipa_nat.c
new file mode 100644
index 0000000..c13c53a
--- /dev/null
+++ b/drivers/platform/msm/ipa/ipa_nat.c
@@ -0,0 +1,466 @@
+/* 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/device.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/uaccess.h>
+#include "ipa_i.h"
+
+#define IPA_NAT_PHYS_MEM_OFFSET  0
+#define IPA_NAT_PHYS_MEM_SIZE  IPA_RAM_NAT_SIZE
+
+#define IPA_NAT_SYSTEM_MEMORY  0
+#define IPA_NAT_SHARED_MEMORY  1
+
+static int ipa_nat_vma_fault_remap(
+	 struct vm_area_struct *vma, struct vm_fault *vmf)
+{
+	IPADBG("\n");
+	vmf->page = NULL;
+
+	return VM_FAULT_SIGBUS;
+}
+
+/* VMA related file operations functions */
+static struct vm_operations_struct ipa_nat_remap_vm_ops = {
+	.fault = ipa_nat_vma_fault_remap,
+};
+
+static int ipa_nat_open(struct inode *inode, struct file *filp)
+{
+	struct ipa_nat_mem *nat_ctx;
+	IPADBG("\n");
+	nat_ctx = container_of(inode->i_cdev, struct ipa_nat_mem, cdev);
+	filp->private_data = nat_ctx;
+	IPADBG("return\n");
+	return 0;
+}
+
+static int ipa_nat_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+	unsigned long vsize = vma->vm_end - vma->vm_start;
+	struct ipa_nat_mem *nat_ctx = (struct ipa_nat_mem *)filp->private_data;
+	unsigned long phys_addr;
+	int result;
+
+	mutex_lock(&nat_ctx->lock);
+	vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+	if (nat_ctx->is_sys_mem) {
+		IPADBG("Mapping system memory\n");
+		if (nat_ctx->is_mapped) {
+			IPAERR("mapping already exists, only 1 supported\n");
+			result = -EINVAL;
+			goto bail;
+		}
+		IPADBG("map sz=0x%x\n", nat_ctx->size);
+		result =
+			dma_mmap_coherent(
+				 NULL, vma,
+				 nat_ctx->vaddr, nat_ctx->dma_handle,
+				 nat_ctx->size);
+
+		if (result) {
+			IPAERR("unable to map memory. Err:%d\n", result);
+			goto bail;
+		}
+	} else {
+		IPADBG("Mapping shared(local) memory\n");
+		IPADBG("map sz=0x%lx\n", vsize);
+		phys_addr = ipa_ctx->ipa_wrapper_base + IPA_REG_BASE_OFST +
+			IPA_SRAM_DIRECT_ACCESS_n_OFST(IPA_NAT_PHYS_MEM_OFFSET);
+
+		if (remap_pfn_range(
+			 vma, vma->vm_start,
+			 phys_addr >> PAGE_SHIFT, vsize, vma->vm_page_prot)) {
+			IPAERR("remap failed\n");
+			result = -EAGAIN;
+			goto bail;
+		}
+
+	}
+	nat_ctx->is_mapped = true;
+	vma->vm_ops = &ipa_nat_remap_vm_ops;
+	IPADBG("return\n");
+	result = 0;
+bail:
+	mutex_unlock(&nat_ctx->lock);
+	return result;
+}
+
+static const struct file_operations ipa_nat_fops = {
+	.owner = THIS_MODULE,
+	.open = ipa_nat_open,
+	.mmap = ipa_nat_mmap
+};
+
+/**
+ * allocate_nat_device() - Allocates memory for the NAT device
+ * @mem:	[in/out] memory parameters
+ *
+ * Called by NAT client driver to allocate memory for the NAT entries. Based on
+ * the request size either shared or system memory will be used.
+ *
+ * Returns:	0 on success, negative on failure
+ */
+int allocate_nat_device(struct ipa_ioc_nat_alloc_mem *mem)
+{
+	struct ipa_nat_mem *nat_ctx = &(ipa_ctx->nat_mem);
+	int gfp_flags = GFP_KERNEL | __GFP_ZERO;
+	int result;
+
+	IPADBG("passed memory size %d\n", mem->size);
+
+	mutex_lock(&nat_ctx->lock);
+	if (mem->size <= 0 || !strlen(mem->dev_name)
+			|| nat_ctx->is_dev_init == true) {
+		IPADBG("Invalid Parameters or device is already init\n");
+		result = -EPERM;
+		goto bail;
+	}
+
+	if (mem->size > IPA_NAT_PHYS_MEM_SIZE) {
+		IPADBG("Allocating system memory\n");
+		nat_ctx->is_sys_mem = true;
+		nat_ctx->vaddr =
+		   dma_alloc_coherent(NULL, mem->size, &nat_ctx->dma_handle,
+				       gfp_flags);
+		if (nat_ctx->vaddr == NULL) {
+			IPAERR("memory alloc failed\n");
+			result = -ENOMEM;
+			goto bail;
+		}
+		nat_ctx->size = mem->size;
+	} else {
+		IPADBG("using shared(local) memory\n");
+		nat_ctx->is_sys_mem = false;
+	}
+
+	nat_ctx->class = class_create(THIS_MODULE, mem->dev_name);
+	if (IS_ERR(nat_ctx->class)) {
+		IPAERR("unable to create the class\n");
+		result = -ENODEV;
+		goto vaddr_alloc_fail;
+	}
+	result = alloc_chrdev_region(&nat_ctx->dev_num,
+					0,
+					1,
+					mem->dev_name);
+	if (result) {
+		IPAERR("alloc_chrdev_region err.\n");
+		result = -ENODEV;
+		goto alloc_chrdev_region_fail;
+	}
+
+	nat_ctx->dev =
+	   device_create(nat_ctx->class, NULL, nat_ctx->dev_num, nat_ctx,
+			 mem->dev_name);
+
+	if (IS_ERR(nat_ctx->dev)) {
+		IPAERR("device_create err:%ld\n", PTR_ERR(nat_ctx->dev));
+		result = -ENODEV;
+		goto device_create_fail;
+	}
+
+	cdev_init(&nat_ctx->cdev, &ipa_nat_fops);
+	nat_ctx->cdev.owner = THIS_MODULE;
+	nat_ctx->cdev.ops = &ipa_nat_fops;
+
+	result = cdev_add(&nat_ctx->cdev, nat_ctx->dev_num, 1);
+	if (result) {
+		IPAERR("cdev_add err=%d\n", -result);
+		goto cdev_add_fail;
+	}
+	nat_ctx->is_dev_init = true;
+	IPADBG("IPA NAT driver init successfully\n");
+	result = 0;
+	goto bail;
+
+cdev_add_fail:
+	device_destroy(nat_ctx->class, nat_ctx->dev_num);
+device_create_fail:
+	unregister_chrdev_region(nat_ctx->dev_num, 1);
+alloc_chrdev_region_fail:
+	class_destroy(nat_ctx->class);
+vaddr_alloc_fail:
+	if (nat_ctx->vaddr) {
+		IPADBG("Releasing system memory\n");
+		dma_free_coherent(
+			 NULL, nat_ctx->size,
+			 nat_ctx->vaddr, nat_ctx->dma_handle);
+		nat_ctx->vaddr = NULL;
+		nat_ctx->dma_handle = 0;
+		nat_ctx->size = 0;
+	}
+bail:
+	mutex_unlock(&nat_ctx->lock);
+
+	return result;
+}
+
+/* IOCTL function handlers */
+/**
+ * ipa_nat_init_cmd() - Post IP_V4_NAT_INIT command to IPA HW
+ * @init:	[in] initialization command attributes
+ *
+ * Called by NAT client driver to post IP_V4_NAT_INIT command to IPA HW
+ *
+ * Returns:	0 on success, negative on failure
+ */
+int ipa_nat_init_cmd(struct ipa_ioc_v4_nat_init *init)
+{
+	struct ipa_desc desc = { 0 };
+	struct ipa_ip_v4_nat_init *cmd;
+	u16 size = sizeof(struct ipa_ip_v4_nat_init);
+	int result;
+
+	IPADBG("\n");
+	if (init->tbl_index < 0 || init->table_entries <= 0) {
+		IPADBG("Table index or entries is zero\n");
+		result = -EPERM;
+		goto bail;
+	}
+	cmd = kmalloc(size, GFP_KERNEL);
+	if (!cmd) {
+		IPAERR("Failed to alloc immediate command object\n");
+		result = -ENOMEM;
+		goto bail;
+	}
+	if (ipa_ctx->nat_mem.vaddr) {
+		IPADBG("using system memory for nat table\n");
+		cmd->ipv4_rules_addr_type = IPA_NAT_SYSTEM_MEMORY;
+		cmd->ipv4_expansion_rules_addr_type = IPA_NAT_SYSTEM_MEMORY;
+		cmd->index_table_addr_type = IPA_NAT_SYSTEM_MEMORY;
+		cmd->index_table_expansion_addr_type = IPA_NAT_SYSTEM_MEMORY;
+
+		cmd->ipv4_rules_addr =
+			ipa_ctx->nat_mem.dma_handle + init->ipv4_rules_offset;
+		IPADBG("ipv4_rules_offset:0x%x\n", init->ipv4_rules_offset);
+
+		cmd->ipv4_expansion_rules_addr =
+		   ipa_ctx->nat_mem.dma_handle + init->expn_rules_offset;
+		IPADBG("expn_rules_offset:0x%x\n", init->expn_rules_offset);
+
+		cmd->index_table_addr =
+			ipa_ctx->nat_mem.dma_handle + init->index_offset;
+		IPADBG("index_offset:0x%x\n", init->index_offset);
+
+		cmd->index_table_expansion_addr =
+		   ipa_ctx->nat_mem.dma_handle + init->index_expn_offset;
+		IPADBG("index_expn_offset:0x%x\n", init->index_expn_offset);
+	} else {
+		IPADBG("using shared(local) memory for nat table\n");
+		cmd->ipv4_rules_addr_type = IPA_NAT_SHARED_MEMORY;
+		cmd->ipv4_expansion_rules_addr_type = IPA_NAT_SHARED_MEMORY;
+		cmd->index_table_addr_type = IPA_NAT_SHARED_MEMORY;
+		cmd->index_table_expansion_addr_type = IPA_NAT_SHARED_MEMORY;
+
+		cmd->ipv4_rules_addr =
+			init->ipv4_rules_offset + IPA_RAM_NAT_OFST;
+
+		cmd->ipv4_expansion_rules_addr =
+			init->expn_rules_offset + IPA_RAM_NAT_OFST;
+
+		cmd->index_table_addr = init->index_offset + IPA_RAM_NAT_OFST;
+
+		cmd->index_table_expansion_addr =
+			init->index_expn_offset + IPA_RAM_NAT_OFST;
+	}
+	cmd->table_index = init->tbl_index;
+	IPADBG("Table index:0x%x\n", cmd->table_index);
+	cmd->size_base_tables = init->table_entries;
+	IPADBG("Base Table size:0x%x\n", cmd->size_base_tables);
+	cmd->size_expansion_tables = init->expn_table_entries;
+	IPADBG("Expansion Table size:0x%x\n", cmd->size_expansion_tables);
+	cmd->public_ip_addr = init->ip_addr;
+	IPADBG("Public ip address:0x%x\n", cmd->public_ip_addr);
+	desc.opcode = IPA_IP_V4_NAT_INIT;
+	desc.type = IPA_IMM_CMD_DESC;
+	desc.callback = NULL;
+	desc.user1 = NULL;
+	desc.user2 = NULL;
+	desc.pyld = (void *)cmd;
+	desc.len = size;
+	IPADBG("posting v4 init command\n");
+	if (ipa_send_cmd(1, &desc)) {
+		IPAERR("Fail to send immediate command\n");
+		result = -EPERM;
+		goto free_cmd;
+	}
+
+	IPADBG("return\n");
+	result = 0;
+free_cmd:
+	kfree(cmd);
+bail:
+	return result;
+}
+
+/**
+ * ipa_nat_dma_cmd() - Post NAT_DMA command to IPA HW
+ * @dma:	[in] initialization command attributes
+ *
+ * Called by NAT client driver to post NAT_DMA command to IPA HW
+ *
+ * Returns:	0 on success, negative on failure
+ */
+int ipa_nat_dma_cmd(struct ipa_ioc_nat_dma_cmd *dma)
+{
+	struct ipa_nat_dma *cmd = NULL;
+	struct ipa_desc *desc = NULL;
+	u16 size = 0, cnt = 0;
+	int ret = 0;
+
+	IPADBG("\n");
+	if (dma->entries <= 0) {
+		IPADBG("Invalid number of commands\n");
+		ret = -EPERM;
+		goto bail;
+	}
+	size = sizeof(struct ipa_desc) * dma->entries;
+	desc = kmalloc(size, GFP_KERNEL);
+	if (desc == NULL) {
+		IPAERR("Failed to alloc memory\n");
+		ret = -ENOMEM;
+		goto bail;
+	}
+	size = sizeof(struct ipa_nat_dma) * dma->entries;
+	cmd = kmalloc(size, GFP_KERNEL);
+	if (cmd == NULL) {
+		IPAERR("Failed to alloc memory\n");
+		ret = -ENOMEM;
+		goto bail;
+	}
+	for (cnt = 0; cnt < dma->entries; cnt++) {
+		cmd[cnt].table_index = dma->dma[cnt].table_index;
+		cmd[cnt].base_addr = dma->dma[cnt].base_addr;
+		cmd[cnt].offset = dma->dma[cnt].offset;
+		cmd[cnt].data = dma->dma[cnt].data;
+		desc[cnt].type = IPA_IMM_CMD_DESC;
+		desc[cnt].opcode = IPA_NAT_DMA;
+		desc[cnt].callback = NULL;
+		desc[cnt].user1 = NULL;
+
+		desc[cnt].user2 = NULL;
+
+		desc[cnt].len = sizeof(struct ipa_nat_dma);
+		desc[cnt].pyld = (void *)&cmd[cnt];
+	}
+	IPADBG("posting dma command with entries %d\n", dma->entries);
+	ret = ipa_send_cmd(dma->entries, desc);
+	if (ret == -EPERM)
+		IPAERR("Fail to send immediate command\n");
+
+bail:
+	kfree(cmd);
+	kfree(desc);
+
+	return ret;
+}
+
+/**
+ * ipa_nat_free_mem_and_device() - free the NAT memory and remove the device
+ * @nat_ctx:	[in] the IPA NAT memory to free
+ *
+ * Called by NAT client driver to free the NAT memory and remove the device
+ */
+void ipa_nat_free_mem_and_device(struct ipa_nat_mem *nat_ctx)
+{
+	IPADBG("\n");
+	mutex_lock(&nat_ctx->lock);
+
+	if (nat_ctx->is_sys_mem) {
+		IPADBG("freeing the dma memory\n");
+		dma_free_coherent(
+			 NULL, nat_ctx->size,
+			 nat_ctx->vaddr, nat_ctx->dma_handle);
+		nat_ctx->size = 0;
+		nat_ctx->vaddr = NULL;
+	}
+	nat_ctx->is_mapped = false;
+	nat_ctx->is_sys_mem = false;
+	cdev_del(&nat_ctx->cdev);
+	device_destroy(nat_ctx->class, nat_ctx->dev_num);
+	unregister_chrdev_region(nat_ctx->dev_num, 1);
+	class_destroy(nat_ctx->class);
+	nat_ctx->is_dev_init = false;
+
+	mutex_unlock(&nat_ctx->lock);
+	IPADBG("return\n");
+	return;
+}
+
+/**
+ * ipa_nat_del_cmd() - Delete a NAT table
+ * @del:	[in] delete table table table parameters
+ *
+ * Called by NAT client driver to delete the nat table
+ *
+ * Returns:	0 on success, negative on failure
+ */
+int ipa_nat_del_cmd(struct ipa_ioc_v4_nat_del *del)
+{
+	struct ipa_desc desc = { 0 };
+	struct ipa_ip_v4_nat_init *cmd;
+	u16 size = sizeof(struct ipa_ip_v4_nat_init);
+	u8 mem_type = IPA_NAT_SHARED_MEMORY;
+	u32 base_addr = IPA_NAT_PHYS_MEM_OFFSET;
+	int result;
+
+	IPADBG("\n");
+	if (del->table_index < 0 || del->public_ip_addr == 0) {
+		IPADBG("Bad Parameter\n");
+		result = -EPERM;
+		goto bail;
+	}
+	cmd = kmalloc(size, GFP_KERNEL);
+	if (cmd == NULL) {
+		IPAERR("Failed to alloc immediate command object\n");
+		result = -ENOMEM;
+		goto bail;
+	}
+	cmd->table_index = del->table_index;
+	cmd->ipv4_rules_addr = base_addr;
+	cmd->ipv4_rules_addr_type = mem_type;
+	cmd->ipv4_expansion_rules_addr = base_addr;
+	cmd->ipv4_expansion_rules_addr_type = mem_type;
+	cmd->index_table_addr = base_addr;
+	cmd->index_table_addr_type = mem_type;
+	cmd->index_table_expansion_addr = base_addr;
+	cmd->index_table_expansion_addr_type = mem_type;
+	cmd->size_base_tables = 0;
+	cmd->size_expansion_tables = 0;
+	cmd->public_ip_addr = del->public_ip_addr;
+
+	desc.opcode = IPA_IP_V4_NAT_INIT;
+	desc.type = IPA_IMM_CMD_DESC;
+	desc.callback = NULL;
+	desc.user1 = NULL;
+	desc.user2 = NULL;
+	desc.pyld = (void *)cmd;
+	desc.len = size;
+	if (ipa_send_cmd(1, &desc)) {
+		IPAERR("Fail to send immediate command\n");
+		result = -EPERM;
+		goto free_mem;
+	}
+
+	ipa_nat_free_mem_and_device(&ipa_ctx->nat_mem);
+	IPADBG("return\n");
+	result = 0;
+free_mem:
+	kfree(cmd);
+bail:
+	return result;
+}
diff --git a/drivers/platform/msm/ipa/ipa_ram_mmap.h b/drivers/platform/msm/ipa/ipa_ram_mmap.h
new file mode 100644
index 0000000..000718b
--- /dev/null
+++ b/drivers/platform/msm/ipa/ipa_ram_mmap.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 _IPA_RAM_MMAP_H_
+#define _IPA_RAM_MMAP_H_
+
+/*
+ * This header defines the memory map of the IPA RAM (not all 8K is available
+ * for SW use) the first 2K are set aside for NAT
+ */
+
+#define IPA_RAM_NAT_OFST    0
+#define IPA_RAM_NAT_SIZE    2048
+#define IPA_RAM_HDR_OFST    2048
+#define IPA_RAM_HDR_SIZE    256
+#define IPA_RAM_V4_FLT_OFST (IPA_RAM_HDR_OFST + IPA_RAM_HDR_SIZE)
+#define IPA_RAM_V4_FLT_SIZE 1024
+#define IPA_RAM_V4_RT_OFST  (IPA_RAM_V4_FLT_OFST + IPA_RAM_V4_FLT_SIZE)
+#define IPA_RAM_V4_RT_SIZE  1024
+#define IPA_RAM_V6_FLT_OFST (IPA_RAM_V4_RT_OFST + IPA_RAM_V4_RT_SIZE)
+#define IPA_RAM_V6_FLT_SIZE 1024
+#define IPA_RAM_V6_RT_OFST  (IPA_RAM_V6_FLT_OFST + IPA_RAM_V6_FLT_SIZE)
+#define IPA_RAM_V6_RT_SIZE  1024
+#define IPA_RAM_END_OFST    (IPA_RAM_V6_RT_OFST + IPA_RAM_V6_RT_SIZE)
+
+#endif /* _IPA_RAM_MMAP_H_ */
diff --git a/drivers/platform/msm/ipa/ipa_reg.h b/drivers/platform/msm/ipa/ipa_reg.h
new file mode 100644
index 0000000..61913b6
--- /dev/null
+++ b/drivers/platform/msm/ipa/ipa_reg.h
@@ -0,0 +1,223 @@
+/* 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 __IPA_REG_H__
+#define __IPA_REG_H__
+
+/*
+ * IPA's BAM specific registers
+ */
+
+#define IPA_BAM_REG_BASE_OFST 0x00004000
+
+#define IPA_BAM_CNFG_BITS_OFST 0x7c
+#define IPA_BAM_REMAP_SIZE (0x1000)
+
+/*
+ * IPA's core specific regtisters
+ */
+
+#define IPA_REG_BASE_OFST 0x00020000
+
+#define IPA_COMP_HW_VERSION_OFST 0x00000030
+#define IPA_COMP_HW_VERSION_RMSK 0xffffffff
+#define IPA_COMP_HW_VERSION_MAJOR_BMSK 0xff000000
+#define IPA_COMP_HW_VERSION_MAJOR_SHFT 0x18
+#define IPA_COMP_HW_VERSION_MINOR_BMSK 0xff0000
+#define IPA_COMP_HW_VERSION_MINOR_SHFT 0x10
+#define IPA_COMP_HW_VERSION_STEP_BMSK 0xffff
+#define IPA_COMP_HW_VERSION_STEP_SHFT 0x0
+
+#define IPA_VERSION_OFST 0x00000034
+#define IPA_VERSION_RMSK 0xffffffff
+#define IPA_VERSION_IPA_R_REV_BMSK 0xff000000
+#define IPA_VERSION_IPA_R_REV_SHFT 0x18
+#define IPA_VERSION_IPA_Q_REV_BMSK 0xff0000
+#define IPA_VERSION_IPA_Q_REV_SHFT 0x10
+#define IPA_VERSION_IPA_P_REV_BMSK 0xff00
+#define IPA_VERSION_IPA_P_REV_SHFT 0x8
+#define IPA_VERSION_IPA_ECO_REV_BMSK 0xff
+#define IPA_VERSION_IPA_ECO_REV_SHFT 0x0
+
+#define IPA_COMP_CFG_OFST 0x00000038
+#define IPA_COMP_CFG_RMSK 0x1
+#define IPA_COMP_CFG_ENABLE_BMSK 0x1
+#define IPA_COMP_CFG_ENABLE_SHFT 0x0
+
+#define IPA_COMP_SW_RESET_OFST 0x0000003c
+#define IPA_COMP_SW_RESET_RMSK 0x1
+#define IPA_COMP_SW_RESET_SW_RESET_BMSK 0x1
+#define IPA_COMP_SW_RESET_SW_RESET_SHFT 0x0
+
+#define IPA_CLKON_CFG_OFST 0x00000040
+#define IPA_CLKON_CFG_RMSK 0xf
+#define IPA_CLKON_CFG_CGC_OPEN_MISC_BMSK 0x8
+#define IPA_CLKON_CFG_CGC_OPEN_MISC_SHFT 0x3
+#define IPA_CLKON_CFG_CGC_OPEN_TX_BMSK 0x4
+#define IPA_CLKON_CFG_CGC_OPEN_TX_SHFT 0x2
+#define IPA_CLKON_CFG_CGC_OPEN_PROC_BMSK 0x2
+#define IPA_CLKON_CFG_CGC_OPEN_PROC_SHFT 0x1
+#define IPA_CLKON_CFG_CGC_OPEN_RX_BMSK 0x1
+#define IPA_CLKON_CFG_CGC_OPEN_RX_SHFT 0x0
+
+#define IPA_HEAD_OF_LINE_BLOCK_EN_OFST 0x00000044
+#define IPA_HEAD_OF_LINE_BLOCK_EN_RMSK 0x1
+#define IPA_HEAD_OF_LINE_BLOCK_EN_EN_BMSK 0x1
+#define IPA_HEAD_OF_LINE_BLOCK_EN_EN_SHFT 0x0
+
+#define IPA_HEAD_OF_LINE_BLOCK_TIMER_OFST 0x00000048
+#define IPA_HEAD_OF_LINE_BLOCK_TIMER_RMSK 0x1ff
+#define IPA_HEAD_OF_LINE_BLOCK_TIMER_TIMER_BMSK 0x1ff
+#define IPA_HEAD_OF_LINE_BLOCK_TIMER_TIMER_SHFT 0x0
+
+#define IPA_ROUTE_OFST 0x0000004c
+#define IPA_ROUTE_RMSK 0x1ffff
+#define IPA_ROUTE_ROUTE_DEF_HDR_OFST_BMSK 0x1ff80
+#define IPA_ROUTE_ROUTE_DEF_HDR_OFST_SHFT 0x7
+#define IPA_ROUTE_ROUTE_DEF_HDR_TABLE_BMSK 0x40
+#define IPA_ROUTE_ROUTE_DEF_HDR_TABLE_SHFT 0x6
+#define IPA_ROUTE_ROUTE_DEF_PIPE_BMSK 0x3e
+#define IPA_ROUTE_ROUTE_DEF_PIPE_SHFT 0x1
+#define IPA_ROUTE_ROUTE_DIS_BMSK 0x1
+#define IPA_ROUTE_ROUTE_DIS_SHFT 0x0
+
+#define IPA_FILTER_OFST 0x00000050
+#define IPA_FILTER_RMSK 0x1
+#define IPA_FILTER_FILTER_EN_BMSK 0x1
+#define IPA_FILTER_FILTER_EN_SHFT 0x0
+
+#define IPA_MASTER_PRIORITY_OFST 0x00000054
+#define IPA_MASTER_PRIORITY_RMSK 0xffffffff
+#define IPA_MASTER_PRIORITY_MASTER_7_WR_BMSK 0xc0000000
+#define IPA_MASTER_PRIORITY_MASTER_7_WR_SHFT 0x1e
+#define IPA_MASTER_PRIORITY_MASTER_7_RD_BMSK 0x30000000
+#define IPA_MASTER_PRIORITY_MASTER_7_RD_SHFT 0x1c
+#define IPA_MASTER_PRIORITY_MASTER_6_WR_BMSK 0xc000000
+#define IPA_MASTER_PRIORITY_MASTER_6_WR_SHFT 0x1a
+#define IPA_MASTER_PRIORITY_MASTER_6_RD_BMSK 0x3000000
+#define IPA_MASTER_PRIORITY_MASTER_6_RD_SHFT 0x18
+#define IPA_MASTER_PRIORITY_MASTER_5_WR_BMSK 0xc00000
+#define IPA_MASTER_PRIORITY_MASTER_5_WR_SHFT 0x16
+#define IPA_MASTER_PRIORITY_MASTER_5_RD_BMSK 0x300000
+#define IPA_MASTER_PRIORITY_MASTER_5_RD_SHFT 0x14
+#define IPA_MASTER_PRIORITY_MASTER_4_WR_BMSK 0xc0000
+#define IPA_MASTER_PRIORITY_MASTER_4_WR_SHFT 0x12
+#define IPA_MASTER_PRIORITY_MASTER_4_RD_BMSK 0x30000
+#define IPA_MASTER_PRIORITY_MASTER_4_RD_SHFT 0x10
+#define IPA_MASTER_PRIORITY_MASTER_3_WR_BMSK 0xc000
+#define IPA_MASTER_PRIORITY_MASTER_3_WR_SHFT 0xe
+#define IPA_MASTER_PRIORITY_MASTER_3_RD_BMSK 0x3000
+#define IPA_MASTER_PRIORITY_MASTER_3_RD_SHFT 0xc
+#define IPA_MASTER_PRIORITY_MASTER_2_WR_BMSK 0xc00
+#define IPA_MASTER_PRIORITY_MASTER_2_WR_SHFT 0xa
+#define IPA_MASTER_PRIORITY_MASTER_2_RD_BMSK 0x300
+#define IPA_MASTER_PRIORITY_MASTER_2_RD_SHFT 0x8
+#define IPA_MASTER_PRIORITY_MASTER_1_WR_BMSK 0xc0
+#define IPA_MASTER_PRIORITY_MASTER_1_WR_SHFT 0x6
+#define IPA_MASTER_PRIORITY_MASTER_1_RD_BMSK 0x30
+#define IPA_MASTER_PRIORITY_MASTER_1_RD_SHFT 0x4
+#define IPA_MASTER_PRIORITY_MASTER_0_WR_BMSK 0xc
+#define IPA_MASTER_PRIORITY_MASTER_0_WR_SHFT 0x2
+#define IPA_MASTER_PRIORITY_MASTER_0_RD_BMSK 0x3
+#define IPA_MASTER_PRIORITY_MASTER_0_RD_SHFT 0x0
+
+#define IPA_SHARED_MEM_SIZE_OFST 0x00000058
+#define IPA_SHARED_MEM_SIZE_RMSK 0x1fff
+#define IPA_SHARED_MEM_SIZE_SHARED_MEM_SIZE_BMSK 0x1fff
+#define IPA_SHARED_MEM_SIZE_SHARED_MEM_SIZE_SHFT 0x0
+
+#define IPA_NAT_TIMER_OFST 0x0000005c
+#define IPA_NAT_TIMER_RMSK 0xffffff
+#define IPA_NAT_TIMER_NAT_TIMER_BMSK 0xffffff
+#define IPA_NAT_TIMER_NAT_TIMER_SHFT 0x0
+
+#define IPA_NAT_TIMER_RESET_OFST 0x00000060
+#define IPA_NAT_TIMER_RESET_RMSK 0x1
+#define IPA_NAT_TIMER_RESET_NAT_TIMER_RESET_BMSK 0x1
+#define IPA_NAT_TIMER_RESET_NAT_TIMER_RESET_SHFT 0x0
+
+#define IPA_ENDP_INIT_NAT_n_OFST(n) (0x00000080 + 0x4 * (n))
+#define IPA_ENDP_INIT_NAT_n_RMSK 0x3
+#define IPA_ENDP_INIT_NAT_n_MAXn 19
+#define IPA_ENDP_INIT_NAT_n_NAT_EN_BMSK 0x3
+#define IPA_ENDP_INIT_NAT_n_NAT_EN_SHFT 0x0
+
+#define IPA_ENDP_INIT_HDR_n_OFST(n) (0x000000e0 + 0x4 * (n))
+#define IPA_ENDP_INIT_HDR_n_RMSK 0x7ffffff
+#define IPA_ENDP_INIT_HDR_n_MAXn 19
+#define IPA_ENDP_INIT_HDR_n_HDR_A5_MUX_BMSK 0x4000000
+#define IPA_ENDP_INIT_HDR_n_HDR_A5_MUX_SHFT 0x1a
+#define IPA_ENDP_INIT_HDR_n_HDR_OFST_PKT_SIZE_BMSK 0x3f00000
+#define IPA_ENDP_INIT_HDR_n_HDR_OFST_PKT_SIZE_SHFT 0x14
+#define IPA_ENDP_INIT_HDR_n_HDR_OFST_PKT_SIZE_VALID_BMSK 0x80000
+#define IPA_ENDP_INIT_HDR_n_HDR_OFST_PKT_SIZE_VALID_SHFT 0x13
+#define IPA_ENDP_INIT_HDR_n_HDR_ADDITIONAL_CONST_LEN_BMSK 0x7e000
+#define IPA_ENDP_INIT_HDR_n_HDR_ADDITIONAL_CONST_LEN_SHFT 0xd
+#define IPA_ENDP_INIT_HDR_n_HDR_OFST_METADATA_BMSK 0x1f80
+#define IPA_ENDP_INIT_HDR_n_HDR_OFST_METADATA_SHFT 0x7
+#define IPA_ENDP_INIT_HDR_n_HDR_OFST_METADATA_VALID_BMSK 0x40
+#define IPA_ENDP_INIT_HDR_n_HDR_OFST_METADATA_VALID_SHFT 0x6
+#define IPA_ENDP_INIT_HDR_n_HDR_LEN_BMSK 0x3f
+#define IPA_ENDP_INIT_HDR_n_HDR_LEN_SHFT 0x0
+
+#define IPA_ENDP_INIT_MODE_n_OFST(n) (0x00000140 + 0x4 * (n))
+#define IPA_ENDP_INIT_MODE_n_RMSK 0x7f
+#define IPA_ENDP_INIT_MODE_n_MAXn 19
+#define IPA_ENDP_INIT_MODE_n_DEST_PIPE_INDEX_BMSK 0x7c
+#define IPA_ENDP_INIT_MODE_n_DEST_PIPE_INDEX_SHFT 0x2
+#define IPA_ENDP_INIT_MODE_n_MODE_BMSK 0x3
+#define IPA_ENDP_INIT_MODE_n_MODE_SHFT 0x0
+
+#define IPA_ENDP_INIT_AGGR_n_OFST(n) (0x000001a0 + 0x4 * (n))
+#define IPA_ENDP_INIT_AGGR_n_RMSK 0x7fff
+#define IPA_ENDP_INIT_AGGR_n_MAXn 19
+#define IPA_ENDP_INIT_AGGR_n_AGGR_TIME_LIMIT_BMSK 0x7c00
+#define IPA_ENDP_INIT_AGGR_n_AGGR_TIME_LIMIT_SHFT 0xa
+#define IPA_ENDP_INIT_AGGR_n_AGGR_BYTE_LIMIT_BMSK 0x3e0
+#define IPA_ENDP_INIT_AGGR_n_AGGR_BYTE_LIMIT_SHFT 0x5
+#define IPA_ENDP_INIT_AGGR_n_AGGR_TYPE_BMSK 0x1c
+#define IPA_ENDP_INIT_AGGR_n_AGGR_TYPE_SHFT 0x2
+#define IPA_ENDP_INIT_AGGR_n_AGGR_EN_BMSK 0x3
+#define IPA_ENDP_INIT_AGGR_n_AGGR_EN_SHFT 0x0
+
+#define IPA_ENDP_INIT_ROUTE_n_OFST(n) (0x00000200 + 0x4 * (n))
+#define IPA_ENDP_INIT_ROUTE_n_RMSK 0x1f
+#define IPA_ENDP_INIT_ROUTE_n_MAXn 19
+#define IPA_ENDP_INIT_ROUTE_n_ROUTE_TABLE_INDEX_BMSK 0x1f
+#define IPA_ENDP_INIT_ROUTE_n_ROUTE_TABLE_INDEX_SHFT 0x0
+
+#define IPA_AGGREGATION_SPARE_REG_1_OFST 0x00002090
+#define IPA_AGGREGATION_SPARE_REG_1_RMSK 0xffffffff
+#define IPA_AGGREGATION_SPARE_REG_1_GENERAL_CONFIG_BMSK 0xffffffff
+#define IPA_AGGREGATION_SPARE_REG_1_GENERAL_CONFIG_SHFT 0x0
+
+#define IPA_AGGREGATION_SPARE_REG_2_OFST 0x00002094
+#define IPA_AGGREGATION_SPARE_REG_2_RMSK 0xffffffff
+#define IPA_AGGREGATION_SPARE_REG_2_GENERAL_CONFIG_BMSK 0xffffffff
+#define IPA_AGGREGATION_SPARE_REG_2_GENERAL_CONFIG_SHFT 0x0
+
+#define IPA_AGGREGATION_MODE_MSK 0x1
+#define IPA_AGGREGATION_MODE_SHFT 31
+#define IPA_AGGREGATION_MODE_BMSK 0x7fffffff
+#define IPA_AGGREGATION_QCNCM_SIG0_SHFT 16
+#define IPA_AGGREGATION_QCNCM_SIG1_SHFT 8
+#define IPA_AGGREGATION_QCNCM_SIG_BMSK 0xff000000
+#define IPA_AGGREGATION_SINGLE_NDP_MSK 0x1
+#define IPA_AGGREGATION_SINGLE_NDP_BMSK 0xfffffffe
+
+#define IPA_SRAM_DIRECT_ACCESS_n_OFST(n) (0x00004000 + 0x4 * (n))
+#define IPA_SRAM_DIRECT_ACCESS_n_RMSK 0xffffffff
+#define IPA_SRAM_DIRECT_ACCESS_n_MAXn 2047
+#define IPA_SRAM_DIRECT_ACCESS_n_DATA_WORD_BMSK 0xffffffff
+#define IPA_SRAM_DIRECT_ACCESS_n_DATA_WORD_SHFT 0x0
+
+#endif /* __IPA_REG_H__ */
diff --git a/drivers/platform/msm/ipa/ipa_rt.c b/drivers/platform/msm/ipa/ipa_rt.c
new file mode 100644
index 0000000..c69e1fb
--- /dev/null
+++ b/drivers/platform/msm/ipa/ipa_rt.c
@@ -0,0 +1,964 @@
+/* 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/bitops.h>
+#include "ipa_i.h"
+
+#define IPA_RT_TABLE_INDEX_NOT_FOUND	(-1)
+#define IPA_RT_TABLE_WORD_SIZE		(4)
+#define IPA_RT_INDEX_BITMAP_SIZE	(32)
+#define IPA_RT_TABLE_MEMORY_ALLIGNMENT	(127)
+#define IPA_RT_ENTRY_MEMORY_ALLIGNMENT	(3)
+#define IPA_RT_BIT_MASK			(0x1)
+#define IPA_RT_STATUS_OF_ADD_FAILED	(-1)
+#define IPA_RT_STATUS_OF_DEL_FAILED	(-1)
+
+/**
+ * ipa_generate_rt_hw_rule() - generates the routing hardware rule
+ * @ip: the ip address family type
+ * @entry: routing entry
+ * @buf: output buffer, buf == NULL means
+ *		caller wants to know the size of the rule as seen
+ *		by HW so they did not pass a valid buffer, we will use a
+ *		scratch buffer instead.
+ *		With this scheme we are going to
+ *		generate the rule twice, once to know size using scratch
+ *		buffer and second to write the rule to the actual caller
+ *		supplied buffer which is of required size
+ *
+ * Returns:	0 on success, negative on failure
+ *
+ * caller needs to hold any needed locks to ensure integrity
+ *
+ */
+static int ipa_generate_rt_hw_rule(enum ipa_ip_type ip,
+		struct ipa_rt_entry *entry, u8 *buf)
+{
+	struct ipa_rt_rule_hw_hdr *rule_hdr;
+	const struct ipa_rt_rule *rule =
+		(const struct ipa_rt_rule *)&entry->rule;
+	u16 en_rule = 0;
+	u8 tmp[IPA_RT_FLT_HW_RULE_BUF_SIZE];
+	u8 *start;
+	int pipe_idx;
+
+	memset(tmp, 0, IPA_RT_FLT_HW_RULE_BUF_SIZE);
+	if (buf == NULL)
+		buf = tmp;
+
+	start = buf;
+	rule_hdr = (struct ipa_rt_rule_hw_hdr *)buf;
+	pipe_idx = ipa_get_ep_mapping(ipa_ctx->mode,
+			entry->rule.dst);
+	if (pipe_idx == -1) {
+		IPAERR("Wrong destination pipe specified in RT rule\n");
+		WARN_ON(1);
+		return -EPERM;
+	}
+	rule_hdr->u.hdr.pipe_dest_idx = pipe_idx;
+	rule_hdr->u.hdr.system = !ipa_ctx->hdr_tbl_lcl;
+	if (entry->hdr)
+		rule_hdr->u.hdr.hdr_offset =
+			entry->hdr->offset_entry->offset >> 2;
+	else
+		rule_hdr->u.hdr.hdr_offset = 0;
+
+	buf += sizeof(struct ipa_rt_rule_hw_hdr);
+	if (ipa_generate_hw_rule(ip, &rule->attrib, &buf, &en_rule)) {
+		IPAERR("fail to generate hw rule\n");
+		return -EPERM;
+	}
+
+	IPADBG("en_rule 0x%x\n", en_rule);
+
+	rule_hdr->u.hdr.en_rule = en_rule;
+	ipa_write_32(rule_hdr->u.word, (u8 *)rule_hdr);
+
+	if (entry->hw_len == 0) {
+		entry->hw_len = buf - start;
+	} else if (entry->hw_len != (buf - start)) {
+			IPAERR(
+			"hw_len differs b/w passes passed=0x%x calc=0x%x\n",
+			entry->hw_len,
+			(buf - start));
+			return -EPERM;
+		}
+
+	return 0;
+}
+
+/**
+ * ipa_get_rt_hw_tbl_size() - returns the size of HW routing table
+ * @ip: the ip address family type
+ * @hdr_sz: header size
+ * @max_rt_idx: maximal index
+ *
+ * Returns:	0 on success, negative on failure
+ *
+ * caller needs to hold any needed locks to ensure integrity
+ *
+ * the MSB set in rt_idx_bitmap indicates the size of hdr of routing tbl
+ */
+static int ipa_get_rt_hw_tbl_size(enum ipa_ip_type ip, u32 *hdr_sz,
+		int *max_rt_idx)
+{
+	struct ipa_rt_tbl_set *set;
+	struct ipa_rt_tbl *tbl;
+	struct ipa_rt_entry *entry;
+	u32 total_sz = 0;
+	u32 tbl_sz;
+	u32 bitmap = ipa_ctx->rt_idx_bitmap[ip];
+	int highest_bit_set = IPA_RT_TABLE_INDEX_NOT_FOUND;
+	int i;
+
+	*hdr_sz = 0;
+	set = &ipa_ctx->rt_tbl_set[ip];
+
+	for (i = 0; i < IPA_RT_INDEX_BITMAP_SIZE; i++) {
+		if (bitmap & IPA_RT_BIT_MASK)
+			highest_bit_set = i;
+		bitmap >>= 1;
+	}
+
+	*max_rt_idx = highest_bit_set;
+	if (highest_bit_set == IPA_RT_TABLE_INDEX_NOT_FOUND) {
+		IPAERR("no rt tbls present\n");
+		total_sz = IPA_RT_TABLE_WORD_SIZE;
+		*hdr_sz = IPA_RT_TABLE_WORD_SIZE;
+		return total_sz;
+	}
+
+	*hdr_sz = (highest_bit_set + 1) * IPA_RT_TABLE_WORD_SIZE;
+	total_sz += *hdr_sz;
+	list_for_each_entry(tbl, &set->head_rt_tbl_list, link) {
+		tbl_sz = 0;
+		list_for_each_entry(entry, &tbl->head_rt_rule_list, link) {
+			if (ipa_generate_rt_hw_rule(ip, entry, NULL)) {
+				IPAERR("failed to find HW RT rule size\n");
+				return -EPERM;
+			}
+			tbl_sz += entry->hw_len;
+		}
+
+		if (tbl_sz)
+			tbl->sz = tbl_sz + IPA_RT_TABLE_WORD_SIZE;
+
+		if (tbl->in_sys)
+			continue;
+
+		if (tbl_sz) {
+			/* add the terminator */
+			total_sz += (tbl_sz + IPA_RT_TABLE_WORD_SIZE);
+			/* every rule-set should start at word boundary */
+			total_sz = (total_sz + IPA_RT_ENTRY_MEMORY_ALLIGNMENT) &
+						~IPA_RT_ENTRY_MEMORY_ALLIGNMENT;
+		}
+	}
+
+	IPADBG("RT HW TBL SZ %d HDR SZ %d IP %d\n", total_sz, *hdr_sz, ip);
+
+	return total_sz;
+}
+
+/**
+ * ipa_generate_rt_hw_tbl() - generates the routing hardware table
+ * @ip:	[in] the ip address family type
+ * @mem:	[out] buffer to put the filtering table
+ *
+ * Returns:	0 on success, negative on failure
+ */
+int ipa_generate_rt_hw_tbl(enum ipa_ip_type ip, struct ipa_mem_buffer *mem)
+{
+	struct ipa_rt_tbl *tbl;
+	struct ipa_rt_entry *entry;
+	struct ipa_rt_tbl_set *set;
+	u32 hdr_sz;
+	u32 offset;
+	u8 *hdr;
+	u8 *body;
+	u8 *base;
+	struct ipa_mem_buffer rt_tbl_mem;
+	u8 *rt_tbl_mem_body;
+	int max_rt_idx;
+	int i;
+
+	mem->size = ipa_get_rt_hw_tbl_size(ip, &hdr_sz, &max_rt_idx);
+	mem->size = (mem->size + IPA_RT_TABLE_MEMORY_ALLIGNMENT) &
+				~IPA_RT_TABLE_MEMORY_ALLIGNMENT;
+
+	if (mem->size == 0) {
+		IPAERR("rt tbl empty ip=%d\n", ip);
+		goto error;
+	}
+	mem->base = dma_alloc_coherent(NULL, mem->size, &mem->phys_base,
+			GFP_KERNEL);
+	if (!mem->base) {
+		IPAERR("fail to alloc DMA buff of size %d\n", mem->size);
+		goto error;
+	}
+
+	memset(mem->base, 0, mem->size);
+
+	/* build the rt tbl in the DMA buffer to submit to IPA HW */
+	base = hdr = (u8 *)mem->base;
+	body = base + hdr_sz;
+
+	/* setup all indices to point to the empty sys rt tbl */
+	for (i = 0; i <= max_rt_idx; i++)
+		ipa_write_32(ipa_ctx->empty_rt_tbl_mem.phys_base,
+				hdr + (i * IPA_RT_TABLE_WORD_SIZE));
+
+	set = &ipa_ctx->rt_tbl_set[ip];
+	list_for_each_entry(tbl, &set->head_rt_tbl_list, link) {
+		offset = body - base;
+		if (offset & IPA_RT_ENTRY_MEMORY_ALLIGNMENT) {
+			IPAERR("offset is not word multiple %d\n", offset);
+			goto proc_err;
+		}
+
+		if (!tbl->in_sys) {
+			/* convert offset to words from bytes */
+			offset &= ~IPA_RT_ENTRY_MEMORY_ALLIGNMENT;
+			/* rule is at an offset from base */
+			offset |= IPA_RT_BIT_MASK;
+
+			/* update the hdr at the right index */
+			ipa_write_32(offset, hdr +
+					(tbl->idx * IPA_RT_TABLE_WORD_SIZE));
+
+			/* generate the rule-set */
+			list_for_each_entry(entry, &tbl->head_rt_rule_list,
+					link) {
+				if (ipa_generate_rt_hw_rule(ip, entry, body)) {
+					IPAERR("failed to gen HW RT rule\n");
+					goto proc_err;
+				}
+				body += entry->hw_len;
+			}
+
+			/* write the rule-set terminator */
+			body = ipa_write_32(0, body);
+			if ((u32)body & IPA_RT_ENTRY_MEMORY_ALLIGNMENT)
+				/* advance body to next word boundary */
+				body = body + (IPA_RT_TABLE_WORD_SIZE -
+					      ((u32)body &
+					      IPA_RT_ENTRY_MEMORY_ALLIGNMENT));
+		} else {
+			WARN_ON(tbl->sz == 0);
+			/* allocate memory for the RT tbl */
+			rt_tbl_mem.size = tbl->sz;
+			rt_tbl_mem.base =
+			   dma_alloc_coherent(NULL, rt_tbl_mem.size,
+					   &rt_tbl_mem.phys_base, GFP_KERNEL);
+			if (!rt_tbl_mem.base) {
+				IPAERR("fail to alloc DMA buff of size %d\n",
+						rt_tbl_mem.size);
+				WARN_ON(1);
+				goto proc_err;
+			}
+
+			WARN_ON(rt_tbl_mem.phys_base &
+					IPA_RT_ENTRY_MEMORY_ALLIGNMENT);
+			rt_tbl_mem_body = rt_tbl_mem.base;
+			memset(rt_tbl_mem.base, 0, rt_tbl_mem.size);
+			/* update the hdr at the right index */
+			ipa_write_32(rt_tbl_mem.phys_base,
+					hdr + (tbl->idx *
+					IPA_RT_TABLE_WORD_SIZE));
+			/* generate the rule-set */
+			list_for_each_entry(entry, &tbl->head_rt_rule_list,
+					link) {
+				if (ipa_generate_rt_hw_rule(ip, entry,
+							rt_tbl_mem_body)) {
+					IPAERR("failed to gen HW RT rule\n");
+					WARN_ON(1);
+					goto rt_table_mem_alloc_failed;
+				}
+				rt_tbl_mem_body += entry->hw_len;
+			}
+
+			/* write the rule-set terminator */
+			rt_tbl_mem_body = ipa_write_32(0, rt_tbl_mem_body);
+
+			if (tbl->curr_mem.phys_base) {
+				WARN_ON(tbl->prev_mem.phys_base);
+				tbl->prev_mem = tbl->curr_mem;
+			}
+			tbl->curr_mem = rt_tbl_mem;
+		}
+	}
+
+	return 0;
+
+rt_table_mem_alloc_failed:
+	dma_free_coherent(NULL, rt_tbl_mem.size,
+			  rt_tbl_mem.base, rt_tbl_mem.phys_base);
+proc_err:
+	dma_free_coherent(NULL, mem->size, mem->base, mem->phys_base);
+error:
+	return -EPERM;
+}
+
+static void __ipa_reap_sys_rt_tbls(enum ipa_ip_type ip)
+{
+	struct ipa_rt_tbl *tbl;
+	struct ipa_rt_tbl *next;
+	struct ipa_rt_tbl_set *set;
+
+	set = &ipa_ctx->rt_tbl_set[ip];
+	list_for_each_entry(tbl, &set->head_rt_tbl_list, link) {
+		if (tbl->prev_mem.phys_base) {
+			IPADBG("reaping rt tbl name=%s ip=%d\n", tbl->name, ip);
+			dma_free_coherent(NULL, tbl->prev_mem.size,
+					tbl->prev_mem.base,
+					tbl->prev_mem.phys_base);
+			memset(&tbl->prev_mem, 0, sizeof(tbl->prev_mem));
+		}
+	}
+
+	set = &ipa_ctx->reap_rt_tbl_set[ip];
+	list_for_each_entry_safe(tbl, next, &set->head_rt_tbl_list, link) {
+		list_del(&tbl->link);
+		WARN_ON(tbl->prev_mem.phys_base != 0);
+		if (tbl->curr_mem.phys_base) {
+			IPADBG("reaping sys rt tbl name=%s ip=%d\n", tbl->name,
+					ip);
+			dma_free_coherent(NULL, tbl->curr_mem.size,
+					tbl->curr_mem.base,
+					tbl->curr_mem.phys_base);
+			kmem_cache_free(ipa_ctx->rt_tbl_cache, tbl);
+		}
+	}
+}
+
+static int __ipa_commit_rt(enum ipa_ip_type ip)
+{
+	struct ipa_desc desc = { 0 };
+	struct ipa_mem_buffer *mem;
+	void *cmd;
+	struct ipa_ip_v4_routing_init *v4;
+	struct ipa_ip_v6_routing_init *v6;
+	u16 avail;
+	u16 size;
+
+	mem = kmalloc(sizeof(struct ipa_mem_buffer), GFP_KERNEL);
+	if (!mem) {
+		IPAERR("failed to alloc memory object\n");
+		goto fail_alloc_mem;
+	}
+
+	if (ip == IPA_IP_v4) {
+		avail = IPA_RAM_V4_RT_SIZE;
+		size = sizeof(struct ipa_ip_v4_routing_init);
+	} else {
+		avail = IPA_RAM_V6_RT_SIZE;
+		size = sizeof(struct ipa_ip_v6_routing_init);
+	}
+	cmd = kmalloc(size, GFP_KERNEL);
+	if (!cmd) {
+		IPAERR("failed to alloc immediate command object\n");
+		goto fail_alloc_cmd;
+	}
+
+	if (ipa_generate_rt_hw_tbl(ip, mem)) {
+		IPAERR("fail to generate RT HW TBL ip %d\n", ip);
+		goto fail_hw_tbl_gen;
+	}
+
+	if (mem->size > avail) {
+		IPAERR("tbl too big, needed %d avail %d\n", mem->size, avail);
+		goto fail_hw_tbl_gen;
+	}
+
+	if (ip == IPA_IP_v4) {
+		v4 = (struct ipa_ip_v4_routing_init *)cmd;
+		desc.opcode = IPA_IP_V4_ROUTING_INIT;
+		v4->ipv4_rules_addr = mem->phys_base;
+		v4->size_ipv4_rules = mem->size;
+		v4->ipv4_addr = IPA_RAM_V4_RT_OFST;
+	} else {
+		v6 = (struct ipa_ip_v6_routing_init *)cmd;
+		desc.opcode = IPA_IP_V6_ROUTING_INIT;
+		v6->ipv6_rules_addr = mem->phys_base;
+		v6->size_ipv6_rules = mem->size;
+		v6->ipv6_addr = IPA_RAM_V6_RT_OFST;
+	}
+
+	desc.pyld = cmd;
+	desc.len = size;
+	desc.type = IPA_IMM_CMD_DESC;
+	IPA_DUMP_BUFF(mem->base, mem->phys_base, mem->size);
+
+	if (ipa_send_cmd(1, &desc)) {
+		IPAERR("fail to send immediate command\n");
+		goto fail_send_cmd;
+	}
+
+	__ipa_reap_sys_rt_tbls(ip);
+	dma_free_coherent(NULL, mem->size, mem->base, mem->phys_base);
+	kfree(cmd);
+	kfree(mem);
+
+	return 0;
+
+fail_send_cmd:
+	if (mem->phys_base)
+		dma_free_coherent(NULL, mem->size, mem->base, mem->phys_base);
+fail_hw_tbl_gen:
+	kfree(cmd);
+fail_alloc_cmd:
+	kfree(mem);
+fail_alloc_mem:
+	return -EPERM;
+}
+
+/**
+ * __ipa_find_rt_tbl() - find the routing table
+ *			which name is given as parameter
+ * @ip:	[in] the ip address family type of the wanted routing table
+ * @name:	[in] the name of the wanted routing table
+ *
+ * Returns: the routing table which name is given as parameter, or NULL if it
+ * doesn't exist
+ */
+struct ipa_rt_tbl *__ipa_find_rt_tbl(enum ipa_ip_type ip, const char *name)
+{
+	struct ipa_rt_tbl *entry;
+	struct ipa_rt_tbl_set *set;
+
+	set = &ipa_ctx->rt_tbl_set[ip];
+	list_for_each_entry(entry, &set->head_rt_tbl_list, link) {
+		if (!strncmp(name, entry->name, IPA_RESOURCE_NAME_MAX))
+			return entry;
+	}
+
+	return NULL;
+}
+
+static struct ipa_rt_tbl *__ipa_add_rt_tbl(enum ipa_ip_type ip,
+		const char *name)
+{
+	struct ipa_rt_tbl *entry;
+	struct ipa_rt_tbl_set *set;
+	struct ipa_tree_node *node;
+	int i;
+
+	node = kmem_cache_zalloc(ipa_ctx->tree_node_cache, GFP_KERNEL);
+	if (!node) {
+		IPAERR("failed to alloc tree node object\n");
+		goto node_alloc_fail;
+	}
+
+	if (ip >= IPA_IP_MAX || name == NULL) {
+		IPAERR("bad parm\n");
+		goto error;
+	}
+
+	set = &ipa_ctx->rt_tbl_set[ip];
+	/* check if this table exists */
+	entry = __ipa_find_rt_tbl(ip, name);
+	if (!entry) {
+		entry = kmem_cache_zalloc(ipa_ctx->rt_tbl_cache, GFP_KERNEL);
+		if (!entry) {
+			IPAERR("failed to alloc RT tbl object\n");
+			goto error;
+		}
+		/* find a routing tbl index */
+		for (i = 0; i < IPA_RT_INDEX_BITMAP_SIZE; i++) {
+			if (!test_bit(i, &ipa_ctx->rt_idx_bitmap[ip])) {
+				entry->idx = i;
+				set_bit(i, &ipa_ctx->rt_idx_bitmap[ip]);
+				break;
+			}
+		}
+		if (i == IPA_RT_INDEX_BITMAP_SIZE) {
+			IPAERR("not free RT tbl indices left\n");
+			goto fail_rt_idx_alloc;
+		}
+
+		INIT_LIST_HEAD(&entry->head_rt_rule_list);
+		INIT_LIST_HEAD(&entry->link);
+		strlcpy(entry->name, name, IPA_RESOURCE_NAME_MAX);
+		entry->set = set;
+		entry->cookie = IPA_COOKIE;
+		entry->in_sys = (ip == IPA_IP_v4) ?
+			!ipa_ctx->ip4_rt_tbl_lcl : !ipa_ctx->ip6_rt_tbl_lcl;
+		set->tbl_cnt++;
+		list_add(&entry->link, &set->head_rt_tbl_list);
+
+		IPADBG("add rt tbl idx=%d tbl_cnt=%d ip=%d\n", entry->idx,
+				set->tbl_cnt, ip);
+
+		node->hdl = (u32)entry;
+		if (ipa_insert(&ipa_ctx->rt_tbl_hdl_tree, node)) {
+			IPAERR("failed to add to tree\n");
+			WARN_ON(1);
+		}
+	}
+
+	return entry;
+
+fail_rt_idx_alloc:
+	entry->cookie = 0;
+	kmem_cache_free(ipa_ctx->rt_tbl_cache, entry);
+error:
+	kmem_cache_free(ipa_ctx->tree_node_cache, node);
+node_alloc_fail:
+	return NULL;
+}
+
+static int __ipa_del_rt_tbl(struct ipa_rt_tbl *entry)
+{
+	struct ipa_tree_node *node;
+	enum ipa_ip_type ip = IPA_IP_MAX;
+
+	if (entry == NULL || (entry->cookie != IPA_COOKIE)) {
+		IPAERR("bad parms\n");
+		return -EINVAL;
+	}
+	node = ipa_search(&ipa_ctx->rt_tbl_hdl_tree, (u32)entry);
+	if (node == NULL) {
+		IPAERR("lookup failed\n");
+		return -EPERM;
+	}
+
+	if (entry->set == &ipa_ctx->rt_tbl_set[IPA_IP_v4])
+		ip = IPA_IP_v4;
+	else if (entry->set == &ipa_ctx->rt_tbl_set[IPA_IP_v6])
+		ip = IPA_IP_v6;
+	else
+		WARN_ON(1);
+
+	if (!entry->in_sys) {
+		list_del(&entry->link);
+		clear_bit(entry->idx, &ipa_ctx->rt_idx_bitmap[ip]);
+		entry->set->tbl_cnt--;
+		IPADBG("del rt tbl_idx=%d tbl_cnt=%d\n", entry->idx,
+				entry->set->tbl_cnt);
+		kmem_cache_free(ipa_ctx->rt_tbl_cache, entry);
+	} else {
+		list_move(&entry->link,
+				&ipa_ctx->reap_rt_tbl_set[ip].head_rt_tbl_list);
+		clear_bit(entry->idx, &ipa_ctx->rt_idx_bitmap[ip]);
+		entry->set->tbl_cnt--;
+		IPADBG("del sys rt tbl_idx=%d tbl_cnt=%d\n", entry->idx,
+				entry->set->tbl_cnt);
+	}
+
+	/* remove the handle from the database */
+	rb_erase(&node->node, &ipa_ctx->rt_tbl_hdl_tree);
+	kmem_cache_free(ipa_ctx->tree_node_cache, node);
+
+	return 0;
+}
+
+static int __ipa_add_rt_rule(enum ipa_ip_type ip, const char *name,
+		const struct ipa_rt_rule *rule, u8 at_rear, u32 *rule_hdl)
+{
+	struct ipa_rt_tbl *tbl;
+	struct ipa_rt_entry *entry;
+	struct ipa_tree_node *node;
+
+	if (rule->hdr_hdl &&
+	    ((ipa_search(&ipa_ctx->hdr_hdl_tree, rule->hdr_hdl) == NULL) ||
+	     ((struct ipa_hdr_entry *)rule->hdr_hdl)->cookie != IPA_COOKIE)) {
+		IPAERR("rt rule does not point to valid hdr\n");
+		goto error;
+	}
+
+	node = kmem_cache_zalloc(ipa_ctx->tree_node_cache, GFP_KERNEL);
+	if (!node) {
+		IPAERR("failed to alloc tree node object\n");
+		goto error;
+	}
+
+	tbl = __ipa_add_rt_tbl(ip, name);
+	if (tbl == NULL || (tbl->cookie != IPA_COOKIE)) {
+		IPAERR("bad params\n");
+		goto fail_rt_tbl_sanity;
+	}
+	/*
+	 * do not allow any rules to be added at end of the "default" routing
+	 * tables
+	 */
+	if (!strncmp(tbl->name, IPA_DFLT_RT_TBL_NAME, IPA_RESOURCE_NAME_MAX) &&
+	    (tbl->rule_cnt > 0) && (at_rear != 0)) {
+		IPAERR("cannot add rule at end of tbl rule_cnt=%d at_rear=%d\n",
+		       tbl->rule_cnt, at_rear);
+		goto fail_rt_tbl_sanity;
+	}
+
+	entry = kmem_cache_zalloc(ipa_ctx->rt_rule_cache, GFP_KERNEL);
+	if (!entry) {
+		IPAERR("failed to alloc RT rule object\n");
+		goto fail_rt_tbl_sanity;
+	}
+	INIT_LIST_HEAD(&entry->link);
+	entry->cookie = IPA_COOKIE;
+	entry->rule = *rule;
+	entry->tbl = tbl;
+	entry->hdr = (struct ipa_hdr_entry *)rule->hdr_hdl;
+	if (at_rear)
+		list_add_tail(&entry->link, &tbl->head_rt_rule_list);
+	else
+		list_add(&entry->link, &tbl->head_rt_rule_list);
+	tbl->rule_cnt++;
+	if (entry->hdr)
+		entry->hdr->ref_cnt++;
+	IPADBG("add rt rule tbl_idx=%d rule_cnt=%d\n", tbl->idx, tbl->rule_cnt);
+	*rule_hdl = (u32)entry;
+
+	node->hdl = *rule_hdl;
+	if (ipa_insert(&ipa_ctx->rt_rule_hdl_tree, node)) {
+		IPAERR("failed to add to tree\n");
+		WARN_ON(1);
+		goto ipa_insert_failed;
+	}
+
+	return 0;
+
+ipa_insert_failed:
+	list_del(&entry->link);
+	kmem_cache_free(ipa_ctx->rt_rule_cache, entry);
+fail_rt_tbl_sanity:
+	kmem_cache_free(ipa_ctx->tree_node_cache, node);
+error:
+	return -EPERM;
+}
+
+/**
+ * ipa_add_rt_rule() - Add the specified routing rules to SW and optionally
+ * commit to IPA HW
+ * @rules:	[inout] set of routing rules to add
+ *
+ * Returns:	0 on success, negative on failure
+ *
+ * Note:	Should not be called from atomic context
+ */
+int ipa_add_rt_rule(struct ipa_ioc_add_rt_rule *rules)
+{
+	int i;
+	int ret;
+
+	if (rules == NULL || rules->num_rules == 0 || rules->ip >= IPA_IP_MAX) {
+		IPAERR("bad parm\n");
+		return -EINVAL;
+	}
+
+	mutex_lock(&ipa_ctx->lock);
+	for (i = 0; i < rules->num_rules; i++) {
+		if (__ipa_add_rt_rule(rules->ip, rules->rt_tbl_name,
+					&rules->rules[i].rule,
+					rules->rules[i].at_rear,
+					&rules->rules[i].rt_rule_hdl)) {
+			IPAERR("failed to add rt rule %d\n", i);
+			rules->rules[i].status = IPA_RT_STATUS_OF_ADD_FAILED;
+		} else {
+			rules->rules[i].status = 0;
+		}
+	}
+
+	if (rules->commit)
+		if (__ipa_commit_rt(rules->ip)) {
+			ret = -EPERM;
+			goto bail;
+		}
+
+	ret = 0;
+bail:
+	mutex_unlock(&ipa_ctx->lock);
+	return ret;
+}
+EXPORT_SYMBOL(ipa_add_rt_rule);
+
+static int __ipa_del_rt_rule(u32 rule_hdl)
+{
+	struct ipa_rt_entry *entry = (struct ipa_rt_entry *)rule_hdl;
+	struct ipa_tree_node *node;
+
+	if (entry == NULL || (entry->cookie != IPA_COOKIE)) {
+		IPAERR("bad params\n");
+		return -EINVAL;
+	}
+	node = ipa_search(&ipa_ctx->rt_rule_hdl_tree, rule_hdl);
+	if (node == NULL) {
+		IPAERR("lookup failed\n");
+		return -EPERM;
+	}
+
+	if (entry->hdr)
+		entry->hdr->ref_cnt--;
+	list_del(&entry->link);
+	entry->tbl->rule_cnt--;
+	IPADBG("del rt rule tbl_idx=%d rule_cnt=%d\n", entry->tbl->idx,
+			entry->tbl->rule_cnt);
+	if (entry->tbl->rule_cnt == 0 && entry->tbl->ref_cnt == 0) {
+		if (__ipa_del_rt_tbl(entry->tbl))
+			IPAERR("fail to del RT tbl\n");
+	}
+	entry->cookie = 0;
+	kmem_cache_free(ipa_ctx->rt_rule_cache, entry);
+
+	/* remove the handle from the database */
+	rb_erase(&node->node, &ipa_ctx->rt_rule_hdl_tree);
+	kmem_cache_free(ipa_ctx->tree_node_cache, node);
+
+	return 0;
+}
+
+/**
+ * ipa_del_rt_rule() - Remove the specified routing rules to SW and optionally
+ * commit to IPA HW
+ * @hdls:	[inout] set of routing rules to delete
+ *
+ * Returns:	0 on success, negative on failure
+ *
+ * Note:	Should not be called from atomic context
+ */
+int ipa_del_rt_rule(struct ipa_ioc_del_rt_rule *hdls)
+{
+	int i;
+	int ret;
+
+	if (hdls == NULL || hdls->num_hdls == 0 || hdls->ip >= IPA_IP_MAX) {
+		IPAERR("bad parm\n");
+		return -EINVAL;
+	}
+
+	mutex_lock(&ipa_ctx->lock);
+	for (i = 0; i < hdls->num_hdls; i++) {
+		if (__ipa_del_rt_rule(hdls->hdl[i].hdl)) {
+			IPAERR("failed to del rt rule %i\n", i);
+			hdls->hdl[i].status = IPA_RT_STATUS_OF_DEL_FAILED;
+		} else {
+			hdls->hdl[i].status = 0;
+		}
+	}
+
+	if (hdls->commit)
+		if (__ipa_commit_rt(hdls->ip)) {
+			ret = -EPERM;
+			goto bail;
+		}
+
+	ret = 0;
+bail:
+	mutex_unlock(&ipa_ctx->lock);
+	return ret;
+}
+EXPORT_SYMBOL(ipa_del_rt_rule);
+
+/**
+ * ipa_commit_rt_rule() - Commit the current SW routing table of specified type
+ * to IPA HW
+ * @ip:	The family of routing tables
+ *
+ * Returns:	0 on success, negative on failure
+ *
+ * Note:	Should not be called from atomic context
+ */
+int ipa_commit_rt(enum ipa_ip_type ip)
+{
+	int ret;
+	/*
+	 * issue a commit on the filtering module of same IP type since
+	 * filtering rules point to routing tables
+	 */
+	if (ipa_commit_flt(ip))
+		return -EPERM;
+
+	mutex_lock(&ipa_ctx->lock);
+	if (__ipa_commit_rt(ip)) {
+		ret = -EPERM;
+		goto bail;
+	}
+
+	ret = 0;
+bail:
+	mutex_unlock(&ipa_ctx->lock);
+	return ret;
+}
+EXPORT_SYMBOL(ipa_commit_rt);
+
+/**
+ * ipa_reset_rt() - reset the current SW routing table of specified type
+ * (does not commit to HW)
+ * @ip:	The family of routing tables
+ *
+ * Returns:	0 on success, negative on failure
+ *
+ * Note:	Should not be called from atomic context
+ */
+int ipa_reset_rt(enum ipa_ip_type ip)
+{
+	struct ipa_rt_tbl *tbl;
+	struct ipa_rt_tbl *tbl_next;
+	struct ipa_rt_tbl_set *set;
+	struct ipa_rt_entry *rule;
+	struct ipa_rt_entry *rule_next;
+	struct ipa_tree_node *node;
+	struct ipa_rt_tbl_set *rset;
+
+	/*
+	 * issue a reset on the filtering module of same IP type since
+	 * filtering rules point to routing tables
+	 */
+	if (ipa_reset_flt(ip))
+		IPAERR("fail to reset flt ip=%d\n", ip);
+
+	set = &ipa_ctx->rt_tbl_set[ip];
+	rset = &ipa_ctx->reap_rt_tbl_set[ip];
+	mutex_lock(&ipa_ctx->lock);
+	IPADBG("reset rt ip=%d\n", ip);
+	list_for_each_entry_safe(tbl, tbl_next, &set->head_rt_tbl_list, link) {
+		list_for_each_entry_safe(rule, rule_next,
+					 &tbl->head_rt_rule_list, link) {
+			node = ipa_search(&ipa_ctx->rt_rule_hdl_tree,
+					  (u32)rule);
+			if (node == NULL)
+				WARN_ON(1);
+
+			/*
+			 * for the "default" routing tbl, remove all but the
+			 *  last rule
+			 */
+			if (tbl->idx == 0 && tbl->rule_cnt == 1)
+				continue;
+
+			list_del(&rule->link);
+			tbl->rule_cnt--;
+			if (rule->hdr)
+				rule->hdr->ref_cnt--;
+			rule->cookie = 0;
+			kmem_cache_free(ipa_ctx->rt_rule_cache, rule);
+
+			/* remove the handle from the database */
+			rb_erase(&node->node, &ipa_ctx->rt_rule_hdl_tree);
+			kmem_cache_free(ipa_ctx->tree_node_cache, node);
+		}
+
+		node = ipa_search(&ipa_ctx->rt_tbl_hdl_tree, (u32)tbl);
+		if (node  == NULL)
+			WARN_ON(1);
+
+		/* do not remove the "default" routing tbl which has index 0 */
+		if (tbl->idx != 0) {
+			if (!tbl->in_sys) {
+				list_del(&tbl->link);
+				set->tbl_cnt--;
+				clear_bit(tbl->idx,
+					  &ipa_ctx->rt_idx_bitmap[ip]);
+				IPADBG("rst rt tbl_idx=%d tbl_cnt=%d\n",
+						tbl->idx, set->tbl_cnt);
+				kmem_cache_free(ipa_ctx->rt_tbl_cache, tbl);
+			} else {
+				list_move(&tbl->link, &rset->head_rt_tbl_list);
+				clear_bit(tbl->idx,
+					  &ipa_ctx->rt_idx_bitmap[ip]);
+				set->tbl_cnt--;
+				IPADBG("rst sys rt tbl_idx=%d tbl_cnt=%d\n",
+						tbl->idx, set->tbl_cnt);
+			}
+			/* remove the handle from the database */
+			rb_erase(&node->node, &ipa_ctx->rt_tbl_hdl_tree);
+			kmem_cache_free(ipa_ctx->tree_node_cache, node);
+		}
+	}
+	mutex_unlock(&ipa_ctx->lock);
+
+	return 0;
+}
+EXPORT_SYMBOL(ipa_reset_rt);
+
+/**
+ * ipa_get_rt_tbl() - lookup the specified routing table and return handle if it
+ * exists, if lookup succeeds the routing table ref cnt is increased
+ * @lookup:	[inout] routing table to lookup and its handle
+ *
+ * Returns:	0 on success, negative on failure
+ *
+ * Note:	Should not be called from atomic context
+ *	Caller should call ipa_put_rt_tbl later if this function succeeds
+ */
+int ipa_get_rt_tbl(struct ipa_ioc_get_rt_tbl *lookup)
+{
+	struct ipa_rt_tbl *entry;
+	int result = -EFAULT;
+
+	if (lookup == NULL || lookup->ip >= IPA_IP_MAX) {
+		IPAERR("bad parm\n");
+		return -EINVAL;
+	}
+	mutex_lock(&ipa_ctx->lock);
+	entry = __ipa_add_rt_tbl(lookup->ip, lookup->name);
+	if (entry && entry->cookie == IPA_COOKIE) {
+		entry->ref_cnt++;
+		lookup->hdl = (uint32_t)entry;
+
+		/* commit for get */
+		if (__ipa_commit_rt(lookup->ip))
+			IPAERR("fail to commit RT tbl\n");
+
+		result = 0;
+	}
+	mutex_unlock(&ipa_ctx->lock);
+
+	return result;
+}
+EXPORT_SYMBOL(ipa_get_rt_tbl);
+
+/**
+ * ipa_put_rt_tbl() - Release the specified routing table handle
+ * @rt_tbl_hdl:	[in] the routing table handle to release
+ *
+ * Returns:	0 on success, negative on failure
+ *
+ * Note:	Should not be called from atomic context
+ */
+int ipa_put_rt_tbl(u32 rt_tbl_hdl)
+{
+	struct ipa_rt_tbl *entry = (struct ipa_rt_tbl *)rt_tbl_hdl;
+	struct ipa_tree_node *node;
+	enum ipa_ip_type ip = IPA_IP_MAX;
+
+	if (entry == NULL || (entry->cookie != IPA_COOKIE) ||
+			entry->ref_cnt == 0) {
+		IPAERR("bad parms\n");
+		return -EINVAL;
+	}
+	node = ipa_search(&ipa_ctx->rt_tbl_hdl_tree, rt_tbl_hdl);
+	if (node == NULL) {
+		IPAERR("lookup failed\n");
+		return -EPERM;
+	}
+
+	if (entry->set == &ipa_ctx->rt_tbl_set[IPA_IP_v4])
+		ip = IPA_IP_v4;
+	else if (entry->set == &ipa_ctx->rt_tbl_set[IPA_IP_v6])
+		ip = IPA_IP_v6;
+	else
+		WARN_ON(1);
+
+	mutex_lock(&ipa_ctx->lock);
+	entry->ref_cnt--;
+	if (entry->ref_cnt == 0 && entry->rule_cnt == 0) {
+		if (__ipa_del_rt_tbl(entry))
+			IPAERR("fail to del RT tbl\n");
+		/* commit for put */
+		if (__ipa_commit_rt(ip))
+			IPAERR("fail to commit RT tbl\n");
+	}
+	mutex_unlock(&ipa_ctx->lock);
+
+	return 0;
+}
+EXPORT_SYMBOL(ipa_put_rt_tbl);
diff --git a/drivers/platform/msm/ipa/ipa_utils.c b/drivers/platform/msm/ipa/ipa_utils.c
new file mode 100644
index 0000000..d5d5566
--- /dev/null
+++ b/drivers/platform/msm/ipa/ipa_utils.c
@@ -0,0 +1,1353 @@
+/* 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 <net/ip.h>
+#include <linux/genalloc.h>	/* gen_pool_alloc() */
+#include <linux/io.h>
+#include "ipa_i.h"
+
+static const int ipa_ofst_meq32[] = { IPA_OFFSET_MEQ32_0,
+					IPA_OFFSET_MEQ32_1, -1 };
+static const int ipa_ofst_meq128[] = { IPA_OFFSET_MEQ128_0,
+					IPA_OFFSET_MEQ128_1, -1 };
+static const int ipa_ihl_ofst_rng16[] = { IPA_IHL_OFFSET_RANGE16_0,
+					IPA_IHL_OFFSET_RANGE16_1, -1 };
+static const int ipa_ihl_ofst_meq32[] = { IPA_IHL_OFFSET_MEQ32_0,
+					IPA_IHL_OFFSET_MEQ32_1, -1 };
+
+static const int ep_mapping[IPA_MODE_MAX][IPA_CLIENT_MAX] = {
+	{ -1, -1, -1, -1, -1, 11, -1, 8, 6, 2, 1, 5, -1, -1, -1, -1, -1, 10, 9, 7, 3, 4 },
+	{ -1, -1, -1, -1, -1, 11, -1, 8, 6, 2, 1, 5, -1, -1, -1, -1, -1, 10, 9, 7, 3, 4 },
+	{ 11, 13, 15, 17, 19, -1, -1, 8, 6, 2, 1, 5, 10, 12, 14, 16, 18, -1, 9, 7, 3, 4 },
+	{ 19, -1, -1, -1, -1, 11, 15, 8, 6, 2, 1, 5, 14, 16, 17, 18, -1, 10, 9, 7, 3, 4 },
+	{ 19, -1, -1, -1, -1, 11, 15, 8, 6, 2, 1, 5, 14, 16, 17, 18, -1, 10, 9, 7, 3, 4 },
+	{ 19, -1, -1, -1, -1, 11, 15, 8, 6, 2, 1, 5, 14, 16, 17, 18, -1, 10, 9, 7, 3, 4 },
+};
+
+/**
+ * ipa_cfg_route() - configure IPA route
+ * @route: IPA route
+ *
+ * Return codes:
+ * 0: success
+ */
+int ipa_cfg_route(struct ipa_route *route)
+{
+	ipa_write_reg(ipa_ctx->mmio, IPA_ROUTE_OFST,
+		     IPA_SETFIELD(route->route_dis,
+				  IPA_ROUTE_ROUTE_DIS_SHFT,
+				  IPA_ROUTE_ROUTE_DIS_BMSK) |
+			IPA_SETFIELD(route->route_def_pipe,
+				     IPA_ROUTE_ROUTE_DEF_PIPE_SHFT,
+				     IPA_ROUTE_ROUTE_DEF_PIPE_BMSK) |
+			IPA_SETFIELD(route->route_def_hdr_table,
+				     IPA_ROUTE_ROUTE_DEF_HDR_TABLE_SHFT,
+				     IPA_ROUTE_ROUTE_DEF_HDR_TABLE_BMSK) |
+			IPA_SETFIELD(route->route_def_hdr_ofst,
+				     IPA_ROUTE_ROUTE_DEF_HDR_OFST_SHFT,
+				     IPA_ROUTE_ROUTE_DEF_HDR_OFST_BMSK));
+
+	return 0;
+}
+/**
+ * ipa_cfg_filter() - configure filter
+ * @disable: disable value
+ *
+ * Return codes:
+ * 0: success
+ */
+int ipa_cfg_filter(u32 disable)
+{
+	ipa_write_reg(ipa_ctx->mmio, IPA_FILTER_OFST,
+		     IPA_SETFIELD(!disable,
+				  IPA_FILTER_FILTER_EN_SHFT,
+				  IPA_FILTER_FILTER_EN_BMSK));
+	return 0;
+}
+
+/**
+ * ipa_init_hw() - initialize HW
+ *
+ * Return codes:
+ * 0: success
+ */
+int ipa_init_hw(void)
+{
+	u32 ipa_version = 0;
+
+	/* do soft reset of IPA */
+	ipa_write_reg(ipa_ctx->mmio, IPA_COMP_SW_RESET_OFST, 1);
+	ipa_write_reg(ipa_ctx->mmio, IPA_COMP_SW_RESET_OFST, 0);
+
+	/* enable IPA */
+	ipa_write_reg(ipa_ctx->mmio, IPA_COMP_CFG_OFST, 1);
+
+	/* Read IPA version and make sure we have access to the registers */
+	ipa_version = ipa_read_reg(ipa_ctx->mmio, IPA_VERSION_OFST);
+	if (ipa_version == 0)
+		return -EFAULT;
+
+	return 0;
+}
+
+/**
+ * ipa_get_ep_mapping() - provide endpoint mapping
+ * @mode: IPA operating mode
+ * @client: client type
+ *
+ * Return value: endpoint mapping
+ */
+int ipa_get_ep_mapping(enum ipa_operating_mode mode,
+		enum ipa_client_type client)
+{
+	return ep_mapping[mode][client];
+}
+
+/**
+ * ipa_write_32() - convert 32 bit value to byte array
+ * @w: 32 bit integer
+ * @dest: byte array
+ *
+ * Return value: converted value
+ */
+u8 *ipa_write_32(u32 w, u8 *dest)
+{
+	*dest++ = (u8)((w) & 0xFF);
+	*dest++ = (u8)((w >> 8) & 0xFF);
+	*dest++ = (u8)((w >> 16) & 0xFF);
+	*dest++ = (u8)((w >> 24) & 0xFF);
+
+	return dest;
+}
+
+/**
+ * ipa_write_16() - convert 16 bit value to byte array
+ * @hw: 16 bit integer
+ * @dest: byte array
+ *
+ * Return value: converted value
+ */
+u8 *ipa_write_16(u16 hw, u8 *dest)
+{
+	*dest++ = (u8)((hw) & 0xFF);
+	*dest++ = (u8)((hw >> 8) & 0xFF);
+
+	return dest;
+}
+
+/**
+ * ipa_write_8() - convert 8 bit value to byte array
+ * @hw: 8 bit integer
+ * @dest: byte array
+ *
+ * Return value: converted value
+ */
+u8 *ipa_write_8(u8 b, u8 *dest)
+{
+	*dest++ = (b) & 0xFF;
+
+	return dest;
+}
+
+/**
+ * ipa_pad_to_32() - pad byte array to 32 bit value
+ * @dest: byte array
+ *
+ * Return value: padded value
+ */
+u8 *ipa_pad_to_32(u8 *dest)
+{
+	int i = (u32)dest & 0x3;
+	int j;
+
+	if (i)
+		for (j = 0; j < (4 - i); j++)
+			*dest++ = 0;
+
+	return dest;
+}
+
+/**
+ * ipa_generate_hw_rule() - generate HW rule
+ * @ip: IP address type
+ * @attrib: IPA rule attribute
+ * @buf: output buffer
+ * @en_rule: rule
+ *
+ * Return codes:
+ * 0: success
+ * -EPERM: wrong input
+ */
+int ipa_generate_hw_rule(enum ipa_ip_type ip,
+		const struct ipa_rule_attrib *attrib, u8 **buf, u16 *en_rule)
+{
+	u8 ofst_meq32 = 0;
+	u8 ihl_ofst_rng16 = 0;
+	u8 ihl_ofst_meq32 = 0;
+	u8 ofst_meq128 = 0;
+
+	if (ip == IPA_IP_v4) {
+
+		/* error check */
+		if (attrib->attrib_mask & IPA_FLT_NEXT_HDR ||
+		    attrib->attrib_mask & IPA_FLT_TC || attrib->attrib_mask &
+		    IPA_FLT_FLOW_LABEL) {
+			IPAERR("v6 attrib's specified for v4 rule\n");
+			return -EPERM;
+		}
+
+		if (attrib->attrib_mask & IPA_FLT_TOS) {
+			*en_rule |= IPA_TOS_EQ;
+			*buf = ipa_write_8(attrib->u.v4.tos, *buf);
+			*buf = ipa_pad_to_32(*buf);
+		}
+
+		if (attrib->attrib_mask & IPA_FLT_PROTOCOL) {
+			*en_rule |= IPA_PROTOCOL_EQ;
+			*buf = ipa_write_8(attrib->u.v4.protocol, *buf);
+			*buf = ipa_pad_to_32(*buf);
+		}
+
+		if (attrib->attrib_mask & IPA_FLT_SRC_ADDR) {
+			if (ipa_ofst_meq32[ofst_meq32] == -1) {
+				IPAERR("ran out of meq32 eq\n");
+				return -EPERM;
+			}
+			*en_rule |= ipa_ofst_meq32[ofst_meq32];
+			/* 12 => offset of src ip in v4 header */
+			*buf = ipa_write_8(12, *buf);
+			*buf = ipa_write_32(attrib->u.v4.src_addr_mask, *buf);
+			*buf = ipa_write_32(attrib->u.v4.src_addr, *buf);
+			*buf = ipa_pad_to_32(*buf);
+			ofst_meq32++;
+		}
+
+		if (attrib->attrib_mask & IPA_FLT_DST_ADDR) {
+			if (ipa_ofst_meq32[ofst_meq32] == -1) {
+				IPAERR("ran out of meq32 eq\n");
+				return -EPERM;
+			}
+			*en_rule |= ipa_ofst_meq32[ofst_meq32];
+			/* 16 => offset of dst ip in v4 header */
+			*buf = ipa_write_8(16, *buf);
+			*buf = ipa_write_32(attrib->u.v4.dst_addr_mask, *buf);
+			*buf = ipa_write_32(attrib->u.v4.dst_addr, *buf);
+			*buf = ipa_pad_to_32(*buf);
+			ofst_meq32++;
+		}
+
+		if (attrib->attrib_mask & IPA_FLT_SRC_PORT_RANGE) {
+			if (ipa_ihl_ofst_rng16[ihl_ofst_rng16] == -1) {
+				IPAERR("ran out of ihl_rng16 eq\n");
+				return -EPERM;
+			}
+			if (attrib->src_port_hi < attrib->src_port_lo) {
+				IPAERR("bad src port range param\n");
+				return -EPERM;
+			}
+			*en_rule |= ipa_ihl_ofst_rng16[ihl_ofst_rng16];
+			/* 0  => offset of src port after v4 header */
+			*buf = ipa_write_8(0, *buf);
+			*buf = ipa_write_16(attrib->src_port_hi, *buf);
+			*buf = ipa_write_16(attrib->src_port_lo, *buf);
+			*buf = ipa_pad_to_32(*buf);
+			ihl_ofst_rng16++;
+		}
+
+		if (attrib->attrib_mask & IPA_FLT_DST_PORT_RANGE) {
+			if (ipa_ihl_ofst_rng16[ihl_ofst_rng16] == -1) {
+				IPAERR("ran out of ihl_rng16 eq\n");
+				return -EPERM;
+			}
+			if (attrib->dst_port_hi < attrib->dst_port_lo) {
+				IPAERR("bad dst port range param\n");
+				return -EPERM;
+			}
+			*en_rule |= ipa_ihl_ofst_rng16[ihl_ofst_rng16];
+			/* 2  => offset of dst port after v4 header */
+			*buf = ipa_write_8(2, *buf);
+			*buf = ipa_write_16(attrib->dst_port_hi, *buf);
+			*buf = ipa_write_16(attrib->dst_port_lo, *buf);
+			*buf = ipa_pad_to_32(*buf);
+			ihl_ofst_rng16++;
+		}
+
+		if (attrib->attrib_mask & IPA_FLT_TYPE) {
+			if (ipa_ihl_ofst_meq32[ihl_ofst_meq32] == -1) {
+				IPAERR("ran out of ihl_meq32 eq\n");
+				return -EPERM;
+			}
+			*en_rule |= ipa_ihl_ofst_meq32[ihl_ofst_meq32];
+			/* 0  => offset of type after v4 header */
+			*buf = ipa_write_8(0, *buf);
+			*buf = ipa_write_32(0xFF, *buf);
+			*buf = ipa_write_32(attrib->type, *buf);
+			*buf = ipa_pad_to_32(*buf);
+			ihl_ofst_meq32++;
+		}
+
+		if (attrib->attrib_mask & IPA_FLT_CODE) {
+			if (ipa_ihl_ofst_meq32[ihl_ofst_meq32] == -1) {
+				IPAERR("ran out of ihl_meq32 eq\n");
+				return -EPERM;
+			}
+			*en_rule |= ipa_ihl_ofst_meq32[ihl_ofst_meq32];
+			/* 1  => offset of code after v4 header */
+			*buf = ipa_write_8(1, *buf);
+			*buf = ipa_write_32(0xFF, *buf);
+			*buf = ipa_write_32(attrib->code, *buf);
+			*buf = ipa_pad_to_32(*buf);
+			ihl_ofst_meq32++;
+		}
+
+		if (attrib->attrib_mask & IPA_FLT_SPI) {
+			if (ipa_ihl_ofst_meq32[ihl_ofst_meq32] == -1) {
+				IPAERR("ran out of ihl_meq32 eq\n");
+				return -EPERM;
+			}
+			*en_rule |= ipa_ihl_ofst_meq32[ihl_ofst_meq32];
+			/* 0  => offset of SPI after v4 header FIXME */
+			*buf = ipa_write_8(0, *buf);
+			*buf = ipa_write_32(0xFFFFFFFF, *buf);
+			*buf = ipa_write_32(attrib->spi, *buf);
+			*buf = ipa_pad_to_32(*buf);
+			ihl_ofst_meq32++;
+		}
+
+		if (attrib->attrib_mask & IPA_FLT_SRC_PORT) {
+			if (ipa_ihl_ofst_rng16[ihl_ofst_rng16] == -1) {
+				IPAERR("ran out of ihl_rng16 eq\n");
+				return -EPERM;
+			}
+			*en_rule |= ipa_ihl_ofst_rng16[ihl_ofst_rng16];
+			/* 0  => offset of src port after v4 header */
+			*buf = ipa_write_8(0, *buf);
+			*buf = ipa_write_16(attrib->src_port, *buf);
+			*buf = ipa_write_16(attrib->src_port, *buf);
+			*buf = ipa_pad_to_32(*buf);
+			ihl_ofst_rng16++;
+		}
+
+		if (attrib->attrib_mask & IPA_FLT_DST_PORT) {
+			if (ipa_ihl_ofst_rng16[ihl_ofst_rng16] == -1) {
+				IPAERR("ran out of ihl_rng16 eq\n");
+				return -EPERM;
+			}
+			*en_rule |= ipa_ihl_ofst_rng16[ihl_ofst_rng16];
+			/* 2  => offset of dst port after v4 header */
+			*buf = ipa_write_8(2, *buf);
+			*buf = ipa_write_16(attrib->dst_port, *buf);
+			*buf = ipa_write_16(attrib->dst_port, *buf);
+			*buf = ipa_pad_to_32(*buf);
+			ihl_ofst_rng16++;
+		}
+
+		if (attrib->attrib_mask & IPA_FLT_META_DATA) {
+			*en_rule |= IPA_METADATA_COMPARE;
+			*buf = ipa_write_8(0, *buf);    /* offset, reserved */
+			*buf = ipa_write_32(attrib->meta_data_mask, *buf);
+			*buf = ipa_write_32(attrib->meta_data, *buf);
+			*buf = ipa_pad_to_32(*buf);
+		}
+
+		if (attrib->attrib_mask & IPA_FLT_FRAGMENT) {
+			*en_rule |= IPA_IPV4_IS_FRAG;
+			*buf = ipa_pad_to_32(*buf);
+		}
+
+	} else if (ip == IPA_IP_v6) {
+
+		/* v6 code below assumes no extension headers TODO: fix this */
+
+		/* error check */
+		if (attrib->attrib_mask & IPA_FLT_TOS ||
+		    attrib->attrib_mask & IPA_FLT_PROTOCOL ||
+		    attrib->attrib_mask & IPA_FLT_FRAGMENT) {
+			IPAERR("v4 attrib's specified for v6 rule\n");
+			return -EPERM;
+		}
+
+		if (attrib->attrib_mask & IPA_FLT_NEXT_HDR) {
+			*en_rule |= IPA_PROTOCOL_EQ;
+			*buf = ipa_write_8(attrib->u.v6.next_hdr, *buf);
+			*buf = ipa_pad_to_32(*buf);
+		}
+
+		if (attrib->attrib_mask & IPA_FLT_TYPE) {
+			if (ipa_ihl_ofst_meq32[ihl_ofst_meq32] == -1) {
+				IPAERR("ran out of ihl_meq32 eq\n");
+				return -EPERM;
+			}
+			*en_rule |= ipa_ihl_ofst_meq32[ihl_ofst_meq32];
+			/* 0  => offset of type after v6 header */
+			*buf = ipa_write_8(0, *buf);
+			*buf = ipa_write_32(0xFF, *buf);
+			*buf = ipa_write_32(attrib->type, *buf);
+			*buf = ipa_pad_to_32(*buf);
+			ihl_ofst_meq32++;
+		}
+
+		if (attrib->attrib_mask & IPA_FLT_CODE) {
+			if (ipa_ihl_ofst_meq32[ihl_ofst_meq32] == -1) {
+				IPAERR("ran out of ihl_meq32 eq\n");
+				return -EPERM;
+			}
+			*en_rule |= ipa_ihl_ofst_meq32[ihl_ofst_meq32];
+			/* 1  => offset of code after v6 header */
+			*buf = ipa_write_8(1, *buf);
+			*buf = ipa_write_32(0xFF, *buf);
+			*buf = ipa_write_32(attrib->code, *buf);
+			*buf = ipa_pad_to_32(*buf);
+			ihl_ofst_meq32++;
+		}
+
+		if (attrib->attrib_mask & IPA_FLT_SPI) {
+			if (ipa_ihl_ofst_meq32[ihl_ofst_meq32] == -1) {
+				IPAERR("ran out of ihl_meq32 eq\n");
+				return -EPERM;
+			}
+			*en_rule |= ipa_ihl_ofst_meq32[ihl_ofst_meq32];
+			/* 0  => offset of SPI after v6 header FIXME */
+			*buf = ipa_write_8(0, *buf);
+			*buf = ipa_write_32(0xFFFFFFFF, *buf);
+			*buf = ipa_write_32(attrib->spi, *buf);
+			*buf = ipa_pad_to_32(*buf);
+			ihl_ofst_meq32++;
+		}
+
+		if (attrib->attrib_mask & IPA_FLT_SRC_PORT) {
+			if (ipa_ihl_ofst_rng16[ihl_ofst_rng16] == -1) {
+				IPAERR("ran out of ihl_rng16 eq\n");
+				return -EPERM;
+			}
+			*en_rule |= ipa_ihl_ofst_rng16[ihl_ofst_rng16];
+			/* 0  => offset of src port after v6 header */
+			*buf = ipa_write_8(0, *buf);
+			*buf = ipa_write_16(attrib->src_port, *buf);
+			*buf = ipa_write_16(attrib->src_port, *buf);
+			*buf = ipa_pad_to_32(*buf);
+			ihl_ofst_rng16++;
+		}
+
+		if (attrib->attrib_mask & IPA_FLT_DST_PORT) {
+			if (ipa_ihl_ofst_rng16[ihl_ofst_rng16] == -1) {
+				IPAERR("ran out of ihl_rng16 eq\n");
+				return -EPERM;
+			}
+			*en_rule |= ipa_ihl_ofst_rng16[ihl_ofst_rng16];
+			/* 2  => offset of dst port after v6 header */
+			*buf = ipa_write_8(2, *buf);
+			*buf = ipa_write_16(attrib->dst_port, *buf);
+			*buf = ipa_write_16(attrib->dst_port, *buf);
+			*buf = ipa_pad_to_32(*buf);
+			ihl_ofst_rng16++;
+		}
+
+		if (attrib->attrib_mask & IPA_FLT_SRC_PORT_RANGE) {
+			if (ipa_ihl_ofst_rng16[ihl_ofst_rng16] == -1) {
+				IPAERR("ran out of ihl_rng16 eq\n");
+				return -EPERM;
+			}
+			if (attrib->src_port_hi < attrib->src_port_lo) {
+				IPAERR("bad src port range param\n");
+				return -EPERM;
+			}
+			*en_rule |= ipa_ihl_ofst_rng16[ihl_ofst_rng16];
+			/* 0  => offset of src port after v6 header */
+			*buf = ipa_write_8(0, *buf);
+			*buf = ipa_write_16(attrib->src_port_hi, *buf);
+			*buf = ipa_write_16(attrib->src_port_lo, *buf);
+			*buf = ipa_pad_to_32(*buf);
+			ihl_ofst_rng16++;
+		}
+
+		if (attrib->attrib_mask & IPA_FLT_DST_PORT_RANGE) {
+			if (ipa_ihl_ofst_rng16[ihl_ofst_rng16] == -1) {
+				IPAERR("ran out of ihl_rng16 eq\n");
+				return -EPERM;
+			}
+			if (attrib->dst_port_hi < attrib->dst_port_lo) {
+				IPAERR("bad dst port range param\n");
+				return -EPERM;
+			}
+			*en_rule |= ipa_ihl_ofst_rng16[ihl_ofst_rng16];
+			/* 2  => offset of dst port after v6 header */
+			*buf = ipa_write_8(2, *buf);
+			*buf = ipa_write_16(attrib->dst_port_hi, *buf);
+			*buf = ipa_write_16(attrib->dst_port_lo, *buf);
+			*buf = ipa_pad_to_32(*buf);
+			ihl_ofst_rng16++;
+		}
+
+		if (attrib->attrib_mask & IPA_FLT_SRC_ADDR) {
+			if (ipa_ofst_meq128[ofst_meq128] == -1) {
+				IPAERR("ran out of meq128 eq\n");
+				return -EPERM;
+			}
+			*en_rule |= ipa_ofst_meq128[ofst_meq128];
+			/* 8 => offset of src ip in v6 header */
+			*buf = ipa_write_8(8, *buf);
+			*buf = ipa_write_32(attrib->u.v6.src_addr_mask[0],
+					*buf);
+			*buf = ipa_write_32(attrib->u.v6.src_addr_mask[1],
+					*buf);
+			*buf = ipa_write_32(attrib->u.v6.src_addr_mask[2],
+					*buf);
+			*buf = ipa_write_32(attrib->u.v6.src_addr_mask[3],
+					*buf);
+			*buf = ipa_write_32(attrib->u.v6.src_addr[0], *buf);
+			*buf = ipa_write_32(attrib->u.v6.src_addr[1], *buf);
+			*buf = ipa_write_32(attrib->u.v6.src_addr[2], *buf);
+			*buf = ipa_write_32(attrib->u.v6.src_addr[3], *buf);
+			*buf = ipa_pad_to_32(*buf);
+			ofst_meq128++;
+		}
+
+		if (attrib->attrib_mask & IPA_FLT_DST_ADDR) {
+			if (ipa_ofst_meq128[ofst_meq128] == -1) {
+				IPAERR("ran out of meq128 eq\n");
+				return -EPERM;
+			}
+			*en_rule |= ipa_ofst_meq128[ofst_meq128];
+			/* 24 => offset of dst ip in v6 header */
+			*buf = ipa_write_8(24, *buf);
+			*buf = ipa_write_32(attrib->u.v6.dst_addr_mask[0],
+					*buf);
+			*buf = ipa_write_32(attrib->u.v6.dst_addr_mask[1],
+					*buf);
+			*buf = ipa_write_32(attrib->u.v6.dst_addr_mask[2],
+					*buf);
+			*buf = ipa_write_32(attrib->u.v6.dst_addr_mask[3],
+					*buf);
+			*buf = ipa_write_32(attrib->u.v6.dst_addr[0], *buf);
+			*buf = ipa_write_32(attrib->u.v6.dst_addr[1], *buf);
+			*buf = ipa_write_32(attrib->u.v6.dst_addr[2], *buf);
+			*buf = ipa_write_32(attrib->u.v6.dst_addr[3], *buf);
+			*buf = ipa_pad_to_32(*buf);
+			ofst_meq128++;
+		}
+
+		if (attrib->attrib_mask & IPA_FLT_TC) {
+			*en_rule |= IPA_FLT_TC;
+			*buf = ipa_write_8(attrib->u.v6.tc, *buf);
+			*buf = ipa_pad_to_32(*buf);
+		}
+
+		if (attrib->attrib_mask & IPA_FLT_FLOW_LABEL) {
+			*en_rule |= IPA_FLT_FLOW_LABEL;
+			 /* FIXME FL is only 20 bits */
+			*buf = ipa_write_32(attrib->u.v6.flow_label, *buf);
+			*buf = ipa_pad_to_32(*buf);
+		}
+
+		if (attrib->attrib_mask & IPA_FLT_META_DATA) {
+			*en_rule |= IPA_METADATA_COMPARE;
+			*buf = ipa_write_8(0, *buf);    /* offset, reserved */
+			*buf = ipa_write_32(attrib->meta_data_mask, *buf);
+			*buf = ipa_write_32(attrib->meta_data, *buf);
+			*buf = ipa_pad_to_32(*buf);
+		}
+
+	} else {
+		IPAERR("unsupported ip %d\n", ip);
+		return -EPERM;
+	}
+
+	/*
+	 * default "rule" means no attributes set -> map to
+	 * OFFSET_MEQ32_0 with mask of 0 and val of 0 and offset 0
+	 */
+	if (attrib->attrib_mask == 0) {
+		if (ipa_ofst_meq32[ofst_meq32] == -1) {
+			IPAERR("ran out of meq32 eq\n");
+			return -EPERM;
+		}
+		*en_rule |= ipa_ofst_meq32[ofst_meq32];
+		*buf = ipa_write_8(0, *buf);    /* offset */
+		*buf = ipa_write_32(0, *buf);   /* mask */
+		*buf = ipa_write_32(0, *buf);   /* val */
+		*buf = ipa_pad_to_32(*buf);
+		ofst_meq32++;
+	}
+
+	return 0;
+}
+
+/**
+ * ipa_cfg_ep - IPA end-point configuration
+ * @clnt_hdl:	[in] opaque client handle assigned by IPA to client
+ * @ipa_ep_cfg:	[in] IPA end-point configuration params
+ *
+ * This includes nat, header, mode, aggregation and route settings and is a one
+ * shot API to configure the IPA end-point fully
+ *
+ * Returns:	0 on success, negative on failure
+ *
+ * Note:	Should not be called from atomic context
+ */
+int ipa_cfg_ep(u32 clnt_hdl, const struct ipa_ep_cfg *ipa_ep_cfg)
+{
+	int result = -EINVAL;
+
+	if (clnt_hdl >= IPA_NUM_PIPES || ipa_ctx->ep[clnt_hdl].valid == 0 ||
+			ipa_ep_cfg == NULL) {
+		IPAERR("bad parm.\n");
+		return -EINVAL;
+	}
+
+	result = ipa_cfg_ep_hdr(clnt_hdl, &ipa_ep_cfg->hdr);
+	if (result)
+		return result;
+
+	result = ipa_cfg_ep_aggr(clnt_hdl, &ipa_ep_cfg->aggr);
+	if (result)
+		return result;
+
+	if (IPA_CLIENT_IS_PROD(ipa_ctx->ep[clnt_hdl].client)) {
+		result = ipa_cfg_ep_nat(clnt_hdl, &ipa_ep_cfg->nat);
+		if (result)
+			return result;
+
+		result = ipa_cfg_ep_mode(clnt_hdl, &ipa_ep_cfg->mode);
+		if (result)
+			return result;
+
+		result = ipa_cfg_ep_route(clnt_hdl, &ipa_ep_cfg->route);
+		if (result)
+			return result;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(ipa_cfg_ep);
+
+/**
+ * ipa_cfg_ep_nat() - IPA end-point NAT configuration
+ * @clnt_hdl:	[in] opaque client handle assigned by IPA to client
+ * @ipa_ep_cfg:	[in] IPA end-point configuration params
+ *
+ * Returns:	0 on success, negative on failure
+ *
+ * Note:	Should not be called from atomic context
+ */
+int ipa_cfg_ep_nat(u32 clnt_hdl, const struct ipa_ep_cfg_nat *ipa_ep_cfg)
+{
+	if (clnt_hdl >= IPA_NUM_PIPES || ipa_ctx->ep[clnt_hdl].valid == 0 ||
+			ipa_ep_cfg == NULL) {
+		IPAERR("bad parm.\n");
+		return -EINVAL;
+	}
+
+	if (IPA_CLIENT_IS_CONS(ipa_ctx->ep[clnt_hdl].client)) {
+		IPAERR("NAT does not apply to IPA out EP %d\n", clnt_hdl);
+		return -EINVAL;
+	}
+	/* copy over EP cfg */
+	ipa_ctx->ep[clnt_hdl].cfg.nat = *ipa_ep_cfg;
+	/* clnt_hdl is used as pipe_index */
+	ipa_write_reg(ipa_ctx->mmio, IPA_ENDP_INIT_NAT_n_OFST(clnt_hdl),
+		      IPA_SETFIELD(ipa_ctx->ep[clnt_hdl].cfg.nat.nat_en,
+				   IPA_ENDP_INIT_NAT_n_NAT_EN_SHFT,
+				   IPA_ENDP_INIT_NAT_n_NAT_EN_BMSK));
+	return 0;
+}
+EXPORT_SYMBOL(ipa_cfg_ep_nat);
+
+/**
+ * ipa_cfg_ep_hdr() -  IPA end-point header configuration
+ * @clnt_hdl:	[in] opaque client handle assigned by IPA to client
+ * @ipa_ep_cfg:	[in] IPA end-point configuration params
+ *
+ * Returns:	0 on success, negative on failure
+ *
+ * Note:	Should not be called from atomic context
+ */
+int ipa_cfg_ep_hdr(u32 clnt_hdl, const struct ipa_ep_cfg_hdr *ipa_ep_cfg)
+{
+	u32 val;
+	struct ipa_ep_context *ep;
+
+	if (clnt_hdl >= IPA_NUM_PIPES || ipa_ctx->ep[clnt_hdl].valid == 0 ||
+			ipa_ep_cfg == NULL) {
+		IPAERR("bad parm.\n");
+		return -EINVAL;
+	}
+
+	ep = &ipa_ctx->ep[clnt_hdl];
+
+	/* copy over EP cfg */
+	ep->cfg.hdr = *ipa_ep_cfg;
+
+	val = IPA_SETFIELD(ep->cfg.hdr.hdr_len,
+		   IPA_ENDP_INIT_HDR_n_HDR_LEN_SHFT,
+		   IPA_ENDP_INIT_HDR_n_HDR_LEN_BMSK) |
+	      IPA_SETFIELD(ep->cfg.hdr.hdr_ofst_metadata_valid,
+		   IPA_ENDP_INIT_HDR_n_HDR_OFST_METADATA_VALID_SHFT,
+		   IPA_ENDP_INIT_HDR_n_HDR_OFST_METADATA_VALID_BMSK) |
+	      IPA_SETFIELD(ep->cfg.hdr.hdr_ofst_metadata,
+		   IPA_ENDP_INIT_HDR_n_HDR_OFST_METADATA_SHFT,
+		   IPA_ENDP_INIT_HDR_n_HDR_OFST_METADATA_BMSK) |
+	      IPA_SETFIELD(ep->cfg.hdr.hdr_additional_const_len,
+		   IPA_ENDP_INIT_HDR_n_HDR_ADDITIONAL_CONST_LEN_SHFT,
+		   IPA_ENDP_INIT_HDR_n_HDR_ADDITIONAL_CONST_LEN_BMSK) |
+	      IPA_SETFIELD(ep->cfg.hdr.hdr_ofst_pkt_size_valid,
+		   IPA_ENDP_INIT_HDR_n_HDR_OFST_PKT_SIZE_VALID_SHFT,
+		   IPA_ENDP_INIT_HDR_n_HDR_OFST_PKT_SIZE_VALID_BMSK) |
+	      IPA_SETFIELD(ep->cfg.hdr.hdr_ofst_pkt_size,
+		   IPA_ENDP_INIT_HDR_n_HDR_OFST_PKT_SIZE_SHFT,
+		   IPA_ENDP_INIT_HDR_n_HDR_OFST_PKT_SIZE_BMSK) |
+	      IPA_SETFIELD(ep->cfg.hdr.hdr_a5_mux,
+		   IPA_ENDP_INIT_HDR_n_HDR_A5_MUX_SHFT,
+		   IPA_ENDP_INIT_HDR_n_HDR_A5_MUX_BMSK);
+
+	ipa_write_reg(ipa_ctx->mmio, IPA_ENDP_INIT_HDR_n_OFST(clnt_hdl), val);
+
+	return 0;
+}
+EXPORT_SYMBOL(ipa_cfg_ep_hdr);
+
+/**
+ * ipa_cfg_ep_mode() - IPA end-point mode configuration
+ * @clnt_hdl:	[in] opaque client handle assigned by IPA to client
+ * @ipa_ep_cfg:	[in] IPA end-point configuration params
+ *
+ * Returns:	0 on success, negative on failure
+ *
+ * Note:	Should not be called from atomic context
+ */
+int ipa_cfg_ep_mode(u32 clnt_hdl, const struct ipa_ep_cfg_mode *ipa_ep_cfg)
+{
+	u32 val;
+
+	if (clnt_hdl >= IPA_NUM_PIPES || ipa_ctx->ep[clnt_hdl].valid == 0 ||
+			ipa_ep_cfg == NULL) {
+		IPAERR("bad parm.\n");
+		return -EINVAL;
+	}
+
+	if (IPA_CLIENT_IS_CONS(ipa_ctx->ep[clnt_hdl].client)) {
+		IPAERR("MODE does not apply to IPA out EP %d\n", clnt_hdl);
+		return -EINVAL;
+	}
+
+	/* copy over EP cfg */
+	ipa_ctx->ep[clnt_hdl].cfg.mode = *ipa_ep_cfg;
+	ipa_ctx->ep[clnt_hdl].dst_pipe_index = ipa_get_ep_mapping(ipa_ctx->mode,
+			ipa_ep_cfg->dst);
+
+	val = IPA_SETFIELD(ipa_ctx->ep[clnt_hdl].cfg.mode.mode,
+			   IPA_ENDP_INIT_MODE_n_MODE_SHFT,
+			   IPA_ENDP_INIT_MODE_n_MODE_BMSK) |
+	      IPA_SETFIELD(ipa_ctx->ep[clnt_hdl].dst_pipe_index,
+			   IPA_ENDP_INIT_MODE_n_DEST_PIPE_INDEX_SHFT,
+			   IPA_ENDP_INIT_MODE_n_DEST_PIPE_INDEX_BMSK);
+
+	ipa_write_reg(ipa_ctx->mmio, IPA_ENDP_INIT_MODE_n_OFST(clnt_hdl), val);
+
+	return 0;
+}
+EXPORT_SYMBOL(ipa_cfg_ep_mode);
+
+/**
+ * ipa_cfg_ep_aggr() - IPA end-point aggregation configuration
+ * @clnt_hdl:	[in] opaque client handle assigned by IPA to client
+ * @ipa_ep_cfg:	[in] IPA end-point configuration params
+ *
+ * Returns:	0 on success, negative on failure
+ *
+ * Note:	Should not be called from atomic context
+ */
+int ipa_cfg_ep_aggr(u32 clnt_hdl, const struct ipa_ep_cfg_aggr *ipa_ep_cfg)
+{
+	u32 val;
+
+	if (clnt_hdl >= IPA_NUM_PIPES || ipa_ctx->ep[clnt_hdl].valid == 0 ||
+			ipa_ep_cfg == NULL) {
+		IPAERR("bad parm.\n");
+		return -EINVAL;
+	}
+	/* copy over EP cfg */
+	ipa_ctx->ep[clnt_hdl].cfg.aggr = *ipa_ep_cfg;
+
+	val = IPA_SETFIELD(ipa_ctx->ep[clnt_hdl].cfg.aggr.aggr_en,
+			   IPA_ENDP_INIT_AGGR_n_AGGR_EN_SHFT,
+			   IPA_ENDP_INIT_AGGR_n_AGGR_EN_BMSK) |
+	      IPA_SETFIELD(ipa_ctx->ep[clnt_hdl].cfg.aggr.aggr,
+			   IPA_ENDP_INIT_AGGR_n_AGGR_TYPE_SHFT,
+			   IPA_ENDP_INIT_AGGR_n_AGGR_TYPE_BMSK) |
+	      IPA_SETFIELD(ipa_ctx->ep[clnt_hdl].cfg.aggr.aggr_byte_limit,
+			   IPA_ENDP_INIT_AGGR_n_AGGR_BYTE_LIMIT_SHFT,
+			   IPA_ENDP_INIT_AGGR_n_AGGR_BYTE_LIMIT_BMSK) |
+	      IPA_SETFIELD(ipa_ctx->ep[clnt_hdl].cfg.aggr.aggr_time_limit,
+			   IPA_ENDP_INIT_AGGR_n_AGGR_TIME_LIMIT_SHFT,
+			   IPA_ENDP_INIT_AGGR_n_AGGR_TIME_LIMIT_BMSK);
+
+	ipa_write_reg(ipa_ctx->mmio, IPA_ENDP_INIT_AGGR_n_OFST(clnt_hdl), val);
+
+	return 0;
+}
+EXPORT_SYMBOL(ipa_cfg_ep_aggr);
+
+/**
+ * ipa_cfg_ep_route() - IPA end-point routing configuration
+ * @clnt_hdl:	[in] opaque client handle assigned by IPA to client
+ * @ipa_ep_cfg:	[in] IPA end-point configuration params
+ *
+ * Returns:	0 on success, negative on failure
+ *
+ * Note:	Should not be called from atomic context
+ */
+int ipa_cfg_ep_route(u32 clnt_hdl, const struct ipa_ep_cfg_route *ipa_ep_cfg)
+{
+	if (clnt_hdl >= IPA_NUM_PIPES || ipa_ctx->ep[clnt_hdl].valid == 0 ||
+			ipa_ep_cfg == NULL) {
+		IPAERR("bad parm.\n");
+		return -EINVAL;
+	}
+
+	if (IPA_CLIENT_IS_CONS(ipa_ctx->ep[clnt_hdl].client)) {
+		IPAERR("ROUTE does not apply to IPA out EP %d\n", clnt_hdl);
+		return -EINVAL;
+	}
+
+	/*
+	 * if DMA mode was configured previously for this EP, return with
+	 * success
+	 */
+	if (ipa_ctx->ep[clnt_hdl].cfg.mode.mode == IPA_DMA) {
+		IPADBG("DMA mode for EP %d\n", clnt_hdl);
+		return 0;
+	}
+
+	if (ipa_ep_cfg->rt_tbl_hdl)
+		IPAERR("client specified non-zero RT TBL hdl - ignore it\n");
+
+	/* always use the "default" routing tables whose indices are 0 */
+	ipa_ctx->ep[clnt_hdl].rt_tbl_idx = 0;
+
+	ipa_write_reg(ipa_ctx->mmio, IPA_ENDP_INIT_ROUTE_n_OFST(clnt_hdl),
+		      IPA_SETFIELD(ipa_ctx->ep[clnt_hdl].rt_tbl_idx,
+			   IPA_ENDP_INIT_ROUTE_n_ROUTE_TABLE_INDEX_SHFT,
+			   IPA_ENDP_INIT_ROUTE_n_ROUTE_TABLE_INDEX_BMSK));
+
+	return 0;
+}
+EXPORT_SYMBOL(ipa_cfg_ep_route);
+
+/**
+ * ipa_dump_buff_internal() - dumps buffer for debug purposes
+ * @base: buffer base address
+ * @phy_base: buffer physical base address
+ * @size: size of the buffer
+ */
+void ipa_dump_buff_internal(void *base, dma_addr_t phy_base, u32 size)
+{
+	int i;
+	u32 *cur = (u32 *)base;
+	u8 *byt;
+	IPADBG("START phys=%x\n", phy_base);
+	for (i = 0; i < size / 4; i++) {
+		byt = (u8 *)(cur + i);
+		IPADBG("%2d %08x   %02x %02x %02x %02x\n", i, *(cur + i),
+				byt[0], byt[1], byt[2], byt[3]);
+	}
+	IPADBG("END\n");
+}
+
+/**
+ * ipa_dump() - dumps part of driver data structures for debug purposes
+ */
+void ipa_dump(void)
+{
+	struct ipa_mem_buffer hdr_mem = { 0 };
+	struct ipa_mem_buffer rt_mem = { 0 };
+	struct ipa_mem_buffer flt_mem = { 0 };
+
+	mutex_lock(&ipa_ctx->lock);
+
+	if (ipa_generate_hdr_hw_tbl(&hdr_mem))
+		IPAERR("fail\n");
+	if (ipa_generate_rt_hw_tbl(IPA_IP_v4, &rt_mem))
+		IPAERR("fail\n");
+	if (ipa_generate_flt_hw_tbl(IPA_IP_v4, &flt_mem))
+		IPAERR("fail\n");
+	IPAERR("PHY hdr=%x rt=%x flt=%x\n", hdr_mem.phys_base, rt_mem.phys_base,
+			flt_mem.phys_base);
+	IPAERR("VIRT hdr=%x rt=%x flt=%x\n", (u32)hdr_mem.base,
+			(u32)rt_mem.base, (u32)flt_mem.base);
+	IPAERR("SIZE hdr=%d rt=%d flt=%d\n", hdr_mem.size, rt_mem.size,
+			flt_mem.size);
+	IPA_DUMP_BUFF(hdr_mem.base, hdr_mem.phys_base, hdr_mem.size);
+	IPA_DUMP_BUFF(rt_mem.base, rt_mem.phys_base, rt_mem.size);
+	IPA_DUMP_BUFF(flt_mem.base, flt_mem.phys_base, flt_mem.size);
+	if (hdr_mem.phys_base)
+		dma_free_coherent(NULL, hdr_mem.size, hdr_mem.base,
+				hdr_mem.phys_base);
+	if (rt_mem.phys_base)
+		dma_free_coherent(NULL, rt_mem.size, rt_mem.base,
+				rt_mem.phys_base);
+	if (flt_mem.phys_base)
+		dma_free_coherent(NULL, flt_mem.size, flt_mem.base,
+				flt_mem.phys_base);
+	mutex_unlock(&ipa_ctx->lock);
+}
+
+/*
+ * TODO: add swap if needed, for now assume LE is ok for device memory
+ * even though IPA registers are assumed to be BE
+ */
+/**
+ * ipa_write_dev_8() - writes 8 bit value
+ * @val: value
+ * @ofst_ipa_sram: address to write to
+ */
+void ipa_write_dev_8(u8 val, u16 ofst_ipa_sram)
+{
+	iowrite8(val, (u32)ipa_ctx->mmio + 0x4000 + ofst_ipa_sram);
+}
+
+/**
+ * ipa_write_dev_16() - writes 16 bit value
+ * @val: value
+ * @ofst_ipa_sram: address to write to
+ *
+ */
+void ipa_write_dev_16(u16 val, u16 ofst_ipa_sram)
+{
+	iowrite16(val, (u32)ipa_ctx->mmio + 0x4000 + ofst_ipa_sram);
+}
+
+/**
+ * ipa_write_dev_32() - writes 32 bit value
+ * @val: value
+ * @ofst_ipa_sram: address to write to
+ */
+void ipa_write_dev_32(u32 val, u16 ofst_ipa_sram)
+{
+	iowrite32(val, (u32)ipa_ctx->mmio + 0x4000 + ofst_ipa_sram);
+}
+
+/**
+ * ipa_read_dev_8() - reads 8 bit value
+ * @ofst_ipa_sram: address to read from
+ *
+ * Return value: value read
+ */
+unsigned int ipa_read_dev_8(u16 ofst_ipa_sram)
+{
+	return ioread8((u32)ipa_ctx->mmio + 0x4000 + ofst_ipa_sram);
+}
+
+/**
+ * ipa_read_dev_16() - reads 16 bit value
+ * @ofst_ipa_sram: address to read from
+ *
+ * Return value: value read
+ */
+unsigned int ipa_read_dev_16(u16 ofst_ipa_sram)
+{
+	return ioread16((u32)ipa_ctx->mmio + 0x4000 + ofst_ipa_sram);
+}
+
+/**
+ * ipa_read_dev_32() - reads 32 bit value
+ * @ofst_ipa_sram: address to read from
+ *
+ * Return value: value read
+ */
+unsigned int ipa_read_dev_32(u16 ofst_ipa_sram)
+{
+	return ioread32((u32)ipa_ctx->mmio + 0x4000 + ofst_ipa_sram);
+}
+
+/**
+ * ipa_write_dev_8rep() - writes 8 bit value
+ * @val: value
+ * @ofst_ipa_sram: address to write to
+ * @count: num of bytes to write
+ */
+void ipa_write_dev_8rep(u16 ofst_ipa_sram, const void *buf, unsigned long count)
+{
+	iowrite8_rep((void *)((u32)ipa_ctx->mmio + 0x4000 + ofst_ipa_sram), buf,
+			count);
+}
+
+/**
+ * ipa_write_dev_16rep() - writes 16 bit value
+ * @val: value
+ * @ofst_ipa_sram: address to write to
+ * @count: num of bytes to write
+ */
+void ipa_write_dev_16rep(u16 ofst_ipa_sram, const void *buf,
+		unsigned long count)
+{
+	iowrite16_rep((void *)((u32)ipa_ctx->mmio + 0x4000 + ofst_ipa_sram),
+			buf, count);
+}
+
+/**
+ * ipa_write_dev_32rep() - writes 32 bit value
+ * @val: value
+ * @ofst_ipa_sram: address to write to
+ * @count: num of bytes to write
+ */
+void ipa_write_dev_32rep(u16 ofst_ipa_sram, const void *buf,
+		unsigned long count)
+{
+	iowrite32_rep((void *)((u32)ipa_ctx->mmio + 0x4000 + ofst_ipa_sram),
+			buf, count);
+}
+
+/**
+ * ipa_read_dev_8rep() - reads 8 bit value
+ * @ofst_ipa_sram: address to read from
+ * @buf: buffer to read to
+ * @count: number of bytes to read
+ */
+void ipa_read_dev_8rep(u16 ofst_ipa_sram, void *buf, unsigned long count)
+{
+	ioread8_rep((void *)((u32)ipa_ctx->mmio + 0x4000 + ofst_ipa_sram), buf,
+			count);
+}
+
+/**
+ * ipa_read_dev_16rep() - reads 16 bit value
+ * @ofst_ipa_sram: address to read from
+ * @buf: buffer to read to
+ * @count: number of bytes to read
+ */
+void ipa_read_dev_16rep(u16 ofst_ipa_sram, void *buf, unsigned long count)
+{
+	ioread16_rep((void *)((u32)ipa_ctx->mmio + 0x4000 + ofst_ipa_sram), buf,
+			count);
+}
+
+/**
+ * ipa_read_dev_32rep() - reads 32 bit value
+ * @ofst_ipa_sram: address to read from
+ * @buf: buffer to read to
+ * @count: number of bytes to read
+ */
+void ipa_read_dev_32rep(u16 ofst_ipa_sram, void *buf, unsigned long count)
+{
+	ioread32_rep((void *)((u32)ipa_ctx->mmio + 0x4000 + ofst_ipa_sram), buf,
+			count);
+}
+
+/**
+ * ipa_memset_dev() - memset IO
+ * @ofst_ipa_sram: address to set
+ * @value: value
+ * @count: number of bytes to set
+ */
+void ipa_memset_dev(u16 ofst_ipa_sram, u8 value, unsigned int count)
+{
+	memset_io((void *)((u32)ipa_ctx->mmio + 0x4000 + ofst_ipa_sram), value,
+			count);
+}
+
+/**
+ * ipa_memcpy_from_dev() - copy memory from device
+ * @dest: buffer to copy to
+ * @ofst_ipa_sram: address
+ * @count: number of bytes to copy
+ */
+void ipa_memcpy_from_dev(void *dest, u16 ofst_ipa_sram, unsigned int count)
+{
+	memcpy_fromio(dest, (void *)((u32)ipa_ctx->mmio + 0x4000 +
+				ofst_ipa_sram), count);
+}
+
+/**
+ * ipa_memcpy_to_dev() - copy memory to device
+ * @ofst_ipa_sram: address
+ * @source: buffer to copy from
+ * @count: number of bytes to copy
+ */
+void ipa_memcpy_to_dev(u16 ofst_ipa_sram, void *source, unsigned int count)
+{
+	memcpy_toio((void *)((u32)ipa_ctx->mmio + 0x4000 + ofst_ipa_sram),
+			source, count);
+}
+
+/**
+ * ipa_defrag() - handle de-frag for bridging type of cases
+ * @skb: skb
+ *
+ * Return value:
+ * 0: success
+ */
+int ipa_defrag(struct sk_buff *skb)
+{
+	/*
+	 * Reassemble IP fragments. TODO: need to setup network_header to
+	 * point to start of IP header
+	 */
+	if (ip_hdr(skb)->frag_off & htons(IP_MF | IP_OFFSET)) {
+		if (ip_defrag(skb, IP_DEFRAG_CONNTRACK_IN))
+			return -EINPROGRESS;
+	}
+
+	/* skb is not fully assembled, send it back out */
+	return 0;
+}
+
+/**
+ * ipa_search() - search for handle in RB tree
+ * @root: tree root
+ * @hdl: handle
+ *
+ * Return value: tree node corresponding to the handle
+ */
+struct ipa_tree_node *ipa_search(struct rb_root *root, u32 hdl)
+{
+	struct rb_node *node = root->rb_node;
+
+	while (node) {
+		struct ipa_tree_node *data = container_of(node,
+				struct ipa_tree_node, node);
+
+		if (hdl < data->hdl)
+			node = node->rb_left;
+		else if (hdl > data->hdl)
+			node = node->rb_right;
+		else
+			return data;
+	}
+	return NULL;
+}
+
+/**
+ * ipa_insert() - insert new node to RB tree
+ * @root: tree root
+ * @data: new data to insert
+ *
+ * Return value:
+ * 0: success
+ * -EPERM: tree already contains the node with provided handle
+ */
+int ipa_insert(struct rb_root *root, struct ipa_tree_node *data)
+{
+	struct rb_node **new = &(root->rb_node), *parent = NULL;
+
+	/* Figure out where to put new node */
+	while (*new) {
+		struct ipa_tree_node *this = container_of(*new,
+				struct ipa_tree_node, node);
+
+		parent = *new;
+		if (data->hdl < this->hdl)
+			new = &((*new)->rb_left);
+		else if (data->hdl > this->hdl)
+			new = &((*new)->rb_right);
+		else
+			return -EPERM;
+	}
+
+	/* Add new node and rebalance tree. */
+	rb_link_node(&data->node, parent, new);
+	rb_insert_color(&data->node, root);
+
+	return 0;
+}
+
+/**
+ * ipa_pipe_mem_init() - initialize the pipe memory
+ * @start_ofst: start offset
+ * @size: size
+ *
+ * Return value:
+ * 0: success
+ * -ENOMEM: no memory
+ */
+int ipa_pipe_mem_init(u32 start_ofst, u32 size)
+{
+	int res;
+	u32 aligned_start_ofst;
+	u32 aligned_size;
+	struct gen_pool *pool;
+
+	if (!size) {
+		IPAERR("no IPA pipe mem alloted\n");
+		goto fail;
+	}
+
+	aligned_start_ofst = IPA_HW_TABLE_ALIGNMENT(start_ofst);
+	aligned_size = size - (aligned_start_ofst - start_ofst);
+
+	IPADBG("start_ofst=%u aligned_start_ofst=%u size=%u aligned_size=%u\n",
+	       start_ofst, aligned_start_ofst, size, aligned_size);
+
+	/* allocation order of 8 i.e. 128 bytes, global pool */
+	pool = gen_pool_create(8, -1);
+	if (!pool) {
+		IPAERR("Failed to create a new memory pool.\n");
+		goto fail;
+	}
+
+	res = gen_pool_add(pool, aligned_start_ofst, aligned_size, -1);
+	if (res) {
+		IPAERR("Failed to add memory to IPA pipe pool\n");
+		goto err_pool_add;
+	}
+
+	ipa_ctx->pipe_mem_pool = pool;
+	return 0;
+
+err_pool_add:
+	gen_pool_destroy(pool);
+fail:
+	return -ENOMEM;
+}
+
+/**
+ * ipa_pipe_mem_alloc() - allocate pipe memory
+ * @ofst: offset
+ * @size: size
+ *
+ * Return value:
+ * 0: success
+ */
+int ipa_pipe_mem_alloc(u32 *ofst, u32 size)
+{
+	u32 vaddr;
+	int res = -1;
+
+	if (!ipa_ctx->pipe_mem_pool || !size) {
+		IPAERR("failed size=%u pipe_mem_pool=%p\n", size,
+				ipa_ctx->pipe_mem_pool);
+		return res;
+	}
+
+	vaddr = gen_pool_alloc(ipa_ctx->pipe_mem_pool, size);
+
+	if (vaddr) {
+		*ofst = vaddr;
+		res = 0;
+		IPADBG("size=%u ofst=%u\n", size, vaddr);
+	} else {
+		IPAERR("size=%u failed\n", size);
+	}
+
+	return res;
+}
+
+/**
+ * ipa_pipe_mem_free() - free pipe memory
+ * @ofst: offset
+ * @size: size
+ *
+ * Return value:
+ * 0: success
+ */
+int ipa_pipe_mem_free(u32 ofst, u32 size)
+{
+	IPADBG("size=%u ofst=%u\n", size, ofst);
+	if (ipa_ctx->pipe_mem_pool && size)
+		gen_pool_free(ipa_ctx->pipe_mem_pool, ofst, size);
+	return 0;
+}
+
+/**
+ * ipa_set_aggr_mode() - Set the aggregation mode which is a global setting
+ * @mode:	[in] the desired aggregation mode for e.g. straight MBIM, QCNCM,
+ * etc
+ *
+ * Returns:	0 on success
+ */
+int ipa_set_aggr_mode(enum ipa_aggr_mode mode)
+{
+	u32 reg_val;
+	reg_val = ipa_read_reg(ipa_ctx->mmio, IPA_AGGREGATION_SPARE_REG_2_OFST);
+	ipa_write_reg(ipa_ctx->mmio, IPA_AGGREGATION_SPARE_REG_2_OFST,
+			((mode & IPA_AGGREGATION_MODE_MSK) <<
+				IPA_AGGREGATION_MODE_SHFT) |
+			(reg_val & IPA_AGGREGATION_MODE_BMSK));
+	return 0;
+}
+EXPORT_SYMBOL(ipa_set_aggr_mode);
+
+/**
+ * ipa_set_qcncm_ndp_sig() - Set the NDP signature used for QCNCM aggregation
+ * mode
+ * @sig:	[in] the first 3 bytes of QCNCM NDP signature (expected to be
+ * "QND")
+ *
+ * Set the NDP signature used for QCNCM aggregation mode. The fourth byte
+ * (expected to be 'P') needs to be set using the header addition mechanism
+ *
+ * Returns:	0 on success, negative on failure
+ */
+int ipa_set_qcncm_ndp_sig(char sig[3])
+{
+	u32 reg_val;
+
+	if (sig == NULL) {
+		IPAERR("bad argument for ipa_set_qcncm_ndp_sig/n");
+		return -EINVAL;
+	}
+	reg_val = ipa_read_reg(ipa_ctx->mmio, IPA_AGGREGATION_SPARE_REG_2_OFST);
+	ipa_write_reg(ipa_ctx->mmio, IPA_AGGREGATION_SPARE_REG_2_OFST, sig[0] <<
+			IPA_AGGREGATION_QCNCM_SIG0_SHFT |
+			(sig[1] << IPA_AGGREGATION_QCNCM_SIG1_SHFT) |
+			sig[2] | (reg_val & IPA_AGGREGATION_QCNCM_SIG_BMSK));
+	return 0;
+}
+EXPORT_SYMBOL(ipa_set_qcncm_ndp_sig);
+
+/**
+ * ipa_set_single_ndp_per_mbim() - Enable/disable single NDP per MBIM frame
+ * configuration
+ * @enable:	[in] true for single NDP/MBIM; false otherwise
+ *
+ * Returns:	0 on success
+ */
+int ipa_set_single_ndp_per_mbim(bool enable)
+{
+	u32 reg_val;
+	reg_val = ipa_read_reg(ipa_ctx->mmio, IPA_AGGREGATION_SPARE_REG_1_OFST);
+	ipa_write_reg(ipa_ctx->mmio, IPA_AGGREGATION_SPARE_REG_1_OFST, (enable &
+			IPA_AGGREGATION_SINGLE_NDP_MSK) |
+			(reg_val & IPA_AGGREGATION_SINGLE_NDP_BMSK));
+	return 0;
+}
+EXPORT_SYMBOL(ipa_set_single_ndp_per_mbim);
+
+/**
+ * ipa_straddle_boundary() - Checks whether a memory buffer straddles a boundary
+ * @start: start address of the memory buffer
+ * @end: end address of the memory buffer
+ * @boundary: boundary
+ *
+ * Return value:
+ * 1: if the interval [start, end] straddles boundary
+ * 0: otherwise
+ */
+int ipa_straddle_boundary(u32 start, u32 end, u32 boundary)
+{
+	u32 next_start;
+	u32 prev_end;
+
+	IPADBG("start=%u end=%u boundary=%u\n", start, end, boundary);
+
+	next_start = (start + (boundary - 1)) & ~(boundary - 1);
+	prev_end = ((end + (boundary - 1)) & ~(boundary - 1)) - boundary;
+
+	while (next_start < prev_end)
+		next_start += boundary;
+
+	if (next_start == prev_end)
+		return 1;
+	else
+		return 0;
+}
+
diff --git a/drivers/platform/msm/ipa/rmnet_bridge.c b/drivers/platform/msm/ipa/rmnet_bridge.c
new file mode 100644
index 0000000..3c7f5ca
--- /dev/null
+++ b/drivers/platform/msm/ipa/rmnet_bridge.c
@@ -0,0 +1,122 @@
+/* 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/export.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <mach/bam_dmux.h>
+#include <mach/ipa.h>
+#include <mach/sps.h>
+#include "a2_service.h"
+#include "ipa_i.h"
+
+static struct rmnet_bridge_cb_type {
+	u32 producer_handle;
+	u32 consumer_handle;
+	bool is_connected;
+} rmnet_bridge_cb;
+
+/**
+* rmnet_bridge_init() - Initialize RmNet bridge module
+*
+* Return codes:
+* 0: success
+*/
+int rmnet_bridge_init(void)
+{
+	memset(&rmnet_bridge_cb, 0, sizeof(struct rmnet_bridge_cb_type));
+
+	return 0;
+}
+EXPORT_SYMBOL(rmnet_bridge_init);
+
+/**
+* rmnet_bridge_disconnect() - Disconnect RmNet bridge module
+*
+* Return codes:
+* 0: success
+* -EINVAL: invalid parameters
+*/
+int rmnet_bridge_disconnect(void)
+{
+	int ret = 0;
+	if (false == rmnet_bridge_cb.is_connected) {
+		pr_err("%s: trying to disconnect already disconnected RmNet bridge\n",
+		       __func__);
+		goto bail;
+	}
+
+	rmnet_bridge_cb.is_connected = false;
+
+	ret = ipa_bridge_teardown(IPA_DL);
+	ret = ipa_bridge_teardown(IPA_UL);
+bail:
+	return ret;
+}
+EXPORT_SYMBOL(rmnet_bridge_disconnect);
+
+/**
+* rmnet_bridge_connect() - Connect RmNet bridge module
+* @producer_hdl:	IPA producer handle
+* @consumer_hdl:	IPA consumer handle
+* @wwan_logical_channel_id:	WWAN logical channel ID
+*
+* Return codes:
+* 0: success
+* -EINVAL: invalid parameters
+*/
+int rmnet_bridge_connect(u32 producer_hdl,
+			 u32 consumer_hdl,
+			 int wwan_logical_channel_id)
+{
+	int ret = 0;
+
+	if (true == rmnet_bridge_cb.is_connected) {
+		ret = 0;
+		pr_err("%s: trying to connect already connected RmNet bridge\n",
+		       __func__);
+		goto bail;
+	}
+
+	rmnet_bridge_cb.consumer_handle = consumer_hdl;
+	rmnet_bridge_cb.producer_handle = producer_hdl;
+	rmnet_bridge_cb.is_connected = true;
+
+	ret = ipa_bridge_setup(IPA_DL);
+	if (ret) {
+		pr_err("%s: IPA DL bridge setup failure\n", __func__);
+		goto bail_dl;
+	}
+	ret = ipa_bridge_setup(IPA_UL);
+	if (ret) {
+		pr_err("%s: IPA UL bridge setup failure\n", __func__);
+		goto bail_ul;
+	}
+	return 0;
+bail_ul:
+	ipa_bridge_teardown(IPA_DL);
+bail_dl:
+	rmnet_bridge_cb.is_connected = false;
+bail:
+	return ret;
+}
+EXPORT_SYMBOL(rmnet_bridge_connect);
+
+void rmnet_bridge_get_client_handles(u32 *producer_handle,
+		u32 *consumer_handle)
+{
+	if (producer_handle == NULL || consumer_handle == NULL)
+		return;
+
+	*producer_handle = rmnet_bridge_cb.producer_handle;
+	*consumer_handle = rmnet_bridge_cb.consumer_handle;
+}
diff --git a/drivers/platform/msm/qpnp-power-on.c b/drivers/platform/msm/qpnp-power-on.c
index 76a758b..1907adc 100644
--- a/drivers/platform/msm/qpnp-power-on.c
+++ b/drivers/platform/msm/qpnp-power-on.c
@@ -64,7 +64,7 @@
 #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,
diff --git a/drivers/platform/msm/sps/sps.c b/drivers/platform/msm/sps/sps.c
index 25febff..e9c3371 100644
--- a/drivers/platform/msm/sps/sps.c
+++ b/drivers/platform/msm/sps/sps.c
@@ -2122,14 +2122,6 @@
 		SPS_DBG("sps:bamdma_restricted_pipes=0x%x.",
 			sps->bamdma_restricted_pipes);
 
-	if (of_property_read_u32((&pdev->dev)->of_node,
-				"qcom,device-type",
-				&d_type)) {
-		d_type = 1;
-		SPS_DBG("sps:default device type.\n");
-	} else
-		SPS_DBG("sps:device type is %d.", d_type);
-
 	resource  = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	if (resource) {
 		sps->bamdma_bam_phys_base = resource->start;
@@ -2174,6 +2166,14 @@
 	}
 #endif
 
+	if (of_property_read_u32((&pdev->dev)->of_node,
+				"qcom,device-type",
+				&d_type)) {
+		d_type = 1;
+		SPS_DBG("sps:default device type.\n");
+	} else
+		SPS_DBG("sps:device type is %d.", d_type);
+
 	return 0;
 }
 
diff --git a/drivers/platform/msm/sps/sps_bam.c b/drivers/platform/msm/sps/sps_bam.c
index f671ece..a3bbb73 100644
--- a/drivers/platform/msm/sps/sps_bam.c
+++ b/drivers/platform/msm/sps/sps_bam.c
@@ -1005,6 +1005,8 @@
 	no_queue = ((options & SPS_O_NO_Q));
 	ack_xfers = ((options & SPS_O_ACK_TRANSFERS));
 
+	pipe->hybrid = options & SPS_O_HYBRID;
+
 	/* Create interrupt source mask */
 	mask = 0;
 	for (n = 0; n < ARRAY_SIZE(opt_event_table); n++) {
@@ -1773,7 +1775,7 @@
 	}
 
 	/* If pipe is polled and queue is enabled, perform polling operation */
-	if (pipe->polled && !pipe->sys.no_queue)
+	if ((pipe->polled || pipe->hybrid) && !pipe->sys.no_queue)
 		pipe_handler_eot(dev, pipe);
 
 	/* Is there a completed descriptor? */
diff --git a/drivers/platform/msm/sps/sps_bam.h b/drivers/platform/msm/sps/sps_bam.h
index 6004b75..84d2b97 100644
--- a/drivers/platform/msm/sps/sps_bam.h
+++ b/drivers/platform/msm/sps/sps_bam.h
@@ -170,6 +170,7 @@
 	u32 pipe_index_mask;
 	u32 irq_mask;
 	int polled;
+	int hybrid;
 	u32 irq_gen_addr;
 	enum sps_mode mode;
 	u32 num_descs; /* Size (number of elements) of descriptor FIFO */
diff --git a/drivers/platform/msm/sps/sps_dma.c b/drivers/platform/msm/sps/sps_dma.c
index f8b4f51d..335de9a 100644
--- a/drivers/platform/msm/sps/sps_dma.c
+++ b/drivers/platform/msm/sps/sps_dma.c
@@ -274,7 +274,6 @@
 {
 	struct bamdma_device *dev;
 	struct sps_bam_props *props;
-	u32 chan;
 	int result = SPS_ERROR;
 
 	mutex_lock(&bam_dma_lock);
@@ -343,14 +342,6 @@
 
 	dev->num_pipes = dev->bam->props.num_pipes;
 
-	/* Disable all channels */
-	if (dev->local)
-		for (chan = 0; chan < (dev->num_pipes / 2); chan++) {
-			dma_write_reg_field(dev->virt_addr,
-					    DMA_CHNL_CONFIG(chan),
-					    DMA_CHNL_ENABLE, 0);
-		}
-
 	result = 0;
 exit_err:
 	if (result) {
diff --git a/drivers/platform/msm/usb_bam.c b/drivers/platform/msm/usb_bam.c
index bf78b1c..8f97531 100644
--- a/drivers/platform/msm/usb_bam.c
+++ b/drivers/platform/msm/usb_bam.c
@@ -23,11 +23,12 @@
 #include <linux/usb/msm_hsusb.h>
 #include <mach/usb_bam.h>
 #include <mach/sps.h>
+#include <mach/ipa.h>
 #include <linux/workqueue.h>
 #include <linux/dma-mapping.h>
 
 #define USB_SUMMING_THRESHOLD 512
-#define CONNECTIONS_NUM	4
+#define CONNECTIONS_NUM	8
 
 static struct sps_bam_props usb_props;
 static struct sps_pipe *sps_pipes[CONNECTIONS_NUM][2];
@@ -49,7 +50,8 @@
 	u32 *src_pipe;
 	u32 *dst_pipe;
 	struct usb_bam_wake_event_info peer_event;
-	bool enabled;
+	bool src_enabled;
+	bool dst_enabled;
 };
 
 static struct usb_bam_connect_info usb_bam_connections[CONNECTIONS_NUM];
@@ -206,7 +208,7 @@
 
 	ret = sps_connect(*pipe, connection);
 	if (ret < 0) {
-		pr_err("%s: tx connect error %d\n", __func__, ret);
+		pr_err("%s: sps_connect failed %d\n", __func__, ret);
 		goto error;
 	}
 	return 0;
@@ -218,9 +220,119 @@
 	return ret;
 }
 
+static int connect_pipe_ipa(
+			struct usb_bam_connect_ipa_params *connection_params)
+{
+	int ret;
+	u8 conn_idx = connection_params->idx;
+	enum usb_bam_pipe_dir pipe_dir = connection_params->dir;
+	struct sps_pipe **pipe = &sps_pipes[conn_idx][pipe_dir];
+	struct sps_connect *connection =
+		&sps_connections[conn_idx][pipe_dir];
+	struct msm_usb_bam_platform_data *pdata =
+		usb_bam_pdev->dev.platform_data;
+	struct usb_bam_pipe_connect *pipe_connection =
+				&msm_usb_bam_connections_info
+				[pdata->usb_active_bam][conn_idx][pipe_dir];
 
-static int disconnect_pipe(u8 connection_idx, enum usb_bam_pipe_dir pipe_dir,
-						u32 *usb_pipe_idx)
+	struct ipa_connect_params ipa_in_params;
+	struct ipa_sps_params sps_out_params;
+	u32 usb_handle, usb_phy_addr;
+	u32 clnt_hdl = 0;
+
+	memset(&ipa_in_params, 0, sizeof(ipa_in_params));
+	memset(&sps_out_params, 0, sizeof(sps_out_params));
+
+	if (pipe_dir == USB_TO_PEER_PERIPHERAL) {
+		usb_phy_addr = pipe_connection->src_phy_addr;
+		ipa_in_params.client_ep_idx = pipe_connection->src_pipe_index;
+	} else {
+		usb_phy_addr = pipe_connection->dst_phy_addr;
+		ipa_in_params.client_ep_idx = pipe_connection->dst_pipe_index;
+	}
+	/* Get HSUSB / HSIC bam handle */
+	ret = sps_phy2h(usb_phy_addr, &usb_handle);
+	if (ret) {
+		pr_err("%s: sps_phy2h failed (HSUSB/HSIC BAM) %d\n",
+			   __func__, ret);
+		goto get_config_failed;
+	}
+
+	/* IPA input parameters */
+	ipa_in_params.client_bam_hdl = usb_handle;
+	ipa_in_params.desc_fifo_sz = pipe_connection->desc_fifo_size;
+	ipa_in_params.data_fifo_sz = pipe_connection->data_fifo_size;
+	ipa_in_params.notify = connection_params->notify;
+	ipa_in_params.priv = connection_params->priv;
+	ipa_in_params.client = connection_params->client;
+	if (pipe_connection->mem_type != SYSTEM_MEM)
+		ipa_in_params.pipe_mem_preferred = true;
+
+	memcpy(&ipa_in_params.ipa_ep_cfg, &connection_params->ipa_ep_cfg,
+		   sizeof(struct ipa_ep_cfg));
+
+	ret = ipa_connect(&ipa_in_params, &sps_out_params, &clnt_hdl);
+	if (ret) {
+		pr_err("%s: ipa_connect failed\n", __func__);
+		return ret;
+	}
+
+	*pipe = sps_alloc_endpoint();
+	if (*pipe == NULL) {
+		pr_err("%s: sps_alloc_endpoint failed\n", __func__);
+		ret = -ENOMEM;
+		goto disconnect_ipa;
+	}
+
+	ret = sps_get_config(*pipe, connection);
+	if (ret) {
+		pr_err("%s: tx get config failed %d\n", __func__, ret);
+		goto get_config_failed;
+	}
+
+	if (pipe_dir == USB_TO_PEER_PERIPHERAL) {
+		/* USB src IPA dest */
+		connection->mode = SPS_MODE_SRC;
+		connection_params->cons_clnt_hdl = clnt_hdl;
+		connection->source = usb_handle;
+		connection->destination = sps_out_params.ipa_bam_hdl;
+		connection->src_pipe_index = pipe_connection->src_pipe_index;
+		connection->dest_pipe_index = sps_out_params.ipa_ep_idx;
+		*(connection_params->src_pipe) = connection->src_pipe_index;
+	} else {
+		/* IPA src, USB dest */
+		connection->mode = SPS_MODE_DEST;
+		connection_params->prod_clnt_hdl = clnt_hdl;
+		connection->source = sps_out_params.ipa_bam_hdl;
+		connection->destination = usb_handle;
+		connection->src_pipe_index = sps_out_params.ipa_ep_idx;
+		connection->dest_pipe_index = pipe_connection->dst_pipe_index;
+		*(connection_params->dst_pipe) = connection->dest_pipe_index;
+	}
+
+	connection->data = sps_out_params.data;
+	connection->desc = sps_out_params.desc;
+	connection->event_thresh = 16;
+	connection->options = SPS_O_AUTO_ENABLE;
+
+	ret = sps_connect(*pipe, connection);
+	if (ret < 0) {
+		pr_err("%s: sps_connect failed %d\n", __func__, ret);
+		goto error;
+	}
+
+	return 0;
+
+error:
+	sps_disconnect(*pipe);
+get_config_failed:
+	sps_free_endpoint(*pipe);
+disconnect_ipa:
+	ipa_disconnect(clnt_hdl);
+	return ret;
+}
+
+static int disconnect_pipe(u8 connection_idx, enum usb_bam_pipe_dir pipe_dir)
 {
 	struct msm_usb_bam_platform_data *pdata =
 				usb_bam_pdev->dev.platform_data;
@@ -269,7 +381,7 @@
 		return -EINVAL;
 	}
 
-	if (connection->enabled) {
+	if (connection->src_enabled && connection->dst_enabled) {
 		pr_debug("%s: connection %d was already established\n",
 			__func__, idx);
 		return 0;
@@ -281,22 +393,66 @@
 	if (src_pipe_idx) {
 		/* open USB -> Peripheral pipe */
 		ret = connect_pipe(connection->idx, USB_TO_PEER_PERIPHERAL,
-			connection->src_pipe);
+						   connection->src_pipe);
 		if (ret) {
 			pr_err("%s: src pipe connection failure\n", __func__);
 			return ret;
 		}
 	}
+	connection->src_enabled = 1;
+
 	if (dst_pipe_idx) {
 		/* open Peripheral -> USB pipe */
 		ret = connect_pipe(connection->idx, PEER_PERIPHERAL_TO_USB,
-			connection->dst_pipe);
+						   connection->dst_pipe);
 		if (ret) {
 			pr_err("%s: dst pipe connection failure\n", __func__);
 			return ret;
 		}
 	}
-	connection->enabled = 1;
+	connection->dst_enabled = 1;
+
+	return 0;
+}
+
+int usb_bam_connect_ipa(struct usb_bam_connect_ipa_params *ipa_params)
+{
+	u8 idx = ipa_params->idx;
+	struct usb_bam_connect_info *connection = &usb_bam_connections[idx];
+	int ret;
+
+	if (idx >= CONNECTIONS_NUM) {
+		pr_err("%s: Invalid connection index\n",
+			__func__);
+		return -EINVAL;
+	}
+
+	if ((connection->src_enabled &&
+		 ipa_params->dir == USB_TO_PEER_PERIPHERAL) ||
+		 (connection->dst_enabled &&
+		  ipa_params->dir == PEER_PERIPHERAL_TO_USB)) {
+		pr_debug("%s: connection %d was already established\n",
+			__func__, idx);
+		return 0;
+	}
+
+	if (ipa_params->dir == USB_TO_PEER_PERIPHERAL)
+		connection->src_pipe = ipa_params->src_pipe;
+	else
+		connection->dst_pipe = ipa_params->dst_pipe;
+
+	connection->idx = idx;
+
+	ret = connect_pipe_ipa(ipa_params);
+	if (ret) {
+		pr_err("%s: dst pipe connection failure\n", __func__);
+		return ret;
+	}
+
+	if (ipa_params->dir == USB_TO_PEER_PERIPHERAL)
+		connection->src_enabled = 1;
+	else
+		connection->dst_enabled = 1;
 
 	return 0;
 }
@@ -364,39 +520,78 @@
 		return -EINVAL;
 	}
 
-	if (!connection->enabled) {
+	if (!connection->src_enabled && !connection->dst_enabled) {
 		pr_debug("%s: connection %d isn't enabled\n",
 			__func__, idx);
 		return 0;
 	}
 
-	if (connection->src_pipe) {
+	if (connection->src_enabled) {
 		/* close USB -> Peripheral pipe */
-		ret = disconnect_pipe(connection->idx, USB_TO_PEER_PERIPHERAL,
-						   connection->src_pipe);
+		ret = disconnect_pipe(connection->idx, USB_TO_PEER_PERIPHERAL);
 		if (ret) {
 			pr_err("%s: src pipe connection failure\n", __func__);
 			return ret;
 		}
-
+		connection->src_enabled = 0;
 	}
-	if (connection->dst_pipe) {
+	if (connection->dst_enabled) {
 		/* close Peripheral -> USB pipe */
-		ret = disconnect_pipe(connection->idx, PEER_PERIPHERAL_TO_USB,
-			connection->dst_pipe);
+		ret = disconnect_pipe(connection->idx, PEER_PERIPHERAL_TO_USB);
 		if (ret) {
 			pr_err("%s: dst pipe connection failure\n", __func__);
 			return ret;
 		}
+		connection->dst_enabled = 0;
 	}
 
 	connection->src_pipe = 0;
 	connection->dst_pipe = 0;
-	connection->enabled = 0;
 
 	return 0;
 }
+int usb_bam_disconnect_ipa(u8 idx,
+		struct usb_bam_connect_ipa_params *ipa_params)
+{
+	struct usb_bam_connect_info *connection = &usb_bam_connections[idx];
+	int ret;
 
+	if (!usb_bam_pdev) {
+		pr_err("%s: usb_bam device not found\n", __func__);
+		return -ENODEV;
+	}
+
+	if (idx >= CONNECTIONS_NUM) {
+		pr_err("%s: Invalid connection index\n",
+			__func__);
+		return -EINVAL;
+	}
+
+	/* Currently just calls ipa_disconnect, no sps pipes
+	   disconenction support */
+
+	/* close IPA -> USB pipe */
+	if (connection->dst_pipe) {
+		ret = ipa_disconnect(ipa_params->prod_clnt_hdl);
+		if (ret) {
+			pr_err("%s: dst pipe disconnection failure\n",
+				__func__);
+			return ret;
+		}
+	}
+	/* close USB -> IPA pipe */
+	if (connection->src_pipe) {
+		ret = ipa_disconnect(ipa_params->cons_clnt_hdl);
+		if (ret) {
+			pr_err("%s: src pipe disconnection failure\n",
+				__func__);
+			return ret;
+		}
+	}
+
+	return 0;
+
+}
 static int update_connections_info(struct device_node *node, int bam,
 	int conn_num, int dir, enum usb_pipe_mem_type mem_type)
 {
@@ -412,33 +607,28 @@
 
 	key = "qcom,src-bam-physical-address";
 	rc = of_property_read_u32(node, key, &val);
-	if (rc)
-		goto err;
-	pipe_connection->src_phy_addr = val;
+	if (!rc)
+		pipe_connection->src_phy_addr = val;
 
 	key = "qcom,src-bam-pipe-index";
 	rc = of_property_read_u32(node, key, &val);
-	if (rc)
-		goto err;
-	pipe_connection->src_pipe_index = val;
+	if (!rc)
+		pipe_connection->src_pipe_index = val;
 
 	key = "qcom,dst-bam-physical-address";
 	rc = of_property_read_u32(node, key, &val);
-	if (rc)
-		goto err;
-	pipe_connection->dst_phy_addr = val;
+	if (!rc)
+		pipe_connection->dst_phy_addr = val;
 
 	key = "qcom,dst-bam-pipe-index";
 	rc = of_property_read_u32(node, key, &val);
-	if (rc)
-		goto err;
-	pipe_connection->dst_pipe_index = val;
+	if (!rc)
+		pipe_connection->dst_pipe_index = val;
 
 	key = "qcom,data-fifo-offset";
 	rc = of_property_read_u32(node, key, &val);
-	if (rc)
-		goto err;
-	pipe_connection->data_fifo_base_offset = val;
+	if (!rc)
+		pipe_connection->data_fifo_base_offset = val;
 
 	key = "qcom,data-fifo-size";
 	rc = of_property_read_u32(node, key, &val);
@@ -448,9 +638,8 @@
 
 	key = "qcom,descriptor-fifo-offset";
 	rc = of_property_read_u32(node, key, &val);
-	if (rc)
-		goto err;
-	pipe_connection->desc_fifo_base_offset = val;
+	if (!rc)
+		pipe_connection->desc_fifo_base_offset = val;
 
 	key = "qcom,descriptor-fifo-size";
 	rc = of_property_read_u32(node, key, &val);
@@ -539,10 +728,8 @@
 
 	rc = of_property_read_u32(node, "qcom,usb-base-address",
 		&pdata->usb_base_address);
-	if (rc) {
-		pr_err("Invalid usb base address property\n");
-		return NULL;
-	}
+	if (rc)
+		pr_debug("%s: Invalid usb base address property\n", __func__);
 
 	pdata->ignore_core_reset_ack = of_property_read_bool(node,
 					"qcom,ignore-core-reset-ack");
@@ -588,22 +775,28 @@
 		if (rc)
 			goto err;
 
+		if (mem_type == USB_PRIVATE_MEM &&
+			!pdata->usb_base_address)
+			goto err;
+
 		rc = of_property_read_string(node, "label", &str);
 		if (rc) {
 			pr_err("Cannot read string\n");
 			goto err;
 		}
 
-		if (strstr(str, "usb-to-peri"))
+		if (strnstr(str, "usb-to", 30))
 			dir = USB_TO_PEER_PERIPHERAL;
-		else if (strstr(str, "peri-to-usb"))
+		else if (strnstr(str, "to-usb", 30))
 			dir = PEER_PERIPHERAL_TO_USB;
 		else
 			goto err;
 
-		/* Check if connection type is suported */
+		/* Check if connection type is supported */
 		if (!strcmp(str, "usb-to-peri-qdss-dwc3") ||
 			!strcmp(str, "peri-to-usb-qdss-dwc3") ||
+			!strcmp(str, "usb-to-ipa") ||
+			!strcmp(str, "ipa-to-usb") ||
 			!strcmp(str, "usb-to-peri-qdss-hsusb") ||
 			!strcmp(str, "peri-to-usb-qdss-hsusb"))
 				conn_num = 0;
@@ -772,7 +965,8 @@
 	dev_dbg(&pdev->dev, "usb_bam_probe\n");
 
 	for (i = 0; i < CONNECTIONS_NUM; i++) {
-		usb_bam_connections[i].enabled = 0;
+		usb_bam_connections[i].src_enabled = 0;
+		usb_bam_connections[i].dst_enabled = 0;
 		INIT_WORK(&usb_bam_connections[i].peer_event.wake_w,
 			usb_bam_wake_work);
 	}
diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
index 376750f..bc2e2ae 100644
--- a/drivers/power/Kconfig
+++ b/drivers/power/Kconfig
@@ -333,6 +333,16 @@
 	  To compile this driver as a module, choose M here: the module will
 	  be called smb137b.
 
+config SMB137C_CHARGER
+	tristate "Summit SMB137C Battery Charger"
+	depends on I2C
+	depends on OF
+	help
+	  The SMB137C charger chip from Summit is a switching mode based
+	  charging solution.  This driver supports enabling and disabling
+	  charging, setting the input current limit, and enabling USB OTG mode
+	  in order to supply 5 V on the VBUS line.
+
 config SMB349_CHARGER
 	tristate "smb349 charger"
 	depends on I2C
@@ -343,6 +353,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
@@ -388,6 +410,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..990bd03 100644
--- a/drivers/power/Makefile
+++ b/drivers/power/Makefile
@@ -48,11 +48,14 @@
 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_SMB137C_CHARGER)	+= smb137c-charger.o
 obj-$(CONFIG_PM8XXX_CCADC)	+= pm8xxx-ccadc.o
 obj-$(CONFIG_PM8921_BMS)	+= pm8921-bms.o
 obj-$(CONFIG_QPNP_BMS)		+= qpnp-bms.o
diff --git a/drivers/power/bq28400_battery.c b/drivers/power/bq28400_battery.c
new file mode 100644
index 0000000..77e74fa
--- /dev/null
+++ b/drivers/power/bq28400_battery.c
@@ -0,0 +1,933 @@
+/* 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);
+	rsoc = bq28400_read_rsoc(client);
+	current_ma = bq28400_read_current(client);
+
+	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 is full but DC-Jack is removed then report discahrging.
+	 */
+	if (battery_status & BAT_STATUS_FULL) {
+		pr_debug("Battery report Full.\n");
+		bq28400_enable_charging(bq28400_dev, false);
+		if (current_ma < 0)
+			return POWER_SUPPLY_STATUS_DISCHARGING;
+		return POWER_SUPPLY_STATUS_FULL;
+	}
+
+	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" },
+	{ .compatible = "ti,bq30z55-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/pm8921-bms.c b/drivers/power/pm8921-bms.c
index 23903df..2eddb9d 100644
--- a/drivers/power/pm8921-bms.c
+++ b/drivers/power/pm8921-bms.c
@@ -55,6 +55,9 @@
 #define TEMP_IAVG_STORAGE	0x105
 #define TEMP_IAVG_STORAGE_USE_MASK	0x0F
 
+#define PON_CNTRL_6		0x018
+#define WD_BIT			BIT(7)
+
 enum pmic_bms_interrupts {
 	PM8921_BMS_SBI_WRITE_OK,
 	PM8921_BMS_CC_THR,
@@ -87,7 +90,7 @@
 struct pm8921_bms_chip {
 	struct device		*dev;
 	struct dentry		*dent;
-	unsigned int		r_sense;
+	int			r_sense_uohm;
 	unsigned int		v_cutoff;
 	unsigned int		fcc;
 	struct single_row_lut	*fcc_temp_lut;
@@ -151,6 +154,9 @@
 	struct power_supply	*batt_psy;
 	bool			low_voltage_wake_lock_held;
 	struct wake_lock	low_voltage_wake_lock;
+	int			soc_calc_period;
+	int			normal_voltage_calc_ms;
+	int			low_voltage_calc_ms;
 };
 
 /*
@@ -500,15 +506,15 @@
 #define SLEEP_CLK_HZ		32764
 #define SECONDS_PER_HOUR	3600
 /**
- * ccmicrovolt_to_nvh -
+ * ccmicrovolt_to_uvh -
  * @cc_uv:  coulumb counter converted to uV
  *
- * RETURNS:	coulumb counter based charge in nVh
- *		(nano Volt Hour)
+ * RETURNS:	coulumb counter based charge in uVh
+ *		(micro Volt Hour)
  */
-static s64 ccmicrovolt_to_nvh(s64 cc_uv)
+static s64 ccmicrovolt_to_uvh(s64 cc_uv)
 {
-	return div_s64(cc_uv * CC_READING_TICKS * 1000,
+	return div_s64(cc_uv * CC_READING_TICKS,
 			SLEEP_CLK_HZ * SECONDS_PER_HOUR);
 }
 
@@ -618,7 +624,7 @@
 
 	convert_vbatt_raw_to_uv(the_chip, usb_chg, vbat_raw, vbat_uv);
 	convert_vsense_to_uv(the_chip, vsense_raw, &vsense_uv);
-	*ibat_ua = vsense_uv * 1000 / (int)the_chip->r_sense;
+	*ibat_ua = div_s64((s64)vsense_uv * 1000000LL, the_chip->r_sense_uohm);
 
 	pr_debug("vsense_raw = 0x%x vbat_raw = 0x%x"
 			" ibat_ua = %d vbat_uv = %d\n",
@@ -627,17 +633,17 @@
 	return 0;
 }
 
-#define MBG_TRANSIENT_ERROR_RAW 51
-static void adjust_pon_ocv_raw(struct pm8921_bms_chip *chip,
-				struct pm8921_soc_params *raw)
+#define MBG_TRANSIENT_ERROR_UV 15000
+static void adjust_pon_ocv(struct pm8921_bms_chip *chip, int *uv)
 {
-	/* in 8921 parts the PON ocv is taken when the MBG is not settled.
+	/*
+	 * In 8921 parts the PON ocv is taken when the MBG is not settled.
 	 * decrease the pon ocv by 15mV raw value to account for it
 	 * Since a 1/3rd  of vbatt is supplied to the adc the raw value
 	 * needs to be adjusted by 5mV worth bits
 	 */
-	if (raw->last_good_ocv_raw >= MBG_TRANSIENT_ERROR_RAW)
-		raw->last_good_ocv_raw -= MBG_TRANSIENT_ERROR_RAW;
+	if (*uv >= MBG_TRANSIENT_ERROR_UV)
+		*uv -= MBG_TRANSIENT_ERROR_UV;
 }
 
 #define SEL_ALT_OREG_BIT  BIT(2)
@@ -660,10 +666,71 @@
 	return compensated_ocv;
 }
 
+#define RESET_CC_BIT BIT(3)
+static int reset_cc(struct pm8921_bms_chip *chip)
+{
+	int rc;
+
+	rc = pm_bms_masked_write(chip, BMS_TEST1, RESET_CC_BIT, RESET_CC_BIT);
+	if (rc < 0) {
+		pr_err("err setting cc reset rc = %d\n", rc);
+		return rc;
+	}
+
+	/* sleep 100uS for the coulomb counter to reset */
+	udelay(100);
+
+	rc = pm_bms_masked_write(chip, BMS_TEST1, RESET_CC_BIT, 0);
+	if (rc < 0)
+		pr_err("err clearing cc reset rc = %d\n", rc);
+	return rc;
+}
+
+static int estimate_ocv(struct pm8921_bms_chip *chip)
+{
+	int ibat_ua, vbat_uv, ocv_est_uv;
+	int rc;
+
+	int rbatt_mohm = chip->default_rbatt_mohm + chip->rconn_mohm;
+
+	rc = pm8921_bms_get_simultaneous_battery_voltage_and_current(
+							&ibat_ua,
+							&vbat_uv);
+	if (rc) {
+		pr_err("simultaneous failed rc = %d\n", rc);
+		return rc;
+	}
+
+	ocv_est_uv = vbat_uv + (ibat_ua * rbatt_mohm) / 1000;
+	pr_debug("estimated pon ocv = %d\n", ocv_est_uv);
+	return ocv_est_uv;
+}
+
+static bool is_warm_restart(struct pm8921_bms_chip *chip)
+{
+	u8 reg;
+	int rc;
+
+	rc = pm8xxx_readb(chip->dev->parent, PON_CNTRL_6, &reg);
+	if (rc) {
+		pr_err("err reading pon 6 rc = %d\n", rc);
+		return false;
+	}
+	return reg & WD_BIT;
+}
+/*
+ * This reflects what should the CC readings should be for
+ * a 5mAh discharge. This value is dependent on
+ * CC_RESOLUTION_N, CC_RESOLUTION_D, CC_READING_TICKS
+ * and rsense
+ */
+#define CC_RAW_5MAH	0x00110000
+#define MIN_OCV_UV	2000000
 static int read_soc_params_raw(struct pm8921_bms_chip *chip,
 				struct pm8921_soc_params *raw)
 {
 	int usb_chg;
+	int est_ocv_uv;
 
 	mutex_lock(&chip->bms_output_lock);
 	pm_bms_lock_output_data(chip);
@@ -679,12 +746,40 @@
 
 	if (chip->prev_last_good_ocv_raw == 0) {
 		chip->prev_last_good_ocv_raw = raw->last_good_ocv_raw;
-		adjust_pon_ocv_raw(chip, raw);
+
 		convert_vbatt_raw_to_uv(chip, usb_chg,
 			raw->last_good_ocv_raw, &raw->last_good_ocv_uv);
+		adjust_pon_ocv(chip, &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;
+
+		if (is_warm_restart(chip)
+			|| raw->cc > CC_RAW_5MAH
+			|| (raw->last_good_ocv_uv < MIN_OCV_UV
+			&& raw->cc > 0)) {
+			/*
+			 * The CC value is higher than 5mAh.
+			 * The phone started without going through a pon
+			 * sequence
+			 * OR
+			 * The ocv was very small and there was no
+			 * charging in the bootloader
+			 * - reset the CC and take an ocv again
+			 */
+			pr_debug("cc_raw = 0x%x may be > 5mAh(0x%x)\n",
+				       raw->cc,	CC_RAW_5MAH);
+			pr_debug("ocv_uv = %d ocv_raw = 0x%x may be < 2V\n",
+				       chip->last_ocv_uv,
+				       raw->last_good_ocv_raw);
+			est_ocv_uv = estimate_ocv(chip);
+			if (est_ocv_uv > 0) {
+				raw->last_good_ocv_uv = est_ocv_uv;
+				chip->last_ocv_uv = est_ocv_uv;
+				reset_cc(chip);
+				raw->cc = 0;
+			}
+		}
 		pr_debug("PON_OCV_UV = %d\n", chip->last_ocv_uv);
 	} else if (chip->prev_last_good_ocv_raw != raw->last_good_ocv_raw) {
 		chip->prev_last_good_ocv_raw = raw->last_good_ocv_raw;
@@ -840,7 +935,7 @@
  */
 static void calculate_cc_uah(struct pm8921_bms_chip *chip, int cc, int *val)
 {
-	int64_t cc_voltage_uv, cc_nvh, cc_uah;
+	int64_t cc_voltage_uv, cc_uvh, cc_uah;
 
 	cc_voltage_uv = cc;
 	cc_voltage_uv -= chip->cc_reading_at_100;
@@ -850,12 +945,28 @@
 	cc_voltage_uv = cc_to_microvolt(chip, cc_voltage_uv);
 	cc_voltage_uv = pm8xxx_cc_adjust_for_gain(cc_voltage_uv);
 	pr_debug("cc_voltage_uv = %lld microvolts\n", cc_voltage_uv);
-	cc_nvh = ccmicrovolt_to_nvh(cc_voltage_uv);
-	pr_debug("cc_nvh = %lld nano_volt_hour\n", cc_nvh);
-	cc_uah = div_s64(cc_nvh, chip->r_sense);
+	cc_uvh = ccmicrovolt_to_uvh(cc_voltage_uv);
+	pr_debug("cc_uvh = %lld micro_volt_hour\n", cc_uvh);
+	cc_uah = div_s64(cc_uvh * 1000000LL, chip->r_sense_uohm);
 	*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,
@@ -1354,6 +1465,7 @@
 		pr_debug("voltage = %d low holding wakelock\n", vbat_uv);
 		wake_lock(&chip->low_voltage_wake_lock);
 		chip->low_voltage_wake_lock_held = 1;
+		chip->soc_calc_period = chip->low_voltage_calc_ms;
 	}
 
 	if (vbat_uv > (chip->v_cutoff + 20) * 1000
@@ -1361,6 +1473,7 @@
 		pr_debug("voltage = %d releasing wakelock\n", vbat_uv);
 		chip->low_voltage_wake_lock_held = 0;
 		wake_unlock(&chip->low_voltage_wake_lock);
+		chip->soc_calc_period = chip->normal_voltage_calc_ms;
 	}
 }
 
@@ -1398,7 +1511,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);
@@ -1795,7 +1908,6 @@
 	return calculated_soc;
 }
 
-#define CALCULATE_SOC_MS	20000
 static void calculate_soc_work(struct work_struct *work)
 {
 	struct pm8921_bms_chip *chip = container_of(work,
@@ -1825,7 +1937,7 @@
 
 	schedule_delayed_work(&chip->calculate_soc_delayed_work,
 			round_jiffies_relative(msecs_to_jiffies
-			(CALCULATE_SOC_MS)));
+			(chip->soc_calc_period)));
 }
 
 static int report_state_of_charge(struct pm8921_bms_chip *chip)
@@ -1895,7 +2007,8 @@
 	}
 
 	/* last_soc < soc  ... scale and catch up */
-	if (last_soc != -EINVAL && last_soc < soc && soc != 100)
+	if (last_soc != -EINVAL && last_soc < soc && soc != 100
+				&& chip->catch_up_time_us != 0)
 		soc = scale_soc_while_chg(chip, delta_time_us, soc, last_soc);
 
 	last_soc = soc;
@@ -2054,25 +2167,25 @@
 
 int pm8921_bms_get_battery_current(int *result_ua)
 {
-	int vsense;
+	int vsense_uv;
 
 	if (!the_chip) {
 		pr_err("called before initialization\n");
 		return -EINVAL;
 	}
-	if (the_chip->r_sense == 0) {
+	if (the_chip->r_sense_uohm == 0) {
 		pr_err("r_sense is zero\n");
 		return -EINVAL;
 	}
 
 	mutex_lock(&the_chip->bms_output_lock);
 	pm_bms_lock_output_data(the_chip);
-	read_vsense_avg(the_chip, &vsense);
+	read_vsense_avg(the_chip, &vsense_uv);
 	pm_bms_unlock_output_data(the_chip);
 	mutex_unlock(&the_chip->bms_output_lock);
-	pr_debug("vsense=%duV\n", vsense);
+	pr_debug("vsense=%duV\n", vsense_uv);
 	/* cast for signed division */
-	*result_ua = vsense * 1000 / (int)the_chip->r_sense;
+	*result_ua = div_s64(vsense_uv * 1000000LL, the_chip->r_sense_uohm);
 	pr_debug("ibat=%duA\n", *result_ua);
 	return 0;
 }
@@ -2669,7 +2782,9 @@
 	int ret = 0;
 	struct pm8921_soc_params raw;
 
+	mutex_lock(&the_chip->bms_output_lock);
 	read_soc_params_raw(the_chip, &raw);
+	mutex_unlock(&the_chip->bms_output_lock);
 
 	*val = 0;
 
@@ -2880,7 +2995,7 @@
 	mutex_init(&chip->bms_output_lock);
 	mutex_init(&chip->last_ocv_uv_mutex);
 	chip->dev = &pdev->dev;
-	chip->r_sense = pdata->r_sense;
+	chip->r_sense_uohm = pdata->r_sense_uohm;
 	chip->v_cutoff = pdata->v_cutoff;
 	chip->max_voltage_uv = pdata->max_voltage_uv;
 	chip->chg_term_ua = pdata->chg_term_ua;
@@ -2890,6 +3005,12 @@
 	chip->end_percent = -EINVAL;
 	chip->shutdown_soc_valid_limit = pdata->shutdown_soc_valid_limit;
 	chip->adjust_soc_low_threshold = pdata->adjust_soc_low_threshold;
+
+	chip->normal_voltage_calc_ms = pdata->normal_voltage_calc_ms;
+	chip->low_voltage_calc_ms = pdata->low_voltage_calc_ms;
+
+	chip->soc_calc_period = pdata->normal_voltage_calc_ms;
+
 	if (chip->adjust_soc_low_threshold >= 45)
 		chip->adjust_soc_low_threshold = 45;
 
diff --git a/drivers/power/pm8921-charger.c b/drivers/power/pm8921-charger.c
index 3977f17..20b3fc6 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
@@ -87,6 +87,7 @@
 #define EOC_CHECK_PERIOD_MS	10000
 /* check for USB unplug every 200 msecs */
 #define UNPLUG_CHECK_WAIT_PERIOD_MS 200
+#define USB_TRIM_ENTRIES 16
 
 enum chg_fsm_state {
 	FSM_STATE_OFF_0 = 0,
@@ -209,7 +210,6 @@
  * @dc_present:			present status of dc
  * @usb_charger_current:	usb current to charge the battery with used when
  *				the usb path is enabled or charging is resumed
- * @safety_time:		max time for which charging will happen
  * @update_time:		how frequently the userland needs to be updated
  * @max_voltage_mv:		the max volts the batt should be charged up to
  * @min_voltage_mv:		the min battery voltage before turning the FETon
@@ -230,12 +230,12 @@
 	unsigned int			usb_charger_current;
 	unsigned int			max_bat_chg_current;
 	unsigned int			pmic_chg_irq[PM_CHG_MAX_INTS];
-	unsigned int			safety_time;
 	unsigned int			ttrkl_time;
 	unsigned int			update_time;
 	unsigned int			max_voltage_mv;
 	unsigned int			min_voltage_mv;
 	unsigned int			uvd_voltage_mv;
+	unsigned int			safe_current_ma;
 	unsigned int			alarm_low_mv;
 	unsigned int			alarm_high_mv;
 	int				cool_temp_dc;
@@ -259,11 +259,10 @@
 	struct power_supply		batt_psy;
 	struct dentry			*dent;
 	struct bms_notify		bms_notify;
-	bool				keep_btm_on_suspend;
+	int				*usb_trim_table;
 	bool				ext_charging;
 	bool				ext_charge_done;
 	bool				iusb_fine_res;
-	bool				dc_unplug_check;
 	bool				disable_hw_clock_switching;
 	DECLARE_BITMAP(enabled_irqs, PM_CHG_MAX_INTS);
 	struct work_struct		battery_id_valid_work;
@@ -289,6 +288,8 @@
 	bool				has_dc_supply;
 	u8				active_path;
 	int				recent_reported_soc;
+	int				battery_less_hardware;
+	int				ibatmax_max_adj_ma;
 };
 
 /* user space parameter to limit usb current */
@@ -304,8 +305,6 @@
 
 static struct pm8921_chg_chip *the_chip;
 
-static struct pm8xxx_adc_arb_btm_param btm_config;
-
 static int pm_chg_masked_write(struct pm8921_chg_chip *chip, u16 addr,
 							u8 mask, u8 val)
 {
@@ -633,10 +632,26 @@
 }
 
 #define PM8921_CHG_IBATMAX_MIN	325
-#define PM8921_CHG_IBATMAX_MAX	2000
+#define PM8921_CHG_IBATMAX_MAX	3025
 #define PM8921_CHG_I_MIN_MA	225
 #define PM8921_CHG_I_STEP_MA	50
 #define PM8921_CHG_I_MASK	0x3F
+static int pm_chg_ibatmax_get(struct pm8921_chg_chip *chip, int *ibat_ma)
+{
+	u8 temp;
+	int rc;
+
+	rc = pm8xxx_readb(chip->dev->parent, CHG_IBAT_MAX, &temp);
+	if (rc) {
+		pr_err("rc = %d while reading ibat max\n", rc);
+		*ibat_ma = 0;
+		return rc;
+	}
+	*ibat_ma = (int)(temp & PM8921_CHG_I_MASK) * PM8921_CHG_I_STEP_MA
+							+ PM8921_CHG_I_MIN_MA;
+	return 0;
+}
+
 static int pm_chg_ibatmax_set(struct pm8921_chg_chip *chip, int chg_current)
 {
 	u8 temp;
@@ -708,6 +723,46 @@
 	u8	value;
 };
 
+/* USB Trim tables */
+static int usb_trim_8038_table[USB_TRIM_ENTRIES] = {
+	0x0,
+	0x0,
+	-0x9,
+	0x0,
+	-0xD,
+	0x0,
+	-0x10,
+	-0x11,
+	0x0,
+	0x0,
+	-0x25,
+	0x0,
+	-0x28,
+	0x0,
+	-0x32,
+	0x0
+};
+
+static int usb_trim_8917_table[USB_TRIM_ENTRIES] = {
+	0x0,
+	0x0,
+	0xA,
+	0xC,
+	0x10,
+	0x10,
+	0x13,
+	0x14,
+	0x13,
+	0x16,
+	0x1A,
+	0x1D,
+	0x1D,
+	0x21,
+	0x24,
+	0x26
+};
+
+/* Maximum USB  setting table */
 static struct usb_ma_limit_entry usb_ma_table[] = {
 	{100, 0x0},
 	{200, 0x1},
@@ -727,18 +782,92 @@
 	{1600, 0xF},
 };
 
+#define REG_SBI_CONFIG		0x04F
+#define PAGE3_ENABLE_MASK	0x6
+#define USB_OVP_TRIM_MASK	0x3F
+#define USB_OVP_TRIM_MIN	0x00
+#define REG_USB_OVP_TRIM_ORIG_LSB	0x10A
+#define REG_USB_OVP_TRIM_ORIG_MSB	0x09C
+static int pm_chg_usb_trim(struct pm8921_chg_chip *chip, int index)
+{
+	u8 temp, sbi_config, msb, lsb;
+	s8 trim;
+	int rc = 0;
+	static u8 usb_trim_reg_orig = 0xFF;
+
+	/* No trim data for PM8921 */
+	if (!chip->usb_trim_table)
+		return 0;
+
+	if (usb_trim_reg_orig == 0xFF) {
+		rc = pm8xxx_readb(chip->dev->parent,
+				REG_USB_OVP_TRIM_ORIG_MSB, &msb);
+		if (rc) {
+			pr_err("error = %d reading sbi config reg\n", rc);
+			return rc;
+		}
+
+		rc = pm8xxx_readb(chip->dev->parent,
+				REG_USB_OVP_TRIM_ORIG_LSB, &lsb);
+		if (rc) {
+			pr_err("error = %d reading sbi config reg\n", rc);
+			return rc;
+		}
+
+		msb = msb >> 5;
+		lsb = lsb >> 5;
+		usb_trim_reg_orig = msb << 3 | lsb;
+	}
+
+	/* use the original trim value */
+	trim = usb_trim_reg_orig;
+
+	trim += chip->usb_trim_table[index];
+	if (trim < 0)
+		trim = 0;
+
+	pr_err("trim_orig %d write 0x%x index=%d value 0x%x to USB_OVP_TRIM\n",
+		usb_trim_reg_orig, trim, index, chip->usb_trim_table[index]);
+
+	rc = pm8xxx_readb(chip->dev->parent, REG_SBI_CONFIG, &sbi_config);
+	if (rc) {
+		pr_err("error = %d reading sbi config reg\n", rc);
+		return rc;
+	}
+
+	temp = sbi_config | PAGE3_ENABLE_MASK;
+	rc = pm8xxx_writeb(chip->dev->parent, REG_SBI_CONFIG, temp);
+	if (rc) {
+		pr_err("error = %d writing sbi config reg\n", rc);
+		return rc;
+	}
+
+	rc = pm_chg_masked_write(chip, USB_OVP_TRIM, USB_OVP_TRIM_MASK, trim);
+	if (rc) {
+		pr_err("error = %d writing USB_OVP_TRIM\n", rc);
+		return rc;
+	}
+
+	rc = pm8xxx_writeb(chip->dev->parent, REG_SBI_CONFIG, sbi_config);
+	if (rc) {
+		pr_err("error = %d writing sbi config reg\n", rc);
+		return rc;
+	}
+	return rc;
+}
+
 #define PM8921_CHG_IUSB_MASK 0x1C
 #define PM8921_CHG_IUSB_SHIFT 2
 #define PM8921_CHG_IUSB_MAX  7
 #define PM8921_CHG_IUSB_MIN  0
 #define PM8917_IUSB_FINE_RES BIT(0)
-static int pm_chg_iusbmax_set(struct pm8921_chg_chip *chip, int reg_val)
+static int pm_chg_iusbmax_set(struct pm8921_chg_chip *chip, int index)
 {
-	u8 temp, fineres;
+	u8 temp, fineres, reg_val;
 	int rc;
 
-	fineres = PM8917_IUSB_FINE_RES & usb_ma_table[reg_val].value;
-	reg_val = usb_ma_table[reg_val].value >> 1;
+	reg_val = usb_ma_table[index].value >> 1;
+	fineres = PM8917_IUSB_FINE_RES & usb_ma_table[index].value;
 
 	if (reg_val < PM8921_CHG_IUSB_MIN || reg_val > PM8921_CHG_IUSB_MAX) {
 		pr_err("bad mA=%d asked to set\n", reg_val);
@@ -763,17 +892,25 @@
 		if (fineres) {
 			rc = pm_chg_masked_write(chip, IUSB_FINE_RES,
 				PM8917_IUSB_FINE_RES, fineres);
-			if (rc)
+			if (rc) {
 				pr_err("Failed to write ISUB_FINE_RES rc=%d\n",
 					rc);
+				return rc;
+			}
 		}
 	} else {
 		rc = pm_chg_masked_write(chip, PBL_ACCESS2,
 			PM8921_CHG_IUSB_MASK, temp);
-		if (rc)
+		if (rc) {
 			pr_err("Failed to write PBL_ACCESS2 rc=%d\n", rc);
+			return rc;
+		}
 	}
 
+	rc = pm_chg_usb_trim(chip, index);
+	if (rc)
+			pr_err("unable to set usb trim rc = %d\n", rc);
+
 	return rc;
 }
 
@@ -806,6 +943,11 @@
 			break;
 	}
 
+	if (i < 0) {
+		pr_err("can't find %d in usb_ma_table. Use min.\n", temp);
+		i = 0;
+	}
+
 	*mA = usb_ma_table[i].usb_ma;
 
 	return rc;
@@ -985,55 +1127,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;
-	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;
-	}
-	/* unset the input voltage disable bit */
-	temp &= 0xFE;
-	/* set the write bit */
-	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;
-	}
-}
 
 static int64_t read_battery_id(struct pm8921_chg_chip *chip)
 {
@@ -1334,15 +1427,6 @@
 	case POWER_SUPPLY_PROP_PRESENT:
 	case POWER_SUPPLY_PROP_ONLINE:
 		val->intval = 0;
-		if (charging_disabled)
-			return 0;
-
-		/*
-		 * if drawing any current from usb is disabled behave
-		 * as if no usb cable is connected
-		 */
-		if (pm_is_chg_charge_dis(the_chip))
-			return 0;
 
 		/* USB charging */
 		if (usb_target_ma < USB_WALL_THRESHOLD_MA)
@@ -1376,7 +1460,8 @@
 	POWER_SUPPLY_PROP_CURRENT_MAX,
 	POWER_SUPPLY_PROP_CURRENT_NOW,
 	POWER_SUPPLY_PROP_TEMP,
-	POWER_SUPPLY_PROP_ENERGY_FULL,
+	POWER_SUPPLY_PROP_CHARGE_FULL,
+	POWER_SUPPLY_PROP_CHARGE_NOW,
 };
 
 static int get_prop_battery_uvolts(struct pm8921_chg_chip *chip)
@@ -1416,10 +1501,41 @@
 	return pm_chg_get_rt_status(chip, BATT_INSERTED_IRQ);
 }
 
+static int get_prop_batt_status(struct pm8921_chg_chip *chip)
+{
+	int batt_state = POWER_SUPPLY_STATUS_DISCHARGING;
+	int fsm_state = pm_chg_get_fsm_state(chip);
+	int i;
+
+	if (chip->ext_psy) {
+		if (chip->ext_charge_done)
+			return POWER_SUPPLY_STATUS_FULL;
+		if (chip->ext_charging)
+			return POWER_SUPPLY_STATUS_CHARGING;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(map); i++)
+		if (map[i].fsm_state == fsm_state)
+			batt_state = map[i].batt_state;
+
+	if (fsm_state == FSM_STATE_ON_CHG_HIGHI_1) {
+		if (!pm_chg_get_rt_status(chip, BATT_INSERTED_IRQ)
+			|| !pm_chg_get_rt_status(chip, BAT_TEMP_OK_IRQ)
+			|| pm_chg_get_rt_status(chip, CHGHOT_IRQ)
+			|| pm_chg_get_rt_status(chip, VBATDET_LOW_IRQ))
+
+			batt_state = POWER_SUPPLY_STATUS_NOT_CHARGING;
+	}
+	return batt_state;
+}
+
 static int get_prop_batt_capacity(struct pm8921_chg_chip *chip)
 {
 	int percent_soc;
 
+	if (chip->battery_less_hardware)
+		return 100;
+
 	if (!get_prop_batt_present(chip))
 		percent_soc = voltage_based_capacity(chip);
 	else
@@ -1432,8 +1548,8 @@
 		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) {
+	if (percent_soc <= chip->resume_charge_percent
+		&& get_prop_batt_status(chip) == POWER_SUPPLY_STATUS_FULL) {
 		pr_debug("soc fell below %d. charging enabled.\n",
 						chip->resume_charge_percent);
 		if (chip->is_bat_warm)
@@ -1509,6 +1625,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;
@@ -1548,40 +1678,15 @@
 	return POWER_SUPPLY_CHARGE_TYPE_NONE;
 }
 
-static int get_prop_batt_status(struct pm8921_chg_chip *chip)
-{
-	int batt_state = POWER_SUPPLY_STATUS_DISCHARGING;
-	int fsm_state = pm_chg_get_fsm_state(chip);
-	int i;
-
-	if (chip->ext_psy) {
-		if (chip->ext_charge_done)
-			return POWER_SUPPLY_STATUS_FULL;
-		if (chip->ext_charging)
-			return POWER_SUPPLY_STATUS_CHARGING;
-	}
-
-	for (i = 0; i < ARRAY_SIZE(map); i++)
-		if (map[i].fsm_state == fsm_state)
-			batt_state = map[i].batt_state;
-
-	if (fsm_state == FSM_STATE_ON_CHG_HIGHI_1) {
-		if (!pm_chg_get_rt_status(chip, BATT_INSERTED_IRQ)
-			|| !pm_chg_get_rt_status(chip, BAT_TEMP_OK_IRQ)
-			|| pm_chg_get_rt_status(chip, CHGHOT_IRQ)
-			|| pm_chg_get_rt_status(chip, VBATDET_LOW_IRQ))
-
-			batt_state = POWER_SUPPLY_STATUS_NOT_CHARGING;
-	}
-	return batt_state;
-}
-
 #define MAX_TOLERABLE_BATT_TEMP_DDC	680
 static int get_prop_batt_temp(struct pm8921_chg_chip *chip)
 {
 	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",
@@ -1641,8 +1746,11 @@
 	case POWER_SUPPLY_PROP_TEMP:
 		val->intval = get_prop_batt_temp(chip);
 		break;
-	case POWER_SUPPLY_PROP_ENERGY_FULL:
-		val->intval = get_prop_batt_fcc(chip) * 1000;
+	case POWER_SUPPLY_PROP_CHARGE_FULL:
+		val->intval = get_prop_batt_fcc(chip);
+		break;
+	case POWER_SUPPLY_PROP_CHARGE_NOW:
+		val->intval = get_prop_batt_charge_now(chip);
 		break;
 	default:
 		return -EINVAL;
@@ -1691,7 +1799,13 @@
 		return;
 	}
 
-	if (mA >= 0 && mA <= 2) {
+	if (usb_max_current && mA > usb_max_current) {
+		pr_debug("restricting usb current to %d instead of %d\n",
+					usb_max_current, mA);
+		mA = usb_max_current;
+	}
+
+	if (mA <= 2) {
 		usb_chg_current = 0;
 		rc = pm_chg_iusbmax_set(the_chip, 0);
 		if (rc) {
@@ -1709,16 +1823,21 @@
 				break;
 		}
 
+		if (i < 0) {
+			pr_err("can't find %dmA in usb_ma_table. Use min.\n",
+			       mA);
+			i = 0;
+		}
+
 		/* 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)
 			i = 0;
 		rc = pm_chg_iusbmax_set(the_chip, i);
-		if (rc) {
+		if (rc)
 			pr_err("unable to set iusb to %d rc = %d\n", i, rc);
-		}
 	}
 }
 
@@ -1779,22 +1898,6 @@
 }
 EXPORT_SYMBOL_GPL(pm8921_charger_vbus_draw);
 
-int pm8921_charger_enable(bool enable)
-{
-	int rc;
-
-	if (!the_chip) {
-		pr_err("called before init\n");
-		return -EINVAL;
-	}
-	enable = !!enable;
-	rc = pm_chg_auto_enable(the_chip, enable);
-	if (rc)
-		pr_err("Failed rc=%d\n", rc);
-	return rc;
-}
-EXPORT_SYMBOL(pm8921_charger_enable);
-
 int pm8921_is_usb_chg_plugged_in(void)
 {
 	if (!the_chip) {
@@ -2125,7 +2228,6 @@
 		usb_target_ma = 0;
 		pm8921_chg_disable_irq(chip, CHG_GONE_IRQ);
 	}
-	enable_input_voltage_regulation(chip);
 	bms_notify_check(chip);
 }
 
@@ -2187,7 +2289,10 @@
 		pr_warn("%s. battery temperature not ok.\n", __func__);
 		return;
 	}
-	pm8921_disable_source_current(true); /* Force BATFET=ON */
+
+	/* Force BATFET=ON */
+	pm8921_disable_source_current(true);
+
 	vbat_ov = pm_chg_get_rt_status(chip, VBAT_OV_IRQ);
 	if (vbat_ov) {
 		pr_warn("%s. battery over voltage.\n", __func__);
@@ -2197,16 +2302,17 @@
 	schedule_delayed_work(&chip->unplug_check_work,
 	round_jiffies_relative(msecs_to_jiffies
 		(UNPLUG_CHECK_WAIT_PERIOD_MS)));
-	pm8921_chg_enable_irq(chip, CHG_GONE_IRQ);
 
 	power_supply_set_online(chip->ext_psy, dc_present);
 	power_supply_set_charge_type(chip->ext_psy,
 					POWER_SUPPLY_CHARGE_TYPE_FAST);
-	power_supply_changed(&chip->dc_psy);
 	chip->ext_charging = true;
 	chip->ext_charge_done = false;
 	bms_notify_check(chip);
-	/* Start BMS */
+	/*
+	 * since we wont get a fastchg irq from external charger
+	 * use eoc worker to detect end of charging
+	 */
 	schedule_delayed_work(&chip->eoc_work, delay);
 	wake_lock(&chip->eoc_wake_lock);
 	/* Update battery charging LEDs and user space battery info */
@@ -2407,6 +2513,13 @@
 		while (!the_chip->iusb_fine_res && i > 0
 			&& (usb_ma_table[i].value & PM8917_IUSB_FINE_RES))
 			i--;
+
+		if (i < 0) {
+			pr_err("can't find %dmA in usb_ma_table. Use min.\n",
+			       *value);
+			i = 0;
+		}
+
 		*value = usb_ma_table[i].usb_ma;
 	}
 }
@@ -2436,15 +2549,25 @@
 	struct pm8921_chg_chip *chip = container_of(dwork,
 			struct pm8921_chg_chip, vin_collapse_check_work);
 
-	/* AICL only for wall-chargers */
-	if (is_usb_chg_plugged_in(chip) &&
-		usb_target_ma > USB_WALL_THRESHOLD_MA) {
+	/*
+	 * AICL only for wall-chargers. If the charger appears to be plugged
+	 * back in now, the corresponding unplug must have been because of we
+	 * were trying to draw more current than the charger can support. In
+	 * such a case reset usb current to 500mA and decrease the target.
+	 * The AICL algorithm will step up the current from 500mA to target
+	 */
+	if (is_usb_chg_plugged_in(chip)
+		&& usb_target_ma > USB_WALL_THRESHOLD_MA) {
 		/* decrease usb_target_ma */
 		decrease_usb_ma_value(&usb_target_ma);
 		/* reset here, increase in unplug_check_worker */
 		__pm8921_charger_vbus_draw(USB_WALL_THRESHOLD_MA);
 		pr_debug("usb_now=%d, usb_target = %d\n",
 				USB_WALL_THRESHOLD_MA, usb_target_ma);
+		if (!delayed_work_pending(&chip->unplug_check_work))
+			schedule_delayed_work(&chip->unplug_check_work,
+				      round_jiffies_relative(msecs_to_jiffies
+						(UNPLUG_CHECK_WAIT_PERIOD_MS)));
 	} else {
 		handle_usb_insertion_removal(chip);
 	}
@@ -2587,29 +2710,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)
@@ -2640,11 +2783,6 @@
 		pr_debug("USB charger active\n");
 
 		pm_chg_iusbmax_get(chip, &usb_ma);
-		if (usb_ma == 500 && !usb_target_ma) {
-			pr_debug("Stopping Unplug Check Worker USB == 500mA\n");
-			disable_input_voltage_regulation(chip);
-			return;
-		}
 
 		if (usb_ma <= 100) {
 			pr_debug(
@@ -2654,10 +2792,6 @@
 		}
 	} 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 */
-		if (!chip->dc_unplug_check)
-			return;
 	} else {
 		/* No charger active */
 		if (!(is_usb_chg_plugged_in(chip)
@@ -2673,8 +2807,8 @@
 			goto check_again_later;
 		}
 	}
-
-	if (active_path & USB_ACTIVE_BIT) {
+	/* AICL only for usb wall charger */
+	if ((active_path & USB_ACTIVE_BIT) && usb_target_ma > 0) {
 		reg_loop = pm_chg_get_regulation_loop(chip);
 		pr_debug("reg_loop=0x%x usb_ma = %d\n", reg_loop, usb_ma);
 		if ((reg_loop & VIN_ACTIVE_BIT) &&
@@ -2694,27 +2828,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);
 		}
 	}
 
@@ -2729,7 +2853,9 @@
 		unplug_ovp_fet_open(chip);
 	}
 
+	/* AICL only for usb wall charger */
 	if (!(reg_loop & VIN_ACTIVE_BIT) && (active_path & USB_ACTIVE_BIT)
+		&& usb_target_ma > 0
 		&& !charging_disabled) {
 		/* only increase iusb_max if vin loop not active */
 		if (usb_ma < usb_target_ma) {
@@ -2759,6 +2885,30 @@
 	return IRQ_HANDLED;
 }
 
+struct ibatmax_max_adj_entry {
+	int ibat_max_ma;
+	int max_adj_ma;
+};
+
+static struct ibatmax_max_adj_entry ibatmax_adj_table[] = {
+	{975, 300},
+	{1475, 150},
+	{1975, 200},
+	{2475, 250},
+};
+
+static int find_ibat_max_adj_ma(int ibat_target_ma)
+{
+	int i = 0;
+
+	for (i = ARRAY_SIZE(ibatmax_adj_table) - 1; i >= 0; i--) {
+		if (ibat_target_ma <= ibatmax_adj_table[i].ibat_max_ma)
+			break;
+	}
+
+	return ibatmax_adj_table[i].max_adj_ma;
+}
+
 static irqreturn_t fastchg_irq_handler(int irq, void *data)
 {
 	struct pm8921_chg_chip *chip = data;
@@ -2914,20 +3064,33 @@
 	struct pm8921_chg_chip *chip = data;
 	int dc_present;
 
+	pm_chg_failed_clear(chip, 1);
 	dc_present = pm_chg_get_rt_status(chip, DCIN_VALID_IRQ);
-	if (chip->ext_psy)
-		power_supply_set_online(chip->ext_psy, dc_present);
-	chip->dc_present = dc_present;
-	if (dc_present)
-		handle_start_ext_chg(chip);
-	else
-		handle_stop_ext_chg(chip);
 
-	if (!chip->ext_psy) {
+	if (chip->dc_present ^ dc_present)
+		pm8921_bms_calibrate_hkadc();
+
+	if (dc_present)
+		pm8921_chg_enable_irq(chip, CHG_GONE_IRQ);
+	else
+		pm8921_chg_disable_irq(chip, CHG_GONE_IRQ);
+
+	chip->dc_present = dc_present;
+
+	if (chip->ext_psy) {
+		if (dc_present)
+			handle_start_ext_chg(chip);
+		else
+			handle_stop_ext_chg(chip);
+	} else {
+		if (dc_present)
+			schedule_delayed_work(&chip->unplug_check_work,
+				round_jiffies_relative(msecs_to_jiffies
+					(UNPLUG_CHECK_WAIT_PERIOD_MS)));
 		power_supply_changed(&chip->dc_psy);
-		power_supply_changed(&chip->batt_psy);
 	}
 
+	power_supply_changed(&chip->batt_psy);
 	return IRQ_HANDLED;
 }
 
@@ -3062,6 +3225,95 @@
 	pm_chg_vddmax_set(chip, adj_vdd_max_mv);
 }
 
+static void set_appropriate_vbatdet(struct pm8921_chg_chip *chip)
+{
+	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);
+}
+
+static void set_appropriate_battery_current(struct pm8921_chg_chip *chip)
+{
+	unsigned int chg_current = chip->max_bat_chg_current;
+
+	if (chip->is_bat_cool)
+		chg_current = min(chg_current, chip->cool_bat_chg_current);
+
+	if (chip->is_bat_warm)
+		chg_current = min(chg_current, chip->warm_bat_chg_current);
+
+	if (thermal_mitigation != 0 && chip->thermal_mitigation)
+		chg_current = min(chg_current,
+				chip->thermal_mitigation[thermal_mitigation]);
+
+	pm_chg_ibatmax_set(the_chip, chg_current);
+}
+
+#define TEMP_HYSTERISIS_DECIDEGC 20
+static void battery_cool(bool enter)
+{
+	pr_debug("enter = %d\n", enter);
+	if (enter == the_chip->is_bat_cool)
+		return;
+	the_chip->is_bat_cool = enter;
+	if (enter)
+		pm_chg_vddmax_set(the_chip, the_chip->cool_bat_voltage);
+	else
+		pm_chg_vddmax_set(the_chip, the_chip->max_voltage_mv);
+	set_appropriate_battery_current(the_chip);
+	set_appropriate_vbatdet(the_chip);
+}
+
+static void battery_warm(bool enter)
+{
+	pr_debug("enter = %d\n", enter);
+	if (enter == the_chip->is_bat_warm)
+		return;
+	the_chip->is_bat_warm = enter;
+	if (enter)
+		pm_chg_vddmax_set(the_chip, the_chip->warm_bat_voltage);
+	else
+		pm_chg_vddmax_set(the_chip, the_chip->max_voltage_mv);
+
+	set_appropriate_battery_current(the_chip);
+	set_appropriate_vbatdet(the_chip);
+}
+
+static void check_temp_thresholds(struct pm8921_chg_chip *chip)
+{
+	int temp = 0;
+
+	temp = get_prop_batt_temp(chip);
+	pr_debug("temp = %d, warm_thr_temp = %d, cool_thr_temp = %d\n",
+			temp, chip->warm_temp_dc,
+			chip->cool_temp_dc);
+
+	if (chip->warm_temp_dc != INT_MIN) {
+		if (chip->is_bat_warm
+			&& temp < chip->warm_temp_dc - TEMP_HYSTERISIS_DECIDEGC)
+			battery_warm(false);
+		else if (!chip->is_bat_warm && temp >= chip->warm_temp_dc)
+			battery_warm(true);
+	}
+
+	if (chip->cool_temp_dc != INT_MIN) {
+		if (chip->is_bat_cool
+			&& temp > chip->cool_temp_dc + TEMP_HYSTERISIS_DECIDEGC)
+			battery_cool(false);
+		else if (!chip->is_bat_cool && temp <= chip->cool_temp_dc)
+			battery_cool(true);
+	}
+}
+
 enum {
 	CHG_IN_PROGRESS,
 	CHG_NOT_IN_PROGRESS,
@@ -3200,8 +3452,7 @@
 
 	if (end == CHG_NOT_IN_PROGRESS) {
 		count = 0;
-		wake_unlock(&chip->eoc_wake_lock);
-		return;
+		goto eoc_worker_stop;
 	}
 
 	/* If the disable hw clock switching
@@ -3225,21 +3476,6 @@
 	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);
 
@@ -3252,120 +3488,20 @@
 			chip->bms_notify.is_battery_full = 1;
 		/* declare end of charging by invoking chgdone interrupt */
 		chgdone_irq_handler(chip->pmic_chg_irq[CHGDONE_IRQ], chip);
-		wake_unlock(&chip->eoc_wake_lock);
 	} else {
+		check_temp_thresholds(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
 						     (EOC_CHECK_PERIOD_MS)));
-	}
-}
-
-static void btm_configure_work(struct work_struct *work)
-{
-	int rc;
-
-	rc = pm8xxx_adc_btm_configure(&btm_config);
-	if (rc)
-		pr_err("failed to configure btm rc=%d", rc);
-}
-
-DECLARE_WORK(btm_config_work, btm_configure_work);
-
-static void set_appropriate_battery_current(struct pm8921_chg_chip *chip)
-{
-	unsigned int chg_current = chip->max_bat_chg_current;
-
-	if (chip->is_bat_cool)
-		chg_current = min(chg_current, chip->cool_bat_chg_current);
-
-	if (chip->is_bat_warm)
-		chg_current = min(chg_current, chip->warm_bat_chg_current);
-
-	if (thermal_mitigation != 0 && chip->thermal_mitigation)
-		chg_current = min(chg_current,
-				chip->thermal_mitigation[thermal_mitigation]);
-
-	pm_chg_ibatmax_set(the_chip, chg_current);
-}
-
-#define TEMP_HYSTERISIS_DEGC 2
-static void battery_cool(bool enter)
-{
-	pr_debug("enter = %d\n", enter);
-	if (enter == the_chip->is_bat_cool)
 		return;
-	the_chip->is_bat_cool = enter;
-	if (enter) {
-		btm_config.low_thr_temp =
-			the_chip->cool_temp_dc + TEMP_HYSTERISIS_DEGC;
-		set_appropriate_battery_current(the_chip);
-		pm_chg_vddmax_set(the_chip, the_chip->cool_bat_voltage);
-		pm_chg_vbatdet_set(the_chip,
-			the_chip->cool_bat_voltage
-			- the_chip->resume_voltage_delta);
-	} else {
-		btm_config.low_thr_temp = the_chip->cool_temp_dc;
-		set_appropriate_battery_current(the_chip);
-		pm_chg_vddmax_set(the_chip, the_chip->max_voltage_mv);
-		pm_chg_vbatdet_set(the_chip,
-			the_chip->max_voltage_mv
-			- the_chip->resume_voltage_delta);
 	}
-	schedule_work(&btm_config_work);
-}
 
-static void battery_warm(bool enter)
-{
-	pr_debug("enter = %d\n", enter);
-	if (enter == the_chip->is_bat_warm)
-		return;
-	the_chip->is_bat_warm = enter;
-	if (enter) {
-		btm_config.high_thr_temp =
-			the_chip->warm_temp_dc - TEMP_HYSTERISIS_DEGC;
-		set_appropriate_battery_current(the_chip);
-		pm_chg_vddmax_set(the_chip, the_chip->warm_bat_voltage);
-		pm_chg_vbatdet_set(the_chip,
-			the_chip->warm_bat_voltage
-			- the_chip->resume_voltage_delta);
-	} else {
-		btm_config.high_thr_temp = the_chip->warm_temp_dc;
-		set_appropriate_battery_current(the_chip);
-		pm_chg_vddmax_set(the_chip, the_chip->max_voltage_mv);
-		pm_chg_vbatdet_set(the_chip,
-			the_chip->max_voltage_mv
-			- the_chip->resume_voltage_delta);
-	}
-	schedule_work(&btm_config_work);
-}
-
-static int configure_btm(struct pm8921_chg_chip *chip)
-{
-	int rc;
-
-	if (chip->warm_temp_dc != INT_MIN)
-		btm_config.btm_warm_fn = battery_warm;
-	else
-		btm_config.btm_warm_fn = NULL;
-
-	if (chip->cool_temp_dc != INT_MIN)
-		btm_config.btm_cool_fn = battery_cool;
-	else
-		btm_config.btm_cool_fn = NULL;
-
-	btm_config.low_thr_temp = chip->cool_temp_dc;
-	btm_config.high_thr_temp = chip->warm_temp_dc;
-	btm_config.interval = chip->temp_check_period;
-	rc = pm8xxx_adc_btm_configure(&btm_config);
-	if (rc)
-		pr_err("failed to configure btm rc = %d\n", rc);
-	rc = pm8xxx_adc_btm_start();
-	if (rc)
-		pr_err("failed to start btm rc = %d\n", rc);
-
-	return rc;
+eoc_worker_stop:
+	wake_unlock(&chip->eoc_wake_lock);
+	/* set the vbatdet back, in case it was changed to trigger charging */
+	set_appropriate_vbatdet(chip);
 }
 
 /**
@@ -3543,6 +3679,12 @@
 			chip->dc_present,
 			get_prop_batt_present(chip),
 			fsm_state);
+
+	/* Determine which USB trim column to use */
+	if (pm8xxx_get_version(chip->dev->parent) == PM8XXX_VERSION_8917)
+		chip->usb_trim_table = usb_trim_8917_table;
+	else if (pm8xxx_get_version(chip->dev->parent) == PM8XXX_VERSION_8038)
+		chip->usb_trim_table = usb_trim_8038_table;
 }
 
 struct pm_chg_irq_init_data {
@@ -3749,11 +3891,15 @@
 #define CHG_BAT_TEMP_DIS_BIT	BIT(2)
 #define SAFE_CURRENT_MA		1500
 #define PM_SUB_REV		0x001
+#define MIN_CHARGE_CURRENT_MA	350
+#define DEFAULT_SAFETY_MINUTES	500
 static int __devinit pm8921_chg_hw_init(struct pm8921_chg_chip *chip)
 {
 	int rc;
 	int vdd_safe;
 	u8 subrev;
+	int fcc_uah;
+	int safety_time = DEFAULT_SAFETY_MINUTES;
 
 	/* forcing 19p2mhz before accessing any charger registers */
 	pm8921_chg_force_19p2mhz_clk(chip);
@@ -3794,7 +3940,11 @@
 						chip->max_voltage_mv, rc);
 		return rc;
 	}
-	rc = pm_chg_ibatsafe_set(chip, SAFE_CURRENT_MA);
+
+	if (chip->safe_current_ma == 0)
+		chip->safe_current_ma = SAFE_CURRENT_MA;
+
+	rc = pm_chg_ibatsafe_set(chip, chip->safe_current_ma);
 	if (rc) {
 		pr_err("Failed to set max voltage to %d rc=%d\n",
 						SAFE_CURRENT_MA, rc);
@@ -3822,20 +3972,26 @@
 		return rc;
 	}
 
-	if (chip->safety_time != 0) {
-		rc = pm_chg_tchg_max_set(chip, chip->safety_time);
-		if (rc) {
-			pr_err("Failed to set max time to %d minutes rc=%d\n",
-							chip->safety_time, rc);
-			return rc;
-		}
+	fcc_uah = pm8921_bms_get_fcc();
+	if (fcc_uah > 0) {
+		safety_time = div_s64((s64)fcc_uah * 60,
+						1000 * MIN_CHARGE_CURRENT_MA);
+		/* add 20 minutes of buffer time */
+		safety_time += 20;
+	}
+
+	rc = pm_chg_tchg_max_set(chip, safety_time);
+	if (rc) {
+		pr_err("Failed to set max time to %d minutes rc=%d\n",
+						safety_time, rc);
+		return rc;
 	}
 
 	if (chip->ttrkl_time != 0) {
 		rc = pm_chg_ttrkl_max_set(chip, chip->ttrkl_time);
 		if (rc) {
 			pr_err("Failed to set trkl time to %d minutes rc=%d\n",
-							chip->safety_time, rc);
+							chip->ttrkl_time, rc);
 			return rc;
 		}
 	}
@@ -4055,6 +4211,81 @@
 }
 DEFINE_SIMPLE_ATTRIBUTE(reg_fops, get_reg, set_reg, "0x%02llx\n");
 
+static int reg_loop;
+#define MAX_REG_LOOP_CHAR	10
+static int get_reg_loop_param(char *buf, struct kernel_param *kp)
+{
+	u8 temp;
+
+	if (!the_chip) {
+		pr_err("called before init\n");
+		return -EINVAL;
+	}
+	temp = pm_chg_get_regulation_loop(the_chip);
+	return snprintf(buf, MAX_REG_LOOP_CHAR, "%d", temp);
+}
+module_param_call(reg_loop, NULL, get_reg_loop_param,
+					&reg_loop, 0644);
+
+static int max_chg_ma;
+#define MAX_MA_CHAR	10
+static int get_max_chg_ma_param(char *buf, struct kernel_param *kp)
+{
+	if (!the_chip) {
+		pr_err("called before init\n");
+		return -EINVAL;
+	}
+	return snprintf(buf, MAX_MA_CHAR, "%d", the_chip->max_bat_chg_current);
+}
+module_param_call(max_chg_ma, NULL, get_max_chg_ma_param,
+					&max_chg_ma, 0644);
+static int ibatmax_ma;
+static int set_ibat_max(const char *val, struct kernel_param *kp)
+{
+	int rc;
+
+	if (!the_chip) {
+		pr_err("called before init\n");
+		return -EINVAL;
+	}
+
+	rc = param_set_int(val, kp);
+	if (rc) {
+		pr_err("error setting value %d\n", rc);
+		return rc;
+	}
+
+	if (abs(ibatmax_ma - the_chip->max_bat_chg_current)
+				<= the_chip->ibatmax_max_adj_ma) {
+		rc = pm_chg_ibatmax_set(the_chip, ibatmax_ma);
+		if (rc) {
+			pr_err("Failed to set ibatmax rc = %d\n", rc);
+			return rc;
+		}
+	}
+
+	return 0;
+}
+static int get_ibat_max(char *buf, struct kernel_param *kp)
+{
+	int ibat_ma;
+	int rc;
+
+	if (!the_chip) {
+		pr_err("called before init\n");
+		return -EINVAL;
+	}
+
+	rc = pm_chg_ibatmax_get(the_chip, &ibat_ma);
+	if (rc) {
+		pr_err("ibatmax_get error = %d\n", rc);
+		return rc;
+	}
+
+	return snprintf(buf, MAX_MA_CHAR, "%d", ibat_ma);
+}
+module_param_call(ibatmax_ma, set_ibat_max, get_ibat_max,
+					&ibatmax_ma, 0644);
 enum {
 	BAT_WARM_ZONE,
 	BAT_COOL_ZONE,
@@ -4182,19 +4413,8 @@
 
 static int pm8921_charger_resume(struct device *dev)
 {
-	int rc;
 	struct pm8921_chg_chip *chip = dev_get_drvdata(dev);
 
-	if (!(chip->cool_temp_dc == INT_MIN && chip->warm_temp_dc == INT_MIN)
-		&& !(chip->keep_btm_on_suspend)) {
-		rc = pm8xxx_adc_btm_configure(&btm_config);
-		if (rc)
-			pr_err("couldn't reconfigure btm rc=%d\n", rc);
-
-		rc = pm8xxx_adc_btm_start();
-		if (rc)
-			pr_err("couldn't restart btm rc=%d\n", rc);
-	}
 	if (pm8921_chg_is_enabled(chip, LOOP_CHANGE_IRQ)) {
 		disable_irq_wake(chip->pmic_chg_irq[LOOP_CHANGE_IRQ]);
 		pm8921_chg_disable_irq(chip, LOOP_CHANGE_IRQ);
@@ -4207,18 +4427,14 @@
 	int rc;
 	struct pm8921_chg_chip *chip = dev_get_drvdata(dev);
 
-	if (!(chip->cool_temp_dc == INT_MIN && chip->warm_temp_dc == INT_MIN)
-		&& !(chip->keep_btm_on_suspend)) {
-		rc = pm8xxx_adc_btm_end();
-		if (rc)
-			pr_err("Failed to disable BTM on suspend rc=%d\n", rc);
-	}
-
 	if (is_usb_chg_plugged_in(chip)) {
 		pm8921_chg_enable_irq(chip, LOOP_CHANGE_IRQ);
 		enable_irq_wake(chip->pmic_chg_irq[LOOP_CHANGE_IRQ]);
 	}
 
+	rc = pm8xxx_batt_alarm_enable(PM8XXX_BATT_ALARM_LOWER_COMPARATOR);
+	if (rc < 0)
+		pr_err("Failed to enable lower comparator\n");
 	return 0;
 }
 static int __devinit pm8921_charger_probe(struct platform_device *pdev)
@@ -4241,13 +4457,13 @@
 	}
 
 	chip->dev = &pdev->dev;
-	chip->safety_time = pdata->safety_time;
 	chip->ttrkl_time = pdata->ttrkl_time;
 	chip->update_time = pdata->update_time;
 	chip->max_voltage_mv = pdata->max_voltage;
 	chip->alarm_low_mv = pdata->alarm_low_mv;
 	chip->alarm_high_mv = pdata->alarm_high_mv;
 	chip->min_voltage_mv = pdata->min_voltage;
+	chip->safe_current_ma = pdata->safe_current_ma;
 	chip->uvd_voltage_mv = pdata->uvd_thresh_voltage;
 	chip->resume_voltage_delta = pdata->resume_voltage_delta;
 	chip->resume_charge_percent = pdata->resume_charge_percent;
@@ -4268,13 +4484,13 @@
 		chip->warm_temp_dc = INT_MIN;
 
 	chip->temp_check_period = pdata->temp_check_period;
-	chip->dc_unplug_check = pdata->dc_unplug_check;
 	chip->max_bat_chg_current = pdata->max_bat_chg_current;
+	/* Assign to corresponding module parameter */
+	usb_max_current = pdata->usb_max_current;
 	chip->cool_bat_chg_current = pdata->cool_bat_chg_current;
 	chip->warm_bat_chg_current = pdata->warm_bat_chg_current;
 	chip->cool_bat_voltage = pdata->cool_bat_voltage;
 	chip->warm_bat_voltage = pdata->warm_bat_voltage;
-	chip->keep_btm_on_suspend = pdata->keep_btm_on_suspend;
 	chip->trkl_voltage = pdata->trkl_voltage;
 	chip->weak_voltage = pdata->weak_voltage;
 	chip->trkl_current = pdata->trkl_current;
@@ -4288,6 +4504,13 @@
 	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;
+
+	chip->ibatmax_max_adj_ma = find_ibat_max_adj_ma(
+					chip->max_bat_chg_current);
 
 	rc = pm8921_chg_hw_init(chip);
 	if (rc) {
@@ -4345,6 +4568,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);
@@ -4353,20 +4581,8 @@
 
 	enable_irq_wake(chip->pmic_chg_irq[USBIN_VALID_IRQ]);
 	enable_irq_wake(chip->pmic_chg_irq[DCIN_VALID_IRQ]);
-	enable_irq_wake(chip->pmic_chg_irq[BAT_TEMP_OK_IRQ]);
 	enable_irq_wake(chip->pmic_chg_irq[VBATDET_LOW_IRQ]);
 	enable_irq_wake(chip->pmic_chg_irq[FASTCHG_IRQ]);
-	/*
-	 * if both the cool_temp_dc and warm_temp_dc are invalid device doesnt
-	 * care for jeita compliance
-	 */
-	if (!(chip->cool_temp_dc == INT_MIN && chip->warm_temp_dc == INT_MIN)) {
-		rc = configure_btm(chip);
-		if (rc) {
-			pr_err("couldn't register with btm rc=%d\n", rc);
-			goto free_irq;
-		}
-	}
 
 	rc = pm8921_charger_configure_batt_alarm(chip);
 	if (rc) {
@@ -4381,19 +4597,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 1b9426a..a586f3d8 100644
--- a/drivers/power/pm8xxx-ccadc.c
+++ b/drivers/power/pm8xxx-ccadc.c
@@ -72,7 +72,7 @@
 	unsigned int		revision;
 	unsigned int		calib_delay_ms;
 	int			eoc_irq;
-	int			r_sense;
+	int			r_sense_uohm;
 	struct delayed_work	calib_ccadc_work;
 };
 
@@ -562,7 +562,8 @@
 		return rc;
 	}
 
-	*bat_current_ua = voltage_uv * 1000/the_chip->r_sense;
+	*bat_current_ua = div_s64((s64)voltage_uv * 1000000LL,
+						the_chip->r_sense_uohm);
 	/*
 	 * ccadc reads +ve current when the battery is charging
 	 * We need to return -ve if the battery is charging
@@ -675,7 +676,7 @@
 	chip->dev = &pdev->dev;
 	chip->revision = pm8xxx_get_revision(chip->dev->parent);
 	chip->eoc_irq = res->start;
-	chip->r_sense = pdata->r_sense;
+	chip->r_sense_uohm = pdata->r_sense_uohm;
 	chip->calib_delay_ms = pdata->calib_delay_ms;
 
 	calib_ccadc_read_offset_and_gain(chip,
diff --git a/drivers/power/qpnp-bms.c b/drivers/power/qpnp-bms.c
index 7b4d97e..6623d81 100644
--- a/drivers/power/qpnp-bms.c
+++ b/drivers/power/qpnp-bms.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, The Linux Foundation. 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
@@ -10,7 +10,7 @@
  * GNU General Public License for more details.
  */
 
-#define pr_fmt(fmt)	"%s: " fmt, __func__
+#define pr_fmt(fmt)	"BMS: %s: " fmt, __func__
 
 #include <linux/module.h>
 #include <linux/types.h>
@@ -21,26 +21,17 @@
 #include <linux/of_device.h>
 #include <linux/power_supply.h>
 #include <linux/spmi.h>
-
-/* Interrupt offsets */
-#define INT_RT_STS(base)		(base + 0x10)
-#define INT_SET_TYPE(base)		(base + 0x11)
-#define INT_POLARITY_HIGH(base)		(base + 0x12)
-#define INT_POLARITY_LOW(base)		(base + 0x13)
-#define INT_LATCHED_CLR(base)		(base + 0x14)
-#define INT_EN_SET(base)		(base + 0x15)
-#define INT_EN_CLR(base)		(base + 0x16)
-#define INT_LATCHED_STS(base)		(base + 0x18)
-#define INT_PENDING_STS(base)		(base + 0x19)
-#define INT_MID_SEL(base)		(base + 0x1A)
-#define INT_PRIORITY(base)		(base + 0x1B)
+#include <linux/rtc.h>
+#include <linux/delay.h>
+#include <linux/qpnp/qpnp-adc.h>
+#include <linux/mfd/pm8xxx/batterydata-lib.h>
 
 /* BMS Register Offsets */
 #define BMS1_REVISION1			0x0
 #define BMS1_REVISION2			0x1
 #define BMS1_STATUS1			0x8
 #define BMS1_MODE_CTL			0X40
-/* Columb counter clear registers */
+/* Coulomb counter clear registers */
 #define BMS1_CC_DATA_CTL		0x42
 #define BMS1_CC_CLEAR_CTRL		0x43
 /* OCV limit registers */
@@ -49,6 +40,8 @@
 #define BMS1_OCV_USE_HIGH_LIMIT_THR0	0x4A
 #define BMS1_OCV_USE_HIGH_LIMIT_THR1	0x4B
 #define BMS1_OCV_USE_LIMIT_CTL		0x4C
+/* Delay control */
+#define BMS1_S1_DELAY_CTL		0x5A
 /* CC interrupt threshold */
 #define BMS1_CC_THR0			0x7A
 #define BMS1_CC_THR1			0x7B
@@ -60,7 +53,7 @@
 #define BMS1_OCV_FOR_R_DATA1		0x81
 #define BMS1_VSENSE_FOR_R_DATA0		0x82
 #define BMS1_VSENSE_FOR_R_DATA1		0x83
-/* Columb counter data */
+/* Coulomb counter data */
 #define BMS1_CC_DATA0			0x8A
 #define BMS1_CC_DATA1			0x8B
 #define BMS1_CC_DATA2			0x8C
@@ -71,19 +64,45 @@
 #define BMS1_OCV_FOR_SOC_DATA1		0x91
 #define BMS1_VSENSE_PON_DATA0		0x94
 #define BMS1_VSENSE_PON_DATA1		0x95
+#define BMS1_VSENSE_AVG_DATA0		0x98
+#define BMS1_VSENSE_AVG_DATA1		0x99
 #define BMS1_VBAT_AVG_DATA0		0x9E
 #define BMS1_VBAT_AVG_DATA1		0x9F
 /* Extra bms registers */
 #define BMS1_BMS_DATA_REG_0		0xB0
-#define BMS1_BMS_DATA_REG_1		0xB1
-#define BMS1_BMS_DATA_REG_2		0xB2
+#define IAVG_STORAGE_REG		0xB1
+#define SOC_STORAGE_REG			0xB2
 #define BMS1_BMS_DATA_REG_3		0xB3
 
+/* Configuration for saving of shutdown soc/iavg */
+#define IGNORE_SOC_TEMP_DECIDEG		50
+#define IAVG_STEP_SIZE_MA		50
+#define IAVG_START			600
+#define SOC_ZERO			0xFF
+
+#define IAVG_SAMPLES 16
+
 #define QPNP_BMS_DEV_NAME "qcom,qpnp-bms"
 
+struct soc_params {
+	int		fcc_uah;
+	int		cc_uah;
+	int		rbatt;
+	int		iavg_ua;
+	int		uuc_uah;
+	int		ocv_charge_uah;
+};
+
+struct raw_soc_params {
+	uint16_t	last_good_ocv_raw;
+	int64_t		cc;
+	int		last_good_ocv_uv;
+};
+
 struct qpnp_bms_chip {
 	struct device			*dev;
 	struct power_supply		bms_psy;
+	struct power_supply		*batt_psy;
 	struct spmi_device		*spmi;
 	u16				base;
 
@@ -93,13 +112,72 @@
 	bool				online;
 	/* platform data */
 	unsigned int			r_sense_mohm;
-	unsigned int			v_cutoff;
-	unsigned int			max_voltage;
+	unsigned int			v_cutoff_uv;
+	unsigned int			max_voltage_uv;
 	unsigned int			r_conn_mohm;
 	int				shutdown_soc_valid_limit;
 	int				adjust_soc_low_threshold;
 	int				adjust_soc_high_threshold;
-	int				chg_term;
+	int				chg_term_ua;
+	enum battery_type		batt_type;
+	unsigned int			fcc;
+	struct single_row_lut		*fcc_temp_lut;
+	struct single_row_lut		*fcc_sf_lut;
+	struct pc_temp_ocv_lut		*pc_temp_ocv_lut;
+	struct sf_lut			*pc_sf_lut;
+	struct sf_lut			*rbatt_sf_lut;
+	int				default_rbatt_mohm;
+
+	struct delayed_work		calculate_soc_delayed_work;
+
+	struct mutex			bms_output_lock;
+	struct mutex			last_ocv_uv_mutex;
+	struct mutex			soc_invalidation_mutex;
+
+	unsigned int			start_percent;
+	unsigned int			end_percent;
+	bool				ignore_shutdown_soc;
+	int				shutdown_soc_invalid;
+	int				shutdown_soc;
+	int				shutdown_iavg_ma;
+
+	int				low_soc_calc_threshold;
+	int				low_soc_calculate_soc_ms;
+	int				calculate_soc_ms;
+
+	uint16_t			ocv_reading_at_100;
+	int64_t				cc_reading_at_100;
+	uint16_t			prev_last_good_ocv_raw;
+	int				last_ocv_uv;
+	int				last_cc_uah;
+	unsigned long			tm_sec;
+	bool				first_time_calc_soc;
+	bool				first_time_calc_uuc;
+	int				pon_ocv_uv;
+
+	int				iavg_samples_ma[IAVG_SAMPLES];
+	int				iavg_index;
+	int				iavg_num_samples;
+	struct timespec			t_soc_queried;
+	int				last_soc;
+	int				last_soc_est;
+
+	int				charge_time_us;
+	int				catch_up_time_us;
+	struct single_row_lut		*adjusted_fcc_temp_lut;
+
+	unsigned int			vadc_v0625;
+	unsigned int			vadc_v1250;
+
+	int				prev_iavg_ua;
+	int				prev_uuc_iavg_ma;
+	int				prev_pc_unusable;
+	int				ibat_at_cv_ua;
+	int				soc_at_cv;
+	int				prev_chg_soc;
+	int				calculated_soc;
+	int				prev_voltage_based_soc;
+	bool				use_voltage_soc;
 };
 
 static struct of_device_id qpnp_bms_match_table[] = {
@@ -119,6 +197,7 @@
 	POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
 };
 
+
 static int qpnp_read_wrapper(struct qpnp_bms_chip *chip, u8 *val,
 			u16 base, int count)
 {
@@ -126,31 +205,1449 @@
 	struct spmi_device *spmi = chip->spmi;
 
 	rc = spmi_ext_register_readl(spmi->ctrl, spmi->sid, base, val, count);
-	if (rc)
+	if (rc) {
 		pr_err("SPMI read failed rc=%d\n", rc);
+		return rc;
+	}
+	return 0;
+}
+
+static int qpnp_write_wrapper(struct qpnp_bms_chip *chip, u8 *val,
+			u16 base, int count)
+{
+	int rc;
+	struct spmi_device *spmi = chip->spmi;
+
+	rc = spmi_ext_register_writel(spmi->ctrl, spmi->sid, base, val, count);
+	if (rc) {
+		pr_err("SPMI write failed rc=%d\n", rc);
+		return rc;
+	}
+	return 0;
+}
+
+static int qpnp_masked_write(struct qpnp_bms_chip *chip, u16 addr,
+							u8 mask, u8 val)
+{
+	int rc;
+	u8 reg;
+
+	rc = qpnp_read_wrapper(chip, &reg, chip->base + addr, 1);
+	if (rc) {
+		pr_err("read failed addr = %03X, rc = %d\n",
+				chip->base + addr, rc);
+		return rc;
+	}
+	reg &= ~mask;
+	reg |= val & mask;
+	rc = qpnp_write_wrapper(chip, &reg, chip->base + addr, 1);
+	if (rc) {
+		pr_err("write failed addr = %03X, val = %02x, mask = %02x, reg = %02x, rc = %d\n",
+				chip->base + addr, val, mask, reg, rc);
+		return rc;
+	}
+	return 0;
+}
+
+#define HOLD_OREG_DATA		BIT(0)
+static int lock_output_data(struct qpnp_bms_chip *chip)
+{
+	int rc;
+
+	rc = qpnp_masked_write(chip, BMS1_CC_DATA_CTL,
+				HOLD_OREG_DATA, HOLD_OREG_DATA);
+	if (rc) {
+		pr_err("couldnt lock bms output rc = %d\n", rc);
+		return rc;
+	}
+	return 0;
+}
+
+static int unlock_output_data(struct qpnp_bms_chip *chip)
+{
+	int rc;
+
+	rc = qpnp_masked_write(chip, BMS1_CC_DATA_CTL, HOLD_OREG_DATA, 0);
+	if (rc) {
+		pr_err("fail to unlock BMS_CONTROL rc = %d\n", rc);
+		return rc;
+	}
+	return 0;
+}
+
+#define V_PER_BIT_MUL_FACTOR	97656
+#define V_PER_BIT_DIV_FACTOR	1000
+#define VADC_INTRINSIC_OFFSET	0x6000
+
+static int vadc_reading_to_uv(unsigned int reading)
+{
+	if (reading <= VADC_INTRINSIC_OFFSET)
+		return 0;
+
+	return (reading - VADC_INTRINSIC_OFFSET)
+			* V_PER_BIT_MUL_FACTOR / V_PER_BIT_DIV_FACTOR;
+}
+
+#define VADC_CALIB_UV		625000
+#define VBATT_MUL_FACTOR	3
+
+static int adjust_vbatt_reading(struct qpnp_bms_chip *chip,
+						unsigned int reading_uv)
+{
+	s64 numerator, denominator;
+
+	if (reading_uv == 0)
+		return 0;
+
+	/* don't adjust if not calibrated */
+	if (chip->vadc_v0625 == 0 || chip->vadc_v1250 == 0) {
+		pr_debug("No cal yet return %d\n",
+				VBATT_MUL_FACTOR * reading_uv);
+		return VBATT_MUL_FACTOR * reading_uv;
+	}
+
+	numerator = ((s64)reading_uv - chip->vadc_v0625) * VADC_CALIB_UV;
+	denominator =  (s64)chip->vadc_v1250 - chip->vadc_v0625;
+	if (denominator == 0)
+		return reading_uv * VBATT_MUL_FACTOR;
+	return (VADC_CALIB_UV + div_s64(numerator, denominator))
+						* VBATT_MUL_FACTOR;
+}
+
+static inline int convert_vbatt_raw_to_uv(struct qpnp_bms_chip *chip,
+					uint16_t reading)
+{
+	int uv;
+
+	uv = vadc_reading_to_uv(reading);
+	pr_debug("%u raw converted into %d uv\n", reading, uv);
+	uv = adjust_vbatt_reading(chip, uv);
+	pr_debug("adjusted into %d uv\n", uv);
+	return uv;
+}
+
+#define CC_READING_RESOLUTION_N	542535
+#define CC_READING_RESOLUTION_D	100000
+static int cc_reading_to_uv(int16_t reading)
+{
+	return div_s64(reading * CC_READING_RESOLUTION_N,
+					CC_READING_RESOLUTION_D);
+}
+
+#define QPNP_ADC_GAIN_NV				17857LL
+static s64 cc_adjust_for_gain(s64 uv, uint16_t gain)
+{
+	s64 result_uv;
+
+	pr_debug("adjusting_uv = %lld\n", uv);
+	if (gain == 0) {
+		pr_debug("gain is %d, not adjusting\n", gain);
+		return uv;
+	}
+	pr_debug("adjusting by factor: %lld/%hu = %lld%%\n",
+			QPNP_ADC_GAIN_NV, gain,
+			div_s64(QPNP_ADC_GAIN_NV * 100LL, (s64)gain));
+
+	result_uv = div_s64(uv * QPNP_ADC_GAIN_NV, (s64)gain);
+	pr_debug("result_uv = %lld\n", result_uv);
+	return result_uv;
+}
+
+static int convert_vsense_to_uv(struct qpnp_bms_chip *chip,
+					int16_t reading)
+{
+	struct qpnp_iadc_calib calibration;
+
+	qpnp_iadc_get_gain_and_offset(&calibration);
+	return cc_adjust_for_gain(cc_reading_to_uv(reading),
+			calibration.gain_raw);
+}
+
+static int read_vsense_avg(struct qpnp_bms_chip *chip, int *result_uv)
+{
+	int rc;
+	int16_t reading;
+
+	rc = qpnp_read_wrapper(chip, (u8 *)&reading,
+			chip->base + BMS1_VSENSE_AVG_DATA0, 2);
+
+	if (rc) {
+		pr_err("fail to read VSENSE_AVG rc = %d\n", rc);
+		return rc;
+	}
+
+	*result_uv = convert_vsense_to_uv(chip, reading);
+	return 0;
+}
+
+static int get_battery_current(struct qpnp_bms_chip *chip, int *result_ua)
+{
+	int vsense_uv = 0;
+
+	if (chip->r_sense_mohm == 0) {
+		pr_err("r_sense is zero\n");
+		return -EINVAL;
+	}
+
+	mutex_lock(&chip->bms_output_lock);
+	lock_output_data(chip);
+	read_vsense_avg(chip, &vsense_uv);
+	unlock_output_data(chip);
+	mutex_unlock(&chip->bms_output_lock);
+
+	pr_debug("vsense_uv=%duV\n", vsense_uv);
+	/* cast for signed division */
+	*result_ua = vsense_uv * 1000 / (int)chip->r_sense_mohm;
+	pr_debug("ibat=%duA\n", *result_ua);
+	return 0;
+}
+
+static int get_battery_voltage(int *result_uv)
+{
+	int rc;
+	struct qpnp_vadc_result adc_result;
+
+	rc = qpnp_vadc_read(VBAT_SNS, &adc_result);
+	if (rc) {
+		pr_err("error reading adc channel = %d, rc = %d\n",
+					VBAT_SNS, rc);
+		return rc;
+	}
+	pr_debug("mvolts phy = %lld meas = 0x%llx\n", adc_result.physical,
+						adc_result.measurement);
+	*result_uv = (int)adc_result.physical;
+	return 0;
+}
+
+#define CC_36_BIT_MASK 0xFFFFFFFFFLL
+
+static int read_cc_raw(struct qpnp_bms_chip *chip, int64_t *reading)
+{
+	int64_t raw_reading;
+	int rc;
+
+	rc = qpnp_read_wrapper(chip, (u8 *)&raw_reading,
+			chip->base + BMS1_CC_DATA0, 5);
+	if (rc) {
+		pr_err("Error reading cc: rc = %d\n", rc);
+		return -ENXIO;
+	}
+
+	raw_reading = raw_reading & CC_36_BIT_MASK;
+	/* convert 36 bit signed value into 64 signed value */
+	*reading = (raw_reading >> 35) == 0LL ?
+		raw_reading : ((-1LL ^ CC_36_BIT_MASK) | raw_reading);
+	pr_debug("before conversion: %llx, after conversion: %llx\n",
+			raw_reading, *reading);
 
 	return 0;
 }
 
+static int calib_vadc(struct qpnp_bms_chip *chip)
+{
+	int rc;
+	struct qpnp_vadc_result result;
+
+	rc = qpnp_vadc_read(REF_625MV, &result);
+	if (rc) {
+		pr_debug("vadc read failed with rc = %d\n", rc);
+		return rc;
+	}
+	chip->vadc_v0625 = result.physical;
+
+	rc = qpnp_vadc_read(REF_125V, &result);
+	if (rc) {
+		pr_debug("vadc read failed with rc = %d\n", rc);
+		return rc;
+	}
+	chip->vadc_v1250 = result.physical;
+	pr_debug("vadc calib: 0625 = %d, 1250 = %d\n",
+			chip->vadc_v0625, chip->vadc_v1250);
+	return 0;
+}
+
+static void convert_and_store_ocv(struct qpnp_bms_chip *chip,
+				struct raw_soc_params *raw)
+{
+	int rc;
+
+	pr_debug("prev_last_good_ocv_raw = %d, last_good_ocv_raw = %d\n",
+			chip->prev_last_good_ocv_raw,
+			raw->last_good_ocv_raw);
+	rc = calib_vadc(chip);
+	if (rc)
+		pr_err("Vadc reference voltage read failed, rc = %d\n", rc);
+	chip->prev_last_good_ocv_raw = raw->last_good_ocv_raw;
+	raw->last_good_ocv_uv = convert_vbatt_raw_to_uv(chip,
+					raw->last_good_ocv_raw);
+	chip->last_ocv_uv = raw->last_good_ocv_uv;
+	pr_debug("last_good_ocv_uv = %d\n", raw->last_good_ocv_uv);
+}
+
+static int read_soc_params_raw(struct qpnp_bms_chip *chip,
+				struct raw_soc_params *raw)
+{
+	int rc;
+
+	mutex_lock(&chip->bms_output_lock);
+	lock_output_data(chip);
+
+	rc = qpnp_read_wrapper(chip, (u8 *)&raw->last_good_ocv_raw,
+			chip->base + BMS1_OCV_FOR_SOC_DATA0, 2);
+	if (rc) {
+		pr_err("Error reading ocv: rc = %d\n", rc);
+		return -ENXIO;
+	}
+
+	rc = read_cc_raw(chip, &raw->cc);
+	if (rc) {
+		pr_err("Failed to read raw cc data, rc = %d\n", rc);
+		return rc;
+	}
+
+	unlock_output_data(chip);
+	mutex_unlock(&chip->bms_output_lock);
+
+	if (chip->prev_last_good_ocv_raw == 0) {
+		convert_and_store_ocv(chip, raw);
+		pr_debug("PON_OCV_UV = %d\n", chip->last_ocv_uv);
+	} else if (chip->prev_last_good_ocv_raw != raw->last_good_ocv_raw) {
+		convert_and_store_ocv(chip, raw);
+		/* forget the old cc value upon ocv */
+		chip->last_cc_uah = 0;
+	} else {
+		raw->last_good_ocv_uv = chip->last_ocv_uv;
+	}
+
+	/* fake a high OCV if done charging */
+	if (chip->ocv_reading_at_100 != raw->last_good_ocv_raw) {
+		chip->ocv_reading_at_100 = 0;
+		chip->cc_reading_at_100 = 0;
+	} else {
+		/*
+		 * force 100% ocv by selecting the highest voltage the
+		 * battery could ever reach
+		 */
+		raw->last_good_ocv_uv = chip->max_voltage_uv;
+		chip->last_ocv_uv = chip->max_voltage_uv;
+	}
+	pr_debug("last_good_ocv_raw= 0x%x, last_good_ocv_uv= %duV\n",
+			raw->last_good_ocv_raw, raw->last_good_ocv_uv);
+	pr_debug("cc_raw= 0x%llx\n", raw->cc);
+	return 0;
+}
+
+static int calculate_pc(struct qpnp_bms_chip *chip, int ocv_uv,
+							int batt_temp)
+{
+	int pc;
+
+	pc = interpolate_pc(chip->pc_temp_ocv_lut,
+			batt_temp / 10, ocv_uv / 1000);
+	pr_debug("pc = %u %% for ocv = %d uv batt_temp = %d\n",
+					pc, ocv_uv, batt_temp);
+	/* Multiply the initial FCC value by the scale factor. */
+	return pc;
+}
+
+static int calculate_fcc(struct qpnp_bms_chip *chip, int batt_temp)
+{
+	int fcc_uah;
+
+	if (chip->adjusted_fcc_temp_lut == NULL) {
+		/* interpolate_fcc returns a mv value. */
+		fcc_uah = interpolate_fcc(chip->fcc_temp_lut,
+						batt_temp) * 1000;
+		pr_debug("fcc = %d uAh\n", fcc_uah);
+		return fcc_uah;
+	} else {
+		return 1000 * interpolate_fcc(chip->adjusted_fcc_temp_lut,
+				batt_temp);
+	}
+}
+
+/* calculate remaining charge at the time of ocv */
+static int calculate_ocv_charge(struct qpnp_bms_chip *chip,
+						struct raw_soc_params *raw,
+						int fcc_uah,
+						int batt_temp)
+{
+	int  ocv_uv, pc;
+
+	ocv_uv = raw->last_good_ocv_uv;
+	pc = calculate_pc(chip, ocv_uv, batt_temp);
+	pr_debug("ocv_uv = %d pc = %d\n", ocv_uv, pc);
+	return (fcc_uah * pc) / 100;
+}
+
+#define CC_RESOLUTION_N		542535
+#define CC_RESOLUTION_D		100000
+
+static s64 cc_to_uv(s64 cc)
+{
+	return div_s64(cc * CC_RESOLUTION_N, CC_RESOLUTION_D);
+}
+
+#define CC_READING_TICKS	56
+#define SLEEP_CLK_HZ		32764
+#define SECONDS_PER_HOUR	3600
+
+static s64 cc_uv_to_nvh(s64 cc_uv)
+{
+	return div_s64(cc_uv * CC_READING_TICKS * 1000,
+			SLEEP_CLK_HZ * SECONDS_PER_HOUR);
+}
+
+/**
+ * calculate_cc-
+ * @chip:		the bms chip pointer
+ * @cc:			the cc reading from bms h/w
+ * @val:		return value
+ * @coulomb_counter:	adjusted coulomb counter for 100%
+ *
+ * RETURNS: in val pointer coulomb counter based charger in uAh
+ *          (micro Amp hour)
+ */
+static int calculate_cc(struct qpnp_bms_chip *chip, int64_t cc)
+{
+	int64_t cc_voltage_uv, cc_nvh, cc_uah;
+	struct qpnp_iadc_calib calibration;
+
+	qpnp_iadc_get_gain_and_offset(&calibration);
+	cc_voltage_uv = cc;
+	cc_voltage_uv -= chip->cc_reading_at_100;
+	pr_debug("cc = %lld. after subtracting 0x%llx cc = %lld\n",
+					cc, chip->cc_reading_at_100,
+					cc_voltage_uv);
+	cc_voltage_uv = cc_to_uv(cc_voltage_uv);
+	cc_voltage_uv = cc_adjust_for_gain(cc_voltage_uv, calibration.gain_raw);
+	pr_debug("cc_voltage_uv = %lld uv\n", cc_voltage_uv);
+	cc_nvh = cc_uv_to_nvh(cc_voltage_uv);
+	pr_debug("cc_nvh = %lld nano_volt_hour\n", cc_nvh);
+	cc_uah = div_s64(cc_nvh, chip->r_sense_mohm);
+	/* cc_raw had 4 bits of extra precision.
+	   By now it should be within 32 bit range */
+	return (int)cc_uah;
+}
+
+static int get_rbatt(struct qpnp_bms_chip *chip,
+					int soc_rbatt_mohm, int batt_temp)
+{
+	int rbatt_mohm, scalefactor;
+
+	rbatt_mohm = chip->default_rbatt_mohm;
+	pr_debug("rbatt before scaling = %d\n", rbatt_mohm);
+	if (chip->rbatt_sf_lut == NULL)  {
+		pr_debug("RBATT = %d\n", rbatt_mohm);
+		return rbatt_mohm;
+	}
+	/* Convert the batt_temp to DegC from deciDegC */
+	batt_temp = batt_temp / 10;
+	scalefactor = interpolate_scalingfactor(chip->rbatt_sf_lut,
+						batt_temp, soc_rbatt_mohm);
+	pr_debug("rbatt sf = %d for batt_temp = %d, soc_rbatt = %d\n",
+				scalefactor, batt_temp, soc_rbatt_mohm);
+	rbatt_mohm = (rbatt_mohm * scalefactor) / 100;
+
+	rbatt_mohm += chip->r_conn_mohm;
+	pr_debug("adding r_conn_mohm = %d rbatt = %d\n",
+				chip->r_conn_mohm, rbatt_mohm);
+
+	pr_debug("RBATT = %d\n", rbatt_mohm);
+	return rbatt_mohm;
+}
+
+static void calculate_iavg(struct qpnp_bms_chip *chip, int cc_uah,
+				int *iavg_ua)
+{
+	int delta_cc_uah, delta_time_s, rc;
+	struct rtc_time tm;
+	struct rtc_device *rtc;
+	unsigned long now_tm_sec = 0;
+
+	rc = 0;
+	/* if anything fails report the previous iavg_ua */
+	*iavg_ua = chip->prev_iavg_ua;
+
+	rtc = rtc_class_open(CONFIG_RTC_HCTOSYS_DEVICE);
+	if (rtc == NULL) {
+		pr_err("%s: unable to open rtc device (%s)\n",
+			__FILE__, CONFIG_RTC_HCTOSYS_DEVICE);
+		goto out;
+	}
+
+	rc = rtc_read_time(rtc, &tm);
+	if (rc) {
+		pr_err("Error reading rtc device (%s) : %d\n",
+			CONFIG_RTC_HCTOSYS_DEVICE, rc);
+		goto out;
+	}
+
+	rc = rtc_valid_tm(&tm);
+	if (rc) {
+		pr_err("Invalid RTC time (%s): %d\n",
+			CONFIG_RTC_HCTOSYS_DEVICE, rc);
+		goto out;
+	}
+	rtc_tm_to_time(&tm, &now_tm_sec);
+
+	if (chip->tm_sec == 0) {
+		get_battery_current(chip, iavg_ua);
+		goto out;
+	}
+
+	delta_time_s = (now_tm_sec - chip->tm_sec);
+
+	/* use the previous iavg if called within 15 seconds */
+	if (delta_time_s < 15) {
+		*iavg_ua = chip->prev_iavg_ua;
+		goto out;
+	}
+
+	delta_cc_uah = cc_uah - chip->last_cc_uah;
+
+	*iavg_ua = div_s64((s64)delta_cc_uah * 3600, delta_time_s);
+
+	pr_debug("tm_sec = %ld, now_tm_sec = %ld delta_s = %d delta_cc = %d iavg_ua = %d\n",
+				chip->tm_sec, now_tm_sec,
+				delta_time_s, delta_cc_uah, (int)*iavg_ua);
+
+out:
+	/* remember the iavg */
+	chip->prev_iavg_ua = *iavg_ua;
+
+	/* remember cc_uah */
+	chip->last_cc_uah = cc_uah;
+
+	/* remember this time */
+	chip->tm_sec = now_tm_sec;
+}
+
+static int calculate_termination_uuc(struct qpnp_bms_chip *chip,
+					struct soc_params *params,
+					int batt_temp, int uuc_iavg_ma,
+					int *ret_pc_unusable)
+{
+	int unusable_uv, pc_unusable, uuc_uah;
+	int i = 0;
+	int ocv_mv;
+	int batt_temp_degc = batt_temp / 10;
+	int rbatt_mohm;
+	int delta_uv;
+	int prev_delta_uv = 0;
+	int prev_rbatt_mohm = 0;
+	int uuc_rbatt_mohm;
+
+	for (i = 0; i <= 100; i++) {
+		ocv_mv = interpolate_ocv(chip->pc_temp_ocv_lut,
+				batt_temp_degc, i);
+		rbatt_mohm = get_rbatt(chip, i, batt_temp);
+		unusable_uv = (rbatt_mohm * uuc_iavg_ma)
+							+ (chip->v_cutoff_uv);
+		delta_uv = ocv_mv * 1000 - unusable_uv;
+
+		pr_debug("soc = %d ocv = %d rbat = %d u_uv = %d delta_v = %d\n",
+				i, ocv_mv, rbatt_mohm, unusable_uv, delta_uv);
+
+		if (delta_uv > 0)
+			break;
+
+		prev_delta_uv = delta_uv;
+		prev_rbatt_mohm = rbatt_mohm;
+	}
+
+	uuc_rbatt_mohm = linear_interpolate(rbatt_mohm, delta_uv,
+					prev_rbatt_mohm, prev_delta_uv,
+					0);
+
+	unusable_uv = (uuc_rbatt_mohm * uuc_iavg_ma) + (chip->v_cutoff_uv);
+
+	pc_unusable = calculate_pc(chip, unusable_uv, batt_temp);
+	uuc_uah = (params->fcc_uah * pc_unusable) / 100;
+	pr_debug("For uuc_iavg_ma = %d, unusable_rbatt = %d unusable_uv = %d unusable_pc = %d uuc = %d\n",
+					uuc_iavg_ma,
+					uuc_rbatt_mohm, unusable_uv,
+					pc_unusable, uuc_uah);
+	*ret_pc_unusable = pc_unusable;
+	return uuc_uah;
+}
+
+static int adjust_uuc(struct qpnp_bms_chip *chip,
+			struct soc_params *params,
+			int new_pc_unusable,
+			int new_uuc_uah,
+			int batt_temp)
+{
+	int new_unusable_mv, new_iavg_ma;
+	int batt_temp_degc = batt_temp / 10;
+
+	if (chip->prev_pc_unusable == -EINVAL
+		|| abs(chip->prev_pc_unusable - new_pc_unusable) <= 1) {
+		chip->prev_pc_unusable = new_pc_unusable;
+		return new_uuc_uah;
+	}
+
+	/* the uuc is trying to change more than 1% restrict it */
+	if (new_pc_unusable > chip->prev_pc_unusable)
+		chip->prev_pc_unusable++;
+	else
+		chip->prev_pc_unusable--;
+
+	new_uuc_uah = (params->fcc_uah * chip->prev_pc_unusable) / 100;
+
+	/* also find update the iavg_ma accordingly */
+	new_unusable_mv = interpolate_ocv(chip->pc_temp_ocv_lut,
+			batt_temp_degc, chip->prev_pc_unusable);
+	if (new_unusable_mv < chip->v_cutoff_uv/1000)
+		new_unusable_mv = chip->v_cutoff_uv/1000;
+
+	new_iavg_ma = (new_unusable_mv * 1000 - chip->v_cutoff_uv)
+						/ params->rbatt;
+	if (new_iavg_ma == 0)
+		new_iavg_ma = 1;
+	chip->prev_uuc_iavg_ma = new_iavg_ma;
+	pr_debug("Restricting UUC to %d (%d%%) unusable_mv = %d iavg_ma = %d\n",
+					new_uuc_uah, chip->prev_pc_unusable,
+					new_unusable_mv, new_iavg_ma);
+
+	return new_uuc_uah;
+}
+
+#define CHARGING_IAVG_MA 250
+#define MIN_SECONDS_FOR_VALID_SAMPLE	20
+static int calculate_unusable_charge_uah(struct qpnp_bms_chip *chip,
+					struct soc_params *params,
+					int batt_temp)
+{
+	int uuc_uah_iavg;
+	int i;
+	int uuc_iavg_ma = params->iavg_ua / 1000;
+	int pc_unusable;
+
+	/*
+	 * if called first time, fill all the samples with
+	 * the shutdown_iavg_ma
+	 */
+	if (chip->first_time_calc_uuc && chip->shutdown_iavg_ma != 0) {
+		pr_debug("Using shutdown_iavg_ma = %d in all samples\n",
+				chip->shutdown_iavg_ma);
+		for (i = 0; i < IAVG_SAMPLES; i++)
+			chip->iavg_samples_ma[i] = chip->shutdown_iavg_ma;
+
+		chip->iavg_index = 0;
+		chip->iavg_num_samples = IAVG_SAMPLES;
+	}
+
+	/*
+	 * if charging use a nominal avg current to keep
+	 * a reasonable UUC while charging
+	 */
+	if (uuc_iavg_ma < 0)
+		uuc_iavg_ma = CHARGING_IAVG_MA;
+	chip->iavg_samples_ma[chip->iavg_index] = uuc_iavg_ma;
+	chip->iavg_index = (chip->iavg_index + 1) % IAVG_SAMPLES;
+	chip->iavg_num_samples++;
+	if (chip->iavg_num_samples >= IAVG_SAMPLES)
+		chip->iavg_num_samples = IAVG_SAMPLES;
+
+	/* now that this sample is added calcualte the average */
+	uuc_iavg_ma = 0;
+	if (chip->iavg_num_samples != 0) {
+		for (i = 0; i < chip->iavg_num_samples; i++) {
+			pr_debug("iavg_samples_ma[%d] = %d\n", i,
+					chip->iavg_samples_ma[i]);
+			uuc_iavg_ma += chip->iavg_samples_ma[i];
+		}
+
+		uuc_iavg_ma = DIV_ROUND_CLOSEST(uuc_iavg_ma,
+						chip->iavg_num_samples);
+	}
+
+	uuc_uah_iavg = calculate_termination_uuc(chip, params, uuc_iavg_ma,
+						batt_temp, &pc_unusable);
+	pr_debug("uuc_iavg_ma = %d uuc with iavg = %d\n",
+						uuc_iavg_ma, uuc_uah_iavg);
+
+	chip->prev_uuc_iavg_ma = uuc_iavg_ma;
+	/* restrict the uuc such that it can increase only by one percent */
+	uuc_uah_iavg = adjust_uuc(chip, params, pc_unusable,
+					uuc_uah_iavg, batt_temp);
+
+	chip->first_time_calc_uuc = 0;
+	return uuc_uah_iavg;
+}
+
+static void find_ocv_for_soc(struct qpnp_bms_chip *chip,
+				struct soc_params *params,
+				int batt_temp,
+				int shutdown_soc,
+				int *ret_ocv_uv)
+{
+	s64 ocv_charge_uah;
+	int pc, new_pc;
+	int batt_temp_degc = batt_temp / 10;
+	int ocv_uv;
+
+	ocv_charge_uah = (s64)shutdown_soc
+				* (params->fcc_uah - params->uuc_uah);
+	ocv_charge_uah = div_s64(ocv_charge_uah, 100)
+				+ params->cc_uah + params->uuc_uah;
+	pc = DIV_ROUND_CLOSEST((int)ocv_charge_uah * 100, params->fcc_uah);
+	pc = clamp(pc, 0, 100);
+
+	ocv_uv = interpolate_ocv(chip->pc_temp_ocv_lut, batt_temp_degc, pc);
+
+	pr_debug("s_soc = %d, fcc = %d uuc = %d rc = %d, pc = %d, ocv mv = %d\n",
+					shutdown_soc, params->fcc_uah,
+					params->uuc_uah, (int)ocv_charge_uah,
+					pc, ocv_uv);
+	new_pc = interpolate_pc(chip->pc_temp_ocv_lut, batt_temp_degc, ocv_uv);
+	pr_debug("test revlookup pc = %d for ocv = %d\n", new_pc, ocv_uv);
+
+	while (abs(new_pc - pc) > 1) {
+		int delta_mv = 5;
+
+		if (new_pc > pc)
+			delta_mv = -1 * delta_mv;
+
+		ocv_uv = ocv_uv + delta_mv;
+		new_pc = interpolate_pc(chip->pc_temp_ocv_lut,
+				batt_temp_degc, ocv_uv);
+		pr_debug("test revlookup pc = %d for ocv = %d\n",
+				new_pc, ocv_uv);
+	}
+
+	*ret_ocv_uv = ocv_uv * 1000;
+	params->ocv_charge_uah = (int)ocv_charge_uah;
+}
+
+static void calculate_soc_params(struct qpnp_bms_chip *chip,
+						struct raw_soc_params *raw,
+						struct soc_params *params,
+						int batt_temp)
+{
+	int soc_rbatt;
+
+	params->fcc_uah = calculate_fcc(chip, batt_temp);
+	pr_debug("FCC = %uuAh batt_temp = %d\n", params->fcc_uah, batt_temp);
+
+	/* calculate remainging charge */
+	params->ocv_charge_uah = calculate_ocv_charge(
+						chip, raw,
+						params->fcc_uah,
+						batt_temp);
+	pr_debug("ocv_charge_uah = %uuAh\n", params->ocv_charge_uah);
+
+	/* calculate cc micro_volt_hour */
+	params->cc_uah = calculate_cc(chip, raw->cc);
+	pr_debug("cc_uah = %duAh raw->cc = %llx cc = %lld after subtracting %llx\n",
+				params->cc_uah, raw->cc,
+				(int64_t)raw->cc - chip->cc_reading_at_100,
+				chip->cc_reading_at_100);
+
+	soc_rbatt = ((params->ocv_charge_uah - params->cc_uah) * 100)
+							/ params->fcc_uah;
+	if (soc_rbatt < 0)
+		soc_rbatt = 0;
+	params->rbatt = get_rbatt(chip, soc_rbatt, batt_temp);
+
+	calculate_iavg(chip, params->cc_uah, &params->iavg_ua);
+
+	params->uuc_uah = calculate_unusable_charge_uah(chip, params,
+							batt_temp);
+	pr_debug("UUC = %uuAh\n", params->uuc_uah);
+}
+
+static bool is_shutdown_soc_within_limits(struct qpnp_bms_chip *chip, int soc)
+{
+	if (chip->shutdown_soc_invalid) {
+		pr_debug("NOT forcing shutdown soc = %d\n", chip->shutdown_soc);
+		return 0;
+	}
+
+	if (abs(chip->shutdown_soc - soc) > chip->shutdown_soc_valid_limit) {
+		pr_debug("rejecting shutdown soc = %d, soc = %d limit = %d\n",
+			chip->shutdown_soc, soc,
+			chip->shutdown_soc_valid_limit);
+		chip->shutdown_soc_invalid = 1;
+		return 0;
+	}
+
+	return 1;
+}
+
+#define BMS_OVERRIDE_MODE_EN_BIT	BIT(7)
+#define EN_VBAT_BIT			BIT(0)
+#define OVERRIDE_MODE_DELAY_MS		20
+static int override_mode_batt_v_and_i(
+		struct qpnp_bms_chip *chip, int *ibat_ua, int *vbat_uv)
+{
+	int16_t vsense_raw, vbat_raw;
+	int vsense_uv, rc;
+	u8 delay;
+
+	mutex_lock(&chip->bms_output_lock);
+
+	delay = 0x00;
+	rc = qpnp_write_wrapper(chip, &delay,
+			chip->base + BMS1_S1_DELAY_CTL, 1);
+	if (rc)
+		pr_err("unable to write into BMS1_S1_DELAY, rc: %d\n", rc);
+
+	rc = qpnp_masked_write(chip, BMS1_MODE_CTL,
+			BMS_OVERRIDE_MODE_EN_BIT | EN_VBAT_BIT,
+			BMS_OVERRIDE_MODE_EN_BIT | EN_VBAT_BIT);
+	if (rc)
+		pr_err("unable to write into BMS1_MODE_CTL, rc: %d\n", rc);
+
+	msleep(OVERRIDE_MODE_DELAY_MS);
+
+	lock_output_data(chip);
+	qpnp_read_wrapper(chip, (u8 *)&vsense_raw,
+			chip->base + BMS1_VSENSE_AVG_DATA0, 2);
+	qpnp_read_wrapper(chip, (u8 *)&vbat_raw,
+			chip->base + BMS1_VBAT_AVG_DATA0, 2);
+	unlock_output_data(chip);
+
+	rc = qpnp_masked_write(chip, BMS1_MODE_CTL,
+			BMS_OVERRIDE_MODE_EN_BIT | EN_VBAT_BIT, 0);
+
+	delay = 0x0B;
+	rc = qpnp_write_wrapper(chip, &delay,
+			chip->base + BMS1_S1_DELAY_CTL, 1);
+	if (rc)
+		pr_err("unable to write into BMS1_S1_DELAY, rc: %d\n", rc);
+
+	mutex_unlock(&chip->bms_output_lock);
+
+	*vbat_uv = convert_vbatt_raw_to_uv(chip, vbat_raw);
+	vsense_uv = convert_vsense_to_uv(chip, vsense_raw);
+	*ibat_ua = vsense_uv * 1000 / (int)chip->r_sense_mohm;
+
+	pr_debug("vsense_raw = 0x%x vbat_raw = 0x%x ibat_ua = %d vbat_uv = %d\n",
+			(uint16_t)vsense_raw, (uint16_t)vbat_raw,
+			*ibat_ua, *vbat_uv);
+	return 0;
+}
+
+static int get_simultaneous_batt_v_and_i(
+						struct qpnp_bms_chip *chip,
+						int *ibat_ua, int *vbat_uv)
+{
+	int rc;
+	union power_supply_propval ret = {0,};
+
+	if (chip->batt_psy == NULL)
+		chip->batt_psy = power_supply_get_by_name("battery");
+	if (chip->batt_psy) {
+		/* if battery has been registered, use the status property */
+		chip->batt_psy->get_property(chip->batt_psy,
+					POWER_SUPPLY_PROP_STATUS, &ret);
+	} else {
+		/* default to using separate vbat/ibat if unregistered */
+		ret.intval = POWER_SUPPLY_STATUS_FULL;
+	}
+
+	if (ret.intval == POWER_SUPPLY_STATUS_FULL) {
+		pr_debug("batfet is open using separate vbat and ibat meas\n");
+		rc = get_battery_voltage(vbat_uv);
+		if (rc < 0) {
+			pr_err("adc vbat failed err = %d\n", rc);
+			return rc;
+		}
+		rc = get_battery_current(chip, ibat_ua);
+		if (rc < 0) {
+			pr_err("bms ibat failed err = %d\n", rc);
+			return rc;
+		}
+	} else {
+		return override_mode_batt_v_and_i(chip, ibat_ua, vbat_uv);
+	}
+
+	return 0;
+}
+
+static int bound_soc(int soc)
+{
+	soc = max(0, soc);
+	soc = min(100, soc);
+	return soc;
+}
+
+static int charging_adjustments(struct qpnp_bms_chip *chip,
+				struct soc_params *params, int soc,
+				int vbat_uv, int ibat_ua, int batt_temp)
+{
+	int chg_soc;
+
+	if (chip->soc_at_cv == -EINVAL) {
+		/* In constant current charging return the calc soc */
+		if (vbat_uv <= chip->max_voltage_uv)
+			pr_debug("CC CHG SOC %d\n", soc);
+
+		/* Note the CC to CV point */
+		if (vbat_uv >= chip->max_voltage_uv) {
+			chip->soc_at_cv = soc;
+			chip->prev_chg_soc = soc;
+			chip->ibat_at_cv_ua = ibat_ua;
+			pr_debug("CC_TO_CV ibat_ua = %d CHG SOC %d\n",
+					ibat_ua, soc);
+		}
+		return soc;
+	}
+
+	/*
+	 * battery is in CV phase - begin liner inerpolation of soc based on
+	 * battery charge current
+	 */
+
+	/*
+	 * if voltage lessened (possibly because of a system load)
+	 * keep reporting the prev chg soc
+	 */
+	if (vbat_uv <= chip->max_voltage_uv) {
+		pr_debug("vbat %d < max = %d CC CHG SOC %d\n",
+			vbat_uv, chip->max_voltage_uv, chip->prev_chg_soc);
+		return chip->prev_chg_soc;
+	}
+
+	chg_soc = linear_interpolate(chip->soc_at_cv, chip->ibat_at_cv_ua,
+					100, -100000,
+					ibat_ua);
+
+	/* always report a higher soc */
+	if (chg_soc > chip->prev_chg_soc) {
+		int new_ocv_uv;
+
+		chip->prev_chg_soc = chg_soc;
+
+		find_ocv_for_soc(chip, params, batt_temp, chg_soc, &new_ocv_uv);
+		chip->last_ocv_uv = new_ocv_uv;
+		pr_debug("CC CHG ADJ OCV = %d CHG SOC %d\n",
+				new_ocv_uv,
+				chip->prev_chg_soc);
+	}
+
+	pr_debug("Reporting CHG SOC %d\n", chip->prev_chg_soc);
+	return chip->prev_chg_soc;
+}
+
+static int adjust_soc(struct qpnp_bms_chip *chip, struct soc_params *params,
+							int soc, int batt_temp)
+{
+	int ibat_ua = 0, vbat_uv = 0;
+	int ocv_est_uv = 0, soc_est = 0, pc_est = 0, pc = 0;
+	int delta_ocv_uv = 0;
+	int n = 0;
+	int rc_new_uah = 0;
+	int pc_new = 0;
+	int soc_new = 0;
+	int slope = 0;
+	int rc = 0;
+	int delta_ocv_uv_limit = 0;
+
+	rc = get_simultaneous_batt_v_and_i(chip, &ibat_ua, &vbat_uv);
+	if (rc < 0) {
+		pr_err("simultaneous vbat ibat failed err = %d\n", rc);
+		goto out;
+	}
+
+	delta_ocv_uv_limit = DIV_ROUND_CLOSEST(ibat_ua, 1000);
+
+	ocv_est_uv = vbat_uv + (ibat_ua * params->rbatt)/1000;
+	pc_est = calculate_pc(chip, ocv_est_uv, batt_temp);
+	soc_est = div_s64((s64)params->fcc_uah * pc_est - params->uuc_uah*100,
+				(s64)params->fcc_uah - params->uuc_uah);
+	soc_est = bound_soc(soc_est);
+
+	if (ibat_ua < 0) {
+		soc = charging_adjustments(chip, params, soc, vbat_uv, ibat_ua,
+				batt_temp);
+		goto out;
+	}
+
+	/*
+	 * do not adjust
+	 * if soc is same as what bms calculated
+	 * if soc_est is between 45 and 25, this is the flat portion of the
+	 * curve where soc_est is not so accurate. We generally don't want to
+	 * adjust when soc_est is inaccurate except for the cases when soc is
+	 * way far off (higher than 50 or lesser than 20).
+	 * Also don't adjust soc if it is above 90 becuase it might be pulled
+	 * low and cause a bad user experience
+	 */
+	if (soc_est == soc
+		|| (is_between(45, chip->adjust_soc_low_threshold, soc_est)
+		&& is_between(50, chip->adjust_soc_low_threshold - 5, soc))
+		|| soc >= 90)
+		goto out;
+
+	if (chip->last_soc_est == -EINVAL)
+		chip->last_soc_est = soc;
+
+	n = min(200, max(1 , soc + soc_est + chip->last_soc_est));
+	chip->last_soc_est = soc_est;
+
+	pc = calculate_pc(chip, chip->last_ocv_uv, batt_temp);
+	if (pc > 0) {
+		pc_new = calculate_pc(chip,
+				chip->last_ocv_uv - (++slope * 1000),
+				batt_temp);
+		while (pc_new == pc) {
+			/* start taking 10mV steps */
+			slope = slope + 10;
+			pc_new = calculate_pc(chip,
+				chip->last_ocv_uv - (slope * 1000),
+				batt_temp);
+		}
+	} else {
+		/*
+		 * pc is already at the lowest point,
+		 * assume 1 millivolt translates to 1% pc
+		 */
+		pc = 1;
+		pc_new = 0;
+		slope = 1;
+	}
+
+	delta_ocv_uv = div_s64((soc - soc_est) * (s64)slope * 1000,
+							n * (pc - pc_new));
+
+	if (abs(delta_ocv_uv) > delta_ocv_uv_limit) {
+		pr_debug("limiting delta ocv %d limit = %d\n", delta_ocv_uv,
+				delta_ocv_uv_limit);
+
+		if (delta_ocv_uv > 0)
+			delta_ocv_uv = delta_ocv_uv_limit;
+		else
+			delta_ocv_uv = -1 * delta_ocv_uv_limit;
+		pr_debug("new delta ocv = %d\n", delta_ocv_uv);
+	}
+
+	chip->last_ocv_uv -= delta_ocv_uv;
+
+	if (chip->last_ocv_uv >= chip->max_voltage_uv)
+		chip->last_ocv_uv = chip->max_voltage_uv;
+
+	/* calculate the soc based on this new ocv */
+	pc_new = calculate_pc(chip, chip->last_ocv_uv, batt_temp);
+	rc_new_uah = (params->fcc_uah * pc_new) / 100;
+	soc_new = (rc_new_uah - params->cc_uah - params->uuc_uah)*100
+					/ (params->fcc_uah - params->uuc_uah);
+	soc_new = bound_soc(soc_new);
+
+	/*
+	 * if soc_new is ZERO force it higher so that phone doesnt report soc=0
+	 * soc = 0 should happen only when soc_est == 0
+	 */
+	if (soc_new == 0 && soc_est != 0)
+		soc_new = 1;
+
+	soc = soc_new;
+
+out:
+	pr_debug("ibat_ua = %d, vbat_uv = %d, ocv_est_uv = %d, pc_est = %d, soc_est = %d, n = %d, delta_ocv_uv = %d, last_ocv_uv = %d, pc_new = %d, soc_new = %d, rbatt = %d, slope = %d\n",
+		ibat_ua, vbat_uv, ocv_est_uv, pc_est,
+		soc_est, n, delta_ocv_uv, chip->last_ocv_uv,
+		pc_new, soc_new, params->rbatt, slope);
+
+	return soc;
+}
+
+static int calculate_state_of_charge(struct qpnp_bms_chip *chip,
+					struct raw_soc_params *raw,
+					int batt_temp)
+{
+	int soc, new_ocv_uv;
+	int shutdown_soc, new_calculated_soc, remaining_usable_charge_uah;
+	struct soc_params params;
+
+	calculate_soc_params(chip, raw, &params, batt_temp);
+	/* calculate remaining usable charge */
+	remaining_usable_charge_uah = params.ocv_charge_uah
+					- params.cc_uah
+					- params.uuc_uah;
+
+	pr_debug("RUC = %duAh\n", remaining_usable_charge_uah);
+	if (params.fcc_uah - params.uuc_uah <= 0) {
+		pr_warn("FCC = %duAh, UUC = %duAh forcing soc = 0\n",
+						params.fcc_uah,
+						params.uuc_uah);
+		soc = 0;
+	} else {
+		soc = DIV_ROUND_CLOSEST((remaining_usable_charge_uah * 100),
+					(params.fcc_uah
+						- params.uuc_uah));
+	}
+
+	if (chip->first_time_calc_soc && soc < 0) {
+		/*
+		 * first time calcualtion and the pon ocv  is too low resulting
+		 * in a bad soc. Adjust ocv to get 0 soc
+		 */
+		pr_debug("soc is %d, adjusting pon ocv to make it 0\n", soc);
+		find_ocv_for_soc(chip, &params, batt_temp, 0, &new_ocv_uv);
+		chip->last_ocv_uv = new_ocv_uv;
+
+		remaining_usable_charge_uah = params.ocv_charge_uah
+					- params.cc_uah
+					- params.uuc_uah;
+
+		soc = DIV_ROUND_CLOSEST((remaining_usable_charge_uah * 100),
+					(params.fcc_uah
+						- params.uuc_uah));
+		pr_debug("DONE for O soc is %d, pon ocv adjusted to %duV\n",
+				soc, chip->last_ocv_uv);
+	}
+
+	if (soc > 100)
+		soc = 100;
+
+	if (soc < 0) {
+		pr_err("bad rem_usb_chg = %d rem_chg %d, cc_uah %d, unusb_chg %d\n",
+				remaining_usable_charge_uah,
+				params.ocv_charge_uah,
+				params.cc_uah, params.uuc_uah);
+
+		pr_err("for bad rem_usb_chg last_ocv_uv = %d batt_temp = %d fcc = %d soc =%d\n",
+				chip->last_ocv_uv, batt_temp,
+				params.fcc_uah, soc);
+		soc = 0;
+	}
+
+	mutex_lock(&chip->soc_invalidation_mutex);
+	shutdown_soc = chip->shutdown_soc;
+
+	if (chip->first_time_calc_soc && soc != shutdown_soc
+			&& is_shutdown_soc_within_limits(chip, soc)) {
+		/*
+		 * soc for the first time - use shutdown soc
+		 * to adjust pon ocv since it is a small percent away from
+		 * the real soc
+		 */
+		pr_debug("soc = %d before forcing shutdown_soc = %d\n",
+							soc, shutdown_soc);
+		find_ocv_for_soc(chip, &params, batt_temp,
+					shutdown_soc, &new_ocv_uv);
+		chip->pon_ocv_uv = chip->last_ocv_uv;
+		chip->last_ocv_uv = new_ocv_uv;
+
+		remaining_usable_charge_uah = params.ocv_charge_uah
+					- params.cc_uah
+					- params.uuc_uah;
+
+		soc = DIV_ROUND_CLOSEST((remaining_usable_charge_uah * 100),
+					(params.fcc_uah
+						- params.uuc_uah));
+
+		pr_debug("DONE for shutdown_soc = %d soc is %d, adjusted ocv to %duV\n",
+				shutdown_soc, soc, chip->last_ocv_uv);
+	}
+	mutex_unlock(&chip->soc_invalidation_mutex);
+
+	pr_debug("SOC before adjustment = %d\n", soc);
+	new_calculated_soc = adjust_soc(chip, &params, soc, batt_temp);
+
+	if (new_calculated_soc != chip->calculated_soc
+			&& chip->bms_psy.name != NULL) {
+		power_supply_changed(&chip->bms_psy);
+		pr_debug("power supply changed\n");
+	}
+
+	chip->calculated_soc = new_calculated_soc;
+	pr_debug("CC based calculated SOC = %d\n", chip->calculated_soc);
+	chip->first_time_calc_soc = 0;
+	return chip->calculated_soc;
+}
+
+static int read_vbat(struct qpnp_bms_chip *chip)
+{
+	int rc;
+	struct qpnp_vadc_result result;
+
+	rc = qpnp_vadc_read(VBAT_SNS, &result);
+	if (rc) {
+		pr_err("error reading vadc VBAT_SNS = %d, rc = %d\n",
+					VBAT_SNS, rc);
+		return rc;
+	}
+	pr_debug("read %duv from vadc\n", (int)result.physical);
+	return (int)result.physical;
+}
+
+static int calculate_soc_from_voltage(struct qpnp_bms_chip *chip)
+{
+	int voltage_range_uv, voltage_remaining_uv, voltage_based_soc;
+	int vbat_uv;
+
+	vbat_uv = read_vbat(chip);
+
+	voltage_range_uv = chip->max_voltage_uv - chip->v_cutoff_uv;
+	voltage_remaining_uv = vbat_uv - chip->v_cutoff_uv;
+	voltage_based_soc = voltage_remaining_uv * 100 / voltage_range_uv;
+
+	voltage_based_soc = clamp(voltage_based_soc, 0, 100);
+
+	if (chip->prev_voltage_based_soc != voltage_based_soc
+				&& chip->bms_psy.name != NULL) {
+		power_supply_changed(&chip->bms_psy);
+		pr_debug("power supply changed\n");
+	}
+	chip->prev_voltage_based_soc = voltage_based_soc;
+
+	pr_debug("vbat used = %duv\n", vbat_uv);
+	pr_debug("Calculated voltage based soc = %d\n", voltage_based_soc);
+	return voltage_based_soc;
+}
+
+static void calculate_soc_work(struct work_struct *work)
+{
+	struct qpnp_bms_chip *chip = container_of(work,
+				struct qpnp_bms_chip,
+				calculate_soc_delayed_work.work);
+	int batt_temp, rc, soc;
+	struct qpnp_vadc_result result;
+	struct raw_soc_params raw;
+
+	if (chip->use_voltage_soc) {
+		soc = calculate_soc_from_voltage(chip);
+	} else {
+		rc = qpnp_vadc_read(LR_MUX1_BATT_THERM, &result);
+		if (rc) {
+			pr_err("error reading vadc LR_MUX1_BATT_THERM = %d, rc = %d\n",
+						LR_MUX1_BATT_THERM, rc);
+			return;
+		}
+		pr_debug("batt_temp phy = %lld meas = 0x%llx\n",
+						result.physical,
+						result.measurement);
+		batt_temp = (int)result.physical;
+
+		mutex_lock(&chip->last_ocv_uv_mutex);
+		read_soc_params_raw(chip, &raw);
+		soc = calculate_state_of_charge(chip, &raw, batt_temp);
+		mutex_unlock(&chip->last_ocv_uv_mutex);
+	}
+
+	if (soc < chip->low_soc_calc_threshold)
+		schedule_delayed_work(&chip->calculate_soc_delayed_work,
+			round_jiffies_relative(msecs_to_jiffies
+			(chip->low_soc_calculate_soc_ms)));
+	else
+		schedule_delayed_work(&chip->calculate_soc_delayed_work,
+			round_jiffies_relative(msecs_to_jiffies
+			(chip->calculate_soc_ms)));
+}
+
+static void backup_soc_and_iavg(struct qpnp_bms_chip *chip, int batt_temp,
+				int soc)
+{
+	u8 temp;
+	int rc;
+	int iavg_ma = chip->prev_uuc_iavg_ma;
+
+	if (iavg_ma > IAVG_START)
+		temp = (iavg_ma - IAVG_START) / IAVG_STEP_SIZE_MA;
+	else
+		temp = 0;
+
+	rc = qpnp_write_wrapper(chip, &temp,
+			chip->base + IAVG_STORAGE_REG, 1);
+
+	if (soc == 0)
+		temp = SOC_ZERO;
+	else
+		temp = soc;
+
+	/* don't store soc if temperature is below 5degC */
+	if (batt_temp > IGNORE_SOC_TEMP_DECIDEG)
+		rc = qpnp_write_wrapper(chip, &temp,
+				chip->base + SOC_STORAGE_REG, 1);
+}
+
+#define SOC_CATCHUP_SEC_MAX		600
+#define SOC_CATCHUP_SEC_PER_PERCENT	60
+#define MAX_CATCHUP_SOC	(SOC_CATCHUP_SEC_MAX/SOC_CATCHUP_SEC_PER_PERCENT)
+static int scale_soc_while_chg(struct qpnp_bms_chip *chip,
+				int delta_time_us, int new_soc, int prev_soc)
+{
+	int chg_time_sec;
+	int catch_up_sec;
+	int scaled_soc;
+	int numerator;
+
+	/*
+	 * The device must be charging for reporting a higher soc, if
+	 * not ignore this soc and continue reporting the prev_soc.
+	 * Also don't report a high value immediately slowly scale the
+	 * value from prev_soc to the new soc based on a charge time
+	 * weighted average
+	 */
+
+	/* if not charging, return last soc */
+	if (chip->start_percent == -EINVAL)
+		return prev_soc;
+
+	chg_time_sec = DIV_ROUND_UP(chip->charge_time_us, USEC_PER_SEC);
+	catch_up_sec = DIV_ROUND_UP(chip->catch_up_time_us, USEC_PER_SEC);
+	pr_debug("cts= %d catch_up_sec = %d\n", chg_time_sec, catch_up_sec);
+
+	/*
+	 * if charging for more than catch_up time, simply return
+	 * new soc
+	 */
+	if (chg_time_sec > catch_up_sec)
+		return new_soc;
+
+	numerator = (catch_up_sec - chg_time_sec) * prev_soc
+			+ chg_time_sec * new_soc;
+	scaled_soc = numerator / catch_up_sec;
+
+	pr_debug("cts = %d new_soc = %d prev_soc = %d scaled_soc = %d\n",
+			chg_time_sec, new_soc, prev_soc, scaled_soc);
+
+	return scaled_soc;
+}
+
+/*
+ * bms_fake_battery is set in setups where a battery emulator is used instead
+ * of a real battery. This makes the bms driver report a different/fake value
+ * regardless of the calculated state of charge.
+ */
+static int bms_fake_battery = -EINVAL;
+module_param(bms_fake_battery, int, 0644);
+
+static int report_voltage_based_soc(struct qpnp_bms_chip *chip)
+{
+	pr_debug("Reported voltage based soc = %d\n",
+			chip->prev_voltage_based_soc);
+	return chip->prev_voltage_based_soc;
+}
+
+static int report_cc_based_soc(struct qpnp_bms_chip *chip)
+{
+	int soc;
+	int delta_time_us;
+	struct timespec now;
+	struct qpnp_vadc_result result;
+	int batt_temp;
+	int rc;
+
+	soc = chip->calculated_soc;
+
+	rc = qpnp_vadc_read(LR_MUX1_BATT_THERM, &result);
+
+	if (rc) {
+		pr_err("error reading adc channel = %d, rc = %d\n",
+					LR_MUX1_BATT_THERM, rc);
+		return rc;
+	}
+	pr_debug("batt_temp phy = %lld meas = 0x%llx\n", result.physical,
+						result.measurement);
+	batt_temp = (int)result.physical;
+
+	do_posix_clock_monotonic_gettime(&now);
+	if (chip->t_soc_queried.tv_sec != 0) {
+		delta_time_us
+		= (now.tv_sec - chip->t_soc_queried.tv_sec) * USEC_PER_SEC
+			+ (now.tv_nsec - chip->t_soc_queried.tv_nsec) / 1000;
+	} else {
+		/* calculation for the first time */
+		delta_time_us = 0;
+	}
+
+	/*
+	 * account for charge time - limit it to SOC_CATCHUP_SEC to
+	 * avoid overflows when charging continues for extended periods
+	 */
+	if (chip->start_percent != -EINVAL) {
+		if (chip->charge_time_us == 0) {
+			/*
+			 * calculating soc for the first time
+			 * after start of chg. Initialize catchup time
+			 */
+			if (abs(soc - chip->last_soc) < MAX_CATCHUP_SOC)
+				chip->catch_up_time_us =
+				(soc - chip->last_soc)
+					* SOC_CATCHUP_SEC_PER_PERCENT
+					* USEC_PER_SEC;
+			else
+				chip->catch_up_time_us =
+				SOC_CATCHUP_SEC_MAX * USEC_PER_SEC;
+
+			if (chip->catch_up_time_us < 0)
+				chip->catch_up_time_us = 0;
+		}
+
+		/* add charge time */
+		if (chip->charge_time_us < SOC_CATCHUP_SEC_MAX * USEC_PER_SEC)
+			chip->charge_time_us += delta_time_us;
+
+		/* end catchup if calculated soc and last soc are same */
+		if (chip->last_soc == soc)
+			chip->catch_up_time_us = 0;
+	}
+
+	/* last_soc < soc  ... scale and catch up */
+	if (chip->last_soc != -EINVAL && chip->last_soc < soc && soc != 100)
+		soc = scale_soc_while_chg(chip, delta_time_us,
+						soc, chip->last_soc);
+
+	pr_debug("last_soc = %d, calculated_soc = %d, soc = %d\n",
+			chip->last_soc, chip->calculated_soc, soc);
+	chip->last_soc = soc;
+	backup_soc_and_iavg(chip, batt_temp, chip->last_soc);
+	pr_debug("Reported SOC = %d\n", chip->last_soc);
+	chip->t_soc_queried = now;
+
+	return chip->last_soc;
+}
+
+static int report_state_of_charge(struct qpnp_bms_chip *chip)
+{
+	if (bms_fake_battery != -EINVAL) {
+		pr_debug("Returning Fake SOC = %d%%\n", bms_fake_battery);
+		return bms_fake_battery;
+	} else if (chip->use_voltage_soc)
+		return report_voltage_based_soc(chip);
+	else
+		return report_cc_based_soc(chip);
+}
+
 /* Returns capacity as a SoC percentage between 0 and 100 */
 static int get_prop_bms_capacity(struct qpnp_bms_chip *chip)
 {
-	/* return 50 until a real algorithm is implemented */
-	return 50;
+	return report_state_of_charge(chip);
 }
 
 /* Returns instantaneous current in uA */
 static int get_prop_bms_current_now(struct qpnp_bms_chip *chip)
 {
 	/* temporarily return 0 until a real algorithm is put in */
-	return 0;
+	int rc, result_ua;
+
+	rc = get_battery_current(chip, &result_ua);
+	if (rc) {
+		pr_err("failed to get current: %d\n", rc);
+		return rc;
+	}
+	return result_ua;
 }
 
 /* Returns full charge design in uAh */
 static int get_prop_bms_charge_full_design(struct qpnp_bms_chip *chip)
 {
-	/* temporarily return 0 until a real algorithm is put in */
-	return 0;
+	return chip->fcc;
+}
+
+static bool get_prop_bms_online(struct qpnp_bms_chip *chip)
+{
+	return chip->online;
+}
+
+static int get_prop_bms_status(struct qpnp_bms_chip *chip)
+{
+	return chip->charger_status;
 }
 
 static void set_prop_bms_online(struct qpnp_bms_chip *chip, bool online)
@@ -184,6 +1681,12 @@
 	case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
 		val->intval = get_prop_bms_charge_full_design(chip);
 		break;
+	case POWER_SUPPLY_PROP_STATUS:
+		val->intval = get_prop_bms_status(chip);
+		break;
+	case POWER_SUPPLY_PROP_ONLINE:
+		val->intval = get_prop_bms_online(chip);
+		break;
 	default:
 		return -EINVAL;
 	}
@@ -210,24 +1713,205 @@
 	return 0;
 }
 
-#define SPMI_PROPERTY_READ(chip_prop, qpnp_spmi_property, retval, errlabel)\
+static void read_shutdown_soc_and_iavg(struct qpnp_bms_chip *chip)
+{
+	int rc;
+	u8 temp;
+
+	if (chip->ignore_shutdown_soc) {
+		chip->shutdown_soc_invalid = 1;
+		chip->shutdown_soc = 0;
+		chip->shutdown_iavg_ma = 0;
+	} else {
+		rc = qpnp_read_wrapper(chip, &temp,
+				chip->base + IAVG_STORAGE_REG, 1);
+		if (rc) {
+			pr_err("failed to read addr = %d %d assuming %d\n",
+					chip->base + IAVG_STORAGE_REG, rc,
+					IAVG_START);
+			chip->shutdown_iavg_ma = IAVG_START;
+		} else {
+			if (temp == 0) {
+				chip->shutdown_iavg_ma = IAVG_START;
+			} else {
+				chip->shutdown_iavg_ma = IAVG_START
+					+ IAVG_STEP_SIZE_MA * (temp + 1);
+			}
+		}
+
+		rc = qpnp_read_wrapper(chip, &temp,
+				chip->base + SOC_STORAGE_REG, 1);
+		if (rc) {
+			pr_err("failed to read addr = %d %d\n",
+					chip->base + SOC_STORAGE_REG, rc);
+		} else {
+			chip->shutdown_soc = temp;
+
+			if (chip->shutdown_soc == 0) {
+				pr_debug("No shutdown soc available\n");
+				chip->shutdown_soc_invalid = 1;
+				chip->shutdown_iavg_ma = 0;
+			} else if (chip->shutdown_soc == SOC_ZERO) {
+				chip->shutdown_soc = 0;
+			}
+		}
+	}
+
+	pr_debug("shutdown_soc = %d shutdown_iavg = %d shutdown_soc_invalid = %d\n",
+			chip->shutdown_soc,
+			chip->shutdown_iavg_ma,
+			chip->shutdown_soc_invalid);
+}
+
+#define PALLADIUM_ID_MIN	0x7F40
+#define PALLADIUM_ID_MAX	0x7F5A
+#define DESAY_5200_ID_MIN	0x7F7F
+#define DESAY_5200_ID_MAX	0x802F
+static int32_t read_battery_id(struct qpnp_bms_chip *chip)
+{
+	int rc;
+	struct qpnp_vadc_result result;
+
+	rc = qpnp_vadc_read(LR_MUX2_BAT_ID, &result);
+	if (rc) {
+		pr_err("error reading batt id channel = %d, rc = %d\n",
+					LR_MUX2_BAT_ID, rc);
+		return rc;
+	}
+	pr_debug("batt_id phy = %lld meas = 0x%llx\n", result.physical,
+						result.measurement);
+	pr_debug("raw_code = 0x%x\n", result.adc_code);
+	return result.adc_code;
+}
+
+static int set_battery_data(struct qpnp_bms_chip *chip)
+{
+	int64_t battery_id;
+
+	if (chip->batt_type == BATT_DESAY)
+		goto desay;
+	else if (chip->batt_type == BATT_PALLADIUM)
+		goto palladium;
+
+	battery_id = read_battery_id(chip);
+	if (battery_id < 0) {
+		pr_err("cannot read battery id err = %lld\n", battery_id);
+		return battery_id;
+	}
+
+	if (is_between(PALLADIUM_ID_MIN, PALLADIUM_ID_MAX, battery_id)) {
+		goto palladium;
+	} else if (is_between(DESAY_5200_ID_MIN, DESAY_5200_ID_MAX,
+				battery_id)) {
+		goto desay;
+	} else {
+		pr_warn("invalid battid, palladium 1500 assumed batt_id %llx\n",
+				battery_id);
+		goto palladium;
+	}
+
+palladium:
+		chip->fcc = palladium_1500_data.fcc;
+		chip->fcc_temp_lut = palladium_1500_data.fcc_temp_lut;
+		chip->fcc_sf_lut = palladium_1500_data.fcc_sf_lut;
+		chip->pc_temp_ocv_lut = palladium_1500_data.pc_temp_ocv_lut;
+		chip->pc_sf_lut = palladium_1500_data.pc_sf_lut;
+		chip->rbatt_sf_lut = palladium_1500_data.rbatt_sf_lut;
+		chip->default_rbatt_mohm
+				= palladium_1500_data.default_rbatt_mohm;
+		goto check_lut;
+desay:
+		chip->fcc = desay_5200_data.fcc;
+		chip->fcc_temp_lut = desay_5200_data.fcc_temp_lut;
+		chip->pc_temp_ocv_lut = desay_5200_data.pc_temp_ocv_lut;
+		chip->pc_sf_lut = desay_5200_data.pc_sf_lut;
+		chip->rbatt_sf_lut = desay_5200_data.rbatt_sf_lut;
+		chip->default_rbatt_mohm = desay_5200_data.default_rbatt_mohm;
+		goto check_lut;
+check_lut:
+		if (chip->pc_temp_ocv_lut == NULL) {
+			pr_err("temp ocv lut table is NULL\n");
+			return -EINVAL;
+		}
+		return 0;
+}
+
+#define SPMI_PROP_READ(chip_prop, qpnp_spmi_property, retval)		\
 do {									\
-	retval = of_property_read_u32(spmi->dev.of_node,		\
+	retval = of_property_read_u32(chip->spmi->dev.of_node,		\
 				"qcom,bms-" qpnp_spmi_property,		\
 					&chip->chip_prop);		\
 	if (retval) {							\
 		pr_err("Error reading " #qpnp_spmi_property		\
 						" property %d\n", rc);	\
-		goto errlabel;						\
+		return -EINVAL;						\
 	}								\
 } while (0)
 
+static inline int bms_read_properties(struct qpnp_bms_chip *chip)
+{
+	int rc;
+
+	SPMI_PROP_READ(r_sense_mohm, "r-sense-mohm", rc);
+	SPMI_PROP_READ(v_cutoff_uv, "v-cutoff-uv", rc);
+	SPMI_PROP_READ(max_voltage_uv, "max-voltage-uv", rc);
+	SPMI_PROP_READ(r_conn_mohm, "r-conn-mohm", rc);
+	SPMI_PROP_READ(chg_term_ua, "chg-term-ua", rc);
+	SPMI_PROP_READ(shutdown_soc_valid_limit,
+			"shutdown-soc-valid-limit", rc);
+	SPMI_PROP_READ(adjust_soc_high_threshold,
+			"adjust-soc-high-threshold", rc);
+	SPMI_PROP_READ(adjust_soc_low_threshold,
+			"adjust-soc-low-threshold", rc);
+	SPMI_PROP_READ(batt_type, "batt-type", rc);
+	SPMI_PROP_READ(low_soc_calc_threshold,
+			"low-soc-calculate-soc-threshold", rc);
+	SPMI_PROP_READ(low_soc_calculate_soc_ms,
+			"low-soc-calculate-soc-ms", rc);
+	SPMI_PROP_READ(calculate_soc_ms, "calculate-soc-ms", rc);
+	chip->ignore_shutdown_soc = of_property_read_bool(
+			chip->spmi->dev.of_node,
+			"qcom,bms-ignore-shutdown-soc");
+	chip->use_voltage_soc = of_property_read_bool(chip->spmi->dev.of_node,
+			"qcom,bms-use-voltage-soc");
+
+	if (chip->adjust_soc_low_threshold >= 45)
+		chip->adjust_soc_low_threshold = 45;
+
+	pr_debug("dts data: r_sense_mohm:%d, v_cutoff_uv:%d, max_v:%d\n",
+			chip->r_sense_mohm, chip->v_cutoff_uv,
+			chip->max_voltage_uv);
+	pr_debug("r_conn:%d, shutdown_soc: %d, adjust_soc_low:%d\n",
+			chip->r_conn_mohm, chip->shutdown_soc_valid_limit,
+			chip->adjust_soc_low_threshold);
+	pr_debug("adjust_soc_high:%d, chg_term_ua:%d, batt_type:%d\n",
+			chip->adjust_soc_high_threshold, chip->chg_term_ua,
+			chip->batt_type);
+	pr_debug("ignore_shutdown_soc:%d, use_voltage_soc:%d\n",
+			chip->ignore_shutdown_soc, chip->use_voltage_soc);
+
+	return 0;
+}
+
+static inline void bms_initialize_constants(struct qpnp_bms_chip *chip)
+{
+	chip->start_percent = -EINVAL;
+	chip->end_percent = -EINVAL;
+	chip->prev_pc_unusable = -EINVAL;
+	chip->soc_at_cv = -EINVAL;
+	chip->calculated_soc = -EINVAL;
+	chip->last_soc = -EINVAL;
+	chip->last_soc_est = -EINVAL;
+	chip->first_time_calc_soc = 1;
+	chip->first_time_calc_uuc = 1;
+}
+
 static int __devinit
 qpnp_bms_probe(struct spmi_device *spmi)
 {
 	struct qpnp_bms_chip *chip;
 	struct resource *bms_resource;
-	int rc;
+	int rc, vbatt;
 
 	chip = kzalloc(sizeof *chip, GFP_KERNEL);
 
@@ -239,6 +1923,10 @@
 	chip->dev = &(spmi->dev);
 	chip->spmi = spmi;
 
+	mutex_init(&chip->bms_output_lock);
+	mutex_init(&chip->last_ocv_uv_mutex);
+	mutex_init(&chip->soc_invalidation_mutex);
+
 	bms_resource = spmi_get_resource(spmi, NULL, IORESOURCE_MEM, 0);
 	if (!bms_resource) {
 		dev_err(&spmi->dev, "Unable to get BMS base address\n");
@@ -260,29 +1948,42 @@
 		goto error_read;
 	}
 
-	SPMI_PROPERTY_READ(r_sense_mohm, "r-sense-mohm", rc, error_read);
-	SPMI_PROPERTY_READ(v_cutoff, "v-cutoff-uv", rc, error_read);
-	SPMI_PROPERTY_READ(max_voltage, "max-voltage-uv", rc, error_read);
-	SPMI_PROPERTY_READ(r_conn_mohm, "r-conn-mohm", rc, error_read);
-	SPMI_PROPERTY_READ(shutdown_soc_valid_limit,
-			"shutdown-soc-valid-limit", rc, error_read);
-	SPMI_PROPERTY_READ(adjust_soc_low_threshold,
-			"adjust-soc-low-threshold", rc, error_read);
-	SPMI_PROPERTY_READ(adjust_soc_high_threshold,
-			"adjust-soc-high-threshold", rc, error_read);
-	SPMI_PROPERTY_READ(chg_term, "chg-term-ua", rc, error_read);
+	rc = qpnp_vadc_is_ready();
+	if (rc) {
+		pr_info("vadc not ready: %d, deferring probe\n", rc);
+		goto error_read;
+	}
 
-	pr_debug("dts data: r_sense_mohm:%d, v_cutoff:%d, max_v:%d, r_conn:%d, shutdown_soc: %d, adjust_soc_low:%d, adjust_soc_high:%d, chg_term:%d\n",
-			chip->r_sense_mohm, chip->v_cutoff,
-			chip->max_voltage, chip->r_conn_mohm,
-			chip->shutdown_soc_valid_limit,
-			chip->adjust_soc_low_threshold,
-			chip->adjust_soc_high_threshold,
-			chip->chg_term);
+	rc = qpnp_iadc_is_ready();
+	if (rc) {
+		pr_info("iadc not ready: %d, deferring probe\n", rc);
+		goto error_read;
+	}
+
+	rc = set_battery_data(chip);
+	if (rc) {
+		pr_err("Bad battery data %d\n", rc);
+		goto error_read;
+	}
+
+	rc = bms_read_properties(chip);
+	if (rc) {
+		pr_err("Unable to read all bms properties, rc = %d\n", rc);
+		goto error_read;
+	}
+
+	bms_initialize_constants(chip);
+
+	INIT_DELAYED_WORK(&chip->calculate_soc_delayed_work,
+			calculate_soc_work);
+
+	read_shutdown_soc_and_iavg(chip);
 
 	dev_set_drvdata(&spmi->dev, chip);
 	device_init_wakeup(&spmi->dev, 1);
 
+	calculate_soc_work(&(chip->calculate_soc_delayed_work.work));
+
 	/* setup & register the battery power supply */
 	chip->bms_psy.name = "bms";
 	chip->bms_psy.type = POWER_SUPPLY_TYPE_BMS;
@@ -302,6 +2003,12 @@
 		goto unregister_dc;
 	}
 
+	vbatt = 0;
+	get_battery_voltage(&vbatt);
+
+	pr_debug("OK battery_capacity_at_boot=%d vbatt = %d\n",
+				get_prop_bms_capacity(chip),
+				vbatt);
 	pr_info("probe success\n");
 	return 0;
 
diff --git a/drivers/power/qpnp-charger.c b/drivers/power/qpnp-charger.c
index 8d5244b..b5559db 100644
--- a/drivers/power/qpnp-charger.c
+++ b/drivers/power/qpnp-charger.c
@@ -189,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;
@@ -202,9 +203,6 @@
 	uint32_t			flags;
 };
 
-static struct qpnp_chg_chip *the_chip;
-static bool charging_disabled;
-
 static struct of_device_id qpnp_charger_match_table[] = {
 	{ .compatible = QPNP_CHARGER_DEV_NAME, },
 	{}
@@ -424,6 +422,20 @@
 	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,
@@ -462,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);
@@ -690,7 +702,7 @@
 }
 
 static int
-qpnp_chg_charge_dis(struct qpnp_chg_chip *chip, int disable)
+qpnp_chg_force_run_on_batt(struct qpnp_chg_chip *chip, int disable)
 {
 	/* This bit forces the charger to run off of the battery rather
 	 * than a connected charger */
@@ -753,7 +765,7 @@
 		val->intval = get_prop_full_design(chip);
 		break;
 	case POWER_SUPPLY_PROP_CHARGING_ENABLED:
-		val->intval = !charging_disabled;
+		val->intval = !(chip->charging_disabled);
 		break;
 	default:
 		return -EINVAL;
@@ -772,11 +784,9 @@
 
 	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);
-		charging_disabled = !(val->intval);
+		chip->charging_disabled = !(val->intval);
+		qpnp_chg_charge_en(chip, !chip->charging_disabled);
+		qpnp_chg_force_run_on_batt(chip, chip->charging_disabled);
 		break;
 	default:
 		return -EINVAL;
@@ -786,25 +796,6 @@
 	return 0;
 }
 
-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;
-	}
-	pr_info("factory set disable param to %d\n", charging_disabled);
-	if (chip)
-		qpnp_chg_charge_dis(chip, charging_disabled);
-	return 0;
-}
-module_param_call(disabled, qpnp_chg_set_disable_status_param, param_get_bool,
-					&charging_disabled, 0644);
-
 #define QPNP_CHG_VINMIN_MIN_MV		3400
 #define QPNP_CHG_VINMIN_HIGH_MIN_MV	5600
 #define QPNP_CHG_VINMIN_HIGH_MIN_VAL	0x2B
@@ -1145,6 +1136,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");
@@ -1255,6 +1250,7 @@
 	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;
 
@@ -1276,8 +1272,9 @@
 	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_force_run_on_batt(chip, chip->charging_disabled);
+
 	pr_info("Probe success !\n");
 	return 0;
 
@@ -1293,7 +1290,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/smb137c-charger.c b/drivers/power/smb137c-charger.c
new file mode 100644
index 0000000..fc3277f
--- /dev/null
+++ b/drivers/power/smb137c-charger.c
@@ -0,0 +1,1418 @@
+/* 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/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/power_supply.h>
+#include <linux/slab.h>
+
+struct smb137c_chip {
+	struct i2c_client	*client;
+	struct power_supply	psy;
+	struct power_supply	*usb_psy;
+	struct mutex		lock;
+	int			charge_current_limit_ua;
+	int			input_current_limit_ua;
+	int			term_current_ua;
+	bool			charging_enabled;
+	bool			otg_mode_enabled;
+	bool			charging_allowed;
+	bool			usb_suspend_enabled;
+};
+
+struct input_current_config {
+	int			current_limit_ua;
+	u8			cmd_b_reg;
+	u8			var_func_reg;
+	u8			input_cur_reg;
+};
+
+struct term_current_config {
+	int			term_current_ua;
+	u8			charge_cur_reg;
+};
+
+#define INPUT_CURRENT(_current_limit_ua, _cmd_b_reg, _var_func_reg, \
+			_input_cur_reg) \
+	{ \
+		.current_limit_ua	= _current_limit_ua, \
+		.cmd_b_reg		= _cmd_b_reg, \
+		.var_func_reg		= _var_func_reg, \
+		.input_cur_reg		= _input_cur_reg, \
+	}
+
+#define CHARGE_CURRENT_REG		0x00
+#define CHARGE_CURRENT_FAST_CHG_MASK	0xE0
+#define CHARGE_CURRENT_FAST_CHG_SHIFT	5
+#define CHARGE_CURRENT_PRE_CHG_MASK	0x18
+#define CHARGE_CURRENT_PRE_CHG_SHIFT	3
+#define CHARGE_CURRENT_TERM_CUR_MASK	0x06
+
+#define INPUT_CURRENT_REG		0x01
+#define INPUT_CURRENT_LIMIT_MASK	0xE0
+
+#define FLOAT_VOLTAGE_REG		0x02
+#define FLOAT_VOLTAGE_MASK		0x7F
+#define FLOAT_VOLTAGE_SHIFT		0
+
+#define CTRL_A_REG			0x03
+#define CTRL_A_AUTO_RECHARGE_MASK	0x80
+#define CTRL_A_AUTO_RECHARGE_ENABLED	0x00
+#define CTRL_A_AUTO_RECHARGE_DISABLED	0x80
+#define CTRL_A_TERM_CUR_MASK		0x40
+#define CTRL_A_TERM_CUR_ENABLED		0x00
+#define CTRL_A_TERM_CUR_DISABLED	0x40
+#define CTRL_A_THRESH_VOLTAGE_MASK	0x38
+#define CTRL_A_THRESH_VOLTAGE_SHIFT	3
+#define CTRL_A_VOUTL_MASK		0x02
+#define CTRL_A_VOUTL_4250MV		0x00
+#define CTRL_A_VOUTL_4460MV		0x02
+#define CTRL_A_THERM_MONITOR_MASK	0x01
+#define CTRL_A_THERM_MONITOR_ENABLED	0x01
+#define CTRL_A_THERM_MONITOR_DISABLED	0x00
+
+#define PIN_CTRL_REG			0x05
+#define PIN_CTRL_DEAD_BATT_CHG_MASK	0x80
+#define PIN_CTRL_DEAD_BATT_CHG_ENABLED	0x80
+#define PIN_CTRL_DEAD_BATT_CHG_DISABLED	0x00
+#define PIN_CTRL_OTG_LBR_MASK		0x20
+#define PIN_CTRL_OTG			0x00
+#define PIN_CTRL_LBR			0x20
+#define PIN_CTRL_USB_CUR_LIMIT_MASK	0x10
+#define PIN_CTRL_USB_CUR_LIMIT_REG	0x00
+#define PIN_CTRL_USB_CUR_LIMIT_PIN	0x10
+#define PIN_CTRL_CHG_EN_MASK		0x0C
+#define PIN_CTRL_CHG_EN_REG_LOW		0x00
+#define PIN_CTRL_CHG_EN_REG_HIGH	0x04
+#define PIN_CTRL_CHG_EN_PIN_LOW		0x08
+#define PIN_CTRL_CHG_EN_PIN_HIGH	0x0C
+#define PIN_CTRL_OTG_CTRL_MASK		0x02
+#define PIN_CTRL_OTG_CTRL_REG		0x00
+#define PIN_CTRL_OTG_CTRL_PIN		0x02
+
+#define OTG_CTRL_REG			0x06
+#define OTG_CTRL_BMD_MASK		0x80
+#define OTG_CTRL_BMD_ENABLED		0x80
+#define OTG_CTRL_BMD_DISABLED		0x00
+#define OTG_CTRL_AUTO_RECHARGE_MASK	0x40
+#define OTG_CTRL_AUTO_RECHARGE_75MV	0x00
+#define OTG_CTRL_AUTO_RECHARGE_120MV	0x40
+
+#define TEMP_MON_REG			0x08
+#define TEMP_MON_THERM_CURRENT_MASK	0xC0
+#define TEMP_MON_THERM_CURRENT_SHIFT	6
+#define TEMP_MON_TEMP_LOW_MASK		0x38
+#define TEMP_MON_TEMP_LOW_SHIFT		3
+#define TEMP_MON_TEMP_HIGH_MASK		0x07
+#define TEMP_MON_TEMP_HIGH_SHIFT	0
+
+#define SAFETY_TIMER_REG		0x09
+#define SAFETY_TIMER_RELOAD_MASK	0x40
+#define SAFETY_TIMER_RELOAD_ENABLED	0x40
+#define SAFETY_TIMER_RELOAD_DISABLED	0x00
+#define SAFETY_TIMER_CHG_TIMEOUT_MASK	0x0C
+#define SAFETY_TIMER_CHG_TIMEOUT_SHIFT	2
+#define SAFETY_TIMER_PRE_CHG_TIME_MASK	0x03
+#define SAFETY_TIMER_PRE_CHG_TIME_SHIFT	0
+
+#define VAR_FUNC_REG			0x0C
+#define VAR_FUNC_USB_MODE_MASK		0x80
+#define VAR_FUNC_USB_SUSPEND_CTRL_MASK	0x20
+#define VAR_FUNC_USB_SUSPEND_CTRL_REG	0x00
+#define VAR_FUNC_USB_SUSPEND_CTRL_PIN	0x20
+#define VAR_FUNC_BMD_MASK		0x0C
+#define VAR_FUNC_BMD_DISABLED		0x00
+#define VAR_FUNC_BMD_ALGO_PERIODIC	0x04
+#define VAR_FUNC_BMD_ALGO		0x08
+#define VAR_FUNC_BMD_THERM		0x0C
+
+#define CMD_A_REG			0x30
+#define CMD_A_VOLATILE_WRITE_MASK	0x80
+#define CMD_A_VOLATILE_WRITE_ALLOW	0x80
+#define CMD_A_VOLATILE_WRITE_DISALLOW	0x00
+#define CMD_A_FAST_CHG_MASK		0x40
+#define CMD_A_FAST_CHG_ALLOW		0x40
+#define CMD_A_FAST_CHG_DISALLOW		0x00
+#define CMD_A_OTG_MASK			0x10
+#define CMD_A_OTG_ENABLED		0x10
+#define CMD_A_OTG_DISABLED		0x00
+#define CMD_A_USB_SUSPEND_MASK		0x04
+#define CMD_A_USB_SUSPEND_DISABLED	0x00
+#define CMD_A_USB_SUSPEND_ENABLED	0x04
+#define CMD_A_CHARGING_MASK		0x02
+#define CMD_A_CHARGING_ENABLED		0x00
+#define CMD_A_CHARGING_DISABLED		0x02
+
+#define CMD_B_REG			0x31
+#define CMD_B_USB_MODE_MASK		0x03
+
+#define DEV_ID_REG			0x33
+#define DEV_ID_PART_MASK		0x80
+#define DEV_ID_PART_SMB137C		0x00
+#define DEV_ID_GUI_REV_MASK		0x70
+#define DEV_ID_GUI_REV_SHIFT		4
+#define DEV_ID_SILICON_REV_MASK		0x0F
+#define DEV_ID_SILICON_REV_SHIFT	0
+
+#define IRQ_STAT_A_REG			0x35
+#define IRQ_STAT_A_BATT_HOT		0x40
+#define IRQ_STAT_A_BATT_COLD		0x10
+
+#define IRQ_STAT_B_REG			0x36
+#define IRQ_STAT_B_BATT_OVERVOLT	0x40
+#define IRQ_STAT_B_BATT_MISSING		0x10
+#define IRQ_STAT_B_BATT_UNDERVOLT	0x04
+
+#define STAT_C_REG			0x3D
+#define STAT_C_CHG_ERROR		0x40
+#define STAT_C_VBATT_LEVEL_BELOW_2P1V	0x10
+#define STAT_C_CHG_STAT_MASK		0x06
+#define STAT_C_CHG_STAT_SHIFT		1
+#define STAT_C_CHG_ENABLED		0x01
+
+/* Charge status register values */
+enum smb137c_charge_status {
+	CHARGE_STAT_NO_CHG	= 0,
+	CHARGE_STAT_PRE_CHG	= 1,
+	CHARGE_STAT_FAST_CHG	= 2,
+	CHARGE_STAT_TAPER_CHG	= 3,
+};
+
+#define PRE_CHARGE_CURRENT_MIN_UA	50000
+#define PRE_CHARGE_CURRENT_MAX_UA	200000
+#define PRE_CHARGE_CURRENT_STEP_UA	50000
+
+#define FLOAT_VOLTAGE_MIN_UV		3460000
+#define FLOAT_VOLTAGE_MAX_UV		4730000
+#define FLOAT_VOLTAGE_STEP_UV		10000
+
+#define PRE_CHG_THRESH_VOLTAGE_MIN_UV	2400000
+#define PRE_CHG_THRESH_VOLTAGE_MAX_UV	3100000
+#define PRE_CHG_THRESH_VOLTAGE_STEP_UV	100000
+
+#define USB_MIN_CURRENT_UA		100000
+
+static int smb137c_read_reg(struct smb137c_chip *chip, u8 reg, u8 *val)
+{
+	int rc;
+
+	rc = i2c_smbus_read_byte_data(chip->client, reg);
+	if (rc < 0) {
+		pr_err("i2c_smbus_read_byte_data failed. reg=0x%02X, rc=%d\n",
+			reg, rc);
+	} else {
+		*val = rc;
+		rc = 0;
+		pr_debug("read(0x%02X)=0x%02X\n", reg, *val);
+	}
+
+	return rc;
+}
+
+static int smb137c_write_reg(struct smb137c_chip *chip, u8 reg, u8 val)
+{
+	int rc;
+
+	rc = i2c_smbus_write_byte_data(chip->client, reg, val);
+	if (rc < 0)
+		pr_err("i2c_smbus_write_byte_data failed. reg=0x%02X, rc=%d\n",
+			reg, rc);
+	else
+		pr_debug("write(0x%02X)=0x%02X\n", reg, val);
+
+	return rc;
+}
+
+static int smb137c_masked_write_reg(struct smb137c_chip *chip, u8 reg, u8 mask,
+					u8 val)
+{
+	u8 reg_val;
+	int rc;
+
+	pr_debug("masked write(0x%02X), mask=0x%02X, value=0x%02X\n", reg, mask,
+		val);
+
+	rc = smb137c_read_reg(chip, reg, &reg_val);
+	if (rc < 0)
+		return rc;
+
+	val = (reg_val & ~mask) | (val & mask);
+
+	if (val != reg_val)
+		rc = smb137c_write_reg(chip, reg, val);
+
+	return rc;
+}
+
+static int smb137c_enable_charging(struct smb137c_chip *chip)
+{
+	int rc = 0;
+
+	chip->charging_allowed = true;
+
+	if (!chip->charging_enabled && chip->charge_current_limit_ua > 0) {
+		rc = smb137c_masked_write_reg(chip, CMD_A_REG,
+			CMD_A_CHARGING_MASK, CMD_A_CHARGING_ENABLED);
+		if (!rc)
+			chip->charging_enabled = true;
+	}
+
+	if (!rc)
+		dev_dbg(&chip->client->dev, "%s\n", __func__);
+
+	return rc;
+}
+
+static int smb137c_disable_charging(struct smb137c_chip *chip)
+{
+	int rc = 0;
+
+	chip->charging_allowed = false;
+
+	if (chip->charging_enabled) {
+		rc = smb137c_masked_write_reg(chip, CMD_A_REG,
+			CMD_A_CHARGING_MASK, CMD_A_CHARGING_DISABLED);
+		if (!rc)
+			chip->charging_enabled = false;
+	}
+
+	if (!rc)
+		dev_dbg(&chip->client->dev, "%s\n", __func__);
+
+	return rc;
+}
+
+static int smb137c_enable_otg_mode(struct smb137c_chip *chip)
+{
+	int rc = 0;
+
+	if (!chip->otg_mode_enabled) {
+		rc = smb137c_masked_write_reg(chip, CMD_A_REG, CMD_A_OTG_MASK,
+					CMD_A_OTG_ENABLED);
+		if (!rc)
+			chip->otg_mode_enabled = true;
+	}
+
+	if (!rc)
+		dev_dbg(&chip->client->dev, "%s\n", __func__);
+
+	return rc;
+}
+
+static int smb137c_disable_otg_mode(struct smb137c_chip *chip)
+{
+	int rc = 0;
+
+	if (chip->otg_mode_enabled) {
+		rc = smb137c_masked_write_reg(chip, CMD_A_REG, CMD_A_OTG_MASK,
+					CMD_A_OTG_DISABLED);
+		if (!rc)
+			chip->otg_mode_enabled = false;
+	}
+
+	if (!rc)
+		dev_dbg(&chip->client->dev, "%s\n", __func__);
+
+	return rc;
+}
+
+static int smb137c_enable_usb_suspend(struct smb137c_chip *chip)
+{
+	int rc = 0;
+
+	if (!chip->usb_suspend_enabled) {
+		rc = smb137c_masked_write_reg(chip, CMD_A_REG,
+			CMD_A_USB_SUSPEND_MASK, CMD_A_USB_SUSPEND_ENABLED);
+		if (!rc)
+			chip->usb_suspend_enabled = true;
+	}
+
+	if (!rc)
+		dev_dbg(&chip->client->dev, "%s\n", __func__);
+
+	return rc;
+}
+
+static int smb137c_disable_usb_suspend(struct smb137c_chip *chip)
+{
+	int rc = 0;
+
+	if (chip->input_current_limit_ua > 0 && chip->usb_suspend_enabled) {
+		rc = smb137c_masked_write_reg(chip, CMD_A_REG,
+			CMD_A_USB_SUSPEND_MASK, CMD_A_USB_SUSPEND_DISABLED);
+		if (!rc)
+			chip->usb_suspend_enabled = false;
+	}
+
+	if (!rc)
+		dev_dbg(&chip->client->dev, "%s\n", __func__);
+
+	return rc;
+}
+
+static struct input_current_config supported_input_current[] = {
+	INPUT_CURRENT(100000,  0x00, 0x00, 0x00),
+	INPUT_CURRENT(150000,  0x00, 0x80, 0x00),
+	INPUT_CURRENT(500000,  0x02, 0x00, 0x00),
+	INPUT_CURRENT(700000,  0x01, 0x00, 0x00),
+	INPUT_CURRENT(800000,  0x01, 0x00, 0x20),
+	INPUT_CURRENT(900000,  0x01, 0x00, 0x40),
+	INPUT_CURRENT(1000000, 0x01, 0x00, 0x60),
+	INPUT_CURRENT(1100000, 0x01, 0x00, 0x80),
+	INPUT_CURRENT(1200000, 0x01, 0x00, 0xA0),
+	INPUT_CURRENT(1300000, 0x01, 0x00, 0xC0),
+	INPUT_CURRENT(1500000, 0x01, 0x00, 0xE0),
+};
+
+static int smb137c_set_usb_input_current_limit(struct smb137c_chip *chip,
+					int current_limit_ua)
+{
+	struct input_current_config *config = NULL;
+	int rc = 0;
+	int i;
+
+	for (i = ARRAY_SIZE(supported_input_current) - 1; i >= 0; i--) {
+		if (current_limit_ua
+		    >= supported_input_current[i].current_limit_ua) {
+			config = &supported_input_current[i];
+			break;
+		}
+	}
+
+	if (config) {
+		if (chip->input_current_limit_ua != config->current_limit_ua) {
+			rc = smb137c_masked_write_reg(chip, INPUT_CURRENT_REG,
+			       INPUT_CURRENT_LIMIT_MASK, config->input_cur_reg);
+			if (rc)
+				return rc;
+
+			rc = smb137c_masked_write_reg(chip, VAR_FUNC_REG,
+				VAR_FUNC_USB_MODE_MASK, config->var_func_reg);
+			if (rc)
+				return rc;
+
+			rc = smb137c_masked_write_reg(chip, CMD_B_REG,
+				CMD_B_USB_MODE_MASK, config->cmd_b_reg);
+			if (rc)
+				return rc;
+
+			chip->input_current_limit_ua = config->current_limit_ua;
+		}
+
+		rc = smb137c_disable_usb_suspend(chip);
+	} else {
+		chip->input_current_limit_ua = 0;
+
+		rc = smb137c_enable_usb_suspend(chip);
+	}
+
+	if (!rc)
+		dev_dbg(&chip->client->dev, "%s: current=%d uA\n", __func__,
+			chip->input_current_limit_ua);
+
+	return rc;
+}
+
+static int fast_charge_current_ua[] = {
+	 500000,
+	 650000,
+	 750000,
+	 850000,
+	 950000,
+	1100000,
+	1300000,
+	1500000,
+};
+
+static int smb137c_set_charge_current_limit(struct smb137c_chip *chip,
+					int current_limit_ua)
+{
+	int fast_charge_limit_ua = 0;
+	int rc = 0;
+	u8 val = 0;
+	int i;
+
+	for (i = ARRAY_SIZE(fast_charge_current_ua) - 1; i >= 0; i--) {
+		if (current_limit_ua >= fast_charge_current_ua[i]) {
+			val = i << CHARGE_CURRENT_FAST_CHG_SHIFT;
+			fast_charge_limit_ua = fast_charge_current_ua[i];
+			break;
+		}
+	}
+
+	if (fast_charge_limit_ua
+	    && chip->charge_current_limit_ua != fast_charge_limit_ua)
+		rc = smb137c_masked_write_reg(chip, CHARGE_CURRENT_REG,
+					CHARGE_CURRENT_FAST_CHG_MASK, val);
+	else if (fast_charge_limit_ua == 0)
+		rc = smb137c_disable_charging(chip);
+
+	chip->charge_current_limit_ua = fast_charge_limit_ua;
+
+	if (!rc)
+		dev_dbg(&chip->client->dev, "%s: current=%d uA\n", __func__,
+			fast_charge_limit_ua);
+
+	return rc;
+}
+
+static int smb137c_get_charge_current_limit(struct smb137c_chip *chip)
+{
+	int fast_charge_limit_ua = 0;
+	u8 val = 0;
+	int rc, i;
+
+	rc = smb137c_read_reg(chip, CHARGE_CURRENT_REG, &val);
+	if (rc)
+		return rc;
+
+	i = (val & CHARGE_CURRENT_FAST_CHG_MASK)
+		>> CHARGE_CURRENT_FAST_CHG_SHIFT;
+
+	if (i >= 0 && i < ARRAY_SIZE(fast_charge_current_ua))
+		fast_charge_limit_ua = fast_charge_current_ua[i];
+
+	dev_dbg(&chip->client->dev, "%s: current=%d uA\n", __func__,
+		fast_charge_limit_ua);
+
+	return fast_charge_limit_ua;
+}
+
+static struct term_current_config term_current_ua[] = {
+	{ 35000, 0x06},
+	{ 50000, 0x00},
+	{100000, 0x02},
+	{150000, 0x04},
+};
+
+static int smb137c_set_term_current(struct smb137c_chip *chip,
+					int current_limit_ua)
+{
+	int term_current_limit_ua = 0;
+	int rc = 0;
+	u8 val = 0;
+	int i;
+
+	for (i = ARRAY_SIZE(term_current_ua) - 1; i >= 0; i--) {
+		if (current_limit_ua >= term_current_ua[i].term_current_ua) {
+			val = term_current_ua[i].charge_cur_reg;
+			term_current_limit_ua
+				= term_current_ua[i].term_current_ua;
+			break;
+		}
+	}
+
+	if (term_current_limit_ua) {
+		rc = smb137c_masked_write_reg(chip, CHARGE_CURRENT_REG,
+				CHARGE_CURRENT_TERM_CUR_MASK, val);
+		if (rc)
+			return rc;
+		rc = smb137c_masked_write_reg(chip, CTRL_A_REG,
+				CTRL_A_TERM_CUR_MASK, CTRL_A_TERM_CUR_ENABLED);
+	} else {
+		rc = smb137c_masked_write_reg(chip, CTRL_A_REG,
+				CTRL_A_TERM_CUR_MASK, CTRL_A_TERM_CUR_DISABLED);
+	}
+
+	if (!rc)
+		dev_dbg(&chip->client->dev, "%s: current=%d uA\n", __func__,
+			term_current_limit_ua);
+
+	return rc;
+}
+
+static int smb137c_set_pre_charge_current_limit(struct smb137c_chip *chip,
+					int current_limit_ua)
+{
+	int setpoint, rc;
+	u8 val;
+
+	if (current_limit_ua < PRE_CHARGE_CURRENT_MIN_UA ||
+	    current_limit_ua > PRE_CHARGE_CURRENT_MAX_UA) {
+		dev_err(&chip->client->dev, "%s: current limit out of bounds: %d\n",
+			__func__, current_limit_ua);
+		return -EINVAL;
+	}
+
+	setpoint = (current_limit_ua - PRE_CHARGE_CURRENT_MIN_UA)
+			/ PRE_CHARGE_CURRENT_STEP_UA;
+	val = setpoint << CHARGE_CURRENT_PRE_CHG_SHIFT;
+
+	rc = smb137c_masked_write_reg(chip, CHARGE_CURRENT_REG,
+			CHARGE_CURRENT_PRE_CHG_MASK, val);
+
+	if (!rc)
+		dev_dbg(&chip->client->dev, "%s: current=%d uA\n", __func__,
+			setpoint * PRE_CHARGE_CURRENT_STEP_UA
+			+ PRE_CHARGE_CURRENT_MIN_UA);
+
+	return rc;
+}
+
+static int smb137c_set_float_voltage(struct smb137c_chip *chip, int voltage_uv)
+{
+	int setpoint, rc;
+	u8 val;
+
+	if (voltage_uv < FLOAT_VOLTAGE_MIN_UV ||
+	    voltage_uv > FLOAT_VOLTAGE_MAX_UV) {
+		dev_err(&chip->client->dev, "%s: voltage out of bounds: %d\n",
+			__func__, voltage_uv);
+		return -EINVAL;
+	}
+
+	setpoint = (voltage_uv - FLOAT_VOLTAGE_MIN_UV) / FLOAT_VOLTAGE_STEP_UV;
+	val = setpoint << FLOAT_VOLTAGE_SHIFT;
+
+	rc = smb137c_masked_write_reg(chip, FLOAT_VOLTAGE_REG,
+			FLOAT_VOLTAGE_MASK, val);
+
+	if (!rc)
+		dev_dbg(&chip->client->dev, "%s: voltage=%d uV\n", __func__,
+		       setpoint * FLOAT_VOLTAGE_STEP_UV + FLOAT_VOLTAGE_MIN_UV);
+
+	return rc;
+}
+
+static int smb137c_set_pre_charge_threshold_voltage(struct smb137c_chip *chip,
+							int voltage_uv)
+{
+	int setpoint, rc;
+	u8 val;
+
+	if (voltage_uv < PRE_CHG_THRESH_VOLTAGE_MIN_UV ||
+	    voltage_uv > PRE_CHG_THRESH_VOLTAGE_MAX_UV) {
+		dev_err(&chip->client->dev, "%s: voltage out of bounds: %d\n",
+			__func__, voltage_uv);
+		return -EINVAL;
+	}
+
+	setpoint = (voltage_uv - PRE_CHG_THRESH_VOLTAGE_MIN_UV)
+			/ PRE_CHG_THRESH_VOLTAGE_STEP_UV;
+	val = setpoint << CTRL_A_THRESH_VOLTAGE_SHIFT;
+
+	rc = smb137c_masked_write_reg(chip, CTRL_A_REG,
+			CTRL_A_THRESH_VOLTAGE_MASK, val);
+
+	if (!rc)
+		dev_dbg(&chip->client->dev, "%s: voltage=%d uV\n", __func__,
+		       setpoint * PRE_CHG_THRESH_VOLTAGE_STEP_UV
+		       + PRE_CHG_THRESH_VOLTAGE_MIN_UV);
+
+	return rc;
+}
+
+static int smb137c_set_recharge_threshold_voltage(struct smb137c_chip *chip,
+							int voltage_uv)
+{
+	int rc;
+	u8 val;
+
+	if (voltage_uv == 75000) {
+		val = OTG_CTRL_AUTO_RECHARGE_75MV;
+	} else if (voltage_uv == 120000) {
+		val = OTG_CTRL_AUTO_RECHARGE_120MV;
+	} else {
+		dev_err(&chip->client->dev, "%s: voltage out of bounds: %d\n",
+			__func__, voltage_uv);
+		return -EINVAL;
+	}
+
+	rc = smb137c_masked_write_reg(chip, OTG_CTRL_REG,
+			OTG_CTRL_AUTO_RECHARGE_MASK, val);
+
+	if (!rc)
+		dev_dbg(&chip->client->dev, "%s: voltage=%d uV\n", __func__,
+		       voltage_uv);
+
+	return rc;
+}
+
+static int smb137c_set_system_voltage(struct smb137c_chip *chip, int voltage_uv)
+{
+	int rc;
+	u8 val;
+
+	if (voltage_uv == 4250000) {
+		val = CTRL_A_VOUTL_4250MV;
+	} else if (voltage_uv == 4460000) {
+		val = CTRL_A_VOUTL_4460MV;
+	} else {
+		dev_err(&chip->client->dev, "%s: voltage out of bounds: %d\n",
+			__func__, voltage_uv);
+		return -EINVAL;
+	}
+
+	rc = smb137c_masked_write_reg(chip, CTRL_A_REG, CTRL_A_VOUTL_MASK, val);
+
+	if (!rc)
+		dev_dbg(&chip->client->dev, "%s: voltage=%d uV\n", __func__,
+		       voltage_uv);
+
+	return rc;
+}
+
+static int charging_timeout[] = {
+	 382,
+	 764,
+	 1527,
+};
+
+static int smb137c_set_charging_timeout(struct smb137c_chip *chip, int timeout)
+{
+	int timeout_chosen = 0;
+	u8 val = 3 << SAFETY_TIMER_CHG_TIMEOUT_SHIFT;
+	int rc, i;
+
+	for (i = ARRAY_SIZE(charging_timeout) - 1; i >= 0; i--) {
+		if (timeout >= charging_timeout[i]) {
+			val = i << SAFETY_TIMER_CHG_TIMEOUT_SHIFT;
+			timeout_chosen = charging_timeout[i];
+			break;
+		}
+	}
+
+	rc = smb137c_masked_write_reg(chip, SAFETY_TIMER_REG,
+			SAFETY_TIMER_CHG_TIMEOUT_MASK, val);
+
+	if (!rc)
+		dev_dbg(&chip->client->dev, "%s: timeout=%d min\n", __func__,
+			timeout_chosen);
+
+	return rc;
+}
+
+static int pre_charge_timeout[] = {
+	 48,
+	 95,
+	 191,
+};
+
+static int smb137c_set_pre_charge_timeout(struct smb137c_chip *chip,
+					int timeout)
+{
+	int timeout_chosen = 0;
+	u8 val = 3 << SAFETY_TIMER_PRE_CHG_TIME_SHIFT;
+	int rc, i;
+
+	for (i = ARRAY_SIZE(pre_charge_timeout) - 1; i >= 0; i--) {
+		if (timeout >= pre_charge_timeout[i]) {
+			val = i << SAFETY_TIMER_PRE_CHG_TIME_SHIFT;
+			timeout_chosen = pre_charge_timeout[i];
+			break;
+		}
+	}
+
+	rc = smb137c_masked_write_reg(chip, SAFETY_TIMER_REG,
+			SAFETY_TIMER_PRE_CHG_TIME_MASK, val);
+
+	if (!rc)
+		dev_dbg(&chip->client->dev, "%s: timeout=%d min\n", __func__,
+			timeout_chosen);
+
+	return rc;
+}
+
+static int thermistor_current[] = {
+	100,
+	40,
+	20,
+	10,
+};
+
+static int smb137c_set_thermistor_current(struct smb137c_chip *chip,
+					int current_ua)
+{
+	bool found = false;
+	u8 val = 0;
+	int rc, i;
+
+	for (i = 0; i < ARRAY_SIZE(thermistor_current); i++) {
+		if (current_ua == thermistor_current[i]) {
+			found = true;
+			val = i << TEMP_MON_THERM_CURRENT_SHIFT;
+		}
+	}
+
+	if (!found) {
+		dev_err(&chip->client->dev, "%s: current out of bounds: %d\n",
+			__func__, current_ua);
+		return -EINVAL;
+	}
+
+	rc = smb137c_masked_write_reg(chip, TEMP_MON_REG,
+			TEMP_MON_THERM_CURRENT_MASK, val);
+
+	if (!rc)
+		dev_dbg(&chip->client->dev, "%s: current=%d uA\n", __func__,
+			current_ua);
+
+	return 0;
+}
+
+static int smb137c_set_temperature_low_limit(struct smb137c_chip *chip,
+					int value)
+{
+	int rc;
+
+	if (value < 0 || value > 7) {
+		dev_err(&chip->client->dev, "%s: temperature value out of bounds: %d\n",
+			__func__, value);
+		return -EINVAL;
+	}
+
+	rc = smb137c_masked_write_reg(chip, TEMP_MON_REG,
+		TEMP_MON_TEMP_LOW_MASK, value << TEMP_MON_TEMP_LOW_SHIFT);
+
+	if (!rc)
+		dev_dbg(&chip->client->dev, "%s: temperature value=%d\n",
+			__func__, value);
+
+	return rc;
+}
+
+static int smb137c_set_temperature_high_limit(struct smb137c_chip *chip,
+					int value)
+{
+	int rc;
+
+	if (value < 0 || value > 7) {
+		dev_err(&chip->client->dev, "%s: temperature value out of bounds: %d\n",
+			__func__, value);
+		return -EINVAL;
+	}
+
+	rc = smb137c_masked_write_reg(chip, TEMP_MON_REG,
+		TEMP_MON_TEMP_HIGH_MASK, value << TEMP_MON_TEMP_HIGH_SHIFT);
+
+	if (!rc)
+		dev_dbg(&chip->client->dev, "%s: temperature value=%d\n",
+			__func__, value);
+
+	return rc;
+}
+
+static int charge_status_type_map[] = {
+	[CHARGE_STAT_NO_CHG]	= POWER_SUPPLY_CHARGE_TYPE_NONE,
+	[CHARGE_STAT_PRE_CHG]	= POWER_SUPPLY_CHARGE_TYPE_TRICKLE,
+	[CHARGE_STAT_FAST_CHG]	= POWER_SUPPLY_CHARGE_TYPE_FAST,
+	[CHARGE_STAT_TAPER_CHG]	= POWER_SUPPLY_CHARGE_TYPE_FAST,
+};
+
+static const char * const charge_status_name[] = {
+	[CHARGE_STAT_NO_CHG]	= "none",
+	[CHARGE_STAT_PRE_CHG]	= "pre-charge",
+	[CHARGE_STAT_FAST_CHG]	= "fast-charge",
+	[CHARGE_STAT_TAPER_CHG]	= "taper-charge",
+};
+
+static int smb137c_get_property_status(struct smb137c_chip *chip)
+{
+	int status = POWER_SUPPLY_STATUS_DISCHARGING;
+	enum smb137c_charge_status charging_status;
+	bool charging_enabled;
+	bool charging_error;
+	int rc;
+	u8 val;
+
+	rc = smb137c_read_reg(chip, STAT_C_REG, &val);
+	if (rc)
+		return POWER_SUPPLY_STATUS_UNKNOWN;
+
+	charging_enabled = val & STAT_C_CHG_ENABLED;
+	charging_error = val & STAT_C_CHG_ERROR;
+	charging_status = (val & STAT_C_CHG_STAT_MASK) >> STAT_C_CHG_STAT_SHIFT;
+
+	if (charging_enabled && !charging_error
+	    && charging_status != CHARGE_STAT_NO_CHG)
+		status = POWER_SUPPLY_STATUS_CHARGING;
+
+	dev_dbg(&chip->client->dev, "%s: status=%s\n", __func__,
+		(status == POWER_SUPPLY_STATUS_CHARGING ? "charging"
+			: "discharging"));
+
+	return status;
+}
+
+static int smb137c_get_property_battery_present(struct smb137c_chip *chip)
+{
+	int rc;
+	u8 val;
+
+	rc = smb137c_read_reg(chip, IRQ_STAT_B_REG, &val);
+	if (rc || (val & IRQ_STAT_B_BATT_MISSING))
+		return 0;
+
+	/* Treat battery voltage less than 2.1 V as battery not present. */
+	rc = smb137c_read_reg(chip, STAT_C_REG, &val);
+	if (rc || (val & STAT_C_VBATT_LEVEL_BELOW_2P1V))
+		return 0;
+
+	return 1;
+}
+
+static int smb137c_get_property_battery_health(struct smb137c_chip *chip)
+{
+	int rc;
+	u8 val;
+
+	/* The health of a disconnected battery is unknown. */
+	if (!smb137c_get_property_battery_present(chip))
+		return POWER_SUPPLY_HEALTH_UNKNOWN;
+
+	rc = smb137c_read_reg(chip, IRQ_STAT_B_REG, &val);
+	if (rc)
+		return POWER_SUPPLY_HEALTH_UNKNOWN;
+
+	if (val & IRQ_STAT_B_BATT_OVERVOLT)
+		return POWER_SUPPLY_HEALTH_OVERVOLTAGE;
+	else if (val & IRQ_STAT_B_BATT_UNDERVOLT)
+		return POWER_SUPPLY_HEALTH_DEAD;
+
+	rc = smb137c_read_reg(chip, IRQ_STAT_A_REG, &val);
+	if (rc)
+		return POWER_SUPPLY_HEALTH_UNKNOWN;
+
+	if (val & IRQ_STAT_A_BATT_HOT)
+		return POWER_SUPPLY_HEALTH_OVERHEAT;
+	else if (val & IRQ_STAT_A_BATT_COLD)
+		return POWER_SUPPLY_HEALTH_COLD;
+
+	return POWER_SUPPLY_HEALTH_GOOD;
+}
+
+static int smb137c_get_property_charge_type(struct smb137c_chip *chip)
+{
+	enum smb137c_charge_status status;
+	int charge_type = POWER_SUPPLY_CHARGE_TYPE_NONE;
+	bool charging_enabled;
+	bool charging_error;
+	int rc;
+	u8 val;
+
+	rc = smb137c_read_reg(chip, STAT_C_REG, &val);
+	if (rc)
+		return POWER_SUPPLY_CHARGE_TYPE_UNKNOWN;
+
+	charging_enabled = val & STAT_C_CHG_ENABLED;
+	charging_error = val & STAT_C_CHG_ERROR;
+	status = (val & STAT_C_CHG_STAT_MASK) >> STAT_C_CHG_STAT_SHIFT;
+
+	if (!charging_enabled) {
+		dev_dbg(&chip->client->dev, "%s: not charging\n", __func__);
+	} else if (charging_error) {
+		dev_warn(&chip->client->dev, "%s: charger error detected\n",
+			__func__);
+	} else {
+		charge_type = charge_status_type_map[status];
+	}
+
+	dev_dbg(&chip->client->dev, "%s: charging status=%s\n", __func__,
+		charge_status_name[status]);
+
+	return charge_type;
+}
+
+static enum power_supply_property smb137c_power_properties[] = {
+	POWER_SUPPLY_PROP_STATUS,
+	POWER_SUPPLY_PROP_HEALTH,
+	POWER_SUPPLY_PROP_PRESENT,
+	POWER_SUPPLY_PROP_CHARGING_ENABLED,
+	POWER_SUPPLY_PROP_CHARGE_TYPE,
+	POWER_SUPPLY_PROP_CURRENT_MAX,
+	POWER_SUPPLY_PROP_TECHNOLOGY,
+	POWER_SUPPLY_PROP_MODEL_NAME,
+	POWER_SUPPLY_PROP_MANUFACTURER,
+};
+
+static int smb137c_property_is_writeable(struct power_supply *psy,
+						enum power_supply_property psp)
+{
+	switch (psp) {
+	case POWER_SUPPLY_PROP_CHARGING_ENABLED:
+	case POWER_SUPPLY_PROP_CURRENT_MAX:
+		return 1;
+	default:
+		break;
+	}
+
+	return 0;
+}
+
+static int smb137c_power_set_property(struct power_supply *psy,
+				  enum power_supply_property psp,
+				  const union power_supply_propval *val)
+{
+	struct smb137c_chip *chip = container_of(psy, struct smb137c_chip, psy);
+
+	mutex_lock(&chip->lock);
+
+	switch (psp) {
+	case POWER_SUPPLY_PROP_CHARGING_ENABLED:
+		if (val->intval)
+			smb137c_enable_charging(chip);
+		else
+			smb137c_disable_charging(chip);
+		break;
+	case POWER_SUPPLY_PROP_CURRENT_MAX:
+		smb137c_set_charge_current_limit(chip, val->intval);
+		break;
+	default:
+		mutex_unlock(&chip->lock);
+		return -EINVAL;
+	}
+
+	mutex_unlock(&chip->lock);
+
+	power_supply_changed(&chip->psy);
+	return 0;
+}
+
+static int smb137c_power_get_property(struct power_supply *psy,
+				       enum power_supply_property psp,
+				       union power_supply_propval *val)
+{
+	struct smb137c_chip *chip = container_of(psy, struct smb137c_chip, psy);
+
+	mutex_lock(&chip->lock);
+
+	switch (psp) {
+	case POWER_SUPPLY_PROP_STATUS:
+		val->intval = smb137c_get_property_status(chip);
+		break;
+	case POWER_SUPPLY_PROP_HEALTH:
+		val->intval = smb137c_get_property_battery_health(chip);
+		break;
+	case POWER_SUPPLY_PROP_PRESENT:
+		val->intval = smb137c_get_property_battery_present(chip);
+		break;
+	case POWER_SUPPLY_PROP_CHARGING_ENABLED:
+		val->intval = chip->charging_enabled;
+		break;
+	case POWER_SUPPLY_PROP_CHARGE_TYPE:
+		val->intval = smb137c_get_property_charge_type(chip);
+		break;
+	case POWER_SUPPLY_PROP_CURRENT_MAX:
+		val->intval = chip->charge_current_limit_ua;
+		break;
+	case POWER_SUPPLY_PROP_TECHNOLOGY:
+		val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
+		break;
+	case POWER_SUPPLY_PROP_MODEL_NAME:
+		val->strval = "SMB137C";
+		break;
+	case POWER_SUPPLY_PROP_MANUFACTURER:
+		val->strval = "Summit Microelectronics";
+		break;
+	default:
+		mutex_unlock(&chip->lock);
+		return -EINVAL;
+	}
+
+	mutex_unlock(&chip->lock);
+
+	return 0;
+}
+
+static void smb137c_external_power_changed(struct power_supply *psy)
+{
+	struct smb137c_chip *chip = container_of(psy, struct smb137c_chip, psy);
+	union power_supply_propval prop = {0,};
+	int scope = POWER_SUPPLY_SCOPE_DEVICE;
+	int current_limit = 0;
+	int online = 0;
+	int rc;
+
+	mutex_lock(&chip->lock);
+	dev_dbg(&chip->client->dev, "%s: start\n", __func__);
+
+	rc = chip->usb_psy->get_property(chip->usb_psy,
+					POWER_SUPPLY_PROP_ONLINE, &prop);
+	if (rc)
+		dev_err(&chip->client->dev, "%s: could not read USB online property, rc=%d\n",
+			__func__, rc);
+	else
+		online = prop.intval;
+
+	rc = chip->usb_psy->get_property(chip->usb_psy, POWER_SUPPLY_PROP_SCOPE,
+					&prop);
+	if (rc)
+		dev_err(&chip->client->dev, "%s: could not read USB scope property, rc=%d\n",
+			__func__, rc);
+	else
+		scope = prop.intval;
+
+	rc = chip->usb_psy->get_property(chip->usb_psy,
+					POWER_SUPPLY_PROP_CURRENT_MAX, &prop);
+	if (rc)
+		dev_err(&chip->client->dev, "%s: could not read USB current_max property, rc=%d\n",
+			__func__, rc);
+	else
+		current_limit = prop.intval;
+
+	if (scope == POWER_SUPPLY_SCOPE_SYSTEM) {
+		/* USB host mode */
+		smb137c_disable_charging(chip);
+		smb137c_enable_otg_mode(chip);
+	} else if (online) {
+		/* USB online in device mode */
+		smb137c_set_usb_input_current_limit(chip, current_limit);
+		smb137c_enable_charging(chip);
+		smb137c_disable_otg_mode(chip);
+	} else {
+		/* USB offline */
+		smb137c_disable_charging(chip);
+		smb137c_disable_otg_mode(chip);
+		smb137c_set_usb_input_current_limit(chip,
+			min(current_limit, USB_MIN_CURRENT_UA));
+	}
+
+	dev_dbg(&chip->client->dev, "%s: end\n", __func__);
+	mutex_unlock(&chip->lock);
+
+	power_supply_changed(&chip->psy);
+}
+
+static int __devinit smb137c_set_register_defaults(struct smb137c_chip *chip)
+{
+	int rc;
+	u8 val, mask;
+
+	/* Allow volatile register writes. */
+	rc = smb137c_masked_write_reg(chip, CMD_A_REG,
+			CMD_A_VOLATILE_WRITE_MASK, CMD_A_VOLATILE_WRITE_ALLOW);
+	if (rc)
+		return rc;
+
+	/* Do not reset register values on USB reinsertion. */
+	rc = smb137c_masked_write_reg(chip, SAFETY_TIMER_REG,
+			SAFETY_TIMER_RELOAD_MASK, SAFETY_TIMER_RELOAD_DISABLED);
+	if (rc)
+		return rc;
+
+	/* Set various default control parameters. */
+	val = PIN_CTRL_DEAD_BATT_CHG_ENABLED | PIN_CTRL_OTG
+		| PIN_CTRL_USB_CUR_LIMIT_REG | PIN_CTRL_CHG_EN_REG_LOW
+		| PIN_CTRL_OTG_CTRL_REG;
+	mask = PIN_CTRL_DEAD_BATT_CHG_MASK | PIN_CTRL_OTG_LBR_MASK
+		| PIN_CTRL_USB_CUR_LIMIT_MASK | PIN_CTRL_CHG_EN_MASK
+		| PIN_CTRL_OTG_CTRL_MASK;
+	rc = smb137c_masked_write_reg(chip, PIN_CTRL_REG, mask, val);
+	if (rc)
+		return rc;
+
+	/* Disable charging, disable OTG mode, and allow fast-charge current. */
+	val = CMD_A_CHARGING_DISABLED | CMD_A_OTG_DISABLED
+		| CMD_A_FAST_CHG_ALLOW;
+	mask = CMD_A_CHARGING_MASK | CMD_A_OTG_MASK | CMD_A_FAST_CHG_MASK;
+	rc = smb137c_masked_write_reg(chip, CMD_A_REG, mask, val);
+	if (rc)
+		return rc;
+
+	/* Enable auto recharging and full-time THERM monitor. */
+	val = CTRL_A_AUTO_RECHARGE_ENABLED | CTRL_A_THERM_MONITOR_ENABLED;
+	mask = CTRL_A_AUTO_RECHARGE_MASK | CTRL_A_THERM_MONITOR_MASK;
+	rc = smb137c_masked_write_reg(chip, CTRL_A_REG, mask, val);
+	if (rc)
+		return rc;
+
+	/* Use register value instead of pin to control USB suspend. */
+	rc = smb137c_masked_write_reg(chip, VAR_FUNC_REG,
+		VAR_FUNC_USB_SUSPEND_CTRL_MASK, VAR_FUNC_USB_SUSPEND_CTRL_REG);
+	if (rc)
+		return rc;
+
+	return rc;
+}
+
+static int __devinit smb137c_apply_dt_configs(struct smb137c_chip *chip)
+{
+	struct device *dev = &chip->client->dev;
+	struct device_node *node = chip->client->dev.of_node;
+	int ret, current_ma, voltage_mv, timeout, value;
+	int rc = 0;
+
+	/*
+	 * All device tree parameters are optional so it is ok if read calls
+	 * fail.
+	 */
+	ret = of_property_read_u32(node, "summit,chg-current-ma", &current_ma);
+	if (ret == 0) {
+		rc = smb137c_set_charge_current_limit(chip, current_ma * 1000);
+		if (rc) {
+			dev_err(dev, "%s: Failed to set charge current, rc=%d\n",
+				__func__, rc);
+			return rc;
+		}
+	} else {
+		chip->charge_current_limit_ua
+			= smb137c_get_charge_current_limit(chip);
+		rc = chip->charge_current_limit_ua;
+		if (rc < 0) {
+			dev_err(dev, "%s: Failed to get charge current, rc=%d\n",
+				__func__, rc);
+			return rc;
+		}
+	}
+
+	ret = of_property_read_u32(node, "summit,term-current-ma", &current_ma);
+	if (ret == 0) {
+		rc = smb137c_set_term_current(chip, current_ma * 1000);
+		if (rc) {
+			dev_err(dev, "%s: Failed to set termination current, rc=%d\n",
+				__func__, rc);
+			return rc;
+		}
+	}
+
+	ret = of_property_read_u32(node, "summit,pre-chg-current-ma",
+					&current_ma);
+	if (ret == 0) {
+		rc = smb137c_set_pre_charge_current_limit(chip,
+				current_ma * 1000);
+		if (rc) {
+			dev_err(dev, "%s: Failed to set pre-charge current limit, rc=%d\n",
+				__func__, rc);
+			return rc;
+		}
+	}
+
+	ret = of_property_read_u32(node, "summit,float-voltage-mv",
+					&voltage_mv);
+	if (ret == 0) {
+		rc = smb137c_set_float_voltage(chip, voltage_mv * 1000);
+		if (rc) {
+			dev_err(dev, "%s: Failed to set float voltage, rc=%d\n",
+				__func__, rc);
+			return rc;
+		}
+	}
+
+	ret = of_property_read_u32(node, "summit,thresh-voltage-mv",
+					&voltage_mv);
+	if (ret == 0) {
+		rc = smb137c_set_pre_charge_threshold_voltage(chip,
+				voltage_mv * 1000);
+		if (rc) {
+			dev_err(dev, "%s: Failed to set fast-charge threshold voltage, rc=%d\n",
+				__func__, rc);
+			return rc;
+		}
+	}
+
+	ret = of_property_read_u32(node, "summit,recharge-thresh-mv",
+					&voltage_mv);
+	if (ret == 0) {
+		rc = smb137c_set_recharge_threshold_voltage(chip,
+				voltage_mv * 1000);
+		if (rc) {
+			dev_err(dev, "%s: Failed to set recharge threshold voltage, rc=%d\n",
+				__func__, rc);
+			return rc;
+		}
+	}
+
+	ret = of_property_read_u32(node, "summit,system-voltage-mv",
+					&voltage_mv);
+	if (ret == 0) {
+		rc = smb137c_set_system_voltage(chip, voltage_mv * 1000);
+		if (rc) {
+			dev_err(dev, "%s: Failed to set system voltage, rc=%d\n",
+				__func__, rc);
+			return rc;
+		}
+	}
+
+	ret = of_property_read_u32(node, "summit,charging-timeout", &timeout);
+	if (ret == 0) {
+		rc = smb137c_set_charging_timeout(chip, timeout);
+		if (rc) {
+			dev_err(dev, "%s: Failed to set charging timeout, rc=%d\n",
+				__func__, rc);
+			return rc;
+		}
+	}
+
+	ret = of_property_read_u32(node, "summit,pre-charge-timeout", &timeout);
+	if (ret == 0) {
+		rc = smb137c_set_pre_charge_timeout(chip, timeout);
+		if (rc) {
+			dev_err(dev, "%s: Failed to set pre-charge timeout, rc=%d\n",
+				__func__, rc);
+			return rc;
+		}
+	}
+
+	ret = of_property_read_u32(node, "summit,therm-current-ua", &value);
+	if (ret == 0) {
+		rc = smb137c_set_thermistor_current(chip, value);
+		if (rc) {
+			dev_err(dev, "%s: Failed to set thermistor current, rc=%d\n",
+				__func__, rc);
+			return rc;
+		}
+	}
+
+	ret = of_property_read_u32(node, "summit,temperature-min", &value);
+	if (ret == 0) {
+		rc = smb137c_set_temperature_low_limit(chip, value);
+		if (rc) {
+			dev_err(dev, "%s: Failed to set low temperature limit, rc=%d\n",
+				__func__, rc);
+			return rc;
+		}
+	}
+
+	ret = of_property_read_u32(node, "summit,temperature-max", &value);
+	if (ret == 0) {
+		rc = smb137c_set_temperature_high_limit(chip, value);
+		if (rc) {
+			dev_err(dev, "%s: Failed to set high temperature limit, rc=%d\n",
+				__func__, rc);
+			return rc;
+		}
+	}
+
+	return rc;
+}
+
+static int __devinit smb137c_probe(struct i2c_client *client,
+				  const struct i2c_device_id *id)
+{
+	struct smb137c_chip *chip;
+	struct device *dev = &client->dev;
+	struct device_node *node = client->dev.of_node;
+	int rc = 0;
+	int gui_rev, silicon_rev;
+	u8 dev_id;
+
+	if (!node) {
+		dev_err(dev, "%s: device tree information missing\n", __func__);
+		return -ENODEV;
+	}
+
+	if (!i2c_check_functionality(client->adapter,
+			I2C_FUNC_SMBUS_BYTE_DATA)) {
+		dev_err(dev, "%s: SMBUS_BYTE_DATA unsupported\n", __func__);
+		return -EIO;
+	}
+
+	chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
+	if (!chip) {
+		dev_err(dev, "%s: devm_kzalloc failed\n", __func__);
+		return -ENOMEM;
+	}
+
+	mutex_init(&chip->lock);
+	chip->client = client;
+	i2c_set_clientdata(client, chip);
+
+	chip->usb_psy = power_supply_get_by_name("usb");
+	if (!chip->usb_psy) {
+		dev_dbg(dev, "%s: USB supply not found; deferring charger probe\n",
+			__func__);
+		return -EPROBE_DEFER;
+	}
+
+	rc = smb137c_read_reg(chip, DEV_ID_REG, &dev_id);
+	if (rc)
+		return rc;
+
+	if ((dev_id & DEV_ID_PART_MASK) != DEV_ID_PART_SMB137C) {
+		dev_err(dev, "%s: invalid device ID=0x%02X\n", __func__,
+			dev_id);
+		return -ENODEV;
+	}
+
+	gui_rev = (dev_id & DEV_ID_GUI_REV_MASK) >> DEV_ID_GUI_REV_SHIFT;
+	silicon_rev = (dev_id & DEV_ID_SILICON_REV_MASK)
+			>> DEV_ID_SILICON_REV_SHIFT;
+
+	rc = smb137c_set_register_defaults(chip);
+	if (rc)
+		return rc;
+
+	rc = smb137c_apply_dt_configs(chip);
+	if (rc)
+		return rc;
+
+	chip->psy.name			 = "battery";
+	chip->psy.type			 = POWER_SUPPLY_TYPE_BATTERY;
+	chip->psy.properties		 = smb137c_power_properties;
+	chip->psy.num_properties	 = ARRAY_SIZE(smb137c_power_properties);
+	chip->psy.get_property		 = smb137c_power_get_property;
+	chip->psy.set_property		 = smb137c_power_set_property;
+	chip->psy.property_is_writeable  = smb137c_property_is_writeable;
+	chip->psy.external_power_changed = smb137c_external_power_changed;
+
+	rc = power_supply_register(dev, &chip->psy);
+	if (rc < 0) {
+		dev_err(dev, "%s: power_supply_register failed, rc=%d\n",
+						__func__, rc);
+		return rc;
+	}
+
+	smb137c_external_power_changed(&chip->psy);
+
+	dev_info(dev, "%s: SMB137C charger probed successfully, gui_rev=%d, silicon_rev=%d\n",
+		__func__, gui_rev, silicon_rev);
+
+	return rc;
+}
+
+static int __devexit smb137c_remove(struct i2c_client *client)
+{
+	return 0;
+}
+
+static const struct i2c_device_id smb137c_id[] = {
+	{ .name = "smb137c", },
+	{},
+};
+MODULE_DEVICE_TABLE(i2c, smb137c_id);
+
+static const struct of_device_id smb137c_match[] = {
+	{ .compatible = "summit,smb137c", },
+	{ },
+};
+
+static struct i2c_driver smb137c_driver = {
+	.driver	= {
+		.name		= "smb137c",
+		.owner		= THIS_MODULE,
+		.of_match_table	= smb137c_match,
+	},
+	.probe		= smb137c_probe,
+	.remove		= __devexit_p(smb137c_remove),
+	.id_table	= smb137c_id,
+};
+
+static int __init smb137c_init(void)
+{
+	return i2c_add_driver(&smb137c_driver);
+}
+module_init(smb137c_init);
+
+static void __exit smb137c_exit(void)
+{
+	return i2c_del_driver(&smb137c_driver);
+}
+module_exit(smb137c_exit);
+
+MODULE_DESCRIPTION("SMB137C Charger");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("i2c:smb137c");
diff --git a/drivers/power/smb350_charger.c b/drivers/power/smb350_charger.c
new file mode 100644
index 0000000..21d7aea
--- /dev/null
+++ b/drivers/power/smb350_charger.c
@@ -0,0 +1,860 @@
+/* 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 HW_VERSION_REG			0x34	/* Volatile Read-Only */
+
+#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(HW_VERSION),
+	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;
+}
+
+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_charger_present(struct i2c_client *client)
+{
+	int val;
+
+	/* Normally the device is non-removable and embedded on the board.
+	 * Verify that charger is present by getting I2C response.
+	 */
+	val = smb350_read_reg(client, STATUS_B_REG);
+	if (val < 0)
+		return false;
+
+	return true;
+}
+
+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,
+	/* 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_charger_present(client);
+		break;
+	case POWER_SUPPLY_PROP_ONLINE:
+		val->intval = smb350_is_dc_present(client);
+		break;
+	case POWER_SUPPLY_PROP_CHARGE_TYPE:
+		val->intval = smb350_get_prop_charge_type(dev);
+		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;
+	u8 version;
+
+	/* 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;
+	}
+
+	version = smb350_read_reg(client, HW_VERSION_REG);
+	version &= 0x0F; /* bits 0..3 */
+
+	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;
+	}
+
+	pr_info("HW Version = 0x%X.\n", version);
+
+	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/core.c b/drivers/regulator/core.c
index 57cde45..0ebb944 100644
--- a/drivers/regulator/core.c
+++ b/drivers/regulator/core.c
@@ -1897,6 +1897,8 @@
 		if (rdev->desc->ops->list_voltage)
 			selector = rdev->desc->ops->list_voltage(rdev,
 								 selector);
+		else if (rdev->desc->ops->get_voltage)
+			selector = rdev->desc->ops->get_voltage(rdev);
 		else
 			selector = -1;
 	} else if (rdev->desc->ops->set_voltage_sel) {
diff --git a/drivers/regulator/qpnp-regulator.c b/drivers/regulator/qpnp-regulator.c
index 0549593..a330f1b 100644
--- a/drivers/regulator/qpnp-regulator.c
+++ b/drivers/regulator/qpnp-regulator.c
@@ -550,11 +550,12 @@
 }
 
 static int qpnp_regulator_select_voltage(struct qpnp_regulator *vreg,
-		int min_uV, int max_uV, int *range_sel, int *voltage_sel)
+		int min_uV, int max_uV, int *range_sel, int *voltage_sel,
+		unsigned *selector)
 {
 	struct qpnp_voltage_range *range;
 	int uV = min_uV;
-	int lim_min_uV, lim_max_uV, i;
+	int lim_min_uV, lim_max_uV, i, range_id;
 
 	/* Check if request voltage is outside of physically settable range. */
 	lim_min_uV = vreg->set_points->range[0].set_point_min_uV;
@@ -575,7 +576,8 @@
 	for (i = vreg->set_points->count - 1; i > 0; i--)
 		if (uV > vreg->set_points->range[i - 1].max_uV)
 			break;
-	range = &vreg->set_points->range[i];
+	range_id = i;
+	range = &vreg->set_points->range[range_id];
 	*range_sel = range->range_sel;
 
 	/*
@@ -594,6 +596,11 @@
 		return -EINVAL;
 	}
 
+	*selector = 0;
+	for (i = 0; i < range_id; i++)
+		*selector += vreg->set_points->range[i].n_voltages;
+	*selector += (uV - range->set_point_min_uV) / range->step_uV;
+
 	return 0;
 }
 
@@ -605,7 +612,7 @@
 	u8 buf[2];
 
 	rc = qpnp_regulator_select_voltage(vreg, min_uV, max_uV, &range_sel,
-		&voltage_sel);
+		&voltage_sel, selector);
 	if (rc) {
 		vreg_err(vreg, "could not set voltage, rc=%d\n", rc);
 		return rc;
@@ -669,7 +676,7 @@
 	int rc, range_sel, voltage_sel;
 
 	rc = qpnp_regulator_select_voltage(vreg, min_uV, max_uV, &range_sel,
-		&voltage_sel);
+		&voltage_sel, selector);
 	if (rc) {
 		vreg_err(vreg, "could not set voltage, rc=%d\n", rc);
 		return rc;
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 58a1d66..9c69f47 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;
 }
 
@@ -618,6 +619,7 @@
 		u16 chh[40];
 		struct slim_ch prop;
 		u32 exp;
+		u16 *grph = NULL;
 		u8 coeff, cc;
 		u8 prrate = buf[6];
 		if (len <= 8)
@@ -638,6 +640,9 @@
 					return ret;
 				if (mc == SLIM_USR_MC_DEF_ACT_CHAN)
 					sat->satch[j].req_def++;
+				/* First channel in group from satellite */
+				if (i == 8)
+					grph = &sat->satch[j].chanh;
 				continue;
 			}
 			if (sat->nsatch >= MSM_MAX_SATCH)
@@ -649,6 +654,8 @@
 			sat->satch[j].chanh = chh[i - 8];
 			if (mc == SLIM_USR_MC_DEF_ACT_CHAN)
 				sat->satch[j].req_def++;
+			if (i == 8)
+				grph = &sat->satch[j].chanh;
 			sat->nsatch++;
 		}
 		prop.dataf = (enum slim_ch_dataf)((buf[3] & 0xE0) >> 5);
@@ -669,10 +676,12 @@
 					true, &chh[0]);
 		else
 			ret = slim_define_ch(&sat->satcl, &prop,
-					&chh[0], 1, false, NULL);
+					chh, 1, true, &chh[0]);
 		dev_dbg(dev->dev, "define sat grp returned:%d", ret);
 		if (ret)
 			return ret;
+		else if (grph)
+			*grph = chh[0];
 
 		/* part of group so activating 1 will take care of rest */
 		if (mc == SLIM_USR_MC_DEF_ACT_CHAN)
@@ -805,6 +814,8 @@
 						slim_control_ch(&sat->satcl,
 							sat->satch[i].chanh,
 							SLIM_CH_REMOVE, true);
+						slim_dealloc_ch(&sat->satcl,
+							sat->satch[i].chanh);
 						sat->satch[i].reconf = false;
 					}
 				}
diff --git a/drivers/slimbus/slim-msm-ngd.c b/drivers/slimbus/slim-msm-ngd.c
new file mode 100644
index 0000000..02e1952
--- /dev/null
+++ b/drivers/slimbus/slim-msm-ngd.c
@@ -0,0 +1,1037 @@
+/* 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.
+ */
+
+#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_clk_pause_wakeup(struct slim_controller *ctrl)
+{
+	struct msm_slim_ctrl *dev = slim_get_ctrldata(ctrl);
+	return msm_slim_qmi_power_request(dev, true);
+}
+
+static int ngd_qmi_available(struct notifier_block *n, unsigned long code,
+				void *_cmd)
+{
+	struct msm_slim_qmi *qmi = container_of(n, struct msm_slim_qmi, nb);
+	pr_info("Slimbus QMI NGD CB received event:%ld", code);
+	switch (code) {
+	case QMI_SERVER_ARRIVE:
+		complete(&qmi->qmi_comp);
+		break;
+	case QMI_SERVER_EXIT:
+		/* SSR implementation */
+		break;
+	default:
+		break;
+	}
+	return 0;
+}
+
+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;
+	u8 la = txn->la;
+	u8 wbuf[SLIM_RX_MSGQ_BUF_LEN];
+
+	if (txn->mc == (SLIM_MSG_CLK_PAUSE_SEQ_FLG |
+			SLIM_MSG_MC_RECONFIGURE_NOW))
+		return msm_slim_qmi_power_request(dev, false);
+	else if (txn->mc & SLIM_MSG_CLK_PAUSE_SEQ_FLG)
+		return 0;
+
+	if (txn->mt == SLIM_MSG_MT_CORE &&
+		(txn->mc >= SLIM_MSG_MC_BEGIN_RECONFIGURATION &&
+		 txn->mc <= SLIM_MSG_MC_RECONFIGURE_NOW)) {
+		return 0;
+	}
+	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 {
+			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);
+			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);
+		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);
+	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) {
+			/* Per protocol, only last 5 bits for client no. */
+			wbuf[txn.len++] = (u8) (slc->prop.dataf << 5) |
+					(sb->laddr & 0x1f);
+			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;
+		pr_debug("slim define chan:%d, tid:0x%x", slc->chan, txn.tid);
+	}
+	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) {
+			/* Per protocol, only last 5 bits for client no. */
+			wbuf[txn.len++] = (u8) (SLIM_CH_REMOVE << 6) |
+					(sb->laddr & 0x1f);
+			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;
+		pr_debug("slim remove chan:%d, tid:0x%x", slc->chan, txn.tid);
+	}
+	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;
+		pr_info("SLIM SAT: Received master capability");
+		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;
+
+			pm_runtime_use_autosuspend(dev->dev);
+			pm_runtime_set_autosuspend_delay(dev->dev,
+							MSM_SLIM_AUTOSUSPEND);
+			pm_runtime_set_active(dev->dev);
+			pm_runtime_enable(dev->dev);
+			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_enable(struct msm_slim_ctrl *dev, bool enable)
+{
+	u32 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);
+	if (enable) {
+		int ret = msm_slim_qmi_init(dev, false);
+		if (ret)
+			return ret;
+		ret = msm_slim_qmi_power_request(dev, true);
+		if (ret)
+			return ret;
+		writel_relaxed(ngd_int, dev->base + NGD_INT_EN +
+					NGD_BASE(dev->ctrl.nr, dev->ver));
+		/*
+		 * Enable NGD. Configure NGD in register acc. 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();
+	} else {
+		writel_relaxed(0, dev->base + NGD_BASE(dev->ctrl.nr, dev->ver));
+		writel_relaxed(0, dev->base + NGD_INT_EN +
+				NGD_BASE(dev->ctrl.nr, dev->ver));
+		/* make sure NGD disabling goes through */
+		mb();
+		msm_slim_qmi_exit(dev);
+	}
+
+	return 0;
+}
+
+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;
+
+	wait_for_completion_interruptible(&dev->qmi.qmi_comp);
+	ret = ngd_slim_enable(dev, true);
+	/* Exit the thread if component can't be enabled */
+	if (ret) {
+		pr_err("Enabling NGD failed:%d", ret);
+		return 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;
+
+	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 =  ngd_clk_pause_wakeup;
+	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;
+	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;
+	}
+
+	init_completion(&dev->qmi.qmi_comp);
+	dev->qmi.nb.notifier_call = ngd_qmi_available;
+	ret = qmi_svc_event_notifier_register(SLIMBUS_QMI_SVC_ID,
+				SLIMBUS_QMI_INS_ID, &dev->qmi.nb);
+	if (ret) {
+		pr_err("Slimbus QMI service registration failed:%d", ret);
+		goto qmi_register_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;
+	}
+
+	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);
+
+	dev_dbg(dev->dev, "NGD SB controller is up!\n");
+	return 0;
+
+err_thread_create_failed:
+	qmi_svc_event_notifier_unregister(SLIMBUS_QMI_SVC_ID,
+				SLIMBUS_QMI_INS_ID, &dev->qmi.nb);
+qmi_register_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);
+	ngd_slim_enable(dev, false);
+	qmi_svc_event_notifier_unregister(SLIMBUS_QMI_SVC_ID,
+				SLIMBUS_QMI_INS_ID, &dev->qmi.nb);
+	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 7cd34d3..c62ac27 100644
--- a/drivers/slimbus/slim-msm.c
+++ b/drivers/slimbus/slim-msm.c
@@ -582,3 +582,351 @@
 		sps_deregister_bam_device(dev->bam.hdl);
 	}
 }
+
+/* Slimbus QMI Messaging */
+#define SLIMBUS_QMI_SELECT_INSTANCE_REQ_V01 0x0020
+#define SLIMBUS_QMI_SELECT_INSTANCE_RESP_V01 0x0020
+#define SLIMBUS_QMI_POWER_REQ_V01 0x0021
+#define SLIMBUS_QMI_POWER_RESP_V01 0x0021
+
+enum slimbus_mode_enum_type_v01 {
+	/* To force a 32 bit signed enum. Do not change or use*/
+	SLIMBUS_MODE_ENUM_TYPE_MIN_ENUM_VAL_V01 = INT_MIN,
+	SLIMBUS_MODE_SATELLITE_V01 = 1,
+	SLIMBUS_MODE_MASTER_V01 = 2,
+	SLIMBUS_MODE_ENUM_TYPE_MAX_ENUM_VAL_V01 = INT_MAX,
+};
+
+enum slimbus_pm_enum_type_v01 {
+	/* To force a 32 bit signed enum. Do not change or use*/
+	SLIMBUS_PM_ENUM_TYPE_MIN_ENUM_VAL_V01 = INT_MIN,
+	SLIMBUS_PM_INACTIVE_V01 = 1,
+	SLIMBUS_PM_ACTIVE_V01 = 2,
+	SLIMBUS_PM_ENUM_TYPE_MAX_ENUM_VAL_V01 = INT_MAX,
+};
+
+struct slimbus_select_inst_req_msg_v01 {
+	/* Mandatory */
+	/* Hardware Instance Selection */
+	uint32_t instance;
+
+	/* Optional */
+	/* Optional Mode Request Operation */
+	/* Must be set to true if mode is being passed */
+	uint8_t mode_valid;
+	enum slimbus_mode_enum_type_v01 mode;
+};
+
+struct slimbus_select_inst_resp_msg_v01 {
+	/* Mandatory */
+	/* Result Code */
+	struct qmi_response_type_v01 resp;
+};
+
+struct slimbus_power_req_msg_v01 {
+	/* Mandatory */
+	/* Power Request Operation */
+	enum slimbus_pm_enum_type_v01 pm_req;
+};
+
+struct slimbus_power_resp_msg_v01 {
+	/* Mandatory */
+	/* Result Code */
+	struct qmi_response_type_v01 resp;
+};
+
+static struct elem_info slimbus_select_inst_req_msg_v01_ei[] = {
+	{
+		.data_type = QMI_UNSIGNED_4_BYTE,
+		.elem_len  = 1,
+		.elem_size = sizeof(uint32_t),
+		.is_array  = NO_ARRAY,
+		.tlv_type  = 0x01,
+		.offset    = offsetof(struct slimbus_select_inst_req_msg_v01,
+				      instance),
+		.ei_array  = NULL,
+	},
+	{
+		.data_type = QMI_OPT_FLAG,
+		.elem_len  = 1,
+		.elem_size = sizeof(uint8_t),
+		.is_array  = NO_ARRAY,
+		.tlv_type  = 0x10,
+		.offset    = offsetof(struct slimbus_select_inst_req_msg_v01,
+				      mode_valid),
+		.ei_array  = NULL,
+	},
+	{
+		.data_type = QMI_UNSIGNED_4_BYTE,
+		.elem_len  = 1,
+		.elem_size = sizeof(enum slimbus_mode_enum_type_v01),
+		.is_array  = NO_ARRAY,
+		.tlv_type  = 0x10,
+		.offset    = offsetof(struct slimbus_select_inst_req_msg_v01,
+				      mode),
+		.ei_array  = NULL,
+	},
+	{
+		.data_type = QMI_EOTI,
+		.elem_len  = 0,
+		.elem_size = 0,
+		.is_array  = NO_ARRAY,
+		.tlv_type  = 0x00,
+		.offset    = 0,
+		.ei_array  = NULL,
+	},
+};
+
+static struct elem_info slimbus_select_inst_resp_msg_v01_ei[] = {
+	{
+		.data_type = QMI_STRUCT,
+		.elem_len  = 1,
+		.elem_size = sizeof(struct qmi_response_type_v01),
+		.is_array  = NO_ARRAY,
+		.tlv_type  = 0x02,
+		.offset    = offsetof(struct slimbus_select_inst_resp_msg_v01,
+				      resp),
+		.ei_array  = get_qmi_response_type_v01_ei(),
+	},
+	{
+		.data_type = QMI_EOTI,
+		.elem_len  = 0,
+		.elem_size = 0,
+		.is_array  = NO_ARRAY,
+		.tlv_type  = 0x00,
+		.offset    = 0,
+		.ei_array  = NULL,
+	},
+};
+
+static struct elem_info slimbus_power_req_msg_v01_ei[] = {
+	{
+		.data_type = QMI_UNSIGNED_4_BYTE,
+		.elem_len  = 1,
+		.elem_size = sizeof(enum slimbus_pm_enum_type_v01),
+		.is_array  = NO_ARRAY,
+		.tlv_type  = 0x01,
+		.offset    = offsetof(struct slimbus_power_req_msg_v01, pm_req),
+		.ei_array  = NULL,
+	},
+	{
+		.data_type = QMI_EOTI,
+		.elem_len  = 0,
+		.elem_size = 0,
+		.is_array  = NO_ARRAY,
+		.tlv_type  = 0x00,
+		.offset    = 0,
+		.ei_array  = NULL,
+	},
+};
+
+static struct elem_info slimbus_power_resp_msg_v01_ei[] = {
+	{
+		.data_type = QMI_STRUCT,
+		.elem_len  = 1,
+		.elem_size = sizeof(struct qmi_response_type_v01),
+		.is_array  = NO_ARRAY,
+		.tlv_type  = 0x02,
+		.offset    = offsetof(struct slimbus_power_resp_msg_v01, resp),
+		.ei_array  = get_qmi_response_type_v01_ei(),
+	},
+	{
+		.data_type = QMI_EOTI,
+		.elem_len  = 0,
+		.elem_size = 0,
+		.is_array  = NO_ARRAY,
+		.tlv_type  = 0x00,
+		.offset    = 0,
+		.ei_array  = NULL,
+	},
+};
+
+static void msm_slim_qmi_recv_msg(struct kthread_work *work)
+{
+	int rc;
+	struct msm_slim_qmi *qmi =
+			container_of(work, struct msm_slim_qmi, kwork);
+
+	rc = qmi_recv_msg(qmi->handle);
+	if (rc < 0)
+		pr_err("%s: Error receiving QMI message\n", __func__);
+}
+
+static void msm_slim_qmi_notify(struct qmi_handle *handle,
+				enum qmi_event_type event, void *notify_priv)
+{
+	struct msm_slim_ctrl *dev = notify_priv;
+	struct msm_slim_qmi *qmi = &dev->qmi;
+
+	switch (event) {
+	case QMI_RECV_MSG:
+		queue_kthread_work(&qmi->kworker, &qmi->kwork);
+		break;
+	default:
+		break;
+	}
+}
+
+static const char *get_qmi_error(struct qmi_response_type_v01 *r)
+{
+	if (r->result == QMI_RESULT_SUCCESS_V01 || r->error == QMI_ERR_NONE_V01)
+		return "No Error";
+	else if (r->error == QMI_ERR_NO_MEMORY_V01)
+		return "Out of Memory";
+	else if (r->error == QMI_ERR_INTERNAL_V01)
+		return "Unexpected error occurred";
+	else if (r->error == QMI_ERR_INCOMPATIBLE_STATE_V01)
+		return "Slimbus s/w already configured to a different mode";
+	else if (r->error == QMI_ERR_INVALID_ID_V01)
+		return "Slimbus hardware instance is not valid";
+	else
+		return "Unknown error";
+}
+
+static int msm_slim_qmi_send_select_inst_req(struct msm_slim_ctrl *dev,
+				struct slimbus_select_inst_req_msg_v01 *req)
+{
+	struct slimbus_select_inst_resp_msg_v01 resp = { { 0, 0 } };
+	struct msg_desc req_desc, resp_desc;
+	int rc;
+
+	req_desc.msg_id = SLIMBUS_QMI_SELECT_INSTANCE_REQ_V01;
+	req_desc.max_msg_len = sizeof(*req);
+	req_desc.ei_array = slimbus_select_inst_req_msg_v01_ei;
+
+	resp_desc.msg_id = SLIMBUS_QMI_SELECT_INSTANCE_RESP_V01;
+	resp_desc.max_msg_len = sizeof(resp);
+	resp_desc.ei_array = slimbus_select_inst_resp_msg_v01_ei;
+
+	rc = qmi_send_req_wait(dev->qmi.handle, &req_desc, req, sizeof(*req),
+					&resp_desc, &resp, sizeof(resp), 5000);
+	if (rc < 0) {
+		pr_err("%s: QMI send req failed %d\n", __func__, rc);
+		return rc;
+	}
+
+	/* Check the response */
+	if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
+		pr_err("%s: QMI request failed 0x%x (%s)\n", __func__,
+				resp.resp.result, get_qmi_error(&resp.resp));
+		return -EREMOTEIO;
+	}
+
+	return 0;
+}
+
+static int msm_slim_qmi_send_power_request(struct msm_slim_ctrl *dev,
+				struct slimbus_power_req_msg_v01 *req)
+{
+	struct slimbus_power_resp_msg_v01 resp = { { 0, 0 } };
+	struct msg_desc req_desc, resp_desc;
+	int rc;
+
+	req_desc.msg_id = SLIMBUS_QMI_POWER_REQ_V01;
+	req_desc.max_msg_len = sizeof(*req);
+	req_desc.ei_array = slimbus_power_req_msg_v01_ei;
+
+	resp_desc.msg_id = SLIMBUS_QMI_POWER_RESP_V01;
+	resp_desc.max_msg_len = sizeof(resp);
+	resp_desc.ei_array = slimbus_power_resp_msg_v01_ei;
+
+	rc = qmi_send_req_wait(dev->qmi.handle, &req_desc, req, sizeof(*req),
+					&resp_desc, &resp, sizeof(resp), 5000);
+	if (rc < 0) {
+		pr_err("%s: QMI send req failed %d\n", __func__, rc);
+		return rc;
+	}
+
+	/* Check the response */
+	if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
+		pr_err("%s: QMI request failed 0x%x (%s)\n", __func__,
+				resp.resp.result, get_qmi_error(&resp.resp));
+		return -EREMOTEIO;
+	}
+
+	return 0;
+}
+
+int msm_slim_qmi_init(struct msm_slim_ctrl *dev, bool apps_is_master)
+{
+	int rc = 0;
+	struct qmi_handle *handle;
+	struct slimbus_select_inst_req_msg_v01 req;
+
+	init_kthread_worker(&dev->qmi.kworker);
+
+	dev->qmi.task = kthread_run(kthread_worker_fn,
+			&dev->qmi.kworker, "msm_slim_qmi_clnt%d", dev->ctrl.nr);
+
+	if (IS_ERR(dev->qmi.task)) {
+		pr_err("%s: Failed to create QMI client kthread\n", __func__);
+		return -ENOMEM;
+	}
+
+	init_kthread_work(&dev->qmi.kwork, msm_slim_qmi_recv_msg);
+
+	handle = qmi_handle_create(msm_slim_qmi_notify, dev);
+	if (!handle) {
+		rc = -ENOMEM;
+		pr_err("%s: QMI client handle alloc failed\n", __func__);
+		goto qmi_handle_create_failed;
+	}
+
+	rc = qmi_connect_to_service(handle, SLIMBUS_QMI_SVC_ID,
+						SLIMBUS_QMI_INS_ID);
+	if (rc < 0) {
+		pr_err("%s: QMI server not found\n", __func__);
+		goto qmi_connect_to_service_failed;
+	}
+
+	/* Instance is 0 based */
+	req.instance = dev->ctrl.nr - 1;
+	req.mode_valid = 1;
+
+	/* Mode indicates the role of the ADSP */
+	if (apps_is_master)
+		req.mode = SLIMBUS_MODE_SATELLITE_V01;
+	else
+		req.mode = SLIMBUS_MODE_MASTER_V01;
+
+	dev->qmi.handle = handle;
+
+	rc = msm_slim_qmi_send_select_inst_req(dev, &req);
+	if (rc) {
+		pr_err("%s: failed to select h/w instance\n", __func__);
+		goto qmi_select_instance_failed;
+	}
+
+	return 0;
+
+qmi_select_instance_failed:
+	dev->qmi.handle = NULL;
+qmi_connect_to_service_failed:
+	qmi_handle_destroy(handle);
+qmi_handle_create_failed:
+	flush_kthread_worker(&dev->qmi.kworker);
+	kthread_stop(dev->qmi.task);
+	dev->qmi.task = NULL;
+	return rc;
+}
+
+void msm_slim_qmi_exit(struct msm_slim_ctrl *dev)
+{
+	qmi_handle_destroy(dev->qmi.handle);
+	flush_kthread_worker(&dev->qmi.kworker);
+	kthread_stop(dev->qmi.task);
+	dev->qmi.task = NULL;
+	dev->qmi.handle = NULL;
+}
+
+int msm_slim_qmi_power_request(struct msm_slim_ctrl *dev, bool active)
+{
+	struct slimbus_power_req_msg_v01 req;
+
+	if (active)
+		req.pm_req = SLIMBUS_PM_ACTIVE_V01;
+	else
+		req.pm_req = SLIMBUS_PM_INACTIVE_V01;
+
+	return msm_slim_qmi_send_power_request(dev, &req);
+}
diff --git a/drivers/slimbus/slim-msm.h b/drivers/slimbus/slim-msm.h
index 35bb040..3daf7ee 100644
--- a/drivers/slimbus/slim-msm.h
+++ b/drivers/slimbus/slim-msm.h
@@ -12,6 +12,10 @@
 
 #ifndef _SLIM_MSM_H
 #define _SLIM_MSM_H
+
+#include <linux/kthread.h>
+#include <mach/msm_qmi_interface.h>
+
 /* Per spec.max 40 bytes per received message */
 #define SLIM_RX_MSGQ_BUF_LEN	40
 
@@ -64,6 +68,10 @@
 #define MSM_MAX_NSATS	2
 #define MSM_MAX_SATCH	32
 
+/* Slimbus QMI service */
+#define SLIMBUS_QMI_SVC_ID 0x0301
+#define SLIMBUS_QMI_INS_ID 1
+
 #define PGD_THIS_EE(r, v) ((v) ? PGD_THIS_EE_V2(r) : PGD_THIS_EE_V1(r))
 #define PGD_PORT(r, p, v) ((v) ? PGD_PORT_V2(r, p) : PGD_PORT_V1(r, p))
 #define CFG_PORT(r, v) ((v) ? CFG_PORT_V2(r) : CFG_PORT_V1(r))
@@ -161,12 +169,22 @@
 	bool				connected;
 };
 
+struct msm_slim_qmi {
+	struct qmi_handle		*handle;
+	struct task_struct		*task;
+	struct kthread_work		kwork;
+	struct kthread_worker		kworker;
+	struct completion		qmi_comp;
+	struct notifier_block		nb;
+};
+
 struct msm_slim_ctrl {
 	struct slim_controller  ctrl;
 	struct slim_framer	framer;
 	struct device		*dev;
 	void __iomem		*base;
 	struct resource		*slew_mem;
+	struct resource		*bam_mem;
 	u32			curr_bw;
 	u8			msg_cnt;
 	u32			tx_buf[10];
@@ -196,6 +214,7 @@
 	enum msm_ctrl_state	state;
 	int			nsats;
 	u32			ver;
+	struct msm_slim_qmi	qmi;
 };
 
 struct msm_sat_chan {
@@ -248,4 +267,8 @@
 int msm_slim_sps_init(struct msm_slim_ctrl *dev, struct resource *bam_mem,
 			u32 pipe_reg, bool remote);
 void msm_slim_sps_exit(struct msm_slim_ctrl *dev);
+
+void msm_slim_qmi_exit(struct msm_slim_ctrl *dev);
+int msm_slim_qmi_init(struct msm_slim_ctrl *dev, bool apps_is_master);
+int msm_slim_qmi_power_request(struct msm_slim_ctrl *dev, bool active);
 #endif
diff --git a/drivers/slimbus/slimbus.c b/drivers/slimbus/slimbus.c
index bd25875..d5d6e0c 100644
--- a/drivers/slimbus/slimbus.c
+++ b/drivers/slimbus/slimbus.c
@@ -26,6 +26,7 @@
 #define SLIM_HDL_TO_PORT(hdl)	((u32)(hdl) & 0xFF)
 
 #define SLIM_HDL_TO_CHIDX(hdl)	((u16)(hdl) & 0xFF)
+#define SLIM_GRP_TO_NCHAN(hdl)	((u16)(hdl >> 8) & 0xFF)
 
 #define SLIM_SLAVE_PORT(p, la)	(((la)<<16) | (p))
 #define SLIM_MGR_PORT(p)	((0xFF << 16) | (p))
@@ -767,6 +768,7 @@
 	list_for_each_entry(sbdev, &ctrl->devs, dev_list) {
 		if (memcmp(sbdev->e_addr, e_addr, 6) == 0) {
 			struct slim_driver *sbdrv;
+			sbdev->laddr = *laddr;
 			if (sbdev->dev.driver) {
 				sbdrv = to_slim_driver(sbdev->dev.driver);
 				if (sbdrv->device_up)
@@ -1845,7 +1847,7 @@
 	}
 
 	if (grp)
-		*grph = chanh[0];
+		*grph = ((nchan << 8) | SLIM_HDL_TO_CHIDX(chanh[0]));
 	for (i = 0; i < nchan; i++) {
 		u8 chan = SLIM_HDL_TO_CHIDX(chanh[i]);
 		struct slim_ich *slc = &ctrl->chans[chan];
@@ -2868,6 +2870,7 @@
 	int ret = 0;
 	/* Get rid of the group flag in MSB if any */
 	u8 chan = SLIM_HDL_TO_CHIDX(chanh);
+	u8 nchan = 0;
 	struct slim_ich *slc = &ctrl->chans[chan];
 	if (!(slc->nextgrp & SLIM_START_GRP))
 		return -EINVAL;
@@ -2875,6 +2878,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,14 +2905,36 @@
 				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))
+		nchan++;
+		if (nchan < SLIM_GRP_TO_NCHAN(chanh))
 			chan = SLIM_HDL_TO_CHIDX(slc->nextgrp);
-	} while (!(slc->nextgrp & SLIM_END_GRP));
+	} while (nchan < SLIM_GRP_TO_NCHAN(chanh));
 	mutex_unlock(&ctrl->m_ctrl);
 	if (!ret && commit == true)
 		ret = slim_reconfigure_now(sb);
diff --git a/drivers/spi/spi_qsd.c b/drivers/spi/spi_qsd.c
index c26da60..dba02f8 100644
--- a/drivers/spi/spi_qsd.c
+++ b/drivers/spi/spi_qsd.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008-2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2008-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,17 +30,18 @@
 #include <linux/workqueue.h>
 #include <linux/io.h>
 #include <linux/debugfs.h>
-#include <mach/msm_spi.h>
-#include <linux/dma-mapping.h>
-#include <linux/sched.h>
-#include <mach/dma.h>
-#include <asm/atomic.h>
-#include <linux/mutex.h>
 #include <linux/gpio.h>
 #include <linux/remote_spinlock.h>
 #include <linux/pm_qos.h>
 #include <linux/of.h>
 #include <linux/of_gpio.h>
+#include <linux/dma-mapping.h>
+#include <linux/sched.h>
+#include <linux/mutex.h>
+#include <linux/atomic.h>
+#include <mach/msm_spi.h>
+#include <mach/sps.h>
+#include <mach/dma.h>
 #include "spi_qsd.h"
 
 static inline int msm_spi_configure_gsbi(struct msm_spi *dd,
@@ -211,16 +212,19 @@
 				   &dd->output_block_size, block, mult)) {
 		goto fifo_size_err;
 	}
-	/* DM mode is not available for this block size */
-	if (dd->input_block_size == 4 || dd->output_block_size == 4)
-		dd->use_dma = 0;
+	if (dd->qup_ver == SPI_QUP_VERSION_NONE) {
+		/* DM mode is not available for this block size */
+		if (dd->input_block_size == 4 || dd->output_block_size == 4)
+			dd->use_dma = 0;
 
-	/* DM mode is currently unsupported for different block sizes */
-	if (dd->input_block_size != dd->output_block_size)
-		dd->use_dma = 0;
+		/* DM mode is currently unsupported for different block sizes */
+		if (dd->input_block_size != dd->output_block_size)
+			dd->use_dma = 0;
 
-	if (dd->use_dma)
-		dd->burst_size = max(dd->input_block_size, DM_BURST_SIZE);
+		if (dd->use_dma)
+			dd->burst_size = max(dd->input_block_size,
+							DM_BURST_SIZE);
+	}
 
 	return;
 
@@ -284,6 +288,18 @@
 	return spi_op & SPI_OP_STATE_VALID;
 }
 
+static inline void msm_spi_udelay(unsigned long delay_usecs)
+{
+	/*
+	 * For smaller values of delay, context switch time
+	 * would negate the usage of usleep
+	 */
+	if (delay_usecs > 20)
+		usleep_range(delay_usecs, delay_usecs);
+	else if (delay_usecs)
+		udelay(delay_usecs);
+}
+
 static inline int msm_spi_wait_valid(struct msm_spi *dd)
 {
 	unsigned long delay = 0;
@@ -317,14 +333,7 @@
 			} else
 				return 0;
 		}
-		/*
-		 * For smaller values of delay, context switch time
-		 * would negate the usage of usleep
-		 */
-		if (delay > 20)
-			usleep(delay);
-		else if (delay)
-			udelay(delay);
+		msm_spi_udelay(delay);
 	}
 	return 0;
 }
@@ -352,14 +361,19 @@
 	return 0;
 }
 
-static inline void msm_spi_add_configs(struct msm_spi *dd, u32 *config, int n)
+/**
+ * msm_spi_set_bpw_and_no_io_flags: configure N, and no-input/no-output flags
+ */
+static inline void
+msm_spi_set_bpw_and_no_io_flags(struct msm_spi *dd, u32 *config, int n)
 {
 	*config &= ~(SPI_NO_INPUT|SPI_NO_OUTPUT);
 
 	if (n != (*config & SPI_CFG_N))
 		*config = (*config & ~SPI_CFG_N) | n;
 
-	if ((dd->mode == SPI_DMOV_MODE) && (!dd->read_len)) {
+	if (((dd->mode == SPI_DMOV_MODE) && (!dd->read_len))
+					|| (dd->mode == SPI_BAM_MODE)) {
 		if (dd->read_buf == NULL)
 			*config |= SPI_NO_INPUT;
 		if (dd->write_buf == NULL)
@@ -367,23 +381,207 @@
 	}
 }
 
-static void msm_spi_set_config(struct msm_spi *dd, int bpw)
+/**
+ * msm_spi_calc_spi_config_loopback_and_input_first: Calculate the values that
+ * should be updated into SPI_CONFIG's LOOPBACK and INPUT_FIRST flags
+ * @return calculatd value for SPI_CONFIG
+ */
+static u32
+msm_spi_calc_spi_config_loopback_and_input_first(u32 spi_config, u8 mode)
 {
-	u32 spi_config;
-
-	spi_config = readl_relaxed(dd->base + SPI_CONFIG);
-
-	if (dd->cur_msg->spi->mode & SPI_CPHA)
-		spi_config &= ~SPI_CFG_INPUT_FIRST;
-	else
-		spi_config |= SPI_CFG_INPUT_FIRST;
-	if (dd->cur_msg->spi->mode & SPI_LOOP)
+	if (mode & SPI_LOOP)
 		spi_config |= SPI_CFG_LOOPBACK;
 	else
 		spi_config &= ~SPI_CFG_LOOPBACK;
-	msm_spi_add_configs(dd, &spi_config, bpw-1);
+
+	if (mode & SPI_CPHA)
+		spi_config &= ~SPI_CFG_INPUT_FIRST;
+	else
+		spi_config |= SPI_CFG_INPUT_FIRST;
+
+	return spi_config;
+}
+
+/**
+ * msm_spi_set_spi_config: prepares register SPI_CONFIG to process the
+ * next transfer
+ */
+static void msm_spi_set_spi_config(struct msm_spi *dd, int bpw)
+{
+	u32 spi_config = readl_relaxed(dd->base + SPI_CONFIG);
+	spi_config = msm_spi_calc_spi_config_loopback_and_input_first(
+					spi_config, dd->cur_msg->spi->mode);
+
+	if (dd->qup_ver == SPI_QUP_VERSION_NONE)
+		/* flags removed from SPI_CONFIG in QUP version-2 */
+		msm_spi_set_bpw_and_no_io_flags(dd, &spi_config, bpw-1);
+	else if (dd->mode == SPI_BAM_MODE)
+		spi_config |= SPI_CFG_INPUT_FIRST;
+
 	writel_relaxed(spi_config, dd->base + SPI_CONFIG);
-	msm_spi_set_qup_config(dd, bpw);
+}
+
+/**
+ * msm_spi_set_mx_counts: set SPI_MX_INPUT_COUNT and SPI_MX_INPUT_COUNT
+ * for FIFO-mode. set SPI_MX_INPUT_COUNT and SPI_MX_OUTPUT_COUNT for
+ * BAM and DMOV modes.
+ * @n_words The number of reads/writes of size N.
+ */
+static void msm_spi_set_mx_counts(struct msm_spi *dd, u32 n_words)
+{
+	/*
+	 * n_words cannot exceed fifo_size, and only one READ COUNT
+	 * interrupt is generated per transaction, so for transactions
+	 * larger than fifo size READ COUNT must be disabled.
+	 * For those transactions we usually move to Data Mover mode.
+	 */
+	if (dd->mode == SPI_FIFO_MODE) {
+		if (n_words <= dd->input_fifo_size) {
+			writel_relaxed(n_words,
+				       dd->base + SPI_MX_READ_COUNT);
+			msm_spi_set_write_count(dd, n_words);
+		} else {
+			writel_relaxed(0, dd->base + SPI_MX_READ_COUNT);
+			msm_spi_set_write_count(dd, 0);
+		}
+		if (dd->qup_ver == SPI_QUP_VERSION_BFAM) {
+			/* must be zero for FIFO */
+			writel_relaxed(0, dd->base + SPI_MX_INPUT_COUNT);
+			writel_relaxed(0, dd->base + SPI_MX_OUTPUT_COUNT);
+		}
+	} else {
+		/* must be zero for BAM and DMOV */
+		writel_relaxed(0, dd->base + SPI_MX_READ_COUNT);
+		msm_spi_set_write_count(dd, 0);
+
+		/*
+		 * for DMA transfers, both QUP_MX_INPUT_COUNT and
+		 * QUP_MX_OUTPUT_COUNT must be zero to all cases but one.
+		 * That case is a non-balanced transfer when there is
+		 * only a read_buf.
+		 */
+		if (dd->qup_ver == SPI_QUP_VERSION_BFAM) {
+			if (dd->write_buf)
+				writel_relaxed(0,
+						dd->base + SPI_MX_INPUT_COUNT);
+			else
+				writel_relaxed(n_words,
+						dd->base + SPI_MX_INPUT_COUNT);
+
+			writel_relaxed(0, dd->base + SPI_MX_OUTPUT_COUNT);
+		}
+	}
+}
+
+/**
+ * msm_spi_bam_begin_transfer: transfer dd->tx_bytes_remaining bytes
+ * using BAM.
+ * @brief BAM can transfer SPI_MAX_TRFR_BTWN_RESETS byte at a single
+ * transfer. Between transfer QUP must change to reset state. A loop is
+ * issuing a single BAM transfer at a time. If another tsranfer is
+ * required, it waits for the trasfer to finish, then moving to reset
+ * state, and back to run state to issue the next transfer.
+ * The function dose not wait for the last transfer to end, or if only
+ * a single transfer is required, the function dose not wait for it to
+ * end.
+ * @timeout max time in jiffies to wait for a transfer to finish.
+ * @return zero on success
+ */
+static int
+msm_spi_bam_begin_transfer(struct msm_spi *dd, u32 timeout, u8 bpw)
+{
+	u32 bytes_to_send, bytes_sent, n_words_xfr, cons_flags, prod_flags;
+	int ret;
+	/*
+	 * QUP must move to reset mode every 64K-1 bytes of transfer
+	 * (counter is 16 bit)
+	 */
+	if (dd->tx_bytes_remaining > SPI_MAX_TRFR_BTWN_RESETS) {
+		/* assert chip select unconditionally */
+		u32 spi_ioc = readl_relaxed(dd->base + SPI_IO_CONTROL);
+		if (!(spi_ioc & SPI_IO_C_FORCE_CS))
+			writel_relaxed(spi_ioc | SPI_IO_C_FORCE_CS,
+				dd->base + SPI_IO_CONTROL);
+	}
+
+	/* Following flags are required since we are waiting on all transfers */
+	cons_flags = SPS_IOVEC_FLAG_EOT | SPS_IOVEC_FLAG_NWD;
+	/*
+	 * on a balanced transaction, BAM will set the flags on the producer
+	 * pipe based on the flags set on the consumer pipe
+	 */
+	prod_flags = (dd->write_buf) ? 0 : cons_flags;
+
+	while (dd->tx_bytes_remaining > 0) {
+		bytes_sent = dd->cur_transfer->len - dd->tx_bytes_remaining;
+		bytes_to_send = min_t(u32, dd->tx_bytes_remaining
+						, SPI_MAX_TRFR_BTWN_RESETS);
+		n_words_xfr = DIV_ROUND_UP(bytes_to_send
+						, dd->bytes_per_word);
+
+		msm_spi_set_mx_counts(dd, n_words_xfr);
+
+		ret = msm_spi_set_state(dd, SPI_OP_STATE_RUN);
+		if (ret < 0) {
+			dev_err(dd->dev,
+				"%s: Failed to set QUP state to run",
+				__func__);
+			goto xfr_err;
+		}
+
+		/* enqueue read buffer in BAM */
+		if (dd->read_buf) {
+			ret = sps_transfer_one(dd->bam.prod.handle,
+				dd->cur_transfer->rx_dma + bytes_sent,
+				bytes_to_send, dd, prod_flags);
+			if (ret < 0) {
+				dev_err(dd->dev,
+				"%s: Failed to queue producer BAM transfer",
+				__func__);
+				goto xfr_err;
+			}
+		}
+
+		/* enqueue write buffer in BAM */
+		if (dd->write_buf) {
+			ret = sps_transfer_one(dd->bam.cons.handle,
+				dd->cur_transfer->tx_dma + bytes_sent,
+				bytes_to_send, dd, cons_flags);
+			if (ret < 0) {
+				dev_err(dd->dev,
+				"%s: Failed to queue consumer BAM transfer",
+				__func__);
+				goto xfr_err;
+			}
+		}
+
+		dd->tx_bytes_remaining -= bytes_to_send;
+
+		/* move to reset state after SPI_MAX_TRFR_BTWN_RESETS */
+		if (dd->tx_bytes_remaining > 0) {
+			if (!wait_for_completion_timeout(
+				&dd->transfer_complete, timeout)) {
+				dev_err(dd->dev,
+					"%s: SPI transaction timeout",
+					__func__);
+				dd->cur_msg->status = -EIO;
+				ret = -EIO;
+				goto xfr_err;
+			}
+			ret = msm_spi_set_state(dd, SPI_OP_STATE_RESET);
+			if (ret < 0) {
+				dev_err(dd->dev,
+					"%s: Failed to set QUP state to reset",
+					__func__);
+				goto xfr_err;
+			}
+			init_completion(&dd->transfer_complete);
+		}
+	}
+	return 0;
+
+xfr_err:
+	return ret;
 }
 
 static void msm_spi_setup_dm_transfer(struct msm_spi *dd)
@@ -767,7 +965,15 @@
 	return IRQ_HANDLED;
 }
 
-static int msm_spi_map_dma_buffers(struct msm_spi *dd)
+/**
+ * msm_spi_dma_map_buffers: prepares buffer for DMA transfer
+ * @return zero on success or negative error code
+ *
+ * calls dma_map_single() on the read/write buffers, effectively invalidating
+ * their cash entries. for For WR-WR and WR-RD transfers, allocates temporary
+ * buffer and copy the data to/from the client buffers
+ */
+static int msm_spi_dma_map_buffers(struct msm_spi *dd)
 {
 	struct device *dev;
 	struct spi_transfer *first_xfr;
@@ -847,7 +1053,7 @@
 	return ret;
 }
 
-static void msm_spi_unmap_dma_buffers(struct msm_spi *dd)
+static void msm_spi_dmov_unmap_buffers(struct msm_spi *dd)
 {
 	struct device *dev;
 	u32 offset;
@@ -914,56 +1120,190 @@
 	}
 }
 
+static void msm_spi_bam_unmap_buffers(struct msm_spi *dd)
+{
+	struct device *dev;
+
+	 /* mapped by client */
+	if (dd->cur_msg->is_dma_mapped)
+		return;
+
+	dev = &dd->cur_msg->spi->dev;
+	if (dd->cur_transfer->rx_buf)
+		dma_unmap_single(dev, dd->cur_transfer->rx_dma,
+				dd->cur_transfer->len,
+				DMA_FROM_DEVICE);
+
+	if (dd->cur_transfer->tx_buf)
+		dma_unmap_single(dev, dd->cur_transfer->tx_dma,
+				dd->cur_transfer->len,
+				DMA_TO_DEVICE);
+}
+
+static inline void msm_spi_dma_unmap_buffers(struct msm_spi *dd)
+{
+	if (dd->mode == SPI_DMOV_MODE)
+		msm_spi_dmov_unmap_buffers(dd);
+	else if (dd->mode == SPI_BAM_MODE)
+		msm_spi_bam_unmap_buffers(dd);
+}
+
 /**
- * msm_use_dm - decides whether to use data mover for this
- * 		transfer
+ * msm_spi_use_dma - decides whether to use Data-Mover or BAM for
+ * the given transfer
  * @dd:       device
  * @tr:       transfer
  *
- * Start using DM if:
- * 1. Transfer is longer than 3*block size.
- * 2. Buffers should be aligned to cache line.
- * 3. For WR-RD or WR-WR transfers, if condition (1) and (2) above are met.
+ * Start using DMA if:
+ * 1. Is supported by HW
+ * 2. Is not diabled by platfrom data
+ * 3. Transfer size is greater than 3*block size.
+ * 4. Buffers are aligned to cache line.
+ * 5. Bytes-per-word is 8,16 or 32.
   */
-static inline int msm_use_dm(struct msm_spi *dd, struct spi_transfer *tr,
-			     u8 bpw)
+static inline bool
+msm_spi_use_dma(struct msm_spi *dd, struct spi_transfer *tr, u8 bpw)
 {
-	u32 cache_line = dma_get_cache_alignment();
-
 	if (!dd->use_dma)
-		return 0;
+		return false;
+
+	/* check constraints from platform data */
+	if ((dd->qup_ver == SPI_QUP_VERSION_BFAM) && !dd->pdata->use_bam)
+		return false;
 
 	if (dd->cur_msg_len < 3*dd->input_block_size)
-		return 0;
+		return false;
 
 	if (dd->multi_xfr && !dd->read_len && !dd->write_len)
-		return 0;
+		return false;
 
-	if (tr->tx_buf) {
-		if (!IS_ALIGNED((size_t)tr->tx_buf, cache_line))
-			return 0;
-	}
-	if (tr->rx_buf) {
-		if (!IS_ALIGNED((size_t)tr->rx_buf, cache_line))
-			return 0;
+	if (dd->qup_ver == SPI_QUP_VERSION_NONE) {
+		u32 cache_line = dma_get_cache_alignment();
+
+		if (tr->tx_buf) {
+			if (!IS_ALIGNED((size_t)tr->tx_buf, cache_line))
+				return 0;
+		}
+		if (tr->rx_buf) {
+			if (!IS_ALIGNED((size_t)tr->rx_buf, cache_line))
+				return false;
+		}
+
+		if (tr->cs_change &&
+		   ((bpw != 8) || (bpw != 16) || (bpw != 32)))
+			return false;
 	}
 
-	if (tr->cs_change &&
-	   ((bpw != 8) || (bpw != 16) || (bpw != 32)))
-		return 0;
-	return 1;
+	return true;
+}
+
+/**
+ * msm_spi_set_transfer_mode: Chooses optimal transfer mode. Sets dd->mode and
+ * prepares to process a transfer.
+ */
+static void
+msm_spi_set_transfer_mode(struct msm_spi *dd, u8 bpw, u32 read_count)
+{
+	if (msm_spi_use_dma(dd, dd->cur_transfer, bpw)) {
+		if (dd->qup_ver) {
+			dd->mode = SPI_BAM_MODE;
+		} else {
+			dd->mode = SPI_DMOV_MODE;
+			if (dd->write_len && dd->read_len) {
+				dd->tx_bytes_remaining = dd->write_len;
+				dd->rx_bytes_remaining = dd->read_len;
+			}
+		}
+	} else {
+		dd->mode = SPI_FIFO_MODE;
+		if (dd->multi_xfr) {
+			dd->read_len = dd->cur_transfer->len;
+			dd->write_len = dd->cur_transfer->len;
+		}
+	}
+}
+
+/**
+ * msm_spi_set_qup_io_modes: prepares register QUP_IO_MODES to process a
+ * transfer
+ */
+static void msm_spi_set_qup_io_modes(struct msm_spi *dd)
+{
+	u32 spi_iom;
+	spi_iom = readl_relaxed(dd->base + SPI_IO_MODES);
+	/* Set input and output transfer mode: FIFO, DMOV, or BAM */
+	spi_iom &= ~(SPI_IO_M_INPUT_MODE | SPI_IO_M_OUTPUT_MODE);
+	spi_iom = (spi_iom | (dd->mode << OUTPUT_MODE_SHIFT));
+	spi_iom = (spi_iom | (dd->mode << INPUT_MODE_SHIFT));
+	/* Turn on packing for data mover */
+	if ((dd->mode == SPI_DMOV_MODE) || (dd->mode == SPI_BAM_MODE))
+		spi_iom |= SPI_IO_M_PACK_EN | SPI_IO_M_UNPACK_EN;
+	else
+		spi_iom &= ~(SPI_IO_M_PACK_EN | SPI_IO_M_UNPACK_EN);
+
+	/*if (dd->mode == SPI_BAM_MODE) {
+		spi_iom |= SPI_IO_C_NO_TRI_STATE;
+		spi_iom &= ~(SPI_IO_C_CS_SELECT | SPI_IO_C_CS_N_POLARITY);
+	}*/
+	writel_relaxed(spi_iom, dd->base + SPI_IO_MODES);
+}
+
+static u32 msm_spi_calc_spi_ioc_clk_polarity(u32 spi_ioc, u8 mode)
+{
+	if (mode & SPI_CPOL)
+		spi_ioc |= SPI_IO_C_CLK_IDLE_HIGH;
+	else
+		spi_ioc &= ~SPI_IO_C_CLK_IDLE_HIGH;
+	return spi_ioc;
+}
+
+/**
+ * msm_spi_set_spi_io_control: prepares register SPI_IO_CONTROL to process the
+ * next transfer
+ * @return the new set value of SPI_IO_CONTROL
+ */
+static u32 msm_spi_set_spi_io_control(struct msm_spi *dd)
+{
+	u32 spi_ioc, spi_ioc_orig, chip_select;
+
+	spi_ioc = readl_relaxed(dd->base + SPI_IO_CONTROL);
+	spi_ioc_orig = spi_ioc;
+	spi_ioc = msm_spi_calc_spi_ioc_clk_polarity(spi_ioc
+						, dd->cur_msg->spi->mode);
+	/* Set chip-select */
+	chip_select = dd->cur_msg->spi->chip_select << 2;
+	if ((spi_ioc & SPI_IO_C_CS_SELECT) != chip_select)
+		spi_ioc = (spi_ioc & ~SPI_IO_C_CS_SELECT) | chip_select;
+	if (!dd->cur_transfer->cs_change)
+		spi_ioc |= SPI_IO_C_MX_CS_MODE;
+
+	if (spi_ioc != spi_ioc_orig)
+		writel_relaxed(spi_ioc, dd->base + SPI_IO_CONTROL);
+
+	return spi_ioc;
+}
+
+/**
+ * msm_spi_set_qup_op_mask: prepares register QUP_OPERATIONAL_MASK to process
+ * the next transfer
+ */
+static void msm_spi_set_qup_op_mask(struct msm_spi *dd)
+{
+	/* mask INPUT and OUTPUT service flags in to prevent IRQs on FIFO status
+	 * change in BAM mode */
+	u32 mask = (dd->mode == SPI_BAM_MODE) ?
+		QUP_OP_MASK_OUTPUT_SERVICE_FLAG | QUP_OP_MASK_INPUT_SERVICE_FLAG
+		: 0;
+	writel_relaxed(mask, dd->base + QUP_OPERATIONAL_MASK);
 }
 
 static void msm_spi_process_transfer(struct msm_spi *dd)
 {
 	u8  bpw;
-	u32 spi_ioc;
-	u32 spi_iom;
-	u32 spi_ioc_orig;
 	u32 max_speed;
-	u32 chip_select;
 	u32 read_count;
 	u32 timeout;
+	u32 spi_ioc;
 	u32 int_loopback = 0;
 
 	dd->tx_bytes_remaining = dd->cur_msg_len;
@@ -987,6 +1327,10 @@
 	if (!dd->clock_speed || max_speed != dd->clock_speed)
 		msm_spi_clock_set(dd, max_speed);
 
+	timeout = 100 * msecs_to_jiffies(
+			DIV_ROUND_UP(dd->cur_msg_len * 8,
+			DIV_ROUND_UP(max_speed, MSEC_PER_SEC)));
+
 	read_count = DIV_ROUND_UP(dd->cur_msg_len, dd->bytes_per_word);
 	if (dd->cur_msg->spi->mode & SPI_LOOP)
 		int_loopback = 1;
@@ -1004,60 +1348,24 @@
 			__func__);
 		return;
 	}
-	if (!msm_use_dm(dd, dd->cur_transfer, bpw)) {
-		dd->mode = SPI_FIFO_MODE;
-		if (dd->multi_xfr) {
-			dd->read_len = dd->cur_transfer->len;
-			dd->write_len = dd->cur_transfer->len;
-		}
-		/* read_count cannot exceed fifo_size, and only one READ COUNT
-		   interrupt is generated per transaction, so for transactions
-		   larger than fifo size READ COUNT must be disabled.
-		   For those transactions we usually move to Data Mover mode.
-		*/
-		if (read_count <= dd->input_fifo_size) {
-			writel_relaxed(read_count,
-				       dd->base + SPI_MX_READ_COUNT);
-			msm_spi_set_write_count(dd, read_count);
-		} else {
-			writel_relaxed(0, dd->base + SPI_MX_READ_COUNT);
-			msm_spi_set_write_count(dd, 0);
-		}
-	} else {
-		dd->mode = SPI_DMOV_MODE;
-		if (dd->write_len && dd->read_len) {
-			dd->tx_bytes_remaining = dd->write_len;
-			dd->rx_bytes_remaining = dd->read_len;
-		}
-	}
 
-	/* Write mode - fifo or data mover*/
-	spi_iom = readl_relaxed(dd->base + SPI_IO_MODES);
-	spi_iom &= ~(SPI_IO_M_INPUT_MODE | SPI_IO_M_OUTPUT_MODE);
-	spi_iom = (spi_iom | (dd->mode << OUTPUT_MODE_SHIFT));
-	spi_iom = (spi_iom | (dd->mode << INPUT_MODE_SHIFT));
-	/* Turn on packing for data mover */
-	if (dd->mode == SPI_DMOV_MODE)
-		spi_iom |= SPI_IO_M_PACK_EN | SPI_IO_M_UNPACK_EN;
-	else
-		spi_iom &= ~(SPI_IO_M_PACK_EN | SPI_IO_M_UNPACK_EN);
-	writel_relaxed(spi_iom, dd->base + SPI_IO_MODES);
+	if (msm_spi_set_state(dd, SPI_OP_STATE_RESET))
+		dev_err(dd->dev,
+			"%s: Error setting QUP to reset-state",
+			__func__);
 
-	msm_spi_set_config(dd, bpw);
-
-	spi_ioc = readl_relaxed(dd->base + SPI_IO_CONTROL);
-	spi_ioc_orig = spi_ioc;
-	if (dd->cur_msg->spi->mode & SPI_CPOL)
-		spi_ioc |= SPI_IO_C_CLK_IDLE_HIGH;
-	else
-		spi_ioc &= ~SPI_IO_C_CLK_IDLE_HIGH;
-	chip_select = dd->cur_msg->spi->chip_select << 2;
-	if ((spi_ioc & SPI_IO_C_CS_SELECT) != chip_select)
-		spi_ioc = (spi_ioc & ~SPI_IO_C_CS_SELECT) | chip_select;
-	if (!dd->cur_transfer->cs_change)
-		spi_ioc |= SPI_IO_C_MX_CS_MODE;
-	if (spi_ioc != spi_ioc_orig)
-		writel_relaxed(spi_ioc, dd->base + SPI_IO_CONTROL);
+	msm_spi_set_transfer_mode(dd, bpw, read_count);
+	msm_spi_set_mx_counts(dd, read_count);
+	if ((dd->mode == SPI_BAM_MODE) || (dd->mode == SPI_DMOV_MODE))
+		if (msm_spi_dma_map_buffers(dd) < 0) {
+			pr_err("Mapping DMA buffers\n");
+			return;
+		}
+	msm_spi_set_qup_io_modes(dd);
+	msm_spi_set_spi_config(dd, bpw);
+	msm_spi_set_qup_config(dd, bpw);
+	spi_ioc = msm_spi_set_spi_io_control(dd);
+	msm_spi_set_qup_op_mask(dd);
 
 	if (dd->mode == SPI_DMOV_MODE) {
 		msm_spi_setup_dm_transfer(dd);
@@ -1071,27 +1379,35 @@
 		if (msm_spi_prepare_for_write(dd))
 			goto transfer_end;
 		msm_spi_start_write(dd, read_count);
+	} else if (dd->mode == SPI_BAM_MODE) {
+		if ((msm_spi_bam_begin_transfer(dd, timeout, bpw)) < 0)
+			dev_err(dd->dev, "%s: BAM transfer setup failed\n",
+				__func__);
 	}
 
-	/* Only enter the RUN state after the first word is written into
-	   the output FIFO. Otherwise, the output FIFO EMPTY interrupt
-	   might fire before the first word is written resulting in a
-	   possible race condition.
+	/*
+	 * On BAM mode, current state here is run.
+	 * Only enter the RUN state after the first word is written into
+	 * the output FIFO. Otherwise, the output FIFO EMPTY interrupt
+	 * might fire before the first word is written resulting in a
+	 * possible race condition.
 	 */
-	if (msm_spi_set_state(dd, SPI_OP_STATE_RUN))
-		goto transfer_end;
-
-	timeout = 100 * msecs_to_jiffies(
-	      DIV_ROUND_UP(dd->cur_msg_len * 8,
-		 DIV_ROUND_UP(max_speed, MSEC_PER_SEC)));
+	if (dd->mode != SPI_BAM_MODE)
+		if (msm_spi_set_state(dd, SPI_OP_STATE_RUN)) {
+			dev_warn(dd->dev,
+				"%s: Failed to set QUP to run-state. Mode:%d",
+				__func__, dd->mode);
+			goto transfer_end;
+		}
 
 	/* Assume success, this might change later upon transaction result */
 	dd->cur_msg->status = 0;
 	do {
 		if (!wait_for_completion_timeout(&dd->transfer_complete,
 						 timeout)) {
-				dev_err(dd->dev, "%s: SPI transaction "
-						 "timeout\n", __func__);
+				dev_err(dd->dev,
+					"%s: SPI transaction timeout\n",
+					__func__);
 				dd->cur_msg->status = -EIO;
 				if (dd->mode == SPI_DMOV_MODE) {
 					msm_dmov_flush(dd->tx_dma_chan, 1);
@@ -1101,9 +1417,9 @@
 		}
 	} while (msm_spi_dm_send_next(dd));
 
+	msm_spi_udelay(dd->cur_transfer->delay_usecs);
 transfer_end:
-	if (dd->mode == SPI_DMOV_MODE)
-		msm_spi_unmap_dma_buffers(dd);
+	msm_spi_dma_unmap_buffers(dd);
 	dd->mode = SPI_MODE_NONE;
 
 	msm_spi_set_state(dd, SPI_OP_STATE_RESET);
@@ -1194,6 +1510,8 @@
 	int xfrs_grped = 0;
 	int cs_num;
 	int rc;
+	bool xfer_delay = false;
+	struct spi_transfer *tr;
 
 	dd->write_xfr_cnt = dd->read_xfr_cnt = 0;
 	cs_num = dd->cur_msg->spi->chip_select;
@@ -1211,8 +1529,21 @@
 		dd->cs_gpios[cs_num].valid = 1;
 	}
 
-	if (dd->qup_ver) {
-		write_force_cs(dd, 0);
+	list_for_each_entry(tr,
+				&dd->cur_msg->transfers,
+				transfer_list) {
+		if (tr->delay_usecs) {
+			dev_info(dd->dev, "SPI slave requests delay per txn :%d",
+					tr->delay_usecs);
+			xfer_delay = true;
+			break;
+		}
+	}
+
+	/* Don't combine xfers if delay is needed after every xfer */
+	if (dd->qup_ver || xfer_delay) {
+		if (dd->qup_ver)
+			write_force_cs(dd, 0);
 		list_for_each_entry(dd->cur_transfer,
 				&dd->cur_msg->transfers,
 				transfer_list) {
@@ -1224,9 +1555,10 @@
 						struct spi_transfer,
 						transfer_list);
 
-				if (t->cs_change == nxt->cs_change)
+				if (dd->qup_ver &&
+					t->cs_change == nxt->cs_change)
 					write_force_cs(dd, 1);
-				else
+				else if (dd->qup_ver)
 					write_force_cs(dd, 0);
 			}
 
@@ -1266,10 +1598,10 @@
 			 * WR-WR or WR-RD transfers
 			 */
 			if ((!dd->cur_msg->is_dma_mapped) &&
-			    (msm_use_dm(dd, dd->cur_transfer,
+			    (msm_spi_use_dma(dd, dd->cur_transfer,
 					dd->cur_transfer->bits_per_word))) {
 				/* Mapping of DMA buffers */
-				int ret = msm_spi_map_dma_buffers(dd);
+				int ret = msm_spi_dma_map_buffers(dd);
 				if (ret < 0) {
 					dd->cur_msg->status = ret;
 					goto error;
@@ -1474,22 +1806,13 @@
 		spi_ioc |= mask;
 	else
 		spi_ioc &= ~mask;
-	if (spi->mode & SPI_CPOL)
-		spi_ioc |= SPI_IO_C_CLK_IDLE_HIGH;
-	else
-		spi_ioc &= ~SPI_IO_C_CLK_IDLE_HIGH;
+	spi_ioc = msm_spi_calc_spi_ioc_clk_polarity(spi_ioc, spi->mode);
 
 	writel_relaxed(spi_ioc, dd->base + SPI_IO_CONTROL);
 
 	spi_config = readl_relaxed(dd->base + SPI_CONFIG);
-	if (spi->mode & SPI_LOOP)
-		spi_config |= SPI_CFG_LOOPBACK;
-	else
-		spi_config &= ~SPI_CFG_LOOPBACK;
-	if (spi->mode & SPI_CPHA)
-		spi_config &= ~SPI_CFG_INPUT_FIRST;
-	else
-		spi_config |= SPI_CFG_INPUT_FIRST;
+	spi_config = msm_spi_calc_spi_config_loopback_and_input_first(
+							spi_config, spi->mode);
 	writel_relaxed(spi_config, dd->base + SPI_CONFIG);
 
 	/* Ensure previous write completed before disabling the clocks */
@@ -1730,7 +2053,7 @@
 			  roundup(dd->burst_size, cache_line))*2;
 }
 
-static void msm_spi_teardown_dma(struct msm_spi *dd)
+static void msm_spi_dmov_teardown(struct msm_spi *dd)
 {
 	int limit = 0;
 
@@ -1749,7 +2072,171 @@
 	dd->tx_padding = dd->rx_padding = NULL;
 }
 
-static __init int msm_spi_init_dma(struct msm_spi *dd)
+static void msm_spi_bam_pipe_teardown(struct msm_spi *dd,
+					enum msm_spi_pipe_direction pipe_dir)
+{
+	struct msm_spi_bam_pipe *pipe = (pipe_dir == SPI_BAM_CONSUMER_PIPE) ?
+					(&dd->bam.prod) : (&dd->bam.cons);
+	if (!pipe->teardown_required)
+		return;
+
+	sps_disconnect(pipe->handle);
+	dma_free_coherent(dd->dev, pipe->config.desc.size,
+		pipe->config.desc.base, pipe->config.desc.phys_base);
+	sps_free_endpoint(pipe->handle);
+	pipe->handle = 0;
+	pipe->teardown_required = false;
+}
+
+static int msm_spi_bam_pipe_init(struct msm_spi *dd,
+					enum msm_spi_pipe_direction pipe_dir)
+{
+	int rc = 0;
+	struct sps_pipe *pipe_handle;
+	struct sps_register_event event = {0};
+	struct msm_spi_bam_pipe *pipe = (pipe_dir == SPI_BAM_CONSUMER_PIPE) ?
+					(&dd->bam.prod) : (&dd->bam.cons);
+	struct sps_connect *pipe_conf = &pipe->config;
+
+	pipe->handle = 0;
+	pipe_handle = sps_alloc_endpoint();
+	if (!pipe_handle) {
+		dev_err(dd->dev, "%s: Failed to allocate BAM endpoint\n"
+								, __func__);
+		return -ENOMEM;
+	}
+
+	memset(pipe_conf, 0, sizeof(*pipe_conf));
+	rc = sps_get_config(pipe_handle, pipe_conf);
+	if (rc) {
+		dev_err(dd->dev, "%s: Failed to get BAM pipe config\n"
+			, __func__);
+		goto config_err;
+	}
+
+	if (pipe_dir == SPI_BAM_CONSUMER_PIPE) {
+		pipe_conf->source          = dd->bam.handle;
+		pipe_conf->destination     = SPS_DEV_HANDLE_MEM;
+		pipe_conf->mode            = SPS_MODE_SRC;
+		pipe_conf->src_pipe_index  =
+					dd->pdata->bam_producer_pipe_index;
+		pipe_conf->dest_pipe_index = 0;
+	} else {
+		pipe_conf->source          = SPS_DEV_HANDLE_MEM;
+		pipe_conf->destination     = dd->bam.handle;
+		pipe_conf->mode            = SPS_MODE_DEST;
+		pipe_conf->src_pipe_index  = 0;
+		pipe_conf->dest_pipe_index =
+					dd->pdata->bam_consumer_pipe_index;
+	}
+	pipe_conf->options = SPS_O_EOT | SPS_O_AUTO_ENABLE;
+	pipe_conf->desc.size = SPI_BAM_MAX_DESC_NUM * sizeof(struct sps_iovec);
+	pipe_conf->desc.base = dma_alloc_coherent(dd->dev,
+				pipe_conf->desc.size,
+				&pipe_conf->desc.phys_base,
+				GFP_KERNEL);
+	if (!pipe_conf->desc.base) {
+		dev_err(dd->dev, "%s: Failed allocate BAM pipe memory"
+			, __func__);
+		rc = -ENOMEM;
+		goto config_err;
+	}
+
+	memset(pipe_conf->desc.base, 0x00, pipe_conf->desc.size);
+
+	rc = sps_connect(pipe_handle, pipe_conf);
+	if (rc) {
+		dev_err(dd->dev, "%s: Failed to connect BAM pipe", __func__);
+		goto connect_err;
+	}
+
+	event.mode      = SPS_TRIGGER_WAIT;
+	event.options   = SPS_O_EOT;
+	event.xfer_done = &dd->transfer_complete;
+	event.user      = (void *)dd;
+	rc = sps_register_event(pipe_handle, &event);
+	if (rc) {
+		dev_err(dd->dev, "%s: Failed to register BAM EOT event",
+			__func__);
+		goto register_err;
+	}
+
+	pipe->handle = pipe_handle;
+	pipe->teardown_required = true;
+	return 0;
+
+register_err:
+	sps_disconnect(pipe_handle);
+connect_err:
+	dma_free_coherent(dd->dev, pipe_conf->desc.size,
+		pipe_conf->desc.base, pipe_conf->desc.phys_base);
+config_err:
+	sps_free_endpoint(pipe_handle);
+
+	return rc;
+}
+
+static void msm_spi_bam_teardown(struct msm_spi *dd)
+{
+	msm_spi_bam_pipe_teardown(dd, SPI_BAM_PRODUCER_PIPE);
+	msm_spi_bam_pipe_teardown(dd, SPI_BAM_CONSUMER_PIPE);
+
+	if (dd->bam.deregister_required) {
+		sps_deregister_bam_device(dd->bam.handle);
+		dd->bam.deregister_required = false;
+	}
+}
+
+static int msm_spi_bam_init(struct msm_spi *dd)
+{
+	struct sps_bam_props bam_props = {0};
+	u32 bam_handle;
+	int rc = 0;
+
+	rc = sps_phy2h(dd->bam.phys_addr, &bam_handle);
+	if (rc || !bam_handle) {
+		bam_props.phys_addr = dd->bam.phys_addr;
+		bam_props.virt_addr = dd->bam.base;
+		bam_props.irq       = dd->bam.irq;
+		bam_props.manage    = SPS_BAM_MGR_LOCAL;
+		bam_props.summing_threshold = 0x10;
+
+		rc = sps_register_bam_device(&bam_props, &bam_handle);
+		if (rc) {
+			dev_err(dd->dev,
+				"%s: Failed to register BAM device",
+				__func__);
+			return rc;
+		}
+		dd->bam.deregister_required = true;
+	}
+
+	dd->bam.handle = bam_handle;
+
+	rc = msm_spi_bam_pipe_init(dd, SPI_BAM_PRODUCER_PIPE);
+	if (rc) {
+		dev_err(dd->dev,
+			"%s: Failed to init producer BAM-pipe",
+			__func__);
+		goto bam_init_error;
+	}
+
+	rc = msm_spi_bam_pipe_init(dd, SPI_BAM_CONSUMER_PIPE);
+	if (rc) {
+		dev_err(dd->dev,
+			"%s: Failed to init consumer BAM-pipe",
+			__func__);
+		goto bam_init_error;
+	}
+
+	return 0;
+
+bam_init_error:
+	msm_spi_bam_teardown(dd);
+	return rc;
+}
+
+static __init int msm_spi_dmov_init(struct msm_spi *dd)
 {
 	dmov_box *box;
 	u32 cache_line = dma_get_cache_alignment();
@@ -1811,10 +2298,15 @@
 	return 0;
 }
 
-struct msm_spi_platform_data *msm_spi_dt_to_pdata(struct platform_device *pdev)
+/**
+ * msm_spi_dt_to_pdata: copy device-tree data to platfrom data struct
+ */
+struct msm_spi_platform_data *
+__init msm_spi_dt_to_pdata(struct platform_device *pdev)
 {
 	struct device_node *node = pdev->dev.of_node;
 	struct msm_spi_platform_data *pdata;
+	int rc;
 
 	pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
 	if (!pdata) {
@@ -1827,9 +2319,76 @@
 	of_property_read_u32(node, "infinite_mode",
 			&pdata->infinite_mode);
 
+	pdata->ver_reg_exists = of_property_read_bool(node
+						, "qcom,ver-reg-exists");
+
+	pdata->use_bam = of_property_read_bool(node, "qcom,use-bam");
+
+	if (pdata->use_bam) {
+		rc = of_property_read_u32(node, "qcom,bam-consumer-pipe-index",
+					&pdata->bam_consumer_pipe_index);
+		if (rc) {
+			dev_warn(&pdev->dev,
+			"missing qcom,bam-consumer-pipe-index entry in device-tree\n");
+			pdata->use_bam = false;
+		}
+
+		rc = of_property_read_u32(node, "qcom,bam-producer-pipe-index",
+					&pdata->bam_producer_pipe_index);
+		if (rc) {
+			dev_warn(&pdev->dev,
+			"missing qcom,bam-producer-pipe-index entry in device-tree\n");
+			pdata->use_bam = false;
+		}
+	}
 	return pdata;
 }
 
+static int __init msm_spi_get_qup_hw_ver(struct device *dev, struct msm_spi *dd)
+{
+	u32 data = readl_relaxed(dd->base + QUP_HARDWARE_VER);
+	return (data >= QUP_HARDWARE_VER_2_1_1) ? SPI_QUP_VERSION_BFAM
+						: SPI_QUP_VERSION_NONE;
+}
+
+static int __init msm_spi_bam_get_resources(struct msm_spi *dd,
+	struct platform_device *pdev, struct spi_master *master)
+{
+	struct resource *resource;
+	size_t bam_mem_size;
+
+	resource = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+						"spi_bam_physical");
+	if (!resource) {
+		dev_warn(&pdev->dev,
+			"%s: Missing spi_bam_physical entry in DT",
+			__func__);
+		return -ENXIO;
+	}
+
+	dd->bam.phys_addr = resource->start;
+	bam_mem_size = resource_size(resource);
+	dd->bam.base = devm_ioremap(&pdev->dev, dd->bam.phys_addr,
+					bam_mem_size);
+	if (!dd->bam.base) {
+		dev_warn(&pdev->dev,
+			"%s: Failed to ioremap(spi_bam_physical)",
+			__func__);
+		return -ENXIO;
+	}
+
+	dd->bam.irq = platform_get_irq_byname(pdev, "spi_bam_irq");
+	if (dd->bam.irq < 0) {
+		dev_warn(&pdev->dev, "%s: Missing spi_bam_irq entry in DT",
+			__func__);
+		return -EINVAL;
+	}
+
+	dd->dma_init = msm_spi_bam_init;
+	dd->dma_teardown = msm_spi_bam_teardown;
+	return 0;
+}
+
 static int __init msm_spi_probe(struct platform_device *pdev)
 {
 	struct spi_master      *master;
@@ -1926,21 +2485,39 @@
 				goto skip_dma_resources;
 			}
 		}
-		resource = platform_get_resource(pdev, IORESOURCE_DMA, 0);
-		if (resource) {
-			dd->rx_dma_chan = resource->start;
-			dd->tx_dma_chan = resource->end;
-			resource = platform_get_resource(pdev, IORESOURCE_DMA,
-							1);
-			if (!resource) {
-				rc = -ENXIO;
-				goto err_probe_res;
-			}
+		if (dd->qup_ver == SPI_QUP_VERSION_NONE) {
+			resource = platform_get_resource(pdev,
+							IORESOURCE_DMA, 0);
+			if (resource) {
+				dd->rx_dma_chan = resource->start;
+				dd->tx_dma_chan = resource->end;
+				resource = platform_get_resource(pdev,
+							IORESOURCE_DMA, 1);
+				if (!resource) {
+					rc = -ENXIO;
+					goto err_probe_res;
+				}
 
-			dd->rx_dma_crci = resource->start;
-			dd->tx_dma_crci = resource->end;
+				dd->rx_dma_crci = resource->start;
+				dd->tx_dma_crci = resource->end;
+				dd->use_dma = 1;
+				master->dma_alignment =
+						dma_get_cache_alignment();
+				dd->dma_init = msm_spi_dmov_init ;
+				dd->dma_teardown = msm_spi_dmov_teardown;
+			}
+		} else {
+			if (!dd->pdata->use_bam)
+				goto skip_dma_resources;
+
+			rc = msm_spi_bam_get_resources(dd, pdev, master);
+			if (rc) {
+				dev_warn(dd->dev,
+					"%s: Faild to get BAM resources",
+					__func__);
+				goto skip_dma_resources;
+			}
 			dd->use_dma = 1;
-			master->dma_alignment =	dma_get_cache_alignment();
 		}
 	}
 
@@ -1968,6 +2545,15 @@
 		goto err_probe_reqmem;
 	}
 
+	if (pdata && pdata->ver_reg_exists) {
+		enum msm_spi_qup_version ver =
+					msm_spi_get_qup_hw_ver(&pdev->dev, dd);
+		if (dd->qup_ver != ver)
+			dev_warn(&pdev->dev,
+			"%s: HW version different then initially assumed by probe",
+			__func__);
+	}
+
 	if (pdata && pdata->rsl_id) {
 		struct remote_mutex_id rmid;
 		rmid.r_spinlock_id = pdata->rsl_id;
@@ -1984,7 +2570,7 @@
 		dd->use_rlock = 1;
 		dd->pm_lat = pdata->pm_lat;
 		pm_qos_add_request(&qos_req_list, PM_QOS_CPU_DMA_LATENCY,
-					PM_QOS_DEFAULT_VALUE);
+						PM_QOS_DEFAULT_VALUE);
 	}
 
 	mutex_lock(&dd->core_lock);
@@ -2026,13 +2612,16 @@
 	}
 
 	pclk_enabled = 1;
-	rc = msm_spi_configure_gsbi(dd, pdev);
-	if (rc)
-		goto err_probe_gsbi;
+	/* GSBI dose not exists on B-family MSM-chips */
+	if (dd->qup_ver != SPI_QUP_VERSION_BFAM) {
+		rc = msm_spi_configure_gsbi(dd, pdev);
+		if (rc)
+			goto err_probe_gsbi;
+	}
 
 	msm_spi_calculate_fifo_size(dd);
 	if (dd->use_dma) {
-		rc = msm_spi_init_dma(dd);
+		rc = dd->dma_init(dd);
 		if (rc)
 			goto err_probe_dma;
 	}
@@ -2091,7 +2680,8 @@
 err_probe_reg_master:
 err_probe_irq:
 err_probe_state:
-	msm_spi_teardown_dma(dd);
+	if (dd->dma_teardown)
+		dd->dma_teardown(dd);
 err_probe_dma:
 err_probe_gsbi:
 	if (pclk_enabled)
@@ -2174,8 +2764,8 @@
 	spi_debugfs_exit(dd);
 	sysfs_remove_group(&pdev->dev.kobj, &dev_attr_grp);
 
-	msm_spi_teardown_dma(dd);
-
+	if (dd->dma_teardown)
+		dd->dma_teardown(dd);
 	clk_put(dd->clk);
 	clk_put(dd->pclk);
 	destroy_workqueue(dd->workqueue);
diff --git a/drivers/spi/spi_qsd.h b/drivers/spi/spi_qsd.h
index a0dee34..62f1830 100644
--- a/drivers/spi/spi_qsd.h
+++ b/drivers/spi/spi_qsd.h
@@ -41,8 +41,13 @@
 
 #define GSBI_CTRL_REG                 0x0
 #define GSBI_SPI_CONFIG               0x30
+/* B-family only registers */
 #define QUP_HARDWARE_VER              0x0030
+#define QUP_HARDWARE_VER_2_1_1        0X20010001
 #define QUP_OPERATIONAL_MASK          0x0028
+#define QUP_OP_MASK_OUTPUT_SERVICE_FLAG 0x100
+#define QUP_OP_MASK_INPUT_SERVICE_FLAG  0x200
+
 #define QUP_ERROR_FLAGS               0x0308
 
 #define SPI_CONFIG                    QSD_REG(0x0000) QUP_REG(0x0300)
@@ -73,6 +78,7 @@
 #define SPI_NO_OUTPUT                 0x00000040
 #define SPI_CFG_LOOPBACK              0x00000100
 #define SPI_CFG_N                     0x0000001F
+#define SPI_EN_EXT_OUT_FLAG           0x00010000
 
 /* SPI_IO_CONTROL fields */
 #define SPI_IO_C_FORCE_CS             0x00000800
@@ -148,8 +154,18 @@
 /* Data Mover commands should be aligned to 64 bit(8 bytes) */
 #define DM_BYTE_ALIGN                 8
 
-#define SPI_QUP_VERSION_NONE      0x0
-#define SPI_QUP_VERSION_BFAM      0x2
+enum msm_spi_qup_version {
+	SPI_QUP_VERSION_NONE    = 0x0,
+	SPI_QUP_VERSION_BFAM    = 0x2,
+};
+
+enum msm_spi_pipe_direction {
+	SPI_BAM_CONSUMER_PIPE   = 0x0,
+	SPI_BAM_PRODUCER_PIPE   = 0x1,
+};
+
+#define SPI_BAM_MAX_DESC_NUM      32
+#define SPI_MAX_TRFR_BTWN_RESETS  ((64 * 1024) - 16)  /* 64KB - 16byte */
 
 static char const * const spi_rsrcs[] = {
 	"spi_clk",
@@ -231,6 +247,22 @@
 };
 #endif
 
+struct msm_spi_bam_pipe {
+	struct sps_pipe         *handle;
+	struct sps_connect       config;
+	bool                     teardown_required;
+};
+
+struct msm_spi_bam {
+	void __iomem            *base;
+	u32                      phys_addr;
+	u32                      handle;
+	u32                      irq;
+	struct msm_spi_bam_pipe  prod;
+	struct msm_spi_bam_pipe  cons;
+	bool                     deregister_required;
+};
+
 struct msm_spi {
 	u8                      *read_buf;
 	const u8                *write_buf;
@@ -244,8 +276,8 @@
 	struct spi_message      *cur_msg;
 	struct spi_transfer     *cur_transfer;
 	struct completion        transfer_complete;
-	struct clk              *clk;
-	struct clk              *pclk;
+	struct clk              *clk;    /* core clock */
+	struct clk              *pclk;   /* interface clock */
 	unsigned long            mem_phys_addr;
 	size_t                   mem_size;
 	int                      input_fifo_size;
@@ -273,6 +305,9 @@
 	int                      tx_dma_crci;
 	int                      rx_dma_chan;
 	int                      rx_dma_crci;
+	int                      (*dma_init) (struct msm_spi *dd);
+	void                     (*dma_teardown) (struct msm_spi *dd);
+	struct msm_spi_bam       bam;
 	/* Data Mover Commands */
 	struct spi_dmov_cmd      *tx_dmov_cmd;
 	struct spi_dmov_cmd      *rx_dmov_cmd;
@@ -321,7 +356,7 @@
 	int                      spi_gpios[ARRAY_SIZE(spi_rsrcs)];
 	/* SPI CS GPIOs for each slave */
 	struct spi_cs_gpio       cs_gpios[ARRAY_SIZE(spi_cs_rsrcs)];
-	int                      qup_ver;
+	enum msm_spi_qup_version qup_ver;
 	int			 max_trfr_len;
 };
 
@@ -333,7 +368,7 @@
 				    enum msm_spi_state state);
 static void msm_spi_write_word_to_fifo(struct msm_spi *dd);
 static inline void msm_spi_write_rmn_to_fifo(struct msm_spi *dd);
-static inline irqreturn_t msm_spi_qup_irq(int irq, void *dev_id);
+static irqreturn_t msm_spi_qup_irq(int irq, void *dev_id);
 
 #if defined(CONFIG_SPI_QSD) || defined(CONFIG_SPI_QSD_MODULE)
 static inline void msm_spi_disable_irqs(struct msm_spi *dd)
@@ -385,7 +420,7 @@
 static inline void msm_spi_ack_clk_err(struct msm_spi *dd) {}
 static inline void msm_spi_set_qup_config(struct msm_spi *dd, int bpw) {}
 
-static inline int msm_spi_prepare_for_write(struct msm_spi *dd) { return 0; }
+static inline int  msm_spi_prepare_for_write(struct msm_spi *dd) { return 0; }
 static inline void msm_spi_start_write(struct msm_spi *dd, u32 read_count)
 {
 	msm_spi_write_word_to_fifo(dd);
@@ -441,16 +476,18 @@
 	writel_relaxed(QUP_ERR_MASK, dd->base + QUP_ERROR_FLAGS);
 }
 
-static inline void msm_spi_add_configs(struct msm_spi *dd, u32 *config, int n);
+static inline void
+msm_spi_set_bpw_and_no_io_flags(struct msm_spi *dd, u32 *config, int n);
 
-/* QUP has no_input, no_output, and N bits at QUP_CONFIG */
+/**
+ * msm_spi_set_qup_config: set QUP_CONFIG to no_input, no_output, and N bits
+ */
 static inline void msm_spi_set_qup_config(struct msm_spi *dd, int bpw)
 {
 	u32 qup_config = readl_relaxed(dd->base + QUP_CONFIG);
 
-	msm_spi_add_configs(dd, &qup_config, bpw-1);
-	writel_relaxed(qup_config | QUP_CONFIG_SPI_MODE,
-		       dd->base + QUP_CONFIG);
+	msm_spi_set_bpw_and_no_io_flags(dd, &qup_config, bpw-1);
+	writel_relaxed(qup_config | QUP_CONFIG_SPI_MODE, dd->base + QUP_CONFIG);
 }
 
 static inline int msm_spi_prepare_for_write(struct msm_spi *dd)
@@ -482,12 +519,22 @@
 
 static inline void msm_spi_enable_error_flags(struct msm_spi *dd)
 {
-	writel_relaxed(0x00000078, dd->base + SPI_ERROR_FLAGS_EN);
+	if (dd->qup_ver == SPI_QUP_VERSION_BFAM)
+		writel_relaxed(
+			SPI_ERR_CLK_UNDER_RUN_ERR | SPI_ERR_CLK_OVER_RUN_ERR,
+			dd->base + SPI_ERROR_FLAGS_EN);
+	else
+		writel_relaxed(0x00000078, dd->base + SPI_ERROR_FLAGS_EN);
 }
 
 static inline void msm_spi_clear_error_flags(struct msm_spi *dd)
 {
-	writel_relaxed(0x0000007C, dd->base + SPI_ERROR_FLAGS);
+	if (dd->qup_ver == SPI_QUP_VERSION_BFAM)
+		writel_relaxed(
+			SPI_ERR_CLK_UNDER_RUN_ERR | SPI_ERR_CLK_OVER_RUN_ERR,
+			dd->base + SPI_ERROR_FLAGS);
+	else
+		writel_relaxed(0x0000007C, dd->base + SPI_ERROR_FLAGS);
 }
 
 #endif
diff --git a/drivers/spmi/Makefile b/drivers/spmi/Makefile
index becd823..2161fac 100644
--- a/drivers/spmi/Makefile
+++ b/drivers/spmi/Makefile
@@ -4,3 +4,7 @@
 obj-$(CONFIG_SPMI)			+= spmi.o spmi-resources.o
 obj-$(CONFIG_SPMI_MSM_PMIC_ARB)		+= spmi-pmic-arb.o
 obj-$(CONFIG_MSM_QPNP_INT)		+= qpnp-int.o
+
+ifdef CONFIG_DEBUG_FS
+obj-$(CONFIG_SPMI)			+= spmi-dbgfs.o
+endif
diff --git a/drivers/spmi/spmi-dbgfs.c b/drivers/spmi/spmi-dbgfs.c
new file mode 100644
index 0000000..a23f945
--- /dev/null
+++ b/drivers/spmi/spmi-dbgfs.c
@@ -0,0 +1,725 @@
+/* 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 Debug-fs support.
+ *
+ * Hierarchy schema:
+ * /sys/kernel/debug/spmi
+ *        /help			-- static help text
+ *        /spmi-0
+ *        /spmi-0/address	-- Starting register address for reads or writes
+ *        /spmi-0/count		-- number of registers to read (only on read)
+ *        /spmi-0/data		-- Triggers the SPMI formatted read.
+ *        /spmi-0/data_raw	-- Triggers the SPMI raw read or write
+ *        /spmi-#
+ */
+
+#define DEBUG
+#define pr_fmt(fmt) "%s:%d: " fmt, __func__, __LINE__
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/spinlock.h>
+#include <linux/string.h>
+#include <linux/debugfs.h>
+#include <linux/spmi.h>
+#include <linux/ctype.h>
+
+#define ADDR_LEN	 6	/* 5 byte address + 1 space character */
+#define CHARS_PER_ITEM   3	/* Format is 'XX ' */
+#define ITEMS_PER_LINE	16	/* 16 data items per line */
+#define MAX_LINE_LENGTH  (ADDR_LEN + (ITEMS_PER_LINE * CHARS_PER_ITEM) + 1)
+#define MAX_REG_PER_TRANSACTION	(8)
+
+static const char *DFS_ROOT_NAME	= "spmi";
+static const mode_t DFS_MODE = S_IRUSR | S_IWUSR;
+
+/* Log buffer */
+struct spmi_log_buffer {
+	u32  rpos;	/* Current 'read' position in buffer */
+	u32  wpos;	/* Current 'write' position in buffer */
+	u32  len;	/* Length of the buffer */
+	char data[0];	/* Log buffer */
+};
+
+/* SPMI controller specific data */
+struct spmi_ctrl_data {
+	u32 cnt;
+	u32 addr;
+	struct list_head node;
+	struct spmi_controller *ctrl;
+};
+
+/* SPMI transaction parameters */
+struct spmi_trans {
+	u32 cnt;	/* Number of bytes to read */
+	u32 addr;	/* 20-bit address: SID + PID + Register offset */
+	u32 offset;	/* Offset of last read data */
+	bool raw_data;	/* Set to true for raw data dump */
+	struct spmi_controller *ctrl;
+	struct spmi_log_buffer *log; /* log buffer */
+};
+
+struct spmi_dbgfs {
+	struct dentry *root;
+	struct mutex  lock;
+	struct list_head ctrl; /* List of spmi_ctrl_data nodes */
+	struct debugfs_blob_wrapper help_msg;
+};
+
+static struct spmi_dbgfs dbgfs_data = {
+	.lock = __MUTEX_INITIALIZER(dbgfs_data.lock),
+	.ctrl = LIST_HEAD_INIT(dbgfs_data.ctrl),
+	.help_msg = {
+	.data =
+"SPMI Debug-FS support\n"
+"\n"
+"Hierarchy schema:\n"
+"/sys/kernel/debug/spmi\n"
+"       /help            -- Static help text\n"
+"       /spmi-0          -- Directory for SPMI bus 0\n"
+"       /spmi-0/address  -- Starting register address for reads or writes\n"
+"       /spmi-0/count    -- Number of registers to read (only used for reads)\n"
+"       /spmi-0/data     -- Initiates the SPMI read (formatted output)\n"
+"       /spmi-0/data_raw -- Initiates the SPMI raw read or write\n"
+"       /spmi-n          -- Directory for SPMI bus n\n"
+"\n"
+"To perform SPMI read or write transactions, you need to first write the\n"
+"address of the slave device register to the 'address' file.  For read\n"
+"transactions, the number of bytes to be read needs to be written to the\n"
+"'count' file.\n"
+"\n"
+"The 'address' file specifies the 20-bit address of a slave device register.\n"
+"The upper 4 bits 'address[19..16]' specify the slave identifier (SID) for\n"
+"the slave device.  The lower 16 bits specify the slave register address.\n"
+"\n"
+"Reading from the 'data' file will initiate a SPMI read transaction starting\n"
+"from slave register 'address' for 'count' number of bytes.\n"
+"\n"
+"Writing to the 'data' file will initiate a SPMI write transaction starting\n"
+"from slave register 'address'.  The number of registers written to will\n"
+"match the number of bytes written to the 'data' file.\n"
+"\n"
+"Example: Read 4 bytes starting at register address 0x1234 for SID 2\n"
+"\n"
+"echo 0x21234 > address\n"
+"echo 4 > count\n"
+"cat data\n"
+"\n"
+"Example: Write 3 bytes starting at register address 0x1008 for SID 1\n"
+"\n"
+"echo 0x11008 > address\n"
+"echo 0x01 0x02 0x03 > data\n"
+"\n"
+"Note that the count file is not used for writes.  Since 3 bytes are\n"
+"written to the 'data' file, then 3 bytes will be written across the\n"
+"SPMI bus.\n\n",
+	},
+};
+
+static int spmi_dfs_open(struct spmi_ctrl_data *ctrl_data, struct file *file)
+{
+	struct spmi_log_buffer *log;
+	struct spmi_trans *trans;
+
+	size_t logbufsize = SZ_4K;
+
+	if (!ctrl_data) {
+		pr_err("No SPMI controller data\n");
+		return -EINVAL;
+	}
+
+	/* Per file "transaction" data */
+	trans = kzalloc(sizeof(*trans), GFP_KERNEL);
+
+	if (!trans) {
+		pr_err("Unable to allocate memory for transaction data\n");
+		return -ENOMEM;
+	}
+
+	/* Allocate log buffer */
+	log = kzalloc(logbufsize, GFP_KERNEL);
+
+	if (!log) {
+		kfree(trans);
+		pr_err("Unable to allocate memory for log buffer\n");
+		return -ENOMEM;
+	}
+
+	log->rpos = 0;
+	log->wpos = 0;
+	log->len = logbufsize - sizeof(*log);
+
+	trans->log = log;
+	trans->cnt = ctrl_data->cnt;
+	trans->addr = ctrl_data->addr;
+	trans->ctrl = ctrl_data->ctrl;
+	trans->offset = trans->addr;
+
+	file->private_data = trans;
+	return 0;
+}
+
+static int spmi_dfs_data_open(struct inode *inode, struct file *file)
+{
+	struct spmi_ctrl_data *ctrl_data = inode->i_private;
+	return spmi_dfs_open(ctrl_data, file);
+}
+
+static int spmi_dfs_raw_data_open(struct inode *inode, struct file *file)
+{
+	int rc;
+	struct spmi_trans *trans;
+	struct spmi_ctrl_data *ctrl_data = inode->i_private;
+
+	rc = spmi_dfs_open(ctrl_data, file);
+	trans = file->private_data;
+	trans->raw_data = true;
+	return rc;
+}
+
+static int spmi_dfs_close(struct inode *inode, struct file *file)
+{
+	struct spmi_trans *trans = file->private_data;
+
+	if (trans && trans->log) {
+		file->private_data = NULL;
+		kfree(trans->log);
+		kfree(trans);
+	}
+
+	return 0;
+}
+
+/**
+ * spmi_read_data: reads data across the SPMI bus
+ * @ctrl: The SPMI controller
+ * @buf: buffer to store the data read.
+ * @offset: SPMI address offset to start reading from.
+ * @cnt: The number of bytes to read.
+ *
+ * Returns 0 on success, otherwise returns error code from SPMI driver.
+ */
+static int
+spmi_read_data(struct spmi_controller *ctrl, uint8_t *buf, int offset, int cnt)
+{
+	int ret = 0;
+	int len;
+	uint8_t sid;
+	uint16_t addr;
+
+	while (cnt > 0) {
+		sid = (offset >> 16) & 0xF;
+		addr = offset & 0xFFFF;
+		len = min(cnt, MAX_REG_PER_TRANSACTION);
+
+		ret = spmi_ext_register_readl(ctrl, sid, addr, buf, len);
+		if (ret < 0) {
+			pr_err("SPMI read failed, err = %d\n", ret);
+			goto done;
+		}
+
+		cnt -= len;
+		buf += len;
+		offset += len;
+	}
+
+done:
+	return ret;
+}
+
+/**
+ * spmi_write_data: writes data across the SPMI bus
+ * @ctrl: The SPMI controller
+ * @buf: data to be written.
+ * @offset: SPMI address offset to start writing to.
+ * @cnt: The number of bytes to write.
+ *
+ * Returns 0 on success, otherwise returns error code from SPMI driver.
+ */
+static int
+spmi_write_data(struct spmi_controller *ctrl, uint8_t *buf, int offset, int cnt)
+{
+	int ret = 0;
+	int len;
+	uint8_t sid;
+	uint16_t addr;
+
+	while (cnt > 0) {
+		sid = (offset >> 16) & 0xF;
+		addr = offset & 0xFFFF;
+		len = min(cnt, MAX_REG_PER_TRANSACTION);
+
+		ret = spmi_ext_register_writel(ctrl, sid, addr, buf, len);
+		if (ret < 0) {
+			pr_err("SPMI write failed, err = %d\n", ret);
+			goto done;
+		}
+
+		cnt -= len;
+		buf += len;
+		offset += len;
+	}
+
+done:
+	return ret;
+}
+
+/**
+ * print_to_log: format a string and place into the log buffer
+ * @log: The log buffer to place the result into.
+ * @fmt: The format string to use.
+ * @...: The arguments for the format string.
+ *
+ * The return value is the number of characters written to @log buffer
+ * not including the trailing '\0'.
+ */
+static int print_to_log(struct spmi_log_buffer *log, const char *fmt, ...)
+{
+	va_list args;
+	int cnt;
+	char *buf = &log->data[log->wpos];
+	size_t size = log->len - log->wpos;
+
+	va_start(args, fmt);
+	cnt = vscnprintf(buf, size, fmt, args);
+	va_end(args);
+
+	log->wpos += cnt;
+	return cnt;
+}
+
+/**
+ * write_next_line_to_log: Writes a single "line" of data into the log buffer
+ * @trans: Pointer to SPMI transaction data.
+ * @offset: SPMI address offset to start reading from.
+ * @pcnt: Pointer to 'cnt' variable.  Indicates the number of bytes to read.
+ *
+ * The 'offset' is a 20-bits SPMI address which includes a 4-bit slave id (SID),
+ * an 8-bit peripheral id (PID), and an 8-bit peripheral register address.
+ *
+ * On a successful read, the pcnt is decremented by the number of data
+ * bytes read across the SPMI bus.  When the cnt reaches 0, all requested
+ * bytes have been read.
+ */
+static int
+write_next_line_to_log(struct spmi_trans *trans, int offset, size_t *pcnt)
+{
+	int i, j;
+	u8  data[ITEMS_PER_LINE];
+	struct spmi_log_buffer *log = trans->log;
+
+	int cnt = 0;
+	int padding = offset % ITEMS_PER_LINE;
+	int items_to_read = min(ARRAY_SIZE(data) - padding, *pcnt);
+	int items_to_log = min(ITEMS_PER_LINE, padding + items_to_read);
+
+	/* Buffer needs enough space for an entire line */
+	if ((log->len - log->wpos) < MAX_LINE_LENGTH)
+		goto done;
+
+	/* Read the desired number of "items" */
+	if (spmi_read_data(trans->ctrl, data, offset, items_to_read))
+		goto done;
+
+	*pcnt -= items_to_read;
+
+	/* Each line starts with the aligned offset (20-bit address) */
+	cnt = print_to_log(log, "%5.5X ", offset & 0xffff0);
+	if (cnt == 0)
+		goto done;
+
+	/* If the offset is unaligned, add padding to right justify items */
+	for (i = 0; i < padding; ++i) {
+		cnt = print_to_log(log, "-- ");
+		if (cnt == 0)
+			goto done;
+	}
+
+	/* Log the data items */
+	for (j = 0; i < items_to_log; ++i, ++j) {
+		cnt = print_to_log(log, "%2.2X ", data[j]);
+		if (cnt == 0)
+			goto done;
+	}
+
+	/* If the last character was a space, then replace it with a newline */
+	if (log->wpos > 0 && log->data[log->wpos - 1] == ' ')
+		log->data[log->wpos - 1] = '\n';
+
+done:
+	return cnt;
+}
+
+/**
+ * write_raw_data_to_log: Writes a single "line" of data into the log buffer
+ * @trans: Pointer to SPMI transaction data.
+ * @offset: SPMI address offset to start reading from.
+ * @pcnt: Pointer to 'cnt' variable.  Indicates the number of bytes to read.
+ *
+ * The 'offset' is a 20-bits SPMI address which includes a 4-bit slave id (SID),
+ * an 8-bit peripheral id (PID), and an 8-bit peripheral register address.
+ *
+ * On a successful read, the pcnt is decremented by the number of data
+ * bytes read across the SPMI bus.  When the cnt reaches 0, all requested
+ * bytes have been read.
+ */
+static int
+write_raw_data_to_log(struct spmi_trans *trans, int offset, size_t *pcnt)
+{
+	u8  data[16];
+	struct spmi_log_buffer *log = trans->log;
+
+	int i;
+	int cnt = 0;
+	int items_to_read = min(ARRAY_SIZE(data), *pcnt);
+
+	/* Buffer needs enough space for an entire line */
+	if ((log->len - log->wpos) < 80)
+		goto done;
+
+	/* Read the desired number of "items" */
+	if (spmi_read_data(trans->ctrl, data, offset, items_to_read))
+		goto done;
+
+	*pcnt -= items_to_read;
+
+	/* Log the data items */
+	for (i = 0; i < items_to_read; ++i) {
+		cnt = print_to_log(log, "0x%2.2X ", data[i]);
+		if (cnt == 0)
+			goto done;
+	}
+
+	/* If the last character was a space, then replace it with a newline */
+	if (log->wpos > 0 && log->data[log->wpos - 1] == ' ')
+		log->data[log->wpos - 1] = '\n';
+
+done:
+	return cnt;
+}
+
+/**
+ * get_log_data - reads data across the SPMI bus and saves to the log buffer
+ * @trans: Pointer to SPMI transaction data.
+ *
+ * Returns the number of "items" read or SPMI error code for read failures.
+ */
+static int get_log_data(struct spmi_trans *trans)
+{
+	int cnt;
+	int last_cnt;
+	int items_read;
+	int total_items_read = 0;
+	u32 offset = trans->offset;
+	size_t item_cnt = trans->cnt;
+	struct spmi_log_buffer *log = trans->log;
+	int (*write_to_log)(struct spmi_trans *, int, size_t *);
+
+	if (item_cnt == 0)
+		return 0;
+
+	if (trans->raw_data)
+		write_to_log = write_raw_data_to_log;
+	else
+		write_to_log = write_next_line_to_log;
+
+	/* Reset the log buffer 'pointers' */
+	log->wpos = log->rpos = 0;
+
+	/* Keep reading data until the log is full */
+	do {
+		last_cnt = item_cnt;
+		cnt = write_to_log(trans, offset, &item_cnt);
+		items_read = last_cnt - item_cnt;
+		offset += items_read;
+		total_items_read += items_read;
+	} while (cnt && item_cnt > 0);
+
+	/* Adjust the transaction offset and count */
+	trans->cnt = item_cnt;
+	trans->offset += total_items_read;
+
+	return total_items_read;
+}
+
+/**
+ * spmi_dfs_reg_write: write user's byte array (coded as string) over SPMI.
+ * @file: file pointer
+ * @buf: user data to be written.
+ * @count: maximum space available in @buf
+ * @ppos: starting position
+ * @return number of user byte written, or negative error value
+ */
+static ssize_t spmi_dfs_reg_write(struct file *file, const char __user *buf,
+			size_t count, loff_t *ppos)
+{
+	int bytes_read;
+	int data;
+	int pos = 0;
+	int cnt = 0;
+	u8  *values;
+	size_t ret = 0;
+
+	struct spmi_trans *trans = file->private_data;
+	u32 offset = trans->offset;
+
+	/* Make a copy of the user data */
+	char *kbuf = kmalloc(count + 1, GFP_KERNEL);
+	if (!kbuf)
+		return -ENOMEM;
+
+	ret = copy_from_user(kbuf, buf, count);
+	if (ret == count) {
+		pr_err("failed to copy data from user\n");
+		ret = -EFAULT;
+		goto free_buf;
+	}
+
+	count -= ret;
+	*ppos += count;
+	kbuf[count] = '\0';
+
+	/* Override the text buffer with the raw data */
+	values = kbuf;
+
+	/* Parse the data in the buffer.  It should be a string of numbers */
+	while (sscanf(kbuf + pos, "%i%n", &data, &bytes_read) == 1) {
+		pos += bytes_read;
+		values[cnt++] = data & 0xff;
+	}
+
+	if (!cnt)
+		goto free_buf;
+
+	/* Perform the SPMI write(s) */
+	ret = spmi_write_data(trans->ctrl, values, offset, cnt);
+
+	if (ret) {
+		pr_err("SPMI write failed, err = %zu\n", ret);
+	} else {
+		ret = count;
+		trans->offset += cnt;
+	}
+
+free_buf:
+	kfree(kbuf);
+	return ret;
+}
+
+/**
+ * spmi_dfs_reg_read: reads value(s) over SPMI and fill user's buffer a
+ *  byte array (coded as string)
+ * @file: file pointer
+ * @buf: where to put the result
+ * @count: maximum space available in @buf
+ * @ppos: starting position
+ * @return number of user bytes read, or negative error value
+ */
+static ssize_t spmi_dfs_reg_read(struct file *file, char __user *buf,
+	size_t count, loff_t *ppos)
+{
+	struct spmi_trans *trans = file->private_data;
+	struct spmi_log_buffer *log = trans->log;
+	size_t ret;
+	size_t len;
+
+	/* Is the the log buffer empty */
+	if (log->rpos >= log->wpos) {
+		if (get_log_data(trans) <= 0)
+			return 0;
+	}
+
+	len = min(count, log->wpos - log->rpos);
+
+	ret = copy_to_user(buf, &log->data[log->rpos], len);
+	if (ret == len) {
+		pr_err("error copy SPMI register values to user\n");
+		return -EFAULT;
+	}
+
+	/* 'ret' is the number of bytes not copied */
+	len -= ret;
+
+	*ppos += len;
+	log->rpos += len;
+	return len;
+}
+
+static const struct file_operations spmi_dfs_reg_fops = {
+	.open		= spmi_dfs_data_open,
+	.release	= spmi_dfs_close,
+	.read		= spmi_dfs_reg_read,
+	.write		= spmi_dfs_reg_write,
+};
+
+static const struct file_operations spmi_dfs_raw_data_fops = {
+	.open		= spmi_dfs_raw_data_open,
+	.release	= spmi_dfs_close,
+	.read		= spmi_dfs_reg_read,
+	.write		= spmi_dfs_reg_write,
+};
+
+/**
+ * spmi_dfs_create_fs: create debugfs file system.
+ * @return pointer to root directory or NULL if failed to create fs
+ */
+static struct dentry *spmi_dfs_create_fs(void)
+{
+	struct dentry *root, *file;
+
+	pr_debug("Creating SPMI debugfs file-system at\n");
+	root = debugfs_create_dir(DFS_ROOT_NAME, NULL);
+	if (IS_ERR(root)) {
+		pr_err("Error creating top level directory err:%ld",
+			(long)root);
+		if ((int)root == -ENODEV)
+			pr_err("debugfs is not enabled in the kernel");
+		return NULL;
+	}
+
+	dbgfs_data.help_msg.size = strlen(dbgfs_data.help_msg.data);
+
+	file = debugfs_create_blob("help", S_IRUGO, root, &dbgfs_data.help_msg);
+	if (!file) {
+		pr_err("error creating help entry\n");
+		goto err_remove_fs;
+	}
+	return root;
+
+err_remove_fs:
+	debugfs_remove_recursive(root);
+	return NULL;
+}
+
+/**
+ * spmi_dfs_get_root: return a pointer to SPMI debugfs root directory.
+ * @brief return a pointer to the existing directory, or if no root
+ * directory exists then create one. Directory is created with file that
+ * configures SPMI transaction, namely: sid, address, and count.
+ * @returns valid pointer on success or NULL
+ */
+struct dentry *spmi_dfs_get_root(void)
+{
+	if (dbgfs_data.root)
+		return dbgfs_data.root;
+
+	if (mutex_lock_interruptible(&dbgfs_data.lock) < 0)
+		return NULL;
+	/* critical section */
+	if (!dbgfs_data.root) { /* double checking idiom */
+		dbgfs_data.root = spmi_dfs_create_fs();
+	}
+	mutex_unlock(&dbgfs_data.lock);
+	return dbgfs_data.root;
+}
+
+/*
+ * spmi_dfs_add_controller: adds new spmi controller entry
+ * @return zero on success
+ */
+int spmi_dfs_add_controller(struct spmi_controller *ctrl)
+{
+	struct dentry *dir;
+	struct dentry *root;
+	struct dentry *file;
+	struct spmi_ctrl_data *ctrl_data;
+
+	pr_debug("Adding controller %s\n", ctrl->dev.kobj.name);
+	root = spmi_dfs_get_root();
+	if (!root)
+		return -ENOENT;
+
+	/* Allocate transaction data for the controller */
+	ctrl_data = kzalloc(sizeof(*ctrl_data), GFP_KERNEL);
+	if (!ctrl_data)
+		return -ENOMEM;
+
+	dir = debugfs_create_dir(ctrl->dev.kobj.name, root);
+	if (!dir) {
+		pr_err("Error creating entry for spmi controller %s\n",
+						ctrl->dev.kobj.name);
+		goto err_create_dir_failed;
+	}
+
+	ctrl_data->cnt  = 1;
+	ctrl_data->ctrl = ctrl;
+
+	file = debugfs_create_u32("count", DFS_MODE, dir, &ctrl_data->cnt);
+	if (!file) {
+		pr_err("error creating 'count' entry\n");
+		goto err_remove_fs;
+	}
+
+	file = debugfs_create_x32("address", DFS_MODE, dir, &ctrl_data->addr);
+	if (!file) {
+		pr_err("error creating 'address' entry\n");
+		goto err_remove_fs;
+	}
+
+	file = debugfs_create_file("data", DFS_MODE, dir, ctrl_data,
+							&spmi_dfs_reg_fops);
+	if (!file) {
+		pr_err("error creating 'data' entry\n");
+		goto err_remove_fs;
+	}
+
+	file = debugfs_create_file("data_raw", DFS_MODE, dir, ctrl_data,
+						&spmi_dfs_raw_data_fops);
+	if (!file) {
+		pr_err("error creating 'data' entry\n");
+		goto err_remove_fs;
+	}
+
+	list_add(&ctrl_data->node, &dbgfs_data.ctrl);
+	return 0;
+
+err_remove_fs:
+	debugfs_remove_recursive(dir);
+err_create_dir_failed:
+	kfree(ctrl_data);
+	return -ENOMEM;
+}
+
+static void __exit spmi_dfs_delete_all_ctrl(struct list_head *head)
+{
+	struct list_head *pos, *tmp;
+
+	list_for_each_safe(pos, tmp, head) {
+		struct spmi_ctrl_data *ctrl_data;
+
+		ctrl_data = list_entry(pos, struct spmi_ctrl_data, node);
+		list_del(pos);
+		kfree(ctrl_data);
+	}
+}
+
+static void __exit spmi_dfs_destroy(void)
+{
+	pr_debug("de-initializing spmi debugfs ...");
+	if (mutex_lock_interruptible(&dbgfs_data.lock) < 0)
+		return;
+	if (dbgfs_data.root) {
+		debugfs_remove_recursive(dbgfs_data.root);
+		dbgfs_data.root = NULL;
+		spmi_dfs_delete_all_ctrl(&dbgfs_data.ctrl);
+	}
+	mutex_unlock(&dbgfs_data.lock);
+}
+
+module_exit(spmi_dfs_destroy);
+
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:spmi_debug_fs");
diff --git a/drivers/spmi/spmi-dbgfs.h b/drivers/spmi/spmi-dbgfs.h
new file mode 100644
index 0000000..0baa4db
--- /dev/null
+++ b/drivers/spmi/spmi-dbgfs.h
@@ -0,0 +1,21 @@
+/* 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 _SPMI_DBGFS_H
+#define _SPMI_DBGFS_H
+
+#ifdef CONFIG_DEBUG_FS
+int spmi_dfs_add_controller(struct spmi_controller *ctrl);
+#else
+int spmi_dfs_add_controller(struct spmi_controller *ctrl) { return 0; }
+#endif
+
+#endif /* _SPMI_DBGFS_H */
diff --git a/drivers/spmi/spmi.c b/drivers/spmi/spmi.c
index 914df95..ad58240 100644
--- a/drivers/spmi/spmi.c
+++ b/drivers/spmi/spmi.c
@@ -22,6 +22,8 @@
 #include <linux/module.h>
 #include <linux/pm_runtime.h>
 
+#include "spmi-dbgfs.h"
+
 struct spmii_boardinfo {
 	struct list_head	list;
 	struct spmi_boardinfo	board_info;
@@ -755,6 +757,7 @@
 	list_add_tail(&ctrl->list, &spmi_ctrl_list);
 	mutex_unlock(&board_lock);
 
+	spmi_dfs_add_controller(ctrl);
 	return 0;
 
 exit:
diff --git a/drivers/staging/android/Kconfig b/drivers/staging/android/Kconfig
index 9a99238..43d17c2 100644
--- a/drivers/staging/android/Kconfig
+++ b/drivers/staging/android/Kconfig
@@ -67,6 +67,15 @@
 	---help---
 	  Register processes to be killed when memory is low
 
+config ANDROID_LOW_MEMORY_KILLER_AUTODETECT_OOM_ADJ_VALUES
+	bool "Android Low Memory Killer: detect oom_adj values"
+	depends on ANDROID_LOW_MEMORY_KILLER
+	default y
+	---help---
+	  Detect oom_adj values written to
+	  /sys/module/lowmemorykiller/parameters/adj and convert them
+	  to oom_score_adj values.
+
 source "drivers/staging/android/switch/Kconfig"
 
 config ANDROID_INTF_ALARM_DEV
diff --git a/drivers/staging/android/lowmemorykiller.c b/drivers/staging/android/lowmemorykiller.c
index e73caf1..c67b75b 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);
@@ -174,9 +259,94 @@
 	unregister_shrinker(&lowmem_shrinker);
 }
 
+#ifdef CONFIG_ANDROID_LOW_MEMORY_KILLER_AUTODETECT_OOM_ADJ_VALUES
+static int lowmem_oom_adj_to_oom_score_adj(int oom_adj)
+{
+	if (oom_adj == OOM_ADJUST_MAX)
+		return OOM_SCORE_ADJ_MAX;
+	else
+		return (oom_adj * OOM_SCORE_ADJ_MAX) / -OOM_DISABLE;
+}
+
+static void lowmem_autodetect_oom_adj_values(void)
+{
+	int i;
+	int oom_adj;
+	int oom_score_adj;
+	int array_size = ARRAY_SIZE(lowmem_adj);
+
+	if (lowmem_adj_size < array_size)
+		array_size = lowmem_adj_size;
+
+	if (array_size <= 0)
+		return;
+
+	oom_adj = lowmem_adj[array_size - 1];
+	if (oom_adj > OOM_ADJUST_MAX)
+		return;
+
+	oom_score_adj = lowmem_oom_adj_to_oom_score_adj(oom_adj);
+	if (oom_score_adj <= OOM_ADJUST_MAX)
+		return;
+
+	lowmem_print(1, "lowmem_shrink: convert oom_adj to oom_score_adj:\n");
+	for (i = 0; i < array_size; i++) {
+		oom_adj = lowmem_adj[i];
+		oom_score_adj = lowmem_oom_adj_to_oom_score_adj(oom_adj);
+		lowmem_adj[i] = oom_score_adj;
+		lowmem_print(1, "oom_adj %d => oom_score_adj %d\n",
+			     oom_adj, oom_score_adj);
+	}
+}
+
+static int lowmem_adj_array_set(const char *val, const struct kernel_param *kp)
+{
+	int ret;
+
+	ret = param_array_ops.set(val, kp);
+
+	/* HACK: Autodetect oom_adj values in lowmem_adj array */
+	lowmem_autodetect_oom_adj_values();
+
+	return ret;
+}
+
+static int lowmem_adj_array_get(char *buffer, const struct kernel_param *kp)
+{
+	return param_array_ops.get(buffer, kp);
+}
+
+static void lowmem_adj_array_free(void *arg)
+{
+	param_array_ops.free(arg);
+}
+
+static struct kernel_param_ops lowmem_adj_array_ops = {
+	.set = lowmem_adj_array_set,
+	.get = lowmem_adj_array_get,
+	.free = lowmem_adj_array_free,
+};
+
+static const struct kparam_array __param_arr_adj = {
+	.max = ARRAY_SIZE(lowmem_adj),
+	.num = &lowmem_adj_size,
+	.ops = &param_ops_int,
+	.elemsize = sizeof(lowmem_adj[0]),
+	.elem = lowmem_adj,
+};
+#endif
+
 module_param_named(cost, lowmem_shrinker.seeks, int, S_IRUGO | S_IWUSR);
+#ifdef CONFIG_ANDROID_LOW_MEMORY_KILLER_AUTODETECT_OOM_ADJ_VALUES
+__module_param_call(MODULE_PARAM_PREFIX, adj,
+		    &lowmem_adj_array_ops,
+		    .arr = &__param_arr_adj,
+		    S_IRUGO | S_IWUSR, -1);
+__MODULE_PARM_TYPE(adj, "array of int");
+#else
 module_param_array_named(adj, lowmem_adj, int, &lowmem_adj_size,
 			 S_IRUGO | S_IWUSR);
+#endif
 module_param_array_named(minfree, lowmem_minfree, uint, &lowmem_minfree_size,
 			 S_IRUGO | S_IWUSR);
 module_param_named(debug_level, lowmem_debug_level, uint, S_IRUGO | S_IWUSR);
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 f3387d9..4cb93b8 100644
--- a/drivers/thermal/msm8974-tsens.c
+++ b/drivers/thermal/msm8974-tsens.c
@@ -50,7 +50,13 @@
 #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)
@@ -156,7 +162,6 @@
 #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
@@ -205,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;
 }
@@ -525,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,
@@ -555,6 +558,7 @@
 	int tsens9_point2 = 0, tsens10_point2 = 0;
 	int tsens_base2_data = 0, tsens_calibration_mode = 0, temp = 0;
 	uint32_t calib_data[6], calib_redun_sel, calib_data_backup[4];
+	uint32_t calib_tsens_point1_data[11], calib_tsens_point2_data[11];
 
 	if (tmdev->calibration_less_mode)
 		goto calibration_less_mode;
@@ -616,45 +620,46 @@
 			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] &
+		} else
+			goto calibration_less_mode;
+
+		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 = (calib_data_backup[2] &
 					TSENS0_POINT2_BACKUP_MASK) >>
 					TSENS0_POINT2_BACKUP_SHIFT;
-		tsens1_point2 = (calib_data_backup[3] &
+			tsens1_point2 = (calib_data_backup[3] &
 					TSENS1_POINT2_BACKUP_MASK);
-		tsens2_point2 = (calib_data_backup[3] &
+			tsens2_point2 = (calib_data_backup[3] &
 					TSENS2_POINT2_BACKUP_MASK) >>
 					TSENS2_POINT2_BACKUP_SHIFT;
-		tsens3_point2 = (calib_data_backup[3] &
+			tsens3_point2 = (calib_data_backup[3] &
 					TSENS3_POINT2_BACKUP_MASK) >>
 					TSENS3_POINT2_BACKUP_SHIFT;
-		tsens4_point2 = (calib_data_backup[3] &
+			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;
+			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;
 		}
-		tsens_calibration_mode = 0;
-		goto compute_intercept_slope;
-	}
 	} else {
 		tsens_calibration_mode = (calib_data[1] & TSENS_CAL_SEL_0_1)
 			>> TSENS_CAL_SEL_SHIFT;
@@ -687,7 +692,10 @@
 			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) {
+		} else
+			goto calibration_less_mode;
+
+		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;
@@ -711,120 +719,145 @@
 						TSENS9_POINT2_SHIFT;
 			tsens10_point2 = (calib_data[4] & TSENS10_POINT2_MASK)
 						>> TSENS10_POINT2_SHIFT;
-		} else {
+		}
+
+		if (tsens_calibration_mode == 0) {
 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;
+				calib_tsens_point2_data[i] = 780;
+			calib_tsens_point1_data[0] = 502;
+			calib_tsens_point1_data[1] = 509;
+			calib_tsens_point1_data[2] = 503;
+			calib_tsens_point1_data[3] = 509;
+			calib_tsens_point1_data[4] = 505;
+			calib_tsens_point1_data[5] = 509;
+			calib_tsens_point1_data[6] = 507;
+			calib_tsens_point1_data[7] = 510;
+			calib_tsens_point1_data[8] = 508;
+			calib_tsens_point1_data[9] = 509;
+			calib_tsens_point1_data[10] = 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 =
-		(((tsens_base1_data) << 2) | TSENS_BIT_APPEND) + tsens1_point1;
-		tmdev->sensor[2].calib_data_point1 =
-		(((tsens_base1_data) << 2) | TSENS_BIT_APPEND) + tsens2_point1;
-		tmdev->sensor[3].calib_data_point1 =
-		(((tsens_base1_data) << 2) | TSENS_BIT_APPEND) + tsens3_point1;
-		tmdev->sensor[4].calib_data_point1 =
-		(((tsens_base1_data) << 2) | TSENS_BIT_APPEND) + tsens4_point1;
-		tmdev->sensor[5].calib_data_point1 =
-		(((tsens_base1_data) << 2) | TSENS_BIT_APPEND) + tsens5_point1;
-		tmdev->sensor[6].calib_data_point1 =
-		(((tsens_base1_data) << 2) | TSENS_BIT_APPEND) + tsens6_point1;
-		tmdev->sensor[7].calib_data_point1 =
-		(((tsens_base1_data) << 2) | TSENS_BIT_APPEND) + tsens7_point1;
-		tmdev->sensor[8].calib_data_point1 =
-		(((tsens_base1_data) << 2) | TSENS_BIT_APPEND) + tsens8_point1;
-		tmdev->sensor[9].calib_data_point1 =
-		(((tsens_base1_data) << 2) | TSENS_BIT_APPEND) + tsens9_point1;
-		tmdev->sensor[10].calib_data_point1 =
-		(((tsens_base1_data) << 2) | TSENS_BIT_APPEND) + tsens10_point1;
+		calib_tsens_point1_data[0] =
+			(((tsens_base1_data) << 2) | TSENS_BIT_APPEND)
+							+ tsens0_point1;
+		calib_tsens_point1_data[1] =
+			(((tsens_base1_data) << 2) | TSENS_BIT_APPEND)
+							+ tsens1_point1;
+		calib_tsens_point1_data[2] =
+			(((tsens_base1_data) << 2) | TSENS_BIT_APPEND)
+							+ tsens2_point1;
+		calib_tsens_point1_data[3] =
+			(((tsens_base1_data) << 2) | TSENS_BIT_APPEND)
+							+ tsens3_point1;
+		calib_tsens_point1_data[4] =
+			(((tsens_base1_data) << 2) | TSENS_BIT_APPEND)
+							+ tsens4_point1;
+		calib_tsens_point1_data[5] =
+			(((tsens_base1_data) << 2) | TSENS_BIT_APPEND)
+							+ tsens5_point1;
+		calib_tsens_point1_data[6] =
+			(((tsens_base1_data) << 2) | TSENS_BIT_APPEND)
+							+ tsens6_point1;
+		calib_tsens_point1_data[7] =
+			(((tsens_base1_data) << 2) | TSENS_BIT_APPEND)
+							+ tsens7_point1;
+		calib_tsens_point1_data[8] =
+			(((tsens_base1_data) << 2) | TSENS_BIT_APPEND)
+							+ tsens8_point1;
+		calib_tsens_point1_data[9] =
+			(((tsens_base1_data) << 2) | TSENS_BIT_APPEND)
+							+ tsens9_point1;
+		calib_tsens_point1_data[10] =
+			(((tsens_base1_data) << 2) | TSENS_BIT_APPEND)
+							+ tsens10_point1;
 	}
 
 	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) |
+		calib_tsens_point1_data[0] =
+			((((tsens_base1_data) + tsens0_point1) << 2) |
 						TSENS_BIT_APPEND);
-		tmdev->sensor[1].calib_data_point1 =
-		((((tsens_base1_data) + tsens1_point1) << 2) |
+		calib_tsens_point1_data[1] =
+			((((tsens_base1_data) + tsens1_point1) << 2) |
 						TSENS_BIT_APPEND);
-		tmdev->sensor[2].calib_data_point1 =
-		((((tsens_base1_data) + tsens2_point1) << 2) |
+		calib_tsens_point1_data[2] =
+			((((tsens_base1_data) + tsens2_point1) << 2) |
 						TSENS_BIT_APPEND);
-		tmdev->sensor[3].calib_data_point1 =
-		((((tsens_base1_data) + tsens3_point1) << 2) |
+		calib_tsens_point1_data[3] =
+			((((tsens_base1_data) + tsens3_point1) << 2) |
 						TSENS_BIT_APPEND);
-		tmdev->sensor[4].calib_data_point1 =
-		((((tsens_base1_data) + tsens4_point1) << 2) |
+		calib_tsens_point1_data[4] =
+			((((tsens_base1_data) + tsens4_point1) << 2) |
 						TSENS_BIT_APPEND);
-		tmdev->sensor[5].calib_data_point1 =
-		((((tsens_base1_data) + tsens5_point1) << 2) |
+		calib_tsens_point1_data[5] =
+			((((tsens_base1_data) + tsens5_point1) << 2) |
 						TSENS_BIT_APPEND);
-		tmdev->sensor[6].calib_data_point1 =
-		((((tsens_base1_data) + tsens6_point1) << 2) |
+		calib_tsens_point1_data[6] =
+			((((tsens_base1_data) + tsens6_point1) << 2) |
 						TSENS_BIT_APPEND);
-		tmdev->sensor[7].calib_data_point1 =
-		((((tsens_base1_data) + tsens7_point1) << 2) |
+		calib_tsens_point1_data[7] =
+			((((tsens_base1_data) + tsens7_point1) << 2) |
 						TSENS_BIT_APPEND);
-		tmdev->sensor[8].calib_data_point1 =
-		((((tsens_base1_data) + tsens8_point1) << 2) |
+		calib_tsens_point1_data[8] =
+			((((tsens_base1_data) + tsens8_point1) << 2) |
 						TSENS_BIT_APPEND);
-		tmdev->sensor[9].calib_data_point1 =
-		((((tsens_base1_data) + tsens9_point1) << 2) |
+		calib_tsens_point1_data[9] =
+			((((tsens_base1_data) + tsens9_point1) << 2) |
 						TSENS_BIT_APPEND);
-		tmdev->sensor[10].calib_data_point1 =
-		((((tsens_base1_data) + tsens10_point1) << 2) |
+		calib_tsens_point1_data[10] =
+			((((tsens_base1_data) + tsens10_point1) << 2) |
 						TSENS_BIT_APPEND);
 	}
 
 	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 =
-		(((tsens_base2_data + tsens1_point2) << 2) | TSENS_BIT_APPEND);
-		tmdev->sensor[2].calib_data_point2 =
-		(((tsens_base2_data + tsens2_point2) << 2) | TSENS_BIT_APPEND);
-		tmdev->sensor[3].calib_data_point2 =
-		(((tsens_base2_data + tsens3_point2) << 2) | TSENS_BIT_APPEND);
-		tmdev->sensor[4].calib_data_point2 =
-		(((tsens_base2_data + tsens4_point2) << 2) | TSENS_BIT_APPEND);
-		tmdev->sensor[5].calib_data_point2 =
-		(((tsens_base2_data + tsens5_point2) << 2) | TSENS_BIT_APPEND);
-		tmdev->sensor[6].calib_data_point2 =
-		(((tsens_base2_data + tsens6_point2) << 2) | TSENS_BIT_APPEND);
-		tmdev->sensor[7].calib_data_point2 =
-		(((tsens_base2_data + tsens7_point2) << 2) | TSENS_BIT_APPEND);
-		tmdev->sensor[8].calib_data_point2 =
-		(((tsens_base2_data + tsens8_point2) << 2) | TSENS_BIT_APPEND);
-		tmdev->sensor[9].calib_data_point2 =
-		(((tsens_base2_data + tsens9_point2) << 2) | TSENS_BIT_APPEND);
-		tmdev->sensor[10].calib_data_point2 =
-		(((tsens_base2_data + tsens10_point2) << 2) | TSENS_BIT_APPEND);
+		calib_tsens_point2_data[0] =
+			(((tsens_base2_data + tsens0_point2) << 2) |
+					TSENS_BIT_APPEND);
+		calib_tsens_point2_data[1] =
+			(((tsens_base2_data + tsens1_point2) << 2) |
+					TSENS_BIT_APPEND);
+		calib_tsens_point2_data[2] =
+			(((tsens_base2_data + tsens2_point2) << 2) |
+					TSENS_BIT_APPEND);
+		calib_tsens_point2_data[3] =
+			(((tsens_base2_data + tsens3_point2) << 2) |
+					TSENS_BIT_APPEND);
+		calib_tsens_point2_data[4] =
+			(((tsens_base2_data + tsens4_point2) << 2) |
+					TSENS_BIT_APPEND);
+		calib_tsens_point2_data[5] =
+			(((tsens_base2_data + tsens5_point2) << 2) |
+					TSENS_BIT_APPEND);
+		calib_tsens_point2_data[6] =
+			(((tsens_base2_data + tsens6_point2) << 2) |
+					TSENS_BIT_APPEND);
+		calib_tsens_point2_data[7] =
+			(((tsens_base2_data + tsens7_point2) << 2) |
+					TSENS_BIT_APPEND);
+		calib_tsens_point2_data[8] =
+			(((tsens_base2_data + tsens8_point2) << 2) |
+					TSENS_BIT_APPEND);
+		calib_tsens_point2_data[9] =
+			(((tsens_base2_data + tsens9_point2) << 2) |
+					TSENS_BIT_APPEND);
+		calib_tsens_point2_data[10] =
+			(((tsens_base2_data + tsens10_point2) << 2) |
+					TSENS_BIT_APPEND);
 	}
 
 compute_intercept_slope:
 	for (i = 0; i < tmdev->tsens_num_sensor; i++) {
 		int32_t num = 0, den = 0;
+		tmdev->sensor[i].calib_data_point2 = calib_tsens_point2_data[i];
+		tmdev->sensor[i].calib_data_point1 = calib_tsens_point1_data[i];
 		if (tsens_calibration_mode == TSENS_TWO_POINT_CALIB) {
 			num = TSENS_CAL_DEGC_POINT2 - TSENS_CAL_DEGC_POINT2;
 			den = tmdev->sensor[i].calib_data_point2 -
diff --git a/drivers/thermal/pm8xxx-tm.c b/drivers/thermal/pm8xxx-tm.c
index ec04369..4568933 100644
--- a/drivers/thermal/pm8xxx-tm.c
+++ b/drivers/thermal/pm8xxx-tm.c
@@ -33,29 +33,32 @@
 #include <linux/msm_adc.h>
 
 /* Register TEMP_ALARM_CTRL bits */
-#define	TEMP_ALARM_CTRL_ST3_SD		0x80
-#define	TEMP_ALARM_CTRL_ST2_SD		0x40
-#define	TEMP_ALARM_CTRL_STATUS_MASK	0x30
-#define	TEMP_ALARM_CTRL_STATUS_SHIFT	4
-#define	TEMP_ALARM_CTRL_THRESH_MASK	0x0C
-#define	TEMP_ALARM_CTRL_THRESH_SHIFT	2
-#define	TEMP_ALARM_CTRL_OVRD_ST3	0x02
-#define	TEMP_ALARM_CTRL_OVRD_ST2	0x01
-#define	TEMP_ALARM_CTRL_OVRD_MASK	0x03
+#define TEMP_ALARM_CTRL_ST3_SD		0x80
+#define TEMP_ALARM_CTRL_ST2_SD		0x40
+#define TEMP_ALARM_CTRL_STATUS_MASK	0x30
+#define TEMP_ALARM_CTRL_STATUS_SHIFT	4
+#define TEMP_ALARM_CTRL_THRESH_MASK	0x0C
+#define TEMP_ALARM_CTRL_THRESH_SHIFT	2
+#define TEMP_ALARM_CTRL_OVRD_ST3	0x02
+#define TEMP_ALARM_CTRL_OVRD_ST2	0x01
+#define TEMP_ALARM_CTRL_OVRD_MASK	0x03
 
-#define	TEMP_STAGE_STEP			20000	/* Stage step: 20.000 C */
-#define	TEMP_STAGE_HYSTERESIS		2000
+#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 TEMP_THRESH_MIN			105000	/* Threshold Min: 105 C */
+#define TEMP_THRESH_STEP		5000	/* Threshold step: 5 C */
 
 /* Register TEMP_ALARM_PWM bits */
-#define	TEMP_ALARM_PWM_EN_MASK		0xC0
-#define	TEMP_ALARM_PWM_EN_SHIFT		6
-#define	TEMP_ALARM_PWM_PER_PRE_MASK	0x38
-#define	TEMP_ALARM_PWM_PER_PRE_SHIFT	3
-#define	TEMP_ALARM_PWM_PER_DIV_MASK	0x07
-#define	TEMP_ALARM_PWM_PER_DIV_SHIFT	0
+#define TEMP_ALARM_PWM_EN_MASK		0xC0
+#define TEMP_ALARM_PWM_EN_NEVER		0x00
+#define TEMP_ALARM_PWM_EN_SLEEP_B	0x40
+#define TEMP_ALARM_PWM_EN_PWM		0x80
+#define TEMP_ALARM_PWM_EN_ALWAYS	0xC0
+#define TEMP_ALARM_PWM_PER_PRE_MASK	0x38
+#define TEMP_ALARM_PWM_PER_PRE_SHIFT	3
+#define TEMP_ALARM_PWM_PER_DIV_MASK	0x07
+#define TEMP_ALARM_PWM_PER_DIV_SHIFT	0
 
 /* Trips: from critical to less critical */
 #define TRIP_STAGE3			0
@@ -516,16 +519,15 @@
 		return rc;
 
 	/*
-	 * Set the PMIC alarm module PWM to have a frequency of 8 Hz. This
-	 * helps cut down on the number of unnecessary interrupts fired when
-	 * changing between thermal stages.  Also, Enable the over temperature
-	 * PWM whenever the PMIC is enabled.
+	 * Set the PMIC temperature alarm module to be always on.  This ensures
+	 * that die temperature monitoring is active even if CXO is disabled
+	 * (i.e. when sleep_b is low).  This is necessary since CXO can be
+	 * disabled while the system is still heavily loaded.  Also, using
+	 * the alway-on instead of PWM-enabled configurations ensures that the
+	 * die temperature can be measured by the PMIC ADC without reconfiguring
+	 * the temperature alarm module first.
 	 */
-	reg =  (1 << TEMP_ALARM_PWM_EN_SHIFT)
-		| (3 << TEMP_ALARM_PWM_PER_PRE_SHIFT)
-		| (3 << TEMP_ALARM_PWM_PER_DIV_SHIFT);
-
-	rc = pm8xxx_tm_write_pwm(chip, reg);
+	rc = pm8xxx_tm_write_pwm(chip, TEMP_ALARM_PWM_EN_ALWAYS);
 
 	return rc;
 }
diff --git a/drivers/tty/serial/msm_serial.c b/drivers/tty/serial/msm_serial.c
index e6f5bf5..5e7ab9f 100644
--- a/drivers/tty/serial/msm_serial.c
+++ b/drivers/tty/serial/msm_serial.c
@@ -848,8 +848,7 @@
 	},
 };
 
-#define UART_NR	ARRAY_SIZE(msm_uart_ports)
-
+#define UART_NR 256
 static inline struct uart_port * get_port_from_line(unsigned int line)
 {
 	return &msm_uart_ports[line].uart;
@@ -1002,9 +1001,7 @@
 	struct resource *resource;
 	struct uart_port *port;
 	int irq;
-#ifdef CONFIG_SERIAL_MSM_RX_WAKEUP
 	struct msm_serial_platform_data *pdata = pdev->dev.platform_data;
-#endif
 
 	if (unlikely(pdev->id < 0 || pdev->id >= UART_NR))
 		return -ENXIO;
@@ -1057,6 +1054,8 @@
 #endif
 
 	pm_runtime_enable(port->dev);
+	if (pdata != NULL && pdata->userid && pdata->userid <= UART_NR)
+		port->line = pdata->userid;
 	return uart_add_one_port(&msm_uart_driver, port);
 }
 
diff --git a/drivers/tty/serial/msm_serial_hs.c b/drivers/tty/serial/msm_serial_hs.c
index 4a9c9a3..7a0e32b 100644
--- a/drivers/tty/serial/msm_serial_hs.c
+++ b/drivers/tty/serial/msm_serial_hs.c
@@ -168,6 +168,7 @@
 	struct work_struct clock_off_w; /* work for actual clock off */
 	struct workqueue_struct *hsuart_wq; /* hsuart workqueue */
 	struct mutex clk_mutex; /* mutex to guard against clock off/clock on */
+	bool tty_flush_receive;
 };
 
 #define MSM_UARTDM_BURST_SIZE 16   /* DM burst size (in bytes) */
@@ -390,8 +391,6 @@
 
 	struct msm_hs_port *msm_uport;
 	struct device *dev;
-	struct msm_serial_hs_platform_data *pdata = pdev->dev.platform_data;
-
 
 	if (pdev->id < 0 || pdev->id >= UARTDM_NR) {
 		printk(KERN_ERR "Invalid plaform device ID = %d\n", pdev->id);
@@ -401,10 +400,6 @@
 	msm_uport = &q_uart_port[pdev->id];
 	dev = msm_uport->uport.dev;
 
-	if (pdata && pdata->gpio_config)
-		if (pdata->gpio_config(0))
-			dev_err(dev, "GPIO config error\n");
-
 	sysfs_remove_file(&pdev->dev.kobj, &dev_attr_clock.attr);
 	debugfs_remove(msm_uport->loopback_dir);
 
@@ -1250,6 +1245,13 @@
 
 }
 
+static void msm_hs_flush_buffer(struct uart_port *uport)
+{
+	struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
+
+	msm_uport->tty_flush_receive = true;
+}
+
 /*
  *  Standard API, Break Signal
  *
@@ -1465,7 +1467,14 @@
 		 */
 		mb();
 		/* Complete DMA TX transactions and submit new transactions */
-		tx_buf->tail = (tx_buf->tail + tx->tx_count) & ~UART_XMIT_SIZE;
+
+		/* Do not update tx_buf.tail if uart_flush_buffer already
+						called in serial core */
+		if (!msm_uport->tty_flush_receive)
+			tx_buf->tail = (tx_buf->tail +
+					tx->tx_count) & ~UART_XMIT_SIZE;
+		else
+			msm_uport->tty_flush_receive = false;
 
 		tx->dma_in_flight = 0;
 
@@ -1631,6 +1640,9 @@
 	unsigned long flags;
 	unsigned int data;
 	struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
+	struct platform_device *pdev = to_platform_device(uport->dev);
+	const struct msm_serial_hs_platform_data *pdata =
+					pdev->dev.platform_data;
 	struct circ_buf *tx_buf = &uport->state->xmit;
 	struct msm_hs_tx *tx = &msm_uport->tx;
 
@@ -1650,6 +1662,10 @@
 		return ret;
 	}
 
+	if (pdata && pdata->gpio_config)
+		if (unlikely(pdata->gpio_config(1)))
+			dev_err(uport->dev, "Cannot configure gpios\n");
+
 	/* Set auto RFR Level */
 	data = msm_hs_read(uport, UARTDM_MR1_ADDR);
 	data &= ~UARTDM_MR1_AUTO_RFR_LEVEL1_BMSK;
@@ -1930,10 +1946,6 @@
 		if (unlikely(msm_uport->wakeup.irq < 0))
 			return -ENXIO;
 
-		if (pdata->gpio_config)
-			if (unlikely(pdata->gpio_config(1)))
-				dev_err(uport->dev, "Cannot configure"
-					"gpios\n");
 	}
 
 	resource = platform_get_resource_byname(pdev, IORESOURCE_DMA,
@@ -2071,6 +2083,9 @@
 	unsigned int data;
 	unsigned long flags;
 	struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
+	struct platform_device *pdev = to_platform_device(uport->dev);
+	const struct msm_serial_hs_platform_data *pdata =
+				pdev->dev.platform_data;
 
 	if (msm_uport->tx.dma_in_flight) {
 		spin_lock_irqsave(&uport->lock, flags);
@@ -2133,6 +2148,10 @@
 	free_irq(uport->irq, msm_uport);
 	if (use_low_power_wakeup(msm_uport))
 		free_irq(msm_uport->wakeup.irq, msm_uport);
+
+	if (pdata && pdata->gpio_config)
+		if (pdata->gpio_config(0))
+			dev_err(uport->dev, "GPIO config error\n");
 }
 
 static void __exit msm_serial_hs_exit(void)
@@ -2209,6 +2228,7 @@
 	.config_port = msm_hs_config_port,
 	.release_port = msm_hs_release_port,
 	.request_port = msm_hs_request_port,
+	.flush_buffer = msm_hs_flush_buffer,
 };
 
 module_init(msm_serial_hs_init);
diff --git a/drivers/tty/serial/msm_serial_hs_lite.c b/drivers/tty/serial/msm_serial_hs_lite.c
index f065eaa..cc9ffaa 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,35 @@
 	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 alias if present */
+	if (pdev->dev.of_node) {
+		ret = of_alias_get_id(pdev->dev.of_node, "serial");
+		if (ret >= 0)
+			line = ret;
+	}
+
+	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/driver.c b/drivers/usb/core/driver.c
index a292416..55ff980 100644
--- a/drivers/usb/core/driver.c
+++ b/drivers/usb/core/driver.c
@@ -1357,8 +1357,14 @@
 {
 	struct usb_device	*udev = to_usb_device(dev);
 
-	if (udev->bus->skip_resume && udev->state == USB_STATE_SUSPENDED)
-		return 0;
+	if (udev->bus->skip_resume) {
+		if (udev->state == USB_STATE_SUSPENDED) {
+			return 0;
+		} else {
+			dev_err(dev, "abort suspend\n");
+			return -EBUSY;
+		}
+	}
 
 	unbind_no_pm_drivers_interfaces(udev);
 
@@ -1405,7 +1411,6 @@
 	 * (This can't be done in usb_resume_interface()
 	 * above because it doesn't own the right set of locks.)
 	 */
-	pm_runtime_get_sync(dev->parent);
 	status = usb_resume_both(udev, msg);
 	if (status == 0) {
 		pm_runtime_disable(dev);
@@ -1413,7 +1418,6 @@
 		pm_runtime_enable(dev);
 		unbind_no_reset_resume_drivers_interfaces(udev);
 	}
-	pm_runtime_put_sync(dev->parent);
 
 	/* Avoid PM error messages for devices disconnected while suspended
 	 * as we'll display regular disconnect messages just a bit later.
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/debugfs.c b/drivers/usb/dwc3/debugfs.c
index d4a30f1..be4eff7 100644
--- a/drivers/usb/dwc3/debugfs.c
+++ b/drivers/usb/dwc3/debugfs.c
@@ -56,7 +56,7 @@
 #define dump_register(nm)				\
 {							\
 	.name	= __stringify(nm),			\
-	.offset	= DWC3_ ##nm,				\
+	.offset	= DWC3_ ##nm - DWC3_GLOBALS_REGS_START,	\
 }
 
 static const struct debugfs_reg32 dwc3_regs[] = {
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..7430e5a 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,11 @@
 #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/rpm-regulator-smd.h>
+#include <mach/msm_xo.h>
 #include <mach/msm_bus.h>
 
 #include "dwc3_otg.h"
@@ -127,6 +131,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;
@@ -137,12 +142,12 @@
 	struct regulator	*hsusb_vddcx;
 	struct regulator	*ssusb_1p8;
 	struct regulator	*ssusb_vddcx;
-	enum usb_vdd_type	ss_vdd_type;
-	enum usb_vdd_type	hs_vdd_type;
 	struct dwc3_ext_xceiv	ext_xceiv;
 	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 +157,14 @@
 	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;
+	unsigned int		vdd_no_vol_level;
+	unsigned int		vdd_low_vol_level;
+	unsigned int		vdd_high_vol_level;
+	bool			vbus_active;
 };
 
 #define USB_HSPHY_3P3_VOL_MIN		3050000 /* uV */
@@ -166,23 +179,6 @@
 #define USB_SSPHY_1P8_VOL_MAX		1800000 /* uV */
 #define USB_SSPHY_1P8_HPM_LOAD		23000	/* uA */
 
-#define USB_PHY_VDD_DIG_VOL_NONE	0	/* uV */
-#define USB_PHY_VDD_DIG_VOL_MIN		1045000 /* uV */
-#define USB_PHY_VDD_DIG_VOL_MAX		1320000 /* uV */
-
-static const int vdd_val[VDD_TYPE_MAX][VDD_VAL_MAX] = {
-		{  /* VDD_CX CORNER Voting */
-			[VDD_NONE]	= RPM_VREG_CORNER_NONE,
-			[VDD_MIN]	= RPM_VREG_CORNER_NOMINAL,
-			[VDD_MAX]	= RPM_VREG_CORNER_HIGH,
-		},
-		{ /* VDD_CX Voltage Voting */
-			[VDD_NONE]	= USB_PHY_VDD_DIG_VOL_NONE,
-			[VDD_MIN]	= USB_PHY_VDD_DIG_VOL_MIN,
-			[VDD_MAX]	= USB_PHY_VDD_DIG_VOL_MAX,
-		},
-};
-
 static struct dwc3_msm *context;
 static u64 dwc3_msm_dma_mask = DMA_BIT_MASK(64);
 
@@ -615,7 +611,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,
@@ -846,12 +843,11 @@
 /* HSPHY */
 static int dwc3_hsusb_config_vddcx(int high)
 {
-	int min_vol, ret;
+	int min_vol, max_vol, ret;
 	struct dwc3_msm *dwc = context;
-	enum usb_vdd_type vdd_type = context->hs_vdd_type;
-	int max_vol = vdd_val[vdd_type][VDD_MAX];
 
-	min_vol = vdd_val[vdd_type][high ? VDD_MIN : VDD_NONE];
+	max_vol = dwc->vdd_high_vol_level;
+	min_vol = high ? dwc->vdd_low_vol_level : dwc->vdd_no_vol_level;
 	ret = regulator_set_voltage(dwc->hsusb_vddcx, min_vol, max_vol);
 	if (ret) {
 		dev_err(dwc->dev, "unable to set voltage for HSUSB_VDDCX\n");
@@ -971,12 +967,11 @@
 /* SSPHY */
 static int dwc3_ssusb_config_vddcx(int high)
 {
-	int min_vol, ret;
+	int min_vol, max_vol, ret;
 	struct dwc3_msm *dwc = context;
-	enum usb_vdd_type vdd_type = context->ss_vdd_type;
-	int max_vol = vdd_val[vdd_type][VDD_MAX];
 
-	min_vol = vdd_val[vdd_type][high ? VDD_MIN : VDD_NONE];
+	max_vol = dwc->vdd_high_vol_level;
+	min_vol = high ? dwc->vdd_low_vol_level : dwc->vdd_no_vol_level;
 	ret = regulator_set_voltage(dwc->ssusb_vddcx, min_vol, max_vol);
 	if (ret) {
 		dev_err(dwc->dev, "unable to set voltage for SSUSB_VDDCX\n");
@@ -1238,12 +1233,54 @@
 		return 0;
 	}
 
-	clk_disable_unprepare(mdwc->iface_clk);
-	clk_disable_unprepare(mdwc->core_clk);
+	if (cancel_delayed_work_sync(&mdwc->chg_work))
+		dev_dbg(mdwc->dev, "%s: chg_work was pending\n", __func__);
+	if (mdwc->chg_state != USB_CHG_STATE_DETECTED) {
+		/* charger detection wasn't complete; re-init flags */
+		mdwc->chg_state = USB_CHG_STATE_UNDEFINED;
+		mdwc->charger.chg_type = DWC3_INVALID_CHARGER;
+	}
+
+	/* 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);
+
+	/* Sequence to put SSPHY in low power state:
+	 * 1. Clear REF_SS_PHY_EN in SS_PHY_CTRL_REG
+	 * 2. Clear REF_USE_PAD in SS_PHY_CTRL_REG
+	 * 3. Set TEST_POWERED_DOWN in SS_PHY_CTRL_REG to enable PHY retention
+	 * 4. Disable SSPHY ref clk
+	 */
+	dwc3_msm_write_readback(mdwc->base, SS_PHY_CTRL_REG, (1 << 8), 0x0);
+	dwc3_msm_write_readback(mdwc->base, SS_PHY_CTRL_REG, (1 << 28), 0x0);
+	dwc3_msm_write_readback(mdwc->base, SS_PHY_CTRL_REG, (1 << 26),
+								(1 << 26));
+
+	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 +1289,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 +1314,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 +1323,59 @@
 			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);
+	/* 10usec delay required before de-asserting PHY RESET */
+	udelay(10);
+	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);
+
+	/* Assert SS PHY RESET */
+	dwc3_msm_write_readback(mdwc->base, SS_PHY_CTRL_REG, (1 << 7),
+								(1 << 7));
+	dwc3_msm_write_readback(mdwc->base, SS_PHY_CTRL_REG, (1 << 28),
+								(1 << 28));
+	dwc3_msm_write_readback(mdwc->base, SS_PHY_CTRL_REG, (1 << 8),
+								(1 << 8));
+	dwc3_msm_write_readback(mdwc->base, SS_PHY_CTRL_REG, (1 << 26), 0x0);
+	/* 10usec delay required before de-asserting SS PHY RESET */
+	udelay(10);
+	dwc3_msm_write_readback(mdwc->base, SS_PHY_CTRL_REG, (1 << 7), 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 +1405,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 +1481,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 +1498,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;
@@ -1411,6 +1606,8 @@
 	struct resource *res;
 	void __iomem *tcsr;
 	int ret = 0;
+	int len = 0;
+	u32 tmp[3];
 
 	msm = devm_kzalloc(&pdev->dev, sizeof(*msm), GFP_KERNEL);
 	if (!msm) {
@@ -1426,6 +1623,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 +1644,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);
@@ -1470,19 +1682,26 @@
 	}
 	clk_prepare_enable(msm->ref_clk);
 
+
+	of_get_property(node, "qcom,vdd-voltage-level", &len);
+	if (len == sizeof(tmp)) {
+		of_property_read_u32_array(node, "qcom,vdd-voltage-level",
+							tmp, len/sizeof(*tmp));
+		msm->vdd_no_vol_level = tmp[0];
+		msm->vdd_low_vol_level = tmp[1];
+		msm->vdd_high_vol_level = tmp[2];
+	} else {
+		dev_err(&pdev->dev, "no qcom,vdd-voltage-level property\n");
+		ret = -EINVAL;
+		goto disable_ref_clk;
+	}
+
 	/* SS PHY */
-	msm->ss_vdd_type = VDDCX_CORNER;
 	msm->ssusb_vddcx = devm_regulator_get(&pdev->dev, "ssusb_vdd_dig");
 	if (IS_ERR(msm->ssusb_vddcx)) {
-		msm->ssusb_vddcx = devm_regulator_get(&pdev->dev,
-							"SSUSB_VDDCX");
-		if (IS_ERR(msm->ssusb_vddcx)) {
-			dev_err(&pdev->dev, "unable to get ssusb vddcx\n");
-			ret = PTR_ERR(msm->ssusb_vddcx);
-			goto disable_ref_clk;
-		}
-		msm->ss_vdd_type = VDDCX;
-		dev_dbg(&pdev->dev, "ss_vdd_type: VDDCX\n");
+		dev_err(&pdev->dev, "unable to get ssusb vddcx\n");
+		ret = PTR_ERR(msm->ssusb_vddcx);
+		goto disable_ref_clk;
 	}
 
 	ret = dwc3_ssusb_config_vddcx(1);
@@ -1510,18 +1729,11 @@
 	}
 
 	/* HS PHY */
-	msm->hs_vdd_type = VDDCX_CORNER;
 	msm->hsusb_vddcx = devm_regulator_get(&pdev->dev, "hsusb_vdd_dig");
 	if (IS_ERR(msm->hsusb_vddcx)) {
-		msm->hsusb_vddcx = devm_regulator_get(&pdev->dev,
-							"HSUSB_VDDCX");
-		if (IS_ERR(msm->hsusb_vddcx)) {
-			dev_err(&pdev->dev, "unable to get hsusb vddcx\n");
-			ret = PTR_ERR(msm->ssusb_vddcx);
-			goto disable_ss_ldo;
-		}
-		msm->hs_vdd_type = VDDCX;
-		dev_dbg(&pdev->dev, "hs_vdd_type: VDDCX\n");
+		dev_err(&pdev->dev, "unable to get hsusb vddcx\n");
+		ret = PTR_ERR(msm->hsusb_vddcx);
+		goto disable_ss_ldo;
 	}
 
 	ret = dwc3_hsusb_config_vddcx(1);
@@ -1548,6 +1760,28 @@
 		goto free_hs_ldo_init;
 	}
 
+	msm->ext_xceiv.otg_capability = of_property_read_bool(node,
+				"qcom,otg-capability");
+	msm->charger.charging_disabled = of_property_read_bool(node,
+				"qcom,charging-disabled");
+
+	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 +1805,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 +1813,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 +1848,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 +1870,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 +1918,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 +1951,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 +1984,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 +2017,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 +2054,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 +2116,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..fab443c 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,25 @@
 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;
+	struct dwc3 *dwc = dotg->dwc;
 	int ret = 0;
 
-	if (!otg->host)
+	if (!dwc->xhci)
 		return -EINVAL;
 
-	hcd = bus_to_hcd(otg->host);
-	xhci = hcd_to_xhci(hcd);
+	if (!dotg->vbus_otg) {
+		dotg->vbus_otg = devm_regulator_get(dwc->dev->parent,
+							"vbus_dwc3");
+		if (IS_ERR(dotg->vbus_otg)) {
+			dev_err(dwc->dev, "Failed to get vbus regulator\n");
+			ret = PTR_ERR(dotg->vbus_otg);
+			dotg->vbus_otg = 0;
+			return ret;
+		}
+	}
+
 	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 +130,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(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(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(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(dwc);
+		dwc3_otg_reset(dotg);
 	}
 
 	return 0;
@@ -141,26 +182,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 +245,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 +307,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 +319,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 +372,75 @@
 	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 || !dotg->charger) {
+		dev_err(phy->dev, "no usb power supply/charger registered\n");
+		return 0;
+	}
+
+	if (dotg->charger->charging_disabled)
+		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 +454,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 +507,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 +557,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 +582,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 +601,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;
@@ -515,14 +640,10 @@
 				}
 			}
 		} else {
-			if (charger) {
-				if (charger->chg_type == DWC3_INVALID_CHARGER)
-					charger->start_detection(dotg->charger,
-									false);
-				else
-					charger->chg_type =
-							DWC3_INVALID_CHARGER;
-			}
+			if (charger)
+				charger->start_detection(dotg->charger, false);
+
+			dwc3_otg_set_power(phy, 0);
 			dev_dbg(phy->dev, "No device, trying to suspend\n");
 			pm_runtime_put_sync(phy->dev);
 		}
@@ -588,6 +709,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 +728,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);
 }
 
 /**
@@ -675,8 +803,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 +816,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..c93ce5f 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,8 @@
 
 struct dwc3_charger {
 	enum dwc3_chg_type	chg_type;
+	unsigned		max_power;
+	bool			charging_disabled;
 
 	/* start/stop charger detection, provided by external charger module */
 	void	(*start_detection)(struct dwc3_charger *charger, bool start);
@@ -89,6 +98,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 f431e9d..3679191 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;
@@ -1058,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);
@@ -1066,6 +1196,7 @@
 		goto out0;
 	}
 
+out1:
 	/* giveback the request */
 	dwc3_gadget_giveback(dep, req, -ECONNRESET);
 
@@ -1084,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)
@@ -1102,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)
@@ -1112,7 +1231,7 @@
 					value ? "set" : "clear",
 					dep->name);
 		else
-			dep->flags &= ~DWC3_EP_STALL;
+			dep->flags &= ~(DWC3_EP_STALL | DWC3_EP_WEDGE);
 	}
 
 	return ret;
@@ -1129,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;
@@ -1152,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);
 }
 
 /* -------------------------------------------------------------------------- */
@@ -1170,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,
 };
 
@@ -1246,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);
@@ -1285,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;
 	}
@@ -1312,7 +1443,7 @@
 		}
 		timeout--;
 		if (!timeout)
-			break;
+			return -ETIMEDOUT;
 		udelay(1);
 	} while (1);
 
@@ -1320,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;
 
@@ -1345,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;
@@ -1377,15 +1523,53 @@
 			 * 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);
+		}
+	} else if (dwc->gadget_driver && !dwc->softconnect &&
+						!dwc->vbus_active) {
+		if (dwc->gadget_driver->disconnect) {
+			spin_unlock_irqrestore(&dwc->lock, flags);
+			dwc->gadget_driver->disconnect(&dwc->gadget);
+			return 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,
@@ -1412,7 +1596,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;
@@ -1421,14 +1622,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;
@@ -1469,11 +1670,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,
@@ -1560,6 +1763,7 @@
 	struct dwc3_trb		*trb;
 	unsigned int		count;
 	unsigned int		s_pkt = 0;
+	unsigned int		trb_status;
 
 	do {
 		req = next_request(&dep->req_queued);
@@ -1585,9 +1789,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))
@@ -1606,7 +1819,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))
@@ -1642,7 +1856,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;
@@ -1659,65 +1873,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)
 {
@@ -1726,6 +1881,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));
 
@@ -1736,9 +1894,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;
@@ -1747,7 +1905,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;
@@ -1756,7 +1914,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;
@@ -1777,7 +1935,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;
@@ -1799,7 +1957,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;
 	}
 }
@@ -1822,16 +1980,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)
@@ -1874,11 +2053,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;
@@ -1886,9 +2063,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;
 
@@ -1896,30 +2071,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);
 }
@@ -1927,6 +2102,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__);
 
@@ -1964,9 +2140,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);
@@ -2011,16 +2193,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;
 	}
 }
@@ -2083,18 +2265,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;
@@ -2172,6 +2357,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);
@@ -2347,8 +2539,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;
@@ -2356,7 +2547,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;
@@ -2366,7 +2558,7 @@
 	dev_set_name(&dwc->gadget.dev, "gadget");
 
 	dwc->gadget.ops			= &dwc3_gadget_ops;
-	dwc->gadget.max_speed		= USB_SPEED_HIGH;
+	dwc->gadget.max_speed		= USB_SPEED_SUPER;
 	dwc->gadget.speed		= USB_SPEED_UNKNOWN;
 	dwc->gadget.dev.parent		= dwc->dev;
 	dwc->gadget.sg_supported	= true;
@@ -2397,6 +2589,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 |
@@ -2408,6 +2604,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");
@@ -2448,8 +2662,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);
@@ -2483,8 +2697,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 52c19cf..b773d1a 100644
--- a/drivers/usb/gadget/android.c
+++ b/drivers/usb/gadget/android.c
@@ -24,6 +24,7 @@
 #include <linux/utsname.h>
 #include <linux/platform_device.h>
 #include <linux/pm_qos.h>
+#include <linux/of.h>
 
 #include <linux/usb/ch9.h>
 #include <linux/usb/composite.h>
@@ -107,9 +108,6 @@
 	char *dev_name;
 	struct device_attribute **attributes;
 
-	/* for android_conf.enabled_functions */
-	struct list_head enabled_list;
-
 	struct android_dev *android_dev;
 
 	/* Optional: initialization during gadget bind */
@@ -134,6 +132,14 @@
 					const struct usb_ctrlrequest *);
 };
 
+struct android_usb_function_holder {
+
+	struct android_usb_function *f;
+
+	/* for android_conf.enabled_functions */
+	struct list_head enabled_list;
+};
+
 struct android_dev {
 	const char *name;
 	struct android_usb_function **functions;
@@ -1713,15 +1719,15 @@
 android_bind_enabled_functions(struct android_dev *dev,
 			       struct usb_configuration *c)
 {
-	struct android_usb_function *f;
+	struct android_usb_function_holder *f_holder;
 	struct android_configuration *conf =
 		container_of(c, struct android_configuration, usb_config);
 	int ret;
 
-	list_for_each_entry(f, &conf->enabled_functions, enabled_list) {
-		ret = f->bind_config(f, c);
+	list_for_each_entry(f_holder, &conf->enabled_functions, enabled_list) {
+		ret = f_holder->f->bind_config(f_holder->f, c);
 		if (ret) {
-			pr_err("%s: %s failed", __func__, f->name);
+			pr_err("%s: %s failed", __func__, f_holder->f->name);
 			return ret;
 		}
 	}
@@ -1732,13 +1738,13 @@
 android_unbind_enabled_functions(struct android_dev *dev,
 			       struct usb_configuration *c)
 {
-	struct android_usb_function *f;
+	struct android_usb_function_holder *f_holder;
 	struct android_configuration *conf =
 		container_of(c, struct android_configuration, usb_config);
 
-	list_for_each_entry(f, &conf->enabled_functions, enabled_list) {
-		if (f->unbind_config)
-			f->unbind_config(f, c);
+	list_for_each_entry(f_holder, &conf->enabled_functions, enabled_list) {
+		if (f_holder->f->unbind_config)
+			f_holder->f->unbind_config(f_holder->f, c);
 	}
 }
 
@@ -1748,16 +1754,24 @@
 {
 	struct android_usb_function **functions = dev->functions;
 	struct android_usb_function *f;
+	struct android_usb_function_holder *f_holder;
 	while ((f = *functions++)) {
 		if (!strcmp(name, f->name)) {
-			if (f->android_dev)
-				pr_err("%s already enabled in other " \
-					"configuration or device\n",
+			if (f->android_dev && f->android_dev != dev)
+				pr_err("%s is enabled in other device\n",
 					f->name);
 			else {
-				list_add_tail(&f->enabled_list,
-					      &conf->enabled_functions);
+				f_holder = kzalloc(sizeof(*f_holder),
+						GFP_KERNEL);
+				if (!f_holder) {
+					pr_err("Failed to alloc f_holder\n");
+					return -ENOMEM;
+				}
+
 				f->android_dev = dev;
+				f_holder->f = f;
+				list_add_tail(&f_holder->enabled_list,
+					      &conf->enabled_functions);
 				return 0;
 			}
 		}
@@ -1817,7 +1831,7 @@
 {
 	struct android_dev *dev = dev_get_drvdata(pdev);
 	struct android_configuration *conf;
-	struct android_usb_function *f;
+	struct android_usb_function_holder *f_holder;
 	char *buff = buf;
 
 	mutex_lock(&dev->mutex);
@@ -1825,8 +1839,10 @@
 	list_for_each_entry(conf, &dev->configs, list_item) {
 		if (buff != buf)
 			*(buff-1) = ':';
-		list_for_each_entry(f, &conf->enabled_functions, enabled_list)
-			buff += snprintf(buff, PAGE_SIZE, "%s,", f->name);
+		list_for_each_entry(f_holder, &conf->enabled_functions,
+					enabled_list)
+			buff += snprintf(buff, PAGE_SIZE, "%s,",
+					f_holder->f->name);
 	}
 
 	mutex_unlock(&dev->mutex);
@@ -1841,10 +1857,10 @@
 			       const char *buff, size_t size)
 {
 	struct android_dev *dev = dev_get_drvdata(pdev);
-	struct android_usb_function *f;
 	struct list_head *curr_conf = &dev->configs;
 	struct android_configuration *conf;
 	char *conf_str;
+	struct android_usb_function_holder *f_holder;
 	char *name;
 	char buf[256], *b;
 	int err;
@@ -1858,8 +1874,15 @@
 
 	/* Clear previous enabled list */
 	list_for_each_entry(conf, &dev->configs, list_item) {
-		list_for_each_entry(f, &conf->enabled_functions, enabled_list)
-			f->android_dev = NULL;
+		while (conf->enabled_functions.next !=
+				&conf->enabled_functions) {
+			f_holder = list_entry(conf->enabled_functions.next,
+					typeof(*f_holder),
+					enabled_list);
+			f_holder->f->android_dev = NULL;
+			list_del(&f_holder->enabled_list);
+			kfree(f_holder);
+		}
 		INIT_LIST_HEAD(&conf->enabled_functions);
 	}
 
@@ -1916,7 +1939,7 @@
 {
 	struct android_dev *dev = dev_get_drvdata(pdev);
 	struct usb_composite_dev *cdev = dev->cdev;
-	struct android_usb_function *f;
+	struct android_usb_function_holder *f_holder;
 	struct android_configuration *conf;
 	int enabled = 0;
 
@@ -1938,20 +1961,20 @@
 		cdev->desc.bDeviceSubClass = device_desc.bDeviceSubClass;
 		cdev->desc.bDeviceProtocol = device_desc.bDeviceProtocol;
 		list_for_each_entry(conf, &dev->configs, list_item)
-			list_for_each_entry(f, &conf->enabled_functions,
+			list_for_each_entry(f_holder, &conf->enabled_functions,
 						enabled_list) {
-				if (f->enable)
-					f->enable(f);
+				if (f_holder->f->enable)
+					f_holder->f->enable(f_holder->f);
 			}
 		android_enable(dev);
 		dev->enabled = true;
 	} else if (!enabled && dev->enabled) {
 		android_disable(dev);
 		list_for_each_entry(conf, &dev->configs, list_item)
-			list_for_each_entry(f, &conf->enabled_functions,
+			list_for_each_entry(f_holder, &conf->enabled_functions,
 						enabled_list) {
-				if (f->disable)
-					f->disable(f);
+				if (f_holder->f->disable)
+					f_holder->f->disable(f_holder->f);
 			}
 		dev->enabled = false;
 	} else {
@@ -2199,6 +2222,7 @@
 	struct android_dev		*dev = cdev_to_android_dev(cdev);
 	struct usb_request		*req = cdev->req;
 	struct android_usb_function	*f;
+	struct android_usb_function_holder *f_holder;
 	struct android_configuration	*conf;
 	int value = -EOPNOTSUPP;
 	unsigned long flags;
@@ -2209,14 +2233,16 @@
 	gadget->ep0->driver_data = cdev;
 
 	list_for_each_entry(conf, &dev->configs, list_item)
-			list_for_each_entry(f,
-					    &conf->enabled_functions,
-					    enabled_list)
-				if (f->ctrlrequest) {
-					value = f->ctrlrequest(f, cdev, c);
-					if (value >= 0)
-						break;
-				}
+		list_for_each_entry(f_holder,
+				    &conf->enabled_functions,
+				    enabled_list) {
+			f = f_holder->f;
+			if (f->ctrlrequest) {
+				value = f->ctrlrequest(f, cdev, c);
+				if (value >= 0)
+					break;
+			}
+		}
 
 	/* Special case the accessory function.
 	 * It needs to handle control requests before it is enabled.
@@ -2384,11 +2410,26 @@
 
 static int __devinit android_probe(struct platform_device *pdev)
 {
-	struct android_usb_platform_data *pdata = pdev->dev.platform_data;
+	struct android_usb_platform_data *pdata;
 	struct android_dev *android_dev;
 	struct resource *res;
 	int ret = 0;
 
+	if (pdev->dev.of_node) {
+		dev_dbg(&pdev->dev, "device tree enabled\n");
+		pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
+		if (!pdata) {
+			pr_err("unable to allocate platform data\n");
+			return -ENOMEM;
+		}
+
+		of_property_read_u32(pdev->dev.of_node,
+				"qcom,android-usb-swfi-latency",
+				&pdata->swfi_latency);
+	} else {
+		pdata = pdev->dev.platform_data;
+	}
+
 	if (!android_class) {
 		android_class = class_create(THIS_MODULE, "android_usb");
 		if (IS_ERR(android_class))
diff --git a/drivers/usb/gadget/ci13xxx_udc.c b/drivers/usb/gadget/ci13xxx_udc.c
index fa1bf43..9c7b1ec 100644
--- a/drivers/usb/gadget/ci13xxx_udc.c
+++ b/drivers/usb/gadget/ci13xxx_udc.c
@@ -75,7 +75,7 @@
  *****************************************************************************/
 
 #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 */
 
@@ -177,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
@@ -381,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
@@ -390,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;
@@ -1006,29 +1040,30 @@
 }
 
 /**
- * dbg_prime_fail: prints a PRIME FAIL event
+ * dbg_usb_op_fail: prints USB Operation FAIL event
  * @addr: endpoint address
  * @mEp:  endpoint structure
  */
-static void dbg_prime_fail(u8 addr, const char *name,
-				const struct ci13xxx_ep *mEp)
+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) {
+	if (mep != NULL) {
 		scnprintf(msg, sizeof(msg),
-			  "PRIME fail EP%d%s QH:%08X",
-			  mEp->num, mEp->dir ? "IN" : "OUT", mEp->qh.ptr->cap);
+			"%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);
+				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) {
+		list_for_each(ptr, &mep->qh.queue) {
 			req = list_entry(ptr, struct ci13xxx_req, queue);
 			scnprintf(msg, sizeof(msg),
 					"%08X:%08X:%08X\n",
@@ -1703,52 +1738,52 @@
 
 static void ep_prime_timer_func(unsigned long data)
 {
-	struct ci13xxx_ep *mEp = (struct ci13xxx_ep *)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);
+	int n = hw_ep_bit(mep->num, mep->dir);
 	unsigned long flags;
 
 
-	spin_lock_irqsave(mEp->lock, flags);
+	spin_lock_irqsave(mep->lock, flags);
 	if (!hw_cread(CAP_ENDPTPRIME, BIT(n)))
 		goto out;
 
-	if (list_empty(&mEp->qh.queue))
+	if (list_empty(&mep->qh.queue))
 		goto out;
 
-	req = list_entry(mEp->qh.queue.next, struct ci13xxx_req, queue);
+	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;
+	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) {
+				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_prime_fail(0xFF, "PRIMEF", mEp);
-		mEp->prime_fail_count++;
+		dbg_usb_op_fail(0xFF, "PRIMEF", mep);
+		mep->prime_fail_count++;
 	} else {
-		mod_timer(&mEp->prime_timer, EP_PRIME_CHECK_DELAY);
+		mod_timer(&mep->prime_timer, EP_PRIME_CHECK_DELAY);
 	}
 
-	spin_unlock_irqrestore(mEp->lock, flags);
+	spin_unlock_irqrestore(mep->lock, flags);
 	return;
 
 out:
-	mEp->prime_timer_count = 0;
-	spin_unlock_irqrestore(mEp->lock, flags);
+	mep->prime_timer_count = 0;
+	spin_unlock_irqrestore(mep->lock, flags);
 
 }
 
@@ -1874,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
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 42a6c43..5659c79 100644
--- a/drivers/usb/gadget/f_accessory.c
+++ b/drivers/usb/gadget/f_accessory.c
@@ -132,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,
@@ -176,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 */
@@ -907,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);
@@ -1135,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;
diff --git a/drivers/usb/gadget/f_adb.c b/drivers/usb/gadget/f_adb.c
index 7966a79..68c99a3 100644
--- a/drivers/usb/gadget/f_adb.c
+++ b/drivers/usb/gadget/f_adb.c
@@ -56,6 +56,7 @@
 	struct usb_request *rx_req;
 	int rx_done;
 	bool notify_close;
+	bool close_notified;
 };
 
 static struct usb_interface_descriptor adb_interface_desc = {
@@ -68,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,
@@ -112,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);
 
@@ -424,8 +468,10 @@
 	/* clear the error latch */
 	atomic_set(&_adb_dev->error, 0);
 
-	if (_adb_dev->notify_close)
+	if (_adb_dev->close_notified) {
+		_adb_dev->close_notified = false;
 		adb_ready_callback();
+	}
 
 	_adb_dev->notify_close = true;
 	return 0;
@@ -443,8 +489,10 @@
 	 * undesired.  We want to force bus reset only for certain
 	 * commands like "adb root" and "adb usb".
 	 */
-	if (_adb_dev->notify_close)
+	if (_adb_dev->notify_close) {
 		adb_closed_callback();
+		_adb_dev->close_notified = true;
+	}
 
 	adb_unlock(&_adb_dev->open_excl);
 	return 0;
@@ -498,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",
@@ -600,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;
@@ -625,7 +682,9 @@
 	atomic_set(&dev->open_excl, 0);
 	atomic_set(&dev->read_excl, 0);
 	atomic_set(&dev->write_excl, 0);
-	dev->notify_close = true;
+
+	/* config is disabled by default if adb is present. */
+	dev->close_notified = true;
 
 	INIT_LIST_HEAD(&dev->tx_idle);
 
diff --git a/drivers/usb/gadget/f_diag.c b/drivers/usb/gadget/f_diag.c
index 87597d5..aca2af3 100644
--- a/drivers/usb/gadget/f_diag.c
+++ b/drivers/usb/gadget/f_diag.c
@@ -18,6 +18,7 @@
 #include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/platform_device.h>
+#include <linux/ratelimit.h>
 
 #include <mach/usbdiag.h>
 
@@ -73,6 +74,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 +121,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
@@ -384,6 +428,7 @@
 	struct diag_context *ctxt = ch->priv_usb;
 	unsigned long flags;
 	struct usb_request *req;
+	static DEFINE_RATELIMIT_STATE(rl, 10*HZ, 1);
 
 	if (!ctxt)
 		return -ENODEV;
@@ -413,7 +458,9 @@
 		spin_lock_irqsave(&ctxt->lock, flags);
 		list_add_tail(&req->list, &ctxt->read_pool);
 		spin_unlock_irqrestore(&ctxt->lock, flags);
-		ERROR(ctxt->cdev, "%s: cannot queue"
+		/* 1 error message for every 10 sec */
+		if (__ratelimit(&rl))
+			ERROR(ctxt->cdev, "%s: cannot queue"
 				" read request\n", __func__);
 		return -EIO;
 	}
@@ -440,6 +487,7 @@
 	struct diag_context *ctxt = ch->priv_usb;
 	unsigned long flags;
 	struct usb_request *req = NULL;
+	static DEFINE_RATELIMIT_STATE(rl, 10*HZ, 1);
 
 	if (!ctxt)
 		return -ENODEV;
@@ -469,7 +517,9 @@
 		spin_lock_irqsave(&ctxt->lock, flags);
 		list_add_tail(&req->list, &ctxt->write_pool);
 		spin_unlock_irqrestore(&ctxt->lock, flags);
-		ERROR(ctxt->cdev, "%s: cannot queue"
+		/* 1 error message for every 10 sec */
+		if (__ratelimit(&rl))
+			ERROR(ctxt->cdev, "%s: cannot queue"
 				" read request\n", __func__);
 		return -EIO;
 	}
@@ -551,6 +601,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 +632,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 +646,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 681435a..85240ef 100644
--- a/drivers/usb/gadget/f_mbim.c
+++ b/drivers/usb/gadget/f_mbim.c
@@ -541,32 +541,34 @@
 	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);
 
 	if (!atomic_read(&dev->online)) {
-		pr_info("dev:%p is not online\n", dev);
+		pr_err("dev:%p is not online\n", dev);
 		spin_unlock_irqrestore(&dev->lock, flags);
 		return;
 	}
 
 	if (!req) {
-		pr_info("dev:%p req is NULL\n", dev);
+		pr_err("dev:%p req is NULL\n", dev);
 		spin_unlock_irqrestore(&dev->lock, flags);
 		return;
 	}
 
 	if (!req->buf) {
-		pr_info("dev:%p req->buf is NULL\n", dev);
+		pr_err("dev:%p req->buf is NULL\n", dev);
 		spin_unlock_irqrestore(&dev->lock, flags);
 		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
@@ -577,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) {
@@ -586,7 +586,7 @@
 		pr_err("ep enqueue error %d\n", ret);
 	}
 
-	pr_info("Succcessfull Exit");
+	pr_debug("Successful Exit");
 }
 
 static int
@@ -601,10 +601,10 @@
 		return -ENODEV;
 	}
 
-	pr_info("dev:%p port_num#%d\n", dev, dev->port_num);
+	pr_debug("dev:%p port_num#%d\n", dev, dev->port_num);
 
 	if (!atomic_read(&dev->online)) {
-		pr_info("dev:%p is not connected\n", dev);
+		pr_err("dev:%p is not connected\n", dev);
 		mbim_free_ctrl_pkt(cpkt);
 		return 0;
 	}
@@ -672,7 +672,6 @@
 
 	mbim->ntb_input_size = NTB_DEFAULT_IN_SIZE;
 
-	atomic_set(&mbim->not_port.notify_count, 0);
 	atomic_set(&mbim->online, 0);
 }
 
@@ -740,7 +739,7 @@
 	__le32				*data;
 	int				status;
 
-	pr_info("notify_state: %d", mbim->not_port.notify_state);
+	pr_debug("notify_state: %d", mbim->not_port.notify_state);
 
 	if (!req)
 		return;
@@ -750,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:
@@ -782,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);
 	}
@@ -811,7 +827,7 @@
 	 * notification is sent, then it will reset to send the SPEED
 	 * notificaion again (and again, and again), but it's not a problem
 	 */
-	pr_info("dev:%p\n", mbim);
+	pr_debug("dev:%p\n", mbim);
 
 	mbim->not_port.notify_state = NCM_NOTIFY_SPEED;
 	mbim_do_notify(mbim);
@@ -822,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:
@@ -862,12 +865,10 @@
 		break;
 	}
 
-	mbim->not_port.notify_req = req;
 	mbim_do_notify(mbim);
-
 	spin_unlock(&mbim->lock);
 
-	pr_info("dev:%p Exit\n", mbim);
+	pr_debug("dev:%p Exit\n", mbim);
 }
 
 static void mbim_ep0out_complete(struct usb_ep *ep, struct usb_request *req)
@@ -878,7 +879,7 @@
 	struct f_mbim		*mbim = func_to_mbim(f);
 	struct mbim_ntb_input_size *ntb = NULL;
 
-	pr_info("dev:%p\n", mbim);
+	pr_debug("dev:%p\n", mbim);
 
 	req->context = NULL;
 	if (req->status || req->actual != req->length) {
@@ -908,7 +909,7 @@
 		goto invalid;
 	}
 
-	pr_info("Set NTB INPUT SIZE %d\n", in_size);
+	pr_debug("Set NTB INPUT SIZE %d\n", in_size);
 
 	mbim->ntb_input_size = in_size;
 	return;
@@ -938,29 +939,30 @@
 		return;
 	}
 
-	pr_info("dev:%p port#%d\n", dev, dev->port_num);
+	pr_debug("dev:%p port#%d\n", dev, dev->port_num);
+
+	cpkt = mbim_alloc_ctrl_pkt(len, GFP_ATOMIC);
+	if (!cpkt) {
+		pr_err("Unable to allocate ctrl pkt\n");
+		return;
+	}
+
+	pr_debug("Add to cpkt_req_q packet with len = %d\n", len);
+	memcpy(cpkt->buf, req->buf, len);
 
 	spin_lock(&dev->lock);
 	if (!dev->is_open) {
 		pr_err("mbim file handler %p is not open", dev);
 		spin_unlock(&dev->lock);
+		mbim_free_ctrl_pkt(cpkt);
 		return;
 	}
 
-	cpkt = mbim_alloc_ctrl_pkt(len, GFP_ATOMIC);
-	if (!cpkt) {
-		pr_err("Unable to allocate ctrl pkt\n");
-		spin_unlock(&dev->lock);
-		return;
-	}
-
-	pr_info("Add to cpkt_req_q packet with len = %d\n", len);
-	memcpy(cpkt->buf, req->buf, len);
 	list_add_tail(&cpkt->list, &dev->cpkt_req_q);
 	spin_unlock(&dev->lock);
 
 	/* wakeup read thread */
-	pr_info("Wake up read queue");
+	pr_debug("Wake up read queue");
 	wake_up(&dev->read_wq);
 
 	return;
@@ -992,7 +994,7 @@
 	case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
 		| USB_CDC_RESET_FUNCTION:
 
-		pr_info("USB_CDC_RESET_FUNCTION");
+		pr_debug("USB_CDC_RESET_FUNCTION");
 		value = 0;
 		req->complete = fmbim_reset_cmd_complete;
 		req->context = mbim;
@@ -1001,10 +1003,10 @@
 	case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
 		| USB_CDC_SEND_ENCAPSULATED_COMMAND:
 
-		pr_info("USB_CDC_SEND_ENCAPSULATED_COMMAND");
+		pr_debug("USB_CDC_SEND_ENCAPSULATED_COMMAND");
 
 		if (w_length > req->length) {
-			pr_err("w_length > req->length: %d > %d",
+			pr_debug("w_length > req->length: %d > %d",
 			w_length, req->length);
 		}
 		value = w_length;
@@ -1015,14 +1017,14 @@
 	case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
 		| USB_CDC_GET_ENCAPSULATED_RESPONSE:
 
-		pr_info("USB_CDC_GET_ENCAPSULATED_RESPONSE");
+		pr_debug("USB_CDC_GET_ENCAPSULATED_RESPONSE");
 
 		if (w_value) {
 			pr_err("w_length > 0: %d", w_length);
 			break;
 		}
 
-		pr_info("req%02x.%02x v%04x i%04x l%d\n",
+		pr_debug("req%02x.%02x v%04x i%04x l%d\n",
 			ctrl->bRequestType, ctrl->bRequest,
 			w_value, w_index, w_length);
 
@@ -1042,7 +1044,7 @@
 		memcpy(req->buf, cpkt->buf, value);
 		mbim_free_ctrl_pkt(cpkt);
 
-		pr_info("copied encapsulated_response %d bytes",
+		pr_debug("copied encapsulated_response %d bytes",
 			value);
 
 		break;
@@ -1050,7 +1052,7 @@
 	case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
 		| USB_CDC_GET_NTB_PARAMETERS:
 
-		pr_info("USB_CDC_GET_NTB_PARAMETERS");
+		pr_debug("USB_CDC_GET_NTB_PARAMETERS");
 
 		if (w_length == 0 || w_value != 0 || w_index != mbim->ctrl_id)
 			break;
@@ -1063,21 +1065,21 @@
 	case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
 		| USB_CDC_GET_NTB_INPUT_SIZE:
 
-		pr_info("USB_CDC_GET_NTB_INPUT_SIZE");
+		pr_debug("USB_CDC_GET_NTB_INPUT_SIZE");
 
 		if (w_length < 4 || w_value != 0 || w_index != mbim->ctrl_id)
 			break;
 
 		put_unaligned_le32(mbim->ntb_input_size, req->buf);
 		value = 4;
-		pr_info("Reply to host INPUT SIZE %d\n",
+		pr_debug("Reply to host INPUT SIZE %d\n",
 		     mbim->ntb_input_size);
 		break;
 
 	case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
 		| USB_CDC_SET_NTB_INPUT_SIZE:
 
-		pr_info("USB_CDC_SET_NTB_INPUT_SIZE");
+		pr_debug("USB_CDC_SET_NTB_INPUT_SIZE");
 
 		if (w_length != 4 && w_length != 8) {
 			pr_err("wrong NTB length %d", w_length);
@@ -1099,7 +1101,7 @@
 	{
 		uint16_t format;
 
-		pr_info("USB_CDC_GET_NTB_FORMAT");
+		pr_debug("USB_CDC_GET_NTB_FORMAT");
 
 		if (w_length < 2 || w_value != 0 || w_index != mbim->ctrl_id)
 			break;
@@ -1107,25 +1109,25 @@
 		format = (mbim->parser_opts == &ndp16_opts) ? 0x0000 : 0x0001;
 		put_unaligned_le16(format, req->buf);
 		value = 2;
-		pr_info("NTB FORMAT: sending %d\n", format);
+		pr_debug("NTB FORMAT: sending %d\n", format);
 		break;
 	}
 
 	case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
 		| USB_CDC_SET_NTB_FORMAT:
 	{
-		pr_info("USB_CDC_SET_NTB_FORMAT");
+		pr_debug("USB_CDC_SET_NTB_FORMAT");
 
 		if (w_length != 0 || w_index != mbim->ctrl_id)
 			break;
 		switch (w_value) {
 		case 0x0000:
 			mbim->parser_opts = &ndp16_opts;
-			pr_info("NCM16 selected\n");
+			pr_debug("NCM16 selected\n");
 			break;
 		case 0x0001:
 			mbim->parser_opts = &ndp32_opts;
-			pr_info("NCM32 selected\n");
+			pr_debug("NCM32 selected\n");
 			break;
 		default:
 			break;
@@ -1146,7 +1148,7 @@
 
 	 /* respond with data transfer or status phase? */
 	if (value >= 0) {
-		pr_info("control request: %02x.%02x v%04x i%04x l%d\n",
+		pr_debug("control request: %02x.%02x v%04x i%04x l%d\n",
 			ctrl->bRequestType, ctrl->bRequest,
 			w_value, w_index, w_length);
 		req->zero = (value < w_length);
@@ -1308,12 +1310,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);
@@ -1368,6 +1371,8 @@
 		mbim->not_port.notify->driver_data = NULL;
 	}
 
+	atomic_set(&mbim->not_port.notify_count, 0);
+
 	pr_info("mbim deactivated\n");
 }
 
@@ -1789,7 +1794,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");
@@ -1805,7 +1809,6 @@
 
 	spin_lock(&mbim->lock);
 	mbim->is_open = false;
-	mbim_notify(mbim);
 	spin_unlock(&mbim->lock);
 
 	mbim_unlock(&_mbim_dev->open_excl);
@@ -1818,7 +1821,7 @@
 	struct f_mbim *mbim = fp->private_data;
 	int ret = 0;
 
-	pr_info("Received command %d", cmd);
+	pr_debug("Received command %d", cmd);
 
 	if (mbim_lock(&mbim->ioctl_excl))
 		return -EBUSY;
diff --git a/drivers/usb/gadget/f_mtp.c b/drivers/usb/gadget/f_mtp.c
index ccbc330..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",
@@ -1126,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);
@@ -1239,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_rmnet.c b/drivers/usb/gadget/f_rmnet.c
index aa9daf3..79aac27 100644
--- a/drivers/usb/gadget/f_rmnet.c
+++ b/drivers/usb/gadget/f_rmnet.c
@@ -18,6 +18,7 @@
 #include <linux/spinlock.h>
 
 #include <mach/usb_gadget_xport.h>
+#include <mach/usb_bam.h>
 
 #include "u_rmnet.h"
 #include "gadget_chips.h"
@@ -49,6 +50,9 @@
 	struct list_head		cpkt_resp_q;
 	atomic_t			notify_count;
 	unsigned long			cpkts_len;
+
+	/* IPA / RmNet Bridge support*/
+	struct usb_bam_connect_ipa_params ipa_params;
 };
 
 #define NR_RMNET_PORTS	3
@@ -147,6 +151,71 @@
 	NULL,
 };
 
+/* Super speed support */
+static struct usb_endpoint_descriptor rmnet_ss_notify_desc  = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.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 =		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 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 =		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 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[] = {
@@ -365,7 +434,17 @@
 	case USB_GADGET_XPORT_BAM:
 	case USB_GADGET_XPORT_BAM2BAM:
 		ret = gbam_connect(&dev->port, port_num,
-						   dxport, port_num);
+						   dxport, port_num, NULL);
+		if (ret) {
+			pr_err("%s: gbam_connect failed: err:%d\n",
+					__func__, ret);
+			gsmd_ctrl_disconnect(&dev->port, port_num);
+			return ret;
+		}
+		break;
+	case USB_GADGET_XPORT_BAM2BAM_IPA:
+		ret = gbam_connect(&dev->port, port_num,
+					dxport, port_num, &(dev->ipa_params));
 		if (ret) {
 			pr_err("%s: gbam_connect failed: err:%d\n",
 					__func__, ret);
@@ -435,7 +514,11 @@
 	switch (dxport) {
 	case USB_GADGET_XPORT_BAM:
 	case USB_GADGET_XPORT_BAM2BAM:
-		gbam_disconnect(&dev->port, port_num, dxport);
+		gbam_disconnect(&dev->port, port_num, dxport, NULL);
+		break;
+	case USB_GADGET_XPORT_BAM2BAM_IPA:
+		gbam_disconnect(&dev->port, port_num, dxport,
+						&(dev->ipa_params));
 		break;
 	case USB_GADGET_XPORT_HSIC:
 		ghsic_data_disconnect(&dev->port, port_num);
@@ -460,6 +543,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);
@@ -484,6 +569,7 @@
 	case USB_GADGET_XPORT_BAM:
 		break;
 	case USB_GADGET_XPORT_BAM2BAM:
+	case USB_GADGET_XPORT_BAM2BAM_IPA:
 		gbam_suspend(&dev->port, port_num, dxport);
 		break;
 	case USB_GADGET_XPORT_HSIC:
@@ -513,6 +599,7 @@
 	case USB_GADGET_XPORT_BAM:
 		break;
 	case USB_GADGET_XPORT_BAM2BAM:
+	case USB_GADGET_XPORT_BAM2BAM_IPA:
 		gbam_resume(&dev->port, port_num, dxport);
 		break;
 	case USB_GADGET_XPORT_HSIC:
@@ -964,6 +1051,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 +1072,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 +1095,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;
@@ -1146,6 +1255,7 @@
 		no_data_bam_ports++;
 		break;
 	case USB_GADGET_XPORT_BAM2BAM:
+	case USB_GADGET_XPORT_BAM2BAM_IPA:
 		rmnet_port->data_xport_num = no_data_bam2bam_ports;
 		no_data_bam2bam_ports++;
 		break;
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 649fe14..74dba07 100644
--- a/drivers/usb/gadget/f_serial.c
+++ b/drivers/usb/gadget/f_serial.c
@@ -244,8 +244,37 @@
 	.bDescriptorType =      USB_DT_SS_ENDPOINT_COMP,
 };
 
+#ifdef CONFIG_MODEM_SUPPORT
+static struct usb_endpoint_descriptor gser_ss_notify_desc  = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.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/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/u_bam.c b/drivers/usb/gadget/u_bam.c
index f092329..7f3713f 100644
--- a/drivers/usb/gadget/u_bam.c
+++ b/drivers/usb/gadget/u_bam.c
@@ -101,6 +101,8 @@
 	u32					src_pipe_idx;
 	u32					dst_pipe_idx;
 	u8					connection_idx;
+	enum transport_type trans;
+	struct usb_bam_connect_ipa_params *ipa_params;
 
 	/* stats */
 	unsigned int		pending_with_bam;
@@ -640,6 +642,21 @@
 	clear_bit(BAM_CH_OPENED, &d->flags);
 }
 
+static void gbam2bam_disconnect_work(struct work_struct *w)
+{
+	struct gbam_port *port = container_of(w, struct gbam_port, connect_w);
+	struct bam_ch_info *d = &port->data_ch;
+	int ret;
+
+	if (d->trans == USB_GADGET_XPORT_BAM2BAM_IPA) {
+		ret = usb_bam_disconnect_ipa(d->connection_idx, d->ipa_params);
+		if (ret)
+			pr_err("%s: usb_bam_disconnect_ipa failed: err:%d\n",
+				__func__, ret);
+		rmnet_bridge_disconnect();
+	}
+}
+
 static void gbam_connect_work(struct work_struct *w)
 {
 	struct gbam_port *port = container_of(w, struct gbam_port, connect_w);
@@ -680,12 +697,38 @@
 	u32 sps_params;
 	int ret;
 
-	ret = usb_bam_connect(d->connection_idx, &d->src_pipe_idx,
-						  &d->dst_pipe_idx);
-	if (ret) {
-		pr_err("%s: usb_bam_connect failed: err:%d\n",
-			__func__, ret);
-		return;
+	if (d->trans == USB_GADGET_XPORT_BAM2BAM) {
+		ret = usb_bam_connect(d->connection_idx, &d->src_pipe_idx,
+							  &d->dst_pipe_idx);
+		if (ret) {
+			pr_err("%s: usb_bam_connect failed: err:%d\n",
+				__func__, ret);
+			return;
+		}
+	} else if (d->trans == USB_GADGET_XPORT_BAM2BAM_IPA) {
+		d->ipa_params->client = IPA_CLIENT_USB_CONS;
+		d->ipa_params->dir = PEER_PERIPHERAL_TO_USB;
+		ret = usb_bam_connect_ipa(d->ipa_params);
+		if (ret) {
+			pr_err("%s: usb_bam_connect_ipa failed: err:%d\n",
+				__func__, ret);
+			return;
+		}
+
+		d->ipa_params->client = IPA_CLIENT_USB_PROD;
+		d->ipa_params->dir = USB_TO_PEER_PERIPHERAL;
+		/* Currently only DMA mode is supported */
+		d->ipa_params->ipa_ep_cfg.mode.mode = IPA_DMA;
+		d->ipa_params->ipa_ep_cfg.mode.dst =
+				IPA_CLIENT_A2_TETHERED_CONS;
+		ret = usb_bam_connect_ipa(d->ipa_params);
+		if (ret) {
+			pr_err("%s: usb_bam_connect_ipa failed: err:%d\n",
+				__func__, ret);
+			return;
+		}
+		rmnet_bridge_connect(d->ipa_params->prod_clnt_hdl,
+				d->ipa_params->cons_clnt_hdl, 0);
 	}
 
 	d->rx_req = usb_ep_alloc_request(port->port_usb->out, GFP_KERNEL);
@@ -873,6 +916,7 @@
 	spin_lock_init(&port->port_lock_dl);
 
 	INIT_WORK(&port->connect_w, gbam2bam_connect_work);
+	INIT_WORK(&port->disconnect_w, gbam2bam_disconnect_work);
 
 	/* data ch */
 	d = &port->data_ch;
@@ -993,7 +1037,8 @@
 static void gam_debugfs_init(void) { }
 #endif
 
-void gbam_disconnect(struct grmnet *gr, u8 port_num, enum transport_type trans)
+void gbam_disconnect(struct grmnet *gr, u8 port_num, enum transport_type trans,
+			struct usb_bam_connect_ipa_params *ipa_params)
 {
 	struct gbam_port	*port;
 	unsigned long		flags;
@@ -1008,7 +1053,8 @@
 		return;
 	}
 
-	if (trans == USB_GADGET_XPORT_BAM2BAM &&
+	if ((trans == USB_GADGET_XPORT_BAM2BAM ||
+		 trans == USB_GADGET_XPORT_BAM2BAM_IPA) &&
 		port_num >= n_bam2bam_ports) {
 		pr_err("%s: invalid bam2bam portno#%d\n",
 			   __func__, port_num);
@@ -1044,12 +1090,14 @@
 	gr->in->driver_data = NULL;
 	gr->out->driver_data = NULL;
 
-	if (trans == USB_GADGET_XPORT_BAM)
+	if (trans == USB_GADGET_XPORT_BAM ||
+		trans == USB_GADGET_XPORT_BAM2BAM_IPA)
 		queue_work(gbam_wq, &port->disconnect_w);
 }
 
 int gbam_connect(struct grmnet *gr, u8 port_num,
-				 enum transport_type trans, u8 connection_idx)
+				 enum transport_type trans, u8 connection_idx,
+				 struct usb_bam_connect_ipa_params *ipa_params)
 {
 	struct gbam_port	*port;
 	struct bam_ch_info	*d;
@@ -1063,7 +1111,9 @@
 		return -ENODEV;
 	}
 
-	if (trans == USB_GADGET_XPORT_BAM2BAM && port_num >= n_bam2bam_ports) {
+	if ((trans == USB_GADGET_XPORT_BAM2BAM ||
+		trans == USB_GADGET_XPORT_BAM2BAM_IPA)
+		&& port_num >= n_bam2bam_ports) {
 		pr_err("%s: invalid portno#%d\n", __func__, port_num);
 		return -ENODEV;
 	}
@@ -1115,8 +1165,15 @@
 	if (trans == USB_GADGET_XPORT_BAM2BAM) {
 		port->gr = gr;
 		d->connection_idx = connection_idx;
+	} else if (trans == USB_GADGET_XPORT_BAM2BAM_IPA) {
+		d->ipa_params = ipa_params;
+		port->gr = gr;
+		d->ipa_params->src_pipe = &(d->src_pipe_idx);
+		d->ipa_params->dst_pipe = &(d->dst_pipe_idx);
+		d->ipa_params->idx = connection_idx;
 	}
 
+	d->trans = trans;
 	queue_work(gbam_wq, &port->connect_w);
 
 	return 0;
@@ -1195,7 +1252,8 @@
 	struct gbam_port	*port;
 	struct bam_ch_info *d;
 
-	if (trans != USB_GADGET_XPORT_BAM2BAM)
+	if (trans != USB_GADGET_XPORT_BAM2BAM &&
+		trans != USB_GADGET_XPORT_BAM2BAM_IPA)
 		return;
 
 	port = bam2bam_ports[port_num];
@@ -1211,7 +1269,8 @@
 	struct gbam_port	*port;
 	struct bam_ch_info *d;
 
-	if (trans != USB_GADGET_XPORT_BAM2BAM)
+	if (trans != USB_GADGET_XPORT_BAM2BAM &&
+		trans != USB_GADGET_XPORT_BAM2BAM_IPA)
 		return;
 
 	port = bam2bam_ports[port_num];
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 4931c1e..bba2ca6 100644
--- a/drivers/usb/gadget/u_qc_ether.c
+++ b/drivers/usb/gadget/u_qc_ether.c
@@ -332,6 +332,7 @@
 	net_dev = dev_get_by_name(&init_net, netname);
 
 	if (net_dev) {
+		dev_put(net_dev);
 		unregister_netdev(net_dev);
 		free_netdev(net_dev);
 	}
@@ -355,6 +356,10 @@
 
 	/* Extract the eth_qc_dev from the net device */
 	net_dev = dev_get_by_name(&init_net, netname);
+	if (!net_dev)
+		return ERR_PTR(-EINVAL);
+
+	dev_put(net_dev);
 	dev = netdev_priv(net_dev);
 
 	if (!dev)
@@ -400,6 +405,10 @@
 
 	/* Extract the eth_qc_dev from the net device */
 	net_dev = dev_get_by_name(&init_net, netname);
+	if (!net_dev)
+		return;
+
+	dev_put(net_dev);
 	dev = netdev_priv(net_dev);
 
 	if (!dev)
diff --git a/drivers/usb/gadget/u_rmnet.h b/drivers/usb/gadget/u_rmnet.h
index 0f7c4fb..a3d42fa 100644
--- a/drivers/usb/gadget/u_rmnet.h
+++ b/drivers/usb/gadget/u_rmnet.h
@@ -48,8 +48,10 @@
 
 int gbam_setup(unsigned int no_bam_port, unsigned int no_bam2bam_port);
 int gbam_connect(struct grmnet *gr, u8 port_num,
-				 enum transport_type trans, u8 connection_idx);
-void gbam_disconnect(struct grmnet *gr, u8 port_num, enum transport_type trans);
+				 enum transport_type trans, u8 connection_idx,
+				 struct usb_bam_connect_ipa_params *ipa_params);
+void gbam_disconnect(struct grmnet *gr, u8 port_num, enum transport_type trans,
+				struct usb_bam_connect_ipa_params *ipa_params);
 void gbam_suspend(struct grmnet *gr, u8 port_num, enum transport_type trans);
 void gbam_resume(struct grmnet *gr, u8 port_num, enum transport_type trans);
 int gsmd_ctrl_connect(struct grmnet *gr, int port_num);
diff --git a/drivers/usb/gadget/u_smd.c b/drivers/usb/gadget/u_smd.c
index ce285a3..effe418 100644
--- a/drivers/usb/gadget/u_smd.c
+++ b/drivers/usb/gadget/u_smd.c
@@ -72,6 +72,7 @@
 
 	struct smd_port_info	*pi;
 	struct delayed_work	connect_work;
+	struct work_struct	disconnect_work;
 
 	/* At present, smd does not notify
 	 * control bit change info from modem
@@ -589,6 +590,20 @@
 	}
 }
 
+static void gsmd_disconnect_work(struct work_struct *w)
+{
+	struct gsmd_port *port;
+	struct smd_port_info *pi;
+
+	port = container_of(w, struct gsmd_port, disconnect_work);
+	pi = port->pi;
+
+	pr_debug("%s: port:%p port#%d\n", __func__, port, port->port_num);
+
+	smd_close(port->pi->ch);
+	port->pi->ch = NULL;
+}
+
 static void gsmd_notify_modem(void *gptr, u8 portno, int ctrl_bits)
 {
 	struct gsmd_port *port;
@@ -731,10 +746,8 @@
 				~port->cbits_to_modem);
 	}
 
-	if (port->pi->ch) {
-		smd_close(port->pi->ch);
-		port->pi->ch = NULL;
-	}
+	if (port->pi->ch)
+		queue_work(gsmd_wq, &port->disconnect_work);
 }
 
 #define SMD_CH_MAX_LEN	20
@@ -819,6 +832,7 @@
 	INIT_WORK(&port->pull, gsmd_tx_pull);
 
 	INIT_DELAYED_WORK(&port->connect_work, gsmd_connect_work);
+	INIT_WORK(&port->disconnect_work, gsmd_disconnect_work);
 
 	smd_ports[portno].port = port;
 	pdrv = &smd_ports[portno].pdrv;
diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c
index fff9465..1a75bd7 100644
--- a/drivers/usb/host/ehci-hub.c
+++ b/drivers/usb/host/ehci-hub.c
@@ -337,7 +337,7 @@
 
 
 /* caller has locked the root hub, and should reset/reinit on error */
-static int ehci_bus_resume (struct usb_hcd *hcd)
+static int __maybe_unused ehci_bus_resume(struct usb_hcd *hcd)
 {
 	struct ehci_hcd		*ehci = hcd_to_ehci (hcd);
 	u32			temp;
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 cd02489..7d12598 100644
--- a/drivers/usb/host/ehci-msm-hsic.c
+++ b/drivers/usb/host/ehci-msm-hsic.c
@@ -76,12 +76,12 @@
 	struct clk		*phy_clk;
 	struct clk		*cal_clk;
 	struct regulator	*hsic_vddcx;
+	struct regulator	*hsic_gdsc;
 	bool			async_int;
 	atomic_t                in_lpm;
 	struct wake_lock	wlock;
 	int			peripheral_status_irq;
 	int			wakeup_irq;
-	int			wakeup_gpio;
 	bool			wakeup_irq_enabled;
 	atomic_t		pm_usage_cnt;
 	uint32_t		bus_perf_client;
@@ -104,6 +104,8 @@
 struct msm_hsic_hcd *__mehci;
 
 static bool debug_bus_voting_enabled = true;
+static u64 ehci_msm_hsic_dma_mask = DMA_BIT_MASK(32);
+
 
 static unsigned int enable_payload_log = 1;
 module_param(enable_payload_log, uint, S_IRUGO | S_IWUSR);
@@ -325,7 +327,7 @@
 #define ULPI_IO_TIMEOUT_USEC	(10 * 1000)
 
 #define USB_PHY_VDD_DIG_VOL_NONE	0 /*uV */
-#define USB_PHY_VDD_DIG_VOL_MIN		1000000 /* uV */
+#define USB_PHY_VDD_DIG_VOL_MIN		945000 /* uV */
 #define USB_PHY_VDD_DIG_VOL_MAX		1320000 /* uV */
 
 #define HSIC_DBG1_REG		0x38
@@ -394,6 +396,35 @@
 
 }
 
+/* Global Distributed Switch Controller (GDSC) init */
+static int msm_hsic_init_gdsc(struct msm_hsic_hcd *mehci, int init)
+{
+	int ret = 0;
+
+	if (IS_ERR(mehci->hsic_gdsc))
+		return 0;
+
+	if (!mehci->hsic_gdsc) {
+		mehci->hsic_gdsc = devm_regulator_get(mehci->dev,
+			"HSIC_GDSC");
+		if (IS_ERR(mehci->hsic_gdsc))
+			return 0;
+	}
+
+	if (init) {
+		ret = regulator_enable(mehci->hsic_gdsc);
+		if (ret) {
+			dev_err(mehci->dev, "unable to enable hsic gdsc\n");
+			return ret;
+		}
+	} else {
+		regulator_disable(mehci->hsic_gdsc);
+	}
+
+	return 0;
+
+}
+
 static int ulpi_read(struct msm_hsic_hcd *mehci, u32 reg)
 {
 	struct usb_hcd *hcd = hsic_to_hcd(mehci);
@@ -525,22 +556,11 @@
 	if (rc < 0) {
 		dev_err(mehci->dev, "gpio request failed for HSIC DATA\n");
 		goto free_strobe;
-		}
-
-	if (mehci->wakeup_gpio) {
-		rc = gpio_request(mehci->wakeup_gpio, "HSIC_WAKEUP_GPIO");
-		if (rc < 0) {
-			dev_err(mehci->dev, "gpio request failed for HSIC WAKEUP\n");
-			goto free_data;
-		}
 	}
 
 	return 0;
 
 free_gpio:
-	if (mehci->wakeup_gpio)
-		gpio_free(mehci->wakeup_gpio);
-free_data:
 	gpio_free(pdata->data);
 free_strobe:
 	gpio_free(pdata->strobe);
@@ -575,18 +595,22 @@
 #define HSIC_PAD_CALIBRATION	0xA8
 #define HSIC_GPIO_PAD_VAL	0x0A0AAA10
 #define LINK_RESET_TIMEOUT_USEC		(250 * 1000)
-static int msm_hsic_reset(struct msm_hsic_hcd *mehci)
+
+static void msm_hsic_phy_reset(struct msm_hsic_hcd *mehci)
 {
 	struct usb_hcd *hcd = hsic_to_hcd(mehci);
-	int ret;
-	struct msm_hsic_host_platform_data *pdata = mehci->dev->platform_data;
 
 	msm_hsic_clk_reset(mehci);
 
 	/* select ulpi phy */
 	writel_relaxed(0x80000000, USB_PORTSC);
-
 	mb();
+}
+
+static int msm_hsic_start(struct msm_hsic_hcd *mehci)
+{
+	struct msm_hsic_host_platform_data *pdata = mehci->dev->platform_data;
+	int ret;
 
 	/* HSIC init sequence when HSIC signals (Strobe/Data) are
 	routed via GPIOs */
@@ -647,12 +671,22 @@
 #define PHY_RESUME_TIMEOUT_USEC		(100 * 1000)
 
 #ifdef CONFIG_PM_SLEEP
+static int msm_hsic_reset(struct msm_hsic_hcd *mehci)
+{
+	/* reset HSIC phy */
+	msm_hsic_phy_reset(mehci);
+
+	/* HSIC init procedure (caliberation) */
+	return msm_hsic_start(mehci);
+}
+
 static int msm_hsic_suspend(struct msm_hsic_hcd *mehci)
 {
 	struct usb_hcd *hcd = hsic_to_hcd(mehci);
 	int cnt = 0, ret;
 	u32 val;
 	int none_vol, max_vol;
+	struct msm_hsic_host_platform_data *pdata = mehci->dev->platform_data;
 
 	if (atomic_read(&mehci->in_lpm)) {
 		dev_dbg(mehci->dev, "%s called in lpm\n", __func__);
@@ -731,6 +765,10 @@
 	enable_irq_wake(mehci->wakeup_irq);
 	enable_irq(mehci->wakeup_irq);
 
+	if (pdata && pdata->standalone_latency)
+		pm_qos_update_request(&mehci->pm_qos_req_dma,
+			PM_QOS_DEFAULT_VALUE);
+
 	wake_unlock(&mehci->wlock);
 
 	dev_info(mehci->dev, "HSIC-USB in low power mode\n");
@@ -745,12 +783,17 @@
 	unsigned temp;
 	int min_vol, max_vol;
 	unsigned long flags;
+	struct msm_hsic_host_platform_data *pdata = mehci->dev->platform_data;
 
 	if (!atomic_read(&mehci->in_lpm)) {
 		dev_dbg(mehci->dev, "%s called in !in_lpm\n", __func__);
 		return 0;
 	}
 
+	if (pdata && pdata->standalone_latency)
+		pm_qos_update_request(&mehci->pm_qos_req_dma,
+			pdata->standalone_latency + 1);
+
 	spin_lock_irqsave(&mehci->wakeup_lock, flags);
 	if (mehci->wakeup_irq_enabled) {
 		disable_irq_wake(mehci->wakeup_irq);
@@ -1043,9 +1086,9 @@
 				pm_qos_update_request(&mehci->pm_qos_req_dma,
 					pdata->swfi_latency + 1);
 			wait_for_completion(&mehci->gpt0_completion);
-			if (pdata && pdata->swfi_latency)
+			if (pdata && pdata->standalone_latency)
 				pm_qos_update_request(&mehci->pm_qos_req_dma,
-					PM_QOS_DEFAULT_VALUE);
+					pdata->standalone_latency + 1);
 			spin_lock_irq(&ehci->lock);
 		} else {
 			dbg_log_event(NULL, "FPR: Tightloop", 0);
@@ -1536,6 +1579,11 @@
 
 	dev_dbg(&pdev->dev, "ehci_msm-hsic probe\n");
 
+	if (!pdev->dev.dma_mask)
+		pdev->dev.dma_mask = &ehci_msm_hsic_dma_mask;
+	if (!pdev->dev.coherent_dma_mask)
+		pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
+
 	/* After parent device's probe is executed, it will be put in suspend
 	 * mode. When child device's probe is called, driver core is not
 	 * resuming parent device due to which parent will be in suspend even
@@ -1590,16 +1638,22 @@
 	if (pdata)
 		mehci->ehci.log2_irq_thresh = pdata->log2_irq_thresh;
 
+	ret = msm_hsic_init_gdsc(mehci, 1);
+	if (ret) {
+		dev_err(&pdev->dev, "unable to initialize GDSC\n");
+		ret = -ENODEV;
+		goto put_hcd;
+	}
+
 	res = platform_get_resource_byname(pdev,
 			IORESOURCE_IRQ,
 			"peripheral_status_irq");
 	if (res)
 		mehci->peripheral_status_irq = res->start;
 
-	res = platform_get_resource_byname(pdev, IORESOURCE_IO, "wakeup");
+	res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "wakeup");
 	if (res) {
-		mehci->wakeup_gpio = res->start;
-		mehci->wakeup_irq = MSM_GPIO_TO_INT(res->start);
+		mehci->wakeup_irq = res->start;
 		dev_dbg(mehci->dev, "wakeup_irq: %d\n", mehci->wakeup_irq);
 	}
 
@@ -1619,11 +1673,8 @@
 
 	init_completion(&mehci->rt_completion);
 	init_completion(&mehci->gpt0_completion);
-	ret = msm_hsic_reset(mehci);
-	if (ret) {
-		dev_err(&pdev->dev, "unable to initialize PHY\n");
-		goto deinit_vddcx;
-	}
+
+	msm_hsic_phy_reset(mehci);
 
 	ehci_wq = create_singlethread_workqueue("ehci_wq");
 	if (!ehci_wq) {
@@ -1637,7 +1688,13 @@
 	ret = usb_add_hcd(hcd, hcd->irq, IRQF_SHARED);
 	if (ret) {
 		dev_err(&pdev->dev, "unable to register HCD\n");
-		goto unconfig_gpio;
+		goto destroy_wq;
+	}
+
+	ret = msm_hsic_start(mehci);
+	if (ret) {
+		dev_err(&pdev->dev, "unable to initialize PHY\n");
+		goto destroy_wq;
 	}
 
 	device_init_wakeup(&pdev->dev, 1);
@@ -1693,9 +1750,9 @@
 
 	__mehci = mehci;
 
-	if (pdata && pdata->swfi_latency)
+	if (pdata && pdata->standalone_latency)
 		pm_qos_add_request(&mehci->pm_qos_req_dma,
-			PM_QOS_CPU_DMA_LATENCY, PM_QOS_DEFAULT_VALUE);
+			PM_QOS_CPU_DMA_LATENCY, pdata->standalone_latency + 1);
 
 	/*
 	 * This pdev->dev is assigned parent of root-hub by USB core,
@@ -1713,11 +1770,11 @@
 
 	return 0;
 
-unconfig_gpio:
+destroy_wq:
 	destroy_workqueue(ehci_wq);
-	msm_hsic_config_gpios(mehci, 0);
 deinit_vddcx:
 	msm_hsic_init_vddcx(mehci, 0);
+	msm_hsic_init_gdsc(mehci, 0);
 deinit_clocks:
 	msm_hsic_init_clocks(mehci, 0);
 unmap:
@@ -1734,7 +1791,7 @@
 	struct msm_hsic_hcd *mehci = hcd_to_hsic(hcd);
 	struct msm_hsic_host_platform_data *pdata = mehci->dev->platform_data;
 
-	if (pdata && pdata->swfi_latency)
+	if (pdata && pdata->standalone_latency)
 		pm_qos_remove_request(&mehci->pm_qos_req_dma);
 
 	if (mehci->peripheral_status_irq)
@@ -1766,6 +1823,7 @@
 	usb_remove_hcd(hcd);
 	msm_hsic_config_gpios(mehci, 0);
 	msm_hsic_init_vddcx(mehci, 0);
+	msm_hsic_init_gdsc(mehci, 0);
 
 	msm_hsic_init_clocks(mehci, 0);
 	wake_lock_destroy(&mehci->wlock);
@@ -1828,7 +1886,8 @@
 	 * when remote wakeup is received or interface driver
 	 * start I/O.
 	 */
-	if (!atomic_read(&mehci->pm_usage_cnt))
+	if (!atomic_read(&mehci->pm_usage_cnt) &&
+			pm_runtime_suspended(dev))
 		return 0;
 
 	ret = msm_hsic_resume(mehci);
@@ -1884,7 +1943,11 @@
 				msm_hsic_runtime_idle)
 };
 #endif
-
+static const struct of_device_id hsic_host_dt_match[] = {
+	{ .compatible = "qcom,hsic-host",
+	},
+	{}
+};
 static struct platform_driver ehci_msm_hsic_driver = {
 	.probe	= ehci_hsic_msm_probe,
 	.remove	= __devexit_p(ehci_hsic_msm_remove),
@@ -1893,5 +1956,6 @@
 #ifdef CONFIG_PM
 		.pm = &msm_hsic_dev_pm_ops,
 #endif
+		.of_match_table = hsic_host_dt_match,
 	},
 };
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/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index 3d9422f..38a3c15 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -1277,7 +1277,7 @@
 static void handle_port_status(struct xhci_hcd *xhci,
 		union xhci_trb *event)
 {
-	struct usb_hcd *hcd;
+	struct usb_hcd *hcd = NULL;
 	u32 port_id;
 	u32 temp, temp1;
 	int max_ports;
@@ -1331,6 +1331,8 @@
 	 */
 	/* Find the right roothub. */
 	hcd = xhci_to_hcd(xhci);
+	if (!hcd)
+		goto cleanup;
 	if ((major_revision == 0x03) != (hcd->speed == HCD_USB3))
 		hcd = xhci->shared_hcd;
 	bus_state = &xhci->bus_state[hcd_index(hcd)];
diff --git a/drivers/usb/misc/mdm_ctrl_bridge.c b/drivers/usb/misc/mdm_ctrl_bridge.c
index 2f7e2c3..abc7b86 100644
--- a/drivers/usb/misc/mdm_ctrl_bridge.c
+++ b/drivers/usb/misc/mdm_ctrl_bridge.c
@@ -320,7 +320,6 @@
 	dev_dbg(&dev->intf->dev, "%s:\n", __func__);
 
 	ctrl_bridge_set_cbits(dev->brdg->ch_id, 0);
-	usb_unlink_anchored_urbs(&dev->tx_submitted);
 
 	dev->brdg = NULL;
 }
@@ -718,6 +717,8 @@
 
 	platform_device_unregister(dev->pdev);
 
+	usb_unlink_anchored_urbs(&dev->tx_submitted);
+
 	kfree(dev->in_ctlreq);
 	kfree(dev->readbuf);
 	kfree(dev->intbuf);
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 18f8729..dde9312 100644
--- a/drivers/usb/otg/msm_otg.c
+++ b/drivers/usb/otg/msm_otg.c
@@ -82,6 +82,11 @@
 MODULE_PARM_DESC(override_phy_init,
 	"Override HSUSB PHY Init Settings");
 
+unsigned int lpm_disconnect_thresh = 1000;
+module_param(lpm_disconnect_thresh , uint, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(lpm_disconnect_thresh,
+	"Delay before entering LPM on USB disconnect");
+
 static DECLARE_COMPLETION(pmic_vbus_init);
 static struct msm_otg *the_msm_otg;
 static bool debug_aca_enabled;
@@ -480,6 +485,9 @@
 	ret = msm_otg_phy_clk_reset(motg);
 	if (ret)
 		return ret;
+	/* 10 usec delay is required according to spec */
+	if (IS_ERR(motg->phy_reset_clk))
+		usleep_range(10, 12);
 	ret = msm_otg_link_clk_reset(motg, 0);
 	if (ret)
 		return ret;
@@ -766,10 +774,12 @@
 	if (aca_enabled())
 		return 0;
 
-	if (atomic_read(&motg->in_lpm) == suspend &&
-		!atomic_read(&motg->suspend_work_pending))
-		return 0;
-
+	/*
+	 * UDC and HCD call usb_phy_set_suspend() to enter/exit LPM
+	 * during bus suspend/resume.  Update the relevant state
+	 * machine inputs and trigger LPM entry/exit.  Checking
+	 * in_lpm flag would avoid unnecessary work scheduling.
+	 */
 	if (suspend) {
 		switch (phy->state) {
 		case OTG_STATE_A_WAIT_BCON:
@@ -779,16 +789,18 @@
 		case OTG_STATE_A_HOST:
 			pr_debug("host bus suspend\n");
 			clear_bit(A_BUS_REQ, &motg->inputs);
-			queue_work(system_nrt_wq, &motg->sm_work);
+			if (!atomic_read(&motg->in_lpm))
+				queue_work(system_nrt_wq, &motg->sm_work);
 			break;
 		case OTG_STATE_B_PERIPHERAL:
 			pr_debug("peripheral bus suspend\n");
 			if (!(motg->caps & ALLOW_LPM_ON_DEV_SUSPEND))
 				break;
 			set_bit(A_BUS_SUSPEND, &motg->inputs);
-			atomic_set(&motg->suspend_work_pending, 1);
-			queue_delayed_work(system_nrt_wq, &motg->suspend_work,
-				USB_SUSPEND_DELAY_TIME);
+			if (!atomic_read(&motg->in_lpm))
+				queue_delayed_work(system_nrt_wq,
+					&motg->suspend_work,
+					USB_SUSPEND_DELAY_TIME);
 			break;
 
 		default:
@@ -796,20 +808,29 @@
 		}
 	} else {
 		switch (phy->state) {
+		case OTG_STATE_A_WAIT_BCON:
+			/* Remote wakeup or resume */
+			set_bit(A_BUS_REQ, &motg->inputs);
+			/* ensure hardware is not in low power mode */
+			if (atomic_read(&motg->in_lpm))
+				pm_runtime_resume(phy->dev);
+			break;
 		case OTG_STATE_A_SUSPEND:
 			/* Remote wakeup or resume */
 			set_bit(A_BUS_REQ, &motg->inputs);
 			phy->state = OTG_STATE_A_HOST;
 
 			/* ensure hardware is not in low power mode */
-			pm_runtime_resume(phy->dev);
+			if (atomic_read(&motg->in_lpm))
+				pm_runtime_resume(phy->dev);
 			break;
 		case OTG_STATE_B_PERIPHERAL:
 			pr_debug("peripheral bus resume\n");
 			if (!(motg->caps & ALLOW_LPM_ON_DEV_SUSPEND))
 				break;
 			clear_bit(A_BUS_SUSPEND, &motg->inputs);
-			queue_work(system_nrt_wq, &motg->sm_work);
+			if (atomic_read(&motg->in_lpm))
+				queue_work(system_nrt_wq, &motg->sm_work);
 			break;
 		default:
 			break;
@@ -844,9 +865,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;
 	}
@@ -1120,30 +1146,36 @@
 
 static void msm_otg_notify_host_mode(struct msm_otg *motg, bool host_mode)
 {
-	struct power_supply *usb = psy ? psy : &motg->usb_psy;
-
-	if (!usb) {
+	if (!psy) {
 		pr_err("No USB power supply registered!\n");
 		return;
 	}
 
-	if (psy) {
+	if (legacy_power_supply) {
 		/* legacy support */
-		if (host_mode)
+		if (host_mode) {
 			power_supply_set_scope(psy, POWER_SUPPLY_SCOPE_SYSTEM);
-		else
+		} else {
 			power_supply_set_scope(psy, POWER_SUPPLY_SCOPE_DEVICE);
-		return;
+			/*
+			 * VBUS comparator is disabled by PMIC charging driver
+			 * when SYSTEM scope is selected.  For ID_GND->ID_A
+			 * transition, give 50 msec delay so that PMIC charger
+			 * driver detect the VBUS and ready for accepting
+			 * charging current value from USB.
+			 */
+			if (test_bit(ID_A, &motg->inputs))
+				msleep(50);
+		}
 	} else {
 		motg->host_mode = host_mode;
-		power_supply_changed(usb);
+		power_supply_changed(psy);
 	}
 }
 
 static int msm_otg_notify_chg_type(struct msm_otg *motg)
 {
 	static int charger_type;
-	struct power_supply *usb = psy ? psy : &motg->usb_psy;
 
 	/*
 	 * TODO
@@ -1167,40 +1199,38 @@
 	else
 		charger_type = POWER_SUPPLY_TYPE_BATTERY;
 
-	if (!usb) {
+	if (!psy) {
 		pr_err("No USB power supply registered!\n");
 		return -EINVAL;
 	}
 
 	pr_debug("setting usb power supply type %d\n", charger_type);
-	power_supply_set_supply_type(usb, charger_type);
+	power_supply_set_supply_type(psy, charger_type);
 	return 0;
 }
 
 static int msm_otg_notify_power_supply(struct msm_otg *motg, unsigned mA)
 {
-	struct power_supply *usb = psy ? psy : &motg->usb_psy;
-
-	if (!usb) {
+	if (!psy) {
 		dev_dbg(motg->phy.dev, "no usb power supply registered\n");
 		goto psy_error;
 	}
 
 	if (motg->cur_power == 0 && mA > 2) {
 		/* Enable charging */
-		if (power_supply_set_online(usb, true))
+		if (power_supply_set_online(psy, true))
 			goto psy_error;
-		if (power_supply_set_current_limit(usb, 1000*mA))
+		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(usb, false))
+		if (power_supply_set_online(psy, false))
 			goto psy_error;
 		/* Set max current limit */
-		if (power_supply_set_current_limit(usb, 0))
+		if (power_supply_set_current_limit(psy, 0))
 			goto psy_error;
 	}
-	power_supply_changed(usb);
+	power_supply_changed(psy);
 	return 0;
 
 psy_error:
@@ -1568,6 +1598,26 @@
 	return 0;
 }
 
+static bool msm_otg_read_pmic_id_state(struct msm_otg *motg)
+{
+	unsigned long flags;
+	int id;
+
+	if (!motg->pdata->pmic_id_irq)
+		return -ENODEV;
+
+	local_irq_save(flags);
+	id = irq_read_line(motg->pdata->pmic_id_irq);
+	local_irq_restore(flags);
+
+	/*
+	 * If we can not read ID line state for some reason, treat
+	 * it as float. This would prevent MHL discovery and kicking
+	 * host mode unnecessarily.
+	 */
+	return !!id;
+}
+
 static int msm_otg_mhl_register_callback(struct msm_otg *motg,
 						void (*callback)(int on))
 {
@@ -1650,14 +1700,11 @@
 static bool msm_chg_mhl_detect(struct msm_otg *motg)
 {
 	bool ret, id;
-	unsigned long flags;
 
 	if (!motg->mhl_enabled)
 		return false;
 
-	local_irq_save(flags);
-	id = irq_read_line(motg->pdata->pmic_id_irq);
-	local_irq_restore(flags);
+	id = msm_otg_read_pmic_id_state(motg);
 
 	if (id)
 		return false;
@@ -2285,13 +2332,10 @@
 				clear_bit(B_SESS_VLD, &motg->inputs);
 		} else if (pdata->otg_control == OTG_PMIC_CONTROL) {
 			if (pdata->pmic_id_irq) {
-				unsigned long flags;
-				local_irq_save(flags);
-				if (irq_read_line(pdata->pmic_id_irq))
+				if (msm_otg_read_pmic_id_state(motg))
 					set_bit(ID, &motg->inputs);
 				else
 					clear_bit(ID, &motg->inputs);
-				local_irq_restore(flags);
 			}
 			/*
 			 * VBUS initial state is reported after PMIC
@@ -2439,8 +2483,25 @@
 			motg->chg_type = USB_INVALID_CHARGER;
 			msm_otg_notify_charger(motg, 0);
 			msm_otg_reset(otg->phy);
+			/*
+			 * There is a small window where ID interrupt
+			 * is not monitored during ID detection circuit
+			 * switch from ACA to PMIC.  Check ID state
+			 * before entering into low power mode.
+			 */
+			if (!msm_otg_read_pmic_id_state(motg)) {
+				pr_debug("process missed ID intr\n");
+				clear_bit(ID, &motg->inputs);
+				work = 1;
+				break;
+			}
 			pm_runtime_put_noidle(otg->phy->dev);
-			pm_runtime_suspend(otg->phy->dev);
+			/*
+			 * Only if autosuspend was enabled in probe, it will be
+			 * used here. Otherwise, no delay will be used.
+			 */
+			pm_runtime_mark_last_busy(otg->phy->dev);
+			pm_runtime_autosuspend(otg->phy->dev);
 		}
 		break;
 	case OTG_STATE_B_SRP_INIT:
@@ -2900,8 +2961,10 @@
 {
 	struct msm_otg *motg =
 		container_of(w, struct msm_otg, suspend_work.work);
-	atomic_set(&motg->suspend_work_pending, 0);
-	msm_otg_sm_work(&motg->sm_work);
+
+	/* This work is only for device bus suspend */
+	if (test_bit(A_BUS_SUSPEND, &motg->inputs))
+		msm_otg_sm_work(&motg->sm_work);
 }
 
 static irqreturn_t msm_otg_irq(int irq, void *data)
@@ -3068,10 +3131,9 @@
 {
 	static bool init;
 	struct msm_otg *motg = the_msm_otg;
-	struct usb_otg *otg = motg->phy.otg;
 
-	/* In A Host Mode, ignore received BSV interrupts */
-	if (otg->phy->state >= OTG_STATE_A_IDLE)
+	/* Ignore received BSV interrupts, if ID pin is GND */
+	if (!test_bit(ID, &motg->inputs))
 		return;
 
 	if (online) {
@@ -3106,10 +3168,8 @@
 	struct msm_otg *motg = container_of(w, struct msm_otg,
 						pmic_id_status_work.work);
 	int work = 0;
-	unsigned long flags;
 
-	local_irq_save(flags);
-	if (irq_read_line(motg->pdata->pmic_id_irq)) {
+	if (msm_otg_read_pmic_id_state(motg)) {
 		if (!test_and_set_bit(ID, &motg->inputs)) {
 			pr_debug("PMIC: ID set\n");
 			work = 1;
@@ -3128,7 +3188,6 @@
 		else
 			queue_work(system_nrt_wq, &motg->sm_work);
 	}
-	local_irq_restore(flags);
 
 }
 
@@ -3386,8 +3445,7 @@
 				  enum power_supply_property psp,
 				  union power_supply_propval *val)
 {
-	struct msm_otg *motg = container_of(psy, struct msm_otg,
-								usb_psy);
+	struct msm_otg *motg = container_of(psy, struct msm_otg, usb_psy);
 	switch (psp) {
 	case POWER_SUPPLY_PROP_SCOPE:
 		if (motg->host_mode)
@@ -3413,8 +3471,7 @@
 				  enum power_supply_property psp,
 				  const union power_supply_propval *val)
 {
-	struct msm_otg *motg = container_of(psy, struct msm_otg,
-								usb_psy);
+	struct msm_otg *motg = container_of(psy, struct msm_otg, usb_psy);
 
 	switch (psp) {
 	/* Process PMIC notification in PRESENT prop */
@@ -3436,6 +3493,21 @@
 	return 0;
 }
 
+static int otg_power_property_is_writeable_usb(struct power_supply *psy,
+						enum power_supply_property psp)
+{
+	switch (psp) {
+	case POWER_SUPPLY_PROP_PRESENT:
+	case POWER_SUPPLY_PROP_ONLINE:
+	case POWER_SUPPLY_PROP_CURRENT_MAX:
+		return 1;
+	default:
+		break;
+	}
+
+	return 0;
+}
+
 static char *otg_pm_power_supplied_to[] = {
 	"battery",
 };
@@ -3992,6 +4064,12 @@
 	pm_runtime_set_active(&pdev->dev);
 	pm_runtime_enable(&pdev->dev);
 
+	if (motg->pdata->delay_lpm_on_disconnect) {
+		pm_runtime_set_autosuspend_delay(&pdev->dev,
+			lpm_disconnect_thresh);
+		pm_runtime_use_autosuspend(&pdev->dev);
+	}
+
 	if (motg->pdata->bus_scale_table) {
 		motg->bus_perf_client =
 		    msm_bus_scale_register_client(motg->pdata->bus_scale_table);
@@ -4010,20 +4088,22 @@
 	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;
+	motg->usb_psy.property_is_writeable
+		= otg_power_property_is_writeable_usb;
 
-	if (motg->pdata->otg_control == OTG_PMIC_CONTROL) {
+	if (!pm8921_charger_register_vbus_sn(NULL)) {
 		/* if pm8921 use legacy implementation */
-		if (!pm8921_charger_register_vbus_sn(&msm_otg_set_vbus_state)) {
-			dev_dbg(motg->phy.dev, "%s: legacy support\n",
-				__func__);
-			legacy_power_supply = true;
-		} else {
-			ret = msm_otg_register_power_supply(pdev, motg);
-			if (ret)
-				goto remove_phy;
-		}
+		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/external_common.c b/drivers/video/msm/external_common.c
index c6ffaf2..0411baa 100644
--- a/drivers/video/msm/external_common.c
+++ b/drivers/video/msm/external_common.c
@@ -1407,30 +1407,37 @@
 	struct hdmi_disp_mode_list_type *disp_mode_list,
 	uint32 video_format)
 {
-	const struct hdmi_disp_mode_timing_type *timing =
-		hdmi_common_get_supported_mode(video_format);
-	boolean supported = timing != NULL;
+	const struct hdmi_disp_mode_timing_type *timing;
+	boolean supported = false;
+	boolean mhl_supported = true;
 
 	if (video_format >= HDMI_VFRMT_MAX)
 		return;
 
+	timing = hdmi_common_get_supported_mode(video_format);
+	supported = timing != NULL;
 	DEV_DBG("EDID: format: %d [%s], %s\n",
 		video_format, video_format_2string(video_format),
 		supported ? "Supported" : "Not-Supported");
-	if (supported) {
-		if (mhl_is_enabled()) {
-			const struct hdmi_disp_mode_timing_type *mhl_timing =
-				hdmi_mhl_get_supported_mode(video_format);
-			boolean mhl_supported = mhl_timing != NULL;
-			DEV_DBG("EDID: format: %d [%s], %s by MHL\n",
+
+	if (mhl_is_enabled()) {
+		const struct hdmi_disp_mode_timing_type *mhl_timing =
+			hdmi_mhl_get_supported_mode(video_format);
+		mhl_supported = mhl_timing != NULL;
+		DEV_DBG("EDID: format: %d [%s], %s by MHL\n",
 			video_format, video_format_2string(video_format),
-				mhl_supported ? "Supported" : "Not-Supported");
-			if (mhl_supported)
-				disp_mode_list->disp_mode_list[
+			mhl_supported ? "Supported" : "Not-Supported");
+	}
+
+	if (supported && mhl_supported) {
+		disp_mode_list->disp_mode_list[
 			disp_mode_list->num_of_elements++] = video_format;
-		} else
-			disp_mode_list->disp_mode_list[
-			disp_mode_list->num_of_elements++] = video_format;
+		if (video_format == external_common_state->video_resolution) {
+			DEV_DBG("%s: Default resolution %d [%s] supported\n",
+					__func__, video_format,
+					video_format_2string(video_format));
+			external_common_state->default_res_supported = true;
+		}
 	}
 }
 
@@ -1866,6 +1873,7 @@
 	memset(&external_common_state->disp_mode_list, 0,
 		sizeof(external_common_state->disp_mode_list));
 	memset(edid_buf, 0, sizeof(edid_buf));
+	external_common_state->default_res_supported = false;
 
 	status = hdmi_common_read_edid_block(0, edid_buf);
 	if (status || !check_edid_header(edid_buf)) {
diff --git a/drivers/video/msm/external_common.h b/drivers/video/msm/external_common.h
index 43a8794..70a99ee 100644
--- a/drivers/video/msm/external_common.h
+++ b/drivers/video/msm/external_common.h
@@ -210,8 +210,10 @@
 	boolean hpd_state;
 	struct kobject *uevent_kobj;
 	uint32 video_resolution;
+	boolean default_res_supported;
 	struct device *dev;
 	struct switch_dev sdev;
+	struct switch_dev audio_sdev;
 #ifdef CONFIG_FB_MSM_HDMI_3D
 	boolean format_3d;
 	void (*switch_3d)(boolean on);
diff --git a/drivers/video/msm/hdmi_msm.c b/drivers/video/msm/hdmi_msm.c
index 9f30041..516c92c 100644
--- a/drivers/video/msm/hdmi_msm.c
+++ b/drivers/video/msm/hdmi_msm.c
@@ -57,6 +57,22 @@
 #define HDCP_DDC_CTRL_1		0x0124
 #define HDMI_DDC_CTRL		0x020C
 
+#define HPD_DISCONNECT_POLARITY	0
+#define HPD_CONNECT_POLARITY	1
+
+#define SWITCH_SET_HDMI_AUDIO(d, force) \
+	do {\
+		if (!hdmi_msm_is_dvi_mode() &&\
+			((force) ||\
+			 (external_common_state->audio_sdev.state != (d)))) {\
+			switch_set_state(&external_common_state->audio_sdev,\
+					(d));\
+			DEV_INFO("%s: hdmi_audio state switched to %d\n",\
+				__func__,\
+				external_common_state->audio_sdev.state);\
+		} \
+	} while (0)
+
 struct workqueue_struct *hdmi_work_queue;
 struct hdmi_msm_state_type *hdmi_msm_state;
 
@@ -74,6 +90,7 @@
 static int hdmi_msm_audio_off(void);
 static int hdmi_msm_read_edid(void);
 static void hdmi_msm_hpd_off(void);
+static boolean hdmi_msm_is_dvi_mode(void);
 
 #ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_CEC_SUPPORT
 
@@ -768,8 +785,8 @@
 		/* Build EDID table */
 		hdmi_msm_read_edid();
 		switch_set_state(&external_common_state->sdev, 1);
-		DEV_INFO("Hdmi state switched to %d: %s\n",
-			external_common_state->sdev.state,  __func__);
+		DEV_INFO("%s: hdmi state switched to %d\n", __func__,
+				external_common_state->sdev.state);
 
 		DEV_INFO("HDMI HPD: CONNECTED: send ONLINE\n");
 		kobject_uevent(external_common_state->uevent_kobj, KOBJ_ONLINE);
@@ -783,8 +800,8 @@
 		}
 	} else {
 		switch_set_state(&external_common_state->sdev, 0);
-		DEV_INFO("hdmi: Hdmi state switch to %d: %s\n",
-			external_common_state->sdev.state,  __func__);
+		DEV_INFO("%s: hdmi state switch to %d\n", __func__,
+				external_common_state->sdev.state);
 		DEV_INFO("hdmi: HDMI HPD: sense DISCONNECTED: send OFFLINE\n");
 		kobject_uevent(external_common_state->uevent_kobj,
 			KOBJ_OFFLINE);
@@ -793,66 +810,13 @@
 
 static void hdmi_msm_hpd_state_work(struct work_struct *work)
 {
-	boolean hpd_state;
-
 	if (!hdmi_msm_state || !hdmi_msm_state->hpd_initialized ||
 		!MSM_HDMI_BASE) {
 		DEV_ERR("hdmi: %s: ignored, probe failed\n", __func__);
 		return;
 	}
 
-	mutex_lock(&hdmi_msm_state_mutex);
-	DEV_DBG("%s: Handling HPD event in the workqueue\n", __func__);
-
-	if (!hdmi_msm_state->hpd_cable_chg_detected) {
-		/* The work item got called from outside the ISR */
-		mutex_unlock(&hdmi_msm_state_mutex);
-		if (external_common_state->hpd_state) {
-			if (!external_common_state->
-					disp_mode_list.num_of_elements)
-				hdmi_msm_read_edid();
-		}
-	} else {
-		hdmi_msm_state->hpd_cable_chg_detected = FALSE;
-		mutex_unlock(&hdmi_msm_state_mutex);
-		mutex_lock(&external_common_state_hpd_mutex);
-		/*
-		 * Handle the connect event only if the cable is
-		 * still connected. This check is needed for the case
-		 * where we get a connect event followed by a disconnect
-		 * event in quick succession. In this case, there is no need
-		 * to process the connect event.
-		 */
-		if ((external_common_state->hpd_state) &&
-				!((HDMI_INP(0x0250) & 0x2) >> 1)) {
-			external_common_state->hpd_state = 0;
-			hdmi_msm_state->hpd_state_in_isr = 0;
-			mutex_unlock(&external_common_state_hpd_mutex);
-			DEV_DBG("%s: Ignoring HPD connect event\n", __func__);
-			return;
-		}
-		mutex_unlock(&external_common_state_hpd_mutex);
-		hdmi_msm_send_event(external_common_state->hpd_state);
-	}
-
-	/*
-	 * Wait for a short time before checking for
-	 * any changes in the connection status
-	 */
-	udelay(100);
-
-	mutex_lock(&external_common_state_hpd_mutex);
-	/* HPD_INT_STATUS[0x0250] */
-	hpd_state = (HDMI_INP(0x0250) & 0x2) >> 1;
-
-	if (external_common_state->hpd_state != hpd_state) {
-		external_common_state->hpd_state = hpd_state;
-		hdmi_msm_state->hpd_state_in_isr = hpd_state;
-		mutex_unlock(&external_common_state_hpd_mutex);
-		hdmi_msm_send_event(hpd_state);
-	} else {
-		mutex_unlock(&external_common_state_hpd_mutex);
-	}
+	hdmi_msm_send_event(external_common_state->hpd_state);
 }
 
 #ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_CEC_SUPPORT
@@ -965,9 +929,7 @@
 		DEV_INFO("HDCP: AUTH_FAIL_INT received, LINK0_STATUS=0x%08x\n",
 			link_status);
 		if (hdmi_msm_state->full_auth_done) {
-			switch_set_state(&external_common_state->sdev, 0);
-			DEV_INFO("Hdmi state switched to %d: %s\n",
-				external_common_state->sdev.state,  __func__);
+			SWITCH_SET_HDMI_AUDIO(0, 0);
 
 			envp[0] = "HDCP_STATE=FAIL";
 			envp[1] = NULL;
@@ -1051,55 +1013,18 @@
 	/* HDMI_HPD_INT_CTRL[0x0254] */
 	hpd_int_ctrl = HDMI_INP_ND(0x0254);
 	if ((hpd_int_ctrl & (1 << 2)) && (hpd_int_status & (1 << 0))) {
-		boolean cable_detected = (hpd_int_status & 2) >> 1;
-		DEV_DBG("%s: HPD IRQ, Ctrl=%04x, State=%04x\n", __func__,
-				hpd_int_ctrl, hpd_int_status);
+		/*
+		 * Got HPD interrupt. Ack the interrupt and disable any
+		 * further HPD interrupts until we process this interrupt.
+		 */
+		HDMI_OUTP(0x0254, ((hpd_int_ctrl | (BIT(0))) & ~BIT(2)));
 
-		/* Ack the interrupt */
-		HDMI_OUTP(0x0254, (hpd_int_ctrl | (1 << 0)));
-
-		mutex_lock(&external_common_state_hpd_mutex);
-		if (hdmi_msm_state->hpd_state_in_isr == cable_detected) {
-			DEV_INFO("%s: HPD has the same state. Ignoring\n",
-					__func__);
-			mutex_unlock(&external_common_state_hpd_mutex);
-		} else {
-			if (!mod_timer(&hdmi_msm_state->hpd_state_timer,
-						jiffies + HZ/2)) {
-				hdmi_msm_state->hpd_state_in_isr =
-					cable_detected;
-				hdmi_msm_state->hpd_cable_chg_detected = TRUE;
-				DEV_DBG("%s: Scheduled work to handle HPD %s\n",
-						__func__,
-						cable_detected ? "connect"
-						: "disconnect");
-			}
-
-			mutex_unlock(&external_common_state_hpd_mutex);
-			/*
-			 * HDCP Compliance 1A-01:
-			 * The Quantum Data Box 882 triggers two consecutive
-			 * HPD events very close to each other as a part of this
-			 * test which can trigger two parallel HDCP auth threads
-			 * if HDCP authentication is going on and we get ISR
-			 * then stop the authentication , rather than
-			 * reauthenticating it again
-			 */
-			if (hdmi_msm_state->hdcp_activating &&
-					!(hdmi_msm_state->full_auth_done)) {
-				DEV_DBG("%s getting hpd while authenticating\n",
-					    __func__);
-				mutex_lock(&hdcp_auth_state_mutex);
-				hdmi_msm_state->hpd_during_auth = TRUE;
-				mutex_unlock(&hdcp_auth_state_mutex);
-			}
-		}
-
-		/* Set up HPD_CTRL to sense HPD event */
-		HDMI_OUTP(0x0254, 4 | (cable_detected ? 0 : 2));
-		DEV_DBG("%s: Setting HPD_CTRL=%d\n", __func__,
-				HDMI_INP(0x0254));
-
+		external_common_state->hpd_state =
+			(HDMI_INP(0x0250) & BIT(1)) >> 1;
+		DEV_DBG("%s: Queuing work to handle HPD %s event\n", __func__,
+				external_common_state->hpd_state ? "connect" :
+				"disconnect");
+		queue_work(hdmi_work_queue, &hdmi_msm_state->hpd_state_work);
 		return IRQ_HANDLED;
 	}
 
@@ -2468,8 +2393,8 @@
 		/* 0x0110 HDCP_CTRL
 			[8] ENCRYPTION_ENABLE
 			[0] ENABLE */
-		/* encryption_enable | enable  */
-		HDMI_OUTP(0x0110, (1 << 8) | (1 << 0));
+		/* Enable HDCP. Encryption should be enabled after reading R0 */
+		HDMI_OUTP(0x0110, BIT(0));
 
 		/*
 		 * Check to see if a HDCP DDC Failure is indicated in
@@ -2664,6 +2589,9 @@
 			goto error;
 		}
 
+		/* Enable HDCP Encryption */
+		HDMI_OUTP(0x0110, BIT(0) | BIT(8));
+
 		DEV_INFO("HDCP: authentication part I, successful\n");
 		is_part1_done = FALSE;
 		return 0;
@@ -3022,20 +2950,16 @@
 	hdmi_msm_state->hdcp_activating = TRUE;
 	mutex_unlock(&hdmi_msm_state_mutex);
 
-	fill_black_screen();
-
 	mutex_lock(&hdcp_auth_state_mutex);
-	/*
-	 * Initialize this to zero here to make
-	 * sure HPD has not happened yet
-	 */
-	hdmi_msm_state->hpd_during_auth = FALSE;
 	/* This flag prevents other threads from re-authenticating
 	* after we've just authenticated (i.e., finished part3)
 	* We probably need to protect this in a mutex lock */
 	hdmi_msm_state->full_auth_done = FALSE;
 	mutex_unlock(&hdcp_auth_state_mutex);
 
+	/* Disable HDCP before we start part1 */
+	HDMI_OUTP(0x0110, 0x0);
+
 	/* PART I Authentication*/
 	ret = hdcp_authentication_part1();
 	if (ret)
@@ -3065,8 +2989,6 @@
 	if (ret)
 		goto error;
 
-	unfill_black_screen();
-
 	mutex_lock(&hdmi_msm_state_mutex);
 	hdmi_msm_state->hdcp_activating = FALSE;
 	mutex_unlock(&hdmi_msm_state_mutex);
@@ -3086,17 +3008,13 @@
 		envp[1] = NULL;
 		kobject_uevent_env(external_common_state->uevent_kobj,
 		    KOBJ_CHANGE, envp);
+
+		SWITCH_SET_HDMI_AUDIO(1, 0);
 	}
 
-	switch_set_state(&external_common_state->sdev, 1);
-	DEV_INFO("Hdmi state switched to %d: %s\n",
-		external_common_state->sdev.state, __func__);
 	return;
 
 error:
-	mutex_lock(&hdmi_msm_state_mutex);
-	hdmi_msm_state->hdcp_activating = FALSE;
-	mutex_unlock(&hdmi_msm_state_mutex);
 	if (hdmi_msm_state->hpd_during_auth) {
 		DEV_WARN("Calling Deauthentication: HPD occured during "
 			 "authentication  from [%s]\n", __func__);
@@ -3110,9 +3028,9 @@
 			queue_work(hdmi_work_queue,
 			    &hdmi_msm_state->hdcp_reauth_work);
 	}
-	switch_set_state(&external_common_state->sdev, 0);
-	DEV_INFO("Hdmi state switched to %d: %s\n",
-		external_common_state->sdev.state, __func__);
+	mutex_lock(&hdmi_msm_state_mutex);
+	hdmi_msm_state->hdcp_activating = FALSE;
+	mutex_unlock(&hdmi_msm_state_mutex);
 }
 
 static void hdmi_msm_video_setup(int video_format)
@@ -3667,28 +3585,24 @@
 
 static int hdmi_msm_audio_off(void)
 {
-	uint32 audio_pkt_ctrl, audio_cfg;
-	 /* Number of wait iterations */
-	int i = 10;
-	audio_pkt_ctrl = HDMI_INP_ND(0x0020);
-	audio_cfg = HDMI_INP_ND(0x01D0);
+	uint32 audio_cfg;
+	int i, timeout_val = 50;
 
-	/* Checking BIT[0] of AUDIO PACKET CONTROL and */
-	/* AUDIO CONFIGURATION register */
-	while (((audio_pkt_ctrl & 0x00000001) || (audio_cfg & 0x00000001))
-		&& (i--)) {
-		audio_pkt_ctrl = HDMI_INP_ND(0x0020);
-		audio_cfg = HDMI_INP_ND(0x01D0);
-		DEV_DBG("%d times :: HDMI AUDIO PACKET is %08x and "
-		"AUDIO CFG is %08x", i, audio_pkt_ctrl, audio_cfg);
-		msleep(100);
-		if (!i) {
-			DEV_ERR("%s:failed to set BIT[0] AUDIO PACKET"
-			"CONTROL or AUDIO CONFIGURATION REGISTER\n",
-				__func__);
-			return -ETIMEDOUT;
+	for (i = 0; (i < timeout_val) &&
+		((audio_cfg = HDMI_INP_ND(0x01D0)) & BIT(0)); i++) {
+		DEV_DBG("%s: %d times: AUDIO CFG is %08xi\n", __func__,
+				i+1, audio_cfg);
+		if (!((i+1) % 10)) {
+			DEV_ERR("%s: audio still on after %d sec. try again\n",
+				__func__, (i+1)/10);
+			SWITCH_SET_HDMI_AUDIO(0, 1);
 		}
+		msleep(100);
 	}
+
+	if (i == timeout_val)
+		DEV_ERR("%s: Error: cannot turn off audio engine\n", __func__);
+
 	hdmi_msm_audio_info_setup(FALSE, 0, 0, 0, FALSE);
 	hdmi_msm_audio_acr_setup(FALSE, 0, 0, 0);
 	DEV_INFO("HDMI Audio: Disabled\n");
@@ -4201,8 +4115,17 @@
 	hdmi_msm_set_mode(TRUE);
 
 	hdmi_msm_video_setup(external_common_state->video_resolution);
-	if (!hdmi_msm_is_dvi_mode())
+	if (!hdmi_msm_is_dvi_mode()) {
 		hdmi_msm_audio_setup();
+
+		/*
+		 * Send the audio switch device notification if HDCP is
+		 * not enabled. Otherwise, the notification would be
+		 * sent after HDCP authentication is successful.
+		 */
+		if (!hdmi_msm_state->hdcp_enable)
+			SWITCH_SET_HDMI_AUDIO(1, 0);
+	}
 	hdmi_msm_avi_info_frame();
 #ifdef CONFIG_FB_MSM_HDMI_3D
 	hdmi_msm_vendor_infoframe_packetsetup();
@@ -4227,36 +4150,6 @@
 	DEV_INFO("HDMI Core: Initialized\n");
 }
 
-static void hdmi_msm_hpd_state_timer(unsigned long data)
-{
-	if (!work_busy(&hdmi_msm_state->hpd_state_work)) {
-		/*
-		 * There is no event currently queued.
-		 * Only queue the work if this event has not already
-		 * been processed.
-		 */
-		if (external_common_state->hpd_state !=
-				hdmi_msm_state->hpd_state_in_isr) {
-			/*
-			 * There is no need to use any synchronization
-			 * construct for safeguarding these state vairables
-			 * here since the only other place these are modified
-			 * is in the HPD work thread, which is known to be not
-			 * pending/running.
-			 */
-			external_common_state->hpd_state =
-				hdmi_msm_state->hpd_state_in_isr;
-			DEV_DBG("%s: Queuing work to handle HPD %s event\n",
-					__func__,
-					external_common_state->hpd_state ?
-					"connect" : "disconnect");
-			queue_work(hdmi_work_queue,
-					&hdmi_msm_state->hpd_state_work);
-			return;
-		}
-	}
-}
-
 static void hdmi_msm_hdcp_timer(unsigned long data)
 {
 	if (!hdmi_msm_state->hdcp_enable) {
@@ -4274,6 +4167,27 @@
 }
 #endif
 
+static void hdmi_msm_hpd_polarity_setup(bool polarity, bool trigger)
+{
+	u32 cable_sense;
+	if (polarity)
+		HDMI_OUTP(0x0254, BIT(2) | BIT(1));
+	else
+		HDMI_OUTP(0x0254, BIT(2));
+
+	cable_sense = (HDMI_INP(0x0250) & BIT(1)) >> 1;
+	DEV_DBG("%s: listen=%s, sense=%s\n", __func__,
+		polarity ? "connect" : "disconnect",
+		cable_sense ? "connect" : "disconnect");
+	if (trigger && (cable_sense == polarity)) {
+		u32 reg_val = HDMI_INP(0x0258);
+
+		/* Toggle HPD circuit to trigger HPD sense */
+		HDMI_OUTP(0x0258, reg_val & ~BIT(28));
+		HDMI_OUTP(0x0258, reg_val | BIT(28));
+	}
+}
+
 static void hdmi_msm_hpd_off(void)
 {
 	int rc = 0;
@@ -4284,7 +4198,6 @@
 	}
 
 	DEV_DBG("%s: (timer, 5V, IRQ off)\n", __func__);
-	del_timer(&hdmi_msm_state->hpd_state_timer);
 	disable_irq(hdmi_msm_state->irq);
 
 	/* Disable HPD interrupt */
@@ -4353,27 +4266,22 @@
 		/* Set up HPD state variables */
 		mutex_lock(&external_common_state_hpd_mutex);
 		external_common_state->hpd_state = 0;
-		hdmi_msm_state->hpd_state_in_isr = 0;
 		mutex_unlock(&external_common_state_hpd_mutex);
 		mutex_lock(&hdmi_msm_state_mutex);
-		hdmi_msm_state->hpd_cable_chg_detected = TRUE;
 		mutex_unlock(&hdmi_msm_state_mutex);
 
-		/* Set up HPD_CTRL to sense HPD event */
-		HDMI_OUTP(0x0254, 0x6);
-		DEV_DBG("%s: Setting HPD_CTRL=%d\n", __func__,
-				HDMI_INP(0x0254));
+		enable_irq(hdmi_msm_state->irq);
 
 		hdmi_msm_state->hpd_initialized = TRUE;
 
-		enable_irq(hdmi_msm_state->irq);
-
 		/* set timeout to 4.1ms (max) for hardware debounce */
 		hpd_ctrl = HDMI_INP(0x0258) | 0x1FFF;
 
-		/* Toggle HPD circuit to trigger HPD sense */
-		HDMI_OUTP(0x0258, ~(1 << 28) & hpd_ctrl);
-		HDMI_OUTP(0x0258, (1 << 28) | hpd_ctrl);
+		/* Turn on HPD HW circuit */
+		HDMI_OUTP(0x0258, hpd_ctrl | BIT(28));
+
+		/* Set up HPD_CTRL to sense HPD event */
+		hdmi_msm_hpd_polarity_setup(HPD_CONNECT_POLARITY, true);
 	}
 
 	DEV_DBG("%s: (IRQ, 5V on)\n", __func__);
@@ -4417,35 +4325,54 @@
 	if (!hdmi_msm_state || !hdmi_msm_state->hdmi_app_clk || !MSM_HDMI_BASE)
 		return -ENODEV;
 
+	if (!hdmi_msm_state->hpd_initialized ||
+		!external_common_state->hpd_state) {
+		DEV_DBG("%s: HPD not initialized/cable not conn. Returning\n",
+				__func__);
+		return 0;
+	}
+
 	DEV_INFO("power: ON (%dx%d %d)\n", mfd->var_xres, mfd->var_yres,
 		mfd->var_pixclock);
 
+	/* Only start transmission with supported resolution */
 	changed = hdmi_common_get_video_format_from_drv_data(mfd);
-	hdmi_msm_audio_info_setup(TRUE, 0, 0, 0, FALSE);
+	if (changed || external_common_state->default_res_supported) {
+		hdmi_msm_audio_info_setup(TRUE, 0, 0, 0, FALSE);
+		mutex_lock(&external_common_state_hpd_mutex);
+		hdmi_msm_state->panel_power_on = TRUE;
+		if (external_common_state->hpd_state &&
+				hdmi_msm_is_power_on()) {
+			DEV_DBG("%s: Turning HDMI on\n", __func__);
+			mutex_unlock(&external_common_state_hpd_mutex);
+			hdmi_msm_turn_on();
 
-	mutex_lock(&external_common_state_hpd_mutex);
-	hdmi_msm_state->panel_power_on = TRUE;
-	if (external_common_state->hpd_state && hdmi_msm_is_power_on()) {
-		DEV_DBG("%s: Turning HDMI on\n", __func__);
-		mutex_unlock(&external_common_state_hpd_mutex);
-		hdmi_msm_turn_on();
-
-		if (hdmi_msm_state->hdcp_enable) {
-			/* Kick off HDCP Authentication */
-			mutex_lock(&hdcp_auth_state_mutex);
-			hdmi_msm_state->reauth = FALSE;
-			hdmi_msm_state->full_auth_done = FALSE;
-			mutex_unlock(&hdcp_auth_state_mutex);
-			mod_timer(&hdmi_msm_state->hdcp_timer, jiffies + HZ/2);
+			if (hdmi_msm_state->hdcp_enable) {
+				/* Kick off HDCP Authentication */
+				mutex_lock(&hdcp_auth_state_mutex);
+				hdmi_msm_state->reauth = FALSE;
+				hdmi_msm_state->full_auth_done = FALSE;
+				mutex_unlock(&hdcp_auth_state_mutex);
+				mod_timer(&hdmi_msm_state->hdcp_timer,
+						jiffies + HZ/2);
+			}
+		} else {
+			mutex_unlock(&external_common_state_hpd_mutex);
 		}
-	} else
-		mutex_unlock(&external_common_state_hpd_mutex);
 
-	hdmi_msm_dump_regs("HDMI-ON: ");
+		hdmi_msm_dump_regs("HDMI-ON: ");
+		DEV_INFO("power=%s DVI= %s\n",
+			hdmi_msm_is_power_on() ? "ON" : "OFF" ,
+			hdmi_msm_is_dvi_mode() ? "ON" : "OFF");
+	} else {
+		DEV_ERR("%s: Video fmt %d not supp. Returning\n",
+				__func__,
+				external_common_state->video_resolution);
+	}
 
-	DEV_INFO("power=%s DVI= %s\n",
-		hdmi_msm_is_power_on() ? "ON" : "OFF" ,
-		hdmi_msm_is_dvi_mode() ? "ON" : "OFF");
+	/* Enable HPD interrupt and listen to disconnect interrupts */
+	hdmi_msm_hpd_polarity_setup(HPD_DISCONNECT_POLARITY,
+			external_common_state->hpd_state);
 	return 0;
 }
 
@@ -4454,11 +4381,6 @@
 	char *envp[2];
 
 	/* Simulating a HPD event based on MHL event */
-	hdmi_msm_state->hpd_cable_chg_detected = FALSE;
-	/* QDSP OFF preceding the HPD event notification */
-	switch_set_state(&external_common_state->sdev, 0);
-	DEV_INFO("Hdmi state switched to %d: %s\n",
-		 external_common_state->sdev.state,  __func__);
 	if (on) {
 		hdmi_msm_read_edid();
 		hdmi_msm_state->reauth = FALSE ;
@@ -4476,8 +4398,8 @@
 			kobject_uevent_env(external_common_state->uevent_kobj,
 					   KOBJ_CHANGE, envp);
 			switch_set_state(&external_common_state->sdev, 1);
-			DEV_INFO("Hdmi state switched to %d: %s\n",
-				 external_common_state->sdev.state, __func__);
+			DEV_INFO("%s: hdmi state switched to %d\n",
+				 __func__, external_common_state->sdev.state);
 		} else {
 			hdmi_msm_hdcp_enable();
 		}
@@ -4486,8 +4408,8 @@
 		kobject_uevent(external_common_state->uevent_kobj,
 			       KOBJ_OFFLINE);
 		switch_set_state(&external_common_state->sdev, 0);
-		DEV_INFO("Hdmi state switched to %d: %s\n",
-			 external_common_state->sdev.state,  __func__);
+		DEV_INFO("%s: hdmi state switched to %d\n", __func__,
+				external_common_state->sdev.state);
 	}
 }
 EXPORT_SYMBOL(mhl_connect_api);
@@ -4503,21 +4425,48 @@
 	if (!hdmi_msm_state->hdmi_app_clk)
 		return -ENODEV;
 
-	mutex_lock(&hdmi_msm_state_mutex);
-	if (hdmi_msm_state->hdcp_activating) {
-		hdmi_msm_state->panel_power_on = FALSE;
-		mutex_unlock(&hdmi_msm_state_mutex);
-		DEV_INFO("HDCP: activating, returning\n");
+	if (!hdmi_msm_state->panel_power_on) {
+		DEV_DBG("%s: panel not on. returning\n", __func__);
 		return 0;
 	}
-	mutex_unlock(&hdmi_msm_state_mutex);
 
-	DEV_INFO("power: OFF (audio off, Reset Core)\n");
-	hdmi_msm_audio_off();
-	hdcp_deauthenticate();
+	if (hdmi_msm_state->hdcp_enable) {
+		if (hdmi_msm_state->hdcp_activating) {
+			/*
+			 * Let the HDCP work know that we got an HPD
+			 * disconnect so that it can stop the
+			 * reauthentication loop.
+			 */
+			mutex_lock(&hdcp_auth_state_mutex);
+			hdmi_msm_state->hpd_during_auth = TRUE;
+			mutex_unlock(&hdcp_auth_state_mutex);
+		}
+
+		/*
+		 * Cancel any pending reauth attempts.
+		 * If one is ongoing, wait for it to finish
+		 */
+		cancel_work_sync(&hdmi_msm_state->hdcp_reauth_work);
+		cancel_work_sync(&hdmi_msm_state->hdcp_work);
+		del_timer_sync(&hdmi_msm_state->hdcp_timer);
+
+		hdcp_deauthenticate();
+	}
+
+	SWITCH_SET_HDMI_AUDIO(0, 0);
+
+	if (!hdmi_msm_is_dvi_mode())
+		hdmi_msm_audio_off();
+
 	hdmi_msm_powerdown_phy();
 
 	hdmi_msm_state->panel_power_on = FALSE;
+	DEV_INFO("power: OFF (audio off)\n");
+
+	/* Enable HPD interrupt and listen to connect interrupts */
+	hdmi_msm_hpd_polarity_setup(HPD_CONNECT_POLARITY,
+				!external_common_state->hpd_state);
+
 	return 0;
 }
 
@@ -4653,13 +4602,6 @@
 	}
 	disable_irq(hdmi_msm_state->irq);
 
-	init_timer(&hdmi_msm_state->hpd_state_timer);
-	hdmi_msm_state->hpd_state_timer.function =
-		hdmi_msm_hpd_state_timer;
-	hdmi_msm_state->hpd_state_timer.data = (uint32)NULL;
-
-	hdmi_msm_state->hpd_state_timer.expires = 0xffffffffL;
-
 #ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_CEC_SUPPORT
 	init_timer(&hdmi_msm_state->cec_read_timer);
 	hdmi_msm_state->cec_read_timer.function =
@@ -4693,8 +4635,19 @@
 		external_common_state->sdev.name = "hdmi_as_primary";
 	else
 		external_common_state->sdev.name = "hdmi";
-	if (switch_dev_register(&external_common_state->sdev) < 0)
+	if (switch_dev_register(&external_common_state->sdev) < 0) {
 		DEV_ERR("Hdmi switch registration failed\n");
+		rc = -ENODEV;
+		goto error;
+	}
+
+	external_common_state->audio_sdev.name = "hdmi_audio";
+	if (switch_dev_register(&external_common_state->audio_sdev) < 0) {
+		DEV_ERR("Hdmi audio switch registration failed\n");
+		switch_dev_unregister(&external_common_state->sdev);
+		rc = -ENODEV;
+		goto error;
+	}
 
 	/* Set the default video resolution for MHL-enabled display */
 	if (hdmi_msm_state->is_mhl_enabled) {
@@ -4737,6 +4690,7 @@
 
 	/* Unregister hdmi node from switch driver */
 	switch_dev_unregister(&external_common_state->sdev);
+	switch_dev_unregister(&external_common_state->audio_sdev);
 
 	hdmi_msm_hpd_off();
 	free_irq(hdmi_msm_state->irq, NULL);
@@ -4776,9 +4730,14 @@
 	if (on) {
 		rc = hdmi_msm_hpd_on();
 	} else {
+		external_common_state->hpd_state = 0;
 		hdmi_msm_hpd_off();
+		SWITCH_SET_HDMI_AUDIO(0, 0);
+
 		/* Set HDMI switch node to 0 on HPD feature disable */
 		switch_set_state(&external_common_state->sdev, 0);
+		DEV_INFO("%s: hdmi state switched to %d\n", __func__,
+				external_common_state->sdev.state);
 	}
 
 	return rc;
diff --git a/drivers/video/msm/hdmi_msm.h b/drivers/video/msm/hdmi_msm.h
index 20bd492..17cefdd 100644
--- a/drivers/video/msm/hdmi_msm.h
+++ b/drivers/video/msm/hdmi_msm.h
@@ -53,15 +53,12 @@
 struct hdmi_msm_state_type {
 	boolean panel_power_on;
 	boolean hpd_initialized;
-	boolean hpd_state_in_isr;
 #ifdef CONFIG_SUSPEND
 	boolean pm_suspended;
 #endif
-	boolean hpd_cable_chg_detected;
 	boolean full_auth_done;
 	boolean hpd_during_auth;
 	struct work_struct hpd_state_work;
-	struct timer_list hpd_state_timer;
 	struct completion ddc_sw_done;
 
 	bool hdcp_enable;
diff --git a/drivers/video/msm/mdp.c b/drivers/video/msm/mdp.c
index 712d41b..a827d6a 100644
--- a/drivers/video/msm/mdp.c
+++ b/drivers/video/msm/mdp.c
@@ -1333,7 +1333,7 @@
 }
 #endif
 
-static ssize_t vsync_show_event(struct device *dev,
+ssize_t mdp_dma_show_event(struct device *dev,
 		struct device_attribute *attr, char *buf)
 {
 	ssize_t ret = 0;
@@ -1452,6 +1452,11 @@
 					      vsync_period);
 	if (diff_to_next > vsync_period)
 		return;
+	pr_debug("%s cur_time %d, pre_vsync %d, to_next %d\n",
+		 __func__,
+		 (int)ktime_to_ms(cur_time),
+		 (int)ktime_to_ms(pre_vsync),
+		 diff_to_next);
 	wakeup_time = ktime_add_ns(cur_time, diff_to_next * NSEC_PER_MSEC);
 	activate_event_timer(mfd->cpu_pm_hdl, wakeup_time);
 }
@@ -2239,15 +2244,6 @@
 }
 
 #endif
-static DEVICE_ATTR(vsync_event, S_IRUGO, vsync_show_event, NULL);
-static struct attribute *vsync_fs_attrs[] = {
-	&dev_attr_vsync_event.attr,
-	NULL,
-};
-static struct attribute_group vsync_fs_attr_group = {
-	.attrs = vsync_fs_attrs,
-};
-
 static int mdp_on(struct platform_device *pdev)
 {
 	int ret = 0;
@@ -2282,21 +2278,7 @@
 	if (mdp_rev == MDP_REV_303 && mfd->panel.type == MIPI_CMD_PANEL) {
 
 		vsync_cntrl.dev = mfd->fbi->dev;
-
-		if (!vsync_cntrl.sysfs_created) {
-			ret = sysfs_create_group(&vsync_cntrl.dev->kobj,
-				&vsync_fs_attr_group);
-			if (ret) {
-				pr_err("%s: sysfs creation failed, ret=%d\n",
-					__func__, ret);
-				return ret;
-			}
-
-			kobject_uevent(&vsync_cntrl.dev->kobj, KOBJ_ADD);
-			pr_debug("%s: kobject_uevent(KOBJ_ADD)\n", __func__);
-			vsync_cntrl.sysfs_created = 1;
-		}
-		atomic_set(&vsync_cntrl.suspend, 0);
+		atomic_set(&vsync_cntrl.suspend, 1);
 	}
 
 	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
@@ -2528,9 +2510,6 @@
 #if defined(CONFIG_FB_MSM_MIPI_DSI) && defined(CONFIG_FB_MSM_MDP40)
 	struct mipi_panel_info *mipi;
 #endif
-	static int contSplash_update_done;
-	char *cp;
-	unsigned int mdp_r = 0;
 
 	if ((pdev->id == 0) && (pdev->num_resources > 0)) {
 		mdp_init_pdev = pdev;
@@ -2552,14 +2531,6 @@
 		}
 
 		mdp_rev = mdp_pdata->mdp_rev;
-		if (mdp_rev == MDP_REV_42) {
-			mdp_r = inpdw(MDP_BASE + 0x0);
-			mdp_r = ((mdp_r & 0x30000) >> 16);
-			if (mdp_r == 3) {
-				mdp_rev = MDP_REV_43;
-				mdp_pdata->mdp_rev = MDP_REV_43;
-			}
-		}
 
 		mdp_iommu_split_domain = mdp_pdata->mdp_iommu_split_domain;
 
@@ -2612,57 +2583,7 @@
 	/* link to the latest pdev */
 	mfd->pdev = msm_fb_dev;
 	mfd->mdp_rev = mdp_rev;
-
-	if (mdp_pdata) {
-		if (mdp_pdata->cont_splash_enabled) {
-			mfd->cont_splash_done = 0;
-
-			if (!contSplash_update_done) {
-				uint32 bpp = 3;
-				/*read panel wxh and calculate splash screen
-				size*/
-				mdp_pdata->splash_screen_size =
-						inpdw(MDP_BASE + 0x90004);
-				mdp_pdata->splash_screen_size =
-				(((mdp_pdata->splash_screen_size >> 16) &
-					0x00000FFF) * (
-					mdp_pdata->splash_screen_size &
-					0x00000FFF)) * bpp;
-
-				mdp_pdata->splash_screen_addr =
-						inpdw(MDP_BASE + 0x90008);
-
-				mfd->copy_splash_buf = dma_alloc_coherent(NULL,
-					mdp_pdata->splash_screen_size,
-					(dma_addr_t *) &(mfd->copy_splash_phys),
-					GFP_KERNEL);
-
-				if (!mfd->copy_splash_buf) {
-					pr_err("DMA ALLOC FAILED for SPLASH\n");
-					return -ENOMEM;
-				}
-				cp = (char *)ioremap(
-						mdp_pdata->splash_screen_addr,
-						mdp_pdata->splash_screen_size);
-				if (!cp) {
-					pr_err("IOREMAP FAILED for SPLASH\n");
-					return -ENOMEM;
-				}
-				memcpy(mfd->copy_splash_buf, cp,
-					mdp_pdata->splash_screen_size);
-
-				MDP_OUTP(MDP_BASE + 0x90008,
-						mfd->copy_splash_phys);
-
-				if (mfd->panel.type == MIPI_VIDEO_PANEL ||
-				    mfd->panel.type == LCDC_PANEL)
-					mdp_pipe_ctrl(MDP_CMD_BLOCK,
-						MDP_BLOCK_POWER_ON, FALSE);
-				contSplash_update_done = 1;
-			}
-		} else
-			mfd->cont_splash_done = 1;
-	}
+	mfd->vsync_init = NULL;
 
 	mfd->ov0_wb_buf = MDP_ALLOC(sizeof(struct mdp_buf_type));
 	mfd->ov1_wb_buf = MDP_ALLOC(sizeof(struct mdp_buf_type));
@@ -2694,6 +2615,53 @@
 		rc = -ENOMEM;
 		goto mdp_probe_err;
 	}
+
+	if (mdp_pdata) {
+		if (mdp_pdata->cont_splash_enabled &&
+				 mfd->panel_info.pdest == DISPLAY_1) {
+			char *cp;
+			uint32 bpp = 3;
+			/*read panel wxh and calculate splash screen
+			  size*/
+			mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+
+			mdp_pdata->splash_screen_size =
+				inpdw(MDP_BASE + 0x90004);
+			mdp_pdata->splash_screen_size =
+				(((mdp_pdata->splash_screen_size >> 16) &
+				  0x00000FFF) * (
+					  mdp_pdata->splash_screen_size &
+					  0x00000FFF)) * bpp;
+
+			mdp_pdata->splash_screen_addr =
+				inpdw(MDP_BASE + 0x90008);
+
+			mfd->copy_splash_buf = dma_alloc_coherent(NULL,
+					mdp_pdata->splash_screen_size,
+					(dma_addr_t *) &(mfd->copy_splash_phys),
+					GFP_KERNEL);
+
+			if (!mfd->copy_splash_buf) {
+				pr_err("DMA ALLOC FAILED for SPLASH\n");
+				return -ENOMEM;
+			}
+			cp = (char *)ioremap(
+					mdp_pdata->splash_screen_addr,
+					mdp_pdata->splash_screen_size);
+			if (!cp) {
+				pr_err("IOREMAP FAILED for SPLASH\n");
+				return -ENOMEM;
+			}
+			memcpy(mfd->copy_splash_buf, cp,
+					mdp_pdata->splash_screen_size);
+
+			MDP_OUTP(MDP_BASE + 0x90008,
+					mfd->copy_splash_phys);
+		}
+
+		mfd->cont_splash_done = (1 - mdp_pdata->cont_splash_enabled);
+	}
+
 	/* data chain */
 	pdata = msm_fb_dev->dev.platform_data;
 	pdata->on = mdp_on;
@@ -2781,7 +2749,8 @@
 	case MIPI_VIDEO_PANEL:
 #ifndef CONFIG_FB_MSM_MDP303
 		mipi = &mfd->panel_info.mipi;
-		mdp4_dsi_vsync_init(0);
+		mfd->vsync_init = mdp4_dsi_vsync_init;
+		mfd->vsync_show = mdp4_dsi_video_show_event;
 		mfd->hw_refresh = TRUE;
 		mfd->dma_fnc = mdp4_dsi_video_overlay;
 		mfd->lut_update = mdp_lut_update_lcdc;
@@ -2805,6 +2774,7 @@
 		mfd->start_histogram = mdp_histogram_start;
 		mfd->stop_histogram = mdp_histogram_stop;
 		mfd->vsync_ctrl = mdp_dma_video_vsync_ctrl;
+		mfd->vsync_show = mdp_dma_video_show_event;
 		if (mfd->panel_info.pdest == DISPLAY_1)
 			mfd->dma = &dma2_data;
 		else {
@@ -2825,7 +2795,8 @@
 #ifndef CONFIG_FB_MSM_MDP303
 		mfd->dma_fnc = mdp4_dsi_cmd_overlay;
 		mipi = &mfd->panel_info.mipi;
-		mdp4_dsi_rdptr_init(0);
+		mfd->vsync_init = mdp4_dsi_rdptr_init;
+		mfd->vsync_show = mdp4_dsi_cmd_show_event;
 		if (mfd->panel_info.pdest == DISPLAY_1) {
 			if_no = PRIMARY_INTF_SEL;
 			mfd->dma = &dma2_data;
@@ -2838,20 +2809,13 @@
 		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;
 		mfd->stop_histogram = mdp_histogram_stop;
 		mfd->vsync_ctrl = mdp_dma_vsync_ctrl;
+		mfd->vsync_show = mdp_dma_show_event;
 		if (mfd->panel_info.pdest == DISPLAY_1)
 			mfd->dma = &dma2_data;
 		else {
@@ -2869,11 +2833,12 @@
 
 #ifdef CONFIG_FB_MSM_DTV
 	case DTV_PANEL:
-		mdp4_dtv_vsync_init(0);
+		mfd->vsync_init = mdp4_dtv_vsync_init;
+		mfd->vsync_show = mdp4_dtv_show_event;
 		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;
@@ -2908,7 +2873,8 @@
 #endif
 
 #ifdef CONFIG_FB_MSM_MDP40
-		mdp4_lcdc_vsync_init(0);
+		mfd->vsync_init = mdp4_lcdc_vsync_init;
+		mfd->vsync_show = mdp4_lcdc_show_event;
 		if (mfd->panel.type == HDMI_PANEL) {
 			mfd->dma = &dma_e_data;
 			mdp4_display_intf_sel(EXTERNAL_INTF_SEL, LCDC_RGB_INTF);
@@ -2919,6 +2885,7 @@
 #else
 		mfd->dma = &dma2_data;
 		mfd->vsync_ctrl = mdp_dma_lcdc_vsync_ctrl;
+		mfd->vsync_show = mdp_dma_lcdc_show_event;
 		spin_lock_irqsave(&mdp_spin_lock, flag);
 		mdp_intr_mask &= ~MDP_DMA_P_DONE;
 		outp32(MDP_INTR_ENABLE, mdp_intr_mask);
@@ -3001,7 +2968,7 @@
 	}
 
 	/* req bus bandwidth immediately */
-	if (!(mfd->cont_splash_done))
+	if (!(mfd->cont_splash_done) && (mfd->panel_info.pdest == DISPLAY_1))
 		mdp_bus_scale_update_request(5);
 
 #endif
@@ -3019,6 +2986,29 @@
 
 	pdev_list[pdev_list_cnt++] = pdev;
 	mdp4_extn_disp = 0;
+
+	if (mfd->vsync_init != NULL) {
+		mfd->vsync_init(0);
+
+		if (!mfd->vsync_sysfs_created) {
+			mfd->dev_attr.attr.name = "vsync_event";
+			mfd->dev_attr.attr.mode = S_IRUGO;
+			mfd->dev_attr.show = mfd->vsync_show;
+			sysfs_attr_init(&mfd->dev_attr.attr);
+
+			rc = sysfs_create_file(&mfd->fbi->dev->kobj,
+							&mfd->dev_attr.attr);
+			if (rc) {
+				pr_err("%s: sysfs creation failed, ret=%d\n",
+					__func__, rc);
+				return rc;
+			}
+
+			kobject_uevent(&mfd->fbi->dev->kobj, KOBJ_ADD);
+			pr_debug("%s: kobject_uevent(KOBJ_ADD)\n", __func__);
+			mfd->vsync_sysfs_created = 1;
+		}
+	}
 	return 0;
 
       mdp_probe_err:
@@ -3128,6 +3118,7 @@
 {
 	mdp_suspend_sub();
 #ifdef CONFIG_FB_MSM_DTV
+	mdp4_solidfill_commit(MDP4_MIXER1);
 	mdp4_dtv_set_black_screen();
 #endif
 	mdp_footswitch_ctrl(FALSE);
diff --git a/drivers/video/msm/mdp.h b/drivers/video/msm/mdp.h
index b4a7f79..0bc2532 100644
--- a/drivers/video/msm/mdp.h
+++ b/drivers/video/msm/mdp.h
@@ -862,6 +862,12 @@
 void mdp_dma_vsync_ctrl(int enable);
 void mdp_dma_video_vsync_ctrl(int enable);
 void mdp_dma_lcdc_vsync_ctrl(int enable);
+ssize_t mdp_dma_show_event(struct device *dev,
+		struct device_attribute *attr, char *buf);
+ssize_t mdp_dma_video_show_event(struct device *dev,
+		struct device_attribute *attr, char *buf);
+ssize_t mdp_dma_lcdc_show_event(struct device *dev,
+		struct device_attribute *attr, char *buf);
 
 #ifdef MDP_HW_VSYNC
 void vsync_clk_prepare_enable(void);
diff --git a/drivers/video/msm/mdp4.h b/drivers/video/msm/mdp4.h
index 3ea196a..67ef8bf 100644
--- a/drivers/video/msm/mdp4.h
+++ b/drivers/video/msm/mdp4.h
@@ -28,6 +28,8 @@
 extern char *mmss_cc_base;	/* mutimedia sub system clock control */
 extern spinlock_t dsi_clk_lock;
 extern u32 mdp_max_clk;
+extern u32 dbg_force_ov0_blt;
+extern u32 dbg_force_ov1_blt;
 
 #define MDP4_OVERLAYPROC0_BASE	0x10000
 #define MDP4_OVERLAYPROC1_BASE	0x18000
@@ -94,11 +96,6 @@
 #define MDP4_PANEL_WRITEBACK		BIT(6)
 
 enum {
-	OVERLAY_MODE_NONE,
-	OVERLAY_MODE_BLT
-};
-
-enum {
 	OVERLAY_REFRESH_ON_DEMAND,
 	OVERLAY_REFRESH_VSYNC,
 	OVERLAY_REFRESH_VSYNC_HALF,
@@ -449,7 +446,6 @@
 void mdp4_intr_clear_set(ulong clear, ulong set);
 void mdp4_dma_p_cfg(void);
 unsigned is_mdp4_hw_reset(void);
-void mdp4_overlay_cfg_init(void);
 void mdp4_hw_init(void);
 void mdp4_isr_read(int);
 void mdp4_clear_lcdc(void);
@@ -557,6 +553,14 @@
 void mdp4_dsi_vsync_init(int cndx);
 void mdp4_lcdc_vsync_init(int cndx);
 void mdp4_dtv_vsync_init(int cndx);
+ssize_t mdp4_dsi_cmd_show_event(struct device *dev,
+	struct device_attribute *attr, char *buf);
+ssize_t mdp4_dsi_video_show_event(struct device *dev,
+	struct device_attribute *attr, char *buf);
+ssize_t mdp4_lcdc_show_event(struct device *dev,
+	struct device_attribute *attr, char *buf);
+ssize_t mdp4_dtv_show_event(struct device *dev,
+	struct device_attribute *attr, char *buf);
 void mdp4_overlay_dsi_state_set(int state);
 int mdp4_overlay_dsi_state_get(void);
 void mdp4_overlay_rgb_setup(struct mdp4_overlay_pipe *pipe);
@@ -581,10 +585,11 @@
 int mdp4_overlay_play_wait(struct fb_info *info,
 	struct msmfb_overlay_data *req);
 int mdp4_overlay_play(struct fb_info *info, struct msmfb_overlay_data *req);
-int mdp4_overlay_commit(struct fb_info *info, int mixer);
+int mdp4_overlay_commit(struct fb_info *info);
 struct mdp4_overlay_pipe *mdp4_overlay_pipe_alloc(int ptype, int mixer);
 void mdp4_overlay_dma_commit(int mixer);
 void mdp4_overlay_vsync_commit(struct mdp4_overlay_pipe *pipe);
+void mdp4_solidfill_commit(int mixer);
 void mdp4_mixer_stage_commit(int mixer);
 void mdp4_dsi_cmd_do_update(int cndx, struct mdp4_overlay_pipe *pipe);
 void mdp4_lcdc_pipe_queue(int cndx, struct mdp4_overlay_pipe *pipe);
@@ -980,9 +985,15 @@
 {
 	/* empty */
 }
+static inline int mdp4_wfd_pipe_commit(struct msm_fb_data_type *mfd,
+					int cndx, int wait)
+{
+	return 0;
+}
 #else
 void mdp4_wfd_pipe_queue(int cndx, struct mdp4_overlay_pipe *pipe);
 void mdp4_wfd_init(int cndx);
+int mdp4_wfd_pipe_commit(struct msm_fb_data_type *mfd, int cndx, int wait);
 #endif
 
 #endif /* MDP_H */
diff --git a/drivers/video/msm/mdp4_dtv.c b/drivers/video/msm/mdp4_dtv.c
index bd0ce2f..4b83224 100644
--- a/drivers/video/msm/mdp4_dtv.c
+++ b/drivers/video/msm/mdp4_dtv.c
@@ -38,6 +38,7 @@
 
 static int dtv_off(struct platform_device *pdev);
 static int dtv_on(struct platform_device *pdev);
+static int dtv_off_sub(void);
 
 static struct platform_device *pdev_list[MSM_FB_MAX_DEV_LIST];
 static int pdev_list_cnt;
@@ -45,6 +46,9 @@
 static struct clk *tv_src_clk;
 static struct clk *hdmi_clk;
 static struct clk *mdp_tv_clk;
+static struct platform_device *dtv_pdev;
+static struct workqueue_struct *dtv_work_queue;
+static struct work_struct dtv_off_work;
 
 
 static int mdp4_dtv_runtime_suspend(struct device *dev)
@@ -86,8 +90,48 @@
 static int dtv_off(struct platform_device *pdev)
 {
 	int ret = 0;
+	struct msm_fb_data_type *mfd = NULL;
 
-	ret = panel_next_off(pdev);
+	if (!pdev) {
+		pr_err("%s: FAILED: invalid arg\n", __func__);
+		return -EINVAL;
+	}
+
+	mfd = platform_get_drvdata(pdev);
+	if (!mfd) {
+		pr_err("%s: FAILED: invalid mfd\n", __func__);
+		return -EINVAL;
+	}
+
+	dtv_pdev = pdev;
+	/*
+	 * If it's a suspend operation then handle the device
+	 * power down synchronously.
+	 * Otherwise, queue work item to handle power down sequence.
+	 * This is needed since we need to wait for the audio engine
+	 * to shutdown first before we turn off the DTV device.
+	 */
+	if (!mfd->suspend.op_suspend) {
+		pr_debug("%s: Queuing work to turn off HDMI core\n", __func__);
+		queue_work(dtv_work_queue, &dtv_off_work);
+	} else {
+		pr_debug("%s: turning off HDMI core\n", __func__);
+		ret = dtv_off_sub();
+	}
+
+	return ret;
+}
+
+static int dtv_off_sub(void)
+{
+	int ret = 0;
+
+	if (!dtv_pdev) {
+		pr_err("%s: FAILED: invalid arg\n", __func__);
+		return -EINVAL;
+	}
+
+	ret = panel_next_off(dtv_pdev);
 
 	pr_info("%s\n", __func__);
 
@@ -112,12 +156,20 @@
 	return ret;
 }
 
+static void dtv_off_work_func(struct work_struct *work)
+{
+	dtv_off_sub();
+}
+
 static int dtv_on(struct platform_device *pdev)
 {
 	int ret = 0;
 	struct msm_fb_data_type *mfd;
 	unsigned long panel_pixclock_freq , pm_qos_rate;
 
+	/* If a power down is already underway, wait for it to finish */
+	flush_work_sync(&dtv_off_work);
+
 	mfd = platform_get_drvdata(pdev);
 	panel_pixclock_freq = mfd->fbi->var.pixclock;
 
@@ -215,6 +267,8 @@
 		return 0;
 	}
 
+	dtv_work_queue = create_singlethread_workqueue("dtv_work");
+	INIT_WORK(&dtv_off_work, dtv_off_work_func);
 	mfd = platform_get_drvdata(pdev);
 
 	if (!mfd)
@@ -302,6 +356,8 @@
 
 static int dtv_remove(struct platform_device *pdev)
 {
+	if (dtv_work_queue)
+		destroy_workqueue(dtv_work_queue);
 #ifdef CONFIG_MSM_BUS_SCALING
 	if (dtv_pdata && dtv_pdata->bus_scale_table &&
 		dtv_bus_scale_handle > 0)
diff --git a/drivers/video/msm/mdp4_overlay.c b/drivers/video/msm/mdp4_overlay.c
index 7c87c44..f4332dd 100644
--- a/drivers/video/msm/mdp4_overlay.c
+++ b/drivers/video/msm/mdp4_overlay.c
@@ -48,11 +48,11 @@
 	struct mdp4_overlay_pipe *stage[MDP4_MIXER_MAX][MDP4_MIXER_STAGE_MAX];
 	struct mdp4_overlay_pipe *baselayer[MDP4_MIXER_MAX];
 	struct blend_cfg blend[MDP4_MIXER_MAX][MDP4_MIXER_STAGE_MAX];
+	struct mdp4_overlay_pipe sf_plist[MDP4_MIXER_MAX][OVERLAY_PIPE_MAX];
 	uint32 mixer_cfg[MDP4_MIXER_MAX];
 	uint32 flush[MDP4_MIXER_MAX];
 	struct iommu_free_list iommu_free[MDP4_MIXER_MAX];
 	uint32 cs_controller;
-	uint32 hw_version;
 	uint32 panel_3d;
 	uint32 panel_mode;
 	uint32 mixer0_played;
@@ -121,6 +121,7 @@
 
 static struct ion_client *display_iclient;
 
+static void mdp4_overlay_bg_solidfill(struct blend_cfg *blend);
 
 /*
  * mdp4_overlay_iommu_unmap_freelist()
@@ -192,12 +193,19 @@
 		return;
 
 	if (pipe->flags & MDP_MEMORY_ID_TYPE_FB) {
-		if (pipe->put0_need)
+		pipe->flags &= ~MDP_MEMORY_ID_TYPE_FB;
+		if (pipe->put0_need) {
 			fput_light(pipe->srcp0_file, pipe->put0_need);
-		if (pipe->put1_need)
+			pipe->put0_need = 0;
+		}
+		if (pipe->put1_need) {
 			fput_light(pipe->srcp1_file, pipe->put1_need);
-		if (pipe->put2_need)
+			pipe->put1_need = 0;
+		}
+		if (pipe->put2_need) {
 			fput_light(pipe->srcp2_file, pipe->put2_need);
+			pipe->put2_need = 0;
+		}
 
 		pr_debug("%s: ndx=%d flags=%x put=%d\n", __func__,
 			pipe->pipe_ndx, pipe->flags, pipe->put0_need);
@@ -244,7 +252,7 @@
 		pipe->pipe_ndx, plane);
 	if (ion_map_iommu(display_iclient, *srcp_ihdl,
 		DISPLAY_READ_DOMAIN, GEN_POOL, SZ_4K, 0, start,
-		len, 0, ION_IOMMU_UNMAP_DELAYED)) {
+		len, 0, 0)) {
 		ion_free(display_iclient, *srcp_ihdl);
 		pr_err("ion_map_iommu() failed\n");
 		return -EINVAL;
@@ -345,23 +353,9 @@
 	return ctrl->panel_mode;
 }
 
-void mdp4_overlay_cfg_init(void)
-{
-	if (ctrl->hw_version == 0) {
-		mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
-		ctrl->hw_version = inpdw(MDP_BASE + 0x0); /* MDP_HW_VERSION */
-		mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
-	}
-
-	if (ctrl->hw_version >= 0x0402030b) {
-		/* MDP_LAYERMIXER_IN_CFG_UPDATE_METHOD */
-		outpdw(MDP_BASE + 0x100fc, 0x01);
-	}
-}
-
 int mdp4_overlay_borderfill_supported(void)
 {
-	return (ctrl->hw_version >= 0x0402030b);
+	return (mdp_rev >= MDP_REV_42);
 }
 
 void mdp4_overlay_dmae_cfg(struct msm_fb_data_type *mfd, int atv)
@@ -409,53 +403,34 @@
 	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
 }
 
-#ifdef CONFIG_FB_MSM_HDMI_3D
-void unfill_black_screen(void) { return; }
-#else
-void unfill_black_screen(void)
+void fill_black_screen(bool on, uint8 pipe_num, uint8 mixer_num)
 {
-	uint32 temp_src_format;
-	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
-	/*
-	* VG2 Constant Color
-	*/
-	temp_src_format = inpdw(MDP_BASE + 0x30050);
-	MDP_OUTP(MDP_BASE + 0x30050, temp_src_format&(~BIT(22)));
-	/*
-	* MDP_OVERLAY_REG_FLUSH
-	*/
-	MDP_OUTP(MDP_BASE + 0x18000, BIT(3));
-	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
-	return;
-}
-#endif
+	uint32 reg_base        = 0x010000;
+	uint32 const_color_reg = reg_base * (pipe_num + 2) + 0x1008;
+	uint32 src_fmt_reg     = reg_base * (pipe_num + 2) + 0x50;
+	uint32 color           = 0x00000000;
+	uint32 temp_src_format = 0x00000000;
+	uint8  bit             = pipe_num + 2;
 
-#ifdef CONFIG_FB_MSM_HDMI_3D
-void fill_black_screen(void) { return; }
-#else
-void fill_black_screen(void)
-{
-	/*Black color*/
-	uint32 color = 0x00000000;
-	uint32 temp_src_format;
 	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
-	/*
-	* VG2 Constant Color
-	*/
-	MDP_OUTP(MDP_BASE + 0x31008, color);
-	/*
-	* MDP_VG2_SRC_FORMAT
-	*/
-	temp_src_format = inpdw(MDP_BASE + 0x30050);
-	MDP_OUTP(MDP_BASE + 0x30050, temp_src_format | BIT(22));
-	/*
-	* MDP_OVERLAY_REG_FLUSH
-	*/
-	MDP_OUTP(MDP_BASE + 0x18000, BIT(3));
+
+	/* Fill constant color */
+	MDP_OUTP(MDP_BASE + const_color_reg, color);
+
+	/* Update source format for pipe */
+	temp_src_format = inpdw(MDP_BASE + src_fmt_reg);
+
+	if (on)
+		MDP_OUTP(MDP_BASE + src_fmt_reg, temp_src_format | BIT(22));
+	else
+		MDP_OUTP(MDP_BASE + src_fmt_reg, temp_src_format | (~BIT(22)));
+
+	/* MDP_OVERLAY_REG_FLUSH for pipe*/
+	MDP_OUTP(MDP_BASE + 0x18000, BIT(bit) | BIT(mixer_num));
 	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+
 	return;
 }
-#endif
 
 void mdp4_overlay_dmae_xy(struct mdp4_overlay_pipe *pipe)
 {
@@ -730,8 +705,14 @@
 		op_mode &= ~(MDP4_OP_FLIP_LR + MDP4_OP_SCALEX_EN);
 		op_mode &= ~(MDP4_OP_FLIP_UD + MDP4_OP_SCALEY_EN);
 		outpdw(rgb_base + 0x0058, op_mode);/* MDP_RGB_OP_MODE */
-	} else
+	} else {
+		if (pipe->op_mode & MDP4_OP_FLIP_LR && mdp_rev >= MDP_REV_42) {
+			/* Enable x-scaling bit to enable LR flip */
+			/* for MDP > 4.2 targets */
+			pipe->op_mode |= 0x01;
+		}
 		outpdw(rgb_base + 0x0058, pipe->op_mode);/* MDP_RGB_OP_MODE */
+	}
 	outpdw(rgb_base + 0x005c, pipe->phasex_step);
 	outpdw(rgb_base + 0x0060, pipe->phasey_step);
 
@@ -784,6 +765,7 @@
 		case MDP_Y_CRCB_H2V1:
 		case MDP_Y_CBCR_H2V1:
 		case MDP_Y_CRCB_H1V2:
+		case MDP_Y_CBCR_H1V2:
 			*luma_off = pipe->src_x +
 				(pipe->src_y * pipe->srcp0_ystride);
 			*chroma_off = pipe->src_x +
@@ -993,6 +975,7 @@
 	case MDP_Y_CRCB_H2V1:
 	case MDP_Y_CBCR_H2V1:
 	case MDP_Y_CRCB_H1V2:
+	case MDP_Y_CBCR_H1V2:
 	case MDP_Y_CRCB_H2V2:
 	case MDP_Y_CBCR_H2V2:
 	case MDP_Y_CBCR_H2V2_TILE:
@@ -1177,6 +1160,7 @@
 	case MDP_Y_CRCB_H2V1:
 	case MDP_Y_CBCR_H2V1:
 	case MDP_Y_CRCB_H1V2:
+	case MDP_Y_CBCR_H1V2:
 	case MDP_Y_CRCB_H2V2:
 	case MDP_Y_CBCR_H2V2:
 	case MDP_Y_CRCB_H1V1:
@@ -1217,6 +1201,10 @@
 			pipe->element1 = C1_B_Cb;
 			pipe->element0 = C2_R_Cr;
 			pipe->chroma_sample = MDP4_CHROMA_H1V2;
+		} else if (pipe->src_format == MDP_Y_CBCR_H1V2) {
+			pipe->element1 = C2_R_Cr;
+			pipe->element0 = C1_B_Cb;
+			pipe->chroma_sample = MDP4_CHROMA_H1V2;
 		} else if (pipe->src_format == MDP_Y_CRCB_H2V2) {
 			pipe->element1 = C1_B_Cb;
 			pipe->element0 = C2_R_Cr;
@@ -1378,6 +1366,7 @@
 	case MDP_Y_CRCB_H2V2:
 	case MDP_Y_CRCB_H2V1:
 	case MDP_Y_CRCB_H1V2:
+	case MDP_Y_CBCR_H1V2:
 	case MDP_Y_CRCB_H1V1:
 	case MDP_Y_CBCR_H1V1:
 	case MDP_YCRCB_H1V1:
@@ -1610,6 +1599,35 @@
 	return cnt;
 }
 
+void mdp4_solidfill_commit(int mixer)
+{
+	struct blend_cfg bcfg;
+	struct mdp4_overlay_pipe *pp = NULL;
+	int i = 0;
+
+	for (i = 0; i < OVERLAY_PIPE_MAX; i++) {
+		pp = &ctrl->sf_plist[mixer][i];
+		if (pp->pipe_ndx && pp->solid_fill) {
+			bcfg.solidfill = 1;
+			bcfg.solidfill_pipe = pp;
+			mdp4_overlay_bg_solidfill(&bcfg);
+			mdp4_overlay_reg_flush(pp, 1);
+			mdp4_mixer_stage_up(pp, 0);
+		}
+	}
+	mdp4_mixer_stage_commit(MDP4_MIXER1);
+
+	for (i = 0; i < OVERLAY_PIPE_MAX; i++) {
+		pp = &ctrl->sf_plist[mixer][i];
+		if (pp->pipe_ndx && pp->solid_fill) {
+			mdp4_overlay_reg_flush(pp, 1);
+			mdp4_mixer_stage_down(pp, 0);
+			pp->solid_fill = 0;
+		}
+	}
+	mdp4_mixer_stage_commit(MDP4_MIXER1);
+}
+
 void mdp4_mixer_stage_commit(int mixer)
 {
 	struct mdp4_overlay_pipe *pipe;
@@ -1632,8 +1650,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;
@@ -1701,7 +1725,8 @@
 			ctrl->stage[mixer][i] = NULL;  /* clear it */
 	}
 
-	if (commit || (mixer > 0 && !hdmi_prim_display))
+	if (commit || ((mixer == 1) && !hdmi_prim_display) ||
+		(mixer == 2))
 		mdp4_mixer_stage_commit(mixer);
 }
 /*
@@ -1771,6 +1796,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;
@@ -1785,6 +1811,7 @@
 		return;
 	}
 
+	iom = bspipe->iommu;
 	ptype = bspipe->pipe_type;
 	pnum = bspipe->pipe_num;
 	pndx = bspipe->pipe_ndx;
@@ -1798,6 +1825,7 @@
 	bspipe->src_format = format;
 	bspipe->alpha_enable = alpha_enable;
 	bspipe->alpha = alpha;
+	bspipe->iommu = iom;
 
 	bspipe->pipe_used++;	/* mark base layer pipe used */
 
@@ -1942,7 +1970,7 @@
 	struct mdp4_overlay_pipe *d_pipe;
 	struct mdp4_overlay_pipe *s_pipe;
 	struct blend_cfg *blend;
-	int i, off, alpha_drop = 0;
+	int i, off, alpha_drop;
 	int d_alpha, s_alpha;
 	unsigned char *overlay_base;
 	uint32 c0, c1, c2, base_premulti;
@@ -1968,8 +1996,10 @@
 			d_alpha = 0;
 			continue;
 		}
+		alpha_drop = 0;	/* per stage */
 		/* alpha channel is lost on VG pipe when using QSEED or M/N */
 		if (s_pipe->pipe_type == OVERLAY_TYPE_VIDEO &&
+			s_pipe->alpha_enable &&
 			((s_pipe->op_mode & MDP4_OP_SCALEY_EN) ||
 			(s_pipe->op_mode & MDP4_OP_SCALEX_EN)) &&
 			!(s_pipe->op_mode & (MDP4_OP_SCALEX_PIXEL_RPT |
@@ -2082,7 +2112,9 @@
 		outpdw(overlay_base + off + 0x108, blend->fg_alpha);
 		outpdw(overlay_base + off + 0x10c, blend->bg_alpha);
 
-		if (mdp_rev >= MDP_REV_42)
+		if (mdp_rev >= MDP_REV_42 ||
+			ctrl->panel_mode & MDP4_PANEL_MDDI ||
+			 ctrl->panel_mode & MDP4_PANEL_DSI_CMD)
 			outpdw(overlay_base + off + 0x104, blend->op);
 
 		outpdw(overlay_base + (off << 5) + 0x1004, blend->co3_sel);
@@ -2848,8 +2880,9 @@
 				perf_req->mdp_bw);
 			perf_cur->mdp_bw = perf_req->mdp_bw;
 		}
-		if (mfd->panel_info.pdest == DISPLAY_1 &&
-		    perf_req->use_ov0_blt && !perf_cur->use_ov0_blt) {
+		if ((mfd->panel_info.pdest == DISPLAY_1 &&
+		     perf_req->use_ov0_blt && !perf_cur->use_ov0_blt) ||
+		    dbg_force_ov0_blt) {
 			if (mfd->panel_info.type == LCDC_PANEL ||
 			    mfd->panel_info.type == LVDS_PANEL)
 				mdp4_lcdc_overlay_blt_start(mfd);
@@ -2859,17 +2892,18 @@
 				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",
+			pr_debug("%s mixer0 start blt [%d] from %d to %d.\n",
 				__func__,
 				flag,
 				perf_cur->use_ov0_blt,
 				perf_req->use_ov0_blt);
 			perf_cur->use_ov0_blt = perf_req->use_ov0_blt;
 		}
-		if (mfd->panel_info.pdest == DISPLAY_2 &&
-		    perf_req->use_ov1_blt && !perf_cur->use_ov1_blt) {
+		if ((mfd->panel_info.pdest == DISPLAY_2 &&
+		     perf_req->use_ov1_blt && !perf_cur->use_ov1_blt) ||
+		    dbg_force_ov1_blt) {
 			mdp4_dtv_overlay_blt_start(mfd);
-			pr_info("%s mixer1 start blt [%d] from %d to %d.\n",
+			pr_debug("%s mixer1 start blt [%d] from %d to %d.\n",
 				__func__,
 				flag,
 				perf_cur->use_ov1_blt,
@@ -2898,8 +2932,9 @@
 				 perf_req->mdp_bw);
 			perf_cur->mdp_bw = perf_req->mdp_bw;
 		}
-		if (mfd->panel_info.pdest == DISPLAY_1 &&
-		    !perf_req->use_ov0_blt && perf_cur->use_ov0_blt) {
+		if ((mfd->panel_info.pdest == DISPLAY_1 &&
+		     !perf_req->use_ov0_blt && perf_cur->use_ov0_blt) ||
+		    dbg_force_ov0_blt) {
 			if (mfd->panel_info.type == LCDC_PANEL ||
 			    mfd->panel_info.type == LVDS_PANEL)
 				mdp4_lcdc_overlay_blt_stop(mfd);
@@ -2909,17 +2944,18 @@
 				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",
+			pr_debug("%s mixer0 stop blt [%d] from %d to %d.\n",
 				__func__,
 				flag,
 				perf_cur->use_ov0_blt,
 				perf_req->use_ov0_blt);
 			perf_cur->use_ov0_blt = perf_req->use_ov0_blt;
 		}
-		if (mfd->panel_info.pdest == DISPLAY_2 &&
-		    !perf_req->use_ov1_blt && perf_cur->use_ov1_blt) {
+		if ((mfd->panel_info.pdest == DISPLAY_2 &&
+		     !perf_req->use_ov1_blt && perf_cur->use_ov1_blt) ||
+		    dbg_force_ov1_blt) {
 			mdp4_dtv_overlay_blt_stop(mfd);
-			pr_info("%s mixer1 stop blt [%d] from %d to %d.\n",
+			pr_debug("%s mixer1 stop blt [%d] from %d to %d.\n",
 				__func__,
 				flag,
 				perf_cur->use_ov1_blt,
@@ -3182,8 +3218,16 @@
 	if (pipe->mixer_num == MDP4_MIXER0) {
 
 	} else {	/* mixer1, DTV, ATV */
-		if (ctrl->panel_mode & MDP4_PANEL_DTV)
+		if (ctrl->panel_mode & MDP4_PANEL_DTV) {
+			if (hdmi_prim_display) {
+				struct mdp4_overlay_pipe *pp;
+				pp = &ctrl->sf_plist[pipe->mixer_num]
+					[pipe->pipe_ndx - 1];
+				*pp = *pipe; /* clone it */
+				pp->solid_fill = 1;
+			}
 			mdp4_overlay_dtv_unset(mfd, pipe);
+		}
 	}
 
 	mdp4_stat.overlay_unset[pipe->mixer_num]++;
@@ -3386,7 +3430,8 @@
 		pipe->srcp0_ystride = pipe->src_width;
 		if ((pipe->src_format == MDP_Y_CRCB_H1V1) ||
 			(pipe->src_format == MDP_Y_CBCR_H1V1) ||
-			(pipe->src_format == MDP_Y_CRCB_H1V2)) {
+			(pipe->src_format == MDP_Y_CRCB_H1V2) ||
+			(pipe->src_format == MDP_Y_CBCR_H1V2)) {
 			if (pipe->src_width > YUV_444_MAX_WIDTH)
 				pipe->srcp1_ystride = pipe->src_width << 2;
 			else
@@ -3486,8 +3531,9 @@
 	return ret;
 }
 
-int mdp4_overlay_commit(struct fb_info *info, int mixer)
+int mdp4_overlay_commit(struct fb_info *info)
 {
+	int ret = 0;
 	struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
 
 	if (mfd == NULL)
@@ -3496,34 +3542,37 @@
 	if (!mfd->panel_power_on) /* suspended */
 		return -EINVAL;
 
-	if (mixer >= MDP4_MIXER_MAX)
-		return -EPERM;
-
 	mutex_lock(&mfd->dma->ov_mutex);
 
 	mdp4_overlay_mdp_perf_upd(mfd, 1);
 
-	if (mixer == MDP4_MIXER0) {
-		if (ctrl->panel_mode & MDP4_PANEL_DSI_CMD) {
-			/* cndx = 0 */
-			mdp4_dsi_cmd_pipe_commit(0, 1);
-		} else if (ctrl->panel_mode & MDP4_PANEL_DSI_VIDEO) {
-			/* cndx = 0 */
-			mdp4_dsi_video_pipe_commit(0, 1);
-		} else if (ctrl->panel_mode & MDP4_PANEL_LCDC) {
-			/* cndx = 0 */
-			mdp4_lcdc_pipe_commit(0, 1);
-		}
-	} else if (mixer == MDP4_MIXER1) {
-		if (ctrl->panel_mode & MDP4_PANEL_DTV)
-			mdp4_dtv_pipe_commit(0, 1);
+	switch (mfd->panel.type) {
+	case MIPI_CMD_PANEL:
+		mdp4_dsi_cmd_pipe_commit(0, 1);
+		break;
+	case MIPI_VIDEO_PANEL:
+		mdp4_dsi_video_pipe_commit(0, 1);
+		break;
+	case LCDC_PANEL:
+		mdp4_lcdc_pipe_commit(0, 1);
+		break;
+	case DTV_PANEL:
+		mdp4_dtv_pipe_commit(0, 1);
+		break;
+	case WRITEBACK_PANEL:
+		mdp4_wfd_pipe_commit(mfd, 0, 1);
+		break;
+	default:
+		pr_err("Panel Not Supported for Commit");
+		ret = -EINVAL;
+		break;
 	}
 
 	mdp4_overlay_mdp_perf_upd(mfd, 0);
 
 	mutex_unlock(&mfd->dma->ov_mutex);
 
-	return 0;
+	return ret;
 }
 
 struct msm_iommu_ctx {
diff --git a/drivers/video/msm/mdp4_overlay_dsi_cmd.c b/drivers/video/msm/mdp4_overlay_dsi_cmd.c
index c5442a7..9cb2b34 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;
@@ -70,8 +70,8 @@
 	struct vsync_update vlist[2];
 	int vsync_enabled;
 	int clk_enabled;
-	int clk_control;
 	int new_update;
+	int clk_control;
 	ktime_t vsync_time;
 	struct work_struct clk_work;
 } vsync_ctrl_db[MAX_CONTROLLER];
@@ -229,8 +229,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;
@@ -271,6 +273,8 @@
 	pipe = vctrl->base_pipe;
 	mixer = pipe->mixer_num;
 
+	mdp_update_pm(vctrl->mfd, vctrl->vsync_time);
+
 	if (vp->update_cnt == 0) {
 		mutex_unlock(&vctrl->update_lock);
 		return cnt;
@@ -363,12 +367,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 +398,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,33 +415,28 @@
 	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);
 			mdp_clk_ctrl(1);
 			vctrl->clk_enabled = 1;
+			vctrl->new_update = 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 +514,33 @@
 	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++;
 
 	spin_lock(&vctrl->spin_lock);
 	vctrl->vsync_time = ktime_get();
 
+	if (vctrl->new_update) {
+		vctrl->new_update = 0;
+		spin_unlock(&vctrl->spin_lock);
+		return;
+	}
+
 	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 +560,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 +604,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,25 +640,29 @@
 
 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);
 }
 
-static ssize_t vsync_show_event(struct device *dev,
+ssize_t mdp4_dsi_cmd_show_event(struct device *dev,
 		struct device_attribute *attr, char *buf)
 {
 	int cndx;
@@ -652,8 +674,7 @@
 	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);
@@ -662,15 +683,20 @@
 	vctrl->wait_vsync_cnt++;
 	spin_unlock_irqrestore(&vctrl->spin_lock, flags);
 
-	ret = wait_for_completion_interruptible(&vctrl->vsync_comp);
-	if (ret)
-		return ret;
+	ret = wait_for_completion_interruptible_timeout(&vctrl->vsync_comp,
+		msecs_to_jiffies(VSYNC_PERIOD * 4));
+	if (ret <= 0) {
+		vctrl->wait_vsync_cnt = 0;
+		return -EBUSY;
+	}
 
 	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;
 }
@@ -695,7 +721,7 @@
 	init_completion(&vctrl->dmap_comp);
 	init_completion(&vctrl->vsync_comp);
 	spin_lock_init(&vctrl->spin_lock);
-	atomic_set(&vctrl->vsync_resume, 1);
+	atomic_set(&vctrl->suspend, 1);
 	INIT_WORK(&vctrl->clk_work, clk_ctrl_work);
 }
 
@@ -704,20 +730,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)
 {
 	/*
@@ -942,6 +954,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);
 
@@ -957,6 +970,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);
 }
 
@@ -976,14 +990,6 @@
 	mdp4_dsi_cmd_do_blt(mfd, req->enable);
 }
 
-static DEVICE_ATTR(vsync_event, S_IRUGO, vsync_show_event, NULL);
-static struct attribute *vsync_fs_attrs[] = {
-	&dev_attr_vsync_event.attr,
-	NULL,
-};
-static struct attribute_group vsync_fs_attr_group = {
-	.attrs = vsync_fs_attrs,
-};
 int mdp4_dsi_cmd_on(struct platform_device *pdev)
 {
 	int ret = 0;
@@ -991,9 +997,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;
@@ -1006,22 +1013,8 @@
 	mdp4_iommu_attach();
 
 	atomic_set(&vctrl->suspend, 0);
+
 	pr_debug("%s-:\n", __func__);
-
-	if (!vctrl->sysfs_created) {
-		ret = sysfs_create_group(&vctrl->dev->kobj,
-			&vsync_fs_attr_group);
-		if (ret) {
-			pr_err("%s: sysfs group creation failed, ret=%d\n",
-				__func__, ret);
-			return ret;
-		}
-
-		kobject_uevent(&vctrl->dev->kobj, KOBJ_ADD);
-		pr_debug("%s: kobject_uevent(KOBJ_ADD)\n", __func__);
-		vctrl->sysfs_created = 1;
-	}
-
 	return ret;
 }
 
@@ -1034,8 +1027,10 @@
 	struct mdp4_overlay_pipe *pipe;
 	struct vsync_update *vp;
 	int undx;
+	int need_wait, cnt;
+	unsigned long flags;
 
-	pr_debug("%s+:\n", __func__);
+	pr_debug("%s+: pid=%d\n", __func__, current->pid);
 
 	mfd = (struct msm_fb_data_type *)platform_get_drvdata(pdev);
 
@@ -1046,26 +1041,45 @@
 		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;
+		}
+	}
+
+	if (cnt > 10) {
+		spin_lock_irqsave(&vctrl->spin_lock, flags);
+		vctrl->clk_control = 0;
+		vctrl->clk_enabled = 0;
+		vctrl->expire_tick = 0;
+		spin_unlock_irqrestore(&vctrl->spin_lock, flags);
+		mipi_dsi_clk_cfg(0);
+		mdp_clk_ctrl(0);
+		pr_err("%s: Error, SET_CLK_OFF by force\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;
 
-	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) {
@@ -1076,23 +1090,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;
 }
 
@@ -1127,7 +1125,7 @@
 	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];
@@ -1145,25 +1143,32 @@
 	}
 
 	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);
@@ -1172,8 +1177,6 @@
 
 	mdp4_overlay_mdp_perf_upd(mfd, 1);
 	mdp4_dsi_cmd_pipe_commit(cndx, 0);
-	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 a83c340..450f1de 100644
--- a/drivers/video/msm/mdp4_overlay_dsi_video.c
+++ b/drivers/video/msm/mdp4_overlay_dsi_video.c
@@ -56,6 +56,8 @@
 	int wait_vsync_cnt;
 	int blt_change;
 	int blt_free;
+	u32 blt_ctrl;
+	u32 blt_mode;
 	int sysfs_created;
 	struct mutex update_lock;
 	struct completion ov_comp;
@@ -167,6 +169,8 @@
 	pipe = vctrl->base_pipe;
 	mixer = pipe->mixer_num;
 
+	mdp_update_pm(vctrl->mfd, vctrl->vsync_time);
+
 	if (vp->update_cnt == 0) {
 		mutex_unlock(&vctrl->update_lock);
 		return cnt;
@@ -194,18 +198,6 @@
 	}
 	spin_unlock_irqrestore(&vctrl->spin_lock, flags);
 
-	if (vctrl->blt_change) {
-		pipe = vctrl->base_pipe;
-		spin_lock_irqsave(&vctrl->spin_lock, flags);
-		INIT_COMPLETION(vctrl->dmap_comp);
-		INIT_COMPLETION(vctrl->ov_comp);
-		vsync_irq_enable(INTR_DMA_P_DONE, MDP_DMAP_TERM);
-		spin_unlock_irqrestore(&vctrl->spin_lock, flags);
-		mdp4_dsi_video_wait4dmap(0);
-		if (pipe->ov_blt_addr)
-			mdp4_dsi_video_wait4ov(0);
-	}
-
 	pipe = vp->plist;
 
 	for (i = 0; i < OVERLAY_PIPE_MAX; i++, pipe++) {
@@ -373,7 +365,7 @@
 	wait_for_completion(&vctrl->ov_comp);
 }
 
-static ssize_t vsync_show_event(struct device *dev,
+ssize_t mdp4_dsi_video_show_event(struct device *dev,
 	struct device_attribute *attr, char *buf)
 {
 	int cndx;
@@ -394,9 +386,12 @@
 		INIT_COMPLETION(vctrl->vsync_comp);
 	vctrl->wait_vsync_cnt++;
 	spin_unlock_irqrestore(&vctrl->spin_lock, flags);
-	ret = wait_for_completion_interruptible(&vctrl->vsync_comp);
-	if (ret)
-		return ret;
+	ret = wait_for_completion_interruptible_timeout(&vctrl->vsync_comp,
+		msecs_to_jiffies(VSYNC_PERIOD * 4));
+	if (ret <= 0) {
+		vctrl->wait_vsync_cnt = 0;
+		return -EBUSY;
+	}
 
 	spin_lock_irqsave(&vctrl->spin_lock, flags);
 	vsync_tick = ktime_to_ns(vctrl->vsync_time);
@@ -416,7 +411,7 @@
 		return;
 	}
 
-	pr_info("%s: ndx=%d\n", __func__, cndx);
+	pr_debug("%s: ndx=%d\n", __func__, cndx);
 
 	vctrl = &vsync_ctrl_db[cndx];
 	if (vctrl->inited)
@@ -428,7 +423,7 @@
 	init_completion(&vctrl->vsync_comp);
 	init_completion(&vctrl->dmap_comp);
 	init_completion(&vctrl->ov_comp);
-	atomic_set(&vctrl->suspend, 0);
+	atomic_set(&vctrl->suspend, 1);
 	atomic_set(&vctrl->vsync_resume, 1);
 	spin_lock_init(&vctrl->spin_lock);
 }
@@ -446,16 +441,6 @@
 	vctrl->base_pipe = pipe;
 }
 
-static DEVICE_ATTR(vsync_event, S_IRUGO, vsync_show_event, NULL);
-
-static struct attribute *vsync_fs_attrs[] = {
-	&dev_attr_vsync_event.attr,
-	NULL,
-};
-
-static struct attribute_group vsync_fs_attr_group = {
-	.attrs = vsync_fs_attrs,
-};
 int mdp4_dsi_video_on(struct platform_device *pdev)
 {
 	int dsi_width;
@@ -498,6 +483,7 @@
 	int ret = 0;
 	int cndx = 0;
 	struct vsycn_ctrl *vctrl;
+	struct msm_panel_info *pinfo;
 
 	vctrl = &vsync_ctrl_db[cndx];
 	mfd = (struct msm_fb_data_type *)platform_get_drvdata(pdev);
@@ -510,6 +496,9 @@
 
 	vctrl->mfd = mfd;
 	vctrl->dev = mfd->fbi->dev;
+	pinfo = &mfd->panel_info;
+	vctrl->blt_ctrl = pinfo->lcd.blt_ctrl;
+	vctrl->blt_mode = pinfo->lcd.blt_mode;
 
 	/* mdp clock on */
 	mdp_clk_ctrl(1);
@@ -552,6 +541,7 @@
 		mfd->cont_splash_done = 1;
 		mdp4_dsi_video_wait4dmap_done(0);
 		MDP_OUTP(MDP_BASE + DSI_VIDEO_BASE, 0);
+		dsi_video_enabled = 0;
 		mipi_dsi_controller_cfg(0);
 		/* Clks are enabled in probe.
 		   Disabling clocks now */
@@ -669,20 +659,6 @@
 
 	mdp_histogram_ctrl_all(TRUE);
 
-	if (!vctrl->sysfs_created) {
-		ret = sysfs_create_group(&vctrl->dev->kobj,
-			&vsync_fs_attr_group);
-		if (ret) {
-			pr_err("%s: sysfs group creation failed, ret=%d\n",
-				__func__, ret);
-			return ret;
-		}
-
-		kobject_uevent(&vctrl->dev->kobj, KOBJ_ADD);
-		pr_debug("%s: kobject_uevent(KOBJ_ADD)\n", __func__);
-		vctrl->sysfs_created = 1;
-	}
-
 	return ret;
 }
 
@@ -955,20 +931,8 @@
 	if (vctrl->blt_change) {
 		mdp4_overlayproc_cfg(pipe);
 		mdp4_overlay_dmap_xy(pipe);
-		if (pipe->ov_blt_addr) {
-			mdp4_dsi_video_blt_ov_update(pipe);
-			pipe->ov_cnt++;
-			/* Prefill one frame */
-			vsync_irq_enable(INTR_OVERLAY0_DONE,
-						MDP_OVERLAY0_TERM);
-			/* kickoff overlay0 engine */
-			mdp4_stat.kickoff_ov0++;
-			vctrl->ov_koff++;	/* make up for prefill */
-			outpdw(MDP_BASE + 0x0004, 0);
-		}
 		vctrl->blt_change = 0;
 	}
-
 	complete_all(&vctrl->dmap_comp);
 	mdp4_overlay_dma_commit(cndx);
 	spin_unlock(&vctrl->spin_lock);
@@ -1011,19 +975,44 @@
 	int cndx = 0;
 	struct vsycn_ctrl *vctrl;
 	struct mdp4_overlay_pipe *pipe;
+	long long vtime;
+	u32 mode, ctrl;
 
 	vctrl = &vsync_ctrl_db[cndx];
 	pipe = vctrl->base_pipe;
 
-	mdp4_allocate_writeback_buf(mfd, MDP4_MIXER0);
+	mode = (dbg_force_ov0_blt & 0x0f) ?
+		(dbg_force_ov0_blt & 0x0f) : vctrl->blt_mode;
+	ctrl = (dbg_force_ov0_blt >> 4) ?
+		(dbg_force_ov0_blt >> 4) : vctrl->blt_ctrl;
 
-	if (mfd->ov0_wb_buf->write_addr == 0) {
-		pr_info("%s: no blt_base assigned\n", __func__);
+	pr_debug("%s: mode=%d, enable=%d ov_blt_addr=%x\n",
+		 __func__, mode, enable, (int)pipe->ov_blt_addr);
+
+	if ((mode == MDP4_OVERLAY_MODE_BLT_ALWAYS_OFF) &&
+	    !pipe->ov_blt_addr)
 		return;
+	else if ((mode == MDP4_OVERLAY_MODE_BLT_ALWAYS_ON) &&
+	    pipe->ov_blt_addr)
+		return;
+	else if (enable && pipe->ov_blt_addr)
+		return;
+	else if (!enable && !pipe->ov_blt_addr)
+		return;
+
+	if (pipe->ov_blt_addr == 0) {
+		mdp4_allocate_writeback_buf(mfd, MDP4_MIXER0);
+		if (mfd->ov0_wb_buf->write_addr == 0) {
+			pr_warning("%s: no blt_base assigned\n", __func__);
+			return;
+		}
 	}
 
+	pr_debug("%s: mode=%d, enable=%d ov_blt_addr=%x\n",
+		 __func__, mode, enable, (int)pipe->ov_blt_addr);
+
 	spin_lock_irqsave(&vctrl->spin_lock, flag);
-	if (enable && pipe->ov_blt_addr == 0) {
+	if (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;
@@ -1032,23 +1021,57 @@
 		vctrl->ov_done = 0;
 		vctrl->blt_free = 0;
 		mdp4_stat.blt_dsi_video++;
-		vctrl->blt_change++;
-	} else if (enable == 0 && pipe->ov_blt_addr) {
+	} else {
 		pipe->ov_blt_addr = 0;
 		pipe->dma_blt_addr =  0;
 		vctrl->blt_free = 4;	/* 4 commits to free wb buf */
-		vctrl->blt_change++;
 	}
-
-	pr_info("%s: changed=%d enable=%d ov_blt_addr=%x\n", __func__,
-		vctrl->blt_change, enable, (int)pipe->ov_blt_addr);
-
-	if (!vctrl->blt_change) {
-		spin_unlock_irqrestore(&vctrl->spin_lock, flag);
-		return;
-	}
-
 	spin_unlock_irqrestore(&vctrl->spin_lock, flag);
+
+	if (ctrl == MDP4_OVERLAY_BLT_SWITCH_TG_ON) {
+		spin_lock_irqsave(&vctrl->spin_lock, flag);
+		if (!dsi_video_enabled) {
+			pr_debug("%s: blt switched not in ISR dsi_video_enabled=%d\n",
+				__func__, dsi_video_enabled);
+			mdp4_overlayproc_cfg(pipe);
+			mdp4_overlay_dmap_xy(pipe);
+		} else {
+			pr_debug("%s: blt switched in ISR dsi_video_enabled=%d\n",
+				__func__, dsi_video_enabled);
+			vctrl->blt_change++;
+		}
+		spin_unlock_irqrestore(&vctrl->spin_lock, flag);
+		if (dsi_video_enabled)
+			mdp4_dsi_video_wait4dmap_done(0);
+	} else if (ctrl == MDP4_OVERLAY_BLT_SWITCH_TG_OFF) {
+		pr_debug("%s: blt switched by turning TG off\n", __func__);
+		if (dsi_video_enabled) {
+			mdp4_dsi_video_wait4vsync(0, &vtime);
+			MDP_OUTP(MDP_BASE + DSI_VIDEO_BASE, 0);
+			mdp4_dsi_video_wait4dmap_done(0);
+		}
+		mdp4_overlayproc_cfg(pipe);
+		mdp4_overlay_dmap_xy(pipe);
+		if (dsi_video_enabled) {
+			/*
+			* need wait for more than 1 ms to
+			* make sure dsi lanes' fifo is empty and
+			* lanes in stop state befroe reset
+			* controller
+			*/
+			usleep(2000);
+			mipi_dsi_sw_reset();
+			MDP_OUTP(MDP_BASE + DSI_VIDEO_BASE, 1);
+		}
+	} else if (ctrl == MDP4_OVERLAY_BLT_SWITCH_POLL) {
+		pr_debug("%s: blt switched by polling mdp status\n", __func__);
+		if (dsi_video_enabled)
+			while (inpdw(MDP_BASE + 0x0018) & 0x05)
+				cpu_relax();
+		mdp4_overlayproc_cfg(pipe);
+		mdp4_overlay_dmap_xy(pipe);
+	} else
+		pr_err("%s: ctrl=%d is not supported\n", __func__, ctrl);
 }
 
 void mdp4_dsi_video_overlay_blt(struct msm_fb_data_type *mfd,
@@ -1073,7 +1096,7 @@
 	uint8 *buf;
 	unsigned int buf_offset;
 	int bpp;
-	int cndx = 0;
+	int cnt, cndx = 0;
 	struct vsycn_ctrl *vctrl;
 	struct mdp4_overlay_pipe *pipe;
 
@@ -1102,16 +1125,16 @@
 		mdp4_dsi_video_pipe_queue(0, pipe);
 	}
 
-	mdp_update_pm(mfd, vsync_ctrl_db[0].vsync_time);
 	mdp4_overlay_mdp_perf_upd(mfd, 1);
-	mdp4_dsi_video_pipe_commit(cndx, 0);
 
-	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 67690cf..7de2e2a 100644
--- a/drivers/video/msm/mdp4_overlay_dtv.c
+++ b/drivers/video/msm/mdp4_overlay_dtv.c
@@ -66,12 +66,16 @@
 	int dmae_wait_cnt;
 	int wait_vsync_cnt;
 	int blt_change;
+	int blt_ctrl;
+	int blt_mode;
+	int blt_free;
 	int sysfs_created;
 	struct mutex update_lock;
 	struct completion ov_comp;
 	struct completion dmae_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_irq_enabled;
@@ -158,6 +162,7 @@
 }
 
 static void mdp4_dtv_blt_ov_update(struct mdp4_overlay_pipe *pipe);
+static void mdp4_dtv_wait4ov(int cndx);
 static void mdp4_dtv_wait4dmae(int cndx);
 
 int mdp4_dtv_pipe_commit(int cndx, int wait)
@@ -180,6 +185,8 @@
 	mixer = pipe->mixer_num;
 	mdp4_overlay_iommu_unmap_freelist(mixer);
 
+	mdp_update_pm(vctrl->mfd, vctrl->vsync_time);
+
 	if (vp->update_cnt == 0) {
 		mutex_unlock(&vctrl->update_lock);
 		return 0;
@@ -188,6 +195,13 @@
 	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);
 
 	pipe = vp->plist;
@@ -217,6 +231,7 @@
 	if (pipe->ov_blt_addr) {
 		mdp4_dtv_blt_ov_update(pipe);
 		pipe->blt_ov_done++;
+		INIT_COMPLETION(vctrl->ov_comp);
 		vsync_irq_enable(INTR_OVERLAY1_DONE, MDP_OVERLAY1_TERM);
 		mb();
 		pipe->blt_ov_koff++;
@@ -231,9 +246,12 @@
 	spin_unlock_irqrestore(&vctrl->spin_lock, flags);
 	mdp4_stat.overlay_commit[pipe->mixer_num]++;
 
-	if (wait)
-		mdp4_dtv_wait4dmae(cndx);
-
+	if (wait) {
+		if (pipe->ov_blt_addr)
+			mdp4_dtv_wait4ov(cndx);
+		else
+			mdp4_dtv_wait4dmae(cndx);
+	}
 	return cnt;
 }
 
@@ -293,6 +311,23 @@
 	*vtime = ktime_to_ns(vctrl->vsync_time);
 }
 
+static void mdp4_dtv_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);
+}
+
 static void mdp4_dtv_wait4dmae(int cndx)
 {
 	struct vsycn_ctrl *vctrl;
@@ -310,7 +345,7 @@
 	wait_for_completion(&vctrl->dmae_comp);
 }
 
-static ssize_t vsync_show_event(struct device *dev,
+ssize_t mdp4_dtv_show_event(struct device *dev,
 		struct device_attribute *attr, char *buf)
 {
 	int cndx;
@@ -337,9 +372,12 @@
 	vctrl->wait_vsync_cnt++;
 	spin_unlock_irqrestore(&vctrl->spin_lock, flags);
 
-	ret = wait_for_completion_interruptible(&vctrl->vsync_comp);
-	if (ret)
-		return ret;
+	ret = wait_for_completion_interruptible_timeout(&vctrl->vsync_comp,
+		msecs_to_jiffies(VSYNC_PERIOD * 4));
+	if (ret <= 0) {
+		vctrl->wait_vsync_cnt = 0;
+		return -EBUSY;
+	}
 
 	spin_lock_irqsave(&vctrl->spin_lock, flags);
 	vg1fd = vctrl->vg1fd;
@@ -361,6 +399,28 @@
 	return ret;
 }
 
+static void mdp4_dtv_wait4dmae_done(int cndx)
+{
+	unsigned long flags;
+	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;
+
+	spin_lock_irqsave(&vctrl->spin_lock, flags);
+	INIT_COMPLETION(vctrl->dmae_comp);
+	vsync_irq_enable(INTR_DMA_E_DONE, MDP_DMA_E_TERM);
+	spin_unlock_irqrestore(&vctrl->spin_lock, flags);
+	mdp4_dtv_wait4dmae(cndx);
+}
+
 void mdp4_dtv_vsync_init(int cndx)
 {
 	struct vsycn_ctrl *vctrl;
@@ -382,7 +442,7 @@
 	init_completion(&vctrl->vsync_comp);
 	init_completion(&vctrl->ov_comp);
 	init_completion(&vctrl->dmae_comp);
-	atomic_set(&vctrl->suspend, 0);
+	atomic_set(&vctrl->suspend, 1);
 	atomic_set(&vctrl->vsync_resume, 1);
 	spin_lock_init(&vctrl->spin_lock);
 }
@@ -439,6 +499,9 @@
 	int hsync_end_x;
 	struct fb_info *fbi;
 	struct fb_var_screeninfo *var;
+	struct vsycn_ctrl *vctrl;
+
+	vctrl = &vsync_ctrl_db[0];
 
 	if (!mfd)
 		return -ENODEV;
@@ -449,6 +512,8 @@
 	fbi = mfd->fbi;
 	var = &fbi->var;
 
+	vctrl->mfd = mfd;
+
 	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
 	if (hdmi_prim_display) {
 		if (is_mdp4_hw_reset()) {
@@ -543,6 +608,7 @@
 
 	/* enable DTV block */
 	MDP_OUTP(MDP_BASE + DTV_BASE, 1);
+	dtv_enabled = 1;
 
 	return 0;
 }
@@ -557,25 +623,18 @@
 		return -EINVAL;
 
 	MDP_OUTP(MDP_BASE + DTV_BASE, 0);
+	dtv_enabled = 0;
 
 	return 0;
 }
 
-static DEVICE_ATTR(vsync_event, S_IRUGO, vsync_show_event, NULL);
-static struct attribute *vsync_fs_attrs[] = {
-	&dev_attr_vsync_event.attr,
-	NULL,
-};
-static struct attribute_group vsync_fs_attr_group = {
-	.attrs = vsync_fs_attrs,
-};
-
 int mdp4_dtv_on(struct platform_device *pdev)
 {
 	struct msm_fb_data_type *mfd;
 	int ret = 0;
 	int cndx = 0;
 	struct vsycn_ctrl *vctrl;
+	struct msm_panel_info *pinfo;
 
 	vctrl = &vsync_ctrl_db[cndx];
 
@@ -587,7 +646,12 @@
 	if (mfd->key != MFD_KEY)
 		return -EINVAL;
 
+	vctrl->mfd = mfd;
 	vctrl->dev = mfd->fbi->dev;
+	pinfo = &mfd->panel_info;
+
+	vctrl->blt_ctrl = pinfo->lcd.blt_ctrl;
+	vctrl->blt_mode = pinfo->lcd.blt_mode;
 
 	mdp_footswitch_ctrl(TRUE);
 	/* Mdp clock enable */
@@ -609,21 +673,6 @@
 		pr_warn("%s: panel_next_on failed", __func__);
 
 	atomic_set(&vctrl->suspend, 0);
-
-	if (!vctrl->sysfs_created) {
-		ret = sysfs_create_group(&vctrl->dev->kobj,
-			&vsync_fs_attr_group);
-		if (ret) {
-			pr_err("%s: sysfs group creation failed, ret=%d\n",
-				__func__, ret);
-			return ret;
-		}
-
-		kobject_uevent(&vctrl->dev->kobj, KOBJ_ADD);
-		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)
@@ -686,7 +735,7 @@
 
 	if (vctrl->vsync_irq_enabled) {
 		vctrl->vsync_irq_enabled = 0;
-		vsync_irq_disable(INTR_PRIMARY_VSYNC, MDP_PRIM_VSYNC_TERM);
+		vsync_irq_disable(INTR_EXTERNAL_VSYNC, MDP_EXTER_VSYNC_TERM);
 	}
 
 	undx =  vctrl->update_ndx;
@@ -761,11 +810,6 @@
 	MDP_OUTP(MDP_BASE + 0xb0008, addr);
 }
 
-void mdp4_overlay_dtv_set_perf(struct msm_fb_data_type *mfd)
-{
-
-}
-
 static void mdp4_overlay_dtv_alloc_pipe(struct msm_fb_data_type *mfd,
 		int32 ptype, struct vsycn_ctrl *vctrl)
 {
@@ -953,18 +997,8 @@
 
 	spin_lock(&vctrl->spin_lock);
 	if (vctrl->blt_change) {
-		if (pipe->ov_blt_addr) {
-			mdp4_overlayproc_cfg(pipe);
-			mdp4_overlay_dmae_xy(pipe);
-			mdp4_dtv_blt_ov_update(pipe);
-			pipe->blt_ov_done++;
-
-			/* Prefill one frame */
-			vsync_irq_enable(INTR_OVERLAY1_DONE, MDP_OVERLAY1_TERM);
-			/* kickoff overlay1 engine */
-			mdp4_stat.kickoff_ov1++;
-			outpdw(MDP_BASE + 0x0008, 0);
-		}
+		mdp4_overlayproc_cfg(pipe);
+		mdp4_overlay_dmae_xy(pipe);
 		vctrl->blt_change = 0;
 	}
 
@@ -999,104 +1033,154 @@
 	}
 
 	mdp4_dtv_blt_dmae_update(pipe);
+	complete_all(&vctrl->ov_comp);
 	pipe->blt_dmap_done++;
 	vsync_irq_disable(INTR_OVERLAY1_DONE, MDP_OVERLAY1_TERM);
 	spin_unlock(&vctrl->spin_lock);
 }
 
-void mdp4_dtv_set_black_screen(void)
+void mdp4_dtv_set_black_screen()
 {
 	char *rgb_base;
 	/*Black color*/
 	uint32 color = 0x00000000;
 	uint32 temp_src_format;
-	int cndx = 0;
+	int commit = 1, cndx = 0;
+	int pipe_num = OVERLAY_PIPE_RGB1;
 	struct vsycn_ctrl *vctrl;
 
 	vctrl = &vsync_ctrl_db[cndx];
-	if (vctrl->base_pipe == NULL || !hdmi_prim_display) {
-		pr_err("dtv_pipe is not configured yet\n");
+	if (!hdmi_prim_display)
 		return;
-	}
-	rgb_base = MDP_BASE + MDP4_RGB_BASE;
-	rgb_base += (MDP4_RGB_OFF * vctrl->base_pipe->pipe_num);
 
-	/*
-	* RGB Constant Color
-	*/
+	if (vctrl->base_pipe == NULL)
+		commit = 0;
+	else
+		pipe_num = vctrl->base_pipe->pipe_num;
+
+	rgb_base = MDP_BASE;
+	rgb_base += (MDP4_RGB_OFF * (pipe_num + 2));
+
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+
+	/* RGB Constant Color */
 	MDP_OUTP(rgb_base + 0x1008, color);
-	/*
-	* MDP_RGB_SRC_FORMAT
-	*/
+
+	/* MDP_RGB_SRC_FORMAT */
 	temp_src_format = inpdw(rgb_base + 0x0050);
 	MDP_OUTP(rgb_base + 0x0050, temp_src_format | BIT(22));
-	mdp4_overlay_reg_flush(vctrl->base_pipe, 1);
 
-	mdp4_mixer_stage_up(vctrl->base_pipe, 0);
-	mdp4_mixer_stage_commit(vctrl->base_pipe->mixer_num);
-	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+	if (commit) {
+		mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+
+		mdp4_overlay_reg_flush(vctrl->base_pipe, 1);
+		mdp4_mixer_stage_up(vctrl->base_pipe, 0);
+		mdp4_mixer_stage_commit(vctrl->base_pipe->mixer_num);
+	} else {
+		/* MDP_OVERLAY_REG_FLUSH for pipe*/
+		MDP_OUTP(MDP_BASE + 0x18000,
+			BIT(pipe_num + 2) | BIT(MDP4_MIXER1));
+		mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+	}
 }
 
 static void mdp4_dtv_do_blt(struct msm_fb_data_type *mfd, int enable)
 {
 	unsigned long flag;
-	int data;
 	int cndx = 0;
 	struct vsycn_ctrl *vctrl;
 	struct mdp4_overlay_pipe *pipe;
+	u32 mode, ctrl;
 
 	vctrl = &vsync_ctrl_db[cndx];
 	pipe = vctrl->base_pipe;
 
-	mdp4_allocate_writeback_buf(mfd, MDP4_MIXER1);
+	mode = (dbg_force_ov1_blt & 0x0f) ?
+		(dbg_force_ov1_blt & 0x0f) : vctrl->blt_mode;
+	ctrl = (dbg_force_ov1_blt >> 4) ?
+		(dbg_force_ov1_blt >> 4) : vctrl->blt_ctrl;
 
-	if (!mfd->ov1_wb_buf->write_addr) {
-		pr_info("%s: ctrl=%d blt_base NOT assigned\n", __func__, cndx);
+	pr_debug("%s: mode=%d, ctrl = %d, enable=%d ov_blt_addr=%x\n",
+		 __func__, mode, ctrl, enable, (int)pipe->ov_blt_addr);
+
+	if ((mode == MDP4_OVERLAY_MODE_BLT_ALWAYS_OFF) &&
+	    !pipe->ov_blt_addr)
 		return;
+	else if ((mode == MDP4_OVERLAY_MODE_BLT_ALWAYS_ON) &&
+	    pipe->ov_blt_addr)
+		return;
+	else if (enable && pipe->ov_blt_addr)
+		return;
+	else if (!enable && !pipe->ov_blt_addr)
+		return;
+
+	if (pipe->ov_blt_addr == 0) {
+		mdp4_allocate_writeback_buf(vctrl->mfd, MDP4_MIXER1);
+		if (!vctrl->mfd->ov1_wb_buf->write_addr) {
+			pr_warning("%s: ctrl=%d blt_base NOT assigned\n",
+				__func__, cndx);
+			return;
+		}
 	}
 
+	pr_debug("%s: mode=%d, ctrl=%d, enable=%d ov_blt_addr=%x\n",
+		 __func__, mode, ctrl, enable, (int)pipe->ov_blt_addr);
+
 	spin_lock_irqsave(&vctrl->spin_lock, flag);
 	if (enable && pipe->ov_blt_addr == 0) {
-		pipe->ov_blt_addr = mfd->ov1_wb_buf->write_addr;
-		pipe->dma_blt_addr = mfd->ov1_wb_buf->read_addr;
+		pipe->ov_blt_addr = vctrl->mfd->ov1_wb_buf->write_addr;
+		pipe->dma_blt_addr = vctrl->mfd->ov1_wb_buf->read_addr;
 		pipe->blt_cnt = 0;
 		pipe->ov_cnt = 0;
 		pipe->blt_dmap_done = 0;
 		pipe->blt_ov_koff = 0;
 		pipe->blt_ov_done = 0;
 		mdp4_stat.blt_dtv++;
-		vctrl->blt_change++;
+		vctrl->blt_free = 0;
 	} else if (enable == 0 && pipe->ov_blt_addr) {
 		pipe->ov_blt_addr = 0;
 		pipe->dma_blt_addr = 0;
-		vctrl->blt_change++;
+		vctrl->blt_free = 4;
 	}
-
-	pr_info("%s: enable=%d change=%d blt_addr=%x\n", __func__,
-		enable, vctrl->blt_change, (int)pipe->ov_blt_addr);
-
-	if (!vctrl->blt_change) {
-		spin_unlock_irqrestore(&vctrl->spin_lock, flag);
-		return;
-	}
-
-	atomic_set(&vctrl->suspend, 1);
 	spin_unlock_irqrestore(&vctrl->spin_lock, flag);
 
-	data = inpdw(MDP_BASE + DTV_BASE);
-	data &= 0x01;
-	if (data)       /* timing generator enabled */
-		mdp4_dtv_wait4dmae(0);
+	if (ctrl == MDP4_OVERLAY_BLT_SWITCH_TG_ON) {
+		spin_lock_irqsave(&vctrl->spin_lock, flag);
+		if (!dtv_enabled) {
+			pr_debug("%s: blt switched not in isr dtv_enabled=%d\n",
+				 __func__, dtv_enabled);
+			mdp4_overlayproc_cfg(pipe);
+			mdp4_overlay_dmae_xy(pipe);
+		} else {
+			pr_debug("%s: blt switched in ISR dtv_enabled=%d\n",
+				__func__, dtv_enabled);
+			vctrl->blt_change++;
 
-	if (pipe->ov_blt_addr == 0) {
-		MDP_OUTP(MDP_BASE + DTV_BASE, 0);       /* stop dtv */
-		msleep(20);
+		}
+		spin_unlock_irqrestore(&vctrl->spin_lock, flag);
+		if (dtv_enabled)
+			mdp4_dtv_wait4dmae_done(0);
+	} else if (ctrl == MDP4_OVERLAY_BLT_SWITCH_TG_OFF) {
+		pr_debug("%s: dtv blt switched by turning TG off\n",
+			 __func__);
+		if (dtv_enabled) {
+			mdp4_dtv_wait4dmae_done(0);
+			MDP_OUTP(MDP_BASE + DTV_BASE, 0);
+			msleep(20);
+		}
 		mdp4_overlayproc_cfg(pipe);
 		mdp4_overlay_dmae_xy(pipe);
-		MDP_OUTP(MDP_BASE + DTV_BASE, 1);       /* start dtv */
-	}
-
-	atomic_set(&vctrl->suspend, 0);
+		if (dtv_enabled)
+			MDP_OUTP(MDP_BASE + DTV_BASE, 1);
+	} else if (ctrl == MDP4_OVERLAY_BLT_SWITCH_POLL) {
+		pr_debug("%s: dtv blt change by polling status\n",
+			__func__);
+		while (inpdw(MDP_BASE + 0x0018) & 0x12)
+			cpu_relax();
+		mdp4_overlayproc_cfg(pipe);
+		mdp4_overlay_dmae_xy(pipe);
+	} else
+		pr_err("%s: ctrl=%d is not supported\n", __func__, ctrl);
 }
 
 void mdp4_dtv_overlay_blt_start(struct msm_fb_data_type *mfd)
@@ -1114,6 +1198,7 @@
 	int cndx = 0;
 	struct vsycn_ctrl *vctrl;
 	struct mdp4_overlay_pipe *pipe;
+	int wait = 0;
 
 	mutex_lock(&mfd->dma->ov_mutex);
 	if (!mfd->panel_power_on) {
@@ -1144,9 +1229,12 @@
 		pipe->srcp0_addr = (uint32)mfd->ibuf.buf;
 		mdp4_dtv_pipe_queue(0, pipe);
 	}
-	mdp_update_pm(mfd, vsync_ctrl_db[0].vsync_time);
+
+	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);
 }
diff --git a/drivers/video/msm/mdp4_overlay_lcdc.c b/drivers/video/msm/mdp4_overlay_lcdc.c
index 9e0c411..a7058ce 100644
--- a/drivers/video/msm/mdp4_overlay_lcdc.c
+++ b/drivers/video/msm/mdp4_overlay_lcdc.c
@@ -172,6 +172,8 @@
 	pipe = vctrl->base_pipe;
 	mixer = pipe->mixer_num;
 
+	mdp_update_pm(vctrl->mfd, vctrl->vsync_time);
+
 	if (vp->update_cnt == 0) {
 		mutex_unlock(&vctrl->update_lock);
 		return 0;
@@ -359,7 +361,7 @@
 	wait_for_completion(&vctrl->ov_comp);
 }
 
-static ssize_t vsync_show_event(struct device *dev,
+ssize_t mdp4_lcdc_show_event(struct device *dev,
 		struct device_attribute *attr, char *buf)
 {
 	int cndx;
@@ -380,9 +382,12 @@
 		INIT_COMPLETION(vctrl->vsync_comp);
 	vctrl->wait_vsync_cnt++;
 	spin_unlock_irqrestore(&vctrl->spin_lock, flags);
-	ret = wait_for_completion_interruptible(&vctrl->vsync_comp);
-	if (ret)
-		return ret;
+	ret = wait_for_completion_interruptible_timeout(&vctrl->vsync_comp,
+		msecs_to_jiffies(VSYNC_PERIOD * 4));
+	if (ret <= 0) {
+		vctrl->wait_vsync_cnt = 0;
+		return -EBUSY;
+	}
 
 	spin_lock_irqsave(&vctrl->spin_lock, flags);
 	vsync_tick = ktime_to_ns(vctrl->vsync_time);
@@ -414,7 +419,7 @@
 	init_completion(&vctrl->vsync_comp);
 	init_completion(&vctrl->dmap_comp);
 	init_completion(&vctrl->ov_comp);
-	atomic_set(&vctrl->suspend, 0);
+	atomic_set(&vctrl->suspend, 1);
 	atomic_set(&vctrl->vsync_resume, 1);
 	spin_lock_init(&vctrl->spin_lock);
 }
@@ -432,15 +437,6 @@
 	vctrl->base_pipe = pipe;
 }
 
-static DEVICE_ATTR(vsync_event, S_IRUGO, vsync_show_event, NULL);
-static struct attribute *vsync_fs_attrs[] = {
-	&dev_attr_vsync_event.attr,
-	NULL,
-};
-static struct attribute_group vsync_fs_attr_group = {
-	.attrs = vsync_fs_attrs,
-};
-
 int mdp4_lcdc_on(struct platform_device *pdev)
 {
 	int lcdc_width;
@@ -655,20 +651,6 @@
 
 	mdp_histogram_ctrl_all(TRUE);
 
-	if (!vctrl->sysfs_created) {
-		ret = sysfs_create_group(&vctrl->dev->kobj,
-			&vsync_fs_attr_group);
-		if (ret) {
-			pr_err("%s: sysfs group creation failed, ret=%d\n",
-				__func__, ret);
-			return ret;
-		}
-
-		kobject_uevent(&vctrl->dev->kobj, KOBJ_ADD);
-		pr_debug("%s: kobject_uevent(KOBJ_ADD)\n", __func__);
-		vctrl->sysfs_created = 1;
-	}
-
 	return ret;
 }
 
@@ -961,7 +943,7 @@
 	uint8 *buf;
 	unsigned int buf_offset;
 	int bpp;
-	int cndx = 0;
+	int cnt, cndx = 0;
 	struct vsycn_ctrl *vctrl;
 	struct mdp4_overlay_pipe *pipe;
 
@@ -989,17 +971,16 @@
 
 		mdp4_lcdc_pipe_queue(0, pipe);
 	}
-	mdp_update_pm(mfd, vsync_ctrl_db[0].vsync_time);
 
 	mdp4_overlay_mdp_perf_upd(mfd, 1);
 
-
-	mdp4_lcdc_pipe_commit(cndx, 0);
-
-	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_writeback.c b/drivers/video/msm/mdp4_overlay_writeback.c
index 18c6635..aa50d94 100644
--- a/drivers/video/msm/mdp4_overlay_writeback.c
+++ b/drivers/video/msm/mdp4_overlay_writeback.c
@@ -88,6 +88,10 @@
 }
 
 static int mdp4_overlay_writeback_update(struct msm_fb_data_type *mfd);
+static void mdp4_wfd_queue_wakeup(struct msm_fb_data_type *mfd,
+		struct msmfb_writeback_data_list *node);
+static void mdp4_wfd_dequeue_update(struct msm_fb_data_type *mfd,
+		struct msmfb_writeback_data_list **wfdnode);
 
 int mdp4_overlay_writeback_on(struct platform_device *pdev)
 {
@@ -171,6 +175,8 @@
 	struct vsycn_ctrl *vctrl;
 	struct mdp4_overlay_pipe *pipe;
 	int ret = 0;
+	int undx;
+	struct vsync_update *vp;
 
 	pr_debug("%s+:\n", __func__);
 
@@ -189,6 +195,16 @@
 	mdp4_overlay_pipe_free(pipe);
 	vctrl->base_pipe = NULL;
 
+	undx =  vctrl->update_ndx;
+	vp = &vctrl->vlist[undx];
+	if (vp->update_cnt) {
+		/*
+		 * pipe's iommu will be freed at next overlay play
+		 * and iommu_drop statistic will be increased by one
+		 */
+		vp->update_cnt = 0;     /* empty queue */
+	}
+
 	ret = panel_next_off(pdev);
 
 	mdp_clk_ctrl(1);
@@ -253,6 +269,12 @@
 	mdp4_mixer_stage_up(pipe, 0);
 
 	mdp4_overlayproc_cfg(pipe);
+
+	if (hdmi_prim_display)
+		outpdw(MDP_BASE + 0x100F4, 0x01);
+	else
+		outpdw(MDP_BASE + 0x100F4, 0x02);
+
 	/* MDP cmd block disable */
 	mdp_clk_ctrl(0);
 
@@ -299,7 +321,8 @@
 
 static void mdp4_wfd_wait4ov(int cndx);
 
-int mdp4_wfd_pipe_commit(void)
+int mdp4_wfd_pipe_commit(struct msm_fb_data_type *mfd,
+			int cndx, int wait)
 {
 	int  i, undx;
 	int mixer = 0;
@@ -309,8 +332,9 @@
 	struct mdp4_overlay_pipe *real_pipe;
 	unsigned long flags;
 	int cnt = 0;
+	struct msmfb_writeback_data_list *node = NULL;
 
-	vctrl = &vsync_ctrl_db[0];
+	vctrl = &vsync_ctrl_db[cndx];
 
 	mutex_lock(&vctrl->update_lock);
 	undx =  vctrl->update_ndx;
@@ -328,6 +352,8 @@
 	vp->update_cnt = 0;     /* reset */
 	mutex_unlock(&vctrl->update_lock);
 
+	mdp4_wfd_dequeue_update(mfd, &node);
+
 	/* free previous committed iommu back to pool */
 	mdp4_overlay_iommu_unmap_freelist(mixer);
 
@@ -365,6 +391,11 @@
 
 	mdp4_stat.overlay_commit[pipe->mixer_num]++;
 
+	if (wait)
+		mdp4_wfd_wait4ov(cndx);
+
+	mdp4_wfd_queue_wakeup(mfd, node);
+
 	return cnt;
 }
 
@@ -426,7 +457,6 @@
 
 void mdp4_writeback_overlay(struct msm_fb_data_type *mfd)
 {
-	struct msmfb_writeback_data_list *node = NULL;
 	struct vsycn_ctrl *vctrl;
 	struct mdp4_overlay_pipe *pipe;
 
@@ -438,36 +468,7 @@
 	vctrl = &vsync_ctrl_db[0];
 	pipe = vctrl->base_pipe;
 
-	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);
-
-	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);
-	if (pipe && !pipe->ov_blt_addr) {
-		pr_err("%s: no writeback buffer 0x%x\n", __func__,
-				(unsigned int)pipe->ov_blt_addr);
-		goto fail_no_blt_addr;
-	}
 
 	if (pipe->pipe_type == OVERLAY_TYPE_RGB)
 		mdp4_wfd_pipe_queue(0, pipe);
@@ -475,26 +476,15 @@
 	mdp4_overlay_mdp_perf_upd(mfd, 1);
 
 	mdp_clk_ctrl(1);
-	mdp4_overlay_writeback_update(mfd);
 
-	mdp4_wfd_pipe_commit();
+	mdp4_wfd_pipe_commit(mfd, 0, 1);
 
 	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);
-	mfd->writeback_active_cnt--;
-	mutex_unlock(&mfd->writeback_mutex);
-	wake_up(&mfd->wait_q);
-fail_no_blt_addr:
-	/*NOTE: This api was removed
-	  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(
@@ -745,3 +735,68 @@
 	mutex_unlock(&mfd->unregister_mutex);
 	return rc;
 }
+
+static void mdp4_wfd_dequeue_update(struct msm_fb_data_type *mfd,
+			struct msmfb_writeback_data_list **wfdnode)
+{
+	struct vsycn_ctrl *vctrl;
+	struct mdp4_overlay_pipe *pipe;
+	struct msmfb_writeback_data_list *node = NULL;
+
+	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);
+	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);
+
+	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;
+	}
+
+	mdp4_overlay_writeback_update(mfd);
+
+	*wfdnode = node;
+
+	mutex_unlock(&mfd->unregister_mutex);
+}
+
+static void mdp4_wfd_queue_wakeup(struct msm_fb_data_type *mfd,
+			struct msmfb_writeback_data_list *node)
+{
+
+	if (mfd && !mfd->panel_power_on)
+		return;
+
+	if (node == NULL)
+		return;
+
+	pr_debug("%s: mfd=%x node: %p", __func__, (int)mfd, node);
+
+	mutex_lock(&mfd->writeback_mutex);
+	list_add_tail(&node->active_entry, &mfd->writeback_busy_queue);
+	mfd->writeback_active_cnt--;
+	mutex_unlock(&mfd->writeback_mutex);
+	wake_up(&mfd->wait_q);
+}
diff --git a/drivers/video/msm/mdp4_util.c b/drivers/video/msm/mdp4_util.c
index 516722e..b6c2634 100644
--- a/drivers/video/msm/mdp4_util.c
+++ b/drivers/video/msm/mdp4_util.c
@@ -454,8 +454,6 @@
 	clk_rate = mdp_get_core_clk();
 	mdp4_fetch_cfg(clk_rate);
 
-	mdp4_overlay_cfg_init();
-
 	/* Mark hardware as initialized. Only revisions > v2.1 have a register
 	 * for tracking core reset status. */
 	if (mdp_hw_revision > MDP4_REVISION_V2_1)
@@ -630,6 +628,7 @@
 		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) {
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 54f5ef5..767375d 100644
--- a/drivers/video/msm/mdp_debugfs.c
+++ b/drivers/video/msm/mdp_debugfs.c
@@ -1029,6 +1029,135 @@
 	.write = dbg_reg_write,
 };
 
+u32 dbg_force_ov0_blt;
+u32 dbg_force_ov1_blt;
+
+static ssize_t dbg_force_ov0_blt_read(
+	struct file *file,
+	char __user *buff,
+	size_t count,
+	loff_t *ppos) {
+	int len;
+
+	if (*ppos)
+		return 0;
+
+	len = snprintf(debug_buf, sizeof(debug_buf),
+		       "%d\n", dbg_force_ov0_blt);
+
+	if (len < 0)
+		return 0;
+
+	if (copy_to_user(buff, debug_buf, len))
+		return -EFAULT;
+
+	*ppos += len;
+
+	return len;
+}
+
+static ssize_t dbg_force_ov0_blt_write(
+	struct file *file,
+	const char __user *buff,
+	size_t count,
+	loff_t *ppos)
+{
+	u32 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", &dbg_force_ov0_blt);
+
+	pr_info("%s: dbg_force_ov0_blt = %x\n",
+		__func__, dbg_force_ov0_blt);
+
+	if ((dbg_force_ov0_blt & 0x0f) > 2)
+		pr_err("%s: invalid dbg_force_ov0_blt = %d\n",
+			__func__, dbg_force_ov0_blt);
+
+	if ((dbg_force_ov0_blt >> 4) > 2)
+		pr_err("%s: invalid dbg_force_ov0_blt = %d\n",
+			__func__, dbg_force_ov0_blt);
+
+	return count;
+}
+
+static const struct file_operations dbg_force_ov0_blt_fops = {
+	.open = dbg_open,
+	.release = dbg_release,
+	.read = dbg_force_ov0_blt_read,
+	.write = dbg_force_ov0_blt_write,
+};
+
+static ssize_t dbg_force_ov1_blt_read(
+	struct file *file,
+	char __user *buff,
+	size_t count,
+	loff_t *ppos) {
+	int len;
+
+	if (*ppos)
+		return 0;
+
+	len = snprintf(debug_buf, sizeof(debug_buf),
+		       "%x\n", dbg_force_ov1_blt);
+
+	if (len < 0)
+		return 0;
+
+	if (copy_to_user(buff, debug_buf, len))
+		return -EFAULT;
+
+	*ppos += len;
+
+	return len;
+}
+
+static ssize_t dbg_force_ov1_blt_write(
+	struct file *file,
+	const char __user *buff,
+	size_t count,
+	loff_t *ppos)
+{
+	u32 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", &dbg_force_ov1_blt);
+
+	pr_info("%s: dbg_force_ov1_blt = %x\n",
+		__func__, dbg_force_ov1_blt);
+
+	if ((dbg_force_ov1_blt & 0x0f) > 2)
+		pr_err("%s: invalid dbg_force_ov1_blt = %x\n",
+			__func__, dbg_force_ov1_blt);
+
+	if ((dbg_force_ov1_blt >> 4) > 2)
+		pr_err("%s: invalid dbg_force_ov1_blt = %d\n",
+			__func__, dbg_force_ov1_blt);
+
+	return count;
+}
+
+static const struct file_operations dbg_force_ov1_blt_fops = {
+	.open = dbg_open,
+	.release = dbg_release,
+	.read = dbg_force_ov1_blt_read,
+	.write = dbg_force_ov1_blt_write,
+};
+
 #ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL
 static uint32 hdmi_offset;
 static uint32 hdmi_count;
@@ -1249,6 +1378,22 @@
 	}
 #endif
 
+	if (debugfs_create_file("force_ov0_blt", 0644, dent, 0,
+				&dbg_force_ov0_blt_fops)
+			== NULL) {
+		pr_err("%s(%d): debugfs_create_file: debug fail\n",
+			__FILE__, __LINE__);
+		return -EFAULT;
+	}
+
+	if (debugfs_create_file("force_ov1_blt", 0644, dent, 0,
+				&dbg_force_ov1_blt_fops)
+			== NULL) {
+		pr_err("%s(%d): debugfs_create_file: debug fail\n",
+			__FILE__, __LINE__);
+		return -EFAULT;
+	}
+
 	dent = debugfs_create_dir("mddi", NULL);
 
 	if (IS_ERR(dent)) {
diff --git a/drivers/video/msm/mdp_dma_dsi_video.c b/drivers/video/msm/mdp_dma_dsi_video.c
index e2fb8ba..cfbff9a 100644
--- a/drivers/video/msm/mdp_dma_dsi_video.c
+++ b/drivers/video/msm/mdp_dma_dsi_video.c
@@ -34,7 +34,7 @@
 static int first_pixel_start_x;
 static int first_pixel_start_y;
 
-static ssize_t vsync_show_event(struct device *dev,
+ssize_t mdp_dma_video_show_event(struct device *dev,
 		struct device_attribute *attr, char *buf)
 {
 	ssize_t ret = 0;
@@ -52,15 +52,6 @@
 	return ret;
 }
 
-static DEVICE_ATTR(vsync_event, S_IRUGO, vsync_show_event, NULL);
-static struct attribute *vsync_fs_attrs[] = {
-	&dev_attr_vsync_event.attr,
-	NULL,
-};
-static struct attribute_group vsync_fs_attr_group = {
-	.attrs = vsync_fs_attrs,
-};
-
 int mdp_dsi_video_on(struct platform_device *pdev)
 {
 	int dsi_width;
@@ -254,20 +245,6 @@
 	/* MDP cmd block disable */
 	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
 
-	if (!vsync_cntrl.sysfs_created) {
-		ret = sysfs_create_group(&vsync_cntrl.dev->kobj,
-			&vsync_fs_attr_group);
-		if (ret) {
-			pr_err("%s: sysfs creation failed, ret=%d\n",
-				__func__, ret);
-			return ret;
-		}
-
-		kobject_uevent(&vsync_cntrl.dev->kobj, KOBJ_ADD);
-		pr_debug("%s: kobject_uevent(KOBJ_ADD)\n", __func__);
-		vsync_cntrl.sysfs_created = 1;
-	}
-
 	return ret;
 }
 
diff --git a/drivers/video/msm/mdp_dma_lcdc.c b/drivers/video/msm/mdp_dma_lcdc.c
index fbfe35f..c51a99a 100644
--- a/drivers/video/msm/mdp_dma_lcdc.c
+++ b/drivers/video/msm/mdp_dma_lcdc.c
@@ -51,7 +51,7 @@
 int first_pixel_start_x;
 int first_pixel_start_y;
 
-static ssize_t vsync_show_event(struct device *dev,
+ssize_t mdp_dma_lcdc_show_event(struct device *dev,
 		struct device_attribute *attr, char *buf)
 {
 	ssize_t ret = 0;
@@ -69,15 +69,6 @@
 	return ret;
 }
 
-static DEVICE_ATTR(vsync_event, S_IRUGO, vsync_show_event, NULL);
-static struct attribute *vsync_fs_attrs[] = {
-	&dev_attr_vsync_event.attr,
-	NULL,
-};
-static struct attribute_group vsync_fs_attr_group = {
-	.attrs = vsync_fs_attrs,
-};
-
 int mdp_lcdc_on(struct platform_device *pdev)
 {
 	int lcdc_width;
@@ -320,20 +311,6 @@
 	/* MDP cmd block disable */
 	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
 
-	if (!vsync_cntrl.sysfs_created) {
-		ret = sysfs_create_group(&vsync_cntrl.dev->kobj,
-			&vsync_fs_attr_group);
-		if (ret) {
-			pr_err("%s: sysfs creation failed, ret=%d\n",
-				__func__, ret);
-			return ret;
-		}
-
-		kobject_uevent(&vsync_cntrl.dev->kobj, KOBJ_ADD);
-		pr_debug("%s: kobject_uevent(KOBJ_ADD)\n", __func__);
-		vsync_cntrl.sysfs_created = 1;
-	}
-
 	return ret;
 }
 
diff --git a/drivers/video/msm/mdss/Kconfig b/drivers/video/msm/mdss/Kconfig
index 424455f..56eb90c 100644
--- a/drivers/video/msm/mdss/Kconfig
+++ b/drivers/video/msm/mdss/Kconfig
@@ -11,3 +11,12 @@
 	---help---
 	The MDSS HDMI Panel provides support for transmitting TMDS signals of
 	MDSS frame buffer data to connected hdmi compliant TVs, monitors etc.
+
+config FB_MSM_MDSS_HDMI_MHL_8334
+	depends on FB_MSM_MDSS_HDMI_PANEL
+	bool 'MHL SII8334 support '
+	default n
+	---help---
+	  Support the HDMI to MHL conversion.
+	  MHL (Mobile High-Definition Link) technology
+	  uses USB connector to output HDMI content
diff --git a/drivers/video/msm/mdss/Makefile b/drivers/video/msm/mdss/Makefile
index b4bd31e..88a7c45 100644
--- a/drivers/video/msm/mdss/Makefile
+++ b/drivers/video/msm/mdss/Makefile
@@ -18,5 +18,6 @@
 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_HDMI_MHL_8334) += mhl_sii8334.o
 
 obj-$(CONFIG_FB_MSM_MDSS_WRITEBACK) += mdss_wb.o
diff --git a/drivers/video/msm/mdss/mdss.h b/drivers/video/msm/mdss/mdss.h
index 3c60c2b..d041125 100644
--- a/drivers/video/msm/mdss/mdss.h
+++ b/drivers/video/msm/mdss/mdss.h
@@ -21,6 +21,8 @@
 #include <linux/types.h>
 #include <linux/workqueue.h>
 
+#include <mach/iommu_domains.h>
+
 #define MDSS_REG_WRITE(addr, val) writel_relaxed(val, mdss_res->mdp_base + addr)
 #define MDSS_REG_READ(addr) readl_relaxed(mdss_res->mdp_base + addr)
 
@@ -34,6 +36,21 @@
 	MDSS_MAX_CLK
 };
 
+enum mdss_iommu_domain_type {
+	MDSS_IOMMU_DOMAIN_SECURE,
+	MDSS_IOMMU_DOMAIN_UNSECURE,
+	MDSS_IOMMU_MAX_DOMAIN
+};
+
+struct mdss_iommu_map_type {
+	char *client_name;
+	char *ctx_name;
+	struct device *ctx;
+	struct msm_iova_partition partitions[1];
+	int npartitions;
+	int domain_idx;
+};
+
 struct mdss_data_type {
 	u32 rev;
 	u32 mdp_rev;
@@ -72,8 +89,8 @@
 	u32 *mixer_type_map;
 
 	struct ion_client *iclient;
-	int iommu_domain;
 	int iommu_attached;
+	struct mdss_iommu_map_type *iommu_map;
 
 	struct early_suspend early_suspend;
 };
@@ -112,14 +129,14 @@
 	return mdss_res->iommu_attached;
 }
 
-static inline int mdss_get_iommu_domain(void)
+static inline int mdss_get_iommu_domain(u32 type)
 {
+	if (type >= MDSS_IOMMU_MAX_DOMAIN)
+		return -EINVAL;
+
 	if (!mdss_res)
 		return -ENODEV;
 
-	return mdss_res->iommu_domain;
+	return mdss_res->iommu_map[type].domain_idx;
 }
-
-int mdss_iommu_attach(void);
-int mdss_iommu_dettach(void);
 #endif /* MDSS_H */
diff --git a/drivers/video/msm/mdss/mdss_dsi.c b/drivers/video/msm/mdss/mdss_dsi.c
index 8f4f4d5..980ed46 100644
--- a/drivers/video/msm/mdss/mdss_dsi.c
+++ b/drivers/video/msm/mdss/mdss_dsi.c
@@ -327,6 +327,26 @@
 	return ret;
 }
 
+static int mdss_dsi_event_handler(struct mdss_panel_data *pdata,
+				  int event, void *arg)
+{
+	int rc = 0;
+
+	pr_debug("%s: event=%d\n", __func__, event);
+	switch (event) {
+	case MDSS_EVENT_UNBLANK:
+		rc = mdss_dsi_on(pdata);
+		break;
+	case MDSS_EVENT_BLANK:
+		rc = mdss_dsi_ctrl_unprepare(pdata);
+		break;
+	case MDSS_EVENT_TIMEGEN_OFF:
+		rc = mdss_dsi_off(pdata);
+		break;
+	}
+	return rc;
+}
+
 static int mdss_dsi_resource_initialized;
 
 static int __devinit mdss_dsi_probe(struct platform_device *pdev)
@@ -476,9 +496,7 @@
 	if (!ctrl_pdata)
 		return -ENOMEM;
 
-	(ctrl_pdata->panel_data).on = mdss_dsi_on;
-	(ctrl_pdata->panel_data).off = mdss_dsi_off;
-	(ctrl_pdata->panel_data).intf_unprepare = mdss_dsi_ctrl_unprepare;
+	ctrl_pdata->panel_data.event_handler = mdss_dsi_event_handler;
 	memcpy(&((ctrl_pdata->panel_data).panel_info),
 				&(panel_data->panel_info),
 				       sizeof(struct mdss_panel_info));
diff --git a/drivers/video/msm/mdss/mdss_dsi_host.c b/drivers/video/msm/mdss/mdss_dsi_host.c
index 125644e..8c3b1a8 100644
--- a/drivers/video/msm/mdss/mdss_dsi_host.c
+++ b/drivers/video/msm/mdss/mdss_dsi_host.c
@@ -1199,6 +1199,7 @@
 {
 	int len;
 	int i;
+	int domain = MDSS_IOMMU_DOMAIN_UNSECURE;
 	char *bp;
 	unsigned long size, addr;
 	struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL;
@@ -1229,7 +1230,7 @@
 
 	if (is_mdss_iommu_attached()) {
 		int ret = msm_iommu_map_contig_buffer(tp->dmap,
-					mdss_get_iommu_domain(), 0,
+					mdss_get_iommu_domain(domain), 0,
 					size, SZ_4K, 0, &(addr));
 		if (IS_ERR_VALUE(ret)) {
 			pr_err("unable to map dma memory to iommu(%d)\n", ret);
@@ -1251,8 +1252,8 @@
 	wait_for_completion(&dsi_dma_comp);
 
 	if (is_mdss_iommu_attached())
-		msm_iommu_unmap_contig_buffer(addr, mdss_get_iommu_domain(),
-					      0, size);
+		msm_iommu_unmap_contig_buffer(addr,
+			mdss_get_iommu_domain(domain), 0, size);
 
 	dma_unmap_single(&dsi_dev, tp->dmap, size, DMA_TO_DEVICE);
 	tp->dmap = 0;
diff --git a/drivers/video/msm/mdss/mdss_edp.c b/drivers/video/msm/mdss/mdss_edp.c
index 9460d71..1cf3101 100644
--- a/drivers/video/msm/mdss/mdss_edp.c
+++ b/drivers/video/msm/mdss/mdss_edp.c
@@ -22,6 +22,8 @@
 #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>
@@ -37,15 +39,14 @@
 #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);
@@ -134,7 +135,7 @@
 }
 
 /*
- * Enables the gpio that supply power to the panel
+ * 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)
 {
@@ -143,7 +144,8 @@
 	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 not specified\n", __func__);
+		pr_err("%s: gpio_panel_en=%d not specified\n", __func__,
+				edp_drv->gpio_panel_en);
 		goto gpio_err;
 	}
 
@@ -151,7 +153,7 @@
 	if (ret) {
 		pr_err("%s: Request reset gpio_panel_en failed, ret=%d\n",
 				__func__, ret);
-		goto gpio_free;
+		return ret;
 	}
 
 	ret = gpio_direction_output(edp_drv->gpio_panel_en, 1);
@@ -161,8 +163,6 @@
 		goto gpio_free;
 	}
 
-	gpio_set_value(edp_drv->gpio_panel_en, 1);
-
 	return 0;
 
 gpio_free:
@@ -171,7 +171,92 @@
 	return -ENODEV;
 }
 
-static void mdss_edp_config_sync(unsigned char *edp_base)
+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_pwm;
+	}
+
+	return 0;
+
+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;
 
@@ -234,6 +319,7 @@
 	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);
+	gpio_set_value(edp_drv->gpio_panel_en, 1);
 
 	return 0;
 }
@@ -251,6 +337,8 @@
 		return -EINVAL;
 	}
 
+	gpio_set_value(edp_drv->gpio_panel_en, 0);
+	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);
@@ -265,6 +353,23 @@
 	return ret;
 }
 
+static int mdss_edp_event_handler(struct mdss_panel_data *pdata,
+				  int event, void *arg)
+{
+	int rc = 0;
+
+	pr_debug("%s: event=%d\n", __func__, event);
+	switch (event) {
+	case MDSS_EVENT_UNBLANK:
+		rc = mdss_edp_on(pdata);
+		break;
+	case MDSS_EVENT_TIMEGEN_OFF:
+		rc = mdss_edp_off(pdata);
+		break;
+	}
+	return rc;
+}
+
 /*
  * Converts from EDID struct to mdss_panel_info
  */
@@ -322,9 +427,11 @@
 	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.event_handler = mdss_edp_event_handler;
+	edp_drv->panel_data.set_backlight = mdss_edp_set_backlight;
 
 	ret = mdss_register_panel(&edp_drv->panel_data);
 	if (ret) {
@@ -468,12 +575,19 @@
 	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);
diff --git a/drivers/video/msm/mdss/mdss_edp.h b/drivers/video/msm/mdss/mdss_edp.h
index 72c061f..00ef206 100644
--- a/drivers/video/msm/mdss/mdss_edp.h
+++ b/drivers/video/msm/mdss/mdss_edp.h
@@ -92,6 +92,12 @@
 
 	/* 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);
diff --git a/drivers/video/msm/mdss/mdss_fb.c b/drivers/video/msm/mdss/mdss_fb.c
index ca5f890..4ec4046 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 = {
@@ -209,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;
@@ -269,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);
 
@@ -280,6 +325,24 @@
 	return 0;
 }
 
+static inline int mdss_fb_send_panel_event(struct msm_fb_data_type *mfd, int e)
+{
+	struct mdss_panel_data *pdata;
+
+	pdata = dev_get_platdata(&mfd->pdev->dev);
+	if (!pdata) {
+		pr_err("no panel connected\n");
+		return -ENODEV;
+	}
+
+	pr_debug("sending event=%d for fb%d\n", e, mfd->index);
+
+	if (pdata->event_handler)
+		return pdata->event_handler(pdata, e, NULL);
+
+	return 0;
+}
+
 static int mdss_fb_suspend_sub(struct msm_fb_data_type *mfd)
 {
 	int ret = 0;
@@ -289,6 +352,12 @@
 
 	pr_debug("mdss_fb suspend index=%d\n", mfd->index);
 
+	ret = mdss_fb_send_panel_event(mfd, MDSS_EVENT_SUSPEND);
+	if (ret) {
+		pr_warn("unable to suspend fb%d (%d)\n", mfd->index, ret);
+		return ret;
+	}
+
 	mfd->suspend.op_enable = mfd->op_enable;
 	mfd->suspend.panel_power_on = mfd->panel_power_on;
 
@@ -314,6 +383,12 @@
 
 	pr_debug("mdss_fb resume index=%d\n", mfd->index);
 
+	ret = mdss_fb_send_panel_event(mfd, MDSS_EVENT_RESUME);
+	if (ret) {
+		pr_warn("unable to resume fb%d (%d)\n", mfd->index, ret);
+		return ret;
+	}
+
 	/* resume state var recover */
 	mfd->op_enable = mfd->suspend.op_enable;
 
@@ -416,9 +491,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;
@@ -430,15 +542,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;
 	}
 }
 
@@ -602,6 +721,7 @@
 	size *= mfd->fb_page;
 
 	if (mfd->index == 0) {
+		int dom;
 		virt = allocate_contiguous_memory(size, MEMTYPE_EBI1, SZ_1M, 0);
 		if (!virt) {
 			pr_err("unable to alloc fbmem size=%u\n", size);
@@ -609,9 +729,9 @@
 		}
 		phys = memory_pool_node_paddr(virt);
 		if (is_mdss_iommu_attached()) {
-			msm_iommu_map_contig_buffer(phys,
-				mdss_get_iommu_domain(), 0, size, SZ_4K, 0,
-				&(mfd->iova));
+			dom = mdss_get_iommu_domain(MDSS_IOMMU_DOMAIN_UNSECURE);
+			msm_iommu_map_contig_buffer(phys, dom, 0, size, SZ_4K,
+						    0, &(mfd->iova));
 		}
 		pr_info("allocating %u bytes at %p (%lx phys) for fb %d\n",
 			size, virt, phys, mfd->index);
@@ -822,6 +942,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)
@@ -1124,7 +1251,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;
@@ -1179,6 +1307,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;
@@ -1256,7 +1388,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 25c39f6..5e57de6 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;
@@ -57,6 +64,9 @@
 	u32 fb_imgType;
 	u32 dst_format;
 	int vsync_pending;
+	ktime_t vsync_time;
+	struct completion vsync_comp;
+	spinlock_t vsync_lock;
 
 	int hw_refresh;
 
@@ -83,7 +93,10 @@
 	unsigned long cursor_buf_iova;
 
 	u32 bl_level;
+	u32 bl_scale;
+	u32 bl_min_lvl;
 	struct mutex lock;
+	struct mutex ov_lock;
 
 	struct platform_device *pdev;
 
@@ -98,6 +111,8 @@
 	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..539cd49 100644
--- a/drivers/video/msm/mdss/mdss_hdmi_tx.c
+++ b/drivers/video/msm/mdss/mdss_hdmi_tx.c
@@ -15,9 +15,11 @@
 #include <linux/delay.h>
 #include <linux/module.h>
 #include <linux/mutex.h>
+#include <linux/iopoll.h>
 #include <linux/of_address.h>
 #include <linux/of_gpio.h>
 #include <linux/types.h>
+#include <mach/msm_hdmi_audio.h>
 
 #define REG_DUMP 0
 
@@ -36,12 +38,27 @@
 #define SW_RESET BIT(2)
 #define SW_RESET_PLL BIT(0)
 
+#define HPD_DISCONNECT_POLARITY 0
+#define HPD_CONNECT_POLARITY    1
+
 #define IFRAME_CHECKSUM_32(d)			\
 	((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);
+static void hdmi_tx_hpd_off(struct hdmi_tx_ctrl *hdmi_ctrl);
 
 struct mdss_hw hdmi_tx_hw = {
 	.hw_ndx = MDSS_HW_HDMI,
@@ -49,6 +66,21 @@
 	.irq_handler = hdmi_tx_isr,
 };
 
+struct dss_gpio hpd_gpio_config[] = {
+	{0, 1, COMPATIBLE_NAME "-hpd"},
+	{0, 1, COMPATIBLE_NAME "-ddc-clk"},
+	{0, 1, COMPATIBLE_NAME "-ddc-data"},
+	{0, 1, COMPATIBLE_NAME "-mux-en"},
+	{0, 0, COMPATIBLE_NAME "-mux-sel"}
+};
+
+struct dss_gpio core_gpio_config[] = {
+};
+
+struct dss_gpio cec_gpio_config[] = {
+	{0, 1, COMPATIBLE_NAME "-cec"}
+};
+
 const char *hdmi_pm_name(enum hdmi_tx_power_module_type module)
 {
 	switch (module) {
@@ -90,6 +122,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 +388,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,
@@ -424,86 +477,8 @@
 	return status;
 } /* hdmi_tx_read_sink_info */
 
-static void hdmi_tx_hpd_state_work(struct work_struct *work)
-{
-	u32 hpd_state = false;
-	struct hdmi_tx_ctrl *hdmi_ctrl = NULL;
-	struct dss_io_data *io = NULL;
-
-	hdmi_ctrl = container_of(work, struct hdmi_tx_ctrl, hpd_state_work);
-	if (!hdmi_ctrl || !hdmi_ctrl->hpd_initialized) {
-		DEV_DBG("%s: invalid input\n", __func__);
-		return;
-	}
-
-	io = &hdmi_ctrl->pdata.io[HDMI_TX_CORE_IO];
-	if (!io->base) {
-		DEV_ERR("%s: Core io is not initialized\n", __func__);
-		return;
-	}
-
-	DEV_DBG("%s: Got HPD interrupt\n", __func__);
-
-	hpd_state = (DSS_REG_R(io, HDMI_HPD_INT_STATUS) & BIT(1)) >> 1;
-	mutex_lock(&hdmi_ctrl->mutex);
-	if ((hdmi_ctrl->hpd_prev_state != hdmi_ctrl->hpd_state) ||
-		(hdmi_ctrl->hpd_state != hpd_state)) {
-
-		hdmi_ctrl->hpd_state = hpd_state;
-		hdmi_ctrl->hpd_prev_state = hdmi_ctrl->hpd_state;
-		hdmi_ctrl->hpd_stable = 0;
-
-		DEV_DBG("%s: state not stable yet, wait again (%d|%d|%d)\n",
-			__func__, hdmi_ctrl->hpd_prev_state,
-			hdmi_ctrl->hpd_state, hpd_state);
-
-		mutex_unlock(&hdmi_ctrl->mutex);
-
-		mod_timer(&hdmi_ctrl->hpd_state_timer, jiffies + HZ/2);
-
-		return;
-	}
-
-	if (hdmi_ctrl->hpd_stable) {
-		mutex_unlock(&hdmi_ctrl->mutex);
-		DEV_DBG("%s: no more timer, depending on IRQ now\n",
-			__func__);
-		return;
-	}
-
-	hdmi_ctrl->hpd_stable = 1;
-
-	/*
-	 *todo: Revisit cable chg detected condition when HPD support is ready
-	 */
-	hdmi_ctrl->hpd_cable_chg_detected = false;
-	mutex_unlock(&hdmi_ctrl->mutex);
-
-	if (hpd_state) {
-		/* todo: what if EDID read fails? */
-		hdmi_tx_read_sink_info(hdmi_ctrl);
-		DEV_INFO("HDMI HPD: sense CONNECTED: send ONLINE\n");
-		kobject_uevent(hdmi_ctrl->kobj, KOBJ_ONLINE);
-		switch_set_state(&hdmi_ctrl->sdev, 1);
-		DEV_INFO("%s: Hdmi state switch to %d\n", __func__,
-			hdmi_ctrl->sdev.state);
-	} else {
-		DEV_INFO("HDMI HPD: sense DISCONNECTED: send OFFLINE\n");
-		kobject_uevent(hdmi_ctrl->kobj, KOBJ_OFFLINE);
-		switch_set_state(&hdmi_ctrl->sdev, 0);
-		DEV_INFO("%s: Hdmi state switch to %d\n", __func__,
-			hdmi_ctrl->sdev.state);
-	}
-
-	/* Set IRQ for HPD */
-	DSS_REG_W(io, HDMI_HPD_INT_CTRL, 4 | (hpd_state ? 0 : 2));
-} /* hdmi_tx_hpd_state_work */
-
 static void hdmi_tx_hpd_int_work(struct work_struct *work)
 {
-	u32 hpd_int_status;
-	u32 hpd_int_ctrl;
-	u32 cable_detected;
 	struct hdmi_tx_ctrl *hdmi_ctrl = NULL;
 	struct dss_io_data *io = NULL;
 
@@ -519,23 +494,27 @@
 		return;
 	}
 
-	/* Process HPD Interrupt */
-	hpd_int_status = DSS_REG_R(io, HDMI_HPD_INT_STATUS);
-	hpd_int_ctrl = DSS_REG_R(io, HDMI_HPD_INT_CTRL);
+	DEV_DBG("%s: Got HPD interrupt\n", __func__);
 
-	DSS_REG_W(io, HDMI_HPD_INT_CTRL, BIT(2));
+	hdmi_ctrl->hpd_state =
+		(DSS_REG_R(io, HDMI_HPD_INT_STATUS) & BIT(1)) >> 1;
+	if (hdmi_ctrl->hpd_state) {
+		hdmi_tx_read_sink_info(hdmi_ctrl);
+		DEV_INFO("HDMI HPD: sense CONNECTED: send ONLINE\n");
+		kobject_uevent(hdmi_ctrl->kobj, KOBJ_ONLINE);
+		switch_set_state(&hdmi_ctrl->sdev, 1);
+		DEV_INFO("%s: Hdmi state switch to %d\n", __func__,
+			hdmi_ctrl->sdev.state);
+	} else {
+		DEV_INFO("HDMI HPD: sense DISCONNECTED: send OFFLINE\n");
+		kobject_uevent(hdmi_ctrl->kobj, KOBJ_OFFLINE);
+		switch_set_state(&hdmi_ctrl->sdev, 0);
+		DEV_INFO("%s: Hdmi state switch to %d\n", __func__,
+			hdmi_ctrl->sdev.state);
+	}
 
-	cable_detected = hpd_int_status & BIT(1);
-	mutex_lock(&hdmi_ctrl->mutex);
-	hdmi_ctrl->hpd_cable_chg_detected = true;
-	hdmi_ctrl->hpd_prev_state = cable_detected ? 0 : 1;
-	hdmi_ctrl->hpd_stable = 0;
-	mutex_unlock(&hdmi_ctrl->mutex);
-
-	mod_timer(&hdmi_ctrl->hpd_state_timer, jiffies + HZ/2);
-
-	DEV_DBG("%s: HPD<Ctrl=%04x, State=%04x>\n", __func__, hpd_int_ctrl,
-		hpd_int_status);
+	if (!completion_done(&hdmi_ctrl->hpd_done))
+		complete_all(&hdmi_ctrl->hpd_done);
 } /* hdmi_tx_hpd_int_work */
 
 static int hdmi_tx_check_capability(struct dss_io_data *io)
@@ -976,17 +955,6 @@
 	DSS_REG_W(io, HDMI_GEN_PKT_CTRL, packet_control);
 } /* hdmi_tx_set_spd_infoframe */
 
-/* todo: revisit when new HPD debouncing logic is avialble */
-static void hdmi_tx_hpd_state_timer(unsigned long data)
-{
-	struct hdmi_tx_ctrl *hdmi_ctrl = (struct hdmi_tx_ctrl *)data;
-
-	if (hdmi_ctrl)
-		queue_work(hdmi_ctrl->workq, &hdmi_ctrl->hpd_state_work);
-	else
-		DEV_ERR("%s: invalid input\n", __func__);
-} /* hdmi_tx_hpd_state_timer */
-
 static void hdmi_tx_set_mode(struct hdmi_tx_ctrl *hdmi_ctrl, u32 power_on)
 {
 	u32 reg_val = 0;
@@ -1133,6 +1101,8 @@
 	} else {
 		msm_dss_enable_clk(power_data->clk_config,
 			power_data->num_clk, 0);
+		msm_dss_clk_set_rate(power_data->clk_config,
+			power_data->num_clk);
 		msm_dss_enable_gpio(power_data->gpio_config,
 			power_data->num_gpio, 0);
 		msm_dss_enable_vreg(power_data->vreg_config,
@@ -1173,7 +1143,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 +1155,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 +1245,346 @@
 	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,
+		enabled ? "HDMI-AUDIO-ON: " : "HDMI-AUDIO-OFF: ", 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)
+{
+	u32 i, status, max_reads, timeout_us, timeout_sec = 15;
+	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;
+	}
+
+	/* Check if audio engine is turned off by QDSP or not */
+	/* send off notification after every 1 sec for 15 seconds */
+	for (i = 0; i < timeout_sec; i++) {
+		max_reads = 500;
+		timeout_us = 1000 * 2;
+
+		if (readl_poll_timeout_noirq((io->base + HDMI_AUDIO_CFG),
+			status, ((status & BIT(0)) == 0),
+			max_reads, timeout_us)) {
+
+			DEV_ERR("%s: audio still on after %d sec. try again\n",
+				__func__, i+1);
+
+			switch_set_state(&hdmi_ctrl->audio_sdev, 0);
+			continue;
+		}
+		break;
+	}
+	if (i == timeout_sec)
+		DEV_ERR("%s: Error: cannot turn off audio engine\n", __func__);
+
+	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 +1600,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,14 +1607,25 @@
 	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);
 
-	/* Set IRQ for HPD */
-	DSS_REG_W(io, HDMI_HPD_INT_CTRL, 4 | (hdmi_ctrl->hpd_state ? 0 : 2));
-
 	/* todo: HDCP/CEC */
 
 	DEV_INFO("%s: HDMI Core: Initialized\n", __func__);
@@ -1316,21 +1633,107 @@
 	return rc;
 } /* hdmi_tx_start */
 
+static void hdmi_tx_hpd_polarity_setup(struct hdmi_tx_ctrl *hdmi_ctrl,
+	bool polarity)
+{
+	struct dss_io_data *io = NULL;
+	u32 cable_sense;
+
+	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 is not initialized\n", __func__);
+		return;
+	}
+
+	if (polarity)
+		DSS_REG_W(io, HDMI_HPD_INT_CTRL, BIT(2) | BIT(1));
+	else
+		DSS_REG_W(io, HDMI_HPD_INT_CTRL, BIT(2));
+
+	cable_sense = (DSS_REG_R(io, HDMI_HPD_INT_STATUS) & BIT(1)) >> 1;
+	DEV_DBG("%s: listen = %s, sense = %s\n", __func__,
+		polarity ? "connect" : "disconnect",
+		cable_sense ? "connect" : "disconnect");
+
+	if (cable_sense == polarity) {
+		u32 reg_val = DSS_REG_R(io, HDMI_HPD_CTRL);
+
+		/* Toggle HPD circuit to trigger HPD sense */
+		DSS_REG_W(io, HDMI_HPD_CTRL, reg_val & ~BIT(28));
+		DSS_REG_W(io, HDMI_HPD_CTRL, reg_val | BIT(28));
+	}
+} /* hdmi_tx_hpd_polarity_setup */
+
+static void hdmi_tx_power_off_work(struct work_struct *work)
+{
+	struct hdmi_tx_ctrl *hdmi_ctrl = NULL;
+	struct dss_io_data *io = NULL;
+
+	hdmi_ctrl = container_of(work, struct hdmi_tx_ctrl, power_off_work);
+	if (!hdmi_ctrl) {
+		DEV_DBG("%s: invalid input\n", __func__);
+		return;
+	}
+
+	io = &hdmi_ctrl->pdata.io[HDMI_TX_CORE_IO];
+	if (!io->base) {
+		DEV_ERR("%s: Core io is not initialized\n", __func__);
+		return;
+	}
+
+	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);
+
+	/*
+	 * this is needed to avoid pll lock failure due to
+	 * clk framework's rate caching.
+	 */
+	hdmi_ctrl->pdata.power_data[HDMI_TX_CORE_PM].clk_config[0].rate = 0;
+
+	hdmi_tx_core_off(hdmi_ctrl);
+
+	mutex_lock(&hdmi_ctrl->mutex);
+	hdmi_ctrl->panel_power_on = false;
+	mutex_unlock(&hdmi_ctrl->mutex);
+
+	if (hdmi_ctrl->hpd_off_pending) {
+		hdmi_tx_hpd_off(hdmi_ctrl);
+		hdmi_ctrl->hpd_off_pending = false;
+	} else {
+		hdmi_tx_hpd_polarity_setup(hdmi_ctrl, HPD_CONNECT_POLARITY);
+	}
+
+	DEV_INFO("%s: HDMI Core: OFF\n", __func__);
+} /* hdmi_tx_power_off_work */
+
 static int hdmi_tx_power_off(struct mdss_panel_data *panel_data)
 {
 	struct hdmi_tx_ctrl *hdmi_ctrl =
 		hdmi_tx_get_drvdata_from_panel_data(panel_data);
 
-	if (!hdmi_ctrl) {
+	if (!hdmi_ctrl || !hdmi_ctrl->panel_power_on) {
 		DEV_ERR("%s: invalid input\n", __func__);
 		return -EINVAL;
 	}
 
-	DEV_INFO("%s: power: OFF (audio off, Reset Core)\n", __func__);
-	/* todo: Audio */
-	hdmi_tx_powerdown_phy(hdmi_ctrl);
-	hdmi_ctrl->panel_power_on = false;
-	hdmi_tx_core_off(hdmi_ctrl);
+	/*
+	 * Queue work item to handle power down sequence.
+	 * This is needed since we need to wait for the audio engine
+	 * to shutdown first before we shutdown the HDMI core.
+	 */
+	DEV_DBG("%s: Queuing work to power off HDMI core\n", __func__);
+	queue_work(hdmi_ctrl->workq, &hdmi_ctrl->power_off_work);
 
 	return 0;
 } /* hdmi_tx_power_off */
@@ -1352,6 +1755,15 @@
 		return -EINVAL;
 	}
 
+	if (!hdmi_ctrl->hpd_initialized) {
+		DEV_ERR("%s: HDMI on is not possible w/o cable detection.\n",
+			__func__);
+		return -EPERM;
+	}
+
+	/* If a power down is already underway, wait for it to finish */
+	flush_work_sync(&hdmi_ctrl->power_off_work);
+
 	DEV_INFO("power: ON (%dx%d %ld)\n", hdmi_ctrl->xres, hdmi_ctrl->yres,
 		hdmi_ctrl->pixel_clk);
 
@@ -1370,7 +1782,6 @@
 	mutex_lock(&hdmi_ctrl->mutex);
 	hdmi_ctrl->panel_power_on = true;
 
-	/* todo: check hdmi_tx_is_controller_on when hpd is on */
 	if (hdmi_ctrl->hpd_state) {
 		DEV_DBG("%s: Turning HDMI on\n", __func__);
 		mutex_unlock(&hdmi_ctrl->mutex);
@@ -1392,6 +1803,8 @@
 		hdmi_tx_is_controller_on(hdmi_ctrl) ? "ON" : "OFF" ,
 		hdmi_tx_is_dvi_mode(hdmi_ctrl) ? "ON" : "OFF");
 
+	hdmi_tx_hpd_polarity_setup(hdmi_ctrl, HPD_DISCONNECT_POLARITY);
+
 	return 0;
 } /* hdmi_tx_power_on */
 
@@ -1409,8 +1822,6 @@
 		return;
 	}
 
-	DEV_DBG("%s: (timer, 5V, IRQ off)\n", __func__);
-	del_timer_sync(&hdmi_ctrl->hpd_state_timer);
 	mdss_disable_irq(&hdmi_tx_hw);
 
 	hdmi_tx_set_mode(hdmi_ctrl, false);
@@ -1458,28 +1869,18 @@
 
 		DSS_REG_W(io, HDMI_USEC_REFTIMER, 0x0001001B);
 
-		/* set timeout to 4.1ms (max) for hardware debounce */
-		reg_val = DSS_REG_R(io, HDMI_HPD_CTRL) | 0x1FFF;
-
-		/* Toggle HPD circuit to trigger HPD sense */
-		DSS_REG_W(io, HDMI_HPD_CTRL,
-			~(1 << 28) & reg_val);
-		DSS_REG_W(io, HDMI_HPD_CTRL, (1 << 28) | reg_val);
+		mdss_enable_irq(&hdmi_tx_hw);
 
 		hdmi_ctrl->hpd_initialized = true;
 
-		/* Check HPD State */
-		mdss_enable_irq(&hdmi_tx_hw);
-	}
+		/* set timeout to 4.1ms (max) for hardware debounce */
+		reg_val = DSS_REG_R(io, HDMI_HPD_CTRL) | 0x1FFF;
 
-	/* Set HPD state machine: ensure at least 2 readouts */
-	mutex_lock(&hdmi_ctrl->mutex);
-	hdmi_ctrl->hpd_stable = 0;
-	hdmi_ctrl->hpd_prev_state = true;
-	hdmi_ctrl->hpd_state = false;
-	hdmi_ctrl->hpd_cable_chg_detected = true;
-	mutex_unlock(&hdmi_ctrl->mutex);
-	mod_timer(&hdmi_ctrl->hpd_state_timer, jiffies + HZ/2);
+		/* Turn on HPD HW circuit */
+		DSS_REG_W(io, HDMI_HPD_CTRL, reg_val | BIT(28));
+
+		hdmi_tx_hpd_polarity_setup(hdmi_ctrl, HPD_CONNECT_POLARITY);
+	}
 
 	return rc;
 } /* hdmi_tx_hpd_on */
@@ -1497,10 +1898,20 @@
 	if (on) {
 		rc = hdmi_tx_hpd_on(hdmi_ctrl);
 	} else {
-		hdmi_tx_hpd_off(hdmi_ctrl);
-		switch_set_state(&hdmi_ctrl->sdev, 0);
-		DEV_INFO("%s: Hdmi state switch to %d\n", __func__,
-			hdmi_ctrl->sdev.state);
+		/* If power down is already underway, wait for it to finish */
+		flush_work_sync(&hdmi_ctrl->power_off_work);
+
+		if (!hdmi_ctrl->panel_power_on) {
+			hdmi_tx_hpd_off(hdmi_ctrl);
+		} else {
+			hdmi_ctrl->hpd_off_pending = true;
+
+			switch_set_state(&hdmi_ctrl->sdev, 0);
+			DEV_DBG("%s: Hdmi state switch to %d\n", __func__,
+				hdmi_ctrl->sdev.state);
+			DEV_DBG("HDMI HPD: sent fake OFFLINE event\n");
+			kobject_uevent(hdmi_ctrl->kobj, KOBJ_OFFLINE);
+		}
 	}
 
 	return rc;
@@ -1525,10 +1936,10 @@
 
 	if (DSS_REG_R(io, HDMI_HPD_INT_STATUS) & BIT(0)) {
 		/*
-		 * Turn off HPD irq and clear all interrupts,
-		 * worker will turn IRQ back on
+		 * Ack the current hpd interrupt and stop listening to
+		 * new hpd interrupt.
 		 */
-		DSS_REG_W(io, HDMI_HPD_INT_CTRL, ~BIT(2) | BIT(0));
+		DSS_REG_W(io, HDMI_HPD_INT_CTRL, BIT(0));
 		queue_work(hdmi_ctrl->workq, &hdmi_ctrl->hpd_int_work);
 	}
 
@@ -1548,8 +1959,8 @@
 	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)
 		destroy_workqueue(hdmi_ctrl->workq);
 	mutex_destroy(&hdmi_ctrl->mutex);
@@ -1583,29 +1994,45 @@
 	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;
 	}
 
 	hdmi_ctrl->ddc_ctrl.io = &pdata->io[HDMI_TX_CORE_IO];
 	init_completion(&hdmi_ctrl->ddc_ctrl.ddc_sw_done);
 
-	INIT_WORK(&hdmi_ctrl->hpd_state_work, hdmi_tx_hpd_state_work);
+	hdmi_ctrl->panel_power_on = false;
+	hdmi_ctrl->panel_suspend = false;
+
+	hdmi_ctrl->hpd_state = false;
+	hdmi_ctrl->hpd_initialized = false;
+	hdmi_ctrl->hpd_off_pending = false;
+	init_completion(&hdmi_ctrl->hpd_done);
 	INIT_WORK(&hdmi_ctrl->hpd_int_work, hdmi_tx_hpd_int_work);
-	init_timer(&hdmi_ctrl->hpd_state_timer);
-	hdmi_ctrl->hpd_state_timer.function = hdmi_tx_hpd_state_timer;
-	hdmi_ctrl->hpd_state_timer.data = (u32)hdmi_ctrl;
-	hdmi_ctrl->hpd_state_timer.expires = 0xffffffffL;
+
+	INIT_WORK(&hdmi_ctrl->power_off_work, hdmi_tx_power_off_work);
+
+	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__);
-		goto fail_switch_dev;
+		rc = -ENODEV;
+		goto fail_create_workq;
+	}
+
+	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_switch_dev:
-	del_timer_sync(&hdmi_ctrl->hpd_state_timer);
+fail_audio_switch_dev:
+	switch_dev_unregister(&hdmi_ctrl->sdev);
 fail_create_workq:
 	if (hdmi_ctrl->workq)
 		destroy_workqueue(hdmi_ctrl->workq);
@@ -1614,6 +2041,101 @@
 	return rc;
 } /* hdmi_tx_dev_init */
 
+static int hdmi_tx_panel_event_handler(struct mdss_panel_data *panel_data,
+	int event, void *arg)
+{
+	int rc = 0;
+	struct hdmi_tx_ctrl *hdmi_ctrl =
+		hdmi_tx_get_drvdata_from_panel_data(panel_data);
+
+	if (!hdmi_ctrl) {
+		DEV_ERR("%s: invalid input\n", __func__);
+		return -EINVAL;
+	}
+
+	DEV_DBG("%s: event = %d suspend=%d, hpd_feature=%d\n", __func__,
+		event, hdmi_ctrl->panel_suspend, hdmi_ctrl->hpd_feature_on);
+
+	switch (event) {
+	case MDSS_EVENT_RESUME:
+		if (hdmi_ctrl->hpd_feature_on) {
+			INIT_COMPLETION(hdmi_ctrl->hpd_done);
+
+			rc = hdmi_tx_hpd_on(hdmi_ctrl);
+			if (rc)
+				DEV_ERR("%s: hdmi_tx_hpd_on failed. rc=%d\n",
+					__func__, rc);
+		}
+		break;
+
+	case MDSS_EVENT_RESET:
+		if (hdmi_ctrl->panel_suspend) {
+			u32 timeout;
+			hdmi_ctrl->panel_suspend = false;
+
+			timeout = wait_for_completion_interruptible_timeout(
+				&hdmi_ctrl->hpd_done, HZ/10);
+			if (!timeout & !hdmi_ctrl->hpd_state) {
+				DEV_INFO("%s: cable removed during suspend\n",
+					__func__);
+
+				kobject_uevent(hdmi_ctrl->kobj, KOBJ_OFFLINE);
+				switch_set_state(&hdmi_ctrl->sdev, 0);
+
+				rc = -EPERM;
+			} else {
+				DEV_DBG("%s: cable present after resume\n",
+					__func__);
+			}
+		}
+		break;
+
+	case MDSS_EVENT_UNBLANK:
+		rc = hdmi_tx_power_on(panel_data);
+		if (rc)
+			DEV_ERR("%s: hdmi_tx_power_on failed. rc=%d\n",
+				__func__, rc);
+		break;
+
+	case MDSS_EVENT_TIMEGEN_ON:
+		break;
+
+	case MDSS_EVENT_SUSPEND:
+		if (!hdmi_ctrl->panel_power_on) {
+			if (hdmi_ctrl->hpd_feature_on)
+				hdmi_tx_hpd_off(hdmi_ctrl);
+			else
+				DEV_ERR("%s: invalid state\n", __func__);
+
+			hdmi_ctrl->panel_suspend = false;
+		} else {
+			hdmi_ctrl->hpd_off_pending = true;
+			hdmi_ctrl->panel_suspend = true;
+		}
+		break;
+
+	case MDSS_EVENT_BLANK:
+		if (hdmi_ctrl->panel_power_on) {
+			rc = hdmi_tx_power_off(panel_data);
+			if (rc)
+				DEV_ERR("%s: hdmi_tx_power_off failed.rc=%d\n",
+					__func__, rc);
+
+		} else {
+			DEV_DBG("%s: hdmi is already powered off\n", __func__);
+		}
+		break;
+
+	case MDSS_EVENT_TIMEGEN_OFF:
+		/* If a power off is already underway, wait for it to finish */
+		if (hdmi_ctrl->panel_suspend)
+			flush_work_sync(&hdmi_ctrl->power_off_work);
+		break;
+	}
+
+	return rc;
+} /* hdmi_tx_panel_event_handler */
+
 static int hdmi_tx_register_panel(struct hdmi_tx_ctrl *hdmi_ctrl)
 {
 	int rc = 0;
@@ -1623,8 +2145,7 @@
 		return -EINVAL;
 	}
 
-	hdmi_ctrl->panel_data.on = hdmi_tx_power_on;
-	hdmi_ctrl->panel_data.off = hdmi_tx_power_off;
+	hdmi_ctrl->panel_data.event_handler = hdmi_tx_panel_event_handler;
 
 	hdmi_ctrl->video_resolution = DEFAULT_VIDEO_RESOLUTION;
 	rc = hdmi_tx_init_panel_info(hdmi_ctrl->video_resolution,
@@ -2027,29 +2548,32 @@
 static int hdmi_tx_get_dt_gpio_data(struct device *dev,
 	struct dss_module_power *mp, u32 module_type)
 {
-	int i, j, rc = 0;
-	int dt_gpio_total = 0, mod_gpio_total = 0;
-	u32 ndx_mask = 0;
-	const char *mod_name = NULL;
+	int i, j;
+	int mp_gpio_cnt = 0, gpio_list_size = 0;
+	struct dss_gpio *gpio_list = NULL;
 	struct device_node *of_node = NULL;
-	char prop_name[32];
-	snprintf(prop_name, 32, "%s-%s", COMPATIBLE_NAME, "gpio-names");
+
+	DEV_DBG("%s: module: '%s'\n", __func__, hdmi_tx_pm_name(module_type));
 
 	if (!dev || !mp) {
 		DEV_ERR("%s: invalid input\n", __func__);
-		rc = -EINVAL;
-		goto error;
+		return -EINVAL;
 	}
 
+	of_node = dev->of_node;
+
 	switch (module_type) {
 	case HDMI_TX_HPD_PM:
-		mod_name = "hpd";
+		gpio_list_size = ARRAY_SIZE(hpd_gpio_config);
+		gpio_list = hpd_gpio_config;
 		break;
 	case HDMI_TX_CORE_PM:
-		mod_name = "core";
+		gpio_list_size = ARRAY_SIZE(core_gpio_config);
+		gpio_list = core_gpio_config;
 		break;
 	case HDMI_TX_CEC_PM:
-		mod_name = "cec";
+		gpio_list_size = ARRAY_SIZE(cec_gpio_config);
+		gpio_list = cec_gpio_config;
 		break;
 	default:
 		DEV_ERR("%s: invalid module type=%d\n", __func__,
@@ -2057,90 +2581,49 @@
 		return -EINVAL;
 	}
 
-	DEV_DBG("%s: module: '%s'\n", __func__, hdmi_tx_pm_name(module_type));
+	for (i = 0; i < gpio_list_size; i++)
+		if (of_find_property(of_node, gpio_list[i].gpio_name, NULL))
+			mp_gpio_cnt++;
 
-	of_node = dev->of_node;
-
-	dt_gpio_total = of_gpio_count(of_node);
-	if (dt_gpio_total < 0) {
-		DEV_ERR("%s: gpio not found. rc=%d\n", __func__,
-			dt_gpio_total);
-		rc = dt_gpio_total;
-		goto error;
-	}
-
-	/* count how many gpio for particular hdmi module */
-	for (i = 0; i < dt_gpio_total; i++) {
-		const char *st = NULL;
-
-		rc = of_property_read_string_index(of_node,
-			prop_name, i, &st);
-		if (rc) {
-			DEV_ERR("%s: error reading name. i=%d, rc=%d\n",
-				__func__, i, rc);
-			goto error;
-		}
-
-		if (strnstr(st, mod_name, strlen(st))) {
-			ndx_mask |= BIT(i);
-			mod_gpio_total++;
-		}
-	}
-
-	if (mod_gpio_total > 0) {
-		mp->num_gpio = mod_gpio_total;
-		mp->gpio_config = devm_kzalloc(dev, sizeof(struct dss_gpio) *
-			mod_gpio_total, GFP_KERNEL);
-		if (!mp->gpio_config) {
-			DEV_ERR("%s: can't alloc '%s' gpio mem\n", __func__,
-				hdmi_tx_pm_name(module_type));
-			goto error;
-		}
-	} else {
+	if (!mp_gpio_cnt) {
 		DEV_DBG("%s: no gpio\n", __func__);
 		return 0;
 	}
 
+	DEV_DBG("%s: mp_gpio_cnt = %d\n", __func__, mp_gpio_cnt);
+	mp->num_gpio = mp_gpio_cnt;
 
-	for (i = 0, j = 0; (i < dt_gpio_total) && (j < mod_gpio_total); i++) {
-		const char *st = NULL;
+	mp->gpio_config = devm_kzalloc(dev, sizeof(struct dss_gpio) *
+		mp_gpio_cnt, GFP_KERNEL);
+	if (!mp->gpio_config) {
+		DEV_ERR("%s: can't alloc '%s' gpio mem\n", __func__,
+			hdmi_tx_pm_name(module_type));
 
-		if (!(ndx_mask & BIT(0))) {
-			ndx_mask >>= 1;
+		mp->num_gpio = 0;
+		return -ENOMEM;
+	}
+
+	for (i = 0, j = 0; i < gpio_list_size; i++) {
+		int gpio = of_get_named_gpio(of_node,
+			gpio_list[i].gpio_name, 0);
+		if (gpio < 0) {
+			DEV_DBG("%s: no gpio named %s\n", __func__,
+				gpio_list[i].gpio_name);
 			continue;
 		}
+		memcpy(&mp->gpio_config[j], &gpio_list[i],
+			sizeof(struct dss_gpio));
 
-		/* gpio-name */
-		rc = of_property_read_string_index(of_node,
-			prop_name, i, &st);
-		if (rc) {
-			DEV_ERR("%s: error reading name. i=%d, rc=%d\n",
-				__func__, i, rc);
-			goto error;
-		}
-		snprintf(mp->gpio_config[j].gpio_name, 32, "%s", st);
+		mp->gpio_config[j].gpio = (unsigned)gpio;
 
-		/* gpio-number */
-		mp->gpio_config[j].gpio = of_get_gpio(of_node, i);
-
-		DEV_DBG("%s: gpio num=%d, name=%s\n", __func__,
-			mp->gpio_config[j].gpio,
-			mp->gpio_config[j].gpio_name);
-
-		ndx_mask >>= 1;
+		DEV_DBG("%s: gpio num=%d, name=%s, value=%d\n",
+			__func__, mp->gpio_config[j].gpio,
+			mp->gpio_config[j].gpio_name,
+			mp->gpio_config[j].value);
 		j++;
 	}
 
-	return rc;
-
-error:
-	if (mp->gpio_config) {
-		devm_kfree(dev, mp->gpio_config);
-		mp->gpio_config = NULL;
-	}
-	mp->num_gpio = 0;
-
-	return rc;
+	return 0;
 } /* hdmi_tx_get_dt_gpio_data */
 
 static void hdmi_tx_put_dt_data(struct device *dev,
diff --git a/drivers/video/msm/mdss/mdss_hdmi_tx.h b/drivers/video/msm/mdss/mdss_hdmi_tx.h
index 94e0fda..2d431b7 100644
--- a/drivers/video/msm/mdss/mdss_hdmi_tx.h
+++ b/drivers/video/msm/mdss/mdss_hdmi_tx.h
@@ -41,23 +41,26 @@
 	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;
 	u32 panel_power_on;
+	u32 panel_suspend;
 
-	u32 hpd_initialized;
-	int hpd_stable;
-	u32 hpd_prev_state;
-	u32 hpd_cable_chg_detected;
 	u32 hpd_state;
+	u32 hpd_off_pending;
 	u32 hpd_feature_on;
-	struct work_struct hpd_state_work;
+	u32 hpd_initialized;
+	struct completion hpd_done;
 	struct work_struct hpd_int_work;
-	struct timer_list hpd_state_timer;
+
+	struct work_struct power_off_work;
 
 	unsigned long pixel_clk;
 	u32 xres;
diff --git a/drivers/video/msm/mdss/mdss_io_util.c b/drivers/video/msm/mdss/mdss_io_util.c
index 5778525..2bf2d74 100644
--- a/drivers/video/msm/mdss/mdss_io_util.c
+++ b/drivers/video/msm/mdss/mdss_io_util.c
@@ -15,6 +15,7 @@
 #include <linux/io.h>
 #include "mdss_io_util.h"
 
+#define MAX_I2C_CMDS  16
 void dss_reg_w(struct dss_io_data *io, u32 offset, u32 value, u32 debug)
 {
 	u32 in_val;
@@ -136,7 +137,7 @@
 			curr_vreg = &in_vreg[i];
 			curr_vreg->vreg = regulator_get(dev,
 				curr_vreg->vreg_name);
-			rc = IS_ERR(curr_vreg->vreg);
+			rc = PTR_RET(curr_vreg->vreg);
 			if (rc) {
 				DEV_ERR("%pS->%s: %s get failed. rc=%d\n",
 					 __builtin_return_address(0), __func__,
@@ -215,7 +216,7 @@
 	int i = 0, rc = 0;
 	if (enable) {
 		for (i = 0; i < num_vreg; i++) {
-			rc = IS_ERR(in_vreg[i].vreg);
+			rc = PTR_RET(in_vreg[i].vreg);
 			if (rc) {
 				DEV_ERR("%pS->%s: %s regulator error. rc=%d\n",
 					__builtin_return_address(0), __func__,
@@ -248,6 +249,10 @@
 	int i = 0, rc = 0;
 	if (enable) {
 		for (i = 0; i < num_gpio; i++) {
+			DEV_DBG("%pS->%s: %s enable\n",
+				__builtin_return_address(0), __func__,
+				in_gpio[i].gpio_name);
+
 			rc = gpio_request(in_gpio[i].gpio,
 				in_gpio[i].gpio_name);
 			if (rc < 0) {
@@ -256,10 +261,16 @@
 					in_gpio[i].gpio_name);
 				goto disable_gpio;
 			}
+			gpio_set_value(in_gpio[i].gpio, in_gpio[i].value);
 		}
 	} else {
-		for (i = num_gpio-1; i >= 0; i--)
+		for (i = num_gpio-1; i >= 0; i--) {
+			DEV_DBG("%pS->%s: %s disable\n",
+				__builtin_return_address(0), __func__,
+				in_gpio[i].gpio_name);
+
 			gpio_free(in_gpio[i].gpio);
+		}
 	}
 	return rc;
 
@@ -286,7 +297,7 @@
 
 	for (i = 0; i < num_clk; i++) {
 		clk_arry[i].clk = clk_get(dev, clk_arry[i].clk_name);
-		rc = IS_ERR(clk_arry[i].clk);
+		rc = PTR_RET(clk_arry[i].clk);
 		if (rc) {
 			DEV_ERR("%pS->%s: '%s' get failed. rc=%d\n",
 				__builtin_return_address(0), __func__,
@@ -382,3 +393,59 @@
 
 	return rc;
 } /* msm_dss_enable_clk */
+
+
+int mdss_i2c_byte_read(struct i2c_client *client, uint8_t slave_addr,
+			uint8_t reg_offset, uint8_t *read_buf)
+{
+	struct i2c_msg msgs[2];
+	int ret = -1;
+
+	pr_debug("%s: reading from slave_addr=[%x] and offset=[%x]\n",
+		 __func__, slave_addr, reg_offset);
+
+	msgs[0].addr = slave_addr >> 1;
+	msgs[0].flags = 0;
+	msgs[0].buf = &reg_offset;
+	msgs[0].len = 1;
+
+	msgs[1].addr = slave_addr >> 1;
+	msgs[1].flags = I2C_M_RD;
+	msgs[1].buf = read_buf;
+	msgs[1].len = 1;
+
+	ret = i2c_transfer(client->adapter, msgs, 2);
+	if (ret < 1) {
+		pr_err("%s: I2C READ FAILED=[%d]\n", __func__, ret);
+		return -EACCES;
+	}
+	pr_debug("%s: i2c buf is [%x]\n", __func__, *read_buf);
+	return 0;
+}
+
+int mdss_i2c_byte_write(struct i2c_client *client, uint8_t slave_addr,
+			uint8_t reg_offset, uint8_t *value)
+{
+	struct i2c_msg msgs[1];
+	uint8_t data[2];
+	int status = -EACCES;
+
+	pr_debug("%s: writing from slave_addr=[%x] and offset=[%x]\n",
+		 __func__, slave_addr, reg_offset);
+
+	data[0] = reg_offset;
+	data[1] = *value;
+
+	msgs[0].addr = slave_addr >> 1;
+	msgs[0].flags = 0;
+	msgs[0].len = 2;
+	msgs[0].buf = data;
+
+	status = i2c_transfer(client->adapter, msgs, 1);
+	if (status < 1) {
+		pr_err("I2C WRITE FAILED=[%d]\n", status);
+		return -EACCES;
+	}
+	pr_debug("%s: I2C write status=%x\n", __func__, status);
+	return status;
+}
diff --git a/drivers/video/msm/mdss/mdss_io_util.h b/drivers/video/msm/mdss/mdss_io_util.h
index 51e9e54..85826f7 100644
--- a/drivers/video/msm/mdss/mdss_io_util.h
+++ b/drivers/video/msm/mdss/mdss_io_util.h
@@ -16,6 +16,8 @@
 #include <linux/gpio.h>
 #include <linux/platform_device.h>
 #include <linux/regulator/consumer.h>
+#include <linux/i2c.h>
+#include <linux/types.h>
 
 #ifdef DEBUG
 #define DEV_DBG(fmt, args...)   pr_err(fmt, ##args)
@@ -56,6 +58,7 @@
 
 struct dss_gpio {
 	unsigned gpio;
+	unsigned value;
 	char gpio_name[32];
 };
 
@@ -97,4 +100,9 @@
 int msm_dss_clk_set_rate(struct dss_clk *clk_arry, int num_clk);
 int msm_dss_enable_clk(struct dss_clk *clk_arry, int num_clk, int enable);
 
+int mdss_i2c_byte_read(struct i2c_client *client, uint8_t slave_addr,
+		       uint8_t reg_offset, uint8_t *read_buf);
+int mdss_i2c_byte_write(struct i2c_client *client, uint8_t slave_addr,
+			uint8_t reg_offset, uint8_t *value);
+
 #endif /* __MDSS_IO_UTIL_H__ */
diff --git a/drivers/video/msm/mdss/mdss_mdp.c b/drivers/video/msm/mdss/mdss_mdp.c
index 0f6cfe9..bcb3aee 100644
--- a/drivers/video/msm/mdss/mdss_mdp.c
+++ b/drivers/video/msm/mdss/mdss_mdp.c
@@ -95,28 +95,29 @@
 	.name = "mdss_mdp",
 };
 
-struct msm_iova_partition mdp_iommu_partitions[] = {
-	{
-		.start = SZ_128K,
-		.size = SZ_2G - SZ_128K,
+struct mdss_iommu_map_type mdss_iommu_map[MDSS_IOMMU_MAX_DOMAIN] = {
+	[MDSS_IOMMU_DOMAIN_UNSECURE] = {
+		.client_name = "mdp_ns",
+		.ctx_name = "mdp_0",
+		.partitions = {
+			{
+				.start = SZ_128K,
+				.size = SZ_1G - SZ_128K,
+			},
+		},
+		.npartitions = 1,
 	},
-};
-struct msm_iova_layout mdp_iommu_layout = {
-	.client_name = "mdss_mdp",
-	.partitions = mdp_iommu_partitions,
-	.npartitions = ARRAY_SIZE(mdp_iommu_partitions),
-};
-
-struct {
-	char *name;
-	struct device *ctx;
-} mdp_iommu_ctx[] = {
-	{
-		.name = "mdp_0",
+	[MDSS_IOMMU_DOMAIN_SECURE] = {
+		.client_name = "mdp_secure",
+		.ctx_name = "mdp_1",
+		.partitions = {
+			{
+				.start = SZ_1G,
+				.size = SZ_1G,
+			},
+		},
+		.npartitions = 1,
 	},
-	{
-		.name = "mdp_1",
-	}
 };
 
 struct mdss_hw mdss_mdp_hw = {
@@ -226,7 +227,7 @@
 		mdss_res->irq_mask &= ~ndx_bit;
 		if (mdss_res->irq_mask == 0) {
 			mdss_res->irq_ena = false;
-			disable_irq(mdss_res->irq);
+			disable_irq_nosync(mdss_res->irq);
 		}
 	}
 	spin_unlock_irqrestore(&mdss_lock, irq_flags);
@@ -292,7 +293,7 @@
 		msm_bus_scale_unregister_client(mdata->bus_hdl);
 }
 
-int mdss_mdp_bus_scale_set_quota(u32 ab_quota, u32 ib_quota)
+int mdss_mdp_bus_scale_set_quota(u64 ab_quota, u64 ib_quota)
 {
 	static int current_bus_idx;
 	int bus_idx;
@@ -310,6 +311,10 @@
 
 		bus_idx = (current_bus_idx % (num_cases - 1)) + 1;
 
+		/* aligning to avoid performing updates for small changes */
+		ab_quota = ALIGN(ab_quota, SZ_64M);
+		ib_quota = ALIGN(ib_quota, SZ_64M);
+
 		vect = mdp_bus_scale_table.usecase[current_bus_idx].vectors;
 		if ((ab_quota == vect->ab) && (ib_quota == vect->ib)) {
 			pr_debug("skip bus scaling, no change in vectors\n");
@@ -320,7 +325,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;
@@ -666,85 +671,103 @@
 	return 0;
 }
 
-int mdss_iommu_attach(void)
+int mdss_iommu_attach(struct mdss_data_type *mdata)
 {
 	struct iommu_domain *domain;
-	int i, domain_idx;
+	struct mdss_iommu_map_type *iomap;
+	int i;
 
-	if (mdss_res->iommu_attached) {
+	if (mdata->iommu_attached) {
 		pr_warn("mdp iommu already attached\n");
 		return 0;
 	}
 
-	domain_idx = mdss_get_iommu_domain();
-	domain = msm_get_iommu_domain(domain_idx);
-	if (!domain) {
-		pr_err("unable to get iommu domain(%d)\n", domain_idx);
-		return -EINVAL;
+	for (i = 0; i < MDSS_IOMMU_MAX_DOMAIN; i++) {
+		iomap = mdata->iommu_map + i;
+
+		domain = msm_get_iommu_domain(iomap->domain_idx);
+		if (!domain) {
+			WARN(1, "could not attach iommu client %s to ctx %s\n",
+				iomap->client_name, iomap->ctx_name);
+			continue;
+		}
+		iommu_attach_device(domain, iomap->ctx);
 	}
 
-	for (i = 0; i < ARRAY_SIZE(mdp_iommu_ctx); i++) {
-		if (iommu_attach_device(domain, mdp_iommu_ctx[i].ctx)) {
-			WARN(1, "could not attach iommu domain %d to ctx %s\n",
-				domain_idx, mdp_iommu_ctx[i].name);
-			return -EINVAL;
-		}
-	}
-	mdss_res->iommu_attached = true;
+	mdata->iommu_attached = true;
 
 	return 0;
 }
 
-int mdss_iommu_dettach(void)
+int mdss_iommu_dettach(struct mdss_data_type *mdata)
 {
 	struct iommu_domain *domain;
-	int i, domain_idx;
+	struct mdss_iommu_map_type *iomap;
+	int i;
 
-	if (!mdss_res->iommu_attached) {
+	if (!mdata->iommu_attached) {
 		pr_warn("mdp iommu already dettached\n");
 		return 0;
 	}
 
-	domain_idx = mdss_get_iommu_domain();
-	domain = msm_get_iommu_domain(domain_idx);
-	if (!domain) {
-		pr_err("unable to get iommu domain(%d)\n", domain_idx);
-		return -EINVAL;
+	for (i = 0; i < MDSS_IOMMU_MAX_DOMAIN; i++) {
+		iomap = mdata->iommu_map + i;
+
+		domain = msm_get_iommu_domain(iomap->domain_idx);
+		if (!domain) {
+			pr_err("unable to get iommu domain(%d)\n",
+				iomap->domain_idx);
+			continue;
+		}
+		iommu_detach_device(domain, iomap->ctx);
 	}
 
-	for (i = 0; i < ARRAY_SIZE(mdp_iommu_ctx); i++)
-		iommu_detach_device(domain, mdp_iommu_ctx[i].ctx);
-	mdss_res->iommu_attached = false;
+	mdata->iommu_attached = false;
 
 	return 0;
 }
 
-int mdss_iommu_init(void)
+int mdss_iommu_init(struct mdss_data_type *mdata)
 {
+	struct msm_iova_layout layout;
 	struct iommu_domain *domain;
-	int domain_idx, i;
+	struct mdss_iommu_map_type *iomap;
+	int i;
 
-	domain_idx = msm_register_domain(&mdp_iommu_layout);
-	if (IS_ERR_VALUE(domain_idx))
-		return -EINVAL;
-
-	domain = msm_get_iommu_domain(domain_idx);
-	if (!domain) {
-		pr_err("unable to get iommu domain(%d)\n", domain_idx);
-		return -EINVAL;
+	if (mdata->iommu_map) {
+		pr_warn("iommu already initialized\n");
+		return 0;
 	}
 
-	iommu_set_fault_handler(domain, mdss_iommu_fault_handler);
+	for (i = 0; i < MDSS_IOMMU_MAX_DOMAIN; i++) {
+		iomap = &mdss_iommu_map[i];
 
-	for (i = 0; i < ARRAY_SIZE(mdp_iommu_ctx); i++) {
-		mdp_iommu_ctx[i].ctx = msm_iommu_get_ctx(mdp_iommu_ctx[i].name);
-		if (!mdp_iommu_ctx[i].ctx) {
+		layout.client_name = iomap->client_name;
+		layout.partitions = iomap->partitions;
+		layout.npartitions = iomap->npartitions;
+		layout.is_secure = (i == MDSS_IOMMU_DOMAIN_SECURE);
+
+		iomap->domain_idx = msm_register_domain(&layout);
+		if (IS_ERR_VALUE(iomap->domain_idx))
+			return -EINVAL;
+
+		domain = msm_get_iommu_domain(iomap->domain_idx);
+		if (!domain) {
+			pr_err("unable to get iommu domain(%d)\n",
+				iomap->domain_idx);
+			return -EINVAL;
+		}
+		iommu_set_fault_handler(domain, mdss_iommu_fault_handler);
+
+		iomap->ctx = msm_iommu_get_ctx(iomap->ctx_name);
+		if (!iomap->ctx) {
 			pr_warn("unable to get iommu ctx(%s)\n",
-					mdp_iommu_ctx[i].name);
+				iomap->ctx_name);
 			return -EINVAL;
 		}
 	}
-	mdss_res->iommu_domain = domain_idx;
+
+	mdata->iommu_map = mdss_iommu_map;
 
 	return 0;
 }
@@ -811,9 +834,9 @@
 		mdata->iclient = NULL;
 	}
 
-	rc = mdss_iommu_init();
+	rc = mdss_iommu_init(mdata);
 	if (!IS_ERR_VALUE(rc))
-		mdss_iommu_attach();
+		mdss_iommu_attach(mdata);
 
 	rc = mdss_hw_init(mdata);
 
@@ -930,11 +953,11 @@
 	if (on && !mdata->fs_ena) {
 		pr_debug("Enable MDP FS\n");
 		regulator_enable(mdata->fs);
-		mdss_iommu_attach();
+		mdss_iommu_attach(mdata);
 		mdata->fs_ena = true;
 	} else if (!on && mdata->fs_ena) {
 		pr_debug("Disable MDP FS\n");
-		mdss_iommu_dettach();
+		mdss_iommu_dettach(mdata);
 		regulator_disable(mdata->fs);
 		mdata->fs_ena = false;
 	}
diff --git a/drivers/video/msm/mdss/mdss_mdp.h b/drivers/video/msm/mdss/mdss_mdp.h
index 33028cb..2e92591 100644
--- a/drivers/video/msm/mdss/mdss_mdp.h
+++ b/drivers/video/msm/mdss/mdss_mdp.h
@@ -229,6 +229,7 @@
 	u8 mixer_stage;
 	u8 is_fg;
 	u8 alpha;
+	u8 overfetch_disable;
 	u32 transp;
 
 	struct msm_fb_data_type *mfd;
@@ -276,20 +277,20 @@
 int mdss_mdp_set_intr_callback(u32 intr_type, u32 intf_num,
 			       void (*fnc_ptr)(void *), void *arg);
 
-int mdss_mdp_bus_scale_set_quota(u32 ab_quota, u32 ib_quota);
+int mdss_mdp_bus_scale_set_quota(u64 ab_quota, u64 ib_quota);
 void mdss_mdp_set_clk_rate(unsigned long min_clk_rate);
 unsigned long mdss_mdp_get_clk_rate(u32 clk_idx);
 int mdss_mdp_vsync_clk_enable(int enable);
 void mdss_mdp_clk_ctrl(int enable, int isr);
 
 int mdss_mdp_overlay_init(struct msm_fb_data_type *mfd);
-int mdss_mdp_overlay_release_all(struct msm_fb_data_type *mfd);
 int mdss_mdp_overlay_vsync_ctrl(struct msm_fb_data_type *mfd, int en);
 int mdss_mdp_video_start(struct mdss_mdp_ctl *ctl);
 int mdss_mdp_writeback_start(struct mdss_mdp_ctl *ctl);
 
 int mdss_mdp_ctl_on(struct msm_fb_data_type *mfd);
 int mdss_mdp_ctl_off(struct msm_fb_data_type *mfd);
+int mdss_mdp_ctl_intf_event(struct mdss_mdp_ctl *ctl, int event, void *arg);
 
 struct mdss_mdp_mixer *mdss_mdp_wb_mixer_alloc(int rotator);
 int mdss_mdp_wb_mixer_destroy(struct mdss_mdp_mixer *mixer);
diff --git a/drivers/video/msm/mdss/mdss_mdp_ctl.c b/drivers/video/msm/mdss/mdss_mdp_ctl.c
index 73b8c60..00f5874 100644
--- a/drivers/video/msm/mdss/mdss_mdp_ctl.c
+++ b/drivers/video/msm/mdss/mdss_mdp_ctl.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,8 +20,10 @@
 #include "mdss_fb.h"
 #include "mdss_mdp.h"
 
+/* truncate at 1k */
+#define MDSS_MDP_BUS_FACTOR_SHIFT 10
 /* 1.5 bus fudge factor */
-#define MDSS_MDP_BUS_FUDGE_FACTOR(val) ALIGN((((val) * 3) / 2), SZ_16M)
+#define MDSS_MDP_BUS_FUDGE_FACTOR(val) (((val) / 2) * 3)
 /* 1.25 clock fudge factor */
 #define MDSS_MDP_CLK_FUDGE_FACTOR(val) (((val) * 5) / 4)
 
@@ -44,7 +46,7 @@
 	struct mdss_mdp_ctl *ctl;
 	int cnum;
 	unsigned long clk_rate = 0;
-	u32 bus_ab_quota = 0, bus_ib_quota = 0;
+	u64 bus_ab_quota = 0, bus_ib_quota = 0;
 
 	if (!flags) {
 		pr_err("nothing to update\n");
@@ -63,7 +65,14 @@
 		}
 	}
 	if (flags & MDSS_MDP_PERF_UPDATE_BUS) {
+		bus_ab_quota = bus_ab_quota << MDSS_MDP_BUS_FACTOR_SHIFT;
 		bus_ib_quota = MDSS_MDP_BUS_FUDGE_FACTOR(bus_ib_quota);
+		bus_ib_quota <<= MDSS_MDP_BUS_FACTOR_SHIFT;
+
+		if ((bus_ib_quota == 0) && (clk_rate > 0)) {
+			/* allocate min bw for panel cmds if mdp is active */
+			bus_ib_quota = SZ_16M;
+		}
 		mdss_mdp_bus_scale_set_quota(bus_ab_quota, bus_ib_quota);
 	}
 	if (flags & MDSS_MDP_PERF_UPDATE_CLK) {
@@ -116,6 +125,7 @@
 		if (is_writeback) {
 			/* perf for bus writeback */
 			*bus_ab_quota = fps * mixer->width * mixer->height * 3;
+			*bus_ab_quota >>= MDSS_MDP_BUS_FACTOR_SHIFT;
 			*bus_ib_quota = *bus_ab_quota;
 		}
 	}
@@ -148,13 +158,13 @@
 		if (mixer->rotator_mode)
 			rate /= 4; /* block mode fetch at 4 pix/clk */
 
-		*bus_ab_quota += quota;
-		*bus_ib_quota += ib_quota;
-		if (rate > *clk_rate)
-			*clk_rate = rate;
-
 		pr_debug("mixer=%d pnum=%d clk_rate=%u bus ab=%u ib=%u\n",
 			 mixer->num, pipe->num, rate, quota, ib_quota);
+
+		*bus_ab_quota += quota >> MDSS_MDP_BUS_FACTOR_SHIFT;
+		*bus_ib_quota += ib_quota >> MDSS_MDP_BUS_FACTOR_SHIFT;
+		if (rate > *clk_rate)
+			*clk_rate = rate;
 	}
 
 	pr_debug("final mixer=%d clk_rate=%u bus ab=%u ib=%u\n", mixer->num,
@@ -469,15 +479,25 @@
 	if (ctl->intf_num == MDSS_MDP_NO_INTF) {
 		ctl->dst_format = mfd->panel_info.out_format;
 	} else {
+		struct mdp_dither_cfg_data dither = {
+			.block = mfd->index + MDP_LOGICAL_BLOCK_DISP_0,
+			.flags = MDP_PP_OPS_DISABLE,
+		};
+
 		switch (mfd->panel_info.bpp) {
 		case 18:
 			ctl->dst_format = MDSS_MDP_PANEL_FORMAT_RGB666;
+			dither.flags = MDP_PP_OPS_ENABLE | MDP_PP_OPS_WRITE;
+			dither.g_y_depth = 2;
+			dither.r_cr_depth = 2;
+			dither.b_cb_depth = 2;
 			break;
 		case 24:
 		default:
 			ctl->dst_format = MDSS_MDP_PANEL_FORMAT_RGB888;
 			break;
 		}
+		mdss_mdp_dither_config(&dither, NULL);
 	}
 
 	if (ctl->mixer_right) {
@@ -516,9 +536,28 @@
 	return 0;
 }
 
-int mdss_mdp_ctl_on(struct msm_fb_data_type *mfd)
+int mdss_mdp_ctl_intf_event(struct mdss_mdp_ctl *ctl, int event, void *arg)
 {
 	struct mdss_panel_data *pdata;
+	if (!ctl || !ctl->mfd)
+		return -ENODEV;
+
+	pdata = dev_get_platdata(&ctl->mfd->pdev->dev);
+	if (!pdata) {
+		pr_err("no panel connected\n");
+		return -ENODEV;
+	}
+
+	pr_debug("sending ctl=%d event=%d\n", ctl->num, event);
+
+	if (pdata->event_handler)
+		return pdata->event_handler(pdata, event, arg);
+
+	return 0;
+}
+
+int mdss_mdp_ctl_on(struct msm_fb_data_type *mfd)
+{
 	struct mdss_mdp_ctl *ctl;
 	struct mdss_mdp_mixer *mixer;
 	u32 outsize, temp, off;
@@ -530,12 +569,6 @@
 	if (mfd->key != MFD_KEY)
 		return -EINVAL;
 
-	pdata = dev_get_platdata(&mfd->pdev->dev);
-	if (!pdata) {
-		pr_err("no panel connected\n");
-		return -ENODEV;
-	}
-
 	if (mdss_mdp_ctl_init(mfd)) {
 		pr_err("unable to initialize ctl\n");
 		return -ENODEV;
@@ -553,6 +586,12 @@
 	ctl->power_on = true;
 
 	mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false);
+	ret = mdss_mdp_ctl_intf_event(ctl, MDSS_EVENT_RESET, NULL);
+	if (ret) {
+		pr_err("panel power on failed ctl=%d\n", ctl->num);
+		goto start_fail;
+	}
+
 	if (ctl->start_fnc)
 		ret = ctl->start_fnc(ctl);
 	else
@@ -592,23 +631,17 @@
 		mdss_mdp_ctl_write(ctl, MDSS_MDP_REG_CTL_PACK_3D, 0);
 	}
 
-	/* request bus bandwidth for panel commands */
-	ctl->clk_rate = MDP_CLK_DEFAULT_RATE;
-	ctl->bus_ib_quota = SZ_1M;
-	mdss_mdp_ctl_perf_commit(MDSS_MDP_PERF_UPDATE_ALL);
-
-	ret = pdata->on(pdata);
-
 start_fail:
 	mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false);
 	mutex_unlock(&ctl->lock);
+	if (ret)
+		mdss_mdp_ctl_destroy(mfd);
 
 	return ret;
 }
 
 int mdss_mdp_ctl_off(struct msm_fb_data_type *mfd)
 {
-	struct mdss_panel_data *pdata;
 	struct mdss_mdp_ctl *ctl;
 	int ret = 0;
 
@@ -623,12 +656,6 @@
 		return -ENODEV;
 	}
 
-	pdata = dev_get_platdata(&mfd->pdev->dev);
-	if (!pdata) {
-		pr_err("no panel connected\n");
-		return -ENODEV;
-	}
-
 	ctl = mfd->ctl;
 
 	if (!ctl->power_on) {
@@ -638,43 +665,33 @@
 
 	pr_debug("ctl_num=%d\n", mfd->ctl->num);
 
-	mdss_mdp_overlay_release_all(mfd);
-
-	/* request bus bandwidth for panel commands */
-	ctl->bus_ib_quota = SZ_1M;
-	mdss_mdp_ctl_perf_commit(MDSS_MDP_PERF_UPDATE_ALL);
-
 	mutex_lock(&ctl->lock);
-	ctl->power_on = false;
 
 	mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false);
 
-	if (pdata->intf_unprepare)
-		ret = pdata->intf_unprepare(pdata);
-
-	if (ret)
-		pr_err("%s: intf_unprepare failed\n", __func__);
-
 	if (ctl->stop_fnc)
 		ret = ctl->stop_fnc(ctl);
 	else
 		pr_warn("no stop func for ctl=%d\n", ctl->num);
 
-	if (ret)
+	if (ret) {
 		pr_warn("error powering off intf ctl=%d\n", ctl->num);
-
-	ret = pdata->off(pdata);
+	} else {
+		ctl->power_on = false;
+		ctl->play_cnt = 0;
+		ctl->clk_rate = 0;
+		mdss_mdp_ctl_perf_commit(MDSS_MDP_PERF_UPDATE_ALL);
+	}
 
 	mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false);
 
-	ctl->play_cnt = 0;
-
-	mdss_mdp_ctl_perf_commit(MDSS_MDP_PERF_UPDATE_ALL);
-
 	mutex_unlock(&ctl->lock);
 
-	if (!mfd->ref_cnt)
+	if (!ret && !mfd->ref_cnt) {
+		ret = mdss_mdp_ctl_intf_event(ctl, MDSS_EVENT_CLOSE, NULL);
+		WARN(ret, "unable to close intf %d\n", ctl->intf_num);
 		mdss_mdp_ctl_destroy(mfd);
+	}
 
 	return ret;
 }
@@ -682,7 +699,7 @@
 static int mdss_mdp_mixer_setup(struct mdss_mdp_ctl *ctl,
 				struct mdss_mdp_mixer *mixer)
 {
-	struct mdss_mdp_pipe *pipe, *bgpipe = NULL;
+	struct mdss_mdp_pipe *pipe;
 	u32 off, blend_op, blend_stage;
 	u32 mixercfg = 0, blend_color_out = 0, bgalpha = 0;
 	int stage;
@@ -692,26 +709,24 @@
 
 	pr_debug("setup mixer=%d\n", mixer->num);
 
-	for (stage = MDSS_MDP_STAGE_BASE; stage < MDSS_MDP_MAX_STAGE; stage++) {
+	pipe = mixer->stage_pipe[MDSS_MDP_STAGE_BASE];
+	if (pipe == NULL) {
+		mixercfg = MDSS_MDP_LM_BORDER_COLOR;
+	} else {
+		mixercfg = 1 << (3 * pipe->num);
+		if (pipe->src_fmt->alpha_enable)
+			bgalpha = 1;
+	}
+
+	for (stage = MDSS_MDP_STAGE_0; stage < MDSS_MDP_MAX_STAGE; stage++) {
 		pipe = mixer->stage_pipe[stage];
-		if (pipe == NULL) {
-			if (stage == MDSS_MDP_STAGE_BASE)
-				mixercfg |= MDSS_MDP_LM_BORDER_COLOR;
+		if (pipe == NULL)
 			continue;
-		}
 
 		if (stage != pipe->mixer_stage) {
 			mixer->stage_pipe[stage] = NULL;
 			continue;
 		}
-		mixercfg |= stage << (3 * pipe->num);
-
-		if (stage == MDSS_MDP_STAGE_BASE) {
-			bgpipe = pipe;
-			if (pipe->src_fmt->alpha_enable)
-				bgalpha = 1;
-			continue;
-		}
 
 		blend_stage = stage - MDSS_MDP_STAGE_0;
 		off = MDSS_MDP_REG_LM_OFFSET(mixer->num) +
@@ -719,10 +734,8 @@
 
 		if (pipe->is_fg) {
 			bgalpha = 0;
-			if (bgpipe) {
-				mixercfg &= ~(0x7 << (3 * bgpipe->num));
-				mixercfg |= MDSS_MDP_LM_BORDER_COLOR;
-			}
+			mixercfg = MDSS_MDP_LM_BORDER_COLOR;
+
 			blend_op = (MDSS_MDP_BLEND_FG_ALPHA_FG_CONST |
 				    MDSS_MDP_BLEND_BG_ALPHA_BG_CONST);
 			/* keep fg alpha */
@@ -753,6 +766,8 @@
 					stage);
 		}
 
+		mixercfg |= stage << (3 * pipe->num);
+
 		MDSS_MDP_REG_WRITE(off + MDSS_MDP_REG_LM_OP_MODE, blend_op);
 		MDSS_MDP_REG_WRITE(off + MDSS_MDP_REG_LM_BLEND_FG_ALPHA,
 				   pipe->alpha);
@@ -903,13 +918,16 @@
 		return -ENODEV;
 	}
 
-	if (!ctl->power_on)
-		return 0;
-
 	pr_debug("commit ctl=%d play_cnt=%d\n", ctl->num, ctl->play_cnt);
 
-	if (mutex_lock_interruptible(&ctl->lock))
-		return -EINTR;
+	ret = mutex_lock_interruptible(&ctl->lock);
+	if (ret)
+		return ret;
+
+	if (!ctl->power_on) {
+		mutex_unlock(&ctl->lock);
+		return 0;
+	}
 
 	mixer1_changed = (ctl->mixer_left && ctl->mixer_left->params_changed);
 	mixer2_changed = (ctl->mixer_right && ctl->mixer_right->params_changed);
@@ -969,7 +987,7 @@
 	mutex_lock(&mdss_mdp_ctl_lock);
 	for (i = 0; i < MDSS_MDP_MAX_CTL; i++) {
 		ctl = &mdss_mdp_ctl_list[i];
-		if ((ctl->power_on) &&
+		if ((ctl->power_on) && (ctl->mfd) &&
 			(ctl->mfd->index == fb_num)) {
 			if (ctl->mixer_left) {
 				mixer_id[mixer_cnt] = ctl->mixer_left->num;
diff --git a/drivers/video/msm/mdss/mdss_mdp_hwio.h b/drivers/video/msm/mdss/mdss_mdp_hwio.h
index 1da30b8..b6ac126 100644
--- a/drivers/video/msm/mdss/mdss_mdp_hwio.h
+++ b/drivers/video/msm/mdss/mdss_mdp_hwio.h
@@ -166,6 +166,7 @@
 #define MDSS_MDP_REG_SSPP_SRC_CONSTANT_COLOR		0x03C
 #define MDSS_MDP_REG_SSPP_FETCH_CONFIG			0x048
 #define MDSS_MDP_REG_SSPP_VC1_RANGE			0x04C
+#define MDSS_MDP_REG_SSPP_SRC_ADDR_SW_STATUS		0x070
 
 #define MDSS_MDP_REG_SSPP_CURRENT_SRC0_ADDR		0x0A4
 #define MDSS_MDP_REG_SSPP_CURRENT_SRC1_ADDR		0x0A8
diff --git a/drivers/video/msm/mdss/mdss_mdp_intf_video.c b/drivers/video/msm/mdss/mdss_mdp_intf_video.c
index 4d3fbf0..052d78c 100644
--- a/drivers/video/msm/mdss/mdss_mdp_intf_video.c
+++ b/drivers/video/msm/mdss/mdss_mdp_intf_video.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
@@ -13,8 +13,6 @@
 
 #define pr_fmt(fmt)	"%s: " fmt, __func__
 
-#include <linux/workqueue.h>
-
 #include "mdss_fb.h"
 #include "mdss_mdp.h"
 
@@ -39,7 +37,6 @@
 
 #define MAX_SESSIONS 3
 struct mdss_mdp_video_ctx {
-	u32 ctl_num;
 	u32 pp_num;
 	u8 ref_cnt;
 
@@ -47,11 +44,9 @@
 	struct completion pp_comp;
 	struct completion vsync_comp;
 
-	struct mutex vsync_lock;
-	struct work_struct vsync_work;
+	atomic_t vsync_ref;
+	spinlock_t vsync_lock;
 	mdp_vsync_handler_t vsync_handler;
-	void *vsync_ptr;
-	ktime_t vsync_time;
 };
 
 struct mdss_mdp_video_ctx mdss_mdp_video_ctx_list[MAX_SESSIONS];
@@ -157,43 +152,43 @@
 	return 0;
 }
 
-static void send_vsync_work(struct work_struct *work)
-{
-	struct mdss_mdp_video_ctx *ctx;
 
-	ctx = container_of(work, typeof(*ctx), vsync_work);
-	mutex_lock(&ctx->vsync_lock);
-	if (ctx->vsync_handler)
-		ctx->vsync_handler(ctx->vsync_ptr, ctx->vsync_time);
-	mutex_unlock(&ctx->vsync_lock);
+static inline void video_vsync_irq_enable(struct mdss_mdp_ctl *ctl)
+{
+	struct mdss_mdp_video_ctx *ctx = ctl->priv_data;
+
+	if (atomic_inc_return(&ctx->vsync_ref) == 1)
+		mdss_mdp_irq_enable(MDSS_MDP_IRQ_INTF_VSYNC, ctl->intf_num);
+}
+
+static inline void video_vsync_irq_disable(struct mdss_mdp_ctl *ctl)
+{
+	struct mdss_mdp_video_ctx *ctx = ctl->priv_data;
+
+	if (atomic_dec_return(&ctx->vsync_ref) == 0)
+		mdss_mdp_irq_disable(MDSS_MDP_IRQ_INTF_VSYNC, ctl->intf_num);
 }
 
 static int mdss_mdp_video_set_vsync_handler(struct mdss_mdp_ctl *ctl,
 		mdp_vsync_handler_t vsync_handler)
 {
 	struct mdss_mdp_video_ctx *ctx;
+	unsigned long flags;
 
 	ctx = (struct mdss_mdp_video_ctx *) ctl->priv_data;
 	if (!ctx) {
 		pr_err("invalid ctx for ctl=%d\n", ctl->num);
 		return -ENODEV;
 	}
-	if (mutex_lock_interruptible(&ctx->vsync_lock))
-		return -EINTR;
 
-	if (vsync_handler && !ctx->timegen_en) {
-		ctx->vsync_time = ktime_get();
-		schedule_work(&ctx->vsync_work);
-	}
-
+	spin_lock_irqsave(&ctx->vsync_lock, flags);
 	if (!ctx->vsync_handler && vsync_handler)
-		mdss_mdp_irq_enable(MDSS_MDP_IRQ_INTF_VSYNC, ctl->intf_num);
+		video_vsync_irq_enable(ctl);
 	else if (ctx->vsync_handler && !vsync_handler)
-		mdss_mdp_irq_disable(MDSS_MDP_IRQ_INTF_VSYNC, ctl->intf_num);
+		video_vsync_irq_disable(ctl);
 
 	ctx->vsync_handler = vsync_handler;
-	ctx->vsync_ptr = ctl;
-	mutex_unlock(&ctx->vsync_lock);
+	spin_unlock_irqrestore(&ctx->vsync_lock, flags);
 
 	return 0;
 }
@@ -201,7 +196,7 @@
 static int mdss_mdp_video_stop(struct mdss_mdp_ctl *ctl)
 {
 	struct mdss_mdp_video_ctx *ctx;
-	int off;
+	int rc, off;
 
 	pr_debug("stop ctl=%d\n", ctl->num);
 
@@ -211,16 +206,26 @@
 		return -ENODEV;
 	}
 
-	if (ctx->vsync_handler)
-		mdss_mdp_video_set_vsync_handler(ctl, NULL);
-
 	if (ctx->timegen_en) {
+		rc = mdss_mdp_ctl_intf_event(ctl, MDSS_EVENT_BLANK, NULL);
+		if (rc == -EBUSY) {
+			pr_debug("intf #%d busy don't turn off\n",
+				 ctl->intf_num);
+			return rc;
+		}
+		WARN(rc, "intf %d blank error (%d)\n", ctl->intf_num, rc);
+
 		off = MDSS_MDP_REG_INTF_OFFSET(ctl->intf_num);
 		MDSS_MDP_REG_WRITE(off + MDSS_MDP_REG_INTF_TIMING_ENGINE_EN, 0);
 		mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false);
 		ctx->timegen_en = false;
+
+		rc = mdss_mdp_ctl_intf_event(ctl, MDSS_EVENT_TIMEGEN_OFF, NULL);
+		WARN(rc, "intf %d timegen off error (%d)\n", ctl->intf_num, rc);
 	}
 
+	mdss_mdp_video_set_vsync_handler(ctl, NULL);
+
 	mdss_mdp_set_intr_callback(MDSS_MDP_IRQ_INTF_VSYNC, ctl->intf_num,
 				   NULL, NULL);
 	mdss_mdp_set_intr_callback(MDSS_MDP_IRQ_PING_PONG_COMP, ctx->pp_num,
@@ -233,9 +238,9 @@
 
 static void mdss_mdp_video_pp_intr_done(void *arg)
 {
-	struct mdss_mdp_video_ctx *ctx;
+	struct mdss_mdp_ctl *ctl = arg;
+	struct mdss_mdp_video_ctx *ctx = ctl->priv_data;
 
-	ctx = (struct mdss_mdp_video_ctx *) arg;
 	if (!ctx) {
 		pr_err("invalid ctx\n");
 		return;
@@ -248,20 +253,24 @@
 
 static void mdss_mdp_video_vsync_intr_done(void *arg)
 {
-	struct mdss_mdp_video_ctx *ctx;
+	struct mdss_mdp_ctl *ctl = arg;
+	struct mdss_mdp_video_ctx *ctx = ctl->priv_data;
+	ktime_t vsync_time;
 
-	ctx = (struct mdss_mdp_video_ctx *) arg;
 	if (!ctx) {
 		pr_err("invalid ctx\n");
 		return;
 	}
-	ctx->vsync_time = ktime_get();
 
-	pr_debug("intr ctl=%d\n", ctx->ctl_num);
+	vsync_time = ktime_get();
+
+	pr_debug("intr ctl=%d\n", ctl->num);
 
 	complete(&ctx->vsync_comp);
+	spin_lock(&ctx->vsync_lock);
 	if (ctx->vsync_handler)
-		schedule_work(&ctx->vsync_work);
+		ctx->vsync_handler(ctl, vsync_time);
+	spin_unlock(&ctx->vsync_lock);
 }
 
 static int mdss_mdp_video_prepare(struct mdss_mdp_ctl *ctl, void *arg)
@@ -288,6 +297,7 @@
 static int mdss_mdp_video_display(struct mdss_mdp_ctl *ctl, void *arg)
 {
 	struct mdss_mdp_video_ctx *ctx;
+	int rc;
 
 	pr_debug("kickoff ctl=%d\n", ctl->num);
 
@@ -297,27 +307,30 @@
 		return -ENODEV;
 	}
 	INIT_COMPLETION(ctx->vsync_comp);
-
-	if (mutex_lock_interruptible(&ctx->vsync_lock))
-		return -EINTR;
-	if (!ctx->vsync_handler)
-		mdss_mdp_irq_enable(MDSS_MDP_IRQ_INTF_VSYNC, ctl->intf_num);
+	video_vsync_irq_enable(ctl);
 
 	if (!ctx->timegen_en) {
 		int off = MDSS_MDP_REG_INTF_OFFSET(ctl->intf_num);
 
+		rc = mdss_mdp_ctl_intf_event(ctl, MDSS_EVENT_UNBLANK, NULL);
+		WARN(rc, "intf %d unblank error (%d)\n", ctl->intf_num, rc);
+
 		pr_debug("enabling timing gen for intf=%d\n", ctl->intf_num);
 
 		mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false);
 		MDSS_MDP_REG_WRITE(off + MDSS_MDP_REG_INTF_TIMING_ENGINE_EN, 1);
-		ctx->timegen_en = true;
 		wmb();
 	}
 
 	wait_for_completion(&ctx->vsync_comp);
-	if (!ctx->vsync_handler)
-		mdss_mdp_irq_disable(MDSS_MDP_IRQ_INTF_VSYNC, ctl->intf_num);
-	mutex_unlock(&ctx->vsync_lock);
+
+	if (!ctx->timegen_en) {
+		ctx->timegen_en = true;
+		rc = mdss_mdp_ctl_intf_event(ctl, MDSS_EVENT_TIMEGEN_ON, NULL);
+		WARN(rc, "intf %d timegen on error (%d)\n", ctl->intf_num, rc);
+	}
+
+	video_vsync_irq_disable(ctl);
 
 	return 0;
 }
@@ -356,17 +369,16 @@
 		return -ENOMEM;
 	}
 	ctl->priv_data = ctx;
-	ctx->ctl_num = ctl->num;
 	ctx->pp_num = mixer->num;
 	init_completion(&ctx->pp_comp);
 	init_completion(&ctx->vsync_comp);
+	spin_lock_init(&ctx->vsync_lock);
+	atomic_set(&ctx->vsync_ref, 0);
 
-	INIT_WORK(&ctx->vsync_work, send_vsync_work);
-	mutex_init(&ctx->vsync_lock);
 	mdss_mdp_set_intr_callback(MDSS_MDP_IRQ_INTF_VSYNC, ctl->intf_num,
-				   mdss_mdp_video_vsync_intr_done, ctx);
+				   mdss_mdp_video_vsync_intr_done, ctl);
 	mdss_mdp_set_intr_callback(MDSS_MDP_IRQ_PING_PONG_COMP, ctx->pp_num,
-				   mdss_mdp_video_pp_intr_done, ctx);
+				   mdss_mdp_video_pp_intr_done, ctl);
 
 	itp.width = pinfo->xres + pinfo->lcdc.xres_pad;
 	itp.height = pinfo->yres + pinfo->lcdc.yres_pad;
diff --git a/drivers/video/msm/mdss/mdss_mdp_intf_writeback.c b/drivers/video/msm/mdss/mdss_mdp_intf_writeback.c
index 8b4434e..a1f1bcc 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) |
@@ -242,7 +245,7 @@
 
 	ctx->format = rot->format;
 
-	ctx->rot90 = !!(rot->rotations & MDP_ROT_90);
+	ctx->rot90 = !!(rot->flags & MDP_ROT_90);
 	if (ctx->rot90) {
 		ctx->opmode |= BIT(5); /* ROT 90 */
 		swap(ctx->width, ctx->height);
diff --git a/drivers/video/msm/mdss/mdss_mdp_overlay.c b/drivers/video/msm/mdss/mdss_mdp_overlay.c
index 452ebdc..9c62ea2 100644
--- a/drivers/video/msm/mdss/mdss_mdp_overlay.c
+++ b/drivers/video/msm/mdss/mdss_mdp_overlay.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
@@ -26,9 +26,12 @@
 #include "mdss_mdp.h"
 #include "mdss_mdp_rotator.h"
 
+#define VSYNC_PERIOD 16
 #define CHECK_BOUNDS(offset, size, max_size) \
 	(((size) > (max_size)) || ((offset) > ((max_size) - (size))))
 
+static atomic_t ov_active_panels = ATOMIC_INIT(0);
+
 static int mdss_mdp_overlay_get(struct msm_fb_data_type *mfd,
 				struct mdp_overlay *req)
 {
@@ -196,7 +199,9 @@
 		return -EINVAL;
 	}
 
-	rot->rotations = req->flags & (MDP_ROT_90 | MDP_FLIP_LR | MDP_FLIP_UD);
+	/* keep only flags of interest to rotator */
+	rot->flags = req->flags & (MDP_ROT_90 | MDP_FLIP_LR | MDP_FLIP_UD |
+				   MDP_SECURE_OVERLAY_SESSION);
 
 	rot->format = fmt->format;
 	rot->img_width = req->src.width;
@@ -206,6 +211,11 @@
 	rot->src_rect.w = req->src_rect.w;
 	rot->src_rect.h = req->src_rect.h;
 
+	if (req->flags & MDP_DEINTERLACE) {
+		rot->flags |= MDP_DEINTERLACE;
+		rot->src_rect.h /= 2;
+	}
+
 	rot->params_changed++;
 
 	req->id = rot->session_id;
@@ -313,6 +323,7 @@
 	pipe->is_fg = req->is_fg;
 	pipe->alpha = req->alpha;
 	pipe->transp = req->transp_mask;
+	pipe->overfetch_disable = fmt->is_yuv;
 
 	pipe->req_data = *req;
 
@@ -325,6 +336,15 @@
 								__func__);
 	}
 
+	if (pipe->flags & MDP_DEINTERLACE) {
+		if (pipe->flags & MDP_SOURCE_ROTATED_90) {
+			pipe->src.w /= 2;
+			pipe->img_width /= 2;
+		} else {
+			pipe->src.h /= 2;
+		}
+	}
+
 	pipe->params_changed++;
 
 	req->id = pipe->ndx;
@@ -341,6 +361,15 @@
 {
 	int ret;
 
+	ret = mutex_lock_interruptible(&mfd->ov_lock);
+	if (ret)
+		return ret;
+
+	if (!mfd->panel_power_on) {
+		mutex_unlock(&mfd->ov_lock);
+		return -EPERM;
+	}
+
 	if (req->flags & MDSS_MDP_ROT_ONLY) {
 		ret = mdss_mdp_overlay_rotator_setup(mfd, req);
 	} else {
@@ -354,18 +383,22 @@
 		req->z_order -= MDSS_MDP_STAGE_0;
 	}
 
+	mutex_unlock(&mfd->ov_lock);
+
 	return ret;
 }
 
 static inline int mdss_mdp_overlay_get_buf(struct msm_fb_data_type *mfd,
 					   struct mdss_mdp_data *data,
 					   struct msmfb_data *planes,
-					   int num_planes)
+					   int num_planes,
+					   u32 flags)
 {
 	int i;
 
 	memset(data, 0, sizeof(*data));
 	for (i = 0; i < num_planes; i++) {
+		data->p[i].flags = flags;
 		mdss_mdp_get_img(&planes[i], &data->p[i]);
 		if (data->p[0].len == 0)
 			break;
@@ -393,26 +426,18 @@
 	return 0;
 }
 
-static int mdss_mdp_overlay_kickoff(struct mdss_mdp_ctl *ctl)
+static int mdss_mdp_overlay_cleanup(struct msm_fb_data_type *mfd)
 {
 	struct mdss_mdp_pipe *pipe, *tmp;
-	struct msm_fb_data_type *mfd = ctl->mfd;
-	int i, ret;
+	LIST_HEAD(destroy_pipes);
+	int i;
 
-	if (mfd->kickoff_fnc)
-		ret = mfd->kickoff_fnc(ctl);
-	else
-		ret = mdss_mdp_display_commit(ctl, NULL);
-	if (IS_ERR_VALUE(ret))
-		return ret;
-
+	mutex_lock(&mfd->ov_lock);
 	mutex_lock(&mfd->lock);
 	list_for_each_entry_safe(pipe, tmp, &mfd->pipes_cleanup, cleanup_list) {
-		list_del(&pipe->cleanup_list);
+		list_move(&pipe->cleanup_list, &destroy_pipes);
 		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)) {
@@ -431,33 +456,44 @@
 		}
 	}
 	mutex_unlock(&mfd->lock);
+	list_for_each_entry_safe(pipe, tmp, &destroy_pipes, cleanup_list)
+		mdss_mdp_pipe_destroy(pipe);
+	mutex_unlock(&mfd->ov_lock);
+
+	return 0;
+}
+
+static int mdss_mdp_overlay_kickoff(struct mdss_mdp_ctl *ctl)
+{
+	struct msm_fb_data_type *mfd = ctl->mfd;
+	int ret;
+
+	if (mfd->kickoff_fnc)
+		ret = mfd->kickoff_fnc(ctl);
+	else
+		ret = mdss_mdp_display_commit(ctl, NULL);
+	if (IS_ERR_VALUE(ret))
+		return ret;
+
+	complete(&mfd->update.comp);
+	mutex_lock(&mfd->no_update.lock);
+	if (mfd->no_update.timer.function)
+		del_timer(&(mfd->no_update.timer));
+
+	mfd->no_update.timer.expires = jiffies + (2 * HZ);
+	add_timer(&mfd->no_update.timer);
+	mutex_unlock(&mfd->no_update.lock);
+
+	ret = mdss_mdp_overlay_cleanup(mfd);
 
 	return ret;
 }
 
-static int mdss_mdp_overlay_unset(struct msm_fb_data_type *mfd, int ndx)
+static int mdss_mdp_overlay_release(struct msm_fb_data_type *mfd, int ndx)
 {
 	struct mdss_mdp_pipe *pipe;
-	int i, ret = 0;
 	u32 pipe_ndx, unset_ndx = 0;
-
-	if (!mfd || !mfd->ctl)
-		return -ENODEV;
-
-	pr_debug("unset ndx=%x\n", ndx);
-
-	if (ndx & MDSS_MDP_ROT_SESSION_MASK) {
-		struct mdss_mdp_rotator_session *rot;
-		rot = mdss_mdp_rotator_session_get(ndx);
-		if (rot) {
-			mdss_mdp_rotator_finish(rot);
-		} else {
-			pr_warn("unknown session id=%x\n", ndx);
-			ret = -ENODEV;
-		}
-
-		return ret;
-	}
+	int i;
 
 	for (i = 0; unset_ndx != ndx && i < MDSS_MDP_MAX_SSPP; i++) {
 		pipe_ndx = BIT(i);
@@ -475,37 +511,59 @@
 			mdss_mdp_mixer_pipe_unstage(pipe);
 		}
 	}
+	return 0;
+}
+
+static int mdss_mdp_overlay_unset(struct msm_fb_data_type *mfd, int ndx)
+{
+	int ret = 0;
+
+	if (!mfd || !mfd->ctl)
+		return -ENODEV;
+
+	ret = mutex_lock_interruptible(&mfd->ov_lock);
+	if (ret)
+		return ret;
+
+	if (!mfd->panel_power_on) {
+		mutex_unlock(&mfd->ov_lock);
+		return -EPERM;
+	}
+
+	pr_debug("unset ndx=%x\n", ndx);
+
+	if (ndx & MDSS_MDP_ROT_SESSION_MASK)
+		ret = mdss_mdp_rotator_release(ndx);
+	else
+		ret = mdss_mdp_overlay_release(mfd, ndx);
+
+	mutex_unlock(&mfd->ov_lock);
 
 	return ret;
 }
 
-int mdss_mdp_overlay_release_all(struct msm_fb_data_type *mfd)
+static int mdss_mdp_overlay_release_all(struct msm_fb_data_type *mfd)
 {
 	struct mdss_mdp_pipe *pipe;
 	u32 unset_ndx = 0;
 	int cnt = 0;
 
+	mutex_lock(&mfd->ov_lock);
 	mutex_lock(&mfd->lock);
-	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);
-				if (rot)
-					mdss_mdp_rotator_finish(rot);
-			} else {
-				unset_ndx |= pipe->ndx;
-				cnt++;
-			}
-		}
+	list_for_each_entry(pipe, &mfd->pipes_used, used_list) {
+		unset_ndx |= pipe->ndx;
+		cnt++;
 	}
 	mutex_unlock(&mfd->lock);
 
 	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);
+		mdss_mdp_overlay_release(mfd, unset_ndx);
 	}
+	mutex_unlock(&mfd->ov_lock);
+
+	if (cnt)
+		mdss_mdp_overlay_kickoff(mfd->ctl);
 
 	return 0;
 }
@@ -531,26 +589,28 @@
 	struct mdss_mdp_rotator_session *rot;
 	struct mdss_mdp_data src_data, dst_data;
 	int ret;
+	u32 flgs;
 
-	ret = mdss_mdp_overlay_get_buf(mfd, &src_data, &req->data, 1);
+	rot = mdss_mdp_rotator_session_get(req->id);
+	if (!rot) {
+		pr_err("invalid session id=%x\n", req->id);
+		return -ENOENT;
+	}
+
+	flgs = rot->flags & MDP_SECURE_OVERLAY_SESSION;
+
+	ret = mdss_mdp_overlay_get_buf(mfd, &src_data, &req->data, 1, flgs);
 	if (ret) {
 		pr_err("src_data pmem error\n");
 		goto rotate_done;
 	}
 
-	ret = mdss_mdp_overlay_get_buf(mfd, &dst_data, &req->dst_data, 1);
+	ret = mdss_mdp_overlay_get_buf(mfd, &dst_data, &req->dst_data, 1, flgs);
 	if (ret) {
 		pr_err("dst_data pmem error\n");
 		goto rotate_done;
 	}
 
-	rot = mdss_mdp_rotator_session_get(req->id);
-	if (!rot) {
-		pr_err("invalid session id=%x\n", req->id);
-		ret = -ENODEV;
-		goto rotate_done;
-	}
-
 	ret = mdss_mdp_rotator_queue(rot, &src_data, &dst_data);
 	if (ret) {
 		pr_err("rotator queue error session id=%x\n", req->id);
@@ -571,6 +631,7 @@
 	struct mdss_mdp_pipe *pipe;
 	struct mdss_mdp_data *src_data;
 	int ret, buf_ndx;
+	u32 flags;
 
 	pipe = mdss_mdp_pipe_get_locked(req->id);
 	if (pipe == NULL) {
@@ -580,11 +641,13 @@
 
 	pr_debug("ov queue pnum=%d\n", pipe->num);
 
+	flags = (pipe->flags & MDP_SECURE_OVERLAY_SESSION);
+
 	buf_ndx = (pipe->play_cnt + 1) & 1; /* next buffer */
 	src_data = &pipe->buffers[buf_ndx];
 	mdss_mdp_overlay_free_buf(src_data);
 
-	ret = mdss_mdp_overlay_get_buf(mfd, src_data, &req->data, 1);
+	ret = mdss_mdp_overlay_get_buf(mfd, src_data, &req->data, 1, flags);
 	if (IS_ERR_VALUE(ret)) {
 		pr_err("src_data pmem error\n");
 	} else {
@@ -595,9 +658,6 @@
 	ctl = pipe->mixer->ctl;
 	mdss_mdp_pipe_unlock(pipe);
 
-	if ((ret == 0) && (mfd->panel_info.type == WRITEBACK_PANEL))
-		ret = mdss_mdp_overlay_kickoff(ctl);
-
 	return ret;
 }
 
@@ -608,11 +668,29 @@
 
 	pr_debug("play req id=%x\n", req->id);
 
-	if (req->id & MDSS_MDP_ROT_SESSION_MASK)
+	ret = mutex_lock_interruptible(&mfd->ov_lock);
+	if (ret)
+		return ret;
+
+	if (!mfd->panel_power_on) {
+		mutex_unlock(&mfd->ov_lock);
+		return -EPERM;
+	}
+
+	if (req->id & MDSS_MDP_ROT_SESSION_MASK) {
 		ret = mdss_mdp_overlay_rotate(mfd, req);
-	else
+	} else {
 		ret = mdss_mdp_overlay_queue(mfd, req);
 
+		if ((ret == 0) && (mfd->panel_info.type == WRITEBACK_PANEL)) {
+			mutex_unlock(&mfd->ov_lock);
+			ret = mdss_mdp_overlay_kickoff(mfd->ctl);
+			return ret;
+		}
+	}
+
+	mutex_unlock(&mfd->ov_lock);
+
 	return ret;
 }
 
@@ -686,10 +764,7 @@
 	u32 offset;
 	int bpp, ret;
 
-	if (!mfd)
-		return;
-
-	if (!mfd->ctl || !mfd->panel_power_on)
+	if (!mfd || !mfd->ctl)
 		return;
 
 	fbi = mfd->fbi;
@@ -699,6 +774,14 @@
 		return;
 	}
 
+	if (mutex_lock_interruptible(&mfd->ov_lock))
+		return;
+
+	if (!mfd->panel_power_on) {
+		mutex_unlock(&mfd->ov_lock);
+		return;
+	}
+
 	memset(&data, 0, sizeof(data));
 
 	bpp = fbi->var.bits_per_pixel / 8;
@@ -749,35 +832,33 @@
 			return;
 		}
 	}
+	mutex_unlock(&mfd->ov_lock);
 
 	if (fbi->var.activate & FB_ACTIVATE_VBL)
 		mdss_mdp_overlay_kickoff(mfd->ctl);
 }
 
+/* function is called in irq context should have minimum processing */
 static void mdss_mdp_overlay_handle_vsync(struct mdss_mdp_ctl *ctl, ktime_t t)
 {
-	struct device *dev;
-	char buf[64];
-	char *envp[2];
-
-	if (!ctl || !ctl->mfd || !ctl->mfd->fbi) {
+	struct msm_fb_data_type *mfd = ctl->mfd;
+	if (!mfd) {
 		pr_warn("Invalid handle for vsync\n");
 		return;
 	}
 
-	dev = ctl->mfd->fbi->dev;
+	pr_debug("vsync on fb%d play_cnt=%d\n", mfd->index, ctl->play_cnt);
 
-	snprintf(buf, sizeof(buf), "VSYNC=%llu", ktime_to_ns(t));
-	envp[0] = buf;
-	envp[1] = NULL;
-	kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, envp);
-
-	pr_debug("sent vsync on ctl=%d ts=%llu\n", ctl->num, ktime_to_ns(t));
+	spin_lock(&mfd->vsync_lock);
+	mfd->vsync_time = t;
+	complete(&mfd->vsync_comp);
+	spin_unlock(&mfd->vsync_lock);
 }
 
 int mdss_mdp_overlay_vsync_ctrl(struct msm_fb_data_type *mfd, int en)
 {
 	struct mdss_mdp_ctl *ctl = mfd->ctl;
+	unsigned long flags;
 	int rc;
 
 	if (!ctl)
@@ -785,13 +866,23 @@
 	if (!ctl->set_vsync_handler)
 		return -ENOTSUPP;
 
-	pr_debug("vsync en=%d\n", en);
-
 	if (!ctl->power_on) {
+		pr_debug("fb%d vsync pending first update en=%d\n",
+				mfd->index, en);
 		mfd->vsync_pending = en;
 		return 0;
 	}
 
+	pr_debug("fb%d vsync en=%d\n", mfd->index, en);
+
+	spin_lock_irqsave(&mfd->vsync_lock, flags);
+	INIT_COMPLETION(mfd->vsync_comp);
+	if (en && ctl->play_cnt == 0) {
+		mfd->vsync_time = ktime_get();
+		complete(&mfd->vsync_comp);
+	}
+	spin_unlock_irqrestore(&mfd->vsync_lock, flags);
+
 	mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false);
 	if (en)
 		rc = ctl->set_vsync_handler(ctl, mdss_mdp_overlay_handle_vsync);
@@ -802,6 +893,47 @@
 	return rc;
 }
 
+static ssize_t mdss_mdp_vsync_show_event(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct fb_info *fbi = dev_get_drvdata(dev);
+	struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)fbi->par;
+	unsigned long flags;
+	u64 vsync_ticks;
+	int ret;
+
+	if (!mfd->ctl || !mfd->ctl->power_on)
+		return 0;
+
+	ret = wait_for_completion_interruptible_timeout(&mfd->vsync_comp,
+			msecs_to_jiffies(VSYNC_PERIOD * 5));
+	if (ret <= 0) {
+		pr_warn("vsync wait on fb%d interrupted (%d)\n",
+			mfd->index, ret);
+		return -EBUSY;
+	}
+
+	spin_lock_irqsave(&mfd->vsync_lock, flags);
+	vsync_ticks = ktime_to_ns(mfd->vsync_time);
+	spin_unlock_irqrestore(&mfd->vsync_lock, flags);
+
+	pr_debug("fb%d vsync=%llu", mfd->index, vsync_ticks);
+	ret = snprintf(buf, PAGE_SIZE, "VSYNC=%llu", vsync_ticks);
+
+	return ret;
+}
+
+static DEVICE_ATTR(vsync_event, S_IRUGO, mdss_mdp_vsync_show_event, NULL);
+
+static struct attribute *vsync_fs_attrs[] = {
+	&dev_attr_vsync_event.attr,
+	NULL,
+};
+
+static struct attribute_group vsync_fs_attr_group = {
+	.attrs = vsync_fs_attrs,
+};
+
 static int mdss_mdp_hw_cursor_update(struct msm_fb_data_type *mfd,
 				     struct fb_cursor *cursor)
 {
@@ -820,9 +952,9 @@
 		}
 
 		ret = msm_iommu_map_contig_buffer(mfd->cursor_buf_phys,
-						mdss_get_iommu_domain(), 0,
-						MDSS_MDP_CURSOR_SIZE, SZ_4K,
-						0, &(mfd->cursor_buf_iova));
+			mdss_get_iommu_domain(MDSS_IOMMU_DOMAIN_UNSECURE),
+			0, MDSS_MDP_CURSOR_SIZE, SZ_4K, 0,
+			&(mfd->cursor_buf_iova));
 		if (IS_ERR_VALUE(ret)) {
 			dma_free_coherent(NULL, MDSS_MDP_CURSOR_SIZE,
 					  mfd->cursor_buf,
@@ -951,7 +1083,7 @@
 		}
 
 		if (ret) {
-			pr_err("OVERLAY_GET failed (%d)\n", ret);
+			pr_debug("OVERLAY_GET failed (%d)\n", ret);
 			ret = -EFAULT;
 		}
 		break;
@@ -965,7 +1097,7 @@
 				ret = copy_to_user(argp, &req, sizeof(req));
 		}
 		if (ret) {
-			pr_err("OVERLAY_SET failed (%d)\n", ret);
+			pr_debug("OVERLAY_SET failed (%d)\n", ret);
 			ret = -EFAULT;
 		}
 		break;
@@ -997,7 +1129,7 @@
 			}
 
 			if (ret) {
-				pr_err("OVERLAY_PLAY failed (%d)\n", ret);
+				pr_debug("OVERLAY_PLAY failed (%d)\n", ret);
 				ret = -EFAULT;
 			}
 		} else {
@@ -1043,10 +1175,39 @@
 	return ret;
 }
 
+static int mdss_mdp_overlay_on(struct msm_fb_data_type *mfd)
+{
+	int rc;
+
+	rc = mdss_mdp_ctl_on(mfd);
+	if (rc == 0)
+		atomic_inc(&ov_active_panels);
+
+	return rc;
+}
+
+static int mdss_mdp_overlay_off(struct msm_fb_data_type *mfd)
+{
+	int rc;
+
+	mdss_mdp_overlay_release_all(mfd);
+
+	rc = mdss_mdp_ctl_off(mfd);
+	if (rc == 0) {
+		if (atomic_dec_return(&ov_active_panels) == 0)
+			mdss_mdp_rotator_release_all();
+	}
+
+	return rc;
+}
+
 int mdss_mdp_overlay_init(struct msm_fb_data_type *mfd)
 {
-	mfd->on_fnc = mdss_mdp_ctl_on;
-	mfd->off_fnc = mdss_mdp_ctl_off;
+	struct device *dev = mfd->fbi->dev;
+	int rc;
+
+	mfd->on_fnc = mdss_mdp_overlay_on;
+	mfd->off_fnc = mdss_mdp_overlay_off;
 	mfd->hw_refresh = true;
 	mfd->do_histogram = NULL;
 	mfd->overlay_play_enable = true;
@@ -1059,6 +1220,18 @@
 
 	INIT_LIST_HEAD(&mfd->pipes_used);
 	INIT_LIST_HEAD(&mfd->pipes_cleanup);
+	init_completion(&mfd->vsync_comp);
+	spin_lock_init(&mfd->vsync_lock);
+	mutex_init(&mfd->ov_lock);
 
-	return 0;
+	rc = sysfs_create_group(&dev->kobj, &vsync_fs_attr_group);
+	if (rc) {
+		pr_err("vsync sysfs group creation failed, ret=%d\n", rc);
+		return rc;
+	}
+
+	kobject_uevent(&dev->kobj, KOBJ_ADD);
+	pr_debug("vsync kobject_uevent(KOBJ_ADD)\n");
+
+	return rc;
 }
diff --git a/drivers/video/msm/mdss/mdss_mdp_pipe.c b/drivers/video/msm/mdss/mdss_mdp_pipe.c
index c90ce8c..459cf14 100644
--- a/drivers/video/msm/mdss/mdss_mdp_pipe.c
+++ b/drivers/video/msm/mdss/mdss_mdp_pipe.c
@@ -22,6 +22,7 @@
 #define SMP_MB_CNT (mdss_res->smp_mb_cnt)
 
 static DEFINE_MUTEX(mdss_mdp_sspp_lock);
+static DEFINE_MUTEX(mdss_mdp_smp_lock);
 static DECLARE_BITMAP(mdss_mdp_smp_mmb_pool, MDSS_MDP_SMP_MMB_BLOCKS);
 
 static struct mdss_mdp_pipe mdss_mdp_pipe_list[MDSS_MDP_MAX_SSPP];
@@ -69,9 +70,11 @@
 
 static void mdss_mdp_smp_free(struct mdss_mdp_pipe *pipe)
 {
+	mutex_lock(&mdss_mdp_smp_lock);
 	mdss_mdp_smp_mmb_free(&pipe->smp[0]);
 	mdss_mdp_smp_mmb_free(&pipe->smp[1]);
 	mdss_mdp_smp_mmb_free(&pipe->smp[2]);
+	mutex_unlock(&mdss_mdp_smp_lock);
 }
 
 static int mdss_mdp_smp_reserve(struct mdss_mdp_pipe *pipe)
@@ -83,7 +86,7 @@
 	    (pipe->type == MDSS_MDP_PIPE_TYPE_RGB))
 		return -EINVAL;
 
-	mutex_lock(&mdss_mdp_sspp_lock);
+	mutex_lock(&mdss_mdp_smp_lock);
 	for (i = 0; i < pipe->src_planes.num_planes; i++) {
 		num_blks = DIV_ROUND_UP(2 * pipe->src_planes.ystride[i],
 					mdss_res->smp_mb_size);
@@ -98,10 +101,11 @@
 
 	if (reserved < num_blks) {
 		pr_err("insufficient MMB blocks\n");
-		mdss_mdp_smp_free(pipe);
+		for (; i >= 0; i--)
+			mdss_mdp_smp_mmb_free(&pipe->smp[i]);
 		return -ENOMEM;
 	}
-	mutex_unlock(&mdss_mdp_sspp_lock);
+	mutex_unlock(&mdss_mdp_smp_lock);
 
 	return 0;
 }
@@ -141,10 +145,10 @@
 		return -EINVAL;
 	}
 
-	mutex_lock(&mdss_mdp_sspp_lock);
+	mutex_lock(&mdss_mdp_smp_lock);
 	for (i = 0; i < pipe->src_planes.num_planes; i++)
 		mdss_mdp_smp_mmb_set(client_id + i, &pipe->smp[i]);
-	mutex_unlock(&mdss_mdp_sspp_lock);
+	mutex_unlock(&mdss_mdp_smp_lock);
 	return 0;
 }
 
@@ -264,9 +268,7 @@
 			atomic_read(&pipe->ref_cnt));
 
 	mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false);
-	mutex_lock(&mdss_mdp_sspp_lock);
 	mdss_mdp_pipe_free(pipe);
-	mutex_unlock(&mdss_mdp_sspp_lock);
 	mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false);
 
 	return 0;
@@ -464,6 +466,7 @@
 static int mdss_mdp_image_setup(struct mdss_mdp_pipe *pipe)
 {
 	u32 img_size, src_size, src_xy, dst_size, dst_xy, ystride0, ystride1;
+	u32 width, height;
 
 	pr_debug("pnum=%d wh=%dx%d src={%d,%d,%d,%d} dst={%d,%d,%d,%d}\n",
 		   pipe->num, pipe->img_width, pipe->img_height,
@@ -473,10 +476,21 @@
 	if (mdss_mdp_scale_setup(pipe))
 		return -EINVAL;
 
-	mdss_mdp_get_plane_sizes(pipe->src_fmt->format, pipe->img_width,
-				 pipe->img_height, &pipe->src_planes);
+	width = pipe->img_width;
+	height = pipe->img_height;
+	mdss_mdp_get_plane_sizes(pipe->src_fmt->format, width, height,
+			&pipe->src_planes);
 
-	img_size = (pipe->img_height << 16) | pipe->img_width;
+	if ((pipe->flags & MDP_DEINTERLACE) &&
+			!(pipe->flags & MDP_SOURCE_ROTATED_90)) {
+		int i;
+		for (i = 0; i < pipe->src_planes.num_planes; i++)
+			pipe->src_planes.ystride[i] *= 2;
+		width *= 2;
+		height /= 2;
+	}
+
+	img_size = (height << 16) | width;
 	src_size = (pipe->src.h << 16) | pipe->src.w;
 	src_xy = (pipe->src.y << 16) | pipe->src.x;
 	dst_size = (pipe->dst.h << 16) | pipe->dst.w;
@@ -486,6 +500,11 @@
 	ystride1 =  (pipe->src_planes.ystride[2]) |
 		    (pipe->src_planes.ystride[3] << 16);
 
+	if (pipe->overfetch_disable) {
+		img_size = src_size;
+		src_xy = 0;
+	}
+
 	mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SSPP_SRC_IMG_SIZE, img_size);
 	mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SSPP_SRC_SIZE, src_size);
 	mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SSPP_SRC_XY, src_xy);
@@ -501,9 +520,13 @@
 {
 	struct mdss_mdp_format_params *fmt;
 	u32 opmode, chroma_samp, unpack, src_format;
+	u32 secure = 0;
 
 	fmt = pipe->src_fmt;
 
+	if (pipe->flags & MDP_SECURE_OVERLAY_SESSION)
+		secure = 0xF;
+
 	opmode = pipe->bwc_mode;
 	if (pipe->flags & MDP_FLIP_LR)
 		opmode |= MDSS_MDP_OP_FLIP_LR;
@@ -550,6 +573,7 @@
 	mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SSPP_SRC_FORMAT, src_format);
 	mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SSPP_SRC_UNPACK_PATTERN, unpack);
 	mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SSPP_SRC_OP_MODE, opmode);
+	mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SSPP_SRC_ADDR_SW_STATUS, secure);
 
 	return 0;
 }
@@ -591,6 +615,28 @@
 	return 0;
 }
 
+static void mdss_mdp_addr_add_offset(struct mdss_mdp_pipe *pipe,
+				    struct mdss_mdp_data *data)
+{
+	data->p[0].addr += pipe->src.x +
+		(pipe->src.y * pipe->src_planes.ystride[0]);
+	if (data->num_planes > 1) {
+		u8 hmap[] = { 1, 2, 1, 2 };
+		u8 vmap[] = { 1, 1, 2, 2 };
+		u16 xoff = pipe->src.x / hmap[pipe->src_fmt->chroma_sample];
+		u16 yoff = pipe->src.y / vmap[pipe->src_fmt->chroma_sample];
+
+		if (data->num_planes == 2) /* pseudo planar */
+			xoff *= 2;
+		data->p[1].addr += xoff + (yoff * pipe->src_planes.ystride[1]);
+
+		if (data->num_planes > 2) { /* planar */
+			data->p[2].addr += xoff +
+				(yoff * pipe->src_planes.ystride[2]);
+		}
+	}
+}
+
 static int mdss_mdp_src_addr_setup(struct mdss_mdp_pipe *pipe,
 				   struct mdss_mdp_data *data)
 {
@@ -606,6 +652,9 @@
 	if (ret)
 		return ret;
 
+	if (pipe->overfetch_disable)
+		mdss_mdp_addr_add_offset(pipe, data);
+
 	/* planar format expects YCbCr, swap chroma planes if YCrCb */
 	if (!is_rot && (pipe->src_fmt->fetch_planes == MDSS_MDP_PLANE_PLANAR) &&
 	    (pipe->src_fmt->element[0] == C2_R_Cr))
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.c b/drivers/video/msm/mdss/mdss_mdp_rotator.c
index dc1cb0d..1e58269 100644
--- a/drivers/video/msm/mdss/mdss_mdp_rotator.c
+++ b/drivers/video/msm/mdss/mdss_mdp_rotator.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
@@ -187,12 +187,17 @@
 {
 	struct mdss_mdp_pipe *rot_pipe;
 	struct mdss_mdp_ctl *ctl;
-	int ret;
+	int ret, need_wait = false;
 
-	if (!rot)
+	ret = mutex_lock_interruptible(&rotator_lock);
+	if (ret)
+		return ret;
+
+	if (!rot || !rot->ref_cnt) {
+		mutex_unlock(&rotator_lock);
 		return -ENODEV;
+	}
 
-	mutex_lock(&rotator_lock);
 	ret = mdss_mdp_rotator_pipe_dequeue(rot);
 	if (ret) {
 		pr_err("unable to acquire rotator\n");
@@ -207,7 +212,7 @@
 
 	if (rot->params_changed) {
 		rot->params_changed = 0;
-		rot_pipe->flags = rot->rotations;
+		rot_pipe->flags = rot->flags;
 		rot_pipe->src_fmt = mdss_mdp_get_format_params(rot->format);
 		rot_pipe->img_width = rot->img_width;
 		rot_pipe->img_height = rot->img_height;
@@ -225,16 +230,18 @@
 
 	ret = mdss_mdp_rotator_kickoff(ctl, rot, dst_data);
 
+	if (ret == 0 && !rot->no_wait)
+		need_wait = true;
 done:
 	mutex_unlock(&rotator_lock);
 
-	if (!rot->no_wait)
+	if (need_wait)
 		mdss_mdp_rotator_busy_wait(rot);
 
 	return ret;
 }
 
-int mdss_mdp_rotator_finish(struct mdss_mdp_rotator_session *rot)
+static int mdss_mdp_rotator_finish(struct mdss_mdp_rotator_session *rot)
 {
 	struct mdss_mdp_pipe *rot_pipe;
 
@@ -243,7 +250,6 @@
 
 	pr_debug("finish rot id=%x\n", rot->session_id);
 
-	mutex_lock(&rotator_lock);
 	rot_pipe = rot->pipe;
 	if (rot_pipe) {
 		mdss_mdp_rotator_busy_wait(rot);
@@ -255,7 +261,43 @@
 		mdss_mdp_pipe_destroy(rot_pipe);
 		mdss_mdp_wb_mixer_destroy(mixer);
 	}
+
+	return 0;
+}
+
+int mdss_mdp_rotator_release(u32 ndx)
+{
+	struct mdss_mdp_rotator_session *rot;
+	mutex_lock(&rotator_lock);
+	rot = mdss_mdp_rotator_session_get(ndx);
+	if (rot) {
+		mdss_mdp_rotator_finish(rot);
+	} else {
+		pr_warn("unknown session id=%x\n", ndx);
+		return -ENOENT;
+	}
 	mutex_unlock(&rotator_lock);
 
 	return 0;
 }
+
+int mdss_mdp_rotator_release_all(void)
+{
+	struct mdss_mdp_rotator_session *rot;
+	int i, cnt;
+
+	mutex_lock(&rotator_lock);
+	for (i = 0, cnt = 0; i < MAX_ROTATOR_SESSIONS; i++) {
+		rot = &rotator_session[i];
+		if (rot->ref_cnt) {
+			mdss_mdp_rotator_finish(rot);
+			cnt++;
+		}
+	}
+	mutex_unlock(&rotator_lock);
+
+	if (cnt)
+		pr_debug("cleaned up %d rotator sessions\n", cnt);
+
+	return 0;
+}
diff --git a/drivers/video/msm/mdss/mdss_mdp_rotator.h b/drivers/video/msm/mdss/mdss_mdp_rotator.h
index eb5b47a..70ef6bf 100644
--- a/drivers/video/msm/mdss/mdss_mdp_rotator.h
+++ b/drivers/video/msm/mdss/mdss_mdp_rotator.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
@@ -25,7 +25,7 @@
 	u32 params_changed;
 
 	u32 format;
-	u32 rotations;
+	u32 flags;
 
 	u16 img_width;
 	u16 img_height;
@@ -48,7 +48,8 @@
 int mdss_mdp_rotator_queue(struct mdss_mdp_rotator_session *rot,
 			   struct mdss_mdp_data *src_data,
 			   struct mdss_mdp_data *dst_data);
-int mdss_mdp_rotator_finish(struct mdss_mdp_rotator_session *rot);
-int mdss_mdp_rotator_ctl_busy_wait(struct mdss_mdp_ctl *ctl);
+
+int mdss_mdp_rotator_release(u32 ndx);
+int mdss_mdp_rotator_release_all(void);
 
 #endif /* MDSS_MDP_ROTATOR_H */
diff --git a/drivers/video/msm/mdss/mdss_mdp_util.c b/drivers/video/msm/mdss/mdss_mdp_util.c
index 95c92fc..9f2df85 100644
--- a/drivers/video/msm/mdss/mdss_mdp_util.c
+++ b/drivers/video/msm/mdss/mdss_mdp_util.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,6 +21,7 @@
 #include <linux/msm_kgsl.h>
 #include <linux/spinlock.h>
 #include <linux/types.h>
+#include <media/msm_media_info.h>
 
 #include <mach/iommu_domains.h>
 
@@ -210,6 +211,13 @@
 		ps->num_planes = 1;
 		ps->plane_size[0] = w * h * bpp;
 		ps->ystride[0] = w * bpp;
+	} else if (format == MDP_Y_CBCR_H2V2_VENUS) {
+		int cf = COLOR_FMT_NV12;
+		ps->num_planes = 2;
+		ps->ystride[0] = VENUS_Y_STRIDE(cf, w);
+		ps->ystride[1] = VENUS_UV_STRIDE(cf, w);
+		ps->plane_size[0] = VENUS_Y_SCANLINES(cf, h) * ps->ystride[0];
+		ps->plane_size[1] = VENUS_UV_SCANLINES(cf, h) * ps->ystride[1];
 	} else {
 		u8 hmap[] = { 1, 2, 1, 2 };
 		u8 vmap[] = { 1, 1, 2, 2 };
@@ -223,10 +231,6 @@
 			stride_align = 16;
 			height_align = 1;
 			break;
-		case MDP_Y_CBCR_H2V2_VENUS:
-			stride_align = 32;
-			height_align = 32;
-			break;
 		default:
 			stride_align = 1;
 			height_align = 1;
@@ -312,9 +316,20 @@
 	} else if (!IS_ERR_OR_NULL(data->srcp_ihdl)) {
 		pr_debug("ion hdl=%p buf=0x%x\n", data->srcp_ihdl, data->addr);
 
-		if (is_mdss_iommu_attached())
+		if (is_mdss_iommu_attached()) {
+			int domain;
+			if (data->flags & MDP_SECURE_OVERLAY_SESSION)
+				domain = MDSS_IOMMU_DOMAIN_SECURE;
+			else
+				domain = MDSS_IOMMU_DOMAIN_UNSECURE;
 			ion_unmap_iommu(iclient, data->srcp_ihdl,
-					mdss_get_iommu_domain(), 0);
+					mdss_get_iommu_domain(domain), 0);
+
+			if (domain == MDSS_IOMMU_DOMAIN_SECURE) {
+				msm_ion_unsecure_buffer(iclient,
+					data->srcp_ihdl);
+			}
+		}
 
 		ion_free(iclient, data->srcp_ihdl);
 		data->srcp_ihdl = NULL;
@@ -335,7 +350,7 @@
 
 	start = (unsigned long *) &data->addr;
 	len = (unsigned long *) &data->len;
-	data->flags = img->flags;
+	data->flags |= img->flags;
 	data->p_need = 0;
 
 	if (img->flags & MDP_BLIT_SRC_GEM) {
@@ -370,8 +385,24 @@
 		}
 
 		if (is_mdss_iommu_attached()) {
+			int domain;
+			if (data->flags & MDP_SECURE_OVERLAY_SESSION) {
+				domain = MDSS_IOMMU_DOMAIN_SECURE;
+				ret = msm_ion_secure_buffer(iclient,
+					data->srcp_ihdl, 0x2,
+					ION_UNSECURE_DELAYED);
+				if (IS_ERR_VALUE(ret)) {
+					ion_free(iclient, data->srcp_ihdl);
+					pr_err("failed to secure handle (%d)\n",
+						ret);
+					return ret;
+				}
+			} else {
+				domain = MDSS_IOMMU_DOMAIN_UNSECURE;
+			}
+
 			ret = ion_map_iommu(iclient, data->srcp_ihdl,
-					    mdss_get_iommu_domain(),
+					    mdss_get_iommu_domain(domain),
 					    0, SZ_4K, 0, start, len, 0,
 					    ION_IOMMU_UNMAP_DELAYED);
 		} else {
diff --git a/drivers/video/msm/mdss/mdss_mdp_wb.c b/drivers/video/msm/mdss/mdss_mdp_wb.c
index b18efbe..b74523b 100644
--- a/drivers/video/msm/mdss/mdss_mdp_wb.c
+++ b/drivers/video/msm/mdss/mdss_mdp_wb.c
@@ -96,8 +96,9 @@
 		ion_phys(iclient, ihdl, &mdss_wb_mem, &img_size);
 
 		if (is_mdss_iommu_attached()) {
+			int domain = MDSS_IOMMU_DOMAIN_UNSECURE;
 			rc = ion_map_iommu(iclient, ihdl,
-					   mdss_get_iommu_domain(),
+					   mdss_get_iommu_domain(domain),
 					   0, SZ_4K, 0,
 					   (unsigned long *) &img->addr,
 					   (unsigned long *) &img->len,
@@ -569,6 +570,6 @@
 
 int msm_fb_get_iommu_domain(void)
 {
-	return mdss_get_iommu_domain();
+	return mdss_get_iommu_domain(MDSS_IOMMU_DOMAIN_UNSECURE);
 }
 EXPORT_SYMBOL(msm_fb_get_iommu_domain);
diff --git a/drivers/video/msm/mdss/mdss_panel.h b/drivers/video/msm/mdss/mdss_panel.h
index 5cdfe34..28d7051 100644
--- a/drivers/video/msm/mdss/mdss_panel.h
+++ b/drivers/video/msm/mdss/mdss_panel.h
@@ -55,6 +55,17 @@
 	MAX_PHYS_TARGET_NUM,
 };
 
+enum mdss_intf_events {
+	MDSS_EVENT_RESET,
+	MDSS_EVENT_UNBLANK,
+	MDSS_EVENT_TIMEGEN_ON,
+	MDSS_EVENT_BLANK,
+	MDSS_EVENT_TIMEGEN_OFF,
+	MDSS_EVENT_CLOSE,
+	MDSS_EVENT_SUSPEND,
+	MDSS_EVENT_RESUME,
+};
+
 /* panel info type */
 struct lcd_panel_info {
 	u32 vsync_enable;
@@ -178,14 +189,11 @@
 
 struct mdss_panel_data {
 	struct mdss_panel_info panel_info;
-	void (*set_backlight) (struct mdss_panel_data *pdata,
-							u32 bl_level);
-	int (*intf_unprepare) (struct mdss_panel_data *pdata);
+	void (*set_backlight) (struct mdss_panel_data *pdata, u32 bl_level);
 	unsigned char *mmss_cc_base;
 
 	/* function entry chain */
-	int (*on) (struct mdss_panel_data *pdata);
-	int (*off) (struct mdss_panel_data *pdata);
+	int (*event_handler) (struct mdss_panel_data *pdata, int e, void *arg);
 };
 
 int mdss_register_panel(struct mdss_panel_data *pdata);
diff --git a/drivers/video/msm/mdss/mdss_wb.c b/drivers/video/msm/mdss/mdss_wb.c
index d4c924f..c3dc06b 100644
--- a/drivers/video/msm/mdss/mdss_wb.c
+++ b/drivers/video/msm/mdss/mdss_wb.c
@@ -25,15 +25,10 @@
 
 #include "mdss_panel.h"
 
-static int mdss_wb_on(struct mdss_panel_data *pdata)
+static int mdss_wb_event_handler(struct mdss_panel_data *pdata,
+				 int event, void *arg)
 {
-	pr_debug("%s\n", __func__);
-	return 0;
-}
-
-static int mdss_wb_off(struct mdss_panel_data *pdata)
-{
-	pr_debug("%s\n", __func__);
+	pr_debug("%s: event=%d\n", __func__, event);
 	return 0;
 }
 
@@ -75,8 +70,7 @@
 	pdata->panel_info.pdest = DISPLAY_3;
 	pdata->panel_info.out_format = MDP_Y_CBCR_H2V2_VENUS;
 
-	pdata->on = mdss_wb_on;
-	pdata->off = mdss_wb_off;
+	pdata->event_handler = mdss_wb_event_handler;
 	pdev->dev.platform_data = pdata;
 
 	rc = mdss_register_panel(pdata);
diff --git a/drivers/video/msm/mdss/mhl_sii8334.c b/drivers/video/msm/mdss/mhl_sii8334.c
new file mode 100644
index 0000000..6a63964
--- /dev/null
+++ b/drivers/video/msm/mdss/mhl_sii8334.c
@@ -0,0 +1,1184 @@
+/* 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/bitops.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of_address.h>
+#include <linux/of_gpio.h>
+#include <linux/types.h>
+#include <linux/mhl_8334.h>
+
+#include "mdss_fb.h"
+#include "mdss_hdmi_tx.h"
+#include "mdss_hdmi_edid.h"
+#include "mdss.h"
+#include "mdss_panel.h"
+#include "mdss_io_util.h"
+
+#define MHL_DRIVER_NAME "sii8334"
+#define COMPATIBLE_NAME "qcom,mhl-sii8334"
+
+#define pr_debug_intr(...) pr_debug("\n")
+
+enum mhl_gpio_type {
+	MHL_TX_RESET_GPIO,
+	MHL_TX_INTR_GPIO,
+	MHL_TX_PMIC_PWR_GPIO,
+	MHL_TX_MAX_GPIO,
+};
+
+enum mhl_vreg_type {
+	MHL_TX_3V_VREG,
+	MHL_TX_MAX_VREG,
+};
+
+struct mhl_tx_platform_data {
+	/* Data filled from device tree nodes */
+	struct dss_gpio *gpios[MHL_TX_MAX_GPIO];
+	struct dss_vreg *vregs[MHL_TX_MAX_VREG];
+	int irq;
+};
+
+struct mhl_tx_ctrl {
+	struct platform_device *pdev;
+	struct mhl_tx_platform_data *pdata;
+	struct i2c_client *i2c_handle;
+	uint8_t cur_state;
+	uint8_t chip_rev_id;
+	int mhl_mode;
+};
+
+
+uint8_t slave_addrs[MAX_PAGES] = {
+	DEV_PAGE_TPI_0    ,
+	DEV_PAGE_TX_L0_0  ,
+	DEV_PAGE_TX_L1_0  ,
+	DEV_PAGE_TX_2_0   ,
+	DEV_PAGE_TX_3_0   ,
+	DEV_PAGE_CBUS     ,
+	DEV_PAGE_DDC_EDID ,
+	DEV_PAGE_DDC_SEGM ,
+};
+
+static irqreturn_t mhl_tx_isr(int irq, void *dev_id);
+static void switch_mode(struct mhl_tx_ctrl *mhl_ctrl,
+			enum mhl_st_type to_mode);
+static void mhl_drive_hpd(struct mhl_tx_ctrl *mhl_ctrl,
+			  uint8_t to_state);
+
+static int mhl_i2c_reg_read(struct i2c_client *client,
+			    uint8_t slave_addr_index, uint8_t reg_offset)
+{
+	int rc = -1;
+	uint8_t buffer = 0;
+
+	rc = mdss_i2c_byte_read(client, slave_addrs[slave_addr_index],
+				reg_offset, &buffer);
+	if (rc) {
+		pr_err("%s: slave=%x, off=%x\n",
+		       __func__, slave_addrs[slave_addr_index], reg_offset);
+		return rc;
+	}
+	return buffer;
+}
+
+
+static int mhl_i2c_reg_write(struct i2c_client *client,
+			     uint8_t slave_addr_index, uint8_t reg_offset,
+			     uint8_t value)
+{
+	return mdss_i2c_byte_write(client, slave_addrs[slave_addr_index],
+				 reg_offset, &value);
+}
+
+static void mhl_i2c_reg_modify(struct i2c_client *client,
+			       uint8_t slave_addr_index, uint8_t reg_offset,
+			       uint8_t mask, uint8_t val)
+{
+	uint8_t temp;
+
+	temp = mhl_i2c_reg_read(client, slave_addr_index, reg_offset);
+	temp &= (~mask);
+	temp |= (mask & val);
+	mhl_i2c_reg_write(client, slave_addr_index, reg_offset, temp);
+}
+
+
+static int mhl_tx_get_dt_data(struct device *dev,
+	struct mhl_tx_platform_data *pdata)
+{
+	int i, rc = 0;
+	struct device_node *of_node = NULL;
+	struct dss_gpio *temp_gpio = NULL;
+	i = 0;
+
+	if (!dev || !pdata) {
+		pr_err("%s: invalid input\n", __func__);
+		return -EINVAL;
+	}
+
+	of_node = dev->of_node;
+	if (!of_node) {
+		pr_err("%s: invalid of_node\n", __func__);
+		goto error;
+	}
+
+	pr_debug("%s: id=%d\n", __func__, dev->id);
+
+	/* GPIOs */
+	temp_gpio = NULL;
+	temp_gpio = devm_kzalloc(dev, sizeof(struct dss_gpio), GFP_KERNEL);
+	pr_debug("%s: gpios allocd\n", __func__);
+	if (!(temp_gpio)) {
+		pr_err("%s: can't alloc %d gpio mem\n", __func__, i);
+		goto error;
+	}
+	/* RESET */
+	temp_gpio->gpio = of_get_named_gpio(of_node, "mhl-rst-gpio", 0);
+	snprintf(temp_gpio->gpio_name, 32, "%s", "mhl-rst-gpio");
+	pr_debug("%s: rst gpio=[%d]\n", __func__,
+		 temp_gpio->gpio);
+	pdata->gpios[MHL_TX_RESET_GPIO] = temp_gpio;
+
+	/* PWR */
+	temp_gpio = NULL;
+	temp_gpio = devm_kzalloc(dev, sizeof(struct dss_gpio), GFP_KERNEL);
+	pr_debug("%s: gpios allocd\n", __func__);
+	if (!(temp_gpio)) {
+		pr_err("%s: can't alloc %d gpio mem\n", __func__, i);
+		goto error;
+	}
+	temp_gpio->gpio = of_get_named_gpio(of_node, "mhl-pwr-gpio", 0);
+	snprintf(temp_gpio->gpio_name, 32, "%s", "mhl-pwr-gpio");
+	pr_debug("%s: pmic gpio=[%d]\n", __func__,
+		 temp_gpio->gpio);
+	pdata->gpios[MHL_TX_PMIC_PWR_GPIO] = temp_gpio;
+
+	/* INTR */
+	temp_gpio = NULL;
+	temp_gpio = devm_kzalloc(dev, sizeof(struct dss_gpio), GFP_KERNEL);
+	pr_debug("%s: gpios allocd\n", __func__);
+	if (!(temp_gpio)) {
+		pr_err("%s: can't alloc %d gpio mem\n", __func__, i);
+		goto error;
+	}
+	temp_gpio->gpio = of_get_named_gpio(of_node, "mhl-intr-gpio", 0);
+	snprintf(temp_gpio->gpio_name, 32, "%s", "mhl-intr-gpio");
+	pr_debug("%s: intr gpio=[%d]\n", __func__,
+		 temp_gpio->gpio);
+	pdata->gpios[MHL_TX_INTR_GPIO] = temp_gpio;
+
+	return 0;
+error:
+	pr_err("%s: ret due to err\n", __func__);
+	for (i = 0; i < MHL_TX_MAX_GPIO; i++)
+		if (pdata->gpios[i])
+			devm_kfree(dev, pdata->gpios[i]);
+	return rc;
+} /* mhl_tx_get_dt_data */
+
+static int mhl_sii_reset_pin(struct mhl_tx_ctrl *mhl_ctrl, int on)
+{
+	gpio_set_value(mhl_ctrl->pdata->gpios[MHL_TX_RESET_GPIO]->gpio,
+		       on);
+	return 0;
+}
+
+static void cbus_reset(struct i2c_client *client)
+{
+	uint8_t i;
+
+	/*
+	 * REG_SRST
+	 */
+	MHL_SII_REG_NAME_MOD(REG_SRST, BIT3, BIT3);
+	msleep(20);
+	MHL_SII_REG_NAME_MOD(REG_SRST, BIT3, 0x00);
+	/*
+	 * REG_INTR1 and REG_INTR4
+	 */
+	MHL_SII_REG_NAME_WR(REG_INTR1_MASK, BIT6);
+	MHL_SII_REG_NAME_WR(REG_INTR4_MASK,
+		BIT0 | BIT2 | BIT3 | BIT4 | BIT5 | BIT6);
+
+	MHL_SII_REG_NAME_WR(REG_INTR5_MASK, 0x00);
+
+	/* Unmask CBUS1 Intrs */
+	MHL_SII_CBUS_WR(0x0009,
+		BIT2 | BIT3 | BIT4 | BIT5 | BIT6);
+
+	/* Unmask CBUS2 Intrs */
+	MHL_SII_CBUS_WR(0x001F, BIT2 | BIT3);
+
+	for (i = 0; i < 4; i++) {
+		/*
+		 * Enable WRITE_STAT interrupt for writes to
+		 * all 4 MSC Status registers.
+		 */
+		MHL_SII_CBUS_WR((0xE0 + i), 0xFF);
+
+		/*
+		 * Enable SET_INT interrupt for writes to
+		 * all 4 MSC Interrupt registers.
+		 */
+		MHL_SII_CBUS_WR((0xF0 + i), 0xFF);
+	}
+	return;
+}
+
+static void init_cbus_regs(struct i2c_client *client)
+{
+	uint8_t		regval;
+
+	/* Increase DDC translation layer timer*/
+	MHL_SII_CBUS_WR(0x0007, 0xF2);
+	/* Drive High Time */
+	MHL_SII_CBUS_WR(0x0036, 0x03);
+	/* Use programmed timing */
+	MHL_SII_CBUS_WR(0x0039, 0x30);
+	/* CBUS Drive Strength */
+	MHL_SII_CBUS_WR(0x0040, 0x03);
+	/*
+	 * Write initial default settings
+	 * to devcap regs: default settings
+	 */
+	MHL_SII_CBUS_WR(0x0080 |
+			  DEVCAP_OFFSET_DEV_STATE, DEVCAP_VAL_DEV_STATE);
+	MHL_SII_CBUS_WR(0x0080 |
+			  DEVCAP_OFFSET_MHL_VERSION, DEVCAP_VAL_MHL_VERSION);
+	MHL_SII_CBUS_WR(0x0080 |
+			  DEVCAP_OFFSET_DEV_CAT, DEVCAP_VAL_DEV_CAT);
+	MHL_SII_CBUS_WR(0x0080 |
+			  DEVCAP_OFFSET_ADOPTER_ID_H, DEVCAP_VAL_ADOPTER_ID_H);
+	MHL_SII_CBUS_WR(0x0080 |
+			  DEVCAP_OFFSET_ADOPTER_ID_L, DEVCAP_VAL_ADOPTER_ID_L);
+	MHL_SII_CBUS_WR(0x0080 | DEVCAP_OFFSET_VID_LINK_MODE,
+			  DEVCAP_VAL_VID_LINK_MODE);
+	MHL_SII_CBUS_WR(0x0080 |
+			  DEVCAP_OFFSET_AUD_LINK_MODE,
+			  DEVCAP_VAL_AUD_LINK_MODE);
+	MHL_SII_CBUS_WR(0x0080 |
+			  DEVCAP_OFFSET_VIDEO_TYPE, DEVCAP_VAL_VIDEO_TYPE);
+	MHL_SII_CBUS_WR(0x0080 |
+			  DEVCAP_OFFSET_LOG_DEV_MAP, DEVCAP_VAL_LOG_DEV_MAP);
+	MHL_SII_CBUS_WR(0x0080 |
+			  DEVCAP_OFFSET_BANDWIDTH, DEVCAP_VAL_BANDWIDTH);
+	MHL_SII_CBUS_WR(0x0080 |
+			  DEVCAP_OFFSET_FEATURE_FLAG, DEVCAP_VAL_FEATURE_FLAG);
+	MHL_SII_CBUS_WR(0x0080 |
+			  DEVCAP_OFFSET_DEVICE_ID_H, DEVCAP_VAL_DEVICE_ID_H);
+	MHL_SII_CBUS_WR(0x0080 |
+			  DEVCAP_OFFSET_DEVICE_ID_L, DEVCAP_VAL_DEVICE_ID_L);
+	MHL_SII_CBUS_WR(0x0080 |
+			  DEVCAP_OFFSET_SCRATCHPAD_SIZE,
+			  DEVCAP_VAL_SCRATCHPAD_SIZE);
+	MHL_SII_CBUS_WR(0x0080 |
+			  DEVCAP_OFFSET_INT_STAT_SIZE,
+			  DEVCAP_VAL_INT_STAT_SIZE);
+	MHL_SII_CBUS_WR(0x0080 |
+			  DEVCAP_OFFSET_RESERVED, DEVCAP_VAL_RESERVED);
+
+	/* Make bits 2,3 (initiator timeout) to 1,1
+	 * for register CBUS_LINK_CONTROL_2
+	 * REG_CBUS_LINK_CONTROL_2
+	 */
+	regval = MHL_SII_CBUS_RD(0x0031);
+	regval = (regval | 0x0C);
+	/* REG_CBUS_LINK_CONTROL_2 */
+	MHL_SII_CBUS_WR(0x0031, regval);
+	 /* REG_MSC_TIMEOUT_LIMIT */
+	MHL_SII_CBUS_WR(0x0022, 0x0F);
+	/* REG_CBUS_LINK_CONTROL_1 */
+	MHL_SII_CBUS_WR(0x0030, 0x01);
+	/* disallow vendor specific commands */
+	MHL_SII_CBUS_MOD(0x002E, BIT4, BIT4);
+}
+
+/*
+ * Configure the initial reg settings
+ */
+static void mhl_init_reg_settings(struct i2c_client *client, bool mhl_disc_en)
+{
+	/*
+	 * ============================================
+	 * POWER UP
+	 * ============================================
+	 */
+
+	/* Power up 1.2V core */
+	MHL_SII_PAGE1_WR(0x003D, 0x3F);
+	/*
+	 * Wait for the source power to be enabled
+	 * before enabling pll clocks.
+	 */
+	msleep(50);
+	/* Enable Tx PLL Clock */
+	MHL_SII_PAGE2_WR(0x0011, 0x01);
+	/* Enable Tx Clock Path and Equalizer */
+	MHL_SII_PAGE2_WR(0x0012, 0x11);
+	/* Tx Source Termination ON */
+	MHL_SII_REG_NAME_WR(REG_MHLTX_CTL1, 0x10);
+	/* Enable 1X MHL Clock output */
+	MHL_SII_REG_NAME_WR(REG_MHLTX_CTL6, 0xAC);
+	/* Tx Differential Driver Config */
+	MHL_SII_REG_NAME_WR(REG_MHLTX_CTL2, 0x3C);
+	MHL_SII_REG_NAME_WR(REG_MHLTX_CTL4, 0xD9);
+	/* PLL Bandwidth Control */
+	MHL_SII_REG_NAME_WR(REG_MHLTX_CTL8, 0x02);
+	/*
+	 * ============================================
+	 * Analog PLL Control
+	 * ============================================
+	 */
+	/* Enable Rx PLL clock */
+	MHL_SII_REG_NAME_WR(REG_TMDS_CCTRL,  0x00);
+	MHL_SII_PAGE0_WR(0x00F8, 0x0C);
+	MHL_SII_PAGE0_WR(0x0085, 0x02);
+	MHL_SII_PAGE2_WR(0x0000, 0x00);
+	MHL_SII_PAGE2_WR(0x0013, 0x60);
+	/* PLL Cal ref sel */
+	MHL_SII_PAGE2_WR(0x0017, 0x03);
+	/* VCO Cal */
+	MHL_SII_PAGE2_WR(0x001A, 0x20);
+	/* Auto EQ */
+	MHL_SII_PAGE2_WR(0x0022, 0xE0);
+	MHL_SII_PAGE2_WR(0x0023, 0xC0);
+	MHL_SII_PAGE2_WR(0x0024, 0xA0);
+	MHL_SII_PAGE2_WR(0x0025, 0x80);
+	MHL_SII_PAGE2_WR(0x0026, 0x60);
+	MHL_SII_PAGE2_WR(0x0027, 0x40);
+	MHL_SII_PAGE2_WR(0x0028, 0x20);
+	MHL_SII_PAGE2_WR(0x0029, 0x00);
+	/* Rx PLL Bandwidth 4MHz */
+	MHL_SII_PAGE2_WR(0x0031, 0x0A);
+	/* Rx PLL Bandwidth value from I2C */
+	MHL_SII_PAGE2_WR(0x0045, 0x06);
+	MHL_SII_PAGE2_WR(0x004B, 0x06);
+	/* Manual zone control */
+	MHL_SII_PAGE2_WR(0x004C, 0xE0);
+	/* PLL Mode value */
+	MHL_SII_PAGE2_WR(0x004D, 0x00);
+	MHL_SII_PAGE0_WR(0x0008, 0x35);
+	/*
+	 * Discovery Control and Status regs
+	 * Setting De-glitch time to 50 ms (default)
+	 * Switch Control Disabled
+	 */
+	MHL_SII_REG_NAME_WR(REG_DISC_CTRL2, 0xAD);
+	/* 1.8V CBUS VTH */
+	MHL_SII_REG_NAME_WR(REG_DISC_CTRL5, 0x55);
+	/* RGND and single Discovery attempt */
+	MHL_SII_REG_NAME_WR(REG_DISC_CTRL6, 0x11);
+	/* Ignore VBUS */
+	MHL_SII_REG_NAME_WR(REG_DISC_CTRL8, 0x82);
+	MHL_SII_REG_NAME_WR(REG_DISC_CTRL9, 0x24);
+
+	/* Enable CBUS Discovery */
+	if (mhl_disc_en) {
+		/* Enable MHL Discovery */
+		MHL_SII_REG_NAME_WR(REG_DISC_CTRL1, 0x27);
+		/* Pull-up resistance off for IDLE state */
+		MHL_SII_REG_NAME_WR(REG_DISC_CTRL4, 0xA4);
+	} else {
+		/* Disable MHL Discovery */
+		MHL_SII_REG_NAME_WR(REG_DISC_CTRL1, 0x26);
+		MHL_SII_REG_NAME_WR(REG_DISC_CTRL4, 0x8C);
+	}
+
+	MHL_SII_REG_NAME_WR(REG_DISC_CTRL7, 0x20);
+	/* MHL CBUS Discovery - immediate comm.  */
+	MHL_SII_REG_NAME_WR(REG_DISC_CTRL3, 0x86);
+
+	MHL_SII_REG_NAME_MOD(REG_INT_CTRL, BIT5 | BIT4, BIT4);
+
+	/* Enable Auto Soft RESET */
+	MHL_SII_REG_NAME_WR(REG_SRST, 0x084);
+	/* HDMI Transcode mode enable */
+	MHL_SII_PAGE0_WR(0x000D, 0x1C);
+
+	cbus_reset(client);
+	init_cbus_regs(client);
+}
+
+
+static void switch_mode(struct mhl_tx_ctrl *mhl_ctrl, enum mhl_st_type to_mode)
+{
+	struct i2c_client *client = mhl_ctrl->i2c_handle;
+
+	switch (to_mode) {
+	case POWER_STATE_D0_NO_MHL:
+		break;
+	case POWER_STATE_D0_MHL:
+		mhl_init_reg_settings(client, true);
+		/* REG_DISC_CTRL1 */
+		MHL_SII_REG_NAME_MOD(REG_DISC_CTRL1, BIT1 | BIT0, BIT0);
+
+		/* TPI_DEVICE_POWER_STATE_CTRL_REG */
+		mhl_i2c_reg_modify(client, TX_PAGE_TPI, 0x001E, BIT1 | BIT0,
+			0x00);
+		break;
+	case POWER_STATE_D3:
+		if (mhl_ctrl->cur_state == POWER_STATE_D3)
+			break;
+
+		/* Force HPD to 0 when not in MHL mode.  */
+		mhl_drive_hpd(mhl_ctrl, HPD_DOWN);
+		/*
+		 * Change TMDS termination to high impedance
+		 * on disconnection.
+		 */
+		MHL_SII_REG_NAME_WR(REG_MHLTX_CTL1, 0xD0);
+		msleep(50);
+		MHL_SII_REG_NAME_MOD(REG_DISC_CTRL1, BIT1 | BIT0, 0x00);
+		MHL_SII_PAGE3_MOD(0x003D, BIT0,
+				   0x00);
+		mhl_ctrl->cur_state = POWER_STATE_D3;
+		break;
+	default:
+		break;
+	}
+}
+
+static void mhl_drive_hpd(struct mhl_tx_ctrl *mhl_ctrl, uint8_t to_state)
+{
+	struct i2c_client *client = mhl_ctrl->i2c_handle;
+
+	pr_debug("%s: To state=[0x%x]\n", __func__, to_state);
+	if (to_state == HPD_UP) {
+		/*
+		 * Drive HPD to UP state
+		 *
+		 * The below two reg configs combined
+		 * enable TMDS output.
+		 */
+
+		/* Enable TMDS on TMDS_CCTRL */
+		MHL_SII_REG_NAME_MOD(REG_TMDS_CCTRL, BIT4, BIT4);
+
+		/*
+		 * Set HPD_OUT_OVR_EN = HPD State
+		 * EDID read and Un-force HPD (from low)
+		 * propogate to src let HPD float by clearing
+		 * HPD OUT OVRRD EN
+		 */
+		MHL_SII_REG_NAME_MOD(REG_INT_CTRL, BIT4, 0x00);
+	} else {
+		/*
+		 * Drive HPD to DOWN state
+		 * Disable TMDS Output on REG_TMDS_CCTRL
+		 * Enable/Disable TMDS output (MHL TMDS output only)
+		 */
+		MHL_SII_REG_NAME_MOD(REG_INT_CTRL, BIT4, BIT4);
+		MHL_SII_REG_NAME_MOD(REG_TMDS_CCTRL, BIT4, 0x00);
+	}
+	return;
+}
+
+static void mhl_msm_connection(struct mhl_tx_ctrl *mhl_ctrl)
+{
+	uint8_t val;
+	struct i2c_client *client = mhl_ctrl->i2c_handle;
+
+	pr_debug("%s: cur st [0x%x]\n", __func__,
+		mhl_ctrl->cur_state);
+
+	if (mhl_ctrl->cur_state == POWER_STATE_D0_MHL) {
+		/* Already in D0 - MHL power state */
+		pr_err("%s: cur st not D0\n", __func__);
+		return;
+	}
+	/* spin_lock_irqsave(&mhl_state_lock, flags); */
+	mhl_ctrl->cur_state = POWER_STATE_D0_MHL;
+	/* spin_unlock_irqrestore(&mhl_state_lock, flags); */
+
+	MHL_SII_REG_NAME_WR(REG_MHLTX_CTL1, 0x10);
+	MHL_SII_CBUS_WR(0x07, 0xF2);
+
+	/*
+	 * Keep the discovery enabled. Need RGND interrupt
+	 * Possibly chip disables discovery after MHL_EST??
+	 * Need to re-enable here
+	 */
+	val = MHL_SII_PAGE3_RD(0x10);
+	MHL_SII_PAGE3_WR(0x10, val | BIT0);
+
+	return;
+}
+
+static void mhl_msm_disconnection(struct mhl_tx_ctrl *mhl_ctrl)
+{
+	struct i2c_client *client = mhl_ctrl->i2c_handle;
+	/*
+	 * MHL TX CTL1
+	 * Disabling Tx termination
+	 */
+	MHL_SII_PAGE3_WR(0x30, 0xD0);
+
+	switch_mode(mhl_ctrl, POWER_STATE_D3);
+	/*
+	 * Only if MHL-USB handshake is not implemented
+	 */
+	mhl_init_reg_settings(client, true);
+	return;
+}
+
+static int  mhl_msm_read_rgnd_int(struct mhl_tx_ctrl *mhl_ctrl)
+{
+	uint8_t rgnd_imp;
+	struct i2c_client *client = mhl_ctrl->i2c_handle;
+	/* DISC STATUS REG 2 */
+	rgnd_imp = (mhl_i2c_reg_read(client,
+				     TX_PAGE_3, 0x001C) & (BIT1 | BIT0));
+	pr_debug("imp range read=%02X\n", (int)rgnd_imp);
+
+	if (0x02 == rgnd_imp) {
+		pr_debug("%s: mhl sink\n", __func__);
+		MHL_SII_REG_NAME_MOD(REG_DISC_CTRL9, BIT0, BIT0);
+		mhl_ctrl->mhl_mode = 1;
+	} else {
+		pr_debug("%s: non-mhl sink\n", __func__);
+		mhl_ctrl->mhl_mode = 0;
+		MHL_SII_REG_NAME_MOD(REG_DISC_CTRL9, BIT3, BIT3);
+		switch_mode(mhl_ctrl, POWER_STATE_D3);
+	}
+	return mhl_ctrl->mhl_mode ?
+		MHL_DISCOVERY_RESULT_MHL : MHL_DISCOVERY_RESULT_USB;
+}
+
+static void force_usb_switch_open(struct mhl_tx_ctrl *mhl_ctrl)
+{
+	struct i2c_client *client = mhl_ctrl->i2c_handle;
+
+	/*disable discovery*/
+	MHL_SII_REG_NAME_MOD(REG_DISC_CTRL1, BIT0, 0);
+	/* force USB ID switch to open*/
+	MHL_SII_REG_NAME_MOD(REG_DISC_CTRL6, BIT6, BIT6);
+	MHL_SII_REG_NAME_WR(REG_DISC_CTRL3, 0x86);
+	/* force HPD to 0 when not in mhl mode. */
+	MHL_SII_REG_NAME_MOD(REG_INT_CTRL, BIT5 | BIT4, BIT4);
+}
+
+static void release_usb_switch_open(struct mhl_tx_ctrl *mhl_ctrl)
+{
+	struct i2c_client *client = mhl_ctrl->i2c_handle;
+
+	msleep(50);
+	MHL_SII_REG_NAME_MOD(REG_DISC_CTRL6, BIT6, 0x00);
+	MHL_SII_REG_NAME_MOD(REG_DISC_CTRL1, BIT0, BIT0);
+}
+
+static void scdt_st_chg(struct i2c_client *client)
+{
+	uint8_t tmds_cstat;
+	uint8_t mhl_fifo_status;
+
+	/* tmds cstat */
+	tmds_cstat = MHL_SII_PAGE3_RD(0x0040);
+	pr_debug("%s: tmds cstat: 0x%02x\n", __func__,
+		 tmds_cstat);
+
+	if (!(tmds_cstat & BIT1))
+		return;
+
+	mhl_fifo_status = MHL_SII_REG_NAME_RD(REG_INTR5);
+	pr_debug("%s: mhl fifo st: 0x%02x\n", __func__,
+		 mhl_fifo_status);
+	if (mhl_fifo_status & 0x0C) {
+		MHL_SII_REG_NAME_WR(REG_INTR5,  0x0C);
+		pr_debug("%s: mhl fifo rst\n", __func__);
+		MHL_SII_REG_NAME_WR(REG_SRST, 0x94);
+		MHL_SII_REG_NAME_WR(REG_SRST, 0x84);
+	}
+}
+
+
+static void dev_detect_isr(struct mhl_tx_ctrl *mhl_ctrl)
+{
+	uint8_t status, reg ;
+	struct i2c_client *client = mhl_ctrl->i2c_handle;
+
+	/* INTR_STATUS4 */
+	status = MHL_SII_REG_NAME_RD(REG_INTR4);
+	pr_debug("%s: reg int4 st=%02X\n", __func__, status);
+
+	if ((0x00 == status) &&\
+	    (mhl_ctrl->cur_state == POWER_STATE_D3)) {
+		pr_err("%s: invalid intr\n", __func__);
+		return;
+	}
+
+	if (0xFF == status) {
+		pr_debug("%s: invalid intr 0xff\n", __func__);
+		MHL_SII_REG_NAME_WR(REG_INTR4, status);
+		return;
+	}
+
+	if ((status & BIT0) && (mhl_ctrl->chip_rev_id < 1)) {
+		pr_debug("%s: scdt intr\n", __func__);
+		scdt_st_chg(client);
+	}
+
+	if (status & BIT1)
+		pr_debug("mhl: int4 bit1 set\n");
+
+	/* mhl_est interrupt */
+	if (status & BIT2) {
+		pr_debug("%s: mhl_est st=%02X\n", __func__,
+			 (int) status);
+		mhl_msm_connection(mhl_ctrl);
+	} else if (status & BIT3) {
+		pr_debug("%s: uUSB-a type dev detct\n", __func__);
+		MHL_SII_REG_NAME_WR(REG_DISC_STAT2, 0x80);
+		switch_mode(mhl_ctrl, POWER_STATE_D3);
+	}
+
+	if (status & BIT5) {
+		/* clr intr - reg int4 */
+		pr_debug("%s: mhl discon: int4 st=%02X\n", __func__,
+			 (int)status);
+		reg = MHL_SII_REG_NAME_RD(REG_INTR4);
+		MHL_SII_REG_NAME_WR(REG_INTR4, reg);
+		mhl_msm_disconnection(mhl_ctrl);
+	}
+
+	if ((mhl_ctrl->cur_state != POWER_STATE_D0_MHL) &&\
+	    (status & BIT6)) {
+		/* rgnd rdy Intr */
+		pr_debug("%s: rgnd ready intr\n", __func__);
+		switch_mode(mhl_ctrl, POWER_STATE_D0_MHL);
+		mhl_msm_read_rgnd_int(mhl_ctrl);
+	}
+
+	/* Can't succeed at these in D3 */
+	if ((mhl_ctrl->cur_state != POWER_STATE_D3) &&\
+	     (status & BIT4)) {
+		/* cbus lockout interrupt?
+		 * Hardware detection mechanism figures that
+		 * CBUS line is latched and raises this intr
+		 * where we force usb switch open and release
+		 */
+		pr_warn("%s: cbus locked out!\n", __func__);
+		force_usb_switch_open(mhl_ctrl);
+		release_usb_switch_open(mhl_ctrl);
+	}
+	MHL_SII_REG_NAME_WR(REG_INTR4, status);
+
+	return;
+}
+
+static void mhl_misc_isr(struct mhl_tx_ctrl *mhl_ctrl)
+{
+	uint8_t intr_5_stat;
+	struct i2c_client *client = mhl_ctrl->i2c_handle;
+
+	/*
+	 * Clear INT 5
+	 * INTR5 is related to FIFO underflow/overflow reset
+	 * which is handled in 8334 by auto FIFO reset
+	 */
+	intr_5_stat = MHL_SII_REG_NAME_RD(REG_INTR5);
+	MHL_SII_REG_NAME_WR(REG_INTR5,  intr_5_stat);
+}
+
+
+static void mhl_hpd_stat_isr(struct mhl_tx_ctrl *mhl_ctrl)
+{
+	uint8_t intr_1_stat;
+	uint8_t cbus_stat;
+	struct i2c_client *client = mhl_ctrl->i2c_handle;
+
+	/* INTR STATUS 1 */
+	intr_1_stat = MHL_SII_PAGE0_RD(0x0071);
+
+	if (!intr_1_stat)
+		return;
+
+	/* Clear interrupts */
+	MHL_SII_PAGE0_WR(0x0071, intr_1_stat);
+	if (BIT6 & intr_1_stat) {
+		/*
+		 * HPD status change event is pending
+		 * Read CBUS HPD status for this info
+		 * MSC REQ ABRT REASON
+		 */
+		cbus_stat = MHL_SII_CBUS_RD(0x0D);
+		if (BIT6 & cbus_stat)
+			mhl_drive_hpd(mhl_ctrl, HPD_UP);
+	}
+	return;
+}
+
+static void clear_all_intrs(struct i2c_client *client)
+{
+	uint8_t regval = 0x00;
+
+	pr_debug_intr("********* exiting isr mask check ?? *************\n");
+	pr_debug_intr("int1 mask = %02X\n",
+		(int) MHL_SII_REG_NAME_RD(REG_INTR1));
+	pr_debug_intr("int3 mask = %02X\n",
+		(int) MHL_SII_PAGE0_RD(0x0077));
+	pr_debug_intr("int4 mask = %02X\n",
+		(int) MHL_SII_REG_NAME_RD(REG_INTR4));
+	pr_debug_intr("int5 mask = %02X\n",
+		(int) MHL_SII_REG_NAME_RD(REG_INTR5));
+	pr_debug_intr("cbus1 mask = %02X\n",
+		(int) MHL_SII_CBUS_RD(0x0009));
+	pr_debug_intr("cbus2 mask = %02X\n",
+		(int) MHL_SII_CBUS_RD(0x001F));
+	pr_debug_intr("********* end of isr mask check *************\n");
+
+	regval = MHL_SII_REG_NAME_RD(REG_INTR1);
+	pr_debug_intr("int1 st = %02X\n", (int)regval);
+	MHL_SII_REG_NAME_WR(REG_INTR1, regval);
+
+	regval =  MHL_SII_REG_NAME_RD(REG_INTR2);
+	pr_debug_intr("int2 st = %02X\n", (int)regval);
+	MHL_SII_REG_NAME_WR(REG_INTR2, regval);
+
+	regval =  MHL_SII_PAGE0_RD(0x0073);
+	pr_debug_intr("int3 st = %02X\n", (int)regval);
+	MHL_SII_PAGE0_WR(0x0073, regval);
+
+	regval =  MHL_SII_REG_NAME_RD(REG_INTR4);
+	pr_debug_intr("int4 st = %02X\n", (int)regval);
+	MHL_SII_REG_NAME_WR(REG_INTR4, regval);
+
+	regval =  MHL_SII_REG_NAME_RD(REG_INTR5);
+	pr_debug_intr("int5 st = %02X\n", (int)regval);
+	MHL_SII_REG_NAME_WR(REG_INTR5, regval);
+
+	regval =  MHL_SII_CBUS_RD(0x0008);
+	pr_debug_intr("cbusInt st = %02X\n", (int)regval);
+	MHL_SII_CBUS_WR(0x0008, regval);
+
+	regval =  MHL_SII_CBUS_RD(0x001E);
+	pr_debug_intr("CBUS intR_2: %d\n", (int)regval);
+	MHL_SII_CBUS_WR(0x001E, regval);
+
+	regval =  MHL_SII_CBUS_RD(0x00A0);
+	pr_debug_intr("A0 int set = %02X\n", (int)regval);
+	MHL_SII_CBUS_WR(0x00A0, regval);
+
+	regval =  MHL_SII_CBUS_RD(0x00A1);
+	pr_debug_intr("A1 int set = %02X\n", (int)regval);
+	MHL_SII_CBUS_WR(0x00A1, regval);
+
+	regval =  MHL_SII_CBUS_RD(0x00A2);
+	pr_debug_intr("A2 int set = %02X\n", (int)regval);
+	MHL_SII_CBUS_WR(0x00A2, regval);
+
+	regval =  MHL_SII_CBUS_RD(0x00A3);
+	pr_debug_intr("A3 int set = %02X\n", (int)regval);
+	MHL_SII_CBUS_WR(0x00A3, regval);
+
+	regval =  MHL_SII_CBUS_RD(0x00B0);
+	pr_debug_intr("B0 st set = %02X\n", (int)regval);
+	MHL_SII_CBUS_WR(0x00B0, regval);
+
+	regval =  MHL_SII_CBUS_RD(0x00B1);
+	pr_debug_intr("B1 st set = %02X\n", (int)regval);
+	MHL_SII_CBUS_WR(0x00B1, regval);
+
+	regval =  MHL_SII_CBUS_RD(0x00B2);
+	pr_debug_intr("B2 st set = %02X\n", (int)regval);
+	MHL_SII_CBUS_WR(0x00B2, regval);
+
+	regval =  MHL_SII_CBUS_RD(0x00B3);
+	pr_debug_intr("B3 st set = %02X\n", (int)regval);
+	MHL_SII_CBUS_WR(0x00B3, regval);
+
+	regval =  MHL_SII_CBUS_RD(0x00E0);
+	pr_debug_intr("E0 st set = %02X\n", (int)regval);
+	MHL_SII_CBUS_WR(0x00E0, regval);
+
+	regval =  MHL_SII_CBUS_RD(0x00E1);
+	pr_debug_intr("E1 st set = %02X\n", (int)regval);
+	MHL_SII_CBUS_WR(0x00E1, regval);
+
+	regval =  MHL_SII_CBUS_RD(0x00E2);
+	pr_debug_intr("E2 st set = %02X\n", (int)regval);
+	MHL_SII_CBUS_WR(0x00E2, regval);
+
+	regval =  MHL_SII_CBUS_RD(0x00E3);
+	pr_debug_intr("E3 st set = %02X\n", (int)regval);
+	MHL_SII_CBUS_WR(0x00E3, regval);
+
+	regval =  MHL_SII_CBUS_RD(0x00F0);
+	pr_debug_intr("F0 int set = %02X\n", (int)regval);
+	MHL_SII_CBUS_WR(0x00F0, regval);
+
+	regval =  MHL_SII_CBUS_RD(0x00F1);
+	pr_debug_intr("F1 int set = %02X\n", (int)regval);
+	MHL_SII_CBUS_WR(0x00F1, regval);
+
+	regval =  MHL_SII_CBUS_RD(0x00F2);
+	pr_debug_intr("F2 int set = %02X\n", (int)regval);
+	MHL_SII_CBUS_WR(0x00F2, regval);
+
+	regval =  MHL_SII_CBUS_RD(0x00F3);
+	pr_debug_intr("F3 int set = %02X\n", (int)regval);
+	MHL_SII_CBUS_WR(0x00F3, regval);
+	pr_debug_intr("********* end of exiting in isr *************\n");
+}
+
+
+static irqreturn_t mhl_tx_isr(int irq, void *data)
+{
+	struct mhl_tx_ctrl *mhl_ctrl = (struct mhl_tx_ctrl *)data;
+	pr_debug("%s: Getting Interrupts\n", __func__);
+
+	/*
+	 * Check RGND, MHL_EST, CBUS_LOCKOUT, SCDT
+	 * interrupts. In D3, we get only RGND
+	 */
+	dev_detect_isr(mhl_ctrl);
+
+	pr_debug("%s: cur pwr state is [0x%x]\n",
+		 __func__, mhl_ctrl->cur_state);
+	if (mhl_ctrl->cur_state == POWER_STATE_D0_MHL) {
+		/*
+		 * If dev_detect_isr() didn't move the tx to D3
+		 * on disconnect, continue to check other
+		 * interrupt sources.
+		 */
+		mhl_misc_isr(mhl_ctrl);
+
+		/*
+		 * Check for any peer messages for DCAP_CHG etc
+		 * Dispatch to have the CBUS module working only
+		 * once connected.
+		mhl_cbus_isr(mhl_ctrl);
+		 */
+		mhl_hpd_stat_isr(mhl_ctrl);
+	}
+
+	clear_all_intrs(mhl_ctrl->i2c_handle);
+
+	return IRQ_HANDLED;
+}
+
+static int mhl_tx_chip_init(struct mhl_tx_ctrl *mhl_ctrl)
+{
+	uint8_t chip_rev_id = 0x00;
+	struct i2c_client *client = mhl_ctrl->i2c_handle;
+
+	/* Reset the TX chip */
+	mhl_sii_reset_pin(mhl_ctrl, 0);
+	msleep(20);
+	mhl_sii_reset_pin(mhl_ctrl, 1);
+	/* TX PR-guide requires a 100 ms wait here */
+	msleep(100);
+
+	/* Read the chip rev ID */
+	chip_rev_id = MHL_SII_PAGE0_RD(0x04);
+	pr_debug("MHL: chip rev ID read=[%x]\n", chip_rev_id);
+
+	/*
+	 * Need to disable MHL discovery if
+	 * MHL-USB handshake is implemented
+	 */
+	mhl_init_reg_settings(client, true);
+	return 0;
+}
+
+static int mhl_sii_reg_config(struct i2c_client *client, bool enable)
+{
+	static struct regulator *reg_8941_l24;
+	static struct regulator *reg_8941_l02;
+	int rc;
+
+	pr_debug("Inside %s\n", __func__);
+	if (!reg_8941_l24) {
+		reg_8941_l24 = regulator_get(&client->dev,
+			"avcc_18");
+		if (IS_ERR(reg_8941_l24)) {
+			pr_err("could not get reg_8038_l20, rc = %ld\n",
+				PTR_ERR(reg_8941_l24));
+			return -ENODEV;
+		}
+		if (enable)
+			rc = regulator_enable(reg_8941_l24);
+		else
+			rc = regulator_disable(reg_8941_l24);
+		if (rc) {
+			pr_err("'%s' regulator config[%u] failed, rc=%d\n",
+			       "avcc_1.8V", enable, rc);
+			return rc;
+		} else {
+			pr_debug("%s: vreg L24 %s\n",
+				 __func__, (enable ? "enabled" : "disabled"));
+		}
+	}
+
+	if (!reg_8941_l02) {
+		reg_8941_l02 = regulator_get(&client->dev,
+			"avcc_12");
+		if (IS_ERR(reg_8941_l02)) {
+			pr_err("could not get reg_8941_l02, rc = %ld\n",
+				PTR_ERR(reg_8941_l02));
+			return -ENODEV;
+		}
+		if (enable)
+			rc = regulator_enable(reg_8941_l02);
+		else
+			rc = regulator_disable(reg_8941_l02);
+		if (rc) {
+			pr_debug("'%s' regulator configure[%u] failed, rc=%d\n",
+				 "avcc_1.2V", enable, rc);
+			return rc;
+		} else {
+			pr_debug("%s: vreg L02 %s\n",
+				 __func__, (enable ? "enabled" : "disabled"));
+		}
+	}
+
+	return rc;
+}
+
+
+static int mhl_vreg_config(struct mhl_tx_ctrl *mhl_ctrl, uint8_t on)
+{
+	int ret;
+	struct i2c_client *client = mhl_ctrl->i2c_handle;
+	int pwr_gpio = mhl_ctrl->pdata->gpios[MHL_TX_PMIC_PWR_GPIO]->gpio;
+
+	pr_debug("%s\n", __func__);
+	if (on) {
+		ret = gpio_request(pwr_gpio,
+		    mhl_ctrl->pdata->gpios[MHL_TX_PMIC_PWR_GPIO]->gpio_name);
+		if (ret < 0) {
+			pr_err("%s: mhl pwr gpio req failed: %d\n",
+			       __func__, ret);
+			return ret;
+		}
+		ret = gpio_direction_output(pwr_gpio, 1);
+		if (ret < 0) {
+			pr_err("%s: set gpio MHL_PWR_EN dircn failed: %d\n",
+			       __func__, ret);
+			return ret;
+		}
+
+		ret = mhl_sii_reg_config(client, true);
+		if (ret) {
+			pr_err("%s: regulator enable failed\n", __func__);
+			return -EINVAL;
+		}
+		pr_debug("%s: mhl sii power on successful\n", __func__);
+	} else {
+		pr_warn("%s: turning off pwr controls\n", __func__);
+		mhl_sii_reg_config(client, false);
+		gpio_free(pwr_gpio);
+	}
+	pr_debug("%s: successful\n", __func__);
+	return 0;
+}
+
+/*
+ * Request for GPIO allocations
+ * Set appropriate GPIO directions
+ */
+static int mhl_gpio_config(struct mhl_tx_ctrl *mhl_ctrl, int on)
+{
+	int ret;
+	struct dss_gpio *temp_reset_gpio, *temp_intr_gpio;
+
+	/* caused too many line spills */
+	temp_reset_gpio = mhl_ctrl->pdata->gpios[MHL_TX_RESET_GPIO];
+	temp_intr_gpio = mhl_ctrl->pdata->gpios[MHL_TX_INTR_GPIO];
+
+	if (on) {
+		if (gpio_is_valid(temp_reset_gpio->gpio)) {
+			ret = gpio_request(temp_reset_gpio->gpio,
+					   temp_reset_gpio->gpio_name);
+			if (ret < 0) {
+				pr_err("%s:rst_gpio=[%d] req failed:%d\n",
+				       __func__, temp_reset_gpio->gpio, ret);
+				return -EBUSY;
+			}
+			ret = gpio_direction_output(temp_reset_gpio->gpio, 0);
+			if (ret < 0) {
+				pr_err("%s: set dirn rst failed: %d\n",
+				       __func__, ret);
+				return -EBUSY;
+			}
+		}
+		if (gpio_is_valid(temp_intr_gpio->gpio)) {
+			ret = gpio_request(temp_intr_gpio->gpio,
+					   temp_intr_gpio->gpio_name);
+			if (ret < 0) {
+				pr_err("%s: intr_gpio req failed: %d\n",
+				       __func__, ret);
+				return -EBUSY;
+			}
+			ret = gpio_direction_input(temp_intr_gpio->gpio);
+			if (ret < 0) {
+				pr_err("%s: set dirn intr failed: %d\n",
+				       __func__, ret);
+				return -EBUSY;
+			}
+			mhl_ctrl->i2c_handle->irq = gpio_to_irq(
+				temp_intr_gpio->gpio);
+			pr_debug("%s: gpio_to_irq=%d\n",
+				 __func__, mhl_ctrl->i2c_handle->irq);
+		}
+	} else {
+		pr_warn("%s: freeing gpios\n", __func__);
+		gpio_free(temp_intr_gpio->gpio);
+		gpio_free(temp_reset_gpio->gpio);
+	}
+	pr_debug("%s: successful\n", __func__);
+	return 0;
+}
+
+static int mhl_i2c_probe(struct i2c_client *client,
+			 const struct i2c_device_id *id)
+{
+	int rc = 0;
+	struct mhl_tx_platform_data *pdata = NULL;
+	struct mhl_tx_ctrl *mhl_ctrl;
+
+	mhl_ctrl = devm_kzalloc(&client->dev, sizeof(*mhl_ctrl), GFP_KERNEL);
+	if (!mhl_ctrl) {
+		pr_err("%s: FAILED: cannot alloc hdmi tx ctrl\n", __func__);
+		rc = -ENOMEM;
+		goto failed_no_mem;
+	}
+
+	if (client->dev.of_node) {
+		pdata = devm_kzalloc(&client->dev,
+			     sizeof(struct mhl_tx_platform_data), GFP_KERNEL);
+		if (!pdata) {
+			dev_err(&client->dev, "Failed to allocate memory\n");
+			rc = -ENOMEM;
+			goto failed_no_mem;
+		}
+
+		rc = mhl_tx_get_dt_data(&client->dev, pdata);
+		if (rc) {
+			pr_err("%s: FAILED: parsing device tree data; rc=%d\n",
+				__func__, rc);
+			goto failed_dt_data;
+		}
+		mhl_ctrl->i2c_handle = client;
+		mhl_ctrl->pdata = pdata;
+		i2c_set_clientdata(client, mhl_ctrl);
+	}
+
+	/*
+	 * Regulator init
+	 */
+	rc = mhl_vreg_config(mhl_ctrl, 1);
+	if (rc) {
+		pr_err("%s: vreg init failed [%d]\n",
+			__func__, rc);
+		goto failed_probe;
+	}
+
+	/*
+	 * GPIO init
+	 */
+	rc = mhl_gpio_config(mhl_ctrl, 1);
+	if (rc) {
+		pr_err("%s: gpio init failed [%d]\n",
+			__func__, rc);
+		goto failed_probe;
+	}
+
+	/*
+	 * Other initializations
+	 * such tx specific
+	 */
+	rc = mhl_tx_chip_init(mhl_ctrl);
+	if (rc) {
+		pr_err("%s: tx chip init failed [%d]\n",
+			__func__, rc);
+		goto failed_probe;
+	}
+
+	pr_debug("%s: IRQ from GPIO INTR = %d\n",
+		__func__, mhl_ctrl->i2c_handle->irq);
+	pr_debug("%s: Driver name = [%s]\n", __func__,
+		client->dev.driver->name);
+	rc = request_threaded_irq(mhl_ctrl->i2c_handle->irq, NULL,
+				   &mhl_tx_isr,
+				  IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+				 client->dev.driver->name, mhl_ctrl);
+	if (rc) {
+		pr_err("request_threaded_irq failed, status: %d\n",
+			rc);
+		goto failed_probe;
+	} else {
+		pr_debug("request_threaded_irq succeeded\n");
+	}
+	pr_debug("%s: i2c client addr is [%x]\n", __func__, client->addr);
+	return 0;
+failed_probe:
+failed_dt_data:
+	if (pdata)
+		devm_kfree(&client->dev, pdata);
+failed_no_mem:
+	if (mhl_ctrl)
+		devm_kfree(&client->dev, mhl_ctrl);
+	pr_err("%s: PROBE FAILED, rc=%d\n", __func__, rc);
+	return rc;
+}
+
+
+static int mhl_i2c_remove(struct i2c_client *client)
+{
+	struct mhl_tx_ctrl *mhl_ctrl = i2c_get_clientdata(client);
+
+	if (!mhl_ctrl) {
+		pr_warn("%s: i2c get client data failed\n", __func__);
+		return -EINVAL;
+	}
+
+	free_irq(mhl_ctrl->i2c_handle->irq, mhl_ctrl);
+	mhl_gpio_config(mhl_ctrl, 0);
+	mhl_vreg_config(mhl_ctrl, 0);
+	if (mhl_ctrl->pdata)
+		devm_kfree(&client->dev, mhl_ctrl->pdata);
+	devm_kfree(&client->dev, mhl_ctrl);
+	return 0;
+}
+
+static struct i2c_device_id mhl_sii_i2c_id[] = {
+	{ MHL_DRIVER_NAME, 0 },
+	{ }
+};
+
+
+MODULE_DEVICE_TABLE(i2c, mhl_sii_i2c_id);
+
+static struct of_device_id mhl_match_table[] = {
+	{.compatible = COMPATIBLE_NAME,},
+	{ },
+};
+
+static struct i2c_driver mhl_sii_i2c_driver = {
+	.driver = {
+		.name = MHL_DRIVER_NAME,
+		.owner = THIS_MODULE,
+		.of_match_table = mhl_match_table,
+	},
+	.probe = mhl_i2c_probe,
+	.remove =  mhl_i2c_remove,
+	.id_table = mhl_sii_i2c_id,
+};
+
+module_i2c_driver(mhl_sii_i2c_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("MHL SII 8334 TX Driver");
diff --git a/drivers/video/msm/mipi_dsi.c b/drivers/video/msm/mipi_dsi.c
index 7d534ed..cc19555 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
@@ -442,9 +446,6 @@
 	if (pdev_list_cnt >= MSM_FB_MAX_DEV_LIST)
 		return -ENOMEM;
 
-	if (!mfd->cont_splash_done)
-		cont_splash_clk_ctrl(1);
-
 	mdp_dev = platform_device_alloc("mdp", pdev->id);
 	if (!mdp_dev)
 		return -ENOMEM;
@@ -580,6 +581,9 @@
 
 	pdev_list[pdev_list_cnt++] = pdev;
 
+	if (!mfd->cont_splash_done)
+		cont_splash_clk_ctrl(1);
+
 return 0;
 
 mipi_dsi_probe_err:
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..ecac82d 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,16 @@
 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 &&
+	    mipi_novatek_pdata->gpio_set_backlight) {
+		mipi_novatek_pdata->gpio_set_backlight(mfd->bl_level);
+		return;
+	}
 
 	if ((mipi_novatek_pdata->enable_wled_bl_ctrl)
 	    && (wled_trigger_initialized)) {
@@ -446,7 +476,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/mipi_novatek_cmd_qhd_pt.c b/drivers/video/msm/mipi_novatek_cmd_qhd_pt.c
index f139f4c..4c1aa63 100644
--- a/drivers/video/msm/mipi_novatek_cmd_qhd_pt.c
+++ b/drivers/video/msm/mipi_novatek_cmd_qhd_pt.c
@@ -64,7 +64,7 @@
 	pinfo.is_3d_panel = FB_TYPE_3D_PANEL;
 	pinfo.lcd.vsync_enable = TRUE;
 	pinfo.lcd.hw_vsync_mode = TRUE;
-	pinfo.lcd.refx100 = 6000; /* adjust refx100 to prevent tearing */
+	pinfo.lcd.refx100 = 6200; /* adjust refx100 to prevent tearing */
 	pinfo.lcd.v_back_porch = 11;
 	pinfo.lcd.v_front_porch = 10;
 	pinfo.lcd.v_pulse_width = 5;
diff --git a/drivers/video/msm/msm_fb.c b/drivers/video/msm/msm_fb.c
index 1994b1b..993ec01 100644
--- a/drivers/video/msm/msm_fb.c
+++ b/drivers/video/msm/msm_fb.c
@@ -532,6 +532,7 @@
 	mfd->suspend.sw_refreshing_enable = mfd->sw_refreshing_enable;
 	mfd->suspend.op_enable = mfd->op_enable;
 	mfd->suspend.panel_power_on = mfd->panel_power_on;
+	mfd->suspend.op_suspend = true;
 
 	if (mfd->op_enable) {
 		ret =
@@ -597,6 +598,8 @@
 			MSM_FB_INFO("msm_fb_resume: can't turn on display!\n");
 	}
 
+	mfd->suspend.op_suspend = false;
+
 	return ret;
 }
 #endif
@@ -653,12 +656,6 @@
 	struct msm_fb_panel_data *pdata = NULL;
 	int ret = 0;
 
-	if (hdmi_prim_display) {
-		MSM_FB_INFO("%s: hdmi primary handles early suspend only\n",
-			__func__);
-		return 0;
-	}
-
 	if ((!mfd) || (mfd->key != MFD_KEY))
 		return 0;
 
@@ -684,12 +681,6 @@
 	struct msm_fb_panel_data *pdata = NULL;
 	int ret = 0;
 
-	if (hdmi_prim_display) {
-		MSM_FB_INFO("%s: hdmi primary handles early resume only\n",
-			__func__);
-		return 0;
-	}
-
 	if ((!mfd) || (mfd->key != MFD_KEY))
 		return 0;
 
@@ -714,8 +705,7 @@
 	.runtime_suspend = msm_fb_runtime_suspend,
 	.runtime_resume = msm_fb_runtime_resume,
 	.runtime_idle = msm_fb_runtime_idle,
-#if (defined(CONFIG_SUSPEND) && defined(CONFIG_FB_MSM_HDMI_MSM_PANEL) && \
-	!defined(CONFIG_FB_MSM_HDMI_AS_PRIMARY))
+#if (defined(CONFIG_SUSPEND) && defined(CONFIG_FB_MSM_HDMI_MSM_PANEL))
 	.suspend = msm_fb_ext_suspend,
 	.resume = msm_fb_ext_resume,
 #endif
@@ -749,9 +739,7 @@
 static void msmfb_early_suspend(struct early_suspend *h)
 {
 	struct msm_fb_data_type *mfd = container_of(h, struct msm_fb_data_type,
-						early_suspend);
-	struct msm_fb_panel_data *pdata = NULL;
-
+						    early_suspend);
 #if defined(CONFIG_FB_MSM_MDP303)
 	/*
 	* For MDP with overlay, set framebuffer with black pixels
@@ -769,37 +757,12 @@
 	}
 #endif
 	msm_fb_suspend_sub(mfd);
-
-	pdata = (struct msm_fb_panel_data *)mfd->pdev->dev.platform_data;
-	if (hdmi_prim_display &&
-		(mfd->panel_info.type == HDMI_PANEL ||
-		 mfd->panel_info.type == DTV_PANEL)) {
-		/* Turn off the HPD circuitry */
-		if (pdata->power_ctrl) {
-			MSM_FB_INFO("%s: Turning off HPD circuitry\n",
-				__func__);
-			pdata->power_ctrl(FALSE);
-		}
-	}
 }
 
 static void msmfb_early_resume(struct early_suspend *h)
 {
 	struct msm_fb_data_type *mfd = container_of(h, struct msm_fb_data_type,
-						early_suspend);
-	struct msm_fb_panel_data *pdata = NULL;
-
-	pdata = (struct msm_fb_panel_data *)mfd->pdev->dev.platform_data;
-	if (hdmi_prim_display &&
-		(mfd->panel_info.type == HDMI_PANEL ||
-		 mfd->panel_info.type == DTV_PANEL)) {
-		/* Turn on the HPD circuitry */
-		if (pdata->power_ctrl) {
-			MSM_FB_INFO("%s: Turning on HPD circuitry\n", __func__);
-			pdata->power_ctrl(TRUE);
-		}
-	}
-
+						    early_suspend);
 	msm_fb_resume_sub(mfd);
 }
 #endif
@@ -1510,10 +1473,7 @@
 	ret = 0;
 
 #ifdef CONFIG_HAS_EARLYSUSPEND
-
-	if (hdmi_prim_display ||
-	    (mfd->panel_info.type != DTV_PANEL &&
-	     mfd->panel_info.type != WRITEBACK_PANEL)) {
+	if (mfd->panel_info.type != DTV_PANEL) {
 		mfd->early_suspend.suspend = msmfb_early_suspend;
 		mfd->early_suspend.resume = msmfb_early_resume;
 		mfd->early_suspend.level = EARLY_SUSPEND_LEVEL_DISABLE_FB - 2;
@@ -2961,17 +2921,9 @@
 	return ret;
 }
 
-static int msmfb_overlay_commit(struct fb_info *info, unsigned long *argp)
+static int msmfb_overlay_commit(struct fb_info *info)
 {
-	int ret, ndx;
-
-	ret = copy_from_user(&ndx, argp, sizeof(ndx));
-	if (ret) {
-		pr_err("%s: ioctl failed\n", __func__);
-		return ret;
-	}
-
-	return mdp4_overlay_commit(info, ndx);
+	return mdp4_overlay_commit(info);
 }
 
 static int msmfb_overlay_play(struct fb_info *info, unsigned long *argp)
@@ -3279,7 +3231,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,
@@ -3403,7 +3357,7 @@
 		break;
 	case MSMFB_OVERLAY_COMMIT:
 		down(&msm_fb_ioctl_ppp_sem);
-		ret = msmfb_overlay_commit(info, argp);
+		ret = msmfb_overlay_commit(info);
 		up(&msm_fb_ioctl_ppp_sem);
 		break;
 	case MSMFB_OVERLAY_PLAY:
diff --git a/drivers/video/msm/msm_fb.h b/drivers/video/msm/msm_fb.h
index 1594825..9c4f3d3 100644
--- a/drivers/video/msm/msm_fb.h
+++ b/drivers/video/msm/msm_fb.h
@@ -53,6 +53,7 @@
 	boolean op_enable;
 	boolean sw_refreshing_enable;
 	boolean panel_power_on;
+	boolean op_suspend;
 };
 
 struct msmfb_writeback_data_list {
@@ -125,6 +126,7 @@
 	__u32 channel_irq;
 
 	struct mdp_dma_data *dma;
+	struct device_attribute dev_attr;
 	void (*dma_fnc) (struct msm_fb_data_type *mfd);
 	int (*cursor_update) (struct fb_info *info,
 			      struct fb_cursor *cursor);
@@ -135,6 +137,8 @@
 	int (*start_histogram) (struct mdp_histogram_start_req *req);
 	int (*stop_histogram) (struct fb_info *info, uint32_t block);
 	void (*vsync_ctrl) (int enable);
+	void (*vsync_init) (int cndx);
+	void *vsync_show;
 	void *cursor_buf;
 	void *cursor_buf_phys;
 
@@ -190,6 +194,7 @@
 	unsigned char *copy_splash_phys;
 	void *cpu_pm_hdl;
 	u32 avtimer_phy;
+	int vsync_sysfs_created;
 };
 
 struct dentry *msm_fb_get_debugfs_root(void);
@@ -214,8 +219,7 @@
 void msm_fb_config_backlight(struct msm_fb_data_type *mfd);
 #endif
 
-void fill_black_screen(void);
-void unfill_black_screen(void);
+void fill_black_screen(bool on, uint8 pipe_num, uint8 mixer_num);
 int msm_fb_check_frame_rate(struct msm_fb_data_type *mfd,
 				struct fb_info *info);
 
diff --git a/drivers/video/msm/msm_fb_panel.h b/drivers/video/msm/msm_fb_panel.h
index 7fc7a2f..505928e 100644
--- a/drivers/video/msm/msm_fb_panel.h
+++ b/drivers/video/msm/msm_fb_panel.h
@@ -56,6 +56,11 @@
 	MAX_PHYS_TARGET_NUM,
 } DISP_TARGET_PHYS;
 
+enum {
+	BLT_SWITCH_TG_OFF,
+	BLT_SWITCH_TG_ON
+};
+
 /* panel info type */
 struct lcd_panel_info {
 	__u32 vsync_enable;
@@ -65,6 +70,8 @@
 	__u32 v_pulse_width;
 	__u32 hw_vsync_mode;
 	__u32 vsync_notifier_period;
+	__u32 blt_ctrl;
+	__u32 blt_mode;
 	__u32 rev;
 };
 
@@ -198,6 +205,18 @@
 	int (*clk_func) (int enable);
 };
 
+enum {
+	MDP4_OVERLAY_BLT_SWITCH_TG_OFF,
+	MDP4_OVERLAY_BLT_SWITCH_TG_ON,
+	MDP4_OVERLAY_BLT_SWITCH_POLL
+};
+
+enum {
+	MDP4_OVERLAY_MODE_BLT_CTRL,
+	MDP4_OVERLAY_MODE_BLT_ALWAYS_ON,
+	MDP4_OVERLAY_MODE_BLT_ALWAYS_OFF
+};
+
 /*===========================================================================
   FUNCTIONS PROTOTYPES
 ============================================================================*/
diff --git a/drivers/video/msm/vidc/1080p/ddl/vcd_ddl.c b/drivers/video/msm/vidc/1080p/ddl/vcd_ddl.c
index d5b195d..a82feb9 100644
--- a/drivers/video/msm/vidc/1080p/ddl/vcd_ddl.c
+++ b/drivers/video/msm/vidc/1080p/ddl/vcd_ddl.c
@@ -198,7 +198,8 @@
 		ddl->client_state = DDL_CLIENT_OPEN;
 		ddl->codec_data.hdr.decoding = decoding;
 		ddl->decoding = decoding;
-		ddl_set_default_meta_data_hdr(ddl);
+		if (!res_trk_check_for_sec_session())
+			ddl_set_default_meta_data_hdr(ddl);
 		ddl_set_initial_default_values(ddl);
 		*ddl_handle	= (u32 *) ddl;
 	} else {
diff --git a/drivers/video/msm/vidc/1080p/ddl/vcd_ddl.h b/drivers/video/msm/vidc/1080p/ddl/vcd_ddl.h
index 2a850d8..7a1d521 100644
--- a/drivers/video/msm/vidc/1080p/ddl/vcd_ddl.h
+++ b/drivers/video/msm/vidc/1080p/ddl/vcd_ddl.h
@@ -293,6 +293,7 @@
 	struct vcd_property_slice_delivery_info slice_delivery_info;
 	struct ddl_batch_frame_data batch_frame;
 	u32 avc_delimiter_enable;
+	u32 vui_timinginfo_enable;
 };
 struct ddl_decoder_data {
 	struct ddl_codec_data_hdr  hdr;
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..78f96c8 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;
@@ -1258,7 +1259,8 @@
 				output_vcd_frm->flags |=
 					VCD_FRAME_FLAG_DATACORRUPT;
 		}
-		if (decoder->codec.codec != VCD_CODEC_H264)
+		if (decoder->codec.codec != VCD_CODEC_H264 ||
+			decoder->codec.codec != VCD_CODEC_MPEG2)
 			output_vcd_frm->flags &= ~VCD_FRAME_FLAG_DATACORRUPT;
 		output_vcd_frm->ip_frm_tag = dec_disp_info->tag_top;
 		vidc_sm_get_picture_times(&ddl->shared_mem
diff --git a/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_properties.c b/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_properties.c
index d6558c3..332497f 100644
--- a/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_properties.c
+++ b/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_properties.c
@@ -1061,6 +1061,20 @@
 		}
 		break;
 	}
+	case VCD_I_ENABLE_VUI_TIMING_INFO:
+	{
+		struct vcd_property_vui_timing_info_enable *vui_timing_enable =
+			(struct vcd_property_vui_timing_info_enable *)
+				property_value;
+		if (sizeof(struct vcd_property_vui_timing_info_enable) ==
+			property_hdr->sz &&
+			encoder->codec.codec == VCD_CODEC_H264) {
+			encoder->vui_timinginfo_enable =
+			vui_timing_enable->vui_timing_info;
+			vcd_status = VCD_S_SUCCESS;
+		}
+		break;
+	}
 	default:
 		DDL_MSG_ERROR("INVALID ID %d\n", (int)property_hdr->prop_id);
 		vcd_status = VCD_ERR_ILLEGAL_OP;
@@ -1553,6 +1567,15 @@
 			vcd_status = VCD_S_SUCCESS;
 		}
 		break;
+	case VCD_I_ENABLE_VUI_TIMING_INFO:
+		if (sizeof(struct vcd_property_vui_timing_info_enable) ==
+			property_hdr->sz) {
+			((struct vcd_property_vui_timing_info_enable *)
+				property_value)->vui_timing_info =
+					encoder->vui_timinginfo_enable;
+			vcd_status = VCD_S_SUCCESS;
+		}
+		break;
 	default:
 		vcd_status = VCD_ERR_ILLEGAL_OP;
 		break;
@@ -1715,6 +1738,7 @@
 	encoder->slice_delivery_info.num_slices = 0;
 	encoder->slice_delivery_info.num_slices_enc = 0;
 	encoder->avc_delimiter_enable = 0;
+	encoder->vui_timinginfo_enable = 0;
 }
 
 static void ddl_set_default_enc_profile(struct ddl_encoder_data *encoder)
diff --git a/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_shared_mem.c b/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_shared_mem.c
index 40dc2aa..de3fc4f 100644
--- a/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_shared_mem.c
+++ b/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_shared_mem.c
@@ -83,6 +83,8 @@
 #define VIDC_SM_ENC_EXT_CTRL_ADDR                    0x0028
 #define VIDC_SM_ENC_EXT_CTRL_VBV_BUFFER_SIZE_BMSK    0xffff0000
 #define VIDC_SM_ENC_EXT_CTRL_VBV_BUFFER_SIZE_SHFT    16
+#define VIDC_SM_ENC_EXT_CTRL_TIMING_INFO_EN_BMSK     0x00004000
+#define VIDC_SM_ENC_EXT_CTRL_TIMING_INFO_EN_SHFT     14
 #define VIDC_SM_ENC_EXT_CTRL_AU_DELIMITER_EN_BMSK    0x00000800
 #define VIDC_SM_ENC_EXT_CTRL_AU_DELIMITER_EN_SHFT    11
 #define VIDC_SM_ENC_EXT_CTRL_H263_CPCFC_ENABLE_BMSK  0x80
@@ -176,6 +178,13 @@
 #define VIDC_SM_ENC_NUM_OF_SLICE_COMP_ADDR                        0x01d0
 #define VIDC_SM_ENC_NUM_OF_SLICE_COMP_VALUE_BMSK                  0xffffffff
 #define VIDC_SM_ENC_NUM_OF_SLICE_COMP_VALUE_SHFT                  0
+#define VIDC_SM_ENC_NUM_UNITS_IN_TICK_ADDR                        0x01dc
+#define VIDC_SM_ENC_NUM_UNITS_IN_TICK_VALUE_BMSK                  0xffffffff
+#define VIDC_SM_ENC_NUM_UNITS_IN_TICK_VALUE_SHFT                  0
+#define VIDC_SM_ENC_TIME_SCALE_ADDR                               0x01e0
+#define VIDC_SM_ENC_TIME_SCALE_VALUE_BMSK                         0xffffffff
+#define VIDC_SM_ENC_TIME_SCALE_VALUE_SHFT                         0
+
 
 #define VIDC_SM_ALLOCATED_LUMA_DPB_SIZE_ADDR               0x0064
 #define VIDC_SM_ALLOCATED_CHROMA_DPB_SIZE_ADDR             0x0068
@@ -449,7 +458,8 @@
 	enum VIDC_SM_frame_skip frame_skip_mode,
 	u32 seq_hdr_in_band, u32 vbv_buffer_size, u32 cpcfc_enable,
 	u32 sps_pps_control, u32 closed_gop_enable,
-	u32 au_delim_enable)
+	u32 au_delim_enable,
+	u32 vui_timing_info_enable)
 {
 	u32 enc_ctrl;
 	enc_ctrl = VIDC_SETFIELD((hec_enable) ? 1 : 0,
@@ -475,7 +485,10 @@
 			VIDC_SM_ENC_EXT_CTRL_CLOSED_GOP_ENABLE_BMSK) |
 			VIDC_SETFIELD((au_delim_enable) ? 1 : 0,
 			VIDC_SM_ENC_EXT_CTRL_AU_DELIMITER_EN_SHFT,
-			VIDC_SM_ENC_EXT_CTRL_AU_DELIMITER_EN_BMSK);
+			VIDC_SM_ENC_EXT_CTRL_AU_DELIMITER_EN_BMSK) |
+			VIDC_SETFIELD((vui_timing_info_enable) ? 1 : 0,
+			VIDC_SM_ENC_EXT_CTRL_TIMING_INFO_EN_SHFT,
+			VIDC_SM_ENC_EXT_CTRL_TIMING_INFO_EN_BMSK);
 
 	DDL_MEM_WRITE_32(shared_mem, VIDC_SM_ENC_EXT_CTRL_ADDR, enc_ctrl);
 }
@@ -962,20 +975,12 @@
 		(codec == VCD_CODEC_DIVX_4) ||
 		(codec == VCD_CODEC_DIVX_5) ||
 		(codec == VCD_CODEC_DIVX_6) ||
-		(codec == VCD_CODEC_XVID) ||
-		(codec == VCD_CODEC_MPEG2)) {
+		(codec == VCD_CODEC_XVID)) {
 
-		if (codec == VCD_CODEC_MPEG2) {
-			aspect_ratio_info->aspect_ratio =
-				VIDC_GETFIELD(aspect_ratio,
-				VIDC_SM_MPEG2_ASPECT_RATIO_INFO_BMSK,
-				VIDC_SM_MPEG2_ASPECT_RATIO_INFO_SHFT);
-		} else {
-			aspect_ratio_info->aspect_ratio =
-				VIDC_GETFIELD(aspect_ratio,
-				VIDC_SM_MPEG4_ASPECT_RATIO_INFO_BMSK,
-				VIDC_SM_MPEG4_ASPECT_RATIO_INFO_SHFT);
-		}
+		aspect_ratio_info->aspect_ratio =
+			VIDC_GETFIELD(aspect_ratio,
+			VIDC_SM_MPEG4_ASPECT_RATIO_INFO_BMSK,
+			VIDC_SM_MPEG4_ASPECT_RATIO_INFO_SHFT);
 
 		switch (aspect_ratio_info->aspect_ratio) {
 		case 1:
@@ -1016,7 +1021,38 @@
 			aspect_ratio_info->par_height   = 1;
 			break;
 		}
+	} else if (codec == VCD_CODEC_MPEG2) {
+
+		aspect_ratio_info->aspect_ratio =
+			VIDC_GETFIELD(aspect_ratio,
+			VIDC_SM_MPEG2_ASPECT_RATIO_INFO_BMSK,
+			VIDC_SM_MPEG2_ASPECT_RATIO_INFO_SHFT);
+
+		switch (aspect_ratio_info->aspect_ratio) {
+		case 1:
+			aspect_ratio_info->par_width    = 1;
+			aspect_ratio_info->par_height   = 1;
+			break;
+		case 2:
+			aspect_ratio_info->par_width    = 4;
+			aspect_ratio_info->par_height   = 3;
+			break;
+		case 3:
+			aspect_ratio_info->par_width    = 16;
+			aspect_ratio_info->par_height   = 9;
+			break;
+		case 4:
+			aspect_ratio_info->par_width    = 221;
+			aspect_ratio_info->par_height   = 100;
+			break;
+		default:
+			DDL_MSG_LOW("Incorrect Aspect Ratio.");
+			aspect_ratio_info->par_width    = 1;
+			aspect_ratio_info->par_height   = 1;
+			break;
+		}
 	}
+
 }
 
 void vidc_sm_set_encoder_slice_batch_int_ctrl(struct ddl_buf_addr *shared_mem,
@@ -1139,3 +1175,15 @@
 			VIDC_SM_MP2_DATA_DUMP_BUFFER_SIZE_ADDR,
 			mp2datadumpsize);
 }
+
+void vidc_sm_set_h264_encoder_timing_info(struct ddl_buf_addr *shared_mem,
+	u32 num_units_in_tick, u32 time_scale)
+{
+	DDL_MEM_WRITE_32(shared_mem,
+			VIDC_SM_ENC_NUM_UNITS_IN_TICK_ADDR,
+			num_units_in_tick);
+
+	DDL_MEM_WRITE_32(shared_mem,
+			VIDC_SM_ENC_TIME_SCALE_ADDR,
+			time_scale);
+}
diff --git a/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_shared_mem.h b/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_shared_mem.h
index c4d577b..2eef99d 100644
--- a/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_shared_mem.h
+++ b/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_shared_mem.h
@@ -106,7 +106,7 @@
 	struct ddl_buf_addr *shared_mem, u32 hec_enable,
 	enum VIDC_SM_frame_skip  frame_skip_mode, u32 seq_hdr_in_band,
 	u32 vbv_buffer_size, u32 cpcfc_enable, u32 sps_pps_control,
-	u32 closed_gop_enable, u32 au_delim_enable);
+	u32 closed_gop_enable, u32 au_delim_enable, u32 vui_timing_info_enable);
 void vidc_sm_set_encoder_param_change(struct ddl_buf_addr *shared_mem,
 	u32 bit_rate_chg, u32 frame_rate_chg, u32 i_period_chg);
 void vidc_sm_set_encoder_vop_time(struct ddl_buf_addr *shared_mem,
@@ -198,6 +198,8 @@
 void vidc_sm_set_mp2datadump_enable(struct ddl_buf_addr *shared_mem,
 	struct ddl_mp2_datadumpenabletype *ddl_mp2_datadump_enable);
 void vidc_sm_set_mp2datadumpbuffer(struct ddl_buf_addr *shared_mem,
-		u32 mp2datadumpaddr, u32 mp2datadumpsize);
+	u32 mp2datadumpaddr, u32 mp2datadumpsize);
+void vidc_sm_set_h264_encoder_timing_info(struct ddl_buf_addr *shared_mem,
+	u32 num_units_in_tick, u32 time_scale);
 
 #endif
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/vcd_ddl_utils.h b/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_utils.h
index bbde7ae..6817101 100644
--- a/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_utils.h
+++ b/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_utils.h
@@ -62,7 +62,8 @@
 #define DDL_GET_ALIGNED_VITUAL(x)   ((x).align_virtual_addr)
 #define DDL_KILO_BYTE(x)   ((x)*1024)
 #define DDL_MEGA_BYTE(x)   ((x)*1024*1024)
-#define DDL_FRAMERATE_SCALE(x)            ((x) * 1000)
+#define DDL_FRAMERATE_SCALE_FACTOR      (1000)
+#define DDL_FRAMERATE_SCALE(x)          ((x) * DDL_FRAMERATE_SCALE_FACTOR)
 
 #define DDL_MIN(x, y)  ((x < y) ? x : y)
 #define DDL_MAX(x, y)  ((x > y) ? x : y)
diff --git a/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_vidc.c b/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_vidc.c
index 5eed305..76972ca 100644
--- a/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_vidc.c
+++ b/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_vidc.c
@@ -370,6 +370,15 @@
 				(u32)(DDL_FRAMERATE_SCALE(encoder->\
 				frame_rate.fps_numerator) /
 				encoder->frame_rate.fps_denominator));
+			if (encoder->vui_timinginfo_enable &&
+				encoder->frame_rate.fps_denominator) {
+				vidc_sm_set_h264_encoder_timing_info(
+					&ddl->shared_mem[ddl->command_channel],
+					DDL_FRAMERATE_SCALE_FACTOR,
+					(u32)(DDL_FRAMERATE_SCALE(encoder->\
+					frame_rate.fps_numerator) / encoder->\
+					frame_rate.fps_denominator) << 1);
+			}
 			encoder->dynamic_prop_change &=
 				~(DDL_ENC_CHANGE_FRAMERATE);
 		}
@@ -596,7 +605,14 @@
 		[ddl->command_channel], hdr_ext_control,
 		r_cframe_skip, false, 0,
 		h263_cpfc_enable, encoder->sps_pps.sps_pps_for_idr_enable_flag,
-		encoder->closed_gop, encoder->avc_delimiter_enable);
+		encoder->closed_gop, encoder->avc_delimiter_enable,
+		encoder->vui_timinginfo_enable);
+	if (encoder->vui_timinginfo_enable) {
+		vidc_sm_set_h264_encoder_timing_info(
+			&ddl->shared_mem[ddl->command_channel],
+			DDL_FRAMERATE_SCALE_FACTOR,
+			scaled_frame_rate << 1);
+	}
 	vidc_sm_set_encoder_init_rc_value(&ddl->shared_mem
 		[ddl->command_channel],
 		encoder->target_bit_rate.target_bitrate);
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 0bc2228..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;
 }
 
@@ -826,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_sub.c b/drivers/video/msm/vidc/common/vcd/vcd_sub.c
index 5fdee02..b84ae44 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;
 		}
@@ -784,7 +784,11 @@
 		buf_pool->allocated--;
 	}
 
-	memset(buf_entry, 0, sizeof(struct vcd_buffer_entry));
+	buf_entry->valid = buf_entry->allocated = buf_entry->in_use = 0;
+	buf_entry->alloc = buf_entry->virtual = buf_entry->physical = NULL;
+	buf_entry->sz = 0;
+	memset(&buf_entry->frame, 0, sizeof(struct vcd_frame_data));
+
 	buf_pool->validated--;
 	if (buf_pool->validated == 0)
 		vcd_free_buffer_pool_entries(buf_pool);
diff --git a/include/drm/kgsl_drm.h b/include/drm/kgsl_drm.h
index f1c7f4e..7ffae9d 100644
--- a/include/drm/kgsl_drm.h
+++ b/include/drm/kgsl_drm.h
@@ -19,6 +19,7 @@
 #define DRM_KGSL_GEM_UNLOCK_HANDLE 0x0C
 #define DRM_KGSL_GEM_UNLOCK_ON_TS 0x0D
 #define DRM_KGSL_GEM_CREATE_FD 0x0E
+#define DRM_KGSL_GEM_GET_ION_FD 0x0F
 
 #define DRM_IOCTL_KGSL_GEM_CREATE \
 DRM_IOWR(DRM_COMMAND_BASE + DRM_KGSL_GEM_CREATE, struct drm_kgsl_gem_create)
@@ -75,6 +76,10 @@
 DRM_IOWR(DRM_COMMAND_BASE + DRM_KGSL_GEM_CREATE_FD, \
 struct drm_kgsl_gem_create_fd)
 
+#define DRM_IOCTL_KGSL_GEM_GET_ION_FD \
+DRM_IOWR(DRM_COMMAND_BASE + DRM_KGSL_GEM_GET_ION_FD, \
+struct drm_kgsl_gem_get_ion_fd)
+
 /* Maximum number of sub buffers per GEM object */
 #define DRM_KGSL_GEM_MAX_BUFFERS 2
 
@@ -189,4 +194,9 @@
 	uint32_t handle;
 };
 
+struct drm_kgsl_gem_get_ion_fd {
+	uint32_t ion_fd;
+	uint32_t handle;
+};
+
 #endif
diff --git a/include/linux/Kbuild b/include/linux/Kbuild
index 5b07403..29546b7 100644
--- a/include/linux/Kbuild
+++ b/include/linux/Kbuild
@@ -445,3 +445,5 @@
 header-y += genlock.h
 header-y += msm_audio_amrwb.h
 header-y += coresight-stm.h
+header-y += ci-bridge-spi.h
+header-y += msm_audio_amrwbplus.h
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 bb3ebca..b156eba 100644
--- a/include/linux/coresight-stm.h
+++ b/include/linux/coresight-stm.h
@@ -1,5 +1,5 @@
-#ifndef __MACH_STM_H
-#define __MACH_STM_H
+#ifndef _LINUX_CORESIGHT_STM_H
+#define _LINUX_CORESIGHT_STM_H
 
 enum {
 	OST_ENTITY_NONE			= 0x00,
@@ -7,7 +7,8 @@
 	OST_ENTITY_TRACE_PRINTK		= 0x02,
 	OST_ENTITY_TRACE_MARKER		= 0x04,
 	OST_ENTITY_DEV_NODE		= 0x08,
-	OST_ENTITY_ALL			= 0x1F,
+	OST_ENTITY_QVIEW		= 0xFE,
+	OST_ENTITY_MAX			= 0xFF,
 };
 
 enum {
diff --git a/include/linux/diagchar.h b/include/linux/diagchar.h
index f0e42c2..43daaf2 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
@@ -42,6 +42,8 @@
 #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
+#define DIAG_IOCTL_REMOTE_DEV		32
 
 /* PC Tools IDs */
 #define APQ8060_TOOLS_ID	4062
@@ -673,18 +675,18 @@
 };
 
 static const uint32_t msg_bld_masks_22[] = {
-	MSG_LVL_HIGH,
-	MSG_LVL_HIGH,
-	MSG_LVL_HIGH,
-	MSG_LVL_HIGH,
-	MSG_LVL_HIGH,
-	MSG_LVL_HIGH,
-	MSG_LVL_HIGH,
-	MSG_LVL_HIGH,
-	MSG_LVL_HIGH,
-	MSG_LVL_HIGH,
-	MSG_LVL_HIGH,
-	MSG_LVL_HIGH
+	MSG_LVL_LOW,
+	MSG_LVL_LOW,
+	MSG_LVL_LOW,
+	MSG_LVL_LOW,
+	MSG_LVL_LOW,
+	MSG_LVL_LOW,
+	MSG_LVL_LOW,
+	MSG_LVL_LOW,
+	MSG_LVL_LOW,
+	MSG_LVL_LOW,
+	MSG_LVL_LOW,
+	MSG_LVL_LOW
 };
 
 /* LOG CODES */
diff --git a/include/linux/dma-attrs.h b/include/linux/dma-attrs.h
index 547ab56..18513e3 100644
--- a/include/linux/dma-attrs.h
+++ b/include/linux/dma-attrs.h
@@ -15,6 +15,8 @@
 	DMA_ATTR_WEAK_ORDERING,
 	DMA_ATTR_WRITE_COMBINE,
 	DMA_ATTR_NON_CONSISTENT,
+	DMA_ATTR_NO_KERNEL_MAPPING,
+	DMA_ATTR_STRONGLY_ORDERED,
 	DMA_ATTR_MAX,
 };
 
diff --git a/include/linux/dvb/video.h b/include/linux/dvb/video.h
index 2a2a53d..6c42099 100644
--- a/include/linux/dvb/video.h
+++ b/include/linux/dvb/video.h
@@ -151,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 */
@@ -161,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/i2c/ti_drv2667.h b/include/linux/i2c/ti_drv2667.h
new file mode 100644
index 0000000..0ae0e5e
--- /dev/null
+++ b/include/linux/i2c/ti_drv2667.h
@@ -0,0 +1,26 @@
+/* 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 __TI_DRV2667__
+
+#define DRV2667_WAV_SEQ_LEN	11
+
+struct drv2667_pdata {
+	const char *name;
+	u8 mode;
+	/* support one waveform for now */
+	u8 wav_seq[DRV2667_WAV_SEQ_LEN];
+	u8 gain;
+	u8 idle_timeout_ms;
+	u32 max_runtime_ms;
+};
+#endif
diff --git a/include/linux/ion.h b/include/linux/ion.h
index 85e5002..3a29f20 100644
--- a/include/linux/ion.h
+++ b/include/linux/ion.h
@@ -33,12 +33,14 @@
  * @ION_HEAP_TYPE_CP:	 memory allocated from a prereserved
  *				carveout heap, allocations are physically
  *				contiguous. Used for content protection.
+ * @ION_HEAP_TYPE_DMA:          memory allocated via DMA API
  * @ION_HEAP_END:		helper for iterating over heaps
  */
 enum ion_heap_type {
 	ION_HEAP_TYPE_SYSTEM,
 	ION_HEAP_TYPE_SYSTEM_CONTIG,
 	ION_HEAP_TYPE_CARVEOUT,
+	ION_HEAP_TYPE_DMA,
 	ION_HEAP_TYPE_CUSTOM, /* must be last so device specific heaps always
 				 are at the end of this enum */
 	ION_NUM_HEAPS,
@@ -47,6 +49,7 @@
 #define ION_HEAP_SYSTEM_MASK		(1 << ION_HEAP_TYPE_SYSTEM)
 #define ION_HEAP_SYSTEM_CONTIG_MASK	(1 << ION_HEAP_TYPE_SYSTEM_CONTIG)
 #define ION_HEAP_CARVEOUT_MASK		(1 << ION_HEAP_TYPE_CARVEOUT)
+#define ION_HEAP_TYPE_DMA_MASK         (1 << ION_HEAP_TYPE_DMA)
 
 /**
  * heap flags - the lower 16 bits are used by core ion, the upper 16
@@ -84,6 +87,7 @@
  * @memory_type:Memory type used for the heap
  * @has_outer_cache:    set to 1 if outer cache is used, 0 otherwise.
  * @extra_data:	Extra data specific to each heap type
+ * @priv:	heap private data
  */
 struct ion_platform_heap {
 	enum ion_heap_type type;
@@ -94,6 +98,7 @@
 	enum ion_memory_types memory_type;
 	unsigned int has_outer_cache;
 	void *extra_data;
+	void *priv;
 };
 
 /**
@@ -115,7 +120,7 @@
 	int (*request_region)(void *);
 	int (*release_region)(void *);
 	void *(*setup_region)(void);
-	struct ion_platform_heap heaps[];
+	struct ion_platform_heap *heaps;
 };
 
 #ifdef CONFIG_ION
@@ -222,11 +227,9 @@
  * ion_map_kernel - create mapping for the given handle
  * @client:	the client
  * @handle:	handle to map
- * @flags:	flags for this mapping
  *
  * Map the given handle into the kernel and return a kernel address that
- * can be used to access this address. If no flags are specified, this
- * will return a non-secure uncached mapping.
+ * can be used to access this address.
  */
 void *ion_map_kernel(struct ion_client *client, struct ion_handle *handle);
 
@@ -418,7 +421,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 +456,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/kernel.h b/include/linux/kernel.h
index f581c8f..37b1fdc 100644
--- a/include/linux/kernel.h
+++ b/include/linux/kernel.h
@@ -486,7 +486,7 @@
 		  __attribute__((section("__trace_printk_fmt"))) =	\
 			__builtin_constant_p(fmt) ? fmt : NULL;		\
 									\
-		__trace_bprintk(_THIS_IP_, trace_printk_fmt, ##args);	\
+		__trace_printk(_THIS_IP_, trace_printk_fmt, ##args);	\
 	} else								\
 		__trace_printk(_THIS_IP_, fmt, ##args);		\
 } while (0)
diff --git a/include/linux/mfd/pm8xxx/batterydata-lib.h b/include/linux/mfd/pm8xxx/batterydata-lib.h
index c55e47e..afa1843 100644
--- a/include/linux/mfd/pm8xxx/batterydata-lib.h
+++ b/include/linux/mfd/pm8xxx/batterydata-lib.h
@@ -102,7 +102,8 @@
 };
 
 #if defined(CONFIG_PM8921_BMS) || \
-	defined(CONFIG_PM8921_BMS_MODULE)
+	defined(CONFIG_PM8921_BMS_MODULE) || \
+	defined(CONFIG_QPNP_BMS)
 extern struct bms_battery_data  palladium_1500_data;
 extern struct bms_battery_data  desay_5200_data;
 
diff --git a/include/linux/mfd/pm8xxx/ccadc.h b/include/linux/mfd/pm8xxx/ccadc.h
index 29f7a62..fc31f89 100644
--- a/include/linux/mfd/pm8xxx/ccadc.h
+++ b/include/linux/mfd/pm8xxx/ccadc.h
@@ -19,11 +19,11 @@
 
 /**
  * struct pm8xxx_ccadc_platform_data -
- * @r_sense:		sense resistor value in (mOhms)
+ * @r_sense_uohm:		sense resistor value in (micro Ohms)
  * @calib_delay_ms:	how often should the adc calculate gain and offset
  */
 struct pm8xxx_ccadc_platform_data {
-	int		r_sense;
+	int		r_sense_uohm;
 	unsigned int	calib_delay_ms;
 };
 
diff --git a/include/linux/mfd/pm8xxx/pm8038.h b/include/linux/mfd/pm8xxx/pm8038.h
index 574dab6..9e25b5c 100644
--- a/include/linux/mfd/pm8xxx/pm8038.h
+++ b/include/linux/mfd/pm8xxx/pm8038.h
@@ -83,6 +83,7 @@
 	struct pm8921_bms_platform_data		*bms_pdata;
 	struct pm8xxx_adc_platform_data		*adc_pdata;
 	struct pm8xxx_led_platform_data		*leds_pdata;
+	struct pm8xxx_vibrator_platform_data	*vibrator_pdata;
 	struct pm8xxx_ccadc_platform_data	*ccadc_pdata;
 	struct pm8xxx_spk_platform_data		*spk_pdata;
 };
diff --git a/include/linux/mfd/pm8xxx/pm8921-bms.h b/include/linux/mfd/pm8xxx/pm8921-bms.h
index 5f2fe9f..6db6204 100644
--- a/include/linux/mfd/pm8xxx/pm8921-bms.h
+++ b/include/linux/mfd/pm8xxx/pm8921-bms.h
@@ -30,18 +30,22 @@
 /**
  * struct pm8921_bms_platform_data -
  * @batt_type:		allows to force chose battery calibration data
- * @r_sense:		sense resistor value in (mOhms)
+ * @r_sense_uohm:	sense resistor value in (micro Ohms)
  * @i_test:		current at which the unusable charger cutoff is to be
  *			calculated or the peak system current (mA)
  * @v_cutoff:		the loaded voltage at which the battery
  *			is considered empty(mV)
  * @enable_fcc_learning:	if set the driver will learn full charge
  *				capacity of the battery upon end of charge
+ * @normal_voltage_calc_ms:	The period of soc calculation in ms when battery
+ *				voltage higher than cutoff voltage
+ * @low_voltage_calc_ms:	The period of soc calculation in ms when battery
+ *				voltage is near cutoff voltage
  */
 struct pm8921_bms_platform_data {
 	struct pm8xxx_bms_core_data	bms_cdata;
 	enum battery_type		battery_type;
-	unsigned int			r_sense;
+	int				r_sense_uohm;
 	unsigned int			i_test;
 	unsigned int			v_cutoff;
 	unsigned int			max_voltage_uv;
@@ -51,6 +55,8 @@
 	int				ignore_shutdown_soc;
 	int				adjust_soc_low_threshold;
 	int				chg_term_ua;
+	int				normal_voltage_calc_ms;
+	int				low_voltage_calc_ms;
 };
 
 #if defined(CONFIG_PM8921_BMS) || defined(CONFIG_PM8921_BMS_MODULE)
@@ -127,6 +133,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 +180,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 16d4a4b..ae2c3d8 100644
--- a/include/linux/mfd/pm8xxx/pm8921-charger.h
+++ b/include/linux/mfd/pm8xxx/pm8921-charger.h
@@ -58,7 +58,6 @@
 
 /**
  * struct pm8921_charger_platform_data -
- * @safety_time:	max charging time in minutes incl. fast and trkl
  *			valid range 4 to 512 min. PON default 120 min
  * @ttrkl_time:		max trckl charging time in minutes
  *			valid range 1 to 64 mins. PON default 15 min
@@ -70,6 +69,9 @@
  *			trickle to fast. This is also the minimum voltage the
  *			system operates at
  * @uvd_thresh_voltage:	the USB falling UVD threshold (mV) (PM8917 only)
+ * @safe_current_ma:	The upper limit of current allowed to be pushed in
+ *			battery. This ends up writing in a one time
+ *			programmable register.
  * @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
@@ -87,6 +89,7 @@
  *			area
  * @max_bat_chg_current:	Max charge current of the battery in mA
  *				Usually 70% of full charge capacity
+ * @usb_max_current:		Maximum USB current in mA
  * @cool_bat_chg_current:	chg current (mA) when the battery is cool
  * @warm_bat_chg_current:	chg current (mA)  when the battery is warm
  * @cool_bat_voltage:		chg voltage (mV) when the battery is cool
@@ -94,10 +97,6 @@
  * @get_batt_capacity_percent:
  *			a board specific function to return battery
  *			capacity. If null - a default one will be used
- * @dc_unplug_check:	enables the reverse boosting fix for the DC_IN line
- *			however, this should only be enabled for devices which
- *			control the DC OVP FETs otherwise this option should
- *			remain disabled
  * @has_dc_supply:	report DC online if this bit is set in board file
  * @trkl_voltage:	the trkl voltage in (mV) below which hw controlled
  *			 trkl charging happens with linear charger
@@ -127,12 +126,12 @@
  */
 struct pm8921_charger_platform_data {
 	struct pm8xxx_charger_core_data	charger_cdata;
-	unsigned int			safety_time;
 	unsigned int			ttrkl_time;
 	unsigned int			update_time;
 	unsigned int			max_voltage;
 	unsigned int			min_voltage;
 	unsigned int			uvd_thresh_voltage;
+	unsigned int			safe_current_ma;
 	unsigned int			alarm_low_mv;
 	unsigned int			alarm_high_mv;
 	unsigned int			resume_voltage_delta;
@@ -142,6 +141,7 @@
 	int				warm_temp;
 	unsigned int			temp_check_period;
 	unsigned int			max_bat_chg_current;
+	unsigned int			usb_max_current;
 	unsigned int			cool_bat_chg_current;
 	unsigned int			warm_bat_chg_current;
 	unsigned int			cool_bat_voltage;
@@ -150,7 +150,6 @@
 	int64_t				batt_id_min;
 	int64_t				batt_id_max;
 	bool				keep_btm_on_suspend;
-	bool				dc_unplug_check;
 	bool				has_dc_supply;
 	int				trkl_voltage;
 	int				weak_voltage;
@@ -163,6 +162,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 {
@@ -175,15 +175,6 @@
 void pm8921_charger_vbus_draw(unsigned int mA);
 int pm8921_charger_register_vbus_sn(void (*callback)(int));
 void pm8921_charger_unregister_vbus_sn(void (*callback)(int));
-/**
- * pm8921_charger_enable -
- *
- * @enable: 1 means enable charging, 0 means disable
- *
- * Enable/Disable battery charging current, the device will still draw current
- * from the charging source
- */
-int pm8921_charger_enable(bool enable);
 
 /**
  * pm8921_is_usb_chg_plugged_in - is usb plugged in
@@ -309,10 +300,6 @@
 static inline void pm8921_charger_unregister_vbus_sn(void (*callback)(int))
 {
 }
-static inline int pm8921_charger_enable(bool enable)
-{
-	return -ENXIO;
-}
 static inline int pm8921_is_usb_chg_plugged_in(void)
 {
 	return -ENXIO;
diff --git a/include/linux/mfd/wcd9xxx/core.h b/include/linux/mfd/wcd9xxx/core.h
index 2dea611..4e9e1ce 100644
--- a/include/linux/mfd/wcd9xxx/core.h
+++ b/include/linux/mfd/wcd9xxx/core.h
@@ -131,7 +131,7 @@
 	u32 bit_width;				/* sit width 16,24,32   */
 	struct list_head wcd9xxx_ch_list;	/* channel list         */
 	u16 grph;				/* slimbus group handle */
-	u32 ch_mask;
+	unsigned long ch_mask;
 	wait_queue_head_t dai_wait;
 };
 
diff --git a/include/linux/mfd/wcd9xxx/wcd9320_registers.h b/include/linux/mfd/wcd9xxx/wcd9320_registers.h
index 4b8626a..f9966be 100644
--- a/include/linux/mfd/wcd9xxx/wcd9320_registers.h
+++ b/include/linux/mfd/wcd9xxx/wcd9320_registers.h
@@ -1334,9 +1334,16 @@
 
 /* SLIMBUS Slave Registers */
 #define TAIKO_SLIM_PGD_PORT_INT_EN0                     (0x30)
-#define TAIKO_SLIM_PGD_PORT_INT_STATUS0                 (0x34)
-#define TAIKO_SLIM_PGD_PORT_INT_CLR0                    (0x38)
-#define TAIKO_SLIM_PGD_PORT_INT_SOURCE0			(0x60)
+#define TAIKO_SLIM_PGD_PORT_INT_STATUS_RX_0             (0x34)
+#define TAIKO_SLIM_PGD_PORT_INT_STATUS_RX_1             (0x35)
+#define TAIKO_SLIM_PGD_PORT_INT_STATUS_TX_0             (0x36)
+#define TAIKO_SLIM_PGD_PORT_INT_STATUS_TX_1             (0x37)
+#define TAIKO_SLIM_PGD_PORT_INT_CLR_RX_0                (0x38)
+#define TAIKO_SLIM_PGD_PORT_INT_CLR_RX_1                (0x39)
+#define TAIKO_SLIM_PGD_PORT_INT_CLR_TX_0                (0x3A)
+#define TAIKO_SLIM_PGD_PORT_INT_CLR_TX_1                (0x3B)
+#define TAIKO_SLIM_PGD_PORT_INT_RX_SOURCE0		(0x60)
+#define TAIKO_SLIM_PGD_PORT_INT_TX_SOURCE0		(0x70)
 
 /* Macros for Packing Register Writes into a U32 */
 #define TAIKO_PACKED_REG_SIZE sizeof(u32)
diff --git a/include/linux/mfd/wcd9xxx/wcd9xxx-slimslave.h b/include/linux/mfd/wcd9xxx/wcd9xxx-slimslave.h
index 45abd92..aaa8fd6 100644
--- a/include/linux/mfd/wcd9xxx/wcd9xxx-slimslave.h
+++ b/include/linux/mfd/wcd9xxx/wcd9xxx-slimslave.h
@@ -109,4 +109,8 @@
 int wcd9xxx_get_slave_port(unsigned int ch_num);
 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 4b7a32c..a1609b8 100644
--- a/include/linux/mfd/wcd9xxx/wcd9xxx_registers.h
+++ b/include/linux/mfd/wcd9xxx/wcd9xxx_registers.h
@@ -202,5 +202,7 @@
 #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)
+#define WCD9XXX_A_MAD_ANA_CTRL			(0x150)
+#define WCD9XXX_A_MAD_ANA_CTRL__POR		(0xF1)
 
 #endif
diff --git a/include/linux/mhl_8334.h b/include/linux/mhl_8334.h
index cb9d7fa..c9f57c5 100644
--- a/include/linux/mhl_8334.h
+++ b/include/linux/mhl_8334.h
@@ -130,4 +130,162 @@
 	DEV_PAGE_DDC_SEGM   = (0x60),
 };
 
+#define MHL_SII_PAGE0_RD(off) \
+	mhl_i2c_reg_read(client, TX_PAGE_L0, off)
+#define MHL_SII_PAGE0_WR(off, val) \
+	mhl_i2c_reg_write(client, TX_PAGE_L0, off, val)
+#define MHL_SII_PAGE0_MOD(off, mask, val)		\
+	mhl_i2c_reg_modify(client, TX_PAGE_L0, off, mask, val)
+
+
+#define MHL_SII_PAGE1_RD(off) \
+	mhl_i2c_reg_read(client, TX_PAGE_L1, off)
+#define MHL_SII_PAGE1_WR(off, val) \
+	mhl_i2c_reg_write(client, TX_PAGE_L1, off, val)
+#define MHL_SII_PAGE1_MOD(off, mask, val) \
+	mhl_i2c_reg_modify(client, TX_PAGE_L1, off, mask, val)
+
+
+#define MHL_SII_PAGE2_RD(off) \
+	mhl_i2c_reg_read(client, TX_PAGE_2, off)
+#define MHL_SII_PAGE2_WR(off, val) \
+	mhl_i2c_reg_write(client, TX_PAGE_2, off, val)
+#define MHL_SII_PAGE2_MOD(off, mask, val) \
+	mhl_i2c_reg_modify(client, TX_PAGE_2, off, mask, val)
+
+
+#define MHL_SII_PAGE3_RD(off) \
+	mhl_i2c_reg_read(client, TX_PAGE_3, off)
+#define MHL_SII_PAGE3_WR(off, val) \
+	mhl_i2c_reg_write(client, TX_PAGE_3, off, val)
+#define MHL_SII_PAGE3_MOD(off, mask, val)		\
+	mhl_i2c_reg_modify(client, TX_PAGE_3, off, mask, val)
+
+#define MHL_SII_CBUS_RD(off) \
+	mhl_i2c_reg_read(client, TX_PAGE_CBUS, off)
+#define MHL_SII_CBUS_WR(off, val) \
+	mhl_i2c_reg_write(client, TX_PAGE_CBUS, off, val)
+#define MHL_SII_CBUS_MOD(off, mask, val) \
+	mhl_i2c_reg_modify(client, TX_PAGE_CBUS, off, mask, val)
+
+#define REG_SRST        ((TX_PAGE_3 << 16) | 0x0000)
+#define REG_INTR1       ((TX_PAGE_L0 << 16) | 0x0071)
+#define REG_INTR1_MASK  ((TX_PAGE_L0 << 16) | 0x0075)
+#define REG_INTR2       ((TX_PAGE_L0 << 16) | 0x0072)
+#define REG_TMDS_CCTRL  ((TX_PAGE_L0 << 16) | 0x0080)
+
+#define REG_DISC_CTRL1	((TX_PAGE_3 << 16) | 0x0010)
+#define REG_DISC_CTRL2	((TX_PAGE_3 << 16) | 0x0011)
+#define REG_DISC_CTRL3	((TX_PAGE_3 << 16) | 0x0012)
+#define REG_DISC_CTRL4	((TX_PAGE_3 << 16) | 0x0013)
+#define REG_DISC_CTRL5	((TX_PAGE_3 << 16) | 0x0014)
+#define REG_DISC_CTRL6	((TX_PAGE_3 << 16) | 0x0015)
+#define REG_DISC_CTRL7	((TX_PAGE_3 << 16) | 0x0016)
+#define REG_DISC_CTRL8	((TX_PAGE_3 << 16) | 0x0017)
+#define REG_DISC_CTRL9	((TX_PAGE_3 << 16) | 0x0018)
+#define REG_DISC_CTRL10	((TX_PAGE_3 << 16) | 0x0019)
+#define REG_DISC_CTRL11	((TX_PAGE_3 << 16) | 0x001A)
+#define REG_DISC_STAT	((TX_PAGE_3 << 16) | 0x001B)
+#define REG_DISC_STAT2	((TX_PAGE_3 << 16) | 0x001C)
+
+#define REG_INT_CTRL	((TX_PAGE_3 << 16) | 0x0020)
+#define REG_INTR4		((TX_PAGE_3 << 16) | 0x0021)
+#define REG_INTR4_MASK	((TX_PAGE_3 << 16) | 0x0022)
+#define REG_INTR5		((TX_PAGE_3 << 16) | 0x0023)
+#define REG_INTR5_MASK	((TX_PAGE_3 << 16) | 0x0024)
+
+#define REG_MHLTX_CTL1	((TX_PAGE_3 << 16) | 0x0030)
+#define REG_MHLTX_CTL2	((TX_PAGE_3 << 16) | 0x0031)
+#define REG_MHLTX_CTL3	((TX_PAGE_3 << 16) | 0x0032)
+#define REG_MHLTX_CTL4	((TX_PAGE_3 << 16) | 0x0033)
+#define REG_MHLTX_CTL5	((TX_PAGE_3 << 16) | 0x0034)
+#define REG_MHLTX_CTL6	((TX_PAGE_3 << 16) | 0x0035)
+#define REG_MHLTX_CTL7	((TX_PAGE_3 << 16) | 0x0036)
+#define REG_MHLTX_CTL8	((TX_PAGE_3 << 16) | 0x0037)
+
+#define REG_TMDS_CSTAT	((TX_PAGE_3 << 16) | 0x0040)
+
+#define REG_CBUS_INTR_ENABLE            ((TX_PAGE_CBUS << 16) | 0x0009)
+
+#define REG_DDC_ABORT_REASON            ((TX_PAGE_CBUS << 16) | 0x000B)
+#define REG_CBUS_BUS_STATUS             ((TX_PAGE_CBUS << 16) | 0x000A)
+#define REG_PRI_XFR_ABORT_REASON        ((TX_PAGE_CBUS << 16) | 0x000D)
+#define REG_CBUS_PRI_FWR_ABORT_REASON   ((TX_PAGE_CBUS << 16) | 0x000E)
+#define REG_CBUS_PRI_START              ((TX_PAGE_CBUS << 16) | 0x0012)
+#define REG_CBUS_PRI_ADDR_CMD           ((TX_PAGE_CBUS << 16) | 0x0013)
+#define REG_CBUS_PRI_WR_DATA_1ST        ((TX_PAGE_CBUS << 16) | 0x0014)
+#define REG_CBUS_PRI_WR_DATA_2ND        ((TX_PAGE_CBUS << 16) | 0x0015)
+#define REG_CBUS_PRI_RD_DATA_1ST        ((TX_PAGE_CBUS << 16) | 0x0016)
+#define REG_CBUS_PRI_RD_DATA_2ND        ((TX_PAGE_CBUS << 16) | 0x0017)
+#define REG_CBUS_PRI_VS_CMD             ((TX_PAGE_CBUS << 16) | 0x0018)
+#define REG_CBUS_PRI_VS_DATA            ((TX_PAGE_CBUS << 16) | 0x0019)
+#define	REG_CBUS_MSC_RETRY_INTERVAL		((TX_PAGE_CBUS << 16) | 0x001A)
+#define	REG_CBUS_DDC_FAIL_LIMIT			((TX_PAGE_CBUS << 16) | 0x001C)
+#define	REG_CBUS_MSC_FAIL_LIMIT			((TX_PAGE_CBUS << 16) | 0x001D)
+#define	REG_CBUS_MSC_INT2_STATUS        ((TX_PAGE_CBUS << 16) | 0x001E)
+#define REG_CBUS_MSC_INT2_ENABLE        ((TX_PAGE_CBUS << 16) | 0x001F)
+#define	REG_MSC_WRITE_BURST_LEN         ((TX_PAGE_CBUS << 16) | 0x0020)
+#define	REG_MSC_HEARTBEAT_CONTROL       ((TX_PAGE_CBUS << 16) | 0x0021)
+#define REG_MSC_TIMEOUT_LIMIT           ((TX_PAGE_CBUS << 16) | 0x0022)
+#define	REG_CBUS_LINK_CONTROL_1			((TX_PAGE_CBUS << 16) | 0x0030)
+#define	REG_CBUS_LINK_CONTROL_2			((TX_PAGE_CBUS << 16) | 0x0031)
+#define	REG_CBUS_LINK_CONTROL_3			((TX_PAGE_CBUS << 16) | 0x0032)
+#define	REG_CBUS_LINK_CONTROL_4			((TX_PAGE_CBUS << 16) | 0x0033)
+#define	REG_CBUS_LINK_CONTROL_5			((TX_PAGE_CBUS << 16) | 0x0034)
+#define	REG_CBUS_LINK_CONTROL_6			((TX_PAGE_CBUS << 16) | 0x0035)
+#define	REG_CBUS_LINK_CONTROL_7			((TX_PAGE_CBUS << 16) | 0x0036)
+#define REG_CBUS_LINK_STATUS_1          ((TX_PAGE_CBUS << 16) | 0x0037)
+#define REG_CBUS_LINK_STATUS_2          ((TX_PAGE_CBUS << 16) | 0x0038)
+#define	REG_CBUS_LINK_CONTROL_8			((TX_PAGE_CBUS << 16) | 0x0039)
+#define	REG_CBUS_LINK_CONTROL_9			((TX_PAGE_CBUS << 16) | 0x003A)
+#define	REG_CBUS_LINK_CONTROL_10		((TX_PAGE_CBUS << 16) | 0x003B)
+#define	REG_CBUS_LINK_CONTROL_11		((TX_PAGE_CBUS << 16) | 0x003C)
+#define	REG_CBUS_LINK_CONTROL_12		((TX_PAGE_CBUS << 16) | 0x003D)
+
+
+#define REG_CBUS_LINK_CTRL9_0			((TX_PAGE_CBUS << 16) | 0x003A)
+#define REG_CBUS_LINK_CTRL9_1           ((TX_PAGE_CBUS << 16) | 0x00BA)
+
+#define	REG_CBUS_DRV_STRENGTH_0			((TX_PAGE_CBUS << 16) | 0x0040)
+#define	REG_CBUS_DRV_STRENGTH_1			((TX_PAGE_CBUS << 16) | 0x0041)
+#define	REG_CBUS_ACK_CONTROL			((TX_PAGE_CBUS << 16) | 0x0042)
+#define	REG_CBUS_CAL_CONTROL			((TX_PAGE_CBUS << 16) | 0x0043)
+
+#define REG_CBUS_SCRATCHPAD_0           ((TX_PAGE_CBUS << 16) | 0x00C0)
+#define REG_CBUS_DEVICE_CAP_0           ((TX_PAGE_CBUS << 16) | 0x0080)
+#define REG_CBUS_DEVICE_CAP_1           ((TX_PAGE_CBUS << 16) | 0x0081)
+#define REG_CBUS_DEVICE_CAP_2           ((TX_PAGE_CBUS << 16) | 0x0082)
+#define REG_CBUS_DEVICE_CAP_3           ((TX_PAGE_CBUS << 16) | 0x0083)
+#define REG_CBUS_DEVICE_CAP_4           ((TX_PAGE_CBUS << 16) | 0x0084)
+#define REG_CBUS_DEVICE_CAP_5           ((TX_PAGE_CBUS << 16) | 0x0085)
+#define REG_CBUS_DEVICE_CAP_6           ((TX_PAGE_CBUS << 16) | 0x0086)
+#define REG_CBUS_DEVICE_CAP_7           ((TX_PAGE_CBUS << 16) | 0x0087)
+#define REG_CBUS_DEVICE_CAP_8           ((TX_PAGE_CBUS << 16) | 0x0088)
+#define REG_CBUS_DEVICE_CAP_9           ((TX_PAGE_CBUS << 16) | 0x0089)
+#define REG_CBUS_DEVICE_CAP_A           ((TX_PAGE_CBUS << 16) | 0x008A)
+#define REG_CBUS_DEVICE_CAP_B           ((TX_PAGE_CBUS << 16) | 0x008B)
+#define REG_CBUS_DEVICE_CAP_C           ((TX_PAGE_CBUS << 16) | 0x008C)
+#define REG_CBUS_DEVICE_CAP_D           ((TX_PAGE_CBUS << 16) | 0x008D)
+#define REG_CBUS_DEVICE_CAP_E           ((TX_PAGE_CBUS << 16) | 0x008E)
+#define REG_CBUS_DEVICE_CAP_F           ((TX_PAGE_CBUS << 16) | 0x008F)
+#define REG_CBUS_SET_INT_0              ((TX_PAGE_CBUS << 16) | 0x00A0)
+#define REG_CBUS_SET_INT_1		((TX_PAGE_CBUS << 16) | 0x00A1)
+#define REG_CBUS_SET_INT_2		((TX_PAGE_CBUS << 16) | 0x00A2)
+#define REG_CBUS_SET_INT_3		((TX_PAGE_CBUS << 16) | 0x00A3)
+#define REG_CBUS_WRITE_STAT_0           ((TX_PAGE_CBUS << 16) | 0x00B0)
+#define REG_CBUS_WRITE_STAT_1           ((TX_PAGE_CBUS << 16) | 0x00B1)
+#define REG_CBUS_WRITE_STAT_2           ((TX_PAGE_CBUS << 16) | 0x00B2)
+#define REG_CBUS_WRITE_STAT_3           ((TX_PAGE_CBUS << 16) | 0x00B3)
+
+#define GET_PAGE(x) (x >> 16)
+#define GET_OFF(x) (x & 0xffff)
+
+
+#define MHL_SII_REG_NAME_RD(arg)\
+	mhl_i2c_reg_read(client, GET_PAGE(arg), GET_OFF(arg))
+#define MHL_SII_REG_NAME_WR(arg, val)\
+	mhl_i2c_reg_write(client, GET_PAGE(arg), GET_OFF(arg), val)
+#define MHL_SII_REG_NAME_MOD(arg, mask, val)\
+	mhl_i2c_reg_modify(client, GET_PAGE(arg), GET_OFF(arg), mask, val)
+
 #endif /* __MHL_MSM_H__ */
diff --git a/include/linux/mm.h b/include/linux/mm.h
index ddfb7c5..48268f0 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -1561,6 +1561,32 @@
 #define in_gate_area(mm, addr) ({(void)mm; in_gate_area_no_mm(addr);})
 #endif	/* __HAVE_ARCH_GATE_AREA */
 
+#ifdef CONFIG_USE_USER_ACCESSIBLE_TIMERS
+static inline int use_user_accessible_timers(void) { return 1; }
+extern int in_user_timers_area(struct mm_struct *mm, unsigned long addr);
+extern struct vm_area_struct *get_user_timers_vma(struct mm_struct *mm);
+extern int get_user_timer_page(struct vm_area_struct *vma,
+	struct mm_struct *mm, unsigned long start, unsigned int gup_flags,
+	struct page **pages, int idx, int *goto_next_page);
+#else
+static inline int use_user_accessible_timers(void) { return 0; }
+static inline int in_user_timers_area(struct mm_struct *mm, unsigned long addr)
+{
+	return 0;
+}
+static inline struct vm_area_struct *get_user_timers_vma(struct mm_struct *mm)
+{
+	return NULL;
+}
+static inline int get_user_timer_page(struct vm_area_struct *vma,
+	struct mm_struct *mm, unsigned long start, unsigned int gup_flags,
+	struct page **pages, int idx, int *goto_next_page)
+{
+	*goto_next_page = 0;
+	return 0;
+}
+#endif
+
 int drop_caches_sysctl_handler(struct ctl_table *, int,
 					void __user *, size_t *, loff_t *);
 unsigned long shrink_slab(struct shrink_control *shrink,
diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
index fb854ba..2046198 100644
--- a/include/linux/mmc/card.h
+++ b/include/linux/mmc/card.h
@@ -324,6 +324,7 @@
 #define MMC_QUIRK_BLK_NO_CMD23	(1<<7)		/* Avoid CMD23 for regular multiblock */
 #define MMC_QUIRK_BROKEN_BYTE_MODE_512 (1<<8)	/* Avoid sending 512 bytes in */
 #define MMC_QUIRK_LONG_READ_TIME (1<<9)		/* Data read time > CSD says */
+#define MMC_QUIRK_SEC_ERASE_TRIM_BROKEN (1<<10)	/* Skip secure for erase/trim */
 						/* byte mode */
 #define MMC_QUIRK_INAND_DATA_TIMEOUT  (1<<8)    /* For incorrect data timeout */
 
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index f435221..6c43ec7 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -138,6 +138,8 @@
 	void	(*enable_preset_value)(struct mmc_host *host, bool enable);
 	int	(*select_drive_strength)(unsigned int max_dtr, int host_drv, int card_drv);
 	void	(*hw_reset)(struct mmc_host *host);
+	unsigned long (*get_max_frequency)(struct mmc_host *host);
+	unsigned long (*get_min_frequency)(struct mmc_host *host);
 };
 
 struct mmc_card;
@@ -250,6 +252,7 @@
 
 #define MMC_CAP2_SANITIZE	(1 << 13)		/* Support Sanitize */
 #define MMC_CAP2_INIT_BKOPS	    (1 << 15)	/* Need to set BKOPS_EN */
+#define MMC_CAP2_CLK_SCALE	(1 << 16)	/* Allow dynamic clk scaling */
 	mmc_pm_flag_t		pm_caps;	/* supported pm features */
 
 	int			clk_requests;	/* internal reference counter */
@@ -353,6 +356,19 @@
 #endif
 
 	struct mmc_ios saved_ios;
+	struct {
+		unsigned long	busy_time_us;
+		unsigned long	window_time;
+		unsigned long	curr_freq;
+		unsigned long	polling_delay_ms;
+		unsigned int	up_threshold;
+		unsigned int	down_threshold;
+		ktime_t		start_busy;
+		bool		enable;
+		bool		initialized;
+		bool		in_progress;
+		struct delayed_work work;
+	} clk_scaling;
 	unsigned long		private[0] ____cacheline_aligned;
 };
 
@@ -468,6 +484,14 @@
 	return !(host->caps2 & MMC_CAP2_BOOTPART_NOACC);
 }
 
+static inline int mmc_host_uhs(struct mmc_host *host)
+{
+	return host->caps &
+		(MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 |
+		 MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR104 |
+		 MMC_CAP_UHS_DDR50);
+}
+
 #ifdef CONFIG_MMC_CLKGATE
 void mmc_host_clk_hold(struct mmc_host *host);
 void mmc_host_clk_release(struct mmc_host *host);
diff --git a/include/linux/mmc/sdhci.h b/include/linux/mmc/sdhci.h
index e9051e1..c5b492b 100644
--- a/include/linux/mmc/sdhci.h
+++ b/include/linux/mmc/sdhci.h
@@ -91,6 +91,7 @@
 	unsigned int quirks2;	/* More deviations from spec. */
 
 #define SDHCI_QUIRK2_HOST_OFF_CARD_ON			(1<<0)
+#define SDHCI_QUIRK2_OWN_CARD_DETECTION			(1<<1)
 
 	int irq;		/* Device IRQ */
 	void __iomem *ioaddr;	/* Mapped address */
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_amrwbplus.h b/include/linux/msm_audio_amrwbplus.h
new file mode 100644
index 0000000..aa17117
--- /dev/null
+++ b/include/linux/msm_audio_amrwbplus.h
@@ -0,0 +1,18 @@
+#ifndef __MSM_AUDIO_AMR_WB_PLUS_H
+#define __MSM_AUDIO_AMR_WB_PLUS_H
+
+#define AUDIO_GET_AMRWBPLUS_CONFIG_V2  _IOR(AUDIO_IOCTL_MAGIC, \
+	(AUDIO_MAX_COMMON_IOCTL_NUM+2), struct msm_audio_amrwbplus_config_v2)
+#define AUDIO_SET_AMRWBPLUS_CONFIG_V2  _IOW(AUDIO_IOCTL_MAGIC, \
+	(AUDIO_MAX_COMMON_IOCTL_NUM+3), struct msm_audio_amrwbplus_config_v2)
+
+struct msm_audio_amrwbplus_config_v2 {
+	unsigned int size_bytes;
+	unsigned int version;
+	unsigned int num_channels;
+	unsigned int amr_band_mode;
+	unsigned int amr_dtx_mode;
+	unsigned int amr_frame_fmt;
+	unsigned int amr_lsf_idx;
+};
+#endif /* __MSM_AUDIO_AMR_WB_PLUS_H */
diff --git a/include/linux/msm_ion.h b/include/linux/msm_ion.h
index c1ea490..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"
 
diff --git a/include/linux/msm_ipa.h b/include/linux/msm_ipa.h
new file mode 100644
index 0000000..613cd9f
--- /dev/null
+++ b/include/linux/msm_ipa.h
@@ -0,0 +1,714 @@
+#ifndef _MSM_IPA_H_
+#define _MSM_IPA_H_
+
+#ifndef __KERNEL__
+#include <stdint.h>
+#include <stddef.h>
+#include <sys/stat.h>
+#endif
+#include <linux/ioctl.h>
+
+/**
+ * unique magic number of the IPA device
+ */
+#define IPA_IOC_MAGIC 0xCF
+
+/**
+ * name of the default routing tables for v4 and v6
+ */
+#define IPA_DFLT_RT_TBL_NAME "ipa_dflt_rt"
+
+/**
+ *   the commands supported by IPA driver
+ */
+#define IPA_IOCTL_ADD_HDR            0
+#define IPA_IOCTL_DEL_HDR            1
+#define IPA_IOCTL_ADD_RT_RULE        2
+#define IPA_IOCTL_DEL_RT_RULE        3
+#define IPA_IOCTL_ADD_FLT_RULE       4
+#define IPA_IOCTL_DEL_FLT_RULE       5
+#define IPA_IOCTL_COMMIT_HDR         6
+#define IPA_IOCTL_RESET_HDR          7
+#define IPA_IOCTL_COMMIT_RT          8
+#define IPA_IOCTL_RESET_RT           9
+#define IPA_IOCTL_COMMIT_FLT        10
+#define IPA_IOCTL_RESET_FLT         11
+#define IPA_IOCTL_DUMP              12
+#define IPA_IOCTL_GET_RT_TBL        13
+#define IPA_IOCTL_PUT_RT_TBL        14
+#define IPA_IOCTL_COPY_HDR          15
+#define IPA_IOCTL_QUERY_INTF        16
+#define IPA_IOCTL_QUERY_INTF_TX_PROPS 17
+#define IPA_IOCTL_QUERY_INTF_RX_PROPS 18
+#define IPA_IOCTL_GET_HDR           19
+#define IPA_IOCTL_PUT_HDR           20
+#define IPA_IOCTL_SET_FLT        21
+#define IPA_IOCTL_ALLOC_NAT_MEM  22
+#define IPA_IOCTL_V4_INIT_NAT    23
+#define IPA_IOCTL_NAT_DMA        24
+#define IPA_IOCTL_V4_DEL_NAT     26
+#define IPA_IOCTL_GET_ASYNC_MSG  27
+#define IPA_IOCTL_GET_NAT_OFFSET 28
+#define IPA_IOCTL_MAX            29
+
+/**
+ * max size of the header to be inserted
+ */
+#define IPA_HDR_MAX_SIZE 64
+
+/**
+ * max size of the name of the resource (routing table, header)
+ */
+#define IPA_RESOURCE_NAME_MAX 20
+
+/**
+ * the attributes of the rule (routing or filtering)
+ */
+#define IPA_FLT_TOS            (1ul << 0)
+#define IPA_FLT_PROTOCOL       (1ul << 1)
+#define IPA_FLT_SRC_ADDR       (1ul << 2)
+#define IPA_FLT_DST_ADDR       (1ul << 3)
+#define IPA_FLT_SRC_PORT_RANGE (1ul << 4)
+#define IPA_FLT_DST_PORT_RANGE (1ul << 5)
+#define IPA_FLT_TYPE           (1ul << 6)
+#define IPA_FLT_CODE           (1ul << 7)
+#define IPA_FLT_SPI            (1ul << 8)
+#define IPA_FLT_SRC_PORT       (1ul << 9)
+#define IPA_FLT_DST_PORT       (1ul << 10)
+#define IPA_FLT_TC             (1ul << 11)
+#define IPA_FLT_FLOW_LABEL     (1ul << 12)
+#define IPA_FLT_NEXT_HDR       (1ul << 13)
+#define IPA_FLT_META_DATA      (1ul << 14)
+#define IPA_FLT_FRAGMENT       (1ul << 15)
+
+/**
+ * enum ipa_client_type - names for the various IPA "clients"
+ * these are from the perspective of the clients, for e.g.
+ * HSIC1_PROD means HSIC client is the producer and IPA is the
+ * consumer
+ */
+enum ipa_client_type {
+	IPA_CLIENT_PROD,
+	IPA_CLIENT_HSIC1_PROD = IPA_CLIENT_PROD,
+	IPA_CLIENT_HSIC2_PROD,
+	IPA_CLIENT_HSIC3_PROD,
+	IPA_CLIENT_HSIC4_PROD,
+	IPA_CLIENT_HSIC5_PROD,
+	IPA_CLIENT_USB_PROD,
+	IPA_CLIENT_A5_WLAN_AMPDU_PROD,
+	IPA_CLIENT_A2_EMBEDDED_PROD,
+	IPA_CLIENT_A2_TETHERED_PROD,
+	IPA_CLIENT_A5_LAN_WAN_PROD,
+	IPA_CLIENT_A5_CMD_PROD,
+	IPA_CLIENT_Q6_LAN_PROD,
+
+	IPA_CLIENT_CONS,
+	IPA_CLIENT_HSIC1_CONS = IPA_CLIENT_CONS,
+	IPA_CLIENT_HSIC2_CONS,
+	IPA_CLIENT_HSIC3_CONS,
+	IPA_CLIENT_HSIC4_CONS,
+	IPA_CLIENT_HSIC5_CONS,
+	IPA_CLIENT_USB_CONS,
+	IPA_CLIENT_A2_EMBEDDED_CONS,
+	IPA_CLIENT_A2_TETHERED_CONS,
+	IPA_CLIENT_A5_LAN_WAN_CONS,
+	IPA_CLIENT_Q6_LAN_CONS,
+
+	IPA_CLIENT_MAX,
+};
+
+/**
+ * enum ipa_ip_type - Address family: IPv4 or IPv6
+ */
+enum ipa_ip_type {
+	IPA_IP_v4,
+	IPA_IP_v6,
+	IPA_IP_MAX
+};
+
+/**
+ * enum ipa_flt_action - action field of filtering rule
+ *
+ * Pass to routing: 5'd0
+ * Pass to source NAT: 5'd1
+ * Pass to destination NAT: 5'd2
+ * Pass to default output pipe (e.g., A5): 5'd3
+ */
+enum ipa_flt_action {
+	IPA_PASS_TO_ROUTING,
+	IPA_PASS_TO_SRC_NAT,
+	IPA_PASS_TO_DST_NAT,
+	IPA_PASS_TO_EXCEPTION
+};
+
+/**
+ * struct ipa_rule_attrib - attributes of a routing/filtering
+ * rule, all in LE
+ * @attrib_mask: what attributes are valid
+ * @src_port_lo: low port of src port range
+ * @src_port_hi: high port of src port range
+ * @dst_port_lo: low port of dst port range
+ * @dst_port_hi: high port of dst port range
+ * @type: ICMP/IGMP type
+ * @code: ICMP/IGMP code
+ * @spi: IPSec SPI
+ * @src_port: exact src port
+ * @dst_port: exact dst port
+ * @meta_data: meta-data val
+ * @meta_data_mask: meta-data mask
+ * @u.v4.tos: type of service
+ * @u.v4.protocol: protocol
+ * @u.v4.src_addr: src address value
+ * @u.v4.src_addr_mask: src address mask
+ * @u.v4.dst_addr: dst address value
+ * @u.v4.dst_addr_mask: dst address mask
+ * @u.v6.tc: traffic class
+ * @u.v6.flow_label: flow label
+ * @u.v6.next_hdr: next header
+ * @u.v6.src_addr: src address val
+ * @u.v6.src_addr_mask: src address mask
+ * @u.v6.dst_addr: dst address val
+ * @u.v6.dst_addr_mask: dst address mask
+ */
+struct ipa_rule_attrib {
+	uint32_t attrib_mask;
+	uint16_t src_port_lo;
+	uint16_t src_port_hi;
+	uint16_t dst_port_lo;
+	uint16_t dst_port_hi;
+	uint8_t type;
+	uint8_t code;
+	uint32_t spi;
+	uint16_t src_port;
+	uint16_t dst_port;
+	uint32_t meta_data;
+	uint32_t meta_data_mask;
+	union {
+		struct {
+			uint8_t tos;
+			uint8_t protocol;
+			uint32_t src_addr;
+			uint32_t src_addr_mask;
+			uint32_t dst_addr;
+			uint32_t dst_addr_mask;
+		} v4;
+		struct {
+			uint8_t tc;
+			uint32_t flow_label;
+			uint8_t next_hdr;
+			uint32_t src_addr[4];
+			uint32_t src_addr_mask[4];
+			uint32_t dst_addr[4];
+			uint32_t dst_addr_mask[4];
+		} v6;
+	} u;
+};
+
+/**
+ * struct ipa_flt_rule - attributes of a filtering rule
+ * @action: action field
+ * @rt_tbl_hdl: handle of table from "get"
+ * @attrib: attributes of the rule
+ */
+struct ipa_flt_rule {
+	enum ipa_flt_action action;
+	uint32_t rt_tbl_hdl;
+	struct ipa_rule_attrib attrib;
+};
+
+/**
+ * struct ipa_rt_rule - attributes of a routing rule
+ * @dst: dst "client"
+ * @hdr_hdl: handle to the dynamic header
+	it is not an index or an offset
+ * @attrib: attributes of the rule
+ */
+struct ipa_rt_rule {
+	enum ipa_client_type dst;
+	uint32_t hdr_hdl;
+	struct ipa_rule_attrib attrib;
+};
+
+/**
+ * struct ipa_hdr_add - header descriptor includes in and out
+ * parameters
+ * @name: name of the header
+ * @hdr: actual header to be inserted
+ * @hdr_len: size of above header
+ * @is_partial: header not fully specified
+ * @hdr_hdl: out paramerer, handle to header, valid when status is 0
+ * @status:	out paramerer, status of header add operation,
+ *		0 for success,
+ *		-1 for failure
+ */
+struct ipa_hdr_add {
+	char name[IPA_RESOURCE_NAME_MAX];
+	uint8_t hdr[IPA_HDR_MAX_SIZE];
+	uint8_t hdr_len;
+	uint8_t is_partial;
+	uint32_t hdr_hdl;
+	int status;
+};
+
+/**
+ * struct ipa_ioc_add_hdr - header addition parameters (support
+ * multiple headers and commit)
+ * @commit: should headers be written to IPA HW also?
+ * @num_hdrs: num of headers that follow
+ * @ipa_hdr_add hdr:	all headers need to go here back to
+ *			back, no pointers
+ */
+struct ipa_ioc_add_hdr {
+	uint8_t commit;
+	uint8_t num_hdrs;
+	struct ipa_hdr_add hdr[0];
+};
+
+/**
+ * struct ipa_ioc_copy_hdr - retrieve a copy of the specified
+ * header - caller can then derive the complete header
+ * @name: name of the header resource
+ * @hdr:	out parameter, contents of specified header,
+ *	valid only when ioctl return val is non-negative
+ * @hdr_len: out parameter, size of above header
+ *	valid only when ioctl return val is non-negative
+ * @is_partial:	out parameter, indicates whether specified header is partial
+ *		valid only when ioctl return val is non-negative
+ */
+struct ipa_ioc_copy_hdr {
+	char name[IPA_RESOURCE_NAME_MAX];
+	uint8_t hdr[IPA_HDR_MAX_SIZE];
+	uint8_t hdr_len;
+	uint8_t is_partial;
+};
+
+/**
+ * struct ipa_ioc_get_hdr - header entry lookup parameters, if lookup was
+ * successful caller must call put to release the reference count when done
+ * @name: name of the header resource
+ * @hdl:	out parameter, handle of header entry
+ *		valid only when ioctl return val is non-negative
+ */
+struct ipa_ioc_get_hdr {
+	char name[IPA_RESOURCE_NAME_MAX];
+	uint32_t hdl;
+};
+
+/**
+ * struct ipa_hdr_del - header descriptor includes in and out
+ * parameters
+ *
+ * @hdl: handle returned from header add operation
+ * @status:	out parameter, status of header remove operation,
+ *		0 for success,
+ *		-1 for failure
+ */
+struct ipa_hdr_del {
+	uint32_t hdl;
+	int status;
+};
+
+/**
+ * struct ipa_ioc_del_hdr - header deletion parameters (support
+ * multiple headers and commit)
+ * @commit: should headers be removed from IPA HW also?
+ * @num_hdls: num of headers being removed
+ * @ipa_hdr_del hdl: all handles need to go here back to back, no pointers
+ */
+struct ipa_ioc_del_hdr {
+	uint8_t commit;
+	uint8_t num_hdls;
+	struct ipa_hdr_del hdl[0];
+};
+
+/**
+ * struct ipa_rt_rule_add - routing rule descriptor includes in
+ * and out parameters
+ * @rule: actual rule to be added
+ * @at_rear:	add at back of routing table, it is NOT possible to add rules at
+ *		the rear of the "default" routing tables
+ * @rt_rule_hdl: output parameter, handle to rule, valid when status is 0
+ * @status:	output parameter, status of routing rule add operation,
+ *		0 for success,
+ *		-1 for failure
+ */
+struct ipa_rt_rule_add {
+	struct ipa_rt_rule rule;
+	uint8_t at_rear;
+	uint32_t rt_rule_hdl;
+	int status;
+};
+
+/**
+ * struct ipa_ioc_add_rt_rule - routing rule addition parameters (supports
+ * multiple rules and commit);
+ *
+ * all rules MUST be added to same table
+ * @commit: should rules be written to IPA HW also?
+ * @ip: IP family of rule
+ * @rt_tbl_name: name of routing table resource
+ * @num_rules: number of routing rules that follow
+ * @ipa_rt_rule_add rules: all rules need to go back to back here, no pointers
+ */
+struct ipa_ioc_add_rt_rule {
+	uint8_t commit;
+	enum ipa_ip_type ip;
+	char rt_tbl_name[IPA_RESOURCE_NAME_MAX];
+	uint8_t num_rules;
+	struct ipa_rt_rule_add rules[0];
+};
+
+/**
+ * struct ipa_rt_rule_del - routing rule descriptor includes in
+ * and out parameters
+ * @hdl: handle returned from route rule add operation
+ * @status:	output parameter, status of route rule delete operation,
+ *		0 for success,
+ *		-1 for failure
+ */
+struct ipa_rt_rule_del {
+	uint32_t hdl;
+	int status;
+};
+
+/**
+ * struct ipa_ioc_del_rt_rule - routing rule deletion parameters (supports
+ * multiple headers and commit)
+ * @commit: should rules be removed from IPA HW also?
+ * @ip: IP family of rules
+ * @num_hdls: num of rules being removed
+ * @ipa_rt_rule_del hdl: all handles need to go back to back here, no pointers
+ */
+struct ipa_ioc_del_rt_rule {
+	uint8_t commit;
+	enum ipa_ip_type ip;
+	uint8_t num_hdls;
+	struct ipa_rt_rule_del hdl[0];
+};
+
+/**
+ * struct ipa_flt_rule_add - filtering rule descriptor includes
+ * in and out parameters
+ * @rule: actual rule to be added
+ * @at_rear: add at back of filtering table?
+ * @flt_rule_hdl: out parameter, handle to rule, valid when status is 0
+ * @status:	output parameter, status of filtering rule add   operation,
+ *		0 for success,
+ *		-1 for failure
+ *
+ */
+struct ipa_flt_rule_add {
+	struct ipa_flt_rule rule;
+	uint8_t at_rear;
+	uint32_t flt_rule_hdl;
+	int status;
+};
+
+/**
+ * struct ipa_ioc_add_flt_rule - filtering rule addition parameters (supports
+ * multiple rules and commit)
+ * all rules MUST be added to same table
+ * @commit: should rules be written to IPA HW also?
+ * @ip: IP family of rule
+ * @ep:	which "clients" pipe does this rule apply to?
+ *	valid only when global is 0
+ * @global: does this apply to global filter table of specific IP family
+ * @num_rules: number of filtering rules that follow
+ * @rules: all rules need to go back to back here, no pointers
+ */
+struct ipa_ioc_add_flt_rule {
+	uint8_t commit;
+	enum ipa_ip_type ip;
+	enum ipa_client_type ep;
+	uint8_t global;
+	uint8_t num_rules;
+	struct ipa_flt_rule_add rules[0];
+};
+
+/**
+ * struct ipa_flt_rule_del - filtering rule descriptor includes
+ * in and out parameters
+ *
+ * @hdl: handle returned from filtering rule add operation
+ * @status:	output parameter, status of filtering rule delete operation,
+ *		0 for success,
+ *		-1 for failure
+ */
+struct ipa_flt_rule_del {
+	uint32_t hdl;
+	int status;
+};
+
+/**
+ * struct ipa_ioc_del_flt_rule - filtering rule deletion parameters (supports
+ * multiple headers and commit)
+ * @commit: should rules be removed from IPA HW also?
+ * @ip: IP family of rules
+ * @num_hdls: num of rules being removed
+ * @hdl: all handles need to go back to back here, no pointers
+ */
+struct ipa_ioc_del_flt_rule {
+	uint8_t commit;
+	enum ipa_ip_type ip;
+	uint8_t num_hdls;
+	struct ipa_flt_rule_del hdl[0];
+};
+
+/**
+ * struct ipa_ioc_get_rt_tbl - routing table lookup parameters, if lookup was
+ * successful caller must call put to release the reference
+ * count when done
+ * @ip: IP family of table
+ * @name: name of routing table resource
+ * @htl:	output parameter, handle of routing table, valid only when ioctl
+ *		return val is non-negative
+ */
+struct ipa_ioc_get_rt_tbl {
+	enum ipa_ip_type ip;
+	char name[IPA_RESOURCE_NAME_MAX];
+	uint32_t hdl;
+};
+
+/**
+ * struct ipa_ioc_query_intf - used to lookup number of tx and
+ * rx properties of interface
+ * @name: name of interface
+ * @num_tx_props:	output parameter, number of tx properties
+ *			valid only when ioctl return val is non-negative
+ * @num_rx_props:	output parameter, number of rx properties
+ *			valid only when ioctl return val is non-negative
+ */
+struct ipa_ioc_query_intf {
+	char name[IPA_RESOURCE_NAME_MAX];
+	uint32_t num_tx_props;
+	uint32_t num_rx_props;
+};
+
+/**
+ * struct ipa_ioc_tx_intf_prop - interface tx property
+ * @ip: IP family of routing rule
+ * @attrib: routing rule
+ * @dst_pipe: routing output pipe
+ * @hdr_name: name of associated header if any, empty string when no header
+ */
+struct ipa_ioc_tx_intf_prop {
+	enum ipa_ip_type ip;
+	struct ipa_rule_attrib attrib;
+	enum ipa_client_type dst_pipe;
+	char hdr_name[IPA_RESOURCE_NAME_MAX];
+};
+
+/**
+ * struct ipa_ioc_query_intf_tx_props - interface tx propertie
+ * @name: name of interface
+ * @tx[0]: output parameter, the tx properties go here back to back
+ */
+struct ipa_ioc_query_intf_tx_props {
+	char name[IPA_RESOURCE_NAME_MAX];
+	struct ipa_ioc_tx_intf_prop tx[0];
+};
+
+/**
+ * struct ipa_ioc_rx_intf_prop - interface rx property
+ * @ip: IP family of filtering rule
+ * @attrib: filtering rule
+ * @src_pipe: input pipe
+ */
+struct ipa_ioc_rx_intf_prop {
+	enum ipa_ip_type ip;
+	struct ipa_rule_attrib attrib;
+	enum ipa_client_type src_pipe;
+};
+
+/**
+ * struct ipa_ioc_query_intf_rx_props - interface rx propertie
+ * @name: name of interface
+ * @rx: output parameter, the rx properties go here back to back
+ */
+struct ipa_ioc_query_intf_rx_props {
+	char name[IPA_RESOURCE_NAME_MAX];
+	struct ipa_ioc_rx_intf_prop rx[0];
+};
+
+/**
+ * struct ipa_ioc_nat_alloc_mem - nat table memory allocation
+ * properties
+ * @dev_name: input parameter, the name of table
+ * @size: input parameter, size of table in bytes
+ * @offset: output parameter, offset into page in case of system memory
+ */
+struct ipa_ioc_nat_alloc_mem {
+	char dev_name[IPA_RESOURCE_NAME_MAX];
+	size_t size;
+	off_t offset;
+};
+
+/**
+ * struct ipa_ioc_v4_nat_init - nat table initialization
+ * parameters
+ * @tbl_index: input parameter, index of the table
+ * @ipv4_rules_offset: input parameter, ipv4 rules address offset
+ * @expn_rules_offset: input parameter, ipv4 expansion rules address offset
+ * @index_offset: input parameter, index rules offset
+ * @index_expn_offset: input parameter, index expansion rules offset
+ * @table_entries: input parameter, ipv4 rules table size in entries
+ * @expn_table_entries: input parameter, ipv4 expansion rules table size
+ * @ip_addr: input parameter, public ip address
+ */
+struct ipa_ioc_v4_nat_init {
+	uint8_t tbl_index;
+	uint32_t ipv4_rules_offset;
+	uint32_t expn_rules_offset;
+
+	uint32_t index_offset;
+	uint32_t index_expn_offset;
+
+	uint16_t table_entries;
+	uint16_t expn_table_entries;
+	uint32_t ip_addr;
+};
+
+/**
+ * struct ipa_ioc_v4_nat_del - nat table delete parameter
+ * @table_index: input parameter, index of the table
+ * @public_ip_addr: input parameter, public ip address
+ */
+struct ipa_ioc_v4_nat_del {
+	uint8_t table_index;
+	uint32_t public_ip_addr;
+};
+
+/**
+ * struct ipa_ioc_nat_dma_one - nat dma command parameter
+ * @table_index: input parameter, index of the table
+ * @base_addr:	type of table, from which the base address of the table
+ *		can be inferred
+ * @offset: destination offset within the NAT table
+ * @data: data to be written.
+ */
+struct ipa_ioc_nat_dma_one {
+	uint8_t table_index;
+	uint8_t base_addr;
+
+	uint32_t offset;
+	uint16_t data;
+
+};
+
+/**
+ * struct ipa_ioc_nat_dma_cmd - To hold multiple nat dma commands
+ * @entries: number of dma commands in use
+ * @dma: data pointer to the dma commands
+ */
+struct ipa_ioc_nat_dma_cmd {
+	uint8_t entries;
+	struct ipa_ioc_nat_dma_one dma[0];
+
+};
+
+/**
+ * struct ipa_msg_meta - Format of the message meta-data.
+ * @msg_type: the type of the message
+ * @msg_len: the length of the message in bytes
+ * @rsvd: reserved bits for future use.
+ *
+ * Client in user-space should issue a read on the device (/dev/ipa) with a
+ * buffer of atleast this size in an continuous loop, call will block when there
+ * is no pending async message.
+ *
+ * After reading a message's meta-data using above scheme, client should issue a
+ * GET_MSG IOCTL to actually read the message itself into the buffer of
+ * "msg_len" immediately following the ipa_msg_meta itself in the IOCTL payload
+ */
+struct ipa_msg_meta {
+	uint8_t msg_type;
+	uint16_t msg_len;
+	uint8_t rsvd;
+};
+
+/**
+ *   actual IOCTLs supported by IPA driver
+ */
+#define IPA_IOC_ADD_HDR _IOWR(IPA_IOC_MAGIC, \
+					IPA_IOCTL_ADD_HDR, \
+					struct ipa_ioc_add_hdr *)
+#define IPA_IOC_DEL_HDR _IOWR(IPA_IOC_MAGIC, \
+					IPA_IOCTL_DEL_HDR, \
+					struct ipa_ioc_del_hdr *)
+#define IPA_IOC_ADD_RT_RULE _IOWR(IPA_IOC_MAGIC, \
+					IPA_IOCTL_ADD_RT_RULE, \
+					struct ipa_ioc_add_rt_rule *)
+#define IPA_IOC_DEL_RT_RULE _IOWR(IPA_IOC_MAGIC, \
+					IPA_IOCTL_DEL_RT_RULE, \
+					struct ipa_ioc_del_rt_rule *)
+#define IPA_IOC_ADD_FLT_RULE _IOWR(IPA_IOC_MAGIC, \
+					IPA_IOCTL_ADD_FLT_RULE, \
+					struct ipa_ioc_add_flt_rule *)
+#define IPA_IOC_DEL_FLT_RULE _IOWR(IPA_IOC_MAGIC, \
+					IPA_IOCTL_DEL_FLT_RULE, \
+					struct ipa_ioc_del_flt_rule *)
+#define IPA_IOC_COMMIT_HDR _IO(IPA_IOC_MAGIC,\
+					IPA_IOCTL_COMMIT_HDR)
+#define IPA_IOC_RESET_HDR _IO(IPA_IOC_MAGIC,\
+					IPA_IOCTL_RESET_HDR)
+#define IPA_IOC_COMMIT_RT _IOW(IPA_IOC_MAGIC, \
+					IPA_IOCTL_COMMIT_RT, \
+					enum ipa_ip_type)
+#define IPA_IOC_RESET_RT _IOW(IPA_IOC_MAGIC, \
+					IPA_IOCTL_RESET_RT, \
+					enum ipa_ip_type)
+#define IPA_IOC_COMMIT_FLT _IOW(IPA_IOC_MAGIC, \
+					IPA_IOCTL_COMMIT_FLT, \
+					enum ipa_ip_type)
+#define IPA_IOC_RESET_FLT _IOW(IPA_IOC_MAGIC, \
+			IPA_IOCTL_RESET_FLT, \
+			enum ipa_ip_type)
+#define IPA_IOC_DUMP _IO(IPA_IOC_MAGIC, \
+			IPA_IOCTL_DUMP)
+#define IPA_IOC_GET_RT_TBL _IOWR(IPA_IOC_MAGIC, \
+				IPA_IOCTL_GET_RT_TBL, \
+				struct ipa_ioc_get_rt_tbl *)
+#define IPA_IOC_PUT_RT_TBL _IOW(IPA_IOC_MAGIC, \
+				IPA_IOCTL_PUT_RT_TBL, \
+				uint32_t)
+#define IPA_IOC_COPY_HDR _IOWR(IPA_IOC_MAGIC, \
+				IPA_IOCTL_COPY_HDR, \
+				struct ipa_ioc_copy_hdr *)
+#define IPA_IOC_QUERY_INTF _IOWR(IPA_IOC_MAGIC, \
+				IPA_IOCTL_QUERY_INTF, \
+				struct ipa_ioc_query_intf *)
+#define IPA_IOC_QUERY_INTF_TX_PROPS _IOWR(IPA_IOC_MAGIC, \
+				IPA_IOCTL_QUERY_INTF_TX_PROPS, \
+				struct ipa_ioc_query_intf_tx_props *)
+#define IPA_IOC_QUERY_INTF_RX_PROPS _IOWR(IPA_IOC_MAGIC, \
+					IPA_IOCTL_QUERY_INTF_RX_PROPS, \
+					struct ipa_ioc_query_intf_rx_props *)
+#define IPA_IOC_GET_HDR _IOWR(IPA_IOC_MAGIC, \
+				IPA_IOCTL_GET_HDR, \
+				struct ipa_ioc_get_hdr *)
+#define IPA_IOC_PUT_HDR _IOW(IPA_IOC_MAGIC, \
+				IPA_IOCTL_PUT_HDR, \
+				uint32_t)
+#define IPA_IOC_ALLOC_NAT_MEM _IOWR(IPA_IOC_MAGIC, \
+				IPA_IOCTL_ALLOC_NAT_MEM, \
+				struct ipa_ioc_nat_alloc_mem *)
+#define IPA_IOC_V4_INIT_NAT _IOWR(IPA_IOC_MAGIC, \
+				IPA_IOCTL_V4_INIT_NAT, \
+				struct ipa_ioc_v4_nat_init *)
+#define IPA_IOC_NAT_DMA _IOWR(IPA_IOC_MAGIC, \
+				IPA_IOCTL_NAT_DMA, \
+				struct ipa_ioc_nat_dma_cmd *)
+#define IPA_IOC_V4_DEL_NAT _IOWR(IPA_IOC_MAGIC, \
+				IPA_IOCTL_V4_DEL_NAT, \
+				struct ipa_ioc_v4_nat_del *)
+#define IPA_IOC_GET_NAT_OFFSET _IOWR(IPA_IOC_MAGIC, \
+				IPA_IOCTL_GET_NAT_OFFSET, \
+				uint32_t *)
+#define IPA_IOC_SET_FLT _IOW(IPA_IOC_MAGIC, \
+			IPA_IOCTL_SET_FLT, \
+			uint32_t)
+#define IPA_IOC_GET_ASYNC_MSG _IOWR(IPA_IOC_MAGIC, \
+				IPA_IOCTL_GET_ASYNC_MSG, \
+				struct ipa_msg_meta *)
+
+#endif /* _MSM_IPA_H_ */
diff --git a/include/linux/msm_ipc.h b/include/linux/msm_ipc.h
index 44fa8eb..7b6bf41 100644
--- a/include/linux/msm_ipc.h
+++ b/include/linux/msm_ipc.h
@@ -45,6 +45,14 @@
 	unsigned char reserved;
 };
 
+struct config_sec_rules_args {
+	int num_group_info;
+	uint32_t service_id;
+	uint32_t instance_id;
+	unsigned reserved;
+	gid_t group_id[0];
+};
+
 #define IPC_ROUTER_IOCTL_MAGIC (0xC3)
 
 #define IPC_ROUTER_IOCTL_GET_VERSION \
@@ -62,6 +70,9 @@
 #define IPC_ROUTER_IOCTL_BIND_CONTROL_PORT \
 	_IOR(IPC_ROUTER_IOCTL_MAGIC, 4, unsigned int)
 
+#define IPC_ROUTER_IOCTL_CONFIG_SEC_RULES \
+	_IOR(IPC_ROUTER_IOCTL_MAGIC, 5, struct config_sec_rules_args)
+
 struct msm_ipc_server_info {
 	uint32_t node_id;
 	uint32_t port_id;
diff --git a/include/linux/msm_kgsl.h b/include/linux/msm_kgsl.h
index 6912087..4e62b4f 100644
--- a/include/linux/msm_kgsl.h
+++ b/include/linux/msm_kgsl.h
@@ -1,6 +1,13 @@
 #ifndef _MSM_KGSL_H
 #define _MSM_KGSL_H
 
+/*
+ * The KGSL version has proven not to be very useful in userspace if features
+ * are cherry picked into other trees out of order so it is frozen as of 3.14.
+ * It is left here for backwards compatabilty and as a reminder that
+ * software releases are never linear. Also, I like pie.
+ */
+
 #define KGSL_VERSION_MAJOR        3
 #define KGSL_VERSION_MINOR        14
 
@@ -16,13 +23,25 @@
 
 #define KGSL_CONTEXT_INVALID 0xffffffff
 
-/* Memory allocayion flags */
-#define KGSL_MEMFLAGS_GPUREADONLY	0x01000000
+/* --- Memory allocation flags --- */
 
+/* General allocation hints */
+#define KGSL_MEMFLAGS_GPUREADONLY 0x01000000
+#define KGSL_MEMFLAGS_USE_CPU_MAP 0x10000000
+
+/* Memory caching hints */
+#define KGSL_CACHEMODE_MASK 0x0C000000
+#define KGSL_CACHEMODE_SHIFT 26
+
+#define KGSL_CACHEMODE_WRITECOMBINE 0
+#define KGSL_CACHEMODE_UNCACHED 1
+#define KGSL_CACHEMODE_WRITETHROUGH 2
+#define KGSL_CACHEMODE_WRITEBACK 3
+
+/* Memory types for which allocations are made */
 #define KGSL_MEMTYPE_MASK		0x0000FF00
 #define KGSL_MEMTYPE_SHIFT		8
 
-/* Memory types for which allocations are made */
 #define KGSL_MEMTYPE_OBJECTANY			0
 #define KGSL_MEMTYPE_FRAMEBUFFER		1
 #define KGSL_MEMTYPE_RENDERBUFFER		2
@@ -53,7 +72,8 @@
 #define KGSL_MEMALIGN_MASK		0x00FF0000
 #define KGSL_MEMALIGN_SHIFT		16
 
-/* generic flag values */
+/* --- generic KGSL flag values --- */
+
 #define KGSL_FLAGS_NORMALMODE  0x00000000
 #define KGSL_FLAGS_SAFEMODE    0x00000001
 #define KGSL_FLAGS_INITIALIZED0 0x00000002
@@ -419,6 +439,14 @@
 #define IOCTL_KGSL_SHAREDMEM_FROM_VMALLOC \
 	_IOWR(KGSL_IOC_TYPE, 0x23, struct kgsl_sharedmem_from_vmalloc)
 
+/*
+ * This is being deprecated in favor of IOCTL_KGSL_GPUMEM_CACHE_SYNC which
+ * supports both directions (flush and invalidate). This code will still
+ * work, but by definition it will do a flush of the cache which might not be
+ * what you want to have happen on a buffer following a GPU operation.  It is
+ * safer to go with IOCTL_KGSL_GPUMEM_CACHE_SYNC
+ */
+
 #define IOCTL_KGSL_SHAREDMEM_FLUSH_CACHE \
 	_IOW(KGSL_IOC_TYPE, 0x24, struct kgsl_sharedmem_free)
 
@@ -511,6 +539,115 @@
 #define IOCTL_KGSL_TIMESTAMP_EVENT \
 	_IOWR(KGSL_IOC_TYPE, 0x33, struct kgsl_timestamp_event)
 
+/**
+ * struct kgsl_gpumem_alloc_id - argument to IOCTL_KGSL_GPUMEM_ALLOC_ID
+ * @id: returned id value for this allocation.
+ * @flags: mask of KGSL_MEM* values requested and actual flags on return.
+ * @size: requested size of the allocation and actual size on return.
+ * @mmapsize: returned size to pass to mmap() which may be larger than 'size'
+ * @gpuaddr: returned GPU address for the allocation
+ *
+ * Allocate memory for access by the GPU. The flags and size fields are echoed
+ * back by the kernel, so that the caller can know if the request was
+ * adjusted.
+ *
+ * Supported flags:
+ * KGSL_MEMFLAGS_GPUREADONLY: the GPU will be unable to write to the buffer
+ * KGSL_MEMTYPE*: usage hint for debugging aid
+ * KGSL_MEMALIGN*: alignment hint, may be ignored or adjusted by the kernel.
+ * KGSL_MEMFLAGS_USE_CPU_MAP: If set on call and return, the returned GPU
+ * address will be 0. Calling mmap() will set the GPU address.
+ */
+struct kgsl_gpumem_alloc_id {
+	unsigned int id;
+	unsigned int flags;
+	unsigned int size;
+	unsigned int mmapsize;
+	unsigned long gpuaddr;
+/* private: reserved for future use*/
+	unsigned int __pad[2];
+};
+
+#define IOCTL_KGSL_GPUMEM_ALLOC_ID \
+	_IOWR(KGSL_IOC_TYPE, 0x34, struct kgsl_gpumem_alloc_id)
+
+/**
+ * struct kgsl_gpumem_free_id - argument to IOCTL_KGSL_GPUMEM_FREE_ID
+ * @id: GPU allocation id to free
+ *
+ * Free an allocation by id, in case a GPU address has not been assigned or
+ * is unknown. Freeing an allocation by id with this ioctl or by GPU address
+ * with IOCTL_KGSL_SHAREDMEM_FREE are equivalent.
+ */
+struct kgsl_gpumem_free_id {
+	unsigned int id;
+/* private: reserved for future use*/
+	unsigned int __pad;
+};
+
+#define IOCTL_KGSL_GPUMEM_FREE_ID \
+	_IOWR(KGSL_IOC_TYPE, 0x35, struct kgsl_gpumem_free_id)
+
+/**
+ * struct kgsl_gpumem_get_info - argument to IOCTL_KGSL_GPUMEM_GET_INFO
+ * @gpuaddr: GPU address to query. Also set on return.
+ * @id: GPU allocation id to query. Also set on return.
+ * @flags: returned mask of KGSL_MEM* values.
+ * @size: returned size of the allocation.
+ * @mmapsize: returned size to pass mmap(), which may be larger than 'size'
+ * @useraddr: returned address of the userspace mapping for this buffer
+ *
+ * This ioctl allows querying of all user visible attributes of an existing
+ * allocation, by either the GPU address or the id returned by a previous
+ * call to IOCTL_KGSL_GPUMEM_ALLOC_ID. Legacy allocation ioctls may not
+ * return all attributes so this ioctl can be used to look them up if needed.
+ *
+ */
+struct kgsl_gpumem_get_info {
+	unsigned long gpuaddr;
+	unsigned int id;
+	unsigned int flags;
+	unsigned int size;
+	unsigned int mmapsize;
+	unsigned long useraddr;
+/* private: reserved for future use*/
+	unsigned int __pad[4];
+};
+
+#define IOCTL_KGSL_GPUMEM_GET_INFO\
+	_IOWR(KGSL_IOC_TYPE, 0x36, struct kgsl_gpumem_get_info)
+
+/**
+ * struct kgsl_gpumem_sync_cache - argument to IOCTL_KGSL_GPUMEM_SYNC_CACHE
+ * @gpuaddr: GPU address of the buffer to sync.
+ * @id: id of the buffer to sync. Either gpuaddr or id is sufficient.
+ * @op: a mask of KGSL_GPUMEM_CACHE_* values
+ *
+ * Sync the L2 cache for memory headed to and from the GPU - this replaces
+ * KGSL_SHAREDMEM_FLUSH_CACHE since it can handle cache management for both
+ * directions
+ *
+ */
+struct kgsl_gpumem_sync_cache {
+	unsigned int gpuaddr;
+	unsigned int id;
+	unsigned int op;
+/* private: reserved for future use*/
+	unsigned int __pad[2]; /* For future binary compatibility */
+};
+
+#define KGSL_GPUMEM_CACHE_CLEAN (1 << 0)
+#define KGSL_GPUMEM_CACHE_TO_GPU KGSL_GPUMEM_CACHE_CLEAN
+
+#define KGSL_GPUMEM_CACHE_INV (1 << 1)
+#define KGSL_GPUMEM_CACHE_FROM_GPU KGSL_GPUMEM_CACHE_INV
+
+#define KGSL_GPUMEM_CACHE_FLUSH \
+	(KGSL_GPUMEM_CACHE_CLEAN | KGSL_GPUMEM_CACHE_INV)
+
+#define IOCTL_KGSL_GPUMEM_SYNC_CACHE \
+	_IOW(KGSL_IOC_TYPE, 0x37, struct kgsl_gpumem_sync_cache)
+
 #ifdef __KERNEL__
 #ifdef CONFIG_MSM_KGSL_DRM
 int kgsl_gem_obj_addr(int drm_fd, int handle, unsigned long *start,
diff --git a/include/linux/msm_mdp.h b/include/linux/msm_mdp.h
index 1cdc434..d3f6792 100644
--- a/include/linux/msm_mdp.h
+++ b/include/linux/msm_mdp.h
@@ -71,7 +71,7 @@
 #define MSMFB_OVERLAY_VSYNC_CTRL _IOW(MSMFB_IOCTL_MAGIC, 160, unsigned int)
 #define MSMFB_VSYNC_CTRL  _IOW(MSMFB_IOCTL_MAGIC, 161, unsigned int)
 #define MSMFB_METADATA_SET  _IOW(MSMFB_IOCTL_MAGIC, 162, struct msmfb_metadata)
-#define MSMFB_OVERLAY_COMMIT      _IOW(MSMFB_IOCTL_MAGIC, 163, unsigned int)
+#define MSMFB_OVERLAY_COMMIT      _IO(MSMFB_IOCTL_MAGIC, 163)
 
 #define FB_TYPE_3D_PANEL 0x10101010
 #define MDP_IMGTYPE2_START 0x10000
@@ -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/nl80211.h b/include/linux/nl80211.h
index 89a8421..e0d9072 100644
--- a/include/linux/nl80211.h
+++ b/include/linux/nl80211.h
@@ -548,6 +548,14 @@
  * @NL80211_CMD_SET_NOACK_MAP: sets a bitmap for the individual TIDs whether
  *      No Acknowledgement Policy should be applied.
  *
+ * @NL80211_CMD_UPDATE_FT_IES: Pass down the most up-to-date Fast Transition
+ *	Information Element to the WLAN driver
+ *
+ * @NL80211_CMD_FT_EVENT: Send a Fast transition event from the WLAN driver
+ *	to the supplicant. This will carry the target AP's MAC address along
+ *	with the relevant Information Elements. This event to report received
+ *	FT IEs( MDIE, FTIE,RSN IE, TIE, RICIE).
+ *
  * @NL80211_CMD_MAX: highest used command number
  * @__NL80211_CMD_AFTER_LAST: internal use
  */
@@ -689,6 +697,9 @@
 
 	NL80211_CMD_SET_NOACK_MAP,
 
+	NL80211_CMD_UPDATE_FT_IES,
+	NL80211_CMD_FT_EVENT,
+
 	/* add new commands above here */
 
 	/* used to define NL80211_CMD_MAX below */
@@ -1264,6 +1275,10 @@
  * @NL80211_ATTR_BG_SCAN_PERIOD: Background scan period in seconds
  *      or 0 to disable background scan.
  *
+ * @NL80211_ATTR_MDID: Mobility Domain Identifier
+ *
+ * @NL80211_ATTR_IE_RIC: Resource Information Container Information Element
+ *
  * @NL80211_ATTR_MAX: highest attribute number currently defined
  * @__NL80211_ATTR_AFTER_LAST: internal use
  */
@@ -1515,6 +1530,9 @@
 
 	NL80211_ATTR_BG_SCAN_PERIOD,
 
+	NL80211_ATTR_MDID,
+	NL80211_ATTR_IE_RIC,
+
 	/* add attributes here, update the policy in nl80211.c */
 
 	__NL80211_ATTR_AFTER_LAST,
diff --git a/include/linux/of_irq.h b/include/linux/of_irq.h
index d229ad3..1717cd9 100644
--- a/include/linux/of_irq.h
+++ b/include/linux/of_irq.h
@@ -11,7 +11,7 @@
 #include <linux/of.h>
 
 /*
- * irq_of_parse_and_map() is used ba all OF enabled platforms; but SPARC
+ * irq_of_parse_and_map() is used by all OF enabled platforms; but SPARC
  * implements it differently.  However, the prototype is the same for all,
  * so declare it here regardless of the CONFIG_OF_IRQ setting.
  */
@@ -76,5 +76,13 @@
 extern void of_irq_init(const struct of_device_id *matches);
 
 #endif /* CONFIG_OF_IRQ */
-#endif /* CONFIG_OF */
+
+#else /* !CONFIG_OF */
+static inline unsigned int irq_of_parse_and_map(struct device_node *dev,
+						int index)
+{
+	return 0;
+}
+#endif /* !CONFIG_OF */
+
 #endif /* __OF_IRQ_H */
diff --git a/include/linux/qmi_encdec.h b/include/linux/qmi_encdec.h
new file mode 100644
index 0000000..4c5f6d3
--- /dev/null
+++ b/include/linux/qmi_encdec.h
@@ -0,0 +1,169 @@
+/* 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 _QMI_ENCDEC_H_
+#define _QMI_ENCDEC_H_
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/mm.h>
+#include <linux/list.h>
+#include <linux/socket.h>
+#include <linux/gfp.h>
+
+#define QMI_REQUEST_CONTROL_FLAG 0x00
+#define QMI_RESPONSE_CONTROL_FLAG 0x02
+#define QMI_INDICATION_CONTROL_FLAG 0x04
+#define QMI_HEADER_SIZE 7
+
+/**
+ * elem_type - Enum to identify the data type of elements in a data
+ *             structure.
+ */
+enum elem_type {
+	QMI_OPT_FLAG = 1,
+	QMI_DATA_LEN,
+	QMI_UNSIGNED_1_BYTE,
+	QMI_UNSIGNED_2_BYTE,
+	QMI_UNSIGNED_4_BYTE,
+	QMI_UNSIGNED_8_BYTE,
+	QMI_SIGNED_2_BYTE_ENUM,
+	QMI_SIGNED_4_BYTE_ENUM,
+	QMI_STRUCT,
+	QMI_EOTI,
+};
+
+/**
+ * array_type - Enum to identify if an element in a data structure is
+ *              an array. If so, then is it a static length array or a
+ *              variable length array.
+ */
+enum array_type {
+	NO_ARRAY = 0,
+	STATIC_ARRAY = 1,
+	VAR_LEN_ARRAY = 2,
+};
+
+/**
+ * elem_info - Data structure to specify information about an element
+ *               in a data structure. An array of this data structure
+ *               can be used to specify info about a complex data
+ *               structure to be encoded/decoded.
+ *
+ * @data_type: Data type of this element.
+ * @elem_len: Array length of this element, if an array.
+ * @elem_size: Size of a single instance of this data type.
+ * @is_array: Array type of this element.
+ * @tlv_type: QMI message specific type to identify which element
+ *            is present in an incoming message.
+ * @offset: To identify the address of the first instance of this
+ *          element in the data structure.
+ * @ei_array: Array to provide information about the nested structure
+ *            within a data structure to be encoded/decoded.
+ */
+struct elem_info {
+	enum elem_type data_type;
+	uint32_t elem_len;
+	uint32_t elem_size;
+	enum array_type is_array;
+	uint8_t tlv_type;
+	uint32_t offset;
+	struct elem_info *ei_array;
+};
+
+/**
+ * @msg_desc - Describe about the main/outer structure to be
+ *		  encoded/decoded.
+ *
+ * @max_msg_len: Maximum possible length of the QMI message.
+ * @ei_array: Array to provide information about a data structure.
+ */
+struct msg_desc {
+	uint16_t msg_id;
+	int max_msg_len;
+	struct elem_info *ei_array;
+};
+
+struct qmi_header {
+	unsigned char cntl_flag;
+	uint16_t txn_id;
+	uint16_t msg_id;
+	uint16_t msg_len;
+} __attribute__((__packed__));
+
+static inline void encode_qmi_header(unsigned char *buf,
+			unsigned char cntl_flag, uint16_t txn_id,
+			uint16_t msg_id, uint16_t msg_len)
+{
+	struct qmi_header *hdr = (struct qmi_header *)buf;
+
+	hdr->cntl_flag = cntl_flag;
+	hdr->txn_id = txn_id;
+	hdr->msg_id = msg_id;
+	hdr->msg_len = msg_len;
+}
+
+static inline void decode_qmi_header(unsigned char *buf,
+			unsigned char *cntl_flag, uint16_t *txn_id,
+			uint16_t *msg_id, uint16_t *msg_len)
+{
+	struct qmi_header *hdr = (struct qmi_header *)buf;
+
+	*cntl_flag = hdr->cntl_flag;
+	*txn_id = hdr->txn_id;
+	*msg_id = hdr->msg_id;
+	*msg_len = hdr->msg_len;
+}
+
+#ifdef CONFIG_QMI_ENCDEC
+/**
+ * qmi_kernel_encode() - Encode to QMI message wire format
+ * @desc: Pointer to structure descriptor.
+ * @out_buf: Buffer to hold the encoded QMI message.
+ * @out_buf_len: Length of the out buffer.
+ * @in_c_struct: C Structure to be encoded.
+ *
+ * @return: size of encoded message on success, < 0 on error.
+ */
+int qmi_kernel_encode(struct msg_desc *desc,
+		      void *out_buf, uint32_t out_buf_len,
+		      void *in_c_struct);
+
+/**
+ * qmi_kernel_decode() - Decode to C Structure format
+ * @desc: Pointer to structure descriptor.
+ * @out_c_struct: Buffer to hold the decoded C structure.
+ * @in_buf: Buffer containg the QMI message to be decoded.
+ * @in_buf_len: Length of the incoming QMI message.
+ *
+ * @return: 0 on success, < 0 on error.
+ */
+int qmi_kernel_decode(struct msg_desc *desc, void *out_c_struct,
+		      void *in_buf, uint32_t in_buf_len);
+
+#else
+static inline int qmi_kernel_encode(struct msg_desc *desc,
+				    void *out_buf, uint32_t out_buf_len,
+				    void *in_c_struct)
+{
+	return -EOPNOTSUPP;
+}
+
+static inline int qmi_kernel_decode(struct msg_desc *desc,
+				    void *out_c_struct,
+				    void *in_buf, uint32_t in_buf_len)
+{
+	return -EOPNOTSUPP;
+}
+#endif
+
+#endif
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..fc34b22 100644
--- a/include/linux/qpnp/qpnp-adc.h
+++ b/include/linux/qpnp/qpnp-adc.h
@@ -28,7 +28,7 @@
 	DCIN,
 	VCHG_SNS,
 	SPARE1_03,
-	SPARE2_03,
+	USB_ID_MV,
 	VCOIN,
 	VBAT_SNS,
 	VSYS,
@@ -81,44 +81,44 @@
 	LR_MUX7_HW_ID,
 	LR_MUX8_AMUX_THM4,
 	LR_MUX9_AMUX_THM5,
-	LR_MUX10_USB_ID,
+	LR_MUX10_USB_ID_LV,
 	AMUX_PU1,
 	AMUX_PU2,
 	LR_MUX3_BUF_XO_THERM_BUF,
-	LR_MUX1_PU1_BAT_THERM,
-	LR_MUX2_PU1_BAT_ID,
-	LR_MUX3_PU1_XO_THERM,
-	LR_MUX4_PU1_AMUX_THM1,
-	LR_MUX5_PU1_AMUX_THM2,
-	LR_MUX6_PU1_AMUX_THM3,
-	LR_MUX7_PU1_AMUX_HW_ID,
-	LR_MUX8_PU1_AMUX_THM4,
-	LR_MUX9_PU1_AMUX_THM5,
-	LR_MUX10_PU1_AMUX_USB_ID,
-	LR_MUX3_BUF_PU1_XO_THERM_BUF,
-	LR_MUX1_PU2_BAT_THERM,
-	LR_MUX2_PU2_BAT_ID,
-	LR_MUX3_PU2_XO_THERM,
-	LR_MUX4_PU2_AMUX_THM1,
-	LR_MUX5_PU2_AMUX_THM2,
-	LR_MUX6_PU2_AMUX_THM3,
-	LR_MUX7_PU2_AMUX_HW_ID,
-	LR_MUX8_PU2_AMUX_THM4,
-	LR_MUX9_PU2_AMUX_THM5,
-	LR_MUX10_PU2_AMUX_USB_ID,
-	LR_MUX3_BUF_PU2_XO_THERM_BUF,
-	LR_MUX1_PU1_PU2_BAT_THERM,
-	LR_MUX2_PU1_PU2_BAT_ID,
-	LR_MUX3_PU1_PU2_XO_THERM,
-	LR_MUX4_PU1_PU2_AMUX_THM1,
-	LR_MUX5_PU1_PU2_AMUX_THM2,
-	LR_MUX6_PU1_PU2_AMUX_THM3,
-	LR_MUX7_PU1_PU2_AMUX_HW_ID,
-	LR_MUX8_PU1_PU2_AMUX_THM4,
-	LR_MUX9_PU1_PU2_AMUX_THM5,
-	LR_MUX10_PU1_PU2_AMUX_USB_ID,
-	LR_MUX3_BUF_PU1_PU2_XO_THERM_BUF,
-	ALL_OFF,
+	LR_MUX1_PU1_BAT_THERM = 112,
+	LR_MUX2_PU1_BAT_ID = 113,
+	LR_MUX3_PU1_XO_THERM = 114,
+	LR_MUX4_PU1_AMUX_THM1 = 115,
+	LR_MUX5_PU1_AMUX_THM2 = 116,
+	LR_MUX6_PU1_AMUX_THM3 = 117,
+	LR_MUX7_PU1_AMUX_HW_ID = 118,
+	LR_MUX8_PU1_AMUX_THM4 = 119,
+	LR_MUX9_PU1_AMUX_THM5 = 120,
+	LR_MUX10_PU1_AMUX_USB_ID_LV = 121,
+	LR_MUX3_BUF_PU1_XO_THERM_BUF = 124,
+	LR_MUX1_PU2_BAT_THERM = 176,
+	LR_MUX2_PU2_BAT_ID = 177,
+	LR_MUX3_PU2_XO_THERM = 178,
+	LR_MUX4_PU2_AMUX_THM1 = 179,
+	LR_MUX5_PU2_AMUX_THM2 = 180,
+	LR_MUX6_PU2_AMUX_THM3 = 181,
+	LR_MUX7_PU2_AMUX_HW_ID = 182,
+	LR_MUX8_PU2_AMUX_THM4 = 183,
+	LR_MUX9_PU2_AMUX_THM5 = 184,
+	LR_MUX10_PU2_AMUX_USB_ID_LV = 185,
+	LR_MUX3_BUF_PU2_XO_THERM_BUF = 188,
+	LR_MUX1_PU1_PU2_BAT_THERM = 240,
+	LR_MUX2_PU1_PU2_BAT_ID = 241,
+	LR_MUX3_PU1_PU2_XO_THERM = 242,
+	LR_MUX4_PU1_PU2_AMUX_THM1 = 243,
+	LR_MUX5_PU1_PU2_AMUX_THM2 = 244,
+	LR_MUX6_PU1_PU2_AMUX_THM3 = 245,
+	LR_MUX7_PU1_PU2_AMUX_HW_ID = 246,
+	LR_MUX8_PU1_PU2_AMUX_THM4 = 247,
+	LR_MUX9_PU1_PU2_AMUX_THM5 = 248,
+	LR_MUX10_PU1_PU2_AMUX_USB_ID_LV = 249,
+	LR_MUX3_BUF_PU1_PU2_XO_THERM_BUF = 252,
+	ALL_OFF = 255,
 	ADC_MAX_NUM,
 };
 
@@ -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,
@@ -137,7 +137,7 @@
 };
 
 #define QPNP_ADC_625_UV	625000
-#define QPNP_ADC_HWMON_NAME_LENGTH				16
+#define QPNP_ADC_HWMON_NAME_LENGTH				64
 
 /**
  * enum qpnp_adc_decimation_type - Sampling rate supported.
@@ -402,8 +402,11 @@
 };
 
 /**
- * enum qpnp_adc_meas_timer - Selects the measurement interval time.
+ * enum qpnp_adc_meas_timer_1 - Selects the measurement interval time.
  *		If value = 0, use 0ms else use 2^(value + 4)/ 32768).
+ * The timer period is used by the USB_ID. Do not set a polling rate
+ * greater than 1 second on PMIC 2.0. The max polling rate on the PMIC 2.0
+ * appears to be limited to 1 second.
  * %ADC_MEAS_INTERVAL_0MS : 0ms
  * %ADC_MEAS_INTERVAL_1P0MS : 1ms
  * %ADC_MEAS_INTERVAL_2P0MS : 2ms
@@ -421,24 +424,126 @@
  * %ADC_MEAS_INTERVAL_8S : 8seconds
  * %ADC_MEAS_INTERVAL_16S: 16seconds
  */
-enum qpnp_adc_meas_timer {
-	ADC_MEAS_INTERVAL_0MS = 0,
-	ADC_MEAS_INTERVAL_1P0MS,
-	ADC_MEAS_INTERVAL_2P0MS,
-	ADC_MEAS_INTERVAL_3P9MS,
-	ADC_MEAS_INTERVAL_7P8MS,
-	ADC_MEAS_INTERVAL_15P6MS,
-	ADC_MEAS_INTERVAL_31P3MS,
-	ADC_MEAS_INTERVAL_62P5MS,
-	ADC_MEAS_INTERVAL_125MS,
-	ADC_MEAS_INTERVAL_250MS,
-	ADC_MEAS_INTERVAL_500MS,
-	ADC_MEAS_INTERVAL_1S,
-	ADC_MEAS_INTERVAL_2S,
-	ADC_MEAS_INTERVAL_4S,
-	ADC_MEAS_INTERVAL_8S,
-	ADC_MEAS_INTERVAL_16S,
-	ADC_MEAS_INTERVAL_NONE,
+enum qpnp_adc_meas_timer_1 {
+	ADC_MEAS1_INTERVAL_0MS = 0,
+	ADC_MEAS1_INTERVAL_1P0MS,
+	ADC_MEAS1_INTERVAL_2P0MS,
+	ADC_MEAS1_INTERVAL_3P9MS,
+	ADC_MEAS1_INTERVAL_7P8MS,
+	ADC_MEAS1_INTERVAL_15P6MS,
+	ADC_MEAS1_INTERVAL_31P3MS,
+	ADC_MEAS1_INTERVAL_62P5MS,
+	ADC_MEAS1_INTERVAL_125MS,
+	ADC_MEAS1_INTERVAL_250MS,
+	ADC_MEAS1_INTERVAL_500MS,
+	ADC_MEAS1_INTERVAL_1S,
+	ADC_MEAS1_INTERVAL_2S,
+	ADC_MEAS1_INTERVAL_4S,
+	ADC_MEAS1_INTERVAL_8S,
+	ADC_MEAS1_INTERVAL_16S,
+	ADC_MEAS1_INTERVAL_NONE,
+};
+
+/**
+ * enum qpnp_adc_meas_timer_2 - Selects the measurement interval time.
+ *		If value = 0, use 0ms else use 2^(value + 4)/ 32768).
+ * The timer period is used by the batt_therm. Do not set a polling rate
+ * greater than 1 second on PMIC 2.0. The max polling rate on the PMIC 2.0
+ * appears to be limited to 1 second.
+ * %ADC_MEAS_INTERVAL_0MS : 0ms
+ * %ADC_MEAS_INTERVAL_100MS : 100ms
+ * %ADC_MEAS_INTERVAL_200MS : 200ms
+ * %ADC_MEAS_INTERVAL_300MS : 300ms
+ * %ADC_MEAS_INTERVAL_400MS : 400ms
+ * %ADC_MEAS_INTERVAL_500MS : 500ms
+ * %ADC_MEAS_INTERVAL_600MS : 600ms
+ * %ADC_MEAS_INTERVAL_700MS : 700ms
+ * %ADC_MEAS_INTERVAL_800MS : 800ms
+ * %ADC_MEAS_INTERVAL_900MS : 900ms
+ * %ADC_MEAS_INTERVAL_1S: 1seconds
+ * %ADC_MEAS_INTERVAL_1P1S: 1.1seconds
+ * %ADC_MEAS_INTERVAL_1P2S: 1.2seconds
+ * %ADC_MEAS_INTERVAL_1P3S: 1.3seconds
+ * %ADC_MEAS_INTERVAL_1P4S: 1.4seconds
+ * %ADC_MEAS_INTERVAL_1P5S: 1.5seconds
+ */
+enum qpnp_adc_meas_timer_2 {
+	ADC_MEAS2_INTERVAL_0MS = 0,
+	ADC_MEAS2_INTERVAL_100MS,
+	ADC_MEAS2_INTERVAL_200MS,
+	ADC_MEAS2_INTERVAL_300MS,
+	ADC_MEAS2_INTERVAL_400MS,
+	ADC_MEAS2_INTERVAL_500MS,
+	ADC_MEAS2_INTERVAL_600MS,
+	ADC_MEAS2_INTERVAL_700MS,
+	ADC_MEAS2_INTERVAL_800MS,
+	ADC_MEAS2_INTERVAL_900MS,
+	ADC_MEAS2_INTERVAL_1S,
+	ADC_MEAS2_INTERVAL_1P1S,
+	ADC_MEAS2_INTERVAL_1P2S,
+	ADC_MEAS2_INTERVAL_1P3S,
+	ADC_MEAS2_INTERVAL_1P4S,
+	ADC_MEAS2_INTERVAL_1P5S,
+	ADC_MEAS2_INTERVAL_NONE,
+};
+
+/**
+ * enum qpnp_adc_meas_timer_3 - Selects the measurement interval time.
+ *		If value = 0, use 0ms else use 2^(value + 4)/ 32768).
+ * Do not set a polling rate greater than 1 second on PMIC 2.0.
+ * The max polling rate on the PMIC 2.0 appears to be limited to 1 second.
+ * %ADC_MEAS_INTERVAL_0MS : 0ms
+ * %ADC_MEAS_INTERVAL_1S : 1seconds
+ * %ADC_MEAS_INTERVAL_2S : 2seconds
+ * %ADC_MEAS_INTERVAL_3S : 3seconds
+ * %ADC_MEAS_INTERVAL_4S : 4seconds
+ * %ADC_MEAS_INTERVAL_5S : 5seconds
+ * %ADC_MEAS_INTERVAL_6S: 6seconds
+ * %ADC_MEAS_INTERVAL_7S : 7seconds
+ * %ADC_MEAS_INTERVAL_8S : 8seconds
+ * %ADC_MEAS_INTERVAL_9S : 9seconds
+ * %ADC_MEAS_INTERVAL_10S : 10seconds
+ * %ADC_MEAS_INTERVAL_11S : 11seconds
+ * %ADC_MEAS_INTERVAL_12S : 12seconds
+ * %ADC_MEAS_INTERVAL_13S : 13seconds
+ * %ADC_MEAS_INTERVAL_14S : 14seconds
+ * %ADC_MEAS_INTERVAL_15S : 15seconds
+ */
+enum qpnp_adc_meas_timer_3 {
+	ADC_MEAS3_INTERVAL_0S = 0,
+	ADC_MEAS3_INTERVAL_1S,
+	ADC_MEAS3_INTERVAL_2S,
+	ADC_MEAS3_INTERVAL_3S,
+	ADC_MEAS3_INTERVAL_4S,
+	ADC_MEAS3_INTERVAL_5S,
+	ADC_MEAS3_INTERVAL_6S,
+	ADC_MEAS3_INTERVAL_7S,
+	ADC_MEAS3_INTERVAL_8S,
+	ADC_MEAS3_INTERVAL_9S,
+	ADC_MEAS3_INTERVAL_10S,
+	ADC_MEAS3_INTERVAL_11S,
+	ADC_MEAS3_INTERVAL_12S,
+	ADC_MEAS3_INTERVAL_13S,
+	ADC_MEAS3_INTERVAL_14S,
+	ADC_MEAS3_INTERVAL_15S,
+	ADC_MEAS3_INTERVAL_NONE,
+};
+
+/**
+ * enum qpnp_adc_meas_timer_select - Selects the timer for which
+ *	the appropriate polling frequency is set.
+ * %ADC_MEAS_TIMER_SELECT1 - Select this timer if the client is USB_ID.
+ * %ADC_MEAS_TIMER_SELECT2 - Select this timer if the client is batt_therm.
+ * %ADC_MEAS_TIMER_SELECT3 - The timer is added only for completion. It is
+ *	not used by kernel space clients and user space clients cannot set
+ *	the polling frequency. The driver will set a appropriate polling
+ *	frequency to measure the user space clients from qpnp_adc_meas_timer_3.
+ */
+enum qpnp_adc_meas_timer_select {
+	ADC_MEAS_TIMER_SELECT1 = 0,
+	ADC_MEAS_TIMER_SELECT2,
+	ADC_MEAS_TIMER_SELECT3,
+	ADC_MEAS_TIMER_NUM,
 };
 
 /**
@@ -455,6 +560,134 @@
 };
 
 /**
+ * Channel selection registers for each of the 5 configurable measurements
+ * Channels allotment is fixed for the given channels below.
+ * The USB_ID and BATT_THERM channels are used only by the kernel space
+ * USB and Battery drivers.
+ * The other 3 channels are configurable for use by userspace clients.
+ * USB_ID uses QPNP_ADC_TM_M0_ADC_CH_SEL_CTL
+ * BATT_TEMP uses QPNP_ADC_TM_M1_ADC_CH_SEL_CTL
+ * PA_THERM1 uses QPNP_ADC_TM_M2_ADC_CH_SEL_CTL
+ * PA_THERM2 uses QPNP_ADC_TM_M3_ADC_CH_SEL_CTL
+ * EMMC_THERM uses QPNP_ADC_TM_M4_ADC_CH_SEL_CTL
+ */
+enum qpnp_adc_tm_channel_select	{
+	QPNP_ADC_TM_M0_ADC_CH_SEL_CTL = 0x48,
+	QPNP_ADC_TM_M1_ADC_CH_SEL_CTL = 0x68,
+	QPNP_ADC_TM_M2_ADC_CH_SEL_CTL = 0x70,
+	QPNP_ADC_TM_M3_ADC_CH_SEL_CTL = 0x78,
+	QPNP_ADC_TM_M4_ADC_CH_SEL_CTL = 0x80,
+	QPNP_ADC_TM_CH_SELECT_NONE
+};
+
+/**
+ * struct qpnp_adc_tm_config - Represent ADC Thermal Monitor configuration.
+ * @channel: ADC channel for which thermal monitoring is requested.
+ * @adc_code: The pre-calibrated digital output of a given ADC releative to the
+ *		ADC reference.
+ * @high_thr_temp: Temperature at which high threshold notification is required.
+ * @low_thr_temp: Temperature at which low threshold notification is required.
+ * @low_thr_voltage : Low threshold voltage ADC code used for reverse
+ *			calibration.
+ * @high_thr_voltage: High threshold voltage ADC code used for reverse
+ *			calibration.
+ */
+struct qpnp_adc_tm_config {
+	int	channel;
+	int	adc_code;
+	int	high_thr_temp;
+	int	low_thr_temp;
+	int64_t	high_thr_voltage;
+	int64_t	low_thr_voltage;
+};
+
+/**
+ * enum qpnp_adc_tm_trip_type - Type for setting high/low temperature/voltage.
+ * %ADC_TM_TRIP_HIGH_WARM: Setting high temperature. Note that high temperature
+ *			corresponds to low voltage. Driver handles this case
+ *			appropriately to set high/low thresholds for voltage.
+ *			threshold.
+ * %ADC_TM_TRIP_LOW_COOL: Setting low temperature.
+ */
+enum qpnp_adc_tm_trip_type {
+	ADC_TM_TRIP_HIGH_WARM = 0,
+	ADC_TM_TRIP_LOW_COOL,
+	ADC_TM_TRIP_NUM,
+};
+
+/**
+ * enum qpnp_tm_state - This lets the client know whether the threshold
+ *		that was crossed was high/low.
+ * %ADC_TM_HIGH_STATE: Client is notified of crossing the requested high
+ *			threshold.
+ * %ADC_TM_LOW_STATE: Client is notified of crossing the requested low
+ *			threshold.
+ */
+enum qpnp_tm_state {
+	ADC_TM_HIGH_STATE = 0,
+	ADC_TM_LOW_STATE,
+	ADC_TM_STATE_NUM,
+};
+
+/**
+ * enum qpnp_state_request - Request to enable/disable the corresponding
+ *			high/low voltage/temperature thresholds.
+ * %ADC_TM_HIGH_THR_ENABLE: Enable high voltage/temperature threshold.
+ * %ADC_TM_LOW_THR_ENABLE: Enable low voltage/temperature threshold.
+ * %ADC_TM_HIGH_LOW_THR_ENABLE: Enable high and low voltage/temperature
+ *				threshold.
+ * %ADC_TM_HIGH_THR_DISABLE: Disable high voltage/temperature threshold.
+ * %ADC_TM_LOW_THR_DISABLE: Disable low voltage/temperature threshold.
+ * %ADC_TM_HIGH_THR_DISABLE: Disable high and low voltage/temperature
+ *				threshold.
+ */
+enum qpnp_state_request {
+	ADC_TM_HIGH_THR_ENABLE = 0,
+	ADC_TM_LOW_THR_ENABLE,
+	ADC_TM_HIGH_LOW_THR_ENABLE,
+	ADC_TM_HIGH_THR_DISABLE,
+	ADC_TM_LOW_THR_DISABLE,
+	ADC_TM_HIGH_LOW_THR_DISABLE,
+	ADC_TM_THR_NUM,
+};
+
+/**
+ * struct qpnp_adc_tm_usbid_param - Represent USB_ID threshold
+ *				monitoring configuration.
+ * @high_thr: High voltage threshold for which notification is requested.
+ * @low_thr: Low voltage threshold for which notification is requested.
+ * @state_request: Enable/disable the corresponding high and low voltage
+ *		thresholds.
+ * @timer_interval: Select polling rate from qpnp_adc_meas_timer_1 type.
+ * @threshold_notification: Notification callback once threshold are crossed.
+ */
+struct qpnp_adc_tm_usbid_param {
+	int32_t					high_thr;
+	int32_t					low_thr;
+	enum qpnp_state_request			state_request;
+	enum qpnp_adc_meas_timer_1		timer_interval;
+	void	(*threshold_notification) (enum qpnp_tm_state state);
+};
+
+/**
+ * struct qpnp_adc_tm_btm_param - Represent Battery temperature threshold
+ *				monitoring configuration.
+ * @high_temp: High temperature threshold for which notification is requested.
+ * @low_temp: Low temperature threshold for which notification is requested.
+ * @state_request: Enable/disable the corresponding high and low temperature
+ *		thresholds.
+ * @timer_interval: Select polling rate from qpnp_adc_meas_timer_2 type.
+ * @threshold_notification: Notification callback once threshold are crossed.
+ */
+struct qpnp_adc_tm_btm_param {
+	int32_t					high_temp;
+	int32_t					low_temp;
+	enum qpnp_state_request			state_request;
+	enum qpnp_adc_meas_timer_2		timer_interval;
+	void	(*threshold_notification) (enum qpnp_tm_state state);
+};
+
+/**
  * struct qpnp_vadc_linear_graph - Represent ADC characteristics.
  * @dy: Numerator slope to calculate the gain.
  * @dx: Denominator slope to calculate the gain.
@@ -541,7 +774,7 @@
 };
 
 /**
- * struct qpnp_adc_amux - AMUX properties for individual channel
+ * struct qpnp_vadc_amux - AMUX properties for individual channel
  * @name: Channel string name.
  * @channel_num: Channel in integer used from qpnp_adc_channels.
  * @chan_path_prescaling: Channel scaling performed on the input signal.
@@ -590,13 +823,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;
 };
 
 /**
@@ -606,7 +857,7 @@
  * @adc_prop - ADC properties specific to the ADC peripheral.
  * @amux_prop - AMUX properties representing the ADC peripheral.
  * @adc_channels - ADC channel properties for the ADC peripheral.
- * @adc_irq - IRQ number that is mapped to the ADC peripheral.
+ * @adc_irq_eoc - End of Conversion IRQ.
  * @adc_lock - ADC lock for access to the peripheral.
  * @adc_rslt_completion - ADC result notification after interrupt
  *			  is received.
@@ -619,7 +870,7 @@
 	struct qpnp_adc_properties	*adc_prop;
 	struct qpnp_adc_amux_properties	*amux_prop;
 	struct qpnp_vadc_amux		*adc_channels;
-	int				adc_irq;
+	int				adc_irq_eoc;
 	struct mutex			adc_lock;
 	struct completion		adc_rslt_completion;
 	struct qpnp_iadc_calib		calib;
@@ -810,6 +1061,70 @@
  *		has not occured.
  */
 int32_t qpnp_vadc_is_ready(void);
+/**
+ * qpnp_adc_tm_scaler() - Performs reverse calibration.
+ * @config:	Thermal monitoring configuration.
+ * @adc_prop:	adc properties of the qpnp adc such as bit resolution and
+ *		reference voltage.
+ * @chan_prop:	Individual channel properties to compensate the i/p scaling,
+ *		slope and offset.
+ */
+static inline int32_t qpnp_adc_tm_scaler(struct qpnp_adc_tm_config *tm_config,
+			const struct qpnp_adc_properties *adc_prop,
+			const struct qpnp_vadc_chan_properties *chan_prop)
+{ return -ENXIO; }
+/**
+ * qpnp_get_vadc_gain_and_offset() - Obtains the VADC gain and offset
+ *		for absolute and ratiometric calibration.
+ * @param:	The result in which the ADC offset and gain values are stored.
+ * @type:	The calibration type whether client needs the absolute or
+ *		ratiometric gain and offset values.
+ */
+int32_t qpnp_get_vadc_gain_and_offset(struct qpnp_vadc_linear_graph *param,
+			enum qpnp_adc_calib_type calib_type);
+/**
+ * qpnp_adc_btm_scaler() - Performs reverse calibration on the low/high
+ *		temperature threshold values passed by the client.
+ *		The function maps the temperature to voltage and applies
+ *		ratiometric calibration on the voltage values.
+ * @param:	The input parameters that contain the low/high temperature
+ *		values.
+ * @low_threshold: The low threshold value that needs to be updated with
+ *		the above calibrated voltage value.
+ * @high_threshold: The low threshold value that needs to be updated with
+ *		the above calibrated voltage value.
+ */
+int32_t qpnp_adc_btm_scaler(struct qpnp_adc_tm_btm_param *param,
+		uint32_t *low_threshold, uint32_t *high_threshold);
+/**
+ * qpnp_adc_tm_scale_therm_voltage_pu2() - Performs reverse calibration
+ *		and convert given temperature to voltage on supported
+ *		thermistor channels using 100k pull-up.
+ * @param:	The input temperature values.
+ */
+int32_t qpnp_adc_tm_scale_therm_voltage_pu2(struct qpnp_adc_tm_config *param);
+/**
+ * qpnp_adc_tm_scale_therm_voltage_pu2() - Performs reverse calibration
+ *		and converts the given ADC code to temperature for
+ *		thermistor channels using 100k pull-up.
+ * @reg:	The input ADC code.
+ * @result:	The physical measurement temperature on the thermistor.
+ */
+int32_t qpnp_adc_tm_scale_voltage_therm_pu2(uint32_t reg, int64_t *result);
+/**
+ * qpnp_adc_usb_scaler() - Performs reverse calibration on the low/high
+ *		voltage threshold values passed by the client.
+ *		The function applies ratiometric calibration on the
+ *		voltage values.
+ * @param:	The input parameters that contain the low/high voltage
+ *		threshold values.
+ * @low_threshold: The low threshold value that needs to be updated with
+ *		the above calibrated voltage value.
+ * @high_threshold: The low threshold value that needs to be updated with
+ *		the above calibrated voltage value.
+ */
+int32_t qpnp_adc_usb_scaler(struct qpnp_adc_tm_usbid_param *param,
+		uint32_t *low_threshold, uint32_t *high_threshold);
 #else
 static inline int32_t qpnp_vadc_read(uint32_t channel,
 				struct qpnp_vadc_result *result)
@@ -854,7 +1169,30 @@
 			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; }
+static inline int32_t qpnp_adc_scale_default(int32_t adc_code,
+			const struct qpnp_adc_properties *adc_prop,
+			const struct qpnp_adc_chan_properties *chan_prop,
+			struct qpnp_adc_chan_result *chan_rslt)
+{ return -ENXIO; }
+static inline int32_t qpnp_get_vadc_gain_and_offset(
+			struct qpnp_vadc_linear_graph *param,
+			enum qpnp_adc_calib_type calib_type)
+{ return -ENXIO; }
+static inline int32_t qpnp_adc_usb_scaler(
+		struct qpnp_adc_tm_usbid_param *param,
+		uint32_t *low_threshold, uint32_t *high_threshold)
+{ return -ENXIO; }
+static inline int32_t qpnp_adc_btm_scaler(
+		struct qpnp_adc_tm_btm_param *param,
+		uint32_t *low_threshold, uint32_t *high_threshold)
+{ return -ENXIO; }
+static inline int32_t qpnp_adc_tm_scale_therm_voltage_pu2(
+				struct qpnp_adc_tm_config *param)
+{ return -ENXIO; }
+static inline int32_t qpnp_adc_tm_scale_voltage_therm_pu2(
+				uint32_t reg, int64_t *result)
 { return -ENXIO; }
 #endif
 
@@ -867,23 +1205,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 +1225,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/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/sched.h b/include/linux/sched.h
index 1f13da3..0a1428e 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -2041,6 +2041,7 @@
 extern unsigned int sysctl_sched_min_granularity;
 extern unsigned int sysctl_sched_wakeup_granularity;
 extern unsigned int sysctl_sched_child_runs_first;
+extern unsigned int sysctl_sched_wake_to_idle;
 
 enum sched_tunable_scaling {
 	SCHED_TUNABLESCALING_NONE,
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/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/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/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/msm_hsusb.h b/include/linux/usb/msm_hsusb.h
index a998ac2..5b7820d 100644
--- a/include/linux/usb/msm_hsusb.h
+++ b/include/linux/usb/msm_hsusb.h
@@ -199,6 +199,8 @@
  *              is connected.
  * @core_clk_always_on_workaround: Don't disable core_clk when
  *              USB enters LPM.
+ * @delay_lpm_on_disconnect: Use a delay before entering LPM
+ *              upon USB cable disconnection.
  * @bus_scale_table: parameters for bus bandwidth requirements
  * @mhl_dev_name: MHL device name used to register with MHL driver.
  */
@@ -218,6 +220,7 @@
 	bool pnoc_errata_fix;
 	bool enable_lpm_on_dev_suspend;
 	bool core_clk_always_on_workaround;
+	bool delay_lpm_on_disconnect;
 	struct msm_bus_scale_pdata *bus_scale_table;
 	const char *mhl_dev_name;
 };
@@ -330,7 +333,6 @@
 	bool sm_work_pending;
 	atomic_t pm_suspended;
 	atomic_t in_lpm;
-	atomic_t suspend_work_pending;
 	int async_int;
 	unsigned cur_power;
 	struct delayed_work chg_work;
@@ -394,7 +396,12 @@
 	unsigned data;
 	struct msm_bus_scale_pdata *bus_scale_table;
 	unsigned log2_irq_thresh;
+
+	/*swfi latency is required while driving resume on to the bus */
 	u32 swfi_latency;
+
+	/*standalone latency is required when HSCI is active*/
+	u32 standalone_latency;
 };
 
 struct msm_usb_host_platform_data {
diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h
index 07beb50..e5e0bb4 100644
--- a/include/linux/videodev2.h
+++ b/include/linux/videodev2.h
@@ -1804,6 +1804,42 @@
 	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
+};
+#define V4L2_CID_MPEG_VIDC_VIDEO_SECURE (V4L2_CID_MPEG_MSM_VIDC_BASE+24)
+#define V4L2_CID_MPEG_VIDC_VIDEO_EXTRADATA \
+		(V4L2_CID_MPEG_MSM_VIDC_BASE + 25)
+enum v4l2_mpeg_vidc_extradata {
+	V4L2_MPEG_VIDC_EXTRADATA_NONE,
+	V4L2_MPEG_VIDC_EXTRADATA_MB_QUANTIZATION,
+	V4L2_MPEG_VIDC_EXTRADATA_INTERLACE_VIDEO,
+	V4L2_MPEG_VIDC_EXTRADATA_VC1_FRAMEDISP,
+	V4L2_MPEG_VIDC_EXTRADATA_VC1_SEQDISP,
+	V4L2_MPEG_VIDC_EXTRADATA_TIMESTAMP,
+	V4L2_MPEG_VIDC_EXTRADATA_S3D_FRAME_PACKING,
+	V4L2_MPEG_VIDC_EXTRADATA_FRAME_RATE,
+	V4L2_MPEG_VIDC_EXTRADATA_PANSCAN_WINDOW,
+	V4L2_MPEG_VIDC_EXTRADATA_RECOVERY_POINT_SEI,
+	V4L2_MPEG_VIDC_EXTRADATA_CLOSED_CAPTION_UD,
+	V4L2_MPEG_VIDC_EXTRADATA_AFD_UD,
+	V4L2_MPEG_VIDC_EXTRADATA_MULTISLICE_INFO,
+	V4L2_MPEG_VIDC_EXTRADATA_NUM_CONCEALED_MB,
+	V4L2_MPEG_VIDC_EXTRADATA_METADATA_FILLER,
+	V4L2_MPEG_VIDC_INDEX_EXTRADATA_INPUT_CROP,
+	V4L2_MPEG_VIDC_INDEX_EXTRADATA_DIGITAL_ZOOM,
+	V4L2_MPEG_VIDC_INDEX_EXTRADATA_ASPECT_RATIO,
+};
+#define V4L2_CID_MPEG_VIDC_VIDEO_H264_VUI_TIMING_INFO \
+		(V4L2_CID_MPEG_MSM_VIDC_BASE + 23)
+enum v4l2_mpeg_vidc_video_h264_vui_timing_info {
+	V4L2_MPEG_VIDC_VIDEO_H264_VUI_TIMING_INFO_DISABLED = 0,
+	V4L2_MPEG_VIDC_VIDEO_H264_VUI_TIMING_INFO_ENABLED = 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 +2402,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/linux/wcnss_wlan.h b/include/linux/wcnss_wlan.h
index 7a194ca..6d2eee4 100644
--- a/include/linux/wcnss_wlan.h
+++ b/include/linux/wcnss_wlan.h
@@ -31,6 +31,8 @@
 };
 
 #define WCNSS_WLAN_IRQ_INVALID -1
+#define HAVE_WCNSS_SUSPEND_RESUME_NOTIFY 1
+#define HAVE_WCNSS_RESET_INTR 1
 
 struct device *wcnss_wlan_get_device(void);
 struct resource *wcnss_wlan_get_memory_map(struct device *dev);
@@ -59,6 +61,8 @@
 void *wcnss_prealloc_get(unsigned int size);
 int wcnss_prealloc_put(void *ptr);
 void wcnss_reset_intr(void);
+void wcnss_suspend_notify(void);
+void wcnss_resume_notify(void);
 
 #define wcnss_wlan_get_drvdata(dev) dev_get_drvdata(dev)
 #define wcnss_wlan_set_drvdata(dev, data) dev_set_drvdata((dev), (data))
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/msm/vcd_property.h b/include/media/msm/vcd_property.h
index 180b38d..545dcd2 100644
--- a/include/media/msm/vcd_property.h
+++ b/include/media/msm/vcd_property.h
@@ -56,6 +56,8 @@
 #define VCD_I_VOP_TIMING_CONSTANT_DELTA (VCD_START_BASE + 0x28)
 #define VCD_I_SET_TURBO_CLK (VCD_START_BASE + 0x29)
 #define VCD_I_ENABLE_DELIMITER_FLAG (VCD_START_BASE + 0x2A)
+#define VCD_I_ENABLE_VUI_TIMING_INFO (VCD_START_BASE + 0x2B)
+
 
 #define VCD_START_REQ      (VCD_START_BASE + 0x1000)
 #define VCD_I_REQ_IFRAME   (VCD_START_REQ + 0x1)
@@ -378,4 +380,8 @@
 	u32 avc_delimiter_enable_flag;
 };
 
+struct vcd_property_vui_timing_info_enable {
+	u32 vui_timing_info;
+};
+
 #endif
diff --git a/include/media/msm_camera.h b/include/media/msm_camera.h
index 6658b8c..a932011 100644
--- a/include/media/msm_camera.h
+++ b/include/media/msm_camera.h
@@ -1,4 +1,4 @@
-/* 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 and
@@ -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;
@@ -1975,6 +1981,7 @@
 	IRQ_ROUTER_DEV,
 	CPP_DEV,
 	CCI_DEV,
+	FLASH_DEV,
 };
 
 struct msm_mctl_set_sdev_data {
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..13ce043
--- /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 = 128;
+		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 = 128;
+		stride = ALIGN(width, alignment);
+		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 = 16;
+		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 = 0;
+		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..369cf45 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,
@@ -37,8 +38,8 @@
 };
 
 enum hal_vcap_polar {
-	HAL_VCAP_POLAR_NEG = 0,
-	HAL_VCAP_POLAR_POS,
+	HAL_VCAP_POLAR_POS = 0,
+	HAL_VCAP_POLAR_NEG,
 };
 
 enum hal_vcap_color {
@@ -119,8 +120,14 @@
 	VP_OUT_TYPE,
 };
 
+enum vcap_stride {
+	VC_STRIDE_16,
+	VC_STRIDE_32,
+};
+
 struct vcap_priv_fmt {
 	enum vcap_type type;
+	enum vcap_stride stride;
 	union {
 		struct v4l2_format_vc_ext timing;
 		struct v4l2_pix_format pix;
diff --git a/include/media/vcap_v4l2.h b/include/media/vcap_v4l2.h
index 6d684ef..39aa1b9 100644
--- a/include/media/vcap_v4l2.h
+++ b/include/media/vcap_v4l2.h
@@ -39,10 +39,10 @@
 
 #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_STRIDE_ALIGN_16 0x10
+#define VCAP_STRIDE_ALIGN_32 0x20
+#define VCAP_STRIDE_CALC(x, align) (((x / align) + \
+			(!(!(x % align)))) * align)
 
 #define VCAP_BASE (dev->vcapbase)
 #define VCAP_OFFSET(off) (VCAP_BASE + off)
@@ -109,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;
@@ -240,6 +241,7 @@
 	enum vcap_op_mode		op_mode;
 
 	struct v4l2_format_vc_ext vc_format;
+	enum vcap_stride		stride;
 
 	enum v4l2_buf_type		vp_buf_type_field;
 	struct vp_format_data	vp_in_fmt;
@@ -253,6 +255,8 @@
 	uint32_t				hold_vc;
 	uint32_t				hold_vp;
 
+	/* Mutex ensures only one thread is dq buffer or turning streamoff */
+	struct mutex			mutex;
 	spinlock_t				cap_slock;
 	bool					streaming;
 
diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h
index 14ccf3e..3bf514f 100644
--- a/include/net/bluetooth/mgmt.h
+++ b/include/net/bluetooth/mgmt.h
@@ -247,6 +247,11 @@
 	bdaddr_t	bdaddr;
 } __packed;
 
+#define MGMT_OP_CANCEL_RESOLVE_NAME	0x0024
+struct mgmt_cp_cancel_resolve_name {
+	bdaddr_t bdaddr;
+} __packed;
+
 #define MGMT_OP_LE_READ_WHITE_LIST_SIZE	0xE000
 
 #define MGMT_OP_LE_CLEAR_WHITE_LIST	0xE001
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index a57c9f9..5c1daf3 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -1318,6 +1318,21 @@
 };
 
 /**
+ * struct cfg80211_update_ft_ies_params - FT IE Information
+ *
+ * This structure provides information needed to update the fast transition IE
+ *
+ * @md: The Mobility Domain ID, 2 Octet value
+ * @ie: Fast Transition IEs
+ * @ie_len: Length of ft_ie in octets
+ */
+struct cfg80211_update_ft_ies_params {
+	u16 md;
+	u8 *ie;
+	size_t ie_len;
+};
+
+/**
  * struct cfg80211_ops - backend description for wireless configuration
  *
  * This struct is registered by fullmac card drivers and/or wireless stacks
@@ -1697,6 +1712,8 @@
 				  u16 noack_map);
 
 	struct ieee80211_channel *(*get_channel)(struct wiphy *wiphy);
+	int	(*update_ft_ies)(struct wiphy *wiphy, struct net_device *dev,
+				 struct cfg80211_update_ft_ies_params *ftie);
 };
 
 /*
@@ -3348,6 +3365,32 @@
  */
 u16 cfg80211_calculate_bitrate(struct rate_info *rate);
 
+/**
+ * struct cfg80211_ft_event - FT Information Elements
+ * @dev: network device
+ * @ies: FT IEs
+ * @ies_len: length of the FT IE in bytes
+ * @target_ap: target AP's MAC address
+ * @ric_ies: RIC IE
+ * @ric_ies_len: length of the RIC IE in bytes
+ */
+struct cfg80211_ft_event_params {
+	u8 *ies;
+	size_t ies_len;
+	u8 target_ap[ETH_ALEN];
+	u8 *ric_ies;
+	size_t ric_ies_len;
+};
+
+/**
+ * cfg80211_ft_event - notify userspace about FT IE and RIC IE
+ * @dev: network device
+ * @cfg80211_ft_event_params: IE information
+ */
+int cfg80211_ft_event(struct net_device *dev,
+			struct cfg80211_ft_event_params ft_event);
+
+
 /* Logging, debugging and troubleshooting/diagnostic helpers. */
 
 /* wiphy_printk helpers, similar to dev_printk */
diff --git a/include/sound/apr_audio-v2.h b/include/sound/apr_audio-v2.h
index 07179e9..3dd0ccd 100644
--- a/include/sound/apr_audio-v2.h
+++ b/include/sound/apr_audio-v2.h
@@ -712,8 +712,8 @@
 #define AFE_PORT_ID_PRIMARY_MI2S_TX         0x1001
 #define AFE_PORT_ID_SECONDARY_MI2S_RX       0x1002
 #define AFE_PORT_ID_SECONDARY_MI2S_TX       0x1003
-#define AFE_PORT_IDERTIARY_MI2S_RX        0x1004
-#define AFE_PORT_IDERTIARY_MI2S_TX        0x1005
+#define AFE_PORT_ID_TERTIARY_MI2S_RX        0x1004
+#define AFE_PORT_ID_TERTIARY_MI2S_TX        0x1005
 #define AFE_PORT_ID_QUATERNARY_MI2S_RX      0x1006
 #define AFE_PORT_ID_QUATERNARY_MI2S_TX      0x1007
 #define AUDIO_PORT_ID_I2S_RX				0x1008
@@ -6123,6 +6123,11 @@
 /*	Band cut equalizer effect.*/
 #define ASM_PARAM_EQ_BAND_CUT       6
 
+/* Voice get & set params */
+#define VOICE_CMD_SET_PARAM				0x0001133D
+#define VOICE_CMD_GET_PARAM				0x0001133E
+#define VOICE_EVT_GET_PARAM_ACK				0x00011008
+
 
 /* ERROR CODES */
 /* Success. The operation completed with no errors. */
diff --git a/include/sound/apr_audio.h b/include/sound/apr_audio.h
index 8c35ada..bfd7208 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
@@ -157,6 +157,55 @@
 	u16 reserved;
 } __attribute__ ((packed));
 
+/*
+ * Opcode for AFE to start DTMF.
+ */
+#define AFE_PORTS_CMD_DTMF_CTL	0x00010102
+
+/** DTMF payload.*/
+struct afe_dtmf_generation_command {
+	struct apr_hdr hdr;
+
+	/*
+	 * Duration of the DTMF tone in ms.
+	 * -1      -> continuous,
+	 *  0      -> disable
+	 */
+	int64_t                   duration_in_ms;
+
+	/*
+	 * The DTMF high tone frequency.
+	 */
+	uint16_t                  high_freq;
+
+	/*
+	 * The DTMF low tone frequency.
+	 */
+	uint16_t                  low_freq;
+
+	/*
+	 * The DTMF volume setting
+	 */
+	uint16_t                  gain;
+
+	/*
+	 * The number of ports to enable/disable on.
+	 */
+	uint16_t                  num_ports;
+
+	/*
+	 * The Destination ports - array  .
+	 * For DTMF on multiple ports, portIds needs to
+	 * be populated numPorts times.
+	 */
+	uint16_t                  port_ids;
+
+	/*
+	 * variable for 32 bit alignment of APR packet.
+	 */
+	uint16_t                  reserved;
+} __packed;
+
 #define AFE_PCM_CFG_MODE_PCM			0x0
 #define AFE_PCM_CFG_MODE_AUX			0x1
 #define AFE_PCM_CFG_SYNC_EXT			0x0
@@ -299,6 +348,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 +369,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 +632,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 +985,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 +1209,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;
 };
 
@@ -1209,6 +1312,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
 
@@ -1238,7 +1483,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 02d69ea..b95fa3c 100644
--- a/include/sound/compress_params.h
+++ b/include/sound/compress_params.h
@@ -88,6 +88,7 @@
 #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/cs8427.h b/include/sound/cs8427.h
index 2004ec3..a1e988d 100644
--- a/include/sound/cs8427.h
+++ b/include/sound/cs8427.h
@@ -209,7 +209,8 @@
 	int irq_base;
 	int num_irqs;
 	int reset_gpio;
-	int (*enable) (int enable);
+	int (*enable) (int enable, int gpio);
+	int ls_gpio;
 };
 
 struct snd_pcm_substream;
diff --git a/include/sound/msm-dai-q6-v2.h b/include/sound/msm-dai-q6-v2.h
index 3d5ffdd..4ecd435 100644
--- a/include/sound/msm-dai-q6-v2.h
+++ b/include/sound/msm-dai-q6-v2.h
@@ -21,6 +21,11 @@
 #define MSM_MI2S_CAP_RX 0
 #define MSM_MI2S_CAP_TX 1
 
+#define MSM_PRIM_MI2S 0
+#define MSM_SEC_MI2S  1
+#define MSM_TERT_MI2S 2
+#define MSM_QUAT_MI2S  3
+
 struct msm_dai_auxpcm_pdata {
 	const char *clk;
 	u16 mode;
@@ -35,6 +40,11 @@
 	int pcm_clk_rate;
 };
 
+struct msm_mi2s_pdata {
+	u16 rx_sd_lines;
+	u16 tx_sd_lines;
+};
+
 struct msm_i2s_data {
 	u32 capability; /* RX or TX */
 	u16 sd_lines;
diff --git a/include/sound/pcm.h b/include/sound/pcm.h
index 485e1c5..028e683 100644
--- a/include/sound/pcm.h
+++ b/include/sound/pcm.h
@@ -446,6 +446,7 @@
 	struct snd_info_entry *proc_xrun_debug_entry;
 #endif
 #endif
+	struct snd_kcontrol *chmap_kctl; /* channel-mapping controls */
 };
 
 struct snd_pcm {
@@ -1080,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-v2.h b/include/sound/q6adm-v2.h
index cb2f3d7..9c43d09 100644
--- a/include/sound/q6adm-v2.h
+++ b/include/sound/q6adm-v2.h
@@ -16,13 +16,13 @@
 #define ADM_PATH_PLAYBACK 0x1
 #define ADM_PATH_LIVE_REC 0x2
 #define ADM_PATH_NONLIVE_REC 0x3
+#include <sound/q6afe-v2.h>
 #include <sound/q6audio-v2.h>
 
-#define Q6_AFE_MAX_PORTS 32
 
 /* multiple copp per stream. */
 struct route_payload {
-	unsigned int copp_ids[Q6_AFE_MAX_PORTS];
+	unsigned int copp_ids[AFE_MAX_PORTS];
 	unsigned short num_copps;
 	unsigned int session_id;
 };
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-v2.h b/include/sound/q6afe-v2.h
index e107130..444b432 100644
--- a/include/sound/q6afe-v2.h
+++ b/include/sound/q6afe-v2.h
@@ -16,6 +16,7 @@
 #define IN			0x000
 #define OUT			0x001
 #define MSM_AFE_MONO        0
+#define MSM_AFE_CH_STEREO   1
 #define MSM_AFE_MONO_RIGHT  1
 #define MSM_AFE_MONO_LEFT   2
 #define MSM_AFE_STEREO      3
@@ -70,6 +71,8 @@
 	IDX_INT_FM_TX = 29,
 	IDX_RT_PROXY_PORT_001_RX = 30,
 	IDX_RT_PROXY_PORT_001_TX = 31,
+	IDX_AFE_PORT_ID_QUATERNARY_MI2S_RX = 32,
+	IDX_AFE_PORT_ID_QUATERNARY_MI2S_TX = 33,
 	AFE_MAX_PORTS
 };
 
diff --git a/include/sound/q6afe.h b/include/sound/q6afe.h
index a7264e8..1e12d48 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
 };
 
@@ -87,7 +88,10 @@
 int afe_cmd_memory_map_nowait(u32 dma_addr_p, u32 dma_buf_sz);
 int afe_cmd_memory_unmap(u32 dma_addr_p);
 int afe_cmd_memory_unmap_nowait(u32 dma_addr_p);
-
+void afe_set_dtmf_gen_rx_portid(u16 rx_port_id, int set);
+int afe_dtmf_generate_rx(int64_t duration_in_ms,
+			 uint16_t high_freq,
+			 uint16_t low_freq, uint16_t gain);
 int afe_register_get_events(u16 port_id,
 		void (*cb) (uint32_t opcode,
 		uint32_t token, uint32_t *payload, void *priv),
diff --git a/include/sound/q6asm.h b/include/sound/q6asm.h
index b0d74ba..6b4c17b 100644
--- a/include/sound/q6asm.h
+++ b/include/sound/q6asm.h
@@ -55,6 +55,7 @@
 #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
@@ -194,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);
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/kernel/sched/fair.c b/kernel/sched/fair.c
index 9885a9e..b175073 100644
--- a/kernel/sched/fair.c
+++ b/kernel/sched/fair.c
@@ -77,6 +77,14 @@
 unsigned int sysctl_sched_child_runs_first __read_mostly;
 
 /*
+ * Controls whether, when SD_SHARE_PKG_RESOURCES is on, if all
+ * tasks go to idle CPUs when woken. If this is off, note that the
+ * per-task flag PF_WAKE_ON_IDLE can still cause a task to go to an
+ * idle CPU upon being woken.
+ */
+unsigned int __read_mostly sysctl_sched_wake_to_idle;
+
+/*
  * SCHED_OTHER wake-up granularity.
  * (default: 1 msec * (1 + ilog(ncpus)), units: nanoseconds)
  *
@@ -2654,7 +2662,8 @@
 	if (target == prev_cpu && idle_cpu(prev_cpu))
 		return prev_cpu;
 
-	if (!(current->flags & PF_WAKE_UP_IDLE) &&
+	if (!sysctl_sched_wake_to_idle &&
+	    !(current->flags & PF_WAKE_UP_IDLE) &&
 	    !(p->flags & PF_WAKE_UP_IDLE))
 		return target;
 
diff --git a/kernel/sysctl.c b/kernel/sysctl.c
index b693142..b390dad 100644
--- a/kernel/sysctl.c
+++ b/kernel/sysctl.c
@@ -264,6 +264,13 @@
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec,
 	},
+	{
+		.procname	= "sched_wake_to_idle",
+		.data		= &sysctl_sched_wake_to_idle,
+		.maxlen		= sizeof(unsigned int),
+		.mode		= 0644,
+		.proc_handler	= proc_dointvec,
+	},
 #ifdef CONFIG_SCHED_DEBUG
 	{
 		.procname	= "sched_min_granularity_ns",
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c
index b9d1a73..700d2ae 100644
--- a/kernel/trace/trace.c
+++ b/kernel/trace/trace.c
@@ -1630,7 +1630,7 @@
 	memcpy(&entry->buf, trace_buf, len);
 	entry->buf[len] = '\0';
 	if (!filter_check_discard(call, entry, buffer, event)) {
-		stm_log(OST_ENTITY_TRACE_PRINTK, event, size);
+		stm_log(OST_ENTITY_TRACE_PRINTK, entry->buf, len + 1);
 		ring_buffer_unlock_commit(buffer, event);
 		ftrace_trace_stack(buffer, irq_flags, 6, pc);
 	}
@@ -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/lib/Kconfig b/lib/Kconfig
index 4a8aba2..8437e36 100644
--- a/lib/Kconfig
+++ b/lib/Kconfig
@@ -378,4 +378,20 @@
 	  Digital signature verification. Currently only RSA is supported.
 	  Implementation is done using GnuPG MPI library
 
+config QMI_ENCDEC
+	bool
+	help
+	  Library to encode & decode QMI messages from within
+	  the kernel. The kernel drivers encode the C structure into
+	  QMI message wire format and then send it over a transport.
+	  The kernel drivers receive the QMI message over a transport
+	  and then decode it into a C structure.
+
+config QMI_ENCDEC_DEBUG
+	bool
+	help
+	  Kernel config option to enable debugging QMI Encode/Decode
+	  library. This will log the information regarding the element
+	  and message being encoded & decoded.
+
 endmenu
diff --git a/lib/Makefile b/lib/Makefile
index acd6869..0fbcb04 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -123,6 +123,8 @@
 
 obj-$(CONFIG_CLZ_TAB) += clz_tab.o
 
+obj-$(CONFIG_QMI_ENCDEC) += qmi_encdec.o
+
 hostprogs-y	:= gen_crc32table
 clean-files	:= crc32table.h
 
diff --git a/lib/qmi_encdec.c b/lib/qmi_encdec.c
new file mode 100644
index 0000000..40273d0
--- /dev/null
+++ b/lib/qmi_encdec.c
@@ -0,0 +1,555 @@
+/* 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/slab.h>
+#include <linux/uaccess.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/io.h>
+#include <linux/string.h>
+#include <linux/qmi_encdec.h>
+
+#include "qmi_encdec_priv.h"
+
+#define TLV_LEN_SIZE sizeof(uint16_t)
+#define TLV_TYPE_SIZE sizeof(uint8_t)
+
+#ifdef CONFIG_QMI_ENCDEC_DEBUG
+
+#define qmi_encdec_dump(prefix_str, buf, buf_len) do { \
+	const u8 *ptr = buf; \
+	int i, linelen, remaining = buf_len; \
+	int rowsize = 16, groupsize = 1; \
+	unsigned char linebuf[256]; \
+	for (i = 0; i < buf_len; i += rowsize) { \
+		linelen = min(remaining, rowsize); \
+		remaining -= linelen; \
+		hex_dump_to_buffer(ptr + i, linelen, rowsize, groupsize, \
+				   linebuf, sizeof(linebuf), false); \
+		pr_debug("%s: %s\n", prefix_str, linebuf); \
+	} \
+} while (0)
+
+#define QMI_ENCODE_LOG_MSG(buf, buf_len) do { \
+	qmi_encdec_dump("QMI_ENCODE_MSG", buf, buf_len); \
+} while (0)
+
+#define QMI_DECODE_LOG_MSG(buf, buf_len) do { \
+	qmi_encdec_dump("QMI_DECODE_MSG", buf, buf_len); \
+} while (0)
+
+#define QMI_ENCODE_LOG_ELEM(level, elem_len, elem_size, buf) do { \
+	pr_debug("QMI_ENCODE_ELEM lvl: %d, len: %d, size: %d\n", \
+		 level, elem_len, elem_size); \
+	qmi_encdec_dump("QMI_ENCODE_ELEM", buf, (elem_len * elem_size)); \
+} while (0)
+
+#define QMI_DECODE_LOG_ELEM(level, elem_len, elem_size, buf) do { \
+	pr_debug("QMI_DECODE_ELEM lvl: %d, len: %d, size: %d\n", \
+		 level, elem_len, elem_size); \
+	qmi_encdec_dump("QMI_DECODE_ELEM", buf, (elem_len * elem_size)); \
+} while (0)
+
+#define QMI_ENCODE_LOG_TLV(tlv_type, tlv_len) do { \
+	pr_debug("QMI_ENCODE_TLV type: %d, len: %d\n", tlv_type, tlv_len); \
+} while (0)
+
+#define QMI_DECODE_LOG_TLV(tlv_type, tlv_len) do { \
+	pr_debug("QMI_DECODE_TLV type: %d, len: %d\n", tlv_type, tlv_len); \
+} while (0)
+
+#else
+
+#define QMI_ENCODE_LOG_MSG(buf, buf_len) { }
+#define QMI_DECODE_LOG_MSG(buf, buf_len) { }
+#define QMI_ENCODE_LOG_ELEM(level, elem_len, elem_size, buf) { }
+#define QMI_DECODE_LOG_ELEM(level, elem_len, elem_size, buf) { }
+#define QMI_ENCODE_LOG_TLV(tlv_type, tlv_len) { }
+#define QMI_DECODE_LOG_TLV(tlv_type, tlv_len) { }
+
+#endif
+
+static int _qmi_kernel_encode(struct elem_info *ei_array,
+			      void *out_buf, void *in_c_struct,
+			      int enc_level);
+
+static int _qmi_kernel_decode(struct elem_info *ei_array,
+			      void *out_c_struct,
+			      void *in_buf, uint32_t in_buf_len,
+			      int dec_level);
+
+/**
+ * qmi_kernel_encode() - Encode to QMI message wire format
+ * @desc: Pointer to structure descriptor.
+ * @out_buf: Buffer to hold the encoded QMI message.
+ * @out_buf_len: Length of the out buffer.
+ * @in_c_struct: C Structure to be encoded.
+ *
+ * @return: size of encoded message on success, < 0 for error.
+ */
+int qmi_kernel_encode(struct msg_desc *desc,
+		      void *out_buf, uint32_t out_buf_len,
+		      void *in_c_struct)
+{
+	int enc_level = 1;
+
+	if (!desc || !desc->ei_array)
+		return -EINVAL;
+
+	if (!out_buf || !in_c_struct)
+		return -EINVAL;
+
+	if (desc->max_msg_len < out_buf_len)
+		return -ETOOSMALL;
+
+	return _qmi_kernel_encode(desc->ei_array, out_buf,
+				  in_c_struct, enc_level);
+}
+EXPORT_SYMBOL(qmi_kernel_encode);
+
+/**
+ * qmi_encode_basic_elem() - Encodes elements of basic/primary data type
+ * @buf_dst: Buffer to store the encoded information.
+ * @buf_src: Buffer containing the elements to be encoded.
+ * @elem_len: Number of elements, in the buf_src, to be encoded.
+ * @elem_size: Size of a single instance of the element to be encoded.
+ *
+ * @return: number of bytes of encoded information.
+ *
+ * This function encodes the "elem_len" number of data elements, each of
+ * size "elem_size" bytes from the source buffer "buf_src" and stores the
+ * encoded information in the destination buffer "buf_dst". The elements are
+ * of primary data type which include uint8_t - uint64_t or similar. This
+ * function returns the number of bytes of encoded information.
+ */
+static int qmi_encode_basic_elem(void *buf_dst, void *buf_src,
+				 uint32_t elem_len, uint32_t elem_size)
+{
+	uint32_t i, rc = 0;
+
+	for (i = 0; i < elem_len; i++) {
+		QMI_ENCDEC_ENCODE_N_BYTES(buf_dst, buf_src, elem_size);
+		rc += elem_size;
+	}
+
+	return rc;
+}
+
+/**
+ * qmi_encode_struct_elem() - Encodes elements of struct data type
+ * @ei_array: Struct info array descibing the struct element.
+ * @buf_dst: Buffer to store the encoded information.
+ * @buf_src: Buffer containing the elements to be encoded.
+ * @elem_len: Number of elements, in the buf_src, to be encoded.
+ * @enc_level: Depth of the nested structure from the main structure.
+ *
+ * @return: Mumber of bytes of encoded information, on success.
+ *          < 0 on error.
+ *
+ * This function encodes the "elem_len" number of struct elements, each of
+ * size "ei_array->elem_size" bytes from the source buffer "buf_src" and
+ * stores the encoded information in the destination buffer "buf_dst". The
+ * elements are of struct data type which includes any C structure. This
+ * function returns the number of bytes of encoded information.
+ */
+static int qmi_encode_struct_elem(struct elem_info *ei_array,
+				  void *buf_dst, void *buf_src,
+				  uint32_t elem_len, int enc_level)
+{
+	int i, rc, encoded_bytes = 0;
+	struct elem_info *temp_ei = ei_array;
+
+	for (i = 0; i < elem_len; i++) {
+		rc = _qmi_kernel_encode(temp_ei->ei_array,
+					buf_dst, buf_src, enc_level);
+		if (rc < 0) {
+			pr_err("%s: STRUCT Encode failure\n", __func__);
+			return rc;
+		}
+		buf_dst = buf_dst + rc;
+		buf_src = buf_src + temp_ei->elem_size;
+		encoded_bytes += rc;
+	}
+
+	return encoded_bytes;
+}
+
+/**
+ * skip_to_next_elem() - Skip to next element in the structure to be encoded
+ * @ei_array: Struct info describing the element to be skipped.
+ *
+ * @return: Struct info of the next element that can be encoded.
+ *
+ * This function is used while encoding optional elements. If the flag
+ * corresponding to an optional element is not set, then encoding the
+ * optional element can be skipped. This function can be used to perform
+ * that operation.
+ */
+static struct elem_info *skip_to_next_elem(struct elem_info *ei_array)
+{
+	struct elem_info *temp_ei = ei_array;
+	uint8_t tlv_type;
+
+	do {
+		tlv_type = temp_ei->tlv_type;
+		temp_ei = temp_ei + 1;
+	} while (tlv_type == temp_ei->tlv_type);
+
+	return temp_ei;
+}
+
+/**
+ * _qmi_kernel_encode() - Core Encode Function
+ * @ei_array: Struct info array describing the structure to be encoded.
+ * @out_buf: Buffer to hold the encoded QMI message.
+ * @in_c_struct: Pointer to the C structure to be encoded.
+ * @enc_level: Encode level to indicate the depth of the nested structure,
+ *             within the main structure, being encoded.
+ *
+ * @return: Number of bytes of encoded information, on success.
+ *          < 0 on error.
+ */
+static int _qmi_kernel_encode(struct elem_info *ei_array,
+			      void *out_buf, void *in_c_struct,
+			      int enc_level)
+{
+	struct elem_info *temp_ei = ei_array;
+	uint8_t opt_flag_value = 0;
+	uint32_t data_len_value = 0, data_len_sz;
+	uint8_t *buf_dst = (uint8_t *)out_buf;
+	uint8_t *tlv_pointer;
+	uint32_t tlv_len;
+	uint8_t tlv_type;
+	uint32_t encoded_bytes = 0;
+	void *buf_src;
+	int encode_tlv = 0;
+	int rc;
+
+	tlv_pointer = buf_dst;
+	tlv_len = 0;
+	buf_dst = buf_dst + (TLV_LEN_SIZE + TLV_TYPE_SIZE);
+
+	while (temp_ei->data_type != QMI_EOTI) {
+		buf_src = in_c_struct + temp_ei->offset;
+		tlv_type = temp_ei->tlv_type;
+
+		if (temp_ei->is_array == NO_ARRAY) {
+			data_len_value = 1;
+		} else if (temp_ei->is_array == STATIC_ARRAY) {
+			data_len_value = temp_ei->elem_len;
+		} else if (data_len_value <= 0 ||
+			    temp_ei->elem_len < data_len_value) {
+			pr_err("%s: Invalid data length\n", __func__);
+			return -EINVAL;
+		}
+
+		switch (temp_ei->data_type) {
+		case QMI_OPT_FLAG:
+			rc = qmi_encode_basic_elem(&opt_flag_value, buf_src,
+						   1, sizeof(uint8_t));
+			if (opt_flag_value)
+				temp_ei = temp_ei + 1;
+			else
+				temp_ei = skip_to_next_elem(temp_ei);
+			break;
+
+		case QMI_DATA_LEN:
+			memcpy(&data_len_value, buf_src, temp_ei->elem_size);
+			data_len_sz = temp_ei->elem_size == sizeof(uint8_t) ?
+					sizeof(uint8_t) : sizeof(uint16_t);
+			rc = qmi_encode_basic_elem(buf_dst, &data_len_value,
+						   1, data_len_sz);
+			if (data_len_value) {
+				UPDATE_ENCODE_VARIABLES(temp_ei, buf_dst,
+					encoded_bytes, tlv_len, encode_tlv, rc);
+				encode_tlv = 0;
+			} else {
+				temp_ei = skip_to_next_elem(temp_ei);
+			}
+			break;
+
+		case QMI_UNSIGNED_1_BYTE:
+		case QMI_UNSIGNED_2_BYTE:
+		case QMI_UNSIGNED_4_BYTE:
+		case QMI_UNSIGNED_8_BYTE:
+		case QMI_SIGNED_2_BYTE_ENUM:
+		case QMI_SIGNED_4_BYTE_ENUM:
+			rc = qmi_encode_basic_elem(buf_dst, buf_src,
+				data_len_value, temp_ei->elem_size);
+			QMI_ENCODE_LOG_ELEM(enc_level, data_len_value,
+				temp_ei->elem_size, buf_src);
+			UPDATE_ENCODE_VARIABLES(temp_ei, buf_dst,
+				encoded_bytes, tlv_len, encode_tlv, rc);
+			break;
+
+		case QMI_STRUCT:
+			rc = qmi_encode_struct_elem(temp_ei, buf_dst, buf_src,
+				data_len_value, (enc_level + 1));
+			if (rc < 0)
+				return rc;
+			UPDATE_ENCODE_VARIABLES(temp_ei, buf_dst,
+				encoded_bytes, tlv_len, encode_tlv, rc);
+			break;
+
+		default:
+			pr_err("%s: Unrecognized data type\n", __func__);
+			return -EINVAL;
+
+		}
+
+		if (encode_tlv && enc_level == 1) {
+			QMI_ENCDEC_ENCODE_TLV(tlv_type, tlv_len, tlv_pointer);
+			QMI_ENCODE_LOG_TLV(tlv_type, tlv_len);
+			encoded_bytes += (TLV_TYPE_SIZE + TLV_LEN_SIZE);
+			tlv_pointer = buf_dst;
+			tlv_len = 0;
+			buf_dst = buf_dst + TLV_LEN_SIZE + TLV_TYPE_SIZE;
+			encode_tlv = 0;
+		}
+	}
+	QMI_ENCODE_LOG_MSG(out_buf, encoded_bytes);
+	return encoded_bytes;
+}
+
+/**
+ * qmi_kernel_decode() - Decode to C Structure format
+ * @desc: Pointer to structure descriptor.
+ * @out_c_struct: Buffer to hold the decoded C structure.
+ * @in_buf: Buffer containg the QMI message to be decoded.
+ * @in_buf_len: Length of the incoming QMI message.
+ *
+ * @return: 0 on success, < 0 on error.
+ */
+int qmi_kernel_decode(struct msg_desc *desc, void *out_c_struct,
+		      void *in_buf, uint32_t in_buf_len)
+{
+	int dec_level = 1;
+	int rc = 0;
+
+	if (!desc || !desc->ei_array)
+		return -EINVAL;
+
+	if (!out_c_struct || !in_buf || !in_buf_len)
+		return -EINVAL;
+
+	if (desc->max_msg_len < in_buf_len)
+		return -EINVAL;
+
+	rc = _qmi_kernel_decode(desc->ei_array, out_c_struct,
+				in_buf, in_buf_len, dec_level);
+	if (rc < 0)
+		return rc;
+	else
+		return 0;
+}
+EXPORT_SYMBOL(qmi_kernel_decode);
+
+/**
+ * qmi_decode_basic_elem() - Decodes elements of basic/primary data type
+ * @buf_dst: Buffer to store the decoded element.
+ * @buf_src: Buffer containing the elements in QMI wire format.
+ * @elem_len: Number of elements to be decoded.
+ * @elem_size: Size of a single instance of the element to be decoded.
+ *
+ * @return: Total size of the decoded data elements, in bytes.
+ *
+ * This function decodes the "elem_len" number of elements in QMI wire format,
+ * each of size "elem_size" bytes from the source buffer "buf_src" and stores
+ * the decoded elements in the destination buffer "buf_dst". The elements are
+ * of primary data type which include uint8_t - uint64_t or similar. This
+ * function returns the number of bytes of decoded information.
+ */
+static int qmi_decode_basic_elem(void *buf_dst, void *buf_src,
+				 uint32_t elem_len, uint32_t elem_size)
+{
+	uint32_t i, rc = 0;
+
+	for (i = 0; i < elem_len; i++) {
+		QMI_ENCDEC_DECODE_N_BYTES(buf_dst, buf_src, elem_size);
+		rc += elem_size;
+	}
+
+	return rc;
+}
+
+/**
+ * qmi_decode_struct_elem() - Decodes elements of struct data type
+ * @ei_array: Struct info array descibing the struct element.
+ * @buf_dst: Buffer to store the decoded element.
+ * @buf_src: Buffer containing the elements in QMI wire format.
+ * @elem_len: Number of elements to be decoded.
+ * @tlv_len: Total size of the encoded inforation corresponding to
+ *           this struct element.
+ * @dec_level: Depth of the nested structure from the main structure.
+ *
+ * @return: Total size of the decoded data elements, on success.
+ *          < 0 on error.
+ *
+ * This function decodes the "elem_len" number of elements in QMI wire format,
+ * each of size "(tlv_len/elem_len)" bytes from the source buffer "buf_src"
+ * and stores the decoded elements in the destination buffer "buf_dst". The
+ * elements are of struct data type which includes any C structure. This
+ * function returns the number of bytes of decoded information.
+ */
+static int qmi_decode_struct_elem(struct elem_info *ei_array, void *buf_dst,
+				  void *buf_src, uint32_t elem_len,
+				  uint32_t tlv_len, int dec_level)
+{
+	int i, rc, decoded_bytes = 0;
+	struct elem_info *temp_ei = ei_array;
+
+	for (i = 0; i < elem_len; i++) {
+		rc = _qmi_kernel_decode(temp_ei->ei_array, buf_dst, buf_src,
+					(tlv_len/elem_len), dec_level);
+		if (rc < 0)
+			return rc;
+		if (rc != (tlv_len/elem_len)) {
+			pr_err("%s: Fault in decoding\n", __func__);
+			return -EFAULT;
+		}
+		buf_src = buf_src + rc;
+		buf_dst = buf_dst + temp_ei->elem_size;
+		decoded_bytes += rc;
+	}
+
+	return decoded_bytes;
+}
+
+/**
+ * find_ei() - Find element info corresponding to TLV Type
+ * @ei_array: Struct info array of the message being decoded.
+ * @type: TLV Type of the element being searched.
+ *
+ * @return: Pointer to struct info, if found
+ *
+ * Every element that got encoded in the QMI message will have a type
+ * information associated with it. While decoding the QMI message,
+ * this function is used to find the struct info regarding the element
+ * that corresponds to the type being decoded.
+ */
+static struct elem_info *find_ei(struct elem_info *ei_array,
+				   uint32_t type)
+{
+	struct elem_info *temp_ei = ei_array;
+	while (temp_ei->data_type != QMI_EOTI) {
+		if (temp_ei->tlv_type == (uint8_t)type)
+			return temp_ei;
+		temp_ei = temp_ei + 1;
+	}
+	return NULL;
+}
+
+/**
+ * _qmi_kernel_decode() - Core Decode Function
+ * @ei_array: Struct info array describing the structure to be decoded.
+ * @out_c_struct: Buffer to hold the decoded C struct
+ * @in_buf: Buffer containing the QMI message to be decoded
+ * @in_buf_len: Length of the QMI message to be decoded
+ * @dec_level: Decode level to indicate the depth of the nested structure,
+ *             within the main structure, being decoded
+ *
+ * @return: Number of bytes of decoded information, on success
+ *          < 0 on error.
+ */
+static int _qmi_kernel_decode(struct elem_info *ei_array,
+			      void *out_c_struct,
+			      void *in_buf, uint32_t in_buf_len,
+			      int dec_level)
+{
+	struct elem_info *temp_ei = ei_array;
+	uint8_t opt_flag_value = 1;
+	uint32_t data_len_value = 0, data_len_sz = 0;
+	uint8_t *buf_dst = out_c_struct;
+	uint8_t *tlv_pointer;
+	uint32_t tlv_len = 0;
+	uint32_t tlv_type;
+	uint32_t decoded_bytes = 0;
+	void *buf_src = in_buf;
+	int rc;
+
+	QMI_DECODE_LOG_MSG(in_buf, in_buf_len);
+	while (decoded_bytes < in_buf_len) {
+		if (dec_level == 1) {
+			tlv_pointer = buf_src;
+			QMI_ENCDEC_DECODE_TLV(&tlv_type,
+					      &tlv_len, tlv_pointer);
+			QMI_DECODE_LOG_TLV(tlv_type, tlv_len);
+			buf_src += (TLV_TYPE_SIZE + TLV_LEN_SIZE);
+			decoded_bytes += (TLV_TYPE_SIZE + TLV_LEN_SIZE);
+			temp_ei = find_ei(ei_array, tlv_type);
+			if (!temp_ei) {
+				pr_err("%s: Inval element info\n", __func__);
+				return -EINVAL;
+			}
+		}
+
+		buf_dst = out_c_struct + temp_ei->offset;
+		if (temp_ei->data_type == QMI_OPT_FLAG) {
+			memcpy(buf_dst, &opt_flag_value, sizeof(uint8_t));
+			temp_ei = temp_ei + 1;
+			buf_dst = out_c_struct + temp_ei->offset;
+		}
+
+		if (temp_ei->data_type == QMI_DATA_LEN) {
+			data_len_sz = temp_ei->elem_size == sizeof(uint8_t) ?
+					sizeof(uint8_t) : sizeof(uint16_t);
+			rc = qmi_decode_basic_elem(&data_len_value, buf_src,
+						   1, data_len_sz);
+			memcpy(buf_dst, &data_len_value, sizeof(uint32_t));
+			temp_ei = temp_ei + 1;
+			buf_dst = out_c_struct + temp_ei->offset;
+			UPDATE_DECODE_VARIABLES(buf_src, decoded_bytes, rc);
+		}
+
+		if (temp_ei->is_array == NO_ARRAY) {
+			data_len_value = 1;
+		} else if (temp_ei->is_array == STATIC_ARRAY) {
+			data_len_value = temp_ei->elem_len;
+		} else if (data_len_value > temp_ei->elem_len) {
+			pr_err("%s: Data len %d > max spec %d\n",
+				__func__, data_len_value, temp_ei->elem_len);
+			return -ETOOSMALL;
+		}
+
+		switch (temp_ei->data_type) {
+		case QMI_UNSIGNED_1_BYTE:
+		case QMI_UNSIGNED_2_BYTE:
+		case QMI_UNSIGNED_4_BYTE:
+		case QMI_UNSIGNED_8_BYTE:
+		case QMI_SIGNED_2_BYTE_ENUM:
+		case QMI_SIGNED_4_BYTE_ENUM:
+			rc = qmi_decode_basic_elem(buf_dst, buf_src,
+				data_len_value, temp_ei->elem_size);
+			QMI_DECODE_LOG_ELEM(dec_level, data_len_value,
+				temp_ei->elem_size, buf_dst);
+			UPDATE_DECODE_VARIABLES(buf_src, decoded_bytes, rc);
+			break;
+
+		case QMI_STRUCT:
+			rc = qmi_decode_struct_elem(temp_ei, buf_dst, buf_src,
+				data_len_value, tlv_len, (dec_level + 1));
+			if (rc < 0)
+				return rc;
+			UPDATE_DECODE_VARIABLES(buf_src, decoded_bytes, rc);
+			break;
+		default:
+			pr_err("%s: Unrecognized data type\n", __func__);
+			return -EINVAL;
+		}
+		temp_ei = temp_ei + 1;
+	}
+	return decoded_bytes;
+}
+MODULE_DESCRIPTION("QMI kernel enc/dec");
+MODULE_LICENSE("GPL v2");
diff --git a/lib/qmi_encdec_priv.h b/lib/qmi_encdec_priv.h
new file mode 100644
index 0000000..97fe45b
--- /dev/null
+++ b/lib/qmi_encdec_priv.h
@@ -0,0 +1,66 @@
+/* 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 _QMI_ENCDEC_PRIV_H_
+#define _QMI_ENCDEC_PRIV_H_
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/mm.h>
+#include <linux/list.h>
+#include <linux/socket.h>
+#include <linux/gfp.h>
+#include <linux/qmi_encdec.h>
+
+#define QMI_ENCDEC_ENCODE_TLV(type, length, p_dst) do { \
+	*p_dst++ = type; \
+	*p_dst++ = ((uint8_t)((length) & 0xFF)); \
+	*p_dst++ = ((uint8_t)(((length) >> 8) & 0xFF)); \
+} while (0)
+
+#define QMI_ENCDEC_DECODE_TLV(p_type, p_length, p_src) do { \
+	*p_type = (uint8_t)*p_src++; \
+	*p_length = (uint8_t)*p_src++; \
+	*p_length |= ((uint8_t)*p_src) << 8; \
+} while (0)
+
+#define QMI_ENCDEC_ENCODE_N_BYTES(p_dst, p_src, size) \
+do { \
+	memcpy(p_dst, p_src, size); \
+	p_dst = (uint8_t *)p_dst + size; \
+	p_src = (uint8_t *)p_src + size; \
+} while (0)
+
+#define QMI_ENCDEC_DECODE_N_BYTES(p_dst, p_src, size) \
+do { \
+	memcpy(p_dst, p_src, size); \
+	p_dst = (uint8_t *)p_dst + size; \
+	p_src = (uint8_t *)p_src + size; \
+} while (0)
+
+#define UPDATE_ENCODE_VARIABLES(temp_si, buf_dst, \
+				encoded_bytes, tlv_len, encode_tlv, rc) \
+do { \
+	buf_dst = (uint8_t *)buf_dst + rc; \
+	encoded_bytes += rc; \
+	tlv_len += rc; \
+	temp_si = temp_si + 1; \
+	encode_tlv = 1; \
+} while (0)
+
+#define UPDATE_DECODE_VARIABLES(buf_src, decoded_bytes, rc) \
+do { \
+	buf_src = (uint8_t *)buf_src + rc; \
+	decoded_bytes += rc; \
+} while (0)
+
+#endif
diff --git a/mm/Kconfig b/mm/Kconfig
index 84489cd..bbab5a6 100644
--- a/mm/Kconfig
+++ b/mm/Kconfig
@@ -379,3 +379,25 @@
 	  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.
+
+config USE_USER_ACCESSIBLE_TIMERS
+	bool "Enables timers accessible from userspace"
+	depends on MMU
+	help
+	  User-accessible timers allow the kernel to map kernel timer
+	  registers to a userspace accessible page, to allow faster
+	  access to time information.  This flag will enable the
+	  interface code in the main kernel.  However, there are
+	  architecture-specific code that will need to be enabled
+	  separately.
+
diff --git a/mm/memory.c b/mm/memory.c
index 6105f47..174fcaa 100644
--- a/mm/memory.c
+++ b/mm/memory.c
@@ -1726,6 +1726,19 @@
 			goto next_page;
 		}
 
+		if (use_user_accessible_timers()) {
+			if (!vma && in_user_timers_area(mm, start)) {
+				int goto_next_page = 0;
+				int user_timer_ret = get_user_timer_page(vma,
+					mm, start, gup_flags, pages, i,
+					&goto_next_page);
+				if (goto_next_page)
+					goto next_page;
+				else
+					return user_timer_ret;
+			}
+		}
+
 		if (!vma ||
 		    (vma->vm_flags & (VM_IO | VM_PFNMAP)) ||
 		    !(vm_flags & vma->vm_flags))
diff --git a/mm/mlock.c b/mm/mlock.c
index ef726e8..38c77ab 100644
--- a/mm/mlock.c
+++ b/mm/mlock.c
@@ -229,7 +229,9 @@
 
 	if (!((vma->vm_flags & (VM_DONTEXPAND | VM_RESERVED)) ||
 			is_vm_hugetlb_page(vma) ||
-			vma == get_gate_vma(current->mm))) {
+			vma == get_gate_vma(current->mm) ||
+			((use_user_accessible_timers() &&
+				(vma == get_user_timers_vma(current->mm)))))) {
 
 		__mlock_vma_pages_range(vma, start, end, NULL);
 
@@ -324,7 +326,9 @@
 	int lock = !!(newflags & VM_LOCKED);
 
 	if (newflags == vma->vm_flags || (vma->vm_flags & VM_SPECIAL) ||
-	    is_vm_hugetlb_page(vma) || vma == get_gate_vma(current->mm))
+	    is_vm_hugetlb_page(vma) || vma == get_gate_vma(current->mm) ||
+	    ((use_user_accessible_timers()) &&
+		(vma == get_user_timers_vma(current->mm))))
 		goto out;	/* don't set VM_LOCKED,  don't count */
 
 	pgoff = vma->vm_pgoff + ((start - vma->vm_start) >> PAGE_SHIFT);
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/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 22a4dbe..fa2469e 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -1426,6 +1426,11 @@
 	} else {
 		u16 flags;
 
+		if (!(pi->conn)) {
+			kfree_skb(skb);
+			return;
+		}
+
 		bt_cb(skb)->force_active = pi->force_active;
 		BT_DBG("Sending on BR/EDR connection %p", pi->conn->hcon);
 
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index 28eb7ea..493801a 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -1956,6 +1956,38 @@
 	return err;
 }
 
+static int cancel_resolve_name(struct sock *sk, u16 index, unsigned char *data,
+								u16 len)
+{
+	struct mgmt_cp_cancel_resolve_name *mgmt_cp = (void *) data;
+	struct hci_cp_remote_name_req_cancel hci_cp;
+	struct hci_dev *hdev;
+	int err;
+
+	BT_DBG("");
+
+	if (len != sizeof(*mgmt_cp))
+		return cmd_status(sk, index, MGMT_OP_CANCEL_RESOLVE_NAME,
+								EINVAL);
+
+	hdev = hci_dev_get(index);
+	if (!hdev)
+		return cmd_status(sk, index, MGMT_OP_CANCEL_RESOLVE_NAME,
+								ENODEV);
+
+	hci_dev_lock_bh(hdev);
+
+	memset(&hci_cp, 0, sizeof(hci_cp));
+	bacpy(&hci_cp.bdaddr, &mgmt_cp->bdaddr);
+	err = hci_send_cmd(hdev, HCI_OP_REMOTE_NAME_REQ_CANCEL, sizeof(hci_cp),
+								&hci_cp);
+
+	hci_dev_unlock_bh(hdev);
+	hci_dev_put(hdev);
+
+	return err;
+}
+
 static int set_connection_params(struct sock *sk, u16 index,
 				unsigned char *data, u16 len)
 {
@@ -2189,10 +2221,11 @@
 	struct mgmt_mode cp = {0};
 	int err = -1;
 
-	BT_DBG("");
-
 	hdev = hci_dev_get(index);
 
+	if (hdev)
+		BT_DBG("disco_state: %d", hdev->disco_state);
+
 	if (!hdev || !lmp_le_capable(hdev)) {
 
 		mgmt_pending_foreach(MGMT_OP_STOP_DISCOVERY, index,
@@ -2200,6 +2233,8 @@
 
 		mgmt_event(MGMT_EV_DISCOVERING, index, &cp, sizeof(cp), NULL);
 
+		hdev->disco_state = SCAN_IDLE;
+
 		if (hdev)
 			goto done;
 		else
@@ -2315,6 +2350,7 @@
 	if (!hdev)
 		return cmd_status(sk, index, MGMT_OP_START_DISCOVERY, ENODEV);
 
+	BT_DBG("disco_state: %d", hdev->disco_state);
 	hci_dev_lock_bh(hdev);
 
 	if (hdev->disco_state && timer_pending(&hdev->disco_timer)) {
@@ -2395,6 +2431,8 @@
 	if (!hdev)
 		return cmd_status(sk, index, MGMT_OP_STOP_DISCOVERY, ENODEV);
 
+	BT_DBG("disco_state: %d", hdev->disco_state);
+
 	hci_dev_lock_bh(hdev);
 
 	state = hdev->disco_state;
@@ -2659,6 +2697,9 @@
 	case MGMT_OP_RESOLVE_NAME:
 		err = resolve_name(sk, index, buf + sizeof(*hdr), len);
 		break;
+	case MGMT_OP_CANCEL_RESOLVE_NAME:
+		err = cancel_resolve_name(sk, index, buf + sizeof(*hdr), len);
+		break;
 	case MGMT_OP_SET_CONNECTION_PARAMS:
 		err = set_connection_params(sk, index, buf + sizeof(*hdr), len);
 		break;
diff --git a/net/rfkill/core.c b/net/rfkill/core.c
index 04afd89..b7a3656 100644
--- a/net/rfkill/core.c
+++ b/net/rfkill/core.c
@@ -256,6 +256,7 @@
 static void rfkill_set_block(struct rfkill *rfkill, bool blocked)
 {
 	unsigned long flags;
+	bool prev, curr;
 	int err;
 
 	if (unlikely(rfkill->dev.power.power_state.event & PM_EVENT_SLEEP))
@@ -270,6 +271,8 @@
 		rfkill->ops->query(rfkill, rfkill->data);
 
 	spin_lock_irqsave(&rfkill->lock, flags);
+	prev = rfkill->state & RFKILL_BLOCK_SW;
+
 	if (rfkill->state & RFKILL_BLOCK_SW)
 		rfkill->state |= RFKILL_BLOCK_SW_PREV;
 	else
@@ -299,10 +302,15 @@
 	}
 	rfkill->state &= ~RFKILL_BLOCK_SW_SETCALL;
 	rfkill->state &= ~RFKILL_BLOCK_SW_PREV;
+	curr = rfkill->state & RFKILL_BLOCK_SW;
+
 	spin_unlock_irqrestore(&rfkill->lock, flags);
 
 	rfkill_led_trigger_event(rfkill);
-	rfkill_event(rfkill);
+
+	if (prev != curr)
+		rfkill_event(rfkill);
+
 }
 
 #ifdef CONFIG_RFKILL_INPUT
diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c
index f5a7ac3..dd99041 100644
--- a/net/wireless/mlme.c
+++ b/net/wireless/mlme.c
@@ -954,3 +954,16 @@
 	return nl80211_unexpected_4addr_frame(dev, addr, gfp);
 }
 EXPORT_SYMBOL(cfg80211_rx_unexpected_4addr_frame);
+
+int cfg80211_ft_event(struct net_device *dev,
+			struct cfg80211_ft_event_params ft_event)
+{
+	int err = 0;
+	struct wiphy *wiphy = dev->ieee80211_ptr->wiphy;
+	struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+
+	nl80211_ft_event(rdev, dev, ft_event);
+
+	return err;
+}
+EXPORT_SYMBOL(cfg80211_ft_event);
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index e322d4d..0410707 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -206,6 +206,9 @@
 	[NL80211_ATTR_NOACK_MAP] = { .type = NLA_U16 },
 	[NL80211_ATTR_INACTIVITY_TIMEOUT] = { .type = NLA_U16 },
 	[NL80211_ATTR_BG_SCAN_PERIOD] = { .type = NLA_U16 },
+	[NL80211_ATTR_MDID] = { .type = NLA_U16 },
+	[NL80211_ATTR_IE_RIC] = { .type = NLA_BINARY,
+					 .len = IEEE80211_MAX_DATA_LEN },
 };
 
 /* policy for the key attributes */
@@ -6299,6 +6302,26 @@
 	return 0;
 }
 
+static int nl80211_update_ft_ies(struct sk_buff *skb, struct genl_info *info)
+{
+	struct cfg80211_registered_device *rdev = info->user_ptr[0];
+	struct cfg80211_update_ft_ies_params ft_params;
+	struct net_device *dev = info->user_ptr[1];
+
+	if (!info->attrs[NL80211_ATTR_MDID])
+		return -EINVAL;
+
+	ft_params.md = nla_get_u16(info->attrs[NL80211_ATTR_MDID]);
+
+	if (!info->attrs[NL80211_ATTR_IE])
+		return -EINVAL;
+
+	ft_params.ie = nla_data(info->attrs[NL80211_ATTR_IE]);
+	ft_params.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
+
+	return rdev->ops->update_ft_ies(&rdev->wiphy, dev, &ft_params);
+}
+
 #define NL80211_FLAG_NEED_WIPHY		0x01
 #define NL80211_FLAG_NEED_NETDEV	0x02
 #define NL80211_FLAG_NEED_RTNL		0x04
@@ -6887,6 +6910,14 @@
 		.internal_flags = NL80211_FLAG_NEED_NETDEV |
 				  NL80211_FLAG_NEED_RTNL,
 	},
+	{
+		.cmd = NL80211_CMD_UPDATE_FT_IES,
+		.doit = nl80211_update_ft_ies,
+		.policy = nl80211_policy,
+		.flags = GENL_ADMIN_PERM,
+		.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
+				  NL80211_FLAG_NEED_RTNL,
+	},
 
 };
 
@@ -8080,6 +8111,47 @@
 	.notifier_call = nl80211_netlink_notify,
 };
 
+void nl80211_ft_event(struct cfg80211_registered_device *rdev,
+	struct net_device *netdev, struct cfg80211_ft_event_params ft_event)
+{
+	struct sk_buff *msg;
+	void *hdr;
+
+	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+	if (!msg)
+		return;
+
+	hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_FT_EVENT);
+	if (!hdr) {
+		nlmsg_free(msg);
+		return;
+	}
+
+	NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
+	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
+	if (ft_event.target_ap)
+		NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, ft_event.target_ap);
+	if (ft_event.ies)
+		NLA_PUT(msg, NL80211_ATTR_IE, ft_event.ies_len, ft_event.ies);
+	if (ft_event.ric_ies)
+		NLA_PUT(msg, NL80211_ATTR_IE_RIC, ft_event.ric_ies_len,
+					ft_event.ric_ies);
+
+	if (genlmsg_end(msg, hdr) < 0) {
+		nlmsg_free(msg);
+		return;
+	}
+
+	genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
+				nl80211_mlme_mcgrp.id, GFP_KERNEL);
+	return;
+
+ nla_put_failure:
+	genlmsg_cancel(msg, hdr);
+	nlmsg_free(msg);
+}
+
+
 /* initialisation/exit functions */
 
 int nl80211_init(void)
diff --git a/net/wireless/nl80211.h b/net/wireless/nl80211.h
index 4ffe50d..ffd4c8a 100644
--- a/net/wireless/nl80211.h
+++ b/net/wireless/nl80211.h
@@ -123,4 +123,8 @@
 bool nl80211_unexpected_4addr_frame(struct net_device *dev,
 				    const u8 *addr, gfp_t gfp);
 
+void nl80211_ft_event(struct cfg80211_registered_device *rdev,
+			struct net_device *netdev,
+			struct cfg80211_ft_event_params ft_event);
+
 #endif /* __NET_WIRELESS_NL80211_H */
diff --git a/scripts/build-all.py b/scripts/build-all.py
index 296d9ad..7f37841 100755
--- a/scripts/build-all.py
+++ b/scripts/build-all.py
@@ -35,6 +35,7 @@
 import subprocess
 import os
 import os.path
+import re
 import shutil
 import sys
 
@@ -83,12 +84,15 @@
 def scan_configs():
     """Get the full list of defconfigs appropriate for this tree."""
     names = {}
-    for n in glob.glob('arch/arm/configs/[fm]sm[0-9-]*_defconfig'):
-        names[os.path.basename(n)[:-10]] = n
-    for n in glob.glob('arch/arm/configs/qsd*_defconfig'):
-        names[os.path.basename(n)[:-10]] = n
-    for n in glob.glob('arch/arm/configs/apq*_defconfig'):
-        names[os.path.basename(n)[:-10]] = n
+    arch_pats = (
+        r'[fm]sm[0-9]*_defconfig',
+        r'apq*_defconfig',
+        r'qsd*_defconfig',
+        r'omap2*_defconfig',
+        )
+    for p in arch_pats:
+        for n in glob.glob('arch/arm/configs/' + p):
+            names[os.path.basename(n)[:-10]] = n
     return names
 
 class Builder:
@@ -142,23 +146,41 @@
     savedefconfig = '%s/defconfig' % dest_dir
     shutil.copyfile(defconfig, dotconfig)
 
+    staging_dir = 'install_staging'
+    modi_dir = '%s' % staging_dir
+    hdri_dir = '%s/usr' % staging_dir
+    shutil.rmtree(os.path.join(dest_dir, staging_dir), ignore_errors=True)
+
     devnull = open('/dev/null', 'r')
     subprocess.check_call(['make', 'O=%s' % dest_dir,
         '%s_defconfig' % target], env=make_env, stdin=devnull)
     devnull.close()
 
     if not all_options.updateconfigs:
-        build = Builder(log_name)
-
-        result = build.run(['make', 'O=%s' % dest_dir] + make_command)
-
-        if result != 0:
-            if all_options.keep_going:
-                failed_targets.append(target)
-                fail_or_error = error
+        # Build targets can be dependent upon the completion of previous
+        # build targets, so build them one at a time.
+        cmd_line = ['make',
+            'INSTALL_HDR_PATH=%s' % hdri_dir,
+            'INSTALL_MOD_PATH=%s' % modi_dir,
+            'O=%s' % dest_dir]
+        build_targets = []
+        for c in make_command:
+            if re.match(r'^-{1,2}\w', c):
+                cmd_line.append(c)
             else:
-                fail_or_error = fail
-            fail_or_error("Failed to build %s, see %s" % (target, build.logname))
+                build_targets.append(c)
+        for t in build_targets:
+            build = Builder(log_name)
+
+            result = build.run(cmd_line + [t])
+            if result != 0:
+                if all_options.keep_going:
+                    failed_targets.append(target)
+                    fail_or_error = error
+                else:
+                    fail_or_error = fail
+                fail_or_error("Failed to build %s, see %s" %
+                              (target, build.logname))
 
     # Copy the defconfig back.
     if all_options.configs or all_options.updateconfigs:
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/soc/codecs/cs8427.c b/sound/soc/codecs/cs8427.c
index 23870a4..6e08742 100644
--- a/sound/soc/codecs/cs8427.c
+++ b/sound/soc/codecs/cs8427.c
@@ -110,7 +110,7 @@
 	 * with CS8427 chip
 	 */
 	if (pdata->enable) {
-		err = pdata->enable(1);
+		err = pdata->enable(1, pdata->ls_gpio);
 		if (err < 0) {
 			dev_err(&chip->client->dev,
 				"failed to enable the level shifter\n");
@@ -124,7 +124,7 @@
 	 * with CS8427 chip
 	 */
 	if (pdata->enable) {
-		err = pdata->enable(0);
+		err = pdata->enable(0, pdata->ls_gpio);
 		if (err < 0) {
 			dev_err(&chip->client->dev,
 				"failed to disable the level shifter\n");
@@ -192,7 +192,7 @@
 	 * with CS8427 chip
 	 */
 	if (pdata->enable) {
-		err = pdata->enable(1);
+		err = pdata->enable(1, pdata->ls_gpio);
 		if (err < 0) {
 			dev_err(&chip->client->dev,
 				"failed to enable the level shifter\n");
@@ -207,7 +207,7 @@
 	 * with CS8427 chip
 	 */
 	if (pdata->enable) {
-		err = pdata->enable(0);
+		err = pdata->enable(0, pdata->ls_gpio);
 		if (err < 0) {
 			dev_err(&chip->client->dev,
 				"failed to disable the level shifter\n");
@@ -239,7 +239,7 @@
 	 * with CS8427 chip
 	 */
 	if (pdata->enable) {
-		err = pdata->enable(1);
+		err = pdata->enable(1, pdata->ls_gpio);
 		if (err < 0) {
 			dev_err(&chip->client->dev,
 				"failed to enable the level shifter\n");
@@ -262,7 +262,7 @@
 	 * with CS8427 chip
 	 */
 	if (pdata->enable) {
-		err = pdata->enable(0);
+		err = pdata->enable(0, pdata->ls_gpio);
 		if (err < 0) {
 			dev_err(&chip->client->dev,
 				"failed to disable the level shifter\n");
@@ -734,6 +734,16 @@
 	struct cs8427_platform_data *pdata = chip->client->dev.platform_data;
 	int ret = 0;
 
+	if (pdata->enable) {
+		ret = gpio_request(pdata->ls_gpio, "cs8427 ls");
+		if (ret < 0) {
+			dev_err(&chip->client->dev,
+				 "failed to request the gpio %d\n",
+					pdata->reset_gpio);
+			return ret;
+		}
+	}
+
 	ret = gpio_request(pdata->reset_gpio, "cs8427 reset");
 	if (ret < 0) {
 		dev_err(&chip->client->dev,
@@ -928,8 +938,10 @@
 	}
 	pdata = chip->client->dev.platform_data;
 	gpio_free(pdata->reset_gpio);
-	if (pdata->enable)
-		pdata->enable(0);
+	if (pdata->enable) {
+		pdata->enable(0, pdata->ls_gpio);
+		gpio_free(pdata->ls_gpio);
+	}
 	kfree(chip);
 	return 0;
 }
diff --git a/sound/soc/codecs/wcd9304.c b/sound/soc/codecs/wcd9304.c
index 9f02134..fd9d825 100644
--- a/sound/soc/codecs/wcd9304.c
+++ b/sound/soc/codecs/wcd9304.c
@@ -951,39 +951,6 @@
 	SOC_DAPM_SINGLE("Switch", SITAR_A_RX_EAR_EN, 5, 1, 0),
 };
 
-static int slim_tx_vport_validation(u32 dai_id, u32 port_id,
-				    struct sitar_priv *sitar_p)
-{
-	struct wcd9xxx_ch *ch;
-	int ret = 0;
-	int index = 0;
-	u32 vtable = vport_check_table[dai_id];
-	pr_debug("%s: dai_id %u vtable 0x%x port_id %u\n", __func__,
-		 dai_id, vtable, port_id);
-	while (vtable) {
-		if (vtable & 1) {
-			list_for_each_entry(ch,
-				&sitar_p->dai[index].wcd9xxx_ch_list,
-				list) {
-				pr_debug("%s: index %u ch->port%u  vtable 0x%x\n",
-					__func__, index, ch->port, vtable);
-				if (ch->port == port_id) {
-					pr_err("%s: TX%u is used by AIF%u_CAP Mixer\n",
-						__func__, port_id + 1,
-						(index + 1)/2);
-					ret = -EINVAL;
-					break;
-				}
-			}
-		}
-		if (ret)
-			break;
-		index++;
-		vtable = vtable >> 1;
-	}
-	return ret;
-}
-
 static int slim_tx_mixer_get(struct snd_kcontrol *kcontrol,
 				struct snd_ctl_elem_value *ucontrol)
 {
@@ -1022,8 +989,10 @@
 	switch (dai_id) {
 	case AIF1_CAP:
 		if (enable && !(widget->value & 1 << port_id)) {
-			if (slim_tx_vport_validation(dai_id,
-						     port_id, sitar_p)) {
+			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);
@@ -1093,8 +1062,7 @@
 		if (widget->value > 1) {
 			dev_err(codec->dev, "%s: invalid AIF for I2C mode\n",
 				__func__);
-			mutex_unlock(&codec->mutex);
-			return -EINVAL;
+			goto err;
 		}
 	}
 
@@ -1103,17 +1071,29 @@
 		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);
-		mutex_unlock(&codec->mutex);
-		return -EINVAL;
+		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 =
@@ -1258,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);
@@ -2076,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),
@@ -2087,7 +2085,9 @@
 	/*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),
@@ -2836,7 +2836,7 @@
 	if (dai->id <= NUM_CODEC_DAIS) {
 		if (sitar->dai[dai->id].ch_mask) {
 			active = 1;
-			pr_debug("%s(): Codec DAI: chmask[%d] = 0x%x\n",
+			pr_debug("%s(): Codec DAI: chmask[%d] = 0x%lx\n",
 				__func__, dai->id,
 				sitar->dai[dai->id].ch_mask);
 		}
@@ -5157,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),
diff --git a/sound/soc/codecs/wcd9310.c b/sound/soc/codecs/wcd9310.c
index 564cad6..e11b985 100644
--- a/sound/soc/codecs/wcd9310.c
+++ b/sound/soc/codecs/wcd9310.c
@@ -1722,39 +1722,6 @@
 static const struct snd_kcontrol_new lineout4_ground_switch =
 	SOC_DAPM_SINGLE("Switch", TABLA_A_RX_LINE_4_DAC_CTL, 6, 1, 0);
 
-static int slim_tx_vport_validation(u32 dai_id, u32 port_id,
-				    struct tabla_priv *tabla_p)
-{
-	struct wcd9xxx_ch *ch;
-	int ret = 0;
-	int index = 0;
-	u32 vtable = vport_check_table[dai_id];
-	pr_debug("%s: dai_id %u vtable 0x%x port_id %u\n", __func__,
-		 dai_id, vtable, port_id);
-	while (vtable) {
-		if (vtable & 1) {
-			list_for_each_entry(ch,
-				&tabla_p->dai[index].wcd9xxx_ch_list,
-				list) {
-				pr_debug("%s: index %u ch->port %u vtable 0x%x\n",
-					__func__, index, ch->port, vtable);
-				if (ch->port == port_id) {
-					pr_err("%s: TX%u is used by AIF%u_CAP Mixer\n",
-						__func__, port_id + 1,
-						(index + 1)/2);
-					ret = -EINVAL;
-					break;
-				}
-			}
-		}
-		if (ret)
-			break;
-		index++;
-		vtable = vtable >> 1;
-	}
-	return ret;
-}
-
 /* virtual port entries */
 static int slim_tx_mixer_get(struct snd_kcontrol *kcontrol,
 			     struct snd_ctl_elem_value *ucontrol)
@@ -1800,8 +1767,10 @@
 		/* only add to the list if value not set
 		 */
 		if (enable && !(widget->value & 1 << port_id)) {
-			if (slim_tx_vport_validation(dai_id,
-						     port_id, tabla_p)) {
+			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);
@@ -1877,8 +1846,7 @@
 		if (widget->value > 1) {
 			dev_err(codec->dev, "%s: invalid AIF for I2C mode\n",
 				__func__);
-			mutex_unlock(&codec->mutex);
-			return -EINVAL;
+			goto err;
 		}
 	}
 	/* value need to match the Virtual port and AIF number
@@ -1888,27 +1856,44 @@
 		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);
-		mutex_unlock(&codec->mutex);
-		return -EINVAL;
+		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);
+	mutex_unlock(&codec->mutex);
+	return 0;
+err:
+	mutex_unlock(&codec->mutex);
+	return -EINVAL;
 }
 
 static const struct soc_enum slim_rx_mux_enum =
@@ -3050,7 +3035,7 @@
 					msecs_to_jiffies(300));
 		}
 		/* apply the digital gain after the decimator is enabled*/
-		if ((w->shift) < ARRAY_SIZE(rx_digital_gain_reg))
+		if ((w->shift + offset) < ARRAY_SIZE(tx_digital_gain_reg))
 			snd_soc_write(codec,
 				  tx_digital_gain_reg[w->shift + offset],
 				  snd_soc_read(codec,
@@ -4143,7 +4128,7 @@
 	if (dai->id <= NUM_CODEC_DAIS) {
 		if (tabla->dai[dai->id].ch_mask) {
 			active = 1;
-			pr_debug("%s(): Codec DAI: chmask[%d] = 0x%x\n",
+			pr_debug("%s(): Codec DAI: chmask[%d] = 0x%lx\n",
 				__func__, dai->id, tabla->dai[dai->id].ch_mask);
 		}
 	}
@@ -7878,7 +7863,7 @@
 				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",
+					pr_debug("%s: tabla_p->dai[%d].ch_mask = 0x%lx\n",
 						 __func__, k,
 						 tabla_p->dai[k].ch_mask);
 					if (ch_mask_temp &
diff --git a/sound/soc/codecs/wcd9320.c b/sound/soc/codecs/wcd9320.c
index 886e4d3..5ffb60a 100644
--- a/sound/soc/codecs/wcd9320.c
+++ b/sound/soc/codecs/wcd9320.c
@@ -18,6 +18,8 @@
 #include <linux/printk.h>
 #include <linux/ratelimit.h>
 #include <linux/debugfs.h>
+#include <linux/wait.h>
+#include <linux/bitops.h>
 #include <linux/mfd/wcd9xxx/core.h>
 #include <linux/mfd/wcd9xxx/wcd9xxx_registers.h>
 #include <linux/mfd/wcd9xxx/wcd9320_registers.h>
@@ -45,6 +47,13 @@
 #define TAIKO_TX_PORT_NUMBER	16
 
 #define TAIKO_I2S_MASTER_MODE_MASK 0x08
+#define TAIKO_MCLK_CLK_12P288MHZ 12288000
+#define TAIKO_MCLK_CLK_9P6HZ 9600000
+
+#define TAIKO_SLIM_CLOSE_TIMEOUT 1000
+#define TAIKO_SLIM_IRQ_OVERFLOW (1 << 0)
+#define TAIKO_SLIM_IRQ_UNDERFLOW (1 << 1)
+#define TAIKO_SLIM_IRQ_PORT_CLOSED (1 << 2)
 
 enum {
 	AIF1_PB = 0,
@@ -97,7 +106,8 @@
 };
 
 enum {
-	COMPANDER_1 = 0,
+	COMPANDER_0,
+	COMPANDER_1,
 	COMPANDER_2,
 	COMPANDER_MAX,
 };
@@ -203,6 +213,7 @@
 };
 
 static const u32 comp_shift[] = {
+	4, /* Compander 0's clock source is on interpolator 7 */
 	0,
 	2,
 };
@@ -214,31 +225,46 @@
 	COMPANDER_2,
 	COMPANDER_2,
 	COMPANDER_2,
+	COMPANDER_0,
 	COMPANDER_MAX,
 };
 
 static const struct comp_sample_dependent_params comp_samp_params[] = {
 	{
-		.peak_det_timeout = 0x2,
-		.rms_meter_div_fact = 0x8 << 4,
-		.rms_meter_resamp_fact = 0x21,
+		/* 8 Khz */
+		.peak_det_timeout = 0x02,
+		.rms_meter_div_fact = 0x09,
+		.rms_meter_resamp_fact = 0x06,
 	},
 	{
-		.peak_det_timeout = 0x3,
-		.rms_meter_div_fact = 0x9 << 4,
+		/* 16 Khz */
+		.peak_det_timeout = 0x03,
+		.rms_meter_div_fact = 0x0A,
+		.rms_meter_resamp_fact = 0x0C,
+	},
+	{
+		/* 32 Khz */
+		.peak_det_timeout = 0x05,
+		.rms_meter_div_fact = 0x0B,
+		.rms_meter_resamp_fact = 0x1E,
+	},
+	{
+		/* 48 Khz */
+		.peak_det_timeout = 0x05,
+		.rms_meter_div_fact = 0x0B,
 		.rms_meter_resamp_fact = 0x28,
 	},
-
 	{
-		.peak_det_timeout = 0x5,
-		.rms_meter_div_fact = 0xB << 4,
-		.rms_meter_resamp_fact = 0x28,
+		/* 96 Khz */
+		.peak_det_timeout = 0x06,
+		.rms_meter_div_fact = 0x0C,
+		.rms_meter_resamp_fact = 0x50,
 	},
-
 	{
-		.peak_det_timeout = 0x5,
-		.rms_meter_div_fact = 0xB << 4,
-		.rms_meter_resamp_fact = 0x28,
+		/* 192 Khz */
+		.peak_det_timeout = 0x07,
+		.rms_meter_div_fact = 0xD,
+		.rms_meter_resamp_fact = 0xA0,
 	},
 };
 
@@ -297,6 +323,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);
@@ -309,12 +336,24 @@
 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);
+		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);
+		break;
 	}
 	return 0;
 }
@@ -536,194 +575,167 @@
 	return 0;
 }
 
-static int taiko_compander_gain_offset(
-	struct snd_soc_codec *codec, u32 enable,
-	unsigned int reg, int mask,	int event)
-{
-	int pa_mode = snd_soc_read(codec, reg) & mask;
-	int gain_offset = 0;
-	/*  if PMU && enable is 1-> offset is 3
-	 *  if PMU && enable is 0-> offset is 0
-	 *  if PMD && pa_mode is PA -> offset is 0: PMU compander is off
-	 *  if PMD && pa_mode is comp -> offset is -3: PMU compander is on.
-	 */
-
-	if (SND_SOC_DAPM_EVENT_ON(event) && (enable != 0))
-		gain_offset = TAIKO_COMP_DIGITAL_GAIN_OFFSET;
-	if (SND_SOC_DAPM_EVENT_OFF(event) && (pa_mode == 0))
-		gain_offset = -TAIKO_COMP_DIGITAL_GAIN_OFFSET;
-	return gain_offset;
-}
-
-
-static int taiko_config_gain_compander(
-				struct snd_soc_codec *codec,
-				u32 compander, u32 enable, int event)
-{
-	int value = 0;
-	int mask = 1 << 5;
-	int gain = 0;
-	int gain_offset;
-	if (compander >= COMPANDER_MAX) {
-		pr_err("%s: Error, invalid compander channel\n", __func__);
-		return -EINVAL;
-	}
-
-	if ((enable == 0) || SND_SOC_DAPM_EVENT_OFF(event))
-		value = 1 << 4;
-
-	if (compander == COMPANDER_1) {
-		gain_offset = taiko_compander_gain_offset(codec, enable,
-				TAIKO_A_RX_HPH_L_GAIN, mask, event);
-		snd_soc_update_bits(codec, TAIKO_A_RX_HPH_L_GAIN, mask, value);
-		gain = snd_soc_read(codec, TAIKO_A_CDC_RX1_VOL_CTL_B2_CTL);
-		snd_soc_update_bits(codec, TAIKO_A_CDC_RX1_VOL_CTL_B2_CTL,
-				0xFF, gain - gain_offset);
-		gain_offset = taiko_compander_gain_offset(codec, enable,
-				TAIKO_A_RX_HPH_R_GAIN, mask, event);
-		snd_soc_update_bits(codec, TAIKO_A_RX_HPH_R_GAIN, mask, value);
-		gain = snd_soc_read(codec, TAIKO_A_CDC_RX2_VOL_CTL_B2_CTL);
-		snd_soc_update_bits(codec, TAIKO_A_CDC_RX2_VOL_CTL_B2_CTL,
-				0xFF, gain - gain_offset);
-	} else if (compander == COMPANDER_2) {
-		gain_offset = taiko_compander_gain_offset(codec, enable,
-				TAIKO_A_RX_LINE_1_GAIN, mask, event);
-		snd_soc_update_bits(codec, TAIKO_A_RX_LINE_1_GAIN, mask, value);
-		gain = snd_soc_read(codec, TAIKO_A_CDC_RX3_VOL_CTL_B2_CTL);
-		snd_soc_update_bits(codec, TAIKO_A_CDC_RX3_VOL_CTL_B2_CTL,
-				0xFF, gain - gain_offset);
-		gain_offset = taiko_compander_gain_offset(codec, enable,
-				TAIKO_A_RX_LINE_3_GAIN, mask, event);
-		snd_soc_update_bits(codec, TAIKO_A_RX_LINE_3_GAIN, mask, value);
-		gain = snd_soc_read(codec, TAIKO_A_CDC_RX4_VOL_CTL_B2_CTL);
-		snd_soc_update_bits(codec, TAIKO_A_CDC_RX4_VOL_CTL_B2_CTL,
-				0xFF, gain - gain_offset);
-		gain_offset = taiko_compander_gain_offset(codec, enable,
-				TAIKO_A_RX_LINE_2_GAIN, mask, event);
-		snd_soc_update_bits(codec, TAIKO_A_RX_LINE_2_GAIN, mask, value);
-		gain = snd_soc_read(codec, TAIKO_A_CDC_RX5_VOL_CTL_B2_CTL);
-		snd_soc_update_bits(codec, TAIKO_A_CDC_RX5_VOL_CTL_B2_CTL,
-				0xFF, gain - gain_offset);
-		gain_offset = taiko_compander_gain_offset(codec, enable,
-				TAIKO_A_RX_LINE_4_GAIN, mask, event);
-		snd_soc_update_bits(codec, TAIKO_A_RX_LINE_4_GAIN, mask, value);
-		gain = snd_soc_read(codec, TAIKO_A_CDC_RX6_VOL_CTL_B2_CTL);
-		snd_soc_update_bits(codec, TAIKO_A_CDC_RX6_VOL_CTL_B2_CTL,
-				0xFF, gain - gain_offset);
-	}
-	return 0;
-}
 static int taiko_get_compander(struct snd_kcontrol *kcontrol,
-					   struct snd_ctl_elem_value *ucontrol)
+			       struct snd_ctl_elem_value *ucontrol)
 {
 
 	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
 	int comp = ((struct soc_multi_mixer_control *)
-					kcontrol->private_value)->max;
+		    kcontrol->private_value)->shift;
 	struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
 
 	ucontrol->value.integer.value[0] = taiko->comp_enabled[comp];
-
 	return 0;
 }
 
 static int taiko_set_compander(struct snd_kcontrol *kcontrol,
-					   struct snd_ctl_elem_value *ucontrol)
+			       struct snd_ctl_elem_value *ucontrol)
 {
 	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
 	struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
 	int comp = ((struct soc_multi_mixer_control *)
-					kcontrol->private_value)->max;
+		    kcontrol->private_value)->shift;
 	int value = ucontrol->value.integer.value[0];
 
-	if (value == taiko->comp_enabled[comp]) {
-		pr_debug("%s: compander #%d enable %d no change\n",
-			    __func__, comp, value);
-		return 0;
-	}
+	pr_debug("%s: Compander %d enable current %d, new %d\n",
+		 __func__, comp, taiko->comp_enabled[comp], value);
 	taiko->comp_enabled[comp] = value;
 	return 0;
 }
 
+static int taiko_config_gain_compander(struct snd_soc_codec *codec,
+				       int comp, bool enable)
+{
+	int ret = 0;
+
+	switch (comp) {
+	case COMPANDER_0:
+		snd_soc_update_bits(codec, TAIKO_A_SPKR_DRV_GAIN,
+				    1 << 2, !enable << 2);
+		break;
+	case COMPANDER_1:
+		snd_soc_update_bits(codec, TAIKO_A_RX_HPH_L_GAIN,
+				    1 << 5, !enable << 5);
+		snd_soc_update_bits(codec, TAIKO_A_RX_HPH_R_GAIN,
+				    1 << 5, !enable << 5);
+		break;
+	case COMPANDER_2:
+		snd_soc_update_bits(codec, TAIKO_A_RX_LINE_1_GAIN,
+				    1 << 5, !enable << 5);
+		snd_soc_update_bits(codec, TAIKO_A_RX_LINE_3_GAIN,
+				    1 << 5, !enable << 5);
+		snd_soc_update_bits(codec, TAIKO_A_RX_LINE_2_GAIN,
+				    1 << 5, !enable << 5);
+		snd_soc_update_bits(codec, TAIKO_A_RX_LINE_4_GAIN,
+				    1 << 5, !enable << 5);
+		break;
+	default:
+		WARN_ON(1);
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+static void taiko_discharge_comp(struct snd_soc_codec *codec, int comp)
+{
+	/* Update RSM to 1, DIVF to 5 */
+	snd_soc_write(codec, TAIKO_A_CDC_COMP0_B3_CTL + (comp * 8), 1);
+	snd_soc_update_bits(codec, TAIKO_A_CDC_COMP0_B2_CTL + (comp * 8), 0xF0,
+			    1 << 5);
+	/* Wait for 1ms */
+	usleep_range(1000, 1000);
+}
 
 static int taiko_config_compander(struct snd_soc_dapm_widget *w,
-						  struct snd_kcontrol *kcontrol,
-						  int event)
+				  struct snd_kcontrol *kcontrol, int event)
 {
+	int mask, emask;
+	bool timedout;
+	unsigned long timeout;
 	struct snd_soc_codec *codec = w->codec;
 	struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
-	u32 rate = taiko->comp_fs[w->shift];
+	const int comp = w->shift;
+	const u32 rate = taiko->comp_fs[comp];
+	const struct comp_sample_dependent_params *comp_params =
+	    &comp_samp_params[rate];
 
-	pr_debug("%s: %s event %d enabled = %d", __func__, w->name,
-		event, taiko->comp_enabled[w->shift]);
+	pr_debug("%s: %s event %d compander %d, enabled %d", __func__,
+		 w->name, event, comp, taiko->comp_enabled[comp]);
+
+	if (!taiko->comp_enabled[comp])
+		return 0;
+
+	/* Compander 0 has single channel */
+	mask = (comp == COMPANDER_0 ? 0x01 : 0x03);
+	emask = (comp == COMPANDER_0 ? 0x02 : 0x03);
 
 	switch (event) {
 	case SND_SOC_DAPM_PRE_PMU:
-		if (taiko->comp_enabled[w->shift] != 0) {
-			/* Enable both L/R compander clocks */
-			snd_soc_update_bits(codec,
-					TAIKO_A_CDC_CLK_RX_B2_CTL,
-					0x03 << comp_shift[w->shift],
-					0x03 << comp_shift[w->shift]);
-			/* Clar the HALT for the compander*/
-			snd_soc_update_bits(codec,
-					TAIKO_A_CDC_COMP1_B1_CTL +
-					w->shift * 8, 1 << 2, 0);
-			/* Toggle compander reset bits*/
-			snd_soc_update_bits(codec,
-					TAIKO_A_CDC_CLK_OTHR_RESET_B2_CTL,
-					0x03 << comp_shift[w->shift],
-					0x03 << comp_shift[w->shift]);
-			snd_soc_update_bits(codec,
-					TAIKO_A_CDC_CLK_OTHR_RESET_B2_CTL,
-					0x03 << comp_shift[w->shift], 0);
-			taiko_config_gain_compander(codec, w->shift, 1, event);
-			/* Update the RMS meter resampling*/
-			snd_soc_update_bits(codec,
-					TAIKO_A_CDC_COMP1_B3_CTL +
-					w->shift * 8, 0xFF, 0x01);
-			/* Wait for 1ms*/
-			usleep_range(1000, 1000);
-		}
+		/* Set gain source to compander */
+		taiko_config_gain_compander(codec, comp, true);
+		/* Enable RX interpolation path clocks */
+		snd_soc_update_bits(codec, TAIKO_A_CDC_CLK_RX_B2_CTL,
+				    mask << comp_shift[comp],
+				    mask << comp_shift[comp]);
+
+		taiko_discharge_comp(codec, comp);
+
+		/* Clear compander halt */
+		snd_soc_update_bits(codec, TAIKO_A_CDC_COMP0_B1_CTL +
+					   (comp * 8),
+				    1 << 2, 0);
+		/* Toggle compander reset bits */
+		snd_soc_update_bits(codec, TAIKO_A_CDC_CLK_OTHR_RESET_B2_CTL,
+				    mask << comp_shift[comp],
+				    mask << comp_shift[comp]);
+		snd_soc_update_bits(codec, TAIKO_A_CDC_CLK_OTHR_RESET_B2_CTL,
+				    mask << comp_shift[comp], 0);
 		break;
 	case SND_SOC_DAPM_POST_PMU:
-		/* Set sample rate dependent paramater*/
-		if (taiko->comp_enabled[w->shift] != 0) {
-			snd_soc_update_bits(codec, TAIKO_A_CDC_COMP1_FS_CFG +
-			w->shift * 8, 0x03,	rate);
-			snd_soc_update_bits(codec, TAIKO_A_CDC_COMP1_B2_CTL +
-			w->shift * 8, 0x0F,
-			comp_samp_params[rate].peak_det_timeout);
-			snd_soc_update_bits(codec, TAIKO_A_CDC_COMP1_B2_CTL +
-			w->shift * 8, 0xF0,
-			comp_samp_params[rate].rms_meter_div_fact);
-			snd_soc_update_bits(codec, TAIKO_A_CDC_COMP1_B3_CTL +
-			w->shift * 8, 0xFF,
-			comp_samp_params[rate].rms_meter_resamp_fact);
-			/* Compander enable -> 0x370/0x378*/
-			snd_soc_update_bits(codec, TAIKO_A_CDC_COMP1_B1_CTL +
-			w->shift * 8, 0x03, 0x03);
-		}
+		/* Set sample rate dependent paramater */
+		snd_soc_update_bits(codec,
+				    TAIKO_A_CDC_COMP0_FS_CFG + (comp * 8),
+				    0x07, rate);
+		snd_soc_write(codec, TAIKO_A_CDC_COMP0_B3_CTL + (comp * 8),
+			      comp_params->rms_meter_resamp_fact);
+		snd_soc_update_bits(codec,
+				    TAIKO_A_CDC_COMP0_B2_CTL + (comp * 8),
+				    0x0F, comp_params->peak_det_timeout);
+		snd_soc_update_bits(codec,
+				    TAIKO_A_CDC_COMP0_B2_CTL + (comp * 8),
+				    0xF0, comp_params->rms_meter_div_fact << 4);
+		/* Compander enable */
+		snd_soc_update_bits(codec, TAIKO_A_CDC_COMP0_B1_CTL +
+				    (comp * 8), emask, emask);
 		break;
 	case SND_SOC_DAPM_PRE_PMD:
-		/* Halt the compander*/
-		if (taiko->comp_enabled[w->shift] != 0) {
-			snd_soc_update_bits(codec, TAIKO_A_CDC_COMP1_B1_CTL +
-				w->shift * 8, 1 << 2, 1 << 2);
-		}
+		/* Halt compander */
+		snd_soc_update_bits(codec,
+				    TAIKO_A_CDC_COMP0_B1_CTL + (comp * 8),
+				    1 << 2, 1 << 2);
+		/* Wait up to a second for shutdown complete */
+		timeout = jiffies + HZ;
+		do {
+			if ((snd_soc_read(codec,
+					  TAIKO_A_CDC_COMP0_SHUT_DOWN_STATUS +
+					  (comp * 8)) & mask) == mask)
+				break;
+		} while (!(timedout = time_after(jiffies, timeout)));
+		pr_debug("%s: Compander %d shutdown %s in %dms\n", __func__,
+			 comp, timedout ? "timedout" : "completed",
+			 jiffies_to_msecs(timeout - HZ - jiffies));
 		break;
 	case SND_SOC_DAPM_POST_PMD:
-		/* Restore the gain */
-		if (taiko->comp_enabled[w->shift] != 0) {
-			taiko_config_gain_compander(codec, w->shift,
-					taiko->comp_enabled[w->shift], event);
-			/* Disable the compander*/
-			snd_soc_update_bits(codec, TAIKO_A_CDC_COMP1_B1_CTL +
-					w->shift * 8, 0x03, 0x00);
-			/* Turn off the clock for compander in pair*/
-			snd_soc_update_bits(codec, TAIKO_A_CDC_CLK_RX_B2_CTL,
-				0x03 << comp_shift[w->shift], 0);
-		}
+		/* Disable compander */
+		snd_soc_update_bits(codec,
+				    TAIKO_A_CDC_COMP0_B1_CTL + (comp * 8),
+				    emask, 0x00);
+		/* Turn off the clock for compander in pair */
+		snd_soc_update_bits(codec, TAIKO_A_CDC_CLK_RX_B2_CTL,
+				    mask << comp_shift[comp], 0);
+		/* Set gain source to register */
+		taiko_config_gain_compander(codec, comp, false);
 		break;
 	}
 	return 0;
@@ -947,10 +959,12 @@
 	SOC_SINGLE_MULTI_EXT("IIR2 Band5", IIR2, BAND5, 255, 0, 5,
 	taiko_get_iir_band_audio_mixer, taiko_put_iir_band_audio_mixer),
 
-	SOC_SINGLE_EXT("COMP1 Switch", SND_SOC_NOPM, 1, COMPANDER_1, 0,
-				   taiko_get_compander, taiko_set_compander),
-	SOC_SINGLE_EXT("COMP2 Switch", SND_SOC_NOPM, 0, COMPANDER_2, 0,
-				   taiko_get_compander, taiko_set_compander),
+	SOC_SINGLE_EXT("COMP0 Switch", SND_SOC_NOPM, COMPANDER_0, 1, 0,
+		       taiko_get_compander, taiko_set_compander),
+	SOC_SINGLE_EXT("COMP1 Switch", SND_SOC_NOPM, COMPANDER_1, 1, 0,
+		       taiko_get_compander, taiko_set_compander),
+	SOC_SINGLE_EXT("COMP2 Switch", SND_SOC_NOPM, COMPANDER_2, 1, 0,
+		       taiko_get_compander, taiko_set_compander),
 
 };
 
@@ -963,10 +977,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"
@@ -1120,11 +1139,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);
@@ -1265,11 +1284,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);
@@ -1480,39 +1499,6 @@
 static const struct snd_kcontrol_new lineout4_ground_switch =
 	SOC_DAPM_SINGLE("Switch", TAIKO_A_RX_LINE_4_DAC_CTL, 6, 1, 0);
 
-static int slim_tx_vport_validation(u32 dai_id, u32 port_id,
-				    struct taiko_priv *taiko_p)
-{
-	struct wcd9xxx_ch *ch;
-	int ret = 0;
-	int index = 0;
-	u32 vtable = vport_check_table[dai_id];
-	pr_debug("%s: dai_id %u vtable 0x%x port_id %u\n", __func__,
-		 dai_id, vtable, port_id);
-	while (vtable) {
-		if (vtable & 1) {
-			list_for_each_entry(ch,
-				&taiko_p->dai[index].wcd9xxx_ch_list,
-				list) {
-				pr_debug("%s: index %u ch->port %u vtable 0x%x\n",
-					__func__, index, ch->port, vtable);
-				if (ch->port == port_id) {
-					pr_err("%s: TX%u is used by AIF%u_CAP Mixer\n",
-						__func__, port_id + 1,
-						(index + 1)/2);
-					ret = -EINVAL;
-					break;
-				}
-			}
-		}
-		if (ret)
-			break;
-		index++;
-		vtable = vtable >> 1;
-	}
-	return ret;
-}
-
 /* virtual port entries */
 static int slim_tx_mixer_get(struct snd_kcontrol *kcontrol,
 			     struct snd_ctl_elem_value *ucontrol)
@@ -1553,43 +1539,50 @@
 			return -EINVAL;
 		}
 	}
-	switch (dai_id) {
-	case AIF1_CAP:
-	case AIF2_CAP:
-	case AIF3_CAP:
-		/* only add to the list if value not set
-		 */
-		if (enable && !(widget->value & 1 << port_id)) {
-			if (slim_tx_vport_validation(dai_id,
-						     port_id, taiko_p)) {
-				pr_info("%s: TX%u is used by other virtual port\n",
-					__func__, port_id + 1);
-				mutex_unlock(&codec->mutex);
-				return -EINVAL;
-			}
-			widget->value |= 1 << port_id;
-			list_add_tail(&core->tx_chs[port_id].list,
+	if (taiko_p->intf_type == WCD9XXX_INTERFACE_TYPE_SLIMBUS) {
+		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_debug("%s: TX%u is used by other\n"
+						"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 */
+					      );
+			} 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_debug("%s: TX%u port is used by\n"
+						"this virtual port\n",
+						__func__, port_id + 1);
+				else
+					pr_debug("%s: TX%u port is not used by\n"
+						"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 0;
+			return -EINVAL;
 		}
-		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);
@@ -1637,8 +1630,7 @@
 		if (widget->value > 1) {
 			dev_err(codec->dev, "%s: invalid AIF for I2C mode\n",
 				__func__);
-			mutex_unlock(&codec->mutex);
-			return -EINVAL;
+			goto err;
 		}
 	}
 	/* value need to match the Virtual port and AIF number
@@ -1648,27 +1640,41 @@
 		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);
-		mutex_unlock(&codec->mutex);
-		return -EINVAL;
+		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 =
@@ -1950,7 +1956,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) {
@@ -2017,9 +2023,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);
 
@@ -2084,7 +2088,6 @@
 		/* 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);
 		/* Get cfilt */
 		wcd9xxx_resmgr_cfilt_get(&taiko->resmgr, cfilt_sel_val);
 
@@ -2244,7 +2247,7 @@
 					msecs_to_jiffies(300));
 		}
 		/* apply the digital gain after the decimator is enabled*/
-		if ((w->shift) < ARRAY_SIZE(rx_digital_gain_reg))
+		if ((w->shift + offset) < ARRAY_SIZE(tx_digital_gain_reg))
 			snd_soc_write(codec,
 				  tx_digital_gain_reg[w->shift + offset],
 				  snd_soc_read(codec,
@@ -2441,10 +2444,10 @@
 	{"SLIM RX3", NULL, "RX_I2S_CLK"},
 	{"SLIM RX4", NULL, "RX_I2S_CLK"},
 
-	{"SLIM TX7", NULL, "TX_I2S_CLK"},
-	{"SLIM TX8", NULL, "TX_I2S_CLK"},
-	{"SLIM TX9", NULL, "TX_I2S_CLK"},
-	{"SLIM TX10", NULL, "TX_I2S_CLK"},
+	{"SLIM TX7 MUX", NULL, "TX_I2S_CLK"},
+	{"SLIM TX8 MUX", NULL, "TX_I2S_CLK"},
+	{"SLIM TX9 MUX", NULL, "TX_I2S_CLK"},
+	{"SLIM TX10 MUX", NULL, "TX_I2S_CLK"},
 };
 
 static const struct snd_soc_dapm_route audio_map[] = {
@@ -2617,26 +2620,41 @@
 	{"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 MIX2"},
@@ -2653,6 +2671,7 @@
 	{"LINEOUT4 DAC", NULL, "RX_BIAS"},
 	{"SPK DAC", NULL, "RX_BIAS"},
 
+	{"RX7 MIX1", NULL, "COMP0_CLK"},
 	{"RX1 MIX1", NULL, "COMP1_CLK"},
 	{"RX2 MIX1", NULL, "COMP1_CLK"},
 	{"RX3 MIX1", NULL, "COMP2_CLK"},
@@ -3089,7 +3108,11 @@
 static int taiko_set_dai_sysclk(struct snd_soc_dai *dai,
 		int clk_id, unsigned int freq, int dir)
 {
-	pr_debug("%s\n", __func__);
+	struct snd_soc_codec *codec = dai->codec;
+	if (freq == TAIKO_MCLK_CLK_12P288MHZ)
+		snd_soc_write(codec, TAIKO_A_CHIP_CTL, 0x04);
+	else if (freq == TAIKO_MCLK_CLK_9P6HZ)
+		snd_soc_write(codec, TAIKO_A_CHIP_CTL, 0x0A);
 	return 0;
 }
 
@@ -3173,8 +3196,8 @@
 		}
 		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);
+			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);
@@ -3607,6 +3630,37 @@
 	},
 };
 
+static int taiko_codec_enable_slim_chmask(struct wcd9xxx_codec_dai_data *dai,
+					  bool up)
+{
+	int ret = 0;
+	struct wcd9xxx_ch *ch;
+
+	if (up) {
+		list_for_each_entry(ch, &dai->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);
+				ret = -EINVAL;
+			} else {
+				set_bit(ret, &dai->ch_mask);
+			}
+		}
+	} else {
+		ret = wait_event_timeout(dai->dai_wait, (dai->ch_mask == 0),
+					 msecs_to_jiffies(
+						     TAIKO_SLIM_CLOSE_TIMEOUT));
+		if (!ret) {
+			pr_err("%s: Slim close tx/rx wait timeout\n", __func__);
+			ret = -ETIMEDOUT;
+		} else {
+			ret = 0;
+		}
+	}
+	return ret;
+}
+
 static int taiko_codec_enable_slimrx(struct snd_soc_dapm_widget *w,
 				     struct snd_kcontrol *kcontrol,
 				     int event)
@@ -3614,7 +3668,7 @@
 	struct wcd9xxx *core;
 	struct snd_soc_codec *codec = w->codec;
 	struct taiko_priv *taiko_p = snd_soc_codec_get_drvdata(codec);
-	u32  ret = 0;
+	int ret = 0;
 	struct wcd9xxx_codec_dai_data *dai;
 
 	core = dev_get_drvdata(codec->dev->parent);
@@ -3633,6 +3687,7 @@
 
 	switch (event) {
 	case SND_SOC_DAPM_POST_PMU:
+		(void) taiko_codec_enable_slim_chmask(dai, true);
 		ret = wcd9xxx_cfg_slim_sch_rx(core, &dai->wcd9xxx_ch_list,
 					      dai->rate, dai->bit_width,
 					      &dai->grph);
@@ -3640,7 +3695,14 @@
 	case SND_SOC_DAPM_POST_PMD:
 		ret = wcd9xxx_close_slim_sch_rx(core, &dai->wcd9xxx_ch_list,
 						dai->grph);
-		usleep_range(15000, 15000);
+		ret = taiko_codec_enable_slim_chmask(dai, false);
+		if (ret < 0) {
+			ret = wcd9xxx_disconnect_port(core,
+						      &dai->wcd9xxx_ch_list,
+						      dai->grph);
+			pr_debug("%s: Disconnect RX port, ret = %d\n",
+				 __func__, ret);
+		}
 		break;
 	}
 	return ret;
@@ -3671,6 +3733,7 @@
 	dai = &taiko_p->dai[w->shift];
 	switch (event) {
 	case SND_SOC_DAPM_POST_PMU:
+		(void) taiko_codec_enable_slim_chmask(dai, true);
 		ret = wcd9xxx_cfg_slim_sch_tx(core, &dai->wcd9xxx_ch_list,
 					      dai->rate, dai->bit_width,
 					      &dai->grph);
@@ -3678,6 +3741,14 @@
 	case SND_SOC_DAPM_POST_PMD:
 		ret = wcd9xxx_close_slim_sch_tx(core, &dai->wcd9xxx_ch_list,
 						dai->grph);
+		ret = taiko_codec_enable_slim_chmask(dai, false);
+		if (ret < 0) {
+			ret = wcd9xxx_disconnect_port(core,
+						      &dai->wcd9xxx_ch_list,
+						      dai->grph);
+			pr_debug("%s: Disconnect RX port, ret = %d\n",
+				 __func__, ret);
+		}
 		break;
 	}
 	return ret;
@@ -3837,13 +3908,6 @@
 		0, taiko_codec_enable_interpolator, SND_SOC_DAPM_PRE_PMU |
 		SND_SOC_DAPM_POST_PMU),
 
-	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),
@@ -3891,6 +3955,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),
@@ -3904,6 +3973,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),
@@ -3920,10 +3992,13 @@
 	SND_SOC_DAPM_SUPPLY("LDO_H", TAIKO_A_LDO_H_MODE_1, 7, 0,
 		taiko_codec_enable_ldo_h, SND_SOC_DAPM_POST_PMU),
 
-	SND_SOC_DAPM_SUPPLY("COMP1_CLK", SND_SOC_NOPM, 0, 0,
+	SND_SOC_DAPM_SUPPLY("COMP0_CLK", SND_SOC_NOPM, 0, 0,
 		taiko_config_compander, SND_SOC_DAPM_PRE_PMU |
 		SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_POST_PMD),
-	SND_SOC_DAPM_SUPPLY("COMP2_CLK", SND_SOC_NOPM, 1, 0,
+	SND_SOC_DAPM_SUPPLY("COMP1_CLK", SND_SOC_NOPM, 1, 0,
+		taiko_config_compander, SND_SOC_DAPM_PRE_PMU |
+		SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_POST_PMD),
+	SND_SOC_DAPM_SUPPLY("COMP2_CLK", SND_SOC_NOPM, 2, 0,
 		taiko_config_compander, SND_SOC_DAPM_PRE_PMU |
 		SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_POST_PMD),
 
@@ -4154,34 +4229,69 @@
 
 };
 
-static unsigned long slimbus_value;
-
 static irqreturn_t taiko_slimbus_irq(int irq, void *data)
 {
 	struct taiko_priv *priv = data;
 	struct snd_soc_codec *codec = priv->codec;
-	int i, j;
+	unsigned long status = 0;
+	int i, j, port_id, k;
+	u32 bit;
 	u8 val;
+	bool tx, cleared;
 
-	for (i = 0; i < WCD9XXX_SLIM_NUM_PORT_REG; i++) {
-		slimbus_value = wcd9xxx_interface_reg_read(codec->control_data,
-			TAIKO_SLIM_PGD_PORT_INT_STATUS0 + i);
-		for_each_set_bit(j, &slimbus_value, BITS_PER_BYTE) {
-			val = wcd9xxx_interface_reg_read(codec->control_data,
-				TAIKO_SLIM_PGD_PORT_INT_SOURCE0 + i*8 + j);
-			if (val & 0x1)
-				pr_err_ratelimited(
-				"overflow error on port %x, value %x\n",
-				i*8 + j, val);
-			if (val & 0x2)
-				pr_err_ratelimited(
-				"underflow error on port %x, value %x\n",
-				i*8 + j, val);
+	for (i = TAIKO_SLIM_PGD_PORT_INT_STATUS_RX_0, j = 0;
+	     i <= TAIKO_SLIM_PGD_PORT_INT_STATUS_TX_1; i++, j++) {
+		val = wcd9xxx_interface_reg_read(codec->control_data, i);
+		status |= ((u32)val << (8 * j));
+	}
+
+	for_each_set_bit(j, &status, 32) {
+		tx = (j >= 16 ? true : false);
+		port_id = (tx ? j - 16 : j);
+		val = wcd9xxx_interface_reg_read(codec->control_data,
+					TAIKO_SLIM_PGD_PORT_INT_RX_SOURCE0 + j);
+		if (val & TAIKO_SLIM_IRQ_OVERFLOW)
+			pr_err_ratelimited(
+			    "%s: overflow error on %s port %d, value %x\n",
+			    __func__, (tx ? "TX" : "RX"), port_id, val);
+		if (val & TAIKO_SLIM_IRQ_UNDERFLOW)
+			pr_err_ratelimited(
+			    "%s: underflow error on %s port %d, value %x\n",
+			    __func__, (tx ? "TX" : "RX"), port_id, val);
+		if (val & TAIKO_SLIM_IRQ_PORT_CLOSED) {
+			/*
+			 * INT SOURCE register starts from RX to TX
+			 * but port number in the ch_mask is in opposite way
+			 */
+			bit = (tx ? j - 16 : j + 16);
+			pr_debug("%s: %s port %d closed value %x, bit %u\n",
+				 __func__, (tx ? "TX" : "RX"), port_id, val,
+				 bit);
+			for (k = 0, cleared = false; k < NUM_CODEC_DAIS; k++) {
+				pr_debug("%s: priv->dai[%d].ch_mask = 0x%lx\n",
+					 __func__, k, priv->dai[k].ch_mask);
+				if (test_and_clear_bit(bit,
+						       &priv->dai[k].ch_mask)) {
+					cleared = true;
+					if (!priv->dai[k].ch_mask)
+						wake_up(&priv->dai[k].dai_wait);
+					/*
+					 * There are cases when multiple DAIs
+					 * might be using the same slimbus
+					 * channel. Hence don't break here.
+					 */
+				}
+			}
+			WARN(!cleared,
+			     "Couldn't find slimbus %s port %d for closing\n",
+			     (tx ? "TX" : "RX"), port_id);
 		}
 		wcd9xxx_interface_reg_write(codec->control_data,
-			TAIKO_SLIM_PGD_PORT_INT_CLR0 + i, 0xFF);
-
+					    TAIKO_SLIM_PGD_PORT_INT_CLR_RX_0 +
+					    (j / 8),
+					    1 << (j % 8));
 	}
+
 	return IRQ_HANDLED;
 }
 
@@ -4387,6 +4497,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);
 
@@ -4464,6 +4588,19 @@
 	TAIKO_REG_VAL(TAIKO_A_RX_EAR_BIAS_PA, 0x76),
 	/* Reduce LINE DAC bias to 70% */
 	TAIKO_REG_VAL(TAIKO_A_RX_LINE_BIAS_PA, 0x78),
+
+	/*
+	 * There is a diode to pull down the micbias while doing
+	 * insertion detection.  This diode can cause leakage.
+	 * Set bit 0 to 1 to prevent leakage.
+	 * Setting this bit of micbias 2 prevents leakage for all other micbias.
+	 */
+	TAIKO_REG_VAL(TAIKO_A_MICB_2_MBHC, 0x41),
+
+	/* Disable TX7 internal biasing path which can cause leakage */
+	TAIKO_REG_VAL(TAIKO_A_TX_SUP_SWITCH_CTRL_1, 0xBF),
+	/* Enable MICB 4 VDDIO switch to prevent leakage */
+	TAIKO_REG_VAL(TAIKO_A_MICB_4_MBHC, 0x81),
 };
 
 static void taiko_update_reg_defaults(struct snd_soc_codec *codec)
@@ -4496,6 +4633,7 @@
 	{TAIKO_A_RX_LINE_2_GAIN, 0x20, 0x20},
 	{TAIKO_A_RX_LINE_3_GAIN, 0x20, 0x20},
 	{TAIKO_A_RX_LINE_4_GAIN, 0x20, 0x20},
+	{TAIKO_A_SPKR_DRV_GAIN, 0x04, 0x04},
 
 	/* CLASS H config */
 	{TAIKO_A_CDC_CONN_CLSH_CTL, 0x3C, 0x14},
@@ -4546,6 +4684,13 @@
 	{TAIKO_A_CDC_CLK_DMIC_B1_CTL, 0xEE, 0x22},
 	{TAIKO_A_CDC_CLK_DMIC_B2_CTL, 0x0E, 0x02},
 
+	/* Compander zone selection */
+	{TAIKO_A_CDC_COMP0_B4_CTL, 0x3F, 0x37},
+	{TAIKO_A_CDC_COMP1_B4_CTL, 0x3F, 0x37},
+	{TAIKO_A_CDC_COMP2_B4_CTL, 0x3F, 0x37},
+	{TAIKO_A_CDC_COMP0_B5_CTL, 0x7F, 0x7F},
+	{TAIKO_A_CDC_COMP1_B5_CTL, 0x7F, 0x7F},
+	{TAIKO_A_CDC_COMP2_B5_CTL, 0x7F, 0x7F},
 };
 
 static void taiko_codec_init_reg(struct snd_soc_codec *codec)
diff --git a/sound/soc/codecs/wcd9320.h b/sound/soc/codecs/wcd9320.h
index 7bc5a57..1fff80c 100644
--- a/sound/soc/codecs/wcd9320.h
+++ b/sound/soc/codecs/wcd9320.h
@@ -23,6 +23,7 @@
 #define TAIKO_CACHE_SIZE TAIKO_NUM_REGISTERS
 
 #define TAIKO_REG_VAL(reg, val)		{reg, 0, val}
+#define TAIKO_MCLK_ID 0
 
 extern const u8 taiko_reg_readable[TAIKO_CACHE_SIZE];
 extern const u8 taiko_reset_reg_defaults[TAIKO_CACHE_SIZE];
diff --git a/sound/soc/codecs/wcd9xxx-mbhc.c b/sound/soc/codecs/wcd9xxx-mbhc.c
index 3d7c0d4..0f2a19c 100644
--- a/sound/soc/codecs/wcd9xxx-mbhc.c
+++ b/sound/soc/codecs/wcd9xxx-mbhc.c
@@ -9,6 +9,7 @@
  * 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>
@@ -68,7 +69,7 @@
 #define FW_READ_ATTEMPTS 15
 #define FW_READ_TIMEOUT 2000000
 
-#define BUTTON_POLLING_SUPPORTED false
+#define BUTTON_POLLING_SUPPORTED true
 
 #define MCLK_RATE_12288KHZ 12288000
 #define MCLK_RATE_9600KHZ 9600000
@@ -564,9 +565,12 @@
 		return;
 	pr_debug("%s: Setting up %s detection\n", __func__,
 		 ins ? "insert" : "removal");
-	/* Enable interrupt and insertion detection */
+	/* Disable detection to avoid glitch */
+	snd_soc_update_bits(mbhc->codec, WCD9XXX_A_MBHC_INSERT_DETECT, 1, 0);
 	snd_soc_write(mbhc->codec, WCD9XXX_A_MBHC_INSERT_DETECT,
-		      (0x69 | (ins ? (1 << 1) : 0)));
+		      (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 */
@@ -835,8 +839,6 @@
 	cfilt_mode = snd_soc_read(codec, mbhc->mbhc_bias_regs.cfilt_ctl);
 	snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.cfilt_ctl, 0x70, 0x00);
 
-	snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x1F, 0x16);
-
 	snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x2, 0x2);
 	snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x84);
 
@@ -928,6 +930,23 @@
 	return abs(mic_volt - mic_volt_prev) > threshold;
 }
 
+static void wcd9xxx_onoff_vddio_switch(struct wcd9xxx_mbhc *mbhc, bool on)
+{
+	if (on) {
+		snd_soc_update_bits(mbhc->codec, mbhc->mbhc_bias_regs.mbhc_reg,
+				    1 << 7, 1 << 7);
+		snd_soc_update_bits(mbhc->codec, WCD9XXX_A_MAD_ANA_CTRL,
+				    1 << 4, 0);
+	} else {
+		snd_soc_update_bits(mbhc->codec, WCD9XXX_A_MAD_ANA_CTRL,
+				    1 << 4, 1 << 4);
+		snd_soc_update_bits(mbhc->codec, mbhc->mbhc_bias_regs.mbhc_reg,
+				    1 << 7, 0);
+	}
+	if (on)
+		usleep_range(10000, 10000);
+}
+
 /* 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)
@@ -978,8 +997,7 @@
 			scaled = mic_mv[i];
 		} else {
 			if (vddioswitch)
-				__wcd9xxx_switch_micbias(mbhc, 1,
-							     false, false);
+				wcd9xxx_onoff_vddio_switch(mbhc, true);
 			if (gndswitch)
 				wcd9xxx_codec_hphr_gnd_switch(codec, true);
 			mb_v[i] = __wcd9xxx_codec_sta_dce(mbhc, 1, true, true);
@@ -1002,8 +1020,7 @@
 			if (gndswitch)
 				wcd9xxx_codec_hphr_gnd_switch(codec, false);
 			if (vddioswitch)
-				__wcd9xxx_switch_micbias(mbhc, 0,
-							     false, false);
+				wcd9xxx_onoff_vddio_switch(mbhc, false);
 			/* claim UNSUPPORTED plug insertion when
 			 * good headset is detected but HPHR GND switch makes
 			 * delta difference */
@@ -1567,7 +1584,7 @@
 	vddio = (mbhc->mbhc_data.micb_mv != VDDIO_MICBIAS_MV &&
 		 mbhc->mbhc_micbias_switched);
 	if (vddio)
-		__wcd9xxx_switch_micbias(mbhc, 0, false, true);
+		wcd9xxx_onoff_vddio_switch(mbhc, true);
 
 	if (mbhc->mbhc_cfg->detect_extn_cable &&
 	    !wcd9xxx_swch_level_remove(mbhc))
@@ -1581,7 +1598,7 @@
 	 * 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_onoff_vddio_switch(mbhc, true);
 	WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
 
 	return IRQ_HANDLED;
@@ -2473,7 +2490,7 @@
 
 static void wcd9xxx_mbhc_cal(struct wcd9xxx_mbhc *mbhc)
 {
-	u8 cfilt_mode, bg_mode;
+	u8 cfilt_mode;
 	struct snd_soc_codec *codec = mbhc->codec;
 
 	pr_debug("%s: enter\n", __func__);
@@ -2491,8 +2508,6 @@
 	 */
 	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
@@ -2545,14 +2560,13 @@
 	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__);
 }
 
@@ -3021,7 +3035,7 @@
 		break;
 	/* PA usage change */
 	case WCD9XXX_EVENT_PRE_HPHL_PA_ON:
-		if (!(snd_soc_read(codec, mbhc->mbhc_bias_regs.ctl_reg & 0x80)))
+		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;
@@ -3177,10 +3191,6 @@
 		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);
@@ -3290,8 +3300,6 @@
 	wcd9xxx_resmgr_unregister_notifier(mbhc->resmgr, &mbhc->nblock);
 
 	wcd9xxx_cleanup_debugfs(mbhc);
-
-	kfree(mbhc->mbhc_cfg);
 }
 EXPORT_SYMBOL_GPL(wcd9xxx_mbhc_deinit);
 
diff --git a/sound/soc/codecs/wcd9xxx-resmgr.c b/sound/soc/codecs/wcd9xxx-resmgr.c
index 5dfa41c..3952dd5 100644
--- a/sound/soc/codecs/wcd9xxx-resmgr.c
+++ b/sound/soc/codecs/wcd9xxx-resmgr.c
@@ -111,7 +111,7 @@
 	blocking_notifier_call_chain(&resmgr->notifier, e, resmgr);
 }
 
-static void wcd9xxx_codec_disable_bg(struct wcd9xxx_resmgr *resmgr)
+static void wcd9xxx_disable_bg(struct wcd9xxx_resmgr *resmgr)
 {
 	/* Notify bg mode change */
 	wcd9xxx_resmgr_notifier_call(resmgr, WCD9XXX_EVENT_PRE_BG_OFF);
@@ -122,18 +122,28 @@
 	wcd9xxx_resmgr_notifier_call(resmgr, WCD9XXX_EVENT_POST_BG_OFF);
 }
 
-static void wcd9xxx_codec_enable_bg_audio(struct wcd9xxx_resmgr *resmgr)
+/*
+ * BG enablement should always enable in slow mode.
+ * The fast mode doesn't need to be enabled as fast mode BG is to be driven
+ * by MBHC override.
+ */
+static void wcd9xxx_enable_bg(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 */
+	/* Enable BG in slow mode and precharge */
 	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);
+}
+
+static void wcd9xxx_enable_bg_audio(struct wcd9xxx_resmgr *resmgr)
+{
+	/* Notify bandgap mode change */
+	wcd9xxx_resmgr_notifier_call(resmgr, WCD9XXX_EVENT_PRE_BG_AUDIO_ON);
+	wcd9xxx_enable_bg(resmgr);
 	/* Notify bandgap mode change */
 	wcd9xxx_resmgr_notifier_call(resmgr, WCD9XXX_EVENT_POST_BG_AUDIO_ON);
 }
@@ -146,28 +156,15 @@
 	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);
-
+	wcd9xxx_enable_bg(resmgr);
 	/* 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;
@@ -223,14 +220,14 @@
 			/* 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);
+			wcd9xxx_disable_bg(resmgr);
+			wcd9xxx_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);
+			wcd9xxx_enable_bg_audio(resmgr);
 		}
 		resmgr->bandgap_type = WCD9XXX_BANDGAP_AUDIO_MODE;
 		break;
diff --git a/sound/soc/msm/Kconfig b/sound/soc/msm/Kconfig
index 894e114..d5cada7 100644
--- a/sound/soc/msm/Kconfig
+++ b/sound/soc/msm/Kconfig
@@ -200,4 +200,15 @@
         default n
         help
          To add support for SoC audio on APQ8060 board
+
+config SND_SOC_MDM9625
+	tristate "SoC Machine driver for MDM9625 boards"
+	depends on ARCH_MSM9625
+	select SND_SOC_QDSP6V2
+	select SND_SOC_MSM_STUB
+	select SND_SOC_WCD9320
+	select SND_SOC_MSM_HOSTLESS_PCM
+	select SND_DYNAMIC_MINORS
+	help
+	 To add support for SoC audio on MDM9625 boards.
 endmenu
diff --git a/sound/soc/msm/Makefile b/sound/soc/msm/Makefile
index 99302eb..a4c365a 100644
--- a/sound/soc/msm/Makefile
+++ b/sound/soc/msm/Makefile
@@ -58,7 +58,7 @@
 
 snd-soc-qdsp6-objs := msm-dai-q6.o msm-pcm-q6.o msm-multi-ch-pcm-q6.o msm-lowlatency-pcm-q6.o msm-pcm-routing.o msm-dai-fe.o msm-compr-q6.o msm-dai-stub.o
 obj-$(CONFIG_SND_SOC_MSM_QDSP6_HDMI_AUDIO) += msm-dai-q6-hdmi.o
-obj-$(CONFIG_SND_SOC_VOICE) += msm-pcm-voice.o msm-pcm-voip.o
+obj-$(CONFIG_SND_SOC_VOICE) += msm-pcm-voice.o msm-pcm-voip.o msm-pcm-dtmf.o
 snd-soc-qdsp6-objs += msm-pcm-lpa.o msm-pcm-afe.o
 obj-$(CONFIG_SND_SOC_QDSP6) += snd-soc-qdsp6.o
 
@@ -84,3 +84,6 @@
 snd-soc-qdsp6v2-objs := msm-dai-fe.o msm-dai-stub.o
 obj-$(CONFIG_SND_SOC_QDSP6V2) += snd-soc-qdsp6v2.o
 
+#for MDM9625 sound card driver
+snd-soc-mdm9625-objs := mdm9625.o
+obj-$(CONFIG_SND_SOC_MDM9625) += snd-soc-mdm9625.o
diff --git a/sound/soc/msm/mdm9615.c b/sound/soc/msm/mdm9615.c
index 5a47efe..7190ae9 100644
--- a/sound/soc/msm/mdm9615.c
+++ b/sound/soc/msm/mdm9615.c
@@ -1024,6 +1024,9 @@
 	}
 	codec_clk = clk_get(cpu_dai->dev, "osr_clk");
 	err = tabla_hs_detect(codec, &mbhc_cfg);
+	msm_gpiomux_install(
+			msm9615_audio_prim_i2s_codec_configs,
+			ARRAY_SIZE(msm9615_audio_prim_i2s_codec_configs));
 	return err;
 }
 
@@ -1617,6 +1620,7 @@
 	codec_clk = clk_get(cpu_dai->dev, "osr_clk");
 
 	if (hs_detect_use_gpio) {
+		pr_debug("%s: GPIO Headset detection enabled\n", __func__);
 		mbhc_cfg.gpio = PM8018_GPIO_PM_TO_SYS(JACK_DETECT_GPIO);
 		mbhc_cfg.gpio_irq = JACK_DETECT_INT;
 	}
@@ -1702,6 +1706,19 @@
 
 	return 0;
 }
+
+static int mdm9615_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 mdm9615_aux_pcm_get_gpios(void)
 {
 	int ret = 0;
@@ -2014,6 +2031,30 @@
 		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
 		.ignore_suspend = 1,
 	},
+	{
+		.name = "DTMF RX Hostless",
+		.stream_name = "DTMF RX Hostless",
+		.cpu_dai_name	= "DTMF_RX_HOSTLESS",
+		.platform_name  = "msm-pcm-dtmf",
+		.dynamic = 1,
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			    SND_SOC_DPCM_TRIGGER_POST},
+		.ignore_suspend = 1,
+		.be_id = MSM_FRONTEND_DAI_DTMF_RX,
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+	},
+	{
+		.name = "DTMF TX",
+		.stream_name = "DTMF TX",
+		.cpu_dai_name = "msm-dai-stub",
+		.platform_name  = "msm-pcm-dtmf",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-tx",
+		.ignore_suspend = 1,
+	},
+
 	/* Backend BT DAI Links */
 	{
 		.name = LPASS_BE_INT_BT_SCO_RX,
@@ -2110,6 +2151,43 @@
 		.be_hw_params_fixup = mdm9615_auxpcm_be_params_fixup,
 		.ops = &mdm9615_sec_auxpcm_be_ops,
 	},
+	/* Incall Music BACK END DAI Link */
+	{
+		.name = LPASS_BE_VOICE_PLAYBACK_TX,
+		.stream_name = "Voice Farend Playback",
+		.cpu_dai_name = "msm-dai-q6.32773",
+		.platform_name = "msm-pcm-routing",
+		.codec_name     = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-rx",
+		.no_pcm = 1,
+		.be_id = MSM_BACKEND_DAI_VOICE_PLAYBACK_TX,
+		.be_hw_params_fixup = mdm9615_be_hw_params_fixup,
+	},
+	/* Incall Record Uplink BACK END DAI Link */
+	{
+		.name = LPASS_BE_INCALL_RECORD_TX,
+		.stream_name = "Voice Uplink Capture",
+		.cpu_dai_name = "msm-dai-q6.32772",
+		.platform_name = "msm-pcm-routing",
+		.codec_name     = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-tx",
+		.no_pcm = 1,
+		.be_id = MSM_BACKEND_DAI_INCALL_RECORD_TX,
+		.be_hw_params_fixup = mdm9615_be_hw_params_fixup,
+	},
+	/* Incall Record Downlink BACK END DAI Link */
+	{
+		.name = LPASS_BE_INCALL_RECORD_RX,
+		.stream_name = "Voice Downlink Capture",
+		.cpu_dai_name = "msm-dai-q6.32771",
+		.platform_name = "msm-pcm-routing",
+		.codec_name     = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-tx",
+		.no_pcm = 1,
+		.be_id = MSM_BACKEND_DAI_INCALL_RECORD_RX,
+		.be_hw_params_fixup = mdm9615_be_hw_params_fixup,
+		.ignore_pmdown_time = 1, /* this dailink has playback support */
+	},
 };
 
 static struct snd_soc_dai_link mdm9615_dai_i2s_tabla[] = {
@@ -2354,6 +2432,8 @@
 	sif_virt_addr = ioremap(LPASS_SIF_MUX_ADDR, 4);
 	secpcm_portslc_virt_addr = ioremap(SEC_PCM_PORT_SLC_ADDR, 4);
 
+	hs_detect_use_gpio = true;
+
 	return ret;
 }
 module_init(mdm9615_audio_init);
diff --git a/sound/soc/msm/mdm9625.c b/sound/soc/msm/mdm9625.c
new file mode 100644
index 0000000..b1822f6
--- /dev/null
+++ b/sound/soc/msm/mdm9625.c
@@ -0,0 +1,798 @@
+/* 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/clk.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/qpnp/clkdiv.h>
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/pcm.h>
+#include <sound/jack.h>
+#include <asm/mach-types.h>
+#include <mach/socinfo.h>
+#include <qdsp6v2/msm-pcm-routing-v2.h>
+#include "../codecs/wcd9320.h"
+
+/* MI2S GPIO SECTION */
+
+#define GPIO_MI2S_WS     12
+#define GPIO_MI2S_SCLK   15
+#define GPIO_MI2S_DOUT   14
+#define GPIO_MI2S_DIN    13
+#define GPIO_MI2S_MCLK   71
+
+/* Spk control */
+#define MDM9625_SPK_ON 1
+
+/* MDM9625 run Taiko at 12.288 Mhz.
+ * At present MDM supports 12.288mhz
+ * only. Taiko supports 9.6 MHz also.
+ */
+#define MDM_MCLK_CLK_12P288MHZ 12288000
+#define MDM_MCLK_CLK_9P6HZ 9600000
+#define MDM_IBIT_CLK_DIV_1P56MHZ 7
+
+/* Machine driver Name*/
+#define MDM9625_MACHINE_DRV_NAME "mdm9625-asoc-taiko"
+
+struct mdm9625_machine_data {
+	u32 mclk_freq;
+};
+
+/* MI2S clock */
+struct mdm_mi2s_clk {
+	struct clk *cdc_cr_clk;
+	struct clk *cdc_osr_clk;
+	struct clk *cdc_bit_clk;
+	bool clk_enable;
+
+};
+static struct mdm_mi2s_clk prim_clk;
+
+/* I2S GPIO */
+struct request_gpio {
+	unsigned gpio_no;
+	char *gpio_name;
+};
+static bool cdc_mclk_init;
+static struct mutex cdc_mclk_mutex;
+static int mdm9625_mi2s_rx_ch = 1;
+static int mdm9625_mi2s_tx_ch = 1;
+static int msm_spk_control;
+static atomic_t mi2s_ref_count;
+
+/* MI2S GPIO CONFIG */
+static struct request_gpio mi2s_gpio[] = {
+	{
+		.gpio_no = GPIO_MI2S_WS,
+		.gpio_name = "MI2S_WS",
+	},
+	{
+		.gpio_no = GPIO_MI2S_SCLK,
+		.gpio_name = "MI2S_SCLK",
+	},
+	{
+		.gpio_no = GPIO_MI2S_DOUT,
+		.gpio_name = "MI2S_DOUT",
+	},
+	{
+		.gpio_no = GPIO_MI2S_DIN,
+		.gpio_name = "MI2S_DIN",
+	},
+	{
+		.gpio_no = GPIO_MI2S_MCLK,
+		.gpio_name = "MI2S_MCLK",
+	},
+};
+
+static int mdm9625_enable_codec_ext_clk(struct snd_soc_codec *codec,
+					int enable, bool dapm);
+
+void *def_taiko_mbhc_cal(void);
+
+static struct wcd9xxx_mbhc_config mbhc_cfg = {
+	.read_fw_bin = false,
+	.calibration = NULL,
+	.micbias = MBHC_MICBIAS2,
+	.mclk_cb_fn = mdm9625_enable_codec_ext_clk,
+	.mclk_rate = MDM_MCLK_CLK_12P288MHZ,
+	.gpio = 0,
+	.gpio_irq = 0,
+	.gpio_level_insert = 1,
+	.detect_extn_cable = true,
+	.insert_detect = true,
+	.swap_gnd_mic = NULL,
+};
+
+#define WCD9XXX_MBHC_DEF_BUTTONS 8
+#define WCD9XXX_MBHC_DEF_RLOADS 5
+
+
+static bool gpio_enable;
+
+static int mdm9625_set_mi2s_gpio(void)
+{
+	int rtn = 0;
+	int i;
+	int j;
+
+	if (gpio_enable == false) {
+		for (i = 0; i < ARRAY_SIZE(mi2s_gpio); i++) {
+			rtn = gpio_request(mi2s_gpio[i].gpio_no,
+					   mi2s_gpio[i].gpio_name);
+			pr_debug("%s: gpio = %d, gpio name = %s\n"
+				 "rtn = %d\n", __func__,
+				 mi2s_gpio[i].gpio_no,
+				 mi2s_gpio[i].gpio_name,
+				 rtn);
+			if (rtn) {
+				pr_err("%s: Failed to request gpio %d\n",
+					__func__, mi2s_gpio[i].gpio_no);
+				/* Release all the GPIO on failure */
+				for (j = i; j >= 0; j--)
+					gpio_free(mi2s_gpio[j].gpio_no);
+				goto err;
+			}
+		}
+	gpio_enable = true;
+	}
+err:
+	return rtn;
+}
+
+static int mdm9625_mi2s_free_gpios(void)
+{
+	int i;
+	pr_debug("%s:", __func__);
+	for (i = 0; i < ARRAY_SIZE(mi2s_gpio); i++)
+		gpio_free(mi2s_gpio[i].gpio_no);
+	gpio_enable = false;
+	return 0;
+}
+static int mdm9625_mi2s_clk_ctl(struct snd_soc_pcm_runtime *rtd, bool enable)
+{
+	struct mdm_mi2s_clk *clk = &prim_clk;
+	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+	struct snd_soc_card *card = rtd->card;
+	struct mdm9625_machine_data *pdata = snd_soc_card_get_drvdata(card);
+	int ret = 0;
+
+	if (pdata == NULL) {
+		pr_err("%s:platform data is null\n", __func__);
+		return -ENODEV;
+	}
+
+	if (enable) {
+		if (clk->clk_enable == true) {
+			pr_info("%s:Device clock already enabled\n", __func__);
+			return 0;
+		}
+		/* Set up core clock. */
+		clk->cdc_cr_clk = clk_get(cpu_dai->dev, "core_clk");
+		if (IS_ERR(clk->cdc_cr_clk)) {
+			pr_err("%s: Failed to Core clk %ld\n"
+			       "CPU dai name %s\n", __func__,
+			       PTR_ERR(clk->cdc_cr_clk),
+			       cpu_dai->dev->driver->name);
+			return -ENODEV ;
+		}
+		/* osr clock */
+		clk->cdc_osr_clk = clk_get(cpu_dai->dev, "osr_clk");
+		if (IS_ERR(clk->cdc_osr_clk)) {
+			pr_err("%s: Failed to request OSR %ld\n"
+			       "CPU dai name %s\n", __func__,
+			       PTR_ERR(clk->cdc_osr_clk),
+			       cpu_dai->dev->driver->name);
+			clk_put(clk->cdc_cr_clk);
+			return -ENODEV ;
+		}
+		/* ibit clock */
+		clk->cdc_bit_clk = clk_get(cpu_dai->dev, "ibit_clk");
+		if (IS_ERR(clk->cdc_bit_clk)) {
+			pr_err("%s: Failed to request Bit %ld\n"
+			       "CPU dai name %s\n", __func__,
+			       PTR_ERR(clk->cdc_bit_clk),
+			       cpu_dai->dev->driver->name);
+			clk_put(clk->cdc_cr_clk);
+			clk_put(clk->cdc_osr_clk);
+			return -ENODEV ;
+		}
+		/* Set rate core and ibit clock */
+		clk_set_rate(clk->cdc_cr_clk, pdata->mclk_freq);
+		clk_set_rate(clk->cdc_bit_clk, MDM_IBIT_CLK_DIV_1P56MHZ);
+
+		/* Enable clocks. core clock need not be enabled.
+		 * Enabling branch clocks indirectly enables
+		 * core clock.
+		 */
+		ret = clk_prepare_enable(clk->cdc_osr_clk);
+		if (ret != 0) {
+			pr_err("Fail to enable cdc_osr_clk\n");
+			goto exit_osrclk_err;
+		}
+		ret = clk_prepare_enable(clk->cdc_bit_clk);
+		if (ret != 0) {
+			pr_err("Fail to enable cdc_bit_clk\n");
+			goto exit_bclk_err;
+		}
+		clk->clk_enable = true;
+		return ret;
+	} else {
+		clk->clk_enable = false;
+		ret = 0;
+		goto exit_bclk_err;
+	}
+exit_bclk_err:
+	clk_disable_unprepare(clk->cdc_bit_clk);
+	clk_put(clk->cdc_bit_clk);
+exit_osrclk_err:
+	clk_disable_unprepare(clk->cdc_osr_clk);
+	clk_put(clk->cdc_osr_clk);
+	clk_put(clk->cdc_cr_clk);
+	clk->cdc_cr_clk = NULL;
+	clk->cdc_bit_clk = NULL;
+	clk->cdc_osr_clk = NULL;
+	clk->clk_enable = false;
+	return ret;
+}
+
+static void mdm9625_mi2s_snd_shutdown(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	int ret;
+	if (atomic_dec_return(&mi2s_ref_count) == 0) {
+		mdm9625_mi2s_free_gpios();
+		ret = mdm9625_mi2s_clk_ctl(rtd, false);
+		if (ret < 0)
+			pr_err("%s:clock disable failed\n", __func__);
+	}
+}
+
+static int mdm9625_mi2s_startup(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+	struct snd_soc_dai *codec_dai = rtd->codec_dai;
+	int ret = 0;
+
+	if (atomic_inc_return(&mi2s_ref_count) == 1) {
+		mdm9625_set_mi2s_gpio();
+		ret = mdm9625_mi2s_clk_ctl(rtd, true);
+		if (ret < 0) {
+			pr_err("set format for codec dai failed\n");
+			return ret;
+		}
+	}
+	/* This sets the CONFIG PARAMETER WS_SRC.
+	 * 1 means internal clock master mode.
+	 * 0 means external clock slave mode.
+	 */
+	ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_CBS_CFS);
+	if (ret < 0)
+		pr_err("set fmt cpu dai failed\n");
+
+	ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_CBS_CFS);
+	if (ret < 0)
+		pr_err("set fmt for codec dai failed\n");
+
+	return ret;
+}
+
+static int set_codec_mclk(struct snd_soc_pcm_runtime *rtd)
+{
+	int ret = 0;
+	struct snd_soc_dai *codec_dai = rtd->codec_dai;
+	struct snd_soc_card *card = rtd->card;
+	struct mdm9625_machine_data *pdata = snd_soc_card_get_drvdata(card);
+
+	if (cdc_mclk_init == true)
+		return 0;
+	ret = snd_soc_dai_set_sysclk(codec_dai, TAIKO_MCLK_ID, pdata->mclk_freq,
+				     SND_SOC_CLOCK_IN);
+	if (ret < 0) {
+		pr_err("%s: Set codec sys clk failed %x", __func__, ret);
+		return ret;
+	}
+	cdc_mclk_init = true;
+	return 0;
+}
+
+static int mdm9625_mi2s_rx_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);
+	rate->min = rate->max = 48000;
+	channels->min = channels->max = mdm9625_mi2s_rx_ch;
+	set_codec_mclk(rtd);
+	return 0;
+}
+
+static int mdm9625_mi2s_tx_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);
+	rate->min = rate->max = 48000;
+	channels->min = channels->max = mdm9625_mi2s_tx_ch;
+	set_codec_mclk(rtd);
+	return 0;
+}
+
+
+static int mdm9625_mi2s_rx_ch_get(struct snd_kcontrol *kcontrol,
+				 struct snd_ctl_elem_value *ucontrol)
+{
+	pr_debug("%s: msm9615_i2s_rx_ch  = %d\n", __func__,
+			mdm9625_mi2s_rx_ch);
+	ucontrol->value.integer.value[0] = mdm9625_mi2s_rx_ch - 1;
+	return 0;
+}
+
+static int mdm9625_mi2s_rx_ch_put(struct snd_kcontrol *kcontrol,
+				 struct snd_ctl_elem_value *ucontrol)
+{
+	mdm9625_mi2s_rx_ch = ucontrol->value.integer.value[0] + 1;
+	pr_debug("%s: msm9615_i2s_rx_ch = %d\n", __func__,
+			mdm9625_mi2s_rx_ch);
+	return 1;
+}
+
+static int mdm9625_mi2s_tx_ch_get(struct snd_kcontrol *kcontrol,
+				 struct snd_ctl_elem_value *ucontrol)
+{
+	pr_debug("%s: msm9615_i2s_tx_ch  = %d\n", __func__,
+			mdm9625_mi2s_tx_ch);
+	ucontrol->value.integer.value[0] = mdm9625_mi2s_tx_ch - 1;
+	return 0;
+}
+
+static int mdm9625_mi2s_tx_ch_put(struct snd_kcontrol *kcontrol,
+				 struct snd_ctl_elem_value *ucontrol)
+{
+	mdm9625_mi2s_tx_ch = ucontrol->value.integer.value[0] + 1;
+	pr_debug("%s: msm9615_i2s_tx_ch = %d\n", __func__,
+			mdm9625_mi2s_tx_ch);
+	return 1;
+}
+
+
+static int mdm9625_mi2s_get_spk(struct snd_kcontrol *kcontrol,
+		       struct snd_ctl_elem_value *ucontrol)
+{
+	pr_debug("%s: msm_spk_control = %d", __func__, msm_spk_control);
+	ucontrol->value.integer.value[0] = msm_spk_control;
+	return 0;
+}
+
+static void mdm_ext_control(struct snd_soc_codec *codec)
+{
+	struct snd_soc_dapm_context *dapm = &codec->dapm;
+	pr_debug("%s: msm_spk_control = %d", __func__, msm_spk_control);
+	mutex_lock(&dapm->codec->mutex);
+	if (msm_spk_control == MDM9625_SPK_ON) {
+		snd_soc_dapm_enable_pin(dapm, "Ext Spk Bottom Pos");
+		snd_soc_dapm_enable_pin(dapm, "Ext Spk Bottom Neg");
+		snd_soc_dapm_enable_pin(dapm, "Ext Spk Top Pos");
+		snd_soc_dapm_enable_pin(dapm, "Ext Spk Top Neg");
+	} else {
+		snd_soc_dapm_disable_pin(dapm, "Ext Spk Bottom Pos");
+		snd_soc_dapm_disable_pin(dapm, "Ext Spk Bottom Neg");
+		snd_soc_dapm_disable_pin(dapm, "Ext Spk Top Pos");
+		snd_soc_dapm_disable_pin(dapm, "Ext Spk Top Neg");
+	}
+	snd_soc_dapm_sync(dapm);
+	mutex_unlock(&dapm->codec->mutex);
+}
+
+static int mdm9625_mi2s_set_spk(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+	pr_debug("%s()\n", __func__);
+	if (msm_spk_control == ucontrol->value.integer.value[0])
+		return 0;
+	msm_spk_control = ucontrol->value.integer.value[0];
+	mdm_ext_control(codec);
+	return 1;
+}
+
+static int mdm9625_enable_codec_ext_clk(struct snd_soc_codec *codec,
+					int enable, bool dapm)
+{
+	int ret = 0;
+	pr_debug("%s: enable = %d  codec name %s\n", __func__,
+		enable, codec->name);
+	mutex_lock(&cdc_mclk_mutex);
+	if (enable)
+		taiko_mclk_enable(codec, 1, dapm);
+	else
+		taiko_mclk_enable(codec, 0, dapm);
+	mutex_unlock(&cdc_mclk_mutex);
+	return ret;
+}
+
+static int mdm9625_mclk_event(struct snd_soc_dapm_widget *w,
+			      struct snd_kcontrol *kcontrol, int event)
+{
+	pr_debug("%s: event = %d\n", __func__, event);
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		return mdm9625_enable_codec_ext_clk(w->codec, 1, true);
+	case SND_SOC_DAPM_POST_PMD:
+		return mdm9625_enable_codec_ext_clk(w->codec, 0, true);
+	}
+	return 0;
+}
+
+
+static const struct snd_soc_dapm_widget mdm9625_dapm_widgets[] = {
+
+	SND_SOC_DAPM_SUPPLY("MCLK",  SND_SOC_NOPM, 0, 0,
+	mdm9625_mclk_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_SPK("Ext Spk Bottom Pos", NULL),
+	SND_SOC_DAPM_SPK("Ext Spk Bottom Neg", NULL),
+	SND_SOC_DAPM_SPK("Ext Spk Top Pos", NULL),
+	SND_SOC_DAPM_SPK("Ext Spk Top Neg", NULL),
+	SND_SOC_DAPM_MIC("Handset Mic", NULL),
+	SND_SOC_DAPM_MIC("Headset Mic", NULL),
+	SND_SOC_DAPM_MIC("ANCRight Headset Mic", NULL),
+	SND_SOC_DAPM_MIC("ANCLeft Headset Mic", NULL),
+	SND_SOC_DAPM_MIC("Digital Mic1", NULL),
+	SND_SOC_DAPM_MIC("Digital Mic2", NULL),
+	SND_SOC_DAPM_MIC("Digital Mic3", NULL),
+	SND_SOC_DAPM_MIC("Digital Mic4", NULL),
+	SND_SOC_DAPM_MIC("Digital Mic5", NULL),
+	SND_SOC_DAPM_MIC("Digital Mic6", NULL),
+};
+
+static const char *const spk_function[] = {"Off", "On"};
+static const char *const mi2s_rx_ch_text[] = {"One", "Two"};
+static const char *const mi2s_tx_ch_text[] = {"One", "Two"};
+
+static const struct soc_enum mdm9625_enum[] = {
+	SOC_ENUM_SINGLE_EXT(2, spk_function),
+	SOC_ENUM_SINGLE_EXT(2, mi2s_rx_ch_text),
+	SOC_ENUM_SINGLE_EXT(2, mi2s_tx_ch_text),
+};
+
+static const struct snd_kcontrol_new mdm_snd_controls[] = {
+	SOC_ENUM_EXT("Speaker Function", mdm9625_enum[0],
+				 mdm9625_mi2s_get_spk,
+				 mdm9625_mi2s_set_spk),
+	SOC_ENUM_EXT("MI2S_RX Channels", mdm9625_enum[1],
+				 mdm9625_mi2s_rx_ch_get,
+				 mdm9625_mi2s_rx_ch_put),
+	SOC_ENUM_EXT("MI2S_TX Channels", mdm9625_enum[2],
+				 mdm9625_mi2s_tx_ch_get,
+				 mdm9625_mi2s_tx_ch_put),
+};
+
+static int mdm9625_mi2s_audrx_init(struct snd_soc_pcm_runtime *rtd)
+{
+	int err;
+	struct snd_soc_codec *codec = rtd->codec;
+	struct snd_soc_dapm_context *dapm = &codec->dapm;
+	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+	pr_info("%s(), dev_name%s\n", __func__, dev_name(cpu_dai->dev));
+
+	rtd->pmdown_time = 0;
+	err = snd_soc_add_codec_controls(codec, mdm_snd_controls,
+					 ARRAY_SIZE(mdm_snd_controls));
+	if (err < 0)
+		return err;
+
+	snd_soc_dapm_new_controls(dapm, mdm9625_dapm_widgets,
+				  ARRAY_SIZE(mdm9625_dapm_widgets));
+
+	/* After DAPM Enable pins alawys
+	 * DAPM SYNC needs to be called.
+	 */
+	snd_soc_dapm_enable_pin(dapm, "Ext Spk Bottom Pos");
+	snd_soc_dapm_enable_pin(dapm, "Ext Spk Bottom Neg");
+	snd_soc_dapm_enable_pin(dapm, "Ext Spk Top Pos");
+	snd_soc_dapm_enable_pin(dapm, "Ext Spk Top Neg");
+	snd_soc_dapm_sync(dapm);
+
+	/* start mbhc */
+	 mdm9625_set_mi2s_gpio();
+	mdm9625_mi2s_clk_ctl(rtd, true);
+	mbhc_cfg.calibration = def_taiko_mbhc_cal();
+	if (mbhc_cfg.calibration)
+		err = taiko_hs_detect(codec, &mbhc_cfg);
+	else
+		err = -ENOMEM;
+	return err;
+}
+
+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] = 10;
+	btn_low[1] = 11;
+	btn_high[1] = 52;
+	btn_low[2] = 53;
+	btn_high[2] = 94;
+	btn_low[3] = 95;
+	btn_high[3] = 133;
+	btn_low[4] = 134;
+	btn_high[4] = 171;
+	btn_low[5] = 172;
+	btn_high[5] = 208;
+	btn_low[6] = 209;
+	btn_high[6] = 244;
+	btn_low[7] = 245;
+	btn_high[7] = 330;
+	n_ready = wcd9xxx_mbhc_cal_btn_det_mp(btn_cfg, MBHC_BTN_DET_N_READY);
+	n_ready[0] = 80;
+	n_ready[1] = 68;
+	n_cic = wcd9xxx_mbhc_cal_btn_det_mp(btn_cfg, MBHC_BTN_DET_N_CIC);
+	n_cic[0] = 60;
+	n_cic[1] = 47;
+	gain = wcd9xxx_mbhc_cal_btn_det_mp(btn_cfg, MBHC_BTN_DET_GAIN);
+	gain[0] = 11;
+	gain[1] = 9;
+
+	return taiko_cal;
+}
+
+
+static struct snd_soc_ops mdm9625_mi2s_be_ops = {
+	.startup = mdm9625_mi2s_startup,
+	.shutdown = mdm9625_mi2s_snd_shutdown,
+};
+
+/* Digital audio interface connects codec <---> CPU */
+static struct snd_soc_dai_link mdm9625_dai[] = {
+	/* FrontEnd DAI Links */
+	{
+		.name = "MDM9625 Media1",
+		.stream_name = "MultiMedia1",
+		.cpu_dai_name = "MultiMedia1",
+		.platform_name  = "msm-pcm-dsp",
+		.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,
+		/* This dainlink has playback support */
+		.ignore_pmdown_time = 1,
+		.be_id = MSM_FRONTEND_DAI_MULTIMEDIA1
+	},
+	{
+		.name = "MSM VoIP",
+		.stream_name = "VoIP",
+		.cpu_dai_name = "VoIP",
+		.platform_name  = "msm-voip-dsp",
+		.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,
+		/* This dainlink has VOIP support */
+		.ignore_pmdown_time = 1,
+		.be_id = MSM_FRONTEND_DAI_VOIP,
+	},
+	{
+		.name = "Circuit-Switch Voice",
+		.stream_name = "CS-Voice",
+		.cpu_dai_name   = "CS-VOICE",
+		.platform_name  = "msm-pcm-voice",
+		.dynamic = 1,
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			    SND_SOC_DPCM_TRIGGER_POST},
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ignore_suspend = 1,
+		/* This dainlink has Voice support */
+		.ignore_pmdown_time = 1,
+		.be_id = MSM_FRONTEND_DAI_CS_VOICE,
+	},
+	{
+		.name = "MI2S Hostless",
+		.stream_name = "MI2S Hostless",
+		.cpu_dai_name = "MI2S_TX_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,
+		 /* This dainlink has MI2S support */
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+	},
+	/* Backend DAI Links */
+	{
+		.name = LPASS_BE_MI2S_RX,
+		.stream_name = "MI2S Playback",
+		.cpu_dai_name = "msm-dai-q6-mi2s.0",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "taiko_codec",
+		.codec_dai_name = "taiko_i2s_rx1",
+		.no_pcm = 1,
+		.be_id = MSM_BACKEND_DAI_MI2S_RX,
+		.init  = &mdm9625_mi2s_audrx_init,
+		.be_hw_params_fixup = &mdm9625_mi2s_rx_be_hw_params_fixup,
+		.ops = &mdm9625_mi2s_be_ops,
+	},
+	{
+		.name = LPASS_BE_MI2S_TX,
+		.stream_name = "MI2S Capture",
+		.cpu_dai_name = "msm-dai-q6-mi2s.0",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "taiko_codec",
+		.codec_dai_name = "taiko_i2s_tx1",
+		.no_pcm = 1,
+		.be_id = MSM_BACKEND_DAI_MI2S_TX,
+		.be_hw_params_fixup = &mdm9625_mi2s_tx_be_hw_params_fixup,
+		.ops = &mdm9625_mi2s_be_ops,
+	},
+};
+
+static struct snd_soc_card snd_soc_card_mdm9625 = {
+	.name = "mdm9625-taiko-i2s-snd-card",
+	.dai_link = mdm9625_dai,
+	.num_links = ARRAY_SIZE(mdm9625_dai),
+};
+
+static __devinit int mdm9625_asoc_machine_probe(struct platform_device *pdev)
+{
+	int ret;
+	struct snd_soc_card *card = &snd_soc_card_mdm9625;
+	struct mdm9625_machine_data *pdata;
+
+	mutex_init(&cdc_mclk_mutex);
+	gpio_enable = false;
+	cdc_mclk_init = false;
+	if (!pdev->dev.of_node) {
+		dev_err(&pdev->dev, "No platform supplied from device tree\n");
+		return -EINVAL;
+	}
+	pdata = devm_kzalloc(&pdev->dev, sizeof(struct mdm9625_machine_data),
+			     GFP_KERNEL);
+	if (!pdata) {
+		dev_err(&pdev->dev, "Can't allocate msm8974_asoc_mach_data\n");
+		ret = -ENOMEM;
+		goto err;
+	}
+	card->dev = &pdev->dev;
+	platform_set_drvdata(pdev, card);
+	snd_soc_card_set_drvdata(card, pdata);
+	ret = snd_soc_of_parse_card_name(card, "qcom,model");
+	if (ret)
+		goto err;
+	ret = snd_soc_of_parse_audio_routing(card, "qcom,audio-routing");
+	if (ret)
+		goto err;
+	ret = of_property_read_u32(pdev->dev.of_node,
+				   "qcom,taiko-mclk-clk-freq",
+				   &pdata->mclk_freq);
+	if (ret) {
+		dev_err(&pdev->dev,
+			"Looking up %s property in node %s failed",
+			"qcom,taiko-mclk-clk-freq",
+			pdev->dev.of_node->full_name);
+		goto err;
+	}
+	/* At present only 12.288MHz is supported on MDM. */
+	if (pdata->mclk_freq != MDM_MCLK_CLK_12P288MHZ) {
+		dev_err(&pdev->dev, "unsupported taiko mclk freq %u\n",
+			pdata->mclk_freq);
+		ret = -EINVAL;
+		goto err;
+	}
+	ret = snd_soc_register_card(card);
+	if (ret) {
+		dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n",
+				ret);
+		goto err;
+	}
+	return 0;
+err:
+	devm_kfree(&pdev->dev, pdata);
+	return ret;
+}
+
+static int __devexit mdm9625_asoc_machine_remove(struct platform_device *pdev)
+{
+	struct snd_soc_card *card = platform_get_drvdata(pdev);
+	struct mdm9625_machine_data *pdata = snd_soc_card_get_drvdata(card);
+	pdata->mclk_freq = 0;
+	snd_soc_unregister_card(card);
+	return 0;
+}
+
+static const struct of_device_id msm9625_asoc_machine_of_match[]  = {
+	{ .compatible = "qcom,mdm9625-audio-taiko", },
+	{},
+};
+
+static struct platform_driver msm9625_asoc_machine_driver = {
+	.driver = {
+		.name = MDM9625_MACHINE_DRV_NAME,
+		.owner = THIS_MODULE,
+		.pm = &snd_soc_pm_ops,
+		.of_match_table = msm9625_asoc_machine_of_match,
+	},
+	.probe = mdm9625_asoc_machine_probe,
+	.remove = __devexit_p(mdm9625_asoc_machine_remove),
+};
+
+
+module_platform_driver(msm9625_asoc_machine_driver);
+
+MODULE_DESCRIPTION("ALSA SoC msm");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" MDM9625_MACHINE_DRV_NAME);
+MODULE_DEVICE_TABLE(of, msm9625_asoc_machine_of_match);
+
diff --git a/sound/soc/msm/mpq8064.c b/sound/soc/msm/mpq8064.c
index 7656f9f..d0bfb76 100644
--- a/sound/soc/msm/mpq8064.c
+++ b/sound/soc/msm/mpq8064.c
@@ -144,8 +144,6 @@
 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;
 
@@ -847,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)
 {
@@ -912,7 +925,6 @@
 		clk_put(mi2s_bit_clk);
 		mi2s_bit_clk = NULL;
 	}
-	msm_mi2s_free_gpios();
 }
 
 static int configure_mi2s_gpio(void)
@@ -945,7 +957,6 @@
 	int ret = 0;
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
-	configure_mi2s_gpio();
 	mi2s_bit_clk = clk_get(cpu_dai->dev, "bit_clk");
 	if (IS_ERR(mi2s_bit_clk))
 		return PTR_ERR(mi2s_bit_clk);
@@ -1125,7 +1136,6 @@
 			clk_put(sec_i2s_rx_osr_clk);
 			sec_i2s_rx_osr_clk = NULL;
 		}
-		mpq8064_sec_i2s_rx_free_gpios();
 	}
 	pr_info("%s(): substream = %s  stream = %d\n", __func__,
 		 substream->name, substream->stream);
@@ -1164,7 +1174,6 @@
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
-		configure_sec_i2s_rx_gpio();
 		sec_i2s_rx_osr_clk = clk_get(cpu_dai->dev, "osr_clk");
 		if (IS_ERR(sec_i2s_rx_osr_clk)) {
 			pr_err("Failed to get sec_i2s_rx_osr_clk\n");
@@ -1473,6 +1482,20 @@
 		.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 */
 	{
@@ -1572,6 +1595,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 */
 	},
 	{
@@ -1582,6 +1606,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 */
@@ -1609,6 +1634,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,
+	},
 };
 
 
@@ -1622,57 +1659,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;
@@ -1705,13 +1691,8 @@
 		kfree(mbhc_cfg.calibration);
 		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;
-
+	configure_sec_i2s_rx_gpio();
+	configure_mi2s_gpio();
 	return ret;
 
 }
@@ -1723,7 +1704,8 @@
 		pr_err("%s: Not the right machine type\n", __func__);
 		return ;
 	}
-	msm_free_headset_mic_gpios();
+	mpq8064_sec_i2s_rx_free_gpios();
+	msm_mi2s_free_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 8202982..6ac4562 100644
--- a/sound/soc/msm/msm-compr-q6.c
+++ b/sound/soc/msm/msm-compr-q6.c
@@ -143,6 +143,10 @@
 			atomic_set(&prtd->pending_buffer, 0);
 		if (runtime->status->hw_ptr >= runtime->control->appl_ptr) {
 			runtime->render_flag |= SNDRV_RENDER_STOPPED;
+			atomic_set(&prtd->pending_buffer, 1);
+			pr_debug("%s:compr driver underrun hw_ptr = %ld appl_ptr = %ld\n",
+				__func__, runtime->status->hw_ptr,
+				runtime->control->appl_ptr);
 			break;
 		}
 		buf = prtd->audio_client->port[IN].buf;
@@ -382,6 +386,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);
@@ -473,7 +478,23 @@
 	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;
 
@@ -649,6 +670,8 @@
 	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:
@@ -659,6 +682,8 @@
 	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;
@@ -675,7 +700,7 @@
 {
 	pr_debug("%s\n", __func__);
 	/* MP3 Block */
-	compr->info.compr_cap.num_codecs = 13;
+	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;
@@ -693,6 +718,7 @@
 	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*/
 }
 
@@ -809,21 +835,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;
@@ -840,6 +875,7 @@
 	pr_debug("%s\n", __func__);
 	atomic_set(&prtd->pending_buffer, 0);
 	q6asm_cmd(prtd->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 ==
@@ -941,6 +977,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);
 
@@ -964,6 +1001,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) {
@@ -1113,6 +1173,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 */
@@ -1139,10 +1215,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,
@@ -1158,7 +1238,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,
diff --git a/sound/soc/msm/msm-dai-fe.c b/sound/soc/msm/msm-dai-fe.c
index dc8d9e6..4165254 100644
--- a/sound/soc/msm/msm-dai-fe.c
+++ b/sound/soc/msm/msm-dai-fe.c
@@ -289,6 +289,78 @@
 	},
 	{
 		.playback = {
+			.stream_name = "SLIMBUS1 Hostless Playback",
+			.aif_name = "SLIM1_DL_HL",
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+			.channels_min = 1,
+			.channels_max = 2,
+			.rate_min =     8000,
+			.rate_max =     48000,
+		},
+		.capture = {
+			.stream_name = "SLIMBUS1 Hostless Capture",
+			.aif_name = "SLIM1_UL_HL",
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+			.channels_min = 1,
+			.channels_max = 2,
+			.rate_min =     8000,
+			.rate_max =     48000,
+		},
+		.ops = &msm_fe_dai_ops,
+		.name = "SLIMBUS1_HOSTLESS",
+	},
+	{
+		.playback = {
+			.stream_name = "SLIMBUS3 Hostless Playback",
+			.aif_name = "SLIM3_DL_HL",
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+			.channels_min = 1,
+			.channels_max = 2,
+			.rate_min =     8000,
+			.rate_max =     48000,
+		},
+		.capture = {
+			.stream_name = "SLIMBUS3 Hostless Capture",
+			.aif_name = "SLIM3_UL_HL",
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+			.channels_min = 1,
+			.channels_max = 2,
+			.rate_min =     8000,
+			.rate_max =     48000,
+		},
+		.ops = &msm_fe_dai_ops,
+		.name = "SLIMBUS3_HOSTLESS",
+	},
+	{
+		.playback = {
+			.stream_name = "SLIMBUS4 Hostless Playback",
+			.aif_name = "SLIM4_DL_HL",
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+			.channels_min = 1,
+			.channels_max = 2,
+			.rate_min =     8000,
+			.rate_max =     48000,
+		},
+		.capture = {
+			.stream_name = "SLIMBUS4 Hostless Capture",
+			.aif_name = "SLIM4_UL_HL",
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+			.channels_min = 1,
+			.channels_max = 2,
+			.rate_min =     8000,
+			.rate_max =     48000,
+		},
+		.ops = &msm_fe_dai_ops,
+		.name = "SLIMBUS4_HOSTLESS",
+	},
+	{
+		.playback = {
 			.stream_name = "INT_FM Hostless Playback",
 			.aif_name = "INTFM_DL_HL",
 			.rates = SNDRV_PCM_RATE_8000_48000,
@@ -426,15 +498,25 @@
 		.name = "VoLTE",
 	},
 	{
+		.playback = {
+			.stream_name = "MI2S_RX_HOSTLESS Playback",
+			.aif_name = "MI2S_DL_HL",
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+			.channels_min = 1,
+			.channels_max = 2,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
 		.capture = {
 			.stream_name = "MI2S_TX Hostless Capture",
 			.aif_name = "MI2S_UL_HL",
 			.rates = SNDRV_PCM_RATE_8000_48000,
 			.formats = SNDRV_PCM_FMTBIT_S16_LE,
 			.channels_min = 1,
-			.channels_max = 8,
-			.rate_min =     8000,
-			.rate_max =    48000,
+			.channels_max = 2,
+			.rate_min = 8000,
+			.rate_max = 48000,
 		},
 		.ops = &msm_fe_dai_ops,
 		.name = "MI2S_TX_HOSTLESS",
@@ -477,6 +559,46 @@
 		.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",
+	},
+	{
+		.playback = {
+			.stream_name = "DTMF_RX Hostless Playback",
+			.aif_name = "DTMF_DL_HL",
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+			.channels_min = 1,
+			.channels_max = 2,
+			.rate_min =	8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_fe_dai_ops,
+		.name = "DTMF_RX_HOSTLESS",
+	},
 };
 
 static __devinit int msm_fe_dai_dev_probe(struct platform_device *pdev)
diff --git a/sound/soc/msm/msm-dai-q6.c b/sound/soc/msm/msm-dai-q6.c
index 18c2329..8cc0eaa 100644
--- a/sound/soc/msm/msm-dai-q6.c
+++ b/sound/soc/msm/msm-dai-q6.c
@@ -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
@@ -703,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:
@@ -1819,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)
@@ -1915,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 fcfcb66..98c28aa 100644
--- a/sound/soc/msm/msm-lowlatency-pcm-q6.c
+++ b/sound/soc/msm/msm-lowlatency-pcm-q6.c
@@ -224,14 +224,14 @@
 			prtd->channel_map[0] = PCM_CHANNEL_FL;
 		} else if (prtd->channel_mode == 2) {
 			prtd->channel_map[0] = PCM_CHANNEL_FL;
-			prtd->channel_map[0] = PCM_CHANNEL_FR;
+			prtd->channel_map[1] = PCM_CHANNEL_FR;
 		} else if (prtd->channel_mode == 6) {
 			prtd->channel_map[0] = PCM_CHANNEL_FC;
-			prtd->channel_map[0] = PCM_CHANNEL_FL;
-			prtd->channel_map[0] = PCM_CHANNEL_FR;
-			prtd->channel_map[0] = PCM_CHANNEL_LB;
-			prtd->channel_map[0] = PCM_CHANNEL_RB;
-			prtd->channel_map[0] = PCM_CHANNEL_LFE;
+			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);
diff --git a/sound/soc/msm/msm-multi-ch-pcm-q6.c b/sound/soc/msm/msm-multi-ch-pcm-q6.c
index 7d04f95..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;
 		}
@@ -276,8 +281,8 @@
 		if (prtd->channel_mode == 1) {
 			prtd->channel_map[0] = PCM_CHANNEL_FL;
 		} else if (prtd->channel_mode == 2) {
-			prtd->channel_map[1] = PCM_CHANNEL_FL;
-			prtd->channel_map[2] = PCM_CHANNEL_FR;
+			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;
@@ -513,16 +518,6 @@
 	return rc;
 }
 
-void multi_ch_pcm_set_channel_map(char *channel_mapping)
-{
-	pr_debug("%s\n", __func__);
-	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);
-	}
-}
-
 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)
 {
@@ -810,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;
 }
 
@@ -866,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-dtmf.c b/sound/soc/msm/msm-pcm-dtmf.c
new file mode 100644
index 0000000..94cc1ca
--- /dev/null
+++ b/sound/soc/msm/msm-pcm-dtmf.c
@@ -0,0 +1,585 @@
+/* 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/time.h>
+#include <linux/wait.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/pcm.h>
+#include <sound/q6afe.h>
+
+#include "msm-pcm-q6.h"
+#include "msm-pcm-routing.h"
+#include "qdsp6/q6voice.h"
+
+enum {
+	DTMF_IN_RX,
+	DTMF_IN_TX,
+};
+
+enum format {
+	FORMAT_S16_LE = 2
+};
+
+struct dtmf_det_info {
+	char     session[MAX_SESSION_NAME_LEN];
+	uint8_t  dir;
+	uint16_t high_freq;
+	uint16_t low_freq;
+};
+
+struct dtmf_buf_node {
+	struct list_head list;
+	struct dtmf_det_info dtmf_det_pkt;
+};
+
+enum dtmf_state {
+	DTMF_GEN_RX_STOPPED,
+	DTMF_GEN_RX_STARTED,
+};
+
+#define DTMF_MAX_Q_LEN 10
+#define DTMF_PKT_SIZE sizeof(struct dtmf_det_info)
+
+struct dtmf_drv_info {
+	enum  dtmf_state state;
+	struct snd_pcm_substream *capture_substream;
+
+	struct list_head out_queue;
+	struct list_head free_out_queue;
+
+	wait_queue_head_t out_wait;
+
+	struct mutex lock;
+	spinlock_t dsp_lock;
+
+	uint8_t capture_start;
+	uint8_t capture_instance;
+
+	unsigned int pcm_capture_size;
+	unsigned int pcm_capture_count;
+	unsigned int pcm_capture_irq_pos;
+	unsigned int pcm_capture_buf_pos;
+};
+
+static struct snd_pcm_hardware msm_pcm_hardware = {
+	.info =                 (SNDRV_PCM_INFO_MMAP |
+				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
+				 SNDRV_PCM_INFO_MMAP_VALID |
+				 SNDRV_PCM_INFO_INTERLEAVED),
+	.formats =              SNDRV_PCM_FMTBIT_S16_LE,
+	.channels_min =         1,
+	.channels_max =         1,
+	.buffer_bytes_max =	(sizeof(struct dtmf_buf_node) * DTMF_MAX_Q_LEN),
+	.period_bytes_min =	DTMF_PKT_SIZE,
+	.period_bytes_max =	DTMF_PKT_SIZE,
+	.periods_min =		DTMF_MAX_Q_LEN,
+	.periods_max =		DTMF_MAX_Q_LEN,
+	.fifo_size =            0,
+};
+
+static int msm_dtmf_rx_generate_put(struct snd_kcontrol *kcontrol,
+				    struct snd_ctl_elem_value *ucontrol)
+{
+	uint16_t low_freq = ucontrol->value.integer.value[0];
+	uint16_t high_freq = ucontrol->value.integer.value[1];
+	int64_t duration = ucontrol->value.integer.value[2];
+	uint16_t gain = ucontrol->value.integer.value[3];
+
+	pr_debug("%s: low_freq=%d high_freq=%d duration=%d gain=%d\n",
+		 __func__, low_freq, high_freq, (int)duration, gain);
+	afe_dtmf_generate_rx(duration, high_freq, low_freq, gain);
+	return 0;
+}
+
+static int msm_dtmf_rx_generate_get(struct  snd_kcontrol *kcontrol,
+				    struct snd_ctl_elem_value *ucontrol)
+{
+	pr_debug("%s:\n", __func__);
+	ucontrol->value.integer.value[0] = 0;
+	return 0;
+}
+
+static int msm_dtmf_detect_voice_rx_put(struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	int enable = ucontrol->value.integer.value[0];
+
+	pr_debug("%s: enable=%d\n", __func__, enable);
+	voc_enable_dtmf_rx_detection(voc_get_session_id(VOICE_SESSION_NAME),
+				     enable);
+
+	return 0;
+}
+
+static int msm_dtmf_detect_voice_rx_get(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	ucontrol->value.integer.value[0] = 0;
+	return 0;
+}
+
+static int msm_dtmf_detect_volte_rx_put(struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	int enable = ucontrol->value.integer.value[0];
+
+	pr_debug("%s: enable=%d\n", __func__, enable);
+	voc_enable_dtmf_rx_detection(voc_get_session_id(VOLTE_SESSION_NAME),
+				     enable);
+
+	return 0;
+}
+
+static int msm_dtmf_detect_volte_rx_get(struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	ucontrol->value.integer.value[0] = 0;
+	return 0;
+}
+
+static struct snd_kcontrol_new msm_dtmf_controls[] = {
+	SOC_SINGLE_MULTI_EXT("DTMF_Generate Rx Low High Duration Gain",
+			     SND_SOC_NOPM, 0, 5000, 0, 4,
+			     msm_dtmf_rx_generate_get,
+			     msm_dtmf_rx_generate_put),
+	SOC_SINGLE_EXT("DTMF_Detect Rx Voice enable", SND_SOC_NOPM, 0, 1, 0,
+				msm_dtmf_detect_voice_rx_get,
+				msm_dtmf_detect_voice_rx_put),
+	SOC_SINGLE_EXT("DTMF_Detect Rx VoLTE enable", SND_SOC_NOPM, 0, 1, 0,
+				msm_dtmf_detect_volte_rx_get,
+				msm_dtmf_detect_volte_rx_put),
+};
+
+static int msm_pcm_dtmf_probe(struct snd_soc_platform *platform)
+{
+	snd_soc_add_platform_controls(platform, msm_dtmf_controls,
+				      ARRAY_SIZE(msm_dtmf_controls));
+	return 0;
+}
+
+static void dtmf_rx_detected_cb(uint8_t *pkt,
+				char *session,
+				void *private_data)
+{
+	struct dtmf_buf_node *buf_node = NULL;
+	struct vss_istream_evt_rx_dtmf_detected *dtmf_det_pkt =
+		(struct vss_istream_evt_rx_dtmf_detected *)pkt;
+	struct dtmf_drv_info *prtd = private_data;
+	unsigned long dsp_flags;
+
+	pr_debug("%s\n", __func__);
+	if (prtd->capture_substream == NULL)
+		return;
+
+	/* Copy dtmf detected info into out_queue. */
+	spin_lock_irqsave(&prtd->dsp_lock, dsp_flags);
+	/* discarding dtmf detection info till start is received */
+	if (!list_empty(&prtd->free_out_queue) && prtd->capture_start) {
+		buf_node = list_first_entry(&prtd->free_out_queue,
+					    struct dtmf_buf_node, list);
+		list_del(&buf_node->list);
+		buf_node->dtmf_det_pkt.high_freq = dtmf_det_pkt->high_freq;
+		buf_node->dtmf_det_pkt.low_freq = dtmf_det_pkt->low_freq;
+		if (session != NULL)
+			strlcpy(buf_node->dtmf_det_pkt.session,
+				session, MAX_SESSION_NAME_LEN);
+
+		buf_node->dtmf_det_pkt.dir = DTMF_IN_RX;
+		pr_debug("high =%d, low=%d session=%s\n",
+			 buf_node->dtmf_det_pkt.high_freq,
+			 buf_node->dtmf_det_pkt.low_freq,
+			 buf_node->dtmf_det_pkt.session);
+		list_add_tail(&buf_node->list, &prtd->out_queue);
+		prtd->pcm_capture_irq_pos += prtd->pcm_capture_count;
+		spin_unlock_irqrestore(&prtd->dsp_lock, dsp_flags);
+		snd_pcm_period_elapsed(prtd->capture_substream);
+	} else {
+		spin_unlock_irqrestore(&prtd->dsp_lock, dsp_flags);
+		pr_err("DTMF detection pkt in Rx  dropped, no free node available\n");
+	}
+
+	wake_up(&prtd->out_wait);
+}
+
+static int msm_pcm_capture_copy(struct snd_pcm_substream *substream,
+				int channel, snd_pcm_uframes_t hwoff,
+				void __user *buf, snd_pcm_uframes_t frames)
+{
+	int ret = 0;
+	int count = 0;
+	struct dtmf_buf_node *buf_node = NULL;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct dtmf_drv_info *prtd = runtime->private_data;
+	unsigned long dsp_flags;
+
+	count = frames_to_bytes(runtime, frames);
+
+	ret = wait_event_interruptible_timeout(prtd->out_wait,
+				(!list_empty(&prtd->out_queue)),
+				1 * HZ);
+
+	if (ret > 0) {
+		if (count <= DTMF_PKT_SIZE) {
+			spin_lock_irqsave(&prtd->dsp_lock, dsp_flags);
+			buf_node = list_first_entry(&prtd->out_queue,
+					struct dtmf_buf_node, list);
+			list_del(&buf_node->list);
+			spin_unlock_irqrestore(&prtd->dsp_lock, dsp_flags);
+			ret = copy_to_user(buf,
+					   &buf_node->dtmf_det_pkt,
+					   count);
+			if (ret) {
+				pr_err("%s: Copy to user retuned %d\n",
+					__func__, ret);
+				ret = -EFAULT;
+			}
+			spin_lock_irqsave(&prtd->dsp_lock, dsp_flags);
+			list_add_tail(&buf_node->list,
+				      &prtd->free_out_queue);
+			spin_unlock_irqrestore(&prtd->dsp_lock, dsp_flags);
+
+		} else {
+			pr_err("%s: Read count %d > DTMF_PKT_SIZE\n",
+				__func__, count);
+			ret = -ENOMEM;
+		}
+	} else if (ret == 0) {
+		pr_err("%s: No UL data available\n", __func__);
+		ret = -ETIMEDOUT;
+	} else {
+		pr_err("%s: Read was interrupted\n", __func__);
+		ret = -ERESTARTSYS;
+	}
+	return ret;
+}
+
+static int msm_pcm_copy(struct snd_pcm_substream *substream, int a,
+	 snd_pcm_uframes_t hwoff, void __user *buf, snd_pcm_uframes_t frames)
+{
+	int ret = 0;
+	pr_debug("%s() DTMF\n", __func__);
+
+	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+		ret = msm_pcm_capture_copy(substream, a, hwoff, buf, frames);
+
+	return ret;
+}
+
+static int msm_pcm_open(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct dtmf_drv_info *prtd = NULL;
+	int ret = 0;
+
+	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+		prtd = kzalloc(sizeof(struct dtmf_drv_info), GFP_KERNEL);
+
+		if (prtd == NULL) {
+			pr_err("Failed to allocate memory for msm_audio\n");
+			ret = -ENOMEM;
+			goto done;
+		}
+
+		mutex_init(&prtd->lock);
+		spin_lock_init(&prtd->dsp_lock);
+		init_waitqueue_head(&prtd->out_wait);
+		INIT_LIST_HEAD(&prtd->out_queue);
+		INIT_LIST_HEAD(&prtd->free_out_queue);
+
+		runtime->hw = msm_pcm_hardware;
+
+		ret = snd_pcm_hw_constraint_integer(runtime,
+						    SNDRV_PCM_HW_PARAM_PERIODS);
+		if (ret < 0)
+			pr_info("snd_pcm_hw_constraint_integer failed\n");
+
+		prtd->capture_substream = substream;
+		prtd->capture_instance++;
+		runtime->private_data = prtd;
+	}
+
+done:
+	return ret;
+}
+
+static int msm_pcm_close(struct snd_pcm_substream *substream)
+{
+	int ret = 0;
+	struct list_head *ptr = NULL;
+	struct list_head *next = NULL;
+	struct dtmf_buf_node *buf_node = NULL;
+	struct snd_dma_buffer *c_dma_buf;
+	struct snd_pcm_substream *c_substream;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct dtmf_drv_info *prtd = runtime->private_data;
+	unsigned long dsp_flags;
+
+	pr_debug("%s() DTMF\n", __func__);
+
+	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+		mutex_lock(&prtd->lock);
+		wake_up(&prtd->out_wait);
+
+		if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+			prtd->capture_instance--;
+
+		if (!prtd->capture_instance) {
+			if (prtd->state == DTMF_GEN_RX_STARTED) {
+				prtd->state = DTMF_GEN_RX_STOPPED;
+				voc_disable_dtmf_det_on_active_sessions();
+				voc_register_dtmf_rx_detection_cb(NULL, NULL);
+			}
+			/* release all buffer */
+			/* release out_queue and free_out_queue */
+			pr_debug("release all buffer\n");
+			c_substream = prtd->capture_substream;
+			if (c_substream == NULL) {
+				pr_debug("c_substream is NULL\n");
+				mutex_unlock(&prtd->lock);
+				return -EINVAL;
+			}
+
+			c_dma_buf = &c_substream->dma_buffer;
+			if (c_dma_buf == NULL) {
+				pr_debug("c_dma_buf is NULL.\n");
+				mutex_unlock(&prtd->lock);
+				return -EINVAL;
+			}
+
+			if (c_dma_buf->area != NULL) {
+				spin_lock_irqsave(&prtd->dsp_lock, dsp_flags);
+				list_for_each_safe(ptr, next,
+							&prtd->out_queue) {
+					buf_node = list_entry(ptr,
+						   struct dtmf_buf_node, list);
+					list_del(&buf_node->list);
+				}
+
+				list_for_each_safe(ptr, next,
+						   &prtd->free_out_queue) {
+					buf_node = list_entry(ptr,
+						   struct dtmf_buf_node, list);
+					list_del(&buf_node->list);
+				}
+
+				spin_unlock_irqrestore(&prtd->dsp_lock,
+						       dsp_flags);
+				dma_free_coherent(c_substream->pcm->card->dev,
+						  runtime->hw.buffer_bytes_max,
+						  c_dma_buf->area,
+						  c_dma_buf->addr);
+				c_dma_buf->area = NULL;
+			}
+		}
+		prtd->capture_substream = NULL;
+		mutex_unlock(&prtd->lock);
+	}
+
+	return ret;
+}
+
+static int msm_pcm_hw_params(struct snd_pcm_substream *substream,
+			     struct snd_pcm_hw_params *params)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct dtmf_drv_info *prtd = runtime->private_data;
+	struct snd_dma_buffer *dma_buf = &substream->dma_buffer;
+	struct dtmf_buf_node *buf_node = NULL;
+	int i = 0, offset = 0;
+	int ret = 0;
+
+	pr_debug("%s: DTMF\n", __func__);
+	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+		mutex_lock(&prtd->lock);
+		dma_buf->dev.type = SNDRV_DMA_TYPE_DEV;
+		dma_buf->dev.dev = substream->pcm->card->dev;
+		dma_buf->private_data = NULL;
+
+		dma_buf->area = dma_alloc_coherent(substream->pcm->card->dev,
+						runtime->hw.buffer_bytes_max,
+						&dma_buf->addr, GFP_KERNEL);
+		if (!dma_buf->area) {
+			pr_err("%s:MSM DTMF dma_alloc failed\n", __func__);
+			mutex_unlock(&prtd->lock);
+			return -ENOMEM;
+		}
+
+		dma_buf->bytes = runtime->hw.buffer_bytes_max;
+		memset(dma_buf->area, 0, runtime->hw.buffer_bytes_max);
+
+		for (i = 0; i < DTMF_MAX_Q_LEN; i++) {
+			pr_debug("node =%d\n", i);
+			buf_node = (void *) dma_buf->area + offset;
+			list_add_tail(&buf_node->list,
+				      &prtd->free_out_queue);
+			offset = offset + sizeof(struct dtmf_buf_node);
+		}
+
+		snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
+		mutex_unlock(&prtd->lock);
+	}
+
+	return ret;
+}
+
+static int msm_pcm_capture_prepare(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct dtmf_drv_info *prtd = runtime->private_data;
+
+	pr_debug("%s: DTMF\n", __func__);
+	prtd->pcm_capture_size  = snd_pcm_lib_buffer_bytes(substream);
+	prtd->pcm_capture_count = snd_pcm_lib_period_bytes(substream);
+	prtd->pcm_capture_irq_pos = 0;
+	prtd->pcm_capture_buf_pos = 0;
+	return 0;
+}
+
+static int msm_pcm_prepare(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct dtmf_drv_info *prtd = runtime->private_data;
+
+	pr_debug("%s: DTMF\n", __func__);
+
+	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+		mutex_lock(&prtd->lock);
+
+		msm_pcm_capture_prepare(substream);
+
+		if (runtime->format != FORMAT_S16_LE) {
+			pr_err("format:%u doesnt match %d\n",
+			       (uint32_t)runtime->format, FORMAT_S16_LE);
+			mutex_unlock(&prtd->lock);
+			return -EINVAL;
+		}
+
+		if (prtd->capture_instance &&
+			(prtd->state != DTMF_GEN_RX_STARTED)) {
+			voc_register_dtmf_rx_detection_cb(dtmf_rx_detected_cb,
+							  prtd);
+			prtd->state = DTMF_GEN_RX_STARTED;
+		}
+		mutex_unlock(&prtd->lock);
+	}
+
+	return 0;
+}
+
+static int msm_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+	int ret = 0;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct dtmf_drv_info *prtd = runtime->private_data;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+		pr_debug("%s: Trigger start\n", __func__);
+		if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+			prtd->capture_start = 1;
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+		pr_debug("SNDRV_PCM_TRIGGER_STOP\n");
+		if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+			prtd->capture_start = 0;
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	}
+
+	return ret;
+}
+
+static snd_pcm_uframes_t msm_pcm_pointer(struct snd_pcm_substream *substream)
+{
+	snd_pcm_uframes_t ret = 0;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct dtmf_drv_info *prtd = runtime->private_data;
+
+	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+		if (prtd->pcm_capture_irq_pos >= prtd->pcm_capture_size)
+			prtd->pcm_capture_irq_pos = 0;
+		ret = bytes_to_frames(runtime, (prtd->pcm_capture_irq_pos));
+	}
+
+	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,
+	.prepare        = msm_pcm_prepare,
+	.trigger        = msm_pcm_trigger,
+	.pointer        = msm_pcm_pointer,
+};
+
+static int msm_asoc_pcm_new(struct snd_soc_pcm_runtime *rtd)
+{
+	struct snd_card *card = rtd->card->snd_card;
+	int ret = 0;
+
+	if (!card->dev->coherent_dma_mask)
+		card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
+	return ret;
+}
+
+static struct snd_soc_platform_driver msm_soc_platform = {
+	.ops		= &msm_pcm_ops,
+	.pcm_new	= msm_asoc_pcm_new,
+	.probe		= msm_pcm_dtmf_probe,
+};
+
+static __devinit int msm_pcm_probe(struct platform_device *pdev)
+{
+	return snd_soc_register_platform(&pdev->dev,
+					 &msm_soc_platform);
+}
+
+static int msm_pcm_remove(struct platform_device *pdev)
+{
+	snd_soc_unregister_platform(&pdev->dev);
+	return 0;
+}
+
+static struct platform_driver msm_pcm_driver = {
+	.driver = {
+		.name = "msm-pcm-dtmf",
+		.owner = THIS_MODULE,
+	},
+	.probe = msm_pcm_probe,
+	.remove = __devexit_p(msm_pcm_remove),
+};
+
+static int __init msm_soc_platform_init(void)
+{
+	return platform_driver_register(&msm_pcm_driver);
+}
+module_init(msm_soc_platform_init);
+
+static void __exit msm_soc_platform_exit(void)
+{
+	platform_driver_unregister(&msm_pcm_driver);
+}
+module_exit(msm_soc_platform_exit);
+
+MODULE_DESCRIPTION("DTMF platform driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/msm/msm-pcm-q6.h b/sound/soc/msm/msm-pcm-q6.h
index 2678498..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;
 
diff --git a/sound/soc/msm/msm-pcm-routing.c b/sound/soc/msm/msm-pcm-routing.c
index 2b889b5..79ce671 100644
--- a/sound/soc/msm/msm-pcm-routing.c
+++ b/sound/soc/msm/msm-pcm-routing.c
@@ -78,7 +78,6 @@
 static const DECLARE_TLV_DB_LINEAR(compressed2_rx_vol_gain, 0,
 			INT_RX_VOL_MAX_STEPS);
 static int msm_route_ec_ref_rx;
-static char channel_mapping[PCM_FORMAT_MAX_NUM_CHANNEL];
 
 /* Equal to Frontend after last of the MULTIMEDIA SESSIONS */
 #define MAX_EQ_SESSIONS		MSM_FRONTEND_DAI_CS_VOICE
@@ -195,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},
 };
 
 
@@ -216,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)
@@ -299,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)
 {
@@ -376,6 +430,31 @@
 	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);
+
+	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;
@@ -400,8 +479,12 @@
 		if (!is_be_dai_extproc(i) &&
 		   (afe_get_port_type(msm_bedais[i].port_id) == port_type) &&
 		   (msm_bedais[i].active) &&
-		   (test_bit(fedai_id, &msm_bedais[i].fe_sessions)))
-			adm_close(msm_bedais[i].port_id);
+		   (test_bit(fedai_id, &msm_bedais[i].fe_sessions))) {
+			if (msm_bedais[i].port_id == PSEUDOPORT_01)
+				adm_pseudo_close(msm_bedais[i].port_id);
+			else
+				adm_close(msm_bedais[i].port_id);
+		}
 	}
 
 	fe_dai_map[fedai_id][session_type] = INVALID_SESSION;
@@ -575,6 +658,14 @@
 	else
 		clear_bit(val, &msm_bedais[reg].fe_sessions);
 
+	if (val == MSM_FRONTEND_DAI_DTMF_RX &&
+	    afe_get_port_type(msm_bedais[reg].port_id) ==
+						MSM_AFE_PORT_TYPE_RX) {
+		pr_debug("%s(): set=%d port id=0x%x for dtmf generation\n",
+			 __func__, set, msm_bedais[reg].port_id);
+		afe_set_dtmf_gen_rx_portid(msm_bedais[reg].port_id, set);
+	}
+
 	mutex_unlock(&routing_lock);
 
 	if (afe_get_port_type(msm_bedais[reg].port_id) ==
@@ -864,27 +955,6 @@
 	return 0;
 }
 
-static int msm_routing_get_channel_map_mixer(struct snd_kcontrol *kcontrol,
-				struct snd_ctl_elem_value *ucontrol)
-{
-	int i;
-	for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL; i++)
-		ucontrol->value.integer.value[i] = channel_mapping[i];
-	return 0;
-}
-
-static int msm_routing_put_channel_map_mixer(struct snd_kcontrol *kcontrol,
-				struct snd_ctl_elem_value *ucontrol)
-{
-	int i;
-
-	for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL; i++)
-		channel_mapping[i] = (char)(ucontrol->value.integer.value[i]);
-	multi_ch_pcm_set_channel_map(channel_mapping);
-
-	return 0;
-}
-
 static int msm_routing_set_compressed_vol_mixer(struct snd_kcontrol *kcontrol,
 				struct snd_ctl_elem_value *ucontrol)
 {
@@ -1232,6 +1302,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[] = {
@@ -1313,6 +1386,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[] = {
@@ -1515,6 +1599,9 @@
 	SOC_SINGLE_EXT("Voice Stub", MSM_BACKEND_DAI_PRI_I2S_RX,
 	MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
 	msm_routing_put_voice_stub_mixer),
+	SOC_SINGLE_EXT("DTMF", MSM_BACKEND_DAI_PRI_I2S_RX,
+	MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
 };
 
 static const struct snd_kcontrol_new sec_i2s_rx_voice_mixer_controls[] = {
@@ -1530,6 +1617,9 @@
 	SOC_SINGLE_EXT("SGLTE", MSM_BACKEND_DAI_SEC_I2S_RX,
 	MSM_FRONTEND_DAI_SGLTE, 1, 0, msm_routing_get_voice_mixer,
 	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("DTMF", MSM_BACKEND_DAI_SEC_I2S_RX,
+	MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
 };
 
 static const struct snd_kcontrol_new slimbus_rx_voice_mixer_controls[] = {
@@ -1545,6 +1635,9 @@
 	SOC_SINGLE_EXT("SGLTE", MSM_BACKEND_DAI_SLIMBUS_0_RX ,
 	MSM_FRONTEND_DAI_SGLTE, 1, 0, msm_routing_get_voice_mixer,
 	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("DTMF", MSM_BACKEND_DAI_SLIMBUS_0_RX ,
+	MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
 };
 
 static const struct snd_kcontrol_new bt_sco_rx_voice_mixer_controls[] = {
@@ -1563,6 +1656,9 @@
 	SOC_SINGLE_EXT("SGLTE", MSM_BACKEND_DAI_INT_BT_SCO_RX ,
 	MSM_FRONTEND_DAI_SGLTE, 1, 0, msm_routing_get_voice_mixer,
 	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("DTMF", MSM_BACKEND_DAI_INT_BT_SCO_RX ,
+	MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
 };
 
 static const struct snd_kcontrol_new mi2s_rx_voice_mixer_controls[] = {
@@ -1578,6 +1674,9 @@
 	SOC_SINGLE_EXT("SGLTE", MSM_BACKEND_DAI_MI2S_RX,
 	MSM_FRONTEND_DAI_SGLTE, 1, 0, msm_routing_get_voice_mixer,
 	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("DTMF", MSM_BACKEND_DAI_MI2S_RX,
+	MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
 };
 
 static const struct snd_kcontrol_new afe_pcm_rx_voice_mixer_controls[] = {
@@ -1596,6 +1695,9 @@
 	SOC_SINGLE_EXT("SGLTE", MSM_BACKEND_DAI_AFE_PCM_RX,
 	MSM_FRONTEND_DAI_SGLTE, 1, 0, msm_routing_get_voice_mixer,
 	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("DTMF", MSM_BACKEND_DAI_AFE_PCM_RX,
+	MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
 };
 
 static const struct snd_kcontrol_new aux_pcm_rx_voice_mixer_controls[] = {
@@ -1614,6 +1716,9 @@
 	SOC_SINGLE_EXT("SGLTE", MSM_BACKEND_DAI_AUXPCM_RX,
 	MSM_FRONTEND_DAI_SGLTE, 1, 0, msm_routing_get_voice_mixer,
 	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("DTMF", MSM_BACKEND_DAI_AUXPCM_RX,
+	MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
 };
 
 static const struct snd_kcontrol_new sec_aux_pcm_rx_voice_mixer_controls[] = {
@@ -1629,6 +1734,9 @@
 	SOC_SINGLE_EXT("SGLTE", MSM_BACKEND_DAI_SEC_AUXPCM_RX,
 	MSM_FRONTEND_DAI_SGLTE, 1, 0, msm_routing_get_voice_mixer,
 	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("DTMF", MSM_BACKEND_DAI_SEC_AUXPCM_RX,
+	MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
 };
 
 static const struct snd_kcontrol_new hdmi_rx_voice_mixer_controls[] = {
@@ -1647,6 +1755,9 @@
 	SOC_SINGLE_EXT("SGLTE", MSM_BACKEND_DAI_HDMI_RX,
 	MSM_FRONTEND_DAI_SGLTE, 1, 0, msm_routing_get_voice_mixer,
 	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("DTMF", MSM_BACKEND_DAI_HDMI_RX,
+	MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
 };
 
 static const struct snd_kcontrol_new stub_rx_mixer_controls[] = {
@@ -1938,12 +2049,6 @@
 	msm_routing_set_compressed2_vol_mixer, compressed2_rx_vol_gain),
 };
 
-static const struct snd_kcontrol_new multi_ch_channel_map_mixer_controls[] = {
-	SOC_SINGLE_MULTI_EXT("Playback Channel Map", SND_SOC_NOPM, 0, 8,
-	0, 8, msm_routing_get_channel_map_mixer,
-	msm_routing_put_channel_map_mixer),
-};
-
 static const struct snd_kcontrol_new lpa_SRS_trumedia_controls[] = {
 	{.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 	.name = "SRS TruMedia",
@@ -2191,6 +2296,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),
@@ -2220,6 +2326,8 @@
 		0, 0, 0, 0),
 	SND_SOC_DAPM_AIF_OUT("MI2S_UL_HL", "MI2S_TX_HOSTLESS Capture",
 		0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("DTMF_DL_HL", "DTMF_RX_HOSTLESS Playback",
+		0, 0, 0, 0),
 
 	/* Backend AIF */
 	/* Stream name equals to backend dai link stream name
@@ -2229,6 +2337,7 @@
 				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),
@@ -2288,6 +2397,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,
@@ -2426,6 +2537,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"},
@@ -2446,8 +2558,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"},
@@ -2530,48 +2646,56 @@
 	{"PRI_RX_Voice Mixer", "SGLTE", "SGLTE_DL"},
 	{"PRI_RX_Voice Mixer", "Voip", "VOIP_DL"},
 	{"PRI_RX_Voice Mixer", "Voice Stub", "VOICE_STUB_DL"},
+	{"PRI_RX_Voice Mixer", "DTMF", "DTMF_DL_HL"},
 	{"PRI_I2S_RX", NULL, "PRI_RX_Voice Mixer"},
 
 	{"SEC_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
 	{"SEC_RX_Voice Mixer", "VoLTE", "VoLTE_DL"},
 	{"SEC_RX_Voice Mixer", "SGLTE", "SGLTE_DL"},
 	{"SEC_RX_Voice Mixer", "Voip", "VOIP_DL"},
+	{"SEC_RX_Voice Mixer", "DTMF", "DTMF_DL_HL"},
 	{"SEC_I2S_RX", NULL, "SEC_RX_Voice Mixer"},
 
 	{"SLIM_0_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
 	{"SLIM_0_RX_Voice Mixer", "VoLTE", "VoLTE_DL"},
 	{"SLIM_0_RX_Voice Mixer", "SGLTE", "SGLTE_DL"},
 	{"SLIM_0_RX_Voice Mixer", "Voip", "VOIP_DL"},
+	{"SLIM_0_RX_Voice Mixer", "DTMF", "DTMF_DL_HL"},
 	{"SLIMBUS_0_RX", NULL, "SLIM_0_RX_Voice Mixer"},
 
 	{"INTERNAL_BT_SCO_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
 	{"INTERNAL_BT_SCO_RX_Voice Mixer", "VoLTE", "VoLTE_DL"},
 	{"INTERNAL_BT_SCO_RX_Voice Mixer", "SGLTE", "SGLTE_DL"},
 	{"INTERNAL_BT_SCO_RX_Voice Mixer", "Voip", "VOIP_DL"},
+	{"INTERNAL_BT_SCO_RX_Voice Mixer", "DTMF", "DTMF_DL_HL"},
 	{"INT_BT_SCO_RX", NULL, "INTERNAL_BT_SCO_RX_Voice Mixer"},
 
 	{"AFE_PCM_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
 	{"AFE_PCM_RX_Voice Mixer", "VoLTE", "VoLTE_DL"},
 	{"AFE_PCM_RX_Voice Mixer", "SGLTE", "SGLTE_DL"},
 	{"AFE_PCM_RX_Voice Mixer", "Voip", "VOIP_DL"},
+	{"AFE_PCM_RX_Voice Mixer", "DTMF", "DTMF_DL_HL"},
 	{"PCM_RX", NULL, "AFE_PCM_RX_Voice Mixer"},
 
 	{"AUX_PCM_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
 	{"AUX_PCM_RX_Voice Mixer", "VoLTE", "VoLTE_DL"},
 	{"AUX_PCM_RX_Voice Mixer", "SGLTE", "SGLTE_DL"},
 	{"AUX_PCM_RX_Voice Mixer", "Voip", "VOIP_DL"},
+	{"AUX_PCM_RX_Voice Mixer", "DTMF", "DTMF_DL_HL"},
 	{"AUX_PCM_RX", NULL, "AUX_PCM_RX_Voice Mixer"},
 
 	{"SEC_AUX_PCM_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
 	{"SEC_AUX_PCM_RX_Voice Mixer", "VoLTE", "VoLTE_DL"},
 	{"SEC_AUX_PCM_RX_Voice Mixer", "SGLTE", "SGLTE_DL"},
 	{"SEC_AUX_PCM_RX_Voice Mixer", "Voip", "VOIP_DL"},
+	{"SEC_AUX_PCM_RX_Voice Mixer", "DTMF", "DTMF_DL_HL"},
 	{"SEC_AUX_PCM_RX", NULL, "SEC_AUX_PCM_RX_Voice Mixer"},
 
 	{"HDMI_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
 	{"HDMI_RX_Voice Mixer", "VoLTE", "VoLTE_DL"},
 	{"HDMI_RX_Voice Mixer", "SGLTE", "SGLTE_DL"},
 	{"HDMI_RX_Voice Mixer", "Voip", "VOIP_DL"},
+	{"HDMI_RX_Voice Mixer", "DTMF", "DTMF_DL_HL"},
 	{"HDMI", NULL, "HDMI_RX_Voice Mixer"},
 	{"HDMI", NULL, "HDMI_DL_HL"},
 
@@ -2693,6 +2817,7 @@
 	{"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"},
@@ -2812,7 +2937,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,
@@ -2931,9 +3063,6 @@
 				ec_ref_rx_mixer_controls,
 			ARRAY_SIZE(ec_ref_rx_mixer_controls));
 
-	snd_soc_add_platform_controls(platform,
-				multi_ch_channel_map_mixer_controls,
-			ARRAY_SIZE(multi_ch_channel_map_mixer_controls));
 	return 0;
 }
 
diff --git a/sound/soc/msm/msm-pcm-routing.h b/sound/soc/msm/msm-pcm-routing.h
index e11133e..0c0d3b4 100644
--- a/sound/soc/msm/msm-pcm-routing.h
+++ b/sound/soc/msm/msm-pcm-routing.h
@@ -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"
@@ -61,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,
@@ -68,11 +70,12 @@
 	MSM_FRONTEND_DAI_VOICE_STUB,
 	MSM_FRONTEND_DAI_VOLTE,
 	MSM_FRONTEND_DAI_SGLTE,
+	MSM_FRONTEND_DAI_DTMF_RX,
 	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,
@@ -106,6 +109,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,
 };
 
@@ -118,6 +122,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);
diff --git a/sound/soc/msm/msm-pcm-voip.c b/sound/soc/msm/msm-pcm-voip.c
index 359414b..4d2b253 100644
--- a/sound/soc/msm/msm-pcm-voip.c
+++ b/sound/soc/msm/msm-pcm-voip.c
@@ -43,6 +43,25 @@
 #define MODE_AMR		0x5
 #define MODE_AMR_WB		0xD
 #define MODE_PCM		0xC
+#define MODE_G711		0xA
+#define MODE_G711A		0xF
+
+enum msm_audio_g711a_frame_type {
+	MVS_G711A_SPEECH_GOOD,
+	MVS_G711A_SID,
+	MVS_G711A_NO_DATA,
+	MVS_G711A_ERASURE
+};
+
+enum msm_audio_g711a_mode {
+	MVS_G711A_MODE_MULAW,
+	MVS_G711A_MODE_ALAW
+};
+
+enum msm_audio_g711_mode {
+	MVS_G711_MODE_MULAW,
+	MVS_G711_MODE_ALAW
+};
 
 enum format {
 	FORMAT_S16_LE = 2,
@@ -135,7 +154,7 @@
 	unsigned int pcm_capture_buf_pos;       /* position in buffer */
 };
 
-static int voip_get_media_type(uint32_t mode,
+static int voip_get_media_type(uint32_t mode, uint32_t rate_type,
 				unsigned int samp_rate);
 static int voip_get_rate_type(uint32_t mode,
 				uint32_t rate,
@@ -309,6 +328,79 @@
 			list_add_tail(&buf_node->list, &prtd->out_queue);
 			break;
 		}
+		case MODE_G711:
+		case MODE_G711A:{
+			/* G711 frames are 10ms each, but the DSP works with
+			 * 20ms frames and sends two 10ms frames per buffer.
+			 * Extract the two frames and put them in separate
+			 * buffers.
+			 */
+			/* Remove the first DSP frame info header.
+			 * Header format: G711A
+			 * Bits 0-1: Frame type
+			 * Bits 2-3: Frame rate
+			 *
+			 * Header format: G711
+			 * Bits 2-3: Frame rate
+			 */
+			if (prtd->mode == MODE_G711A)
+				buf_node->frame.header.frame_type =
+							(*voc_pkt) & 0x03;
+			voc_pkt = voc_pkt + DSP_FRAME_HDR_LEN;
+
+			/* There are two frames in the buffer. Length of the
+			 * first frame:
+			 */
+			buf_node->frame.len = (pkt_len -
+					       2 * DSP_FRAME_HDR_LEN) / 2;
+
+			memcpy(&buf_node->frame.voc_pkt[0],
+			       voc_pkt,
+			       buf_node->frame.len);
+			voc_pkt = voc_pkt + buf_node->frame.len;
+
+			list_add_tail(&buf_node->list, &prtd->out_queue);
+
+			/* Get another buffer from the free Q and fill in the
+			 * second frame.
+			 */
+			if (!list_empty(&prtd->free_out_queue)) {
+				buf_node =
+					list_first_entry(&prtd->free_out_queue,
+							 struct voip_buf_node,
+							 list);
+				list_del(&buf_node->list);
+
+				/* Remove the second DSP frame info header.
+				 * Header format:
+				 * Bits 0-1: Frame type
+				 * Bits 2-3: Frame rate
+				 */
+
+				if (prtd->mode == MODE_G711A)
+					buf_node->frame.header.frame_type =
+							(*voc_pkt) & 0x03;
+				voc_pkt = voc_pkt + DSP_FRAME_HDR_LEN;
+
+				/* There are two frames in the buffer. Length
+				 * of the second frame:
+				 */
+				buf_node->frame.len = (pkt_len -
+					2 * DSP_FRAME_HDR_LEN) / 2;
+
+				memcpy(&buf_node->frame.voc_pkt[0],
+				       voc_pkt,
+				       buf_node->frame.len);
+
+				list_add_tail(&buf_node->list,
+					      &prtd->out_queue);
+			} else {
+				/* Drop the second frame */
+				pr_err("%s: UL data dropped, read is slow\n",
+				       __func__);
+			}
+			break;
+		}
 		default: {
 			buf_node->frame.len = pkt_len;
 			memcpy(&buf_node->frame.voc_pkt[0],
@@ -383,6 +475,67 @@
 			list_add_tail(&buf_node->list, &prtd->free_in_queue);
 			break;
 		}
+		case MODE_G711:
+		case MODE_G711A:{
+			/* G711 frames are 10ms each but the DSP expects 20ms
+			 * worth of data, so send two 10ms frames per buffer.
+			 */
+			/* Add the first DSP frame info header. Header format:
+			 * Bits 0-1: Frame type
+			 * Bits 2-3: Frame rate
+			 */
+
+			*voc_pkt = ((prtd->rate_type  & 0x0F) << 2) |
+				    (buf_node->frame.header.frame_type & 0x03);
+			voc_pkt = voc_pkt + DSP_FRAME_HDR_LEN;
+
+			*pkt_len = buf_node->frame.len + DSP_FRAME_HDR_LEN;
+
+			memcpy(voc_pkt,
+			       &buf_node->frame.voc_pkt[0],
+			       buf_node->frame.len);
+			voc_pkt = voc_pkt + buf_node->frame.len;
+
+			list_add_tail(&buf_node->list, &prtd->free_in_queue);
+
+			if (!list_empty(&prtd->in_queue)) {
+				/* Get the second buffer. */
+				buf_node = list_first_entry(&prtd->in_queue,
+							struct voip_buf_node,
+							list);
+				list_del(&buf_node->list);
+
+				/* Add the second DSP frame info header.
+				 * Header format:
+				 * Bits 0-1: Frame type
+				 * Bits 2-3: Frame rate
+				 */
+				*voc_pkt = ((prtd->rate_type & 0x0F) << 2) |
+				(buf_node->frame.header.frame_type & 0x03);
+				voc_pkt = voc_pkt + DSP_FRAME_HDR_LEN;
+
+				*pkt_len = *pkt_len +
+					buf_node->frame.len + DSP_FRAME_HDR_LEN;
+
+				memcpy(voc_pkt,
+				       &buf_node->frame.voc_pkt[0],
+				       buf_node->frame.len);
+
+				list_add_tail(&buf_node->list,
+					      &prtd->free_in_queue);
+			} else {
+				/* Only 10ms worth of data is available, signal
+				 * erasure frame.
+				 */
+				*voc_pkt = ((prtd->rate_type & 0x0F) << 2) |
+					    (MVS_G711A_ERASURE & 0x03);
+
+				*pkt_len = *pkt_len + DSP_FRAME_HDR_LEN;
+				pr_debug("%s, Only 10ms read, erase 2nd frame\n",
+					 __func__);
+			}
+			break;
+		}
 		default: {
 			*pkt_len = buf_node->frame.len;
 
@@ -753,7 +906,8 @@
 	if ((runtime->format != FORMAT_SPECIAL) &&
 		 ((prtd->mode == MODE_AMR) || (prtd->mode == MODE_AMR_WB) ||
 		 (prtd->mode == MODE_IS127) || (prtd->mode == MODE_4GV_NB) ||
-		 (prtd->mode == MODE_4GV_WB))) {
+		 (prtd->mode == MODE_4GV_WB) || (prtd->mode == MODE_G711) ||
+		 (prtd->mode == MODE_G711A))) {
 		pr_err("mode:%d and format:%u are not mached\n",
 			prtd->mode, (uint32_t)runtime->format);
 		ret =  -EINVAL;
@@ -781,6 +935,7 @@
 		}
 		prtd->rate_type = rate_type;
 		media_type = voip_get_media_type(prtd->mode,
+						prtd->rate_type,
 						prtd->play_samp_rate);
 		if (media_type < 0) {
 			pr_err("fail at getting media_type\n");
@@ -1041,6 +1196,10 @@
 		}
 		break;
 	}
+	case MODE_G711:
+	case MODE_G711A:
+		*rate_type = rate;
+		break;
 	default:
 		pr_err("wrong mode type.\n");
 		ret = -EINVAL;
@@ -1050,7 +1209,7 @@
 	return ret;
 }
 
-static int voip_get_media_type(uint32_t mode,
+static int voip_get_media_type(uint32_t mode, uint32_t rate_type,
 				unsigned int samp_rate)
 {
 	uint32_t media_type;
@@ -1079,6 +1238,13 @@
 	case MODE_4GV_WB: /* EVRC-WB */
 		media_type = VSS_MEDIA_ID_4GV_WB_MODEM;
 		break;
+	case MODE_G711:
+	case MODE_G711A:
+		if (rate_type == MVS_G711A_MODE_MULAW)
+			media_type = VSS_MEDIA_ID_G711_MULAW;
+		else
+			media_type = VSS_MEDIA_ID_G711_ALAW;
+		break;
 	default:
 		pr_debug(" input mode is not supported\n");
 		media_type = -EINVAL;
diff --git a/sound/soc/msm/msm8930.c b/sound/soc/msm/msm8930.c
index b3db9e1..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  {
 
@@ -788,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;
@@ -1195,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 */
 	},
 	{
@@ -1206,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 */
 	{
diff --git a/sound/soc/msm/msm8974.c b/sound/soc/msm/msm8974.c
index e8ea058..37a4234 100644
--- a/sound/soc/msm/msm8974.c
+++ b/sound/soc/msm/msm8974.c
@@ -18,6 +18,7 @@
 #include <linux/slab.h>
 #include <linux/mfd/pm8xxx/pm8921.h>
 #include <linux/qpnp/clkdiv.h>
+#include <linux/regulator/consumer.h>
 #include <sound/core.h>
 #include <sound/soc.h>
 #include <sound/soc-dapm.h>
@@ -30,9 +31,6 @@
 
 #define DRV_NAME "msm8974-asoc-taiko"
 
-#define PM8921_GPIO_BASE		NR_GPIO_IRQS
-#define PM8921_GPIO_PM_TO_SYS(pm_gpio)  (pm_gpio - 1 + PM8921_GPIO_BASE)
-
 #define MSM8974_SPK_ON 1
 #define MSM8974_SPK_OFF 0
 
@@ -42,10 +40,10 @@
 #define BTSCO_RATE_8KHZ 8000
 #define BTSCO_RATE_16KHZ 16000
 
-#define BOTTOM_SPK_AMP_POS	0x1
-#define BOTTOM_SPK_AMP_NEG	0x2
-#define TOP_SPK_AMP_POS		0x4
-#define TOP_SPK_AMP_NEG		0x8
+#define LO_1_SPK_AMP	0x1
+#define LO_3_SPK_AMP	0x2
+#define LO_2_SPK_AMP	0x4
+#define LO_4_SPK_AMP	0x8
 
 #define GPIO_AUX_PCM_DOUT 43
 #define GPIO_AUX_PCM_DIN 44
@@ -56,6 +54,9 @@
 #define WCD9XXX_MBHC_DEF_RLOADS 5
 #define TAIKO_EXT_CLK_RATE 9600000
 
+/* It takes about 13ms for Class-D PAs to ramp-up */
+#define EXT_CLASS_D_EN_DELAY 13000
+
 void *def_taiko_mbhc_cal(void);
 static int msm_snd_enable_codec_ext_clk(struct snd_soc_codec *codec, int enable,
 					bool dapm);
@@ -89,11 +90,11 @@
 	SLIM_4_TX_1 = 150, /* In-call musid delivery TX */
 };
 
-static u32 top_spk_pamp_gpio  = PM8921_GPIO_PM_TO_SYS(18);
-static u32 bottom_spk_pamp_gpio = PM8921_GPIO_PM_TO_SYS(19);
-static int msm_spk_control;
-static int msm_ext_bottom_spk_pamp;
-static int msm_ext_top_spk_pamp;
+static struct platform_device *spdev;
+static struct regulator *ext_spk_amp_regulator;
+static int ext_spk_amp_gpio = -1;
+static int msm8974_spk_control = 1;
+static int msm8974_ext_spk_pamp;
 static int msm_slim_0_rx_ch = 1;
 static int msm_slim_0_tx_ch = 1;
 
@@ -105,103 +106,70 @@
 static int clk_users;
 static atomic_t auxpcm_rsc_ref;
 
-static void msm_enable_ext_spk_amp_gpio(u32 spk_amp_gpio)
+static int msm8974_liquid_ext_spk_power_amp_init(void)
 {
 	int ret = 0;
 
-	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,
-	};
-
-	if (spk_amp_gpio == bottom_spk_pamp_gpio) {
-
-		ret = gpio_request(bottom_spk_pamp_gpio, "BOTTOM_SPK_AMP");
+	ext_spk_amp_gpio = of_get_named_gpio(spdev->dev.of_node,
+		"qcom,ext-spk-amp-gpio", 0);
+	if (ext_spk_amp_gpio >= 0) {
+		ret = gpio_request(ext_spk_amp_gpio, "ext_spk_amp_gpio");
 		if (ret) {
-			pr_err("%s: Error requesting BOTTOM SPK AMP GPIO %u\n",
-				__func__, bottom_spk_pamp_gpio);
-			return;
+			pr_err("%s: gpio_request failed for ext_spk_amp_gpio.\n",
+				__func__);
+			return -EINVAL;
 		}
-		ret = pm8xxx_gpio_config(bottom_spk_pamp_gpio, &param);
-		if (ret)
-			pr_err("%s: Failed to configure Bottom Spk Ampl gpio %u\n",
-				__func__, bottom_spk_pamp_gpio);
-		else {
-			pr_debug("%s: enable Bottom spkr amp gpio\n", __func__);
-			gpio_direction_output(bottom_spk_pamp_gpio, 1);
-		}
+		gpio_direction_output(ext_spk_amp_gpio, 0);
 
-	} else if (spk_amp_gpio == top_spk_pamp_gpio) {
+		if (ext_spk_amp_regulator == NULL) {
+			ext_spk_amp_regulator = regulator_get(&spdev->dev,
+									"qcom,ext-spk-amp");
 
-		ret = gpio_request(top_spk_pamp_gpio, "TOP_SPK_AMP");
-		if (ret) {
-			pr_err("%s: Error requesting GPIO %d\n", __func__,
-				top_spk_pamp_gpio);
-			return;
+			if (IS_ERR(ext_spk_amp_regulator)) {
+				pr_err("%s: Cannot get regulator %s.\n",
+					__func__, "qcom,ext-spk-amp");
+
+				gpio_free(ext_spk_amp_gpio);
+				return PTR_ERR(ext_spk_amp_regulator);
+			}
 		}
-		ret = pm8xxx_gpio_config(top_spk_pamp_gpio, &param);
-		if (ret)
-			pr_err("%s: Failed to configure Top Spk Ampl gpio %u\n",
-				__func__, top_spk_pamp_gpio);
-		else {
-			pr_debug("%s: enable Top spkr amp gpio\n", __func__);
-			gpio_direction_output(top_spk_pamp_gpio, 1);
-		}
-	} else {
-		pr_err("%s: ERROR : Invalid External Speaker Ampl GPIO. gpio = %u\n",
-			__func__, spk_amp_gpio);
-		return;
 	}
+
+	return 0;
 }
 
-static void msm_ext_spk_power_amp_on(u32 spk)
+static void msm8974_liquid_ext_spk_power_amp_enable(u32 on)
 {
-	if (spk & (BOTTOM_SPK_AMP_POS | BOTTOM_SPK_AMP_NEG)) {
+	if (on)
+		regulator_enable(ext_spk_amp_regulator);
+	else
+		regulator_disable(ext_spk_amp_regulator);
 
-		if ((msm_ext_bottom_spk_pamp & BOTTOM_SPK_AMP_POS) &&
-			(msm_ext_bottom_spk_pamp & BOTTOM_SPK_AMP_NEG)) {
+	gpio_direction_output(ext_spk_amp_gpio, on);
+	usleep_range(EXT_CLASS_D_EN_DELAY, EXT_CLASS_D_EN_DELAY);
+	pr_debug("%s: %s external speaker PAs.\n", __func__,
+			on ? "Enable" : "Disable");
+}
 
-			pr_debug("%s() External Bottom Speaker Ampl already turned on. spk = 0x%08x\n",
+static void msm8974_ext_spk_power_amp_on(u32 spk)
+{
+	if (spk & (LO_1_SPK_AMP |
+					  LO_3_SPK_AMP |
+					  LO_2_SPK_AMP |
+					  LO_4_SPK_AMP)) {
+
+		pr_debug("%s() External Left/Right Speakers already turned on. spk = 0x%08x\n",
 						__func__, spk);
-			return;
-		}
 
-		msm_ext_bottom_spk_pamp |= spk;
+		msm8974_ext_spk_pamp |= spk;
 
-		if ((msm_ext_bottom_spk_pamp & BOTTOM_SPK_AMP_POS) &&
-			(msm_ext_bottom_spk_pamp & BOTTOM_SPK_AMP_NEG)) {
+		if ((msm8974_ext_spk_pamp & LO_1_SPK_AMP) &&
+			(msm8974_ext_spk_pamp & LO_3_SPK_AMP) &&
+			(msm8974_ext_spk_pamp & LO_2_SPK_AMP) &&
+			(msm8974_ext_spk_pamp & LO_4_SPK_AMP)) {
 
-			msm_enable_ext_spk_amp_gpio(bottom_spk_pamp_gpio);
-			pr_debug("%s: slepping 4 ms after turning on external Bottom Speaker Ampl\n",
-							__func__);
-			usleep_range(4000, 4000);
-		}
-
-	} else if (spk & (TOP_SPK_AMP_POS | TOP_SPK_AMP_NEG)) {
-
-		if ((msm_ext_top_spk_pamp & TOP_SPK_AMP_POS) &&
-			(msm_ext_top_spk_pamp & TOP_SPK_AMP_NEG)) {
-
-			pr_debug("%s() External Top Speaker Ampl already turned on. spk = 0x%08x\n",
-						__func__, spk);
-			return;
-		}
-
-		msm_ext_top_spk_pamp |= spk;
-
-		if ((msm_ext_top_spk_pamp & TOP_SPK_AMP_POS) &&
-			(msm_ext_top_spk_pamp & TOP_SPK_AMP_NEG)) {
-
-			msm_enable_ext_spk_amp_gpio(top_spk_pamp_gpio);
-			pr_debug("%s: sleeping 4 ms after turning on external Top Speaker Ampl\n",
-						__func__);
-			usleep_range(4000, 4000);
+			if (ext_spk_amp_gpio >= 0)
+				msm8974_liquid_ext_spk_power_amp_enable(1);
 		}
 	} else  {
 
@@ -211,35 +179,22 @@
 	}
 }
 
-static void msm_ext_spk_power_amp_off(u32 spk)
+static void msm8974_ext_spk_power_amp_off(u32 spk)
 {
-	if (spk & (BOTTOM_SPK_AMP_POS | BOTTOM_SPK_AMP_NEG)) {
+	if (spk & (LO_1_SPK_AMP |
+					  LO_3_SPK_AMP |
+					  LO_2_SPK_AMP |
+					  LO_4_SPK_AMP)) {
 
-		if (!msm_ext_bottom_spk_pamp)
-			return;
+		pr_debug("%s Left and right speakers case spk = 0x%08x",
+				  __func__, spk);
 
-		gpio_direction_output(bottom_spk_pamp_gpio, 0);
-		gpio_free(bottom_spk_pamp_gpio);
-		msm_ext_bottom_spk_pamp = 0;
+		if (!msm8974_ext_spk_pamp) {
+			if (ext_spk_amp_gpio >= 0)
+				msm8974_liquid_ext_spk_power_amp_enable(0);
+			msm8974_ext_spk_pamp = 0;
+		}
 
-		pr_debug("%s: sleeping 4 ms after turning off external Bottom Speaker Ampl\n",
-					__func__);
-
-		usleep_range(4000, 4000);
-
-	} else if (spk & (TOP_SPK_AMP_POS | TOP_SPK_AMP_NEG)) {
-
-		if (!msm_ext_top_spk_pamp)
-			return;
-
-		gpio_direction_output(top_spk_pamp_gpio, 0);
-		gpio_free(top_spk_pamp_gpio);
-		msm_ext_top_spk_pamp = 0;
-
-		pr_debug("%s: sleeping 4 ms after turning off external Top Spkaker Ampl\n",
-					__func__);
-
-		usleep_range(4000, 4000);
 	} else  {
 
 		pr_err("%s: ERROR : Invalid Ext Spk Ampl. spk = 0x%08x\n",
@@ -248,87 +203,89 @@
 	}
 }
 
-static void msm_ext_control(struct snd_soc_codec *codec)
+static void msm8974_ext_control(struct snd_soc_codec *codec)
 {
 	struct snd_soc_dapm_context *dapm = &codec->dapm;
 
 	mutex_lock(&dapm->codec->mutex);
 
-	pr_debug("%s: msm_spk_control = %d", __func__, msm_spk_control);
-	if (msm_spk_control == MSM8974_SPK_ON) {
-		snd_soc_dapm_enable_pin(dapm, "Ext Spk Bottom Pos");
-		snd_soc_dapm_enable_pin(dapm, "Ext Spk Bottom Neg");
-		snd_soc_dapm_enable_pin(dapm, "Ext Spk Top Pos");
-		snd_soc_dapm_enable_pin(dapm, "Ext Spk Top Neg");
+	pr_debug("%s: msm8974_spk_control = %d", __func__, msm8974_spk_control);
+	if (msm8974_spk_control == MSM8974_SPK_ON) {
+		snd_soc_dapm_enable_pin(dapm, "Lineout_1 amp");
+		snd_soc_dapm_enable_pin(dapm, "Lineout_3 amp");
+		snd_soc_dapm_enable_pin(dapm, "Lineout_2 amp");
+		snd_soc_dapm_enable_pin(dapm, "Lineout_4 amp");
 	} else {
-		snd_soc_dapm_disable_pin(dapm, "Ext Spk Bottom Pos");
-		snd_soc_dapm_disable_pin(dapm, "Ext Spk Bottom Neg");
-		snd_soc_dapm_disable_pin(dapm, "Ext Spk Top Pos");
-		snd_soc_dapm_disable_pin(dapm, "Ext Spk Top Neg");
+		snd_soc_dapm_disable_pin(dapm, "Lineout_1 amp");
+		snd_soc_dapm_disable_pin(dapm, "Lineout_3 amp");
+		snd_soc_dapm_disable_pin(dapm, "Lineout_2 amp");
+		snd_soc_dapm_disable_pin(dapm, "Lineout_4 amp");
 	}
 
 	snd_soc_dapm_sync(dapm);
 	mutex_unlock(&dapm->codec->mutex);
 }
 
-static int msm_get_spk(struct snd_kcontrol *kcontrol,
+static int msm8974_get_spk(struct snd_kcontrol *kcontrol,
 		       struct snd_ctl_elem_value *ucontrol)
 {
-	pr_debug("%s: msm_spk_control = %d", __func__, msm_spk_control);
-	ucontrol->value.integer.value[0] = msm_spk_control;
+	pr_debug("%s: msm8974_spk_control = %d", __func__, msm8974_spk_control);
+	ucontrol->value.integer.value[0] = msm8974_spk_control;
 	return 0;
 }
 
-static int msm_set_spk(struct snd_kcontrol *kcontrol,
+static int msm8974_set_spk(struct snd_kcontrol *kcontrol,
 		       struct snd_ctl_elem_value *ucontrol)
 {
 	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
 
 	pr_debug("%s()\n", __func__);
-	if (msm_spk_control == ucontrol->value.integer.value[0])
+	if (msm8974_spk_control == ucontrol->value.integer.value[0])
 		return 0;
 
-	msm_spk_control = ucontrol->value.integer.value[0];
-	msm_ext_control(codec);
+	msm8974_spk_control = ucontrol->value.integer.value[0];
+	msm8974_ext_control(codec);
 	return 1;
 }
 
-static int msm_spkramp_event(struct snd_soc_dapm_widget *w,
+
+static int msm_ext_spkramp_event(struct snd_soc_dapm_widget *w,
 			     struct snd_kcontrol *k, int event)
 {
-	pr_debug("%s() %x\n", __func__, SND_SOC_DAPM_EVENT_ON(event));
+	pr_debug("%s()\n", __func__);
 
 	if (SND_SOC_DAPM_EVENT_ON(event)) {
-		if (!strncmp(w->name, "Ext Spk Bottom Pos", 18))
-			msm_ext_spk_power_amp_on(BOTTOM_SPK_AMP_POS);
-		else if (!strncmp(w->name, "Ext Spk Bottom Neg", 18))
-			msm_ext_spk_power_amp_on(BOTTOM_SPK_AMP_NEG);
-		else if (!strncmp(w->name, "Ext Spk Top Pos", 15))
-			msm_ext_spk_power_amp_on(TOP_SPK_AMP_POS);
-		else if  (!strncmp(w->name, "Ext Spk Top Neg", 15))
-			msm_ext_spk_power_amp_on(TOP_SPK_AMP_NEG);
+		if (!strncmp(w->name, "Lineout_1 amp", 14))
+			msm8974_ext_spk_power_amp_on(LO_1_SPK_AMP);
+		else if (!strncmp(w->name, "Lineout_3 amp", 14))
+			msm8974_ext_spk_power_amp_on(LO_3_SPK_AMP);
+		else if (!strncmp(w->name, "Lineout_2 amp", 14))
+			msm8974_ext_spk_power_amp_on(LO_2_SPK_AMP);
+		else if  (!strncmp(w->name, "Lineout_4 amp", 14))
+			msm8974_ext_spk_power_amp_on(LO_4_SPK_AMP);
 		else {
 			pr_err("%s() Invalid Speaker Widget = %s\n",
 					__func__, w->name);
 			return -EINVAL;
 		}
-
 	} else {
-		if (!strncmp(w->name, "Ext Spk Bottom Pos", 18))
-			msm_ext_spk_power_amp_off(BOTTOM_SPK_AMP_POS);
-		else if (!strncmp(w->name, "Ext Spk Bottom Neg", 18))
-			msm_ext_spk_power_amp_off(BOTTOM_SPK_AMP_NEG);
-		else if (!strncmp(w->name, "Ext Spk Top Pos", 15))
-			msm_ext_spk_power_amp_off(TOP_SPK_AMP_POS);
-		else if  (!strncmp(w->name, "Ext Spk Top Neg", 15))
-			msm_ext_spk_power_amp_off(TOP_SPK_AMP_NEG);
+		if (!strncmp(w->name, "Lineout_1 amp", 14))
+			msm8974_ext_spk_power_amp_off(LO_1_SPK_AMP);
+		else if (!strncmp(w->name, "Lineout_3 amp", 14))
+			msm8974_ext_spk_power_amp_off(LO_3_SPK_AMP);
+		else if (!strncmp(w->name, "Lineout_2 amp", 14))
+			msm8974_ext_spk_power_amp_off(LO_2_SPK_AMP);
+		else if  (!strncmp(w->name, "Lineout_4 amp", 14))
+			msm8974_ext_spk_power_amp_off(LO_4_SPK_AMP);
 		else {
 			pr_err("%s() Invalid Speaker Widget = %s\n",
 					__func__, w->name);
 			return -EINVAL;
 		}
 	}
+
 	return 0;
+
 }
 
 static int msm_snd_enable_codec_ext_clk(struct snd_soc_codec *codec, int enable,
@@ -343,18 +300,20 @@
 		if (!codec_clk) {
 			dev_err(codec->dev, "%s: did not get Taiko MCLK\n",
 				__func__);
-			return -EINVAL;
+			ret = -EINVAL;
+			goto exit;
 		}
 
 		clk_users++;
 		if (clk_users != 1)
-			return ret;
+			goto exit;
 
 		ret = qpnp_clkdiv_enable(codec_clk);
 		if (ret) {
 			dev_err(codec->dev, "%s: Error enabling taiko MCLK\n",
 			       __func__);
-			return -ENODEV;
+			ret = -ENODEV;
+			goto exit;
 		}
 		taiko_mclk_enable(codec, 1, dapm);
 	} else {
@@ -367,8 +326,10 @@
 		} else {
 			pr_err("%s: Error releasing Tabla MCLK\n", __func__);
 			ret = -EINVAL;
+			goto exit;
 		}
 	}
+exit:
 	mutex_unlock(&cdc_mclk_mutex);
 	return ret;
 }
@@ -393,11 +354,11 @@
 	SND_SOC_DAPM_SUPPLY("MCLK",  SND_SOC_NOPM, 0, 0,
 	msm8974_mclk_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
 
-	SND_SOC_DAPM_SPK("Ext Spk Bottom Pos", msm_spkramp_event),
-	SND_SOC_DAPM_SPK("Ext Spk Bottom Neg", msm_spkramp_event),
+	SND_SOC_DAPM_SPK("Lineout_1 amp", msm_ext_spkramp_event),
+	SND_SOC_DAPM_SPK("Lineout_3 amp", msm_ext_spkramp_event),
 
-	SND_SOC_DAPM_SPK("Ext Spk Top Pos", msm_spkramp_event),
-	SND_SOC_DAPM_SPK("Ext Spk Top Neg", msm_spkramp_event),
+	SND_SOC_DAPM_SPK("Lineout_2 amp", msm_ext_spkramp_event),
+	SND_SOC_DAPM_SPK("Lineout_4 amp", msm_ext_spkramp_event),
 
 	SND_SOC_DAPM_MIC("Handset Mic", NULL),
 	SND_SOC_DAPM_MIC("Headset Mic", NULL),
@@ -689,8 +650,8 @@
 };
 
 static const struct snd_kcontrol_new msm_snd_controls[] = {
-	SOC_ENUM_EXT("Speaker Function", msm_snd_enum[0], msm_get_spk,
-		     msm_set_spk),
+	SOC_ENUM_EXT("Speaker Function", msm_snd_enum[0], msm8974_get_spk,
+		     msm8974_set_spk),
 	SOC_ENUM_EXT("SLIM_0_RX Channels", msm_snd_enum[1],
 		     msm_slim_0_rx_ch_get, msm_slim_0_rx_ch_put),
 	SOC_ENUM_EXT("SLIM_0_TX Channels", msm_snd_enum[2],
@@ -719,11 +680,6 @@
 
 	pr_info("%s(), dev_name%s\n", __func__, dev_name(cpu_dai->dev));
 
-	if (machine_is_msm8960_liquid()) {
-		top_spk_pamp_gpio = (PM8921_GPIO_PM_TO_SYS(19));
-		bottom_spk_pamp_gpio = (PM8921_GPIO_PM_TO_SYS(18));
-	}
-
 	rtd->pmdown_time = 0;
 
 	err = snd_soc_add_codec_controls(codec, msm_snd_controls,
@@ -731,13 +687,21 @@
 	if (err < 0)
 		return err;
 
+	err = msm8974_liquid_ext_spk_power_amp_init();
+	if (err) {
+		pr_err("%s: LiQUID 8974 CLASS_D PAs init failed (%d)\n",
+			__func__, err);
+		return err;
+	}
+
 	snd_soc_dapm_new_controls(dapm, msm8974_dapm_widgets,
 				ARRAY_SIZE(msm8974_dapm_widgets));
 
-	snd_soc_dapm_enable_pin(dapm, "Ext Spk Bottom Pos");
-	snd_soc_dapm_enable_pin(dapm, "Ext Spk Bottom Neg");
-	snd_soc_dapm_enable_pin(dapm, "Ext Spk Top Pos");
-	snd_soc_dapm_enable_pin(dapm, "Ext Spk Top Neg");
+	snd_soc_dapm_enable_pin(dapm, "Lineout_1 amp");
+	snd_soc_dapm_enable_pin(dapm, "Lineout_3 amp");
+	snd_soc_dapm_enable_pin(dapm, "Lineout_2 amp");
+	snd_soc_dapm_enable_pin(dapm, "Lineout_4 amp");
+
 
 	snd_soc_dapm_sync(dapm);
 
@@ -811,8 +775,8 @@
 	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[0] = 10;
+	btn_low[1] = 11;
 	btn_high[1] = 52;
 	btn_low[2] = 53;
 	btn_high[2] = 94;
@@ -1070,6 +1034,48 @@
 		.codec_dai_name = "snd-soc-dummy-dai",
 		.codec_name = "snd-soc-dummy",
 	},
+	{
+		.name = "SLIMBUS_1 Hostless",
+		.stream_name = "SLIMBUS_1 Hostless",
+		.cpu_dai_name = "SLIMBUS1_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, /* dai link has playback support */
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+	},
+	{
+		.name = "SLIMBUS_3 Hostless",
+		.stream_name = "SLIMBUS_3 Hostless",
+		.cpu_dai_name = "SLIMBUS3_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, /* dai link has playback support */
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+	},
+	{
+		.name = "SLIMBUS_4 Hostless",
+		.stream_name = "SLIMBUS_4 Hostless",
+		.cpu_dai_name = "SLIMBUS4_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, /* dai link has playback support */
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+	},
 	/* Backend BT/FM DAI Links */
 	{
 		.name = LPASS_BE_INT_BT_SCO_RX,
@@ -1226,6 +1232,84 @@
 		.be_hw_params_fixup = msm_slim_0_tx_be_hw_params_fixup,
 		.ops = &msm8974_be_ops,
 	},
+	{
+		.name = LPASS_BE_SLIMBUS_1_RX,
+		.stream_name = "Slimbus1 Playback",
+		.cpu_dai_name = "msm-dai-q6-dev.16386",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "taiko_codec",
+		.codec_dai_name	= "taiko_rx1",
+		.no_pcm = 1,
+		.be_id = MSM_BACKEND_DAI_SLIMBUS_1_RX,
+		.be_hw_params_fixup = msm_slim_0_rx_be_hw_params_fixup,
+		.ops = &msm8974_be_ops,
+		/* dai link has playback support */
+		.ignore_pmdown_time = 1,
+	},
+	{
+		.name = LPASS_BE_SLIMBUS_1_TX,
+		.stream_name = "Slimbus1 Capture",
+		.cpu_dai_name = "msm-dai-q6-dev.16387",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "taiko_codec",
+		.codec_dai_name	= "taiko_tx1",
+		.no_pcm = 1,
+		.be_id = MSM_BACKEND_DAI_SLIMBUS_1_TX,
+		.be_hw_params_fixup = msm_slim_0_tx_be_hw_params_fixup,
+		.ops = &msm8974_be_ops,
+	},
+	{
+		.name = LPASS_BE_SLIMBUS_3_RX,
+		.stream_name = "Slimbus3 Playback",
+		.cpu_dai_name = "msm-dai-q6-dev.16390",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "taiko_codec",
+		.codec_dai_name	= "taiko_rx1",
+		.no_pcm = 1,
+		.be_id = MSM_BACKEND_DAI_SLIMBUS_3_RX,
+		.be_hw_params_fixup = msm_slim_0_rx_be_hw_params_fixup,
+		.ops = &msm8974_be_ops,
+		/* dai link has playback support */
+		.ignore_pmdown_time = 1,
+	},
+	{
+		.name = LPASS_BE_SLIMBUS_3_TX,
+		.stream_name = "Slimbus3 Capture",
+		.cpu_dai_name = "msm-dai-q6-dev.16391",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "taiko_codec",
+		.codec_dai_name	= "taiko_tx1",
+		.no_pcm = 1,
+		.be_id = MSM_BACKEND_DAI_SLIMBUS_3_TX,
+		.be_hw_params_fixup = msm_slim_0_tx_be_hw_params_fixup,
+		.ops = &msm8974_be_ops,
+	},
+	{
+		.name = LPASS_BE_SLIMBUS_4_RX,
+		.stream_name = "Slimbus4 Playback",
+		.cpu_dai_name = "msm-dai-q6-dev.16392",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "taiko_codec",
+		.codec_dai_name	= "taiko_rx1",
+		.no_pcm = 1,
+		.be_id = MSM_BACKEND_DAI_SLIMBUS_4_RX,
+		.be_hw_params_fixup = msm_slim_0_rx_be_hw_params_fixup,
+		.ops = &msm8974_be_ops,
+		/* dai link has playback support */
+		.ignore_pmdown_time = 1,
+	},
+	{
+		.name = LPASS_BE_SLIMBUS_4_TX,
+		.stream_name = "Slimbus4 Capture",
+		.cpu_dai_name = "msm-dai-q6-dev.16393",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "taiko_codec",
+		.codec_dai_name	= "taiko_tx1",
+		.no_pcm = 1,
+		.be_id = MSM_BACKEND_DAI_SLIMBUS_4_TX,
+		.be_hw_params_fixup = msm_slim_0_tx_be_hw_params_fixup,
+		.ops = &msm8974_be_ops,
+	},
 };
 
 struct snd_soc_card snd_soc_card_msm8974 = {
@@ -1334,9 +1418,13 @@
 			ret);
 		goto err;
 	}
+
 	mutex_init(&cdc_mclk_mutex);
 	atomic_set(&auxpcm_rsc_ref, 0);
 
+	spdev = pdev;
+	ext_spk_amp_regulator = NULL;
+
 	return 0;
 err:
 	devm_kfree(&pdev->dev, pdata);
@@ -1348,7 +1436,13 @@
 	struct snd_soc_card *card = platform_get_drvdata(pdev);
 	struct msm8974_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card);
 
+	if (ext_spk_amp_regulator)
+		regulator_put(ext_spk_amp_regulator);
+
 	gpio_free(pdata->mclk_gpio);
+	if (ext_spk_amp_gpio >= 0)
+		gpio_free(ext_spk_amp_gpio);
+
 	snd_soc_unregister_card(card);
 
 	return 0;
diff --git a/sound/soc/msm/qdsp6/q6adm.c b/sound/soc/msm/qdsp6/q6adm.c
index c6970f1..119e017 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,129 @@
 	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_LS;
+			open.dev_channel_mapping[3] = PCM_CHANNEL_RS;
+		} else if (channel_mode == 6) {
+			open.dev_channel_mapping[0] = PCM_CHANNEL_FC;
+			open.dev_channel_mapping[1] = PCM_CHANNEL_FL;
+			open.dev_channel_mapping[2] = PCM_CHANNEL_FR;
+			open.dev_channel_mapping[3] = PCM_CHANNEL_LS;
+			open.dev_channel_mapping[4] = PCM_CHANNEL_RS;
+			open.dev_channel_mapping[5] = PCM_CHANNEL_LFE;
+		} 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 +1330,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..2d44a41 100644
--- a/sound/soc/msm/qdsp6/q6afe.c
+++ b/sound/soc/msm/qdsp6/q6afe.c
@@ -33,6 +33,7 @@
 		uint32_t token, uint32_t *payload, void *priv);
 	void *tx_private_data;
 	void *rx_private_data;
+	u16 dtmf_gen_rx_portid;
 };
 
 static struct afe_ctl this_afe;
@@ -91,6 +92,7 @@
 			case AFE_SERVICE_CMD_MEMORY_MAP:
 			case AFE_SERVICE_CMD_MEMORY_UNMAP:
 			case AFE_SERVICE_CMD_UNREG_RTPORT:
+			case AFE_PORTS_CMD_DTMF_CTL:
 				atomic_set(&this_afe.state, 0);
 				wake_up(&this_afe.wait);
 				break;
@@ -156,6 +158,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 +228,7 @@
 	case RT_PROXY_PORT_001_TX:
 	case SLIMBUS_4_RX:
 	case SLIMBUS_4_TX:
+	case PSEUDOPORT_01:
 	{
 		ret = 0;
 		break;
@@ -295,6 +299,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 +336,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 +514,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;
@@ -1610,6 +1623,83 @@
 	.write = afe_debug_write
 };
 #endif
+
+void afe_set_dtmf_gen_rx_portid(u16 port_id, int set)
+{
+	if (set)
+		this_afe.dtmf_gen_rx_portid = port_id;
+	else if (this_afe.dtmf_gen_rx_portid == port_id)
+		this_afe.dtmf_gen_rx_portid = -1;
+}
+
+int afe_dtmf_generate_rx(int64_t duration_in_ms,
+			 uint16_t high_freq,
+			 uint16_t low_freq, uint16_t gain)
+{
+	int ret = 0;
+	struct afe_dtmf_generation_command cmd_dtmf;
+
+	pr_debug("%s: DTMF AFE Gen\n", __func__);
+
+	if (afe_validate_port(this_afe.dtmf_gen_rx_portid) < 0) {
+		pr_err("%s: Failed : Invalid Port id = %d\n",
+		       __func__, this_afe.dtmf_gen_rx_portid);
+		ret = -EINVAL;
+		goto fail_cmd;
+	}
+
+	if (this_afe.apr == NULL) {
+		this_afe.apr = apr_register("ADSP", "AFE", afe_callback,
+					    0xFFFFFFFF, &this_afe);
+		pr_debug("%s: Register AFE\n", __func__);
+		if (this_afe.apr == NULL) {
+			pr_err("%s: Unable to register AFE\n", __func__);
+			ret = -ENODEV;
+			return ret;
+		}
+	}
+
+	pr_debug("dur=%lld: hfreq=%d lfreq=%d gain=%d portid=%x\n",
+		duration_in_ms, high_freq, low_freq, gain,
+		this_afe.dtmf_gen_rx_portid);
+
+	cmd_dtmf.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+				APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+	cmd_dtmf.hdr.pkt_size = sizeof(cmd_dtmf);
+	cmd_dtmf.hdr.src_port = 0;
+	cmd_dtmf.hdr.dest_port = 0;
+	cmd_dtmf.hdr.token = 0;
+	cmd_dtmf.hdr.opcode = AFE_PORTS_CMD_DTMF_CTL;
+	cmd_dtmf.duration_in_ms = duration_in_ms;
+	cmd_dtmf.high_freq = high_freq;
+	cmd_dtmf.low_freq = low_freq;
+	cmd_dtmf.gain = gain;
+	cmd_dtmf.num_ports = 1;
+	cmd_dtmf.port_ids = this_afe.dtmf_gen_rx_portid;
+
+	atomic_set(&this_afe.state, 1);
+	ret = apr_send_pkt(this_afe.apr, (uint32_t *) &cmd_dtmf);
+	if (ret < 0) {
+		pr_err("%s: AFE DTMF failed for num_ports:%d ids:%x\n",
+		       __func__, cmd_dtmf.num_ports, cmd_dtmf.port_ids);
+		ret = -EINVAL;
+		goto fail_cmd;
+	}
+
+	ret = wait_event_timeout(this_afe.wait,
+		(atomic_read(&this_afe.state) == 0),
+			msecs_to_jiffies(TIMEOUT_MS));
+	if (ret < 0) {
+		pr_err("%s: wait_event timeout\n", __func__);
+		ret = -EINVAL;
+		goto fail_cmd;
+	}
+	return 0;
+
+fail_cmd:
+	return ret;
+}
+
 int afe_sidetone(u16 tx_port_id, u16 rx_port_id, u16 enable, uint16_t gain)
 {
 	struct afe_port_sidetone_command cmd_sidetone;
@@ -1700,6 +1790,9 @@
 		goto fail_cmd;
 	}
 	pr_debug("%s: port_id=%d\n", __func__, port_id);
+	if ((port_id == RT_PROXY_DAI_001_RX) ||
+		(port_id == RT_PROXY_DAI_002_TX))
+		return 0;
 	port_id = afe_convert_virtual_to_portid(port_id);
 
 	stop.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
@@ -1744,6 +1837,7 @@
 	atomic_set(&this_afe.state, 0);
 	atomic_set(&this_afe.status, 0);
 	this_afe.apr = NULL;
+	this_afe.dtmf_gen_rx_portid = -1;
 #ifdef CONFIG_DEBUG_FS
 	debugfs_afelb = debugfs_create_file("afe_loopback",
 	S_IFREG | S_IWUGO, NULL, (void *) "afe_loopback",
diff --git a/sound/soc/msm/qdsp6/q6asm.c b/sound/soc/msm/qdsp6/q6asm.c
index 7e70d02..52e481a 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>
 
@@ -487,6 +486,7 @@
 	struct audio_buffer *buf;
 #ifdef CONFIG_MSM_MULTIMEDIA_USE_ION
 	int len;
+	unsigned int bufsz_4k_aligned;
 #endif
 
 	if (!(ac) || ((dir != IN) && (dir != OUT)))
@@ -527,8 +527,15 @@
 						mutex_unlock(&ac->cmd_lock);
 						goto fail;
 					}
+					bufsz_4k_aligned = (bufsz + 4095) &
+								(~4095);
+					pr_debug("%s: bufsz_4k_aligned %d"\
+						"bufsz = %d\n",
+						__func__, bufsz_4k_aligned,
+						bufsz);
 					buf[cnt].handle = ion_alloc
-						(buf[cnt].client, bufsz, SZ_4K,
+						(buf[cnt].client,
+						bufsz_4k_aligned, SZ_4K,
 						(0x1 << ION_AUDIO_HEAP_ID), 0);
 					if (IS_ERR_OR_NULL((void *)
 						buf[cnt].handle)) {
@@ -758,6 +765,7 @@
 {
 	uint32_t token;
 	uint32_t *payload = data->payload;
+	struct audio_client *ac;
 
 	if (data->opcode == RESET_EVENTS) {
 		pr_debug("%s: Reset event is received: %d %d apr[%p]\n",
@@ -777,6 +785,8 @@
 
 	if (data->opcode == APR_BASIC_RSP_RESULT) {
 		token = data->token;
+		ac = (struct audio_client *)data->token;
+		pr_debug("%s: audio_client addr %x\n", __func__, (uint32_t)ac);
 		switch (payload[0]) {
 		case ASM_SESSION_CMD_MEMORY_MAP:
 		case ASM_SESSION_CMD_MEMORY_UNMAP:
@@ -784,9 +794,15 @@
 		case ASM_SESSION_CMD_MEMORY_UNMAP_REGIONS:
 			pr_debug("%s:command[0x%x]success [0x%x]\n",
 					__func__, payload[0], payload[1]);
-			if (atomic_read(&this_mmap.cmd_state)) {
-				atomic_set(&this_mmap.cmd_state, 0);
-				wake_up(&this_mmap.cmd_wait);
+			if (atomic_read(&ac->cmd_state)) {
+				atomic_set(&ac->cmd_state, 0);
+				if (payload[1] != ADSP_EOK) {
+					pr_err("payload[1]:%d error case\n",
+						payload[1]);
+					atomic_set(&ac->cmd_response, 1);
+				} else
+					atomic_set(&ac->cmd_response, 0);
+				wake_up(&ac->cmd_wait);
 			}
 			break;
 		default:
@@ -894,12 +910,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 +1083,116 @@
 	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 == 1) {
+		enc_cfg.enc_blk.cfg.dts.channel_mapping[0] = PCM_CHANNEL_FC;
+	} else 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_FC;
+		enc_cfg.enc_blk.cfg.dts.channel_mapping[1] = PCM_CHANNEL_FL;
+		enc_cfg.enc_blk.cfg.dts.channel_mapping[2] = PCM_CHANNEL_FR;
+		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_LFE;
+	}
+	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)
 {
@@ -1218,14 +1346,16 @@
 static void q6asm_add_mmaphdr(struct apr_hdr *hdr, uint32_t pkt_size,
 							uint32_t cmd_flg)
 {
+	struct audio_client *ac;
 	pr_debug("%s:pkt size=%d cmd_flg=%d\n", __func__, pkt_size, cmd_flg);
+	ac = (struct audio_client *)hdr->token;
+	pr_debug("%s: audio_client = %x\n", __func__, (uint32_t)ac);
 	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);
+		atomic_set(&ac->cmd_state, 1);
 	}
 	hdr->pkt_size  = pkt_size;
 	return;
@@ -1485,6 +1615,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;
@@ -1633,6 +1767,9 @@
 	case FORMAT_AMRWB:
 		open.write_format = AMRWB_FS;
 		break;
+	case FORMAT_AMR_WB_PLUS:
+		open.write_format = AMR_WB_PLUS;
+		break;
 	case FORMAT_V13K:
 		open.write_format = V13K_FS;
 		break;
@@ -2700,6 +2837,8 @@
 	mem_map.mempool_id = 0; /* EBI */
 	mem_map.reserved = 0;
 
+	pr_debug("%s: audio_client addr %x\n", __func__, (uint32_t)ac);
+	mem_map.hdr.token = (uint32_t)ac;
 	q6asm_add_mmaphdr(&mem_map.hdr,
 			sizeof(struct asm_stream_cmd_memory_map), TRUE);
 
@@ -2714,14 +2853,20 @@
 		goto fail_cmd;
 	}
 
-	rc = wait_event_timeout(this_mmap.cmd_wait,
-		(atomic_read(&this_mmap.cmd_state) == 0), 5 * HZ);
+	rc = wait_event_timeout(ac->cmd_wait,
+		(atomic_read(&ac->cmd_state) == 0), 5*HZ);
 	if (!rc) {
 		pr_err("timeout. waited for memory_map\n");
 		rc = -EINVAL;
 		goto fail_cmd;
 	}
+	if (atomic_read(&ac->cmd_response)) {
+		pr_err("%s: ASM_SESSION_CMD_MEMORY_MAP cmd failed\n", __func__);
+		rc = -EINVAL;
+		goto fail_cmd;
+	}
 	rc = 0;
+
 fail_cmd:
 	return rc;
 }
@@ -2737,6 +2882,8 @@
 	}
 	pr_debug("%s: Session[%d]\n", __func__, ac->session);
 
+	pr_debug("%s: audio_client addr %x\n", __func__, (uint32_t)ac);
+	mem_unmap.hdr.token = (uint32_t)ac;
 	q6asm_add_mmaphdr(&mem_unmap.hdr,
 			sizeof(struct asm_stream_cmd_memory_unmap), TRUE);
 	mem_unmap.hdr.opcode = ASM_SESSION_CMD_MEMORY_UNMAP;
@@ -2750,14 +2897,21 @@
 		goto fail_cmd;
 	}
 
-	rc = wait_event_timeout(this_mmap.cmd_wait,
-			(atomic_read(&this_mmap.cmd_state) == 0), 5 * HZ);
+	rc = wait_event_timeout(ac->cmd_wait,
+		(atomic_read(&ac->cmd_state) == 0), 5*HZ);
 	if (!rc) {
-		pr_err("timeout. waited for memory_map\n");
+		pr_err("timeout. waited for memory_unmap\n");
+		rc = -EINVAL;
+		goto fail_cmd;
+	}
+	if (atomic_read(&ac->cmd_response)) {
+		pr_err("%s: ASM_SESSION_CMD_MEMORY_UNMAP cmd failed\n",
+			__func__);
 		rc = -EINVAL;
 		goto fail_cmd;
 	}
 	rc = 0;
+
 fail_cmd:
 	return rc;
 }
@@ -2846,6 +3000,8 @@
 	}
 	mmap_regions = (struct asm_stream_cmd_memory_map_regions *)
 							mmap_region_cmd;
+	mmap_regions->hdr.token = (uint32_t)ac;
+	pr_debug("%s: audio_client addr %x\n", __func__, (uint32_t)ac);
 	q6asm_add_mmaphdr(&mmap_regions->hdr, cmd_size, TRUE);
 	mmap_regions->hdr.opcode = ASM_SESSION_CMD_MEMORY_MAP_REGIONS;
 	mmap_regions->mempool_id = 0;
@@ -2871,14 +3027,21 @@
 		goto fail_cmd;
 	}
 
-	rc = wait_event_timeout(this_mmap.cmd_wait,
-			(atomic_read(&this_mmap.cmd_state) == 0), 5*HZ);
+	rc = wait_event_timeout(ac->cmd_wait,
+		(atomic_read(&ac->cmd_state) == 0), 5*HZ);
 	if (!rc) {
-		pr_err("timeout. waited for memory_map\n");
+		pr_err("timeout. waited for map_regions\n");
+		rc = -EINVAL;
+		goto fail_cmd;
+	}
+	if (atomic_read(&ac->cmd_response)) {
+		pr_err("%s: ASM_SESSION_CMD_MEMORY_MAP_REGIONS cmd failed\n",
+			__func__);
 		rc = -EINVAL;
 		goto fail_cmd;
 	}
 	rc = 0;
+
 fail_cmd:
 	kfree(mmap_region_cmd);
 	return rc;
@@ -2914,6 +3077,8 @@
 	}
 	unmap_regions = (struct asm_stream_cmd_memory_unmap_regions *)
 							unmap_region_cmd;
+	unmap_regions->hdr.token = (uint32_t)ac;
+	pr_debug("%s: audio_client addr %x\n", __func__, (uint32_t)ac);
 	q6asm_add_mmaphdr(&unmap_regions->hdr, cmd_size, TRUE);
 	unmap_regions->hdr.opcode = ASM_SESSION_CMD_MEMORY_UNMAP_REGIONS;
 	unmap_regions->nregions = bufcnt & 0x00ff;
@@ -2935,10 +3100,17 @@
 		goto fail_cmd;
 	}
 
-	rc = wait_event_timeout(this_mmap.cmd_wait,
-			(atomic_read(&this_mmap.cmd_state) == 0), 5*HZ);
+	rc = wait_event_timeout(ac->cmd_wait,
+		(atomic_read(&ac->cmd_state) == 0), 5*HZ);
 	if (!rc) {
-		pr_err("timeout. waited for memory_unmap\n");
+		pr_err("timeout. waited for unmap_regions\n");
+		rc = -EINVAL;
+		goto fail_cmd;
+	}
+	if (atomic_read(&ac->cmd_response)) {
+		pr_err("%s: ASM_SESSION_CMD_MEMORY_UNMAP_REGIONS cmd failed\n",
+			__func__);
+		rc = -EINVAL;
 		goto fail_cmd;
 	}
 	rc = 0;
diff --git a/sound/soc/msm/qdsp6/q6voice.c b/sound/soc/msm/qdsp6/q6voice.c
index cb2e39b..3b1e722 100644
--- a/sound/soc/msm/qdsp6/q6voice.c
+++ b/sound/soc/msm/qdsp6/q6voice.c
@@ -146,6 +146,21 @@
 	v->cvp_handle = cvp_handle;
 }
 
+char *voc_get_session_name(u16 session_id)
+{
+	char *session_name = NULL;
+
+	if (session_id == common.voice[VOC_PATH_PASSIVE].session_id) {
+		session_name = VOICE_SESSION_NAME;
+	} else if (session_id ==
+			common.voice[VOC_PATH_VOLTE_PASSIVE].session_id) {
+		session_name = VOLTE_SESSION_NAME;
+	} else if (session_id == common.voice[VOC_PATH_FULL].session_id) {
+		session_name = VOIP_SESSION_NAME;
+	}
+	return session_name;
+}
+
 uint16_t voc_get_session_id(char *name)
 {
 	u16 session_id = 0;
@@ -894,6 +909,105 @@
 	return 0;
 }
 
+static int voice_send_dtmf_rx_detection_cmd(struct voice_data *v,
+					    uint32_t enable)
+{
+	int ret = 0;
+	void *apr_cvs;
+	u16 cvs_handle;
+	struct cvs_set_rx_dtmf_detection_cmd cvs_dtmf_rx_detection;
+
+	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);
+
+	/* Set SET_DTMF_RX_DETECTION */
+	cvs_dtmf_rx_detection.hdr.hdr_field =
+				APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+					      APR_HDR_LEN(APR_HDR_SIZE),
+					      APR_PKT_VER);
+	cvs_dtmf_rx_detection.hdr.pkt_size =
+				APR_PKT_SIZE(APR_HDR_SIZE,
+				sizeof(cvs_dtmf_rx_detection) - APR_HDR_SIZE);
+	cvs_dtmf_rx_detection.hdr.src_port = v->session_id;
+	cvs_dtmf_rx_detection.hdr.dest_port = cvs_handle;
+	cvs_dtmf_rx_detection.hdr.token = 0;
+	cvs_dtmf_rx_detection.hdr.opcode =
+					VSS_ISTREAM_CMD_SET_RX_DTMF_DETECTION;
+	cvs_dtmf_rx_detection.cvs_dtmf_det.enable = enable;
+
+	v->cvs_state = CMD_STATUS_FAIL;
+
+	ret = apr_send_pkt(apr_cvs, (uint32_t *) &cvs_dtmf_rx_detection);
+	if (ret < 0) {
+		pr_err("%s: Error %d sending SET_DTMF_RX_DETECTION\n",
+		       __func__,
+		       ret);
+		return -EINVAL;
+	}
+
+	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__);
+		return -EINVAL;
+	}
+
+	return ret;
+}
+
+void voc_disable_dtmf_det_on_active_sessions(void)
+{
+	struct voice_data *v = NULL;
+	int i;
+	for (i = 0; i < MAX_VOC_SESSIONS; i++) {
+		v = &common.voice[i];
+		if ((v->dtmf_rx_detect_en) &&
+			((v->voc_state == VOC_RUN) ||
+			 (v->voc_state == VOC_CHANGE) ||
+			 (v->voc_state == VOC_STANDBY))) {
+			pr_debug("disable dtmf det on ses_id=%d\n",
+				 v->session_id);
+			voice_send_dtmf_rx_detection_cmd(v, 0);
+		}
+	}
+}
+
+int voc_enable_dtmf_rx_detection(uint16_t session_id, uint32_t enable)
+{
+	struct voice_data *v = voice_get_session(session_id);
+	int ret = 0;
+
+	if (v == NULL) {
+		pr_err("%s: invalid session_id 0x%x\n", __func__, session_id);
+		return -EINVAL;
+	}
+
+	mutex_lock(&v->lock);
+	v->dtmf_rx_detect_en = enable;
+
+	if ((v->voc_state == VOC_RUN) ||
+	    (v->voc_state == VOC_CHANGE) ||
+	    (v->voc_state == VOC_STANDBY))
+		ret = voice_send_dtmf_rx_detection_cmd(v,
+						       v->dtmf_rx_detect_en);
+
+	mutex_unlock(&v->lock);
+
+	return ret;
+}
+
 static int voice_config_cvs_vocoder(struct voice_data *v)
 {
 	int ret = 0;
@@ -2207,6 +2321,9 @@
 	if (v->rec_info.rec_enable)
 		voice_cvs_start_record(v, v->rec_info.rec_mode);
 
+	if (v->dtmf_rx_detect_en)
+		voice_send_dtmf_rx_detection_cmd(v, v->dtmf_rx_detect_en);
+
 	rtac_add_voice(voice_get_cvs_handle(v),
 		voice_get_cvp_handle(v),
 		v->dev_rx.port_id, v->dev_tx.port_id,
@@ -2445,6 +2562,10 @@
 	/* send stop voice cmd */
 	voice_send_stop_voice_cmd(v);
 
+	/* send stop dtmf detecton cmd */
+	if (v->dtmf_rx_detect_en)
+		voice_send_dtmf_rx_detection_cmd(v, 0);
+
 	/* Clear mute setting */
 	v->dev_tx.mute = common.default_mute_val;
 
@@ -3700,6 +3821,13 @@
 	common.mvs_info.private_data = private_data;
 }
 
+void voc_register_dtmf_rx_detection_cb(dtmf_rx_det_cb_fn dtmf_rx_ul_cb,
+				       void *private_data)
+{
+	common.dtmf_info.dtmf_rx_ul_cb = dtmf_rx_ul_cb;
+	common.dtmf_info.private_data = private_data;
+}
+
 void voc_config_vocoder(uint32_t media_type,
 			  uint32_t rate,
 			  uint32_t network_type,
@@ -3876,6 +4004,7 @@
 			case VSS_ISTREAM_CMD_STOP_PLAYBACK:
 			case VSS_ISTREAM_CMD_START_RECORD:
 			case VSS_ISTREAM_CMD_STOP_RECORD:
+			case VSS_ISTREAM_CMD_SET_RX_DTMF_DETECTION:
 				pr_debug("%s: cmd = 0x%x\n", __func__, ptr[0]);
 				v->cvs_state = CMD_STATUS_SUCCESS;
 				wake_up(&v->cvs_wait);
@@ -3947,8 +4076,30 @@
 	} else if (data->opcode ==  VOICE_EVT_GET_PARAM_ACK) {
 		rtac_make_voice_callback(RTAC_CVS, data->payload,
 					data->payload_size);
-	} else
+	} else if (data->opcode == VSS_ISTREAM_EVT_RX_DTMF_DETECTED) {
+		struct vss_istream_evt_rx_dtmf_detected *dtmf_rx_detected;
+		uint32_t *voc_pkt = data->payload;
+		uint32_t pkt_len = data->payload_size;
+
+		if ((voc_pkt != NULL) &&
+		    (pkt_len ==
+			sizeof(struct vss_istream_evt_rx_dtmf_detected))) {
+
+			dtmf_rx_detected =
+			(struct vss_istream_evt_rx_dtmf_detected *) voc_pkt;
+			pr_debug("RX_DTMF_DETECTED low_freq=%d high_freq=%d\n",
+				 dtmf_rx_detected->low_freq,
+				 dtmf_rx_detected->high_freq);
+			if (c->dtmf_info.dtmf_rx_ul_cb)
+				c->dtmf_info.dtmf_rx_ul_cb((uint8_t *)voc_pkt,
+					voc_get_session_name(v->session_id),
+					c->dtmf_info.private_data);
+		} else {
+			pr_err("Invalid packet\n");
+		}
+	} else {
 		pr_debug("Unknown opcode 0x%x\n", data->opcode);
+	}
 
 fail:
 	return 0;
@@ -3971,7 +4122,6 @@
 	v = voice_get_session(data->dest_port);
 	if (v == NULL) {
 		pr_err("%s: v is NULL\n", __func__);
-
 		return -EINVAL;
 	}
 
@@ -4141,6 +4291,7 @@
 		common.voice[i].dev_tx.port_id = 1;
 		common.voice[i].dev_rx.port_id = 0;
 		common.voice[i].sidetone_gain = 0x512;
+		common.voice[i].dtmf_rx_detect_en = 0;
 
 		common.voice[i].voc_state = VOC_INIT;
 
diff --git a/sound/soc/msm/qdsp6/q6voice.h b/sound/soc/msm/qdsp6/q6voice.h
index 34b1b52..2fc2266 100644
--- a/sound/soc/msm/qdsp6/q6voice.h
+++ b/sound/soc/msm/qdsp6/q6voice.h
@@ -567,6 +567,55 @@
 	/* Reserved, set to 0. */
 };
 
+/*
+ * Event sent by the stream to the client that enables Rx DTMF
+ * detection whenever DTMF is detected in the Rx path.
+ *
+ * The DTMF detection feature can only be used to detect DTMF
+ * frequencies as listed in the vss_istream_evt_rx_dtmf_detected_t
+ * structure.
+ */
+
+#define VSS_ISTREAM_EVT_RX_DTMF_DETECTED (0x0001101A)
+
+struct vss_istream_cmd_set_rx_dtmf_detection {
+	/*
+	 * Enables/disables Rx DTMF detection
+	 *
+	 * Possible values are
+	 * 0 - disable
+	 * 1 - enable
+	 *
+	 */
+	uint32_t enable;
+};
+
+#define VSS_ISTREAM_CMD_SET_RX_DTMF_DETECTION (0x00011027)
+
+struct vss_istream_evt_rx_dtmf_detected {
+	uint16_t low_freq;
+	/*
+	 * Detected low frequency. Possible values:
+	 * 697 Hz
+	 * 770 Hz
+	 * 852 Hz
+	 * 941 Hz
+	 */
+	uint16_t high_freq;
+	/*
+	 * Detected high frequency. Possible values:
+	 * 1209 Hz
+	 * 1336 Hz
+	 * 1477 Hz
+	 * 1633 Hz
+	 */
+};
+
+struct cvs_set_rx_dtmf_detection_cmd {
+	struct apr_hdr hdr;
+	struct vss_istream_cmd_set_rx_dtmf_detection cvs_dtmf_det;
+} __packed;
+
 struct cvs_create_passive_ctl_session_cmd {
 	struct apr_hdr hdr;
 	struct vss_istream_cmd_create_passive_control_session_t cvs_session;
@@ -858,6 +907,10 @@
 			 uint32_t *pkt_len,
 			 void *private_data);
 
+/* CB for DTMF RX Detection */
+typedef void (*dtmf_rx_det_cb_fn)(uint8_t *pkt,
+				  char *session,
+				  void *private_data);
 
 struct mvs_driver_info {
 	uint32_t media_type;
@@ -869,6 +922,11 @@
 	void *private_data;
 };
 
+struct dtmf_driver_info {
+	dtmf_rx_det_cb_fn dtmf_rx_ul_cb;
+	void *private_data;
+};
+
 struct incall_rec_info {
 	uint32_t rec_enable;
 	uint32_t rec_mode;
@@ -915,6 +973,8 @@
 	/* FENC enable value */
 	uint32_t fens_enable;
 
+	uint32_t dtmf_rx_detect_en;
+
 	struct voice_dev_route_state voc_route_state;
 
 	u16 session_id;
@@ -961,6 +1021,8 @@
 
 	struct mvs_driver_info mvs_info;
 
+	struct dtmf_driver_info dtmf_info;
+
 	struct voice_data voice[MAX_VOC_SESSIONS];
 };
 
@@ -968,6 +1030,9 @@
 			dl_cb_fn dl_cb,
 			void *private_data);
 
+void voc_register_dtmf_rx_detection_cb(dtmf_rx_det_cb_fn dtmf_rx_ul_cb,
+				       void *private_data);
+
 void voc_config_vocoder(uint32_t media_type,
 			uint32_t rate,
 			uint32_t network_type,
@@ -1005,11 +1070,20 @@
 int voc_enable_cvp(uint16_t session_id);
 int voc_set_route_flag(uint16_t session_id, uint8_t path_dir, uint8_t set);
 uint8_t voc_get_route_flag(uint16_t session_id, uint8_t path_dir);
+int voc_enable_dtmf_rx_detection(uint16_t session_id, uint32_t enable);
+void voc_disable_dtmf_det_on_active_sessions(void);
 
+#define MAX_SESSION_NAME_LEN 32
 #define VOICE_SESSION_NAME "Voice session"
 #define VOIP_SESSION_NAME "VoIP session"
 #define VOLTE_SESSION_NAME "VoLTE session"
 #define SGLTE_SESSION_NAME "SGLTE session"
+
+#define VOC_PATH_PASSIVE 0
+#define VOC_PATH_FULL 1
+#define VOC_PATH_VOLTE_PASSIVE 2
+#define VOC_PATH_SGLTE_PASSIVE 3
+
 uint16_t voc_get_session_id(char *name);
 
 int voc_start_playback(uint32_t set);
diff --git a/sound/soc/msm/qdsp6v2/audio_ocmem.c b/sound/soc/msm/qdsp6v2/audio_ocmem.c
index c046b63..f151e51 100644
--- a/sound/soc/msm/qdsp6v2/audio_ocmem.c
+++ b/sound/soc/msm/qdsp6v2/audio_ocmem.c
@@ -30,7 +30,13 @@
 
 #define AUDIO_OCMEM_BUF_SIZE (512 * SZ_1K)
 
+static int enable_ocmem_audio_voice = 1;
+module_param(enable_ocmem_audio_voice, int,
+			S_IRUGO | S_IWUSR | S_IWGRP);
+MODULE_PARM_DESC(enable_ocmem_audio_voice, "control OCMEM usage for audio/voice");
+
 enum {
+	OCMEM_STATE_DEFAULT = 0,
 	OCMEM_STATE_ALLOC = 1,
 	OCMEM_STATE_MAP_TRANSITION,
 	OCMEM_STATE_MAP_COMPL,
@@ -70,6 +76,7 @@
 	spinlock_t audio_lock;
 	struct workqueue_struct *audio_ocmem_workqueue;
 	struct workqueue_struct *voice_ocmem_workqueue;
+	bool ocmem_en;
 };
 
 static struct audio_ocmem_prv audio_ocmem_lcl;
@@ -80,6 +87,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,11 +114,21 @@
 				OCMEM_STATE_UNMAP_FAIL);
 		break;
 	case OCMEM_ALLOC_GROW:
-		audio_ocmem_lcl.buf = data;
-		pr_debug("%s: Alloc grow request received buf->addr: 0x%ld\n",
+		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);
+			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__);
@@ -120,7 +139,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);
 	}
@@ -189,7 +208,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);
@@ -204,7 +223,7 @@
 
 	atomic_set(&audio_ocmem_lcl.audio_state, OCMEM_STATE_MAP_TRANSITION);
 
-	pr_debug("%s: buf->addr: 0x%ld, len: %ld, audio_state[0x%x]\n",
+	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,
@@ -214,7 +233,7 @@
 	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__,
@@ -285,6 +304,7 @@
 			break;
 		}
 	}
+	ret = 0;
 fail_cmd:
 	pr_debug("%s: exit\n", __func__);
 	return ret;
@@ -301,15 +321,19 @@
 int audio_ocmem_disable(int cid)
 {
 	int ret;
+	int cur_state;
 
 	pr_debug("%s: disable\n", __func__);
-	if (atomic_read(&audio_ocmem_lcl.audio_cond))
-		atomic_set(&audio_ocmem_lcl.audio_cond, 0);
+	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,
@@ -326,6 +350,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",
@@ -333,10 +360,14 @@
 				atomic_read(&audio_ocmem_lcl.audio_state));
 			goto fail_cmd;
 		}
+		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: state=%d", __func__,
+		pr_debug("%s:error: state=%d", __func__,
 			atomic_read(&audio_ocmem_lcl.audio_state));
 		break;
 
@@ -391,21 +422,31 @@
 
 	struct voice_ocmem_workdata *workdata = NULL;
 
-	if (audio_ocmem_lcl.voice_ocmem_workqueue == NULL) {
-		pr_err("%s: voice ocmem workqueue is NULL\n", __func__);
-		return -EINVAL;
+	if (enable) {
+		if (!enable_ocmem_audio_voice)
+			audio_ocmem_lcl.ocmem_en = false;
+		else
+			audio_ocmem_lcl.ocmem_en = true;
 	}
-	workdata = kzalloc(sizeof(struct voice_ocmem_workdata),
-						GFP_ATOMIC);
-	if (workdata == NULL) {
-		pr_err("%s: mem failure\n", __func__);
-		return -ENOMEM;
-	}
-	workdata->id = cid;
-	workdata->en = enable;
+	if (audio_ocmem_lcl.ocmem_en) {
+		if (audio_ocmem_lcl.voice_ocmem_workqueue == NULL) {
+			pr_err("%s: voice ocmem workqueue is NULL\n",
+								__func__);
+			return -EINVAL;
+		}
+		workdata = kzalloc(sizeof(struct voice_ocmem_workdata),
+							GFP_ATOMIC);
+		if (workdata == NULL) {
+			pr_err("%s: mem failure\n", __func__);
+			return -ENOMEM;
+		}
+		workdata->id = cid;
+		workdata->en = enable;
 
-	INIT_WORK(&workdata->work, voice_ocmem_process_workdata);
-	queue_work(audio_ocmem_lcl.voice_ocmem_workqueue, &workdata->work);
+		INIT_WORK(&workdata->work, voice_ocmem_process_workdata);
+		queue_work(audio_ocmem_lcl.voice_ocmem_workqueue,
+							&workdata->work);
+	}
 
 	return 0;
 }
@@ -485,24 +526,35 @@
 {
 	struct audio_ocmem_workdata *workdata = NULL;
 
-	if (audio_ocmem_lcl.audio_ocmem_workqueue == NULL) {
-		pr_err("%s: audio ocmem workqueue is NULL\n", __func__);
-		return -EINVAL;
+	if (enable) {
+		if (!enable_ocmem_audio_voice)
+			audio_ocmem_lcl.ocmem_en = false;
+		else
+			audio_ocmem_lcl.ocmem_en = true;
 	}
-	workdata = kzalloc(sizeof(struct audio_ocmem_workdata),
+
+	if (audio_ocmem_lcl.ocmem_en) {
+		if (audio_ocmem_lcl.audio_ocmem_workqueue == NULL) {
+			pr_err("%s: audio ocmem workqueue is NULL\n",
+								__func__);
+			return -EINVAL;
+		}
+		workdata = kzalloc(sizeof(struct audio_ocmem_workdata),
 							GFP_ATOMIC);
-	if (workdata == NULL) {
-		pr_err("%s: mem failure\n", __func__);
-		return -ENOMEM;
+		if (workdata == NULL) {
+			pr_err("%s: mem failure\n", __func__);
+			return -ENOMEM;
+		}
+		workdata->id = id;
+		workdata->en = enable;
+
+		/* if previous work waiting for ocmem - signal it to exit */
+		atomic_set(&audio_ocmem_lcl.audio_exit, 1);
+
+		INIT_WORK(&workdata->work, audio_ocmem_process_workdata);
+		queue_work(audio_ocmem_lcl.audio_ocmem_workqueue,
+							&workdata->work);
 	}
-	workdata->id = id;
-	workdata->en = enable;
-
-	/* if previous work waiting for ocmem - signal it to exit */
-	atomic_set(&audio_ocmem_lcl.audio_exit, 1);
-
-	INIT_WORK(&workdata->work, audio_ocmem_process_workdata);
-	queue_work(audio_ocmem_lcl.audio_ocmem_workqueue, &workdata->work);
 
 	return 0;
 }
@@ -556,8 +608,10 @@
 
 	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);
+	audio_ocmem_lcl.ocmem_en = true;
 
 	/* populate platform data */
 	ret = audio_ocmem_platform_data_populate(pdev);
diff --git a/sound/soc/msm/qdsp6v2/msm-compr-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-compr-q6-v2.c
index d85bbbc..94c1c85 100644
--- a/sound/soc/msm/qdsp6v2/msm-compr-q6-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-compr-q6-v2.c
@@ -48,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} ;
 
@@ -434,8 +435,11 @@
 	switch (cmd) {
 	case SNDRV_PCM_TRIGGER_START:
 		prtd->pcm_irq_pos = 0;
-		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
-			audio_ocmem_process_req(AUDIO, true);
+		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) {
@@ -453,12 +457,10 @@
 		pr_debug("%s: Trigger start\n", __func__);
 		q6asm_run_nowait(prtd->audio_client, 0, 0, 0);
 		atomic_set(&prtd->start, 1);
+		atomic_set(&prtd->pending_buffer, 1);
 		break;
 	case SNDRV_PCM_TRIGGER_STOP:
 		pr_debug("SNDRV_PCM_TRIGGER_STOP\n");
-		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
-			audio_ocmem_process_req(AUDIO, false);
-
 		if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
 			switch (compr->info.codec_param.codec.id) {
 			case SND_AUDIOCODEC_AMRWB:
@@ -565,6 +567,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 +614,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;
diff --git a/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c
index 354dece..a307bcc 100644
--- a/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c
@@ -37,9 +37,39 @@
 	DECLARE_BITMAP(status_mask, STATUS_MAX);
 	u32 rate;
 	u32 channels;
+	u32 bitwidth;
 	union afe_port_config port_config;
 };
 
+struct msm_dai_q6_mi2s_dai_config {
+	u16 pdata_mi2s_lines;
+	struct msm_dai_q6_dai_data mi2s_dai_data;
+};
+
+struct msm_dai_q6_mi2s_dai_data {
+	struct msm_dai_q6_mi2s_dai_config tx_dai;
+	struct msm_dai_q6_mi2s_dai_config rx_dai;
+	struct snd_pcm_hw_constraint_list rate_constraint;
+	struct snd_pcm_hw_constraint_list bitwidth_constraint;
+};
+
+/* MI2S format field for AFE_PORT_CMD_I2S_CONFIG command
+ *  0: linear PCM
+ *  1: non-linear PCM
+ *  2: PCM data in IEC 60968 container
+ *  3: compressed data in IEC 60958 container
+ */
+static const char *const mi2s_format[] = {
+	"LPCM",
+	"Compr",
+	"LPCM-60958",
+	"Compr-60958"
+};
+
+static const struct soc_enum mi2s_config_enum[] = {
+	SOC_ENUM_SINGLE_EXT(4, mi2s_format),
+};
+
 static struct clk *pcm_src_clk;
 static struct clk *pcm_branch_clk;
 static struct clk *pcm_oe_src_clk;
@@ -597,8 +627,12 @@
 		break;
 	case SLIMBUS_0_RX:
 	case SLIMBUS_1_RX:
+	case SLIMBUS_3_RX:
+	case SLIMBUS_4_RX:
 	case SLIMBUS_0_TX:
 	case SLIMBUS_1_TX:
+	case SLIMBUS_3_TX:
+	case SLIMBUS_4_TX:
 		rc = msm_dai_q6_slim_bus_hw_params(params, dai,
 				substream->stream);
 		break;
@@ -708,6 +742,8 @@
 	switch (dai->id) {
 	case SLIMBUS_0_RX:
 	case SLIMBUS_1_RX:
+	case SLIMBUS_3_RX:
+	case SLIMBUS_4_RX:
 		/*
 		 * channel number to be between 128 and 255.
 		 * For RX port use channel numbers
@@ -730,6 +766,8 @@
 		break;
 	case SLIMBUS_0_TX:
 	case SLIMBUS_1_TX:
+	case SLIMBUS_3_TX:
+	case SLIMBUS_4_TX:
 		/*
 		 * channel number to be between 128 and 255.
 		 * For TX port use channel numbers
@@ -845,12 +883,13 @@
 
 static struct snd_soc_dai_driver msm_dai_q6_slimbus_1_rx_dai = {
 	.playback = {
-		.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000,
+		.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+		SNDRV_PCM_RATE_48000,
 		.formats = SNDRV_PCM_FMTBIT_S16_LE,
 		.channels_min = 1,
-		.channels_max = 1,
+		.channels_max = 2,
 		.rate_min = 8000,
-		.rate_max = 16000,
+		.rate_max = 48000,
 	},
 	.ops = &msm_dai_q6_ops,
 	.probe = msm_dai_q6_dai_probe,
@@ -859,12 +898,13 @@
 
 static struct snd_soc_dai_driver msm_dai_q6_slimbus_1_tx_dai = {
 	.capture = {
-		.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000,
+		.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+		SNDRV_PCM_RATE_48000,
 		.formats = SNDRV_PCM_FMTBIT_S16_LE,
 		.channels_min = 1,
-		.channels_max = 1,
+		.channels_max = 2,
 		.rate_min = 8000,
-		.rate_max = 16000,
+		.rate_max = 48000,
 	},
 	.ops = &msm_dai_q6_ops,
 	.probe = msm_dai_q6_dai_probe,
@@ -1145,6 +1185,639 @@
 	.remove = msm_dai_q6_dai_remove,
 };
 
+static int msm_dai_q6_mi2s_format_put(struct snd_kcontrol *kcontrol,
+				      struct snd_ctl_elem_value *ucontrol)
+{
+	struct msm_dai_q6_dai_data *dai_data = kcontrol->private_data;
+	int value = ucontrol->value.integer.value[0];
+	dai_data->port_config.i2s.data_format = value;
+	pr_debug("%s: value = %d, channel = %d, line = %d\n",
+		 __func__, value, dai_data->port_config.i2s.mono_stereo,
+		 dai_data->port_config.i2s.channel_mode);
+	return 0;
+}
+
+static int msm_dai_q6_mi2s_format_get(struct snd_kcontrol *kcontrol,
+				      struct snd_ctl_elem_value *ucontrol)
+{
+	struct msm_dai_q6_dai_data *dai_data = kcontrol->private_data;
+	ucontrol->value.integer.value[0] =
+		dai_data->port_config.i2s.data_format;
+	return 0;
+}
+
+static const struct snd_kcontrol_new mi2s_config_controls[] = {
+	SOC_ENUM_EXT("PRI MI2S RX Format", mi2s_config_enum[0],
+		     msm_dai_q6_mi2s_format_get,
+		     msm_dai_q6_mi2s_format_put),
+	SOC_ENUM_EXT("SEC RX Format", mi2s_config_enum[0],
+		     msm_dai_q6_mi2s_format_get,
+		     msm_dai_q6_mi2s_format_put),
+	SOC_ENUM_EXT("PRI MI2S TX Format", mi2s_config_enum[0],
+		     msm_dai_q6_mi2s_format_get,
+		     msm_dai_q6_mi2s_format_put),
+};
+
+static int msm_dai_q6_dai_mi2s_probe(struct snd_soc_dai *dai)
+{
+	struct msm_dai_q6_mi2s_dai_data *mi2s_dai_data =
+			dev_get_drvdata(dai->dev);
+	struct snd_kcontrol *kcontrol = NULL;
+	int rc = 0;
+
+	if (mi2s_dai_data->rx_dai.mi2s_dai_data.port_config.i2s.channel_mode) {
+		kcontrol = snd_ctl_new1(&mi2s_config_controls[0],
+					&mi2s_dai_data->rx_dai.mi2s_dai_data);
+		rc = snd_ctl_add(dai->card->snd_card, kcontrol);
+
+		if (IS_ERR_VALUE(rc)) {
+			dev_err(dai->dev, "%s: err add RX fmt ctl\n", __func__);
+			goto rtn;
+		}
+	}
+	if (mi2s_dai_data->tx_dai.mi2s_dai_data.port_config.i2s.channel_mode) {
+		rc = snd_ctl_add(dai->card->snd_card,
+				snd_ctl_new1(&mi2s_config_controls[2],
+				&mi2s_dai_data->tx_dai.mi2s_dai_data));
+
+		if (IS_ERR_VALUE(rc)) {
+			if (kcontrol)
+				snd_ctl_remove(dai->card->snd_card, kcontrol);
+			dev_err(dai->dev, "%s: err add TX fmt ctl\n", __func__);
+		}
+	}
+rtn:
+	return rc;
+}
+
+
+static int msm_dai_q6_dai_mi2s_remove(struct snd_soc_dai *dai)
+{
+	struct msm_dai_q6_mi2s_dai_data *mi2s_dai_data =
+		dev_get_drvdata(dai->dev);
+	int rc;
+
+	/* If AFE port is still up, close it */
+	if (test_bit(STATUS_PORT_STARTED,
+		     mi2s_dai_data->rx_dai.mi2s_dai_data.status_mask)) {
+		rc = afe_close(MI2S_RX); /* can block */
+		if (IS_ERR_VALUE(rc))
+			dev_err(dai->dev, "fail to close MI2S_RX port\n");
+		clear_bit(STATUS_PORT_STARTED,
+			  mi2s_dai_data->rx_dai.mi2s_dai_data.status_mask);
+	}
+	if (test_bit(STATUS_PORT_STARTED,
+		     mi2s_dai_data->tx_dai.mi2s_dai_data.status_mask)) {
+		rc = afe_close(MI2S_TX); /* can block */
+		if (IS_ERR_VALUE(rc))
+			dev_err(dai->dev, "fail to close MI2S_TX port\n");
+		clear_bit(STATUS_PORT_STARTED,
+			  mi2s_dai_data->tx_dai.mi2s_dai_data.status_mask);
+	}
+	kfree(mi2s_dai_data);
+	snd_soc_unregister_dai(dai->dev);
+	return 0;
+}
+
+static int msm_dai_q6_mi2s_startup(struct snd_pcm_substream *substream,
+				   struct snd_soc_dai *dai)
+{
+	struct msm_dai_q6_mi2s_dai_data *mi2s_dai_data =
+		dev_get_drvdata(dai->dev);
+
+	dev_dbg(dai->dev, "%s: cnst list %p\n", __func__,
+		mi2s_dai_data->rate_constraint.list);
+
+	if (mi2s_dai_data->rate_constraint.list) {
+		snd_pcm_hw_constraint_list(substream->runtime, 0,
+				SNDRV_PCM_HW_PARAM_RATE,
+				&mi2s_dai_data->rate_constraint);
+		snd_pcm_hw_constraint_list(substream->runtime, 0,
+				SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
+				&mi2s_dai_data->bitwidth_constraint);
+	}
+
+	return 0;
+}
+
+
+static int msm_mi2s_get_port_id(u32 mi2s_id, int stream, u16 *port_id)
+{
+	int ret = 0;
+
+	switch (stream) {
+	case SNDRV_PCM_STREAM_PLAYBACK:
+		switch (mi2s_id) {
+		case MSM_PRIM_MI2S:
+			*port_id = MI2S_RX;
+			break;
+		case MSM_SEC_MI2S:
+			*port_id = AFE_PORT_ID_SECONDARY_MI2S_RX;
+			break;
+		case MSM_TERT_MI2S:
+			*port_id = AFE_PORT_ID_TERTIARY_MI2S_RX;
+			break;
+		case MSM_QUAT_MI2S:
+			*port_id = AFE_PORT_ID_QUATERNARY_MI2S_RX;
+			break;
+		break;
+		default:
+			ret = -1;
+		break;
+		}
+	break;
+	case SNDRV_PCM_STREAM_CAPTURE:
+		switch (mi2s_id) {
+		case MSM_PRIM_MI2S:
+			*port_id = MI2S_TX;
+			break;
+		case MSM_SEC_MI2S:
+			*port_id = AFE_PORT_ID_SECONDARY_MI2S_TX;
+			break;
+		case MSM_TERT_MI2S:
+			*port_id = AFE_PORT_ID_TERTIARY_MI2S_TX;
+			break;
+		case MSM_QUAT_MI2S:
+			*port_id = AFE_PORT_ID_QUATERNARY_MI2S_TX;
+			break;
+		default:
+			ret = -1;
+		break;
+		}
+	break;
+	default:
+		ret = -1;
+	break;
+	}
+	pr_debug("%s: port_id = %#x\n", __func__, *port_id);
+	return ret;
+}
+
+static int msm_dai_q6_mi2s_prepare(struct snd_pcm_substream *substream,
+		struct snd_soc_dai *dai)
+{
+	struct msm_dai_q6_mi2s_dai_data *mi2s_dai_data =
+		dev_get_drvdata(dai->dev);
+	struct msm_dai_q6_dai_data *dai_data =
+		(substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
+		 &mi2s_dai_data->rx_dai.mi2s_dai_data :
+		 &mi2s_dai_data->tx_dai.mi2s_dai_data);
+	u16 port_id = 0;
+	int rc = 0;
+
+	if (msm_mi2s_get_port_id(dai->id, substream->stream,
+				 &port_id) != 0) {
+		dev_err(dai->dev, "%s: Invalid Port ID %#x\n",
+				__func__, port_id);
+		return -EINVAL;
+	}
+
+	dev_dbg(dai->dev, "%s: dai id %d, afe port id = %x\n"
+		"dai_data->channels = %u sample_rate = %u\n", __func__,
+		dai->id, port_id, dai_data->channels, dai_data->rate);
+
+	if (!test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) {
+		/* PORT START should be set if prepare called
+		 * in active state.
+		 */
+		rc = afe_port_start(port_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_mi2s_hw_params(struct snd_pcm_substream *substream,
+				struct snd_pcm_hw_params *params,
+				struct snd_soc_dai *dai)
+{
+	struct msm_dai_q6_mi2s_dai_data *mi2s_dai_data =
+		dev_get_drvdata(dai->dev);
+	struct msm_dai_q6_mi2s_dai_config *mi2s_dai_config =
+		(substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
+		&mi2s_dai_data->rx_dai : &mi2s_dai_data->tx_dai);
+	struct msm_dai_q6_dai_data *dai_data = &mi2s_dai_config->mi2s_dai_data;
+	struct afe_param_id_i2s_cfg *i2s = &dai_data->port_config.i2s;
+
+
+	dai_data->channels = params_channels(params);
+	switch (dai_data->channels) {
+	case 8:
+	case 7:
+		if (mi2s_dai_config->pdata_mi2s_lines < AFE_PORT_I2S_8CHS)
+			goto error_invalid_data;
+		dai_data->port_config.i2s.channel_mode = AFE_PORT_I2S_8CHS;
+		break;
+	case 6:
+	case 5:
+		if (mi2s_dai_config->pdata_mi2s_lines < AFE_PORT_I2S_6CHS)
+			goto error_invalid_data;
+		dai_data->port_config.i2s.channel_mode = AFE_PORT_I2S_6CHS;
+		break;
+	case 4:
+	case 3:
+		if (mi2s_dai_config->pdata_mi2s_lines < AFE_PORT_I2S_QUAD01)
+			goto error_invalid_data;
+		if (mi2s_dai_config->pdata_mi2s_lines == AFE_PORT_I2S_QUAD23)
+			dai_data->port_config.i2s.channel_mode =
+				mi2s_dai_config->pdata_mi2s_lines;
+		else
+			dai_data->port_config.i2s.channel_mode =
+					AFE_PORT_I2S_QUAD01;
+		break;
+	case 2:
+	case 1:
+		if (mi2s_dai_config->pdata_mi2s_lines < AFE_PORT_I2S_SD0)
+			goto error_invalid_data;
+		switch (mi2s_dai_config->pdata_mi2s_lines) {
+		case AFE_PORT_I2S_SD0:
+		case AFE_PORT_I2S_SD1:
+		case AFE_PORT_I2S_SD2:
+		case AFE_PORT_I2S_SD3:
+			dai_data->port_config.i2s.channel_mode =
+				mi2s_dai_config->pdata_mi2s_lines;
+			break;
+		case AFE_PORT_I2S_QUAD01:
+		case AFE_PORT_I2S_6CHS:
+		case AFE_PORT_I2S_8CHS:
+			dai_data->port_config.i2s.channel_mode =
+						AFE_PORT_I2S_SD0;
+			break;
+		case AFE_PORT_I2S_QUAD23:
+			dai_data->port_config.i2s.channel_mode =
+						AFE_PORT_I2S_SD2;
+			break;
+		}
+		if (dai_data->channels == 2)
+			dai_data->port_config.i2s.mono_stereo =
+						MSM_AFE_CH_STEREO;
+		else
+			dai_data->port_config.i2s.mono_stereo = MSM_AFE_MONO;
+		break;
+	default:
+		goto error_invalid_data;
+	}
+	dai_data->rate = params_rate(params);
+	dai_data->port_config.i2s.bit_width = 16;
+	dai_data->bitwidth = 16;
+	dai_data->port_config.i2s.i2s_cfg_minor_version =
+			AFE_API_VERSION_I2S_CONFIG;
+	dai_data->port_config.i2s.sample_rate = dai_data->rate;
+	if (!mi2s_dai_data->rate_constraint.list) {
+		mi2s_dai_data->rate_constraint.list = &dai_data->rate;
+		mi2s_dai_data->bitwidth_constraint.list = &dai_data->bitwidth;
+	}
+
+	dev_dbg(dai->dev, "%s: dai id %d dai_data->channels = %d\n"
+		"sample_rate = %u i2s_cfg_minor_version = %#x\n"
+		"bit_width = %hu  channel_mode = %#x mono_stereo = %#x\n"
+		"ws_src = %#x sample_rate = %u data_format = %#x\n"
+		"reserved = %u\n", __func__, dai->id, dai_data->channels,
+		dai_data->rate, i2s->i2s_cfg_minor_version, i2s->bit_width,
+		i2s->channel_mode, i2s->mono_stereo, i2s->ws_src,
+		i2s->sample_rate, i2s->data_format, i2s->reserved);
+
+	return 0;
+
+error_invalid_data:
+	pr_debug("%s: dai_data->channels = %d channel_mode = %d\n", __func__,
+		 dai_data->channels, dai_data->port_config.i2s.channel_mode);
+	return -EINVAL;
+}
+
+
+static int msm_dai_q6_mi2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+	struct msm_dai_q6_mi2s_dai_data *mi2s_dai_data =
+	dev_get_drvdata(dai->dev);
+
+	if (test_bit(STATUS_PORT_STARTED,
+	    mi2s_dai_data->rx_dai.mi2s_dai_data.status_mask) ||
+	    test_bit(STATUS_PORT_STARTED,
+	    mi2s_dai_data->tx_dai.mi2s_dai_data.status_mask)) {
+		dev_err(dai->dev, "%s: err chg i2s mode while dai running",
+			__func__);
+		return -EPERM;
+	}
+
+	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBS_CFS:
+		mi2s_dai_data->rx_dai.mi2s_dai_data.port_config.i2s.ws_src = 1;
+		mi2s_dai_data->tx_dai.mi2s_dai_data.port_config.i2s.ws_src = 1;
+		break;
+	case SND_SOC_DAIFMT_CBM_CFM:
+		mi2s_dai_data->rx_dai.mi2s_dai_data.port_config.i2s.ws_src = 0;
+		mi2s_dai_data->tx_dai.mi2s_dai_data.port_config.i2s.ws_src = 0;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static void msm_dai_q6_mi2s_shutdown(struct snd_pcm_substream *substream,
+				     struct snd_soc_dai *dai)
+{
+	struct msm_dai_q6_mi2s_dai_data *mi2s_dai_data =
+			dev_get_drvdata(dai->dev);
+	struct msm_dai_q6_dai_data *dai_data =
+		(substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
+		 &mi2s_dai_data->rx_dai.mi2s_dai_data :
+		 &mi2s_dai_data->tx_dai.mi2s_dai_data);
+	 u16 port_id = 0;
+	int rc = 0;
+
+	if (msm_mi2s_get_port_id(dai->id, substream->stream,
+				 &port_id) != 0) {
+		dev_err(dai->dev, "%s: Invalid Port ID %#x\n",
+				__func__, port_id);
+	}
+
+	dev_dbg(dai->dev, "%s: closing afe port id = %x\n",
+			__func__, port_id);
+
+	if (test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) {
+		rc = afe_close(port_id);
+		if (IS_ERR_VALUE(rc))
+			dev_err(dai->dev, "fail to close AFE port\n");
+		clear_bit(STATUS_PORT_STARTED, dai_data->status_mask);
+	}
+
+	if (!test_bit(STATUS_PORT_STARTED,
+			mi2s_dai_data->rx_dai.mi2s_dai_data.status_mask) &&
+		!test_bit(STATUS_PORT_STARTED,
+			mi2s_dai_data->rx_dai.mi2s_dai_data.status_mask)) {
+		mi2s_dai_data->rate_constraint.list = NULL;
+		mi2s_dai_data->bitwidth_constraint.list = NULL;
+	}
+}
+
+static struct snd_soc_dai_ops msm_dai_q6_mi2s_ops = {
+	.startup	= msm_dai_q6_mi2s_startup,
+	.prepare	= msm_dai_q6_mi2s_prepare,
+	.hw_params	= msm_dai_q6_mi2s_hw_params,
+	.set_fmt	= msm_dai_q6_mi2s_set_fmt,
+	.shutdown	= msm_dai_q6_mi2s_shutdown,
+};
+
+/* Channel min and max are initialized base on platform data */
+static struct snd_soc_dai_driver msm_dai_q6_mi2s_dai = {
+	.playback = {
+		.rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+		SNDRV_PCM_RATE_16000,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE,
+		.rate_min =     8000,
+		.rate_max =     48000,
+	},
+	.capture = {
+		.rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+		SNDRV_PCM_RATE_16000,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE,
+		.rate_min =     8000,
+		.rate_max =     48000,
+	},
+	.ops = &msm_dai_q6_mi2s_ops,
+	.probe = msm_dai_q6_dai_mi2s_probe,
+	.remove = msm_dai_q6_dai_mi2s_remove,
+};
+
+
+static int msm_dai_q6_mi2s_get_lineconfig(u16 sd_lines, u16 *config_ptr,
+					  unsigned int *ch_cnt)
+{
+	u8 num_of_sd_lines;
+
+	num_of_sd_lines = num_of_bits_set(sd_lines);
+	switch (num_of_sd_lines) {
+	case 0:
+		pr_debug("%s: no line is assigned\n", __func__);
+		break;
+	case 1:
+		switch (sd_lines) {
+		case MSM_MI2S_SD0:
+			*config_ptr = AFE_PORT_I2S_SD0;
+			break;
+		case MSM_MI2S_SD1:
+			*config_ptr = AFE_PORT_I2S_SD1;
+			break;
+		case MSM_MI2S_SD2:
+			*config_ptr = AFE_PORT_I2S_SD2;
+			break;
+		case MSM_MI2S_SD3:
+			*config_ptr = AFE_PORT_I2S_SD3;
+			break;
+		default:
+			pr_err("%s: invalid SD line\n",
+				   __func__);
+			goto error_invalid_data;
+		}
+		break;
+	case 2:
+		switch (sd_lines) {
+		case MSM_MI2S_SD0 | MSM_MI2S_SD1:
+			*config_ptr = AFE_PORT_I2S_QUAD01;
+			break;
+		case MSM_MI2S_SD2 | MSM_MI2S_SD3:
+			*config_ptr = AFE_PORT_I2S_QUAD23;
+			break;
+		default:
+			pr_err("%s: invalid SD line\n",
+				   __func__);
+			goto error_invalid_data;
+		}
+		break;
+	case 3:
+		switch (sd_lines) {
+		case MSM_MI2S_SD0 | MSM_MI2S_SD1 | MSM_MI2S_SD2:
+			*config_ptr = AFE_PORT_I2S_6CHS;
+			break;
+		default:
+			pr_err("%s: invalid SD lines\n",
+				   __func__);
+			goto error_invalid_data;
+		}
+		break;
+	case 4:
+		switch (sd_lines) {
+		case MSM_MI2S_SD0 | MSM_MI2S_SD1 | MSM_MI2S_SD2 | MSM_MI2S_SD3:
+			*config_ptr = AFE_PORT_I2S_8CHS;
+			break;
+		default:
+			pr_err("%s: invalid SD lines\n",
+				   __func__);
+			goto error_invalid_data;
+		}
+		break;
+	default:
+		pr_err("%s: invalid SD lines\n", __func__);
+		goto error_invalid_data;
+	}
+	*ch_cnt = num_of_sd_lines;
+	return 0;
+
+error_invalid_data:
+	return -EINVAL;
+}
+
+static int msm_dai_q6_mi2s_platform_data_validation(
+	struct platform_device *pdev, struct snd_soc_dai_driver *dai_driver)
+{
+	struct msm_dai_q6_mi2s_dai_data *dai_data = dev_get_drvdata(&pdev->dev);
+	struct msm_mi2s_pdata *mi2s_pdata =
+			(struct msm_mi2s_pdata *) pdev->dev.platform_data;
+	unsigned int ch_cnt;
+	int rc = 0;
+	u16 sd_line;
+
+	if (mi2s_pdata == NULL) {
+		pr_err("%s: mi2s_pdata NULL", __func__);
+		return -EINVAL;
+	}
+
+	rc = msm_dai_q6_mi2s_get_lineconfig(mi2s_pdata->rx_sd_lines,
+					    &sd_line, &ch_cnt);
+
+	if (IS_ERR_VALUE(rc)) {
+		dev_err(&pdev->dev, "invalid MI2S RX sd line config\n");
+		goto rtn;
+	}
+
+	if (ch_cnt) {
+		dai_data->rx_dai.mi2s_dai_data.port_config.i2s.channel_mode =
+				mi2s_pdata->rx_sd_lines;
+		dai_data->rx_dai.pdata_mi2s_lines = mi2s_pdata->rx_sd_lines;
+		dai_driver->playback.channels_min = 1;
+		dai_driver->playback.channels_max = ch_cnt << 1;
+	} else {
+		dai_driver->playback.channels_min = 0;
+		dai_driver->playback.channels_max = 0;
+	}
+	rc = msm_dai_q6_mi2s_get_lineconfig(mi2s_pdata->tx_sd_lines,
+					    &sd_line, &ch_cnt);
+
+	if (IS_ERR_VALUE(rc)) {
+		dev_err(&pdev->dev, "invalid MI2S TX sd line config\n");
+		goto rtn;
+	}
+
+	if (ch_cnt) {
+		dai_data->tx_dai.mi2s_dai_data.port_config.i2s.channel_mode =
+			mi2s_pdata->tx_sd_lines;
+		dai_data->tx_dai.pdata_mi2s_lines = mi2s_pdata->tx_sd_lines;
+		dai_driver->capture.channels_min = 1;
+		dai_driver->capture.channels_max = ch_cnt << 1;
+	} else {
+		dai_driver->capture.channels_min = 0;
+		dai_driver->capture.channels_max = 0;
+	}
+
+	dev_dbg(&pdev->dev, "%s: playback sdline %x capture sdline %x\n",
+		__func__, dai_data->rx_dai.pdata_mi2s_lines,
+		dai_data->tx_dai.pdata_mi2s_lines);
+	dev_dbg(&pdev->dev, "%s: playback ch_max %d capture ch_mx %d\n",
+		__func__, dai_driver->playback.channels_max,
+		dai_driver->capture.channels_max);
+rtn:
+	return rc;
+}
+
+static __devinit int msm_dai_q6_mi2s_dev_probe(struct platform_device *pdev)
+{
+	struct msm_dai_q6_mi2s_dai_data *dai_data;
+	const char *q6_mi2s_dev_id = "qcom,msm-dai-q6-mi2s-dev-id";
+	u32 tx_line = 0;
+	u32  rx_line = 0;
+	u32 mi2s_intf = 0;
+	struct msm_mi2s_pdata *mi2s_pdata;
+	int rc = 0;
+
+
+	rc = of_property_read_u32(pdev->dev.of_node, q6_mi2s_dev_id,
+				  &mi2s_intf);
+	if (rc) {
+		dev_err(&pdev->dev,
+			"%s: missing %x in dt node\n", __func__, mi2s_intf);
+		return rc;
+	}
+
+	dev_dbg(&pdev->dev, "dev name %s dev id %x\n", dev_name(&pdev->dev),
+			     mi2s_intf);
+
+	if (mi2s_intf < MSM_PRIM_MI2S || mi2s_intf > MSM_QUAT_MI2S) {
+		dev_err(&pdev->dev,
+			"%s: Invalid MI2S ID %u from Device Tree\n",
+			__func__, mi2s_intf);
+		return -ENXIO;
+	}
+
+	dev_set_name(&pdev->dev, "%s.%d", "msm-dai-q6-mi2s", mi2s_intf);
+	pdev->id = mi2s_intf;
+
+	mi2s_pdata = kzalloc(sizeof(struct msm_mi2s_pdata), GFP_KERNEL);
+	if (!mi2s_pdata) {
+		dev_err(&pdev->dev, "fail to allocate mi2s_pdata data\n");
+		rc = -ENOMEM;
+		goto rtn;
+	}
+
+	rc = of_property_read_u32(pdev->dev.of_node, "qcom,msm-mi2s-rx-lines",
+				  &rx_line);
+	if (rc) {
+		dev_err(&pdev->dev, "%s: Rx line from DT file %s\n", __func__,
+			"qcom,msm-mi2s-rx-lines");
+		return rc;
+	}
+
+	rc = of_property_read_u32(pdev->dev.of_node, "qcom,msm-mi2s-tx-lines",
+				  &tx_line);
+	if (rc) {
+		dev_err(&pdev->dev, "%s: Tx line from DT file %s\n", __func__,
+			"qcom,msm-mi2s-tx-lines");
+		return rc;
+	}
+	dev_dbg(&pdev->dev, "dev name %s Rx line %x , Tx ine %x\n",
+		dev_name(&pdev->dev), rx_line, tx_line);
+	mi2s_pdata->rx_sd_lines = rx_line;
+	mi2s_pdata->tx_sd_lines = tx_line;
+	dai_data = kzalloc(sizeof(struct msm_dai_q6_mi2s_dai_data),
+				GFP_KERNEL);
+	if (!dai_data) {
+		dev_err(&pdev->dev, "fail to allocate dai data\n");
+		rc = -ENOMEM;
+		goto rtn;
+	} else
+		dev_set_drvdata(&pdev->dev, dai_data);
+	pdev->dev.platform_data = mi2s_pdata;
+	rc = msm_dai_q6_mi2s_platform_data_validation(pdev,
+					&msm_dai_q6_mi2s_dai);
+	if (IS_ERR_VALUE(rc))
+		goto err_pdata;
+	dai_data->rate_constraint.count = 1;
+	dai_data->bitwidth_constraint.count = 1;
+	rc = snd_soc_register_dai(&pdev->dev, &msm_dai_q6_mi2s_dai);
+	if (IS_ERR_VALUE(rc))
+		goto err_pdata;
+	return 0;
+err_pdata:
+	dev_err(&pdev->dev, "fail to msm_dai_q6_mi2s_dev_probe\n");
+	kfree(dai_data);
+rtn:
+	return rc;
+}
+
+static __devexit int msm_dai_q6_mi2s_dev_remove(struct platform_device *pdev)
+{
+	snd_soc_unregister_dai(&pdev->dev);
+	return 0;
+}
+
 static int msm_dai_q6_dev_probe(struct platform_device *pdev)
 {
 	int rc, id;
@@ -1173,10 +1846,14 @@
 					  &msm_dai_q6_slimbus_tx_dai);
 		break;
 	case SLIMBUS_1_RX:
+	case SLIMBUS_3_RX:
+	case SLIMBUS_4_RX:
 		rc = snd_soc_register_dai(&pdev->dev,
 					  &msm_dai_q6_slimbus_1_rx_dai);
 		break;
 	case SLIMBUS_1_TX:
+	case SLIMBUS_3_TX:
+	case SLIMBUS_4_TX:
 		rc = snd_soc_register_dai(&pdev->dev,
 					  &msm_dai_q6_slimbus_1_tx_dai);
 		break;
@@ -1267,6 +1944,57 @@
 	},
 };
 
+static int msm_dai_mi2s_q6_probe(struct platform_device *pdev)
+{
+	int rc;
+	rc = of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev);
+	if (rc) {
+		dev_err(&pdev->dev, "%s: failed to add child nodes, rc=%d\n",
+			__func__, rc);
+	} else
+		dev_dbg(&pdev->dev, "%s: added child node\n", __func__);
+	return rc;
+}
+
+static int msm_dai_mi2s_q6_remove(struct platform_device *pdev)
+{
+	return 0;
+}
+
+static const struct of_device_id msm_dai_mi2s_dt_match[] = {
+	{ .compatible = "qcom,msm-dai-mi2s", },
+	{ }
+};
+
+MODULE_DEVICE_TABLE(of, msm_dai_mi2s_dt_match);
+
+static struct platform_driver msm_dai_mi2s_q6 = {
+	.probe  = msm_dai_mi2s_q6_probe,
+	.remove = msm_dai_mi2s_q6_remove,
+	.driver = {
+		.name = "msm-dai-mi2s",
+		.owner = THIS_MODULE,
+		.of_match_table = msm_dai_mi2s_dt_match,
+	},
+};
+
+static const struct of_device_id msm_dai_q6_mi2s_dev_dt_match[] = {
+	{ .compatible = "qcom,msm-dai-q6-mi2s", },
+	{ }
+};
+
+MODULE_DEVICE_TABLE(of, msm_dai_q6_mi2s_dev_dt_match);
+
+static struct platform_driver msm_dai_q6_mi2s_driver = {
+	.probe  = msm_dai_q6_mi2s_dev_probe,
+	.remove  = __devexit_p(msm_dai_q6_mi2s_dev_remove),
+	.driver = {
+		.name = "msm-dai-q6-mi2s",
+		.owner = THIS_MODULE,
+		.of_match_table = msm_dai_q6_mi2s_dev_dt_match,
+	},
+};
+
 static int __init msm_dai_q6_init(void)
 {
 	int rc;
@@ -1279,26 +2007,44 @@
 
 	if (rc) {
 		pr_err("%s: fail to register cpu dai driver\n", __func__);
-		platform_driver_unregister(&msm_auxpcm_dev_driver);
-		goto fail;
+		goto aux_pcm_resource_fail;
 	}
 
 	rc = platform_driver_register(&msm_dai_q6);
 	if (rc) {
 		pr_err("%s: fail to register dai q6 driver", __func__);
-		platform_driver_unregister(&msm_auxpcm_dev_driver);
-		platform_driver_unregister(&msm_auxpcm_resource_driver);
-		goto fail;
+		goto dai_q6_fail;
 	}
 
 	rc = platform_driver_register(&msm_dai_q6_dev);
 	if (rc) {
 		pr_err("%s: fail to register dai q6 dev driver", __func__);
-		platform_driver_unregister(&msm_dai_q6);
-		platform_driver_unregister(&msm_auxpcm_dev_driver);
-		platform_driver_unregister(&msm_auxpcm_resource_driver);
-		goto fail;
+		goto dai_q6_dev_fail;
 	}
+
+	rc = platform_driver_register(&msm_dai_q6_mi2s_driver);
+	if (rc) {
+		pr_err("%s: fail to register dai MI2S dev drv\n", __func__);
+		goto dai_q6_mi2s_drv_fail;
+	}
+
+	rc = platform_driver_register(&msm_dai_mi2s_q6);
+	if (rc) {
+		pr_err("%s: fail to register dai MI2S\n", __func__);
+		goto dai_mi2s_q6_fail;
+	}
+	return rc;
+
+dai_mi2s_q6_fail:
+	platform_driver_unregister(&msm_dai_q6_mi2s_driver);
+dai_q6_mi2s_drv_fail:
+	platform_driver_unregister(&msm_dai_q6_dev);
+dai_q6_dev_fail:
+	platform_driver_unregister(&msm_dai_q6);
+dai_q6_fail:
+	platform_driver_unregister(&msm_auxpcm_resource_driver);
+aux_pcm_resource_fail:
+	platform_driver_unregister(&msm_auxpcm_dev_driver);
 fail:
 	return rc;
 }
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 af1e19c..1e6fc04 100644
--- a/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c
@@ -297,6 +297,7 @@
 static int msm_pcm_open(struct snd_pcm_substream *substream)
 {
 	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_soc_pcm_runtime *soc_prtd = substream->private_data;
 	struct msm_audio *prtd;
 	int ret = 0;
 
@@ -314,8 +315,25 @@
 		kfree(prtd);
 		return -ENOMEM;
 	}
-	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
 		runtime->hw = msm_pcm_hardware_playback;
+		ret = q6asm_open_write(prtd->audio_client, FORMAT_LINEAR_PCM);
+		if (ret < 0) {
+			pr_err("%s: pcm out open failed\n", __func__);
+			q6asm_audio_client_free(prtd->audio_client);
+			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 */
 	else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
 		runtime->hw = msm_pcm_hardware_capture;
 	else {
@@ -601,25 +619,15 @@
 	struct audio_buffer *buf;
 	int dir, ret;
 
-	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
 		dir = IN;
-		pr_debug("%s Opening %d-ch PCM Write stream\n",
-			__func__, params_channels(params));
-
-		ret = q6asm_open_write(prtd->audio_client, FORMAT_LINEAR_PCM);
-		if (ret < 0) {
-			pr_err("%s: pcm out open failed\n", __func__);
-			q6asm_audio_client_free(prtd->audio_client);
-			kfree(prtd);
-			return -ENOMEM;
-		}
-	} else {
+	else {
 		dir = OUT;
 		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: pcm in open failed\n", __func__);
+			pr_err("%s: q6asm_open_read failed\n", __func__);
 			q6asm_audio_client_free(prtd->audio_client);
 			prtd->audio_client = NULL;
 			return -ENOMEM;
@@ -631,10 +639,7 @@
 	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;
 
-	pr_debug("%s: before buf alloc\n", __func__);
 	ret = q6asm_audio_client_buf_alloc_contiguous(dir,
 			prtd->audio_client,
 			runtime->hw.period_bytes_min,
@@ -644,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/msm-pcm-routing-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c
index 3db5418..17c18dd 100644
--- a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c
@@ -138,9 +138,12 @@
 	{ SLIMBUS_4_RX, 0, 0, 0, 0, 0},
 	{ SLIMBUS_4_TX, 0, 0, 0, 0, 0},
 	{ SLIMBUS_3_RX, 0, 0, 0, 0, 0},
+	{ SLIMBUS_3_TX, 0, 0, 0, 0, 0},
 	{ SLIMBUS_EXTPROC_RX, 0, 0, 0, 0, 0},
 	{ SLIMBUS_EXTPROC_RX, 0, 0, 0, 0, 0},
 	{ SLIMBUS_EXTPROC_RX, 0, 0, 0, 0, 0},
+	{ AFE_PORT_ID_QUATERNARY_MI2S_RX, 0, 0, 0, 0, 0},
+	{ AFE_PORT_ID_QUATERNARY_MI2S_TX, 0, 0, 0, 0, 0},
 };
 
 
@@ -942,6 +945,21 @@
 	msm_routing_put_audio_mixer),
 };
 
+static const struct snd_kcontrol_new quaternary_mi2s_rx_mixer_controls[] = {
+	SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_QUATERNARY_MI2S_RX ,
+	MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_QUATERNARY_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_QUATERNARY_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_QUATERNARY_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+};
+
 static const struct snd_kcontrol_new hdmi_mixer_controls[] = {
 	SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_HDMI_RX,
 	MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
@@ -1042,6 +1060,9 @@
 	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),
+	SOC_SINGLE_EXT("QUAT_MI2S_TX", MSM_BACKEND_DAI_QUATERNARY_MI2S_TX,
+		MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+		msm_routing_put_audio_mixer),
 	SOC_SINGLE_EXT("SLIM_0_TX", MSM_BACKEND_DAI_SLIMBUS_0_TX,
 		MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
 		msm_routing_put_audio_mixer),
@@ -1351,6 +1372,9 @@
 	SOC_SINGLE_EXT("SLIM_1_TX", MSM_BACKEND_DAI_MI2S_RX,
 	MSM_BACKEND_DAI_SLIMBUS_1_TX, 1, 0, msm_routing_get_port_mixer,
 	msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("MI2S_TX", MSM_BACKEND_DAI_MI2S_RX,
+	MSM_BACKEND_DAI_MI2S_TX, 1, 0, msm_routing_get_port_mixer,
+	msm_routing_put_port_mixer),
 };
 
 static const struct snd_kcontrol_new fm_switch_mixer_controls =
@@ -1581,6 +1605,18 @@
 		0, 0, 0, 0),
 	SND_SOC_DAPM_AIF_OUT("SLIM0_UL_HL", "SLIMBUS0_HOSTLESS Capture",
 		0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("SLIM1_DL_HL", "SLIMBUS1_HOSTLESS Playback",
+		0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("SLIM1_UL_HL", "SLIMBUS1_HOSTLESS Capture",
+		0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("SLIM3_DL_HL", "SLIMBUS3_HOSTLESS Playback",
+		0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("SLIM3_UL_HL", "SLIMBUS3_HOSTLESS Capture",
+		0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("SLIM4_DL_HL", "SLIMBUS4_HOSTLESS Playback",
+		0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("SLIM4_UL_HL", "SLIMBUS4_HOSTLESS Capture",
+		0, 0, 0, 0),
 	SND_SOC_DAPM_AIF_IN("INTFM_DL_HL", "INT_FM_HOSTLESS Playback",
 		0, 0, 0, 0),
 	SND_SOC_DAPM_AIF_OUT("INTFM_UL_HL", "INT_FM_HOSTLESS Capture",
@@ -1595,6 +1631,9 @@
 	SND_SOC_DAPM_AIF_OUT("MI2S_UL_HL", "MI2S_TX_HOSTLESS Capture",
 		0, 0, 0, 0),
 
+	SND_SOC_DAPM_AIF_OUT("MI2S_DL_HL", "MI2S_RX_HOSTLESS Playback",
+		0, 0, 0, 0),
+
 	/* Backend AIF */
 	/* Stream name equals to backend dai link stream name
 	*/
@@ -1604,8 +1643,13 @@
 	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("MI2S_RX", "MI2S Playback", 0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("QUAT_MI2S_RX", "Quaternary 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("MI2S_TX", "MI2S Capture", 0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("QUAT_MI2S_TX", "Quaternary 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",
 				0, 0, 0 , 0),
@@ -1641,10 +1685,17 @@
 	SND_SOC_DAPM_AIF_IN("SLIMBUS_1_TX", "Slimbus1 Capture", 0, 0, 0, 0),
 	SND_SOC_DAPM_AIF_IN("STUB_1_TX", "Stub1 Capture", 0, 0, 0, 0),
 	SND_SOC_DAPM_AIF_OUT("SLIMBUS_3_RX", "Slimbus3 Playback", 0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("SLIMBUS_3_TX", "Slimbus3 Capture", 0, 0, 0, 0),
 
 	/* Switch Definitions */
 	SND_SOC_DAPM_SWITCH("SLIMBUS_DL_HL", SND_SOC_NOPM, 0, 0,
 				&fm_switch_mixer_controls),
+	SND_SOC_DAPM_SWITCH("SLIMBUS1_DL_HL", SND_SOC_NOPM, 0, 0,
+				&fm_switch_mixer_controls),
+	SND_SOC_DAPM_SWITCH("SLIMBUS3_DL_HL", SND_SOC_NOPM, 0, 0,
+				&fm_switch_mixer_controls),
+	SND_SOC_DAPM_SWITCH("SLIMBUS4_DL_HL", SND_SOC_NOPM, 0, 0,
+				&fm_switch_mixer_controls),
 	SND_SOC_DAPM_SWITCH("PCM_RX_DL_HL", SND_SOC_NOPM, 0, 0,
 				&pcm_rx_switch_mixer_controls),
 	/* Mixer definitions */
@@ -1658,6 +1709,9 @@
 	hdmi_mixer_controls, ARRAY_SIZE(hdmi_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("QUAT_MI2S_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
+				quaternary_mi2s_rx_mixer_controls,
+				ARRAY_SIZE(quaternary_mi2s_rx_mixer_controls)),
 	SND_SOC_DAPM_MIXER("MultiMedia1 Mixer", SND_SOC_NOPM, 0, 0,
 	mmul1_mixer_controls, ARRAY_SIZE(mmul1_mixer_controls)),
 	SND_SOC_DAPM_MIXER("MultiMedia2 Mixer", SND_SOC_NOPM, 0, 0,
@@ -1801,9 +1855,16 @@
 	{"MI2S_RX Audio Mixer", "MultiMedia4", "MM_DL4"},
 	{"MI2S_RX", NULL, "MI2S_RX Audio Mixer"},
 
+	{"QUAT_MI2S_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
+	{"QUAT_MI2S_RX Audio Mixer", "MultiMedia2", "MM_DL2"},
+	{"QUAT_MI2S_RX Audio Mixer", "MultiMedia3", "MM_DL3"},
+	{"QUAT_MI2S_RX Audio Mixer", "MultiMedia4", "MM_DL4"},
+	{"QUAT_MI2S_RX", NULL, "QUAT_MI2S_RX Audio Mixer"},
+
 	{"MultiMedia1 Mixer", "PRI_TX", "PRI_I2S_TX"},
 	{"MultiMedia1 Mixer", "MI2S_TX", "MI2S_TX"},
 	{"MultiMedia2 Mixer", "MI2S_TX", "MI2S_TX"},
+	{"MultiMedia1 Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"},
 	{"MultiMedia1 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
 	{"MultiMedia1 Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"},
 	{"MultiMedia2 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
@@ -1899,11 +1960,22 @@
 	{"VOIP_UL", NULL, "Voip_Tx Mixer"},
 	{"SLIMBUS_DL_HL", "Switch", "SLIM0_DL_HL"},
 	{"SLIMBUS_0_RX", NULL, "SLIMBUS_DL_HL"},
+	{"SLIMBUS1_DL_HL", "Switch", "SLIM1_DL_HL"},
+	{"SLIMBUS_1_RX", NULL, "SLIMBUS1_DL_HL"},
+	{"SLIMBUS3_DL_HL", "Switch", "SLIM3_DL_HL"},
+	{"SLIMBUS_3_RX", NULL, "SLIMBUS3_DL_HL"},
+	{"SLIMBUS4_DL_HL", "Switch", "SLIM4_DL_HL"},
+	{"SLIMBUS_4_RX", NULL, "SLIMBUS4_DL_HL"},
 	{"SLIM0_UL_HL", NULL, "SLIMBUS_0_TX"},
+	{"SLIM1_UL_HL", NULL, "SLIMBUS_1_TX"},
+	{"SLIM3_UL_HL", NULL, "SLIMBUS_3_TX"},
+	{"SLIM4_UL_HL", NULL, "SLIMBUS_4_TX"},
 	{"INT_FM_RX", NULL, "INTFM_DL_HL"},
 	{"INTFM_UL_HL", NULL, "INT_FM_TX"},
 	{"AUX_PCM_RX", NULL, "AUXPCM_DL_HL"},
 	{"AUXPCM_UL_HL", NULL, "AUX_PCM_TX"},
+	{"MI2S_RX", NULL, "MI2S_DL_HL"},
+	{"MI2S_UL_HL", NULL, "MI2S_TX"},
 	{"PCM_RX_DL_HL", "Switch", "SLIM0_DL_HL"},
 	{"PCM_RX", NULL, "PCM_RX_DL_HL"},
 	{"MI2S_UL_HL", NULL, "MI2S_TX"},
@@ -1932,6 +2004,9 @@
 	{"SLIMBUS_1_RX Mixer", "Voice Stub", "VOICE_STUB_DL"},
 	{"SLIMBUS_1_RX", NULL, "SLIMBUS_1_RX Mixer"},
 	{"INTERNAL_BT_SCO_RX_Voice Mixer", "Voice Stub", "VOICE_STUB_DL"},
+	{"MI2S_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
+	{"MI2S_RX_Voice Mixer", "Voip", "VOIP_DL"},
+	{"MI2S_RX", NULL, "MI2S_RX_Voice Mixer"},
 	{"MI2S_RX_Voice Mixer", "Voice Stub", "VOICE_STUB_DL"},
 	{"MI2S_RX", NULL, "MI2S_RX_Voice Mixer"},
 
@@ -1954,17 +2029,27 @@
 	{"SEC_I2S_RX", NULL, "SEC_I2S_RX Port Mixer"},
 
 	{"MI2S_RX Port Mixer", "SLIM_1_TX", "SLIMBUS_1_TX"},
+	{"MI2S_RX Port Mixer", "MI2S_TX", "MI2S_TX"},
 	{"MI2S_RX", NULL, "MI2S_RX Port Mixer"},
 	/* Backend Enablement */
 
 	{"BE_OUT", NULL, "PRI_I2S_RX"},
 	{"BE_OUT", NULL, "SEC_I2S_RX"},
 	{"BE_OUT", NULL, "SLIMBUS_0_RX"},
+	{"BE_OUT", NULL, "SLIMBUS_1_RX"},
+	{"BE_OUT", NULL, "SLIMBUS_3_RX"},
+	{"BE_OUT", NULL, "SLIMBUS_4_RX"},
 	{"BE_OUT", NULL, "HDMI"},
 	{"BE_OUT", NULL, "MI2S_RX"},
+	{"BE_OUT", NULL, "QUAT_MI2S_RX"},
+
 	{"PRI_I2S_TX", NULL, "BE_IN"},
 	{"MI2S_TX", NULL, "BE_IN"},
+	{"QUAT_MI2S_TX", NULL, "BE_IN"},
 	{"SLIMBUS_0_TX", NULL, "BE_IN" },
+	{"SLIMBUS_1_TX", NULL, "BE_IN" },
+	{"SLIMBUS_3_TX", NULL, "BE_IN" },
+	{"SLIMBUS_4_TX", NULL, "BE_IN" },
 	{"BE_OUT", NULL, "INT_BT_SCO_RX"},
 	{"INT_BT_SCO_TX", NULL, "BE_IN"},
 	{"BE_OUT", NULL, "INT_FM_RX"},
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.h b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.h
index 32e18d8..be646ed 100644
--- a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.h
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.h
@@ -33,12 +33,15 @@
 
 #define LPASS_BE_MI2S_RX "MI2S_RX"
 #define LPASS_BE_MI2S_TX "MI2S_TX"
+#define LPASS_BE_QUAT_MI2S_RX "QUAT_MI2S_RX"
+#define LPASS_BE_QUAT_MI2S_TX "QUAT_MI2S_TX"
 #define LPASS_BE_STUB_RX "STUB_RX"
 #define LPASS_BE_STUB_TX "STUB_TX"
 #define LPASS_BE_SLIMBUS_1_RX "SLIMBUS_1_RX"
 #define LPASS_BE_SLIMBUS_1_TX "SLIMBUS_1_TX"
 #define LPASS_BE_STUB_1_TX "STUB_1_TX"
 #define LPASS_BE_SLIMBUS_3_RX "SLIMBUS_3_RX"
+#define LPASS_BE_SLIMBUS_3_TX "SLIMBUS_3_TX"
 #define LPASS_BE_SLIMBUS_4_RX "SLIMBUS_4_RX"
 #define LPASS_BE_SLIMBUS_4_TX "SLIMBUS_4_TX"
 
@@ -90,9 +93,12 @@
 	MSM_BACKEND_DAI_SLIMBUS_4_RX,
 	MSM_BACKEND_DAI_SLIMBUS_4_TX,
 	MSM_BACKEND_DAI_SLIMBUS_3_RX,
+	MSM_BACKEND_DAI_SLIMBUS_3_TX,
 	MSM_BACKEND_DAI_EXTPROC_RX,
 	MSM_BACKEND_DAI_EXTPROC_TX,
 	MSM_BACKEND_DAI_EXTPROC_EC_TX,
+	MSM_BACKEND_DAI_QUATERNARY_MI2S_RX,
+	MSM_BACKEND_DAI_QUATERNARY_MI2S_TX,
 	MSM_BACKEND_DAI_MAX,
 };
 
diff --git a/sound/soc/msm/qdsp6v2/q6adm.c b/sound/soc/msm/qdsp6v2/q6adm.c
index 6acc136..7267a82 100644
--- a/sound/soc/msm/qdsp6v2/q6adm.c
+++ b/sound/soc/msm/qdsp6v2/q6adm.c
@@ -34,11 +34,11 @@
 
 struct adm_ctl {
 	void *apr;
-	atomic_t copp_id[Q6_AFE_MAX_PORTS];
-	atomic_t copp_cnt[Q6_AFE_MAX_PORTS];
-	atomic_t copp_stat[Q6_AFE_MAX_PORTS];
-	u32      mem_map_handle[Q6_AFE_MAX_PORTS];
-	wait_queue_head_t wait[Q6_AFE_MAX_PORTS];
+	atomic_t copp_id[AFE_MAX_PORTS];
+	atomic_t copp_cnt[AFE_MAX_PORTS];
+	atomic_t copp_stat[AFE_MAX_PORTS];
+	u32      mem_map_handle[AFE_MAX_PORTS];
+	wait_queue_head_t wait[AFE_MAX_PORTS];
 };
 
 static struct acdb_cal_block mem_addr_audproc[MAX_AUDPROC_TYPES];
@@ -81,7 +81,7 @@
 				this_adm.apr);
 		if (this_adm.apr) {
 			apr_reset(this_adm.apr);
-			for (i = 0; i < Q6_AFE_MAX_PORTS; i++) {
+			for (i = 0; i < AFE_MAX_PORTS; i++) {
 				atomic_set(&this_adm.copp_id[i],
 							RESET_COPP_ID);
 				atomic_set(&this_adm.copp_cnt[i], 0);
@@ -107,7 +107,7 @@
 	adm_callback_debug_print(data);
 	if (data->payload_size) {
 		index = q6audio_get_port_index(data->token);
-		if (index < 0 || index >= Q6_AFE_MAX_PORTS) {
+		if (index < 0 || index >= AFE_MAX_PORTS) {
 			pr_err("%s: invalid port idx %d token %d\n",
 					__func__, index, data->token);
 			return 0;
@@ -120,10 +120,10 @@
 			}
 			switch (payload[0]) {
 			case ADM_CMD_SET_PP_PARAMS_V5:
+				pr_debug("%s: ADM_CMD_SET_PP_PARAMS_V5\n",
+					__func__);
 				if (rtac_make_adm_callback(
 					payload, data->payload_size)) {
-					pr_debug("%s: payload[0]: 0x%x\n",
-						__func__, payload[0]);
 					break;
 				}
 			case ADM_CMD_DEVICE_CLOSE_V5:
@@ -148,6 +148,20 @@
 					wake_up(&this_adm.wait[index]);
 				}
 				break;
+			case ADM_CMD_GET_PP_PARAMS_V5:
+				pr_debug("%s: ADM_CMD_GET_PP_PARAMS_V5\n",
+					__func__);
+				/* Should only come here if there is an APR */
+				/* error or malformed APR packet. Otherwise */
+				/* response will be returned as */
+				/* ADM_CMDRSP_GET_PP_PARAMS_V5 */
+				if (payload[1] != 0) {
+					pr_err("%s: ADM get param error = %d, resuming\n",
+						__func__, payload[1]);
+					rtac_make_adm_callback(payload,
+						data->payload_size);
+				}
+				break;
 			default:
 				pr_err("%s: Unknown Cmd: 0x%x\n", __func__,
 								payload[0]);
@@ -174,8 +188,11 @@
 			wake_up(&this_adm.wait[index]);
 			}
 			break;
-		case ADM_CMD_GET_PP_PARAMS_V5:
-			pr_debug("%s: ADM_CMD_GET_PP_PARAMS_V5\n", __func__);
+		case ADM_CMDRSP_GET_PP_PARAMS_V5:
+			pr_debug("%s: ADM_CMDRSP_GET_PP_PARAMS_V5\n", __func__);
+			if (payload[0] != 0)
+				pr_err("%s: ADM_CMDRSP_GET_PP_PARAMS_V5 returned error = 0x%x\n",
+					__func__, payload[0]);
 			rtac_make_adm_callback(payload,
 				data->payload_size);
 			break;
@@ -202,15 +219,15 @@
 	struct adm_cmd_set_pp_params_v5	adm_params;
 	int index = afe_get_port_index(port_id);
 	if (index < 0 || index >= AFE_MAX_PORTS) {
-		pr_err("%s: invalid port idx %d portid %d\n",
+		pr_err("%s: invalid port idx %d portid %#x\n",
 				__func__, index, port_id);
 		return 0;
 	}
 
-	pr_debug("%s: Port id %d, index %d\n", __func__, port_id, index);
+	pr_debug("%s: Port id %#x, index %d\n", __func__, port_id, index);
 
 	if (!aud_cal || aud_cal->cal_size == 0) {
-		pr_debug("%s: No ADM cal to send for port_id = %d!\n",
+		pr_debug("%s: No ADM cal to send for port_id = %#x!\n",
 			__func__, port_id);
 		result = -EINVAL;
 		goto done;
@@ -240,7 +257,7 @@
 		adm_params.payload_size);
 	result = apr_send_pkt(this_adm.apr, (uint32_t *)&adm_params);
 	if (result < 0) {
-		pr_err("%s: Set params failed port = %d payload = 0x%x\n",
+		pr_err("%s: Set params failed port = %#x payload = 0x%x\n",
 			__func__, port_id, aud_cal->cal_paddr);
 		result = -EINVAL;
 		goto done;
@@ -250,7 +267,7 @@
 		atomic_read(&this_adm.copp_stat[index]),
 		msecs_to_jiffies(TIMEOUT_MS));
 	if (!result) {
-		pr_err("%s: Set params timed out port = %d, payload = 0x%x\n",
+		pr_err("%s: Set params timed out port = %#x, payload = 0x%x\n",
 			__func__, port_id, aud_cal->cal_paddr);
 		result = -EINVAL;
 		goto done;
@@ -300,10 +317,10 @@
 	}
 
 	if (!send_adm_cal_block(port_id, &aud_cal))
-		pr_debug("%s: Audproc cal sent for port id: %d, path %d\n",
+		pr_debug("%s: Audproc cal sent for port id: %#x, path %d\n",
 			__func__, port_id, acdb_path);
 	else
-		pr_debug("%s: Audproc cal not sent for port id: %d, path %d\n",
+		pr_debug("%s: Audproc cal not sent for port id: %#x, path %d\n",
 			__func__, port_id, acdb_path);
 
 	pr_debug("%s: Sending audvol cal\n", __func__);
@@ -334,10 +351,10 @@
 	}
 
 	if (!send_adm_cal_block(port_id, &aud_cal))
-		pr_debug("%s: Audvol cal sent for port id: %d, path %d\n",
+		pr_debug("%s: Audvol cal sent for port id: %#x, path %d\n",
 			__func__, port_id, acdb_path);
 	else
-		pr_debug("%s: Audvol cal not sent for port id: %d, path %d\n",
+		pr_debug("%s: Audvol cal not sent for port id: %#x, path %d\n",
 			__func__, port_id, acdb_path);
 }
 
@@ -367,7 +384,7 @@
 		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);
+	pr_debug("%s: Port ID %#x, 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);
@@ -388,7 +405,7 @@
 	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",
+		pr_err("%s:ADM enable for port %#x failed\n",
 					__func__, port_id);
 		ret = -EINVAL;
 		goto fail_cmd;
@@ -398,7 +415,7 @@
 		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__,
+		pr_err("%s ADM connect AFE failed for port %#x\n", __func__,
 							port_id);
 		ret = -EINVAL;
 		goto fail_cmd;
@@ -418,18 +435,18 @@
 	int index;
 	int tmp_port = q6audio_get_port_id(port_id);
 
-	pr_debug("%s: port %d path:%d rate:%d mode:%d\n", __func__,
+	pr_debug("%s: port %#x path:%d rate:%d mode:%d\n", __func__,
 				port_id, path, rate, channel_mode);
 
 	port_id = q6audio_convert_virtual_to_portid(port_id);
 
 	if (q6audio_validate_port(port_id) < 0) {
-		pr_err("%s port idi[%d] is invalid\n", __func__, port_id);
+		pr_err("%s port idi[%#x] is invalid\n", __func__, port_id);
 		return -ENODEV;
 	}
 
 	index = q6audio_get_port_index(port_id);
-	pr_debug("%s: Port ID %d, index %d\n", __func__, port_id, index);
+	pr_debug("%s: Port ID %#x, index %d\n", __func__, port_id, index);
 
 	if (this_adm.apr == NULL) {
 		this_adm.apr = apr_register("ADSP", "ADM", adm_callback,
@@ -526,15 +543,15 @@
 			return -EINVAL;
 		}
 
-		pr_debug("%s: port_id=%d rate=%d"
-			"topology_id=0x%X\n", __func__,	open.endpoint_id_1, \
-				  open.sample_rate, open.topology_id);
+		pr_debug("%s: port_id=%#x rate=%d topology_id=0x%X\n",
+			__func__, open.endpoint_id_1, open.sample_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 for[%d] failed\n",
+			pr_err("%s:ADM enable for port %#x for[%d] failed\n",
 						__func__, tmp_port, port_id);
 			ret = -EINVAL;
 			goto fail_cmd;
@@ -544,7 +561,7 @@
 			atomic_read(&this_adm.copp_stat[index]),
 			msecs_to_jiffies(TIMEOUT_MS));
 		if (!ret) {
-			pr_err("%s ADM open failed for port %d"
+			pr_err("%s ADM open failed for port %#x"
 			"for [%d]\n", __func__, tmp_port, port_id);
 			ret = -EINVAL;
 			goto fail_cmd;
@@ -582,7 +599,7 @@
 
 	/* Assumes port_ids have already been validated during adm_open */
 	int index = q6audio_get_port_index(copp_id);
-	if (index < 0 || index >= Q6_AFE_MAX_PORTS) {
+	if (index < 0 || index >= AFE_MAX_PORTS) {
 		pr_err("%s: invalid port idx %d token %d\n",
 					__func__, index, copp_id);
 		return 0;
@@ -598,7 +615,7 @@
 	}
 	route = (struct adm_cmd_matrix_map_routings_v5 *)matrix_map;
 
-	pr_debug("%s: session 0x%x path:%d num_copps:%d port_id[0] :%d coppid[%d]\n",
+	pr_debug("%s: session 0x%x path:%d num_copps:%d port_id[0]:%#x coppid[%d]\n",
 		 __func__, session_id, path, num_copps, port_id[0], copp_id);
 
 	route->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
@@ -641,10 +658,10 @@
 		tmp = q6audio_get_port_index(port_id[i]);
 
 
-		if (tmp >= 0 && tmp < Q6_AFE_MAX_PORTS)
+		if (tmp >= 0 && tmp < AFE_MAX_PORTS)
 			copps_list[i] =
 					atomic_read(&this_adm.copp_id[tmp]);
-		pr_debug("%s: port_id[%d]: %d, index: %d act coppid[0x%x]\n",
+		pr_debug("%s: port_id[%#x]: %d, index: %d act coppid[0x%x]\n",
 			__func__, i, port_id[i], tmp,
 			atomic_read(&this_adm.copp_id[tmp]));
 	}
@@ -652,7 +669,7 @@
 
 	ret = apr_send_pkt(this_adm.apr, (uint32_t *)matrix_map);
 	if (ret < 0) {
-		pr_err("%s: ADM routing for port %d failed\n",
+		pr_err("%s: ADM routing for port %#x failed\n",
 					__func__, port_id[0]);
 		ret = -EINVAL;
 		goto fail_cmd;
@@ -661,7 +678,7 @@
 				atomic_read(&this_adm.copp_stat[index]),
 				msecs_to_jiffies(TIMEOUT_MS));
 	if (!ret) {
-		pr_err("%s: ADM cmd Route failed for port %d\n",
+		pr_err("%s: ADM cmd Route failed for port %#x\n",
 					__func__, port_id[0]);
 		ret = -EINVAL;
 		goto fail_cmd;
@@ -669,6 +686,11 @@
 	for (i = 0; i < num_copps; i++)
 		send_adm_cal(port_id[i], path);
 
+	for (i = 0; i < num_copps; i++)
+		rtac_add_adm_device(port_id[i],	atomic_read(&this_adm.copp_id
+			[afe_get_port_index(port_id[i])]),
+			path, session_id);
+
 fail_cmd:
 	kfree(matrix_map);
 	return ret;
@@ -702,7 +724,7 @@
 	port_id = q6audio_convert_virtual_to_portid(port_id);
 
 	if (q6audio_validate_port(port_id) < 0) {
-		pr_err("%s port id[%d] is invalid\n", __func__, port_id);
+		pr_err("%s port id[%#x] is invalid\n", __func__, port_id);
 		return -ENODEV;
 	}
 
@@ -842,10 +864,10 @@
 	if (q6audio_validate_port(port_id) < 0)
 		return -EINVAL;
 
-	pr_debug("%s port_id=%d index %d\n", __func__, port_id, index);
+	pr_debug("%s port_id=%#x index %d\n", __func__, port_id, index);
 
 	if (!(atomic_read(&this_adm.copp_cnt[index]))) {
-		pr_err("%s: copp count for port[%d]is 0\n", __func__, port_id);
+		pr_err("%s: copp count for port[%#x]is 0\n", __func__, port_id);
 
 		goto fail_cmd;
 	}
@@ -868,7 +890,7 @@
 		atomic_set(&this_adm.copp_stat[index], 0);
 
 
-		pr_debug("%s:coppid %d portid=%d index=%d coppcnt=%d\n",
+		pr_debug("%s:coppid %d portid=%#x index=%d coppcnt=%d\n",
 				__func__,
 				atomic_read(&this_adm.copp_id[index]),
 				port_id, index,
@@ -885,7 +907,7 @@
 				atomic_read(&this_adm.copp_stat[index]),
 				msecs_to_jiffies(TIMEOUT_MS));
 		if (!ret) {
-			pr_err("%s: ADM cmd Route failed for port %d\n",
+			pr_err("%s: ADM cmd Route failed for port %#x\n",
 						__func__, port_id);
 			ret = -EINVAL;
 			goto fail_cmd;
@@ -903,7 +925,7 @@
 	int i = 0;
 	this_adm.apr = NULL;
 
-	for (i = 0; i < Q6_AFE_MAX_PORTS; i++) {
+	for (i = 0; i < AFE_MAX_PORTS; i++) {
 		atomic_set(&this_adm.copp_id[i], RESET_COPP_ID);
 		atomic_set(&this_adm.copp_cnt[i], 0);
 		atomic_set(&this_adm.copp_stat[i], 0);
diff --git a/sound/soc/msm/qdsp6v2/q6afe.c b/sound/soc/msm/qdsp6v2/q6afe.c
index 4819e0a..de9841a 100644
--- a/sound/soc/msm/qdsp6v2/q6afe.c
+++ b/sound/soc/msm/qdsp6v2/q6afe.c
@@ -165,6 +165,7 @@
 	case INT_FM_RX:
 	case VOICE_PLAYBACK_TX:
 	case RT_PROXY_PORT_001_RX:
+	case AFE_PORT_ID_QUATERNARY_MI2S_RX:
 		ret = MSM_AFE_PORT_TYPE_RX;
 		break;
 
@@ -183,12 +184,13 @@
 	case VOICE_RECORD_RX:
 	case INT_BT_SCO_TX:
 	case RT_PROXY_PORT_001_TX:
+	case AFE_PORT_ID_QUATERNARY_MI2S_TX:
 		ret = MSM_AFE_PORT_TYPE_TX;
 		break;
 
 	default:
 		WARN_ON(1);
-		pr_err("%s: invalid port id %d\n", __func__, port_id);
+		pr_err("%s: invalid port id %#x\n", __func__, port_id);
 		ret = -EINVAL;
 	}
 
@@ -283,10 +285,10 @@
 		(port_id == RT_PROXY_DAI_001_TX))
 		port_id = VIRTUAL_ID_TO_PORTID(port_id);
 
-	pr_debug("%s: port id: %d\n", __func__, port_id);
+	pr_debug("%s: port id: %#x\n", __func__, port_id);
 	index = q6audio_get_port_index(port_id);
 	if (q6audio_validate_port(port_id) < 0) {
-		pr_err("%s: port id: %d\n", __func__, port_id);
+		pr_err("%s: port id: %#x\n", __func__, port_id);
 		return -EINVAL;
 	}
 
@@ -295,7 +297,7 @@
 		return ret;
 
 	if (q6audio_validate_port(port_id) < 0) {
-		pr_err("%s: Failed : Invalid Port id = %d\n", __func__,
+		pr_err("%s: Failed : Invalid Port id = %#x\n", __func__,
 				port_id);
 		ret = -EINVAL;
 		goto fail_cmd;
@@ -321,6 +323,12 @@
 	case SECONDARY_I2S_TX:
 	case MI2S_RX:
 	case MI2S_TX:
+	case AFE_PORT_ID_SECONDARY_MI2S_RX:
+	case AFE_PORT_ID_SECONDARY_MI2S_TX:
+	case AFE_PORT_ID_TERTIARY_MI2S_RX:
+	case AFE_PORT_ID_TERTIARY_MI2S_TX:
+	case AFE_PORT_ID_QUATERNARY_MI2S_RX:
+	case AFE_PORT_ID_QUATERNARY_MI2S_TX:
 		cfg_type = AFE_PARAM_ID_I2S_CONFIG;
 		break;
 	case HDMI_RX:
@@ -370,7 +378,7 @@
 	atomic_set(&this_afe.status, 0);
 	ret = apr_send_pkt(this_afe.apr, (uint32_t *) &config);
 	if (ret < 0) {
-		pr_err("%s: AFE enable for port %d failed\n", __func__,
+		pr_err("%s: AFE enable for port %#x failed\n", __func__,
 				port_id);
 		ret = -EINVAL;
 		goto fail_cmd;
@@ -407,7 +415,7 @@
 	ret = apr_send_pkt(this_afe.apr, (uint32_t *) &start);
 
 	if (IS_ERR_VALUE(ret)) {
-		pr_err("%s: AFE enable for port %d failed\n", __func__,
+		pr_err("%s: AFE enable for port %#x failed\n", __func__,
 				port_id);
 		ret = -EINVAL;
 		goto fail_cmd;
@@ -468,7 +476,10 @@
 	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 AFE_PORT_ID_QUATERNARY_MI2S_RX:
+		return IDX_AFE_PORT_ID_QUATERNARY_MI2S_RX;
+	case AFE_PORT_ID_QUATERNARY_MI2S_TX:
+		return IDX_AFE_PORT_ID_QUATERNARY_MI2S_TX;
 	default: return -EINVAL;
 	}
 }
@@ -630,6 +641,11 @@
 	int ret = 0;
 	int index = 0;
 
+	if (rx_port == MI2S_RX)
+		rx_port = AFE_PORT_ID_PRIMARY_MI2S_RX;
+	if (tx_port == MI2S_TX)
+		tx_port = AFE_PORT_ID_PRIMARY_MI2S_TX;
+
 	ret = afe_q6_interface_prepare();
 	if (ret != 0)
 		return ret;
diff --git a/sound/soc/msm/qdsp6v2/q6asm.c b/sound/soc/msm/qdsp6v2/q6asm.c
index 875bf47..0ddaabe 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>
@@ -933,6 +932,10 @@
 			__func__, payload[0], payload[1]);
 	if (data->opcode == APR_BASIC_RSP_RESULT) {
 		token = data->token;
+		if (payload[1] != 0) {
+			pr_err("%s: cmd = 0x%x returned error = 0x%x\n",
+				__func__, payload[0], payload[1]);
+		}
 		switch (payload[0]) {
 		case ASM_STREAM_CMD_SET_PP_PARAMS_V2:
 			if (rtac_make_asm_callback(ac->session, payload,
@@ -966,6 +969,20 @@
 				ac->cb(data->opcode, data->token,
 					(uint32_t *)data->payload, ac->priv);
 			break;
+		case ASM_STREAM_CMD_GET_PP_PARAMS_V2:
+			pr_debug("%s: ASM_STREAM_CMD_GET_PP_PARAMS_V2\n",
+				__func__);
+			/* Should only come here if there is an APR */
+			/* error or malformed APR packet. Otherwise */
+			/* response will be returned as */
+			/* ASM_STREAM_CMDRSP_GET_PP_PARAMS_V2 */
+			if (payload[1] != 0) {
+				pr_err("%s: ASM get param error = %d, resuming\n",
+					__func__, payload[1]);
+				rtac_make_asm_callback(ac->session, payload,
+							data->payload_size);
+			}
+			break;
 		default:
 			pr_debug("%s:command[0x%x] not expecting rsp\n",
 							__func__, payload[0]);
@@ -1009,6 +1026,10 @@
 		break;
 	}
 	case ASM_STREAM_CMDRSP_GET_PP_PARAMS_V2:
+		pr_debug("%s: ASM_STREAM_CMDRSP_GET_PP_PARAMS_V2\n", __func__);
+			if (payload[0] != 0)
+				pr_err("%s: ASM_STREAM_CMDRSP_GET_PP_PARAMS_V2 returned error = 0x%x\n",
+					__func__, payload[0]);
 		rtac_make_asm_callback(ac->session, payload,
 			data->payload_size);
 		break;
@@ -2348,7 +2369,7 @@
 		buf_node = list_entry(ptr, struct asm_buffer_node,
 						list);
 		if (buf_node->buf_addr_lsw == buf_add) {
-			pr_info("%s: Found the element\n", __func__);
+			pr_debug("%s: Found the element\n", __func__);
 			mem_unmap.mem_map_handle = buf_node->mmap_hdl;
 			break;
 		}
@@ -2366,7 +2387,7 @@
 	rc = wait_event_timeout(ac->cmd_wait,
 			(atomic_read(&ac->cmd_state) == 0), 5 * HZ);
 	if (!rc) {
-		pr_err("timeout. waited for memory_map\n");
+		pr_err("timeout. waited for memory_unmap\n");
 		rc = -EINVAL;
 		goto fail_cmd;
 	}
@@ -2376,6 +2397,7 @@
 		if (buf_node->buf_addr_lsw == buf_add) {
 			list_del(&buf_node->list);
 			kfree(buf_node);
+			break;
 		}
 	}
 
diff --git a/sound/soc/msm/qdsp6v2/q6audio-v2.c b/sound/soc/msm/qdsp6v2/q6audio-v2.c
index 8c524fa..033cb8e 100644
--- a/sound/soc/msm/qdsp6v2/q6audio-v2.c
+++ b/sound/soc/msm/qdsp6v2/q6audio-v2.c
@@ -48,7 +48,10 @@
 	case INT_FM_TX: return IDX_INT_FM_TX;
 	case RT_PROXY_PORT_001_RX: return IDX_RT_PROXY_PORT_001_RX;
 	case RT_PROXY_PORT_001_TX: return IDX_RT_PROXY_PORT_001_TX;
-
+	case AFE_PORT_ID_QUATERNARY_MI2S_RX:
+		return IDX_AFE_PORT_ID_QUATERNARY_MI2S_RX;
+	case AFE_PORT_ID_QUATERNARY_MI2S_TX:
+		return IDX_AFE_PORT_ID_QUATERNARY_MI2S_TX;
 	default: return -EINVAL;
 	}
 }
@@ -82,6 +85,10 @@
 	case INT_FM_TX: return AFE_PORT_ID_INTERNAL_FM_TX;
 	case RT_PROXY_PORT_001_RX: return AFE_PORT_ID_RT_PROXY_PORT_001_RX;
 	case RT_PROXY_PORT_001_TX: return AFE_PORT_ID_RT_PROXY_PORT_001_TX;
+	case AFE_PORT_ID_QUATERNARY_MI2S_RX:
+			     return AFE_PORT_ID_QUATERNARY_MI2S_RX;
+	case AFE_PORT_ID_QUATERNARY_MI2S_TX:
+			     return AFE_PORT_ID_QUATERNARY_MI2S_TX;
 
 	default: return -EINVAL;
 	}
@@ -138,6 +145,8 @@
 	case INT_FM_TX:
 	case RT_PROXY_PORT_001_RX:
 	case RT_PROXY_PORT_001_TX:
+	case AFE_PORT_ID_QUATERNARY_MI2S_RX:
+	case AFE_PORT_ID_QUATERNARY_MI2S_TX:
 	{
 		ret = 0;
 		break;
diff --git a/sound/soc/msm/qdsp6v2/q6voice.c b/sound/soc/msm/qdsp6v2/q6voice.c
index 1f6dbf1..b799e59 100644
--- a/sound/soc/msm/qdsp6v2/q6voice.c
+++ b/sound/soc/msm/qdsp6v2/q6voice.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,6 +21,7 @@
 #include <mach/qdsp6v2/audio_acdb.h>
 #include <mach/qdsp6v2/rtac.h>
 #include <mach/socinfo.h>
+#include <mach/qdsp6v2/apr_tal.h>
 
 #include "sound/apr_audio-v2.h"
 #include "sound/q6afe-v2.h"
@@ -42,6 +43,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 +57,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 +65,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 +84,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) {
@@ -189,6 +209,8 @@
 
 static int voice_apr_register(void)
 {
+	void *modem_mvm, *modem_cvs, *modem_cvp;
+
 	pr_debug("%s\n", __func__);
 
 	mutex_lock(&common.common_lock);
@@ -205,6 +227,18 @@
 			pr_err("%s: Unable to register MVM\n", __func__);
 			goto err;
 		}
+
+		/*
+		 * Register with modem for SSR callback. The APR handle
+		 * is not stored since it is used only to receive notifications
+		 * and not for communication
+		 */
+		modem_mvm = apr_register("MODEM", "MVM",
+						qdsp_mvm_callback,
+						0xFFFFFFFF, &common);
+		if (modem_mvm == NULL)
+			pr_err("%s: Unable to register MVM for MODEM\n",
+					__func__);
 	}
 
 	if (common.apr_q6_cvs == NULL) {
@@ -218,6 +252,18 @@
 			pr_err("%s: Unable to register CVS\n", __func__);
 			goto err;
 		}
+		rtac_set_voice_handle(RTAC_CVS, common.apr_q6_cvs);
+		/*
+		 * Register with modem for SSR callback. The APR handle
+		 * is not stored since it is used only to receive notifications
+		 * and not for communication
+		 */
+		modem_cvs = apr_register("MODEM", "CVS",
+						qdsp_cvs_callback,
+						0xFFFFFFFF, &common);
+		 if (modem_cvs == NULL)
+			pr_err("%s: Unable to register CVS for MODEM\n",
+					__func__);
 
 	}
 
@@ -232,6 +278,18 @@
 			pr_err("%s: Unable to register CVP\n", __func__);
 			goto err;
 		}
+		rtac_set_voice_handle(RTAC_CVP, common.apr_q6_cvp);
+		/*
+		 * Register with modem for SSR callback. The APR handle
+		 * is not stored since it is used only to receive notifications
+		 * and not for communication
+		 */
+		modem_cvp = apr_register("MODEM", "CVP",
+						qdsp_cvp_callback,
+						0xFFFFFFFF, &common);
+		if (modem_cvp == NULL)
+			pr_err("%s: Unable to register CVP for MODEM\n",
+					__func__);
 
 	}
 
@@ -243,6 +301,7 @@
 	if (common.apr_q6_cvs != NULL) {
 		apr_deregister(common.apr_q6_cvs);
 		common.apr_q6_cvs = NULL;
+		rtac_set_voice_handle(RTAC_CVS, NULL);
 	}
 	if (common.apr_q6_mvm != NULL) {
 		apr_deregister(common.apr_q6_mvm);
@@ -586,8 +645,9 @@
 	cvs_handle = voice_get_cvs_handle(v);
 
 	/* MVM, CVS sessions are destroyed only for Full control sessions. */
-	if (is_voip_session(v->session_id)) {
-		pr_debug("%s: MVM detach stream\n", __func__);
+	if (is_voip_session(v->session_id) || v->voc_state == VOC_ERROR) {
+		pr_debug("%s: MVM detach stream, VOC_STATE: %d\n", __func__,
+				v->voc_state);
 
 		/* Detach voice stream. */
 		detach_stream.hdr.hdr_field =
@@ -749,6 +809,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 +1431,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 +1994,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 +2020,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 +2162,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 +2195,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);
@@ -1472,6 +2217,10 @@
 	if (v->rec_info.rec_enable)
 		voice_cvs_start_record(v, v->rec_info.rec_mode);
 
+	rtac_add_voice(voice_get_cvs_handle(v),
+		voice_get_cvp_handle(v),
+		v->dev_rx.port_id, v->dev_tx.port_id,
+		v->session_id);
 
 	return 0;
 
@@ -1789,6 +2538,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,
@@ -1817,6 +2571,7 @@
 		goto fail;
 	}
 
+	rtac_remove_voice(voice_get_cvs_handle(v));
 	cvp_handle = 0;
 	voice_set_cvp_handle(v, cvp_handle);
 	return 0;
@@ -1997,20 +2752,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 +2772,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 +2805,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 +2824,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)
@@ -2562,6 +3327,7 @@
 	mutex_lock(&v->lock);
 
 	if (v->voc_state == VOC_RUN) {
+		rtac_remove_voice(voice_get_cvs_handle(v));
 		/* send cmd to dsp to disable vocproc */
 		ret = voice_send_disable_vocproc_cmd(v);
 		if (ret < 0) {
@@ -2569,6 +3335,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,16 +3367,40 @@
 			goto fail;
 		}
 
-	ret = voice_send_enable_vocproc_cmd(v);
-	if (ret < 0) {
-		pr_err("%s: enable vocproc failed %d\n", __func__, ret);
-		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);
 
-	/* Send tty mode if tty device is used */
-	voice_send_tty_mode_cmd(v);
+		ret = voice_send_enable_vocproc_cmd(v);
+		if (ret < 0) {
+			pr_err("%s: enable vocproc failed %d\n", __func__, ret);
+			goto fail;
+		}
 
-	v->voc_state = VOC_RUN;
+		/* 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);
+
+		rtac_add_voice(voice_get_cvs_handle(v),
+			voice_get_cvp_handle(v),
+			v->dev_rx.port_id, v->dev_tx.port_id,
+			v->session_id);
+		v->voc_state = VOC_RUN;
 	}
 
 fail:
@@ -2770,7 +3563,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 +3608,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;
@@ -2948,7 +3753,9 @@
 
 	mutex_lock(&v->lock);
 
-	if (v->voc_state == VOC_RUN) {
+	if (v->voc_state == VOC_RUN || v->voc_state == VOC_ERROR) {
+		pr_debug("%s: VOC_STATE: %d\n", __func__, v->voc_state);
+
 		ret = voice_destroy_vocproc(v);
 		if (ret < 0)
 			pr_err("%s:  destroy voice failed\n", __func__);
@@ -2973,6 +3780,13 @@
 
 	mutex_lock(&v->lock);
 
+	if (v->voc_state == VOC_ERROR) {
+		pr_debug("%s: VOC in ERR state\n", __func__);
+
+		voice_destroy_mvm_cvs_session(v);
+		v->voc_state = VOC_INIT;
+	}
+
 	if ((v->voc_state == VOC_INIT) ||
 		(v->voc_state == VOC_RELEASE)) {
 		ret = voice_apr_register();
@@ -2985,8 +3799,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);
@@ -3050,6 +3877,7 @@
 	struct common_data *c = NULL;
 	struct voice_data *v = NULL;
 	int i = 0;
+	uint16_t session_id = 0;
 
 	if ((data == NULL) || (priv == NULL)) {
 		pr_err("%s: data or priv is NULL\n", __func__);
@@ -3058,6 +3886,36 @@
 
 	c = priv;
 
+	pr_debug("%s: Payload Length = %d, opcode=%x\n", __func__,
+		data->payload_size, data->opcode);
+
+	if (data->opcode == RESET_EVENTS) {
+
+		if (data->reset_proc == APR_DEST_MODEM) {
+			pr_debug("%s: Received MODEM reset event\n", __func__);
+
+			session_id = voc_get_session_id(VOICE_SESSION_NAME);
+			v = voice_get_session(session_id);
+			if (v != NULL)
+				v->voc_state = VOC_ERROR;
+
+			session_id = voc_get_session_id(VOLTE_SESSION_NAME);
+			v = voice_get_session(session_id);
+			if (v != NULL)
+				v->voc_state = VOC_ERROR;
+		} else {
+			pr_debug("%s: Reset event received in Voice service\n",
+				__func__);
+			apr_reset(c->apr_q6_mvm);
+			c->apr_q6_mvm = NULL;
+
+			/* Sub-system restart is applicable to all sessions. */
+			for (i = 0; i < MAX_VOC_SESSIONS; i++)
+				c->voice[i].mvm_handle = 0;
+		}
+		return 0;
+	}
+
 	pr_debug("%s: session_id 0x%x\n", __func__, data->dest_port);
 
 	v = voice_get_session(data->dest_port);
@@ -3067,23 +3925,6 @@
 		return -EINVAL;
 	}
 
-	pr_debug("%s: Payload Length = %d, opcode=%x\n", __func__,
-		data->payload_size, data->opcode);
-
-	if (data->opcode == RESET_EVENTS) {
-		pr_debug("%s: Reset event received in Voice service\n",
-			 __func__);
-
-		apr_reset(c->apr_q6_mvm);
-		c->apr_q6_mvm = NULL;
-
-		/* Sub-system restart is applicable to all sessions. */
-		for (i = 0; i < MAX_VOC_SESSIONS; i++)
-			c->voice[i].mvm_handle = 0;
-
-		return 0;
-	}
-
 	if (data->opcode == APR_BASIC_RSP_RESULT) {
 		if (data->payload_size) {
 			ptr = data->payload;
@@ -3132,7 +3973,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 +3983,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;
@@ -3152,6 +4009,7 @@
 	struct common_data *c = NULL;
 	struct voice_data *v = NULL;
 	int i = 0;
+	uint16_t session_id = 0;
 
 	if ((data == NULL) || (priv == NULL)) {
 		pr_err("%s: data or priv is NULL\n", __func__);
@@ -3161,6 +4019,35 @@
 	c = priv;
 
 	pr_debug("%s: session_id 0x%x\n", __func__, data->dest_port);
+	pr_debug("%s: Payload Length = %d, opcode=%x\n", __func__,
+		data->payload_size, data->opcode);
+
+	if (data->opcode == RESET_EVENTS) {
+		if (data->reset_proc == APR_DEST_MODEM) {
+			pr_debug("%s: Received Modem reset event\n", __func__);
+
+			session_id = voc_get_session_id(VOICE_SESSION_NAME);
+			v = voice_get_session(session_id);
+			if (v != NULL)
+				v->voc_state = VOC_ERROR;
+
+			session_id = voc_get_session_id(VOLTE_SESSION_NAME);
+			v = voice_get_session(session_id);
+			if (v != NULL)
+				v->voc_state = VOC_ERROR;
+		} else {
+			pr_debug("%s: Reset event received in Voice service\n",
+				 __func__);
+
+			apr_reset(c->apr_q6_cvs);
+			c->apr_q6_cvs = NULL;
+
+			/* Sub-system restart is applicable to all sessions. */
+			for (i = 0; i < MAX_VOC_SESSIONS; i++)
+				c->voice[i].cvs_handle = 0;
+		}
+		return 0;
+	}
 
 	v = voice_get_session(data->dest_port);
 	if (v == NULL) {
@@ -3169,28 +4056,15 @@
 		return -EINVAL;
 	}
 
-	pr_debug("%s: Payload Length = %d, opcode=%x\n", __func__,
-		data->payload_size, data->opcode);
-
-	if (data->opcode == RESET_EVENTS) {
-		pr_debug("%s: Reset event received in Voice service\n",
-			 __func__);
-
-		apr_reset(c->apr_q6_cvs);
-		c->apr_q6_cvs = NULL;
-
-		/* Sub-system restart is applicable to all sessions. */
-		for (i = 0; i < MAX_VOC_SESSIONS; i++)
-			c->voice[i].cvs_handle = 0;
-
-		return 0;
-	}
-
 	if (data->opcode == APR_BASIC_RSP_RESULT) {
 		if (data->payload_size) {
 			ptr = data->payload;
 
 			pr_info("%x %x\n", ptr[0], ptr[1]);
+			if (ptr[1] != 0) {
+				pr_err("%s: cmd = 0x%x returned error = 0x%x\n",
+					__func__, ptr[0], ptr[1]);
+			}
 			/*response from  CVS */
 			switch (ptr[0]) {
 			case VSS_ISTREAM_CMD_CREATE_PASSIVE_CONTROL_SESSION:
@@ -3204,14 +4078,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:
@@ -3227,6 +4101,24 @@
 				wake_up(&v->cvs_wait);
 				break;
 			case VOICE_CMD_SET_PARAM:
+				pr_debug("%s: VOICE_CMD_SET_PARAM\n", __func__);
+				rtac_make_voice_callback(RTAC_CVS, ptr,
+							data->payload_size);
+				break;
+			case VOICE_CMD_GET_PARAM:
+				pr_debug("%s: VOICE_CMD_GET_PARAM\n",
+					__func__);
+				/* Should only come here if there is an APR */
+				/* error or malformed APR packet. Otherwise */
+				/* response will be returned as */
+				/* VOICE_EVT_GET_PARAM_ACK */
+				if (ptr[1] != 0) {
+					pr_err("%s: CVP get param error = %d, resuming\n",
+						__func__, ptr[1]);
+					rtac_make_voice_callback(RTAC_CVP,
+						data->payload,
+						data->payload_size);
+				}
 				break;
 			default:
 				pr_debug("%s: cmd = 0x%x\n", __func__, ptr[0]);
@@ -3342,7 +4234,16 @@
 		pr_debug("Recd VSS_ISTREAM_EVT_NOT_READY\n");
 	} else if (data->opcode == VSS_ISTREAM_EVT_READY) {
 		pr_debug("Recd VSS_ISTREAM_EVT_READY\n");
-	} else
+	} else if (data->opcode ==  VOICE_EVT_GET_PARAM_ACK) {
+		pr_debug("%s: VOICE_EVT_GET_PARAM_ACK\n", __func__);
+		ptr = data->payload;
+		if (ptr[0] != 0) {
+			pr_err("%s: VOICE_EVT_GET_PARAM_ACK returned error = 0x%x\n",
+				__func__, ptr[0]);
+		}
+		rtac_make_voice_callback(RTAC_CVS, data->payload,
+					data->payload_size);
+	}  else
 		pr_err("Unknown opcode 0x%x\n", data->opcode);
 
 fail:
@@ -3355,6 +4256,7 @@
 	struct common_data *c = NULL;
 	struct voice_data *v = NULL;
 	int i = 0;
+	uint16_t session_id = 0;
 
 	if ((data == NULL) || (priv == NULL)) {
 		pr_err("%s: data or priv is NULL\n", __func__);
@@ -3363,6 +4265,33 @@
 
 	c = priv;
 
+	if (data->opcode == RESET_EVENTS) {
+		if (data->reset_proc == APR_DEST_MODEM) {
+			pr_debug("%s: Received Modem reset event\n", __func__);
+
+			session_id = voc_get_session_id(VOICE_SESSION_NAME);
+			v = voice_get_session(session_id);
+			if (v != NULL)
+				v->voc_state = VOC_ERROR;
+
+			session_id = voc_get_session_id(VOLTE_SESSION_NAME);
+			v = voice_get_session(session_id);
+			if (v != NULL)
+				v->voc_state = VOC_ERROR;
+		} else {
+			pr_debug("%s: Reset event received in Voice service\n",
+				 __func__);
+
+			apr_reset(c->apr_q6_cvp);
+			c->apr_q6_cvp = NULL;
+
+			/* Sub-system restart is applicable to all sessions. */
+			for (i = 0; i < MAX_VOC_SESSIONS; i++)
+				c->voice[i].cvp_handle = 0;
+		}
+		return 0;
+	}
+
 	v = voice_get_session(data->dest_port);
 	if (v == NULL) {
 		pr_err("%s: v is NULL\n", __func__);
@@ -3370,28 +4299,15 @@
 		return -EINVAL;
 	}
 
-	pr_debug("%s: Payload Length = %d, opcode=%x\n", __func__,
-		data->payload_size, data->opcode);
-
-	if (data->opcode == RESET_EVENTS) {
-		pr_debug("%s: Reset event received in Voice service\n",
-			 __func__);
-
-		apr_reset(c->apr_q6_cvp);
-		c->apr_q6_cvp = NULL;
-
-		/* Sub-system restart is applicable to all sessions. */
-		for (i = 0; i < MAX_VOC_SESSIONS; i++)
-			c->voice[i].cvp_handle = 0;
-
-		return 0;
-	}
-
 	if (data->opcode == APR_BASIC_RSP_RESULT) {
 		if (data->payload_size) {
 			ptr = data->payload;
 
 			pr_info("%x %x\n", ptr[0], ptr[1]);
+			if (ptr[1] != 0) {
+				pr_err("%s: cmd = 0x%x returned error = 0x%x\n",
+					__func__, ptr[0], ptr[1]);
+			}
 			switch (ptr[0]) {
 			case VSS_IVOCPROC_CMD_CREATE_FULL_CONTROL_SESSION_V2:
 			/*response from  CVP */
@@ -3410,17 +4326,37 @@
 			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;
 			case VOICE_CMD_SET_PARAM:
+				pr_debug("%s: VOICE_CMD_SET_PARAM\n", __func__);
+				rtac_make_voice_callback(RTAC_CVP, ptr,
+							data->payload_size);
+				break;
+			case VOICE_CMD_GET_PARAM:
+				pr_debug("%s: VOICE_CMD_GET_PARAM\n",
+					__func__);
+				/* Should only come here if there is an APR */
+				/* error or malformed APR packet. Otherwise */
+				/* response will be returned as */
+				/* VOICE_EVT_GET_PARAM_ACK */
+				if (ptr[1] != 0) {
+					pr_err("%s: CVP get param error = %d, resuming\n",
+						__func__, ptr[1]);
+					rtac_make_voice_callback(RTAC_CVP,
+						data->payload,
+						data->payload_size);
+				}
 				break;
 			default:
 				pr_debug("%s: not match cmd = 0x%x\n",
@@ -3428,6 +4364,15 @@
 				break;
 			}
 		}
+	} else if (data->opcode ==  VOICE_EVT_GET_PARAM_ACK) {
+		pr_debug("%s: VOICE_EVT_GET_PARAM_ACK\n", __func__);
+		ptr = data->payload;
+		if (ptr[0] != 0) {
+			pr_err("%s: VOICE_EVT_GET_PARAM_ACK returned error = 0x%x\n",
+				__func__, ptr[0]);
+		}
+		rtac_make_voice_callback(RTAC_CVP, data->payload,
+			data->payload_size);
 	}
 	return 0;
 }
@@ -3564,6 +4509,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 +4625,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..aef463f 100644
--- a/sound/soc/msm/qdsp6v2/q6voice.h
+++ b/sound/soc/msm/qdsp6v2/q6voice.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
@@ -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
@@ -65,6 +67,7 @@
 	VOC_RUN,
 	VOC_CHANGE,
 	VOC_RELEASE,
+	VOC_ERROR,
 };
 
 struct mem_buffer {
@@ -437,9 +440,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 +548,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 +684,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 +730,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 +765,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 +822,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,12 +885,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
-
 #define VSS_IVOCPROC_CMD_CREATE_FULL_CONTROL_SESSION_V2	0x000112BF
 
 struct vss_ivocproc_cmd_create_full_control_session_v2_t {
@@ -941,39 +973,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 +1046,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 +1186,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 dc851ce..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;
 
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c
index 4c6a5a4..f02e5c5 100644
--- a/tools/perf/util/parse-events.c
+++ b/tools/perf/util/parse-events.c
@@ -297,9 +297,37 @@
 {
 	u64 config = evsel->attr.config;
 	int type = evsel->attr.type;
+	char *buf;
+	size_t buf_sz;
 
-	if (evsel->name)
+	if (evsel->name) {
+		/* Make new space for the modifier bits. */
+		buf_sz = strlen(evsel->name) + 3;
+		buf = malloc(buf_sz);
+		if (!buf)
+			/*
+			 * Always return what was already in 'name'.
+			 */
+			return evsel->name;
+
+		strlcpy(buf, evsel->name, buf_sz);
+
+		free(evsel->name);
+
+		evsel->name = buf;
+
+		/* User mode profiling. */
+		if (!evsel->attr.exclude_user && evsel->attr.exclude_kernel)
+			strlcpy(&evsel->name[strlen(evsel->name)], ":u",
+					buf_sz);
+		/* Kernel mode profiling. */
+		else if (!evsel->attr.exclude_kernel &&
+				evsel->attr.exclude_user)
+			strlcpy(&evsel->name[strlen(evsel->name)], ":k",
+					buf_sz);
+
 		return evsel->name;
+	}
 
 	return __event_name(type, config, NULL);
 }