Merge "defconfig: Enable IPC Router in 9615 defconfig" into msm-3.0
diff --git a/arch/arm/configs/msm8960_defconfig b/arch/arm/configs/msm8960_defconfig
index 7564020..bf15477 100755
--- a/arch/arm/configs/msm8960_defconfig
+++ b/arch/arm/configs/msm8960_defconfig
@@ -325,6 +325,7 @@
 CONFIG_USB_STORAGE_KARMA=y
 CONFIG_USB_STORAGE_CYPRESS_ATACB=y
 CONFIG_USB_EHSET_TEST_FIXTURE=y
+CONFIG_USB_QCOM_DUN_BRIDGE=y
 CONFIG_USB_GADGET=y
 CONFIG_USB_GADGET_DEBUG_FILES=y
 CONFIG_USB_GADGET_CI13XXX_MSM=y
diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig
index 99655d0..0eb414f 100644
--- a/arch/arm/mach-msm/Kconfig
+++ b/arch/arm/mach-msm/Kconfig
@@ -229,6 +229,7 @@
 	bool
 	select MSM_DALRPC
 	select MSM_PROC_COMM_REGULATOR
+	select MULTI_IRQ_HANDLER
 
 config  MSM_VIC
 	bool
diff --git a/arch/arm/mach-msm/acpuclock-8960.c b/arch/arm/mach-msm/acpuclock-8960.c
index d827dd1..d74db47 100644
--- a/arch/arm/mach-msm/acpuclock-8960.c
+++ b/arch/arm/mach-msm/acpuclock-8960.c
@@ -71,6 +71,9 @@
 
 #define SECCLKAGD		BIT(4)
 
