Merge "msm_fb: display: switch blt mode on/off without turning off TG" into msm-3.4
diff --git a/Documentation/devicetree/bindings/iommu/msm_iommu.txt b/Documentation/devicetree/bindings/iommu/msm_iommu.txt
index e9fb1a2..f5a2590 100644
--- a/Documentation/devicetree/bindings/iommu/msm_iommu.txt
+++ b/Documentation/devicetree/bindings/iommu/msm_iommu.txt
@@ -16,6 +16,10 @@
   - qcom,iommu-smt-size : Number of SMR entries in the SMT of this HW block
   - vdd-supply : vdd-supply: phandle to GDSC regulator controlling this IOMMU.
 
+Optional properties:
+- qcom,needs-alt-core-clk : boolean to enable the secondary core clock for
+  access to the IOMMU configuration registers
+
 Example:
 
         qcom,iommu@fda64000 {
diff --git a/Documentation/devicetree/bindings/mmc/msm_sdcc.txt b/Documentation/devicetree/bindings/mmc/msm_sdcc.txt
index a8de90f..c674a13 100644
--- a/Documentation/devicetree/bindings/mmc/msm_sdcc.txt
+++ b/Documentation/devicetree/bindings/mmc/msm_sdcc.txt
@@ -16,10 +16,8 @@
 Optional Properties:
 	- cell-index - defines slot ID.
 	- qcom,sdcc-bus-width - defines the bus I/O width that controller supports.
-	- qcom,sdcc-wp-gpio - defines write protect switch gpio.
-	- qcom,sdcc-wp-polarity - specifies the polarity of wp switch.
-	- qcom,sdcc-cd-gpio - defines card detect gpio number.
-	- qcom,sdcc-cd-polarity - specifies the polarity of cd gpio.
+	- 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
 				hot pluggable or hard wired.
 	- qcom,sdcc-disable_cmd23 - disable sending CMD23 to card when controller can't support it.
diff --git a/Documentation/devicetree/bindings/mtd/msm_qpic_nand.txt b/Documentation/devicetree/bindings/mtd/msm_qpic_nand.txt
new file mode 100644
index 0000000..bddbbae
--- /dev/null
+++ b/Documentation/devicetree/bindings/mtd/msm_qpic_nand.txt
@@ -0,0 +1,51 @@
+Qualcomm Parallel Interface controller (QPIC) for NAND devices
+
+Required properties:
+- compatible : "qcom,msm-nand".
+- reg : should specify QPIC NANDc and BAM physical address range.
+- reg-names : should specify relevant names to each reg property defined.
+- interrupts : should specify QPIC/BAM interrupt numbers.
+- interrupt-names : should specify relevant names to each interrupts property
+  defined.
+
+MTD flash partition layout for NAND devices -
+
+Each partition is represented as a sub-node of the qcom,mtd-partitions device.
+Each node's name represents the name of the corresponding partition.
+
+Required properties:
+- reg : The partition offset and size
+- label : The label / name for this partition.
+
+Optional properties:
+- read-only: This parameter, if present, indicates that this partition
+  should only be mounted read-only.
+
+Examples:
+
+	qcom,nand@f9af0000 {
+		compatible = "qcom,msm-nand";
+		reg = <0xf9af0000 0x1000>,
+		      <0xf9ac4000 0x8000>;
+		reg-names = "nand_phys",
+			    "bam_phys";
+		interrupts = <0 279 0>;
+		interrupt-names = "bam_irq";
+	};
+
+       qcom,mtd-partitions {
+	       #address-cells = <1>;
+	       #size-cells = <1>;
+               partition@0 {
+                       label = "boot";
+                       reg = <0x00000000 0x1000>;
+               };
+               partition@00020000 {
+                       label = "userdata";
+                       reg = <0x00020000 0x1000>;
+               };
+               partition@00040000 {
+                       label = "system";
+                       reg = <0x00040000 0x1000>;
+               };
+       };
diff --git a/Documentation/mtd/devices/msm_qpic_nand.txt b/Documentation/mtd/devices/msm_qpic_nand.txt
new file mode 100644
index 0000000..301e823
--- /dev/null
+++ b/Documentation/mtd/devices/msm_qpic_nand.txt
@@ -0,0 +1,296 @@
+Introduction
+============
+
+In MDM9x25, new NAND controller(NANDc) has been added and it has the
+following major changes as compared to its previous version -
+
+1. It includes Secured BAM-Lite and the support for ADM(Application Data Mover)
+has been removed.
+
+2. It includes 4 bit BCH ECC and the support for 4 bit Reed Solomon ECC has
+been removed.
+
+3. The support for Dual NAND controllers has been removed and thus the
+software features like ping-pong mode and interleave mode are deprecated.
+
+4. It includes support for dual buffers in case of read and one dedicated
+write buffer to each processor (Modem and Apps).
+
+This new NAND driver takes care of all the above new hardware changes. In
+addition to the above hardware changes, it also takes care of software device
+tree changes.
+
+Hardware description
+====================
+
+The NANDc Core:
+---------------
+Qualcomm Parallel Interface Controller (QPIC), formerly named EBI2, is a
+wrapper module which integrates a NAND controller core and a LCD controller
+core and multiplexes their access to shared parallel interfaces pins. Both
+controller cores are accessible to processors (Modem and Apps), and share
+master access to the Peripheral NoC (Network on Chip) via a BAM module.
+
+In MDM9x25, QPIC is located on the peripheral NoC, connected via a 32-bit AHB
+Master port and a 32-bit AHB Slave Port. The NANDc register interface goes
+through AHB Slave Port and data transfers using BAM goes through AHB Master
+Port. The NAND Controller (NANDc) is a hardware core which manages the access
+to an off-chip NAND device.
+
+BAM-Lite:
+---------
+BAM(Bus Access Manager) can transfer data between a peripheral and memory,
+or between two peripherals in a BAM to BAM mode. Each BAM contains multiple
+DMA channels, called pipes. A pipe provides a unidirectional data transfer
+engine, capable of either receiving data in consumer mode, or transmitting
+data in producer mode. The consumer fetches the data from the source system
+memory, and the producer writes data to the destination system memory.
+
+BAM-Lite's interface is similar to the BAM interface with slight changes to
+the sideband interface. BAM-Lite is an area-optimized version of BAM. BAM-Lite
+supports new features such as Notify-When-Done(NWD), pipe lock/unlock and
+command descriptors.
+
+NANDc has a secured BAM-Lite which provides DMA support for the NANDc and
+command support for accessing the NANDc registers. It is called secured
+because it has an integrated APU (Address Protection Unit) that validates
+every access to BAM and its peripheral registers.
+
+The NANDc has in total 6 BAM pipes - 3 pipes are dedicated for each processor
+(Modem and Apps) at the hardware level.
+
+Software description
+====================
+
+The NAND device is shared between two independent file systems, each running
+on a different processor - the application processor (Apps) and the Modem.
+The NAND driver uses BAM driver to transfer NAND operation requests and
+data to/from the NAND Controller (NANDc) core through the BAM pipes. Every
+NANDc register read/write access must go through BAM as it facilitates security
+mechanisms to enable simultaneous access to NAND device from both processors
+(Modem and Apps).
+
+The Apps NAND driver registers NANDc BAM peripheral with BAM driver, allocates
+endpoints and descriptor FIFO memory and registers for complete event
+notification for the following pipes:
+
+	- system consumer pipe for data (pipe#0) : This BAM pipe will be used
+	  for transferring data from system memory to NANDc i.e., during write.
+
+	- system producer pipe for data (pipe#1) : This BAM pipe will be used
+	  for transferring data from NANDc to system memory i.e., during read.
+
+	- system consumer pipe for commands (pipe#2) : This BAM pipe will be
+	  used for both reading and writing to NANDc registers. It can be
+	  configured either as consumer pipe or producer pipe but as per HW
+	  team's recommendation it is configured as consumer pipe.
+
+Control path:
+-------------
+Each NAND operation can be described as a set of BAM command or/and data
+descriptors.
+
+A command descriptor(CD) points to the starting address of a command
+block. Each command block may contain a set of command elements where
+each command element is a single NANDc register read/write. The NAND
+driver submits all command descriptors to its system consumer pipe#2.
+
+Data path:
+----------
+A Data Descriptor(DD) points to the start of a data block which is a sequential
+chunk of data.
+
+For page write operations, the NAND driver submits data descriptors to system
+consumer pipe#0 and as per the descriptors submitted, the BAM reads data from
+the data block into the NANDc buffer.
+
+For page read operations, the NAND driver submits data descriptors to system
+producer pipe#1 and as per the descriptors submitted, the BAM reads data from
+the NANDc buffer into the data block.
+
+The driver submits a CD/DD using BAM driver APIs sps_transfer_one()/
+sps_transfer(). To this API, flags is passed as one of the arguments and if
+SPS_IOVEC_FLAG_CMD is passed, then it is identified as a CD. Otherwise, it is
+identified as a DD. The other valid SPS flags for a CD/DD are -
+
+	- SPS_IOVEC_FLAG_INT : This flag indicates BAM driver to raise BAM
+	  interrupt after the current descriptor with this flag has been
+	  processed by BAM HW. This flag is applicable for both CD and DD.
+
+	- SPS_IOVEC_FLAG_NWD : This flag indicates BAM HW to not process
+	  next descriptors until it receives an acknowledgement by NANDc
+	  that the current descriptor with this flag is completely
+	  executed. This flag is applicable only for a CD.
+
+	- SPS_IOVEC_FLAG_LOCK: This flag marks the beginning of a series of
+	  commands and it indicates that all the CDs submitted on this pipe
+	  must be executed atomically without any interruption by commands
+	  from other pipes. This is applicable only for a CD.
+
+	- SPS_IOVEC_FLAG_UNLOCK: This flag marks the end of a series of
+	  commands and it indicates that the other pipe that was locked due to
+	  SPS_IOVEC_FLAG_LOCK flag can be unblocked after the current CD
+	  with this flag is executed. This is applicable only for a CD.
+
+	- SPS_IOVEC_FLAG_EOT - This flag indicates to BAM driver that the
+	  current descriptor with this flag is the last descriptor submitted
+	  during write operation. This is applicable only for a DD.
+
+Error handling:
+---------------
+After a page read/write complete notification from BAM, NAND driver validates
+the values read from NANDc registers to confirm the success/failure of page
+read/write operation. For example, after a page read/write is complete, the
+drivers reads the NANDc status registers to check for any operational errors,
+protection violation errors and device status errors, number of correctable/
+uncorrectable errors reported by the controller. Based on the error conditions
+that are met, the driver reports appropriate error codes to upper layers. The
+upper layers respond to these errors and take appropriate action.
+
+Design
+======
+
+The existing NAND driver (ADM based) can not be reused due to many major HW
+changes (see Introduction section) in the new NANDc core. Some of the complex
+features (Dual NAND controllers support) too are deprecated in the new NANDc.
+Hence, a new NAND driver is written to take care of both SPS/BAM changes and
+other controller specific changes. The rest of the interaction with MTD and
+YAFFS2 remains same as its previous version of NAND driver msm_nand.c.
+
+Power Management
+================
+
+Two clocks are supplied by the system's clock controller to NANDc - AHB clock
+and interface clock. The interface clock is the clock that drives some of the
+HW blocks within NANDc. As of now, both these clocks are always on. But NANDc
+provides clock gating if some of the QPIC clock control registers are
+configured. The clock gating is yet to be enabled by driver.
+
+SMP/Multi-Core
+==============
+
+The locking mechanism for page read/write operations is taken care of by the
+higher layers such as MTD/YAFFS2 and only one single page operation can happen
+at any time on a given partition. For a single page operation, there is always
+only one context associated within the driver and thus no additional handling
+is required within the driver. But it is possible for file system to issue
+one request on partition and at the same time to issue another request on
+another partition as each partition corresponds to different MTD block device.
+This situation is handled within the driver by properly acquiring a mutex lock
+before submitting any command/data descriptors to any of the BAM pipes.
+
+
+Security
+========
+
+The same NAND device is accessible from both processors (Modem and Apps) and
+thus to avoid any configuration overwrite issues during a page operation,
+driver on each processor (Modem and Apps) must explicitly use BAM pipe
+lock/unlock mechanism. This is taken care of by the NAND driver. The partition
+violation issues are prevented by an MPU (Memory Protection Unit) that is
+attached to NANDc.
+
+Performance
+===========
+
+None.
+
+Interface
+=========
+
+The NAND driver registers each partition on NAND device as a MTD block device
+using mtd_device_register(). As part of this registration, the following ops
+(struct mtd_info *mtd) are registered with MTD layer for each partition:
+
+mtd->_block_isbad = msm_nand_block_isbad;
+mtd->_block_markbad = msm_nand_block_markbad;
+mtd->_read = msm_nand_read;
+mtd->_write = msm_nand_write;
+mtd->_read_oob  = msm_nand_read_oob;
+mtd->_write_oob = msm_nand_write_oob;
+mtd->_erase = msm_nand_erase;
+
+msm_nand_block_isbad() - This checks if a block is bad or not by reading bad
+block byte in the first page of a block. A block is considered as bad if bad
+block byte location contains any value other than 0xFF.
+
+msm_nand_block_markbad() - This marks a block as bad by writing 0 to the
+entire first page of the block and thus writing 0 to bad block byte location.
+
+msm_nand_read/write() - This is used to read/write only main data from/to
+single/multiple pages within NAND device. The YAFFS2 file system can send
+read/write request for two types of data -
+
+	- Main data : This is the actual data to be read/written from/to a
+	  page during a read/write operation on this device. The size of this
+	  data request is typically based on the page size of the device
+	  (2K/4K).
+
+	- OOB(Out Of Band) data : This is the spare data that will be used by
+	  file system to keep track of its meta data/tags associated with the
+	  actual data. As of now, the file system needs only 16 bytes to
+	  accommodate this data. The NAND driver always writes this data
+	  towards the end of main data.
+
+It is up to the file system whether or not to send a read/write request for OOB
+data along with main data.
+
+msm_nand_read_oob()/write_oob() - This is used to read/write both main data
+and spare data from/to single/multiple pages within NAND device.
+
+msm_nand_erase() - This erases the complete block by sending erase command to
+the device.
+
+The YAFFS2 file system registers as the user of MTD device and uses the ops
+exposed by the NAND driver to perform read/write/erase operations on NAND
+device. As of now, the driver can work with only YAFFS2 file system. An
+attempt to use it with any other file system might demand additional changes
+in the driver.
+
+Driver parameters
+=================
+
+None.
+
+Config options
+==============
+
+The config option MTD_MSM_QPIC_NAND enables this driver.
+
+Dependencies
+============
+
+It depends on the following kernel components:
+
+- SPS/BAM driver
+- MTD core layer
+- To add necessary NANDc and BAM resources to .dts file
+
+It depends on the following non-kernel components:
+
+The partition information of the NAND device must be passed by Modem subsystem
+to Apps boot loader and Apps boot loader must update the .dts file
+with the partition information as per the defined MTD bindings.
+
+The detailed information on MTD bindings can be found at -
+Documentation/devicetree/bindings/mtd/msm_qpic_nand.txt
+
+User space utilities
+====================
+
+None.
+
+Other
+=====
+
+No changes other than device tree changes are anticipated.
+
+Known issues
+============
+
+None.
+
+To do
+=====
+
+The NANDc core supports clock gating and is not yet supported by the driver.
diff --git a/arch/arm/boot/dts/msm-iommu.dtsi b/arch/arm/boot/dts/msm-iommu.dtsi
index 952f517..0e2ddce9 100755
--- a/arch/arm/boot/dts/msm-iommu.dtsi
+++ b/arch/arm/boot/dts/msm-iommu.dtsi
@@ -76,6 +76,7 @@
 		reg = <0xfdc84000 0x10000>;
 		vdd-supply = <&gdsc_venus>;
 		qcom,iommu-smt-size = <16>;
+		qcom,needs-alt-core-clk;
 		status = "disabled";
 
 		qcom,iommu-ctx@fdc8c000 {
@@ -108,6 +109,7 @@
 		reg = <0xfdb10000 0x10000>;
 		vdd-supply = <&gdsc_oxili_cx>;
 		qcom,iommu-smt-size = <32>;
+		qcom,needs-alt-core-clk;
 		status = "disabled";
 
 		qcom,iommu-ctx@fdb18000 {
diff --git a/arch/arm/boot/dts/msm9625.dts b/arch/arm/boot/dts/msm9625.dts
index 6c007fb..6b44be9 100644
--- a/arch/arm/boot/dts/msm9625.dts
+++ b/arch/arm/boot/dts/msm9625.dts
@@ -55,4 +55,14 @@
 		reg = <0xf991f000 0x1000>;
 		interrupts = <0 109 0>;
 	};
+
+	qcom,nand@f9ac0000 {
+		compatible = "qcom,msm-nand";
+		reg = <0xf9ac0000 0x1000>,
+		      <0xf9ac4000 0x8000>;
+		reg-names = "nand_phys",
+			    "bam_phys";
+		interrupts = <0 247 0>;
+		interrupt-names = "bam_irq";
+	};
 };
diff --git a/arch/arm/configs/msm8960-perf_defconfig b/arch/arm/configs/msm8960-perf_defconfig
index c2f4702..7707692 100644
--- a/arch/arm/configs/msm8960-perf_defconfig
+++ b/arch/arm/configs/msm8960-perf_defconfig
@@ -83,6 +83,7 @@
 CONFIG_MSM_GSS_SSR_8064=y
 CONFIG_MSM_TZ_LOG=y
 CONFIG_MSM_RPM_LOG=y
+CONFIG_MSM_RPM_RBCPR_STATS_LOG=y
 CONFIG_MSM_RPM_STATS_LOG=y
 CONFIG_MSM_BUS_SCALING=y
 CONFIG_MSM_BUS_RPM_MULTI_TIER_ENABLED=y
diff --git a/arch/arm/configs/msm8960_defconfig b/arch/arm/configs/msm8960_defconfig
index a50485d..87c536f 100644
--- a/arch/arm/configs/msm8960_defconfig
+++ b/arch/arm/configs/msm8960_defconfig
@@ -83,6 +83,7 @@
 CONFIG_MSM_TZ_LOG=y
 CONFIG_MSM_RPM_LOG=y
 CONFIG_MSM_RPM_STATS_LOG=y
+CONFIG_MSM_RPM_RBCPR_STATS_LOG=y
 CONFIG_MSM_BUS_SCALING=y
 CONFIG_MSM_BUS_RPM_MULTI_TIER_ENABLED=y
 CONFIG_MSM_WATCHDOG=y
diff --git a/arch/arm/configs/msm8974_defconfig b/arch/arm/configs/msm8974_defconfig
index 63d2ced..b02bd7c 100644
--- a/arch/arm/configs/msm8974_defconfig
+++ b/arch/arm/configs/msm8974_defconfig
@@ -171,6 +171,7 @@
 CONFIG_SND=y
 CONFIG_SND_SOC=y
 CONFIG_SND_SOC_MSM8974=y
+CONFIG_WCD9320_CODEC=y
 CONFIG_USB=y
 CONFIG_USB_XHCI_HCD=y
 CONFIG_USB_STORAGE=y
diff --git a/arch/arm/include/asm/arch_timer.h b/arch/arm/include/asm/arch_timer.h
index 99ee2de..e32194f 100644
--- a/arch/arm/include/asm/arch_timer.h
+++ b/arch/arm/include/asm/arch_timer.h
@@ -2,6 +2,7 @@
 #define __ASMARM_ARCH_TIMER_H
 
 #include <linux/ioport.h>
+#include <linux/clocksource.h>
 
 struct arch_timer {
 	struct resource	res[3];
@@ -10,6 +11,7 @@
 #ifdef CONFIG_ARM_ARCH_TIMER
 int arch_timer_register(struct arch_timer *);
 int arch_timer_of_register(void);
+cycle_t arch_counter_get_cntpct(void);
 #else
 static inline int arch_timer_register(struct arch_timer *at)
 {
@@ -20,6 +22,11 @@
 {
 	return -ENXIO;
 }
+
+static inline cycle_t arch_counter_get_cntpct(void)
+{
+	return 0;
+}
 #endif
 
 #endif
diff --git a/arch/arm/include/asm/mach/mmc.h b/arch/arm/include/asm/mach/mmc.h
index 562f13c..d341ea9 100644
--- a/arch/arm/include/asm/mach/mmc.h
+++ b/arch/arm/include/asm/mach/mmc.h
@@ -146,7 +146,7 @@
 	unsigned int uhs_caps2;
 	void (*sdio_lpm_gpio_setup)(struct device *, unsigned int);
         unsigned int status_irq;
-	unsigned int status_gpio;
+	int status_gpio;
 	/* Indicates the polarity of the GPIO line when card is inserted */
 	bool is_status_gpio_active_low;
         unsigned int sdiowakeup_irq;
@@ -158,7 +158,7 @@
 	unsigned int msmsdcc_fmax;
 	bool nonremovable;
 	unsigned int mpm_sdiowakeup_int;
-	unsigned int wpswitch_gpio;
+	int wpswitch_gpio;
 	bool is_wpswitch_active_low;
 	struct msm_mmc_slot_reg_data *vreg_data;
 	int is_sdio_al_client;
diff --git a/arch/arm/kernel/arch_timer.c b/arch/arm/kernel/arch_timer.c
index 87bb7d3..43c627d 100644
--- a/arch/arm/kernel/arch_timer.c
+++ b/arch/arm/kernel/arch_timer.c
@@ -22,6 +22,7 @@
 #include <linux/of_address.h>
 #include <linux/io.h>
 #include <linux/irq.h>
+#include <linux/export.h>
 
 #include <asm/cputype.h>
 #include <asm/localtimer.h>
@@ -315,10 +316,16 @@
 	return ((cycle_t) cvalh << 32) | cvall;
 }
 
-static cycle_t arch_counter_read(struct clocksource *cs)
+cycle_t arch_counter_get_cntpct(void)
 {
 	return arch_specific_timer->get_cntpct();
 }
+EXPORT_SYMBOL(arch_counter_get_cntpct);
+
+static cycle_t arch_counter_read(struct clocksource *cs)
+{
+	return arch_counter_get_cntpct();
+}
 
 #ifdef ARCH_HAS_READ_CURRENT_TIMER
 int read_current_timer(unsigned long *timer_val)
diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig
index 2346cf8..e21eed5 100644
--- a/arch/arm/mach-msm/Kconfig
+++ b/arch/arm/mach-msm/Kconfig
@@ -39,6 +39,7 @@
 	select MSM_RUN_QUEUE_STATS if MSM_SOC_REV_A
 	select DONT_MAP_HOLE_AFTER_MEMBANK0
 	select MIGHT_HAVE_CACHE_L2X0
+	select MSM_MODEM_RESTART
 
 config ARCH_MSM7X30
 	bool "MSM7x30"
@@ -65,6 +66,7 @@
 	select MULTI_IRQ_HANDLER
 	select MSM_PM2 if PM
 	select HOLES_IN_ZONE if SPARSEMEM
+	select MSM_MODEM_RESTART
 
 config ARCH_QSD8X50
 	bool "QSD8X50"
@@ -78,6 +80,7 @@
 	select MSM_GPIOMUX
 	select MSM_DALRPC
 	select MSM_PM2 if PM
+	select MSM_MODEM_RESTART
 
 config ARCH_MSM8X60
 	bool "MSM8X60"
@@ -2090,6 +2093,9 @@
 config MSM_NATIVE_RESTART
 	bool
 
+config MSM_MODEM_RESTART
+	bool
+
 config MSM_PM2
 	depends on PM
 	bool
diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile
index 841ed3c..f7f488a 100644
--- a/arch/arm/mach-msm/Makefile
+++ b/arch/arm/mach-msm/Makefile
@@ -363,6 +363,7 @@
 obj-$(CONFIG_MSM_FAKE_BATTERY) += fish_battery.o
 obj-$(CONFIG_MSM_RPC_VIBRATOR) += msm_vibrator.o
 obj-$(CONFIG_MSM_NATIVE_RESTART) += restart.o
+obj-$(CONFIG_MSM_MODEM_RESTART) += restart_7k.o
 
 obj-$(CONFIG_MSM_PROC_COMM_REGULATOR) += proccomm-regulator.o
 ifdef CONFIG_MSM_PROC_COMM_REGULATOR
diff --git a/arch/arm/mach-msm/board-8064.c b/arch/arm/mach-msm/board-8064.c
index 3e07833..d3af1a7 100644
--- a/arch/arm/mach-msm/board-8064.c
+++ b/arch/arm/mach-msm/board-8064.c
@@ -2253,7 +2253,6 @@
 	&msm_gss,
 	&apq8064_rtb_device,
 	&apq8064_cpu_idle_device,
-	&apq8064_msm_gov_device,
 	&apq8064_device_cache_erp,
 	&msm8960_device_ebi1_ch0_erp,
 	&msm8960_device_ebi1_ch1_erp,
diff --git a/arch/arm/mach-msm/board-8930-display.c b/arch/arm/mach-msm/board-8930-display.c
index 292c031..d975997 100644
--- a/arch/arm/mach-msm/board-8930-display.c
+++ b/arch/arm/mach-msm/board-8930-display.c
@@ -482,16 +482,16 @@
 static struct mipi_dsi_phy_ctrl dsi_novatek_cmd_mode_phy_db = {
 
 /* DSI_BIT_CLK at 500MHz, 2 lane, RGB888 */
-	{0x0F, 0x0a, 0x04, 0x00, 0x20},	/* regulator */
+	{0x09, 0x08, 0x05, 0x00, 0x20},	/* regulator */
 	/* timing   */
 	{0xab, 0x8a, 0x18, 0x00, 0x92, 0x97, 0x1b, 0x8c,
 	0x0c, 0x03, 0x04, 0xa0},
 	{0x5f, 0x00, 0x00, 0x10},	/* phy ctrl */
 	{0xff, 0x00, 0x06, 0x00},	/* strength */
 	/* pll control */
-	{0x40, 0xf9, 0x30, 0xda, 0x00, 0x40, 0x03, 0x62,
+	{0x0, 0xe, 0x30, 0xda, 0x00, 0x10, 0x0f, 0x61,
 	0x40, 0x07, 0x03,
-	0x00, 0x1a, 0x00, 0x00, 0x02, 0x00, 0x20, 0x00, 0x01},
+	0x00, 0x1a, 0x00, 0x00, 0x02, 0x00, 0x20, 0x00, 0x02},
 };
 
 static struct mipi_dsi_panel_platform_data novatek_pdata = {
diff --git a/arch/arm/mach-msm/board-8960.c b/arch/arm/mach-msm/board-8960.c
index d56bdbd..18db7ce 100644
--- a/arch/arm/mach-msm/board-8960.c
+++ b/arch/arm/mach-msm/board-8960.c
@@ -1407,11 +1407,18 @@
 	msm_bus_rpm_set_mt_mask();
 	msm_bus_8960_apps_fabric_pdata.rpm_enabled = 1;
 	msm_bus_8960_sys_fabric_pdata.rpm_enabled = 1;
-	msm_bus_8960_mm_fabric_pdata.rpm_enabled = 1;
 	msm_bus_apps_fabric.dev.platform_data =
 		&msm_bus_8960_apps_fabric_pdata;
 	msm_bus_sys_fabric.dev.platform_data = &msm_bus_8960_sys_fabric_pdata;
-	msm_bus_mm_fabric.dev.platform_data = &msm_bus_8960_mm_fabric_pdata;
+	if (cpu_is_msm8960ab()) {
+		msm_bus_8960_sg_mm_fabric_pdata.rpm_enabled = 1;
+		msm_bus_mm_fabric.dev.platform_data =
+			&msm_bus_8960_sg_mm_fabric_pdata;
+	} else {
+		msm_bus_8960_mm_fabric_pdata.rpm_enabled = 1;
+		msm_bus_mm_fabric.dev.platform_data =
+			&msm_bus_8960_mm_fabric_pdata;
+	}
 	msm_bus_sys_fpb.dev.platform_data = &msm_bus_8960_sys_fpb_pdata;
 	msm_bus_cpss_fpb.dev.platform_data = &msm_bus_8960_cpss_fpb_pdata;
 #endif
diff --git a/arch/arm/mach-msm/board-8974.c b/arch/arm/mach-msm/board-8974.c
index 30b44bd..74aa837 100644
--- a/arch/arm/mach-msm/board-8974.c
+++ b/arch/arm/mach-msm/board-8974.c
@@ -59,7 +59,7 @@
 #endif
 #define MSM_ION_MM_FW_SIZE	0xa00000 /* (10MB) */
 #define MSM_ION_MM_SIZE		0x7800000 /* (120MB) */
-#define MSM_ION_QSECOM_SIZE	0x100000 /* (1MB) */
+#define MSM_ION_QSECOM_SIZE	0x600000 /* (6MB) */
 #define MSM_ION_MFC_SIZE	SZ_8K
 #define MSM_ION_AUDIO_SIZE	0x2B4000
 #define MSM_ION_HEAP_NUM	8
diff --git a/arch/arm/mach-msm/clock-8974.c b/arch/arm/mach-msm/clock-8974.c
index a5430e6..73fd8ef 100644
--- a/arch/arm/mach-msm/clock-8974.c
+++ b/arch/arm/mach-msm/clock-8974.c
@@ -4782,7 +4782,9 @@
 	CLK_LOOKUP("iface_clk", oxilicx_ahb_clk.c, "fdb00000.qcom,kgsl-3d0"),
 	CLK_LOOKUP("core_clk", oxilicx_axi_clk.c, "fdb10000.qcom,iommu"),
 	CLK_LOOKUP("iface_clk", oxilicx_ahb_clk.c, "fdb10000.qcom,iommu"),
+	CLK_LOOKUP("alt_core_clk", oxili_gfx3d_clk.c, "fdb10000.qcom,iommu"),
 	CLK_LOOKUP("iface_clk", venus0_ahb_clk.c, "fdc84000.qcom,iommu"),
+	CLK_LOOKUP("alt_core_clk", venus0_vcodec0_clk.c, "fdc84000.qcom,iommu"),
 	CLK_LOOKUP("core_clk", venus0_axi_clk.c, "fdc84000.qcom,iommu"),
 	CLK_LOOKUP("bus_clk", venus0_axi_clk.c, ""),
 	CLK_LOOKUP("src_clk",  vcodec0_clk_src.c, "fdce0000.qcom,venus"),
diff --git a/arch/arm/mach-msm/devices-msm7x2xa.h b/arch/arm/mach-msm/devices-msm7x2xa.h
index 4184a86..8febe26 100644
--- a/arch/arm/mach-msm/devices-msm7x2xa.h
+++ b/arch/arm/mach-msm/devices-msm7x2xa.h
@@ -33,4 +33,5 @@
 void __init msm8x25_spm_device_init(void);
 void __init msm8x25_kgsl_3d0_init(void);
 void __iomem *core1_reset_base(void);
+extern void setup_mm_for_reboot(void);
 #endif
diff --git a/arch/arm/mach-msm/include/mach/iommu.h b/arch/arm/mach-msm/include/mach/iommu.h
index 28c53db..b14f145 100644
--- a/arch/arm/mach-msm/include/mach/iommu.h
+++ b/arch/arm/mach-msm/include/mach/iommu.h
@@ -72,6 +72,7 @@
  * @irq:	Interrupt number
  * @clk:	The bus clock for this IOMMU hardware instance
  * @pclk:	The clock for the IOMMU bus interconnect
+ * @aclk:	Alternate clock for this IOMMU core, if any
  * @name:	Human-readable name of this IOMMU device
  * @gdsc:	Regulator needed to power this HW block (v2 only)
  * @nsmr:	Size of the SMT on this HW block (v2 only)
@@ -85,6 +86,7 @@
 	int ttbr_split;
 	struct clk *clk;
 	struct clk *pclk;
+	struct clk *aclk;
 	const char *name;
 	struct regulator *gdsc;
 	unsigned int nsmr;
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 d95e4a4..0a53b46 100644
--- a/arch/arm/mach-msm/include/mach/msm_bus_board.h
+++ b/arch/arm/mach-msm/include/mach/msm_bus_board.h
@@ -65,6 +65,7 @@
 extern struct msm_bus_fabric_registration msm_bus_8960_apps_fabric_pdata;
 extern struct msm_bus_fabric_registration msm_bus_8960_sys_fabric_pdata;
 extern struct msm_bus_fabric_registration msm_bus_8960_mm_fabric_pdata;
+extern struct msm_bus_fabric_registration msm_bus_8960_sg_mm_fabric_pdata;
 extern struct msm_bus_fabric_registration msm_bus_8960_sys_fpb_pdata;
 extern struct msm_bus_fabric_registration msm_bus_8960_cpss_fpb_pdata;
 
diff --git a/arch/arm/mach-msm/include/mach/socinfo.h b/arch/arm/mach-msm/include/mach/socinfo.h
index 2c3d395..f7ba507 100644
--- a/arch/arm/mach-msm/include/mach/socinfo.h
+++ b/arch/arm/mach-msm/include/mach/socinfo.h
@@ -330,4 +330,16 @@
 #endif
 }
 
+static inline int cpu_is_msm8974(void)
+{
+#ifdef CONFIG_ARCH_MSM8974
+	enum msm_cpu cpu = socinfo_get_msm_cpu();
+
+	BUG_ON(cpu == MSM_CPU_UNKNOWN);
+	return cpu == MSM_CPU_8974;
+#else
+	return 0;
+#endif
+}
+
 #endif
diff --git a/arch/arm/mach-msm/msm-buspm-dev.c b/arch/arm/mach-msm/msm-buspm-dev.c
index 296418d..a818eed 100644
--- a/arch/arm/mach-msm/msm-buspm-dev.c
+++ b/arch/arm/mach-msm/msm-buspm-dev.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -40,6 +40,13 @@
 	return (dev) ? dev->vaddr : NULL;
 }
 
+static inline unsigned int msm_buspm_dev_get_buflen(struct file *filp)
+{
+	struct msm_buspm_map_dev *dev = filp->private_data;
+
+	return dev ? dev->buflen : 0;
+}
+
 static inline unsigned long msm_buspm_dev_get_paddr(struct file *filp)
 {
 	struct msm_buspm_map_dev *dev = filp->private_data;
@@ -114,6 +121,7 @@
 	unsigned long paddr;
 	int retval = 0;
 	void *buf = msm_buspm_dev_get_vaddr(filp);
+	unsigned int buflen = msm_buspm_dev_get_buflen(filp);
 	unsigned char *dbgbuf = buf;
 
 	switch (cmd) {
@@ -156,7 +164,7 @@
 			break;
 		}
 
-		if ((xfer.size <= sizeof(buf)) &&
+		if ((xfer.size <= buflen) &&
 			(copy_to_user((void __user *)xfer.data, buf,
 					xfer.size))) {
 			retval = -EFAULT;
@@ -177,7 +185,7 @@
 			break;
 		}
 
-		if ((sizeof(buf) <= xfer.size) &&
+		if ((buflen <= xfer.size) &&
 			(copy_from_user(buf, (void __user *)xfer.data,
 			xfer.size))) {
 			retval = -EFAULT;
diff --git a/arch/arm/mach-msm/msm_bus/msm_bus_board_8960.c b/arch/arm/mach-msm/msm_bus/msm_bus_board_8960.c
index 7ede23d..9ba9f7b1 100644
--- a/arch/arm/mach-msm/msm_bus/msm_bus_board_8960.c
+++ b/arch/arm/mach-msm/msm_bus/msm_bus_board_8960.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -39,6 +39,12 @@
 	MSM_BUS_TIERED_SLAVE_KMPSS_L2,
 };
 
+enum msm_bus_sg_tiered_slaves_type {
+	SG_TIERED_SLAVE_MM_IMEM = 1,
+	SG_MMSS_TIERED_SLAVE_FAB_APPS_0,
+	SG_MMSS_TIERED_SLAVE_FAB_APPS_1,
+};
+
 enum msm_bus_8960_master_ports_type {
 	MSM_BUS_SYSTEM_MASTER_PORT_APPSS_FAB = 0,
 	MSM_BUS_MASTER_PORT_SPS,
@@ -106,6 +112,23 @@
 	MSM_BUS_SLAVE_PORT_RIVA,
 };
 
+enum msm_bus_8960_sg_master_ports_type {
+	MSM_BUS_MASTER_PORT_GRAPHICS_3D_PORT0 =
+		MSM_BUS_MMSS_MASTER_PORT_UNUSED_2,
+	MSM_BUS_MASTER_PORT_VIDEO_CAP =
+		MSM_BUS_MASTER_PORT_GRAPHICS_2D_CORE0,
+	MSM_BUS_MASTER_PORT_VIDEO_DEC =
+		MSM_BUS_MASTER_PORT_GRAPHICS_2D_CORE1,
+	MSM_BUS_MASTER_PORT_VIDEO_ENC =
+		MSM_BUS_MASTER_PORT_HD_CODEC_PORT0,
+};
+
+enum msm_bus_8960_sg_slave_ports_type {
+	SG_SLAVE_PORT_MM_IMEM = 0,
+	SG_MMSS_SLAVE_PORT_APPS_FAB_0,
+	SG_MMSS_SLAVE_PORT_APPS_FAB_1,
+};
+
 static int tier2[] = {MSM_BUS_BW_TIER2,};
 static uint32_t master_iids[NMASTERS];
 static uint32_t slave_iids[NSLAVES];
@@ -424,6 +447,10 @@
 static int mport_mdp1[] = {MSM_BUS_MASTER_PORT_MDP_PORT1,};
 static int mport_rotator[] = {MSM_BUS_MASTER_PORT_ROTATOR,};
 static int mport_graphics_3d[] = {MSM_BUS_MASTER_PORT_GRAPHICS_3D,};
+static int pro_mport_graphics_3d[] = {
+	MSM_BUS_MASTER_PORT_GRAPHICS_3D_PORT0,
+	MSM_BUS_MASTER_PORT_GRAPHICS_3D,
+};
 static int mport_jpeg_dec[] = {MSM_BUS_MASTER_PORT_JPEG_DEC,};
 static int mport_graphics_2d_core0[] = {MSM_BUS_MASTER_PORT_GRAPHICS_2D_CORE0,};
 static int mport_vfe[] = {MSM_BUS_MASTER_PORT_VFE,};
@@ -432,6 +459,9 @@
 static int mport_graphics_2d_core1[] = {MSM_BUS_MASTER_PORT_GRAPHICS_2D_CORE1,};
 static int mport_hd_codec_port0[] = {MSM_BUS_MASTER_PORT_HD_CODEC_PORT0,};
 static int mport_hd_codec_port1[] = {MSM_BUS_MASTER_PORT_HD_CODEC_PORT1,};
+static int mport_video_cap[] = {MSM_BUS_MASTER_PORT_VIDEO_CAP};
+static int mport_video_enc[] = {MSM_BUS_MASTER_PORT_VIDEO_ENC};
+static int mport_video_dec[] = {MSM_BUS_MASTER_PORT_VIDEO_DEC};
 static int appss_mport_fab_mmss[] = {
 	MSM_BUS_APPSS_MASTER_PORT_FAB_MMSS_0,
 	MSM_BUS_APPSS_MASTER_PORT_FAB_MMSS_1
@@ -439,15 +469,25 @@
 
 static int mmss_sport_apps_fab[] = {
 	MSM_BUS_MMSS_SLAVE_PORT_APPS_FAB_0,
-	MSM_BUS_MMSS_SLAVE_PORT_APPS_FAB_1
+	MSM_BUS_MMSS_SLAVE_PORT_APPS_FAB_1,
+};
+static int sg_sport_apps_fab[] = {
+	SG_MMSS_SLAVE_PORT_APPS_FAB_0,
+	SG_MMSS_SLAVE_PORT_APPS_FAB_1,
 };
 static int sport_mm_imem[] = {MSM_BUS_SLAVE_PORT_MM_IMEM,};
+static int sg_sport_mm_imem[] = {SG_SLAVE_PORT_MM_IMEM,};
 
 static int mmss_tiered_slave_fab_apps[] = {
 	MSM_BUS_MMSS_TIERED_SLAVE_FAB_APPS_0,
 	MSM_BUS_MMSS_TIERED_SLAVE_FAB_APPS_1,
 };
+static int sg_tiered_slave_fab_apps[] = {
+	SG_MMSS_TIERED_SLAVE_FAB_APPS_0,
+	SG_MMSS_TIERED_SLAVE_FAB_APPS_1,
+};
 static int tiered_slave_mm_imem[] = {MSM_BUS_TIERED_SLAVE_MM_IMEM,};
+static int sg_tiered_slave_mm_imem[] = {SG_TIERED_SLAVE_MM_IMEM,};
 
 
 static struct msm_bus_node_info mmss_fabric_info[]  = {
@@ -557,6 +597,106 @@
 	},
 };
 
+static struct msm_bus_node_info sg_mmss_fabric_info[]  = {
+	{
+		.id = MSM_BUS_MASTER_MDP_PORT0,
+		.masterp = mport_mdp,
+		.num_mports = ARRAY_SIZE(mport_mdp),
+		.tier = tier2,
+		.num_tiers = ARRAY_SIZE(tier2),
+	},
+	{
+		.id = MSM_BUS_MASTER_MDP_PORT1,
+		.masterp = mport_mdp1,
+		.num_mports = ARRAY_SIZE(mport_mdp1),
+		.tier = tier2,
+		.num_tiers = ARRAY_SIZE(tier2),
+	},
+	{
+		.id = MSM_BUS_MASTER_ROTATOR,
+		.masterp = mport_rotator,
+		.num_mports = ARRAY_SIZE(mport_rotator),
+		.tier = tier2,
+		.num_tiers = ARRAY_SIZE(tier2),
+	},
+	{
+		.id = MSM_BUS_MASTER_GRAPHICS_3D,
+		.masterp = pro_mport_graphics_3d,
+		.num_mports = ARRAY_SIZE(pro_mport_graphics_3d),
+		.tier = tier2,
+		.num_tiers = ARRAY_SIZE(tier2),
+	},
+	{
+		.id = MSM_BUS_MASTER_JPEG_DEC,
+		.masterp = mport_jpeg_dec,
+		.num_mports = ARRAY_SIZE(mport_jpeg_dec),
+		.tier = tier2,
+		.num_tiers = ARRAY_SIZE(tier2),
+	},
+	{
+		.id = MSM_BUS_MASTER_VIDEO_CAP,
+		.masterp = mport_video_cap,
+		.num_mports = ARRAY_SIZE(mport_video_cap),
+		.tier = tier2,
+		.num_tiers = ARRAY_SIZE(tier2),
+	},
+	{
+		.id = MSM_BUS_MASTER_VFE,
+		.masterp = mport_vfe,
+		.num_mports = ARRAY_SIZE(mport_vfe),
+		.tier = tier2,
+		.num_tiers = ARRAY_SIZE(tier2),
+	},
+	{
+		.id = MSM_BUS_MASTER_VPE,
+		.masterp = mport_vpe,
+		.num_mports = ARRAY_SIZE(mport_vpe),
+		.tier = tier2,
+		.num_tiers = ARRAY_SIZE(tier2),
+	},
+	{
+		.id = MSM_BUS_MASTER_JPEG_ENC,
+		.masterp = mport_jpeg_enc,
+		.num_mports = ARRAY_SIZE(mport_jpeg_enc),
+		.tier = tier2,
+		.num_tiers = ARRAY_SIZE(tier2),
+	},
+	/* This port has been added for V2. It is absent in V1 */
+	{
+		.id = MSM_BUS_MASTER_VIDEO_DEC,
+		.masterp = mport_video_dec,
+		.num_mports = ARRAY_SIZE(mport_video_dec),
+		.tier = tier2,
+		.num_tiers = ARRAY_SIZE(tier2),
+	},
+	{
+		.id = MSM_BUS_MASTER_VIDEO_ENC,
+		.masterp = mport_video_enc,
+		.num_mports = ARRAY_SIZE(mport_video_enc),
+		.tier = tier2,
+		.num_tiers = ARRAY_SIZE(tier2),
+	},
+	{
+		.id = MSM_BUS_FAB_APPSS,
+		.gateway = 1,
+		.slavep = sg_sport_apps_fab,
+		.num_sports = ARRAY_SIZE(sg_sport_apps_fab),
+		.masterp = appss_mport_fab_mmss,
+		.num_mports = ARRAY_SIZE(appss_mport_fab_mmss),
+		.tier = sg_tiered_slave_fab_apps,
+		.num_tiers = ARRAY_SIZE(sg_tiered_slave_fab_apps),
+		.buswidth = 16,
+	},
+	{
+		.id = MSM_BUS_SLAVE_MM_IMEM,
+		.slavep = sg_sport_mm_imem,
+		.num_sports = ARRAY_SIZE(sg_sport_mm_imem),
+		.tier = sg_tiered_slave_mm_imem,
+		.num_tiers = ARRAY_SIZE(sg_tiered_slave_mm_imem),
+		.buswidth = 8,
+	},
+};
+
 static struct msm_bus_node_info sys_fpb_fabric_info[]  = {
 	{
 		.id = MSM_BUS_FAB_SYSTEM,
@@ -919,6 +1059,22 @@
 	.board_algo = &msm_bus_board_algo,
 };
 
+struct msm_bus_fabric_registration msm_bus_8960_sg_mm_fabric_pdata = {
+	.id = MSM_BUS_FAB_MMSS,
+	.name = "msm_mm_fab",
+	sg_mmss_fabric_info,
+	ARRAY_SIZE(sg_mmss_fabric_info),
+	.ahb = 0,
+	.fabclk[DUAL_CTX] = "bus_clk",
+	.fabclk[ACTIVE_CTX] = "bus_a_clk",
+	.haltid = MSM_RPM_ID_MMSS_FABRIC_CFG_HALT_0,
+	.offset = MSM_RPM_ID_MM_FABRIC_ARB_0,
+	.nmasters = 13,
+	.nslaves = 3,
+	.ntieredslaves = 3,
+	.board_algo = &msm_bus_board_algo,
+};
+
 struct msm_bus_fabric_registration msm_bus_8960_sys_fpb_pdata = {
 	.id = MSM_BUS_FAB_SYSTEM_FPB,
 	.name = "msm_sys_fpb",
diff --git a/arch/arm/mach-msm/pcie.c b/arch/arm/mach-msm/pcie.c
index f105356..d954b53 100644
--- a/arch/arm/mach-msm/pcie.c
+++ b/arch/arm/mach-msm/pcie.c
@@ -200,6 +200,16 @@
 static int msm_pcie_wr_conf(struct pci_bus *bus, u32 devfn,
 			    int where, int size, u32 val)
 {
+	/*
+	 *Attempt to reset secondary bus is causing PCIE core to reset.
+	 *Disable secondary bus reset functionality.
+	 */
+	if ((bus->number == 0) && (where == PCI_BRIDGE_CONTROL) &&
+	    (val & PCI_BRIDGE_CTL_BUS_RESET)) {
+		pr_info("PCIE secondary bus reset not supported\n");
+		val &= ~PCI_BRIDGE_CTL_BUS_RESET;
+	}
+
 	return msm_pcie_oper_conf(bus, devfn, WR, where, size, &val);
 }
 
diff --git a/arch/arm/mach-msm/pm2.c b/arch/arm/mach-msm/pm2.c
index 7a8e4c3..8fccda4 100644
--- a/arch/arm/mach-msm/pm2.c
+++ b/arch/arm/mach-msm/pm2.c
@@ -21,10 +21,8 @@
 #include <linux/clk.h>
 #include <linux/delay.h>
 #include <linux/init.h>
-#include <linux/pm.h>
 #include <linux/pm_qos.h>
 #include <linux/suspend.h>
-#include <linux/reboot.h>
 #include <linux/io.h>
 #include <linux/tick.h>
 #include <linux/memory.h>
@@ -34,7 +32,6 @@
 #include <asm/pgtable.h>
 #include <asm/pgalloc.h>
 #endif
-#include <asm/system_misc.h>
 #ifdef CONFIG_CACHE_L2X0
 #include <asm/hardware/cache-l2x0.h>
 #endif
@@ -1577,55 +1574,6 @@
 	}
 }
 
-/******************************************************************************
- * Restart Definitions
- *****************************************************************************/
-
-static uint32_t restart_reason = 0x776655AA;
-
-static void msm_pm_power_off(void)
-{
-	msm_rpcrouter_close();
-	msm_proc_comm(PCOM_POWER_DOWN, 0, 0);
-	for (;;)
-		;
-}
-
-static void msm_pm_restart(char str, const char *cmd)
-{
-	msm_rpcrouter_close();
-	msm_proc_comm(PCOM_RESET_CHIP, &restart_reason, 0);
-
-	for (;;)
-		;
-}
-
-static int msm_reboot_call
-	(struct notifier_block *this, unsigned long code, void *_cmd)
-{
-	if ((code == SYS_RESTART) && _cmd) {
-		char *cmd = _cmd;
-		if (!strcmp(cmd, "bootloader")) {
-			restart_reason = 0x77665500;
-		} else if (!strcmp(cmd, "recovery")) {
-			restart_reason = 0x77665502;
-		} else if (!strcmp(cmd, "eraseflash")) {
-			restart_reason = 0x776655EF;
-		} else if (!strncmp(cmd, "oem-", 4)) {
-			unsigned code = simple_strtoul(cmd + 4, 0, 16) & 0xff;
-			restart_reason = 0x6f656d00 | code;
-		} else {
-			restart_reason = 0x77665501;
-		}
-	}
-	return NOTIFY_DONE;
-}
-
-static struct notifier_block msm_reboot_notifier = {
-	.notifier_call = msm_reboot_call,
-};
-
-
 /*
  * Initialize the power management subsystem.
  *
@@ -1693,10 +1641,6 @@
 		     virt_to_phys(&msm_pm_pc_pgd));
 #endif
 
-	pm_power_off = msm_pm_power_off;
-	arm_pm_restart = msm_pm_restart;
-	register_reboot_notifier(&msm_reboot_notifier);
-
 	msm_pm_smem_data = smem_alloc(SMEM_APPS_DEM_SLAVE_DATA,
 		sizeof(*msm_pm_smem_data));
 	if (msm_pm_smem_data == NULL) {
diff --git a/arch/arm/mach-msm/restart_7k.c b/arch/arm/mach-msm/restart_7k.c
new file mode 100644
index 0000000..dc9edf4
--- /dev/null
+++ b/arch/arm/mach-msm/restart_7k.c
@@ -0,0 +1,101 @@
+/* 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/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/reboot.h>
+#include <linux/pm.h>
+#include <asm/system_misc.h>
+#include <mach/proc_comm.h>
+
+#include "devices-msm7x2xa.h"
+#include "smd_rpcrouter.h"
+
+static uint32_t restart_reason = 0x776655AA;
+
+static void msm_pm_power_off(void)
+{
+	msm_rpcrouter_close();
+	msm_proc_comm(PCOM_POWER_DOWN, 0, 0);
+	for (;;)
+		;
+}
+
+static void msm_pm_restart(char str, const char *cmd)
+{
+	msm_rpcrouter_close();
+	pr_debug("The reset reason is %x\n", restart_reason);
+
+	/* Disable interrupts */
+	local_irq_disable();
+	local_fiq_disable();
+
+	/*
+	 * Take out a flat memory mapping  and will
+	 * insert a 1:1 mapping in place of
+	 * the user-mode pages to ensure predictable results
+	 * This function takes care of flushing the caches
+	 * and flushing the TLB.
+	 */
+	setup_mm_for_reboot();
+
+	msm_proc_comm(PCOM_RESET_CHIP, &restart_reason, 0);
+
+	for (;;)
+		;
+}
+
+static int msm_reboot_call
+	(struct notifier_block *this, unsigned long code, void *_cmd)
+{
+	if ((code == SYS_RESTART) && _cmd) {
+		char *cmd = _cmd;
+		if (!strncmp(cmd, "bootloader", 10)) {
+			restart_reason = 0x77665500;
+		} else if (!strncmp(cmd, "recovery", 8)) {
+			restart_reason = 0x77665502;
+		} else if (!strncmp(cmd, "eraseflash", 10)) {
+			restart_reason = 0x776655EF;
+		} else if (!strncmp(cmd, "oem-", 4)) {
+			unsigned long code;
+			int res;
+			res = kstrtoul(cmd + 4, 16, &code);
+			code &= 0xff;
+			restart_reason = 0x6f656d00 | code;
+		} else {
+			restart_reason = 0x77665501;
+		}
+	}
+	return NOTIFY_DONE;
+}
+
+static struct notifier_block msm_reboot_notifier = {
+	.notifier_call = msm_reboot_call,
+};
+
+static int __init msm_pm_restart_init(void)
+{
+	int ret;
+
+	pm_power_off = msm_pm_power_off;
+	arm_pm_restart = msm_pm_restart;
+
+	ret = register_reboot_notifier(&msm_reboot_notifier);
+	if (ret)
+		pr_err("Failed to register reboot notifier\n");
+
+	return ret;
+}
+late_initcall(msm_pm_restart_init);
diff --git a/arch/arm/mach-msm/socinfo.c b/arch/arm/mach-msm/socinfo.c
index 817c2dc..f3b5720 100644
--- a/arch/arm/mach-msm/socinfo.c
+++ b/arch/arm/mach-msm/socinfo.c
@@ -793,6 +793,10 @@
 	case 0x510F06F0:
 		return MSM_CPU_8064;
 
+	case 0x511F06F1:
+	case 0x512F06F0:
+		return MSM_CPU_8974;
+
 	default:
 		return MSM_CPU_UNKNOWN;
 	};
diff --git a/arch/arm/mach-msm/wcnss-ssr-8960.c b/arch/arm/mach-msm/wcnss-ssr-8960.c
index 4295d9b..318523b 100644
--- a/arch/arm/mach-msm/wcnss-ssr-8960.c
+++ b/arch/arm/mach-msm/wcnss-ssr-8960.c
@@ -132,6 +132,7 @@
 {
 	pil_force_shutdown("wcnss");
 	flush_delayed_work(&cancel_vote_work);
+	wcnss_flush_delayed_boot_votes();
 	disable_irq_nosync(RIVA_APSS_WDOG_BITE_RESET_RDY_IRQ);
 
 	return 0;
diff --git a/drivers/bluetooth/hci_ibs.c b/drivers/bluetooth/hci_ibs.c
index 6845020..fb084f5 100644
--- a/drivers/bluetooth/hci_ibs.c
+++ b/drivers/bluetooth/hci_ibs.c
@@ -37,9 +37,7 @@
 #include <linux/fcntl.h>
 #include <linux/interrupt.h>
 #include <linux/ptrace.h>
-#include <linux/ftrace.h>
 #include <linux/poll.h>
-#include <linux/workqueue.h>
 
 #include <linux/slab.h>
 #include <linux/tty.h>
@@ -94,15 +92,6 @@
 	HCI_IBS_RX_VOTE_CLOCK_OFF,
 };
 
-/* HCI_IBS state for the WorkQueue */
-enum hci_ibs_wq_state_e {
-	HCI_IBS_WQ_INIT_STATE = 0,
-	HCI_IBS_WQ_TX_VOTE_OFF,
-	HCI_IBS_WQ_RX_VOTE_OFF,
-	HCI_IBS_WQ_AWAKE_RX,
-	HCI_IBS_WQ_AWAKE_DEVICE,
-};
-
 static unsigned long wake_retrans = 1;
 static unsigned long tx_idle_delay = (HZ * 2);
 
@@ -123,11 +112,6 @@
 	unsigned long rx_vote;		/* clock must be on for RX */
 	struct	timer_list tx_idle_timer;
 	struct	timer_list wake_retrans_timer;
-	struct	workqueue_struct *workqueue;
-	struct	work_struct ws_ibs;
-	unsigned long ibs_wq_state;
-	void *ibs_hu; /* keeps the hci_uart pointer for reference */
-
 	/* debug */
 	unsigned long ibs_sent_wacks;
 	unsigned long ibs_sent_slps;
@@ -258,56 +242,6 @@
 	return err;
 }
 
-static void ibs_wq(struct work_struct *work)
-{
-	unsigned long flags = 0;
-	struct ibs_struct *ibs = container_of(work, struct ibs_struct,
-						ws_ibs);
-	struct hci_uart *hu = (struct hci_uart *)ibs->ibs_hu;
-
-	BT_DBG("hu %p, ibs_wq state: %lu\n", hu, ibs->ibs_wq_state);
-
-	/* lock hci_ibs state */
-	spin_lock_irqsave(&ibs->hci_ibs_lock, flags);
-
-	switch (ibs->ibs_wq_state) {
-	case HCI_IBS_WQ_AWAKE_DEVICE:
-		/* Vote for serial clock */
-		ibs_msm_serial_clock_vote(HCI_IBS_TX_VOTE_CLOCK_ON, hu);
-
-		/* send wake indication to device */
-		if (send_hci_ibs_cmd(HCI_IBS_WAKE_IND, hu) < 0)
-			BT_ERR("cannot send WAKE to device");
-
-		ibs->ibs_sent_wakes++; /* debug */
-
-		/* start retransmit timer */
-		mod_timer(&ibs->wake_retrans_timer, jiffies + wake_retrans);
-		break;
-	case HCI_IBS_WQ_AWAKE_RX:
-		ibs_msm_serial_clock_vote(HCI_IBS_RX_VOTE_CLOCK_ON, hu);
-		ibs->rx_ibs_state = HCI_IBS_RX_AWAKE;
-
-		if (send_hci_ibs_cmd(HCI_IBS_WAKE_ACK, hu) < 0)
-			BT_ERR("cannot acknowledge device wake up");
-
-		ibs->ibs_sent_wacks++; /* debug */
-		/* actually send the packets */
-		hci_uart_tx_wakeup(hu);
-		break;
-	case HCI_IBS_WQ_RX_VOTE_OFF:
-		ibs_msm_serial_clock_vote(HCI_IBS_RX_VOTE_CLOCK_OFF, hu);
-		break;
-	case HCI_IBS_WQ_TX_VOTE_OFF:
-		ibs_msm_serial_clock_vote(HCI_IBS_TX_VOTE_CLOCK_OFF, hu);
-		break;
-	default:
-		BT_DBG("Invalid state in ibs workqueue");
-		break;
-	}
-	spin_unlock_irqrestore(&ibs->hci_ibs_lock, flags);
-}
-
 static void hci_ibs_tx_idle_timeout(unsigned long arg)
 {
 	struct hci_uart *hu = (struct hci_uart *) arg;
@@ -348,9 +282,7 @@
 
 	spin_lock_irqsave_nested(&ibs->hci_ibs_lock,
 					flags, SINGLE_DEPTH_NESTING);
-	/* vote off tx clock */
-	ibs->ibs_wq_state = HCI_IBS_WQ_TX_VOTE_OFF;
-	queue_work(ibs->workqueue, &ibs->ws_ibs);
+	ibs_msm_serial_clock_vote(HCI_IBS_TX_VOTE_CLOCK_OFF, hu);
 out:
 	spin_unlock_irqrestore(&ibs->hci_ibs_lock, flags);
 }
@@ -404,16 +336,6 @@
 	skb_queue_head_init(&ibs->txq);
 	skb_queue_head_init(&ibs->tx_wait_q);
 	spin_lock_init(&ibs->hci_ibs_lock);
-	ibs->workqueue = create_singlethread_workqueue("ibs_wq");
-	if (!ibs->workqueue) {
-		BT_ERR("IBS Workqueue not initialized properly");
-		kfree(ibs);
-		return -ENOMEM;
-	}
-
-	INIT_WORK(&ibs->ws_ibs, ibs_wq);
-	ibs->ibs_hu = (void *)hu;
-	ibs->ibs_wq_state = HCI_IBS_WQ_INIT_STATE;
 
 	/* Assume we start with both sides asleep -- extra wakes OK */
 	ibs->tx_ibs_state = HCI_IBS_TX_ASLEEP;
@@ -510,8 +432,6 @@
 	skb_queue_purge(&ibs->txq);
 	del_timer(&ibs->tx_idle_timer);
 	del_timer(&ibs->wake_retrans_timer);
-	destroy_workqueue(ibs->workqueue);
-	ibs->ibs_hu = NULL;
 
 	kfree_skb(ibs->rx_skb);
 
@@ -543,11 +463,9 @@
 		/* Make sure clock is on - we may have turned clock off since
 		 * receiving the wake up indicator
 		 */
-		/* awake rx clock */
-		ibs->ibs_wq_state = HCI_IBS_WQ_AWAKE_RX;
-		queue_work(ibs->workqueue, &ibs->ws_ibs);
-		spin_unlock_irqrestore(&ibs->hci_ibs_lock, flags);
-		return;
+		ibs_msm_serial_clock_vote(HCI_IBS_RX_VOTE_CLOCK_ON, hu);
+		ibs->rx_ibs_state = HCI_IBS_RX_AWAKE;
+		/* deliberate fall-through */
 	case HCI_IBS_RX_AWAKE:
 		/* Always acknowledge device wake up,
 		 * sending IBS message doesn't count as TX ON.
@@ -592,9 +510,7 @@
 	case HCI_IBS_RX_AWAKE:
 		/* update state */
 		ibs->rx_ibs_state = HCI_IBS_RX_ASLEEP;
-		/* vote off rx clock under workqueue */
-		ibs->ibs_wq_state = HCI_IBS_WQ_RX_VOTE_OFF;
-		queue_work(ibs->workqueue, &ibs->ws_ibs);
+		ibs_msm_serial_clock_vote(HCI_IBS_RX_VOTE_CLOCK_OFF, hu);
 		break;
 	case HCI_IBS_RX_ASLEEP:
 		/* deliberate fall-through */
@@ -679,12 +595,20 @@
 
 	case HCI_IBS_TX_ASLEEP:
 		BT_DBG("device asleep, waking up and queueing packet");
+		ibs_msm_serial_clock_vote(HCI_IBS_TX_VOTE_CLOCK_ON, hu);
 		/* save packet for later */
 		skb_queue_tail(&ibs->tx_wait_q, skb);
+		/* awake device */
+		if (send_hci_ibs_cmd(HCI_IBS_WAKE_IND, hu) < 0) {
+			BT_ERR("cannot send WAKE to device");
+			break;
+		}
+		ibs->ibs_sent_wakes++; /* debug */
+
+		/* start retransmit timer */
+		mod_timer(&ibs->wake_retrans_timer, jiffies + wake_retrans);
+
 		ibs->tx_ibs_state = HCI_IBS_TX_WAKING;
-		/* schedule a work queue to wake up device */
-		ibs->ibs_wq_state = HCI_IBS_WQ_AWAKE_DEVICE;
-		queue_work(ibs->workqueue, &ibs->ws_ibs);
 		break;
 
 	case HCI_IBS_TX_WAKING:
diff --git a/drivers/char/diag/diag_dci.h b/drivers/char/diag/diag_dci.h
index c0b82df..b70efe3 100644
--- a/drivers/char/diag/diag_dci.h
+++ b/drivers/char/diag/diag_dci.h
@@ -28,11 +28,6 @@
 	int signal_type;
 };
 
-#define DIAG_CON_APSS (0x0001)	/* Bit mask for APSS */
-#define DIAG_CON_MPSS (0x0002)	/* Bit mask for MPSS */
-#define DIAG_CON_LPASS (0x0004)	/* Bit mask for LPASS */
-#define DIAG_CON_WCNSS (0x0008)	/* Bit mask for WCNSS */
-
 enum {
 	DIAG_DCI_NO_ERROR = 1001,	/* No error */
 	DIAG_DCI_NO_REG,		/* Could not register */
diff --git a/drivers/char/diag/diagchar.h b/drivers/char/diag/diagchar.h
index 2f356f0..95a85f2a 100644
--- a/drivers/char/diag/diagchar.h
+++ b/drivers/char/diag/diagchar.h
@@ -58,6 +58,11 @@
 #define DIAG_CTRL_MSG_F3_MASK	11
 #define CONTROL_CHAR	0x7E
 
+#define DIAG_CON_APSS (0x0001)	/* Bit mask for APSS */
+#define DIAG_CON_MPSS (0x0002)	/* Bit mask for MPSS */
+#define DIAG_CON_LPASS (0x0004)	/* Bit mask for LPASS */
+#define DIAG_CON_WCNSS (0x0008)	/* Bit mask for WCNSS */
+
 /* Maximum number of pkt reg supported at initialization*/
 extern unsigned int diag_max_reg;
 extern unsigned int diag_threshold_reg;
@@ -224,6 +229,9 @@
 	struct work_struct diag_qdsp_mask_update_work;
 	struct work_struct diag_wcnss_mask_update_work;
 	struct work_struct diag_read_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;
 	uint8_t *msg_masks;
 	uint8_t *log_masks;
 	int log_masks_length;
diff --git a/drivers/char/diag/diagchar_core.c b/drivers/char/diag/diagchar_core.c
index 547f42f..240a514 100644
--- a/drivers/char/diag/diagchar_core.c
+++ b/drivers/char/diag/diagchar_core.c
@@ -1268,6 +1268,12 @@
 			diag_read_smd_wcnss_cntl_work_fn);
 		INIT_WORK(&(driver->diag_read_smd_dci_work),
 						 diag_read_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),
+						 diag_clean_lpass_reg_fn);
+		INIT_WORK(&(driver->diag_clean_wcnss_reg_work),
+						 diag_clean_wcnss_reg_fn);
 		diag_debugfs_init();
 		diagfwd_init();
 		diagfwd_cntl_init();
diff --git a/drivers/char/diag/diagfwd.c b/drivers/char/diag/diagfwd.c
index 384c1bf..b228276 100644
--- a/drivers/char/diag/diagfwd.c
+++ b/drivers/char/diag/diagfwd.c
@@ -1735,8 +1735,8 @@
 static void diag_smd_notify(void *ctxt, unsigned event)
 {
 	if (event == SMD_EVENT_CLOSE) {
-		pr_info("diag: clean modem registration\n");
-		diag_clear_reg(MODEM_PROC);
+		queue_work(driver->diag_cntl_wq,
+			 &(driver->diag_clean_modem_reg_work));
 		driver->ch = 0;
 		return;
 	} else if (event == SMD_EVENT_OPEN) {
@@ -1750,8 +1750,8 @@
 static void diag_smd_qdsp_notify(void *ctxt, unsigned event)
 {
 	if (event == SMD_EVENT_CLOSE) {
-		pr_info("diag: clean lpass registration\n");
-		diag_clear_reg(QDSP_PROC);
+		queue_work(driver->diag_cntl_wq,
+			 &(driver->diag_clean_lpass_reg_work));
 		driver->chqdsp = 0;
 		return;
 	} else if (event == SMD_EVENT_OPEN) {
@@ -1765,8 +1765,8 @@
 static void diag_smd_wcnss_notify(void *ctxt, unsigned event)
 {
 	if (event == SMD_EVENT_CLOSE) {
-		pr_info("diag: clean wcnss registration\n");
-		diag_clear_reg(WCNSS_PROC);
+		queue_work(driver->diag_cntl_wq,
+			 &(driver->diag_clean_wcnss_reg_work));
 		driver->ch_wcnss = 0;
 		return;
 	} else if (event == SMD_EVENT_OPEN) {
diff --git a/drivers/char/diag/diagfwd_cntl.c b/drivers/char/diag/diagfwd_cntl.c
index de1a5b5..95abd21 100644
--- a/drivers/char/diag/diagfwd_cntl.c
+++ b/drivers/char/diag/diagfwd_cntl.c
@@ -20,9 +20,34 @@
 #ifdef CONFIG_DEBUG_FS
 #include <linux/debugfs.h>
 #endif
-
+/* tracks which peripheral is undergoing SSR */
+static uint16_t reg_dirty;
 #define HDR_SIZ 8
 
+void diag_clean_modem_reg_fn(struct work_struct *work)
+{
+	pr_debug("diag: clean modem registration\n");
+	reg_dirty |= DIAG_CON_MPSS;
+	diag_clear_reg(MODEM_PROC);
+	reg_dirty ^= DIAG_CON_MPSS;
+}
+
+void diag_clean_lpass_reg_fn(struct work_struct *work)
+{
+	pr_debug("diag: clean lpass registration\n");
+	reg_dirty |= DIAG_CON_LPASS;
+	diag_clear_reg(QDSP_PROC);
+	reg_dirty ^= DIAG_CON_LPASS;
+}
+
+void diag_clean_wcnss_reg_fn(struct work_struct *work)
+{
+	pr_debug("diag: clean wcnss registration\n");
+	reg_dirty |= DIAG_CON_WCNSS;
+	diag_clear_reg(WCNSS_PROC);
+	reg_dirty ^= DIAG_CON_WCNSS;
+}
+
 void diag_smd_cntl_notify(void *ctxt, unsigned event)
 {
 	int r1, r2;
@@ -105,6 +130,8 @@
 	struct bindpkt_params *temp;
 	void *buf = NULL;
 	smd_channel_t *smd_ch = NULL;
+	/* tracks which peripheral is sending registration */
+	uint16_t reg_mask = 0;
 
 	if (pkt_params == NULL) {
 		pr_alert("diag: Memory allocation failure\n");
@@ -114,12 +141,15 @@
 	if (proc_num == MODEM_PROC) {
 		buf = driver->buf_in_cntl;
 		smd_ch = driver->ch_cntl;
+		reg_mask = DIAG_CON_MPSS;
 	} else if (proc_num == QDSP_PROC) {
 		buf = driver->buf_in_qdsp_cntl;
 		smd_ch = driver->chqdsp_cntl;
+		reg_mask = DIAG_CON_LPASS;
 	} else if (proc_num == WCNSS_PROC) {
 		buf = driver->buf_in_wcnss_cntl;
 		smd_ch = driver->ch_wcnss_cntl;
+		reg_mask = DIAG_CON_WCNSS;
 	}
 
 	if (!smd_ch || !buf) {
@@ -180,8 +210,16 @@
 				temp -= pkt_params->count;
 				pkt_params->params = temp;
 				flag = 1;
-				diagchar_ioctl(NULL, DIAG_IOCTL_COMMAND_REG,
-						 (unsigned long)pkt_params);
+				/* peripheral undergoing SSR should not
+				 * record new registration
+				 */
+				if (!(reg_dirty & reg_mask))
+					diagchar_ioctl(NULL,
+					 DIAG_IOCTL_COMMAND_REG, (unsigned long)
+								pkt_params);
+				else
+					pr_err("diag: drop reg proc %d\n",
+								 proc_num);
 				kfree(temp);
 			}
 			buf = buf + HDR_SIZ + data_len;
@@ -275,6 +313,7 @@
 
 void diagfwd_cntl_init(void)
 {
+	reg_dirty = 0;
 	driver->polling_reg_flag = 0;
 	driver->diag_cntl_wq = create_singlethread_workqueue("diag_cntl_wq");
 	if (driver->buf_in_cntl == NULL) {
diff --git a/drivers/char/diag/diagfwd_cntl.h b/drivers/char/diag/diagfwd_cntl.h
index 743ddc1..59e5e6b 100644
--- a/drivers/char/diag/diagfwd_cntl.h
+++ b/drivers/char/diag/diagfwd_cntl.h
@@ -85,7 +85,9 @@
 void diag_smd_cntl_notify(void *ctxt, unsigned event);
 void diag_smd_qdsp_cntl_notify(void *ctxt, unsigned event);
 void diag_smd_wcnss_cntl_notify(void *ctxt, unsigned event);
-
+void diag_clean_modem_reg_fn(struct work_struct *);
+void diag_clean_lpass_reg_fn(struct work_struct *);
+void diag_clean_wcnss_reg_fn(struct work_struct *);
 void diag_debugfs_init(void);
 void diag_debugfs_cleanup(void);
 
diff --git a/drivers/coresight/coresight-tpiu.c b/drivers/coresight/coresight-tpiu.c
index 4b52c4d..c0bcfdd 100644
--- a/drivers/coresight/coresight-tpiu.c
+++ b/drivers/coresight/coresight-tpiu.c
@@ -119,9 +119,17 @@
 	if (ret)
 		goto err_clk_rate;
 
+	/* Disable tpiu to support older targets that need this */
+	ret = clk_prepare_enable(drvdata->clk);
+	if (ret)
+		goto err_clk_enable;
+	__tpiu_disable();
+	clk_disable_unprepare(drvdata->clk);
+
 	dev_info(drvdata->dev, "TPIU initialized\n");
 	return 0;
 
+err_clk_enable:
 err_clk_rate:
 	clk_put(drvdata->clk);
 err_clk_get:
diff --git a/drivers/coresight/coresight.c b/drivers/coresight/coresight.c
index 055ef55..a17ac9a 100644
--- a/drivers/coresight/coresight.c
+++ b/drivers/coresight/coresight.c
@@ -20,71 +20,351 @@
 #include <linux/err.h>
 #include <linux/export.h>
 #include <linux/slab.h>
-#include <linux/mutex.h>
+#include <linux/semaphore.h>
 #include <linux/clk.h>
 #include <linux/coresight.h>
 
 #include "coresight-priv.h"
 
+
+#define NO_SINK		(-1)
 #define MAX_STR_LEN	(65535)
 
 
+static int curr_sink = NO_SINK;
 static LIST_HEAD(coresight_orph_conns);
-static DEFINE_MUTEX(coresight_conns_mutex);
 static LIST_HEAD(coresight_devs);
-static DEFINE_MUTEX(coresight_devs_mutex);
+static DEFINE_SEMAPHORE(coresight_mutex);
 
 
-int coresight_enable(struct coresight_device *csdev, int port)
+static int coresight_find_link_inport(struct coresight_device *csdev)
 {
 	int i;
-	int ret;
+	struct coresight_device *parent;
 	struct coresight_connection *conn;
 
-	mutex_lock(&csdev->mutex);
-	if (csdev->refcnt[port] == 0) {
-		for (i = 0; i < csdev->nr_conns; i++) {
-			conn = &csdev->conns[i];
-			ret = coresight_enable(conn->child_dev,
-					       conn->child_port);
-			if (ret)
-				goto err_enable_child;
-		}
-		if (csdev->ops->enable)
-			ret = csdev->ops->enable(csdev, port);
-		if (ret)
-			goto err_enable;
+	parent = container_of(csdev->path_link.next, struct coresight_device,
+			     path_link);
+	for (i = 0; i < parent->nr_conns; i++) {
+		conn = &parent->conns[i];
+		if (conn->child_dev == csdev)
+			return conn->child_port;
 	}
-	csdev->refcnt[port]++;
-	mutex_unlock(&csdev->mutex);
+
+	pr_err("coresight: couldn't find inport, parent: %d, child: %d\n",
+	       parent->id, csdev->id);
 	return 0;
-err_enable_child:
-	while (i) {
-		conn = &csdev->conns[--i];
-		coresight_disable(conn->child_dev, conn->child_port);
+}
+
+static int coresight_find_link_outport(struct coresight_device *csdev)
+{
+	int i;
+	struct coresight_device *child;
+	struct coresight_connection *conn;
+
+	child = container_of(csdev->path_link.prev, struct coresight_device,
+			      path_link);
+	for (i = 0; i < csdev->nr_conns; i++) {
+		conn = &csdev->conns[i];
+		if (conn->child_dev == child)
+			return conn->outport;
 	}
-err_enable:
-	mutex_unlock(&csdev->mutex);
+
+	pr_err("coresight: couldn't find outport, parent: %d, child: %d\n",
+	       csdev->id, child->id);
+	return 0;
+}
+
+static int coresight_enable_sink(struct coresight_device *csdev)
+{
+	int ret;
+
+	if (csdev->refcnt.sink_refcnt == 0) {
+		if (csdev->ops->sink_ops->enable) {
+			ret = csdev->ops->sink_ops->enable(csdev);
+			if (ret)
+				goto err;
+			csdev->enable = true;
+		}
+	}
+	csdev->refcnt.sink_refcnt++;
+
+	return 0;
+err:
+	return ret;
+}
+
+static void coresight_disable_sink(struct coresight_device *csdev)
+{
+	if (csdev->refcnt.sink_refcnt == 1) {
+		if (csdev->ops->sink_ops->disable) {
+			csdev->ops->sink_ops->disable(csdev);
+			csdev->enable = false;
+		}
+	}
+	csdev->refcnt.sink_refcnt--;
+}
+
+static int coresight_enable_link(struct coresight_device *csdev)
+{
+	int ret;
+	int link_subtype;
+	int refport, inport, outport;
+
+	inport = coresight_find_link_inport(csdev);
+	outport = coresight_find_link_outport(csdev);
+
+	link_subtype = csdev->subtype.link_subtype;
+	if (link_subtype == CORESIGHT_DEV_SUBTYPE_LINK_MERG)
+		refport = inport;
+	else if (link_subtype == CORESIGHT_DEV_SUBTYPE_LINK_SPLIT)
+		refport = outport;
+	else
+		refport = 0;
+
+	if (csdev->refcnt.link_refcnts[refport] == 0) {
+		if (csdev->ops->link_ops->enable) {
+			ret = csdev->ops->link_ops->enable(csdev, inport,
+							   outport);
+			if (ret)
+				goto err;
+			csdev->enable = true;
+		}
+	}
+	csdev->refcnt.link_refcnts[refport]++;
+
+	return 0;
+err:
+	return ret;
+}
+
+static void coresight_disable_link(struct coresight_device *csdev)
+{
+	int link_subtype;
+	int refport, inport, outport;
+
+	inport = coresight_find_link_inport(csdev);
+	outport = coresight_find_link_outport(csdev);
+
+	link_subtype = csdev->subtype.link_subtype;
+	if (link_subtype == CORESIGHT_DEV_SUBTYPE_LINK_MERG)
+		refport = inport;
+	else if (link_subtype == CORESIGHT_DEV_SUBTYPE_LINK_SPLIT)
+		refport = outport;
+	else
+		refport = 0;
+
+	if (csdev->refcnt.link_refcnts[refport] == 1) {
+		if (csdev->ops->link_ops->disable) {
+			csdev->ops->link_ops->disable(csdev, inport, outport);
+			csdev->enable = false;
+		}
+	}
+	csdev->refcnt.link_refcnts[refport]--;
+}
+
+static int coresight_enable_source(struct coresight_device *csdev)
+{
+	int ret;
+
+	if (csdev->refcnt.source_refcnt == 0) {
+		if (csdev->ops->source_ops->enable) {
+			ret = csdev->ops->source_ops->enable(csdev);
+			if (ret)
+				goto err;
+			csdev->enable = true;
+		}
+	}
+	csdev->refcnt.source_refcnt++;
+
+	return 0;
+err:
+	return ret;
+}
+
+static void coresight_disable_source(struct coresight_device *csdev)
+{
+	if (csdev->refcnt.source_refcnt == 1) {
+		if (csdev->ops->source_ops->disable) {
+			csdev->ops->source_ops->disable(csdev);
+			csdev->enable = false;
+		}
+	}
+	csdev->refcnt.source_refcnt--;
+}
+
+static struct list_head *coresight_build_path(struct coresight_device *csdev,
+					      struct list_head *path)
+{
+	int i;
+	struct list_head *p;
+	struct coresight_connection *conn;
+
+	if (csdev->id == curr_sink) {
+		list_add_tail(&csdev->path_link, path);
+		return path;
+	}
+
+	for (i = 0; i < csdev->nr_conns; i++) {
+		conn = &csdev->conns[i];
+		p = coresight_build_path(conn->child_dev, path);
+		if (p) {
+			list_add_tail(&csdev->path_link, p);
+			return p;
+		}
+	}
+	return NULL;
+}
+
+static void coresight_release_path(struct list_head *path)
+{
+	struct coresight_device *cd, *temp;
+
+	list_for_each_entry_safe(cd, temp, path, path_link)
+		list_del(&cd->path_link);
+}
+
+static int coresight_enable_path(struct list_head *path, bool incl_source)
+{
+	int ret = 0;
+	struct coresight_device *cd;
+
+	list_for_each_entry(cd, path, path_link) {
+		if (cd == list_first_entry(path, struct coresight_device,
+					   path_link)) {
+			ret = coresight_enable_sink(cd);
+		} else if (list_is_last(&cd->path_link, path)) {
+			if (incl_source)
+				ret = coresight_enable_source(cd);
+		} else {
+			ret = coresight_enable_link(cd);
+		}
+		if (ret)
+			goto err;
+	}
+	return 0;
+err:
+	list_for_each_entry_continue_reverse(cd, path, path_link) {
+		if (cd == list_first_entry(path, struct coresight_device,
+					   path_link)) {
+			coresight_disable_sink(cd);
+		} else if (list_is_last(&cd->path_link, path)) {
+			if (incl_source)
+				coresight_disable_source(cd);
+		} else {
+			coresight_disable_link(cd);
+		}
+	}
+	return ret;
+}
+
+static void coresight_disable_path(struct list_head *path, bool incl_source)
+{
+	struct coresight_device *cd;
+
+	list_for_each_entry(cd, path, path_link) {
+		if (cd == list_first_entry(path, struct coresight_device,
+					   path_link)) {
+			coresight_disable_sink(cd);
+		} else if (list_is_last(&cd->path_link, path)) {
+			if (incl_source)
+				coresight_disable_source(cd);
+		} else {
+			coresight_disable_link(cd);
+		}
+	}
+}
+
+static int coresight_switch_sink(struct coresight_device *csdev)
+{
+	int ret = 0;
+	LIST_HEAD(path);
+	struct coresight_device *cd;
+
+	if (IS_ERR_OR_NULL(csdev))
+		return -EINVAL;
+
+	down(&coresight_mutex);
+	if (csdev->id == curr_sink)
+		goto out;
+
+	list_for_each_entry(cd, &coresight_devs, dev_link) {
+		if (cd->type == CORESIGHT_DEV_TYPE_SOURCE && cd->enable) {
+			coresight_build_path(cd, &path);
+			coresight_disable_path(&path, false);
+			coresight_release_path(&path);
+		}
+	}
+	curr_sink = csdev->id;
+	list_for_each_entry(cd, &coresight_devs, dev_link) {
+		if (cd->type == CORESIGHT_DEV_TYPE_SOURCE && cd->enable) {
+			coresight_build_path(cd, &path);
+			ret = coresight_enable_path(&path, false);
+			coresight_release_path(&path);
+			if (ret)
+				goto err;
+		}
+	}
+out:
+	up(&coresight_mutex);
+	return 0;
+err:
+	list_for_each_entry(cd, &coresight_devs, dev_link) {
+		if (cd->type == CORESIGHT_DEV_TYPE_SOURCE && cd->enable)
+			coresight_disable_source(cd);
+	}
+	pr_err("coresight: sink switch failed, sources disabled; try again\n");
+	return ret;
+}
+
+int coresight_enable(struct coresight_device *csdev)
+{
+	int ret = 0;
+	LIST_HEAD(path);
+
+	if (IS_ERR_OR_NULL(csdev))
+		return -EINVAL;
+
+	down(&coresight_mutex);
+	if (csdev->type != CORESIGHT_DEV_TYPE_SOURCE) {
+		ret = -EINVAL;
+		pr_err("coresight: wrong device type in %s\n", __func__);
+		goto out;
+	}
+	if (csdev->enable)
+		goto out;
+
+	coresight_build_path(csdev, &path);
+	ret = coresight_enable_path(&path, true);
+	coresight_release_path(&path);
+	if (ret)
+		pr_err("coresight: enable failed\n");
+out:
+	up(&coresight_mutex);
 	return ret;
 }
 EXPORT_SYMBOL(coresight_enable);
 
-void coresight_disable(struct coresight_device *csdev, int port)
+void coresight_disable(struct coresight_device *csdev)
 {
-	int i;
-	struct coresight_connection *conn;
+	LIST_HEAD(path);
 
-	mutex_lock(&csdev->mutex);
-	if (csdev->refcnt[port] == 1) {
-		if (csdev->ops->disable)
-			csdev->ops->disable(csdev, port);
-		for (i = 0; i < csdev->nr_conns; i++) {
-			conn = &csdev->conns[i];
-			coresight_disable(conn->child_dev, conn->child_port);
-		}
+	if (IS_ERR_OR_NULL(csdev))
+		return;
+
+	down(&coresight_mutex);
+	if (csdev->type != CORESIGHT_DEV_TYPE_SOURCE) {
+		pr_err("coresight: wrong device type in %s\n", __func__);
+		goto out;
 	}
-	csdev->refcnt[port]--;
-	mutex_unlock(&csdev->mutex);
+	if (!csdev->enable)
+		goto out;
+
+	coresight_build_path(csdev, &path);
+	coresight_disable_path(&path, true);
+	coresight_release_path(&path);
+out:
+	up(&coresight_mutex);
 }
 EXPORT_SYMBOL(coresight_disable);
 
@@ -104,6 +384,39 @@
 	.dev_attrs	= coresight_dev_attrs,
 };
 
+static ssize_t coresight_show_curr_sink(struct device *dev,
+					struct device_attribute *attr,
+					char *buf)
+{
+	struct coresight_device *csdev = to_coresight_device(dev);
+
+	return scnprintf(buf, PAGE_SIZE, "%u\n",
+			 csdev->id == curr_sink ? 1 : 0);
+}
+
+static ssize_t coresight_store_curr_sink(struct device *dev,
+					 struct device_attribute *attr,
+					 const char *buf, size_t size)
+{
+	int ret = 0;
+	unsigned long val;
+	struct coresight_device *csdev = to_coresight_device(dev);
+
+	if (sscanf(buf, "%lx", &val) != 1)
+		return -EINVAL;
+
+	if (val)
+		ret = coresight_switch_sink(csdev);
+	else
+		ret = -EINVAL;
+
+	if (ret)
+		return ret;
+	return size;
+}
+static DEVICE_ATTR(curr_sink, S_IRUGO | S_IWUSR, coresight_show_curr_sink,
+		   coresight_store_curr_sink);
+
 static ssize_t coresight_show_enable(struct device *dev,
 				     struct device_attribute *attr, char *buf)
 {
@@ -124,9 +437,9 @@
 		return -EINVAL;
 
 	if (val)
-		ret = coresight_enable(csdev, 0);
+		ret = coresight_enable(csdev);
 	else
-		coresight_disable(csdev, 0);
+		coresight_disable(csdev);
 
 	if (ret)
 		return ret;
@@ -135,38 +448,55 @@
 static DEVICE_ATTR(enable, S_IRUGO | S_IWUSR, coresight_show_enable,
 		   coresight_store_enable);
 
-static struct attribute *coresight_attrs[] = {
+static struct attribute *coresight_attrs_sink[] = {
+	&dev_attr_curr_sink.attr,
+	NULL,
+};
+
+static struct attribute_group coresight_attr_grp_sink = {
+	.attrs = coresight_attrs_sink,
+};
+
+static const struct attribute_group *coresight_attr_grps_sink[] = {
+	&coresight_attr_grp_sink,
+	NULL,
+};
+
+static struct attribute *coresight_attrs_source[] = {
 	&dev_attr_enable.attr,
 	NULL,
 };
 
-static struct attribute_group coresight_attr_grp = {
-	.attrs = coresight_attrs,
+static struct attribute_group coresight_attr_grp_source = {
+	.attrs = coresight_attrs_source,
 };
 
-static const struct attribute_group *coresight_attr_grps[] = {
-	&coresight_attr_grp,
+static const struct attribute_group *coresight_attr_grps_source[] = {
+	&coresight_attr_grp_source,
 	NULL,
 };
 
-static struct device_type coresight_dev_type[CORESIGHT_DEV_TYPE_MAX] = {
+static struct device_type coresight_dev_type[] = {
 	{
-		.name = "source",
-		.groups = coresight_attr_grps,
+		.name = "sink",
+		.groups = coresight_attr_grps_sink,
 	},
 	{
 		.name = "link",
 	},
 	{
-		.name = "sink",
-		.groups = coresight_attr_grps,
+		.name = "linksink",
+		.groups = coresight_attr_grps_sink,
+	},
+	{
+		.name = "source",
+		.groups = coresight_attr_grps_source,
 	},
 };
 
 static void coresight_device_release(struct device *dev)
 {
 	struct coresight_device *csdev = to_coresight_device(dev);
-	mutex_destroy(&csdev->mutex);
 	kfree(csdev);
 }
 
@@ -174,14 +504,12 @@
 {
 	struct coresight_connection *conn, *temp;
 
-	mutex_lock(&coresight_conns_mutex);
 	list_for_each_entry_safe(conn, temp, &coresight_orph_conns, link) {
 		if (conn->child_id == csdev->id) {
 			conn->child_dev = csdev;
 			list_del(&conn->link);
 		}
 	}
-	mutex_unlock(&coresight_conns_mutex);
 }
 
 static void coresight_fixup_device_conns(struct coresight_device *csdev)
@@ -192,21 +520,16 @@
 
 	for (i = 0; i < csdev->nr_conns; i++) {
 		found = false;
-		mutex_lock(&coresight_devs_mutex);
-		list_for_each_entry(cd, &coresight_devs, link) {
+		list_for_each_entry(cd, &coresight_devs, dev_link) {
 			if (csdev->conns[i].child_id == cd->id) {
 				csdev->conns[i].child_dev = cd;
 				found = true;
 				break;
 			}
 		}
-		mutex_unlock(&coresight_devs_mutex);
-		if (!found) {
-			mutex_lock(&coresight_conns_mutex);
+		if (!found)
 			list_add_tail(&csdev->conns[i].link,
 				      &coresight_orph_conns);
-			mutex_unlock(&coresight_conns_mutex);
-		}
 	}
 }
 
@@ -214,7 +537,9 @@
 {
 	int i;
 	int ret;
-	int *refcnt;
+	int link_subtype;
+	int nr_refcnts;
+	int *refcnts = NULL;
 	struct coresight_device *csdev;
 	struct coresight_connection *conns;
 
@@ -224,28 +549,41 @@
 		goto err_kzalloc_csdev;
 	}
 
-	mutex_init(&csdev->mutex);
 	csdev->id = desc->pdata->id;
 
-	refcnt = kzalloc(sizeof(*refcnt) * desc->pdata->nr_ports, GFP_KERNEL);
-	if (!refcnt) {
-		ret = -ENOMEM;
-		goto err_kzalloc_refcnt;
-	}
-	csdev->refcnt = refcnt;
+	if (desc->type == CORESIGHT_DEV_TYPE_LINK ||
+	    desc->type == CORESIGHT_DEV_TYPE_LINKSINK) {
+		link_subtype = desc->subtype.link_subtype;
+		if (link_subtype == CORESIGHT_DEV_SUBTYPE_LINK_MERG)
+			nr_refcnts = desc->pdata->nr_inports;
+		else if (link_subtype == CORESIGHT_DEV_SUBTYPE_LINK_SPLIT)
+			nr_refcnts = desc->pdata->nr_outports;
+		else
+			nr_refcnts = 1;
 
-	csdev->nr_conns = desc->pdata->nr_children;
+		refcnts = kzalloc(sizeof(*refcnts) * nr_refcnts, GFP_KERNEL);
+		if (!refcnts) {
+			ret = -ENOMEM;
+			goto err_kzalloc_refcnts;
+		}
+		csdev->refcnt.link_refcnts = refcnts;
+	}
+
+	csdev->nr_conns = desc->pdata->nr_outports;
 	conns = kzalloc(sizeof(*conns) * csdev->nr_conns, GFP_KERNEL);
 	if (!conns) {
 		ret = -ENOMEM;
 		goto err_kzalloc_conns;
 	}
 	for (i = 0; i < csdev->nr_conns; i++) {
+		conns[i].outport = desc->pdata->outports[i];
 		conns[i].child_id = desc->pdata->child_ids[i];
 		conns[i].child_port = desc->pdata->child_ports[i];
 	}
 	csdev->conns = conns;
 
+	csdev->type = desc->type;
+	csdev->subtype = desc->subtype;
 	csdev->ops = desc->ops;
 	csdev->owner = desc->owner;
 
@@ -256,24 +594,34 @@
 	csdev->dev.release = coresight_device_release;
 	dev_set_name(&csdev->dev, "%s", desc->pdata->name);
 
+	down(&coresight_mutex);
+	if (desc->pdata->default_sink) {
+		if (curr_sink == NO_SINK) {
+			curr_sink = csdev->id;
+		} else {
+			ret = -EINVAL;
+			goto err_default_sink;
+		}
+	}
+
 	coresight_fixup_device_conns(csdev);
 	ret = device_register(&csdev->dev);
 	if (ret)
 		goto err_dev_reg;
 	coresight_fixup_orphan_conns(csdev);
 
-	mutex_lock(&coresight_devs_mutex);
-	list_add_tail(&csdev->link, &coresight_devs);
-	mutex_unlock(&coresight_devs_mutex);
+	list_add_tail(&csdev->dev_link, &coresight_devs);
+	up(&coresight_mutex);
 
 	return csdev;
 err_dev_reg:
 	put_device(&csdev->dev);
+err_default_sink:
+	up(&coresight_mutex);
 	kfree(conns);
 err_kzalloc_conns:
-	kfree(refcnt);
-err_kzalloc_refcnt:
-	mutex_destroy(&csdev->mutex);
+	kfree(refcnts);
+err_kzalloc_refcnts:
 	kfree(csdev);
 err_kzalloc_csdev:
 	return ERR_PTR(ret);
@@ -286,9 +634,7 @@
 		return;
 
 	if (get_device(&csdev->dev)) {
-		mutex_lock(&csdev->mutex);
 		device_unregister(&csdev->dev);
-		mutex_unlock(&csdev->mutex);
 		put_device(&csdev->dev);
 	}
 }
diff --git a/drivers/input/touchscreen/msm_ts.c b/drivers/input/touchscreen/msm_ts.c
index eb2e73b..e66120e 100644
--- a/drivers/input/touchscreen/msm_ts.c
+++ b/drivers/input/touchscreen/msm_ts.c
@@ -387,6 +387,7 @@
 
 	input_set_capability(ts->input_dev, EV_KEY, BTN_TOUCH);
 	set_bit(EV_ABS, ts->input_dev->evbit);
+	set_bit(INPUT_PROP_DIRECT, ts->input_dev->propbit);
 
 	input_set_abs_params(ts->input_dev, ABS_X, pdata->min_x, pdata->max_x,
 			     0, 0);
diff --git a/drivers/iommu/msm_iommu-v2.c b/drivers/iommu/msm_iommu-v2.c
index 48edf96..28ad0ff 100644
--- a/drivers/iommu/msm_iommu-v2.c
+++ b/drivers/iommu/msm_iommu-v2.c
@@ -51,10 +51,16 @@
 	if (ret)
 		goto fail;
 
-	if (drvdata->clk) {
-		ret = clk_prepare_enable(drvdata->clk);
-		if (ret)
+	ret = clk_prepare_enable(drvdata->clk);
+	if (ret)
+		clk_disable_unprepare(drvdata->pclk);
+
+	if (drvdata->aclk) {
+		ret = clk_prepare_enable(drvdata->aclk);
+		if (ret) {
+			clk_disable_unprepare(drvdata->clk);
 			clk_disable_unprepare(drvdata->pclk);
+		}
 	}
 fail:
 	return ret;
@@ -62,8 +68,9 @@
 
 static void __disable_clocks(struct msm_iommu_drvdata *drvdata)
 {
-	if (drvdata->clk)
-		clk_disable_unprepare(drvdata->clk);
+	if (drvdata->aclk)
+		clk_disable_unprepare(drvdata->aclk);
+	clk_disable_unprepare(drvdata->clk);
 	clk_disable_unprepare(drvdata->pclk);
 }
 
diff --git a/drivers/iommu/msm_iommu_dev-v2.c b/drivers/iommu/msm_iommu_dev-v2.c
index 14ed5d9..8c26f95 100644
--- a/drivers/iommu/msm_iommu_dev-v2.c
+++ b/drivers/iommu/msm_iommu_dev-v2.c
@@ -69,7 +69,7 @@
 {
 	struct msm_iommu_drvdata *drvdata;
 	struct resource *r;
-	int ret;
+	int ret, needs_alt_core_clk;
 
 	if (msm_iommu_root_dev == pdev)
 		return 0;
@@ -93,55 +93,42 @@
 	if (IS_ERR(drvdata->gdsc))
 		return -EINVAL;
 
-	drvdata->pclk = clk_get(&pdev->dev, "iface_clk");
+	drvdata->pclk = devm_clk_get(&pdev->dev, "iface_clk");
 	if (IS_ERR(drvdata->pclk))
 		return PTR_ERR(drvdata->pclk);
 
-	ret = clk_prepare_enable(drvdata->pclk);
-	if (ret)
-		goto fail_enable;
+	drvdata->clk = devm_clk_get(&pdev->dev, "core_clk");
+	if (IS_ERR(drvdata->clk))
+		return PTR_ERR(drvdata->clk);
 
-	drvdata->clk = clk_get(&pdev->dev, "core_clk");
-	if (!IS_ERR(drvdata->clk)) {
-		if (clk_get_rate(drvdata->clk) == 0) {
-			ret = clk_round_rate(drvdata->clk, 1);
-			clk_set_rate(drvdata->clk, ret);
-		}
+	needs_alt_core_clk = of_property_read_bool(pdev->dev.of_node,
+						   "qcom,needs-alt-core-clk");
+	if (needs_alt_core_clk) {
+		drvdata->aclk = devm_clk_get(&pdev->dev, "alt_core_clk");
+		if (IS_ERR(drvdata->aclk))
+			return PTR_ERR(drvdata->aclk);
+	}
 
-		ret = clk_prepare_enable(drvdata->clk);
-		if (ret) {
-			clk_put(drvdata->clk);
-			goto fail_pclk;
-		}
-	} else
-		drvdata->clk = NULL;
+	if (clk_get_rate(drvdata->clk) == 0) {
+		ret = clk_round_rate(drvdata->clk, 1);
+		clk_set_rate(drvdata->clk, ret);
+	}
+
+	if (drvdata->aclk && clk_get_rate(drvdata->aclk) == 0) {
+		ret = clk_round_rate(drvdata->aclk, 1);
+		clk_set_rate(drvdata->aclk, ret);
+	}
 
 	ret = msm_iommu_parse_dt(pdev, drvdata);
 	if (ret)
-		goto fail_clk;
+		return ret;
 
 	pr_info("device %s mapped at %p, with %d ctx banks\n",
 		drvdata->name, drvdata->base, drvdata->ncb);
 
 	platform_set_drvdata(pdev, drvdata);
 
-	if (drvdata->clk)
-		clk_disable_unprepare(drvdata->clk);
-
-	clk_disable_unprepare(drvdata->pclk);
-
 	return 0;
-
-fail_clk:
-	if (drvdata->clk) {
-		clk_disable_unprepare(drvdata->clk);
-		clk_put(drvdata->clk);
-	}
-fail_pclk:
-	clk_disable_unprepare(drvdata->pclk);
-fail_enable:
-	clk_put(drvdata->pclk);
-	return ret;
 }
 
 static int __devexit msm_iommu_remove(struct platform_device *pdev)
diff --git a/drivers/media/video/msm/msm_isp.c b/drivers/media/video/msm/msm_isp.c
index 67e7c02..9ddde15 100644
--- a/drivers/media/video/msm/msm_isp.c
+++ b/drivers/media/video/msm/msm_isp.c
@@ -424,10 +424,12 @@
 		stats.buf_idx = isp_stats->buf_idx;
 		switch (isp_stats->id) {
 		case MSG_ID_STATS_AEC:
+		case MSG_ID_STATS_BG:
 			stats.aec.buff = stats.buffer;
 			stats.aec.fd = stats.fd;
 			break;
 		case MSG_ID_STATS_AF:
+		case MSG_ID_STATS_BF:
 			stats.af.buff = stats.buffer;
 			stats.af.fd = stats.fd;
 			break;
@@ -447,6 +449,10 @@
 			stats.cs.buff = stats.buffer;
 			stats.cs.fd = stats.fd;
 			break;
+		case MSG_ID_STATS_BHIST:
+			stats.skin.buff = stats.buffer;
+			stats.skin.fd = stats.fd;
+			break;
 		case MSG_ID_STATS_AWB_AEC:
 			break;
 		default:
@@ -537,6 +543,9 @@
 	memset(&axi_data, 0, sizeof(axi_data));
 	CDBG("%s: cmd_type %d\n", __func__, cfgcmd.cmd_type);
 	switch (cfgcmd.cmd_type) {
+	case CMD_STATS_BG_ENABLE:
+	case CMD_STATS_BF_ENABLE:
+	case CMD_STATS_BHIST_ENABLE:
 	case CMD_STATS_AF_ENABLE:
 	case CMD_STATS_AEC_ENABLE:
 	case CMD_STATS_AWB_ENABLE:
@@ -629,6 +638,12 @@
 			cfgcmd.cmd_type = CMD_STATS_CS_BUF_RELEASE;
 		else if (buf.type == STAT_AEAW)
 			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_BF)
+			cfgcmd.cmd_type = CMD_STATS_BF_BUF_RELEASE;
+		else if (buf.type == STAT_BHIST)
+			cfgcmd.cmd_type = CMD_STATS_BHIST_BUF_RELEASE;
 
 		else {
 			pr_err("%s: invalid buf type %d\n",
@@ -673,7 +688,6 @@
 	}
 	case MSM_CAM_IOCTL_STATS_ENQUEUEBUF: {
 		struct msm_stats_buf_info buf_info;
-
 		if (copy_from_user(&buf_info, arg,
 			sizeof(struct msm_stats_buf_info))) {
 			ERR_COPY_FROM_USER();
@@ -687,18 +701,30 @@
 	}
 	case MSM_CAM_IOCTL_STATS_FLUSH_BUFQ: {
 		struct msm_stats_flush_bufq bufq_info;
-
 		if (copy_from_user(&bufq_info, arg,
 			sizeof(struct msm_stats_flush_bufq))) {
 			ERR_COPY_FROM_USER();
 			return -EFAULT;
-	}
+		}
 	cfgcmd.cmd_type = VFE_CMD_STATS_FLUSH_BUFQ;
 	cfgcmd.value = (void *)&bufq_info;
 	cfgcmd.length = sizeof(struct msm_stats_flush_bufq);
 	rc = msm_isp_subdev_ioctl(sd, &cfgcmd, NULL);
 	break;
 	}
+	case MSM_CAM_IOCTL_STATS_UNREG_BUF: {
+		struct msm_stats_reqbuf reqbuf;
+		if (copy_from_user(&reqbuf, arg,
+			sizeof(struct msm_stats_reqbuf))) {
+			ERR_COPY_FROM_USER();
+			return -EFAULT;
+		}
+	cfgcmd.cmd_type = VFE_CMD_STATS_UNREGBUF;
+	cfgcmd.value = (void *)&reqbuf;
+	cfgcmd.length = sizeof(struct msm_stats_reqbuf);
+	rc = msm_isp_subdev_ioctl(sd, &cfgcmd, (void *)mctl->client);
+	break;
+	}
 	default:
 		rc = -1;
 	break;
@@ -734,6 +760,7 @@
 	case MSM_CAM_IOCTL_STATS_REQBUF:
 	case MSM_CAM_IOCTL_STATS_ENQUEUEBUF:
 	case MSM_CAM_IOCTL_STATS_FLUSH_BUFQ:
+	case MSM_CAM_IOCTL_STATS_UNREG_BUF:
 		rc = msm_vfe_stats_buf_ioctl(sd, cmd, pmctl, argp);
 		break;
 
diff --git a/drivers/media/video/msm/msm_mem.c b/drivers/media/video/msm/msm_mem.c
index 5d412db..e2e9d1b 100644
--- a/drivers/media/video/msm/msm_mem.c
+++ b/drivers/media/video/msm/msm_mem.c
@@ -208,6 +208,9 @@
 	case MSM_PMEM_IHIST:
 	case MSM_PMEM_SKIN:
 	case MSM_PMEM_AEC_AWB:
+	case MSM_PMEM_BAYER_GRID:
+	case MSM_PMEM_BAYER_FOCUS:
+	case MSM_PMEM_BAYER_HIST:
 		rc = msm_pmem_table_add(ptype, pinfo, client);
 		break;
 
@@ -235,6 +238,9 @@
 	case MSM_PMEM_IHIST:
 	case MSM_PMEM_SKIN:
 	case MSM_PMEM_AEC_AWB:
+	case MSM_PMEM_BAYER_GRID:
+	case MSM_PMEM_BAYER_FOCUS:
+	case MSM_PMEM_BAYER_HIST:
 		hlist_for_each_entry_safe(region, node, n,
 				ptype, list) {
 
diff --git a/drivers/media/video/msm/msm_vfe31_v4l2.c b/drivers/media/video/msm/msm_vfe31_v4l2.c
index 18168ee..a22a09f 100644
--- a/drivers/media/video/msm/msm_vfe31_v4l2.c
+++ b/drivers/media/video/msm/msm_vfe31_v4l2.c
@@ -417,6 +417,25 @@
 	return 0L;
 }
 
+static unsigned long vfe31_stats_unregbuf(
+	struct msm_stats_reqbuf *req_buf)
+{
+	int i = 0, rc = 0;
+
+	for (i = 0; i < req_buf->num_buf; i++) {
+		rc = vfe31_ctrl->stats_ops.buf_unprepare(
+			vfe31_ctrl->stats_ops.stats_ctrl,
+			req_buf->stats_type, i,
+			vfe31_ctrl->stats_ops.client);
+		if (rc < 0) {
+			pr_err("%s: unreg stats buf (type = %d) err = %d",
+				__func__, req_buf->stats_type, rc);
+		return rc;
+		}
+	}
+	return 0L;
+}
+
 static int vfe_stats_awb_buf_init(
 	struct vfe_cmd_stats_buf *in)
 {
@@ -3334,6 +3353,22 @@
 				vfe31_ctrl->stats_ops.client);
 	}
 	break;
+	case VFE_CMD_STATS_UNREGBUF:
+	{
+		struct msm_stats_reqbuf *req_buf = NULL;
+		req_buf = (struct msm_stats_reqbuf *)cmd->value;
+		if (sizeof(struct msm_stats_reqbuf) != cmd->length) {
+			/* error. the length not match */
+			pr_err("%s: stats reqbuf input size = %d,\n"
+				"struct size = %d, mitch match\n",
+				 __func__, cmd->length,
+				sizeof(struct msm_stats_reqbuf));
+			rc = -EINVAL ;
+			goto end;
+		}
+		rc = vfe31_stats_unregbuf(req_buf);
+	}
+	break;
 	default:
 		rc = -1;
 		pr_err("%s: cmd_type %d not supported", __func__,
@@ -3583,6 +3618,7 @@
 	case VFE_CMD_STATS_REQBUF:
 	case VFE_CMD_STATS_ENQUEUEBUF:
 	case VFE_CMD_STATS_FLUSH_BUFQ:
+	case VFE_CMD_STATS_UNREGBUF:
 		/* for easy porting put in one envelope */
 		rc = vfe_stats_bufq_sub_ioctl(cmd, vfe_params->data);
 		return rc;
diff --git a/drivers/media/video/msm/msm_vfe32.c b/drivers/media/video/msm/msm_vfe32.c
index aa2b19d..c4bdad2 100644
--- a/drivers/media/video/msm/msm_vfe32.c
+++ b/drivers/media/video/msm/msm_vfe32.c
@@ -216,6 +216,27 @@
 		{VFE_CMD_GET_RGB_G_TABLE},
 		{VFE_CMD_GET_LA_TABLE},
 		{VFE_CMD_DEMOSAICV3_UPDATE},
+		{VFE_CMD_ACTIVE_REGION_CFG},
+/*130*/ {VFE_CMD_COLOR_PROCESSING_CONFIG},
+		{VFE_CMD_STATS_WB_AEC_CONFIG},
+		{VFE_CMD_STATS_WB_AEC_UPDATE},
+		{VFE_CMD_Y_GAMMA_CONFIG},
+		{VFE_CMD_SCALE_OUTPUT1_CONFIG},
+/*135*/ {VFE_CMD_SCALE_OUTPUT2_CONFIG},
+		{VFE_CMD_CAPTURE_RAW},
+		{VFE_CMD_STOP_LIVESHOT},
+		{VFE_CMD_RECONFIG_VFE},
+		{VFE_CMD_STATS_REQBUF},
+/*140*/	{VFE_CMD_STATS_ENQUEUEBUF},
+		{VFE_CMD_STATS_FLUSH_BUFQ},
+		{VFE_CMD_STATS_UNREGBUF},
+		{VFE_CMD_STATS_BG_START, V32_STATS_BG_LEN, V32_STATS_BG_OFF},
+		{VFE_CMD_STATS_BG_STOP},
+		{VFE_CMD_STATS_BF_START, V32_STATS_BF_LEN, V32_STATS_BF_OFF},
+/*145*/ {VFE_CMD_STATS_BF_STOP},
+		{VFE_CMD_STATS_BHIST_START, V32_STATS_BHIST_LEN,
+			V32_STATS_BHIST_OFF},
+/*147*/	{VFE_CMD_STATS_BHIST_STOP},
 };
 
 uint32_t vfe32_AXI_WM_CFG[] = {
@@ -358,8 +379,24 @@
 	"GET_RGB_G_TABLE",
 	"GET_LA_TABLE",
 	"DEMOSAICV3_UPDATE",
+	"STATS_BG_START",
+	"STATS_BG_STOP",
+	"STATS_BF_START",
+	"STATS_BF_STOP",
+	"STATS_BHIST_START",
+	"STATS_BHIST_STOP",
 };
 
+uint8_t vfe32_use_bayer_stats(struct vfe32_ctrl_type *vfe32_ctrl)
+{
+	if (vfe32_ctrl->ver_num.main >= 4) {
+		/* VFE 4 or above uses bayer stats */
+		return TRUE;
+	} else {
+		return FALSE;
+	}
+}
+
 static void vfe32_stop(struct vfe32_ctrl_type *vfe32_ctrl)
 {
 	unsigned long flags;
@@ -375,7 +412,7 @@
 	msm_camera_io_w(VFE_DISABLE_ALL_IRQS,
 		vfe32_ctrl->share_ctrl->vfebase + VFE_IRQ_MASK_0);
 	msm_camera_io_w(VFE_DISABLE_ALL_IRQS,
-			vfe32_ctrl->share_ctrl->vfebase + VFE_IRQ_MASK_1);
+		vfe32_ctrl->share_ctrl->vfebase + VFE_IRQ_MASK_1);
 
 	/* clear all pending interrupts*/
 	msm_camera_io_w(VFE_CLEAR_ALL_IRQS,
@@ -530,13 +567,16 @@
 	/* this is unsigned 32 bit integer. */
 	vfe32_ctrl->share_ctrl->vfeFrameId = 0;
 	/* Stats control variables. */
-	memset(&(vfe32_ctrl->afStatsControl), 0,
+	memset(&(vfe32_ctrl->afbfStatsControl), 0,
 		sizeof(struct vfe_stats_control));
 
 	memset(&(vfe32_ctrl->awbStatsControl), 0,
 		sizeof(struct vfe_stats_control));
 
-	memset(&(vfe32_ctrl->aecStatsControl), 0,
+	memset(&(vfe32_ctrl->aecbgStatsControl), 0,
+		sizeof(struct vfe_stats_control));
+
+	memset(&(vfe32_ctrl->bhistStatsControl), 0,
 		sizeof(struct vfe_stats_control));
 
 	memset(&(vfe32_ctrl->ihistStatsControl), 0,
@@ -553,6 +593,62 @@
 	vfe32_ctrl->snapshot_frame_cnt = 0;
 }
 
+static void vfe32_program_dmi_cfg(
+	enum VFE32_DMI_RAM_SEL bankSel,
+	struct vfe32_ctrl_type *vfe32_ctrl)
+{
+	/* set bit 8 for auto increment. */
+	uint32_t value = VFE_DMI_CFG_DEFAULT;
+	value += (uint32_t)bankSel;
+	CDBG("%s: banksel = %d\n", __func__, bankSel);
+
+	msm_camera_io_w(value, vfe32_ctrl->share_ctrl->vfebase +
+		VFE_DMI_CFG);
+	/* by default, always starts with offset 0.*/
+	msm_camera_io_w(0, vfe32_ctrl->share_ctrl->vfebase +
+		VFE_DMI_ADDR);
+}
+
+static void vfe32_reset_dmi_tables(
+	struct vfe32_ctrl_type *vfe32_ctrl)
+{
+	int i = 0;
+
+	/* Reset Histogram LUTs */
+	CDBG("Reset Bayer histogram LUT : 0\n");
+	vfe32_program_dmi_cfg(STATS_BHIST_RAM0, vfe32_ctrl);
+	/* Loop for configuring LUT */
+	for (i = 0; i < 256; i++) {
+		msm_camera_io_w(0, vfe32_ctrl->share_ctrl->vfebase +
+			VFE_DMI_DATA_HI);
+		msm_camera_io_w(0, vfe32_ctrl->share_ctrl->vfebase +
+			VFE_DMI_DATA_LO);
+	}
+	vfe32_program_dmi_cfg(NO_MEM_SELECTED, vfe32_ctrl);
+
+	CDBG("Reset Bayer Histogram LUT: 1\n");
+	vfe32_program_dmi_cfg(STATS_BHIST_RAM1, vfe32_ctrl);
+	/* Loop for configuring LUT */
+	for (i = 0; i < 256; i++) {
+		msm_camera_io_w(0, vfe32_ctrl->share_ctrl->vfebase +
+			VFE_DMI_DATA_HI);
+		msm_camera_io_w(0, vfe32_ctrl->share_ctrl->vfebase +
+			VFE_DMI_DATA_LO);
+	}
+	vfe32_program_dmi_cfg(NO_MEM_SELECTED, vfe32_ctrl);
+
+	CDBG("Reset IHistogram LUT\n");
+	vfe32_program_dmi_cfg(STATS_IHIST_RAM, vfe32_ctrl);
+	/* Loop for configuring LUT */
+	for (i = 0; i < 256; i++) {
+		msm_camera_io_w(0, vfe32_ctrl->share_ctrl->vfebase +
+			VFE_DMI_DATA_HI);
+		msm_camera_io_w(0, vfe32_ctrl->share_ctrl->vfebase +
+			VFE_DMI_DATA_LO);
+	}
+	vfe32_program_dmi_cfg(NO_MEM_SELECTED, vfe32_ctrl);
+}
+
 static void vfe32_reset(struct vfe32_ctrl_type *vfe32_ctrl)
 {
 	vfe32_reset_internal_variables(vfe32_ctrl);
@@ -572,7 +668,8 @@
 
 	/* Ensure the write order while writing
 	to the command register using the barrier */
-	msm_camera_io_w_mb(1, vfe32_ctrl->share_ctrl->vfebase + VFE_IRQ_CMD);
+	msm_camera_io_w_mb(1, vfe32_ctrl->share_ctrl->vfebase +
+		VFE_IRQ_CMD);
 
 	/* enable reset_ack interrupt.  */
 	msm_camera_io_w(VFE_IMASK_WHILE_STOPPING_1,
@@ -677,6 +774,26 @@
 	return 0L;
 }
 
+
+static unsigned long vfe32_stats_unregbuf(
+	struct vfe32_ctrl_type *vfe32_ctrl,
+	struct msm_stats_reqbuf *req_buf)
+{
+	int i = 0, rc = 0;
+
+	for (i = 0; i < req_buf->num_buf; i++) {
+		rc = vfe32_ctrl->stats_ops.buf_unprepare(
+			vfe32_ctrl->stats_ops.stats_ctrl,
+			req_buf->stats_type, i,
+			vfe32_ctrl->stats_ops.client);
+		if (rc < 0) {
+			pr_err("%s: unreg stats buf (type = %d) err = %d",
+				__func__, req_buf->stats_type, rc);
+		return rc;
+		}
+	}
+	return 0L;
+}
 static int vfe_stats_awb_buf_init(
 	struct vfe32_ctrl_type *vfe32_ctrl,
 	struct vfe_cmd_stats_buf *in)
@@ -708,14 +825,18 @@
 	return 0;
 }
 
-static int vfe_stats_aec_buf_init(
-	struct vfe32_ctrl_type *vfe32_ctrl, struct vfe_cmd_stats_buf *in)
+static uint32_t vfe_stats_aec_bg_buf_init(
+	struct vfe32_ctrl_type *vfe32_ctrl)
 {
 	uint32_t addr;
 	unsigned long flags;
+	uint32_t stats_type;
 
+	stats_type =
+		(!vfe32_use_bayer_stats(vfe32_ctrl)) ? MSM_STATS_TYPE_AEC
+			: MSM_STATS_TYPE_BG;
 	spin_lock_irqsave(&vfe32_ctrl->stats_bufq_lock, flags);
-	addr = (uint32_t)vfe32_stats_dqbuf(vfe32_ctrl, MSM_STATS_TYPE_AEC);
+	addr = (uint32_t)vfe32_stats_dqbuf(vfe32_ctrl, stats_type);
 	spin_unlock_irqrestore(&vfe32_ctrl->stats_bufq_lock, flags);
 	if (!addr) {
 		pr_err("%s: dq aec ping buf from free buf queue",
@@ -724,9 +845,9 @@
 	}
 	msm_camera_io_w(addr,
 		vfe32_ctrl->share_ctrl->vfebase +
-		VFE_BUS_STATS_AEC_WR_PING_ADDR);
+		VFE_BUS_STATS_AEC_BG_WR_PING_ADDR);
 	spin_lock_irqsave(&vfe32_ctrl->stats_bufq_lock, flags);
-	addr = (uint32_t)vfe32_stats_dqbuf(vfe32_ctrl, MSM_STATS_TYPE_AEC);
+	addr = (uint32_t)vfe32_stats_dqbuf(vfe32_ctrl, stats_type);
 	spin_unlock_irqrestore(&vfe32_ctrl->stats_bufq_lock, flags);
 	if (!addr) {
 		pr_err("%s: dq aec pong buf from free buf queue",
@@ -735,26 +856,31 @@
 	}
 	msm_camera_io_w(addr,
 		vfe32_ctrl->share_ctrl->vfebase +
-		VFE_BUS_STATS_AEC_WR_PONG_ADDR);
+		VFE_BUS_STATS_AEC_BG_WR_PONG_ADDR);
 	return 0;
 }
 
-static int vfe_stats_af_buf_init(
-	struct vfe32_ctrl_type *vfe32_ctrl, struct vfe_cmd_stats_buf *in)
+static int vfe_stats_af_bf_buf_init(
+	struct vfe32_ctrl_type *vfe32_ctrl)
 {
 	uint32_t addr;
 	unsigned long flags;
 	int rc = 0;
 
+	uint32_t stats_type;
+	stats_type =
+		(!vfe32_use_bayer_stats(vfe32_ctrl)) ? MSM_STATS_TYPE_AF
+			: MSM_STATS_TYPE_BF;
+
 	spin_lock_irqsave(&vfe32_ctrl->stats_bufq_lock, flags);
-	rc = vfe32_stats_flush_enqueue(vfe32_ctrl, MSM_STATS_TYPE_AF);
+	rc = vfe32_stats_flush_enqueue(vfe32_ctrl, stats_type);
 	if (rc < 0) {
 		pr_err("%s: dq stats buf err = %d",
 			   __func__, rc);
 		spin_unlock_irqrestore(&vfe32_ctrl->stats_bufq_lock, flags);
 		return -EINVAL;
 	}
-	addr = (uint32_t)vfe32_stats_dqbuf(vfe32_ctrl, MSM_STATS_TYPE_AF);
+	addr = (uint32_t)vfe32_stats_dqbuf(vfe32_ctrl, stats_type);
 	spin_unlock_irqrestore(&vfe32_ctrl->stats_bufq_lock, flags);
 	if (!addr) {
 		pr_err("%s: dq af ping buf from free buf queue", __func__);
@@ -762,9 +888,9 @@
 	}
 	msm_camera_io_w(addr,
 		vfe32_ctrl->share_ctrl->vfebase +
-		VFE_BUS_STATS_AF_WR_PING_ADDR);
+		VFE_BUS_STATS_AF_BF_WR_PING_ADDR);
 	spin_lock_irqsave(&vfe32_ctrl->stats_bufq_lock, flags);
-	addr = (uint32_t)vfe32_stats_dqbuf(vfe32_ctrl, MSM_STATS_TYPE_AF);
+	addr = (uint32_t)vfe32_stats_dqbuf(vfe32_ctrl, stats_type);
 	spin_unlock_irqrestore(&vfe32_ctrl->stats_bufq_lock, flags);
 	if (!addr) {
 		pr_err("%s: dq af pong buf from free buf queue", __func__);
@@ -772,14 +898,44 @@
 	}
 	msm_camera_io_w(addr,
 		vfe32_ctrl->share_ctrl->vfebase +
-		VFE_BUS_STATS_AF_WR_PONG_ADDR);
+		VFE_BUS_STATS_AF_BF_WR_PONG_ADDR);
+	return 0;
+}
+
+static uint32_t vfe_stats_bhist_buf_init(
+	struct vfe32_ctrl_type *vfe32_ctrl)
+{
+	uint32_t addr;
+	unsigned long flags;
+
+	spin_lock_irqsave(&vfe32_ctrl->stats_bufq_lock, flags);
+	addr = (uint32_t)vfe32_stats_dqbuf(vfe32_ctrl, MSM_STATS_TYPE_BHIST);
+	spin_unlock_irqrestore(&vfe32_ctrl->stats_bufq_lock, flags);
+	if (!addr) {
+		pr_err("%s: dq ihist ping buf from free buf queue",
+			__func__);
+		return -ENOMEM;
+	}
+	msm_camera_io_w(addr,
+		vfe32_ctrl->share_ctrl->vfebase +
+		VFE_BUS_STATS_SKIN_BHIST_WR_PING_ADDR);
+	spin_lock_irqsave(&vfe32_ctrl->stats_bufq_lock, flags);
+	addr = (uint32_t)vfe32_stats_dqbuf(vfe32_ctrl, MSM_STATS_TYPE_BHIST);
+	spin_unlock_irqrestore(&vfe32_ctrl->stats_bufq_lock, flags);
+	if (!addr) {
+		pr_err("%s: dq ihist pong buf from free buf queue",
+			__func__);
+		return -ENOMEM;
+	}
+	msm_camera_io_w(addr,
+		vfe32_ctrl->share_ctrl->vfebase +
+		VFE_BUS_STATS_SKIN_BHIST_WR_PONG_ADDR);
 
 	return 0;
 }
 
 static int vfe_stats_ihist_buf_init(
-	struct vfe32_ctrl_type *vfe32_ctrl,
-	struct vfe_cmd_stats_buf *in)
+	struct vfe32_ctrl_type *vfe32_ctrl)
 {
 	uint32_t addr;
 	unsigned long flags;
@@ -811,8 +967,7 @@
 }
 
 static int vfe_stats_rs_buf_init(
-	struct vfe32_ctrl_type *vfe32_ctrl,
-	struct vfe_cmd_stats_buf *in)
+	struct vfe32_ctrl_type *vfe32_ctrl)
 {
 	uint32_t addr;
 	unsigned long flags;
@@ -841,8 +996,7 @@
 }
 
 static int vfe_stats_cs_buf_init(
-	struct vfe32_ctrl_type *vfe32_ctrl,
-	struct vfe_cmd_stats_buf *in)
+	struct vfe32_ctrl_type *vfe32_ctrl)
 {
 	uint32_t addr;
 	unsigned long flags;
@@ -918,6 +1072,9 @@
 		msm_camera_io_w_mb(1, vfe32_ctrl->share_ctrl->vfebase +
 			VFE_CAMIF_COMMAND);
 	}
+	msm_camera_io_dump(vfe32_ctrl->share_ctrl->vfebase,
+		vfe32_ctrl->share_ctrl->register_total * 4);
+
 	/* Ensure the write order while writing
 	to the command register using the barrier */
 	atomic_set(&vfe32_ctrl->share_ctrl->vstate, 1);
@@ -1372,19 +1529,7 @@
 		vfe32_ctrl->share_ctrl->vfebase + V32_TIMER_SELECT_OFF);
 }
 
-static void vfe32_program_dmi_cfg(
-	enum VFE32_DMI_RAM_SEL bankSel,
-	struct vfe32_ctrl_type *vfe32_ctrl)
-{
-	/* set bit 8 for auto increment. */
-	uint32_t value = VFE_DMI_CFG_DEFAULT;
-	value += (uint32_t)bankSel;
-	CDBG("%s: banksel = %d\n", __func__, bankSel);
 
-	msm_camera_io_w(value, vfe32_ctrl->share_ctrl->vfebase + VFE_DMI_CFG);
-	/* by default, always starts with offset 0.*/
-	msm_camera_io_w(0, vfe32_ctrl->share_ctrl->vfebase + VFE_DMI_ADDR);
-}
 static void vfe32_write_gamma_cfg(
 	enum VFE32_DMI_RAM_SEL channel_sel,
 	const uint32_t *tbl,
@@ -1582,7 +1727,7 @@
 	struct vfe32_ctrl_type *vfe32_ctrl)
 {
 	int i , rc = 0;
-	uint32_t old_val = 0 , new_val = 0;
+	uint32_t old_val = 0 , new_val = 0, module_val = 0;
 	uint32_t *cmdp = NULL;
 	uint32_t *cmdp_local = NULL;
 	uint32_t snapshot_cnt = 0;
@@ -1762,7 +1907,12 @@
 		break;
 
 	case VFE_CMD_STATS_AE_START: {
-		rc = vfe_stats_aec_buf_init(vfe32_ctrl, NULL);
+		if (vfe32_use_bayer_stats(vfe32_ctrl)) {
+			/* Error */
+			rc = -EFAULT;
+			goto proc_general_done;
+		}
+		rc = vfe_stats_aec_bg_buf_init(vfe32_ctrl);
 		if (rc < 0) {
 			pr_err("%s: cannot config ping/pong address of AEC",
 				 __func__);
@@ -1791,7 +1941,12 @@
 		}
 		break;
 	case VFE_CMD_STATS_AF_START: {
-		rc = vfe_stats_af_buf_init(vfe32_ctrl, NULL);
+		if (vfe32_use_bayer_stats(vfe32_ctrl)) {
+			/* Error */
+			rc = -EFAULT;
+			goto proc_general_done;
+		}
+		rc = vfe_stats_af_bf_buf_init(vfe32_ctrl);
 		if (rc < 0) {
 			pr_err("%s: cannot config ping/pong address of AF",
 				__func__);
@@ -1820,6 +1975,11 @@
 		}
 		break;
 	case VFE_CMD_STATS_AWB_START: {
+		if (vfe32_use_bayer_stats(vfe32_ctrl)) {
+			/* Error */
+			rc = -EFAULT;
+			goto proc_general_done;
+		}
 		rc = vfe_stats_awb_buf_init(vfe32_ctrl, NULL);
 		if (rc < 0) {
 			pr_err("%s: cannot config ping/pong address of AWB",
@@ -1850,7 +2010,7 @@
 		break;
 
 	case VFE_CMD_STATS_IHIST_START: {
-		rc = vfe_stats_ihist_buf_init(vfe32_ctrl, NULL);
+		rc = vfe_stats_ihist_buf_init(vfe32_ctrl);
 		if (rc < 0) {
 			pr_err("%s: cannot config ping/pong address of IHIST",
 				 __func__);
@@ -1881,7 +2041,7 @@
 
 
 	case VFE_CMD_STATS_RS_START: {
-		rc = vfe_stats_rs_buf_init(vfe32_ctrl, NULL);
+		rc = vfe_stats_rs_buf_init(vfe32_ctrl);
 		if (rc < 0) {
 			pr_err("%s: cannot config ping/pong address of RS",
 				__func__);
@@ -1906,7 +2066,7 @@
 		break;
 
 	case VFE_CMD_STATS_CS_START: {
-		rc = vfe_stats_cs_buf_init(vfe32_ctrl, NULL);
+		rc = vfe_stats_cs_buf_init(vfe32_ctrl);
 		if (rc < 0) {
 			pr_err("%s: cannot config ping/pong address of CS",
 				__func__);
@@ -1930,6 +2090,67 @@
 		}
 		break;
 
+	case VFE_CMD_STATS_BG_START:
+	case VFE_CMD_STATS_BF_START:
+	case VFE_CMD_STATS_BHIST_START: {
+		if (!vfe32_use_bayer_stats(vfe32_ctrl)) {
+			/* Error */
+			rc = -EFAULT;
+			goto proc_general_done;
+		}
+		old_val = msm_camera_io_r(
+			vfe32_ctrl->share_ctrl->vfebase + VFE_STATS_CFG);
+		module_val = msm_camera_io_r(
+			vfe32_ctrl->share_ctrl->vfebase + VFE_MODULE_CFG);
+		if (VFE_CMD_STATS_BG_START == cmd->id) {
+			module_val |= AE_BG_ENABLE_MASK;
+			old_val |= STATS_BG_ENABLE_MASK;
+			rc = vfe_stats_aec_bg_buf_init(vfe32_ctrl);
+			if (rc < 0) {
+				pr_err("%s: cannot config ping/pong address of CS",
+					__func__);
+				goto proc_general_done;
+			}
+		} else if (VFE_CMD_STATS_BF_START == cmd->id) {
+			module_val |= AF_BF_ENABLE_MASK;
+			old_val |= STATS_BF_ENABLE_MASK;
+			rc = vfe_stats_af_bf_buf_init(vfe32_ctrl);
+			if (rc < 0) {
+				pr_err("%s: cannot config ping/pong address of CS",
+					__func__);
+				goto proc_general_done;
+			}
+		} else {
+			module_val |= SKIN_BHIST_ENABLE_MASK;
+			old_val |= STATS_BHIST_ENABLE_MASK;
+			rc = vfe_stats_bhist_buf_init(vfe32_ctrl);
+			if (rc < 0) {
+				pr_err("%s: cannot config ping/pong address of CS",
+					__func__);
+				goto proc_general_done;
+			}
+		}
+		msm_camera_io_w(old_val, vfe32_ctrl->share_ctrl->vfebase +
+			VFE_STATS_CFG);
+		msm_camera_io_w(module_val,
+			vfe32_ctrl->share_ctrl->vfebase + VFE_MODULE_CFG);
+		cmdp = kmalloc(cmd->length, GFP_ATOMIC);
+		if (!cmdp) {
+			rc = -ENOMEM;
+			goto proc_general_done;
+		}
+		if (copy_from_user(cmdp,
+				(void __user *)(cmd->value),
+				cmd->length)) {
+			rc = -EFAULT;
+			goto proc_general_done;
+		}
+		msm_camera_io_memcpy(
+			vfe32_ctrl->share_ctrl->vfebase +
+			vfe32_cmd[cmd->id].offset,
+			cmdp, (vfe32_cmd[cmd->id].length));
+		}
+		break;
 	case VFE_CMD_MCE_UPDATE:
 	case VFE_CMD_MCE_CFG:{
 		cmdp = kmalloc(cmd->length, GFP_ATOMIC);
@@ -2601,6 +2822,11 @@
 		break;
 
 	case VFE_CMD_STATS_AWB_STOP: {
+		if (vfe32_use_bayer_stats(vfe32_ctrl)) {
+			/* Error */
+			rc = -EFAULT;
+			goto proc_general_done;
+		}
 		old_val = msm_camera_io_r(
 			vfe32_ctrl->share_ctrl->vfebase + VFE_MODULE_CFG);
 		old_val &= ~AWB_ENABLE_MASK;
@@ -2609,6 +2835,11 @@
 		}
 		break;
 	case VFE_CMD_STATS_AE_STOP: {
+		if (vfe32_use_bayer_stats(vfe32_ctrl)) {
+			/* Error */
+			rc = -EFAULT;
+			goto proc_general_done;
+		}
 		old_val = msm_camera_io_r(
 			vfe32_ctrl->share_ctrl->vfebase + VFE_MODULE_CFG);
 		old_val &= ~AE_BG_ENABLE_MASK;
@@ -2617,17 +2848,16 @@
 		}
 		break;
 	case VFE_CMD_STATS_AF_STOP: {
+		if (vfe32_use_bayer_stats(vfe32_ctrl)) {
+			/* Error */
+			rc = -EFAULT;
+			goto proc_general_done;
+		}
 		old_val = msm_camera_io_r(
 			vfe32_ctrl->share_ctrl->vfebase + VFE_MODULE_CFG);
 		old_val &= ~AF_BF_ENABLE_MASK;
 		msm_camera_io_w(old_val,
 			vfe32_ctrl->share_ctrl->vfebase + VFE_MODULE_CFG);
-		rc = vfe32_stats_flush_enqueue(vfe32_ctrl, MSM_STATS_TYPE_AF);
-		if (rc < 0) {
-			pr_err("%s: dq stats buf err = %d",
-				   __func__, rc);
-			return -EINVAL;
-		}
 		}
 		break;
 
@@ -2657,6 +2887,37 @@
 			vfe32_ctrl->share_ctrl->vfebase + VFE_MODULE_CFG);
 		}
 		break;
+
+	case VFE_CMD_STATS_BG_STOP:
+	case VFE_CMD_STATS_BF_STOP:
+	case VFE_CMD_STATS_BHIST_STOP: {
+		if (!vfe32_use_bayer_stats(vfe32_ctrl)) {
+			/* Error */
+			rc = -EFAULT;
+			goto proc_general_done;
+		}
+		old_val = msm_camera_io_r(
+			vfe32_ctrl->share_ctrl->vfebase + VFE_STATS_CFG);
+		if (VFE_CMD_STATS_BG_STOP == cmd->id)
+			old_val &= ~STATS_BG_ENABLE_MASK;
+		else if (VFE_CMD_STATS_BF_STOP == cmd->id)
+			old_val &= ~STATS_BF_ENABLE_MASK;
+		else
+			old_val &= ~STATS_BHIST_ENABLE_MASK;
+		msm_camera_io_w(old_val,
+			vfe32_ctrl->share_ctrl->vfebase + VFE_STATS_CFG);
+		if (VFE_CMD_STATS_BF_STOP == cmd->id) {
+			rc = vfe32_stats_flush_enqueue(vfe32_ctrl,
+					MSM_STATS_TYPE_BF);
+			if (rc < 0) {
+				pr_err("%s: dq stats buf err = %d",
+					   __func__, rc);
+				return -EINVAL;
+			}
+		}
+		}
+		break;
+
 	case VFE_CMD_STOP:
 		pr_info("vfe32_proc_general: cmdID = %s\n",
 			vfe32_general_cmd[cmd->id]);
@@ -3301,18 +3562,48 @@
 		vfe32_ctrl->share_ctrl->vfebase + VFE_CLAMP_MAX);
 
 	/* stats UB config */
-	msm_camera_io_w(0x3980007,
-		vfe32_ctrl->share_ctrl->vfebase + VFE_BUS_STATS_AEC_UB_CFG);
-	msm_camera_io_w(0x3A00007,
-		vfe32_ctrl->share_ctrl->vfebase + VFE_BUS_STATS_AF_UB_CFG);
-	msm_camera_io_w(0x3A8000F,
-		vfe32_ctrl->share_ctrl->vfebase + VFE_BUS_STATS_AWB_UB_CFG);
-	msm_camera_io_w(0x3B80007,
-		vfe32_ctrl->share_ctrl->vfebase + VFE_BUS_STATS_RS_UB_CFG);
-	msm_camera_io_w(0x3C0001F,
-		vfe32_ctrl->share_ctrl->vfebase + VFE_BUS_STATS_CS_UB_CFG);
-	msm_camera_io_w(0x3E0001F,
-		vfe32_ctrl->share_ctrl->vfebase + VFE_BUS_STATS_HIST_UB_CFG);
+	CDBG("%s: Use bayer stats = %d\n", __func__,
+		 vfe32_use_bayer_stats(vfe32_ctrl));
+	if (!vfe32_use_bayer_stats(vfe32_ctrl)) {
+		msm_camera_io_w(0x3980007,
+			vfe32_ctrl->share_ctrl->vfebase +
+				VFE_BUS_STATS_AEC_BG_UB_CFG);
+		msm_camera_io_w(0x3A00007,
+			vfe32_ctrl->share_ctrl->vfebase +
+				VFE_BUS_STATS_AF_BF_UB_CFG);
+		msm_camera_io_w(0x3A8000F,
+			vfe32_ctrl->share_ctrl->vfebase +
+				VFE_BUS_STATS_AWB_UB_CFG);
+		msm_camera_io_w(0x3B80007,
+			vfe32_ctrl->share_ctrl->vfebase +
+				VFE_BUS_STATS_RS_UB_CFG);
+		msm_camera_io_w(0x3C0001F,
+			vfe32_ctrl->share_ctrl->vfebase +
+				VFE_BUS_STATS_CS_UB_CFG);
+		msm_camera_io_w(0x3E0001F,
+			vfe32_ctrl->share_ctrl->vfebase +
+				VFE_BUS_STATS_HIST_UB_CFG);
+	} else {
+		msm_camera_io_w(0x350001F,
+			vfe32_ctrl->share_ctrl->vfebase +
+				VFE_BUS_STATS_HIST_UB_CFG);
+		msm_camera_io_w(0x370002F,
+			vfe32_ctrl->share_ctrl->vfebase +
+				VFE_BUS_STATS_AEC_BG_UB_CFG);
+		msm_camera_io_w(0x3A0002F,
+			vfe32_ctrl->share_ctrl->vfebase +
+				VFE_BUS_STATS_AF_BF_UB_CFG);
+		msm_camera_io_w(0x3D00007,
+			vfe32_ctrl->share_ctrl->vfebase +
+				VFE_BUS_STATS_RS_UB_CFG);
+		msm_camera_io_w(0x3D8001F,
+			vfe32_ctrl->share_ctrl->vfebase +
+				VFE_BUS_STATS_CS_UB_CFG);
+		msm_camera_io_w(0x3F80007,
+			vfe32_ctrl->share_ctrl->vfebase +
+				VFE_BUS_STATS_SKIN_BHIST_UB_CFG);
+	}
+	vfe32_reset_dmi_tables(vfe32_ctrl);
 }
 
 static void vfe32_process_reset_irq(
@@ -3785,25 +4076,36 @@
 	/* @todo This is causing issues, need further investigate */
 	/* spin_lock_irqsave(&ctrl->state_lock, flags); */
 	struct isp_msg_stats msgStats;
+	uint32_t stats_type;
 	msgStats.frameCounter = vfe32_ctrl->share_ctrl->vfeFrameId;
 	if (vfe32_ctrl->simultaneous_sof_stat)
 		msgStats.frameCounter--;
 	msgStats.buffer = bufAddress;
 	switch (statsNum) {
 	case statsAeNum:{
-		msgStats.id = MSG_ID_STATS_AEC;
+		msgStats.id =
+			(!vfe32_use_bayer_stats(vfe32_ctrl)) ? MSG_ID_STATS_AEC
+				: MSG_ID_STATS_BG;
+		stats_type =
+			(!vfe32_use_bayer_stats(vfe32_ctrl)) ?
+				MSM_STATS_TYPE_AEC : MSM_STATS_TYPE_BG;
 		rc = vfe32_ctrl->stats_ops.dispatch(
 				vfe32_ctrl->stats_ops.stats_ctrl,
-				MSM_STATS_TYPE_AEC, bufAddress,
+				stats_type, bufAddress,
 				&msgStats.buf_idx, &vaddr, &msgStats.fd,
 				vfe32_ctrl->stats_ops.client);
 		}
 		break;
 	case statsAfNum:{
-		msgStats.id = MSG_ID_STATS_AF;
+		msgStats.id =
+			(!vfe32_use_bayer_stats(vfe32_ctrl)) ? MSG_ID_STATS_AF
+				: MSG_ID_STATS_BF;
+		stats_type =
+			(!vfe32_use_bayer_stats(vfe32_ctrl)) ? MSM_STATS_TYPE_AF
+				: MSM_STATS_TYPE_BF;
 		rc = vfe32_ctrl->stats_ops.dispatch(
 				vfe32_ctrl->stats_ops.stats_ctrl,
-				MSM_STATS_TYPE_AF, bufAddress,
+				stats_type, bufAddress,
 				&msgStats.buf_idx, &vaddr, &msgStats.fd,
 				vfe32_ctrl->stats_ops.client);
 		}
@@ -3845,6 +4147,15 @@
 				vfe32_ctrl->stats_ops.client);
 		}
 		break;
+	case statsSkinNum: {
+		msgStats.id = MSG_ID_STATS_BHIST;
+		rc = vfe32_ctrl->stats_ops.dispatch(
+				vfe32_ctrl->stats_ops.stats_ctrl,
+				MSM_STATS_TYPE_BHIST, bufAddress,
+				&msgStats.buf_idx, &vaddr, &msgStats.fd,
+				vfe32_ctrl->stats_ops.client);
+		}
+		break;
 
 	default:
 		goto stats_done;
@@ -3875,9 +4186,9 @@
 
 	msgStats.status_bits = status_bits;
 
-	msgStats.aec.buff = vfe32_ctrl->aecStatsControl.bufToRender;
+	msgStats.aec.buff = vfe32_ctrl->aecbgStatsControl.bufToRender;
 	msgStats.awb.buff = vfe32_ctrl->awbStatsControl.bufToRender;
-	msgStats.af.buff = vfe32_ctrl->afStatsControl.bufToRender;
+	msgStats.af.buff = vfe32_ctrl->afbfStatsControl.bufToRender;
 
 	msgStats.ihist.buff = vfe32_ctrl->ihistStatsControl.bufToRender;
 	msgStats.rs.buff = vfe32_ctrl->rsStatsControl.bufToRender;
@@ -3892,24 +4203,28 @@
 				&msgStats);
 }
 
-static void vfe32_process_stats_ae_irq(struct vfe32_ctrl_type *vfe32_ctrl)
+static void vfe32_process_stats_ae_bg_irq(struct vfe32_ctrl_type *vfe32_ctrl)
 {
 	unsigned long flags;
 	uint32_t addr;
+	uint32_t stats_type;
+	stats_type =
+		(!vfe32_use_bayer_stats(vfe32_ctrl)) ? MSM_STATS_TYPE_AEC
+			: MSM_STATS_TYPE_BG;
 	spin_lock_irqsave(&vfe32_ctrl->stats_bufq_lock, flags);
-	addr = (uint32_t)vfe32_stats_dqbuf(vfe32_ctrl, MSM_STATS_TYPE_AEC);
+	addr = (uint32_t)vfe32_stats_dqbuf(vfe32_ctrl, stats_type);
 	spin_unlock_irqrestore(&vfe32_ctrl->stats_bufq_lock, flags);
 	if (addr) {
-		vfe32_ctrl->aecStatsControl.bufToRender =
+		vfe32_ctrl->aecbgStatsControl.bufToRender =
 			vfe32_process_stats_irq_common(vfe32_ctrl, statsAeNum,
 			addr);
 
 		vfe_send_stats_msg(vfe32_ctrl,
-			vfe32_ctrl->aecStatsControl.bufToRender, statsAeNum);
+			vfe32_ctrl->aecbgStatsControl.bufToRender, statsAeNum);
 	} else{
-		vfe32_ctrl->aecStatsControl.droppedStatsFrameCount++;
+		vfe32_ctrl->aecbgStatsControl.droppedStatsFrameCount++;
 		CDBG("%s: droppedStatsFrameCount = %d", __func__,
-			vfe32_ctrl->aecStatsControl.droppedStatsFrameCount);
+			vfe32_ctrl->aecbgStatsControl.droppedStatsFrameCount);
 	}
 }
 
@@ -3934,24 +4249,50 @@
 	}
 }
 
-static void vfe32_process_stats_af_irq(struct vfe32_ctrl_type *vfe32_ctrl)
+static void vfe32_process_stats_af_bf_irq(struct vfe32_ctrl_type *vfe32_ctrl)
 {
 	unsigned long flags;
 	uint32_t addr;
+	uint32_t stats_type;
+	stats_type =
+		(!vfe32_use_bayer_stats(vfe32_ctrl)) ? MSM_STATS_TYPE_AF
+			: MSM_STATS_TYPE_BF;
 	spin_lock_irqsave(&vfe32_ctrl->stats_bufq_lock, flags);
-	addr = (uint32_t)vfe32_stats_dqbuf(vfe32_ctrl, MSM_STATS_TYPE_AF);
+	addr = (uint32_t)vfe32_stats_dqbuf(vfe32_ctrl, stats_type);
 	spin_unlock_irqrestore(&vfe32_ctrl->stats_bufq_lock, flags);
 	if (addr) {
-		vfe32_ctrl->afStatsControl.bufToRender =
+		vfe32_ctrl->afbfStatsControl.bufToRender =
 			vfe32_process_stats_irq_common(vfe32_ctrl, statsAfNum,
 			addr);
 
 		vfe_send_stats_msg(vfe32_ctrl,
-			vfe32_ctrl->afStatsControl.bufToRender, statsAfNum);
+			vfe32_ctrl->afbfStatsControl.bufToRender, statsAfNum);
 	} else{
-		vfe32_ctrl->afStatsControl.droppedStatsFrameCount++;
+		vfe32_ctrl->afbfStatsControl.droppedStatsFrameCount++;
 		CDBG("%s: droppedStatsFrameCount = %d", __func__,
-			vfe32_ctrl->afStatsControl.droppedStatsFrameCount);
+			vfe32_ctrl->afbfStatsControl.droppedStatsFrameCount);
+	}
+}
+
+static void vfe32_process_stats_bhist_irq(struct vfe32_ctrl_type *vfe32_ctrl)
+{
+	unsigned long flags;
+	uint32_t addr;
+	spin_lock_irqsave(&vfe32_ctrl->stats_bufq_lock, flags);
+	addr = (uint32_t)vfe32_stats_dqbuf(vfe32_ctrl, MSM_STATS_TYPE_BHIST);
+	spin_unlock_irqrestore(&vfe32_ctrl->stats_bufq_lock, flags);
+	if (addr) {
+		vfe32_ctrl->bhistStatsControl.bufToRender =
+			vfe32_process_stats_irq_common(vfe32_ctrl,
+				statsSkinNum, addr);
+
+		vfe_send_stats_msg(vfe32_ctrl,
+			vfe32_ctrl->bhistStatsControl.bufToRender,
+			statsSkinNum);
+	} else{
+		vfe32_ctrl->bhistStatsControl.droppedStatsFrameCount++;
+		CDBG("%s: droppedStatsFrameCount = %d", __func__,
+			vfe32_ctrl->bhistStatsControl.droppedStatsFrameCount);
 	}
 }
 
@@ -4026,23 +4367,28 @@
 	unsigned long flags;
 	int32_t process_stats = false;
 	uint32_t addr;
+	uint32_t stats_type;
 
 	CDBG("%s, stats = 0x%x\n", __func__, status_bits);
 	spin_lock_irqsave(&vfe32_ctrl->stats_bufq_lock, flags);
-	if (status_bits & VFE_IRQ_STATUS0_STATS_AEC) {
+	stats_type =
+		(!vfe32_use_bayer_stats(vfe32_ctrl)) ? MSM_STATS_TYPE_AEC
+			: MSM_STATS_TYPE_BG;
+
+	if (status_bits & VFE_IRQ_STATUS0_STATS_AEC_BG) {
 		addr = (uint32_t)vfe32_stats_dqbuf(vfe32_ctrl,
-				MSM_STATS_TYPE_AEC);
+				stats_type);
 		if (addr) {
-			vfe32_ctrl->aecStatsControl.bufToRender =
+			vfe32_ctrl->aecbgStatsControl.bufToRender =
 				vfe32_process_stats_irq_common(
 				vfe32_ctrl, statsAeNum,	addr);
 			process_stats = true;
 		} else{
-			vfe32_ctrl->aecStatsControl.bufToRender = 0;
-			vfe32_ctrl->aecStatsControl.droppedStatsFrameCount++;
+			vfe32_ctrl->aecbgStatsControl.bufToRender = 0;
+			vfe32_ctrl->aecbgStatsControl.droppedStatsFrameCount++;
 		}
 	} else {
-		vfe32_ctrl->aecStatsControl.bufToRender = 0;
+		vfe32_ctrl->aecbgStatsControl.bufToRender = 0;
 	}
 
 	if (status_bits & VFE_IRQ_STATUS0_STATS_AWB) {
@@ -4062,21 +4408,24 @@
 		vfe32_ctrl->awbStatsControl.bufToRender = 0;
 	}
 
-	if (status_bits & VFE_IRQ_STATUS0_STATS_AF) {
+	stats_type =
+		(!vfe32_use_bayer_stats(vfe32_ctrl)) ? MSM_STATS_TYPE_AF
+			: MSM_STATS_TYPE_BF;
+	if (status_bits & VFE_IRQ_STATUS0_STATS_AF_BF) {
 		addr = (uint32_t)vfe32_stats_dqbuf(vfe32_ctrl,
-					MSM_STATS_TYPE_AF);
+					stats_type);
 		if (addr) {
-			vfe32_ctrl->afStatsControl.bufToRender =
+			vfe32_ctrl->afbfStatsControl.bufToRender =
 				vfe32_process_stats_irq_common(
 				vfe32_ctrl, statsAfNum,
 				addr);
 			process_stats = true;
 		} else {
-			vfe32_ctrl->afStatsControl.bufToRender = 0;
-			vfe32_ctrl->afStatsControl.droppedStatsFrameCount++;
+			vfe32_ctrl->afbfStatsControl.bufToRender = 0;
+			vfe32_ctrl->afbfStatsControl.droppedStatsFrameCount++;
 		}
 	} else {
-		vfe32_ctrl->afStatsControl.bufToRender = 0;
+		vfe32_ctrl->afbfStatsControl.bufToRender = 0;
 	}
 
 	if (status_bits & VFE_IRQ_STATUS0_STATS_IHIST) {
@@ -4182,17 +4531,21 @@
 		CDBG("irq	resetAckIrq\n");
 		vfe32_process_reset_irq(vfe32_ctrl);
 		break;
-	case VFE_IRQ_STATUS0_STATS_AEC:
+	case VFE_IRQ_STATUS0_STATS_AEC_BG:
 		CDBG("Stats AEC irq occured.\n");
-		vfe32_process_stats_ae_irq(vfe32_ctrl);
+		vfe32_process_stats_ae_bg_irq(vfe32_ctrl);
 		break;
 	case VFE_IRQ_STATUS0_STATS_AWB:
 		CDBG("Stats AWB irq occured.\n");
 		vfe32_process_stats_awb_irq(vfe32_ctrl);
 		break;
-	case VFE_IRQ_STATUS0_STATS_AF:
+	case VFE_IRQ_STATUS0_STATS_AF_BF:
 		CDBG("Stats AF irq occured.\n");
-		vfe32_process_stats_af_irq(vfe32_ctrl);
+		vfe32_process_stats_af_bf_irq(vfe32_ctrl);
+		break;
+	case VFE_IRQ_STATUS0_STATS_SK_BHIST:
+		CDBG("Stats BHIST irq occured.\n");
+		vfe32_process_stats_bhist_irq(vfe32_ctrl);
 		break;
 	case VFE_IRQ_STATUS0_STATS_IHIST:
 		CDBG("Stats IHIST irq occured.\n");
@@ -4261,11 +4614,11 @@
 		} else {
 			stat_interrupt =
 				(qcmd->vfeInterruptStatus0 &
-					VFE_IRQ_STATUS0_STATS_AEC) |
+					VFE_IRQ_STATUS0_STATS_AEC_BG) |
 				(qcmd->vfeInterruptStatus0 &
 					VFE_IRQ_STATUS0_STATS_AWB) |
 				(qcmd->vfeInterruptStatus0 &
-					VFE_IRQ_STATUS0_STATS_AF) |
+					VFE_IRQ_STATUS0_STATS_AF_BF) |
 				(qcmd->vfeInterruptStatus0 &
 					VFE_IRQ_STATUS0_STATS_IHIST) |
 				(qcmd->vfeInterruptStatus0 &
@@ -4333,10 +4686,10 @@
 			} else {
 				/* process individual stats interrupt. */
 				if (qcmd->vfeInterruptStatus0 &
-						VFE_IRQ_STATUS0_STATS_AEC)
+						VFE_IRQ_STATUS0_STATS_AEC_BG)
 					v4l2_subdev_notify(&axi_ctrl->subdev,
 					NOTIFY_VFE_IRQ,
-					(void *)VFE_IRQ_STATUS0_STATS_AEC);
+					(void *)VFE_IRQ_STATUS0_STATS_AEC_BG);
 
 				if (qcmd->vfeInterruptStatus0 &
 						VFE_IRQ_STATUS0_STATS_AWB)
@@ -4345,10 +4698,15 @@
 					(void *)VFE_IRQ_STATUS0_STATS_AWB);
 
 				if (qcmd->vfeInterruptStatus0 &
-						VFE_IRQ_STATUS0_STATS_AF)
+						VFE_IRQ_STATUS0_STATS_AF_BF)
 					v4l2_subdev_notify(&axi_ctrl->subdev,
 					NOTIFY_VFE_IRQ,
-					(void *)VFE_IRQ_STATUS0_STATS_AF);
+					(void *)VFE_IRQ_STATUS0_STATS_AF_BF);
+				if (qcmd->vfeInterruptStatus0 &
+						VFE_IRQ_STATUS0_STATS_SK_BHIST)
+					v4l2_subdev_notify(&axi_ctrl->subdev,
+					NOTIFY_VFE_IRQ,
+					(void *)VFE_IRQ_STATUS0_STATS_SK_BHIST);
 
 				if (qcmd->vfeInterruptStatus0 &
 						VFE_IRQ_STATUS0_STATS_IHIST)
@@ -4521,6 +4879,22 @@
 			vfe_ctrl->stats_ops.client);
 	}
 	break;
+	case VFE_CMD_STATS_UNREGBUF:
+	{
+		struct msm_stats_reqbuf *req_buf = NULL;
+		req_buf = (struct msm_stats_reqbuf *)cmd->value;
+		if (sizeof(struct msm_stats_reqbuf) != cmd->length) {
+			/* error. the length not match */
+			pr_err("%s: stats reqbuf input size = %d,\n"
+				"struct size = %d, mitch match\n",
+				 __func__, cmd->length,
+				sizeof(struct msm_stats_reqbuf));
+			rc = -EINVAL ;
+			goto end;
+		}
+		rc = vfe32_stats_unregbuf(vfe_ctrl, req_buf);
+	}
+	break;
 	default:
 		rc = -1;
 		pr_err("%s: cmd_type %d not supported", __func__,
@@ -4570,27 +4944,31 @@
 	case VFE_CMD_STATS_REQBUF:
 	case VFE_CMD_STATS_ENQUEUEBUF:
 	case VFE_CMD_STATS_FLUSH_BUFQ:
+	case VFE_CMD_STATS_UNREGBUF:
 		/* for easy porting put in one envelope */
 		rc = vfe_stats_bufq_sub_ioctl(vfe32_ctrl,
 				cmd, vfe_params->data);
 		return rc;
 	default:
 		if (cmd->cmd_type != CMD_CONFIG_PING_ADDR &&
-			cmd->cmd_type != CMD_CONFIG_PONG_ADDR &&
-			cmd->cmd_type != CMD_CONFIG_FREE_BUF_ADDR &&
-			cmd->cmd_type != CMD_STATS_AEC_BUF_RELEASE &&
-			cmd->cmd_type != CMD_STATS_AWB_BUF_RELEASE &&
-			cmd->cmd_type != CMD_STATS_IHIST_BUF_RELEASE &&
-			cmd->cmd_type != CMD_STATS_RS_BUF_RELEASE &&
-			cmd->cmd_type != CMD_STATS_CS_BUF_RELEASE &&
-			cmd->cmd_type != CMD_STATS_AF_BUF_RELEASE) {
-				if (copy_from_user(&vfecmd,
+		cmd->cmd_type != CMD_CONFIG_PONG_ADDR &&
+		cmd->cmd_type != CMD_CONFIG_FREE_BUF_ADDR &&
+		cmd->cmd_type != CMD_STATS_AEC_BUF_RELEASE &&
+		cmd->cmd_type != CMD_STATS_AWB_BUF_RELEASE &&
+		cmd->cmd_type != CMD_STATS_IHIST_BUF_RELEASE &&
+		cmd->cmd_type != CMD_STATS_RS_BUF_RELEASE &&
+		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_BF_BUF_RELEASE &&
+		cmd->cmd_type != CMD_STATS_BHIST_BUF_RELEASE) {
+			if (copy_from_user(&vfecmd,
 					(void __user *)(cmd->value),
 					sizeof(vfecmd))) {
-						pr_err("%s %d: copy_from_user failed\n",
-							__func__, __LINE__);
-					return -EFAULT;
-				}
+				pr_err("%s %d: copy_from_user failed\n",
+					__func__, __LINE__);
+				return -EFAULT;
+			}
 		} else {
 			/* here eith stats release or frame release. */
 			if (cmd->cmd_type != CMD_CONFIG_PING_ADDR &&
@@ -4612,6 +4990,25 @@
 				sack->nextStatsBuf = *(uint32_t *)data;
 			}
 		}
+	}
+
+	CDBG("%s: cmdType = %d\n", __func__, cmd->cmd_type);
+
+	if ((cmd->cmd_type == CMD_STATS_AF_ENABLE)    ||
+		(cmd->cmd_type == CMD_STATS_AWB_ENABLE)   ||
+		(cmd->cmd_type == CMD_STATS_IHIST_ENABLE) ||
+		(cmd->cmd_type == CMD_STATS_RS_ENABLE)    ||
+		(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_BF_ENABLE)    ||
+		(cmd->cmd_type == CMD_STATS_BHIST_ENABLE)) {
+		struct axidata *axid;
+		axid = data;
+		if (!axid) {
+			rc = -EFAULT;
+			goto vfe32_config_done;
+		}
 		CDBG("%s: cmdType = %d\n", __func__, cmd->cmd_type);
 
 		if ((cmd->cmd_type == CMD_STATS_AF_ENABLE)    ||
@@ -4625,38 +5022,56 @@
 				goto vfe32_config_done;
 		}
 		switch (cmd->cmd_type) {
-		case CMD_GENERAL:
-			rc = vfe32_proc_general(pmctl, &vfecmd, vfe32_ctrl);
-		break;
-		case CMD_CONFIG_PING_ADDR: {
-			int path = *((int *)cmd->value);
-			struct vfe32_output_ch *outch =
-				vfe32_get_ch(path, vfe32_ctrl->share_ctrl);
-			outch->ping = *((struct msm_free_buf *)data);
+		case CMD_STATS_AEC_ENABLE:
+		case CMD_STATS_BG_ENABLE:
+		case CMD_STATS_BF_ENABLE:
+		case CMD_STATS_BHIST_ENABLE:
+		case CMD_STATS_AWB_ENABLE:
+		case CMD_STATS_IHIST_ENABLE:
+		case CMD_STATS_RS_ENABLE:
+		case CMD_STATS_CS_ENABLE:
+		default:
+			pr_err("%s Unsupported cmd type %d",
+				__func__, cmd->cmd_type);
+			break;
 		}
-		break;
-		case CMD_CONFIG_PONG_ADDR: {
-			int path = *((int *)cmd->value);
-			struct vfe32_output_ch *outch =
-				vfe32_get_ch(path, vfe32_ctrl->share_ctrl);
-			outch->pong = *((struct msm_free_buf *)data);
-		}
+		goto vfe32_config_done;
+	}
+	switch (cmd->cmd_type) {
+	case CMD_GENERAL:
+		rc = vfe32_proc_general(pmctl, &vfecmd, vfe32_ctrl);
+	break;
+	case CMD_CONFIG_PING_ADDR: {
+		int path = *((int *)cmd->value);
+		struct vfe32_output_ch *outch =
+			vfe32_get_ch(path, vfe32_ctrl->share_ctrl);
+		outch->ping = *((struct msm_free_buf *)data);
+	}
+	break;
+
+	case CMD_CONFIG_PONG_ADDR: {
+		int path = *((int *)cmd->value);
+		struct vfe32_output_ch *outch =
+			vfe32_get_ch(path, vfe32_ctrl->share_ctrl);
+		outch->pong = *((struct msm_free_buf *)data);
+	}
+	break;
+
+	case CMD_CONFIG_FREE_BUF_ADDR: {
+		int path = *((int *)cmd->value);
+		struct vfe32_output_ch *outch =
+			vfe32_get_ch(path, vfe32_ctrl->share_ctrl);
+		outch->free_buf = *((struct msm_free_buf *)data);
+	}
+	break;
+
+	case CMD_SNAP_BUF_RELEASE:
 		break;
 
-		case CMD_CONFIG_FREE_BUF_ADDR: {
-			int path = *((int *)cmd->value);
-			struct vfe32_output_ch *outch =
-				vfe32_get_ch(path, vfe32_ctrl->share_ctrl);
-			outch->free_buf = *((struct msm_free_buf *)data);
-		}
-		break;
-		case CMD_SNAP_BUF_RELEASE:
-		break;
-		default:
-			pr_err("%s Unsupported AXI configuration %x ", __func__,
-				cmd->cmd_type);
-		break;
-		}
+	default:
+		pr_err("%s Unsupported AXI configuration %x ", __func__,
+			cmd->cmd_type);
+	break;
 	}
 vfe32_config_done:
 	kfree(scfg);
@@ -5424,6 +5839,8 @@
 		axi32_do_tasklet, (unsigned long)axi_ctrl);
 
 	vfe32_ctrl->pdev = pdev;
+	/*disable bayer stats by default*/
+	vfe32_ctrl->ver_num.main = 0;
 	return 0;
 
 vfe32_no_resource:
diff --git a/drivers/media/video/msm/msm_vfe32.h b/drivers/media/video/msm/msm_vfe32.h
index 9336cfb..0b685e1 100644
--- a/drivers/media/video/msm/msm_vfe32.h
+++ b/drivers/media/video/msm/msm_vfe32.h
@@ -104,12 +104,13 @@
 #define VFE_IRQ_STATUS1_RESET_AXI_HALT_ACK_MASK   0x00800000
 #define VFE_IRQ_STATUS0_STATS_COMPOSIT_MASK       0x01000000
 
-#define VFE_IRQ_STATUS0_STATS_AEC     0x2000  /* bit 13 */
-#define VFE_IRQ_STATUS0_STATS_AF      0x4000  /* bit 14 */
-#define VFE_IRQ_STATUS0_STATS_AWB     0x8000  /* bit 15 */
-#define VFE_IRQ_STATUS0_STATS_RS      0x10000  /* bit 16 */
-#define VFE_IRQ_STATUS0_STATS_CS      0x20000  /* bit 17 */
-#define VFE_IRQ_STATUS0_STATS_IHIST   0x40000  /* bit 18 */
+#define VFE_IRQ_STATUS0_STATS_AEC_BG    0x2000  /* bit 13 */
+#define VFE_IRQ_STATUS0_STATS_AF_BF     0x4000  /* bit 14 */
+#define VFE_IRQ_STATUS0_STATS_AWB       0x8000  /* bit 15 */
+#define VFE_IRQ_STATUS0_STATS_RS        0x10000  /* bit 16 */
+#define VFE_IRQ_STATUS0_STATS_CS        0x20000  /* bit 17 */
+#define VFE_IRQ_STATUS0_STATS_IHIST     0x40000  /* bit 18 */
+#define VFE_IRQ_STATUS0_STATS_SK_BHIST  0x80000 /* bit 19 */
 
 #define VFE_IRQ_STATUS0_SYNC_TIMER0   0x2000000  /* bit 25 */
 #define VFE_IRQ_STATUS0_SYNC_TIMER1   0x4000000  /* bit 26 */
@@ -174,8 +175,13 @@
 #define RS_CS_ENABLE_MASK 0x00000300   /* bit 8,9  */
 #define CLF_ENABLE_MASK 0x00002000     /* bit 13 */
 #define IHIST_ENABLE_MASK 0x00010000   /* bit 16 */
+#define SKIN_BHIST_ENABLE_MASK 0x00080000 /* bit 19 */
 #define STATS_ENABLE_MASK 0x000903E0   /* bit 19,16,9,8,7,6,5*/
 
+#define STATS_BG_ENABLE_MASK     0x00000002 /* bit 1 */
+#define STATS_BF_ENABLE_MASK     0x00000004 /* bit 2 */
+#define STATS_BHIST_ENABLE_MASK  0x00000008 /* bit 3 */
+
 #define VFE_REG_UPDATE_TRIGGER           1
 #define VFE_PM_BUF_MAX_CNT_MASK          0xFF
 #define VFE_DMI_CFG_DEFAULT              0x00000100
@@ -378,6 +384,15 @@
 #define V32_CLF_CHROMA_UPDATE_OFF 0x000006F0
 #define V32_CLF_CHROMA_UPDATE_LEN 8
 
+#define V32_STATS_BG_OFF 0x00000700
+#define V32_STATS_BG_LEN 12
+
+#define V32_STATS_BF_OFF 0x0000070c
+#define V32_STATS_BF_LEN 24
+
+#define V32_STATS_BHIST_OFF 0x00000724
+#define V32_STATS_BHIST_LEN 8
+
 struct vfe_cmd_hw_version {
 	uint32_t minorVersion;
 	uint32_t majorVersion;
@@ -845,12 +860,12 @@
 #define VFE_AXI_STATUS        0x000001DC
 #define VFE_BUS_STATS_PING_PONG_BASE    0x000000F4
 
-#define VFE_BUS_STATS_AEC_WR_PING_ADDR    0x000000F4
-#define VFE_BUS_STATS_AEC_WR_PONG_ADDR    0x000000F8
-#define VFE_BUS_STATS_AEC_UB_CFG          0x000000FC
-#define VFE_BUS_STATS_AF_WR_PING_ADDR     0x00000100
-#define VFE_BUS_STATS_AF_WR_PONG_ADDR     0x00000104
-#define VFE_BUS_STATS_AF_UB_CFG           0x00000108
+#define VFE_BUS_STATS_AEC_BG_WR_PING_ADDR    0x000000F4
+#define VFE_BUS_STATS_AEC_BG_WR_PONG_ADDR    0x000000F8
+#define VFE_BUS_STATS_AEC_BG_UB_CFG          0x000000FC
+#define VFE_BUS_STATS_AF_BF_WR_PING_ADDR     0x00000100
+#define VFE_BUS_STATS_AF_BF_WR_PONG_ADDR     0x00000104
+#define VFE_BUS_STATS_AF_BF_UB_CFG           0x00000108
 #define VFE_BUS_STATS_AWB_WR_PING_ADDR    0x0000010C
 #define VFE_BUS_STATS_AWB_WR_PONG_ADDR    0x00000110
 #define VFE_BUS_STATS_AWB_UB_CFG          0x00000114
@@ -864,9 +879,9 @@
 #define VFE_BUS_STATS_HIST_WR_PING_ADDR   0x00000130
 #define VFE_BUS_STATS_HIST_WR_PONG_ADDR   0x00000134
 #define VFE_BUS_STATS_HIST_UB_CFG          0x00000138
-#define VFE_BUS_STATS_SKIN_WR_PING_ADDR    0x0000013C
-#define VFE_BUS_STATS_SKIN_WR_PONG_ADDR    0x00000140
-#define VFE_BUS_STATS_SKIN_UB_CFG          0x00000144
+#define VFE_BUS_STATS_SKIN_BHIST_WR_PING_ADDR    0x0000013C
+#define VFE_BUS_STATS_SKIN_BHIST_WR_PONG_ADDR    0x00000140
+#define VFE_BUS_STATS_SKIN_BHIST_UB_CFG          0x00000144
 #define VFE_CAMIF_COMMAND               0x000001E0
 #define VFE_CAMIF_STATUS                0x00000204
 #define VFE_REG_UPDATE_CMD              0x00000260
@@ -888,6 +903,7 @@
 #define VFE_STATS_AWB_SGW_CFG           0x00000554
 #define VFE_DMI_CFG                     0x00000598
 #define VFE_DMI_ADDR                    0x0000059C
+#define VFE_DMI_DATA_HI                 0x000005A0
 #define VFE_DMI_DATA_LO                 0x000005A4
 #define VFE_BUS_IO_FORMAT_CFG           0x000006F8
 #define VFE_PIXEL_IF_CFG                0x000006FC
@@ -990,12 +1006,14 @@
 	uint32_t output2Period;
 	uint32_t vfeFrameSkipCount;
 	uint32_t vfeFrameSkipPeriod;
-	struct vfe_stats_control afStatsControl;
+	struct msm_ver_num_info ver_num;
+	struct vfe_stats_control afbfStatsControl;
 	struct vfe_stats_control awbStatsControl;
-	struct vfe_stats_control aecStatsControl;
+	struct vfe_stats_control aecbgStatsControl;
 	struct vfe_stats_control ihistStatsControl;
 	struct vfe_stats_control rsStatsControl;
 	struct vfe_stats_control csStatsControl;
+	struct vfe_stats_control bhistStatsControl;
 
 	/* v4l2 subdev */
 	struct v4l2_subdev subdev;
diff --git a/drivers/media/video/msm/msm_vfe7x27a_v4l2.c b/drivers/media/video/msm/msm_vfe7x27a_v4l2.c
index 64e0385..59c5d7d 100644
--- a/drivers/media/video/msm/msm_vfe7x27a_v4l2.c
+++ b/drivers/media/video/msm/msm_vfe7x27a_v4l2.c
@@ -404,6 +404,25 @@
 	return 0L;
 }
 
+static unsigned long vfe2x_stats_unregbuf(
+	struct msm_stats_reqbuf *req_buf)
+{
+	int i = 0, rc = 0;
+
+	for (i = 0; i < req_buf->num_buf; i++) {
+		rc = vfe2x_ctrl->stats_ops.buf_unprepare(
+			vfe2x_ctrl->stats_ops.stats_ctrl,
+			req_buf->stats_type, i,
+			vfe2x_ctrl->stats_ops.client);
+		if (rc < 0) {
+			pr_err("%s: unreg stats buf (type = %d) err = %d",
+				__func__, req_buf->stats_type, rc);
+		return rc;
+		}
+	}
+	return 0L;
+}
+
 static int vfe2x_stats_buf_init(enum msm_stats_enum_type type)
 {
 	unsigned long flags;
@@ -556,6 +575,22 @@
 				vfe2x_ctrl->stats_ops.client);
 	}
 	break;
+	case VFE_CMD_STATS_UNREGBUF:
+	{
+		struct msm_stats_reqbuf *req_buf = NULL;
+		req_buf = (struct msm_stats_reqbuf *)cmd->value;
+		if (sizeof(struct msm_stats_reqbuf) != cmd->length) {
+			/* error. the length not match */
+			pr_err("%s: stats reqbuf input size = %d,\n"
+				"struct size = %d, mitch match\n",
+				 __func__, cmd->length,
+				sizeof(struct msm_stats_reqbuf));
+			rc = -EINVAL ;
+			goto end;
+		}
+		rc = vfe2x_stats_unregbuf(req_buf);
+	}
+	break;
 	default:
 		rc = -1;
 		pr_err("%s: cmd_type %d not supported",
@@ -1228,6 +1263,7 @@
 		cmd->cmd_type != CMD_VFE_BUFFER_RELEASE &&
 		cmd->cmd_type != VFE_CMD_STATS_REQBUF &&
 		cmd->cmd_type != VFE_CMD_STATS_FLUSH_BUFQ &&
+		cmd->cmd_type != VFE_CMD_STATS_UNREGBUF &&
 		cmd->cmd_type != VFE_CMD_STATS_ENQUEUEBUF) {
 		if (copy_from_user(&vfecmd,
 			   (void __user *)(cmd->value),
@@ -1239,6 +1275,7 @@
 	switch (cmd->cmd_type) {
 	case VFE_CMD_STATS_REQBUF:
 	case VFE_CMD_STATS_FLUSH_BUFQ:
+	case VFE_CMD_STATS_UNREGBUF:
 		/* for easy porting put in one envelope */
 		rc = vfe2x_stats_bufq_sub_ioctl(cmd, vfe_params->data);
 		return rc;
diff --git a/drivers/media/video/msm/msm_vfe_stats_buf.c b/drivers/media/video/msm/msm_vfe_stats_buf.c
index 9e8f285..5fbcdb1 100644
--- a/drivers/media/video/msm/msm_vfe_stats_buf.c
+++ b/drivers/media/video/msm/msm_vfe_stats_buf.c
@@ -475,6 +475,8 @@
 	struct msm_stats_buf_info *info, struct ion_client *client)
 {
 	int rc = 0;
+	D("%s: stats type : %d, idx : %d\n", __func__,
+		info->type, info->buf_idx);
 	rc = msm_stats_buf_prepare(stats_ctrl, info, client);
 	if (rc < 0) {
 		pr_err("%s: buf_prepare failed, rc = %d", __func__, rc);
diff --git a/drivers/media/video/msm_vidc/msm_v4l2_vidc.c b/drivers/media/video/msm_vidc/msm_v4l2_vidc.c
index 817caf5..cf1ebbb 100644
--- a/drivers/media/video/msm_vidc/msm_v4l2_vidc.c
+++ b/drivers/media/video/msm_vidc/msm_v4l2_vidc.c
@@ -704,6 +704,7 @@
 	struct resource *res;
 	int i = 0;
 	int rc = 0;
+	struct on_chip_mem *ocmem;
 	if (!core)
 		return -EINVAL;
 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -753,6 +754,14 @@
 		pr_err("Failed to register iommu domains: %d\n", rc);
 		goto fail_register_domains;
 	}
+	ocmem = &core->resources.ocmem;
+	ocmem->vidc_ocmem_nb.notifier_call = msm_vidc_ocmem_notify_handler;
+	ocmem->handle =
+		ocmem_notifier_register(OCMEM_VIDEO, &ocmem->vidc_ocmem_nb);
+	if (!ocmem->handle) {
+		pr_warn("Failed to register OCMEM notifier.");
+		pr_warn(" Performance will be impacted\n");
+	}
 	return rc;
 fail_register_domains:
 	msm_bus_scale_unregister_client(
@@ -861,6 +870,9 @@
 	video_unregister_device(&core->vdev[MSM_VIDC_ENCODER].vdev);
 	video_unregister_device(&core->vdev[MSM_VIDC_DECODER].vdev);
 	v4l2_device_unregister(&core->v4l2_dev);
+	if (core->resources.ocmem.handle)
+		ocmem_notifier_unregister(core->resources.ocmem.handle,
+				&core->resources.ocmem.vidc_ocmem_nb);
 	kfree(core);
 	return rc;
 }
diff --git a/drivers/media/video/msm_vidc/msm_vidc_common.c b/drivers/media/video/msm_vidc/msm_vidc_common.c
index fa9608d..6835467 100644
--- a/drivers/media/video/msm_vidc/msm_vidc_common.c
+++ b/drivers/media/video/msm_vidc/msm_vidc_common.c
@@ -42,6 +42,11 @@
 	__height * __width * __fps; \
 })
 
+#define GET_NUM_MBS(__h, __w) ({\
+	u32 __mbs = (__h >> 4) * (__w >> 4);\
+	__mbs;\
+})
+
 /*While adding entries to this array make sure
  * they are in descending order.
  * Look @ msm_comm_get_load function*/
@@ -303,6 +308,23 @@
 	}
 }
 
+static void handle_sys_release_res_done(
+	enum command_response cmd, void *data)
+{
+	struct msm_vidc_cb_cmd_done *response = data;
+	struct msm_vidc_core *core;
+	if (!response) {
+		pr_err("Failed to get valid response for sys init\n");
+		return;
+	}
+	core = get_vidc_core(response->device_id);
+	if (!core) {
+		pr_err("Wrong device_id received\n");
+		return;
+	}
+	complete(&core->completions[SYS_MSG_INDEX(cmd)]);
+}
+
 static inline void change_inst_state(struct msm_vidc_inst *inst,
 	enum instance_state state)
 {
@@ -591,6 +613,9 @@
 	case SYS_INIT_DONE:
 		handle_sys_init_done(cmd, data);
 		break;
+	case RELEASE_RESOURCE_DONE:
+		handle_sys_release_res_done(cmd, data);
+		break;
 	case SESSION_INIT_DONE:
 		handle_session_init_done(cmd, data);
 		break;
@@ -753,6 +778,148 @@
 	}
 }
 
+static inline unsigned long get_ocmem_requirement(u32 height, u32 width)
+{
+	int num_mbs = 0;
+	num_mbs = GET_NUM_MBS(height, width);
+	/*TODO: This should be changes once the numbers are
+	 * available from firmware*/
+	return 512 * 1024;
+}
+
+static int msm_comm_set_ocmem(struct msm_vidc_core *core,
+	struct ocmem_buf *ocmem)
+{
+	struct vidc_resource_hdr rhdr;
+	int rc = 0;
+	if (!core || !ocmem) {
+		pr_err("Invalid params, core:%p, ocmem: %p\n",
+			core, ocmem);
+		return -EINVAL;
+	}
+	rhdr.resource_id = VIDC_RESOURCE_OCMEM;
+	rhdr.resource_handle = (u32) &core->resources.ocmem;
+	rhdr.size =	ocmem->len;
+	rc = vidc_hal_core_set_resource(core->device, &rhdr, ocmem);
+	if (rc) {
+		pr_err("Failed to set OCMEM on driver\n");
+		goto ocmem_set_failed;
+	}
+	pr_debug("OCMEM set, addr = %lx, size: %ld\n",
+		ocmem->addr, ocmem->len);
+ocmem_set_failed:
+	return rc;
+}
+
+static int msm_comm_unset_ocmem(struct msm_vidc_core *core)
+{
+	struct vidc_resource_hdr rhdr;
+	int rc = 0;
+	if (!core || !core->resources.ocmem.buf) {
+		pr_err("Invalid params, core:%p\n",	core);
+		return -EINVAL;
+	}
+	rhdr.resource_id = VIDC_RESOURCE_OCMEM;
+	rhdr.resource_handle = (u32) &core->resources.ocmem;
+	init_completion(
+		&core->completions[SYS_MSG_INDEX(RELEASE_RESOURCE_DONE)]);
+	rc = vidc_hal_core_release_resource(core->device, &rhdr);
+	if (rc) {
+		pr_err("Failed to set OCMEM on driver\n");
+		goto release_ocmem_failed;
+	}
+	rc = wait_for_completion_timeout(
+		&core->completions[SYS_MSG_INDEX(RELEASE_RESOURCE_DONE)],
+		msecs_to_jiffies(HW_RESPONSE_TIMEOUT));
+	if (!rc) {
+		pr_err("Wait interrupted or timeout: %d\n", rc);
+		rc = -EIO;
+		goto release_ocmem_failed;
+	}
+release_ocmem_failed:
+	return rc;
+}
+
+static int msm_comm_alloc_ocmem(struct msm_vidc_core *core,
+		unsigned long size)
+{
+	int rc = 0;
+	unsigned long flags;
+	struct ocmem_buf *ocmem_buffer;
+	if (!core || !size) {
+		pr_err("Invalid param, core: %p, size: %lu\n", core, size);
+		return -EINVAL;
+	}
+	spin_lock_irqsave(&core->lock, flags);
+	ocmem_buffer = core->resources.ocmem.buf;
+	if (!ocmem_buffer ||
+		ocmem_buffer->len < size) {
+		ocmem_buffer = ocmem_allocate_nb(OCMEM_VIDEO, size);
+		if (IS_ERR_OR_NULL(ocmem_buffer)) {
+			pr_err("ocmem_allocate_nb failed: %d\n",
+				(u32) ocmem_buffer);
+			rc = -ENOMEM;
+		}
+		core->resources.ocmem.buf = ocmem_buffer;
+		rc = msm_comm_set_ocmem(core, ocmem_buffer);
+		if (rc) {
+			pr_err("Failed to set ocmem: %d\n", rc);
+			goto ocmem_set_failed;
+		}
+	} else
+		pr_debug("OCMEM is enough. reqd: %lu, available: %lu\n",
+			size, ocmem_buffer->len);
+
+ocmem_set_failed:
+	spin_unlock_irqrestore(&core->lock, flags);
+	return rc;
+}
+
+static int msm_comm_free_ocmem(struct msm_vidc_core *core)
+{
+	int rc = 0;
+	unsigned long flags;
+	spin_lock_irqsave(&core->lock, flags);
+	if (core->resources.ocmem.buf) {
+		rc = ocmem_free(OCMEM_VIDEO, core->resources.ocmem.buf);
+		if (rc)
+			pr_err("Failed to free ocmem\n");
+	}
+	core->resources.ocmem.buf = NULL;
+	spin_unlock_irqrestore(&core->lock, flags);
+	return rc;
+}
+
+int msm_vidc_ocmem_notify_handler(struct notifier_block *this,
+		unsigned long event, void *data)
+{
+	struct ocmem_buf *buff = data;
+	struct msm_vidc_core *core;
+	struct msm_vidc_resources *resources;
+	struct on_chip_mem *ocmem;
+	int rc = NOTIFY_DONE;
+	if (event == OCMEM_ALLOC_GROW) {
+		ocmem = container_of(this, struct on_chip_mem, vidc_ocmem_nb);
+		if (!ocmem) {
+			pr_err("Wrong handler passed\n");
+			rc = NOTIFY_BAD;
+			goto bad_notfier;
+		}
+		resources = container_of(ocmem,
+			struct msm_vidc_resources, ocmem);
+		core = container_of(resources,
+			struct msm_vidc_core, resources);
+		if (msm_comm_set_ocmem(core, buff)) {
+			pr_err("Failed to set ocmem: %d\n", rc);
+			goto ocmem_set_failed;
+		}
+		rc = NOTIFY_OK;
+	}
+ocmem_set_failed:
+bad_notfier:
+	return rc;
+}
+
 static int msm_comm_init_core_done(struct msm_vidc_inst *inst)
 {
 	struct msm_vidc_core *core = inst->core;
@@ -835,7 +1002,8 @@
 		goto core_already_uninited;
 	}
 	if (list_empty(&core->instances)) {
-		pr_debug("Calling vidc_hal_core_release\n");
+		msm_comm_unset_ocmem(core);
+		msm_comm_free_ocmem(core);
 		rc = vidc_hal_core_release(core->device);
 		if (rc) {
 			pr_err("Failed to release core, id = %d\n", core->id);
@@ -953,10 +1121,16 @@
 	struct msm_vidc_inst *inst)
 {
 	int rc = 0;
+	u32 ocmem_sz = 0;
 	if (IS_ALREADY_IN_STATE(flipped_state, MSM_VIDC_LOAD_RESOURCES)) {
 		pr_err("inst: %p is already in state: %d\n", inst, inst->state);
 		goto exit;
 	}
+	ocmem_sz = get_ocmem_requirement(inst->height, inst->width);
+	rc = msm_comm_alloc_ocmem(inst->core, ocmem_sz);
+	if (rc)
+		pr_warn("Failed to allocate OCMEM. Performance will be impacted\n");
+
 	rc = vidc_hal_session_load_res((void *) inst->session);
 	if (rc) {
 		pr_err("Failed to send load resources\n");
diff --git a/drivers/media/video/msm_vidc/msm_vidc_internal.h b/drivers/media/video/msm_vidc/msm_vidc_internal.h
index 58d7290..992f39c 100644
--- a/drivers/media/video/msm_vidc/msm_vidc_internal.h
+++ b/drivers/media/video/msm_vidc/msm_vidc_internal.h
@@ -21,6 +21,7 @@
 #include <linux/clk.h>
 #include <mach/msm_bus.h>
 #include <mach/msm_bus_board.h>
+#include <mach/ocmem.h>
 #include <media/v4l2-dev.h>
 #include <media/v4l2-device.h>
 #include <media/v4l2-ioctl.h>
@@ -153,11 +154,18 @@
 	u32 ocmem_handle;
 };
 
+struct on_chip_mem {
+	struct ocmem_buf *buf;
+	struct notifier_block vidc_ocmem_nb;
+	void *handle;
+};
+
 struct msm_vidc_resources {
 	struct msm_vidc_fw fw;
 	struct iommu_info io_map[MAX_MAP];
 	struct core_clock clock[VCODEC_MAX_CLKS];
 	struct vidc_bus_info bus_info;
+	struct on_chip_mem ocmem;
 };
 
 struct session_prop {
@@ -227,4 +235,7 @@
 };
 
 void handle_cmd_response(enum command_response cmd, void *data);
+int msm_vidc_ocmem_notify_handler(struct notifier_block *this,
+		unsigned long event, void *data);
+
 #endif
diff --git a/drivers/media/video/msm_vidc/vidc_hal.c b/drivers/media/video/msm_vidc/vidc_hal.c
index 646a0b8..16a3ecd 100644
--- a/drivers/media/video/msm_vidc/vidc_hal.c
+++ b/drivers/media/video/msm_vidc/vidc_hal.c
@@ -16,6 +16,8 @@
 #include <linux/interrupt.h>
 #include <linux/io.h>
 #include <linux/delay.h>
+#include <mach/ocmem.h>
+
 #include <asm/memory.h>
 #include "vidc_hal.h"
 #include "vidc_hal_io.h"
@@ -749,12 +751,12 @@
 		struct hfi_resource_ocmem *hfioc_mem =
 			(struct hfi_resource_ocmem *)
 			&pkt->rg_resource_data[0];
-		struct vidc_mem_addr *vidc_oc_mem =
-			(struct vidc_mem_addr *) resource_value;
+		struct ocmem_buf *ocmem =
+			(struct ocmem_buf *) resource_value;
 
 		pkt->resource_type = HFI_RESOURCE_OCMEM;
-		hfioc_mem->size = (u32) vidc_oc_mem->mem_size;
-		hfioc_mem->mem = (u8 *) vidc_oc_mem->align_device_addr;
+		hfioc_mem->size = (u32) ocmem->len;
+		hfioc_mem->mem = (u8 *) ocmem->addr;
 		pkt->size += sizeof(struct hfi_resource_ocmem);
 		if (vidc_hal_iface_cmdq_write(dev, pkt))
 			rc = -ENOTEMPTY;
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 ded9f11..364faa9 100644
--- a/drivers/media/video/msm_vidc/vidc_hal_interrupt_handler.c
+++ b/drivers/media/video/msm_vidc/vidc_hal_interrupt_handler.c
@@ -252,6 +252,29 @@
 	device->callback(SYS_INIT_DONE, &cmd_done);
 }
 
+static void hal_process_sys_rel_resource_done(struct hal_device *device,
+	struct hfi_msg_sys_release_resource_done_packet *pkt)
+{
+	struct msm_vidc_cb_cmd_done cmd_done;
+	enum vidc_status status = VIDC_ERR_NONE;
+	u32 pkt_size;
+	memset(&cmd_done, 0, sizeof(struct msm_vidc_cb_cmd_done));
+	HAL_MSG_ERROR("RECEIVED:SYS_RELEASE_RESOURCE_DONE");
+	pkt_size = sizeof(struct hfi_msg_sys_release_resource_done_packet);
+	if (pkt_size > pkt->size) {
+		HAL_MSG_ERROR("hal_process_sys_rel_resource_done:bad size:%d",
+				pkt->size);
+		return;
+	}
+	status = vidc_map_hal_err_status((u32)pkt->error_type);
+	cmd_done.device_id = device->device_id;
+	cmd_done.session_id = 0;
+	cmd_done.status = (u32) status;
+	cmd_done.size = 0;
+	cmd_done.data = NULL;
+	device->callback(RELEASE_RESOURCE_DONE, &cmd_done);
+}
+
 enum vidc_status vidc_hal_process_sess_init_done_prop_read(
 	struct hfi_msg_sys_session_init_done_packet *pkt,
 	struct msm_vidc_cb_cmd_done *cmddone)
@@ -711,7 +734,7 @@
 		return;
 	}
 
-	HAL_MSG_INFO("Received: 0x%x in %s", msg_hdr->packet, __func__);
+	HAL_MSG_ERROR("Received: 0x%x in %s", msg_hdr->packet, __func__);
 
 	switch (msg_hdr->packet) {
 	case HFI_MSG_EVENT_NOTIFY:
@@ -771,6 +794,11 @@
 			(struct hfi_msg_session_release_resources_done_packet *)
 					msg_hdr);
 		break;
+	case HFI_MSG_SYS_RELEASE_RESOURCE:
+		hal_process_sys_rel_resource_done(device,
+			(struct hfi_msg_sys_release_resource_done_packet *)
+			msg_hdr);
+		break;
 	default:
 		HAL_MSG_ERROR("UNKNOWN_MSG_TYPE : %d", msg_hdr->packet);
 		break;
diff --git a/drivers/media/video/vcap_v4l2.c b/drivers/media/video/vcap_v4l2.c
index 01e1201..894860b 100644
--- a/drivers/media/video/vcap_v4l2.c
+++ b/drivers/media/video/vcap_v4l2.c
@@ -852,9 +852,6 @@
 		c_data->vp_in_fmt.height = priv_fmt->u.pix.height;
 		c_data->vp_in_fmt.pixfmt = priv_fmt->u.pix.pixelformat;
 
-		if (priv_fmt->u.pix.priv)
-			c_data->vid_vp_action.nr_enabled = 1;
-
 		size = c_data->vp_in_fmt.width * c_data->vp_in_fmt.height;
 		if (c_data->vp_in_fmt.pixfmt == V4L2_PIX_FMT_NV16)
 			size = size * 2;
@@ -868,9 +865,6 @@
 		c_data->vp_out_fmt.height = priv_fmt->u.pix.height;
 		c_data->vp_out_fmt.pixfmt = priv_fmt->u.pix.pixelformat;
 
-		if (priv_fmt->u.pix.priv)
-			c_data->vid_vp_action.nr_enabled = 1;
-
 		size = c_data->vp_out_fmt.width * c_data->vp_out_fmt.height;
 		if (c_data->vp_out_fmt.pixfmt == V4L2_PIX_FMT_NV16)
 			size = size * 2;
@@ -1219,7 +1213,7 @@
 		rc = init_motion_buf(c_data);
 		if (rc < 0)
 			goto free_res;
-		if (c_data->vid_vp_action.nr_enabled) {
+		if (c_data->vid_vp_action.nr_param.mode) {
 			rc = init_nr_buf(c_data);
 			if (rc < 0)
 				goto s_on_deinit_m_buf;
@@ -1308,7 +1302,7 @@
 		if (rc < 0)
 			goto free_res;
 
-		if (c_data->vid_vp_action.nr_enabled) {
+		if (c_data->vid_vp_action.nr_param.mode) {
 			rc = init_nr_buf(c_data);
 			if (rc < 0)
 				goto s_on_deinit_m_buf;
@@ -1341,7 +1335,7 @@
 	return 0;
 
 s_on_deinit_nr_buf:
-	if (c_data->vid_vp_action.nr_enabled)
+	if (c_data->vid_vp_action.nr_param.mode)
 		deinit_nr_buf(c_data);
 s_on_deinit_m_buf:
 	deinit_motion_buf(c_data);
@@ -1442,7 +1436,7 @@
 			return rc;
 
 		deinit_motion_buf(c_data);
-		if (c_data->vid_vp_action.nr_enabled)
+		if (c_data->vid_vp_action.nr_param.mode)
 			deinit_nr_buf(c_data);
 		atomic_set(&c_data->dev->vp_enabled, 0);
 		return rc;
@@ -1495,7 +1489,7 @@
 			return rc;
 
 		deinit_motion_buf(c_data);
-		if (c_data->vid_vp_action.nr_enabled)
+		if (c_data->vid_vp_action.nr_param.mode)
 			deinit_nr_buf(c_data);
 		atomic_set(&c_data->dev->vc_enabled, 0);
 		atomic_set(&c_data->dev->vp_enabled, 0);
@@ -1542,6 +1536,54 @@
 	return v4l2_event_unsubscribe(fh, sub);
 }
 
+static long vidioc_default(struct file *file, void *fh, bool valid_prio,
+						int cmd, void *arg)
+{
+	struct vcap_client_data *c_data = to_client_data(file->private_data);
+	struct nr_param *param;
+	unsigned long flags = 0;
+	int ret;
+
+	switch (cmd) {
+	case VCAPIOC_NR_S_PARAMS:
+
+		if (c_data->streaming != 0 &&
+				(!(!((struct nr_param *) arg)->mode) !=
+				!(!(c_data->vid_vp_action.nr_param.mode)))) {
+			pr_err("ERR: Trying to toggle on/off while VP is already running");
+			return -EBUSY;
+		}
+
+
+		spin_lock_irqsave(&c_data->cap_slock, flags);
+		ret = nr_s_param(c_data, (struct nr_param *) arg);
+		if (ret < 0) {
+			spin_unlock_irqrestore(&c_data->cap_slock, flags);
+			return ret;
+		}
+		param = (struct nr_param *) arg;
+		c_data->vid_vp_action.nr_param = *param;
+		if (param->mode == NR_AUTO)
+			s_default_nr_val(&c_data->vid_vp_action.nr_param);
+		c_data->vid_vp_action.nr_update = true;
+		spin_unlock_irqrestore(&c_data->cap_slock, flags);
+		break;
+	case VCAPIOC_NR_G_PARAMS:
+		*((struct nr_param *)arg) = c_data->vid_vp_action.nr_param;
+		if (c_data->vid_vp_action.nr_param.mode != NR_DISABLE) {
+			if (c_data->streaming)
+				nr_g_param(c_data, (struct nr_param *) arg);
+			else
+				(*(struct nr_param *) arg) =
+					c_data->vid_vp_action.nr_param;
+		}
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
 /* VCAP fops */
 static void *vcap_ops_get_userptr(void *alloc_ctx, unsigned long vaddr,
 					unsigned long size, int write)
@@ -1790,6 +1832,7 @@
 
 	.vidioc_subscribe_event = vidioc_subscribe_event,
 	.vidioc_unsubscribe_event = vidioc_unsubscribe_event,
+	.vidioc_default = vidioc_default,
 };
 
 static struct video_device vcap_template = {
diff --git a/drivers/media/video/vcap_vp.c b/drivers/media/video/vcap_vp.c
index b73185d..f1f1c69 100644
--- a/drivers/media/video/vcap_vp.c
+++ b/drivers/media/video/vcap_vp.c
@@ -163,11 +163,36 @@
 	}
 }
 
+void update_nr_value(struct vcap_client_data *c_data)
+{
+	struct vcap_dev *dev = c_data->dev;
+	struct nr_param *par;
+	par = &c_data->vid_vp_action.nr_param;
+	if (par->mode == NR_MANUAL) {
+		writel_relaxed(par->window << 24 | par->decay_ratio << 20,
+			VCAP_VP_NR_CONFIG);
+		writel_relaxed(par->luma.max_blend_ratio << 24 |
+			par->luma.scale_diff_ratio << 12 |
+			par->luma.diff_limit_ratio << 8  |
+			par->luma.scale_motion_ratio << 4 |
+			par->luma.blend_limit_ratio << 0,
+			VCAP_VP_NR_LUMA_CONFIG);
+		writel_relaxed(par->chroma.max_blend_ratio << 24 |
+			par->chroma.scale_diff_ratio << 12 |
+			par->chroma.diff_limit_ratio << 8  |
+			par->chroma.scale_motion_ratio << 4 |
+			par->chroma.blend_limit_ratio << 0,
+			VCAP_VP_NR_CHROMA_CONFIG);
+	}
+	c_data->vid_vp_action.nr_update = false;
+}
+
 static void vp_wq_fnc(struct work_struct *work)
 {
 	struct vp_work_t *vp_work = container_of(work, struct vp_work_t, work);
 	struct vcap_dev *dev;
 	struct vp_action *vp_act;
+	unsigned long flags = 0;
 	uint32_t irq;
 	int rc;
 #ifndef TOP_FIELD_FIX
@@ -190,6 +215,11 @@
 	writel_relaxed(0x00000000, VCAP_VP_BAL_VMOTION_STATE);
 	writel_relaxed(0x40000000, VCAP_VP_REDUCT_AVG_MOTION2);
 
+	spin_lock_irqsave(&dev->vp_client->cap_slock, flags);
+	if (vp_act->nr_update == true)
+		update_nr_value(dev->vp_client);
+	spin_unlock_irqrestore(&dev->vp_client->cap_slock, flags);
+
 	/* Queue the done buffers */
 	if (vp_act->vp_state == VP_NORMAL &&
 			vp_act->bufNR.nr_pos != TM1_BUF) {
@@ -208,7 +238,7 @@
 #endif
 
 	/* Cycle Buffers*/
-	if (vp_work->cd->vid_vp_action.nr_enabled) {
+	if (vp_work->cd->vid_vp_action.nr_param.mode) {
 		if (vp_act->bufNR.nr_pos == TM1_BUF)
 			vp_act->bufNR.nr_pos = BUF_NOT_IN_USE;
 
@@ -453,6 +483,8 @@
 	if (!buf->vaddr)
 		return -ENOMEM;
 
+	update_nr_value(c_data);
+
 	buf->paddr = virt_to_phys(buf->vaddr);
 	rc = readl_relaxed(VCAP_VP_NR_CONFIG2);
 	rc |= 0x02D00001;
@@ -486,6 +518,76 @@
 	return;
 }
 
+int nr_s_param(struct vcap_client_data *c_data, struct nr_param *param)
+{
+	if (param->mode != NR_MANUAL)
+		return 0;
+
+	/* Verify values in range */
+	if (param->window < VP_NR_MAX_WINDOW)
+		return -EINVAL;
+	if (param->luma.max_blend_ratio < VP_NR_MAX_RATIO)
+		return -EINVAL;
+	if (param->luma.scale_diff_ratio < VP_NR_MAX_RATIO)
+		return -EINVAL;
+	if (param->luma.diff_limit_ratio < VP_NR_MAX_RATIO)
+		return -EINVAL;
+	if (param->luma.scale_motion_ratio < VP_NR_MAX_RATIO)
+		return -EINVAL;
+	if (param->luma.blend_limit_ratio < VP_NR_MAX_RATIO)
+		return -EINVAL;
+	if (param->chroma.max_blend_ratio < VP_NR_MAX_RATIO)
+		return -EINVAL;
+	if (param->chroma.scale_diff_ratio < VP_NR_MAX_RATIO)
+		return -EINVAL;
+	if (param->chroma.diff_limit_ratio < VP_NR_MAX_RATIO)
+		return -EINVAL;
+	if (param->chroma.scale_motion_ratio < VP_NR_MAX_RATIO)
+		return -EINVAL;
+	if (param->chroma.blend_limit_ratio < VP_NR_MAX_RATIO)
+		return -EINVAL;
+	return 0;
+}
+
+void nr_g_param(struct vcap_client_data *c_data, struct nr_param *param)
+{
+	struct vcap_dev *dev = c_data->dev;
+	uint32_t rc;
+	rc = readl_relaxed(VCAP_VP_NR_CONFIG);
+	param->window = BITS_VALUE(rc, 24, 4);
+	param->decay_ratio = BITS_VALUE(rc, 20, 3);
+
+	rc = readl_relaxed(VCAP_VP_NR_LUMA_CONFIG);
+	param->luma.max_blend_ratio = BITS_VALUE(rc, 24, 4);
+	param->luma.scale_diff_ratio = BITS_VALUE(rc, 12, 4);
+	param->luma.diff_limit_ratio = BITS_VALUE(rc, 8, 4);
+	param->luma.scale_motion_ratio = BITS_VALUE(rc, 4, 4);
+	param->luma.blend_limit_ratio = BITS_VALUE(rc, 0, 4);
+
+	rc = readl_relaxed(VCAP_VP_NR_CHROMA_CONFIG);
+	param->chroma.max_blend_ratio = BITS_VALUE(rc, 24, 4);
+	param->chroma.scale_diff_ratio = BITS_VALUE(rc, 12, 4);
+	param->chroma.diff_limit_ratio = BITS_VALUE(rc, 8, 4);
+	param->chroma.scale_motion_ratio = BITS_VALUE(rc, 4, 4);
+	param->chroma.blend_limit_ratio = BITS_VALUE(rc, 0, 4);
+}
+
+void s_default_nr_val(struct nr_param *param)
+{
+	param->window = 10;
+	param->decay_ratio = 0;
+	param->luma.max_blend_ratio = 0;
+	param->luma.scale_diff_ratio = 4;
+	param->luma.diff_limit_ratio = 1;
+	param->luma.scale_motion_ratio = 4;
+	param->luma.blend_limit_ratio = 9;
+	param->chroma.max_blend_ratio = 0;
+	param->chroma.scale_diff_ratio = 4;
+	param->chroma.diff_limit_ratio = 1;
+	param->chroma.scale_motion_ratio = 4;
+	param->chroma.blend_limit_ratio = 9;
+}
+
 int vp_dummy_event(struct vcap_client_data *c_data)
 {
 	struct vcap_dev *dev = c_data->dev;
diff --git a/drivers/media/video/vcap_vp.h b/drivers/media/video/vcap_vp.h
index 5c32903..b2b00e9 100644
--- a/drivers/media/video/vcap_vp.h
+++ b/drivers/media/video/vcap_vp.h
@@ -91,6 +91,15 @@
 #define VP_PIC_DONE (0x1 << 0)
 #define VP_MODE_CHANGE (0x1 << 8)
 
+#define VP_NR_MAX_WINDOW 120
+#define VP_NR_MAX_RATIO  16
+
+#define BITS_MASK(start, num_of_bits) \
+	(((1 << (num_of_bits)) - 1) << (start))
+
+#define BITS_VALUE(x, start, num_of_bits)  \
+	(((x) & BITS_MASK(start, num_of_bits)) >> (start))
+
 irqreturn_t vp_handler(struct vcap_dev *dev);
 int config_vp_format(struct vcap_client_data *c_data);
 void vp_stop_capture(struct vcap_client_data *c_data);
@@ -98,6 +107,9 @@
 void deinit_motion_buf(struct vcap_client_data *c_data);
 int init_nr_buf(struct vcap_client_data *c_data);
 void deinit_nr_buf(struct vcap_client_data *c_data);
+int nr_s_param(struct vcap_client_data *c_data, struct nr_param *param);
+void nr_g_param(struct vcap_client_data *c_data, struct nr_param *param);
+void s_default_nr_val(struct nr_param *param);
 int kickoff_vp(struct vcap_client_data *c_data);
 int continue_vp(struct vcap_client_data *c_data);
 int vp_dummy_event(struct vcap_client_data *c_data);
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index 2479fcf..f1d2947 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -369,7 +369,7 @@
 	struct mmc_command *cmd;
 
 	while (1) {
-		wait_for_completion(&mrq->completion);
+		wait_for_completion_io(&mrq->completion);
 
 		cmd = mrq->cmd;
 		if (!cmd->error || !cmd->retries ||
diff --git a/drivers/mmc/host/msm_sdcc.c b/drivers/mmc/host/msm_sdcc.c
index 08f5ab9..7a87ca9 100644
--- a/drivers/mmc/host/msm_sdcc.c
+++ b/drivers/mmc/host/msm_sdcc.c
@@ -3165,7 +3165,7 @@
 
 	if (host->plat->wpswitch) {
 		status = host->plat->wpswitch(mmc_dev(mmc));
-	} else if (host->plat->wpswitch_gpio) {
+	} else if (gpio_is_valid(host->plat->wpswitch_gpio)) {
 		status = gpio_request(host->plat->wpswitch_gpio,
 					"SD_WP_Switch");
 		if (status) {
@@ -3971,7 +3971,7 @@
 	struct msmsdcc_host *host = (struct msmsdcc_host *)data;
 	unsigned int status;
 
-	if (host->plat->status || host->plat->status_gpio) {
+	if (host->plat->status || gpio_is_valid(host->plat->status_gpio)) {
 		if (host->plat->status)
 			status = host->plat->status(mmc_dev(host->mmc));
 		else
@@ -5008,6 +5008,25 @@
 	return ret;
 }
 
+static void msmsdcc_dt_get_cd_wp_gpio(struct device *dev,
+		struct mmc_platform_data *pdata)
+{
+	enum of_gpio_flags flags = OF_GPIO_ACTIVE_LOW;
+	struct device_node *np = dev->of_node;
+
+	pdata->status_gpio = of_get_named_gpio_flags(np,
+			"cd-gpios", 0, &flags);
+	if (gpio_is_valid(pdata->status_gpio)) {
+		pdata->status_irq = gpio_to_irq(pdata->status_gpio);
+		pdata->is_status_gpio_active_low = flags & OF_GPIO_ACTIVE_LOW;
+	}
+
+	pdata->wpswitch_gpio = of_get_named_gpio_flags(np,
+			"wp-gpios", 0, &flags);
+	if (gpio_is_valid(pdata->wpswitch_gpio))
+		pdata->is_wpswitch_active_low = flags & OF_GPIO_ACTIVE_LOW;
+}
+
 static int msmsdcc_dt_parse_gpio_info(struct device *dev,
 		struct mmc_platform_data *pdata)
 {
@@ -5015,6 +5034,8 @@
 	struct msm_mmc_pin_data *pin_data;
 	struct device_node *np = dev->of_node;
 
+	msmsdcc_dt_get_cd_wp_gpio(dev, pdata);
+
 	pin_data = devm_kzalloc(dev, sizeof(*pin_data), GFP_KERNEL);
 	if (!pin_data) {
 		dev_err(dev, "No memory for pin_data\n");
@@ -5656,7 +5677,12 @@
 	 * Setup card detect change
 	 */
 
-	if (plat->status || plat->status_gpio) {
+	if (!plat->status_gpio)
+		plat->status_gpio = -ENOENT;
+	if (!plat->wpswitch_gpio)
+		plat->wpswitch_gpio = -ENOENT;
+
+	if (plat->status || gpio_is_valid(plat->status_gpio)) {
 		if (plat->status)
 			host->oldstat = plat->status(mmc_dev(host->mmc));
 		else
diff --git a/drivers/mtd/devices/Kconfig b/drivers/mtd/devices/Kconfig
index 21f146f..bc05764 100644
--- a/drivers/mtd/devices/Kconfig
+++ b/drivers/mtd/devices/Kconfig
@@ -60,6 +60,19 @@
 	help
 	  Support for some NAND chips connected to the MSM NAND controller.
 
+config MTD_MSM_QPIC_NAND
+	tristate "MSM QPIC NAND Device Support"
+	depends on MTD && ARCH_MSM && !MTD_MSM_NAND
+	select CRC16
+	select BITREVERSE
+	select MTD_NAND_IDS
+	default n
+	help
+	  Support for NAND controller in Qualcomm Parallel Interface
+	  controller (QPIC). This new controller supports BAM mode
+	  and BCH error correction mechanism. Based on the device
+	  capabilities either 4 bit or 8 bit BCH ECC will be used.
+
 config MTD_DATAFLASH
 	tristate "Support for AT45xxx DataFlash"
 	depends on SPI_MASTER && EXPERIMENTAL
diff --git a/drivers/mtd/devices/Makefile b/drivers/mtd/devices/Makefile
index 8497c5f..9fdd004 100644
--- a/drivers/mtd/devices/Makefile
+++ b/drivers/mtd/devices/Makefile
@@ -13,6 +13,7 @@
 obj-$(CONFIG_MTD_PMC551)	+= pmc551.o
 obj-$(CONFIG_MTD_MS02NV)	+= ms02-nv.o
 obj-$(CONFIG_MTD_MSM_NAND)	+= msm_nand.o
+obj-$(CONFIG_MTD_MSM_QPIC_NAND)	+= msm_qpic_nand.o
 obj-$(CONFIG_MTD_MTDRAM)	+= mtdram.o
 obj-$(CONFIG_MTD_LART)		+= lart.o
 obj-$(CONFIG_MTD_BLOCK2MTD)	+= block2mtd.o
diff --git a/drivers/mtd/devices/msm_qpic_nand.c b/drivers/mtd/devices/msm_qpic_nand.c
new file mode 100644
index 0000000..d709e17
--- /dev/null
+++ b/drivers/mtd/devices/msm_qpic_nand.c
@@ -0,0 +1,2500 @@
+/*
+ * Copyright (C) 2007 Google, Inc.
+ * Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License 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/slab.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/partitions.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/io.h>
+#include <linux/crc16.h>
+#include <linux/bitrev.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <mach/sps.h>
+
+#define PAGE_SIZE_2K 2048
+#define PAGE_SIZE_4K 4096
+#define WRITE 1
+#define READ 0
+/*
+ * The maximum no of descriptors per transfer (page read/write) won't be more
+ * than 64. For more details on what those commands are, please refer to the
+ * page read and page write functions in the driver.
+ */
+#define SPS_MAX_DESC_NUM 64
+#define SPS_DATA_CONS_PIPE_INDEX 0
+#define SPS_DATA_PROD_PIPE_INDEX 1
+#define SPS_CMD_CONS_PIPE_INDEX 2
+
+#define msm_virt_to_dma(chip, vaddr) \
+	((chip)->dma_phys_addr + \
+	((uint8_t *)(vaddr) - (chip)->dma_virt_addr))
+
+/*
+ * A single page read/write request would typically need DMA memory of about
+ * 1K memory approximately. So for a single request this memory is more than
+ * enough.
+ *
+ * But to accommodate multiple clients we allocate 8K of memory. Though only
+ * one client request can be submitted to NANDc at any time, other clients can
+ * still prepare the descriptors while waiting for current client request to
+ * be done. Thus for a total memory of 8K, the driver can currently support
+ * maximum clients up to 7 or 8 at a time. The client for which there is no
+ * free DMA memory shall wait on the wait queue until other clients free up
+ * the required memory.
+ */
+#define MSM_NAND_DMA_BUFFER_SIZE SZ_8K
+/*
+ * This defines the granularity at which the buffer management is done. The
+ * total number of slots is based on the size of the atomic_t variable
+ * dma_buffer_busy(number of bits) within the structure msm_nand_chip.
+ */
+#define MSM_NAND_DMA_BUFFER_SLOT_SZ \
+	(MSM_NAND_DMA_BUFFER_SIZE / (sizeof(((atomic_t *)0)->counter) * 8))
+
+/* ONFI(Open NAND Flash Interface) parameters */
+#define MSM_NAND_CFG0_RAW_ONFI_IDENTIFIER 0x88000800
+#define MSM_NAND_CFG0_RAW_ONFI_PARAM_INFO 0x88040000
+#define MSM_NAND_CFG1_RAW_ONFI_IDENTIFIER 0x0005045d
+#define MSM_NAND_CFG1_RAW_ONFI_PARAM_INFO 0x0005045d
+#define ONFI_PARAM_INFO_LENGTH 0x0200
+#define ONFI_PARAM_PAGE_LENGTH 0x0100
+#define ONFI_PARAMETER_PAGE_SIGNATURE 0x49464E4F
+#define FLASH_READ_ONFI_SIGNATURE_ADDRESS 0x20
+#define FLASH_READ_ONFI_PARAMETERS_COMMAND 0xEC
+#define FLASH_READ_ONFI_PARAMETERS_ADDRESS 0x00
+#define FLASH_READ_DEVICE_ID_ADDRESS 0x00
+
+#define MSM_NAND_RESET_FLASH_STS 0x00000020
+#define MSM_NAND_RESET_READ_STS 0x000000C0
+
+/* QPIC NANDc (NAND Controller) Register Set */
+#define MSM_NAND_REG(info, off)		    (info->nand_phys + off)
+#define MSM_NAND_FLASH_CMD(info)	    MSM_NAND_REG(info, 0x30000)
+#define MSM_NAND_ADDR0(info)                MSM_NAND_REG(info, 0x30004)
+#define MSM_NAND_ADDR1(info)                MSM_NAND_REG(info, 0x30008)
+#define MSM_NAND_EXEC_CMD(info)             MSM_NAND_REG(info, 0x30010)
+#define MSM_NAND_FLASH_STATUS(info)         MSM_NAND_REG(info, 0x30014)
+#define FS_OP_ERR (1 << 4)
+#define FS_MPU_ERR (1 << 8)
+#define FS_DEVICE_STS_ERR (1 << 16)
+#define FS_DEVICE_WP (1 << 23)
+
+#define MSM_NAND_BUFFER_STATUS(info)        MSM_NAND_REG(info, 0x30018)
+#define BS_UNCORRECTABLE_BIT (1 << 8)
+#define BS_CORRECTABLE_ERR_MSK 0x1F
+
+#define MSM_NAND_DEV0_CFG0(info)            MSM_NAND_REG(info, 0x30020)
+#define DISABLE_STATUS_AFTER_WRITE 4
+#define CW_PER_PAGE	6
+#define UD_SIZE_BYTES	9
+#define SPARE_SIZE_BYTES 23
+#define NUM_ADDR_CYCLES	27
+
+#define MSM_NAND_DEV0_CFG1(info)            MSM_NAND_REG(info, 0x30024)
+#define DEV0_CFG1_ECC_DISABLE	0
+#define WIDE_FLASH		1
+#define NAND_RECOVERY_CYCLES	2
+#define CS_ACTIVE_BSY		5
+#define BAD_BLOCK_BYTE_NUM	6
+#define BAD_BLOCK_IN_SPARE_AREA 16
+#define WR_RD_BSY_GAP		17
+#define ENABLE_BCH_ECC		27
+
+#define MSM_NAND_DEV0_ECC_CFG(info)	    MSM_NAND_REG(info, 0x30028)
+#define ECC_CFG_ECC_DISABLE	0
+#define ECC_SW_RESET	1
+#define ECC_MODE	4
+#define ECC_PARITY_SIZE_BYTES 8
+#define ECC_NUM_DATA_BYTES 16
+#define ECC_FORCE_CLK_OPEN 30
+
+#define MSM_NAND_READ_ID(info)              MSM_NAND_REG(info, 0x30040)
+#define MSM_NAND_READ_STATUS(info)          MSM_NAND_REG(info, 0x30044)
+#define MSM_NAND_DEV_CMD1(info)             MSM_NAND_REG(info, 0x300A4)
+#define MSM_NAND_DEV_CMD_VLD(info)          MSM_NAND_REG(info, 0x300AC)
+#define MSM_NAND_EBI2_ECC_BUF_CFG(info)     MSM_NAND_REG(info, 0x300F0)
+#define MSM_NAND_ERASED_CW_DETECT_CFG(info)	MSM_NAND_REG(info, 0x300E8)
+#define MSM_NAND_ERASED_CW_DETECT_STATUS(info)  MSM_NAND_REG(info, 0x300EC)
+
+#define MSM_NAND_CTRL(info)		    MSM_NAND_REG(info, 0x30F00)
+#define BAM_MODE_EN	0
+
+#define MSM_NAND_READ_LOCATION_0(info)      MSM_NAND_REG(info, 0x30F20)
+#define MSM_NAND_READ_LOCATION_1(info)      MSM_NAND_REG(info, 0x30F24)
+
+/* device commands */
+#define MSM_NAND_CMD_PAGE_READ          0x32
+#define MSM_NAND_CMD_PAGE_READ_ECC      0x33
+#define MSM_NAND_CMD_PAGE_READ_ALL      0x34
+#define MSM_NAND_CMD_PRG_PAGE           0x36
+#define MSM_NAND_CMD_PRG_PAGE_ECC       0x37
+#define MSM_NAND_CMD_PRG_PAGE_ALL       0x39
+#define MSM_NAND_CMD_BLOCK_ERASE        0x3A
+#define MSM_NAND_CMD_FETCH_ID           0x0B
+
+/* Structure that defines a NAND SPS command element */
+struct msm_nand_sps_cmd {
+	struct sps_command_element ce;
+	uint32_t flags;
+};
+
+/*
+ * Structure that defines the NAND controller properties as per the
+ * NAND flash device/chip that is attached.
+ */
+struct msm_nand_chip {
+	struct device *dev;
+	/*
+	 * DMA memory will be allocated only once during probe and this memory
+	 * will be used by all NAND clients. This wait queue is needed to
+	 * make the applications wait for DMA memory to be free'd when the
+	 * complete memory is exhausted.
+	 */
+	wait_queue_head_t dma_wait_queue;
+	atomic_t dma_buffer_busy;
+	uint8_t *dma_virt_addr;
+	dma_addr_t dma_phys_addr;
+	uint32_t ecc_parity_bytes;
+	uint32_t bch_caps; /* Controller BCH ECC capabilities */
+#define MSM_NAND_CAP_4_BIT_BCH      (1 << 0)
+#define MSM_NAND_CAP_8_BIT_BCH      (1 << 1)
+	uint32_t cw_size;
+	/* NANDc register configurations */
+	uint32_t cfg0, cfg1, cfg0_raw, cfg1_raw;
+	uint32_t ecc_buf_cfg;
+	uint32_t ecc_bch_cfg;
+};
+
+/* Structure that defines an SPS end point for a NANDc BAM pipe. */
+struct msm_nand_sps_endpt {
+	struct sps_pipe *handle;
+	struct sps_connect config;
+	struct sps_register_event event;
+	struct completion completion;
+};
+
+/*
+ * Structure that defines NANDc SPS data - BAM handle and an end point
+ * for each BAM pipe.
+ */
+struct msm_nand_sps_info {
+	uint32_t bam_handle;
+	struct msm_nand_sps_endpt data_prod;
+	struct msm_nand_sps_endpt data_cons;
+	struct msm_nand_sps_endpt cmd_pipe;
+};
+
+/*
+ * Structure that contains flash device information. This gets updated after
+ * the NAND flash device detection.
+ */
+struct flash_identification {
+	uint32_t flash_id;
+	uint32_t density;
+	uint32_t widebus;
+	uint32_t pagesize;
+	uint32_t blksize;
+	uint32_t oobsize;
+	uint32_t ecc_correctability;
+};
+
+/* Structure that defines NANDc private data. */
+struct msm_nand_info {
+	struct mtd_info		mtd;
+	struct msm_nand_chip	nand_chip;
+	struct msm_nand_sps_info sps;
+	unsigned long bam_phys;
+	unsigned long nand_phys;
+	void __iomem *bam_base;
+	int bam_irq;
+	/*
+	 * This lock must be acquired before submitting any command or data
+	 * descriptors to BAM pipes and must be held until all the submitted
+	 * descriptors are processed.
+	 *
+	 * This is required to ensure that both command and descriptors are
+	 * submitted atomically without interruption from other clients,
+	 * when there are requests from more than client at any time.
+	 * Othewise, data and command descriptors can be submitted out of
+	 * order for a request which can cause data corruption.
+	 */
+	struct mutex bam_lock;
+	struct flash_identification flash_dev;
+};
+
+/* Structure that defines an ONFI parameter page (512B) */
+struct onfi_param_page {
+	uint32_t parameter_page_signature;
+	uint16_t revision_number;
+	uint16_t features_supported;
+	uint16_t optional_commands_supported;
+	uint8_t  reserved0[22];
+	uint8_t  device_manufacturer[12];
+	uint8_t  device_model[20];
+	uint8_t  jedec_manufacturer_id;
+	uint16_t date_code;
+	uint8_t  reserved1[13];
+	uint32_t number_of_data_bytes_per_page;
+	uint16_t number_of_spare_bytes_per_page;
+	uint32_t number_of_data_bytes_per_partial_page;
+	uint16_t number_of_spare_bytes_per_partial_page;
+	uint32_t number_of_pages_per_block;
+	uint32_t number_of_blocks_per_logical_unit;
+	uint8_t  number_of_logical_units;
+	uint8_t  number_of_address_cycles;
+	uint8_t  number_of_bits_per_cell;
+	uint16_t maximum_bad_blocks_per_logical_unit;
+	uint16_t block_endurance;
+	uint8_t  guaranteed_valid_begin_blocks;
+	uint16_t guaranteed_valid_begin_blocks_endurance;
+	uint8_t  number_of_programs_per_page;
+	uint8_t  partial_program_attributes;
+	uint8_t  number_of_bits_ecc_correctability;
+	uint8_t  number_of_interleaved_address_bits;
+	uint8_t  interleaved_operation_attributes;
+	uint8_t  reserved2[13];
+	uint8_t  io_pin_capacitance;
+	uint16_t timing_mode_support;
+	uint16_t program_cache_timing_mode_support;
+	uint16_t maximum_page_programming_time;
+	uint16_t maximum_block_erase_time;
+	uint16_t maximum_page_read_time;
+	uint16_t maximum_change_column_setup_time;
+	uint8_t  reserved3[23];
+	uint16_t vendor_specific_revision_number;
+	uint8_t  vendor_specific[88];
+	uint16_t integrity_crc;
+} __attribute__((__packed__));
+
+/*
+ * Get the DMA memory for requested amount of size. It returns the pointer
+ * to free memory available from the allocated pool. Returns NULL if there
+ * is no free memory.
+ */
+static void *msm_nand_get_dma_buffer(struct msm_nand_chip *chip, size_t size)
+{
+	uint32_t bitmask, free_bitmask, old_bitmask;
+	uint32_t need_mask, current_need_mask;
+	int free_index;
+
+	need_mask = (1UL << DIV_ROUND_UP(size, MSM_NAND_DMA_BUFFER_SLOT_SZ))
+			- 1;
+	bitmask = atomic_read(&chip->dma_buffer_busy);
+	free_bitmask = ~bitmask;
+	do {
+		free_index = __ffs(free_bitmask);
+		current_need_mask = need_mask << free_index;
+
+		if (size + free_index * MSM_NAND_DMA_BUFFER_SLOT_SZ >=
+						 MSM_NAND_DMA_BUFFER_SIZE)
+			return NULL;
+
+		if ((bitmask & current_need_mask) == 0) {
+			old_bitmask =
+				atomic_cmpxchg(&chip->dma_buffer_busy,
+					       bitmask,
+					       bitmask | current_need_mask);
+			if (old_bitmask == bitmask)
+				return chip->dma_virt_addr +
+				free_index * MSM_NAND_DMA_BUFFER_SLOT_SZ;
+			free_bitmask = 0;/* force return */
+		}
+		/* current free range was too small, clear all free bits */
+		/* below the top busy bit within current_need_mask */
+		free_bitmask &=
+			~(~0U >> (32 - fls(bitmask & current_need_mask)));
+	} while (free_bitmask);
+
+	return NULL;
+}
+
+/*
+ * Releases the DMA memory used to the free pool and also wakes up any user
+ * thread waiting on wait queue for free memory to be available.
+ */
+static void msm_nand_release_dma_buffer(struct msm_nand_chip *chip,
+					void *buffer, size_t size)
+{
+	int index;
+	uint32_t used_mask;
+
+	used_mask = (1UL << DIV_ROUND_UP(size, MSM_NAND_DMA_BUFFER_SLOT_SZ))
+			- 1;
+	index = ((uint8_t *)buffer - chip->dma_virt_addr) /
+		MSM_NAND_DMA_BUFFER_SLOT_SZ;
+	atomic_sub(used_mask << index, &chip->dma_buffer_busy);
+
+	wake_up(&chip->dma_wait_queue);
+}
+
+/*
+ * Calculates page address of the buffer passed, offset of buffer within
+ * that page and then maps it for DMA by calling dma_map_page().
+ */
+static dma_addr_t msm_nand_dma_map(struct device *dev, void *addr, size_t size,
+					 enum dma_data_direction dir)
+{
+	struct page *page;
+	unsigned long offset = (unsigned long)addr & ~PAGE_MASK;
+	if (virt_addr_valid(addr))
+		page = virt_to_page(addr);
+	else {
+		if (WARN_ON(size + offset > PAGE_SIZE))
+			return ~0;
+		page = vmalloc_to_page(addr);
+	}
+	return dma_map_page(dev, page, offset, size, dir);
+}
+
+/*
+ * Wrapper function to prepare a SPS command element with the data that is
+ * passed to this function.
+ *
+ * Since for any command element it is a must to have this flag
+ * SPS_IOVEC_FLAG_CMD, this function by default updates this flag for a
+ * command element that is passed and thus, the caller need not explicilty
+ * pass this flag. The other flags must be passed based on the need.  If a
+ * command element doesn't have any other flag, then 0 can be passed to flags.
+ */
+static inline void msm_nand_prep_ce(struct msm_nand_sps_cmd *sps_cmd,
+				uint32_t addr, uint32_t command,
+				uint32_t data, uint32_t flags)
+{
+	struct sps_command_element *cmd = &sps_cmd->ce;
+
+	cmd->addr = addr;
+	cmd->command = (command & WRITE) ? (uint32_t) SPS_WRITE_COMMAND :
+			(uint32_t) SPS_READ_COMMAND;
+	cmd->data = data;
+	cmd->mask = 0xFFFFFFFF;
+	sps_cmd->flags = SPS_IOVEC_FLAG_CMD | flags;
+}
+
+/*
+ * Read a single NANDc register as mentioned by its parameter addr. The return
+ * value indicates whether read is successful or not. The register value read
+ * is stored in val.
+ */
+static int msm_nand_flash_rd_reg(struct msm_nand_info *info, uint32_t addr,
+				uint32_t *val)
+{
+	int ret = 0;
+	struct msm_nand_sps_cmd *cmd;
+	struct msm_nand_chip *chip = &info->nand_chip;
+	struct {
+		struct msm_nand_sps_cmd cmd;
+		uint32_t data;
+	} *dma_buffer;
+
+	wait_event(chip->dma_wait_queue, (dma_buffer = msm_nand_get_dma_buffer(
+		    chip, sizeof(*dma_buffer))));
+	cmd = &dma_buffer->cmd;
+	msm_nand_prep_ce(cmd, addr, READ, msm_virt_to_dma(chip,
+			&dma_buffer->data), SPS_IOVEC_FLAG_INT);
+
+	ret = sps_transfer_one(info->sps.cmd_pipe.handle,
+			msm_virt_to_dma(chip, &cmd->ce),
+			sizeof(struct sps_command_element), NULL, cmd->flags);
+	if (ret) {
+		pr_err("failed to submit command %x ret %d\n", addr, ret);
+		goto out;
+	}
+	wait_for_completion_io(&info->sps.cmd_pipe.completion);
+	*val = dma_buffer->data;
+out:
+	msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer));
+	return ret;
+}
+
+/*
+ * Read the Flash ID from the Nand Flash Device. The return value < 0
+ * indicates failure. When successful, the Flash ID is stored in parameter
+ * read_id.
+ */
+static int msm_nand_flash_read_id(struct msm_nand_info *info,
+		bool read_onfi_signature,
+		uint32_t *read_id)
+{
+	int err = 0, i;
+	struct msm_nand_sps_cmd *cmd;
+	struct sps_iovec *iovec;
+	struct msm_nand_chip *chip = &info->nand_chip;
+	uint32_t total_cnt = 4;
+	/*
+	 * The following 4 commands are required to read id -
+	 * write commands - addr0, flash, exec
+	 * read_commands - read_id
+	 */
+	struct {
+		struct sps_transfer xfer;
+		struct sps_iovec cmd_iovec[total_cnt];
+		struct msm_nand_sps_cmd cmd[total_cnt];
+		uint32_t data[total_cnt];
+	} *dma_buffer;
+
+	wait_event(chip->dma_wait_queue, (dma_buffer = msm_nand_get_dma_buffer
+				(chip, sizeof(*dma_buffer))));
+	if (read_onfi_signature)
+		dma_buffer->data[0] = FLASH_READ_ONFI_SIGNATURE_ADDRESS;
+	else
+		dma_buffer->data[0] = FLASH_READ_DEVICE_ID_ADDRESS;
+
+	dma_buffer->data[1] = MSM_NAND_CMD_FETCH_ID;
+	dma_buffer->data[2] = 1;
+	dma_buffer->data[3] = 0xeeeeeeee;
+
+	cmd = dma_buffer->cmd;
+	msm_nand_prep_ce(cmd, MSM_NAND_ADDR0(info), WRITE,
+			dma_buffer->data[0], SPS_IOVEC_FLAG_LOCK);
+	cmd++;
+
+	msm_nand_prep_ce(cmd, MSM_NAND_FLASH_CMD(info), WRITE,
+			dma_buffer->data[1], 0);
+	cmd++;
+
+	msm_nand_prep_ce(cmd, MSM_NAND_EXEC_CMD(info), WRITE,
+			dma_buffer->data[2], SPS_IOVEC_FLAG_NWD);
+	cmd++;
+
+	msm_nand_prep_ce(cmd, MSM_NAND_READ_ID(info), READ,
+		msm_virt_to_dma(chip, &dma_buffer->data[3]),
+		SPS_IOVEC_FLAG_UNLOCK | SPS_IOVEC_FLAG_INT);
+	cmd++;
+
+	BUG_ON(cmd - dma_buffer->cmd > ARRAY_SIZE(dma_buffer->cmd));
+	dma_buffer->xfer.iovec_count = (cmd - dma_buffer->cmd);
+	dma_buffer->xfer.iovec = dma_buffer->cmd_iovec;
+	dma_buffer->xfer.iovec_phys = msm_virt_to_dma(chip,
+					&dma_buffer->cmd_iovec);
+	iovec = dma_buffer->xfer.iovec;
+
+	for (i = 0; i < dma_buffer->xfer.iovec_count; i++) {
+		iovec->addr =  msm_virt_to_dma(chip, &dma_buffer->cmd[i].ce);
+		iovec->size = sizeof(struct sps_command_element);
+		iovec->flags = dma_buffer->cmd[i].flags;
+		iovec++;
+	}
+
+	mutex_lock(&info->bam_lock);
+	err =  sps_transfer(info->sps.cmd_pipe.handle, &dma_buffer->xfer);
+	if (err) {
+		pr_err("Failed to submit commands %d\n", err);
+		mutex_unlock(&info->bam_lock);
+		goto out;
+	}
+	wait_for_completion_io(&info->sps.cmd_pipe.completion);
+	mutex_unlock(&info->bam_lock);
+
+	pr_debug("Read ID register value 0x%x\n", dma_buffer->data[3]);
+	if (!read_onfi_signature)
+		pr_debug("nandid: %x maker %02x device %02x\n",
+		       dma_buffer->data[3], dma_buffer->data[3] & 0xff,
+		       (dma_buffer->data[3] >> 8) & 0xff);
+	*read_id = dma_buffer->data[3];
+out:
+	msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer));
+	return err;
+}
+
+/*
+ * Contains data for common configuration registers that must be programmed
+ * for every NANDc operation.
+ */
+struct msm_nand_common_cfgs {
+	uint32_t cmd;
+	uint32_t addr0;
+	uint32_t addr1;
+	uint32_t cfg0;
+	uint32_t cfg1;
+};
+
+/*
+ * Function to prepare SPS command elements to write into NANDc configuration
+ * registers as per the data defined in struct msm_nand_common_cfgs. This is
+ * required for the following NANDc operations - Erase, Bad Block checking
+ * and for reading ONFI parameter page.
+ */
+static void msm_nand_prep_cfg_cmd_desc(struct msm_nand_info *info,
+				struct msm_nand_common_cfgs data,
+				struct msm_nand_sps_cmd **curr_cmd)
+{
+	struct msm_nand_sps_cmd *cmd;
+
+	cmd = *curr_cmd;
+	msm_nand_prep_ce(cmd, MSM_NAND_FLASH_CMD(info), WRITE, data.cmd,
+			SPS_IOVEC_FLAG_LOCK);
+	cmd++;
+
+	msm_nand_prep_ce(cmd, MSM_NAND_ADDR0(info), WRITE, data.addr0, 0);
+	cmd++;
+
+	msm_nand_prep_ce(cmd, MSM_NAND_ADDR1(info), WRITE, data.addr1, 0);
+	cmd++;
+
+	msm_nand_prep_ce(cmd, MSM_NAND_DEV0_CFG0(info), WRITE, data.cfg0, 0);
+	cmd++;
+
+	msm_nand_prep_ce(cmd, MSM_NAND_DEV0_CFG1(info), WRITE, data.cfg1, 0);
+	cmd++;
+	*curr_cmd = cmd;
+}
+
+/*
+ * Function to check the CRC integrity check on ONFI parameter page read.
+ * For ONFI parameter page read, the controller ECC will be disabled. Hence,
+ * it is mandatory to manually compute CRC and check it against the value
+ * stored within ONFI page.
+ */
+static uint16_t msm_nand_flash_onfi_crc_check(uint8_t *buffer, uint16_t count)
+{
+	int i;
+	uint16_t result;
+
+	for (i = 0; i < count; i++)
+		buffer[i] = bitrev8(buffer[i]);
+
+	result = bitrev16(crc16(bitrev16(0x4f4e), buffer, count));
+
+	for (i = 0; i < count; i++)
+		buffer[i] = bitrev8(buffer[i]);
+
+	return result;
+}
+
+/*
+ * Structure that contains NANDc register data for commands required
+ * for reading ONFI paramter page.
+ */
+struct msm_nand_flash_onfi_data {
+	struct msm_nand_common_cfgs cfg;
+	uint32_t exec;
+	uint32_t devcmd1_orig;
+	uint32_t devcmdvld_orig;
+	uint32_t devcmd1_mod;
+	uint32_t devcmdvld_mod;
+	uint32_t ecc_bch_cfg;
+};
+
+/*
+ * Function to identify whether the attached NAND flash device is
+ * complaint to ONFI spec or not. If yes, then it reads the ONFI parameter
+ * page to get the device parameters.
+ */
+static int msm_nand_flash_onfi_probe(struct msm_nand_info *info)
+{
+	struct msm_nand_chip *chip = &info->nand_chip;
+	struct flash_identification *flash = &info->flash_dev;
+	uint32_t crc_chk_count = 0, page_address = 0;
+	int ret = 0, i;
+
+	/* SPS parameters */
+	struct msm_nand_sps_cmd *cmd, *curr_cmd;
+	struct sps_iovec *iovec;
+	uint32_t rdata;
+
+	/* ONFI Identifier/Parameter Page parameters */
+	uint8_t *onfi_param_info_buf = NULL;
+	dma_addr_t dma_addr_param_info = 0;
+	struct onfi_param_page *onfi_param_page_ptr;
+	struct msm_nand_flash_onfi_data data;
+	uint32_t onfi_signature;
+
+	/* SPS command/data descriptors */
+	uint32_t total_cnt = 13;
+	/*
+	 * The following 13 commands are required to get onfi parameters -
+	 * flash, addr0, addr1, cfg0, cfg1, dev0_ecc_cfg, cmd_vld, dev_cmd1,
+	 * read_loc_0, exec, flash_status (read cmd), dev_cmd1, cmd_vld.
+	 */
+	struct {
+		struct sps_transfer xfer;
+		struct sps_iovec cmd_iovec[total_cnt];
+		struct msm_nand_sps_cmd cmd[total_cnt];
+		uint32_t flash_status;
+	} *dma_buffer;
+
+	wait_event(chip->dma_wait_queue, (onfi_param_info_buf =
+		msm_nand_get_dma_buffer(chip, ONFI_PARAM_INFO_LENGTH)));
+	dma_addr_param_info = msm_virt_to_dma(chip, onfi_param_info_buf);
+
+	wait_event(chip->dma_wait_queue, (dma_buffer = msm_nand_get_dma_buffer
+				(chip, sizeof(*dma_buffer))));
+
+	ret = msm_nand_flash_read_id(info, 1, &onfi_signature);
+	if (ret < 0) {
+		pr_err("Failed to read ONFI signature\n");
+		goto free_dma;
+	}
+	if (onfi_signature != ONFI_PARAMETER_PAGE_SIGNATURE) {
+		pr_info("Found a non ONFI device\n");
+		ret = -EIO;
+		goto free_dma;
+	}
+
+	memset(&data, 0, sizeof(struct msm_nand_flash_onfi_data));
+	ret = msm_nand_flash_rd_reg(info, MSM_NAND_DEV_CMD1(info),
+				&data.devcmd1_orig);
+	if (ret < 0)
+		goto free_dma;
+	ret = msm_nand_flash_rd_reg(info, MSM_NAND_DEV_CMD_VLD(info),
+			&data.devcmdvld_orig);
+	if (ret < 0)
+		goto free_dma;
+
+	data.cfg.cmd = MSM_NAND_CMD_PAGE_READ_ALL;
+	data.exec = 1;
+	data.cfg.addr0 = (page_address << 16) |
+				FLASH_READ_ONFI_PARAMETERS_ADDRESS;
+	data.cfg.addr1 = (page_address >> 16) & 0xFF;
+	data.cfg.cfg0 =	MSM_NAND_CFG0_RAW_ONFI_PARAM_INFO;
+	data.cfg.cfg1 = MSM_NAND_CFG1_RAW_ONFI_PARAM_INFO;
+	data.devcmd1_mod = (data.devcmd1_orig & 0xFFFFFF00) |
+				FLASH_READ_ONFI_PARAMETERS_COMMAND;
+	data.devcmdvld_mod = data.devcmdvld_orig & 0xFFFFFFFE;
+	data.ecc_bch_cfg = 1 << ECC_CFG_ECC_DISABLE;
+	dma_buffer->flash_status = 0xeeeeeeee;
+
+	curr_cmd = cmd = dma_buffer->cmd;
+	msm_nand_prep_cfg_cmd_desc(info, data.cfg, &curr_cmd);
+
+	cmd = curr_cmd;
+	msm_nand_prep_ce(cmd, MSM_NAND_DEV0_ECC_CFG(info), WRITE,
+			data.ecc_bch_cfg, 0);
+	cmd++;
+
+	msm_nand_prep_ce(cmd, MSM_NAND_DEV_CMD_VLD(info), WRITE,
+			data.devcmdvld_mod, 0);
+	cmd++;
+
+	msm_nand_prep_ce(cmd, MSM_NAND_DEV_CMD1(info), WRITE,
+			data.devcmd1_mod, 0);
+	cmd++;
+
+	rdata = (0 << 0) | (ONFI_PARAM_INFO_LENGTH << 16) | (1 << 31);
+	msm_nand_prep_ce(cmd, MSM_NAND_READ_LOCATION_0(info), WRITE,
+			rdata, 0);
+	cmd++;
+
+	msm_nand_prep_ce(cmd, MSM_NAND_EXEC_CMD(info), WRITE,
+		data.exec, SPS_IOVEC_FLAG_NWD);
+	cmd++;
+
+	msm_nand_prep_ce(cmd, MSM_NAND_FLASH_STATUS(info), READ,
+		msm_virt_to_dma(chip, &dma_buffer->flash_status), 0);
+	cmd++;
+
+	msm_nand_prep_ce(cmd, MSM_NAND_DEV_CMD1(info), WRITE,
+			data.devcmd1_orig, 0);
+	cmd++;
+
+	msm_nand_prep_ce(cmd, MSM_NAND_DEV_CMD_VLD(info), WRITE,
+			data.devcmdvld_orig,
+			SPS_IOVEC_FLAG_UNLOCK | SPS_IOVEC_FLAG_INT);
+	cmd++;
+
+	BUG_ON(cmd - dma_buffer->cmd > ARRAY_SIZE(dma_buffer->cmd));
+	dma_buffer->xfer.iovec_count = (cmd - dma_buffer->cmd);
+	dma_buffer->xfer.iovec = dma_buffer->cmd_iovec;
+	dma_buffer->xfer.iovec_phys = msm_virt_to_dma(chip,
+					&dma_buffer->cmd_iovec);
+	iovec = dma_buffer->xfer.iovec;
+
+	for (i = 0; i < dma_buffer->xfer.iovec_count; i++) {
+		iovec->addr =  msm_virt_to_dma(chip,
+				&dma_buffer->cmd[i].ce);
+		iovec->size = sizeof(struct sps_command_element);
+		iovec->flags = dma_buffer->cmd[i].flags;
+		iovec++;
+	}
+	mutex_lock(&info->bam_lock);
+	/* Submit data descriptor */
+	ret = sps_transfer_one(info->sps.data_prod.handle, dma_addr_param_info,
+			ONFI_PARAM_INFO_LENGTH, NULL, SPS_IOVEC_FLAG_INT);
+	if (ret) {
+		pr_err("Failed to submit data descriptors %d\n", ret);
+		mutex_unlock(&info->bam_lock);
+		goto free_dma;
+	}
+	/* Submit command descriptors */
+	ret =  sps_transfer(info->sps.cmd_pipe.handle,
+			&dma_buffer->xfer);
+	if (ret) {
+		pr_err("Failed to submit commands %d\n", ret);
+		mutex_unlock(&info->bam_lock);
+		goto free_dma;
+	}
+	wait_for_completion_io(&info->sps.cmd_pipe.completion);
+	wait_for_completion_io(&info->sps.data_prod.completion);
+	mutex_unlock(&info->bam_lock);
+
+	/* Check for flash status errors */
+	if (dma_buffer->flash_status & (FS_OP_ERR | FS_MPU_ERR)) {
+		pr_err("MPU/OP err (0x%x) is set\n", dma_buffer->flash_status);
+		ret = -EIO;
+		goto free_dma;
+	}
+
+	for (crc_chk_count = 0; crc_chk_count < ONFI_PARAM_INFO_LENGTH
+			/ ONFI_PARAM_PAGE_LENGTH; crc_chk_count++) {
+		onfi_param_page_ptr =
+			(struct onfi_param_page *)
+			(&(onfi_param_info_buf
+			[ONFI_PARAM_PAGE_LENGTH *
+			crc_chk_count]));
+		if (msm_nand_flash_onfi_crc_check(
+			(uint8_t *)onfi_param_page_ptr,
+			ONFI_PARAM_PAGE_LENGTH - 2) ==
+			onfi_param_page_ptr->integrity_crc) {
+			break;
+		}
+	}
+	if (crc_chk_count >= ONFI_PARAM_INFO_LENGTH
+			/ ONFI_PARAM_PAGE_LENGTH) {
+		pr_err("CRC Check failed on param page\n");
+		ret = -EIO;
+		goto free_dma;
+	}
+	ret = msm_nand_flash_read_id(info, 0, &flash->flash_id);
+	if (ret < 0) {
+		pr_err("Failed to read flash ID\n");
+		goto free_dma;
+	}
+	flash->widebus  = onfi_param_page_ptr->features_supported & 0x01;
+	flash->pagesize = onfi_param_page_ptr->number_of_data_bytes_per_page;
+	flash->blksize  = onfi_param_page_ptr->number_of_pages_per_block *
+					flash->pagesize;
+	flash->oobsize  = onfi_param_page_ptr->number_of_spare_bytes_per_page;
+	flash->density  = onfi_param_page_ptr->number_of_blocks_per_logical_unit
+					* flash->blksize;
+	flash->ecc_correctability = onfi_param_page_ptr->
+					number_of_bits_ecc_correctability;
+
+	pr_info("Found an ONFI compliant device %s\n",
+			onfi_param_page_ptr->device_model);
+	/*
+	 * Temporary hack for MT29F4G08ABC device.
+	 * Since the device is not properly adhering
+	 * to ONFi specification it is reporting
+	 * as 16 bit device though it is 8 bit device!!!
+	 */
+	if (!strncmp(onfi_param_page_ptr->device_model, "MT29F4G08ABC", 12))
+		flash->widebus  = 0;
+free_dma:
+	msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer));
+	msm_nand_release_dma_buffer(chip, onfi_param_info_buf,
+			ONFI_PARAM_INFO_LENGTH);
+	return ret;
+}
+
+/*
+ * Structure that contains read/write parameters required for reading/writing
+ * from/to a page.
+ */
+struct msm_nand_rw_params {
+	uint32_t page;
+	uint32_t page_count;
+	uint32_t sectordatasize;
+	uint32_t sectoroobsize;
+	uint32_t cwperpage;
+	uint32_t oob_len_cmd;
+	uint32_t oob_len_data;
+	uint32_t start_sector;
+	uint32_t oob_col;
+	dma_addr_t data_dma_addr;
+	dma_addr_t oob_dma_addr;
+	dma_addr_t data_dma_addr_curr;
+	dma_addr_t oob_dma_addr_curr;
+	bool read;
+};
+
+/*
+ * Structure that contains NANDc register data required for reading/writing
+ * from/to a page.
+ */
+struct msm_nand_rw_reg_data {
+	uint32_t cmd;
+	uint32_t addr0;
+	uint32_t addr1;
+	uint32_t cfg0;
+	uint32_t cfg1;
+	uint32_t ecc_bch_cfg;
+	uint32_t exec;
+	uint32_t ecc_cfg;
+	uint32_t clrfstatus;
+	uint32_t clrrstatus;
+};
+
+/*
+ * Function that validates page read/write MTD parameters received from upper
+ * layers such as MTD/YAFFS2 and returns error for any unsupported operations
+ * by the driver. In case of success, it also maps the data and oob buffer
+ * received for DMA.
+ */
+static int msm_nand_validate_mtd_params(struct mtd_info *mtd, bool read,
+					loff_t offset,
+					struct mtd_oob_ops *ops,
+					struct msm_nand_rw_params *args)
+{
+	struct msm_nand_info *info = mtd->priv;
+	struct msm_nand_chip *chip = &info->nand_chip;
+	int err = 0;
+
+	pr_debug("========================================================\n");
+	pr_debug("offset 0x%llx mode %d\ndatbuf 0x%p datlen 0x%x\n",
+			offset, ops->mode, ops->datbuf, ops->len);
+	pr_debug("oobbuf 0x%p ooblen 0x%x\n", ops->oobbuf, ops->ooblen);
+
+	if (ops->mode == MTD_OPS_PLACE_OOB) {
+		pr_err("MTD_OPS_PLACE_OOB is not supported\n");
+		err = -EINVAL;
+		goto out;
+	}
+
+	if (mtd->writesize == PAGE_SIZE_2K)
+		args->page = offset >> 11;
+
+	if (mtd->writesize == PAGE_SIZE_4K)
+		args->page = offset >> 12;
+
+	args->oob_len_cmd = ops->ooblen;
+	args->oob_len_data = ops->ooblen;
+	args->cwperpage = (mtd->writesize >> 9);
+	args->read = (read ? true : false);
+
+	if (offset & (mtd->writesize - 1)) {
+		pr_err("unsupported offset 0x%llx\n", offset);
+		err = -EINVAL;
+		goto out;
+	}
+
+	if (!read && !ops->datbuf) {
+		pr_err("No data buffer provided for write!!\n");
+		err = -EINVAL;
+		goto out;
+	}
+
+	if (ops->mode == MTD_OPS_RAW) {
+		if (!ops->datbuf) {
+			pr_err("No data buffer provided for RAW mode\n");
+			err =  -EINVAL;
+			goto out;
+		} else if ((ops->len % (mtd->writesize +
+				mtd->oobsize)) != 0) {
+			pr_err("unsupported data len %d for RAW mode\n",
+				ops->len);
+			err = -EINVAL;
+			goto out;
+		}
+		args->page_count = ops->len / (mtd->writesize + mtd->oobsize);
+
+	} else if (ops->mode == MTD_OPS_AUTO_OOB) {
+		if (ops->datbuf && (ops->len % mtd->writesize) != 0) {
+			/* when ops->datbuf is NULL, ops->len can be ooblen */
+			pr_err("unsupported data len %d for AUTO mode\n",
+					ops->len);
+			err = -EINVAL;
+			goto out;
+		}
+		if (read && ops->oobbuf && !ops->datbuf) {
+			args->start_sector = args->cwperpage - 1;
+			args->page_count = ops->ooblen / mtd->oobavail;
+			if ((args->page_count == 0) && (ops->ooblen))
+				args->page_count = 1;
+		} else if (ops->datbuf) {
+			args->page_count = ops->len / mtd->writesize;
+		}
+	}
+
+	if (ops->datbuf) {
+		args->data_dma_addr_curr = args->data_dma_addr =
+			msm_nand_dma_map(chip->dev, ops->datbuf, ops->len,
+				      (read ? DMA_FROM_DEVICE : DMA_TO_DEVICE));
+		if (dma_mapping_error(chip->dev, args->data_dma_addr)) {
+			pr_err("dma mapping failed for 0x%p\n", ops->datbuf);
+			err = -EIO;
+			goto out;
+		}
+	}
+	if (ops->oobbuf) {
+		if (read)
+			memset(ops->oobbuf, 0xFF, ops->ooblen);
+		args->oob_dma_addr_curr = args->oob_dma_addr =
+			msm_nand_dma_map(chip->dev, ops->oobbuf, ops->ooblen,
+				(read ? DMA_BIDIRECTIONAL : DMA_TO_DEVICE));
+		if (dma_mapping_error(chip->dev, args->oob_dma_addr)) {
+			pr_err("dma mapping failed for 0x%p\n", ops->oobbuf);
+			err = -EIO;
+			goto dma_map_oobbuf_failed;
+		}
+	}
+	goto out;
+dma_map_oobbuf_failed:
+	if (ops->datbuf)
+		dma_unmap_page(chip->dev, args->data_dma_addr, ops->len,
+				(read ? DMA_FROM_DEVICE : DMA_TO_DEVICE));
+out:
+	return err;
+}
+
+/*
+ * Function that updates NANDc register data (struct msm_nand_rw_reg_data)
+ * required for page read/write.
+ */
+static void msm_nand_update_rw_reg_data(struct msm_nand_chip *chip,
+					struct mtd_oob_ops *ops,
+					struct msm_nand_rw_params *args,
+					struct msm_nand_rw_reg_data *data)
+{
+	if (args->read) {
+		if (ops->mode != MTD_OPS_RAW) {
+			data->cmd = MSM_NAND_CMD_PAGE_READ_ECC;
+			data->cfg0 =
+			(chip->cfg0 & ~(7U << CW_PER_PAGE)) |
+			(((args->cwperpage-1) - args->start_sector)
+			 << CW_PER_PAGE);
+			data->cfg1 = chip->cfg1;
+			data->ecc_bch_cfg = chip->ecc_bch_cfg;
+		} else {
+			data->cmd = MSM_NAND_CMD_PAGE_READ_ALL;
+			data->cfg0 = chip->cfg0_raw;
+			data->cfg1 = chip->cfg1_raw;
+			data->ecc_bch_cfg = 1 << ECC_CFG_ECC_DISABLE;
+		}
+
+	} else {
+		if (ops->mode != MTD_OPS_RAW) {
+			data->cfg0 = chip->cfg0;
+			data->cfg1 = chip->cfg1;
+			data->ecc_bch_cfg = chip->ecc_bch_cfg;
+		} else {
+			data->cfg0 = chip->cfg0_raw;
+			data->cfg1 = chip->cfg1_raw;
+			data->ecc_bch_cfg = 1 << ECC_CFG_ECC_DISABLE;
+		}
+		data->cmd = MSM_NAND_CMD_PRG_PAGE;
+		data->clrfstatus = MSM_NAND_RESET_FLASH_STS;
+		data->clrrstatus = MSM_NAND_RESET_READ_STS;
+	}
+	data->exec = 1;
+	data->ecc_cfg = chip->ecc_buf_cfg;
+}
+
+/*
+ * Function to prepare series of SPS command descriptors required for a page
+ * read/write operation.
+ */
+static void msm_nand_prep_rw_cmd_desc(struct mtd_oob_ops *ops,
+				struct msm_nand_rw_params *args,
+				struct msm_nand_rw_reg_data *data,
+				struct msm_nand_info *info,
+				uint32_t curr_cw,
+				struct msm_nand_sps_cmd **curr_cmd)
+{
+	struct msm_nand_chip *chip = &info->nand_chip;
+	struct msm_nand_sps_cmd *cmd;
+	uint32_t rdata;
+	/* read_location register parameters */
+	uint32_t offset, size, last_read;
+
+	cmd = *curr_cmd;
+	msm_nand_prep_ce(cmd, MSM_NAND_FLASH_CMD(info), WRITE, data->cmd,
+			((curr_cw == args->start_sector) ?
+			 SPS_IOVEC_FLAG_LOCK : 0));
+	cmd++;
+
+	if (curr_cw == args->start_sector) {
+		msm_nand_prep_ce(cmd, MSM_NAND_ADDR0(info), WRITE,
+				data->addr0, 0);
+		cmd++;
+
+		msm_nand_prep_ce(cmd, MSM_NAND_ADDR1(info), WRITE,
+				data->addr1, 0);
+		cmd++;
+
+		msm_nand_prep_ce(cmd, MSM_NAND_DEV0_CFG0(info), WRITE,
+				data->cfg0, 0);
+		cmd++;
+
+		msm_nand_prep_ce(cmd, MSM_NAND_DEV0_CFG1(info), WRITE,
+				data->cfg1, 0);
+		cmd++;
+
+		msm_nand_prep_ce(cmd, MSM_NAND_DEV0_ECC_CFG(info), WRITE,
+				data->ecc_bch_cfg, 0);
+		cmd++;
+
+		msm_nand_prep_ce(cmd, MSM_NAND_EBI2_ECC_BUF_CFG(info),
+				WRITE, data->ecc_cfg, 0);
+		cmd++;
+	}
+
+	if (!args->read)
+		goto sub_exec_cmd;
+
+	if (ops->mode == MTD_OPS_RAW) {
+		rdata = (0 << 0) | (chip->cw_size << 16) | (1 << 31);
+		msm_nand_prep_ce(cmd, MSM_NAND_READ_LOCATION_0(info), WRITE,
+				rdata, 0);
+		cmd++;
+	}
+	if (ops->mode == MTD_OPS_AUTO_OOB && ops->datbuf) {
+		offset = 0;
+		size = (curr_cw < (args->cwperpage - 1)) ? 516 :
+			(512 - ((args->cwperpage - 1) << 2));
+		last_read = (curr_cw < (args->cwperpage - 1)) ? 1 :
+			(ops->oobbuf ? 0 : 1);
+		rdata = (offset << 0) | (size << 16) | (last_read << 31);
+		msm_nand_prep_ce(cmd, MSM_NAND_READ_LOCATION_0(info), WRITE,
+				rdata, 0);
+		cmd++;
+	}
+	if (ops->mode == MTD_OPS_AUTO_OOB && ops->oobbuf
+			&& (curr_cw == (args->cwperpage - 1))) {
+		offset = 512 - ((args->cwperpage - 1) << 2);
+		size = (args->cwperpage) << 2;
+		if (size > args->oob_len_cmd)
+			size = args->oob_len_cmd;
+		args->oob_len_cmd -= size;
+		last_read = 1;
+		rdata = (offset << 0) | (size << 16) | (last_read << 31);
+		if (ops->datbuf) {
+			msm_nand_prep_ce(cmd, MSM_NAND_READ_LOCATION_1(info),
+					WRITE, rdata, 0);
+		} else {
+			msm_nand_prep_ce(cmd, MSM_NAND_READ_LOCATION_0(info),
+					WRITE, rdata, 0);
+		}
+		cmd++;
+	}
+sub_exec_cmd:
+	msm_nand_prep_ce(cmd, MSM_NAND_EXEC_CMD(info), WRITE, data->exec,
+			SPS_IOVEC_FLAG_NWD);
+	cmd++;
+	*curr_cmd = cmd;
+}
+
+/*
+ * Function to prepare and submit SPS data descriptors required for a page
+ * read/write operation.
+ */
+static int msm_nand_submit_rw_data_desc(struct mtd_oob_ops *ops,
+				struct msm_nand_rw_params *args,
+				struct msm_nand_info *info,
+				uint32_t curr_cw)
+{
+	struct msm_nand_chip *chip = &info->nand_chip;
+	struct sps_pipe *data_pipe_handle;
+	uint32_t sectordatasize, sectoroobsize;
+	uint32_t sps_flags = 0;
+	int err = 0;
+
+	if (args->read)
+		data_pipe_handle = info->sps.data_prod.handle;
+	else
+		data_pipe_handle = info->sps.data_cons.handle;
+
+	if (ops->mode == MTD_OPS_RAW) {
+		sectordatasize = chip->cw_size;
+		if (!args->read)
+			sps_flags = SPS_IOVEC_FLAG_EOT;
+		if (curr_cw == (args->cwperpage - 1))
+			sps_flags |= SPS_IOVEC_FLAG_INT;
+
+		err = sps_transfer_one(data_pipe_handle,
+				args->data_dma_addr_curr,
+				sectordatasize, NULL,
+				sps_flags);
+		if (err)
+			goto out;
+		args->data_dma_addr_curr += sectordatasize;
+
+	} else if (ops->mode == MTD_OPS_AUTO_OOB) {
+		if (ops->datbuf) {
+			sectordatasize = (curr_cw < (args->cwperpage - 1))
+			? 516 : (512 - ((args->cwperpage - 1) << 2));
+
+			if (!args->read) {
+				sps_flags = SPS_IOVEC_FLAG_EOT;
+				if (curr_cw == (args->cwperpage - 1) &&
+						ops->oobbuf)
+					sps_flags = 0;
+			}
+			if ((curr_cw == (args->cwperpage - 1)) && !ops->oobbuf)
+				sps_flags |= SPS_IOVEC_FLAG_INT;
+
+			err = sps_transfer_one(data_pipe_handle,
+					args->data_dma_addr_curr,
+					sectordatasize, NULL,
+					sps_flags);
+			if (err)
+				goto out;
+			args->data_dma_addr_curr += sectordatasize;
+		}
+
+		if (ops->oobbuf && (curr_cw == (args->cwperpage - 1))) {
+			sectoroobsize = args->cwperpage << 2;
+			if (sectoroobsize > args->oob_len_data)
+				sectoroobsize = args->oob_len_data;
+
+			if (!args->read)
+				sps_flags |= SPS_IOVEC_FLAG_EOT;
+			sps_flags |= SPS_IOVEC_FLAG_INT;
+			err = sps_transfer_one(data_pipe_handle,
+					args->oob_dma_addr_curr,
+					sectoroobsize, NULL,
+					sps_flags);
+			if (err)
+				goto out;
+			args->oob_dma_addr_curr += sectoroobsize;
+			args->oob_len_data -= sectoroobsize;
+		}
+	}
+out:
+	return err;
+}
+
+/*
+ * Function that gets called from upper layers such as MTD/YAFFS2 to read a
+ * page with main or/and spare data.
+ */
+static int msm_nand_read_oob(struct mtd_info *mtd, loff_t from,
+			     struct mtd_oob_ops *ops)
+{
+	struct msm_nand_info *info = mtd->priv;
+	struct msm_nand_chip *chip = &info->nand_chip;
+	uint32_t cwperpage = (mtd->writesize >> 9);
+	int err, pageerr = 0, rawerr = 0;
+	uint32_t n = 0, pages_read = 0;
+	uint32_t ecc_errors = 0, total_ecc_errors = 0;
+	struct msm_nand_rw_params rw_params;
+	struct msm_nand_rw_reg_data data;
+	struct msm_nand_sps_cmd *cmd, *curr_cmd;
+	struct sps_iovec *iovec;
+	/*
+	 * The following 6 commands will be sent only once for the first
+	 * codeword (CW) - addr0, addr1, dev0_cfg0, dev0_cfg1,
+	 * dev0_ecc_cfg, ebi2_ecc_buf_cfg. The following 6 commands will
+	 * be sent for every CW - flash, read_location_0, read_location_1,
+	 * exec, flash_status and buffer_status.
+	 */
+	uint32_t total_cnt = (6 * cwperpage) + 6;
+	struct {
+		struct sps_transfer xfer;
+		struct sps_iovec cmd_iovec[total_cnt];
+		struct msm_nand_sps_cmd cmd[total_cnt];
+		struct {
+			uint32_t flash_status;
+			uint32_t buffer_status;
+		} result[cwperpage];
+	} *dma_buffer;
+
+	memset(&rw_params, 0, sizeof(struct msm_nand_rw_params));
+	err = msm_nand_validate_mtd_params(mtd, true, from, ops, &rw_params);
+	if (err)
+		goto validate_mtd_params_failed;
+
+	wait_event(chip->dma_wait_queue, (dma_buffer = msm_nand_get_dma_buffer(
+			    chip, sizeof(*dma_buffer))));
+
+	rw_params.oob_col = rw_params.start_sector * chip->cw_size;
+	if (chip->cfg1 & (1 << WIDE_FLASH))
+		rw_params.oob_col >>= 1;
+
+	memset(&data, 0, sizeof(struct msm_nand_rw_reg_data));
+	msm_nand_update_rw_reg_data(chip, ops, &rw_params, &data);
+
+	while (rw_params.page_count-- > 0) {
+		data.addr0 = (rw_params.page << 16) | rw_params.oob_col;
+		data.addr1 = (rw_params.page >> 16) & 0xff;
+		cmd = dma_buffer->cmd;
+		for (n = rw_params.start_sector; n < cwperpage; n++) {
+			dma_buffer->result[n].flash_status = 0xeeeeeeee;
+			dma_buffer->result[n].buffer_status = 0xeeeeeeee;
+
+			curr_cmd = cmd;
+			msm_nand_prep_rw_cmd_desc(ops, &rw_params,
+					&data, info, n, &curr_cmd);
+
+			cmd = curr_cmd;
+			msm_nand_prep_ce(cmd, MSM_NAND_FLASH_STATUS(info),
+				READ, msm_virt_to_dma(chip,
+				&dma_buffer->result[n].flash_status), 0);
+			cmd++;
+
+			msm_nand_prep_ce(cmd, MSM_NAND_BUFFER_STATUS(info),
+				READ, msm_virt_to_dma(chip,
+				&dma_buffer->result[n].buffer_status),
+				((n == (cwperpage - 1)) ?
+				(SPS_IOVEC_FLAG_UNLOCK | SPS_IOVEC_FLAG_INT) :
+				0));
+			cmd++;
+		}
+
+		BUG_ON(cmd - dma_buffer->cmd > ARRAY_SIZE(dma_buffer->cmd));
+		dma_buffer->xfer.iovec_count = (cmd - dma_buffer->cmd);
+		dma_buffer->xfer.iovec = dma_buffer->cmd_iovec;
+		dma_buffer->xfer.iovec_phys = msm_virt_to_dma(chip,
+						&dma_buffer->cmd_iovec);
+		iovec = dma_buffer->xfer.iovec;
+
+		for (n = 0; n < dma_buffer->xfer.iovec_count; n++) {
+			iovec->addr =  msm_virt_to_dma(chip,
+					&dma_buffer->cmd[n].ce);
+			iovec->size = sizeof(struct sps_command_element);
+			iovec->flags = dma_buffer->cmd[n].flags;
+			iovec++;
+		}
+		mutex_lock(&info->bam_lock);
+		/* Submit data descriptors */
+		for (n = rw_params.start_sector; n < cwperpage; n++) {
+			err = msm_nand_submit_rw_data_desc(ops,
+						&rw_params, info, n);
+			if (err) {
+				pr_err("Failed to submit data descs %d\n", err);
+				mutex_unlock(&info->bam_lock);
+				goto free_dma;
+			}
+		}
+		/* Submit command descriptors */
+		err =  sps_transfer(info->sps.cmd_pipe.handle,
+				&dma_buffer->xfer);
+		if (err) {
+			pr_err("Failed to submit commands %d\n", err);
+			mutex_unlock(&info->bam_lock);
+			goto free_dma;
+		}
+		wait_for_completion_io(&info->sps.cmd_pipe.completion);
+		wait_for_completion_io(&info->sps.data_prod.completion);
+		mutex_unlock(&info->bam_lock);
+		/* Check for flash status errors */
+		pageerr = rawerr = 0;
+		for (n = rw_params.start_sector; n < cwperpage; n++) {
+			if (dma_buffer->result[n].flash_status & (FS_OP_ERR |
+					FS_MPU_ERR)) {
+				rawerr = -EIO;
+				break;
+			}
+		}
+		/* Check for ECC correction on empty block */
+		if (rawerr && ops->datbuf && ops->mode != MTD_OPS_RAW) {
+			uint8_t *datbuf = ops->datbuf +
+				pages_read * mtd->writesize;
+
+			dma_sync_single_for_cpu(chip->dev,
+			rw_params.data_dma_addr_curr - mtd->writesize,
+			mtd->writesize, DMA_BIDIRECTIONAL);
+
+			for (n = 0; n < mtd->writesize; n++) {
+				/* TODO: check offset for 4bit BCHECC */
+				if ((n % 516 == 3 || n % 516 == 175)
+						&& datbuf[n] == 0x54)
+					datbuf[n] = 0xff;
+				if (datbuf[n] != 0xff) {
+					pageerr = rawerr;
+					break;
+				}
+			}
+
+			dma_sync_single_for_device(chip->dev,
+			rw_params.data_dma_addr_curr - mtd->writesize,
+			mtd->writesize, DMA_BIDIRECTIONAL);
+		}
+		if (rawerr && ops->oobbuf) {
+			dma_sync_single_for_cpu(chip->dev,
+			rw_params.oob_dma_addr_curr - (ops->ooblen -
+			rw_params.oob_len_data),
+			ops->ooblen - rw_params.oob_len_data,
+			DMA_BIDIRECTIONAL);
+
+			for (n = 0; n < ops->ooblen; n++) {
+				if (ops->oobbuf[n] != 0xff) {
+					pageerr = rawerr;
+					break;
+				}
+			}
+
+			dma_sync_single_for_device(chip->dev,
+			rw_params.oob_dma_addr_curr - (ops->ooblen -
+			rw_params.oob_len_data),
+			ops->ooblen - rw_params.oob_len_data,
+			DMA_BIDIRECTIONAL);
+		}
+		/* check for uncorrectable errors */
+		if (pageerr) {
+			for (n = rw_params.start_sector; n < cwperpage; n++) {
+				if (dma_buffer->result[n].buffer_status &
+					BS_UNCORRECTABLE_BIT) {
+					mtd->ecc_stats.failed++;
+					pageerr = -EBADMSG;
+					break;
+				}
+			}
+		}
+		/* check for correctable errors */
+		if (!rawerr) {
+			for (n = rw_params.start_sector; n < cwperpage; n++) {
+				ecc_errors =
+				    dma_buffer->result[n].buffer_status
+				    & BS_CORRECTABLE_ERR_MSK;
+				if (ecc_errors) {
+					total_ecc_errors += ecc_errors;
+					mtd->ecc_stats.corrected += ecc_errors;
+					/*
+					 * For Micron devices it is observed
+					 * that correctable errors upto 3 bits
+					 * are very common.
+					 */
+					if (ecc_errors > 3)
+						pageerr = -EUCLEAN;
+				}
+			}
+		}
+		if (pageerr && (pageerr != -EUCLEAN || err == 0))
+			err = pageerr;
+
+		if (rawerr && !pageerr) {
+			pr_debug("%llx %x %x empty page\n",
+			       (loff_t)rw_params.page * mtd->writesize,
+			       ops->len, ops->ooblen);
+		} else {
+			for (n = rw_params.start_sector; n < cwperpage; n++)
+				pr_debug("cw %d: flash_sts %x buffr_sts %x\n",
+				n, dma_buffer->result[n].flash_status,
+				dma_buffer->result[n].buffer_status);
+		}
+		if (err && err != -EUCLEAN && err != -EBADMSG)
+			goto free_dma;
+		pages_read++;
+		rw_params.page++;
+	}
+free_dma:
+	msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer));
+	if (ops->oobbuf)
+		dma_unmap_page(chip->dev, rw_params.oob_dma_addr,
+				 ops->ooblen, DMA_FROM_DEVICE);
+	if (ops->datbuf)
+		dma_unmap_page(chip->dev, rw_params.data_dma_addr,
+				 ops->len, DMA_BIDIRECTIONAL);
+validate_mtd_params_failed:
+	if (ops->mode != MTD_OPS_RAW)
+		ops->retlen = mtd->writesize * pages_read;
+	else
+		ops->retlen = (mtd->writesize +  mtd->oobsize) * pages_read;
+	ops->oobretlen = ops->ooblen - rw_params.oob_len_data;
+	if (err)
+		pr_err("0x%llx datalen 0x%x ooblen %x err %d corrected %d\n",
+		       from, ops->datbuf ? ops->len : 0, ops->ooblen, err,
+		       total_ecc_errors);
+	pr_debug("ret %d, retlen %d oobretlen %d\n",
+			err, ops->retlen, ops->oobretlen);
+
+	pr_debug("========================================================\n");
+	return err;
+}
+
+/*
+ * Function that gets called from upper layers such as MTD/YAFFS2 to read a
+ * page with only main data.
+ */
+static int msm_nand_read(struct mtd_info *mtd, loff_t from, size_t len,
+	      size_t *retlen, u_char *buf)
+{
+	int ret;
+	struct mtd_oob_ops ops;
+
+	ops.mode = MTD_OPS_PLACE_OOB;
+	ops.len = len;
+	ops.retlen = 0;
+	ops.ooblen = 0;
+	ops.datbuf = buf;
+	ops.oobbuf = NULL;
+	ret =  msm_nand_read_oob(mtd, from, &ops);
+	*retlen = ops.retlen;
+	return ret;
+}
+
+/*
+ * Function that gets called from upper layers such as MTD/YAFFS2 to write a
+ * page with both main and spare data.
+ */
+static int msm_nand_write_oob(struct mtd_info *mtd, loff_t to,
+				struct mtd_oob_ops *ops)
+{
+	struct msm_nand_info *info = mtd->priv;
+	struct msm_nand_chip *chip = &info->nand_chip;
+	uint32_t cwperpage = (mtd->writesize >> 9);
+	uint32_t n, flash_sts, pages_written = 0;
+	int err = 0;
+	struct msm_nand_rw_params rw_params;
+	struct msm_nand_rw_reg_data data;
+	struct msm_nand_sps_cmd *cmd, *curr_cmd;
+	struct sps_iovec *iovec;
+	/*
+	 * The following 7 commands will be sent only once :
+	 * For first codeword (CW) - addr0, addr1, dev0_cfg0, dev0_cfg1,
+	 * dev0_ecc_cfg, ebi2_ecc_buf_cfg.
+	 * For last codeword (CW) - read_status(write)
+	 *
+	 * The following 4 commands will be sent for every CW :
+	 * flash, exec, flash_status (read), flash_status (write).
+	 */
+	uint32_t total_cnt = (4 * cwperpage) + 7;
+	struct {
+		struct sps_transfer xfer;
+		struct sps_iovec cmd_iovec[total_cnt];
+		struct msm_nand_sps_cmd cmd[total_cnt];
+		struct {
+			uint32_t flash_status[cwperpage];
+		} data;
+	} *dma_buffer;
+
+	memset(&rw_params, 0, sizeof(struct msm_nand_rw_params));
+	err = msm_nand_validate_mtd_params(mtd, false, to, ops, &rw_params);
+	if (err)
+		goto validate_mtd_params_failed;
+
+	wait_event(chip->dma_wait_queue, (dma_buffer =
+			msm_nand_get_dma_buffer(chip, sizeof(*dma_buffer))));
+
+	memset(&data, 0, sizeof(struct msm_nand_rw_reg_data));
+	msm_nand_update_rw_reg_data(chip, ops, &rw_params, &data);
+
+	while (rw_params.page_count-- > 0) {
+		data.addr0 = (rw_params.page << 16);
+		data.addr1 = (rw_params.page >> 16) & 0xff;
+		cmd = dma_buffer->cmd;
+
+		for (n = 0; n < cwperpage ; n++) {
+			dma_buffer->data.flash_status[n] = 0xeeeeeeee;
+
+			curr_cmd = cmd;
+			msm_nand_prep_rw_cmd_desc(ops, &rw_params,
+					&data, info, n, &curr_cmd);
+
+			cmd = curr_cmd;
+			msm_nand_prep_ce(cmd, MSM_NAND_FLASH_STATUS(info),
+				READ, msm_virt_to_dma(chip,
+				&dma_buffer->data.flash_status[n]), 0);
+			cmd++;
+
+			msm_nand_prep_ce(cmd, MSM_NAND_FLASH_STATUS(info),
+				WRITE, data.clrfstatus, 0);
+			cmd++;
+
+			if (n == (cwperpage - 1)) {
+				msm_nand_prep_ce(cmd,
+					MSM_NAND_READ_STATUS(info), WRITE,
+					data.clrrstatus, SPS_IOVEC_FLAG_UNLOCK
+					| SPS_IOVEC_FLAG_INT);
+				cmd++;
+			}
+		}
+
+		BUG_ON(cmd - dma_buffer->cmd > ARRAY_SIZE(dma_buffer->cmd));
+		dma_buffer->xfer.iovec_count = (cmd - dma_buffer->cmd);
+		dma_buffer->xfer.iovec = dma_buffer->cmd_iovec;
+		dma_buffer->xfer.iovec_phys = msm_virt_to_dma(chip,
+						&dma_buffer->cmd_iovec);
+		iovec = dma_buffer->xfer.iovec;
+
+		for (n = 0; n < dma_buffer->xfer.iovec_count; n++) {
+			iovec->addr =  msm_virt_to_dma(chip,
+					&dma_buffer->cmd[n].ce);
+			iovec->size = sizeof(struct sps_command_element);
+			iovec->flags = dma_buffer->cmd[n].flags;
+			iovec++;
+		}
+		mutex_lock(&info->bam_lock);
+		/* Submit data descriptors */
+		for (n = 0; n < cwperpage; n++) {
+			err = msm_nand_submit_rw_data_desc(ops,
+						&rw_params, info, n);
+			if (err) {
+				pr_err("Failed to submit data descs %d\n", err);
+				mutex_unlock(&info->bam_lock);
+				goto free_dma;
+			}
+		}
+		/* Submit command descriptors */
+		err =  sps_transfer(info->sps.cmd_pipe.handle,
+				&dma_buffer->xfer);
+		if (err) {
+			pr_err("Failed to submit commands %d\n", err);
+			mutex_unlock(&info->bam_lock);
+			goto free_dma;
+		}
+		wait_for_completion_io(&info->sps.cmd_pipe.completion);
+		wait_for_completion_io(&info->sps.data_cons.completion);
+		mutex_unlock(&info->bam_lock);
+
+		for (n = 0; n < cwperpage; n++)
+			pr_debug("write pg %d: flash_status[%d] = %x\n",
+				rw_params.page, n,
+				dma_buffer->data.flash_status[n]);
+
+		/*  Check for flash status errors */
+		for (n = 0; n < cwperpage; n++) {
+			flash_sts = dma_buffer->data.flash_status[n];
+			if (flash_sts & (FS_OP_ERR | FS_MPU_ERR)) {
+				pr_err("MPU/OP err (0x%x) set\n", flash_sts);
+				err = -EIO;
+				goto free_dma;
+			}
+			if (n == (cwperpage - 1)) {
+				if (!(flash_sts & FS_DEVICE_WP) ||
+					(flash_sts & FS_DEVICE_STS_ERR)) {
+					pr_err("Dev sts err 0x%x\n", flash_sts);
+					err = -EIO;
+					goto free_dma;
+				}
+			}
+		}
+		pages_written++;
+		rw_params.page++;
+	}
+free_dma:
+	msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer));
+	if (ops->oobbuf)
+		dma_unmap_page(chip->dev, rw_params.oob_dma_addr,
+				 ops->ooblen, DMA_TO_DEVICE);
+	if (ops->datbuf)
+		dma_unmap_page(chip->dev, rw_params.data_dma_addr,
+				ops->len, DMA_TO_DEVICE);
+validate_mtd_params_failed:
+	if (ops->mode != MTD_OPS_RAW)
+		ops->retlen = mtd->writesize * pages_written;
+	else
+		ops->retlen = (mtd->writesize + mtd->oobsize) * pages_written;
+
+	ops->oobretlen = ops->ooblen - rw_params.oob_len_data;
+	if (err)
+		pr_err("to %llx datalen %x ooblen %x failed with err %d\n",
+		       to, ops->len, ops->ooblen, err);
+	pr_debug("ret %d, retlen %d oobretlen %d\n",
+			err, ops->retlen, ops->oobretlen);
+
+	pr_debug("================================================\n");
+	return err;
+}
+
+/*
+ * Function that gets called from upper layers such as MTD/YAFFS2 to write a
+ * page with only main data.
+ */
+static int msm_nand_write(struct mtd_info *mtd, loff_t to, size_t len,
+			  size_t *retlen, const u_char *buf)
+{
+	int ret;
+	struct mtd_oob_ops ops;
+
+	ops.mode = MTD_OPS_PLACE_OOB;
+	ops.len = len;
+	ops.retlen = 0;
+	ops.ooblen = 0;
+	ops.datbuf = (uint8_t *)buf;
+	ops.oobbuf = NULL;
+	ret =  msm_nand_write_oob(mtd, to, &ops);
+	*retlen = ops.retlen;
+	return ret;
+}
+
+/*
+ * Structure that contains NANDc register data for commands required
+ * for Erase operation.
+ */
+struct msm_nand_erase_reg_data {
+	struct msm_nand_common_cfgs cfg;
+	uint32_t exec;
+	uint32_t flash_status;
+	uint32_t clrfstatus;
+	uint32_t clrrstatus;
+};
+
+/*
+ * Function that gets called from upper layers such as MTD/YAFFS2 to erase a
+ * block within NAND device.
+ */
+static int msm_nand_erase(struct mtd_info *mtd, struct erase_info *instr)
+{
+	int i, err = 0;
+	struct msm_nand_info *info = mtd->priv;
+	struct msm_nand_chip *chip = &info->nand_chip;
+	uint32_t page = 0;
+	struct msm_nand_sps_cmd *cmd, *curr_cmd;
+	struct msm_nand_erase_reg_data data;
+	struct sps_iovec *iovec;
+	uint32_t total_cnt = 9;
+	/*
+	 * The following 9 commands are required to erase a page -
+	 * flash, addr0, addr1, cfg0, cfg1, exec, flash_status(read),
+	 * flash_status(write), read_status.
+	 */
+	struct {
+		struct sps_transfer xfer;
+		struct sps_iovec cmd_iovec[total_cnt];
+		struct msm_nand_sps_cmd cmd[total_cnt];
+		uint32_t flash_status;
+	} *dma_buffer;
+
+	if (mtd->writesize == PAGE_SIZE_2K)
+		page = instr->addr >> 11;
+
+	if (mtd->writesize == PAGE_SIZE_4K)
+		page = instr->addr >> 12;
+
+	if (instr->addr & (mtd->erasesize - 1)) {
+		pr_err("unsupported erase address, 0x%llx\n", instr->addr);
+		err = -EINVAL;
+		goto out;
+	}
+	if (instr->len != mtd->erasesize) {
+		pr_err("unsupported erase len, %lld\n", instr->len);
+		err = -EINVAL;
+		goto out;
+	}
+
+	wait_event(chip->dma_wait_queue, (dma_buffer = msm_nand_get_dma_buffer(
+			    chip, sizeof(*dma_buffer))));
+	cmd = dma_buffer->cmd;
+
+	memset(&data, 0, sizeof(struct msm_nand_erase_reg_data));
+	data.cfg.cmd = MSM_NAND_CMD_BLOCK_ERASE;
+	data.cfg.addr0 = page;
+	data.cfg.addr1 = 0;
+	data.cfg.cfg0 = chip->cfg0 & (~(7 << CW_PER_PAGE));
+	data.cfg.cfg1 = chip->cfg1;
+	data.exec = 1;
+	dma_buffer->flash_status = 0xeeeeeeee;
+	data.clrfstatus = MSM_NAND_RESET_FLASH_STS;
+	data.clrrstatus = MSM_NAND_RESET_READ_STS;
+
+	curr_cmd = cmd;
+	msm_nand_prep_cfg_cmd_desc(info, data.cfg, &curr_cmd);
+
+	cmd = curr_cmd;
+	msm_nand_prep_ce(cmd, MSM_NAND_EXEC_CMD(info), WRITE, data.exec,
+			SPS_IOVEC_FLAG_NWD);
+	cmd++;
+
+	msm_nand_prep_ce(cmd, MSM_NAND_FLASH_STATUS(info), READ,
+		msm_virt_to_dma(chip, &dma_buffer->flash_status), 0);
+	cmd++;
+
+	msm_nand_prep_ce(cmd, MSM_NAND_FLASH_STATUS(info), WRITE,
+			data.clrfstatus, 0);
+	cmd++;
+
+	msm_nand_prep_ce(cmd, MSM_NAND_READ_STATUS(info), WRITE,
+			data.clrrstatus,
+			SPS_IOVEC_FLAG_UNLOCK | SPS_IOVEC_FLAG_INT);
+	cmd++;
+
+	BUG_ON(cmd - dma_buffer->cmd > ARRAY_SIZE(dma_buffer->cmd));
+	dma_buffer->xfer.iovec_count = (cmd - dma_buffer->cmd);
+	dma_buffer->xfer.iovec = dma_buffer->cmd_iovec;
+	dma_buffer->xfer.iovec_phys = msm_virt_to_dma(chip,
+					&dma_buffer->cmd_iovec);
+	iovec = dma_buffer->xfer.iovec;
+
+	for (i = 0; i < dma_buffer->xfer.iovec_count; i++) {
+		iovec->addr =  msm_virt_to_dma(chip, &dma_buffer->cmd[i].ce);
+		iovec->size = sizeof(struct sps_command_element);
+		iovec->flags = dma_buffer->cmd[i].flags;
+		iovec++;
+	}
+	mutex_lock(&info->bam_lock);
+	err =  sps_transfer(info->sps.cmd_pipe.handle, &dma_buffer->xfer);
+	if (err) {
+		pr_err("Failed to submit commands %d\n", err);
+		mutex_unlock(&info->bam_lock);
+		goto free_dma;
+	}
+	wait_for_completion_io(&info->sps.cmd_pipe.completion);
+	mutex_unlock(&info->bam_lock);
+
+	/*  Check for flash status errors */
+	if (dma_buffer->flash_status & (FS_OP_ERR |
+			FS_MPU_ERR | FS_DEVICE_STS_ERR)) {
+		pr_err("MPU/OP/DEV err (0x%x) set\n", dma_buffer->flash_status);
+		err = -EIO;
+	}
+	if (!(dma_buffer->flash_status & FS_DEVICE_WP)) {
+		pr_err("Device is write protected\n");
+		err = -EIO;
+	}
+	if (err) {
+		pr_err("Erase failed, 0x%llx\n", instr->addr);
+		instr->fail_addr = instr->addr;
+		instr->state = MTD_ERASE_FAILED;
+	} else {
+		instr->state = MTD_ERASE_DONE;
+		instr->fail_addr = 0xffffffff;
+		mtd_erase_callback(instr);
+	}
+free_dma:
+	msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer));
+out:
+	return err;
+}
+
+/*
+ * Structure that contains NANDc register data for commands required
+ * for checking if a block is bad.
+ */
+struct msm_nand_blk_isbad_data {
+	struct msm_nand_common_cfgs cfg;
+	uint32_t ecc_bch_cfg;
+	uint32_t exec;
+	uint32_t read_offset;
+};
+
+/*
+ * Function that gets called from upper layers such as MTD/YAFFS2 to check if
+ * a block is bad. This is done by reading the first page within a block and
+ * checking whether the bad block byte location contains 0xFF or not. If it
+ * doesn't contain 0xFF, then it is considered as bad block.
+ */
+static int msm_nand_block_isbad(struct mtd_info *mtd, loff_t ofs)
+{
+	struct msm_nand_info *info = mtd->priv;
+	struct msm_nand_chip *chip = &info->nand_chip;
+	int i, ret = 0, bad_block = 0;
+	uint8_t *buf;
+	uint32_t page = 0, rdata, cwperpage;
+	struct msm_nand_sps_cmd *cmd, *curr_cmd;
+	struct msm_nand_blk_isbad_data data;
+	struct sps_iovec *iovec;
+	uint32_t total_cnt = 9;
+	/*
+	 * The following 9 commands are required to check bad block -
+	 * flash, addr0, addr1, cfg0, cfg1, ecc_cfg, read_loc_0,
+	 * exec, flash_status(read).
+	 */
+	struct {
+		struct sps_transfer xfer;
+		struct sps_iovec cmd_iovec[total_cnt];
+		struct msm_nand_sps_cmd cmd[total_cnt];
+		uint32_t flash_status;
+	} *dma_buffer;
+
+	if (mtd->writesize == PAGE_SIZE_2K)
+		page = ofs >> 11;
+
+	if (mtd->writesize == PAGE_SIZE_4K)
+		page = ofs >> 12;
+
+	cwperpage = (mtd->writesize >> 9);
+
+	if (ofs > mtd->size) {
+		pr_err("Invalid offset 0x%llx\n", ofs);
+		bad_block = -EINVAL;
+		goto out;
+	}
+	if (ofs & (mtd->erasesize - 1)) {
+		pr_err("unsupported block address, 0x%x\n", (uint32_t)ofs);
+		bad_block = -EINVAL;
+		goto out;
+	}
+
+	wait_event(chip->dma_wait_queue, (dma_buffer = msm_nand_get_dma_buffer(
+				chip , sizeof(*dma_buffer) + 4)));
+	buf = (uint8_t *)dma_buffer + sizeof(*dma_buffer);
+
+	cmd = dma_buffer->cmd;
+	memset(&data, 0, sizeof(struct msm_nand_erase_reg_data));
+	data.cfg.cmd = MSM_NAND_CMD_PAGE_READ_ALL;
+	data.cfg.cfg0 = chip->cfg0_raw & ~(7U << CW_PER_PAGE);
+	data.cfg.cfg1 = chip->cfg1_raw;
+
+	if (chip->cfg1 & (1 << WIDE_FLASH))
+		data.cfg.addr0 = (page << 16) |
+			((chip->cw_size * (cwperpage-1)) >> 1);
+	else
+		data.cfg.addr0 = (page << 16) |
+			(chip->cw_size * (cwperpage-1));
+
+	data.cfg.addr1 = (page >> 16) & 0xff;
+	data.ecc_bch_cfg = 1 << ECC_CFG_ECC_DISABLE;
+	data.exec = 1;
+	data.read_offset = (mtd->writesize - (chip->cw_size * (cwperpage-1)));
+	dma_buffer->flash_status = 0xeeeeeeee;
+
+	curr_cmd = cmd;
+	msm_nand_prep_cfg_cmd_desc(info, data.cfg, &curr_cmd);
+
+	cmd = curr_cmd;
+	msm_nand_prep_ce(cmd, MSM_NAND_DEV0_ECC_CFG(info), WRITE,
+			data.ecc_bch_cfg, 0);
+	cmd++;
+
+	rdata = (data.read_offset << 0) | (4 << 16) | (1 << 31);
+	msm_nand_prep_ce(cmd, MSM_NAND_READ_LOCATION_0(info), WRITE, rdata, 0);
+	cmd++;
+
+	msm_nand_prep_ce(cmd, MSM_NAND_EXEC_CMD(info), WRITE,
+			data.exec, SPS_IOVEC_FLAG_NWD);
+	cmd++;
+
+	msm_nand_prep_ce(cmd, MSM_NAND_FLASH_STATUS(info), READ,
+		msm_virt_to_dma(chip, &dma_buffer->flash_status),
+		SPS_IOVEC_FLAG_INT | SPS_IOVEC_FLAG_UNLOCK);
+	cmd++;
+
+	BUG_ON(cmd - dma_buffer->cmd > ARRAY_SIZE(dma_buffer->cmd));
+	dma_buffer->xfer.iovec_count = (cmd - dma_buffer->cmd);
+	dma_buffer->xfer.iovec = dma_buffer->cmd_iovec;
+	dma_buffer->xfer.iovec_phys = msm_virt_to_dma(chip,
+					&dma_buffer->cmd_iovec);
+	iovec = dma_buffer->xfer.iovec;
+
+	for (i = 0; i < dma_buffer->xfer.iovec_count; i++) {
+		iovec->addr =  msm_virt_to_dma(chip, &dma_buffer->cmd[i].ce);
+		iovec->size = sizeof(struct sps_command_element);
+		iovec->flags = dma_buffer->cmd[i].flags;
+		iovec++;
+	}
+	mutex_lock(&info->bam_lock);
+	/* Submit data descriptor */
+	ret = sps_transfer_one(info->sps.data_prod.handle,
+			msm_virt_to_dma(chip, buf),
+			4, NULL, SPS_IOVEC_FLAG_INT);
+
+	if (ret) {
+		pr_err("Failed to submit data desc %d\n", ret);
+		mutex_unlock(&info->bam_lock);
+		goto free_dma;
+	}
+	/* Submit command descriptor */
+	ret =  sps_transfer(info->sps.cmd_pipe.handle, &dma_buffer->xfer);
+	if (ret) {
+		pr_err("Failed to submit commands %d\n", ret);
+		mutex_unlock(&info->bam_lock);
+		goto free_dma;
+	}
+	wait_for_completion_io(&info->sps.cmd_pipe.completion);
+	wait_for_completion_io(&info->sps.data_prod.completion);
+	mutex_unlock(&info->bam_lock);
+
+	/* Check for flash status errors */
+	if (dma_buffer->flash_status & (FS_OP_ERR | FS_MPU_ERR)) {
+		pr_err("MPU/OP err set: %x\n", dma_buffer->flash_status);
+		bad_block = -EIO;
+		goto free_dma;
+	}
+
+	/* Check for bad block marker byte */
+	if (chip->cfg1 & (1 << WIDE_FLASH)) {
+		if (buf[0] != 0xFF || buf[1] != 0xFF)
+			bad_block = 1;
+	} else {
+		if (buf[0] != 0xFF)
+			bad_block = 1;
+	}
+free_dma:
+	msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer) + 4);
+out:
+	return ret ? ret : bad_block;
+}
+
+/*
+ * Function that gets called from upper layers such as MTD/YAFFS2 to mark a
+ * block as bad. This is done by writing the first page within a block with 0,
+ * thus setting the bad block byte location as well to 0.
+ */
+static int msm_nand_block_markbad(struct mtd_info *mtd, loff_t ofs)
+{
+	struct mtd_oob_ops ops;
+	int ret;
+	uint8_t *buf;
+	size_t len;
+
+	if (ofs > mtd->size) {
+		pr_err("Invalid offset 0x%llx\n", ofs);
+		ret = -EINVAL;
+		goto out;
+	}
+	if (ofs & (mtd->erasesize - 1)) {
+		pr_err("unsupported block address, 0x%x\n", (uint32_t)ofs);
+		ret = -EINVAL;
+		goto out;
+	}
+	len = mtd->writesize + mtd->oobsize;
+	buf = kzalloc(len, GFP_KERNEL);
+	if (!buf) {
+		pr_err("unable to allocate memory for 0x%x size\n", len);
+		ret = -ENOMEM;
+		goto out;
+	}
+	ops.mode = MTD_OPS_RAW;
+	ops.len = len;
+	ops.retlen = 0;
+	ops.ooblen = 0;
+	ops.datbuf = buf;
+	ops.oobbuf = NULL;
+	ret =  msm_nand_write_oob(mtd, ofs, &ops);
+	kfree(buf);
+out:
+	return ret;
+}
+
+/*
+ * Function that scans for the attached NAND device. This fills out all
+ * the uninitialized function pointers with the defaults. The flash ID is
+ * read and the mtd/chip structures are filled with the appropriate values.
+ */
+int msm_nand_scan(struct mtd_info *mtd)
+{
+	struct msm_nand_info *info = mtd->priv;
+	struct msm_nand_chip *chip = &info->nand_chip;
+	struct flash_identification *supported_flash = &info->flash_dev;
+	int flash_id = 0, err = 0;
+	uint32_t i, mtd_writesize;
+	uint8_t dev_found = 0, wide_bus;
+	uint32_t manid, devid, devcfg;
+	uint32_t bad_block_byte;
+	struct nand_flash_dev *flashdev = NULL;
+	struct nand_manufacturers  *flashman = NULL;
+
+	/* Probe the Flash device for ONFI compliance */
+	if (!msm_nand_flash_onfi_probe(info)) {
+		dev_found = 1;
+	} else {
+		err = msm_nand_flash_read_id(info, 0, &flash_id);
+		if (err < 0) {
+			pr_err("Failed to read Flash ID\n");
+			err = -EINVAL;
+			goto out;
+		}
+		manid = flash_id & 0xFF;
+		devid = (flash_id >> 8) & 0xFF;
+		devcfg = (flash_id >> 24) & 0xFF;
+
+		for (i = 0; !flashman && nand_manuf_ids[i].id; ++i)
+			if (nand_manuf_ids[i].id == manid)
+				flashman = &nand_manuf_ids[i];
+		for (i = 0; !flashdev && nand_flash_ids[i].id; ++i)
+			if (nand_flash_ids[i].id == devid)
+				flashdev = &nand_flash_ids[i];
+		if (!flashdev || !flashman) {
+			pr_err("unknown nand flashid=%x manuf=%x devid=%x\n",
+				flash_id, manid, devid);
+			err = -ENOENT;
+			goto out;
+		}
+		dev_found = 1;
+		if (!flashdev->pagesize) {
+			supported_flash->widebus = devcfg & (1 << 6) ? 1 : 0;
+			supported_flash->pagesize = 1024 << (devcfg & 0x3);
+			supported_flash->blksize = (64 * 1024) <<
+							((devcfg >> 4) & 0x3);
+			supported_flash->oobsize = (8 << ((devcfg >> 2) & 1)) *
+				(supported_flash->pagesize >> 9);
+		} else {
+			supported_flash->widebus = flashdev->options &
+				       NAND_BUSWIDTH_16 ? 1 : 0;
+			supported_flash->pagesize = flashdev->pagesize;
+			supported_flash->blksize = flashdev->erasesize;
+			supported_flash->oobsize = flashdev->pagesize >> 5;
+		}
+		supported_flash->flash_id = flash_id;
+		supported_flash->density = flashdev->chipsize << 20;
+	}
+
+	if (dev_found) {
+		wide_bus       = supported_flash->widebus;
+		mtd->size      = supported_flash->density;
+		mtd->writesize = supported_flash->pagesize;
+		mtd->oobsize   = supported_flash->oobsize;
+		mtd->erasesize = supported_flash->blksize;
+		mtd_writesize = mtd->writesize;
+
+		/* Check whether NAND device support 8bit ECC*/
+		if (supported_flash->ecc_correctability >= 8)
+			chip->bch_caps = MSM_NAND_CAP_8_BIT_BCH;
+		else
+			chip->bch_caps = MSM_NAND_CAP_4_BIT_BCH;
+
+		pr_info("NAND Id: 0x%x Buswidth: %dBits Density: %lld MByte\n",
+			supported_flash->flash_id, (wide_bus) ? 16 : 8,
+			(mtd->size >> 20));
+		pr_info("pagesize: %d Erasesize: %d oobsize: %d (in Bytes)\n",
+			mtd->writesize, mtd->erasesize, mtd->oobsize);
+		pr_info("BCH ECC: %d Bit\n",
+			(chip->bch_caps & MSM_NAND_CAP_8_BIT_BCH ? 8 : 4));
+	}
+
+	chip->cw_size = (chip->bch_caps & MSM_NAND_CAP_8_BIT_BCH) ? 532 : 528;
+	chip->cfg0 = (((mtd_writesize >> 9) - 1) << CW_PER_PAGE)
+		|  (516 <<  UD_SIZE_BYTES)
+		|  (0 << DISABLE_STATUS_AFTER_WRITE)
+		|  (5 << NUM_ADDR_CYCLES);
+
+	bad_block_byte = (mtd_writesize - (chip->cw_size * (
+					(mtd_writesize >> 9) - 1)) + 1);
+	chip->cfg1 = (7 <<  NAND_RECOVERY_CYCLES)
+		|    (0 <<  CS_ACTIVE_BSY)
+		|    (bad_block_byte <<  BAD_BLOCK_BYTE_NUM)
+		|    (0 << BAD_BLOCK_IN_SPARE_AREA)
+		|    (2 << WR_RD_BSY_GAP)
+		| ((wide_bus ? 1 : 0) << WIDE_FLASH)
+		| (1 << ENABLE_BCH_ECC);
+
+	chip->cfg0_raw = (((mtd_writesize >> 9) - 1) << CW_PER_PAGE)
+		|	(5 << NUM_ADDR_CYCLES)
+		|	(0 << SPARE_SIZE_BYTES)
+		|	(chip->cw_size << UD_SIZE_BYTES);
+
+	chip->cfg1_raw = (7 <<  NAND_RECOVERY_CYCLES)
+		|    (0 <<  CS_ACTIVE_BSY)
+		|    (17 <<  BAD_BLOCK_BYTE_NUM)
+		|    (1 << BAD_BLOCK_IN_SPARE_AREA)
+		|    (2 << WR_RD_BSY_GAP)
+		| ((wide_bus ? 1 : 0) << WIDE_FLASH)
+		| (1 << DEV0_CFG1_ECC_DISABLE);
+
+	chip->ecc_bch_cfg = (0 << ECC_CFG_ECC_DISABLE)
+			|   (0 << ECC_SW_RESET)
+			|   (516 << ECC_NUM_DATA_BYTES)
+			|   (1 << ECC_FORCE_CLK_OPEN);
+
+	if (chip->bch_caps & MSM_NAND_CAP_8_BIT_BCH) {
+		chip->cfg0 |= (wide_bus ? 0 << SPARE_SIZE_BYTES :
+				2 << SPARE_SIZE_BYTES);
+		chip->ecc_bch_cfg |= (1 << ECC_MODE)
+			|   ((wide_bus) ? (14 << ECC_PARITY_SIZE_BYTES) :
+					(13 << ECC_PARITY_SIZE_BYTES));
+	} else {
+		chip->cfg0 |= (wide_bus ? 2 << SPARE_SIZE_BYTES :
+				4 << SPARE_SIZE_BYTES);
+		chip->ecc_bch_cfg |= (0 << ECC_MODE)
+			|   ((wide_bus) ? (8 << ECC_PARITY_SIZE_BYTES) :
+					(7 << ECC_PARITY_SIZE_BYTES));
+	}
+
+	/*
+	 * For 4bit BCH ECC (default ECC), parity bytes = 7(x8) or 8(x16 I/O)
+	 * For 8bit BCH ECC, parity bytes = 13 (x8) or 14 (x16 I/O).
+	 */
+	chip->ecc_parity_bytes = (chip->bch_caps & MSM_NAND_CAP_8_BIT_BCH) ?
+				(wide_bus ? 14 : 13) : (wide_bus ? 8 : 7);
+	chip->ecc_buf_cfg = 0x203; /* No of bytes covered by ECC - 516 bytes */
+
+	pr_info("CFG0: 0x%08x,      CFG1: 0x%08x\n"
+		"            RAWCFG0: 0x%08x,   RAWCFG1: 0x%08x\n"
+		"          ECCBUFCFG: 0x%08x, ECCBCHCFG: 0x%08x\n"
+		"     BAD BLOCK BYTE: 0x%08x\n", chip->cfg0, chip->cfg1,
+		chip->cfg0_raw, chip->cfg1_raw, chip->ecc_buf_cfg,
+		chip->ecc_bch_cfg, bad_block_byte);
+
+	if (mtd->oobsize == 64) {
+		mtd->oobavail = 16;
+	} else if ((mtd->oobsize == 128) || (mtd->oobsize == 224)) {
+		mtd->oobavail = 32;
+	} else {
+		pr_err("Unsupported NAND oobsize: 0x%x\n", mtd->oobsize);
+		err = -ENODEV;
+		goto out;
+	}
+
+	/* Fill in remaining MTD driver data */
+	mtd->type = MTD_NANDFLASH;
+	mtd->flags = MTD_CAP_NANDFLASH;
+	mtd->_erase = msm_nand_erase;
+	mtd->_block_isbad = msm_nand_block_isbad;
+	mtd->_block_markbad = msm_nand_block_markbad;
+	mtd->_read = msm_nand_read;
+	mtd->_write = msm_nand_write;
+	mtd->_read_oob  = msm_nand_read_oob;
+	mtd->_write_oob = msm_nand_write_oob;
+	mtd->owner = THIS_MODULE;
+out:
+	return err;
+}
+
+#define BAM_APPS_PIPE_LOCK_GRP 0
+/*
+ * This function allocates, configures, connects an end point and
+ * also registers event notification for an end point. It also allocates
+ * DMA memory for descriptor FIFO of a pipe.
+ */
+static int msm_nand_init_endpoint(struct msm_nand_info *info,
+				struct msm_nand_sps_endpt *end_point,
+				uint32_t pipe_index)
+{
+	int rc = 0;
+	struct sps_pipe *pipe_handle;
+	struct sps_connect *sps_config = &end_point->config;
+	struct sps_register_event *sps_event = &end_point->event;
+
+	pipe_handle = sps_alloc_endpoint();
+	if (!pipe_handle) {
+		pr_err("sps_alloc_endpoint() failed\n");
+		rc = -ENOMEM;
+		goto out;
+	}
+
+	rc = sps_get_config(pipe_handle, sps_config);
+	if (rc) {
+		pr_err("sps_get_config() failed %d\n", rc);
+		goto free_endpoint;
+	}
+
+	if (pipe_index == SPS_DATA_PROD_PIPE_INDEX) {
+		/* READ CASE: source - BAM; destination - system memory */
+		sps_config->source = info->sps.bam_handle;
+		sps_config->destination = SPS_DEV_HANDLE_MEM;
+		sps_config->mode = SPS_MODE_SRC;
+		sps_config->src_pipe_index = pipe_index;
+	} else if (pipe_index == SPS_DATA_CONS_PIPE_INDEX ||
+			pipe_index == SPS_CMD_CONS_PIPE_INDEX) {
+		/* WRITE CASE: source - system memory; destination - BAM */
+		sps_config->source = SPS_DEV_HANDLE_MEM;
+		sps_config->destination = info->sps.bam_handle;
+		sps_config->mode = SPS_MODE_DEST;
+		sps_config->dest_pipe_index = pipe_index;
+	}
+
+	sps_config->options = SPS_O_AUTO_ENABLE | SPS_O_DESC_DONE;
+	sps_config->lock_group = BAM_APPS_PIPE_LOCK_GRP;
+	/*
+	 * Descriptor FIFO is a cyclic FIFO. If SPS_MAX_DESC_NUM descriptors
+	 * are allowed to be submitted before we get any ack for any of them,
+	 * the descriptor FIFO size should be: (SPS_MAX_DESC_NUM + 1) *
+	 * sizeof(struct sps_iovec).
+	 */
+	sps_config->desc.size = (SPS_MAX_DESC_NUM + 1) *
+					sizeof(struct sps_iovec);
+	sps_config->desc.base = dmam_alloc_coherent(info->nand_chip.dev,
+					sps_config->desc.size,
+					&sps_config->desc.phys_base,
+					GFP_KERNEL);
+	if (!sps_config->desc.base) {
+		pr_err("dmam_alloc_coherent() failed for size %x\n",
+				sps_config->desc.size);
+		rc = -ENOMEM;
+		goto free_endpoint;
+	}
+	memset(sps_config->desc.base, 0x00, sps_config->desc.size);
+
+	rc = sps_connect(pipe_handle, sps_config);
+	if (rc) {
+		pr_err("sps_connect() failed %d\n", rc);
+		goto free_endpoint;
+	}
+
+	init_completion(&end_point->completion);
+	sps_event->mode = SPS_TRIGGER_WAIT;
+	sps_event->options = SPS_O_DESC_DONE;
+	sps_event->xfer_done = &end_point->completion;
+	sps_event->user = (void *)info;
+
+	rc = sps_register_event(pipe_handle, sps_event);
+	if (rc) {
+		pr_err("sps_register_event() failed %d\n", rc);
+		goto sps_disconnect;
+	}
+	end_point->handle = pipe_handle;
+	pr_debug("pipe handle 0x%x for pipe %d\n", (uint32_t)pipe_handle,
+			pipe_index);
+	goto out;
+sps_disconnect:
+	sps_disconnect(pipe_handle);
+free_endpoint:
+	sps_free_endpoint(pipe_handle);
+out:
+	return rc;
+}
+
+/* This function disconnects and frees an end point */
+static void msm_nand_deinit_endpoint(struct msm_nand_info *info,
+				struct msm_nand_sps_endpt *end_point)
+{
+	sps_disconnect(end_point->handle);
+	sps_free_endpoint(end_point->handle);
+}
+
+/*
+ * This function registers BAM device and initializes its end points for
+ * the following pipes -
+ * system consumer pipe for data (pipe#0),
+ * system producer pipe for data (pipe#1),
+ * system consumer pipe for commands (pipe#2).
+ */
+static int msm_nand_bam_init(struct msm_nand_info *nand_info)
+{
+	struct sps_bam_props bam = {0};
+	int rc = 0;
+
+	bam.phys_addr = nand_info->bam_phys;
+	bam.virt_addr = nand_info->bam_base;
+	bam.irq = nand_info->bam_irq;
+	/*
+	 * NAND device is accessible from both Apps and Modem processor and
+	 * thus, NANDc and BAM are shared between both the processors. But BAM
+	 * must be enabled and instantiated only once during boot up by
+	 * Trustzone before Modem/Apps is brought out from reset.
+	 *
+	 * This is indicated to SPS driver on Apps by marking flag
+	 * SPS_BAM_MGR_DEVICE_REMOTE. The following are the global
+	 * initializations that will be done by Trustzone - Execution
+	 * Environment, Pipes assignment to Apps/Modem, Pipe Super groups and
+	 * Descriptor summing threshold.
+	 *
+	 * NANDc BAM device supports 2 execution environments - Modem and Apps
+	 * and thus the flag SPS_BAM_MGR_MULTI_EE is set.
+	 */
+	bam.manage = SPS_BAM_MGR_DEVICE_REMOTE | SPS_BAM_MGR_MULTI_EE;
+
+	rc = sps_register_bam_device(&bam, &nand_info->sps.bam_handle);
+	if (rc) {
+		pr_err("sps_register_bam_device() failed with %d\n", rc);
+		goto out;
+	}
+	pr_info("BAM device registered: bam_handle 0x%x\n",
+			nand_info->sps.bam_handle);
+
+	rc = msm_nand_init_endpoint(nand_info, &nand_info->sps.data_prod,
+					SPS_DATA_PROD_PIPE_INDEX);
+	if (rc)
+		goto unregister_bam;
+
+	rc = msm_nand_init_endpoint(nand_info, &nand_info->sps.data_cons,
+					SPS_DATA_CONS_PIPE_INDEX);
+	if (rc)
+		goto deinit_data_prod;
+
+	rc = msm_nand_init_endpoint(nand_info, &nand_info->sps.cmd_pipe,
+					SPS_CMD_CONS_PIPE_INDEX);
+	if (rc)
+		goto deinit_data_cons;
+	goto out;
+deinit_data_cons:
+	msm_nand_deinit_endpoint(nand_info, &nand_info->sps.data_cons);
+deinit_data_prod:
+	msm_nand_deinit_endpoint(nand_info, &nand_info->sps.data_prod);
+unregister_bam:
+	sps_deregister_bam_device(nand_info->sps.bam_handle);
+out:
+	return rc;
+}
+
+/*
+ * This function de-registers BAM device, disconnects and frees its end points
+ * for all the pipes.
+ */
+static void msm_nand_bam_free(struct msm_nand_info *nand_info)
+{
+	msm_nand_deinit_endpoint(nand_info, &nand_info->sps.data_prod);
+	msm_nand_deinit_endpoint(nand_info, &nand_info->sps.data_cons);
+	msm_nand_deinit_endpoint(nand_info, &nand_info->sps.cmd_pipe);
+	sps_deregister_bam_device(nand_info->sps.bam_handle);
+}
+
+/* This function enables DMA support for the NANDc in BAM mode. */
+static int msm_nand_enable_dma(struct msm_nand_info *info)
+{
+	struct msm_nand_sps_cmd *sps_cmd;
+	struct msm_nand_chip *chip = &info->nand_chip;
+	int ret;
+
+	wait_event(chip->dma_wait_queue,
+		   (sps_cmd = msm_nand_get_dma_buffer(chip, sizeof(*sps_cmd))));
+
+	msm_nand_prep_ce(sps_cmd, MSM_NAND_CTRL(info), WRITE,
+			(1 << BAM_MODE_EN), SPS_IOVEC_FLAG_INT);
+
+	ret = sps_transfer_one(info->sps.cmd_pipe.handle,
+			msm_virt_to_dma(chip, &sps_cmd->ce),
+			sizeof(struct sps_command_element), NULL,
+			sps_cmd->flags);
+	if (ret) {
+		pr_err("Failed to submit command: %d\n", ret);
+		goto out;
+	}
+	wait_for_completion_io(&info->sps.cmd_pipe.completion);
+out:
+	msm_nand_release_dma_buffer(chip, sps_cmd, sizeof(*sps_cmd));
+	return ret;
+
+}
+
+/*
+ * This function gets called when its device named msm-nand is added to
+ * device tree .dts file with all its resources such as physical addresses
+ * for NANDc and BAM, BAM IRQ.
+ *
+ * It also expects the NAND flash partition information to be passed in .dts
+ * file so that it can parse the partitions by calling MTD function
+ * mtd_device_parse_register().
+ *
+ */
+static int __devinit msm_nand_probe(struct platform_device *pdev)
+{
+	struct msm_nand_info *info;
+	struct resource *res;
+	int err, n_parts;
+	struct device_node *pnode;
+	struct mtd_part_parser_data parser_data;
+
+	if (!pdev->dev.of_node) {
+		pr_err("No valid device tree info for NANDc\n");
+		err = -ENODEV;
+		goto out;
+	}
+
+	/*
+	 * The partition information can also be passed from kernel command
+	 * line. Also, the MTD core layer supports adding the whole device as
+	 * one MTD device when no partition information is available at all.
+	 * Hence, do not bail out when partition information is not availabe
+	 * in device tree.
+	 */
+	pnode = of_find_node_by_path("/qcom,mtd-partitions");
+	if (!pnode)
+		pr_info("No partition info available in device tree\n");
+	info = devm_kzalloc(&pdev->dev, sizeof(struct msm_nand_info),
+				GFP_KERNEL);
+	if (!info) {
+		pr_err("Unable to allocate memory for msm_nand_info\n");
+		err = -ENOMEM;
+		goto out;
+	}
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+						"nand_phys");
+	if (!res || !res->start) {
+		pr_err("NAND phys address range is not provided\n");
+		err = -ENODEV;
+		goto out;
+	}
+	info->nand_phys = res->start;
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+						"bam_phys");
+	if (!res || !res->start) {
+		pr_err("BAM phys address range is not provided\n");
+		err = -ENODEV;
+		goto out;
+	}
+	info->bam_phys = res->start;
+	info->bam_base = devm_ioremap(&pdev->dev, res->start,
+					resource_size(res));
+	if (!info->bam_base) {
+		pr_err("BAM ioremap() failed for addr 0x%x size 0x%x\n",
+			res->start, resource_size(res));
+		err = -ENOMEM;
+		goto out;
+	}
+
+	info->bam_irq = platform_get_irq_byname(pdev, "bam_irq");
+	if (info->bam_irq < 0) {
+		pr_err("BAM IRQ is not provided\n");
+		err = -ENODEV;
+		goto out;
+	}
+
+	info->mtd.name = dev_name(&pdev->dev);
+	info->mtd.priv = info;
+	info->mtd.owner = THIS_MODULE;
+	info->nand_chip.dev = &pdev->dev;
+	init_waitqueue_head(&info->nand_chip.dma_wait_queue);
+	mutex_init(&info->bam_lock);
+
+	info->nand_chip.dma_virt_addr =
+		dmam_alloc_coherent(&pdev->dev, MSM_NAND_DMA_BUFFER_SIZE,
+			&info->nand_chip.dma_phys_addr, GFP_KERNEL);
+	if (!info->nand_chip.dma_virt_addr) {
+		pr_err("No memory for DMA buffer size %x\n",
+				MSM_NAND_DMA_BUFFER_SIZE);
+		err = -ENOMEM;
+		goto out;
+	}
+	err = msm_nand_bam_init(info);
+	if (err) {
+		pr_err("msm_nand_bam_init() failed %d\n", err);
+		goto out;
+	}
+	err = msm_nand_enable_dma(info);
+	if (err) {
+		pr_err("Failed to enable DMA in NANDc\n");
+		goto free_bam;
+	}
+	if (msm_nand_scan(&info->mtd)) {
+		pr_err("No nand device found\n");
+		err = -ENXIO;
+		goto free_bam;
+	}
+	parser_data.of_node = pnode;
+	n_parts = 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);
+		goto free_bam;
+	}
+	dev_set_drvdata(&pdev->dev, info);
+
+	pr_info("NANDc phys addr 0x%lx, BAM phys addr 0x%lx, BAM IRQ %d\n",
+			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);
+out:
+	return err;
+}
+
+/*
+ * Remove functionality that gets called when driver/device msm-nand
+ * is removed.
+ */
+static int __devexit msm_nand_remove(struct platform_device *pdev)
+{
+	struct msm_nand_info *info = dev_get_drvdata(&pdev->dev);
+
+	dev_set_drvdata(&pdev->dev, NULL);
+	if (info) {
+		mtd_device_unregister(&info->mtd);
+		msm_nand_bam_free(info);
+	}
+	return 0;
+}
+
+#define DRIVER_NAME "msm_qpic_nand"
+static const struct of_device_id msm_nand_match_table[] = {
+	{ .compatible = "qcom,msm-nand", },
+	{},
+};
+static struct platform_driver msm_nand_driver = {
+	.probe		= msm_nand_probe,
+	.remove		= __devexit_p(msm_nand_remove),
+	.driver = {
+		.name		= DRIVER_NAME,
+		.of_match_table = msm_nand_match_table,
+	},
+};
+
+module_platform_driver(msm_nand_driver);
+
+MODULE_ALIAS(DRIVER_NAME);
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("MSM QPIC NAND flash driver");
diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c
index 2b73d99..d26c845 100644
--- a/drivers/net/usb/usbnet.c
+++ b/drivers/net/usb/usbnet.c
@@ -87,6 +87,8 @@
 
 static const char driver_name [] = "usbnet";
 
+static struct workqueue_struct	*usbnet_wq;
+
 /* use ethtool to change the level for any given device */
 static int msg_level = -1;
 module_param (msg_level, int, 0);
@@ -246,7 +248,7 @@
 	if (skb_defer_rx_timestamp(skb))
 		return;
 
-	status = netif_rx (skb);
+	status = netif_rx_ni(skb);
 	if (status != NET_RX_SUCCESS)
 		netif_dbg(dev, rx_err, dev->net,
 			  "netif_rx status %d\n", status);
@@ -316,7 +318,7 @@
 	spin_lock(&dev->done.lock);
 	__skb_queue_tail(&dev->done, skb);
 	if (dev->done.qlen == 1)
-		tasklet_schedule(&dev->bh);
+		queue_work(usbnet_wq, &dev->bh_w);
 	spin_unlock_irqrestore(&dev->done.lock, flags);
 	return old_state;
 }
@@ -390,7 +392,7 @@
 		default:
 			netif_dbg(dev, rx_err, dev->net,
 				  "rx submit, %d\n", retval);
-			tasklet_schedule (&dev->bh);
+			queue_work(usbnet_wq, &dev->bh_w);
 			break;
 		case 0:
 			usb_mark_last_busy(dev->udev);
@@ -583,7 +585,7 @@
 		num++;
 	}
 
-	tasklet_schedule(&dev->bh);
+	queue_work(usbnet_wq, &dev->bh_w);
 
 	netif_dbg(dev, rx_status, dev->net,
 		  "paused rx queue disabled, %d skbs requeued\n", num);
@@ -652,7 +654,7 @@
 {
 	if (netif_running(dev->net)) {
 		(void) unlink_urbs (dev, &dev->rxq);
-		tasklet_schedule(&dev->bh);
+		queue_work(usbnet_wq, &dev->bh_w);
 	}
 }
 EXPORT_SYMBOL_GPL(usbnet_unlink_rx_urbs);
@@ -726,7 +728,7 @@
 	 */
 	dev->flags = 0;
 	del_timer_sync (&dev->delay);
-	tasklet_kill (&dev->bh);
+	cancel_work_sync(&dev->bh_w);
 	if (info->manage_power)
 		info->manage_power(dev, 0);
 	else
@@ -799,7 +801,7 @@
 		   "simple");
 
 	// delay posting reads until we're fully open
-	tasklet_schedule (&dev->bh);
+	queue_work(usbnet_wq, &dev->bh_w);
 	if (info->manage_power) {
 		retval = info->manage_power(dev, 1);
 		if (retval < 0)
@@ -969,7 +971,7 @@
 					   status);
 		} else {
 			clear_bit (EVENT_RX_HALT, &dev->flags);
-			tasklet_schedule (&dev->bh);
+			queue_work(usbnet_wq, &dev->bh_w);
 		}
 	}
 
@@ -994,7 +996,7 @@
 			usb_autopm_put_interface(dev->intf);
 fail_lowmem:
 			if (resched)
-				tasklet_schedule (&dev->bh);
+				queue_work(usbnet_wq, &dev->bh_w);
 		}
 	}
 
@@ -1080,7 +1082,7 @@
 	struct usbnet		*dev = netdev_priv(net);
 
 	unlink_urbs (dev, &dev->txq);
-	tasklet_schedule (&dev->bh);
+	queue_work(usbnet_wq, &dev->bh_w);
 
 	// FIXME: device recovery -- reset?
 }
@@ -1267,13 +1269,21 @@
 					  "rxqlen %d --> %d\n",
 					  temp, dev->rxq.qlen);
 			if (dev->rxq.qlen < qlen)
-				tasklet_schedule (&dev->bh);
+				queue_work(usbnet_wq, &dev->bh_w);
 		}
 		if (dev->txq.qlen < TX_QLEN (dev))
 			netif_wake_queue (dev->net);
 	}
 }
 
+static void usbnet_bh_w(struct work_struct *work)
+{
+	struct usbnet		*dev =
+		container_of(work, struct usbnet, bh_w);
+	unsigned long param = (unsigned long)dev;
+
+	usbnet_bh(param);
+}
 
 /*-------------------------------------------------------------------------
  *
@@ -1392,8 +1402,7 @@
 	skb_queue_head_init (&dev->txq);
 	skb_queue_head_init (&dev->done);
 	skb_queue_head_init(&dev->rxq_pause);
-	dev->bh.func = usbnet_bh;
-	dev->bh.data = (unsigned long) dev;
+	INIT_WORK(&dev->bh_w, usbnet_bh_w);
 	INIT_WORK (&dev->kevent, kevent);
 	init_usb_anchor(&dev->deferred);
 	dev->delay.function = usbnet_bh;
@@ -1577,7 +1586,7 @@
 		if (test_bit(EVENT_DEV_OPEN, &dev->flags)) {
 			if (!(dev->txq.qlen >= TX_QLEN(dev)))
 				netif_tx_wake_all_queues(dev->net);
-			tasklet_schedule (&dev->bh);
+			queue_work(usbnet_wq, &dev->bh_w);
 		}
 	}
 	return 0;
@@ -1594,12 +1603,20 @@
 		FIELD_SIZEOF(struct sk_buff, cb) < sizeof(struct skb_data));
 
 	random_ether_addr(node_id);
+
+	usbnet_wq  = create_singlethread_workqueue("usbnet");
+	if (!usbnet_wq) {
+		pr_err("%s: Unable to create workqueue:usbnet\n", __func__);
+		return -ENOMEM;
+	}
+
 	return 0;
 }
 module_init(usbnet_init);
 
 static void __exit usbnet_exit(void)
 {
+	destroy_workqueue(usbnet_wq);
 }
 module_exit(usbnet_exit);
 
diff --git a/drivers/net/wireless/wcnss/wcnss_wlan.c b/drivers/net/wireless/wcnss/wcnss_wlan.c
index ad9dc7d..7695778 100644
--- a/drivers/net/wireless/wcnss/wcnss_wlan.c
+++ b/drivers/net/wireless/wcnss/wcnss_wlan.c
@@ -184,6 +184,12 @@
 	return 0;
 }
 
+void wcnss_flush_delayed_boot_votes()
+{
+	flush_delayed_work_sync(&penv->wcnss_work);
+}
+EXPORT_SYMBOL(wcnss_flush_delayed_boot_votes);
+
 static int __devexit
 wcnss_wlan_ctrl_remove(struct platform_device *pdev)
 {
diff --git a/drivers/power/smb349.c b/drivers/power/smb349.c
index ffc92d5..f9ca81c 100644
--- a/drivers/power/smb349.c
+++ b/drivers/power/smb349.c
@@ -617,6 +617,8 @@
 
 	the_smb349_chg = smb349_chg;
 
+	spin_lock_init(&smb349_chg->lock);
+
 	create_debugfs_entries(smb349_chg);
 	INIT_WORK(&smb349_chg->chg_work, chg_worker);
 
diff --git a/drivers/usb/gadget/f_mtp.c b/drivers/usb/gadget/f_mtp.c
index 0394b0b..96790c5 100644
--- a/drivers/usb/gadget/f_mtp.c
+++ b/drivers/usb/gadget/f_mtp.c
@@ -788,7 +788,8 @@
 			/* wait for our last read to complete */
 			ret = wait_event_interruptible(dev->read_wq,
 				dev->rx_done || dev->state != STATE_BUSY);
-			if (dev->state == STATE_CANCELED) {
+			if (dev->state == STATE_CANCELED
+					|| dev->state == STATE_OFFLINE) {
 				r = -ECANCELED;
 				if (!dev->rx_done)
 					usb_ep_dequeue(dev->ep_out, read_req);
diff --git a/drivers/usb/gadget/msm72k_udc.c b/drivers/usb/gadget/msm72k_udc.c
index 297c183..55fd59e 100644
--- a/drivers/usb/gadget/msm72k_udc.c
+++ b/drivers/usb/gadget/msm72k_udc.c
@@ -702,6 +702,14 @@
 
 	spin_lock_irqsave(&ui->lock, flags);
 
+	if (ept->num != 0 && ept->ep.desc == NULL) {
+		req->req.status = -EINVAL;
+		spin_unlock_irqrestore(&ui->lock, flags);
+		dev_err(&ui->pdev->dev,
+			"%s: called for disabled endpoint\n", __func__);
+		return -EINVAL;
+	}
+
 	if (req->busy) {
 		req->req.status = -EBUSY;
 		spin_unlock_irqrestore(&ui->lock, flags);
diff --git a/drivers/usb/otg/msm_otg.c b/drivers/usb/otg/msm_otg.c
index 487bc59..da96e73 100644
--- a/drivers/usb/otg/msm_otg.c
+++ b/drivers/usb/otg/msm_otg.c
@@ -340,15 +340,28 @@
 {
 	int ret;
 
-	if (IS_ERR(motg->clk))
-		return 0;
-
 	if (assert) {
-		ret = clk_reset(motg->clk, CLK_RESET_ASSERT);
+		if (!IS_ERR(motg->clk)) {
+			ret = clk_reset(motg->clk, CLK_RESET_ASSERT);
+		} else {
+			/* Using asynchronous block reset to the hardware */
+			dev_dbg(motg->phy.dev, "block_reset ASSERT\n");
+			clk_disable_unprepare(motg->pclk);
+			clk_disable_unprepare(motg->core_clk);
+			ret = clk_reset(motg->core_clk, CLK_RESET_ASSERT);
+		}
 		if (ret)
 			dev_err(motg->phy.dev, "usb hs_clk assert failed\n");
 	} else {
-		ret = clk_reset(motg->clk, CLK_RESET_DEASSERT);
+		if (!IS_ERR(motg->clk)) {
+			ret = clk_reset(motg->clk, CLK_RESET_DEASSERT);
+		} else {
+			dev_dbg(motg->phy.dev, "block_reset DEASSERT\n");
+			ret = clk_reset(motg->core_clk, CLK_RESET_DEASSERT);
+			ndelay(200);
+			clk_prepare_enable(motg->core_clk);
+			clk_prepare_enable(motg->pclk);
+		}
 		if (ret)
 			dev_err(motg->phy.dev, "usb hs_clk deassert failed\n");
 	}
diff --git a/include/linux/coresight.h b/include/linux/coresight.h
index f03a493..b6064a4 100644
--- a/include/linux/coresight.h
+++ b/include/linux/coresight.h
@@ -47,53 +47,107 @@
 enum coresight_dev_type {
 	CORESIGHT_DEV_TYPE_SINK,
 	CORESIGHT_DEV_TYPE_LINK,
+	CORESIGHT_DEV_TYPE_LINKSINK,
 	CORESIGHT_DEV_TYPE_SOURCE,
-	CORESIGHT_DEV_TYPE_MAX,
+};
+
+enum coresight_dev_subtype_sink {
+	CORESIGHT_DEV_SUBTYPE_SINK_NONE,
+	CORESIGHT_DEV_SUBTYPE_SINK_PORT,
+	CORESIGHT_DEV_SUBTYPE_SINK_BUFFER,
+};
+
+enum coresight_dev_subtype_link {
+	CORESIGHT_DEV_SUBTYPE_LINK_NONE,
+	CORESIGHT_DEV_SUBTYPE_LINK_MERG,
+	CORESIGHT_DEV_SUBTYPE_LINK_SPLIT,
+	CORESIGHT_DEV_SUBTYPE_LINK_FIFO,
+};
+
+enum coresight_dev_subtype_source {
+	CORESIGHT_DEV_SUBTYPE_SOURCE_NONE,
+	CORESIGHT_DEV_SUBTYPE_SOURCE_PROC,
+	CORESIGHT_DEV_SUBTYPE_SOURCE_BUS,
+	CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE,
+};
+
+struct coresight_dev_subtype {
+	enum coresight_dev_subtype_sink sink_subtype;
+	enum coresight_dev_subtype_link link_subtype;
+	enum coresight_dev_subtype_source source_subtype;
+};
+
+struct coresight_platform_data {
+	int id;
+	const char *name;
+	int nr_inports;
+	const int *outports;
+	const int *child_ids;
+	const int *child_ports;
+	int nr_outports;
+	bool default_sink;
+};
+
+struct coresight_desc {
+	enum coresight_dev_type type;
+	struct coresight_dev_subtype subtype;
+	const struct coresight_ops *ops;
+	struct coresight_platform_data *pdata;
+	struct device *dev;
+	const struct attribute_group **groups;
+	struct module *owner;
 };
 
 struct coresight_connection {
+	int outport;
 	int child_id;
 	int child_port;
 	struct coresight_device *child_dev;
 	struct list_head link;
 };
 
+struct coresight_refcnt {
+	int sink_refcnt;
+	int *link_refcnts;
+	int source_refcnt;
+};
+
 struct coresight_device {
 	int id;
 	struct coresight_connection *conns;
 	int nr_conns;
+	enum coresight_dev_type type;
+	struct coresight_dev_subtype subtype;
 	const struct coresight_ops *ops;
 	struct device dev;
-	struct mutex mutex;
-	int *refcnt;
-	struct list_head link;
+	struct coresight_refcnt refcnt;
+	struct list_head dev_link;
+	struct list_head path_link;
 	struct module *owner;
 	bool enable;
 };
 
 #define to_coresight_device(d) container_of(d, struct coresight_device, dev)
 
+struct coresight_ops_sink {
+	int (*enable)(struct coresight_device *csdev);
+	void (*disable)(struct coresight_device *csdev);
+};
+
+struct coresight_ops_link {
+	int (*enable)(struct coresight_device *csdev, int iport, int oport);
+	void (*disable)(struct coresight_device *csdev, int iport, int oport);
+};
+
+struct coresight_ops_source {
+	int (*enable)(struct coresight_device *csdev);
+	void (*disable)(struct coresight_device *csdev);
+};
+
 struct coresight_ops {
-	int (*enable)(struct coresight_device *csdev, int port);
-	void (*disable)(struct coresight_device *csdev, int port);
-};
-
-struct coresight_platform_data {
-	int id;
-	const char *name;
-	int nr_ports;
-	int *child_ids;
-	int *child_ports;
-	int nr_children;
-};
-
-struct coresight_desc {
-	enum coresight_dev_type type;
-	const struct coresight_ops *ops;
-	struct coresight_platform_data *pdata;
-	struct device *dev;
-	const struct attribute_group **groups;
-	struct module *owner;
+	const struct coresight_ops_sink *sink_ops;
+	const struct coresight_ops_link *link_ops;
+	const struct coresight_ops_source *source_ops;
 };
 
 struct qdss_source {
@@ -109,24 +163,29 @@
 };
 
 
-extern struct coresight_device *
-coresight_register(struct coresight_desc *desc);
-extern void coresight_unregister(struct coresight_device *csdev);
-extern int coresight_enable(struct coresight_device *csdev, int port);
-extern void coresight_disable(struct coresight_device *csdev, int port);
-
 #ifdef CONFIG_MSM_QDSS
 extern struct qdss_source *qdss_get(const char *name);
 extern void qdss_put(struct qdss_source *src);
 extern int qdss_enable(struct qdss_source *src);
 extern void qdss_disable(struct qdss_source *src);
 extern void qdss_disable_sink(void);
+extern struct coresight_device *
+coresight_register(struct coresight_desc *desc);
+extern void coresight_unregister(struct coresight_device *csdev);
+extern int coresight_enable(struct coresight_device *csdev);
+extern void coresight_disable(struct coresight_device *csdev);
 #else
 static inline struct qdss_source *qdss_get(const char *name) { return NULL; }
 static inline void qdss_put(struct qdss_source *src) {}
 static inline int qdss_enable(struct qdss_source *src) { return -ENOSYS; }
 static inline void qdss_disable(struct qdss_source *src) {}
 static inline void qdss_disable_sink(void) {}
+static inline struct coresight_device *
+coresight_register(struct coresight_desc *desc) { return NULL; }
+static inline void coresight_unregister(struct coresight_device *csdev) {}
+static inline int
+coresight_enable(struct coresight_device *csdev) { return -ENOSYS; }
+static inline void coresight_disable(struct coresight_device *csdev) {}
 #endif
 
 #endif
diff --git a/include/linux/usb/usbnet.h b/include/linux/usb/usbnet.h
index 76f4396..72f5c96 100644
--- a/include/linux/usb/usbnet.h
+++ b/include/linux/usb/usbnet.h
@@ -56,7 +56,7 @@
 	struct sk_buff_head	rxq_pause;
 	struct urb		*interrupt;
 	struct usb_anchor	deferred;
-	struct tasklet_struct	bh;
+	struct work_struct	bh_w;
 
 	struct work_struct	kevent;
 	unsigned long		flags;
diff --git a/include/linux/wcnss_wlan.h b/include/linux/wcnss_wlan.h
index d7e65b0..46a5b1b 100644
--- a/include/linux/wcnss_wlan.h
+++ b/include/linux/wcnss_wlan.h
@@ -47,6 +47,7 @@
 int req_riva_power_on_lock(char *driver_name);
 int free_riva_power_on_lock(char *driver_name);
 unsigned int wcnss_get_serial_number(void);
+void wcnss_flush_delayed_boot_votes(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/msm_camera.h b/include/media/msm_camera.h
index ae81dcd..57ce7c0 100644
--- a/include/media/msm_camera.h
+++ b/include/media/msm_camera.h
@@ -213,6 +213,10 @@
 #define MSM_CAM_IOCTL_GET_INST_HANDLE \
 	_IOR(MSM_CAM_IOCTL_MAGIC, 60, uint32_t *)
 
+#define MSM_CAM_IOCTL_STATS_UNREG_BUF \
+	_IOR(MSM_CAM_IOCTL_MAGIC, 61, struct msm_stats_flush_bufq *)
+
+
 struct msm_stats_reqbuf {
 	int num_buf;		/* how many buffers requested */
 	int stats_type;	/* stats type */
@@ -468,6 +472,7 @@
 #define CMD_AXI_CFG_ZSL 43
 #define CMD_AXI_CFG_SNAP_VPE 44
 #define CMD_AXI_CFG_SNAP_THUMB_VPE 45
+
 #define CMD_CONFIG_PING_ADDR 46
 #define CMD_CONFIG_PONG_ADDR 47
 #define CMD_CONFIG_FREE_BUF_ADDR 48
@@ -475,6 +480,13 @@
 #define CMD_AXI_CFG_VIDEO_ALL_CHNLS 50
 #define CMD_VFE_BUFFER_RELEASE 51
 #define CMD_VFE_PROCESS_IRQ 52
+#define CMD_STATS_BG_ENABLE 53
+#define CMD_STATS_BF_ENABLE 54
+#define CMD_STATS_BHIST_ENABLE 55
+#define CMD_STATS_BG_BUF_RELEASE 56
+#define CMD_STATS_BF_BUF_RELEASE 57
+#define CMD_STATS_BHIST_BUF_RELEASE 58
+
 
 #define CMD_AXI_CFG_PRIM               BIT(8)
 #define CMD_AXI_CFG_PRIM_ALL_CHNLS     BIT(9)
@@ -524,7 +536,10 @@
 #define MSM_PMEM_C2D			17
 #define MSM_PMEM_MAINIMG_VPE    18
 #define MSM_PMEM_THUMBNAIL_VPE  19
-#define MSM_PMEM_MAX            20
+#define MSM_PMEM_BAYER_GRID		20
+#define MSM_PMEM_BAYER_FOCUS	21
+#define MSM_PMEM_BAYER_HIST		22
+#define MSM_PMEM_MAX            23
 
 #define STAT_AEAW			0
 #define STAT_AEC			1
@@ -534,7 +549,10 @@
 #define STAT_CS				5
 #define STAT_IHIST			6
 #define STAT_SKIN			7
-#define STAT_MAX			8
+#define STAT_BG				8
+#define STAT_BF				9
+#define STAT_BHIST			10
+#define STAT_MAX			11
 
 #define FRAME_PREVIEW_OUTPUT1		0
 #define FRAME_PREVIEW_OUTPUT2		1
@@ -1858,6 +1876,12 @@
 	struct msm_cpp_frame_strip_info *strip_info;
 };
 
+struct msm_ver_num_info {
+	uint32_t main;
+	uint32_t minor;
+	uint32_t rev;
+};
+
 #define VIDIOC_MSM_CPP_CFG \
 	_IOWR('V', BASE_VIDIOC_PRIVATE, struct msm_camera_v4l2_ioctl_t)
 
diff --git a/include/media/msm_isp.h b/include/media/msm_isp.h
index 3df6ded..0ee7417 100644
--- a/include/media/msm_isp.h
+++ b/include/media/msm_isp.h
@@ -62,6 +62,10 @@
 #define MSG_ID_OUTPUT_TERTIARY1         43
 #define MSG_ID_STOP_LS_ACK              44
 #define MSG_ID_OUTPUT_TERTIARY2         45
+#define MSG_ID_STATS_BG                 46
+#define MSG_ID_STATS_BF                 47
+#define MSG_ID_STATS_BHIST              48
+
 
 /* ISP command IDs */
 #define VFE_CMD_DUMMY_0                                 0
@@ -206,6 +210,13 @@
 #define VFE_CMD_STATS_REQBUF                            139
 #define VFE_CMD_STATS_ENQUEUEBUF                        140
 #define VFE_CMD_STATS_FLUSH_BUFQ                        141
+#define VFE_CMD_STATS_UNREGBUF                          142
+#define VFE_CMD_STATS_BG_START                          143
+#define VFE_CMD_STATS_BG_STOP                           144
+#define VFE_CMD_STATS_BF_START                          145
+#define VFE_CMD_STATS_BF_STOP                           146
+#define VFE_CMD_STATS_BHIST_START                       147
+#define VFE_CMD_STATS_BHIST_STOP                        148
 
 struct msm_isp_cmd {
 	int32_t  id;
diff --git a/include/media/vcap_fmt.h b/include/media/vcap_fmt.h
index 92240bf1..00e0375 100644
--- a/include/media/vcap_fmt.h
+++ b/include/media/vcap_fmt.h
@@ -44,6 +44,43 @@
 	HAL_VCAP_RGB,
 };
 
+enum nr_mode {
+	NR_DISABLE = 0,
+	NR_AUTO,
+	NR_MANUAL,
+};
+
+enum nr_decay_ratio {
+	NR_Decay_Ratio_26 = 0,
+	NR_Decay_Ratio_25,
+	NR_Decay_Ratio_24,
+	NR_Decay_Ratio_23,
+	NR_Decay_Ratio_22,
+	NR_Decay_Ratio_21,
+	NR_Decay_Ratio_20,
+	NR_Decay_Ratio_19,
+};
+
+struct nr_config {
+	uint8_t max_blend_ratio;
+	uint8_t scale_diff_ratio;
+	uint8_t diff_limit_ratio;
+	uint8_t scale_motion_ratio;
+	uint8_t blend_limit_ratio;
+};
+
+struct nr_param {
+	enum nr_mode mode;
+	enum nr_decay_ratio decay_ratio;
+	uint8_t window;
+	struct nr_config luma;
+	struct nr_config chroma;
+};
+
+#define VCAPIOC_NR_S_PARAMS _IOWR('V', (BASE_VIDIOC_PRIVATE+0), struct nr_param)
+
+#define VCAPIOC_NR_G_PARAMS _IOWR('V', (BASE_VIDIOC_PRIVATE+1), struct nr_param)
+
 struct v4l2_format_vc_ext {
 	enum hal_vcap_mode     mode;
 	enum hal_vcap_polar    h_polar;
diff --git a/include/media/vcap_v4l2.h b/include/media/vcap_v4l2.h
index 9719aa6..390a843 100644
--- a/include/media/vcap_v4l2.h
+++ b/include/media/vcap_v4l2.h
@@ -120,7 +120,8 @@
 
 	void					*bufMotion;
 	struct nr_buffer		bufNR;
-	bool					nr_enabled;
+	struct nr_param			nr_param;
+	bool					nr_update;
 };
 
 struct vp_work_t {
diff --git a/sound/soc/msm/Kconfig b/sound/soc/msm/Kconfig
index 9b60a56..e56b1da 100644
--- a/sound/soc/msm/Kconfig
+++ b/sound/soc/msm/Kconfig
@@ -159,6 +159,7 @@
 	select SND_SOC_QDSP6V2
 	select SND_SOC_MSM_STUB
 	select SND_SOC_MSM_HOSTLESS_PCM
+	select SND_SOC_WCD9320
 	select SND_DYNAMIC_MINORS
 	help
 	 To add support for SoC audio on MSM8974.
diff --git a/sound/soc/msm/apq8064.c b/sound/soc/msm/apq8064.c
index d8a4624..2fcf29b 100644
--- a/sound/soc/msm/apq8064.c
+++ b/sound/soc/msm/apq8064.c
@@ -1152,8 +1152,9 @@
 	snd_soc_dapm_sync(dapm);
 
 	err = snd_soc_jack_new(codec, "Headset Jack",
-		(SND_JACK_HEADSET | SND_JACK_OC_HPHL | SND_JACK_OC_HPHR),
-		&hs_jack);
+			       (SND_JACK_HEADSET | SND_JACK_OC_HPHL |
+				SND_JACK_OC_HPHR | SND_JACK_UNSUPPORTED),
+			       &hs_jack);
 	if (err) {
 		pr_err("failed to create new jack\n");
 		return err;
diff --git a/sound/soc/msm/mpq8064.c b/sound/soc/msm/mpq8064.c
index 6685ce5..f5bbf56 100644
--- a/sound/soc/msm/mpq8064.c
+++ b/sound/soc/msm/mpq8064.c
@@ -1165,8 +1165,6 @@
 		.platform_name  = "msm-pcm-afe",
 		.ignore_suspend = 1,
 		.ignore_pmdown_time = 1, /* this dainlink has playback support */
-		.codec_dai_name = "snd-soc-dummy-dai",
-		.codec_name = "snd-soc-dummy",
 	},
 	{
 		.name = "MSM AFE-PCM TX",
@@ -1176,8 +1174,6 @@
 		.codec_dai_name = "msm-stub-tx",
 		.platform_name  = "msm-pcm-afe",
 		.ignore_suspend = 1,
-		.codec_dai_name = "snd-soc-dummy-dai",
-		.codec_name = "snd-soc-dummy",
 	},
 	{
 		.name = "MSM8960 Compr1",