Merge "msm: sysmon: Introduce sysmon_get_reason() for querying failure reasons" into msm-3.0
diff --git a/Documentation/devicetree/bindings/arm/msm/lpm-levels.txt b/Documentation/devicetree/bindings/arm/msm/lpm-levels.txt
new file mode 100644
index 0000000..35385d3
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/msm/lpm-levels.txt
@@ -0,0 +1,47 @@
+* Low Power Management Levels
+
+The application processor in MSM can do a variety of C-States for low power
+management. These C-States are invoked by the CPUIdle framework when the core
+becomes idle. But based on the time available until the next scheduled wakeup,
+the system can do a combination of low power modes of different resources -
+L2, XO, Vdd Dig and Vdd Mem. The combination is captured in the device tree as
+lpm-level. The units for voltage are dependent on the PMIC used on the target
+and are in uV.
+
+The required nodes for lpm-levels are:
+
+- compatible: "qcom,lpm-levels"
+- reg: The numeric level id
+- qcom,mode: The sleep mode of the processor
+- qcom,xo: The state of XO clock.
+- qcom,l2: The state of L2 cache.
+- qcom,vdd-mem-upper-bound: The upper bound value of mem voltage in uV
+- qcom,vdd-mem-lower-bound: The lower bound value of mem voltage in uV
+- qcom,vdd-dig-upper-bound: The upper bound value of dig voltage in uV
+- qcom,vdd-dig-lower-bound: The lower bound value of dig voltage in uV
+- qcom,latency-us: The latency in handling the interrupt if this level was
+	chosen, in uSec
+- qcom,ss-power: The steady state power expelled when the processor is in this
+	level in mWatts
+- qcom,energy-overhead: The energy used up in entering and exiting this level
+	in mWatts.uSec
+- qcom,time-overhead: The time spent in entering and exiting this level in uS
+
+Example:
+
+qcom,lpm-levels {
+	qcom,lpm-level@0 {
+		reg = <0>;
+		qcom,mode = <0>;        /* MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT */
+		qcom,xo = <1>;          /* ON */
+		qcom,l2 = <3>;          /* ACTIVE */
+		qcom,vdd-mem-upper-bound = <1150000>; /* MAX */
+		qcom,vdd-mem-lower-bound = <1050000>; /* ACTIVE */
+		qcom,vdd-dig-upper-bound = <1150000>; /* MAX */
+		qcom,vdd-dig-lower-bound = <950000>;  /* ACTIVE */
+		qcom,latency-us = <100>;
+		qcom,ss-power = <650>;
+		qcom,energy-overhead = <801>;
+		qcom,time-overhead = <200>;
+	};
+};
diff --git a/Documentation/devicetree/bindings/arm/msm/pm-boot.txt b/Documentation/devicetree/bindings/arm/msm/pm-boot.txt
new file mode 100644
index 0000000..cce9d0e
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/msm/pm-boot.txt
@@ -0,0 +1,40 @@
+* Power Management boot configuration (pm-boot)
+
+Low power management drivers need to specify the warmboot entry path for the
+application processors to resume from sleep/suspend. The boot configuration
+can vary if the core does/does not support a secure boot mode. The secure
+boot configuration boots the core sets up the core sub system registers and
+calls into the kernel entry point. In the absence of a secure boot mode, the
+core when powered on from reset will need to be configured with the warmboot
+entry pointer. The physical and the virtual address for the entry pointer
+need to provided to the driver.
+
+
+The device tree parameters for pm-boot are:
+
+Required parameters:
+
+- compatible: Must be "qcom,pm-boot"
+- qcom,mode: The mode that the target will use for booting
+	MSM_PM_BOOT_CONFIG_TZ                = 0,
+	MSM_PM_BOOT_CONFIG_RESET_VECTOR_PHYS = 1,
+	MSM_PM_BOOT_CONFIG_RESET_VECTOR_VIRT = 2,
+	MSM_PM_BOOT_CONFIG_REMAP_BOOT_ADDR   = 3,
+
+Optional parameters (based on the mode chosen):
+
+- qcom,phy-addr: The physical address that will be used for warmboot entry if
+	the processor remap register can be programmed.
+	Needed for modes = { MSM_PM_BOOT_CONFIG_RESET_VECTOR_PHYS,
+				MSM_PM_BOOT_CONFIG_REMAP_BOOT_ADDR }
+- qcom,virt-addr: The virtual address at which the processor start booting from
+	Needed for modes = { MSM_PM_BOOT_CONFIG_RESET_VECTOR_VIRT,
+				MSM_PM_BOOT_CONFIG_REMAP_BOOT_ADDR }
+
+
+Example:
+
+	qcom,pm-boot {
+		compatible = "qcom,pm-boot";
+		qcom,mode = <0>; /* MSM_PM_BOOT_CONFIG_TZ */
+	};
diff --git a/Documentation/genlock.txt b/Documentation/genlock.txt
index 6f24a76..cd82614 100644
--- a/Documentation/genlock.txt
+++ b/Documentation/genlock.txt
@@ -34,6 +34,12 @@
 space and kernel space to allow a kernel driver to unlock or lock a buffer
 on behalf of a user process.
 
+Locks within a process using a single genlock handle follow the same rules for
+exclusive write locks with multiple readers. Genlock cannot provide deadlock
+protection because the same handle can be used simultaneously by a producer and
+consumer. In practice in the event that the client creates a deadlock an error
+will still be generated when the timeout expires.
+
 Kernel API
 
 Access to the genlock API can either be via the in-kernel API or via an
@@ -137,7 +143,12 @@
  * GENLOCK_UNLOCK - unlock an existing lock
 
 Pass flags in genlock_lock.flags:
- * GENLOCK_NOBLOCK - Do not block if the lock is already taken
+ * GENLOCK_NOBLOCK       - Do not block if the lock is already taken
+ * GENLOCK_WRITE_TO_READ - Convert a write lock that the handle owns to a read
+                           lock. For instance graphics may hold a write lock
+                           while rendering the back buffer then when swapping
+                           convert the lock to a read lock to copy the front
+                           buffer in the next frame for preserved buffers.
 
 Pass a timeout value in milliseconds in genlock_lock.timeout.
 genlock_lock.flags and genlock_lock.timeout are not used for UNLOCK.
diff --git a/arch/arm/boot/dts/msmcopper.dtsi b/arch/arm/boot/dts/msmcopper.dtsi
index 7dfa4df..8406299 100644
--- a/arch/arm/boot/dts/msmcopper.dtsi
+++ b/arch/arm/boot/dts/msmcopper.dtsi
@@ -39,7 +39,7 @@
 
 	timer {
 		compatible = "qcom,msm-qtimer", "arm,armv7-timer";
-		interrupts = <1 2 0>;
+		interrupts = <1 2 0 1 3 0>;
 		clock-frequency = <19200000>;
 	};
 
diff --git a/arch/arm/boot/dts/msmcopper_pm.dtsi b/arch/arm/boot/dts/msmcopper_pm.dtsi
index 53ad0d1..0da3200 100644
--- a/arch/arm/boot/dts/msmcopper_pm.dtsi
+++ b/arch/arm/boot/dts/msmcopper_pm.dtsi
@@ -131,4 +131,150 @@
 				07 01 b0 78 80 12 44 a0 50
 				3b 60 02 32 a0 50 0f];
 	};
+
+	qcom,lpm-levels {
+		compatible = "qcom,lpm-levels";
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		qcom,lpm-level@0 {
+			reg = <0x0>;
+			qcom,mode = <0>;        /* MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT */
+			qcom,xo = <1>;          /* ON */
+			qcom,l2 = <3>;          /* ACTIVE */
+			qcom,vdd-mem-upper-bound = <1150000>; /* MAX */
+			qcom,vdd-mem-lower-bound = <1050000>; /* ACTIVE */
+			qcom,vdd-dig-upper-bound = <1150000>; /* MAX */
+			qcom,vdd-dig-lower-bound = <950000>;  /* ACTIVE */
+			qcom,latency-us = <100>;
+			qcom,ss-power = <650>;
+			qcom,energy-overhead = <801>;
+			qcom,time-overhead = <200>;
+		};
+
+		qcom,lpm-level@1 {
+			reg = <0x1>;
+			qcom,mode = <2>;        /* MSM_PM_SLEEP_MODE_STANDALONE_POWER_COLLAPSE */
+			qcom,xo = <1>;          /* ON */
+			qcom,l2 = <3>;          /* ACTIVE */
+			qcom,vdd-mem-upper-bound = <1150000>; /* MAX */
+			qcom,vdd-mem-lower-bound = <1050000>; /* ACTIVE */
+			qcom,vdd-dig-upper-bound = <1150000>; /* MAX */
+			qcom,vdd-dig-lower-bound = <950000>;  /* ACTIVE */
+			qcom,latency-us = <2000>;
+			qcom,ss-power = <200>;
+			qcom,energy-overhead = <576000>;
+			qcom,time-overhead = <2000>;
+		};
+
+		qcom,lpm-level@2 {
+			reg = <0x2>;
+			qcom,mode = <3>;        /* MSM_PM_SLEEP_MODE_POWER_COLLAPSE */
+			qcom,xo = <1>;          /* ON */
+			qcom,l2 = <1>;          /* GDHS */
+			qcom,vdd-mem-upper-bound = <1150000>; /* MAX */
+			qcom,vdd-mem-lower-bound = <1050000>; /* ACTIVE */
+			qcom,vdd-dig-upper-bound = <1150000>; /* MAX */
+			qcom,vdd-dig-lower-bound = <950000>;  /* ACTIVE */
+			qcom,latency-us = <8500>;
+			qcom,ss-power = <51>;
+			qcom,energy-overhead = <1122000>;
+			qcom,time-overhead = <8500>;
+		};
+
+		qcom,lpm-level@3 {
+			reg = <0x3>;
+			qcom,mode = <3>;        /* MSM_PM_SLEEP_MODE_POWER_COLLAPSE */
+			qcom,xo = <1>;          /* ON */
+			qcom,l2 = <0>;          /* OFF */
+			qcom,vdd-mem-upper-bound = <1150000>; /* MAX */
+			qcom,vdd-mem-lower-bound = <1050000>; /* ACTIVE */
+			qcom,vdd-dig-upper-bound = <1150000>; /* MAX */
+			qcom,vdd-dig-lower-bound = <950000>;  /* ACTIVE */
+			qcom,latency-us = <9000>;
+			qcom,ss-power = <51>;
+			qcom,energy-overhead = <1130300>;
+			qcom,time-overhead = <9000>;
+		};
+
+		qcom,lpm-level@4 {
+			reg = <0x4>;
+			qcom,mode = <3>;        /* MSM_PM_SLEEP_MODE_POWER_COLLAPSE */
+			qcom,xo = <1>;          /* ON */
+			qcom,l2 = <0>;          /* OFF */
+			qcom,vdd-mem-upper-bound = <1050000>; /* ACTIVE */
+			qcom,vdd-mem-lower-bound = <750000>;  /* RETENTION HIGH */
+			qcom,vdd-dig-upper-bound = <950000>;  /* ACTIVE */
+			qcom,vdd-dig-lower-bound = <750000>;  /* RETENTION HIGH */
+			qcom,latency-us = <10000>;
+			qcom,ss-power = <51>;
+			qcom,energy-overhead = <1130300>;
+			qcom,time-overhead = <10000>;
+		};
+
+		qcom,lpm-level@5 {
+			reg = <0x5>;
+			qcom,mode = <3>;        /* MSM_PM_SLEEP_MODE_POWER_COLLAPSE */
+			qcom,xo = <0>;          /* OFF */
+			qcom,l2 = <1>;          /* GDHS */
+			qcom,vdd-mem-upper-bound = <1150000>; /* MAX */
+			qcom,vdd-mem-lower-bound = <1050000>; /* ACTIVE */
+			qcom,vdd-dig-upper-bound = <1150000>; /* MAX */
+			qcom,vdd-dig-lower-bound = <950000>;  /* ACTIVE */
+			qcom,latency-us = <12000>;
+			qcom,ss-power = <14>;
+			qcom,energy-overhead = <2205900>;
+			qcom,time-overhead = <12000>;
+		};
+
+		qcom,lpm-level@6 {
+			reg = <0x6>;
+			qcom,mode = <3>;        /* MSM_PM_SLEEP_MODE_POWER_COLLAPSE */
+			qcom,xo = <0>;          /* OFF */
+			qcom,l2 = <0>;          /* OFF */
+			qcom,vdd-mem-upper-bound = <1150000>; /* MAX */
+			qcom,vdd-mem-lower-bound = <1050000>; /* ACTIVE */
+			qcom,vdd-dig-upper-bound = <1150000>; /* MAX */
+			qcom,vdd-dig-lower-bound = <950000>;  /* ACTIVE */
+			qcom,latency-us = <18000>;
+			qcom,ss-power = <12>;
+			qcom,energy-overhead = <2364250>;
+			qcom,time-overhead = <18000>;
+		};
+
+		qcom,lpm-level@7 {
+			reg = <0x7>;
+			qcom,mode= <3>;         /* MSM_PM_SLEEP_MODE_POWER_COLLAPSE */
+			qcom,xo = <0>;          /* OFF */
+			qcom,l2 = <0>;          /* OFF */
+			qcom,vdd-mem-upper-bound = <1050000>; /* ACTIVE */
+			qcom,vdd-mem-lower-bound = <750000>;  /* RETENTION HIGH */
+			qcom,vdd-dig-upper-bound = <950000>;  /* ACTIVE */
+			qcom,vdd-dig-lower-bound = <750000>;  /* RETIONTION HIGH */
+			qcom,latency-us = <23500>;
+			qcom,ss-power = <10>;
+			qcom,energy-overhead = <2667000>;
+			qcom,time-overhead = <23500>;
+		};
+
+		qcom,lpm-level@8 {
+			reg = <0x8>;
+			qcom,mode= <3>;         /* MSM_PM_SLEEP_MODE_POWER_COLLAPSE */
+			qcom,xo = <0>;          /* OFF */
+			qcom,l2 = <0>;          /* OFF */
+			qcom,vdd-mem-upper-bound = <750000>; /* RETENTION HIGH */
+			qcom,vdd-mem-lower-bound = <750000>; /* RETENTION LOW */
+			qcom,vdd-dig-upper-bound = <750000>; /* RETENTION HIGH */
+			qcom,vdd-dig-lower-bound = <500000>; /* RETENTION LOW */
+			qcom,latency-us = <29700>;
+			qcom,ss-power = <5>;
+			qcom,energy-overhead = <2867000>;
+			qcom,time-overhead = <30000>;
+		};
+	};
+
+	qcom,pm-boot {
+		compatible = "qcom,pm-boot";
+		qcom,mode = <0>; /* MSM_PM_BOOT_CONFIG_TZ */
+	};
 };
diff --git a/arch/arm/configs/msm9615_defconfig b/arch/arm/configs/msm9615_defconfig
index 601f235..0d965b6 100644
--- a/arch/arm/configs/msm9615_defconfig
+++ b/arch/arm/configs/msm9615_defconfig
@@ -162,7 +162,7 @@
 CONFIG_SCSI_LOGGING=y
 CONFIG_SCSI_SCAN_ASYNC=y
 CONFIG_NETDEVICES=y
-CONFIG_ATH6K_LEGACY=y
+CONFIG_ATH6K_LEGACY_EXT=y
 # CONFIG_MSM_RMNET is not set
 CONFIG_MSM_RMNET_BAM=y
 # CONFIG_INPUT_MOUSEDEV is not set
diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig
index 7c7c0bb..7c117e8 100644
--- a/arch/arm/mach-msm/Kconfig
+++ b/arch/arm/mach-msm/Kconfig
@@ -245,6 +245,8 @@
 	select MSM_PIL
 	select MSM_SPM_V2
 	select MSM_L2_SPM
+	select MSM_RPM
+	select MSM_PM8X60 if PM
 
 config ARCH_FSM9XXX
 	bool "FSM9XXX"
diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile
index 3c4b5a8..860d7fd 100644
--- a/arch/arm/mach-msm/Makefile
+++ b/arch/arm/mach-msm/Makefile
@@ -201,6 +201,7 @@
 	obj-$(CONFIG_ARCH_MSM8960) += cpuidle.o
 	obj-$(CONFIG_ARCH_MSM8X60) += cpuidle.o
 	obj-$(CONFIG_ARCH_MSM9615) += cpuidle.o
+	obj-$(CONFIG_ARCH_MSMCOPPER) += cpuidle.o
 endif
 
 ifdef CONFIG_MSM_CAMERA_V4L2
@@ -293,7 +294,14 @@
 obj-$(CONFIG_HTC_HEADSET) += htc_headset.o
 obj-$(CONFIG_MSM_RMT_STORAGE_CLIENT) += rmt_storage_client.o
 obj-$(CONFIG_MSM_SDIO_SMEM) += sdio_smem.o
-obj-$(CONFIG_MSM_RPM) += rpm.o rpm_resources.o
+obj-$(CONFIG_MSM_RPM) += rpm.o
+ifdef CONFIG_MSM_RPM
+	obj-$(CONFIG_ARCH_APQ8064) += rpm_resources.o
+	obj-$(CONFIG_ARCH_MSM8960) += rpm_resources.o
+	obj-$(CONFIG_ARCH_MSM8X60) += rpm_resources.o
+	obj-$(CONFIG_ARCH_MSM9615) += rpm_resources.o
+	obj-$(CONFIG_ARCH_MSMCOPPER) += lpm_levels.o
+endif
 obj-$(CONFIG_MSM_MPM) += mpm.o
 obj-$(CONFIG_MSM_RPM_STATS_LOG) += rpm_stats.o
 obj-$(CONFIG_MSM_RPM_LOG) += rpm_log.o
diff --git a/arch/arm/mach-msm/bam_dmux.c b/arch/arm/mach-msm/bam_dmux.c
index eba5637..befa5bb 100644
--- a/arch/arm/mach-msm/bam_dmux.c
+++ b/arch/arm/mach-msm/bam_dmux.c
@@ -241,6 +241,8 @@
 static int disconnect_ack;
 static LIST_HEAD(bam_other_notify_funcs);
 static DEFINE_MUTEX(smsm_cb_lock);
+static DEFINE_MUTEX(delayed_ul_vote_lock);
+static int need_delayed_ul_vote;
 
 struct outside_notify_func {
 	void (*notify)(void *, int, unsigned long);
@@ -1601,6 +1603,19 @@
 		return;
 	}
 
+	/*
+	 * if someone is voting for UL before bam is inited (modem up first
+	 * time), set flag for init to kickoff ul wakeup once bam is inited
+	 */
+	mutex_lock(&delayed_ul_vote_lock);
+	if (unlikely(!bam_mux_initialized)) {
+		need_delayed_ul_vote = 1;
+		mutex_unlock(&delayed_ul_vote_lock);
+		mutex_unlock(&wakeup_lock);
+		return;
+	}
+	mutex_unlock(&delayed_ul_vote_lock);
+
 	if (a2_pc_disabled) {
 		/*
 		 * don't grab the wakelock the first time because it is
@@ -2012,7 +2027,13 @@
 		goto rx_event_reg_failed;
 	}
 
+	mutex_lock(&delayed_ul_vote_lock);
 	bam_mux_initialized = 1;
+	if (need_delayed_ul_vote) {
+		need_delayed_ul_vote = 0;
+		msm_bam_dmux_kickoff_ul_wakeup();
+	}
+	mutex_unlock(&delayed_ul_vote_lock);
 	toggle_apps_ack();
 	bam_connection_is_active = 1;
 	complete_all(&bam_connection_completion);
@@ -2079,6 +2100,14 @@
 		goto register_bam_failed;
 	}
 	a2_device_handle = h;
+
+	mutex_lock(&delayed_ul_vote_lock);
+	bam_mux_initialized = 1;
+	if (need_delayed_ul_vote) {
+		need_delayed_ul_vote = 0;
+		msm_bam_dmux_kickoff_ul_wakeup();
+	}
+	mutex_unlock(&delayed_ul_vote_lock);
 	toggle_apps_ack();
 
 	return 0;
diff --git a/arch/arm/mach-msm/board-8064-display.c b/arch/arm/mach-msm/board-8064-display.c
index ee1a2ae..60bc26c 100644
--- a/arch/arm/mach-msm/board-8064-display.c
+++ b/arch/arm/mach-msm/board-8064-display.c
@@ -30,32 +30,13 @@
 
 #ifdef CONFIG_FB_MSM_TRIPLE_BUFFER
 /* prim = 1366 x 768 x 3(bpp) x 3(pages) */
-#define MSM_FB_PRIM_BUF_SIZE roundup(1920 * 1080 * 4 * 3, 0x10000)
+#define MSM_FB_PRIM_BUF_SIZE roundup(1920 * 1088 * 4 * 3, 0x10000)
 #else
 /* prim = 1366 x 768 x 3(bpp) x 2(pages) */
-#define MSM_FB_PRIM_BUF_SIZE roundup(1920 * 1080 * 4 * 2, 0x10000)
+#define MSM_FB_PRIM_BUF_SIZE roundup(1920 * 1088 * 4 * 2, 0x10000)
 #endif
 
-#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL
-#define MSM_FB_EXT_BUF_SIZE \
-		(roundup((1920 * 1088 * 2), 4096) * 1) /* 2 bpp x 1 page */
-#elif defined(CONFIG_FB_MSM_TVOUT)
-#define MSM_FB_EXT_BUF_SIZE \
-		(roundup((720 * 576 * 2), 4096) * 2) /* 2 bpp x 2 pages */
-#else
-#define MSM_FB_EXT_BUF_SIZE	0
-#endif
-
-#ifdef CONFIG_FB_MSM_WRITEBACK_MSM_PANEL
-#define MSM_FB_WFD_BUF_SIZE \
-		(roundup((1280 * 736 * 2), 4096) * 1) /* 2 bpp x 1 page */
-#else
-#define MSM_FB_WFD_BUF_SIZE     0
-#endif
-
-#define MSM_FB_SIZE \
-	roundup(MSM_FB_PRIM_BUF_SIZE + \
-		MSM_FB_EXT_BUF_SIZE + MSM_FB_WFD_BUF_SIZE, 4096)
+#define MSM_FB_SIZE roundup(MSM_FB_PRIM_BUF_SIZE, 4096)
 
 #ifdef CONFIG_FB_MSM_OVERLAY0_WRITEBACK
 #define MSM_FB_OVERLAY0_WRITEBACK_SIZE roundup((1376 * 768 * 3 * 2), 4096)
@@ -615,7 +596,7 @@
 {
 	if (machine_is_apq8064_cdp() ||
 	    machine_is_apq8064_liquid()) {
-		u32 ver = socinfo_get_platform_version();
+		u32 ver = socinfo_get_version();
 		if ((SOCINFO_VERSION_MAJOR(ver) == 1) &&
 		    (SOCINFO_VERSION_MINOR(ver) == 0))
 			return 1;
diff --git a/arch/arm/mach-msm/board-8064-regulator.c b/arch/arm/mach-msm/board-8064-regulator.c
index 01665be..58e83a0 100644
--- a/arch/arm/mach-msm/board-8064-regulator.c
+++ b/arch/arm/mach-msm/board-8064-regulator.c
@@ -559,15 +559,15 @@
 	RPM_SMPS(S1, 1, 1, 0, 1225000, 1225000, NULL, 100000, 3p20, NONE, NONE),
 	RPM_SMPS(S2, 0, 1, 0, 1300000, 1300000, NULL,      0, 1p60, NONE, NONE),
 	RPM_SMPS(S3, 0, 1, 1,  500000, 1150000, NULL, 100000, 4p80, NONE, NONE),
-	RPM_SMPS(S4, 1, 1, 0, 1800000, 1800000, NULL, 100000, 1p60, NONE, NONE),
+	RPM_SMPS(S4, 1, 1, 0, 1800000, 1800000, NULL, 100000, 1p60, AUTO, AUTO),
 	RPM_SMPS(S7, 0, 1, 0, 1300000, 1300000, NULL, 100000, 3p20, NONE, NONE),
 	RPM_SMPS(S8, 0, 1, 0, 2200000, 2200000, NULL,      0, 1p60, NONE, NONE),
 
 	/*	ID a_on pd ss min_uV   max_uV   supply    sys_uA init_ip */
-	RPM_LDO(L1,  1, 1, 0, 1100000, 1100000, "8921_s4",     0, 10000),
+	RPM_LDO(L1,  1, 1, 0, 1100000, 1100000, "8921_s4",     0,  1000),
 	RPM_LDO(L2,  0, 1, 0, 1200000, 1200000, "8921_s4",     0,     0),
 	RPM_LDO(L3,  0, 1, 0, 3075000, 3075000, NULL,          0,     0),
-	RPM_LDO(L4,  1, 1, 0, 1800000, 1800000, NULL,      10000, 10000),
+	RPM_LDO(L4,  1, 1, 0, 1800000, 1800000, NULL,          0, 10000),
 	RPM_LDO(L5,  0, 1, 0, 2950000, 2950000, NULL,          0,     0),
 	RPM_LDO(L6,  0, 1, 0, 2950000, 2950000, NULL,          0,     0),
 	RPM_LDO(L7,  0, 1, 0, 1850000, 2950000, NULL,          0,     0),
diff --git a/arch/arm/mach-msm/board-8064.c b/arch/arm/mach-msm/board-8064.c
index cfaa7da..f9b1e2e 100644
--- a/arch/arm/mach-msm/board-8064.c
+++ b/arch/arm/mach-msm/board-8064.c
@@ -2666,7 +2666,7 @@
 	}
 	apq8064_init_fb();
 	apq8064_init_gpu();
-	platform_add_devices(apq8064_fs_devices, apq8064_num_fs_devices);
+	platform_add_devices(apq8064_footswitch, apq8064_num_footswitch);
 	apq8064_init_cam();
 
 	if (machine_is_apq8064_cdp() || machine_is_apq8064_liquid())
diff --git a/arch/arm/mach-msm/board-8930-camera.c b/arch/arm/mach-msm/board-8930-camera.c
index e7992f9..070f3bf 100644
--- a/arch/arm/mach-msm/board-8930-camera.c
+++ b/arch/arm/mach-msm/board-8930-camera.c
@@ -545,7 +545,7 @@
 };
 
 static struct msm_camera_sensor_platform_info sensor_board_info_s5k3l1yx = {
-	.mount_angle  = 0,
+	.mount_angle  = 90,
 	.cam_vreg = msm_8930_s5k3l1yx_vreg,
 	.num_vreg = ARRAY_SIZE(msm_8930_s5k3l1yx_vreg),
 	.gpio_conf = &msm_8930_back_cam_gpio_conf,
@@ -572,6 +572,12 @@
 	msm_gpiomux_install(msm8930_cam_common_configs,
 			ARRAY_SIZE(msm8930_cam_common_configs));
 
+	if (machine_is_msm8930_cdp()) {
+		struct msm_camera_sensor_info *s_info;
+		s_info = &msm_camera_sensor_s5k3l1yx_data;
+		s_info->sensor_platform_info->mount_angle = 0;
+	}
+
 	platform_device_register(&msm_camera_server);
 	platform_device_register(&msm8960_device_csiphy0);
 	platform_device_register(&msm8960_device_csiphy1);
diff --git a/arch/arm/mach-msm/board-8930-display.c b/arch/arm/mach-msm/board-8930-display.c
index 42b20b2..6230b65 100644
--- a/arch/arm/mach-msm/board-8930-display.c
+++ b/arch/arm/mach-msm/board-8930-display.c
@@ -30,29 +30,13 @@
 
 #ifdef CONFIG_FB_MSM_TRIPLE_BUFFER
 #define MSM_FB_PRIM_BUF_SIZE \
-		(roundup((1376 * 768 * 4), 4096) * 3) /* 4 bpp x 3 pages */
+		(roundup((1920 * 1088 * 4), 4096) * 3) /* 4 bpp x 3 pages */
 #else
 #define MSM_FB_PRIM_BUF_SIZE \
-		(roundup((1376 * 768 * 4), 4096) * 2) /* 4 bpp x 2 pages */
+		(roundup((1920 * 1088 * 4), 4096) * 2) /* 4 bpp x 2 pages */
 #endif
-
-#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL
-#define MSM_FB_EXT_BUF_SIZE \
-		(roundup((1920 * 1088 * 2), 4096) * 1) /* 2 bpp x 1 page */
-#elif defined(CONFIG_FB_MSM_TVOUT)
-#define MSM_FB_EXT_BUF_SIZE \
-		(roundup((720 * 576 * 2), 4096) * 2) /* 2 bpp x 2 pages */
-#else
-#define MSM_FB_EXT_BUF_SIZE	0
-#endif
-
-#ifdef CONFIG_FB_MSM_HDMI_AS_PRIMARY
-/* 4 bpp x 2 page HDMI case */
-#define MSM_FB_SIZE roundup((1920 * 1088 * 4 * 2), 4096)
-#else
 /* Note: must be multiple of 4096 */
-#define MSM_FB_SIZE roundup(MSM_FB_PRIM_BUF_SIZE + MSM_FB_EXT_BUF_SIZE, 4096)
-#endif
+#define MSM_FB_SIZE roundup(MSM_FB_PRIM_BUF_SIZE, 4096)
 
 #ifdef CONFIG_FB_MSM_OVERLAY0_WRITEBACK
 #define MSM_FB_OVERLAY0_WRITEBACK_SIZE roundup((1376 * 768 * 3 * 2), 4096)
diff --git a/arch/arm/mach-msm/board-8930-gpiomux.c b/arch/arm/mach-msm/board-8930-gpiomux.c
index 854f318..cd4aff8 100644
--- a/arch/arm/mach-msm/board-8930-gpiomux.c
+++ b/arch/arm/mach-msm/board-8930-gpiomux.c
@@ -88,6 +88,12 @@
 	},
 };
 
+static struct gpiomux_setting audio_mbhc = {
+	.func = GPIOMUX_FUNC_GPIO,
+	.drv = GPIOMUX_DRV_8MA,
+	.pull = GPIOMUX_PULL_NONE,
+};
+
 #if defined(CONFIG_KS8851) || defined(CONFIG_KS8851_MODULE)
 static struct gpiomux_setting gpio_eth_config = {
 	.pull = GPIOMUX_PULL_NONE,
@@ -374,6 +380,15 @@
 	},
 };
 
+static struct msm_gpiomux_config msm8960_audio_mbhc_configs[] __initdata = {
+	{
+		.gpio = 37,
+		.settings = {
+			[GPIOMUX_SUSPENDED] = &audio_mbhc,
+		},
+	},
+};
+
 static struct msm_gpiomux_config msm8960_audio_auxpcm_configs[] __initdata = {
 	{
 		.gpio = 63,
@@ -618,6 +633,9 @@
 	msm_gpiomux_install(msm8960_audio_codec_configs,
 			ARRAY_SIZE(msm8960_audio_codec_configs));
 
+	msm_gpiomux_install(msm8960_audio_mbhc_configs,
+			ARRAY_SIZE(msm8960_audio_mbhc_configs));
+
 	msm_gpiomux_install(msm8960_audio_auxpcm_configs,
 			ARRAY_SIZE(msm8960_audio_auxpcm_configs));
 
diff --git a/arch/arm/mach-msm/board-8930.c b/arch/arm/mach-msm/board-8930.c
index cbb36a1..6fd0f1b 100644
--- a/arch/arm/mach-msm/board-8930.c
+++ b/arch/arm/mach-msm/board-8930.c
@@ -2068,8 +2068,7 @@
 	msm_spm_init(msm_spm_data, ARRAY_SIZE(msm_spm_data));
 	msm_spm_l2_init(msm_spm_l2_data);
 	msm8930_init_buses();
-	platform_add_devices(msm_footswitch_devices,
-		msm_num_footswitch_devices);
+	platform_add_devices(msm8930_footswitch, msm8930_num_footswitch);
 	platform_add_devices(common_devices, ARRAY_SIZE(common_devices));
 	msm8930_add_vidc_device();
 	/*
diff --git a/arch/arm/mach-msm/board-8960-camera.c b/arch/arm/mach-msm/board-8960-camera.c
index 83e3c8d..83690f3 100644
--- a/arch/arm/mach-msm/board-8960-camera.c
+++ b/arch/arm/mach-msm/board-8960-camera.c
@@ -727,6 +727,11 @@
 			msm_camera_8960_ext_power_ctrl;
 	}
 
+	if (machine_is_msm8960_fluid()) {
+		msm_camera_sensor_imx091_data.sensor_platform_info->
+			mount_angle = 270;
+	}
+
 	platform_device_register(&msm_camera_server);
 	platform_device_register(&msm8960_device_csiphy0);
 	platform_device_register(&msm8960_device_csiphy1);
diff --git a/arch/arm/mach-msm/board-8960-display.c b/arch/arm/mach-msm/board-8960-display.c
index 8c41e8c..b4db968 100644
--- a/arch/arm/mach-msm/board-8960-display.c
+++ b/arch/arm/mach-msm/board-8960-display.c
@@ -38,20 +38,8 @@
 			/* 4 bpp x 2 pages */
 #endif
 
-#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL
-#define MSM_FB_EXT_BUF_SIZE \
-		(roundup((roundup(1920, 32) * roundup(1080, 32) * 2), 4096) * 1)
-			/* 2 bpp x 1 page */
-#elif defined(CONFIG_FB_MSM_TVOUT)
-#define MSM_FB_EXT_BUF_SIZE \
-		(roundup((roundup(720, 32) * roundup(576, 32) * 2), 4096) * 2)
-			/* 2 bpp x 2 pages */
-#else
-#define MSM_FB_EXT_BUF_SIZE	0
-#endif
-
 /* Note: must be multiple of 4096 */
-#define MSM_FB_SIZE roundup(MSM_FB_PRIM_BUF_SIZE + MSM_FB_EXT_BUF_SIZE, 4096)
+#define MSM_FB_SIZE roundup(MSM_FB_PRIM_BUF_SIZE, 4096)
 
 #ifdef CONFIG_FB_MSM_OVERLAY0_WRITEBACK
 #define MSM_FB_OVERLAY0_WRITEBACK_SIZE \
diff --git a/arch/arm/mach-msm/board-8960.c b/arch/arm/mach-msm/board-8960.c
index 42dbbee..a3afdb0 100644
--- a/arch/arm/mach-msm/board-8960.c
+++ b/arch/arm/mach-msm/board-8960.c
@@ -673,21 +673,6 @@
 	msm8960_mdp_writeback(msm8960_reserve_table);
 }
 
-#if defined(CONFIG_MSM_CACHE_DUMP)
-static struct msm_cache_dump_platform_data msm8960_cache_dump_pdata = {
-	.l2_size = L2_BUFFER_SIZE,
-};
-
-static struct platform_device msm8960_cache_dump_device = {
-	.name           = "msm_cache_dump",
-	.id             = -1,
-	.dev            = {
-		.platform_data = &msm8960_cache_dump_pdata,
-	},
-};
-
-#endif
-
 static void __init reserve_cache_dump_memory(void)
 {
 #ifdef CONFIG_MSM_CACHE_DUMP
@@ -2566,9 +2551,7 @@
 	&msm8960_cpu_idle_device,
 	&msm8960_msm_gov_device,
 	&msm8960_device_cache_erp,
-#ifdef CONFIG_MSM_CACHE_DUMP
 	&msm8960_cache_dump_device,
-#endif
 	&msm8960_iommu_domain_device,
 	&msm_tsens_device,
 };
@@ -3124,8 +3107,7 @@
 	msm_spm_init(msm_spm_data, ARRAY_SIZE(msm_spm_data));
 	msm_spm_l2_init(msm_spm_l2_data);
 	msm8960_init_buses();
-	platform_add_devices(msm_footswitch_devices,
-		msm_num_footswitch_devices);
+	platform_add_devices(msm8960_footswitch, msm8960_num_footswitch);
 	if (machine_is_msm8960_liquid())
 		platform_device_register(&msm8960_device_ext_3p3v_vreg);
 	if (machine_is_msm8960_cdp())
diff --git a/arch/arm/mach-msm/board-8960.h b/arch/arm/mach-msm/board-8960.h
index 6a4c92e..925c5b4 100644
--- a/arch/arm/mach-msm/board-8960.h
+++ b/arch/arm/mach-msm/board-8960.h
@@ -21,6 +21,7 @@
 #include <mach/rpm-regulator.h>
 #include <mach/msm_memtypes.h>
 #include <mach/msm_rtb.h>
+#include <mach/msm_cache_dump.h>
 
 /* Macros assume PMIC GPIOs and MPPs start at 1 */
 #define PM8921_GPIO_BASE		NR_GPIO_IRQS
@@ -95,3 +96,4 @@
 #define MSM_8960_GSBI10_QUP_I2C_BUS_ID 10
 
 extern struct msm_rtb_platform_data msm8960_rtb_pdata;
+extern struct msm_cache_dump_platform_data msm8960_cache_dump_pdata;
diff --git a/arch/arm/mach-msm/board-9615.c b/arch/arm/mach-msm/board-9615.c
index b128223..a8602d3 100644
--- a/arch/arm/mach-msm/board-9615.c
+++ b/arch/arm/mach-msm/board-9615.c
@@ -43,10 +43,11 @@
 #include <mach/dma.h>
 #include <mach/ion.h>
 #include <mach/msm_memtypes.h>
+#include <mach/cpuidle.h>
+#include <mach/usb_bam.h>
 #include "timer.h"
 #include "devices.h"
 #include "board-9615.h"
-#include <mach/cpuidle.h>
 #include "pm.h"
 #include "acpuclock.h"
 #include "pm-boot.h"
@@ -568,6 +569,10 @@
 	.disable_reset_on_disconnect	= true,
 };
 
+static struct msm_hsic_peripheral_platform_data msm_hsic_peripheral_pdata = {
+	.keep_core_clk_on_suspend_workaround = true,
+};
+
 #define PID_MAGIC_ID		0x71432909
 #define SERIAL_NUM_MAGIC_ID	0x61945374
 #define SERIAL_NUMBER_LENGTH	127
@@ -756,6 +761,8 @@
 
 	msm_device_otg.dev.platform_data = &msm_otg_pdata;
 	msm_otg_pdata.phy_init_seq = shelby_phy_init_seq;
+	msm_device_hsic_peripheral.dev.platform_data =
+		&msm_hsic_peripheral_pdata;
 	msm_device_usb_bam.dev.platform_data = &msm_usb_bam_pdata;
 	platform_add_devices(common_devices, ARRAY_SIZE(common_devices));
 
diff --git a/arch/arm/mach-msm/board-copper.c b/arch/arm/mach-msm/board-copper.c
index e545f12..2da621f8 100644
--- a/arch/arm/mach-msm/board-copper.c
+++ b/arch/arm/mach-msm/board-copper.c
@@ -42,6 +42,7 @@
 #include <mach/socinfo.h>
 #include "clock.h"
 #include "devices.h"
+#include "spm.h"
 
 #define MSM_KERNEL_EBI1_MEM_SIZE	0x280000
 #ifdef CONFIG_FB_MSM_HDMI_AS_PRIMARY
@@ -407,6 +408,7 @@
  */
 void __init msm_copper_add_drivers(void)
 {
+	msm_spm_device_init();
 	regulator_stub_init();
 }
 
diff --git a/arch/arm/mach-msm/board-msm7627a-display.c b/arch/arm/mach-msm/board-msm7627a-display.c
index 2de581b..c1915fb 100644
--- a/arch/arm/mach-msm/board-msm7627a-display.c
+++ b/arch/arm/mach-msm/board-msm7627a-display.c
@@ -1133,20 +1133,6 @@
 		if (rc < 0)
 			return rc;
 
-		rc = gpio_tlmm_config(GPIO_CFG(GPIO_QRD3_LCD_BACKLIGHT_EN, 0,
-			GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_2MA),
-			GPIO_CFG_ENABLE);
-		if (rc < 0) {
-			pr_err("failed QRD3 GPIO_BACKLIGHT_EN tlmm config\n");
-			return rc;
-		}
-		rc = gpio_direction_output(GPIO_QRD3_LCD_BACKLIGHT_EN, 1);
-		if (rc < 0) {
-			pr_err("failed to enable backlight\n");
-			gpio_free(GPIO_QRD3_LCD_BACKLIGHT_EN);
-			return rc;
-		}
-
 		rc = gpio_request(GPIO_QRD3_LCD_EXT_2V85_EN,
 			"qrd3_gpio_ext_2v85_en");
 		if (rc < 0)
@@ -1191,6 +1177,20 @@
 	}
 
 	if (on) {
+		rc = gpio_tlmm_config(GPIO_CFG(GPIO_QRD3_LCD_BACKLIGHT_EN, 0,
+			GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_2MA),
+			GPIO_CFG_ENABLE);
+		if (rc < 0) {
+			pr_err("failed QRD3 GPIO_BACKLIGHT_EN tlmm config\n");
+			return rc;
+		}
+		rc = gpio_direction_output(GPIO_QRD3_LCD_BACKLIGHT_EN, 1);
+		if (rc < 0) {
+			pr_err("failed to enable backlight\n");
+			gpio_free(GPIO_QRD3_LCD_BACKLIGHT_EN);
+			return rc;
+		}
+
 		gpio_set_value_cansleep(GPIO_QRD3_LCD_BACKLIGHT_EN, 1);
 		udelay(190);
 		gpio_set_value_cansleep(GPIO_QRD3_LCD_BACKLIGHT_EN, 0);
@@ -1198,8 +1198,11 @@
 		gpio_set_value_cansleep(GPIO_QRD3_LCD_BACKLIGHT_EN, 1);
 		/* 1 wire mode starts from this low to high transition */
 		udelay(50);
-	} else
-		gpio_set_value_cansleep(GPIO_QRD3_LCD_BACKLIGHT_EN, !!on);
+	} else {
+		gpio_tlmm_config(GPIO_CFG(GPIO_QRD3_LCD_BACKLIGHT_EN, 0,
+			GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA),
+			GPIO_CFG_DISABLE);
+	}
 
 	gpio_set_value_cansleep(GPIO_QRD3_LCD_EXT_2V85_EN, !!on);
 	gpio_set_value_cansleep(GPIO_QRD3_LCD_EXT_1V8_EN, !!on);
diff --git a/arch/arm/mach-msm/board-msm7x27a.c b/arch/arm/mach-msm/board-msm7x27a.c
index ab52665..2cf3b8d 100644
--- a/arch/arm/mach-msm/board-msm7x27a.c
+++ b/arch/arm/mach-msm/board-msm7x27a.c
@@ -563,9 +563,9 @@
 	(DEC4_FORMAT),
 
 	/* Concurrency 6 */
-	(DEC0_FORMAT|(1<<MSM_ADSP_MODE_TUNNEL)|(1<<MSM_ADSP_OP_DM)),
-	(DEC1_FORMAT|(1<<MSM_ADSP_MODE_NONTUNNEL)|(1<<MSM_ADSP_OP_DM)),
-	0, 0, 0,
+	(DEC0_FORMAT|(1<<MSM_ADSP_MODE_TUNNEL)|
+			(1<<MSM_ADSP_MODE_NONTUNNEL)|(1<<MSM_ADSP_OP_DM)),
+	0, 0, 0, 0,
 
 	/* Concurrency 7 */
 	(DEC0_FORMAT|(1<<MSM_ADSP_MODE_NONTUNNEL)|(1<<MSM_ADSP_OP_DM)),
diff --git a/arch/arm/mach-msm/board-msm8x60-camera.c b/arch/arm/mach-msm/board-msm8x60-camera.c
index 743ca4d..b52f951 100644
--- a/arch/arm/mach-msm/board-msm8x60-camera.c
+++ b/arch/arm/mach-msm/board-msm8x60-camera.c
@@ -448,6 +448,26 @@
 	.actuator_info = &imx074_actuator_info
 };
 
+static struct msm_camera_sensor_flash_data flash_mt9e013 = {
+	.flash_type	= MSM_CAMERA_FLASH_NONE,
+};
+
+static struct msm_camera_sensor_platform_info sensor_board_info_mt9e013 = {
+	.mount_angle	= 0,
+	.cam_vreg = msm_8x60_back_cam_vreg,
+	.num_vreg = ARRAY_SIZE(msm_8x60_back_cam_vreg),
+	.gpio_conf = &msm_8x60_back_cam_gpio_conf,
+};
+
+static struct msm_camera_sensor_info msm_camera_sensor_mt9e013_data = {
+	.sensor_name	= "mt9e013",
+	.pdata	= &msm_camera_csi_device_data[0],
+	.flash_data	= &flash_mt9e013,
+	.sensor_platform_info = &sensor_board_info_mt9e013,
+	.csi_if	= 1,
+	.camera_type = BACK_CAMERA_2D,
+};
+
 static struct platform_device msm_camera_server = {
 	.name = "msm_cam_server",
 	.id = 0,
@@ -468,6 +488,10 @@
 	I2C_BOARD_INFO("imx074", 0x1A),
 	.platform_data = &msm_camera_sensor_imx074_data,
 	},
+	{
+	I2C_BOARD_INFO("mt9e013", 0x6C),
+	.platform_data = &msm_camera_sensor_mt9e013_data,
+	},
 };
 
 struct msm_camera_board_info msm8x60_camera_board_info = {
diff --git a/arch/arm/mach-msm/board-msm8x60.c b/arch/arm/mach-msm/board-msm8x60.c
index 5fc17df..6b22e99 100644
--- a/arch/arm/mach-msm/board-msm8x60.c
+++ b/arch/arm/mach-msm/board-msm8x60.c
@@ -3812,6 +3812,7 @@
 static struct regulator_consumer_supply vreg_consumers_PM8058_L15[] = {
 	REGULATOR_SUPPLY("8058_l15",		NULL),
 	REGULATOR_SUPPLY("cam_vana",		"1-001a"),
+	REGULATOR_SUPPLY("cam_vana",		"1-006c"),
 };
 static struct regulator_consumer_supply vreg_consumers_PM8058_L16[] = {
 	REGULATOR_SUPPLY("8058_l16",		NULL),
@@ -3843,6 +3844,7 @@
 static struct regulator_consumer_supply vreg_consumers_PM8058_L25[] = {
 	REGULATOR_SUPPLY("8058_l25",		NULL),
 	REGULATOR_SUPPLY("cam_vdig",		"1-001a"),
+	REGULATOR_SUPPLY("cam_vdig",		"1-006c"),
 };
 static struct regulator_consumer_supply vreg_consumers_PM8058_S0[] = {
 	REGULATOR_SUPPLY("8058_s0",		NULL),
@@ -3862,6 +3864,7 @@
 static struct regulator_consumer_supply vreg_consumers_PM8058_LVS0[] = {
 	REGULATOR_SUPPLY("8058_lvs0",		NULL),
 	REGULATOR_SUPPLY("cam_vio",			"1-001a"),
+	REGULATOR_SUPPLY("cam_vio",			"1-006c"),
 };
 static struct regulator_consumer_supply vreg_consumers_PM8058_LVS1[] = {
 	REGULATOR_SUPPLY("8058_lvs1",		NULL),
@@ -10315,8 +10318,8 @@
 	    machine_is_msm8x60_fusn_ffa() || machine_is_msm8x60_dragon()) {
 		msm8x60_cfg_smsc911x();
 		if (SOCINFO_VERSION_MAJOR(socinfo_get_version()) != 1)
-			platform_add_devices(msm_footswitch_devices,
-					     msm_num_footswitch_devices);
+			platform_add_devices(msm8660_footswitch,
+					     msm8660_num_footswitch);
 		platform_add_devices(surf_devices,
 				     ARRAY_SIZE(surf_devices));
 
diff --git a/arch/arm/mach-msm/board-qrd7627a.c b/arch/arm/mach-msm/board-qrd7627a.c
index 782bb9e..abc2cef 100644
--- a/arch/arm/mach-msm/board-qrd7627a.c
+++ b/arch/arm/mach-msm/board-qrd7627a.c
@@ -510,9 +510,9 @@
 	(DEC4_FORMAT),
 
 	/* Concurrency 6 */
-	(DEC0_FORMAT|(1<<MSM_ADSP_MODE_TUNNEL)|(1<<MSM_ADSP_OP_DM)),
-	(DEC1_FORMAT|(1<<MSM_ADSP_MODE_NONTUNNEL)|(1<<MSM_ADSP_OP_DM)),
-	0, 0, 0,
+	(DEC0_FORMAT|(1<<MSM_ADSP_MODE_TUNNEL)|
+			(1<<MSM_ADSP_MODE_NONTUNNEL)|(1<<MSM_ADSP_OP_DM)),
+	0, 0, 0, 0,
 
 	/* Concurrency 7 */
 	(DEC0_FORMAT|(1<<MSM_ADSP_MODE_NONTUNNEL)|(1<<MSM_ADSP_OP_DM)),
diff --git a/arch/arm/mach-msm/clock-8960.c b/arch/arm/mach-msm/clock-8960.c
index 1035caf..8879576 100644
--- a/arch/arm/mach-msm/clock-8960.c
+++ b/arch/arm/mach-msm/clock-8960.c
@@ -5578,7 +5578,7 @@
 	CLK_LOOKUP("mem_clk",		rpm_msg_ram_p_clk.c,	""),
 	CLK_LOOKUP("cam_clk",		cam0_clk.c,	"4-001a"),
 	CLK_LOOKUP("cam_clk",		cam1_clk.c,	"4-006c"),
-	CLK_LOOKUP("cam_clk",		cam0_clk.c,	"4-0048"),
+	CLK_LOOKUP("cam_clk",		cam1_clk.c,	"4-0048"),
 	CLK_LOOKUP("cam_clk",		cam2_clk.c,		NULL),
 	CLK_LOOKUP("cam_clk",		cam0_clk.c,	"4-0020"),
 	CLK_LOOKUP("csi_src_clk",	csi0_src_clk.c,		"msm_csid.0"),
diff --git a/arch/arm/mach-msm/devices-8064.c b/arch/arm/mach-msm/devices-8064.c
index b51fb83..e2cfbc4 100644
--- a/arch/arm/mach-msm/devices-8064.c
+++ b/arch/arm/mach-msm/devices-8064.c
@@ -1548,16 +1548,88 @@
 	.resource       = msm_gss_resources,
 };
 
-struct platform_device *apq8064_fs_devices[] = {
-	FS_8X60(FS_ROT,    "fs_rot"),
-	FS_8X60(FS_IJPEG,  "fs_ijpeg"),
-	FS_8X60(FS_VFE,    "fs_vfe"),
-	FS_8X60(FS_VPE,    "fs_vpe"),
-	FS_8X60(FS_GFX3D,  "fs_gfx3d"),
-	FS_8X60(FS_VED,    "fs_ved"),
-	FS_8X60(FS_VCAP,   "fs_vcap"),
+static struct fs_driver_data gfx3d_fs_data = {
+	.clks = (struct fs_clk_data[]){
+		{ .name = "core_clk", .reset_rate = 27000000 },
+		{ .name = "iface_clk" },
+		{ .name = "bus_clk" },
+		{ 0 }
+	},
+	.bus_port0 = MSM_BUS_MASTER_GRAPHICS_3D,
+	.bus_port1 = MSM_BUS_MASTER_GRAPHICS_3D_PORT1,
 };
-unsigned apq8064_num_fs_devices = ARRAY_SIZE(apq8064_fs_devices);
+
+static struct fs_driver_data ijpeg_fs_data = {
+	.clks = (struct fs_clk_data[]){
+		{ .name = "core_clk" },
+		{ .name = "iface_clk" },
+		{ .name = "bus_clk" },
+		{ 0 }
+	},
+	.bus_port0 = MSM_BUS_MASTER_JPEG_ENC,
+};
+
+static struct fs_driver_data rot_fs_data = {
+	.clks = (struct fs_clk_data[]){
+		{ .name = "core_clk" },
+		{ .name = "iface_clk" },
+		{ .name = "bus_clk" },
+		{ 0 }
+	},
+	.bus_port0 = MSM_BUS_MASTER_ROTATOR,
+};
+
+static struct fs_driver_data ved_fs_data = {
+	.clks = (struct fs_clk_data[]){
+		{ .name = "core_clk" },
+		{ .name = "iface_clk" },
+		{ .name = "bus_clk" },
+		{ 0 }
+	},
+	.bus_port0 = MSM_BUS_MASTER_VIDEO_ENC,
+	.bus_port1 = MSM_BUS_MASTER_VIDEO_DEC,
+};
+
+static struct fs_driver_data vfe_fs_data = {
+	.clks = (struct fs_clk_data[]){
+		{ .name = "core_clk" },
+		{ .name = "iface_clk" },
+		{ .name = "bus_clk" },
+		{ 0 }
+	},
+	.bus_port0 = MSM_BUS_MASTER_VFE,
+};
+
+static struct fs_driver_data vpe_fs_data = {
+	.clks = (struct fs_clk_data[]){
+		{ .name = "core_clk" },
+		{ .name = "iface_clk" },
+		{ .name = "bus_clk" },
+		{ 0 }
+	},
+	.bus_port0 = MSM_BUS_MASTER_VPE,
+};
+
+static struct fs_driver_data vcap_fs_data = {
+	.clks = (struct fs_clk_data[]){
+		{ .name = "core_clk" },
+		{ .name = "iface_clk" },
+		{ .name = "bus_clk" },
+		{ 0 },
+	},
+	.bus_port0 = MSM_BUS_MASTER_VIDEO_CAP,
+};
+
+struct platform_device *apq8064_footswitch[] __initdata = {
+	FS_8X60(FS_ROT,    "vdd",	"msm_rotator.0", &rot_fs_data),
+	FS_8X60(FS_IJPEG,  "vdd",	"msm_gemini.0",	&ijpeg_fs_data),
+	FS_8X60(FS_VFE,    "fs_vfe",	NULL,	&vfe_fs_data),
+	FS_8X60(FS_VPE,    "fs_vpe",	NULL,	&vpe_fs_data),
+	FS_8X60(FS_GFX3D,  "vdd",	"kgsl-3d0.0",	&gfx3d_fs_data),
+	FS_8X60(FS_VED,    "vdd",	"msm_vidc.0",	&ved_fs_data),
+	FS_8X60(FS_VCAP,   "vdd",	"msm_vcap.0",	&vcap_fs_data),
+};
+unsigned apq8064_num_footswitch __initdata = ARRAY_SIZE(apq8064_footswitch);
 
 struct msm_rpm_platform_data apq8064_rpm_data __initdata = {
 	.reg_base_addrs = {
diff --git a/arch/arm/mach-msm/devices-8930.c b/arch/arm/mach-msm/devices-8930.c
index ae97189..6a03a77 100644
--- a/arch/arm/mach-msm/devices-8930.c
+++ b/arch/arm/mach-msm/devices-8930.c
@@ -28,6 +28,7 @@
 #include "devices.h"
 #include "rpm_log.h"
 #include "rpm_stats.h"
+#include "footswitch.h"
 
 #ifdef CONFIG_MSM_MPM
 #include <mach/mpm.h>
@@ -352,6 +353,93 @@
 	.id    = MSM_BUS_FAB_CPSS_FPB,
 };
 
+static struct fs_driver_data gfx3d_fs_data = {
+	.clks = (struct fs_clk_data[]){
+		{ .name = "core_clk", .reset_rate = 27000000 },
+		{ .name = "iface_clk" },
+		{ .name = "bus_clk" },
+		{ 0 }
+	},
+	.bus_port0 = MSM_BUS_MASTER_GRAPHICS_3D,
+};
+
+static struct fs_driver_data ijpeg_fs_data = {
+	.clks = (struct fs_clk_data[]){
+		{ .name = "core_clk" },
+		{ .name = "iface_clk" },
+		{ .name = "bus_clk" },
+		{ 0 }
+	},
+	.bus_port0 = MSM_BUS_MASTER_JPEG_ENC,
+};
+
+static struct fs_driver_data mdp_fs_data = {
+	.clks = (struct fs_clk_data[]){
+		{ .name = "core_clk" },
+		{ .name = "iface_clk" },
+		{ .name = "bus_clk" },
+		{ .name = "vsync_clk" },
+		{ .name = "lut_clk" },
+		{ .name = "tv_src_clk" },
+		{ .name = "tv_clk" },
+		{ 0 }
+	},
+	.bus_port0 = MSM_BUS_MASTER_MDP_PORT0,
+	.bus_port1 = MSM_BUS_MASTER_MDP_PORT1,
+};
+
+static struct fs_driver_data rot_fs_data = {
+	.clks = (struct fs_clk_data[]){
+		{ .name = "core_clk" },
+		{ .name = "iface_clk" },
+		{ .name = "bus_clk" },
+		{ 0 }
+	},
+	.bus_port0 = MSM_BUS_MASTER_ROTATOR,
+};
+
+static struct fs_driver_data ved_fs_data = {
+	.clks = (struct fs_clk_data[]){
+		{ .name = "core_clk" },
+		{ .name = "iface_clk" },
+		{ .name = "bus_clk" },
+		{ 0 }
+	},
+	.bus_port0 = MSM_BUS_MASTER_HD_CODEC_PORT0,
+	.bus_port1 = MSM_BUS_MASTER_HD_CODEC_PORT1,
+};
+
+static struct fs_driver_data vfe_fs_data = {
+	.clks = (struct fs_clk_data[]){
+		{ .name = "core_clk" },
+		{ .name = "iface_clk" },
+		{ .name = "bus_clk" },
+		{ 0 }
+	},
+	.bus_port0 = MSM_BUS_MASTER_VFE,
+};
+
+static struct fs_driver_data vpe_fs_data = {
+	.clks = (struct fs_clk_data[]){
+		{ .name = "core_clk" },
+		{ .name = "iface_clk" },
+		{ .name = "bus_clk" },
+		{ 0 }
+	},
+	.bus_port0 = MSM_BUS_MASTER_VPE,
+};
+
+struct platform_device *msm8930_footswitch[] __initdata = {
+	FS_8X60(FS_MDP,    "vdd",	"mdp.0",	&mdp_fs_data),
+	FS_8X60(FS_ROT,    "vdd",	"msm_rotator.0", &rot_fs_data),
+	FS_8X60(FS_IJPEG,  "vdd",	"msm_gemini.0", &ijpeg_fs_data),
+	FS_8X60(FS_VFE,    "fs_vfe",	NULL,	&vfe_fs_data),
+	FS_8X60(FS_VPE,    "fs_vpe",	NULL,	&vpe_fs_data),
+	FS_8X60(FS_GFX3D,  "vdd",	"kgsl-3d0.0",	&gfx3d_fs_data),
+	FS_8X60(FS_VED,    "vdd",	"msm_vidc.0",	&ved_fs_data),
+};
+unsigned msm8930_num_footswitch __initdata = ARRAY_SIZE(msm8930_footswitch);
+
 /* MSM Video core device */
 #ifdef CONFIG_MSM_BUS_SCALING
 static struct msm_bus_vectors vidc_init_vectors[] = {
diff --git a/arch/arm/mach-msm/devices-8960.c b/arch/arm/mach-msm/devices-8960.c
index 7fb4b01..ef5881f 100644
--- a/arch/arm/mach-msm/devices-8960.c
+++ b/arch/arm/mach-msm/devices-8960.c
@@ -33,6 +33,7 @@
 #include <mach/msm_smd.h>
 #include <mach/msm_dcvs.h>
 #include <mach/msm_rtb.h>
+#include <mach/msm_cache_dump.h>
 #include <sound/msm-dai-q6.h>
 #include <sound/apr_audio.h>
 #include <mach/msm_tsif.h>
@@ -1977,19 +1978,111 @@
 	.id	= -1,
 };
 
-struct platform_device *msm_footswitch_devices[] = {
-	FS_8X60(FS_MDP,    "fs_mdp"),
-	FS_8X60(FS_ROT,    "fs_rot"),
-	FS_8X60(FS_IJPEG,  "fs_ijpeg"),
-	FS_8X60(FS_VFE,    "fs_vfe"),
-	FS_8X60(FS_VPE,    "fs_vpe"),
-	FS_8X60(FS_GFX3D,  "fs_gfx3d"),
-	FS_8X60(FS_GFX2D0, "fs_gfx2d0"),
-	FS_8X60(FS_GFX2D1, "fs_gfx2d1"),
-	FS_8X60(FS_VED,    "fs_ved"),
+static struct fs_driver_data gfx2d0_fs_data = {
+	.clks = (struct fs_clk_data[]){
+		{ .name = "core_clk" },
+		{ .name = "iface_clk" },
+		{ 0 }
+	},
+	.bus_port0 = MSM_BUS_MASTER_GRAPHICS_2D_CORE0,
 };
-unsigned msm_num_footswitch_devices = ARRAY_SIZE(msm_footswitch_devices);
 
+static struct fs_driver_data gfx2d1_fs_data = {
+	.clks = (struct fs_clk_data[]){
+		{ .name = "core_clk" },
+		{ .name = "iface_clk" },
+		{ 0 }
+	},
+	.bus_port0 = MSM_BUS_MASTER_GRAPHICS_2D_CORE1,
+};
+
+static struct fs_driver_data gfx3d_fs_data = {
+	.clks = (struct fs_clk_data[]){
+		{ .name = "core_clk", .reset_rate = 27000000 },
+		{ .name = "iface_clk" },
+		{ 0 }
+	},
+	.bus_port0 = MSM_BUS_MASTER_GRAPHICS_3D,
+};
+
+static struct fs_driver_data ijpeg_fs_data = {
+	.clks = (struct fs_clk_data[]){
+		{ .name = "core_clk" },
+		{ .name = "iface_clk" },
+		{ .name = "bus_clk" },
+		{ 0 }
+	},
+	.bus_port0 = MSM_BUS_MASTER_JPEG_ENC,
+};
+
+static struct fs_driver_data mdp_fs_data = {
+	.clks = (struct fs_clk_data[]){
+		{ .name = "core_clk" },
+		{ .name = "iface_clk" },
+		{ .name = "bus_clk" },
+		{ .name = "vsync_clk" },
+		{ .name = "lut_clk" },
+		{ .name = "tv_src_clk" },
+		{ .name = "tv_clk" },
+		{ 0 }
+	},
+	.bus_port0 = MSM_BUS_MASTER_MDP_PORT0,
+	.bus_port1 = MSM_BUS_MASTER_MDP_PORT1,
+};
+
+static struct fs_driver_data rot_fs_data = {
+	.clks = (struct fs_clk_data[]){
+		{ .name = "core_clk" },
+		{ .name = "iface_clk" },
+		{ .name = "bus_clk" },
+		{ 0 }
+	},
+	.bus_port0 = MSM_BUS_MASTER_ROTATOR,
+};
+
+static struct fs_driver_data ved_fs_data = {
+	.clks = (struct fs_clk_data[]){
+		{ .name = "core_clk" },
+		{ .name = "iface_clk" },
+		{ .name = "bus_clk" },
+		{ 0 }
+	},
+	.bus_port0 = MSM_BUS_MASTER_HD_CODEC_PORT0,
+	.bus_port1 = MSM_BUS_MASTER_HD_CODEC_PORT1,
+};
+
+static struct fs_driver_data vfe_fs_data = {
+	.clks = (struct fs_clk_data[]){
+		{ .name = "core_clk" },
+		{ .name = "iface_clk" },
+		{ .name = "bus_clk" },
+		{ 0 }
+	},
+	.bus_port0 = MSM_BUS_MASTER_VFE,
+};
+
+static struct fs_driver_data vpe_fs_data = {
+	.clks = (struct fs_clk_data[]){
+		{ .name = "core_clk" },
+		{ .name = "iface_clk" },
+		{ .name = "bus_clk" },
+		{ 0 }
+	},
+	.bus_port0 = MSM_BUS_MASTER_VPE,
+};
+
+struct platform_device *msm8960_footswitch[] __initdata = {
+	FS_8X60(FS_MDP,    "vdd",	"mdp.0",	&mdp_fs_data),
+	FS_8X60(FS_ROT,    "vdd",	"msm_rotator.0", &rot_fs_data),
+	FS_8X60(FS_IJPEG,  "vdd",	"msm_gemini.0",	&ijpeg_fs_data),
+	FS_8X60(FS_VFE,    "fs_vfe",	NULL,	&vfe_fs_data),
+	FS_8X60(FS_VPE,    "fs_vpe",	NULL,	&vpe_fs_data),
+	FS_8X60(FS_GFX3D,  "vdd",	"kgsl-3d0.0",	&gfx3d_fs_data),
+	FS_8X60(FS_GFX2D0, "vdd",	"kgsl-2d0.0",	&gfx2d0_fs_data),
+	FS_8X60(FS_GFX2D1, "vdd",	"kgsl-2d1.1",	&gfx2d1_fs_data),
+	FS_8X60(FS_VED,    "vdd",	"msm_vidc.0",	&ved_fs_data),
+};
+unsigned msm8960_num_footswitch __initdata = ARRAY_SIZE(msm8960_footswitch);
 
 #ifdef CONFIG_MSM_ROTATOR
 static struct msm_bus_vectors rotator_init_vectors[] = {
@@ -2103,7 +2196,6 @@
 	.number_of_clocks = ARRAY_SIZE(rotator_clocks),
 	.hardware_version_number = 0x01020309,
 	.rotator_clks = rotator_clocks,
-	.regulator_name = "fs_rot",
 #ifdef CONFIG_MSM_BUS_SCALING
 	.bus_scale_table = &rotator_bus_scale_pdata,
 #endif
@@ -3528,3 +3620,15 @@
 		.platform_data = &msm8960_rtb_pdata,
 	},
 };
+
+struct msm_cache_dump_platform_data msm8960_cache_dump_pdata = {
+	.l2_size = L2_BUFFER_SIZE,
+};
+
+struct platform_device msm8960_cache_dump_device = {
+	.name           = "msm_cache_dump",
+	.id             = -1,
+	.dev            = {
+		.platform_data = &msm8960_cache_dump_pdata,
+	},
+};
diff --git a/arch/arm/mach-msm/devices-msm7x27.c b/arch/arm/mach-msm/devices-msm7x27.c
index d36b8d9..2382510 100644
--- a/arch/arm/mach-msm/devices-msm7x27.c
+++ b/arch/arm/mach-msm/devices-msm7x27.c
@@ -870,7 +870,7 @@
 };
 
 struct platform_device *msm_footswitch_devices[] = {
-	FS_PCOM(FS_GFX3D,  "fs_gfx3d"),
+	FS_PCOM(FS_GFX3D,  "vdd", "kgsl-3d0.0"),
 };
 unsigned msm_num_footswitch_devices = ARRAY_SIZE(msm_footswitch_devices);
 
diff --git a/arch/arm/mach-msm/devices-msm7x27a.c b/arch/arm/mach-msm/devices-msm7x27a.c
index a9d68a6..2d79f62 100644
--- a/arch/arm/mach-msm/devices-msm7x27a.c
+++ b/arch/arm/mach-msm/devices-msm7x27a.c
@@ -832,7 +832,7 @@
 
 void __init msm7x25a_kgsl_3d0_init(void)
 {
-	if (cpu_is_msm7x25a() || cpu_is_msm7x25aa()) {
+	if (cpu_is_msm7x25a() || cpu_is_msm7x25aa() || cpu_is_msm7x25ab()) {
 		kgsl_3d0_pdata.num_levels = 2;
 		kgsl_3d0_pdata.pwrlevel[0].gpu_freq = 133330000;
 		kgsl_3d0_pdata.pwrlevel[0].bus_freq = 160000000;
@@ -929,7 +929,7 @@
 };
 
 struct platform_device *msm_footswitch_devices[] = {
-	FS_PCOM(FS_GFX3D,  "fs_gfx3d"),
+	FS_PCOM(FS_GFX3D,  "vdd", "kgsl-3d0.0"),
 };
 unsigned msm_num_footswitch_devices = ARRAY_SIZE(msm_footswitch_devices);
 
@@ -1620,7 +1620,7 @@
 	}
 
 	msm_clock_init(&msm7x27a_clock_init_data);
-	if (cpu_is_msm7x27aa())
+	if (cpu_is_msm7x27aa() || cpu_is_msm7x25ab())
 		acpuclk_init(&acpuclk_7x27aa_soc_data);
 	else if (cpu_is_msm8625()) {
 		if (msm8625_cpu_id() == MSM8625)
diff --git a/arch/arm/mach-msm/devices-msm7x30.c b/arch/arm/mach-msm/devices-msm7x30.c
index 234af04..e055579 100644
--- a/arch/arm/mach-msm/devices-msm7x30.c
+++ b/arch/arm/mach-msm/devices-msm7x30.c
@@ -1187,7 +1187,6 @@
 	.number_of_clocks = ARRAY_SIZE(rotator_clocks),
 	.hardware_version_number = 0x1000303,
 	.rotator_clks = rotator_clocks,
-	.regulator_name = "fs_rot",
 };
 
 struct platform_device msm_rotator_device = {
@@ -1345,13 +1344,13 @@
 };
 
 struct platform_device *msm_footswitch_devices[] = {
-	FS_PCOM(FS_GFX2D0, "fs_gfx2d0"),
-	FS_PCOM(FS_GFX3D,  "fs_gfx3d"),
-	FS_PCOM(FS_MDP,    "fs_mdp"),
-	FS_PCOM(FS_MFC,    "fs_mfc"),
-	FS_PCOM(FS_ROT,    "fs_rot"),
-	FS_PCOM(FS_VFE,    "fs_vfe"),
-	FS_PCOM(FS_VPE,    "fs_vpe"),
+	FS_PCOM(FS_GFX2D0, "vdd", "kgsl-2d0.0"),
+	FS_PCOM(FS_GFX3D,  "vdd", "kgsl-3d0.0"),
+	FS_PCOM(FS_MDP,    "vdd", "mdp.0"),
+	FS_PCOM(FS_MFC,    "fs_mfc",    NULL),
+	FS_PCOM(FS_ROT,    "vdd",  "msm_rotator.0"),
+	FS_PCOM(FS_VFE,    "fs_vfe",    NULL),
+	FS_PCOM(FS_VPE,    "fs_vpe",    NULL),
 };
 unsigned msm_num_footswitch_devices = ARRAY_SIZE(msm_footswitch_devices);
 
diff --git a/arch/arm/mach-msm/devices-msm8x60.c b/arch/arm/mach-msm/devices-msm8x60.c
index e19beb8..d622af2 100644
--- a/arch/arm/mach-msm/devices-msm8x60.c
+++ b/arch/arm/mach-msm/devices-msm8x60.c
@@ -1547,7 +1547,6 @@
 	.number_of_clocks = ARRAY_SIZE(rotator_clocks),
 	.hardware_version_number = 0x01010307,
 	.rotator_clks = rotator_clocks,
-	.regulator_name = "fs_rot",
 #ifdef CONFIG_MSM_BUS_SCALING
 	.bus_scale_table = &rotator_bus_scale_pdata,
 #endif
@@ -2562,18 +2561,112 @@
 };
 #endif
 
-struct platform_device *msm_footswitch_devices[] = {
-	FS_8X60(FS_IJPEG,  "fs_ijpeg"),
-	FS_8X60(FS_MDP,    "fs_mdp"),
-	FS_8X60(FS_ROT,    "fs_rot"),
-	FS_8X60(FS_VED,    "fs_ved"),
-	FS_8X60(FS_VFE,    "fs_vfe"),
-	FS_8X60(FS_VPE,    "fs_vpe"),
-	FS_8X60(FS_GFX3D,  "fs_gfx3d"),
-	FS_8X60(FS_GFX2D0, "fs_gfx2d0"),
-	FS_8X60(FS_GFX2D1, "fs_gfx2d1"),
+static struct fs_driver_data gfx2d0_fs_data = {
+	.clks = (struct fs_clk_data[]){
+		{ .name = "core_clk" },
+		{ .name = "iface_clk" },
+		{ 0 }
+	},
+	.bus_port0 = MSM_BUS_MASTER_GRAPHICS_2D_CORE0,
 };
-unsigned msm_num_footswitch_devices = ARRAY_SIZE(msm_footswitch_devices);
+
+static struct fs_driver_data gfx2d1_fs_data = {
+	.clks = (struct fs_clk_data[]){
+		{ .name = "core_clk" },
+		{ .name = "iface_clk" },
+		{ 0 }
+	},
+	.bus_port0 = MSM_BUS_MASTER_GRAPHICS_2D_CORE1,
+};
+
+static struct fs_driver_data gfx3d_fs_data = {
+	.clks = (struct fs_clk_data[]){
+		{ .name = "core_clk", .reset_rate = 27000000 },
+		{ .name = "iface_clk" },
+		{ 0 }
+	},
+	.bus_port0 = MSM_BUS_MASTER_GRAPHICS_3D,
+};
+
+static struct fs_driver_data ijpeg_fs_data = {
+	.clks = (struct fs_clk_data[]){
+		{ .name = "core_clk" },
+		{ .name = "iface_clk" },
+		{ .name = "bus_clk" },
+		{ 0 }
+	},
+	.bus_port0 = MSM_BUS_MASTER_JPEG_ENC,
+};
+
+static struct fs_driver_data mdp_fs_data = {
+	.clks = (struct fs_clk_data[]){
+		{ .name = "core_clk" },
+		{ .name = "iface_clk" },
+		{ .name = "bus_clk" },
+		{ .name = "vsync_clk" },
+		{ .name = "tv_src_clk" },
+		{ .name = "tv_clk" },
+		{ .name = "pixel_mdp_clk" },
+		{ .name = "pixel_lcdc_clk" },
+		{ 0 }
+	},
+	.bus_port0 = MSM_BUS_MASTER_MDP_PORT0,
+	.bus_port1 = MSM_BUS_MASTER_MDP_PORT1,
+};
+
+static struct fs_driver_data rot_fs_data = {
+	.clks = (struct fs_clk_data[]){
+		{ .name = "core_clk" },
+		{ .name = "iface_clk" },
+		{ .name = "bus_clk" },
+		{ 0 }
+	},
+	.bus_port0 = MSM_BUS_MASTER_ROTATOR,
+};
+
+static struct fs_driver_data ved_fs_data = {
+	.clks = (struct fs_clk_data[]){
+		{ .name = "core_clk" },
+		{ .name = "iface_clk" },
+		{ .name = "bus_clk" },
+		{ 0 }
+	},
+	.bus_port0 = MSM_BUS_MASTER_HD_CODEC_PORT0,
+	.bus_port1 = MSM_BUS_MASTER_HD_CODEC_PORT1,
+};
+
+static struct fs_driver_data vfe_fs_data = {
+	.clks = (struct fs_clk_data[]){
+		{ .name = "core_clk" },
+		{ .name = "iface_clk" },
+		{ .name = "bus_clk" },
+		{ 0 }
+	},
+	.bus_port0 = MSM_BUS_MASTER_VFE,
+};
+
+static struct fs_driver_data vpe_fs_data = {
+	.clks = (struct fs_clk_data[]){
+		{ .name = "core_clk" },
+		{ .name = "iface_clk" },
+		{ .name = "bus_clk" },
+		{ 0 }
+	},
+	.bus_port0 = MSM_BUS_MASTER_VPE,
+};
+
+struct platform_device *msm8660_footswitch[] __initdata = {
+	FS_8X60(FS_IJPEG,  "vdd",	"msm_gemini.0",	&ijpeg_fs_data),
+	FS_8X60(FS_MDP,    "vdd",	"mdp.0",	&mdp_fs_data),
+	FS_8X60(FS_ROT,    "vdd",	"msm_rotator.0", &rot_fs_data),
+	FS_8X60(FS_VED,    "vdd",	"msm_vidc.0",	&ved_fs_data),
+	FS_8X60(FS_VFE,    "fs_vfe",	NULL,	&vfe_fs_data),
+	FS_8X60(FS_VPE,    "fs_vpe",	NULL,	&vpe_fs_data),
+	FS_8X60(FS_GFX3D,  "vdd",	"kgsl-3d0.0",	&gfx3d_fs_data),
+	FS_8X60(FS_GFX2D0, "vdd",	"kgsl-2d0.0",	&gfx2d0_fs_data),
+	FS_8X60(FS_GFX2D1, "vdd",	"kgsl-2d1.1",	&gfx2d1_fs_data),
+};
+unsigned msm8660_num_footswitch __initdata = ARRAY_SIZE(msm8660_footswitch);
 
 struct msm_rpm_platform_data msm8660_rpm_data __initdata = {
 	.reg_base_addrs = {
diff --git a/arch/arm/mach-msm/devices.h b/arch/arm/mach-msm/devices.h
index f7f0611..6a56c04 100644
--- a/arch/arm/mach-msm/devices.h
+++ b/arch/arm/mach-msm/devices.h
@@ -269,8 +269,14 @@
 
 extern struct platform_device *msm_footswitch_devices[];
 extern unsigned msm_num_footswitch_devices;
-extern struct platform_device *apq8064_fs_devices[];
-extern unsigned apq8064_num_fs_devices;
+extern struct platform_device *msm8660_footswitch[];
+extern unsigned msm8660_num_footswitch;
+extern struct platform_device *msm8960_footswitch[];
+extern unsigned msm8960_num_footswitch;
+extern struct platform_device *apq8064_footswitch[];
+extern unsigned apq8064_num_footswitch;
+extern struct platform_device *msm8930_footswitch[];
+extern unsigned msm8930_num_footswitch;
 
 extern struct platform_device fsm_qfp_fuse_device;
 
@@ -383,3 +389,5 @@
 extern struct platform_device msm8960_rtb_device;
 extern struct platform_device msm8930_rtb_device;
 extern struct platform_device apq8064_rtb_device;
+
+extern struct platform_device msm8960_cache_dump_device;
diff --git a/arch/arm/mach-msm/dma.c b/arch/arm/mach-msm/dma.c
index 4f34443..69c91f1 100644
--- a/arch/arm/mach-msm/dma.c
+++ b/arch/arm/mach-msm/dma.c
@@ -332,16 +332,17 @@
 }
 EXPORT_SYMBOL(msm_dmov_enqueue_cmd);
 
-void msm_dmov_flush(unsigned int id)
+void msm_dmov_flush(unsigned int id, int graceful)
 {
 	unsigned long irq_flags;
 	int ch = DMOV_ID_TO_CHAN(id);
 	int adm = DMOV_ID_TO_ADM(id);
+	int flush = graceful ? DMOV_FLUSH_TYPE : 0;
 	spin_lock_irqsave(&dmov_conf[adm].lock, irq_flags);
 	/* XXX not checking if flush cmd sent already */
 	if (!list_empty(&dmov_conf[adm].active_commands[ch])) {
 		PRINT_IO("msm_dmov_flush(%d), send flush cmd\n", id);
-		writel_relaxed(DMOV_FLUSH_TYPE, DMOV_REG(DMOV_FLUSH0(ch), adm));
+		writel_relaxed(flush, DMOV_REG(DMOV_FLUSH0(ch), adm));
 	}
 	/* spin_unlock_irqrestore has the necessary barrier */
 	spin_unlock_irqrestore(&dmov_conf[adm].lock, irq_flags);
diff --git a/arch/arm/mach-msm/footswitch-8x60.c b/arch/arm/mach-msm/footswitch-8x60.c
index eb334b9..72361a6 100644
--- a/arch/arm/mach-msm/footswitch-8x60.c
+++ b/arch/arm/mach-msm/footswitch-8x60.c
@@ -21,10 +21,8 @@
 #include <linux/regulator/machine.h>
 #include <linux/clk.h>
 #include <mach/msm_iomap.h>
-#include <mach/msm_bus_board.h>
 #include <mach/msm_bus.h>
 #include <mach/scm-io.h>
-#include <mach/socinfo.h>
 #include "clock.h"
 #include "footswitch.h"
 
@@ -51,6 +49,8 @@
 #define ENABLE_BIT		BIT(8)
 #define RETENTION_BIT		BIT(9)
 
+#define GFS_DELAY_CNT		31
+
 #define RESET_DELAY_US		1
 /* Clock rate to use if one has not previously been set. */
 #define DEFAULT_RATE		27000000
@@ -62,14 +62,6 @@
  */
 static DEFINE_MUTEX(claim_lock);
 
-struct clk_data {
-	const char *name;
-	struct clk *clk;
-	unsigned long rate;
-	unsigned long reset_rate;
-	bool enabled;
-};
-
 struct footswitch {
 	struct regulator_dev	*rdev;
 	struct regulator_desc	desc;
@@ -77,15 +69,14 @@
 	int			bus_port0, bus_port1;
 	bool			is_enabled;
 	bool			is_claimed;
-	struct clk_data		*clk_data;
+	struct fs_clk_data	*clk_data;
 	struct clk		*core_clk;
-	unsigned int		gfs_delay_cnt:5;
 };
 
 static int setup_clocks(struct footswitch *fs)
 {
 	int rc = 0;
-	struct clk_data *clock;
+	struct fs_clk_data *clock;
 	long rate;
 
 	/*
@@ -124,7 +115,7 @@
 
 static void restore_clocks(struct footswitch *fs)
 {
-	struct clk_data *clock;
+	struct fs_clk_data *clock;
 
 	/* Restore clocks to their orignal states before setup_clocks(). */
 	for (clock = fs->clk_data; clock->clk; clock++) {
@@ -146,7 +137,7 @@
 static int footswitch_enable(struct regulator_dev *rdev)
 {
 	struct footswitch *fs = rdev_get_drvdata(rdev);
-	struct clk_data *clock;
+	struct fs_clk_data *clock;
 	uint32_t regval, rc = 0;
 
 	mutex_lock(&claim_lock);
@@ -232,7 +223,7 @@
 static int footswitch_disable(struct regulator_dev *rdev)
 {
 	struct footswitch *fs = rdev_get_drvdata(rdev);
-	struct clk_data *clock;
+	struct fs_clk_data *clock;
 	uint32_t regval, rc = 0;
 
 	/* Return early if already disabled. */
@@ -308,7 +299,7 @@
 static int gfx2d_footswitch_enable(struct regulator_dev *rdev)
 {
 	struct footswitch *fs = rdev_get_drvdata(rdev);
-	struct clk_data *clock;
+	struct fs_clk_data *clock;
 	uint32_t regval, rc = 0;
 
 	mutex_lock(&claim_lock);
@@ -384,7 +375,7 @@
 static int gfx2d_footswitch_disable(struct regulator_dev *rdev)
 {
 	struct footswitch *fs = rdev_get_drvdata(rdev);
-	struct clk_data *clock;
+	struct fs_clk_data *clock;
 	uint32_t regval, rc = 0;
 
 	/* Return early if already disabled. */
@@ -461,108 +452,7 @@
 	.disable = gfx2d_footswitch_disable,
 };
 
-/*
- * Lists of required clocks for the collapse and restore sequences.
- *
- * Order matters here. Clocks are listed in the same order as their
- * resets will be de-asserted when the core is restored. Also, rate-
- * settable clocks must be listed before any of the branches that
- * are derived from them. Otherwise, the branches may fail to enable
- * if their parent's rate is not yet set.
- */
-
-static struct clk_data gfx2d0_clks[] = {
-	{ .name = "core_clk" },
-	{ .name = "iface_clk" },
-	{ 0 }
-};
-
-static struct clk_data gfx2d1_clks[] = {
-	{ .name = "core_clk" },
-	{ .name = "iface_clk" },
-	{ 0 }
-};
-
-static struct clk_data gfx3d_8660_clks[] = {
-	{ .name = "core_clk", .reset_rate = 27000000 },
-	{ .name = "iface_clk" },
-	{ 0 }
-};
-
-static struct clk_data gfx3d_8064_clks[] = {
-	{ .name = "core_clk", .reset_rate = 27000000 },
-	{ .name = "iface_clk" },
-	{ .name = "bus_clk" },
-	{ 0 }
-};
-
-static struct clk_data ijpeg_clks[] = {
-	{ .name = "core_clk" },
-	{ .name = "iface_clk" },
-	{ .name = "bus_clk" },
-	{ 0 }
-};
-
-static struct clk_data mdp_8960_clks[] = {
-	{ .name = "core_clk" },
-	{ .name = "iface_clk" },
-	{ .name = "bus_clk" },
-	{ .name = "vsync_clk" },
-	{ .name = "lut_clk" },
-	{ .name = "tv_src_clk" },
-	{ .name = "tv_clk" },
-	{ 0 }
-};
-
-static struct clk_data mdp_8660_clks[] = {
-	{ .name = "core_clk" },
-	{ .name = "iface_clk" },
-	{ .name = "bus_clk" },
-	{ .name = "vsync_clk" },
-	{ .name = "tv_src_clk" },
-	{ .name = "tv_clk" },
-	{ .name = "pixel_mdp_clk" },
-	{ .name = "pixel_lcdc_clk" },
-	{ 0 }
-};
-
-static struct clk_data rot_clks[] = {
-	{ .name = "core_clk" },
-	{ .name = "iface_clk" },
-	{ .name = "bus_clk" },
-	{ 0 }
-};
-
-static struct clk_data ved_clks[] = {
-	{ .name = "core_clk" },
-	{ .name = "iface_clk" },
-	{ .name = "bus_clk" },
-	{ 0 }
-};
-
-static struct clk_data vfe_clks[] = {
-	{ .name = "core_clk" },
-	{ .name = "iface_clk" },
-	{ .name = "bus_clk" },
-	{ 0 }
-};
-
-static struct clk_data vpe_clks[] = {
-	{ .name = "core_clk" },
-	{ .name = "iface_clk" },
-	{ .name = "bus_clk" },
-	{ 0 }
-};
-
-static struct clk_data vcap_clks[] = {
-	{ .name = "core_clk" },
-	{ .name = "iface_clk" },
-	{ .name = "bus_clk" },
-	{ 0 }
-};
-
-#define FOOTSWITCH(_id, _name, _ops, _gfs_ctl_reg, _dc, _clk_data, \
-		   _bp1, _bp2) \
+#define FOOTSWITCH(_id, _name, _ops, _gfs_ctl_reg) \
 	[(_id)] = { \
 		.desc = { \
 			.id = (_id), \
@@ -572,51 +462,26 @@
 			.owner = THIS_MODULE, \
 		}, \
 		.gfs_ctl_reg = (_gfs_ctl_reg), \
-		.gfs_delay_cnt = (_dc), \
-		.clk_data = (_clk_data), \
-		.bus_port0 = (_bp1), \
-		.bus_port1 = (_bp2), \
 	}
 static struct footswitch footswitches[] = {
-	FOOTSWITCH(FS_GFX2D0, "fs_gfx2d0", &gfx2d_fs_ops,
-		GFX2D0_GFS_CTL_REG, 31, gfx2d0_clks,
-		MSM_BUS_MASTER_GRAPHICS_2D_CORE0, 0),
-	FOOTSWITCH(FS_GFX2D1, "fs_gfx2d1", &gfx2d_fs_ops,
-		GFX2D1_GFS_CTL_REG, 31, gfx2d1_clks,
-		MSM_BUS_MASTER_GRAPHICS_2D_CORE1, 0),
-	FOOTSWITCH(FS_GFX3D, "fs_gfx3d", &standard_fs_ops,
-		GFX3D_GFS_CTL_REG, 31, gfx3d_8660_clks,
-		MSM_BUS_MASTER_GRAPHICS_3D, 0),
-	FOOTSWITCH(FS_IJPEG, "fs_ijpeg", &standard_fs_ops,
-		GEMINI_GFS_CTL_REG, 31, ijpeg_clks,
-		MSM_BUS_MASTER_JPEG_ENC, 0),
-	FOOTSWITCH(FS_MDP, "fs_mdp", &standard_fs_ops,
-		MDP_GFS_CTL_REG, 31, NULL,
-		MSM_BUS_MASTER_MDP_PORT0,
-		MSM_BUS_MASTER_MDP_PORT1),
-	FOOTSWITCH(FS_ROT, "fs_rot", &standard_fs_ops,
-		ROT_GFS_CTL_REG, 31, rot_clks,
-		MSM_BUS_MASTER_ROTATOR, 0),
-	FOOTSWITCH(FS_VED, "fs_ved", &standard_fs_ops,
-		VED_GFS_CTL_REG, 31, ved_clks,
-		MSM_BUS_MASTER_HD_CODEC_PORT0,
-		MSM_BUS_MASTER_HD_CODEC_PORT1),
-	FOOTSWITCH(FS_VFE, "fs_vfe", &standard_fs_ops,
-		VFE_GFS_CTL_REG, 31, vfe_clks,
-		MSM_BUS_MASTER_VFE, 0),
-	FOOTSWITCH(FS_VPE, "fs_vpe", &standard_fs_ops,
-		VPE_GFS_CTL_REG, 31, vpe_clks,
-		MSM_BUS_MASTER_VPE, 0),
-	FOOTSWITCH(FS_VCAP, "fs_vcap", &standard_fs_ops,
-		VCAP_GFS_CTL_REG, 31, vcap_clks,
-		MSM_BUS_MASTER_VIDEO_CAP, 0),
+	FOOTSWITCH(FS_GFX2D0, "fs_gfx2d0", &gfx2d_fs_ops, GFX2D0_GFS_CTL_REG),
+	FOOTSWITCH(FS_GFX2D1, "fs_gfx2d1", &gfx2d_fs_ops, GFX2D1_GFS_CTL_REG),
+	FOOTSWITCH(FS_GFX3D,  "fs_gfx3d", &standard_fs_ops, GFX3D_GFS_CTL_REG),
+	FOOTSWITCH(FS_IJPEG,  "fs_ijpeg", &standard_fs_ops, GEMINI_GFS_CTL_REG),
+	FOOTSWITCH(FS_MDP,    "fs_mdp",   &standard_fs_ops, MDP_GFS_CTL_REG),
+	FOOTSWITCH(FS_ROT,    "fs_rot",   &standard_fs_ops, ROT_GFS_CTL_REG),
+	FOOTSWITCH(FS_VED,    "fs_ved",   &standard_fs_ops, VED_GFS_CTL_REG),
+	FOOTSWITCH(FS_VFE,    "fs_vfe",   &standard_fs_ops, VFE_GFS_CTL_REG),
+	FOOTSWITCH(FS_VPE,    "fs_vpe",   &standard_fs_ops, VPE_GFS_CTL_REG),
+	FOOTSWITCH(FS_VCAP,   "fs_vcap",  &standard_fs_ops, VCAP_GFS_CTL_REG),
 };
 
 static int footswitch_probe(struct platform_device *pdev)
 {
 	struct footswitch *fs;
 	struct regulator_init_data *init_data;
-	struct clk_data *clock;
+	struct fs_driver_data *driver_data;
+	struct fs_clk_data *clock;
 	uint32_t regval, rc = 0;
 
 	if (pdev == NULL)
@@ -625,29 +490,12 @@
 	if (pdev->id >= MAX_FS)
 		return -ENODEV;
 
-	fs = &footswitches[pdev->id];
 	init_data = pdev->dev.platform_data;
-
-	if (pdev->id == FS_MDP) {
-		if (cpu_is_msm8960() || cpu_is_msm8930() || cpu_is_apq8064())
-			fs->clk_data = mdp_8960_clks;
-		else
-			fs->clk_data = mdp_8660_clks;
-	} else if (pdev->id == FS_GFX3D) {
-		if (cpu_is_apq8064()) {
-			fs->clk_data = gfx3d_8064_clks;
-			fs->bus_port1 = MSM_BUS_MASTER_GRAPHICS_3D_PORT1;
-		} else if (cpu_is_msm8930()) {
-			fs->clk_data = gfx3d_8064_clks;
-		} else {
-			fs->clk_data = gfx3d_8660_clks;
-		}
-	} else if (pdev->id == FS_VED) {
-		if (cpu_is_apq8064()) {
-			fs->bus_port0 = MSM_BUS_MASTER_VIDEO_ENC;
-			fs->bus_port1 = MSM_BUS_MASTER_VIDEO_DEC;
-		}
-	}
+	driver_data = init_data->driver_data;
+	fs = &footswitches[pdev->id];
+	fs->clk_data = driver_data->clks;
+	fs->bus_port0 = driver_data->bus_port0;
+	fs->bus_port1 = driver_data->bus_port1;
 
 	for (clock = fs->clk_data; clock->name; clock++) {
 		clock->clk = clk_get(&pdev->dev, clock->name);
@@ -667,7 +515,7 @@
 	 * clear so disabling the footswitch will power-collapse the core.
 	 */
 	regval = readl_relaxed(fs->gfs_ctl_reg);
-	regval |= fs->gfs_delay_cnt;
+	regval |= GFS_DELAY_CNT;
 	regval &= ~RETENTION_BIT;
 	writel_relaxed(regval, fs->gfs_ctl_reg);
 
@@ -692,7 +540,7 @@
 static int __devexit footswitch_remove(struct platform_device *pdev)
 {
 	struct footswitch *fs = &footswitches[pdev->id];
-	struct clk_data *clock;
+	struct fs_clk_data *clock;
 
 	for (clock = fs->clk_data; clock->clk; clock++)
 		clk_put(clock->clk);
diff --git a/arch/arm/mach-msm/footswitch.h b/arch/arm/mach-msm/footswitch.h
index 4882ff0..1809b2e 100644
--- a/arch/arm/mach-msm/footswitch.h
+++ b/arch/arm/mach-msm/footswitch.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2011 Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2010-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
@@ -30,7 +30,21 @@
 #define FS_VCAP		10
 #define MAX_FS		11
 
-#define FS_GENERIC(_drv_name, _id, _name) (&(struct platform_device){ \
+struct fs_clk_data {
+	const char *name;
+	struct clk *clk;
+	unsigned long rate;
+	unsigned long reset_rate;
+	bool enabled;
+};
+
+struct fs_driver_data {
+	int bus_port0, bus_port1;
+	struct fs_clk_data *clks;
+};
+
+#define FS_GENERIC(_drv_name, _id, _name, _dev_id, _data) \
+(&(struct platform_device){ \
 	.name	= (_drv_name), \
 	.id	= (_id), \
 	.dev	= { \
@@ -42,11 +56,14 @@
 			.num_consumer_supplies = 1, \
 			.consumer_supplies = \
 				&(struct regulator_consumer_supply) \
-				REGULATOR_SUPPLY((_name), NULL), \
+				REGULATOR_SUPPLY((_name), (_dev_id)), \
+			.driver_data = (_data), \
 		} \
 	}, \
 })
-#define FS_PCOM(_id, _name) FS_GENERIC("footswitch-pcom", (_id), (_name))
-#define FS_8X60(_id, _name) FS_GENERIC("footswitch-8x60", (_id), (_name))
+#define FS_PCOM(_id, _name, _dev_id) \
+		FS_GENERIC("footswitch-pcom", _id, _name, _dev_id, NULL)
+#define FS_8X60(_id, _name, _dev_id, _data) \
+		FS_GENERIC("footswitch-8x60", _id, _name, _dev_id, _data)
 
 #endif
diff --git a/arch/arm/mach-msm/include/mach/dma.h b/arch/arm/mach-msm/include/mach/dma.h
index 339a955..b3e536e 100644
--- a/arch/arm/mach-msm/include/mach/dma.h
+++ b/arch/arm/mach-msm/include/mach/dma.h
@@ -46,7 +46,7 @@
 void msm_dmov_enqueue_cmd(unsigned id, struct msm_dmov_cmd *cmd);
 void msm_dmov_enqueue_cmd_ext(unsigned id, struct msm_dmov_cmd *cmd);
 void msm_dmov_stop_cmd(unsigned id, struct msm_dmov_cmd *cmd, int graceful);
-void msm_dmov_flush(unsigned int id);
+void msm_dmov_flush(unsigned int id, int graceful);
 int msm_dmov_exec_cmd(unsigned id, unsigned int cmdptr);
 
 #define DMOV_CRCIS_PER_CONF 10
diff --git a/arch/arm/mach-msm/include/mach/qdss.h b/arch/arm/mach-msm/include/mach/qdss.h
index 3b236b8..05d8577 100644
--- a/arch/arm/mach-msm/include/mach/qdss.h
+++ b/arch/arm/mach-msm/include/mach/qdss.h
@@ -30,6 +30,7 @@
 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 int qdss_clk_enable(void);
 extern void qdss_clk_disable(void);
 #else
@@ -37,6 +38,7 @@
 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 int qdss_clk_enable(void) { return -ENOSYS; }
 static inline void qdss_clk_disable(void) {}
 #endif
diff --git a/arch/arm/mach-msm/include/mach/socinfo.h b/arch/arm/mach-msm/include/mach/socinfo.h
index ae0ea0c..09494ea 100644
--- a/arch/arm/mach-msm/include/mach/socinfo.h
+++ b/arch/arm/mach-msm/include/mach/socinfo.h
@@ -64,6 +64,7 @@
 	FSM_CPU_9XXX,
 	MSM_CPU_7X25A,
 	MSM_CPU_7X25AA,
+	MSM_CPU_7X25AB,
 	MSM_CPU_8064,
 	MSM_CPU_8930,
 	MSM_CPU_7X27AA,
@@ -170,6 +171,18 @@
 #endif
 }
 
+static inline int cpu_is_msm7x25ab(void)
+{
+#ifdef CONFIG_ARCH_MSM7X27A
+	enum msm_cpu cpu = socinfo_get_msm_cpu();
+
+	BUG_ON(cpu == MSM_CPU_UNKNOWN);
+	return cpu == MSM_CPU_7X25AB;
+#else
+	return 0;
+#endif
+}
+
 static inline int cpu_is_msm7x30(void)
 {
 #ifdef CONFIG_ARCH_MSM7X30
diff --git a/arch/arm/mach-msm/include/mach/usb_bam.h b/arch/arm/mach-msm/include/mach/usb_bam.h
index 4caa71b..ec135a3 100644
--- a/arch/arm/mach-msm/include/mach/usb_bam.h
+++ b/arch/arm/mach-msm/include/mach/usb_bam.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -14,6 +14,20 @@
 #define _USB_BAM_H_
 
 /**
+ * SPS Pipes direction.
+ *
+ * USB_TO_PEER_PERIPHERAL	USB (as Producer) to other
+ *                          peer peripheral.
+ * PEER_PERIPHERAL_TO_USB	Other Peripheral to
+ *                          USB (as consumer).
+ */
+enum usb_bam_pipe_dir {
+	USB_TO_PEER_PERIPHERAL,
+	PEER_PERIPHERAL_TO_USB,
+};
+
+#ifdef CONFIG_USB_BAM
+/**
  * Connect USB-to-Periperal SPS connection.
  *
  * This function returns the allocated pipes number.
@@ -29,12 +43,31 @@
  * @return 0 on success, negative value on error
  *
  */
-#ifdef CONFIG_USB_BAM
 int usb_bam_connect(u8 idx, u8 *src_pipe_idx, u8 *dst_pipe_idx);
+
+/**
+ * Register a wakeup callback from peer BAM.
+ *
+ * @idx - Connection index.
+ *
+ * @callback - the callback function
+ *
+ * @return 0 on success, negative value on error
+ *
+ */
+int usb_bam_register_wake_cb(u8 idx,
+	 int (*callback)(void *), void* param);
 #else
-int usb_bam_connect(u8 idx, u8 *src_pipe_idx, u8 *dst_pipe_idx)
+static inline int usb_bam_connect(u8 idx, u8 *src_pipe_idx, u8 *dst_pipe_idx)
+{
+	return -ENODEV;
+}
+
+static inline int usb_bam_register_wake_cb(u8 idx,
+	int (*callback)(void *), void* param)
 {
 	return -ENODEV;
 }
 #endif
 #endif				/* _USB_BAM_H_ */
+
diff --git a/arch/arm/mach-msm/lpm_levels.c b/arch/arm/mach-msm/lpm_levels.c
new file mode 100644
index 0000000..48cf3f7
--- /dev/null
+++ b/arch/arm/mach-msm/lpm_levels.c
@@ -0,0 +1,232 @@
+/* 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/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <mach/mpm.h>
+#include "rpm_resources.h"
+
+static struct msm_rpmrs_level *msm_lpm_levels;
+static int msm_lpm_level_count;
+
+int msm_rpmrs_enter_sleep(uint32_t sclk_count, struct msm_rpmrs_limits *limits,
+		bool from_idle, bool notify_rpm)
+{
+	/* TODO */
+	return 0;
+}
+
+void msm_rpmrs_exit_sleep(struct msm_rpmrs_limits *limits, bool from_idle,
+		bool notify_rpm, bool collapsed)
+{
+	/* TODO */
+	return;
+}
+
+static bool msm_rpmrs_irqs_detectable(struct msm_rpmrs_limits *limits,
+		bool irqs_detect, bool gpio_detect)
+{
+	/* TODO */
+	return true;
+}
+
+void msm_rpmrs_show_resources(void)
+{
+	/* TODO */
+	return;
+}
+
+struct msm_rpmrs_limits *msm_rpmrs_lowest_limits(
+	bool from_idle, enum msm_pm_sleep_mode sleep_mode, uint32_t latency_us,
+	uint32_t sleep_us)
+{
+	unsigned int cpu = smp_processor_id();
+	struct msm_rpmrs_level *best_level = NULL;
+	bool irqs_detectable = false;
+	bool gpio_detectable = false;
+	int i;
+
+	if (!msm_lpm_levels)
+		return NULL;
+
+	if (sleep_mode == MSM_PM_SLEEP_MODE_POWER_COLLAPSE) {
+		irqs_detectable = msm_mpm_irqs_detectable(from_idle);
+		gpio_detectable = msm_mpm_gpio_irqs_detectable(from_idle);
+	}
+
+	for (i = 0; i < msm_lpm_level_count; i++) {
+		struct msm_rpmrs_level *level = &msm_lpm_levels[i];
+		uint32_t power;
+
+		if (!level->available)
+			continue;
+
+		if (sleep_mode != level->sleep_mode)
+			continue;
+
+		if (latency_us < level->latency_us)
+			continue;
+
+		if (!msm_rpmrs_irqs_detectable(&level->rs_limits,
+					irqs_detectable, gpio_detectable))
+			continue;
+
+		if (sleep_us <= 1) {
+			power = level->energy_overhead;
+		} else if (sleep_us <= level->time_overhead_us) {
+			power = level->energy_overhead / sleep_us;
+		} else if ((sleep_us >> 10) > level->time_overhead_us) {
+			power = level->steady_state_power;
+		} else {
+			power = level->steady_state_power;
+			power -= (level->time_overhead_us *
+					level->steady_state_power)/sleep_us;
+			power += level->energy_overhead / sleep_us;
+		}
+
+		if (!best_level ||
+				best_level->rs_limits.power[cpu] >= power) {
+			level->rs_limits.latency_us[cpu] = level->latency_us;
+			level->rs_limits.power[cpu] = power;
+			best_level = level;
+		}
+	}
+
+	return best_level ? &best_level->rs_limits : NULL;
+}
+
+static int __devinit msm_lpm_levels_probe(struct platform_device *pdev)
+{
+	struct msm_rpmrs_level *levels = NULL;
+	struct msm_rpmrs_level *level = NULL;
+	struct device_node *node = NULL;
+	char *key = NULL;
+	uint32_t val = 0;
+	int ret = 0;
+	uint32_t num_levels = 0;
+	int idx = 0;
+
+	for_each_child_of_node(pdev->dev.of_node, node)
+		num_levels++;
+
+	levels = kzalloc(num_levels * sizeof(struct msm_rpmrs_level),
+			GFP_KERNEL);
+	if (!levels)
+		return -ENOMEM;
+
+	for_each_child_of_node(pdev->dev.of_node, node) {
+		level = &levels[idx++];
+		level->available = false;
+
+		key = "qcom,mode";
+		ret = of_property_read_u32(node, key, &val);
+		if (ret)
+			goto fail;
+		level->sleep_mode = val;
+
+		key = "qcom,xo";
+		ret = of_property_read_u32(node, key, &val);
+		if (ret)
+			goto fail;
+		level->rs_limits.pxo = val;
+
+		key = "qcom,l2";
+		ret = of_property_read_u32(node, key, &val);
+		if (ret)
+			goto fail;
+		level->rs_limits.l2_cache = val;
+
+		key = "qcom,vdd-dig-upper-bound";
+		ret = of_property_read_u32(node, key, &val);
+		if (ret)
+			goto fail;
+		level->rs_limits.vdd_dig_upper_bound = val;
+
+		key = "qcom,vdd-dig-lower-bound";
+		ret = of_property_read_u32(node, key, &val);
+		if (ret)
+			goto fail;
+		level->rs_limits.vdd_dig = val;
+
+		key = "qcom,vdd-mem-upper-bound";
+		ret = of_property_read_u32(node, key, &val);
+		if (ret)
+			goto fail;
+		level->rs_limits.vdd_mem_upper_bound = val;
+
+		key = "qcom,vdd-mem-lower-bound";
+		ret = of_property_read_u32(node, key, &val);
+		if (ret)
+			goto fail;
+		level->rs_limits.vdd_mem = val;
+
+		key = "qcom,latency-us";
+		ret = of_property_read_u32(node, key, &val);
+		if (ret)
+			goto fail;
+		level->latency_us = val;
+
+		key = "qcom,ss-power";
+		ret = of_property_read_u32(node, key, &val);
+		if (ret)
+			goto fail;
+		level->steady_state_power = val;
+
+		key = "qcom,energy-overhead";
+		ret = of_property_read_u32(node, key, &val);
+		if (ret)
+			goto fail;
+		level->energy_overhead = val;
+
+		key = "qcom,time-overhead";
+		ret = of_property_read_u32(node, key, &val);
+		if (ret)
+			goto fail;
+		level->time_overhead_us = val;
+
+		level->available = true;
+	}
+
+	msm_lpm_levels = levels;
+	msm_lpm_level_count = idx;
+
+	return 0;
+fail:
+	pr_err("%s: Error in name %s key %s\n", __func__, node->full_name, key);
+	kfree(levels);
+	return -EFAULT;
+}
+
+static struct of_device_id msm_lpm_levels_match_table[] = {
+	{.compatible = "qcom,lpm-levels"},
+	{},
+};
+
+static struct platform_driver msm_lpm_levels_driver = {
+	.probe = msm_lpm_levels_probe,
+	.driver = {
+		.name = "lpm-levels",
+		.owner = THIS_MODULE,
+		.of_match_table = msm_lpm_levels_match_table,
+	},
+};
+
+static int __init msm_lpm_levels_module_init(void)
+{
+	return platform_driver_register(&msm_lpm_levels_driver);
+}
+late_initcall(msm_lpm_levels_module_init);
diff --git a/arch/arm/mach-msm/platsmp.c b/arch/arm/mach-msm/platsmp.c
index 668e0c2..b66729b 100644
--- a/arch/arm/mach-msm/platsmp.c
+++ b/arch/arm/mach-msm/platsmp.c
@@ -75,8 +75,10 @@
 	if (machine_is_apq8064_sim())
 		writel_relaxed(0xf0000, base_ptr+0x04);
 
-	if (machine_is_copper_sim())
-		writel_relaxed(0x9, base_ptr+0x04);
+	if (machine_is_copper_sim()) {
+		writel_relaxed(0x800, base_ptr+0x04);
+		writel_relaxed(0x3FFF, base_ptr+0x14);
+	}
 
 	mb();
 	iounmap(base_ptr);
diff --git a/arch/arm/mach-msm/pm-8x60.c b/arch/arm/mach-msm/pm-8x60.c
index a22b175..1d3a31e 100644
--- a/arch/arm/mach-msm/pm-8x60.c
+++ b/arch/arm/mach-msm/pm-8x60.c
@@ -29,6 +29,7 @@
 #include <linux/wakelock.h>
 #include <linux/delay.h>
 #include <mach/msm_iomap.h>
+#include <mach/socinfo.h>
 #include <mach/system.h>
 #include <asm/cacheflush.h>
 #include <asm/hardware/gic.h>
@@ -612,6 +613,7 @@
  *****************************************************************************/
 
 static struct msm_rpmrs_limits *msm_pm_idle_rs_limits;
+static bool msm_pm_use_qtimer;
 
 static void msm_pm_swfi(void)
 {
@@ -765,6 +767,28 @@
 	return collapsed;
 }
 
+static void msm_pm_qtimer_available(void)
+{
+	if (machine_is_copper())
+		msm_pm_use_qtimer = true;
+}
+
+static int64_t msm_pm_timer_enter_idle(void)
+{
+	if (msm_pm_use_qtimer)
+		return ktime_to_ns(tick_nohz_get_sleep_length());
+
+	return msm_timer_enter_idle();
+}
+
+static void msm_pm_timer_exit_idle(bool timer_halted)
+{
+	if (msm_pm_use_qtimer)
+		return;
+
+	msm_timer_exit_idle((int) timer_halted);
+}
+
 /******************************************************************************
  * External Idle/Suspend Functions
  *****************************************************************************/
@@ -919,7 +943,7 @@
 		break;
 
 	case MSM_PM_SLEEP_MODE_POWER_COLLAPSE: {
-		int64_t timer_expiration = msm_timer_enter_idle();
+		int64_t timer_expiration = 0;
 		bool timer_halted = false;
 		uint32_t sleep_delay;
 		int ret;
@@ -927,6 +951,8 @@
 			(sleep_mode == MSM_PM_SLEEP_MODE_POWER_COLLAPSE);
 		int collapsed;
 
+		timer_expiration = msm_pm_timer_enter_idle();
+
 		sleep_delay = (uint32_t) msm_pm_convert_and_cap_time(
 			timer_expiration, MSM_PM_SLEEP_TICK_LIMIT);
 		if (sleep_delay == 0) /* 0 would mean infinite time */
@@ -944,8 +970,8 @@
 			msm_rpmrs_exit_sleep(msm_pm_idle_rs_limits, true,
 					notify_rpm, collapsed);
 		}
+		msm_pm_timer_exit_idle(timer_halted);
 
-		msm_timer_exit_idle((int) timer_halted);
 #ifdef CONFIG_MSM_IDLE_STATS
 		exit_stat = MSM_PM_STAT_IDLE_POWER_COLLAPSE;
 #endif
@@ -1132,7 +1158,6 @@
 			} else
 				time = 0;
 		}
-
 		msm_pm_add_stat(MSM_PM_STAT_SUSPEND, time);
 #endif /* CONFIG_MSM_IDLE_STATS */
 	} else if (allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE]) {
@@ -1176,11 +1201,10 @@
 	pgd_t *pc_pgd;
 	pmd_t *pmd;
 	unsigned long pmdval;
-	unsigned int cpu;
 #ifdef CONFIG_MSM_IDLE_STATS
+	unsigned int cpu;
 	struct proc_dir_entry *d_entry;
 #endif
-
 	/* Page table for cores to come back up safely. */
 	pc_pgd = pgd_alloc(&init_mm);
 	if (!pc_pgd)
@@ -1258,10 +1282,12 @@
 	}
 #endif  /* CONFIG_MSM_IDLE_STATS */
 
+
 	msm_pm_mode_sysfs_add();
 	msm_spm_allow_x_cpu_set_vdd(false);
 
 	suspend_set_ops(&msm_pm_ops);
+	msm_pm_qtimer_available();
 	msm_cpuidle_init();
 
 	return 0;
diff --git a/arch/arm/mach-msm/pm-boot.c b/arch/arm/mach-msm/pm-boot.c
index b5f0fdc..f6105af1 100644
--- a/arch/arm/mach-msm/pm-boot.c
+++ b/arch/arm/mach-msm/pm-boot.c
@@ -15,6 +15,10 @@
 #include <linux/kernel.h>
 #include <linux/init.h>
 #include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
 #include <mach/msm_iomap.h>
 #include <mach/socinfo.h>
 #include <asm/mach-types.h>
@@ -200,3 +204,69 @@
 
 	return ret;
 }
+
+static int __devinit msm_pm_boot_probe(struct platform_device *pdev)
+{
+	struct msm_pm_boot_platform_data pdata;
+	char *key = NULL;
+	uint32_t val = 0;
+	int ret = 0;
+	int flag = 0;
+
+	key = "qcom,mode";
+	ret = of_property_read_u32(pdev->dev.of_node, key, &val);
+	if (ret) {
+		pr_err("Unable to read boot mode Err(%d).\n", ret);
+		return -ENODEV;
+	}
+	pdata.mode = val;
+
+	key = "qcom,phy-addr";
+	ret = of_property_read_u32(pdev->dev.of_node, key, &val);
+	if (ret && pdata.mode == MSM_PM_BOOT_CONFIG_RESET_VECTOR_PHYS)
+		goto fail;
+	if (!ret) {
+		pdata.p_addr = val;
+		flag++;
+	}
+
+	key = "qcom,virt-addr";
+	ret = of_property_read_u32(pdev->dev.of_node, key, &val);
+	if (ret && pdata.mode == MSM_PM_BOOT_CONFIG_RESET_VECTOR_VIRT)
+		goto fail;
+	if (!ret) {
+		pdata.v_addr = (void *)val;
+		flag++;
+	}
+
+	if (pdata.mode == MSM_PM_BOOT_CONFIG_REMAP_BOOT_ADDR && (flag != 2)) {
+		key = "addresses for boot remap";
+		goto fail;
+	}
+
+	return msm_pm_boot_init(&pdata);
+
+fail:
+	pr_err("Error reading %s\n", key);
+	return -EFAULT;
+}
+
+static struct of_device_id msm_pm_match_table[] = {
+	{.compatible = "qcom,pm-boot"},
+	{},
+};
+
+static struct platform_driver msm_pm_boot_driver = {
+	.probe = msm_pm_boot_probe,
+	.driver = {
+		.name = "pm-boot",
+		.owner = THIS_MODULE,
+		.of_match_table = msm_pm_match_table,
+	},
+};
+
+static int __init msm_pm_boot_module_init(void)
+{
+	return platform_driver_register(&msm_pm_boot_driver);
+}
+module_init(msm_pm_boot_module_init);
diff --git a/arch/arm/mach-msm/pm2.c b/arch/arm/mach-msm/pm2.c
index 39e321a..5726972 100644
--- a/arch/arm/mach-msm/pm2.c
+++ b/arch/arm/mach-msm/pm2.c
@@ -465,7 +465,8 @@
 		__raw_writel(1, APPS_PWRDOWN);
 		mb();
 	} else if (cpu_is_msm7x27a() || cpu_is_msm7x27aa() ||
-		   cpu_is_msm7x25a() || cpu_is_msm7x25aa()) {
+		   cpu_is_msm7x25a() || cpu_is_msm7x25aa() ||
+		   cpu_is_msm7x25ab()) {
 		__raw_writel(0x7, APPS_CLK_SLEEP_EN);
 		mb();
 		__raw_writel(1, APPS_PWRDOWN);
@@ -591,7 +592,8 @@
 		__raw_writel(0x0f, APPS_CLK_SLEEP_EN);
 		mb();
 	} else if (cpu_is_msm7x27a() || cpu_is_msm7x27aa() ||
-		   cpu_is_msm7x25a() || cpu_is_msm7x25aa()) {
+		   cpu_is_msm7x25a() || cpu_is_msm7x25aa() ||
+		   cpu_is_msm7x25ab()) {
 		__raw_writel(0x7, APPS_CLK_SLEEP_EN);
 		mb();
 	}
diff --git a/arch/arm/mach-msm/qdsp5/adsp.c b/arch/arm/mach-msm/qdsp5/adsp.c
index 71e0409..6f5ccbf 100644
--- a/arch/arm/mach-msm/qdsp5/adsp.c
+++ b/arch/arm/mach-msm/qdsp5/adsp.c
@@ -34,6 +34,7 @@
 #include <linux/wait.h>
 #include <linux/wakelock.h>
 #include <linux/slab.h>
+#include <linux/workqueue.h>
 #include <mach/debug_mm.h>
 #include <linux/debugfs.h>
 
@@ -71,6 +72,10 @@
 static uint32_t rpc_adsp_rtos_mtoa_vers_comp;
 static DEFINE_MUTEX(adsp_open_lock);
 
+static struct workqueue_struct *msm_adsp_probe_work_queue;
+static void adsp_probe_work(struct work_struct *work);
+static DECLARE_WORK(msm_adsp_probe_work, adsp_probe_work);
+
 /* protect interactions with the ADSP command/message queue */
 static spinlock_t adsp_cmd_lock;
 static spinlock_t adsp_write_lock;
@@ -1230,8 +1235,8 @@
 		goto fail_rpc_register;
 	}
 
-	/* start the kernel thread to process the callbacks */
-	kthread_run(adsp_rpc_thread, NULL, "kadspd");
+	/* schedule start of kernel thread later using work queue */
+	queue_work(msm_adsp_probe_work_queue, &msm_adsp_probe_work);
 
 	for (i = 0; i < count; i++) {
 		struct msm_adsp_module *mod = adsp_modules + i;
@@ -1271,6 +1276,13 @@
 	kfree(adsp_info.init_info_ptr);
 	return rc;
 }
+
+static void adsp_probe_work(struct work_struct *work)
+{
+	/* start the kernel thread to process the callbacks */
+	kthread_run(adsp_rpc_thread, NULL, "kadspd");
+}
+
 #ifdef CONFIG_DEBUG_FS
 static int get_parameters(char *buf, long int *param1, int num_of_par)
 {
@@ -1433,6 +1445,9 @@
 	rpc_adsp_rtos_mtoa_vers_comp = 0x00030001;
 #endif
 
+	msm_adsp_probe_work_queue = create_workqueue("msm_adsp_probe");
+	if (msm_adsp_probe_work_queue == NULL)
+		return -ENOMEM;
 	msm_adsp_driver.driver.name = msm_adsp_driver_name;
 	rc = platform_driver_register(&msm_adsp_driver);
 	MM_INFO("%s -- %d\n", msm_adsp_driver_name, rc);
diff --git a/arch/arm/mach-msm/qdsp5/audio_aac.c b/arch/arm/mach-msm/qdsp5/audio_aac.c
index de756eb..8cae321 100644
--- a/arch/arm/mach-msm/qdsp5/audio_aac.c
+++ b/arch/arm/mach-msm/qdsp5/audio_aac.c
@@ -39,7 +39,6 @@
 #include <mach/msm_adsp.h>
 #include <mach/iommu.h>
 #include <mach/iommu_domains.h>
-#include <mach/msm_subsystem_map.h>
 #include <mach/qdsp5/qdsp5audppcmdi.h>
 #include <mach/qdsp5/qdsp5audppmsg.h>
 #include <mach/qdsp5/qdsp5audplaycmdi.h>
@@ -126,8 +125,8 @@
 	/* ---- End of Host PCM section */
 
 	struct msm_adsp_module *audplay;
-	struct msm_mapped_buffer *map_v_read;
-	struct msm_mapped_buffer *map_v_write;
+	void *map_v_read;
+	void *map_v_write;
 
 	/* configuration to use on next enable */
 	uint32_t out_sample_rate;
@@ -1509,9 +1508,9 @@
 	audio->event_abort = 1;
 	wake_up(&audio->event_wait);
 	audaac_reset_event_queue(audio);
-	msm_subsystem_unmap_buffer(audio->map_v_write);
+	iounmap(audio->map_v_write);
 	free_contiguous_memory_by_paddr(audio->phys);
-	msm_subsystem_unmap_buffer(audio->map_v_read);
+	iounmap(audio->map_v_read);
 	free_contiguous_memory_by_paddr(audio->read_phys);
 	mutex_unlock(&audio->lock);
 #ifdef CONFIG_DEBUG_FS
@@ -1701,10 +1700,7 @@
 		MM_DBG("pmemsz = %d\n", pmem_sz);
 		audio->phys = allocate_contiguous_ebi_nomap(pmem_sz, SZ_4K);
 		if (audio->phys) {
-			audio->map_v_write = msm_subsystem_map_buffer(
-							audio->phys, pmem_sz,
-							MSM_SUBSYSTEM_MAP_KADDR,
-							NULL, 0);
+			audio->map_v_write = ioremap(audio->phys, pmem_sz);
 			if (IS_ERR(audio->map_v_write)) {
 				MM_ERR("could not map write buffers, \
 						freeing instance 0x%08x\n",
@@ -1715,7 +1711,7 @@
 				kfree(audio);
 				goto done;
 			}
-			audio->data = audio->map_v_write->vaddr;
+			audio->data = audio->map_v_write;
 			MM_DBG("write buf: phy addr 0x%08x kernel addr \
 				0x%08x\n", audio->phys, (int)audio->data);
 			break;
@@ -1737,29 +1733,26 @@
 		MM_ERR("could not allocate read buffers, freeing instance \
 				0x%08x\n", (int)audio);
 		rc = -ENOMEM;
-		msm_subsystem_unmap_buffer(audio->map_v_write);
+		iounmap(audio->map_v_write);
 		free_contiguous_memory_by_paddr(audio->phys);
 		audpp_adec_free(audio->dec_id);
 		kfree(audio);
 		goto done;
 	}
-	audio->map_v_read = msm_subsystem_map_buffer(
-					audio->read_phys,
-					PCM_BUFSZ_MIN * PCM_BUF_MAX_COUNT,
-					MSM_SUBSYSTEM_MAP_KADDR,
-					NULL, 0);
+	audio->map_v_read = ioremap(audio->read_phys,
+					PCM_BUFSZ_MIN * PCM_BUF_MAX_COUNT);
 	if (IS_ERR(audio->map_v_read)) {
 		MM_ERR("could not map read buffers, freeing instance \
 				0x%08x\n", (int)audio);
 		rc = -ENOMEM;
-		msm_subsystem_unmap_buffer(audio->map_v_write);
+		iounmap(audio->map_v_write);
 		free_contiguous_memory_by_paddr(audio->phys);
 		free_contiguous_memory_by_paddr(audio->read_phys);
 		audpp_adec_free(audio->dec_id);
 		kfree(audio);
 		goto done;
 	}
-	audio->read_data = audio->map_v_read->vaddr;
+	audio->read_data = audio->map_v_read;
 	MM_DBG("read buf: phy addr 0x%08x kernel addr 0x%08x\n",
 				audio->read_phys, (int)audio->read_data);
 
@@ -1880,9 +1873,9 @@
 done:
 	return rc;
 err:
-	msm_subsystem_unmap_buffer(audio->map_v_write);
+	iounmap(audio->map_v_write);
 	free_contiguous_memory_by_paddr(audio->phys);
-	msm_subsystem_unmap_buffer(audio->map_v_read);
+	iounmap(audio->map_v_read);
 	free_contiguous_memory_by_paddr(audio->read_phys);
 	audpp_adec_free(audio->dec_id);
 	kfree(audio);
diff --git a/arch/arm/mach-msm/qdsp5/audio_aac_in.c b/arch/arm/mach-msm/qdsp5/audio_aac_in.c
index 456a8ff..ab9f15c 100644
--- a/arch/arm/mach-msm/qdsp5/audio_aac_in.c
+++ b/arch/arm/mach-msm/qdsp5/audio_aac_in.c
@@ -40,7 +40,6 @@
 #include <mach/msm_rpcrouter.h>
 #include <mach/msm_memtypes.h>
 #include <mach/iommu.h>
-#include <mach/msm_subsystem_map.h>
 #include <mach/iommu_domains.h>
 
 #include <mach/msm_adsp.h>
@@ -141,8 +140,8 @@
 	/* data allocated for various buffers */
 	char *data;
 	dma_addr_t phys;
-	struct msm_mapped_buffer *map_v_read;
-	struct msm_mapped_buffer *map_v_write;
+	void *map_v_read;
+	void *map_v_write;
 
 	int opened;
 	int enabled;
@@ -1249,13 +1248,13 @@
 
 	if ((audio->mode == MSM_AUD_ENC_MODE_NONTUNNEL) && \
 	   (audio->out_data)) {
-		msm_subsystem_unmap_buffer(audio->map_v_write);
+		iounmap(audio->map_v_write);
 		free_contiguous_memory_by_paddr(audio->out_phys);
 		audio->out_data = NULL;
 	}
 
 	if (audio->data) {
-		msm_subsystem_unmap_buffer(audio->map_v_read);
+		iounmap(audio->map_v_read);
 		free_contiguous_memory_by_paddr(audio->phys);
 		audio->data = NULL;
 	}
@@ -1355,16 +1354,15 @@
 
 	audio->phys = allocate_contiguous_ebi_nomap(dma_size, SZ_4K);
 	if (audio->phys) {
-		audio->map_v_read = msm_subsystem_map_buffer(
-					audio->phys, dma_size,
-					MSM_SUBSYSTEM_MAP_KADDR, NULL, 0);
+		audio->map_v_read = ioremap(
+					audio->phys, dma_size);
 		if (IS_ERR(audio->map_v_read)) {
 			MM_ERR("could not map DMA buffers\n");
 			rc = -ENOMEM;
 			free_contiguous_memory_by_paddr(audio->phys);
 			goto evt_error;
 		}
-		audio->data = audio->map_v_read->vaddr;
+		audio->data = audio->map_v_read;
 	} else {
 		MM_ERR("could not allocate read buffers\n");
 		rc = -ENOMEM;
@@ -1380,23 +1378,22 @@
 		if (!audio->out_phys) {
 			MM_ERR("could not allocate write buffers\n");
 			rc = -ENOMEM;
-			msm_subsystem_unmap_buffer(audio->map_v_read);
+			iounmap(audio->map_v_read);
 			free_contiguous_memory_by_paddr(audio->phys);
 			goto evt_error;
 		} else {
-			audio->map_v_write = msm_subsystem_map_buffer(
-					audio->out_phys, BUFFER_SIZE,
-					MSM_SUBSYSTEM_MAP_KADDR, NULL, 0);
+			audio->map_v_write = ioremap(
+					audio->out_phys, BUFFER_SIZE);
 			if (IS_ERR(audio->map_v_write)) {
 				MM_ERR("could not map write phys address\n");
 				rc = -ENOMEM;
-				msm_subsystem_unmap_buffer(audio->map_v_read);
+				iounmap(audio->map_v_read);
 				free_contiguous_memory_by_paddr(audio->phys);
 				free_contiguous_memory_by_paddr(\
 						audio->out_phys);
 				goto evt_error;
 			}
-			audio->out_data = audio->map_v_write->vaddr;
+			audio->out_data = audio->map_v_write;
 			MM_DBG("wr buf: phy addr 0x%08x kernel addr 0x%08x\n",
 					audio->out_phys, (int)audio->out_data);
 		}
diff --git a/arch/arm/mach-msm/qdsp5/audio_amrnb.c b/arch/arm/mach-msm/qdsp5/audio_amrnb.c
index d66a270..1ad941f 100644
--- a/arch/arm/mach-msm/qdsp5/audio_amrnb.c
+++ b/arch/arm/mach-msm/qdsp5/audio_amrnb.c
@@ -47,7 +47,6 @@
 #include <mach/msm_adsp.h>
 #include <mach/iommu.h>
 #include <mach/iommu_domains.h>
-#include <mach/msm_subsystem_map.h>
 #include <mach/qdsp5/qdsp5audppcmdi.h>
 #include <mach/qdsp5/qdsp5audppmsg.h>
 #include <mach/qdsp5/qdsp5audplaycmdi.h>
@@ -138,8 +137,8 @@
 	/* data allocated for various buffers */
 	char *data;
 	int32_t phys; /* physical address of write buffer */
-	struct msm_mapped_buffer *map_v_read;
-	struct msm_mapped_buffer *map_v_write;
+	void *map_v_read;
+	void *map_v_write;
 
 
 	int mfield; /* meta field embedded in data */
@@ -979,12 +978,10 @@
 					rc = -ENOMEM;
 					break;
 			}
-			audio->map_v_read = msm_subsystem_map_buffer(
+			audio->map_v_read = ioremap(
 						audio->read_phys,
 						config.buffer_size *
-						config.buffer_count,
-						MSM_SUBSYSTEM_MAP_KADDR,
-						NULL, 0);
+						config.buffer_count);
 			if (IS_ERR(audio->map_v_read)) {
 				MM_ERR("failed to map read buf\n");
 				rc = -ENOMEM;
@@ -994,7 +991,7 @@
 				uint8_t index;
 				uint32_t offset = 0;
 				audio->read_data =
-						audio->map_v_read->vaddr;
+						audio->map_v_read;
 				audio->buf_refresh = 0;
 				audio->pcm_buf_count =
 					config.buffer_count;
@@ -1298,10 +1295,10 @@
 	audio->event_abort = 1;
 	wake_up(&audio->event_wait);
 	audamrnb_reset_event_queue(audio);
-	msm_subsystem_unmap_buffer(audio->map_v_write);
+	iounmap(audio->map_v_write);
 	free_contiguous_memory_by_paddr(audio->phys);
 	if (audio->read_data) {
-		msm_subsystem_unmap_buffer(audio->map_v_read);
+		iounmap(audio->map_v_read);
 		free_contiguous_memory_by_paddr(audio->read_phys);
 	}
 	mutex_unlock(&audio->lock);
@@ -1494,10 +1491,8 @@
 		kfree(audio);
 		goto done;
 	} else {
-		audio->map_v_write = msm_subsystem_map_buffer(
-					audio->phys, DMASZ,
-					MSM_SUBSYSTEM_MAP_KADDR,
-					NULL, 0);
+		audio->map_v_write = ioremap(
+					audio->phys, DMASZ);
 		if (IS_ERR(audio->map_v_write)) {
 			MM_ERR("could not map write buffers, freeing \
 					instance 0x%08x freeing\n", (int)audio);
@@ -1507,7 +1502,7 @@
 			kfree(audio);
 			goto done;
 		}
-		audio->data = audio->map_v_write->vaddr;
+		audio->data = audio->map_v_write;
 		MM_DBG("write buf: phy addr 0x%08x kernel addr \
 				0x%08x\n", audio->phys, (int)audio->data);
 	}
@@ -1595,7 +1590,7 @@
 done:
 	return rc;
 err:
-	msm_subsystem_unmap_buffer(audio->map_v_write);
+	iounmap(audio->map_v_write);
 	free_contiguous_memory_by_paddr(audio->phys);
 	audpp_adec_free(audio->dec_id);
 	kfree(audio);
diff --git a/arch/arm/mach-msm/qdsp5/audio_amrnb_in.c b/arch/arm/mach-msm/qdsp5/audio_amrnb_in.c
index 2b4ead9..d4e4893 100644
--- a/arch/arm/mach-msm/qdsp5/audio_amrnb_in.c
+++ b/arch/arm/mach-msm/qdsp5/audio_amrnb_in.c
@@ -2,7 +2,7 @@
  *
  * amrnb encoder device
  *
- * Copyright (c) 2009, 2011 Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2009, 2011-2012 Code Aurora Forum. All rights reserved.
  *
  * This code is based in part on arch/arm/mach-msm/qdsp5/audio_in.c, which is
  * Copyright (C) 2008 Google, Inc.
@@ -44,7 +44,6 @@
 #include <mach/msm_rpcrouter.h>
 #include <mach/msm_memtypes.h>
 #include <mach/iommu.h>
-#include <mach/msm_subsystem_map.h>
 #include <mach/iommu_domains.h>
 
 #include <mach/msm_adsp.h>
@@ -146,7 +145,7 @@
 	/* data allocated for various buffers */
 	char *data;
 	dma_addr_t phys;
-	struct msm_mapped_buffer *map_v_write;
+	void *map_v_write;
 
 	uint8_t opened;
 	uint8_t enabled;
@@ -1206,7 +1205,7 @@
 	audio->opened = 0;
 	if ((audio->mode == MSM_AUD_ENC_MODE_NONTUNNEL) && \
 	   (audio->out_data)) {
-		msm_subsystem_unmap_buffer(audio->map_v_write);
+		iounmap(audio->map_v_write);
 		free_contiguous_memory_by_paddr(audio->out_phys);
 		audio->out_data = NULL;
 	}
@@ -1330,9 +1329,8 @@
 				dma_size, audio->data, audio->phys);
 			goto evt_error;
 		} else {
-			audio->map_v_write = msm_subsystem_map_buffer(
-					audio->out_phys, BUFFER_SIZE,
-					MSM_SUBSYSTEM_MAP_KADDR, NULL, 0);
+			audio->map_v_write = ioremap(
+					audio->out_phys, BUFFER_SIZE);
 			if (IS_ERR(audio->map_v_write)) {
 				MM_ERR("could not map write phys address\n");
 				rc = -ENOMEM;
@@ -1342,7 +1340,7 @@
 						audio->out_phys);
 				goto evt_error;
 			}
-			audio->out_data = audio->map_v_write->vaddr;
+			audio->out_data = audio->map_v_write;
 			MM_DBG("wr buf: phy addr 0x%08x kernel addr 0x%08x\n",
 					audio->out_phys,
 					(uint32_t)audio->out_data);
diff --git a/arch/arm/mach-msm/qdsp5/audio_amrwb.c b/arch/arm/mach-msm/qdsp5/audio_amrwb.c
index b566c60..0d3b67a 100644
--- a/arch/arm/mach-msm/qdsp5/audio_amrwb.c
+++ b/arch/arm/mach-msm/qdsp5/audio_amrwb.c
@@ -46,7 +46,6 @@
 #include <mach/msm_adsp.h>
 #include <mach/iommu.h>
 #include <mach/iommu_domains.h>
-#include <mach/msm_subsystem_map.h>
 #include <mach/qdsp5/qdsp5audppcmdi.h>
 #include <mach/qdsp5/qdsp5audppmsg.h>
 #include <mach/qdsp5/qdsp5audplaycmdi.h>
@@ -139,8 +138,8 @@
 	/* data allocated for various buffers */
 	char *data;
 	int32_t phys; /* physical address of write buffer */
-	struct msm_mapped_buffer *map_v_read;
-	struct msm_mapped_buffer *map_v_write;
+	void *map_v_read;
+	void *map_v_write;
 	int mfield; /* meta field embedded in data */
 	int rflush; /* Read  flush */
 	int wflush; /* Write flush */
@@ -975,12 +974,10 @@
 					rc = -ENOMEM;
 					break;
 			}
-			audio->map_v_read = msm_subsystem_map_buffer(
+			audio->map_v_read = ioremap(
 						audio->read_phys,
 						config.buffer_size *
-						config.buffer_count,
-						MSM_SUBSYSTEM_MAP_KADDR,
-						NULL, 0);
+						config.buffer_count);
 			if (IS_ERR(audio->map_v_read)) {
 				MM_ERR("failed to map mem for read buf\n");
 				rc = -ENOMEM;
@@ -989,7 +986,7 @@
 			} else {
 				uint8_t index;
 				uint32_t offset = 0;
-				audio->read_data = audio->map_v_read->vaddr;
+				audio->read_data = audio->map_v_read;
 				audio->pcm_feedback = 1;
 				audio->buf_refresh = 0;
 				audio->pcm_buf_count =
@@ -1366,10 +1363,10 @@
 	audio->event_abort = 1;
 	wake_up(&audio->event_wait);
 	audamrwb_reset_event_queue(audio);
-	msm_subsystem_unmap_buffer(audio->map_v_write);
+	iounmap(audio->map_v_write);
 	free_contiguous_memory_by_paddr(audio->phys);
 	if (audio->read_data) {
-		msm_subsystem_unmap_buffer(audio->map_v_read);
+		iounmap(audio->map_v_read);
 		free_contiguous_memory_by_paddr(audio->read_phys);
 	}
 	mutex_unlock(&audio->lock);
@@ -1557,10 +1554,7 @@
 		kfree(audio);
 		goto done;
 	} else {
-		audio->map_v_write = msm_subsystem_map_buffer(
-						audio->phys, DMASZ,
-						MSM_SUBSYSTEM_MAP_KADDR,
-						NULL, 0);
+		audio->map_v_write = ioremap(audio->phys, DMASZ);
 
 		if (IS_ERR(audio->map_v_write)) {
 			MM_ERR("could not map write buffers, freeing \
@@ -1571,7 +1565,7 @@
 			kfree(audio);
 			goto done;
 		}
-		audio->data = audio->map_v_write->vaddr;
+		audio->data = audio->map_v_write;
 		MM_DBG("write buf: phy addr 0x%08x kernel addr 0x%08x\n",
 				audio->phys, (int)audio->data);
 	}
@@ -1664,7 +1658,7 @@
 done:
 	return rc;
 err:
-	msm_subsystem_unmap_buffer(audio->map_v_write);
+	iounmap(audio->map_v_write);
 	free_contiguous_memory_by_paddr(audio->phys);
 	audpp_adec_free(audio->dec_id);
 	kfree(audio);
diff --git a/arch/arm/mach-msm/qdsp5/audio_evrc.c b/arch/arm/mach-msm/qdsp5/audio_evrc.c
index 86035db..7479e36 100644
--- a/arch/arm/mach-msm/qdsp5/audio_evrc.c
+++ b/arch/arm/mach-msm/qdsp5/audio_evrc.c
@@ -41,7 +41,6 @@
 #include <mach/msm_adsp.h>
 #include <mach/iommu.h>
 #include <mach/iommu_domains.h>
-#include <mach/msm_subsystem_map.h>
 #include <mach/qdsp5/qdsp5audppcmdi.h>
 #include <mach/qdsp5/qdsp5audppmsg.h>
 #include <mach/qdsp5/qdsp5audplaycmdi.h>
@@ -135,8 +134,8 @@
 	/* data allocated for various buffers */
 	char *data;
 	int32_t phys;  /* physical address of write buffer */
-	struct msm_mapped_buffer *map_v_read;
-	struct msm_mapped_buffer *map_v_write;
+	void *map_v_read;
+	void *map_v_write;
 
 	int mfield; /* meta field embedded in data */
 	int rflush; /* Read  flush */
@@ -965,12 +964,10 @@
 					rc = -ENOMEM;
 					break;
 				}
-				audio->map_v_read = msm_subsystem_map_buffer(
+				audio->map_v_read = ioremap(
 							audio->read_phys,
 							config.buffer_size *
-							config.buffer_count,
-							MSM_SUBSYSTEM_MAP_KADDR,
-							NULL, 0);
+							config.buffer_count);
 				if (IS_ERR(audio->map_v_read)) {
 					MM_ERR("failed to map mem"
 							" for read buf\n");
@@ -981,7 +978,7 @@
 					uint8_t index;
 					uint32_t offset = 0;
 					audio->read_data =
-						audio->map_v_read->vaddr;
+						audio->map_v_read;
 					audio->buf_refresh = 0;
 					audio->pcm_buf_count =
 					    config.buffer_count;
@@ -1290,10 +1287,10 @@
 	audio->event_abort = 1;
 	wake_up(&audio->event_wait);
 	audevrc_reset_event_queue(audio);
-	msm_subsystem_unmap_buffer(audio->map_v_write);
+	iounmap(audio->map_v_write);
 	free_contiguous_memory_by_paddr(audio->phys);
 	if (audio->read_data) {
-		msm_subsystem_unmap_buffer(audio->map_v_read);
+		iounmap(audio->map_v_read);
 		free_contiguous_memory_by_paddr(audio->read_phys);
 	}
 	mutex_unlock(&audio->lock);
@@ -1485,10 +1482,7 @@
 		kfree(audio);
 		goto done;
 	} else {
-		audio->map_v_write = msm_subsystem_map_buffer(
-						audio->phys, DMASZ,
-						MSM_SUBSYSTEM_MAP_KADDR,
-						NULL, 0);
+		audio->map_v_write = ioremap(audio->phys, DMASZ);
 		if (IS_ERR(audio->map_v_write)) {
 			MM_ERR("could not map write buffers, freeing \
 					instance 0x%08x\n", (int)audio);
@@ -1498,7 +1492,7 @@
 			kfree(audio);
 			goto done;
 		}
-		audio->data = audio->map_v_write->vaddr;
+		audio->data = audio->map_v_write;
 		MM_DBG("write buf: phy addr 0x%08x kernel addr 0x%08x\n",
 				audio->phys, (int)audio->data);
 	}
@@ -1588,7 +1582,7 @@
 done:
 	return rc;
 err:
-	msm_subsystem_unmap_buffer(audio->map_v_write);
+	iounmap(audio->map_v_write);
 	free_contiguous_memory_by_paddr(audio->phys);
 	audpp_adec_free(audio->dec_id);
 	kfree(audio);
diff --git a/arch/arm/mach-msm/qdsp5/audio_evrc_in.c b/arch/arm/mach-msm/qdsp5/audio_evrc_in.c
index 0bdbf5d..99ad02b 100644
--- a/arch/arm/mach-msm/qdsp5/audio_evrc_in.c
+++ b/arch/arm/mach-msm/qdsp5/audio_evrc_in.c
@@ -41,7 +41,6 @@
 #include <mach/msm_rpcrouter.h>
 #include <mach/iommu.h>
 #include <mach/iommu_domains.h>
-#include <mach/msm_subsystem_map.h>
 
 #include "audmgr.h"
 
@@ -144,8 +143,8 @@
 	char *data;
 	dma_addr_t phys;
 
-	struct msm_mapped_buffer *map_v_read;
-	struct msm_mapped_buffer *map_v_write;
+	void *map_v_read;
+	void *map_v_write;
 
 	int opened;
 	int enabled;
@@ -1189,12 +1188,12 @@
 	audio->opened = 0;
 	if ((audio->mode == MSM_AUD_ENC_MODE_NONTUNNEL) && \
 	   (audio->out_data)) {
-		msm_subsystem_unmap_buffer(audio->map_v_write);
+		iounmap(audio->map_v_write);
 		free_contiguous_memory_by_paddr(audio->out_phys);
 		audio->out_data = NULL;
 	}
 	if (audio->data) {
-		msm_subsystem_unmap_buffer(audio->map_v_read);
+		iounmap(audio->map_v_read);
 		free_contiguous_memory_by_paddr(audio->phys);
 		audio->data = NULL;
 	}
@@ -1295,17 +1294,14 @@
 		rc = -ENOMEM;
 		goto evt_error;
 	} else {
-		audio->map_v_read = msm_subsystem_map_buffer(
-						audio->phys, dma_size,
-						MSM_SUBSYSTEM_MAP_KADDR,
-						NULL, 0);
+		audio->map_v_read = ioremap(audio->phys, dma_size);
 		if (IS_ERR(audio->map_v_read)) {
 			MM_ERR("could not map physical address\n");
 			rc = -ENOMEM;
 			free_contiguous_memory_by_paddr(audio->phys);
 			goto evt_error;
 		}
-		audio->data = audio->map_v_read->vaddr;
+		audio->data = audio->map_v_read;
 		MM_DBG("read buf: phy addr 0x%08x kernel addr 0x%08x\n",
 				audio->phys, (int)audio->data);
 	}
@@ -1316,25 +1312,23 @@
 		if (!audio->out_phys) {
 			MM_ERR("could not allocate physical write buffers\n");
 			rc = -ENOMEM;
-			msm_subsystem_unmap_buffer(audio->map_v_read);
+			iounmap(audio->map_v_read);
 			free_contiguous_memory_by_paddr(audio->phys);
 			goto evt_error;
 		} else {
-			audio->map_v_write = msm_subsystem_map_buffer(
-						audio->out_phys, BUFFER_SIZE,
-						MSM_SUBSYSTEM_MAP_KADDR,
-						NULL, 0);
+			audio->map_v_write = ioremap(
+						audio->out_phys, BUFFER_SIZE);
 
 			if (IS_ERR(audio->map_v_write)) {
 				MM_ERR("could not map write phys address\n");
 				rc = -ENOMEM;
-				msm_subsystem_unmap_buffer(audio->map_v_read);
+				iounmap(audio->map_v_read);
 				free_contiguous_memory_by_paddr(audio->phys);
 				free_contiguous_memory_by_paddr(\
 							audio->out_phys);
 				goto evt_error;
 			}
-			audio->out_data = audio->map_v_write->vaddr;
+			audio->out_data = audio->map_v_write;
 			MM_DBG("wr buf: phy addr 0x%08x kernel addr 0x%08x\n",
 					audio->out_phys, (int)audio->out_data);
 		}
diff --git a/arch/arm/mach-msm/qdsp5/audio_lpa.c b/arch/arm/mach-msm/qdsp5/audio_lpa.c
index dab53dc..8dfba0b 100644
--- a/arch/arm/mach-msm/qdsp5/audio_lpa.c
+++ b/arch/arm/mach-msm/qdsp5/audio_lpa.c
@@ -46,7 +46,6 @@
 #include <mach/msm_adsp.h>
 #include <mach/iommu.h>
 #include <mach/iommu_domains.h>
-#include <mach/msm_subsystem_map.h>
 #include <mach/qdsp5/qdsp5audppcmdi.h>
 #include <mach/qdsp5/qdsp5audppmsg.h>
 #include <mach/qdsp5/qdsp5audplaycmdi.h>
@@ -1221,7 +1220,7 @@
 	audpcm_reset_event_queue(audio);
 	MM_DBG("pmem area = 0x%8x\n", (unsigned int)audio->data);
 	if (audio->data) {
-		msm_subsystem_unmap_buffer(audio->map_v_write);
+		iounmap(audio->map_v_write);
 		free_contiguous_memory_by_paddr(audio->phys);
 	}
 	mutex_unlock(&audio->lock);
diff --git a/arch/arm/mach-msm/qdsp5/audio_mp3.c b/arch/arm/mach-msm/qdsp5/audio_mp3.c
index f6fa62a..5abdf85 100644
--- a/arch/arm/mach-msm/qdsp5/audio_mp3.c
+++ b/arch/arm/mach-msm/qdsp5/audio_mp3.c
@@ -39,7 +39,6 @@
 #include <mach/msm_adsp.h>
 #include <mach/iommu.h>
 #include <mach/iommu_domains.h>
-#include <mach/msm_subsystem_map.h>
 #include <mach/msm_memtypes.h>
 #include <mach/qdsp5/qdsp5audppcmdi.h>
 #include <mach/qdsp5/qdsp5audppmsg.h>
@@ -202,8 +201,8 @@
 	/* data allocated for various buffers */
 	char *data;
 	int32_t phys; /* physical address of write buffer */
-	struct msm_mapped_buffer *map_v_read;
-	struct msm_mapped_buffer *map_v_write;
+	void *map_v_read;
+	void *map_v_write;
 
 	uint32_t drv_status;
 	int mfield; /* meta field embedded in data */
@@ -1488,12 +1487,10 @@
 					rc = -ENOMEM;
 					break;
 				}
-				audio->map_v_read = msm_subsystem_map_buffer(
+				audio->map_v_read = ioremap(
 							audio->read_phys,
 							config.buffer_size *
-							config.buffer_count,
-							MSM_SUBSYSTEM_MAP_KADDR,
-							NULL, 0);
+							config.buffer_count);
 
 				if (IS_ERR(audio->map_v_read)) {
 					MM_ERR("map of read buf failed\n");
@@ -1504,7 +1501,7 @@
 					uint8_t index;
 					uint32_t offset = 0;
 					audio->read_data =
-						audio->map_v_read->vaddr;
+						audio->map_v_read;
 					audio->buf_refresh = 0;
 					audio->pcm_buf_count =
 					    config.buffer_count;
@@ -1979,11 +1976,11 @@
 	audmp3_reset_event_queue(audio);
 	MM_DBG("pmem area = 0x%8x\n", (unsigned int)audio->data);
 	if (audio->data) {
-		msm_subsystem_unmap_buffer(audio->map_v_write);
+		iounmap(audio->map_v_write);
 		free_contiguous_memory_by_paddr(audio->phys);
 	}
 	if (audio->read_data) {
-		msm_subsystem_unmap_buffer(audio->map_v_read);
+		iounmap(audio->map_v_read);
 		free_contiguous_memory_by_paddr(audio->read_phys);
 	}
 	mutex_unlock(&audio->lock);
@@ -2178,10 +2175,8 @@
 			audio->phys = allocate_contiguous_ebi_nomap(pmem_sz,
 								SZ_4K);
 			if (audio->phys) {
-				audio->map_v_write = msm_subsystem_map_buffer(
-							audio->phys, pmem_sz,
-							MSM_SUBSYSTEM_MAP_KADDR,
-							NULL, 0);
+				audio->map_v_write = ioremap(
+							audio->phys, pmem_sz);
 				if (IS_ERR(audio->map_v_write)) {
 					MM_ERR("could not map write \
 						buffers, freeing instance \
@@ -2193,7 +2188,7 @@
 					kfree(audio);
 					goto done;
 				}
-				audio->data = audio->map_v_write->vaddr;
+				audio->data = audio->map_v_write;
 				MM_DBG("write buf: phy addr 0x%08x kernel addr\
 					0x%08x\n", audio->phys,\
 					(int)audio->data);
@@ -2321,7 +2316,7 @@
 	return rc;
 err:
 	if (audio->data) {
-		msm_subsystem_unmap_buffer(audio->map_v_write);
+		iounmap(audio->map_v_write);
 		free_contiguous_memory_by_paddr(audio->phys);
 	}
 	audpp_adec_free(audio->dec_id);
diff --git a/arch/arm/mach-msm/qdsp5/audio_pcm.c b/arch/arm/mach-msm/qdsp5/audio_pcm.c
index 6468e93..5cd95f6 100644
--- a/arch/arm/mach-msm/qdsp5/audio_pcm.c
+++ b/arch/arm/mach-msm/qdsp5/audio_pcm.c
@@ -46,7 +46,6 @@
 #include <mach/msm_adsp.h>
 #include <mach/iommu.h>
 #include <mach/iommu_domains.h>
-#include <mach/msm_subsystem_map.h>
 #include <mach/qdsp5/qdsp5audppcmdi.h>
 #include <mach/qdsp5/qdsp5audppmsg.h>
 #include <mach/qdsp5/qdsp5audplaycmdi.h>
@@ -187,7 +186,7 @@
 	/* data allocated for various buffers */
 	char *data;
 	int32_t phys;
-	struct msm_mapped_buffer *map_v_write;
+	void *map_v_write;
 
 	uint32_t drv_status;
 	int wflush; /* Write flush */
@@ -1379,7 +1378,7 @@
 	audpcm_reset_event_queue(audio);
 	MM_DBG("pmem area = 0x%8x\n", (unsigned int)audio->data);
 	if (audio->data) {
-		msm_subsystem_unmap_buffer(audio->map_v_write);
+		iounmap(audio->map_v_write);
 		free_contiguous_memory_by_paddr(audio->phys);
 	}
 	mutex_unlock(&audio->lock);
@@ -1551,10 +1550,8 @@
 			audio->phys = allocate_contiguous_ebi_nomap(pmem_sz,
 								SZ_4K);
 			if (audio->phys) {
-				audio->map_v_write = msm_subsystem_map_buffer(
-							audio->phys, pmem_sz,
-							MSM_SUBSYSTEM_MAP_KADDR,
-							NULL, 0);
+				audio->map_v_write = ioremap(
+							audio->phys, pmem_sz);
 				if (IS_ERR(audio->map_v_write)) {
 					MM_ERR("could not map write\
 							buffers\n");
@@ -1567,7 +1564,7 @@
 					kfree(audio);
 					goto done;
 				}
-				audio->data = audio->map_v_write->vaddr;
+				audio->data = audio->map_v_write;
 				MM_DBG("write buf: phy addr 0x%08x kernel addr\
 					0x%08x\n", audio->phys,\
 					(int)audio->data);
@@ -1678,7 +1675,7 @@
 	return rc;
 err:
 	if (audio->data) {
-		msm_subsystem_unmap_buffer(audio->map_v_write);
+		iounmap(audio->map_v_write);
 		free_contiguous_memory_by_paddr(audio->phys);
 	}
 	audpp_adec_free(audio->dec_id);
diff --git a/arch/arm/mach-msm/qdsp5/audio_qcelp.c b/arch/arm/mach-msm/qdsp5/audio_qcelp.c
index 2be5144..667a164 100644
--- a/arch/arm/mach-msm/qdsp5/audio_qcelp.c
+++ b/arch/arm/mach-msm/qdsp5/audio_qcelp.c
@@ -42,7 +42,6 @@
 #include <mach/msm_adsp.h>
 #include <mach/iommu.h>
 #include <mach/iommu_domains.h>
-#include <mach/msm_subsystem_map.h>
 #include <mach/qdsp5/qdsp5audppcmdi.h>
 #include <mach/qdsp5/qdsp5audppmsg.h>
 #include <mach/qdsp5/qdsp5audplaycmdi.h>
@@ -132,8 +131,8 @@
 	/* data allocated for various buffers */
 	char *data;
 	int32_t phys; /* physical address of write buffer */
-	struct msm_mapped_buffer *map_v_read;
-	struct msm_mapped_buffer *map_v_write;
+	void *map_v_read;
+	void *map_v_write;
 	int mfield; /* meta field embedded in data */
 	int rflush; /* Read  flush */
 	int wflush; /* Write flush */
@@ -966,12 +965,10 @@
 					rc = -ENOMEM;
 					break;
 				}
-				audio->map_v_read = msm_subsystem_map_buffer(
+				audio->map_v_read = ioremap(
 							audio->read_phys,
 							config.buffer_size *
-							config.buffer_count,
-							MSM_SUBSYSTEM_MAP_KADDR,
-							NULL, 0);
+							config.buffer_count);
 				if (IS_ERR(audio->map_v_read)) {
 					MM_ERR("failed to map read buf\n");
 					rc = -ENOMEM;
@@ -981,7 +978,7 @@
 					uint8_t index;
 					uint32_t offset = 0;
 					audio->read_data =
-						audio->map_v_read->vaddr;
+						audio->map_v_read;
 					audio->buf_refresh = 0;
 					audio->pcm_buf_count =
 						config.buffer_count;
@@ -1290,10 +1287,10 @@
 	audio->event_abort = 1;
 	wake_up(&audio->event_wait);
 	audqcelp_reset_event_queue(audio);
-	msm_subsystem_unmap_buffer(audio->map_v_write);
+	iounmap(audio->map_v_write);
 	free_contiguous_memory_by_paddr(audio->phys);
 	if (audio->read_data) {
-		msm_subsystem_unmap_buffer(audio->map_v_read);
+		iounmap(audio->map_v_read);
 		free_contiguous_memory_by_paddr(audio->read_phys);
 	}
 	mutex_unlock(&audio->lock);
@@ -1483,10 +1480,7 @@
 		kfree(audio);
 		goto done;
 	} else {
-		audio->map_v_write = msm_subsystem_map_buffer(
-						audio->phys, DMASZ,
-						MSM_SUBSYSTEM_MAP_KADDR,
-						NULL, 0);
+		audio->map_v_write = ioremap(audio->phys, DMASZ);
 		if (IS_ERR(audio->map_v_write)) {
 			MM_ERR("could not map write buffers, freeing \
 					instance 0x%08x\n", (int)audio);
@@ -1496,7 +1490,7 @@
 			kfree(audio);
 			goto done;
 		}
-		audio->data = audio->map_v_write->vaddr;
+		audio->data = audio->map_v_write;
 		MM_DBG("write buf: phy addr 0x%08x kernel addr 0x%08x\n",
 				audio->phys, (int)audio->data);
 	}
@@ -1586,7 +1580,7 @@
 done:
 	return rc;
 err:
-	msm_subsystem_unmap_buffer(audio->map_v_write);
+	iounmap(audio->map_v_write);
 	free_contiguous_memory_by_paddr(audio->phys);
 	audpp_adec_free(audio->dec_id);
 	kfree(audio);
diff --git a/arch/arm/mach-msm/qdsp5/audio_qcelp_in.c b/arch/arm/mach-msm/qdsp5/audio_qcelp_in.c
index a79f721..6ebd5f9 100644
--- a/arch/arm/mach-msm/qdsp5/audio_qcelp_in.c
+++ b/arch/arm/mach-msm/qdsp5/audio_qcelp_in.c
@@ -42,7 +42,6 @@
 #include <mach/msm_rpcrouter.h>
 #include <mach/iommu.h>
 #include <mach/iommu_domains.h>
-#include <mach/msm_subsystem_map.h>
 #include "audmgr.h"
 
 #include <mach/qdsp5/qdsp5audpreproc.h>
@@ -145,8 +144,8 @@
 	char *data;
 	dma_addr_t phys;
 
-	struct msm_mapped_buffer *map_v_read;
-	struct msm_mapped_buffer *map_v_write;
+	void *map_v_read;
+	void *map_v_write;
 
 	int opened;
 	int enabled;
@@ -1191,13 +1190,13 @@
 
 	if ((audio->mode == MSM_AUD_ENC_MODE_NONTUNNEL) && \
 	   (audio->out_data)) {
-		msm_subsystem_unmap_buffer(audio->map_v_write);
+		iounmap(audio->map_v_write);
 		free_contiguous_memory_by_paddr(audio->out_phys);
 		audio->out_data = NULL;
 	}
 
 	if (audio->data) {
-		msm_subsystem_unmap_buffer(audio->map_v_read);
+		iounmap(audio->map_v_read);
 		free_contiguous_memory_by_paddr(audio->phys);
 		audio->data = NULL;
 	}
@@ -1298,17 +1297,14 @@
 		rc = -ENOMEM;
 		goto evt_error;
 	} else {
-		audio->map_v_read = msm_subsystem_map_buffer(
-						audio->phys, dma_size,
-						MSM_SUBSYSTEM_MAP_KADDR,
-						NULL, 0);
+		audio->map_v_read = ioremap(audio->phys, dma_size);
 		if (IS_ERR(audio->map_v_read)) {
 			MM_ERR("could not map physical address\n");
 			rc = -ENOMEM;
 			free_contiguous_memory_by_paddr(audio->phys);
 			goto evt_error;
 		}
-		audio->data = audio->map_v_read->vaddr;
+		audio->data = audio->map_v_read;
 		MM_DBG("read buf: phy addr 0x%08x kernel addr 0x%08x\n",
 				audio->phys, (int)audio->data);
 	}
@@ -1320,25 +1316,23 @@
 		if (!audio->out_phys) {
 			MM_ERR("could not allocate physical write buffers\n");
 			rc = -ENOMEM;
-			msm_subsystem_unmap_buffer(audio->map_v_read);
+			iounmap(audio->map_v_read);
 			free_contiguous_memory_by_paddr(audio->phys);
 			goto evt_error;
 		} else {
-			audio->map_v_write = msm_subsystem_map_buffer(
-						audio->out_phys, BUFFER_SIZE,
-						MSM_SUBSYSTEM_MAP_KADDR,
-						NULL, 0);
+			audio->map_v_write = ioremap(
+						audio->out_phys, BUFFER_SIZE);
 
 			if (IS_ERR(audio->map_v_write)) {
 				MM_ERR("could not map write phys address\n");
 				rc = -ENOMEM;
-				msm_subsystem_unmap_buffer(audio->map_v_read);
+				iounmap(audio->map_v_read);
 				free_contiguous_memory_by_paddr(audio->phys);
 				free_contiguous_memory_by_paddr(\
 							audio->out_phys);
 				goto evt_error;
 			}
-			audio->out_data = audio->map_v_write->vaddr;
+			audio->out_data = audio->map_v_write;
 			MM_DBG("wr buf: phy addr 0x%08x kernel addr 0x%08x\n",
 					audio->out_phys, (int)audio->out_data);
 		}
diff --git a/arch/arm/mach-msm/qdsp5/audio_wma.c b/arch/arm/mach-msm/qdsp5/audio_wma.c
index 674ee4f..d767916 100644
--- a/arch/arm/mach-msm/qdsp5/audio_wma.c
+++ b/arch/arm/mach-msm/qdsp5/audio_wma.c
@@ -46,7 +46,6 @@
 #include <mach/msm_adsp.h>
 #include <mach/iommu.h>
 #include <mach/iommu_domains.h>
-#include <mach/msm_subsystem_map.h>
 #include <mach/qdsp5/qdsp5audppcmdi.h>
 #include <mach/qdsp5/qdsp5audppmsg.h>
 #include <mach/qdsp5/qdsp5audplaycmdi.h>
@@ -145,8 +144,8 @@
 	/* data allocated for various buffers */
 	char *data;
 	int32_t phys; /* physical address of write buffer */
-	struct msm_mapped_buffer *map_v_read;
-	struct msm_mapped_buffer *map_v_write;
+	void *map_v_read;
+	void *map_v_write;
 
 	int mfield; /* meta field embedded in data */
 	int rflush; /* Read  flush */
@@ -1043,12 +1042,10 @@
 					rc = -ENOMEM;
 					break;
 				}
-				audio->map_v_read = msm_subsystem_map_buffer(
+				audio->map_v_read = ioremap(
 						audio->read_phys,
 						config.buffer_size *
-						config.buffer_count,
-						MSM_SUBSYSTEM_MAP_KADDR,
-						NULL, 0);
+						config.buffer_count);
 				if (IS_ERR(audio->map_v_read)) {
 					MM_ERR("map of read buf failed\n");
 					rc = -ENOMEM;
@@ -1058,7 +1055,7 @@
 					uint8_t index;
 					uint32_t offset = 0;
 					audio->read_data =
-						audio->map_v_read->vaddr;
+						audio->map_v_read;
 					audio->buf_refresh = 0;
 					audio->pcm_buf_count =
 					    config.buffer_count;
@@ -1435,10 +1432,10 @@
 	audio->event_abort = 1;
 	wake_up(&audio->event_wait);
 	audwma_reset_event_queue(audio);
-	msm_subsystem_unmap_buffer(audio->map_v_write);
+	iounmap(audio->map_v_write);
 	free_contiguous_memory_by_paddr(audio->phys);
 	if (audio->read_data) {
-		msm_subsystem_unmap_buffer(audio->map_v_read);
+		iounmap(audio->map_v_read);
 		free_contiguous_memory_by_paddr(audio->read_phys);
 	}
 	mutex_unlock(&audio->lock);
@@ -1630,10 +1627,7 @@
 		MM_DBG("pmemsz = %d\n", pmem_sz);
 		audio->phys = allocate_contiguous_ebi_nomap(pmem_sz, SZ_4K);
 		if (audio->phys) {
-			audio->map_v_write = msm_subsystem_map_buffer(
-							audio->phys, pmem_sz,
-							MSM_SUBSYSTEM_MAP_KADDR,
-							NULL, 0);
+			audio->map_v_write = ioremap(audio->phys, pmem_sz);
 			if (IS_ERR(audio->map_v_write)) {
 				MM_ERR("could not map write buffers, \
 						freeing instance 0x%08x\n",
@@ -1644,7 +1638,7 @@
 				kfree(audio);
 				goto done;
 			}
-			audio->data = audio->map_v_write->vaddr;
+			audio->data = audio->map_v_write;
 			MM_DBG("write buf: phy addr 0x%08x kernel addr \
 				0x%08x\n", audio->phys, (int)audio->data);
 			break;
@@ -1753,7 +1747,7 @@
 done:
 	return rc;
 err:
-	msm_subsystem_unmap_buffer(audio->map_v_write);
+	iounmap(audio->map_v_write);
 	free_contiguous_memory_by_paddr(audio->phys);
 	audpp_adec_free(audio->dec_id);
 	kfree(audio);
diff --git a/arch/arm/mach-msm/qdsp5/audio_wmapro.c b/arch/arm/mach-msm/qdsp5/audio_wmapro.c
index c2a0b93..7fb08ff 100644
--- a/arch/arm/mach-msm/qdsp5/audio_wmapro.c
+++ b/arch/arm/mach-msm/qdsp5/audio_wmapro.c
@@ -52,7 +52,6 @@
 #include <mach/msm_memtypes.h>
 #include <mach/iommu.h>
 #include <mach/iommu_domains.h>
-#include <mach/msm_subsystem_map.h>
 
 #include "audmgr.h"
 
@@ -144,8 +143,8 @@
 	/* data allocated for various buffers */
 	char *data;
 	int32_t phys; /* physical address of write buffer */
-	struct msm_mapped_buffer *map_v_read;
-	struct msm_mapped_buffer *map_v_write;
+	void *map_v_read;
+	void *map_v_write;
 
 	int mfield; /* meta field embedded in data */
 	int rflush; /* Read  flush */
@@ -1040,12 +1039,10 @@
 					rc = -ENOMEM;
 					break;
 				}
-				audio->map_v_read = msm_subsystem_map_buffer(
+				audio->map_v_read = ioremap(
 						audio->read_phys,
 						config.buffer_size *
-						config.buffer_count,
-						MSM_SUBSYSTEM_MAP_KADDR,
-						NULL, 0);
+						config.buffer_count);
 
 				if (IS_ERR(audio->map_v_read)) {
 					MM_ERR("map of read buf failed\n");
@@ -1055,8 +1052,7 @@
 				} else {
 					uint8_t index;
 					uint32_t offset = 0;
-					audio->read_data =
-						audio->map_v_read->vaddr;
+					audio->read_data = audio->map_v_read;
 					audio->pcm_feedback = 1;
 					audio->buf_refresh = 0;
 					audio->pcm_buf_count =
@@ -1432,10 +1428,10 @@
 	audio->event_abort = 1;
 	wake_up(&audio->event_wait);
 	audwmapro_reset_event_queue(audio);
-	msm_subsystem_unmap_buffer(audio->map_v_write);
+	iounmap(audio->map_v_write);
 	free_contiguous_memory_by_paddr(audio->phys);
 	if (audio->read_data) {
-		msm_subsystem_unmap_buffer(audio->map_v_read);
+		iounmap(audio->map_v_read);
 		free_contiguous_memory_by_paddr(audio->read_phys);
 	}
 	mutex_unlock(&audio->lock);
@@ -1627,10 +1623,7 @@
 		MM_DBG("pmemsz = %d\n", pmem_sz);
 		audio->phys = allocate_contiguous_ebi_nomap(pmem_sz, SZ_4K);
 		if (audio->phys) {
-			audio->map_v_write = msm_subsystem_map_buffer(
-							audio->phys, pmem_sz,
-						MSM_SUBSYSTEM_MAP_KADDR,
-						NULL, 0);
+			audio->map_v_write = ioremap(audio->phys, pmem_sz);
 			if (IS_ERR(audio->map_v_write)) {
 				MM_ERR("could not map write buffers, \
 						freeing instance 0x%08x\n",
@@ -1641,7 +1634,7 @@
 				kfree(audio);
 				goto done;
 			}
-			audio->data = audio->map_v_write->vaddr;
+			audio->data = audio->map_v_write;
 			MM_DBG("write buf: phy addr 0x%08x kernel addr \
 				0x%08x\n", audio->phys, (int)audio->data);
 			break;
@@ -1741,7 +1734,7 @@
 done:
 	return rc;
 err:
-	msm_subsystem_unmap_buffer(audio->map_v_write);
+	iounmap(audio->map_v_write);
 	free_contiguous_memory_by_paddr(audio->phys);
 	audpp_adec_free(audio->dec_id);
 	kfree(audio);
diff --git a/arch/arm/mach-msm/qdsp6v2/audio_multi_aac.c b/arch/arm/mach-msm/qdsp6v2/audio_multi_aac.c
index ee32b80..8a0ba3e 100644
--- a/arch/arm/mach-msm/qdsp6v2/audio_multi_aac.c
+++ b/arch/arm/mach-msm/qdsp6v2/audio_multi_aac.c
@@ -2,7 +2,7 @@
  *
  * Copyright (C) 2008 Google, Inc.
  * Copyright (C) 2008 HTC Corporation
- * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
  *
  * This software is licensed under the terms of the GNU General Public
  * License version 2, as published by the Free Software Foundation, and
@@ -16,6 +16,7 @@
  */
 
 #include <linux/msm_audio_aac.h>
+#include <mach/socinfo.h>
 #include "audio_utils_aio.h"
 
 #define AUDIO_AAC_DUAL_MONO_INVALID -1
@@ -122,6 +123,14 @@
 			pr_err("cmd media format block failed\n");
 			break;
 		}
+		if (!cpu_is_msm8x60()) {
+			rc = q6asm_set_encdec_chan_map(audio->ac, 2);
+			if (rc < 0) {
+				pr_err("%s: cmd set encdec_chan_map failed\n",
+					__func__);
+				break;
+			}
+		}
 		rc = audio_aio_enable(audio);
 		audio->eos_rsp = 0;
 		audio->eos_flag = 0;
diff --git a/arch/arm/mach-msm/qdss-etb.c b/arch/arm/mach-msm/qdss-etb.c
index 96eba26..9b23766 100644
--- a/arch/arm/mach-msm/qdss-etb.c
+++ b/arch/arm/mach-msm/qdss-etb.c
@@ -69,7 +69,7 @@
 	void __iomem	*base;
 	bool		enabled;
 	bool		reading;
-	struct mutex	mutex;
+	spinlock_t	spinlock;
 	atomic_t	in_use;
 	struct device	*dev;
 	struct kobject	*kobj;
@@ -100,11 +100,13 @@
 
 void etb_enable(void)
 {
-	mutex_lock(&etb.mutex);
+	unsigned long flags;
+
+	spin_lock_irqsave(&etb.spinlock, flags);
 	__etb_enable();
 	etb.enabled = true;
 	dev_info(etb.dev, "ETB enabled\n");
-	mutex_unlock(&etb.mutex);
+	spin_unlock_irqrestore(&etb.spinlock, flags);
 }
 
 static void __etb_disable(void)
@@ -137,11 +139,13 @@
 
 void etb_disable(void)
 {
-	mutex_lock(&etb.mutex);
+	unsigned long flags;
+
+	spin_lock_irqsave(&etb.spinlock, flags);
 	__etb_disable();
 	etb.enabled = false;
 	dev_info(etb.dev, "ETB disabled\n");
-	mutex_unlock(&etb.mutex);
+	spin_unlock_irqrestore(&etb.spinlock, flags);
 }
 
 static void __etb_dump(void)
@@ -200,7 +204,9 @@
 
 void etb_dump(void)
 {
-	mutex_lock(&etb.mutex);
+	unsigned long flags;
+
+	spin_lock_irqsave(&etb.spinlock, flags);
 	if (etb.enabled) {
 		__etb_disable();
 		__etb_dump();
@@ -208,7 +214,7 @@
 
 		dev_info(etb.dev, "ETB dumped\n");
 	}
-	mutex_unlock(&etb.mutex);
+	spin_unlock_irqrestore(&etb.spinlock, flags);
 }
 
 static int etb_open(struct inode *inode, struct file *file)
@@ -343,7 +349,7 @@
 
 	etb.dev = &pdev->dev;
 
-	mutex_init(&etb.mutex);
+	spin_lock_init(&etb.spinlock);
 
 	ret = misc_register(&etb_misc);
 	if (ret)
@@ -363,7 +369,6 @@
 err_alloc:
 	misc_deregister(&etb_misc);
 err_misc:
-	mutex_destroy(&etb.mutex);
 	iounmap(etb.base);
 err_ioremap:
 err_res:
@@ -378,7 +383,6 @@
 	etb_sysfs_exit();
 	kfree(etb.buf);
 	misc_deregister(&etb_misc);
-	mutex_destroy(&etb.mutex);
 	iounmap(etb.base);
 
 	return 0;
diff --git a/arch/arm/mach-msm/qdss.c b/arch/arm/mach-msm/qdss.c
index cfe65ad..0cc3ca5 100644
--- a/arch/arm/mach-msm/qdss.c
+++ b/arch/arm/mach-msm/qdss.c
@@ -175,6 +175,26 @@
 EXPORT_SYMBOL(qdss_disable);
 
 /**
+ * qdss_disable_sink - force disable the current qdss sink(s)
+ *
+ * Force disable the current qdss sink(s) to stop the sink from accepting any
+ * trace generated subsequent to this call. This function should only be used
+ * as a way to stop the sink from getting polluted with trace data that is
+ * uninteresting after an event of interest has occured.
+ *
+ * CONTEXT:
+ * Can be called from atomic or non-atomic context.
+ */
+void qdss_disable_sink(void)
+{
+	if ((qdss.pdata)->afamily) {
+		etb_dump();
+		etb_disable();
+	}
+}
+EXPORT_SYMBOL(qdss_disable_sink);
+
+/**
  * qdss_clk_enable - enable qdss clocks
  *
  * Enables qdss clocks via RPM if they aren't already enabled, otherwise
diff --git a/arch/arm/mach-msm/socinfo.c b/arch/arm/mach-msm/socinfo.c
index 3faeb37..00748e4 100644
--- a/arch/arm/mach-msm/socinfo.c
+++ b/arch/arm/mach-msm/socinfo.c
@@ -247,6 +247,11 @@
 	[128] = MSM_CPU_8625,
 	[129] = MSM_CPU_8625,
 
+	/* 7x25AB IDs */
+	[131] = MSM_CPU_7X25AB,
+	[132] = MSM_CPU_7X25AB,
+	[133] = MSM_CPU_7X25AB,
+
 	/* Uninitialized IDs are not known to run Linux.
 	   MSM_CPU_UNKNOWN is set to 0 to ensure these IDs are
 	   considered as unknown CPU. */
diff --git a/arch/arm/mach-msm/subsystem_map.c b/arch/arm/mach-msm/subsystem_map.c
index d6a17e6..96c3d49 100644
--- a/arch/arm/mach-msm/subsystem_map.c
+++ b/arch/arm/mach-msm/subsystem_map.c
@@ -392,7 +392,8 @@
 					temp_phys += SZ_4K,
 					temp_va += SZ_4K) {
 				ret = iommu_map(d, temp_va, temp_phys,
-						 get_order(SZ_4K), 0);
+						get_order(SZ_4K),
+						(IOMMU_READ | IOMMU_WRITE));
 				if (ret) {
 					pr_err("%s: could not map iommu for"
 						" domain %p, iova %lx,"
@@ -406,7 +407,8 @@
 
 			if (flags & MSM_SUBSYSTEM_MAP_IOMMU_2X)
 				msm_iommu_map_extra
-					(d, temp_va, length, 0);
+					(d, temp_va, length,
+					(IOMMU_READ | IOMMU_WRITE));
 		}
 
 	}
diff --git a/arch/arm/mach-msm/timer.c b/arch/arm/mach-msm/timer.c
index ea8356c..c888815 100644
--- a/arch/arm/mach-msm/timer.c
+++ b/arch/arm/mach-msm/timer.c
@@ -981,7 +981,7 @@
 
 	if (cpu_is_msm7x01() || cpu_is_msm7x25() || cpu_is_msm7x27() ||
 	    cpu_is_msm7x25a() || cpu_is_msm7x27a() || cpu_is_msm7x25aa() ||
-	    cpu_is_msm7x27aa() || cpu_is_msm8625()) {
+	    cpu_is_msm7x27aa() || cpu_is_msm8625() || cpu_is_msm7x25ab()) {
 		dgt->shift = MSM_DGT_SHIFT;
 		dgt->freq = 19200000 >> MSM_DGT_SHIFT;
 		dgt->clockevent.shift = 32 + MSM_DGT_SHIFT;
diff --git a/drivers/base/genlock.c b/drivers/base/genlock.c
index d9cd600..5e1d7af 100644
--- a/drivers/base/genlock.c
+++ b/drivers/base/genlock.c
@@ -307,12 +307,15 @@
 	if (handle_has_lock(lock, handle)) {
 
 		/*
-		 * If the handle already holds the lock and the type matches,
-		 * then just increment the active pointer. This allows the
-		 * handle to do recursive locks
+		 * If the handle already holds the lock and the lock type is
+		 * a read lock then just increment the active pointer. This
+		 * allows the handle to do recursive read locks. Recursive
+		 * write locks are not allowed in order to support
+		 * synchronization within a process using a single gralloc
+		 * handle.
 		 */
 
-		if (lock->state == op) {
+		if (lock->state == _RDLOCK && op == _RDLOCK) {
 			handle->active++;
 			goto done;
 		}
@@ -321,33 +324,46 @@
 		 * If the handle holds a write lock then the owner can switch
 		 * to a read lock if they want. Do the transition atomically
 		 * then wake up any pending waiters in case they want a read
-		 * lock too.
+		 * lock too. In order to support synchronization within a
+		 * process the caller must explicity request to convert the
+		 * lock type with the GENLOCK_WRITE_TO_READ flag.
 		 */
 
-		if (op == _RDLOCK && handle->active == 1) {
-			lock->state = _RDLOCK;
-			wake_up(&lock->queue);
+		if (flags & GENLOCK_WRITE_TO_READ) {
+			if (lock->state == _WRLOCK && op == _RDLOCK) {
+				lock->state = _RDLOCK;
+				wake_up(&lock->queue);
+				goto done;
+			} else {
+				GENLOCK_LOG_ERR("Invalid state to convert"
+					"write to read\n");
+				ret = -EINVAL;
+				goto done;
+			}
+		}
+	} else {
+
+		/*
+		 * Check to ensure the caller has not attempted to convert a
+		 * write to a read without holding the lock.
+		 */
+
+		if (flags & GENLOCK_WRITE_TO_READ) {
+			GENLOCK_LOG_ERR("Handle must have lock to convert"
+				"write to read\n");
+			ret = -EINVAL;
 			goto done;
 		}
 
 		/*
-		 * Otherwise the user tried to turn a read into a write, and we
-		 * don't allow that.
+		 * If we request a read and the lock is held by a read, then go
+		 * ahead and share the lock
 		 */
-		GENLOCK_LOG_ERR("Trying to upgrade a read lock to a write"
-				"lock\n");
-		ret = -EINVAL;
-		goto done;
+
+		if (op == GENLOCK_RDLOCK && lock->state == _RDLOCK)
+			goto dolock;
 	}
 
-	/*
-	 * If we request a read and the lock is held by a read, then go
-	 * ahead and share the lock
-	 */
-
-	if (op == GENLOCK_RDLOCK && lock->state == _RDLOCK)
-		goto dolock;
-
 	/* Treat timeout 0 just like a NOBLOCK flag and return if the
 	   lock cannot be aquired without blocking */
 
@@ -356,15 +372,26 @@
 		goto done;
 	}
 
-	/* Wait while the lock remains in an incompatible state */
+	/*
+	 * Wait while the lock remains in an incompatible state
+	 * state    op    wait
+	 * -------------------
+	 * unlocked n/a   no
+	 * read     read  no
+	 * read     write yes
+	 * write    n/a   yes
+	 */
 
-	while (lock->state != _UNLOCKED) {
+	while ((lock->state == _RDLOCK && op == _WRLOCK) ||
+			lock->state == _WRLOCK) {
 		signed long elapsed;
 
 		spin_unlock_irqrestore(&lock->lock, irqflags);
 
 		elapsed = wait_event_interruptible_timeout(lock->queue,
-			lock->state == _UNLOCKED, ticks);
+			lock->state == _UNLOCKED ||
+			(lock->state == _RDLOCK && op == _RDLOCK),
+			ticks);
 
 		spin_lock_irqsave(&lock->lock, irqflags);
 
@@ -381,7 +408,7 @@
 
 	list_add_tail(&handle->entry, &lock->active);
 	lock->state = op;
-	handle->active = 1;
+	handle->active++;
 
 done:
 	spin_unlock_irqrestore(&lock->lock, irqflags);
@@ -390,7 +417,7 @@
 }
 
 /**
- * genlock_lock - Acquire or release a lock
+ * genlock_lock - Acquire or release a lock (depreciated)
  * @handle - pointer to the genlock handle that is requesting the lock
  * @op - the operation to perform (RDLOCK, WRLOCK, UNLOCK)
  * @flags - flags to control the operation
@@ -403,6 +430,61 @@
 	uint32_t timeout)
 {
 	struct genlock *lock;
+	unsigned long irqflags;
+
+	int ret = 0;
+
+	if (IS_ERR_OR_NULL(handle)) {
+		GENLOCK_LOG_ERR("Invalid handle\n");
+		return -EINVAL;
+	}
+
+	lock = handle->lock;
+
+	if (lock == NULL) {
+		GENLOCK_LOG_ERR("Handle does not have a lock attached\n");
+		return -EINVAL;
+	}
+
+	switch (op) {
+	case GENLOCK_UNLOCK:
+		ret = _genlock_unlock(lock, handle);
+		break;
+	case GENLOCK_RDLOCK:
+		spin_lock_irqsave(&lock->lock, irqflags);
+		if (handle_has_lock(lock, handle)) {
+			/* request the WRITE_TO_READ flag for compatibility */
+			flags |= GENLOCK_WRITE_TO_READ;
+		}
+		spin_unlock_irqrestore(&lock->lock, irqflags);
+		/* fall through to take lock */
+	case GENLOCK_WRLOCK:
+		ret = _genlock_lock(lock, handle, op, flags, timeout);
+		break;
+	default:
+		GENLOCK_LOG_ERR("Invalid lock operation\n");
+		ret = -EINVAL;
+		break;
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL(genlock_lock);
+
+/**
+ * genlock_dreadlock - Acquire or release a lock
+ * @handle - pointer to the genlock handle that is requesting the lock
+ * @op - the operation to perform (RDLOCK, WRLOCK, UNLOCK)
+ * @flags - flags to control the operation
+ * @timeout - optional timeout to wait for the lock to come free
+ *
+ * Returns: 0 on success or error code on failure
+ */
+
+int genlock_dreadlock(struct genlock_handle *handle, int op, int flags,
+	uint32_t timeout)
+{
+	struct genlock *lock;
 
 	int ret = 0;
 
@@ -434,7 +516,7 @@
 
 	return ret;
 }
-EXPORT_SYMBOL(genlock_lock);
+EXPORT_SYMBOL(genlock_dreadlock);
 
 /**
  * genlock_wait - Wait for the lock to be released
@@ -657,6 +739,14 @@
 		return genlock_lock(handle, param.op, param.flags,
 			param.timeout);
 	}
+	case GENLOCK_IOC_DREADLOCK: {
+		if (copy_from_user(&param, (void __user *) arg,
+		sizeof(param)))
+			return -EFAULT;
+
+		return genlock_dreadlock(handle, param.op, param.flags,
+			param.timeout);
+	}
 	case GENLOCK_IOC_WAIT: {
 		if (copy_from_user(&param, (void __user *) arg,
 		sizeof(param)))
diff --git a/drivers/char/diag/diagchar.h b/drivers/char/diag/diagchar.h
index 656223b..49d687d 100644
--- a/drivers/char/diag/diagchar.h
+++ b/drivers/char/diag/diagchar.h
@@ -162,6 +162,7 @@
 	int count_write_struct_pool;
 	int used;
 	/* Buffers for masks */
+	struct mutex diag_cntl_mutex;
 	struct diag_ctrl_event_mask *event_mask;
 	struct diag_ctrl_log_mask *log_mask;
 	struct diag_ctrl_msg_mask *msg_mask;
diff --git a/drivers/char/diag/diagfwd.c b/drivers/char/diag/diagfwd.c
index 4726da1..a7a4a2a 100644
--- a/drivers/char/diag/diagfwd.c
+++ b/drivers/char/diag/diagfwd.c
@@ -22,7 +22,6 @@
 #include <linux/delay.h>
 #include <linux/reboot.h>
 #include <linux/of.h>
-#include <linux/spinlock.h>
 #include <linux/kmemleak.h>
 #ifdef CONFIG_DIAG_OVER_USB
 #include <mach/usbdiag.h>
@@ -60,7 +59,6 @@
 	int num_items;
 	int index;
 };
-spinlock_t diag_cntl_lock;
 
 #define CREATE_MSG_MASK_TBL_ROW(XX)					\
 do {									\
@@ -775,9 +773,9 @@
 	void *buf = driver->buf_log_mask_update;
 	int header_size = sizeof(struct diag_ctrl_log_mask);
 	struct mask_info *ptr = (struct mask_info *)driver->log_masks;
-	int i, size, wr_size = -ENOMEM, retry_count = 0;
-	unsigned long flags = 0;
+	int i, size, wr_size = -ENOMEM, retry_count = 0, timer;
 
+	mutex_lock(&driver->diag_cntl_mutex);
 	for (i = 0; i < MAX_EQUIP_ID; i++) {
 		size = (ptr->num_items+7)/8;
 		/* reached null entry */
@@ -798,15 +796,13 @@
 									 size);
 			if (ch) {
 				while (retry_count < 3) {
-					spin_lock_irqsave(&diag_cntl_lock,
-									 flags);
 					wr_size = smd_write(ch, buf,
 							 header_size + size);
-					spin_unlock_irqrestore(&diag_cntl_lock,
-									 flags);
 					if (wr_size == -ENOMEM) {
 						retry_count++;
-						usleep(20000);
+						for (timer = 0; timer < 5;
+								 timer++)
+							udelay(2000);
 					} else
 						break;
 				}
@@ -822,17 +818,19 @@
 		}
 		ptr++;
 	}
+	mutex_unlock(&driver->diag_cntl_mutex);
 }
 
 void diag_send_event_mask_update(smd_channel_t *ch, int num_bytes)
 {
 	void *buf = driver->buf_event_mask_update;
 	int header_size = sizeof(struct diag_ctrl_event_mask);
-	int wr_size = -ENOMEM, retry_count = 0;
-	unsigned long flags = 0;
+	int wr_size = -ENOMEM, retry_count = 0, timer;
 
+	mutex_lock(&driver->diag_cntl_mutex);
 	if (num_bytes == 0) {
 		pr_debug("diag: event mask not set yet, so no update\n");
+		mutex_unlock(&driver->diag_cntl_mutex);
 		return;
 	}
 	/* send event mask update */
@@ -846,12 +844,11 @@
 	memcpy(buf+header_size, driver->event_masks, num_bytes);
 	if (ch) {
 		while (retry_count < 3) {
-			spin_lock_irqsave(&diag_cntl_lock, flags);
 			wr_size = smd_write(ch, buf, header_size + num_bytes);
-			spin_unlock_irqrestore(&diag_cntl_lock, flags);
 			if (wr_size == -ENOMEM) {
 				retry_count++;
-				usleep(20000);
+				for (timer = 0; timer < 5; timer++)
+					udelay(2000);
 			} else
 				break;
 		}
@@ -860,17 +857,18 @@
 					 wr_size, header_size + num_bytes);
 	} else
 		pr_err("diag: ch not valid for event update\n");
+	mutex_unlock(&driver->diag_cntl_mutex);
 }
 
 void diag_send_msg_mask_update(smd_channel_t *ch, int updated_ssid_first,
 						int updated_ssid_last, int proc)
 {
 	void *buf = driver->buf_msg_mask_update;
-	int first, last, size = -ENOMEM, retry_count = 0;
+	int first, last, size = -ENOMEM, retry_count = 0, timer;
 	int header_size = sizeof(struct diag_ctrl_msg_mask);
 	uint8_t *ptr = driver->msg_masks;
-	unsigned long flags = 0;
 
+	mutex_lock(&driver->diag_cntl_mutex);
 	while (*(uint32_t *)(ptr + 4)) {
 		first = *(uint32_t *)ptr;
 		ptr += 4;
@@ -893,31 +891,31 @@
 				 4 * (driver->msg_mask->msg_mask_size));
 			if (ch) {
 				while (retry_count < 3) {
-					spin_lock_irqsave(&diag_cntl_lock,
-									 flags);
 					size = smd_write(ch, buf, header_size +
 					 4*(driver->msg_mask->msg_mask_size));
-					spin_unlock_irqrestore(&diag_cntl_lock,
-									 flags);
 					if (size == -ENOMEM) {
 						retry_count++;
-						usleep(20000);
+						for (timer = 0; timer < 5;
+								 timer++)
+							udelay(2000);
 					} else
 						break;
 				}
 				if (size != header_size +
 					 4*(driver->msg_mask->msg_mask_size))
-					pr_err("diag:  msg mask update fail %d,"
-							" tried %d\n", size,
-			 header_size + 4*(driver->msg_mask->msg_mask_size));
+					pr_err("diag: proc %d, msg mask update "
+	 "fail %d, tried %d\n", proc, size,
+	 header_size + 4*(driver->msg_mask->msg_mask_size));
 				else
 					pr_debug("diag: sending mask update for"
-		" ssid first %d, last %d on PROC %d\n", first, last, proc);
+		"ssid first %d, last %d on PROC %d\n", first, last, proc);
 			} else
-				pr_err("diag: ch invalid msg mask update\n");
+				pr_err("diag: proc %d, ch invalid msg mask"
+						 "update\n", proc);
 		}
 		ptr += MAX_SSID_PER_RANGE*4;
 	}
+	mutex_unlock(&driver->diag_cntl_mutex);
 }
 
 static int diag_process_apps_pkt(unsigned char *buf, int len)
@@ -962,7 +960,6 @@
 #endif
 	} /* Disable log masks */
 	else if (*buf == 0x73 && *(int *)(buf+4) == 0) {
-		buf += 8;
 		/* Disable mask for each log code */
 		diag_disable_log_mask();
 		diag_update_userspace_clients(LOG_MASKS_TYPE);
@@ -975,17 +972,16 @@
 			*(int *)(driver->apps_rsp_buf + 4) = 0x0;
 			if (driver->ch_cntl)
 				diag_send_log_mask_update(driver->ch_cntl,
-								 *(int *)buf);
+								 ALL_EQUIP_ID);
 			if (driver->chqdsp_cntl)
 				diag_send_log_mask_update(driver->chqdsp_cntl,
-								 *(int *)buf);
+								 ALL_EQUIP_ID);
 			if (driver->ch_wcnss_cntl)
 				diag_send_log_mask_update(driver->ch_wcnss_cntl,
-								 *(int *)buf);
+								 ALL_EQUIP_ID);
 			ENCODE_RSP_AND_SEND(7);
 			return 0;
-		} else
-			buf = temp;
+		}
 #endif
 	} /* Set runtime message mask  */
 	else if ((*buf == 0x7d) && (*(buf+1) == 0x4)) {
@@ -1792,7 +1788,7 @@
 	diag_debug_buf_idx = 0;
 	driver->read_len_legacy = 0;
 	driver->use_device_tree = has_device_tree();
-	spin_lock_init(&diag_cntl_lock);
+	mutex_init(&driver->diag_cntl_mutex);
 
 	if (driver->event_mask == NULL) {
 		driver->event_mask = kzalloc(sizeof(
diff --git a/drivers/char/msm_rotator.c b/drivers/char/msm_rotator.c
index 865fcc2..b457a00 100644
--- a/drivers/char/msm_rotator.c
+++ b/drivers/char/msm_rotator.c
@@ -1562,7 +1562,8 @@
 		}
 	}
 
-	msm_rotator_dev->regulator = regulator_get(NULL, pdata->regulator_name);
+	msm_rotator_dev->regulator = regulator_get(&msm_rotator_dev->pdev->dev,
+						   "vdd");
 	if (IS_ERR(msm_rotator_dev->regulator))
 		msm_rotator_dev->regulator = NULL;
 
diff --git a/drivers/gpu/ion/ion_carveout_heap.c b/drivers/gpu/ion/ion_carveout_heap.c
index 84789ef..c554e56 100644
--- a/drivers/gpu/ion/ion_carveout_heap.c
+++ b/drivers/gpu/ion/ion_carveout_heap.c
@@ -274,8 +274,9 @@
 	struct iommu_domain *domain;
 	int ret = 0;
 	unsigned long extra;
-	int prot = ION_IS_CACHED(flags) ? 1 : 0;
 	struct scatterlist *sglist = 0;
+	int prot = IOMMU_WRITE | IOMMU_READ;
+	prot |= ION_IS_CACHED(flags) ? IOMMU_CACHE : 0;
 
 	data->mapped_size = iova_length;
 
diff --git a/drivers/gpu/ion/ion_cp_heap.c b/drivers/gpu/ion/ion_cp_heap.c
index b383e68..4c10c64 100644
--- a/drivers/gpu/ion/ion_cp_heap.c
+++ b/drivers/gpu/ion/ion_cp_heap.c
@@ -577,8 +577,9 @@
 	struct iommu_domain *domain;
 	int ret = 0;
 	unsigned long extra;
-	int prot = ION_IS_CACHED(flags) ? 1 : 0;
 	struct scatterlist *sglist = 0;
+	int prot = IOMMU_WRITE | IOMMU_READ;
+	prot |= ION_IS_CACHED(flags) ? IOMMU_CACHE : 0;
 
 	data->mapped_size = iova_length;
 
diff --git a/drivers/gpu/ion/ion_iommu_heap.c b/drivers/gpu/ion/ion_iommu_heap.c
index baf0a66..6951603 100644
--- a/drivers/gpu/ion/ion_iommu_heap.c
+++ b/drivers/gpu/ion/ion_iommu_heap.c
@@ -181,8 +181,9 @@
 	struct iommu_domain *domain;
 	int ret = 0;
 	unsigned long extra;
-	int prot = ION_IS_CACHED(flags) ? 1 : 0;
 	struct ion_iommu_priv_data *buffer_data = buffer->priv_virt;
+	int prot = IOMMU_WRITE | IOMMU_READ;
+	prot |= ION_IS_CACHED(flags) ? IOMMU_CACHE : 0;
 
 	BUG_ON(!msm_use_iommu());
 
diff --git a/drivers/gpu/ion/ion_system_heap.c b/drivers/gpu/ion/ion_system_heap.c
index 9bb240c..7e859ce 100644
--- a/drivers/gpu/ion/ion_system_heap.c
+++ b/drivers/gpu/ion/ion_system_heap.c
@@ -228,7 +228,8 @@
 	int npages = buffer->size >> PAGE_SHIFT;
 	void *vaddr = buffer->priv_virt;
 	struct scatterlist *sglist = 0;
-	int prot = ION_IS_CACHED(flags) ? 1 : 0;
+	int prot = IOMMU_WRITE | IOMMU_READ;
+	prot |= ION_IS_CACHED(flags) ? IOMMU_CACHE : 0;
 
 	if (!ION_IS_CACHED(flags))
 		return -EINVAL;
@@ -440,9 +441,10 @@
 	int ret = 0;
 	struct iommu_domain *domain;
 	unsigned long extra;
-	int prot = ION_IS_CACHED(flags) ? 1 : 0;
 	struct scatterlist *sglist = 0;
 	struct page *page = 0;
+	int prot = IOMMU_WRITE | IOMMU_READ;
+	prot |= ION_IS_CACHED(flags) ? IOMMU_CACHE : 0;
 
 	if (!ION_IS_CACHED(flags))
 		return -EINVAL;
diff --git a/drivers/gpu/msm/adreno.c b/drivers/gpu/msm/adreno.c
index 7c152721..9479302 100644
--- a/drivers/gpu/msm/adreno.c
+++ b/drivers/gpu/msm/adreno.c
@@ -90,7 +90,6 @@
 			.config = ADRENO_MMU_CONFIG,
 		},
 		.pwrctrl = {
-			.regulator_name = "fs_gfx3d",
 			.irq_name = KGSL_3D0_IRQ,
 		},
 		.mutex = __MUTEX_INITIALIZER(device_3d0.dev.mutex),
diff --git a/drivers/gpu/msm/adreno_a3xx.c b/drivers/gpu/msm/adreno_a3xx.c
index 8c5d76a..5187eb1 100644
--- a/drivers/gpu/msm/adreno_a3xx.c
+++ b/drivers/gpu/msm/adreno_a3xx.c
@@ -2599,7 +2599,7 @@
 	adreno_regwrite(device, A3XX_RBBM_AHB_CTL1, 0xA6FFFFFF);
 
 	/* Turn on the power counters */
-	adreno_regwrite(device, A3XX_RBBM_RBBM_CTL, 0x00003000);
+	adreno_regwrite(device, A3XX_RBBM_RBBM_CTL, 0x00030000);
 
 	/* Turn on hang detection - this spews a lot of useful information
 	 * into the RBBM registers on a hang */
diff --git a/drivers/gpu/msm/adreno_ringbuffer.c b/drivers/gpu/msm/adreno_ringbuffer.c
index d4089b9..8d900b0 100644
--- a/drivers/gpu/msm/adreno_ringbuffer.c
+++ b/drivers/gpu/msm/adreno_ringbuffer.c
@@ -337,8 +337,9 @@
 	if (status != 0)
 		return status;
 
+	/* CP ROQ queue sizes (bytes) - RB:16, ST:16, IB1:32, IB2:64 */
 	if (adreno_is_a305(adreno_dev) || adreno_is_a320(adreno_dev))
-		adreno_regwrite(device, REG_CP_QUEUE_THRESHOLDS, 0x000F0602);
+		adreno_regwrite(device, REG_CP_QUEUE_THRESHOLDS, 0x000E0602);
 
 	rb->rptr = 0;
 	rb->wptr = 0;
diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c
index 072edad..a2234f3 100644
--- a/drivers/gpu/msm/kgsl.c
+++ b/drivers/gpu/msm/kgsl.c
@@ -1846,10 +1846,20 @@
 	kgsl_check_idle(dev_priv->device);
 	return result;
 
- error_put_file_ptr:
-	if (entry->priv_data)
-		fput(entry->priv_data);
-
+error_put_file_ptr:
+	switch (entry->memtype) {
+	case KGSL_MEM_ENTRY_PMEM:
+	case KGSL_MEM_ENTRY_ASHMEM:
+		if (entry->priv_data)
+			fput(entry->priv_data);
+		break;
+	case KGSL_MEM_ENTRY_ION:
+		ion_unmap_dma(kgsl_ion_client, entry->priv_data);
+		ion_free(kgsl_ion_client, entry->priv_data);
+		break;
+	default:
+		break;
+	}
 error:
 	kfree(entry);
 	kgsl_check_idle(dev_priv->device);
diff --git a/drivers/gpu/msm/kgsl_iommu.c b/drivers/gpu/msm/kgsl_iommu.c
index e2a945f..dd32ee4 100644
--- a/drivers/gpu/msm/kgsl_iommu.c
+++ b/drivers/gpu/msm/kgsl_iommu.c
@@ -344,7 +344,7 @@
 	iommu_virt_addr = memdesc->gpuaddr;
 
 	ret = iommu_map_range(domain, iommu_virt_addr, memdesc->sg,
-				memdesc->size, 0);
+				memdesc->size, (IOMMU_READ | IOMMU_WRITE));
 	if (ret) {
 		KGSL_CORE_ERR("iommu_map_range(%p, %x, %p, %d, %d) "
 				"failed with err: %d\n", domain,
diff --git a/drivers/gpu/msm/kgsl_mmu.c b/drivers/gpu/msm/kgsl_mmu.c
index e11b119..4ba9e7f 100644
--- a/drivers/gpu/msm/kgsl_mmu.c
+++ b/drivers/gpu/msm/kgsl_mmu.c
@@ -382,18 +382,35 @@
 }
 EXPORT_SYMBOL(kgsl_mmu_start);
 
+static void mh_axi_error(struct kgsl_device *device, const char* type)
+{
+	unsigned int reg, gpu_err, phys_err, pt_base;
+
+	kgsl_regread(device, MH_AXI_ERROR, &reg);
+	pt_base = kgsl_mmu_get_current_ptbase(&device->mmu);
+	/*
+	 * Read gpu virtual and physical addresses that
+	 * caused the error from the debug data.
+	 */
+	kgsl_regwrite(device, MH_DEBUG_CTRL, 44);
+	kgsl_regread(device, MH_DEBUG_DATA, &gpu_err);
+	kgsl_regwrite(device, MH_DEBUG_CTRL, 45);
+	kgsl_regread(device, MH_DEBUG_DATA, &phys_err);
+	KGSL_MEM_CRIT(device,
+			"axi %s error: %08x pt %08x gpu %08x phys %08x\n",
+			type, reg, pt_base, gpu_err, phys_err);
+}
+
 void kgsl_mh_intrcallback(struct kgsl_device *device)
 {
 	unsigned int status = 0;
-	unsigned int reg;
 
 	kgsl_regread(device, MH_INTERRUPT_STATUS, &status);
-	kgsl_regread(device, MH_AXI_ERROR, &reg);
 
 	if (status & MH_INTERRUPT_MASK__AXI_READ_ERROR)
-		KGSL_MEM_CRIT(device, "axi read error interrupt: %08x\n", reg);
+		mh_axi_error(device, "read");
 	if (status & MH_INTERRUPT_MASK__AXI_WRITE_ERROR)
-		KGSL_MEM_CRIT(device, "axi write error interrupt: %08x\n", reg);
+		mh_axi_error(device, "write");
 	if (status & MH_INTERRUPT_MASK__MMU_PAGE_FAULT)
 		device->mmu.mmu_ops->mmu_pagefault(&device->mmu);
 
diff --git a/drivers/gpu/msm/kgsl_pwrctrl.c b/drivers/gpu/msm/kgsl_pwrctrl.c
index 6fa7da2..15a0252 100644
--- a/drivers/gpu/msm/kgsl_pwrctrl.c
+++ b/drivers/gpu/msm/kgsl_pwrctrl.c
@@ -522,7 +522,7 @@
 		clk_set_rate(pwr->grp_clks[0], pwr->
 				pwrlevels[pwr->num_pwrlevels - 1].gpu_freq);
 
-	pwr->gpu_reg = regulator_get(NULL, pwr->regulator_name);
+	pwr->gpu_reg = regulator_get(&pdev->dev, "vdd");
 	if (IS_ERR(pwr->gpu_reg))
 		pwr->gpu_reg = NULL;
 
diff --git a/drivers/gpu/msm/kgsl_pwrctrl.h b/drivers/gpu/msm/kgsl_pwrctrl.h
index a677fec..6325b66 100644
--- a/drivers/gpu/msm/kgsl_pwrctrl.h
+++ b/drivers/gpu/msm/kgsl_pwrctrl.h
@@ -54,7 +54,6 @@
 	uint32_t pcl;
 	unsigned int nap_allowed;
 	unsigned int idle_needed;
-	const char *regulator_name;
 	const char *irq_name;
 	s64 time;
 	struct kgsl_busy busy;
diff --git a/drivers/gpu/msm/kgsl_pwrscale_trustzone.c b/drivers/gpu/msm/kgsl_pwrscale_trustzone.c
index e0825c3..4daff5f 100644
--- a/drivers/gpu/msm/kgsl_pwrscale_trustzone.c
+++ b/drivers/gpu/msm/kgsl_pwrscale_trustzone.c
@@ -180,7 +180,8 @@
 	struct tz_priv *priv;
 
 	/* Trustzone is only valid for some SOCs */
-	if (!(cpu_is_msm8x60() || cpu_is_msm8960() || cpu_is_apq8064()))
+	if (!(cpu_is_msm8x60() || cpu_is_msm8960() || cpu_is_apq8064() ||
+		cpu_is_msm8930()))
 		return -EINVAL;
 
 	priv = pwrscale->priv = kzalloc(sizeof(struct tz_priv), GFP_KERNEL);
diff --git a/drivers/gpu/msm/z180.c b/drivers/gpu/msm/z180.c
index df2ca78..240de9a 100644
--- a/drivers/gpu/msm/z180.c
+++ b/drivers/gpu/msm/z180.c
@@ -150,7 +150,6 @@
 			.config = Z180_MMU_CONFIG,
 		},
 		.pwrctrl = {
-			.regulator_name = "fs_gfx2d0",
 			.irq_name = KGSL_2D0_IRQ,
 		},
 		.mutex = __MUTEX_INITIALIZER(device_2d0.dev.mutex),
@@ -181,7 +180,6 @@
 			.config = Z180_MMU_CONFIG,
 		},
 		.pwrctrl = {
-			.regulator_name = "fs_gfx2d1",
 			.irq_name = KGSL_2D1_IRQ,
 		},
 		.mutex = __MUTEX_INITIALIZER(device_2d1.dev.mutex),
diff --git a/drivers/media/dvb/Kconfig b/drivers/media/dvb/Kconfig
index ee214c3..f41a4ef 100644
--- a/drivers/media/dvb/Kconfig
+++ b/drivers/media/dvb/Kconfig
@@ -84,4 +84,8 @@
 	depends on DVB_CORE
 source "drivers/media/dvb/frontends/Kconfig"
 
+comment "Qualcomm MPQ adapter"
+	depends on ARCH_MSM && DVB_CORE
+source "drivers/media/dvb/mpq/Kconfig"
+
 endif # DVB_CAPTURE_DRIVERS
diff --git a/drivers/media/dvb/Makefile b/drivers/media/dvb/Makefile
index a1a0875..68877c8 100644
--- a/drivers/media/dvb/Makefile
+++ b/drivers/media/dvb/Makefile
@@ -18,3 +18,5 @@
 		ngene/
 
 obj-$(CONFIG_DVB_FIREDTV)	+= firewire/
+obj-$(CONFIG_DVB_MPQ)		+= mpq/
+
diff --git a/drivers/media/dvb/dvb-core/demux.h b/drivers/media/dvb/dvb-core/demux.h
index eb91fd8..ff0c9d8 100644
--- a/drivers/media/dvb/dvb-core/demux.h
+++ b/drivers/media/dvb/dvb-core/demux.h
@@ -7,6 +7,8 @@
  * Copyright (c) 2000 Nokia Research Center
  *                    Tampere, FINLAND
  *
+ * 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 Lesser General Public License
  * as published by the Free Software Foundation; either version 2.1
@@ -190,6 +192,14 @@
 				struct dmx_section_filter * source,
 				enum dmx_success success);
 
+typedef int (*dmx_ts_fullness) (
+				struct dmx_ts_feed *source,
+				int required_space);
+
+typedef int (*dmx_section_fullness) (
+				struct dmx_section_filter *source,
+				int required_space);
+
 /*--------------------------------------------------------------------------*/
 /* DVB Front-End */
 /*--------------------------------------------------------------------------*/
@@ -247,7 +257,7 @@
 	void* priv;                  /* Pointer to private data of the API client */
 	int (*open) (struct dmx_demux* demux);
 	int (*close) (struct dmx_demux* demux);
-	int (*write) (struct dmx_demux* demux, const char __user *buf, size_t count);
+	int (*write) (struct dmx_demux *demux, const char *buf, size_t count);
 	int (*allocate_ts_feed) (struct dmx_demux* demux,
 				 struct dmx_ts_feed** feed,
 				 dmx_ts_cb callback);
@@ -271,7 +281,20 @@
 
 	int (*get_caps) (struct dmx_demux* demux, struct dmx_caps *caps);
 
-	int (*set_source) (struct dmx_demux* demux, const dmx_source_t *src);
+	int (*set_source) (struct dmx_demux *demux, const dmx_source_t *src);
+
+	int (*set_tsp_format) (struct dmx_demux *demux,
+				enum dmx_tsp_format_t tsp_format);
+
+	int (*set_tsp_out_format) (struct dmx_demux *demux,
+				enum dmx_tsp_format_t tsp_format);
+
+	int (*set_playback_mode) (struct dmx_demux *demux,
+				 enum dmx_playback_mode_t mode,
+				 dmx_ts_fullness ts_fullness_callback,
+				 dmx_section_fullness sec_fullness_callback);
+
+	int (*write_cancel) (struct dmx_demux *demux);
 
 	int (*get_stc) (struct dmx_demux* demux, unsigned int num,
 			u64 *stc, unsigned int *base);
diff --git a/drivers/media/dvb/dvb-core/dmxdev.c b/drivers/media/dvb/dvb-core/dmxdev.c
index e4b5c03..ed3f731 100644
--- a/drivers/media/dvb/dvb-core/dmxdev.c
+++ b/drivers/media/dvb/dvb-core/dmxdev.c
@@ -4,6 +4,8 @@
  * Copyright (C) 2000 Ralph Metzler & Marcus Metzler
  *		      for convergence integrated media GmbH
  *
+ * 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 Lesser General Public License
  * as published by the Free Software Foundation; either version 2.1
@@ -28,6 +30,7 @@
 #include <linux/poll.h>
 #include <linux/ioctl.h>
 #include <linux/wait.h>
+#include <linux/mm.h>
 #include <asm/uaccess.h>
 #include <asm/system.h>
 #include "dmxdev.h"
@@ -81,12 +84,15 @@
 			break;
 		}
 
-		ret = wait_event_interruptible(src->queue,
-					       !dvb_ringbuffer_empty(src) ||
-					       (src->error != 0));
+		ret = wait_event_interruptible(src->queue, (!src->data) ||
+						!dvb_ringbuffer_empty(src) ||
+						(src->error != 0));
 		if (ret < 0)
 			break;
 
+		if (!src->data)
+			return 0;
+
 		if (src->error) {
 			ret = src->error;
 			dvb_ringbuffer_flush(src);
@@ -104,6 +110,9 @@
 		buf += ret;
 	}
 
+	if (count - todo) /* some data was read? */
+		wake_up_all(&src->queue);
+
 	return (count - todo) ? (count - todo) : ret;
 }
 
@@ -126,8 +135,11 @@
 	struct dvb_device *dvbdev = file->private_data;
 	struct dmxdev *dmxdev = dvbdev->priv;
 	struct dmx_frontend *front;
+	void *mem;
 
-	dprintk("function : %s\n", __func__);
+	dprintk("function : %s(%X)\n",
+			__func__,
+			(file->f_flags & O_ACCMODE));
 
 	if (mutex_lock_interruptible(&dmxdev->mutex))
 		return -ERESTARTSYS;
@@ -145,21 +157,20 @@
 	}
 
 	if ((file->f_flags & O_ACCMODE) == O_RDONLY) {
-		void *mem;
 		if (!dvbdev->readers) {
 			mutex_unlock(&dmxdev->mutex);
 			return -EBUSY;
 		}
-		mem = vmalloc(DVR_BUFFER_SIZE);
+		mem = vmalloc_user(DVR_BUFFER_SIZE);
 		if (!mem) {
 			mutex_unlock(&dmxdev->mutex);
 			return -ENOMEM;
 		}
 		dvb_ringbuffer_init(&dmxdev->dvr_buffer, mem, DVR_BUFFER_SIZE);
 		dvbdev->readers--;
-	}
-
-	if ((file->f_flags & O_ACCMODE) == O_WRONLY) {
+	} else if (!dvbdev->writers) {
+		dmxdev->dvr_in_exit = 0;
+		dmxdev->dvr_processing_input = 0;
 		dmxdev->dvr_orig_fe = dmxdev->demux->frontend;
 
 		if (!dmxdev->demux->write) {
@@ -173,9 +184,22 @@
 			mutex_unlock(&dmxdev->mutex);
 			return -EINVAL;
 		}
+
+		mem = vmalloc_user(DVR_BUFFER_SIZE);
+		if (!mem) {
+			mutex_unlock(&dmxdev->mutex);
+			return -ENOMEM;
+		}
+
 		dmxdev->demux->disconnect_frontend(dmxdev->demux);
 		dmxdev->demux->connect_frontend(dmxdev->demux, front);
+
+		dvb_ringbuffer_init(&dmxdev->dvr_input_buffer,
+							mem,
+							DVR_BUFFER_SIZE);
+		dvbdev->writers--;
 	}
+
 	dvbdev->users++;
 	mutex_unlock(&dmxdev->mutex);
 	return 0;
@@ -188,11 +212,6 @@
 
 	mutex_lock(&dmxdev->mutex);
 
-	if ((file->f_flags & O_ACCMODE) == O_WRONLY) {
-		dmxdev->demux->disconnect_frontend(dmxdev->demux);
-		dmxdev->demux->connect_frontend(dmxdev->demux,
-						dmxdev->dvr_orig_fe);
-	}
 	if ((file->f_flags & O_ACCMODE) == O_RDONLY) {
 		dvbdev->readers++;
 		if (dmxdev->dvr_buffer.data) {
@@ -201,6 +220,51 @@
 			spin_lock_irq(&dmxdev->lock);
 			dmxdev->dvr_buffer.data = NULL;
 			spin_unlock_irq(&dmxdev->lock);
+			wake_up_all(&dmxdev->dvr_buffer.queue);
+			vfree(mem);
+		}
+	} else {
+		int i;
+
+		dmxdev->dvr_in_exit = 1;
+		wake_up_all(&dmxdev->dvr_input_buffer.queue);
+
+		/*
+		 * There might be dmx filters reading now from DVR
+		 * device, in PULL mode, they might be also stalled
+		 * on output, signal to them that DVR is exiting.
+		 */
+		if (dmxdev->playback_mode == DMX_PB_MODE_PULL) {
+			wake_up_all(&dmxdev->dvr_buffer.queue);
+
+			for (i = 0; i < dmxdev->filternum; i++)
+				if (dmxdev->filter[i].state == DMXDEV_STATE_GO)
+					wake_up_all(
+					&dmxdev->filter[i].buffer.queue);
+		}
+
+		/* notify kernel demux that we are canceling */
+		if (dmxdev->demux->write_cancel)
+			dmxdev->demux->write_cancel(dmxdev->demux);
+
+		/*
+		 * Now flush dvr-in workqueue so that no one
+		 * would process data from dvr input buffer any more
+		 * before it gets freed.
+		 */
+		flush_workqueue(dmxdev->dvr_input_workqueue);
+
+		dvbdev->writers++;
+		dmxdev->demux->disconnect_frontend(dmxdev->demux);
+		dmxdev->demux->connect_frontend(dmxdev->demux,
+						dmxdev->dvr_orig_fe);
+
+		if (dmxdev->dvr_input_buffer.data) {
+			void *mem = dmxdev->dvr_input_buffer.data;
+			mb();
+			spin_lock_irq(&dmxdev->dvr_in_lock);
+			dmxdev->dvr_input_buffer.data = NULL;
+			spin_unlock_irq(&dmxdev->dvr_in_lock);
 			vfree(mem);
 		}
 	}
@@ -217,17 +281,20 @@
 	return 0;
 }
 
-static ssize_t dvb_dvr_write(struct file *file, const char __user *buf,
-			     size_t count, loff_t *ppos)
+
+static int dvb_dvr_mmap(struct file *filp, struct vm_area_struct *vma)
 {
-	struct dvb_device *dvbdev = file->private_data;
+	struct dvb_device *dvbdev = filp->private_data;
 	struct dmxdev *dmxdev = dvbdev->priv;
+	struct dvb_ringbuffer *buffer;
+	int vma_size;
+	int buffer_size;
 	int ret;
 
-	if (!dmxdev->demux->write)
-		return -EOPNOTSUPP;
-	if ((file->f_flags & O_ACCMODE) != O_WRONLY)
+	if (((filp->f_flags & O_ACCMODE) == O_RDONLY) &&
+		(vma->vm_flags & VM_WRITE))
 		return -EINVAL;
+
 	if (mutex_lock_interruptible(&dmxdev->mutex))
 		return -ERESTARTSYS;
 
@@ -235,11 +302,114 @@
 		mutex_unlock(&dmxdev->mutex);
 		return -ENODEV;
 	}
-	ret = dmxdev->demux->write(dmxdev->demux, buf, count);
+
+	if ((filp->f_flags & O_ACCMODE) == O_RDONLY)
+		buffer = &dmxdev->dvr_buffer;
+	else
+		buffer = &dmxdev->dvr_input_buffer;
+
+	vma_size = vma->vm_end - vma->vm_start;
+
+	/* Make sure requested mapping is not larger than buffer size */
+	buffer_size = buffer->size + (PAGE_SIZE-1);
+	buffer_size = buffer_size & ~(PAGE_SIZE-1);
+
+	if (vma_size != buffer_size) {
+		mutex_unlock(&dmxdev->mutex);
+		return -EINVAL;
+	}
+
+	ret = remap_vmalloc_range(vma, buffer->data, 0);
+	if (ret) {
+		mutex_unlock(&dmxdev->mutex);
+		return ret;
+	}
+
+	vma->vm_flags |= VM_RESERVED;
+	vma->vm_flags |= VM_DONTEXPAND;
+
 	mutex_unlock(&dmxdev->mutex);
 	return ret;
 }
 
+static ssize_t dvb_dvr_write(struct file *file, const char __user *buf,
+			     size_t count, loff_t *ppos)
+{
+	struct dvb_device *dvbdev = file->private_data;
+	struct dmxdev *dmxdev = dvbdev->priv;
+	struct dvb_ringbuffer *src = &dmxdev->dvr_input_buffer;
+	int ret;
+	size_t todo;
+	ssize_t free_space;
+
+	if (!dmxdev->demux->write)
+		return -EOPNOTSUPP;
+
+	if ((file->f_flags & O_ACCMODE) == O_RDONLY)
+		return -EINVAL;
+
+	if ((file->f_flags & O_NONBLOCK) &&
+		(dvb_ringbuffer_free(src) == 0))
+		return -EWOULDBLOCK;
+
+	ret = 0;
+	for (todo = count; todo > 0; todo -= ret) {
+		ret = wait_event_interruptible(src->queue,
+						   (!src->data) ||
+					       (dvb_ringbuffer_free(src)) ||
+					       (src->error != 0) ||
+					       (dmxdev->dvr_in_exit));
+
+		if (ret < 0)
+			return ret;
+
+		if (mutex_lock_interruptible(&dmxdev->mutex))
+			return -ERESTARTSYS;
+
+		if (!src->data) {
+			mutex_unlock(&dmxdev->mutex);
+			return 0;
+		}
+
+		if (dmxdev->exit || dmxdev->dvr_in_exit) {
+			mutex_unlock(&dmxdev->mutex);
+			return -ENODEV;
+		}
+
+		if (src->error) {
+			ret = src->error;
+			dvb_ringbuffer_flush(src);
+			mutex_unlock(&dmxdev->mutex);
+			wake_up_all(&src->queue);
+			return ret;
+		}
+
+		free_space = dvb_ringbuffer_free(src);
+
+		if (free_space > todo)
+			free_space = todo;
+
+		ret = dvb_ringbuffer_write_user(src, buf, free_space);
+
+		if (ret < 0) {
+			mutex_unlock(&dmxdev->mutex);
+			return ret;
+		}
+
+		buf += ret;
+
+		mutex_unlock(&dmxdev->mutex);
+
+		wake_up_all(&src->queue);
+
+		if (!work_pending(&dmxdev->dvr_input_work))
+			queue_work(dmxdev->dvr_input_workqueue,
+						&dmxdev->dvr_input_work);
+	}
+
+	return (count - todo) ? (count - todo) : ret;
+}
+
 static ssize_t dvb_dvr_read(struct file *file, char __user *buf, size_t count,
 			    loff_t *ppos)
 {
@@ -254,39 +424,223 @@
 				      buf, count, ppos);
 }
 
-static int dvb_dvr_set_buffer_size(struct dmxdev *dmxdev,
-				      unsigned long size)
+static void dvr_input_work_func(struct work_struct *worker)
 {
-	struct dvb_ringbuffer *buf = &dmxdev->dvr_buffer;
+	struct dmxdev *dmxdev =
+		container_of(worker, struct dmxdev, dvr_input_work);
+	struct dvb_ringbuffer *src = &dmxdev->dvr_input_buffer;
+	int ret;
+	size_t todo;
+	size_t split;
+
+	while (1) {
+		/* wait for input */
+		ret = wait_event_interruptible(src->queue,
+						   (!src->data) ||
+					       (dvb_ringbuffer_avail(src)) ||
+					       (src->error != 0) ||
+					       (dmxdev->dvr_in_exit));
+
+		if (ret < 0)
+			break;
+
+		spin_lock(&dmxdev->dvr_in_lock);
+
+		if (!src->data || dmxdev->exit || dmxdev->dvr_in_exit) {
+			spin_unlock(&dmxdev->dvr_in_lock);
+			break;
+		}
+
+		if (src->error) {
+			spin_unlock(&dmxdev->dvr_in_lock);
+			wake_up_all(&src->queue);
+			break;
+		}
+
+		dmxdev->dvr_processing_input = 1;
+
+		ret = dvb_ringbuffer_avail(src);
+		todo = ret;
+
+		split = (src->pread + ret > src->size) ?
+				src->size - src->pread :
+				0;
+
+		/*
+		 * In DVR PULL mode, write might block.
+		 * Lock on DVR buffer is released before calling to
+		 * write, if DVR was released meanwhile, dvr_in_exit is
+		 * prompted. Lock is aquired when updating the read pointer
+		 * again to preserve read/write pointers consistancy
+		 */
+		if (split > 0) {
+			spin_unlock(&dmxdev->dvr_in_lock);
+			dmxdev->demux->write(dmxdev->demux,
+						src->data + src->pread,
+						split);
+
+			if (dmxdev->dvr_in_exit)
+				break;
+
+			spin_lock(&dmxdev->dvr_in_lock);
+
+			todo -= split;
+			DVB_RINGBUFFER_SKIP(src, split);
+		}
+
+		spin_unlock(&dmxdev->dvr_in_lock);
+		dmxdev->demux->write(dmxdev->demux,
+					src->data + src->pread, todo);
+
+		if (dmxdev->dvr_in_exit)
+			break;
+
+		spin_lock(&dmxdev->dvr_in_lock);
+
+		DVB_RINGBUFFER_SKIP(src, todo);
+		dmxdev->dvr_processing_input = 0;
+		spin_unlock(&dmxdev->dvr_in_lock);
+
+		wake_up_all(&src->queue);
+	}
+}
+
+static int dvb_dvr_set_buffer_size(struct dmxdev *dmxdev,
+						unsigned int f_flags,
+						unsigned long size)
+{
+	struct dvb_ringbuffer *buf;
 	void *newmem;
 	void *oldmem;
+	spinlock_t *lock;
 
 	dprintk("function : %s\n", __func__);
 
+	if ((f_flags & O_ACCMODE) == O_RDONLY) {
+		buf = &dmxdev->dvr_buffer;
+		lock = &dmxdev->lock;
+	} else {
+		buf = &dmxdev->dvr_input_buffer;
+		lock = &dmxdev->dvr_in_lock;
+	}
+
 	if (buf->size == size)
 		return 0;
 	if (!size)
 		return -EINVAL;
 
-	newmem = vmalloc(size);
+	newmem = vmalloc_user(size);
 	if (!newmem)
 		return -ENOMEM;
 
 	oldmem = buf->data;
 
-	spin_lock_irq(&dmxdev->lock);
+	spin_lock_irq(lock);
+
+	if (((f_flags & O_ACCMODE) != O_RDONLY) &&
+		(dmxdev->dvr_processing_input)) {
+		spin_unlock_irq(lock);
+		vfree(oldmem);
+		return -EBUSY;
+	}
+
 	buf->data = newmem;
 	buf->size = size;
 
 	/* reset and not flush in case the buffer shrinks */
 	dvb_ringbuffer_reset(buf);
-	spin_unlock_irq(&dmxdev->lock);
+
+	spin_unlock_irq(lock);
 
 	vfree(oldmem);
 
 	return 0;
 }
 
+static int dvb_dvr_get_buffer_status(struct dmxdev *dmxdev,
+				unsigned int f_flags,
+				struct dmx_buffer_status *dmx_buffer_status)
+{
+	struct dvb_ringbuffer *buf;
+	spinlock_t *lock;
+
+	if ((f_flags & O_ACCMODE) == O_RDONLY) {
+		buf = &dmxdev->dvr_buffer;
+		lock = &dmxdev->lock;
+	} else {
+		buf = &dmxdev->dvr_input_buffer;
+		lock = &dmxdev->dvr_in_lock;
+	}
+
+	spin_lock_irq(lock);
+
+	dmx_buffer_status->error = buf->error;
+	if (buf->error)
+		dvb_ringbuffer_flush(buf);
+
+	dmx_buffer_status->fullness = dvb_ringbuffer_avail(buf);
+	dmx_buffer_status->free_bytes = dvb_ringbuffer_free(buf);
+	dmx_buffer_status->read_offset = buf->pread;
+	dmx_buffer_status->write_offset = buf->pwrite;
+	dmx_buffer_status->size = buf->size;
+
+	spin_unlock_irq(lock);
+
+	return 0;
+}
+
+static int dvb_dvr_release_data(struct dmxdev *dmxdev,
+					unsigned int f_flags,
+					u32 bytes_count)
+{
+	ssize_t buff_fullness;
+
+	if (!(f_flags & O_ACCMODE) == O_RDONLY)
+		return -EINVAL;
+
+	if (!bytes_count)
+		return 0;
+
+	buff_fullness = dvb_ringbuffer_avail(&dmxdev->dvr_buffer);
+
+	if (bytes_count > buff_fullness)
+		return -EINVAL;
+
+	DVB_RINGBUFFER_SKIP(&dmxdev->dvr_buffer, bytes_count);
+	wake_up_all(&dmxdev->dvr_buffer.queue);
+	return 0;
+}
+
+static int dvb_dvr_feed_data(struct dmxdev *dmxdev,
+					unsigned int f_flags,
+					u32 bytes_count)
+{
+	ssize_t free_space;
+	struct dvb_ringbuffer *buffer = &dmxdev->dvr_input_buffer;
+
+	if ((f_flags & O_ACCMODE) == O_RDONLY)
+		return -EINVAL;
+
+	if (!bytes_count)
+		return 0;
+
+	free_space = dvb_ringbuffer_free(buffer);
+
+	if (bytes_count > free_space)
+		return -EINVAL;
+
+	buffer->pwrite =
+		(buffer->pwrite + bytes_count) % buffer->size;
+
+	wake_up_all(&buffer->queue);
+
+	if (!work_pending(&dmxdev->dvr_input_work))
+		queue_work(dmxdev->dvr_input_workqueue,
+					&dmxdev->dvr_input_work);
+
+	return 0;
+}
+
 static inline void dvb_dmxdev_filter_state_set(struct dmxdev_filter
 					       *dmxdevfilter, int state)
 {
@@ -309,7 +663,7 @@
 	if (dmxdevfilter->state >= DMXDEV_STATE_GO)
 		return -EBUSY;
 
-	newmem = vmalloc(size);
+	newmem = vmalloc_user(size);
 	if (!newmem)
 		return -ENOMEM;
 
@@ -324,6 +678,212 @@
 	spin_unlock_irq(&dmxdevfilter->dev->lock);
 
 	vfree(oldmem);
+	return 0;
+}
+
+static int dvb_dmxdev_set_pes_buffer_size(struct dmxdev_filter *dmxdevfilter,
+					unsigned long size)
+{
+	if (dmxdevfilter->pes_buffer_size == size)
+		return 0;
+	if (!size)
+		return -EINVAL;
+	if (dmxdevfilter->state >= DMXDEV_STATE_GO)
+		return -EBUSY;
+
+	dmxdevfilter->pes_buffer_size = size;
+
+	return 0;
+}
+
+static int dvb_dmxdev_set_source(struct dmxdev_filter *dmxdevfilter,
+					dmx_source_t *source)
+{
+	struct dmxdev *dev;
+
+	if (dmxdevfilter->state == DMXDEV_STATE_GO)
+		return -EBUSY;
+
+	dev = dmxdevfilter->dev;
+
+	dev->source = *source;
+
+	if (dev->demux->set_source)
+		return dev->demux->set_source(dev->demux, source);
+
+	return 0;
+}
+
+static int dvb_dmxdev_ts_fullness_callback(
+				struct dmx_ts_feed *filter,
+				int required_space)
+{
+	struct dmxdev_filter *dmxdevfilter = filter->priv;
+	struct dvb_ringbuffer *src;
+	int ret;
+
+	if (dmxdevfilter->params.pes.output == DMX_OUT_TAP
+		|| dmxdevfilter->params.pes.output == DMX_OUT_TSDEMUX_TAP)
+		src = &dmxdevfilter->buffer;
+	else
+		src = &dmxdevfilter->dev->dvr_buffer;
+
+	do {
+		ret = 0;
+
+		if (dmxdevfilter->dev->dvr_in_exit)
+			return -ENODEV;
+
+		spin_lock(&dmxdevfilter->dev->lock);
+
+		if ((!src->data) ||
+			(dmxdevfilter->state != DMXDEV_STATE_GO))
+			ret = -EINVAL;
+		else if (src->error)
+			ret = src->error;
+
+		if (ret) {
+			spin_unlock(&dmxdevfilter->dev->lock);
+			return ret;
+		}
+
+		if (required_space <= dvb_ringbuffer_free(src)) {
+			spin_unlock(&dmxdevfilter->dev->lock);
+			return 0;
+		}
+
+		spin_unlock(&dmxdevfilter->dev->lock);
+
+		ret = wait_event_interruptible(src->queue,
+				(!src->data) ||
+				(dvb_ringbuffer_free(src) >= required_space) ||
+				(src->error != 0) ||
+				(dmxdevfilter->state != DMXDEV_STATE_GO) ||
+				dmxdevfilter->dev->dvr_in_exit);
+
+		if (ret < 0)
+			return ret;
+	} while (1);
+}
+
+static int dvb_dmxdev_sec_fullness_callback(
+				struct dmx_section_filter *filter,
+				int required_space)
+{
+	struct dmxdev_filter *dmxdevfilter = filter->priv;
+	struct dvb_ringbuffer *src = &dmxdevfilter->buffer;
+	int ret;
+
+	do {
+		ret = 0;
+
+		if (dmxdevfilter->dev->dvr_in_exit)
+			return -ENODEV;
+
+		spin_lock(&dmxdevfilter->dev->lock);
+
+		if ((!src->data) ||
+			(dmxdevfilter->state != DMXDEV_STATE_GO))
+			ret = -EINVAL;
+		else if (src->error)
+			ret = src->error;
+
+		if (ret) {
+			spin_unlock(&dmxdevfilter->dev->lock);
+			return ret;
+		}
+
+		if (required_space <= dvb_ringbuffer_free(src)) {
+			spin_unlock(&dmxdevfilter->dev->lock);
+			return 0;
+		}
+
+		spin_unlock(&dmxdevfilter->dev->lock);
+
+		ret = wait_event_interruptible(src->queue,
+				(!src->data) ||
+				(dvb_ringbuffer_free(src) >= required_space) ||
+				(src->error != 0) ||
+				(dmxdevfilter->state != DMXDEV_STATE_GO) ||
+				dmxdevfilter->dev->dvr_in_exit);
+
+		if (ret < 0)
+			return ret;
+	} while (1);
+}
+
+static int dvb_dmxdev_set_playback_mode(struct dmxdev_filter *dmxdevfilter,
+					enum dmx_playback_mode_t playback_mode)
+{
+	struct dmxdev *dmxdev = dmxdevfilter->dev;
+
+	if ((playback_mode != DMX_PB_MODE_PUSH) &&
+		(playback_mode != DMX_PB_MODE_PULL))
+		return -EINVAL;
+
+	if (((dmxdev->source < DMX_SOURCE_DVR0) ||
+		!dmxdev->demux->set_playback_mode ||
+		!(dmxdev->capabilities & DMXDEV_CAP_PULL_MODE)) &&
+		(playback_mode == DMX_PB_MODE_PULL))
+		return -EPERM;
+
+	if (dmxdevfilter->state == DMXDEV_STATE_GO)
+		return -EBUSY;
+
+	dmxdev->playback_mode = playback_mode;
+
+	return dmxdev->demux->set_playback_mode(
+				dmxdev->demux,
+				dmxdev->playback_mode,
+				dvb_dmxdev_ts_fullness_callback,
+				dvb_dmxdev_sec_fullness_callback);
+}
+
+static int dvb_dmxdev_get_buffer_status(
+		struct dmxdev_filter *dmxdevfilter,
+		struct dmx_buffer_status *dmx_buffer_status)
+{
+	struct dvb_ringbuffer *buf = &dmxdevfilter->buffer;
+
+	if (!buf->data)
+		return -EINVAL;
+
+	spin_lock_irq(&dmxdevfilter->dev->lock);
+
+	dmx_buffer_status->error = buf->error;
+	if (buf->error)
+		dvb_ringbuffer_flush(buf);
+
+	dmx_buffer_status->fullness = dvb_ringbuffer_avail(buf);
+	dmx_buffer_status->free_bytes = dvb_ringbuffer_free(buf);
+	dmx_buffer_status->read_offset = buf->pread;
+	dmx_buffer_status->write_offset = buf->pwrite;
+	dmx_buffer_status->size = buf->size;
+
+	spin_unlock_irq(&dmxdevfilter->dev->lock);
+
+	return 0;
+}
+
+static int dvb_dmxdev_release_data(struct dmxdev_filter *dmxdevfilter,
+					u32 bytes_count)
+{
+	ssize_t buff_fullness;
+
+	if (!dmxdevfilter->buffer.data)
+		return -EINVAL;
+
+	if (!bytes_count)
+		return 0;
+
+	buff_fullness = dvb_ringbuffer_avail(&dmxdevfilter->buffer);
+
+	if (bytes_count > buff_fullness)
+		return -EINVAL;
+
+	DVB_RINGBUFFER_SKIP(&dmxdevfilter->buffer, bytes_count);
+
+	wake_up_all(&dmxdevfilter->buffer.queue);
 
 	return 0;
 }
@@ -336,7 +896,7 @@
 	spin_lock_irq(&dmxdevfilter->dev->lock);
 	dmxdevfilter->state = DMXDEV_STATE_TIMEDOUT;
 	spin_unlock_irq(&dmxdevfilter->dev->lock);
-	wake_up(&dmxdevfilter->buffer.queue);
+	wake_up_all(&dmxdevfilter->buffer.queue);
 }
 
 static void dvb_dmxdev_filter_timer(struct dmxdev_filter *dmxdevfilter)
@@ -362,7 +922,7 @@
 	int ret;
 
 	if (dmxdevfilter->buffer.error) {
-		wake_up(&dmxdevfilter->buffer.queue);
+		wake_up_all(&dmxdevfilter->buffer.queue);
 		return 0;
 	}
 	spin_lock(&dmxdevfilter->dev->lock);
@@ -387,7 +947,7 @@
 	if (dmxdevfilter->params.sec.flags & DMX_ONESHOT)
 		dmxdevfilter->state = DMXDEV_STATE_DONE;
 	spin_unlock(&dmxdevfilter->dev->lock);
-	wake_up(&dmxdevfilter->buffer.queue);
+	wake_up_all(&dmxdevfilter->buffer.queue);
 	return 0;
 }
 
@@ -402,18 +962,34 @@
 
 	spin_lock(&dmxdevfilter->dev->lock);
 	if (dmxdevfilter->params.pes.output == DMX_OUT_DECODER) {
-		spin_unlock(&dmxdevfilter->dev->lock);
-		return 0;
-	}
-
-	if (dmxdevfilter->params.pes.output == DMX_OUT_TAP
+		if ((dmxdevfilter->dev->capabilities &
+			DMXDEV_CAP_PCR_EXTRACTION) &&
+			((dmxdevfilter->params.pes.pes_type == DMX_PES_PCR0) ||
+			(dmxdevfilter->params.pes.pes_type == DMX_PES_PCR1) ||
+			(dmxdevfilter->params.pes.pes_type == DMX_PES_PCR2) ||
+			(dmxdevfilter->params.pes.pes_type == DMX_PES_PCR3))) {
+			/*
+			 * Support for reporting PCR and STC pairs to user.
+			 * Reported data should have the following format:
+			 * <8 bit flags><64 bits of STC> <64bits of PCR>
+			 * STC and PCR values are in 27MHz.
+			 * The current flags that are defined:
+			 * 0x00000001: discontinuity_indicator
+			 */
+			buffer = &dmxdevfilter->buffer;
+		} else {
+			spin_unlock(&dmxdevfilter->dev->lock);
+			return 0;
+		}
+	} else if (dmxdevfilter->params.pes.output == DMX_OUT_TAP
 	    || dmxdevfilter->params.pes.output == DMX_OUT_TSDEMUX_TAP)
 		buffer = &dmxdevfilter->buffer;
 	else
 		buffer = &dmxdevfilter->dev->dvr_buffer;
+
 	if (buffer->error) {
 		spin_unlock(&dmxdevfilter->dev->lock);
-		wake_up(&buffer->queue);
+		wake_up_all(&buffer->queue);
 		return 0;
 	}
 	ret = dvb_dmxdev_buffer_write(buffer, buffer1, buffer1_len);
@@ -424,7 +1000,7 @@
 		buffer->error = ret;
 	}
 	spin_unlock(&dmxdevfilter->dev->lock);
-	wake_up(&buffer->queue);
+	wake_up_all(&buffer->queue);
 	return 0;
 }
 
@@ -534,6 +1110,8 @@
 	}
 
 	dvb_ringbuffer_flush(&dmxdevfilter->buffer);
+	wake_up_all(&dmxdevfilter->buffer.queue);
+
 	return 0;
 }
 
@@ -600,7 +1178,9 @@
 	tsfeed = feed->ts;
 	tsfeed->priv = filter;
 
-	ret = tsfeed->set(tsfeed, feed->pid, ts_type, ts_pes, 32768, timeout);
+	ret = tsfeed->set(tsfeed, feed->pid,
+					ts_type, ts_pes,
+					filter->pes_buffer_size, timeout);
 	if (ret < 0) {
 		dmxdev->demux->release_ts_feed(dmxdev->demux, tsfeed);
 		return ret;
@@ -629,7 +1209,7 @@
 		dvb_dmxdev_filter_stop(filter);
 
 	if (!filter->buffer.data) {
-		mem = vmalloc(filter->buffer.size);
+		mem = vmalloc_user(filter->buffer.size);
 		if (!mem)
 			return -ENOMEM;
 		spin_lock_irq(&filter->dev->lock);
@@ -649,7 +1229,6 @@
 		*secfilter = NULL;
 		*secfeed = NULL;
 
-
 		/* find active filter/feed with same PID */
 		for (i = 0; i < dmxdev->filternum; i++) {
 			if (dmxdev->filter[i].state >= DMXDEV_STATE_GO &&
@@ -763,6 +1342,8 @@
 	dvb_dmxdev_filter_state_set(dmxdevfilter, DMXDEV_STATE_ALLOCATED);
 	init_timer(&dmxdevfilter->timer);
 
+	dmxdevfilter->pes_buffer_size = 32768;
+
 	dvbdev->users++;
 
 	mutex_unlock(&dmxdev->mutex);
@@ -788,7 +1369,7 @@
 	}
 
 	dvb_dmxdev_filter_state_set(dmxdevfilter, DMXDEV_STATE_FREE);
-	wake_up(&dmxdevfilter->buffer.queue);
+	wake_up_all(&dmxdevfilter->buffer.queue);
 	mutex_unlock(&dmxdevfilter->mutex);
 	mutex_unlock(&dmxdev->mutex);
 	return 0;
@@ -1023,6 +1604,24 @@
 		mutex_unlock(&dmxdevfilter->mutex);
 		break;
 
+	case DMX_GET_BUFFER_STATUS:
+		if (mutex_lock_interruptible(&dmxdevfilter->mutex)) {
+			mutex_unlock(&dmxdev->mutex);
+			return -ERESTARTSYS;
+		}
+		ret = dvb_dmxdev_get_buffer_status(dmxdevfilter, parg);
+		mutex_unlock(&dmxdevfilter->mutex);
+		break;
+
+	case DMX_RELEASE_DATA:
+		if (mutex_lock_interruptible(&dmxdevfilter->mutex)) {
+			mutex_unlock(&dmxdev->mutex);
+			return -ERESTARTSYS;
+		}
+		ret = dvb_dmxdev_release_data(dmxdevfilter, arg);
+		mutex_unlock(&dmxdevfilter->mutex);
+		break;
+
 	case DMX_GET_PES_PIDS:
 		if (!dmxdev->demux->get_pes_pids) {
 			ret = -EINVAL;
@@ -1040,11 +1639,59 @@
 		break;
 
 	case DMX_SET_SOURCE:
-		if (!dmxdev->demux->set_source) {
+		if (mutex_lock_interruptible(&dmxdevfilter->mutex)) {
+			mutex_unlock(&dmxdev->mutex);
+			return -ERESTARTSYS;
+		}
+		ret = dvb_dmxdev_set_source(dmxdevfilter, parg);
+		mutex_unlock(&dmxdevfilter->mutex);
+		break;
+
+	case DMX_SET_TS_PACKET_FORMAT:
+		if (!dmxdev->demux->set_tsp_format) {
 			ret = -EINVAL;
 			break;
 		}
-		ret = dmxdev->demux->set_source(dmxdev->demux, parg);
+
+		if (dmxdevfilter->state >= DMXDEV_STATE_GO) {
+			ret = -EBUSY;
+			break;
+		}
+		ret = dmxdev->demux->set_tsp_format(
+				dmxdev->demux,
+				*(enum dmx_tsp_format_t *)parg);
+		break;
+
+	case DMX_SET_TS_OUT_FORMAT:
+		if (!dmxdev->demux->set_tsp_out_format) {
+			ret = -EINVAL;
+			break;
+		}
+
+		if (dmxdevfilter->state >= DMXDEV_STATE_GO) {
+			ret = -EBUSY;
+			break;
+		}
+
+		ret = dmxdev->demux->set_tsp_out_format(
+				dmxdev->demux,
+				*(enum dmx_tsp_format_t *)parg);
+		break;
+
+	case DMX_SET_DECODER_BUFFER_SIZE:
+		if (mutex_lock_interruptible(&dmxdevfilter->mutex)) {
+			mutex_unlock(&dmxdev->mutex);
+			return -ERESTARTSYS;
+		}
+
+		ret = dvb_dmxdev_set_pes_buffer_size(dmxdevfilter, arg);
+		mutex_unlock(&dmxdevfilter->mutex);
+		break;
+
+	case DMX_SET_PLAYBACK_MODE:
+		ret = dvb_dmxdev_set_playback_mode(
+				dmxdevfilter,
+				*(enum dmx_playback_mode_t *)parg);
 		break;
 
 	case DMX_GET_STC:
@@ -1114,6 +1761,59 @@
 	return mask;
 }
 
+static int dvb_demux_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+	struct dmxdev_filter *dmxdevfilter = filp->private_data;
+	struct dmxdev *dmxdev = dmxdevfilter->dev;
+	int ret;
+	int vma_size;
+	int buffer_size;
+
+	vma_size = vma->vm_end - vma->vm_start;
+
+	if (vma->vm_flags & VM_WRITE)
+		return -EINVAL;
+
+	if (mutex_lock_interruptible(&dmxdev->mutex))
+		return -ERESTARTSYS;
+
+	if (mutex_lock_interruptible(&dmxdevfilter->mutex)) {
+		mutex_unlock(&dmxdev->mutex);
+		return -ERESTARTSYS;
+	}
+
+	if (!dmxdevfilter->buffer.data) {
+		mutex_unlock(&dmxdevfilter->mutex);
+		mutex_unlock(&dmxdev->mutex);
+		return -EINVAL;
+	}
+
+	/* Make sure requested mapping is not larger than buffer size */
+	buffer_size = dmxdevfilter->buffer.size + (PAGE_SIZE-1);
+	buffer_size = buffer_size & ~(PAGE_SIZE-1);
+
+	if (vma_size != buffer_size) {
+		mutex_unlock(&dmxdevfilter->mutex);
+		mutex_unlock(&dmxdev->mutex);
+		return -EINVAL;
+	}
+
+	ret = remap_vmalloc_range(vma, dmxdevfilter->buffer.data, 0);
+	if (ret) {
+		mutex_unlock(&dmxdevfilter->mutex);
+		mutex_unlock(&dmxdev->mutex);
+		return ret;
+	}
+
+	vma->vm_flags |= VM_RESERVED;
+	vma->vm_flags |= VM_DONTEXPAND;
+
+	mutex_unlock(&dmxdevfilter->mutex);
+	mutex_unlock(&dmxdev->mutex);
+
+	return 0;
+}
+
 static int dvb_demux_release(struct inode *inode, struct file *file)
 {
 	struct dmxdev_filter *dmxdevfilter = file->private_data;
@@ -1144,6 +1844,7 @@
 	.release = dvb_demux_release,
 	.poll = dvb_demux_poll,
 	.llseek = default_llseek,
+	.mmap = dvb_demux_mmap,
 };
 
 static struct dvb_device dvbdev_demux = {
@@ -1166,7 +1867,19 @@
 
 	switch (cmd) {
 	case DMX_SET_BUFFER_SIZE:
-		ret = dvb_dvr_set_buffer_size(dmxdev, arg);
+		ret = dvb_dvr_set_buffer_size(dmxdev, file->f_flags, arg);
+		break;
+
+	case DMX_GET_BUFFER_STATUS:
+		ret = dvb_dvr_get_buffer_status(dmxdev, file->f_flags, parg);
+		break;
+
+	case DMX_RELEASE_DATA:
+		ret = dvb_dvr_release_data(dmxdev, file->f_flags, arg);
+		break;
+
+	case DMX_FEED_DATA:
+		ret = dvb_dvr_feed_data(dmxdev, file->f_flags, arg);
 		break;
 
 	default:
@@ -1191,16 +1904,22 @@
 
 	dprintk("function : %s\n", __func__);
 
-	poll_wait(file, &dmxdev->dvr_buffer.queue, wait);
-
 	if ((file->f_flags & O_ACCMODE) == O_RDONLY) {
+		poll_wait(file, &dmxdev->dvr_buffer.queue, wait);
+
 		if (dmxdev->dvr_buffer.error)
 			mask |= (POLLIN | POLLRDNORM | POLLPRI | POLLERR);
 
 		if (!dvb_ringbuffer_empty(&dmxdev->dvr_buffer))
 			mask |= (POLLIN | POLLRDNORM | POLLPRI);
-	} else
-		mask |= (POLLOUT | POLLWRNORM | POLLPRI);
+	} else {
+		poll_wait(file, &dmxdev->dvr_input_buffer.queue, wait);
+		if (dmxdev->dvr_input_buffer.error)
+			mask |= (POLLOUT | POLLRDNORM | POLLPRI | POLLERR);
+
+		if (dvb_ringbuffer_free(&dmxdev->dvr_input_buffer))
+			mask |= (POLLOUT | POLLRDNORM | POLLPRI);
+	}
 
 	return mask;
 }
@@ -1209,6 +1928,7 @@
 	.owner = THIS_MODULE,
 	.read = dvb_dvr_read,
 	.write = dvb_dvr_write,
+	.mmap = dvb_dvr_mmap,
 	.unlocked_ioctl = dvb_dvr_ioctl,
 	.open = dvb_dvr_open,
 	.release = dvb_dvr_release,
@@ -1234,8 +1954,19 @@
 	if (!dmxdev->filter)
 		return -ENOMEM;
 
+	dmxdev->dvr_input_workqueue =
+		create_singlethread_workqueue("dvr_workqueue");
+
+	if (dmxdev->dvr_input_workqueue == NULL) {
+		vfree(dmxdev->filter);
+		return -ENOMEM;
+	}
+
+	dmxdev->playback_mode = DMX_PB_MODE_PUSH;
+
 	mutex_init(&dmxdev->mutex);
 	spin_lock_init(&dmxdev->lock);
+	spin_lock_init(&dmxdev->dvr_in_lock);
 	for (i = 0; i < dmxdev->filternum; i++) {
 		dmxdev->filter[i].dev = dmxdev;
 		dmxdev->filter[i].buffer.data = NULL;
@@ -1249,6 +1980,10 @@
 			    dmxdev, DVB_DEVICE_DVR);
 
 	dvb_ringbuffer_init(&dmxdev->dvr_buffer, NULL, 8192);
+	dvb_ringbuffer_init(&dmxdev->dvr_input_buffer, NULL, 8192);
+
+	INIT_WORK(&dmxdev->dvr_input_work,
+			  dvr_input_work_func);
 
 	return 0;
 }
@@ -1267,6 +2002,9 @@
 				dmxdev->dvr_dvbdev->users==1);
 	}
 
+	flush_workqueue(dmxdev->dvr_input_workqueue);
+	destroy_workqueue(dmxdev->dvr_input_workqueue);
+
 	dvb_unregister_device(dmxdev->dvbdev);
 	dvb_unregister_device(dmxdev->dvr_dvbdev);
 
diff --git a/drivers/media/dvb/dvb-core/dmxdev.h b/drivers/media/dvb/dvb-core/dmxdev.h
index 02ebe28..82f8f6d 100644
--- a/drivers/media/dvb/dvb-core/dmxdev.h
+++ b/drivers/media/dvb/dvb-core/dmxdev.h
@@ -4,6 +4,8 @@
  * Copyright (C) 2000 Ralph Metzler & Marcus Metzler
  *                    for convergence integrated media GmbH
  *
+ * 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 Lesser General Public License
  * as published by the Free Software Foundation; either version 2.1
@@ -32,7 +34,7 @@
 #include <linux/string.h>
 #include <linux/mutex.h>
 #include <linux/slab.h>
-
+#include <linux/workqueue.h>
 #include <linux/dvb/dmx.h>
 
 #include "dvbdev.h"
@@ -83,14 +85,18 @@
 
 	struct mutex mutex;
 
+	/* relevent for decoder PES */
+	unsigned long pes_buffer_size;
+
 	/* only for sections */
 	struct timer_list timer;
 	int todo;
 	u8 secheader[3];
 };
 
-
 struct dmxdev {
+	struct work_struct dvr_input_work;
+
 	struct dvb_device *dvbdev;
 	struct dvb_device *dvr_dvbdev;
 
@@ -99,16 +105,28 @@
 
 	int filternum;
 	int capabilities;
+#define DMXDEV_CAP_DUPLEX			0x1
+#define DMXDEV_CAP_PULL_MODE		0x2
+#define DMXDEV_CAP_PCR_EXTRACTION	0x4
+
+	enum dmx_playback_mode_t playback_mode;
+	dmx_source_t source;
 
 	unsigned int exit:1;
-#define DMXDEV_CAP_DUPLEX 1
+	unsigned int dvr_in_exit:1;
+	unsigned int dvr_processing_input:1;
+
 	struct dmx_frontend *dvr_orig_fe;
 
 	struct dvb_ringbuffer dvr_buffer;
+	struct dvb_ringbuffer dvr_input_buffer;
+	struct workqueue_struct *dvr_input_workqueue;
+
 #define DVR_BUFFER_SIZE (10*188*1024)
 
 	struct mutex mutex;
 	spinlock_t lock;
+	spinlock_t dvr_in_lock;
 };
 
 
diff --git a/drivers/media/dvb/dvb-core/dvb_demux.c b/drivers/media/dvb/dvb-core/dvb_demux.c
index faa3671..966b48d 100644
--- a/drivers/media/dvb/dvb-core/dvb_demux.c
+++ b/drivers/media/dvb/dvb-core/dvb_demux.c
@@ -5,6 +5,8 @@
  *		       & Marcus Metzler <marcus@convergence.de>
  *			 for convergence integrated media GmbH
  *
+ * 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 Lesser General Public License
  * as published by the Free Software Foundation; either version 2.1
@@ -50,6 +52,14 @@
 MODULE_PARM_DESC(dvb_demux_speedcheck,
 		"enable transport stream speed check");
 
+/* counter advancing for each new dvb-demux device */
+static int dvb_demux_index;
+
+static int dvb_demux_performancecheck;
+module_param(dvb_demux_performancecheck, int, 0644);
+MODULE_PARM_DESC(dvb_demux_performancecheck,
+		"enable transport stream performance check, reported through debugfs");
+
 #define dprintk_tscheck(x...) do {                              \
 		if (dvb_demux_tscheck && printk_ratelimit())    \
 			printk(x);                              \
@@ -95,6 +105,19 @@
 	memcpy(d, s, len);
 }
 
+static u32 dvb_dmx_calc_time_delta(struct timespec past_time)
+{
+	struct timespec curr_time, delta_time;
+	u64 delta_time_us;
+
+	curr_time = current_kernel_time();
+	delta_time = timespec_sub(curr_time, past_time);
+	delta_time_us = ((s64)delta_time.tv_sec * USEC_PER_SEC) +
+					delta_time.tv_nsec / 1000;
+
+	return (u32)delta_time_us;
+}
+
 /******************************************************************************
  * Software filter functions
  ******************************************************************************/
@@ -120,8 +143,14 @@
 		printk("missed packet!\n");
 	*/
 
-	if (buf[1] & 0x40)	// PUSI ?
+	/* PUSI ? */
+	if (buf[1] & 0x40) {
 		feed->peslen = 0xfffa;
+		feed->pusi_seen = 1;
+	}
+
+	if (feed->pusi_seen == 0)
+		return 0;
 
 	feed->peslen += count;
 
@@ -164,10 +193,23 @@
 		return 0;
 
 	if (sec->check_crc) {
+		struct timespec pre_crc_time;
+
+		if (dvb_demux_performancecheck)
+			pre_crc_time = current_kernel_time();
+
 		section_syntax_indicator = ((sec->secbuf[1] & 0x80) != 0);
 		if (section_syntax_indicator &&
-		    demux->check_crc32(feed, sec->secbuf, sec->seclen))
+		    demux->check_crc32(feed, sec->secbuf, sec->seclen)) {
+			if (dvb_demux_performancecheck)
+				demux->total_crc_time +=
+					dvb_dmx_calc_time_delta(pre_crc_time);
 			return -1;
+		}
+
+		if (dvb_demux_performancecheck)
+			demux->total_crc_time +=
+				dvb_dmx_calc_time_delta(pre_crc_time);
 	}
 
 	do {
@@ -351,6 +393,161 @@
 	return 0;
 }
 
+static inline void dvb_dmx_swfilter_output_packet(
+	struct dvb_demux_feed *feed,
+	const u8 *buf)
+{
+	u8 time_stamp[4] = {0};
+	struct dvb_demux *demux = feed->demux;
+
+	/*
+	 * if we output 192 packet with timestamp at head of packet,
+	 * output the timestamp now before the 188 TS packet
+	 */
+	if (demux->tsp_out_format == DMX_TSP_FORMAT_192_HEAD)
+		feed->cb.ts(time_stamp, 4, NULL, 0, &feed->feed.ts, DMX_OK);
+
+	feed->cb.ts(buf, 188, NULL, 0, &feed->feed.ts, DMX_OK);
+
+	/*
+	 * if we output 192 packet with timestamp at tail of packet,
+	 * output the timestamp now after the 188 TS packet
+	 */
+	if (demux->tsp_out_format == DMX_TSP_FORMAT_192_TAIL)
+		feed->cb.ts(time_stamp, 4, NULL, 0, &feed->feed.ts, DMX_OK);
+}
+
+static inline void dvb_dmx_configure_decoder_fullness(
+						struct dvb_demux *demux,
+						int initialize)
+{
+	struct dvb_demux_feed *feed;
+	int j;
+
+	for (j = 0; j < demux->feednum; j++) {
+		feed = &demux->feed[j];
+
+		if ((feed->state != DMX_STATE_GO) ||
+			(feed->type != DMX_TYPE_TS) ||
+			!(feed->ts_type & TS_DECODER))
+			continue;
+
+		if (initialize) {
+			if (demux->decoder_fullness_init)
+				demux->decoder_fullness_init(feed);
+		} else {
+			if (demux->decoder_fullness_abort)
+				demux->decoder_fullness_abort(feed);
+		}
+	}
+}
+
+static inline int dvb_dmx_swfilter_buffer_check(
+					struct dvb_demux *demux,
+					u16 pid)
+{
+	int desired_space;
+	int ret;
+	struct dmx_ts_feed *ts;
+	struct dvb_demux_filter *f;
+	struct dvb_demux_feed *feed;
+	int was_locked;
+	int i, j;
+
+	if (likely(spin_is_locked(&demux->lock)))
+		was_locked = 1;
+	else
+		was_locked = 0;
+
+	/*
+	 * Check that there's enough free space for data output.
+	 * If there no space, wait for it (block).
+	 * Since this function is called while spinlock
+	 * is aquired, the lock should be released first.
+	 * Once we get control back, lock is aquired back
+	 * and checks that the filter is still valid.
+	 */
+	for (j = 0; j < demux->feednum; j++) {
+		feed = &demux->feed[j];
+
+		if (demux->sw_filter_abort)
+			return -ENODEV;
+
+		if ((feed->state != DMX_STATE_GO) ||
+			((feed->pid != pid) && (feed->pid != 0x2000)))
+			continue;
+
+		if (feed->type == DMX_TYPE_TS) {
+			desired_space = 192; /* upper bound */
+			ts = &feed->feed.ts;
+
+			if (feed->ts_type & TS_PACKET) {
+				if (likely(was_locked))
+					spin_unlock(&demux->lock);
+
+				ret = demux->buffer_ctrl.ts(ts, desired_space);
+
+				if (likely(was_locked))
+					spin_lock(&demux->lock);
+
+				if (ret < 0)
+					continue;
+			}
+
+			if (demux->sw_filter_abort)
+				return -ENODEV;
+
+			if (!ts->is_filtering)
+				continue;
+
+			if ((feed->ts_type & TS_DECODER) &&
+				(demux->decoder_fullness_wait)) {
+				if (likely(was_locked))
+					spin_unlock(&demux->lock);
+
+				ret = demux->decoder_fullness_wait(
+								feed,
+								desired_space);
+
+				if (likely(was_locked))
+					spin_lock(&demux->lock);
+
+				if (ret < 0)
+					continue;
+			}
+
+			continue;
+		}
+
+		/* else - section case */
+		desired_space = feed->feed.sec.tsfeedp + 188; /* upper bound */
+		for (i = 0; i < demux->filternum; i++) {
+			if (demux->sw_filter_abort)
+				return -EPERM;
+
+			if (!feed->feed.sec.is_filtering)
+				continue;
+
+			f = &demux->filter[i];
+			if (f->feed != feed)
+				continue;
+
+			if (likely(was_locked))
+				spin_unlock(&demux->lock);
+
+			ret = demux->buffer_ctrl.sec(&f->filter, desired_space);
+
+			if (likely(was_locked))
+				spin_lock(&demux->lock);
+
+			if (ret < 0)
+				break;
+		}
+	}
+
+	return 0;
+}
+
 static inline void dvb_dmx_swfilter_packet_type(struct dvb_demux_feed *feed,
 						const u8 *buf)
 {
@@ -362,8 +559,7 @@
 			if (feed->ts_type & TS_PAYLOAD_ONLY)
 				dvb_dmx_swfilter_payload(feed, buf);
 			else
-				feed->cb.ts(buf, 188, NULL, 0, &feed->feed.ts,
-					    DMX_OK);
+				dvb_dmx_swfilter_output_packet(feed, buf);
 		}
 		if (feed->ts_type & TS_DECODER)
 			if (feed->demux->write_to_decoder)
@@ -446,6 +642,10 @@
 		/* end check */
 	};
 
+	if (demux->playback_mode == DMX_PB_MODE_PULL)
+		if (dvb_dmx_swfilter_buffer_check(demux, pid) < 0)
+			return;
+
 	list_for_each_entry(feed, &demux->feed_list, list_head) {
 		if ((feed->pid != pid) && (feed->pid != 0x2000))
 			continue;
@@ -457,16 +657,25 @@
 
 		if (feed->pid == pid)
 			dvb_dmx_swfilter_packet_type(feed, buf);
-		else if (feed->pid == 0x2000)
-			feed->cb.ts(buf, 188, NULL, 0, &feed->feed.ts, DMX_OK);
+		else if ((feed->pid == 0x2000) &&
+			     (feed->feed.ts.is_filtering))
+			dvb_dmx_swfilter_output_packet(feed, buf);
 	}
 }
 
 void dvb_dmx_swfilter_packets(struct dvb_demux *demux, const u8 *buf,
 			      size_t count)
 {
+	struct timespec pre_time;
+
+	if (dvb_demux_performancecheck)
+		pre_time = current_kernel_time();
+
 	spin_lock(&demux->lock);
 
+	demux->sw_filter_abort = 0;
+	dvb_dmx_configure_decoder_fullness(demux, 1);
+
 	while (count--) {
 		if (buf[0] == 0x47)
 			dvb_dmx_swfilter_packet(demux, buf);
@@ -474,18 +683,24 @@
 	}
 
 	spin_unlock(&demux->lock);
+
+	if (dvb_demux_performancecheck)
+		demux->total_process_time += dvb_dmx_calc_time_delta(pre_time);
 }
 
 EXPORT_SYMBOL(dvb_dmx_swfilter_packets);
 
 static inline int find_next_packet(const u8 *buf, int pos, size_t count,
-				   const int pktsize)
+				   const int pktsize, const int leadingbytes)
 {
 	int start = pos, lost;
 
 	while (pos < count) {
-		if (buf[pos] == 0x47 ||
-		    (pktsize == 204 && buf[pos] == 0xB8))
+		if ((buf[pos] == 0x47 && !leadingbytes) ||
+		    (pktsize == 204 && buf[pos] == 0xB8) ||
+			(pktsize == 192 && leadingbytes &&
+			 (pos+leadingbytes < count) &&
+				buf[pos+leadingbytes] == 0x47))
 			break;
 		pos++;
 	}
@@ -495,7 +710,9 @@
 		/* This garbage is part of a valid packet? */
 		int backtrack = pos - pktsize;
 		if (backtrack >= 0 && (buf[backtrack] == 0x47 ||
-		    (pktsize == 204 && buf[backtrack] == 0xB8)))
+		    (pktsize == 204 && buf[backtrack] == 0xB8) ||
+			(pktsize == 192 &&
+			buf[backtrack+leadingbytes] == 0x47)))
 			return backtrack;
 	}
 
@@ -504,13 +721,20 @@
 
 /* Filter all pktsize= 188 or 204 sized packets and skip garbage. */
 static inline void _dvb_dmx_swfilter(struct dvb_demux *demux, const u8 *buf,
-		size_t count, const int pktsize)
+		size_t count, const int pktsize, const int leadingbytes)
 {
 	int p = 0, i, j;
 	const u8 *q;
+	struct timespec pre_time;
+
+	if (dvb_demux_performancecheck)
+		pre_time = current_kernel_time();
 
 	spin_lock(&demux->lock);
 
+	demux->sw_filter_abort = 0;
+	dvb_dmx_configure_decoder_fullness(demux, 1);
+
 	if (demux->tsbufp) { /* tsbuf[0] is now 0x47. */
 		i = demux->tsbufp;
 		j = pktsize - i;
@@ -520,14 +744,22 @@
 			goto bailout;
 		}
 		memcpy(&demux->tsbuf[i], buf, j);
-		if (demux->tsbuf[0] == 0x47) /* double check */
+		if (pktsize == 192 &&
+			leadingbytes &&
+			demux->tsbuf[leadingbytes] == 0x47)  /* double check */
+			dvb_dmx_swfilter_packet(demux, demux->tsbuf+4);
+		else if (demux->tsbuf[0] == 0x47) /* double check */
 			dvb_dmx_swfilter_packet(demux, demux->tsbuf);
 		demux->tsbufp = 0;
 		p += j;
 	}
 
 	while (1) {
-		p = find_next_packet(buf, p, count, pktsize);
+		p = find_next_packet(buf, p, count, pktsize, leadingbytes);
+
+		if (demux->sw_filter_abort)
+			goto bailout;
+
 		if (p >= count)
 			break;
 		if (count - p < pktsize)
@@ -540,6 +772,10 @@
 			demux->tsbuf[0] = 0x47;
 			q = demux->tsbuf;
 		}
+
+		if (pktsize == 192 && leadingbytes)
+			q = &buf[p+leadingbytes];
+
 		dvb_dmx_swfilter_packet(demux, q);
 		p += pktsize;
 	}
@@ -554,20 +790,55 @@
 
 bailout:
 	spin_unlock(&demux->lock);
+
+	if (dvb_demux_performancecheck)
+		demux->total_process_time += dvb_dmx_calc_time_delta(pre_time);
 }
 
 void dvb_dmx_swfilter(struct dvb_demux *demux, const u8 *buf, size_t count)
 {
-	_dvb_dmx_swfilter(demux, buf, count, 188);
+	_dvb_dmx_swfilter(demux, buf, count, 188, 0);
 }
 EXPORT_SYMBOL(dvb_dmx_swfilter);
 
 void dvb_dmx_swfilter_204(struct dvb_demux *demux, const u8 *buf, size_t count)
 {
-	_dvb_dmx_swfilter(demux, buf, count, 204);
+	_dvb_dmx_swfilter(demux, buf, count, 204, 0);
 }
 EXPORT_SYMBOL(dvb_dmx_swfilter_204);
 
+void dvb_dmx_swfilter_format(
+			struct dvb_demux *demux,
+			const u8 *buf,
+			size_t count,
+			enum dmx_tsp_format_t tsp_format)
+{
+	switch (tsp_format) {
+	case DMX_TSP_FORMAT_188:
+		_dvb_dmx_swfilter(demux, buf, count, 188, 0);
+		break;
+
+	case DMX_TSP_FORMAT_192_TAIL:
+		_dvb_dmx_swfilter(demux, buf, count, 192, 0);
+		break;
+
+	case DMX_TSP_FORMAT_192_HEAD:
+		_dvb_dmx_swfilter(demux, buf, count, 192, 4);
+		break;
+
+	case DMX_TSP_FORMAT_204:
+		_dvb_dmx_swfilter(demux, buf, count, 204, 0);
+		break;
+
+	default:
+		printk(KERN_ERR "%s: invalid TS packet format (format=%d)\n",
+			   __func__,
+			   tsp_format);
+		break;
+	}
+}
+EXPORT_SYMBOL(dvb_dmx_swfilter_format);
+
 static struct dvb_demux_filter *dvb_dmx_filter_alloc(struct dvb_demux *demux)
 {
 	int i;
@@ -778,6 +1049,13 @@
 	feed->peslen = 0xfffa;
 	feed->buffer = NULL;
 
+	/* default behaviour - pass first PES data even if it is
+	 * partial PES data from previous PES that we didn't receive its header.
+	 * Override this to 0 in your start_feed function in order to handle
+	 * first PES differently.
+	 */
+	feed->pusi_seen = 1;
+
 	(*ts_feed) = &feed->feed.ts;
 	(*ts_feed)->parent = dmx;
 	(*ts_feed)->priv = NULL;
@@ -1119,30 +1397,54 @@
 	return 0;
 }
 
-static int dvbdmx_write(struct dmx_demux *demux, const char __user *buf, size_t count)
+static int dvbdmx_write(struct dmx_demux *demux, const char *buf, size_t count)
 {
 	struct dvb_demux *dvbdemux = (struct dvb_demux *)demux;
-	void *p;
 
 	if ((!demux->frontend) || (demux->frontend->source != DMX_MEMORY_FE))
 		return -EINVAL;
 
-	p = memdup_user(buf, count);
-	if (IS_ERR(p))
-		return PTR_ERR(p);
-	if (mutex_lock_interruptible(&dvbdemux->mutex)) {
-		kfree(p);
-		return -ERESTARTSYS;
-	}
-	dvb_dmx_swfilter(dvbdemux, p, count);
-	kfree(p);
-	mutex_unlock(&dvbdemux->mutex);
+	dvb_dmx_swfilter_format(dvbdemux, buf, count, dvbdemux->tsp_format);
 
 	if (signal_pending(current))
 		return -EINTR;
 	return count;
 }
 
+static int dvbdmx_write_cancel(struct dmx_demux *demux)
+{
+	struct dvb_demux *dvbdmx = (struct dvb_demux *)demux;
+
+	spin_lock_irq(&dvbdmx->lock);
+
+	/* cancel any pending wait for decoder's buffers */
+	dvbdmx->sw_filter_abort = 1;
+	dvbdmx->tsbufp = 0;
+	dvb_dmx_configure_decoder_fullness(dvbdmx, 0);
+
+	spin_unlock_irq(&dvbdmx->lock);
+
+	return 0;
+}
+
+static int dvbdmx_set_playback_mode(struct dmx_demux *demux,
+				 enum dmx_playback_mode_t mode,
+				 dmx_ts_fullness ts_fullness_callback,
+				 dmx_section_fullness sec_fullness_callback)
+{
+	struct dvb_demux *dvbdmx = (struct dvb_demux *)demux;
+
+	mutex_lock(&dvbdmx->mutex);
+
+	dvbdmx->playback_mode = mode;
+	dvbdmx->buffer_ctrl.ts = ts_fullness_callback;
+	dvbdmx->buffer_ctrl.sec = sec_fullness_callback;
+
+	mutex_unlock(&dvbdmx->mutex);
+
+	return 0;
+}
+
 static int dvbdmx_add_frontend(struct dmx_demux *demux,
 			       struct dmx_frontend *frontend)
 {
@@ -1214,6 +1516,40 @@
 	return 0;
 }
 
+static int dvbdmx_set_tsp_format(
+	struct dmx_demux *demux,
+	enum dmx_tsp_format_t tsp_format)
+{
+	struct dvb_demux *dvbdemux = (struct dvb_demux *)demux;
+
+	if ((tsp_format > DMX_TSP_FORMAT_204) ||
+		(tsp_format < DMX_TSP_FORMAT_188))
+		return -EINVAL;
+
+	mutex_lock(&dvbdemux->mutex);
+
+	dvbdemux->tsp_format = tsp_format;
+	mutex_unlock(&dvbdemux->mutex);
+	return 0;
+}
+
+static int dvbdmx_set_tsp_out_format(
+	struct dmx_demux *demux,
+	enum dmx_tsp_format_t tsp_format)
+{
+	struct dvb_demux *dvbdemux = (struct dvb_demux *)demux;
+
+	if ((tsp_format > DMX_TSP_FORMAT_192_HEAD) ||
+		(tsp_format < DMX_TSP_FORMAT_188))
+		return -EINVAL;
+
+	mutex_lock(&dvbdemux->mutex);
+
+	dvbdemux->tsp_out_format = tsp_format;
+	mutex_unlock(&dvbdemux->mutex);
+	return 0;
+}
+
 int dvb_dmx_init(struct dvb_demux *dvbdemux)
 {
 	int i;
@@ -1232,6 +1568,30 @@
 		dvbdemux->filter = NULL;
 		return -ENOMEM;
 	}
+
+	dvbdemux->total_process_time = 0;
+	dvbdemux->total_crc_time = 0;
+	snprintf(dvbdemux->alias,
+			MAX_DVB_DEMUX_NAME_LEN,
+			"demux%d",
+			dvb_demux_index++);
+
+	dvbdemux->debugfs_demux_dir = debugfs_create_dir(dvbdemux->alias, NULL);
+
+	if (dvbdemux->debugfs_demux_dir != NULL) {
+		debugfs_create_u32(
+			"total_processing_time",
+			S_IRUGO|S_IWUGO,
+			dvbdemux->debugfs_demux_dir,
+			&dvbdemux->total_process_time);
+
+		debugfs_create_u32(
+			"total_crc_time",
+			S_IRUGO|S_IWUGO,
+			dvbdemux->debugfs_demux_dir,
+			&dvbdemux->total_crc_time);
+	}
+
 	for (i = 0; i < dvbdemux->filternum; i++) {
 		dvbdemux->filter[i].state = DMX_STATE_FREE;
 		dvbdemux->filter[i].index = i;
@@ -1258,6 +1618,9 @@
 	dvbdemux->recording = 0;
 	dvbdemux->tsbufp = 0;
 
+	dvbdemux->tsp_format = DMX_TSP_FORMAT_188;
+	dvbdemux->tsp_out_format = DMX_TSP_FORMAT_188;
+
 	if (!dvbdemux->check_crc32)
 		dvbdemux->check_crc32 = dvb_dmx_crc32;
 
@@ -1269,6 +1632,8 @@
 	dmx->open = dvbdmx_open;
 	dmx->close = dvbdmx_close;
 	dmx->write = dvbdmx_write;
+	dmx->write_cancel = dvbdmx_write_cancel;
+	dmx->set_playback_mode = dvbdmx_set_playback_mode;
 	dmx->allocate_ts_feed = dvbdmx_allocate_ts_feed;
 	dmx->release_ts_feed = dvbdmx_release_ts_feed;
 	dmx->allocate_section_feed = dvbdmx_allocate_section_feed;
@@ -1281,6 +1646,9 @@
 	dmx->disconnect_frontend = dvbdmx_disconnect_frontend;
 	dmx->get_pes_pids = dvbdmx_get_pes_pids;
 
+	dmx->set_tsp_format = dvbdmx_set_tsp_format;
+	dmx->set_tsp_out_format = dvbdmx_set_tsp_out_format;
+
 	mutex_init(&dvbdemux->mutex);
 	spin_lock_init(&dvbdemux->lock);
 
@@ -1291,6 +1659,9 @@
 
 void dvb_dmx_release(struct dvb_demux *dvbdemux)
 {
+	if (dvbdemux->debugfs_demux_dir != NULL)
+		debugfs_remove_recursive(dvbdemux->debugfs_demux_dir);
+
 	vfree(dvbdemux->cnt_storage);
 	vfree(dvbdemux->filter);
 	vfree(dvbdemux->feed);
diff --git a/drivers/media/dvb/dvb-core/dvb_demux.h b/drivers/media/dvb/dvb-core/dvb_demux.h
index a7d876f..297f3df 100644
--- a/drivers/media/dvb/dvb-core/dvb_demux.h
+++ b/drivers/media/dvb/dvb-core/dvb_demux.h
@@ -4,6 +4,8 @@
  * Copyright (C) 2000-2001 Marcus Metzler & Ralph Metzler
  *                         for convergence integrated media GmbH
  *
+ * 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 Lesser General Public License
  * as published by the Free Software Foundation; either version 2.1
@@ -27,6 +29,7 @@
 #include <linux/timer.h>
 #include <linux/spinlock.h>
 #include <linux/mutex.h>
+#include <linux/debugfs.h>
 
 #include "demux.h"
 
@@ -107,6 +110,10 @@
 	int (*stop_feed)(struct dvb_demux_feed *feed);
 	int (*write_to_decoder)(struct dvb_demux_feed *feed,
 				 const u8 *buf, size_t len);
+	int (*decoder_fullness_init)(struct dvb_demux_feed *feed);
+	int (*decoder_fullness_wait)(struct dvb_demux_feed *feed,
+				 size_t required_space);
+	int (*decoder_fullness_abort)(struct dvb_demux_feed *feed);
 	u32 (*check_crc32)(struct dvb_demux_feed *feed,
 			    const u8 *buf, size_t len);
 	void (*memcopy)(struct dvb_demux_feed *feed, u8 *dst,
@@ -136,6 +143,28 @@
 
 	struct timespec speed_last_time; /* for TS speed check */
 	uint32_t speed_pkts_cnt; /* for TS speed check */
+
+	enum dmx_tsp_format_t tsp_format;
+	enum dmx_tsp_format_t tsp_out_format;
+
+	enum dmx_playback_mode_t playback_mode;
+	int sw_filter_abort;
+
+	struct {
+		dmx_ts_fullness ts;
+		dmx_section_fullness sec;
+	} buffer_ctrl;
+
+	/*
+	 * the following is used for debugfs exposing info
+	 * about dvb demux performance.
+	 */
+#define MAX_DVB_DEMUX_NAME_LEN 10
+	char alias[MAX_DVB_DEMUX_NAME_LEN];
+
+	u32 total_process_time;
+	u32 total_crc_time;
+	struct dentry *debugfs_demux_dir;
 };
 
 int dvb_dmx_init(struct dvb_demux *dvbdemux);
@@ -145,5 +174,10 @@
 void dvb_dmx_swfilter(struct dvb_demux *demux, const u8 *buf, size_t count);
 void dvb_dmx_swfilter_204(struct dvb_demux *demux, const u8 *buf,
 			  size_t count);
+void dvb_dmx_swfilter_format(
+			struct dvb_demux *demux, const u8 *buf,
+			size_t count,
+			enum dmx_tsp_format_t tsp_format);
+
 
 #endif /* _DVB_DEMUX_H_ */
diff --git a/drivers/media/dvb/dvb-core/dvb_ringbuffer.c b/drivers/media/dvb/dvb-core/dvb_ringbuffer.c
index a5712cd..36cc475 100644
--- a/drivers/media/dvb/dvb-core/dvb_ringbuffer.c
+++ b/drivers/media/dvb/dvb-core/dvb_ringbuffer.c
@@ -5,6 +5,8 @@
  * Copyright (C) 2003 Oliver Endriss
  * Copyright (C) 2004 Andrew de Quincey
  *
+ * Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
  * based on code originally found in av7110.c & dvb_ci.c:
  * Copyright (C) 1999-2003 Ralph  Metzler
  *                       & Marcus Metzler for convergence integrated media GmbH
@@ -37,6 +39,8 @@
 
 #define PKT_READY 0
 #define PKT_DISPOSED 1
+#define PKT_PENDING 2
+
 
 
 void dvb_ringbuffer_init(struct dvb_ringbuffer *rbuf, void *data, size_t len)
@@ -166,6 +170,35 @@
 	return len;
 }
 
+ssize_t dvb_ringbuffer_write_user(struct dvb_ringbuffer *rbuf,
+					const u8 *buf, size_t len)
+{
+	size_t todo = len;
+	size_t split;
+	ssize_t oldpwrite = rbuf->pwrite;
+
+	split = (rbuf->pwrite + len > rbuf->size) ?
+			rbuf->size - rbuf->pwrite :
+			0;
+
+	if (split > 0) {
+		if (copy_from_user(rbuf->data + rbuf->pwrite, buf, split))
+			return -EFAULT;
+		buf += split;
+		todo -= split;
+		rbuf->pwrite = 0;
+	}
+
+	if (copy_from_user(rbuf->data + rbuf->pwrite, buf, todo)) {
+		rbuf->pwrite = oldpwrite;
+		return -EFAULT;
+	}
+
+	rbuf->pwrite = (rbuf->pwrite + todo) % rbuf->size;
+
+	return len;
+}
+
 ssize_t dvb_ringbuffer_pkt_write(struct dvb_ringbuffer *rbuf, u8* buf, size_t len)
 {
 	int status;
@@ -180,6 +213,31 @@
 	return status;
 }
 
+ssize_t dvb_ringbuffer_pkt_start(struct dvb_ringbuffer *rbuf, size_t len)
+{
+	ssize_t oldpwrite = rbuf->pwrite;
+
+	DVB_RINGBUFFER_WRITE_BYTE(rbuf, len >> 8);
+	DVB_RINGBUFFER_WRITE_BYTE(rbuf, len & 0xff);
+	DVB_RINGBUFFER_WRITE_BYTE(rbuf, PKT_PENDING);
+
+	return oldpwrite;
+}
+EXPORT_SYMBOL(dvb_ringbuffer_pkt_start);
+
+int dvb_ringbuffer_pkt_close(struct dvb_ringbuffer *rbuf, ssize_t idx)
+{
+	idx = (idx + 2) % rbuf->size;
+
+	if (rbuf->data[idx] != PKT_PENDING)
+		return -EINVAL;
+
+	rbuf->data[idx] = PKT_READY;
+
+	return 0;
+}
+EXPORT_SYMBOL(dvb_ringbuffer_pkt_close);
+
 ssize_t dvb_ringbuffer_pkt_read_user(struct dvb_ringbuffer *rbuf, size_t idx,
 				int offset, u8 __user *buf, size_t len)
 {
@@ -187,6 +245,9 @@
 	size_t split;
 	size_t pktlen;
 
+	if (DVB_RINGBUFFER_PEEK(rbuf, (idx+2)) != PKT_READY)
+		return -EINVAL;
+
 	pktlen = rbuf->data[idx] << 8;
 	pktlen |= rbuf->data[(idx + 1) % rbuf->size];
 	if (offset > pktlen) return -EINVAL;
@@ -207,6 +268,7 @@
 
 	return len;
 }
+EXPORT_SYMBOL(dvb_ringbuffer_pkt_read_user);
 
 ssize_t dvb_ringbuffer_pkt_read(struct dvb_ringbuffer *rbuf, size_t idx,
 				int offset, u8* buf, size_t len)
@@ -215,9 +277,13 @@
 	size_t split;
 	size_t pktlen;
 
+	if (rbuf->data[(idx + 2) % rbuf->size] != PKT_READY)
+		return -EINVAL;
+
 	pktlen = rbuf->data[idx] << 8;
 	pktlen |= rbuf->data[(idx + 1) % rbuf->size];
 	if (offset > pktlen) return -EINVAL;
+
 	if ((offset + len) > pktlen) len = pktlen - offset;
 
 	idx = (idx + DVB_RINGBUFFER_PKTHDRSIZE + offset) % rbuf->size;
@@ -232,6 +298,7 @@
 	memcpy(buf, rbuf->data+idx, todo);
 	return len;
 }
+EXPORT_SYMBOL(dvb_ringbuffer_pkt_read);
 
 void dvb_ringbuffer_pkt_dispose(struct dvb_ringbuffer *rbuf, size_t idx)
 {
@@ -251,6 +318,7 @@
 		}
 	}
 }
+EXPORT_SYMBOL(dvb_ringbuffer_pkt_dispose);
 
 ssize_t dvb_ringbuffer_pkt_next(struct dvb_ringbuffer *rbuf, size_t idx, size_t* pktlen)
 {
@@ -279,6 +347,9 @@
 			return idx;
 		}
 
+		if (curpktstatus == PKT_PENDING)
+			return -EFAULT;
+
 		consumed += curpktlen + DVB_RINGBUFFER_PKTHDRSIZE;
 		idx = (idx + curpktlen + DVB_RINGBUFFER_PKTHDRSIZE) % rbuf->size;
 	}
@@ -286,8 +357,7 @@
 	// no packets available
 	return -1;
 }
-
-
+EXPORT_SYMBOL(dvb_ringbuffer_pkt_next);
 
 EXPORT_SYMBOL(dvb_ringbuffer_init);
 EXPORT_SYMBOL(dvb_ringbuffer_empty);
@@ -297,3 +367,5 @@
 EXPORT_SYMBOL(dvb_ringbuffer_read_user);
 EXPORT_SYMBOL(dvb_ringbuffer_read);
 EXPORT_SYMBOL(dvb_ringbuffer_write);
+EXPORT_SYMBOL(dvb_ringbuffer_write_user);
+
diff --git a/drivers/media/dvb/dvb-core/dvb_ringbuffer.h b/drivers/media/dvb/dvb-core/dvb_ringbuffer.h
index 41f04da..8b591a6 100644
--- a/drivers/media/dvb/dvb-core/dvb_ringbuffer.h
+++ b/drivers/media/dvb/dvb-core/dvb_ringbuffer.h
@@ -5,6 +5,8 @@
  * Copyright (C) 2003 Oliver Endriss
  * Copyright (C) 2004 Andrew de Quincey
  *
+ * Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
  * based on code originally found in av7110.c & dvb_ci.c:
  * Copyright (C) 1999-2003 Ralph Metzler & Marcus Metzler
  *                         for convergence integrated media GmbH
@@ -134,6 +136,8 @@
 extern ssize_t dvb_ringbuffer_write(struct dvb_ringbuffer *rbuf, const u8 *buf,
 				    size_t len);
 
+extern ssize_t dvb_ringbuffer_write_user(struct dvb_ringbuffer *rbuf,
+					const u8 *buf, size_t len);
 
 /**
  * Write a packet into the ringbuffer.
@@ -183,4 +187,30 @@
 extern ssize_t dvb_ringbuffer_pkt_next(struct dvb_ringbuffer *rbuf, size_t idx, size_t* pktlen);
 
 
+/**
+ * Start a new packet that will be written directly by the user to the packet buffer.
+ * The function only writes the header of the packet into the packet buffer,
+ * and the packet is in pending state (can't be read by the reader) until it is
+ * closed using dvb_ringbuffer_pkt_close. You must write the data into the
+ * packet buffer using dvb_ringbuffer_write followed by
+ * dvb_ringbuffer_pkt_close.
+ *
+ * <rbuf> Ringbuffer concerned.
+ * <len> Size of the packet's data
+ * returns Index of the packet's header that was started.
+ */
+extern ssize_t dvb_ringbuffer_pkt_start(struct dvb_ringbuffer *rbuf,
+						size_t len);
+
+/**
+ * Close a packet that was started using dvb_ringbuffer_pkt_start.
+ * The packet will be marked as ready to be ready.
+ *
+ * <rbuf> Ringbuffer concerned.
+ * <idx> Packet index that was returned by dvb_ringbuffer_pkt_start
+ * returns error status, -EINVAL if the provided index is invalid
+ */
+extern int dvb_ringbuffer_pkt_close(struct dvb_ringbuffer *rbuf, ssize_t idx);
+
+
 #endif /* _DVB_RINGBUFFER_H_ */
diff --git a/drivers/media/dvb/mpq/Kconfig b/drivers/media/dvb/mpq/Kconfig
new file mode 100644
index 0000000..868ad8c
--- /dev/null
+++ b/drivers/media/dvb/mpq/Kconfig
@@ -0,0 +1,12 @@
+config DVB_MPQ
+	tristate "Qualcomm Multimedia Processor DVB Adapter"
+	depends on ARCH_MSM && DVB_CORE
+	default n
+
+	help
+	  Support for Qualcomm MPQ based DVB adapter.
+	  Say Y or M if you own such a device and want to use it.
+
+source "drivers/media/dvb/mpq/demux/Kconfig"
+
+
diff --git a/drivers/media/dvb/mpq/Makefile b/drivers/media/dvb/mpq/Makefile
new file mode 100644
index 0000000..7ccf13e
--- /dev/null
+++ b/drivers/media/dvb/mpq/Makefile
@@ -0,0 +1,5 @@
+
+obj-$(CONFIG_DVB_MPQ)	    += adapter/
+obj-$(CONFIG_DVB_MPQ_DEMUX) += demux/
+
+
diff --git a/drivers/media/dvb/mpq/adapter/Makefile b/drivers/media/dvb/mpq/adapter/Makefile
new file mode 100644
index 0000000..ed664da
--- /dev/null
+++ b/drivers/media/dvb/mpq/adapter/Makefile
@@ -0,0 +1,8 @@
+
+EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core/
+EXTRA_CFLAGS += -Idrivers/media/dvb/mpq/include/
+
+obj-$(CONFIG_DVB_MPQ) += mpq-adapter.o
+
+mpq-adapter-y := mpq_adapter.o mpq_stream_buffer.o
+
diff --git a/drivers/media/dvb/mpq/adapter/mpq_adapter.c b/drivers/media/dvb/mpq/adapter/mpq_adapter.c
new file mode 100644
index 0000000..9664f04
--- /dev/null
+++ b/drivers/media/dvb/mpq/adapter/mpq_adapter.c
@@ -0,0 +1,212 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+
+#include "mpq_adapter.h"
+#include "mpq_dvb_debug.h"
+
+
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
+/* data-structure holding MPQ adapter information */
+static struct
+{
+	/* MPQ adapter registered to dvb-core */
+	struct dvb_adapter adapter;
+
+	/* mutex protect against the data-structure */
+	struct mutex mutex;
+
+	/* List of stream interfaces registered to the MPQ adapter */
+	struct {
+		/* pointer to the stream buffer using for data tunneling */
+		struct mpq_streambuffer *stream_buffer;
+
+		/* callback triggered when the stream interface is registered */
+		mpq_adapter_stream_if_callback callback;
+
+		/* parameter passed to the callback function */
+		void *user_param;
+	} interfaces[MPQ_ADAPTER_MAX_NUM_OF_INTERFACES];
+} mpq_info;
+
+
+/**
+ * Initialize MPQ DVB adapter module.
+ *
+ * Return     error status
+ */
+static int __init mpq_adapter_init(void)
+{
+	int i;
+	int result;
+
+	MPQ_DVB_DBG_PRINT("%s executed\n", __func__);
+
+	mutex_init(&mpq_info.mutex);
+
+	/* reset stream interfaces list */
+	for (i = 0; i < MPQ_ADAPTER_MAX_NUM_OF_INTERFACES; i++) {
+		mpq_info.interfaces[i].stream_buffer = NULL;
+		mpq_info.interfaces[i].callback = NULL;
+	}
+
+	/* regsiter a new dvb-adapter to dvb-core */
+	result = dvb_register_adapter(&mpq_info.adapter,
+								  "Qualcomm DVB adapter",
+								  THIS_MODULE,
+								  NULL,
+								  adapter_nr);
+
+	if (result < 0) {
+		MPQ_DVB_ERR_PRINT(
+			"%s: dvb_register_adapter failed, errno %d\n",
+			__func__,
+			result);
+	}
+
+	return result;
+}
+
+
+/**
+ * Cleanup MPQ DVB adapter module.
+ */
+static void __exit mpq_adapter_exit(void)
+{
+	MPQ_DVB_DBG_PRINT("%s executed\n", __func__);
+
+	/* un-regsiter adapter from dvb-core */
+	dvb_unregister_adapter(&mpq_info.adapter);
+	mutex_destroy(&mpq_info.mutex);
+}
+
+struct dvb_adapter *mpq_adapter_get(void)
+{
+	return &mpq_info.adapter;
+}
+EXPORT_SYMBOL(mpq_adapter_get);
+
+
+int mpq_adapter_register_stream_if(
+		enum mpq_adapter_stream_if interface_id,
+		struct mpq_streambuffer *stream_buffer)
+{
+	int ret;
+
+	if (interface_id >= MPQ_ADAPTER_MAX_NUM_OF_INTERFACES) {
+		ret = -EINVAL;
+		goto register_failed;
+	}
+
+	if (mutex_lock_interruptible(&mpq_info.mutex)) {
+		ret = -ERESTARTSYS;
+		goto register_failed;
+	}
+
+	if (mpq_info.interfaces[interface_id].stream_buffer != NULL) {
+		/* already registered interface */
+		ret = -EINVAL;
+		goto register_failed_unlock_mutex;
+	}
+
+	mpq_info.interfaces[interface_id].stream_buffer = stream_buffer;
+	mutex_unlock(&mpq_info.mutex);
+
+	/*
+	 * If callback is installed, trigger it to notify that
+	 * stream interface was registered.
+	 */
+	if (mpq_info.interfaces[interface_id].callback != NULL) {
+		mpq_info.interfaces[interface_id].callback(
+				interface_id,
+				mpq_info.interfaces[interface_id].user_param);
+	}
+
+	return 0;
+
+register_failed_unlock_mutex:
+	mutex_unlock(&mpq_info.mutex);
+register_failed:
+	return ret;
+}
+EXPORT_SYMBOL(mpq_adapter_register_stream_if);
+
+
+int mpq_adapter_unregister_stream_if(
+		enum mpq_adapter_stream_if interface_id)
+{
+	if (interface_id >= MPQ_ADAPTER_MAX_NUM_OF_INTERFACES)
+		return -EINVAL;
+
+	if (mutex_lock_interruptible(&mpq_info.mutex))
+		return -ERESTARTSYS;
+
+	/* clear the registered interface */
+	mpq_info.interfaces[interface_id].stream_buffer = NULL;
+
+	mutex_unlock(&mpq_info.mutex);
+
+	return 0;
+}
+EXPORT_SYMBOL(mpq_adapter_unregister_stream_if);
+
+
+int mpq_adapter_get_stream_if(
+		enum mpq_adapter_stream_if interface_id,
+		struct mpq_streambuffer **stream_buffer)
+{
+	if ((interface_id >= MPQ_ADAPTER_MAX_NUM_OF_INTERFACES) ||
+		(stream_buffer == NULL))
+		return -EINVAL;
+
+	if (mutex_lock_interruptible(&mpq_info.mutex))
+		return -ERESTARTSYS;
+
+	*stream_buffer = mpq_info.interfaces[interface_id].stream_buffer;
+
+	mutex_unlock(&mpq_info.mutex);
+
+	return 0;
+}
+EXPORT_SYMBOL(mpq_adapter_get_stream_if);
+
+
+int mpq_adapter_notify_stream_if(
+		enum mpq_adapter_stream_if interface_id,
+		mpq_adapter_stream_if_callback callback,
+		void *user_param)
+{
+	if (interface_id >= MPQ_ADAPTER_MAX_NUM_OF_INTERFACES)
+		return -EINVAL;
+
+	if (mutex_lock_interruptible(&mpq_info.mutex))
+		return -ERESTARTSYS;
+
+	mpq_info.interfaces[interface_id].callback = callback;
+	mpq_info.interfaces[interface_id].user_param = user_param;
+
+	mutex_unlock(&mpq_info.mutex);
+
+	return 0;
+}
+EXPORT_SYMBOL(mpq_adapter_notify_stream_if);
+
+
+module_init(mpq_adapter_init);
+module_exit(mpq_adapter_exit);
+
+MODULE_DESCRIPTION("Qualcomm MPQ adapter");
+MODULE_LICENSE("GPL v2");
+
diff --git a/drivers/media/dvb/mpq/adapter/mpq_stream_buffer.c b/drivers/media/dvb/mpq/adapter/mpq_stream_buffer.c
new file mode 100644
index 0000000..738d730
--- /dev/null
+++ b/drivers/media/dvb/mpq/adapter/mpq_stream_buffer.c
@@ -0,0 +1,224 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include "mpq_dvb_debug.h"
+#include "mpq_stream_buffer.h"
+
+
+void mpq_streambuffer_init(
+		struct mpq_streambuffer *sbuff,
+		void *data_buff, size_t data_buff_len,
+		void *packet_buff, size_t packet_buff_size)
+{
+	dvb_ringbuffer_init(&sbuff->raw_data, data_buff, data_buff_len);
+	dvb_ringbuffer_init(&sbuff->packet_data, packet_buff, packet_buff_size);
+}
+EXPORT_SYMBOL(mpq_streambuffer_init);
+
+
+ssize_t mpq_streambuffer_pkt_next(
+		struct mpq_streambuffer *sbuff,
+		ssize_t idx, size_t *pktlen)
+{
+	return dvb_ringbuffer_pkt_next(&sbuff->packet_data, idx, pktlen);
+}
+EXPORT_SYMBOL(mpq_streambuffer_pkt_next);
+
+
+ssize_t mpq_streambuffer_pkt_read(
+		struct mpq_streambuffer *sbuff,
+		size_t idx,
+		struct mpq_streambuffer_packet_header *packet,
+		u8 *user_data)
+{
+	size_t ret;
+	size_t read_len;
+
+	/* read-out the packet header first */
+	ret = dvb_ringbuffer_pkt_read(
+				&sbuff->packet_data, idx, 0,
+				(u8 *)packet,
+				sizeof(struct mpq_streambuffer_packet_header));
+
+	/* verify length, at least packet header should exist */
+	if (ret != sizeof(struct mpq_streambuffer_packet_header))
+		return -EINVAL;
+
+	read_len = ret;
+
+	/* read-out private user-data if there are such */
+	if ((packet->user_data_len) && (user_data != NULL)) {
+		ret = dvb_ringbuffer_pkt_read(
+				&sbuff->packet_data,
+				idx,
+				sizeof(struct mpq_streambuffer_packet_header),
+				user_data,
+				packet->user_data_len);
+
+		if (ret < 0)
+			return ret;
+
+		read_len += ret;
+	}
+
+	return read_len;
+}
+EXPORT_SYMBOL(mpq_streambuffer_pkt_read);
+
+
+int mpq_streambuffer_pkt_dispose(
+			struct mpq_streambuffer *sbuff,
+			size_t idx,
+			int dispose_data)
+{
+	int ret;
+	struct mpq_streambuffer_packet_header packet;
+
+	if (dispose_data) {
+		/* read-out the packet header first */
+		ret = dvb_ringbuffer_pkt_read(
+				&sbuff->packet_data,
+				idx,
+				0,
+				(u8 *)&packet,
+				sizeof(struct mpq_streambuffer_packet_header));
+
+		if (ret != sizeof(struct mpq_streambuffer_packet_header))
+			return -EINVAL;
+
+		/* Advance the read pointer in the raw-data buffer first */
+		ret = mpq_streambuffer_data_read_dispose(
+							sbuff,
+							packet.raw_data_len);
+		if (ret != 0)
+			return ret;
+	}
+
+	/* Now clear the packet from the packet header */
+	dvb_ringbuffer_pkt_dispose(&sbuff->packet_data, idx);
+
+	return 0;
+}
+EXPORT_SYMBOL(mpq_streambuffer_pkt_dispose);
+
+
+int mpq_streambuffer_pkt_write(
+			struct mpq_streambuffer *sbuff,
+			struct mpq_streambuffer_packet_header *packet,
+			u8 *user_data)
+{
+	ssize_t idx;
+	size_t len;
+
+	len =
+		sizeof(struct mpq_streambuffer_packet_header) +
+		packet->user_data_len;
+
+	/* Make sure enough space available for packet header */
+	if (dvb_ringbuffer_free(&sbuff->packet_data) < len)
+		return -ENOSPC;
+
+	/* Starting writting packet header */
+	idx = dvb_ringbuffer_pkt_start(&sbuff->packet_data, len);
+
+	/* Write non-user private data header */
+	dvb_ringbuffer_write(
+				&sbuff->packet_data,
+				(u8 *)packet,
+				sizeof(struct mpq_streambuffer_packet_header));
+
+	/* Write user's own private data header */
+	dvb_ringbuffer_write(&sbuff->packet_data,
+						 user_data,
+						 packet->user_data_len);
+
+	dvb_ringbuffer_pkt_close(&sbuff->packet_data, idx);
+
+	wake_up_all(&sbuff->packet_data.queue);
+
+	return 0;
+}
+EXPORT_SYMBOL(mpq_streambuffer_pkt_write);
+
+
+ssize_t mpq_streambuffer_data_write(
+			struct mpq_streambuffer *sbuff,
+			const u8 *buf, size_t len)
+{
+	int res;
+
+	if (unlikely(dvb_ringbuffer_free(&sbuff->raw_data) < len))
+		return -ENOSPC;
+
+	res = dvb_ringbuffer_write(&sbuff->raw_data, buf, len);
+	wake_up_all(&sbuff->raw_data.queue);
+
+	return res;
+}
+EXPORT_SYMBOL(mpq_streambuffer_data_write);
+
+
+int mpq_streambuffer_data_write_deposit(
+				struct mpq_streambuffer *sbuff,
+				size_t len)
+{
+	if (unlikely(dvb_ringbuffer_free(&sbuff->raw_data) < len))
+		return -ENOSPC;
+
+	sbuff->raw_data.pwrite =
+		(sbuff->raw_data.pwrite+len) % sbuff->raw_data.size;
+
+	wake_up_all(&sbuff->raw_data.queue);
+
+	return 0;
+}
+EXPORT_SYMBOL(mpq_streambuffer_data_write_deposit);
+
+
+size_t mpq_streambuffer_data_read(
+				struct mpq_streambuffer *sbuff,
+				u8 *buf, size_t len)
+{
+	int actual_len;
+
+	actual_len = dvb_ringbuffer_avail(&sbuff->raw_data);
+	if (actual_len < len)
+		len = actual_len;
+
+	if (actual_len)
+		dvb_ringbuffer_read(&sbuff->raw_data, buf, actual_len);
+
+	wake_up_all(&sbuff->raw_data.queue);
+
+	return actual_len;
+}
+EXPORT_SYMBOL(mpq_streambuffer_data_read);
+
+
+int mpq_streambuffer_data_read_dispose(
+			struct mpq_streambuffer *sbuff,
+			size_t len)
+{
+	if (unlikely(dvb_ringbuffer_avail(&sbuff->raw_data) < len))
+		return -EINVAL;
+
+	DVB_RINGBUFFER_SKIP(&sbuff->raw_data, len);
+
+	wake_up_all(&sbuff->raw_data.queue);
+	return 0;
+}
+EXPORT_SYMBOL(mpq_streambuffer_data_read_dispose);
+
diff --git a/drivers/media/dvb/mpq/demux/Kconfig b/drivers/media/dvb/mpq/demux/Kconfig
new file mode 100644
index 0000000..34961c2
--- /dev/null
+++ b/drivers/media/dvb/mpq/demux/Kconfig
@@ -0,0 +1,45 @@
+config DVB_MPQ_DEMUX
+	tristate "DVB Demux Device"
+	depends on DVB_MPQ && ION && ION_MSM
+	default n
+
+	help
+	  Support for Qualcomm based dvb demux device.
+	  Say Y or M if you own such a device and want to use it.
+
+config DVB_MPQ_NUM_DMX_DEVICES
+	int "Number of demux devices"
+	depends on DVB_MPQ_DEMUX
+	default 4
+	range 1 255
+
+	help
+	  Configure number of demux devices. Depends on your use-cases for maximum concurrent stream playback.
+
+choice
+	prompt "Demux Hardware Plugin"
+	depends on DVB_MPQ_DEMUX
+	default DVB_MPQ_TSIF
+	help
+	  Enable support of specific demux HW plugin depending on the existing HW support.
+	  Depending on the enabled HW, demux may take advantage of HW capbailities to perform some tasks in HW instead of SW.
+
+	config DVB_MPQ_TSPP1
+		bool "TSPPv1 plugin"
+		depends on TSPP
+		help
+			Use this option of your HW has Transport Stream Packet Processor (TSPP) version1 support
+
+	config DVB_MPQ_TSPP2
+		bool "TSPPv2 plugin"
+		depends on TSPP
+		help
+			Use this option of your HW has Transport Stream Packet Processor (TSPP) version2 support
+
+	config DVB_MPQ_TSIF
+		bool "TSIF plugin"
+		depends on TSIF
+		help
+			Use this option of your HW has only TSIF input without any Transport Stream Packet Processor (TSPP) support
+endchoice
+
diff --git a/drivers/media/dvb/mpq/demux/Makefile b/drivers/media/dvb/mpq/demux/Makefile
new file mode 100644
index 0000000..b9310c3
--- /dev/null
+++ b/drivers/media/dvb/mpq/demux/Makefile
@@ -0,0 +1,14 @@
+
+EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core/
+EXTRA_CFLAGS += -Idrivers/media/dvb/mpq/include/
+
+obj-$(CONFIG_DVB_MPQ_DEMUX) += mpq-dmx-hw-plugin.o
+
+mpq-dmx-hw-plugin-y := mpq_dmx_plugin_common.o
+
+mpq-dmx-hw-plugin-$(CONFIG_DVB_MPQ_TSPP1) += mpq_dmx_plugin_tspp_v1.o
+
+mpq-dmx-hw-plugin-$(CONFIG_DVB_MPQ_TSPP2) += mpq_dmx_plugin_tspp_v2.o
+
+mpq-dmx-hw-plugin-$(CONFIG_DVB_MPQ_TSIF) += mpq_dmx_plugin_tsif.o
+
diff --git a/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_common.c b/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_common.c
new file mode 100644
index 0000000..e7bbfcb
--- /dev/null
+++ b/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_common.c
@@ -0,0 +1,1239 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/vmalloc.h>
+#include "mpq_dvb_debug.h"
+#include "mpq_dmx_plugin_common.h"
+
+
+/* Length of mandatory fields that must exist in header of video PES */
+#define PES_MANDATORY_FIELDS_LEN			9
+
+
+/*
+ * 500 PES header packets in the meta-data buffer,
+ * should be more than enough
+ */
+#define VIDEO_NUM_OF_PES_PACKETS			500
+
+#define VIDEO_META_DATA_BUFFER_SIZE              \
+	(VIDEO_NUM_OF_PES_PACKETS *                  \
+	  (DVB_RINGBUFFER_PKTHDRSIZE +               \
+	   sizeof(struct mpq_streambuffer_packet_header) + \
+	   sizeof(struct mpq_adapter_video_meta_data)))
+
+/*
+ * The following threshold defines gap from end of ring-buffer
+ * from which new PES payload will not be written to make
+ * sure that the PES payload does not wrap-around at end of the
+ * buffer. Instead, padding will be inserted and the new PES will
+ * be written from the beginning of the buffer.
+ * Setting this to 0 means no padding will be added.
+ */
+#define VIDEO_WRAP_AROUND_THRESHOLD			(1024*1024+512*1024)
+
+/*
+ * PCR/STC information length saved in ring-buffer.
+ * PCR / STC are saved in ring-buffer in the following form:
+ * <8 bit flags><64 bits of STC> <64bits of PCR>
+ * STC and PCR values are in 27MHz.
+ * The current flags that are defined:
+ * 0x00000001: discontinuity_indicator
+ */
+#define PCR_STC_LEN							17
+
+
+/* Number of demux devices, has default of linux configuration */
+static int mpq_demux_device_num = CONFIG_DVB_MPQ_NUM_DMX_DEVICES;
+module_param(mpq_demux_device_num, int, S_IRUGO);
+
+
+/* Global data-structure for managing demux devices */
+static struct
+{
+	/* ION demux client used for memory allocation */
+	struct ion_client *ion_client;
+
+	/* demux devices array */
+	struct mpq_demux *devices;
+
+	/* Stream buffers objects used for tunneling to decoders */
+	struct mpq_streambuffer
+		decoder_buffers[MPQ_ADAPTER_MAX_NUM_OF_INTERFACES];
+
+	/*
+	 * Indicates whether we allow decoder's data to
+	 * wrap-around in the output buffer or padding is
+	 * inserted in such case.
+	 */
+	int decoder_data_wrap;
+} mpq_dmx_info;
+
+
+/* Check that PES header is valid and that it is a video PES */
+static int mpq_dmx_is_valid_video_pes(struct pes_packet_header *pes_header)
+{
+	/* start-code valid? */
+	if ((pes_header->packet_start_code_prefix_1 != 0) ||
+		(pes_header->packet_start_code_prefix_2 != 0) ||
+		(pes_header->packet_start_code_prefix_3 != 1))
+		return -EINVAL;
+
+	/* stream_id is video? */
+	if ((pes_header->stream_id & 0xF0) != 0xE0)
+		return -EINVAL;
+
+	return 0;
+}
+
+
+/* Extend dvb-demux debugfs with HW statistics */
+void mpq_dmx_init_hw_statistics(struct mpq_demux *mpq_demux)
+{
+	/*
+	 * Extend dvb-demux debugfs with HW statistics.
+	 * Note that destruction of debugfs directory is done
+	 * when dvb-demux is terminated.
+	 */
+	mpq_demux->hw_notification_count = 0;
+	mpq_demux->hw_notification_rate = 0;
+	mpq_demux->hw_notification_size = 0;
+	mpq_demux->decoder_tsp_drop_count = 0;
+
+	if (mpq_demux->demux.debugfs_demux_dir != NULL) {
+		debugfs_create_u32(
+			"hw_notification_rate",
+			S_IRUGO|S_IWUGO,
+			mpq_demux->demux.debugfs_demux_dir,
+			&mpq_demux->hw_notification_rate);
+
+		debugfs_create_u32(
+			"hw_notification_count",
+			S_IRUGO|S_IWUGO,
+			mpq_demux->demux.debugfs_demux_dir,
+			&mpq_demux->hw_notification_count);
+
+		debugfs_create_u32(
+			"hw_notification_size",
+			S_IRUGO|S_IWUGO,
+			mpq_demux->demux.debugfs_demux_dir,
+			&mpq_demux->hw_notification_size);
+
+		debugfs_create_u32(
+			"decoder_tsp_drop_count",
+			S_IRUGO|S_IWUGO,
+			mpq_demux->demux.debugfs_demux_dir,
+			&mpq_demux->decoder_tsp_drop_count);
+	}
+}
+EXPORT_SYMBOL(mpq_dmx_init_hw_statistics);
+
+
+/* Update dvb-demux debugfs with HW notification statistics */
+void mpq_dmx_update_hw_statistics(struct mpq_demux *mpq_demux)
+{
+	struct timespec curr_time, delta_time;
+	u64 delta_time_ms;
+
+	curr_time = current_kernel_time();
+	if (likely(mpq_demux->hw_notification_count)) {
+		/* calculate time-delta between notifications */
+		delta_time =
+			timespec_sub(
+					curr_time,
+					mpq_demux->last_notification_time);
+
+		delta_time_ms = (u64)timespec_to_ns(&delta_time);
+		delta_time_ms = div64_u64(delta_time_ms, 1000000); /* ns->ms */
+
+		mpq_demux->hw_notification_rate = delta_time_ms;
+	}
+
+	mpq_demux->hw_notification_count++;
+	mpq_demux->last_notification_time = curr_time;
+}
+EXPORT_SYMBOL(mpq_dmx_update_hw_statistics);
+
+
+int mpq_dmx_plugin_init(mpq_dmx_init dmx_init_func)
+{
+	int i;
+	int result;
+	struct mpq_demux *mpq_demux;
+	struct dvb_adapter *mpq_adapter;
+
+	MPQ_DVB_DBG_PRINT("%s executed, device num %d\n",
+					  __func__,
+					  mpq_demux_device_num);
+
+	mpq_adapter = mpq_adapter_get();
+
+	if (mpq_adapter == NULL) {
+		MPQ_DVB_ERR_PRINT(
+			"%s: mpq_adapter is not valid\n",
+			__func__);
+		result = -EPERM;
+		goto init_failed;
+	}
+
+	if (mpq_demux_device_num == 0) {
+		MPQ_DVB_ERR_PRINT(
+			"%s: mpq_demux_device_num set to 0\n",
+			__func__);
+
+		result = -EPERM;
+		goto init_failed;
+	}
+
+	mpq_dmx_info.devices = NULL;
+	mpq_dmx_info.ion_client = NULL;
+
+	/* TODO: the following should be set based on the decoder */
+	mpq_dmx_info.decoder_data_wrap = 0;
+
+	/* Allocate memory for all MPQ devices */
+	mpq_dmx_info.devices =
+		vmalloc(mpq_demux_device_num*sizeof(struct mpq_demux));
+
+	if (!mpq_dmx_info.devices) {
+		MPQ_DVB_ERR_PRINT(
+				"%s: failed to allocate devices memory\n",
+				__func__);
+
+		result = -ENOMEM;
+		goto init_failed;
+	}
+
+	/* Zero allocated memory */
+	memset(mpq_dmx_info.devices,
+		   0,
+		   mpq_demux_device_num*sizeof(struct mpq_demux));
+
+	/*
+	 * Create a new ION client used by demux to allocate memory
+	 * for decoder's buffers.
+	 */
+	mpq_dmx_info.ion_client =
+		msm_ion_client_create(UINT_MAX, "demux client");
+
+	if (IS_ERR_OR_NULL(mpq_dmx_info.ion_client)) {
+		MPQ_DVB_ERR_PRINT(
+				"%s: msm_ion_client_create\n",
+				__func__);
+
+		result = PTR_ERR(mpq_dmx_info.ion_client);
+		mpq_dmx_info.ion_client = NULL;
+		goto init_failed_free_demux_devices;
+	}
+
+	/* Initialize and register all demux devices to the system */
+	for (i = 0; i < mpq_demux_device_num; i++) {
+		mpq_demux = mpq_dmx_info.devices+i;
+
+		/* initialize demux source to memory by default */
+		mpq_demux->source = DMX_SOURCE_DVR0 + i;
+
+		/*
+		 * Give the plugin pointer to the ion client so
+		 * that it can allocate memory from ION if it requires so
+		 */
+		mpq_demux->ion_client = mpq_dmx_info.ion_client;
+
+		spin_lock_init(&mpq_demux->feed_lock);
+
+		/*
+		 * mpq_demux_plugin_hw_init should be implemented
+		 * by the specific plugin
+		 */
+		result = dmx_init_func(mpq_adapter, mpq_demux);
+		if (result < 0) {
+			MPQ_DVB_ERR_PRINT(
+				"%s: dmx_init_func (errno=%d)\n",
+				__func__,
+				result);
+
+			goto init_failed_free_demux_devices;
+		}
+
+		mpq_demux->is_initialized = 1;
+
+		/*
+		 * Add capability of receiving input from memory.
+		 * Every demux in our system may be connected to memory input,
+		 * or any live input.
+		 */
+		mpq_demux->fe_memory.source = DMX_MEMORY_FE;
+		result =
+			mpq_demux->demux.dmx.add_frontend(
+					&mpq_demux->demux.dmx,
+					&mpq_demux->fe_memory);
+
+		if (result < 0) {
+			MPQ_DVB_ERR_PRINT(
+				"%s: add_frontend (mem) failed (errno=%d)\n",
+				__func__,
+				result);
+
+			goto init_failed_free_demux_devices;
+		}
+	}
+
+	return 0;
+
+init_failed_free_demux_devices:
+	mpq_dmx_plugin_exit();
+init_failed:
+	return result;
+}
+EXPORT_SYMBOL(mpq_dmx_plugin_init);
+
+
+void mpq_dmx_plugin_exit(void)
+{
+	int i;
+	struct mpq_demux *mpq_demux;
+
+	MPQ_DVB_DBG_PRINT("%s executed\n", __func__);
+
+	if (mpq_dmx_info.ion_client != NULL) {
+		ion_client_destroy(mpq_dmx_info.ion_client);
+		mpq_dmx_info.ion_client = NULL;
+	}
+
+	if (mpq_dmx_info.devices != NULL) {
+		for (i = 0; i < mpq_demux_device_num; i++) {
+			mpq_demux = mpq_dmx_info.devices+i;
+
+			if (mpq_demux->is_initialized) {
+				mpq_demux->demux.dmx.remove_frontend(
+							&mpq_demux->demux.dmx,
+							&mpq_demux->fe_memory);
+
+				dvb_dmxdev_release(&mpq_demux->dmxdev);
+				dvb_dmx_release(&mpq_demux->demux);
+			}
+		}
+
+		vfree(mpq_dmx_info.devices);
+		mpq_dmx_info.devices = NULL;
+	}
+}
+EXPORT_SYMBOL(mpq_dmx_plugin_exit);
+
+
+int mpq_dmx_set_source(
+		struct dmx_demux *demux,
+		const dmx_source_t *src)
+{
+	int i;
+	int dvr_index;
+	int dmx_index;
+	struct mpq_demux *mpq_demux = (struct mpq_demux *)demux->priv;
+
+	if ((mpq_dmx_info.devices == NULL) || (mpq_demux == NULL)) {
+		MPQ_DVB_ERR_PRINT("%s: invalid parameters\n", __func__);
+		return -EINVAL;
+	}
+
+	/*
+	 * For dvr sources,
+	 * verify that this source is connected to the respective demux
+	 */
+	dmx_index = mpq_demux - mpq_dmx_info.devices;
+
+	if (*src >= DMX_SOURCE_DVR0) {
+		dvr_index = *src - DMX_SOURCE_DVR0;
+
+		if (dvr_index != dmx_index) {
+			MPQ_DVB_ERR_PRINT(
+				"%s: can't connect demux%d to dvr%d\n",
+				__func__,
+				dmx_index,
+				dvr_index);
+			return -EINVAL;
+		}
+	}
+
+	/*
+	 * For front-end sources,
+	 * verify that this source is not already set to different demux
+	 */
+	for (i = 0; i < mpq_demux_device_num; i++) {
+		if ((&mpq_dmx_info.devices[i] != mpq_demux) &&
+			(mpq_dmx_info.devices[i].source == *src)) {
+			MPQ_DVB_ERR_PRINT(
+				"%s: demux%d source can't be set,\n"
+				"demux%d occupies this source already\n",
+				__func__,
+				dmx_index,
+				i);
+			return -EBUSY;
+		}
+	}
+
+	mpq_demux->source = *src;
+	return 0;
+}
+EXPORT_SYMBOL(mpq_dmx_set_source);
+
+
+int mpq_dmx_init_video_feed(struct dvb_demux_feed *feed)
+{
+	int ret;
+	void *packet_buffer;
+	void *payload_buffer;
+	struct mpq_video_feed_info *feed_data;
+	struct mpq_demux *mpq_demux =
+		(struct mpq_demux *)feed->demux->priv;
+	struct mpq_streambuffer *stream_buffer;
+	int actual_buffer_size;
+
+	/* Allocate memory for private feed data */
+	feed_data = vmalloc(sizeof(struct mpq_video_feed_info));
+
+	if (feed_data == NULL) {
+		MPQ_DVB_ERR_PRINT(
+			"%s: FAILED to private video feed data\n",
+			__func__);
+
+		ret = -ENOMEM;
+		goto init_failed;
+	}
+
+	/* Allocate packet buffer holding the meta-data */
+	packet_buffer = vmalloc(VIDEO_META_DATA_BUFFER_SIZE);
+
+	if (packet_buffer == NULL) {
+		MPQ_DVB_ERR_PRINT(
+			"%s: FAILED to allocate packets buffer\n",
+			__func__);
+
+		ret = -ENOMEM;
+		goto init_failed_free_priv_data;
+	}
+
+	/*
+	 * Allocate payload buffer through ION.
+	 * TODO: for scrambling support, need to check if the
+	 * stream is scrambled and allocate the buffer with secure
+	 * flag set.
+	 */
+
+	if (mpq_dmx_info.decoder_data_wrap)
+		actual_buffer_size =
+			feed->buffer_size;
+	else
+		actual_buffer_size =
+			feed->buffer_size + VIDEO_WRAP_AROUND_THRESHOLD;
+
+	actual_buffer_size += (SZ_4K - 1);
+	actual_buffer_size &= ~(SZ_4K - 1);
+
+	feed_data->payload_buff_handle =
+		ion_alloc(mpq_demux->ion_client,
+				  actual_buffer_size,
+				  SZ_4K,
+				  ION_HEAP(ION_CP_MM_HEAP_ID));
+
+	if (IS_ERR_OR_NULL(feed_data->payload_buff_handle)) {
+		ret = PTR_ERR(feed_data->payload_buff_handle);
+
+		MPQ_DVB_ERR_PRINT(
+			"%s: FAILED to allocate payload buffer %d\n",
+			__func__,
+			ret);
+
+		goto init_failed_free_packet_buffer;
+	}
+
+	payload_buffer =
+		ion_map_kernel(mpq_demux->ion_client,
+					   feed_data->payload_buff_handle,
+					   0);
+
+	if (IS_ERR_OR_NULL(payload_buffer)) {
+		ret = PTR_ERR(payload_buffer);
+
+		MPQ_DVB_ERR_PRINT(
+			"%s: FAILED to map payload buffer %d\n",
+			__func__,
+			ret);
+
+		goto init_failed_free_payload_buffer;
+	}
+
+	/* Register the new stream-buffer interface to MPQ adapter */
+	switch (feed->pes_type) {
+	case DMX_TS_PES_VIDEO0:
+		feed_data->stream_interface =
+			MPQ_ADAPTER_VIDEO0_STREAM_IF;
+		break;
+
+	case DMX_TS_PES_VIDEO1:
+		feed_data->stream_interface =
+			MPQ_ADAPTER_VIDEO1_STREAM_IF;
+		break;
+
+	case DMX_TS_PES_VIDEO2:
+		feed_data->stream_interface =
+			MPQ_ADAPTER_VIDEO2_STREAM_IF;
+		break;
+
+	case DMX_TS_PES_VIDEO3:
+		feed_data->stream_interface =
+			MPQ_ADAPTER_VIDEO3_STREAM_IF;
+		break;
+
+	default:
+		MPQ_DVB_ERR_PRINT(
+			"%s: Invalid pes type %d\n",
+			__func__,
+			feed->pes_type);
+		ret = -EINVAL;
+		goto init_failed_unmap_payload_buffer;
+	}
+
+	/* make sure not occupied already */
+	stream_buffer = NULL;
+	mpq_adapter_get_stream_if(
+			feed_data->stream_interface,
+			&stream_buffer);
+	if (stream_buffer != NULL) {
+		MPQ_DVB_ERR_PRINT(
+			"%s: Video interface %d already occupied!\n",
+			__func__,
+			feed_data->stream_interface);
+		ret = -EBUSY;
+		goto init_failed_unmap_payload_buffer;
+	}
+
+	feed_data->video_buffer =
+		&mpq_dmx_info.decoder_buffers[feed_data->stream_interface];
+
+	mpq_streambuffer_init(
+			feed_data->video_buffer,
+			payload_buffer,
+			actual_buffer_size,
+			packet_buffer,
+			VIDEO_META_DATA_BUFFER_SIZE);
+
+	ret =
+		mpq_adapter_register_stream_if(
+			feed_data->stream_interface,
+			feed_data->video_buffer);
+
+	if (ret < 0) {
+		MPQ_DVB_ERR_PRINT(
+			"%s: mpq_adapter_register_stream_if failed, "
+			"err = %d\n",
+			__func__,
+			ret);
+		goto init_failed_unmap_payload_buffer;
+	}
+
+	feed->buffer_size = actual_buffer_size;
+	feed_data->pes_payload_address =
+		(u32)feed_data->video_buffer->raw_data.data;
+
+	feed_data->pes_header_left_bytes = PES_MANDATORY_FIELDS_LEN;
+	feed_data->pes_header_offset = 0;
+	feed->pusi_seen = 0;
+	feed->peslen = 0;
+	feed_data->fullness_wait_cancel = 0;
+
+	spin_lock(&mpq_demux->feed_lock);
+	feed->priv = (void *)feed_data;
+	spin_unlock(&mpq_demux->feed_lock);
+
+	return 0;
+
+init_failed_unmap_payload_buffer:
+	ion_unmap_kernel(mpq_demux->ion_client,
+					 feed_data->payload_buff_handle);
+init_failed_free_payload_buffer:
+	ion_free(mpq_demux->ion_client,
+			feed_data->payload_buff_handle);
+init_failed_free_packet_buffer:
+	vfree(packet_buffer);
+init_failed_free_priv_data:
+	vfree(feed_data);
+	feed->priv = NULL;
+init_failed:
+
+	return ret;
+}
+EXPORT_SYMBOL(mpq_dmx_init_video_feed);
+
+
+int mpq_dmx_terminate_video_feed(struct dvb_demux_feed *feed)
+{
+	struct mpq_video_feed_info *feed_data;
+	struct mpq_demux *mpq_demux;
+
+	if (feed->priv == NULL) {
+		MPQ_DVB_ERR_PRINT(
+			"%s: invalid feed, feed->priv is NULL\n",
+			__func__);
+
+		return -EINVAL;
+	}
+
+	mpq_demux =
+		(struct mpq_demux *)feed->demux->priv;
+
+	feed_data =
+		(struct mpq_video_feed_info *)feed->priv;
+
+	spin_lock(&mpq_demux->feed_lock);
+	feed->priv = NULL;
+	spin_unlock(&mpq_demux->feed_lock);
+
+	wake_up_all(&feed_data->video_buffer->raw_data.queue);
+
+	mpq_adapter_unregister_stream_if(
+		feed_data->stream_interface);
+
+	vfree(feed_data->video_buffer->packet_data.data);
+
+	ion_unmap_kernel(mpq_demux->ion_client,
+					 feed_data->payload_buff_handle);
+
+	ion_free(mpq_demux->ion_client,
+			 feed_data->payload_buff_handle);
+
+	vfree(feed_data);
+
+	return 0;
+}
+EXPORT_SYMBOL(mpq_dmx_terminate_video_feed);
+
+int mpq_dmx_decoder_fullness_init(struct dvb_demux_feed *feed)
+{
+	struct mpq_demux *mpq_demux;
+
+	mpq_demux =
+		(struct mpq_demux *)feed->demux->priv;
+
+	if (mpq_dmx_is_video_feed(feed)) {
+		struct mpq_video_feed_info *feed_data;
+
+		spin_lock(&mpq_demux->feed_lock);
+
+		if (feed->priv == NULL) {
+			MPQ_DVB_ERR_PRINT(
+				"%s: invalid feed, feed->priv is NULL\n",
+				__func__);
+			spin_unlock(&mpq_demux->feed_lock);
+			return -EINVAL;
+		}
+
+		feed_data =
+			(struct mpq_video_feed_info *)feed->priv;
+
+		feed_data->fullness_wait_cancel = 0;
+
+		spin_unlock(&mpq_demux->feed_lock);
+
+		return 0;
+	}
+
+	/* else */
+	MPQ_DVB_DBG_PRINT(
+		"%s: Invalid feed type %d\n",
+		__func__,
+		feed->pes_type);
+
+	return -EINVAL;
+}
+EXPORT_SYMBOL(mpq_dmx_decoder_fullness_init);
+
+int mpq_dmx_decoder_fullness_wait(
+		struct dvb_demux_feed *feed,
+		size_t required_space)
+{
+	struct mpq_demux *mpq_demux;
+
+	mpq_demux =
+		(struct mpq_demux *)feed->demux->priv;
+
+	if (mpq_dmx_is_video_feed(feed)) {
+		int ret;
+		int gap;
+		struct mpq_video_feed_info *feed_data;
+		struct dvb_ringbuffer *video_buff;
+
+		spin_lock(&mpq_demux->feed_lock);
+
+		if (feed->priv == NULL) {
+			spin_unlock(&mpq_demux->feed_lock);
+			return -EINVAL;
+		}
+
+		feed_data =
+			(struct mpq_video_feed_info *)feed->priv;
+
+		video_buff =
+			&feed_data->video_buffer->raw_data;
+
+		/*
+		 * If we are now starting new PES and the
+		 * PES payload may wrap-around, extra padding
+		 * needs to be pushed into the buffer.
+		 */
+		gap = video_buff->size - video_buff->pwrite;
+		if ((!mpq_dmx_info.decoder_data_wrap) &&
+			(gap < VIDEO_WRAP_AROUND_THRESHOLD))
+			required_space += gap;
+
+		ret = 0;
+		if ((feed_data != NULL) &&
+			(!feed_data->fullness_wait_cancel) &&
+			(dvb_ringbuffer_free(video_buff) < required_space)) {
+			DEFINE_WAIT(__wait);
+			for (;;) {
+				prepare_to_wait(
+					&video_buff->queue,
+					&__wait,
+					TASK_INTERRUPTIBLE);
+
+				if ((feed->priv == NULL) ||
+					(feed_data->fullness_wait_cancel) ||
+					(dvb_ringbuffer_free(video_buff) >=
+					required_space))
+					break;
+
+				if (!signal_pending(current)) {
+					spin_unlock(&mpq_demux->feed_lock);
+					schedule();
+					spin_lock(&mpq_demux->feed_lock);
+					continue;
+				}
+				ret = -ERESTARTSYS;
+				break;
+			}
+			finish_wait(&video_buff->queue, &__wait);
+		}
+
+		if (ret < 0) {
+			spin_unlock(&mpq_demux->feed_lock);
+			return ret;
+		}
+
+		if ((feed->priv == NULL) ||
+			(feed_data->fullness_wait_cancel)) {
+			spin_unlock(&mpq_demux->feed_lock);
+			return -EINVAL;
+		}
+
+		spin_unlock(&mpq_demux->feed_lock);
+		return 0;
+	}
+
+	/* else */
+	MPQ_DVB_DBG_PRINT(
+		"%s: Invalid feed type %d\n",
+		__func__,
+		feed->pes_type);
+
+	return -EINVAL;
+}
+EXPORT_SYMBOL(mpq_dmx_decoder_fullness_wait);
+
+int mpq_dmx_decoder_fullness_abort(struct dvb_demux_feed *feed)
+{
+	struct mpq_demux *mpq_demux;
+
+	mpq_demux =
+		(struct mpq_demux *)feed->demux->priv;
+
+	if (mpq_dmx_is_video_feed(feed)) {
+		struct mpq_video_feed_info *feed_data;
+		struct dvb_ringbuffer *video_buff;
+
+		spin_lock(&mpq_demux->feed_lock);
+
+		if (feed->priv == NULL) {
+			MPQ_DVB_ERR_PRINT(
+				"%s: invalid feed, feed->priv is NULL\n",
+				__func__);
+			spin_unlock(&mpq_demux->feed_lock);
+			return -EINVAL;
+		}
+
+		feed_data =
+			(struct mpq_video_feed_info *)feed->priv;
+
+		video_buff =
+			&feed_data->video_buffer->raw_data;
+
+		feed_data->fullness_wait_cancel = 1;
+		spin_unlock(&mpq_demux->feed_lock);
+
+		wake_up_all(&video_buff->queue);
+
+		return 0;
+	}
+
+	/* else */
+	MPQ_DVB_ERR_PRINT(
+		"%s: Invalid feed type %d\n",
+		__func__,
+		feed->pes_type);
+
+	return -EINVAL;
+}
+EXPORT_SYMBOL(mpq_dmx_decoder_fullness_abort);
+
+int mpq_dmx_process_video_packet(
+			struct dvb_demux_feed *feed,
+			const u8 *buf)
+{
+	int bytes_avail;
+	int left_size;
+	int copy_len;
+	u32 ts_payload_offset;
+	struct mpq_video_feed_info *feed_data;
+	const struct ts_packet_header *ts_header;
+	struct mpq_streambuffer *stream_buffer;
+	struct pes_packet_header *pes_header;
+	struct mpq_demux *mpq_demux;
+
+	mpq_demux =
+		(struct mpq_demux *)feed->demux->priv;
+
+	spin_lock(&mpq_demux->feed_lock);
+
+	feed_data =
+		(struct mpq_video_feed_info *)feed->priv;
+
+	if (unlikely(feed_data == NULL)) {
+		spin_unlock(&mpq_demux->feed_lock);
+		return 0;
+	}
+
+	ts_header = (const struct ts_packet_header *)buf;
+
+	stream_buffer =
+			feed_data->video_buffer;
+
+	pes_header =
+			&feed_data->pes_header;
+
+/*	printk("TS packet: %X %X %X %X %X%X %X %X %X\n",
+		ts_header->sync_byte,
+		ts_header->transport_error_indicator,
+		ts_header->payload_unit_start_indicator,
+		ts_header->transport_priority,
+		ts_header->pid_msb,
+		ts_header->pid_lsb,
+		ts_header->transport_scrambling_control,
+		ts_header->adaptation_field_control,
+		ts_header->continuity_counter);*/
+
+	/* Make sure this TS packet has a payload and not scrambled */
+	if ((ts_header->sync_byte != 0x47) ||
+		(ts_header->adaptation_field_control == 0) ||
+		(ts_header->adaptation_field_control == 2) ||
+		(ts_header->transport_scrambling_control)) {
+		/* continue to next packet */
+		spin_unlock(&mpq_demux->feed_lock);
+		return 0;
+	}
+
+	if (ts_header->payload_unit_start_indicator) { /* PUSI? */
+		if (feed->pusi_seen) { /* Did we see PUSI before? */
+			struct mpq_streambuffer_packet_header packet;
+			struct mpq_adapter_video_meta_data meta_data;
+
+			/*
+			 * Close previous PES.
+			 * Push new packet to the meta-data buffer.
+			 * Double check that we are not in middle of
+			 * previous PES header parsing.
+			 */
+
+			if (0 == feed_data->pes_header_left_bytes) {
+				packet.raw_data_addr =
+					feed_data->pes_payload_address;
+
+				packet.raw_data_len = feed->peslen;
+
+				if ((!mpq_dmx_info.decoder_data_wrap) &&
+					((feed_data->pes_payload_address +
+					feed->peslen) >
+					((u32)stream_buffer->raw_data.data +
+					stream_buffer->raw_data.size)))
+					MPQ_DVB_ERR_PRINT(
+						"%s: "
+						"Video data has wrapped-around!\n",
+						__func__);
+
+				packet.user_data_len =
+					sizeof(struct
+						mpq_adapter_video_meta_data);
+
+				if ((pes_header->pts_dts_flag == 2) ||
+					(pes_header->pts_dts_flag == 3))
+					meta_data.pts_exist = 1;
+				else
+					meta_data.pts_exist = 0;
+
+				meta_data.pts =
+					((u64)pes_header->pts_1 << 30) |
+					((u64)pes_header->pts_2 << 22) |
+					((u64)pes_header->pts_3 << 15) |
+					((u64)pes_header->pts_4 << 7) |
+					(u64)pes_header->pts_5;
+
+				if (pes_header->pts_dts_flag == 3)
+					meta_data.dts_exist = 1;
+				else
+					meta_data.dts_exist = 0;
+
+				meta_data.dts =
+					((u64)pes_header->dts_1 << 30) |
+					((u64)pes_header->dts_2 << 22) |
+					((u64)pes_header->dts_3 << 15) |
+					((u64)pes_header->dts_4 << 7) |
+					(u64)pes_header->dts_5;
+
+				meta_data.is_padding = 0;
+
+				if (mpq_streambuffer_pkt_write(
+						stream_buffer,
+						&packet,
+						(u8 *)&meta_data) < 0)
+					MPQ_DVB_ERR_PRINT(
+						"%s: "
+						"Couldn't write packet. "
+						"Should never happen\n",
+						__func__);
+			} else {
+				MPQ_DVB_ERR_PRINT(
+					"%s: received PUSI"
+					"while handling PES header"
+					"of previous PES\n",
+					__func__);
+			}
+
+			/* Reset PES info */
+			feed_data->pes_payload_address =
+				(u32)stream_buffer->raw_data.data +
+				stream_buffer->raw_data.pwrite;
+
+			feed->peslen = 0;
+			feed_data->pes_header_offset = 0;
+			feed_data->pes_header_left_bytes =
+				PES_MANDATORY_FIELDS_LEN;
+		} else {
+			feed->pusi_seen = 1;
+		}
+	}
+
+	/*
+	 * Parse PES data only if PUSI was encountered,
+	 * otherwise the data is dropped
+	 */
+	if (!feed->pusi_seen) {
+		spin_unlock(&mpq_demux->feed_lock);
+		return 0; /* drop and wait for next packets */
+	}
+
+	ts_payload_offset = sizeof(struct ts_packet_header);
+
+	/* Skip adaptation field if exists */
+	if (ts_header->adaptation_field_control == 3)
+		ts_payload_offset += buf[ts_payload_offset] + 1;
+
+	bytes_avail = 188 - ts_payload_offset;
+
+	/* Got the mandatory fields of the video PES header? */
+	if (feed_data->pes_header_offset < PES_MANDATORY_FIELDS_LEN) {
+		left_size =
+			PES_MANDATORY_FIELDS_LEN -
+			feed_data->pes_header_offset;
+
+		copy_len = (left_size > bytes_avail) ?
+					bytes_avail :
+					left_size;
+
+		memcpy((u8 *)pes_header+feed_data->pes_header_offset,
+				buf+ts_payload_offset,
+				copy_len);
+
+		feed_data->pes_header_offset += copy_len;
+
+		if (left_size > bytes_avail) {
+			spin_unlock(&mpq_demux->feed_lock);
+			return 0;
+		}
+
+		/* else - we have beginning of PES header */
+		bytes_avail -= left_size;
+		ts_payload_offset += left_size;
+
+		/* Make sure the PES packet is valid */
+		if (mpq_dmx_is_valid_video_pes(pes_header) < 0) {
+			/*
+			 * Since the new PES header parsing
+			 * failed, reset pusi_seen to drop all
+			 * data until next PUSI
+			 */
+			feed->pusi_seen = 0;
+			feed_data->pes_header_offset = 0;
+
+			MPQ_DVB_ERR_PRINT(
+				"%s: invalid packet\n",
+				__func__);
+
+			spin_unlock(&mpq_demux->feed_lock);
+			return 0;
+		}
+
+		feed_data->pes_header_left_bytes =
+			pes_header->pes_header_data_length;
+	}
+
+	/* Remainning header bytes that need to be processed? */
+	if (feed_data->pes_header_left_bytes) {
+		/* Did we capture the PTS value (if exist)? */
+		if ((bytes_avail != 0) &&
+			(feed_data->pes_header_offset <
+			 (PES_MANDATORY_FIELDS_LEN+5)) &&
+			((pes_header->pts_dts_flag == 2) ||
+			 (pes_header->pts_dts_flag == 3))) {
+
+			/* 5 more bytes should be there */
+			left_size =
+				PES_MANDATORY_FIELDS_LEN +
+				5 -
+				feed_data->pes_header_offset;
+
+			copy_len = (left_size > bytes_avail) ?
+						bytes_avail :
+						left_size;
+
+			memcpy((u8 *)pes_header+
+				feed_data->pes_header_offset,
+				buf+ts_payload_offset,
+				copy_len);
+
+			feed_data->pes_header_offset += copy_len;
+			feed_data->pes_header_left_bytes -= copy_len;
+
+			if (left_size > bytes_avail) {
+				spin_unlock(&mpq_demux->feed_lock);
+				return 0;
+			}
+
+			/* else - we have the PTS */
+			bytes_avail -= copy_len;
+			ts_payload_offset += copy_len;
+		}
+
+		/* Did we capture the DTS value (if exist)? */
+		if ((bytes_avail != 0) &&
+			(feed_data->pes_header_offset <
+			 (PES_MANDATORY_FIELDS_LEN+10)) &&
+			(pes_header->pts_dts_flag == 3)) {
+
+			/* 5 more bytes should be there */
+			left_size =
+				PES_MANDATORY_FIELDS_LEN +
+				10 -
+				feed_data->pes_header_offset;
+
+			copy_len = (left_size > bytes_avail) ?
+						bytes_avail :
+						left_size;
+
+			memcpy((u8 *)pes_header+
+				feed_data->pes_header_offset,
+				buf+ts_payload_offset,
+				copy_len);
+
+			feed_data->pes_header_offset += copy_len;
+			feed_data->pes_header_left_bytes -= copy_len;
+
+			if (left_size > bytes_avail) {
+				spin_unlock(&mpq_demux->feed_lock);
+				return 0;
+			}
+
+			/* else - we have the DTS */
+			bytes_avail -= copy_len;
+			ts_payload_offset += copy_len;
+		}
+
+		/* Any more header bytes?! */
+		if (feed_data->pes_header_left_bytes >= bytes_avail) {
+			feed_data->pes_header_left_bytes -= bytes_avail;
+			spin_unlock(&mpq_demux->feed_lock);
+			return 0;
+		}
+
+		/* Got PES header, process payload */
+		bytes_avail -= feed_data->pes_header_left_bytes;
+		ts_payload_offset += feed_data->pes_header_left_bytes;
+		feed_data->pes_header_left_bytes = 0;
+	}
+
+	/*
+	 * If we reached here,
+	 * then we are now at the PES payload data
+	 */
+	if (bytes_avail == 0) {
+		spin_unlock(&mpq_demux->feed_lock);
+		return 0;
+	}
+
+	if (feed->peslen == 0) { /* starting new PES */
+		/* gap till end of the buffer */
+		int gap =
+			stream_buffer->raw_data.size -
+			stream_buffer->raw_data.pwrite;
+
+		if ((!mpq_dmx_info.decoder_data_wrap) &&
+			(gap < VIDEO_WRAP_AROUND_THRESHOLD)) {
+			struct mpq_streambuffer_packet_header packet;
+			struct mpq_adapter_video_meta_data meta_data;
+
+			/*
+			 * Do not start writting new PES from
+			 * this location to prevent possible
+			 * wrap-around of the payload, fill padding instead.
+			 */
+
+			/* push a packet with padding indication */
+			meta_data.is_padding = 1;
+
+			packet.raw_data_len = gap;
+			packet.user_data_len =
+				sizeof(struct mpq_adapter_video_meta_data);
+			packet.raw_data_addr =
+				feed_data->pes_payload_address;
+
+			if (mpq_streambuffer_data_write_deposit(
+						stream_buffer,
+						gap) < 0) {
+				MPQ_DVB_ERR_PRINT(
+					"%s: mpq_streambuffer_data_write_deposit "
+					"failed!\n",
+					__func__);
+			} else if (mpq_streambuffer_pkt_write(
+							stream_buffer,
+							&packet,
+							(u8 *)&meta_data) < 0) {
+				MPQ_DVB_ERR_PRINT(
+					"%s: "
+					"Couldn't write packet. "
+					"Should never happen\n",
+					__func__);
+			} else {
+				feed_data->pes_payload_address =
+					(u32)stream_buffer->raw_data.data +
+					stream_buffer->raw_data.pwrite;
+			}
+		}
+	}
+
+	if (mpq_streambuffer_data_write(
+				stream_buffer,
+				buf+ts_payload_offset,
+				bytes_avail) < 0)
+		mpq_demux->decoder_tsp_drop_count++;
+	else
+		feed->peslen += bytes_avail;
+
+	spin_unlock(&mpq_demux->feed_lock);
+
+	return 0;
+}
+EXPORT_SYMBOL(mpq_dmx_process_video_packet);
+
+
+int mpq_dmx_process_pcr_packet(
+			struct dvb_demux_feed *feed,
+			const u8 *buf)
+{
+	int i;
+	u64 pcr;
+	u64 stc;
+	u8 output[PCR_STC_LEN];
+	struct mpq_demux *mpq_demux =
+		(struct mpq_demux *)feed->demux->priv;
+	const struct ts_packet_header *ts_header;
+	const struct ts_adaptation_field *adaptation_field;
+
+	/*
+	 * When we play from front-end, we configure HW
+	 * to output the extra timestamp, if we are playing
+	 * from DVR, make sure the format is 192 packet.
+	 */
+	if ((mpq_demux->source >= DMX_SOURCE_DVR0) &&
+		(mpq_demux->demux.tsp_format != DMX_TSP_FORMAT_192_TAIL)) {
+		MPQ_DVB_ERR_PRINT(
+			"%s: invalid packet format %d for PCR extraction\n",
+			__func__,
+			mpq_demux->demux.tsp_format);
+
+		 return -EINVAL;
+	}
+
+	ts_header = (const struct ts_packet_header *)buf;
+
+	/* Make sure this TS packet has a adaptation field */
+	if ((ts_header->sync_byte != 0x47) ||
+		(ts_header->adaptation_field_control == 0) ||
+		(ts_header->adaptation_field_control == 1)) {
+		return 0;
+	}
+
+	adaptation_field = (const struct ts_adaptation_field *)
+			(buf + sizeof(struct ts_packet_header));
+
+	if ((!adaptation_field->adaptation_field_length) ||
+		(!adaptation_field->PCR_flag))
+		return 0; /* 0 adaptation field or no PCR */
+
+	pcr = ((u64)adaptation_field->program_clock_reference_base_1) << 25;
+	pcr += ((u64)adaptation_field->program_clock_reference_base_2) << 17;
+	pcr += ((u64)adaptation_field->program_clock_reference_base_3) << 9;
+	pcr += ((u64)adaptation_field->program_clock_reference_base_4) << 1;
+	pcr += adaptation_field->program_clock_reference_base_5;
+	pcr *= 300;
+	pcr +=
+		(((u64)adaptation_field->program_clock_reference_ext_1) << 8) +
+		adaptation_field->program_clock_reference_ext_2;
+
+	stc = buf[189] << 16;
+	stc += buf[190] << 8;
+	stc += buf[191];
+	stc *= 256; /* convert from 105.47 KHZ to 27MHz */
+
+	output[0] = adaptation_field->discontinuity_indicator;
+
+	for (i = 1; i <= 8; i++)
+		output[i] = (stc >> ((8-i) << 3)) & 0xFF;
+
+	for (i = 9; i <= 16; i++)
+		output[i] = (pcr >> ((16-i) << 3)) & 0xFF;
+
+	feed->cb.ts(output, PCR_STC_LEN,
+				NULL, 0,
+				&feed->feed.ts, DMX_OK);
+	return 0;
+}
+EXPORT_SYMBOL(mpq_dmx_process_pcr_packet);
+
diff --git a/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_common.h b/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_common.h
new file mode 100644
index 0000000..d90bd89
--- /dev/null
+++ b/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_common.h
@@ -0,0 +1,494 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _MPQ_DMX_PLUGIN_COMMON_H
+#define _MPQ_DMX_PLUGIN_COMMON_H
+
+#include <linux/ion.h>
+
+#include "dvbdev.h"
+#include "dmxdev.h"
+#include "demux.h"
+#include "dvb_demux.h"
+#include "dvb_frontend.h"
+#include "mpq_adapter.h"
+
+
+/**
+ * Total number of filters per demux,
+ * including section and PES feeds
+ */
+#define MPQ_DMX_MAX_NUM_OF_FILTERS			64
+
+/**
+ * TSIF alias name length
+ */
+#define TSIF_NAME_LENGTH					10
+
+/**
+ * struct mpq_demux - mpq demux information
+ * @demux: The dvb_demux instance used by mpq_demux
+ * @dmxdev: The dmxdev instance used by mpq_demux
+ * @fe_memory: Handle of front-end memory source to mpq_demux
+ * @source: The current source connected to the demux
+ * @is_initialized: Indicates whether this demux device was
+ *                  initialized or not.
+ * @ion_client: ION demux client used to allocate memory from ION.
+ * @feed_lock: Lock used to protect against private feed data
+ * @hw_notification_rate: Notification rate in msec, exposed in debugfs.
+ * @hw_notification_count: Notification count, exposed in debugfs.
+ * @hw_notification_size: Notification size in bytes, exposed in debugfs.
+ * @decoder_tsp_drop_count: Counter of number of dropped TS packets
+ * due to decoder buffer fullness, exposed in debugfs.
+ * @last_notification_time: Time of last HW notification.
+ */
+struct mpq_demux {
+	struct dvb_demux demux;
+	struct dmxdev dmxdev;
+	struct dmx_frontend fe_memory;
+	dmx_source_t source;
+	int is_initialized;
+	struct ion_client *ion_client;
+	spinlock_t feed_lock;
+
+	/* debug-fs */
+	u32 hw_notification_rate;
+	u32 hw_notification_count;
+	u32 hw_notification_size;
+	u32 decoder_tsp_drop_count;
+	struct timespec last_notification_time;
+};
+
+/**
+ * mpq_dmx_init - initialization and registration function of
+ * single MPQ demux device
+ *
+ * @adapter: The adapter to register mpq_demux to
+ * @mpq_demux: The mpq demux to initialize
+ *
+ * Every HW pluging need to provide implementation of such
+ * function that will be called for each demux device on the
+ * module initialization. The function mpq_demux_plugin_init
+ * should be called during the HW plugin module initialization.
+ */
+typedef int (*mpq_dmx_init)(
+				struct dvb_adapter *mpq_adapter,
+				struct mpq_demux *demux);
+
+/**
+ * struct ts_packet_header - Transport packet header
+ * as defined in MPEG2 transport stream standard.
+ */
+struct ts_packet_header {
+#if defined(__BIG_ENDIAN_BITFIELD)
+	unsigned sync_byte:8;
+	unsigned transport_error_indicator:1;
+	unsigned payload_unit_start_indicator:1;
+	unsigned transport_priority:1;
+	unsigned pid_msb:5;
+	unsigned pid_lsb:8;
+	unsigned transport_scrambling_control:2;
+	unsigned adaptation_field_control:2;
+	unsigned continuity_counter:4;
+#elif defined(__LITTLE_ENDIAN_BITFIELD)
+	unsigned sync_byte:8;
+	unsigned pid_msb:5;
+	unsigned transport_priority:1;
+	unsigned payload_unit_start_indicator:1;
+	unsigned transport_error_indicator:1;
+	unsigned pid_lsb:8;
+	unsigned continuity_counter:4;
+	unsigned adaptation_field_control:2;
+	unsigned transport_scrambling_control:2;
+#else
+#error "Please fix <asm/byteorder.h>"
+#endif
+} __packed;
+
+/**
+ * struct ts_adaptation_field - Adaptation field prefix
+ * as defined in MPEG2 transport stream standard.
+ */
+struct ts_adaptation_field {
+#if defined(__BIG_ENDIAN_BITFIELD)
+	unsigned adaptation_field_length:8;
+	unsigned discontinuity_indicator:1;
+	unsigned random_access_indicator:1;
+	unsigned elementary_stream_priority_indicator:1;
+	unsigned PCR_flag:1;
+	unsigned OPCR_flag:1;
+	unsigned splicing_point_flag:1;
+	unsigned transport_private_data_flag:1;
+	unsigned adaptation_field_extension_flag:1;
+	unsigned program_clock_reference_base_1:8;
+	unsigned program_clock_reference_base_2:8;
+	unsigned program_clock_reference_base_3:8;
+	unsigned program_clock_reference_base_4:8;
+	unsigned program_clock_reference_base_5:1;
+	unsigned reserved:6;
+	unsigned program_clock_reference_ext_1:1;
+	unsigned program_clock_reference_ext_2:8;
+#elif defined(__LITTLE_ENDIAN_BITFIELD)
+	unsigned adaptation_field_length:8;
+	unsigned adaptation_field_extension_flag:1;
+	unsigned transport_private_data_flag:1;
+	unsigned splicing_point_flag:1;
+	unsigned OPCR_flag:1;
+	unsigned PCR_flag:1;
+	unsigned elementary_stream_priority_indicator:1;
+	unsigned random_access_indicator:1;
+	unsigned discontinuity_indicator:1;
+	unsigned program_clock_reference_base_1:8;
+	unsigned program_clock_reference_base_2:8;
+	unsigned program_clock_reference_base_3:8;
+	unsigned program_clock_reference_base_4:8;
+	unsigned program_clock_reference_ext_1:1;
+	unsigned reserved:6;
+	unsigned program_clock_reference_base_5:1;
+	unsigned program_clock_reference_ext_2:8;
+#else
+#error "Please fix <asm/byteorder.h>"
+#endif
+} __packed;
+
+
+/*
+ * PES packet header containing dts and/or pts values
+ * as defined in MPEG2 transport stream standard.
+ */
+struct pes_packet_header {
+#if defined(__BIG_ENDIAN_BITFIELD)
+	unsigned packet_start_code_prefix_1:8;
+	unsigned packet_start_code_prefix_2:8;
+	unsigned packet_start_code_prefix_3:8;
+	unsigned stream_id:8;
+	unsigned pes_packet_length_msb:8;
+	unsigned pes_packet_length_lsb:8;
+	unsigned reserved_bits0:2;
+	unsigned pes_scrambling_control:2;
+	unsigned pes_priority:1;
+	unsigned data_alignment_indicator:1;
+	unsigned copyright:1;
+	unsigned original_or_copy:1;
+	unsigned pts_dts_flag:2;
+	unsigned escr_flag:1;
+	unsigned es_rate_flag:1;
+	unsigned dsm_trick_mode_flag:1;
+	unsigned additional_copy_info_flag:1;
+	unsigned pes_crc_flag:1;
+	unsigned pes_extension_flag:1;
+	unsigned pes_header_data_length:8;
+	unsigned reserved_bits1:4;
+	unsigned pts_1:3;
+	unsigned marker_bit0:1;
+	unsigned pts_2:8;
+	unsigned pts_3:7;
+	unsigned marker_bit1:1;
+	unsigned pts_4:8;
+	unsigned pts_5:7;
+	unsigned marker_bit2:1;
+	unsigned reserved_bits2:4;
+	unsigned dts_1:3;
+	unsigned marker_bit3:1;
+	unsigned dts_2:8;
+	unsigned dts_3:7;
+	unsigned marker_bit4:1;
+	unsigned dts_4:8;
+	unsigned dts_5:7;
+	unsigned marker_bit5:1;
+	unsigned reserved_bits3:4;
+#elif defined(__LITTLE_ENDIAN_BITFIELD)
+	unsigned packet_start_code_prefix_1:8;
+	unsigned packet_start_code_prefix_2:8;
+	unsigned packet_start_code_prefix_3:8;
+	unsigned stream_id:8;
+	unsigned pes_packet_length_lsb:8;
+	unsigned pes_packet_length_msb:8;
+	unsigned original_or_copy:1;
+	unsigned copyright:1;
+	unsigned data_alignment_indicator:1;
+	unsigned pes_priority:1;
+	unsigned pes_scrambling_control:2;
+	unsigned reserved_bits0:2;
+	unsigned pes_extension_flag:1;
+	unsigned pes_crc_flag:1;
+	unsigned additional_copy_info_flag:1;
+	unsigned dsm_trick_mode_flag:1;
+	unsigned es_rate_flag:1;
+	unsigned escr_flag:1;
+	unsigned pts_dts_flag:2;
+	unsigned pes_header_data_length:8;
+	unsigned marker_bit0:1;
+	unsigned pts_1:3;
+	unsigned reserved_bits1:4;
+	unsigned pts_2:8;
+	unsigned marker_bit1:1;
+	unsigned pts_3:7;
+	unsigned pts_4:8;
+	unsigned marker_bit2:1;
+	unsigned pts_5:7;
+	unsigned marker_bit3:1;
+	unsigned dts_1:3;
+	unsigned reserved_bits2:4;
+	unsigned dts_2:8;
+	unsigned marker_bit4:1;
+	unsigned dts_3:7;
+	unsigned dts_4:8;
+	unsigned marker_bit5:1;
+	unsigned dts_5:7;
+	unsigned reserved_bits3:4;
+#else
+#error "Please fix <asm/byteorder.h>"
+#endif
+} __packed;
+
+/*
+ * mpq_video_feed_info - private data used for video feed.
+ *
+ * @plugin_data: Underlying plugin's own private data.
+ * @video_buffer: Holds the streamer buffer shared with
+ * the decoder for feeds having the data going to the decoder.
+ * @pes_header: Used for feeds that output data to decoder,
+ * holds PES header of current processed PES.
+ * @pes_header_left_bytes: Used for feeds that output data to decoder,
+ * holds remainning PES header bytes of current processed PES.
+ * @pes_header_offset: Holds the offset within the current processed
+ * pes header.
+ * @fullness_wait_cancel: Flag used to signal to abort waiting for
+ * decoder's fullness.
+ * @pes_payload_address: Used for feeds that output data to decoder,
+ * holds current PES payload start address.
+ * @payload_buff_handle: ION handle for the allocated payload buffer
+ * @stream_interface: The ID of the video stream interface registered
+ * with this stream buffer.
+ */
+struct mpq_video_feed_info {
+	void *plugin_data;
+	struct mpq_streambuffer *video_buffer;
+	struct pes_packet_header pes_header;
+	u32 pes_header_left_bytes;
+	u32 pes_header_offset;
+	u32 pes_payload_address;
+	int fullness_wait_cancel;
+	struct ion_handle *payload_buff_handle;
+	enum mpq_adapter_stream_if stream_interface;
+};
+
+/**
+ * mpq_demux_plugin_init - Initialize demux devices and register
+ * them to the dvb adapter.
+ *
+ * @dmx_init_func: Pointer to the function to be used
+ *  to initialize demux of the udnerlying HW plugin.
+ *
+ * Return     error code
+ *
+ * Should be called at the HW plugin module initialization.
+ */
+int mpq_dmx_plugin_init(mpq_dmx_init dmx_init_func);
+
+/**
+ * mpq_demux_plugin_exit - terminate demux devices.
+ *
+ * Should be called at the HW plugin module termination.
+ */
+void mpq_dmx_plugin_exit(void);
+
+/**
+ * mpq_dmx_set_source - implmenetation of set_source routine.
+ *
+ * @demux: The demux device to set its source.
+ * @src: The source to be set.
+ *
+ * Return     error code
+ *
+ * Can be used by the underlying plugins to implement kernel
+ * demux API set_source routine.
+ */
+int mpq_dmx_set_source(struct dmx_demux *demux, const dmx_source_t *src);
+
+/**
+ * mpq_dmx_init_video_feed - Initializes video feed
+ * used to pass data to decoder directly.
+ *
+ * @feed: The feed used for the video TS packets
+ *
+ * Return     error code.
+ *
+ * If the underlying plugin wishes to perform SW PES assmebly
+ * for the video data and stream it to the decoder, it should
+ * call this function when video feed is initialized before
+ * using mpq_dmx_process_video_packet.
+ *
+ * The function allocates mpq_video_feed_info and saves in
+ * feed->priv.
+ */
+int mpq_dmx_init_video_feed(struct dvb_demux_feed *feed);
+
+/**
+ * mpq_dmx_terminate_video_feed - Free private data of
+ * video feed allocated in mpq_dmx_init_video_feed
+ *
+ * @feed: The feed used for the video TS packets
+ *
+ * Return     error code.
+ */
+int mpq_dmx_terminate_video_feed(struct dvb_demux_feed *feed);
+
+/**
+ * mpq_dmx_decoder_fullness_init - Initialize waiting
+ * mechanism on decoder's buffer fullness.
+ *
+ * @feed: The decoder's feed
+ *
+ * Return     error code.
+ */
+int mpq_dmx_decoder_fullness_init(
+		struct dvb_demux_feed *feed);
+
+/**
+ * mpq_dmx_decoder_fullness_wait - Checks whether decoder buffer
+ * have free space as required, if not, wait for it.
+ *
+ * @feed: The decoder's feed
+ * @required_space: the required free space to wait for
+ *
+ * Return     error code.
+ */
+int mpq_dmx_decoder_fullness_wait(
+		struct dvb_demux_feed *feed,
+		size_t required_space);
+
+/**
+ * mpq_dmx_decoder_fullness_abort - Aborts waiting
+ * on decoder's buffer fullness if any waiting is done
+ * now. After calling this, to wait again the user must
+ * call mpq_dmx_decoder_fullness_init.
+ *
+ * @feed: The decoder's feed
+ *
+ * Return     error code.
+ */
+int mpq_dmx_decoder_fullness_abort(
+		struct dvb_demux_feed *feed);
+
+/**
+ * mpq_dmx_process_video_packet - Assemble PES data and output it
+ * to the stream-buffer connected to the decoder.
+ *
+ * @feed: The feed used for the video TS packets
+ * @buf: The buffer holding video TS packet.
+ *
+ * Return     error code.
+ *
+ * The function assumes it receives buffer with single TS packet
+ * of the relevant PID.
+ * If the output buffer is full while assembly, the function drops
+ * the packet and does not write them to the output buffer.
+ * Scrambled packets are bypassed.
+ */
+int mpq_dmx_process_video_packet(
+		struct dvb_demux_feed *feed,
+		const u8 *buf);
+
+/**
+ * mpq_dmx_process_pcr_packet - Extract PCR/STC pairs from
+ * a 192 bytes packet.
+ *
+ * @feed: The feed used for the PCR TS packets
+ * @buf: The buffer holding pcr/stc packet.
+ *
+ * Return     error code.
+ *
+ * The function assumes it receives buffer with single TS packet
+ * of the relevant PID, and that it has 4 bytes
+ * suffix as extra timestamp in the following format:
+ *
+ * Byte3: TSIF flags
+ * Byte0-2: TTS, 0..2^24-1 at 105.47 Khz (27*10^6/256).
+ *
+ * The function callbacks dmxdev after extraction of the pcr/stc
+ * pair.
+ */
+int mpq_dmx_process_pcr_packet(
+			struct dvb_demux_feed *feed,
+			const u8 *buf);
+
+/**
+ * mpq_dmx_is_video_feed - Returns whether the PES feed
+ * is video one.
+ *
+ * @feed: The feed to be checked.
+ *
+ * Return     1 if feed is video feed, 0 otherwise.
+ */
+static inline int mpq_dmx_is_video_feed(struct dvb_demux_feed *feed)
+{
+	if (feed->type != DMX_TYPE_TS)
+		return 0;
+
+	if (feed->ts_type & (~TS_DECODER))
+		return 0;
+
+	if ((feed->pes_type == DMX_TS_PES_VIDEO0) ||
+		(feed->pes_type == DMX_TS_PES_VIDEO1) ||
+		(feed->pes_type == DMX_TS_PES_VIDEO2) ||
+		(feed->pes_type == DMX_TS_PES_VIDEO3))
+		return 1;
+
+	return 0;
+}
+
+/**
+ * mpq_dmx_is_pcr_feed - Returns whether the PES feed
+ * is PCR one.
+ *
+ * @feed: The feed to be checked.
+ *
+ * Return     1 if feed is PCR feed, 0 otherwise.
+ */
+static inline int mpq_dmx_is_pcr_feed(struct dvb_demux_feed *feed)
+{
+	if (feed->type != DMX_TYPE_TS)
+		return 0;
+
+	if (feed->ts_type & (~TS_DECODER))
+		return 0;
+
+	if ((feed->pes_type == DMX_TS_PES_PCR0) ||
+		(feed->pes_type == DMX_TS_PES_PCR1) ||
+		(feed->pes_type == DMX_TS_PES_PCR2) ||
+		(feed->pes_type == DMX_TS_PES_PCR3))
+		return 1;
+
+	return 0;
+}
+
+/**
+ * mpq_dmx_init_hw_statistics -
+ * Extend dvb-demux debugfs with HW statistics.
+ *
+ * @mpq_demux: The mpq_demux device to initialize.
+ */
+void mpq_dmx_init_hw_statistics(struct mpq_demux *mpq_demux);
+
+
+/**
+ * mpq_dmx_update_hw_statistics -
+ * Update dvb-demux debugfs with HW notification statistics.
+ *
+ * @mpq_demux: The mpq_demux device to update.
+ */
+void mpq_dmx_update_hw_statistics(struct mpq_demux *mpq_demux);
+
+#endif /* _MPQ_DMX_PLUGIN_COMMON_H */
+
diff --git a/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_tsif.c b/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_tsif.c
new file mode 100644
index 0000000..5894a65
--- /dev/null
+++ b/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_tsif.c
@@ -0,0 +1,747 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/tsif_api.h>
+#include <linux/workqueue.h>
+#include <linux/moduleparam.h>
+#include "mpq_dvb_debug.h"
+#include "mpq_dmx_plugin_common.h"
+
+/* TSIF HW configuration: */
+#define TSIF_COUNT				2
+/* When TSIF driver notifies demux that new packets are received */
+#define DMX_TSIF_PACKETS_IN_CHUNK_DEF		16
+#define DMX_TSIF_CHUNKS_IN_BUF			8
+#define DMX_TSIF_TIME_LIMIT			10000
+/* TSIF_DRIVER_MODE: 3 means manual control from debugfs. use 1 normally. */
+#define DMX_TSIF_DRIVER_MODE_DEF		1
+
+
+/* module parameters for load time configuration: */
+static int threshold = DMX_TSIF_PACKETS_IN_CHUNK_DEF;
+static int mode = DMX_TSIF_DRIVER_MODE_DEF;
+module_param(threshold, int, S_IRUGO);
+module_param(mode, int, S_IRUGO);
+
+/*
+ * Work scheduled each time TSIF notifies dmx
+ * of new TS packet
+ */
+struct tsif_work {
+	struct work_struct work;
+	int tsif_id;
+};
+
+
+/*
+ * TSIF driver information
+ */
+struct tsif_driver_info {
+	/* handler to TSIF driver */
+	void *tsif_handler;
+	/* TSIF driver data buffer pointer */
+	void *data_buffer;
+	/* TSIF driver data buffer size, in packets */
+	int buffer_size;
+	/* TSIF driver read pointer */
+	int ri;
+	/* TSIF driver write pointer */
+	int wi;
+	/* TSIF driver state */
+	enum tsif_state state;
+};
+
+
+/*
+ * The following structure hold singelton information
+ * required for dmx implementation on top of TSIF.
+ */
+static struct
+{
+	/* Information for each TSIF input processing */
+	struct {
+		/* work used to submit to workqueue for processing */
+		struct tsif_work work;
+
+		/* workqueue that processes TS packets from specific TSIF */
+		struct workqueue_struct *workqueue;
+
+		/* TSIF alias */
+		char name[TSIF_NAME_LENGTH];
+
+		/* TSIF driver information */
+		struct tsif_driver_info tsif_driver;
+
+		/* TSIF reference count (counts start/stop operations */
+		int ref_count;
+
+		/* Pointer to the demux connected to this TSIF */
+		struct mpq_demux *mpq_demux;
+
+		/* mutex protecting the data-structure */
+		struct mutex mutex;
+	} tsif[TSIF_COUNT];
+} mpq_dmx_tsif_info;
+
+
+/**
+ * Worker function that processes the TS packets notified by the TSIF driver.
+ *
+ * @worker: the executed work
+ */
+static void mpq_dmx_tsif_work(struct work_struct *worker)
+{
+	struct tsif_work *tsif_work =
+		container_of(worker, struct tsif_work, work);
+	struct mpq_demux *mpq_demux;
+	struct tsif_driver_info *tsif_driver;
+	size_t packets = 0;
+	int tsif = tsif_work->tsif_id;
+
+	mpq_demux = mpq_dmx_tsif_info.tsif[tsif].mpq_demux;
+	tsif_driver = &(mpq_dmx_tsif_info.tsif[tsif].tsif_driver);
+
+	MPQ_DVB_DBG_PRINT(
+		"%s executed, tsif = %d\n",
+		__func__,
+		tsif);
+
+	if (mutex_lock_interruptible(&mpq_dmx_tsif_info.tsif[tsif].mutex))
+		return;
+
+	/* Check if driver handler is still valid */
+	if (tsif_driver->tsif_handler == NULL) {
+		mutex_unlock(&mpq_dmx_tsif_info.tsif[tsif].mutex);
+		MPQ_DVB_ERR_PRINT("%s: tsif_driver->tsif_handler is NULL!\n",
+				__func__);
+		return;
+	}
+
+	tsif_get_state(tsif_driver->tsif_handler, &(tsif_driver->ri),
+				&(tsif_driver->wi), &(tsif_driver->state));
+
+	if ((tsif_driver->wi == tsif_driver->ri) ||
+		(tsif_driver->state == tsif_state_stopped) ||
+		(tsif_driver->state == tsif_state_error)) {
+
+		mpq_demux->hw_notification_size = 0;
+
+		mutex_unlock(&mpq_dmx_tsif_info.tsif[tsif].mutex);
+
+		MPQ_DVB_ERR_PRINT(
+			"%s: invalid TSIF state (%d), wi = (%d), ri = (%d)\n",
+			__func__,
+			tsif_driver->state, tsif_driver->wi, tsif_driver->ri);
+		return;
+	}
+
+	if (tsif_driver->wi > tsif_driver->ri) {
+		packets = (tsif_driver->wi - tsif_driver->ri);
+		mpq_demux->hw_notification_size = packets;
+
+		dvb_dmx_swfilter_format(
+			&mpq_demux->demux,
+			(tsif_driver->data_buffer +
+			(tsif_driver->ri * TSIF_PKT_SIZE)),
+			(packets * TSIF_PKT_SIZE),
+			DMX_TSP_FORMAT_192_TAIL);
+
+		tsif_driver->ri =
+			(tsif_driver->ri + packets) % tsif_driver->buffer_size;
+
+		tsif_reclaim_packets(tsif_driver->tsif_handler,
+					tsif_driver->ri);
+	} else {
+		/*
+		 * wi < ri, means wraparound on cyclic buffer.
+		 * Handle in two stages.
+		 */
+		packets = (tsif_driver->buffer_size - tsif_driver->ri);
+		mpq_demux->hw_notification_size = packets;
+
+		dvb_dmx_swfilter_format(
+			&mpq_demux->demux,
+			(tsif_driver->data_buffer +
+			(tsif_driver->ri * TSIF_PKT_SIZE)),
+			(packets * TSIF_PKT_SIZE),
+			DMX_TSP_FORMAT_192_TAIL);
+
+		/* tsif_driver->ri should be 0 after this */
+		tsif_driver->ri =
+			(tsif_driver->ri + packets) % tsif_driver->buffer_size;
+
+		packets = tsif_driver->wi;
+		if (packets > 0) {
+			mpq_demux->hw_notification_size += packets;
+
+			dvb_dmx_swfilter_format(
+				&mpq_demux->demux,
+				(tsif_driver->data_buffer +
+				(tsif_driver->ri * TSIF_PKT_SIZE)),
+				(packets * TSIF_PKT_SIZE),
+				DMX_TSP_FORMAT_192_TAIL);
+
+			tsif_driver->ri =
+				(tsif_driver->ri + packets) %
+				tsif_driver->buffer_size;
+		}
+
+		tsif_reclaim_packets(tsif_driver->tsif_handler,
+					tsif_driver->ri);
+	}
+
+	mutex_unlock(&mpq_dmx_tsif_info.tsif[tsif].mutex);
+}
+
+
+/**
+ * Callback function from TSIF driver when new data is ready.
+ *
+ * @user: user-data holding TSIF number
+ */
+static void mpq_tsif_callback(void *user)
+{
+	int tsif = (int)user;
+	struct work_struct *work;
+	struct mpq_demux *mpq_demux;
+
+	MPQ_DVB_DBG_PRINT("%s executed, tsif = %d\n", __func__,	tsif);
+
+	/* Save statistics on TSIF notifications */
+	mpq_demux = mpq_dmx_tsif_info.tsif[tsif].mpq_demux;
+	mpq_dmx_update_hw_statistics(mpq_demux);
+
+	work = &mpq_dmx_tsif_info.tsif[tsif].work.work;
+
+	/* Scheudle a new work to demux workqueue */
+	if (!work_pending(work))
+		queue_work(mpq_dmx_tsif_info.tsif[tsif].workqueue, work);
+}
+
+
+/**
+ * Attach to TSIF driver and start TSIF operation.
+ *
+ * @mpq_demux: the mpq_demux we are working on.
+ *
+ * Return	error code.
+ */
+static int mpq_tsif_dmx_start(struct mpq_demux *mpq_demux)
+{
+	int ret = 0;
+	int tsif;
+	struct tsif_driver_info *tsif_driver;
+
+	MPQ_DVB_DBG_PRINT("%s executed\n", __func__);
+
+	/* determine the TSIF we are reading from */
+	if (mpq_demux->source == DMX_SOURCE_FRONT0) {
+		tsif = 0;
+	} else if (mpq_demux->source == DMX_SOURCE_FRONT1) {
+		tsif = 1;
+	} else {
+		/* invalid source */
+		MPQ_DVB_ERR_PRINT(
+			"%s: invalid input source (%d)\n",
+			__func__,
+			mpq_demux->source);
+
+		return -EINVAL;
+	}
+
+	if (mutex_lock_interruptible(&mpq_dmx_tsif_info.tsif[tsif].mutex))
+		return -ERESTARTSYS;
+
+	if (mpq_dmx_tsif_info.tsif[tsif].ref_count == 0) {
+		tsif_driver = &(mpq_dmx_tsif_info.tsif[tsif].tsif_driver);
+
+		/* Attach to TSIF driver */
+
+		tsif_driver->tsif_handler =
+			tsif_attach(tsif, mpq_tsif_callback, (void *)tsif);
+		if (IS_ERR_OR_NULL(tsif_driver->tsif_handler)) {
+			tsif_driver->tsif_handler = NULL;
+			mutex_unlock(&mpq_dmx_tsif_info.tsif[tsif].mutex);
+			MPQ_DVB_DBG_PRINT("%s: tsif_attach(%d) failed\n",
+					__func__, tsif);
+			return -ENODEV;
+		}
+
+		/* Set TSIF driver mode */
+		ret = tsif_set_mode(tsif_driver->tsif_handler,
+					mode);
+		if (ret < 0) {
+			MPQ_DVB_ERR_PRINT("%s: tsif_set_mode (%d) failed\n",
+				__func__, mode);
+		}
+
+		/* Set TSIF buffer configuration */
+		ret = tsif_set_buf_config(tsif_driver->tsif_handler,
+						threshold,
+						DMX_TSIF_CHUNKS_IN_BUF);
+		if (ret < 0) {
+			MPQ_DVB_ERR_PRINT(
+				"%s: tsif_set_buf_config (%d, %d) failed\n",
+				__func__, threshold,
+				DMX_TSIF_CHUNKS_IN_BUF);
+			MPQ_DVB_ERR_PRINT("Using default TSIF driver values\n");
+		}
+
+
+		/* Set TSIF driver time limit */
+		/* TODO: needed?? */
+/*		ret = tsif_set_time_limit(tsif_driver->tsif_handler,
+						DMX_TSIF_TIME_LIMIT);
+		if (ret < 0) {
+			MPQ_DVB_ERR_PRINT(
+				"%s: tsif_set_time_limit (%d) failed\n",
+				__func__, DMX_TSIF_TIME_LIMIT);
+		}
+*/
+
+		/* Start TSIF driver */
+		ret = tsif_start(tsif_driver->tsif_handler);
+		if (ret < 0) {
+			mutex_unlock(&mpq_dmx_tsif_info.tsif[tsif].mutex);
+			MPQ_DVB_ERR_PRINT("%s: tsif_start failed\n", __func__);
+			return ret;
+		}
+
+		/*
+		 * Get data buffer information from TSIF driver
+		 * (must be called after tsif_start)
+		 */
+		tsif_get_info(tsif_driver->tsif_handler,
+				&(tsif_driver->data_buffer),
+				&(tsif_driver->buffer_size));
+
+		/* save pointer to the mpq_demux we are working on */
+		mpq_dmx_tsif_info.tsif[tsif].mpq_demux = mpq_demux;
+	}
+	mpq_dmx_tsif_info.tsif[tsif].ref_count++;
+
+	mutex_unlock(&mpq_dmx_tsif_info.tsif[tsif].mutex);
+
+	return ret;
+}
+
+
+/**
+ * Stop TSIF operation and detach from TSIF driver.
+ *
+ * @mpq_demux: the mpq_demux we are working on.
+ *
+ * Return	error code.
+ */
+static int mpq_tsif_dmx_stop(struct mpq_demux *mpq_demux)
+{
+	int tsif;
+	struct tsif_driver_info *tsif_driver;
+
+	MPQ_DVB_DBG_PRINT("%s executed\n", __func__);
+
+	/* determine the TSIF we are reading from */
+	if (mpq_demux->source == DMX_SOURCE_FRONT0) {
+		tsif = 0;
+	} else if (mpq_demux->source == DMX_SOURCE_FRONT1) {
+		tsif = 1;
+	} else {
+		/* invalid source */
+		MPQ_DVB_ERR_PRINT(
+			"%s: invalid input source (%d)\n",
+			__func__,
+			mpq_demux->source);
+
+		return -EINVAL;
+	}
+
+	if (mutex_lock_interruptible(&mpq_dmx_tsif_info.tsif[tsif].mutex))
+		return -ERESTARTSYS;
+
+	mpq_dmx_tsif_info.tsif[tsif].ref_count--;
+
+	if (mpq_dmx_tsif_info.tsif[tsif].ref_count == 0) {
+		tsif_driver = &(mpq_dmx_tsif_info.tsif[tsif].tsif_driver);
+		tsif_stop(tsif_driver->tsif_handler);
+		tsif_detach(tsif_driver->tsif_handler);
+		/*
+		 * temporarily release mutex and flush the work queue
+		 * before setting tsif_handler to NULL
+		 */
+		mutex_unlock(&mpq_dmx_tsif_info.tsif[tsif].mutex);
+		flush_workqueue(mpq_dmx_tsif_info.tsif[tsif].workqueue);
+		/* re-acquire mutex */
+		if (mutex_lock_interruptible(
+				&mpq_dmx_tsif_info.tsif[tsif].mutex))
+			return -ERESTARTSYS;
+
+		tsif_driver->tsif_handler = NULL;
+		tsif_driver->data_buffer = NULL;
+		tsif_driver->buffer_size = 0;
+		mpq_dmx_tsif_info.tsif[tsif].mpq_demux = NULL;
+	}
+
+	mutex_unlock(&mpq_dmx_tsif_info.tsif[tsif].mutex);
+
+	return 0;
+}
+
+
+/**
+ * Start filtering according to feed parameter.
+ *
+ * @feed: the feed we are working on.
+ *
+ * Return	error code.
+ */
+static int mpq_tsif_dmx_start_filtering(struct dvb_demux_feed *feed)
+{
+	int ret = 0;
+	struct mpq_demux *mpq_demux =
+		(struct mpq_demux *)feed->demux->priv;
+
+	MPQ_DVB_DBG_PRINT(
+		"%s(%d) executed\n",
+		__func__,
+		feed->pid);
+
+	if (mpq_demux == NULL) {
+		MPQ_DVB_ERR_PRINT(
+			"%s: invalid mpq_demux handle\n",
+			__func__);
+
+		return -EINVAL;
+	}
+
+	if (mpq_demux->source < DMX_SOURCE_DVR0) {
+		/* Source from TSIF, need to configure TSIF hardware */
+		ret = mpq_tsif_dmx_start(mpq_demux);
+
+		if (ret < 0) {
+			MPQ_DVB_DBG_PRINT(
+				"%s: mpq_tsif_dmx_start failed(%d)\n",
+				__func__,
+				ret);
+			return ret;
+		}
+	}
+
+	/* Always feed sections/PES starting from a new one and
+	 * do not partial transfer data from older one
+	 */
+	feed->pusi_seen = 0;
+
+	/*
+	 * For video PES, data is tunneled to the decoder,
+	 * initialize tunneling and pes parsing.
+	 */
+	if (mpq_dmx_is_video_feed(feed)) {
+		ret = mpq_dmx_init_video_feed(feed);
+
+		if (ret < 0) {
+			MPQ_DVB_DBG_PRINT(
+				"%s: mpq_dmx_init_video_feed failed(%d)\n",
+				__func__,
+				ret);
+
+			if (mpq_demux->source < DMX_SOURCE_DVR0)
+				mpq_tsif_dmx_stop(mpq_demux);
+
+			return ret;
+		}
+	}
+
+	return ret;
+}
+
+
+/**
+ * Stop filtering according to feed parameter.
+ *
+ * @feed: the feed we are working on.
+ *
+ * Return	error code.
+ */
+static int mpq_tsif_dmx_stop_filtering(struct dvb_demux_feed *feed)
+{
+	int ret = 0;
+	struct mpq_demux *mpq_demux =
+		(struct mpq_demux *)feed->demux->priv;
+
+	MPQ_DVB_DBG_PRINT(
+		"%s(%d) executed\n",
+		__func__,
+		feed->pid);
+
+	if (mpq_demux == NULL) {
+		MPQ_DVB_ERR_PRINT(
+			"%s: invalid mpq_demux handle\n",
+			__func__);
+
+		return -EINVAL;
+	}
+
+	/*
+	 * For video PES, data is tunneled to the decoder,
+	 * terminate tunnel and pes parsing.
+	 */
+	if (mpq_dmx_is_video_feed(feed))
+		mpq_dmx_terminate_video_feed(feed);
+
+	if (mpq_demux->source < DMX_SOURCE_DVR0) {
+		/* Source from TSIF, need to configure TSIF hardware */
+		ret = mpq_tsif_dmx_stop(mpq_demux);
+	}
+
+	return ret;
+}
+
+
+/**
+ * TSIF demux plugin write-to-decoder function.
+ *
+ * @feed: The feed we are working on.
+ * @buf: The data buffer to process.
+ * @len: The data buffer length.
+ *
+ * Return	error code
+ */
+static int mpq_tsif_dmx_write_to_decoder(
+					struct dvb_demux_feed *feed,
+					const u8 *buf,
+					size_t len)
+{
+	MPQ_DVB_DBG_PRINT("%s executed\n", __func__);
+
+	/*
+	 * It is assumed that this function is called once for each
+	 * TS packet of the relevant feed.
+	 */
+	if (len > TSIF_PKT_SIZE)
+		MPQ_DVB_DBG_PRINT(
+				"%s: warnning - len larger than one packet\n",
+				__func__);
+
+	if (mpq_dmx_is_video_feed(feed))
+		return mpq_dmx_process_video_packet(feed, buf);
+
+	if (mpq_dmx_is_pcr_feed(feed))
+		return mpq_dmx_process_pcr_packet(feed, buf);
+
+	return 0;
+}
+
+
+/**
+ * Initialize a single demux device.
+ *
+ * @mpq_adapter: MPQ DVB adapter
+ * @mpq_demux: The demux device to initialize
+ *
+ * Return	error code
+ */
+static int mpq_tsif_dmx_init(
+		struct dvb_adapter *mpq_adapter,
+		struct mpq_demux *mpq_demux)
+{
+	int result;
+
+	MPQ_DVB_DBG_PRINT("%s executed\n", __func__);
+
+	/* Set the kernel-demux object capabilities */
+	mpq_demux->demux.dmx.capabilities =
+		DMX_TS_FILTERING		|
+		DMX_PES_FILTERING		|
+		DMX_SECTION_FILTERING		|
+		DMX_MEMORY_BASED_FILTERING	|
+		DMX_CRC_CHECKING		|
+		DMX_TS_DESCRAMBLING;
+
+	/* Set dvb-demux "virtual" function pointers */
+	mpq_demux->demux.priv = (void *)mpq_demux;
+	mpq_demux->demux.filternum = MPQ_DMX_MAX_NUM_OF_FILTERS;
+	mpq_demux->demux.feednum = MPQ_DMX_MAX_NUM_OF_FILTERS;
+	mpq_demux->demux.start_feed = mpq_tsif_dmx_start_filtering;
+	mpq_demux->demux.stop_feed = mpq_tsif_dmx_stop_filtering;
+	mpq_demux->demux.write_to_decoder = mpq_tsif_dmx_write_to_decoder;
+
+	mpq_demux->demux.decoder_fullness_init =
+		mpq_dmx_decoder_fullness_init;
+
+	mpq_demux->demux.decoder_fullness_wait =
+		mpq_dmx_decoder_fullness_wait;
+
+	mpq_demux->demux.decoder_fullness_abort =
+		mpq_dmx_decoder_fullness_abort;
+
+	/* Initialize dvb_demux object */
+	result = dvb_dmx_init(&mpq_demux->demux);
+	if (result < 0) {
+		MPQ_DVB_ERR_PRINT("%s: dvb_dmx_init failed\n", __func__);
+		goto init_failed;
+	}
+
+	/* Now initailize the dmx-dev object */
+	mpq_demux->dmxdev.filternum = MPQ_DMX_MAX_NUM_OF_FILTERS;
+	mpq_demux->dmxdev.demux = &mpq_demux->demux.dmx;
+	mpq_demux->dmxdev.capabilities =
+		DMXDEV_CAP_DUPLEX |
+		DMXDEV_CAP_PULL_MODE |
+		DMXDEV_CAP_PCR_EXTRACTION;
+
+	mpq_demux->dmxdev.demux->set_source = mpq_dmx_set_source;
+
+	result = dvb_dmxdev_init(&mpq_demux->dmxdev, mpq_adapter);
+	if (result < 0) {
+		MPQ_DVB_ERR_PRINT("%s: dvb_dmxdev_init failed (errno=%d)\n",
+						  __func__,
+						  result);
+		goto init_failed_dmx_release;
+	}
+
+	/* Extend dvb-demux debugfs with TSIF statistics. */
+	mpq_dmx_init_hw_statistics(mpq_demux);
+
+	return 0;
+
+init_failed_dmx_release:
+	dvb_dmx_release(&mpq_demux->demux);
+init_failed:
+	return result;
+}
+
+
+/**
+ * Module initialization function.
+ *
+ * Return	error code
+ */
+static int __init mpq_dmx_tsif_plugin_init(void)
+{
+	int i;
+	int ret;
+
+	MPQ_DVB_DBG_PRINT("%s executed\n", __func__);
+
+	/* check module parameters validity */
+	if (threshold < 1) {
+		MPQ_DVB_ERR_PRINT(
+			"%s: invalid threshold parameter, using %d instead\n",
+			__func__, DMX_TSIF_PACKETS_IN_CHUNK_DEF);
+		threshold = DMX_TSIF_PACKETS_IN_CHUNK_DEF;
+	}
+	if ((mode < 1) || (mode > 3)) {
+		MPQ_DVB_ERR_PRINT(
+			"%s: invalid mode parameter, using %d instead\n",
+			__func__, DMX_TSIF_DRIVER_MODE_DEF);
+		mode = DMX_TSIF_DRIVER_MODE_DEF;
+	}
+
+	for (i = 0; i < TSIF_COUNT; i++) {
+		mpq_dmx_tsif_info.tsif[i].work.tsif_id = i;
+
+		INIT_WORK(&mpq_dmx_tsif_info.tsif[i].work.work,
+				  mpq_dmx_tsif_work);
+
+		snprintf(mpq_dmx_tsif_info.tsif[i].name,
+				TSIF_NAME_LENGTH,
+				"tsif_%d",
+				i);
+
+		mpq_dmx_tsif_info.tsif[i].workqueue =
+			create_singlethread_workqueue(
+				mpq_dmx_tsif_info.tsif[i].name);
+
+		if (mpq_dmx_tsif_info.tsif[i].workqueue == NULL) {
+			int j;
+
+			for (j = 0; j < i; j++) {
+				destroy_workqueue(
+					mpq_dmx_tsif_info.tsif[j].workqueue);
+				mutex_destroy(&mpq_dmx_tsif_info.tsif[j].mutex);
+			}
+
+			MPQ_DVB_ERR_PRINT(
+				"%s: create_singlethread_workqueue failed\n",
+				__func__);
+
+			return -ENOMEM;
+		}
+
+		mutex_init(&mpq_dmx_tsif_info.tsif[i].mutex);
+
+		mpq_dmx_tsif_info.tsif[i].tsif_driver.tsif_handler = NULL;
+		mpq_dmx_tsif_info.tsif[i].ref_count = 0;
+	}
+
+	ret = mpq_dmx_plugin_init(mpq_tsif_dmx_init);
+
+	if (ret < 0) {
+		MPQ_DVB_ERR_PRINT(
+			"%s: mpq_dmx_plugin_init failed (errno=%d)\n",
+			__func__,
+			ret);
+
+		for (i = 0; i < TSIF_COUNT; i++) {
+			destroy_workqueue(mpq_dmx_tsif_info.tsif[i].workqueue);
+			mutex_destroy(&mpq_dmx_tsif_info.tsif[i].mutex);
+		}
+	}
+
+	return ret;
+}
+
+
+/**
+ * Module exit function.
+ */
+static void __exit mpq_dmx_tsif_plugin_exit(void)
+{
+	int i;
+	struct tsif_driver_info *tsif_driver;
+
+	MPQ_DVB_DBG_PRINT("%s executed\n", __func__);
+
+	for (i = 0; i < TSIF_COUNT; i++) {
+		mutex_lock(&mpq_dmx_tsif_info.tsif[i].mutex);
+
+		tsif_driver = &(mpq_dmx_tsif_info.tsif[i].tsif_driver);
+		if (mpq_dmx_tsif_info.tsif[i].ref_count > 0) {
+			mpq_dmx_tsif_info.tsif[i].ref_count = 0;
+			if (tsif_driver->tsif_handler)
+				tsif_stop(tsif_driver->tsif_handler);
+		}
+		/* Detach from TSIF driver to avoid further notifications. */
+		if (tsif_driver->tsif_handler)
+			tsif_detach(tsif_driver->tsif_handler);
+
+		/* release mutex to allow work queue to finish scheduled work */
+		mutex_unlock(&mpq_dmx_tsif_info.tsif[i].mutex);
+		/* flush the work queue and destroy it */
+		flush_workqueue(mpq_dmx_tsif_info.tsif[i].workqueue);
+		destroy_workqueue(mpq_dmx_tsif_info.tsif[i].workqueue);
+
+		mutex_destroy(&mpq_dmx_tsif_info.tsif[i].mutex);
+	}
+
+	mpq_dmx_plugin_exit();
+}
+
+
+module_init(mpq_dmx_tsif_plugin_init);
+module_exit(mpq_dmx_tsif_plugin_exit);
+
+MODULE_DESCRIPTION("Qualcomm demux TSIF HW Plugin");
+MODULE_LICENSE("GPL v2");
+
diff --git a/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_tspp_v1.c b/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_tspp_v1.c
new file mode 100644
index 0000000..406ae52
--- /dev/null
+++ b/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_tspp_v1.c
@@ -0,0 +1,847 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/workqueue.h>
+#include <mach/msm_tspp.h>
+#include "mpq_dvb_debug.h"
+#include "mpq_dmx_plugin_common.h"
+
+
+#define TSIF_COUNT						2
+
+#define TSPP_FILTERS_COUNT				16
+
+/* For each TSIF we allocate two pipes, one for PES and one for sections */
+#define TSPP_PES_CHANNEL				0
+#define TSPP_SECTION_CHANNEL			1
+
+/* the channel_id set to TSPP driver based on TSIF number and channel type */
+#define TSPP_CHANNEL_ID(tsif, ch)		((tsif << 1) + ch)
+#define TSPP_IS_PES_CHANNEL(ch_id)		((ch_id & 0x1) == 0)
+#define TSPP_GET_TSIF_NUM(ch_id)		(ch_id >> 1)
+
+/* mask that set to care for all bits in pid filter */
+#define TSPP_PID_MASK					0x1FFF
+
+/* dvb-demux defines pid 0x2000 as full capture pid */
+#define TSPP_PASS_THROUGH_PID			0x2000
+
+/* TODO - NEED TO SET THESE PROPERLY
+ * once TSPP driver is ready, reduce TSPP_BUFFER_SIZE
+ * to single packet and set TSPP_BUFFER_COUNT accordingly
+ */
+
+#define TSPP_RAW_TTS_SIZE				192
+
+/* Size of single descriptor */
+#define TSPP_BUFFER_SIZE				(TSPP_RAW_TTS_SIZE * 35)
+
+/* Number of descriptors, total size: TSPP_BUFFER_SIZE*TSPP_BUFFER_COUNT */
+#define TSPP_BUFFER_COUNT				(16)
+
+/* When TSPP notifies demux that new packets are received */
+#define TSPP_NOTIFICATION_SIZE			(TSPP_RAW_TTS_SIZE * 100)
+
+/* Channel timeout in msec */
+#define TSPP_CHANNEL_TIMEOUT			16
+
+
+/*
+ * Work scheduled each time TSPP notifies dmx
+ * of new TS packet in some channel
+ */
+struct tspp_work {
+	struct work_struct work;
+	int channel_id;
+};
+
+/* The following structure hold singelton information
+ * required for dmx implementation on top of TSPP.
+ */
+static struct
+{
+	/* Information for each TSIF input processing */
+	struct {
+		/*
+		 * TSPP pipe holding all TS packets with PES data.
+		 * The following is reference count for number of feeds
+		 * allocated on that pipe.
+		 */
+		int pes_channel_ref;
+
+		/* work used to submit to workqueue to process pes channel */
+		struct tspp_work pes_work;
+
+		/*
+		 * TSPP pipe holding all TS packets with section data.
+		 * The following is reference count for number of feeds
+		 * allocated on that pipe.
+		 */
+		int section_channel_ref;
+
+		/* work used to submit to workqueue to process pes channel */
+		struct tspp_work section_work;
+
+		/*
+		 * Holds PIDs of allocated TSPP filters along with
+		 * how many feeds are opened on same PID.
+		 */
+		struct {
+			int pid;
+			int ref_count;
+		} filters[TSPP_FILTERS_COUNT];
+
+		/* workqueue that processes TS packets from specific TSIF */
+		struct workqueue_struct *workqueue;
+
+		/* TSIF alias */
+		char name[TSIF_NAME_LENGTH];
+
+		/* Pointer to the demux connected to this TSIF */
+		struct mpq_demux *mpq_demux;
+
+		/* mutex protecting the data-structure */
+		struct mutex mutex;
+	} tsif[TSIF_COUNT];
+} mpq_dmx_tspp_info;
+
+
+/**
+ * Returns a free filter slot that can be used.
+ *
+ * @tsif: The TSIF to allocate filter from
+ * @channel_id: The channel allocating filter to
+ *
+ * Return  filter index or -1 if no filters available
+ *
+ * To give priority to PES data, for pes filters
+ * the table is scanned from high to low priority,
+ * and sections from low to high priority. This way TSPP
+ * would get a match on PES data filters faster as they
+ * are scanned first.
+ */
+static int mpq_tspp_get_free_filter_slot(int tsif, int channel_id)
+{
+	int i;
+
+	if (TSPP_IS_PES_CHANNEL(channel_id)) {
+		for (i = 0; i < TSPP_FILTERS_COUNT; i++)
+			if (mpq_dmx_tspp_info.tsif[tsif].filters[i].pid == -1)
+				return i;
+	} else {
+		for (i = TSPP_FILTERS_COUNT-1; i >= 0; i--)
+			if (mpq_dmx_tspp_info.tsif[tsif].filters[i].pid == -1)
+				return i;
+	}
+
+	return -ENOMEM;
+}
+
+/**
+ * Returns filter index of specific pid.
+ *
+ * @tsif: The TSIF to which the pid is allocated
+ * @pid: The pid to search for
+ *
+ * Return  filter index or -1 if no filter available
+ */
+static int mpq_tspp_get_filter_slot(int tsif, int pid)
+{
+	int i;
+
+	for (i = 0; i < TSPP_FILTERS_COUNT; i++)
+		if (mpq_dmx_tspp_info.tsif[tsif].filters[i].pid == pid)
+			return i;
+
+	return -EINVAL;
+}
+
+/**
+ * Worker function that processes the TS packets notified by TSPP.
+ *
+ * @worker: the executed work
+ */
+static void mpq_dmx_tspp_work(struct work_struct *worker)
+{
+	struct tspp_work *tspp_work =
+		container_of(worker, struct tspp_work, work);
+	struct mpq_demux *mpq_demux;
+	int channel_id = tspp_work->channel_id;
+	int tsif = TSPP_GET_TSIF_NUM(channel_id);
+	const struct tspp_data_descriptor *tspp_data_desc;
+	int ref_count;
+
+	mpq_demux = mpq_dmx_tspp_info.tsif[tsif].mpq_demux;
+
+	/* Lock against the TSPP filters data-structure */
+	if (mutex_lock_interruptible(&mpq_dmx_tspp_info.tsif[tsif].mutex))
+		return;
+
+	/* Make sure channel is still active */
+	if (TSPP_IS_PES_CHANNEL(channel_id))
+		ref_count = mpq_dmx_tspp_info.tsif[tsif].pes_channel_ref;
+	else
+		ref_count = mpq_dmx_tspp_info.tsif[tsif].section_channel_ref;
+
+	if (ref_count == 0) {
+		mutex_unlock(&mpq_dmx_tspp_info.tsif[tsif].mutex);
+		return;
+	}
+
+	mpq_demux->hw_notification_size = 0;
+
+	/* Go through all filled descriptors and perform demuxing on them */
+	while ((tspp_data_desc = tspp_get_buffer(0, channel_id)) != NULL) {
+		mpq_demux->hw_notification_size +=
+			(tspp_data_desc->size / TSPP_RAW_TTS_SIZE);
+
+		dvb_dmx_swfilter_format(
+				&mpq_demux->demux,
+				tspp_data_desc->virt_base,
+				tspp_data_desc->size,
+				DMX_TSP_FORMAT_192_TAIL);
+
+		/* Notify TSPP that the buffer is no longer needed */
+		tspp_release_buffer(0, channel_id, tspp_data_desc->id);
+	}
+
+	mutex_unlock(&mpq_dmx_tspp_info.tsif[tsif].mutex);
+}
+
+/**
+ * Callback function from TSPP when new data is ready.
+ *
+ * @channel_id: Channel with new TS packets
+ * @user: user-data holding TSIF number
+ */
+static void mpq_tspp_callback(u32 channel_id, void *user)
+{
+	int tsif = (int)user;
+	struct work_struct *work;
+	struct mpq_demux *mpq_demux;
+
+	/* Save statistics on TSPP notifications */
+	mpq_demux = mpq_dmx_tspp_info.tsif[tsif].mpq_demux;
+	mpq_dmx_update_hw_statistics(mpq_demux);
+
+	if (TSPP_IS_PES_CHANNEL(channel_id))
+		work = &mpq_dmx_tspp_info.tsif[tsif].pes_work.work;
+	else
+		work = &mpq_dmx_tspp_info.tsif[tsif].section_work.work;
+
+	/* Scheudle a new work to demux workqueue */
+	if (!work_pending(work))
+		queue_work(mpq_dmx_tspp_info.tsif[tsif].workqueue, work);
+}
+
+/**
+ * Configure TSPP channel to filter the PID of new feed.
+ *
+ * @feed: The feed to configure the channel with
+ *
+ * Return  error status
+ *
+ * The function checks if the new PID can be added to an already
+ * allocated channel, if not, a new channel is allocated and configured.
+ */
+static int mpq_tspp_dmx_add_channel(struct dvb_demux_feed *feed)
+{
+	struct mpq_demux *mpq_demux = (struct mpq_demux *)feed->demux->priv;
+	enum tspp_source tspp_source;
+	struct tspp_filter tspp_filter;
+	int tsif;
+	int ret;
+	int channel_id;
+	int *channel_ref_count;
+
+	/* determine the TSIF we are reading from */
+	if (mpq_demux->source == DMX_SOURCE_FRONT0) {
+		tsif = 0;
+		tspp_source = TSPP_SOURCE_TSIF0;
+	} else if (mpq_demux->source == DMX_SOURCE_FRONT1) {
+		tsif = 1;
+		tspp_source = TSPP_SOURCE_TSIF1;
+	} else {
+		/* invalid source */
+		MPQ_DVB_ERR_PRINT(
+			"%s: invalid input source (%d)\n",
+			__func__,
+			mpq_demux->source);
+
+		return -EINVAL;
+	}
+
+	if (mutex_lock_interruptible(&mpq_dmx_tspp_info.tsif[tsif].mutex))
+		return -ERESTARTSYS;
+
+	/*
+	 * It is possible that this PID was already requested before.
+	 * Can happen if we play and record same PES or PCR
+	 * piggypacked on video packet.
+	 */
+	ret = mpq_tspp_get_filter_slot(tsif, feed->pid);
+	if (ret >= 0) {
+		/* PID already configured */
+		mpq_dmx_tspp_info.tsif[tsif].filters[ret].ref_count++;
+		mutex_unlock(&mpq_dmx_tspp_info.tsif[tsif].mutex);
+		return 0;
+	}
+
+	/* determine to which pipe the feed should be routed: section or pes */
+	if ((feed->type == DMX_TYPE_PES) || (feed->type == DMX_TYPE_TS)) {
+		channel_id = TSPP_CHANNEL_ID(tsif, TSPP_PES_CHANNEL);
+		channel_ref_count =
+			&mpq_dmx_tspp_info.tsif[tsif].pes_channel_ref;
+	} else {
+		channel_id = TSPP_CHANNEL_ID(tsif, TSPP_SECTION_CHANNEL);
+		channel_ref_count =
+			&mpq_dmx_tspp_info.tsif[tsif].section_channel_ref;
+	}
+
+	/* check if required TSPP pipe is already allocated or not */
+	if (*channel_ref_count == 0) {
+		ret = tspp_open_channel(0, channel_id);
+
+		if (ret < 0) {
+			MPQ_DVB_ERR_PRINT(
+				"%s: tspp_open_channel(%d) failed (%d)\n",
+				__func__,
+				channel_id,
+				ret);
+
+			goto add_channel_failed;
+		}
+
+		/* set TSPP source */
+		ret = tspp_open_stream(0, channel_id, tspp_source);
+		if (ret < 0) {
+			MPQ_DVB_ERR_PRINT(
+				"%s: tspp_select_source(%d,%d) failed (%d)\n",
+				__func__,
+				channel_id,
+				tspp_source,
+				ret);
+
+			goto add_channel_close_ch;
+		}
+
+		/* register notification on TS packets */
+		tspp_register_notification(0,
+					   channel_id,
+					   mpq_tspp_callback,
+					   (void *)tsif,
+					   TSPP_CHANNEL_TIMEOUT);
+
+		/* TODO: register allocater and provide allocation function
+		 * that allocate from continous memory so that we can have
+		 * big notification size, smallest descriptor, and still provide
+		 * TZ with single big buffer based on notification size.
+		 */
+
+		/* set buffer/descriptor size and count */
+		ret = tspp_allocate_buffers(0,
+					    channel_id,
+					    TSPP_BUFFER_COUNT,
+					    TSPP_BUFFER_SIZE,
+					    TSPP_NOTIFICATION_SIZE,
+					    NULL,
+					    NULL);
+		if (ret < 0) {
+			MPQ_DVB_ERR_PRINT(
+				"%s: tspp_allocate_buffers(%d) failed (%d)\n",
+				__func__,
+				channel_id,
+				ret);
+
+			goto add_channel_unregister_notif;
+		}
+
+		mpq_dmx_tspp_info.tsif[tsif].mpq_demux = mpq_demux;
+	}
+
+	/* add new PID to the existing pipe */
+	ret = mpq_tspp_get_free_filter_slot(tsif, channel_id);
+	if (ret < 0) {
+		MPQ_DVB_ERR_PRINT(
+			"%s: mpq_allocate_filter_slot(%d, %d) failed\n",
+			__func__,
+			tsif,
+			channel_id);
+
+		goto add_channel_unregister_notif;
+	}
+
+	mpq_dmx_tspp_info.tsif[tsif].filters[ret].pid = feed->pid;
+	mpq_dmx_tspp_info.tsif[tsif].filters[ret].ref_count++;
+
+	tspp_filter.priority = ret;
+	if (feed->pid == TSPP_PASS_THROUGH_PID) {
+		/* pass all pids */
+		tspp_filter.pid = 0;
+		tspp_filter.mask = 0;
+	} else {
+		tspp_filter.pid = feed->pid;
+		tspp_filter.mask = TSPP_PID_MASK;
+	}
+
+	/*
+	 * Include TTS in RAW packets, if you change this to
+	 * TSPP_MODE_RAW_NO_SUFFIX you must also change TSPP_RAW_TTS_SIZE
+	 * accordingly.
+	 */
+	tspp_filter.mode = TSPP_MODE_RAW;
+	tspp_filter.source = tspp_source;
+	tspp_filter.decrypt = 0;
+	ret = tspp_add_filter(0, channel_id, &tspp_filter);
+	if (ret < 0) {
+		MPQ_DVB_ERR_PRINT(
+			"%s: tspp_add_filter(%d) failed (%d)\n",
+			__func__,
+			channel_id,
+			ret);
+
+		goto add_channel_free_filter_slot;
+	}
+
+	(*channel_ref_count)++;
+
+	mutex_unlock(&mpq_dmx_tspp_info.tsif[tsif].mutex);
+	return 0;
+
+add_channel_free_filter_slot:
+	mpq_dmx_tspp_info.tsif[tsif].filters[tspp_filter.priority].pid = -1;
+	mpq_dmx_tspp_info.tsif[tsif].filters[tspp_filter.priority].ref_count--;
+add_channel_unregister_notif:
+	tspp_unregister_notification(0, channel_id);
+add_channel_close_ch:
+	tspp_close_channel(0, channel_id);
+add_channel_failed:
+	mutex_unlock(&mpq_dmx_tspp_info.tsif[tsif].mutex);
+	return ret;
+}
+
+/**
+ * Removes filter from TSPP.
+ *
+ * @feed: The feed to remove
+ *
+ * Return  error status
+ *
+ * The function checks if this is the only PID allocated within
+ * the channel, if so, the channel is closed as well.
+ */
+static int mpq_tspp_dmx_remove_channel(struct dvb_demux_feed *feed)
+{
+	int tsif;
+	int ret;
+	int channel_id;
+	int *channel_ref_count;
+	struct tspp_filter tspp_filter;
+	struct mpq_demux *mpq_demux = (struct mpq_demux *)feed->demux->priv;
+
+	/* determine the TSIF we are reading from */
+	if (mpq_demux->source == DMX_SOURCE_FRONT0) {
+		tsif = 0;
+	} else if (mpq_demux->source == DMX_SOURCE_FRONT1) {
+		tsif = 1;
+	} else {
+		/* invalid source */
+		MPQ_DVB_ERR_PRINT(
+			"%s: invalid input source (%d)\n",
+			__func__,
+			mpq_demux->source);
+
+		return -EINVAL;
+	}
+
+	if (mutex_lock_interruptible(&mpq_dmx_tspp_info.tsif[tsif].mutex))
+		return -ERESTARTSYS;
+
+	/* determine to which pipe the feed should be routed: section or pes */
+	if ((feed->type == DMX_TYPE_PES) || (feed->type == DMX_TYPE_TS)) {
+		channel_id = TSPP_CHANNEL_ID(tsif, TSPP_PES_CHANNEL);
+		channel_ref_count =
+			&mpq_dmx_tspp_info.tsif[tsif].pes_channel_ref;
+	} else {
+		channel_id = TSPP_CHANNEL_ID(tsif, TSPP_SECTION_CHANNEL);
+		channel_ref_count =
+			&mpq_dmx_tspp_info.tsif[tsif].section_channel_ref;
+	}
+
+	/* check if required TSPP pipe is already allocated or not */
+	if (*channel_ref_count == 0) {
+		/* invalid feed provided as the channel is not allocated */
+		MPQ_DVB_ERR_PRINT(
+			"%s: invalid feed (%d)\n",
+			__func__,
+			channel_id);
+
+		ret = -EINVAL;
+		goto remove_channel_failed;
+	}
+
+	tspp_filter.priority = mpq_tspp_get_filter_slot(tsif, feed->pid);
+
+	if (tspp_filter.priority < 0) {
+		/* invalid feed provided as it has no filter allocated */
+		MPQ_DVB_ERR_PRINT(
+			"%s: mpq_tspp_get_filter_slot failed (%d,%d)\n",
+			__func__,
+			feed->pid,
+			tsif);
+
+		ret = -EINVAL;
+		goto remove_channel_failed;
+	}
+
+	mpq_dmx_tspp_info.tsif[tsif].filters[tspp_filter.priority].ref_count--;
+
+	if (mpq_dmx_tspp_info.tsif[tsif].
+		filters[tspp_filter.priority].ref_count) {
+		/*
+		 * there are still references to this pid, do not
+		 * remove the filter yet
+		 */
+		mutex_unlock(&mpq_dmx_tspp_info.tsif[tsif].mutex);
+		return 0;
+	}
+
+	ret = tspp_remove_filter(0, channel_id, &tspp_filter);
+	if (ret < 0) {
+		/* invalid feed provided as it has no filter allocated */
+		MPQ_DVB_ERR_PRINT(
+			"%s: tspp_remove_filter failed (%d,%d)\n",
+			__func__,
+			channel_id,
+			tspp_filter.priority);
+
+		goto remove_channel_failed_restore_count;
+	}
+
+	mpq_dmx_tspp_info.tsif[tsif].filters[tspp_filter.priority].pid = -1;
+	(*channel_ref_count)--;
+
+	if (*channel_ref_count == 0) {
+		/* channel is not used any more, release it */
+		tspp_unregister_notification(0, channel_id);
+		tspp_close_channel(0, channel_id);
+	}
+
+	mutex_unlock(&mpq_dmx_tspp_info.tsif[tsif].mutex);
+	return 0;
+
+remove_channel_failed_restore_count:
+	mpq_dmx_tspp_info.tsif[tsif].filters[tspp_filter.priority].ref_count++;
+
+remove_channel_failed:
+	mutex_unlock(&mpq_dmx_tspp_info.tsif[tsif].mutex);
+	return ret;
+}
+
+static int mpq_tspp_dmx_start_filtering(struct dvb_demux_feed *feed)
+{
+	int ret;
+	struct mpq_demux *mpq_demux =
+		(struct mpq_demux *)feed->demux->priv;
+
+	MPQ_DVB_DBG_PRINT(
+		"%s(%d) executed\n",
+		__func__,
+		feed->pid);
+
+	if (mpq_demux == NULL) {
+		MPQ_DVB_ERR_PRINT(
+			"%s: invalid mpq_demux handle\n",
+			__func__);
+
+		return -EINVAL;
+	}
+
+	if (mpq_demux->source < DMX_SOURCE_DVR0) {
+		/* source from TSPP, need to configure tspp pipe */
+		ret = mpq_tspp_dmx_add_channel(feed);
+
+		if (ret < 0) {
+			MPQ_DVB_DBG_PRINT(
+				"%s: mpq_tspp_dmx_add_channel failed(%d)\n",
+				__func__,
+				ret);
+			return ret;
+		}
+	}
+
+	/*
+	 * Always feed sections/PES starting from a new one and
+	 * do not partial transfer data from older one
+	 */
+	feed->pusi_seen = 0;
+
+	/*
+	 * For video PES, data is tunneled to the decoder,
+	 * initialize tunneling and pes parsing.
+	 */
+	if (mpq_dmx_is_video_feed(feed)) {
+		ret = mpq_dmx_init_video_feed(feed);
+
+		if (ret < 0) {
+			MPQ_DVB_DBG_PRINT(
+				"%s: mpq_dmx_init_video_feed failed(%d)\n",
+				__func__,
+				ret);
+
+			if (mpq_demux->source < DMX_SOURCE_DVR0)
+				mpq_tspp_dmx_remove_channel(feed);
+
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+static int mpq_tspp_dmx_stop_filtering(struct dvb_demux_feed *feed)
+{
+	int ret = 0;
+	struct mpq_demux *mpq_demux = (struct mpq_demux *)feed->demux->priv;
+
+	MPQ_DVB_DBG_PRINT(
+		"%s(%d) executed\n",
+		__func__,
+		feed->pid);
+
+	/*
+	 * For video PES, data is tunneled to the decoder,
+	 * terminate tunnel and pes parsing.
+	 */
+	if (mpq_dmx_is_video_feed(feed))
+		mpq_dmx_terminate_video_feed(feed);
+
+	if (mpq_demux->source < DMX_SOURCE_DVR0) {
+		/* source from TSPP, need to configure tspp pipe */
+		ret = mpq_tspp_dmx_remove_channel(feed);
+	}
+
+	return ret;
+}
+
+static int mpq_tspp_dmx_write_to_decoder(
+					struct dvb_demux_feed *feed,
+					const u8 *buf,
+					size_t len)
+{
+	/*
+	 * It is assumed that this function is called once for each
+	 * TS packet of the relevant feed.
+	 */
+	if (len > TSPP_RAW_TTS_SIZE)
+		MPQ_DVB_DBG_PRINT(
+				"%s: warnning - len larger than one packet\n",
+				__func__);
+
+	if (mpq_dmx_is_video_feed(feed))
+		return mpq_dmx_process_video_packet(feed, buf);
+
+	if (mpq_dmx_is_pcr_feed(feed))
+		return mpq_dmx_process_pcr_packet(feed, buf);
+
+	return 0;
+}
+
+static int mpq_tspp_dmx_init(
+			struct dvb_adapter *mpq_adapter,
+			struct mpq_demux *mpq_demux)
+{
+	int result;
+
+	MPQ_DVB_DBG_PRINT("%s executed\n", __func__);
+
+	/* Set the kernel-demux object capabilities */
+	mpq_demux->demux.dmx.capabilities =
+		DMX_TS_FILTERING			|
+		DMX_PES_FILTERING			|
+		DMX_SECTION_FILTERING		|
+		DMX_MEMORY_BASED_FILTERING	|
+		DMX_CRC_CHECKING			|
+		DMX_TS_DESCRAMBLING;
+
+	/* Set dvb-demux "virtual" function pointers */
+	mpq_demux->demux.priv = (void *)mpq_demux;
+	mpq_demux->demux.filternum = MPQ_DMX_MAX_NUM_OF_FILTERS;
+	mpq_demux->demux.feednum = MPQ_DMX_MAX_NUM_OF_FILTERS;
+	mpq_demux->demux.start_feed = mpq_tspp_dmx_start_filtering;
+	mpq_demux->demux.stop_feed = mpq_tspp_dmx_stop_filtering;
+	mpq_demux->demux.write_to_decoder = mpq_tspp_dmx_write_to_decoder;
+
+	mpq_demux->demux.decoder_fullness_init =
+		mpq_dmx_decoder_fullness_init;
+
+	mpq_demux->demux.decoder_fullness_wait =
+		mpq_dmx_decoder_fullness_wait;
+
+	mpq_demux->demux.decoder_fullness_abort =
+		mpq_dmx_decoder_fullness_abort;
+
+	/* Initialize dvb_demux object */
+	result = dvb_dmx_init(&mpq_demux->demux);
+	if (result < 0) {
+		MPQ_DVB_ERR_PRINT("%s: dvb_dmx_init failed\n", __func__);
+		goto init_failed;
+	}
+
+	/* Now initailize the dmx-dev object */
+	mpq_demux->dmxdev.filternum = MPQ_DMX_MAX_NUM_OF_FILTERS;
+	mpq_demux->dmxdev.demux = &mpq_demux->demux.dmx;
+	mpq_demux->dmxdev.capabilities =
+		DMXDEV_CAP_DUPLEX |
+		DMXDEV_CAP_PULL_MODE |
+		DMXDEV_CAP_PCR_EXTRACTION;
+
+	mpq_demux->dmxdev.demux->set_source = mpq_dmx_set_source;
+
+	result = dvb_dmxdev_init(&mpq_demux->dmxdev, mpq_adapter);
+	if (result < 0) {
+		MPQ_DVB_ERR_PRINT("%s: dvb_dmxdev_init failed (errno=%d)\n",
+						  __func__,
+						  result);
+		goto init_failed_dmx_release;
+	}
+
+	/* Extend dvb-demux debugfs with TSPP statistics. */
+	mpq_dmx_init_hw_statistics(mpq_demux);
+
+	return 0;
+
+init_failed_dmx_release:
+	dvb_dmx_release(&mpq_demux->demux);
+init_failed:
+	return result;
+}
+
+static int __init mpq_dmx_tspp_plugin_init(void)
+{
+	int i;
+	int j;
+	int ret;
+
+	MPQ_DVB_DBG_PRINT("%s executed\n", __func__);
+
+	for (i = 0; i < TSIF_COUNT; i++) {
+		mpq_dmx_tspp_info.tsif[i].pes_channel_ref = 0;
+
+		mpq_dmx_tspp_info.tsif[i].pes_work.channel_id =
+			TSPP_CHANNEL_ID(i, TSPP_PES_CHANNEL);
+
+		INIT_WORK(&mpq_dmx_tspp_info.tsif[i].pes_work.work,
+				  mpq_dmx_tspp_work);
+
+		mpq_dmx_tspp_info.tsif[i].section_channel_ref = 0;
+
+		mpq_dmx_tspp_info.tsif[i].section_work.channel_id =
+			TSPP_CHANNEL_ID(i, TSPP_SECTION_CHANNEL);
+
+		INIT_WORK(&mpq_dmx_tspp_info.tsif[i].section_work.work,
+				  mpq_dmx_tspp_work);
+
+		for (j = 0; j < TSPP_FILTERS_COUNT; j++) {
+			mpq_dmx_tspp_info.tsif[i].filters[j].pid = -1;
+			mpq_dmx_tspp_info.tsif[i].filters[j].ref_count = 0;
+		}
+
+		snprintf(mpq_dmx_tspp_info.tsif[i].name,
+				TSIF_NAME_LENGTH,
+				"tsif_%d",
+				i);
+
+		mpq_dmx_tspp_info.tsif[i].workqueue =
+			create_singlethread_workqueue(
+				mpq_dmx_tspp_info.tsif[i].name);
+
+		if (mpq_dmx_tspp_info.tsif[i].workqueue == NULL) {
+
+			for (j = 0; j < i; j++) {
+				destroy_workqueue(
+					mpq_dmx_tspp_info.tsif[j].workqueue);
+
+				mutex_destroy(&mpq_dmx_tspp_info.tsif[j].mutex);
+			}
+
+			MPQ_DVB_ERR_PRINT(
+				"%s: create_singlethread_workqueue failed\n",
+				__func__);
+
+			return -ENOMEM;
+		}
+
+		mutex_init(&mpq_dmx_tspp_info.tsif[i].mutex);
+	}
+
+	ret = mpq_dmx_plugin_init(mpq_tspp_dmx_init);
+
+	if (ret < 0) {
+		MPQ_DVB_ERR_PRINT(
+			"%s: mpq_dmx_plugin_init failed (errno=%d)\n",
+			__func__,
+			ret);
+
+		for (i = 0; i < TSIF_COUNT; i++) {
+			destroy_workqueue(mpq_dmx_tspp_info.tsif[i].workqueue);
+			mutex_destroy(&mpq_dmx_tspp_info.tsif[i].mutex);
+		}
+	}
+
+	return ret;
+}
+
+static void __exit mpq_dmx_tspp_plugin_exit(void)
+{
+	int i;
+
+	MPQ_DVB_DBG_PRINT("%s executed\n", __func__);
+
+	for (i = 0; i < TSIF_COUNT; i++) {
+		mutex_lock(&mpq_dmx_tspp_info.tsif[i].mutex);
+
+		if (mpq_dmx_tspp_info.tsif[i].pes_channel_ref) {
+			tspp_unregister_notification(0, TSPP_PES_CHANNEL);
+			tspp_close_channel(0,
+				TSPP_CHANNEL_ID(i, TSPP_PES_CHANNEL));
+		}
+
+		if (mpq_dmx_tspp_info.tsif[i].section_channel_ref) {
+			tspp_unregister_notification(0, TSPP_SECTION_CHANNEL);
+			tspp_close_channel(0,
+				TSPP_CHANNEL_ID(i, TSPP_SECTION_CHANNEL));
+		}
+
+		/* TODO: if we allocate buffer
+		 * to TSPP ourself, need to free those as well
+		 */
+
+		mutex_unlock(&mpq_dmx_tspp_info.tsif[i].mutex);
+		flush_workqueue(mpq_dmx_tspp_info.tsif[i].workqueue);
+		destroy_workqueue(mpq_dmx_tspp_info.tsif[i].workqueue);
+		mutex_destroy(&mpq_dmx_tspp_info.tsif[i].mutex);
+	}
+
+	mpq_dmx_plugin_exit();
+}
+
+
+module_init(mpq_dmx_tspp_plugin_init);
+module_exit(mpq_dmx_tspp_plugin_exit);
+
+MODULE_DESCRIPTION("Qualcomm demux TSPP version 1 HW Plugin");
+MODULE_LICENSE("GPL v2");
+
+
diff --git a/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_tspp_v2.c b/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_tspp_v2.c
new file mode 100644
index 0000000..d3c2c50
--- /dev/null
+++ b/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_tspp_v2.c
@@ -0,0 +1,132 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include "mpq_dvb_debug.h"
+#include "mpq_dmx_plugin_common.h"
+
+
+static int mpq_tspp_dmx_start_filtering(struct dvb_demux_feed *feed)
+{
+	MPQ_DVB_DBG_PRINT(
+		"%s(%d) executed\n",
+		__func__,
+		feed->pid);
+
+	/* Always feed sections/PES starting from a new one and
+	 * do not partial transfer data from older one
+	 */
+	feed->pusi_seen = 0;
+	return 0;
+}
+
+static int mpq_tspp_dmx_stop_filtering(struct dvb_demux_feed *feed)
+{
+	MPQ_DVB_DBG_PRINT(
+		"%s(%d) executed\n",
+		__func__,
+		feed->pid);
+
+	return 0;
+}
+
+/**
+ * Initialize a single demux device.
+ *
+ * @mpq_adapter: MPQ DVB adapter
+ * @mpq_demux: The demux device to initialize
+ *
+ * Return     error code
+ */
+static int mpq_tspp_dmx_init(
+				struct dvb_adapter *mpq_adapter,
+				struct mpq_demux *mpq_demux)
+{
+	int result;
+
+	MPQ_DVB_DBG_PRINT("%s executed\n", __func__);
+
+	/* Set the kernel-demux object capabilities */
+	mpq_demux->demux.dmx.capabilities =
+		DMX_TS_FILTERING			|
+		DMX_PES_FILTERING			|
+		DMX_SECTION_FILTERING		|
+		DMX_MEMORY_BASED_FILTERING	|
+		DMX_CRC_CHECKING			|
+		DMX_TS_DESCRAMBLING;
+
+	/* Set dvb-demux "virtual" function pointers */
+	mpq_demux->demux.priv = (void *)mpq_demux;
+	mpq_demux->demux.filternum = MPQ_DMX_MAX_NUM_OF_FILTERS;
+	mpq_demux->demux.feednum = MPQ_DMX_MAX_NUM_OF_FILTERS;
+	mpq_demux->demux.start_feed = mpq_tspp_dmx_start_filtering;
+	mpq_demux->demux.stop_feed = mpq_tspp_dmx_stop_filtering;
+	mpq_demux->demux.write_to_decoder = NULL;
+	mpq_demux->demux.decoder_fullness_init = NULL;
+	mpq_demux->demux.decoder_fullness_wait = NULL;
+	mpq_demux->demux.decoder_fullness_abort = NULL;
+
+	/* Initialize dvb_demux object */
+	result = dvb_dmx_init(&mpq_demux->demux);
+	if (result < 0) {
+		MPQ_ERR_PRINT("%s: dvb_dmx_init failed\n", __func__);
+		goto init_failed;
+	}
+
+	/* Now initailize the dmx-dev object */
+	mpq_demux->dmxdev.filternum = MPQ_DMX_MAX_NUM_OF_FILTERS;
+	mpq_demux->dmxdev.demux = &mpq_demux->demux.dmx;
+	mpq_demux->dmxdev.capabilities =
+		DMXDEV_CAP_DUPLEX |
+		DMXDEV_CAP_PULL_MODE |
+		DMXDEV_CAP_PCR_EXTRACTION;
+
+	mpq_demux->dmxdev.demux->set_source = mpq_dmx_set_source;
+
+	result = dvb_dmxdev_init(&mpq_demux->dmxdev, mpq_adapter);
+	if (result < 0) {
+		MPQ_DVB_ERR_PRINT("%s: dvb_dmxdev_init failed (errno=%d)\n",
+						  __func__,
+						  result);
+
+		goto init_failed_dmx_release;
+	}
+
+	return 0;
+
+init_failed_dmx_release:
+	dvb_dmx_release(&mpq_demux->demux);
+init_failed:
+	return result;
+}
+
+static int __init mpq_dmx_tspp_plugin_init(void)
+{
+	MPQ_DVB_DBG_PRINT("%s executed\n", __func__);
+
+	return mpq_dmx_plugin_init(mpq_tspp_dmx_init);
+}
+
+static void __exit mpq_dmx_tspp_plugin_exit(void)
+{
+	MPQ_DVB_DBG_PRINT("%s executed\n", __func__);
+	mpq_dmx_plugin_exit();
+}
+
+
+module_init(mpq_dmx_tspp_plugin_init);
+module_exit(mpq_dmx_tspp_plugin_exit);
+
+MODULE_DESCRIPTION("Qualcomm demux TSPP version2 HW Plugin");
+MODULE_LICENSE("GPL v2");
+
diff --git a/drivers/media/dvb/mpq/include/mpq_adapter.h b/drivers/media/dvb/mpq/include/mpq_adapter.h
new file mode 100644
index 0000000..c720f91
--- /dev/null
+++ b/drivers/media/dvb/mpq/include/mpq_adapter.h
@@ -0,0 +1,146 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _MPQ_ADAPTER_H
+#define _MPQ_ADAPTER_H
+
+#include "dvbdev.h"
+#include "mpq_stream_buffer.h"
+
+
+
+/** IDs of interfaces holding stream-buffers */
+enum mpq_adapter_stream_if {
+	/** Interface holding stream-buffer for video0 stream */
+	MPQ_ADAPTER_VIDEO0_STREAM_IF = 0,
+
+	/** Interface holding stream-buffer for video1 stream */
+	MPQ_ADAPTER_VIDEO1_STREAM_IF = 1,
+
+	/** Interface holding stream-buffer for video1 stream */
+	MPQ_ADAPTER_VIDEO2_STREAM_IF = 2,
+
+	/** Interface holding stream-buffer for video1 stream */
+	MPQ_ADAPTER_VIDEO3_STREAM_IF = 3,
+
+	/** Maximum number of interfaces holding stream-buffers */
+	MPQ_ADAPTER_MAX_NUM_OF_INTERFACES,
+};
+
+
+/** The meta-data used for video interface */
+struct mpq_adapter_video_meta_data {
+	/**
+	 * Indication whether this packet is just a padding packet.
+	 * In this case packet should be just disposed along
+	 * with the padding in the raw-data buffer.
+	 */
+	int is_padding;
+
+	/** Indication whether PTS exist */
+	int pts_exist;
+
+	/** Indication whether DTS exist */
+	int dts_exist;
+
+	/** PTS value associated with the PES data if any */
+	u64 pts;
+
+	/** DTS value associated with the PES data if any */
+	u64 dts;
+} __packed;
+
+
+/** Callback function to notify on registrations of specific interfaces */
+typedef void (*mpq_adapter_stream_if_callback)(
+				enum mpq_adapter_stream_if interface_id,
+				void *user_param);
+
+
+/**
+ * mpq_adapter_get - Returns pointer to Qualcomm DVB adapter
+ *
+ * Return     dvb adapter or NULL if not exist.
+ */
+struct dvb_adapter *mpq_adapter_get(void);
+
+
+/**
+ * mpq_adapter_register_stream_if - Register a stream interface.
+ *
+ * @interface_id: The interface id
+ * @stream_buffer: The buffer used for the interface
+ *
+ * Return     error status
+ *
+ * Stream interface used to connect between two units in tunneling
+ * mode using mpq_streambuffer implementation.
+ * The producer of the interface should register the new interface,
+ * consumer may get the interface using mpq_adapter_get_stream_if.
+ *
+ * Note that the function holds a pointer to this interface,
+ * stream_buffer pointer assumed to be valid as long as interface
+ * is active.
+ */
+int mpq_adapter_register_stream_if(
+		enum mpq_adapter_stream_if interface_id,
+		struct mpq_streambuffer *stream_buffer);
+
+
+/**
+ * mpq_adapter_unregister_stream_if - Un-register a stream interface.
+ *
+ * @interface_id: The interface id
+ *
+ * Return     error status
+ */
+int mpq_adapter_unregister_stream_if(
+		enum mpq_adapter_stream_if interface_id);
+
+
+/**
+ * mpq_adapter_get_stream_if - Get buffer used for a stream interface.
+ *
+ * @interface_id: The interface id
+ * @stream_buffer: The returned stream buffer
+ *
+ * Return     error status
+ */
+int mpq_adapter_get_stream_if(
+		enum mpq_adapter_stream_if interface_id,
+		struct mpq_streambuffer **stream_buffer);
+
+
+/**
+ * mpq_adapter_notify_stream_if - Register notification
+ * to be triggered when a stream interface is registered.
+ *
+ * @interface_id: The interface id
+ * @callback: The callback to be triggered when the interface is registered
+ * @user_param: A parameter that is passed back to the callback function
+ *				when triggered.
+ *
+ * Return     error status
+ *
+ * Producer may use this to register notification when desired
+ * interface registered in the system and query its information
+ * afterwards using mpq_adapter_get_stream_if.
+ * To remove the callback, this function should be called with NULL
+ * value in callback parameter.
+ */
+int mpq_adapter_notify_stream_if(
+		enum mpq_adapter_stream_if interface_id,
+		mpq_adapter_stream_if_callback callback,
+		void *user_param);
+
+#endif /* _MPQ_ADAPTER_H */
+
diff --git a/drivers/media/dvb/mpq/include/mpq_dvb_debug.h b/drivers/media/dvb/mpq/include/mpq_dvb_debug.h
new file mode 100644
index 0000000..4890b85
--- /dev/null
+++ b/drivers/media/dvb/mpq/include/mpq_dvb_debug.h
@@ -0,0 +1,38 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _MPQ_DVB_DEBUG_H
+#define _MPQ_DVB_DEBUG_H
+
+/* Enable this line if you want to output debug printouts */
+#define MPG_DVB_DEBUG_ENABLE
+
+#undef MPQ_DVB_DBG_PRINT		/* undef it, just in case */
+
+#ifdef MPG_DVB_DEBUG_ENABLE
+#define MPQ_DVB_DBG_PRINT(fmt, args...) pr_debug(fmt, ## args)
+#define MPQ_DVB_ERR_PRINT(fmt, args...) pr_err(fmt, ## args)
+#else  /* MPG_DVB_DEBUG_ENABLE */
+#define MPQ_DVB_DBG_PRINT(fmt, args...)
+#define MPQ_DVB_ERR_PRINT(fmt, args...)
+#endif /* MPG_DVB_DEBUG_ENABLE */
+
+
+/*
+ * The following can be used to disable specific printout
+ * by adding a letter to the end of MPQ_DVB_DBG_PRINT
+ */
+#undef MPQ_DVB_DBG_PRINTT
+#define MPQ_DVB_DBG_PRINTT(fmt, args...)
+
+#endif /* _MPQ_DVB_DEBUG_H */
+
diff --git a/drivers/media/dvb/mpq/include/mpq_stream_buffer.h b/drivers/media/dvb/mpq/include/mpq_stream_buffer.h
new file mode 100644
index 0000000..4ea4222
--- /dev/null
+++ b/drivers/media/dvb/mpq/include/mpq_stream_buffer.h
@@ -0,0 +1,269 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _MPQ_STREAM_BUFFER_H
+#define _MPQ_STREAM_BUFFER_H
+
+#include "dvb_ringbuffer.h"
+
+
+/**
+ * DOC: MPQ Stream Buffer
+ *
+ * A stream buffer implmenetation used to transfer data between two units
+ * such as demux and decoders. The implementation relies on dvb_ringbuffer
+ * implementation. Refer to dvb_ringbuffer.h for details.
+ *
+ * The implementation uses two dvb_ringbuffers, one to pass the
+ * raw-data (PES payload for example) and the other to pass
+ * meta-data (information from PES header for example).
+ *
+ * The meta-data uses dvb_ringbuffer packet interface. Each meta-data
+ * packet hold the address and size of raw-data described by the
+ * meta-data packet, in addition to user's own parameters if any required.
+ *
+ * Contrary to dvb_ringbuffer implementation, this API makes sure there's
+ * enough data to read/write when making read/write operations.
+ * Users interested to flush/reset specific buffer, check for bytes
+ * ready or space available for write should use the respective services
+ * in dvb_ringbuffer (dvb_ringbuffer_avail, dvb_ringbuffer_free,
+ * dvb_ringbuffer_reset, dvb_ringbuffer_flush,
+ * dvb_ringbuffer_flush_spinlock_wakeup).
+ *
+ * Concurrency protection is handled in the same manner as in
+ * dvb_ringbuffer implementation.
+ *
+ * Typical call flow from producer:
+ *
+ * - Start writting the raw-data of new packet, the following call is
+ *   repeated until end of data of the specific packet
+ *
+ *     mpq_streambuffer_data_write(...)
+ *
+ * - Now write a new packet describing the new available raw-data
+ *     mpq_streambuffer_pkt_write(...)
+ *
+ * Typical call flow from consumer:
+ *
+ * - Poll for next available packet:
+ *      mpq_streambuffer_pkt_next(&streambuff,-1)
+ *
+ *   In different approach, consumer can wait on event for new data and then
+ *   call mpq_streambuffer_pkt_next, waiting for data can be done as follows:
+ *
+ *      wait_event_interruptible(
+ *			streambuff->packet_data->queue,
+ *			!dvb_ringbuffer_empty(&streambuff->packet_data) ||
+ *			(streambuff->packet_data.error != 0);
+ *
+ * - Get the new packet information:
+ *      mpq_streambuffer_pkt_read(..)
+ *
+ * - Read the raw-data of the new packet. Here you can use two methods:
+ *
+ *   1. Read the data to a user supplied buffer:
+ *         mpq_streambuffer_data_read()
+ *
+ *      In this case memory copy is done, read pointer is updated in the raw
+ *      data buffer, the amount of raw-data is provided part of the
+ *      packet's information. User should then call mpq_streambuffer_pkt_dispose
+ *      with dispose_data set to 0 as the raw-data was already disposed.
+ *
+ *   2. Access the data directly using the raw-data address. The address
+ *      of the raw data is provided part of the packet's information. User
+ *      then should call mpq_streambuffer_pkt_dispose with dispose_data set
+ *      to 1 to dispose the packet along with it's raw-data.
+ */
+
+/**
+ * struct mpq_streambuffer - mpq stream buffer representation
+ *
+ * @raw_data: The buffer used to hold the raw-data
+ * @packet_data: The buffer user to hold the meta-data
+ */
+struct mpq_streambuffer {
+	struct dvb_ringbuffer raw_data;
+	struct dvb_ringbuffer packet_data;
+};
+
+/**
+ * struct mpq_streambuffer_packet_header - packet header saved in packet buffer
+ * @user_data_len: length of private user (meta) data
+ * @raw_data_addr: raw-data address in the raw-buffer described by the packet
+ * @raw_data_len: size of raw-data in the raw-data buffer (can be 0)
+ *
+ * The packet structure that is saved in each packet-buffer:
+ * user_data_len
+ * raw_data_addr
+ * raw_data_len
+ * private user-data bytes
+ */
+struct mpq_streambuffer_packet_header {
+	u32 user_data_len;
+	u32	raw_data_addr;
+	u32	raw_data_len;
+} __packed;
+
+/**
+ * mpq_streambuffer_init - Initialize a new stream buffer
+ *
+ * @sbuff: The buffer to initialize
+ * @data_buff: The buffer holding raw-data
+ * @data_buff_len: Size of raw-data buffer
+ * @packet_buff: The buffer holding meta-data
+ * @packet_buff_size: Size of meta-data buffer
+ */
+void mpq_streambuffer_init(
+		struct mpq_streambuffer *sbuff,
+		void *data_buff, size_t data_buff_len,
+		void *packet_buff, size_t packet_buff_size);
+
+/**
+ * mpq_streambuffer_packet_next - Returns index of next avaialble packet.
+ *
+ * @sbuff: The stream buffer
+ * @idx: Previous packet index or -1 to return index of the the first
+ *       available packet.
+ * @pktlen: The length of the ready packet
+ *
+ * Return index to the packet-buffer, -1 if buffer is empty
+ *
+ * After getting the index, the user of this function can either
+ * access the packet buffer directly using the returned index
+ * or ask to read the data back from the buffer using mpq_ringbuffer_pkt_read
+ */
+ssize_t mpq_streambuffer_pkt_next(
+		struct mpq_streambuffer *sbuff,
+		ssize_t idx, size_t *pktlen);
+
+/**
+ * mpq_streambuffer_pkt_read - Reads out the packet from the provided index.
+ *
+ * @sbuff: The stream buffer
+ * @idx: The index of the packet to be read
+ * @packet: The read packet's header
+ * @user_data: The read private user data
+ *
+ * Return  The actual number of bytes read, -EINVAL if the packet is
+ * already disposed or the packet-data is invalid.
+ *
+ * The packet is not disposed after this function is called, to dispose it
+ * along with the raw-data it points to use mpq_streambuffer_pkt_dispose.
+ * If there are no private user-data, the user-data pointer can be NULL.
+ * The caller of this function must make sure that the private user-data
+ * buffer has enough space for the private user-data length
+ */
+ssize_t mpq_streambuffer_pkt_read(
+		struct mpq_streambuffer *sbuff,
+		size_t idx,
+		struct mpq_streambuffer_packet_header *packet,
+		u8 *user_data);
+
+/**
+ * mpq_streambuffer_pkt_dispose - Disposes a packet from the packet buffer
+ *
+ * @sbuff: The stream buffer
+ * @idx: The index of the packet to be disposed
+ * @dispose_data: Indicates whether to update the read pointer inside the
+ * raw-data buffer for the respective data pointed by the packet.
+ *
+ * Return  error status, -EINVAL if the packet-data is invalid
+ *
+ * The function updates the read pointer inside the raw-data buffer
+ * for the respective data pointed by the packet if dispose_data is set.
+ */
+int mpq_streambuffer_pkt_dispose(
+		struct mpq_streambuffer *sbuff,
+		size_t idx,
+		int dispose_data);
+
+/**
+ * mpq_streambuffer_pkt_write - Write a new packet to the packet buffer.
+ *
+ * @sbuff: The stream buffer
+ * @packet: The packet header to write
+ * @user_data: The private user-data to be written
+ *
+ * Return  error status, -ENOSPC if there's no space to write the packet
+ */
+int mpq_streambuffer_pkt_write(
+		struct mpq_streambuffer *sbuff,
+		struct mpq_streambuffer_packet_header *packet,
+		u8 *user_data);
+
+/**
+ * mpq_streambuffer_data_write - Write data to raw-data buffer
+ *
+ * @sbuff: The stream buffer
+ * @buf: The buffer holding the data to be written
+ * @len: The length of the data buffer
+ *
+ * Return  The actual number of bytes written or -ENOSPC if
+ *			no space to write the data
+ */
+ssize_t mpq_streambuffer_data_write(
+		struct mpq_streambuffer *sbuff,
+		const u8 *buf, size_t len);
+
+/**
+ * mpq_streambuffer_data_write_deposit - Advances the raw-buffer write pointer.
+ * Assumes the raw-data was written by the user directly
+ *
+ * @sbuff: The stream buffer
+ * @len: The length of the raw-data that was already written
+ *
+ * Return  error status
+ */
+int mpq_streambuffer_data_write_deposit(
+		struct mpq_streambuffer *sbuff,
+		size_t len);
+
+/**
+ * mpq_streambuffer_data_read - Reads out raw-data to the provided buffer.
+ *
+ * @sbuff: The stream buffer
+ * @buf: The buffer to read the raw-data data to
+ * @len: The length of the buffer that will hold the raw-data
+ *
+ * Return  The actual number of bytes read
+ *
+ * This fucntion copies the data from the ring-buffer to the
+ * provided buf parameter. The user can save the extra copy by accessing
+ * the data pointer directly and reading from it, then update the
+ * read pointer by the amount of data that was read using
+ * mpq_streambuffer_data_read_dispose
+ */
+size_t mpq_streambuffer_data_read(
+		struct mpq_streambuffer *sbuff,
+		u8 *buf, size_t len);
+
+/**
+ * mpq_streambuffer_data_read_dispose - Advances the raw-buffer read pointer.
+ * Assumes the raw-data was read by the user directly.
+ *
+ * @sbuff: The stream buffer
+ * @len: The length of the raw-data to be disposed
+ *
+ * Return  error status, -EINVAL if buffer there's no enough data to
+ *			be disposed
+ *
+ * The user can instead dipose a packet along with the data in the
+ * raw-data buffer using mpq_streambuffer_pkt_dispose.
+ */
+int mpq_streambuffer_data_read_dispose(
+		struct mpq_streambuffer *sbuff,
+		size_t len);
+
+
+
+#endif /* _MPQ_STREAM_BUFFER_H */
+
diff --git a/drivers/media/rc/gpio-ir-recv.c b/drivers/media/rc/gpio-ir-recv.c
index eee351b..3416c91 100644
--- a/drivers/media/rc/gpio-ir-recv.c
+++ b/drivers/media/rc/gpio-ir-recv.c
@@ -87,7 +87,7 @@
 	rcdev->input_name = GPIO_IR_DEVICE_NAME;
 	rcdev->input_id.bustype = BUS_HOST;
 	rcdev->driver_name = GPIO_IR_DRIVER_NAME;
-	rcdev->map_name = RC_MAP_RC6_PHILIPS;
+	rcdev->map_name = RC_MAP_SAMSUNG_NECX;
 
 	gpio_dev->rcdev = rcdev;
 	gpio_dev->gpio_nr = pdata->gpio_nr;
diff --git a/drivers/media/video/msm/gemini/msm_gemini_platform.c b/drivers/media/video/msm/gemini/msm_gemini_platform.c
index 39b135d..1ebc2f1 100644
--- a/drivers/media/video/msm/gemini/msm_gemini_platform.c
+++ b/drivers/media/video/msm/gemini/msm_gemini_platform.c
@@ -159,7 +159,7 @@
 	if (pgmn_dev->hw_version != GEMINI_7X) {
 		if (pgmn_dev->gemini_fs == NULL) {
 			pgmn_dev->gemini_fs =
-				regulator_get(&pgmn_dev->pdev->dev, "fs_ijpeg");
+				regulator_get(&pgmn_dev->pdev->dev, "vdd");
 			if (IS_ERR(pgmn_dev->gemini_fs)) {
 				pr_err("%s: Regulator FS_ijpeg get failed %ld\n",
 					__func__, PTR_ERR(pgmn_dev->gemini_fs));
diff --git a/drivers/media/video/msm/io/msm_camera_i2c.c b/drivers/media/video/msm/io/msm_camera_i2c.c
index a3cc012..6f45637 100644
--- a/drivers/media/video/msm/io/msm_camera_i2c.c
+++ b/drivers/media/video/msm/io/msm_camera_i2c.c
@@ -252,7 +252,7 @@
 		enum msm_camera_i2c_data_type dt;
 		if (reg_conf_tbl->cmd_type == MSM_CAMERA_I2C_CMD_POLL) {
 			rc = msm_camera_i2c_poll(client, reg_conf_tbl->reg_addr,
-				reg_conf_tbl->reg_addr, reg_conf_tbl->dt);
+				reg_conf_tbl->reg_data, reg_conf_tbl->dt);
 		} else {
 			if (reg_conf_tbl->dt == 0)
 				dt = data_type;
diff --git a/drivers/media/video/msm/msm.c b/drivers/media/video/msm/msm.c
index 668fd8e..12a0f37 100644
--- a/drivers/media/video/msm/msm.c
+++ b/drivers/media/video/msm/msm.c
@@ -560,7 +560,10 @@
 	ctrlcmd.type = MSM_V4L2_SET_CTRL_CMD;
 	ctrlcmd.length = cmd_len + value_len;
 	ctrlcmd.value = (void *)ctrl_data;
-	ctrlcmd.timeout_ms = 1000;
+	if (tmp_cmd->timeout_ms > 0)
+		ctrlcmd.timeout_ms = tmp_cmd->timeout_ms;
+	else
+		ctrlcmd.timeout_ms = 1000;
 	ctrlcmd.vnode_id = pcam->vnode_id;
 	ctrlcmd.queue_idx = pcam->server_queue_idx;
 	ctrlcmd.config_ident = g_server_dev.config_info.config_dev_id[0];
diff --git a/drivers/media/video/msm/msm_isp.c b/drivers/media/video/msm/msm_isp.c
index 5b3101b..d678d86 100644
--- a/drivers/media/video/msm/msm_isp.c
+++ b/drivers/media/video/msm/msm_isp.c
@@ -618,6 +618,8 @@
 	switch (cfgcmd.cmd_type) {
 	case CMD_AXI_CFG_PRIM:
 	case CMD_AXI_CFG_SEC:
+	case CMD_AXI_CFG_ZSL:
+	case CMD_RAW_PICT_AXI_CFG:
 	case CMD_AXI_CFG_PRIM_ALL_CHNLS:
 	case CMD_AXI_CFG_PRIM|CMD_AXI_CFG_SEC:
 	case CMD_AXI_CFG_PRIM|CMD_AXI_CFG_SEC_ALL_CHNLS:
diff --git a/drivers/media/video/msm/sensors/imx074_v4l2.c b/drivers/media/video/msm/sensors/imx074_v4l2.c
index 67b7140..534b85e 100644
--- a/drivers/media/video/msm/sensors/imx074_v4l2.c
+++ b/drivers/media/video/msm/sensors/imx074_v4l2.c
@@ -309,6 +309,7 @@
 	.sensor_config = msm_sensor_config,
 	.sensor_power_up = msm_sensor_power_up,
 	.sensor_power_down = msm_sensor_power_down,
+	.sensor_adjust_frame_lines = msm_sensor_adjust_frame_lines,
 };
 
 static struct msm_sensor_reg_t imx074_regs = {
diff --git a/drivers/media/video/msm/sensors/imx091.c b/drivers/media/video/msm/sensors/imx091.c
index 62e97ac..70c3f6e 100644
--- a/drivers/media/video/msm/sensors/imx091.c
+++ b/drivers/media/video/msm/sensors/imx091.c
@@ -303,6 +303,7 @@
 	.sensor_config = msm_sensor_config,
 	.sensor_power_up = msm_sensor_power_up,
 	.sensor_power_down = msm_sensor_power_down,
+	.sensor_adjust_frame_lines = msm_sensor_adjust_frame_lines,
 };
 
 static struct msm_sensor_reg_t imx091_regs = {
diff --git a/drivers/media/video/msm/sensors/msm_sensor.c b/drivers/media/video/msm/sensors/msm_sensor.c
index cc56ccf..604f8fd 100644
--- a/drivers/media/video/msm/sensors/msm_sensor.c
+++ b/drivers/media/video/msm/sensors/msm_sensor.c
@@ -16,6 +16,32 @@
 #include "msm_camera_i2c_mux.h"
 
 /*=============================================================*/
+int32_t msm_sensor_adjust_frame_lines(struct msm_sensor_ctrl_t *s_ctrl,
+	uint16_t res)
+{
+	uint16_t cur_line = 0;
+	uint16_t exp_fl_lines = 0;
+	if (s_ctrl->sensor_exp_gain_info) {
+		msm_camera_i2c_read(s_ctrl->sensor_i2c_client,
+			s_ctrl->sensor_exp_gain_info->coarse_int_time_addr,
+			&cur_line,
+			MSM_CAMERA_I2C_WORD_DATA);
+		exp_fl_lines = cur_line +
+			s_ctrl->sensor_exp_gain_info->vert_offset;
+		if (exp_fl_lines > s_ctrl->msm_sensor_reg->
+			output_settings[res].frame_length_lines)
+			msm_camera_i2c_write(s_ctrl->sensor_i2c_client,
+				s_ctrl->sensor_output_reg_addr->
+				frame_length_lines,
+				exp_fl_lines,
+				MSM_CAMERA_I2C_WORD_DATA);
+		CDBG("%s cur_fl_lines %d, exp_fl_lines %d\n", __func__,
+			s_ctrl->msm_sensor_reg->
+			output_settings[res].frame_length_lines,
+			exp_fl_lines);
+	}
+	return 0;
+}
 
 int32_t msm_sensor_write_init_settings(struct msm_sensor_ctrl_t *s_ctrl)
 {
@@ -38,6 +64,12 @@
 		return rc;
 
 	rc = msm_sensor_write_output_settings(s_ctrl, res);
+	if (rc < 0)
+		return rc;
+
+	if (s_ctrl->func_tbl->sensor_adjust_frame_lines)
+		rc = s_ctrl->func_tbl->sensor_adjust_frame_lines(s_ctrl, res);
+
 	return rc;
 }
 
diff --git a/drivers/media/video/msm/sensors/msm_sensor.h b/drivers/media/video/msm/sensors/msm_sensor.h
index 0104b98..0055fb8 100644
--- a/drivers/media/video/msm/sensors/msm_sensor.h
+++ b/drivers/media/video/msm/sensors/msm_sensor.h
@@ -133,6 +133,8 @@
 		(struct msm_sensor_ctrl_t *);
 	int (*sensor_power_up) (struct msm_sensor_ctrl_t *);
 	int32_t (*sensor_match_id)(struct msm_sensor_ctrl_t *s_ctrl);
+	int (*sensor_adjust_frame_lines)
+		(struct msm_sensor_ctrl_t *s_ctrl, uint16_t res);
 };
 
 struct msm_sensor_ctrl_t {
@@ -223,6 +225,9 @@
 int32_t msm_sensor_write_output_settings(struct msm_sensor_ctrl_t *s_ctrl,
 	uint16_t res);
 
+int32_t msm_sensor_adjust_frame_lines(struct msm_sensor_ctrl_t *s_ctrl,
+	uint16_t res);
+
 int32_t msm_sensor_setting(struct msm_sensor_ctrl_t *s_ctrl,
 			int update_type, int res);
 
diff --git a/drivers/media/video/msm/sensors/ov2720.c b/drivers/media/video/msm/sensors/ov2720.c
index 05556eb..3b13d04 100644
--- a/drivers/media/video/msm/sensors/ov2720.c
+++ b/drivers/media/video/msm/sensors/ov2720.c
@@ -456,6 +456,7 @@
 	.sensor_config = msm_sensor_config,
 	.sensor_power_up = msm_sensor_power_up,
 	.sensor_power_down = msm_sensor_power_down,
+	.sensor_adjust_frame_lines = msm_sensor_adjust_frame_lines,
 };
 
 static struct msm_sensor_reg_t ov2720_regs = {
diff --git a/drivers/media/video/msm/sensors/s5k3l1yx.c b/drivers/media/video/msm/sensors/s5k3l1yx.c
index 8d022b6..debda88 100644
--- a/drivers/media/video/msm/sensors/s5k3l1yx.c
+++ b/drivers/media/video/msm/sensors/s5k3l1yx.c
@@ -652,6 +652,7 @@
 	.sensor_config = msm_sensor_config,
 	.sensor_power_up = msm_sensor_power_up,
 	.sensor_power_down = msm_sensor_power_down,
+	.sensor_adjust_frame_lines = msm_sensor_adjust_frame_lines,
 };
 
 static struct msm_sensor_reg_t s5k3l1yx_regs = {
diff --git a/drivers/media/video/vcap_v4l2.c b/drivers/media/video/vcap_v4l2.c
index 670d0f8..219f7a0 100644
--- a/drivers/media/video/vcap_v4l2.c
+++ b/drivers/media/video/vcap_v4l2.c
@@ -537,9 +537,9 @@
 	.release	= video_device_release,
 };
 
-int vcap_reg_powerup(struct vcap_dev *dev)
+int vcap_reg_powerup(struct vcap_dev *dev, struct device *ddev)
 {
-	dev->fs_vcap = regulator_get(NULL, "fs_vcap");
+	dev->fs_vcap = regulator_get(ddev, "vdd");
 	if (IS_ERR(dev->fs_vcap)) {
 		pr_err("%s: Regulator FS_VCAP get failed %ld\n", __func__,
 			PTR_ERR(dev->fs_vcap));
@@ -715,7 +715,7 @@
 {
 	int rc;
 
-	rc = vcap_reg_powerup(dev);
+	rc = vcap_reg_powerup(dev, ddev);
 	if (rc < 0)
 		goto reg_failed;
 	rc = vcap_clk_powerup(dev, ddev);
diff --git a/drivers/misc/tsif.c b/drivers/misc/tsif.c
index 42faa07..ace5b0c 100644
--- a/drivers/misc/tsif.c
+++ b/drivers/misc/tsif.c
@@ -1,7 +1,7 @@
 /*
  * TSIF Driver
  *
- * Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2009-2012, Code Aurora Forum. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -680,7 +680,7 @@
 		tsif_device->state = tsif_state_flushing;
 		while (tsif_device->xfer[0].busy ||
 		       tsif_device->xfer[1].busy) {
-			msm_dmov_flush(tsif_device->dma);
+			msm_dmov_flush(tsif_device->dma, 1);
 			msleep(10);
 		}
 	}
@@ -760,7 +760,7 @@
 			      offsetof(struct tsif_dmov_cmd, box_ptr));
 		hdr->complete_func = tsif_dmov_complete_func;
 	}
-	msm_dmov_flush(tsif_device->dma);
+	msm_dmov_flush(tsif_device->dma, 1);
 	return 0;
 err:
 	dev_err(&tsif_device->pdev->dev, "Failed to allocate DMA buffers\n");
diff --git a/drivers/mmc/host/msm_sdcc.c b/drivers/mmc/host/msm_sdcc.c
index e458429..3b115ab 100644
--- a/drivers/mmc/host/msm_sdcc.c
+++ b/drivers/mmc/host/msm_sdcc.c
@@ -1021,11 +1021,8 @@
 	if (/*interrupt*/0)
 		*c |= MCI_CPSM_INTERRUPT;
 
-	if (cmd->opcode == MMC_READ_SINGLE_BLOCK ||
-		cmd->opcode == MMC_READ_MULTIPLE_BLOCK ||
-		cmd->opcode == MMC_WRITE_BLOCK ||
-		cmd->opcode == MMC_WRITE_MULTIPLE_BLOCK ||
-		cmd->opcode == SD_IO_RW_EXTENDED)
+	/* DAT_CMD bit should be set for all ADTC */
+	if (mmc_cmd_type(cmd) == MMC_CMD_ADTC)
 		*c |= MCI_CSPM_DATCMD;
 
 	/* Check if AUTO CMD19 is required or not? */
diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig
index b83e871..bb16fbb 100644
--- a/drivers/net/wireless/Kconfig
+++ b/drivers/net/wireless/Kconfig
@@ -283,13 +283,15 @@
 	---help---
 	  A driver for Qualcomm WLAN SDIO Libra chipset.
 
-config ATH6K_LEGACY
-	tristate "QCA AR6003 wlan SDIO driver"
+config ATH6K_LEGACY_EXT
+	tristate "QCA AR6003 wlan SDIO driver - External"
 	depends on MMC_MSM && WLAN
 	select WIRELESS_EXT
 	select WEXT_PRIV
 	---help---
-	This module adds support for wireless adapters based on QCA AR6003 chipset running over SDIO.
+	  This module adds support for wireless adapters based on QCA AR6003 chipset
+	  running over SDIO. Driver is built outside of kernel tree, this config
+	  only enables configurations required for QCA AR6003 wlan driver.
 
 config WCNSS_CORE
 	tristate "Qualcomm WCNSS CORE driver"
diff --git a/drivers/platform/msm/usb_bam.c b/drivers/platform/msm/usb_bam.c
index 053b81f..c040fac 100644
--- a/drivers/platform/msm/usb_bam.c
+++ b/drivers/platform/msm/usb_bam.c
@@ -19,6 +19,7 @@
 #include <linux/usb/msm_hsusb.h>
 #include <mach/usb_bam.h>
 #include <mach/sps.h>
+#include <linux/workqueue.h>
 
 #define USB_SUMMING_THRESHOLD 512
 #define CONNECTIONS_NUM		4
@@ -29,11 +30,20 @@
 static struct sps_mem_buffer data_mem_buf[CONNECTIONS_NUM][2];
 static struct sps_mem_buffer desc_mem_buf[CONNECTIONS_NUM][2];
 static struct platform_device *usb_bam_pdev;
+static struct workqueue_struct *usb_bam_wq;
+
+struct usb_bam_wake_event_info {
+	struct sps_register_event event;
+	int (*callback)(void *);
+	void *param;
+	struct work_struct wake_w;
+};
 
 struct usb_bam_connect_info {
 	u8 idx;
 	u8 *src_pipe;
 	u8 *dst_pipe;
+	struct usb_bam_wake_event_info peer_event;
 	bool enabled;
 };
 
@@ -48,7 +58,7 @@
 						u8 *usb_pipe_idx)
 {
 	int ret;
-	struct sps_pipe *pipe = sps_pipes[connection_idx][pipe_dir];
+	struct sps_pipe **pipe = &sps_pipes[connection_idx][pipe_dir];
 	struct sps_connect *connection =
 		&sps_connections[connection_idx][pipe_dir];
 	struct msm_usb_bam_platform_data *pdata =
@@ -58,13 +68,13 @@
 			(struct usb_bam_pipe_connect *)(pdata->connections +
 			 bam_offset(pdata) + (2*connection_idx+pipe_dir));
 
-	pipe = sps_alloc_endpoint();
-	if (pipe == NULL) {
+	*pipe = sps_alloc_endpoint();
+	if (*pipe == NULL) {
 		pr_err("%s: sps_alloc_endpoint failed\n", __func__);
 		return -ENOMEM;
 	}
 
-	ret = sps_get_config(pipe, connection);
+	ret = sps_get_config(*pipe, connection);
 	if (ret) {
 		pr_err("%s: tx get config failed %d\n", __func__, ret);
 		goto get_config_failed;
@@ -114,7 +124,7 @@
 	connection->desc = desc_mem_buf[connection_idx][pipe_dir];
 	connection->event_thresh = 512;
 
-	ret = sps_connect(pipe, connection);
+	ret = sps_connect(*pipe, connection);
 	if (ret < 0) {
 		pr_err("%s: tx connect error %d\n", __func__, ret);
 		goto error;
@@ -122,10 +132,10 @@
 	return 0;
 
 error:
-	sps_disconnect(pipe);
+	sps_disconnect(*pipe);
 fifo_setup_error:
 get_config_failed:
-	sps_free_endpoint(pipe);
+	sps_free_endpoint(*pipe);
 	return ret;
 }
 
@@ -168,6 +178,58 @@
 	return 0;
 }
 
+static void usb_bam_wake_work(struct work_struct *w)
+{
+	struct usb_bam_wake_event_info *wake_event_info =
+		container_of(w, struct usb_bam_wake_event_info, wake_w);
+
+	wake_event_info->callback(wake_event_info->param);
+}
+
+static void usb_bam_wake_cb(struct sps_event_notify *notify)
+{
+	struct usb_bam_wake_event_info *wake_event_info =
+		(struct usb_bam_wake_event_info *)notify->user;
+
+	queue_work(usb_bam_wq, &wake_event_info->wake_w);
+}
+
+int usb_bam_register_wake_cb(u8 idx,
+	int (*callback)(void *user), void* param)
+{
+	struct sps_pipe *pipe = sps_pipes[idx][PEER_PERIPHERAL_TO_USB];
+	struct sps_connect *sps_connection =
+		&sps_connections[idx][PEER_PERIPHERAL_TO_USB];
+	struct usb_bam_connect_info *connection = &usb_bam_connections[idx];
+	struct usb_bam_wake_event_info *wake_event_info =
+		&connection->peer_event;
+	int ret;
+
+	wake_event_info->param = param;
+	wake_event_info->callback = callback;
+	wake_event_info->event.mode = SPS_TRIGGER_CALLBACK;
+	wake_event_info->event.xfer_done = NULL;
+	wake_event_info->event.callback = callback ? usb_bam_wake_cb : NULL;
+	wake_event_info->event.user = wake_event_info;
+	wake_event_info->event.options = SPS_O_WAKEUP;
+	ret = sps_register_event(pipe, &wake_event_info->event);
+	if (ret) {
+		pr_err("%s: sps_register_event() failed %d\n", __func__, ret);
+		return ret;
+	}
+
+	sps_connection->options = callback ?
+		(SPS_O_AUTO_ENABLE | SPS_O_WAKEUP | SPS_O_WAKEUP_IS_ONESHOT) :
+			SPS_O_AUTO_ENABLE;
+	ret = sps_set_config(pipe, sps_connection);
+	if (ret) {
+		pr_err("%s: sps_set_config() failed %d\n", __func__, ret);
+		return ret;
+	}
+
+	return 0;
+}
+
 static int usb_bam_init(void)
 {
 	u32 h_usb;
@@ -275,8 +337,11 @@
 
 	dev_dbg(&pdev->dev, "usb_bam_probe\n");
 
-	for (i = 0; i < CONNECTIONS_NUM; i++)
+	for (i = 0; i < CONNECTIONS_NUM; i++) {
 		usb_bam_connections[i].enabled = 0;
+		INIT_WORK(&usb_bam_connections[i].peer_event.wake_w,
+			usb_bam_wake_work);
+	}
 
 	if (!pdev->dev.platform_data) {
 		dev_err(&pdev->dev, "missing platform_data\n");
@@ -288,11 +353,26 @@
 	if (ret)
 		dev_err(&pdev->dev, "failed to create device file\n");
 
+	usb_bam_wq = alloc_workqueue("usb_bam_wq",
+		WQ_UNBOUND | WQ_MEM_RECLAIM, 1);
+	if (!usb_bam_wq) {
+		pr_err("unable to create workqueue usb_bam_wq\n");
+		return -ENOMEM;
+	}
+
 	return ret;
 }
 
+static int usb_bam_remove(struct platform_device *pdev)
+{
+	destroy_workqueue(usb_bam_wq);
+
+	return 0;
+}
+
 static struct platform_driver usb_bam_driver = {
 	.probe = usb_bam_probe,
+	.remove = usb_bam_remove,
 	.driver = { .name = "usb_bam", },
 };
 
diff --git a/drivers/spi/spi_qsd.c b/drivers/spi/spi_qsd.c
index 69e0546..cb0e936 100644
--- a/drivers/spi/spi_qsd.c
+++ b/drivers/spi/spi_qsd.c
@@ -1076,8 +1076,8 @@
 						 "timeout\n", __func__);
 				dd->cur_msg->status = -EIO;
 				if (dd->mode == SPI_DMOV_MODE) {
-					msm_dmov_flush(dd->tx_dma_chan);
-					msm_dmov_flush(dd->rx_dma_chan);
+					msm_dmov_flush(dd->tx_dma_chan, 1);
+					msm_dmov_flush(dd->rx_dma_chan, 1);
 				}
 				break;
 		}
@@ -1672,8 +1672,8 @@
 		return;
 
 	while (dd->mode == SPI_DMOV_MODE && limit++ < 50) {
-		msm_dmov_flush(dd->tx_dma_chan);
-		msm_dmov_flush(dd->rx_dma_chan);
+		msm_dmov_flush(dd->tx_dma_chan, 1);
+		msm_dmov_flush(dd->rx_dma_chan, 1);
 		msleep(10);
 	}
 
@@ -1739,8 +1739,8 @@
 					  SPI_INPUT_FIFO;
 
 	/* Clear remaining activities on channel */
-	msm_dmov_flush(dd->tx_dma_chan);
-	msm_dmov_flush(dd->rx_dma_chan);
+	msm_dmov_flush(dd->tx_dma_chan, 1);
+	msm_dmov_flush(dd->rx_dma_chan, 1);
 
 	return 0;
 }
diff --git a/drivers/tty/serial/msm_serial.c b/drivers/tty/serial/msm_serial.c
index 32e9dc0..72bc8de 100644
--- a/drivers/tty/serial/msm_serial.c
+++ b/drivers/tty/serial/msm_serial.c
@@ -626,6 +626,9 @@
 	unsigned long flags;
 	unsigned int baud, mr;
 
+	if (!termios->c_cflag)
+		return;
+
 	spin_lock_irqsave(&port->lock, flags);
 
 	/* calculate and set baud rate */
diff --git a/drivers/tty/serial/msm_serial_hs.c b/drivers/tty/serial/msm_serial_hs.c
index 5b0b279..f559160 100644
--- a/drivers/tty/serial/msm_serial_hs.c
+++ b/drivers/tty/serial/msm_serial_hs.c
@@ -1427,7 +1427,7 @@
 
 		if (rx->flush == FLUSH_NONE) {
 			rx->flush = FLUSH_DATA_READY;
-			msm_dmov_flush(msm_uport->dma_rx_channel);
+			msm_dmov_flush(msm_uport->dma_rx_channel, 1);
 		}
 	}
 	/* tx ready interrupt */
diff --git a/drivers/tty/serial/msm_serial_hs_lite.c b/drivers/tty/serial/msm_serial_hs_lite.c
index 285d1de..787b826 100644
--- a/drivers/tty/serial/msm_serial_hs_lite.c
+++ b/drivers/tty/serial/msm_serial_hs_lite.c
@@ -792,6 +792,9 @@
 	unsigned int baud, mr;
 	unsigned int vid;
 
+	if (!termios->c_cflag)
+		return;
+
 	spin_lock_irqsave(&port->lock, flags);
 
 	/* calculate and set baud rate */
diff --git a/drivers/usb/gadget/android.c b/drivers/usb/gadget/android.c
index c017859..d74959e 100644
--- a/drivers/usb/gadget/android.c
+++ b/drivers/usb/gadget/android.c
@@ -1679,7 +1679,7 @@
 	}
 
 	/* pm qos request to prevent apps idle power collapse */
-	if (pdata->swfi_latency)
+	if (pdata && pdata->swfi_latency)
 		pm_qos_add_request(&dev->pm_qos_req_dma,
 			PM_QOS_CPU_DMA_LATENCY, PM_QOS_DEFAULT_VALUE);
 	strlcpy(dev->pm_qos, "high", sizeof(dev->pm_qos));
@@ -1700,7 +1700,7 @@
 	android_destroy_device(dev);
 	class_destroy(android_class);
 	usb_composite_unregister(&android_usb_driver);
-	if (pdata->swfi_latency)
+	if (pdata && pdata->swfi_latency)
 		pm_qos_remove_request(&dev->pm_qos_req_dma);
 
 	return 0;
diff --git a/drivers/usb/gadget/ci13xxx_msm_hsic.c b/drivers/usb/gadget/ci13xxx_msm_hsic.c
index eeacceb..39d4720 100644
--- a/drivers/usb/gadget/ci13xxx_msm_hsic.c
+++ b/drivers/usb/gadget/ci13xxx_msm_hsic.c
@@ -65,6 +65,7 @@
 	struct msm_xo_voter	*xo_handle;
 	struct workqueue_struct *wq;
 	struct work_struct	suspend_w;
+	struct msm_hsic_peripheral_platform_data *pdata;
 };
 
 static int msm_hsic_init_vddcx(struct msm_hsic_per *mhsic, int init)
@@ -365,8 +366,10 @@
 	 */
 	mb();
 
-	clk_disable(mhsic->iface_clk);
-	clk_disable(mhsic->core_clk);
+	if (!mhsic->pdata->keep_core_clk_on_suspend_workaround) {
+		clk_disable(mhsic->iface_clk);
+		clk_disable(mhsic->core_clk);
+	}
 	clk_disable(mhsic->phy_clk);
 	clk_disable(mhsic->cal_clk);
 
@@ -416,8 +419,10 @@
 		dev_err(mhsic->dev, "%s failed to vote for TCXO %d\n",
 				__func__, ret);
 
-	clk_enable(mhsic->iface_clk);
-	clk_enable(mhsic->core_clk);
+	if (!mhsic->pdata->keep_core_clk_on_suspend_workaround) {
+		clk_enable(mhsic->iface_clk);
+		clk_enable(mhsic->core_clk);
+	}
 	clk_enable(mhsic->phy_clk);
 	clk_enable(mhsic->cal_clk);
 
@@ -611,9 +616,17 @@
 	struct resource *res;
 	struct msm_hsic_per *mhsic;
 	int ret = 0;
+	struct msm_hsic_peripheral_platform_data *pdata;
 
 	dev_dbg(&pdev->dev, "msm-hsic probe\n");
 
+	if (!pdev->dev.platform_data) {
+		dev_err(&pdev->dev, "No platform data given. Bailing out\n");
+		return -ENODEV;
+	} else {
+		pdata = pdev->dev.platform_data;
+	}
+
 	mhsic = kzalloc(sizeof(struct msm_hsic_per), GFP_KERNEL);
 	if (!mhsic) {
 		dev_err(&pdev->dev, "unable to allocate msm_hsic\n");
@@ -622,6 +635,7 @@
 	the_mhsic = mhsic;
 	platform_set_drvdata(pdev, mhsic);
 	mhsic->dev = &pdev->dev;
+	mhsic->pdata = pdata;
 
 	mhsic->irq = platform_get_irq(pdev, 0);
 	if (mhsic->irq < 0) {
diff --git a/drivers/usb/gadget/f_rmnet.c b/drivers/usb/gadget/f_rmnet.c
index 958ed31..fcbc75c 100644
--- a/drivers/usb/gadget/f_rmnet.c
+++ b/drivers/usb/gadget/f_rmnet.c
@@ -414,6 +414,60 @@
 	kfree(f->name);
 }
 
+static void frmnet_suspend(struct usb_function *f)
+{
+	struct f_rmnet *dev = func_to_rmnet(f);
+	unsigned		port_num;
+	enum transport_type	dxport = rmnet_ports[dev->port_num].data_xport;
+
+	pr_debug("%s: data xport: %s dev: %p portno: %d\n",
+		__func__, xport_to_str(dxport),
+		dev, dev->port_num);
+
+	port_num = rmnet_ports[dev->port_num].data_xport_num;
+	switch (dxport) {
+	case USB_GADGET_XPORT_BAM:
+		break;
+	case USB_GADGET_XPORT_BAM2BAM:
+		gbam_suspend(&dev->port, port_num, dxport);
+		break;
+	case USB_GADGET_XPORT_HSIC:
+		break;
+	case USB_GADGET_XPORT_NONE:
+		break;
+	default:
+		pr_err("%s: Un-supported transport: %s\n", __func__,
+				xport_to_str(dxport));
+	}
+}
+
+static void frmnet_resume(struct usb_function *f)
+{
+	struct f_rmnet *dev = func_to_rmnet(f);
+	unsigned		port_num;
+	enum transport_type	dxport = rmnet_ports[dev->port_num].data_xport;
+
+	pr_debug("%s: data xport: %s dev: %p portno: %d\n",
+		__func__, xport_to_str(dxport),
+		dev, dev->port_num);
+
+	port_num = rmnet_ports[dev->port_num].data_xport_num;
+	switch (dxport) {
+	case USB_GADGET_XPORT_BAM:
+		break;
+	case USB_GADGET_XPORT_BAM2BAM:
+		gbam_resume(&dev->port, port_num, dxport);
+		break;
+	case USB_GADGET_XPORT_HSIC:
+		break;
+	case USB_GADGET_XPORT_NONE:
+		break;
+	default:
+		pr_err("%s: Un-supported transport: %s\n", __func__,
+				xport_to_str(dxport));
+	}
+}
+
 static void frmnet_disable(struct usb_function *f)
 {
 	struct f_rmnet *dev = func_to_rmnet(f);
@@ -912,6 +966,8 @@
 	f->disable = frmnet_disable;
 	f->set_alt = frmnet_set_alt;
 	f->setup = frmnet_setup;
+	f->suspend = frmnet_suspend;
+	f->resume = frmnet_resume;
 	dev->port.send_cpkt_response = frmnet_send_cpkt_response;
 	dev->port.disconnect = frmnet_disconnect;
 	dev->port.connect = frmnet_connect;
diff --git a/drivers/usb/gadget/u_bam.c b/drivers/usb/gadget/u_bam.c
index 3113c45..d379c66 100644
--- a/drivers/usb/gadget/u_bam.c
+++ b/drivers/usb/gadget/u_bam.c
@@ -1214,3 +1214,49 @@
 
 	return ret;
 }
+
+static int gbam_wake_cb(void *param)
+{
+	struct gbam_port	*port = (struct gbam_port *)param;
+	struct bam_ch_info *d;
+	struct f_rmnet		*dev;
+
+	dev = port_to_rmnet(port->gr);
+	d = &port->data_ch;
+
+	pr_debug("%s: woken up by peer\n", __func__);
+
+	return usb_gadget_wakeup(dev->cdev->gadget);
+}
+
+void gbam_suspend(struct grmnet *gr, u8 port_num, enum transport_type trans)
+{
+	struct gbam_port	*port;
+	struct bam_ch_info *d;
+
+	if (trans != USB_GADGET_XPORT_BAM2BAM)
+		return;
+
+	port = bam2bam_ports[port_num];
+	d = &port->data_ch;
+
+	pr_debug("%s: suspended port %d\n", __func__, port_num);
+
+	usb_bam_register_wake_cb(d->connection_idx, gbam_wake_cb, port);
+}
+
+void gbam_resume(struct grmnet *gr, u8 port_num, enum transport_type trans)
+{
+	struct gbam_port	*port;
+	struct bam_ch_info *d;
+
+	if (trans != USB_GADGET_XPORT_BAM2BAM)
+		return;
+
+	port = bam2bam_ports[port_num];
+	d = &port->data_ch;
+
+	pr_debug("%s: resumed port %d\n", __func__, port_num);
+
+	usb_bam_register_wake_cb(d->connection_idx, NULL, NULL);
+}
diff --git a/drivers/usb/gadget/u_rmnet.h b/drivers/usb/gadget/u_rmnet.h
index 386101c..0f7c4fb 100644
--- a/drivers/usb/gadget/u_rmnet.h
+++ b/drivers/usb/gadget/u_rmnet.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -50,6 +50,8 @@
 int gbam_connect(struct grmnet *gr, u8 port_num,
 				 enum transport_type trans, u8 connection_idx);
 void gbam_disconnect(struct grmnet *gr, u8 port_num, enum transport_type trans);
+void gbam_suspend(struct grmnet *gr, u8 port_num, enum transport_type trans);
+void gbam_resume(struct grmnet *gr, u8 port_num, enum transport_type trans);
 int gsmd_ctrl_connect(struct grmnet *gr, int port_num);
 void gsmd_ctrl_disconnect(struct grmnet *gr, u8 port_num);
 int gsmd_ctrl_setup(unsigned int count);
diff --git a/drivers/video/msm/external_common.c b/drivers/video/msm/external_common.c
index 5d12e97..7f603dd 100644
--- a/drivers/video/msm/external_common.c
+++ b/drivers/video/msm/external_common.c
@@ -2056,6 +2056,7 @@
 	pinfo->xres = timing->active_h;
 	pinfo->yres = timing->active_v;
 	pinfo->clk_rate = timing->pixel_freq*1000;
+	pinfo->frame_rate = 60;
 
 	pinfo->lcdc.h_back_porch = timing->back_porch_h;
 	pinfo->lcdc.h_front_porch = timing->front_porch_h;
diff --git a/drivers/video/msm/hdmi_msm.c b/drivers/video/msm/hdmi_msm.c
index 375f82f..ba560b9 100644
--- a/drivers/video/msm/hdmi_msm.c
+++ b/drivers/video/msm/hdmi_msm.c
@@ -4539,7 +4539,6 @@
 	hdmi_msm_state->hdcp_timer.data = (uint32)NULL;
 
 	hdmi_msm_state->hdcp_timer.expires = 0xffffffffL;
-	add_timer(&hdmi_msm_state->hdcp_timer);
 #endif /* CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT */
 
 #ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_CEC_SUPPORT
diff --git a/drivers/video/msm/lcdc_toshiba_fwvga_pt.c b/drivers/video/msm/lcdc_toshiba_fwvga_pt.c
index 3e81471..77606cf 100644
--- a/drivers/video/msm/lcdc_toshiba_fwvga_pt.c
+++ b/drivers/video/msm/lcdc_toshiba_fwvga_pt.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
@@ -436,7 +436,7 @@
 	pinfo->bl_max = 100;
 	pinfo->bl_min = 1;
 
-	if (cpu_is_msm7x25a() || cpu_is_msm7x25aa()) {
+	if (cpu_is_msm7x25a() || cpu_is_msm7x25aa() || cpu_is_msm7x25ab()) {
 		pinfo->yres = 320;
 		pinfo->lcdc.h_back_porch = 10;
 		pinfo->lcdc.h_front_porch = 21;
diff --git a/drivers/video/msm/lvds.c b/drivers/video/msm/lvds.c
index 8f1e510..18225fb 100644
--- a/drivers/video/msm/lvds.c
+++ b/drivers/video/msm/lvds.c
@@ -143,9 +143,9 @@
 			/* MDP_LCDC_LVDS_MUX_CTL_FOR_D2_6_TO_4 */
 			MDP_OUTP(MDP_BASE +  0xc2028, 0x00121314);
 			/* MDP_LCDC_LVDS_MUX_CTL_FOR_D3_3_TO_0 */
-			MDP_OUTP(MDP_BASE +  0xc202c, 0x1706071b);
+			MDP_OUTP(MDP_BASE +  0xc202c, 0x0f16171b);
 			/* MDP_LCDC_LVDS_MUX_CTL_FOR_D3_6_TO_4 */
-			MDP_OUTP(MDP_BASE +  0xc2030, 0x000e0f16);
+			MDP_OUTP(MDP_BASE +  0xc2030, 0x0006070e);
 		}
 		if (mfd->panel_info.lvds.channel_mode ==
 			LVDS_DUAL_CHANNEL_MODE) {
diff --git a/drivers/video/msm/mdp.c b/drivers/video/msm/mdp.c
index 2ca9296..471ed4e 100644
--- a/drivers/video/msm/mdp.c
+++ b/drivers/video/msm/mdp.c
@@ -2071,7 +2071,7 @@
 	}
 	disable_irq(mdp_irq);
 
-	footswitch = regulator_get(NULL, "fs_mdp");
+	footswitch = regulator_get(&pdev->dev, "vdd");
 	if (IS_ERR(footswitch))
 		footswitch = NULL;
 	else {
diff --git a/drivers/video/msm/mdp4_overlay.c b/drivers/video/msm/mdp4_overlay.c
index 42edecc..34fd399 100644
--- a/drivers/video/msm/mdp4_overlay.c
+++ b/drivers/video/msm/mdp4_overlay.c
@@ -2889,6 +2889,11 @@
 		mdp4_overlay_rgb_setup(pipe);	/* rgb pipe */
 	}
 
+	if ((ctrl->panel_mode & MDP4_PANEL_DTV) ||
+		(ctrl->panel_mode & MDP4_PANEL_LCDC) ||
+		(ctrl->panel_mode & MDP4_PANEL_DSI_VIDEO))
+		mdp4_overlay_reg_flush(pipe, 0);
+
 	mdp4_mixer_stage_up(pipe);
 
 	if (pipe->mixer_num == MDP4_MIXER2) {
@@ -2903,7 +2908,6 @@
 		ctrl->mixer1_played++;
 		/* enternal interface */
 		if (ctrl->panel_mode & MDP4_PANEL_DTV) {
-			mdp4_overlay_reg_flush(pipe, 0);
 			mdp4_overlay_dtv_start();
 			mdp4_overlay_dtv_ov_done_push(mfd, pipe);
 			if (!mfd->use_ov1_blt)
@@ -2914,7 +2918,6 @@
 		/* primary interface */
 		ctrl->mixer0_played++;
 		if (ctrl->panel_mode & MDP4_PANEL_LCDC) {
-			mdp4_overlay_reg_flush(pipe, 0);
 			if (!mfd->use_ov0_blt)
 				mdp4_overlay_update_blt_mode(mfd);
 			mdp4_overlay_lcdc_start();
@@ -2922,7 +2925,6 @@
 		}
 #ifdef CONFIG_FB_MSM_MIPI_DSI
 		else if (ctrl->panel_mode & MDP4_PANEL_DSI_VIDEO) {
-			mdp4_overlay_reg_flush(pipe, 0);
 			if (!mfd->use_ov0_blt)
 				mdp4_overlay_update_blt_mode(mfd);
 			mdp4_overlay_dsi_video_start();
diff --git a/drivers/video/msm/mdp4_overlay_dtv.c b/drivers/video/msm/mdp4_overlay_dtv.c
index b9760bf..e3917e6 100644
--- a/drivers/video/msm/mdp4_overlay_dtv.c
+++ b/drivers/video/msm/mdp4_overlay_dtv.c
@@ -470,6 +470,28 @@
 	mfd->ov_start = true;
 }
 
+static void mdp4_overlay_dtv_wait4dmae(struct msm_fb_data_type *mfd)
+{
+	unsigned long flag;
+
+	if (!dtv_pipe) {
+		pr_debug("%s: no mixer1 base layer pipe allocated!\n",
+			 __func__);
+		return;
+	}
+	/* enable irq */
+	spin_lock_irqsave(&mdp_spin_lock, flag);
+	mdp_enable_irq(MDP_DMA_E_TERM);
+	INIT_COMPLETION(dtv_pipe->comp);
+	mfd->dma->waiting = TRUE;
+	outp32(MDP_INTR_CLEAR, INTR_DMA_E_DONE);
+	mdp_intr_mask |= INTR_DMA_E_DONE;
+	outp32(MDP_INTR_ENABLE, mdp_intr_mask);
+	spin_unlock_irqrestore(&mdp_spin_lock, flag);
+	wait_for_completion_killable(&dtv_pipe->comp);
+	mdp_disable_irq(MDP_DMA_E_TERM);
+}
+
 static void mdp4_overlay_dtv_wait4_ov_done(struct msm_fb_data_type *mfd,
 	struct mdp4_overlay_pipe *pipe)
 {
@@ -490,6 +512,9 @@
 	wait_for_completion_timeout(&dtv_pipe->comp,
 			msecs_to_jiffies(VSYNC_PERIOD*2));
 	mdp_disable_irq(MDP_OVERLAY1_TERM);
+
+	if (dtv_pipe->blt_addr)
+		mdp4_overlay_dtv_wait4dmae(mfd);
 }
 
 void mdp4_overlay_dtv_start(void)
@@ -601,28 +626,6 @@
 	mdp_disable_irq(MDP_DMA_E_TERM);
 }
 
-static void mdp4_overlay_dtv_wait4dmae(struct msm_fb_data_type *mfd)
-{
-	unsigned long flag;
-
-	if (!dtv_pipe) {
-		pr_debug("%s: no mixer1 base layer pipe allocated!\n",
-			 __func__);
-		return;
-	}
-	/* enable irq */
-	spin_lock_irqsave(&mdp_spin_lock, flag);
-	mdp_enable_irq(MDP_DMA_E_TERM);
-	INIT_COMPLETION(dtv_pipe->comp);
-	mfd->dma->waiting = TRUE;
-	outp32(MDP_INTR_CLEAR, INTR_DMA_E_DONE);
-	mdp_intr_mask |= INTR_DMA_E_DONE;
-	outp32(MDP_INTR_ENABLE, mdp_intr_mask);
-	spin_unlock_irqrestore(&mdp_spin_lock, flag);
-	wait_for_completion_killable(&dtv_pipe->comp);
-	mdp_disable_irq(MDP_DMA_E_TERM);
-}
-
 static void mdp4_dtv_do_blt(struct msm_fb_data_type *mfd, int enable)
 {
 	unsigned long flag;
diff --git a/drivers/video/msm/mipi_chimei_wuxga.c b/drivers/video/msm/mipi_chimei_wuxga.c
index 7abb0d4..6ddf74d 100644
--- a/drivers/video/msm/mipi_chimei_wuxga.c
+++ b/drivers/video/msm/mipi_chimei_wuxga.c
@@ -125,7 +125,7 @@
 	pinfo->lcdc.hsync_skew = 0;
 
 	/* Backlight levels - controled via PMIC pwm gpio */
-	pinfo->bl_max = 15;
+	pinfo->bl_max = PWM_LEVEL;
 	pinfo->bl_min = 1;
 
 	/* mipi - general */
diff --git a/drivers/video/msm/mipi_chimei_wxga_pt.c b/drivers/video/msm/mipi_chimei_wxga_pt.c
index 4729d83..1ab50b7 100644
--- a/drivers/video/msm/mipi_chimei_wxga_pt.c
+++ b/drivers/video/msm/mipi_chimei_wxga_pt.c
@@ -121,7 +121,7 @@
 	pinfo->lcdc.hsync_skew = 0;
 
 	/* Backlight levels - controled via PMIC pwm gpio */
-	pinfo->bl_max = 15;
+	pinfo->bl_max = PWM_LEVEL;
 	pinfo->bl_min = 1;
 
 	/* mipi - general */
diff --git a/drivers/video/msm/mipi_renesas.c b/drivers/video/msm/mipi_renesas.c
index 6a7027a..c9dc8255 100644
--- a/drivers/video/msm/mipi_renesas.c
+++ b/drivers/video/msm/mipi_renesas.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
@@ -1138,7 +1138,7 @@
 	mipi_dsi_cmds_tx(mfd, &renesas_tx_buf, renesas_display_on_cmds,
 			ARRAY_SIZE(renesas_display_on_cmds));
 
-	if (cpu_is_msm7x25a() || cpu_is_msm7x25aa()) {
+	if (cpu_is_msm7x25a() || cpu_is_msm7x25aa() || cpu_is_msm7x25ab()) {
 		mipi_dsi_cmds_tx(mfd, &renesas_tx_buf, renesas_hvga_on_cmds,
 			ARRAY_SIZE(renesas_hvga_on_cmds));
 	}
diff --git a/drivers/video/msm/mipi_tc358764_dsi2lvds.c b/drivers/video/msm/mipi_tc358764_dsi2lvds.c
index 1624534..f7f353f 100644
--- a/drivers/video/msm/mipi_tc358764_dsi2lvds.c
+++ b/drivers/video/msm/mipi_tc358764_dsi2lvds.c
@@ -188,12 +188,9 @@
 #define DEBUG01		0x05A4	/* LVDS Data */
 
 /* PWM */
-static u32 d2l_pwm_freq_hz = (66*1000);
+static u32 d2l_pwm_freq_hz = (3.921*1000);
 
-/* 1366x768 uses pwm at 66 KHZ */
-/* 1200x1920 uses pwm at 25 KHZ */
 #define PWM_FREQ_HZ	(d2l_pwm_freq_hz)
-#define PWM_LEVEL 15
 #define PWM_PERIOD_USEC (USEC_PER_SEC / PWM_FREQ_HZ)
 #define PWM_DUTY_LEVEL (PWM_PERIOD_USEC / PWM_LEVEL)
 
@@ -559,10 +556,7 @@
 	/* Set gpio#4=U/D=0, gpio#3=L/R=1 , gpio#2,1=CABC=0, gpio#0=NA. */
 	mipi_d2l_write_reg(mfd, GPIOO, d2l_gpio_out_val);
 
-	if (mfd->panel_info.xres == 1366)
-		d2l_pwm_freq_hz = (66*1000);
-	else
-		d2l_pwm_freq_hz = (25*1000);
+	d2l_pwm_freq_hz = (3.921*1000);
 
 	if (bl_level == 0)
 		bl_level = PWM_LEVEL * 2 / 3 ; /* Default ON value */
diff --git a/drivers/video/msm/mipi_tc358764_dsi2lvds.h b/drivers/video/msm/mipi_tc358764_dsi2lvds.h
index 072d1f4..1b949f0 100644
--- a/drivers/video/msm/mipi_tc358764_dsi2lvds.h
+++ b/drivers/video/msm/mipi_tc358764_dsi2lvds.h
@@ -14,6 +14,8 @@
 #ifndef MIPI_TC358764_DSI2LVDS_H
 #define MIPI_TC358764_DSI2LVDS_H
 
+#define PWM_LEVEL 255
+
 int mipi_tc358764_dsi2lvds_register(struct msm_panel_info *pinfo,
 	u32 channel_id, u32 panel_id);
 #endif  /* MIPI_TC358764_DSI2LVDS_H */
diff --git a/drivers/video/msm/msm_fb.c b/drivers/video/msm/msm_fb.c
index c6b0440..b4d8db0 100644
--- a/drivers/video/msm/msm_fb.c
+++ b/drivers/video/msm/msm_fb.c
@@ -1194,6 +1194,7 @@
 
 	mfd->var_xres = panel_info->xres;
 	mfd->var_yres = panel_info->yres;
+	mfd->var_frame_rate = panel_info->frame_rate;
 
 	var->pixclock = mfd->panel_info.clk_rate;
 	mfd->var_pixclock = var->pixclock;
@@ -1229,22 +1230,45 @@
 		/*
 		 * id field for fb app
 		 */
-	    id = (int *)&mfd->panel;
+	id = (int *)&mfd->panel;
 
-#if defined(CONFIG_FB_MSM_MDP22)
-	snprintf(fix->id, sizeof(fix->id), "msmfb22_%x", (__u32) *id);
-#elif defined(CONFIG_FB_MSM_MDP30)
-	snprintf(fix->id, sizeof(fix->id), "msmfb30_%x", (__u32) *id);
-#elif defined(CONFIG_FB_MSM_MDP31)
-	snprintf(fix->id, sizeof(fix->id), "msmfb31_%x", (__u32) *id);
-#elif defined(CONFIG_FB_MSM_MDP40)
-	snprintf(fix->id, sizeof(fix->id), "msmfb40_%x", (__u32) *id);
-#elif defined(CONFIG_FB_MSM_MDP_NONE)
-	snprintf(fix->id, sizeof(fix->id), "msmfb0_%x", (__u32) *id);
-#else
-	error CONFIG_FB_MSM_MDP undefined !
-#endif
-	 fbi->fbops = &msm_fb_ops;
+	switch (mdp_rev) {
+	case MDP_REV_20:
+		snprintf(fix->id, sizeof(fix->id), "msmfb20_%x", (__u32) *id);
+		break;
+	case MDP_REV_22:
+		snprintf(fix->id, sizeof(fix->id), "msmfb22_%x", (__u32) *id);
+		break;
+	case MDP_REV_30:
+		snprintf(fix->id, sizeof(fix->id), "msmfb30_%x", (__u32) *id);
+		break;
+	case MDP_REV_303:
+		snprintf(fix->id, sizeof(fix->id), "msmfb303_%x", (__u32) *id);
+		break;
+	case MDP_REV_31:
+		snprintf(fix->id, sizeof(fix->id), "msmfb31_%x", (__u32) *id);
+		break;
+	case MDP_REV_40:
+		snprintf(fix->id, sizeof(fix->id), "msmfb40_%x", (__u32) *id);
+		break;
+	case MDP_REV_41:
+		snprintf(fix->id, sizeof(fix->id), "msmfb41_%x", (__u32) *id);
+		break;
+	case MDP_REV_42:
+		snprintf(fix->id, sizeof(fix->id), "msmfb42_%x", (__u32) *id);
+		break;
+	case MDP_REV_43:
+		snprintf(fix->id, sizeof(fix->id), "msmfb43_%x", (__u32) *id);
+		break;
+	case MDP_REV_44:
+		snprintf(fix->id, sizeof(fix->id), "msmfb44_%x", (__u32) *id);
+		break;
+	default:
+		snprintf(fix->id, sizeof(fix->id), "msmfb0_%x", (__u32) *id);
+		break;
+	}
+
+	fbi->fbops = &msm_fb_ops;
 	fbi->flags = FBINFO_FLAG_DEFAULT;
 	fbi->pseudo_palette = msm_fb_pseudo_palette;
 
@@ -1736,6 +1760,27 @@
 	return 0;
 }
 
+int msm_fb_check_frame_rate(struct msm_fb_data_type *mfd
+						, struct fb_info *info)
+{
+	int panel_height, panel_width, var_frame_rate, fps_mod;
+	struct fb_var_screeninfo *var = &info->var;
+	fps_mod = 0;
+	if ((mfd->panel_info.type == DTV_PANEL) ||
+		(mfd->panel_info.type == HDMI_PANEL)) {
+		panel_height = var->yres + var->upper_margin +
+			var->vsync_len + var->lower_margin;
+		panel_width = var->xres + var->right_margin +
+			var->hsync_len + var->left_margin;
+		var_frame_rate = ((var->pixclock)/(panel_height * panel_width));
+		if (mfd->var_frame_rate != var_frame_rate) {
+			fps_mod = 1;
+			mfd->var_frame_rate = var_frame_rate;
+		}
+	}
+	return fps_mod;
+}
+
 static int msm_fb_set_par(struct fb_info *info)
 {
 	struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
@@ -1777,7 +1822,8 @@
 		(mfd->hw_refresh && ((mfd->fb_imgType != old_imgType) ||
 				(mfd->var_pixclock != var->pixclock) ||
 				(mfd->var_xres != var->xres) ||
-				(mfd->var_yres != var->yres)))) {
+				(mfd->var_yres != var->yres) ||
+				(msm_fb_check_frame_rate(mfd, info))))) {
 		mfd->var_xres = var->xres;
 		mfd->var_yres = var->yres;
 		mfd->var_pixclock = var->pixclock;
diff --git a/drivers/video/msm/msm_fb.h b/drivers/video/msm/msm_fb.h
index 3becc46..b63c022 100644
--- a/drivers/video/msm/msm_fb.h
+++ b/drivers/video/msm/msm_fb.h
@@ -148,6 +148,7 @@
 	__u32 var_xres;
 	__u32 var_yres;
 	__u32 var_pixclock;
+	__u32 var_frame_rate;
 
 #ifdef MSM_FB_ENABLE_DBGFS
 	struct dentry *sub_dir;
@@ -210,5 +211,7 @@
 
 void fill_black_screen(void);
 void unfill_black_screen(void);
+int msm_fb_check_frame_rate(struct msm_fb_data_type *mfd,
+				struct fb_info *info);
 
 #endif /* MSM_FB_H */
diff --git a/drivers/video/msm/msm_fb_panel.h b/drivers/video/msm/msm_fb_panel.h
index 7744e7a..9d16b7b 100644
--- a/drivers/video/msm/msm_fb_panel.h
+++ b/drivers/video/msm/msm_fb_panel.h
@@ -164,6 +164,7 @@
 	__u32 clk_max;
 	__u32 frame_count;
 	__u32 is_3d_panel;
+	__u32 frame_rate;
 
 
 	struct mddi_panel_info mddi;
diff --git a/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_helper.c b/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_helper.c
index 2862b00..6d3a05a 100644
--- a/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_helper.c
+++ b/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_helper.c
@@ -655,8 +655,9 @@
 		ptr = ddl_pmem_alloc(&dec_bufs->context, buf_size.sz_context,
 			DDL_KILO_BYTE(2));
 		if (!ptr)
-			status = VCD_ERR_ALLOC_FAIL;
-		msm_ion_do_cache_op(ddl_context->video_ion_client,
+			goto fail_free_exit;
+		else
+			msm_ion_do_cache_op(ddl_context->video_ion_client,
 					dec_bufs->context.alloc_handle,
 					dec_bufs->context.virtual_base_addr,
 					dec_bufs->context.buffer_size,
@@ -667,77 +668,77 @@
 		ptr = ddl_pmem_alloc(&dec_bufs->h264_nb_ip, buf_size.sz_nb_ip,
 			DDL_KILO_BYTE(2));
 		if (!ptr)
-			status = VCD_ERR_ALLOC_FAIL;
+			goto fail_free_exit;
 	}
 	if (buf_size.sz_vert_nb_mv > 0) {
 		dec_bufs->h264_vert_nb_mv.mem_type = DDL_MM_MEM;
 		ptr = ddl_pmem_alloc(&dec_bufs->h264_vert_nb_mv,
 			buf_size.sz_vert_nb_mv, DDL_KILO_BYTE(2));
 		if (!ptr)
-			status = VCD_ERR_ALLOC_FAIL;
+			goto fail_free_exit;
 	}
 	if (buf_size.sz_nb_dcac > 0) {
 		dec_bufs->nb_dcac.mem_type = DDL_MM_MEM;
 		ptr = ddl_pmem_alloc(&dec_bufs->nb_dcac, buf_size.sz_nb_dcac,
 			DDL_KILO_BYTE(2));
 		if (!ptr)
-			status = VCD_ERR_ALLOC_FAIL;
+			goto fail_free_exit;
 	}
 	if (buf_size.sz_upnb_mv > 0) {
 		dec_bufs->upnb_mv.mem_type = DDL_MM_MEM;
 		ptr = ddl_pmem_alloc(&dec_bufs->upnb_mv, buf_size.sz_upnb_mv,
 			DDL_KILO_BYTE(2));
 		if (!ptr)
-			status = VCD_ERR_ALLOC_FAIL;
+			goto fail_free_exit;
 	}
 	if (buf_size.sz_sub_anchor_mv > 0) {
 		dec_bufs->sub_anchor_mv.mem_type = DDL_MM_MEM;
 		ptr = ddl_pmem_alloc(&dec_bufs->sub_anchor_mv,
 			buf_size.sz_sub_anchor_mv, DDL_KILO_BYTE(2));
 		if (!ptr)
-			status = VCD_ERR_ALLOC_FAIL;
+			goto fail_free_exit;
 	}
 	if (buf_size.sz_overlap_xform > 0) {
 		dec_bufs->overlay_xform.mem_type = DDL_MM_MEM;
 		ptr = ddl_pmem_alloc(&dec_bufs->overlay_xform,
 			buf_size.sz_overlap_xform, DDL_KILO_BYTE(2));
 		if (!ptr)
-			status = VCD_ERR_ALLOC_FAIL;
+			goto fail_free_exit;
 	}
 	if (buf_size.sz_bit_plane3 > 0) {
 		dec_bufs->bit_plane3.mem_type = DDL_MM_MEM;
 		ptr = ddl_pmem_alloc(&dec_bufs->bit_plane3,
 			buf_size.sz_bit_plane3, DDL_KILO_BYTE(2));
 		if (!ptr)
-			status = VCD_ERR_ALLOC_FAIL;
+			goto fail_free_exit;
 	}
 	if (buf_size.sz_bit_plane2 > 0) {
 		dec_bufs->bit_plane2.mem_type = DDL_MM_MEM;
 		ptr = ddl_pmem_alloc(&dec_bufs->bit_plane2,
 			buf_size.sz_bit_plane2, DDL_KILO_BYTE(2));
 		if (!ptr)
-			status = VCD_ERR_ALLOC_FAIL;
+			goto fail_free_exit;
 	}
 	if (buf_size.sz_bit_plane1 > 0) {
 		dec_bufs->bit_plane1.mem_type = DDL_MM_MEM;
 		ptr = ddl_pmem_alloc(&dec_bufs->bit_plane1,
 			buf_size.sz_bit_plane1, DDL_KILO_BYTE(2));
 		if (!ptr)
-			status = VCD_ERR_ALLOC_FAIL;
+			goto fail_free_exit;
 	}
 	if (buf_size.sz_stx_parser > 0) {
 		dec_bufs->stx_parser.mem_type = DDL_MM_MEM;
 		ptr = ddl_pmem_alloc(&dec_bufs->stx_parser,
 			buf_size.sz_stx_parser, DDL_KILO_BYTE(2));
 		if (!ptr)
-			status = VCD_ERR_ALLOC_FAIL;
+			goto fail_free_exit;
 	}
 	if (buf_size.sz_desc > 0) {
 		dec_bufs->desc.mem_type = DDL_MM_MEM;
 		ptr = ddl_pmem_alloc(&dec_bufs->desc, buf_size.sz_desc,
 			DDL_KILO_BYTE(2));
 		if (!ptr)
-			status = VCD_ERR_ALLOC_FAIL;
+			goto fail_free_exit;
 		else {
 			if (!res_trk_check_for_sec_session()) {
 				memset(dec_bufs->desc.align_virtual_addr,
@@ -751,8 +752,10 @@
 			}
 		}
 	}
-	if (status)
-		ddl_free_dec_hw_buffers(ddl);
+	return status;
+fail_free_exit:
+	status = VCD_ERR_ALLOC_FAIL;
+	ddl_free_dec_hw_buffers(ddl);
 	return status;
 }
 
@@ -880,66 +883,70 @@
 			ptr = ddl_pmem_alloc(&enc_bufs->mv, buf_size.sz_mv,
 				DDL_KILO_BYTE(2));
 			if (!ptr)
-				status = VCD_ERR_ALLOC_FAIL;
+				goto fail_enc_free_exit;
 		}
 		if (buf_size.sz_col_zero > 0) {
 			enc_bufs->col_zero.mem_type = DDL_MM_MEM;
 			ptr = ddl_pmem_alloc(&enc_bufs->col_zero,
 				buf_size.sz_col_zero, DDL_KILO_BYTE(2));
-		if (!ptr)
-			status = VCD_ERR_ALLOC_FAIL;
+			if (!ptr)
+				goto fail_enc_free_exit;
 		}
 		if (buf_size.sz_md > 0) {
 			enc_bufs->md.mem_type = DDL_MM_MEM;
 			ptr = ddl_pmem_alloc(&enc_bufs->md, buf_size.sz_md,
 				DDL_KILO_BYTE(2));
 			if (!ptr)
-				status = VCD_ERR_ALLOC_FAIL;
+				goto fail_enc_free_exit;
 		}
 		if (buf_size.sz_pred > 0) {
 			enc_bufs->pred.mem_type = DDL_MM_MEM;
 			ptr = ddl_pmem_alloc(&enc_bufs->pred,
 				buf_size.sz_pred, DDL_KILO_BYTE(2));
 			if (!ptr)
-				status = VCD_ERR_ALLOC_FAIL;
+				goto fail_enc_free_exit;
 		}
 		if (buf_size.sz_nbor_info > 0) {
 			enc_bufs->nbor_info.mem_type = DDL_MM_MEM;
 			ptr = ddl_pmem_alloc(&enc_bufs->nbor_info,
 				buf_size.sz_nbor_info, DDL_KILO_BYTE(2));
 			if (!ptr)
-				status = VCD_ERR_ALLOC_FAIL;
+				goto fail_enc_free_exit;
 		}
 		if (buf_size.sz_acdc_coef > 0) {
 			enc_bufs->acdc_coef.mem_type = DDL_MM_MEM;
 			ptr = ddl_pmem_alloc(&enc_bufs->acdc_coef,
 				buf_size.sz_acdc_coef, DDL_KILO_BYTE(2));
 			if (!ptr)
-				status = VCD_ERR_ALLOC_FAIL;
+				goto fail_enc_free_exit;
 		}
 		if (buf_size.sz_mb_info > 0) {
 			enc_bufs->mb_info.mem_type = DDL_MM_MEM;
 			ptr = ddl_pmem_alloc(&enc_bufs->mb_info,
 				buf_size.sz_mb_info, DDL_KILO_BYTE(2));
 			if (!ptr)
-				status = VCD_ERR_ALLOC_FAIL;
+				goto fail_enc_free_exit;
 		}
 		if (buf_size.sz_context > 0) {
 			enc_bufs->context.mem_type = DDL_MM_MEM;
 			ptr = ddl_pmem_alloc(&enc_bufs->context,
 				buf_size.sz_context, DDL_KILO_BYTE(2));
 			if (!ptr)
-				status = VCD_ERR_ALLOC_FAIL;
-			msm_ion_do_cache_op(ddl_context->video_ion_client,
+				goto fail_enc_free_exit;
+			else
+				msm_ion_do_cache_op(
+					ddl_context->video_ion_client,
 					enc_bufs->context.alloc_handle,
 					enc_bufs->context.virtual_base_addr,
 					enc_bufs->context.buffer_size,
 					ION_IOC_CLEAN_INV_CACHES);
 		}
-		if (status)
-			ddl_free_enc_hw_buffers(ddl);
 	}
 	return status;
+fail_enc_free_exit:
+	status = VCD_ERR_ALLOC_FAIL;
+	ddl_free_enc_hw_buffers(ddl);
+	return status;
 }
 
 void ddl_decoder_chroma_dpb_change(struct ddl_client_context *ddl)
diff --git a/drivers/video/msm/vidc/1080p/resource_tracker/vcd_res_tracker.c b/drivers/video/msm/vidc/1080p/resource_tracker/vcd_res_tracker.c
index 1c23da3..e71259a 100644
--- a/drivers/video/msm/vidc/1080p/resource_tracker/vcd_res_tracker.c
+++ b/drivers/video/msm/vidc/1080p/resource_tracker/vcd_res_tracker.c
@@ -25,7 +25,7 @@
 #include "vidc.h"
 #include "vcd_res_tracker.h"
 
-#define PIL_FW_BASE_ADDR 0xafe00000
+#define PIL_FW_BASE_ADDR 0x9fe00000
 #define PIL_FW_SIZE 0x200000
 
 static unsigned int vidc_clk_table[3] = {
@@ -427,7 +427,8 @@
 		goto bail_out;
 	}
 	if (!resource_context.footswitch)
-		resource_context.footswitch = regulator_get(NULL, "fs_ved");
+		resource_context.footswitch =
+			regulator_get(resource_context.device, "vdd");
 	if (IS_ERR(resource_context.footswitch)) {
 		VCDRES_MSG_ERROR("foot switch get failed\n");
 		resource_context.footswitch = NULL;
diff --git a/drivers/video/msm/vidc/common/dec/vdec.c b/drivers/video/msm/vidc/common/dec/vdec.c
index 7822717..11177b8 100644
--- a/drivers/video/msm/vidc/common/dec/vdec.c
+++ b/drivers/video/msm/vidc/common/dec/vdec.c
@@ -347,7 +347,7 @@
 		ion_flag = vidc_get_fd_info(client_ctx, BUFFER_TYPE_OUTPUT,
 				pmem_fd, kernel_vaddr, buffer_index,
 				&buff_handle);
-		if (ion_flag == CACHED) {
+		if (ion_flag == CACHED && buff_handle) {
 			msm_ion_do_cache_op(client_ctx->user_ion_client,
 					buff_handle,
 					(unsigned long *) kernel_vaddr,
@@ -1270,7 +1270,7 @@
 						kernel_vaddr,
 						buffer_index,
 						&buff_handle);
-			if (ion_flag == CACHED) {
+			if (ion_flag == CACHED && buff_handle) {
 				msm_ion_do_cache_op(client_ctx->user_ion_client,
 				buff_handle,
 				(unsigned long *)kernel_vaddr,
diff --git a/drivers/video/msm/vidc/common/enc/venc.c b/drivers/video/msm/vidc/common/enc/venc.c
index 5542856..1b77b67 100644
--- a/drivers/video/msm/vidc/common/enc/venc.c
+++ b/drivers/video/msm/vidc/common/enc/venc.c
@@ -265,7 +265,7 @@
 		ion_flag = vidc_get_fd_info(client_ctx, BUFFER_TYPE_OUTPUT,
 					pmem_fd, kernel_vaddr, buffer_index,
 					&buff_handle);
-		if (ion_flag == CACHED) {
+		if (ion_flag == CACHED && buff_handle) {
 			msm_ion_do_cache_op(client_ctx->user_ion_client,
 				buff_handle,
 				(unsigned long *) kernel_vaddr,
diff --git a/drivers/video/msm/vidc/common/enc/venc_internal.c b/drivers/video/msm/vidc/common/enc/venc_internal.c
index ad61c6e..bbbe0cf 100644
--- a/drivers/video/msm/vidc/common/enc/venc_internal.c
+++ b/drivers/video/msm/vidc/common/enc/venc_internal.c
@@ -1688,7 +1688,7 @@
 				&buff_handle);
 
 		if (vcd_input_buffer.data_len > 0) {
-			if (ion_flag == CACHED) {
+			if (ion_flag == CACHED && buff_handle) {
 				msm_ion_do_cache_op(
 				client_ctx->user_ion_client,
 				buff_handle,
diff --git a/include/linux/dvb/dmx.h b/include/linux/dvb/dmx.h
index f078f3a..7f963e6 100644
--- a/include/linux/dvb/dmx.h
+++ b/include/linux/dvb/dmx.h
@@ -5,6 +5,8 @@
  *                  & Ralph  Metzler <ralph@convergence.de>
  *                    for convergence integrated media GmbH
  *
+ * 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 Lesser General Public License
  * as published by the Free Software Foundation; either version 2.1
@@ -119,6 +121,29 @@
 	__u32          flags;
 };
 
+struct dmx_buffer_status {
+	/* size of buffer in bytes */
+	unsigned int size;
+
+	/* fullness of buffer in bytes */
+	unsigned int fullness;
+
+	/*
+	 * How many bytes are free
+	 * It's the same as: size-fullness-1
+	 */
+	unsigned int free_bytes;
+
+	/* read pointer offset in bytes */
+	unsigned int read_offset;
+
+	/* write pointer offset in bytes */
+	unsigned int write_offset;
+
+	/* non-zero if data error occured */
+	int error;
+};
+
 typedef struct dmx_caps {
 	__u32 caps;
 	int num_decoders;
@@ -135,6 +160,34 @@
 	DMX_SOURCE_DVR3
 } dmx_source_t;
 
+enum dmx_tsp_format_t {
+	DMX_TSP_FORMAT_188 = 0,
+	DMX_TSP_FORMAT_192_TAIL,
+	DMX_TSP_FORMAT_192_HEAD,
+	DMX_TSP_FORMAT_204,
+};
+
+enum dmx_playback_mode_t {
+	/*
+	 * In push mode, if one of output buffers
+	 * is full, the buffer would overflow
+	 * and demux continue processing incoming stream.
+	 * This is the default mode. When playing from frontend,
+	 * this is the only mode that is allowed.
+	 */
+	DMX_PB_MODE_PUSH = 0,
+
+	/*
+	 * In pull mode, if one of output buffers
+	 * is full, demux stalls waiting for free space,
+	 * this would cause DVR input buffer fullness
+	 * to accumulate.
+	 * This mode is possible only when playing
+	 * from DVR.
+	 */
+	DMX_PB_MODE_PULL,
+};
+
 struct dmx_stc {
 	unsigned int num;	/* input : which STC? 0..N */
 	unsigned int base;	/* output: divisor for stc to get 90 kHz clock */
@@ -153,5 +206,12 @@
 #define DMX_GET_STC              _IOWR('o', 50, struct dmx_stc)
 #define DMX_ADD_PID              _IOW('o', 51, __u16)
 #define DMX_REMOVE_PID           _IOW('o', 52, __u16)
+#define DMX_SET_TS_PACKET_FORMAT _IOW('o', 53, enum dmx_tsp_format_t)
+#define DMX_SET_TS_OUT_FORMAT	 _IOW('o', 54, enum dmx_tsp_format_t)
+#define DMX_SET_DECODER_BUFFER_SIZE	_IO('o', 55)
+#define DMX_GET_BUFFER_STATUS	 _IOR('o', 56, struct dmx_buffer_status)
+#define DMX_RELEASE_DATA		 _IO('o', 57)
+#define DMX_FEED_DATA			 _IO('o', 58)
+#define DMX_SET_PLAYBACK_MODE	 _IOW('o', 59, enum dmx_playback_mode_t)
 
 #endif /*_DVBDMX_H_*/
diff --git a/include/linux/genlock.h b/include/linux/genlock.h
index 9351a15..587c49d 100644
--- a/include/linux/genlock.h
+++ b/include/linux/genlock.h
@@ -21,7 +21,8 @@
 #define GENLOCK_WRLOCK 1
 #define GENLOCK_RDLOCK 2
 
-#define GENLOCK_NOBLOCK (1 << 0)
+#define GENLOCK_NOBLOCK       (1 << 0)
+#define GENLOCK_WRITE_TO_READ (1 << 1)
 
 struct genlock_lock {
 	int fd;
@@ -37,6 +38,8 @@
 	struct genlock_lock)
 #define GENLOCK_IOC_ATTACH _IOW(GENLOCK_IOC_MAGIC, 2, \
 	struct genlock_lock)
+
+/* Deprecated */
 #define GENLOCK_IOC_LOCK _IOW(GENLOCK_IOC_MAGIC, 3, \
 	struct genlock_lock)
 
@@ -44,4 +47,6 @@
 #define GENLOCK_IOC_RELEASE _IO(GENLOCK_IOC_MAGIC, 4)
 #define GENLOCK_IOC_WAIT _IOW(GENLOCK_IOC_MAGIC, 5, \
 	struct genlock_lock)
+#define GENLOCK_IOC_DREADLOCK _IOW(GENLOCK_IOC_MAGIC, 6, \
+	struct genlock_lock)
 #endif
diff --git a/include/linux/iopoll.h b/include/linux/iopoll.h
index 0104abc..7169870 100644
--- a/include/linux/iopoll.h
+++ b/include/linux/iopoll.h
@@ -43,7 +43,7 @@
 		if ((cond) || (timeout_us && time_after(jiffies, timeout))) \
 			break; \
 		if (sleep_us) \
-			usleep_range(1, sleep_us); \
+			usleep_range(DIV_ROUND_UP(sleep_us, 4), sleep_us); \
 	} \
 	(cond) ? 0 : -ETIMEDOUT; \
 })
diff --git a/include/linux/msm_rotator.h b/include/linux/msm_rotator.h
index 02ffd59..0f15a8b 100644
--- a/include/linux/msm_rotator.h
+++ b/include/linux/msm_rotator.h
@@ -52,7 +52,6 @@
 	unsigned int number_of_clocks;
 	unsigned int hardware_version_number;
 	struct msm_rot_clocks *rotator_clks;
-	const char *regulator_name;
 #ifdef CONFIG_MSM_BUS_SCALING
 	struct msm_bus_scale_pdata *bus_scale_table;
 #endif
diff --git a/include/linux/usb/msm_hsusb.h b/include/linux/usb/msm_hsusb.h
index 14d49eb..b53d9dd 100644
--- a/include/linux/usb/msm_hsusb.h
+++ b/include/linux/usb/msm_hsusb.h
@@ -159,19 +159,6 @@
 };
 
 /**
- * SPS Pipes direction.
- *
- * USB_TO_PEER_PERIPHERAL	USB (as Producer) to other
- *                          peer peripheral.
- * PEER_PERIPHERAL_TO_USB	Other Peripheral to
- *                          USB (as consumer).
- */
-enum usb_bam_pipe_dir {
-	USB_TO_PEER_PERIPHERAL,
-	PEER_PERIPHERAL_TO_USB,
-};
-
-/**
  * struct msm_otg_platform_data - platform device data
  *              for msm_otg driver.
  * @phy_init_seq: PHY configuration sequence. val, reg pairs
@@ -355,6 +342,10 @@
 	unsigned int dock_connect_irq;
 };
 
+struct msm_hsic_peripheral_platform_data {
+	bool keep_core_clk_on_suspend_workaround;
+};
+
 struct usb_bam_pipe_connect {
 	u32 src_phy_addr;
 	int src_pipe_index;
diff --git a/include/sound/apr_audio.h b/include/sound/apr_audio.h
index 0007fd8..b806040 100644
--- a/include/sound/apr_audio.h
+++ b/include/sound/apr_audio.h
@@ -770,7 +770,10 @@
 
 #define PCM_FORMAT_MAX_NUM_CHANNEL  8
 
-
+/* Maximum number of channels supported
+ * in ASM_ENCDEC_DEC_CHAN_MAP command
+ */
+#define MAX_CHAN_MAP_CHANNELS 16
 /*
  *  Multiple-channel PCM decoder format block structure used in the
  *  #ASM_STREAM_CMD_OPEN_WRITE command.
@@ -980,6 +983,32 @@
 	u16 sce_right;
 };
 
+struct asm_dec_chan_map {
+	u32 num_channels;			  /* Number of decoder output
+						   * channels. A value of 0
+						   * indicates native channel
+						   * mapping, which is valid
+						   * only for NT mode. This
+						   * means the output of the
+						   * decoder is to be preserved
+						   * as is.
+						   */
+
+	u8 channel_mapping[MAX_CHAN_MAP_CHANNELS];/* Channel array of size
+						   * num_channels. It can grow
+						   * till MAX_CHAN_MAP_CHANNELS.
+						   * Channel[i] mapping
+						   * describes channel I inside
+						   * the decoder output buffer.
+						   * Valid channel mapping
+						   * values are to be present at
+						   * the beginning of the array.
+						   * All remaining elements of
+						   * the array are to be filled
+						   * with PCM_CHANNEL_NULL.
+						   */
+};
+
 struct asm_encode_cfg_blk {
 	u32 frames_per_buf;
 	u32 format_id;
@@ -1132,6 +1161,14 @@
 	struct asm_dual_mono channel_map;
 } __packed;
 
+#define ASM_ENCDEC_DEC_CHAN_MAP				 0x00010D82
+struct asm_stream_cmd_encdec_channelmap {
+	struct apr_hdr hdr;
+	u32            param_id;
+	u32            param_size;
+	struct asm_dec_chan_map chan_map;
+} __packed;
+
 #define ASM_STREAM _CMD_ADJUST_SAMPLES                   0x00010C0A
 struct asm_stream_cmd_adjust_samples{
 	struct apr_hdr hdr;
diff --git a/include/sound/q6asm.h b/include/sound/q6asm.h
index aec4171..968d46e 100644
--- a/include/sound/q6asm.h
+++ b/include/sound/q6asm.h
@@ -247,6 +247,9 @@
 int q6asm_cfg_dual_mono_aac(struct audio_client *ac,
 			uint16_t sce_left, uint16_t sce_right);
 
+int q6asm_set_encdec_chan_map(struct audio_client *ac,
+			uint32_t num_channels);
+
 int q6asm_enc_cfg_blk_qcelp(struct audio_client *ac, uint32_t frames_per_buf,
 		uint16_t min_rate, uint16_t max_rate,
 		uint16_t reduced_rate_level, uint16_t rate_modulation_cmd);
diff --git a/net/bluetooth/amp.c b/net/bluetooth/amp.c
index df80c42..f4ceeea 100644
--- a/net/bluetooth/amp.c
+++ b/net/bluetooth/amp.c
@@ -374,13 +374,11 @@
 {
 	struct amp_mgr *mgr;
 
-	read_lock(&amp_mgr_list_lock);
 	list_for_each_entry(mgr, &amp_mgr_list, list) {
 		if (mgr->discovered)
 			send_a2mp_cl(mgr, next_ident(mgr),
 					A2MP_CHANGE_NOTIFY, 0, NULL);
 	}
-	read_unlock(&amp_mgr_list_lock);
 }
 
 static inline int discover_req(struct amp_mgr *mgr, struct sk_buff *skb)
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 9f9f17d..a7b95d3 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -3365,6 +3365,7 @@
 		break;
 
 	case L2CAP_MODE_STREAMING:
+		l2cap_setup_txwin(pi);
 		rfc.txwin_size      = 0;
 		rfc.max_transmit    = 0;
 		rfc.retrans_timeout = 0;
diff --git a/sound/soc/codecs/wcd9304.c b/sound/soc/codecs/wcd9304.c
index 6b590c9..4b6d83b 100644
--- a/sound/soc/codecs/wcd9304.c
+++ b/sound/soc/codecs/wcd9304.c
@@ -31,6 +31,8 @@
 #include <linux/bitops.h>
 #include <linux/delay.h>
 #include <linux/pm_runtime.h>
+#include <linux/kernel.h>
+#include <linux/gpio.h>
 #include "wcd9304.h"
 
 #define WCD9304_RATES (SNDRV_PCM_RATE_8000|SNDRV_PCM_RATE_16000|\
@@ -66,6 +68,21 @@
 
 #define SITAR_FAKE_INS_THRESHOLD_MS 2500
 #define SITAR_FAKE_REMOVAL_MIN_PERIOD_MS 50
+#define SITAR_MBHC_BUTTON_MIN 0x8000
+#define SITAR_GPIO_IRQ_DEBOUNCE_TIME_US 5000
+
+#define SITAR_ACQUIRE_LOCK(x) do { mutex_lock(&x); } while (0)
+#define SITAR_RELEASE_LOCK(x) do { mutex_unlock(&x); } while (0)
+
+#define MBHC_NUM_DCE_PLUG_DETECT 3
+#define SITAR_MBHC_FAKE_INSERT_LOW 10
+#define SITAR_MBHC_FAKE_INSERT_HIGH 80
+#define SITAR_MBHC_FAKE_INSERT_VOLT_DELTA_MV 500
+#define SITAR_HS_DETECT_PLUG_TIME_MS (5 * 1000)
+#define SITAR_HS_DETECT_PLUG_INERVAL_MS 100
+#define NUM_ATTEMPTS_TO_REPORT 5
+#define SITAR_MBHC_STATUS_REL_DETECTION 0x0C
+#define SITAR_MBHC_GPIO_REL_DEBOUNCE_TIME_MS 200
 
 static const DECLARE_TLV_DB_SCALE(digital_gain, 0, 1, 0);
 static const DECLARE_TLV_DB_SCALE(line_gain, 0, 7, 1);
@@ -139,6 +156,21 @@
 	u8 nbounce_wait;
 };
 
+enum sitar_mbhc_plug_type {
+	PLUG_TYPE_INVALID = -1,
+	PLUG_TYPE_NONE,
+	PLUG_TYPE_HEADSET,
+	PLUG_TYPE_HEADPHONE,
+	PLUG_TYPE_HIGH_HPH,
+};
+
+enum sitar_mbhc_state {
+	MBHC_STATE_NONE = -1,
+	MBHC_STATE_POTENTIAL,
+	MBHC_STATE_POTENTIAL_RECOVERY,
+	MBHC_STATE_RELEASE,
+};
+
 struct sitar_priv {
 	struct snd_soc_codec *codec;
 	u32 mclk_freq;
@@ -167,15 +199,10 @@
 	void *calibration;
 	struct mbhc_internal_cal_data mbhc_data;
 
-	struct snd_soc_jack *headset_jack;
-	struct snd_soc_jack *button_jack;
-
 	struct wcd9xxx_pdata *pdata;
 	u32 anc_slot;
 
 	bool no_mic_headset_override;
-	/* Delayed work to report long button press */
-	struct delayed_work btn0_dwork;
 
 	struct mbhc_micbias_regs mbhc_bias_regs;
 	u8 cfilt_k_value;
@@ -207,6 +234,23 @@
 
 	/* num of slim ports required */
 	struct sitar_codec_dai_data dai[NUM_CODEC_DAIS];
+
+	/* Currently, only used for mbhc purpose, to protect
+	 * concurrent execution of mbhc threaded irq handlers and
+	 * kill race between DAPM and MBHC.But can serve as a
+	 * general lock to protect codec resource
+	 */
+	struct mutex codec_resource_lock;
+
+	struct sitar_mbhc_config mbhc_cfg;
+	bool in_gpio_handler;
+	u8 current_plug;
+	bool lpi_enabled;
+	enum sitar_mbhc_state mbhc_state;
+	struct work_struct hs_correct_plug_work;
+	bool hs_detect_work_stop;
+	struct delayed_work mbhc_btn_dwork;
+	unsigned long mbhc_last_resume; /* in jiffies */
 };
 
 #ifdef CONFIG_DEBUG_FS
@@ -734,10 +778,6 @@
 	SOC_DAPM_SINGLE("Switch", SITAR_A_RX_EAR_EN, 5, 1, 0),
 };
 
-static const struct snd_kcontrol_new hphl_switch[] = {
-	SOC_DAPM_SINGLE("Switch", SITAR_A_RX_HPH_L_DAC_CTL, 6, 1, 0)
-};
-
 static void sitar_codec_enable_adc_block(struct snd_soc_codec *codec,
 	int enable)
 {
@@ -910,27 +950,41 @@
 	return 0;
 }
 
-
-static void sitar_codec_disable_button_presses(struct snd_soc_codec *codec)
-{
-	snd_soc_write(codec, SITAR_A_CDC_MBHC_VOLT_B4_CTL, 0x80);
-	snd_soc_write(codec, SITAR_A_CDC_MBHC_VOLT_B3_CTL, 0x00);
-}
-
 static void sitar_codec_start_hs_polling(struct snd_soc_codec *codec)
 {
 	struct sitar_priv *sitar = snd_soc_codec_get_drvdata(codec);
+	int mbhc_state = sitar->mbhc_state;
+
+	pr_debug("%s: enter\n", __func__);
+	if (!sitar->mbhc_polling_active) {
+		pr_debug("Polling is not active, do not start polling\n");
+		return;
+	}
+	snd_soc_write(codec, SITAR_A_MBHC_SCALING_MUX_1, 0x84);
+
+
+	if (!sitar->no_mic_headset_override) {
+		if (mbhc_state == MBHC_STATE_POTENTIAL) {
+			pr_debug("%s recovering MBHC state macine\n", __func__);
+			sitar->mbhc_state = MBHC_STATE_POTENTIAL_RECOVERY;
+			/* set to max button press threshold */
+			snd_soc_write(codec, SITAR_A_CDC_MBHC_VOLT_B2_CTL,
+				      0x7F);
+			snd_soc_write(codec, SITAR_A_CDC_MBHC_VOLT_B1_CTL,
+				      0xFF);
+			snd_soc_write(codec, SITAR_A_CDC_MBHC_VOLT_B4_CTL,
+				       0x7F);
+			snd_soc_write(codec, SITAR_A_CDC_MBHC_VOLT_B3_CTL,
+				      0xFF);
+			/* set to max */
+			snd_soc_write(codec, SITAR_A_CDC_MBHC_VOLT_B6_CTL,
+				      0x7F);
+			snd_soc_write(codec, SITAR_A_CDC_MBHC_VOLT_B5_CTL,
+				      0xFF);
+		}
+	}
 
 	snd_soc_write(codec, SITAR_A_MBHC_SCALING_MUX_1, 0x84);
-	wcd9xxx_enable_irq(codec->control_data, SITAR_IRQ_MBHC_REMOVAL);
-	if (!sitar->no_mic_headset_override) {
-		wcd9xxx_enable_irq(codec->control_data,
-				SITAR_IRQ_MBHC_POTENTIAL);
-		wcd9xxx_enable_irq(codec->control_data,
-				SITAR_IRQ_MBHC_RELEASE);
-	} else {
-		sitar_codec_disable_button_presses(codec);
-	}
 	snd_soc_write(codec, SITAR_A_CDC_MBHC_EN_CTL, 0x1);
 	snd_soc_update_bits(codec, SITAR_A_CDC_MBHC_CLK_CTL, 0x8, 0x0);
 	snd_soc_write(codec, SITAR_A_CDC_MBHC_EN_CTL, 0x1);
@@ -940,14 +994,15 @@
 {
 	struct sitar_priv *sitar = snd_soc_codec_get_drvdata(codec);
 
-	snd_soc_update_bits(codec, SITAR_A_CDC_MBHC_CLK_CTL, 0x8, 0x8);
-	wcd9xxx_disable_irq(codec->control_data, SITAR_IRQ_MBHC_REMOVAL);
-	if (!sitar->no_mic_headset_override) {
-		wcd9xxx_disable_irq(codec->control_data,
-			SITAR_IRQ_MBHC_POTENTIAL);
-		wcd9xxx_disable_irq(codec->control_data,
-			SITAR_IRQ_MBHC_RELEASE);
+	pr_debug("%s: enter\n", __func__);
+	if (!sitar->mbhc_polling_active) {
+		pr_debug("polling not active, nothing to pause\n");
+		return;
 	}
+
+	snd_soc_update_bits(codec, SITAR_A_CDC_MBHC_CLK_CTL, 0x8, 0x8);
+	pr_debug("%s: leave\n", __func__);
+
 }
 
 static void sitar_codec_switch_cfilt_mode(struct snd_soc_codec *codec,
@@ -966,6 +1021,7 @@
 			sitar->mbhc_bias_regs.cfilt_ctl) & 0x40;
 
 	if (cur_mode_val != reg_mode_val) {
+		SITAR_ACQUIRE_LOCK(sitar->codec_resource_lock);
 		if (sitar->mbhc_polling_active) {
 			sitar_codec_pause_hs_polling(codec);
 			mbhc_was_polling = true;
@@ -974,6 +1030,7 @@
 			sitar->mbhc_bias_regs.cfilt_ctl, 0x40, reg_mode_val);
 		if (mbhc_was_polling)
 			sitar_codec_start_hs_polling(codec);
+		SITAR_RELEASE_LOCK(sitar->codec_resource_lock);
 		pr_debug("%s: CFILT mode change (%x to %x)\n", __func__,
 			cur_mode_val, reg_mode_val);
 	} else {
@@ -1077,10 +1134,10 @@
 	u8 hph_reg_val = 0;
 	if (left)
 		hph_reg_val = snd_soc_read(codec,
-					  SITAR_A_RX_HPH_L_DAC_CTL);
+					   SITAR_A_RX_HPH_L_DAC_CTL);
 	else
 		hph_reg_val = snd_soc_read(codec,
-					  SITAR_A_RX_HPH_R_DAC_CTL);
+					   SITAR_A_RX_HPH_R_DAC_CTL);
 
 	return (hph_reg_val & 0xC0) ? true : false;
 }
@@ -1094,7 +1151,8 @@
 
 	switch (vddio_switch) {
 	case 1:
-		if (sitar->mbhc_polling_active) {
+		if (sitar->mbhc_micbias_switched == 0 &&
+			sitar->mbhc_polling_active) {
 
 			sitar_codec_pause_hs_polling(codec);
 			/* Enable Mic Bias switch to VDDIO */
@@ -1176,9 +1234,11 @@
 	switch (event) {
 	case SND_SOC_DAPM_PRE_PMU:
 		/* Decide whether to switch the micbias for MBHC */
-		if ((w->reg == sitar->mbhc_bias_regs.ctl_reg)
-				&& sitar->mbhc_micbias_switched)
+		if (w->reg == sitar->mbhc_bias_regs.ctl_reg) {
+			SITAR_ACQUIRE_LOCK(sitar->codec_resource_lock);
 			sitar_codec_switch_micbias(codec, 0);
+			SITAR_RELEASE_LOCK(sitar->codec_resource_lock);
+		}
 
 		snd_soc_update_bits(codec, w->reg, 0x1E, 0x00);
 		sitar_codec_update_cfilt_usage(codec, cfilt_sel_val, 1);
@@ -1191,8 +1251,10 @@
 	case SND_SOC_DAPM_POST_PMU:
 		if (sitar->mbhc_polling_active &&
 		    sitar->micbias == micb_line) {
+			SITAR_ACQUIRE_LOCK(sitar->codec_resource_lock);
 			sitar_codec_pause_hs_polling(codec);
 			sitar_codec_start_hs_polling(codec);
+			SITAR_RELEASE_LOCK(sitar->codec_resource_lock);
 		}
 		break;
 	case SND_SOC_DAPM_POST_PMD:
@@ -1302,7 +1364,7 @@
 	}
 	return 0;
 }
-static int sitar_hphr_dac_event(struct snd_soc_dapm_widget *w,
+static int sitar_hph_dac_event(struct snd_soc_dapm_widget *w,
 	struct snd_kcontrol *kcontrol, int event)
 {
 	struct snd_soc_codec *codec = w->codec;
@@ -1333,18 +1395,22 @@
 {
 	struct snd_soc_codec *codec;
 
-	if (sitar) {
-		pr_info("%s: clear ocp status %x\n", __func__, jack_status);
-		codec = sitar->codec;
+	if (!sitar) {
+		pr_err("%s: Bad sitar private data\n", __func__);
+		return;
+	}
+
+	pr_info("%s: clear ocp status %x\n", __func__, jack_status);
+	codec = sitar->codec;
+	if (sitar->hph_status & jack_status) {
 		sitar->hph_status &= ~jack_status;
-		if (sitar->headset_jack)
-			sitar_snd_soc_jack_report(sitar, sitar->headset_jack,
-						 sitar->hph_status,
-						 SITAR_JACK_MASK);
-		snd_soc_update_bits(codec, SITAR_A_RX_HPH_OCP_CTL, 0x10,
-		0x00);
-		snd_soc_update_bits(codec, SITAR_A_RX_HPH_OCP_CTL, 0x10,
-		0x10);
+		if (sitar->mbhc_cfg.headset_jack)
+			sitar_snd_soc_jack_report(sitar,
+						sitar->mbhc_cfg.headset_jack,
+						sitar->hph_status,
+						SITAR_JACK_MASK);
+		snd_soc_update_bits(codec, SITAR_A_RX_HPH_OCP_CTL, 0x10, 0x00);
+		snd_soc_update_bits(codec, SITAR_A_RX_HPH_OCP_CTL, 0x10, 0x10);
 		/* reset retry counter as PA is turned off signifying
 		* start of new OCP detection session
 		*/
@@ -1353,8 +1419,6 @@
 		else
 			sitar->hphrocp_cnt = 0;
 		wcd9xxx_enable_irq(codec->control_data, irq);
-	} else {
-		pr_err("%s: Bad sitar private data\n", __func__);
 	}
 }
 
@@ -1385,9 +1449,11 @@
 		mbhc_micb_ctl_val = snd_soc_read(codec,
 				sitar->mbhc_bias_regs.ctl_reg);
 
-		if (!(mbhc_micb_ctl_val & 0x80)
-				&& !sitar->mbhc_micbias_switched)
+		if (!(mbhc_micb_ctl_val & 0x80)) {
+			SITAR_ACQUIRE_LOCK(sitar->codec_resource_lock);
 			sitar_codec_switch_micbias(codec, 1);
+			SITAR_RELEASE_LOCK(sitar->codec_resource_lock);
+		}
 
 		break;
 
@@ -1413,8 +1479,9 @@
 				schedule_work(&sitar->hphrocp_work);
 		}
 
-		if (sitar->mbhc_micbias_switched)
-			sitar_codec_switch_micbias(codec, 0);
+		SITAR_ACQUIRE_LOCK(sitar->codec_resource_lock);
+		sitar_codec_switch_micbias(codec, 0);
+		SITAR_RELEASE_LOCK(sitar->codec_resource_lock);
 
 		pr_debug("%s: sleep 10 ms after %s PA disable.\n", __func__,
 				w->name);
@@ -1527,15 +1594,16 @@
 	SND_SOC_DAPM_PGA_E("HPHL", SITAR_A_RX_HPH_CNP_EN, 5, 0, NULL, 0,
 		sitar_hph_pa_event, SND_SOC_DAPM_PRE_PMU |
 			SND_SOC_DAPM_POST_PMD),
-	SND_SOC_DAPM_MIXER("HPHL DAC", SITAR_A_RX_HPH_L_DAC_CTL, 7, 0,
-		hphl_switch, ARRAY_SIZE(hphl_switch)),
 
 	SND_SOC_DAPM_PGA_E("HPHR", SITAR_A_RX_HPH_CNP_EN, 4, 0, NULL, 0,
 		sitar_hph_pa_event, SND_SOC_DAPM_PRE_PMU |
 			SND_SOC_DAPM_POST_PMD),
 
+	SND_SOC_DAPM_DAC_E("HPHL DAC", NULL, SITAR_A_RX_HPH_L_DAC_CTL, 7, 0,
+		sitar_hph_dac_event,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
 	SND_SOC_DAPM_DAC_E("HPHR DAC", NULL, SITAR_A_RX_HPH_R_DAC_CTL, 7, 0,
-		sitar_hphr_dac_event,
+		sitar_hph_dac_event,
 		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
 
 	/* Speaker */
@@ -1708,7 +1776,7 @@
 	{"HEADPHONE", NULL, "HPHR"},
 
 	{"HPHL", NULL, "HPHL DAC"},
-	{"HPHL DAC", "Switch", "DAC4 MUX"},
+	{"HPHL DAC", "NULL", "DAC4 MUX"},
 	{"HPHR", NULL, "HPHR DAC"},
 	{"HPHR DAC", NULL, "RX3 MIX1"},
 
@@ -2015,9 +2083,9 @@
 
 static int sitar_codec_mclk_index(const struct sitar_priv *sitar)
 {
-	if (sitar->mclk_freq == SITAR_MCLK_RATE_12288KHZ)
+	if (sitar->mbhc_cfg.mclk_rate == SITAR_MCLK_RATE_12288KHZ)
 		return 0;
-	else if (sitar->mclk_freq == SITAR_MCLK_RATE_9600KHZ)
+	else if (sitar->mbhc_cfg.mclk_rate == SITAR_MCLK_RATE_9600KHZ)
 		return 1;
 	else {
 		BUG_ON(1);
@@ -2031,7 +2099,7 @@
 	struct sitar_mbhc_btn_detect_cfg *btn_det;
 	struct sitar_priv *sitar = snd_soc_codec_get_drvdata(codec);
 
-	btn_det = SITAR_MBHC_CAL_BTN_DET_PTR(sitar->calibration);
+	btn_det = SITAR_MBHC_CAL_BTN_DET_PTR(sitar->mbhc_cfg.calibration);
 
 	snd_soc_write(codec, SITAR_A_CDC_MBHC_VOLT_B1_CTL,
 		      sitar->mbhc_data.v_ins_hu & 0xFF);
@@ -2096,12 +2164,14 @@
 		substream->name, substream->stream);
 }
 
-int sitar_mclk_enable(struct snd_soc_codec *codec, int mclk_enable)
+int sitar_mclk_enable(struct snd_soc_codec *codec, int mclk_enable, bool dapm)
 {
 	struct sitar_priv *sitar = snd_soc_codec_get_drvdata(codec);
 
 	pr_debug("%s() mclk_enable = %u\n", __func__, mclk_enable);
 
+	if (dapm)
+		SITAR_ACQUIRE_LOCK(sitar->codec_resource_lock);
 	if (mclk_enable) {
 		sitar->mclk_enabled = true;
 
@@ -2120,6 +2190,8 @@
 	} else {
 
 		if (!sitar->mclk_enabled) {
+			if (dapm)
+				SITAR_RELEASE_LOCK(sitar->codec_resource_lock);
 			pr_err("Error, MCLK already diabled\n");
 			return -EINVAL;
 		}
@@ -2143,6 +2215,8 @@
 				SITAR_BANDGAP_OFF);
 		}
 	}
+	if (dapm)
+		SITAR_RELEASE_LOCK(sitar->codec_resource_lock);
 	return 0;
 }
 
@@ -2558,13 +2632,25 @@
 	return bias_value;
 }
 
-static short sitar_codec_sta_dce(struct snd_soc_codec *codec, int dce)
+static void sitar_turn_onoff_rel_detection(struct snd_soc_codec *codec,
+				bool on)
 {
-	struct sitar_priv *sitar = snd_soc_codec_get_drvdata(codec);
+	snd_soc_update_bits(codec, SITAR_A_CDC_MBHC_B1_CTL, 0x02, on << 1);
+}
+
+static short __sitar_codec_sta_dce(struct snd_soc_codec *codec, int dce,
+				   bool override_bypass, bool noreldetection)
+{
 	short bias_value;
+	struct sitar_priv *sitar = snd_soc_codec_get_drvdata(codec);
+
+	wcd9xxx_disable_irq(codec->control_data, SITAR_IRQ_MBHC_POTENTIAL);
+	if (noreldetection)
+		sitar_turn_onoff_rel_detection(codec, false);
 
 	/* Turn on the override */
-	snd_soc_update_bits(codec, SITAR_A_CDC_MBHC_B1_CTL, 0x4, 0x4);
+	if (!override_bypass)
+		snd_soc_update_bits(codec, SITAR_A_CDC_MBHC_B1_CTL, 0x4, 0x4);
 	if (dce) {
 		snd_soc_update_bits(codec, SITAR_A_CDC_MBHC_CLK_CTL, 0x8, 0x8);
 		snd_soc_write(codec, SITAR_A_CDC_MBHC_EN_CTL, 0x4);
@@ -2589,24 +2675,73 @@
 		snd_soc_write(codec, SITAR_A_CDC_MBHC_EN_CTL, 0x0);
 	}
 	/* Turn off the override after measuring mic voltage */
-	snd_soc_update_bits(codec, SITAR_A_CDC_MBHC_B1_CTL, 0x04, 0x00);
+	if (!override_bypass)
+		snd_soc_update_bits(codec, SITAR_A_CDC_MBHC_B1_CTL, 0x04, 0x00);
+
+	if (noreldetection)
+		sitar_turn_onoff_rel_detection(codec, true);
+	wcd9xxx_enable_irq(codec->control_data, SITAR_IRQ_MBHC_POTENTIAL);
 
 	return bias_value;
 }
 
+static short sitar_codec_sta_dce(struct snd_soc_codec *codec, int dce,
+				bool norel)
+{
+	return __sitar_codec_sta_dce(codec, dce, false, norel);
+}
+
+static void sitar_codec_shutdown_hs_removal_detect(struct snd_soc_codec *codec)
+{
+	struct sitar_priv *sitar = snd_soc_codec_get_drvdata(codec);
+	const struct sitar_mbhc_general_cfg *generic =
+	    SITAR_MBHC_CAL_GENERAL_PTR(sitar->mbhc_cfg.calibration);
+
+	if (!sitar->mclk_enabled && !sitar->mbhc_polling_active)
+		sitar_codec_enable_config_mode(codec, 1);
+
+	snd_soc_update_bits(codec, SITAR_A_CDC_MBHC_CLK_CTL, 0x2, 0x2);
+	snd_soc_update_bits(codec, SITAR_A_CDC_MBHC_B1_CTL, 0x6, 0x0);
+
+	snd_soc_update_bits(codec, sitar->mbhc_bias_regs.mbhc_reg, 0x80, 0x00);
+
+	usleep_range(generic->t_shutdown_plug_rem,
+		     generic->t_shutdown_plug_rem);
+
+	snd_soc_update_bits(codec, SITAR_A_CDC_MBHC_CLK_CTL, 0xA, 0x8);
+	if (!sitar->mclk_enabled && !sitar->mbhc_polling_active)
+		sitar_codec_enable_config_mode(codec, 0);
+
+	snd_soc_write(codec, SITAR_A_MBHC_SCALING_MUX_1, 0x00);
+}
+
+static void sitar_codec_cleanup_hs_polling(struct snd_soc_codec *codec)
+{
+	struct sitar_priv *sitar = snd_soc_codec_get_drvdata(codec);
+
+	sitar_codec_shutdown_hs_removal_detect(codec);
+
+	if (!sitar->mclk_enabled) {
+		sitar_codec_disable_clock_block(codec);
+		sitar_codec_enable_bandgap(codec, SITAR_BANDGAP_OFF);
+	}
+
+	sitar->mbhc_polling_active = false;
+	sitar->mbhc_state = MBHC_STATE_NONE;
+}
+
+/* called only from interrupt which is under codec_resource_lock acquisition */
 static short sitar_codec_setup_hs_polling(struct snd_soc_codec *codec)
 {
 	struct sitar_priv *sitar = snd_soc_codec_get_drvdata(codec);
 	short bias_value;
 	u8 cfilt_mode;
 
-	if (!sitar->calibration) {
+	if (!sitar->mbhc_cfg.calibration) {
 		pr_err("Error, no sitar calibration\n");
 		return -ENODEV;
 	}
 
-	sitar->mbhc_polling_active = true;
-
 	if (!sitar->mclk_enabled) {
 		sitar_codec_enable_bandgap(codec, SITAR_BANDGAP_MBHC_MODE);
 		sitar_enable_rx_bias(codec, 1);
@@ -2615,13 +2750,11 @@
 
 	snd_soc_update_bits(codec, SITAR_A_CLK_BUFF_EN1, 0x05, 0x01);
 
-	snd_soc_update_bits(codec, SITAR_A_TX_COM_BIAS, 0xE0, 0xE0);
-
 	/* Make sure CFILT is in fast mode, save current mode */
-	cfilt_mode = snd_soc_read(codec,
-		sitar->mbhc_bias_regs.cfilt_ctl);
-	snd_soc_update_bits(codec, sitar->mbhc_bias_regs.cfilt_ctl,
-		0x70, 0x00);
+	cfilt_mode = snd_soc_read(codec, sitar->mbhc_bias_regs.cfilt_ctl);
+	snd_soc_update_bits(codec, sitar->mbhc_bias_regs.cfilt_ctl, 0x70, 0x00);
+
+	snd_soc_update_bits(codec, sitar->mbhc_bias_regs.ctl_reg, 0x1F, 0x16);
 
 	snd_soc_update_bits(codec, SITAR_A_CDC_MBHC_CLK_CTL, 0x2, 0x2);
 	snd_soc_write(codec, SITAR_A_MBHC_SCALING_MUX_1, 0x84);
@@ -2639,7 +2772,8 @@
 
 	sitar_codec_calibrate_hs_polling(codec);
 
-	bias_value = sitar_codec_sta_dce(codec, 0);
+	/* don't flip override */
+	bias_value = __sitar_codec_sta_dce(codec, 1, true, true);
 	snd_soc_update_bits(codec, sitar->mbhc_bias_regs.cfilt_ctl, 0x40,
 			    cfilt_mode);
 	snd_soc_update_bits(codec, SITAR_A_MBHC_HPH, 0x13, 0x00);
@@ -2647,111 +2781,21 @@
 	return bias_value;
 }
 
-static int sitar_codec_enable_hs_detect(struct snd_soc_codec *codec,
-		int insertion)
+static int sitar_cancel_btn_work(struct sitar_priv *sitar)
 {
-	struct sitar_priv *sitar = snd_soc_codec_get_drvdata(codec);
-	int central_bias_enabled = 0;
-	const struct sitar_mbhc_general_cfg *generic =
-	    SITAR_MBHC_CAL_GENERAL_PTR(sitar->calibration);
-	const struct sitar_mbhc_plug_detect_cfg *plug_det =
-	    SITAR_MBHC_CAL_PLUG_DET_PTR(sitar->calibration);
-	u8 wg_time;
+	int r = 0;
+	struct wcd9xxx *core = dev_get_drvdata(sitar->codec->dev->parent);
 
-	if (!sitar->calibration) {
-		pr_err("Error, no sitar calibration\n");
-		return -EINVAL;
+	if (cancel_delayed_work_sync(&sitar->mbhc_btn_dwork)) {
+		/* if scheduled mbhc_btn_dwork is canceled from here,
+		 * we have to unlock from here instead btn_work */
+		wcd9xxx_unlock_sleep(core);
+		r = 1;
 	}
-
-	snd_soc_update_bits(codec, SITAR_A_CDC_MBHC_INT_CTL, 0x1, 0);
-
-	if (insertion) {
-		/* Make sure mic bias and Mic line schmitt trigger
-		* are turned OFF
-		*/
-		snd_soc_update_bits(codec, sitar->mbhc_bias_regs.ctl_reg,
-			0x81, 0x01);
-		snd_soc_update_bits(codec, sitar->mbhc_bias_regs.mbhc_reg,
-			0x90, 0x00);
-		wg_time = snd_soc_read(codec, SITAR_A_RX_HPH_CNP_WG_TIME) ;
-		wg_time += 1;
-
-		/* Enable HPH Schmitt Trigger */
-		snd_soc_update_bits(codec, SITAR_A_MBHC_HPH, 0x11, 0x11);
-		snd_soc_update_bits(codec, SITAR_A_MBHC_HPH, 0x0C,
-			plug_det->hph_current << 2);
-
-		/* Turn off HPH PAs and DAC's during insertion detection to
-		* avoid false insertion interrupts
-		*/
-		if (sitar->mbhc_micbias_switched)
-			sitar_codec_switch_micbias(codec, 0);
-		snd_soc_update_bits(codec, SITAR_A_RX_HPH_CNP_EN, 0x30, 0x00);
-		snd_soc_update_bits(codec, SITAR_A_RX_HPH_L_DAC_CTL,
-			0xC0, 0x00);
-		snd_soc_update_bits(codec, SITAR_A_RX_HPH_R_DAC_CTL,
-			0xC0, 0x00);
-		usleep_range(wg_time * 1000, wg_time * 1000);
-
-		/* setup for insetion detection */
-		snd_soc_update_bits(codec, SITAR_A_MBHC_HPH, 0x02, 0x02);
-		snd_soc_update_bits(codec, SITAR_A_CDC_MBHC_INT_CTL, 0x2, 0);
-	} else {
-		/* Make sure the HPH schmitt trigger is OFF */
-		snd_soc_update_bits(codec, SITAR_A_MBHC_HPH, 0x12, 0x00);
-
-		/* enable the mic line schmitt trigger */
-		snd_soc_update_bits(codec, sitar->mbhc_bias_regs.mbhc_reg, 0x60,
-			plug_det->mic_current << 5);
-		snd_soc_update_bits(codec, sitar->mbhc_bias_regs.mbhc_reg,
-			0x80, 0x80);
-		usleep_range(plug_det->t_mic_pid, plug_det->t_mic_pid);
-		snd_soc_update_bits(codec, sitar->mbhc_bias_regs.mbhc_reg,
-			0x10, 0x10);
-
-		/* Setup for low power removal detection */
-		snd_soc_update_bits(codec, SITAR_A_CDC_MBHC_INT_CTL, 0x2, 0x2);
-	}
-
-	if (snd_soc_read(codec, SITAR_A_CDC_MBHC_B1_CTL) & 0x4) {
-		if (!(sitar->clock_active)) {
-			sitar_codec_enable_config_mode(codec, 1);
-			snd_soc_update_bits(codec, SITAR_A_CDC_MBHC_B1_CTL,
-				0x06, 0);
-			usleep_range(generic->t_shutdown_plug_rem,
-				generic->t_shutdown_plug_rem);
-			sitar_codec_enable_config_mode(codec, 0);
-		} else
-			snd_soc_update_bits(codec, SITAR_A_CDC_MBHC_B1_CTL,
-				0x06, 0);
-	}
-
-	snd_soc_update_bits(codec, sitar->mbhc_bias_regs.int_rbias, 0x80, 0);
-
-	/* If central bandgap disabled */
-	if (!(snd_soc_read(codec, SITAR_A_PIN_CTL_OE1) & 1)) {
-		snd_soc_update_bits(codec, SITAR_A_PIN_CTL_OE1, 0x3, 0x3);
-		usleep_range(generic->t_bg_fast_settle,
-			generic->t_bg_fast_settle);
-		central_bias_enabled = 1;
-	}
-
-	/* If LDO_H disabled */
-	if (snd_soc_read(codec, SITAR_A_PIN_CTL_OE0) & 0x80) {
-		snd_soc_update_bits(codec, SITAR_A_PIN_CTL_OE0, 0x10, 0);
-		snd_soc_update_bits(codec, SITAR_A_PIN_CTL_OE0, 0x80, 0x80);
-		usleep_range(generic->t_ldoh, generic->t_ldoh);
-		snd_soc_update_bits(codec, SITAR_A_PIN_CTL_OE0, 0x80, 0);
-
-		if (central_bias_enabled)
-			snd_soc_update_bits(codec, SITAR_A_PIN_CTL_OE1, 0x1, 0);
-	}
-
-	wcd9xxx_enable_irq(codec->control_data, SITAR_IRQ_MBHC_INSERTION);
-	snd_soc_update_bits(codec, SITAR_A_CDC_MBHC_INT_CTL, 0x1, 0x1);
-	return 0;
+	return r;
 }
 
+
 static u16 sitar_codec_v_sta_dce(struct snd_soc_codec *codec, bool dce,
 				 s16 vin_mv)
 {
@@ -2783,58 +2827,63 @@
 				 u16 bias_value)
 {
 	struct sitar_priv *sitar;
+	s16 value, z, mb;
 	s32 mv;
 
 	sitar = snd_soc_codec_get_drvdata(codec);
+	value = bias_value;
 
 	if (dce) {
-		mv = ((s32)bias_value - (s32)sitar->mbhc_data.dce_z) *
-		     (s32)sitar->mbhc_data.micb_mv /
-		     (s32)(sitar->mbhc_data.dce_mb - sitar->mbhc_data.dce_z);
+		z = (sitar->mbhc_data.dce_z);
+		mb = (sitar->mbhc_data.dce_mb);
+		mv = (value - z) * (s32)sitar->mbhc_data.micb_mv / (mb - z);
 	} else {
-		mv = ((s32)bias_value - (s32)sitar->mbhc_data.sta_z) *
-		     (s32)sitar->mbhc_data.micb_mv /
-		     (s32)(sitar->mbhc_data.sta_mb - sitar->mbhc_data.sta_z);
+		z = (sitar->mbhc_data.sta_z);
+		mb = (sitar->mbhc_data.sta_mb);
+		mv = (value - z) * (s32)sitar->mbhc_data.micb_mv / (mb - z);
 	}
 
 	return mv;
 }
 
-static void btn0_lpress_fn(struct work_struct *work)
+static void btn_lpress_fn(struct work_struct *work)
 {
 	struct delayed_work *delayed_work;
 	struct sitar_priv *sitar;
 	short bias_value;
 	int dce_mv, sta_mv;
-	struct sitar *core;
+	struct wcd9xxx *core;
 
 	pr_debug("%s:\n", __func__);
 
 	delayed_work = to_delayed_work(work);
-	sitar = container_of(delayed_work, struct sitar_priv, btn0_dwork);
+	sitar = container_of(delayed_work, struct sitar_priv, mbhc_btn_dwork);
 	core = dev_get_drvdata(sitar->codec->dev->parent);
 
 	if (sitar) {
-		if (sitar->button_jack) {
+		if (sitar->mbhc_cfg.button_jack) {
 			bias_value = sitar_codec_read_sta_result(sitar->codec);
 			sta_mv = sitar_codec_sta_dce_v(sitar->codec, 0,
-						bias_value);
+							bias_value);
 			bias_value = sitar_codec_read_dce_result(sitar->codec);
 			dce_mv = sitar_codec_sta_dce_v(sitar->codec, 1,
-						bias_value);
+							bias_value);
 			pr_debug("%s: Reporting long button press event"
-				 " STA: %d, DCE: %d\n", __func__,
-				 sta_mv, dce_mv);
-			sitar_snd_soc_jack_report(sitar, sitar->button_jack,
-						  SND_JACK_BTN_0,
-						  SND_JACK_BTN_0);
+			" STA: %d, DCE: %d\n", __func__, sta_mv, dce_mv);
+			sitar_snd_soc_jack_report(sitar,
+						sitar->mbhc_cfg.button_jack,
+						sitar->buttons_pressed,
+						sitar->buttons_pressed);
 		}
 	} else {
 		pr_err("%s: Bad sitar private data\n", __func__);
 	}
 
+	pr_debug("%s: leave\n", __func__);
+	wcd9xxx_unlock_sleep(core);
 }
 
+
 void sitar_mbhc_cal(struct snd_soc_codec *codec)
 {
 	struct sitar_priv *sitar;
@@ -2844,24 +2893,29 @@
 	u32 mclk_rate;
 	u32 dce_wait, sta_wait;
 	u8 *n_cic;
+	void *calibration;
 
 	sitar = snd_soc_codec_get_drvdata(codec);
+	calibration = sitar->mbhc_cfg.calibration;
+
+	wcd9xxx_disable_irq(codec->control_data, SITAR_IRQ_MBHC_POTENTIAL);
+	sitar_turn_onoff_rel_detection(codec, false);
 
 	/* First compute the DCE / STA wait times
 	 * depending on tunable parameters.
 	 * The value is computed in microseconds
 	 */
-	btn_det = SITAR_MBHC_CAL_BTN_DET_PTR(sitar->calibration);
+	btn_det = SITAR_MBHC_CAL_BTN_DET_PTR(calibration);
 	n_cic = sitar_mbhc_cal_btn_det_mp(btn_det, SITAR_BTN_DET_N_CIC);
 	ncic = n_cic[sitar_codec_mclk_index(sitar)];
-	nmeas = SITAR_MBHC_CAL_BTN_DET_PTR(sitar->calibration)->n_meas;
-	navg = SITAR_MBHC_CAL_GENERAL_PTR(sitar->calibration)->mbhc_navg;
-	mclk_rate = sitar->mclk_freq;
-	dce_wait = (1000 * 512 * ncic * (nmeas + 1)) / (mclk_rate / 1000);
+	nmeas = SITAR_MBHC_CAL_BTN_DET_PTR(calibration)->n_meas;
+	navg = SITAR_MBHC_CAL_GENERAL_PTR(calibration)->mbhc_navg;
+	mclk_rate = sitar->mbhc_cfg.mclk_rate;
+	dce_wait = (1000 * 512 * 60 * (nmeas + 1)) / (mclk_rate / 1000);
 	sta_wait = (1000 * 128 * (navg + 1)) / (mclk_rate / 1000);
 
-	sitar->mbhc_data.t_dce = dce_wait;
-	sitar->mbhc_data.t_sta = sta_wait;
+	sitar->mbhc_data.t_dce = DEFAULT_DCE_WAIT;
+	sitar->mbhc_data.t_sta = DEFAULT_STA_WAIT;
 
 	/* LDOH and CFILT are already configured during pdata handling.
 	 * Only need to make sure CFILT and bandgap are in Fast mode.
@@ -2876,9 +2930,10 @@
 	 * to perform ADC calibration
 	 */
 	snd_soc_update_bits(codec, sitar->mbhc_bias_regs.ctl_reg, 0x60,
-			    sitar->micbias << 5);
+			    sitar->mbhc_cfg.micbias << 5);
 	snd_soc_update_bits(codec, sitar->mbhc_bias_regs.ctl_reg, 0x01, 0x00);
 	snd_soc_update_bits(codec, SITAR_A_LDO_H_MODE_1, 0x60, 0x60);
+	snd_soc_write(codec, SITAR_A_TX_4_MBHC_TEST_CTL, 0x78);
 	snd_soc_update_bits(codec, SITAR_A_CDC_MBHC_B1_CTL, 0x04, 0x04);
 
 	/* DCE measurement for 0 volts */
@@ -2925,6 +2980,9 @@
 
 	snd_soc_write(codec, SITAR_A_MBHC_SCALING_MUX_1, 0x84);
 	usleep_range(100, 100);
+
+	wcd9xxx_enable_irq(codec->control_data, SITAR_IRQ_MBHC_POTENTIAL);
+	sitar_turn_onoff_rel_detection(codec, true);
 }
 
 void *sitar_mbhc_cal_btn_det_mp(const struct sitar_mbhc_btn_detect_cfg* btn_det,
@@ -2962,21 +3020,22 @@
 	int i;
 
 	sitar = snd_soc_codec_get_drvdata(codec);
-	btn_det = SITAR_MBHC_CAL_BTN_DET_PTR(sitar->calibration);
-	plug_type = SITAR_MBHC_CAL_PLUG_TYPE_PTR(sitar->calibration);
+	btn_det = SITAR_MBHC_CAL_BTN_DET_PTR(sitar->mbhc_cfg.calibration);
+	plug_type = SITAR_MBHC_CAL_PLUG_TYPE_PTR(sitar->mbhc_cfg.calibration);
 
 	n_ready = sitar_mbhc_cal_btn_det_mp(btn_det, SITAR_BTN_DET_N_READY);
-	if (sitar->mclk_freq == SITAR_MCLK_RATE_12288KHZ) {
+	if (sitar->mbhc_cfg.mclk_rate == SITAR_MCLK_RATE_12288KHZ) {
 		sitar->mbhc_data.npoll = 9;
 		sitar->mbhc_data.nbounce_wait = 30;
-	} else if (sitar->mclk_freq == SITAR_MCLK_RATE_9600KHZ) {
+	} else if (sitar->mbhc_cfg.mclk_rate == SITAR_MCLK_RATE_9600KHZ) {
 		sitar->mbhc_data.npoll = 7;
 		sitar->mbhc_data.nbounce_wait = 23;
 	}
 
-	sitar->mbhc_data.t_sta_dce = ((1000 * 256) / (sitar->mclk_freq / 1000) *
-				      n_ready[sitar_codec_mclk_index(sitar)]) +
-				     10;
+	sitar->mbhc_data.t_sta_dce = ((1000 * 256) /
+				(sitar->mbhc_cfg.mclk_rate / 1000) *
+				n_ready[sitar_codec_mclk_index(sitar)]) +
+				10;
 	sitar->mbhc_data.v_ins_hu =
 	    sitar_codec_v_sta_dce(codec, STA, plug_type->v_hs_max);
 	sitar->mbhc_data.v_ins_h =
@@ -2998,7 +3057,7 @@
 	    sitar_codec_v_sta_dce(codec, DCE, btn_delta_mv);
 
 	sitar->mbhc_data.v_brh = sitar->mbhc_data.v_b1_h;
-	sitar->mbhc_data.v_brl = 0xFA55;
+	sitar->mbhc_data.v_brl = SITAR_MBHC_BUTTON_MIN;
 
 	sitar->mbhc_data.v_no_mic =
 	    sitar_codec_v_sta_dce(codec, STA, plug_type->v_no_mic);
@@ -3012,9 +3071,10 @@
 	int n;
 	u8 *n_cic, *gain;
 
+	pr_err("%s(): ENTER\n", __func__);
 	sitar = snd_soc_codec_get_drvdata(codec);
-	generic = SITAR_MBHC_CAL_GENERAL_PTR(sitar->calibration);
-	btn_det = SITAR_MBHC_CAL_BTN_DET_PTR(sitar->calibration);
+	generic = SITAR_MBHC_CAL_GENERAL_PTR(sitar->mbhc_cfg.calibration);
+	btn_det = SITAR_MBHC_CAL_BTN_DET_PTR(sitar->mbhc_cfg.calibration);
 
 	for (n = 0; n < 8; n++) {
 		if (n != 7) {
@@ -3049,7 +3109,13 @@
 	snd_soc_update_bits(codec, SITAR_A_CDC_MBHC_B1_CTL, 0x78,
 			    btn_det->mbhc_nsc << 3);
 
+	snd_soc_update_bits(codec, SITAR_A_MICB_1_MBHC, 0x03,
+						sitar->mbhc_cfg.micbias);
+
 	snd_soc_update_bits(codec, SITAR_A_CDC_MBHC_B1_CTL, 0x02, 0x02);
+
+	snd_soc_update_bits(codec, SITAR_A_MBHC_SCALING_MUX_2, 0xF0, 0xF0);
+
 }
 
 static bool sitar_mbhc_fw_validate(const struct firmware *fw)
@@ -3083,13 +3149,563 @@
 
 	return true;
 }
+
+
+static void sitar_turn_onoff_override(struct snd_soc_codec *codec, bool on)
+{
+	snd_soc_update_bits(codec, SITAR_A_CDC_MBHC_B1_CTL, 0x04, on << 2);
+}
+
+/* called under codec_resource_lock acquisition */
+void sitar_set_and_turnoff_hph_padac(struct snd_soc_codec *codec)
+{
+	struct sitar_priv *sitar = snd_soc_codec_get_drvdata(codec);
+	u8 wg_time;
+
+	wg_time = snd_soc_read(codec, SITAR_A_RX_HPH_CNP_WG_TIME) ;
+	wg_time += 1;
+
+	/* If headphone PA is on, check if userspace receives
+	 * removal event to sync-up PA's state */
+	if (sitar_is_hph_pa_on(codec)) {
+		pr_debug("%s PA is on, setting PA_OFF_ACK\n", __func__);
+		set_bit(SITAR_HPHL_PA_OFF_ACK, &sitar->hph_pa_dac_state);
+		set_bit(SITAR_HPHR_PA_OFF_ACK, &sitar->hph_pa_dac_state);
+	} else {
+		pr_debug("%s PA is off\n", __func__);
+	}
+
+	if (sitar_is_hph_dac_on(codec, 1))
+		set_bit(SITAR_HPHL_DAC_OFF_ACK, &sitar->hph_pa_dac_state);
+	if (sitar_is_hph_dac_on(codec, 0))
+		set_bit(SITAR_HPHR_DAC_OFF_ACK, &sitar->hph_pa_dac_state);
+
+	snd_soc_update_bits(codec, SITAR_A_RX_HPH_CNP_EN, 0x30, 0x00);
+	snd_soc_update_bits(codec, SITAR_A_RX_HPH_L_DAC_CTL,
+			    0xC0, 0x00);
+	snd_soc_update_bits(codec, SITAR_A_RX_HPH_R_DAC_CTL,
+			    0xC0, 0x00);
+	usleep_range(wg_time * 1000, wg_time * 1000);
+}
+
+static void sitar_clr_and_turnon_hph_padac(struct sitar_priv *sitar)
+{
+	bool pa_turned_on = false;
+	struct snd_soc_codec *codec = sitar->codec;
+	u8 wg_time;
+
+	wg_time = snd_soc_read(codec, SITAR_A_RX_HPH_CNP_WG_TIME) ;
+	wg_time += 1;
+
+	if (test_and_clear_bit(SITAR_HPHR_DAC_OFF_ACK,
+			       &sitar->hph_pa_dac_state)) {
+		pr_debug("%s: HPHR clear flag and enable DAC\n", __func__);
+		snd_soc_update_bits(sitar->codec, SITAR_A_RX_HPH_R_DAC_CTL,
+				    0xC0, 0xC0);
+	}
+	if (test_and_clear_bit(SITAR_HPHL_DAC_OFF_ACK,
+			       &sitar->hph_pa_dac_state)) {
+		pr_debug("%s: HPHL clear flag and enable DAC\n", __func__);
+		snd_soc_update_bits(sitar->codec, SITAR_A_RX_HPH_L_DAC_CTL,
+				    0xC0, 0xC0);
+	}
+
+	if (test_and_clear_bit(SITAR_HPHR_PA_OFF_ACK,
+			       &sitar->hph_pa_dac_state)) {
+		pr_debug("%s: HPHR clear flag and enable PA\n", __func__);
+		snd_soc_update_bits(sitar->codec, SITAR_A_RX_HPH_CNP_EN, 0x10,
+				    1 << 4);
+		pa_turned_on = true;
+	}
+	if (test_and_clear_bit(SITAR_HPHL_PA_OFF_ACK,
+			       &sitar->hph_pa_dac_state)) {
+		pr_debug("%s: HPHL clear flag and enable PA\n", __func__);
+		snd_soc_update_bits(sitar->codec, SITAR_A_RX_HPH_CNP_EN, 0x20,
+				    1 << 5);
+		pa_turned_on = true;
+	}
+
+	if (pa_turned_on) {
+		pr_debug("%s: PA was turned off by MBHC and not by DAPM\n",
+				__func__);
+		usleep_range(wg_time * 1000, wg_time * 1000);
+	}
+}
+
+static void sitar_codec_report_plug(struct snd_soc_codec *codec, int insertion,
+				    enum snd_jack_types jack_type)
+{
+	struct sitar_priv *sitar = snd_soc_codec_get_drvdata(codec);
+
+	if (!insertion) {
+		/* Report removal */
+		sitar->hph_status &= ~jack_type;
+		if (sitar->mbhc_cfg.headset_jack) {
+			/* cancel possibly scheduled btn work and
+			* report release if we reported button press */
+			if (sitar_cancel_btn_work(sitar)) {
+				pr_debug("%s: button press is canceled\n",
+					__func__);
+			} else if (sitar->buttons_pressed) {
+				pr_debug("%s: Reporting release for reported "
+					 "button press %d\n", __func__,
+					 jack_type);
+				sitar_snd_soc_jack_report(sitar,
+						 sitar->mbhc_cfg.button_jack, 0,
+						 sitar->buttons_pressed);
+				sitar->buttons_pressed &=
+							~SITAR_JACK_BUTTON_MASK;
+			}
+			pr_debug("%s: Reporting removal %d\n", __func__,
+				 jack_type);
+			sitar_snd_soc_jack_report(sitar,
+						  sitar->mbhc_cfg.headset_jack,
+						  sitar->hph_status,
+						  SITAR_JACK_MASK);
+		}
+		sitar_set_and_turnoff_hph_padac(codec);
+		hphocp_off_report(sitar, SND_JACK_OC_HPHR,
+				  SITAR_IRQ_HPH_PA_OCPR_FAULT);
+		hphocp_off_report(sitar, SND_JACK_OC_HPHL,
+				  SITAR_IRQ_HPH_PA_OCPL_FAULT);
+		sitar->current_plug = PLUG_TYPE_NONE;
+		sitar->mbhc_polling_active = false;
+	} else {
+		/* Report insertion */
+		sitar->hph_status |= jack_type;
+
+		if (jack_type == SND_JACK_HEADPHONE)
+			sitar->current_plug = PLUG_TYPE_HEADPHONE;
+		else if (jack_type == SND_JACK_HEADSET) {
+			sitar->mbhc_polling_active = true;
+			sitar->current_plug = PLUG_TYPE_HEADSET;
+		}
+		if (sitar->mbhc_cfg.headset_jack) {
+			pr_debug("%s: Reporting insertion %d\n", __func__,
+				 jack_type);
+			sitar_snd_soc_jack_report(sitar,
+						  sitar->mbhc_cfg.headset_jack,
+						  sitar->hph_status,
+						  SITAR_JACK_MASK);
+		}
+		sitar_clr_and_turnon_hph_padac(sitar);
+	}
+}
+
+
+static bool sitar_hs_gpio_level_remove(struct sitar_priv *sitar)
+{
+	return (gpio_get_value_cansleep(sitar->mbhc_cfg.gpio) !=
+		sitar->mbhc_cfg.gpio_level_insert);
+}
+
+static bool sitar_is_invalid_insert_delta(struct snd_soc_codec *codec,
+					int mic_volt, int mic_volt_prev)
+{
+	int delta = abs(mic_volt - mic_volt_prev);
+	if (delta > SITAR_MBHC_FAKE_INSERT_VOLT_DELTA_MV) {
+		pr_debug("%s: volt delta %dmv\n", __func__, delta);
+		return true;
+	}
+	return false;
+}
+
+static bool sitar_is_invalid_insertion_range(struct snd_soc_codec *codec,
+				       s32 mic_volt)
+{
+	bool invalid = false;
+
+	if (mic_volt < SITAR_MBHC_FAKE_INSERT_HIGH
+			&& (mic_volt > SITAR_MBHC_FAKE_INSERT_LOW)) {
+		invalid = true;
+	}
+
+	return invalid;
+}
+
+static bool sitar_codec_is_invalid_plug(struct snd_soc_codec *codec,
+	s32 mic_mv[MBHC_NUM_DCE_PLUG_DETECT],
+	enum sitar_mbhc_plug_type plug_type[MBHC_NUM_DCE_PLUG_DETECT])
+{
+	int i;
+	bool r = false;
+	struct sitar_priv *sitar = snd_soc_codec_get_drvdata(codec);
+	struct sitar_mbhc_plug_type_cfg *plug_type_ptr =
+		SITAR_MBHC_CAL_PLUG_TYPE_PTR(sitar->mbhc_cfg.calibration);
+
+	for (i = 0 ; i < MBHC_NUM_DCE_PLUG_DETECT && !r; i++) {
+		if (mic_mv[i] < plug_type_ptr->v_no_mic)
+			plug_type[i] = PLUG_TYPE_HEADPHONE;
+		else if (mic_mv[i] < plug_type_ptr->v_hs_max)
+			plug_type[i] = PLUG_TYPE_HEADSET;
+		else if (mic_mv[i] > plug_type_ptr->v_hs_max)
+			plug_type[i] = PLUG_TYPE_HIGH_HPH;
+
+		r = sitar_is_invalid_insertion_range(codec, mic_mv[i]);
+		if (!r && i > 0) {
+			if (plug_type[i-1] != plug_type[i])
+				r = true;
+			else
+				r = sitar_is_invalid_insert_delta(codec,
+							mic_mv[i],
+							mic_mv[i - 1]);
+		}
+	}
+
+	return r;
+}
+
+/* called under codec_resource_lock acquisition */
+void sitar_find_plug_and_report(struct snd_soc_codec *codec,
+				enum sitar_mbhc_plug_type plug_type)
+{
+	struct sitar_priv *sitar = snd_soc_codec_get_drvdata(codec);
+
+	if (plug_type == PLUG_TYPE_HEADPHONE
+		&& sitar->current_plug == PLUG_TYPE_NONE) {
+		/* Nothing was reported previously
+		 * reporte a headphone
+		 */
+		sitar_codec_report_plug(codec, 1, SND_JACK_HEADPHONE);
+		sitar_codec_cleanup_hs_polling(codec);
+	} else if (plug_type == PLUG_TYPE_HEADSET) {
+		/* If Headphone was reported previously, this will
+		 * only report the mic line
+		 */
+		sitar_codec_report_plug(codec, 1, SND_JACK_HEADSET);
+		msleep(100);
+		sitar_codec_start_hs_polling(codec);
+	} else if (plug_type == PLUG_TYPE_HIGH_HPH) {
+		if (sitar->current_plug == PLUG_TYPE_NONE)
+			sitar_codec_report_plug(codec, 1, SND_JACK_HEADPHONE);
+		sitar_codec_cleanup_hs_polling(codec);
+		pr_debug("setup mic trigger for further detection\n");
+		sitar->lpi_enabled = true;
+		/* TODO ::: sitar_codec_enable_hs_detect */
+		pr_err("%s(): High impedence hph not supported\n", __func__);
+	}
+}
+
+/* should be called under interrupt context that hold suspend */
+static void sitar_schedule_hs_detect_plug(struct sitar_priv *sitar)
+{
+	pr_debug("%s: scheduling sitar_hs_correct_gpio_plug\n", __func__);
+	sitar->hs_detect_work_stop = false;
+	wcd9xxx_lock_sleep(sitar->codec->control_data);
+	schedule_work(&sitar->hs_correct_plug_work);
+}
+
+/* called under codec_resource_lock acquisition */
+static void sitar_cancel_hs_detect_plug(struct sitar_priv *sitar)
+{
+	pr_debug("%s: canceling hs_correct_plug_work\n", __func__);
+	sitar->hs_detect_work_stop = true;
+	wmb();
+	SITAR_RELEASE_LOCK(sitar->codec_resource_lock);
+	if (cancel_work_sync(&sitar->hs_correct_plug_work)) {
+		pr_debug("%s: hs_correct_plug_work is canceled\n", __func__);
+		wcd9xxx_unlock_sleep(sitar->codec->control_data);
+	}
+	SITAR_ACQUIRE_LOCK(sitar->codec_resource_lock);
+}
+
+static void sitar_hs_correct_gpio_plug(struct work_struct *work)
+{
+	struct sitar_priv *sitar;
+	struct snd_soc_codec *codec;
+	int retry = 0, i;
+	bool correction = false;
+	s32 mic_mv[MBHC_NUM_DCE_PLUG_DETECT];
+	short mb_v[MBHC_NUM_DCE_PLUG_DETECT];
+	enum sitar_mbhc_plug_type plug_type[MBHC_NUM_DCE_PLUG_DETECT];
+	unsigned long timeout;
+
+	sitar = container_of(work, struct sitar_priv, hs_correct_plug_work);
+	codec = sitar->codec;
+
+	pr_debug("%s: enter\n", __func__);
+	sitar->mbhc_cfg.mclk_cb_fn(codec, 1, false);
+
+	/* Keep override on during entire plug type correction work.
+	 *
+	 * This is okay under the assumption that any GPIO irqs which use
+	 * MBHC block cancel and sync this work so override is off again
+	 * prior to GPIO interrupt handler's MBHC block usage.
+	 * Also while this correction work is running, we can guarantee
+	 * DAPM doesn't use any MBHC block as this work only runs with
+	 * headphone detection.
+	 */
+	sitar_turn_onoff_override(codec, true);
+
+	timeout = jiffies + msecs_to_jiffies(SITAR_HS_DETECT_PLUG_TIME_MS);
+	while (!time_after(jiffies, timeout)) {
+		++retry;
+		rmb();
+		if (sitar->hs_detect_work_stop) {
+			pr_debug("%s: stop requested\n", __func__);
+			break;
+		}
+
+		msleep(SITAR_HS_DETECT_PLUG_INERVAL_MS);
+		if (sitar_hs_gpio_level_remove(sitar)) {
+			pr_debug("%s: GPIO value is low\n", __func__);
+			break;
+		}
+
+		/* can race with removal interrupt */
+		SITAR_ACQUIRE_LOCK(sitar->codec_resource_lock);
+		for (i = 0; i < MBHC_NUM_DCE_PLUG_DETECT; i++) {
+			mb_v[i] = __sitar_codec_sta_dce(codec, 1, true, true);
+			mic_mv[i] = sitar_codec_sta_dce_v(codec, 1 , mb_v[i]);
+			pr_debug("%s : DCE run %d, mic_mv = %d(%x)\n",
+				 __func__, retry, mic_mv[i], mb_v[i]);
+		}
+		SITAR_RELEASE_LOCK(sitar->codec_resource_lock);
+
+		if (sitar_codec_is_invalid_plug(codec, mic_mv, plug_type)) {
+			pr_debug("Invalid plug in attempt # %d\n", retry);
+			if (retry == NUM_ATTEMPTS_TO_REPORT &&
+			    sitar->current_plug == PLUG_TYPE_NONE) {
+				sitar_codec_report_plug(codec, 1,
+							SND_JACK_HEADPHONE);
+			}
+		} else if (!sitar_codec_is_invalid_plug(codec, mic_mv,
+							plug_type) &&
+			   plug_type[0] == PLUG_TYPE_HEADPHONE) {
+			pr_debug("Good headphone detected, continue polling mic\n");
+			if (sitar->current_plug == PLUG_TYPE_NONE) {
+				sitar_codec_report_plug(codec, 1,
+							SND_JACK_HEADPHONE);
+			}
+		} else {
+			SITAR_ACQUIRE_LOCK(sitar->codec_resource_lock);
+			/* Turn off override */
+			sitar_turn_onoff_override(codec, false);
+			sitar_find_plug_and_report(codec, plug_type[0]);
+			SITAR_RELEASE_LOCK(sitar->codec_resource_lock);
+			pr_debug("Attempt %d found correct plug %d\n", retry,
+				 plug_type[0]);
+			correction = true;
+			break;
+		}
+	}
+
+	/* Turn off override */
+	if (!correction)
+		sitar_turn_onoff_override(codec, false);
+
+	sitar->mbhc_cfg.mclk_cb_fn(codec, 0, false);
+	pr_debug("%s: leave\n", __func__);
+	/* unlock sleep */
+	wcd9xxx_unlock_sleep(sitar->codec->control_data);
+}
+
+/* called under codec_resource_lock acquisition */
+static void sitar_codec_decide_gpio_plug(struct snd_soc_codec *codec)
+{
+	struct sitar_priv *sitar = snd_soc_codec_get_drvdata(codec);
+	short mb_v[MBHC_NUM_DCE_PLUG_DETECT];
+	s32 mic_mv[MBHC_NUM_DCE_PLUG_DETECT];
+	enum sitar_mbhc_plug_type plug_type[MBHC_NUM_DCE_PLUG_DETECT];
+	int i;
+
+	pr_debug("%s: enter\n", __func__);
+
+	sitar_turn_onoff_override(codec, true);
+	mb_v[0] = sitar_codec_setup_hs_polling(codec);
+	mic_mv[0] = sitar_codec_sta_dce_v(codec, 1, mb_v[0]);
+	pr_debug("%s: DCE run 1, mic_mv = %d\n", __func__, mic_mv[0]);
+
+	for (i = 1; i < MBHC_NUM_DCE_PLUG_DETECT; i++) {
+		mb_v[i] = __sitar_codec_sta_dce(codec, 1, true, true);
+		mic_mv[i] = sitar_codec_sta_dce_v(codec, 1 , mb_v[i]);
+		pr_debug("%s: DCE run %d, mic_mv = %d\n", __func__, i + 1,
+			 mic_mv[i]);
+	}
+	sitar_turn_onoff_override(codec, false);
+
+	if (sitar_hs_gpio_level_remove(sitar)) {
+		pr_debug("%s: GPIO value is low when determining plug\n",
+			 __func__);
+		return;
+	}
+
+	if (sitar_codec_is_invalid_plug(codec, mic_mv, plug_type)) {
+		sitar_schedule_hs_detect_plug(sitar);
+	} else if (plug_type[0] == PLUG_TYPE_HEADPHONE) {
+		sitar_codec_report_plug(codec, 1, SND_JACK_HEADPHONE);
+		sitar_schedule_hs_detect_plug(sitar);
+	} else if (plug_type[0] == PLUG_TYPE_HEADSET) {
+		pr_debug("%s: Valid plug found, determine plug type\n",
+			 __func__);
+		sitar_find_plug_and_report(codec, plug_type[0]);
+	}
+
+}
+
+/* called under codec_resource_lock acquisition */
+static void sitar_codec_detect_plug_type(struct snd_soc_codec *codec)
+{
+	struct sitar_priv *sitar = snd_soc_codec_get_drvdata(codec);
+	const struct sitar_mbhc_plug_detect_cfg *plug_det =
+	    SITAR_MBHC_CAL_PLUG_DET_PTR(sitar->mbhc_cfg.calibration);
+
+	if (plug_det->t_ins_complete > 20)
+		msleep(plug_det->t_ins_complete);
+	else
+		usleep_range(plug_det->t_ins_complete * 1000,
+			     plug_det->t_ins_complete * 1000);
+
+	if (sitar_hs_gpio_level_remove(sitar))
+		pr_debug("%s: GPIO value is low when determining "
+				 "plug\n", __func__);
+	else
+		sitar_codec_decide_gpio_plug(codec);
+
+	return;
+}
+
+static void sitar_hs_gpio_handler(struct snd_soc_codec *codec)
+{
+	bool insert;
+	struct sitar_priv *priv = snd_soc_codec_get_drvdata(codec);
+	bool is_removed = false;
+
+	pr_debug("%s: enter\n", __func__);
+
+	priv->in_gpio_handler = true;
+	/* Wait here for debounce time */
+	usleep_range(SITAR_GPIO_IRQ_DEBOUNCE_TIME_US,
+		     SITAR_GPIO_IRQ_DEBOUNCE_TIME_US);
+
+	SITAR_ACQUIRE_LOCK(priv->codec_resource_lock);
+
+	/* cancel pending button press */
+	if (sitar_cancel_btn_work(priv))
+		pr_debug("%s: button press is canceled\n", __func__);
+
+	insert = (gpio_get_value_cansleep(priv->mbhc_cfg.gpio) ==
+		  priv->mbhc_cfg.gpio_level_insert);
+	if ((priv->current_plug == PLUG_TYPE_NONE) && insert) {
+		priv->lpi_enabled = false;
+		wmb();
+
+		/* cancel detect plug */
+		sitar_cancel_hs_detect_plug(priv);
+
+		/* Disable Mic Bias pull down and HPH Switch to GND */
+		snd_soc_update_bits(codec, priv->mbhc_bias_regs.ctl_reg, 0x01,
+				    0x00);
+		snd_soc_update_bits(codec, SITAR_A_MBHC_HPH, 0x01, 0x00);
+		sitar_codec_detect_plug_type(codec);
+	} else if ((priv->current_plug != PLUG_TYPE_NONE) && !insert) {
+		priv->lpi_enabled = false;
+		wmb();
+
+		/* cancel detect plug */
+		sitar_cancel_hs_detect_plug(priv);
+
+		if (priv->current_plug == PLUG_TYPE_HEADPHONE) {
+			sitar_codec_report_plug(codec, 0, SND_JACK_HEADPHONE);
+			is_removed = true;
+		} else if (priv->current_plug == PLUG_TYPE_HEADSET) {
+			sitar_codec_pause_hs_polling(codec);
+			sitar_codec_cleanup_hs_polling(codec);
+			sitar_codec_report_plug(codec, 0, SND_JACK_HEADSET);
+			is_removed = true;
+		}
+
+		if (is_removed) {
+			/* Enable Mic Bias pull down and HPH Switch to GND */
+			snd_soc_update_bits(codec,
+					    priv->mbhc_bias_regs.ctl_reg, 0x01,
+					    0x01);
+			snd_soc_update_bits(codec, SITAR_A_MBHC_HPH, 0x01,
+					    0x01);
+			/* Make sure mic trigger is turned off */
+			snd_soc_update_bits(codec,
+					    priv->mbhc_bias_regs.ctl_reg,
+					    0x01, 0x01);
+			snd_soc_update_bits(codec,
+					    priv->mbhc_bias_regs.mbhc_reg,
+					    0x90, 0x00);
+			/* Reset MBHC State Machine */
+			snd_soc_update_bits(codec, SITAR_A_CDC_MBHC_CLK_CTL,
+					    0x08, 0x08);
+			snd_soc_update_bits(codec, SITAR_A_CDC_MBHC_CLK_CTL,
+					    0x08, 0x00);
+			/* Turn off override */
+			sitar_turn_onoff_override(codec, false);
+		}
+	}
+
+	priv->in_gpio_handler = false;
+	SITAR_RELEASE_LOCK(priv->codec_resource_lock);
+	pr_debug("%s: leave\n", __func__);
+}
+
+static irqreturn_t sitar_mechanical_plug_detect_irq(int irq, void *data)
+{
+	int r = IRQ_HANDLED;
+	struct snd_soc_codec *codec = data;
+
+	if (unlikely(wcd9xxx_lock_sleep(codec->control_data) == false)) {
+		pr_warn("%s(): Failed to hold suspend\n", __func__);
+		r = IRQ_NONE;
+	} else {
+		sitar_hs_gpio_handler(codec);
+		wcd9xxx_unlock_sleep(codec->control_data);
+	}
+	return r;
+}
+
+static int sitar_mbhc_init_and_calibrate(struct snd_soc_codec *codec)
+{
+	int rc = 0;
+	struct sitar_priv *sitar = snd_soc_codec_get_drvdata(codec);
+
+	sitar->mbhc_cfg.mclk_cb_fn(codec, 1, false);
+	sitar_mbhc_init(codec);
+	sitar_mbhc_cal(codec);
+	sitar_mbhc_calc_thres(codec);
+	sitar->mbhc_cfg.mclk_cb_fn(codec, 0, false);
+	sitar_codec_calibrate_hs_polling(codec);
+
+	/* Enable Mic Bias pull down and HPH Switch to GND */
+	snd_soc_update_bits(codec, sitar->mbhc_bias_regs.ctl_reg,
+						0x01, 0x01);
+	snd_soc_update_bits(codec, SITAR_A_MBHC_HPH,
+						0x01, 0x01);
+
+	rc = request_threaded_irq(sitar->mbhc_cfg.gpio_irq,
+				NULL,
+				sitar_mechanical_plug_detect_irq,
+				(IRQF_TRIGGER_RISING |
+				IRQF_TRIGGER_FALLING),
+				"sitar-hs-gpio", codec);
+
+	if (!IS_ERR_VALUE(rc)) {
+		rc = enable_irq_wake(sitar->mbhc_cfg.gpio_irq);
+		snd_soc_update_bits(codec, SITAR_A_RX_HPH_OCP_CTL,
+							0x10, 0x10);
+		wcd9xxx_enable_irq(codec->control_data,
+					SITAR_IRQ_HPH_PA_OCPL_FAULT);
+		wcd9xxx_enable_irq(codec->control_data,
+					SITAR_IRQ_HPH_PA_OCPR_FAULT);
+		/* Bootup time detection */
+		sitar_hs_gpio_handler(codec);
+	}
+
+	return rc;
+}
+
 static void mbhc_fw_read(struct work_struct *work)
 {
 	struct delayed_work *dwork;
 	struct sitar_priv *sitar;
 	struct snd_soc_codec *codec;
 	const struct firmware *fw;
-	int ret = -1, retry = 0, rc;
+	int ret = -1, retry = 0;
 
 	dwork = to_delayed_work(work);
 	sitar = container_of(dwork, struct sitar_priv,
@@ -3124,80 +3740,53 @@
 		sitar->mbhc_fw = fw;
 	}
 
-	sitar->mclk_cb(codec, 1);
-	sitar_mbhc_init(codec);
-	sitar_mbhc_cal(codec);
-	sitar_mbhc_calc_thres(codec);
-	sitar->mclk_cb(codec, 0);
-	sitar_codec_calibrate_hs_polling(codec);
-	rc = sitar_codec_enable_hs_detect(codec, 1);
-
-	if (IS_ERR_VALUE(rc))
-		pr_err("%s: Failed to setup MBHC detection\n", __func__);
-
+	sitar_mbhc_init_and_calibrate(codec);
 }
 
 int sitar_hs_detect(struct snd_soc_codec *codec,
-		    struct snd_soc_jack *headset_jack,
-		    struct snd_soc_jack *button_jack,
-		    void *calibration, enum sitar_micbias_num micbias,
-		    int (*mclk_cb_fn) (struct snd_soc_codec*, int),
-		    int read_fw_bin, u32 mclk_rate)
+			const struct sitar_mbhc_config *cfg)
 {
 	struct sitar_priv *sitar;
 	int rc = 0;
 
-	if (!codec || !calibration) {
+	if (!codec || !cfg->calibration) {
 		pr_err("Error: no codec or calibration\n");
 		return -EINVAL;
 	}
 
-	if (mclk_rate != SITAR_MCLK_RATE_12288KHZ) {
-		if (mclk_rate == SITAR_MCLK_RATE_9600KHZ)
+	if (cfg->mclk_rate != SITAR_MCLK_RATE_12288KHZ) {
+		if (cfg->mclk_rate == SITAR_MCLK_RATE_9600KHZ)
 			pr_err("Error: clock rate %dHz is not yet supported\n",
-				mclk_rate);
+				cfg->mclk_rate);
 		else
-			pr_err("Error: unsupported clock rate %d\n", mclk_rate);
+			pr_err("Error: unsupported clock rate %d\n",
+				   cfg->mclk_rate);
 		return -EINVAL;
 	}
 
 	sitar = snd_soc_codec_get_drvdata(codec);
-	sitar->headset_jack = headset_jack;
-	sitar->button_jack = button_jack;
-	sitar->micbias = micbias;
-	sitar->calibration = calibration;
-	sitar->mclk_cb = mclk_cb_fn;
-	sitar->mclk_freq = mclk_rate;
+	sitar->mbhc_cfg = *cfg;
+	sitar->in_gpio_handler = false;
+	sitar->current_plug = PLUG_TYPE_NONE;
+	sitar->lpi_enabled = false;
 	sitar_get_mbhc_micbias_regs(codec, &sitar->mbhc_bias_regs);
 
 	/* Put CFILT in fast mode by default */
 	snd_soc_update_bits(codec, sitar->mbhc_bias_regs.cfilt_ctl,
-		0x40, SITAR_CFILT_FAST_MODE);
+			    0x40, SITAR_CFILT_FAST_MODE);
+
 	INIT_DELAYED_WORK(&sitar->mbhc_firmware_dwork, mbhc_fw_read);
-	INIT_DELAYED_WORK(&sitar->btn0_dwork, btn0_lpress_fn);
+	INIT_DELAYED_WORK(&sitar->mbhc_btn_dwork, btn_lpress_fn);
 	INIT_WORK(&sitar->hphlocp_work, hphlocp_off_report);
 	INIT_WORK(&sitar->hphrocp_work, hphrocp_off_report);
+	INIT_WORK(&sitar->hs_correct_plug_work,
+			  sitar_hs_correct_gpio_plug);
 
-	if (!read_fw_bin) {
-		sitar->mclk_cb(codec, 1);
-		sitar_mbhc_init(codec);
-		sitar_mbhc_cal(codec);
-		sitar_mbhc_calc_thres(codec);
-		sitar->mclk_cb(codec, 0);
-		sitar_codec_calibrate_hs_polling(codec);
-		rc =  sitar_codec_enable_hs_detect(codec, 1);
+	if (!sitar->mbhc_cfg.read_fw_bin) {
+		rc = sitar_mbhc_init_and_calibrate(codec);
 	} else {
 		schedule_delayed_work(&sitar->mbhc_firmware_dwork,
-				usecs_to_jiffies(MBHC_FW_READ_TIMEOUT));
-	}
-
-	if (!IS_ERR_VALUE(rc)) {
-		snd_soc_update_bits(codec, SITAR_A_RX_HPH_OCP_CTL, 0x10,
-			0x10);
-		wcd9xxx_enable_irq(codec->control_data,
-			SITAR_IRQ_HPH_PA_OCPL_FAULT);
-		wcd9xxx_enable_irq(codec->control_data,
-			SITAR_IRQ_HPH_PA_OCPR_FAULT);
+					usecs_to_jiffies(MBHC_FW_READ_TIMEOUT));
 	}
 
 	return rc;
@@ -3211,7 +3800,7 @@
 	struct sitar_mbhc_btn_detect_cfg *btn_det;
 	int i, btn = -1;
 
-	btn_det = SITAR_MBHC_CAL_BTN_DET_PTR(priv->calibration);
+	btn_det = SITAR_MBHC_CAL_BTN_DET_PTR(priv->mbhc_cfg.calibration);
 	v_btn_low = sitar_mbhc_cal_btn_det_mp(btn_det, SITAR_BTN_DET_V_BTN_LOW);
 	v_btn_high = sitar_mbhc_cal_btn_det_mp(btn_det,
 				SITAR_BTN_DET_V_BTN_HIGH);
@@ -3261,33 +3850,76 @@
 	return mask;
 }
 
+
 static irqreturn_t sitar_dce_handler(int irq, void *data)
 {
 	int i, mask;
-	short bias_value_dce;
-	s32 bias_mv_dce;
+	short dce, sta, bias_value_dce;
+	s32 mv, stamv, bias_mv_dce;
 	int btn = -1, meas = 0;
 	struct sitar_priv *priv = data;
 	const struct sitar_mbhc_btn_detect_cfg *d =
-	    SITAR_MBHC_CAL_BTN_DET_PTR(priv->calibration);
+	    SITAR_MBHC_CAL_BTN_DET_PTR(priv->mbhc_cfg.calibration);
 	short btnmeas[d->n_btn_meas + 1];
 	struct snd_soc_codec *codec = priv->codec;
 	struct wcd9xxx *core = dev_get_drvdata(priv->codec->dev->parent);
+	int n_btn_meas = d->n_btn_meas;
+	u8 mbhc_status = snd_soc_read(codec, SITAR_A_CDC_MBHC_B1_STATUS) & 0x3E;
 
-	wcd9xxx_disable_irq(codec->control_data, SITAR_IRQ_MBHC_REMOVAL);
-	wcd9xxx_disable_irq(codec->control_data, SITAR_IRQ_MBHC_POTENTIAL);
+	pr_debug("%s: enter\n", __func__);
 
-	bias_value_dce = sitar_codec_read_dce_result(codec);
-	bias_mv_dce = sitar_codec_sta_dce_v(codec, 1, bias_value_dce);
+	SITAR_ACQUIRE_LOCK(priv->codec_resource_lock);
+	if (priv->mbhc_state == MBHC_STATE_POTENTIAL_RECOVERY) {
+		pr_debug("%s: mbhc is being recovered, skip button press\n",
+			 __func__);
+		goto done;
+	}
+
+	priv->mbhc_state = MBHC_STATE_POTENTIAL;
+
+	if (!priv->mbhc_polling_active) {
+		pr_warn("%s: mbhc polling is not active, skip button press\n",
+			__func__);
+		goto done;
+	}
+
+	dce = sitar_codec_read_dce_result(codec);
+	mv = sitar_codec_sta_dce_v(codec, 1, dce);
+
+	/* If GPIO interrupt already kicked in, ignore button press */
+	if (priv->in_gpio_handler) {
+		pr_debug("%s: GPIO State Changed, ignore button press\n",
+			 __func__);
+		btn = -1;
+		goto done;
+	}
+
+	if (mbhc_status != SITAR_MBHC_STATUS_REL_DETECTION) {
+		if (priv->mbhc_last_resume &&
+		    !time_after(jiffies, priv->mbhc_last_resume + HZ)) {
+			pr_debug("%s: Button is already released shortly after "
+				 "resume\n", __func__);
+			n_btn_meas = 0;
+		} else {
+			pr_debug("%s: Button is already released without "
+				 "resume", __func__);
+			sta = sitar_codec_read_sta_result(codec);
+			stamv = sitar_codec_sta_dce_v(codec, 0, sta);
+			btn = sitar_determine_button(priv, mv);
+			if (btn != sitar_determine_button(priv, stamv))
+				btn = -1;
+			goto done;
+		}
+	}
 
 	/* determine pressed button */
-	btnmeas[meas++] = sitar_determine_button(priv, bias_mv_dce);
+	btnmeas[meas++] = sitar_determine_button(priv, mv);
 	pr_debug("%s: meas %d - DCE %d,%d, button %d\n", __func__,
-		 meas - 1, bias_value_dce, bias_mv_dce, btnmeas[meas - 1]);
-	if (d->n_btn_meas == 0)
+		 meas - 1, dce, mv, btnmeas[meas - 1]);
+	if (n_btn_meas == 0)
 		btn = btnmeas[0];
 	for (; ((d->n_btn_meas) && (meas < (d->n_btn_meas + 1))); meas++) {
-		bias_value_dce = sitar_codec_sta_dce(codec, 1);
+		bias_value_dce = sitar_codec_sta_dce(codec, 1, false);
 		bias_mv_dce = sitar_codec_sta_dce_v(codec, 1, bias_value_dce);
 		btnmeas[meas] = sitar_determine_button(priv, bias_mv_dce);
 		pr_debug("%s: meas %d - DCE %d,%d, button %d\n",
@@ -3305,147 +3937,133 @@
 				/* button pressed */
 				btn = btnmeas[meas];
 				break;
+			} else if ((n_btn_meas - meas) < (d->n_btn_con - 1)) {
+				/* if left measurements are less than n_btn_con,
+				 * it's impossible to find button number */
+				break;
 			}
 		}
-		/* if left measurements are less than n_btn_con,
-		 * it's impossible to find button number */
-		if ((d->n_btn_meas - meas) < d->n_btn_con)
-			break;
 	}
 
 	if (btn >= 0) {
+		if (priv->in_gpio_handler) {
+			pr_debug("%s: GPIO already triggered, ignore button "
+				 "press\n", __func__);
+			goto done;
+		}
 		mask = sitar_get_button_mask(btn);
 		priv->buttons_pressed |= mask;
-
-		msleep(100);
-
-		/* XXX: assuming button 0 has the lowest micbias voltage */
-		if (btn == 0) {
-			wcd9xxx_lock_sleep(core);
-			if (schedule_delayed_work(&priv->btn0_dwork,
-						  msecs_to_jiffies(400)) == 0) {
-				WARN(1, "Button pressed twice without release"
-				     "event\n");
-				wcd9xxx_unlock_sleep(core);
-			}
-		} else {
-			pr_debug("%s: Reporting short button %d(0x%x) press\n",
-				  __func__, btn, mask);
-			sitar_snd_soc_jack_report(priv, priv->button_jack, mask,
-						  mask);
+		wcd9xxx_lock_sleep(core);
+		if (schedule_delayed_work(&priv->mbhc_btn_dwork,
+					  msecs_to_jiffies(400)) == 0) {
+			WARN(1, "Button pressed twice without release"
+			     "event\n");
+			wcd9xxx_unlock_sleep(core);
 		}
 	} else {
 		pr_debug("%s: bogus button press, too short press?\n",
 			 __func__);
 	}
 
+ done:
+	pr_debug("%s: leave\n", __func__);
+	SITAR_RELEASE_LOCK(priv->codec_resource_lock);
 	return IRQ_HANDLED;
 }
 
+static int sitar_is_fake_press(struct sitar_priv *priv)
+{
+	int i;
+	int r = 0;
+	struct snd_soc_codec *codec = priv->codec;
+	const int dces = MBHC_NUM_DCE_PLUG_DETECT;
+	short mb_v;
+
+	for (i = 0; i < dces; i++) {
+		usleep_range(10000, 10000);
+		if (i == 0) {
+			mb_v = sitar_codec_sta_dce(codec, 0, true);
+			pr_debug("%s: STA[0]: %d,%d\n", __func__, mb_v,
+			sitar_codec_sta_dce_v(codec, 0, mb_v));
+			if (mb_v < (short)priv->mbhc_data.v_b1_hu ||
+				mb_v > (short)priv->mbhc_data.v_ins_hu) {
+				r = 1;
+				break;
+			}
+		} else {
+			mb_v = sitar_codec_sta_dce(codec, 1, true);
+			pr_debug("%s: DCE[%d]: %d,%d\n", __func__, i, mb_v,
+			sitar_codec_sta_dce_v(codec, 1, mb_v));
+			if (mb_v < (short)priv->mbhc_data.v_b1_h ||
+				mb_v > (short)priv->mbhc_data.v_ins_h) {
+				r = 1;
+				break;
+			}
+		}
+	}
+
+	return r;
+}
+
 static irqreturn_t sitar_release_handler(int irq, void *data)
 {
 	int ret;
-	short mb_v;
 	struct sitar_priv *priv = data;
 	struct snd_soc_codec *codec = priv->codec;
-	struct wcd9xxx *core = dev_get_drvdata(priv->codec->dev->parent);
 
 	pr_debug("%s: enter\n", __func__);
-	wcd9xxx_disable_irq(codec->control_data, SITAR_IRQ_MBHC_RELEASE);
 
-	if (priv->buttons_pressed & SND_JACK_BTN_0) {
-		ret = cancel_delayed_work(&priv->btn0_dwork);
+	SITAR_ACQUIRE_LOCK(priv->codec_resource_lock);
+	priv->mbhc_state = MBHC_STATE_RELEASE;
 
+	if (priv->buttons_pressed & SITAR_JACK_BUTTON_MASK) {
+		ret = sitar_cancel_btn_work(priv);
 		if (ret == 0) {
-			pr_debug("%s: Reporting long button 0 release event\n",
+			pr_debug("%s: Reporting long button release event\n",
 				 __func__);
-			if (priv->button_jack)
+			if (priv->mbhc_cfg.button_jack)
 				sitar_snd_soc_jack_report(priv,
-							  priv->button_jack, 0,
-							  SND_JACK_BTN_0);
+						  priv->mbhc_cfg.button_jack, 0,
+						  priv->buttons_pressed);
 		} else {
-			/* if scheduled btn0_dwork is canceled from here,
-			 * we have to unlock from here instead btn0_work */
-			wcd9xxx_unlock_sleep(core);
-			mb_v = sitar_codec_sta_dce(codec, 0);
-			pr_debug("%s: Mic Voltage on release STA: %d,%d\n",
-				 __func__, mb_v,
-				 sitar_codec_sta_dce_v(codec, 0, mb_v));
-
-			if (mb_v < (short)priv->mbhc_data.v_b1_hu ||
-			    mb_v > (short)priv->mbhc_data.v_ins_hu)
-				pr_debug("%s: Fake buttton press interrupt\n",
+			if (sitar_is_fake_press(priv)) {
+				pr_debug("%s: Fake button press interrupt\n",
 					 __func__);
-			else if (priv->button_jack) {
-				pr_debug("%s: Reporting short button 0 "
-					 "press and release\n", __func__);
-				sitar_snd_soc_jack_report(priv,
-							  priv->button_jack,
-							  SND_JACK_BTN_0,
-							  SND_JACK_BTN_0);
-				sitar_snd_soc_jack_report(priv,
-							  priv->button_jack, 0,
-							  SND_JACK_BTN_0);
+			} else if (priv->mbhc_cfg.button_jack) {
+				if (priv->in_gpio_handler) {
+					pr_debug("%s: GPIO kicked in, ignore\n",
+						 __func__);
+				} else {
+					pr_debug("%s: Reporting short button 0 "
+						 "press and release\n",
+						 __func__);
+					sitar_snd_soc_jack_report(priv,
+						priv->mbhc_cfg.button_jack,
+						priv->buttons_pressed,
+						priv->buttons_pressed);
+					sitar_snd_soc_jack_report(priv,
+						priv->mbhc_cfg.button_jack, 0,
+						priv->buttons_pressed);
+				}
 			}
 		}
 
-		priv->buttons_pressed &= ~SND_JACK_BTN_0;
-	}
-
-	if (priv->buttons_pressed) {
-		pr_debug("%s:reporting button release mask 0x%x\n", __func__,
-			 priv->buttons_pressed);
-		sitar_snd_soc_jack_report(priv, priv->button_jack, 0,
-					  priv->buttons_pressed);
-		/* hardware doesn't detect another button press until
-		 * already pressed button is released.
-		 * therefore buttons_pressed has only one button's mask. */
 		priv->buttons_pressed &= ~SITAR_JACK_BUTTON_MASK;
 	}
 
+	sitar_codec_calibrate_hs_polling(codec);
+
+	if (priv->mbhc_cfg.gpio)
+		msleep(SITAR_MBHC_GPIO_REL_DEBOUNCE_TIME_MS);
+
 	sitar_codec_start_hs_polling(codec);
+
+	pr_debug("%s: leave\n", __func__);
+	SITAR_RELEASE_LOCK(priv->codec_resource_lock);
+
 	return IRQ_HANDLED;
 }
 
-static void sitar_codec_shutdown_hs_removal_detect(struct snd_soc_codec *codec)
-{
-	struct sitar_priv *sitar = snd_soc_codec_get_drvdata(codec);
-	const struct sitar_mbhc_general_cfg *generic =
-	    SITAR_MBHC_CAL_GENERAL_PTR(sitar->calibration);
-
-	if (!sitar->mclk_enabled && !sitar->mbhc_polling_active)
-		sitar_codec_enable_config_mode(codec, 1);
-
-	snd_soc_update_bits(codec, SITAR_A_CDC_MBHC_CLK_CTL, 0x2, 0x2);
-	snd_soc_update_bits(codec, SITAR_A_CDC_MBHC_B1_CTL, 0x6, 0x0);
-
-	snd_soc_update_bits(codec, sitar->mbhc_bias_regs.mbhc_reg, 0x80, 0x00);
-
-	usleep_range(generic->t_shutdown_plug_rem,
-		     generic->t_shutdown_plug_rem);
-
-	snd_soc_update_bits(codec, SITAR_A_CDC_MBHC_CLK_CTL, 0xA, 0x8);
-	if (!sitar->mclk_enabled && !sitar->mbhc_polling_active)
-		sitar_codec_enable_config_mode(codec, 0);
-
-	snd_soc_write(codec, SITAR_A_MBHC_SCALING_MUX_1, 0x00);
-}
-
-static void sitar_codec_shutdown_hs_polling(struct snd_soc_codec *codec)
-{
-	struct sitar_priv *sitar = snd_soc_codec_get_drvdata(codec);
-
-	sitar_codec_shutdown_hs_removal_detect(codec);
-
-	if (!sitar->mclk_enabled) {
-		snd_soc_update_bits(codec, SITAR_A_TX_COM_BIAS, 0xE0, 0x00);
-		sitar_codec_disable_clock_block(codec);
-		sitar_codec_enable_bandgap(codec, SITAR_BANDGAP_OFF);
-	}
-
-	sitar->mbhc_polling_active = false;
-}
-
 static irqreturn_t sitar_hphl_ocp_irq(int irq, void *data)
 {
 	struct sitar_priv *sitar = data;
@@ -3466,11 +4084,11 @@
 					  SITAR_IRQ_HPH_PA_OCPL_FAULT);
 			sitar->hphlocp_cnt = 0;
 			sitar->hph_status |= SND_JACK_OC_HPHL;
-			if (sitar->headset_jack)
+			if (sitar->mbhc_cfg.headset_jack)
 				sitar_snd_soc_jack_report(sitar,
-							  sitar->headset_jack,
-							  sitar->hph_status,
-							  SITAR_JACK_MASK);
+						sitar->mbhc_cfg.headset_jack,
+						sitar->hph_status,
+						SITAR_JACK_MASK);
 		}
 	} else {
 		pr_err("%s: Bad sitar private data\n", __func__);
@@ -3499,11 +4117,11 @@
 					 SITAR_IRQ_HPH_PA_OCPR_FAULT);
 			sitar->hphrocp_cnt = 0;
 			sitar->hph_status |= SND_JACK_OC_HPHR;
-			if (sitar->headset_jack)
+			if (sitar->mbhc_cfg.headset_jack)
 				sitar_snd_soc_jack_report(sitar,
-							 sitar->headset_jack,
-							 sitar->hph_status,
-							 SITAR_JACK_MASK);
+						sitar->mbhc_cfg.headset_jack,
+						sitar->hph_status,
+						SITAR_JACK_MASK);
 		}
 	} else {
 		pr_err("%s: Bad sitar private data\n", __func__);
@@ -3512,252 +4130,131 @@
 	return IRQ_HANDLED;
 }
 
-static void sitar_sync_hph_state(struct sitar_priv *sitar)
-{
-	if (test_and_clear_bit(SITAR_HPHR_PA_OFF_ACK,
-				&sitar->hph_pa_dac_state)) {
-		pr_err("%s: HPHR clear flag and enable PA\n", __func__);
-		snd_soc_update_bits(sitar->codec, SITAR_A_RX_HPH_CNP_EN, 0x10,
-				   1 << 4);
-	}
-	if (test_and_clear_bit(SITAR_HPHL_PA_OFF_ACK,
-				&sitar->hph_pa_dac_state)) {
-		pr_err("%s: HPHL clear flag and enable PA\n", __func__);
-		snd_soc_update_bits(sitar->codec, SITAR_A_RX_HPH_CNP_EN, 0x20,
-				   1 << 5);
-	}
-
-	if (test_and_clear_bit(SITAR_HPHR_DAC_OFF_ACK,
-				&sitar->hph_pa_dac_state)) {
-		pr_err("%s: HPHR clear flag and enable DAC\n", __func__);
-		snd_soc_update_bits(sitar->codec, SITAR_A_RX_HPH_R_DAC_CTL,
-				   0xC0, 0xC0);
-	}
-	if (test_and_clear_bit(SITAR_HPHL_DAC_OFF_ACK,
-				&sitar->hph_pa_dac_state)) {
-		pr_err("%s: HPHL clear flag and enable DAC\n", __func__);
-		snd_soc_update_bits(sitar->codec, SITAR_A_RX_HPH_L_DAC_CTL,
-				   0xC0, 0xC0);
-	}
-}
-
 static irqreturn_t sitar_hs_insert_irq(int irq, void *data)
 {
 	struct sitar_priv *priv = data;
 	struct snd_soc_codec *codec = priv->codec;
-	const struct sitar_mbhc_plug_detect_cfg *plug_det =
-	    SITAR_MBHC_CAL_PLUG_DET_PTR(priv->calibration);
-	int ldo_h_on, micb_cfilt_on;
-	short mb_v;
-	u8 is_removal;
-	int mic_mv;
 
 	pr_debug("%s: enter\n", __func__);
+	SITAR_ACQUIRE_LOCK(priv->codec_resource_lock);
 	wcd9xxx_disable_irq(codec->control_data, SITAR_IRQ_MBHC_INSERTION);
 
-	is_removal = snd_soc_read(codec, SITAR_A_CDC_MBHC_INT_CTL) & 0x02;
 	snd_soc_update_bits(codec, SITAR_A_CDC_MBHC_INT_CTL, 0x03, 0x00);
 
 	/* Turn off both HPH and MIC line schmitt triggers */
 	snd_soc_update_bits(codec, priv->mbhc_bias_regs.mbhc_reg, 0x90, 0x00);
 	snd_soc_update_bits(codec, SITAR_A_MBHC_HPH, 0x13, 0x00);
+	snd_soc_update_bits(codec, priv->mbhc_bias_regs.ctl_reg, 0x01, 0x00);
 
-	if (priv->mbhc_fake_ins_start &&
-	    time_after(jiffies, priv->mbhc_fake_ins_start +
-			msecs_to_jiffies(SITAR_FAKE_INS_THRESHOLD_MS))) {
-		pr_debug("%s: fake context interrupt, reset insertion\n",
-			 __func__);
-		priv->mbhc_fake_ins_start = 0;
-		sitar_codec_shutdown_hs_polling(codec);
-		sitar_codec_enable_hs_detect(codec, 1);
-		return IRQ_HANDLED;
-	}
+	pr_debug("%s: MIC trigger insertion interrupt\n", __func__);
 
-	ldo_h_on = snd_soc_read(codec, SITAR_A_LDO_H_MODE_1) & 0x80;
-	micb_cfilt_on = snd_soc_read(codec, priv->mbhc_bias_regs.cfilt_ctl)
-			    & 0x80;
+	rmb();
+	if (priv->lpi_enabled)
+		msleep(100);
 
-	if (!ldo_h_on)
-		snd_soc_update_bits(codec, SITAR_A_LDO_H_MODE_1, 0x80, 0x80);
-	if (!micb_cfilt_on)
-		snd_soc_update_bits(codec, priv->mbhc_bias_regs.cfilt_ctl,
-				    0x80, 0x80);
-	if (plug_det->t_ins_complete > 20)
-		msleep(plug_det->t_ins_complete);
-	else
-		usleep_range(plug_det->t_ins_complete * 1000,
-			     plug_det->t_ins_complete * 1000);
-
-	if (!ldo_h_on)
-		snd_soc_update_bits(codec, SITAR_A_LDO_H_MODE_1, 0x80, 0x0);
-	if (!micb_cfilt_on)
-		snd_soc_update_bits(codec, priv->mbhc_bias_regs.cfilt_ctl,
-							0x80, 0x0);
-
-	if (is_removal) {
-		/*
-		* If headphone is removed while playback is in progress,
-		* it is possible that micbias will be switched to VDDIO.
-		*/
-		if (priv->mbhc_micbias_switched)
-			sitar_codec_switch_micbias(codec, 0);
-		priv->hph_status &= ~SND_JACK_HEADPHONE;
-
-		/* If headphone PA is on, check if userspace receives
-		* removal event to sync-up PA's state */
-		if (sitar_is_hph_pa_on(codec)) {
-			set_bit(SITAR_HPHL_PA_OFF_ACK, &priv->hph_pa_dac_state);
-			set_bit(SITAR_HPHR_PA_OFF_ACK, &priv->hph_pa_dac_state);
-		}
-
-		if (sitar_is_hph_dac_on(codec, 1))
-			set_bit(SITAR_HPHL_DAC_OFF_ACK,
-				&priv->hph_pa_dac_state);
-		if (sitar_is_hph_dac_on(codec, 0))
-			set_bit(SITAR_HPHR_DAC_OFF_ACK,
-				&priv->hph_pa_dac_state);
-
-		if (priv->headset_jack) {
-			pr_err("%s: Reporting removal\n", __func__);
-			sitar_snd_soc_jack_report(priv, priv->headset_jack,
-						 priv->hph_status,
-						 SITAR_JACK_MASK);
-		}
-		sitar_codec_shutdown_hs_removal_detect(codec);
-		sitar_codec_enable_hs_detect(codec, 1);
-		return IRQ_HANDLED;
-	}
-
-	mb_v = sitar_codec_setup_hs_polling(codec);
-	mic_mv = sitar_codec_sta_dce_v(codec, 0, mb_v);
-
-	if (mb_v > (short) priv->mbhc_data.v_ins_hu) {
-		pr_debug("%s: Fake insertion interrupt since %dmsec ago, "
-			 "STA : %d,%d\n", __func__,
-			 (priv->mbhc_fake_ins_start ?
-			     jiffies_to_msecs(jiffies -
-					      priv->mbhc_fake_ins_start) :
-			     0),
-			 mb_v, mic_mv);
-		if (time_after(jiffies,
-			priv->mbhc_fake_ins_start +
-			msecs_to_jiffies(SITAR_FAKE_INS_THRESHOLD_MS))) {
-			/* Disable HPH trigger and enable MIC line trigger */
-			snd_soc_update_bits(codec, SITAR_A_MBHC_HPH, 0x12,
-					    0x00);
-			snd_soc_update_bits(codec,
-					    priv->mbhc_bias_regs.mbhc_reg, 0x60,
-					    plug_det->mic_current << 5);
-			snd_soc_update_bits(codec,
-					    priv->mbhc_bias_regs.mbhc_reg,
-					    0x80, 0x80);
-			usleep_range(plug_det->t_mic_pid, plug_det->t_mic_pid);
-			snd_soc_update_bits(codec,
-					    priv->mbhc_bias_regs.mbhc_reg,
-					    0x10, 0x10);
-		} else {
-			if (priv->mbhc_fake_ins_start == 0)
-				priv->mbhc_fake_ins_start = jiffies;
-			/* Setup normal insert detection
-			 * Enable HPH Schmitt Trigger
-			 */
-			snd_soc_update_bits(codec, SITAR_A_MBHC_HPH,
-					    0x13 | 0x0C,
-					    0x13 | plug_det->hph_current << 2);
-		}
-		/* Setup for insertion detection */
-		snd_soc_update_bits(codec, SITAR_A_CDC_MBHC_INT_CTL, 0x2, 0);
-		wcd9xxx_enable_irq(codec->control_data,
-					SITAR_IRQ_MBHC_INSERTION);
-		snd_soc_update_bits(codec, SITAR_A_CDC_MBHC_INT_CTL, 0x1, 0x1);
-
-	} else if (mb_v < (short) priv->mbhc_data.v_no_mic) {
-		pr_debug("%s: Headphone Detected, mb_v: %d,%d\n",
-			 __func__, mb_v, mic_mv);
-		priv->mbhc_fake_ins_start = 0;
-		priv->hph_status |= SND_JACK_HEADPHONE;
-		if (priv->headset_jack) {
-			pr_debug("%s: Reporting insertion %d\n", __func__,
-				 SND_JACK_HEADPHONE);
-			sitar_snd_soc_jack_report(priv, priv->headset_jack,
-						  priv->hph_status,
-						  SITAR_JACK_MASK);
-		}
-		sitar_codec_shutdown_hs_polling(codec);
-		sitar_codec_enable_hs_detect(codec, 0);
-		sitar_sync_hph_state(priv);
+	rmb();
+	if (!priv->lpi_enabled) {
+		pr_debug("%s: lpi is disabled\n", __func__);
+	} else if (gpio_get_value_cansleep(priv->mbhc_cfg.gpio) ==
+		   priv->mbhc_cfg.gpio_level_insert) {
+		pr_debug("%s: Valid insertion, "
+			 "detect plug type\n", __func__);
+		sitar_codec_decide_gpio_plug(codec);
 	} else {
-		pr_debug("%s: Headset detected, mb_v: %d,%d\n",
-			__func__, mb_v, mic_mv);
-		priv->mbhc_fake_ins_start = 0;
-		priv->hph_status |= SND_JACK_HEADSET;
-		if (priv->headset_jack) {
-			pr_debug("%s: Reporting insertion %d\n", __func__,
-				 SND_JACK_HEADSET);
-			sitar_snd_soc_jack_report(priv, priv->headset_jack,
-						  priv->hph_status,
-						  SITAR_JACK_MASK);
+		pr_debug("%s: Invalid insertion, "
+			 "stop plug detection\n", __func__);
+	}
+	SITAR_RELEASE_LOCK(priv->codec_resource_lock);
+	return IRQ_HANDLED;
+}
+
+static bool is_valid_mic_voltage(struct snd_soc_codec *codec, s32 mic_mv)
+{
+	struct sitar_priv *sitar = snd_soc_codec_get_drvdata(codec);
+	struct sitar_mbhc_plug_type_cfg *plug_type =
+		SITAR_MBHC_CAL_PLUG_TYPE_PTR(sitar->mbhc_cfg.calibration);
+
+	return (!(mic_mv > SITAR_MBHC_FAKE_INSERT_LOW
+				&& mic_mv < SITAR_MBHC_FAKE_INSERT_HIGH)
+			&& (mic_mv > plug_type->v_no_mic)
+			&& (mic_mv < plug_type->v_hs_max)) ? true : false;
+}
+
+/* called under codec_resource_lock acquisition
+ * returns true if mic voltage range is back to normal insertion
+ * returns false either if timedout or removed */
+static bool sitar_hs_remove_settle(struct snd_soc_codec *codec)
+{
+	int i;
+	bool timedout, settled = false;
+	s32 mic_mv[MBHC_NUM_DCE_PLUG_DETECT];
+	short mb_v[MBHC_NUM_DCE_PLUG_DETECT];
+	unsigned long retry = 0, timeout;
+	struct sitar_priv *sitar = snd_soc_codec_get_drvdata(codec);
+
+	timeout = jiffies + msecs_to_jiffies(SITAR_HS_DETECT_PLUG_TIME_MS);
+	while (!(timedout = time_after(jiffies, timeout))) {
+		retry++;
+		if (sitar_hs_gpio_level_remove(sitar)) {
+			pr_debug("%s: GPIO indicates removal\n", __func__);
+			break;
 		}
-		/* avoid false button press detect */
-		msleep(50);
-		sitar_codec_start_hs_polling(codec);
-		sitar_sync_hph_state(priv);
+
+		if (retry > 1)
+			msleep(250);
+		else
+			msleep(50);
+
+		if (sitar_hs_gpio_level_remove(sitar)) {
+			pr_debug("%s: GPIO indicates removal\n", __func__);
+			break;
+		}
+
+		sitar_turn_onoff_override(codec, true);
+		for (i = 0; i < MBHC_NUM_DCE_PLUG_DETECT; i++) {
+			mb_v[i] = __sitar_codec_sta_dce(codec, 1,  true, true);
+			mic_mv[i] = sitar_codec_sta_dce_v(codec, 1 , mb_v[i]);
+			pr_debug("%s : DCE run %lu, mic_mv = %d(%x)\n",
+				 __func__, retry, mic_mv[i], mb_v[i]);
+		}
+		sitar_turn_onoff_override(codec, false);
+
+		if (sitar_hs_gpio_level_remove(sitar)) {
+			pr_debug("%s: GPIO indicates removal\n", __func__);
+			break;
+		}
+
+		for (i = 0; i < MBHC_NUM_DCE_PLUG_DETECT; i++)
+			if (!is_valid_mic_voltage(codec, mic_mv[i]))
+				break;
+
+		if (i == MBHC_NUM_DCE_PLUG_DETECT) {
+			pr_debug("%s: MIC voltage settled\n", __func__);
+			settled = true;
+			msleep(200);
+			break;
+		}
 	}
 
-	return IRQ_HANDLED;
+	if (timedout)
+		pr_debug("%s: Microphone did not settle in %d seconds\n",
+			 __func__, SITAR_HS_DETECT_PLUG_TIME_MS);
+	return settled;
 }
 
 static irqreturn_t sitar_hs_remove_irq(int irq, void *data)
 {
-	short bias_value;
 	struct sitar_priv *priv = data;
 	struct snd_soc_codec *codec = priv->codec;
-	const struct sitar_mbhc_general_cfg *generic =
-	    SITAR_MBHC_CAL_GENERAL_PTR(priv->calibration);
-	int fake_removal = 0;
-	int min_us = SITAR_FAKE_REMOVAL_MIN_PERIOD_MS * 1000;
 
 	pr_debug("%s: enter, removal interrupt\n", __func__);
-	wcd9xxx_disable_irq(codec->control_data, SITAR_IRQ_MBHC_REMOVAL);
-	wcd9xxx_disable_irq(codec->control_data, SITAR_IRQ_MBHC_POTENTIAL);
-	wcd9xxx_disable_irq(codec->control_data, SITAR_IRQ_MBHC_RELEASE);
 
-	usleep_range(generic->t_shutdown_plug_rem,
-		     generic->t_shutdown_plug_rem);
-
-	do {
-		bias_value = sitar_codec_sta_dce(codec, 1);
-		pr_debug("%s: DCE %d,%d, %d us left\n", __func__, bias_value,
-			 sitar_codec_sta_dce_v(codec, 1, bias_value), min_us);
-		if (bias_value < (short)priv->mbhc_data.v_ins_h) {
-			fake_removal = 1;
-			break;
-		}
-		min_us -= priv->mbhc_data.t_dce;
-	} while (min_us > 0);
-
-	if (fake_removal) {
-		pr_debug("False alarm, headset not actually removed\n");
+	SITAR_ACQUIRE_LOCK(priv->codec_resource_lock);
+	if (sitar_hs_remove_settle(codec))
 		sitar_codec_start_hs_polling(codec);
-	} else {
-		/*
-		 * If this removal is not false, first check the micbias
-		 * switch status and switch it to LDOH if it is already
-		 * switched to VDDIO.
-		 */
-		if (priv->mbhc_micbias_switched)
-			sitar_codec_switch_micbias(codec, 0);
-		priv->hph_status &= ~SND_JACK_HEADSET;
-		if (priv->headset_jack) {
-			pr_err("%s: Reporting removal\n", __func__);
-			sitar_snd_soc_jack_report(priv, priv->headset_jack, 0,
-						 SITAR_JACK_MASK);
-		}
-		sitar_codec_shutdown_hs_polling(codec);
+	pr_debug("%s: remove settle done\n", __func__);
 
-		sitar_codec_enable_hs_detect(codec, 1);
-	}
-
+	SITAR_RELEASE_LOCK(priv->codec_resource_lock);
 	return IRQ_HANDLED;
 }
 
@@ -4015,7 +4512,10 @@
 	sitar->config_mode_active = false;
 	sitar->mbhc_polling_active = false;
 	sitar->no_mic_headset_override = false;
+	mutex_init(&sitar->codec_resource_lock);
 	sitar->codec = codec;
+	sitar->mbhc_state = MBHC_STATE_NONE;
+	sitar->mbhc_last_resume = 0;
 	sitar->pdata = dev_get_platdata(codec->dev->parent);
 	sitar_update_reg_defaults(codec);
 	sitar_codec_init_reg(codec);
@@ -4057,7 +4557,6 @@
 			SITAR_IRQ_MBHC_REMOVAL);
 		goto err_remove_irq;
 	}
-	wcd9xxx_disable_irq(codec->control_data, SITAR_IRQ_MBHC_REMOVAL);
 
 	ret = wcd9xxx_request_irq(codec->control_data, SITAR_IRQ_MBHC_POTENTIAL,
 		sitar_dce_handler, "DC Estimation detect", sitar);
@@ -4066,7 +4565,6 @@
 			SITAR_IRQ_MBHC_POTENTIAL);
 		goto err_potential_irq;
 	}
-	wcd9xxx_disable_irq(codec->control_data, SITAR_IRQ_MBHC_POTENTIAL);
 
 	ret = wcd9xxx_request_irq(codec->control_data, SITAR_IRQ_MBHC_RELEASE,
 		sitar_release_handler, "Button Release detect", sitar);
@@ -4075,7 +4573,6 @@
 			SITAR_IRQ_MBHC_RELEASE);
 		goto err_release_irq;
 	}
-	wcd9xxx_disable_irq(codec->control_data, SITAR_IRQ_MBHC_RELEASE);
 
 	ret = wcd9xxx_request_irq(codec->control_data, SITAR_IRQ_SLIMBUS,
 		sitar_slimbus_irq, "SLIMBUS Slave", sitar);
@@ -4151,6 +4648,7 @@
 			SITAR_IRQ_MBHC_INSERTION, sitar);
 err_insert_irq:
 err_pdata:
+	mutex_destroy(&sitar->codec_resource_lock);
 	kfree(sitar);
 	return ret;
 }
@@ -4163,12 +4661,15 @@
 	wcd9xxx_free_irq(codec->control_data, SITAR_IRQ_MBHC_POTENTIAL, sitar);
 	wcd9xxx_free_irq(codec->control_data, SITAR_IRQ_MBHC_REMOVAL, sitar);
 	wcd9xxx_free_irq(codec->control_data, SITAR_IRQ_MBHC_INSERTION, sitar);
+	SITAR_ACQUIRE_LOCK(sitar->codec_resource_lock);
 	sitar_codec_disable_clock_block(codec);
+	SITAR_RELEASE_LOCK(sitar->codec_resource_lock);
 	sitar_codec_enable_bandgap(codec, SITAR_BANDGAP_OFF);
 	if (sitar->mbhc_fw)
 		release_firmware(sitar->mbhc_fw);
 	for (i = 0; i < ARRAY_SIZE(sitar_dai); i++)
 		kfree(sitar->dai[i].ch_num);
+	mutex_destroy(&sitar->codec_resource_lock);
 	kfree(sitar);
 	return 0;
 }
@@ -4232,7 +4733,10 @@
 
 static int sitar_resume(struct device *dev)
 {
+	struct platform_device *pdev = to_platform_device(dev);
+	struct sitar_priv *sitar = platform_get_drvdata(pdev);
 	dev_dbg(dev, "%s: system resume\n", __func__);
+	sitar->mbhc_last_resume = jiffies;
 	return 0;
 }
 
diff --git a/sound/soc/codecs/wcd9304.h b/sound/soc/codecs/wcd9304.h
index cfe839b..70b3f0b 100644
--- a/sound/soc/codecs/wcd9304.h
+++ b/sound/soc/codecs/wcd9304.h
@@ -156,12 +156,29 @@
 	u16 _beta[3];
 } __packed;
 
+struct sitar_mbhc_config {
+	struct snd_soc_jack *headset_jack;
+	struct snd_soc_jack *button_jack;
+	bool read_fw_bin;
+	/* void* calibration contains:
+	 *  struct tabla_mbhc_general_cfg generic;
+	 *  struct tabla_mbhc_plug_detect_cfg plug_det;
+	 *  struct tabla_mbhc_plug_type_cfg plug_type;
+	 *  struct tabla_mbhc_btn_detect_cfg btn_det;
+	 *  struct tabla_mbhc_imped_detect_cfg imped_det;
+	 * Note: various size depends on btn_det->num_btn
+	 */
+	void *calibration;
+	enum sitar_micbias_num micbias;
+	int (*mclk_cb_fn) (struct snd_soc_codec*, int, bool);
+	unsigned int mclk_rate;
+	unsigned int gpio;
+	unsigned int gpio_irq;
+	int gpio_level_insert;
+};
+
 extern int sitar_hs_detect(struct snd_soc_codec *codec,
-			   struct snd_soc_jack *headset_jack,
-			   struct snd_soc_jack *button_jack,
-			   void *calibration, enum sitar_micbias_num micbis,
-			   int (*mclk_cb_fn) (struct snd_soc_codec*, int),
-			   int read_fw_bin, u32 mclk_rate);
+			const struct sitar_mbhc_config *cfg);
 
 #ifndef anc_header_dec
 struct anc_header {
@@ -171,7 +188,8 @@
 #define anc_header_dec
 #endif
 
-extern int sitar_mclk_enable(struct snd_soc_codec *codec, int mclk_enable);
+extern int sitar_mclk_enable(struct snd_soc_codec *codec, int mclk_enable,
+							 bool dapm);
 
 extern void *sitar_mbhc_cal_btn_det_mp(const struct sitar_mbhc_btn_detect_cfg
 				       *btn_det,
diff --git a/sound/soc/codecs/wcd9310.c b/sound/soc/codecs/wcd9310.c
index d2f58a5..6ee8287 100644
--- a/sound/soc/codecs/wcd9310.c
+++ b/sound/soc/codecs/wcd9310.c
@@ -3828,8 +3828,7 @@
 		*tx_num = tabla_dai[dai->id - 1].capture.channels_max;
 		tx_slot[0] = tx_ch[cnt];
 		tx_slot[1] = tx_ch[1 + cnt];
-		tx_slot[2] = tx_ch[3 + cnt];
-		tx_slot[3] = tx_ch[5 + cnt];
+		tx_slot[2] = tx_ch[5 + cnt];
 	}
 
 	return 0;
@@ -4526,7 +4525,7 @@
 				SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
 
 	SND_SOC_DAPM_MUX("SLIM TX4 MUX", SND_SOC_NOPM, 0, 0, &sb_tx4_mux),
-	SND_SOC_DAPM_AIF_OUT_E("SLIM TX4", "AIF2 Capture", 0, SND_SOC_NOPM, 0,
+	SND_SOC_DAPM_AIF_OUT_E("SLIM TX4", "AIF1 Capture", 0, SND_SOC_NOPM, 0,
 				0, tabla_codec_enable_slimtx,
 				SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
 
diff --git a/sound/soc/msm/apq8064.c b/sound/soc/msm/apq8064.c
index 392e05d..b3374fe 100644
--- a/sound/soc/msm/apq8064.c
+++ b/sound/soc/msm/apq8064.c
@@ -442,7 +442,7 @@
 	SND_SOC_DAPM_MIC("Digital Mic6", NULL),
 };
 
-static const struct snd_soc_dapm_route apq8064_audio_map[] = {
+static const struct snd_soc_dapm_route apq8064_common_audio_map[] = {
 
 	{"RX_BIAS", NULL, "MCLK"},
 	{"LDO_H", NULL, "MCLK"},
@@ -457,13 +457,6 @@
 	{"Ext Spk Top Neg", NULL, "LINEOUT4"},
 
 	/************   Analog MIC Paths  ************/
-	/**
-	 * Analog mic7 (Front Top Mic) on Liquid.
-	 * Used as Handset mic on CDP.
-	 * Not there on MTP.
-	 */
-	{"AMIC1", NULL, "MIC BIAS1 External"},
-	{"MIC BIAS1 External", NULL, "Analog mic7"},
 
 	/* Headset Mic */
 	{"AMIC2", NULL, "MIC BIAS2 External"},
@@ -475,6 +468,59 @@
 
 	{"AMIC4", NULL, "MIC BIAS1 Internal2"},
 	{"MIC BIAS1 Internal2", NULL, "ANCLeft Headset Mic"},
+};
+
+static const struct snd_soc_dapm_route apq8064_mtp_audio_map[] = {
+
+	/************   Digital MIC Paths  ************/
+
+	/*
+	 * Digital Mic1 (Front bottom Left) on MTP.
+	 * Conncted to DMIC1 Input on Tabla codec.
+	 */
+	{"DMIC1", NULL, "MIC BIAS1 External"},
+	{"MIC BIAS1 External", NULL, "Digital Mic1"},
+
+	/**
+	 * Digital Mic2 (Front bottom right) on MTP.
+	 * Conncted to DMIC2 Input on Tabla codec.
+	 */
+	{"DMIC2", NULL, "MIC BIAS1 External"},
+	{"MIC BIAS1 External", NULL, "Digital Mic2"},
+
+	/**
+	 * Digital Mic3 (Back bottom) on MTP.
+	 * Conncted to DMIC3 Input on Tabla codec.
+	 */
+	{"DMIC3", NULL, "MIC BIAS3 External"},
+	{"MIC BIAS3 External", NULL, "Digital Mic3"},
+
+	/**
+	 * Digital Mic4 (Back top) on MTP.
+	 * Conncted to DMIC4 Input on Tabla codec.
+	 */
+	{"DMIC4", NULL, "MIC BIAS3 External"},
+	{"MIC BIAS3 External", NULL, "Digital Mic4"},
+
+	/**
+	 * Digital Mic5 (Top front Mic) on MTP.
+	 * Conncted to DMIC6 Input on Tabla codec.
+	 */
+	{"DMIC6", NULL, "MIC BIAS4 External"},
+	{"MIC BIAS4 External", NULL, "Digital Mic5"},
+
+};
+
+static const struct snd_soc_dapm_route apq8064_liquid_cdp_audio_map[] = {
+
+	/************   Analog MIC Paths  ************/
+	/**
+	 * Analog mic7 (Front Top Mic) on Liquid.
+	 * Used as Handset mic on CDP.
+	 * Not there on MTP.
+	 */
+	{"AMIC1", NULL, "MIC BIAS1 External"},
+	{"MIC BIAS1 External", NULL, "Analog mic7"},
 
 
 	/************   Digital MIC Paths  ************/
@@ -940,8 +986,16 @@
 	snd_soc_dapm_new_controls(dapm, apq8064_dapm_widgets,
 				ARRAY_SIZE(apq8064_dapm_widgets));
 
-	snd_soc_dapm_add_routes(dapm, apq8064_audio_map,
-		ARRAY_SIZE(apq8064_audio_map));
+	snd_soc_dapm_add_routes(dapm, apq8064_common_audio_map,
+		ARRAY_SIZE(apq8064_common_audio_map));
+
+	if (machine_is_apq8064_mtp()) {
+		snd_soc_dapm_add_routes(dapm, apq8064_mtp_audio_map,
+			ARRAY_SIZE(apq8064_mtp_audio_map));
+	} else  {
+		snd_soc_dapm_add_routes(dapm, apq8064_liquid_cdp_audio_map,
+			ARRAY_SIZE(apq8064_liquid_cdp_audio_map));
+	}
 
 	snd_soc_dapm_enable_pin(dapm, "Ext Spk Bottom Pos");
 	snd_soc_dapm_enable_pin(dapm, "Ext Spk Bottom Neg");
diff --git a/sound/soc/msm/msm8930.c b/sound/soc/msm/msm8930.c
index 04b0fb0..a2c6f5f 100644
--- a/sound/soc/msm/msm8930.c
+++ b/sound/soc/msm/msm8930.c
@@ -55,7 +55,24 @@
 
 static struct snd_soc_jack hs_jack;
 static struct snd_soc_jack button_jack;
-static void *sitar_mbhc_cal;
+
+static int msm8930_enable_codec_ext_clk(
+		struct snd_soc_codec *codec, int enable,
+		bool dapm);
+
+static struct sitar_mbhc_config mbhc_cfg = {
+	.headset_jack = &hs_jack,
+	.button_jack = &button_jack,
+	.read_fw_bin = false,
+	.calibration = NULL,
+	.micbias = SITAR_MICBIAS2,
+	.mclk_cb_fn = msm8930_enable_codec_ext_clk,
+	.mclk_rate = SITAR_EXT_CLK_RATE,
+	.gpio = 0,
+	.gpio_irq = 0,
+	.gpio_level_insert = 1,
+};
+
 
 static void msm8930_ext_control(struct snd_soc_codec *codec)
 {
@@ -102,8 +119,9 @@
 	return 0;
 }
 
-int msm8930_enable_codec_ext_clk(
-		struct snd_soc_codec *codec, int enable)
+static int msm8930_enable_codec_ext_clk(
+		struct snd_soc_codec *codec, int enable,
+		bool dapm)
 {
 	pr_debug("%s: enable = %d\n", __func__, enable);
 	if (enable) {
@@ -115,7 +133,7 @@
 		if (codec_clk) {
 			clk_set_rate(codec_clk, SITAR_EXT_CLK_RATE);
 			clk_prepare_enable(codec_clk);
-			sitar_mclk_enable(codec, 1);
+			sitar_mclk_enable(codec, 1, dapm);
 		} else {
 			pr_err("%s: Error setting Sitar MCLK\n", __func__);
 			clk_users--;
@@ -129,7 +147,7 @@
 		if (!clk_users) {
 			pr_debug("%s: disabling MCLK. clk_users = %d\n",
 					 __func__, clk_users);
-			sitar_mclk_enable(codec, 0);
+			sitar_mclk_enable(codec, 0, dapm);
 			clk_disable_unprepare(codec_clk);
 		}
 	}
@@ -143,9 +161,9 @@
 
 	switch (event) {
 	case SND_SOC_DAPM_PRE_PMU:
-		return msm8930_enable_codec_ext_clk(w->codec, 1);
+		return msm8930_enable_codec_ext_clk(w->codec, 1, true);
 	case SND_SOC_DAPM_POST_PMD:
-		return msm8930_enable_codec_ext_clk(w->codec, 0);
+		return msm8930_enable_codec_ext_clk(w->codec, 0, true);
 	}
 	return 0;
 }
@@ -520,6 +538,10 @@
 	}
 	codec_clk = clk_get(cpu_dai->dev, "osr_clk");
 
+	mbhc_cfg.gpio = 37;
+	mbhc_cfg.gpio_irq = gpio_to_irq(mbhc_cfg.gpio);
+	sitar_hs_detect(codec, &mbhc_cfg);
+
 	return 0;
 }
 
@@ -978,8 +1000,8 @@
 		pr_err("%s: Not the right machine type\n", __func__);
 		return -ENODEV ;
 	}
-	sitar_mbhc_cal = def_sitar_mbhc_cal();
-	if (!sitar_mbhc_cal) {
+	mbhc_cfg.calibration = def_sitar_mbhc_cal();
+	if (!mbhc_cfg.calibration) {
 		pr_err("Calibration data allocation failed\n");
 		return -ENOMEM;
 	}
@@ -987,7 +1009,7 @@
 	msm8930_snd_device = platform_device_alloc("soc-audio", 0);
 	if (!msm8930_snd_device) {
 		pr_err("Platform device allocation failed\n");
-		kfree(sitar_mbhc_cal);
+		kfree(mbhc_cfg.calibration);
 		return -ENOMEM;
 	}
 
@@ -995,7 +1017,7 @@
 	ret = platform_device_add(msm8930_snd_device);
 	if (ret) {
 		platform_device_put(msm8930_snd_device);
-		kfree(sitar_mbhc_cal);
+		kfree(mbhc_cfg.calibration);
 		return ret;
 	}
 
@@ -1018,7 +1040,7 @@
 	}
 	msm8930_free_headset_mic_gpios();
 	platform_device_unregister(msm8930_snd_device);
-	kfree(sitar_mbhc_cal);
+	kfree(mbhc_cfg.calibration);
 }
 module_exit(msm8930_audio_exit);
 
diff --git a/sound/soc/msm/qdsp6/q6asm.c b/sound/soc/msm/qdsp6/q6asm.c
index f3a2383..ba5c79d 100644
--- a/sound/soc/msm/qdsp6/q6asm.c
+++ b/sound/soc/msm/qdsp6/q6asm.c
@@ -1791,6 +1791,67 @@
 	return -EINVAL;
 }
 
+int q6asm_set_encdec_chan_map(struct audio_client *ac,
+			uint32_t num_channels)
+{
+	struct asm_stream_cmd_encdec_channelmap chan_map;
+	u8 *channel_mapping;
+
+	int rc = 0;
+
+	pr_debug("%s: Session %d, num_channels = %d\n",
+			 __func__, ac->session, num_channels);
+
+	q6asm_add_hdr(ac, &chan_map.hdr, sizeof(chan_map), TRUE);
+
+	chan_map.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM;
+	chan_map.param_id = ASM_ENCDEC_DEC_CHAN_MAP;
+	chan_map.param_size = sizeof(struct asm_dec_chan_map);
+	chan_map.chan_map.num_channels = num_channels;
+
+	channel_mapping =
+		chan_map.chan_map.channel_mapping;
+
+	memset(channel_mapping, PCM_CHANNEL_NULL, MAX_CHAN_MAP_CHANNELS);
+	if (num_channels == 1)  {
+		channel_mapping[0] = PCM_CHANNEL_FL;
+	} else if (num_channels == 2) {
+		channel_mapping[0] = PCM_CHANNEL_FL;
+		channel_mapping[1] = PCM_CHANNEL_FR;
+	} else if (num_channels == 6) {
+		channel_mapping[0] = PCM_CHANNEL_FC;
+		channel_mapping[1] = PCM_CHANNEL_FL;
+		channel_mapping[2] = PCM_CHANNEL_FR;
+		channel_mapping[3] = PCM_CHANNEL_LB;
+		channel_mapping[4] = PCM_CHANNEL_RB;
+		channel_mapping[5] = PCM_CHANNEL_LFE;
+	} else {
+		pr_err("%s: ERROR.unsupported num_ch = %u\n", __func__,
+				num_channels);
+		rc = -EINVAL;
+		goto fail_cmd;
+	}
+
+	rc = apr_send_pkt(ac->apr, (uint32_t *) &chan_map);
+	if (rc < 0) {
+		pr_err("%s:Command opcode[0x%x]paramid[0x%x] failed\n",
+				__func__, ASM_STREAM_CMD_SET_ENCDEC_PARAM,
+				ASM_ENCDEC_DEC_CHAN_MAP);
+		goto fail_cmd;
+	}
+	rc = wait_event_timeout(ac->cmd_wait,
+			(atomic_read(&ac->cmd_state) == 0), 5*HZ);
+	if (!rc) {
+		pr_err("%s:timeout opcode[0x%x]\n", __func__,
+						chan_map.hdr.opcode);
+		rc = -ETIMEDOUT;
+		goto fail_cmd;
+	}
+	return 0;
+fail_cmd:
+	return rc;
+}
+
 int q6asm_enc_cfg_blk_qcelp(struct audio_client *ac, uint32_t frames_per_buf,
 		uint16_t min_rate, uint16_t max_rate,
 		uint16_t reduced_rate_level, uint16_t rate_modulation_cmd)