+/* PTE EFUSE register. */
+#define QFPROM_PTE_EFUSE_ADDR	(MSM_QFPROM_BASE + 0x00C0)
+
 enum scalables {
 	CPU0 = 0,
 	CPU1,
@@ -975,6 +978,30 @@
 
 	/* Select frequency tables. */
 	if (cpu_is_msm8960()) {
+		uint32_t pte_efuse, pvs;
+
+		pte_efuse = readl_relaxed(QFPROM_PTE_EFUSE_ADDR);
+		pvs = (pte_efuse >> 10) & 0x7;
+		if (pvs == 0x7)
+			pvs = (pte_efuse >> 13) & 0x7;
+
+		switch (pvs) {
+		case 0x0:
+		case 0x7:
+			pr_info("ACPU PVS: Slow\n");
+			break;
+		case 0x1:
+			pr_info("ACPU PVS: Nominal\n");
+			break;
+		case 0x3:
+			pr_info("ACPU PVS: Fast\n");
+			break;
+		default:
+			pr_warn("ACPU PVS: Unknown. Defaulting to slow.\n");
+			break;
+		}
+
+		/* TODO: Select tables based on PVS data. */
 		scalable = scalable_8960;
 		acpu_freq_tbl = acpu_freq_tbl_8960;
 		l2_freq_tbl = l2_freq_tbl_8960;
diff --git a/arch/arm/mach-msm/board-msm7x27a.c b/arch/arm/mach-msm/board-msm7x27a.c
index 67b1dcc..51bd856 100644
--- a/arch/arm/mach-msm/board-msm7x27a.c
+++ b/arch/arm/mach-msm/board-msm7x27a.c
@@ -3249,6 +3249,7 @@
 	.init_machine	= msm7627a_rumi3_init,
 	.timer		= &msm_timer,
 	.init_early     = msm7x2x_init_early,
+	.handle_irq	= vic_handle_irq,
 MACHINE_END
 MACHINE_START(MSM7X27A_SURF, "QCT MSM7x27a SURF")
 	.boot_params	= PHYS_OFFSET + 0x100,
@@ -3258,6 +3259,7 @@
 	.init_machine	= msm7x2x_init,
 	.timer		= &msm_timer,
 	.init_early     = msm7x2x_init_early,
+	.handle_irq	= vic_handle_irq,
 MACHINE_END
 MACHINE_START(MSM7X27A_FFA, "QCT MSM7x27a FFA")
 	.boot_params	= PHYS_OFFSET + 0x100,
@@ -3267,6 +3269,7 @@
 	.init_machine	= msm7x2x_init,
 	.timer		= &msm_timer,
 	.init_early     = msm7x2x_init_early,
+	.handle_irq	= vic_handle_irq,
 MACHINE_END
 MACHINE_START(MSM7625A_SURF, "QCT MSM7625a SURF")
 	.boot_params    = PHYS_OFFSET + 0x100,
@@ -3276,6 +3279,7 @@
 	.init_machine   = msm7x2x_init,
 	.timer          = &msm_timer,
 	.init_early     = msm7x2x_init_early,
+	.handle_irq	= vic_handle_irq,
 MACHINE_END
 MACHINE_START(MSM7625A_FFA, "QCT MSM7625a FFA")
 	.boot_params    = PHYS_OFFSET + 0x100,
@@ -3285,4 +3289,5 @@
 	.init_machine   = msm7x2x_init,
 	.timer          = &msm_timer,
 	.init_early     = msm7x2x_init_early,
+	.handle_irq	= vic_handle_irq,
 MACHINE_END
diff --git a/arch/arm/mach-msm/clock-8960.c b/arch/arm/mach-msm/clock-8960.c
index 243b3d2..24ada1a 100644
--- a/arch/arm/mach-msm/clock-8960.c
+++ b/arch/arm/mach-msm/clock-8960.c
@@ -5794,13 +5794,18 @@
 	clk_set_rate(&usb_hsic_hsio_cal_clk.c, 9000000);
 
 	/*
-	 * The halt status bits for PDM and TSSC may be incorrect at boot.
+	 * The halt status bits for these clocks may be incorrect at boot.
 	 * Toggle these clocks on and off to refresh them.
 	 */
 	rcg_clk_enable(&pdm_clk.c);
 	rcg_clk_disable(&pdm_clk.c);
 	rcg_clk_enable(&tssc_clk.c);
 	rcg_clk_disable(&tssc_clk.c);
+	if (cpu_is_msm8960() &&
+			SOCINFO_VERSION_MAJOR(socinfo_get_version()) >= 2) {
+		clk_enable(&usb_hsic_hsic_clk.c);
+		clk_disable(&usb_hsic_hsic_clk.c);
+	}
 
 	if (machine_is_msm8960_sim()) {
 		clk_set_rate(&sdc1_clk.c, 48000000);
diff --git a/arch/arm/mach-msm/devices-8960.c b/arch/arm/mach-msm/devices-8960.c
index a92324c..b531dec 100644
--- a/arch/arm/mach-msm/devices-8960.c
+++ b/arch/arm/mach-msm/devices-8960.c
@@ -81,6 +81,9 @@
 #define MSM_PMIC2_SSBI_CMD_PHYS	0x00C00000
 #define MSM_PMIC_SSBI_SIZE	SZ_4K
 
+#define MSM8960_HSUSB_PHYS		0x12500000
+#define MSM8960_HSUSB_SIZE		SZ_4K
+
 static struct resource resources_otg[] = {
 	{
 		.start	= MSM8960_HSUSB_PHYS,
@@ -154,8 +157,8 @@
 
 static struct resource resources_hsic_host[] = {
 	{
-		.start	= MSM_HSIC_PHYS,
-		.end	= MSM_HSIC_PHYS + MSM_HSIC_SIZE - 1,
+		.start	= 0x12520000,
+		.end	= 0x12520000 + SZ_4K - 1,
 		.flags	= IORESOURCE_MEM,
 	},
 	{
diff --git a/arch/arm/mach-msm/include/mach/board.h b/arch/arm/mach-msm/include/mach/board.h
index df6a64c..349b2d0 100644
--- a/arch/arm/mach-msm/include/mach/board.h
+++ b/arch/arm/mach-msm/include/mach/board.h
@@ -419,6 +419,7 @@
 void __init msm_map_msm7x30_io(void);
 void __init msm_map_fsm9xxx_io(void);
 void __init msm_init_irq(void);
+void vic_handle_irq(struct pt_regs *regs);
 
 struct mmc_platform_data;
 int __init msm_add_sdcc(unsigned int controller,
diff --git a/arch/arm/mach-msm/include/mach/entry-macro.S b/arch/arm/mach-msm/include/mach/entry-macro.S
index eb5921f..d384366 100644
--- a/arch/arm/mach-msm/include/mach/entry-macro.S
+++ b/arch/arm/mach-msm/include/mach/entry-macro.S
@@ -10,11 +10,15 @@
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
  */
-
-#if defined(CONFIG_MSM_VIC)
+#if defined(CONFIG_MSM_VIC) && !defined(CONFIG_MULTI_IRQ_HANDLER)
 #include <mach/entry-macro-vic.S>
 #elif defined(CONFIG_ARM_GIC)
 #include <mach/entry-macro-qgic.S>
 #else
-#error "No interrupt controller selected!"
+	.macro	disable_fiq
+	.endm
+
+	.macro	arch_ret_to_user, tmp1, tmp2
+	.endm
+
 #endif
diff --git a/arch/arm/mach-msm/include/mach/msm_iomap-8960.h b/arch/arm/mach-msm/include/mach/msm_iomap-8960.h
index e6b7beb..56cbd2f 100644
--- a/arch/arm/mach-msm/include/mach/msm_iomap-8960.h
+++ b/arch/arm/mach-msm/include/mach/msm_iomap-8960.h
@@ -98,12 +98,6 @@
 #define MSM_GPT_BASE			(MSM_TMR_BASE + 0x4)
 #define MSM_DGT_BASE			(MSM_TMR_BASE + 0x24)
 
-#define MSM8960_HSUSB_PHYS		0x12500000
-#define MSM8960_HSUSB_SIZE		SZ_4K
-
-#define MSM_HSIC_PHYS			0x12520000
-#define MSM_HSIC_SIZE			SZ_4K
-
 #define MSM8960_HDMI_PHYS		0x04A00000
 #define MSM8960_HDMI_SIZE		SZ_4K
 
diff --git a/arch/arm/mach-msm/include/mach/usb_dun_bridge.h b/arch/arm/mach-msm/include/mach/usb_dun_bridge.h
new file mode 100644
index 0000000..b4a8eef
--- /dev/null
+++ b/arch/arm/mach-msm/include/mach/usb_dun_bridge.h
@@ -0,0 +1,113 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __USB_DUN_BRIDGE_H
+#define __USB_DUN_BRIDGE_H
+
+/**
+ * struct dun_bridge_ops - context and callbacks for DUN bridge
+ *
+ * @ctxt: caller private context
+ * @read_complete: called when read is completed. buf and len correspond
+ *	to original passed-in values. actual length of buffer returned, or
+ *	negative error value.
+ * @write_complete: called when write is completed. buf and len correspond
+ *	to original passed-in values. actual length of buffer returned, or
+ *	negative error value.
+ * @ctrl_status: asynchronous notification of control status. ctrl_bits
+ *	is a bitfield of CDC ACM control status bits.
+ */
+struct dun_bridge_ops {
+	void *ctxt;
+	void (*read_complete)(void *ctxt, char *buf, size_t len, size_t actual);
+	void (*write_complete)(void *ctxt, char *buf,
+				size_t len, size_t actual);
+	void (*ctrl_status)(void *ctxt, unsigned int ctrl_bits);
+};
+
+#ifdef CONFIG_USB_QCOM_DUN_BRIDGE
+
+/**
+ * dun_bridge_open - Open the DUN bridge
+ *
+ * @ops: pointer to ops struct containing private context and callback
+ *	pointers
+ */
+int dun_bridge_open(struct dun_bridge_ops *ops);
+
+/**
+ * dun_bridge_close - Closes the DUN bridge
+ */
+int dun_bridge_close(void);
+
+/**
+ * dun_bridge_read - Request to read data from the DUN bridge. This call is
+ *	asynchronous: user's read callback (ops->read_complete) will be called
+ *	when data is returned.
+ *
+ * @data: pointer to caller-allocated buffer to fill in
+ * @len: size of the buffer
+ */
+int dun_bridge_read(void *data, int len);
+
+/**
+ * dun_bridge_write - Request to write data to the DUN bridge. This call is
+ *	asynchronous: user's write callback (ops->write_complete) will be called
+ *	upon completion of the write indicating status and number of bytes
+ *	written.
+ *
+ * @data: pointer to caller-allocated buffer to write
+ * @len: length of the data in buffer
+ */
+int dun_bridge_write(void *data, int len);
+
+/**
+ * dun_bridge_send_ctrl_bits - Request to write line control data to the DUN
+ *	bridge.  This call is asynchronous, however no callback will be issued
+ *	upon completion.
+ *
+ * @ctrl_bits: CDC ACM line control bits
+ */
+int dun_bridge_send_ctrl_bits(unsigned ctrl_bits);
+
+#else
+
+#include <linux/errno.h>
+
+static int __maybe_unused dun_bridge_open(struct dun_bridge_ops *ops)
+{
+	return -ENODEV;
+}
+
+static int __maybe_unused dun_bridge_close(void)
+{
+	return -ENODEV;
+}
+
+static int __maybe_unused dun_bridge_read(void *data, int len)
+{
+	return -ENODEV;
+}
+
+static int __maybe_unused dun_bridge_write(void *data, int len)
+{
+	return -ENODEV;
+}
+
+static int __maybe_unused dun_bridge_send_ctrl_bits(unsigned ctrl_bits)
+{
+	return -ENODEV;
+}
+
+#endif
+
+#endif
diff --git a/arch/arm/mach-msm/irq-vic.c b/arch/arm/mach-msm/irq-vic.c
index 109231a..660d530 100644
--- a/arch/arm/mach-msm/irq-vic.c
+++ b/arch/arm/mach-msm/irq-vic.c
@@ -610,6 +610,33 @@
 	mb();
 }
 
+static inline void msm_vic_handle_irq(void __iomem *base_addr, struct pt_regs
+		*regs)
+{
+	u32 irqnr;
+
+	do {
+		/* 0xD0 has irq# or old irq# if the irq has been handled
+		 * 0xD4 has irq# or -1 if none pending *but* if you just
+		 * read 0xD4 you never get the first irq for some reason
+		 */
+		irqnr = readl_relaxed(base_addr + 0xD0);
+		irqnr = readl_relaxed(base_addr + 0xD4);
+		if (irqnr == -1)
+			break;
+		handle_IRQ(irqnr, regs);
+	} while (1);
+}
+
+/* enable imprecise aborts */
+#define local_cpsie_enable()  __asm__ __volatile__("cpsie a    @ enable")
+
+asmlinkage void __exception_irq_entry vic_handle_irq(struct pt_regs *regs)
+{
+	local_cpsie_enable();
+	msm_vic_handle_irq((void __iomem *)MSM_VIC_BASE, regs);
+}
+
 #if defined(CONFIG_MSM_FIQ_SUPPORT)
 void msm_trigger_irq(int irq)
 {
diff --git a/arch/arm/mach-msm/pm-8x60.c b/arch/arm/mach-msm/pm-8x60.c
index a929659..c831d4b 100644
--- a/arch/arm/mach-msm/pm-8x60.c
+++ b/arch/arm/mach-msm/pm-8x60.c
@@ -279,6 +279,12 @@
 	ret = 0;
 
 mode_sysfs_add_cpu_exit:
+	if (!ret) {
+		if (mode && mode->kobj)
+			kobject_del(mode->kobj);
+		kfree(mode);
+	}
+
 	return ret;
 }
 
diff --git a/drivers/media/video/msm/msm.h b/drivers/media/video/msm/msm.h
index bf75e8b..083fc57 100644
--- a/drivers/media/video/msm/msm.h
+++ b/drivers/media/video/msm/msm.h
@@ -91,6 +91,11 @@
 	uint32_t vb;
 };
 
+struct isp_msg_event {
+	uint32_t msg_id;
+	uint32_t sof_count;
+};
+
 struct isp_msg_output {
 	uint8_t   output_id;
 	struct msm_free_buf buf;
diff --git a/drivers/media/video/msm/msm_isp.c b/drivers/media/video/msm/msm_isp.c
index d914404..9204269 100644
--- a/drivers/media/video/msm/msm_isp.c
+++ b/drivers/media/video/msm/msm_isp.c
@@ -172,9 +172,13 @@
 	isp_event->isp_data.isp_msg.len = 0;
 
 	switch (notification) {
-	case NOTIFY_ISP_MSG_EVT:
-		isp_event->isp_data.isp_msg.msg_id = (uint32_t)arg;
+	case NOTIFY_ISP_MSG_EVT: {
+		struct isp_msg_event *isp_msg = (struct isp_msg_event *)arg;
+
+		isp_event->isp_data.isp_msg.msg_id = isp_msg->msg_id;
+		isp_event->isp_data.isp_msg.frame_id = isp_msg->sof_count;
 		break;
+	}
 	case NOTIFY_VFE_MSG_OUT: {
 		uint8_t msgid;
 		struct isp_msg_output *isp_output =
@@ -202,6 +206,8 @@
 		if (!rc) {
 			isp_event->isp_data.isp_msg.msg_id =
 				isp_output->output_id;
+			isp_event->isp_data.isp_msg.frame_id =
+				isp_output->frameCounter;
 			buf = isp_output->buf;
 			msm_mctl_buf_done(pmctl, msgid,
 				&buf, isp_output->frameCounter);
@@ -213,6 +219,8 @@
 		struct isp_msg_stats *isp_stats = (struct isp_msg_stats *)arg;
 
 		isp_event->isp_data.isp_msg.msg_id = isp_stats->id;
+		isp_event->isp_data.isp_msg.frame_id =
+			isp_stats->frameCounter;
 		stats.buffer = msm_pmem_stats_ptov_lookup(&pmctl->sync,
 						isp_stats->buffer,
 						&(stats.fd));
diff --git a/drivers/media/video/msm/msm_vfe32.c b/drivers/media/video/msm/msm_vfe32.c
index ea70ce5..219e504 100644
--- a/drivers/media/video/msm/msm_vfe32.c
+++ b/drivers/media/video/msm/msm_vfe32.c
@@ -1121,6 +1121,19 @@
 	vfe32_program_dmi_cfg(NO_MEM_SELECTED);
 }
 
+static void vfe32_send_isp_msg(
+	struct vfe32_ctrl_type *vctrl,
+	uint32_t isp_msg_id)
+{
+	struct isp_msg_event isp_msg_evt;
+
+	isp_msg_evt.msg_id = isp_msg_id;
+	isp_msg_evt.sof_count = vfe32_ctrl->vfeFrameId;
+	v4l2_subdev_notify(vctrl->subdev,
+			NOTIFY_ISP_MSG_EVT,
+			(void *)&isp_msg_evt);
+}
+
 static int vfe32_proc_general(struct msm_isp_cmd *cmd)
 {
 	int i , rc = 0;
@@ -2088,8 +2101,7 @@
 		CDBG("stop video triggered .\n");
 	}
 	if (vfe32_ctrl->start_ack_pending == TRUE) {
-		v4l2_subdev_notify(vfe32_ctrl->subdev, NOTIFY_ISP_MSG_EVT,
-			(void *)MSG_ID_START_ACK);
+		vfe32_send_isp_msg(vfe32_ctrl, MSG_ID_START_ACK);
 		vfe32_ctrl->start_ack_pending = FALSE;
 	} else {
 		if (vfe32_ctrl->recording_state ==
@@ -2102,9 +2114,7 @@
 			vfe32_ctrl->vfebase + VFE_REG_UPDATE_CMD);
 		} else if (vfe32_ctrl->recording_state ==
 			VFE_REC_STATE_STOPPED) {
-			v4l2_subdev_notify(vfe32_ctrl->subdev,
-					NOTIFY_ISP_MSG_EVT,
-					(void *)MSG_ID_STOP_REC_ACK);
+			vfe32_send_isp_msg(vfe32_ctrl, MSG_ID_STOP_REC_ACK);
 			vfe32_ctrl->recording_state = VFE_REC_STATE_IDLE;
 		}
 		spin_lock_irqsave(&vfe32_ctrl->update_ack_lock, flags);
@@ -2112,9 +2122,7 @@
 			vfe32_ctrl->update_ack_pending = FALSE;
 			spin_unlock_irqrestore(
 				&vfe32_ctrl->update_ack_lock, flags);
-			v4l2_subdev_notify(vfe32_ctrl->subdev,
-					NOTIFY_ISP_MSG_EVT,
-					(void *)MSG_ID_UPDATE_ACK);
+			vfe32_send_isp_msg(vfe32_ctrl, MSG_ID_UPDATE_ACK);
 		} else {
 			spin_unlock_irqrestore(
 				&vfe32_ctrl->update_ack_lock, flags);
@@ -2195,9 +2203,7 @@
 	if (vfe32_ctrl->stop_ack_pending) {
 		vfe32_ctrl->stop_ack_pending = FALSE;
 		spin_unlock_irqrestore(&vfe32_ctrl->stop_flag_lock, flags);
-		v4l2_subdev_notify(vfe32_ctrl->subdev,
-					NOTIFY_ISP_MSG_EVT,
-					(void *)MSG_ID_STOP_ACK);
+		vfe32_send_isp_msg(vfe32_ctrl, MSG_ID_STOP_ACK);
 	} else {
 		spin_unlock_irqrestore(&vfe32_ctrl->stop_flag_lock, flags);
 		/* this is from reset command. */
@@ -2205,9 +2211,7 @@
 
 		/* reload all write masters. (frame & line)*/
 		msm_io_w(0x7FFF, vfe32_ctrl->vfebase + VFE_BUS_CMD);
-		v4l2_subdev_notify(vfe32_ctrl->subdev,
-					NOTIFY_ISP_MSG_EVT,
-					(void *)MSG_ID_RESET_ACK);
+		vfe32_send_isp_msg(vfe32_ctrl, MSG_ID_RESET_ACK);
 	}
 }
 
@@ -2217,9 +2221,7 @@
 	if (vfe32_ctrl->operation_mode ==
 		VFE_MODE_OF_OPERATION_RAW_SNAPSHOT) {
 		if (vfe32_ctrl->start_ack_pending) {
-			v4l2_subdev_notify(vfe32_ctrl->subdev,
-					NOTIFY_ISP_MSG_EVT,
-					(void *)MSG_ID_START_ACK);
+			vfe32_send_isp_msg(vfe32_ctrl, MSG_ID_START_ACK);
 			vfe32_ctrl->start_ack_pending = FALSE;
 		}
 		vfe32_ctrl->vfe_capture_count--;
@@ -2231,11 +2233,10 @@
 				vfe32_ctrl->vfebase + VFE_CAMIF_COMMAND);
 		}
 	} /* if raw snapshot mode. */
-
-	v4l2_subdev_notify(vfe32_ctrl->subdev,
-				NOTIFY_ISP_MSG_EVT,
-				(void *)MSG_ID_SOF_ACK);
 	vfe32_ctrl->vfeFrameId++;
+	if (vfe32_ctrl->vfeFrameId == 0)
+		vfe32_ctrl->vfeFrameId = 1; /* wrapped back */
+	vfe32_send_isp_msg(vfe32_ctrl, MSG_ID_SOF_ACK);
 	CDBG("camif_sof_irq, frameId = %d\n", vfe32_ctrl->vfeFrameId);
 
 	if (vfe32_ctrl->sync_timer_state) {
@@ -2256,9 +2257,7 @@
 		temp = (uint32_t *)(vfe32_ctrl->vfebase + VFE_CAMIF_STATUS);
 		camifStatus = msm_io_r(temp);
 		pr_err("camifStatus  = 0x%x\n", camifStatus);
-		v4l2_subdev_notify(vfe32_ctrl->subdev,
-				NOTIFY_ISP_MSG_EVT,
-				(void *)MSG_ID_CAMIF_ERROR);
+		vfe32_send_isp_msg(vfe32_ctrl, MSG_ID_CAMIF_ERROR);
 	}
 
 	if (errStatus & VFE32_IMASK_BHIST_OVWR)
@@ -2881,9 +2880,8 @@
 						CAMIF_COMMAND_STOP_IMMEDIATELY,
 						vfe32_ctrl->vfebase +
 						VFE_CAMIF_COMMAND);
-					v4l2_subdev_notify(vfe32_ctrl->subdev,
-							NOTIFY_ISP_MSG_EVT,
-						(void *)MSG_ID_SNAPSHOT_DONE);
+					vfe32_send_isp_msg(vfe32_ctrl,
+						MSG_ID_SNAPSHOT_DONE);
 				}
 			}
 			/* then process stats irq. */
@@ -2930,22 +2928,19 @@
 				if (qcmd->vfeInterruptStatus0 &
 						VFE_IRQ_STATUS0_SYNC_TIMER0) {
 					CDBG("SYNC_TIMER 0 irq occured.\n");
-					v4l2_subdev_notify(vfe32_ctrl->subdev,
-						NOTIFY_ISP_MSG_EVT, (void *)
+					vfe32_send_isp_msg(vfe32_ctrl,
 						MSG_ID_SYNC_TIMER0_DONE);
 				}
 				if (qcmd->vfeInterruptStatus0 &
 						VFE_IRQ_STATUS0_SYNC_TIMER1) {
 					CDBG("SYNC_TIMER 1 irq occured.\n");
-					v4l2_subdev_notify(vfe32_ctrl->subdev,
-						NOTIFY_ISP_MSG_EVT, (void *)
+					vfe32_send_isp_msg(vfe32_ctrl,
 						MSG_ID_SYNC_TIMER1_DONE);
 				}
 				if (qcmd->vfeInterruptStatus0 &
 						VFE_IRQ_STATUS0_SYNC_TIMER2) {
 					CDBG("SYNC_TIMER 2 irq occured.\n");
-					v4l2_subdev_notify(vfe32_ctrl->subdev,
-						NOTIFY_ISP_MSG_EVT, (void *)
+					vfe32_send_isp_msg(vfe32_ctrl,
 						MSG_ID_SYNC_TIMER2_DONE);
 				}
 			}
diff --git a/drivers/mfd/pm8018-core.c b/drivers/mfd/pm8018-core.c
index e60b97b..1567c5b 100644
--- a/drivers/mfd/pm8018-core.c
+++ b/drivers/mfd/pm8018-core.c
@@ -145,6 +145,19 @@
 	.num_resources	= ARRAY_SIZE(gpio_cell_resources),
 };
 
+static const struct resource adc_cell_resources[] __devinitconst = {
+	SINGLE_IRQ_RESOURCE(NULL, PM8018_ADC_EOC_USR_IRQ),
+	SINGLE_IRQ_RESOURCE(NULL, PM8018_ADC_BATT_TEMP_WARM_IRQ),
+	SINGLE_IRQ_RESOURCE(NULL, PM8018_ADC_BATT_TEMP_COLD_IRQ),
+};
+
+static struct mfd_cell adc_cell __devinitdata = {
+	.name		= PM8XXX_ADC_DEV_NAME,
+	.id		= -1,
+	.resources	= adc_cell_resources,
+	.num_resources	= ARRAY_SIZE(adc_cell_resources),
+};
+
 static const struct resource mpp_cell_resources[] __devinitconst = {
 	{
 		.start	= PM8018_IRQ_BLOCK_BIT(PM8018_MPP_BLOCK_START, 0),
@@ -283,6 +296,17 @@
 		}
 	}
 
+	if (pdata->adc_pdata) {
+		adc_cell.platform_data = pdata->adc_pdata;
+		adc_cell.pdata_size = sizeof(struct pm8xxx_adc_platform_data);
+		ret = mfd_add_devices(pmic->dev, 0, &adc_cell, 1, NULL,
+				      irq_base);
+		if (ret) {
+			pr_err("Failed to add adc subdevice ret=%d\n", ret);
+			goto bail;
+		}
+	}
+
 	ret = mfd_add_devices(pmic->dev, 0, &debugfs_cell, 1, NULL, irq_base);
 	if (ret) {
 		pr_err("Failed to add debugfs subdevice ret=%d\n", ret);
diff --git a/drivers/net/ks8851.c b/drivers/net/ks8851.c
index 74b6ac2..e338aed 100644
--- a/drivers/net/ks8851.c
+++ b/drivers/net/ks8851.c
@@ -1723,8 +1723,6 @@
 
 err_id:
 err_irq:
-	free_netdev(ndev);
-
 	if (!IS_ERR(ks->vdd_io)) {
 		regulator_disable(ks->vdd_io);
 		regulator_put(ks->vdd_io);
@@ -1735,6 +1733,8 @@
 		regulator_put(ks->vdd_phy);
 	}
 
+	free_netdev(ndev);
+
 	if (pdata && gpio_is_valid(pdata->rst_gpio))
 		gpio_free(pdata->rst_gpio);
 
diff --git a/drivers/usb/misc/Kconfig b/drivers/usb/misc/Kconfig
index 527dc85..1135806 100644
--- a/drivers/usb/misc/Kconfig
+++ b/drivers/usb/misc/Kconfig
@@ -280,3 +280,28 @@
 
 	  To compile this driver as a module, choose M here: the
 	  module will be called qcaux.  If unsure, choose N.
+
+config USB_QCOM_DUN_BRIDGE
+	tristate "USB Qualcomm modem DUN bridge driver"
+	depends on USB && !USB_SERIAL_QUALCOMM
+	help
+	  Say Y here if you have a Qualcomm modem device connected via USB that
+	  will be bridged in kernel space. This driver will enable bridging
+	  with the gadget serial driver for use in dial-up networking. This is
+	  not the same as the qcserial driver that exposes a TTY interface to
+	  userspace.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called dun_bridge.
+
+config USB_QCOM_DUN_BRIDGE_TEST
+	tristate "USB Qualcomm modem DUN bridge driver test"
+	depends on USB && USB_QCOM_DUN_BRIDGE && !USB_SERIAL_QUALCOMM
+	help
+	  Say Y here if you want to enable the test hook for the
+	  Qualcomm modem bridge driver. When enabled, this will create
+	  a debugfs file entry named "dun_bridge_test" which can be used
+	  to read and write directly to the modem.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called dun_bridge_test.
diff --git a/drivers/usb/misc/Makefile b/drivers/usb/misc/Makefile
index 20ee62a..03568bc 100644
--- a/drivers/usb/misc/Makefile
+++ b/drivers/usb/misc/Makefile
@@ -31,3 +31,5 @@
 
 obj-$(CONFIG_USB_QCOM_DIAG)		+= diag_usb.o
 obj-$(CONFIG_USB_QCOM_DIAG_TEST)	+= diag_bridge_test.o
+obj-$(CONFIG_USB_QCOM_DUN_BRIDGE)	+= dun_bridge.o
+obj-$(CONFIG_USB_QCOM_DUN_BRIDGE_TEST)	+= dun_bridge_test.o
diff --git a/drivers/usb/misc/dun_bridge.c b/drivers/usb/misc/dun_bridge.c
new file mode 100644
index 0000000..aca7714
--- /dev/null
+++ b/drivers/usb/misc/dun_bridge.c
@@ -0,0 +1,520 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/kref.h>
+#include <linux/platform_device.h>
+#include <linux/usb.h>
+#include <linux/usb/cdc.h>
+#include <linux/usb/ch9.h>
+#include <asm/unaligned.h>
+#include <mach/usb_dun_bridge.h>
+
+#define DRIVER_DESC "Qualcomm USB DUN bridge driver"
+#define DRIVER_VERSION "1.0"
+
+struct dun_bridge {
+	struct usb_device	*udev;
+	struct usb_interface	*intf;
+	struct usb_anchor	submitted;
+	u8			int_in_epaddr;
+	unsigned		in, out; /* bulk in/out pipes */
+
+	struct urb		*inturb;
+	struct usb_ctrlrequest	cmd;
+	u8			*ctrl_buf;
+
+	struct kref		kref;
+	struct platform_device	*pdev;
+
+	struct dun_bridge_ops	*ops;
+};
+
+static struct dun_bridge *__dev;
+
+/* This assumes that __dev has already been initialized by probe(). */
+int dun_bridge_open(struct dun_bridge_ops *ops)
+{
+	struct dun_bridge *dev = __dev;
+	int ret = 0;
+
+	if (!dev) {
+		err("%s: dev is null", __func__);
+		return -ENODEV;
+	}
+
+	if (!ops || !ops->read_complete || !ops->write_complete)
+		return -EINVAL;
+
+	dev->ops = ops;
+	if (ops->ctrl_status) {
+		ret = usb_submit_urb(dev->inturb, GFP_KERNEL);
+		if (ret)
+			pr_err("%s: submitting int urb failed: %d\n",
+				__func__, ret);
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL(dun_bridge_open);
+
+int dun_bridge_close(void)
+{
+	struct dun_bridge *dev = __dev;
+	if (!dev)
+		return -ENODEV;
+
+	dev_dbg(&dev->udev->dev, "%s:", __func__);
+	usb_unlink_anchored_urbs(&dev->submitted);
+	usb_unlink_urb(dev->inturb);
+	dev->ops = NULL;
+
+	return 0;
+}
+EXPORT_SYMBOL(dun_bridge_close);
+
+static void read_cb(struct urb *urb)
+{
+	struct dun_bridge *dev = urb->context;
+	struct dun_bridge_ops *ops;
+
+	if (!dev || !dev->intf) {
+		pr_err("%s: device is disconnected\n", __func__);
+		kfree(urb->transfer_buffer);
+		return;
+	}
+
+	dev_dbg(&dev->udev->dev, "%s: status:%d actual:%d\n", __func__,
+			urb->status, urb->actual_length);
+
+	usb_autopm_put_interface(dev->intf);
+	ops = dev->ops;
+	if (ops)
+		ops->read_complete(ops->ctxt,
+				urb->transfer_buffer,
+				urb->transfer_buffer_length,
+				/* callback must check this value for error */
+				urb->status < 0 ?
+					urb->status : urb->actual_length);
+	else {
+		/* can't call back, free buffer on caller's behalf */
+		dev_err(&dev->udev->dev, "cannot complete read callback\n");
+		kfree(urb->transfer_buffer);
+	}
+}
+
+int dun_bridge_read(void *data, int len)
+{
+	struct dun_bridge *dev = __dev;
+	struct urb *urb;
+	int ret;
+
+	if (!dev || !dev->ops)
+		return -ENODEV;
+
+	if (!dev->intf) {
+		pr_err("%s: device is disconnected\n", __func__);
+		return -ENODEV;
+	}
+
+	if (!len) {
+		dev_err(&dev->udev->dev, "%s: invalid len:%d\n", __func__, len);
+		return -EINVAL;
+	}
+
+	urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!urb) {
+		dev_err(&dev->udev->dev, "%s: Unable to alloc urb\n", __func__);
+		return -ENOMEM;
+	}
+
+	usb_fill_bulk_urb(urb, dev->udev, dev->in,
+			data, len, read_cb, dev);
+	usb_anchor_urb(urb, &dev->submitted);
+
+	usb_autopm_get_interface(dev->intf);
+	ret = usb_submit_urb(urb, GFP_KERNEL);
+	if (ret) {
+		dev_err(&dev->udev->dev, "%s: submit urb err:%d\n",
+			__func__, ret);
+		usb_unanchor_urb(urb);
+		usb_autopm_put_interface(dev->intf);
+	}
+
+	usb_free_urb(urb);
+	return ret;
+}
+EXPORT_SYMBOL(dun_bridge_read);
+
+static void write_cb(struct urb *urb)
+{
+	struct dun_bridge *dev = urb->context;
+	struct dun_bridge_ops *ops;
+
+	if (!dev || !dev->intf) {
+		pr_err("%s: device is disconnected\n", __func__);
+		kfree(urb->transfer_buffer);
+		return;
+	}
+
+	dev_dbg(&dev->udev->dev, "%s: status:%d actual:%d\n", __func__,
+			urb->status, urb->actual_length);
+
+	usb_autopm_put_interface(dev->intf);
+	ops = dev->ops;
+	if (ops)
+		ops->write_complete(ops->ctxt,
+				urb->transfer_buffer,
+				urb->transfer_buffer_length,
+				/* callback must check this value for error */
+				urb->status < 0 ?
+					urb->status : urb->actual_length);
+	else {
+		/* can't call back, free buffer on caller's behalf */
+		dev_err(&dev->udev->dev, "cannot complete write callback\n");
+		kfree(urb->transfer_buffer);
+	}
+}
+
+int dun_bridge_write(void *data, int len)
+{
+	struct dun_bridge *dev = __dev;
+	struct urb *urb;
+	int ret;
+
+	if (!dev || !dev->ops)
+		return -ENODEV;
+
+	if (!dev->intf) {
+		pr_err("%s: device is disconnected\n", __func__);
+		return -ENODEV;
+	}
+
+	if (!len) {
+		dev_err(&dev->udev->dev, "%s: invalid len:%d\n", __func__, len);
+		return -EINVAL;
+	}
+
+	urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!urb) {
+		dev_err(&dev->udev->dev, "%s: Unable to alloc urb\n", __func__);
+		return -ENOMEM;
+	}
+
+	usb_fill_bulk_urb(urb, dev->udev, dev->out,
+			data, len, write_cb, dev);
+	usb_anchor_urb(urb, &dev->submitted);
+
+	usb_autopm_get_interface(dev->intf);
+	ret = usb_submit_urb(urb, GFP_KERNEL);
+	if (ret) {
+		dev_err(&dev->udev->dev, "%s: submit urb err:%d\n",
+			__func__, ret);
+		usb_unanchor_urb(urb);
+		usb_autopm_put_interface(dev->intf);
+	}
+
+	usb_free_urb(urb);
+	return ret;
+}
+EXPORT_SYMBOL(dun_bridge_write);
+
+static void ctrl_cb(struct urb *urb)
+{
+	struct dun_bridge *dev = urb->context;
+	usb_autopm_put_interface(dev->intf);
+}
+
+int dun_bridge_send_ctrl_bits(unsigned ctrl_bits)
+{
+	struct dun_bridge *dev = __dev;
+	struct urb *urb = NULL;
+	int ret;
+
+	if (!dev || !dev->intf) {
+		pr_err("%s: device is disconnected\n", __func__);
+		return -ENODEV;
+	}
+
+	dev_dbg(&dev->udev->dev, "%s: %#x", __func__, ctrl_bits);
+
+	dev->cmd.bRequestType = USB_TYPE_CLASS | USB_RECIP_INTERFACE;
+	dev->cmd.bRequest = USB_CDC_REQ_SET_CONTROL_LINE_STATE;
+	dev->cmd.wValue = cpu_to_le16(ctrl_bits);
+	dev->cmd.wIndex = cpu_to_le16(dev->int_in_epaddr);
+	dev->cmd.wLength = 0;
+
+	urb = usb_alloc_urb(0, GFP_ATOMIC);
+	if (!urb) {
+		dev_err(&dev->udev->dev, "%s: Unable to alloc urb\n", __func__);
+		return -ENOMEM;
+	}
+
+	usb_fill_control_urb(urb, dev->udev, usb_sndctrlpipe(dev->udev, 0),
+			     (unsigned char *)&dev->cmd, NULL, 0,
+			     ctrl_cb, dev);
+
+	usb_autopm_get_interface(dev->intf);
+	ret = usb_submit_urb(urb, GFP_ATOMIC);
+	if (ret) {
+		dev_err(&dev->udev->dev, "%s: submit urb err:%d\n",
+			__func__, ret);
+		usb_autopm_put_interface(dev->intf);
+	}
+
+	usb_free_urb(urb);
+	return ret;
+}
+EXPORT_SYMBOL(dun_bridge_send_ctrl_bits);
+
+static void int_cb(struct urb *urb)
+{
+	struct dun_bridge *dev = urb->context;
+	struct usb_cdc_notification *dr = urb->transfer_buffer;
+	unsigned char *data;
+	unsigned int ctrl_bits;
+	int status = urb->status;
+
+	if (!dev || !dev->intf) {
+		pr_err("%s: device is disconnected\n", __func__);
+		return;
+	}
+
+	switch (status) {
+	case 0:
+		/* success */
+		break;
+	case -ECONNRESET:
+	case -ENOENT:
+	case -ESHUTDOWN:
+		/* this urb is terminated, clean up */
+		dev_err(&dev->udev->dev,
+			"%s - urb shutting down with status: %d\n",
+			__func__, status);
+		return;
+	default:
+		dev_err(&dev->udev->dev,
+			"%s - nonzero urb status received: %d\n",
+			__func__, status);
+		goto resubmit_urb;
+	}
+
+	data = (unsigned char *)(dr + 1);
+	switch (dr->bNotificationType) {
+	case USB_CDC_NOTIFY_NETWORK_CONNECTION:
+		dev_dbg(&dev->udev->dev, "%s network\n", dr->wValue ?
+					"connected to" : "disconnected from");
+		break;
+
+	case USB_CDC_NOTIFY_SERIAL_STATE:
+		ctrl_bits = get_unaligned_le16(data);
+		dev_dbg(&dev->udev->dev, "serial state: %d\n", ctrl_bits);
+		if (dev->ops && dev->ops->ctrl_status)
+			dev->ops->ctrl_status(dev->ops->ctxt, ctrl_bits);
+		break;
+
+	default:
+		dev_err(&dev->udev->dev, "unknown notification %d received: "
+			"index %d len %d data0 %d data1 %d\n",
+			dr->bNotificationType, dr->wIndex,
+			dr->wLength, data[0], data[1]);
+		break;
+	}
+resubmit_urb:
+	status = usb_submit_urb(dev->inturb, GFP_ATOMIC);
+	if (status)
+		dev_err(&dev->udev->dev, "%s: submit urb err:%d\n",
+			__func__, status);
+}
+
+static void dun_bridge_delete(struct kref *kref)
+{
+	struct dun_bridge *dev = container_of(kref, struct dun_bridge, kref);
+
+	__dev = NULL;
+	usb_put_dev(dev->udev);
+	usb_free_urb(dev->inturb);
+	kfree(dev->ctrl_buf);
+	kfree(dev);
+}
+
+static int
+dun_bridge_probe(struct usb_interface *intf, const struct usb_device_id *id)
+{
+	struct dun_bridge *dev;
+	struct usb_host_interface *iface_desc;
+	struct usb_endpoint_descriptor *epd;
+	__u8 iface_num;
+	int i;
+	int ctrlsize = 0;
+	int ret = -ENOMEM;
+
+	iface_desc = intf->cur_altsetting;
+	iface_num = iface_desc->desc.bInterfaceNumber;
+
+	/* is this interface supported? */
+	if (iface_num != id->driver_info)
+		return -ENODEV;
+
+	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+	if (!dev) {
+		pr_err("%s: unable to allocate dev\n", __func__);
+		goto error;
+	}
+
+	dev->pdev = platform_device_alloc("dun_bridge", 0);
+	if (!dev->pdev) {
+		pr_err("%s: unable to allocate platform device\n", __func__);
+		kfree(dev);
+		return -ENOMEM;
+	}
+	__dev = dev;
+
+	kref_init(&dev->kref);
+	dev->udev = usb_get_dev(interface_to_usbdev(intf));
+	dev->intf = intf;
+
+	init_usb_anchor(&dev->submitted);
+	dev->inturb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!dev->inturb) {
+		ret = -ENOMEM;
+		goto error;
+	}
+
+	for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
+		epd = &iface_desc->endpoint[i].desc;
+
+		if (usb_endpoint_is_int_in(epd)) {
+			dev->int_in_epaddr = epd->bEndpointAddress;
+			ctrlsize = le16_to_cpu(epd->wMaxPacketSize);
+
+			dev->ctrl_buf = kzalloc(ctrlsize, GFP_KERNEL);
+			if (!dev->ctrl_buf) {
+				ret = -ENOMEM;
+				goto error;
+			}
+
+			usb_fill_int_urb(dev->inturb, dev->udev,
+					 usb_rcvintpipe(dev->udev,
+							dev->int_in_epaddr),
+					 dev->ctrl_buf, ctrlsize,
+					 int_cb, dev, epd->bInterval);
+
+		} else if (usb_endpoint_is_bulk_in(epd))
+			dev->in = usb_rcvbulkpipe(dev->udev,
+						epd->bEndpointAddress &
+						USB_ENDPOINT_NUMBER_MASK);
+
+		else if (usb_endpoint_is_bulk_out(epd))
+			dev->out = usb_sndbulkpipe(dev->udev,
+						epd->bEndpointAddress &
+						USB_ENDPOINT_NUMBER_MASK);
+	}
+
+	if (!dev->int_in_epaddr && !dev->in && !dev->out) {
+		dev_err(&dev->udev->dev, "%s: could not find all endpoints\n",
+					__func__);
+		ret = -ENODEV;
+		goto error;
+	}
+
+	usb_set_intfdata(intf, dev);
+	platform_device_add(dev->pdev);
+	return 0;
+error:
+	if (dev)
+		kref_put(&dev->kref, dun_bridge_delete);
+	return ret;
+}
+
+static void dun_bridge_disconnect(struct usb_interface *intf)
+{
+	struct dun_bridge *dev = usb_get_intfdata(intf);
+
+	platform_device_del(dev->pdev);
+	usb_set_intfdata(intf, NULL);
+	dev->intf = NULL;
+
+	kref_put(&dev->kref, dun_bridge_delete);
+
+	pr_debug("%s: DUN Bridge now disconnected\n", __func__);
+}
+
+static int dun_bridge_suspend(struct usb_interface *intf, pm_message_t message)
+{
+	struct dun_bridge *dev = usb_get_intfdata(intf);
+
+	dev_dbg(&dev->udev->dev, "%s:", __func__);
+	usb_unlink_anchored_urbs(&dev->submitted);
+	usb_unlink_urb(dev->inturb);
+
+	return 0;
+}
+
+static int dun_bridge_resume(struct usb_interface *intf)
+{
+	struct dun_bridge *dev = usb_get_intfdata(intf);
+	int ret = 0;
+
+	if (dev->ops && dev->ops->ctrl_status) {
+		ret = usb_submit_urb(dev->inturb, GFP_KERNEL);
+		if (ret)
+			dev_err(&dev->udev->dev, "%s: submit int urb err: %d\n",
+				__func__, ret);
+	}
+
+	return ret;
+}
+
+#define VALID_INTERFACE_NUM	2
+static const struct usb_device_id id_table[] = {
+	{ USB_DEVICE(0x05c6, 0x9001),	/* Generic QC Modem device */
+	.driver_info = VALID_INTERFACE_NUM },
+	{ }				/* Terminating entry */
+};
+MODULE_DEVICE_TABLE(usb, id_table);
+
+static struct usb_driver dun_bridge_driver = {
+	.name			= "dun_usb_bridge",
+	.probe			= dun_bridge_probe,
+	.disconnect		= dun_bridge_disconnect,
+	.id_table		= id_table,
+	.suspend		= dun_bridge_suspend,
+	.resume			= dun_bridge_resume,
+	.supports_autosuspend	= true,
+};
+
+static int __init dun_bridge_init(void)
+{
+	int ret;
+
+	ret = usb_register(&dun_bridge_driver);
+	if (ret)
+		pr_err("%s: unable to register dun_bridge_driver\n", __func__);
+
+	return ret;
+}
+
+static void __exit dun_bridge_exit(void)
+{
+	usb_deregister(&dun_bridge_driver);
+}
+
+module_init(dun_bridge_init);
+module_exit(dun_bridge_exit);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL V2");
diff --git a/drivers/usb/misc/dun_bridge_test.c b/drivers/usb/misc/dun_bridge_test.c
new file mode 100644
index 0000000..d545e13
--- /dev/null
+++ b/drivers/usb/misc/dun_bridge_test.c
@@ -0,0 +1,201 @@
+/*
+ * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/debugfs.h>
+#include <linux/usb/cdc.h>
+#include <linux/uaccess.h>
+#include <mach/usb_dun_bridge.h>
+
+#define RD_BUF_SIZE		2048
+#define DUN_TEST_CONNECTED	0
+
+
+struct dun_bridge_test_dev {
+	char *read_buf;
+	size_t buflen;
+	struct work_struct read_w;
+	unsigned long	flags;
+
+	struct dun_bridge_ops	ops;
+};
+static struct dun_bridge_test_dev *__dev;
+
+static struct dentry *dfile;
+
+static void
+dun_bridge_test_read_complete(void *d, char *buf, size_t size, size_t actual)
+{
+	if (actual < 0) {
+		pr_err("%s: read complete err\n", __func__);
+		return;
+	}
+
+	__dev->buflen = actual;
+	buf[actual] = 0;
+
+	pr_info("%s: %s\n", __func__, buf);
+
+	if (test_bit(DUN_TEST_CONNECTED, &__dev->flags))
+		schedule_work(&__dev->read_w);
+}
+
+static void dun_bridge_test_read_work(struct work_struct *w)
+{
+	struct dun_bridge_test_dev *dev =
+		container_of(w, struct dun_bridge_test_dev, read_w);
+
+	dun_bridge_read(dev->read_buf, RD_BUF_SIZE);
+}
+
+static void
+dun_bridge_test_write_complete(void *d, char *buf, size_t size, size_t actual)
+{
+	struct dun_bridge_test_dev *dev = d;
+
+	if (actual > 0)
+		schedule_work(&dev->read_w);
+
+	kfree(buf);
+}
+
+#if defined(CONFIG_DEBUG_FS)
+#define DEBUG_BUF_SIZE	1024
+
+#define ACM_CTRL_DTR		0x01
+#define ACM_CTRL_RTS		0x02
+
+static int debug_open(struct inode *inode, struct file *file)
+{
+	struct dun_bridge_test_dev *dev = __dev;
+	int ret = 0;
+
+	if (!dev)
+		return -ENODEV;
+
+	if (!test_bit(DUN_TEST_CONNECTED, &dev->flags)) {
+		ret = dun_bridge_open(&dev->ops);
+		if (ret)
+			return ret;
+		set_bit(DUN_TEST_CONNECTED, &dev->flags);
+		dun_bridge_send_ctrl_bits(ACM_CTRL_DTR | ACM_CTRL_RTS);
+	}
+
+	return ret;
+}
+
+static ssize_t debug_read(struct file *file, char __user *ubuf,
+				 size_t count, loff_t *ppos)
+{
+	struct dun_bridge_test_dev	*dev = __dev;
+	return simple_read_from_buffer(ubuf, count, ppos,
+			dev->read_buf, dev->buflen);
+}
+
+static ssize_t debug_write(struct file *file, const char __user *ubuf,
+				 size_t count, loff_t *ppos)
+{
+	struct dun_bridge_test_dev *dev = __dev;
+	unsigned char *buf;
+	int ret;
+
+	if (!dev)
+		return -ENODEV;
+
+	buf = kmalloc(count, GFP_KERNEL);
+	if (!buf) {
+		pr_err("%s: unable to allocate mem for writing\n", __func__);
+		return -ENOMEM;
+	}
+
+	if (!copy_from_user(buf, ubuf, count)) {
+		ret = dun_bridge_write(buf, count);
+		if (ret < 0) {
+			pr_err("%s: error writing to dun_bridge\n", __func__);
+			kfree(buf);
+			return ret;
+		}
+	} else {
+		pr_err("%s: error copying for writing\n", __func__);
+		kfree(buf);
+	}
+
+	return count;
+}
+
+const struct file_operations dun_bridge_test_debug_ops = {
+	.open = debug_open,
+	.read = debug_read,
+	.write = debug_write,
+};
+
+static void dun_bridge_test_debug_init(void)
+{
+	dfile = debugfs_create_file("dun_bridge_test", 0555, NULL,
+			NULL, &dun_bridge_test_debug_ops);
+}
+#else
+static void dun_bridge_test_debug_init(void) { }
+#endif
+
+static int __init dun_bridge_test_init(void)
+{
+	struct dun_bridge_test_dev	*dev;
+
+	pr_info("%s\n", __func__);
+
+	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+	if (!dev)
+		return -ENOMEM;
+
+	__dev = dev;
+
+	dev->ops.read_complete = dun_bridge_test_read_complete;
+	dev->ops.write_complete = dun_bridge_test_write_complete;
+	dev->read_buf = kmalloc(RD_BUF_SIZE, GFP_KERNEL);
+	if (!dev->read_buf) {
+		pr_err("%s: unable to allocate read buffer\n", __func__);
+		kfree(dev);
+		return -ENOMEM;
+	}
+
+	dev->ops.ctxt = dev;
+	INIT_WORK(&dev->read_w, dun_bridge_test_read_work);
+
+	dun_bridge_test_debug_init();
+
+	return 0;
+}
+
+static void __exit dun_bridge_test_exit(void)
+{
+	struct dun_bridge_test_dev *dev = __dev;
+
+	pr_info("%s:\n", __func__);
+
+	if (test_bit(DUN_TEST_CONNECTED, &dev->flags))
+		dun_bridge_close();
+
+	debugfs_remove(dfile);
+
+	kfree(dev->read_buf);
+	kfree(dev);
+}
+
+module_init(dun_bridge_test_init);
+module_exit(dun_bridge_test_exit);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL V2");
diff --git a/drivers/video/msm/Kconfig b/drivers/video/msm/Kconfig
index 7fd603d..c33919d 100644
--- a/drivers/video/msm/Kconfig
+++ b/drivers/video/msm/Kconfig
@@ -602,6 +602,14 @@
 	  Support for HDCP mode for MSM HDMI 1080p Panel
 	  Choose to enable HDCP
 
+config FB_MSM_HDMI_MSM_PANEL_CEC_SUPPORT
+	depends on FB_MSM_HDMI_MSM_PANEL
+	bool "Enable CEC"
+	default n
+	---help---
+	  Support for HDMI CEC Feature
+	  Choose to enable CEC
+
 choice
 	depends on  (FB_MSM_MDP22 || FB_MSM_MDP31 || FB_MSM_MDP40)
 	prompt "TVOut Region"
diff --git a/drivers/video/msm/external_common.c b/drivers/video/msm/external_common.c
index a499a62..1d87de6 100644
--- a/drivers/video/msm/external_common.c
+++ b/drivers/video/msm/external_common.c
@@ -18,7 +18,9 @@
 /* #define DEBUG */
 #define DEV_DBG_PREFIX "EXT_COMMON: "
 
+/* #define CEC_COMPLIANCE_TESTING */
 #include "msm_fb.h"
+#include "hdmi_msm.h"
 #include "external_common.h"
 
 struct external_common_state_type *external_common_state;
@@ -26,6 +28,7 @@
 DEFINE_MUTEX(external_common_state_hpd_mutex);
 EXPORT_SYMBOL(external_common_state_hpd_mutex);
 
+
 static int atoi(const char *name)
 {
 	int val = 0;
@@ -307,6 +310,132 @@
 	return ret;
 }
 
+#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_CEC_SUPPORT
+/*
+ * This interface for CEC feature is defined to suit
+ * the current requirements. However, the actual functionality is
+ * added to accommodate different interfaces
+ */
+static ssize_t hdmi_msm_rda_cec(struct device *dev,
+				 struct device_attribute *attr, char *buf)
+{
+	/* 0x028C CEC_CTRL */
+	ssize_t ret = snprintf(buf, PAGE_SIZE, "%d\n",
+		(HDMI_INP(0x028C) & BIT(0)));
+	return ret;
+}
+
+static ssize_t hdmi_msm_wta_cec(struct device *dev,
+	struct device_attribute *attr, const char *buf, size_t count)
+{
+	ssize_t ret = strnlen(buf, PAGE_SIZE);
+	int cec = atoi(buf);
+
+	if (cec != 0) {
+		mutex_lock(&hdmi_msm_state_mutex);
+		hdmi_msm_state->cec_enabled = true;
+		hdmi_msm_state->cec_logical_addr = 4;
+		mutex_unlock(&hdmi_msm_state_mutex);
+		hdmi_msm_cec_init();
+		hdmi_msm_cec_write_logical_addr(
+			hdmi_msm_state->cec_logical_addr);
+		DEV_DBG("CEC enabled\n");
+	} else {
+		mutex_lock(&hdmi_msm_state_mutex);
+		hdmi_msm_state->cec_enabled = false;
+		hdmi_msm_state->cec_logical_addr = 15;
+		mutex_unlock(&hdmi_msm_state_mutex);
+		hdmi_msm_cec_write_logical_addr(
+			hdmi_msm_state->cec_logical_addr);
+		/* 0x028C CEC_CTRL */
+		HDMI_OUTP(0x028C, 0);
+		DEV_DBG("CEC disabled\n");
+	}
+	return ret;
+}
+
+static ssize_t hdmi_msm_rda_cec_logical_addr(struct device *dev,
+				 struct device_attribute *attr, char *buf)
+{
+	ssize_t ret;
+
+	mutex_lock(&hdmi_msm_state_mutex);
+	ret = snprintf(buf, PAGE_SIZE, "%d\n",
+		hdmi_msm_state->cec_logical_addr);
+	mutex_unlock(&hdmi_msm_state_mutex);
+	return ret;
+}
+
+static ssize_t hdmi_msm_wta_cec_logical_addr(struct device *dev,
+	struct device_attribute *attr, const char *buf, size_t count)
+{
+
+#ifdef CEC_COMPLIANCE_TESTING
+	/*
+	 * Only for testing
+	 */
+	hdmi_msm_cec_one_touch_play();
+	return 0;
+#else
+	ssize_t ret = strnlen(buf, PAGE_SIZE);
+	int logical_addr = atoi(buf);
+
+	if (logical_addr < 0 || logical_addr > 15)
+		return -EINVAL;
+
+	mutex_lock(&hdmi_msm_state_mutex);
+	hdmi_msm_state->cec_logical_addr = logical_addr;
+	mutex_unlock(&hdmi_msm_state_mutex);
+
+	hdmi_msm_cec_write_logical_addr(logical_addr);
+
+	return ret;
+#endif
+}
+
+static ssize_t hdmi_msm_rda_cec_frame(struct device *dev,
+				 struct device_attribute *attr, char *buf)
+{
+	mutex_lock(&hdmi_msm_state_mutex);
+	if (hdmi_msm_state->cec_queue_rd == hdmi_msm_state->cec_queue_wr
+	    && !hdmi_msm_state->cec_queue_full) {
+		mutex_unlock(&hdmi_msm_state_mutex);
+		DEV_ERR("CEC message queue is empty\n");
+		return -EBUSY;
+	}
+	memcpy(buf, hdmi_msm_state->cec_queue_rd++,
+		sizeof(struct hdmi_msm_cec_msg));
+	hdmi_msm_state->cec_queue_full = false;
+	if (hdmi_msm_state->cec_queue_rd == CEC_QUEUE_END)
+		hdmi_msm_state->cec_queue_rd = hdmi_msm_state->cec_queue_start;
+	mutex_unlock(&hdmi_msm_state_mutex);
+
+	return sizeof(struct hdmi_msm_cec_msg);
+}
+
+static ssize_t hdmi_msm_wta_cec_frame(struct device *dev,
+	struct device_attribute *attr, const char *buf, size_t count)
+{
+	int retry = ((struct hdmi_msm_cec_msg *) buf)->retransmit;
+
+	if (retry > 15)
+		retry = 15;
+	while (1) {
+		hdmi_msm_cec_msg_send((struct hdmi_msm_cec_msg *) buf);
+		if (hdmi_msm_state->cec_frame_wr_status
+		    & CEC_STATUS_WR_ERROR && retry--)
+			msleep(360);
+		else
+			break;
+	}
+
+	if (hdmi_msm_state->cec_frame_wr_status & CEC_STATUS_WR_DONE)
+		return sizeof(struct hdmi_msm_cec_msg);
+	else
+		return -EINVAL;
+}
+#endif /* CONFIG_FB_MSM_HDMI_MSM_PANEL_CEC_SUPPORT */
+
 static ssize_t hdmi_common_rda_3d_present(struct device *dev,
 	struct device_attribute *attr, char *buf)
 {
@@ -364,6 +493,23 @@
 }
 #endif
 
+#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_CEC_SUPPORT
+static DEVICE_ATTR(cec, S_IRUGO | S_IWUSR,
+	hdmi_msm_rda_cec,
+	hdmi_msm_wta_cec);
+
+static DEVICE_ATTR(cec_logical_addr, S_IRUGO | S_IWUSR,
+	hdmi_msm_rda_cec_logical_addr,
+	hdmi_msm_wta_cec_logical_addr);
+
+static DEVICE_ATTR(cec_rd_frame, S_IRUGO,
+	hdmi_msm_rda_cec_frame,	NULL);
+
+static DEVICE_ATTR(cec_wr_frame, S_IWUSR,
+	NULL, hdmi_msm_wta_cec_frame);
+#endif /* CONFIG_FB_MSM_HDMI_MSM_PANEL_CEC_SUPPORT */
+
+
 static ssize_t external_common_rda_video_mode(struct device *dev,
 	struct device_attribute *attr, char *buf)
 {
@@ -456,6 +602,12 @@
 #ifdef CONFIG_FB_MSM_HDMI_3D
 	&dev_attr_format_3d.attr,
 #endif
+#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_CEC_SUPPORT
+	&dev_attr_cec.attr,
+	&dev_attr_cec_logical_addr.attr,
+	&dev_attr_cec_rd_frame.attr,
+	&dev_attr_cec_wr_frame.attr,
+#endif /* CONFIG_FB_MSM_HDMI_MSM_PANEL_CEC_SUPPORT */
 	NULL,
 };
 static struct attribute_group external_common_fs_attr_group = {
diff --git a/drivers/video/msm/external_common.h b/drivers/video/msm/external_common.h
index 30a8f48..f629d0f 100644
--- a/drivers/video/msm/external_common.h
+++ b/drivers/video/msm/external_common.h
@@ -225,6 +225,7 @@
 /* The external interface driver needs to initialize the common state. */
 extern struct external_common_state_type *external_common_state;
 extern struct mutex external_common_state_hpd_mutex;
+extern struct mutex hdmi_msm_state_mutex;
 
 #ifdef CONFIG_FB_MSM_HDMI_COMMON
 #define VFRMT_NOT_SUPPORTED(VFRMT) \
diff --git a/drivers/video/msm/hdmi_msm.c b/drivers/video/msm/hdmi_msm.c
index 7eca334..63c2147 100644
--- a/drivers/video/msm/hdmi_msm.c
+++ b/drivers/video/msm/hdmi_msm.c
@@ -15,6 +15,9 @@
 #define DEV_DBG_PREFIX "HDMI: "
 /* #define REG_DUMP */
 
+#define CEC_MSG_PRINT
+/* #define CEC_COMPLIANCE_TESTING */
+
 #include <linux/types.h>
 #include <linux/bitops.h>
 #include <linux/clk.h>
@@ -49,7 +52,8 @@
 struct workqueue_struct *hdmi_work_queue;
 struct hdmi_msm_state_type *hdmi_msm_state;
 
-static DEFINE_MUTEX(hdmi_msm_state_mutex);
+DEFINE_MUTEX(hdmi_msm_state_mutex);
+EXPORT_SYMBOL(hdmi_msm_state_mutex);
 static DEFINE_MUTEX(hdcp_auth_state_mutex);
 
 #ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT
@@ -58,6 +62,478 @@
 static inline void hdmi_msm_hdcp_enable(void) {}
 #endif
 
+static void hdmi_msm_turn_on(void);
+static int hdmi_msm_audio_off(void);
+static int hdmi_msm_read_edid(void);
+static void hdmi_msm_hpd_off(void);
+
+#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_CEC_SUPPORT
+
+#define HDMI_MSM_CEC_REFTIMER_REFTIMER_ENABLE	BIT(16)
+#define HDMI_MSM_CEC_REFTIMER_REFTIMER(___t)	(((___t)&0xFFFF) << 0)
+
+#define HDMI_MSM_CEC_TIME_SIGNAL_FREE_TIME(___t)	(((___t)&0x1FF) << 7)
+#define HDMI_MSM_CEC_TIME_ENABLE			BIT(0)
+
+#define HDMI_MSM_CEC_ADDR_LOGICAL_ADDR(___la)	(((___la)&0xFF) << 0)
+
+#define HDMI_MSM_CEC_CTRL_LINE_OE			BIT(9)
+#define HDMI_MSM_CEC_CTRL_FRAME_SIZE(___sz)		(((___sz)&0x1F) << 4)
+#define HDMI_MSM_CEC_CTRL_SOFT_RESET		BIT(2)
+#define HDMI_MSM_CEC_CTRL_SEND_TRIG			BIT(1)
+#define HDMI_MSM_CEC_CTRL_ENABLE			BIT(0)
+
+#define HDMI_MSM_CEC_INT_FRAME_RD_DONE_MASK		BIT(7)
+#define HDMI_MSM_CEC_INT_FRAME_RD_DONE_ACK		BIT(6)
+#define HDMI_MSM_CEC_INT_FRAME_RD_DONE_INT		BIT(6)
+#define HDMI_MSM_CEC_INT_MONITOR_MASK		BIT(5)
+#define HDMI_MSM_CEC_INT_MONITOR_ACK		BIT(4)
+#define HDMI_MSM_CEC_INT_MONITOR_INT		BIT(4)
+#define HDMI_MSM_CEC_INT_FRAME_ERROR_MASK		BIT(3)
+#define HDMI_MSM_CEC_INT_FRAME_ERROR_ACK		BIT(2)
+#define HDMI_MSM_CEC_INT_FRAME_ERROR_INT		BIT(2)
+#define HDMI_MSM_CEC_INT_FRAME_WR_DONE_MASK		BIT(1)
+#define HDMI_MSM_CEC_INT_FRAME_WR_DONE_ACK		BIT(0)
+#define HDMI_MSM_CEC_INT_FRAME_WR_DONE_INT		BIT(0)
+
+#define HDMI_MSM_CEC_FRAME_WR_SUCCESS(___st)         (((___st)&0xF) ==\
+		(HDMI_MSM_CEC_INT_FRAME_WR_DONE_INT &&\
+			HDMI_MSM_CEC_INT_FRAME_WR_DONE_MASK &&\
+			(HDMI_MSM_CEC_INT_FRAME_ERROR_MASK &&\
+				!(HDMI_MSM_CEC_INT_FRAME_ERROR_INT))))
+
+#define HDMI_MSM_CEC_RETRANSMIT_NUM(___num)		(((___num)&0xF) << 4)
+#define HDMI_MSM_CEC_RETRANSMIT_ENABLE		BIT(0)
+
+#define HDMI_MSM_CEC_WR_DATA_DATA(___d)		(((___d)&0xFF) << 8)
+
+
+void hdmi_msm_cec_init(void)
+{
+	/* 0x02A8 CEC_REFTIMER */
+	HDMI_OUTP(0x02A8,
+		HDMI_MSM_CEC_REFTIMER_REFTIMER_ENABLE
+		| HDMI_MSM_CEC_REFTIMER_REFTIMER(27 * 50)
+		);
+
+	/* 0x02A4 CEC_TIME */
+	HDMI_OUTP(0x02A4,
+		HDMI_MSM_CEC_TIME_SIGNAL_FREE_TIME(350)
+		| HDMI_MSM_CEC_TIME_ENABLE
+		);
+
+	/*
+	 * 0x02A0 CEC_ADDR
+	 * Starting with a default address of 4
+	 */
+	HDMI_OUTP(0x02A0, HDMI_MSM_CEC_ADDR_LOGICAL_ADDR(4));
+
+	/* 0x028C CEC_CTRL */
+	HDMI_OUTP(0x028C, HDMI_MSM_CEC_CTRL_ENABLE);
+
+	/* 0x029C CEC_INT */
+	/* Enable CEC interrupts */
+	HDMI_OUTP(0x029C,					\
+		  HDMI_MSM_CEC_INT_FRAME_WR_DONE_MASK		\
+		  | HDMI_MSM_CEC_INT_FRAME_ERROR_MASK		\
+		  | HDMI_MSM_CEC_INT_MONITOR_MASK		\
+		  | HDMI_MSM_CEC_INT_FRAME_RD_DONE_MASK);
+
+	HDMI_OUTP(0x02B0, 0x7FF << 4 | 1);
+
+	/*
+	 * Slight adjustment to logic 1 low periods on read,
+	 * CEC Test 8.2-3 was failing, 8 for the
+	 * BIT_1_ERR_RANGE_HI = 8 => 750us, the test used 775us,
+	 * so increased this to 9 which => 800us.
+	 */
+	HDMI_OUTP(0x02E0, 0x889788);
+
+	/*
+	 * Slight adjustment to logic 0 low period on write
+	 */
+	HDMI_OUTP(0x02DC, 0x8888A888);
+
+	/*
+	 * Enable Signal Free Time counter and set to 7 bit periods
+	 */
+	HDMI_OUTP(0x02A4, 0x1 | (7 * 0x30) << 7);
+
+}
+
+void hdmi_msm_cec_write_logical_addr(int addr)
+{
+	/* 0x02A0 CEC_ADDR
+	 *   LOGICAL_ADDR       7:0  NUM
+	 */
+	HDMI_OUTP(0x02A0, addr & 0xFF);
+}
+
+void hdmi_msm_dump_cec_msg(struct hdmi_msm_cec_msg *msg)
+{
+#ifdef CEC_MSG_PRINT
+	int i;
+	DEV_DBG("sender_id     : %d", msg->sender_id);
+	DEV_DBG("recvr_id     : %d", msg->recvr_id);
+	if (msg->frame_size < 2) {
+		DEV_DBG("polling message");
+		return;
+	}
+	DEV_DBG("opcode      : %02x", msg->opcode);
+	for (i = 0; i < msg->frame_size - 2; i++)
+		DEV_DBG("operand(%2d) : %02x", i + 1, msg->operand[i]);
+#endif /* CEC_MSG_PRINT */
+}
+
+void hdmi_msm_cec_msg_send(struct hdmi_msm_cec_msg *msg)
+{
+	int i;
+	uint32 timeout_count = 1;
+	int retry = 10;
+
+	boolean frameType = (msg->recvr_id == 15 ? BIT(0) : 0);
+
+	INIT_COMPLETION(hdmi_msm_state->cec_frame_wr_done);
+	hdmi_msm_state->cec_frame_wr_status = 0;
+
+	/* 0x0294 HDMI_MSM_CEC_RETRANSMIT */
+	HDMI_OUTP(0x0294,
+		HDMI_MSM_CEC_RETRANSMIT_NUM(msg->retransmit)
+		| (msg->retransmit > 0) ? HDMI_MSM_CEC_RETRANSMIT_ENABLE : 0);
+
+	/* 0x028C CEC_CTRL */
+	HDMI_OUTP(0x028C, 0x1 | msg->frame_size << 4);
+
+	/* 0x0290 CEC_WR_DATA */
+
+	/* header block */
+	HDMI_OUTP(0x0290,
+		HDMI_MSM_CEC_WR_DATA_DATA(msg->sender_id << 4 | msg->recvr_id)
+		| frameType);
+
+	/* data block 0 : opcode */
+	HDMI_OUTP(0x0290,
+		HDMI_MSM_CEC_WR_DATA_DATA(msg->frame_size < 2 ? 0 : msg->opcode)
+		| frameType);
+
+	/* data block 1-14 : operand 0-13 */
+	for (i = 0; i < msg->frame_size - 1; i++)
+		HDMI_OUTP(0x0290,
+			HDMI_MSM_CEC_WR_DATA_DATA(msg->operand[i])
+			| (msg->recvr_id == 15 ? BIT(0) : 0));
+
+	for (; i < 14; i++)
+		HDMI_OUTP(0x0290,
+			HDMI_MSM_CEC_WR_DATA_DATA(0)
+			| (msg->recvr_id == 15 ? BIT(0) : 0));
+
+	while ((HDMI_INP(0x0298) & 1) && retry--) {
+		DEV_DBG("CEC line is busy(%d)\n", retry);
+		schedule();
+	}
+
+	/* 0x028C CEC_CTRL */
+	HDMI_OUTP(0x028C,
+		  HDMI_MSM_CEC_CTRL_LINE_OE
+		  | HDMI_MSM_CEC_CTRL_FRAME_SIZE(msg->frame_size)
+		  | HDMI_MSM_CEC_CTRL_SEND_TRIG
+		  | HDMI_MSM_CEC_CTRL_ENABLE);
+
+	timeout_count = wait_for_completion_interruptible_timeout(
+		&hdmi_msm_state->cec_frame_wr_done, HZ);
+
+	if (!timeout_count) {
+		hdmi_msm_state->cec_frame_wr_status |= CEC_STATUS_WR_TMOUT;
+		DEV_ERR("%s: timedout", __func__);
+		hdmi_msm_dump_cec_msg(msg);
+	} else {
+		DEV_DBG("CEC write frame done (frame len=%d)",
+			msg->frame_size);
+		hdmi_msm_dump_cec_msg(msg);
+	}
+}
+
+void hdmi_msm_cec_msg_recv(void)
+{
+	uint32 data;
+	int i;
+#ifdef CEC_COMPLIANCE_TESTING
+	struct hdmi_msm_cec_msg temp_msg;
+#endif
+	mutex_lock(&hdmi_msm_state_mutex);
+	if (hdmi_msm_state->cec_queue_wr == hdmi_msm_state->cec_queue_rd
+		&& hdmi_msm_state->cec_queue_full) {
+		mutex_unlock(&hdmi_msm_state_mutex);
+		DEV_ERR("CEC message queue is overflowing\n");
+#ifdef CEC_COMPLIANCE_TESTING
+		/*
+		 * Without CEC daemon:
+		 * Compliance tests fail once the queue gets filled up.
+		 * so reset the pointers to the start of the queue.
+		 */
+		hdmi_msm_state->cec_queue_wr = hdmi_msm_state->cec_queue_start;
+		hdmi_msm_state->cec_queue_rd = hdmi_msm_state->cec_queue_start;
+		hdmi_msm_state->cec_queue_full = false;
+#else
+		return;
+#endif
+	}
+	if (hdmi_msm_state->cec_queue_wr == NULL) {
+		DEV_ERR("%s: wp is NULL\n", __func__);
+		return;
+	}
+	mutex_unlock(&hdmi_msm_state_mutex);
+
+	/* 0x02AC CEC_RD_DATA */
+	data = HDMI_INP(0x02AC);
+
+	hdmi_msm_state->cec_queue_wr->sender_id = (data & 0xF0) >> 4;
+	hdmi_msm_state->cec_queue_wr->recvr_id = (data & 0x0F);
+	hdmi_msm_state->cec_queue_wr->frame_size = (data & 0x1F00) >> 8;
+	DEV_DBG("Recvd init=[%u] dest=[%u] size=[%u]\n",
+		hdmi_msm_state->cec_queue_wr->sender_id,
+		hdmi_msm_state->cec_queue_wr->recvr_id,
+		hdmi_msm_state->cec_queue_wr->frame_size);
+
+	if (hdmi_msm_state->cec_queue_wr->frame_size < 1) {
+		DEV_ERR("%s: invalid message (frame length = %d)",
+			__func__, hdmi_msm_state->cec_queue_wr->frame_size);
+		return;
+	} else if (hdmi_msm_state->cec_queue_wr->frame_size == 1) {
+		DEV_DBG("%s: polling message (dest[%x] <- init[%x])",
+			__func__,
+			hdmi_msm_state->cec_queue_wr->recvr_id,
+			hdmi_msm_state->cec_queue_wr->sender_id);
+		return;
+	}
+
+	/* data block 0 : opcode */
+	data = HDMI_INP(0x02AC);
+	hdmi_msm_state->cec_queue_wr->opcode = data & 0xFF;
+
+	/* data block 1-14 : operand 0-13 */
+	for (i = 0; i < hdmi_msm_state->cec_queue_wr->frame_size - 2; i++) {
+		data = HDMI_INP(0x02AC);
+		hdmi_msm_state->cec_queue_wr->operand[i] = data & 0xFF;
+	}
+
+	for (; i < 14; i++)
+		hdmi_msm_state->cec_queue_wr->operand[i] = 0;
+
+	DEV_DBG("CEC read frame done\n");
+	DEV_DBG("=======================================\n");
+	hdmi_msm_dump_cec_msg(hdmi_msm_state->cec_queue_wr);
+	DEV_DBG("=======================================\n");
+
+#ifdef CEC_COMPLIANCE_TESTING
+	switch (hdmi_msm_state->cec_queue_wr->opcode) {
+	case 0x64:
+		/* Set OSD String */
+		DEV_INFO("Recvd OSD Str=[%x]\n",\
+			hdmi_msm_state->cec_queue_wr->operand[3]);
+		break;
+	case 0x83:
+		/* Give Phy Addr */
+		DEV_INFO("Recvd a Give Phy Addr cmd\n");
+		memset(&temp_msg, 0x00, sizeof(struct hdmi_msm_cec_msg));
+		/* Setup a frame for sending out phy addr */
+		temp_msg.sender_id = 0x4;
+
+		/* Broadcast */
+		temp_msg.recvr_id = 0xf;
+		temp_msg.opcode = 0x84;
+		i = 0;
+		temp_msg.operand[i++] = 0x10;
+		temp_msg.operand[i++] = 0x00;
+		temp_msg.operand[i++] = 0x04;
+		temp_msg.frame_size = i + 2;
+		hdmi_msm_cec_msg_send(&temp_msg);
+		break;
+	case 0xFF:
+		/* Abort */
+		DEV_INFO("Recvd an abort cmd 0xFF\n");
+		memset(&temp_msg, 0x00, sizeof(struct hdmi_msm_cec_msg));
+		temp_msg.sender_id = 0x4;
+		temp_msg.recvr_id = hdmi_msm_state->cec_queue_wr->sender_id;
+		i = 0;
+
+		/*feature abort */
+		temp_msg.opcode = 0x00;
+		temp_msg.operand[i++] =
+			hdmi_msm_state->cec_queue_wr->opcode;
+
+		/*reason for abort = "Refused" */
+		temp_msg.operand[i++] = 0x04;
+		temp_msg.frame_size = i + 2;
+		hdmi_msm_dump_cec_msg(&temp_msg);
+		hdmi_msm_cec_msg_send(&temp_msg);
+		break;
+	case 0x046:
+		/* Give OSD name */
+		DEV_INFO("Recvd cmd 0x046\n");
+		memset(&temp_msg, 0x00, sizeof(struct hdmi_msm_cec_msg));
+		temp_msg.sender_id = 0x4;
+		temp_msg.recvr_id = hdmi_msm_state->cec_queue_wr->sender_id;
+		i = 0;
+
+		/* OSD Name */
+		temp_msg.opcode = 0x47;
+
+		/* Display control byte */
+		temp_msg.operand[i++] = 0x00;
+		temp_msg.operand[i++] = 'H';
+		temp_msg.operand[i++] = 'e';
+		temp_msg.operand[i++] = 'l';
+		temp_msg.operand[i++] = 'l';
+		temp_msg.operand[i++] = 'o';
+		temp_msg.operand[i++] = ' ';
+		temp_msg.operand[i++] = 'W';
+		temp_msg.operand[i++] = 'o';
+		temp_msg.operand[i++] = 'r';
+		temp_msg.operand[i++] = 'l';
+		temp_msg.operand[i++] = 'd';
+		temp_msg.frame_size = i + 2;
+		hdmi_msm_cec_msg_send(&temp_msg);
+		break;
+	case 0x08F:
+		/* Give Device Power status */
+		DEV_INFO("Recvd a Power status message\n");
+		memset(&temp_msg, 0x00, sizeof(struct hdmi_msm_cec_msg));
+		temp_msg.sender_id = 0x4;
+		temp_msg.recvr_id = hdmi_msm_state->cec_queue_wr->sender_id;
+		i = 0;
+
+		/* OSD String */
+		temp_msg.opcode = 0x90;
+		temp_msg.operand[i++] = 'H';
+		temp_msg.operand[i++] = 'e';
+		temp_msg.operand[i++] = 'l';
+		temp_msg.operand[i++] = 'l';
+		temp_msg.operand[i++] = 'o';
+		temp_msg.operand[i++] = ' ';
+		temp_msg.operand[i++] = 'W';
+		temp_msg.operand[i++] = 'o';
+		temp_msg.operand[i++] = 'r';
+		temp_msg.operand[i++] = 'l';
+		temp_msg.operand[i++] = 'd';
+		temp_msg.frame_size = i + 2;
+		hdmi_msm_cec_msg_send(&temp_msg);
+		break;
+	case 0x080:
+		/* Routing Change cmd */
+	case 0x086:
+		/* Set Stream Path */
+		DEV_INFO("Recvd Set Stream\n");
+		memset(&temp_msg, 0x00, sizeof(struct hdmi_msm_cec_msg));
+		temp_msg.sender_id = 0x4;
+
+		/*Broadcast this message*/
+		temp_msg.recvr_id = 0xf;
+		i = 0;
+		temp_msg.opcode = 0x82; /* Active Source */
+		temp_msg.operand[i++] = 0x10;
+		temp_msg.operand[i++] = 0x00;
+		temp_msg.frame_size = i + 2;
+		hdmi_msm_cec_msg_send(&temp_msg);
+
+		/*
+		 * sending <Image View On> message
+		 */
+		memset(&temp_msg, 0x00, sizeof(struct hdmi_msm_cec_msg));
+		temp_msg.sender_id = 0x4;
+		temp_msg.recvr_id = hdmi_msm_state->cec_queue_wr->sender_id;
+		i = 0;
+		/* opcode for Image View On */
+		temp_msg.opcode = 0x04;
+		temp_msg.frame_size = i + 2;
+		hdmi_msm_cec_msg_send(&temp_msg);
+		break;
+	default:
+		DEV_INFO("Recvd an unknown cmd = [%u]\n",
+			hdmi_msm_state->cec_queue_wr->opcode);
+#ifdef __SEND_ABORT__
+		memset(&temp_msg, 0x00, sizeof(struct hdmi_msm_cec_msg));
+		temp_msg.sender_id = 0x4;
+		temp_msg.recvr_id = hdmi_msm_state->cec_queue_wr->sender_id;
+		i = 0;
+		/* opcode for feature abort */
+		temp_msg.opcode = 0x00;
+		temp_msg.operand[i++] =
+			hdmi_msm_state->cec_queue_wr->opcode;
+		/*reason for abort = "Unrecognized opcode" */
+		temp_msg.operand[i++] = 0x00;
+		temp_msg.frame_size = i + 2;
+		hdmi_msm_cec_msg_send(&temp_msg);
+		break;
+#else
+		memset(&temp_msg, 0x00, sizeof(struct hdmi_msm_cec_msg));
+		temp_msg.sender_id = 0x4;
+		temp_msg.recvr_id = hdmi_msm_state->cec_queue_wr->sender_id;
+		i = 0;
+		/* OSD String */
+		temp_msg.opcode = 0x64;
+		temp_msg.operand[i++] = 0x0;
+		temp_msg.operand[i++] = 'H';
+		temp_msg.operand[i++] = 'e';
+		temp_msg.operand[i++] = 'l';
+		temp_msg.operand[i++] = 'l';
+		temp_msg.operand[i++] = 'o';
+		temp_msg.operand[i++] = ' ';
+		temp_msg.operand[i++] = 'W';
+		temp_msg.operand[i++] = 'o';
+		temp_msg.operand[i++] = 'r';
+		temp_msg.operand[i++] = 'l';
+		temp_msg.operand[i++] = 'd';
+		temp_msg.frame_size = i + 2;
+		hdmi_msm_cec_msg_send(&temp_msg);
+		break;
+#endif /* __SEND_ABORT__ */
+	}
+
+#endif /* CEC_COMPLIANCE_TESTING */
+	mutex_lock(&hdmi_msm_state_mutex);
+	hdmi_msm_state->cec_queue_wr++;
+	if (hdmi_msm_state->cec_queue_wr == CEC_QUEUE_END)
+		hdmi_msm_state->cec_queue_wr = hdmi_msm_state->cec_queue_start;
+	if (hdmi_msm_state->cec_queue_wr == hdmi_msm_state->cec_queue_rd)
+		hdmi_msm_state->cec_queue_full = true;
+	mutex_unlock(&hdmi_msm_state_mutex);
+	DEV_DBG("Exiting %s()\n", __func__);
+}
+
+void hdmi_msm_cec_one_touch_play(void)
+{
+	struct hdmi_msm_cec_msg temp_msg;
+	uint32 i = 0;
+	memset(&temp_msg, 0x00, sizeof(struct hdmi_msm_cec_msg));
+	temp_msg.sender_id = 0x4;
+	/*
+	 * Broadcast this message
+	 */
+	temp_msg.recvr_id = 0xf;
+	i = 0;
+	/* Active Source */
+	temp_msg.opcode = 0x82;
+	temp_msg.operand[i++] = 0x10;
+	temp_msg.operand[i++] = 0x00;
+	/*temp_msg.operand[i++] = 0x04;*/
+	temp_msg.frame_size = i + 2;
+	hdmi_msm_cec_msg_send(&temp_msg);
+	/*
+	 * sending <Image View On> message
+	 */
+	memset(&temp_msg, 0x00, sizeof(struct hdmi_msm_cec_msg));
+	temp_msg.sender_id = 0x4;
+	temp_msg.recvr_id = hdmi_msm_state->cec_queue_wr->sender_id;
+	i = 0;
+	/* Image View On */
+	temp_msg.opcode = 0x04;
+	temp_msg.frame_size = i + 2;
+	hdmi_msm_cec_msg_send(&temp_msg);
+
+}
+#endif /* CONFIG_FB_MSM_HDMI_MSM_PANEL_CEC_SUPPORT */
+
 uint32 hdmi_msm_get_io_base(void)
 {
 	return (uint32)MSM_HDMI_BASE;
@@ -397,6 +873,9 @@
 {
 	uint32 hpd_int_status;
 	uint32 hpd_int_ctrl;
+#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_CEC_SUPPORT
+	uint32 cec_intr_status;
+#endif
 	uint32 ddc_int_ctrl;
 	uint32 audio_int_val;
 #ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT
@@ -601,8 +1080,55 @@
 	}
 #endif /* CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT */
 
+#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_CEC_SUPPORT
+	/* Process CEC Interrupt */
+	/* HDMI_MSM_CEC_INT[0x029C] */
+	cec_intr_status = HDMI_INP_ND(0x029C);
+
+	DEV_DBG("cec interrupt status is [%u]\n", cec_intr_status);
+
+	if (HDMI_MSM_CEC_FRAME_WR_SUCCESS(cec_intr_status)) {
+		DEV_DBG("CEC_IRQ_FRAME_WR_DONE\n");
+		HDMI_OUTP(0x029C, cec_intr_status |
+			HDMI_MSM_CEC_INT_FRAME_WR_DONE_ACK);
+		mutex_lock(&hdmi_msm_state_mutex);
+		hdmi_msm_state->cec_frame_wr_status |= CEC_STATUS_WR_DONE;
+		mutex_unlock(&hdmi_msm_state_mutex);
+		complete(&hdmi_msm_state->cec_frame_wr_done);
+		return IRQ_HANDLED;
+	}
+	if ((cec_intr_status & (1 << 2)) && (cec_intr_status & (1 << 3))) {
+		DEV_DBG("CEC_IRQ_FRAME_ERROR\n");
+		/* Toggle CEC hardware FSM */
+		HDMI_OUTP(0x028C, 0x0);
+		HDMI_OUTP(0x028C, HDMI_MSM_CEC_CTRL_ENABLE);
+		HDMI_OUTP(0x029C, cec_intr_status);
+		mutex_lock(&hdmi_msm_state_mutex);
+		hdmi_msm_state->cec_frame_wr_status |= CEC_STATUS_WR_ERROR;
+		mutex_unlock(&hdmi_msm_state_mutex);
+		complete(&hdmi_msm_state->cec_frame_wr_done);
+		return IRQ_HANDLED;
+	}
+
+	if ((cec_intr_status & (1 << 4)) && (cec_intr_status & (1 << 5)))
+		DEV_DBG("CEC_IRQ_MONITOR\n");
+
+	if ((cec_intr_status & (1 << 6)) && (cec_intr_status & (1 << 7))) {
+		DEV_DBG("CEC_IRQ_FRAME_RD_DONE\n");
+		HDMI_OUTP(0x029C, cec_intr_status |
+			HDMI_MSM_CEC_INT_FRAME_RD_DONE_ACK);
+		hdmi_msm_cec_msg_recv();
+
+		/* Toggle CEC hardware FSM */
+		HDMI_OUTP(0x028C, 0x0);
+		HDMI_OUTP(0x028C, HDMI_MSM_CEC_CTRL_ENABLE);
+
+		return IRQ_HANDLED;
+	}
+#endif /* CONFIG_FB_MSM_HDMI_MSM_PANEL_CEC_SUPPORT */
+
 	DEV_DBG("%s: HPD<Ctrl=%04x, State=%04x>, ddc_int_ctrl=%04x, "
-		"aud_int=%04x, cec_int=%04x\n", __func__, hpd_int_ctrl,
+		"aud_int=%04x, cec_intr_status=%04x\n", __func__, hpd_int_ctrl,
 		hpd_int_status, ddc_int_ctrl, audio_int_val,
 		HDMI_INP_ND(0x029C));
 
@@ -3130,6 +3656,17 @@
 		hdmi_msm_state->reauth = FALSE ;
 	}
 #endif /* CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT */
+
+#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_CEC_SUPPORT
+	/* re-initialize CEC if enabled */
+	mutex_lock(&hdmi_msm_state_mutex);
+	if (hdmi_msm_state->cec_enabled == true) {
+		hdmi_msm_cec_init();
+		hdmi_msm_cec_write_logical_addr(
+			hdmi_msm_state->cec_logical_addr);
+	}
+	mutex_unlock(&hdmi_msm_state_mutex);
+#endif /* CONFIG_FB_MSM_HDMI_MSM_PANEL_CEC_SUPPORT */
 	DEV_INFO("HDMI Core: Initialized\n");
 }
 
@@ -3187,6 +3724,7 @@
 	hdmi_msm_set_mode(FALSE);
 	HDMI_OUTP_ND(0x0308, 0x7F); /*0b01111111*/
 	hdmi_msm_state->hpd_initialized = FALSE;
+	hdmi_msm_state->pd->cec_power(0);
 	hdmi_msm_state->pd->enable_5v(0);
 	hdmi_msm_state->pd->core_power(0, 1);
 	hdmi_msm_clk(0);
@@ -3208,6 +3746,7 @@
 	hdmi_msm_clk(1);
 	hdmi_msm_state->pd->core_power(1, 1);
 	hdmi_msm_state->pd->enable_5v(1);
+	hdmi_msm_state->pd->cec_power(1);
 	hdmi_msm_dump_regs("HDMI-INIT: ");
 	hdmi_msm_set_mode(FALSE);
 
@@ -3455,6 +3994,12 @@
 		goto error;
 	}
 
+	if (!hdmi_msm_state->pd->cec_power) {
+		DEV_ERR("Init FAILED: cec_power function missing\n");
+		rc = -ENODEV;
+		goto error;
+	}
+
 	rc = request_threaded_irq(hdmi_msm_state->irq, NULL, &hdmi_msm_isr,
 		IRQF_TRIGGER_HIGH | IRQF_ONESHOT, "hdmi_msm_isr", NULL);
 	if (rc) {
@@ -3686,6 +4231,21 @@
 	external_common_state->switch_3d = hdmi_msm_switch_3d;
 #endif
 
+#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_CEC_SUPPORT
+	hdmi_msm_state->cec_queue_start =
+		kzalloc(sizeof(struct hdmi_msm_cec_msg)*CEC_QUEUE_SIZE,
+			GFP_KERNEL);
+	if (!hdmi_msm_state->cec_queue_start) {
+		pr_err("hdmi_msm_init FAILED: CEC queue out of memory\n");
+		rc = -ENOMEM;
+		goto init_exit;
+	}
+
+	hdmi_msm_state->cec_queue_wr = hdmi_msm_state->cec_queue_start;
+	hdmi_msm_state->cec_queue_rd = hdmi_msm_state->cec_queue_start;
+	hdmi_msm_state->cec_queue_full = false;
+#endif
+
 	/*
 	 * Create your work queue
 	 * allocs and returns ptr
@@ -3710,6 +4270,10 @@
 	INIT_WORK(&hdmi_msm_state->hdcp_work, hdmi_msm_hdcp_work);
 #endif /* CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT */
 
+#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_CEC_SUPPORT
+	init_completion(&hdmi_msm_state->cec_frame_wr_done);
+#endif
+
 	rc = platform_device_register(&this_device);
 	if (rc) {
 		pr_err("hdmi_msm_init FAILED: platform_device_register rc=%d\n",
diff --git a/drivers/video/msm/hdmi_msm.h b/drivers/video/msm/hdmi_msm.h
index 2f44b6d..6d19d157 100644
--- a/drivers/video/msm/hdmi_msm.h
+++ b/drivers/video/msm/hdmi_msm.h
@@ -33,7 +33,22 @@
 #define HDMI_INP(offset)		inpdw(MSM_HDMI_BASE+(offset))
 #endif
 
+
+/*
+ * Ref. HDMI 1.4a
+ * Supplement-1 CEC Section 6, 7
+ */
+struct hdmi_msm_cec_msg {
+	uint8 sender_id;
+	uint8 recvr_id;
+	uint8 opcode;
+	uint8 operand[15];
+	uint8 frame_size;
+	uint8 retransmit;
+};
+
 #define QFPROM_BASE		((uint32)hdmi_msm_state->qfprom_io)
+#define HDMI_BASE		((uint32)hdmi_msm_state->hdmi_io)
 
 struct hdmi_msm_state_type {
 	boolean panel_power_on;
@@ -58,6 +73,23 @@
 	struct timer_list hdcp_timer;
 #endif /* CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT */
 
+#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_CEC_SUPPORT
+	boolean cec_enabled;
+	int cec_logical_addr;
+	struct completion cec_frame_wr_done;
+#define CEC_STATUS_WR_ERROR	0x0001
+#define CEC_STATUS_WR_DONE	0x0002
+#define CEC_STATUS_WR_TMOUT	0x0004
+	uint32 cec_frame_wr_status;
+
+	struct hdmi_msm_cec_msg *cec_queue_start;
+	struct hdmi_msm_cec_msg *cec_queue_wr;
+	struct hdmi_msm_cec_msg *cec_queue_rd;
+	boolean cec_queue_full;
+#define CEC_QUEUE_SIZE		16
+#define CEC_QUEUE_END	 (hdmi_msm_state->cec_queue_start + CEC_QUEUE_SIZE)
+#endif /* CONFIG_FB_MSM_HDMI_MSM_PANEL_CEC_SUPPORT */
+
 	int irq;
 	struct msm_hdmi_platform_data *pd;
 	struct clk *hdmi_app_clk;
@@ -84,4 +116,12 @@
 void hdmi_msm_phy_status_poll(void);
 #endif
 
+#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_CEC_SUPPORT
+void hdmi_msm_cec_init(void);
+void hdmi_msm_cec_write_logical_addr(int addr);
+void hdmi_msm_cec_msg_recv(void);
+void hdmi_msm_cec_one_touch_play(void);
+void hdmi_msm_cec_msg_send(struct hdmi_msm_cec_msg *msg);
+#endif /* CONFIG_FB_MSM_HDMI_MSM_PANEL_CEC_SUPPORT */
+
 #endif /* __HDMI_MSM_H__ */
diff --git a/include/linux/mfd/pm8xxx/pm8018.h b/include/linux/mfd/pm8xxx/pm8018.h
index ac7231a..69e781c 100644
--- a/include/linux/mfd/pm8xxx/pm8018.h
+++ b/include/linux/mfd/pm8xxx/pm8018.h
@@ -26,6 +26,7 @@
 #include <linux/input/pmic8xxx-pwrkey.h>
 #include <linux/mfd/pm8xxx/misc.h>
 #include <linux/regulator/pm8018-regulator.h>
+#include <linux/mfd/pm8xxx/pm8xxx-adc.h>
 
 #define PM8018_CORE_DEV_NAME "pm8018-core"
 
@@ -50,6 +51,9 @@
 
 #define PM8018_PWRKEY_REL_IRQ		PM8018_IRQ_BLOCK_BIT(6, 2)
 #define PM8018_PWRKEY_PRESS_IRQ		PM8018_IRQ_BLOCK_BIT(6, 3)
+#define PM8018_ADC_EOC_USR_IRQ		PM8018_IRQ_BLOCK_BIT(9, 6)
+#define PM8018_ADC_BATT_TEMP_WARM_IRQ	PM8018_IRQ_BLOCK_BIT(9, 1)
+#define PM8018_ADC_BATT_TEMP_COLD_IRQ	PM8018_IRQ_BLOCK_BIT(9, 0)
 
 struct pm8018_platform_data {
 	struct pm8xxx_irq_platform_data		*irq_pdata;
@@ -59,6 +63,7 @@
 	struct pm8xxx_pwrkey_platform_data	*pwrkey_pdata;
 	struct pm8xxx_misc_platform_data	*misc_pdata;
 	struct pm8018_regulator_platform_data	*regulator_pdatas;
+	struct pm8xxx_adc_platform_data		*adc_pdata;
 	int					num_regulators;
 };
 
diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h
index 04f01b7..b98af28 100644
--- a/include/net/bluetooth/l2cap.h
+++ b/include/net/bluetooth/l2cap.h
@@ -648,6 +648,11 @@
 #define L2CAP_AMP_STATE_WAIT_PREPARE		11
 #define L2CAP_AMP_STATE_RESEGMENT		12
 
+#define L2CAP_ATT_ERROR				0x01
+#define L2CAP_ATT_RESPONSE_BIT			0x01
+#define L2CAP_ATT_INDICATE			0x1D
+#define L2CAP_ATT_NOT_SUPPORTED			0x06
+
 #define __delta_seq(x, y, pi) ((x) >= (y) ? (x) - (y) : \
 				(pi)->tx_win_max + 1 - (y) + (x))
 #define __next_seq(x, pi) ((x + 1) & ((pi)->tx_win_max))
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index dea21c1..c21eb88 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -7081,6 +7081,10 @@
 static inline int l2cap_att_channel(struct l2cap_conn *conn, __le16 cid, struct sk_buff *skb)
 {
 	struct sock *sk;
+	struct sk_buff *skb_rsp;
+	struct l2cap_hdr *lh;
+	u8 err_rsp[] = {L2CAP_ATT_ERROR, 0x00, 0x00, 0x00,
+						L2CAP_ATT_NOT_SUPPORTED};
 
 	sk = l2cap_get_sock_by_fixed_scid(0, cid, conn->src, conn->dst);
 	if (!sk)
@@ -7100,6 +7104,25 @@
 		goto done;
 
 drop:
+	if (skb->data[0] & L2CAP_ATT_RESPONSE_BIT &&
+			skb->data[0] != L2CAP_ATT_INDICATE)
+		goto free_skb;
+
+	/* If this is an incoming PDU that requires a response, respond with
+	 * a generic error so remote device doesn't hang */
+
+	skb_rsp = bt_skb_alloc(sizeof(err_rsp) + L2CAP_HDR_SIZE, GFP_ATOMIC);
+	if (!skb_rsp)
+		goto free_skb;
+
+	lh = (struct l2cap_hdr *) skb_put(skb_rsp, L2CAP_HDR_SIZE);
+	lh->len = cpu_to_le16(sizeof(err_rsp));
+	lh->cid = cpu_to_le16(L2CAP_CID_LE_DATA);
+	err_rsp[1] = skb->data[0];
+	memcpy(skb_put(skb_rsp, sizeof(err_rsp)), err_rsp, sizeof(err_rsp));
+	hci_send_acl(conn->hcon, NULL, skb_rsp, 0);
+
+free_skb:
 	kfree_skb(skb);
 
 done:
diff --git a/sound/soc/msm/msm-pcm-lpa.c b/sound/soc/msm/msm-pcm-lpa.c
index eeb163e..2719d22 100644
--- a/sound/soc/msm/msm-pcm-lpa.c
+++ b/sound/soc/msm/msm-pcm-lpa.c
@@ -79,9 +79,11 @@
 	struct snd_pcm_runtime *runtime = substream->runtime;
 	struct audio_aio_write_param param;
 	struct audio_buffer *buf = prtd->audio_client->port[IN].buf;
+	unsigned long flag = 0;
 	int i = 0;
 
 	pr_debug("%s\n", __func__);
+	spin_lock_irqsave(&the_locks.event_lock, flag);
 	switch (opcode) {
 	case ASM_DATA_EVENT_WRITE_DONE: {
 		uint32_t *ptrmem = (uint32_t *)&param;
@@ -163,6 +165,7 @@
 		pr_debug("Not Supported Event opcode[0x%x]\n", opcode);
 		break;
 	}
+	spin_unlock_irqrestore(&the_locks.event_lock, flag);
 }
 
 static int msm_pcm_playback_prepare(struct snd_pcm_substream *substream)
@@ -201,6 +204,7 @@
 	pr_debug("%s\n", __func__);
 	switch (cmd) {
 	case SNDRV_PCM_TRIGGER_START:
+		prtd->pcm_irq_pos = 0;
 	case SNDRV_PCM_TRIGGER_RESUME:
 	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
 		pr_debug("SNDRV_PCM_TRIGGER_START\n");
@@ -511,6 +515,7 @@
 
 static int __init msm_soc_platform_init(void)
 {
+	spin_lock_init(&the_locks.event_lock);
 	init_waitqueue_head(&the_locks.enable_wait);
 	init_waitqueue_head(&the_locks.eos_wait);
 	init_waitqueue_head(&the_locks.write_wait);
diff --git a/sound/soc/msm/msm-pcm-q6.h b/sound/soc/msm/msm-pcm-q6.h
index 42e54de..214bd9d 100644
--- a/sound/soc/msm/msm-pcm-q6.h
+++ b/sound/soc/msm/msm-pcm-q6.h
@@ -43,6 +43,7 @@
 };
 
 struct audio_locks {
+	spinlock_t event_lock;
 	wait_queue_head_t read_wait;
 	wait_queue_head_t write_wait;
 	wait_queue_head_t eos_wait;